commit 9865aeaa79903de3462de115f78cea2a3944aaed Author: Ben Gras Date: Thu Apr 21 14:53:53 2005 +0000 Initial revision diff --git a/LICENSE b/LICENSE new file mode 100755 index 000000000..b1d84c6bf --- /dev/null +++ b/LICENSE @@ -0,0 +1,32 @@ + Copyright (c) 1987,1997,2001 Prentice Hall + All rights reserved. + + Redistribution and use of the MINIX operating system in source and + binary forms, with or without modification, are permitted provided + that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Prentice Hall nor the names of the software + authors or contributors may be used to endorse or promote + products derived from this software without specific prior + written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS, AUTHORS, AND + CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + IN NO EVENT SHALL PRENTICE HALL OR ANY AUTHORS OR CONTRIBUTORS BE + LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR + BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile b/Makefile new file mode 100755 index 000000000..092b7a69e --- /dev/null +++ b/Makefile @@ -0,0 +1,51 @@ +# Master Makefile to compile everything in /usr/src except the system. + +MAKE = exec make -$(MAKEFLAGS) + +usage: + @echo "" >&2 + @echo "Master Makefile for MINIX commands and utilities." >&2 + @echo "Usage:" >&2 + @echo " make world # Compile everything (libraries & commands)" >&2 + @echo " make libraries # Compile and install libraries" >&2 + @echo " make commands # Compile commands, but don't install" >&2 + @echo " make install # Compile and install commands" >&2 + @echo " make clean # Remove all compiler results" >&2 + @echo "" >&2 + @echo "Run 'make' in tools/ to create a new MINIX configuration." >&2; exit 0 + @echo "" >&2 + +# world has to be able to make a new system, even if there +# is no complete old system. it has to install commands, for which +# it has to install libraries, for which it has to install includes, +# for which it has to install /etc (for users and ownerships). +# etcfiles also creates a directory hierarchy in its +# 'make install' target. +world: etcfiles includes libraries commands install + +libraries: + cd lib && $(MAKE) install + +commands: + @echo "Are the libraries up to date?"; sleep 2 + test ! -f commands/Makefile || { cd commands && $(MAKE); } + +install: + test ! -f commands/Makefile || { cd commands && $(MAKE) $@; } + +clean:: + cd lib && $(MAKE) $@ + test ! -f commands/Makefile || { cd commands && $(MAKE) $@; } + +etcfiles:: + cd etc && $(MAKE) install + +includes:: + cd include && $(MAKE) install + +all install clean:: + cd boot && $(MAKE) $@ + test ! -f commands/Makefile || { cd commands && $(MAKE) $@; } + cd tools && $(MAKE) $@ + cd man && $(MAKE) $@ + diff --git a/boot/Makefile b/boot/Makefile new file mode 100755 index 000000000..39cd5e3ed --- /dev/null +++ b/boot/Makefile @@ -0,0 +1,116 @@ +# Makefile for the boot monitor package. + +SYS = .. + +CC = exec cc +CC86 = exec cc -mi86 -Was-ncc +CFLAGS = -I$(SYS) +LIBS = -lsys +LD = $(CC) -s -.o +LD86 = $(CC86) -.o +BIN = /usr/bin +MDEC = /usr/mdec + +all: bootblock boot edparams masterboot jumpboot installboot addaout +dos: boot.com mkfile.com + +bootblock: bootblock.s + $(LD86) -com -o $@ bootblock.s + +masterboot: masterboot.s + $(LD86) -com -o $@ masterboot.s + +jumpboot: jumpboot.s + $(LD86) -com -o $@ jumpboot.s + +boot.o: boot.c + $(CC86) $(CFLAGS) -c boot.c + +bootimage.o: bootimage.c + $(CC86) $(CFLAGS) -c bootimage.c + +rawfs86.o: rawfs.c rawfs.o + ln -f rawfs.c rawfs86.c + $(CC86) $(CFLAGS) -c rawfs86.c + rm rawfs86.c + -cmp -s rawfs.o rawfs86.o && ln -f rawfs.o rawfs86.o + +boot: boothead.s boot.o bootimage.o rawfs86.o + $(LD86) -o $@ \ + boothead.s boot.o bootimage.o rawfs86.o $(LIBS) + install -S 16kb boot + +edparams.o: boot.c + ln -f boot.c edparams.c + $(CC) $(CFLAGS) -DUNIX -c edparams.c + rm edparams.c + +edparams: edparams.o rawfs.o + $(CC) $(CFLAGS) $(STRIP) -o $@ edparams.o rawfs.o + install -S 16kw edparams + +dosboot.o: boot.c + $(CC86) $(CFLAGS) -DDOS -o $@ -c boot.c + +doshead.o: doshead.s + $(CC) -mi386 -o $@ -c doshead.s + +dosboot: doshead.o dosboot.o bootimage.o rawfs86.o + $(LD86) -com -o $@ \ + doshead.o dosboot.o bootimage.o rawfs86.o $(LIBS) + +boot.com: dosboot + ./a.out2com dosboot boot.com + +mkfile: mkfhead.s mkfile.c + $(LD) -.o -mi86 -com -o $@ mkfhead.s mkfile.c $(LIBS) + +mkfile.com: mkfile + ./a.out2com mkfile mkfile.com + +installboot: installboot.o rawfs.o + $(CC) $(STRIP) -o installboot installboot.o rawfs.o + install -S 6kw installboot + +addaout: addaout.o + $(CC) -o addaout addaout.o + +installboot.o bootimage.o: image.h +boot.o bootimage.o dosboot.o edparams.o: boot.h +rawfs.o rawfs86.o installboot.o boot.o bootimage.o: rawfs.h + +install: $(MDEC)/bootblock $(MDEC)/boot $(MDEC)/masterboot \ + $(MDEC)/jumpboot $(BIN)/installboot $(BIN)/edparams +dosinstall: $(MDEC)/boot.com $(MDEC)/mkfile.com + +$(MDEC)/bootblock: bootblock + install -cs -o bin -m 644 $? $@ + +$(MDEC)/boot: boot + install -cs -o bin -m 644 $? $@ + +$(MDEC)/boot.com: boot.com + install -c -m 644 $? $@ + +$(MDEC)/mkfile.com: mkfile.com + install -c -m 644 $? $@ + +$(MDEC)/masterboot: masterboot + install -cs -o bin -m 644 $? $@ + +$(MDEC)/jumpboot: jumpboot + install -cs -o bin -m 644 $? $@ + +$(BIN)/installboot: installboot + install -cs -o bin $? $@ + +$(BIN)/addaout: addaout + install -cs -o bin $? $@ + +$(BIN)/edparams: edparams + install -cs -o bin $? $@ + +clean: + rm -f *.bak *.o + rm -f bootblock addaout installboot boot masterboot jumpboot edparams + rm -f dosboot boot.com mkfile mkfile.com diff --git a/boot/a.out2com b/boot/a.out2com new file mode 100755 index 000000000..4bf450868 --- /dev/null +++ b/boot/a.out2com @@ -0,0 +1,25 @@ +#!/bin/sh +# +# a.out2com - Minix a.out to DOS .COM Author: Kees J. Bot +# 17 Jun 1995 +# Transform a Minix a.out to the COM format of MS-DOS, +# the executable must be common I&D with 256 scratch bytes at the start of +# the text segment to make space for the Program Segment Prefix. The Minix +# a.out header and these 256 bytes are removed to make a COM file. + +case $# in +2) aout="$1" + com="$2" + ;; +*) echo "Usage: $0 " >&2 + exit 1 +esac + +size "$aout" >/dev/null || exit +set `size "$aout" | sed 1d` +count=`expr \( $1 + $2 - 256 + 31 \) / 32` + +exec dd if="$aout" of="$com" bs=32 skip=9 count=$count conv=silent + +# +# $PchId: a.out2com,v 1.3 1998/08/01 09:13:01 philip Exp $ diff --git a/boot/addaout.c b/boot/addaout.c new file mode 100644 index 000000000..d6b775f6a --- /dev/null +++ b/boot/addaout.c @@ -0,0 +1,127 @@ +/* A small utility to append an a.out header to an arbitrary file. This allows + * inclusion of arbitrary data in the boot image, so that it is magically + * loaded as a RAM disk. The a.out header is structured as follows: + * + * a_flags: A_IMG to indicate this is not an executable + * + * Created: April 2005, Jorrit N. Herder + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define INPUT_FILE 1 +#define OUTPUT_FILE 2 + +/* Report problems. */ +void report(char *problem, char *message) +{ + fprintf(stderr, "%s:\n", problem); + fprintf(stderr, " %s\n\n", message); +} + + +int copy_data(int srcfd, int dstfd) +{ + char buf[8192]; + ssize_t n; + int total=0; + + /* Copy the little bytes themselves. (Source from cp.c). */ + while ((n= read(srcfd, buf, sizeof(buf))) > 0) { + char *bp = buf; + ssize_t r; + + while (n > 0 && (r= write(dstfd, bp, n)) > 0) { + bp += r; + n -= r; + total += r; + } + if (r <= 0) { + if (r == 0) { + fprintf(stderr, "Warning: EOF writing to output file.\n"); + return(-1); + } + } + } + return(total); +} + + +/* Main program. */ +int main(int argc, char **argv) +{ + struct exec aout; + struct stat stin; + int fdin, fdout; + char * bp; + int n,r; + int total_size=0; + int result; + + /* Check if command line arguments are present, or print usage. */ + if (argc!=3) { + printf("Invalid arguments. Usage:\n"); + printf(" %s \n",argv[0]); + return(1); + } + + /* Check if we can open the input and output file. */ + if (stat(argv[INPUT_FILE], &stin) != 0) { + report("Couldn't get status of input file", strerror(errno)); + return(1); + } + if ((fdin = open(argv[INPUT_FILE], O_RDONLY)) < 0) { + report("Couldn't open input file", strerror(errno)); + return(1); + } + if ((fdout = open(argv[OUTPUT_FILE], O_WRONLY|O_CREAT|O_TRUNC, + stin.st_mode & 0777)) < 0) { + report("Couldn't open output file", strerror(errno)); + return(1); + } + + + /* Copy input file to output file, but leave space for a.out header. */ + lseek(fdout, sizeof(aout), SEEK_SET); + total_size = copy_data(fdin, fdout); + if (total_size < 0) { + report("Aborted", "Output file may be truncated."); + return(1); + } else if (total_size == 0) { + report("Aborted without prepending header", "No data in input file."); + return(1); + } + + + /* Build a.out header and write to output file. */ + memset(&aout, 0, sizeof(struct exec)); + aout.a_magic[0] = A_MAGIC0; + aout.a_magic[1] = A_MAGIC1; + aout.a_flags |= A_IMG; + aout.a_hdrlen = sizeof(aout); + aout.a_text = 0; + aout.a_data = total_size; + aout.a_bss = 0; + aout.a_total = aout.a_hdrlen + aout.a_data; + + bp = (char *) &aout; + n = sizeof(aout); + lseek(fdout, 0, SEEK_SET); + while (n > 0 && (r= write(fdout, bp, n)) > 0) { + bp += r; + n -= r; + } + + printf("Prepended data file (%u bytes) with a.out header.\n", total_size); + printf("Done.\n"); + + return(0); +} + diff --git a/boot/boot.c b/boot/boot.c new file mode 100755 index 000000000..63b40d26c --- /dev/null +++ b/boot/boot.c @@ -0,0 +1,1930 @@ +/* boot.c - Load and start Minix. Author: Kees J. Bot + * 27 Dec 1991 + */ + +char version[]= "2.20"; + +#define BIOS (!UNIX) /* Either uses BIOS or UNIX syscalls. */ + +#define nil 0 +#define _POSIX_SOURCE 1 +#define _MINIX 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if BIOS +#include +#include +#endif +#if UNIX +#include +#include +#include +#include +#include +#include +#endif +#include "rawfs.h" +#undef EXTERN +#define EXTERN /* Empty */ +#include "boot.h" + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) +#define between(a, c, z) ((unsigned) ((c) - (a)) <= ((z) - (a))) + +int fsok= -1; /* File system state. Initially unknown. */ + +static int block_size; + +#if BIOS +char *bios_err(int err) +/* Translate BIOS error code to a readable string. (This is a rare trait + * known as error checking and reporting. Take a good look at it, you won't + * see it often.) + */ +{ + static struct errlist { + int err; + char *what; + } errlist[] = { +#if !DOS + { 0x00, "No error" }, + { 0x01, "Invalid command" }, + { 0x02, "Address mark not found" }, + { 0x03, "Disk write-protected" }, + { 0x04, "Sector not found" }, + { 0x05, "Reset failed" }, + { 0x06, "Floppy disk removed" }, + { 0x07, "Bad parameter table" }, + { 0x08, "DMA overrun" }, + { 0x09, "DMA crossed 64 KB boundary" }, + { 0x0A, "Bad sector flag" }, + { 0x0B, "Bad track flag" }, + { 0x0C, "Media type not found" }, + { 0x0D, "Invalid number of sectors on format" }, + { 0x0E, "Control data address mark detected" }, + { 0x0F, "DMA arbitration level out of range" }, + { 0x10, "Uncorrectable CRC or ECC data error" }, + { 0x11, "ECC corrected data error" }, + { 0x20, "Controller failed" }, + { 0x40, "Seek failed" }, + { 0x80, "Disk timed-out" }, + { 0xAA, "Drive not ready" }, + { 0xBB, "Undefined error" }, + { 0xCC, "Write fault" }, + { 0xE0, "Status register error" }, + { 0xFF, "Sense operation failed" } +#else /* DOS */ + { 0x00, "No error" }, + { 0x01, "Function number invalid" }, + { 0x02, "File not found" }, + { 0x03, "Path not found" }, + { 0x04, "Too many open files" }, + { 0x05, "Access denied" }, + { 0x06, "Invalid handle" }, + { 0x0C, "Access code invalid" }, +#endif /* DOS */ + }; + struct errlist *errp; + + for (errp= errlist; errp < arraylimit(errlist); errp++) { + if (errp->err == err) return errp->what; + } + return "Unknown error"; +} + +char *unix_err(int err) +/* Translate the few errors rawfs can give. */ +{ + switch (err) { + case ENOENT: return "No such file or directory"; + case ENOTDIR: return "Not a directory"; + default: return "Unknown error"; + } +} + +void rwerr(char *rw, off_t sec, int err) +{ + printf("\n%s error 0x%02x (%s) at sector %ld absolute\n", + rw, err, bios_err(err), sec); +} + +void readerr(off_t sec, int err) { rwerr("Read", sec, err); } +void writerr(off_t sec, int err) { rwerr("Write", sec, err); } + +void readblock(off_t blk, char *buf, int block_size) +/* Read blocks for the rawfs package. */ +{ + int r; + u32_t sec= lowsec + blk * RATIO(block_size); + + if(!block_size) { + printf("block_size 0\n"); + exit(1); + } + + if ((r= readsectors(mon2abs(buf), sec, 1 * RATIO(block_size))) != 0) { + readerr(sec, r); exit(1); + } +} + +#define istty (1) +#define alarm(n) (0) + +#endif /* BIOS */ + +#if UNIX + +/* The Minix boot block must start with these bytes: */ +char boot_magic[] = { 0x31, 0xC0, 0x8E, 0xD8, 0xFA, 0x8E, 0xD0, 0xBC }; + +struct biosdev { + char *name; /* Name of device. */ + int device; /* Device to edit parameters. */ +} bootdev; + +struct termios termbuf; +int istty; + +void quit(int status) +{ + if (istty) (void) tcsetattr(0, TCSANOW, &termbuf); + exit(status); +} + +#define exit(s) quit(s) + +void report(char *label) +/* edparams: label: No such file or directory */ +{ + fprintf(stderr, "edparams: %s: %s\n", label, strerror(errno)); +} + +void fatal(char *label) +{ + report(label); + exit(1); +} + +void *alloc(void *m, size_t n) +{ + m= m == nil ? malloc(n) : realloc(m, n); + if (m == nil) fatal(""); + return m; +} + +#define malloc(n) alloc(nil, n) +#define realloc(m, n) alloc(m, n) + +#define mon2abs(addr) ((void *) (addr)) + +int rwsectors(int rw, void *addr, u32_t sec, int nsec) +{ + ssize_t r; + size_t len= nsec * SECTOR_SIZE; + + if (lseek(bootdev.device, sec * SECTOR_SIZE, SEEK_SET) == -1) + return errno; + + if (rw == 0) { + r= read(bootdev.device, (char *) addr, len); + } else { + r= write(bootdev.device, (char *) addr, len); + } + if (r == -1) return errno; + if (r != len) return EIO; + return 0; +} + +#define readsectors(a, s, n) rwsectors(0, (a), (s), (n)) +#define writesectors(a, s, n) rwsectors(1, (a), (s), (n)) +#define readerr(sec, err) (errno= (err), report(bootdev.name)) +#define writerr(sec, err) (errno= (err), report(bootdev.name)) +#define putch(c) putchar(c) +#define unix_err(err) strerror(err) + +void readblock(off_t blk, char *buf, int block_size) +/* Read blocks for the rawfs package. */ +{ + if(!block_size) fatal("block_size 0"); + errno= EIO; + if (lseek(bootdev.device, blk * block_size, SEEK_SET) == -1 + || read(bootdev.device, buf, block_size) != block_size) + { + fatal(bootdev.name); + } +} + +sig_atomic_t trapsig; + +void trap(int sig) +{ + trapsig= sig; + signal(sig, trap); +} + +int escape(void) +{ + if (trapsig == SIGINT) { + trapsig= 0; + return 1; + } + return 0; +} + +static unsigned char unchar; + +int getch(void) +{ + unsigned char c; + + fflush(stdout); + + if (unchar != 0) { + c= unchar; + unchar= 0; + return c; + } + + switch (read(0, &c, 1)) { + case -1: + if (errno != EINTR) fatal(""); + return(ESC); + case 0: + if (istty) putch('\n'); + exit(0); + default: + if (istty && c == termbuf.c_cc[VEOF]) { + putch('\n'); + exit(0); + } + return c; + } +} + +#define ungetch(c) ((void) (unchar = (c))) + +#define get_tick() ((u32_t) time(nil)) +#define clear_screen() printf("[clear]") +#define boot_device(device) printf("[boot %s]\n", device) +#define ctty(line) printf("[ctty %s]\n", line) +#define bootminix() (run_trailer() && printf("[boot]\n")) +#define off() printf("[off]") + +#endif /* UNIX */ + +char *readline(void) +/* Read a line including a newline with echoing. */ +{ + char *line; + size_t i, z; + int c; + + i= 0; + z= 20; + line= malloc(z * sizeof(char)); + + do { + c= getch(); + + if (strchr("\b\177\25\30", c) != nil) { + /* Backspace, DEL, ctrl-U, or ctrl-X. */ + do { + if (i == 0) break; + printf("\b \b"); + i--; + } while (c == '\25' || c == '\30'); + } else + if (c < ' ' && c != '\n') { + putch('\7'); + } else { + putch(c); + line[i++]= c; + if (i == z) { + z*= 2; + line= realloc(line, z * sizeof(char)); + } + } + } while (c != '\n'); + line[i]= 0; + return line; +} + +int sugar(char *tok) +/* Recognize special tokens. */ +{ + return strchr("=(){};\n", tok[0]) != nil; +} + +char *onetoken(char **aline) +/* Returns a string with one token for tokenize. */ +{ + char *line= *aline; + size_t n; + char *tok; + + /* Skip spaces and runs of newlines. */ + while (*line == ' ' || (*line == '\n' && line[1] == '\n')) line++; + + *aline= line; + + /* Don't do odd junk (nor the terminating 0!). */ + if ((unsigned) *line < ' ' && *line != '\n') return nil; + + if (*line == '(') { + /* Function argument, anything goes but () must match. */ + int depth= 0; + + while ((unsigned) *line >= ' ') { + if (*line == '(') depth++; + if (*line++ == ')' && --depth == 0) break; + } + } else + if (sugar(line)) { + /* Single character token. */ + line++; + } else { + /* Multicharacter token. */ + do line++; while ((unsigned) *line > ' ' && !sugar(line)); + } + n= line - *aline; + tok= malloc((n + 1) * sizeof(char)); + memcpy(tok, *aline, n); + tok[n]= 0; + if (tok[0] == '\n') tok[0]= ';'; /* ';' same as '\n' */ + + *aline= line; + return tok; +} + +/* Typed commands form strings of tokens. */ + +typedef struct token { + struct token *next; /* Next in a command chain. */ + char *token; +} token; + +token **tokenize(token **acmds, char *line) +/* Takes a line apart to form tokens. The tokens are inserted into a command + * chain at *acmds. Tokenize returns a reference to where another line could + * be added. Tokenize looks at spaces as token separators, and recognizes only + * ';', '=', '{', '}', and '\n' as single character tokens. One token is + * formed from '(' and ')' with anything in between as long as more () match. + */ +{ + char *tok; + token *newcmd; + + while ((tok= onetoken(&line)) != nil) { + newcmd= malloc(sizeof(*newcmd)); + newcmd->token= tok; + newcmd->next= *acmds; + *acmds= newcmd; + acmds= &newcmd->next; + } + return acmds; +} + +token *cmds; /* String of commands to execute. */ +int err; /* Set on an error. */ + +char *poptoken(void) +/* Pop one token off the command chain. */ +{ + token *cmd= cmds; + char *tok= cmd->token; + + cmds= cmd->next; + free(cmd); + + return tok; +} + +void voidtoken(void) +/* Remove one token from the command chain. */ +{ + free(poptoken()); +} + +void parse_code(char *code) +/* Tokenize a string of monitor code, making sure there is a delimiter. It is + * to be executed next. (Prepended to the current input.) + */ +{ + if (cmds != nil && cmds->token[0] != ';') (void) tokenize(&cmds, ";"); + (void) tokenize(&cmds, code); +} + +int interrupt(void) +/* Clean up after an ESC has been typed. */ +{ + if (escape()) { + printf("[ESC]\n"); + err= 1; + return 1; + } + return 0; +} + +#if BIOS + +int activate; + +struct biosdev { + char name[8]; + int device, primary, secondary; +} bootdev, tmpdev; + +int get_master(char *master, struct part_entry **table, u32_t pos) +/* Read a master boot sector and its partition table. */ +{ + int r, n; + struct part_entry *pe, **pt; + + if ((r= readsectors(mon2abs(master), pos, 1)) != 0) return r; + + pe= (struct part_entry *) (master + PART_TABLE_OFF); + for (pt= table; pt < table + NR_PARTITIONS; pt++) *pt= pe++; + + /* DOS has the misguided idea that partition tables must be sorted. */ + if (pos != 0) return 0; /* But only the primary. */ + + n= NR_PARTITIONS; + do { + for (pt= table; pt < table + NR_PARTITIONS-1; pt++) { + if (pt[0]->sysind == NO_PART + || pt[0]->lowsec > pt[1]->lowsec) { + pe= pt[0]; pt[0]= pt[1]; pt[1]= pe; + } + } + } while (--n > 0); + return 0; +} + +void initialize(void) +{ + char master[SECTOR_SIZE]; + struct part_entry *table[NR_PARTITIONS]; + int r, p; + u32_t masterpos; + char *argp; + + /* Copy the boot program to the far end of low memory, this must be + * done to get out of the way of Minix, and to put the data area + * cleanly inside a 64K chunk if using BIOS I/O (no DMA problems). + */ + u32_t oldaddr= caddr; + u32_t memend= mem[0].base + mem[0].size; + u32_t newaddr= (memend - runsize) & ~0x0000FL; +#if !DOS + u32_t dma64k= (memend - 1) & ~0x0FFFFL; + + /* Check if data segment crosses a 64K boundary. */ + if (newaddr + (daddr - caddr) < dma64k) newaddr= dma64k - runsize; +#endif + /* Set the new caddr for relocate. */ + caddr= newaddr; + + /* Copy code and data. */ + raw_copy(newaddr, oldaddr, runsize); + + /* Make the copy running. */ + relocate(); + +#if !DOS + /* Take the monitor out of the memory map if we have memory to spare, + * and also keep the BIOS data area safe (1.5K), plus a bit extra for + * where we may have to put a.out headers for older kernels. + */ + if (mon_return = (mem[1].size > 512*1024L)) mem[0].size = newaddr; + mem[0].base += 2048; + mem[0].size -= 2048; + + /* Find out what the boot device and partition was. */ + bootdev.name[0]= 0; + bootdev.device= device; + bootdev.primary= -1; + bootdev.secondary= -1; + + if (device < 0x80) { + /* Floppy. */ + strcpy(bootdev.name, "fd0"); + bootdev.name[2] += bootdev.device; + return; + } + + /* Disk: Get the partition table from the very first sector, and + * determine the partition we booted from using the information from + * the booted partition entry as passed on by the bootstrap (rem_part). + * All we need from it is the partition offset. + */ + raw_copy(mon2abs(&lowsec), + vec2abs(&rem_part) + offsetof(struct part_entry, lowsec), + sizeof(lowsec)); + + masterpos= 0; /* Master bootsector position. */ + + for (;;) { + /* Extract the partition table from the master boot sector. */ + if ((r= get_master(master, table, masterpos)) != 0) { + readerr(masterpos, r); exit(1); + } + + /* See if you can find "lowsec" back. */ + for (p= 0; p < NR_PARTITIONS; p++) { + if (lowsec - table[p]->lowsec < table[p]->size) break; + } + + if (lowsec == table[p]->lowsec) { /* Found! */ + if (bootdev.primary < 0) + bootdev.primary= p; + else + bootdev.secondary= p; + break; + } + + if (p == NR_PARTITIONS || bootdev.primary >= 0 + || table[p]->sysind != MINIX_PART) { + /* The boot partition cannot be named, this only means + * that "bootdev" doesn't work. + */ + bootdev.device= -1; + return; + } + + /* See if the primary partition is subpartitioned. */ + bootdev.primary= p; + masterpos= table[p]->lowsec; + } + strcpy(bootdev.name, "d0p0"); + bootdev.name[1] += (device - 0x80); + bootdev.name[3] += bootdev.primary; + if (bootdev.secondary >= 0) { + strcat(bootdev.name, "s0"); + bootdev.name[5] += bootdev.secondary; + } + +#else /* DOS */ + /* Take the monitor out of the memory map if we have memory to spare, + * note that only half our PSP is needed at the new place, the first + * half is to be kept in its place. + */ + if (mem[1].size > 0) mem[0].size = newaddr + 0x80 - mem[0].base; + + /* Parse the command line. */ + argp= PSP + 0x81; + argp[PSP[0x80]]= 0; + while (between('\1', *argp, ' ')) argp++; + vdisk= argp; + while (!between('\0', *argp, ' ')) argp++; + while (between('\1', *argp, ' ')) *argp++= 0; + if (*vdisk == 0) { + printf("\nUsage: boot [commands ...]\n"); + exit(1); + } + drun= *argp == 0 ? "main" : argp; + + if ((r= dev_open()) != 0) { + printf("\n%s: Error %02x (%s)\n", vdisk, r, bios_err(r)); + exit(1); + } + + /* Find the active partition on the virtual disk. */ + if ((r= get_master(master, table, 0)) != 0) { + readerr(0, r); exit(1); + } + + strcpy(bootdev.name, "d0"); + bootdev.primary= -1; + for (p= 0; p < NR_PARTITIONS; p++) { + if (table[p]->bootind != 0 && table[p]->sysind == MINIX_PART) { + bootdev.primary= p; + strcat(bootdev.name, "p0"); + bootdev.name[3] += p; + lowsec= table[p]->lowsec; + break; + } + } +#endif /* DOS */ +} + +#endif /* BIOS */ + +/* Reserved names: */ +enum resnames { + R_NULL, R_BOOT, R_CTTY, R_DELAY, R_ECHO, R_EXIT, R_HELP, + R_LS, R_MENU, R_OFF, R_SAVE, R_SET, R_TRAP, R_UNSET +}; + +char resnames[][6] = { + "", "boot", "ctty", "delay", "echo", "exit", "help", + "ls", "menu", "off", "save", "set", "trap", "unset", +}; + +/* Using this for all null strings saves a lot of memory. */ +#define null (resnames[0]) + +int reserved(char *s) +/* Recognize reserved strings. */ +{ + int r; + + for (r= R_BOOT; r <= R_UNSET; r++) { + if (strcmp(s, resnames[r]) == 0) return r; + } + return R_NULL; +} + +void sfree(char *s) +/* Free a non-null string. */ +{ + if (s != nil && s != null) free(s); +} + +char *copystr(char *s) +/* Copy a non-null string using malloc. */ +{ + char *c; + + if (*s == 0) return null; + c= malloc((strlen(s) + 1) * sizeof(char)); + strcpy(c, s); + return c; +} + +int is_default(environment *e) +{ + return (e->flags & E_SPECIAL) && e->defval == nil; +} + +environment **searchenv(char *name) +{ + environment **aenv= &env; + + while (*aenv != nil && strcmp((*aenv)->name, name) != 0) { + aenv= &(*aenv)->next; + } + + return aenv; +} + +#define b_getenv(name) (*searchenv(name)) +/* Return the environment *structure* belonging to name, or nil if not found. */ + +char *b_value(char *name) +/* The value of a variable. */ +{ + environment *e= b_getenv(name); + + return e == nil || !(e->flags & E_VAR) ? nil : e->value; +} + +char *b_body(char *name) +/* The value of a function. */ +{ + environment *e= b_getenv(name); + + return e == nil || !(e->flags & E_FUNCTION) ? nil : e->value; +} + +int b_setenv(int flags, char *name, char *arg, char *value) +/* Change the value of an environment variable. Returns the flags of the + * variable if you are not allowed to change it, 0 otherwise. + */ +{ + environment **aenv, *e; + + if (*(aenv= searchenv(name)) == nil) { + if (reserved(name)) return E_RESERVED; + e= malloc(sizeof(*e)); + e->name= copystr(name); + e->flags= flags; + e->defval= nil; + e->next= nil; + *aenv= e; + } else { + e= *aenv; + + /* Don't change special variables to functions or vv. */ + if (e->flags & E_SPECIAL + && (e->flags & E_FUNCTION) != (flags & E_FUNCTION) + ) return e->flags; + + e->flags= (e->flags & E_STICKY) | flags; + if (is_default(e)) { + e->defval= e->value; + } else { + sfree(e->value); + } + sfree(e->arg); + } + e->arg= copystr(arg); + e->value= copystr(value); + return 0; +} + +int b_setvar(int flags, char *name, char *value) +/* Set variable or simple function. */ +{ + return b_setenv(flags, name, null, value); +} + +void b_unset(char *name) +/* Remove a variable from the environment. A special variable is reset to + * its default value. + */ +{ + environment **aenv, *e; + + if ((e= *(aenv= searchenv(name))) == nil) return; + + if (e->flags & E_SPECIAL) { + if (e->defval != nil) { + sfree(e->arg); + e->arg= null; + sfree(e->value); + e->value= e->defval; + e->defval= nil; + } + } else { + sfree(e->name); + sfree(e->arg); + sfree(e->value); + *aenv= e->next; + free(e); + } +} + +long a2l(char *a) +/* Cheap atol(). */ +{ + int sign= 1; + long n= 0; + + if (*a == '-') { sign= -1; a++; } + + while (between('0', *a, '9')) n= n * 10 + (*a++ - '0'); + + return sign * n; +} + +char *ul2a(u32_t n, unsigned b) +/* Transform a long number to ascii at base b, (b >= 8). */ +{ + static char num[(CHAR_BIT * sizeof(n) + 2) / 3 + 1]; + char *a= arraylimit(num) - 1; + static char hex[16] = "0123456789ABCDEF"; + + do *--a = hex[(int) (n % b)]; while ((n/= b) > 0); + return a; +} + +char *ul2a10(u32_t n) +/* Transform a long number to ascii at base 10. */ +{ + return ul2a(n, 10); +} + +unsigned a2x(char *a) +/* Ascii to hex. */ +{ + unsigned n= 0; + int c; + + for (;;) { + c= *a; + if (between('0', c, '9')) c= c - '0' + 0x0; + else + if (between('A', c, 'F')) c= c - 'A' + 0xA; + else + if (between('a', c, 'f')) c= c - 'a' + 0xa; + else + break; + n= (n<<4) | c; + a++; + } + return n; +} + +void get_parameters(void) +{ + char params[SECTOR_SIZE + 1]; + token **acmds; + int r, bus; + memory *mp; + static char bus_type[][4] = { + "xt", "at", "mca" + }; + static char vid_type[][4] = { + "mda", "cga", "ega", "ega", "vga", "vga" + }; + static char vid_chrome[][6] = { + "mono", "color" + }; + + /* Variables that Minix needs: */ + b_setvar(E_SPECIAL|E_VAR|E_DEV, "rootdev", "ram"); + b_setvar(E_SPECIAL|E_VAR|E_DEV, "ramimagedev", "bootdev"); + b_setvar(E_SPECIAL|E_VAR, "ramsize", "0"); +#if BIOS + b_setvar(E_SPECIAL|E_VAR, "processor", ul2a10(getprocessor())); + b_setvar(E_SPECIAL|E_VAR, "bus", bus_type[get_bus()]); + b_setvar(E_SPECIAL|E_VAR, "video", vid_type[get_video()]); + b_setvar(E_SPECIAL|E_VAR, "chrome", vid_chrome[get_video() & 1]); + params[0]= 0; + for (mp= mem; mp < arraylimit(mem); mp++) { + if (mp->size == 0) continue; + if (params[0] != 0) strcat(params, ","); + strcat(params, ul2a(mp->base, 0x10)); + strcat(params, ":"); + strcat(params, ul2a(mp->size, 0x10)); + } + b_setvar(E_SPECIAL|E_VAR, "memory", params); + b_setvar(E_SPECIAL|E_VAR, "c0", + DOS ? "dosfile" : get_bus() == 1 ? "at" : "bios"); +#if DOS + b_setvar(E_SPECIAL|E_VAR, "dosfile-d0", vdisk); +#endif + +#endif +#if UNIX + b_setvar(E_SPECIAL|E_VAR, "processor", "?"); + b_setvar(E_SPECIAL|E_VAR, "bus", "?"); + b_setvar(E_SPECIAL|E_VAR, "video", "?"); + b_setvar(E_SPECIAL|E_VAR, "chrome", "?"); + b_setvar(E_SPECIAL|E_VAR, "memory", "?"); + b_setvar(E_SPECIAL|E_VAR, "c0", "?"); +#endif + + /* Variables boot needs: */ + b_setvar(E_SPECIAL|E_VAR, "image", "boot/image"); + b_setvar(E_SPECIAL|E_FUNCTION, "leader", + "echo \\cMinix boot monitor \\v\\n" + "\\nPress ESC to enter the monitor"); + b_setvar(E_SPECIAL|E_FUNCTION, "main", "menu"); + b_setvar(E_SPECIAL|E_FUNCTION, "trailer", "echo \\c"); + + /* Default hidden menu function: */ + b_setenv(E_RESERVED|E_FUNCTION, null, "=,Start Minix", "boot"); + + /* Tokenize bootparams sector. */ + if ((r= readsectors(mon2abs(params), lowsec+PARAMSEC, 1)) != 0) { + readerr(lowsec+PARAMSEC, r); + exit(1); + } + params[SECTOR_SIZE]= 0; + acmds= tokenize(&cmds, params); + + /* Stuff the default action into the command chain. */ +#if UNIX + (void) tokenize(acmds, ":;"); +#elif DOS + (void) tokenize(tokenize(acmds, ":;leader;"), drun); +#else /* BIOS */ + (void) tokenize(acmds, ":;leader;main"); +#endif +} + +char *addptr; + +void addparm(char *n) +{ + while (*n != 0 && *addptr != 0) *addptr++ = *n++; +} + +void save_parameters(void) +/* Save nondefault environment variables to the bootparams sector. */ +{ + environment *e; + char params[SECTOR_SIZE + 1]; + int r; + + /* Default filling: */ + memset(params, '\n', SECTOR_SIZE); + + /* Don't touch the 0! */ + params[SECTOR_SIZE]= 0; + addptr= params; + + for (e= env; e != nil; e= e->next) { + if (e->flags & E_RESERVED || is_default(e)) continue; + + addparm(e->name); + if (e->flags & E_FUNCTION) { + addparm("("); + addparm(e->arg); + addparm(")"); + } else { + addparm((e->flags & (E_DEV|E_SPECIAL)) != E_DEV + ? "=" : "=d "); + } + addparm(e->value); + if (*addptr == 0) { + printf("The environment is too big\n"); + return; + } + *addptr++= '\n'; + } + + /* Save the parameters on disk. */ + if ((r= writesectors(mon2abs(params), lowsec+PARAMSEC, 1)) != 0) { + writerr(lowsec+PARAMSEC, r); + printf("Can't save environment\n"); + } +} + +void show_env(void) +/* Show the environment settings. */ +{ + environment *e; + unsigned more= 0; + int c; + + for (e= env; e != nil; e= e->next) { + if (e->flags & E_RESERVED) continue; + if (!istty && is_default(e)) continue; + + if (e->flags & E_FUNCTION) { + printf("%s(%s) %s\n", e->name, e->arg, e->value); + } else { + printf(is_default(e) ? "%s = (%s)\n" : "%s = %s\n", + e->name, e->value); + } + + if (e->next != nil && istty && ++more % 20 == 0) { + printf("More? "); + c= getch(); + if (c == ESC || c > ' ') { + putch('\n'); + if (c > ' ') ungetch(c); + break; + } + printf("\b\b\b\b\b\b"); + } + } +} + +int numprefix(char *s, char **ps) +/* True iff s is a string of digits. *ps will be set to the first nondigit + * if non-nil, otherwise the string should end. + */ +{ + char *n= s; + + while (between('0', *n, '9')) n++; + + if (n == s) return 0; + + if (ps == nil) return *n == 0; + + *ps= n; + return 1; +} + +int numeric(char *s) +{ + return numprefix(s, (char **) nil); +} + +#if BIOS + +/* Device numbers of standard Minix devices. */ +#define DEV_FD0 0x0200 +static dev_t dev_cNd0[] = { 0x0300, 0x0800, 0x0A00, 0x0C00, 0x1000 }; +#define minor_p0s0 128 + +static int block_size; + +dev_t name2dev(char *name) +/* Translate, say, /dev/c0d0p2 to a device number. If the name can't be + * found on the boot device, then do some guesswork. The global structure + * "tmpdev" will be filled in based on the name, so that "boot d1p0" knows + * what device to boot without interpreting device numbers. + */ +{ + dev_t dev; + ino_t ino; + int drive; + struct stat st; + char *n, *s; + + /* "boot *d0p2" means: make partition 2 active before you boot it. */ + if ((activate= (name[0] == '*'))) name++; + + /* The special name "bootdev" must be translated to the boot device. */ + if (strcmp(name, "bootdev") == 0) { + if (bootdev.device == -1) { + printf("The boot device could not be named\n"); + errno= 0; + return -1; + } + name= bootdev.name; + } + + /* If our boot device doesn't have a file system, or we want to know + * what a name means for the BIOS, then we need to interpret the + * device name ourselves: "fd" = floppy, "c0d0" = hard disk, etc. + */ + tmpdev.device= tmpdev.primary= tmpdev.secondary= -1; + dev= -1; + n= name; + if (strncmp(n, "/dev/", 5) == 0) n+= 5; + + if (strcmp(n, "ram") == 0) { + dev= DEV_RAM; + } else + if (strcmp(n, "boot") == 0) { + dev= DEV_BOOT; + } else + if (n[0] == 'f' && n[1] == 'd' && numeric(n+2)) { + /* Floppy. */ + tmpdev.device= a2l(n+2); + dev= DEV_FD0 + tmpdev.device; + } else + if ((n[0] == 'h' || n[0] == 's') && n[1] == 'd' && numprefix(n+2, &s) + && (*s == 0 || (between('a', *s, 'd') && s[1] == 0)) + ) { + /* Old style hard disk (backwards compatibility.) */ + dev= a2l(n+2); + tmpdev.device= dev / (1 + NR_PARTITIONS); + tmpdev.primary= (dev % (1 + NR_PARTITIONS)) - 1; + if (*s != 0) { + /* Subpartition. */ + tmpdev.secondary= *s - 'a'; + dev= minor_p0s0 + + (tmpdev.device * NR_PARTITIONS + + tmpdev.primary) * NR_PARTITIONS + + tmpdev.secondary; + } + tmpdev.device+= 0x80; + dev+= n[0] == 'h' ? dev_cNd0[0] : dev_cNd0[2]; + } else { + /* Hard disk. */ + int ctrlr= 0; + + if (n[0] == 'c' && between('0', n[1], '4')) { + ctrlr= (n[1] - '0'); + tmpdev.device= 0; + n+= 2; + } + if (n[0] == 'd' && between('0', n[1], '7')) { + tmpdev.device= (n[1] - '0'); + n+= 2; + if (n[0] == 'p' && between('0', n[1], '3')) { + tmpdev.primary= (n[1] - '0'); + n+= 2; + if (n[0] == 's' && between('0', n[1], '3')) { + tmpdev.secondary= (n[1] - '0'); + n+= 2; + } + } + } + if (*n == 0) { + dev= dev_cNd0[ctrlr]; + if (tmpdev.secondary < 0) { + dev += tmpdev.device * (NR_PARTITIONS+1) + + (tmpdev.primary + 1); + } else { + dev += minor_p0s0 + + (tmpdev.device * NR_PARTITIONS + + tmpdev.primary) * NR_PARTITIONS + + tmpdev.secondary; + } + tmpdev.device+= 0x80; + } + } + + /* Look the name up on the boot device for the UNIX device number. */ + if (fsok == -1) fsok= r_super(&block_size) != 0; + if (fsok) { + /* The current working directory is "/dev". */ + ino= r_lookup(r_lookup(ROOT_INO, "dev"), name); + + if (ino != 0) { + /* Name has been found, extract the device number. */ + r_stat(ino, &st); + if (!S_ISBLK(st.st_mode)) { + printf("%s is not a block device\n", name); + errno= 0; + return (dev_t) -1; + } + dev= st.st_rdev; + } + } + + if (tmpdev.primary < 0) activate= 0; /* Careful now! */ + + if (dev == -1) { + printf("Can't recognize '%s' as a device\n", name); + errno= 0; + } + return dev; +} + +#if DEBUG +static void apm_perror(char *label, u16_t ax) +{ + unsigned ah; + char *str; + + ah= (ax >> 8); + switch(ah) + { + case 0x01: str= "APM functionality disabled"; break; + case 0x03: str= "interface not connected"; break; + case 0x09: str= "unrecognized device ID"; break; + case 0x0A: str= "parameter value out of range"; break; + case 0x0B: str= "interface not engaged"; break; + case 0x60: str= "unable to enter requested state"; break; + case 0x86: str= "APM not present"; break; + default: printf("%s: error 0x%02x\n", label, ah); return; + } + printf("%s: %s\n", label, str); +} + +#define apm_printf printf +#else +#define apm_perror(label, ax) ((void)0) +#define apm_printf +#endif + +static void off(void) +{ + bios_env_t be; + unsigned al, ah; + + /* Try to switch off the system. Print diagnostic information + * that can be useful if the operation fails. + */ + + be.ax= 0x5300; /* APM, Installation check */ + be.bx= 0; /* Device, APM BIOS */ + int15(&be); + if (be.flags & FL_CARRY) + { + apm_perror("APM installation check failed", be.ax); + return; + } + if (be.bx != (('P' << 8) | 'M')) + { + apm_printf("APM signature not found (got 0x%04x)\n", be.bx); + return; + } + + ah= be.ax >> 8; + if (ah > 9) + ah= (ah >> 4)*10 + (ah & 0xf); + al= be.ax & 0xff; + if (al > 9) + al= (al >> 4)*10 + (al & 0xf); + apm_printf("APM version %u.%u%s%s%s%s%s\n", + ah, al, + (be.cx & 0x1) ? ", 16-bit PM" : "", + (be.cx & 0x2) ? ", 32-bit PM" : "", + (be.cx & 0x4) ? ", CPU-Idle" : "", + (be.cx & 0x8) ? ", APM-disabled" : "", + (be.cx & 0x10) ? ", APM-disengaged" : ""); + + /* Connect */ + be.ax= 0x5301; /* APM, Real mode interface connect */ + be.bx= 0x0000; /* APM BIOS */ + int15(&be); + if (be.flags & FL_CARRY) + { + apm_perror("APM real mode connect failed", be.ax); + return; + } + + /* Ask for a seat upgrade */ + be.ax= 0x530e; /* APM, Driver Version */ + be.bx= 0x0000; /* BIOS */ + be.cx= 0x0102; /* version 1.2 */ + int15(&be); + if (be.flags & FL_CARRY) + { + apm_perror("Set driver version failed", be.ax); + goto disco; + } + + /* Is this version really worth reporting. Well, if the system + * does switch off, you won't see it anyway. + */ + ah= be.ax >> 8; + if (ah > 9) + ah= (ah >> 4)*10 + (ah & 0xf); + al= be.ax & 0xff; + if (al > 9) + al= (al >> 4)*10 + (al & 0xf); + apm_printf("Got APM connection version %u.%u\n", ah, al); + + /* Enable */ + be.ax= 0x5308; /* APM, Enable/disable power management */ + be.bx= 0x0001; /* All device managed by APM BIOS */ +#if 0 + /* For old APM 1.0 systems, we need 0xffff. Assume that those + * systems do not exist. + */ + be.bx= 0xffff; /* All device managed by APM BIOS (compat) */ +#endif + be.cx= 0x0001; /* Enable power management */ + int15(&be); + if (be.flags & FL_CARRY) + { + apm_perror("Enable power management failed", be.ax); + goto disco; + } + + /* Off */ + be.ax= 0x5307; /* APM, Set Power State */ + be.bx= 0x0001; /* All devices managed by APM */ + be.cx= 0x0003; /* Off */ + int15(&be); + if (be.flags & FL_CARRY) + { + apm_perror("Set power state failed", be.ax); + goto disco; + } + + apm_printf("Power off sequence successfully completed.\n\n"); + apm_printf("Ha, ha, just kidding!\n"); + +disco: + /* Disconnect */ + be.ax= 0x5304; /* APM, interface disconnect */ + be.bx= 0x0000; /* APM BIOS */ + int15(&be); + if (be.flags & FL_CARRY) + { + apm_perror("APM interface disconnect failed", be.ax); + return; + } +} + +#if !DOS +#define B_NOSIG -1 /* "No signature" error code. */ + +int exec_bootstrap(void) +/* Load boot sector from the disk or floppy described by tmpdev and execute it. + */ +{ + int r, n, dirty= 0; + char master[SECTOR_SIZE]; + struct part_entry *table[NR_PARTITIONS], dummy, *active= &dummy; + u32_t masterpos; + + active->lowsec= 0; + + /* Select a partition table entry. */ + while (tmpdev.primary >= 0) { + masterpos= active->lowsec; + + if ((r= get_master(master, table, masterpos)) != 0) return r; + + active= table[tmpdev.primary]; + + /* How does one check a partition table entry? */ + if (active->sysind == NO_PART) return B_NOSIG; + + tmpdev.primary= tmpdev.secondary; + tmpdev.secondary= -1; + } + + if (activate && !active->bootind) { + for (n= 0; n < NR_PARTITIONS; n++) table[n]->bootind= 0; + active->bootind= ACTIVE_FLAG; + dirty= 1; + } + + /* Read the boot sector. */ + if ((r= readsectors(BOOTPOS, active->lowsec, 1)) != 0) return r; + + /* Check signature word. */ + if (get_word(BOOTPOS+SIGNATOFF) != SIGNATURE) return B_NOSIG; + + /* Write the partition table if a member must be made active. */ + if (dirty && (r= writesectors(mon2abs(master), masterpos, 1)) != 0) + return r; + + bootstrap(device, active); +} + +void boot_device(char *devname) +/* Boot the device named by devname. */ +{ + dev_t dev= name2dev(devname); + int save_dev= device; + int r; + char *err; + + if (tmpdev.device < 0) { + if (dev != -1) printf("Can't boot from %s\n", devname); + return; + } + + /* Change current device and try to load and execute its bootstrap. */ + device= tmpdev.device; + + if ((r= dev_open()) == 0) r= exec_bootstrap(); + + err= r == B_NOSIG ? "Not bootable" : bios_err(r); + printf("Can't boot %s: %s\n", devname, err); + + /* Restore boot device setting. */ + device= save_dev; + (void) dev_open(); +} + +void ctty(char *line) +{ + if (between('0', line[0], '3') && line[1] == 0) { + serial_init(line[0] - '0'); + } else { + printf("Bad serial line number: %s\n", line); + } +} + +#else /* DOS */ + +void boot_device(char *devname) +/* No booting of other devices under DOS. */ +{ + printf("Can't boot devices under DOS\n"); +} + +void ctty(char *line) +/* Don't know how to handle serial lines under DOS. */ +{ + printf("No serial line support under DOS\n"); +} + +#endif /* DOS */ +#endif /* BIOS */ + +void ls(char *dir) +/* List the contents of a directory. */ +{ + ino_t ino; + struct stat st; + char name[NAME_MAX+1]; + + if (fsok == -1) fsok= r_super(&block_size) != 0; + if (!fsok) return; + + /* (,) construct because r_stat returns void */ + if ((ino= r_lookup(ROOT_INO, dir)) == 0 || + (r_stat(ino, &st), r_readdir(name)) == -1) + { + printf("ls: %s: %s\n", dir, unix_err(errno)); + return; + } + (void) r_readdir(name); /* Skip ".." too. */ + + while ((ino= r_readdir(name)) != 0) printf("%s/%s\n", dir, name); +} + +u32_t milli_time(void) +{ + return get_tick() * MSEC_PER_TICK; +} + +u32_t milli_since(u32_t base) +{ + return (milli_time() + (TICKS_PER_DAY*MSEC_PER_TICK) - base) + % (TICKS_PER_DAY*MSEC_PER_TICK); +} + +char *Thandler; +u32_t Tbase, Tcount; + +void unschedule(void) +/* Invalidate a waiting command. */ +{ + alarm(0); + + if (Thandler != nil) { + free(Thandler); + Thandler= nil; + } +} + +void schedule(long msec, char *cmd) +/* Schedule command at a certain time from now. */ +{ + unschedule(); + Thandler= cmd; + Tbase= milli_time(); + Tcount= msec; + alarm(1); +} + +int expired(void) +/* Check if the timer expired for getch(). */ +{ + return (Thandler != nil && milli_since(Tbase) >= Tcount); +} + +void delay(char *msec) +/* Delay for a given time. */ +{ + u32_t base, count; + + if ((count= a2l(msec)) == 0) return; + base= milli_time(); + + alarm(1); + + do { + pause(); + } while (!interrupt() && !expired() && milli_since(base) < count); +} + +enum whatfun { NOFUN, SELECT, DEFFUN, USERFUN } menufun(environment *e) +{ + if (!(e->flags & E_FUNCTION) || e->arg[0] == 0) return NOFUN; + if (e->arg[1] != ',') return SELECT; + return e->flags & E_RESERVED ? DEFFUN : USERFUN; +} + +void menu(void) +/* By default: Show a simple menu. + * Multiple kernels/images: Show extra selection options. + * User defined function: Kill the defaults and show these. + * Wait for a keypress and execute the given function. + */ +{ + int c, def= 1; + char *choice= nil; + environment *e; + + /* Just a default menu? */ + for (e= env; e != nil; e= e->next) if (menufun(e) == USERFUN) def= 0; + + printf("\nHit a key as follows:\n\n"); + + /* Show the choices. */ + for (e= env; e != nil; e= e->next) { + switch (menufun(e)) { + case DEFFUN: + if (!def) break; + /*FALL THROUGH*/ + case USERFUN: + printf(" %c %s\n", e->arg[0], e->arg+2); + break; + case SELECT: + printf(" %c Select %s kernel\n", e->arg[0],e->name); + break; + default:; + } + } + + /* Wait for a keypress. */ + do { + c= getch(); + if (interrupt() || expired()) return; + + unschedule(); + + for (e= env; e != nil; e= e->next) { + switch (menufun(e)) { + case DEFFUN: + if (!def) break; + case USERFUN: + case SELECT: + if (c == e->arg[0]) choice= e->value; + } + } + } while (choice == nil); + + /* Execute the chosen function. */ + printf("%c\n", c); + (void) tokenize(&cmds, choice); +} + +void help(void) +/* Not everyone is a rocket scientist. */ +{ + struct help { + char *thing; + char *help; + } *pi; + static struct help info[] = { + { nil, "Names:" }, + { "rootdev", "Root device" }, + { "ramimagedev", "RAM disk image if root is RAM" }, + { "ramsize", "RAM disk size" }, + { "bootdev", "Special name for the boot device" }, + { "fd0, d0p2, c0d0p1s0", "Devices (as in /dev)" }, + { "image", "Name of the kernel image" }, + { "main", "Startup function" }, + { nil, "Commands:" }, + { "name = [device] value", "Set environment variable" }, + { "name() { ... }", "Define function" }, + { "name(key,text) { ... }", + "A menu function like: minix(=,Start Minix) {boot}" }, + { "name", "Call function" }, + { "boot [device]", "Boot Minix or another O.S." }, + { "ctty [line]", "Duplicate to serial line" }, + { "delay [msec]", "Delay (500 msec default)" }, + { "echo word ...", "Print the words" }, + { "ls [directory]", "List contents of directory" }, + { "menu", "Choose a menu function" }, + { "save / set", "Save or show environment" }, + { "trap msec command", "Schedule command" }, + { "unset name ...", "Unset variable or set to default" }, + { "exit / off", "Exit the Monitor / Power off" }, + }; + + for (pi= info; pi < arraylimit(info); pi++) { + if (pi->thing != nil) printf(" %-24s- ", pi->thing); + printf("%s\n", pi->help); + } +} + +void execute(void) +/* Get one command from the command chain and execute it. */ +{ + token *second, *third, *fourth, *sep; + char *name; + int res; + size_t n= 0; + + if (err) { + /* An error occured, stop interpreting. */ + while (cmds != nil) voidtoken(); + return; + } + + if (expired()) { /* Timer expired? */ + parse_code(Thandler); + unschedule(); + } + + /* There must be a separator lurking somewhere. */ + for (sep= cmds; sep != nil && sep->token[0] != ';'; sep= sep->next) n++; + + name= cmds->token; + res= reserved(name); + if ((second= cmds->next) != nil + && (third= second->next) != nil) + fourth= third->next; + + /* Null command? */ + if (n == 0) { + voidtoken(); + return; + } else + /* name = [device] value? */ + if ((n == 3 || n == 4) + && !sugar(name) + && second->token[0] == '=' + && !sugar(third->token) + && (n == 3 || (n == 4 && third->token[0] == 'd' + && !sugar(fourth->token) + ))) { + char *value= third->token; + int flags= E_VAR; + + if (n == 4) { value= fourth->token; flags|= E_DEV; } + + if ((flags= b_setvar(flags, name, value)) != 0) { + printf("%s is a %s\n", name, + flags & E_RESERVED ? "reserved word" : + "special function"); + err= 1; + } + while (cmds != sep) voidtoken(); + return; + } else + /* name '(arg)' ... ? */ + if (n >= 3 + && !sugar(name) + && second->token[0] == '(' + ) { + token *fun; + int c, flags, depth; + char *body; + size_t len; + + sep= fun= third; + depth= 0; + len= 1; + while (sep != nil) { + if ((c= sep->token[0]) == ';' && depth == 0) break; + len+= strlen(sep->token) + 1; + sep= sep->next; + if (c == '{') depth++; + if (c == '}' && --depth == 0) break; + } + + body= malloc(len * sizeof(char)); + *body= 0; + + while (fun != sep) { + strcat(body, fun->token); + if (!sugar(fun->token) + && !sugar(fun->next->token) + ) strcat(body, " "); + fun= fun->next; + } + second->token[strlen(second->token)-1]= 0; + + if (depth != 0) { + printf("Missing '}'\n"); + err= 1; + } else + if ((flags= b_setenv(E_FUNCTION, name, + second->token+1, body)) != 0) { + printf("%s is a %s\n", name, + flags & E_RESERVED ? "reserved word" : + "special variable"); + err= 1; + } + while (cmds != sep) voidtoken(); + free(body); + return; + } else + /* Grouping? */ + if (name[0] == '{') { + token **acmds= &cmds->next; + char *t; + int depth= 1; + + /* Find and remove matching '}' */ + depth= 1; + while (*acmds != nil) { + t= (*acmds)->token; + if (t[0] == '{') depth++; + if (t[0] == '}' && --depth == 0) { t[0]= ';'; break; } + acmds= &(*acmds)->next; + } + voidtoken(); + return; + } else + /* Command coming up, check if ESC typed. */ + if (interrupt()) { + return; + } else + /* unset name ..., echo word ...? */ + if (n >= 1 && (res == R_UNSET || res == R_ECHO)) { + char *arg= poptoken(), *p; + + for (;;) { + free(arg); + if (cmds == sep) break; + arg= poptoken(); + if (res == R_UNSET) { /* unset arg */ + b_unset(arg); + } else { /* echo arg */ + p= arg; + while (*p != 0) { + if (*p != '\\') { + putch(*p); + } else + switch (*++p) { + case 0: + if (cmds == sep) return; + continue; + case 'n': + putch('\n'); + break; + case 'v': + printf(version); + break; + case 'c': + clear_screen(); + break; + case 'w': + for (;;) { + if (interrupt()) + return; + if (getch() == '\n') + break; + } + break; + default: + putch(*p); + } + p++; + } + putch(cmds != sep ? ' ' : '\n'); + } + } + return; + } else + /* boot -opts? */ + if (n == 2 && res == R_BOOT && second->token[0] == '-') { + static char optsvar[]= "bootopts"; + (void) b_setvar(E_VAR, optsvar, second->token); + voidtoken(); + voidtoken(); + bootminix(); + b_unset(optsvar); + return; + } else + /* boot device, ls dir, delay msec? */ + if (n == 2 && (res == R_BOOT || res == R_CTTY + || res == R_DELAY || res == R_LS) + ) { + if (res == R_BOOT) boot_device(second->token); + if (res == R_CTTY) ctty(second->token); + if (res == R_DELAY) delay(second->token); + if (res == R_LS) ls(second->token); + voidtoken(); + voidtoken(); + return; + } else + /* trap msec command? */ + if (n == 3 && res == R_TRAP && numeric(second->token)) { + long msec= a2l(second->token); + + voidtoken(); + voidtoken(); + schedule(msec, poptoken()); + return; + } else + /* Simple command. */ + if (n == 1) { + char *body; + int ok= 0; + + name= poptoken(); + + switch (res) { + case R_BOOT: bootminix(); ok= 1; break; + case R_DELAY: delay("500"); ok= 1; break; + case R_LS: ls(null); ok= 1; break; + case R_MENU: menu(); ok= 1; break; + case R_SAVE: save_parameters(); ok= 1;break; + case R_SET: show_env(); ok= 1; break; + case R_HELP: help(); ok= 1; break; + case R_EXIT: exit(0); + case R_OFF: off(); ok= 1; break; + } + + /* Command to check bootparams: */ + if (strcmp(name, ":") == 0) ok= 1; + + /* User defined function. */ + if (!ok && (body= b_body(name)) != nil) { + (void) tokenize(&cmds, body); + ok= 1; + } + if (!ok) printf("%s: unknown function", name); + free(name); + if (ok) return; + } else { + /* Syntax error. */ + printf("Can't parse:"); + while (cmds != sep) { + printf(" %s", cmds->token); voidtoken(); + } + } + + /* Getting here means that the command is not understood. */ + printf("\nTry 'help'\n"); + err= 1; +} + +int run_trailer(void) +/* Run the trailer function between loading Minix and handing control to it. + * Return true iff there was no error. + */ +{ + token *save_cmds= cmds; + + cmds= nil; + (void) tokenize(&cmds, "trailer"); + while (cmds != nil) execute(); + cmds= save_cmds; + return !err; +} + +void monitor(void) +/* Read a line and tokenize it. */ +{ + char *line; + + unschedule(); /* Kill a trap. */ + err= 0; /* Clear error state. */ + + if (istty) printf("%s>", bootdev.name); + line= readline(); + (void) tokenize(&cmds, line); + free(line); + (void) escape(); /* Forget if ESC typed. */ +} + +#if BIOS + +void boot(void) +/* Load Minix and start it, among other things. */ +{ + /* Initialize tables. */ + initialize(); + + /* Get environment variables from the parameter sector. */ + get_parameters(); + + while (1) { + /* While there are commands, execute them! */ + while (cmds != nil) execute(); + + /* The "monitor" is just a "read one command" thing. */ + monitor(); + } +} +#endif /* BIOS */ + +#if UNIX + +void main(int argc, char **argv) +/* Do not load or start anything, just edit parameters. */ +{ + int i; + char bootcode[SECTOR_SIZE]; + struct termios rawterm; + + istty= (argc <= 2 && tcgetattr(0, &termbuf) == 0); + + if (argc < 2) { + fprintf(stderr, "Usage: edparams device [command ...]\n"); + exit(1); + } + + /* Go over the arguments, changing control characters to spaces. */ + for (i= 2; i < argc; i++) { + char *p; + + for (p= argv[i]; *p != 0; p++) { + if ((unsigned) *p < ' ' && *p != '\n') *p= ' '; + } + } + + bootdev.name= argv[1]; + if (strncmp(bootdev.name, "/dev/", 5) == 0) bootdev.name+= 5; + if ((bootdev.device= open(argv[1], O_RDWR, 0666)) < 0) + fatal(bootdev.name); + + /* Check if it is a bootable Minix device. */ + if (readsectors(mon2abs(bootcode), lowsec, 1) != 0 + || memcmp(bootcode, boot_magic, sizeof(boot_magic)) != 0) { + fprintf(stderr, "edparams: %s: not a bootable Minix device\n", + bootdev.name); + exit(1); + } + + /* Print greeting message. */ + if (istty) printf("Boot parameters editor.\n"); + + signal(SIGINT, trap); + signal(SIGALRM, trap); + + if (istty) { + rawterm= termbuf; + rawterm.c_lflag&= ~(ICANON|ECHO|IEXTEN); + rawterm.c_cc[VINTR]= ESC; + if (tcsetattr(0, TCSANOW, &rawterm) < 0) fatal(""); + } + + /* Get environment variables from the parameter sector. */ + get_parameters(); + + i= 2; + for (;;) { + /* While there are commands, execute them! */ + while (cmds != nil || i < argc) { + if (cmds == nil) { + /* A command line command. */ + parse_code(argv[i++]); + } + execute(); + + /* Bail out on errors if not interactive. */ + if (err && !istty) exit(1); + } + + /* Commands on the command line? */ + if (argc > 2) break; + + /* The "monitor" is just a "read one command" thing. */ + monitor(); + } + exit(0); +} +#endif /* UNIX */ + +/* + * $PchId: boot.c,v 1.14 2002/02/27 19:46:14 philip Exp $ + */ diff --git a/boot/boot.h b/boot/boot.h new file mode 100755 index 000000000..5d1d9a257 --- /dev/null +++ b/boot/boot.h @@ -0,0 +1,209 @@ +/* boot.h - Info between different parts of boot. Author: Kees J. Bot + */ + +#ifndef DEBUG +#define DEBUG 0 +#endif + +/* Constants describing the metal: */ + +#define SECTOR_SIZE 512 +#define SECTOR_SHIFT 9 +#define RATIO(b) ((b) / SECTOR_SIZE) + +#define PARAMSEC 1 /* Sector containing boot parameters. */ + +#define DSKBASE 0x1E /* Floppy disk parameter vector. */ +#define DSKPARSIZE 11 /* There are this many bytes of parameters. */ + +#define ESC '\33' /* Escape key. */ + +#define HEADERPOS 0x00600L /* Place for an array of struct exec's. */ + +#define FREEPOS 0x08000L /* Memory from FREEPOS to caddr is free to + * play with. + */ +#if BIOS +#define MSEC_PER_TICK 55 /* Clock does 18.2 ticks per second. */ +#define TICKS_PER_DAY 0x1800B0L /* After 24 hours it wraps. */ +#endif + +#if UNIX +#define MSEC_PER_TICK 1000 /* Clock does 18.2 ticks per second. */ +#define TICKS_PER_DAY 86400L /* Doesn't wrap, but that doesn't matter. */ +#endif + +#define BOOTPOS 0x07C00L /* Bootstraps are loaded here. */ +#define SIGNATURE 0xAA55 /* Proper bootstraps have this signature. */ +#define SIGNATOFF 510 /* Offset within bootblock. */ + +/* BIOS video modes. */ +#define MONO_MODE 0x07 /* 80x25 monochrome. */ +#define COLOR_MODE 0x03 /* 80x25 color. */ + + +/* Variables shared with boothead.s: */ +#ifndef EXTERN +#define EXTERN extern +#endif + +typedef struct vector { /* 8086 vector */ + u16_t offset; + u16_t segment; +} vector; + +EXTERN vector rem_part; /* Boot partition table entry. */ + +EXTERN u32_t caddr, daddr; /* Code and data address of the boot program. */ +EXTERN u32_t runsize; /* Size of this program. */ + +EXTERN u16_t device; /* Drive being booted from. */ + +typedef struct { /* One chunk of free memory. */ + u32_t base; /* Start byte. */ + u32_t size; /* Number of bytes. */ +} memory; + +EXTERN memory mem[3]; /* List of available memory. */ +EXTERN int mon_return; /* Monitor stays in memory? */ + +typedef struct bios_env +{ + u16_t ax; + u16_t bx; + u16_t cx; + u16_t flags; +} bios_env_t; + +#define FL_CARRY 0x0001 /* carry flag */ + +/* Functions defined by boothead.s: */ + +void exit(int code); + /* Exit the monitor. */ +u32_t mon2abs(void *ptr); + /* Local monitor address to absolute address. */ +u32_t vec2abs(vector *vec); + /* Vector to absolute address. */ +void raw_copy(u32_t dstaddr, u32_t srcaddr, u32_t count); + /* Copy bytes from anywhere to anywhere. */ +u16_t get_word(u32_t addr); + /* Get a word from anywhere. */ +void put_word(u32_t addr, U16_t word); + /* Put a word anywhere. */ +void relocate(void); + /* Switch to a copy of this program. */ +int dev_open(void), dev_close(void); + /* Open device and determine params / close device. */ +int dev_boundary(u32_t sector); + /* True if sector is on a track boundary. */ +int readsectors(u32_t bufaddr, u32_t sector, U8_t count); + /* Read 1 or more sectors from "device". */ +int writesectors(u32_t bufaddr, u32_t sector, U8_t count); + /* Write 1 or more sectors to "device". */ +int getch(void); + /* Read a keypress. */ +void ungetch(int c); + /* Undo a keypress. */ +int escape(void); + /* True if escape typed. */ +void putch(int c); + /* Send a character to the screen. */ +#if BIOS +void pause(void); + /* Wait for an interrupt. */ +void serial_init(int line); +#endif /* Enable copying console I/O to a serial line. */ + +void set_mode(unsigned mode); +void clear_screen(void); + /* Set video mode / clear the screen. */ + +u16_t get_bus(void); + /* System bus type, XT, AT, or MCA. */ +u16_t get_video(void); + /* Display type, MDA to VGA. */ +u32_t get_tick(void); + /* Current value of the clock tick counter. */ + +void bootstrap(int device, struct part_entry *entry); + /* Execute a bootstrap routine for a different O.S. */ +void minix(u32_t koff, u32_t kcs, u32_t kds, + char *bootparams, size_t paramsize, u32_t aout); + /* Start Minix. */ +void int15(bios_env_t *); + + +/* Shared between boot.c and bootimage.c: */ + +/* Sticky attributes. */ +#define E_SPECIAL 0x01 /* These are known to the program. */ +#define E_DEV 0x02 /* The value is a device name. */ +#define E_RESERVED 0x04 /* May not be set by user, e.g. 'boot' */ +#define E_STICKY 0x07 /* Don't go once set. */ + +/* Volatile attributes. */ +#define E_VAR 0x08 /* Variable */ +#define E_FUNCTION 0x10 /* Function definition. */ + +/* Variables, functions, and commands. */ +typedef struct environment { + struct environment *next; + char flags; + char *name; /* name = value */ + char *arg; /* name(arg) {value} */ + char *value; + char *defval; /* Safehouse for default values. */ +} environment; + +EXTERN environment *env; /* Lists the environment. */ + +char *b_value(char *name); /* Get/set the value of a variable. */ +int b_setvar(int flags, char *name, char *value); + +void parse_code(char *code); /* Parse boot monitor commands. */ + +extern int fsok; /* True if the boot device contains an FS. */ +EXTERN u32_t lowsec; /* Offset to the file system on the boot device. */ + +/* Called by boot.c: */ + +void bootminix(void); /* Load and start a Minix image. */ + + +/* Called by bootimage.c: */ + +void readerr(off_t sec, int err); + /* Report a read error. */ +char *ul2a(u32_t n, unsigned b), *ul2a10(u32_t n); + /* Transform u32_t to ASCII at base b or base 10. */ +long a2l(char *a); + /* Cheap atol(). */ +unsigned a2x(char *a); + /* ASCII to hex. */ +dev_t name2dev(char *name); + /* Translate a device name to a device number. */ +int numprefix(char *s, char **ps); + /* True for a numeric prefix. */ +int numeric(char *s); + /* True for a numeric string. */ +char *unix_err(int err); + /* Give a descriptive text for some UNIX errors. */ +int run_trailer(void); + /* Run the trailer function. */ + +#if DOS +/* The monitor runs under MS-DOS. */ +extern char PSP[256]; /* Program Segment Prefix. */ +EXTERN char *vdisk; /* Name of the virtual disk. */ +EXTERN char *drun; /* Initial command from DOS command line. */ +#else +/* The monitor uses only the BIOS. */ +#define DOS 0 +#endif + +void readblock(off_t, char *, int); + +/* + * $PchId: boot.h,v 1.12 2002/02/27 19:42:45 philip Exp $ + */ diff --git a/boot/bootblock.s b/boot/bootblock.s new file mode 100755 index 000000000..77b187073 --- /dev/null +++ b/boot/bootblock.s @@ -0,0 +1,231 @@ +! Bootblock 1.5 - Minix boot block. Author: Kees J. Bot +! 21 Dec 1991 +! +! When the PC is powered on, it will try to read the first sector of floppy +! disk 0 at address 0x7C00. If this fails due to the absence of flexible +! magnetic media, it will read the master boot record from the first sector +! of the hard disk. This sector not only contains executable code, but also +! the partition table of the hard disk. When executed, it will select the +! active partition and load the first sector of that at address 0x7C00. +! This file contains the code that is eventually read from either the floppy +! disk, or the hard disk partition. It is just smart enough to load /boot +! from the boot device into memory at address 0x10000 and execute that. The +! disk addresses for /boot are patched into this code by installboot as 24-bit +! sector numbers and 8-bit sector counts above enddata upwards. /boot is in +! turn smart enough to load the different parts of the Minix kernel into +! memory and execute them to finally get Minix started. +! + + LOADOFF = 0x7C00 ! 0x0000:LOADOFF is where this code is loaded + BOOTSEG = 0x1000 ! Secondary boot code segment. + BOOTOFF = 0x0030 ! Offset into /boot above header + BUFFER = 0x0600 ! First free memory + LOWSEC = 8 ! Offset of logical first sector in partition + ! table + + ! Variables addressed using bp register + device = 0 ! The boot device + lowsec = 2 ! Offset of boot partition within drive + secpcyl = 6 ! Sectors per cylinder = heads * sectors + +.text + +! Start boot procedure. + +boot: + xor ax, ax ! ax = 0x0000, the vector segment + mov ds, ax + cli ! Ignore interrupts while setting stack + mov ss, ax ! ss = ds = vector segment + mov sp, #LOADOFF ! Usual place for a bootstrap stack + sti + + push ax + push ax ! Push a zero lowsec(bp) + + push dx ! Boot device in dl will be device(bp) + mov bp, sp ! Using var(bp) is one byte cheaper then var. + + push es + push si ! es:si = partition table entry if hard disk + + mov di, #LOADOFF+sectors ! char *di = sectors; + + testb dl, dl ! Winchester disks if dl >= 0x80 + jge floppy + +winchester: + +! Get the offset of the first sector of the boot partition from the partition +! table. The table is found at es:si, the lowsec parameter at offset LOWSEC. + + eseg + les ax, LOWSEC(si) ! es:ax = LOWSEC+2(si):LOWSEC(si) + mov lowsec+0(bp), ax ! Low 16 bits of partition's first sector + mov lowsec+2(bp), es ! High 16 bits of partition's first sector + +! Get the drive parameters, the number of sectors is bluntly written into the +! floppy disk sectors/track array. + + movb ah, #0x08 ! Code for drive parameters + int 0x13 ! dl still contains drive + andb cl, #0x3F ! cl = max sector number (1-origin) + movb (di), cl ! Number of sectors per track + incb dh ! dh = 1 + max head number (0-origin) + jmp loadboot + +! Floppy: +! Execute three read tests to determine the drive type. Test for each floppy +! type by reading the last sector on the first track. If it fails, try a type +! that has less sectors. Therefore we start with 1.44M (18 sectors) then 1.2M +! (15 sectors) ending with 720K/360K (both 9 sectors). + +next: inc di ! Next number of sectors per track + +floppy: xorb ah, ah ! Reset drive + int 0x13 + + movb cl, (di) ! cl = number of last sector on track + + cmpb cl, #9 ! No need to do the last 720K/360K test + je success + +! Try to read the last sector on track 0 + + mov es, lowsec(bp) ! es = vector segment (lowsec = 0) + mov bx, #BUFFER ! es:bx buffer = 0x0000:0x0600 + mov ax, #0x0201 ! Read sector, #sectors = 1 + xorb ch, ch ! Track 0, last sector + xorb dh, dh ! Drive dl, head 0 + int 0x13 + jc next ! Error, try the next floppy type + +success:movb dh, #2 ! Load number of heads for multiply + +loadboot: +! Load /boot from the boot device + + movb al, (di) ! al = (di) = sectors per track + mulb dh ! dh = heads, ax = heads * sectors + mov secpcyl(bp), ax ! Sectors per cylinder = heads * sectors + + mov ax, #BOOTSEG ! Segment to load /boot into + mov es, ax + xor bx, bx ! Load first sector at es:bx = BOOTSEG:0x0000 + mov si, #LOADOFF+addresses ! Start of the boot code addresses +load: + mov ax, 1(si) ! Get next sector number: low 16 bits + movb dl, 3(si) ! Bits 16-23 for your up to 8GB partition + xorb dh, dh ! dx:ax = sector within partition + add ax, lowsec+0(bp) + adc dx, lowsec+2(bp)! dx:ax = sector within drive + cmp dx, #[1024*255*63-255]>>16 ! Near 8G limit? + jae bigdisk + div secpcyl(bp) ! ax = cylinder, dx = sector within cylinder + xchg ax, dx ! ax = sector within cylinder, dx = cylinder + movb ch, dl ! ch = low 8 bits of cylinder + divb (di) ! al = head, ah = sector (0-origin) + xorb dl, dl ! About to shift bits 8-9 of cylinder into dl + shr dx, #1 + shr dx, #1 ! dl[6..7] = high cylinder + orb dl, ah ! dl[0..5] = sector (0-origin) + movb cl, dl ! cl[0..5] = sector, cl[6..7] = high cyl + incb cl ! cl[0..5] = sector (1-origin) + movb dh, al ! dh = al = head + movb dl, device(bp) ! dl = device to read + movb al, (di) ! Sectors per track - Sector number (0-origin) + subb al, ah ! = Sectors left on this track + cmpb al, (si) ! Compare with # sectors to read + jbe read ! Can't read past the end of a cylinder? + movb al, (si) ! (si) < sectors left on this track +read: push ax ! Save al = sectors to read + movb ah, #0x02 ! Code for disk read (all registers in use now!) + int 0x13 ! Call the BIOS for a read + pop cx ! Restore al in cl + jmp rdeval +bigdisk: + movb cl, (si) ! Number of sectors to read + push si ! Save si + mov si, #LOADOFF+ext_rw ! si = extended read/write parameter packet + movb 2(si), cl ! Fill in # blocks to transfer + mov 4(si), bx ! Buffer address + mov 8(si), ax ! Starting block number = dx:ax + mov 10(si), dx + movb dl, device(bp) ! dl = device to read + movb ah, #0x42 ! Extended read + int 0x13 + pop si ! Restore si to point to the addresses array + !jmp rdeval +rdeval: + jc error ! Jump on disk read error + movb al, cl ! Restore al = sectors read + addb bh, al ! bx += 2 * al * 256 (add bytes read) + addb bh, al ! es:bx = where next sector must be read + add 1(si), ax ! Update address by sectors read + adcb 3(si), ah ! Don't forget bits 16-23 (add ah = 0) + subb (si), al ! Decrement sector count by sectors read + jnz load ! Not all sectors have been read + add si, #4 ! Next (address, count) pair + cmpb ah, (si) ! Done when no sectors to read + jnz load ! Read next chunk of /boot + +done: + +! Call /boot, assuming a long a.out header (48 bytes). The a.out header is +! usually short (32 bytes), but to be sure /boot has two entry points: +! One at offset 0 for the long, and one at offset 16 for the short header. +! Parameters passed in registers are: +! +! dl = Boot-device. +! es:si = Partition table entry if hard disk. +! + pop si ! Restore es:si = partition table entry + pop es ! dl is still loaded + jmpf BOOTOFF, BOOTSEG ! jmp to sec. boot (skipping header). + +! Read error: print message, hang forever +error: + mov si, #LOADOFF+errno+1 +prnum: movb al, ah ! Error number in ah + andb al, #0x0F ! Low 4 bits + cmpb al, #10 ! A-F? + jb digit ! 0-9! + addb al, #7 ! 'A' - ':' +digit: addb (si), al ! Modify '0' in string + dec si + movb cl, #4 ! Next 4 bits + shrb ah, cl + jnz prnum ! Again if digit > 0 + + mov si, #LOADOFF+rderr ! String to print +print: lodsb ! al = *si++ is char to be printed + testb al, al ! Null byte marks end +hang: jz hang ! Hang forever waiting for CTRL-ALT-DEL + movb ah, #0x0E ! Print character in teletype mode + mov bx, #0x0001 ! Page 0, foreground color + int 0x10 ! Call BIOS VIDEO_IO + jmp print + +.data +rderr: .ascii "Read error " +errno: .ascii "00 \0" +errend: + +! Floppy disk sectors per track for the 1.44M, 1.2M and 360K/720K types: +sectors: + .data1 18, 15, 9 + +! Extended read/write commands require a parameter packet. +ext_rw: + .data1 0x10 ! Length of extended r/w packet + .data1 0 ! Reserved + .data2 0 ! Blocks to transfer (to be filled in) + .data2 0 ! Buffer address offset (tbfi) + .data2 BOOTSEG ! Buffer address segment + .data4 0 ! Starting block number low 32 bits (tbfi) + .data4 0 ! Starting block number high 32 bits + + .align 2 +addresses: +! The space below this is for disk addresses for a 38K /boot program (worst +! case, i.e. file is completely fragmented). It should be enough. diff --git a/boot/boothead.s b/boot/boothead.s new file mode 100755 index 000000000..e212c867d --- /dev/null +++ b/boot/boothead.s @@ -0,0 +1,1497 @@ +! Boothead.s - BIOS support for boot.c Author: Kees J. Bot +! +! +! This file contains the startup and low level support for the secondary +! boot program. It contains functions for disk, tty and keyboard I/O, +! copying memory to arbitrary locations, etc. +! +! The primary bootstrap code supplies the following parameters in registers: +! dl = Boot-device. +! es:si = Partition table entry if hard disk. +! +.text + + o32 = 0x66 ! This assembler doesn't know 386 extensions + BOOTOFF = 0x7C00 ! 0x0000:BOOTOFF load a bootstrap here + LOADSEG = 0x1000 ! Where this code is loaded. + BUFFER = 0x0600 ! First free memory + PENTRYSIZE = 16 ! Partition table entry size. + a_flags = 2 ! From a.out.h, struct exec + a_text = 8 + a_data = 12 + a_bss = 16 + a_total = 24 + A_SEP = 0x20 ! Separate I&D flag + K_I386 = 0x0001 ! Call Minix in 386 mode + K_RET = 0x0020 ! Returns to the monitor on reboot + K_INT86 = 0x0040 ! Requires generic INT support + K_MEML = 0x0080 ! Pass a list of free memory + + DS_SELECTOR = 3*8 ! Kernel data selector + ES_SELECTOR = 4*8 ! Flat 4 Gb + SS_SELECTOR = 5*8 ! Monitor stack + CS_SELECTOR = 6*8 ! Kernel code + MCS_SELECTOR= 7*8 ! Monitor code + + ESC = 0x1B ! Escape character + +! Imported variables and functions: +.extern _caddr, _daddr, _runsize, _edata, _end ! Runtime environment +.extern _device ! BIOS device number +.extern _rem_part ! To pass partition info +.extern _k_flags ! Special kernel flags +.extern _mem ! Free memory list + +.text + +! Set segment registers and stack pointer using the programs own header! +! The header is either 32 bytes (short form) or 48 bytes (long form). The +! bootblock will jump to address 0x10030 in both cases, calling one of the +! two jmpf instructions below. + + jmpf boot, LOADSEG+3 ! Set cs right (skipping long a.out header) + .space 11 ! jmpf + 11 = 16 bytes + jmpf boot, LOADSEG+2 ! Set cs right (skipping short a.out header) +boot: + mov ax, #LOADSEG + mov ds, ax ! ds = header + + movb al, a_flags + testb al, #A_SEP ! Separate I&D? + jnz sepID +comID: xor ax, ax + xchg ax, a_text ! No text + add a_data, ax ! Treat all text as data +sepID: + mov ax, a_total ! Total nontext memory usage + and ax, #0xFFFE ! Round down to even + mov a_total, ax ! total - text = data + bss + heap + stack + cli ! Ignore interrupts while stack in limbo + mov sp, ax ! Set sp at the top of all that + + mov ax, a_text ! Determine offset of ds above cs + movb cl, #4 + shr ax, cl + mov cx, cs + add ax, cx + mov ds, ax ! ds = cs + text / 16 + mov ss, ax + sti ! Stack ok now + push es ! Save es, we need it for the partition table + mov es, ax + cld ! C compiler wants UP + +! Clear bss + xor ax, ax ! Zero + mov di, #_edata ! Start of bss is at end of data + mov cx, #_end ! End of bss (begin of heap) + sub cx, di ! Number of bss bytes + shr cx, #1 ! Number of words + rep + stos ! Clear bss + +! Copy primary boot parameters to variables. (Can do this now that bss is +! cleared and may be written into). + xorb dh, dh + mov _device, dx ! Boot device (probably 0x00 or 0x80) + mov _rem_part+0, si ! Remote partition table offset + pop _rem_part+2 ! and segment (saved es) + +! Remember the current video mode for restoration on exit. + movb ah, #0x0F ! Get current video mode + int 0x10 + andb al, #0x7F ! Mask off bit 7 (no blanking) + movb old_vid_mode, al + movb cur_vid_mode, al + +! Give C code access to the code segment, data segment and the size of this +! process. + xor ax, ax + mov dx, cs + call seg2abs + mov _caddr+0, ax + mov _caddr+2, dx + xor ax, ax + mov dx, ds + call seg2abs + mov _daddr+0, ax + mov _daddr+2, dx + push ds + mov ax, #LOADSEG + mov ds, ax ! Back to the header once more + mov ax, a_total+0 + mov dx, a_total+2 ! dx:ax = data + bss + heap + stack + add ax, a_text+0 + adc dx, a_text+2 ! dx:ax = text + data + bss + heap + stack + pop ds + mov _runsize+0, ax + mov _runsize+2, dx ! 32 bit size of this process + +! Determine available memory as a list of (base,size) pairs as follows: +! mem[0] = low memory, mem[1] = memory between 1M and 16M, mem[2] = memory +! above 16M. Last two coalesced into mem[1] if adjacent. + mov di, #_mem ! di = memory list + int 0x12 ! Returns low memory size (in K) in ax + mul c1024 + mov 4(di), ax ! mem[0].size = low memory size in bytes + mov 6(di), dx + call _getprocessor + cmp ax, #286 ! Only 286s and above have extended memory + jb no_ext + cmp ax, #486 ! Assume 486s were the first to have >64M + jb small_ext ! (It helps to be paranoid when using the BIOS) +big_ext: + mov ax, #0xE801 ! Code for get memory size for >64M + int 0x15 ! ax = mem at 1M per 1K, bx = mem at 16M per 64K + jnc got_ext +small_ext: + movb ah, #0x88 ! Code for get extended memory size + clc ! Carry will stay clear if call exists + int 0x15 ! Returns size (in K) in ax for AT's + jc no_ext + test ax, ax ! An AT with no extended memory? + jz no_ext + xor bx, bx ! bx = mem above 16M per 64K = 0 +got_ext: + mov cx, ax ! cx = copy of ext mem at 1M + mov 10(di), #0x0010 ! mem[1].base = 0x00100000 (1M) + mul c1024 + mov 12(di), ax ! mem[1].size = "ext mem at 1M" * 1024 + mov 14(di), dx + test bx, bx + jz no_ext ! No more ext mem above 16M? + cmp cx, #15*1024 ! Chunks adjacent? (precisely 15M at 1M?) + je adj_ext + mov 18(di), #0x0100 ! mem[2].base = 0x01000000 (16M) + mov 22(di), bx ! mem[2].size = "ext mem at 16M" * 64K + jmp no_ext +adj_ext: + add 14(di), bx ! Add ext mem above 16M to mem below 16M +no_ext: + +! Time to switch to a higher level language (not much higher) + call _boot + +! void ..exit(int status) +! Exit the monitor by rebooting the system. +.define _exit, __exit, ___exit ! Make various compilers happy +_exit: +__exit: +___exit: + mov bx, sp + cmp 2(bx), #0 ! Good exit status? + jz reboot +quit: mov ax, #any_key + push ax + call _printf + xorb ah, ah ! Read character from keyboard + int 0x16 +reboot: call dev_reset + call restore_video + int 0x19 ! Reboot the system +.data +any_key: + .ascii "\nHit any key to reboot\n\0" +.text + +! u32_t mon2abs(void *ptr) +! Address in monitor data to absolute address. +.define _mon2abs +_mon2abs: + mov bx, sp + mov ax, 2(bx) ! ptr + mov dx, ds ! Monitor data segment + jmp seg2abs + +! u32_t vec2abs(vector *vec) +! 8086 interrupt vector to absolute address. +.define _vec2abs +_vec2abs: + mov bx, sp + mov bx, 2(bx) + mov ax, (bx) + mov dx, 2(bx) ! dx:ax vector + !jmp seg2abs ! Translate + +seg2abs: ! Translate dx:ax to the 32 bit address dx-ax + push cx + movb ch, dh + movb cl, #4 + shl dx, cl + shrb ch, cl ! ch-dx = dx << 4 + add ax, dx + adcb ch, #0 ! ch-ax = ch-dx + ax + movb dl, ch + xorb dh, dh ! dx-ax = ch-ax + pop cx + ret + +abs2seg: ! Translate the 32 bit address dx-ax to dx:ax + push cx + movb ch, dl + mov dx, ax ! ch-dx = dx-ax + and ax, #0x000F ! Offset in ax + movb cl, #4 + shr dx, cl + shlb ch, cl + orb dh, ch ! dx = ch-dx >> 4 + pop cx + ret + +! void raw_copy(u32_t dstaddr, u32_t srcaddr, u32_t count) +! Copy count bytes from srcaddr to dstaddr. Don't do overlaps. +! Also handles copying words to or from extended memory. +.define _raw_copy +_raw_copy: + push bp + mov bp, sp + push si + push di ! Save C variable registers +copy: + cmp 14(bp), #0 + jnz bigcopy + mov cx, 12(bp) + jcxz copydone ! Count is zero, end copy + cmp cx, #0xFFF0 + jb smallcopy +bigcopy:mov cx, #0xFFF0 ! Don't copy more than about 64K at once +smallcopy: + push cx ! Save copying count + mov ax, 4(bp) + mov dx, 6(bp) + cmp dx, #0x0010 ! Copy to extended memory? + jae ext_copy + cmp 10(bp), #0x0010 ! Copy from extended memory? + jae ext_copy + call abs2seg + mov di, ax + mov es, dx ! es:di = dstaddr + mov ax, 8(bp) + mov dx, 10(bp) + call abs2seg + mov si, ax + mov ds, dx ! ds:si = srcaddr + shr cx, #1 ! Words to move + rep + movs ! Do the word copy + adc cx, cx ! One more byte? + rep + movsb ! Do the byte copy + mov ax, ss ! Restore ds and es from the remaining ss + mov ds, ax + mov es, ax + jmp copyadjust +ext_copy: + mov x_dst_desc+2, ax + movb x_dst_desc+4, dl ! Set base of destination segment + mov ax, 8(bp) + mov dx, 10(bp) + mov x_src_desc+2, ax + movb x_src_desc+4, dl ! Set base of source segment + mov si, #x_gdt ! es:si = global descriptor table + shr cx, #1 ! Words to move + movb ah, #0x87 ! Code for extended memory move + int 0x15 +copyadjust: + pop cx ! Restore count + add 4(bp), cx + adc 6(bp), #0 ! srcaddr += copycount + add 8(bp), cx + adc 10(bp), #0 ! dstaddr += copycount + sub 12(bp), cx + sbb 14(bp), #0 ! count -= copycount + jmp copy ! and repeat +copydone: + pop di + pop si ! Restore C variable registers + pop bp + ret + +! u16_t get_word(u32_t addr); +! void put_word(u32_t addr, u16_t word); +! Read or write a 16 bits word at an arbitrary location. +.define _get_word, _put_word +_get_word: + mov bx, sp + call gp_getaddr + mov ax, (bx) ! Word to get from addr + jmp gp_ret +_put_word: + mov bx, sp + push 6(bx) ! Word to store at addr + call gp_getaddr + pop (bx) ! Store the word + jmp gp_ret +gp_getaddr: + mov ax, 2(bx) + mov dx, 4(bx) + call abs2seg + mov bx, ax + mov ds, dx ! ds:bx = addr + ret +gp_ret: + push es + pop ds ! Restore ds + ret + +! void relocate(void); +! After the program has copied itself to a safer place, it needs to change +! the segment registers. Caddr has already been set to the new location. +.define _relocate +_relocate: + pop bx ! Return address + mov ax, _caddr+0 + mov dx, _caddr+2 + call abs2seg + mov cx, dx ! cx = new code segment + mov ax, cs ! Old code segment + sub ax, cx ! ax = -(new - old) = -Moving offset + mov dx, ds + sub dx, ax + mov ds, dx ! ds += (new - old) + mov es, dx + mov ss, dx + xor ax, ax + call seg2abs + mov _daddr+0, ax + mov _daddr+2, dx ! New data address + push cx ! New text segment + push bx ! Return offset of this function + retf ! Relocate + +! void *brk(void *addr) +! void *sbrk(size_t incr) +! Cannot fail implementations of brk(2) and sbrk(3), so we can use +! malloc(3). They reboot on stack collision instead of returning -1. +.data + .align 2 +break: .data2 _end ! A fake heap pointer +.text +.define _brk, __brk, _sbrk, __sbrk +_brk: +__brk: ! __brk is for the standard C compiler + xor ax, ax + jmp sbrk ! break= 0; return sbrk(addr); +_sbrk: +__sbrk: + mov ax, break ! ax= current break +sbrk: push ax ! save it as future return value + mov bx, sp ! Stack is now: (retval, retaddr, incr, ...) + add ax, 4(bx) ! ax= break + increment + mov break, ax ! Set new break + lea dx, -1024(bx) ! sp minus a bit of breathing space + cmp dx, ax ! Compare with the new break + jb heaperr ! Suffocating noises + lea dx, -4096(bx) ! A warning when heap+stack goes < 4K + cmp dx, ax + jae plenty ! No reason to complain + mov ax, #memwarn + push ax + call _printf ! Warn about memory running low + pop ax + movb memwarn, #0 ! No more warnings +plenty: pop ax ! Return old break (0 for brk) + ret +heaperr:mov ax, #chmem + push ax + mov ax, #nomem + push ax + call _printf + jmp quit +.data +nomem: .ascii "\nOut of%s\0" +memwarn:.ascii "\nLow on" +chmem: .ascii " memory, use chmem to increase the heap\n\0" +.text + +! int dev_open(void); +! Given the device "_device" figure out if it exists and what its number +! of heads and sectors may be. Return the BIOS error code on error, +! otherwise 0. +.define _dev_open +_dev_open: + call dev_reset ! Optionally reset the disks + movb dev_state, #0 ! State is "closed" + push es + push di ! Save registers used by BIOS calls + movb dl, _device ! The default device + cmpb dl, #0x80 ! Floppy < 0x80, winchester >= 0x80 + jae winchester +floppy: + mov di, #3 ! Three tries to init drive by reading sector 0 +finit0: xor ax, ax + mov es, ax + mov bx, #BUFFER ! es:bx = scratch buffer + mov ax, #0x0201 ! Read sector, #sectors = 1 + mov cx, #0x0001 ! Track 0, first sector + xorb dh, dh ! Drive dl, head 0 + int 0x13 + jnc finit0ok ! Sector 0 read ok? + cmpb ah, #0x80 ! Disk timed out? (Floppy drive empty) + je geoerr + dec di + jz geoerr + xorb ah, ah ! Reset drive + int 0x13 + jc geoerr + jmp finit0 ! Retry once more, it may need to spin up +finit0ok: + mov di, #seclist ! List of per floppy type sectors/track +flast: movb cl, (di) ! Sectors per track to test + cmpb cl, #9 ! No need to do the last 720K/360K test + je ftestok + xor ax, ax + mov es, ax + mov bx, #BUFFER ! es:bx = scratch buffer + mov ax, #0x0201 ! Read sector, #sectors = 1 + xorb ch, ch ! Track 0, last sector + xorb dh, dh ! Drive dl, head 0 + int 0x13 + jnc ftestok ! Sector cl read ok? + xorb ah, ah ! Reset drive + int 0x13 + jc geoerr + inc di ! Try next sec/track number + jmp flast +ftestok: + movb dh, #2 ! Floppies have two sides + jmp geoboth +winchester: + movb ah, #0x08 ! Code for drive parameters + int 0x13 ! dl still contains drive + jc geoerr ! No such drive? + andb cl, #0x3F ! cl = max sector number (1-origin) + incb dh ! dh = 1 + max head number (0-origin) +geoboth: + movb sectors, cl ! Sectors per track + movb al, cl ! al = sectors per track + mulb dh ! ax = heads * sectors + mov secspcyl, ax ! Sectors per cylinder = heads * sectors + movb dev_state, #1 ! Device state is "open" + xor ax, ax ! Code for success +geodone: + pop di + pop es ! Restore di and es registers + ret +geoerr: movb al, ah + xorb ah, ah ! ax = BIOS error code + jmp geodone +.data +seclist: + .data1 18, 15, 9 ! 1.44M, 1.2M, and 360K/720K floppy sec/track +.text + +! int dev_close(void); +! Close the current device. Under the BIOS this does nothing much. +.define _dev_close +_dev_close: + xor ax, ax + movb dev_state, al ! State is "closed" + ret + +! Reset the disks if needed. Minix may have messed things up. +dev_reset: + cmpb dev_state, #0 ! Need reset if dev_state < 0 + jge 0f + xorb ah, ah ! Reset (ah = 0) + movb dl, #0x80 ! All disks + int 0x13 + movb dev_state, #0 ! State is "closed" +0: ret + +! int dev_boundary(u32_t sector); +! True if a sector is on a boundary, i.e. sector % sectors == 0. +.define _dev_boundary +_dev_boundary: + mov bx, sp + xor dx, dx + mov ax, 4(bx) ! divide high half of sector number + div sectors + mov ax, 2(bx) ! divide low half of sector number + div sectors ! dx = sector % sectors + sub dx, #1 ! CF = dx == 0 + sbb ax, ax ! ax = -CF + neg ax ! ax = (sector % sectors) == 0 + ret + +! int readsectors(u32_t bufaddr, u32_t sector, u8_t count) +! int writesectors(u32_t bufaddr, u32_t sector, u8_t count) +! Read/write several sectors from/to disk or floppy. The buffer must +! be between 64K boundaries! Count must fit in a byte. The external +! variables _device, sectors and secspcyl describe the disk and its +! geometry. Returns 0 for success, otherwise the BIOS error code. +! +.define _readsectors, _writesectors +_writesectors: + push bp + mov bp, sp + movb 13(bp), #0x03 ! Code for a disk write + jmp rwsec +_readsectors: + push bp + mov bp, sp + movb 13(bp), #0x02 ! Code for a disk read +rwsec: push si + push di + push es + cmpb dev_state, #0 ! Device state? + jg 0f ! >0 if open + call _dev_open ! Initialize + test ax, ax + jnz badopen +0: mov ax, 4(bp) + mov dx, 6(bp) + call abs2seg + mov bx, ax + mov es, dx ! es:bx = bufaddr + mov di, #3 ! Execute 3 resets on floppy error + cmpb _device, #0x80 + jb nohd + mov di, #1 ! But only 1 reset on hard disk error +nohd: cmpb 12(bp), #0 ! count equals zero? + jz done +more: mov ax, 8(bp) + mov dx, 10(bp) ! dx:ax = abs sector. Divide it by sectors/cyl + cmp dx, #[1024*255*63-255]>>16 ! Near 8G limit? + jae bigdisk + div secspcyl ! ax = cylinder, dx = sector within cylinder + xchg ax, dx ! ax = sector within cylinder, dx = cylinder + movb ch, dl ! ch = low 8 bits of cylinder + divb sectors ! al = head, ah = sector (0-origin) + xorb dl, dl ! About to shift bits 8-9 of cylinder into dl + shr dx, #1 + shr dx, #1 ! dl[6..7] = high cylinder + orb dl, ah ! dl[0..5] = sector (0-origin) + movb cl, dl ! cl[0..5] = sector, cl[6..7] = high cyl + incb cl ! cl[0..5] = sector (1-origin) + movb dh, al ! dh = head + movb dl, _device ! dl = device to use + movb al, sectors ! Sectors per track - Sector number (0-origin) + subb al, ah ! = Sectors left on this track + cmpb al, 12(bp) ! Compare with # sectors to transfer + jbe doit ! Can't go past the end of a cylinder? + movb al, 12(bp) ! 12(bp) < sectors left on this track +doit: movb ah, 13(bp) ! Code for disk read (0x02) or write (0x03) + push ax ! Save al = sectors to read + int 0x13 ! call the BIOS to do the transfer + pop cx ! Restore al in cl + jmp rdeval +bigdisk: + mov si, #ext_rw ! si = extended read/write parameter packet + movb cl, 12(bp) + movb 2(si), cl ! Fill in # blocks to transfer + mov 4(si), bx ! Buffer address = es:bx + mov 6(si), es + mov 8(si), ax ! Starting block number = dx:ax + mov 10(si), dx + movb dl, _device ! dl = device to use + mov ax, #0x4000 ! This, or-ed with 0x02 or 0x03 becomes + orb ah, 13(bp) ! extended read (0x4200) or write (0x4300) + int 0x13 + !jmp rdeval +rdeval: + jc ioerr ! I/O error + movb al, cl ! Restore al = sectors read + addb bh, al ! bx += 2 * al * 256 (add bytes transferred) + addb bh, al ! es:bx = where next sector is located + add 8(bp), ax ! Update address by sectors transferred + adc 10(bp), #0 ! Don't forget high word + subb 12(bp), al ! Decrement sector count by sectors transferred + jnz more ! Not all sectors have been transferred +done: xorb ah, ah ! No error here! + jmp finish +ioerr: cmpb ah, #0x80 ! Disk timed out? (Floppy drive empty) + je finish + cmpb ah, #0x03 ! Disk write protected? + je finish + dec di ! Do we allow another reset? + jl finish ! No, report the error + xorb ah, ah ! Code for a reset (0) + int 0x13 + jnc more ! Succesful reset, try request again +finish: movb al, ah + xorb ah, ah ! ax = error number +badopen:pop es + pop di + pop si + pop bp + ret +.data + .align 4 +! Extended read/write commands require a parameter packet. +ext_rw: + .data1 0x10 ! Length of extended r/w packet + .data1 0 ! Reserved + .data2 0 ! Blocks to transfer (to be filled in) + .data2 0 ! Buffer address offset (tbfi) + .data2 0 ! Buffer address segment (tbfi) + .data4 0 ! Starting block number low 32 bits (tbfi) + .data4 0 ! Starting block number high 32 bits +.text + +! int getch(void); +! Read a character from the keyboard, and check for an expired timer. +! A carriage return is changed into a linefeed for UNIX compatibility. +.define _getch +_getch: + xor ax, ax + xchg ax, unchar ! Ungotten character? + test ax, ax + jnz gotch +getch: + hlt ! Play dead until interrupted (see pause()) + movb ah, #0x01 ! Keyboard status + int 0x16 + jz 0f ! Nothing typed + xorb ah, ah ! Read character from keyboard + int 0x16 + jmp press ! Keypress +0: mov dx, line ! Serial line? + test dx, dx + jz 0f + add dx, #5 ! Line Status Register + inb dx + testb al, #0x01 ! Data Ready? + jz 0f + mov dx, line + !add dx, 0 ! Receive Buffer Register + inb dx ! Get character + jmp press +0: call _expired ! Timer expired? + test ax, ax + jz getch + mov ax, #ESC ! Return ESC + ret +press: + cmpb al, #0x0D ! Carriage return? + jnz nocr + movb al, #0x0A ! Change to linefeed +nocr: cmpb al, #ESC ! Escape typed? + jne noesc + inc escape ! Set flag +noesc: xorb ah, ah ! ax = al +gotch: ret + +! int ungetch(void); +! Return a character to undo a getch(). +.define _ungetch +_ungetch: + mov bx, sp + mov ax, 2(bx) + mov unchar, ax + ret + +! int escape(void); +! True if ESC has been typed. +.define _escape +_escape: + movb ah, #0x01 ! Keyboard status + int 0x16 + jz escflg ! Keypress? + cmpb al, #ESC ! Escape typed? + jne escflg + xorb ah, ah ! Discard the escape + int 0x16 + inc escape ! Set flag +escflg: xor ax, ax + xchg ax, escape ! Escape typed flag + ret + +! int putch(int c); +! Write a character in teletype mode. The putk synonym is +! for the kernel printf function that uses it. +! Newlines are automatically preceded by a carriage return. +! +.define _putch, _putk +_putch: +_putk: mov bx, sp + movb al, 2(bx) ! al = character to be printed + testb al, al ! Kernel printf adds a null char to flush queue + jz nulch + cmpb al, #0x0A ! al = newline? + jnz putc + movb al, #0x0D + call putc ! putc('\r') + movb al, #0x0A ! Restore the '\n' and print it +putc: movb ah, #0x0E ! Print character in teletype mode + mov bx, #0x0001 ! Page 0, foreground color + int 0x10 + mov bx, line ! Serial line? + test bx, bx + jz nulch + push ax ! Save character to print + call _get_tick ! Current clock tick counter + mov cx, ax + add cx, #2 ! Don't want to see it count twice +1: lea dx, 5(bx) ! Line Status Register + inb dx + testb al, #0x20 ! Transmitter Holding Register Empty? + jnz 0f + call _get_tick + cmp ax, cx ! Clock ticked more than once? + jne 1b +0: pop ax ! Restore character to print + mov dx, bx ! Transmit Holding Register + outb dx ! Send character down the serial line +nulch: ret + +! void pause(void); +! Wait for an interrupt using the HLT instruction. This either saves +! power, or tells an x86 emulator that nothing is happening right now. +.define _pause +_pause: + hlt + ret + +! void set_mode(unsigned mode); +! void clear_screen(void); +! Set video mode / clear the screen. +.define _set_mode, _clear_screen +_set_mode: + mov bx, sp + mov ax, 2(bx) ! Video mode + cmp ax, cur_vid_mode + je modeok ! Mode already as requested? + mov cur_vid_mode, ax +_clear_screen: + xor ax, ax + mov es, ax ! es = Vector segment + mov ax, cur_vid_mode + movb ch, ah ! Copy of the special flags + andb ah, #0x0F ! Test bits 8-11, clear special flags + jnz xvesa ! VESA extended mode? + int 0x10 ! Reset video (ah = 0) + jmp md_480 +xvesa: mov bx, ax ! bx = extended mode + mov ax, #0x4F02 ! Reset video + int 0x10 +md_480: ! Basic video mode is set, now build on it + testb ch, #0x20 ! 480 scan lines requested? + jz md_14pt + mov dx, #0x3CC ! Get CRTC port + inb dx + movb dl, #0xD4 + testb al, #1 ! Mono or color? + jnz 0f + movb dl, #0xB4 +0: mov ax, #0x110C ! Vertical sync end (also unlocks CR0-7) + call out2 + mov ax, #0x060B ! Vertical total + call out2 + mov ax, #0x073E ! (Vertical) overflow + call out2 + mov ax, #0x10EA ! Vertical sync start + call out2 + mov ax, #0x12DF ! Vertical display end + call out2 + mov ax, #0x15E7 ! Vertical blank start + call out2 + mov ax, #0x1604 ! Vertical blank end + call out2 + push dx + movb dl, #0xCC ! Misc output register (read) + inb dx + movb dl, #0xC2 ! (write) + andb al, #0x0D ! Preserve clock select bits and color bit + orb al, #0xE2 ! Set correct sync polarity + outb dx + pop dx ! Index register still in dx +md_14pt: + testb ch, #0x40 ! 9x14 point font requested? + jz md_8pt + mov ax, #0x1111 ! Load ROM 9 by 14 font + xorb bl, bl ! Load block 0 + int 0x10 + testb ch, #0x20 ! 480 scan lines? + jz md_8pt + mov ax, #0x12DB ! VGA vertical display end + call out2 + eseg movb 0x0484, #33 ! Tell BIOS the last line number +md_8pt: + testb ch, #0x80 ! 8x8 point font requested? + jz setcur + mov ax, #0x1112 ! Load ROM 8 by 8 font + xorb bl, bl ! Load block 0 + int 0x10 + testb ch, #0x20 ! 480 scan lines? + jz setcur + mov ax, #0x12DF ! VGA vertical display end + call out2 + eseg movb 0x0484, #59 ! Tell BIOS the last line number +setcur: + xor dx, dx ! dl = column = 0, dh = row = 0 + xorb bh, bh ! Page 0 + movb ah, #0x02 ! Set cursor position + int 0x10 + push ss + pop es ! Restore es +modeok: ret + +! Out to the usual [index, data] port pair that are common for VGA devices +! dx = port, ah = index, al = data. +out2: + push dx + push ax + movb al, ah + outb dx ! Set index + inc dx + pop ax + outb dx ! Send data + pop dx + ret + +restore_video: ! To restore the video mode on exit + mov ax, old_vid_mode + push ax + call _set_mode + pop ax + ret + +! void serial_init(int line) +! Initialize copying console I/O to a serial line. +.define _serial_init +_serial_init: + mov bx, sp + mov dx, 2(bx) ! Line number + push ds + xor ax, ax + mov ds, ax ! Vector and BIOS data segment + mov bx, dx ! Line number + shl bx, #1 ! Word offset + mov bx, 0x0400(bx) ! I/O port for the given line + pop ds + mov line, bx ! Remember I/O port +serial_init: + mov bx, line + test bx, bx ! I/O port must be nonzero + jz 0f + mov ax, #0x00E3 ! 9600 N-8-1 + int 0x14 ! Initialize serial line dx + lea dx, 4(bx) ! Modem Control Register + movb al, #0x0B ! DTR, RTS, OUT2 + outb dx +0: ret + +! u32_t get_tick(void); +! Return the current value of the clock tick counter. This counter +! increments 18.2 times per second. Poll it to do delays. Does not +! work on the original PC, but works on the PC/XT. +.define _get_tick +_get_tick: + push cx + xorb ah, ah ! Code for get tick count + int 0x1A + mov ax, dx + mov dx, cx ! dx:ax = cx:dx = tick count + pop cx + ret + + +! Functions used to obtain info about the hardware. Boot uses this information +! itself, but will also pass them on to a pure 386 kernel, because one can't +! make BIOS calls from protected mode. The video type could probably be +! determined by the kernel too by looking at the hardware, but there is a small +! chance on errors that the monitor allows you to correct by setting variables. + +.define _get_bus ! returns type of system bus +.define _get_video ! returns type of display + +! u16_t get_bus(void) +! Return type of system bus, in order: XT, AT, MCA. +_get_bus: + call _getprocessor + xor dx, dx ! Assume XT + cmp ax, #286 ! An AT has at least a 286 + jb got_bus + inc dx ! Assume AT + movb ah, #0xC0 ! Code for get configuration + int 0x15 + jc got_bus ! Carry clear and ah = 00 if supported + testb ah, ah + jne got_bus + eseg + movb al, 5(bx) ! Load feature byte #1 + inc dx ! Assume MCA + testb al, #0x02 ! Test bit 1 - "bus is Micro Channel" + jnz got_bus + dec dx ! Assume AT + testb al, #0x40 ! Test bit 6 - "2nd 8259 installed" + jnz got_bus + dec dx ! It is an XT +got_bus: + push ds + pop es ! Restore es + mov ax, dx ! Return bus code + mov bus, ax ! Keep bus code, A20 handler likes to know + ret + +! u16_t get_video(void) +! Return type of display, in order: MDA, CGA, mono EGA, color EGA, +! mono VGA, color VGA. +_get_video: + mov ax, #0x1A00 ! Function 1A returns display code + int 0x10 ! al = 1A if supported + cmpb al, #0x1A + jnz no_dc ! No display code function supported + + mov ax, #2 + cmpb bl, #5 ! Is it a monochrome EGA? + jz got_video + inc ax + cmpb bl, #4 ! Is it a color EGA? + jz got_video + inc ax + cmpb bl, #7 ! Is it a monochrome VGA? + jz got_video + inc ax + cmpb bl, #8 ! Is it a color VGA? + jz got_video + +no_dc: movb ah, #0x12 ! Get information about the EGA + movb bl, #0x10 + int 0x10 + cmpb bl, #0x10 ! Did it come back as 0x10? (No EGA) + jz no_ega + + mov ax, #2 + cmpb bh, #1 ! Is it monochrome? + jz got_video + inc ax + jmp got_video + +no_ega: int 0x11 ! Get bit pattern for equipment + and ax, #0x30 ! Isolate color/mono field + sub ax, #0x30 + jz got_video ! Is it an MDA? + mov ax, #1 ! No it's CGA + +got_video: + ret + + +! Functions to leave the boot monitor. +.define _bootstrap ! Call another bootstrap +.define _minix ! Call Minix + +! void _bootstrap(int device, struct part_entry *entry) +! Call another bootstrap routine to boot MS-DOS for instance. (No real +! need for that anymore, now that you can format floppies under Minix). +! The bootstrap must have been loaded at BOOTSEG from "device". +_bootstrap: + call restore_video + mov bx, sp + movb dl, 2(bx) ! Device to boot from + mov si, 4(bx) ! ds:si = partition table entry + xor ax, ax + mov es, ax ! Vector segment + mov di, #BUFFER ! es:di = buffer in low core + mov cx, #PENTRYSIZE ! cx = size of partition table entry + rep movsb ! Copy the entry to low core + mov si, #BUFFER ! es:si = partition table entry + mov ds, ax ! Some bootstraps need zero segment registers + cli + mov ss, ax + mov sp, #BOOTOFF ! This should do it + sti + jmpf BOOTOFF, 0 ! Back to where the BIOS loads the boot code + +! void minix(u32_t koff, u32_t kcs, u32_t kds, +! char *bootparams, size_t paramsize, u32_t aout); +! Call Minix. +_minix: + push bp + mov bp, sp ! Pointer to arguments + + mov dx, #0x03F2 ! Floppy motor drive control bits + movb al, #0x0C ! Bits 4-7 for floppy 0-3 are off + outb dx ! Kill the motors + push ds + xor ax, ax ! Vector & BIOS data segments + mov ds, ax + andb 0x043F, #0xF0 ! Clear diskette motor status bits of BIOS + pop ds + cli ! No more interruptions + + test _k_flags, #K_I386 ! Switch to 386 mode? + jnz minix386 + +! Call Minix in real mode. +minix86: + test _k_flags, #K_MEML ! New memory arrangements? + jz 0f + push 22(bp) ! Address of a.out headers + push 20(bp) +0: + push 18(bp) ! # bytes of boot parameters + push 16(bp) ! Address of boot parameters + + test _k_flags, #K_RET ! Can the kernel return? + jz noret86 + xor dx, dx ! If little ext mem then monitor not preserved + xor ax, ax + cmp _mon_return, ax ! Minix can return to the monitor? + jz 0f + mov dx, cs ! Monitor far return address + mov ax, #ret86 +0: push dx ! Push monitor far return address or zero + push ax +noret86: + + mov ax, 8(bp) + mov dx, 10(bp) + call abs2seg + push dx ! Kernel code segment + push 4(bp) ! Kernel code offset + mov ax, 12(bp) + mov dx, 14(bp) + call abs2seg + mov ds, dx ! Kernel data segment + mov es, dx ! Set es to kernel data too + retf ! Make a far call to the kernel + +! Call Minix in 386 mode. +minix386: + cseg mov cs_real-2, cs ! Patch CS and DS into the instructions that + cseg mov ds_real-2, ds ! reload them when switching back to real mode + .data1 0x0F,0x20,0xC0 ! mov eax, cr0 + orb al, #0x01 ! Set PE (protection enable) bit + .data1 o32 + mov msw, ax ! Save as protected mode machine status word + + mov dx, ds ! Monitor ds + mov ax, #p_gdt ! dx:ax = Global descriptor table + call seg2abs + mov p_gdt_desc+2, ax + movb p_gdt_desc+4, dl ! Set base of global descriptor table + + mov ax, 12(bp) + mov dx, 14(bp) ! Kernel ds (absolute address) + mov p_ds_desc+2, ax + movb p_ds_desc+4, dl ! Set base of kernel data segment + + mov dx, ss ! Monitor ss + xor ax, ax ! dx:ax = Monitor stack segment + call seg2abs ! Minix starts with the stack of the monitor + mov p_ss_desc+2, ax + movb p_ss_desc+4, dl + + mov ax, 8(bp) + mov dx, 10(bp) ! Kernel cs (absolute address) + mov p_cs_desc+2, ax + movb p_cs_desc+4, dl + + mov dx, cs ! Monitor cs + xor ax, ax ! dx:ax = Monitor code segment + call seg2abs + mov p_mcs_desc+2, ax + movb p_mcs_desc+4, dl + + push #MCS_SELECTOR + test _k_flags, #K_INT86 ! Generic INT86 support? + jz 0f + push #int86 ! Far address to INT86 support + jmp 1f +0: push #bios13 ! Far address to BIOS int 13 support +1: + test _k_flags, #K_MEML ! New memory arrangements? + jz 0f + .data1 o32 + push 20(bp) ! Address of a.out headers +0: + push #0 + push 18(bp) ! 32 bit size of parameters on stack + push #0 + push 16(bp) ! 32 bit address of parameters (ss relative) + + test _k_flags, #K_RET ! Can the kernel return? + jz noret386 + push #MCS_SELECTOR + push #ret386 ! Monitor far return address +noret386: + + push #0 + push #CS_SELECTOR + push 6(bp) + push 4(bp) ! 32 bit far address to kernel entry point + + call real2prot ! Switch to protected mode + mov ax, #DS_SELECTOR ! Kernel data + mov ds, ax + mov ax, #ES_SELECTOR ! Flat 4 Gb + mov es, ax + .data1 o32 ! Make a far call to the kernel + retf + +! Minix-86 returns here on a halt or reboot. +ret86: + mov _reboot_code+0, ax + mov _reboot_code+2, dx ! Return value (obsolete method) + jmp return + +! Minix-386 returns here on a halt or reboot. +ret386: + .data1 o32 + mov _reboot_code, ax ! Return value (obsolete method) + call prot2real ! Switch to real mode + +return: + mov sp, bp ! Pop parameters + sti ! Can take interrupts again + + call _get_video ! MDA, CGA, EGA, ... + movb dh, #24 ! dh = row 24 + cmp ax, #2 ! At least EGA? + jb is25 ! Otherwise 25 rows + push ds + xor ax, ax ! Vector & BIOS data segments + mov ds, ax + movb dh, 0x0484 ! Number of rows on display minus one + pop ds +is25: + xorb dl, dl ! dl = column 0 + xorb bh, bh ! Page 0 + movb ah, #0x02 ! Set cursor position + int 0x10 + + movb dev_state, #-1 ! Minix may have upset the disks, must reset. + call serial_init ! Likewise with our serial console + + call _getprocessor + cmp ax, #286 + jb noclock + xorb al, al +tryclk: decb al + jz noclock + movb ah, #0x02 ! Get real-time clock time (from CMOS clock) + int 0x1A + jc tryclk ! Carry set, not running or being updated + movb al, ch ! ch = hour in BCD + call bcd ! al = (al >> 4) * 10 + (al & 0x0F) + mulb c60 ! 60 minutes in an hour + mov bx, ax ! bx = hour * 60 + movb al, cl ! cl = minutes in BCD + call bcd + add bx, ax ! bx = hour * 60 + minutes + movb al, dh ! dh = seconds in BCD + call bcd + xchg ax, bx ! ax = hour * 60 + minutes, bx = seconds + mul c60 ! dx-ax = (hour * 60 + minutes) * 60 + add bx, ax + adc dx, #0 ! dx-bx = seconds since midnight + mov ax, dx + mul c19663 + xchg ax, bx + mul c19663 + add dx, bx ! dx-ax = dx-bx * (0x1800B0 / (2*2*2*2*5)) + mov cx, ax ! (0x1800B0 = ticks per day of BIOS clock) + mov ax, dx + xor dx, dx + div c1080 + xchg ax, cx + div c1080 ! cx-ax = dx-ax / (24*60*60 / (2*2*2*2*5)) + mov dx, ax ! cx-dx = ticks since midnight + movb ah, #0x01 ! Set system time + int 0x1A +noclock: + + pop bp + ret ! Return to monitor as if nothing much happened + +! Transform BCD number in al to a regular value in ax. +bcd: movb ah, al + shrb ah, #4 + andb al, #0x0F + .data1 0xD5,10 ! aad ! ax = (al >> 4) * 10 + (al & 0x0F) + ret ! (BUG: assembler messes up aad & aam!) + +! Support function for Minix-386 to make a BIOS int 13 call (disk I/O). +bios13: + mov bp, sp + call prot2real + sti ! Enable interrupts + + mov ax, 8(bp) ! Load parameters + mov bx, 10(bp) + mov cx, 12(bp) + mov dx, 14(bp) + mov es, 16(bp) + int 0x13 ! Make the BIOS call + mov 8(bp), ax ! Save results + mov 10(bp), bx + mov 12(bp), cx + mov 14(bp), dx + mov 16(bp), es + + cli ! Disable interrupts + call real2prot + mov ax, #DS_SELECTOR ! Kernel data + mov ds, ax + .data1 o32 + retf ! Return to the kernel + +! Support function for Minix-386 to make an 8086 interrupt call. +int86: + mov bp, sp + call prot2real + + .data1 o32 + xor ax, ax + mov es, ax ! Vector & BIOS data segments + .data1 o32 + eseg mov 0x046C, ax ! Clear BIOS clock tick counter + + sti ! Enable interrupts + + movb al, #0xCD ! INT instruction + movb ah, 8(bp) ! Interrupt number? + testb ah, ah + jnz 0f ! Nonzero if INT, otherwise far call + push cs + push #intret+2 ! Far return address + .data1 o32 + push 12(bp) ! Far driver address + mov ax, #0x90CB ! RETF; NOP +0: + cseg cmp ax, intret ! Needs to be changed? + je 0f ! If not then avoid a huge I-cache stall + cseg mov intret, ax ! Patch `INT n' or `RETF; NOP' into code + jmp .+2 ! Clear instruction queue +0: + mov ds, 16(bp) ! Load parameters + mov es, 18(bp) + .data1 o32 + mov ax, 20(bp) + .data1 o32 + mov bx, 24(bp) + .data1 o32 + mov cx, 28(bp) + .data1 o32 + mov dx, 32(bp) + .data1 o32 + mov si, 36(bp) + .data1 o32 + mov di, 40(bp) + .data1 o32 + mov bp, 44(bp) + +intret: int 0xFF ! Do the interrupt or far call + + .data1 o32 ! Save results + push bp + .data1 o32 + pushf + mov bp, sp + .data1 o32 + pop 8+8(bp) ! eflags + mov 8+16(bp), ds + mov 8+18(bp), es + .data1 o32 + mov 8+20(bp), ax + .data1 o32 + mov 8+24(bp), bx + .data1 o32 + mov 8+28(bp), cx + .data1 o32 + mov 8+32(bp), dx + .data1 o32 + mov 8+36(bp), si + .data1 o32 + mov 8+40(bp), di + .data1 o32 + pop 8+44(bp) ! ebp + + cli ! Disable interrupts + + xor ax, ax + mov ds, ax ! Vector & BIOS data segments + .data1 o32 + mov cx, 0x046C ! Collect lost clock ticks in ecx + + mov ax, ss + mov ds, ax ! Restore monitor ds + call real2prot + mov ax, #DS_SELECTOR ! Kernel data + mov ds, ax + .data1 o32 + retf ! Return to the kernel + +! Switch from real to protected mode. +real2prot: + movb ah, #0x02 ! Code for A20 enable + call gate_A20 + + lgdt p_gdt_desc ! Global descriptor table + .data1 o32 + mov ax, pdbr ! Load page directory base register + .data1 0x0F,0x22,0xD8 ! mov cr3, eax + .data1 0x0F,0x20,0xC0 ! mov eax, cr0 + .data1 o32 + xchg ax, msw ! Exchange real mode msw for protected mode msw + .data1 0x0F,0x22,0xC0 ! mov cr0, eax + jmpf cs_prot, MCS_SELECTOR ! Set code segment selector +cs_prot: + mov ax, #SS_SELECTOR ! Set data selectors + mov ds, ax + mov es, ax + mov ss, ax + ret + +! Switch from protected to real mode. +prot2real: + lidt p_idt_desc ! Real mode interrupt vectors + .data1 0x0F,0x20,0xD8 ! mov eax, cr3 + .data1 o32 + mov pdbr, ax ! Save page directory base register + .data1 0x0F,0x20,0xC0 ! mov eax, cr0 + .data1 o32 + xchg ax, msw ! Exchange protected mode msw for real mode msw + .data1 0x0F,0x22,0xC0 ! mov cr0, eax + jmpf cs_real, 0xDEAD ! Reload cs register +cs_real: + mov ax, #0xBEEF +ds_real: + mov ds, ax ! Reload data segment registers + mov es, ax + mov ss, ax + + xorb ah, ah ! Code for A20 disable + !jmp gate_A20 + +! Enable (ah = 0x02) or disable (ah = 0x00) the A20 address line. +gate_A20: + cmp bus, #2 ! PS/2 bus? + je gate_PS_A20 + call kb_wait + movb al, #0xD1 ! Tell keyboard that a command is coming + outb 0x64 + call kb_wait + movb al, #0xDD ! 0xDD = A20 disable code if ah = 0x00 + orb al, ah ! 0xDF = A20 enable code if ah = 0x02 + outb 0x60 + call kb_wait + movb al, #0xFF ! Pulse output port + outb 0x64 + call kb_wait ! Wait for the A20 line to settle down + ret +kb_wait: + inb 0x64 + testb al, #0x02 ! Keyboard input buffer full? + jnz kb_wait ! If so, wait + ret + +gate_PS_A20: ! The PS/2 can twiddle A20 using port A + inb 0x92 ! Read port A + andb al, #0xFD + orb al, ah ! Set A20 bit to the required state + outb 0x92 ! Write port A + jmp .+2 ! Small delay +A20ok: inb 0x92 ! Check port A + andb al, #0x02 + cmpb al, ah ! A20 line settled down to the new state? + jne A20ok ! If not then wait + ret + +! void int15(bios_env_t *ep) +! Do an "INT 15" call, primarily for APM (Power Management). +.define _int15 +_int15: + push si ! Save callee-save register si + mov si, sp + mov si, 4(si) ! ep + mov ax, (si) ! ep->ax + mov bx, 2(si) ! ep->bx + mov cx, 4(si) ! ep->cx + int 0x15 ! INT 0x15 BIOS call + pushf ! Save flags + mov (si), ax ! ep->ax + mov 2(si), bx ! ep->bx + mov 4(si), cx ! ep->cx + pop 6(si) ! ep->flags + pop si ! Restore + ret + +.data + .ascii "(null)\0" ! Just in case someone follows a null pointer + .align 2 +c60: .data2 60 ! Constants for MUL and DIV +c1024: .data2 1024 +c1080: .data2 1080 +c19663: .data2 19663 + +! Global descriptor tables. + UNSET = 0 ! Must be computed + +! For "Extended Memory Block Move". +x_gdt: +x_null_desc: + ! Null descriptor + .data2 0x0000, 0x0000 + .data1 0x00, 0x00, 0x00, 0x00 +x_gdt_desc: + ! Descriptor for this descriptor table + .data2 6*8-1, UNSET + .data1 UNSET, 0x00, 0x00, 0x00 +x_src_desc: + ! Source segment descriptor + .data2 0xFFFF, UNSET + .data1 UNSET, 0x92, 0x00, 0x00 +x_dst_desc: + ! Destination segment descriptor + .data2 0xFFFF, UNSET + .data1 UNSET, 0x92, 0x00, 0x00 +x_bios_desc: + ! BIOS segment descriptor (scratch for int 0x15) + .data2 UNSET, UNSET + .data1 UNSET, UNSET, UNSET, UNSET +x_ss_desc: + ! BIOS stack segment descriptor (scratch for int 0x15) + .data2 UNSET, UNSET + .data1 UNSET, UNSET, UNSET, UNSET + +! Protected mode descriptor table. +p_gdt: +p_null_desc: + ! Null descriptor + .data2 0x0000, 0x0000 + .data1 0x00, 0x00, 0x00, 0x00 +p_gdt_desc: + ! Descriptor for this descriptor table + .data2 8*8-1, UNSET + .data1 UNSET, 0x00, 0x00, 0x00 +p_idt_desc: + ! Real mode interrupt descriptor table descriptor + .data2 0x03FF, 0x0000 + .data1 0x00, 0x00, 0x00, 0x00 +p_ds_desc: + ! Kernel data segment descriptor (4 Gb flat) + .data2 0xFFFF, UNSET + .data1 UNSET, 0x92, 0xCF, 0x00 +p_es_desc: + ! Physical memory descriptor (4 Gb flat) + .data2 0xFFFF, 0x0000 + .data1 0x00, 0x92, 0xCF, 0x00 +p_ss_desc: + ! Monitor data segment descriptor (64 kb flat) + .data2 0xFFFF, UNSET + .data1 UNSET, 0x92, 0x00, 0x00 +p_cs_desc: + ! Kernel code segment descriptor (4 Gb flat) + .data2 0xFFFF, UNSET + .data1 UNSET, 0x9A, 0xCF, 0x00 +p_mcs_desc: + ! Monitor code segment descriptor (64 kb flat) + .data2 0xFFFF, UNSET + .data1 UNSET, 0x9A, 0x00, 0x00 + +.bss + .comm old_vid_mode, 2 ! Video mode at startup + .comm cur_vid_mode, 2 ! Current video mode + .comm dev_state, 2 ! Device state: reset (-1), closed (0), open (1) + .comm sectors, 2 ! # sectors of current device + .comm secspcyl, 2 ! (Sectors * heads) of current device + .comm msw, 4 ! Saved machine status word (cr0) + .comm pdbr, 4 ! Saved page directory base register (cr3) + .comm escape, 2 ! Escape typed? + .comm bus, 2 ! Saved return value of _get_bus + .comm unchar, 2 ! Char returned by ungetch(c) + .comm line, 2 ! Serial line I/O port to copy console I/O to. diff --git a/boot/bootimage.c b/boot/bootimage.c new file mode 100755 index 000000000..e29bf3b3d --- /dev/null +++ b/boot/bootimage.c @@ -0,0 +1,712 @@ +/* bootimage.c - Load an image and start it. Author: Kees J. Bot + * 19 Jan 1992 + */ +#define BIOS 1 /* Can only be used under the BIOS. */ +#define nil 0 +#define _POSIX_SOURCE 1 +#define _MINIX 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rawfs.h" +#include "image.h" +#include "boot.h" + +static int block_size = 0; + +#define click_shift clck_shft /* 7 char clash with click_size. */ + +/* Some kernels have extra features: */ +#define K_I386 0x0001 /* Make the 386 transition before you call me. */ +#define K_CLAIM 0x0002 /* I will acquire my own bss pages, thank you. */ +#define K_CHMEM 0x0004 /* This kernel listens to chmem for its stack size. */ +#define K_HIGH 0x0008 /* Load mm, fs, etc. in extended memory. */ +#define K_HDR 0x0010 /* No need to patch sizes, kernel uses the headers. */ +#define K_RET 0x0020 /* Returns to the monitor on reboot. */ +#define K_INT86 0x0040 /* Requires generic INT support. */ +#define K_MEML 0x0080 /* Pass a list of free memory. */ +#define K_BRET 0x0100 /* New monitor code on shutdown in boot parameters. */ +#define K_ALL 0x01FF /* All feature bits this monitor supports. */ + + +/* Data about the different processes. */ + +#define PROCESS_MAX 16 /* Must match the space in kernel/mpx.x */ +#define KERNEL 0 /* The first process is the kernel. */ +#define FS 2 /* The third must be fs. */ + +struct process { /* Per-process memory adresses. */ + u32_t entry; /* Entry point. */ + u32_t cs; /* Code segment. */ + u32_t ds; /* Data segment. */ + u32_t data; /* To access the data segment. */ + u32_t end; /* End of this process, size = (end - cs). */ +} process[PROCESS_MAX]; +int n_procs; /* Number of processes. */ + +/* Magic numbers in process' data space. */ +#define MAGIC_OFF 0 /* Offset of magic # in data seg. */ +#define CLICK_OFF 2 /* Offset in kernel text to click_shift. */ +#define FLAGS_OFF 4 /* Offset in kernel text to flags. */ +#define KERNEL_D_MAGIC 0x526F /* Kernel magic number. */ + +/* Offsets of sizes to be patched into kernel and fs. */ +#define P_SIZ_OFF 0 /* Process' sizes into kernel data. */ +#define P_INIT_OFF 4 /* Init cs & sizes into fs data. */ + + +#define between(a, c, z) ((unsigned) ((c) - (a)) <= ((z) - (a))) + +void pretty_image(char *image) +/* Pretty print the name of the image to load. Translate '/' and '_' to + * space, first letter goes uppercase. An 'r' before a digit prints as + * 'revision'. E.g. 'minix/1.6.16r10' -> 'Minix 1.6.16 revision 10'. + * The idea is that the part before the 'r' is the official Minix release + * and after the 'r' you can put version numbers for your own changes. + */ +{ + int up= 0, c; + + while ((c= *image++) != 0) { + if (c == '/' || c == '_') c= ' '; + + if (c == 'r' && between('0', *image, '9')) { + printf(" revision "); + continue; + } + if (!up && between('a', c, 'z')) c= c - 'a' + 'A'; + + if (between('A', c, 'Z')) up= 1; + + putch(c); + } +} + +void raw_clear(u32_t addr, u32_t count) +/* Clear "count" bytes at absolute address "addr". */ +{ + static char zeros[128]; + u32_t dst; + u32_t zct; + + zct= sizeof(zeros); + if (zct > count) zct= count; + raw_copy(addr, mon2abs(&zeros), zct); + count-= zct; + + while (count > 0) { + dst= addr + zct; + if (zct > count) zct= count; + raw_copy(dst, addr, zct); + count-= zct; + zct*= 2; + } +} + +/* Align a to a multiple of n (a power of 2): */ +#define align(a, n) (((u32_t)(a) + ((u32_t)(n) - 1)) & ~((u32_t)(n) - 1)) +unsigned click_shift; +unsigned click_size; /* click_size = Smallest kernel memory object. */ +unsigned k_flags; /* Not all kernels are created equal. */ +u32_t reboot_code; /* Obsolete reboot code return pointer. */ + +int params2params(char *params, size_t psize) +/* Repackage the environment settings for the kernel. */ +{ + size_t i, n; + environment *e; + char *name, *value; + dev_t dev; + + i= 0; + for (e= env; e != nil; e= e->next) { + name= e->name; + value= e->value; + + if (!(e->flags & E_VAR)) continue; + + if (e->flags & E_DEV) { + if ((dev= name2dev(value)) == -1) return 0; + value= ul2a10((u16_t) dev); + } + + n= i + strlen(name) + 1 + strlen(value) + 1; + if (n < psize) { + strcpy(params + i, name); + strcat(params + i, "="); + strcat(params + i, value); + } + i= n; + } + + if (!(k_flags & K_MEML)) { + /* Require old memory size variables. */ + + value= ul2a10((mem[0].base + mem[0].size) / 1024); + n= i + 7 + 1 + strlen(value) + 1; + if (n < psize) { + strcpy(params + i, "memsize="); + strcat(params + i, value); + } + i= n; + value= ul2a10(mem[1].size / 1024); + n= i + 7 + 1 + strlen(value) + 1; + if (n < psize) { + strcpy(params + i, "emssize="); + strcat(params + i, value); + } + i= n; + } + + if (i >= psize) { + printf("Too many boot parameters\n"); + return 0; + } + params[i]= 0; /* End marked with empty string. */ + return 1; +} + +void patch_sizes(void) +/* Patch sizes of each process into kernel data space, kernel ds into kernel + * text space, and sizes of init into data space of fs. All the patched + * numbers are based on the kernel click size, not hardware segments. + */ +{ + u16_t text_size, data_size; + int i; + struct process *procp, *initp; + u32_t doff; + + if (k_flags & K_HDR) return; /* Uses the headers. */ + + /* Patch text and data sizes of the processes into kernel data space. + */ + doff= process[KERNEL].data + P_SIZ_OFF; + + for (i= 0; i < n_procs; i++) { + procp= &process[i]; + text_size= (procp->ds - procp->cs) >> click_shift; + data_size= (procp->end - procp->ds) >> click_shift; + + /* Two words per process, the text and data size: */ + put_word(doff, text_size); doff+= 2; + put_word(doff, data_size); doff+= 2; + + initp= procp; /* The last process must be init. */ + } + + if (k_flags & (K_HIGH|K_MEML)) return; /* Doesn't need FS patching. */ + + /* Patch cs and sizes of init into fs data. */ + put_word(process[FS].data + P_INIT_OFF+0, initp->cs >> click_shift); + put_word(process[FS].data + P_INIT_OFF+2, text_size); + put_word(process[FS].data + P_INIT_OFF+4, data_size); +} + +int selected(char *name) +/* True iff name has no label or the proper label. */ +{ + char *colon, *label; + int cmp; + + if ((colon= strchr(name, ':')) == nil) return 1; + if ((label= b_value("label")) == nil) return 1; + + *colon= 0; + cmp= strcmp(label, name); + *colon= ':'; + return cmp == 0; +} + +u32_t proc_size(struct image_header *hdr) +/* Return the size of a process in sectors as found in an image. */ +{ + u32_t len= hdr->process.a_text; + + if (hdr->process.a_flags & A_PAL) len+= hdr->process.a_hdrlen; + if (hdr->process.a_flags & A_SEP) len= align(len, SECTOR_SIZE); + len= align(len + hdr->process.a_data, SECTOR_SIZE); + + return len >> SECTOR_SHIFT; +} + +off_t image_off, image_size; +u32_t (*vir2sec)(u32_t vsec); /* Where is a sector on disk? */ + +u32_t file_vir2sec(u32_t vsec) +/* Translate a virtual sector number to an absolute disk sector. */ +{ + off_t blk; + + if(!block_size) { errno = 0; return -1; } + + if ((blk= r_vir2abs(vsec / RATIO(block_size))) == -1) { + errno= EIO; + return -1; + } + return blk == 0 ? 0 : lowsec + blk * RATIO(block_size) + vsec % RATIO(block_size); +} + +u32_t flat_vir2sec(u32_t vsec) +/* Simply add an absolute sector offset to vsec. */ +{ + return lowsec + image_off + vsec; +} + +char *get_sector(u32_t vsec) +/* Read a sector "vsec" from the image into memory and return its address. + * Return nil on error. (This routine tries to read an entire track, so + * the next request is usually satisfied from the track buffer.) + */ +{ + u32_t sec; + int r; + static char buf[32 * SECTOR_SIZE]; + static size_t count; /* Number of sectors in the buffer. */ + static u32_t bufsec; /* First Sector now in the buffer. */ + + if (vsec == 0) count= 0; /* First sector; initialize. */ + + if ((sec= (*vir2sec)(vsec)) == -1) return nil; + + if (sec == 0) { + /* A hole. */ + count= 0; + memset(buf, 0, SECTOR_SIZE); + return buf; + } + + /* Can we return a sector from the buffer? */ + if ((sec - bufsec) < count) { + return buf + ((size_t) (sec - bufsec) << SECTOR_SHIFT); + } + + /* Not in the buffer. */ + count= 0; + bufsec= sec; + + /* Read a whole track if possible. */ + while (++count < 32 && !dev_boundary(bufsec + count)) { + vsec++; + if ((sec= (*vir2sec)(vsec)) == -1) break; + + /* Consecutive? */ + if (sec != bufsec + count) break; + } + + /* Actually read the sectors. */ + if ((r= readsectors(mon2abs(buf), bufsec, count)) != 0) { + readerr(bufsec, r); + count= 0; + errno= 0; + return nil; + } + return buf; +} + +int get_clickshift(u32_t ksec, struct image_header *hdr) +/* Get the click shift and special flags from kernel text. */ +{ + char *textp; + + if ((textp= get_sector(ksec)) == nil) return 0; + + if (hdr->process.a_flags & A_PAL) textp+= hdr->process.a_hdrlen; + click_shift= * (u16_t *) (textp + CLICK_OFF); + k_flags= * (u16_t *) (textp + FLAGS_OFF); + + if ((k_flags & ~K_ALL) != 0) { + printf("%s requires features this monitor doesn't offer\n", + hdr->name); + return 0; + } + + if (click_shift < HCLICK_SHIFT || click_shift > 16) { + printf("%s click size is bad\n", hdr->name); + errno= 0; + return 0; + } + + click_size= 1 << click_shift; + + return 1; +} + +int get_segment(u32_t *vsec, long *size, u32_t *addr, u32_t limit) +/* Read *size bytes starting at virtual sector *vsec to memory at *addr. */ +{ + char *buf; + size_t cnt, n; + + cnt= 0; + while (*size > 0) { + if (cnt == 0) { + if ((buf= get_sector((*vsec)++)) == nil) return 0; + cnt= SECTOR_SIZE; + } + if (*addr + click_size > limit) { errno= ENOMEM; return 0; } + n= click_size; + if (n > cnt) n= cnt; + raw_copy(*addr, mon2abs(buf), n); + *addr+= n; + *size-= n; + buf+= n; + cnt-= n; + } + + /* Zero extend to a click. */ + n= align(*addr, click_size) - *addr; + raw_clear(*addr, n); + *addr+= n; + *size-= n; + return 1; +} + +void exec_image(char *image) +/* Get a Minix image into core, patch it up and execute. */ +{ + int i; + struct image_header hdr; + char *buf; + u32_t vsec, addr, limit, aout, n; + struct process *procp; /* Process under construction. */ + long a_text, a_data, a_bss, a_stack; + int banner= 0; + long processor= a2l(b_value("processor")); + u16_t mode; + char *console; + char params[SECTOR_SIZE]; + extern char *sbrk(int); + + /* The stack is pretty deep here, so check if heap and stack collide. */ + (void) sbrk(0); + + printf("\nLoading "); + pretty_image(image); + printf(".\n\n"); + + vsec= 0; /* Load this sector from image next. */ + addr= mem[0].base; /* Into this memory block. */ + limit= mem[0].base + mem[0].size; + if (limit > caddr) limit= caddr; + + /* Allocate and clear the area where the headers will be placed. */ + aout = (limit -= PROCESS_MAX * A_MINHDR); + + /* Clear the area where the headers will be placed. */ + raw_clear(aout, PROCESS_MAX * A_MINHDR); + + /* Read the many different processes: */ + for (i= 0; vsec < image_size; i++) { + if (i == PROCESS_MAX) { + printf("There are more then %d programs in %s\n", + PROCESS_MAX, image); + errno= 0; + return; + } + procp= &process[i]; + + /* Read header. */ + for (;;) { + if ((buf= get_sector(vsec++)) == nil) return; + + memcpy(&hdr, buf, sizeof(hdr)); + + if (BADMAG(hdr.process)) { errno= ENOEXEC; return; } + + /* Check the optional label on the process. */ + if (selected(hdr.name)) break; + + /* Bad label, skip this process. */ + vsec+= proc_size(&hdr); + } + + /* Sanity check: an 8086 can't run a 386 kernel. */ + if (hdr.process.a_cpu == A_I80386 && processor < 386) { + printf("You can't run a 386 kernel on this 80%ld\n", + processor); + errno= 0; + return; + } + + /* Get the click shift from the kernel text segment. */ + if (i == KERNEL) { + if (!get_clickshift(vsec, &hdr)) return; + addr= align(addr, click_size); + } + + /* Save a copy of the header for the kernel, with a_syms + * misused as the address where the process is loaded at. + */ + hdr.process.a_syms= addr; + raw_copy(aout + i * A_MINHDR, mon2abs(&hdr.process), A_MINHDR); + + if (!banner) { + printf(" cs ds text data bss"); + if (k_flags & K_CHMEM) printf(" stack"); + putch('\n'); + banner= 1; + } + + /* Segment sizes. */ + a_text= hdr.process.a_text; + a_data= hdr.process.a_data; + a_bss= hdr.process.a_bss; + if (k_flags & K_CHMEM) { + a_stack= hdr.process.a_total - a_data - a_bss; + if (!(hdr.process.a_flags & A_SEP)) a_stack-= a_text; + } else { + a_stack= 0; + } + + /* Collect info about the process to be. */ + procp->cs= addr; + + /* Process may be page aligned so that the text segment contains + * the header, or have an unmapped zero page against vaxisms. + */ + procp->entry= hdr.process.a_entry; + if (hdr.process.a_flags & A_PAL) a_text+= hdr.process.a_hdrlen; + if (hdr.process.a_flags & A_UZP) procp->cs-= click_size; + + /* Separate I&D: two segments. Common I&D: only one. */ + if (hdr.process.a_flags & A_SEP) { + /* Read the text segment. */ + if (!get_segment(&vsec, &a_text, &addr, limit)) return; + + /* The data segment follows. */ + procp->ds= addr; + if (hdr.process.a_flags & A_UZP) procp->ds-= click_size; + procp->data= addr; + } else { + /* Add text to data to form one segment. */ + procp->data= addr + a_text; + procp->ds= procp->cs; + a_data+= a_text; + } + + printf("%06lx %06lx %7ld %7ld %7ld", + procp->cs, procp->ds, + hdr.process.a_text, hdr.process.a_data, + hdr.process.a_bss + ); + if (k_flags & K_CHMEM) printf(" %8ld", a_stack); + + printf(" %s\n", hdr.name); + + /* Read the data segment. */ + if (!get_segment(&vsec, &a_data, &addr, limit)) return; + + /* Make space for bss and stack unless... */ + if (i != KERNEL && (k_flags & K_CLAIM)) a_bss= a_stack= 0; + + /* Note that a_data may be negative now, but we can look at it + * as -a_data bss bytes. + */ + + /* Compute the number of bss clicks left. */ + a_bss+= a_data; + n= align(a_bss, click_size); + a_bss-= n; + + /* Zero out bss. */ + if (addr + n > limit) { errno= ENOMEM; return; } + raw_clear(addr, n); + addr+= n; + + /* And the number of stack clicks. */ + a_stack+= a_bss; + n= align(a_stack, click_size); + a_stack-= n; + + /* Add space for the stack. */ + addr+= n; + + /* Process endpoint. */ + procp->end= addr; + + if (i == 0 && (k_flags & K_HIGH)) { + /* Load the rest in extended memory. */ + addr= mem[1].base; + limit= mem[1].base + mem[1].size; + } + } + + if ((n_procs= i) == 0) { + printf("There are no programs in %s\n", image); + errno= 0; + return; + } + + /* Check the kernel magic number. */ + if (get_word(process[KERNEL].data + MAGIC_OFF) != KERNEL_D_MAGIC) { + printf("Kernel magic number is incorrect\n"); + errno= 0; + return; + } + + /* Patch sizes, etc. into kernel data. */ + patch_sizes(); + +#if !DOS + if (!(k_flags & K_MEML)) { + /* Copy the a.out headers to the old place. */ + raw_copy(HEADERPOS, aout, PROCESS_MAX * A_MINHDR); + } +#endif + + /* Run the trailer function just before starting Minix. */ + if (!run_trailer()) { errno= 0; return; } + + /* Translate the boot parameters to what Minix likes best. */ + if (!params2params(params, sizeof(params))) { errno= 0; return; } + + /* Set the video to the required mode. */ + if ((console= b_value("console")) == nil || (mode= a2x(console)) == 0) { + mode= strcmp(b_value("chrome"), "color") == 0 ? COLOR_MODE : + MONO_MODE; + } + set_mode(mode); + + /* Close the disk. */ + (void) dev_close(); + + /* Minix. */ + minix(process[KERNEL].entry, process[KERNEL].cs, + process[KERNEL].ds, params, sizeof(params), aout); + + if (!(k_flags & K_BRET)) { + extern u32_t reboot_code; + raw_copy(mon2abs(params), reboot_code, sizeof(params)); + } + parse_code(params); + + /* Return from Minix. Things may have changed, so assume nothing. */ + fsok= -1; + errno= 0; +} + +ino_t latest_version(char *version, struct stat *stp) +/* Recursively read the current directory, selecting the newest image on + * the way up. (One can't use r_stat while reading a directory.) + */ +{ + char name[NAME_MAX + 1]; + ino_t ino, newest; + time_t mtime; + + if ((ino= r_readdir(name)) == 0) { stp->st_mtime= 0; return 0; } + + newest= latest_version(version, stp); + mtime= stp->st_mtime; + r_stat(ino, stp); + + if (S_ISREG(stp->st_mode) && stp->st_mtime > mtime) { + newest= ino; + strcpy(version, name); + } else { + stp->st_mtime= mtime; + } + return newest; +} + +char *select_image(char *image) +/* Look image up on the filesystem, if it is a file then we're done, but + * if its a directory then we want the newest file in that directory. If + * it doesn't exist at all, then see if it is 'number:number' and get the + * image from that absolute offset off the disk. + */ +{ + ino_t image_ino; + struct stat st; + + image= strcpy(malloc((strlen(image) + 1 + NAME_MAX + 1) + * sizeof(char)), image); + + if (fsok == -1) fsok= r_super(&block_size) != 0; + if (!fsok || (image_ino= r_lookup(ROOT_INO, image)) == 0) { + char *size; + + if (numprefix(image, &size) && *size++ == ':' + && numeric(size)) { + vir2sec= flat_vir2sec; + image_off= a2l(image); + image_size= a2l(size); + strcpy(image, "Minix"); + return image; + } + if (!fsok) + printf("No image selected\n"); + else + printf("Can't load %s: %s\n", image, unix_err(errno)); + goto bail_out; + } + + r_stat(image_ino, &st); + if (!S_ISREG(st.st_mode)) { + char *version= image + strlen(image); + char dots[NAME_MAX + 1]; + + if (!S_ISDIR(st.st_mode)) { + printf("%s: %s\n", image, unix_err(ENOTDIR)); + goto bail_out; + } + (void) r_readdir(dots); + (void) r_readdir(dots); /* "." & ".." */ + *version++= '/'; + *version= 0; + if ((image_ino= latest_version(version, &st)) == 0) { + printf("There are no images in %s\n", image); + goto bail_out; + } + r_stat(image_ino, &st); + } + vir2sec= file_vir2sec; + image_size= (st.st_size + SECTOR_SIZE - 1) >> SECTOR_SHIFT; + return image; +bail_out: + free(image); + return nil; +} + +void bootminix(void) +/* Load Minix and run it. (Given the size of this program it is surprising + * that it ever gets to that.) + */ +{ + char *image; + + if ((image= select_image(b_value("image"))) == nil) return; + + exec_image(image); + + switch (errno) { + case ENOEXEC: + printf("%s contains a bad program header\n", image); + break; + case ENOMEM: + printf("Not enough memory to load %s\n", image); + break; + case EIO: + printf("Unsuspected EOF on %s\n", image); + case 0: + /* No error or error already reported. */; + } + free(image); +} + +/* + * $PchId: bootimage.c,v 1.10 2002/02/27 19:39:09 philip Exp $ + */ diff --git a/boot/doshead.s b/boot/doshead.s new file mode 100755 index 000000000..c1a41ec38 --- /dev/null +++ b/boot/doshead.s @@ -0,0 +1,1369 @@ +! Doshead.s - DOS & BIOS support for boot.c Author: Kees J. Bot +! +! +! This file contains the startup and low level support for the secondary +! boot program. It contains functions for disk, tty and keyboard I/O, +! copying memory to arbitrary locations, etc. +! +! This runs under MS-DOS as a .COM file. A .COM file is what Minix calls +! a common I&D executable, except that the first 256 bytes contains DOS +! thingies. +! +.sect .text; .sect .rom; .sect .data; .sect .bss + + K_I386 = 0x0001 ! Call Minix in 386 mode + STACK = 16384 ! Number of bytes for the stack + + DS_SELECTOR = 3*8 ! Kernel data selector + ES_SELECTOR = 4*8 ! Flat 4 Gb + SS_SELECTOR = 5*8 ! Monitor stack + CS_SELECTOR = 6*8 ! Kernel code + MCS_SELECTOR= 7*8 ! Monitor code + + ESC = 0x1B ! Escape character + +! Imported variables and functions: +.extern _caddr, _daddr, _runsize, _edata, _end ! Runtime environment +.extern _k_flags ! Special kernel flags +.extern _mem ! Free memory list +.extern _vdisk ! Name of the virtual disk + +.sect .text + +.use16 ! Tell 386 assembler we're in 16-bit mode + +.define _PSP +_PSP: + .space 256 ! Program Segment Prefix + +dosboot: + cld ! C compiler wants UP + xor ax, ax ! Zero + mov di, _edata ! Start of bss is at end of data + mov cx, _end ! End of bss (begin of heap) + sub cx, di ! Number of bss bytes + shr cx, 1 ! Number of words + rep stos ! Clear bss + cmp sp, _end+STACK + jb 0f + mov sp, _end+STACK ! "chmem" to 16 kb +0: + +! Are we called with the /U option? + movb cl, (_PSP+0x80) ! Argument byte count + xorb ch, ch + mov bx, _PSP+0x81 ! Argument string +0: jcxz notuflag + cmpb (bx), 0x20 ! Whitespace? + ja 1f + inc bx + dec cx + jmp 0b +1: cmp cx, 2 ! '/U' is two bytes + jne notuflag + cmpb (bx), 0x2F ! '/'? + jne notuflag + movb al, 1(bx) + andb al, ~0x20 ! Ignore case + cmpb al, 0x55 ! 'U'? + jne notuflag + jmp keepumb ! Go grab an UMB +notuflag: + +! Remember the current video mode for restoration on exit. + movb ah, 0x0F ! Get current video mode + int 0x10 + andb al, 0x7F ! Mask off bit 7 (no blanking) + movb (old_vid_mode), al + movb (cur_vid_mode), al + +! We require at least MS-DOS 3.0. + mov ax, 0x3000 ! Get DOS version + int 0x21 + cmpb al, 3 ! DOS 3.0+ ? + jae dosok + push tellbaddos + call _printf + jmp quit +.sect .rom +tellbaddos: .ascii "MS-DOS 3.0 or better required\n\0" +.sect .text +dosok: + +! Find out how much "low" memory there is available, where it starts and +! where it ends. + mov di, _mem ! di = memory list + mov ax, _PSP+0x80 ! From PSP:80 to next PSP is ours + mov dx, ds + call seg2abs + mov (di), ax + mov 2(di), dx ! mem[0].base = ds * 16 + 0x80 + xor ax, ax + mov dx, (_PSP+2) ! First in-use segment far above us + call seg2abs + sub ax, (di) + sbb dx, 2(di) ! Minus base gives size + mov 4(di), ax + mov 6(di), dx ! mem[1].size = free low memory size + +! Give C code access to the code segment, data segment and the size of this +! process. + xor ax, ax + mov dx, cs + call seg2abs + mov (_caddr+0), ax + mov (_caddr+2), dx + xor ax, ax + mov dx, ds + call seg2abs + mov (_daddr+0), ax + mov (_daddr+2), dx + mov ax, sp + mov dx, ss ! End of stack = end of program + call seg2abs + sub ax, (_caddr+0) + sbb dx, (_caddr+2) ! Minus start of our code + mov (_runsize+0), ax + mov (_runsize+2), dx ! Is our size + +! Patch the regular _getprocessor library routine to jump to 'getprocessor', +! that checks if we happen to be in a V8086 straightjacket by returning '86'. + cseg movb (_getprocessor+0), 0xE9 + mov ax, getprocessor + sub ax, _getprocessor+3 + cseg mov (_getprocessor+1), ax + +! Grab the largest chunk of extended memory available. + call _getprocessor + cmp ax, 286 ! Only 286s and above have extended memory + jb no_ext + mov ax, 0x4300 ! XMS driver check + int 0x2F + cmpb al, 0x80 ! XMS driver exists? + je xmsthere +get_ext: ! No driver, so can use all ext memory directly + call _getprocessor + cmp ax, 486 ! Assume 486s were the first to have >64M + jb small_ext ! (It helps to be paranoid when using the BIOS) +big_ext: + mov ax, 0xE801 ! Code for get memory size for >64M + int 0x15 ! ax = mem at 1M per 1K, bx = mem at 16M per 64K + jnc got_ext +small_ext: + movb ah, 0x88 ! Code for get extended memory size + clc ! Carry will stay clear if call exists + int 0x15 ! Returns size (in K) in ax for AT's + jc no_ext + test ax, ax ! An AT with no extended memory? + jz no_ext + xor bx, bx ! bx = mem above 16M per 64K = 0 +got_ext: + mov cx, ax ! cx = copy of ext mem at 1M + mov 10(di), 0x0010 ! mem[1].base = 0x00100000 (1M) + mul (c1024) + mov 12(di), ax ! mem[1].size = "ext mem at 1M" * 1024 + mov 14(di), dx + test bx, bx + jz no_ext ! No more ext mem above 16M? + cmp cx, 15*1024 ! Chunks adjacent? (precisely 15M at 1M?) + je adj_ext + mov 18(di), 0x0100 ! mem[2].base = 0x01000000 (16M) + mov 22(di), bx ! mem[2].size = "ext mem at 16M" * 64K + jmp no_ext +adj_ext: + add 14(di), bx ! Add ext mem above 16M to mem below 16M +no_ext: + jmp gotxms + +xmsthere: + mov ax, 0x4310 ! Get XMS driver address + int 0x2F + mov (xms_driver+0), bx + mov (xms_driver+2), es + push ds + pop es + movb ah, 0x08 ! Query free extended memory + xorb bl, bl + callf (xms_driver) + testb bl, bl + jnz xmserr + push ax ! ax = size of largest block in kb + mul (c1024) + mov 12(di), ax + mov 14(di), dx ! mem[1].size = ax * 1024 + pop dx ! dx = size of largest block in kb + movb ah, 0x09 ! Allocate XMS block of size dx + callf (xms_driver) + test ax, ax + jz xmserr + mov (xms_handle), dx ! Save handle + movb ah, 0x0C ! Lock XMS block (handle in dx) + callf (xms_driver) + test ax, ax + jz xmserr + mov 8(di), bx + mov 10(di), dx ! mem[1].base = Address of locked block +gotxms: + +! If we're running in a DOS box then they're might be an Upper Memory Block +! we can use. Every little bit helps when in real mode. + mov ax, 20(di) + or ax, 22(di) ! Can we use mem[2]? + jnz gotumb + mov dx, 0xFFFF ! dx = Maximum size, i.e. gimme all + call getumb ! Get UMB, dx = segment, cx = length + test cx, cx ! Did we get a block? + jz gotumb + xor ax, ax ! dx:ax = memory block + call seg2abs + mov 16(di), ax + mov 18(di), dx ! mem[2].base = memory block base + mov dx, cx + xor ax, ax ! dx:ax = length of memory block + call seg2abs + mov 20(di), ax + mov 22(di), dx ! mem[2].size = memory block length +gotumb: + +! Set up an INT 24 "critical error" handler that returns "fail". This way +! Minix won't suffer from "(A)bort, (R)etry, (I)nfluence with a large hammer?". + mov (0x007C), 0x03B0 ! movb al, 0x03 (fail code) + movb (0x007E), 0xCF ! iret + movb ah, 0x25 ! Set interrupt vector + mov dx, 0x007C ! ds:dx = ds:0x007C = interrupt handler + int 0x21 + +! Time to switch to a higher level language (not much higher) + call _boot + +! void ..exit(int status) +! Exit the monitor by returning to DOS. +.define _exit, __exit, ___exit ! Make various compilers happy +_exit: +__exit: +___exit: + mov dx, (xms_handle) + cmp dx, -1 ! Is there an ext mem block in use? + je nohandle + movb ah, 0x0D ! Unlock extended memory block + callf (xms_driver) + mov dx, (xms_handle) + movb ah, 0x0A ! Free extended memory block + callf (xms_driver) +nohandle: + call restore_video + pop ax + pop ax ! Return code in al + movb ah, 0x4C ! Terminate with return code + int 0x21 + +quit: ! exit(1) + movb al, 1 + push ax + call _exit + +xmserr: + xorb bh, bh + push bx + push tellxmserr + call _printf + jmp quit +.sect .rom +tellxmserr: .ascii "Extended memory problem, error 0x%02x\n\0" +.sect .text + +! int getprocessor(void) +! Prefix for the regular _getprocessor call that first checks if we're +! running in a virtual 8086 box. +getprocessor: + push sp ! Is pushed sp equal to sp? + pop ax + cmp ax, sp + jne gettrueproc ! If not then it's a plain 8086 or 80186 + .data1 0x0F,0x01,0xE0 ! Use old 286 SMSW instruction to get the MSW + testb al, 0x01 ! Protected mode enabled? + jz gettrueproc ! If not then a 286 or better in real mode + mov ax, 86 ! Forget fancy tricks, say it's an 8086 + ret +gettrueproc: ! Get the true processor type + push bp ! _getprocessor prologue that is patched over. + mov bp, sp + jmp _getprocessor+3 + +! Try to get an Upper Memory Block under MS-DOS 5+. Try to get one up to size +! dx, return segment of UMB found in dx and size in paragraphs in cx. +getumb: + xor cx, cx ! Initially nothing found + mov ax, 0x3000 ! Get DOS version + int 0x21 + cmpb al, 5 ! MS-DOS 5.0 or better? + jb retumb + mov ax, 0x544D ! Get UMB kept by BOOT /U + int 0x15 ! Returns dx = segment, cx = size + jc 0f + cmp ax, 0x4D54 ! Carry clear and ax byte swapped? + je retumb +0: mov ax, 0x5802 ! Get UMB link state + int 0x21 + xorb ah, ah + push ax ! Save UMB link state + mov ax, 0x5803 ! Set UMB link state + mov bx, 0x0001 ! Add UMBs to DOS memory chain + int 0x21 + mov ax, 0x5800 ! Get memory allocation strategy + int 0x21 + push ax ! Save allocation strategy + mov ax, 0x5801 ! Set memory allocation strategy + mov bx, 0x0080 ! First fit, try high then low memory + int 0x21 + movb ah, 0x48 ! Allocate memory + mov bx, dx ! Number of paragraphs wanted + int 0x21 ! Fails with bx = size of largest + jnc 0f ! Succeeds with ax = allocated block + test bx, bx ! Is there any? + jz no_umb + movb ah, 0x48 ! Allocate memory + int 0x21 + jc no_umb ! Did we get some? +0: mov dx, ax ! dx = segment + mov cx, bx ! cx = size +no_umb: mov ax, 0x5801 ! Set memory allocation strategy + pop bx ! bx = saved former strategy + int 0x21 + mov ax, 0x5803 ! Set UMB link state + pop bx ! bx = saved former link state + int 0x21 +retumb: ret + +! 'BOOT /U' instructs this program to grab the biggest available UMB and to +! sit on it until the next invocation of BOOT wants it back. These shenanigans +! are necessary because Windows 95 keeps all UMBs to itself unless you get hold +! of them first. + umb = 0x80 ! UMB base and size + old15 = 0x84 ! Old 15 interrupt vector + new15 = 0x88 ! New 15 interrupt handler +keepumb: + mov ax, 0x544D ! "Keep UMB" handler already present? + int 0x15 + jc 0f + cmp ax, 0x4D54 + je exitumb ! Already present, so quit +0: + mov si, new15start + mov di, new15 + mov cx, new15end + sub cx, si + rep movsb ! Copy handler into place + add di, 15 + movb cl, 4 + shr di, cl ! di = first segment above handler + mov cx, cs + cmp cx, 0xA000 ! Are we loaded high perchance? + jb nothigh +werehigh: + add cx, di + mov (umb+0), cx ! Use my own memory as the UMB to keep + mov ax, (_PSP+2) ! Up to the next in-use segment + sub ax, dx ! ax = size of my free memory + cmp ax, 0x1000 ! At least 64K? + jb exitumb ! Don't bother if less + mov (umb+2), 0x1000 ! Size of UMB + add di, 0x1000 ! Keep my code plus 64K when TSR + jmp hook15 +nothigh: + mov dx, 0x1000 + call getumb ! Grab an UMB of at most 64K + cmp cx, 0x1000 ! Did we get 64K? + jb exitumb ! Otherwise don't bother + mov (umb+0), dx + mov (umb+2), cx +hook15: + mov ax, 0x3515 ! Get interrupt vector + int 0x21 + mov (old15+0), bx + mov (old15+2), es ! Old 15 interrupt + mov ax, 0x2515 ! Set interrupt vector + mov dx, new15 ! ds:dx = new 15 handler + int 0x21 + mov ax, 0x3100 ! Terminate and stay resident + mov dx, di ! dx = di = paragraphs we keep + int 0x21 +exitumb: + mov ax, 0x4C00 ! exit(0) + int 0x21 + +new15start: ! New interrupt 15 handler + pushf + cmp ax, 0x544D ! Is it my call? + je my15 + popf + cseg jmpf (old15) ! No, continue with old 15 +my15: popf + push bp + mov bp, sp + andb 6(bp), ~0x01 ! clear carry, call will succeed + xchgb al, ah ! ax = 4D54, also means call works + cseg mov dx, (umb+0) ! dx = base of UMB + cseg mov cx, (umb+2) ! cx = size of UMB + pop bp + iret ! return to caller +new15end: + +! u32_t mon2abs(void *ptr) +! Address in monitor data to absolute address. +.define _mon2abs +_mon2abs: + mov bx, sp + mov ax, 2(bx) ! ptr + mov dx, ds ! Monitor data segment + !jmp seg2abs + +seg2abs: ! Translate dx:ax to the 32 bit address dx-ax + push cx + movb ch, dh + movb cl, 4 + shl dx, cl + shrb ch, cl ! ch-dx = dx << 4 + add ax, dx + adcb ch, 0 ! ch-ax = ch-dx + ax + movb dl, ch + xorb dh, dh ! dx-ax = ch-ax + pop cx + ret + +abs2seg: ! Translate the 32 bit address dx-ax to dx:ax + push cx + movb ch, dl + mov dx, ax ! ch-dx = dx-ax + and ax, 0x000F ! Offset in ax + movb cl, 4 + shr dx, cl + shlb ch, cl + orb dh, ch ! dx = ch-dx >> 4 + pop cx + ret + +! void raw_copy(u32_t dstaddr, u32_t srcaddr, u32_t count) +! Copy count bytes from srcaddr to dstaddr. Don't do overlaps. +! Also handles copying words to or from extended memory. +.define _raw_copy +_raw_copy: + push bp + mov bp, sp + push si + push di ! Save C variable registers +copy: + cmp 14(bp), 0 + jnz bigcopy + mov cx, 12(bp) + jcxz copydone ! Count is zero, end copy + cmp cx, 0xFFF0 + jb smallcopy +bigcopy:mov cx, 0xFFF0 ! Don't copy more than about 64K at once +smallcopy: + push cx ! Save copying count + mov ax, 4(bp) + mov dx, 6(bp) + cmp dx, 0x0010 ! Copy to extended memory? + jae ext_copy + cmp 10(bp), 0x0010 ! Copy from extended memory? + jae ext_copy + call abs2seg + mov di, ax + mov es, dx ! es:di = dstaddr + mov ax, 8(bp) + mov dx, 10(bp) + call abs2seg + mov si, ax + mov ds, dx ! ds:si = srcaddr + shr cx, 1 ! Words to move + rep movs ! Do the word copy + adc cx, cx ! One more byte? + rep movsb ! Do the byte copy + mov ax, ss ! Restore ds and es from the remaining ss + mov ds, ax + mov es, ax + jmp copyadjust +ext_copy: + mov (x_dst_desc+2), ax + movb (x_dst_desc+4), dl ! Set base of destination segment + mov ax, 8(bp) + mov dx, 10(bp) + mov (x_src_desc+2), ax + movb (x_src_desc+4), dl ! Set base of source segment + mov si, x_gdt ! es:si = global descriptor table + shr cx, 1 ! Words to move + movb ah, 0x87 ! Code for extended memory move + int 0x15 +copyadjust: + pop cx ! Restore count + add 4(bp), cx + adc 6(bp), 0 ! srcaddr += copycount + add 8(bp), cx + adc 10(bp), 0 ! dstaddr += copycount + sub 12(bp), cx + sbb 14(bp), 0 ! count -= copycount + jmp copy ! and repeat +copydone: + pop di + pop si ! Restore C variable registers + pop bp + ret + +! u16_t get_word(u32_t addr); +! void put_word(u32_t addr, u16_t word); +! Read or write a 16 bits word at an arbitrary location. +.define _get_word, _put_word +_get_word: + mov bx, sp + call gp_getaddr + mov ax, (bx) ! Word to get from addr + jmp gp_ret +_put_word: + mov bx, sp + push 6(bx) ! Word to store at addr + call gp_getaddr + pop (bx) ! Store the word + jmp gp_ret +gp_getaddr: + mov ax, 2(bx) + mov dx, 4(bx) + call abs2seg + mov bx, ax + mov ds, dx ! ds:bx = addr + ret +gp_ret: + push es + pop ds ! Restore ds + ret + +! void relocate(void); +! After the program has copied itself to a safer place, it needs to change +! the segment registers. Caddr has already been set to the new location. +.define _relocate +_relocate: + pop bx ! Return address + mov ax, (_caddr+0) + mov dx, (_caddr+2) + call abs2seg + mov cx, dx ! cx = new code segment + mov ax, cs ! Old code segment + sub ax, cx ! ax = -(new - old) = -Moving offset + mov dx, ds + sub dx, ax + mov ds, dx ! ds += (new - old) + mov es, dx + mov ss, dx + xor ax, ax + call seg2abs + mov (_daddr+0), ax + mov (_daddr+2), dx ! New data address + push cx ! New text segment + push bx ! Return offset of this function + retf ! Relocate + +! void *brk(void *addr) +! void *sbrk(size_t incr) +! Cannot fail implementations of brk(2) and sbrk(3), so we can use +! malloc(3). They reboot on stack collision instead of returning -1. +.sect .data + .align 2 +break: .data2 _end ! A fake heap pointer +.sect .text +.define _brk, __brk, _sbrk, __sbrk +_brk: +__brk: ! __brk is for the standard C compiler + xor ax, ax + jmp sbrk ! break= 0; return sbrk(addr); +_sbrk: +__sbrk: + mov ax, (break) ! ax= current break +sbrk: push ax ! save it as future return value + mov bx, sp ! Stack is now: (retval, retaddr, incr, ...) + add ax, 4(bx) ! ax= break + increment + mov (break), ax ! Set new break + lea dx, -1024(bx) ! sp minus a bit of breathing space + cmp dx, ax ! Compare with the new break + jb heaperr ! Suffocating noises + pop ax ! Return old break (0 for brk) + ret +heaperr:push nomem + call _printf + call quit +.sect .rom +nomem: .ascii "\nOut of memory\n\0" +.sect .text + +! int dev_open(void); +! Open file 'vdisk' to use as the Minix virtual disk. Store handle in +! vfd. Returns 0 for success, otherwise the DOS error code. +.define _dev_open +_dev_open: + call _dev_close ! If already open then first close + mov dx, (_vdisk) ! ds:dx = Address of file name + mov ax, 0x3D22 ! Open file read-write & deny write + int 0x21 + jnc opok ! Open succeeded? + cmp ax, 5 ! Open failed, "access denied"? + jne opbad + mov ax, 0x3D40 ! Open file read-only + int 0x21 + jc opbad +opok: mov (vfd), ax ! File handle to open file + xor ax, ax ! Zero for success +opbad: ret + +! int dev_close(void); +! Close the dos virtual disk. +.define _dev_close +_dev_close: + mov bx, -1 + cmp (vfd), bx ! Already closed? + je 1f + movb ah, 0x3E ! Close file + xchg bx, (vfd) ! bx = vfd; vfd = -1; + int 0x21 + jc 0f +1: xor ax, ax +0: ret + +! int dev_boundary(u32_t sector); +! Returns false; files have no visible boundaries. +.define _dev_boundary +_dev_boundary: + xor ax, ax + ret + +! int readsectors(u32_t bufaddr, u32_t sector, u8_t count) +! int writesectors(u32_t bufaddr, u32_t sector, u8_t count) +! Read/write several sectors from/to the Minix virtual disk. Count +! must fit in a byte. The external variable vfd is the file handle. +! Returns 0 for success, otherwise the DOS error code. +! +.define _readsectors, _writesectors +_writesectors: + push bp + mov bp, sp + movb 13(bp), 0x40 ! Code for a file write + jmp rwsec +_readsectors: + push bp + mov bp, sp + movb 13(bp), 0x3F ! Code for a file read +rwsec: + cmp (vfd), -1 ! Currently closed? + jne 0f + call _dev_open ! Open file if needed + test ax, ax + jnz rwerr +0: mov dx, 8(bp) + mov bx, 10(bp) ! bx-dx = Sector number + mov cx, 9 +mul512: shl dx, 1 + rcl bx, 1 ! bx-dx *= 512 + loop mul512 + mov cx, bx ! cx-dx = Byte position in file + mov bx, (vfd) ! bx = File handle + mov ax, 0x4200 ! Lseek absolute + int 0x21 + jb rwerr + mov bx, (vfd) ! bx = File handle + mov ax, 4(bp) + mov dx, 6(bp) ! dx-ax = Address to transfer data to/from + call abs2seg + mov ds, dx + mov dx, ax ! ds:dx = Address to transfer data to/from + xorb cl, cl + movb ch, 12(bp) ! ch = Number of sectors to transfer + shl cx, 1 ! cx = Number of bytes to transfer + push cx ! Save count + movb ah, 13(bp) ! Read or write + int 0x21 + pop cx ! Restore count + push es + pop ds ! Restore ds + jb rwerr + cmp ax, cx ! All bytes transferred? + je rwall + mov ax, 0x05 ! The DOS code for "I/O error", but different + jmp rwerr +rwall: call wheel ! Display tricks + xor ax, ax +rwerr: pop bp + ret + +! int getch(void); +! Read a character from the keyboard, and check for an expired timer. +! A carriage return is changed into a linefeed for UNIX compatibility. +.define _getch +_getch: + xor ax, ax + xchg ax, (unchar) ! Ungotten character? + test ax, ax + jnz gotch +getch: hlt ! Play dead until interrupted (see pause()) + movb ah, 0x01 ! Keyboard status + int 0x16 + jnz press ! Keypress? + call _expired ! Timer expired? + test ax, ax + jz getch + mov ax, ESC ! Return ESC + ret +press: + xorb ah, ah ! Read character from keyboard + int 0x16 + cmpb al, 0x0D ! Carriage return? + jnz nocr + movb al, 0x0A ! Change to linefeed +nocr: cmpb al, ESC ! Escape typed? + jne noesc + inc (escape) ! Set flag +noesc: xorb ah, ah ! ax = al +gotch: ret + +! int ungetch(void); +! Return a character to undo a getch(). +.define _ungetch +_ungetch: + mov bx, sp + mov ax, 2(bx) + mov (unchar), ax + ret + +! int escape(void); +! True if ESC has been typed. +.define _escape +_escape: + movb ah, 0x01 ! Keyboard status + int 0x16 + jz escflg ! Keypress? + cmpb al, ESC ! Escape typed? + jne escflg + xorb ah, ah ! Discard the escape + int 0x16 + inc (escape) ! Set flag +escflg: xor ax, ax + xchg ax, (escape) ! Escape typed flag + ret + +! int putch(int c); +! Write a character in teletype mode. The putk synonym is +! for the kernel printf function that uses it. +! Newlines are automatically preceded by a carriage return. +! +.define _putch, _putk +_putch: +_putk: mov bx, sp + movb al, 2(bx) ! al = character to be printed + testb al, al ! Kernel printf adds a null char to flush queue + jz nulch + cmpb al, 0x0A ! al = newline? + jnz putc + movb al, 0x20 ! Erase wheel and do a carriage return + call plotc ! plotc(' '); +nodirt: movb al, 0x0D + call putc ! putc('\r') + movb al, 0x0A ! Restore the '\n' and print it +putc: movb ah, 0x0E ! Print character in teletype mode + mov bx, 0x0001 ! Page 0, foreground color + int 0x10 ! Call BIOS VIDEO_IO +nulch: ret + +! |/-\|/-\|/-\|/-\|/-\ (playtime) +wheel: mov bx, (gp) + movb al, (bx) + inc bx ! al = *gp++; + cmp bx, glyphs+4 + jne 0f + mov bx, glyphs +0: mov (gp), bx ! gp= gp == glyphs + 4 ? glyphs : gp; + !jmp plotc +plotc: movb ah, 0x0A ! 0x0A = write character at cursor + mov bx, 0x0001 ! Page 0, foreground color + mov cx, 0x0001 ! Just one character + int 0x10 + ret +.sect .data + .align 2 +gp: .data2 glyphs +glyphs: .ascii "|/-\\" +.sect .text + +! void pause(void); +! Wait for an interrupt using the HLT instruction. This either saves +! power, or tells an x86 emulator that nothing is happening right now. +.define _pause +_pause: + hlt + ret + +! void set_mode(unsigned mode); +! void clear_screen(void); +! Set video mode / clear the screen. +.define _set_mode, _clear_screen +_set_mode: + mov bx, sp + mov ax, 2(bx) ! Video mode + cmp ax, (cur_vid_mode) + je modeok ! Mode already as requested? + mov (cur_vid_mode), ax +_clear_screen: + mov ax, (cur_vid_mode) + andb ah, 0x7F ! Test bits 8-14, clear bit 15 (8x8 flag) + jnz xvesa ! VESA extended mode? + int 0x10 ! Reset video (ah = 0) + jmp mdset +xvesa: mov bx, ax ! bx = extended mode + mov ax, 0x4F02 ! Reset video + int 0x10 +mdset: testb (cur_vid_mode+1), 0x80 + jz setcur ! 8x8 font requested? + mov ax, 0x1112 ! Load ROM 8 by 8 double-dot patterns + xorb bl, bl ! Load block 0 + int 0x10 +setcur: xor dx, dx ! dl = column = 0, dh = row = 0 + xorb bh, bh ! Page 0 + movb ah, 0x02 ! Set cursor position + int 0x10 +modeok: ret + +restore_video: ! To restore the video mode on exit + movb al, 0x20 + call plotc ! Erase wheel + push (old_vid_mode) + call _set_mode + pop ax + ret + +! u32_t get_tick(void); +! Return the current value of the clock tick counter. This counter +! increments 18.2 times per second. Poll it to do delays. Does not +! work on the original PC, but works on the PC/XT. +.define _get_tick +_get_tick: + xorb ah, ah ! Code for get tick count + int 0x1A + mov ax, dx + mov dx, cx ! dx:ax = cx:dx = tick count + ret + + +! Functions used to obtain info about the hardware. Boot uses this information +! itself, but will also pass them on to a pure 386 kernel, because one can't +! make BIOS calls from protected mode. The video type could probably be +! determined by the kernel too by looking at the hardware, but there is a small +! chance on errors that the monitor allows you to correct by setting variables. + +.define _get_bus ! returns type of system bus +.define _get_video ! returns type of display + +! u16_t get_bus(void) +! Return type of system bus, in order: XT, AT, MCA. +_get_bus: + call gettrueproc + xor dx, dx ! Assume XT + cmp ax, 286 ! An AT has at least a 286 + jb got_bus + inc dx ! Assume AT + movb ah, 0xC0 ! Code for get configuration + int 0x15 + jc got_bus ! Carry clear and ah = 00 if supported + testb ah, ah + jne got_bus + eseg movb al, 5(bx) ! Load feature byte #1 + inc dx ! Assume MCA + testb al, 0x02 ! Test bit 1 - "bus is Micro Channel" + jnz got_bus + dec dx ! Assume AT + testb al, 0x40 ! Test bit 6 - "2nd 8259 installed" + jnz got_bus + dec dx ! It is an XT +got_bus: + push ds + pop es ! Restore es + mov ax, dx ! Return bus code + mov (bus), ax ! Keep bus code, A20 handler likes to know + ret + +! u16_t get_video(void) +! Return type of display, in order: MDA, CGA, mono EGA, color EGA, +! mono VGA, color VGA. +_get_video: + mov ax, 0x1A00 ! Function 1A returns display code + int 0x10 ! al = 1A if supported + cmpb al, 0x1A + jnz no_dc ! No display code function supported + + mov ax, 2 + cmpb bl, 5 ! Is it a monochrome EGA? + jz got_video + inc ax + cmpb bl, 4 ! Is it a color EGA? + jz got_video + inc ax + cmpb bl, 7 ! Is it a monochrome VGA? + jz got_video + inc ax + cmpb bl, 8 ! Is it a color VGA? + jz got_video + +no_dc: movb ah, 0x12 ! Get information about the EGA + movb bl, 0x10 + int 0x10 + cmpb bl, 0x10 ! Did it come back as 0x10? (No EGA) + jz no_ega + + mov ax, 2 + cmpb bh, 1 ! Is it monochrome? + jz got_video + inc ax + jmp got_video + +no_ega: int 0x11 ! Get bit pattern for equipment + and ax, 0x30 ! Isolate color/mono field + sub ax, 0x30 + jz got_video ! Is it an MDA? + mov ax, 1 ! No it's CGA + +got_video: + ret + + +! Function to leave the boot monitor and run Minix. +.define _minix + +! void minix(u32_t koff, u32_t kcs, u32_t kds, +! char *bootparams, size_t paramsize, u32_t aout); +_minix: + push bp + mov bp, sp ! Pointer to arguments + + mov dx, 0x03F2 ! Floppy motor drive control bits + movb al, 0x0C ! Bits 4-7 for floppy 0-3 are off + outb dx ! Kill the motors + push ds + xor ax, ax ! Vector & BIOS data segments + mov ds, ax + andb (0x043F), 0xF0 ! Clear diskette motor status bits of BIOS + pop ds + cli ! No more interruptions + + test (_k_flags), K_I386 ! Minix-386? + jnz minix386 + +! Call Minix in real mode. +minix86: + push 22(bp) ! Address of a.out headers + push 20(bp) + + push 18(bp) ! # bytes of boot parameters + push 16(bp) ! Address of boot parameters + + mov dx, cs ! Monitor far return address + mov ax, ret86 + cmp (_mem+14), 0 ! Any extended memory? (mem[1].size > 0 ?) + jnz 0f + xor dx, dx ! If no ext mem then monitor not preserved + xor ax, ax +0: push dx ! Push monitor far return address or zero + push ax + + mov ax, 8(bp) + mov dx, 10(bp) + call abs2seg + push dx ! Kernel code segment + push 4(bp) ! Kernel code offset + mov ax, 12(bp) + mov dx, 14(bp) + call abs2seg + mov ds, dx ! Kernel data segment + mov es, dx ! Set es to kernel data too + retf ! Make a far call to the kernel + +! Call 386 Minix in 386 mode. +minix386: + cseg mov (cs_real-2), cs ! Patch CS and DS into the instructions that + cseg mov (ds_real-2), ds ! reload them when switching back to real mode + mov eax, cr0 + orb al, 0x01 ! Set PE (protection enable) bit + o32 mov (msw), eax ! Save as protected mode machine status word + + mov dx, ds ! Monitor ds + mov ax, p_gdt ! dx:ax = Global descriptor table + call seg2abs + mov (p_gdt_desc+2), ax + movb (p_gdt_desc+4), dl ! Set base of global descriptor table + + mov ax, 12(bp) + mov dx, 14(bp) ! Kernel ds (absolute address) + mov (p_ds_desc+2), ax + movb (p_ds_desc+4), dl ! Set base of kernel data segment + + mov dx, ss ! Monitor ss + xor ax, ax ! dx:ax = Monitor stack segment + call seg2abs ! Minix starts with the stack of the monitor + mov (p_ss_desc+2), ax + movb (p_ss_desc+4), dl + + mov ax, 8(bp) + mov dx, 10(bp) ! Kernel cs (absolute address) + mov (p_cs_desc+2), ax + movb (p_cs_desc+4), dl + + mov dx, cs ! Monitor cs + xor ax, ax ! dx:ax = Monitor code segment + call seg2abs + mov (p_mcs_desc+2), ax + movb (p_mcs_desc+4), dl + + push MCS_SELECTOR + push int86 ! Far address to INT86 support + + o32 push 20(bp) ! Address of a.out headers + + push 0 + push 18(bp) ! 32 bit size of parameters on stack + push 0 + push 16(bp) ! 32 bit address of parameters (ss relative) + + push MCS_SELECTOR + push ret386 ! Monitor far return address + + push 0 + push CS_SELECTOR + push 6(bp) + push 4(bp) ! 32 bit far address to kernel entry point + + call real2prot ! Switch to protected mode + mov ax, DS_SELECTOR + mov ds, ax ! Kernel data + mov ax, ES_SELECTOR + mov es, ax ! Flat 4 Gb + o32 retf ! Make a far call to the kernel + +! Minix-86 returns here on a halt or reboot. +ret86: + mov 8(bp), ax + mov 10(bp), dx ! Return value + jmp return + +! Minix-386 returns here on a halt or reboot. +ret386: + o32 mov 8(bp), eax ! Return value + call prot2real ! Switch to real mode + +return: + mov sp, bp ! Pop parameters + sti ! Can take interrupts again + + call _get_video ! MDA, CGA, EGA, ... + movb dh, 24 ! dh = row 24 + cmp ax, 2 ! At least EGA? + jb is25 ! Otherwise 25 rows + push ds + xor ax, ax ! Vector & BIOS data segments + mov ds, ax + movb dh, (0x0484) ! Number of rows on display minus one + pop ds +is25: + xorb dl, dl ! dl = column 0 + xorb bh, bh ! Page 0 + movb ah, 0x02 ! Set cursor position + int 0x10 + + xorb ah, ah ! Whack the disk system, Minix may have messed + movb dl, 0x80 ! it up + int 0x13 + + call gettrueproc + cmp ax, 286 + jb noclock + xorb al, al +tryclk: decb al + jz noclock + movb ah, 0x02 ! Get real-time clock time (from CMOS clock) + int 0x1A + jc tryclk ! Carry set, not running or being updated + movb al, ch ! ch = hour in BCD + call bcd ! al = (al >> 4) * 10 + (al & 0x0F) + mulb (c60) ! 60 minutes in an hour + mov bx, ax ! bx = hour * 60 + movb al, cl ! cl = minutes in BCD + call bcd + add bx, ax ! bx = hour * 60 + minutes + movb al, dh ! dh = seconds in BCD + call bcd + xchg ax, bx ! ax = hour * 60 + minutes, bx = seconds + mul (c60) ! dx-ax = (hour * 60 + minutes) * 60 + add bx, ax + adc dx, 0 ! dx-bx = seconds since midnight + mov ax, dx + mul (c19663) + xchg ax, bx + mul (c19663) + add dx, bx ! dx-ax = dx-bx * (0x1800B0 / (2*2*2*2*5)) + mov cx, ax ! (0x1800B0 = ticks per day of BIOS clock) + mov ax, dx + xor dx, dx + div (c1080) + xchg ax, cx + div (c1080) ! cx-ax = dx-ax / (24*60*60 / (2*2*2*2*5)) + mov dx, ax ! cx-dx = ticks since midnight + movb ah, 0x01 ! Set system time + int 0x1A +noclock: + + mov ax, 8(bp) + mov dx, 10(bp) ! dx-ax = return value from the kernel + pop bp + ret ! Return to monitor as if nothing much happened + +! Transform BCD number in al to a regular value in ax. +bcd: movb ah, al + shrb ah, 4 + andb al, 0x0F + aad ! ax = (al >> 4) * 10 + (al & 0x0F) + ret + +! Support function for Minix-386 to make an 8086 interrupt call. +int86: + mov bp, sp + call prot2real + + o32 xor eax, eax + mov es, ax ! Vector & BIOS data segments +o32 eseg mov (0x046C), eax ! Clear BIOS clock tick counter + + sti ! Enable interrupts + + movb al, 0xCD ! INT instruction + movb ah, 8(bp) ! Interrupt number? + testb ah, ah + jnz 0f ! Nonzero if INT, otherwise far call + push cs + push intret+2 ! Far return address + o32 push 12(bp) ! Far driver address + mov ax, 0x90CB ! RETF; NOP +0: cseg mov (intret), ax ! Patch `INT n' or `RETF; NOP' into code + + mov ds, 16(bp) ! Load parameters + mov es, 18(bp) + o32 mov eax, 20(bp) + o32 mov ebx, 24(bp) + o32 mov ecx, 28(bp) + o32 mov edx, 32(bp) + o32 mov esi, 36(bp) + o32 mov edi, 40(bp) + o32 mov ebp, 44(bp) + +intret: int 0xFF ! Do the interrupt or far call + + o32 push ebp ! Save results + o32 pushf + mov bp, sp + o32 pop 8+8(bp) ! eflags + mov 8+16(bp), ds + mov 8+18(bp), es + o32 mov 8+20(bp), eax + o32 mov 8+24(bp), ebx + o32 mov 8+28(bp), ecx + o32 mov 8+32(bp), edx + o32 mov 8+36(bp), esi + o32 mov 8+40(bp), edi + o32 pop 8+44(bp) ! ebp + + cli ! Disable interrupts + + xor ax, ax + mov ds, ax ! Vector & BIOS data segments + o32 mov cx, (0x046C) ! Collect lost clock ticks in ecx + + mov ax, ss + mov ds, ax ! Restore monitor ds + call real2prot + mov ax, DS_SELECTOR ! Kernel data + mov ds, ax + o32 retf ! Return to the kernel + +! Switch from real to protected mode. +real2prot: + movb ah, 0x02 ! Code for A20 enable + call gate_A20 + + lgdt (p_gdt_desc) ! Global descriptor table + o32 mov eax, (pdbr) ! Load page directory base register + mov cr3, eax + mov eax, cr0 + o32 xchg eax, (msw) ! Exchange real mode msw for protected mode msw + mov cr0, eax + jmpf MCS_SELECTOR:cs_prot ! Set code segment selector +cs_prot: + mov ax, SS_SELECTOR ! Set data selectors + mov ds, ax + mov es, ax + mov ss, ax + ret + +! Switch from protected to real mode. +prot2real: + lidt (p_idt_desc) ! Real mode interrupt vectors + mov eax, cr3 + o32 mov (pdbr), eax ! Save page directory base register + mov eax, cr0 + o32 xchg eax, (msw) ! Exchange protected mode msw for real mode msw + mov cr0, eax + jmpf 0xDEAD:cs_real ! Reload cs register +cs_real: + mov ax, 0xBEEF +ds_real: + mov ds, ax ! Reload data segment registers + mov es, ax + mov ss, ax + + xorb ah, ah ! Code for A20 disable + !jmp gate_A20 + +! Enable (ah = 0x02) or disable (ah = 0x00) the A20 address line. +gate_A20: + cmp (bus), 2 ! PS/2 bus? + je gate_PS_A20 + call kb_wait + movb al, 0xD1 ! Tell keyboard that a command is coming + outb 0x64 + call kb_wait + movb al, 0xDD ! 0xDD = A20 disable code if ah = 0x00 + orb al, ah ! 0xDF = A20 enable code if ah = 0x02 + outb 0x60 + call kb_wait + movb al, 0xFF ! Pulse output port + outb 0x64 + call kb_wait ! Wait for the A20 line to settle down + ret +kb_wait: + inb 0x64 + testb al, 0x02 ! Keyboard input buffer full? + jnz kb_wait ! If so, wait + ret + +gate_PS_A20: ! The PS/2 can twiddle A20 using port A + inb 0x92 ! Read port A + andb al, 0xFD + orb al, ah ! Set A20 bit to the required state + outb 0x92 ! Write port A + jmp .+2 ! Small delay +A20ok: inb 0x92 ! Check port A + andb al, 0x02 + cmpb al, ah ! A20 line settled down to the new state? + jne A20ok ! If not then wait + ret + +! void int15(bios_env_t *ep) +! Do an "INT 15" call, primarily for APM (Power Management). +.define _int15 +_int15: + push si ! Save callee-save register si + mov si, sp + mov si, 4(si) ! ep + mov ax, (si) ! ep->ax + mov bx, 2(si) ! ep->bx + mov cx, 4(si) ! ep->cx + int 0x15 ! INT 0x15 BIOS call + pushf ! Save flags + mov (si), ax ! ep->ax + mov 2(si), bx ! ep->bx + mov 4(si), cx ! ep->cx + pop 6(si) ! ep->flags + pop si ! Restore + ret + +.sect .rom + .align 4 +c60: .data2 60 ! Constants for MUL and DIV +c1024: .data2 1024 +c1080: .data2 1080 +c19663: .data2 19663 + +.sect .data + .align 4 + +! Global descriptor tables. + UNSET = 0 ! Must be computed + +! For "Extended Memory Block Move". +x_gdt: +x_null_desc: + ! Null descriptor + .data2 0x0000, 0x0000 + .data1 0x00, 0x00, 0x00, 0x00 +x_gdt_desc: + ! Descriptor for this descriptor table + .data2 6*8-1, UNSET + .data1 UNSET, 0x00, 0x00, 0x00 +x_src_desc: + ! Source segment descriptor + .data2 0xFFFF, UNSET + .data1 UNSET, 0x92, 0x00, 0x00 +x_dst_desc: + ! Destination segment descriptor + .data2 0xFFFF, UNSET + .data1 UNSET, 0x92, 0x00, 0x00 +x_bios_desc: + ! BIOS segment descriptor (scratch for int 0x15) + .data2 UNSET, UNSET + .data1 UNSET, UNSET, UNSET, UNSET +x_ss_desc: + ! BIOS stack segment descriptor (scratch for int 0x15) + .data2 UNSET, UNSET + .data1 UNSET, UNSET, UNSET, UNSET + +! Protected mode descriptor table. +p_gdt: +p_null_desc: + ! Null descriptor + .data2 0x0000, 0x0000 + .data1 0x00, 0x00, 0x00, 0x00 +p_gdt_desc: + ! Descriptor for this descriptor table + .data2 8*8-1, UNSET + .data1 UNSET, 0x00, 0x00, 0x00 +p_idt_desc: + ! Real mode interrupt descriptor table descriptor + .data2 0x03FF, 0x0000 + .data1 0x00, 0x00, 0x00, 0x00 +p_ds_desc: + ! Kernel data segment descriptor (4Gb flat) + .data2 0xFFFF, UNSET + .data1 UNSET, 0x92, 0xCF, 0x00 +p_es_desc: + ! Physical memory descriptor (4Gb flat) + .data2 0xFFFF, 0x0000 + .data1 0x00, 0x92, 0xCF, 0x00 +p_ss_desc: + ! Monitor data segment descriptor (64Kb flat) + .data2 0xFFFF, UNSET + .data1 UNSET, 0x92, 0x00, 0x00 +p_cs_desc: + ! Kernel code segment descriptor (4Gb flat) + .data2 0xFFFF, UNSET + .data1 UNSET, 0x9A, 0xCF, 0x00 +p_mcs_desc: + ! Monitor code segment descriptor (64 kb flat) (unused) + .data2 0xFFFF, UNSET + .data1 UNSET, 0x9A, 0x00, 0x00 + +xms_handle: .data2 -1 ! Handle of allocated XMS block +vfd: .data2 -1 ! Virtual disk file handle + +.sect .bss + .comm xms_driver, 4 ! Vector to XMS driver + .comm old_vid_mode, 2 ! Video mode at startup + .comm cur_vid_mode, 2 ! Current video mode + .comm msw, 4 ! Saved machine status word (cr0) + .comm pdbr, 4 ! Saved page directory base register (cr3) + .comm escape, 2 ! Escape typed? + .comm bus, 2 ! Saved return value of _get_bus + .comm unchar, 2 ! Char returned by ungetch(c) + +! +! $PchId: doshead.ack.s,v 1.7 2002/02/27 19:37:52 philip Exp $ diff --git a/boot/image.h b/boot/image.h new file mode 100755 index 000000000..4afc8dcc5 --- /dev/null +++ b/boot/image.h @@ -0,0 +1,13 @@ +/* image.h - Info between installboot and boot. Author: Kees J. Bot + */ + +#define IM_NAME_MAX 63 + +struct image_header { + char name[IM_NAME_MAX + 1]; /* Null terminated. */ + struct exec process; +}; + +/* + * $PchId: image.h,v 1.4 1995/11/27 22:23:12 philip Exp $ + */ diff --git a/boot/installboot.c b/boot/installboot.c new file mode 100755 index 000000000..0a4b4e858 --- /dev/null +++ b/boot/installboot.c @@ -0,0 +1,833 @@ +/* installboot 3.0 - Make a device bootable Author: Kees J. Bot + * 21 Dec 1991 + * + * Either make a device bootable or make an image from kernel, mm, fs, etc. + */ +#define nil 0 +#define _POSIX_SOURCE 1 +#define _MINIX 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rawfs.h" +#include "image.h" + +#define BOOTBLOCK 0 /* Of course */ +#define SECTOR_SIZE 512 /* Disk sector size. */ +#define RATIO(b) ((b)/SECTOR_SIZE) +#define SIGNATURE 0xAA55 /* Boot block signature. */ +#define BOOT_MAX 64 /* Absolute maximum size of secondary boot */ +#define SIGPOS 510 /* Where to put signature word. */ +#define PARTPOS 446 /* Offset to the partition table in a master + * boot block. + */ + +#define between(a, c, z) ((unsigned) ((c) - (a)) <= ((z) - (a))) +#define control(c) between('\0', (c), '\37') + +#define BOOT_BLOCK_SIZE 1024 + +void report(char *label) +/* installboot: label: No such file or directory */ +{ + fprintf(stderr, "installboot: %s: %s\n", label, strerror(errno)); +} + +void fatal(char *label) +{ + report(label); + exit(1); +} + +char *basename(char *name) +/* Return the last component of name, stripping trailing slashes from name. + * Precondition: name != "/". If name is prefixed by a label, then the + * label is copied to the basename too. + */ +{ + static char base[IM_NAME_MAX]; + char *p, *bp= base; + + if ((p= strchr(name, ':')) != nil) { + while (name <= p && bp < base + IM_NAME_MAX - 1) + *bp++ = *name++; + } + for (;;) { + if ((p= strrchr(name, '/')) == nil) { p= name; break; } + if (*++p != 0) break; + *--p= 0; + } + while (*p != 0 && bp < base + IM_NAME_MAX - 1) *bp++ = *p++; + *bp= 0; + return base; +} + +void bread(FILE *f, char *name, void *buf, size_t len) +/* Read len bytes. Don't dare return without them. */ +{ + if (len > 0 && fread(buf, len, 1, f) != 1) { + if (ferror(f)) fatal(name); + fprintf(stderr, "installboot: Unexpected EOF on %s\n", name); + exit(1); + } +} + +void bwrite(FILE *f, char *name, void *buf, size_t len) +{ + if (len > 0 && fwrite(buf, len, 1, f) != 1) fatal(name); +} + +long total_text= 0, total_data= 0, total_bss= 0; +int making_image= 0; + +void read_header(int talk, char *proc, FILE *procf, struct image_header *ihdr) +/* Read the a.out header of a program and check it. If procf happens to be + * nil then the header is already in *image_hdr and need only be checked. + */ +{ + int n, big= 0; + static int banner= 0; + struct exec *phdr= &ihdr->process; + + if (procf == nil) { + /* Header already present. */ + n= phdr->a_hdrlen; + } else { + memset(ihdr, 0, sizeof(*ihdr)); + + /* Put the basename of proc in the header. */ + strncpy(ihdr->name, basename(proc), IM_NAME_MAX); + + /* Read the header. */ + n= fread(phdr, sizeof(char), A_MINHDR, procf); + if (ferror(procf)) fatal(proc); + } + + if (n < A_MINHDR || BADMAG(*phdr)) { + fprintf(stderr, "installboot: %s is not an executable\n", proc); + exit(1); + } + + /* Get the rest of the exec header. */ + if (procf != nil) { + bread(procf, proc, ((char *) phdr) + A_MINHDR, + phdr->a_hdrlen - A_MINHDR); + } + + if (talk && !banner) { + printf(" text data bss size\n"); + banner= 1; + } + + if (talk) { + printf("%8ld%8ld%8ld%9ld %s\n", + phdr->a_text, phdr->a_data, phdr->a_bss, + phdr->a_text + phdr->a_data + phdr->a_bss, proc); + } + total_text+= phdr->a_text; + total_data+= phdr->a_data; + total_bss+= phdr->a_bss; + + if (phdr->a_cpu == A_I8086) { + long data= phdr->a_data + phdr->a_bss; + + if (!(phdr->a_flags & A_SEP)) data+= phdr->a_text; + + if (phdr->a_text >= 65536) big|= 1; + if (data >= 65536) big|= 2; + } + if (big) { + fprintf(stderr, + "%s will crash, %s%s%s segment%s larger then 64K\n", + proc, + big & 1 ? "text" : "", + big == 3 ? " and " : "", + big & 2 ? "data" : "", + big == 3 ? "s are" : " is"); + } +} + +void padimage(char *image, FILE *imagef, int n) +/* Add n zeros to image to pad it to a sector boundary. */ +{ + while (n > 0) { + if (putc(0, imagef) == EOF) fatal(image); + n--; + } +} + +#define align(n) (((n) + ((SECTOR_SIZE) - 1)) & ~((SECTOR_SIZE) - 1)) + +void copyexec(char *proc, FILE *procf, char *image, FILE *imagef, long n) +/* Copy n bytes from proc to image padded to fill a sector. */ +{ + int pad, c; + + /* Compute number of padding bytes. */ + pad= align(n) - n; + + while (n > 0) { + if ((c= getc(procf)) == EOF) { + if (ferror(procf)) fatal(proc); + fprintf(stderr, "installboot: premature EOF on %s\n", + proc); + exit(1); + } + if (putc(c, imagef) == EOF) fatal(image); + n--; + } + padimage(image, imagef, pad); +} + +void make_image(char *image, char **procv) +/* Collect a set of files in an image, each "segment" is nicely padded out + * to SECTOR_SIZE, so it may be read from disk into memory without trickery. + */ +{ + FILE *imagef, *procf; + char *proc, *file; + int procn; + struct image_header ihdr; + struct exec phdr; + struct stat st; + + making_image= 1; + + if ((imagef= fopen(image, "w")) == nil) fatal(image); + + for (procn= 0; (proc= *procv++) != nil; procn++) { + /* Remove the label from the file name. */ + if ((file= strchr(proc, ':')) != nil) file++; else file= proc; + + /* Real files please, may need to seek. */ + if (stat(file, &st) < 0 + || (errno= EISDIR, !S_ISREG(st.st_mode)) + || (procf= fopen(file, "r")) == nil + ) fatal(proc); + + /* Read a.out header. */ + read_header(1, proc, procf, &ihdr); + + /* Scratch. */ + phdr= ihdr.process; + + /* The symbol table is always stripped off. */ + ihdr.process.a_syms= 0; + ihdr.process.a_flags &= ~A_NSYM; + + /* Write header padded to fill a sector */ + bwrite(imagef, image, &ihdr, sizeof(ihdr)); + + padimage(image, imagef, SECTOR_SIZE - sizeof(ihdr)); + + /* A page aligned executable needs the header in text. */ + if (phdr.a_flags & A_PAL) { + rewind(procf); + phdr.a_text+= phdr.a_hdrlen; + } + + /* Copy text and data of proc to image. */ + if (phdr.a_flags & A_SEP) { + /* Separate I&D: pad text & data separately. */ + + copyexec(proc, procf, image, imagef, phdr.a_text); + copyexec(proc, procf, image, imagef, phdr.a_data); + } else { + /* Common I&D: keep text and data together. */ + + copyexec(proc, procf, image, imagef, + phdr.a_text + phdr.a_data); + } + + /* Done with proc. */ + (void) fclose(procf); + } + /* Done with image. */ + + if (fclose(imagef) == EOF) fatal(image); + + printf(" ------ ------ ------ -------\n"); + printf("%8ld%8ld%8ld%9ld total\n", + total_text, total_data, total_bss, + total_text + total_data + total_bss); +} + +void extractexec(FILE *imagef, char *image, FILE *procf, char *proc, + long count, off_t *alen) +/* Copy a segment of an executable. It is padded to a sector in image. */ +{ + char buf[SECTOR_SIZE]; + + while (count > 0) { + bread(imagef, image, buf, sizeof(buf)); + *alen-= sizeof(buf); + + bwrite(procf, proc, buf, + count < sizeof(buf) ? (size_t) count : sizeof(buf)); + count-= sizeof(buf); + } +} + +void extract_image(char *image) +/* Extract the executables from an image. */ +{ + FILE *imagef, *procf; + off_t len; + struct stat st; + struct image_header ihdr; + struct exec phdr; + char buf[SECTOR_SIZE]; + + if (stat(image, &st) < 0) fatal(image); + + /* Size of the image. */ + len= S_ISREG(st.st_mode) ? st.st_size : -1; + + if ((imagef= fopen(image, "r")) == nil) fatal(image); + + while (len != 0) { + /* Extract a program, first sector is an extended header. */ + bread(imagef, image, buf, sizeof(buf)); + len-= sizeof(buf); + + memcpy(&ihdr, buf, sizeof(ihdr)); + phdr= ihdr.process; + + /* Check header. */ + read_header(1, ihdr.name, nil, &ihdr); + + if ((procf= fopen(ihdr.name, "w")) == nil) fatal(ihdr.name); + + if (phdr.a_flags & A_PAL) { + /* A page aligned process contains a header in text. */ + phdr.a_text+= phdr.a_hdrlen; + } else { + bwrite(procf, ihdr.name, &ihdr.process, phdr.a_hdrlen); + } + + /* Extract text and data segments. */ + if (phdr.a_flags & A_SEP) { + extractexec(imagef, image, procf, ihdr.name, + phdr.a_text, &len); + extractexec(imagef, image, procf, ihdr.name, + phdr.a_data, &len); + } else { + extractexec(imagef, image, procf, ihdr.name, + phdr.a_text + phdr.a_data, &len); + } + + if (fclose(procf) == EOF) fatal(ihdr.name); + } +} + +int rawfd; /* File descriptor to open device. */ +char *rawdev; /* Name of device. */ + +void readblock(off_t blk, char *buf, int block_size) +/* For rawfs, so that it can read blocks. */ +{ + int n; + + if (lseek(rawfd, blk * block_size, SEEK_SET) < 0 + || (n= read(rawfd, buf, block_size)) < 0 + ) fatal(rawdev); + + if (n < block_size) { + fprintf(stderr, "installboot: Unexpected EOF on %s\n", rawdev); + exit(1); + } +} + +void writeblock(off_t blk, char *buf, int block_size) +/* Add a function to write blocks for local use. */ +{ + if (lseek(rawfd, blk * block_size, SEEK_SET) < 0 + || write(rawfd, buf, block_size) < 0 + ) fatal(rawdev); +} + +int raw_install(char *file, off_t *start, off_t *len, int block_size) +/* Copy bootcode or an image to the boot device at the given absolute disk + * block number. This "raw" installation is used to place bootcode and + * image on a disk without a filesystem to make a simple boot disk. Useful + * in automated scripts for J. Random User. + * Note: *len == 0 when an image is read. It is set right afterwards. + */ +{ + static char buf[MAX_BLOCK_SIZE]; /* Nonvolatile block buffer. */ + FILE *f; + off_t sec; + unsigned long devsize; + static int banner= 0; + struct partition entry; + + /* See if the device has a maximum size. */ + devsize= -1; + if (ioctl(rawfd, DIOCGETP, &entry) == 0) devsize= cv64ul(entry.size); + + if ((f= fopen(file, "r")) == nil) fatal(file); + + /* Copy sectors from file onto the boot device. */ + sec= *start; + do { + int off= sec % RATIO(BOOT_BLOCK_SIZE); + + if (fread(buf + off * SECTOR_SIZE, 1, SECTOR_SIZE, f) == 0) + break; + + if (sec >= devsize) { + fprintf(stderr, + "installboot: %s can't be attached to %s\n", + file, rawdev); + return 0; + } + + if (off == RATIO(BOOT_BLOCK_SIZE) - 1) writeblock(sec / RATIO(BOOT_BLOCK_SIZE), buf, BOOT_BLOCK_SIZE); + } while (++sec != *start + *len); + + if (ferror(f)) fatal(file); + (void) fclose(f); + + /* Write a partial block, this may be the last image. */ + if (sec % RATIO(BOOT_BLOCK_SIZE) != 0) writeblock(sec / RATIO(BOOT_BLOCK_SIZE), buf, BOOT_BLOCK_SIZE); + + if (!banner) { + printf(" sector length\n"); + banner= 1; + } + *len= sec - *start; + printf("%8ld%8ld %s\n", *start, *len, file); + *start= sec; + return 1; +} + +enum howto { FS, BOOT }; + +void make_bootable(enum howto how, char *device, char *bootblock, + char *bootcode, char **imagev) +/* Install bootblock on the bootsector of device with the disk addresses to + * bootcode patched into the data segment of bootblock. "How" tells if there + * should or shoudn't be a file system on the disk. The images in the imagev + * vector are added to the end of the device. + */ +{ + char buf[MAX_BLOCK_SIZE + 256], *adrp, *parmp; + struct fileaddr { + off_t address; + int count; + } bootaddr[BOOT_MAX + 1], *bap= bootaddr; + struct exec boothdr; + struct image_header dummy; + struct stat st; + ino_t ino; + off_t sector, max_sector; + FILE *bootf; + off_t addr, fssize, pos, len; + char *labels, *label, *image; + int nolabel; + int block_size = 0; + + /* Open device and set variables for readblock. */ + if ((rawfd= open(rawdev= device, O_RDWR)) < 0) fatal(device); + + /* Read and check the superblock. */ + fssize= r_super(&block_size); + + switch (how) { + case FS: + if (fssize == 0) { + fprintf(stderr, + "installboot: %s is not a Minix file system\n", + device); + exit(1); + } + break; + case BOOT: + if (fssize != 0) { + int s; + printf("%s contains a file system!\n", device); + printf("Scribbling in 10 seconds"); + for (s= 0; s < 10; s++) { + fputc('.', stdout); + fflush(stdout); + sleep(1); + } + fputc('\n', stdout); + } + fssize= 1; /* Just a boot block. */ + } + + if (how == FS) { + /* See if the boot code can be found on the file system. */ + if ((ino= r_lookup(ROOT_INO, bootcode)) == 0) { + if (errno != ENOENT) fatal(bootcode); + } + } else { + /* Boot code must be attached at the end. */ + ino= 0; + } + + if (ino == 0) { + /* For a raw installation, we need to copy the boot code onto + * the device, so we need to look at the file to be copied. + */ + if (stat(bootcode, &st) < 0) fatal(bootcode); + + if ((bootf= fopen(bootcode, "r")) == nil) fatal(bootcode); + } else { + /* Boot code is present in the file system. */ + r_stat(ino, &st); + + /* Get the header from the first block. */ + if ((addr= r_vir2abs((off_t) 0)) == 0) { + boothdr.a_magic[0]= !A_MAGIC0; + } else { + readblock(addr, buf, block_size); + memcpy(&boothdr, buf, sizeof(struct exec)); + } + bootf= nil; + dummy.process= boothdr; + } + /* See if it is an executable (read_header does the check). */ + read_header(0, bootcode, bootf, &dummy); + boothdr= dummy.process; + + if (bootf != nil) fclose(bootf); + + /* Get all the sector addresses of the secondary boot code. */ + max_sector= (boothdr.a_hdrlen + boothdr.a_text + + boothdr.a_data + SECTOR_SIZE - 1) / SECTOR_SIZE; + + if (max_sector > BOOT_MAX * RATIO(block_size)) { + fprintf(stderr, "installboot: %s is way too big\n", bootcode); + exit(0); + } + + /* Determine the addresses to the boot code to be patched into the + * boot block. + */ + bap->count= 0; /* Trick to get the address recording going. */ + + for (sector= 0; sector < max_sector; sector++) { + if (ino == 0) { + addr= fssize + (sector / RATIO(block_size)); + } else + if ((addr= r_vir2abs(sector / RATIO(block_size))) == 0) { + fprintf(stderr, "installboot: %s has holes!\n", + bootcode); + exit(1); + } + addr= (addr * RATIO(block_size)) + (sector % RATIO(block_size)); + + /* First address of the addresses array? */ + if (bap->count == 0) bap->address= addr; + + /* Paste sectors together in a multisector read. */ + if (bap->address + bap->count == addr) + bap->count++; + else { + /* New address. */ + bap++; + bap->address= addr; + bap->count= 1; + } + } + (++bap)->count= 0; /* No more. */ + + /* Get the boot block and patch the pieces in. */ + readblock(BOOTBLOCK, buf, BOOT_BLOCK_SIZE); + + if ((bootf= fopen(bootblock, "r")) == nil) fatal(bootblock); + + read_header(0, bootblock, bootf, &dummy); + boothdr= dummy.process; + + if (boothdr.a_text + boothdr.a_data + + 4 * (bap - bootaddr) + 1 > PARTPOS) { + fprintf(stderr, + "installboot: %s + addresses to %s don't fit in the boot sector\n", + bootblock, bootcode); + fprintf(stderr, + "You can try copying/reinstalling %s to defragment it\n", + bootcode); + exit(1); + } + + /* All checks out right. Read bootblock into the boot block! */ + bread(bootf, bootblock, buf, boothdr.a_text + boothdr.a_data); + (void) fclose(bootf); + + /* Patch the addresses in. */ + adrp= buf + (int) (boothdr.a_text + boothdr.a_data); + for (bap= bootaddr; bap->count != 0; bap++) { + *adrp++= bap->count; + *adrp++= (bap->address >> 0) & 0xFF; + *adrp++= (bap->address >> 8) & 0xFF; + *adrp++= (bap->address >> 16) & 0xFF; + } + /* Zero count stops bootblock's reading loop. */ + *adrp++= 0; + + if (bap > bootaddr+1) { + printf("%s and %d addresses to %s patched into %s\n", + bootblock, (int)(bap - bootaddr), bootcode, device); + } + + /* Boot block signature. */ + buf[SIGPOS+0]= (SIGNATURE >> 0) & 0xFF; + buf[SIGPOS+1]= (SIGNATURE >> 8) & 0xFF; + + /* Sector 2 of the boot block is used for boot parameters, initially + * filled with null commands (newlines). Initialize it only if + * necessary. + */ + for (parmp= buf + SECTOR_SIZE; parmp < buf + 2*SECTOR_SIZE; parmp++) { + if (*imagev != nil || (control(*parmp) && *parmp != '\n')) { + /* Param sector must be initialized. */ + memset(buf + SECTOR_SIZE, '\n', SECTOR_SIZE); + break; + } + } + + /* Offset to the end of the file system to add boot code and images. */ + pos= fssize * RATIO(block_size); + + if (ino == 0) { + /* Place the boot code onto the boot device. */ + len= max_sector; + if (!raw_install(bootcode, &pos, &len, block_size)) { + if (how == FS) { + fprintf(stderr, + "\t(Isn't there a copy of %s on %s that can be used?)\n", + bootcode, device); + } + exit(1); + } + } + + parmp= buf + SECTOR_SIZE; + nolabel= 0; + + if (how == BOOT) { + /* A boot only disk needs to have floppies swapped. */ + strcpy(parmp, + "trailer()echo \\nInsert the root diskette then hit RETURN\\n\\w\\c\n"); + parmp+= strlen(parmp); + } + + while ((labels= *imagev++) != nil) { + /* Place each kernel image on the boot device. */ + + if ((image= strchr(labels, ':')) != nil) + *image++= 0; + else { + if (nolabel) { + fprintf(stderr, + "installboot: Only one image can be the default\n"); + exit(1); + } + nolabel= 1; + image= labels; + labels= nil; + } + len= 0; + if (!raw_install(image, &pos, &len, block_size)) exit(1); + + if (labels == nil) { + /* Let this image be the default. */ + sprintf(parmp, "image=%ld:%ld\n", pos-len, len); + parmp+= strlen(parmp); + } + + while (labels != nil) { + /* Image is prefixed by a comma separated list of + * labels. Define functions to select label and image. + */ + label= labels; + if ((labels= strchr(labels, ',')) != nil) *labels++ = 0; + + sprintf(parmp, + "%s(%c){label=%s;image=%ld:%ld;echo %s kernel selected;menu}\n", + label, + between('A', label[0], 'Z') + ? label[0]-'A'+'a' : label[0], + label, pos-len, len, label); + parmp+= strlen(parmp); + } + + if (parmp > buf + block_size) { + fprintf(stderr, + "installboot: Out of parameter space, too many images\n"); + exit(1); + } + } + /* Install boot block. */ + writeblock((off_t) BOOTBLOCK, buf, 1024); + + if (pos > fssize * RATIO(block_size)) { + /* Tell the total size of the data on the device. */ + printf("%16ld (%ld kb) total\n", pos, + (pos + RATIO(block_size) - 1) / RATIO(block_size)); + } +} + +void install_master(char *device, char *masterboot, char **guide) +/* Booting a hard disk is a two stage process: The master bootstrap in sector + * 0 loads the bootstrap from sector 0 of the active partition which in turn + * starts the operating system. This code installs such a master bootstrap + * on a hard disk. If guide[0] is non-null then the master bootstrap is + * guided into booting a certain device. + */ +{ + FILE *masf; + unsigned long size; + struct stat st; + static char buf[MAX_BLOCK_SIZE]; + + /* Open device. */ + if ((rawfd= open(rawdev= device, O_RDWR)) < 0) fatal(device); + + /* Open the master boot code. */ + if ((masf= fopen(masterboot, "r")) == nil) fatal(masterboot); + + /* See if the user is cloning a device. */ + if (fstat(fileno(masf), &st) >=0 && S_ISBLK(st.st_mode)) + size= PARTPOS; + else { + /* Read and check header otherwise. */ + struct image_header ihdr; + + read_header(1, masterboot, masf, &ihdr); + size= ihdr.process.a_text + ihdr.process.a_data; + } + if (size > PARTPOS) { + fprintf(stderr, "installboot: %s is too big\n", masterboot); + exit(1); + } + + /* Read the master boot block, patch it, write. */ + readblock(BOOTBLOCK, buf, BOOT_BLOCK_SIZE); + + memset(buf, 0, PARTPOS); + (void) bread(masf, masterboot, buf, size); + + if (guide[0] != nil) { + /* Fixate partition to boot. */ + char *keys= guide[0]; + char *logical= guide[1]; + size_t i; + int logfd; + u32_t offset; + struct partition geometry; + + /* A string of digits to be seen as keystrokes. */ + i= 0; + do { + if (!between('0', keys[i], '9')) { + fprintf(stderr, + "installboot: bad guide keys '%s'\n", + keys); + exit(1); + } + } while (keys[++i] != 0); + + if (size + i + 1 > PARTPOS) { + fprintf(stderr, + "installboot: not enough space after '%s' for '%s'\n", + masterboot, keys); + exit(1); + } + memcpy(buf + size, keys, i); + size += i; + buf[size]= '\r'; + + if (logical != nil) { + if ((logfd= open(logical, O_RDONLY)) < 0 + || ioctl(logfd, DIOCGETP, &geometry) < 0 + ) { + fatal(logical); + } + offset= div64u(geometry.base, SECTOR_SIZE); + if (size + 5 > PARTPOS) { + fprintf(stderr, + "installboot: not enough space " + "after '%s' for '%s' and an offset " + "to '%s'\n", + masterboot, keys, logical); + exit(1); + } + buf[size]= '#'; + memcpy(buf+size+1, &offset, 4); + } + } + + /* Install signature. */ + buf[SIGPOS+0]= (SIGNATURE >> 0) & 0xFF; + buf[SIGPOS+1]= (SIGNATURE >> 8) & 0xFF; + + writeblock(BOOTBLOCK, buf, BOOT_BLOCK_SIZE); +} + +void usage(void) +{ + fprintf(stderr, + "Usage: installboot -i(mage) image kernel mm fs ... init\n" + " installboot -(e)x(tract) image\n" + " installboot -d(evice) device bootblock boot [image ...]\n" + " installboot -b(oot) device bootblock boot image ...\n" + " installboot -m(aster) device masterboot [keys [logical]]\n"); + exit(1); +} + +int isoption(char *option, char *test) +/* Check if the option argument is equals "test". Also accept -i as short + * for -image, and the special case -x for -extract. + */ +{ + if (strcmp(option, test) == 0) return 1; + if (option[0] != '-' && strlen(option) != 2) return 0; + if (option[1] == test[1]) return 1; + if (option[1] == 'x' && test[1] == 'e') return 1; + return 0; +} + +int main(int argc, char **argv) +{ + if (argc < 2) usage(); + + if (argc >= 4 && isoption(argv[1], "-image")) { + make_image(argv[2], argv + 3); + } else + if (argc == 3 && isoption(argv[1], "-extract")) { + extract_image(argv[2]); + } else + if (argc >= 5 && isoption(argv[1], "-device")) { + make_bootable(FS, argv[2], argv[3], argv[4], argv + 5); + } else + if (argc >= 6 && isoption(argv[1], "-boot")) { + make_bootable(BOOT, argv[2], argv[3], argv[4], argv + 5); + } else + if ((4 <= argc && argc <= 6) && isoption(argv[1], "-master")) { + install_master(argv[2], argv[3], argv + 4); + } else { + usage(); + } + exit(0); +} + +/* + * $PchId: installboot.c,v 1.10 2000/08/13 22:07:50 philip Exp $ + */ diff --git a/boot/jumpboot.s b/boot/jumpboot.s new file mode 100755 index 000000000..6e758821e --- /dev/null +++ b/boot/jumpboot.s @@ -0,0 +1,261 @@ +! jumpboot 1.0 - Jump to another bootstrap Author: Kees J. Bot +! 14 Apr 1999 +! +! This code may be placed into any free boot sector, like the first sector +! of an extended partition, a file system partition other than the root, +! or even the master bootstrap. It will load and run another bootstrap whose +! disk, partition, and slice number (not necessarily all three) are patched +! into this code by installboot. If the ALT key is held down when this code +! is booted then you can type the disk, partition, and slice numbers manually. +! The manual interface is default if no numbers are patched in by installboot. +! + + o32 = 0x66 ! This assembler doesn't know 386 extensions + LOADOFF = 0x7C00 ! 0x0000:LOADOFF is where this code is loaded + BUFFER = 0x0600 ! First free memory + PART_TABLE = 446 ! Location of partition table within master + PENTRYSIZE = 16 ! Size of one partition table entry + MAGIC = 510 ! Location of the AA55 magic number + + ! : + MINIX_PART = 0x81 + sysind = 4 + lowsec = 8 + + +.text + +! Find and load another bootstrap and jump to it. +jumpboot: + xor ax, ax + mov ds, ax + mov es, ax + cli + mov ss, ax ! ds = es = ss = Vector segment + mov sp, #LOADOFF + sti + +! Move this code to safety, then jump to it. + mov si, sp ! si = start of this code + mov di, #BUFFER ! di = Buffer area + mov cx, #512/2 ! One sector + cld + rep movs + jmpf BUFFER+migrate, 0 ! To safety +migrate: + + mov bp, #BUFFER+guide ! Patched guiding characters +altkey: + movb ah, #0x02 ! Keyboard shift status + int 0x16 + testb al, #0x08 ! Bit 3 = ALT key + jz noalt ! ALT key pressed? +again: + mov bp, #zero ! Ignore patched stuff +noalt: + +! Follow guide characters to find the boot partition. + call print + .ascii "d?\b\0" ! Initial greeting + +! Disk number? +disk: + movb dl, #0x80 - 0x30 ! Prepare to add an ASCII digit + call getch ! Get number to tell which disk + addb dl, al ! dl = 0x80 + (al - '0') + jns n0nboot ! Result should be >= 0x80 + mov si, #BUFFER+zero-lowsec ! si = where lowsec(si) is zero + cmpb (bp), #0x23 ! Next guide character is '#'? + jne notlogical + lea si, 1-lowsec(bp) ! Logical sector offset follows '#' +notlogical: + call load ! Load chosen sector of chosen disk + cmpb (bp), #0x23 + je boot ! Run bootstrap if a logical is chosen + + call print ! Intro to partition number + .ascii "p?\b\0" + +part: + call getch ! Get character to tell partition + call gettable ! Get partition table + call sort ! Sort partition table + call choose_load ! Compute chosen entry and load + + cmpb sysind(si), #MINIX_PART ! Minix subpartition table possible? + jne waitboot + + call print ! Intro to slice number + .ascii "s?\b\0" + +slice: + call getch ! Get character to tell slice + call gettable ! Get partition table + call choose_load ! Compute chosen entry and load + +waitboot: + call print ! Intro to nothing + .ascii " ?\b\0" + call getch ! Supposed to type RETURN now +n0nboot:jmp nonboot ! Sorry, can't go further + +! Get a character, either the patched-in, or one from the keyboard. +getch: + movb al, (bp) ! Get patched-in character + testb al, al + jz getkey + inc bp + jmp gotkey +getkey: xorb ah, ah ! Wait for keypress + int 0x16 +gotkey: testb dl, dl ! Ignore CR if disk number not yet set + jns putch + cmpb al, #0x0D ! Carriage return? + je boot + !jmp putch + +! Print a character +putch: movb ah, #0x0E ! Print character in teletype mode + mov bx, #0x0001 ! Page 0, foreground color + int 0x10 + ret + +! Print a message. +print: mov cx, si ! Save si + pop si ! si = String following 'call print' +prnext: lodsb ! al = *si++ is char to be printed + testb al, al ! Null marks end + jz prdone + call putch + jmp prnext +prdone: xchg si, cx ! Restore si + jmp (cx) ! Continue after the string + +! Return typed (or in patched data) means to run the bootstrap now in core! +boot: + call print ! Make line on screen look proper + .ascii "\b \r\n\0" + jmp LOADOFF-BUFFER ! Jump to LOADOFF + +! Compute address of chosen partition entry from choice al into si, then +! continue to load the boot sector of that partition. +choose_load: + subb al, #0x30 ! al -= '0' + cmpb al, #4 ! Only four partitions + ja n0nboot + movb ah, #PENTRYSIZE + mulb ah ! al *= PENTRYSIZE + add ax, #BUFFER+PART_TABLE + mov si, ax ! si = &part_table[al - '0'] + movb al, sysind(si) ! System indicator + testb al, al ! Unused partition? + jz n0nboot + !jmp load ! Continue to load boot sector + +! Load boot sector of the current partition. +load: + push dx ! Save drive code + push es ! Next call sets es + movb ah, #0x08 ! Code for drive parameters + int 0x13 + pop es + andb cl, #0x3F ! cl = max sector number (1-origin) + incb dh ! dh = 1 + max head number (0-origin) + movb al, cl ! al = cl = sectors per track + mulb dh ! dh = heads, ax = heads * sectors + mov bx, ax ! bx = sectors per cylinder = heads * sectors + mov ax, lowsec+0(si) + mov dx, lowsec+2(si) ! dx:ax = sector within drive + cmp dx, #[1024*255*63-255]>>16 ! Near 8G limit? + jae bigdisk + div bx ! ax = cylinder, dx = sector within cylinder + xchg ax, dx ! ax = sector within cylinder, dx = cylinder + movb ch, dl ! ch = low 8 bits of cylinder + divb cl ! al = head, ah = sector (0-origin) + xorb dl, dl ! About to shift bits 8-9 of cylinder into dl + shr dx, #1 + shr dx, #1 ! dl[6..7] = high cylinder + orb dl, ah ! dl[0..5] = sector (0-origin) + movb cl, dl ! cl[0..5] = sector, cl[6..7] = high cyl + incb cl ! cl[0..5] = sector (1-origin) + pop dx ! Restore drive code in dl + movb dh, al ! dh = al = head + mov bx, #LOADOFF ! es:bx = where sector is loaded + mov ax, #0x0201 ! ah = Code for read / al = one sector + int 0x13 + jmp rdeval ! Evaluate read result +bigdisk: + mov bx, dx ! bx:ax = dx:ax = sector to read + pop dx ! Restore drive code in dl + push si ! Save si + mov si, #BUFFER+ext_rw ! si = extended read/write parameter packet + mov 8(si), ax ! Starting block number = bx:ax + mov 10(si), bx + movb ah, #0x42 ! Extended read + int 0x13 + pop si ! Restore si to point to partition entry + !jmp rdeval +rdeval: + jnc rdok +rderr: + call print + .ascii "\r\nRead error\r\n\0" + jmp again +rdok: + cmp LOADOFF+MAGIC, #0xAA55 + je sigok ! Signature ok? +nonboot: + call print + .ascii "\r\nNot bootable\r\n\0" + jmp again +sigok: + ret + +! Get the partition table into my space. +gettable: + mov si, #LOADOFF+PART_TABLE + mov di, #BUFFER+PART_TABLE + mov cx, #4*PENTRYSIZE/2 + rep movs + ret + +! Sort the partition table. +sort: + mov cx, #4 ! Four times is enough to sort +bubble: mov si, #BUFFER+PART_TABLE ! First table entry +bubble1:lea di, PENTRYSIZE(si) ! Next entry + cmpb sysind(si), ch ! Partition type, nonzero when in use + jz exchg ! Unused entries sort to the end +inuse: mov bx, lowsec+0(di) + sub bx, lowsec+0(si) ! Compute di->lowsec - si->lowsec + mov bx, lowsec+2(di) + sbb bx, lowsec+2(si) + jae order ! In order if si->lowsec <= di->lowsec +exchg: movb bl, (si) + xchgb bl, PENTRYSIZE(si) ! Exchange entries byte by byte + movb (si), bl + inc si + cmp si, di + jb exchg +order: mov si, di + cmp si, #BUFFER+PART_TABLE+3*PENTRYSIZE + jb bubble1 + loop bubble + ret + +.data + +! Extended read/write commands require a parameter packet. +ext_rw: + .data1 0x10 ! Length of extended r/w packet + .data1 0 ! Reserved + .data2 1 ! Blocks to transfer (just one) + .data2 LOADOFF ! Buffer address offset + .data2 0 ! Buffer address segment + .data4 0 ! Starting block number low 32 bits (tbfi) +zero: .data4 0 ! Starting block number high 32 bits + + .align 2 +guide: +! Guide characters and possibly a logical partition number patched here by +! installboot, up to 6 bytes maximum. diff --git a/boot/masterboot.s b/boot/masterboot.s new file mode 100755 index 000000000..9294c9890 --- /dev/null +++ b/boot/masterboot.s @@ -0,0 +1,218 @@ +! masterboot 2.0 - Master boot block code Author: Kees J. Bot +! +! This code may be placed in the first sector (the boot sector) of a floppy, +! hard disk or hard disk primary partition. There it will perform the +! following actions at boot time: +! +! - If the booted device is a hard disk and one of the partitions is active +! then the active partition is booted. +! +! - Otherwise the next floppy or hard disk device is booted, trying them one +! by one. +! +! To make things a little clearer, the boot path might be: +! /dev/fd0 - Floppy disk containing data, tries fd1 then d0 +! [/dev/fd1] - Drive empty +! /dev/c0d0 - Master boot block, selects active partition 2 +! /dev/c0d0p2 - Submaster, selects active subpartition 0 +! /dev/c0d0p2s0 - Minix bootblock, reads Boot Monitor /boot +! Minix - Started by /boot from a kernel image in /minix + + LOADOFF = 0x7C00 ! 0x0000:LOADOFF is where this code is loaded + BUFFER = 0x0600 ! First free memory + PART_TABLE = 446 ! Location of partition table within this code + PENTRYSIZE = 16 ! Size of one partition table entry + MAGIC = 510 ! Location of the AA55 magic number + + ! .h: + bootind = 0 + sysind = 4 + lowsec = 8 + + +.text + +! Find active (sub)partition, load its first sector, run it. + +master: + xor ax, ax + mov ds, ax + mov es, ax + cli + mov ss, ax ! ds = es = ss = Vector segment + mov sp, #LOADOFF + sti + +! Copy this code to safety, then jump to it. + mov si, sp ! si = start of this code + push si ! Also where we'll return to eventually + mov di, #BUFFER ! Buffer area + mov cx, #512/2 ! One sector + cld + rep movs + jmpf BUFFER+migrate, 0 ! To safety +migrate: + +! Find the active partition +findactive: + testb dl, dl + jns nextdisk ! No bootable partitions on floppies + mov si, #BUFFER+PART_TABLE +find: cmpb sysind(si), #0 ! Partition type, nonzero when in use + jz nextpart + testb bootind(si), #0x80 ! Active partition flag in bit 7 + jz nextpart ! It's not active +loadpart: + call load ! Load partition bootstrap + jc error1 ! Not supposed to fail +bootstrap: + ret ! Jump to the master bootstrap +nextpart: + add si, #PENTRYSIZE + cmp si, #BUFFER+PART_TABLE+4*PENTRYSIZE + jb find +! No active partition, tell 'em + call print + .ascii "No active partition\0" + jmp reboot + +! There are no active partitions on this drive, try the next drive. +nextdisk: + incb dl ! Increment dl for the next drive + testb dl, dl + js nexthd ! Hard disk if negative + int 0x11 ! Get equipment configuration + shl ax, #1 ! Highest floppy drive # in bits 6-7 + shl ax, #1 ! Now in bits 0-1 of ah + andb ah, #0x03 ! Extract bits + cmpb dl, ah ! Must be dl <= ah for drive to exist + ja nextdisk ! Otherwise try disk 0 eventually + call load0 ! Read the next floppy bootstrap + jc nextdisk ! It failed, next disk please + ret ! Jump to the next master bootstrap +nexthd: call load0 ! Read the hard disk bootstrap +error1: jc error ! No disk? + ret + + +! Load sector 0 from the current device. It's either a floppy bootstrap or +! a hard disk master bootstrap. +load0: + mov si, #BUFFER+zero-lowsec ! si = where lowsec(si) is zero + !jmp load + +! Load sector lowsec(si) from the current device. The obvious head, sector, +! and cylinder numbers are ignored in favour of the more trustworthy absolute +! start of partition. +load: + mov di, #3 ! Three retries for floppy spinup +retry: push dx ! Save drive code + push es + push di ! Next call destroys es and di + movb ah, #0x08 ! Code for drive parameters + int 0x13 + pop di + pop es + andb cl, #0x3F ! cl = max sector number (1-origin) + incb dh ! dh = 1 + max head number (0-origin) + movb al, cl ! al = cl = sectors per track + mulb dh ! dh = heads, ax = heads * sectors + mov bx, ax ! bx = sectors per cylinder = heads * sectors + mov ax, lowsec+0(si) + mov dx, lowsec+2(si)! dx:ax = sector within drive + cmp dx, #[1024*255*63-255]>>16 ! Near 8G limit? + jae bigdisk + div bx ! ax = cylinder, dx = sector within cylinder + xchg ax, dx ! ax = sector within cylinder, dx = cylinder + movb ch, dl ! ch = low 8 bits of cylinder + divb cl ! al = head, ah = sector (0-origin) + xorb dl, dl ! About to shift bits 8-9 of cylinder into dl + shr dx, #1 + shr dx, #1 ! dl[6..7] = high cylinder + orb dl, ah ! dl[0..5] = sector (0-origin) + movb cl, dl ! cl[0..5] = sector, cl[6..7] = high cyl + incb cl ! cl[0..5] = sector (1-origin) + pop dx ! Restore drive code in dl + movb dh, al ! dh = al = head + mov bx, #LOADOFF ! es:bx = where sector is loaded + mov ax, #0x0201 ! Code for read, just one sector + int 0x13 ! Call the BIOS for a read + jmp rdeval ! Evaluate read result +bigdisk: + mov bx, dx ! bx:ax = dx:ax = sector to read + pop dx ! Restore drive code in dl + push si ! Save si + mov si, #BUFFER+ext_rw ! si = extended read/write parameter packet + mov 8(si), ax ! Starting block number = bx:ax + mov 10(si), bx + movb ah, #0x42 ! Extended read + int 0x13 + pop si ! Restore si to point to partition entry + !jmp rdeval +rdeval: + jnc rdok ! Read succeeded + cmpb ah, #0x80 ! Disk timed out? (Floppy drive empty) + je rdbad + dec di + jl rdbad ! Retry count expired + xorb ah, ah + int 0x13 ! Reset + jnc retry ! Try again +rdbad: stc ! Set carry flag + ret +rdok: cmp LOADOFF+MAGIC, #0xAA55 + jne nosig ! Error if signature wrong + ret ! Return with carry still clear +nosig: call print + .ascii "Not bootable\0" + jmp reboot + +! A read error occurred, complain and hang +error: + mov si, #LOADOFF+errno+1 +prnum: movb al, ah ! Error number in ah + andb al, #0x0F ! Low 4 bits + cmpb al, #10 ! A-F? + jb digit ! 0-9! + addb al, #7 ! 'A' - ':' +digit: addb (si), al ! Modify '0' in string + dec si + movb cl, #4 ! Next 4 bits + shrb ah, cl + jnz prnum ! Again if digit > 0 + call print + .ascii "Read error " +errno: .ascii "00\0" + !jmp reboot + +reboot: + call print + .ascii ". Hit any key to reboot.\0" + xorb ah, ah ! Wait for keypress + int 0x16 + call print + .ascii "\r\n\0" + int 0x19 + +! Print a message. +print: pop si ! si = String following 'call print' +prnext: lodsb ! al = *si++ is char to be printed + testb al, al ! Null marks end + jz prdone + movb ah, #0x0E ! Print character in teletype mode + mov bx, #0x0001 ! Page 0, foreground color + int 0x10 + jmp prnext +prdone: jmp (si) ! Continue after the string + +.data + +! Extended read/write commands require a parameter packet. +ext_rw: + .data1 0x10 ! Length of extended r/w packet + .data1 0 ! Reserved + .data2 1 ! Blocks to transfer (just one) + .data2 LOADOFF ! Buffer address offset + .data2 0 ! Buffer address segment + .data4 0 ! Starting block number low 32 bits (tbfi) +zero: .data4 0 ! Starting block number high 32 bits diff --git a/boot/mkfhead.s b/boot/mkfhead.s new file mode 100755 index 000000000..221e1faea --- /dev/null +++ b/boot/mkfhead.s @@ -0,0 +1,137 @@ +! Mkfhead.s - DOS & BIOS support for mkfile.c Author: Kees J. Bot +! 9 May 1998 +! +! This file contains the startup and low level support for the MKFILE.COM +! utility. See doshead.ack.s for more comments on .COM files. +! +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text + +.define _PSP +_PSP: + .space 256 ! Program Segment Prefix + +mkfile: + cld ! C compiler wants UP + xor ax, ax ! Zero + mov di, _edata ! Start of bss is at end of data + mov cx, _end ! End of bss (begin of heap) + sub cx, di ! Number of bss bytes + shr cx, 1 ! Number of words + rep stos ! Clear bss + + xor cx, cx ! cx = argc + xor bx, bx + push bx ! argv[argc] = NULL + movb bl, (_PSP+0x80) ! Argument byte count +0: movb _PSP+0x81(bx), ch ! Null terminate + dec bx + js 9f + cmpb _PSP+0x81(bx), 0x20 ! Whitespace? + jbe 0b +1: dec bx ! One argument character + js 2f + cmpb _PSP+0x81(bx), 0x20 ! More argument characters? + ja 1b +2: lea ax, _PSP+0x81+1(bx) ! Address of argument + push ax ! argv[n] + inc cx ! argc++; + test bx, bx + jns 0b ! More arguments? +9: movb _PSP+0x81(bx), ch ! Make a null string + lea ax, _PSP+0x81(bx) + push ax ! to use as argv[0] + inc cx ! Final value of argc + mov ax, sp + push ax ! argv + push cx ! argc + call _main ! main(argc, argv) + push ax + call _exit ! exit(main(argc, argv)) + +! int creat(const char *path, mode_t mode) +! Create a file with the old creat() call. +.define _creat +_creat: + mov bx, sp + mov dx, 2(bx) ! Filename + xor cx, cx ! Ignore mode, always read-write + movb ah, 0x3C ! "CREAT" +dos: int 0x21 ! ax = creat(path, 0666); + jc seterrno + ret + +seterrno: + mov (_errno), ax ! Set errno to the DOS error code + mov ax, -1 + cwd ! return -1L; + ret + +! int open(const char *path, int oflag) +! Open a file with the oldfashioned two-argument open() call. +.define _open +_open: + mov bx, sp + mov dx, 2(bx) ! Filename + movb al, 4(bx) ! O_RDONLY, O_WRONLY, O_RDWR + movb ah, 0x3D ! "OPEN" + jmp dos + +! int close(int fd) +! Close an open file. +.define _close +_close: + mov bx, sp + mov bx, 2(bx) ! bx = file handle + movb ah, 0x3E ! "CLOSE" + jmp dos + +! void exit(int status) +! void _exit(int status) +! Return to DOS. +.define _exit, __exit, ___exit +_exit: +__exit: +___exit: + pop ax + pop ax ! al = status + movb ah, 0x4C ! "EXIT" + int 0x21 + hlt + +! ssize_t read(int fd, void *buf, size_t n) +! Read bytes from an open file. +.define _read +_read: + mov bx, sp + mov cx, 6(bx) + mov dx, 4(bx) + mov bx, 2(bx) + movb ah, 0x3F ! "READ" + jmp dos + +! ssize_t write(int fd, const void *buf, size_t n) +! Write bytes to an open file. +.define _write +_write: + mov bx, sp + mov cx, 6(bx) + mov dx, 4(bx) + mov bx, 2(bx) + movb ah, 0x40 ! "WRITE" + jmp dos + +! off_t lseek(int fd, off_t offset, int whence) +! Set file position for read or write. +.define _lseek +_lseek: + mov bx, sp + movb al, 8(bx) ! SEEK_SET, SEEK_CUR, SEEK_END + mov dx, 4(bx) + mov cx, 6(bx) ! cx:dx = offset + mov bx, 2(bx) + movb ah, 0x42 ! "LSEEK" + jmp dos + +! +! $PchId: mkfhead.ack.s,v 1.3 1999/01/14 21:17:06 philip Exp $ diff --git a/boot/mkfile.c b/boot/mkfile.c new file mode 100755 index 000000000..bee810f4b --- /dev/null +++ b/boot/mkfile.c @@ -0,0 +1,180 @@ +/* mkfile 1.0 - create a file under DOS for use as a Minix "disk". + * Author: Kees J. Bot + * 9 May 1998 + */ +#define nil 0 +#include +#include +#include + +/* Stuff normally found in , , etc. */ +extern int errno; +int creat(const char *file, int mode); +int open(const char *file, int oflag); +off_t lseek(int fd, off_t offset, int whence); +ssize_t write(int fd, const char *buf, size_t len); +void exit(int status); +int printf(const char *fmt, ...); + +#define O_WRONLY 1 +#define SEEK_SET 0 +#define SEEK_END 2 + +/* Kernel printf requires a putk() function. */ +int putk(int c) +{ + char ch = c; + + if (c == 0) return; + if (c == '\n') putk('\r'); + (void) write(2, &ch, 1); +} + +static void usage(void) +{ + printf("Usage: mkfile [gmk] \n" + "(Example sizes, all 50 meg: 52428800, 51200k, 50m)\n"); + exit(1); +} + +char *strerror(int err) +/* Translate some DOS error numbers to text. */ +{ + static struct errlist { + int err; + char *what; + } errlist[] = { + { 0, "No error" }, + { 1, "Function number invalid" }, + { 2, "File not found" }, + { 3, "Path not found" }, + { 4, "Too many open files" }, + { 5, "Access denied" }, + { 6, "Invalid handle" }, + { 12, "Access code invalid" }, + { 39, "Insufficient disk space" }, + }; + struct errlist *ep; + static char unknown[]= "Error 65535"; + unsigned e; + char *p; + + for (ep= errlist; ep < errlist + sizeof(errlist)/sizeof(errlist[0]); + ep++) { + if (ep->err == err) return ep->what; + } + p= unknown + sizeof(unknown) - 1; + e= err; + do *--p= '0' + (e % 10); while ((e /= 10) > 0); + strcpy(unknown + 6, p); + return unknown; +} + +int main(int argc, char **argv) +{ + int i; + static char buf[512]; + unsigned long size, mul; + off_t offset; + char *cp; + int fd; + char *file; + + if (argc != 3) usage(); + + cp= argv[1]; + size= 0; + while ((unsigned) (*cp - '0') < 10) { + unsigned d= *cp++ - '0'; + if (size <= (ULONG_MAX-9) / 10) { + size= size * 10 + d; + } else { + size= ULONG_MAX; + } + } + if (cp == argv[1]) usage(); + while (*cp != 0) { + mul = 1; + switch (*cp++) { + case 'G': + case 'g': mul *= 1024; + case 'M': + case 'm': mul *= 1024; + case 'K': + case 'k': mul *= 1024; + case 'B': + case 'b': break; + default: usage(); + } + if (size <= ULONG_MAX / mul) { + size *= mul; + } else { + size= ULONG_MAX; + } + } + + if (size > 1024L*1024*1024) { + printf("mkfile: A file size over 1G is a bit too much\n"); + exit(1); + } + + /* Open existing file, or create a new file. */ + file= argv[2]; + if ((fd= open(file, O_WRONLY)) < 0) { + if (errno == 2) { + fd= creat(file, 0666); + } + } + if (fd < 0) { + printf("mkfile: Can't open %s: %s\n", file, strerror(errno)); + exit(1); + } + + /* How big is the file now? */ + if ((offset= lseek(fd, 0, SEEK_END)) == -1) { + printf("mkfile: Can't seek in %s: %s\n", file, strerror(errno)); + exit(1); + } + + if (offset == 0 && size == 0) exit(0); /* Huh? */ + + /* Write the first bit if the file is zero length. This is necessary + * to circumvent a DOS bug by extending a new file by lseek. We also + * want to make sure there are zeros in the first sector. + */ + if (offset == 0) { + if (write(fd, buf, sizeof(buf)) == -1) { + printf("mkfile: Can't write to %s: %s\n", + file, strerror(errno)); + exit(1); + } + } + + /* Seek to the required size and write 0 bytes to extend/truncate the + * file to that size. + */ + if (lseek(fd, size, SEEK_SET) == -1) { + printf("mkfile: Can't seek in %s: %s\n", file, strerror(errno)); + exit(1); + } + if (write(fd, buf, 0) == -1) { + printf("mkfile: Can't write to %s: %s\n", + file, strerror(errno)); + exit(1); + } + + /* Did the file become the required size? */ + if ((offset= lseek(fd, 0, SEEK_END)) == -1) { + printf("mkfile: Can't seek in %s: %s\n", file, strerror(errno)); + exit(1); + } + if (offset != size) { + printf("mkfile: Failed to extend %s. Disk full?\n", file); + exit(1); + } + return 0; +} + +/* + * $PchId: mkfile.c,v 1.4 2000/08/13 22:06:40 philip Exp $ + */ diff --git a/boot/rawfs.c b/boot/rawfs.c new file mode 100755 index 000000000..072028634 --- /dev/null +++ b/boot/rawfs.c @@ -0,0 +1,350 @@ +/* rawfs.c - Raw Minix file system support. Author: Kees J. Bot + * 23 Dec 1991 + * Based on readfs by Paul Polderman + */ +#define nil 0 +#define _POSIX_SOURCE 1 +#define _MINIX 1 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "rawfs.h" + +void readblock(off_t blockno, char *buf, int); + +/* The following code handles two file system types: Version 1 with small + * inodes and 16-bit disk addresses and Version 2 with big inodes and 32-bit + * disk addresses. +#ifdef FLEX + * To make matters worse, Minix-vmd knows about the normal Unix Version 7 + * directories and directories with flexible entries. +#endif + */ + +/* File system parameters. */ +static unsigned nr_dzones; /* Fill these in after reading superblock. */ +static unsigned nr_indirects; +static unsigned inodes_per_block; +static int block_size; +#ifdef FLEX +#include +#define direct _v7_direct +#else +#include +#endif + +#if __minix_vmd +static struct v12_super_block super; /* Superblock of file system */ +#define s_log_zone_size s_dummy /* Zones are obsolete. */ +#else +static struct super_block super; /* Superblock of file system */ +#define SUPER_V1 SUPER_MAGIC /* V1 magic has a weird name. */ +#endif + +#define RAWFS_MAX_BLOCK_SIZE 1024 + +static struct inode curfil; /* Inode of file under examination */ +static char indir[RAWFS_MAX_BLOCK_SIZE]; /* Single indirect block. */ +static char dindir[RAWFS_MAX_BLOCK_SIZE]; /* Double indirect block. */ +static char dirbuf[RAWFS_MAX_BLOCK_SIZE]; /* Scratch/Directory block. */ +#define scratch dirbuf + +static block_t a_indir, a_dindir; /* Addresses of the indirects. */ +static off_t dirpos; /* Reading pos in a dir. */ + +#define fsbuf(b) (* (struct buf *) (b)) + +#define zone_shift (super.s_log_zone_size) /* zone to block ratio */ + +off_t r_super(int *bs) +/* Initialize variables, return size of file system in blocks, + * (zero on error). + */ +{ + /* Read superblock. */ + readblock(1, scratch, 1024); + + memcpy(&super, scratch, sizeof(super)); + + printf("super: %lx\n", super.s_magic); + + /* Is it really a MINIX file system ? */ + if (super.s_magic == SUPER_V2 || super.s_magic == SUPER_V3) { + if(super.s_magic == SUPER_V2) + super.s_block_size = 1024; + *bs = block_size = super.s_block_size; + printf("max_size: %d zones: %d imap_blocks: %d zones: %d\n", + super.s_max_size, super.s_imap_blocks, + super.s_zmap_blocks, super.s_nzones); + if(block_size < MIN_BLOCK_SIZE || + block_size > RAWFS_MAX_BLOCK_SIZE) { + printf("bogus block size %d\n", block_size); + return 0; + } + nr_dzones= V2_NR_DZONES; + nr_indirects= V2_INDIRECTS(block_size); + inodes_per_block= V2_INODES_PER_BLOCK(block_size); + printf("v2/v3 %d ok\n", block_size); + printf("ipb %d\n", inodes_per_block); + return (off_t) super.s_zones << zone_shift; + } else + if (super.s_magic == SUPER_V1) { + *bs = block_size = 1024; + nr_dzones= V1_NR_DZONES; + nr_indirects= V1_INDIRECTS; + inodes_per_block= V1_INODES_PER_BLOCK; + printf("v1 ok\n"); + return (off_t) super.s_nzones << zone_shift; + } else { + /* Filesystem not recognized as Minix. */ + printf("not minix\n"); + return 0; + } +} + +void r_stat(Ino_t inum, struct stat *stp) +/* Return information about a file like stat(2) and remember it. */ +{ + block_t block; + block_t ino_block; + ino_t ino_offset; + + /* Calculate start of i-list */ + block = START_BLOCK + super.s_imap_blocks + super.s_zmap_blocks; + + printf("r_stat: start of i-list: %d (ipb %d)\n", + block, inodes_per_block); + + /* Calculate block with inode inum */ + ino_block = ((inum - 1) / inodes_per_block); + ino_offset = ((inum - 1) % inodes_per_block); + block += ino_block; + + printf("r_stat: block with inode: %d - readblock %d..\n", block, block_size); + + /* Fetch the block */ + readblock(block, scratch, block_size); + printf("r_stat: readblock done..\n", block); + + if (super.s_magic == SUPER_V2 || super.s_magic == SUPER_V3) { + d2_inode *dip; + int i; + + dip= &fsbuf(scratch).b_v2_ino[ino_offset]; + + curfil.i_mode= dip->d2_mode; + curfil.i_nlinks= dip->d2_nlinks; + curfil.i_uid= dip->d2_uid; + curfil.i_gid= dip->d2_gid; + curfil.i_size= dip->d2_size; + curfil.i_atime= dip->d2_atime; + curfil.i_mtime= dip->d2_mtime; + curfil.i_ctime= dip->d2_ctime; + for (i= 0; i < V2_NR_TZONES; i++) + curfil.i_zone[i]= dip->d2_zone[i]; + } else { + d1_inode *dip; + int i; + + dip= &fsbuf(scratch).b_v1_ino[ino_offset]; + + curfil.i_mode= dip->d1_mode; + curfil.i_nlinks= dip->d1_nlinks; + curfil.i_uid= dip->d1_uid; + curfil.i_gid= dip->d1_gid; + curfil.i_size= dip->d1_size; + curfil.i_atime= dip->d1_mtime; + curfil.i_mtime= dip->d1_mtime; + curfil.i_ctime= dip->d1_mtime; + for (i= 0; i < V1_NR_TZONES; i++) + curfil.i_zone[i]= dip->d1_zone[i]; + } + curfil.i_dev= -1; /* Can't fill this in alas. */ + curfil.i_num= inum; + + stp->st_dev= curfil.i_dev; + stp->st_ino= curfil.i_num; + stp->st_mode= curfil.i_mode; + stp->st_nlink= curfil.i_nlinks; + stp->st_uid= curfil.i_uid; + stp->st_gid= curfil.i_gid; + stp->st_rdev= (dev_t) curfil.i_zone[0]; + stp->st_size= curfil.i_size; + stp->st_atime= curfil.i_atime; + stp->st_mtime= curfil.i_mtime; + stp->st_ctime= curfil.i_ctime; + + a_indir= a_dindir= 0; + dirpos= 0; +} + +ino_t r_readdir(char *name) +/* Read next directory entry at "dirpos" from file "curfil". */ +{ + ino_t inum= 0; + int blkpos; + struct direct *dp; + + if (!S_ISDIR(curfil.i_mode)) { errno= ENOTDIR; return -1; } + + if(!block_size) { errno = 0; return -1; } + + while (inum == 0 && dirpos < curfil.i_size) { + if ((blkpos= (int) (dirpos % block_size)) == 0) { + /* Need to fetch a new directory block. */ + + readblock(r_vir2abs(dirpos / block_size), dirbuf, block_size); + } +#ifdef FLEX + if (super.s_flags & S_FLEX) { + struct _fl_direct *dp; + + dp= (struct _fl_direct *) (dirbuf + blkpos); + if ((inum= dp->d_ino) != 0) strcpy(name, dp->d_name); + + dirpos+= (1 + dp->d_extent) * FL_DIR_ENTRY_SIZE; + continue; + } +#endif + /* Let dp point to the next entry. */ + dp= (struct direct *) (dirbuf + blkpos); + + if ((inum= dp->d_ino) != 0) { + /* This entry is occupied, return name. */ + strncpy(name, dp->d_name, sizeof(dp->d_name)); + name[sizeof(dp->d_name)]= 0; + } + dirpos+= DIR_ENTRY_SIZE; + } + return inum; +} + +off_t r_vir2abs(off_t virblk) +/* Translate a block number in a file to an absolute disk block number. + * Returns 0 for a hole and -1 if block is past end of file. + */ +{ + block_t b= virblk; + zone_t zone, ind_zone; + block_t z, zone_index; + int i; + + if(!block_size) return -1; + + /* Check if virblk within file. */ + if (virblk * block_size >= curfil.i_size) return -1; + + /* Calculate zone in which the datablock number is contained */ + zone = (zone_t) (b >> zone_shift); + + /* Calculate index of the block number in the zone */ + zone_index = b - ((block_t) zone << zone_shift); + + /* Go get the zone */ + if (zone < (zone_t) nr_dzones) { /* direct block */ + zone = curfil.i_zone[(int) zone]; + z = ((block_t) zone << zone_shift) + zone_index; + return z; + } + + /* The zone is not a direct one */ + zone -= (zone_t) nr_dzones; + + /* Is it single indirect ? */ + if (zone < (zone_t) nr_indirects) { /* single indirect block */ + ind_zone = curfil.i_zone[nr_dzones]; + } else { /* double indirect block */ + /* Fetch the double indirect block */ + if ((ind_zone = curfil.i_zone[nr_dzones + 1]) == 0) return 0; + + z = (block_t) ind_zone << zone_shift; + if (a_dindir != z) { + readblock(z, dindir, block_size); + a_dindir= z; + } + /* Extract the indirect zone number from it */ + zone -= (zone_t) nr_indirects; + + i = zone / (zone_t) nr_indirects; + ind_zone = (super.s_magic == SUPER_V2 || super.s_magic == SUPER_V3) + ? fsbuf(dindir).b_v2_ind[i] + : fsbuf(dindir).b_v1_ind[i]; + zone %= (zone_t) nr_indirects; + } + if (ind_zone == 0) return 0; + + /* Extract the datablock number from the indirect zone */ + z = (block_t) ind_zone << zone_shift; + if (a_indir != z) { + readblock(z, indir, block_size); + a_indir= z; + } + zone = (super.s_magic == SUPER_V2 || super.s_magic == SUPER_V3) + ? fsbuf(indir).b_v2_ind[(int) zone] + : fsbuf(indir).b_v1_ind[(int) zone]; + + /* Calculate absolute datablock number */ + z = ((block_t) zone << zone_shift) + zone_index; + return z; +} + +ino_t r_lookup(Ino_t cwd, char *path) +/* Translates a pathname to an inode number. This is just a nice utility + * function, it only needs r_stat and r_readdir. + */ +{ + char name[NAME_MAX+1], r_name[NAME_MAX+1]; + char *n; + struct stat st; + ino_t ino; + + ino= path[0] == '/' ? ROOT_INO : cwd; + + for (;;) { + printf("ino %d..\n", ino); + if (ino == 0) { + errno= ENOENT; + return 0; + } + + while (*path == '/') path++; + + if (*path == 0) return ino; + + printf("r_stat..\n"); + r_stat(ino, &st); + printf("r_stat done..\n"); + + if (!S_ISDIR(st.st_mode)) { + errno= ENOTDIR; + return 0; + } + + n= name; + while (*path != 0 && *path != '/') + if (n < name + NAME_MAX) *n++ = *path++; + *n= 0; + + printf("r_readdir..\n"); + while ((ino= r_readdir(r_name)) != 0 + && strcmp(name, r_name) != 0) { + printf("r_readdir %s isn't it..\n", r_name); + } + printf("after readdir loop: %s %s\n", name, r_name); + } +} + +/* + * $PchId: rawfs.c,v 1.8 1999/11/05 23:14:15 philip Exp $ + */ diff --git a/boot/rawfs.h b/boot/rawfs.h new file mode 100755 index 000000000..fea904cf0 --- /dev/null +++ b/boot/rawfs.h @@ -0,0 +1,43 @@ +/* rawfs.h - Raw Minix file system support. Author: Kees J. Bot + * + * off_t r_super(int *block_size); + * Initialize variables, returns the size of a valid Minix + * file system blocks, but zero on error. + * + * void r_stat(ino_t file, struct stat *stp); + * Return information about a file like stat(2) and + * remembers file for the next two calls. + * + * off_t r_vir2abs(off_t virblockno); + * Translate virtual block number in file to absolute + * disk block number. Returns 0 if the file contains + * a hole, or -1 if the block lies past the end of file. + * + * ino_t r_readdir(char *name); + * Return next directory entry or 0 if there are no more. + * Returns -1 and sets errno on error. + * + * ino_t r_lookup(ino_t cwd, char *path); + * A utility function that translates a pathname to an + * inode number. It starts from directory "cwd" unless + * path starts with a '/', then from ROOT_INO. + * Returns 0 and sets errno on error. + * + * One function needs to be provided by the outside world: + * + * void readblock(off_t blockno, char *buf, int block_size); + * Read a block into the buffer. Outside world handles + * errors. + */ + +#define ROOT_INO ((ino_t) 1) /* Inode nr of root dir. */ + +off_t r_super(int *); +void r_stat(Ino_t file, struct stat *stp); +off_t r_vir2abs(off_t virblockno); +ino_t r_readdir(char *name); +ino_t r_lookup(Ino_t cwd, char *path); + +/* + * $PchId: rawfs.h,v 1.4 1996/04/19 08:16:36 philip Exp $ + */ diff --git a/commands/Makefile b/commands/Makefile new file mode 100755 index 000000000..1108b27c4 --- /dev/null +++ b/commands/Makefile @@ -0,0 +1,47 @@ +# Makefile for commands. + +MAKE = exec make -$(MAKEFLAGS) + +usage: + @echo "Usage: make all # Compile all commands" >&2 + @echo " make install # Install the result (run as bin!)" >&2 + @echo " make clean # Delete .o files and other junk" >&2 + @false + +all install clean: + cd `arch` && $(MAKE) $@ + cd aal && $(MAKE) $@ + cd advent && $(MAKE) $@ + cd ash && $(MAKE) $@ + cd autil && $(MAKE) $@ + cd awk && $(MAKE) $@ + cd bc && $(MAKE) $@ + cd byacc && $(MAKE) $@ + cd cawf && $(MAKE) $@ + cd cron && $(MAKE) $@ + cd de && $(MAKE) $@ + cd dhcpd && $(MAKE) $@ + cd dis88 && $(MAKE) $@ + cd elle && $(MAKE) $@ + cd elvis && $(MAKE) $@ + cd flex-2.3.7 && $(MAKE) $@ + cd ftp && $(MAKE) $@ + cd ftpd && $(MAKE) $@ + cd ibm && $(MAKE) $@ + cd kermit && $(MAKE) $@ + cd m4 && $(MAKE) $@ + cd make && $(MAKE) $@ + cd mined && $(MAKE) $@ + cd patch && $(MAKE) $@ + cd reboot && $(MAKE) $@ + cd rlogind && $(MAKE) $@ + cd scripts && $(MAKE) $@ + cd sh && $(MAKE) $@ + cd simple && $(MAKE) $@ + cd talk && $(MAKE) $@ + cd talkd && $(MAKE) $@ + cd telnet && $(MAKE) $@ + cd telnetd && $(MAKE) $@ + cd urlget && $(MAKE) $@ + cd yap && $(MAKE) $@ + cd zmodem && $(MAKE) $@ diff --git a/commands/aal/Makefile b/commands/aal/Makefile new file mode 100755 index 000000000..ca92521ac --- /dev/null +++ b/commands/aal/Makefile @@ -0,0 +1,56 @@ +# Makefile for aal + +CC=exec cc +CFLAGS=-I. -wo -DAAL -DSTB -DNDEBUG -DDISTRIBUTION -D_POSIX_SOURCE -D_MINIX +LDFLAGS=-i + +all: aal + +OFILES= archiver.o \ + print.o \ + rd.o \ + rd_arhdr.o \ + rd_unsig2.o \ + sprint.o \ + wr_arhdr.o \ + wr_bytes.o \ + wr_int2.o \ + wr_long.o \ + wr_ranlib.o \ + format.o \ + rd_bytes.o \ + system.o \ + write.o \ + long2str.o + +aal: $(OFILES) + $(CC) $(LDFLAGS) -o aal $(OFILES) + install -S 64k $@ + +install: /usr/bin/aal /usr/bin/ar + +/usr/bin/aal: aal + install -cs -o bin aal $@ + +/usr/bin/ar: /usr/bin/aal + install -l $? $@ + +archiver.o: +print.o: +rd.o: +rd_arhdr.o: +rd_unsig2.o: +sprint.o: +wr_arhdr.o: +wr_bytes.o: +wr_int2.o: +wr_long.o: +wr_ranlib.o: +format.o: +rd_bytes.o: +system.o: +write.o: +long2str.o: + +clean: + rm -f *.o core *.bak aal diff --git a/commands/aal/arch.h b/commands/aal/arch.h new file mode 100755 index 000000000..96d489f65 --- /dev/null +++ b/commands/aal/arch.h @@ -0,0 +1,25 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ + +#ifndef __ARCH_H_INCLUDED +#define __ARCH_H_INCLUDED + +#define ARMAG 0177545 +#define AALMAG 0177454 + +struct ar_hdr { + char ar_name[14]; + long ar_date; + char ar_uid; + char ar_gid; + short ar_mode; + long ar_size; +}; + +#define AR_TOTAL 26 +#define AR_SIZE 22 + +#endif /* __ARCH_H_INCLUDED */ diff --git a/commands/aal/archiver.c b/commands/aal/archiver.c new file mode 100755 index 000000000..797f5ce11 --- /dev/null +++ b/commands/aal/archiver.c @@ -0,0 +1,800 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* ar - archiver Author: Michiel Huisjes */ +/* Made into arch/aal by Ceriel Jacobs + */ + +static char RcsId[] = "$Header$"; + +/* + * Usage: [arch|aal] [adprtvx] archive [file] ... + * v: verbose + * x: extract + * a: append + * r: replace (append when not in archive) + * d: delete + * t: print contents of archive + * p: print named files + * l: temporaries in current directory instead of /usr/tmp + * c: don't give "create" message + * u: replace only if dated later than member in archive +#ifdef DISTRIBUTION + * D: make distribution: use distr_time, uid=2, gid=2, mode=0644 +#endif + */ + +#include +#include +#ifndef S_IREAD +#define S_IREAD S_IRUSR +#endif +#ifndef S_IWRITE +#define S_IWRITE S_IWUSR +#endif +#ifndef S_IEXEC +#define S_IEXEC S_IXUSR +#endif +#include +#include +#ifdef AAL +#include +#include +#define MAGIC_NUMBER AALMAG +long offset; +struct ranlib *tab; +unsigned int tnum = 0; +char *tstrtab; +unsigned int tssiz = 0; +char *malloc(), *realloc(), *strcpy(), *strncpy(); +long time(); +unsigned int tabsz, strtabsz; +#else +#define MAGIC_NUMBER ARMAG +#endif +long lseek(); + +#define odd(nr) (nr & 01) +#define even(nr) (odd(nr) ? nr + 1 : nr) + +typedef char BOOL; +#define FALSE 0 +#define TRUE 1 + +#define READ 0 +#define APPEND 2 +#define CREATE 1 + +#define MEMBER struct ar_hdr + +#define NIL_PTR ((char *) 0) +#define NIL_MEM ((MEMBER *) 0) +#define NIL_LONG ((long *) 0) + +#define IO_SIZE (10 * 1024) + +#define equal(str1, str2) (!strncmp((str1), (str2), 14)) +#ifndef S_ISDIR +#define S_ISDIR(m) (m & S_IFDIR) /* is a directory */ +#endif + +BOOL verbose; +BOOL app_fl; +BOOL ex_fl; +BOOL show_fl; +BOOL pr_fl; +BOOL rep_fl; +BOOL del_fl; +BOOL nocr_fl; +BOOL local_fl; +BOOL update_fl; +#ifdef DISTRIBUTION +BOOL distr_fl; +long distr_time; +#endif + +int ar_fd; + +char io_buffer[IO_SIZE]; + +char *progname; + +char temp_buf[32]; +char *temp_arch = &temp_buf[0]; +extern char *mktemp(); +extern char *ctime(); + +usage() +{ + error(TRUE, "usage: %s %s archive [file] ...\n", + progname, +#ifdef AAL + "[acdrtxvlu]" +#else + "[acdprtxvlu]" +#endif + ); +} + +/*VARARGS2*/ +error(quit, str1, str2, str3, str4) +BOOL quit; +char *str1, *str2, *str3, *str4; +{ + char errbuf[256]; + + sprint(errbuf, str1, str2, str3, str4); + write(2, errbuf, strlen(errbuf)); + if (quit) { + unlink(temp_arch); + _exit(1); + } +} + +char *basename(path) +char *path; +{ + register char *ptr = path; + register char *last = NIL_PTR; + + while (*ptr != '\0') { + if (*ptr == '/') + last = ptr; + ptr++; + } + if (last == NIL_PTR) + return path; + if (*(last + 1) == '\0') { + *last = '\0'; + return basename(path); + } + return last + 1; +} + +extern unsigned int rd_unsigned2(); + +open_archive(name, mode) +register char *name; +register int mode; +{ + unsigned short magic = 0; + int fd; + + if (mode == CREATE) { + if ((fd = creat(name, 0666)) < 0) + error(TRUE, "cannot creat %s\n", name); + magic = MAGIC_NUMBER; + wr_int2(fd, magic); + return fd; + } + + if ((fd = open(name, mode)) < 0) { + if (mode == APPEND) { + close(open_archive(name, CREATE)); + if (!nocr_fl) error(FALSE, "%s: creating %s\n", progname, name); + return open_archive(name, APPEND); + } + error(TRUE, "cannot open %s\n", name); + } + lseek(fd, 0L, 0); + magic = rd_unsigned2(fd); + if (magic != AALMAG && magic != ARMAG) + error(TRUE, "%s is not in ar format\n", name); + + return fd; +} + +#if __STDC__ +void catch(int sig) +#else +catch() +#endif +{ + unlink(temp_arch); + _exit (2); +} + +main(argc, argv) +int argc; +char *argv[]; +{ + register char *ptr; + int needs_arg = 0; + + progname = argv[0]; + + if (argc < 3) + usage(); + + for (ptr = argv[1]; *ptr; ptr++) { + switch (*ptr) { + case 't' : + show_fl = TRUE; + break; + case 'v' : + verbose = TRUE; + break; + case 'x' : + ex_fl = TRUE; + break; + case 'a' : + needs_arg = 1; + app_fl = TRUE; + break; + case 'c' : + nocr_fl = TRUE; + break; +#ifndef AAL + case 'p' : + needs_arg = 1; + pr_fl = TRUE; + break; +#endif + case 'd' : + needs_arg = 1; + del_fl = TRUE; + break; + case 'r' : + needs_arg = 1; + rep_fl = TRUE; + break; + case 'l' : + local_fl = TRUE; + break; + case 'u' : + update_fl = TRUE; + break; +#ifdef DISTRIBUTION + case 'D' : + distr_fl = TRUE; + break; +#endif + default : + usage(); + } + } + + if (needs_arg && argc <= 3) + usage(); +#ifdef DISTRIBUTION + if (distr_fl) { + struct stat statbuf; + + stat(progname, &statbuf); + distr_time = statbuf.st_mtime; + } +#endif + if (local_fl) strcpy(temp_arch, "ar.XXXXXX"); + else strcpy(temp_arch, "/usr/tmp/ar.XXXXXX"); + + if (app_fl + ex_fl + del_fl + rep_fl + show_fl + pr_fl != 1) + usage(); + + if (update_fl && !rep_fl) + usage(); + + if (rep_fl || del_fl +#ifdef AAL + || app_fl +#endif + ) { + mktemp(temp_arch); + } +#ifdef AAL + tab = (struct ranlib *) malloc(512 * sizeof(struct ranlib)); + tstrtab = malloc(4096); + if (!tab || !tstrtab) error(TRUE,"Out of core\n"); + tabsz = 512; + strtabsz = 4096; +#endif + + signal(SIGINT, catch); + get(argc, argv); + + return 0; +} + +MEMBER * +get_member() +{ + static MEMBER member; + +again: + if (rd_arhdr(ar_fd, &member) == 0) + return NIL_MEM; + if (member.ar_size < 0) { + error(TRUE, "archive has member with negative size\n"); + } +#ifdef AAL + if (equal(SYMDEF, member.ar_name)) { + lseek(ar_fd, member.ar_size, 1); + goto again; + } +#endif + return &member; +} + +char *get_mode(); + +get(argc, argv) +int argc; +register char *argv[]; +{ + register MEMBER *member; + int i = 0; + int temp_fd, read_chars; + + ar_fd = open_archive(argv[2], (show_fl || pr_fl || ex_fl) ? READ : APPEND); + if (rep_fl || del_fl +#ifdef AAL + || app_fl +#endif + ) + temp_fd = open_archive(temp_arch, CREATE); + while ((member = get_member()) != NIL_MEM) { + if (argc > 3) { + for (i = 3; i < argc; i++) { + if (equal(basename(argv[i]), member->ar_name)) + break; + } + if (i == argc || app_fl) { + if (rep_fl || del_fl +#ifdef AAL + || app_fl +#endif + ) { +#ifdef AAL + if (i != argc) { + print("%s: already in archive\n", argv[i]); + argv[i] = ""; + } +#endif + wr_arhdr(temp_fd, member); + copy_member(member, ar_fd, temp_fd, 0); + } + else { +#ifndef AAL + if (app_fl && i != argc) { + print("%s: already in archive\n", argv[i]); + argv[i] = ""; + } +#endif + lseek(ar_fd, even(member->ar_size),1); + } + continue; + } + } + if (ex_fl || pr_fl) + extract(member); + else { + if (rep_fl) { + int isold = 0; + if(update_fl) { + struct stat status; + if (stat(argv[i], &status) >= 0) { + if(status.st_mtime <= member->ar_date) + isold = 1; + } + } + if(!isold) + add(argv[i], temp_fd, "r - %s\n"); + else { + wr_arhdr(temp_fd, member); + copy_member(member, ar_fd, temp_fd, 0); + if(verbose) + show("r - %s (old)\n", member->ar_name); + } + } + else if (show_fl) { + char buf[sizeof(member->ar_name) + 2]; + register char *p = buf, *q = member->ar_name; + + while (q <= &member->ar_name[sizeof(member->ar_name)-1] && *q) { + *p++ = *q++; + } + *p++ = '\n'; + *p = '\0'; + if (verbose) { + char *mode = get_mode(member->ar_mode); + char *date = ctime(&(member->ar_date)); + + *(date + 16) = '\0'; + *(date + 24) = '\0'; + + print("%s%3u/%u%7ld %s %s %s", + mode, + (unsigned) (member->ar_uid & 0377), + (unsigned) (member->ar_gid & 0377), + member->ar_size, + date+4, + date+20, + buf); + } + else print(buf); + } + else if (del_fl) + show("d - %s\n", member->ar_name); + lseek(ar_fd, even(member->ar_size), 1); + } + argv[i] = ""; + } + + if (argc > 3) { + for (i = 3; i < argc; i++) + if (argv[i][0] != '\0') { +#ifndef AAL + if (app_fl) + add(argv[i], ar_fd, "a - %s\n"); + else +#endif + if (rep_fl +#ifdef AAL + || app_fl +#endif + ) + add(argv[i], temp_fd, "a - %s\n"); + else { + print("%s: not found\n", argv[i]); + } + } + } + + if (rep_fl || del_fl +#ifdef AAL + || app_fl +#endif + ) { + signal(SIGINT, SIG_IGN); + close(ar_fd); + close(temp_fd); + ar_fd = open_archive(argv[2], CREATE); + temp_fd = open_archive(temp_arch, APPEND); +#ifdef AAL + write_symdef(); +#endif + while ((read_chars = read(temp_fd, io_buffer, IO_SIZE)) > 0) + mwrite(ar_fd, io_buffer, read_chars); + close(temp_fd); + unlink(temp_arch); + } + close(ar_fd); +} + +add(name, fd, mess) +char *name; +int fd; +char *mess; +{ + static MEMBER member; + register int read_chars; + struct stat status; + int src_fd; + + if (stat(name, &status) < 0) { + error(FALSE, "cannot find %s\n", name); + return; + } + else if (S_ISDIR(status.st_mode)) { + error(FALSE, "%s is a directory (ignored)\n", name); + return; + } + else if ((src_fd = open(name, 0)) < 0) { + error(FALSE, "cannot open %s\n", name); + return; + } + + strncpy (member.ar_name, basename (name), sizeof(member.ar_name)); + member.ar_uid = status.st_uid; + member.ar_gid = status.st_gid; + member.ar_mode = status.st_mode; + member.ar_date = status.st_mtime; + member.ar_size = status.st_size; +#ifdef DISTRIBUTION + if (distr_fl) { + member.ar_uid = 2; + member.ar_gid = 2; + member.ar_mode = 0644; + member.ar_date = distr_time; + } +#endif + wr_arhdr(fd, &member); +#ifdef AAL + do_object(src_fd, member.ar_size); + lseek(src_fd, 0L, 0); + offset += AR_TOTAL + even(member.ar_size); +#endif + while (status.st_size > 0) { + int x = IO_SIZE; + + read_chars = x; + if (status.st_size < x) { + x = status.st_size; + read_chars = x; + status.st_size = 0; + x = even(x); + } + else status.st_size -= x; + if (read(src_fd, io_buffer, read_chars) != read_chars) { + error(FALSE,"%s seems to shrink\n", name); + break; + } + mwrite(fd, io_buffer, x); + } + + if (verbose) + show(mess, member.ar_name); + close(src_fd); +} + +extract(member) +register MEMBER *member; +{ + int fd = 1; + char buf[sizeof(member->ar_name) + 1]; + + strncpy(buf, member->ar_name, sizeof(member->ar_name)); + buf[sizeof(member->ar_name)] = 0; + if (pr_fl == FALSE && (fd = creat(buf, 0666)) < 0) { + error(FALSE, "cannot create %s\n", buf); + fd = -1; + } + + if (verbose) { + if (pr_fl == FALSE) show("x - %s\n", buf); + else show("\n<%s>\n\n", buf); + } + + copy_member(member, ar_fd, fd, 1); + + if (fd >= 0 && fd != 1) + close(fd); + if (pr_fl == FALSE) chmod(buf, member->ar_mode); +} + +copy_member(member, from, to, extracting) +register MEMBER *member; +int from, to; +{ + register int rest; + long mem_size = member->ar_size; + BOOL is_odd = odd(mem_size) ? TRUE : FALSE; + +#ifdef AAL + if (! extracting) { + long pos = lseek(from, 0L, 1); + + do_object(from, mem_size); + offset += AR_TOTAL + even(mem_size); + lseek(from, pos, 0); + } +#endif + do { + rest = mem_size > (long) IO_SIZE ? IO_SIZE : (int) mem_size; + if (read(from, io_buffer, rest) != rest) { + char buf[sizeof(member->ar_name) + 1]; + + strncpy(buf, member->ar_name, sizeof(member->ar_name)); + buf[sizeof(member->ar_name)] = 0; + error(TRUE, "read error on %s\n", buf); + } + if (to >= 0) mwrite(to, io_buffer, rest); + mem_size -= (long) rest; + } while (mem_size > 0L); + + if (is_odd) { + lseek(from, 1L, 1); + if (to >= 0 && ! extracting) + lseek(to, 1L, 1); + } +} + +char * +get_mode(mode) +register int mode; +{ + static char mode_buf[11]; + register int tmp = mode; + int i; + + mode_buf[9] = ' '; + for (i = 0; i < 3; i++) { + mode_buf[i * 3] = (tmp & S_IREAD) ? 'r' : '-'; + mode_buf[i * 3 + 1] = (tmp & S_IWRITE) ? 'w' : '-'; + mode_buf[i * 3 + 2] = (tmp & S_IEXEC) ? 'x' : '-'; + tmp <<= 3; + } + if (mode & S_ISUID) + mode_buf[2] = 's'; + if (mode & S_ISGID) + mode_buf[5] = 's'; + return mode_buf; +} + +wr_fatal() +{ + error(TRUE, "write error\n"); +} + +rd_fatal() +{ + error(TRUE, "read error\n"); +} + +mwrite(fd, address, bytes) +int fd; +register char *address; +register int bytes; +{ + if (write(fd, address, bytes) != bytes) + error(TRUE, "write error\n"); +} + +show(s, name) +char *s, *name; +{ + MEMBER x; + char buf[sizeof(x.ar_name)+1]; + register char *p = buf, *q = name; + + while (q <= &name[sizeof(x.ar_name)-1] && *q) *p++ = *q++; + *p++ = '\0'; + print(s, buf); +} + +#ifdef AAL +/* + * Write out the ranlib table: first 4 bytes telling how many ranlib structs + * there are, followed by the ranlib structs, + * then 4 bytes giving the size of the string table, followed by the string + * table itself. + */ +write_symdef() +{ + register struct ranlib *ran; + register int i; + register long delta; + MEMBER arbuf; + + if (odd(tssiz)) + tstrtab[tssiz++] = '\0'; + for (i = 0; i < sizeof(arbuf.ar_name); i++) + arbuf.ar_name[i] = '\0'; + strcpy(arbuf.ar_name, SYMDEF); + arbuf.ar_size = 4 + 2 * 4 * (long)tnum + 4 + (long)tssiz; + time(&arbuf.ar_date); + arbuf.ar_uid = getuid(); + arbuf.ar_gid = getgid(); + arbuf.ar_mode = 0444; +#ifdef DISTRIBUTION + if (distr_fl) { + arbuf.ar_uid = 2; + arbuf.ar_gid = 2; + arbuf.ar_date = distr_time; + } +#endif + wr_arhdr(ar_fd,&arbuf); + wr_long(ar_fd, (long) tnum); + /* + * Account for the space occupied by the magic number + * and the ranlib table. + */ + delta = 2 + AR_TOTAL + arbuf.ar_size; + for (ran = tab; ran < &tab[tnum]; ran++) { + ran->ran_pos += delta; + } + + wr_ranlib(ar_fd, tab, (long) tnum); + wr_long(ar_fd, (long) tssiz); + wr_bytes(ar_fd, tstrtab, (long) tssiz); +} + +/* + * Return whether the bytes in `buf' form a good object header. + * The header is put in `headp'. + */ +int +is_outhead(headp) + register struct outhead *headp; +{ + + return !BADMAGIC(*headp) && headp->oh_nname != 0; +} + +do_object(f, size) + long size; +{ + struct outhead headbuf; + + if (size < SZ_HEAD) { + /* It can't be an object file. */ + return; + } + /* + * Read a header to see if it is an object file. + */ + if (! rd_fdopen(f)) { + rd_fatal(); + } + rd_ohead(&headbuf); + if (!is_outhead(&headbuf)) { + return; + } + do_names(&headbuf); +} + +/* + * First skip the names and read in the string table, then seek back to the + * name table and read and write the names one by one. Update the ranlib table + * accordingly. + */ +do_names(headp) + struct outhead *headp; +{ + register char *strings; + register int nnames = headp->oh_nname; +#define NNAMES 100 + struct outname namebuf[NNAMES]; + long xxx = OFF_CHAR(*headp); + + if ( headp->oh_nchar != (unsigned int)headp->oh_nchar || + (strings = malloc((unsigned int)headp->oh_nchar)) == (char *)0 + ) { + error(TRUE, "string table too big\n"); + } + rd_string(strings, headp->oh_nchar); + while (nnames) { + int i = nnames >= NNAMES ? NNAMES : nnames; + register struct outname *p = namebuf; + + nnames -= i; + rd_name(namebuf, i); + while (i--) { + long off = p->on_foff - xxx; + if (p->on_foff == (long)0) { + p++; + continue; /* An unrecognizable name. */ + } + p->on_mptr = strings + off; + /* + * Only enter names that are exported and are really + * defined. Also enter common names. Note, that + * this might cause problems when the name is really + * defined in a later file, with a value != 0. + * However, this problem also exists on the Unix + * ranlib archives. + */ + if ( (p->on_type & S_EXT) && + (p->on_type & S_TYP) != S_UND + ) + enter_name(p); + p++; + } + } + free(strings); +} + +enter_name(namep) + struct outname *namep; +{ + register char *cp; + + if (tnum >= tabsz) { + tab = (struct ranlib *) + realloc((char *) tab, (tabsz += 512) * sizeof(struct ranlib)); + if (! tab) error(TRUE, "Out of core\n"); + } + tab[tnum].ran_off = tssiz; + tab[tnum].ran_pos = offset; + + for (cp = namep->on_mptr;; cp++) { + if (tssiz >= strtabsz) { + tstrtab = realloc(tstrtab, (strtabsz += 4096)); + if (! tstrtab) error(TRUE, "string table overflow\n"); + } + tstrtab[tssiz++] = *cp; + if (!*cp) break; + } + tnum++; +} +#endif AAL diff --git a/commands/aal/byte_order.h b/commands/aal/byte_order.h new file mode 100755 index 000000000..3081cf796 --- /dev/null +++ b/commands/aal/byte_order.h @@ -0,0 +1,9 @@ +#if defined(mc68020) || defined(mc68000) || defined(sparc) +#define BYTES_REVERSED 1 +#define WORDS_REVERSED 1 +#define CHAR_UNSIGNED 0 +#else +#define BYTES_REVERSED 0 +#define WORDS_REVERSED 0 +#define CHAR_UNSIGNED 0 +#endif diff --git a/commands/aal/format.c b/commands/aal/format.c new file mode 100755 index 000000000..ffb3a65b7 --- /dev/null +++ b/commands/aal/format.c @@ -0,0 +1,112 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#if __STDC__ +#include +#else +#include +#endif + +extern char *long2str(); + +static int +integral(c) +{ + switch (c) { + case 'b': + return -2; + case 'd': + return 10; + case 'o': + return -8; + case 'u': + return -10; + case 'x': + return -16; + } + return 0; +} + +/*VARARGS2*/ +/*FORMAT1 $ + %s = char * + %l = long + %c = int + %[uxbo] = unsigned int + %d = int +$ */ +int +_format(buf, fmt, argp) + char *buf, *fmt; + register va_list argp; +{ + register char *pf = fmt; + register char *pb = buf; + + while (*pf) { + if (*pf == '%') { + register width, base, pad, npad; + char *arg; + char cbuf[2]; + char *badformat = ""; + + /* get padder */ + if (*++pf == '0') { + pad = '0'; + ++pf; + } + else + pad = ' '; + + /* get width */ + width = 0; + while (*pf >= '0' && *pf <= '9') + width = 10 * width + *pf++ - '0'; + + if (*pf == 's') { + arg = va_arg(argp, char *); + } + else + if (*pf == 'c') { + cbuf[0] = va_arg(argp, int); + cbuf[1] = '\0'; + arg = &cbuf[0]; + } + else + if (*pf == 'l') { + /* alignment ??? */ + if (base = integral(*++pf)) { + arg = long2str(va_arg(argp,long), base); + } + else { + pf--; + arg = badformat; + } + } + else + if (base = integral(*pf)) { + arg = long2str((long)va_arg(argp,int), base); + } + else + if (*pf == '%') + arg = "%"; + else + arg = badformat; + + npad = width - strlen(arg); + + while (npad-- > 0) + *pb++ = pad; + + while (*pb++ = *arg++); + pb--; + pf++; + } + else + *pb++ = *pf++; + } + return pb - buf; +} diff --git a/commands/aal/local.h b/commands/aal/local.h new file mode 100755 index 000000000..9f9811457 --- /dev/null +++ b/commands/aal/local.h @@ -0,0 +1,19 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* collection of options, selected by including or excluding 'defines' */ + +/* Version number of the EM object code */ +# define VERSION 3 /* 16 bits number */ + +/* The default machine used by ack, acc, apc */ +# define ACKM "minix" + +/* size of local machine, either 0 (for 16 bit address space), or 1 */ +# undef BIGMACHINE 1 + +/* operating system, SYS_5, V7, BSD4_1 or BSD4_2; Do NOT delete the comment + in the next line! */ +# define V7 1 /* SYSTEM */ diff --git a/commands/aal/long2str.c b/commands/aal/long2str.c new file mode 100755 index 000000000..7052ecaec --- /dev/null +++ b/commands/aal/long2str.c @@ -0,0 +1,67 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* Integer to String translator + -> base is a value from [-16,-2] V [2,16] + -> base < 0: see 'val' as unsigned value + -> no checks for buffer overflow and illegal parameters + (1985, EHB) +*/ + +#define MAXWIDTH 32 + +char * +long2str(val, base) + register long val; + register base; +{ + static char numbuf[MAXWIDTH]; + static char vec[] = "0123456789ABCDEF"; + register char *p = &numbuf[MAXWIDTH]; + int sign = (base > 0); + + *--p = '\0'; /* null-terminate string */ + if (val) { + if (base > 0) { + if (val < 0L) { + long v1 = -val; + if (v1 == val) + goto overflow; + val = v1; + } + else + sign = 0; + } + else + if (base < 0) { /* unsigned */ + base = -base; + if (val < 0L) { /* taken from Amoeba src */ + register mod, i; + overflow: + mod = 0; + for (i = 0; i < 8 * sizeof val; i++) { + mod <<= 1; + if (val < 0) + mod++; + val <<= 1; + if (mod >= base) { + mod -= base; + val++; + } + } + *--p = vec[mod]; + } + } + do { + *--p = vec[(int) (val % base)]; + val /= base; + } while (val != 0L); + if (sign) + *--p = '-'; /* don't forget it !! */ + } + else + *--p = '0'; /* just a simple 0 */ + return p; +} diff --git a/commands/aal/object.h b/commands/aal/object.h new file mode 100755 index 000000000..92bc4b1cc --- /dev/null +++ b/commands/aal/object.h @@ -0,0 +1,76 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#include "byte_order.h" +#include +#include + +#if CHAR_UNSIGNED +#define Xchar(ch) (ch) +#else +#define Xchar(ch) ((ch) & 0377) +#endif + +#if ! defined(BYTES_REVERSED) +#define BYTES_REVERSED 1 +#endif + +#if ! defined(WORDS_REVERSED) +#define WORDS_REVERSED 1 +#endif + +#if BYTES_REVERSED +#define uget2(c) (Xchar((c)[0]) | ((unsigned) Xchar((c)[1]) << 8)) +#define Xput2(i, c) (((c)[0] = (i)), ((c)[1] = (i) >> 8)) +#define put2(i, c) { register int j = (i); Xput2(j, c); } +#else +#define uget2(c) (* ((unsigned short *) (c))) +#define Xput2(i, c) (* ((short *) (c)) = (i)) +#define put2(i, c) Xput2(i, c) +#endif + +#define get2(c) ((short) uget2(c)) + +#if WORDS_REVERSED || BYTES_REVERSED +#define get4(c) (uget2(c) | ((long) uget2((c)+2) << 16)) +#define put4(l, c) { register long x=(l); \ + Xput2((int)x,c); \ + Xput2((int)(x>>16),(c)+2); \ + } +#else +#define get4(c) (* ((long *) (c))) +#define put4(l, c) (* ((long *) (c)) = (l)) +#endif + +#define SECTCNT 3 /* number of sections with own output buffer */ +#if BIGMACHINE +#define WBUFSIZ (8*BUFSIZ) +#else +#define WBUFSIZ BUFSIZ +#endif + +struct fil { + int cnt; + char *pnow; + char *pbegin; + long currpos; + int fd; + char pbuf[WBUFSIZ]; +}; + +extern struct fil __parts[]; + +#define PARTEMIT 0 +#define PARTRELO (PARTEMIT+SECTCNT) +#define PARTNAME (PARTRELO+1) +#define PARTCHAR (PARTNAME+1) +#ifdef SYMDBUG +#define PARTDBUG (PARTCHAR+1) +#else +#define PARTDBUG (PARTCHAR+0) +#endif +#define NPARTS (PARTDBUG + 1) + +#define getsect(s) (PARTEMIT+((s)>=(SECTCNT-1)?(SECTCNT-1):(s))) diff --git a/commands/aal/out.h b/commands/aal/out.h new file mode 100755 index 000000000..0834f665b --- /dev/null +++ b/commands/aal/out.h @@ -0,0 +1,126 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#ifndef __OUT_H_INCLUDED +#define __OUT_H_INCLUDED +/* + * output format for ACK assemblers + */ +#ifndef ushort +#define ushort unsigned short +#endif /* ushort */ + +struct outhead { + ushort oh_magic; /* magic number */ + ushort oh_stamp; /* version stamp */ + ushort oh_flags; /* several format flags */ + ushort oh_nsect; /* number of outsect structures */ + ushort oh_nrelo; /* number of outrelo structures */ + ushort oh_nname; /* number of outname structures */ + long oh_nemit; /* sum of all os_flen */ + long oh_nchar; /* size of string area */ +}; + +#define O_MAGIC 0x0201 /* magic number of output file */ +#define O_STAMP 0 /* version stamp */ +#define MAXSECT 64 /* Maximum number of sections */ + +#define HF_LINK 0x0004 /* unresolved references left */ +#define HF_8086 0x0008 /* os_base specially encoded */ + +struct outsect { + long os_base; /* startaddress in machine */ + long os_size; /* section size in machine */ + long os_foff; /* startaddress in file */ + long os_flen; /* section size in file */ + long os_lign; /* section alignment */ +}; + +struct outrelo { + char or_type; /* type of reference */ + char or_sect; /* referencing section */ + ushort or_nami; /* referenced symbol index */ + long or_addr; /* referencing address */ +}; + +struct outname { + union { + char *on_ptr; /* symbol name (in core) */ + long on_off; /* symbol name (in file) */ + } on_u; +#define on_mptr on_u.on_ptr +#define on_foff on_u.on_off + ushort on_type; /* symbol type */ + ushort on_desc; /* debug info */ + long on_valu; /* symbol value */ +}; + +/* + * relocation type bits + */ +#define RELSZ 0x07 /* relocation length */ +#define RELO1 1 /* 1 byte */ +#define RELO2 2 /* 2 bytes */ +#define RELO4 4 /* 4 bytes */ +#define RELPC 0x08 /* pc relative */ +#define RELBR 0x10 /* High order byte lowest address. */ +#define RELWR 0x20 /* High order word lowest address. */ + +/* + * section type bits and fields + */ +#define S_TYP 0x007F /* undefined, absolute or relative */ +#define S_EXT 0x0080 /* external flag */ +#define S_ETC 0x7F00 /* for symbolic debug, bypassing 'as' */ + +/* + * S_TYP field values + */ +#define S_UND 0x0000 /* undefined item */ +#define S_ABS 0x0001 /* absolute item */ +#define S_MIN 0x0002 /* first user section */ +#define S_MAX (S_TYP-1) /* last user section */ +#define S_CRS S_TYP /* on_valu is symbol index which contains value */ + +/* + * S_ETC field values + */ +#define S_SCT 0x0100 /* section names */ +#define S_LIN 0x0200 /* hll source line item */ +#define S_FIL 0x0300 /* hll source file item */ +#define S_MOD 0x0400 /* ass source file item */ +#define S_COM 0x1000 /* Common name. */ +#define S_STB 0xe000 /* entries with any of these bits set are + reserved for debuggers + */ + +/* + * structure format strings + */ +#define SF_HEAD "22222244" +#define SF_SECT "44444" +#define SF_RELO "1124" +#define SF_NAME "4224" + +/* + * structure sizes (bytes in file; add digits in SF_*) + */ +#define SZ_HEAD 20 +#define SZ_SECT 20 +#define SZ_RELO 8 +#define SZ_NAME 12 + +/* + * file access macros + */ +#define BADMAGIC(x) ((x).oh_magic!=O_MAGIC) +#define OFF_SECT(x) SZ_HEAD +#define OFF_EMIT(x) (OFF_SECT(x) + ((long)(x).oh_nsect * SZ_SECT)) +#define OFF_RELO(x) (OFF_EMIT(x) + (x).oh_nemit) +#define OFF_NAME(x) (OFF_RELO(x) + ((long)(x).oh_nrelo * SZ_RELO)) +#define OFF_CHAR(x) (OFF_NAME(x) + ((long)(x).oh_nname * SZ_NAME)) + +#endif /* __OUT_H_INCLUDED */ diff --git a/commands/aal/param.h b/commands/aal/param.h new file mode 100755 index 000000000..884ef3e9d --- /dev/null +++ b/commands/aal/param.h @@ -0,0 +1,7 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#define SSIZE 1024 diff --git a/commands/aal/print.c b/commands/aal/print.c new file mode 100755 index 000000000..bb7353d7c --- /dev/null +++ b/commands/aal/print.c @@ -0,0 +1,42 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#if __STDC__ +#include +#else +#include +#endif +#include +#include "param.h" + +/*VARARGS*/ +/*FORMAT0v $ + %s = char * + %l = long + %c = int + %[uxbo] = unsigned int + %d = int +$ */ +int +#if __STDC__ +print(char *fmt, ...) +#else +print(fmt, va_alist) + char *fmt; + va_dcl +#endif +{ + va_list args; + char buf[SSIZE]; + +#if __STDC__ + va_start(args, fmt); +#else + va_start(args); +#endif + sys_write(STDOUT, buf, _format(buf, fmt, args)); + va_end(args); +} diff --git a/commands/aal/ranlib.h b/commands/aal/ranlib.h new file mode 100755 index 000000000..84f4d6c96 --- /dev/null +++ b/commands/aal/ranlib.h @@ -0,0 +1,34 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#ifndef __RANLIB_H_INCLUDED +#define __RANLIB_H_INCLUDED + +#ifndef SYMDEF +# define SYMDEF "__.SYMDEF" +#endif /* SYMDEF */ + +/* + * Structure of the SYMDEF table of contents for an archive. + * SYMDEF begins with a long giving the number of ranlib + * structures that immediately follow, and then continues with a string + * table consisting of a long giving the number of bytes of + * strings that follow and then the strings themselves. + */ +struct ranlib { + union { + char *ran__ptr; /* symbol name (in core) */ + long ran__off; /* symbol name (in file) */ + } ran_u; +#define ran_ptr ran_u.ran__ptr +#define ran_off ran_u.ran__off + long ran_pos; /* library member is at this position */ +}; + +#define SZ_RAN 8 +#define SF_RAN "44" + +#endif /* __RANLIB_H_INCLUDED */ diff --git a/commands/aal/rd.c b/commands/aal/rd.c new file mode 100755 index 000000000..2618750de --- /dev/null +++ b/commands/aal/rd.c @@ -0,0 +1,254 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#include +#include "object.h" + +extern long lseek(); + +/* + * Parts of the output file. + */ +#undef PARTEMIT +#undef PARTRELO +#undef PARTNAME +#undef PARTCHAR +#undef PARTDBUG +#undef NPARTS + +#define PARTEMIT 0 +#define PARTRELO 1 +#define PARTNAME 2 +#define PARTCHAR 3 +#ifdef SYMDBUG +#define PARTDBUG 4 +#else +#define PARTDBUG 3 +#endif +#define NPARTS (PARTDBUG + 1) + +static long offset[MAXSECT]; + +static int outfile; +static long outseek[NPARTS]; +static long currpos; +static long rd_base; +#define OUTSECT(i) \ + (outseek[PARTEMIT] = offset[i]) +#define BEGINSEEK(p, o) \ + (outseek[(p)] = (o)) + +static int sectionnr; + +static +OUTREAD(p, b, n) + char *b; + long n; +{ + register long l = outseek[p]; + + if (currpos != l) { + lseek(outfile, l, 0); + } + rd_bytes(outfile, b, n); + l += n; + currpos = l; + outseek[p] = l; +} + +/* + * Open the output file according to the chosen strategy. + */ +int +rd_open(f) + char *f; +{ + + if ((outfile = open(f, 0)) < 0) + return 0; + return rd_fdopen(outfile); +} + +static int offcnt; + +rd_fdopen(fd) +{ + register int i; + + for (i = 0; i < NPARTS; i++) outseek[i] = 0; + offcnt = 0; + rd_base = lseek(fd, 0L, 1); + if (rd_base < 0) { + return 0; + } + currpos = rd_base; + outseek[PARTEMIT] = currpos; + outfile = fd; + sectionnr = 0; + return 1; +} + +rd_close() +{ + + close(outfile); + outfile = -1; +} + +rd_fd() +{ + return outfile; +} + +rd_ohead(head) + register struct outhead *head; +{ + register long off; + + OUTREAD(PARTEMIT, (char *) head, (long) SZ_HEAD); +#if ! (BYTES_REVERSED || WORDS_REVERSED) + if (sizeof(struct outhead) != SZ_HEAD) +#endif + { + register char *c = (char *) head + (SZ_HEAD-4); + + head->oh_nchar = get4(c); + c -= 4; head->oh_nemit = get4(c); + c -= 2; head->oh_nname = uget2(c); + c -= 2; head->oh_nrelo = uget2(c); + c -= 2; head->oh_nsect = uget2(c); + c -= 2; head->oh_flags = uget2(c); + c -= 2; head->oh_stamp = uget2(c); + c -= 2; head->oh_magic = uget2(c); + } + off = OFF_RELO(*head) + rd_base; + BEGINSEEK(PARTRELO, off); + off += (long) head->oh_nrelo * SZ_RELO; + BEGINSEEK(PARTNAME, off); + off += (long) head->oh_nname * SZ_NAME; + BEGINSEEK(PARTCHAR, off); +#ifdef SYMDBUG + off += head->oh_nchar; + BEGINSEEK(PARTDBUG, off); +#endif +} + +rd_rew_relos(head) + register struct outhead *head; +{ + register long off = OFF_RELO(*head) + rd_base; + + BEGINSEEK(PARTRELO, off); +} + +rd_sect(sect, cnt) + register struct outsect *sect; + register unsigned int cnt; +{ + register char *c = (char *) sect + cnt * SZ_SECT; + + OUTREAD(PARTEMIT, (char *) sect, (long)cnt * SZ_SECT); + sect += cnt; + offcnt += cnt; + while (cnt--) { + sect--; +#if ! (BYTES_REVERSED || WORDS_REVERSED) + if (sizeof(struct outsect) != SZ_SECT) +#endif + { + c -= 4; sect->os_lign = get4(c); + c -= 4; sect->os_flen = get4(c); + c -= 4; sect->os_foff = get4(c); + } + offset[--offcnt] = sect->os_foff + rd_base; +#if ! (BYTES_REVERSED || WORDS_REVERSED) + if (sizeof(struct outsect) != SZ_SECT) +#endif + { + c -= 4; sect->os_size = get4(c); + c -= 4; sect->os_base = get4(c); + } + } +} + +rd_outsect(s) +{ + OUTSECT(s); + sectionnr = s; +} + +/* + * We don't have to worry about byte order here. + */ +rd_emit(emit, cnt) + char *emit; + long cnt; +{ + OUTREAD(PARTEMIT, emit, cnt); + offset[sectionnr] += cnt; +} + +rd_relo(relo, cnt) + register struct outrelo *relo; + register unsigned int cnt; +{ + + OUTREAD(PARTRELO, (char *) relo, (long) cnt * SZ_RELO); +#if ! (BYTES_REVERSED || WORDS_REVERSED) + if (sizeof(struct outrelo) != SZ_RELO) +#endif + { + register char *c = (char *) relo + (long) cnt * SZ_RELO; + + relo += cnt; + while (cnt--) { + relo--; + c -= 4; relo->or_addr = get4(c); + c -= 2; relo->or_nami = uget2(c); + relo->or_sect = *--c; + relo->or_type = *--c; + } + } +} + +rd_name(name, cnt) + register struct outname *name; + register unsigned int cnt; +{ + + OUTREAD(PARTNAME, (char *) name, (long) cnt * SZ_NAME); +#if ! (BYTES_REVERSED || WORDS_REVERSED) + if (sizeof(struct outname) != SZ_NAME) +#endif + { + register char *c = (char *) name + (long) cnt * SZ_NAME; + + name += cnt; + while (cnt--) { + name--; + c -= 4; name->on_valu = get4(c); + c -= 2; name->on_desc = uget2(c); + c -= 2; name->on_type = uget2(c); + c -= 4; name->on_foff = get4(c); + } + } +} + +rd_string(addr, len) + char *addr; + long len; +{ + + OUTREAD(PARTCHAR, addr, len); +} + +#ifdef SYMDBUG +rd_dbug(buf, size) + char *buf; + long size; +{ + OUTREAD(PARTDBUG, buf, size); +} +#endif diff --git a/commands/aal/rd_arhdr.c b/commands/aal/rd_arhdr.c new file mode 100755 index 000000000..8ea9f773e --- /dev/null +++ b/commands/aal/rd_arhdr.c @@ -0,0 +1,35 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#include +#include "object.h" + +int +rd_arhdr(fd, arhdr) + register struct ar_hdr *arhdr; +{ + char buf[AR_TOTAL]; + register char *c = buf; + register char *p = arhdr->ar_name; + register int i; + + i = read(fd, c, AR_TOTAL); + if (i == 0) return 0; + if (i != AR_TOTAL) { + rd_fatal(); + } + i = 14; + while (i--) { + *p++ = *c++; + } + arhdr->ar_date = ((long) get2(c)) << 16; c += 2; + arhdr->ar_date |= ((long) get2(c)) & 0xffff; c += 2; + arhdr->ar_uid = *c++; + arhdr->ar_gid = *c++; + arhdr->ar_mode = get2(c); c += 2; + arhdr->ar_size = (long) get2(c) << 16; c += 2; + arhdr->ar_size |= (long) get2(c) & 0xffff; + return 1; +} diff --git a/commands/aal/rd_bytes.c b/commands/aal/rd_bytes.c new file mode 100755 index 000000000..9964e0ac1 --- /dev/null +++ b/commands/aal/rd_bytes.c @@ -0,0 +1,32 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#define MININT (1 << (sizeof(int) * 8 - 1)) +#define MAXCHUNK (~MININT) /* Highest count we read(2). */ +/* Unfortunately, MAXCHUNK is too large with some compilers. Put it in + an int! +*/ + +static int maxchunk = MAXCHUNK; + +/* + * We don't have to worry about byte order here. + * Just read "cnt" bytes from file-descriptor "fd". + */ +int +rd_bytes(fd, string, cnt) + register char *string; + register long cnt; +{ + + while (cnt) { + register int n = cnt >= maxchunk ? maxchunk : cnt; + + if (read(fd, string, n) != n) + rd_fatal(); + string += n; + cnt -= n; + } +} diff --git a/commands/aal/rd_unsig2.c b/commands/aal/rd_unsig2.c new file mode 100755 index 000000000..13a816f06 --- /dev/null +++ b/commands/aal/rd_unsig2.c @@ -0,0 +1,15 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#include "object.h" + +unsigned int +rd_unsigned2(fd) +{ + char buf[2]; + + rd_bytes(fd, buf, 2L); + return uget2(buf); +} diff --git a/commands/aal/sprint.c b/commands/aal/sprint.c new file mode 100755 index 000000000..bdef67a45 --- /dev/null +++ b/commands/aal/sprint.c @@ -0,0 +1,42 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#if __STDC__ +#include +#else +#include +#endif +#include +#include "param.h" + +/*VARARGS*/ +/*FORMAT1v $ + %s = char * + %l = long + %c = int + %[uxbo] = unsigned int + %d = int +$ */ +char * +#if __STDC__ +sprint(char *buf, char *fmt, ...) +#else +sprint(buf, fmt, va_alist) + char *buf, *fmt; + va_dcl +#endif +{ + va_list args; + +#if __STDC__ + va_start(args, fmt); +#else + va_start(args); +#endif + buf[_format(buf, fmt, args)] = '\0'; + va_end(args); + return buf; +} diff --git a/commands/aal/system.c b/commands/aal/system.c new file mode 100755 index 000000000..4529fea97 --- /dev/null +++ b/commands/aal/system.c @@ -0,0 +1,24 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* RCS: $Header$ */ + +#include + +File _sys_ftab[SYS_NOPEN] = { + { 0, OP_READ}, + { 1, OP_APPEND}, + { 2, OP_APPEND} +}; + +File * +_get_entry() +{ + register File *fp; + + for (fp = &_sys_ftab[0]; fp < &_sys_ftab[SYS_NOPEN]; fp++) + if (fp->o_flags == 0) + return fp; + return (File *)0; +} diff --git a/commands/aal/system.h b/commands/aal/system.h new file mode 100755 index 000000000..72e523e77 --- /dev/null +++ b/commands/aal/system.h @@ -0,0 +1,47 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* RCS: $Header$ */ +#ifndef __SYSTEM_INCLUDED__ +#define __SYSTEM_INCLUDED__ + +struct _sys_fildes { + int o_fd; /* UNIX filedescriptor */ + int o_flags; /* flags for open; 0 if not used */ +}; + +typedef struct _sys_fildes File; + +extern File _sys_ftab[]; + +/* flags for sys_open() */ +#define OP_READ 01 +#define OP_WRITE 02 +#define OP_APPEND 04 + +/* flags for sys_access() */ +#define AC_EXIST 00 +#define AC_READ 04 +#define AC_WRITE 02 +#define AC_EXEC 01 + +/* flags for sys_stop() */ +#define S_END 0 +#define S_EXIT 1 +#define S_ABORT 2 + +/* standard file decsriptors */ +#define STDIN &_sys_ftab[0] +#define STDOUT &_sys_ftab[1] +#define STDERR &_sys_ftab[2] + +/* maximum number of open files */ +#define SYS_NOPEN 20 + +/* return value for sys_break */ +#define ILL_BREAK ((char *)0) + +/* system's idea of block */ +#define BUFSIZ 1024 +#endif __SYSTEM_INCLUDED__ diff --git a/commands/aal/varargs.h b/commands/aal/varargs.h new file mode 100755 index 000000000..140836d6f --- /dev/null +++ b/commands/aal/varargs.h @@ -0,0 +1,16 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#ifndef _VARARGS_H +#define _VARARGS_H + +typedef char *va_list; +# define __va_sz(mode) (((sizeof(mode) + sizeof(int) - 1) / sizeof(int)) * sizeof(int)) +# define va_dcl int va_alist; +# define va_start(list) (list = (char *) &va_alist) +# define va_end(list) +# define va_arg(list,mode) (*((mode *)((list += __va_sz(mode)) - __va_sz(mode)))) +#endif /* _VARARGS_H */ diff --git a/commands/aal/wr_arhdr.c b/commands/aal/wr_arhdr.c new file mode 100755 index 000000000..a074f2dfe --- /dev/null +++ b/commands/aal/wr_arhdr.c @@ -0,0 +1,28 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#include +#include "object.h" + +wr_arhdr(fd, arhdr) + register struct ar_hdr *arhdr; +{ + char buf[AR_TOTAL]; + register char *c = buf; + register char *p = arhdr->ar_name; + register int i = 14; + + while (i--) { + *c++ = *p++; + } + put2((int)(arhdr->ar_date>>16),c); c += 2; + put2((int)(arhdr->ar_date),c); c += 2; + *c++ = arhdr->ar_uid; + *c++ = arhdr->ar_gid; + put2(arhdr->ar_mode,c); c += 2; + put2((int)(arhdr->ar_size>>16),c); c += 2; + put2((int)(arhdr->ar_size),c); + wr_bytes(fd, buf, (long) AR_TOTAL); +} diff --git a/commands/aal/wr_bytes.c b/commands/aal/wr_bytes.c new file mode 100755 index 000000000..36629da8a --- /dev/null +++ b/commands/aal/wr_bytes.c @@ -0,0 +1,30 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#define MININT (1 << (sizeof(int) * 8 - 1)) +#define MAXCHUNK (~MININT) /* Highest count we write(2). */ +/* Notice that MAXCHUNK itself might be too large with some compilers. + You have to put it in an int! +*/ + +static int maxchunk = MAXCHUNK; + +/* + * Just write "cnt" bytes to file-descriptor "fd". + */ +wr_bytes(fd, string, cnt) + register char *string; + register long cnt; +{ + + while (cnt) { + register int n = cnt >= maxchunk ? maxchunk : cnt; + + if (write(fd, string, n) != n) + wr_fatal(); + string += n; + cnt -= n; + } +} diff --git a/commands/aal/wr_int2.c b/commands/aal/wr_int2.c new file mode 100755 index 000000000..39966b183 --- /dev/null +++ b/commands/aal/wr_int2.c @@ -0,0 +1,14 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#include "object.h" + +wr_int2(fd, i) +{ + char buf[2]; + + put2(i, buf); + wr_bytes(fd, buf, 2L); +} diff --git a/commands/aal/wr_long.c b/commands/aal/wr_long.c new file mode 100755 index 000000000..c5df7d8f6 --- /dev/null +++ b/commands/aal/wr_long.c @@ -0,0 +1,15 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#include "object.h" + +wr_long(fd, l) + long l; +{ + char buf[4]; + + put4(l, buf); + wr_bytes(fd, buf, 4L); +} diff --git a/commands/aal/wr_ranlib.c b/commands/aal/wr_ranlib.c new file mode 100755 index 000000000..a0b4be366 --- /dev/null +++ b/commands/aal/wr_ranlib.c @@ -0,0 +1,36 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#include +#include "object.h" + +wr_ranlib(fd, ran, cnt) + register struct ranlib *ran; + register long cnt; +{ +#if ! (BYTES_REVERSED || WORDS_REVERSED) + if (sizeof (struct ranlib) != SZ_RAN) +#endif + { + char buf[100 * SZ_RAN]; + + while (cnt) { + register int i = (cnt > 100) ? 100 : cnt; + register char *c = buf; + long j = i * SZ_RAN; + + cnt -= i; + while (i--) { + put4(ran->ran_off,c); c += 4; + put4(ran->ran_pos,c); c += 4; + ran++; + } + wr_bytes(fd, buf, j); + } + } +#if ! (BYTES_REVERSED || WORDS_REVERSED) + else wr_bytes(fd, (char *) ran, cnt * SZ_RAN); +#endif +} diff --git a/commands/aal/write.c b/commands/aal/write.c new file mode 100755 index 000000000..dd98446e0 --- /dev/null +++ b/commands/aal/write.c @@ -0,0 +1,17 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include + +int +sys_write(fp, bufptr, nbytes) + File *fp; + char *bufptr; + int nbytes; +{ + if (! fp) return 0; + return write(fp->o_fd, bufptr, nbytes) == nbytes; +} diff --git a/commands/advent/Makefile b/commands/advent/Makefile new file mode 100755 index 000000000..249622e78 --- /dev/null +++ b/commands/advent/Makefile @@ -0,0 +1,63 @@ +# Makefile for advent + +# Where to put the adventure text files, and the binary executable. +# Need the trailing "/"s. +TEXTDIR = /usr/lib/advent/ +BINDIR = /usr/bin + +# Flags you may want to add to CFLAGS: +# -DHAS_STDC=0 or 1 We have Standard C. Default=1 iff __STDC__ is nonzero. + +CC = cc +CFLAGS = -D_POSIX_SOURCE +LDFLAGS = -i + +OBJS = advent.o database.o english.o initial.o itverb.o score.o\ + travel.o turn.o utility.o verb.o vocab.o + +DAT = advent1.dat advent2.dat advent3.dat advent4.dat +INSTDAT = $(TEXTDIR)advent1.dat $(TEXTDIR)advent2.dat \ + $(TEXTDIR)advent3.dat $(TEXTDIR)advent4.dat + +all: $(DAT) advent + +install: $(TEXTDIR) $(INSTDAT) $(BINDIR)/advent + +$(TEXTDIR): + install -d -o bin $(TEXTDIR) + +$(TEXTDIR)advent1.dat: advent1.dat + install -c -o bin $? $@ + +$(TEXTDIR)advent2.dat: advent2.dat + install -c -o bin $? $@ + +$(TEXTDIR)advent3.dat: advent3.dat + install -c -o bin $? $@ + +$(TEXTDIR)advent4.dat: advent4.dat + install -c -o bin $? $@ + +$(BINDIR)/advent: advent + install -cs -o bin $? $@ + +advent: $(OBJS) + $(CC) $(LDFLAGS) -o advent $(OBJS) + +setup: setup.c advent.h + $(CC) $(CFLAGS) $(LDFLAGS) -o setup setup.c + +advtext.h advent1.dat advent2.dat advent3.dat advent4.dat: \ + setup advent1.txt advent2.txt advent3.txt advent4.txt + ./setup + +advent.o: advent.h advdec.h advent.c + $(CC) -c $(CFLAGS) -DTEXTDIR='"$(TEXTDIR)"' advent.c + +database.o: advent.h advdec.h advtext.h +travel.o: advent.h advdec.h advcave.h +initial.o english.o itverb.o score.o turn.o utility.o\ + verb.o vocab.o: advent.h advdec.h + +clean: + @rm -f *.o *.BAK *.dat advtext.h core advent setup diff --git a/commands/advent/advcave.h b/commands/advent/advcave.h new file mode 100755 index 000000000..cedb392e7 --- /dev/null +++ b/commands/advent/advcave.h @@ -0,0 +1,1357 @@ +/* header ADVCAVE.H */ + + +/** WARNING: the travel array for the cave is stored as an + * array of 1.. LONG INTEGERS. This requires 32 bit LONG INTEGERS. + * These values are used in database.c "gettrav". + * tcond*1000000 + tdest*1000 + tverb = value stored + */ + +static long room1[] = { + 2002, 2044, 2029, 2056, + 3003, 3012, 3019, 3043, + 4005, 4013, 4014, 4046, 4030, + 5006, 5045, 5043, + 8063, + 147081, + 157052, 157084, + 0}; +static long room2[] = { + 1002, 1012, 1007, 1043, 1045, 1030, + 30146006, + 5006, 5045, 5046, + 146044, + 0}; +static long room3[] = { + 1011, 1032, 1044, + 11062, + 33065, + 467785085, + 202085, + 79005, 79014, + 238057, 238003, 238019, + 0}; +static long room4[] = { + 1004, 1012, 1045, + 5006, 5043, 5044, 5029, + 7005, 7046, 7030, + 8063, + 0}; +static long room5[] = { + 4009, 4043, 4030, + 50005006, 50005007, 50005045, + 6006, + 5044, 5046, + 0}; +static long room6[] = { + 1002, 1045, + 4009, 4043, 4044, 4030, + 5006, 5046, + 0}; +static long room7[] = { + 1012, + 4004, 4045, + 5006, 5043, 5044, + 8005, 8015, 8016, 8046, + 595060, 595014, 595030, + 0}; +static long room8[] = { + 5006, 5043, 5044, 5046, + 1012, + 7004, 7013, 7045, + 303009003, 303009019, 303009030, + 593003, + 0}; +static long room9[] = { + 303008011, 303008029, + 593011, + 10017, 10018, 10019, 10044, + 14031, + 11051, + 0}; +static long room10[] = { + 9011, 9020, 9021, 9043, + 11019, 11022, 11044, 11051, + 14031, + 0}; +static long room11[] = { + 303008063, + 9064, + 10017, 10018, 10023, 10024, 10043, + 12025, 12019, 12029, 12044, + 3062, + 14031, + 0}; +static long room12[] = { + 303008063, + 9064, + 11030, 11043, 11051, + 13019, 13029, 13044, + 14031, + 0}; +static long room13[] = { + 303008063, + 9064, + 11051, + 12025, 12043, + 14023, 14031, 14044, + 0}; +static long room14[] = { + 303008063, + 9064, + 11051, + 13023, 13043, + 150020030, 150020031, 150020034, + 15030, + 16033, 16044, + 0}; +static long room15[] = { + 18036, 18046, + 17007, 17038, 17044, + 19010, 19030, 19045, + 150022029, 150022031, 150022034, 150022035, + 150022023, 150022043, 150022056, 150022010, + 14029, + 33055, + 0}; +static long room16[] = { + 14001, + 0}; +static long room17[] = { + 15038, 15043, + 312596039, + 412021007, + 412597041, 412597042, 412597044, 412597069, 412597089, + 27041, + 0}; +static long room18[] = { + 15038, 15011, 15045, + 0}; +static long room19[] = { + 150237029, 150237031, 150237034, 150237035, + 150237043, 150237056, 150237010, + 15025, + 15010, 15029, 15043, + 75556047, + 311143047, + 311028045, 311028036, + 311029046, 311029037, + 311030044, 311030007, + 32045, + 15074049, + 211032049, + 74066, + 0}; +static long room20[] = { + 1, + 0}; +static long room21[] = { + 1, + 0}; +static long room22[] = { + 15001, + 0}; +static long room23[] = { + 67043, 67042, + 68044, 68061, + 25030, 25031, + 648052, + 0}; +static long room24[] = { + 67029, 67011, + 92027, + 0}; +static long room25[] = { + 23029, 23011, + 724031056, + 26056, + 0}; +static long room26[] = { + 88001, + 0}; +static long room27[] = { + 312596039, + 412021007, + 412597041, 412597042, 412597043, 412597069, 412597089, + 17041, + 40045, + 41044, + 0}; +static long room28[] = { + 19038, 19011, 19046, + 33045, 33055, + 36030, 36052, + 0}; +static long room29[] = { + 19038, 19011, 19045, + 0}; +static long room30[] = { + 19038, 19011, 19043, + 62044, 62029, + 0}; +static long room31[] = { + 524089001, + 90001, + 0}; +static long room32[] = { + 19001, + 0}; +static long room33[] = { + 3065, + 28046, + 34043, 34053, 34054, + 35044, + 159302071, + 100071, + 0}; +static long room34[] = { + 33030, 33055, + 15029, + 0}; +static long room35[] = { + 33043, 33055, + 20039, + 0}; +static long room36[] = { + 37043, 37017, + 28029, 28052, + 39044, + 65070, + 0}; +static long room37[] = { + 36044, 36017, + 38030, 38031, 38056, + 0}; +static long room38[] = { + 37056, 37029, 37011, + 595060, 595014, 595030, 595004, 595005, + 0}; +static long room39[] = { + 36043, 36023, + 64030, 64052, 64058, + 65070, + 0}; +static long room40[] = { + 41001, + 0}; +static long room41[] = { + 42046, 42029, 42023, 42056, + 27043, + 59045, + 60044, 60017, + 0}; +static long room42[] = { + 41029, + 42045, + 43043, + 45046, + 80044, + 0}; +static long room43[] = { + 42044, + 44046, + 45043, + 0}; +static long room44[] = { + 43043, + 48030, + 50046, + 82045, + 0}; +static long room45[] = { + 42044, + 43045, + 46043, + 47046, + 87029, 87030, + 0}; +static long room46[] = { + 45044, 45011, + 0}; +static long room47[] = { + 45043, 45011, + 0}; +static long room48[] = { + 44029, 44011, + 0}; +static long room49[] = { + 50043, + 51044, + 0}; +static long room50[] = { + 44043, + 49044, + 51030, + 52046, + 0}; +static long room51[] = { + 49044, + 50029, + 52043, + 53046, + 0}; +static long room52[] = { + 50044, + 51043, + 52046, + 53029, + 55045, + 86030, + 0}; +static long room53[] = { + 51044, + 52045, + 54046, + 0}; +static long room54[] = { + 53044, 53011, + 0}; +static long room55[] = { + 52044, + 55045, + 56030, + 57043, + 0}; +static long room56[] = { + 55029, 55011, + 0}; +static long room57[] = { + 13030, 13056, + 55044, + 58046, + 83045, + 84043, + 0}; +static long room58[] = { + 57043, 57011, + 0}; +static long room59[] = { + 27001, + 0}; +static long room60[] = { + 41043, 41029, 41017, + 61044, + 62045, 62030, 62052, + 50191046, 50191033, + 392173046, 392173033, + 172046, 172033, + 648056, + 0}; +static long room61[] = { + 60043, + 62045, + 100107046, + 0}; +static long room62[] = { + 60044, + 63045, + 30043, + 61046, + 0}; +static long room63[] = { + 62046, 62011, + 0}; +static long room64[] = { + 39029, 39056, 39059, + 65044, 65070, + 103045, 103074, + 106043, + 0}; +static long room65[] = { + 64043, + 66044, + 40556046, + 68061, + 40556029, + 50070029, + 39029, + 40556045, + 50072045, + 71045, + 50556030, + 106030, + 71025, + 0}; +static long room66[] = { + 65047, + 67044, + 80556046, + 77025, + 96043, + 50556050, + 97072, + 0}; +static long room67[] = { + 66043, + 23044, 23042, + 24030, 24031, + 0}; +static long room68[] = { + 23046, + 69029, 69056, + 65045, + 0}; +static long room69[] = { + 68030, 68061, + 331120046, + 119046, + 109045, + 113075, + 0}; +static long room70[] = { + 71045, + 65030, 65023, + 111046, + 0}; +static long room71[] = { + 65048, + 70046, + 110045, + 0}; +static long room72[] = { + 65070, + 118047, + 73045, + 97049, 97072, + 0}; +static long room73[] = { + 72046, 72017, 72011, + 0}; +static long room74[] = { + 19043, + 331120044, + 121044, + 75030, + 0}; +static long room75[] = { + 76046, + 77045, + 0}; +static long room76[] = { + 75045, + 0}; +static long room77[] = { + 75043, + 78044, + 66045, 66017, + 0}; +static long room78[] = { + 77046, + 0}; +static long room79[] = { + 3001, + 0}; +static long room80[] = { + 42045, + 80044, 80046, + 81043, + 0}; +static long room81[] = { + 80044, 80011, + 0}; +static long room82[] = { + 44046, 44011, + 0}; +static long room83[] = { + 57046, + 84043, + 85044, + 0}; +static long room84[] = { + 57045, + 83044, + 114050, + 0}; +static long room85[] = { + 83043, 83011, + 0}; +static long room86[] = { + 52029, 52011, + 0}; +static long room87[] = { + 45029, 45030, + 0}; +static long room88[] = { + 25030, 25056, 25043, + 20039, + 92044, 92027, + 0}; +static long room89[] = { + 25001, + 0}; +static long room90[] = { + 23001, + 0}; +static long room91[] = { + 95045, 95073, 95023, + 72030, 72056, + 0}; +static long room92[] = { + 88046, + 93043, + 94045, + 0}; +static long room93[] = { + 92046, 92027, 92011, + 0}; +static long room94[] = { + 92046, 92027, 92023, + 341095045, 341095003, 341095073, + 611045, + 0}; +static long room95[] = { + 94046, 94011, + 92027, + 91044, + 145030, 145039, 145080, 145052, + 0}; +static long room96[] = { + 66044, 66011, + 0}; +static long room97[] = { + 66048, + 72043, 72017, + 98029, 98045, 98073, + 0}; +static long room98[] = { + 235047, + 97046, 97072, + 99044, + 3142029, 3142049, 3142056, 3142079, + 75702029, 75702049, 75702056, 75702079, + 703029, 703049, 703056, 703079, + 141078, + 0}; +static long room99[] = { + 98050, 98073, + 301043, 301023, + 100043, + 0}; +static long room100[] = { + 301044, 301023, 301011, + 99044, + 159302071, 159302055, + 33071, 33055, + 101047, 101022, + 0}; +static long room101[] = { + 100046, 100071, 100011, + 0}; +static long room102[] = { + 103030, 103074, 103011, + 0}; +static long room103[] = { + 388196029, 388196038, + 102029, 102038, + 104030, + 114618046, + 115619046, + 64046, + 0}; +static long room104[] = { + 103029, 103074, + 105030, + 0}; +static long room105[] = { + 104029, 104011, + 103074, + 0}; +static long room106[] = { + 64029, + 65044, + 108043, + 0}; +static long room107[] = { + 131046, + 132049, + 133047, + 134048, + 135029, + 136050, + 137043, + 138044, + 139045, + 61030, + 0}; +static long room108[] = { + 90556043, 90556045, 90556046, 90556047, + 90556048, 90556049, 90556050, 90556029, + 90556030, + 106043, + 626044, + 0}; +static long room109[] = { + 69046, + 113045, 113075, + 0}; +static long room110[] = { + 71044, + 20039, + 0}; +static long room111[] = { + 70045, + 40050030, 40050039, 40050056, + 50053030, + 45030, + 0}; +static long room112[] = { + 131049, + 132045, + 133043, + 134050, + 135048, + 136047, + 137044, + 138030, + 139029, + 140046, + 0}; +static long room113[] = { + 109046, 109011, + 0}; +static long room114[] = { + 84048, + 0}; +static long room115[] = { + 116049, 116042, 116069, + 212019, 212003, 212045, + 0}; +static long room116[] = { + 115047, 115042, 115069, + 212019, 212003, + 593030, + 0}; +static long room117[] = { + 118049, + 233660041, 233660042, 233660069, 233660047, 233660089, + 332661041, + 303041, + 332021039, + 596039, + 0}; +static long room118[] = { + 72030, + 117029, + 0}; +static long room119[] = { + 69045, 69011, + 653043, 65307, + 0}; +static long room120[] = { + 69045, + 74043, + 0}; +static long room121[] = { + 74043, 74011, + 653045, 653007, + 0}; +static long room122[] = { + 123047, + 233660041, 233660042, 233660069, 233660049, 233660089, + 303041, + 596039, + 124077, + 126028, + 129040, + 0}; +static long room123[] = { + 122044, + 124043, 124077, + 126028, + 129040, + 0}; +static long room124[] = { + 123044, + 125047, 125036, + 128048, 128037, 128030, + 126028, + 129040, + 0}; +static long room125[] = { + 124046, 124077, + 126045, 126028, + 127043, 127017, + 0}; +static long room126[] = { + 125046, 125023, 125011, + 124077, + 610030, 610039, + 0}; +static long room127[] = { + 125044, 125011, 125017, + 124077, + 126028, + 0}; +static long room128[] = { + 124045, 124029, 124077, + 129046, 129030, 129040, + 126028, + 0}; +static long room129[] = { + 128044, 128029, + 124077, + 130043, 130019, 130040, 130003, + 126028, + 0}; +static long room130[] = { + 129044, 129011, + 124077, + 126028, + 0}; +static long room131[] = { + 107044, + 132048, + 133050, + 134049, + 135047, + 136029, + 137030, + 138045, + 139046, + 112043, + 0}; +static long room132[] = { + 107050, + 131029, + 133045, + 134046, + 135044, + 136049, + 137047, + 138043, + 139030, + 112048, + 0}; +static long room133[] = { + 107029, + 131030, + 132044, + 134047, + 135049, + 136043, + 137045, + 138050, + 139048, + 112046, + 0}; +static long room134[] = { + 107047, + 131045, + 132050, + 133048, + 135043, + 136030, + 137046, + 138029, + 139044, + 112049, + 0}; +static long room135[] = { + 107045, + 131048, + 132030, + 133046, + 134043, + 136044, + 137049, + 138047, + 139050, + 112029, + 0}; +static long room136[] = { + 107043, + 131044, + 132029, + 133049, + 134030, + 135046, + 137050, + 138048, + 139047, + 112045, + 0}; +static long room137[] = { + 107048, + 131047, + 132046, + 133030, + 134029, + 135050, + 136045, + 138049, + 139043, + 112044, + 0}; +static long room138[] = { + 107030, + 131043, + 132047, + 133029, + 134044, + 135045, + 136046, + 137048, + 139049, + 112050, + 0}; +static long room139[] = { + 107049, + 131050, + 132043, + 133044, + 134045, + 135030, + 136048, + 137029, + 138046, + 112047, + 0}; +static long room140[] = { + 112045, 112011, + 0}; +static long room141[] = { + 142029, 142043, 142056, 142078, 142079, + 0}; +static long room142[] = { + 98043, 98030, 98007, 98079, + 141008, 141044, 141078, + 0}; +static long room143[] = { + 19044, 19011, + 144043, + 0}; +static long room144[] = { + 143044, 143008, + 19011, + 0}; +static long room145[] = { + 141001, + 0}; +static long room146[] = { + 2043, 2029, 2002, 2056, + 50006046, 50006006, + 146046, 146006, + 147044, + 148045, + 0}; +static long room147[] = { + 146043, 146046, + 148045, + 153044, + 157052, + 3012, + 157084, + 0}; +static long room148[] = { + 50147046, + 146046, + 149043, 149044, 149045, + 3012, + 166091, + 0}; +static long room149[] = { + 149046, + 151043, + 150044, + 152045, + 0}; +static long room150[] = { + 151045, 151046, + 149043, 149044, + 0}; +static long room151[] = { + 148043, + 152044, + 150045, + 149046, + 0}; +static long room152[] = { + 151043, + 150046, + 0}; +static long room153[] = { + 154045, + 239046, + 147043, + 3012, + 0}; +static long room154[] = { + 50155045, + 157045, 157030, + 155029, + 153046, + 3012, + 0}; +static long room155[] = { + 154030, 154008, 154046, + 156039, + 3012, + 0}; +static long room156[] = { + 1, + 0}; +static long room157[] = { + 227019, 227043, + 154011, 154046, 154029, + 3012, + 0}; +static long room158[] = { + 229044, 229034, 229030, + 157011, + 161082, + 725056, 725029, + 726083, + 747003, 747019, 747043, 747064, + 0}; +static long room159[] = { + 732011, 732044, 732034, + 0}; +static long room160[] = { + 343167043, 343167019, 343167003, + 738043, + 162082, + 723044, 723056, 723030, + 165039, + 0}; +static long room161[] = { + 304001, + 0}; +static long room162[] = { + 304001, + 0}; +static long room163[] = { + 304001, + 0}; +static long room164[] = { + 304001, + 0}; +static long room165[] = { + 1, + 0}; +static long room166[] = { + 747003, 747011, 747019, 747044, + 148232043, 148232069, 148232041, 148232042, + 148169047, + 148233046, + 148234045, + 717043, 717069, 717041, 717042, 717047, + 448164082, + 720082, + 0}; +static long room167[] = { + 163082, + 343160044, 343160011, 343160003, 343160019, + 738044, + 722043, 722069, 722041, 722042, + 0}; +static long room168[] = { + 448170048, 448170029, 448170023, + 719048, 719029, 719023, + 148169045, + 148192046, + 148166044, 148166069, 148166041, 148166042, + 717044, 717045, 717046, + 0}; +static long room169[] = { + 148168046, + 148166049, + 717049, 717046, + 448179023, 448179043, 448179029, + 719023, 719029, + 0}; +static long room170[] = { + 171043, 171029, + 168044, 168030, + 0}; +static long room171[] = { + 170050, 170030, + 180043, 180029, + 0}; +static long room172[] = { + 60045, + 174046, + 0}; +static long room173[] = { + 60045, + 0}; +static long room174[] = { + 392178047, + 172047, 172033, + 175050, + 0}; +static long room175[] = { + 177045, + 174043, + 176031, 176030, 176056, + 0}; +static long room176[] = { + 175029, 175056, 175011, 175008, + 0}; +static long room177[] = { + 60030, 60056, 60039, + 175046, 175008, + 0}; +static long room178[] = { + 174001, + 0}; +static long room179[] = { + 169030, 169044, + 198029, 198043, 198014, + 0}; +static long room180[] = { + 171044, + 182029, 182056, + 213043, + 0}; +static long room181[] = { + 227001, + 0}; +static long room182[] = { + 180044, + 183043, + 0}; +static long room183[] = { + 182044, + 50184043, + 186043, + 185046, + 187045, + 0}; +static long room184[] = { + 183049, + 188048, + 0}; +static long room185[] = { + 183045, + 0}; +static long room186[] = { + 183050, + 188047, + 0}; +static long room187[] = { + 183044, + 188043, + 0}; +static long room188[] = { + 187045, + 184044, + 65190049, 65190030, + 186049, + 305019, 305003, + 0}; +static long room189[] = { + 188011, 188008, + 0}; +static long room190[] = { + 422306069, 422306045, 422306042, 422306089, + 661069, + 188029, 188008, + 0}; +static long room191[] = { + 60045, + 0}; +static long room192[] = { + 148166045, + 148168047, + 717045, 717047, + 448197043, + 720043, + 0}; +static long room193[] = { + 197044, + 0}; +static long room194[] = { + 224044, + 0}; +static long room195[] = { + 196052, 196053, 196046, 196069, 196036, + 224044, + 0}; +static long room196[] = { + 103030, 103074, + 114618052, 114618053, 114618045, 114618069, + 195052, + 0}; +static long room197[] = { + 193043, + 192044, + 0}; +static long room198[] = { + 179049, 179014, 179005, 179023, + 203030, 203043, + 402200046, 402200029, + 772046, 772029, + 0}; +static long room199[] = { + 198001, + 0}; +static long room200[] = { + 198044, + 201043, 201029, + 0}; +static long room201[] = { + 402200044, 402200030, + 772044, 772030, + 202043, + 230045, + 0}; +static long room202[] = { + 201044, + 467785085, + 205085, + 206043, + 0}; +static long room203[] = { + 447199030, 447199043, 447199088, + 204030, 204043, 204088, + 198029, 198044, + 0}; +static long room204[] = { + 203029, 203044, 203008, 203011, + 0}; +static long room205[] = { + 219045, + 467785085, + 202085, + 226039, + 0}; +static long room206[] = { + 207045, 207017, + 202044, + 648029, 648052, + 0}; +static long room207[] = { + 208045, + 206046, + 0}; +static long room208[] = { + 236043, + 207046, 207017, + 209044, + 793069, 793042, 793045, + 0}; +static long room209[] = { + 208043, + 210029, 210056, + 0}; +static long room210[] = { + 211030, 211043, + 209044, + 0}; +static long room211[] = { + 210029, 210008, 210044, + 72030, 72043, + 0}; +static long room212[] = { + 115011, 115008, 115046, + 0}; +static long room213[] = { + 206030, 206052, + 180044, 180008, + 0}; +static long room214[] = { + 148166046, + 148169048, + 717046, 717048, + 448215047, + 720047, + 0}; +static long room215[] = { + 214049, + 216029, + 0}; +static long room216[] = { + 215030, + 217043, 217029, + 0}; +static long room217[] = { + 216044, 216030, 216011, + 218043, 218031, + 0}; +static long room218[] = { + 217008, 217044, + 230029, 230056, + 0}; +static long room219[] = { + 205046, 205089, + 222045, 222090, + 220043, + 221044, + 3092, + 0}; +static long room220[] = { + 219044, + 225043, + 0}; +static long room221[] = { + 219043, + 0}; +static long room222[] = { + 219046, + 223029, + 3092, + 0}; +static long room223[] = { + 222030, + 224029, + 0}; +static long room224[] = { + 223030, 223052, + 338195043, + 194043, + 0}; +static long room225[] = { + 220044, + 0}; +static long room226[] = { + 1, + 0}; +static long room227[] = { + 157044, 157011, 157029, + 398228043, 398228019, 398228030, + 181043, 181019, 181030, + 0}; +static long room228[] = { + 792045, 792046, 792004, 792005, 792033, + 227029, 227044, 227011, + 229039, + 786043, 786019, 786042, 786069, 786041, + 0}; +static long room229[] = { + 792045, 792046, 792004, 792005, 792033, + 228039, + 786044, 786011, 786042, 786069, 796041, + 158043, 158019, 158034, + 0}; +static long room230[] = { + 65218030, 65218039, + 231030, 231039, + 201046, 201017, + 0}; +static long room231[] = { + 1, + 0}; +static long room232[] = { + 168001, + 0}; +static long room233[] = { + 192001, + 0}; +static long room234[] = { + 214001, + 0}; +static long room235[] = { + 422306069, 422306046, 422306042, 422306089, + 661069, + 98044, + 0}; +static long room236[] = { + 208044, + 0}; +static long room237[] = { + 19001, + 0}; +static long room238[] = { + 3011, 3064, + 0}; +static long room239[] = { + 153045, 153047, + 240093, + 0}; +static long room240[] = { + 239047, 239042, 239069, 239093, + 241030, + 0}; +static long room241[] = { + 240029, + 242044, + 0}; +static long room242[] = { + 241043, + 243049, + 244044, + 246050, + 0}; +static long room243[] = { + 242047, + 244045, + 247050, + 0}; +static long room244[] = { + 242043, + 243046, + 245044, + 246045, + 0}; +static long room245[] = { + 243046, + 244043, + 246045, + 247044, + 0}; +static long room246[] = { + 242048, + 245046, + 247049, + 0}; +static long room247[] = { + 243048, + 245043, + 246047, + 307044, + 0}; +static long room248[] = { + 247043, + 0}; + +static long *cave[MAXLOC] = { + room1, room2, room3, room4, + room5, room6, room7, room8, room9, + room10, room11, room12, room13, room14, + room15, room16, room17, room18, room19, + room20, room21, room22, room23, room24, + room25, room26, room27, room28, room29, + room30, room31, room32, room33, room34, + room35, room36, room37, room38, room39, + room40, room41, room42, room43, room44, + room45, room46, room47, room48, room49, + room50, room51, room52, room53, room54, + room55, room56, room57, room58, room59, + room60, room61, room62, room63, room64, + room65, room66, room67, room68, room69, + room70, room71, room72, room73, room74, + room75, room76, room77, room78, room79, + room80, room81, room82, room83, room84, + room85, room86, room87, room88, room89, + room90, room91, room92, room93, room94, + room95, room96, room97, room98, room99, + room100, room101, room102, room103, room104, + room105, room106, room107, room108, room109, + room110, room111, room112, room113, room114, + room115, room116, room117, room118, room119, + room120, room121, room122, room123, room124, + room125, room126, room127, room128, room129, + room130, room131, room132, room133, room134, + room135, room136, room137, room138, room139, + room140, room141, room142, room143, room144, + room145, room146, room147, room148, room149, + room150, room151, room152, room153, room154, + room155, room156, room157, room158, room159, + room160, room161, room162, room163, room164, + room165, room166, room167, room168, room169, + room170, room171, room172, room173, room174, + room175, room176, room177, room178, room179, + room180, room181, room182, room183, room184, + room185, room186, room187, room188, room189, + room190, room191, room192, room193, room194, + room195, room196, room197, room198, room199, + room200, room201, room202, room203, room204, + room205, room206, room207, room208, room209, + room210, room211, room212, room213, room214, + room215, room216, room217, room218, room219, + room220, room221, room222, room223, room224, + room225, room226, room227, room228, room229, + room230, room231, room232, room233, room234, + room235, room236, room237, room238, room239, + room240, room241, room242, room243, room244, + room245, room246, room247, room248}; diff --git a/commands/advent/advdec.h b/commands/advent/advdec.h new file mode 100755 index 000000000..c2c144fe0 --- /dev/null +++ b/commands/advent/advdec.h @@ -0,0 +1,71 @@ +/* header ADVDEC.H * + * WARNING: GLOBAL (EXTERNAL) declarations for adventure */ + +#ifndef EXTERN /* #define as '' to define the variables */ +#define EXTERN extern +#endif + +EXTERN boolean gaveup; /* TRUE if he quits early */ +EXTERN FILE *fd1, *fd2, *fd3, *fd4; + +/* + English variables +*/ +EXTERN char *vtxt[MAXWORDS], *iotxt[MAXITEMS], *otxt[MAXITEMS]; +EXTERN int verbs[MAXWORDS], objs[MAXITEMS], iobjs[MAXITEMS]; +EXTERN int vrbx, objx, iobx; +EXTERN int verb, object, motion, iobj, prep; +EXTERN boolean newtravel, is_wiz; + +/* + Play variables +*/ +extern int plac[MAXOBJ]; /* initial location */ +extern int fixd[MAXOBJ]; +struct playinfo { + int turns; + int loc, oldloc, oldloc2, newloc; /* location variables */ + long loc_attrib[MAXLOC+1]; /* location status */ + int place[MAXOBJ]; /* object location */ + int fixed[MAXOBJ]; /* second object loc */ + int weight[MAXOBJ]; + int atloc[MAXLOC+1]; + int link[MAXOBJ * 2]; + int holder[MAXOBJ]; + int hlink[MAXOBJ]; + int visited[MAXLOC+1]; /* >0 if has been here */ + int prop[MAXOBJ]; /* status of object */ + long obj_state[MAXOBJ]; + long points[MAXOBJ]; + int hinted[HNTMAX+1]; + int hints[HNTMAX+1][5]; + int hintlc[HNTMAX+1]; + int tally, tally2; /* item counts */ + int limit; /* time limit */ + int lmwarn; /* lamp warning flag */ + int wzdark, closing, closed; /* game state flags */ + int holding; /* count of held items */ + int detail; /* LOOK count */ + int knfloc; /* knife location */ + int clock, clock2, panic; /* timing variables */ + int dloc[DWARFMAX+1]; /* dwarf locations */ + int dflag; /* dwarf flag */ + int dseen[DWARFMAX+1]; /* dwarf seen flag */ + int odloc[DWARFMAX+1]; /* dwarf old locations */ + int daltloc; /* alternate appearance */ + int dkill; /* dwarves killed */ + int chloc, chloc2; /* chest locations */ + int bonus; /* to pass to end */ + int numdie; /* number of deaths */ + int foobar; /* fee fie foe foo... */ + int combo; /* combination for safe */ + boolean terse; + int abbnum; + int health; + int chase; + boolean flg239; + + + int lastglob; /* to get space req. */ +}; +extern struct playinfo g; diff --git a/commands/advent/advent.c b/commands/advent/advent.c new file mode 100755 index 000000000..34145ef94 --- /dev/null +++ b/commands/advent/advent.c @@ -0,0 +1,136 @@ +/** Adventure translated from Fortran to "C" + and ported to Minix by: + Robert R. Hall + San Diego, Calif 92115 + hall@crash.cts.com + */ + +/** program ADVENT.C * + * "advent.c" allocates GLOBAL storage space by * + * #defining EXTERN before #including "advdec.h". */ + + +#include +#include +#include +#include +#include +#include +#include "advent.h" /* #define preprocessor equates */ +#include "advdec.h" + +#ifndef TEXTDIR +#define TEXTDIR "" +#endif + +char textdir[] = TEXTDIR; /* directory where text files + live. */ + +_PROTOTYPE(int main, (int, char **)); +_PROTOTYPE(static void opentxt, (void)); +_PROTOTYPE(static void file_error, (char *)); + +int main(argc, argv) +int argc; +char **argv; +{ + opentxt(); + initialize(); + rspeak(325); + if (argc == 2) + restore(argv[1]); + else { + g.hinted[3] = yes(65, 1, 0); + g.limit = (g.hinted[3] ? 800 : 550); + } + gaveup = FALSE; + srand((unsigned) time(NULL)); /* seed random */ + while (!gaveup) + turn(); + fclose(fd1); + fclose(fd2); + fclose(fd3); + fclose(fd4); + return (EXIT_SUCCESS); /* exit = ok */ +} /* main */ + +/* + Open advent?.txt files +*/ +static void opentxt() +{ + static char filename[sizeof(textdir) + 16]; + static FILE **fp[] = {0, &fd1, &fd2, &fd3, &fd4}; + int i; + for (i = 1; i <= 4; i++) { + sprintf(filename, "%sadvent%d.dat", textdir, i); + *fp[i] = fopen(filename, "r"); + if (!*fp[i]) + file_error(filename); + } +} + +/* + save adventure game +*/ +void saveadv(username) +char *username; +{ + int cnt; + FILE *savefd; + + savefd = fopen(username, "wb"); + if (savefd == NULL) { + perror(username); + return; + } + cnt = fwrite((void *) &g, 1, sizeof(struct playinfo), savefd); + if (cnt != sizeof(struct playinfo)) { + fprintf(stderr, "wrote %d of %u bytes\n", + cnt, (unsigned) sizeof(struct playinfo)); + if (ferror(savefd)) { + fprintf(stderr, "errno is: 0x%.4x\n", errno); + perror(username); + } + } + if (fclose(savefd) == -1) { + perror(username); + } + printf("Saved in %s.\n", username); + return; +} + +/* + restore saved game handler +*/ +void restore(username) +char *username; +{ + int cnt; + FILE *restfd; + + restfd = fopen(username, "rb"); + if (restfd == NULL) + file_error(username); + cnt = fread((void *) &g, 1, sizeof(struct playinfo), restfd); + if (cnt != sizeof(struct playinfo)) { + fprintf(stderr, "read %d bytes, expected %u\n", + cnt, (unsigned) sizeof(struct playinfo)); + if (ferror(restfd)) { + fprintf(stderr, "errno is: 0x%.4x\n", errno); + perror(username); + } + } + if (fclose(restfd) == -1) { + perror(username); + } + printf("Restored from %s.\n", username); + return; +} + +static void file_error(filename) +char *filename; +{ + perror(filename); + exit(EXIT_FAILURE); +} diff --git a/commands/advent/advent.h b/commands/advent/advent.h new file mode 100755 index 000000000..5e7abb419 --- /dev/null +++ b/commands/advent/advent.h @@ -0,0 +1,405 @@ +/* header ADVENT.H * + * WARNING: HEADER file for all adventure modules */ + +#ifndef EXIT_FAILURE +#define EXIT_FAILURE 1 +#define EXIT_SUCCESS (!(EXIT_FAILURE)) +#endif + +#define INPUTBUFLEN 80 /* Max input line length */ + +typedef int boolean; +#define FALSE (0) +#define TRUE (!FALSE) + +#define MAXOBJ 123 /* max # of objects in cave */ +#define MAXLOC 248 /* max # of cave locations */ +#define WORDSIZE 20 /* max # of chars in commands */ +#define MAXMSG 408 /* max # of long location descr */ +#define HNTMAX 18 /* max # of hints */ +#define HNTMIN 7 /* hints starting count */ + +#define MAXWORDS 25 +#define MAXITEMS 45 + +#define CLASS(word) ((word)<0 ? -((-(word)) / 1000) : (word) / 1000) +#define VAL(word) ((word)<0 ? -((-(word)) % 1000) : (word) % 1000) +#define MAXTRAV (23+1) /* max # of travel directions from loc */ + /* +1 for terminator travel[x].tdest=-1 */ +#define DWARFMAX 6 /* max # of nasty dwarves */ +#define MAXDIE 3 /* max # of deaths before close */ +#define MAXTRS 79 /* max # of */ + +#define Y2 33 +/* + Object definitions +*/ +#define ANVIL 91 +#define AXE 28 +#define BATTERIES 39 +#define BEAR 35 +#define BEES 87 +#define BILLBD 116 +#define BIRD 101 +#define BOAT 48 +#define BOOK 110 +#define BOOK2 BOOK + 1 +#define BOOTH 93 +#define BOTTLE 20 +#define BRUSH 114 +#define CAGE 4 +#define CAKES 107 +#define CARVNG 115 +#define CASK 71 +#define CHAIN 64 +#define CHASM 21 +#define CHASM2 CHASM + 1 +#define CHEST 55 +#define CLAM 14 +#define CLOAK 47 +#define COINS 54 +#define CROWN 66 +#define DOG 98 +#define DOOR 41 /* giant door */ +#define DRAGON 31 +#define DWARF 17 +#define EGGS 56 +#define EMERALD 59 +#define FISSURE 12 +#define FLOWER 46 +#define FLY 69 +#define FOOD 19 +#define GNOME 105 +#define GRAIL 70 +#define GRATE 3 +#define HIVE 97 +#define HONEY 96 +#define HORN 52 +#define JEWELS 53 +#define KEYS 102 +#define KNIFE 18 +#define LAMP 2 +#define LYRE 68 +#define MAGAZINE 16 +#define MESSAGE 36 +#define MIRROR 23 +#define MUSHRM 106 +#define NUGGET 50 +#define OIL 83 +#define OIL2 OIL + 1 +#define OYSTER 15 +#define PLAGUE 125 +#define PEARL 61 +#define PHONE 94 +#define PILLOW 10 +#define PLANT 24 +#define PLANT2 PLANT + 1 +#define POLE 9 +#define POSTER 113 +#define PYRAMID 60 +#define RADIUM 119 +#define RING 72 +#define ROCKS 92 +#define ROD 5 +#define ROD2 ROD + 1 +#define RUG 62 +#define SAFE 112 +#define SAPPHIRE 69 +#define SHIELD 118 +#define SHOES 67 +#define SKEY 90 +#define SLUGS 95 +#define SNAKE 11 +#define SPHERE 120 +#define SPICES 63 +#define SPIDER 121 +#define STEPS 7 +#define STICKS 49 +#define SWORD 65 +#define TABLET 13 +#define TDOOR 42 /* tiny door */ +#define TDOOR2 TDOOR + 1 /* wrought-iron door */ +#define PDOOR TDOOR2 + 1 /* door to phone booth */ +#define TRIDENT 57 +#define TROLL 33 +#define TROLL2 TROLL + 1 +#define VASE 58 +#define VEND 38 +#define WALL 88 +#define WALL2 WALL + 1 +#define WATER 81 /* in bottle */ +#define WATER2 WATER + 1 /* in cask */ +#define WINE 85 /* in bottle */ +#define WINE2 WINE + 1 /* in cask */ +#define WUMPUS 99 + +/* + Verb definitions +*/ +#define BACK 8 +#define CAVE 67 +#define DEPRESSION 63 +#define ENTRANCE 64 +#define EXIT 11 +#define NULLX 21 + +/* + Action verb definitions +*/ +#define TAKE 1 +#define DROP 2 +#define SAY 3 +#define OPEN 4 +#define NOTHING 5 +#define CLOSE 6 +#define ON 7 +#define OFF 8 +#define WAVE 9 +#define CALM 10 +#define WALK 11 +#define KILL 12 +#define POUR 13 +#define EAT 14 +#define DRINK 15 +#define RUB 16 +#define THROW 17 +#define QUIT 18 +#define FIND 19 +#define INVENTORY 20 +#define FEED 21 +#define FILL 22 +#define BLAST 23 +#define SCORE 24 +#define FOO 25 +#define BRIEF 26 +#define READ 27 +#define BREAK 28 +#define WAKE 29 +#define SUSPEND 30 +#define RESTORE 31 +#define YANK 32 +#define WEAR 33 +#define HIT 34 +#define ANSWER 35 +#define BLOW 36 +#define LEAVE 37 +#define YELL 38 +#define DIAL 39 +#define PLAY 40 +#define PICK 41 +#define PUT 42 +#define TURN 43 +#define GET 44 +#define INSRT 45 +#define REMOVE 46 +#define BURN 47 +#define GRIPE 48 +#define LOCK 49 +#define UNLOCK 50 +#define HEALTH 51 +#define LOOK 52 +#define COMBO 53 +#define SWEEP 54 +#define TERSE 55 +#define WIZ 56 +#define MAP 57 +#define GATE 58 +#define PIRLOC 59 + +#define GO 11 +#define SHUT 6 +#define LOG 33 + +#define MOTION 0 /* CLASSD */ +#define NOUN 1 /* CLASSN */ +#define ACTION 2 /* CLASSA */ +#define MISC 3 /* CLASSM */ +#define PREPOSITION 4 /* CLASSP */ +#define ADJACTIVE 5 /* CLASSJ */ +#define CONJUNCTION 6 /* CLASSC */ + +/* + and a few preposition. prefix PREP to distinguish them from + verbs or nouns + */ +#define PREPAT 9 +#define PREPDN 8 +#define PREPIN 1 +#define PREPFR 5 +#define PREPOF 6 +#define PREPOFF 6 +#define PREPON 2 + +/* + BIT mapping of "cond" array which indicates location status +*/ +#define LIGHT 1 +#define WATOIL 2 +#define LIQUID 4 +#define NOPIRAT 16 + +/* Object condition bit functions */ +#define OPENBT 2 +#define LOCKBT 4 +#define BURNBT 6 +#define DEADBT 10 +#define WEARBT 12 +/* + Structure definitions +*/ +struct wac { + char *aword; + int acode; +}; + +struct trav { + int tdest; + int tverb; + int tcond; +}; + +/* Function prototypes. + "#if (__STDC__)" should have been be enough, + but some compilers are stupid, so allow Makefile to say -DHAS_STDC=whatever. +*/ +#if defined(HAS_STDC) ? (HAS_STDC) : (__STDC__) +#undef HAS_STDC +#define HAS_STDC 1 +#define _PROTOTYPE(function, params) function params +#define _CONST const +#else +#define _PROTOTYPE(function, params) function () +#define _CONST +#endif + +/* Advent.c */ + +_PROTOTYPE(void saveadv, (char *username)); +_PROTOTYPE(void restore, (char *username)); + +/* Initialize.c */ + +_PROTOTYPE(void initialize, (void)); + +/* Database.c */ + +_PROTOTYPE(int yes, (int msg1, int msg2, int msg3)); +_PROTOTYPE(void rspeak, (int msg)); +_PROTOTYPE(void pspeak, (int item, int state)); +_PROTOTYPE(void desclg, (int loc)); +_PROTOTYPE(void descsh, (int loc)); + +/* English.c */ + +_PROTOTYPE(int english, (void)); +_PROTOTYPE(int analyze, (char *word, int *type, int *value)); + +/* Itverb.c */ + +_PROTOTYPE(void itverb, (void)); +_PROTOTYPE(void ivblast, (void)); +_PROTOTYPE(void ivlook, (void)); + +/* Turn.c */ + +_PROTOTYPE(void turn, (void)); +_PROTOTYPE(void describe, (void)); +_PROTOTYPE(void descitem, (void)); +_PROTOTYPE(void dwarfend, (void)); +_PROTOTYPE(void normend, (void)); +_PROTOTYPE(void score, (int)); +_PROTOTYPE(void death, (void)); +_PROTOTYPE(char *probj, (void)); +_PROTOTYPE(void trobj, (void)); +_PROTOTYPE(void dwarves, (void)); +_PROTOTYPE(void dopirate, (void)); +_PROTOTYPE(int stimer, (void)); + +/* Verb.c */ + +_PROTOTYPE(void trverb, (void)); +_PROTOTYPE(void vtake, (void)); +_PROTOTYPE(void vdrop, (void)); +_PROTOTYPE(void vopen, (void)); +_PROTOTYPE(void vsay, (void)); +_PROTOTYPE(void von, (void)); +_PROTOTYPE(void voff, (void)); +_PROTOTYPE(void vwave, (void)); +_PROTOTYPE(void vkill, (void)); +_PROTOTYPE(void vpour, (void)); +_PROTOTYPE(void veat, (void)); +_PROTOTYPE(void vdrink, (void)); +_PROTOTYPE(void vthrow, (void)); +_PROTOTYPE(void vfind, (void)); +_PROTOTYPE(void vfill, (void)); +_PROTOTYPE(void vfeed, (void)); +_PROTOTYPE(void vread, (void)); +_PROTOTYPE(void vbreak, (void)); +_PROTOTYPE(void vwake, (void)); +_PROTOTYPE(void actspk, (int verb)); +_PROTOTYPE(void vyank, (void)); +_PROTOTYPE(void vwear, (void)); +_PROTOTYPE(void vlock, (void)); +_PROTOTYPE(void vunlock, (void)); +_PROTOTYPE(void vclose, (void)); + +/* Utility.c */ + +_PROTOTYPE(boolean ajar, (int)); +_PROTOTYPE(boolean at, (int item)); +_PROTOTYPE(boolean athand, (int)); +_PROTOTYPE(void bitoff, (int, int)); +_PROTOTYPE(void biton, (int, int)); +_PROTOTYPE(boolean bitset, (long, int)); +_PROTOTYPE(boolean blind, (void)); +_PROTOTYPE(int burden, (int)); +_PROTOTYPE(void carry, (int obj, int where)); +_PROTOTYPE(int confuz, (void)); +_PROTOTYPE(boolean dark, (void)); +_PROTOTYPE(boolean dcheck, (void)); +_PROTOTYPE(boolean dead, (int)); +_PROTOTYPE(void drop, (int obj, int where)); +_PROTOTYPE(void destroy, (int obj)); +_PROTOTYPE(boolean edible, (int)); +_PROTOTYPE(boolean enclosed, (int)); +_PROTOTYPE(void extract, (int)); +_PROTOTYPE(boolean forced, (int atloc)); +_PROTOTYPE(boolean here, (int item)); +_PROTOTYPE(boolean hinged, (int)); +_PROTOTYPE(boolean holding, (int)); +_PROTOTYPE(void insert, (int, int)); +_PROTOTYPE(boolean inside, (int)); +_PROTOTYPE(void juggle, (int loc)); +_PROTOTYPE(int liq, (int)); +_PROTOTYPE(int liqloc, (int loc)); +_PROTOTYPE(int liq2, (int pbottle)); +_PROTOTYPE(boolean living, (int)); +_PROTOTYPE(boolean locked, (int)); +_PROTOTYPE(boolean locks, (int)); +_PROTOTYPE(void lookin, (int)); +_PROTOTYPE(void move, (int obj, int where)); +_PROTOTYPE(int noway, (void)); +_PROTOTYPE(boolean opaque, (int)); +_PROTOTYPE(boolean outside, (int)); +_PROTOTYPE(boolean pct, (int x)); +_PROTOTYPE(boolean plural, (int)); +_PROTOTYPE(boolean portal, (int)); +_PROTOTYPE(boolean printed, (int)); +_PROTOTYPE(int put, (int obj, int where, int pval)); +_PROTOTYPE(int ranz, (int)); +_PROTOTYPE(boolean small, (int)); +_PROTOTYPE(boolean toting, (int item)); +_PROTOTYPE(boolean treasr, (int)); +_PROTOTYPE(boolean vessel, (int)); +_PROTOTYPE(boolean wearng, (int)); +_PROTOTYPE(boolean worn, (int)); +_PROTOTYPE(void bug, (unsigned int n)); +_PROTOTYPE(char *ask, (char *prompt, char *buf, int buflen)); +_PROTOTYPE(void panic, (char *msg, boolean save)); + +/* travel.c */ +_PROTOTYPE(void domove, (void)); +_PROTOTYPE(void gettrav, (int loc, struct trav *travel)); + +/* vocab.c */ +_PROTOTYPE(int vocab, (char *word, int val)); diff --git a/commands/advent/advent1.txt b/commands/advent/advent1.txt new file mode 100755 index 000000000..e69fea14f --- /dev/null +++ b/commands/advent/advent1.txt @@ -0,0 +1,827 @@ +#1 +You are standing at the end of a road before a small brick building. +Around you is a forest. A small stream flows out of the building and +down a gully. +#2 +You have walked up a hill, still in the forest. The road slopes back +down the other side of the hill. There is a building in the distance. +#3 +You are inside a building, a well house for a large spring. Off +to one side is a small pantry. +#4 +You are in a valley in the forest beside a stream tumbling along a +rocky bed. +#5 +You are in open forest, with a deep valley to one side. Not far +is a large billboard. +#6 +You are in open forest near both a valley and a road. +#7 +At your feet all the water of the stream splashes into a 2-inch slit +in the rock. Downstream the stream bed is bare rock. +#8 +You are in a 20-foot depression floored with bare dirt. Set into the +dirt is a strong steel grate mounted in concrete. A dry stream bed +leads into the depression. +#9 +You are in a small chamber beneath a 3x3 steel grate to the surface. +A low crawl over cobbles leads inward to the west. +#10 +You are crawling over cobbles in a low passage. There is a dim light +at the east end of the passage. +#11 +You are in a debris room filled with stuff washed in from the surface. +A low wide passage with cobbles becomes plugged with mud and debris +here, but an awkward canyon leads upward and west. A note on the wall +says "Magic Word XYZZY". +#12 +You are in an awkward sloping east/west canyon. +#13 +You are in a splendid chamber thirty feet high. The walls are frozen +rivers of orange stone. An awkward canyon and a good passage exit +from east and west sides of the chamber. +#14 +At your feet is a small pit breathing traces of white mist. An east +passage ends here except for a small crack leading on. +#15 +You are at one end of a vast hall stretching forward out of sight to +the west. There are openings to either side. Nearby, a wide stone +staircase leads downward. The hall is filled with wisps of white mist +swaying to and fro almost as if alive. A cold wind blows up the +staircase. There is a passage at the top of a dome behind you. +#16 +The crack is far too small for you to follow. +#17 +You are on the east bank of a fissure slicing clear across the hall. +The mist is quite thick here, and the fissure is too wide to jump. +#18 +This is a low room with a crude note on the wall. The note says, +"You won't get it up the steps". +#19 +You are in the Hall of the Mountain King, with passages off in all +directions. +#20 +You are at the bottom of the pit with a broken neck. +#21 +You didn't make it. +#22 +The dome is unclimbable. +#23 +You are at the west end of the Twopit Room. There is a large hole in +the wall above the pit at this end of the room. +#24 +You are at the bottom of the eastern pit in the Twopit Room. There is +a small pool of oil in one corner of the pit. +#25 +You are at the bottom of the western pit in the Twopit Room. There is +a large hole in the wall about 25 feet above you. +#26 +You clamber up the plant and scurry through the hole at the top. +#27 +You are on the west side of the fissure in the Hall of Mists. +#28 +You are in a low N/S passage at a hole in the floor. The hole goes +down to an E/W passage. +#29 +You are in the South Side Chamber. +#30 +You are in the West Side Chamber of the Hall of the Mountain King. +A passage continues west and up here. +#31 +<$$< +#32 +You can't get by the snake. +#33 +You are in a large room, with a passage to the south, a passage to the +west, and a wall of broken rock to the east. There is a large "Y2" on +a rock in the room's center. +#34 +You are in a jumble of rock, with cracks everywhere. +#35 +You're at a low window overlooking a huge pit, which extends up out of +sight. A floor is indistinctly visible over 50 feet below. Traces of +white mist cover the floor of the pit, becoming thicker to the right. +Marks in the dust around the window would seem to indicate that +someone has been here recently. Directly across the pit from you and +25 feet away there is a similar window looking into a lighted room. A +shadowy figure can be seen there peering back at you. +#36 +You are in a dirty broken passage. To the east is a crawl. To the +west is a large passage. Above you is a hole to another passage. +#37 +You are on the brink of a small clean climbable pit. A crawl leads +west. +#38 +You are in the bottom of a small pit with a little stream, which +enters and exits through tiny slits. +#39 +You are in a large room full of dusty rocks. There is a big hole in +the floor. There are cracks everywhere, and a passage leading east. +#40 +You have crawled through a very low wide passage parallel to and north +of the Hall of Mists. +#41 +You are at the west end of Hall of Mists. A low wide crawl continues +west and another goes north. To the south is a little passage 6 feet +off the floor. +#42 +You are in a maze of twisty little passages, all alike. +#43 +You are in a maze of twisty little passages, all alike. +#44 +You are in a maze of twisty little passages, all alike. +#45 +You are in a maze of twisty little passages, all alike. +#46 +Dead end. +#47 +Dead end. +#48 +Dead end. +#49 +You are in a maze of twisty little passages, all alike. +#50 +You are in a maze of twisty little passages, all alike. +#51 +You are in a maze of twisty little passages, all alike. +#52 +You are in a maze of twisty little passages, all alike. +#53 +You are in a maze of twisty little passages, all alike. +#54 +Dead end. +#55 +You are in a maze of twisty little passages, all alike. +#56 +Dead end. +#57 +You are on the brink of a thirty foot pit with a massive orange column +down one wall. You could climb down here but you could not get back +up. The maze continues at this level. +#58 +Dead end. +#59 +You have crawled through a very low wide passage parallel to and north +of the Hall of Mists. +#60 +You are at the east end of a very long hall apparently without side +chambers. In the south wall are several wide cracks and a high +hole, but the hole is far above your head. To the east a wide +crawl slants up. To the north a round two foot hole slants down. +#61 +You are at the west end of a very long featureless hall. The hall +joins up with a narrow north/south passage. +#62 +You are at a crossover of a high N/S passage and a low E/W one. +#63 +Dead end. +#64 +You are at a complex junction. A low hands and knees passage from the +north joins a higher crawl from the east to make a walking passage +going west. There is also a large room above. The air is damp here. +#65 +You are in Bedquilt, a long east/west passage with holes everywhere. +To explore at random select north, south, up, or down. +#66 +You are in a room whose walls resemble swiss cheese. Obvious passages +go west, east, NE, and NW. Part of the room is occupied by a large +bedrock block. +#67 +You are at the east end of the Twopit Room. The floor here is +littered with thin rock slabs, which make it easy to descend the pits. +There is a path here bypassing the pits to connect passages from east +and west. There are holes all over, but the only big one is on the +wall directly over the west pit where you can't get to it. +#68 +You are in a large low circular chamber whose floor is an immense slab +fallen from the ceiling (Slab Room). East and west there once were +large passages, but they are now filled with boulders. Low small +passages go north and south, and the south one quickly bends west +around the boulders. +#69 +You are in a secret N/S canyon above a large room. +#70 +You are in a secret N/S canyon above a sizable passage. +#71 +You are in a secret canyon at a junction of three canyons, bearing +north, south, and SE. The north one is as tall as the other two +combined. +#72 +You are in a large low room. Crawls lead north, NE, and SW. +#73 +Dead end crawl. +#74 +You are in a secret canyon which here runs E/W. It crosses over a +very tight canyon 15 feet below. If you go down you may not be able +to get back up. +#75 +You are at a wide place in a very tight N/S canyon. +#76 +The canyon here becomes too tight to go further south. +#77 +You are in a tall E/W canyon. A low tight crawl goes 3 feet north and +seems to open up. +#78 +The canyon runs into a mass of boulders -- dead end. +#79 +The stream flows out through a pair of 1 foot diameter sewer pipes. +It would be advisable to use the exit. +#80 +You are in a maze of twisty little passages, all alike. +#81 +Dead end. +#82 +Dead end. +#83 +You are in a maze of twisty little passages, all alike. +#84 +You are in a maze of twisty little passages, all alike. +#85 +Dead end. +#86 +Dead end. +#87 +You are in a maze of twisty little passages, all alike. +#88 +You are in a long, narrow corridor stretching out of sight to the +west. At the eastern end is a hole through which you can see a +profusion of leaves. +#89 +There is nothing here to climb. Use "up" or "out" to leave the pit. +#90 +You have climbed up the plant and out of the pit. +#91 +You are at the top of a steep incline above a large room. You could +climb down here, but you would not be able to climb up. There is a +passage leading back to the north. +#92 +You are in the Giant Room. The ceiling here is too high up for your +lamp to show it. Cavernous passages lead east, north, and south. On +the west wall is scrawled the inscription, "FEE FIE FOE FOO" [sic]. +#93 +The passage here is blocked by a recent cave-in. +#94 +You are at one end of an immense north/south passage. +#95 +You are in a magnificent cavern with a rushing stream, which cascades +over a sparkling waterfall into a roaring whirlpool which disappears +through a hole in the floor. Passages exit to the south and west. +#96 +You are in the Soft Room. The walls are covered with heavy curtains, +the floor with a thick pile carpet. Moss covers the ceiling. +#97 +This is the Oriental Room. Ancient oriental cave drawings cover the +walls. A gently sloping passage leads upward to the north, another +passage leads SE, and a hands and knees crawl leads east. +#98 +You are following a wide path around the outer edge of a large cavern. +Far below, through a heavy white mist, strange splashing noises can be +heard. The mist rises up through a fissure in the ceiling. The path +hugs the cavern's rim to the NE and south, while another branch forks +west. A round chute with extremely smooth walls angles sharply up +to the southwest. +#99 +You are in an alcove. A small NW path seems to widen after a short +distance. An extremely tight tunnel leads east. It looks like a very +tight squeeze. An eerie light can be seen at the other end. +#100 +You're in a small chamber lit by an eerie green light. An extremely +narrow tunnel exits to the west, and a dark corridor leads NE. +#101 +You're in the Dark-Room. A corridor leading south is the only exit. +#102 +You are in an arched hall. A coral passage once continued up and east +from here, but is now blocked by debris. The air smells of sea water. +#103 +You're in a large room carved out of sedimentary rock. The floor and +walls are littered with bits of shells imbedded in the stone. A +shallow passage proceeds downward, and a somewhat steeper one leads +up. A low hands and knees passage enters from the south. +#104 +You are in a long sloping corridor with ragged sharp walls. +#105 +You are in a cul-de-sac about eight feet across. +#106 +You are in an anteroom leading to a large passage to the east. Small +passages go west and up. The remnants of recent digging are evident. +A sign in midair here says "Cave under construction beyond this point. +Proceed at own risk. [Witt Construction Company]". +#107 +You are in a maze of twisty little passages, all different. +#108 +You are at Witt's End. Passages lead off in *ALL* directions. +#109 +You are in a north/south canyon about 25 feet across. The floor is +covered by white mist seeping in from the north. The walls extend +upward for well over 100 feet. Suspended from some unseen point far +above you, an enormous two-sided mirror is hanging parallel to and +midway between the canyon walls. (The mirror is obviously provided +for the use of the dwarves, who as you know, are extremely vain.) A +small window can be seen in either wall, some fifty feet up. +#110 +You're at a low window overlooking a huge pit, which extends up out of +sight. A floor is indistinctly visible over 50 feet below. Traces of +white mist cover the floor of the pit, becoming thicker to the left. +Marks in the dust around the window would seem to indicate that +someone has been here recently. Directly across the pit from you and +25 feet away there is a similar window looking into a lighted room. A +shadowy figure can be seen there peering back at you. +#111 +A large stalactite extends from the roof and almost reaches the floor +below. You could climb down it, and jump from it to the floor, but +having done so you would be unable to reach it to climb back up. +#112 +You are in a little maze of twisting passages, all different. +#113 +You are at the edge of a large underground reservoir. An opaque cloud +of white mist fills the room and rises rapidly upward. The lake is +fed by a stream, which tumbles out of a hole in the wall about 10 feet +overhead and splashes noisily into the water somewhere within the +mist. The indistinct shape of the opposite shore can be dimly seen +through the mist. The only passage goes back toward the south. +#114 +Dead end. +#115 +You are at the northeast end of an immense room, even larger than the +Giant Room. It appears to be a repository for the "ADVENTURE" +program. Massive torches far overhead bathe the room with smoky +yellow light. Scattered about you can be seen a pile of bottles (all +of them empty), a nursery of young beanstalks murmuring quietly, a bed +of oysters, a bundle of black rods with rusty stars on their ends, and +a collection of brass lanterns. Off to one side a great many dwarves +are sleeping on the floor, snoring loudly. A sign nearby reads: "Do +not disturb the dwarves!" An immense mirror is hanging against one +wall, and stretches to the other end of the room, where various other +sundry objects can be glimpsed dimly in the distance. An unoccupied +telephone booth stands against the north wall. +#116 +You are at the southwest end of the repository. To one side is a pit +full of fierce green snakes. On the other side is a row of small +wicker cages, each of which contains a little sulking bird. In one +corner is a bundle of black rods with rusty marks on their ends. A +large number of velvet pillows are scattered about on the floor. +Beside one of the pillows is a large, dusty, leather-bound volume +with the title "History of Adventure" embossed in pure gold. +A vast mirror stretches off to the northeast, almost reaching the +phone booth. At your feet is a large steel grate, next to which is +a sign which reads, "Treasure Vault. Keys in Main Office." +#117 +You are on one side of a large, deep chasm. A heavy white mist rising +up from below obscures all view of the far side. A SW path leads away +from the chasm into a winding corridor. +#118 +You are in a long winding corridor sloping out of sight in both +directions. +#119 +You are in a secret canyon which exits to the north and east. +#120 +You are in a secret canyon which exits to the north and east. +#121 +You are in a secret canyon which exits to the north and east. +#122 +You are on the northeast side of a deep chasm. A NE path leads away +from the chasm on this side. +#123 +You're in a long east/west corridor. A faint rumbling noise can be +heard in the distance. +#124 +The path forks here. The left fork leads northeast. A dull rumbling +seems to get louder in that direction. The right fork leads southeast +down a gentle slope. The main corridor enters from the west. +#125 +The walls are quite warm here. From the north can be heard a steady +roar, so loud that the entire cave seems to be trembling. Another +passage leads south, and a low crawl goes east. +#126 +You are on the edge of a breathtaking view. Far below you is an +active volcano, from which great gouts of molten lava come surging +out, cascading back down into the depths. The glowing rock fills the +farthest reaches of the cavern with a blood-red glare, giving every- +thing an eerie, macabre appearance. The air is filled with flickering +sparks of ash and a heavy smell of brimstone. The walls are hot to +the touch, and the thundering of the volcano drowns out all other +sounds. Embedded in the jagged roof far overhead are myriad twisted +formations composed of pure white alabaster, which scatter the murky +light into sinister apparitions upon the walls. To one side is a deep +gorge, filled with a bizarre chaos of tortured rock which seems to +have been crafted by the devil himself. An immense river of fire +crashes out from the depths of the volcano, burns its way through the +gorge, and plummets into a bottomless pit far off to your left. To +the right, an immense geyser of blistering steam erupts continuously +from a barren island in the center of a sulfurous lake, which bubbles +ominously. The far right wall is aflame with an incandescence of its +own, which lends an additional infernal splendor to the already +hellish scene. A dark, foreboding passage exits to the south. +#127 +You are in a small chamber filled with large boulders. The walls are +very warm, causing the air in the room to be almost stifling from the +heat. The only exit is a crawl heading west, through which is coming +a low rumbling. +#128 +You are walking along a gently sloping north/south passage lined with +oddly shaped limestone formations. +#129 +You are standing at the entrance to a large, barren room. A sign +posted above the entrance reads: "Caution! Bear in room!" +#130 +You are inside a barren room. The center of the room is completely +empty except for some dust. Marks in the dust lead away toward the +far end of the room. The only exit is the way you came in. +#131 +You are in a maze of twisting little passages, all different. +#132 +You are in a little maze of twisty passages, all different. +#133 +You are in a twisting maze of little passages, all different. +#134 +You are in a twisting little maze of passages, all different. +#135 +You are in a twisty little maze of passages, all different. +#136 +You are in a twisty maze of little passages, all different. +#137 +You are in a little twisty maze of passages, all different. +#138 +You are in a maze of little twisting passages, all different. +#139 +You are in a maze of little twisty passages, all different. +#140 +Dead end. +#141 +You are on a narrow promontory at the foot of a waterfall, which +spurts from an overhead hole in the rock wall and splashes into a +large reservoir, sending up clouds of mist and spray. +Through the thick white mist looms a polished marble slab, to +which is affixed an enormous rusty iron anvil. In golden letters +are written the words: "Whoso Pulleth Out This Sword of This +Stone and Anvil, is Right wise King-Born of All This Mountain." +There is a narrow chimney on the east side of the promontory. +#142 +You are on a narrow shelf above and east of the top of a very steep +chimney. A long smooth granite slide curves down out of sight +to the east. If you go down the slide, you may not be able to +climb back up. +#143 +You are in the private chamber of the Mountain King. Hewn into the +solid rock of the east wall of the chamber is an intricately-wrought +throne of elvish design. There is an exit to the west. +#144 +You are on the east side of the throne room. On the arm of the throne +has been hung a sign which reads "Gone for the day: visiting +sick snake. --M.K." +#145 +You are dragged down, down, into the depths of the whirlpool. +Just as you can no longer hold your breath, you are shot out over +a waterfall into the shallow end of a large reservoir. Gasping +and sputtering, you crawl weakly towards the shore.... +#146 +You are in dense forest, with a hill to one side. The trees appear +to thin out towards the north and east. +#147 +You are at the high point of a wide grassy knoll, partially surrounded +by dense forest. The land rises to the south and east, and drops off +sharply to the north and west. The air smells of sea water. +#148 +You are at the edge of a trackless salt marsh. Tall reeds obscure +the view. +#149 +You're in salt marsh. +#150 +You're in salty marsh. +#151 +You are in salt marsh. +#152 +Dead end. +#153 +You're on a sandy beach at the edge of the open sea. The beach +ends a short distance south and the land rises to a point. To +the north, the beach ends cliffs and broken rocks. +#154 +You are at a jumble of large broken rocks. A gentle path leads up +to the top of the nearby cliffs. A narrow treacherous path +disappears among the rocks at the foot of the cliff. +#155 +You are on a high cliff overlooking the sea. Far below the +rolling breakers smash into a jumble of large broken rocks. +The thunder of the surf is deafening. +#156 +You're at the bottom of the cliff, smashed to smithereens by the +pounding surf. +#157 +You are at Thunder Hole, a funnel shaped cavern opening onto the sea. +The noise of the surf pounding against the outer rocks of the cave is +amplified by the peculiar shape of the cave, causing a thunder-like +booming sound to reverberate throughout the cave. Outside, a narrow +path leads south towards some large rocks. +#158 +You are at the top of some arched steps. On one side is a blank wall +with a tiny door at the base and a shelf overhead. On the other side +a westward passage leads to the sea. +#159 +You are in a low cramped chamber at the back of a small cave. +There is a shelf in the rock wall at about the height of your +shoulder. +#160 +You are on a wide ledge, bounded on one side by a rock wall, +and on the other by a sheer cliff. The only way past is through +a large wrought-iron door. +#161 +You feel dizzy...Everything around you is spinning, expanding, +growing larger.... Dear me! Is the cave bigger or are you smaller? +#162 +You are again overcome by a sickening vertigo, but this time +everything around you is shrinking...Shrinking... +#163 +You are again overcome by a sickening vertigo, but this time +everything is shrinking... I mean, you are growing. This is +terribly confusing! +#164 +You feel dizzy...Everything around you is spinning, expanding, +growing larger.... +#165 +You're at the bottom of the cliff with a broken neck. +#166 +You are at the western tip of the Blue Grotto. A large lake almost +covers the cavern floor, except for where you are standing. Small +holes high in the rock wall to the east admit a dim light. The +reflection of the light from the water suffuses the cavern with +a hazy bluish glow. +#167 +You are on the shore of an underground sea. A high wooden +structure of vast proportions extends out into the water to the +east. The way west is through a wrought-iron door. +#168 +You are on the eastern shore of the Blue Grotto. An ascending +tunnel disappears into the darkness to the SE. +#169 +You are at a high rock on the NE side of a watery chamber at the mouth +of a small brook. An unknown gas bubbles up through the water from +the chamber floor. A bluish light can be seen to the southwest. +#170 +You are in a windy tunnel between two large rooms. +#171 +You are in the Bat Cave. The walls and ceiling are covered with +sleeping bats. The floor is buried by a mass of dry, foul-smelling +guano. The stench is overpowering. Exits to the NW and east. +#172 +You are in a very tight N/S crack. The passage seems to widen to +the south. +#173 +You are in a very tight N/S crack. The passage south is blocked +by a recent cave-in. +#174 +You're in the Cloakroom. This is where the dreaded Wumpus repairs +to sleep off heavy meals. (Adventurers are his favorite dinner!) +Two very narrow passages exit NW and NE. +#175 +You're in a room containing several small climbable pits. Passages +exit to the east and north. +#176 +You are at the bottom of a small featureless pit. +#177 +You are at a high hole in a rock wall. +#178 +The NE passage is blocked by a recent cave-in. +#179 +You are in a sloping muddy defile, next to a tumbling brook. +#180 +You are in a level E/W passage partially blocked by an overhanging +tongue of rock. A steep scramble would take you up over the tongue, +whence continues an upward crawl. +#181 +The dog won't let you pass. +#182 +You're in the Upper Passage, a long level E/W tunnel. +#183 +You are in a star-shaped chamber. Passages exit north, east, south, +and west. +#184 +You are at an elbow in a winding E/W passage. +#185 +Dead end. +#186 +You're at the intersection of two long tunnels. One goes NW, +the other NE. +#187 +You're in a long narrow east-west passage which curves out of sight +at both ends. +#188 +You're in the Rotunda. Corridors radiate in all directions. +There is a telephone booth standing against the north wall. +#189 +You are standing in a telephone booth at the side of a large chamber. +Hung on the wall is a banged-up pay telephone of ancient design. +#190 +You're at the Devil's Chair, a large crystallization shaped like a +seat, at the edge of a black abyss. You can't see the bottom. +An upward path leads away from the abyss. +#191 +You're in a dead-end crack. +#192 +You're on a small gravel beach at the south wall of the Blue Grotto. +A gravelly path leads east. +#193 +You are in the Flower Room. The walls are covered with colorful, +intricate, flowerlike patterns of crystallized gypsum. A hole leads +to the west. +#194 +You are at the end of a short E/W corridor. +#195 +You are looking west from the end of a short E/W corridor. At your +feet is a pile of loose rubble. On your left is a hole into another +chamber. +#196 +You are in an arched hall. The remnants of a now-plugged coral +passage lie to the east. The north wall has partially crumbled, +exposing a large connecting hole to another room. +#197 +You're in the Vestibule, a short east-west passage between two rooms. +#198 +You are in the Fairy Grotto. All around you innumerable stalactites, +arranged in immense colonnades, form elegant arches. On every side +you hear the dripping of water, like the footsteps of a thousand +fairies. A small stream runs from the SW corner. A bright glow +emanates from the south side of the grotto, and a steep passage +descends to the east. +#199 +You have approached the lower end of a steep passage, but it is +just too cold here to hang around, and you aren't properly equipped +to continue. With teeth chattering, you climb back up.... +#200 +You are in the Crystal Palace. An overhead vein of phosphorescent +quartz casts a luminous glow which is reflected by countless chips of +mica embedded in both walls, which consist of some sort of highly +reflective glass, apparently of volcanic origin. A winding path +of yellow sandstone leads west and rises steeply to the east. +#201 +You are following a yellow sandstone path. There is a glow +to the west. +#202 +You are in a very tall chamber whose walls are comprised of many +different rock strata. Layers of red and yellow sandstone +intertwine with bright bands of calcareous limestone in a rainbow- +like profusion of color. The rainbow effect is so real, you +are almost tempted to look for a pot of gold! Poised far over +your head, a gigantic slab, wedged tightly between the north and +south walls, forms a natural bridge across the roof of the chamber. +A trail leads east and west. +#203 +You're in a steeply sloping passage. It is very cold here. +#204 +You are in the Hall of Ice, in the deepest part of the caverns. +During winter, frigid outside air settles here, making this room +extremely cold all year round. The walls and ceilings are covered +with a thick coating of ice. An upward passage exits to the west. +#205 +You are standing on a natural bridge far above the floor of a circular +chamber whose walls are a rainbow of multicolored rock. The bridge +was formed eons ago by a huge slab which fell from the ceiling and +is now jammed between the north and south walls of the chamber. +#206 +You are in a low, wide room below another chamber. A small green +pond fills the center of the room. The lake is apparently spring +fed. A small stream exits through a narrow passage to the north. +A larger passage continues west. +#207 +You are in a tight north/south crawl through a stratum of red +colored rock. The air is damp with mist. +#208 +You are in a tall canyon on the south side of a swift, wide river. +Written in the mud in crude letters are the words: "You Have Found +Lost River." A wide path leads east and west along the bank. A tight +crawl way would take you south out of the canyon. +#209 +You are standing on a large flat rock table at the western end of +Lost River Canyon. Beneath your feet, the river disappears amidst +foam and spray into a large sinkhole. A gentle path leads east +along the river's south shore. Another leads sharply upward along +the river's north side. +#210 +You are at a niche in the canyon wall, far above a raging river. +The air is filled with mist and spray, making it difficult to see +ahead. A downward sloping ledge narrows to the east. The path +to the west is easier. +#211 +The ledge is growing very narrow and treacherous, and falls off almost +vertically. You could go down, but you won't be able to climb back. +#212 +You are standing in a telephone booth at the side of the Repository. +#213 +You're at the east end of a level passage at a hole in the floor. +#214 +You're at the north edge of a dark cove. +#215 +You are in a dry granite basin, worn smooth eons ago by water +swirling down from a now-dry spillway. +#216 +You're in a dry spillway east of and above a smooth rock basin. +#217 +You are in the Winery, a cool dark room which extends some +distance off to the east. +#218 +You are to the east of the Winery, where the room ends in a thicket +of high, sharp, pointed, climbable limestone pinnacles. There is a +narrow ledge just above the top of the spires. If you go up, it +might be difficult to get back down. +#219 +You are in a high-vaulted cavern whose roof rises over fifty +meters to culminate in a series of pointed arches directly over +your head. There are also two low arches to either side, forming +side portals. The whole effect is that of a gothic cathedral. +You can proceed north, south, east, or west. +#220 +You're at the east portal of the Gothic Cathedral. The path leads +east and west. +#221 +You're at the west portal of the Gothic Cathedral. +#222 +You are at the foot of the Altar, an immense, broad stalagmite. +An opening leads south. +#223 +You're on top of an enormous, broad stalagmite. There is a hole +in the ceiling overhead. +#224 +You are in a room the size and shape of a small crypt. A narrow +cut exits east. There is a hole in the floor. +#225 +You are in the Gothic Chapel, a small chamber adjoining the Gothic +Cathedral. A path leads west. +#226 +You are on the floor of the Rainbow Room. In fact, you are spread +*ALL OVER* the floor of the Rainbow Room. +#227 +You are in a dimly lit passage behind Thunder Hole. Etched into +the rock wall are the ominous words: +* "You are approaching the River Styx. * +* Laciate Ogni Speranza Voi Ch'Entrate." * +#228 +You are at the River Styx, a narrow little stream cutting directly +across the passageway. The edge of the stream is littered with sticks +and other debris washed in by a recent rainfall. On the far side +of the river, the passage continues east. +#229 +You're on the east side of the river's sticks. +#230 +You are on a ledge at the northern end of a long N/S crawl. The +ledge is above a large number of sharp vertical limestone spires. +An attempt to climb down could be dangerous, if you get my *point*! +#231 +You are very neatly skewered on the point of a sharp rock. +#232 +You have poled your boat across the calm water. +#233 +You have poled your boat across the dark water. +#234 +You have poled your boat across the Blue Grotto. +#235 +You're at Dante's Rest, on the north side of a yawning dark chasm. +A passage continues west along the chasm's edge. +#236 +You are at the east end of a river bank path in Lost River Canyon. +#237 +The staircase is now unclimbable. +#238 +You're in the caretaker's pantry. +#239 +You are on a small rise overlooking a beautiful bay. In the center +of the bay is the castle of the elves. +#240 +You are on the highest pinnacle of the castle in the bay. +Steps lead down into the garden. +#241 +You are in the outer courtyard of the garden of the elves. +Steps lead up to the tower, and to the west, separating you +from the inner courtyard, is a maze of hedges, living things, +but almost crystalline in their multicolored splendor. +#242 +From the inside the maze looks like a kaleidoscope, with +swatches of color dancing as you move. In this part the colors +are produced by shining red berries on the branches. +#243 +You are surrounded by a tall hedge with sharp iridescent leaves +and metallic orange flowers. +#244 +You are in the center of the living maze. The plants here are +dormant this season, but still carry brilliant yellow leaves. +#245 +Unlike the other areas of the hedge system, this area seems to +have no metallic gleam; nevertheless it is still breathtaking. +The trees and bushes are all variegated shades of green, the +evergreens being a rich dark shade while the seasonal bushes +are a lighter yellowish green, making a startling contrast. +#246 +You are near the edge of the maze. You sample the blueberries +on the bushes. They are delicious. +#247 +You are at the western end of the living maze. Beside the +shrubs forming the walls are tastefully planted beds of +violets and brilliant purple pansies. +To the west is the inner garden. +#248 +You are in the inner garden of the elves. In the center is +a living tree, with shimmering silvery bark, glistening metallic +green leaves, and flowers ripe with nectar. As the nectar falls +to the ground it forms droplets of silver. Around the tree is +a hedge of briars which cannot be crossed. Unfortunately for +adventurers such as you, most of the nectar falls inside the hedge. +The exit is to the east. diff --git a/commands/advent/advent2.txt b/commands/advent/advent2.txt new file mode 100755 index 000000000..5480fc9a1 --- /dev/null +++ b/commands/advent/advent2.txt @@ -0,0 +1,514 @@ +#1 +You're at end of road again. +#2 +You're at hill in road. +#3 +You're inside building. +#4 +You're in valley. +#5 +You're in forest. +#6 +You're in forest. +#7 +You're at slit in streambed. +#8 +You're outside grate. +#9 +You're below the grate. +#10 +You're in cobble crawl. +#11 +You're in Debris Room. +#12 +You are in an awkward sloping east/west canyon. +#13 +You're in Bird Chamber. +#14 +You're at top of small pit. +#15 +You're in Hall of Mists. +#16 +The crack is far too small for you to follow. +#17 +You're on east bank of fissure. +#18 +You're in Nugget of Gold Room. +#19 +You're in Hall of Mt King. +#20 +You are at the bottom of the pit with a broken neck. +#21 +You didn't make it. +#22 +The dome is unclimbable. +#23 +You're at west end of Twopit Room. +#24 +You're in east pit. +#25 +You're in west pit. +#26 +You clamber up the plant and scurry through the hole at the top. +#27 +You are on the west side of the fissure in the Hall of Mists. +#28 +You are in a low N/S passage at a hole in the floor. The hole goes +down to an E/W passage. +#29 +You are in the South Side Chamber. +#30 +You are in the West Side Chamber of the Hall of the Mountain King. +#31 +<$$< +#32 +You can't get by the snake. +#33 +You're at "Y2". +#34 +You are in a jumble of rock, with cracks everywhere. +#35 +You're at window on pit. +#36 +You're in dirty passage. +#37 +You are on the brink of a small clean climbable pit. +#38 +You are in the bottom of a small pit with a little stream. +#39 +You're in dusty rock room. +#40 +You have crawled through a very low wide passage parallel. +#41 +You're at west end of Hall of Mists. +#42 +You are in a maze of twisty little passages, all alike. +#43 +You are in a maze of twisty little passages, all alike. +#44 +You are in a maze of twisty little passages, all alike. +#45 +You are in a maze of twisty little passages, all alike. +#46 +Dead end. +#47 +Dead end. +#48 +Dead end. +#49 +You are in a maze of twisty little passages, all alike. +#50 +You are in a maze of twisty little passages, all alike. +#51 +You are in a maze of twisty little passages, all alike. +#52 +You are in a maze of twisty little passages, all alike. +#53 +You are in a maze of twisty little passages, all alike. +#54 +Dead end. +#55 +You are in a maze of twisty little passages, all alike. +#56 +Dead end. +#57 +You're at brink of pit. +#58 +Dead end. +#59 +You have crawled through a very low wide passage. +#60 +You're at east end of Long Hall. +#61 +You're at west end of Long Hall. +#62 +You are at a crossover of a high N/S passage and a low E/W one. +#63 +Dead end. +#64 +You're at Complex Junction. +#65 +You are in Bedquilt. +#66 +You're in Swiss Cheese Room. +#67 +You're at east end of Twopit Room. +#68 +You're in Slab Room. +#69 +You are in a secret N/S canyon above a large room. +#70 +You are in a secret N/S canyon above a sizable passage. +#71 +You're at junction of three secret canyons. +#72 +You are in a large low room. Crawls lead north, NE, and SW. +#73 +Dead end crawl. +#74 +You're in secret E/W canyon above tight canyon. +#75 +You are at a wide place in a very tight N/S canyon. +#76 +The canyon here becomes too tight to go further south. +#77 +You are in a tall E/W canyon. +#78 +The canyon runs into a mass of boulders -- dead end. +#79 +The stream flows out through a pair of 1 foot diameter sewer pipes. +#80 +You are in a maze of twisty little passages, all alike. +#81 +Dead end. +#82 +Dead end. +#83 +You are in a maze of twisty little passages, all alike. +#84 +You are in a maze of twisty little passages, all alike. +#85 +Dead end. +#86 +Dead end. +#87 +You are in a maze of twisty little passages, all alike. +#88 +You're in narrow corridor. +#89 +There is nothing here to climb. Use "up" or "out" to leave the pit. +#90 +You have climbed up the plant and out of the pit. +#91 +You're at steep incline above large room. +#92 +You're in Giant Room. +#93 +The passage here is blocked by a recent cave-in. +#94 +You are at one end of an immense north/south passage. +#95 +You're in cavern with waterfall. +#96 +You're in Soft Room. +#97 +You're in Oriental Room. +#98 +You're in Misty Cavern. +#99 +You're in Alcove. +#100 +You're in Plover Room. +#101 +You're in Dark-Room. +#102 +You're in Arched Hall. +#103 +You're in Shell Room. +#104 +You are in a long sloping corridor with ragged sharp walls. +#105 +You are in a cul-de-sac about eight feet across. +#106 +You're in Anteroom. +#107 +You are in a maze of twisty little passages, all different. +#108 +You're at Witt's End. +#109 +You're in Mirror Canyon. +#110 +You're at window on pit. +#111 +You're at top of stalactite. +#112 +You are in a little maze of twisting passages, all different. +#113 +You're at Reservoir. +#114 +Dead end. +#115 +You're at NE end. +#116 +You're at SW end. +#117 +You're on SW side of chasm. +#118 +You're in sloping corridor. +#119 +You are in a secret canyon which exits to the north and east. +#120 +You are in a secret canyon which exits to the north and east. +#121 +You are in a secret canyon which exits to the north and east. +#122 +You're on NE side of chasm. +#123 +You're in corridor. +#124 +You're at fork in path. +#125 +You're at junction with warm walls. +#126 +You're at breath-taking view. +#127 +You're in Chamber of Boulders. +#128 +You're in Limestone Passage. +#129 +You're in front of Barren Room. +#130 +You're in Barren Room. +#131 +You are in a maze of twisting little passages, all different. +#132 +You are in a little maze of twisty passages, all different. +#133 +You are in a twisting maze of little passages, all different. +#134 +You are in a twisting little maze of passages, all different. +#135 +You are in a twisty little maze of passages, all different. +#136 +You are in a twisty maze of little passages, all different. +#137 +You are in a little twisty maze of passages, all different. +#138 +You are in a maze of little twisting passages, all different. +#139 +You are in a maze of little twisty passages, all different. +#140 +Dead end. +#141 +You're at Sword Point. +#142 +You're at top of slide. +#143 +You're at entrance to Throne Room. +#144 +You're on east side of Throne Room. +#145 +<$$< +#146 +You're in forest. +#147 +You're on grassy knoll. +#148 +You are at the edge of a trackless salt marsh. Tall reeds obscure +the view. +#149 +You're in salt marsh. +#150 +You're in salty marsh. +#151 +You are in salt marsh. +#152 +Dead end. +#153 +You're on sandy beach. +#154 +You're at broken rocks. +#155 +You're at Ocean Vista. +#156 +You're at the bottom of the cliff. +#157 +You're at Thunder Hole. +#158 +You're at top of steps in back of Thunder Hole. +#159 +You're in cramped chamber. +#160 +You're at ledge by wrought-iron door. +#161 +You feel dizzy...Everything around you is spinning, expanding, +growing larger.... Dear me! Is the cave bigger or are you smaller? +#162 +You are again overcome by a sickening vertigo, but this time +everything around you is shrinking...Shrinking... +#163 +You are again overcome by a sickening vertigo, but this time +everything is shrinking... I mean, you are growing. This is +terribly confusing! +#164 +You feel dizzy...Everything around you is spinning, expanding, +growing larger.... +#165 +You're at the bottom of the cliff with a broken neck. +#166 +You're at west wall of Blue Grotto. +#167 +You're at underground sea. +#168 +You're on east side of the Blue Grotto. +#169 +You're in Bubble Chamber. +#170 +You are in a windy tunnel between two large rooms. +#171 +You're in Bat Cave. +#172 +You are in a very tight N/S crack. +#173 +You are in a very tight N/S crack. +#174 +You're in the Cloakroom. +#175 +You're in a room containing several small climbable pits. +#176 +You are at the bottom of a small featureless pit. +#177 +You are at a high hole in a rock wall. +#178 +The NE passage is blocked by a recent cave-in. +#179 +You are in a sloping muddy defile, next to a tumbling brook. +#180 +You're at Tongue of Rock. +#181 +The dog won't let you pass. +#182 +You're in the Upper Passage, a long level E/W tunnel. +#183 +You're in Star Chamber. +#184 +You are at an elbow in a winding E/W passage. +#185 +Dead end. +#186 +You're at the intersection of two long tunnels. +#187 +You're in a long narrow east-west passage. +#188 +You're in Rotunda. +#189 +You're in phone booth. +#190 +You're at Devil's Chair. +#191 +You're in a dead-end crack. +#192 +You're on gravel beach. +#193 +You're in Flower Room. +#194 +You are at east end of short E/W corridor. +#195 +You are at east end of short E/W corridor. +#196 +You're in Arched Hall. +#197 +You're in the Vestibule, a short east-west passage between two rooms. +#198 +You're in the Fairy Grotto. +#199 +You have approached the lower end of a steep passage. +#200 +You're in the Crystal Palace. +#201 +You are following a yellow sandstone path. +#202 +You're in the Rainbow Room. +#203 +You're in a steeply sloping passage. It is very cold here. +#204 +You're in the Hall of Ice. +#205 +You are over the Rainbow (Room). +#206 +You're in Green Lake Room. +#207 +You're in red rock crawl. +#208 +You're on south side of Lost River Canyon. +#209 +You're at end of Lost River Canyon. +#210 +You're at niche in ledge above Lost River. +#211 +A very narrow and treacherous ledge. +#212 +You're in phone booth. +#213 +You're at the east end of a level passage at a hole in the floor. +#214 +You're in dark cove. +#215 +You're in dry basin. +#216 +You're in old spillway. +#217 +You're in the Winery. +#218 +You're at limestone pinnacles. +#219 +You're in Gothic Cathedral. +#220 +You're at the east portal of the Gothic Cathedral. +#221 +You're at the west portal of the Gothic Cathedral. +#222 +You are at the foot of the Altar, an immense, broad stalagmite. +#223 +You're on top of stalagmite. +#224 +You're in the Crypt. +#225 +You're in Gothic Chapel. +#226 +You are on the floor of the Rainbow Room. +#227 +You are at approach to River Styx. +#228 +You're at the River Styx. +#229 +You're on the east side of the river's sticks. +#230 +You're on ledge above limestone pinnacles. +#231 +You are very neatly skewered on the point of a sharp rock. +#232 +You have poled your boat across the calm water. +#233 +You have poled your boat across the dark water. +#234 +You have poled your boat across the Blue Grotto. +#235 +You're at Dante's Rest. +#236 +You're at east end of Lost River Canyon. +#237 +The staircase is now unclimbable. +#238 +You're in the caretaker's pantry. +#239 +You're on a small rise over the bay. +#240 +You are on the highest pinnacle of the castle in the bay. +Steps lead down into the garden. +#241 +You are in the outer courtyard of the elves. +#242 +You are in the living maze. There are red berries here. +#243 +You are surrounded by a tall hedge with sharp iridescent leaves +and metallic orange flowers. +#244 +You are in the center of the living maze. The plants here are +dormant this season, but still carry brilliant yellow leaves. +#245 +Unlike the other areas of the hedge system, this area seems to +have no metallic gleam; nevertheless it is still breathtaking. +The trees and bushes are all variegated shades of green, the +evergreens being a rich dark shade while the seasonal bushes +are a lighter yellowish green, making a startling contrast. +#246 +You are near the edge of the maze. You sample the blueberries +on the bushes. They are delicious. +#247 +You are at the western end of the living maze. Beside the +shrubs forming the walls are tastefully planted beds of +violets and brilliant purple pansies. +To the west is the inner garden. +#248 +You're in the inner courtyard of the elves. diff --git a/commands/advent/advent3.txt b/commands/advent/advent3.txt new file mode 100755 index 000000000..b31aca634 --- /dev/null +++ b/commands/advent/advent3.txt @@ -0,0 +1,539 @@ +#1 +#2 +/Brass lantern +/There is a shiny brass lamp nearby. +/There is a lamp shining nearby. +/ +#3 +/*Grate +/The grate is locked. +/The grate is open. +/ +#4 +/Wicker cage +/There is a small wicker cage discarded nearby. +/ +#5 +/Black rod +/A three foot black rod with a rusty star on an end lies nearby. +/ +#6 +/Black rod +/A three foot black rod with a rusty mark on an end lies nearby. +/ +#7 +/*Steps +/Rough stone steps lead down the pit. +/Rough stone steps lead up the dome. +/ +#8 +#9 +/Wooden pole +/A wooden pole is lying nearby. +/A wooden pole has been stuck in the mud here. +/ +#10 +/Velvet pillow +/A small velvet pillow lies on the floor. +/ +#11 +/*Snake +/A huge green fierce snake bars the way! +/<$$< (Chased away) +/ +#12 +/*Fissure +/<$$< +/A crystal bridge now spans the fissure. +/The crystal bridge has vanished! +/ +#13 +/*Stone tablet +/A massive stone tablet imbedded in the wall reads: +"Congratulations on bringing light into the Dark-Room!" +/ +#14 +/Giant clam >grunt!< +/There is an enormous clam here with its shell tightly closed. +/ +#15 +/Giant oyster >groan!< +/There is an enormous oyster here with its shell tightly closed. +/Interesting. There seems to be something written on the underside of +the oyster. +/ +#16 +/"Spelunker Today" +/There are a few recent issues of "Spelunker Today" magazine here. +/ +#17 +#18 +#19 +/Tasty food +/There is food here. +/ +#20 +/Small bottle +/There is a small bottle here. +/There is an empty bottle here. +/There is a small bottle here. +/There is a broken bottle here. +/There is a small bottle here. +/ +#21 +/*Chasm - troll bridge +/A rickety wooden bridge extends across the chasm, vanishing into the +mist. A sign posted on the bridge reads, "Stop! Pay troll!" +/The wreckage of a bridge (and a dead bear) can be seen at the bottom +of the chasm. +/ +#22 +/*Chasm2 & decrepit natural bridge +/A decrepit natural bridge spans the chasm. A message scrawled into +the rock wall reads: "Bridge out of repair. Maximum load: 35 Foonts." +/The remnants of a natural bridge partially overhang the chasm. +/ +#23 +/*Mirror +/<$$< +/ +#24 +/*plant +/There is a tiny little plant in the pit, murmuring "Water, water, ..." +/The plant spurts into furious growth for a few seconds. +/There is a 12-foot-tall beanstalk stretching up out of the pit, +bellowing "Water!! Water!!" +/The plant grows explosively, almost filling the bottom of the pit. +/There is a gigantic beanstalk stretching all the way up to the hole. +/You've over-watered the plant! It's shriveling up! It's, it's... +/ +#25 +/*Phony plant (seen in twopit room only when tall enough) +/<$$< +/The top of a 12-foot-tall beanstalk is poking out of the west pit. +/There is a huge beanstalk growing out of the west pit up to the hole. +/ +#26 +/*Stalactite +/<$$< +/ +#27 +/*shadowy figure +/The shadowy figure seems to be trying to attract your attention. +/ +#28 +/Dwarf's axe +/There is a little axe here. +/There is a little axe lying beside the bear. +/There is a little axe lying beside the Wumpus. +/There is a little axe lying beside the dog. +/ +#29 +/*Cave drawings +/<$$< +/ +#30 +/*pirate +/<$$< +/ +#31 +/*dragon +/A huge green fierce dragon bars the way! +/Congratulations! You have just vanquished a dragon with your bare +hands! (Unbelievable, isn't it?) +/The body of a huge green dead dragon is lying off to one side. +/ +#32 +#33 +/*Troll +/A burly troll stands by the bridge and insists you throw him a +treasure before you may cross. +/The troll steps out from beneath the bridge and blocks your way. +/<$$< (Chased away) +/ +#34 +/*phony troll +/The troll is nowhere to be seen. +/ +#35 +/<$$< (Bear uses rtext 141) +/There is a ferocious cave bear eying you from the far end of the room! +/There is a gentle cave bear sitting placidly in one corner. +/There is a contented-looking bear wandering about nearby. +/<$$< (Dead) +/ +#36 +/*Message in second maze +/There is a message scrawled in the dust in a flowery script, reading: +"This is not the maze where the pirate leaves his treasure chest." +/ +#37 +/*Volcano and/or geyser +/<$$< +/ +#38 +/*Vending machine +/There is a massive vending machine here. The instructions on it +read: "Insert coins to receive fresh batteries." +/There is a massive vending machine here. +/ +#39 +/Batteries +/There are fresh batteries here. +/Some worn-out batteries have been discarded nearby. +/Some worn-out batteries have been discarded nearby. +/ +#40 +/*Carpet and/or moss +/<$$< +/ +#41 +/*Rusty door +/The way north is barred by a massive, rusty, iron door. +/The way north leads through a massive, rusty, iron door. +/ +#42 +/*Tiny door +/The only way past the wall is through a tiny locked door. +/The only way past the wall is through a tiny open door. +/ +#43 +/*Tiny door-2 +/The door is locked. +/The door is open. +/ +#44 +/*Phone booth door +/<$$< +/ +#45 +#46 +/Beautiful flowers +/There are some beautiful flowers here! +/On the other side of the room a swarm of bees eagerly buzzes over +a bunch of fresh flowers. +/ +#47 +/Silken cloak +/There is a silken cloak here! +/<$$< (wearing cloak) +/A lovely silken cloak lies partially buried under a pile of +loose rocks. +/ +#48 +/Wooden boat +/There is a small wooden boat here. +/You are in a wooden boat. +/ +#49 +/*Sticks at Styx +/<$$< +/ +#50 +/Large gold nugget +/There is a large sparkling nugget of gold here! +/ +#51 +/Several diamonds +/There are diamonds here! +/ +#52 +/Silver horn +/There is a silver horn here! +/ +#53 +/Precious jewelry +/There is precious jewelry here! +/<$$< (wearing jewelry) +/ +#54 +/Rare coins +/There are many coins here! +/ +#55 +/Treasure chest +/The pirate's treasure chest is here! +/ +#56 +/Golden eggs +/There is a large nest here, full of golden eggs! +/The nest of golden eggs has vanished! +/Done! +/ +#57 +/Jeweled trident +/There is a jewel-encrusted trident here! +/ +#58 +/Ming vase +/There is a delicate, precious, ming vase here! +/The vase is now resting, delicately, on a velvet pillow. +/The floor is littered with worthless shards of pottery. +/The ming vase drops with a delicate crash. +/ +#59 +/Egg-sized emerald +/There is an emerald here the size of a plover's egg! +/ +#60 +/Platinum pyramid +/There is a platinum pyramid here, 8 inches on a side! +/ +#61 +/Glistening pearl +/Off to one side lies a glistening pearl! +/ +#62 +/Persian rug +/There is a persian rug spread out on the floor! +/The dragon is sprawled out on a persian rug!! +/ +#63 +/Rare spices +/There are rare spices here! +/ +#64 +/Golden chain +/There is a golden chain lying in a heap on the floor! +/The bear is locked to the wall with a golden chain! +/There is a golden chain locked to the wall! +/ +#65 +/Gleaming sword +/There is a gleaming sword here! +/A gleaming sword is stuck into the anvil! +/You grasp the sword's handle and give a mighty heave, but with a +loud clang the sword blade shatters into several fragments. +/Rusty shards of a elven sword lie scattered about. +/A very clean sword is stuck into the anvil! +/An oily sword is stuck into the anvil. +/ +#66 +/Elfin crown +/An ancient crown of elfin kings lies here! +/<$$< (wearing crown) +/ +#67 +/Ruby slippers +/There is a pair of ruby slippers here. +/<$$< (wearing slippers) +/There is a pair of ruby slippers here. +/ +#68 +/Delicate lyre +/There is a delicate lyre here! +/ +#69 +/Star sapphire +/There is a star sapphire here! +/ +#70 +/Holy Grail +/There is an ornate silver chalice here! +/ +#71 +/Oaken cask +/There is an oaken cask here. +/There is an empty oaken cask here. +/There is an oaken cask here. +/<$$< (Unused) +/There is an oaken cask here. +/ +#72 +/Golden ring +/There is a small gold ring here. +/<$$< (wearing ring) +/On the Wumpus' finger is a small gold ring. +/ +#73 +/Four-leafed clover +/There is a four-leafed clover here! +/<$$< (wearing clover) +/ +#74 +/Gold tree +/There is a gold statue of a tree here. +/ +#75 +/Silver droplet +/There is a single droplet of silver on the ground here. +/ +#76 +#77 +#78 +#79 +#80 +#81 +/Clear water +/ +#82 +/Cool water +/ +#83 +/Thick, black oil +/ +#84 +/Thick, black oil +/ +#85 +/Vintage wine +/ +#86 +/Vintage wine +/ +#87 +/*Bumblebees +/<$$< +/Some bumblebees are swarming around a bunch of fresh flowers. +/ +#88 +/*Hollow wall +/Your footsteps echo hollowly throughout the chamber. +/<$$< +/ +#89 +/*Wall with safe +/A steel safe is embedded in the wall. +/ +#90 +/Tiny brass key +/There is a tiny brass key here. +/There is a tiny brass key on the shelf. +/ +#91 +/*Anvil +/<$$< +/ +#92 +/*rocks on cloak +/<$$< (ON CLOAK) +/<$$< (AFTER ROCK SLIDE) +/ +#93 +/*telephone booth +/The telephone booth is empty. + +The phone is ringing. +/The phone booth is occupied by a gnome. He is talking excitedly +to someone at the other end. +/The telephone booth is empty. +/<$$< +/ +#94 +/*Telephone +/The phone is ringing. +/The telephone is out of order. +/The telephone is out of order. It is badly dented. +/<$$< +/ +#95 +/Lead slugs +/There are some lead slugs here! +/ +#96 +/Sweet honeycomb +/There is a sweet honeycomb here! +/ +#97 +/*Beehive +/There is an active beehive nearby. The bees hum protectively +around the hive. +/There is an empty beehive nearby. +/ +#98 +/*Black dog +/A hideous black dog bares his teeth and growls at your approach. +/Nearby, a large black dog is in a deep slumber. +/ +#99 +/*Dreaded Wumpus +/In the corner, a Wumpus is sleeping peacefully. +/A sleepy Wumpus is ambling towards you. He wants to invite you to +dinner. He wants you to *be* the dinner! +/The Wumpus is still on your trail! And he's getting closer!! +/The Wumpus is only a few steps behind you! All this exercise is +making him veerrrrry hungry! +/The Wumpus almost has you in his grasp! You can feel his hot breath +on your neck! +/"Chomp, chomp." Crunch! Chew! Slurp! Smack! Yum!!! +/Nearby is the smashed body of a defunct Wumpus. +/ +#100 +#101 +/Little bird in cage +/A cheerful little bird is sitting here singing. +/There is a little bird in the cage. +/ +#102 +/Set of keys +/There are some keys on the ground here. +/ +#103 +/*Fountain +/There is a fountain of sparkling vintage wine here! +/ +#104 +/*Bats & guano in bat-cave +/<$$< +/ +#105 +/*gnome in phone booth +/<$$< +/ +#106 +/Colored mushrooms +/There are some oddly-colored mushrooms here. +/ +#107 +/Tiny cakes +/There are some tiny cakes on the shelf. +/ +#108 +/Leather Sack +/There is a leather sack here. +/ +#109 +#110 +/Rare book +/There is a dusty, leather-bound volume here. +/ +#111 +/Rare book +/There is a dusty, leather-bound volume here. +/ +#112 +/Steel wall-safe +/The safe door is locked. +/The safe door is open. +/ +#113 +/Faded poster +/Taped to the wall is a faded poster. +/There is a faded poster here. +/ +#114 +/Whiskbroom +/There is a small whiskbroom here. +/ +#115 +/*Carving on dusty rocks +/<$$< +/<$$< +/ +#116 +/*Billboard +/ +/ +#117 +#118 +/Small metal canister +/There is a heavy, grey, metal canister here. +/ +#119 +/Glowing stone +/Nearby, a strange, greenish stone is glowing brightly. +/ +#120 +/Quartz sphere +/There is a polished sphere of pure quartz here! +/ diff --git a/commands/advent/advent4.txt b/commands/advent/advent4.txt new file mode 100755 index 000000000..0900374fb --- /dev/null +++ b/commands/advent/advent4.txt @@ -0,0 +1,1068 @@ +#1 +Somewhere nearby is Colossal Cave, where others have found fortunes in +treasure and gold, though it is rumored that some who enter are never +seen again. Magic is said to work in the cave. I will be your eyes +and hands. Direct me with natural English commands. I should warn +you that I look at all the letters of each word. Also you +should enter "Northeast" as "NE" to distinguish it from "North". +(Should you get stuck, type "HELP" or "?" for some general hints.) +Good Luck! +- - - - +#2 +A little dwarf with a big knife blocks your way. +#3 +A little dwarf just walked around a corner, saw you, threw a little +axe at you which missed, cursed, and ran away. +#4 +There is a threatening little dwarf in the room with you! +#5 +One sharp nasty knife is thrown at you! +#6 +None of them hit you! +#7 +One of them gets you! +#8 +A hollow voice says "Plugh". +#9 +There is no way to go that direction. +#10 +I am unsure how you are facing. Use compass points or nearby objects. +#11 +I don't know in from out here. Use compass points or name something +in the general direction you want to go. +#12 +I don't know how to apply that word here. +#13 +I don't understand that! +#14 +I'm game. Would you care to explain how? +#15 +Sorry, but I am not allowed to give more detail. I will repeat the +long description of your location. +#16 +It is now pitch dark. If you proceed you will likely fall into a pit. +#17 +If you prefer, simply type "W" rather than "West". +#18 +Are you trying to catch the bird? +#19 +The bird is frightened right now and you cannot catch it no matter +what you try. Perhaps you might try later. +#20 +Are you trying to somehow deal with the snake? +#21 +You can't kill the snake, or drive it away, or avoid it, or anything +like that. There is a way to get by, but you don't have the necessary +resources right now. +#22 +Do you really want to quit now? +#23 +You fell into a pit and broke every bone in your body! +#24 +You are already carrying it! +#25 +You can't be serious! +#26 +The bird was unafraid when you entered, but as you approach it becomes +disturbed and you cannot catch it. +#27 +You can catch the bird, but you cannot carry it. +#28 +There is nothing here with a lock! +#29 +You aren't carrying it! +#30 +The little bird attacks the green snake, and in an astounding flurry +drives the snake away. +#31 +You have no keys! +#32 +It has no lock. +#33 +I don't know how to lock or unlock such a thing. +#34 +It was already locked. +#35 +The grate is now locked. +#36 +The grate is now unlocked. +#37 +It was already unlocked. +#38 +You have no source of light. +#39 +Your lamp is now on. +#40 +Your lamp is now off. +#41 +There is no way to get past the bear to unlock the chain, which is +probably just as well. +#42 +Nothing happens. +#43 +Where? +#44 +There is nothing here to attack. +#45 +The little bird is now dead. Its body disappears. +#46 +Attacking the snake both doesn't work and is very dangerous. +#47 +You killed a little dwarf. +#48 +You attack a little dwarf, but he dodges out of the way. +#49 +With what? Your bare hands? +#50 +Good try, but that is an old worn-out magic word. +#51 +To move, try words like FOREST, BUILDING, DOWNSTREAM, IN, EAST, NORTH, +UP, OR DOWN. Saying a place name may take you there, as may some +magic words. You can say TAKE LAMP or DROP LAMP or PUT LAMP IN SACK. +Some objects have side effects; for instance, the rod scares the bird. +You can take or drop multiple objects by saying TAKE BOOK AND LAMP, +etc. To list what you are currently holding, say INVENTORY, or just I. +To reprint the detailed description of where you are, say LOOK +or L. You can also LOOK IN or LOOK AT appropriate objects. Other +useful commands are HEALTH, QUIT, SCORE, BRIEF, TERSE, UNBRIEF, +UNTERSE, SAVE, and RESTORE. The latter two save or restore your +current position. To get full credit for a treasure, you must have +left it **safely** in the building. Some non-treasure items are best +left near where you find them. There are clues for various problems +lying about the cave: look and listen. If you seem to have found +all the treasures, keep looking around a while, something else +may happen to you (for a bonus!) +(The word "give" doesn't exist, but you can say FEED BONE TO DOG or +THROW BONE AT DOG. Also, you can use conveyances or animals(!) +by TAKEing and DROPing them.) +#52 +It misses! +#53 +It gets you! +#54 +Ok +#55 +You can't unlock the keys. +#56 +You have crawled around in some little holes and wound up back in the +main passage. +#57 +I don't know where the cave is, but hereabouts no stream can run on +the surface for long. I would try the stream. +#58 +I need more detailed instructions to do that. +#59 +I can only tell you what you see as you move about and manipulate +things. I cannot tell you where remote things are. +#60 +I can't make any sense out of that. +#61 +What? +#62 +Are you trying to get into the cave? +#63 +The grate is very solid and has a hardened steel lock. You cannot +enter without a key, and there are no keys nearby. I would recommend +looking elsewhere for the keys. +#64 +The trees of the forest are large hardwood oak and maple, with an +occasional grove of pine or spruce. There is quite a bit of under- +growth, largely birch and ash saplings plus nondescript bushes of +various sorts. This time of year visibility is quite restricted by +all the leaves, but travel is quite easy if you detour around the +spruce and berry bushes. +#65 + + + Welcome to ADVENTURE! + + + Original development by Willie Crowther. + + Conversion to BDS C by J. R. Jaeger + + Unix standardization by Jerry D. Pohl. + + Upgraded to version 6.6 by Robert R. Hall. + + + Would you like instructions? + +#66 +Digging without a shovel is quite impractical. Even with a shovel +progress is unlikely. +#67 +Blasting requires dynamite. +#68 +I'm as confused as you are. +#69 +Mist is a white vapor, usually water, seen from time to time in +caverns. It can be found anywhere but is frequently a sign of a deep +pit leading down to water. +#70 +Your feet are now wet. +#71 +I think I just lost my appetite. +#72 +Thank you, it was delicious! +#73 +You have taken a drink from the stream. The water tastes strongly of +minerals, but is not unpleasant. It is extremely cold. +#74 +The bottle is now empty. +#75 +Rubbing the electric lamp is not particularly rewarding. Anyway, +nothing exciting happens. +#76 +Peculiar. Nothing unexpected happens. +#77 +Your bottle is empty and the ground is wet. +#78 +You can't pour that. +#79 +Watch it! +#80 +Which way? +#81 +Oh dear, you seem to have gotten yourself killed. I might be able to +help you out, but I've never really done this before. Do you want me +to try to reincarnate you? +#82 +All right. But don't blame me if something goes wr...... +* --- POOF!! --- * +You are engulfed in a cloud of orange smoke. Coughing and gasping, +you emerge from the smoke and find.... + +#83 +You clumsy oaf, you've done it again! I don't know how long I can +keep this up. Do you want me to try reincarnating you again? +#84 +Okay, now where did I put my orange smoke?.... >POOF!< +Everything disappears in a dense cloud of orange smoke. + +#85 +Now you've really done it! I'm out of orange smoke! You don't expect +me to do a decent reincarnation without any orange smoke, do you? +#86 +Okay, if you're so smart, do it yourself! I'm leaving! +#87 +#88 +#89 +#90 +>>> Messages 81 thru 90 are reserved for "Obituaries". <<< +#91 +Sorry, but I no longer seem to remember how it was you got here. +#92 +It's too heavy. You'll have to drop something first. +#93 +You can't go through a locked steel grate! +#94 +I believe what you want is right here with you. +#95 +You don't fit through a two-inch slit! +#96 +I respectfully suggest you go across the bridge instead of jumping. +#97 +There is no way across the fissure. +#98 +You're not carrying anything. +#99 +You are currently holding the following: +#100 +It's not hungry (it's merely pining' for the fjords). Besides, you +have no bird seed. +#101 +The snake has now devoured your bird. +#102 +There's nothing here it wants to eat (except perhaps you). +#103 +You fool, dwarves eat only coal! Now you've made him *REALLY* mad!! +#104 +Your cask is empty and the ground is soaked. +#105 +You aren't carrying them! +#106 +I don't need to, thank you. +#107 +The ground is wet and the air is smelly. +#108 +Your lamp is now on, but the glare from the walls is absolutely +blinding. If you proceed you are likely to fall into a pit. +#109 +You haven't any matches. +#110 +Don't be ridiculous! +#111 +The door is extremely rusty and refuses to open. +#112 +The plant indignantly shakes dry its leaves and asks, "Water?" +#113 +The hinges are quite thoroughly rusted now and won't budge. +#114 +The oil has freed up the hinges so that the door will now move, +although it requires some effort. +#115 +The plant has exceptionally deep roots and cannot be pulled free. +#116 +The dwarves' knives vanish as they strike the walls of the cave. +#117 +Something you're carrying won't fit through the tunnel with you. +You'd best take inventory and drop something. +#118 +You can't fit this five-foot clam through that little passage! +#119 +You can't fit this five-foot oyster through that little passage! +#120 +I advise you to put down the clam before opening it. >Strain!< +#121 +I advise you to put down the oyster before opening it. >Wrench!< +#122 +You don't have anything strong enough to open the clam. +#123 +You don't have anything strong enough to open the oyster. +#124 +A glistening pearl falls out of the clam and rolls away. Goodness, +this must really be an oyster. (I never was very good at identifying +bivalves.) Whatever it is, it has now snapped shut again. +#125 +The oyster creaks open, revealing nothing but oyster inside. It +promptly snaps shut again. +#126 +You have crawled around in some little holes and found your way +blocked by a recent cave-in. You are now back in the main passage. +#127 +There are faint rustling noises from the darkness behind you. +#128 +Out from the shadows behind you pounces a bearded pirate! "Har, har," +he chortles, "I'll just take all this booty and hide it away with me +chest deep in the maze!" He snatches your treasure and vanishes into +the gloom. +#129 +A sepulchral voice reverberating through the cave, says, "Cave closing +soon. All adventurers exit immediately through Main Office." +#130 +A mysterious recorded voice groans into life and announces: + "This exit is closed. Please leave via Main Office." + +#131 +It looks as though you're dead. Well, seeing as how it's so close to +closing time anyway, I think we'll just call it a day. + +#132 +The sepulchral voice intones, "The cave is now closed." As the echoes +fade, there is a blinding flash of light (and a small puff of orange +smoke). . . . As your eyes refocus, you look around and find... + +#133 +There is a loud explosion, and a twenty-foot hole appears in the far +wall, burying the dwarves in the rubble. You march through the hole +and find yourself in the Main Office, where a cheering band of +friendly elves carry the conquering adventurer off into the sunset. + +#134 +There is a loud explosion, and a twenty-foot hole appears in the far +wall, burying the snakes in the rubble. A river of molten lava pours +in through the hole, destroying everything in its path, including you! + +#135 +There is a loud explosion, and you are suddenly splashed across the +walls of the room. + +#136 +The resulting ruckus has awakened the dwarves. There are now several +threatening little dwarves in the room with you! Most of them throw +knives at you! All of them get you! + +#137 +Oh, leave the poor unhappy bird alone. +#138 +I dare say whatever you want is around here somewhere. +#139 +I'm not sure what you mean by "Stop". Use "Quit" if you want to give +up. +#140 +You can't get there from here. +#141 +You are being followed by a very large, tame bear. +#142 +- *** THE HISTORY OF ADVENTURE (ABRIDGED) *** - +- ** By Ima Wimp ** - +ADVENTURE was originally developed by William Crowther, and later +substantially rewritten and expanded by Don Woods at Stanford Univ. +According to legend, Crowther's original version was modelled on an +a real cavern, called Colossal Cave, which is a part of Kentucky's +Mammoth Caverns. That version of the game included the main maze +and a portion of the third-level (Complex Junction - Bedquilt - +Swiss Cheese rooms, etc.), but not much more. +Don Woods and some others at Stanford later rewrote portions of +the original program, and greatly expanded the cave. That version +of the game is recognizable by the maximum score of 350 points. +Some major additions were done by David Long while at the University +of Chicago, Graduate School of Business. Long's additions include the +seaside entrance and all of the cave on the "far side" of Lost River +(Rainbow Room - Crystal Palace - Blue Grotto, etc.). +The castle problem was added in late 1984 by an anonymous writer. +Thanks are owed to Roger Matus and David Feldman, both of U. of C., +for several suggestions, including the Rainbow Room, the telephone +booth and the fearsome Wumpus. Most thanks (and apologies) +go to Thomas Malory, Charles Dodgson, the Grimm Brothers, Dante, +Homer, Frank Baum and especially Anon., the real authors of ADVENTURE. +#143 +Do you indeed wish to quit now? +#144 +There is nothing here with which to fill the vase. +#145 +The sudden change in temperature has delicately shattered the vase. +#146 +It is beyond your power to do that. +#147 +I don't know how. +#148 +It is too far up for you to reach. +#149 +You killed a little dwarf. The body vanishes in a cloud of greasy +black smoke. +#150 +The shell is very strong and is impervious to attack. +#151 +What's the matter, can't you read? Now you'd best start over. +#152 +The axe bounces harmlessly off the dragon's thick scales. +#153 +The dragon looks rather nasty. You'd best not try to get by. +#154 +The little bird attacks the green dragon, and in an astounding flurry +gets burnt to a cinder. The ashes blow away. +#155 +On what? +#156 +Okay, from now on I'll only describe a place in full the first time +you come to it. To get the full description, say "LOOK". +#157 +Trolls are close relatives with the rocks and have skin as tough as +that of a rhinoceros. The troll fends off your blows effortlessly. +#158 +The troll deftly catches the axe, examines it carefully, and tosses it +back, declaring, "Good workmanship, but it's not valuable enough." +#159 +The troll catches your treasure and scurries away out of sight. +#160 +The troll refuses to let you cross. +#161 +There is no longer any way across the chasm. +#162 +Just as you reach the other side, the bridge buckles beneath the +weight of the bear, which was still following you around. You +scrabble desperately for support, but as the bridge collapses you +stumble back and fall into the chasm. +#163 +The bear lumbers toward the troll, who lets out a startled shriek and +scurries away. The bear soon gives up the pursuit and wanders back. +#164 +The axe misses and lands near the bear where you can't get at it. +#165 +With what? Your bare hands? Against *HIS* bear hands?? +#166 +The bear is confused; he only wants to be your friend. +#167 +For crying out loud, the poor thing is already dead! +#168 +The bear eagerly licks up the honeycomb, after which he seems to calm +down considerably and even becomes rather friendly. +#169 +The bear is still chained to the wall. +#170 +The chain is still locked. +#171 +The chain is now unlocked. +#172 +The chain is now locked. +#173 +There is nothing here to which the chain can be locked. +#174 +There is nothing here to eat. +#175 +Do you want the hint? +#176 +Do you need help getting out of the maze? +#177 +You can make the passages look less alike by dropping things. +#178 +Are you trying to explore beyond the Plover Room? +#179 +There is a way to explore that region without having to worry about +falling into a pit. None of the objects available is immediately +useful in discovering the secret. +#180 +Do you need help getting out of here? +#181 +Don't go west. +#182 +Gluttony is not one of the troll's vices. Avarice, however, is. +#183 +Your lamp is getting dim. You'd best start wrapping this up, unless +you can find some fresh batteries. I seem to recall there's a vending +machine in the maze. Bring some coins with you. +#184 +Your lamp has run out of power. +#185 +There's not much point in wandering around out here, and you can't +explore the cave without a lamp. So let's just call it a day. +#186 +There are faint rustling noises from the darkness behind you. As you +turn toward them, the beam of your lamp falls across a bearded pirate. +He is carrying a large chest. "Shiver me timbers!", he cries, "I've +been spotted! I'd best hie meself off to the maze to hide me chest!" +With that, he vanishes into the gloom. +#187 +Your lamp is getting dim. You'd best replace those batteries. +#188 +Your lamp is now shining with renewed strength. +#189 +Your lamp is getting dim, and you haven't any more coins. You'd +best start wrapping this up. +#190 +I'm afraid the magazine is written in dwarvish. +#191 +"This is not the maze where the pirate leaves his treasure chest." +#192 +Hmmm, this looks like a clue, which means it'll cost you 10 points to +read it. Should I go ahead and read it anyway? +#193 +It says, "Not all black rods are magic wands. Some are useful for +other cave construction purposes. There might be some around here." +#194 +It says the same thing it did before. +#195 +I'm afraid I don't understand. +#196 +"Congratulations on bringing light into the dark-room!" +#197 +You strike the mirror a resounding blow, whereupon it shatters into a +myriad tiny fragments. +#198 +You have taken the vase and hurled it delicately to the ground. +#199 +You prod the nearest dwarf, who wakes up grumpily, takes one look at +you, curses, and grabs for his axe. +#200 +Is this acceptable? +#201 +There's no point in suspending a demonstration game. +#202 +You managed to climb about halfway up before losing your hold and +sliding back. +#203 +You were only a few yards from the top when you slipped and tumbled +all the way back down. +#204 +Ok, ok. No need to be grabby. +#205 +Pulling an angry bear around is a good way to get your arm ripped off. +#206 +The sword is now very clean. +#207 +The sword is now covered with oil. +#208 +The handle is now too slippery to grasp. +#209 +You have no scabbard! +#210 +You are already wearing it! +#211 +Do you want to extract the sword from the anvil? +#212 +If you could convince me of your royal blood, you might get somewhere. +#213 +Are you trying to go up the slide? +#214 +Maybe you'd better look for another way into the cave. +#215 +You grasp the sword's handle and pull, but the sword won't budge. +Do you want to try yanking it out? +#216 +A tiny elf runs straight at you, shouts "Phuce!", and +disappears into the forest. +#217 +You can't swim. You'd best go by boat. +#218 +The boat's oars were stolen by the dwarves to play bing-bong. +(That's dwarvish ping-pong -- with rocks!). You have no way +to propel the boat. +#219 +You are still in the boat. It is too heavy to carry. +#220 +You had better leave the boat first. +#221 +You are now sitting in a small boat. +#222 +You couldn't possibly cross this sea without a large ship. +#223 +The cliff is unscalable. +#224 +Your keys are all too large for the lock. +#225 +The wall is too smooth to climb. +#226 +The shelf is beyond your reach. +#227 +You are already wearing them! +#228 +You thought maybe these were peyote?? You feel a little dizzy, +but nothing happens. +#229 +You are growing taller, expanding like a telescope! Just before +your head strikes the top of the chamber, the mysterious process +stops as suddenly as it began. +#230 +You are closing up like an accordion....shrinking..shrinking. You +are now your normal size. +#231 +You have smashed your bottle all over the ground. +#232 +You are now too big to leave the way you came in. +#233 +This is obviously an elfin entrance. Unless you can squeeze +yourself through a six-inch door, you'll have to find another +way into the cave. +#234 +The wrought-iron door is now locked. +#235 +The tiny door is now locked. +#236 +The wrought-iron door is now unlocked. +#237 +The tiny door is now unlocked. +#238 +You are blocked by the wrought-iron door. +#239 +<$$< +#240 +The Wumpus looks at the food with distaste. He looks at *YOU* with +relish! +#241 +You have jerked the cloak free of the rocks. However, in doing +so you have caused a small rock slide, blocking the entrance +and making an unholy din. +#242 +The cloak is stuck tight under the rocks. You'll probably have to +yank it out. +#243 +The Wumpus grabs the axe, stops and picks his teeth with it for a few +moments while looking thoughtfully at you. When he finishes picking +his teeth, he eats the axe, belches, farts... and starts after +you again! +#244 +As the bridge disappears, the Wumpus scrambles frantically to reach +your side of the fissure. He misses by inches, and with a horrible +shriek plunges to his death in the depths of the fissure! +#245 +You can't even hit a sleeping Wumpus! The axe is now lying too near +the Wumpus for you to retrieve it. +#246 +The Wumpus is between you and the axe. You can't reach it. +#247 +You can't fit through a six-inch door! +#248 +The dog easily dodges the axe, which lands beyond him where you can't +get at it. +#249 +The dog woofs (natch) down the food and looks around hungrily for +more. However, he does not appear to be any better disposed towards +your presence. +#250 +You've got to be kidding! +#251 +It's attached to the wall! +#252 +You can't put a thing into itself! +#253 +The gnome firmly blocks the door of the booth. You can't enter. +#254 +The constant ringing has awakened the dwarves! +There are now several threatening little dwarves in the room with +you! Most of them throw knives at you! All of them get you! +#255 +The telephone is out of order. +#256 +The telephone is out of order and your hand is sore. +#257 +A couple of lead slugs drop from the coin box. (Gnomes are +notoriously cheap....) But you've broken the phone beyond +all hope. +#258 +He wants treasure, not gab. +#259 +He didn't say anything! +#260 +It isn't a parrot. He didn't say anything. +#261 +No one replies. The line goes dead with a faint "Click". +#262 +I think you are a little confused! +#263 +As you move towards the phone booth, a gnome suddenly streaks +around the corner, jumps into the booth and rudely slams the door +in your face. You can't get in. +#264 +He isn't hungry. +#265 +As the blast of the horn reverberates through the chamber, the +seemingly solid rock wall crumbles away, revealing another room just +beyond. The wall was most likely worn thin by an ancient watercourse +which dried up just before completely wearing away the rock. +#266 +The chamber reverberates to the blast of the horn. +(Satchmo you ain't!) +#267 +The bees swarm over the fresh flowers, leaving the hive unguarded +and revealing a sweet honeycomb. +#268 +You are now out of breath. +#269 +It isn't ringing! +#270 +A hairy paw reaches out of the darkness and.... +#271 +You don't have the correct change. +#272 +You go a short way down the bright passage, but the light +grows to blinding intensity. You can't continue. +#273 +You know, if you type "BRIEF", I won't have to keep repeating these +long descriptions of each room. +#274 +Are you trying to reach the natural bridge? +#275 +You can't get to it by climbing the walls or jumping or anything +like that. You could always put in a telephone call to the local +spelunking club for advice, if you could find a phone. +#276 +You turkey!!! Now you've done it! It took some effort, but you +woke up the Wumpus. He slowly opens one red eye, and then another, +and then one more (!!), and looks at you sleepily. He had been +dreaming of a late snack. If you don't act quickly, you'll +be a *late* adventurer! +#277 +The blast of your horn echoes throughout hill and dale. +#278 +All you have are watercress sandwiches. The bear is less than +interested. +#279 +You have smashed your sword to smithereens against a rock. +#280 +Setting yourself adrift in the boat with no way to propel it would +not be very smart. Best to keep the pole. +#281 +You're already in it! +#282 +You've hit the jackpot!! Hundreds of coins and slugs cascade from +the telephone's coin return slot and spill all over the floor of +the booth. +#283 +Whoops! The floor has opened out from under you! It seems you +have fallen into a bottomless pit. As a matter of fact, you're +still falling! Well, I have better things to do than wait around +for you to strike bottom, so let's just assume you're dead. +Sorry about that, Chief. +#284 +The telephone in the booth has begun to ring. +#285 +I don't understand what you are trying to do! +#286 +How do you propose to cross the river? +#287 +The air is filled with beautiful music. +#288 +The air fills with beautiful music. The dog gradually becomes +less fierce, and after a short while he lies down by the side of +the cavern and falls into a deep sleep. +#289 +Are you trying to get past the dog? +#290 +The only person I can remember who managed to get past the dog +was that singer, ummm, what's his name... Eurydice's friend.... +#291 +That wouldn't be wise. It is best to let sleeping dogs lie. +#292 +The stream flows out of one very small crack and into another. +You can't go that way. +#293 +The river is too wide and deep to cross. +#294 +The glare is absolutely blinding. If you proceed you are likely +to fall into a pit. +#295 +The hum of the bees rises to an angry buzz as you move towards +the hive. +#296 +The sticks and branches are all in a tangle and are stuck in the +mud. You'd need a shovel to dig them out. +#297 +You're already carrying them! +#298 +The chalice is slightly cracked. It won't hold any liquids. +#299 +The cask is now empty. +#300 +The wine goes right to your head. You reel around in a drunken +stupor and finally pass out. You awaken with a splitting headache, +and try to focus your eyes.... +#301 +Yeeeecchhh!! +#302 +Your bottle is already full. +#303 +The cask is already full. +#304 +There is nothing here with which to fill the bottle. +#305 +There is nothing here which you would want to put into the cask. +#306 +Your bottle is now full of water. +#307 +The cask is now full of water. +#308 +Your bottle is now full of oil. +#309 +The cask is now full of oil. +#310 +The bottle is now full of wine. +#311 +The cask is now full of wine. +#312 +You have nothing in which to carry it. +#313 +You can't fill that. +#314 +Do you want it in the bottle or the cask? +#315 +Your containers are both full. +#316 +It's empty. +#317 +Hmmm, I see you have already gone through an extra set of batteries. +I'll get rid of the trash for you. +#318 +The bridge shakes as you cross. Large hunks of clay and rock near +the edge break off and hurtle far down into the chasm. Several of +the cracks on the bridge surface widen perceptibly. +#319 +The load is too much for the bridge! With a roar, the entire +structure gives way, plunging you headlong into the raging river at +the bottom of the chasm and scattering all your holdings. As the +icy waters close over your head, you flail and thrash with all your +might, and with your last ounce of strength pull yourself onto the +south bank of the river. +#320 +You can't get at him. He is inside the phone booth. +#321 +Your lamp is already on. +#322 +Your lamp is already off. +#323 +Your lamp is again growing dim. You'd better get some more batteries. +#324 +You know, you are wasting your batteries by wandering around out +here with your light on. +#325 + +#326 +Dead wumpi, as a rule, are light eaters. Nothing happens. +#327 +How do expect to feed a sleeping Wumpus? +#328 +You aren't holding it! +#329 +It won't fit! +#330 +The coin drops into the slot with a dull "clunk". There is no +dial tone. +#331 +It's already there. +#332 +Please read the supplied documentation files to find out where to +send complaints, suggestions, and bug reports. +#333 +The chest is now unlocked. +#334 +The chest is now locked. +#335 +You can't get at it. +#336 +It's already open. +#337 +It's locked. +#338 +It's already closed. +#339 +You can't fill that. It would leak all over the place. +#340 +It's not inside anything. +#341 +It isn't there! +#342 +How? +#343 +Taken. +#344 +Dropped. +#345 +I don't think I can. +#346 +Interesting. How? +#347 +Huh? +#348 +You are in perfect health. +#349 +You are fit as a fiddle. +#350 +Only wizards can do that! +#351 +Are you kidding? Do you want to suffocate the poor thing? +#352 +Thrown. +#353 +Left. +#354 +You can't get close enough for a clean thrust. +#355 +As you move in for the kill, the dwarf neatly slips a knife +between your ribs. +#356 +As you approach, the dwarf slashes out with his knife! +#357 +It's too dark to see anything! +#358 +It's not open. +#359 +There's nothing inside. +#360 +It contains: +#361 +The billboard reads: +"Visit Beautiful Colossal Cave. Open Year Around. Fun for +the entire family, or at least for those who survive." +Below the headline is an impossibly complicated map showing how +to find Colossal Cave. Not that it matters, because all the +directions are written in Elvish. +#362 +Hidden behind the poster is a steel safe, embedded in the wall. +#363 +Brushing the dust from one of the larger rocks reveals some carved +characters. +#364 +Enough dusting, already! You're making me sneeze. +#365 +The safe door smoothly swings open. +#366 +Maybe if you rubbed your fingertips with sandpaper.... +#367 +The safe's door clicks shut. +#368 +This is a combination safe. The keys won't help. +#369 +I certainly can't read in this light. +#370 +The poster has a picture of a thin man with a long white beard. +He is wearing a high pointed cap embroidered with strange symbols, +and he is pointing a finger at you. Below the picture are the words: +"I want you!--To report all good ideas for extensions to this game +to me without delay. Remember: ask not what ADVENTURE can do to +you; ask what you can do for ADVENTURE." +- * * * - +"A public service of the John Dillinger Died for You Society." +#371 +"Click." +#372 +In the rock is carved the message "7-22-34". +#373 +You can't get at them. +#374 +Ok, I'll give you the full description whenever you enter a room +for the first time. +#375 +You don't have the right key. +#376 +That's not strong enough to open the clam. +#377 +That's not strong enough to open the oyster. +#378 +Game suspension is no longer permitted! +#379 +** Unused ** +#380 +>>> Messages 381 thru 389 are reserved for "Diagnoses". <<< +#381 +You are a bit off top form, but nothing to worry about. +#382 +You are weaker than usual. Better avoid fights. +#383 +You really ought to take a break. You're in tough shape. +#384 +You are on the edge of collapse. Lots of sun and fresh air will +speed your recovery. +#385 +Your strength is nearly gone. Only a miracle can save you now. +#386 +** Unused ** +#387 +** Unused ** +#388 +** Unused ** +#389 +** Unused ** +#390 +>>> Messages 391 thru 399 are reserved for Radium Poisoning. <<< +#391 +Is it hot in here? You are flushed and sweating. +#392 +You are feeling definitely peculiar, weak.... +#393 +You're dizzy, nauseous. You can barely stand. +#394 +You are really ill. If you don't find an antidote soon, it's +curtains. +#395 +You are a walking wound. You are very weak. You'd better find out +what's wrong before it's too late. +#396 +Sheeesh! What a mess! Your hair has fallen out and your skin is +covered with blisters. And not an aspirin in sight! +#397 +Well, you tried, but your strength is gone. The agony is finally +over. +#398 +** Unused ** +#399 +** Unused ** +#400 +You feel rather disembodied, as if you were suddenly somewhere +else entirely. +#401 +You sense that you are in a dark place. The only thing in sight +appears to be a companion to the crystal ball which holds your +gaze. It seems to be searching the gloom for something to +show you, but all it can see is itself: a brilliant blue +six-pointed star suspended in space. +#402 +Your gaze withdraws from the crystal ball, and you are now back +in your normal senses. +#403 +A large, stately elf walks up the rise, says the word +"Saint-Michel", and is instantly transported to the castle. +#404 +Are you trying to get to the castle? +#405 +It's easy to get there if you know how. The elves cross over +from here so perhaps you might watch an elf to see how. +Of course they are a bit shy, so keep a good way off while you +watch. +#406 +You get a tingling feeling as you walk through the gate, and ... +#407 +Fiddling with the bird in its cage is not useful. +If you had it in your hand it would make a mess. +#408 +Would you like a map? diff --git a/commands/advent/database.c b/commands/advent/database.c new file mode 100755 index 000000000..d4c55d1b9 --- /dev/null +++ b/commands/advent/database.c @@ -0,0 +1,141 @@ +/* program DATABASE.C */ + +#include +#include +#include "advent.h" +#include "advdec.h" +#include "advtext.h" + +static char oline[256]; + +_PROTOTYPE(void rdupto, (FILE *, int, int, char *)); +_PROTOTYPE(void rdskip, (FILE *, int, int)); + +/* + Function to scan a file up to a specified + point and either print or return a string. +*/ +void rdupto(fdi, uptoc, print, string) +FILE *fdi; +int uptoc, print; +char *string; +{ + int c, i; + static _CONST unsigned char key[4] = {'c' | 0x80, 'L' | 0x80, + 'y' | 0x80, 'D' | 0x80}; + + i = 1; + while ((c = getc(fdi)) != uptoc && c != EOF) { + if (c == '\n') + i = 1; + if (c >= 0x80) + c ^= key[i++ & 3]; + if (c == '\r') + continue; + if (print) + putchar(c); + else + *string++ = (char) c; + } + if (!print) + *string = '\0'; + return; +} + +/* + Function to read a file skipping + a given character a specified number + of times, with or without repositioning + the file. +*/ +void rdskip(fdi, skipc, n) +FILE *fdi; +int skipc, n; +{ + int c; + + while (n--) + while ((c = getc(fdi)) != skipc) + if (c == EOF) + bug(32); + return; +} + +/* + Routine to request a yes or no answer to a question. +*/ +boolean yes(msg1, msg2, msg3) +int msg1, msg2, msg3; +{ + char answer[INPUTBUFLEN]; + + if (msg1) + rspeak(msg1); + do { + switch (*ask("\n> ", answer, sizeof(answer))) { + case 'n': + case 'N': + if (msg3) + rspeak(msg3); + return (FALSE); + case 'y': + case 'Y': + if (msg2) + rspeak(msg2); + return (TRUE); + default: + fputs("Please answer Y (yes) or N (no).", stdout); + } + } while (TRUE); +} + +/* + Print a location description from "advent4.txt" +*/ +void rspeak(msg) +int msg; +{ + if (msg == 54) + printf("ok.\n"); + else { + fseek(fd4, idx4[msg - 1], 0); + rdupto(fd4, '#', 1, 0); + } + return; +} + +/* + Print an item message for a given state from "advent3.txt" +*/ +void pspeak(item, state) +int item, state; +{ + fseek(fd3, idx3[item - 1], 0); + rdskip(fd3, '/', state + 2); + rdupto(fd3, '/', FALSE, oline); + if (strncmp(oline, "<$$<", 4) != 0) + printf("%s", oline); + return; +} + +/* + Print a long location description from "advent1.txt" +*/ +void desclg(loc) +int loc; +{ + fseek(fd1, idx1[loc - 1], 0); + rdupto(fd1, '#', 1, 0); + return; +} + +/* + Print a short location description from "advent2.txt" +*/ +void descsh(loc) +int loc; +{ + fseek(fd2, idx2[loc - 1], 0); + rdupto(fd2, '#', 1, 0); + return; +} diff --git a/commands/advent/english.c b/commands/advent/english.c new file mode 100755 index 000000000..a0134bbfe --- /dev/null +++ b/commands/advent/english.c @@ -0,0 +1,676 @@ +/* program ENGLISH.C */ + + +#include +#include +#include +#include +#include "advent.h" +#include "advdec.h" + +#define ALL 109 + +#define ENTER 3 +#define CRAWL 17 +#define JUMP 39 +#define CLIMB 56 +#define XYZZY 62 +#define PLUGH 65 +#define PLOVER 71 +#define PHUCE 82 + +_PROTOTYPE(static void getwords, (void)); +_PROTOTYPE(static void clrlin, (void)); +_PROTOTYPE(static void doobj, (int *)); +_PROTOTYPE(static boolean doiobj, (void)); +_PROTOTYPE(static boolean do_scoop_up, (void)); +_PROTOTYPE(static boolean check_next, (void)); + +static char buffer[INPUTBUFLEN] = {'\0', '\0', '\0', '\0'}; +static char *txt[MAXWORDS] = {buffer, buffer, buffer, buffer}; +static char *cindex = buffer; +static boolean pflag; +static int vrbkey, words[MAXWORDS] = {0, 0, 0, 0}, word, wdx = 0; +static int takdir[20] = {2, 6, 9, 10, 11, 13, 14, 17, 23, 25, + 33, 34, 36, 37, 39, 78, 79, 80, 89, -1}; + +static int vkey[60] = { + 0, 199, 9, 0, 130, 0, 197, 0, 0, 243, + 0, 0, 89, 140, 0, 5, 0, 227, 0, 0, + 0, 31, 42, 0, 0, 0, 0, 172, 1, 0, + 0, 0, 254, 0, 69, 0, 0, 92, 0, 0, + 138, 137, 149, 239, 45, 74, 183, 0, 0, 112, + 241, 0, 114, 0, 30, 0, 0, 0, 0, 0 +}; + +static int ptab[260] = { + 0, 3028, 3065, 3009, -3005, 5071, 5070, 5058, -5020, 19055, + 19108, 19038, 19020, 19071, 19070, 19058, 19004, 19048, 19091, 19094, + 19112, 19002, 19118, 2062, 2066, 2047, 2067, 2053, 2065, -2010, + -3114, 4034, 4011, 4101, 4035, 4099, 4098, 4017, 4104, 4014, + 4015, -4087, 3083, 3085, -3081, 5055, 5108, 5020, 5071, 5070, + 5058, 5004, 5048, 5091, 5112, 5099, 5118, 19055, 19108, 19020, + 19071, 19070, 19058, 19004, 19048, 19091, 19112, 19099,-19118, 3028, + 3065, 3009, 3005, -3018, 19055, 19108, 19038, 19020, 19071, 19070, + 19058, 19004, 19004, 19048, 19091, 19094, 19112, 19002,-19118, 3028, + 3065, -3018, 19055, 19108, 19038, 19020, 19071, 19070, 19058, 19004, + 19048, 19091, 19094, 19112, 19118, 2062, 2066, 2047, 2067, 2053, + 2065, -2010, 3102, -3090, 19055, 19108, 19020, 19071, 19070, 19058, + 19004, 19048, 19091, 19014, 19015, 19112, 19118, 19120, 19120, -9999, + 3090, 3102, 3028, 3057, 3065, 3009, -3005,-29999, 2052, -2068, + 2024, 2065, 2091, 2042, 2073, 5071, 5070, 5058, -5020, 30999, + 2062, 2066, 2047, 2067, 2053, 2065, 2010, 2073, 19055, 19108, + 19038, 19020, 19071, 19070, 19058, 19004, 19048, 19091, 19094, 19112, + 19002,-19118, 2014, 2015, 2013, 2999, 5014, 5015, 5013, 5999, + 5110, 5113, -5999, 5055, 5108, 5020, 5071, 5070, 5058, 5004, + 5048, 5091, 5014, 5015, 5112, 5099, -5118, 3102, -3090, 6066, + 6047, 6067, 6053, 6072, 6073, 5055, 5108, 5020, 5071, 5070, + 5004, 5004, 5048, 5091, 5112, 5099, 5118, 19055, 19108, 19020, + 19071, 19070, 19058, 19004, 19048, 19091,-19118, 4034, 4011, 4101, + 4035, 4099, 4098, 4017, 4104, 4027, 4087, 9999,-30999, 2002, + -6002, 3102, -3090, 9999, 4034, 4011, 4101, 4035, 4099, 4087, + 4098, 4017, 4104, -4027, -5999, 0, 0, 0, 0, 0, +}; + +static int adjkey[40] = { + 0, 15, 38, 64, 4, 63, 1, 61, 62, 67, + 9, 27, 53, 46, 47, 60, 31, 39, 40, 6, + 43, 26, 32, 28, 34, 50, 49, 45, 44, 10, + 20, 25, 21, 36, 37, 30, 33, 0, 0, 0 +}; + +static int adjtab[70] = { + 0, 5, 98, -83, 2, -90, 66, 41, -90, -39, + 41, 14, 15, 50, -11, 50, 64, 56, 72, -74, + -19, 119, 59, 73, -118, -119, -70, -41, 95, -118, + -118, -58, -71, -120, 110, -108, -120, -73, -62, -60, + 110, 54, -63, -67, -41, -27, -47, 52, -75, -69, + 65, 112, -3, 41, 72, 90, 20, 101, 107, -118, + -55, -10, -38, -4, 48, 9, -71, -39, 0, 0 +}; + +/* + Analyze a two word sentence +*/ +int english() +{ + + char *ch_ptr, *word1, *word2; + int type, val, type2, val2, adj, k, kk; + static int iwest = 0; + + if (!(words[++wdx])) { + getwords(); + wdx = 0; + } + pflag = FALSE; + word = words[wdx]; + if (word < 0) { /* check first word */ + printf("I didn't understand the word \"%s\"\n", txt[wdx]); + words[wdx+1] = 0; + return (FALSE); /* didn't know it */ + } + type2 = val2 = -1; + type = CLASS(word); + clrlin(); + val = VAL(word); + if (words[wdx + 1] && CLASS(words[wdx + 1]) != CONJUNCTION) { + + /* 'SAY' or 'CALL'. If no next word, pass on to higher powers. */ + if (type == ACTION && (val == SAY || val == YELL)) { + word = words[++wdx]; + if (!(word == XYZZY || word == PLUGH + || word == PLOVER || word == PHUCE)) { + if (val == SAY) + printf("Okay, \"%s\".\n", txt[wdx]); + else { + for (ch_ptr = txt[wdx]; *ch_ptr; ch_ptr++) + if (islower(*ch_ptr)) + *ch_ptr = toupper(*ch_ptr); + printf("Okay, \"%s\"!!!!!\n", txt[wdx]); + } + return (FALSE); + } + } else { + word1 = txt[wdx]; + word2 = txt[wdx + 1]; + + /* Special stuff for 'ENTER'. Can't go into water. 'ENTER + BOAT' means 'TAKE BOAT' */ + if (word == ENTER) { + if (CLASS(words[wdx + 1]) == NOUN && VAL(words[wdx + 1]) == BOAT) + word = TAKE + 2000; + else if ((strcmp(word2, "stream") == 0) + || (strcmp(word2, "water") == 0) + || (strcmp(word2, "reservoir") == 0) + || (strcmp(word2, "ocean") == 0) + || (strcmp(word2, "sea") == 0) + || (strcmp(word2, "pool") == 0)) { + rspeak(liqloc(g.loc) == WATER ? 70 : 43); + wdx++; + return (FALSE); + } + } else { + type2 = CLASS(words[wdx + 1]); + val2 = VAL(words[wdx + 1]); + + /* 'LEAVE' is motion verb, unsless leaving an object. + E.G., 'LEAVE BOAT' or 'LEAVE BOTTLE'. BUt make sure + to leave ('DROP') only totable objects. */ + if (strcmp(word1, "leave") == 0 && type2 == NOUN) { + if (!hinged(val2) || g.fixed[val2]) + word = LEAVE + 2000; + + /* IF 'LIGHT LAMP', Light must be taken as an + action verb, not a noun. */ + } else if (strcmp(word1, "light") == 0 + && VAL(words[wdx + 1]) == LAMP) { + word = ON + 2000; + + /* 'WATER PLANT' becomes 'POUR WATER', If we are at + plant. 'OIL DOOR' becomes 'POUR OIL', etc., etc. */ + } else if ((strcmp(word1, "water") == 0 || strcmp(word1, "oil") == 0) + && (strcmp(word2, "plant") == 0 || strcmp(word2, "door") == 0 + || strcmp(word2, "sword") == 0 || strcmp(word2, "anvil") == 0) + && at(val2)) { + words[wdx + 1] = word; + txt[wdx + 1] = txt[wdx]; + word = POUR + 2000; + } + } + } + + } + /* This is the 'inner' loop. Dispatching of all word in a clause + after the first comes through here. */ + do { + switch (CLASS(word)) { + case MOTION: + { + boolean do_part2; + int i; + + do_part2 = FALSE; + type = CLASS(verbs[vrbx]); + val = VAL(verbs[vrbx]); + if (!vrbx) + do_part2 = TRUE; + else { + if (type > ACTION) { + rspeak(confuz()); + return (FALSE); + } + } + if (type == ACTION) { + if (val == GO) + do_part2 = TRUE; + else { + if (val == TAKE) { + for (i = 0; i < 20; i++) + if (takdir[i] == val) + do_part2 = TRUE; + } + if (!do_part2) { + word = vocab(txt[wdx], 1); + if (word) + words[wdx--] = word; + } + } + } else if (type != CRAWL && type != JUMP + && type != CLIMB) + do_part2 = TRUE; + if (do_part2) { + verbs[1] = word; + vrbx = 1; + if (strcmp(txt[wdx], "west") == 0) { + iwest++; + if (iwest == 10) + rspeak(17); + } + } + break; + } + case NOUN: + if (pflag) { + if (!doiobj()) + return (FALSE); + } else { + word = VAL(word); + if (word == ALL) { + if (!do_scoop_up()) + return (FALSE); + } else { + doobj(&word); + if (word > 0) { + objs[++objx] = word; + otxt[objx] = txt[wdx]; + } else { + clrlin(); + pflag = FALSE; + wdx++; + while (words[wdx]) { + if (CLASS(words[wdx]) == CONJUNCTION) + break; + wdx++; + } + if (words[wdx] == 0) + return (FALSE); + } + } + } + break; + case ACTION: + if (vrbx == 0) + vrbx++; + else { + if (VAL(verbs[vrbx]) == TAKE) { + val = VAL(word); + if (val == DRINK || val == INVENTORY + || val == SCORE || val == NOTHING + || val == LOOK); + else if (val == GO && ( + strcmp(txt[wdx], "walk") == 0 + || strcmp(txt[wdx], "run") == 0 + || strcmp(txt[wdx], "hike") == 0)); + else { + rspeak(confuz()); + return (FALSE); + } + } else if (objx || CLASS(words[wdx - 1]) == CONJUNCTION) { + rspeak(confuz()); + return (FALSE); + } + } + verbs[vrbx] = word; + vtxt[vrbx] = txt[wdx]; + break; + case MISC: + if (vrbx) { + rspeak(confuz()); + return (FALSE); + } + verbs[1] = word; + vrbx = 1; + break; + case PREPOSITION: + if (CLASS(verbs[vrbx]) != ACTION || iobx) { + rspeak(confuz()); + return (FALSE); + } + vrbkey = vkey[VAL(verbs[vrbx])]; + if (!vrbkey) { + rspeak(confuz()); + return (FALSE); + } + prep = VAL(word); + pflag = TRUE; + break; + case ADJACTIVE: + /* Adjective handler. Scarf the next word, make sure it is + a valid object for this object. Then call getobj to see + if it is really there, Then link into object code. */ + adj = VAL(word); + if (!check_next()) + return (FALSE); + else if (CLASS(word) == CONJUNCTION) { + printf("%s what?\n", txt[wdx - 1]); + return (FALSE); + } else { + if (CLASS(word) != NOUN) + word = vocab(txt[wdx], NOUN); + if (word == -1 || CLASS(word) != NOUN || VAL(word) == ALL) { + rspeak(confuz()); + return (FALSE); + } + words[wdx] = word; + kk = VAL(word); + for (k = adjkey[adj]; adjtab[k] >= 0; k++) { + if (kk == abs(adjtab[k])) + break; + } + if (adjtab[k] < 0) { + rspeak(confuz()); + return (FALSE); + } + } + break; + case CONJUNCTION: + if (!check_next()) + return (FALSE); + switch (CLASS(word)) { + case MOTION: + case ACTION: + case MISC: + words[wdx--] = 0; + break; + case NOUN: + case ADJACTIVE: + break; + case PREPOSITION: + case CONJUNCTION: + rspeak(confuz()); + return (FALSE); + default: + bug(33); + } + break; + default: + bug(33); + } + word = words[++wdx]; + if (word < 0) { + if (pct(50)) + printf("I don't understand the word %s?\n", txt[wdx]); + else + printf("Mumble ? %s\n", txt[wdx]); + + words[wdx+1] = 0; + return (FALSE); + } + type = CLASS(word); + if (type == NOUN) { + /* It's not the first: Make sure he included a comma or + 'and'. Differenctiate between direct & indirect objects. + Check for special case of multiple ofjects: 'feed bear + honey' or 'throw troll nugget'. */ + if ((pflag ? iobx : objx) + && CLASS(words[wdx - 1]) != CONJUNCTION) { + val = VAL(verbs[vrbx]); + if (!living(objs[objx]) || (val != THROW && val != FEED)) { + rspeak(confuz()); + return (FALSE); + } + iobx++; + iobjs[iobx] = objs[objx]; + objs[objx] = 0; + objx++; + } + } + } while (word); + + if (verbs[1] == 0) { + if (objs[1] == 0) { + rspeak(confuz()); + clrlin(); + } else if (objs[2]) + printf("What do you want to do with them?\n"); + else + printf("What do you want to do with %s?\n", otxt[1]); + return (FALSE); + } else if (objx > 1 && iobx > 1) { + rspeak(confuz()); + return (FALSE); + } + return (TRUE); + +} + +/* + retrieve input line (max INPUTBUFLEN chars), convert to lower case + & rescan for first two words (max. WORDSIZE-1 chars). +*/ +static void getwords() +{ + static int wdx = 0; + int i, term_loc; + char terminator; + + if (*cindex == '\0') { + while (!*ask("\n> ", buffer, sizeof(buffer))) ; + for (cindex = buffer; *cindex; cindex++) + if (isupper(*cindex)) + *cindex = tolower(*cindex); + cindex = buffer; + } + wdx = 0; + buffer[sizeof(buffer)-1] = '\0'; + for (i = 0; i < MAXWORDS; i++) { + txt[i] = &buffer[sizeof(buffer)-1]; + words[i] = 0; + } + do { + while (*cindex == ' ') + cindex++; + txt[wdx] = cindex; + term_loc = strcspn(cindex, " ,.;\n"); + cindex += term_loc; + terminator = *cindex; + *cindex++ = '\0'; + if ((strcmp(txt[wdx], "a") != 0) + && (strcmp(txt[wdx], "the") != 0) + && (strcmp(txt[wdx], "an") != 0)) { + words[wdx] = vocab(txt[wdx], 0); + wdx++; + } + if (terminator == ',') { + txt[wdx] = "and"; + words[wdx] = vocab(txt[wdx], 0); + wdx++; + } + } + while ((terminator != ';') && (terminator != '.') + && (terminator != '\0') && (terminator != '\n')); + if (terminator == '\0') + cindex--; + return; +} + +/* CLRIN, clears out all surrent syntax args in preparation for + * new input line + */ + +static void clrlin() +{ + int i; + + for (i = 0; i < MAXWORDS; i++) { + verbs[i] = 0; + vtxt[i] = &buffer[sizeof(buffer)-1]; + } + + for (i = 0; i < MAXITEMS; i++) { + objs[i] = 0; + otxt[i] = &buffer[sizeof(buffer)-1]; + iobjs[i] = 0; + iotxt[i] = &buffer[sizeof(buffer)-1]; + } + vrbx = 0; + objx = 0; + iobx = 0; + prep = 0; +} + +/* + Routine to process an object. +*/ +static void doobj(object) +int *object; +{ + int msg; + + if (holding(*object)) + return; + if (blind()) { + printf("I see no %s here.\n", txt[wdx]); + *object = 0; + return; + } + /* Is object here? if so, transitive */ + if (g.fixed[*object] == g.loc || athand(*object)) + return; + else if (here(*object)) { + msg = plural(*object) ? 373 : 335; + *object = 0; + rspeak(msg); + } + /* Did he give grate as destination? */ + else if (*object == GRATE) { + if (g.loc == 1 || g.loc == 4 || g.loc == 7) { + verbs[1] = DEPRESSION; + vrbx = 1; + return; + } else if (g.loc > 9 && g.loc < 15) { + verbs[1] = ENTRANCE; + vrbx = 1; + return; + } + } + /* Is it a dwarf he is after? */ + else if (dcheck() && g.dflag >= 2) { + *object = DWARF; + } + /* Is he trying to get/use a liquid? */ + else if (liqloc(g.loc) == *object + || (liq(BOTTLE) == *object && athand(BOTTLE)) + || (liq(CASK) == *object && athand(CASK))); + else if (*object == PLANT && at(PLANT2) && + g.prop[PLANT2] == 0) { + *object = PLANT2; + } else if (*object == ROCKS && at(CARVNG)) { + *object = CARVNG; + } + /* Is he trying to grab a knife? */ + else if (*object == KNIFE && g.knfloc == g.loc) { + rspeak(116); + g.knfloc = -1; + } + /* Is he trying to get at dynamite? */ + else if (*object == ROD && athand(ROD2)) { + *object = ROD2; + } else if (*object == DOOR && (at(SAFE) || at(TDOOR) + || at(TDOOR2) || at(PDOOR))) { + if (at(TDOOR2)) + *object = TDOOR2; + else if (at(PDOOR)) + *object = PDOOR; + else if (at(SAFE)) + *object = SAFE; + else + *object = TDOOR; + } else if (*object == BOOK && athand(BOOK2)) { + *object = BOOK2; + } else if (!(verbs[vrbx] == FIND || verbs[vrbx] == INVENTORY)) { + *object = 0; + printf("I see no %s here.\n", txt[wdx]); + } + return; +} + +static boolean doiobj() +{ + char dk[INPUTBUFLEN], dkk[INPUTBUFLEN]; + int kk; + boolean ok; + + /* checks object is valid for this preposition */ + ok = TRUE; + word = VAL(word); + if (word != ALL) { + doobj(&word); + if (word > 0) { + iobjs[++iobx] = word; + iotxt[iobx] = txt[wdx]; + } else + ok = FALSE; + } + kk = abs(ptab[vrbkey]) / 1000; + if (kk == prep) { + /* preprosition is valid with this verb now check object of + preprosition */ + + if (word == 0 || CLASS(word) == CONJUNCTION) { + /* no object following prepresition: check special cases */ + + pflag = FALSE; + strcpy(dk, txt[--wdx]); + strcpy(dkk, vtxt[vrbx]); + ok = FALSE; + if ((strcmp(dk, "on") == 0 + || strcmp(dk, "off") == 0) + && (strcmp(dkk, "turn") == 0 + || objs[objx] == LAMP)) + ok = TRUE; + if (strcmp(dkk, "take") == 0 + || strcmp(dkk, "put") == 0) + ok = TRUE; + if (strcmp(dk, "up") == 0 + && strcmp(dkk, "pick") == 0) + ok = TRUE; + if (strcmp(dk, "down") == 0 + && (strcmp(dkk, "put") == 0 || verbs[vrbx] == THROW) ) + ok = TRUE; + } else { + /* object follows preposition See if it's plausible. */ + + kk = abs(ptab[vrbkey]) % 1000; + if (kk == word && kk == ALL) { + if (!do_scoop_up()) + return (FALSE); + } else if (!(kk == word || kk == 999)) { + vrbkey++; + ok = ptab[vrbkey - 1] < 0 ? FALSE : TRUE; + } + } + } + return (ok); +} + +static boolean do_scoop_up() +{ + int i, val; + + val = VAL(verbs[vrbx]); + if (val == DROP || val == PUT || val == LEAVE) { + for (i = 1; i < MAXOBJ; i++) { + if (!athand(i) || g.fixed[i]) + continue; + if (i > WATER && i <= WINE + 1) + continue; + if (toting(i)) { + objs[++objx] = i; + otxt[objx] = "BUG???"; + if (objx >= 44) + break; + } + } + } + if (val == TAKE || val == PICK || val == GET) { + if (blind()) { + rspeak(357); + return (FALSE); + } else { + for (i = 1; i < MAXOBJ; i++) { + if (!athand(i) || g.fixed[i]) + continue; + if (i > WATER && i <= WINE + 1) + continue; + if (!toting(i)) { + objs[++objx] = i; + otxt[objx] = "BUG???"; + if (objx >= 44) + break; + } + } + } + } + return (TRUE); +} + +static boolean check_next() +{ + + word = words[wdx + 1]; + if (word > 0) + return (TRUE); + else if (word == 0) + rspeak(confuz()); + else { + if (pct(50)) + printf("I don't understand the word %s?\n", txt[wdx]); + else + printf("Mumble ? %s\n", txt[wdx]); + words[wdx+1] = 0; + } + + return (FALSE); +} diff --git a/commands/advent/initial.c b/commands/advent/initial.c new file mode 100755 index 000000000..24963acd9 --- /dev/null +++ b/commands/advent/initial.c @@ -0,0 +1,264 @@ +/* + Initialization of adventure play variables +*/ +#include +#include +#include "advent.h" /* #define preprocessor equates */ +#define EXTERN /* define, not declare, in advdec.h */ +#include "advdec.h" + +int plac[MAXOBJ] = { + 0, 0, 3, 8, 10, 11, 0, 14, 0, 148, /* 0 - 9 */ + 96, 19, 17, 101, 103, 0, 106, 0, 0, 238, /* 10 - 19 */ + 238, 117, 190, 109, 25, 23, 111, 35, 0, 97, /* 20 - 29 */ + 0, 119, 0, 117, 0, 130, 0, 126, 140, 0, /* 30 - 39 */ + 96, 94, 158, 160, 188, 0, 155, 174, 166, 228, /* 40 - 49 */ + 18, 204, 27, 29, 30, 0, 92, 168, 97, 100, /* 50 - 59 */ + 101, 0, 119, 127, 130, 141, 144, 205, 28, 182, /* 60 - 69 */ + 225, 230, 0, 147, 241, 248, 0, 0, 0, 0, /* 70 - 79 */ + 0, 0, 0, 0, 0, 0, 0, 193, 102, 0, /* 80 - 89 */ + 159, 141, 172, 188, 189, 0, 0, 193, 227, 174, /* 90 - 99 */ + 0, 13, 238, 217, 171, 0, 146, 159, 3, 0, /* 100 - 109 */ + 0, 0, 0, 3, 180, 39, 5, 0, 110, 169, /* 110 - 119 */ + 200 +}; + +int fixd[MAXOBJ] = { + 0, 0, 0, 9, 0, 0, 0, 15, 0, 0, /* 0 - 9 */ + 0, -1, 27, -1, 0, 0, 0, -1, 0, 0, /* 10 - 19 */ + 0, 122, 235, -1, -1, 67, -1, 110, 0, -1, /* 20 - 29 */ + -1, 121, 0, 122, 0, -1, -1, -1, -1, 0, /* 30 - 39 */ + -1, -1, 166, 167, 189, 0, 0, -1, 0, 229, /* 40 - 49 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 50 - 59 */ + 0, 0, 121, 0, -1, 0, 0, 0, 0, 0, /* 60 - 69 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 70 - 79 */ + 0, 0, 0, 0, 0, 0, 0, -1, 194, -1, /* 80 - 89 */ + 0, -1, 174, -1, -1, 0, 0, -1, -1, -1, /* 90 - 99 */ + 0, 0, 0, -1, -1, -1, 0, 0, 0, 0, /* 100 - 109 */ + 0, 0, -1, 0, 0, -1, -1, 0, 0, 0, /* 110 - 119 */ + 0 +}; + +struct playinfo g = { + 0, /* turns */ + 1, /* loc */ + 1, /* oldloc */ + 1, /* oldloc2 */ + 1, /* newloc */ + { /* loc_attrib[MAXLOC] */ + 0, 73, 65, 73, 73, /* 0 - 4 */ + 65, 65, 73, 1601, 33, /* 5 - 9 */ + 32, 32, 32, 1824, 32, /* 10 - 14 */ + 0, 2, 0, 0, 2816, /* 15 - 19 */ + 2, 2, 2, 0, 10, /* 20 - 24 */ + 0, 2, 0, 0, 0, /* 25 - 29 */ + 0, 2, 2, 8, 0, /* 30 - 34 */ + 0, 0, 0, 8, 0, /* 35 - 39 */ + 2, 0, 256, 256, 256, /* 40 - 44 */ + 256, 272, 272, 272, 256, /* 45 - 49 */ + 256, 0, 256, 256, 272, /* 50 - 54 */ + 256, 272, 0, 16, 2, /* 55 - 59 */ + 0, 0, 0, 0, 0, /* 60 - 64 */ + 0, 0, 0, 0, 0, /* 65 - 69 */ + 0, 0, 0, 0, 0, /* 70 - 74 */ + 0, 0, 0, 0, 2, /* 75 - 79 */ + 256, 256, 272, 0, 0, /* 80 - 84 */ + 16, 272, 0, 0, 2, /* 85 - 89 */ + 2, 0, 0, 0, 0, /* 90 - 94 */ + 8, 0, 0, 1280, 513, /* 95 - 99 */ + 513, 512, 0, 0, 0, /* 00 - 04 */ + 0, 0, 0, 768, 0, /* 105 - 109 */ + 0, 0, 0, 8, 0, /* 110 - 114 */ + 1, 1, 0, 0, 0, /* 115 - 119 */ + 0, 0, 16, 16, 16, /* 120 - 124 */ + 16, 17, 16, 16, 16, /* 125 - 129 */ + 16, 0, 0, 0, 0, /* 130 - 134 */ + 0, 0, 0, 0, 0, /* 135 - 139 */ + 0, 1040, 16, 0, 0, /* 140 - 144 */ + 2, 65, 65, 65, 65, /* 145 - 149 */ + 65, 65, 65, 65, 65, /* 150 - 154 */ + 65, 3, 2625, 2113, 65, /* 155 - 159 */ + 65, 3, 3, 3, 3, /* 160 - 164 */ + 3, 41, 41, 9, 9, /* 165 - 169 */ + 0, 0, 0, 0, 0, /* 170 - 174 */ + 0, 0, 0, 2, 0, /* 175 - 179 */ + 0, 2, 0, 0, 0, /* 180 - 184 */ + 0, 0, 0, 0, 16, /* 185 - 189 */ + 0, 0, 9, 0, 0, /* 190 - 194 */ + 0, 0, 0, 9, 2, /* 195 - 199 */ + 1, 1, 2304, 0, 0, /* 200 - 204 */ + 0, 8, 8, 8, 0, /* 205 - 209 */ + 0, 0, 1, 0, 9, /* 210 - 214 */ + 0, 0, 12, 0, 0, /* 215 - 219 */ + 0, 0, 0, 0, 0, /* 220 - 224 */ + 0, 2, 2625, 73, 73, /* 225 - 229 */ + 0, 2, 2, 2, 2, /* 230 - 234 */ + 0, 0, 2, 65, 3137, /* 235 - 239 */ + 65, 65, 65, 65, 65, /* 240 - 244 */ + 65, 65, 65, 65 /* 245 - 249 */ + }, /* loc_attrib[MAXLOC] */ + {0 }, /* place[MAXOBJ] */ + {0 }, /* fixed[MAXOBJ] */ + { /* weight[MAXOBJ] */ + 0, 0, 1, 0, 1, 2, 2, 0, 0, 2, /* 0 - 9 */ + 1, 0, 0, 0, 7, 7, 1, 0, 0, 2, /* 10 - 19 */ + 1, 0, 0, 0, 0, 0, 0, 0, 3, 0, /* 20 - 29 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, /* 30 - 39 */ + 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, /* 40 - 49 */ + 6, 1, 2, 2, 3, 5, 4, 3, 2, 3, /* 50 - 59 */ + 4, 1, 3, 1, 3, 2, 1, 1, 2, 2, /* 60 - 69 */ + 2, 3, 1, 1, 3, 1, 0, 0, 0, 0, /* 70 - 79 */ + 0, 2, 2, 2, 2, 2, 2, 0, 0, 0, /* 70 - 79 */ + 1, 0, 0, 0, 0, 3, 2, 0, 0, 0, /* 80 - 89 */ + 0, 2, 1, 0, 0, 0, 1, 1, 2, 0, /* 100 - 109 */ + 3, 3, 0, 1, 1, 0, 0, 0, 3, 1, /* 110 - 119 */ + 2, 0, 0 /* 120 - 129 */ + }, /* weight[MAXOBJ] */ + {0 }, /* atloc[MAXLOC] */ + {0 }, /* link[MAXOBJ * 2] */ + {0 }, /* holder[MAXOBJ] */ + {0 }, /* hlink[MAXOBJ] */ + {0 }, /* visited[MAXLOC] */ + {0 }, /* prop[MAXOBJ] */ + { /* obj_state[MAXOBJ] */ + 0, 0, 32800, 26, 32770, /* 0 - 4 */ + 32, 32, 8192, 0, 0, /* 5 - 9 */ + 32, 512, 0, 256, 770, /* 10 - 14 */ + 770, 288, 512, 0, 160, /* 15 - 19 */ + 32802, 0, 0, 0, 128, /* 20 - 24 */ + 0, 0, 0, 32, 8192, /* 25 - 29 */ + 512, 512, 0, 512, 0, /* 30 - 34 */ + 512, 256, 0, 32768, 8224, /* 35 - 39 */ + 0, 18, 26, 26, 2, /* 40 - 44 */ + 0, 8320, 18464, 32768, 0, /* 45 - 49 */ + 16384, 16416, 16416, 26656, 24608, /* 50 - 54 */ + 49240, 24608, 16384, 49184, 16416, /* 55 - 59 */ + 16416, 16416, 16384, 16544, 16442, /* 60 - 64 */ + 16416, 18464, 26656, 16416, 16416, /* 65 - 69 */ + 49184, 49154, 18464, 18464, 16416, /* 70 - 74 */ + 16416, 0, 0, 0, 0, /* 75 - 79 */ + 0, 0, 0, 0, 0, /* 80 - 84 */ + 0, 0, 8704, 0, 0, /* 85 - 89 */ + 0, 0, 0, 0, 32768, /* 90 - 94 */ + 0, 128, 0, 0, 0, /* 95 - 99 */ + 0, 160, 8224, 0, 0, /* 100 - 104 */ + 0, 8352, 8352, 32870, 0, /* 105 - 109 */ + 16674, 258, 32858, 288, 32, /* 110 - 114 */ + 256, 0, 0, 32866, 16416, /* 115 - 119 */ + 16416, 0, 0 /* 120 - 123 */ + }, /* obj_state[MAXOBJ] */ + { /* points[MAXOBJ] */ + 0, 0, 0, 0, 0, /* 0 - 4 */ + 0, 0, 0, 0, 0, /* 5 - 9 */ + 0, 0, 0, 0, 0, /* 10 - 14 */ + 0, 1000108, 0, 0, 0, /* 15 - 19 */ + 0, 0, 0, 0, 0, /* 20 - 24 */ + 0, 0, 0, 0, 0, /* 25 - 29 */ + 0, 0, 0, 0, 0, /* 30 - 34 */ + 0, 0, 0, 0, 0, /* 35 - 39 */ + 0, 0, 0, 0, 0, /* 40 - 44 */ + 0, 0, -3000112, 0, 0, /* 45 - 49 */ + -2000055, -2000112, -2000112, -1000112, -5000112, /* 50 - 54 */ + 5000003, -3000112, -2000055, 2000003, -3000112, /* 55 - 59 */ + -4000112, -4000112, 3000003, -1000112, -4000112, /* 60 - 64 */ + -4000112, -2000112, -3000112, -1000112, -1000112, /* 65 - 69 */ + -2000112, -3012055, -4000112, -1000112, -5000112, /* 70 - 74 */ + -5000112, 0, 0, 0, 0, /* 75 - 79 */ + 0, 0, 0, 0, 0, /* 80 - 84 */ + 0, 0, 0, 0, 0, /* 85 - 89 */ + 0, 0, 0, 0, 0, /* 90 - 94 */ + 0, 0, 0, 0, 0, /* 95 - 99 */ + 0, 0, 0, 0, 0, /* 100 - 104 */ + 0, 0, 0, 0, 0, /* 105 - 109 */ + -2000112, 0, 0, 0, 0, /* 110 - 114 */ + 0, 0, 0, 0, -4000118, /* 115 - 119 */ + -2000112, 0 /* 120 - 122 */ + }, /* points[MAXOBJ] */ + {0 }, /* hinted[HNTMAX+1] */ + { /* hints[HNTMAX+1][5] */ + { 0, 0, 0, 0, 0 }, /* 0 */ + { 0, 9999, 4, 0, 0 }, /* 1 */ + { 0, 9999, 10, 0, 0 }, /* 2 */ + { 0, 9999, 5, 0, 0 }, /* 3 */ + { 0, 0, 0, 0, 0 }, /* 4 */ + { 0, 0, 0, 0, 0 }, /* 5 */ + { 0, 0, 0, 0, 0 }, /* 6 */ + { 0, 15, 4, 176, 177 }, /* 7 */ + { 0, 8, 5, 178, 179 }, /* 8 */ + { 0, 13, 3, 180, 181 }, /* 9 */ + { 0, 6, 10, 211, 212 }, /* 10 */ + { 0, 6, 5, 213, 214 }, /* 11 */ + { 0, 4, 2, 62, 63 }, /* 12 */ + { 0, 5, 2, 18, 19 }, /* 13 */ + { 0, 4, 2, 62, 233 }, /* 14 */ + { 0, 6, 5, 274, 275 }, /* 15 */ + { 0, 10, 5, 289, 290 }, /* 16 */ + { 0, 8, 2, 20, 21 }, /* 17 */ + { 0, 5, 2, 404, 405 } /* 18 */ + }, /* hints[HNTMAX+1][5] */ + {0 }, /* hintlc[HNTMAX+1] */ + 0, /* tally */ + 0, /* tally2 */ + 0, /* limit */ + 0, /* lmwarn */ + 0, /* wzdark */ + 0, /* closing */ + 0, /* closed */ + 0, /* holding */ + 0, /* detail */ + 0, /* knfloc */ + 30, /* clock */ + 50, /* clock2 */ + 0, /* panic */ + { 0, 19, 27, 33, 44, 64, 114 }, /* dloc[DWARFMAX+1] */ + 0, /* dflag */ + { 0, 0, 0, 0, 0, 0, 0 }, /* dseen[DWARFMAX+1] */ + { 0, 0, 0, 0, 0, 0, 0 }, /* odloc[DWARFMAX+1] */ + 18, /* daltloc */ + 0, /* dkill */ + 114, /* chloc */ + 140, /* chloc2 */ + 0, /* bonus */ + 0, /* numdie */ + 0, /* foobar */ + 0, /* combo */ + 0, /* terse */ + 5, /* abbnum */ + 100, /* health */ + 0, /* chase */ + FALSE, /* flg239 */ + 0 /* lastglob */ +}; + +/* + Initialization of adventure play variables +*/ +void initialize() +{ + int i; + + for (i = MAXOBJ; i > 0; i--) { + g.fixed[i] = fixd[i]; + if (fixd[i] > 0) { + drop(i + MAXOBJ, fixd[i]); + drop(i, plac[i]); + } + if ((plac[i] != 0) && (fixd[i] <= 0)) + drop(i, plac[i]); + } + + for (i = 1; i <= MAXOBJ; i++) { + if (treasr(i)) { + g.prop[i] = -1; + g.tally++; + } + } + + newtravel = TRUE; + g.place[BOOK] = -1; + insert(BOOK, SAFE); + g.place[WATER] = -1; + insert(WATER, BOTTLE); + g.prop[BOTTLE] = 8; + return; +} diff --git a/commands/advent/itverb.c b/commands/advent/itverb.c new file mode 100755 index 000000000..1af23157d --- /dev/null +++ b/commands/advent/itverb.c @@ -0,0 +1,597 @@ +/* program ITVERB.C */ + + +#include +#include "advent.h" +#include "advdec.h" + +_PROTOTYPE(void needobj, (void)); +_PROTOTYPE(void ivtake, (void)); +_PROTOTYPE(void ivopen, (void)); +_PROTOTYPE(void ivkill, (void)); +_PROTOTYPE(void ivdrink, (void)); +_PROTOTYPE(void ivquit, (void)); +_PROTOTYPE(void ivfoo, (void)); +_PROTOTYPE(void inventory, (void)); +_PROTOTYPE(void addobj, (int obj)); +_PROTOTYPE(void ivpour, (void)); +_PROTOTYPE(void ivfill, (void)); +_PROTOTYPE(void ivbrief, (void)); +_PROTOTYPE(void ivread, (void)); +_PROTOTYPE(void ivcombo, (void)); +_PROTOTYPE(void iveat, (void)); +/* + Routines to process intransitive verbs +*/ +void itverb() +{ + int i; + + newtravel = FALSE; + switch (verb) { + case DROP: + case SAY: + case WAVE: + case CALM: + case RUB: + case THROW: + case FIND: + case FEED: + case BREAK: + case WAKE: + case WEAR: + case HIT: + case DIAL: + case PLAY: + case PICK: + case PUT: + case TURN: needobj(); break; + case TAKE: + case YANK: + case GET: + case INSRT: + case REMOVE: + case BURN: ivtake(); break; + case OPEN: + case CLOSE: + case LOCK: + case UNLOCK: ivopen(); break; + case NOTHING: rspeak(54); break; + case ON: + case OFF: trverb(); break; + case WALK: actspk(verb); break; + case KILL: ivkill(); break; + case POUR: ivpour(); break; + case EAT: iveat(); break; + case DRINK: ivdrink(); break; + case QUIT: ivquit(); break; + case INVENTORY: inventory(); break; + case FILL: ivfill(); break; + case BLAST: ivblast(); break; + case SCORE: score(TRUE); break; + case FOO: ivfoo(); break; + case BRIEF: ivbrief(); break; + case READ: ivread(); break; + case SUSPEND: + if (g.closing) + rspeak(378); + else + saveadv("advent.sav"); + break; + case RESTORE: restore("advent.sav"); break; + case ANSWER: + if ((g.loc != 189) || (g.prop[PHONE] != 0)) + needobj(); + else { + object = PHONE; + itverb(); + } + break; + case BLOW: rspeak(268); break; + /* Action verb 'LEAVE' has no object */ + case LEAVE: bug(29); break; + /* Call if no phone is handy, yell. */ + case YELL: + if (!here(PHONE)) + needobj(); + else if (!g.closed) + rspeak(271); + else { + rspeak(283); + normend(); + } + break; + /* Health. give him a diagnosis. */ + case HEALTH: + if (g.numdie) + fprintf(stdout, "You have been killed %d times otherwise\n", + g.numdie); + if (g.health >= 95) { + if (pct(50)) + rspeak(348); + else + rspeak(349); + } else { + fprintf(stdout, + "Your health rating is %2d out of a possible 100.\n", + g.health); + rspeak(381 + (100 - g.health) / 20); + } + break; + case LOOK: ivlook(); break; + case COMBO: + if (at(SAFE)) + ivcombo(); + break; + case SWEEP: + /* Dust/sweep */ + if (!at(CARVNG) || !athand(BRUSH) || (g.prop[CARVNG] == 1)) + rspeak(342); + else { + g.prop[CARVNG] = 1; + rspeak(363); + rspeak(372); + } + break; + case TERSE: + /* Terse/unterse. supress all long_form descriptions. */ + g.terse = !g.terse; + g.detail = 3; + rspeak(54); + break; + case WIZ: + is_wiz = !is_wiz; + case MAP: + rspeak(54); + break; + case GATE: + if (is_wiz) { + static char buf[INPUTBUFLEN]; + sscanf(ask("Location ? ", buf, sizeof(buf)), "%d", &g.loc); + } + rspeak(54); + break; + case PIRLOC: + if (is_wiz) { + fprintf(stdout, "The dwarfs are at locations:\n"); + for (i = 1; i < DWARFMAX; i++) + fprintf(stdout, " %4d", g.dloc[i]); + fprintf(stdout, "\nThe pirate is at location %4d\n", + g.dloc[DWARFMAX]); + } + rspeak(54); + break; + default: + printf("This intransitive not implemented yet\n"); + } + return; +} + +/* + Routine to indicate no reasonable + object for verb found. Used mostly by + intransitive verbs. +*/ +void needobj() +{ + printf("%s what?\n", vtxt[vrbx]); + return; +} + +/* + CARRY, TAKE etc. +*/ +void ivtake() +{ + int anobj, item; + + anobj = 0; + for (item = 1; item < MAXOBJ; ++item) + if (g.place[item] == g.loc) + if (anobj == 0) + anobj = item; + else { + needobj(); + return; + } + + if (anobj == 0 || (dcheck() && g.dflag >= 2) || blind()) + needobj(); + else { + object = anobj; + if (verb == YANK) + vyank(); + else if (verb == WEAR) + vwear(); + else + vtake(); + } + return; +} + +/* + OPEN, LOCK, UNLOCK +*/ +void ivopen() +{ + int obj_cnt, item; + + for (item = 1, obj_cnt = 0; item < MAXOBJ; item++) { + if ((g.place[item] == g.loc) && (hinged(item))) { + object = item; + obj_cnt++; + } + } + if (obj_cnt != 1) + needobj(); + else if (verb == LOCK) + vlock(); + else if (verb == UNLOCK) + vunlock(); + else if (verb == SHUT) + vclose(); + else + vopen(); +} + +/* + ATTACK, KILL etc +*/ +boolean previous_obj; + +void ivkill() +{ + previous_obj = FALSE; + if (dcheck() && g.dflag >= 2) + object = DWARF; + if (here(SNAKE)) + addobj(SNAKE); + if (at(DRAGON) && g.prop[DRAGON] == 0) + addobj(DRAGON); + if (at(TROLL)) + addobj(TROLL); + if (here(GNOME)) + addobj(GNOME); + if (here(BEAR) && g.prop[BEAR] == 0) + addobj(BEAR); + if (here(WUMPUS) && g.prop[WUMPUS] == 0) + addobj(WUMPUS); + /* Can't attack bird by throwing axe */ + if (here(BIRD) && verb != THROW) + addobj(BIRD); + /* Clam and oyster both treated as clam for intransitive case; no + harm done. */ + if (here(CLAM) || here(OYSTER)) + addobj(CLAM); + + if ((previous_obj) || (object == 0)) + rspeak(44); + else + vkill(); + return; +} + +/* + POUR if no object, assume liq in container, if holding one. +*/ +void ivpour() +{ + if ((holding(BOTTLE)) && (liq(BOTTLE) != 0) && !holding(CASK)) + object = BOTTLE; + if ((holding(CASK)) && (liq(CASK) != 0) && !holding(BOTTLE)) + object = CASK; + + if (object == 0) + needobj(); + else + trverb(); +} + +/* + EAT. intransitive: assume edible if present, else ask what. + If he as more than one edible, or none, 'EAT' is ambiguous + without an explicit object. +*/ +void iveat() +{ + int i; + + previous_obj = FALSE; + for (i = 1; i < MAXOBJ; i++) { + if ((here(i)) && (edible(i))) + addobj(i); + } + if ((previous_obj) || (object == 0)) + needobj(); + else + trverb(); +} + +/* + DRINK. If no object, assume water or wine and look for them here. + If potable is in bottle or cask, drink that. If not, see if there + is something drinkable nearby (stream, lake, wine fountain, etc.), + and drink that. If he has stuff in both containers, ask which. +*/ +void ivdrink() +{ + int ll; + + previous_obj = FALSE; + ll = liqloc(g.loc); + if ((ll == WATER) || (ll == WINE)) { + object = ll; + iobj = -1; + } + ll = liq(BOTTLE); + if ((athand(BOTTLE)) && ((ll == WATER) || (ll == WINE))) { + object = ll; + iobj = BOTTLE; + } + ll = liq(CASK); + if ((athand(CASK)) && ((ll == WATER) || (ll == WINE)) + && iobj != BOTTLE) { + object = ll; + iobj = CASK; + } else + object = 0; + + if (object == 0) + needobj(); + else + trverb(); +} + +/* + QUIT intransitive only. Verify intent and exit if that's what he wants +*/ +void ivquit() +{ + gaveup = yes(22, 54, 54); + if (gaveup) + normend(); + return; +} + +/* + INVENTORY +*/ +void inventory() +{ + int i, msg; + boolean init_msg; + + init_msg = TRUE; + msg = 98; + for (i = 1; i < MAXOBJ; i++) { + if (!holding(i) || wearng(i) || i == BEAR || i == BOAT) + continue; + if (init_msg) + rspeak(99); + pspeak(i, -1); + init_msg = FALSE; + msg = 0; + lookin(i); + } + + /* Tell him what he is wearing */ + init_msg = TRUE; + for (i = 1; i < MAXOBJ; i++) { + if (wearng(i)) { + if (init_msg) + fprintf(stdout, "\nYou are wearing:\n"); + fprintf(stdout, " "); + pspeak(i, -1); + msg = 0; + init_msg = FALSE; + } + } + + if (holding(BOAT)) { + rspeak(221); + lookin(BOAT); + } + if (holding(BEAR)) + msg = 141; + + if (msg) + rspeak(msg); + return; +} + +/* + FILL bottle or cask must be empty, and some liquid avaible +*/ +void ivfill() +{ + if ((g.prop[CASK] == 1) && !here(CASK)) + object = CASK; + if ((g.prop[BOTTLE] == 1) && !here(BOTTLE)) + object = BOTTLE; + + if ((here(BOTTLE) && here(CASK)) || (object == 0)) + needobj(); + else + trverb(); +} + +/* + BLAST etc. +*/ +void ivblast() +{ + if (!g.closed) + actspk(verb); + else { + g.bonus = 135; + if (g.place[ROD2] == 212 && g.loc == 116) + g.bonus = 133; + if (g.place[ROD2] == 116 && g.loc != 116) + g.bonus = 134; + rspeak(g.bonus); + normend(); + } + return; +} + +/* + Handle fee fie foe foo... +*/ +void ivfoo() +{ + int k; + int msg; + + k = VAL(vocab(vtxt[vrbx], MISC)); + if (g.foobar != 1 - k) { + if (g.foobar == 0) + msg = 42; + else + msg = 151; + rspeak(msg); + return; + } + g.foobar = k; + if (k != 4) + return; + g.foobar = 0; + if (g.place[EGGS] == plac[EGGS] || + (toting(EGGS) && g.loc == plac[EGGS])) { + rspeak(42); + return; + } + /* Bring back troll if we steal the eggs back from him before + crossing */ + if (g.place[EGGS] == 0 && g.place[TROLL] == 0 && g.prop[TROLL] == 0) + g.prop[TROLL] = 1; + + if (here(EGGS)) + k = 1; + else if (g.loc == plac[EGGS]) + k = 0; + else + k = 2; + move(EGGS, plac[EGGS]); + pspeak(EGGS, k); + return; +} + +/* + brief/unbrief. intransitive only. + suppress long descriptions after first time. +*/ +void ivbrief() +{ + int msg; + + g.detail = 3; + g.terse = FALSE; + if (g.abbnum != 10000) { + msg = 156; + g.abbnum = 10000; + } else { + msg = 374; + g.abbnum = 5; + } + rspeak(msg); +} + +/* + read etc... +*/ +void ivread() +{ + previous_obj = FALSE; + if (here(BOOK)) + object = BOOK; + if (here(BOOK2)) + addobj(BOOK2); + if (here(BILLBD)) + addobj(BILLBD); + if (here(CARVNG)) + addobj(CARVNG); + if (here(MAGAZINE)) + addobj(MAGAZINE); + if (here(MESSAGE)) + addobj(MESSAGE); + if (here(OYSTER)) + addobj(OYSTER); + if (here(POSTER)) + addobj(POSTER); + if (here(TABLET)) + addobj(TABLET); + + if (previous_obj || object == 0 || dark()) + needobj(); + else + vread(); + return; +} + +/* + LOOK. can't give more detail. Pretend it wasn't dark (though it may "now" + be dark) so he won't fall into a pit staring into the gloom. +*/ +void ivlook() +{ + if (g.detail++ < 3) + rspeak(15); + g.wzdark = FALSE; + g.visited[g.loc] = 0; + g.newloc = g.loc; + newtravel = TRUE; + return; +} + +/* + COMBO: trying to open safe. (see comments for fee fie foe foo) +*/ +void ivcombo() +{ + int k, msg; + + k = VAL(vocab(vtxt[vrbx], MISC)) - 10; + msg = 42; + if (g.combo != 1 - k) { + if (g.combo != 0) + msg = 366; + rspeak(msg); + return; + } + g.combo = k; + if (k != 3) + rspeak(371); + else { + g.combo = 0; + bitoff(SAFE, LOCKBT); + biton(SAFE, OPENBT); + g.prop[SAFE] = 1; + if (g.prop[BOOK] < 0) { + g.tally--; + g.prop[BOOK] = 0; + /* If remaining treasures too elusive, zap his lamp. this + duplicates some code, must be done here since book is + contained ins safe & tally stuff only works for thing + deposited at a location. */ + if ((g.tally == g.tally2) && (g.tally != 0)) + g.limit = (g.limit < 35) ? g.limit : 35; + } + rspeak(365); + } +} + +/* + ensure uniqueness as objects are searched + out for an intransitive verb +*/ +void addobj(obj) +int obj; +{ + if (!previous_obj) { + if (object != 0) + previous_obj = TRUE; + else + object = obj; + } + return; +} diff --git a/commands/advent/score.c b/commands/advent/score.c new file mode 100755 index 000000000..f04416359 --- /dev/null +++ b/commands/advent/score.c @@ -0,0 +1,199 @@ +/** + SCORE + + Calculate what the player's score would be if he quit now. + This may be the end of the game, or he may just be wondering + how he is doing. + + The present scoring algorithm is as follows: + (treasure points are explained in a following comment) + objective: points: present total possible: + getting well into cave 25 25 + total possible for treasures (+mag) 426 + reaching "closing" 20 20 + "closed": quit/killed 10 + klutzed 20 + wrong way 25 + success 30 30 + total: 501 + (points can also be deducted for using hints or deaths.) + +*/ + +#include +#include "advent.h" +#include "advdec.h" + +void score(scorng) +boolean scorng; +{ + int cur_score, max_score, qk[3]; + int obj, num_treas, k, i; + long t; + char *kk2c; + + cur_score = 0; + max_score = 0; + num_treas = 0; + +/** First tally up the treasures. Must be in building and not broken. + give the poor guy partial score just for finding each treasure. + Gets full score, qk[3], for obj if: + obj is at loc qk[1], and + obj has prop value of qk[2] + + weight total possible + magazine 1 (absolute) 1 + + all the following are multiplied by 5 (range 5-25): + book 2 + cask 3 (with wine only) + chain 4 (must enter via styx) + chest 5 + cloak 3 + clover 1 + coins 5 + crown 2 + crystal-ball 2 + diamonds 2 + eggs 3 + emerald 3 + grail 2 + horn 2 + jewels 1 + lyre 1 + nugget 2 + pearl 4 + pyramid 4 + radium 4 + ring 4 + rug 3 + sapphire 1 + shoes 3 + spices 1 + sword 4 + trident 2 + vase 2 + droplet 5 + tree 5 + total: 85 * 5 = 425 + 1 ==> 426 +*/ + + for (obj = 1; obj < MAXOBJ; obj++) { + if (g.points[obj] == 0) + continue; + t = g.points[obj]; + qk[0] = (int) (t < 0L ? -((t = -t) % 1000) : (t % 1000)); + t /= 1000; + qk[1] = (int) (t % 1000); + qk[2] = (int) (t / 1000); + k = 0; + if (treasr(obj)) { + num_treas++; + k = qk[2] * 2; + if (g.prop[obj] >= 0) + cur_score += k; + qk[2] *= 5; + } + if ((g.place[obj] == qk[0]) && (g.prop[obj] == qk[1]) + && ((g.place[obj] != -CHEST) || (g.place[CHEST] == 3)) + && ((g.place[obj] != -SHIELD) || (g.place[SHIELD] == -SAFE)) + ) + cur_score += qk[2] - k; + max_score += qk[2]; + } + + +/** + Now look at how he finished and how far he got. Maxdie and numdie tell us + how well he survived. Gaveup says whether he exited via quit. Dflag will + tell us if he ever got suitably deep into the cave. Closing still indicates + whether he reached the endgame. And if he got as far as "cave closed" + (indicated by "closed"), then bonus is zero for mundane exits or 133, 134, + 135 if he blew it (so to speak). +*/ + + if (g.dflag) + cur_score += 25; + max_score += 25; + if (g.closing) + cur_score += 20; + max_score += 20; + if (g.closed) { + if (g.bonus == 0) + cur_score += 10; + else if (g.bonus == 135) + cur_score += 20; + else if (g.bonus == 134) + cur_score += 25; + else if (g.bonus == 133) + cur_score += 30; + } + max_score += 30; + +/* Deduct points for hints, deaths and quiting. + hints < hntmin are special; see database description +*/ + for (i = 1; i <= HNTMAX; i++) + if (g.hinted[i]) + cur_score -= g.hints[i][2]; + cur_score -= g.numdie * 10; + if (gaveup) + cur_score -= 4; + + fprintf(stdout, "You have found %3d out of %3d Treasures,", + num_treas - g.tally, num_treas); + fprintf(stdout, " using %4d turns\n", g.turns); + fprintf(stdout, "For a score of: %4d", cur_score); + fprintf(stdout, " out of a possible %4d\n", max_score); + + if (cur_score < 110) { + fprintf(stdout, "You are obviously a rank amateur."); + if (!scorng) + fprintf(stdout, " Better luck next time."); + fputc('\n', stdout); + k = 110 - cur_score; + } else if (cur_score < 152) { + fprintf(stdout, + "Your score qualifies you as a Novice Class Adventurer.\n"); + k = 152 - cur_score; + } else if (cur_score < 200) { + fprintf(stdout, + "You have achieved the rating: \"Experienced Adventurer\".\n"); + k = 200 - cur_score; + } else if (cur_score < 277) { + fprintf(stdout, + "You may now consider yourself a \"Seasoned Adventurer\".\n"); + k = 277 - cur_score; + } else if (cur_score < 345) { + fprintf(stdout, + "You have reached \"Junior Master\" status.\n"); + k = 345 - cur_score; + } else if (cur_score < 451) { + fprintf(stdout, + "Your score puts you in Master Adventurer Class C.\n"); + k = 451 - cur_score; + } else if (cur_score < 471) { + fprintf(stdout, + "Your score puts you in Master Adventurer Class B.\n"); + k = 471 - cur_score; + } else if (cur_score < 501) { + fprintf(stdout, + "Your score puts you in Master Adventurer Class A.\n"); + k = 501 - cur_score; + } else { + fprintf(stdout, + "All of Adventuredom gives tribute to you, Adventurer Grandmaster!\n"); + k = 0; + } + + if (!scorng) { + kk2c = (k == 1) ? "." : "s."; + printf("\nTo acheive the next higher rating,"); + if (cur_score == 501) + printf(" would be a neat trick!\n\n CONGRATULATIONS!!\n"); + else + printf(" you need %3d more point%s\n", k, kk2c); + } + return; +} diff --git a/commands/advent/setup.c b/commands/advent/setup.c new file mode 100755 index 000000000..c983da9e4 --- /dev/null +++ b/commands/advent/setup.c @@ -0,0 +1,99 @@ +/** program SETUP.C * + * execution will read the four adventure text files * + * files; "advent1.txt", "advent2.txt", "advent3.txt" & * + * "advent4.txt". it will create the file "advtext.h" * + * which is an Index Sequential Access Method (ISAM) * + * header to be #included into "advent.c" before the * + * header "advdec.h" is #included. */ + + +#include +#include +#include "advent.h" + +_PROTOTYPE(int main, (void)); +_PROTOTYPE(void file_error, (char *)); +_PROTOTYPE(void encode, (unsigned char *)); + +int main() +{ + + FILE *isam, *src, *dest; + char itxt[255]; + int cnt, i; + long llen; + char filename[12]; + static char *headername[] = { + "idx1[MAXLOC]", "idx2[MAXLOC]", "idx3[MAXOBJ]", "idx4[MAXMSG]", + }; + + long x29 = (1L << 29), x30 = (1L << 30); + if (!(x30 / 2 == x29 && 0L < x30 && x29 < x30)) { + fprintf(stderr, "Sorry, advent needs 32-bit `long int's.\n"); + exit(EXIT_FAILURE); + } + isam = fopen("advtext.h", "w"); + if (!isam) { + fprintf(stderr, "Sorry, I can't open advtext.h...\n"); + exit(EXIT_FAILURE); + } + fprintf(isam, "\n/*\theader: ADVTEXT.H\t\t\t\t\t*/\n\n\n"); + + for (i = 1; i <= 4; i++) { + cnt = -1; + llen = 0L; + sprintf(filename, "advent%d.txt", i); + src = fopen(filename, "r"); + if (!src) + file_error(filename); + sprintf(filename, "advent%d.dat", i); + dest = fopen(filename, "w"); + if (!dest) + file_error(filename); + fprintf(isam, "long\t%s = {\n\t", headername[i - 1]); + while (fgets(itxt, 255, src)) { + encode((unsigned char *) itxt); + if (fprintf(dest, "%s\n", itxt) == EOF) + file_error(filename); + if (itxt[0] == '#') { + if (llen) + fprintf(isam, "%ld,%s\t", llen, + &"\0\0\0\0\0\0\0\n"[++cnt & 7]); + llen = ftell(dest); + if (llen <= 0) { + fprintf(stderr, "ftell err in %s\n", filename); + exit(EXIT_FAILURE); + } /* if (!llen) */ + } /* if (itxt[0]) */ + } /* while fgets */ + if (fprintf(isam, "%ld\n\t};\n\n", llen) == EOF) + file_error("advtext.h"); + fclose(src); + if (fclose(dest) == EOF) + file_error(filename); + } + + if (fclose(isam) == EOF) + file_error("advtext.h"); + return EXIT_SUCCESS; +} /* main */ + +void file_error(filename) +char *filename; +{ + perror(filename); + exit(EXIT_FAILURE); +} + +_CONST unsigned char key[4] = {'c' | 0x80, 'L' | 0x80, 'y' | 0x80, 'D' | 0x80}; + +void encode(msg) +unsigned char *msg; +{ + register int i; + + for (i = 1; msg[i]; i++) + msg[i] ^= key[i & 3]; + msg[--i] = '\0'; + return; +} diff --git a/commands/advent/travel.c b/commands/advent/travel.c new file mode 100755 index 000000000..3adf7de12 --- /dev/null +++ b/commands/advent/travel.c @@ -0,0 +1,336 @@ +/* module TRAVEL.C * + * Routine to handle motion requests */ + + +#include +#include +#include "advent.h" +#include "advdec.h" +#include "advcave.h" + +struct trav travel[MAXTRAV]; +static int kalflg; +static int bcrossing = 0; +static int phuce[2][4] = {158, 160, 167, 166, + 160, 158, 166, 167}; + +_PROTOTYPE(static void goback, (void)); +_PROTOTYPE(static void ck_kal, (void)); +_PROTOTYPE(static void dotrav, (void)); +_PROTOTYPE(static void badmove, (void)); +_PROTOTYPE(static void spcmove, (int rdest)); + +void domove() +{ + gettrav(g.loc, travel); + switch (motion) { + case NULLX: + break; + case BACK: + goback(); + break; + case CAVE: + if (outside(g.loc)) + rspeak(57); + else + rspeak(58); + break; + default: + g.oldloc2 = g.oldloc; + g.oldloc = g.loc; + dotrav(); + } + newtravel = TRUE; + return; +} + +/* + Routine to handle request to return + from whence we came! +*/ +static void goback() +{ + int kk, k2, want, temp; + struct trav strav[MAXTRAV]; + + want = forced(g.oldloc) ? g.oldloc2 : g.oldloc; + g.oldloc2 = g.oldloc; + g.oldloc = g.loc; + k2 = 0; + if (want == g.loc) { + rspeak(91); + ck_kal(); + return; + } + for (kk = 0; travel[kk].tdest != -1; ++kk) { + if (!travel[kk].tcond && travel[kk].tdest == want) { + motion = travel[kk].tverb; + dotrav(); + return; + } + if (!travel[kk].tcond) { + temp = travel[kk].tdest; + gettrav(temp, strav); + if (forced(temp) && strav[0].tdest == want) + k2 = temp; + } + } + if (k2) { + motion = travel[k2].tverb; + dotrav(); + } else + rspeak(140); + ck_kal(); + return; +} + +static void ck_kal() +{ + if (g.newloc >= 242 && g.newloc <= 247) { + if (g.newloc == 242) + kalflg = 0; + else if (g.newloc == (g.oldloc + 1)) + kalflg++; + else + kalflg = -10; + } +} + +/* + Routine to figure out a new location + given current location and a motion. +*/ +static void dotrav() +{ + unsigned char mvflag, hitflag, kk; + int rdest, rverb, rcond, robject; + int pctt; + + g.newloc = g.loc; + mvflag = hitflag = 0; + pctt = ranz(100); + + for (kk = 0; travel[kk].tdest >= 0 && !mvflag; ++kk) { + rdest = travel[kk].tdest; + rverb = travel[kk].tverb; + rcond = travel[kk].tcond; + robject = rcond % 100; + + if ((rverb != 1) && (rverb != motion) && !hitflag) + continue; + ++hitflag; + switch (rcond / 100) { + case 0: + if ((rcond == 0) || (pctt < rcond)) + ++mvflag; + break; + case 1: + if (robject == 0) + ++mvflag; + else if (toting(robject)) + ++mvflag; + break; + case 2: + if (toting(robject) || at(robject)) + ++mvflag; + break; + case 3: + case 4: + case 5: + case 7: + if (g.prop[robject] != (rcond / 100) - 3) + ++mvflag; + break; + default: + bug(37); + } + } + if (!mvflag) + badmove(); + else if (rdest > 500) + rspeak(rdest - 500); + else if (rdest > 300) + spcmove(rdest); + else { + g.newloc = rdest; + ck_kal(); + } + newtravel = TRUE; + return; +} + +/* + The player tried a poor move option. +*/ +static void badmove() +{ + int msg; + + msg = 12; + if (motion >= 43 && motion <= 50) + msg = 9; + if (motion == 29 || motion == 30) + msg = 9; + if (motion == 7 || motion == 36 || motion == 37) + msg = 10; + if (motion == 11 || motion == 19) + msg = 11; + if (motion == 62 || motion == 65 || motion == 82) + msg = 42; + if (motion == 17) + msg = 80; + rspeak(msg); + return; +} + +/* + Routine to handle very special movement. +*/ +static void spcmove(rdest) +int rdest; +{ + int load, obj, k; + + switch (rdest - 300) { + case 1: /* plover movement via alcove */ + load = burden(0); + if (!load || (load == burden(EMERALD) && holding(EMERALD))) + g.newloc = (99 + 100) - g.loc; + else + rspeak(117); + break; + case 2: /* trying to remove plover, bad + route */ + if (enclosed(EMERALD)) + extract(EMERALD); + drop(EMERALD, g.loc); + g.newloc = 33; + break; + case 3: /* troll bridge */ + if (g.prop[TROLL] == 1) { + pspeak(TROLL, 1); + g.prop[TROLL] = 0; + move(TROLL2, 0); + move((TROLL2 + MAXOBJ), 0); + move(TROLL, plac[TROLL]); + move((TROLL + MAXOBJ), fixd[TROLL]); + juggle(CHASM); + g.newloc = g.loc; + } else { + g.newloc = plac[TROLL] + fixd[TROLL] - g.loc; + if (g.prop[TROLL] == 0) + g.prop[TROLL] = 1; + if (toting(BEAR)) { + rspeak(162); + g.prop[CHASM] = 1; + g.prop[TROLL] = 2; + drop(BEAR, g.newloc); + g.fixed[BEAR] = -1; + g.prop[BEAR] = 3; + if (g.prop[SPICES] < 0) + ++g.tally2; + g.oldloc2 = g.newloc; + death(); + } + } + break; + case 4: + /* Growing or shrinking in area of tiny door. Each time he + does this, everything must be moved to the new loc. + Presumably, all his possesions are shrunk or streched along + with him. Phuce[2][4] is an array containg four pairs of + "here" (K) and "there" (KK) locations. */ + k = phuce[0][g.loc - 161]; + g.newloc = phuce[1][g.loc - 161]; + for (obj = 1; obj < MAXOBJ; obj++) { + if (obj == BOAT) + continue; + if (g.place[obj] == k && (g.fixed[obj] == 0 || g.fixed[obj] == -1)) + move(obj, g.newloc); + } + break; + case 5: + /* Phone booth in rotunda. Trying to shove past gnome, to get + into phone booth. */ + if ((g.prop[BOOTH] == 0 && pct(35)) || g.visited[g.loc] == 1) { + rspeak(263); + g.prop[BOOTH] = 1; + move(GNOME, 188); + } else { + if (g.prop[BOOTH] == 1) + rspeak(253); + else + g.newloc = 189; + } + break; + case 6: + /* Collapsing clay bridge. He can cross with three (or fewer) + thing. If more, of if carrying obviously heavy things, he + may end up in the drink. */ + g.newloc = g.loc == 235 ? 190 : 235; + bcrossing++; + load = burden(0); + if (load > 4) { + k = (load + bcrossing) * 6 - 10; + if (!pct(k)) + rspeak(318); + else { + rspeak(319); + g.newloc = 236; + if (holding(LAMP)) + move(LAMP, 236); + if (toting(AXE) && enclosed(AXE)) + extract(AXE); + if (holding(AXE)) + move(AXE, 208); + for (obj = 1; obj < MAXOBJ; obj++) + if (toting(obj)) + destroy(obj); + g.prop[CHASM2] = 1; + } + } + break; + case 7: + /* Kaleidoscope code is here. */ + if (kalflg == 5) { + g.newloc = 248; + g.oldloc = 247; + } else { + g.newloc = 242 + ranz(5); + g.oldloc = g.newloc - 1; + kalflg = g.newloc == 242 ? 0 : -10; + } + break; + default: + bug(38); + } + return; +} + +/* + Routine to fill travel array for a given location +*/ +void gettrav(loc, travel) +int loc; +struct trav *travel; +{ + int i; + long t, *lptr; + + lptr = cave[loc - 1]; + for (i = 0; i < MAXTRAV; i++) { + t = *lptr++; + if (!(t)) { + travel->tdest = -1; /* end of array */ + return; /* terminate for loop */ + } + travel->tverb = (int) (t % 1000); + t /= 1000; + travel->tdest = (int) (t % 1000); + t /= 1000; + travel->tcond = (int) (t % 1000); + travel++; + } + bug(25); + return; +} diff --git a/commands/advent/turn.c b/commands/advent/turn.c new file mode 100755 index 000000000..2e2d63835 --- /dev/null +++ b/commands/advent/turn.c @@ -0,0 +1,729 @@ +/* program TURN.C */ + + +#include +#include +#include "advent.h" +#include "advdec.h" + +_PROTOTYPE(void descitem, (void)); +_PROTOTYPE(void domove, (void)); +_PROTOTYPE(void goback, (void)); +_PROTOTYPE(void copytrv, (struct trav *, struct trav *)); +_PROTOTYPE(void dotrav, (void)); +_PROTOTYPE(void badmove, (void)); +_PROTOTYPE(void spcmove, (int)); +_PROTOTYPE(void death, (void)); +_PROTOTYPE(void dwarves, (void)); +_PROTOTYPE(void dopirate, (void)); +_PROTOTYPE(int stimer, (void)); +_PROTOTYPE(void do_hint, (int)); + + +/* + Routine to take 1 turn +*/ +void turn() +{ + int i, hint; + static int waste = 0; + + if (newtravel) { + /* If closing, then he can't leave except via the main office. */ + if (outside(g.newloc) && g.newloc != 0 && g.closing) { + rspeak(130); + g.newloc = g.loc; + if (!g.panic) + g.clock2 = 15; + g.panic = TRUE; + } + /* See if a dwarf has seen him and has come from where he wants + to go. */ + if (g.newloc != g.loc && !forced(g.loc) && g.loc_attrib[g.loc] & NOPIRAT == 0) + for (i = 1; i < (DWARFMAX - 1); ++i) + if (g.odloc[i] == g.newloc && g.dseen[i]) { + g.newloc = g.loc; + rspeak(2); + break; + } + + g.loc = g.newloc; + dwarves(); /* & special dwarf(pirate who + steals) */ + + /* Check for death */ + if (g.loc == 0) { + death(); + return; + } + /* Check for forced move */ + if (forced(g.loc)) { + desclg(g.loc); + ++g.visited[g.loc]; + domove(); + return; + } + /* Check for wandering in dark */ + if (g.wzdark && dark() && pct(35)) { + rspeak(23); + g.oldloc2 = g.loc; + death(); + return; + } + /* see if he is wasting his batteies out in the open */ + if (outside(g.loc) && g.prop[LAMP]) { + waste++; + if (waste > 11) { + rspeak(324); + waste = 0; + } + } else + waste = 0; + + /* If wumpus is chasing stooge, see if wumpus gets him */ + if (g.chase) { + g.chase++; + g.prop[WUMPUS] = g.chase / 2; + move(WUMPUS, g.loc); + if (g.chase >= 10) { + if (dark()) + rspeak(270); + pspeak(WUMPUS, 5); + death(); + return; + } + } + /* check for radiation poisoning. */ + g.health += (outside(g.loc)) ? 3 : 1; + if (g.health > 100) + g.health = 100; + if (here(RADIUM) && (g.place[RADIUM] != -SHIELD || ajar(SHIELD))) + g.health -= 7; + if (g.health < 60) { + rspeak(391 + (60 - g.health) / 10); + if (g.health < 0) { + death(); + return; + } + } + if ((g.oldloc == 188) && (g.loc != 188 && g.loc != 189) + && (g.prop[BOOTH] == 1)) { + move(GNOME, 0); + g.prop[BOOTH] = 0; + } + /* Describe his situation */ + describe(); + if (!blind()) { + ++g.visited[g.loc]; + descitem(); + } + } /* end of newtravel start for + second entry point */ + /* Check if this location is eligible for any hints. If been here + long enough, branch to help section. Ignore "hints" < HNTMIN + (special stuff, see database notes. */ + for (hint = HNTMIN; hint <= HNTMAX; hint++) { + if (g.hinted[hint]) + continue; + if (g.loc_attrib[g.loc] / 256 != hint - 6) + g.hintlc[hint] = -1; + g.hintlc[hint]++; + if (g.hintlc[hint] >= g.hints[hint][1]) + do_hint(hint); + } + + if (g.closed) { + if (g.prop[OYSTER] < 0 && toting(OYSTER)) + pspeak(OYSTER, 1); + for (i = 1; i < MAXOBJ; ++i) + if (toting(i) && g.prop[i] < 0) + g.prop[i] = -1 - g.prop[i]; + } + g.wzdark = dark(); + if (g.knfloc > 0 && g.knfloc != g.loc) + g.knfloc = 0; + ++g.turns; + i = rand(); + + if (stimer()) /* as the grains of sand slip + by */ + return; + + while (!english()) /* retrieve player instructions */ + ; + + vrbx = 1; + objx = objs[1] ? 1 : 0; + iobx = iobjs[1] ? 1 : 0; + verb = VAL(verbs[vrbx]); + do { + object = objx ? objs[objx] : 0; + iobj = iobx ? iobjs[iobx] : 0; + if (object && (objs[2] || iobjs[2])) { + pspeak(object, -1); + printf(" "); + } + switch (CLASS(verbs[vrbx])) { + case MOTION: + motion = verb; + domove(); + break; + case NOUN: + bug(22); + case ACTION: + if (object || iobj) + trverb(); + else + itverb(); + break; + case MISC: + rspeak(verb); + if (verb == 51) + g.hinted[1] = TRUE; + break; + default: + bug(22); + } + if (objx) { + objx++; + if (objs[objx] == 0) + objx = 0; + } + if ((!objx || !objs[objx]) && iobx) { + iobx++; + if (iobjs[iobx] == 0) + iobx = 0; + if (iobx && iobjs[1]) + objx = 1; + } + } while (objx || iobx); + return; +} + +/* + Routine to describe current location +*/ +void describe() +{ + if (toting(BEAR)) + rspeak(141); + if (dark()) + rspeak(16); + else if ((g.terse && verb != LOOK) || g.visited[g.loc] % g.abbnum) + descsh(g.loc); + else + desclg(g.loc); + if (g.loc == 33 && pct(25) && !g.closing) + rspeak(8); + if (g.loc == 147 && !g.visited[g.loc]) + rspeak(216); + return; +} + +/* + Routine to describe visible items +*/ +void descitem() +{ + int i, state; + + for (i = 1; i < MAXOBJ; ++i) { + if (at(i)) { + if (i == STEPS && toting(NUGGET)) + continue; + if (g.prop[i] < 0) { + if (g.closed) + continue; + else { + g.prop[i] = 0; + if (i == RUG || i == CHAIN + || i == SWORD || i == CASK) + g.prop[i] = 1; + if (i == CLOAK || i == RING) + g.prop[i] = 2; + --g.tally; + } + } + if (i == STEPS && g.loc == g.fixed[STEPS]) + state = 1; + else + state = g.prop[i] % 8; + pspeak(i, state); + lookin(i); + } + } + /* If remaining treasures too elusive, zap his lamp */ + if (g.tally == g.tally2 && g.tally != 0 && g.limit > 35) + g.limit = 35; + return; +} + +/* + Routine to handle player's demise via + waking up the dwarves... +*/ +void dwarfend() +{ + rspeak(136); + normend(); + return; +} + +/* + normal end of game +*/ +void normend() +{ + score(FALSE); + gaveup = TRUE; + return; +} + +/* + Routine to handle the passing on of one + of the player's incarnations... +*/ +void death() +{ + int yea, j; + + if (!g.closing) { + if (g.limit < 0) { + rspeak(185); + normend(); + return; + } + yea = yes(81 + g.numdie * 2, 82 + g.numdie * 2, 54); + if (++g.numdie >= MAXDIE || !yea) + normend(); + if (g.chase) { + g.chase = FALSE; + g.prop[WUMPUS] = 0; + move(WUMPUS, 174); + } + if (toting(LAMP)) + g.prop[LAMP] = 0; + for (j = 1; j < MAXOBJ; ++j) { + if (toting(j)) + drop(j, j == LAMP ? 1 : g.oldloc2); + if (wearng(j)) { + g.prop[j] = 0; + bitoff(j, WEARBT); + } + } + g.newloc = 3; + g.oldloc = g.loc; + g.health = 100; + return; + } + /* Closing -- no resurrection... */ + rspeak(131); + ++g.numdie; + normend(); + return; +} + +/* + dwarf stuff. +*/ +void dwarves() +{ + int i, j, k, attack, stick, dtotal; + + /* See if dwarves allowed here */ + if (g.newloc == 0 || forced(g.newloc) || g.loc_attrib[g.newloc] & NOPIRAT) + return; + + /* See if dwarves are active. */ + if (!g.dflag) { + if (inside(g.newloc)) + ++g.dflag; + return; + } + /* If first close encounter (of 3rd kind) */ + if (g.dflag == 1) { + if (!inside(g.newloc) || pct(85)) + return; + ++g.dflag; + + /* kill 0, 1 or 2 of the dwarfs */ + for (i = 1; i < 3; ++i) + if (pct(50)) + g.dloc[(ranz(DWARFMAX - 1)) + 1] = 0; + + /* If any of the survivors is at location, use alternate choise */ + for (i = 1; i <= DWARFMAX; ++i) { + if (g.dloc[i] == g.newloc) + g.dloc[i] = g.daltloc; + g.odloc[i] = g.dloc[i]; + } + rspeak(3); + drop(AXE, g.newloc); + return; + } + /* Things are in full swing. Move each dwarf at random, except if + he's seen us then he sticks with us. Dwarfs never go to + locations outside or meet the bear or following him into dead + end in maze. And of couse, dead dwarves don't do much of + anything. */ + + dtotal = attack = stick = 0; + for (i = 1; i <= DWARFMAX; ++i) { + if (g.dloc[i] == 0) + continue; + /* Move a dwarf at random. we don't have a matrix around to do + it as in the original version... */ + do + j = ranz(106) + 15; + /* allowed area */ + while (j == g.odloc[i] || j == g.dloc[i] + || g.loc_attrib[j] & NOPIRAT); + + if (j == 0) + bug(36); + g.odloc[i] = g.dloc[i]; + g.dloc[i] = j; + + g.dseen[i] = ((g.dseen[i] && inside(g.newloc)) + || g.dloc[i] == g.newloc + || g.odloc[i] == g.newloc); + + if (g.dseen[i]) { + g.dloc[i] = g.newloc; + if (i == DWARFMAX) + dopirate(); + else { + ++dtotal; + if (g.odloc[i] == g.dloc[i]) { + ++attack; + if (g.knfloc >= 0) + g.knfloc = g.newloc; + if (ranz(1000) < (45 * (g.dflag - 2))) + ++stick; + } + } + } + } + + /* Now we know shat's happing, let's tell the poor sucker about it */ + if (dtotal == 0) + return; + if (dtotal > 1) + printf("There are %d threatening little dwarves in the room with you!\n", dtotal); + else + rspeak(4); + if (attack == 0) + return; + if (g.dflag == 2) + ++g.dflag; + if (attack > 1) { + printf("%d of them throw knives at you!!\n", attack); + k = 6; + } else { + rspeak(5); + k = 52; + } + if (stick <= 1) { + rspeak(stick + k); + if (stick == 0) + return; + } else + printf("%d of them get you !!!\n", stick); + g.oldloc2 = g.newloc; + death(); + return; +} + +/* + pirate stuff +*/ +void dopirate() +{ + int j; + boolean k; + + if (g.newloc == g.chloc || g.prop[CHEST] >= 0) + return; + k = FALSE; + /* Pirate won't take pyramid from plover room or dark room (too + easy! ) */ + for (j = 1; j < MAXOBJ; ++j) + if (treasr(j) && !(j == CASK && liq(CASK) == WINE) + && !(j == PYRAMID && (g.newloc == g.place[PYRAMID] + || g.newloc == g.place[EMERALD]))) { + if (toting(j) && athand(j)) + goto stealit; + if (here(j)) + k = TRUE; + } + if (g.tally == g.tally2 + 1 && k == FALSE && g.place[CHEST] == 0 && + athand(LAMP) && g.prop[LAMP] == 1) { + rspeak(186); + move(CHEST, g.chloc); + move(MESSAGE, g.chloc2); + g.dloc[DWARFMAX] = g.chloc; + g.odloc[DWARFMAX] = g.chloc; + g.dseen[DWARFMAX] = 0; + return; + } + if (g.odloc[DWARFMAX] != g.dloc[DWARFMAX] && pct(30)) + rspeak(127); + return; + +stealit: + + rspeak(128); + /* don't steal chest back from troll! */ + if (g.place[MESSAGE] == 0) + move(CHEST, g.chloc); + move(MESSAGE, g.chloc2); + for (j = 1; j < MAXOBJ; ++j) { + if (!treasr(j) || !athand(j) + || (j == PYRAMID && + (g.newloc == plac[PYRAMID] || g.newloc == plac[EMERALD])) + || (j == CASK && (liq(CASK) != WINE))) + continue; + if (enclosed(j)) + extract(j); + if (wearng(j)) { + g.prop[j] = 0; + bitoff(j, WEARBT); + } + insert(j, CHEST); + } + g.dloc[DWARFMAX] = g.chloc; + g.odloc[DWARFMAX] = g.chloc; + g.dseen[DWARFMAX] = FALSE; + return; +} + +/* + special time limit stuff... +*/ +int stimer() +{ + int i, spk; + static int clock3; + + g.foobar = g.foobar > 0 ? -g.foobar : 0; + g.combo = g.combo > 0 ? -g.combo : 0; + if (g.turns > 310 && g.abbnum != 10000 && !g.terse) + rspeak(273); + + /* Bump all the right clocks for reconning battery life and closing */ + if (g.closed) { + clock3--; + if (clock3 == 0) { + g.prop[PHONE] = 0; + g.prop[BOOTH] = 0; + rspeak(284); + } else if (clock3 < -7) { + rspeak(254); + normend(); + return (TRUE); + } + } + if (g.tally == 0 && inside(g.loc) && g.loc != Y2) + --g.clock; + if (g.clock == 0) { + /* Start closing the cave */ + g.prop[GRATE] = 0; + biton(GRATE, LOCKBT); + bitoff(GRATE, OPENBT); + g.prop[FISSURE] = 0; + g.prop[TDOOR] = 0; + biton(TDOOR, LOCKBT); + bitoff(TDOOR, OPENBT); + g.prop[TDOOR2] = 0; + biton(TDOOR2, LOCKBT); + bitoff(TDOOR2, OPENBT); + for (i = 1; i <= DWARFMAX; ++i) { + g.dseen[i] = FALSE; + g.dloc[i] = 0; + } + move(TROLL, 0); + move((TROLL + MAXOBJ), 0); + move(TROLL2, plac[TROLL]); + move((TROLL2 + MAXOBJ), fixd[TROLL]); + juggle(CHASM); + if (g.prop[BEAR] != 3) + destroy(BEAR); + g.prop[CHAIN] = 0; + g.fixed[CHAIN] = 0; + g.prop[AXE] = 0; + g.fixed[AXE] = 0; + rspeak(129); + g.clock = -1; + g.closing = TRUE; + return (FALSE); + } + if (g.clock < 0) + --g.clock2; + if (g.clock2 == 0) { + /* Set up storage room... and close the cave... */ + g.prop[BOTTLE] = put(BOTTLE, 115, 8); + g.holder[BOTTLE] = WATER; + g.place[WATER] = -BOTTLE; + g.hlink[WATER] = 0; + bitoff(BOTTLE, OPENBT); + g.prop[PLANT] = put(PLANT, 115, 0); + g.prop[OYSTER] = put(OYSTER, 115, 0); + g.prop[LAMP] = put(LAMP, 115, 0); + g.prop[ROD] = put(ROD, 115, 0); + g.prop[DWARF] = put(DWARF, 115, 0); + g.loc = 115; + g.oldloc = 115; + g.newloc = 115; + /* Leave the grate with normal (non-negative property). */ + put(GRATE, 116, 0); + biton(GRATE, LOCKBT); + bitoff(GRATE, OPENBT); + g.prop[SNAKE] = put(SNAKE, 116, 1); + g.prop[BIRD] = put(BIRD, 116, 1); + g.prop[CAGE] = put(CAGE, 116, 0); + g.prop[ROD2] = put(ROD2, 116, 0); + g.prop[PILLOW] = put(PILLOW, 116, 0); + + g.prop[BOOTH] = put(BOOTH, 116, -3); + g.fixed[BOOTH] = 115; + g.prop[PHONE] = put(PHONE, 212, -4); + + g.prop[MIRROR] = put(MIRROR, 115, 0); + g.fixed[MIRROR] = 116; + g.prop[BOOK2] = put(BOOK2, 115, 0); + + for (i = 1; i < MAXOBJ; ++i) { + if (toting(i) && enclosed(i)) + extract(i); + if (toting(i)) + destroy(i); + } + rspeak(132); + g.closed = TRUE; + clock3 = 20 + ranz(20); + newtravel = TRUE; + return (TRUE); + } + if (g.prop[LAMP] == 1) + --g.limit; + if (g.limit == 0) { + --g.limit; + g.prop[LAMP] = 0; + if (here(LAMP)) + rspeak(184); + return (FALSE); + } + if (g.limit < 0 && outside(g.loc)) { + rspeak(185); + normend(); + return (TRUE); + } + if (g.limit <= 40) { + if (g.lmwarn || !here(LAMP)) + return (FALSE); + g.lmwarn = TRUE; + spk = 187; + if (g.prop[BATTERIES] == 1) + spk = 323; + if (g.place[BATTERIES] == 0) + spk = 183; + if (g.prop[VEND] == 1) + spk = 189; + rspeak(spk); + return (FALSE); + } + return (FALSE); +} + +/* HINTS + come here if he's been long enough at required location(s) + for some unused hint, hint number is in variable "hint". + Branch to quick test for additional conditions, then + do neet stuff. If conditions are met and we want to offer + hint. Clear hintlc if no action is taken. + */ + +#define MASE 1 +#define DARK 2 +#define WITT 3 +#define H_SWORD 4 +#define SLIDE 5 +#define H_GRATE 6 +#define H_BIRD 7 +#define H_ELFIN 8 +#define RNBOW 9 +#define STYX 10 +#define H_SNAKE 11 +#define CASTLE 12 + +void do_hint(hint) +int hint; +{ + g.hintlc[hint] = 0; + switch (hint + 1 - HNTMIN) { + case MASE: + if (!at(g.loc) && !at(g.oldloc) + && !at(g.loc) && burden(0) > 1) + break; + else + return; + case DARK: + if (g.prop[EMERALD] != -1 && g.prop[PYRAMID] == -1) + break; + else + return; + case WITT: + break; + case H_SWORD: + if ((g.prop[SWORD] == 1 || g.prop[SWORD] == 5) + && !toting(CROWN)) + break; + else + return; + case SLIDE: + break; + case H_GRATE: + if (g.prop[GRATE] == 0 && !athand(KEYS)) + break; + else + return; + case H_BIRD: + if (here(BIRD) && athand(ROD) && object == BIRD) + break; + else + return; + case H_ELFIN: + if (!g.visited[159]) + break; + else + return; + case RNBOW: + if (!toting(SHOES) || g.visited[205]) + break; + else + return; + case STYX: + if (!athand(LYRE) && g.prop[DOG] != 1) + break; + else + return; + case H_SNAKE: + if (here(SNAKE) && !here(BIRD)) + break; + else + return; + case CASTLE: + break; + default: + printf(" TRYING TO PRINT HINT # %d\n", hint); + bug(27); + } + if (!yes(g.hints[hint][3], 0, 54)) + return; + printf("\nI am prepared to give you a hint,"); + printf(" but it will cost you %2d points\n", g.hints[hint][2]); + g.hinted[hint] = yes(175, g.hints[hint][4], 54); + if (g.hinted[hint] && g.limit > 30) + g.limit += 30 * g.hints[hint][2]; + return; +} diff --git a/commands/advent/utility.c b/commands/advent/utility.c new file mode 100755 index 000000000..40b768616 --- /dev/null +++ b/commands/advent/utility.c @@ -0,0 +1,749 @@ +/* + Utility Routines + the next logical funtions describe attributes of objects. + (ajar, hinged, opaque, printd, treasr, vessel, wearng) +*/ + +#include +#include +#include +#include +#include "advent.h" +#include "advdec.h" + +/* + ajar .TRUE. if item is container and is open or unhinged +*/ +boolean ajar(item) +int item; +{ + return ((bitset(g.obj_state[item], OPENBT)) + || (vessel(item) && !hinged(item))); +} + +/* + at .TRUE. To tell if player is on either side of a two sided object. +*/ +boolean at(item) +int item; +{ + if (item < 1 || item > MAXOBJ) + return (FALSE); + else + return (g.place[item] == g.loc || g.fixed[item] == g.loc); +} + +/* + athand .TRUE. if item readily reachable + it can be lying here, in hand or in open container. +*/ +boolean athand(item) +int item; +{ + int contnr; + boolean aaa; + + contnr = -g.place[item]; + aaa = enclosed(item) && ajar(contnr); + + return ((g.place[item] == g.loc) || holding(item) + || (aaa && ((g.place[contnr] == g.loc) + || (toting(item) && holding(contnr))))); +} + +/* + bitoff turns off (sets to 0) a bit in obj_state word +*/ +void bitoff(obj, bit) +int obj, bit; +{ + long val; + + val = 1L << bit; + g.obj_state[obj] &= ~val; +} + +/* + biton turns on (sets to 1) a bit in obj_state word +*/ +void biton(obj, bit) +int obj, bit; +{ + long val; + + val = 1L << bit; + g.obj_state[obj] |= val; +} + +/* + bitset .TRUE. if object_state has bit N set +*/ +boolean bitset(state, bit) +long state; +int bit; +{ + return (((state >> bit) & 1) == 1); +} + +/* + blind .TRUE. if you can't see at this loc, (darkness of glare) +*/ +boolean blind() +{ + return (dark() || (g.loc == 200 + && athand(LAMP) && (g.prop[LAMP] == 1))); +} + +/* + burden .. returns weight of items being carried + + if obj=0, burden calculates the total weight of the adventurer's burden + including everything in all containers (except the boat) that he is + carring. + + if object is a container, calculate the weight of everything inside + the container (including the container itself). Since donkey FORTRAN + isn't recursive, we will only calculate weight of contained containers + one level down. The only serious contained container would be the sack + The only thing we'll miss will be filled VS empty bottle or cage. + + If object isn't a container, return its weight. +*/ +int burden(obj) +int obj; +{ + int i, sum, temp; + + sum = 0; + if (obj == 0) { + for (i = 1; i < MAXOBJ; i++) { + if (toting(i) && (g.place[i] != -BOAT)) + sum += g.weight[i]; + } + } else { + if (obj != BOAT) { + sum = g.weight[obj]; + temp = g.holder[obj]; + while (temp != 0) { + sum += g.weight[temp]; + temp = g.hlink[temp]; + } + } + } + return (sum); +} + +/* + Routine to carry an object + start toting an object, removing it from the list of things + at its former location. If object > MAXOBJ ( moving "FIXED" + or second loc), then don't change place. +*/ +void carry(obj, where) +int obj, where; +{ + int temp; + + if (obj < MAXOBJ) { + if (g.place[obj] == -1) + return; + g.place[obj] = -1; + } + if (g.atloc[where] == obj) + g.atloc[where] = g.link[obj]; + else { + temp = g.atloc[where]; + while (g.link[temp] != obj) { + temp = g.link[temp]; + if (temp == 0) + bug(35); + } + g.link[temp] = g.link[obj]; + } + return; +} + +/* + confuz generates some variant of "Don't understand that" message. +*/ +int confuz() +{ + int msg; + + msg = 60; + if (pct(50)) + msg = 61; + if (pct(33)) + msg = 13; + if (pct(25)) + msg = 347; + if (pct(20)) + msg = 195; + return (msg); +} + +/* + dark .TRUE. if there is no light here +*/ +boolean dark() +{ + return (!(g.loc_attrib[g.loc] & LIGHT) && + (!g.prop[LAMP] || !athand(LAMP))); +} + +/* + Routine to check for presence + of dwarves.. +*/ +int dcheck() +{ + int i; + + for (i = 1; i < (DWARFMAX); ++i) + if (g.dloc[i] == g.loc) + return (i); + return (0); +} + +/* + dead .TRUE. if object is now dead +*/ +boolean dead(obj) +int obj; +{ + return (bitset(g.obj_state[obj], 10)); +} + +/* + drop Place an object at a given loc, prefixing it onto the atloc list. +*/ +void drop(obj, where) +int obj, where; +{ + if (obj > MAXOBJ) + g.fixed[obj - MAXOBJ] = where; + else + g.place[obj] = where; + if (where > 0) { + g.link[obj] = g.atloc[where]; + g.atloc[where] = obj; + } + return; +} + +/* + destroy Permanently eliminate "object" by moving it to + a non-existent location. +*/ +void destroy(obj) +int obj; +{ + move(obj, 0); + return; +} + +/* + edible .TRUE. if obj can be eaten. +*/ +boolean edible(obj) +int obj; +{ + return (bitset(g.obj_state[obj], 7)); +} + +/* + enclosed .TRUE. If object is inside a container. +*/ +boolean enclosed(item) +int item; +{ + if (item < 1 || item > MAXOBJ) + return (FALSE); + else + return (g.place[item] < -1); +} + +/* + extract remove "object" from a container. + origionally name "remove" but rename to avoid conflict with stdio.h +*/ +void extract(obj) +int obj; +{ + int contnr, temp; + + contnr = -g.place[obj]; + g.place[obj] = -1; + if (g.holder[contnr] == obj) + g.holder[contnr] = g.hlink[obj]; + else { + temp = g.holder[contnr]; + while (g.hlink[temp] != obj) { + temp = g.hlink[temp]; + if (temp == 0) + bug(35); + } + g.hlink[temp] = g.hlink[obj]; + } + return; +} + +/* + forced To tell if a location will causes a forced move. + A forced location is one from which he is immediately bounced + to another. Normal use is for death (forced to location zero) + and for description of journey from on place to another. +*/ +int forced(at_loc) +int at_loc; +{ + return ((g.loc_attrib[at_loc] & 10) == 2); +} + +/* + here .TRUE. If an item is at location or is being carried. +*/ +boolean here(item) +int item; +{ + return (g.place[item] == g.loc || toting(item)); +} + +/* + hinged .TRUE. If object can be opened or shut. +*/ +boolean hinged(object) +int object; +{ + return (bitset(g.obj_state[object], 1)); +} + +/* + holding .TRUE. If the object is being carried in hand. +*/ +boolean holding(item) +int item; +{ + if (item < 1 || item > MAXOBJ) + return (FALSE); + else + return (g.place[item] == -1); +} + +/* + insert +*/ +void insert(obj, contnr) +int obj, contnr; +{ + int temp; + + if (contnr == obj) + bug(32); + carry(obj, g.loc); + + temp = g.holder[contnr]; + g.holder[contnr] = obj; + g.hlink[obj] = temp; + g.place[obj] = -contnr; +} + +/* + inside = .TRUE. If location is well within cave +*/ +boolean inside(loc) +int loc; +{ + return (!outside(loc) && !portal(loc)); +} + +/* + Juggle an object by picking it up and putting it down again, + The purpose being to get the object to the front of the chain + at its loc. +*/ +void juggle(obj) +int obj; +{ + int i, j; + + i = g.place[obj]; + j = g.fixed[obj]; + move(obj, i); + move(obj + MAXOBJ, j); + return; +} + +/* + Determine liquid in the vessel +*/ +int liq(item) +int item; +{ + int liquid; + + if ((item == BOTTLE) || (item == CASK)) + liquid = liq2(((int) g.prop[item] >> 1) & 7); + else + liquid = 0; + + return (liquid); +} + +/* + Determine type of liquid in vessel +*/ +int liq2(liquid) +int liquid; +{ + switch (liquid) { + case 4: + return (WATER); + case 5: + return (OIL); + case 6: + return (WINE); + default: + return (0); /* empty */ + } +} + +/* + Determine liquid at a location +*/ +int liqloc(loc) +int loc; +{ + return (liq2((int) ((g.loc_attrib[loc] >> 1) & 7))); +} + +/* + living .TRUE. If object is living, bear for example +*/ +boolean living(obj) +int obj; +{ + return (bitset(g.obj_state[obj], 9)); +} + +/* + locked .TRUE. if lockable object is locked +*/ +boolean locked(item) +int item; +{ + return (bitset(g.obj_state[item], 4)); +} + +/* + locks .TRUE. if you can lock this object +*/ +boolean locks(item) +int item; +{ + return (bitset(g.obj_state[item], 3)); +} + +/* + LOOKIN list contents if obj is a container and is open or transparent. +*/ +void lookin(contnr) +int contnr; +{ + int temp; + boolean first_time; + + if (vessel(contnr) && (ajar(contnr) || !opaque(contnr))) { + temp = g.holder[contnr]; + first_time = TRUE; + while (temp != 0) { + if (first_time) + rspeak(360); + printf(" "); + pspeak(temp, -1); + temp = g.hlink[temp]; + first_time = FALSE; + } + } + return; +} + +/* + Routine to move an object +*/ +void move(obj, where) +int obj, where; +{ + int from; + + if (obj > MAXOBJ) + from = g.fixed[obj - MAXOBJ]; + else { + if (enclosed(obj)) + extract(obj); + from = g.place[obj]; + } + if ((from > 0) && (from < MAXOBJ * 2)) + carry(obj, from); + drop(obj, where); + return; +} + +/* + noway, generate's some variant of "can't do that" message. +*/ +int noway() +{ + int msg; + + msg = 14; + if (pct(50)) + msg = 110; + if (pct(33)) + msg = 147; + if (pct(25)) + msg = 250; + if (pct(20)) + msg = 262; + if (pct(17)) + msg = 25; + if (pct(14)) + msg = 345; + if (pct(12)) + msg = 346; + return (msg); +} + +/* + opaque .TRUE. If obj is non-transparent container +*/ +boolean opaque(obj) +int obj; +{ + return (bitset(g.obj_state[obj], 6)); +} + +/* + outsid .TRUE. If location is outside the cave +*/ +boolean outside(loc) +int loc; +{ + return (bitset(g.loc_attrib[loc], 6)); +} + +/* + Routine true x% of the time. (x an integer from 0 to 100) +*/ +int pct(x) +int x; +{ + return (ranz(100) < x); +} + +/* + plural .TRUE. if object is multiple objects +*/ +boolean plural(obj) +int obj; +{ + return (bitset(g.obj_state[obj], 13)); +} + +/* + portal .TRUE. If location is a cave entrance +*/ +boolean portal(loc) +int loc; +{ + return (bitset(g.loc_attrib[loc], 5)); +} + +/* + printed .TRUE. If object can be read. +*/ +boolean printed(obj) +int obj; +{ + return (bitset(g.obj_state[obj], 8)); +} + +/* + put is the same as move, except it returns a + value used to set the negated prop values + for the repository objects. +*/ +int put(obj, where, pval) +int obj, where, pval; +{ + move(obj, where); + return ((-1) - pval); +} + +/* + RANZ +*/ +int ranz(range) +int range; +{ + return (rand() % range); +} + +/* + small .TRUE. If object fits in sack or small container +*/ +boolean small(obj) +int obj; +{ + return (bitset(g.obj_state[obj], 5)); +} + +/* + toting .TRUE. If an item is being caried. +*/ +int toting(item) +int item; +{ + boolean aaa, bbb, ccc; + int contnr, outer, outer2; + + contnr = -g.place[item]; + outer = -g.place[contnr]; + outer2 = -g.place[outer]; + + aaa = holding(contnr); + bbb = enclosed(contnr) && holding(outer); + ccc = enclosed(outer) && holding(outer2); + + return (holding(item) || (enclosed(item) && (aaa || bbb || ccc))); +} + +/* + treasr .TRUE. If object is valuable for points +*/ +boolean treasr(obj) +int obj; +{ + return (bitset(g.obj_state[obj], 14)); +} + +/* + vessel .TRUE. if object can hold a liquid +*/ +boolean vessel(obj) +int obj; +{ + return (bitset(g.obj_state[obj], 15)); +} + +/* + wearng .TRUE. If wearing obj +*/ +boolean wearng(item) +int item; +{ + return (bitset(g.obj_state[item], WEARBT)); +} + +/* + worn .TRUE. if object is being worn +*/ +boolean worn(obj) +int obj; +{ + return (bitset(g.obj_state[obj], 11)); +} + +static char *e_msg[] = { + "message line > 70 characters", /* 00 */ + "null line in message", /* 01 */ + "too many words of messages", /* 02 */ + "too many travel options", /* 03 */ + "too many vocabulary words", /* 04 */ + "required vocabulary word not found", /* 05 */ + "too many rtext or mtext messages", /* 06 */ + "too many hints", /* 07 */ + "location has loc_attrib bit being set twice", /* 08 */ + "invalid section number in database", /* 09 */ + "out of order locs or rspeak entries.", /* 10 */ + "illegal motion word in travel table", /* 11 */ + "** unused **.",/* 12 */ + "unknown or illegal word in adjective table.", /* 13 */ + "illegal word in prep/obj table", /* 14 */ + "too many entries in prep/obj table", /* 15 */ + "object has condition bit set twice", /* 16 */ + "object number too large", /* 17 */ + "too many entries in adjective/noun table.", /* 18 */ + "** unused **.",/* 19 */ + "special travel (500>l>300) exceeds goto list", /* 20 */ + "ran off end of vocabulary table", /* 21 */ + "verb class (n/1000) not between 1 and 3", /* 22 */ + "intransitive action verb exceeds goto list", /* 23 */ + "transitive action verb exceeds goto list", /* 24 */ + "conditional travel entry with no alternative", /* 25 */ + "location has no travel entries", /* 26 */ + "hint number exceeds goto list", /* 27 */ + "invalid month returned by date function", /* 28 */ + "action verb 'leave' has no object.", /* 29 */ + "preposition found in unexpected table", /* 30 */ + "received an unexpected word terminator from a1toa5", /* 31 */ + "trying to put a container into itself (tricky!)", /* 32 */ + "unknown word class in getwds", /* 33 */ + "** unused **.",/* 34 */ + "trying to carry a non-existent object"}; /* 35 */ + +/* + Fatal error routine +*/ +void bug(n) +unsigned int n; +{ + if (n < 36 && *e_msg[n] != '*') + fprintf(stderr, "Fatal error, probable cause: %s\n", e_msg[n]); + else + fprintf(stderr, "Fatal error number %d - Unused error number!\n", n); + panic((char *) 0, TRUE); +} + +/* + Prompt for input, strip leading and trailing spaces, + return &buf[first non-whitespace]. + Does not return if end of input. +*/ +char * + ask(prompt, buf, buflen) +char *prompt, *buf; +int buflen; +{ + fputs(prompt, stdout); + fflush(stdout); + if (!fgets(buf, buflen, stdin)) + panic("end of input", FALSE); + if (*buf) { + int c; + char *end = buf + strlen(buf); + if (end[-1] != '\n') + /* Skip to end of line */ + while ((c = getchar()) != '\n' && c != EOF); + while (*buf && isspace(*buf)) + buf++; + while (buf <= --end && isspace(*end)) + *end = '\0'; + } + return buf; +} + +/* + save and abort +*/ + +void panic(msg, save) +char *msg; +boolean save; +{ + fprintf(stderr, "\nPANIC: %s%s\n", + msg ? msg : "", save ? ". Save..." : msg ? "" : "aborting."); + if (save) + saveadv("advpanic.sav"); + exit(EXIT_FAILURE); +} diff --git a/commands/advent/verb.c b/commands/advent/verb.c new file mode 100755 index 000000000..c1b46d0c4 --- /dev/null +++ b/commands/advent/verb.c @@ -0,0 +1,2142 @@ +/* program VERB.C */ + +#include "stdio.h" +#include "advent.h" +#include "advdec.h" + + /* Initialize default verb messages */ +static _CONST int actmsg[56] = { + 0, 24, 29, 0, 33, 0, 33, 38, 38, 42, + 14, 43, 110, 29, 110, 73, 75, 29, 13, 59, + 59, 174, 313, 67, 13, 147, 155, 369, 146, 110, + 13, 13, 24, 25, 110, 262, 14, 29, 271, 14, + 14, 24, 29, 38, 24, 331, 24, 109, 332, 0, + 0, 348, 358, 0, 364, 0}; + +_PROTOTYPE(static int ck_obj, (void)); +_PROTOTYPE(void von, (void)); +_PROTOTYPE(void voff, (void)); +_PROTOTYPE(void vwave, (void)); +_PROTOTYPE(void veat, (void)); +_PROTOTYPE(void vthrow, (void)); +_PROTOTYPE(void vfind, (void)); +_PROTOTYPE(void vfill, (void)); +_PROTOTYPE(void vfeed, (void)); +_PROTOTYPE(void vbreak, (void)); +_PROTOTYPE(void vwake, (void)); +_PROTOTYPE(void vdrop, (void)); +_PROTOTYPE(void vpour, (void)); +_PROTOTYPE(void vput, (void)); +_PROTOTYPE(void vread, (void)); +_PROTOTYPE(void vinsert, (void)); +_PROTOTYPE(void vextract, (void)); +_PROTOTYPE(static boolean do_battle, (int *)); +_PROTOTYPE(void vhit, (void)); +_PROTOTYPE(void vanswer, (void)); +_PROTOTYPE(void vblow, (void)); +_PROTOTYPE(void vdial, (void)); +_PROTOTYPE(void vplay, (void)); +_PROTOTYPE(void vpick, (void)); +_PROTOTYPE(void vput, (void)); +_PROTOTYPE(void vturn, (void)); +_PROTOTYPE(void vget, (void)); +_PROTOTYPE(void vlook, (void)); + + +/* + Routine to process a transitive verb +*/ +void trverb() +{ + newtravel = FALSE; + switch (verb) { + case NOTHING: + case CALM: + case WALK: + case QUIT: + case SCORE: + case FOO: + case SUSPEND: break; + case TAKE: vtake(); break; + case DROP: vdrop(); break; + case SAY: bug(34); break; + case OPEN: vopen(); break; + case CLOSE: vclose(); break; + case LOCK: vlock(); break; + case UNLOCK: vunlock(); break; + case ON: von(); break; + case OFF: voff(); break; + case WAVE: vwave(); break; + case KILL: vkill(); break; + case POUR: vpour(); break; + case EAT: veat(); break; + case DRINK: vdrink(); break; + case RUB: + if (object != LAMP) + rspeak(76); + else + actspk(RUB); + break; + case THROW: + if (prep == PREPDN) + vput(); + else + vthrow(); + break; + case FEED: vfeed(); break; + case FIND: + case INVENTORY: vfind(); break; + case FILL: vfill(); break; + case BLAST: ivblast(); break; + case READ: vread(); break; + case BREAK: vbreak(); break; + case WAKE: vwake(); break; + case REMOVE: vextract(); break; + case YANK: vyank(); break; + case WEAR: vwear(); break; + case HIT: vhit(); break; + case ANSWER: vanswer(); break; + case BLOW: vblow(); break; + case DIAL: vdial(); break; + case PLAY: vplay(); break; + case PICK: vpick(); break; + case PUT: vput(); break; + case TURN: vturn(); break; + case GET: vget(); break; + case INSRT: vinsert(); break; + case LOOK: vlook(); break; + default: + printf("This verb is not implemented yet.\n"); + } + return; +} + +/* + Routine to speak default verb message +*/ +void actspk(verb) +int verb; +{ + int i; + + if (verb < 1 || verb > 55) + bug(39); + i = actmsg[verb]; + if (i) + rspeak(i); + return; +} + +/* + CARRY TAKE etc. +*/ +void vtake() +{ + int msg; + + msg = 0; + if (object == BIRD && !g.closed && athand(BIRD) + && g.place[BIRD] != g.loc) { + rspeak(407); + return; + } + if (prep == PREPOF) { + if (object && iobj) { + rspeak(confuz()); + return; + } else if (!object) { + object = iobj; + iobj = 0; + vdrop(); + return; + } + } + msg = 24; + if (object == BOAT) + msg = 281; + if (plural(object)) + msg = 297; + if (holding(object)) { + rspeak(msg); + return; + } + /* Special case objects and fixed objects */ + msg = ck_obj(); + if (g.fixed[object]) { + rspeak(msg); + return; + } + if (prep == PREPIN) { + vinsert(); + return; + } + /* Special case for liquids */ + if (object == WATER || object == OIL || object == WINE) { + if (here(BOTTLE) && here(CASK)) { + rspeak(315); + return; + } + iobj = object; + if (here(BOTTLE)) { + object = BOTTLE; + if (holding(BOTTLE)) + vfill(); + else + rspeak(312); + return; + } else if (here(CASK)) { + object = CASK; + if (holding(CASK)) + vfill(); + else + rspeak(312); + return; + } else { + rspeak(312); + return; + } + } + if (object != BEAR && ((burden(0) + burden(object)) > 15)) { + if (wearng(object)) { + g.prop[object] = 0; + bitoff(object, WEARBT); + } + rspeak(92); + return; + } + if (prep == PREPFR || enclosed(object)) { + vextract(); + return; + } + msg = 343; + /* Poster: hides wall safe */ + if (object == POSTER && g.place[SAFE] == 0) { + g.prop[POSTER] = 1; + msg = 362; + /* Move safe and wall containing safe into view */ + drop(SAFE, g.loc); + drop(WALL2, g.loc); + } + /* Boat: need the pole to push it */ + if (object == BOAT) { + if (!toting(POLE) && g.place[POLE] != -BOAT) { + rspeak(218); + return; + } else { + g.prop[BOAT] = 1; + msg = 221; + } + } + /* Special case for bird. */ + if (object == BIRD && g.prop[BIRD] <= 0) { + if (athand(ROD)) { + rspeak(26); + return; + } + if (!holding(CAGE)) { + rspeak(27); + return; + } + if (!ajar(CAGE)) { + rspeak(358); + return; + } + insert(BIRD, CAGE); + bitoff(CAGE, OPENBT); + pspeak(object, -1); + rspeak(54); + return; + } + /* SWORD If in anvil, need crown & must yank */ + if (object == SWORD && g.prop[SWORD] != 0) { + if (iobj && iobj != ANVIL) { + rspeak(noway()); + return; + } + if (verb != YANK) + if (!yes(215, 0, 54)) + return; + + if (!wearng(CROWN)) { + g.fixed[SWORD] = -1; + g.prop[SWORD] = 3; + pspeak(SWORD, 2); + return; + } + } + carry(object, g.loc); + if (object == POLE || object == SKEY || object == SWORD + || ((object == CLOAK || object == RING) && !wearng(object)) ) + g.prop[object] = 0; + + if (verb == YANK || object == SWORD) + msg = 204; + rspeak(msg); + return; +} + +static int ck_obj() +{ + int msg; + + msg = noway(); + if (object == PLANT && g.prop[PLANT] <= 0) + msg = 115; + if (object == BEAR && g.prop[BEAR] == 1) + msg = 169; + if (object == CHAIN && g.prop[BEAR] != 0) + msg = 170; + if (object == SWORD && g.prop[SWORD] == 5) + msg = 208; + if (object == CLOAK && g.prop[CLOAK] == 2) + msg = 242; + if (object == AXE && g.prop[AXE] == 2) + msg = 246; + if (object == PHONE) + msg = 251; + if (object == BEES || object == HIVE) + msg = 295; + if (object == STICKS) + msg = 296; + return (msg); +} + +/* + DROP etc. +*/ +void vdrop() +{ + int msg; + + /* Check for dynamite */ + if (holding(ROD2) && object == ROD && !holding(ROD)) + object = ROD2; + if (plural(object)) + msg = 105; + else + msg = 29; + + if (object == liq(BOTTLE)) + object = BOTTLE; + else if (object == liq(CASK)) + object = CASK; + + if (!toting(object)) { + rspeak(msg); + return; + } + if (prep == PREPIN) { + vinsert(); + return; + } + /* Snake and bird */ + if (object == BIRD && here(SNAKE)) { + rspeak(30); + if (g.closed) { + dwarfend(); + return; + } + extract(BIRD); + destroy(SNAKE); + /* Set snake prop for use by travel options */ + g.prop[SNAKE] = 1; + drop(BIRD, g.loc); + return; + } + msg = 344; + if (verb == LEAVE) + msg = 353; + if (verb == THROW) + msg = 352; + if (verb == TAKE) + msg = 54; + if (object == POLE && holding(BOAT)) { + rspeak(280); + return; + } + /* Coins and vending machine */ + if (object == COINS && here(VEND)) { + destroy(COINS); + drop(BATTERIES, g.loc); + pspeak(BATTERIES, 0); + return; + } + /* Bird and dragon (ouch!!) */ + if (object == BIRD && at(DRAGON) && g.prop[DRAGON] == 0) { + rspeak(154); + extract(BIRD); + destroy(BIRD); + if (g.place[SNAKE] == plac[SNAKE]) + g.tally2++; + return; + } + /* Bear and troll */ + if (object == BEAR && at(TROLL)) { + msg = 163; + destroy(TROLL); + destroy(TROLL + MAXOBJ); + move(TROLL2, plac[TROLL]); + move((TROLL2 + MAXOBJ), fixd[TROLL]); + juggle(CHASM); + g.prop[TROLL] = 2; + } + /* Vase */ + else if (object == VASE) { + if (g.loc == plac[PILLOW]) + msg = 54; + else { + g.prop[VASE] = at(PILLOW) ? 0 : 2; + pspeak(VASE, g.prop[VASE] + 1); + if (g.prop[VASE] != 0) + g.fixed[VASE] = -1; + } + } else { + if (worn(object) || object == POLE || object == BOAT) + g.prop[object] = 0; + if (worn(object)) + bitoff(object, WEARBT); + if (object == POLE) + g.prop[BOAT] = 0; + } + + if (enclosed(object)) + extract(object); + drop(object, g.loc); + rspeak(msg); + return; +} + +/* + OPEN. special stuff for opening clam/oyster. + The following can be opened without a key: + clam/oyster, door, pdoor, bottle, cask, cage +*/ +void vopen() +{ + int msg, oyclam; + + if (!hinged(object)) + msg = noway(); + else if (object == PDOOR && g.prop[PDOOR] == 1) + msg = 253; + else if (ajar(object)) + msg = 336; + else if (locks(object) || iobj == KEYS || iobj == SKEY) { + vunlock(); + return; + } else if (locked(object)) + if (object == DOOR) + msg = 111; + else + msg = 337; + else if (object == CLAM || object == OYSTER) { + oyclam = (object == OYSTER ? 1 : 0); + msg = oyclam + holding(object) ? 120 : 124; + if (!athand(TRIDENT)) + msg = 122 + oyclam; + if (iobj != 0 && iobj != TRIDENT) + msg = 376 + oyclam; + + if (msg == 124) { + destroy(CLAM); + drop(OYSTER, g.loc); + drop(PEARL, 105); + } + } else { + msg = 54; + biton(object, OPENBT); + } + rspeak(msg); + return; +} + +/* + close, shut + the following can be closed without keys: + door, pdoor, bottle, cask, cage +*/ +void vclose() +{ + if (!hinged(object)) + rspeak(noway()); + else if (!ajar(object)) + rspeak(338); + else if (locks(object)) + vlock(); + else { + rspeak(54); + bitoff(object, OPENBT); + } +} + +/* + Lamp ON. +*/ +void von() +{ + if (!athand(LAMP)) + actspk(verb); + else if (g.limit < 0) + rspeak(184); + else if (g.prop[LAMP] == 1) + rspeak(321); + else { + g.prop[LAMP] = 1; + if (g.loc == 200) + rspeak(108); + else + rspeak(39); + if (g.wzdark) { + g.wzdark = 0; + describe(); + descitem(); + } + } + return; +} + +/* + Lamp OFF. +*/ +void voff() +{ + if (!athand(LAMP)) + actspk(verb); + else if (g.prop[LAMP] == 0) + rspeak(322); + else { + g.prop[LAMP] = 0; + rspeak(40); + if (dark()) + rspeak(16); + } + return; +} + +/* + WAVE. no effect unless waving rod at fissure. +*/ +void vwave() +{ + if (!holding(object) && + (object != ROD || !holding(ROD2))) + rspeak(29); + else if (object != ROD || !at(FISSURE) || + !holding(object) || g.closing) + actspk(verb); + else if (iobj != 0 && iobj != FISSURE) + actspk(verb); + else { + g.prop[FISSURE] = 1 - g.prop[FISSURE]; + pspeak(FISSURE, 2 - g.prop[FISSURE]); + if (g.chase == 0 || g.prop[FISSURE] != 0) + return; + if ((g.loc == 17 && g.oldloc != 27) + || (g.loc == 27 && g.oldloc != 17)) + return; + /* Demise of the Wumpus. Champ must have just crossed bridge */ + rspeak(244); + g.chase = 0; + drop(RING, 209); + g.prop[WUMPUS] = 6; + move(WUMPUS, 209); + biton(WUMPUS, DEADBT); + if (g.place[AXE] != plac[WUMPUS]) + return; + g.fixed[AXE] = 0; + g.prop[AXE] = 0; + + } + return; +} + +/* + ATTACK, KILL etc. +*/ +void vkill() +{ + int msg, i, k; + boolean survival; + + survival = TRUE; + switch (object) { + case BIRD: + if (g.closed) + msg = 137; + else { + destroy(BIRD); + g.prop[BIRD] = 0; + if (g.place[SNAKE] == plac[SNAKE]) + g.tally2++; + msg = 45; + } + break; + case DWARF: + if (g.closed) { + dwarfend(); + return; + } + survival = do_battle(&msg); + break; + case 0: + msg = 44; + break; + case CLAM: + case OYSTER: + msg = 150; + break; + case DOG: + if (g.prop[DOG] == 1) + msg = 291; + else if (iobj == AXE) { + object = AXE; + iobj = DOG; + vthrow(); + return; + } else + msg = 110; + break; + case SNAKE: + msg = 46; + break; + case TROLL: + if (iobj == AXE) + msg = 158; + else + msg = 110; + break; + case BEAR: + msg = 165 + (g.prop[BEAR] + 1) / 2; + break; + case WUMPUS: + if (g.prop[WUMPUS] == 6) + msg = 167; + else if (iobj == AXE) { + object = AXE; + iobj = WUMPUS; + vthrow(); + return; + } else + msg = 110; + break; + case GNOME: + msg = 320; + break; + case DRAGON: + if (g.prop[DRAGON] != 0) { + msg = 167; + break; + } + if (!yes(49, 0, 0)) + break; + pspeak(DRAGON, 1); + biton(DRAGON, DEADBT); + g.prop[DRAGON] = 2; + g.prop[RUG] = 0; + k = (plac[DRAGON] + fixd[DRAGON]) / 2; + move((DRAGON + MAXOBJ), -1); + move((RUG + MAXOBJ), 0); + move(DRAGON, k); + move(RUG, k); + for (i = 1; i < MAXOBJ; i++) + if (g.place[i] == plac[DRAGON] + || g.place[i] == fixd[DRAGON] + || holding(i)) + move(i, k); + g.loc = k; + g.newloc = k; + return; + default: + actspk(verb); + return; + } + rspeak(msg); + if (!survival) { + g.oldloc2 = g.loc; + death(); + } + return; +} + +static boolean do_battle(msg_ptr) +int *msg_ptr; +{ + boolean survival; + int temp; + + survival = TRUE; + if (iobj == 0) + *msg_ptr = 49; + else if (iobj != AXE && iobj != SWORD) { + *msg_ptr = 355; + survival = FALSE; + } else if (pct(25)) { + temp = iobj; + iobj = object; + object = temp; + vthrow(); + return (TRUE); + } else if (pct(25)) { + *msg_ptr = 355; + survival = FALSE; + } else if (pct(36)) + *msg_ptr = 354; + else { + rspeak(356); + if (pct(61)) + *msg_ptr = 52; + else { + *msg_ptr = 53; + survival = FALSE; + } + } + return (survival); +} + +/* + POUR +*/ +void vpour() +{ + int msg; + + if (object == BOTTLE || object == CASK) { + iobj = object; + object = liq(iobj); + if (object == 0) { + rspeak(316); + return; + } + } else { + if (object < WATER || object > (WINE + 1)) { + rspeak(78); + return; + } + } + if (!holding(BOTTLE) && !holding(CASK)) { + rspeak(29); + return; + } + if (holding(BOTTLE) && liq(BOTTLE) == object) + iobj = BOTTLE; + if (holding(CASK) && liq(CASK) == object) + iobj = CASK; + if (iobj == 0) { + rspeak(29); + return; + } + if (!ajar(iobj)) { + rspeak(335); + return; + } + if (iobj == CASK) + object++; + g.prop[iobj] = 1; + extract(object); + g.place[object] = 0; + msg = 77; + if (iobj == CASK) { + object--; + msg = 104; + } + if (at(PLANT) || at(DOOR) || (at(SWORD) && g.prop[SWORD] != 0)) { + if (at(DOOR)) { + g.prop[DOOR] = 0; + if (object == OIL) { + g.prop[DOOR] = 1; + bitoff(DOOR, LOCKBT); + biton(DOOR, OPENBT); + } + msg = 113 + g.prop[DOOR]; + } else if (at(SWORD)) { + /* If sword is alread oily, don't let him clean it. No + soap. */ + if (g.prop[SWORD] != 5) { + g.prop[SWORD] = 4; + if (object == OIL) { + g.prop[SWORD] = 5; + g.fixed[SWORD] = -1; + } + msg = 206 + g.prop[SWORD] - 4; + } + } else { + msg = 112; + if (object == WATER) { + if (g.prop[PLANT] < 0) + g.prop[PLANT] = -g.prop[PLANT] - 1; + pspeak(PLANT, g.prop[PLANT] + 1); + g.prop[PLANT] = (g.prop[PLANT] + 2) % 6; + g.prop[PLANT2] = g.prop[PLANT] / 2; + newtravel = TRUE; + return; + } + } + } + rspeak(msg); + return; +} + +/* + EAT + If he ate the right thing and is in the right place, move him to + the other place with all his junk. Otherwise, narky message. +*/ +void veat() +{ + int msg, i, k, ll, kk; + + switch (object) { + case HONEY: + g.tally2++; + case FOOD: + destroy(object); + msg = 72; + break; + case BIRD: + case SNAKE: + case CLAM: + case OYSTER: + case FLOWER: + msg = 301; + break; + case DWARF: + case DRAGON: + case TROLL: + case DOG: + case WUMPUS: + case BEAR: + case GNOME: + msg = 250; + break; + case MUSHRM: + case CAKES: + k = object - MUSHRM; + ll = 229 + k; + k = 159 - k; + kk = SKEY; + if (object == MUSHRM) { + kk = TDOOR; + if (g.loc != 158) + g.tally2++; + } + destroy(object); + msg = 228; + if (!(here(kk) || g.fixed[kk] == g.loc)) + break; + msg = ll; + /* If he hasn't taken tiny key off shelf, don't let him get it + for free! */ + for (i = 1; i < MAXOBJ; i++) { + if (i == SKEY && g.prop[SKEY] == 1) + continue; + if (g.place[i] == plac[kk] && g.fixed[i] == 0) + move(i, k); + } + if (g.loc == plac[SKEY] && g.place[SKEY] == plac[SKEY]) + g.tally2++; + g.loc = k; + g.newloc = k; + newtravel = TRUE; + break; + default: + actspk(verb); + return; + } + rspeak(msg); + return; +} + +/* + DRINK +*/ +void vdrink() +{ + int msg, k, j; + + if (object == 0 && (iobj == BOTTLE || iobj == CASK)) + object = liq(iobj); + msg = 110; + if (object == OIL) + msg = 301; + if (object != WATER && object != WINE) { + rspeak(msg); + return; + } + if (iobj == 0) { + if (object == liqloc(g.loc)) + iobj = -1; + if (athand(CASK) && object == liq(CASK)) + iobj = CASK; + if (athand(BOTTLE) && object == liq(BOTTLE)) + iobj = BOTTLE; + } + msg = 73; + if (iobj != -1) { + if (iobj == CASK) + object++; + extract(object); + g.place[object] = 0; + g.prop[iobj] = 1; + msg = (iobj == CASK) ? 299 : 74; + } + if (object == WATER || object == (WATER + 1)) { + rspeak(msg); + return; + } + /* UH-OH. He's a wino. Let him reap the rewards of incontinence. + He'll wander around for awhile, then wake up somewhere or other, + having dropped most of his stuff. */ + rspeak(300); + if (g.prop[LAMP] == 1) + g.limit -= ranz(g.limit) / 2; + if (g.limit < 10) + g.limit = 25; + k = 0; + if (pct(15)) + k = 49; + if (k == 0 && pct(15)) + k = 53; + if (k == 0 && pct(25)) + k = 132; + if (k == 0) + k = 175; + if (outside(g.loc)) + k = 5; + if (k == g.loc) { + rspeak(msg); + return; + } + if (holding(AXE)) + move(AXE, k); + if (holding(LAMP)) + move(LAMP, k); + for (j = 1; j < MAXOBJ; j++) { + if (wearng(j)) + bitoff(j, WEARBT); + if (holding(j)) + drop(j, g.loc); + } + g.loc = k; + g.newloc = k; +} + +/* + THROW etc. +*/ +void vthrow() +{ + int msg, i, k, dwarfn; + + if (holding(ROD2) && object == ROD && !holding(ROD)) + object = ROD2; + if (!holding(object)) { + actspk(verb); + return; + } + if (object == BOAT || object == BEAR) { + rspeak(noway()); + return; + } + dwarfn = dcheck(); + if (iobj == 0) { + /* No indirect object was specified. If a dwarf is present, + assume it is the object. If not, look for other living + thing. If no living things present, treat 'THROW' as 'DROP'. */ + + if (dwarfn) + iobj = DWARF; + else { + /* No dwarves present; figure out pausible object. */ + k = 0; + for (i = 1; i < MAXOBJ; i++) { + if (at(i) && living(i)) { + iobj = i; + k++; + } + } + if (k == 0) { + vdrop(); + return; + } + /* It is a beastie of some sort. Is there more than one? + Don't kill the bird by default. */ + if (k > 1) { + rspeak(43); + return; + } else { + if (iobj == BIRD) { + vdrop(); + return; + } + if (treasr(object) && at(TROLL)) + iobj = TROLL; + } + } + } + if (object == SWORD || object == BOTTLE) { + vbreak(); + return; + } + if (object == FLOWER && iobj == HIVE) + iobj = BEES; + if (edible(object) && living(iobj)) { + vfeed(); + return; + } + /* If not axe, same as drop... */ + if (object != AXE && iobj != TROLL) { + vdrop(); + return; + } + /* AXE is THROWN */ + msg = 48; + switch (iobj) { + case DRAGON: + if (g.prop[DRAGON] == 0) + msg = 152; + break; + case DWARF: + /* At a dwarf... */ + if (pct(75)) { + g.dseen[dwarfn] = g.dloc[dwarfn] = 0; + msg = 47; + ++g.dkill; + if (g.dkill == 1) + msg = 149; + } + break; + case BEAR: + /* This'll teach him to throw axe at the bear */ + if (g.prop[BEAR] == 0) { + msg = 164; + drop(AXE, g.loc); + g.fixed[AXE] = -1; + g.prop[AXE] = 1; + juggle(BEAR); + } + rspeak(msg); + return; + case WUMPUS: + /* Or the WUMPUS! */ + if (g.prop[WUMPUS] == 6) { + vdrop(); + return; + } else { + msg = 245; + g.prop[AXE] = 2; + if (g.prop[WUMPUS] == 0) { + drop(AXE, g.loc); + g.fixed[AXE] = -1; + juggle(iobj); + } else { + msg = 243; + destroy(AXE); + } + } + rspeak(msg); + return; + case DOG: + /* Or the nice doggie! */ + if (g.prop[DOG] != 1) { + msg = 248; + g.prop[AXE] = 3; + drop(AXE, g.loc); + g.fixed[AXE] = -1; + juggle(iobj); + } + rspeak(msg); + return; + case TROLL: + /* Snarf a treasure for the troll */ + if (object == AXE) { + msg = 158; + } else if (!treasr(object) || + (object == CASK && (liq(CASK) != WINE))) { + vdrop(); + return; + } else { + msg = 159; + drop(object, 0); + if (object == CASK) + g.place[WINE + 1] = 0; + move(TROLL, 0); + move((TROLL + MAXOBJ), 0); + drop(TROLL2, plac[TROLL]); + drop((TROLL2 + MAXOBJ), fixd[TROLL]); + juggle(CHASM); + rspeak(msg); + return; + } + break; + + default: + /* Otherwise it is an attack */ + verb = KILL; + object = iobj; + iobj = objs[objx]; + vkill(); + return; + } + + rspeak(msg); + drop(AXE, g.loc); + g.newloc = g.loc; + describe(); +} + +/* + FIND might be carrying it, or it might be here. else give caveat. +*/ +void vfind() +{ + int msg; + + if (at(object) || + (liq(BOTTLE) == object && at(BOTTLE)) || + object == liqloc(g.loc)) + msg = 94; + else if (dcheck() && g.dflag >= 2 && object == DWARF) + msg = 94; + else if (g.closed) + msg = 138; + else if (at(object)) + msg = 24; + else { + actspk(verb); + return; + } + rspeak(msg); + return; +} + +/* + FEED +*/ +void vfeed() +{ + int msg; + + if (iobj == 0 || !living(iobj)) { + int i, k, kk; + + if (object == BIRD) { + rspeak(100); + return; + } + if (!living(object)) { + rspeak(noway()); + return; + } + /* See if there is anything edible around here. */ + + kk = 0; + k = 0; + for (i = 1; i < MAXOBJ; i++) + if (here(i) && edible(i)) { + k++; + kk = i; + } + iobj = object; + object = kk; + if (k != 1 && !dead(iobj)) { + printf("What do you want to feed the %s\n", otxt[objx]); + objs[1] = 0; + objx = 0; + return; + } + } + /* Feed object ot indirect object */ + msg = 102; + switch (iobj) { + case DRAGON: + if (g.prop[DRAGON] != 0) + msg = noway(); + break; + case TROLL: + msg = 182; + break; + case SNAKE: + if (object == BIRD && !g.closed) { + msg = 101; + destroy(BIRD); + g.prop[BIRD] = 0; + g.tally2++; + } + break; + case DWARF: + msg = 103; + g.dflag++; + break; + case BEAR: + if (g.prop[BEAR] == 3) + msg = noway(); + if (g.prop[BEAR] == 1 || g.prop[BEAR] == 2) + msg = 264; + if (object == FOOD) + msg = 278; + if (object == HONEY) { + g.prop[BEAR] = 1; + g.fixed[AXE] = 0; + destroy(HONEY); + msg = 168; + } + break; + case DOG: + msg = 291; + if (object == FOOD && g.prop[DOG] != 1) { + msg = 249; + destroy(FOOD); + } + break; + case WUMPUS: + if (g.prop[WUMPUS] == 6) + msg = 326; + if (g.prop[WUMPUS] == 0) + msg = 327; + if (object == FOOD) + msg = 240; + break; + case BEES: + if (object == FLOWER) { + if (enclosed(FLOWER)) + extract(FLOWER); + drop(FLOWER, g.loc); + g.fixed[FLOWER] = -1; + g.prop[FLOWER] = 1; + drop(HONEY, g.loc); + juggle(HONEY); + msg = 267; + g.prop[HIVE] = 1; + } + } + rspeak(msg); + return; +} + +/* + FILL. object with iobj +*/ +void vfill() +{ + int msg, k; + + if (!vessel(object)) + msg = 313; + else { + if (iobj == 0) + iobj = liqloc(g.loc); + if (object == BOTTLE || object == CASK) { + k = (object == CASK) ? 1 : 0; + msg = 0; + if (iobj == 0) + msg = 304 + k; + if (liq(object) != 0) + msg = 302 + k; + if (msg != 0) { + rspeak(msg); + return; + } + msg = 306 + k; + if (iobj == OIL) + msg = 308 + k; + if (iobj == WINE) + msg = 310 + k; + g.prop[object] = (int) g.loc_attrib[g.loc] & 14; + g.place[iobj + k] = -1; + insert(iobj + k, object); + } else if (object == VASE) { + if (iobj == 0 || !holding(VASE)) { + rspeak(144); + return; + } + msg = 145; + g.prop[VASE] = 2; + g.fixed[VASE] = -1; + if (enclosed(object)) + extract(object); + drop(object, g.loc); + } else if (object == GRAIL) + msg = 298; + else + msg = 339; + } + rspeak(msg); +} + +/* + READ. Magazine in dwarvish, message we've seen, and ... oyster? +*/ +void vread() +{ + int msg; + + if (blind()) { + actspk(verb); + return; + } + if (object && iobj) { + rspeak(confuz()); + return; + } + msg = confuz(); + if (!object) + object = iobj; + switch (object) { + case BOOK: + case BOOK2: + msg = 142; + break; + case BILLBD: + msg = 361; + break; + case CARVNG: + msg = 372; + break; + case MAGAZINE: + msg = 190; + break; + case MESSAGE: + msg = 191; + break; + case POSTER: + msg = 370; + break; + case TABLET: + msg = 196; + break; + case OYSTER: + if (g.hinted[2] && holding(OYSTER)) + msg = 194; + if (!g.hinted[2] && holding(OYSTER) && g.closed) { + g.hinted[2] = yes(192, 193, 54); + return; + } + break; + } + rspeak(msg); + return; +} + +/* + BREAK. works for mirror in repository and, of course the + vase and bottle. Also the sword is more brittle than it appears. +*/ +void vbreak() +{ + int msg, k; + boolean it_broke; + + it_broke = FALSE; + msg = 146; + switch (object) { + case MIRROR: + msg = 148; + if (g.closed) { + rspeak(197); + dwarfend(); + return; + } + break; + case VASE: + if (g.prop[VASE] == 0) { + it_broke = TRUE; + msg = 198; + g.prop[VASE] = 2; + } + break; + case BOTTLE: + if (g.prop[BOTTLE] != 3) { + it_broke = TRUE; + k = liq(BOTTLE); + msg = 231; + g.prop[BOTTLE] = 3; + if (k) { + extract(k); + g.place[k] = 0; + } + } + break; + case SWORD: + msg = 29; + if (holding(SWORD)) { + msg = 279; + g.prop[SWORD] = 4; + it_broke = TRUE; + } + break; + } + if (it_broke) { + if (enclosed(object)) + extract(object); + if (holding(object)) + drop(object, g.loc); + g.fixed[object] = -1; + } + rspeak(msg); + return; +} + +/* + WAKE. only use is to disturb the dwarves or the Wumpus. + Other wumpus-wakers link here. +*/ +void vwake() +{ + int msg; + + msg = actmsg[verb]; + if (at(WUMPUS)) { + g.chase = TRUE; + g.prop[WUMPUS] = 1; + msg = 276; + } + if (at(DOG) && g.prop[DOG] == 1) + msg = 291; + if (object == DWARF && g.closed) { + rspeak(199); + dwarfend(); + return; + } + rspeak(msg); + return; +} + +/* + YANK. A variant of 'CARRY'. In general, not a good idea. + At most, it gets the cloak or a couple of snide comments. + */ +void vyank() +{ + if (toting(object)) + vdrop(); + else if (object == BEAR && g.prop[CHAIN]) + rspeak(205); + else if (object == CLOAK && g.prop[CLOAK] == 2) { + /* Cloak. big trouble ahead. */ + g.prop[ROCKS] = 1; + g.prop[CLOAK] = 0; + g.fixed[CLOAK] = 0; + carry(CLOAK, g.loc); + rspeak(241); + if (at(WUMPUS) && g.prop[WUMPUS] == 0) { + g.chase = 1; + g.prop[WUMPUS] = 1; + rspeak(276); + } + } else + vtake(); + return; +} + +/* + WEAR. Only good for jewels, ruby slippers, cloak & crown. + But he might try the sword. Anything else is ridiculous. + Another variant of 'CARRY'. + */ +void vwear() +{ + int msg; + + if (object == SWORD && g.prop[SWORD] != 3) + msg = 209; + else if (worn(object)) { + if (object == CLOAK && g.prop[CLOAK] == 2) + msg = 242; + else if (wearng(object)) + msg = (object == SHOES) ? 227 : 210; + else { + g.prop[object] = 1; + biton(object, WEARBT); + if (enclosed(object)) + extract(object); + if (holding(object)) + msg = 54; + else { + vtake(); + return; + } + } + } else { + printf("Just exactly how does one wear a %s\n", otxt[objx]); + return; + } + rspeak(msg); + return; +} + +/* + HIT. If not punching out telephone, assume attack. + */ +void vhit() +{ + if (at(WUMPUS) && g.prop[WUMPUS] == 0) { + vwake(); + return; + } + if (object != PHONE) { + vkill(); + return; + } else { + if (g.closed) { + rspeak(282); + dwarfend(); + return; + } + if (g.prop[PHONE] == 2) + rspeak(256); + else { + drop(SLUGS, g.loc); + g.prop[PHONE] = 2; + g.prop[BOOTH] = 2; + rspeak(257); + } + } + return; +} + +/* + ANSWER (telephone). Smartass for anything else. + */ +void vanswer() +{ + int msg; + + switch (object) { + case DWARF: + case WUMPUS: + case SNAKE: + case BEAR: + case DRAGON: + msg = 259; + break; + case TROLL: + msg = 258; + break; + case BIRD: + msg = 260; + break; + case PHONE: + if (g.prop[PHONE] != 0) + msg = 269; + else if (g.closed) { + rspeak(283); + normend(); + return; + } else { + msg = 261; + g.prop[PHONE] = 1; + g.prop[BOOTH] = 2; + } + break; + default: + msg = actmsg[verb]; + break; + } + rspeak(msg); + return; +} + +/* + BLOW. Joshua fit de battle of Jericho, and de walls ... + */ +void vblow() +{ + int msg, i, k; + + msg = actmsg[verb]; + if (object != 0 && iobj != 0) { + rspeak(msg); + return; + } + if (object == 0) + object = iobj; + iobj = 0; + if (object == 0) + msg = 268; + if (object == HORN) { + msg = outside(g.loc) ? 277 : 266; + if (at(WUMPUS)) { + rspeak(msg); + if (g.prop[WUMPUS] == 0) + vwake(); + return; + } else if (g.prop[WALL] != 1 && (g.loc == 102 || g.loc == 194)) { + k = g.loc == 194 ? 195 : 196; + msg = 265; + g.prop[WALL] = 1; + for (i = 1; i < MAXOBJ; i++) + if (g.place[i] == g.loc || g.fixed[i] == g.loc) + move(i, k); + g.newloc = k; + } + } + rspeak(msg); + return; +} + +/* + DIAL. No effect unless at phone. + */ +void vdial() +{ + if (object != PHONE) + actspk(verb); + else if (g.closed) { + rspeak(283); + normend(); + } else + rspeak(271); + return; +} + +/* + PLAY. Only for horn or lyre. + */ +void vplay() +{ + int msg; + + msg = actmsg[verb]; + if (object != 0 && iobj != 0) { + rspeak(confuz()); + return; + } + if (object == 0) + object = iobj; + if (object == HORN) { + vblow(); + return; + } + if (object == LYRE) { + msg = 287; + if (here(DOG) && !dead(DOG)) { + g.prop[DOG] = 1; + biton(DOG, DEADBT); + g.fixed[AXE] = 0; + g.prop[AXE] = 0; + msg = 288; + } + } + rspeak(msg); + return; +} + +/* + PICK/ PICK UP. Can pick flower & mushrooms, + But must 'PICK UP' everything else. + */ +void vpick() +{ + if (object == 0) + object = iobj; + iobj = 0; + if (object == FLOWER || object == MUSHRM || prep != 0) + vtake(); + else + rspeak(confuz()); + return; +} + +/* + PUT DOWN: equivalent to drop + PUT IN: if liquid, means fill + PUT ON: wear of drop + */ +void vput() +{ + if (prep == 0) { + printf("Where do you want to put the %s\n", otxt[objx]); + return; + } + if (prep == PREPIN) + vinsert(); + else { + /* PUT ON: wear or put object on iobj */ + if (prep == PREPON) { + if (object == 0) { + object = iobj; + otxt[objx] = iotxt[iobx]; + iobj = 0; + } + if (worn(object) || object == 0) + vwear(); + else + vdrop(); + } else { + /* PUT DOWN: "drop" */ + if (object == 0 && iobj == 0) { + if (object == 0) + object = iobj; + iobj = 0; + vdrop(); + } else + rspeak(noway()); + } + } + return; +} + +/* turn on/off */ +void vturn() +{ + if (!prep) + rspeak(confuz()); + else { + if (!object && iobj == LAMP) + object = LAMP; + if (object != LAMP) + rspeak(noway()); + else if (prep == PREPON) + von(); + else + voff(); + } + return; +} + +/* + GET (no prep): "take" + GET IN: "enter" + GET OUT: "leave" + */ +void vget() +{ + if (prep == 0 || prep == PREPFR) + vtake(); + else if (object == 0) { + object = iobj; + iobj = 0; + prep = 0; + vtake(); + } + return; +} + +/* + INSERT/PUT IN + */ +void vinsert() +{ + int msg; + + if (iobj == 0) { + printf("Where do you want to %s it?\n", vtxt[vrbx]); + return; + } + msg = noway(); + if (object == SWORD && iobj == ANVIL && g.prop[SWORD] == 0) + msg = 350; + if (!vessel(iobj)) { + rspeak(msg); + return; + } + msg = ck_obj(); + if (g.fixed[object]) { + rspeak(msg); + return; + } + if (object == iobj) { + rspeak(252); + return; + } + if (iobj == BOTTLE || iobj == CASK || iobj == VASE + || iobj == GRAIL || (object >= WATER && object <= WINE + 1)) { + object = iobj; + iobj = objs[objx]; + vfill(); + return; + } + if (!ajar(iobj)) { + rspeak(358); + return; + } + if (iobj == CHEST) { + if (object == BOAT) + msg = noway(); + else { + if (wearng(object)) + bitoff(object, WEARBT); + if (worn(object)) + g.prop[object] = 0; + if (enclosed(object)) + extract(object); + insert(object, iobj); + msg = 54; + } + rspeak(msg); + return; + } + /* Bird goes into cage and only cage */ + if (object == BIRD && iobj != CAGE) { + rspeak(351); + return; + } + if (object != BIRD && iobj == CAGE) { + rspeak(329); + return; + } + if (object == BIRD) { + prep = 0; + vtake(); + return; + } + /* Bar vase & pillow from safe, to force putting down on florr */ + if ((object == VASE || object == PILLOW) && iobj == SAFE) { + rspeak(329); + return; + } + if (object != RADIUM && iobj == SHIELD) { + rspeak(329); + return; + } + if (iobj == PHONE) { + if (object == COINS || object == SLUGS) { + destroy(object); + msg = 330; + } else + msg = 329; + rspeak(msg); + return; + } + if (iobj == VEND) { + if (object == COINS || object == SLUGS) { + destroy(object); + move(BATTERIES, g.loc); + if (g.prop[BATTERIES] == 1) { + rspeak(317); + g.prop[VEND] = 1; + } + g.prop[BATTERIES] = 0; + pspeak(BATTERIES, 0); + } else + rspeak(noway()); + return; + } + /* Put batteries in lamp. There is a glitch here, in that if he + tries to get a third set of batteries before the second set has + been inserted, the second set disappears! + ***fix this some time *** + */ + if (iobj == LAMP) { + if (object != BATTERIES || g.prop[BATTERIES] != 0) + msg = noway(); + else { + g.prop[BATTERIES] = 1; + if (enclosed(BATTERIES)) + extract(BATTERIES); + if (holding(BATTERIES)) + drop(BATTERIES, g.loc); + g.limit = 400; + g.prop[LAMP] = 1; + g.lmwarn = FALSE; + msg = 188; + } + rspeak(msg); + return; + } + if (!small(object)) + msg = 329; + else { + if (wearng(object)) + bitoff(object, WEARBT); + if (worn(object)) + g.prop[object] = 0; + if (enclosed(object)) + extract(object); + insert(object, iobj); + msg = 54; + } + rspeak(msg); + return; + +} + +/* Remove or take from */ +void vextract() +{ + int msg; + + if (object == RING && g.prop[RING] == 2) { + prep = 0; + iobj = 0; + vtake(); + return; + } + msg = 343; + if (iobj == 0) { + if (!enclosed(object)) + msg = 340; + iobj = -g.place[object]; + } + if (g.place[object] != -iobj) + msg = 341; + if (!ajar(iobj)) + msg = 335; + if (object == WATER || object == OIL || object == WINE) + msg = 342; + if (!toting(object) && ((burden(0) + burden(object)) > 15)) + msg = 92; + if (msg == 343) { + if (object == BIRD) { + vdrop(); + return; + } + extract(object); + } + rspeak(msg); + return; +} + +/* + lock. chain, grate, chest, elfin door + Here are the current lock/unlock messages & numbers: + 31 you have no keys. + 32 it has no lock. + 34 it's already locked. + 35 the grate is now locked. + 36 the grate is now unlocked. + 37 it was allready unlocked. + 55 you can't unlock the keys. + 171 The chain is now unlocked. + 172 The chain is now locked. + 173 There is nothing here to which the chain can be locked. + 224 Your keys are all too large. + 234 The wrought-iron door is now locked. + 235 The tiny door is now locked. + 236 The wrought-iron door is now unlocked. + 237 The tiny door is now unlocked. + 375 You don't have the right key. + 333 the chest is now locked. + 334 the chest is now unlocked. + 367 The safe's door swings shut. +*/ +void vlock() +{ + int msg, k; + + if (!hinged(object)) + { + printf("I don't know how to lock or unlock the %s\n", + otxt[objx]); + return; + } + else if (!locks(object)) + msg = 32; + else if (locked(object)) + msg = 34; + else if (!athand(KEYS) && !athand(SKEY) && object != SAFE) + msg = 31; + else { + msg = 375; + switch (object) { + case CHAIN: + if (!athand(KEYS)) + break; + msg = 173; + if (g.loc != plac[CHAIN]) + break; + msg = 172; + g.prop[CHAIN] = 2; + if (enclosed(CHAIN)) + extract(CHAIN); + if (holding(CHAIN)) + drop(CHAIN, g.loc); + g.fixed[CHAIN] = -1; + biton(CHAIN, LOCKBT); + bitoff(CHAIN, OPENBT); + break; + + case CHEST: + if (!athand(KEYS)) + break; + msg = 334; + biton(CHEST, LOCKBT); + bitoff(CHEST, OPENBT); + break; + + case TDOOR: + case TDOOR2: + msg = 224; + if (!toting(SKEY)) + break; + g.prop[TDOOR] = 0; + g.prop[TDOOR2] = 0; + msg = 234 + (TDOOR2 - object); + k = TDOOR + TDOOR2 - object; + biton(k, LOCKBT); + bitoff(k, OPENBT); + biton(object, LOCKBT); + bitoff(object, OPENBT); + break; + + case GRATE: + if (!athand(KEYS)) + break; + g.prop[GRATE] = 0; + msg = 35; + biton(GRATE, LOCKBT); + bitoff(GRATE, OPENBT); + break; + + case SAFE: + g.prop[SAFE] = 0; + msg = 367; + biton(SAFE, LOCKBT); + bitoff(SAFE, OPENBT); + break; + + } + } + rspeak(msg); +} + +/* + UNLOCK. chain, grate, chest, elfin door. +*/ +void vunlock() +{ + int msg, k; + + if (object == KEYS || object == SKEY) + msg = 55; + else if (!hinged(object)) + { + printf("I don't know how to lock or unlock the %s\n", + otxt[objx]); + return; + } + else if (!locked(object)) + msg = 37; + else if (!locks(object)) + msg = 32; + else if (object == SAFE) { + if (iobj == KEYS || iobj == SKEY) + msg = 368; + else + msg = 342; + } else if (!athand(KEYS) && !athand(SKEY)) + msg = 31; + else { + msg = 375; + switch (object) { + case CHAIN: + if (!athand(KEYS)) + break; + if (g.prop[BEAR] == 0) + msg = 41; + else { + msg = 171; + g.prop[CHAIN] = 0; + g.fixed[CHAIN] = 0; + if (g.prop[BEAR] != 3) + g.prop[BEAR] = 2; + g.fixed[BEAR] = 2 - g.prop[BEAR]; + bitoff(CHAIN, LOCKBT); + biton(CHAIN, OPENBT); + } + break; + case CHEST: + if (athand(KEYS)) { + msg = 333; + bitoff(CHEST, LOCKBT); + biton(CHEST, OPENBT); + } + break; + case TDOOR: + case TDOOR2: + /* Elvin door stuff to lock/unlock tiny door w/special key. + the damn thing is really at four places, and we want the + right messages if he only has 'BIG'keys (or no keys). + Also, he can unlock it either while he is big or small. */ + msg = 224; + if (!athand(SKEY)) + break; + if (g.closing) { + msg = 130; + if (!g.panic) + g.clock2 = 15; + g.panic = TRUE; + } else { + g.prop[TDOOR] = 1; + g.prop[TDOOR2] = 1; + msg = 234 + 2 + (TDOOR2 - object); + k = TDOOR + (TDOOR2 - object); + bitoff(k, LOCKBT); + biton(k, OPENBT); + bitoff(object, LOCKBT); + biton(object, OPENBT); + } + break; + case GRATE: + if (!athand(KEYS)) + break; + if (g.closing) { + msg = 130; + if (!g.panic) + g.clock2 = 15; + g.panic = TRUE; + } else { + g.prop[GRATE] = 1; + msg = 36; + bitoff(GRATE, LOCKBT); + biton(GRATE, OPENBT); + } + break; + default: + msg = 33; + } + } + rspeak(msg); +} + +/* + LOOK. +*/ +void vlook() +{ + int sloc; + + if (object != 0) { + rspeak(confuz()); + return; + } + /* Look into something (a container). */ + if (vessel(iobj)) { + if (!ajar(iobj) && opaque(iobj)) + rspeak(actmsg[verb]); + else if (g.holder[iobj] == 0) + rspeak(359); + else { + putchar(' '); + lookin(iobj); + } + + /* Look at something. If written, read it. */ + } else if (printed(iobj)) { + object = iobj; + iobj = 0; + vread(); + } else if (iobj == SPHERE) { + if (!inside(g.loc) || athand(SAPPHIRE)) + rspeak(42); + else { + rspeak(400); + printf(" "); + sloc = g.place[SAPPHIRE]; + if ((g.loc_attrib[sloc] % 2 == 0 || enclosed(SAPPHIRE)) + && sloc != 200 + && !g.place[LAMP] == sloc && g.prop[LAMP] != 0) + rspeak(401); + else + desclg(sloc); + if (sloc == 239 && !g.flg239) { + rspeak(403); + g.flg239 = TRUE; + } + printf(" "); + rspeak(402); + } + } else + printf("I see nothing special about the %s?\n", iotxt[iobx]); + return; +} diff --git a/commands/advent/vocab.c b/commands/advent/vocab.c new file mode 100755 index 000000000..16ad15cc1 --- /dev/null +++ b/commands/advent/vocab.c @@ -0,0 +1,594 @@ + +/* + look-up vocabulary word in lex-ordered table. words may have + two entries with different codes. if minimum acceptable type + = 0, then return minimum of different codes. last word CANNOT + have two entries(due to binary sort). + word is the word to look up. + type is the minimum acceptable value, + if != 0 return %1000 +*/ + +#include +#include +#include "advent.h" +#include "advdec.h" + +static _CONST struct wac wc[] = { + "\"spelunker\"", 1016, + "22", 2053, + "22", 3012, + "34", 2053, + "34", 3013, + "4-leafed", 5034, + "7", 2053, + "7", 3011, + "?", 3051, + "above", 29, + "abra", 3050, + "abracd", 3050, + "across", 42, + "alacaz", 3050, + "all", 1109, + "altar", 90, + "and", 6001, + "answer", 2035, + "anvil", 1091, + "ascend", 29, + "at", 4009, + "attack", 2012, + "awaken", 2029, + "awkward", 26, + "axe", 1028, + "back", 8, + "ball", 1120, + "barren", 40, + "bat", 1104, + "bats", 1104, + "batshit", 1104, + "batteries", 1039, + "beans", 1024, + "bear", 1035, + "bed", 16, + "bedquilt", 70, + "bee", 1087, + "beehive", 1097, + "bees", 1087, + "billboard", 1116, + "bird", 1101, + "bitch", 2048, + "black", 5006, + "blast", 2023, + "blow", 2036, + "blowup", 2023, + "boat", 1048, + "book", 1110, + "booth", 1093, + "bottle", 1020, + "box", 1055, + "brass", 5004, + "break", 2028, + "bridge", 89, + "brief", 2026, + "broken", 54, + "broom", 1114, + "brush", 1114, + "brush", 2054, + "building", 12, + "bumble", 1087, + "burn", 2047, + "cage", 1004, + "cake", 1107, /* value must be + mushrooms + 1 */ + "cakes", 1107, + "call", 2038, + "calm", 2010, + "canister", 1118, + "canyon", 25, + "cape", 1047, + "capture", 2001, + "carpet", 1040, + "carry", 2001, + "carving", 1115, + "cask", 1071, + "catch", 2001, + "cave", 67, + "cavern", 73, + "chain", 1064, + "chalice", 1070, + "chant", 2003, + "chasm", 1021, /* troll bridge */ + "chest", 1055, + "chimney", 78, + "clam", 1014, + "click", 85, + "climb", 56, + "cloak", 1047, + "close", 2006, + "clover", 1073, + "cobble", 18, + "coins", 1054, + "comb", 1096, + "complain", 2048, + "continue", 7, + "continue", 2011, + "crack", 33, + "crap", 3106, + "crap!", 3106, + "crawl", 17, + "cross", 69, + "crown", 1066, + "crystal", 5033, + "cup", 1070, + "cupcakes", 1107, + "d", 30, + "dark", 22, + "debris", 51, + "defile", 23, + "depression", 63, + "descend", 30, + "describe", 2052, + "detonate", 2023, + "devour", 2014, + "diagnose", 2051, + "dial", 2039, + "diamond", 1051, + "diamonds", 1051, + "dig", 3066, + "discard", 2002, + "disturb", 2029, + "doff", 2002, + "dog", 1098, + "dome", 35, + "don", 2033, + "door", 1041, /* giant door */ + "down", 30, + "down", 4008, + "downstream", 5, + "downward", 30, + "dragon", 1031, + "drawing", 1029, + "drink", 2015, + "drop", 2002, + "droplet", 1075, + "dump", 2002, + "dust", 2054, + "dwarf", 1017, + "dwarves", 1017, + "e", 43, + "east", 43, + "eat", 2014, + "egg", 1056, + "eggs", 1056, + "elfin", 5019, + "emerald", 1059, + "empty", 2013, + "enter", 3, + "entrance", 64, + "everything", 1109, + "examine", 2052, + "excavate", 3066, + "exit", 11, + "explore", 2011, + "extinguish", 2008, + "fee", 2025, + "fee", 3001, + "feed", 2021, + "fie", 2025, + "fie", 3002, + "fight", 2012, + "figure", 1027, + "fill", 2022, + "find", 2019, + "fissure", 1012, + "fling", 2017, + "floor", 58, + "flower", 1046, + "flowers", 1046, + "foe", 2025, + "foe", 3003, + "follow", 2011, + "foo", 2025, + "foo", 3004, + "food", 1019, + "forcd", 1, + "forest", 6, + "fork", 77, + "forward", 7, + "fountain", 1103, + "four-leafed", 5034, + "free", 2002, + "fresh", 5010, + "from", 4005, + "fuck", 3079, + "fuck!", 3079, + "fum", 2025, + "fum", 3005, + "gate", 2058, + "get", 2044, + "geyser", 1037, /* same as volcano */ + "giant", 27, + "giant", 5029, + "glowing", 5031, + "gnome", 1105, + "go", 2011, + "gold", 1050, + "golden", 5001, + "goto", 2011, + "grab", 2032, + "grail", 1070, + "grate", 1003, + "green", 5032, + "grey", 5032, + "gripe", 2048, + "grotto", 91, + "guano", 1104, + "gully", 13, + "h20", 1081, + "hall", 38, + "headlamp", 1002, + "health", 2051, + "heave", 2017, + "heels", 1067, + "help", 3051, + "hike", 2011, + "hill", 2, + "hit", 2034, + "hive", 1097, + "hocus", 3050, + "hole", 52, + "holy", 5021, + "honey", 1096, + "honeycomb", 1096, + "horn", 1052, + "hound", 1098, + "house", 12, + "hurl", 2017, + "i", 2020, + "ice", 88, + "ignite", 2023, + "in", 19, + "in", 4001, + "insert", 2045, + "inside", 19, + "inside", 4001, + "into", 4001, + "inventory", 2020, + "inward", 19, + "iron", 5011, + "issue", 1016, + "jar", 1020, + "jerk", 2032, + "jewelry", 1053, + "jewels", 1053, + "jump", 39, + "keep", 2001, + "keg", 1071, + "key", 1090, + "keys", 1102, + "kick", 2034, + "kill", 2012, + "knapsack", 1108, + "knife", 1018, + "knives", 1018, + "knoll", 81, + "l", 2052, + "lamp", 1002, + "lantern", 1002, + "lead", 5023, + "leaden", 5023, + "leap", 39, + "leather", 5024, + "leave", 11, + "leave", 2037, + "ledge", 83, + "left", 36, + "light", 1002, + "little", 5012, + "lock", 2049, + "look", 2052, + "lost", 3068, + "low", 24, + "lyre", 1068, + "machine", 1038, + "magazine", 1016, + "main", 76, + "map", 2057, + "message", 1036, + "metal", 5035, + "ming", 5016, + "mirror", 1023, + "mist", 3069, + "moss", 1040, + "mumble", 2003, + "mushroom", 1106, + "mushrooms", 1106, + "n", 45, + "ne", 47, + "nest", 1056, + "north", 45, + "northeast", 47, + "northwest", 50, + "nothing", 2005, + "nowhere", 21, + "nugget", 1050, + "null", 21, + "nw", 50, + "oak", 5022, + "oaken", 5022, + "off", 4006, + "office", 76, + "oil", 1083, /* in bottle */ + "on", 4002, + "onto", 4002, + "onward", 7, + "open", 2004, + "opensesame", 3050, + "oriental", 72, + "out", 11, + "outdoors", 32, + "outside", 11, + "over", 41, + "oyster", 1015, + "pantry", 57, + "passage", 23, + "pause", 2030, + "pearl", 1061, + "persian", 5002, + "peruse", 2027, + "peyote", 1106, + "phone", 1094, + "phonebooth", 1094, + "phuce", 82, + "pick", 2041, + "pillow", 1010, + "pirate", 1030, + "pirloc", 2059, + "piss", 3107, + "piss!", 3107, + "pit", 31, + "placate", 2010, + "plant", 1024, + "platinum", 5017, + "play", 2040, + "plover", 71, + "plugh", 65, + "pocus", 3050, + "pole", 1009, + "pool", 80, + "poster", 1113, + "pottery", 1058, + "pound", 2034, + "pour", 2013, + "pray", 92, + "prayer", 92, + "proceed", 2011, + "pull", 2032, + "punch", 2034, + "put", 2042, + "pyramid", 1060, + "q", 2018, + "quartz", 5036, + "quit", 2018, + "radium", 1119, + "rare", 5018, + "ration", 1019, + "read", 2027, + "refill", 2022, + "release", 2002, + "remove", 2046, + "reply", 2035, + "report", 2048, + "reservoir", 75, + "restore", 2031, + "retreat", 8, + "return", 8, + "right", 37, + "ring", 1072, + "road", 2, + "rock", 1119, + "rock", 15, + "rocks", 1092, + "rocks", 1115, + "rod", 1005, + "room", 59, + "rowboat", 1048, + "rub", 2016, + "ruby", 5020, + "rug", 1062, + "run", 2011, + "rusty", 5028, + "s", 46, + "sack", 1108, + "safe", 1112, + "saint-michel", 93, + "sandwich", 1019, + "sapphire", 1069, + "save", 2030, + "say", 2003, + "score", 2024, + "se", 48, + "secret", 66, + "sesame", 3050, + "shadowy", 5027, + "shake", 2009, + "shards", 1058, + "shatter", 2028, + "shazam", 3050, + "shelf", 83, + "shell", 74, + "shield", 1118, + "ship", 1048, + "shit", 3106, + "shit!", 3106, + "shoes", 1067, + "shut", 2006, + "silk", 5013, + "silken", 5013, + "silver", 5014, + "sing", 2003, + "slab", 61, + "slabroom", 61, + "slay", 2012, + "slide", 79, + "slippers", 1067, + "slit", 60, + "slugs", 1095, + "small", 5012, + "smash", 2028, + "snake", 1011, + "south", 46, + "southeast", 48, + "southwest", 49, + "spelunker", 1016, + "sphere", 1120, + "spices", 1063, + "stair", 10, + "stairs", 10, + "stalagmite", 1026, + "star", 5026, + "statue", 1074, + "steal", 2001, + "steel", 5025, + "steps", 1007, + "steps", 34, + "stick", 1049, + "sticks", 1049, + "stone", 1119, + "stop", 3139, + "stream", 14, + "strike", 2034, + "strum", 2040, + "suggest", 2048, + "surface", 20, + "suspend", 2030, + "sw", 49, + "sweep", 2054, + "swim", 3147, + "swing", 2009, + "sword", 1065, + "tablet", 1013, + "take", 2001, + "tame", 2010, + "tasty", 5030, + "telephone", 1094, + "terse", 2055, + "then", 6002, + "throw", 2017, + "thunder", 84, + "tiny", 5012, + "to", 4004, + "tome", 1110, + "toss", 2017, + "tote", 2001, + "travel", 2011, + "treasure", 5015, + "tree", 1074, + "tree", 3064, + "trees", 3064, + "trident", 1057, + "troll", 1033, + "tube", 1118, + "tunnel", 23, + "turn", 2043, + "u", 29, + "unbrief", 2026, + "unlock", 2050, + "unterse", 2055, + "up", 29, + "up", 4007, + "upon", 4002, + "upstream", 4, + "upward", 29, + "used", 5009, + "utter", 2003, + "valley", 9, + "vase", 1058, + "velvet", 5007, + "vending", 5008, + "view", 28, + "volcano", 1037, + "volume", 1110, + "w", 44, + "wake", 2029, + "waken", 2029, + "walk", 2011, + "wall", 53, + "wall", 1088, /* in blue grotto */ + "wand", 1005, + "water", 1081, /* in bottle */ + "wave", 2009, + "wear", 2033, + "west", 44, + "whack", 2034, + "where", 2019, + "whirl", 80, + "whirlpool", 80, + "whisk", 1114, + "whiskbroom", 1114, + "wicker", 5005, + "wine", 1085, /* in bottle */ + "with", 4003, + "wiz", 2056, + "wolf", 1098, + "wooden", 5003, + "worn", 5009, + "worn-out", 5009, + "wornout", 5009, + "wumpus", 1099, + "xyzzy", 62, + "y2", 55, + "yank", 2032 +}; + +#define MAXWC (sizeof(wc) / sizeof(struct wac)) + +_PROTOTYPE(int binary, (char *)); + +int vocab(word, type) +char *word; +int type; +{ + int v1, v2, temp; + + if ((v1 = binary(word)) >= 0) { + if (v1 > 0 && strcmp(word, wc[v1 - 1].aword) == 0) + v2 = v1 - 1; + else if (v1 < (MAXWC - 1) && strcmp(word, wc[v1 + 1].aword) == 0) + v2 = v1 + 1; + else + v2 = v1; + if (wc[v1].acode > wc[v2].acode) { + temp = v1; + v1 = v2; + v2 = temp; + } + if (type <= CLASS(wc[v1].acode)) + return (wc[v1].acode); + else if (type <= CLASS(wc[v2].acode)) + return (wc[v2].acode); + else + return (-1); + } else + return (-1); +} + +int binary(w) +char *w; +{ + int lo, mid, hi, check; + + lo = 0; + hi = MAXWC - 1; + do { + mid = (lo + hi) / 2; + check = strcmp(w, wc[mid].aword); + if (check == 0) + return (mid); + else if (check < 0) + hi = mid - 1; + else + lo = mid + 1; + } while (lo <= hi); + return (-1); +} diff --git a/commands/ash/Makefile b/commands/ash/Makefile new file mode 100755 index 000000000..117686011 --- /dev/null +++ b/commands/ash/Makefile @@ -0,0 +1,101 @@ +# Makefile for ash. + +SRCS= builtins.c cd.c dirent.c error.c eval.c exec.c expand.c input.c \ + jobs.c mail.c main.c memalloc.c miscbltin.c mystring.c nodes.c \ + options.c parser.c redir.c show.c signames.c syntax.c trap.c \ + output.c var.c + +OBJS= builtins.o cd.o dirent.o error.o eval.o exec.o expand.o input.o \ + jobs.o mail.o main.o memalloc.o miscbltin.o mystring.o nodes.o \ + options.o parser.o redir.o show.o signames.o syntax.o trap.o \ + output.o var.o init.o \ + bltin/echo.o bltin/expr.o bltin/operators.o bltin/regexp.o + +# +# Set READLINE in shell.h and add -ledit to LIBS if you want to use the +# editline package by Simmule Turner and Rich Salz. (The big, bloated +# and GPL contaminated FSF readline should work too.) +# +CPPFLAGS= -DSHELL -I. -D_MINIX -D_POSIX_SOURCE +CFLAGS= -wo -i $(CPPFLAGS) +LIBS= -ledit + +CLEANFILES= $(OBJS) \ + builtins.c builtins.h init.c mkinit mknodes mksignames mksyntax \ + nodes.c nodes.h signames.c signames.h syntax.c syntax.h token.def \ + bltin/operators.h bltin/operators.c + +all: sh + +sh: $(OBJS) + $(CC) $(CFLAGS) -o sh $(OBJS) $(LIBS) + install -S 12kw sh + +install: /usr/bin/ash /usr/bin/sh /bin/sh + +/usr/bin/ash: sh + install -cs -o bin $? $@ + +/usr/bin/sh: /usr/bin/ash + install -l $? $@ + +/bin/sh: /usr/bin/ash + install -lcs $? $@ + +clean: + rm -f $(CLEANFILES) sh core + +parser.o: token.def + +token.def: mktokens + sh mktokens + +builtins.c builtins.h: builtins.table shell.h + sh mkbuiltins shell.h builtins.table + +init.o: mkinit $(SRCS) + ./mkinit '$(CC) -c $(CFLAGS) init.c' $(SRCS) + +mkinit: mkinit.c + $(CC) $(CFLAGS) mkinit.c -o $@ + +nodes.c nodes.h: mknodes nodetypes nodes.c.pat + ./mknodes nodetypes nodes.c.pat + +mknodes: mknodes.c + $(CC) $(CFLAGS) mknodes.c -o $@ + +signames.c signames.h: mksignames + ./mksignames + +mksignames: mksignames.c + $(CC) $(CFLAGS) mksignames.c -o $@ + +syntax.c syntax.h: mksyntax + ./mksyntax + +mksyntax: mksyntax.c parser.h + $(CC) $(CFLAGS) mksyntax.c -o $@ + +bltin/operators.h: bltin/mkexpr bltin/binary_op bltin/unary_op + cd bltin && sh mkexpr + +bltin/echo.o: bltin/echo.c + cd bltin && $(CC) -I.. $(CFLAGS) -c echo.c + +bltin/expr.o: bltin/expr.c + cd bltin && $(CC) -I.. $(CFLAGS) -c expr.c + +bltin/operators.o: bltin/operators.c + cd bltin && $(CC) -I.. $(CFLAGS) -c operators.c + +bltin/regexp.o: bltin/regexp.c + cd bltin && $(CC) -I.. $(CFLAGS) -c regexp.c + +# Dependencies you say? This will have to do. +$(OBJS): error.h eval.h exec.h expand.h init.h input.h \ + jobs.h machdep.h mail.h main.h memalloc.h mystring.h options.h \ + output.h parser.h redir.h shell.h trap.h var.h \ + builtins.h nodes.h signames.h syntax.h + +bltin/expr.o bltin/operators.o: bltin/operators.h diff --git a/commands/ash/TOUR b/commands/ash/TOUR new file mode 100755 index 000000000..669c0a933 --- /dev/null +++ b/commands/ash/TOUR @@ -0,0 +1,348 @@ +# @(#)TOUR 5.1 (Berkeley) 3/7/91 + + A Tour through Ash + + Copyright 1989 by Kenneth Almquist. + + +DIRECTORIES: The subdirectory bltin contains commands which can +be compiled stand-alone. The rest of the source is in the main +ash directory. + +SOURCE CODE GENERATORS: Files whose names begin with "mk" are +programs that generate source code. A complete list of these +programs is: + + program intput files generates + ------- ------------ --------- + mkbuiltins builtins builtins.h builtins.c + mkinit *.c init.c + mknodes nodetypes nodes.h nodes.c + mksignames - signames.h signames.c + mksyntax - syntax.h syntax.c + mktokens - token.def + bltin/mkexpr unary_op binary_op operators.h operators.c + +There are undoubtedly too many of these. Mkinit searches all the +C source files for entries looking like: + + INIT { + x = 1; /* executed during initialization */ + } + + RESET { + x = 2; /* executed when the shell does a longjmp + back to the main command loop */ + } + + SHELLPROC { + x = 3; /* executed when the shell runs a shell procedure */ + } + +It pulls this code out into routines which are when particular +events occur. The intent is to improve modularity by isolating +the information about which modules need to be explicitly +initialized/reset within the modules themselves. + +Mkinit recognizes several constructs for placing declarations in +the init.c file. + INCLUDE "file.h" +includes a file. The storage class MKINIT makes a declaration +available in the init.c file, for example: + MKINIT int funcnest; /* depth of function calls */ +MKINIT alone on a line introduces a structure or union declara- +tion: + MKINIT + struct redirtab { + short renamed[10]; + }; +Preprocessor #define statements are copied to init.c without any +special action to request this. + +INDENTATION: The ash source is indented in multiples of six +spaces. The only study that I have heard of on the subject con- +cluded that the optimal amount to indent is in the range of four +to six spaces. I use six spaces since it is not too big a jump +from the widely used eight spaces. If you really hate six space +indentation, use the adjind (source included) program to change +it to something else. + +EXCEPTIONS: Code for dealing with exceptions appears in +exceptions.c. The C language doesn't include exception handling, +so I implement it using setjmp and longjmp. The global variable +exception contains the type of exception. EXERROR is raised by +calling error. EXINT is an interrupt. EXSHELLPROC is an excep- +tion which is raised when a shell procedure is invoked. The pur- +pose of EXSHELLPROC is to perform the cleanup actions associated +with other exceptions. After these cleanup actions, the shell +can interpret a shell procedure itself without exec'ing a new +copy of the shell. + +INTERRUPTS: In an interactive shell, an interrupt will cause an +EXINT exception to return to the main command loop. (Exception: +EXINT is not raised if the user traps interrupts using the trap +command.) The INTOFF and INTON macros (defined in exception.h) +provide uninterruptable critical sections. Between the execution +of INTOFF and the execution of INTON, interrupt signals will be +held for later delivery. INTOFF and INTON can be nested. + +MEMALLOC.C: Memalloc.c defines versions of malloc and realloc +which call error when there is no memory left. It also defines a +stack oriented memory allocation scheme. Allocating off a stack +is probably more efficient than allocation using malloc, but the +big advantage is that when an exception occurs all we have to do +to free up the memory in use at the time of the exception is to +restore the stack pointer. The stack is implemented using a +linked list of blocks. + +STPUTC: If the stack were contiguous, it would be easy to store +strings on the stack without knowing in advance how long the +string was going to be: + p = stackptr; + *p++ = c; /* repeated as many times as needed */ + stackptr = p; +The folloing three macros (defined in memalloc.h) perform these +operations, but grow the stack if you run off the end: + STARTSTACKSTR(p); + STPUTC(c, p); /* repeated as many times as needed */ + grabstackstr(p); + +We now start a top-down look at the code: + +MAIN.C: The main routine performs some initialization, executes +the user's profile if necessary, and calls cmdloop. Cmdloop is +repeatedly parses and executes commands. + +OPTIONS.C: This file contains the option processing code. It is +called from main to parse the shell arguments when the shell is +invoked, and it also contains the set builtin. The -i and -j op- +tions (the latter turns on job control) require changes in signal +handling. The routines setjobctl (in jobs.c) and setinteractive +(in trap.c) are called to handle changes to these options. + +PARSING: The parser code is all in parser.c. A recursive des- +cent parser is used. Syntax tables (generated by mksyntax) are +used to classify characters during lexical analysis. There are +three tables: one for normal use, one for use when inside single +quotes, and one for use when inside double quotes. The tables +are machine dependent because they are indexed by character vari- +ables and the range of a char varies from machine to machine. + +PARSE OUTPUT: The output of the parser consists of a tree of +nodes. The various types of nodes are defined in the file node- +types. + +Nodes of type NARG are used to represent both words and the con- +tents of here documents. An early version of ash kept the con- +tents of here documents in temporary files, but keeping here do- +cuments in memory typically results in significantly better per- +formance. It would have been nice to make it an option to use +temporary files for here documents, for the benefit of small +machines, but the code to keep track of when to delete the tem- +porary files was complex and I never fixed all the bugs in it. +(AT&T has been maintaining the Bourne shell for more than ten +years, and to the best of my knowledge they still haven't gotten +it to handle temporary files correctly in obscure cases.) + +The text field of a NARG structure points to the text of the +word. The text consists of ordinary characters and a number of +special codes defined in parser.h. The special codes are: + + CTLVAR Variable substitution + CTLENDVAR End of variable substitution + CTLBACKQ Command substitution + CTLBACKQ|CTLQUOTE Command substitution inside double quotes + CTLESC Escape next character + +A variable substitution contains the following elements: + + CTLVAR type name '=' [ alternative-text CTLENDVAR ] + +The type field is a single character specifying the type of sub- +stitution. The possible types are: + + VSNORMAL $var + VSMINUS ${var-text} + VSMINUS|VSNUL ${var:-text} + VSPLUS ${var+text} + VSPLUS|VSNUL ${var:+text} + VSQUESTION ${var?text} + VSQUESTION|VSNUL ${var:?text} + VSASSIGN ${var=text} + VSASSIGN|VSNUL ${var=text} + +In addition, the type field will have the VSQUOTE flag set if the +variable is enclosed in double quotes. The name of the variable +comes next, terminated by an equals sign. If the type is not +VSNORMAL, then the text field in the substitution follows, ter- +minated by a CTLENDVAR byte. + +Commands in back quotes are parsed and stored in a linked list. +The locations of these commands in the string are indicated by +CTLBACKQ and CTLBACKQ+CTLQUOTE characters, depending upon whether +the back quotes were enclosed in double quotes. + +The character CTLESC escapes the next character, so that in case +any of the CTL characters mentioned above appear in the input, +they can be passed through transparently. CTLESC is also used to +escape '*', '?', '[', and '!' characters which were quoted by the +user and thus should not be used for file name generation. + +CTLESC characters have proved to be particularly tricky to get +right. In the case of here documents which are not subject to +variable and command substitution, the parser doesn't insert any +CTLESC characters to begin with (so the contents of the text +field can be written without any processing). Other here docu- +ments, and words which are not subject to splitting and file name +generation, have the CTLESC characters removed during the vari- +able and command substitution phase. Words which are subject +splitting and file name generation have the CTLESC characters re- +moved as part of the file name phase. + +EXECUTION: Command execution is handled by the following files: + eval.c The top level routines. + redir.c Code to handle redirection of input and output. + jobs.c Code to handle forking, waiting, and job control. + exec.c Code to to path searches and the actual exec sys call. + expand.c Code to evaluate arguments. + var.c Maintains the variable symbol table. Called from expand.c. + +EVAL.C: Evaltree recursively executes a parse tree. The exit +status is returned in the global variable exitstatus. The alter- +native entry evalbackcmd is called to evaluate commands in back +quotes. It saves the result in memory if the command is a buil- +tin; otherwise it forks off a child to execute the command and +connects the standard output of the child to a pipe. + +JOBS.C: To create a process, you call makejob to return a job +structure, and then call forkshell (passing the job structure as +an argument) to create the process. Waitforjob waits for a job +to complete. These routines take care of process groups if job +control is defined. + +REDIR.C: Ash allows file descriptors to be redirected and then +restored without forking off a child process. This is accom- +plished by duplicating the original file descriptors. The redir- +tab structure records where the file descriptors have be dupli- +cated to. + +EXEC.C: The routine find_command locates a command, and enters +the command in the hash table if it is not already there. The +third argument specifies whether it is to print an error message +if the command is not found. (When a pipeline is set up, +find_command is called for all the commands in the pipeline be- +fore any forking is done, so to get the commands into the hash +table of the parent process. But to make command hashing as +transparent as possible, we silently ignore errors at that point +and only print error messages if the command cannot be found +later.) + +The routine shellexec is the interface to the exec system call. + +EXPAND.C: Arguments are processed in three passes. The first +(performed by the routine argstr) performs variable and command +substitution. The second (ifsbreakup) performs word splitting +and the third (expandmeta) performs file name generation. If the +"/u" directory is simulated, then when "/u/username" is replaced +by the user's home directory, the flag "didudir" is set. This +tells the cd command that it should print out the directory name, +just as it would if the "/u" directory were implemented using +symbolic links. + +VAR.C: Variables are stored in a hash table. Probably we should +switch to extensible hashing. The variable name is stored in the +same string as the value (using the format "name=value") so that +no string copying is needed to create the environment of a com- +mand. Variables which the shell references internally are preal- +located so that the shell can reference the values of these vari- +ables without doing a lookup. + +When a program is run, the code in eval.c sticks any environment +variables which precede the command (as in "PATH=xxx command") in +the variable table as the simplest way to strip duplicates, and +then calls "environment" to get the value of the environment. +There are two consequences of this. First, if an assignment to +PATH precedes the command, the value of PATH before the assign- +ment must be remembered and passed to shellexec. Second, if the +program turns out to be a shell procedure, the strings from the +environment variables which preceded the command must be pulled +out of the table and replaced with strings obtained from malloc, +since the former will automatically be freed when the stack (see +the entry on memalloc.c) is emptied. + +BUILTIN COMMANDS: The procedures for handling these are scat- +tered throughout the code, depending on which location appears +most appropriate. They can be recognized because their names al- +ways end in "cmd". The mapping from names to procedures is +specified in the file builtins, which is processed by the mkbuil- +tins command. + +A builtin command is invoked with argc and argv set up like a +normal program. A builtin command is allowed to overwrite its +arguments. Builtin routines can call nextopt to do option pars- +ing. This is kind of like getopt, but you don't pass argc and +argv to it. Builtin routines can also call error. This routine +normally terminates the shell (or returns to the main command +loop if the shell is interactive), but when called from a builtin +command it causes the builtin command to terminate with an exit +status of 2. + +The directory bltins contains commands which can be compiled in- +dependently but can also be built into the shell for efficiency +reasons. The makefile in this directory compiles these programs +in the normal fashion (so that they can be run regardless of +whether the invoker is ash), but also creates a library named +bltinlib.a which can be linked with ash. The header file bltin.h +takes care of most of the differences between the ash and the +stand-alone environment. The user should call the main routine +"main", and #define main to be the name of the routine to use +when the program is linked into ash. This #define should appear +before bltin.h is included; bltin.h will #undef main if the pro- +gram is to be compiled stand-alone. + +CD.C: This file defines the cd and pwd builtins. The pwd com- +mand runs /bin/pwd the first time it is invoked (unless the user +has already done a cd to an absolute pathname), but then +remembers the current directory and updates it when the cd com- +mand is run, so subsequent pwd commands run very fast. The main +complication in the cd command is in the docd command, which +resolves symbolic links into actual names and informs the user +where the user ended up if he crossed a symbolic link. + +SIGNALS: Trap.c implements the trap command. The routine set- +signal figures out what action should be taken when a signal is +received and invokes the signal system call to set the signal ac- +tion appropriately. When a signal that a user has set a trap for +is caught, the routine "onsig" sets a flag. The routine dotrap +is called at appropriate points to actually handle the signal. +When an interrupt is caught and no trap has been set for that +signal, the routine "onint" in error.c is called. + +OUTPUT: Ash uses it's own output routines. There are three out- +put structures allocated. "Output" represents the standard out- +put, "errout" the standard error, and "memout" contains output +which is to be stored in memory. This last is used when a buil- +tin command appears in backquotes, to allow its output to be col- +lected without doing any I/O through the UNIX operating system. +The variables out1 and out2 normally point to output and errout, +respectively, but they are set to point to memout when appropri- +ate inside backquotes. + +INPUT: The basic input routine is pgetc, which reads from the +current input file. There is a stack of input files; the current +input file is the top file on this stack. The code allows the +input to come from a string rather than a file. (This is for the +-c option and the "." and eval builtin commands.) The global +variable plinno is saved and restored when files are pushed and +popped from the stack. The parser routines store the number of +the current line in this variable. + +DEBUGGING: If DEBUG is defined in shell.h, then the shell will +write debugging information to the file $HOME/trace. Most of +this is done using the TRACE macro, which takes a set of printf +arguments inside two sets of parenthesis. Example: +"TRACE(("n=%d0, n))". The double parenthesis are necessary be- +cause the preprocessor can't handle functions with a variable +number of arguments. Defining DEBUG also causes the shell to +generate a core dump if it is sent a quit signal. The tracing +code is in show.c. diff --git a/commands/ash/bltin/LICENSE b/commands/ash/bltin/LICENSE new file mode 100755 index 000000000..83685c506 --- /dev/null +++ b/commands/ash/bltin/LICENSE @@ -0,0 +1,40 @@ + ASH GENERAL PUBLIC LICENSE + + 1. You may copy and distribute ash code or code derived from it in +source or object form, provided that you conspicuously and appropriately +publish on each copy a valid copyright notice "Copyright 1989 by Kenneth +Almquist." (or with whatever year is appropriate); keep intact the +notices on all files that refer to this License Agreement and to the +absence of any warranty; and give any other recipients of the ash program +a copy of this License Agreement along with the program. + + 2. You may not copy, sublicense, distribute or transfer ash except as +expressly provided under this License Agreement. Any attempt otherwise +to copy, sublicense, distribute or transfer ash is void and your rights +to use ash under this License agreement shall be automatically terminated. +However, parties who have received computer software programs from you +with this License Agreement will not have their licenses terminated so +long as such parties remain in full compliance. + + + NO WARRANTY + + Because ash is licensed free of charge, I provide absolutely no +warranty, to the extent permitted by applicable state law. Except +when otherwise stated in writing, Kenneth Almquist and/or other +parties provide ash "as is" without warranty of any kind, either +expressed or implied, including, but not limited to, the implied +warranties of merchantability and fitness for a particular purpose. +The entire risk as to the quality and performance of the program is +with you. Should the ash program prove defective, you assume the cost +of all necessary servicing, repair or correction. + + In no event unless required by applicable law will Kenneth Almquist +and/or any other party who may modify and redistribute ash as permitted +above, be liable to you for damages, including any lost profits, lost +monies, or other special, incidental or consequential damages arising +out of the use or inability to use (including but not limited to loss +of data or data being rendered inaccurate or losses sustained by third +parties or a failure of the program to operate with programs provided +by other parties) the program, even if you have been advised of the +possibility of such damages, or for any claim by any other party. diff --git a/commands/ash/bltin/binary_op b/commands/ash/bltin/binary_op new file mode 100755 index 000000000..9dd732b63 --- /dev/null +++ b/commands/ash/bltin/binary_op @@ -0,0 +1,25 @@ +# List of binary operators used by test/expr. +# +# Copyright 1989 by Kenneth Almquist. All rights reserved. +# This file is part of ash, which is distributed under the terms specified +# by the Ash General Public License. See the file named LICENSE. + +OR1 -o 1 +OR2 | 1 +AND1 -a 2 +AND2 & 2 +STREQ = 4 OP_STRING +STRNE != 4 OP_STRING +NEWER -newer 4 OP_STRING +EQ -eq 4 OP_INT +NE -ne 4 OP_INT +GT -gt 4 OP_INT +LT -lt 4 OP_INT +LE -le 4 OP_INT +GE -ge 4 OP_INT +PLUS + 5 OP_INT +MINUS - 5 OP_INT +TIMES * 6 OP_INT +DIVIDE / 6 OP_INT +REM % 6 OP_INT +MATCHPAT : 7 OP_STRING diff --git a/commands/ash/bltin/bltin.h b/commands/ash/bltin/bltin.h new file mode 100755 index 000000000..5859a01bc --- /dev/null +++ b/commands/ash/bltin/bltin.h @@ -0,0 +1,40 @@ +/* + * This file is included by programs which are optionally built into the + * shell. If SHELL is defined, we try to map the standard UNIX library + * routines to ash routines using defines. + * + * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. + * This file is part of ash, which is distributed under the terms specified + * by the Ash General Public License. See the file named LICENSE. + */ + +#include "../shell.h" +#include "../mystring.h" +#ifdef SHELL +#include "../output.h" +#define stdout out1 +#define stderr out2 +#define printf out1fmt +#define putc(c, file) outc(c, file) +#define putchar(c) out1c(c) +#define fprintf outfmt +#define fputs outstr +#define fflush flushout +#define INITARGS(argv) +#else +#undef NULL +#include +#undef main +#define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else +#endif + +#ifdef __STDC__ +pointer stalloc(int); +void error(char *, ...); +#else +pointer stalloc(); +void error(); +#endif + + +extern char *commandname; diff --git a/commands/ash/bltin/catf.c b/commands/ash/bltin/catf.c new file mode 100755 index 000000000..c7aee99c8 --- /dev/null +++ b/commands/ash/bltin/catf.c @@ -0,0 +1,88 @@ +/* + * Copy the files given as arguments to the standard output. The file + * name "-" refers to the standard input. + * + * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. + * This file is part of ash, which is distributed under the terms specified + * by the Ash General Public License. See the file named LICENSE. + */ + +#define main catfcmd + +#include "bltin.h" +#include "../error.h" +#include +#include + + +#ifdef SBUFSIZE +#define BUFSIZE() SBUFSIZE +#else +#ifdef MAXBSIZE +#define BUFSIZE() MAXBSIZE +#else +#define BUFSIZE() BSIZE +#endif +#endif + + +main(argc, argv) char **argv; { + char *filename; + char *buf = stalloc(BUFSIZE()); + int fd; + int i; +#ifdef SHELL + volatile int input; + struct jmploc jmploc; + struct jmploc *volatile savehandler; +#endif + + INITARGS(argv); +#ifdef SHELL + input = -1; + if (setjmp(jmploc.loc)) { + close(input); + handler = savehandler; + longjmp(handler, 1); + } + savehandler = handler; + handler = &jmploc; +#endif + while ((filename = *++argv) != NULL) { + if (filename[0] == '-' && filename[1] == '\0') { + fd = 0; + } else { +#ifdef SHELL + INTOFF; + if ((fd = open(filename, O_RDONLY)) < 0) + error("Can't open %s", filename); + input = fd; + INTON; +#else + if ((fd = open(filename, O_RDONLY)) < 0) { + fprintf(stderr, "catf: Can't open %s\n", filename); + exit(2); + } +#endif + } + while ((i = read(fd, buf, BUFSIZE())) > 0) { +#ifdef SHELL + if (out1 == &memout) { + register char *p; + for (p = buf ; --i >= 0 ; p++) { + outc(*p, &memout); + } + } else { + write(1, buf, i); + } +#else + write(1, buf, i); +#endif + } + if (fd != 0) + close(fd); + } +#ifdef SHELL + handler = savehandler; +#endif +} diff --git a/commands/ash/bltin/echo.c b/commands/ash/bltin/echo.c new file mode 100755 index 000000000..8855af2dd --- /dev/null +++ b/commands/ash/bltin/echo.c @@ -0,0 +1,74 @@ +/* + * Echo command. + * + * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. + * This file is part of ash, which is distributed under the terms specified + * by the Ash General Public License. See the file named LICENSE. + */ + +#define main echocmd + +#include "bltin.h" + +#undef eflag + + +main(argc, argv) char **argv; { + register char **ap; + register char *p; + register char c; + int count; + int nflag = 0; +#ifndef eflag + int eflag = 0; +#endif + + ap = argv; + if (argc) + ap++; + if ((p = *ap) != NULL) { + if (equal(p, "--")) { + ap++; + } + if (equal(p, "-n")) { + nflag++; + ap++; + } else if (equal(p, "-e")) { +#ifndef eflag + eflag++; +#endif + ap++; + } + } + while ((p = *ap++) != NULL) { + while ((c = *p++) != '\0') { + if (c == '\\' && eflag) { + switch (*p++) { + case 'b': c = '\b'; break; + case 'c': return 0; /* exit */ + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case '\\': break; /* c = '\\' */ + case '0': + c = 0; + count = 3; + while (--count >= 0 && (unsigned)(*p - '0') < 8) + c = (c << 3) + (*p++ - '0'); + break; + default: + p--; + break; + } + } + putchar(c); + } + if (*ap) + putchar(' '); + } + if (! nflag) + putchar('\n'); + return 0; +} diff --git a/commands/ash/bltin/error.c b/commands/ash/bltin/error.c new file mode 100755 index 000000000..7af94dd61 --- /dev/null +++ b/commands/ash/bltin/error.c @@ -0,0 +1,23 @@ +/* + * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. + * This file is part of ash, which is distributed under the terms specified + * by the Ash General Public License. See the file named LICENSE. + */ + +#include + +char *commandname; + + +void +#ifdef __STDC__ +error(char *msg, ...) { +#else +error(msg) + char *msg; + { +#endif + + fprintf(stderr, "%s: %s\n", commandname, msg); + exit(2); +} diff --git a/commands/ash/bltin/expr.c b/commands/ash/bltin/expr.c new file mode 100755 index 000000000..3e9305d86 --- /dev/null +++ b/commands/ash/bltin/expr.c @@ -0,0 +1,481 @@ +/* + * The expr and test commands. + * + * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. + * This file is part of ash, which is distributed under the terms specified + * by the Ash General Public License. See the file named LICENSE. + */ + + +#define main exprcmd + +#include "bltin.h" +#include "operators.h" +#include +#include + +#ifndef S_ISLNK +#define lstat stat +#define S_ISLNK(mode) (0) +#endif + +#define STACKSIZE 12 +#define NESTINCR 16 + +/* data types */ +#define STRING 0 +#define INTEGER 1 +#define BOOLEAN 2 + + +/* + * This structure hold a value. The type keyword specifies the type of + * the value, and the union u holds the value. The value of a boolean + * is stored in u.num (1 = TRUE, 0 = FALSE). + */ + +struct value { + int type; + union { + char *string; + long num; + } u; +}; + + +struct operator { + short op; /* which operator */ + short pri; /* priority of operator */ +}; + + +struct filestat { + int op; /* OP_FILE or OP_LFILE */ + char *name; /* name of file */ + int rcode; /* return code from stat */ + struct stat stat; /* status info on file */ +}; + + +extern char *match_begin[10]; /* matched string */ +extern short match_length[10]; /* defined in regexp.c */ +extern short number_parens; /* number of \( \) pairs */ + + +#ifdef __STDC__ +int expr_is_false(struct value *); +void expr_operator(int, struct value *, struct filestat *); +int lookup_op(char *, char *const*); +char *re_compile(char *); /* defined in regexp.c */ +int re_match(char *, char *); /* defined in regexp.c */ +long atol(const char *); +#else +int expr_is_false(); +void expr_operator(); +int lookup_op(); +char *re_compile(); /* defined in regexp.c */ +int re_match(); /* defined in regexp.c */ +long atol(); +#endif + + + +main(argc, argv) char **argv; { + char **ap; + char *opname; + char c; + char *p; + int print; + int nest; /* parenthises nesting */ + int op; + int pri; + int skipping; + int binary; + struct operator opstack[STACKSIZE]; + struct operator *opsp; + struct value valstack[STACKSIZE + 1]; + struct value *valsp; + struct filestat fs; + + INITARGS(argv); + c = **argv; + print = 1; + if (c == 't') + print = 0; + else if (c == '[') { + if (! equal(argv[argc - 1], "]")) + error("missing ]"); + argv[argc - 1] = NULL; + print = 0; + } + ap = argv + 1; + fs.name = NULL; + + /* + * We use operator precedence parsing, evaluating the expression + * as we parse it. Parentheses are handled by bumping up the + * priority of operators using the variable "nest." We use the + * variable "skipping" to turn off evaluation temporarily for the + * short circuit boolean operators. (It is important do the short + * circuit evaluation because under NFS a stat operation can take + * infinitely long.) + */ + + nest = 0; + skipping = 0; + opsp = opstack + STACKSIZE; + valsp = valstack; + if (*ap == NULL) { + valstack[0].type = BOOLEAN; + valstack[0].u.num = 0; + goto done; + } + for (;;) { + opname = *ap++; + if (opname == NULL) +syntax: error("syntax error"); + if (opname[0] == '(' && opname[1] == '\0') { + nest += NESTINCR; + continue; + } else if (*ap && (op = lookup_op(opname, unary_op)) >= 0) { + if (opsp == &opstack[0]) +overflow: error("Expression too complex"); + --opsp; + opsp->op = op; + opsp->pri = op_priority[op] + nest; + continue; + + } else { + valsp->type = STRING; + valsp->u.string = opname; + valsp++; + } + for (;;) { + opname = *ap++; + if (opname == NULL) { + if (nest != 0) + goto syntax; + pri = 0; + break; + } + if (opname[0] != ')' || opname[1] != '\0') { + if ((op = lookup_op(opname, binary_op)) < 0) + goto syntax; + op += FIRST_BINARY_OP; + pri = op_priority[op] + nest; + break; + } + if ((nest -= NESTINCR) < 0) + goto syntax; + } + while (opsp < &opstack[STACKSIZE] && opsp->pri >= pri) { + binary = opsp->op; + for (;;) { + valsp--; + c = op_argflag[opsp->op]; + if (c == OP_INT) { + if (valsp->type == STRING) + valsp->u.num = atol(valsp->u.string); + valsp->type = INTEGER; + } else if (c >= OP_STRING) { /* OP_STRING or OP_FILE */ + if (valsp->type == INTEGER) { + p = stalloc(32); +#ifdef SHELL + fmtstr(p, 32, "%d", valsp->u.num); +#else + sprintf(p, "%d", valsp->u.num); +#endif + valsp->u.string = p; + } else if (valsp->type == BOOLEAN) { + if (valsp->u.num) + valsp->u.string = "true"; + else + valsp->u.string = ""; + } + valsp->type = STRING; + if (c >= OP_FILE + && (fs.op != c + || fs.name == NULL + || ! equal(fs.name, valsp->u.string))) { + fs.op = c; + fs.name = valsp->u.string; + if (c == OP_FILE) { + fs.rcode = stat(valsp->u.string, + &fs.stat); + } else { + fs.rcode = lstat(valsp->u.string, + &fs.stat); + } + } + } + if (binary < FIRST_BINARY_OP) + break; + binary = 0; + } + if (! skipping) + expr_operator(opsp->op, valsp, &fs); + else if (opsp->op == AND1 || opsp->op == OR1) + skipping--; + valsp++; /* push value */ + opsp++; /* pop operator */ + } + if (opname == NULL) + break; + if (opsp == &opstack[0]) + goto overflow; + if (op == AND1 || op == AND2) { + op = AND1; + if (skipping || expr_is_false(valsp - 1)) + skipping++; + } + if (op == OR1 || op == OR2) { + op = OR1; + if (skipping || ! expr_is_false(valsp - 1)) + skipping++; + } + opsp--; + opsp->op = op; + opsp->pri = pri; + } +done: + if (print) { + if (valstack[0].type == STRING) + printf("%s\n", valstack[0].u.string); + else if (valstack[0].type == INTEGER) + printf("%ld\n", valstack[0].u.num); + else if (valstack[0].u.num != 0) + printf("true\n"); + } + return expr_is_false(&valstack[0]); +} + + +int +expr_is_false(val) + struct value *val; + { + if (val->type == STRING) { + if (val->u.string[0] == '\0') + return 1; + } else { /* INTEGER or BOOLEAN */ + if (val->u.num == 0) + return 1; + } + return 0; +} + + +/* + * Execute an operator. Op is the operator. Sp is the stack pointer; + * sp[0] refers to the first operand, sp[1] refers to the second operand + * (if any), and the result is placed in sp[0]. The operands are converted + * to the type expected by the operator before expr_operator is called. + * Fs is a pointer to a structure which holds the value of the last call + * to stat, to avoid repeated stat calls on the same file. + */ + +void +expr_operator(op, sp, fs) + int op; + struct value *sp; + struct filestat *fs; + { + int i; + struct stat st1, st2; + + switch (op) { + case NOT: + sp->u.num = expr_is_false(sp); + sp->type = BOOLEAN; + break; + case ISREAD: + i = 04; + goto permission; + case ISWRITE: + i = 02; + goto permission; + case ISEXEC: + i = 01; +permission: + if (fs->stat.st_uid == geteuid()) + i <<= 6; + else if (fs->stat.st_gid == getegid()) + i <<= 3; + goto filebit; /* true if (stat.st_mode & i) != 0 */ + case ISFILE: + i = S_IFREG; + goto filetype; + case ISDIR: + i = S_IFDIR; + goto filetype; + case ISCHAR: + i = S_IFCHR; + goto filetype; + case ISBLOCK: + i = S_IFBLK; + goto filetype; + case ISFIFO: +#ifdef S_IFIFO + i = S_IFIFO; + goto filetype; +#else + goto false; +#endif +filetype: + if ((fs->stat.st_mode & S_IFMT) == i && fs->rcode >= 0) { +true: + sp->u.num = 1; + } else { +false: + sp->u.num = 0; + } + sp->type = BOOLEAN; + break; + case ISSETUID: + i = S_ISUID; + goto filebit; + case ISSETGID: + i = S_ISGID; + goto filebit; + case ISSTICKY: + i = S_ISVTX; +filebit: + if (fs->stat.st_mode & i && fs->rcode >= 0) + goto true; + goto false; + case ISSIZE: + sp->u.num = fs->rcode >= 0? fs->stat.st_size : 0L; + sp->type = INTEGER; + break; + case ISLINK1: + case ISLINK2: + if (S_ISLNK(fs->stat.st_mode) && fs->rcode >= 0) + goto true; + fs->op = OP_FILE; /* not a symlink, so expect a -d or so next */ + goto false; + case NEWER: + if (stat(sp->u.string, &st1) != 0) { + sp->u.num = 0; + } else if (stat((sp + 1)->u.string, &st2) != 0) { + sp->u.num = 1; + } else { + sp->u.num = st1.st_mtime >= st2.st_mtime; + } + sp->type = INTEGER; + break; + case ISTTY: + sp->u.num = isatty(sp->u.num); + sp->type = BOOLEAN; + break; + case NULSTR: + if (sp->u.string[0] == '\0') + goto true; + goto false; + case STRLEN: + sp->u.num = strlen(sp->u.string); + sp->type = INTEGER; + break; + case OR1: + case AND1: + /* + * These operators are mostly handled by the parser. If we + * get here it means that both operands were evaluated, so + * the value is the value of the second operand. + */ + *sp = *(sp + 1); + break; + case STREQ: + case STRNE: + i = 0; + if (equal(sp->u.string, (sp + 1)->u.string)) + i++; + if (op == STRNE) + i = 1 - i; + sp->u.num = i; + sp->type = BOOLEAN; + break; + case EQ: + if (sp->u.num == (sp + 1)->u.num) + goto true; + goto false; + case NE: + if (sp->u.num != (sp + 1)->u.num) + goto true; + goto false; + case GT: + if (sp->u.num > (sp + 1)->u.num) + goto true; + goto false; + case LT: + if (sp->u.num < (sp + 1)->u.num) + goto true; + goto false; + case LE: + if (sp->u.num <= (sp + 1)->u.num) + goto true; + goto false; + case GE: + if (sp->u.num >= (sp + 1)->u.num) + goto true; + goto false; + case PLUS: + sp->u.num += (sp + 1)->u.num; + break; + case MINUS: + sp->u.num -= (sp + 1)->u.num; + break; + case TIMES: + sp->u.num *= (sp + 1)->u.num; + break; + case DIVIDE: + if ((sp + 1)->u.num == 0) + error("Division by zero"); + sp->u.num /= (sp + 1)->u.num; + break; + case REM: + if ((sp + 1)->u.num == 0) + error("Division by zero"); + sp->u.num %= (sp + 1)->u.num; + break; + case MATCHPAT: + { + char *pat; + + pat = re_compile((sp + 1)->u.string); + if (re_match(pat, sp->u.string)) { + if (number_parens > 0) { + sp->u.string = match_begin[1]; + sp->u.string[match_length[1]] = '\0'; + } else { + sp->u.num = match_length[0]; + sp->type = INTEGER; + } + } else { + if (number_parens > 0) { + sp->u.string[0] = '\0'; + } else { + sp->u.num = 0; + sp->type = INTEGER; + } + } + } + break; + } +} + + +int +lookup_op(name, table) + char *name; + char *const*table; + { + register char *const*tp; + register char const *p; + char c = name[1]; + + for (tp = table ; (p = *tp) != NULL ; tp++) { + if (p[1] == c && equal(p, name)) + return tp - table; + } + return -1; +} diff --git a/commands/ash/bltin/line.c b/commands/ash/bltin/line.c new file mode 100755 index 000000000..aa6970eed --- /dev/null +++ b/commands/ash/bltin/line.c @@ -0,0 +1,27 @@ +/* + * The line command. Reads one line from the standard input and writes it + * to the standard output. + * + * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. + * This file is part of ash, which is distributed under the terms specified + * by the Ash General Public License. See the file named LICENSE. + */ + +#define main linecmd + +#include "bltin.h" + + +main(argc, argv) char **argv; { + char c; + + for (;;) { + if (read(0, &c, 1) != 1) { + putchar('\n'); + return 1; + } + putchar(c); + if (c == '\n') + return 0; + } +} diff --git a/commands/ash/bltin/makefile.not b/commands/ash/bltin/makefile.not new file mode 100755 index 000000000..ed36c73f5 --- /dev/null +++ b/commands/ash/bltin/makefile.not @@ -0,0 +1,71 @@ +# Copyright (C) 1989 by Kenneth Almquist. All rights reserved. +# This file is part of ash, which is distributed under the terms specified +# by the Ash General Public License. See the file named LICENSE. + +LIBFILES=catfcmd.o echocmd.o exprcmd.o linecmd.o nlechocmd.o\ + operators.o regexp.o +DEBUG=-g +CFLAGS=$(DEBUG) +#CC=gcc + +all:$P bltinlib.a catf echo expr line nlecho true umask + +bltinlib.a:$P $(LIBFILES) + ar rc $@ $(LIBFILES) + +catf: catf.c bltin.h ../shell.h ../error.h error.o stalloc.o + $(CC) $(CFLAGS) -o $@ catf.c error.o stalloc.o + +catfcmd.o: catf.c bltin.h ../shell.h ../error.h + $(CC) -DSHELL $(CFLAGS) -c catf.c + mv catf.o $@ + +expr: expr.c bltin.h ../shell.h operators.h operators.o regexp.o error.o stalloc.o + $(CC) $(CFLAGS) -o $@ expr.c operators.o regexp.o error.o stalloc.o + -rm -f test '[' + ln expr test + ln expr '[' + +exprcmd.o: expr.c bltin.h ../shell.h operators.h + $(CC) -DSHELL $(CFLAGS) -c expr.c + mv expr.o $@ + +operators.c operators.h: unary_op binary_op mkexpr + ./mkexpr + +operators.o: ../shell.h operators.h + +regexp.o: bltin.h ../shell.h + +echo: echo.c bltin.h ../shell.h + $(CC) $(CFLAGS) -o $@ echo.c + +echocmd.o: echo.c bltin.h ../shell.h + $(CC) -DSHELL $(CFLAGS) -c echo.c + mv echo.o $@ + +line: line.c bltin.h ../shell.h + $(CC) $(CFLAGS) -o $@ line.c + +linecmd.o: line.c bltin.h ../shell.h + $(CC) -DSHELL $(CFLAGS) -c line.c + mv line.o $@ + +nlecho: nlecho.c bltin.h ../shell.h + $(CC) $(CFLAGS) -o $@ nlecho.c + +nlechocmd.o: nlecho.c bltin.h ../shell.h + $(CC) -DSHELL $(CFLAGS) -c nlecho.c + mv nlecho.o $@ + +umask: umask.c bltin.h + $(CC) $(CFLAGS) -o $@ umask.c + +true: + > : + chmod 755 : + rm -f true + ln : true + +stalloc.o: ../shell.h + diff --git a/commands/ash/bltin/mkexpr b/commands/ash/bltin/mkexpr new file mode 100755 index 000000000..a5b9b9d87 --- /dev/null +++ b/commands/ash/bltin/mkexpr @@ -0,0 +1,66 @@ +# Copyright 1989 by Kenneth Almquist. All rights reserved. +# +# This file is part of ash. Ash is distributed under the terms specified +# by the Ash General Public License. See the file named LICENSE. + +# All calls to awk removed, because Minix bawk is deficient. (kjb) + +exec > operators.h +i=0 +sed -e '/^[^#]/!d' unary_op binary_op | while read line +do + set -$- $line + echo "#define $1 $i" + i=`expr $i + 1` +done +echo +echo "#define FIRST_BINARY_OP" `sed -e '/^[^#]/!d' unary_op | wc -l` +echo ' +#define OP_INT 1 /* arguments to operator are integer */ +#define OP_STRING 2 /* arguments to operator are string */ +#define OP_FILE 3 /* argument is a file name */ +#define OP_LFILE 4 /* argument is a file name of a symlink? */ + +extern char *const unary_op[]; +extern char *const binary_op[]; +extern const char op_priority[]; +extern const char op_argflag[];' + +exec > operators.c +echo '/* + * Operators used in the expr/test command. + */ + +#include "../shell.h" +#include "operators.h" + +char *const unary_op[] = {' +sed -e '/^[^#]/!d + s/[ ][ ]*/ /g + s/^[^ ][^ ]* \([^ ][^ ]*\).*/ "\1",/ + ' unary_op +echo ' NULL +}; + +char *const binary_op[] = {' +sed -e '/^[^#]/!d + s/[ ][ ]*/ /g + s/^[^ ][^ ]* \([^ ][^ ]*\).*/ "\1",/ + ' binary_op +echo ' NULL +}; + +const char op_priority[] = {' +sed -e '/^[^#]/!d + s/[ ][ ]*/ /g + s/^[^ ][^ ]* [^ ][^ ]* \([^ ][^ ]*\).*/ \1,/ + ' unary_op binary_op +echo '}; + +const char op_argflag[] = {' +sed -e '/^[^#]/!d + s/[ ][ ]*/ /g + s/^[^ ][^ ]* [^ ][^ ]* [^ ][^ ]*$/& 0/ + s/^[^ ][^ ]* [^ ][^ ]* [^ ][^ ]* \([^ ][^ ]*\)/ \1,/ + ' unary_op binary_op +echo '};' diff --git a/commands/ash/bltin/nlecho.c b/commands/ash/bltin/nlecho.c new file mode 100755 index 000000000..ccfb79288 --- /dev/null +++ b/commands/ash/bltin/nlecho.c @@ -0,0 +1,25 @@ +/* + * Echo the command argument to the standard output, one line at a time. + * This command is useful for debugging th shell and whenever you what + * to output strings literally. + * + * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. + * This file is part of ash, which is distributed under the terms specified + * by the Ash General Public License. See the file named LICENSE. + */ + + +#define main nlechocmd + +#include "bltin.h" + + +main(argc, argv) char **argv; { + register char **ap; + + for (ap = argv + 1 ; *ap ; ap++) { + fputs(*ap, stdout); + putchar('\n'); + } + return 0; +} diff --git a/commands/ash/bltin/regexp.c b/commands/ash/bltin/regexp.c new file mode 100755 index 000000000..db546d638 --- /dev/null +++ b/commands/ash/bltin/regexp.c @@ -0,0 +1,299 @@ +/* + * Regular expression matching for expr(1). Bugs: The upper bound of + * a range specified by the \{ feature cannot be zero. + * + * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. + * This file is part of ash, which is distributed under the terms specified + * by the Ash General Public License. See the file named LICENSE. + */ + +#include "bltin.h" + + +#define RE_END 0 /* end of regular expression */ +#define RE_LITERAL 1 /* normal character follows */ +#define RE_DOT 2 /* "." */ +#define RE_CCL 3 /* "[...]" */ +#define RE_NCCL 4 /* "[^...]" */ +#define RE_LP 5 /* "\(" */ +#define RE_RP 6 /* "\)" */ +#define RE_MATCHED 7 /* "\digit" */ +#define RE_EOS 8 /* "$" matches end of string */ +#define RE_STAR 9 /* "*" */ +#define RE_RANGE 10 /* "\{num,num\}" */ + + + +char *match_begin[10]; +short match_length[10]; +short number_parens; +static int match(); + + + +char * +re_compile(pattern) + char *pattern; + { + register char *p; + register char c; + char *comp; + register char *q; + char *begin; + char *endp; + register int len; + int first; + int type; + char *stackp; + char stack[10]; + int paren_num; + int i; + char *malloc(); + + p = pattern; + if (*p == '^') + p++; + comp = q = malloc(2 * strlen(p) + 1); + begin = q; + stackp = stack; + paren_num = 0; + for (;;) { + switch (c = *p++) { + case '\0': + *q = '\0'; + goto out; + case '.': + *q++ = RE_DOT; + len = 1; + break; + case '[': + begin = q; + *q = RE_CCL; + if (*p == '^') { + *q = RE_NCCL; + p++; + } + q++; + first = 1; + while (*p != ']' || first == 1) { + if (p[1] == '-' && p[2] != ']') { + *q++ = '-'; + *q++ = p[0]; + *q++ = p[2]; + p += 3; + } else if (*p == '-') { + *q++ = '-'; + *q++ = '-'; + *q++ = '-'; + p++; + } else { + *q++ = *p++; + } + first = 0; + } + p++; + *q++ = '\0'; + len = q - begin; + break; + case '$': + if (*p != '\0') + goto dft; + *q++ = RE_EOS; + break; + case '*': + if (len == 0) + goto dft; + type = RE_STAR; +range: + i = (type == RE_RANGE)? 3 : 1; + endp = q + i; + begin = q - len; + do { + --q; + *(q + i) = *q; + } while (--len > 0); + q = begin; + *q++ = type; + if (type == RE_RANGE) { + i = 0; + while ((unsigned)(*p - '0') <= 9) + i = 10 * i + (*p++ - '0'); + *q++ = i; + if (*p != ',') { + *q++ = i; + } else { + p++; + i = 0; + while ((unsigned)(*p - '0') <= 9) + i = 10 * i + (*p++ - '0'); + *q++ = i; + } + if (*p != '\\' || *++p != '}') + error("RE error"); + p++; + } + q = endp; + break; + case '\\': + if ((c = *p++) == '(') { + if (++paren_num > 9) + error("RE error"); + *q++ = RE_LP; + *q++ = paren_num; + *stackp++ = paren_num; + len = 0; + } else if (c == ')') { + if (stackp == stack) + error("RE error"); + *q++ = RE_RP; + *q++ = *--stackp; + len = 0; + } else if (c == '{') { + type = RE_RANGE; + goto range; + } else if ((unsigned)(c - '1') < 9) { + /* should check validity here */ + *q++ = RE_MATCHED; + *q++ = c - '0'; + len = 2; + } else { + goto dft; + } + break; + default: +dft: *q++ = RE_LITERAL; + *q++ = c; + len = 2; + break; + } + } +out: + if (stackp != stack) + error("RE error"); + number_parens = paren_num; + return comp; +} + + + +re_match(pattern, string) + char *pattern; + char *string; + { + char **pp; + + match_begin[0] = string; + for (pp = &match_begin[1] ; pp <= &match_begin[9] ; pp++) + *pp = 0; + return match(pattern, string); +} + + + +static +match(pattern, string) + char *pattern; + char *string; + { + register char *p, *q; + int counting; + int low, high, count; + char *curpat; + char *start_count; + int negate; + int found; + char *r; + int len; + char c; + + p = pattern; + q = string; + counting = 0; + for (;;) { + if (counting) { + if (++count > high) + goto bad; + p = curpat; + } + switch (*p++) { + case RE_END: + match_length[0] = q - match_begin[0]; + return 1; + case RE_LITERAL: + if (*q++ != *p++) + goto bad; + break; + case RE_DOT: + if (*q++ == '\0') + goto bad; + break; + case RE_CCL: + negate = 0; + goto ccl; + case RE_NCCL: + negate = 1; +ccl: + found = 0; + c = *q++; + while (*p) { + if (*p == '-') { + if (c >= *++p && c <= *++p) + found = 1; + } else { + if (c == *p) + found = 1; + } + p++; + } + p++; + if (found == negate) + goto bad; + break; + case RE_LP: + match_begin[*p++] = q; + break; + case RE_RP: + match_length[*p] = q - match_begin[*p]; + p++; + break; + case RE_MATCHED: + r = match_begin[*p]; + len = match_length[*p++]; + while (--len >= 0) { + if (*q++ != *r++) + goto bad; + } + break; + case RE_EOS: + if (*q != '\0') + goto bad; + break; + case RE_STAR: + low = 0; + high = 32767; + goto range; + case RE_RANGE: + low = *p++; + high = *p++; + if (high == 0) + high = 32767; +range: + curpat = p; + start_count = q; + count = 0; + counting++; + break; + } + } +bad: + if (! counting) + return 0; + len = 1; + if (*curpat == RE_MATCHED) + len = match_length[curpat[1]]; + while (--count >= low) { + if (match(p, start_count + count * len)) + return 1; + } + return 0; +} diff --git a/commands/ash/bltin/stalloc.c b/commands/ash/bltin/stalloc.c new file mode 100755 index 000000000..381271ac5 --- /dev/null +++ b/commands/ash/bltin/stalloc.c @@ -0,0 +1,21 @@ +/* + * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. + * This file is part of ash, which is distributed under the terms specified + * by the Ash General Public License. See the file named LICENSE. + */ + +#include "../shell.h" + + +void error(); +pointer malloc(); + + +pointer +stalloc(nbytes) { + register pointer p; + + if ((p = malloc(nbytes)) == NULL) + error("Out of space"); + return p; +} diff --git a/commands/ash/bltin/umask.c b/commands/ash/bltin/umask.c new file mode 100755 index 000000000..5c1f54bf2 --- /dev/null +++ b/commands/ash/bltin/umask.c @@ -0,0 +1,19 @@ +/* + * Copyright (C) 1989 by Kenneth Almquist. All rights reserved. + * This file is part of ash, which is distributed under the terms specified + * by the Ash General Public License. See the file named LICENSE. + */ + +#include + + +main(argc, argv) char **argv; { + int mask; + + if (argc > 1) { + fprintf(stderr, "umask: only builtin version of umask can set value\n"); + exit(2); + } + printf("%.4o\n", umask(0)); + return 0; +} diff --git a/commands/ash/bltin/unary_op b/commands/ash/bltin/unary_op new file mode 100755 index 000000000..4a9ddccf7 --- /dev/null +++ b/commands/ash/bltin/unary_op @@ -0,0 +1,24 @@ +# List of unary operators used by test/expr. +# +# Copyright (C) 1989 by Kenneth Almquist. All rights reserved. +# This file is part of ash, which is distributed under the terms specified +# by the Ash General Public License. See the file named LICENSE. + +NOT ! 3 +ISREAD -r 12 OP_FILE +ISWRITE -w 12 OP_FILE +ISEXEC -x 12 OP_FILE +ISFILE -f 12 OP_FILE +ISDIR -d 12 OP_FILE +ISCHAR -c 12 OP_FILE +ISBLOCK -b 12 OP_FILE +ISFIFO -p 12 OP_FILE +ISSETUID -u 12 OP_FILE +ISSETGID -g 12 OP_FILE +ISSTICKY -k 12 OP_FILE +ISSIZE -s 12 OP_FILE +ISLINK1 -h 12 OP_LFILE +ISLINK2 -L 12 OP_LFILE +ISTTY -t 12 OP_INT +NULSTR -z 12 OP_STRING +STRLEN -n 12 OP_STRING diff --git a/commands/ash/builtins.table b/commands/ash/builtins.table new file mode 100755 index 000000000..864cdb3d3 --- /dev/null +++ b/commands/ash/builtins.table @@ -0,0 +1,83 @@ +#!/bin/sh - +# +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)builtins 5.1 (Berkeley) 3/7/91 + +# +# This file lists all the builtin commands. The first column is the name +# of a C routine. The -j flag, if present, specifies that this command +# is to be excluded from systems without job control. The rest of the line +# specifies the command name or names used to run the command. The entry +# for nullcmd, which is run when the user does not specify a command, must +# come first. +# +# Copyright (C) 1989 by Kenneth Almquist. All rights reserved. +# This file is part of ash, which is distributed under the terms specified +# by the Ash General Public License. See the file named LICENSE. + +bltincmd command +#alloccmd alloc +bgcmd -j bg +breakcmd break continue +#catfcmd catf +cdcmd cd chdir +dotcmd . +echocmd echo +evalcmd eval +execcmd exec +exitcmd exit +exportcmd export readonly +exprcmd expr test [ +fgcmd -j fg +getoptscmd getopts +hashcmd hash +jobidcmd jobid +jobscmd jobs +#lccmd lc +#linecmd line +localcmd local +#nlechocmd nlecho +pwdcmd pwd +readcmd read +returncmd return +setcmd set +setvarcmd setvar +shiftcmd shift +trapcmd trap +truecmd : true false +umaskcmd umask +unsetcmd unset +waitcmd wait diff --git a/commands/ash/cd.c b/commands/ash/cd.c new file mode 100755 index 000000000..d1baf3754 --- /dev/null +++ b/commands/ash/cd.c @@ -0,0 +1,372 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)cd.c 5.2 (Berkeley) 3/13/91"; +#endif /* not lint */ + +/* + * The cd and pwd commands. + */ + +#include "shell.h" +#include "var.h" +#include "nodes.h" /* for jobs.h */ +#include "jobs.h" +#include "options.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include +#include +#include + + +#ifdef __STDC__ +STATIC int docd(char *, int, int); +STATIC void updatepwd(char *); +STATIC void getpwd(void); +STATIC char *getcomponent(void); +#else +STATIC int docd(); +STATIC void updatepwd(); +STATIC void getpwd(); +STATIC char *getcomponent(); +#endif + + +char *curdir; /* current working directory */ +STATIC char *cdcomppath; + +#if UDIR || TILDE +extern int didudir; /* set if /u/logname or ~logname expanded */ +#endif + + +int +cdcmd(argc, argv) char **argv; { + char *dest; + char *path; + char *p; + struct stat statb; + char *padvance(); + int tohome= 0; + + nextopt(nullstr); + if ((dest = *argptr) == NULL) { + if ((dest = bltinlookup("HOME", 1)) == NULL) + error("HOME not set"); + tohome = 1; + } + if (*dest == '/' || (path = bltinlookup("CDPATH", 1)) == NULL) + path = nullstr; + while ((p = padvance(&path, dest)) != NULL) { + if (stat(p, &statb) >= 0 + && (statb.st_mode & S_IFMT) == S_IFDIR + && docd(p, strcmp(p, dest), tohome) >= 0) + return 0; + } + error("can't cd to %s", dest); +} + + +/* + * Actually do the chdir. If the name refers to symbolic links, we + * compute the actual directory name before doing the cd. In an + * interactive shell, print the directory name if "print" is nonzero + * or if the name refers to a symbolic link. We also print the name + * if "/u/logname" was expanded in it, since this is similar to a + * symbolic link. (The check for this breaks if the user gives the + * cd command some additional, unused arguments.) + */ + +#if SYMLINKS == 0 +STATIC int +docd(dest, print, tohome) + char *dest; + { +#if UDIR || TILDE + if (didudir) + print = 1; +#endif + INTOFF; + if (chdir(dest) < 0) { + INTON; + return -1; + } + updatepwd(dest); + INTON; + if (print && iflag) + out1fmt("%s\n", stackblock()); + return 0; +} + +#else + + + +STATIC int +docd(dest, print, tohome) + char *dest; + { + register char *p; + register char *q; + char *symlink; + char *component; + struct stat statb; + int first; + int i; + + TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, tohome)); +#if UDIR || TILDE + if (didudir) + print = 1; +#endif + +top: + cdcomppath = dest; + STARTSTACKSTR(p); + if (*dest == '/') { + STPUTC('/', p); + cdcomppath++; + } + first = 1; + while ((q = getcomponent()) != NULL) { + if (q[0] == '\0' || q[0] == '.' && q[1] == '\0') + continue; + if (! first) + STPUTC('/', p); + first = 0; + component = q; + while (*q) + STPUTC(*q++, p); + if (equal(component, "..")) + continue; + STACKSTRNUL(p); + if (lstat(stackblock(), &statb) < 0) + error("lstat %s failed", stackblock()); + if ((statb.st_mode & S_IFMT) != S_IFLNK) + continue; + + /* Hit a symbolic link. We have to start all over again. */ + print = 1; + STPUTC('\0', p); + symlink = grabstackstr(p); + i = (int)statb.st_size + 2; /* 2 for '/' and '\0' */ + if (cdcomppath != NULL) + i += strlen(cdcomppath); + p = stalloc(i); + if (readlink(symlink, p, (int)statb.st_size) < 0) { + error("readlink %s failed", stackblock()); + } + if (cdcomppath != NULL) { + p[(int)statb.st_size] = '/'; + scopy(cdcomppath, p + (int)statb.st_size + 1); + } else { + p[(int)statb.st_size] = '\0'; + } + if (p[0] != '/') { /* relative path name */ + char *r; + q = r = symlink; + while (*q) { + if (*q++ == '/') + r = q; + } + *r = '\0'; + dest = stalloc(strlen(symlink) + strlen(p) + 1); + scopy(symlink, dest); + strcat(dest, p); + } else { + dest = p; + } + goto top; + } + STPUTC('\0', p); + p = grabstackstr(p); + INTOFF; + /* The empty string is not a legal argument to chdir on a POSIX 1003.1 + * system. */ + if (p[0] != '\0' && chdir(p) < 0) { + INTON; + return -1; + } + updatepwd(p); + INTON; + if (print && !tohome && iflag) + out1fmt("%s\n", p); + return 0; +} +#endif /* SYMLINKS */ + + + +/* + * Get the next component of the path name pointed to by cdcomppath. + * This routine overwrites the string pointed to by cdcomppath. + */ + +STATIC char * +getcomponent() { + register char *p; + char *start; + + if ((p = cdcomppath) == NULL) + return NULL; + start = cdcomppath; + while (*p != '/' && *p != '\0') + p++; + if (*p == '\0') { + cdcomppath = NULL; + } else { + *p++ = '\0'; + cdcomppath = p; + } + return start; +} + + + +/* + * Update curdir (the name of the current directory) in response to a + * cd command. We also call hashcd to let the routines in exec.c know + * that the current directory has changed. + */ + +void hashcd(); + +STATIC void +updatepwd(dir) + char *dir; + { + char *new; + char *p; + + hashcd(); /* update command hash table */ + cdcomppath = stalloc(strlen(dir) + 1); + scopy(dir, cdcomppath); + STARTSTACKSTR(new); + if (*dir != '/') { + if (curdir == NULL) + return; + p = curdir; + while (*p) + STPUTC(*p++, new); + if (p[-1] == '/') + STUNPUTC(new); + } + while ((p = getcomponent()) != NULL) { + if (equal(p, "..")) { + while (new > stackblock() && (STUNPUTC(new), *new) != '/'); + } else if (*p != '\0' && ! equal(p, ".")) { + STPUTC('/', new); + while (*p) + STPUTC(*p++, new); + } + } + if (new == stackblock()) + STPUTC('/', new); + STACKSTRNUL(new); + if (curdir) + ckfree(curdir); + curdir = savestr(stackblock()); +} + + + +int +pwdcmd(argc, argv) char **argv; { + getpwd(); + out1str(curdir); + out1c('\n'); + return 0; +} + + + +/* + * Run /bin/pwd to find out what the current directory is. We suppress + * interrupts throughout most of this, but the user can still break out + * of it by killing the pwd program. If we already know the current + * directory, this routine returns immediately. + */ + +#define MAXPWD 256 + +STATIC void +getpwd() { + char buf[MAXPWD]; + char *p; + int i; + int status; + struct job *jp; + int pip[2]; + + if (curdir) + return; + INTOFF; + if (pipe(pip) < 0) + error("Pipe call failed"); + jp = makejob((union node *)NULL, 1); + if (forkshell(jp, (union node *)NULL, FORK_NOJOB) == 0) { + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + execl("/bin/pwd", "pwd", (char *)0); + error("Cannot exec /bin/pwd"); + } + close(pip[1]); + pip[1] = -1; + p = buf; + while ((i = read(pip[0], p, buf + MAXPWD - p)) > 0 + || i == -1 && errno == EINTR) { + if (i > 0) + p += i; + } + close(pip[0]); + pip[0] = -1; + status = waitforjob(jp); + if (status != 0) + error((char *)0); + if (i < 0 || p == buf || p[-1] != '\n') + error("pwd command failed"); + p[-1] = '\0'; + curdir = savestr(buf); + INTON; +} diff --git a/commands/ash/dirent.c b/commands/ash/dirent.c new file mode 100755 index 000000000..521fcc627 --- /dev/null +++ b/commands/ash/dirent.c @@ -0,0 +1,194 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)dirent.c 5.1 (Berkeley) 3/7/91"; +#endif /* not lint */ + +#include "shell.h" /* definitions for pointer, NULL, DIRENT, and BSD */ + +#if ! DIRENT + +#include +#include +#include +#include +#include + +#ifndef S_ISDIR /* macro to test for directory file */ +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif + +#ifdef BSD + +#ifdef __STDC__ +int stat(char *, struct stat *); +#else +int stat(); +#endif + + +/* + * The BSD opendir routine doesn't check that what is being opened is a + * directory, so we have to include the check in a wrapper routine. + */ + +#undef opendir + +DIR * +myopendir(dirname) + char *dirname; /* name of directory */ + { + struct stat statb; + + if (stat(dirname, &statb) != 0 || ! S_ISDIR(statb.st_mode)) { + errno = ENOTDIR; + return NULL; /* not a directory */ + } + return opendir(dirname); +} + +#else /* not BSD */ + +/* + * Dirent routines for old style file systems. + */ + +#ifdef __STDC__ +pointer malloc(unsigned); +void free(pointer); +int open(char *, int, ...); +int close(int); +int fstat(int, struct stat *); +#else +pointer malloc(); +void free(); +int open(); +int close(); +int fstat(); +#endif + + +DIR * +opendir(dirname) + char *dirname; /* name of directory */ + { + register DIR *dirp; /* -> malloc'ed storage */ + register int fd; /* file descriptor for read */ + struct stat statb; /* result of fstat() */ + +#ifdef O_NDELAY + fd = open(dirname, O_RDONLY|O_NDELAY); +#else + fd = open(dirname, O_RDONLY); +#endif + if (fd < 0) + return NULL; /* errno set by open() */ + + if (fstat(fd, &statb) != 0 || !S_ISDIR(statb.st_mode)) { + (void)close(fd); + errno = ENOTDIR; + return NULL; /* not a directory */ + } + + if ((dirp = (DIR *)malloc(sizeof(DIR))) == NULL) { + (void)close(fd); + errno = ENOMEM; + return NULL; /* not enough memory */ + } + + dirp->dd_fd = fd; + dirp->dd_nleft = 0; /* refill needed */ + + return dirp; +} + + + +int +closedir(dirp) + register DIR *dirp; /* stream from opendir() */ + { + register int fd; + + if (dirp == NULL) { + errno = EFAULT; + return -1; /* invalid pointer */ + } + + fd = dirp->dd_fd; + free((pointer)dirp); + return close(fd); +} + + + +struct dirent * +readdir(dirp) + register DIR *dirp; /* stream from opendir() */ + { + register struct direct *dp; + register char *p, *q; + register int i; + + do { + if ((dirp->dd_nleft -= sizeof (struct direct)) < 0) { + if ((i = read(dirp->dd_fd, + (char *)dirp->dd_buf, + DIRBUFENT*sizeof(struct direct))) <= 0) { + if (i == 0) + errno = 0; /* unnecessary */ + return NULL; /* EOF or error */ + } + dirp->dd_loc = dirp->dd_buf; + dirp->dd_nleft = i - sizeof (struct direct); + } + dp = dirp->dd_loc++; + } while (dp->d_ino == 0); + dirp->dd_entry.d_ino = dp->d_ino; + + /* now copy the name, nul terminating it */ + p = dp->d_name; + q = dirp->dd_entry.d_name; + i = DIRSIZ; + while (--i >= 0 && *p != '\0') + *q++ = *p++; + *q = '\0'; + return &dirp->dd_entry; +} + +#endif /* BSD */ +#endif /* DIRENT */ diff --git a/commands/ash/error.c b/commands/ash/error.c new file mode 100755 index 000000000..af4275e4b --- /dev/null +++ b/commands/ash/error.c @@ -0,0 +1,252 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)error.c 5.1 (Berkeley) 3/7/91"; +#endif /* not lint */ + +/* + * Errors and exceptions. + */ + +#include "shell.h" +#include "main.h" +#include "options.h" +#include "output.h" +#include "error.h" +#include +#include +#ifdef __STDC__ +#include "stdarg.h" +#else +#include +#endif +#include + + +/* + * Code to handle exceptions in C. + */ + +struct jmploc *handler; +int exception; +volatile int suppressint; +volatile int intpending; +char *commandname; + + +/* + * Called to raise an exception. Since C doesn't include exceptions, we + * just do a longjmp to the exception handler. The type of exception is + * stored in the global variable "exception". + */ + +void +exraise(e) { + if (handler == NULL) + abort(); + exception = e; + longjmp(handler->loc, 1); +} + + +/* + * Called from trap.c when a SIGINT is received. (If the user specifies + * that SIGINT is to be trapped or ignored using the trap builtin, then + * this routine is not called.) Suppressint is nonzero when interrupts + * are held using the INTOFF macro. The call to _exit is necessary because + * there is a short period after a fork before the signal handlers are + * set to the appropriate value for the child. (The test for iflag is + * just defensive programming.) + */ + +void +onint() { + if (suppressint) { + intpending++; + return; + } + intpending = 0; +#ifdef BSD + sigsetmask(0); +#endif + if (rootshell && iflag) + exraise(EXINT); + else + _exit(128 + SIGINT); +} + + + +void +error2(a, b) + char *a, *b; + { + error("%s: %s", a, b); +} + + +/* + * Error is called to raise the error exception. If the first argument + * is not NULL then error prints an error message using printf style + * formatting. It then raises the error exception. + */ + +#ifdef __STDC__ +void +error(char *msg, ...) { +#else +void +error(va_alist) + va_dcl + { + char *msg; +#endif + va_list ap; + + CLEAR_PENDING_INT; + INTOFF; +#ifdef __STDC__ + va_start(ap, msg); +#else + va_start(ap); + msg = va_arg(ap, char *); +#endif +#if DEBUG + if (msg) + TRACE(("error(\"%s\") pid=%d\n", msg, getpid())); + else + TRACE(("error(NULL) pid=%d\n", getpid())); +#endif + if (msg) { + if (commandname) + outfmt(&errout, "%s: ", commandname); + doformat(&errout, msg, ap); + out2c('\n'); + } + va_end(ap); + flushall(); + exraise(EXERROR); +} + + +#ifdef notdef /* These strange error messages only confuse. -- kjb */ +/* + * Table of error messages. + */ + +struct errname { + short errcode; /* error number */ + short action; /* operation which encountered the error */ + char *msg; /* text describing the error */ +}; + + +#define ALL (E_OPEN|E_CREAT|E_EXEC) + +STATIC const struct errname errormsg[] = { + EINTR, ALL, "interrupted", + EACCES, ALL, "permission denied", + EIO, ALL, "I/O error", + ENOENT, E_OPEN, "no such file", + ENOENT, E_CREAT, "directory nonexistent", + ENOENT, E_EXEC, "not found", + ENOTDIR, E_OPEN, "no such file", + ENOTDIR, E_CREAT, "directory nonexistent", + ENOTDIR, E_EXEC, "not found", + ENOEXEC, ALL, "not an executable", + EISDIR, ALL, "is a directory", +/* EMFILE, ALL, "too many open files", */ + ENFILE, ALL, "file table overflow", + ENOSPC, ALL, "file system full", +#ifdef EDQUOT + EDQUOT, ALL, "disk quota exceeded", +#endif +#ifdef ENOSR + ENOSR, ALL, "no streams resources", +#endif + ENXIO, ALL, "no such device or address", + EROFS, ALL, "read-only file system", + ETXTBSY, ALL, "text busy", +#ifdef SYSV + EAGAIN, E_EXEC, "not enough memory", +#endif + ENOMEM, ALL, "not enough memory", +#ifdef ENOLINK + ENOLINK, ALL, "remote access failed", +#endif +#ifdef EMULTIHOP + EMULTIHOP, ALL, "remote access failed", +#endif +#ifdef ECOMM + ECOMM, ALL, "remote access failed", +#endif +#ifdef ESTALE + ESTALE, ALL, "remote access failed", +#endif +#ifdef ETIMEDOUT + ETIMEDOUT, ALL, "remote access failed", +#endif +#ifdef ELOOP + ELOOP, ALL, "symbolic link loop", +#endif + E2BIG, E_EXEC, "argument list too long", +#ifdef ELIBACC + ELIBACC, E_EXEC, "shared library missing", +#endif + 0, 0, NULL +}; + + +/* + * Return a string describing an error. The returned string may be a + * pointer to a static buffer that will be overwritten on the next call. + * Action describes the operation that got the error. + */ + +char * +errmsg(e, action) { + const struct errname *ep; + static char buf[12]; + + for (ep = errormsg ; ep->errcode ; ep++) { + if (ep->errcode == e && (ep->action & action) != 0) + return ep->msg; + } + fmtstr(buf, sizeof buf, "error %d", e); + return buf; +} +#endif diff --git a/commands/ash/error.h b/commands/ash/error.h new file mode 100755 index 000000000..4cd22507a --- /dev/null +++ b/commands/ash/error.h @@ -0,0 +1,116 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)error.h 5.1 (Berkeley) 3/7/91 + */ + +/* + * Types of operations (passed to the errmsg routine). + */ + +#define E_OPEN 01 /* opening a file */ +#define E_CREAT 02 /* creating a file */ +#define E_EXEC 04 /* executing a program */ + + +/* + * We enclose jmp_buf in a structure so that we can declare pointers to + * jump locations. The global variable handler contains the location to + * jump to when an exception occurs, and the global variable exception + * contains a code identifying the exeception. To implement nested + * exception handlers, the user should save the value of handler on entry + * to an inner scope, set handler to point to a jmploc structure for the + * inner scope, and restore handler on exit from the scope. + */ + +#include + +struct jmploc { + jmp_buf loc; +}; + +extern struct jmploc *handler; +extern int exception; + +/* exceptions */ +#define EXINT 0 /* SIGINT received */ +#define EXERROR 1 /* a generic error */ +#define EXSHELLPROC 2 /* execute a shell procedure */ + + +/* + * These macros allow the user to suspend the handling of interrupt signals + * over a period of time. This is similar to SIGHOLD to or sigblock, but + * much more efficient and portable. (But hacking the kernel is so much + * more fun than worrying about efficiency and portability. :-)) + */ + +extern volatile int suppressint; +extern volatile int intpending; +extern char *commandname; /* name of command--printed on error */ + +#define INTOFF suppressint++ +#define INTON if (--suppressint == 0 && intpending) onint(); else +#define FORCEINTON {suppressint = 0; if (intpending) onint();} +#define CLEAR_PENDING_INT intpending = 0 +#define int_pending() intpending + +#ifdef __STDC__ +void exraise(int); +void onint(void); +void error2(char *, char *); +void error(char *, ...); +char *errmsg(int, int); +#else +void exraise(); +void onint(); +void error2(); +void error(); +char *errmsg(); +#endif + +/* Errmsg uses confusingly different messages. Prefer strerror(). -- kjb */ +#define errmsg(errno, action) strerror(errno) + + +/* + * BSD setjmp saves the signal mask, which violates ANSI C and takes time, + * so we use _setjmp instead. + */ + +#ifdef BSD +#define setjmp(jmploc) _setjmp(jmploc) +#define longjmp(jmploc, val) _longjmp(jmploc, val) +#endif diff --git a/commands/ash/eval.c b/commands/ash/eval.c new file mode 100755 index 000000000..ce0eb1773 --- /dev/null +++ b/commands/ash/eval.c @@ -0,0 +1,926 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)eval.c 5.3 (Berkeley) 4/12/91"; +#endif /* not lint */ + +/* + * Evaluate a command. + */ + +#include "shell.h" +#include "nodes.h" +#include "syntax.h" +#include "expand.h" +#include "parser.h" +#include "jobs.h" +#include "eval.h" +#include "builtins.h" +#include "options.h" +#include "exec.h" +#include "redir.h" +#include "input.h" +#include "output.h" +#include "trap.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include +#include + + +/* flags in argument to evaltree */ +#define EV_EXIT 01 /* exit after evaluating tree */ +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ +#define EV_BACKCMD 04 /* command executing within back quotes */ + + +/* reasons for skipping commands (see comment on breakcmd routine) */ +#define SKIPBREAK 1 +#define SKIPCONT 2 +#define SKIPFUNC 3 + +MKINIT int evalskip; /* set if we are skipping commands */ +STATIC int skipcount; /* number of levels to skip */ +MKINIT int loopnest; /* current loop nesting level */ +int funcnest; /* depth of function calls */ + + +char *commandname; +struct strlist *cmdenviron; +int exitstatus; /* exit status of last command */ + + +#ifdef __STDC__ +STATIC void evalloop(union node *); +STATIC void evalfor(union node *); +STATIC void evalcase(union node *, int); +STATIC void evalsubshell(union node *, int); +STATIC void expredir(union node *); +STATIC void evalpipe(union node *); +STATIC void evalcommand(union node *, int, struct backcmd *); +STATIC void prehash(union node *); +#else +STATIC void evalloop(); +STATIC void evalfor(); +STATIC void evalcase(); +STATIC void evalsubshell(); +STATIC void expredir(); +STATIC void evalpipe(); +STATIC void evalcommand(); +STATIC void prehash(); +#endif + + + +/* + * Called to reset things after an exception. + */ + +#ifdef mkinit +INCLUDE "eval.h" + +RESET { + evalskip = 0; + loopnest = 0; + funcnest = 0; +} + +SHELLPROC { + exitstatus = 0; +} +#endif + + + +/* + * The eval commmand. + */ + +evalcmd(argc, argv) + char **argv; +{ + char *p; + char *concat; + char **ap; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + while (*p) + STPUTC(*p++, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + evalstring(p); + } + return exitstatus; +} + + +/* + * Execute a command or commands contained in a string. + */ + +void +evalstring(s) + char *s; + { + union node *n; + struct stackmark smark; + + setstackmark(&smark); + setinputstring(s, 1); + while ((n = parsecmd(0)) != NEOF) { + evaltree(n, 0); + popstackmark(&smark); + } + popfile(); + popstackmark(&smark); +} + + + +/* + * Evaluate a parse tree. The value is left in the global variable + * exitstatus. + */ + +void +evaltree(n, flags) + union node *n; + { + if (n == NULL) { + TRACE(("evaltree(NULL) called\n")); + return; + } + TRACE(("evaltree(0x%x: %d) called\n", (int)n, n->type)); + switch (n->type) { + case NSEMI: + evaltree(n->nbinary.ch1, 0); + if (evalskip) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NAND: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus != 0) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NOR: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus == 0) + goto out; + evaltree(n->nbinary.ch2, flags); + break; + case NREDIR: + expredir(n->nredir.redirect); + redirect(n->nredir.redirect, REDIR_PUSH); + evaltree(n->nredir.n, flags); + popredir(); + break; + case NSUBSHELL: + evalsubshell(n, flags); + break; + case NBACKGND: + evalsubshell(n, flags); + break; + case NIF: { + int status = 0; + + evaltree(n->nif.test, EV_TESTED); + if (evalskip) + goto out; + if (exitstatus == 0) { + evaltree(n->nif.ifpart, flags); + status = exitstatus; + } else if (n->nif.elsepart) { + evaltree(n->nif.elsepart, flags); + status = exitstatus; + } + exitstatus = status; + break; + } + case NWHILE: + case NUNTIL: + evalloop(n); + break; + case NFOR: + evalfor(n); + break; + case NCASE: + evalcase(n, flags); + break; + case NDEFUN: + defun(n->narg.text, n->narg.next); + exitstatus = 0; + break; + case NPIPE: + evalpipe(n); + break; + case NCMD: + evalcommand(n, flags, (struct backcmd *)NULL); + break; + default: + out1fmt("Node type = %d\n", n->type); + flushout(&output); + break; + } +out: + if (pendingsigs) + dotrap(); + if ((flags & EV_EXIT) || (eflag == 1 && exitstatus && !(flags & EV_TESTED))) + exitshell(exitstatus); +} + + +STATIC void +evalloop(n) + union node *n; + { + int status; + + loopnest++; + status = 0; + for (;;) { + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip) { +skipping: if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + if (n->type == NWHILE) { + if (exitstatus != 0) + break; + } else { + if (exitstatus == 0) + break; + } + evaltree(n->nbinary.ch2, 0); + status = exitstatus; + if (evalskip) + goto skipping; + } + loopnest--; + exitstatus = status; +} + + + +STATIC void +evalfor(n) + union node *n; + { + struct arglist arglist; + union node *argp; + struct strlist *sp; + struct stackmark smark; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { + expandarg(argp, &arglist, 1); + if (evalskip) + goto out; + } + *arglist.lastp = NULL; + + exitstatus = 0; + loopnest++; + for (sp = arglist.list ; sp ; sp = sp->next) { + setvar(n->nfor.var, sp->text, 0); + evaltree(n->nfor.body, 0); + if (evalskip) { + if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + } + loopnest--; +out: + popstackmark(&smark); +} + + + +STATIC void +evalcase(n, flags) + union node *n; + { + union node *cp; + union node *patp; + struct arglist arglist; + struct stackmark smark; + + setstackmark(&smark); + arglist.lastp = &arglist.list; + expandarg(n->ncase.expr, &arglist, 0); + for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { + for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { + if (casematch(patp, arglist.list->text)) { + if (evalskip == 0) { + evaltree(cp->nclist.body, flags); + } + goto out; + } + } + } +out: + popstackmark(&smark); +} + + + +/* + * Kick off a subshell to evaluate a tree. + */ + +STATIC void +evalsubshell(n, flags) + union node *n; + { + struct job *jp; + int backgnd = (n->type == NBACKGND); + + expredir(n->nredir.redirect); + jp = makejob(n, 1); + if (forkshell(jp, n, backgnd) == 0) { + if (backgnd) + flags &=~ EV_TESTED; + redirect(n->nredir.redirect, 0); + evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ + } + if (! backgnd) { + INTOFF; + exitstatus = waitforjob(jp); + INTON; + } +} + + + +/* + * Compute the names of the files in a redirection list. + */ + +STATIC void +expredir(n) + union node *n; + { + register union node *redir; + + for (redir = n ; redir ; redir = redir->nfile.next) { + if (redir->type == NFROM + || redir->type == NTO + || redir->type == NAPPEND) { + struct arglist fn; + fn.lastp = &fn.list; + expandarg(redir->nfile.fname, &fn, 0); + redir->nfile.expfname = fn.list->text; + } + } +} + + + +/* + * Evaluate a pipeline. All the processes in the pipeline are children + * of the process creating the pipeline. (This differs from some versions + * of the shell, which make the last process in a pipeline the parent + * of all the rest.) + */ + +STATIC void +evalpipe(n) + union node *n; + { + struct job *jp; + struct nodelist *lp; + int pipelen; + int prevfd; + int pip[2]; + + TRACE(("evalpipe(0x%x) called\n", (int)n)); + pipelen = 0; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) + pipelen++; + INTOFF; + jp = makejob(n, pipelen); + prevfd = -1; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + prehash(lp->n); + pip[1] = -1; + if (lp->next) { + if (pipe(pip) < 0) { + close(prevfd); + error("Pipe call failed"); + } + } + if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { + INTON; + if (prevfd > 0) { + close(0); + copyfd(prevfd, 0); + close(prevfd); + } + if (pip[1] >= 0) { + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + } + evaltree(lp->n, EV_EXIT); + } + if (prevfd >= 0) + close(prevfd); + prevfd = pip[0]; + close(pip[1]); + } + INTON; + if (n->npipe.backgnd == 0) { + INTOFF; + exitstatus = waitforjob(jp); + TRACE(("evalpipe: job done exit status %d\n", exitstatus)); + INTON; + } +} + + + +/* + * Execute a command inside back quotes. If it's a builtin command, we + * want to save its output in a block obtained from malloc. Otherwise + * we fork off a subprocess and get the output of the command via a pipe. + * Should be called with interrupts off. + */ + +void +evalbackcmd(n, result) + union node *n; + struct backcmd *result; + { + int pip[2]; + struct job *jp; + struct stackmark smark; /* unnecessary */ + + setstackmark(&smark); + result->fd = -1; + result->buf = NULL; + result->nleft = 0; + result->jp = NULL; + if (n == NULL) { + /* `` */ + } else + if (n->type == NCMD) { + evalcommand(n, EV_BACKCMD, result); + } else { + if (pipe(pip) < 0) + error("Pipe call failed"); + jp = makejob(n, 1); + if (forkshell(jp, n, FORK_NOJOB) == 0) { + FORCEINTON; + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + evaltree(n, EV_EXIT); + } + close(pip[1]); + result->fd = pip[0]; + result->jp = jp; + } + popstackmark(&smark); + TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n", + result->fd, result->buf, result->nleft, result->jp)); +} + + + +/* + * Execute a simple command. + */ + +STATIC void +evalcommand(cmd, flags, backcmd) + union node *cmd; + struct backcmd *backcmd; + { + struct stackmark smark; + union node *argp; + struct arglist arglist; + struct arglist varlist; + char **argv; + int argc; + char **envp; + int varflag; + struct strlist *sp; + register char *p; + int mode; + int pip[2]; + struct cmdentry cmdentry; + struct job *jp; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + char *volatile savecmdname; + volatile struct shparam saveparam; + struct localvar *volatile savelocalvars; + volatile int e; + char *lastarg; + + /* First expand the arguments. */ + TRACE(("evalcommand(0x%x, %d) called\n", (int)cmd, flags)); + setstackmark(&smark); + arglist.lastp = &arglist.list; + varlist.lastp = &varlist.list; + varflag = 1; + for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { + p = argp->narg.text; + if (varflag && is_name(*p)) { + do { + p++; + } while (is_in_name(*p)); + if (*p == '=') { + expandarg(argp, &varlist, 0); + continue; + } + } + expandarg(argp, &arglist, 1); + varflag = 0; + } + *arglist.lastp = NULL; + *varlist.lastp = NULL; + expredir(cmd->ncmd.redirect); + argc = 0; + for (sp = arglist.list ; sp ; sp = sp->next) + argc++; + argv = stalloc(sizeof (char *) * (argc + 1)); + for (sp = arglist.list ; sp ; sp = sp->next) + *argv++ = sp->text; + *argv = NULL; + lastarg = NULL; + if (iflag && funcnest == 0 && argc > 0) + lastarg = argv[-1]; + argv -= argc; + + /* Print the command if xflag is set. */ + if (xflag == 1) { + outc('+', &errout); + for (sp = varlist.list ; sp ; sp = sp->next) { + outc(' ', &errout); + out2str(sp->text); + } + for (sp = arglist.list ; sp ; sp = sp->next) { + outc(' ', &errout); + out2str(sp->text); + } + outc('\n', &errout); + flushout(&errout); + } + + /* Now locate the command. */ + if (argc == 0) { + cmdentry.cmdtype = CMDBUILTIN; + cmdentry.u.index = BLTINCMD; + } else { + find_command(argv[0], &cmdentry, 1); + if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ + exitstatus = 2; + flushout(&errout); + popstackmark(&smark); + return; + } + /* implement the bltin builtin here */ + if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) { + for (;;) { + argv++; + if (--argc == 0) + break; + if ((cmdentry.u.index = find_builtin(*argv)) < 0) { + outfmt(&errout, "%s: not found\n", *argv); + exitstatus = 2; + flushout(&errout); + popstackmark(&smark); + return; + } + if (cmdentry.u.index != BLTINCMD) + break; + } + } + } + + /* Fork off a child process if necessary. */ + if (cmd->ncmd.backgnd + || cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0 + || (flags & EV_BACKCMD) != 0 + && (cmdentry.cmdtype != CMDBUILTIN + || cmdentry.u.index == DOTCMD + || cmdentry.u.index == EVALCMD)) { + jp = makejob(cmd, 1); + mode = cmd->ncmd.backgnd; + if (flags & EV_BACKCMD) { + mode = FORK_NOJOB; + if (pipe(pip) < 0) + error("Pipe call failed"); + } + if (forkshell(jp, cmd, mode) != 0) + goto parent; /* at end of routine */ + if (flags & EV_BACKCMD) { + FORCEINTON; + close(pip[0]); + if (pip[1] != 1) { + close(1); + copyfd(pip[1], 1); + close(pip[1]); + } + } + flags |= EV_EXIT; + } + + /* This is the child process if a fork occurred. */ + /* Execute the command. */ + if (cmdentry.cmdtype == CMDFUNCTION) { + trputs("Shell function: "); trargs(argv); + redirect(cmd->ncmd.redirect, REDIR_PUSH); + saveparam = shellparam; + shellparam.malloc = 0; + shellparam.nparam = argc - 1; + shellparam.p = argv + 1; + shellparam.optnext = NULL; + INTOFF; + savelocalvars = localvars; + localvars = NULL; + INTON; + if (setjmp(jmploc.loc)) { + if (exception == EXSHELLPROC) + freeparam((struct shparam *)&saveparam); + else { + freeparam(&shellparam); + shellparam = saveparam; + } + poplocalvars(); + localvars = savelocalvars; + handler = savehandler; + longjmp(handler->loc, 1); + } + savehandler = handler; + handler = &jmploc; + for (sp = varlist.list ; sp ; sp = sp->next) + mklocal(sp->text); + funcnest++; + evaltree(cmdentry.u.func, 0); + funcnest--; + INTOFF; + poplocalvars(); + localvars = savelocalvars; + freeparam(&shellparam); + shellparam = saveparam; + handler = savehandler; + popredir(); + INTON; + if (evalskip == SKIPFUNC) { + evalskip = 0; + skipcount = 0; + } + if (flags & EV_EXIT) + exitshell(exitstatus); + } else if (cmdentry.cmdtype == CMDBUILTIN) { + trputs("builtin command: "); trargs(argv); + mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; + if (flags == EV_BACKCMD) { + memout.nleft = 0; + memout.nextc = memout.buf; + memout.bufsize = 64; + mode |= REDIR_BACKQ; + } + redirect(cmd->ncmd.redirect, mode); + savecmdname = commandname; + cmdenviron = varlist.list; + e = -1; + if (setjmp(jmploc.loc)) { + e = exception; + exitstatus = (e == EXINT)? SIGINT+128 : 2; + goto cmddone; + } + savehandler = handler; + handler = &jmploc; + commandname = argv[0]; + argptr = argv + 1; + optptr = NULL; /* initialize nextopt */ + exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); + flushall(); +cmddone: + out1 = &output; + out2 = &errout; + freestdout(); + if (e != EXSHELLPROC) { + commandname = savecmdname; + if (flags & EV_EXIT) { + exitshell(exitstatus); + } + } + handler = savehandler; + if (e != -1) { + if (e != EXERROR || cmdentry.u.index == BLTINCMD + || cmdentry.u.index == DOTCMD + || cmdentry.u.index == EVALCMD + || cmdentry.u.index == EXECCMD) + exraise(e); + FORCEINTON; + } + if (cmdentry.u.index != EXECCMD) + popredir(); + if (flags == EV_BACKCMD) { + backcmd->buf = memout.buf; + backcmd->nleft = memout.nextc - memout.buf; + memout.buf = NULL; + } + } else { + trputs("normal command: "); trargs(argv); + clearredir(); + redirect(cmd->ncmd.redirect, 0); + if (varlist.list) { + p = stalloc(strlen(pathval()) + 1); + scopy(pathval(), p); + } else { + p = pathval(); + } + for (sp = varlist.list ; sp ; sp = sp->next) + setvareq(sp->text, VEXPORT|VSTACK); + envp = environment(); + shellexec(argv, envp, p, cmdentry.u.index); + /*NOTREACHED*/ + } + goto out; + +parent: /* parent process gets here (if we forked) */ + if (mode == 0) { /* argument to fork */ + INTOFF; + exitstatus = waitforjob(jp); + INTON; + } else if (mode == 2) { + backcmd->fd = pip[0]; + close(pip[1]); + backcmd->jp = jp; + } + +out: + if (lastarg) + setvar("_", lastarg, 0); + popstackmark(&smark); +} + + + +/* + * Search for a command. This is called before we fork so that the + * location of the command will be available in the parent as well as + * the child. The check for "goodname" is an overly conservative + * check that the name will not be subject to expansion. + */ + +STATIC void +prehash(n) + union node *n; + { + struct cmdentry entry; + + if (n->type == NCMD && goodname(n->ncmd.args->narg.text)) + find_command(n->ncmd.args->narg.text, &entry, 0); +} + + + +/* + * Builtin commands. Builtin commands whose functions are closely + * tied to evaluation are implemented here. + */ + +/* + * No command given, or a bltin command with no arguments. Set the + * specified variables. + */ + +bltincmd(argc, argv) char **argv; { + listsetvar(cmdenviron); + return exitstatus; +} + + +/* + * Handle break and continue commands. Break, continue, and return are + * all handled by setting the evalskip flag. The evaluation routines + * above all check this flag, and if it is set they start skipping + * commands rather than executing them. The variable skipcount is + * the number of loops to break/continue, or the number of function + * levels to return. (The latter is always 1.) It should probably + * be an error to break out of more loops than exist, but it isn't + * in the standard shell so we don't make it one here. + */ + +breakcmd(argc, argv) char **argv; { + int n; + + n = 1; + if (argc > 1) + n = number(argv[1]); + if (n > loopnest) + n = loopnest; + if (n > 0) { + evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; + skipcount = n; + } + return 0; +} + + +/* + * The return command. + */ + +returncmd(argc, argv) char **argv; { + int ret; + + ret = exitstatus; + if (argc > 1) + ret = number(argv[1]); + if (funcnest) { + evalskip = SKIPFUNC; + skipcount = 1; + } + return ret; +} + + +truecmd(argc, argv) char **argv; { + return strcmp(argv[0], "false") == 0 ? 1 : 0; +} + + +execcmd(argc, argv) char **argv; { + if (argc > 1) { + iflag = 0; /* exit on error */ + setinteractive(0); +#if JOBS + jflag = 0; + setjobctl(0); +#endif + shellexec(argv + 1, environment(), pathval(), 0); + + } + return 0; +} diff --git a/commands/ash/eval.h b/commands/ash/eval.h new file mode 100755 index 000000000..04499cb6c --- /dev/null +++ b/commands/ash/eval.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)eval.h 5.2 (Berkeley) 4/12/91 + */ + +extern char *commandname; /* currently executing command */ +extern int exitstatus; /* exit status of last command */ +extern struct strlist *cmdenviron; /* environment for builtin command */ + + +struct backcmd { /* result of evalbackcmd */ + int fd; /* file descriptor to read from */ + char *buf; /* buffer */ + int nleft; /* number of chars in buffer */ + struct job *jp; /* job structure for command */ +}; + + +#ifdef __STDC__ +void evalstring(char *); +union node; /* BLETCH for ansi C */ +void evaltree(union node *, int); +void evalbackcmd(union node *, struct backcmd *); +#else +void evalstring(); +void evaltree(); +void evalbackcmd(); +#endif + +/* in_function returns nonzero if we are currently evaluating a function */ +#define in_function() funcnest +extern int funcnest; diff --git a/commands/ash/exec.c b/commands/ash/exec.c new file mode 100755 index 000000000..ad7158eb7 --- /dev/null +++ b/commands/ash/exec.c @@ -0,0 +1,824 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)exec.c 5.2 (Berkeley) 3/13/91"; +#endif /* not lint */ + +/* + * When commands are first encountered, they are entered in a hash table. + * This ensures that a full path search will not have to be done for them + * on each invocation. + * + * We should investigate converting to a linear search, even though that + * would make the command name "hash" a misnomer. + */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" +#include "parser.h" +#include "redir.h" +#include "eval.h" +#include "exec.h" +#include "builtins.h" +#include "var.h" +#include "options.h" +#include "input.h" +#include "output.h" +#include "syntax.h" +#include "memalloc.h" +#include "error.h" +#include "init.h" +#include "mystring.h" +#include +#include +#include +#include +#include + + +#define CMDTABLESIZE 31 /* should be prime */ +#define ARB 1 /* actual size determined at run time */ + + + +struct tblentry { + struct tblentry *next; /* next entry in hash chain */ + union param param; /* definition of builtin function */ + short cmdtype; /* index identifying command */ + char rehash; /* if set, cd done since entry created */ + char cmdname[ARB]; /* name of command */ +}; + + +STATIC struct tblentry *cmdtable[CMDTABLESIZE]; +STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ + + +#ifdef __STDC__ +STATIC void tryexec(char *, char **, char **); +STATIC void execinterp(char **, char **); +STATIC void printentry(struct tblentry *); +STATIC void clearcmdentry(int); +STATIC struct tblentry *cmdlookup(char *, int); +STATIC void delete_cmd_entry(void); +#else +STATIC void tryexec(); +STATIC void execinterp(); +STATIC void printentry(); +STATIC void clearcmdentry(); +STATIC struct tblentry *cmdlookup(); +STATIC void delete_cmd_entry(); +#endif + + + +/* + * Exec a program. Never returns. If you change this routine, you may + * have to change the find_command routine as well. + */ + +void +shellexec(argv, envp, path, index) + char **argv, **envp; + char *path; + { + char *cmdname; + int e; + + if (strchr(argv[0], '/') != NULL) { + tryexec(argv[0], argv, envp); + e = errno; + } else { + e = ENOENT; + while ((cmdname = padvance(&path, argv[0])) != NULL) { + if (--index < 0 && pathopt == NULL) { + tryexec(cmdname, argv, envp); + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + } + stunalloc(cmdname); + } + } + error2(argv[0], errmsg(e, E_EXEC)); +} + + +STATIC void +tryexec(cmd, argv, envp) + char *cmd; + char **argv; + char **envp; + { + int e; + char *p; + +#ifdef SYSV + do { + execve(cmd, argv, envp); + } while (errno == EINTR); +#else + execve(cmd, argv, envp); +#endif +#if HASHBANG + e = errno; + if (e == ENOEXEC) { + initshellproc(); + setinputfile(cmd, 0); + commandname = arg0 = savestr(argv[0]); +#ifndef BSD + pgetc(); pungetc(); /* fill up input buffer */ + p = parsenextc; + if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { + argv[0] = cmd; + execinterp(argv, envp); + } +#endif + setparam(argv + 1); + exraise(EXSHELLPROC); + /*NOTREACHED*/ + } + errno = e; +#endif +} + + +#if !defined(BSD) && HASHBANG +/* + * Execute an interpreter introduced by "#!", for systems where this + * feature has not been built into the kernel. If the interpreter is + * the shell, return (effectively ignoring the "#!"). If the execution + * of the interpreter fails, exit. + * + * This code peeks inside the input buffer in order to avoid actually + * reading any input. It would benefit from a rewrite. + */ + +#define NEWARGS 5 + +STATIC void +execinterp(argv, envp) + char **argv, **envp; + { + int n; + char *inp; + char *outp; + char c; + char *p; + char **ap; + char *newargs[NEWARGS]; + int i; + char **ap2; + char **new; + + n = parsenleft - 2; + inp = parsenextc + 2; + ap = newargs; + for (;;) { + while (--n >= 0 && (*inp == ' ' || *inp == '\t')) + inp++; + if (n < 0) + goto bad; + if ((c = *inp++) == '\n') + break; + if (ap == &newargs[NEWARGS]) +bad: error("Bad #! line"); + STARTSTACKSTR(outp); + do { + STPUTC(c, outp); + } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); + STPUTC('\0', outp); + n++, inp--; + *ap++ = grabstackstr(outp); + } +#if !__minix + if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ + p = newargs[0]; + for (;;) { + if (equal(p, "sh") || equal(p, "ash")) { + return; + } + while (*p != '/') { + if (*p == '\0') + goto break2; + p++; + } + p++; + } +break2:; + } +#endif + i = (char *)ap - (char *)newargs; /* size in bytes */ + if (i == 0) + error("Bad #! line"); + for (ap2 = argv ; *ap2++ != NULL ; ); + new = ckmalloc(i + ((char *)ap2 - (char *)argv)); + ap = newargs, ap2 = new; + while ((i -= sizeof (char **)) >= 0) + *ap2++ = *ap++; + ap = argv; + while (*ap2++ = *ap++); + shellexec(new, envp, pathval(), 0); +} +#endif + + + +/* + * Do a path search. The variable path (passed by reference) should be + * set to the start of the path before the first call; padvance will update + * this value as it proceeds. Successive calls to padvance will return + * the possible path expansions in sequence. If an option (indicated by + * a percent sign) appears in the path entry then the global variable + * pathopt will be set to point to it; otherwise pathopt will be set to + * NULL. + */ + +char *pathopt; + +char * +padvance(path, name) + char **path; + char *name; + { + register char *p, *q; + char *start; + int len; + + if (*path == NULL) + return NULL; + start = *path; + for (p = start ; *p && *p != ':' && *p != '%' ; p++); + len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ + while (stackblocksize() < len) + growstackblock(); + q = stackblock(); + if (p != start) { + bcopy(start, q, p - start); + q += p - start; + *q++ = '/'; + } + strcpy(q, name); + pathopt = NULL; + if (*p == '%') { + pathopt = ++p; + while (*p && *p != ':') p++; + } + if (*p == ':') + *path = p + 1; + else + *path = NULL; + return stalloc(len); +} + + + +/*** Command hashing code ***/ + + +hashcmd(argc, argv) char **argv; { + struct tblentry **pp; + struct tblentry *cmdp; + int c; + int verbose; + struct cmdentry entry; + char *name; + + if (argc <= 1) { + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + printentry(cmdp); + } + } + return 0; + } + verbose = 0; + while ((c = nextopt("rv")) != '\0') { + if (c == 'r') { + clearcmdentry(0); + } else if (c == 'v') { + verbose++; + } + } + while ((name = *argptr) != NULL) { + if ((cmdp = cmdlookup(name, 0)) != NULL + && (cmdp->cmdtype == CMDNORMAL + || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) + delete_cmd_entry(); + find_command(name, &entry, 1); + if (verbose) { + if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ + cmdp = cmdlookup(name, 0); + printentry(cmdp); + } + flushall(); + } + argptr++; + } + return 0; +} + + +STATIC void +printentry(cmdp) + struct tblentry *cmdp; + { + int index; + char *path; + char *name; + + if (cmdp->cmdtype == CMDNORMAL) { + index = cmdp->param.index; + path = pathval(); + do { + name = padvance(&path, cmdp->cmdname); + stunalloc(name); + } while (--index >= 0); + out1str(name); + } else if (cmdp->cmdtype == CMDBUILTIN) { + out1fmt("builtin %s", cmdp->cmdname); + } else if (cmdp->cmdtype == CMDFUNCTION) { + out1fmt("function %s", cmdp->cmdname); +#if DEBUG + } else { + error("internal error: cmdtype %d", cmdp->cmdtype); +#endif + } + if (cmdp->rehash) + out1c('*'); + out1c('\n'); +} + + + +/* + * Resolve a command name. If you change this routine, you may have to + * change the shellexec routine as well. + */ + +void +find_command(name, entry, printerr) + char *name; + struct cmdentry *entry; + { + struct tblentry *cmdp; + int index; + int prev; + char *path; + char *fullname; + struct stat statb; + int e; + int i; + + /* If name contains a slash, don't use the hash table */ + if (strchr(name, '/') != NULL) { + entry->cmdtype = CMDNORMAL; + entry->u.index = 0; + return; + } + + /* If name is in the table, and not invalidated by cd, we're done */ + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->rehash == 0) + goto success; + + /* If %builtin not in path, check for builtin next */ + if (builtinloc < 0 && (i = find_builtin(name)) >= 0) { + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.index = i; + INTON; + goto success; + } + + /* We have to search path. */ + prev = -1; /* where to start */ + if (cmdp) { /* doing a rehash */ + if (cmdp->cmdtype == CMDBUILTIN) + prev = builtinloc; + else + prev = cmdp->param.index; + } + + path = pathval(); + e = ENOENT; + index = -1; +loop: + while ((fullname = padvance(&path, name)) != NULL) { + stunalloc(fullname); + index++; + if (pathopt) { + if (prefix("builtin", pathopt)) { + if ((i = find_builtin(name)) < 0) + goto loop; + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.index = i; + INTON; + goto success; + } else if (prefix("func", pathopt)) { + /* handled below */ + } else { + goto loop; /* ignore unimplemented options */ + } + } + /* if rehash, don't redo absolute path names */ + if (fullname[0] == '/' && index <= prev) { + if (index < prev) + goto loop; + TRACE(("searchexec \"%s\": no change\n", name)); + goto success; + } + while (stat(fullname, &statb) < 0) { +#ifdef SYSV + if (errno == EINTR) + continue; +#endif + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + goto loop; + } + e = EACCES; /* if we fail, this will be the error */ + if ((statb.st_mode & S_IFMT) != S_IFREG) + goto loop; + if (pathopt) { /* this is a %func directory */ + stalloc(strlen(fullname) + 1); + readcmdfile(fullname); + if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) + error("%s not defined in %s", name, fullname); + stunalloc(fullname); + goto success; + } + if (statb.st_uid == geteuid()) { + if ((statb.st_mode & 0100) == 0) + goto loop; + } else if (statb.st_gid == getegid()) { + if ((statb.st_mode & 010) == 0) + goto loop; + } else { +#if __minix_vmd || defined(BSD) + gid_t group_list[NGROUPS_MAX]; + int ngroups, i; + + ngroups = getgroups(NGROUPS_MAX, group_list); + + for (i = 0; i < ngroups; i++) { + if (statb.st_gid == group_list[i]) break; + } + if (i < ngroups) { + if ((statb.st_mode & 010) == 0) + goto loop; + } else +#endif + if ((statb.st_mode & 01) == 0) + goto loop; + } + TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); + INTOFF; + cmdp = cmdlookup(name, 1); + cmdp->cmdtype = CMDNORMAL; + cmdp->param.index = index; + INTON; + goto success; + } + + /* We failed. If there was an entry for this command, delete it */ + if (cmdp) + delete_cmd_entry(); + if (printerr) + outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); + entry->cmdtype = CMDUNKNOWN; + return; + +success: + cmdp->rehash = 0; + entry->cmdtype = cmdp->cmdtype; + entry->u = cmdp->param; +} + + + +/* + * Search the table of builtin commands. + */ + +int +find_builtin(name) + char *name; + { + const register struct builtincmd *bp; + + for (bp = builtincmd ; bp->name ; bp++) { + if (*bp->name == *name && equal(bp->name, name)) + return bp->code; + } + return -1; +} + + + +/* + * Called when a cd is done. Marks all commands so the next time they + * are executed they will be rehashed. + */ + +void +hashcd() { + struct tblentry **pp; + struct tblentry *cmdp; + + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (cmdp->cmdtype == CMDNORMAL + || cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0) + cmdp->rehash = 1; + } + } +} + + + +/* + * Called before PATH is changed. The argument is the new value of PATH; + * pathval() still returns the old value at this point. Called with + * interrupts off. + */ + +void +changepath(newval) + char *newval; + { + char *old, *new; + int index; + int firstchange; + int bltin; + + old = pathval(); + new = newval; + firstchange = 9999; /* assume no change */ + index = 0; + bltin = -1; + for (;;) { + if (*old != *new) { + firstchange = index; + if (*old == '\0' && *new == ':' + || *old == ':' && *new == '\0') + firstchange++; + old = new; /* ignore subsequent differences */ + } + if (*new == '\0') + break; + if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) + bltin = index; + if (*new == ':') { + index++; + } + new++, old++; + } + if (builtinloc < 0 && bltin >= 0) + builtinloc = bltin; /* zap builtins */ + if (builtinloc >= 0 && bltin < 0) + firstchange = 0; + clearcmdentry(firstchange); + builtinloc = bltin; +} + + +/* + * Clear out command entries. The argument specifies the first entry in + * PATH which has changed. + */ + +STATIC void +clearcmdentry(firstchange) { + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if (cmdp->cmdtype == CMDNORMAL && cmdp->param.index >= firstchange + || cmdp->cmdtype == CMDBUILTIN && builtinloc >= firstchange) { + *pp = cmdp->next; + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + +/* + * Delete all functions. + */ + +#ifdef mkinit +MKINIT void deletefuncs(); + +SHELLPROC { + deletefuncs(); +} +#endif + +void +deletefuncs() { + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if (cmdp->cmdtype == CMDFUNCTION) { + *pp = cmdp->next; + freefunc(cmdp->param.func); + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + INTON; +} + + + +/* + * Locate a command in the command hash table. If "add" is nonzero, + * add the command to the table if it is not already present. The + * variable "lastcmdentry" is set to point to the address of the link + * pointing to the entry, so that delete_cmd_entry can delete the + * entry. + */ + +struct tblentry **lastcmdentry; + + +STATIC struct tblentry * +cmdlookup(name, add) + char *name; + { + int hashval; + register char *p; + struct tblentry *cmdp; + struct tblentry **pp; + + p = name; + hashval = *p << 4; + while (*p) + hashval += *p++; + hashval &= 0x7FFF; + pp = &cmdtable[hashval % CMDTABLESIZE]; + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (equal(cmdp->cmdname, name)) + break; + pp = &cmdp->next; + } + if (add && cmdp == NULL) { + INTOFF; + cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB + + strlen(name) + 1); + cmdp->next = NULL; + cmdp->cmdtype = CMDUNKNOWN; + cmdp->rehash = 0; + strcpy(cmdp->cmdname, name); + INTON; + } + lastcmdentry = pp; + return cmdp; +} + + +/* + * Delete the command entry returned on the last lookup. + */ + +STATIC void +delete_cmd_entry() { + struct tblentry *cmdp; + + INTOFF; + cmdp = *lastcmdentry; + *lastcmdentry = cmdp->next; + ckfree(cmdp); + INTON; +} + + + +#ifdef notdef +void +getcmdentry(name, entry) + char *name; + struct cmdentry *entry; + { + struct tblentry *cmdp = cmdlookup(name, 0); + + if (cmdp) { + entry->u = cmdp->param; + entry->cmdtype = cmdp->cmdtype; + } else { + entry->cmdtype = CMDUNKNOWN; + entry->u.index = 0; + } +} +#endif + + +/* + * Add a new command entry, replacing any existing command entry for + * the same name. + */ + +void +addcmdentry(name, entry) + char *name; + struct cmdentry *entry; + { + struct tblentry *cmdp; + + INTOFF; + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + } + cmdp->cmdtype = entry->cmdtype; + cmdp->param = entry->u; + INTON; +} + + +/* + * Define a shell function. + */ + +void +defun(name, func) + char *name; + union node *func; + { + struct cmdentry entry; + + INTOFF; + entry.cmdtype = CMDFUNCTION; + entry.u.func = copyfunc(func); + addcmdentry(name, &entry); + INTON; +} + + +/* + * Delete a function if it exists. + */ + +void +unsetfunc(name) + char *name; + { + struct tblentry *cmdp; + + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { + freefunc(cmdp->param.func); + delete_cmd_entry(); + } +} diff --git a/commands/ash/exec.h b/commands/ash/exec.h new file mode 100755 index 000000000..cd333dc94 --- /dev/null +++ b/commands/ash/exec.h @@ -0,0 +1,75 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)exec.h 5.1 (Berkeley) 3/7/91 + */ + +/* values of cmdtype */ +#define CMDUNKNOWN -1 /* no entry in table for command */ +#define CMDNORMAL 0 /* command is an executable program */ +#define CMDBUILTIN 1 /* command is a shell builtin */ +#define CMDFUNCTION 2 /* command is a shell function */ + + +struct cmdentry { + int cmdtype; + union param { + int index; + union node *func; + } u; +}; + + +extern char *pathopt; /* set by padvance */ + +#ifdef __STDC__ +void shellexec(char **, char **, char *, int); +char *padvance(char **, char *); +void find_command(char *, struct cmdentry *, int); +int find_builtin(char *); +void hashcd(void); +void changepath(char *); +void defun(char *, union node *); +void unsetfunc(char *); +#else +void shellexec(); +char *padvance(); +void find_command(); +int find_builtin(); +void hashcd(); +void changepath(); +void defun(); +void unsetfunc(); +#endif diff --git a/commands/ash/expand.c b/commands/ash/expand.c new file mode 100755 index 000000000..8fbc82650 --- /dev/null +++ b/commands/ash/expand.c @@ -0,0 +1,1156 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)expand.c 5.1 (Berkeley) 3/7/91"; +#endif /* not lint */ + +/* + * Routines to expand arguments to commands. We have to deal with + * backquotes, shell variables, and file metacharacters. + */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" +#include "eval.h" +#include "expand.h" +#include "syntax.h" +#include "parser.h" +#include "jobs.h" +#include "options.h" +#include "var.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include +#include +#include +#include +#if USEGETPW +#include +#endif + +/* + * Structure specifying which parts of the string should be searched + * for IFS characters. + */ + +struct ifsregion { + struct ifsregion *next; /* next region in list */ + int begoff; /* offset of start of region */ + int endoff; /* offset of end of region */ + int nulonly; /* search for nul bytes only */ +}; + + +char *expdest; /* output of current string */ +struct nodelist *argbackq; /* list of back quote expressions */ +struct ifsregion ifsfirst; /* first struct in list of ifs regions */ +struct ifsregion *ifslastp; /* last struct in list */ +struct arglist exparg; /* holds expanded arg list */ +#if UDIR || TILDE +/* + * Set if the last argument processed had /u/logname or ~logname expanded. + * This variable is read by the cd command. + */ +int didudir; +#endif + +#ifdef __STDC__ +STATIC void argstr(char *, int); +STATIC void expbackq(union node *, int, int); +STATIC char *evalvar(char *, int); +STATIC int varisset(int); +STATIC void varvalue(int, int, int); +STATIC void recordregion(int, int, int); +STATIC void ifsbreakup(char *, struct arglist *); +STATIC void expandmeta(struct strlist *); +STATIC void expmeta(char *, char *); +STATIC void addfname(char *); +STATIC struct strlist *expsort(struct strlist *); +STATIC struct strlist *msort(struct strlist *, int); +STATIC int pmatch(char *, char *); +#else +STATIC void argstr(); +STATIC void expbackq(); +STATIC char *evalvar(); +STATIC int varisset(); +STATIC void varvalue(); +STATIC void recordregion(); +STATIC void ifsbreakup(); +STATIC void expandmeta(); +STATIC void expmeta(); +STATIC void addfname(); +STATIC struct strlist *expsort(); +STATIC struct strlist *msort(); +STATIC int pmatch(); +#endif +#if UDIR || TILDE +#ifdef __STDC__ +STATIC char *expudir(char *); +#else +STATIC char *expudir(); +#endif +#endif /* UDIR || TILDE */ + + + +/* + * Expand shell variables and backquotes inside a here document. + */ + +void +expandhere(arg, fd) + union node *arg; /* the document */ + int fd; /* where to write the expanded version */ + { + herefd = fd; + expandarg(arg, (struct arglist *)NULL, 0); + xwrite(fd, stackblock(), expdest - stackblock()); +} + + +/* + * Perform variable substitution and command substitution on an argument, + * placing the resulting list of arguments in arglist. If full is true, + * perform splitting and file name expansion. When arglist is NULL, perform + * here document expansion. + */ + +void +expandarg(arg, arglist, full) + union node *arg; + struct arglist *arglist; + { + struct strlist *sp; + char *p; + +#if UDIR || TILDE + didudir = 0; +#endif + argbackq = arg->narg.backquote; + STARTSTACKSTR(expdest); + ifsfirst.next = NULL; + ifslastp = NULL; + argstr(arg->narg.text, full); + if (arglist == NULL) + return; /* here document expanded */ + STPUTC('\0', expdest); + p = grabstackstr(expdest); + exparg.lastp = &exparg.list; + if (full) { + ifsbreakup(p, &exparg); + *exparg.lastp = NULL; + exparg.lastp = &exparg.list; + expandmeta(exparg.list); + } else { + sp = (struct strlist *)stalloc(sizeof (struct strlist)); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; + } + while (ifsfirst.next != NULL) { + struct ifsregion *ifsp; + INTOFF; + ifsp = ifsfirst.next->next; + ckfree(ifsfirst.next); + ifsfirst.next = ifsp; + INTON; + } + *exparg.lastp = NULL; + if (exparg.list) { + *arglist->lastp = exparg.list; + arglist->lastp = exparg.lastp; + } +} + + + +/* + * Perform variable and command substitution. If full is set, output CTLESC + * characters to allow for further processing. If full is not set, treat + * $@ like $* since no splitting will be performed. + */ + +STATIC void +argstr(p, full) + register char *p; + { + char c; + + for (;;) { + switch (c = *p++) { + case '\0': + case CTLENDVAR: + goto breakloop; + case CTLESC: + if (full) + STPUTC(c, expdest); + c = *p++; + STPUTC(c, expdest); + break; + case CTLVAR: + p = evalvar(p, full); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + expbackq(argbackq->n, c & CTLQUOTE, full); + argbackq = argbackq->next; + break; + default: + STPUTC(c, expdest); + } + } +breakloop:; +} + + +/* + * Expand stuff in backwards quotes. + */ + +STATIC void +expbackq(cmd, quoted, full) + union node *cmd; + { + struct backcmd in; + int i; + char buf[128]; + char *p; + char *dest = expdest; + struct ifsregion saveifs, *savelastp; + struct nodelist *saveargbackq; + char lastc; + int startloc = dest - stackblock(); + char const *syntax = quoted? DQSYNTAX : BASESYNTAX; + int saveherefd; + + INTOFF; + saveifs = ifsfirst; + savelastp = ifslastp; + saveargbackq = argbackq; + saveherefd = herefd; + herefd = -1; + p = grabstackstr(dest); + evalbackcmd(cmd, &in); + ungrabstackstr(p, dest); + ifsfirst = saveifs; + ifslastp = savelastp; + argbackq = saveargbackq; + herefd = saveherefd; + + p = in.buf; + lastc = '\0'; + for (;;) { + if (--in.nleft < 0) { + if (in.fd < 0) + break; + while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR); + TRACE(("expbackq: read returns %d\n", i)); + if (i <= 0) + break; + p = buf; + in.nleft = i - 1; + } + lastc = *p++; + if (lastc != '\0') { + if (full && syntax[lastc] == CCTL) + STPUTC(CTLESC, dest); + STPUTC(lastc, dest); + } + } + if (lastc == '\n') { + STUNPUTC(dest); + } + if (in.fd >= 0) + close(in.fd); + if (in.buf) + ckfree(in.buf); + if (in.jp) + exitstatus = waitforjob(in.jp); + if (quoted == 0) + recordregion(startloc, dest - stackblock(), 0); + TRACE(("evalbackq: size=%d: \"%.*s\"\n", + (dest - stackblock()) - startloc, + (dest - stackblock()) - startloc, + stackblock() + startloc)); + expdest = dest; + INTON; +} + + + +/* + * Expand a variable, and return a pointer to the next character in the + * input string. + */ + +STATIC char * +evalvar(p, full) + char *p; + { + int subtype; + int flags; + char *var; + char *val; + int c; + int set; + int special; + int startloc; + + flags = *p++; + subtype = flags & VSTYPE; + var = p; + special = 0; + if (! is_name(*p)) + special = 1; + p = strchr(p, '=') + 1; +again: /* jump here after setting a variable with ${var=text} */ + if (special) { + set = varisset(*var); + val = NULL; + } else { + val = lookupvar(var); + if (val == NULL || (flags & VSNUL) && val[0] == '\0') { + val = NULL; + set = 0; + } else + set = 1; + } + startloc = expdest - stackblock(); + if (set && subtype != VSPLUS) { + /* insert the value of the variable */ + if (special) { + varvalue(*var, flags & VSQUOTE, full); + } else { + char const *syntax = (flags & VSQUOTE)? DQSYNTAX : BASESYNTAX; + + while (*val) { + if (full && syntax[*val] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*val++, expdest); + } + } + } + if (subtype == VSPLUS) + set = ! set; + if (((flags & VSQUOTE) == 0 || (*var == '@' && shellparam.nparam != 1)) + && (set || subtype == VSNORMAL)) + recordregion(startloc, expdest - stackblock(), flags & VSQUOTE); + if (! set && subtype != VSNORMAL) { + if (subtype == VSPLUS || subtype == VSMINUS) { + argstr(p, full); + } else { + char *startp; + int saveherefd = herefd; + herefd = -1; + argstr(p, 0); + STACKSTRNUL(expdest); + herefd = saveherefd; + startp = stackblock() + startloc; + if (subtype == VSASSIGN) { + setvar(var, startp, 0); + STADJUST(startp - expdest, expdest); + flags &=~ VSNUL; + goto again; + } + /* subtype == VSQUESTION */ + if (*p != CTLENDVAR) { + outfmt(&errout, "%s\n", startp); + error((char *)NULL); + } + error("%.*s: parameter %snot set", p - var - 1, + var, (flags & VSNUL)? "null or " : nullstr); + } + } + if (subtype != VSNORMAL) { /* skip to end of alternative */ + int nesting = 1; + for (;;) { + if ((c = *p++) == CTLESC) + p++; + else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) { + if (set) { + if (argbackq != NULL) + argbackq = argbackq->next; + } + } else if (c == CTLVAR) { + if ((*p++ & VSTYPE) != VSNORMAL) + nesting++; + } else if (c == CTLENDVAR) { + if (--nesting == 0) + break; + } + } + } + return p; +} + + + +/* + * Test whether a specialized variable is set. + */ + +STATIC int +varisset(name) + char name; + { + char **ap; + + if (name == '!') { + if (backgndpid == -1) + return 0; + } else if (name == '@' || name == '*') { + if (*shellparam.p == NULL) + return 0; + } else if ((unsigned)(name -= '1') <= '9' - '1') { + ap = shellparam.p; + do { + if (*ap++ == NULL) + return 0; + } while (--name >= 0); + } + return 1; +} + + + +/* + * Add the value of a specialized variable to the stack string. + */ + +STATIC void +varvalue(name, quoted, allow_split) + char name; + { + int num; + char temp[32]; + char *p; + int i; + extern int exitstatus; + char sep; + char **ap; + char const *syntax; + + switch (name) { + case '$': + num = rootpid; + goto numvar; + case '?': + num = exitstatus; + goto numvar; + case '#': + num = shellparam.nparam; + goto numvar; + case '!': + num = backgndpid; +numvar: + p = temp + 31; + temp[31] = '\0'; + do { + *--p = num % 10 + '0'; + } while ((num /= 10) != 0); + while (*p) + STPUTC(*p++, expdest); + break; + case '-': + for (i = 0 ; optchar[i] ; i++) { + if (optval[i]) + STPUTC(optchar[i], expdest); + } + break; + case '@': + if (allow_split) { + sep = '\0'; + goto allargs; + } + /* fall through */ + case '*': + sep = ' '; +allargs: + /* Only emit CTLESC if we will do further processing, + i.e. if allow_split is set. */ + syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX; + for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { + /* should insert CTLESC characters */ + while (*p) { + if (syntax[*p] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*p++, expdest); + } + if (*ap) + STPUTC(sep, expdest); + } + break; + case '0': + p = arg0; +string: + /* Only emit CTLESC if we will do further processing, + i.e. if allow_split is set. */ + syntax = quoted && allow_split ? DQSYNTAX : BASESYNTAX; + while (*p) { + if (syntax[*p] == CCTL) + STPUTC(CTLESC, expdest); + STPUTC(*p++, expdest); + } + break; + default: + if ((unsigned)(name -= '1') <= '9' - '1') { + p = shellparam.p[name]; + goto string; + } + break; + } +} + + + +/* + * Record the the fact that we have to scan this region of the + * string for IFS characters. + */ + +STATIC void +recordregion(start, end, nulonly) { + register struct ifsregion *ifsp; + + if (ifslastp == NULL) { + ifsp = &ifsfirst; + } else { + ifsp = (struct ifsregion *)ckmalloc(sizeof (struct ifsregion)); + ifslastp->next = ifsp; + } + ifslastp = ifsp; + ifslastp->next = NULL; + ifslastp->begoff = start; + ifslastp->endoff = end; + ifslastp->nulonly = nulonly; +} + + + +/* + * Break the argument string into pieces based upon IFS and add the + * strings to the argument list. The regions of the string to be + * searched for IFS characters have been stored by recordregion. + */ + +STATIC void +ifsbreakup(string, arglist) + char *string; + struct arglist *arglist; + { + struct ifsregion *ifsp; + struct strlist *sp; + char *start; + register char *p; + char *q; + char *ifs; + + start = string; + if (ifslastp != NULL) { + ifsp = &ifsfirst; + do { + p = string + ifsp->begoff; + ifs = ifsp->nulonly? nullstr : ifsval(); + while (p < string + ifsp->endoff) { + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p++)) { + if (q > start || *ifs != ' ') { + *q = '\0'; + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + } + if (*ifs == ' ') { + for (;;) { + if (p >= string + ifsp->endoff) + break; + q = p; + if (*p == CTLESC) + p++; + if (strchr(ifs, *p++) == NULL) { + p = q; + break; + } + } + } + start = p; + } + } + } while ((ifsp = ifsp->next) != NULL); + if (*start || (*ifs != ' ' && start > string)) { + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + } + } else { + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = start; + *arglist->lastp = sp; + arglist->lastp = &sp->next; + } +} + + + +/* + * Expand shell metacharacters. At this point, the only control characters + * should be escapes. The results are stored in the list exparg. + */ + +char *expdir; + + +STATIC void +expandmeta(str) + struct strlist *str; + { + char *p; + struct strlist **savelastp; + struct strlist *sp; + char c; + + while (str) { + if (fflag) + goto nometa; + p = str->text; +#if UDIR + if (p[0] == '/' && p[1] == 'u' && p[2] == '/') + str->text = p = expudir(p); +#endif +#if TILDE + if (p[0] == '~') + str->text = p = expudir(p); +#endif + for (;;) { /* fast check for meta chars */ + if ((c = *p++) == '\0') + goto nometa; + if (c == '*' || c == '?' || c == '[' || c == '!') + break; + } + savelastp = exparg.lastp; + INTOFF; + if (expdir == NULL) + expdir = ckmalloc(1024); /* I hope this is big enough */ + expmeta(expdir, str->text); + ckfree(expdir); + expdir = NULL; + INTON; + if (exparg.lastp == savelastp) { + if (! zflag) { +nometa: + *exparg.lastp = str; + rmescapes(str->text); + exparg.lastp = &str->next; + } + } else { + *exparg.lastp = NULL; + *savelastp = sp = expsort(*savelastp); + while (sp->next != NULL) + sp = sp->next; + exparg.lastp = &sp->next; + } + str = str->next; + } +} + + +#if UDIR || TILDE +/* + * UDIR: Expand /u/username into the home directory for the specified user. + * TILDE: Expand ~username into the home directory for the specified user. + * We hope not to use the getpw stuff here, because then we would have to load + * in stdio and who knows what else. With networked password files there is + * no choice alas. + */ + +#define MAXLOGNAME 32 +#define MAXPWLINE 128 + +char *pfgets(); + + +STATIC char * +expudir(path) + char *path; + { + register char *p, *q, *r; + char name[MAXLOGNAME]; + char line[MAXPWLINE]; + int i; +#if USEGETPW + struct passwd *pw; +#endif + + r = path; /* result on failure */ + p = r + (*r == '~' ? 1 : 3); /* the 1 skips "~", 3 skips "/u/" */ + q = name; + while (*p && *p != '/') { + if (q >= name + MAXLOGNAME - 1) + return r; /* fail, name too long */ + *q++ = *p++; + } + *q = '\0'; + +#if TILDE + if (*name == 0 && *r == '~') { + /* null name, use $HOME */ + if ((q = lookupvar("HOME")) == NULL) + return r; /* fail, home not set */ + i = strlen(q); + r = stalloc(i + strlen(p) + 1); + scopy(q, r); + scopy(p, r + i); + TRACE(("expudir converts %s to %s\n", path, r)); + didudir = 1; + path = r; + return r; + } +#endif +#if !USEGETPW /* can do without the bloat */ + setinputfile("/etc/passwd", 1); + q = line + strlen(name); + while (pfgets(line, MAXPWLINE) != NULL) { + if (line[0] == name[0] && prefix(name, line) && *q == ':') { + /* skip to start of home directory */ + i = 4; + do { + while (*++q && *q != ':'); + } while (--i > 0); + if (*q == '\0') + break; /* fail, corrupted /etc/passwd */ + q++; + for (r = q ; *r && *r != '\n' && *r != ':' ; r++); + *r = '\0'; /* nul terminate home directory */ + i = r - q; /* i = strlen(q) */ + r = stalloc(i + strlen(p) + 1); + scopy(q, r); + scopy(p, r + i); + TRACE(("expudir converts %s to %s\n", path, r)); + didudir = 1; + path = r; /* succeed */ + break; + } + } + popfile(); +#else + if ((pw = getpwnam(name)) != NULL) { + /* user exists */ + q = pw->pw_dir; + i = strlen(q); + r = stalloc(i + strlen(p) + 1); + scopy(q, r); + scopy(p, r + i); + TRACE(("expudir converts %s to %s\n", path, r)); + didudir = 1; + path = r; + } + endpwent(); +#endif /* USEGETPW */ + + return r; +} +#endif + + +/* + * Do metacharacter (i.e. *, ?, [...]) expansion. + */ + +STATIC void +expmeta(enddir, name) + char *enddir; + char *name; + { + register char *p; + char *q; + char *start; + char *endname; + int metaflag; + struct stat statb; + DIR *dirp; + struct dirent *dp; + int atend; + int matchdot; + + metaflag = 0; + start = name; + for (p = name ; ; p++) { + if (*p == '*' || *p == '?') + metaflag = 1; + else if (*p == '[') { + q = p + 1; + if (*q == '!') + q++; + for (;;) { + if (*q == CTLESC) + q++; + if (*q == '/' || *q == '\0') + break; + if (*++q == ']') { + metaflag = 1; + break; + } + } + } else if (*p == '!' && p[1] == '!' && (p == name || p[-1] == '/')) { + metaflag = 1; + } else if (*p == '\0') + break; + else if (*p == CTLESC) + p++; + if (*p == '/') { + if (metaflag) + break; + start = p + 1; + } + } + if (metaflag == 0) { /* we've reached the end of the file name */ + if (enddir != expdir) + metaflag++; + for (p = name ; ; p++) { + if (*p == CTLESC) + p++; + *enddir++ = *p; + if (*p == '\0') + break; + } + if (metaflag == 0 || stat(expdir, &statb) >= 0) + addfname(expdir); + return; + } + endname = p; + if (start != name) { + p = name; + while (p < start) { + if (*p == CTLESC) + p++; + *enddir++ = *p++; + } + } + if (enddir == expdir) { + p = "."; + } else if (enddir == expdir + 1 && *expdir == '/') { + p = "/"; + } else { + p = expdir; + enddir[-1] = '\0'; + } + if ((dirp = opendir(p)) == NULL) + return; + if (enddir != expdir) + enddir[-1] = '/'; + if (*endname == 0) { + atend = 1; + } else { + atend = 0; + *endname++ = '\0'; + } + matchdot = 0; + if (start[0] == '.' || start[0] == CTLESC && start[1] == '.') + matchdot++; + while (! int_pending() && (dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.' && ! matchdot) + continue; + if (patmatch(start, dp->d_name)) { + if (atend) { + scopy(dp->d_name, enddir); + addfname(expdir); + } else { + char *q; + for (p = enddir, q = dp->d_name ; *p++ = *q++ ;); + p[-1] = '/'; + expmeta(p, endname); + } + } + } + closedir(dirp); + if (! atend) + endname[-1] = '/'; +} + + +/* + * Add a file name to the list. + */ + +STATIC void +addfname(name) + char *name; + { + char *p; + struct strlist *sp; + + p = stalloc(strlen(name) + 1); + scopy(name, p); + sp = (struct strlist *)stalloc(sizeof *sp); + sp->text = p; + *exparg.lastp = sp; + exparg.lastp = &sp->next; +} + + +/* + * Sort the results of file name expansion. It calculates the number of + * strings to sort and then calls msort (short for merge sort) to do the + * work. + */ + +STATIC struct strlist * +expsort(str) + struct strlist *str; + { + int len; + struct strlist *sp; + + len = 0; + for (sp = str ; sp ; sp = sp->next) + len++; + return msort(str, len); +} + + +STATIC struct strlist * +msort(list, len) + struct strlist *list; + { + struct strlist *p, *q; + struct strlist **lpp; + int half; + int n; + + if (len <= 1) + return list; + half = len >> 1; + p = list; + for (n = half ; --n >= 0 ; ) { + q = p; + p = p->next; + } + q->next = NULL; /* terminate first half of list */ + q = msort(list, half); /* sort first half of list */ + p = msort(p, len - half); /* sort second half */ + lpp = &list; + for (;;) { + if (strcmp(p->text, q->text) < 0) { + *lpp = p; + lpp = &p->next; + if ((p = *lpp) == NULL) { + *lpp = q; + break; + } + } else { + *lpp = q; + lpp = &q->next; + if ((q = *lpp) == NULL) { + *lpp = p; + break; + } + } + } + return list; +} + + + +/* + * Returns true if the pattern matches the string. + */ + +int +patmatch(pattern, string) + char *pattern; + char *string; + { + if (pattern[0] == '!' && pattern[1] == '!') + return 1 - pmatch(pattern + 2, string); + else + return pmatch(pattern, string); +} + + +STATIC int +pmatch(pattern, string) + char *pattern; + char *string; + { + register char *p, *q; + register char c; + + p = pattern; + q = string; + for (;;) { + switch (c = *p++) { + case '\0': + goto breakloop; + case CTLESC: + if (*q++ != *p++) + return 0; + break; + case '?': + if (*q++ == '\0') + return 0; + break; + case '*': + c = *p; + if (c != CTLESC && c != '?' && c != '*' && c != '[') { + while (*q != c) { + if (*q == '\0') + return 0; + q++; + } + } + do { + if (pmatch(p, q)) + return 1; + } while (*q++ != '\0'); + return 0; + case '[': { + char *endp; + int invert, found; + char chr; + + endp = p; + if (*endp == '!') + endp++; + for (;;) { + if (*endp == '\0') + goto dft; /* no matching ] */ + if (*endp == CTLESC) + endp++; + if (*++endp == ']') + break; + } + invert = 0; + if (*p == '!') { + invert++; + p++; + } + found = 0; + chr = *q++; + c = *p++; + do { + if (c == CTLESC) + c = *p++; + if (*p == '-' && p[1] != ']') { + p++; + if (*p == CTLESC) + p++; + if (chr >= c && chr <= *p) + found = 1; + p++; + } else { + if (chr == c) + found = 1; + } + } while ((c = *p++) != ']'); + if (found == invert) + return 0; + break; + } +dft: default: + if (*q++ != c) + return 0; + break; + } + } +breakloop: + if (*q != '\0') + return 0; + return 1; +} + + + +/* + * Remove any CTLESC characters from a string. + */ + +void +rmescapes(str) + char *str; + { + register char *p, *q; + + p = str; + while (*p != CTLESC) { + if (*p++ == '\0') + return; + } + q = p; + while (*p) { + if (*p == CTLESC) + p++; + *q++ = *p++; + } + *q = '\0'; +} + + + +/* + * See if a pattern matches in a case statement. + */ + +int +casematch(pattern, val) + union node *pattern; + char *val; + { + struct stackmark smark; + int result; + char *p; + + setstackmark(&smark); + argbackq = pattern->narg.backquote; + STARTSTACKSTR(expdest); + ifslastp = NULL; + /* Preserve any CTLESC characters inserted previously, so that + we won't expand reg exps which are inside strings. */ + argstr(pattern->narg.text, 1); + STPUTC('\0', expdest); + p = grabstackstr(expdest); + result = patmatch(p, val); + popstackmark(&smark); + return result; +} diff --git a/commands/ash/expand.h b/commands/ash/expand.h new file mode 100755 index 000000000..65ecf8b09 --- /dev/null +++ b/commands/ash/expand.h @@ -0,0 +1,63 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)expand.h 5.1 (Berkeley) 3/7/91 + */ + +struct strlist { + struct strlist *next; + char *text; +}; + + +struct arglist { + struct strlist *list; + struct strlist **lastp; +}; + +#ifdef __STDC__ +union node; +void expandarg(union node *, struct arglist *, int); +void expandhere(union node *, int); +int patmatch(char *, char *); +void rmescapes(char *); +int casematch(union node *, char *); +#else +void expandarg(); +void expandhere(); +int patmatch(); +void rmescapes(); +int casematch(); +#endif diff --git a/commands/ash/funcs/cmv b/commands/ash/funcs/cmv new file mode 100755 index 000000000..0c6c4d3ba --- /dev/null +++ b/commands/ash/funcs/cmv @@ -0,0 +1,49 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)cmv 5.1 (Berkeley) 3/7/91 + +# Conditional move--don't replace an existing file. + +cmv() { + if test $# != 2 + then echo "cmv: arg count" + return 2 + fi + if test -f "$2" -o -w "$2" + then echo "$2 exists" + return 2 + fi + /bin/mv "$1" "$2" +} diff --git a/commands/ash/funcs/dirs b/commands/ash/funcs/dirs new file mode 100755 index 000000000..a31d23123 --- /dev/null +++ b/commands/ash/funcs/dirs @@ -0,0 +1,73 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)dirs 5.1 (Berkeley) 3/7/91 + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/commands/ash/funcs/kill b/commands/ash/funcs/kill new file mode 100755 index 000000000..9e643183f --- /dev/null +++ b/commands/ash/funcs/kill @@ -0,0 +1,49 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)kill 5.1 (Berkeley) 3/7/91 + +# Convert job names to process ids and then run /bin/kill. + +kill() { + local args x + args= + for x in "$@" + do case $x in + %*) x=`jobid "$x"` ;; + esac + args="$args $x" + done + /bin/kill $args +} diff --git a/commands/ash/funcs/login b/commands/ash/funcs/login new file mode 100755 index 000000000..6e24b437c --- /dev/null +++ b/commands/ash/funcs/login @@ -0,0 +1,38 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)login 5.1 (Berkeley) 3/7/91 + +# replaces the login builtin in the BSD shell +login () exec login "$@" diff --git a/commands/ash/funcs/newgrp b/commands/ash/funcs/newgrp new file mode 100755 index 000000000..7980ff54d --- /dev/null +++ b/commands/ash/funcs/newgrp @@ -0,0 +1,37 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)newgrp 5.1 (Berkeley) 3/7/91 + +newgrp() exec newgrp "$@" diff --git a/commands/ash/funcs/popd b/commands/ash/funcs/popd new file mode 100755 index 000000000..4cecba9ee --- /dev/null +++ b/commands/ash/funcs/popd @@ -0,0 +1,73 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)popd 5.1 (Berkeley) 3/7/91 + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/commands/ash/funcs/pushd b/commands/ash/funcs/pushd new file mode 100755 index 000000000..4e8c48cf6 --- /dev/null +++ b/commands/ash/funcs/pushd @@ -0,0 +1,73 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)pushd 5.1 (Berkeley) 3/7/91 + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/commands/ash/funcs/suspend b/commands/ash/funcs/suspend new file mode 100755 index 000000000..6c4e5795d --- /dev/null +++ b/commands/ash/funcs/suspend @@ -0,0 +1,41 @@ +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)suspend 5.1 (Berkeley) 3/7/91 + +suspend() { + local - + set +j + kill -TSTP 0 +} diff --git a/commands/ash/init.h b/commands/ash/init.h new file mode 100755 index 000000000..51efdb4b4 --- /dev/null +++ b/commands/ash/init.h @@ -0,0 +1,47 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)init.h 5.1 (Berkeley) 3/7/91 + */ + +#ifdef __STDC__ +void init(void); +void reset(void); +void initshellproc(void); +#else +void init(); +void reset(); +void initshellproc(); +#endif diff --git a/commands/ash/input.c b/commands/ash/input.c new file mode 100755 index 000000000..1ed7e0174 --- /dev/null +++ b/commands/ash/input.c @@ -0,0 +1,414 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)input.c 5.4 (Berkeley) 7/1/91"; +#endif /* not lint */ + +/* + * This file implements the input routines used by the parser. + */ + +#include +#include /* defines BUFSIZ */ +#include "shell.h" +#include +#include +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" + +#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ + + +/* + * The parsefile structure pointed to by the global variable parsefile + * contains information about the current file being read. + */ + +MKINIT +struct parsefile { + int linno; /* current line */ + int fd; /* file descriptor (or -1 if string) */ + int nleft; /* number of chars left in buffer */ + char *nextc; /* next char in buffer */ + struct parsefile *prev; /* preceding file on stack */ + char *buf; /* input buffer */ +}; + + +int plinno = 1; /* input line number */ +MKINIT int parsenleft; /* copy of parsefile->nleft */ +char *parsenextc; /* copy of parsefile->nextc */ +MKINIT struct parsefile basepf; /* top level input file */ +char basebuf[BUFSIZ]; /* buffer for top level input file */ +struct parsefile *parsefile = &basepf; /* current input file */ +char *pushedstring; /* copy of parsenextc when text pushed back */ +int pushednleft; /* copy of parsenleft when text pushed back */ + +#if READLINE +char *readline __P((const char *prompt)); +char *r_use_prompt = NULL; /* the prompt to use with readline */ +#endif + +#ifdef __STDC__ +STATIC void pushfile(void); +#else +STATIC void pushfile(); +#endif + + + +#ifdef mkinit +INCLUDE "input.h" +INCLUDE "error.h" + +INIT { + extern char basebuf[]; + + basepf.nextc = basepf.buf = basebuf; +} + +RESET { + if (exception != EXSHELLPROC) + parsenleft = 0; /* clear input buffer */ + popallfiles(); +} + +SHELLPROC { + popallfiles(); +} +#endif + + +/* + * Read a line from the script. + */ + +char * +pfgets(line, len) + char *line; + { + register char *p = line; + int nleft = len; + int c; + + while (--nleft > 0) { + c = pgetc_macro(); + if (c == PEOF) { + if (p == line) + return NULL; + break; + } + *p++ = c; + if (c == '\n') + break; + } + *p = '\0'; + return line; +} + + + +/* + * Read a character from the script, returning PEOF on end of file. + * Nul characters in the input are silently discarded. + */ + +int +pgetc() { + return pgetc_macro(); +} + + +/* + * Refill the input buffer and return the next input character: + * + * 1) If a string was pushed back on the input, switch back to the regular + * buffer. + * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading + * from a string so we can't refill the buffer, return EOF. + * 3) Call read to read in the characters. + * 4) Delete all nul characters from the buffer. + */ + +int +preadbuffer() { + register char *p, *q; + register int i; + + if (pushedstring) { + parsenextc = pushedstring; + pushedstring = NULL; + parsenleft = pushednleft; + if (--parsenleft >= 0) + return *parsenextc++; + } + if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) + return PEOF; + flushout(&output); + flushout(&errout); +#if READLINE + /* Use the readline() call if a prompt is to be printed (interactive). */ + if (r_use_prompt != NULL) { + char *prompt; + char *line; + + p = parsenextc = parsefile->buf; + + prompt = r_use_prompt; + r_use_prompt = NULL; + + if ((line = readline(prompt)) == NULL) { + parsenleft = EOF_NLEFT; + return PEOF; + } + strcpy(p, line); + free(line); + i = strlen(p); + p[i++] = '\n'; + } else { +#endif +retry: + p = parsenextc = parsefile->buf; + i = read(parsefile->fd, p, BUFSIZ); + if (i <= 0) { + if (i < 0) { + if (errno == EINTR) + goto retry; +#ifdef EWOULDBLOCK + if (parsefile->fd == 0 && errno == EWOULDBLOCK) { + int flags = fcntl(0, F_GETFL, 0); + if (flags >= 0 && flags & O_NONBLOCK) { + flags &=~ O_NONBLOCK; + if (fcntl(0, F_SETFL, flags) >= 0) { + out2str("sh: turning off NDELAY mode\n"); + goto retry; + } + } + } +#endif + } + parsenleft = EOF_NLEFT; + return PEOF; + } +#if READLINE + } +#endif + parsenleft = i - 1; + + /* delete nul characters */ + for (;;) { + if (*p++ == '\0') + break; + if (--i <= 0) + return *parsenextc++; /* no nul characters */ + } + q = p - 1; + while (--i > 0) { + if (*p != '\0') + *q++ = *p; + p++; + } + if (q == parsefile->buf) + goto retry; /* buffer contained nothing but nuls */ + parsenleft = q - parsefile->buf - 1; + return *parsenextc++; +} + + +/* + * Undo the last call to pgetc. Only one character may be pushed back. + * PEOF may be pushed back. + */ + +void +pungetc() { + parsenleft++; + parsenextc--; +} + + +/* + * Push a string back onto the input. This code doesn't work if the user + * tries to push back more than one string at once. + */ + +void +ppushback(string, length) + char *string; + { + pushedstring = parsenextc; + pushednleft = parsenleft; + parsenextc = string; + parsenleft = length; +} + + + +/* + * Set the input to take input from a file. If push is set, push the + * old input onto the stack first. + */ + +void +setinputfile(fname, push) + char *fname; + { + int fd; + int fd2; + + INTOFF; + if ((fd = open(fname, O_RDONLY)) < 0) + error("Can't open %s", fname); + if (fd < 10) { + fd2 = copyfd(fd, 10); + close(fd); + if (fd2 < 0) + error("Out of file descriptors"); + fd = fd2; + } + setinputfd(fd, push); + INTON; +} + + +/* + * Like setinputfile, but takes an open file descriptor. Call this with + * interrupts off. + */ + +void +setinputfd(fd, push) { + (void) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); + if (push) { + pushfile(); + parsefile->buf = ckmalloc(BUFSIZ); + } + if (parsefile->fd > 0) + close(parsefile->fd); + parsefile->fd = fd; + if (parsefile->buf == NULL) + parsefile->buf = ckmalloc(BUFSIZ); + parsenleft = 0; + plinno = 1; +} + + +/* + * Like setinputfile, but takes input from a string. + */ + +void +setinputstring(string, push) + char *string; + { + INTOFF; + if (push) + pushfile(); + parsenextc = string; + parsenleft = strlen(string); + parsefile->buf = NULL; + plinno = 1; + INTON; +} + + + +/* + * To handle the "." command, a stack of input files is used. Pushfile + * adds a new entry to the stack and popfile restores the previous level. + */ + +STATIC void +pushfile() { + struct parsefile *pf; + + parsefile->nleft = parsenleft; + parsefile->nextc = parsenextc; + parsefile->linno = plinno; + pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); + pf->prev = parsefile; + pf->fd = -1; + parsefile = pf; +} + + +void +popfile() { + struct parsefile *pf = parsefile; + + INTOFF; + if (pf->fd >= 0) + close(pf->fd); + if (pf->buf) + ckfree(pf->buf); + parsefile = pf->prev; + ckfree(pf); + parsenleft = parsefile->nleft; + parsenextc = parsefile->nextc; + plinno = parsefile->linno; + INTON; +} + + +/* + * Return to top level. + */ + +void +popallfiles() { + while (parsefile != &basepf) + popfile(); +} + + + +/* + * Close the file(s) that the shell is reading commands from. Called + * after a fork is done. + */ + +void +closescript() { + popallfiles(); + if (parsefile->fd > 0) { + close(parsefile->fd); + parsefile->fd = 0; + } +} diff --git a/commands/ash/input.h b/commands/ash/input.h new file mode 100755 index 000000000..656bb2930 --- /dev/null +++ b/commands/ash/input.h @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)input.h 5.1 (Berkeley) 3/7/91 + */ + +/* PEOF (the end of file marker) is defined in syntax.h */ + +/* + * The input line number. Input.c just defines this variable, and saves + * and restores it when files are pushed and popped. The user of this + * package must set its value. + */ +extern int plinno; +extern int parsenleft; /* number of characters left in input buffer */ +extern char *parsenextc; /* next character in input buffer */ + + +#ifdef __STDC__ +char *pfgets(char *, int); +int pgetc(void); +int preadbuffer(void); +void pungetc(void); +void ppushback(char *, int); +void setinputfile(char *, int); +void setinputfd(int, int); +void setinputstring(char *, int); +void popfile(void); +void popallfiles(void); +void closescript(void); +#else +char *pfgets(); +int pgetc(); +int preadbuffer(); +void pungetc(); +void ppushback(); +void setinputfile(); +void setinputfd(); +void setinputstring(); +void popfile(); +void popallfiles(); +void closescript(); +#endif + +#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer()) + +#if READLINE +/* The variable "r_use_prompt" indicates the prompt to use with readline, + * *and* that readline may only be used if non-NULL. + */ +extern char *r_use_prompt; +#endif diff --git a/commands/ash/jobs.c b/commands/ash/jobs.c new file mode 100755 index 000000000..66e4868b5 --- /dev/null +++ b/commands/ash/jobs.c @@ -0,0 +1,1035 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)jobs.c 5.1 (Berkeley) 3/7/91"; +#endif /* not lint */ + +#include "shell.h" +#if JOBS +#include "sgtty.h" +#undef CEOF /* syntax.h redefines this */ +#endif +#include "main.h" +#include "parser.h" +#include "nodes.h" +#include "jobs.h" +#include "options.h" +#include "trap.h" +#include "signames.h" +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "redir.h" +#include +#include +#include +#include +#ifdef BSD +#include +#include +#include +#include +#endif +#if POSIX +#include +#endif + + + +struct job *jobtab; /* array of jobs */ +int njobs; /* size of array */ +MKINIT short backgndpid = -1; /* pid of last background process */ +#if JOBS +int initialpgrp; /* pgrp of shell on invocation */ +short curjob; /* current job */ +#endif + +#ifdef __STDC__ +STATIC void restartjob(struct job *); +STATIC struct job *getjob(char *); +STATIC void freejob(struct job *); +STATIC int procrunning(int); +STATIC int dowait(int, struct job *); +STATIC int waitproc(int, int *); +STATIC char *commandtext(union node *); +#else +STATIC void restartjob(); +STATIC struct job *getjob(); +STATIC void freejob(); +STATIC int procrunning(); +STATIC int dowait(); +STATIC int waitproc(); +STATIC char *commandtext(); +#endif + + + +#if JOBS +/* + * Turn job control on and off. + * + * Note: This code assumes that the third arg to ioctl is a character + * pointer, which is true on Berkeley systems but not System V. Since + * System V doesn't have job control yet, this isn't a problem now. + */ + +MKINIT int jobctl; + +void +setjobctl(on) { + int ldisc; + + if (on == jobctl || rootshell == 0) + return; + if (on) { + do { /* while we are in the background */ + if (ioctl(2, TIOCGPGRP, (char *)&initialpgrp) < 0) { + out2str("ash: can't access tty; job control turned off\n"); + jflag = 0; + return; + } + if (initialpgrp == -1) + initialpgrp = getpgrp(0); + else if (initialpgrp != getpgrp(0)) { + killpg(initialpgrp, SIGTTIN); + continue; + } + } while (0); + if (ioctl(2, TIOCGETD, (char *)&ldisc) < 0 || ldisc != NTTYDISC) { + out2str("ash: need new tty driver to run job control; job control turned off\n"); + jflag = 0; + return; + } + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setpgrp(0, rootpid); + ioctl(2, TIOCSPGRP, (char *)&rootpid); + } else { /* turning job control off */ + setpgrp(0, initialpgrp); + ioctl(2, TIOCSPGRP, (char *)&initialpgrp); + setsignal(SIGTSTP); + setsignal(SIGTTOU); + } + jobctl = on; +} +#endif + + +#ifdef mkinit + +SHELLPROC { + backgndpid = -1; +#if JOBS + jobctl = 0; +#endif +} + +#endif + + + +#if JOBS +fgcmd(argc, argv) char **argv; { + struct job *jp; + int pgrp; + int status; + + jp = getjob(argv[1]); + if (jp->jobctl == 0) + error("job not created under job control"); + pgrp = jp->ps[0].pid; + ioctl(2, TIOCSPGRP, (char *)&pgrp); + restartjob(jp); + INTOFF; + status = waitforjob(jp); + INTON; + return status; +} + + +bgcmd(argc, argv) char **argv; { + struct job *jp; + + do { + jp = getjob(*++argv); + if (jp->jobctl == 0) + error("job not created under job control"); + restartjob(jp); + } while (--argc > 1); + return 0; +} + + +STATIC void +restartjob(jp) + struct job *jp; + { + struct procstat *ps; + int i; + + if (jp->state == JOBDONE) + return; + INTOFF; + killpg(jp->ps[0].pid, SIGCONT); + for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { + if ((ps->status & 0377) == 0177) { + ps->status = -1; + jp->state = 0; + } + } + INTON; +} +#endif + + +int +jobscmd(argc, argv) char **argv; { + showjobs(0); + return 0; +} + + +/* + * Print a list of jobs. If "change" is nonzero, only print jobs whose + * statuses have changed since the last call to showjobs. + * + * If the shell is interrupted in the process of creating a job, the + * result may be a job structure containing zero processes. Such structures + * will be freed here. + */ + +void +showjobs(change) { + int jobno; + int procno; + int i; + struct job *jp; + struct procstat *ps; + int col; + char s[64]; + + TRACE(("showjobs(%d) called\n", change)); + while (dowait(0, (struct job *)NULL) > 0); + for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { + if (! jp->used) + continue; + if (jp->nprocs == 0) { + freejob(jp); + continue; + } + if (change && ! jp->changed) + continue; + procno = jp->nprocs; + for (ps = jp->ps ; ; ps++) { /* for each process */ + if (ps == jp->ps) + fmtstr(s, 64, "[%d] %d ", jobno, ps->pid); + else + fmtstr(s, 64, " %d ", ps->pid); + out1str(s); + col = strlen(s); + s[0] = '\0'; + if (ps->status == -1) { + /* don't print anything */ + } else if ((ps->status & 0xFF) == 0) { + fmtstr(s, 64, "Exit %d", ps->status >> 8); + } else { + i = ps->status; +#if JOBS + if ((i & 0xFF) == 0177) + i >>= 8; +#endif + if ((i & 0x7F) <= MAXSIG && sigmesg[i & 0x7F]) + scopy(sigmesg[i & 0x7F], s); + else + fmtstr(s, 64, "Signal %d", i & 0x7F); + if (i & 0x80) + strcat(s, " (core dumped)"); + } + out1str(s); + col += strlen(s); + do { + out1c(' '); + col++; + } while (col < 30); + out1str(ps->cmd); + out1c('\n'); + if (--procno <= 0) + break; + } + jp->changed = 0; + if (jp->state == JOBDONE) { + freejob(jp); + } + } +} + + +/* + * Mark a job structure as unused. + */ + +STATIC void +freejob(jp) + struct job *jp; + { + struct procstat *ps; + int i; + + INTOFF; + for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) { + if (ps->cmd != nullstr) + ckfree(ps->cmd); + } + if (jp->ps != &jp->ps0) + ckfree(jp->ps); + jp->used = 0; +#if JOBS + if (curjob == jp - jobtab + 1) + curjob = 0; +#endif + INTON; +} + + + +int +waitcmd(argc, argv) char **argv; { + struct job *job; + int status; + struct job *jp; + + if (argc > 1) { + job = getjob(argv[1]); + } else { + job = NULL; + } + for (;;) { /* loop until process terminated or stopped */ + if (job != NULL) { + if (job->state) { + status = job->ps[job->nprocs - 1].status; + if ((status & 0xFF) == 0) + status = status >> 8 & 0xFF; +#if JOBS + else if ((status & 0xFF) == 0177) + status = (status >> 8 & 0x7F) + 128; +#endif + else + status = (status & 0x7F) + 128; + if (! iflag) + freejob(job); + return status; + } + } else { + for (jp = jobtab ; ; jp++) { + if (jp >= jobtab + njobs) { /* no running procs */ + return 0; + } + if (jp->used && jp->state == 0) + break; + } + } + dowait(1, (struct job *)NULL); + } +} + + + +jobidcmd(argc, argv) char **argv; { + struct job *jp; + int i; + + jp = getjob(argv[1]); + for (i = 0 ; i < jp->nprocs ; ) { + out1fmt("%d", jp->ps[i].pid); + out1c(++i < jp->nprocs? ' ' : '\n'); + } + return 0; +} + + + +/* + * Convert a job name to a job structure. + */ + +STATIC struct job * +getjob(name) + char *name; + { + int jobno; + register struct job *jp; + int pid; + int i; + + if (name == NULL) { +#if JOBS +currentjob: + if ((jobno = curjob) == 0 || jobtab[jobno - 1].used == 0) + error("No current job"); + return &jobtab[jobno - 1]; +#else + error("No current job"); +#endif + } else if (name[0] == '%') { + if (is_digit(name[1])) { + jobno = number(name + 1); + if (jobno > 0 && jobno <= njobs + && jobtab[jobno - 1].used != 0) + return &jobtab[jobno - 1]; +#if JOBS + } else if (name[1] == '%' && name[2] == '\0') { + goto currentjob; +#endif + } else { + register struct job *found = NULL; + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && prefix(name + 1, jp->ps[0].cmd)) { + if (found) + error("%s: ambiguous", name); + found = jp; + } + } + if (found) + return found; + } + } else if (is_number(name)) { + pid = number(name); + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && jp->ps[jp->nprocs - 1].pid == pid) + return jp; + } + } + error("No such job: %s", name); +} + + + +/* + * Return a new job structure, + */ + +struct job * +makejob(node, nprocs) + union node *node; + { + int i; + struct job *jp; + + for (i = njobs, jp = jobtab ; ; jp++) { + if (--i < 0) { + INTOFF; + if (njobs == 0) { + jobtab = ckmalloc(4 * sizeof jobtab[0]); + } else { + jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); + bcopy(jobtab, jp, njobs * sizeof jp[0]); + for (i= 0; i= 0 ; jobtab[njobs++].used = 0); + INTON; + break; + } + if (jp->used == 0) + break; + } + INTOFF; + jp->state = 0; + jp->used = 1; + jp->changed = 0; + jp->nprocs = 0; +#if JOBS + jp->jobctl = jobctl; +#endif + if (nprocs > 1) { + jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); + } else { + jp->ps = &jp->ps0; + } + INTON; + TRACE(("makejob(0x%x, %d) returns %%%d\n", (int)node, nprocs, jp - jobtab + 1)); + return jp; +} + + +/* + * Fork of a subshell. If we are doing job control, give the subshell its + * own process group. Jp is a job structure that the job is to be added to. + * N is the command that will be evaluated by the child. Both jp and n may + * be NULL. The mode parameter can be one of the following: + * FORK_FG - Fork off a foreground process. + * FORK_BG - Fork off a background process. + * FORK_NOJOB - Like FORK_FG, but don't give the process its own + * process group even if job control is on. + * + * When job control is turned off, background processes have their standard + * input redirected to /dev/null (except for the second and later processes + * in a pipeline). + */ + +int +forkshell(jp, n, mode) + union node *n; + struct job *jp; + { + int pid; + int pgrp; + + TRACE(("forkshell(%%%d, 0x%x, %d) called\n", jp - jobtab, (int)n, mode)); + INTOFF; + pid = fork(); + if (pid == -1) { + TRACE(("Fork failed, errno=%d\n", errno)); + INTON; + error("Cannot fork"); + } + if (pid == 0) { + struct job *p; + int wasroot; + int i; + + TRACE(("Child shell %d\n", getpid())); + wasroot = rootshell; + rootshell = 0; + for (i = njobs, p = jobtab ; --i >= 0 ; p++) + if (p->used) + freejob(p); + closescript(); + INTON; + clear_traps(); +#if JOBS + jobctl = 0; /* do job control only in root shell */ + if (wasroot && mode != FORK_NOJOB && jflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = getpid(); + else + pgrp = jp->ps[0].pid; + setpgrp(0, pgrp); + if (mode == FORK_FG) { + /*** this causes superfluous TIOCSPGRPS ***/ + if (ioctl(2, TIOCSPGRP, (char *)&pgrp) < 0) + error("TIOCSPGRP failed, errno=%d\n", errno); + } + setsignal(SIGTSTP); + setsignal(SIGTTOU); + } else if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if ((jp == NULL || jp->nprocs == 0) + && ! fd0_redirected_p ()) { + close(0); + if (open("/dev/null", O_RDONLY) != 0) + error("Can't open /dev/null"); + } + } +#else + if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if ((jp == NULL || jp->nprocs == 0) + && ! fd0_redirected_p ()) { + close(0); + if (open("/dev/null", O_RDONLY) != 0) + error("Can't open /dev/null"); + } + } +#endif + if (wasroot && iflag) { + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + } + return pid; + } + if (rootshell && mode != FORK_NOJOB && jflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = pid; + else + pgrp = jp->ps[0].pid; +#if JOBS + setpgrp(pid, pgrp); +#endif + } + if (mode == FORK_BG) + backgndpid = pid; /* set $! */ + if (jp) { + struct procstat *ps = &jp->ps[jp->nprocs++]; + ps->pid = pid; + ps->status = -1; + ps->cmd = nullstr; + if (iflag && rootshell && n) + ps->cmd = commandtext(n); + } + INTON; + TRACE(("In parent shell: child = %d\n", pid)); + return pid; +} + + + +/* + * Wait for job to finish. + * + * Under job control we have the problem that while a child process is + * running interrupts generated by the user are sent to the child but not + * to the shell. This means that an infinite loop started by an inter- + * active user may be hard to kill. With job control turned off, an + * interactive user may place an interactive program inside a loop. If + * the interactive program catches interrupts, the user doesn't want + * these interrupts to also abort the loop. The approach we take here + * is to have the shell ignore interrupt signals while waiting for a + * forground process to terminate, and then send itself an interrupt + * signal if the child process was terminated by an interrupt signal. + * Unfortunately, some programs want to do a bit of cleanup and then + * exit on interrupt; unless these processes terminate themselves by + * sending a signal to themselves (instead of calling exit) they will + * confuse this approach. + */ + +int +waitforjob(jp) + register struct job *jp; + { +#if JOBS + int mypgrp = getpgrp(0); +#endif + int status; + int st; + + INTOFF; + TRACE(("waitforjob(%%%d) called\n", jp - jobtab + 1)); + while (jp->state == 0 && dowait(1, jp) != -1) ; +#if JOBS + if (jp->jobctl) { + if (ioctl(2, TIOCSPGRP, (char *)&mypgrp) < 0) + error("TIOCSPGRP failed, errno=%d\n", errno); + } + if (jp->state == JOBSTOPPED) + curjob = jp - jobtab + 1; +#endif + status = jp->ps[jp->nprocs - 1].status; + /* convert to 8 bits */ + if ((status & 0xFF) == 0) + st = status >> 8 & 0xFF; +#if JOBS + else if ((status & 0xFF) == 0177) + st = (status >> 8 & 0x7F) + 128; +#endif + else + st = (status & 0x7F) + 128; + if (! JOBS || jp->state == JOBDONE) + freejob(jp); + CLEAR_PENDING_INT; + if ((status & 0x7F) == SIGINT) + kill(getpid(), SIGINT); + INTON; + return st; +} + + + +/* + * Wait for a process to terminate. + */ + +STATIC int +dowait(block, job) + struct job *job; + { + int pid; + int status; + struct procstat *sp; + struct job *jp; + struct job *thisjob; + int done; + int stopped; + int core; + + TRACE(("dowait(%d) called\n", block)); + do { + pid = waitproc(block, &status); + TRACE(("wait returns %d, status=%d, errno=%d\n", + pid, status, errno)); + } while (pid == -1 && errno == EINTR); + if (pid <= 0) + return pid; + INTOFF; + thisjob = NULL; + for (jp = jobtab ; jp < jobtab + njobs ; jp++) { + if (jp->used) { + done = 1; + stopped = 1; + for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { + if (sp->pid == -1) + continue; + if (sp->pid == pid) { + TRACE(("Changin status of proc %d from 0x%x to 0x%x\n", pid, sp->status, status)); + sp->status = status; + thisjob = jp; + } + if (sp->status == -1) + stopped = 0; + else if ((sp->status & 0377) == 0177) + done = 0; + } + if (stopped) { /* stopped or done */ + int state = done? JOBDONE : JOBSTOPPED; + if (jp->state != state) { + TRACE(("Job %d: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); + jp->state = state; +#if JOBS + if (done && curjob == jp - jobtab + 1) + curjob = 0; /* no current job */ +#endif + } + } + } + } + INTON; + if (! rootshell || ! iflag || (job && thisjob == job)) { +#if JOBS + if ((status & 0xFF) == 0177) + status >>= 8; +#endif + core = status & 0x80; + status &= 0x7F; + if (status != 0 && status != SIGINT && status != SIGPIPE) { + if (thisjob != job) + outfmt(out2, "%d: ", pid); +#if JOBS + if (status == SIGTSTP && rootshell && iflag) + outfmt(out2, "%%%d ", job - jobtab + 1); +#endif + if (status <= MAXSIG && sigmesg[status]) + out2str(sigmesg[status]); + else + outfmt(out2, "Signal %d", status); + if (core) + out2str(" - core dumped"); + out2c('\n'); + flushout(&errout); + } else { + TRACE(("Not printing status: status=%d\n", status)); + } + } else { + TRACE(("Not printing status, rootshell=%d, job=0x%x\n", rootshell, job)); + if (thisjob) + thisjob->changed = 1; + } + return pid; +} + + + +/* + * Do a wait system call. If job control is compiled in, we accept + * stopped processes. If block is zero, we return a value of zero + * rather than blocking. + * + * System V doesn't have a non-blocking wait system call. It does + * have a SIGCLD signal that is sent to a process when one of it's + * children dies. The obvious way to use SIGCLD would be to install + * a handler for SIGCLD which simply bumped a counter when a SIGCLD + * was received, and have waitproc bump another counter when it got + * the status of a process. Waitproc would then know that a wait + * system call would not block if the two counters were different. + * This approach doesn't work because if a process has children that + * have not been waited for, System V will send it a SIGCLD when it + * installs a signal handler for SIGCLD. What this means is that when + * a child exits, the shell will be sent SIGCLD signals continuously + * until is runs out of stack space, unless it does a wait call before + * restoring the signal handler. The code below takes advantage of + * this (mis)feature by installing a signal handler for SIGCLD and + * then checking to see whether it was called. If there are any + * children to be waited for, it will be. + * + * If neither SYSV nor BSD is defined, we don't implement nonblocking + * waits at all. In this case, the user will not be informed when + * a background process until the next time she runs a real program + * (as opposed to running a builtin command or just typing return), + * and the jobs command may give out of date information. + */ + +#ifdef SYSV +STATIC int gotsigchild; + +STATIC int onsigchild() { + gotsigchild = 1; +} +#endif + + +STATIC int +waitproc(block, status) + int *status; + { +#ifdef BSD + int flags; + +#if JOBS + flags = WUNTRACED; +#else + flags = 0; +#endif + if (block == 0) + flags |= WNOHANG; + return wait3((union wait *)status, flags, (struct rusage *)NULL); +#else +#ifdef SYSV + int (*save)(); + + if (block == 0) { + gotsigchild = 0; + save = signal(SIGCLD, onsigchild); + signal(SIGCLD, save); + if (gotsigchild == 0) + return 0; + } + return wait(status); +#else +#if POSIX + return waitpid(-1, status, block == 0 ? WNOHANG : 0); +#else + if (block == 0) + return 0; + return wait(status); +#endif +#endif +#endif +} + + + +/* + * Return a string identifying a command (to be printed by the + * jobs command. + */ + +STATIC char *cmdnextc; +STATIC int cmdnleft; +STATIC void cmdtxt(), cmdputs(); + +STATIC char * +commandtext(n) + union node *n; + { + char *name; + + cmdnextc = name = ckmalloc(50); + cmdnleft = 50 - 4; + cmdtxt(n); + *cmdnextc = '\0'; + return name; +} + + +STATIC void +cmdtxt(n) + union node *n; + { + union node *np; + struct nodelist *lp; + char *p; + int i; + char s[2]; + + if (n == NULL) return; + + switch (n->type) { + case NSEMI: + cmdtxt(n->nbinary.ch1); + cmdputs("; "); + cmdtxt(n->nbinary.ch2); + break; + case NAND: + cmdtxt(n->nbinary.ch1); + cmdputs(" && "); + cmdtxt(n->nbinary.ch2); + break; + case NOR: + cmdtxt(n->nbinary.ch1); + cmdputs(" || "); + cmdtxt(n->nbinary.ch2); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + cmdtxt(lp->n); + if (lp->next) + cmdputs(" | "); + } + break; + case NSUBSHELL: + cmdputs("("); + cmdtxt(n->nredir.n); + cmdputs(")"); + break; + case NREDIR: + case NBACKGND: + cmdtxt(n->nredir.n); + break; + case NIF: + cmdputs("if "); + cmdtxt(n->nif.test); + cmdputs("; then "); + cmdtxt(n->nif.ifpart); + cmdputs("..."); + break; + case NWHILE: + cmdputs("while "); + goto until; + case NUNTIL: + cmdputs("until "); +until: + cmdtxt(n->nbinary.ch1); + cmdputs("; do "); + cmdtxt(n->nbinary.ch2); + cmdputs("; done"); + break; + case NFOR: + cmdputs("for "); + cmdputs(n->nfor.var); + cmdputs(" in ..."); + break; + case NCASE: + cmdputs("case "); + cmdputs(n->ncase.expr->narg.text); + cmdputs(" in ..."); + break; + case NDEFUN: + cmdputs(n->narg.text); + cmdputs("() ..."); + break; + case NCMD: + for (np = n->ncmd.args ; np ; np = np->narg.next) { + cmdtxt(np); + if (np->narg.next) + cmdputs(" "); + } + for (np = n->ncmd.redirect ; np ; np = np->nfile.next) { + cmdputs(" "); + cmdtxt(np); + } + break; + case NARG: + cmdputs(n->narg.text); + break; + case NTO: + p = ">"; i = 1; goto redir; + case NAPPEND: + p = ">>"; i = 1; goto redir; + case NTOFD: + p = ">&"; i = 1; goto redir; + case NFROM: + p = "<"; i = 0; goto redir; + case NFROMFD: + p = "<&"; i = 0; goto redir; +redir: + if (n->nfile.fd != i) { + s[0] = n->nfile.fd + '0'; + s[1] = '\0'; + cmdputs(s); + } + cmdputs(p); + if (n->type == NTOFD || n->type == NFROMFD) { + s[0] = n->ndup.dupfd + '0'; + s[1] = '\0'; + cmdputs(s); + } else { + cmdtxt(n->nfile.fname); + } + break; + case NHERE: + case NXHERE: + cmdputs("<<..."); + break; + default: + cmdputs("???"); + break; + } +} + + + +STATIC void +cmdputs(s) + char *s; + { + register char *p, *q; + register char c; + int subtype = 0; + + if (cmdnleft <= 0) + return; + p = s; + q = cmdnextc; + while ((c = *p++) != '\0') { + if (c == CTLESC) + *q++ = *p++; + else if (c == CTLVAR) { + *q++ = '$'; + if (--cmdnleft > 0) + *q++ = '{'; + subtype = *p++; + } else if (c == '=' && subtype != 0) { + *q++ = "}-+?="[(subtype & VSTYPE) - VSNORMAL]; + subtype = 0; + } else if (c == CTLENDVAR) { + *q++ = '}'; + } else if (c == CTLBACKQ | c == CTLBACKQ+CTLQUOTE) + cmdnleft++; /* ignore it */ + else + *q++ = c; + if (--cmdnleft <= 0) { + *q++ = '.'; + *q++ = '.'; + *q++ = '.'; + break; + } + } + cmdnextc = q; +} diff --git a/commands/ash/jobs.h b/commands/ash/jobs.h new file mode 100755 index 000000000..1778fcf82 --- /dev/null +++ b/commands/ash/jobs.h @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)jobs.h 5.1 (Berkeley) 3/7/91 + */ + +/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ +#define FORK_FG 0 +#define FORK_BG 1 +#define FORK_NOJOB 2 + + +/* + * A job structure contains information about a job. A job is either a + * single process or a set of processes contained in a pipeline. In the + * latter case, pidlist will be non-NULL, and will point to a -1 terminated + * array of pids. + */ + +struct procstat { + short pid; /* process id */ + short status; /* status flags (defined above) */ + char *cmd; /* text of command being run */ +}; + + +/* states */ +#define JOBSTOPPED 1 /* all procs are stopped */ +#define JOBDONE 2 /* all procs are completed */ + + +struct job { + struct procstat ps0; /* status of process */ + struct procstat *ps; /* status or processes when more than one */ + short nprocs; /* number of processes */ + short pgrp; /* process group of this job */ + char state; /* true if job is finished */ + char used; /* true if this entry is in used */ + char changed; /* true if status has changed */ +#if JOBS + char jobctl; /* job running under job control */ +#endif +}; + +extern short backgndpid; /* pid of last background process */ + + +#ifdef __STDC__ +void setjobctl(int); +void showjobs(int); +struct job *makejob(union node *, int); +int forkshell(struct job *, union node *, int); +int waitforjob(struct job *); +#else +void setjobctl(); +void showjobs(); +struct job *makejob(); +int forkshell(); +int waitforjob(); +#endif + +#if ! JOBS +#define setjobctl(on) /* do nothing */ +#endif diff --git a/commands/ash/machdep.h b/commands/ash/machdep.h new file mode 100755 index 000000000..e4ee3c944 --- /dev/null +++ b/commands/ash/machdep.h @@ -0,0 +1,51 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)machdep.h 5.1 (Berkeley) 3/7/91 + */ + +/* + * Most machines require the value returned from malloc to be aligned + * in some way. The following macro will get this right on many machines. + */ + +#ifndef ALIGN +union align { + int i; + char *cp; +}; + +#define ALIGN(nbytes) ((nbytes) + sizeof(union align) - 1 &~ (sizeof(union align) - 1)) +#endif diff --git a/commands/ash/mail.c b/commands/ash/mail.c new file mode 100755 index 000000000..a90d4a011 --- /dev/null +++ b/commands/ash/mail.c @@ -0,0 +1,108 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)mail.c 5.1 (Berkeley) 3/7/91"; +#endif /* not lint */ + +/* + * Routines to check for mail. (Perhaps make part of main.c?) + */ + +#include "shell.h" +#include "exec.h" /* defines padvance() */ +#include "var.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include +#include + + +#define MAXMBOXES 10 + + +STATIC int nmboxes; /* number of mailboxes */ +STATIC time_t mailtime[MAXMBOXES]; /* times of mailboxes */ + + + +/* + * Print appropriate message(s) if mail has arrived. If the argument is + * nozero, then the value of MAIL has changed, so we just update the + * values. + */ + +void +chkmail(silent) { + register int i; + char *mpath; + char *p; + register char *q; + struct stackmark smark; + struct stat statb; + + if (silent) + nmboxes = 10; + if (nmboxes == 0) + return; + setstackmark(&smark); + mpath = mpathset()? mpathval() : mailval(); + for (i = 0 ; i < nmboxes ; i++) { + p = padvance(&mpath, nullstr); + if (p == NULL) + break; + if (*p == '\0') + continue; + for (q = p ; *q ; q++); + if (q[-1] != '/') + abort(); + q[-1] = '\0'; /* delete trailing '/' */ + if (stat(p, &statb) < 0) + statb.st_mtime = 0; + if (!silent + && statb.st_size > 0 + && statb.st_mtime > mailtime[i] + && statb.st_mtime > statb.st_atime + ) { + out2str(pathopt? pathopt : "You have mail"); + out2c('\n'); + } + mailtime[i] = statb.st_mtime; + } + nmboxes = i; + popstackmark(&smark); +} diff --git a/commands/ash/mail.h b/commands/ash/mail.h new file mode 100755 index 000000000..8280791e8 --- /dev/null +++ b/commands/ash/mail.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mail.h 5.1 (Berkeley) 3/7/91 + */ + +#ifdef __STDC__ +void chkmail(int); +#else +void chkmail(); +#endif diff --git a/commands/ash/main.c b/commands/ash/main.c new file mode 100755 index 000000000..7393cf4d3 --- /dev/null +++ b/commands/ash/main.c @@ -0,0 +1,356 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)main.c 5.2 (Berkeley) 3/13/91"; +#endif /* not lint */ + +#include +#include +#include +#include "shell.h" +#include "main.h" +#include "mail.h" +#include "options.h" +#include "output.h" +#include "parser.h" +#include "nodes.h" +#include "eval.h" +#include "jobs.h" +#include "input.h" +#include "trap.h" +#if ATTY +#include "var.h" +#endif +#include "memalloc.h" +#include "error.h" +#include "init.h" +#include "mystring.h" + +#define PROFILE 0 + +int rootpid; +int rootshell; +STATIC union node *curcmd; +STATIC union node *prevcmd; +extern int errno; +#if PROFILE +short profile_buf[16384]; +extern int etext(); +#endif + +#ifdef __STDC__ +STATIC void read_profile(char *); +char *getenv(char *); +#else +STATIC void read_profile(); +char *getenv(); +#endif + + +/* + * Main routine. We initialize things, parse the arguments, execute + * profiles if we're a login shell, and then call cmdloop to execute + * commands. The setjmp call sets up the location to jump to when an + * exception occurs. When an exception occurs the variable "state" + * is used to figure out how far we had gotten. + */ + +main(argc, argv) char **argv; { + struct jmploc jmploc; + struct stackmark smark; + volatile int state; + char *shinit, *home; + char *profile = NULL, *ashrc = NULL; + +#if PROFILE + monitor(4, etext, profile_buf, sizeof profile_buf, 50); +#endif + state = 0; + if (setjmp(jmploc.loc)) { + /* + * When a shell procedure is executed, we raise the + * exception EXSHELLPROC to clean up before executing + * the shell procedure. + */ + if (exception == EXSHELLPROC) { + rootpid = getpid(); + rootshell = 1; + minusc = NULL; + state = 3; + } else if (state == 0 || iflag == 0 || ! rootshell) + exitshell(2); + reset(); +#if ATTY + if (exception == EXINT + && (! attyset() || equal(termval(), "emacs"))) { +#else + if (exception == EXINT) { +#endif + out2c('\n'); + flushout(&errout); + } + popstackmark(&smark); + FORCEINTON; /* enable interrupts */ + if (state == 1) + goto state1; + else if (state == 2) + goto state2; + else if (state == 3) + goto state3; + else + goto state4; + } + handler = &jmploc; +#if DEBUG + opentrace(); + trputs("Shell args: "); trargs(argv); +#endif + rootpid = getpid(); + rootshell = 1; + init(); + setstackmark(&smark); + procargs(argc, argv); + if (eflag) eflag = 2; /* Truly enable [ex]flag after init. */ + if (xflag) xflag = 2; + if (argv[0] && argv[0][0] == '-') { + state = 1; + read_profile("/etc/profile"); +state1: + state = 2; + if ((home = getenv("HOME")) != NULL + && (profile = (char *) malloc(strlen(home) + 10)) != NULL) + { + strcpy(profile, home); + strcat(profile, "/.profile"); + read_profile(profile); + } else { + read_profile(".profile"); + } + } else if ((sflag || minusc) && (shinit = getenv("SHINIT")) != NULL) { + state = 2; + evalstring(shinit); + } +state2: + if (profile != NULL) free(profile); + + state = 3; + if (!argv[0] || argv[0][0] != '-') { + if ((home = getenv("HOME")) != NULL + && (ashrc = (char *) malloc(strlen(home) + 8)) != NULL) + { + strcpy(ashrc, home); + strcat(ashrc, "/.ashrc"); + read_profile(ashrc); + } + } +state3: + if (ashrc != NULL) free(ashrc); + if (eflag) eflag = 1; /* Init done, enable [ex]flag */ + if (xflag) xflag = 1; + exitstatus = 0; /* Init shouldn't influence initial $? */ + + state = 4; + if (minusc) { + evalstring(minusc); + } + if (sflag || minusc == NULL) { +state4: + cmdloop(1); + } +#if PROFILE + monitor(0); +#endif + exitshell(exitstatus); +} + + +/* + * Read and execute commands. "Top" is nonzero for the top level command + * loop; it turns on prompting if the shell is interactive. + */ + +void +cmdloop(top) { + union node *n; + struct stackmark smark; + int inter; + int numeof; + + TRACE(("cmdloop(%d) called\n", top)); + setstackmark(&smark); + numeof = 0; + for (;;) { + if (pendingsigs) + dotrap(); + inter = 0; + if (iflag && top) { + inter++; + showjobs(1); + chkmail(0); + flushout(&output); + } + n = parsecmd(inter); +#if DEBUG + /* showtree(n); */ +#endif + if (n == NEOF) { + if (Iflag == 0 || numeof >= 50) + break; + out2str("\nUse \"exit\" to leave shell.\n"); + numeof++; + } else if (n != NULL && nflag == 0) { + if (inter) { + INTOFF; + if (prevcmd) + freefunc(prevcmd); + prevcmd = curcmd; + curcmd = copyfunc(n); + INTON; + } + evaltree(n, 0); +#ifdef notdef + if (exitstatus) /*DEBUG*/ + outfmt(&errout, "Exit status 0x%X\n", exitstatus); +#endif + } + popstackmark(&smark); + } + popstackmark(&smark); /* unnecessary */ +} + + + +/* + * Read /etc/profile or .profile. Return on error. + */ + +STATIC void +read_profile(name) + char *name; + { + int fd; + + INTOFF; + if ((fd = open(name, O_RDONLY)) >= 0) + setinputfd(fd, 1); + INTON; + if (fd < 0) + return; + cmdloop(0); + popfile(); +} + + + +/* + * Read a file containing shell functions. + */ + +void +readcmdfile(name) + char *name; + { + int fd; + + INTOFF; + if ((fd = open(name, O_RDONLY)) >= 0) + setinputfd(fd, 1); + else + error("Can't open %s", name); + INTON; + cmdloop(0); + popfile(); +} + + +/* + * Take commands from a file. To be compatable we should do a path + * search for the file, but a path search doesn't make any sense. + */ + +dotcmd(argc, argv) char **argv; { + exitstatus = 0; + if (argc >= 2) { /* That's what SVR2 does */ + setinputfile(argv[1], 1); + commandname = argv[1]; + cmdloop(0); + popfile(); + } + return exitstatus; +} + + +exitcmd(argc, argv) char **argv; { + if (argc > 1) + exitstatus = number(argv[1]); + exitshell(exitstatus); +} + + +lccmd(argc, argv) char **argv; { + if (argc > 1) { + defun(argv[1], prevcmd); + return 0; + } else { + INTOFF; + freefunc(curcmd); + curcmd = prevcmd; + prevcmd = NULL; + INTON; + evaltree(curcmd, 0); + return exitstatus; + } +} + + + +#ifdef notdef +/* + * Should never be called. + */ + +void +exit(exitstatus) { + _exit(exitstatus); +} +#endif diff --git a/commands/ash/main.h b/commands/ash/main.h new file mode 100755 index 000000000..c5fda20ce --- /dev/null +++ b/commands/ash/main.h @@ -0,0 +1,48 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)main.h 5.1 (Berkeley) 3/7/91 + */ + +extern int rootpid; /* pid of main shell */ +extern int rootshell; /* true if we aren't a child of the main shell */ + +#ifdef __STDC__ +void readcmdfile(char *); +void cmdloop(int); +#else +void readcmdfile(); +void cmdloop(); +#endif diff --git a/commands/ash/memalloc.c b/commands/ash/memalloc.c new file mode 100755 index 000000000..31fcd3a3a --- /dev/null +++ b/commands/ash/memalloc.c @@ -0,0 +1,292 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)memalloc.c 5.2 (Berkeley) 3/13/91"; +#endif /* not lint */ + +#include "shell.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "machdep.h" +#include "mystring.h" + +/* + * Like malloc, but returns an error when out of space. + */ + +pointer +ckmalloc(nbytes) { + register pointer p; + pointer malloc(); + + if ((p = malloc(nbytes)) == NULL) + error("Out of space"); + return p; +} + + +/* + * Same for realloc. + */ + +pointer +ckrealloc(p, nbytes) + register pointer p; + { + pointer realloc(); + + if ((p = realloc(p, nbytes)) == NULL) + error("Out of space"); + return p; +} + + +/* + * Make a copy of a string in safe storage. + */ + +char * +savestr(s) + char *s; + { + register char *p; + + p = ckmalloc(strlen(s) + 1); + scopy(s, p); + return p; +} + + +/* + * Parse trees for commands are allocated in lifo order, so we use a stack + * to make this more efficient, and also to avoid all sorts of exception + * handling code to handle interrupts in the middle of a parse. + * + * The size 504 was chosen because the Ultrix malloc handles that size + * well. + */ + +#define MINSIZE 504 /* minimum size of a block */ + + +struct stack_block { + struct stack_block *prev; + char space[MINSIZE]; +}; + +struct stack_block stackbase; +struct stack_block *stackp = &stackbase; +char *stacknxt = stackbase.space; +int stacknleft = MINSIZE; +int sstrnleft; +int herefd = -1; + + + +pointer +stalloc(nbytes) { + register char *p; + + nbytes = ALIGN(nbytes); + if (nbytes > stacknleft) { + int blocksize; + struct stack_block *sp; + + blocksize = nbytes; + if (blocksize < MINSIZE) + blocksize = MINSIZE; + INTOFF; + sp = ckmalloc(sizeof(struct stack_block) - MINSIZE + blocksize); + sp->prev = stackp; + stacknxt = sp->space; + stacknleft = blocksize; + stackp = sp; + INTON; + } + p = stacknxt; + stacknxt += nbytes; + stacknleft -= nbytes; + return p; +} + + +void +stunalloc(p) + pointer p; + { + if (p == NULL) { /*DEBUG */ + write(2, "stunalloc\n", 10); + abort(); + } + stacknleft += stacknxt - (char *)p; + stacknxt = p; +} + + + +void +setstackmark(mark) + struct stackmark *mark; + { + mark->stackp = stackp; + mark->stacknxt = stacknxt; + mark->stacknleft = stacknleft; +} + + +void +popstackmark(mark) + struct stackmark *mark; + { + struct stack_block *sp; + + INTOFF; + while (stackp != mark->stackp) { + sp = stackp; + stackp = sp->prev; + ckfree(sp); + } + stacknxt = mark->stacknxt; + stacknleft = mark->stacknleft; + INTON; +} + + +/* + * When the parser reads in a string, it wants to stick the string on the + * stack and only adjust the stack pointer when it knows how big the + * string is. Stackblock (defined in stack.h) returns a pointer to a block + * of space on top of the stack and stackblocklen returns the length of + * this block. Growstackblock will grow this space by at least one byte, + * possibly moving it (like realloc). Grabstackblock actually allocates the + * part of the block that has been used. + */ + +void +growstackblock() { + char *p; + int newlen = stacknleft * 2 + 100; + char *oldspace = stacknxt; + int oldlen = stacknleft; + struct stack_block *sp; + + if (stacknxt == stackp->space && stackp != &stackbase) { + INTOFF; + sp = stackp; + stackp = sp->prev; + sp = ckrealloc((pointer)sp, sizeof(struct stack_block) - MINSIZE + newlen); + sp->prev = stackp; + stackp = sp; + stacknxt = sp->space; + stacknleft = newlen; + INTON; + } else { + p = stalloc(newlen); + bcopy(oldspace, p, oldlen); + stacknxt = p; /* free the space */ + stacknleft += newlen; /* we just allocated */ + } +} + + + +void +grabstackblock(len) { + len = ALIGN(len); + stacknxt += len; + stacknleft -= len; +} + + + +/* + * The following routines are somewhat easier to use that the above. + * The user declares a variable of type STACKSTR, which may be declared + * to be a register. The macro STARTSTACKSTR initializes things. Then + * the user uses the macro STPUTC to add characters to the string. In + * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is + * grown as necessary. When the user is done, she can just leave the + * string there and refer to it using stackblock(). Or she can allocate + * the space for it using grabstackstr(). If it is necessary to allow + * someone else to use the stack temporarily and then continue to grow + * the string, the user should use grabstack to allocate the space, and + * then call ungrabstr(p) to return to the previous mode of operation. + * + * USTPUTC is like STPUTC except that it doesn't check for overflow. + * CHECKSTACKSPACE can be called before USTPUTC to ensure that there + * is space for at least one character. + */ + + +char * +growstackstr() { + int len = stackblocksize(); + if (herefd >= 0 && len >= 1024) { + xwrite(herefd, stackblock(), len); + sstrnleft = len - 1; + return stackblock(); + } + growstackblock(); + sstrnleft = stackblocksize() - len - 1; + return stackblock() + len; +} + + +/* + * Called from CHECKSTRSPACE. + */ + +char * +makestrspace() { + int len = stackblocksize() - sstrnleft; + growstackblock(); + sstrnleft = stackblocksize() - len; + return stackblock() + len; +} + + + +void +ungrabstackstr(s, p) + char *s; + char *p; + { + stacknleft += stacknxt - s; + stacknxt = s; + sstrnleft = stacknleft - (p - s); +} diff --git a/commands/ash/memalloc.h b/commands/ash/memalloc.h new file mode 100755 index 000000000..4c663ae20 --- /dev/null +++ b/commands/ash/memalloc.h @@ -0,0 +1,95 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)memalloc.h 5.1 (Berkeley) 3/7/91 + */ + +struct stackmark { + struct stack_block *stackp; + char *stacknxt; + int stacknleft; +}; + + +extern char *stacknxt; +extern int stacknleft; +extern int sstrnleft; +extern int herefd; + +#ifdef __STDC__ +pointer ckmalloc(int); +pointer ckrealloc(pointer, int); +void free(pointer); /* defined in C library */ +char *savestr(char *); +pointer stalloc(int); +void stunalloc(pointer); +void setstackmark(struct stackmark *); +void popstackmark(struct stackmark *); +void growstackblock(void); +void grabstackblock(int); +char *growstackstr(void); +char *makestrspace(void); +void ungrabstackstr(char *, char *); +#else +pointer ckmalloc(); +pointer ckrealloc(); +void free(); /* defined in C library */ +char *savestr(); +pointer stalloc(); +void stunalloc(); +void setstackmark(); +void popstackmark(); +void growstackblock(); +void grabstackblock(); +char *growstackstr(); +char *makestrspace(); +void ungrabstackstr(); +#endif + + + +#define stackblock() stacknxt +#define stackblocksize() stacknleft +#define STARTSTACKSTR(p) p = stackblock(), sstrnleft = stackblocksize() +#define STPUTC(c, p) (--sstrnleft >= 0? (*p++ = (c)) : (p = growstackstr(), *p++ = (c))) +#define CHECKSTRSPACE(n, p) if (sstrnleft < n) p = makestrspace(); else +#define USTPUTC(c, p) (--sstrnleft, *p++ = (c)) +#define STACKSTRNUL(p) (sstrnleft == 0? (p = growstackstr(), *p = '\0') : (*p = '\0')) +#define STUNPUTC(p) (++sstrnleft, --p) +#define STTOPC(p) p[-1] +#define STADJUST(amount, p) (p += (amount), sstrnleft -= (amount)) +#define grabstackstr(p) stalloc(stackblocksize() - sstrnleft) + +#define ckfree(p) free((pointer)(p)) diff --git a/commands/ash/miscbltin.c b/commands/ash/miscbltin.c new file mode 100755 index 000000000..f8345b4a6 --- /dev/null +++ b/commands/ash/miscbltin.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)miscbltin.c 5.2 (Berkeley) 3/13/91"; +#endif /* not lint */ + +/* + * Miscelaneous builtins. + */ + +#include "shell.h" +#include "options.h" +#include "var.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" + +#undef eflag + +extern char **argptr; /* argument list for builtin command */ + + +/* + * The read builtin. The -e option causes backslashes to escape the + * following character. + * + * This uses unbuffered input, which may be avoidable in some cases. + */ + +readcmd(argc, argv) char **argv; { + char **ap; + int backslash; + char c; + int eflag; + char *prompt; + char *ifs; + char *p; + int startword; + int status; + int i; + + eflag = 0; + prompt = NULL; + while ((i = nextopt("ep:")) != '\0') { + if (i == 'p') + prompt = optarg; + else + eflag = 1; + } + if (prompt && isatty(0)) { + out2str(prompt); + flushall(); + } + if (*(ap = argptr) == NULL) + error("arg count"); + if ((ifs = bltinlookup("IFS", 1)) == NULL) + ifs = nullstr; + status = 0; + startword = 1; + backslash = 0; + STARTSTACKSTR(p); + for (;;) { + if (read(0, &c, 1) != 1) { + status = 1; + break; + } + if (c == '\0') + continue; + if (backslash) { + backslash = 0; + if (c != '\n') + STPUTC(c, p); + continue; + } + if (eflag && c == '\\') { + backslash++; + continue; + } + if (c == '\n') + break; + if (startword && *ifs == ' ' && strchr(ifs, c)) { + continue; + } + startword = 0; + if (backslash && c == '\\') { + if (read(0, &c, 1) != 1) { + status = 1; + break; + } + STPUTC(c, p); + } else if (ap[1] != NULL && strchr(ifs, c) != NULL) { + STACKSTRNUL(p); + setvar(*ap, stackblock(), 0); + ap++; + startword = 1; + STARTSTACKSTR(p); + } else { + STPUTC(c, p); + } + } + STACKSTRNUL(p); + setvar(*ap, stackblock(), 0); + while (*++ap != NULL) + setvar(*ap, nullstr, 0); + return status; +} + + + +umaskcmd(argc, argv) char **argv; { + int mask; + char *p; + int i; + + if ((p = argv[1]) == NULL) { + INTOFF; + mask = umask(0); + umask(mask); + INTON; + out1fmt("%.4o\n", mask); /* %#o might be better */ + } else { + mask = 0; + do { + if ((unsigned)(i = *p - '0') >= 8) + error("Illegal number: %s", argv[1]); + mask = (mask << 3) + i; + } while (*++p != '\0'); + umask(mask); + } + return 0; +} diff --git a/commands/ash/mkbuiltins b/commands/ash/mkbuiltins new file mode 100755 index 000000000..e729cdaf7 --- /dev/null +++ b/commands/ash/mkbuiltins @@ -0,0 +1,123 @@ +#!/bin/sh - +# +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)mkbuiltins 5.2 (Berkeley) 3/8/91 + +# All calls to awk removed, because Minix bawk is deficient. (kjb) + +if [ $# != 2 ] +then + echo "USAGE: $0 shell.h builtins" + exit 1 +fi +SHL=$1 +BLTINS=$2 + +temp=/tmp/ka$$ +exec > builtins.c +cat <<\! +/* + * This file was generated by the mkbuiltins program. + */ + +#include "shell.h" +#include "builtins.h" + +! +if grep '^#define JOBS[ ]*1' $SHL > /dev/null +then + # Job control. + sed -e '/^#/d + s/ / /g + s/ #.*// + /^ *$/d + s/-j//' $BLTINS > $temp +else + # No job control. + sed -e '/^#/d + s/ / /g + s/ #.*// + /^ *$/d + /-j/d' $BLTINS > $temp +fi +sed -e 's/ .*// + s/\(.*\)/int \1();/' $temp +echo ' +int (*const builtinfunc[])() = {' +sed -e 's/ .*// + s/\(.*\)/ \1,/' $temp +echo '}; + +const struct builtincmd builtincmd[] = {' +i=0 +while read line +do + set -$- $line + shift + for fun + do + echo " \"$fun\", $i," + done + i=`expr $i + 1` +done < $temp +echo ' NULL, 0 +};' + +exec > builtins.h +cat <<\! +/* + * This file was generated by the mkbuiltins program. + */ + +#include +! +i=0 +tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ < $temp | + while read line + do + set -$- $line + echo "#define $1 $i" + i=`expr $i + 1` + done +echo ' +struct builtincmd { + char *name; + int code; +}; + +extern int (*const builtinfunc[])(); +extern const struct builtincmd builtincmd[];' +rm -f $temp diff --git a/commands/ash/mkinit.c b/commands/ash/mkinit.c new file mode 100755 index 000000000..c27cbbdf9 --- /dev/null +++ b/commands/ash/mkinit.c @@ -0,0 +1,547 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mkinit.c 5.3 (Berkeley) 3/13/91"; +#endif /* not lint */ + +/* + * This program scans all the source files for code to handle various + * special events and combines this code into one file. This (allegedly) + * improves the structure of the program since there is no need for + * anyone outside of a module to know that that module performs special + * operations on particular events. The command is executed iff init.c + * is actually changed. + * + * Usage: mkinit command sourcefile... + */ + + +#include + +#include +#include +#include + + +/* + * OUTFILE is the name of the output file. Output is initially written + * to the file OUTTEMP, which is then moved to OUTFILE if OUTTEMP and + * OUTFILE are different. + */ + +#define OUTFILE "init.c" +#define OUTTEMP "init.c.new" +#define OUTOBJ "init.o" + + +/* + * A text structure is basicly just a string that grows as more characters + * are added onto the end of it. It is implemented as a linked list of + * blocks of characters. The routines addstr and addchar append a string + * or a single character, respectively, to a text structure. Writetext + * writes the contents of a text structure to a file. + */ + +#define BLOCKSIZE 512 + +struct text { + char *nextc; + int nleft; + struct block *start; + struct block *last; +}; + +struct block { + struct block *next; + char text[BLOCKSIZE]; +}; + + +/* + * There is one event structure for each event that mkinit handles. + */ + +struct event { + char *name; /* name of event (e.g. INIT) */ + char *routine; /* name of routine called on event */ + char *comment; /* comment describing routine */ + struct text code; /* code for handling event */ +}; + + +char writer[] = "\ +/*\n\ + * This file was generated by the mkinit program.\n\ + */\n\ +\n"; + +char init[] = "\ +/*\n\ + * Initialization code.\n\ + */\n"; + +char reset[] = "\ +/*\n\ + * This routine is called when an error or an interrupt occurs in an\n\ + * interactive shell and control is returned to the main command loop.\n\ + */\n"; + +char shellproc[] = "\ +/*\n\ + * This routine is called to initialize the shell to run a shell procedure.\n\ + */\n"; + + +struct event event[] = { + {"INIT", "init", init}, + {"RESET", "reset", reset}, + {"SHELLPROC", "initshellproc", shellproc}, + {NULL, NULL} +}; + + +char *curfile; /* current file */ +int linno; /* current line */ +char *header_files[200]; /* list of header files */ +struct text defines; /* #define statements */ +struct text decls; /* declarations */ +int amiddecls; /* for formatting */ + + +void readfile(), doevent(), doinclude(), dodecl(), output(); +void addstr(), addchar(), writetext(); + +#define equal(s1, s2) (strcmp(s1, s2) == 0) + +FILE *ckfopen(); +char *savestr(); +void *ckmalloc __P((int)); +void error(); + +main(argc, argv) + char **argv; + { + char **ap; + int fd; + char c; + + if (argc < 2) + error("Usage: mkinit command file..."); + header_files[0] = "\"shell.h\""; + header_files[1] = "\"mystring.h\""; + for (ap = argv + 2 ; *ap ; ap++) + readfile(*ap); + output(); + if (file_changed()) { + unlink(OUTFILE); + link(OUTTEMP, OUTFILE); + unlink(OUTTEMP); + } else { + unlink(OUTTEMP); + if (touch(OUTOBJ)) + exit(0); /* no compilation necessary */ + } + printf("%s\n", argv[1]); + execl("/bin/sh", "sh", "-c", argv[1], (char *)0); + error("Can't exec shell"); +} + + +/* + * Parse an input file. + */ + +void +readfile(fname) + char *fname; + { + FILE *fp; + char line[1024]; + struct event *ep; + + fp = ckfopen(fname, "r"); + curfile = fname; + linno = 0; + amiddecls = 0; + while (fgets(line, sizeof line, fp) != NULL) { + linno++; + for (ep = event ; ep->name ; ep++) { + if (line[0] == ep->name[0] && match(ep->name, line)) { + doevent(ep, fp, fname); + break; + } + } + if (line[0] == 'I' && match("INCLUDE", line)) + doinclude(line); + if (line[0] == 'M' && match("MKINIT", line)) + dodecl(line, fp); + if (line[0] == '#' && gooddefine(line)) + addstr(line, &defines); + } + fclose(fp); +} + + +int +match(name, line) + char *name; + char *line; + { + register char *p, *q; + + p = name, q = line; + while (*p) { + if (*p++ != *q++) + return 0; + } + if (*q != '{' && *q != ' ' && *q != '\t' && *q != '\n') + return 0; + return 1; +} + + +int +gooddefine(line) + char *line; + { + register char *p; + + if (! match("#define", line)) + return 0; /* not a define */ + p = line + 7; + while (*p == ' ' || *p == '\t') + p++; + while (*p != ' ' && *p != '\t') { + if (*p == '(') + return 0; /* macro definition */ + p++; + } + while (*p != '\n' && *p != '\0') + p++; + if (p[-1] == '\\') + return 0; /* multi-line definition */ + return 1; +} + + +void +doevent(ep, fp, fname) + register struct event *ep; + FILE *fp; + char *fname; + { + char line[1024]; + int indent; + char *p; + + sprintf(line, "\n /* from %s: */\n", fname); + addstr(line, &ep->code); + addstr(" {\n", &ep->code); + for (;;) { + linno++; + if (fgets(line, sizeof line, fp) == NULL) + error("Unexpected EOF"); + if (equal(line, "}\n")) + break; + indent = 6; + for (p = line ; *p == '\t' ; p++) + indent += 8; + for ( ; *p == ' ' ; p++) + indent++; + if (*p == '\n' || *p == '#') + indent = 0; + while (indent >= 8) { + addchar('\t', &ep->code); + indent -= 8; + } + while (indent > 0) { + addchar(' ', &ep->code); + indent--; + } + addstr(p, &ep->code); + } + addstr(" }\n", &ep->code); +} + + +void +doinclude(line) + char *line; + { + register char *p; + char *name; + register char **pp; + + for (p = line ; *p != '"' && *p != '<' && *p != '\0' ; p++); + if (*p == '\0') + error("Expecting '\"' or '<'"); + name = p; + while (*p != ' ' && *p != '\t' && *p != '\n') + p++; + if (p[-1] != '"' && p[-1] != '>') + error("Missing terminator"); + *p = '\0'; + + /* name now contains the name of the include file */ + for (pp = header_files ; *pp && ! equal(*pp, name) ; pp++); + if (*pp == NULL) + *pp = savestr(name); +} + + +void +dodecl(line1, fp) + char *line1; + FILE *fp; + { + char line[1024]; + register char *p, *q; + + if (strcmp(line1, "MKINIT\n") == 0) { /* start of struct/union decl */ + addchar('\n', &decls); + do { + linno++; + if (fgets(line, sizeof line, fp) == NULL) + error("Unterminated structure declaration"); + addstr(line, &decls); + } while (line[0] != '}'); + amiddecls = 0; + } else { + if (! amiddecls) + addchar('\n', &decls); + q = NULL; + for (p = line1 + 6 ; *p != '=' && *p != '/' ; p++); + if (*p == '=') { /* eliminate initialization */ + for (q = p ; *q && *q != ';' ; q++); + if (*q == '\0') + q = NULL; + else { + while (p[-1] == ' ') + p--; + *p = '\0'; + } + } + addstr("extern", &decls); + addstr(line1 + 6, &decls); + if (q != NULL) + addstr(q, &decls); + amiddecls = 1; + } +} + + + +/* + * Write the output to the file OUTTEMP. + */ + +void +output() { + FILE *fp; + char **pp; + struct event *ep; + + fp = ckfopen(OUTTEMP, "w"); + fputs(writer, fp); + for (pp = header_files ; *pp ; pp++) + fprintf(fp, "#include %s\n", *pp); + fputs("\n\n\n", fp); + writetext(&defines, fp); + fputs("\n\n", fp); + writetext(&decls, fp); + for (ep = event ; ep->name ; ep++) { + fputs("\n\n\n", fp); + fputs(ep->comment, fp); + fprintf(fp, "\nvoid\n%s() {\n", ep->routine); + writetext(&ep->code, fp); + fprintf(fp, "}\n"); + } + fclose(fp); +} + + +/* + * Return true if the new output file is different from the old one. + */ + +int +file_changed() { + register FILE *f1, *f2; + register int c; + + if ((f1 = fopen(OUTFILE, "r")) == NULL + || (f2 = fopen(OUTTEMP, "r")) == NULL) + return 1; + while ((c = getc(f1)) == getc(f2)) { + if (c == EOF) + return 0; + } + return 1; +} + + +/* + * Touch a file. Returns 0 on failure, 1 on success. + */ + +int +touch(file) + char *file; + { + int fd; + char c; + + if ((fd = open(file, O_RDWR)) < 0) + return 0; + if (read(fd, &c, 1) != 1) { + close(fd); + return 0; + } + lseek(fd, 0L, 0); + write(fd, &c, 1); + close(fd); + return 1; +} + + + +/* + * A text structure is simply a block of text that is kept in memory. + * Addstr appends a string to the text struct, and addchar appends a single + * character. + */ + +void +addstr(s, text) + register char *s; + register struct text *text; + { + while (*s) { + if (--text->nleft < 0) + addchar(*s++, text); + else + *text->nextc++ = *s++; + } +} + + +void +addchar(c, text) + register struct text *text; + { + struct block *bp; + + if (--text->nleft < 0) { + bp = ckmalloc(sizeof *bp); + if (text->start == NULL) + text->start = bp; + else + text->last->next = bp; + text->last = bp; + text->nextc = bp->text; + text->nleft = BLOCKSIZE - 1; + } + *text->nextc++ = c; +} + +/* + * Write the contents of a text structure to a file. + */ +void +writetext(text, fp) + struct text *text; + FILE *fp; + { + struct block *bp; + + if (text->start != NULL) { + for (bp = text->start ; bp != text->last ; bp = bp->next) + fwrite(bp->text, sizeof (char), BLOCKSIZE, fp); + fwrite(bp->text, sizeof (char), BLOCKSIZE - text->nleft, fp); + } +} + +FILE * +ckfopen(file, mode) + char *file; + char *mode; + { + FILE *fp; + + if ((fp = fopen(file, mode)) == NULL) { + fprintf(stderr, "Can't open %s\n", file); + exit(2); + } + return fp; +} + +void * +ckmalloc(nbytes) { + register char *p; + char *malloc(); + + if ((p = malloc(nbytes)) == NULL) + error("Out of space"); + return p; +} + +char * +savestr(s) + char *s; + { + register char *p; + + p = ckmalloc(strlen(s) + 1); + strcpy(p, s); + return p; +} + +void +error(msg) + char *msg; + { + if (curfile != NULL) + fprintf(stderr, "%s:%d: ", curfile, linno); + fprintf(stderr, "%s\n", msg); + exit(2); +} diff --git a/commands/ash/mknodes.c b/commands/ash/mknodes.c new file mode 100755 index 000000000..ff7d89b61 --- /dev/null +++ b/commands/ash/mknodes.c @@ -0,0 +1,432 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mknodes.c 5.1 (Berkeley) 3/7/91"; +#endif /* not lint */ + +/* + * This program reads the nodetypes file and nodes.c.pat file. It generates + * the files nodes.h and nodes.c. + */ + +#include + + +#define MAXTYPES 50 /* max number of node types */ +#define MAXFIELDS 20 /* max fields in a structure */ +#define BUFLEN 100 /* size of character buffers */ + +/* field types */ +#define T_NODE 1 /* union node *field */ +#define T_NODELIST 2 /* struct nodelist *field */ +#define T_STRING 3 +#define T_INT 4 /* int field */ +#define T_OTHER 5 /* other */ +#define T_TEMP 6 /* don't copy this field */ + + +struct field { /* a structure field */ + char *name; /* name of field */ + int type; /* type of field */ + char *decl; /* declaration of field */ +}; + + +struct str { /* struct representing a node structure */ + char *tag; /* structure tag */ + int nfields; /* number of fields in the structure */ + struct field field[MAXFIELDS]; /* the fields of the structure */ + int done; /* set if fully parsed */ +}; + + +int ntypes; /* number of node types */ +char *nodename[MAXTYPES]; /* names of the nodes */ +struct str *nodestr[MAXTYPES]; /* type of structure used by the node */ +int nstr; /* number of structures */ +struct str str[MAXTYPES]; /* the structures */ +struct str *curstr; /* current structure */ + + +FILE *infp = stdin; +char line[1024]; +int linno; +char *linep; + + +char *savestr(); +#define equal(s1, s2) (strcmp(s1, s2) == 0) + + +main(argc, argv) + char **argv; + { + if (argc != 3) + error("usage: mknodes file\n"); + if ((infp = fopen(argv[1], "r")) == NULL) + error("Can't open %s", argv[1]); + while (readline()) { + if (line[0] == ' ' || line[0] == '\t') + parsefield(); + else if (line[0] != '\0') + parsenode(); + } + output(argv[2]); + return 0; +} + + + +parsenode() { + char name[BUFLEN]; + char tag[BUFLEN]; + struct str *sp; + + if (curstr && curstr->nfields > 0) + curstr->done = 1; + nextfield(name); + if (! nextfield(tag)) + error("Tag expected"); + if (*linep != '\0') + error("Garbage at end of line"); + nodename[ntypes] = savestr(name); + for (sp = str ; sp < str + nstr ; sp++) { + if (equal(sp->tag, tag)) + break; + } + if (sp >= str + nstr) { + sp->tag = savestr(tag); + sp->nfields = 0; + curstr = sp; + nstr++; + } + nodestr[ntypes] = sp; + ntypes++; +} + + +parsefield() { + char name[BUFLEN]; + char type[BUFLEN]; + char decl[2 * BUFLEN]; + struct field *fp; + + if (curstr == NULL || curstr->done) + error("No current structure to add field to"); + if (! nextfield(name)) + error("No field name"); + if (! nextfield(type)) + error("No field type"); + fp = &curstr->field[curstr->nfields]; + fp->name = savestr(name); + if (equal(type, "nodeptr")) { + fp->type = T_NODE; + sprintf(decl, "union node *%s", name); + } else if (equal(type, "nodelist")) { + fp->type = T_NODELIST; + sprintf(decl, "struct nodelist *%s", name); + } else if (equal(type, "string")) { + fp->type = T_STRING; + sprintf(decl, "char *%s", name); + } else if (equal(type, "int")) { + fp->type = T_INT; + sprintf(decl, "int %s", name); + } else if (equal(type, "other")) { + fp->type = T_OTHER; + } else if (equal(type, "temp")) { + fp->type = T_TEMP; + } else { + error("Unknown type %s", type); + } + if (fp->type == T_OTHER || fp->type == T_TEMP) { + skipbl(); + fp->decl = savestr(linep); + } else { + if (*linep) + error("Garbage at end of line"); + fp->decl = savestr(decl); + } + curstr->nfields++; +} + + +char writer[] = "\ +/*\n\ + * This file was generated by the mknodes program.\n\ + */\n\ +\n"; + +output(file) + char *file; + { + FILE *hfile; + FILE *cfile; + FILE *patfile; + int i; + struct str *sp; + struct field *fp; + char *p; + + if ((patfile = fopen(file, "r")) == NULL) + error("Can't open %s", file); + if ((hfile = fopen("nodes.h", "w")) == NULL) + error("Can't create nodes.h"); + if ((cfile = fopen("nodes.c", "w")) == NULL) + error("Can't create nodes.c"); + fputs(writer, hfile); + for (i = 0 ; i < ntypes ; i++) + fprintf(hfile, "#define %s %d\n", nodename[i], i); + fputs("\n\n\n", hfile); + for (sp = str ; sp < &str[nstr] ; sp++) { + fprintf(hfile, "struct %s {\n", sp->tag); + for (i = sp->nfields, fp = sp->field ; --i >= 0 ; fp++) { + fprintf(hfile, " %s;\n", fp->decl); + } + fputs("};\n\n\n", hfile); + } + fputs("union node {\n", hfile); + fprintf(hfile, " int type;\n"); + for (sp = str ; sp < &str[nstr] ; sp++) { + fprintf(hfile, " struct %s %s;\n", sp->tag, sp->tag); + } + fputs("};\n\n\n", hfile); + fputs("struct nodelist {\n", hfile); + fputs("\tstruct nodelist *next;\n", hfile); + fputs("\tunion node *n;\n", hfile); + fputs("};\n\n\n", hfile); + fputs("#ifdef __STDC__\n", hfile); + fputs("union node *copyfunc(union node *);\n", hfile); + fputs("void freefunc(union node *);\n", hfile); + fputs("#else\n", hfile); + fputs("union node *copyfunc();\n", hfile); + fputs("void freefunc();\n", hfile); + fputs("#endif\n", hfile); + + fputs(writer, cfile); + while (fgets(line, sizeof line, patfile) != NULL) { + for (p = line ; *p == ' ' || *p == '\t' ; p++); + if (equal(p, "%SIZES\n")) + outsizes(cfile); + else if (equal(p, "%CALCSIZE\n")) + outfunc(cfile, 1); + else if (equal(p, "%COPY\n")) + outfunc(cfile, 0); + else + fputs(line, cfile); + } +} + + + +outsizes(cfile) + FILE *cfile; + { + int i; + + fprintf(cfile, "static const short nodesize[%d] = {\n", ntypes); + for (i = 0 ; i < ntypes ; i++) { + fprintf(cfile, " ALIGN(sizeof (struct %s)),\n", nodestr[i]->tag); + } + fprintf(cfile, "};\n"); +} + + +outfunc(cfile, calcsize) + FILE *cfile; + { + struct str *sp; + struct field *fp; + int i; + + fputs(" if (n == NULL)\n", cfile); + if (calcsize) + fputs(" return;\n", cfile); + else + fputs(" return NULL;\n", cfile); + if (calcsize) + fputs(" funcblocksize += nodesize[n->type];\n", cfile); + else { + fputs(" new = funcblock;\n", cfile); + fputs(" *(char **)&funcblock += nodesize[n->type];\n", + cfile); + } + fputs(" switch (n->type) {\n", cfile); + for (sp = str ; sp < &str[nstr] ; sp++) { + for (i = 0 ; i < ntypes ; i++) { + if (nodestr[i] == sp) + fprintf(cfile, " case %s:\n", nodename[i]); + } + for (i = sp->nfields ; --i >= 1 ; ) { + fp = &sp->field[i]; + switch (fp->type) { + case T_NODE: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "calcsize(n->%s.%s);\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = copynode(n->%s.%s);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_NODELIST: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "sizenodelist(n->%s.%s);\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = copynodelist(n->%s.%s);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_STRING: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "funcstringsize += strlen(n->%s.%s) + 1;\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = nodesavestr(n->%s.%s);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_INT: + case T_OTHER: + if (! calcsize) { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = n->%s.%s;\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + } + } + indent(12, cfile); + fputs("break;\n", cfile); + } + fputs(" };\n", cfile); + if (! calcsize) + fputs(" new->type = n->type;\n", cfile); +} + + +indent(amount, fp) + FILE *fp; + { + while (amount >= 8) { + putc('\t', fp); + amount -= 8; + } + while (--amount >= 0) { + putc(' ', fp); + } +} + + +int +nextfield(buf) + char *buf; + { + register char *p, *q; + + p = linep; + while (*p == ' ' || *p == '\t') + p++; + q = buf; + while (*p != ' ' && *p != '\t' && *p != '\0') + *q++ = *p++; + *q = '\0'; + linep = p; + return (q > buf); +} + + +skipbl() { + while (*linep == ' ' || *linep == '\t') + linep++; +} + + +int +readline() { + register char *p; + + if (fgets(line, 1024, infp) == NULL) + return 0; + for (p = line ; *p != '#' && *p != '\n' && *p != '\0' ; p++); + while (p > line && (p[-1] == ' ' || p[-1] == '\t')) + p--; + *p = '\0'; + linep = line; + linno++; + if (p - line > BUFLEN) + error("Line too long"); + return 1; +} + + + +error(msg, a1, a2, a3, a4, a5, a6) + char *msg; + { + fprintf(stderr, "line %d: ", linno); + fprintf(stderr, msg, a1, a2, a3, a4, a5, a6); + putc('\n', stderr); + exit(2); +} + + + +char * +savestr(s) + char *s; + { + register char *p; + char *malloc(); + + if ((p = malloc(strlen(s) + 1)) == NULL) + error("Out of space"); + strcpy(p, s); + return p; +} diff --git a/commands/ash/mksignames.c b/commands/ash/mksignames.c new file mode 100755 index 000000000..640fcfd14 --- /dev/null +++ b/commands/ash/mksignames.c @@ -0,0 +1,200 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mksignames.c 5.1 (Berkeley) 3/7/91"; +#endif /* not lint */ + +/* + * This program generates the signames.h and signames.c files. + */ +#include +#include +#include + + + +struct sig { + int signo; /* signal number */ + char *name; /* signal name (without leading "SIG") */ + char *mesg; /* description */ +}; + + +struct sig sigtab[] = { + SIGHUP, "HUP", "Hangup", + SIGINT, "INT", "Interrupt", /* normally don't print message */ + SIGQUIT, "QUIT", "Quit", + SIGILL, "ILL", "Illegal instruction", + SIGTRAP, "TRAP", "Trace/BPT trap", +#ifdef SIGABRT + SIGABRT, "ABRT", "abort", +#endif +#if defined(SIGIOT) && (! defined(SIGABRT) || SIGABRT != SIGIOT) + SIGIOT, "IOT", "abort", +#endif +#ifdef SIGEMT + SIGEMT, "EMT", "EMT trap", +#endif + SIGFPE, "FPE", "Floating exception", + SIGKILL, "KILL", "Killed", + SIGBUS, "BUS", "Bus error", + SIGSEGV, "SEGV", "Memory fault", +#ifdef SIGSYS + SIGSYS, "SYS", "Bad system call", +#endif + SIGPIPE, "PIPE", "Broken pipe", /* normally don't print message */ + SIGALRM, "ALRM", "Alarm call", + SIGTERM, "TERM", "Terminated", +#ifdef SIGUSR1 + SIGUSR1, "USR1", "User signal 1", +#endif +#ifdef SIGUSR2 + SIGUSR2, "USR2", "User signal 2", +#endif +#ifdef SIGCLD + SIGCLD, "CLD", NULL, +#endif +#if defined(SIGCHLD) && ! defined(SIGCLD) + SIGCHLD, "CLD", NULL, +#endif +#ifdef SIGPWR + SIGPWR, "PWR", "Power fail", +#endif +#ifdef SIGPOLL + SIGPOLL, "POLL", "Poll", +#endif + /* Now for the BSD signals */ +#ifdef SIGURG + SIGURG, "URG", NULL, +#endif +#ifdef SIGSTOP + SIGSTOP, "STOP", "Stopped", +#endif +#ifdef SIGTSTP + SIGTSTP, "TSTP", "Stopped", +#endif +#ifdef SIGCONT + SIGCONT, "CONT", NULL, +#endif +#ifdef SIGTTIN + SIGTTIN, "TTIN", "Stopped (input)", +#endif +#ifdef SIGTTOU + SIGTTOU, "TTOU", "Stopped (output)", +#endif +#ifdef SIGIO + SIGIO, "IO", NULL, +#endif +#ifdef SIGXCPU + SIGXCPU, "XCPU", "Time limit exceeded", +#endif +#ifdef SIGXFSZ + SIGXFSZ, "XFSZ", NULL, +#endif +#ifdef SIGVTALARM + SIGVTALARM, "VTALARM", "Virtual alarm", +#endif +#ifdef SIGPROF + SIGPROF, "PROF", "Profiling alarm", +#endif +#ifdef SIGWINCH + SIGWINCH, "WINCH", NULL, +#endif + 0, NULL, NULL +}; + + +#define MAXSIG 64 + + +char *sigmesg[MAXSIG + 1]; + + +char writer[] = "\ +/*\n\ + * This file was generated by the mksignames program.\n\ + */\n\ +\n"; + + + +main(argc, argv) char **argv; { + FILE *cfile, *hfile; + struct sig *sigp; + int maxsig; + int i; + + if ((cfile = fopen("signames.c", "w")) == NULL) { + fputs("Can't create signames.c\n", stderr); + exit(2); + } + if ((hfile = fopen("signames.h", "w")) == NULL) { + fputs("Can't create signames.h\n", stderr); + exit(2); + } + maxsig = 0; + for (sigp = sigtab ; sigp->signo != 0 ; sigp++) { + if (sigp->signo < 0 || sigp->signo > MAXSIG) + continue; + sigmesg[sigp->signo] = sigp->mesg; + if (maxsig < sigp->signo) + maxsig = sigp->signo; + } + + fputs(writer, hfile); + fprintf(hfile, "#define MAXSIG %d\n\n", maxsig); + fprintf(hfile, "extern char *const sigmesg[MAXSIG+1];\n"); + + fputs(writer, cfile); + fprintf(cfile, "#include \"shell.h\"\n\n"); + fprintf(cfile, "char *const sigmesg[%d] = {\n", maxsig + 1); + for (i = 0 ; i <= maxsig ; i++) { + if (sigmesg[i] == NULL) { + fprintf(cfile, " 0,\n"); + } else { + fprintf(cfile, " \"%s\",\n", sigmesg[i]); + } + } + fprintf(cfile, "};\n"); + exit(0); +} diff --git a/commands/ash/mksyntax.c b/commands/ash/mksyntax.c new file mode 100755 index 000000000..5b26fc004 --- /dev/null +++ b/commands/ash/mksyntax.c @@ -0,0 +1,356 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1991 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mksyntax.c 5.2 (Berkeley) 3/8/91"; +#endif /* not lint */ + +/* + * This program creates syntax.h and syntax.c. + */ + +#include +#include "parser.h" + + +struct synclass { + char *name; + char *comment; +}; + +/* Syntax classes */ +struct synclass synclass[] = { + "CWORD", "character is nothing special", + "CNL", "newline character", + "CBACK", "a backslash character", + "CSQUOTE", "single quote", + "CDQUOTE", "double quote", + "CENDQUOTE", "a terminating quote", + "CBQUOTE", "backwards single quote", + "CVAR", "a dollar sign", + "CENDVAR", "a '}' character", + "CEOF", "end of file", + "CCTL", "like CWORD, except it must be escaped", + "CSPCL", "these terminate a word", + NULL, NULL +}; + + +/* + * Syntax classes for is_ functions. Warning: if you add new classes + * you may have to change the definition of the is_in_name macro. + */ +struct synclass is_entry[] = { + "ISDIGIT", "a digit", + "ISUPPER", "an upper case letter", + "ISLOWER", "a lower case letter", + "ISUNDER", "an underscore", + "ISSPECL", "the name of a special parameter", + NULL, NULL, +}; + +char writer[] = "\ +/*\n\ + * This file was generated by the mksyntax program.\n\ + */\n\ +\n"; + + +FILE *cfile; +FILE *hfile; +char *syntax[513]; +int base; +int size; /* number of values which a char variable can have */ +int nbits; /* number of bits in a character */ +int digit_contig; /* true if digits are contiguous */ + + +main() { + char c; + char d; + int sign; + int i; + char buf[80]; + int pos; + static char digit[] = "0123456789"; + + /* Create output files */ + if ((cfile = fopen("syntax.c", "w")) == NULL) { + perror("syntax.c"); + exit(2); + } + if ((hfile = fopen("syntax.h", "w")) == NULL) { + perror("syntax.h"); + exit(2); + } + fputs(writer, hfile); + fputs(writer, cfile); + + /* Determine the characteristics of chars. */ + c = -1; + if (c < 0) + sign = 1; + else + sign = 0; + for (nbits = 1 ; ; nbits++) { + d = (1 << nbits) - 1; + if (d == c) + break; + } + printf("%s %d bit chars\n", sign? "signed" : "unsigned", nbits); + if (nbits > 9) { + fputs("Characters can't have more than 9 bits\n", stderr); + exit(2); + } + size = (1 << nbits) + 1; + base = 1; + if (sign) + base += 1 << (nbits - 1); + digit_contig = 1; + for (i = 0 ; i < 10 ; i++) { + if (digit[i] != '0' + i) + digit_contig = 0; + } + + fputs("#include \n", hfile); + + /* Generate the #define statements in the header file */ + fputs("/* Syntax classes */\n", hfile); + for (i = 0 ; synclass[i].name ; i++) { + sprintf(buf, "#define %s %d", synclass[i].name, i); + fputs(buf, hfile); + for (pos = strlen(buf) ; pos < 32 ; pos = pos + 8 &~ 07) + putc('\t', hfile); + fprintf(hfile, "/* %s */\n", synclass[i].comment); + } + putc('\n', hfile); + fputs("/* Syntax classes for is_ functions */\n", hfile); + for (i = 0 ; is_entry[i].name ; i++) { + sprintf(buf, "#define %s %#o", is_entry[i].name, 1 << i); + fputs(buf, hfile); + for (pos = strlen(buf) ; pos < 32 ; pos = pos + 8 &~ 07) + putc('\t', hfile); + fprintf(hfile, "/* %s */\n", is_entry[i].comment); + } + putc('\n', hfile); + fprintf(hfile, "#define SYNBASE %d\n", base); + fprintf(hfile, "#define PEOF %d\n\n", -base); + putc('\n', hfile); + fputs("#define BASESYNTAX (basesyntax + SYNBASE)\n", hfile); + fputs("#define DQSYNTAX (dqsyntax + SYNBASE)\n", hfile); + fputs("#define SQSYNTAX (sqsyntax + SYNBASE)\n", hfile); + putc('\n', hfile); + output_type_macros(); /* is_digit, etc. */ + putc('\n', hfile); + + /* Generate the syntax tables. */ + fputs("#include \"shell.h\"\n", cfile); + fputs("#include \"syntax.h\"\n\n", cfile); + init(); + fputs("/* syntax table used when not in quotes */\n", cfile); + add("\n", "CNL"); + add("\\", "CBACK"); + add("'", "CSQUOTE"); + add("\"", "CDQUOTE"); + add("`", "CBQUOTE"); + add("$", "CVAR"); + add("}", "CENDVAR"); + add("<>();&| \t", "CSPCL"); + print("basesyntax"); + init(); + fputs("\n/* syntax table used when in double quotes */\n", cfile); + add("\n", "CNL"); + add("\\", "CBACK"); + add("\"", "CENDQUOTE"); + add("`", "CBQUOTE"); + add("$", "CVAR"); + add("}", "CENDVAR"); + add("!*?[=", "CCTL"); + print("dqsyntax"); + init(); + fputs("\n/* syntax table used when in single quotes */\n", cfile); + add("\n", "CNL"); + add("'", "CENDQUOTE"); + add("!*?[=", "CCTL"); + print("sqsyntax"); + filltable("0"); + fputs("\n/* character classification table */\n", cfile); + add("0123456789", "ISDIGIT"); + add("abcdefghijklmnopqrstucvwxyz", "ISLOWER"); + add("ABCDEFGHIJKLMNOPQRSTUCVWXYZ", "ISUPPER"); + add("_", "ISUNDER"); + add("#?$!-*@", "ISSPECL"); + print("is_type"); + if (! digit_contig) + digit_convert(); + exit(0); +} + + + +/* + * Clear the syntax table. + */ + +filltable(dftval) + char *dftval; + { + int i; + + for (i = 0 ; i < size ; i++) + syntax[i] = dftval; +} + + +/* + * Initialize the syntax table with default values. + */ + +init() { + filltable("CWORD"); + syntax[0] = "CEOF"; + syntax[base + CTLESC] = "CCTL"; + syntax[base + CTLVAR] = "CCTL"; + syntax[base + CTLENDVAR] = "CCTL"; + syntax[base + CTLBACKQ] = "CCTL"; + syntax[base + CTLBACKQ + CTLQUOTE] = "CCTL"; +} + + +/* + * Add entries to the syntax table. + */ + +add(p, type) + char *p, *type; + { + while (*p) + syntax[*p++ + base] = type; +} + + + +/* + * Output the syntax table. + */ + +print(name) + char *name; + { + int i; + int col; + + fprintf(hfile, "extern const char %s[];\n", name); + fprintf(cfile, "const char %s[%d] = {\n", name, size); + col = 0; + for (i = 0 ; i < size ; i++) { + if (i == 0) { + fputs(" ", cfile); + } else if ((i & 03) == 0) { + fputs(",\n ", cfile); + col = 0; + } else { + putc(',', cfile); + while (++col < 9 * (i & 03)) + putc(' ', cfile); + } + fputs(syntax[i], cfile); + col += strlen(syntax[i]); + } + fputs("\n};\n", cfile); +} + + + +/* + * Output character classification macros (e.g. is_digit). If digits are + * contiguous, we can test for them quickly. + */ + +char *macro[] = { + "#define is_digit(c)\t((is_type+SYNBASE)[c] & ISDIGIT)", + "#define is_alpha(c)\t((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER))", + "#define is_name(c)\t((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER|ISUNDER))", + "#define is_in_name(c)\t((is_type+SYNBASE)[c] & (ISUPPER|ISLOWER|ISUNDER|ISDIGIT))", + "#define is_special(c)\t((is_type+SYNBASE)[c] & (ISSPECL|ISDIGIT))", + NULL +}; + +output_type_macros() { + char **pp; + + if (digit_contig) + macro[0] = "#define is_digit(c)\t((unsigned)((c) - '0') <= 9)"; + for (pp = macro ; *pp ; pp++) + fprintf(hfile, "%s\n", *pp); + if (digit_contig) + fputs("#define digit_val(c)\t((c) - '0')\n", hfile); + else + fputs("#define digit_val(c)\t(digit_value[c])\n", hfile); +} + + + +/* + * Output digit conversion table (if digits are not contiguous). + */ + +digit_convert() { + int maxdigit; + static char digit[] = "0123456789"; + char *p; + int i; + + maxdigit = 0; + for (p = digit ; *p ; p++) + if (*p > maxdigit) + maxdigit = *p; + fputs("extern const char digit_value[];\n", hfile); + fputs("\n\nconst char digit_value[] = {\n", cfile); + for (i = 0 ; i <= maxdigit ; i++) { + for (p = digit ; *p && *p != i ; p++); + if (*p == '\0') + p = digit; + fprintf(cfile, " %d,\n", p - digit); + } + fputs("};\n", cfile); +} diff --git a/commands/ash/mktokens b/commands/ash/mktokens new file mode 100755 index 000000000..a9c33985c --- /dev/null +++ b/commands/ash/mktokens @@ -0,0 +1,121 @@ +#!/bin/sh - +# +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)mktokens 5.1 (Berkeley) 3/7/91 + +# All calls to awk removed, because Minix bawk is deficient. (kjb) + +# The following is a list of tokens. The second column is nonzero if the +# token marks the end of a list. The third column is the name to print in +# error messages. + +cat > /tmp/ka$$ <<\! +TEOF 1 end of file +TNL 0 newline +TSEMI 0 ";" +TBACKGND 0 "&" +TAND 0 "&&" +TOR 0 "||" +TPIPE 0 "|" +TLP 0 "(" +TRP 1 ")" +TENDCASE 1 ";;" +TENDBQUOTE 1 "`" +TREDIR 0 redirection +TWORD 0 word +TIF 0 "if" +TTHEN 1 "then" +TELSE 1 "else" +TELIF 1 "elif" +TFI 1 "fi" +TWHILE 0 "while" +TUNTIL 0 "until" +TFOR 0 "for" +TDO 1 "do" +TDONE 1 "done" +TBEGIN 0 "{" +TEND 1 "}" +TCASE 0 "case" +TESAC 1 "esac" +! +nl=`wc -l /tmp/ka$$` +exec > token.def +i=0 +while read line +do + set -$- $line + echo "#define $1 $i" + i=`expr $i + 1` +done 0) { + if ((*to++ = *from++) == '\0') + return; + } + *to = '\0'; +} + + +/* + * strchr - find first occurrence of a character in a string. + */ + +#ifndef SYS5 +char * +mystrchr(s, charwanted) + char const *s; + register char charwanted; + { + register char const *scan; + + /* + * The odd placement of the two tests is so NUL is findable. + */ + for (scan = s ; *scan != charwanted ; ) /* ++ moved down for opt. */ + if (*scan++ == '\0') + return NULL; + return (char *)scan; +} +#endif + + + +/* + * bcopy - copy bytes + * + * This routine was derived from code by Henry Spencer. + */ + +void +mybcopy(src, dst, length) + pointer dst; + const pointer src; + register int length; + { + register char *d = dst; + register char *s = src; + + while (--length >= 0) + *d++ = *s++; +} + + +/* + * prefix -- see if pfx is a prefix of string. + */ + +int +prefix(pfx, string) + register char const *pfx; + register char const *string; + { + while (*pfx) { + if (*pfx++ != *string++) + return 0; + } + return 1; +} + + +/* + * Convert a string of digits to an integer, printing an error message on + * failure. + */ + +int +number(s) + const char *s; + { + + if (! is_number(s)) + error2("Illegal number", (char *)s); + return atoi(s); +} + + + +/* + * Check for a valid number. This should be elsewhere. + */ + +int +is_number(p) + register const char *p; + { + do { + if (! is_digit(*p)) + return 0; + } while (*++p != '\0'); + return 1; +} diff --git a/commands/ash/mystring.h b/commands/ash/mystring.h new file mode 100755 index 000000000..6eaa1df0b --- /dev/null +++ b/commands/ash/mystring.h @@ -0,0 +1,71 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mystring.h 5.1 (Berkeley) 3/7/91 + */ + +#ifndef SYSV +#define strchr mystrchr +#endif + +#ifdef __STDC__ +void scopyn(const char *, char *, int); +char *strchr(const char *, int); +void mybcopy(const pointer, pointer, int); +int prefix(const char *, const char *); +int number(const char *); +int is_number(const char *); +int strcmp(const char *, const char *); /* from C library */ +char *strcpy(char *, const char *); /* from C library */ +int strlen(const char *); /* from C library */ +char *strcat(char *, const char *); /* from C library */ +char *strerror(int); /* from C library */ +#else +void scopyn(); +char *strchr(); +void mybcopy(); +int prefix(); +int number(); +int is_number(); +int strcmp(); +char *strcpy(); +int strlen(); +char *strcat(); +char *strerror(); +#endif + +#define equal(s1, s2) (strcmp(s1, s2) == 0) +#define scopy(s1, s2) ((void)strcpy(s2, s1)) +#define bcopy(src, dst, n) mybcopy((pointer)(src), (pointer)(dst), n) diff --git a/commands/ash/nodes.c.pat b/commands/ash/nodes.c.pat new file mode 100755 index 000000000..5193a295d --- /dev/null +++ b/commands/ash/nodes.c.pat @@ -0,0 +1,177 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)nodes.c.pat 5.2 (Berkeley) 3/8/91 + */ + +/* + * Routine for dealing with parsed shell commands. + */ + +#include "shell.h" +#include "nodes.h" +#include "memalloc.h" +#include "machdep.h" +#include "mystring.h" + + +int funcblocksize; /* size of structures in function */ +int funcstringsize; /* size of strings in node */ +#ifdef __STDC__ +pointer funcblock; /* block to allocate function from */ +#else +char *funcblock; /* block to allocate function from */ +#endif +char *funcstring; /* block to allocate strings from */ + +%SIZES + + +#ifdef __STDC__ +STATIC void calcsize(union node *); +STATIC void sizenodelist(struct nodelist *); +STATIC union node *copynode(union node *); +STATIC struct nodelist *copynodelist(struct nodelist *); +STATIC char *nodesavestr(char *); +#else +STATIC void calcsize(); +STATIC void sizenodelist(); +STATIC union node *copynode(); +STATIC struct nodelist *copynodelist(); +STATIC char *nodesavestr(); +#endif + + + +/* + * Make a copy of a parse tree. + */ + +union node * +copyfunc(n) + union node *n; + { + if (n == NULL) + return NULL; + funcblocksize = 0; + funcstringsize = 0; + calcsize(n); + funcblock = ckmalloc(funcblocksize + funcstringsize); + funcstring = (char *)funcblock + funcblocksize; + return copynode(n); +} + + + +STATIC void +calcsize(n) + union node *n; + { + %CALCSIZE +} + + + +STATIC void +sizenodelist(lp) + struct nodelist *lp; + { + while (lp) { + funcblocksize += ALIGN(sizeof (struct nodelist)); + calcsize(lp->n); + lp = lp->next; + } +} + + + +STATIC union node * +copynode(n) + union node *n; + { + union node *new; + + %COPY + return new; +} + + +STATIC struct nodelist * +copynodelist(lp) + struct nodelist *lp; + { + struct nodelist *start; + struct nodelist **lpp; + + lpp = &start; + while (lp) { + *lpp = funcblock; + *(char **)&funcblock += ALIGN(sizeof (struct nodelist)); + (*lpp)->n = copynode(lp->n); + lp = lp->next; + lpp = &(*lpp)->next; + } + *lpp = NULL; + return start; +} + + + +STATIC char * +nodesavestr(s) + char *s; + { + register char *p = s; + register char *q = funcstring; + char *rtn = funcstring; + + while (*q++ = *p++); + funcstring = q; + return rtn; +} + + + +/* + * Free a parse tree. + */ + +void +freefunc(n) + union node *n; + { + if (n) + ckfree(n); +} diff --git a/commands/ash/nodetypes b/commands/ash/nodetypes new file mode 100755 index 000000000..b7c680c7e --- /dev/null +++ b/commands/ash/nodetypes @@ -0,0 +1,140 @@ +#!/bin/sh - +# +# Copyright (c) 1991 The Regents of the University of California. +# All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. All advertising materials mentioning features or use of this software +# must display the following acknowledgement: +# This product includes software developed by the University of +# California, Berkeley and its contributors. +# 4. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)nodetypes 5.1 (Berkeley) 3/7/91 + +# This file describes the nodes used in parse trees. Unindented lines +# contain a node type followed by a structure tag. Subsequent indented +# lines specify the fields of the structure. Several node types can share +# the same structure, in which case the fields of the structure should be +# specified only once. +# +# A field of a structure is described by the name of the field followed +# by a type. The currently implemented types are: +# nodeptr - a pointer to a node +# nodelist - a pointer to a list of nodes +# string - a pointer to a nul terminated string +# int - an integer +# other - any type that can be copied by assignment +# temp - a field that doesn't have to be copied when the node is copied +# The last two types should be followed by the text of a C declaration for +# the field. + +NSEMI nbinary # two commands separated by a semicolon + type int + ch1 nodeptr # the first child + ch2 nodeptr # the second child + +NCMD ncmd # a simple command + type int + backgnd int # set to run command in background + args nodeptr # the arguments + redirect nodeptr # list of file redirections + +NPIPE npipe # a pipeline + type int + backgnd int # set to run pipeline in background + cmdlist nodelist # the commands in the pipeline + +NREDIR nredir # redirection (of a compex command) + type int + n nodeptr # the command + redirect nodeptr # list of file redirections + +NBACKGND nredir # run command in background +NSUBSHELL nredir # run command in a subshell + +NAND nbinary # the && operator +NOR nbinary # the || operator + +NIF nif # the if statement. Elif clauses are handled + type int # using multiple if nodes. + test nodeptr # if test + ifpart nodeptr # then ifpart + elsepart nodeptr # else elsepart + +NWHILE nbinary # the while statement. First child is the test +NUNTIL nbinary # the until statement + +NFOR nfor # the for statement + type int + args nodeptr # for var in args + body nodeptr # do body; done + var string # the for variable + +NCASE ncase # a case statement + type int + expr nodeptr # the word to switch on + cases nodeptr # the list of cases (NCLIST nodes) + +NCLIST nclist # a case + type int + next nodeptr # the next case in list + pattern nodeptr # list of patterns for this case + body nodeptr # code to execute for this case + + +NDEFUN narg # define a function. The "next" field contains + # the body of the function. + +NARG narg # represents a word + type int + next nodeptr # next word in list + text string # the text of the word + backquote nodelist # list of commands in back quotes + +NTO nfile # fd> fname +NFROM nfile # fd< fname +NAPPEND nfile # fd>> fname + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + fname nodeptr # file name, in a NARG node + expfname temp char *expfname # actual file name + +NTOFD ndup # fd<&dupfd +NFROMFD ndup # fd>&dupfd + type int + next nodeptr # next redirection in list + fd int # file descriptor being redirected + dupfd int # file descriptor to duplicate + +NHERE nhere # fd<<\! +NXHERE nhere # fd< 0) + argptr++; + for (p = optval ; p < optval + sizeof optval - 1 ; p++) + *p = 2; + options(1); + if (*argptr == NULL && minusc == NULL) + sflag = 1; + editable = (isatty(0) && isatty(1)); + if (iflag == 2 && sflag == 1 && editable) + iflag = 1; + if (jflag == 2) + jflag = iflag; + for (p = optval ; p < optval + sizeof optval - 1 ; p++) + if (*p == 2) + *p = 0; + arg0 = argv[0]; + if (sflag == 0) { + arg0 = *argptr++; + if (minusc == NULL) { + commandname = arg0; + setinputfile(commandname, 0); + } + } + shellparam.p = argptr; + /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ + while (*argptr) { + shellparam.nparam++; + argptr++; + } + setinteractive(iflag); + setjobctl(jflag); +} + + + +/* + * Process shell options. The global variable argptr contains a pointer + * to the argument list; we advance it past the options. + */ + +STATIC void +options(cmdline) { + register char *p; + int val; + int c; + + if (cmdline) + minusc = NULL; + while ((p = *argptr) != NULL) { + argptr++; + if ((c = *p++) == '-') { + val = 1; + if (p[0] == '\0' || p[0] == '-' && p[1] == '\0') { + if (!cmdline) { + /* "-" means turn off -x and -v */ + if (p[0] == '\0') + xflag = vflag = 0; + /* "--" means reset params */ + else if (*argptr == NULL) + setparam(argptr); + } + break; /* "-" or "--" terminates options */ + } + } else if (c == '+') { + val = 0; + } else { + argptr--; + break; + } + while ((c = *p++) != '\0') { + if (c == 'c' && cmdline) { + char *q; +#ifdef NOHACK /* removing this code allows sh -ce 'foo' for compat */ + if (*p == '\0') +#endif + q = *argptr++; + if (q == NULL || minusc != NULL) + error("Bad -c option"); + minusc = q; +#ifdef NOHACK + break; +#endif + } else { + setoption(c, val); + } + } + if (! cmdline) + break; + } +} + + +STATIC void +setoption(flag, val) + char flag; + int val; + { + register char *p; + + if ((p = strchr(optchar, flag)) == NULL) + error("Illegal option -%c", flag); + optval[p - optchar] = val; +} + + + +#ifdef mkinit +INCLUDE "options.h" + +SHELLPROC { + char *p; + + for (p = optval ; p < optval + sizeof optval ; p++) + *p = 0; +} +#endif + + +/* + * Set the shell parameters. + */ + +void +setparam(argv) + char **argv; + { + char **newparam; + char **ap; + int nparam; + + for (nparam = 0 ; argv[nparam] ; nparam++); + ap = newparam = ckmalloc((nparam + 1) * sizeof *ap); + while (*argv) { + *ap++ = savestr(*argv++); + } + *ap = NULL; + freeparam(&shellparam); + shellparam.malloc = 1; + shellparam.nparam = nparam; + shellparam.p = newparam; + shellparam.optnext = NULL; +} + + +/* + * Free the list of positional parameters. + */ + +void +freeparam(param) + struct shparam *param; + { + char **ap; + + if (param->malloc) { + for (ap = param->p ; *ap ; ap++) + ckfree(*ap); + ckfree(param->p); + } +} + + + +/* + * The shift builtin command. + */ + +shiftcmd(argc, argv) char **argv; { + int n; + char **ap1, **ap2; + + n = 1; + if (argc > 1) + n = number(argv[1]); + if (n > shellparam.nparam) + n = shellparam.nparam; + INTOFF; + shellparam.nparam -= n; + for (ap1 = shellparam.p ; --n >= 0 ; ap1++) { + if (shellparam.malloc) + ckfree(*ap1); + } + ap2 = shellparam.p; + while ((*ap2++ = *ap1++) != NULL); + shellparam.optnext = NULL; + INTON; + return 0; +} + + + +/* + * The set command builtin. + */ + +setcmd(argc, argv) char **argv; { + if (argc == 1) + return showvarscmd(argc, argv); + INTOFF; + options(0); + setinteractive(iflag); + setjobctl(jflag); + if (*argptr != NULL) { + setparam(argptr); + } + INTON; + return 0; +} + + +/* + * The getopts builtin. Shellparam.optnext points to the next argument + * to be processed. Shellparam.optptr points to the next character to + * be processed in the current argument. If shellparam.optnext is NULL, + * then it's the first time getopts has been called. + */ + +getoptscmd(argc, argv) char **argv; { + register char *p, *q; + char c; + char s[10]; + + if (argc != 3) + error("Usage: getopts optstring var"); + if (shellparam.optnext == NULL) { + shellparam.optnext = shellparam.p; + shellparam.optptr = NULL; + } + if ((p = shellparam.optptr) == NULL || *p == '\0') { + p = *shellparam.optnext; + if (p == NULL || *p != '-' || *++p == '\0') { +atend: + fmtstr(s, 10, "%d", shellparam.optnext - shellparam.p + 1); + setvar("OPTIND", s, 0); + shellparam.optnext = NULL; + return 1; + } + shellparam.optnext++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + goto atend; + } + c = *p++; + for (q = argv[1] ; *q != c ; ) { + if (*q == '\0') { + out1fmt("Illegal option -%c\n", c); + c = '?'; + goto out; + } + if (*++q == ':') + q++; + } + if (*++q == ':') { + if (*p == '\0') { + if ((p = *shellparam.optnext) == NULL) { + out1fmt("No arg for -%c option\n", c); + c = '?'; + goto out; + } + shellparam.optnext++; + } + setvar("OPTARG", p, 0); + p = ""; + } +out: + shellparam.optptr = p; + s[0] = c; + s[1] = '\0'; + setvar(argv[2], s, 0); + fmtstr(s, 10, "%d", shellparam.optnext - shellparam.p + 1); + setvar("OPTIND", s, 0); + return 0; +} + +/* + * Standard option processing (a la getopt) for builtin routines. The + * only argument that is passed to nextopt is the option string; the + * other arguments are unnecessary. It return the character, or '\0' on + * end of input. + */ + +int +nextopt(optstring) + char *optstring; + { + register char *p, *q; + char c; + + if ((p = optptr) == NULL || *p == '\0') { + p = *argptr; + if (p == NULL || *p != '-' || *++p == '\0') + return '\0'; + argptr++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + return '\0'; + } + c = *p++; + for (q = optstring ; *q != c ; ) { + if (*q == '\0') + error("Illegal option -%c", c); + if (*++q == ':') + q++; + } + if (*++q == ':') { + if (*p == '\0' && (p = *argptr++) == NULL) + error("No arg for -%c option", c); + optarg = p; + p = NULL; + } + optptr = p; + return c; +} diff --git a/commands/ash/options.h b/commands/ash/options.h new file mode 100755 index 000000000..7d86de33d --- /dev/null +++ b/commands/ash/options.h @@ -0,0 +1,90 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)options.h 5.1 (Berkeley) 3/7/91 + */ + +struct shparam { + int nparam; /* number of positional parameters (without $0) */ + char malloc; /* true if parameter list dynamicly allocated */ + char **p; /* parameter list */ + char **optnext; /* next parameter to be processed by getopts */ + char *optptr; /* used by getopts */ +}; + + + +#define eflag optval[0] +#define fflag optval[1] +#define Iflag optval[2] +#define iflag optval[3] +#define jflag optval[4] +#define nflag optval[5] +#define sflag optval[6] +#define xflag optval[7] +#define zflag optval[8] +#define vflag optval[9] + +#define NOPTS 10 + +#ifdef DEFINE_OPTIONS +const char optchar[NOPTS+1] = "efIijnsxzv"; /* shell flags */ +char optval[NOPTS+1]; /* values of option flags */ +#else +extern const char optchar[NOPTS+1]; +extern char optval[NOPTS+1]; +#endif + + +extern char *minusc; /* argument to -c option */ +extern char *arg0; /* $0 */ +extern struct shparam shellparam; /* $@ */ +extern char **argptr; /* argument list for builtin commands */ +extern char *optarg; /* set by nextopt */ +extern char *optptr; /* used by nextopt */ +extern int editable; /* isatty(0) && isatty(1) */ + + +#ifdef __STDC__ +void procargs(int, char **); +void setparam(char **); +void freeparam(struct shparam *); +int nextopt(char *); +#else +void procargs(); +void setparam(); +void freeparam(); +int nextopt(); +#endif diff --git a/commands/ash/output.c b/commands/ash/output.c new file mode 100755 index 000000000..03e48e474 --- /dev/null +++ b/commands/ash/output.c @@ -0,0 +1,531 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)output.c 5.1 (Berkeley) 3/7/91"; +#endif /* not lint */ + +/* + * Shell output routines. We use our own output routines because: + * When a builtin command is interrupted we have to discard + * any pending output. + * When a builtin command appears in back quotes, we want to + * save the output of the command in a region obtained + * via malloc, rather than doing a fork and reading the + * output of the command via a pipe. + * Our output routines may be smaller than the stdio routines. + */ + +#include /* defines BUFSIZ */ +#include "shell.h" +#include "syntax.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#ifdef __STDC__ +#include "stdarg.h" +#else +#include +#endif +#include + + +#define OUTBUFSIZ BUFSIZ +#define BLOCK_OUT -2 /* output to a fixed block of memory */ +#define MEM_OUT -3 /* output to dynamically allocated memory */ +#define OUTPUT_ERR 01 /* error occurred on output */ + + +struct output output = {NULL, 0, NULL, OUTBUFSIZ, 1, 0}; +struct output errout = {NULL, 0, NULL, 100, 2, 0}; +struct output memout = {NULL, 0, NULL, 0, MEM_OUT, 0}; +struct output *out1 = &output; +struct output *out2 = &errout; + + + +#ifdef mkinit + +INCLUDE "output.h" +INCLUDE "memalloc.h" + +RESET { + out1 = &output; + out2 = &errout; + if (memout.buf != NULL) { + ckfree(memout.buf); + memout.buf = NULL; + } +} + +#endif + + +#ifdef notdef /* no longer used */ +/* + * Set up an output file to write to memory rather than a file. + */ + +void +open_mem(block, length, file) + char *block; + int length; + struct output *file; + { + file->nextc = block; + file->nleft = --length; + file->fd = BLOCK_OUT; + file->flags = 0; +} +#endif + + +void +out1str(p) + char *p; + { + outstr(p, out1); +} + + +void +out2str(p) + char *p; + { + outstr(p, out2); +} + + +void +outstr(p, file) + register char *p; + register struct output *file; + { + while (*p) + outc(*p++, file); +} + + +char out_junk[16]; + + +void +emptyoutbuf(dest) + struct output *dest; + { + int offset; + + if (dest->fd == BLOCK_OUT) { + dest->nextc = out_junk; + dest->nleft = sizeof out_junk; + dest->flags |= OUTPUT_ERR; + } else if (dest->buf == NULL) { + INTOFF; + dest->buf = ckmalloc(dest->bufsize); + dest->nextc = dest->buf; + dest->nleft = dest->bufsize; + INTON; + } else if (dest->fd == MEM_OUT) { + offset = dest->bufsize; + INTOFF; + dest->bufsize <<= 1; + dest->buf = ckrealloc(dest->buf, dest->bufsize); + dest->nleft = dest->bufsize - offset; + dest->nextc = dest->buf + offset; + INTON; + } else { + flushout(dest); + } + dest->nleft--; +} + + +void +flushall() { + flushout(&output); + flushout(&errout); +} + + +void +flushout(dest) + struct output *dest; + { + + if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) + return; + if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0) + dest->flags |= OUTPUT_ERR; + dest->nextc = dest->buf; + dest->nleft = dest->bufsize; +} + + +void +freestdout() { + INTOFF; + if (output.buf) { + ckfree(output.buf); + output.buf = NULL; + output.nleft = 0; + } + INTON; +} + + +#ifdef __STDC__ +void +outfmt(struct output *file, char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + doformat(file, fmt, ap); + va_end(ap); +} + + +void +out1fmt(char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + doformat(out1, fmt, ap); + va_end(ap); +} + + +void +fmtstr(char *outbuf, int length, char *fmt, ...) { + va_list ap; + struct output strout; + + va_start(ap, fmt); + strout.nextc = outbuf; + strout.nleft = length; + strout.fd = BLOCK_OUT; + strout.flags = 0; + doformat(&strout, fmt, ap); + outc('\0', &strout); + if (strout.flags & OUTPUT_ERR) + outbuf[length - 1] = '\0'; + va_end(ap); +} + +#else /* not __STDC__ */ + +void +outfmt(va_alist) + va_dcl + { + va_list ap; + struct output *file; + char *fmt; + + va_start(ap); + file = va_arg(ap, struct output *); + fmt = va_arg(ap, char *); + doformat(file, fmt, ap); + va_end(ap); +} + + +void +out1fmt(va_alist) + va_dcl + { + va_list ap; + char *fmt; + + va_start(ap); + fmt = va_arg(ap, char *); + doformat(out1, fmt, ap); + va_end(ap); +} + + +void +fmtstr(va_alist) + va_dcl + { + va_list ap; + struct output strout; + char *outbuf; + int length; + char *fmt; + + va_start(ap); + outbuf = va_arg(ap, char *); + length = va_arg(ap, int); + fmt = va_arg(ap, char *); + strout.nextc = outbuf; + strout.nleft = length; + strout.fd = BLOCK_OUT; + strout.flags = 0; + doformat(&strout, fmt, ap); + outc('\0', &strout); + if (strout.flags & OUTPUT_ERR) + outbuf[length - 1] = '\0'; +} +#endif /* __STDC__ */ + + +/* + * Formatted output. This routine handles a subset of the printf formats: + * - Formats supported: d, u, o, X, s, and c. + * - The x format is also accepted but is treated like X. + * - The l modifier is accepted. + * - The - and # flags are accepted; # only works with the o format. + * - Width and precision may be specified with any format except c. + * - An * may be given for the width or precision. + * - The obsolete practice of preceding the width with a zero to get + * zero padding is not supported; use the precision field. + * - A % may be printed by writing %% in the format string. + */ + +#define TEMPSIZE 24 + +#ifdef __STDC__ +static const char digit[16] = "0123456789ABCDEF"; +#else +static const char digit[17] = "0123456789ABCDEF"; +#endif + + +void +doformat(dest, f, ap) + register struct output *dest; + register char *f; /* format string */ + va_list ap; + { + register char c; + char temp[TEMPSIZE]; + int flushleft; + int sharp; + int width; + int prec; + int islong; + char *p; + int sign; + long l; + unsigned long num; + unsigned base; + int len; + int size; + int pad; + + while ((c = *f++) != '\0') { + if (c != '%') { + outc(c, dest); + continue; + } + flushleft = 0; + sharp = 0; + width = 0; + prec = -1; + islong = 0; + for (;;) { + if (*f == '-') + flushleft++; + else if (*f == '#') + sharp++; + else + break; + f++; + } + if (*f == '*') { + width = va_arg(ap, int); + f++; + } else { + while (is_digit(*f)) { + width = 10 * width + digit_val(*f++); + } + } + if (*f == '.') { + if (*++f == '*') { + prec = va_arg(ap, int); + f++; + } else { + prec = 0; + while (is_digit(*f)) { + prec = 10 * prec + digit_val(*f++); + } + } + } + if (*f == 'l') { + islong++; + f++; + } + switch (*f) { + case 'd': + if (islong) + l = va_arg(ap, long); + else + l = va_arg(ap, int); + sign = 0; + num = l; + if (l < 0) { + num = -l; + sign = 1; + } + base = 10; + goto number; + case 'u': + base = 10; + goto uns_number; + case 'o': + base = 8; + goto uns_number; + case 'x': + /* we don't implement 'x'; treat like 'X' */ + case 'X': + base = 16; +uns_number: /* an unsigned number */ + sign = 0; + if (islong) + num = va_arg(ap, unsigned long); + else + num = va_arg(ap, unsigned int); +number: /* process a number */ + p = temp + TEMPSIZE - 1; + *p = '\0'; + while (num) { + *--p = digit[num % base]; + num /= base; + } + len = (temp + TEMPSIZE - 1) - p; + if (prec < 0) + prec = 1; + if (sharp && *f == 'o' && prec <= len) + prec = len + 1; + pad = 0; + if (width) { + size = len; + if (size < prec) + size = prec; + size += sign; + pad = width - size; + if (flushleft == 0) { + while (--pad >= 0) + outc(' ', dest); + } + } + if (sign) + outc('-', dest); + prec -= len; + while (--prec >= 0) + outc('0', dest); + while (*p) + outc(*p++, dest); + while (--pad >= 0) + outc(' ', dest); + break; + case 's': + p = va_arg(ap, char *); + pad = 0; + if (width) { + len = strlen(p); + if (prec >= 0 && len > prec) + len = prec; + pad = width - len; + if (flushleft == 0) { + while (--pad >= 0) + outc(' ', dest); + } + } + prec++; + while (--prec != 0 && *p) + outc(*p++, dest); + while (--pad >= 0) + outc(' ', dest); + break; + case 'c': + c = va_arg(ap, int); + outc(c, dest); + break; + default: + outc(*f, dest); + break; + } + f++; + } +} + + + +/* + * Version of write which resumes after a signal is caught. + */ + +int +xwrite(fd, buf, nbytes) + int fd; + char *buf; + int nbytes; + { + int ntry; + int i; + int n; + + n = nbytes; + ntry = 0; + for (;;) { + i = write(fd, buf, n); + if (i > 0) { + if ((n -= i) <= 0) + return nbytes; + buf += i; + ntry = 0; + } else if (i == 0) { + if (++ntry > 10) + return nbytes - n; + } else if (errno != EINTR) { + return -1; + } + } +} + + +/* + * Version of ioctl that retries after a signal is caught. + */ + +int +xioctl(fd, request, arg) { + int i; + + while ((i = ioctl(fd, request, arg)) == -1 && errno == EINTR); + return i; +} diff --git a/commands/ash/output.h b/commands/ash/output.h new file mode 100755 index 000000000..b8b814c3e --- /dev/null +++ b/commands/ash/output.h @@ -0,0 +1,94 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)output.h 5.1 (Berkeley) 3/7/91 + */ + +#ifndef OUTPUT_INCL + +struct output { + char *nextc; + int nleft; + char *buf; + int bufsize; + short fd; + short flags; +}; + +extern struct output output; +extern struct output errout; +extern struct output memout; +extern struct output *out1; +extern struct output *out2; + + +#ifdef __STDC__ +void outstr(char *, struct output *); +void out1str(char *); +void out2str(char *); +void outfmt(struct output *, char *, ...); +void out1fmt(char *, ...); +void fmtstr(char *, int, char *, ...); +/* void doformat(struct output *, char *, va_list); */ +void doformat(); +void emptyoutbuf(struct output *); +void flushall(void); +void flushout(struct output *); +void freestdout(void); +int xwrite(int, char *, int); +int xioctl(int, int, int); +#else +void outstr(); +void out1str(); +void out2str(); +void outfmt(); +void out1fmt(); +void fmtstr(); +/* void doformat(); */ +void doformat(); +void emptyoutbuf(); +void flushall(); +void flushout(); +void freestdout(); +int xwrite(); +int xioctl(); +#endif + +#define outc(c, file) (--(file)->nleft < 0? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c))) +#define out1c(c) outc(c, out1); +#define out2c(c) outc(c, out2); + +#define OUTPUT_INCL +#endif diff --git a/commands/ash/parser.c b/commands/ash/parser.c new file mode 100755 index 000000000..318a8a45d --- /dev/null +++ b/commands/ash/parser.c @@ -0,0 +1,1329 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)parser.c 5.3 (Berkeley) 4/12/91"; +#endif /* not lint */ + +#include "shell.h" +#include "parser.h" +#include "nodes.h" +#include "expand.h" /* defines rmescapes() */ +#include "redir.h" /* defines copyfd() */ +#include "syntax.h" +#include "options.h" +#include "input.h" +#include "output.h" +#include "var.h" +#include "error.h" +#include "memalloc.h" +#include "mystring.h" + + +/* + * Shell command parser. + */ + +#define EOFMARKLEN 79 + +/* values returned by readtoken */ +#include "token.def" + + + +struct heredoc { + struct heredoc *next; /* next here document in list */ + union node *here; /* redirection node */ + char *eofmark; /* string indicating end of input */ + int striptabs; /* if set, strip leading tabs */ +}; + + + +struct heredoc *heredoclist; /* list of here documents to read */ +int parsebackquote; /* nonzero if we are inside backquotes */ +int doprompt; /* if set, prompt the user */ +int needprompt; /* true if interactive and at start of line */ +int lasttoken; /* last token read */ +MKINIT int tokpushback; /* last token pushed back */ +char *wordtext; /* text of last word returned by readtoken */ +int checkkwd; /* 1 == check for kwds, 2 == also eat newlines */ +struct nodelist *backquotelist; +union node *redirnode; +struct heredoc *heredoc; +int quoteflag; /* set if (part of) last token was quoted */ +int startlinno; /* line # where last token started */ + + +#define GDB_HACK 1 /* avoid local declarations which gdb can't handle */ +#ifdef GDB_HACK +static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0'}; +static const char types[] = "}-+?="; +#endif + + +STATIC union node *list __P((int)); +STATIC union node *andor __P((void)); +STATIC union node *pipeline __P((void)); +STATIC union node *command __P((void)); +STATIC union node *simplecmd __P((union node **, union node *)); +STATIC void parsefname __P((void)); +STATIC void parseheredoc __P((void)); +STATIC int readtoken __P((void)); +STATIC int readtoken1 __P((int, char const *, char *, int)); +STATIC void attyline __P((void)); +STATIC int noexpand __P((char *)); +STATIC void synexpect __P((int)); +STATIC void synerror __P((char *)); + +#if ATTY || READLINE +STATIC void putprompt __P((char *)); +#else /* not ATTY */ +#define putprompt(s) out2str(s) +#endif + + + + +/* + * Read and parse a command. Returns NEOF on end of file. (NULL is a + * valid parse tree indicating a blank line.) + */ + +union node * +parsecmd(interact) { + int t; + extern int exitstatus; + + doprompt = interact; + if (doprompt) + putprompt(exitstatus == 0 ? ps1val() : pseval()); + needprompt = 0; + if ((t = readtoken()) == TEOF) + return NEOF; + if (t == TNL) + return NULL; + tokpushback++; + return list(1); +} + + +STATIC union node * +list(nlflag) { + union node *n1, *n2, *n3, **pn; + int first; + + checkkwd = 2; + if (nlflag == 0 && tokendlist[peektoken()]) + return NULL; + n1 = andor(); + for (first = 1; ; first = 0) { + switch (readtoken()) { + case TBACKGND: + pn = &n1; + if (!first && n1->type == NSEMI) pn = &n1->nbinary.ch2; + if ((*pn)->type == NCMD || (*pn)->type == NPIPE) { + (*pn)->ncmd.backgnd = 1; + } else if ((*pn)->type == NREDIR) { + (*pn)->type = NBACKGND; + } else { + n3 = (union node *)stalloc(sizeof (struct nredir)); + n3->type = NBACKGND; + n3->nredir.n = *pn; + n3->nredir.redirect = NULL; + *pn = n3; + } + goto tsemi; + case TNL: + tokpushback++; + /* fall through */ +tsemi: case TSEMI: + if (readtoken() == TNL) { + parseheredoc(); + if (nlflag) + return n1; + } else { + tokpushback++; + } + checkkwd = 2; + if (tokendlist[peektoken()]) + return n1; + n2 = andor(); + n3 = (union node *)stalloc(sizeof (struct nbinary)); + n3->type = NSEMI; + n3->nbinary.ch1 = n1; + n3->nbinary.ch2 = n2; + n1 = n3; + break; + case TEOF: + if (heredoclist) + parseheredoc(); + else + pungetc(); /* push back EOF on input */ + return n1; + default: + if (nlflag) + synexpect(-1); + tokpushback++; + return n1; + } + } +} + + + +STATIC union node * +andor() { + union node *n1, *n2, *n3; + int t; + + n1 = pipeline(); + for (;;) { + if ((t = readtoken()) == TAND) { + t = NAND; + } else if (t == TOR) { + t = NOR; + } else { + tokpushback++; + return n1; + } + n2 = pipeline(); + n3 = (union node *)stalloc(sizeof (struct nbinary)); + n3->type = t; + n3->nbinary.ch1 = n1; + n3->nbinary.ch2 = n2; + n1 = n3; + } +} + + + +STATIC union node * +pipeline() { + union node *n1, *pipenode; + struct nodelist *lp, *prev; + + n1 = command(); + if (readtoken() == TPIPE) { + pipenode = (union node *)stalloc(sizeof (struct npipe)); + pipenode->type = NPIPE; + pipenode->npipe.backgnd = 0; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + pipenode->npipe.cmdlist = lp; + lp->n = n1; + do { + prev = lp; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + lp->n = command(); + prev->next = lp; + } while (readtoken() == TPIPE); + lp->next = NULL; + n1 = pipenode; + } + tokpushback++; + return n1; +} + + + +STATIC union node * +command() { + union node *n1, *n2; + union node *ap, **app; + union node *cp, **cpp; + union node *redir, **rpp; + int t; + + checkkwd = 2; + redir = 0; + rpp = &redir; + /* Check for redirection which may precede command */ + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + + switch (readtoken()) { + case TIF: + n1 = (union node *)stalloc(sizeof (struct nif)); + n1->type = NIF; + n1->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n1->nif.ifpart = list(0); + n2 = n1; + while (readtoken() == TELIF) { + n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); + n2 = n2->nif.elsepart; + n2->type = NIF; + n2->nif.test = list(0); + if (readtoken() != TTHEN) + synexpect(TTHEN); + n2->nif.ifpart = list(0); + } + if (lasttoken == TELSE) + n2->nif.elsepart = list(0); + else { + n2->nif.elsepart = NULL; + tokpushback++; + } + if (readtoken() != TFI) + synexpect(TFI); + checkkwd = 1; + break; + case TWHILE: + case TUNTIL: { + int got; + n1 = (union node *)stalloc(sizeof (struct nbinary)); + n1->type = (lasttoken == TWHILE)? NWHILE : NUNTIL; + n1->nbinary.ch1 = list(0); + if ((got=readtoken()) != TDO) { +TRACE(("expecting DO got %s %s\n", tokname[got], got == TWORD ? wordtext : "")); + synexpect(TDO); + } + n1->nbinary.ch2 = list(0); + if (readtoken() != TDONE) + synexpect(TDONE); + checkkwd = 1; + break; + } + case TFOR: + if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) + synerror("Bad for loop variable"); + n1 = (union node *)stalloc(sizeof (struct nfor)); + n1->type = NFOR; + n1->nfor.var = wordtext; + if (readtoken() == TWORD && ! quoteflag && equal(wordtext, "in")) { + app = ≈ + while (readtoken() == TWORD) { + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + *app = n2; + app = &n2->narg.next; + } + *app = NULL; + n1->nfor.args = ap; + /* A newline or semicolon is required here to end + the list. */ + if (lasttoken != TNL && lasttoken != TSEMI) + synexpect(-1); + } else { +#ifndef GDB_HACK + static const char argvars[5] = {CTLVAR, VSNORMAL|VSQUOTE, + '@', '=', '\0'}; +#endif + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = (char *)argvars; + n2->narg.backquote = NULL; + n2->narg.next = NULL; + n1->nfor.args = n2; + /* A newline or semicolon is optional here. Anything + else gets pushed back so we can read it again. */ + if (lasttoken != TNL && lasttoken != TSEMI) + tokpushback++; + } + checkkwd = 2; + if ((t = readtoken()) == TDO) + t = TDONE; + else if (t == TBEGIN) + t = TEND; + else + synexpect(-1); + n1->nfor.body = list(0); + if (readtoken() != t) + synexpect(t); + checkkwd = 1; + break; + case TCASE: + n1 = (union node *)stalloc(sizeof (struct ncase)); + n1->type = NCASE; + if (readtoken() != TWORD) + synexpect(TWORD); + n1->ncase.expr = n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = wordtext; + n2->narg.backquote = backquotelist; + n2->narg.next = NULL; + while (readtoken() == TNL); + if (lasttoken != TWORD || ! equal(wordtext, "in")) + synerror("expecting \"in\""); + cpp = &n1->ncase.cases; + while (checkkwd = 2, readtoken() == TWORD) { + *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); + cp->type = NCLIST; + app = &cp->nclist.pattern; + for (;;) { + *app = ap = (union node *)stalloc(sizeof (struct narg)); + ap->type = NARG; + ap->narg.text = wordtext; + ap->narg.backquote = backquotelist; + if (readtoken() != TPIPE) + break; + app = &ap->narg.next; + if (readtoken() != TWORD) + synexpect(TWORD); + } + ap->narg.next = NULL; + if (lasttoken != TRP) + synexpect(TRP); + cp->nclist.body = list(0); + if ((t = readtoken()) == TESAC) + tokpushback++; + else if (t != TENDCASE) + synexpect(TENDCASE); + cpp = &cp->nclist.next; + } + *cpp = NULL; + if (lasttoken != TESAC) + synexpect(TESAC); + checkkwd = 1; + break; + case TLP: + n1 = (union node *)stalloc(sizeof (struct nredir)); + n1->type = NSUBSHELL; + n1->nredir.n = list(0); + n1->nredir.redirect = NULL; + if (readtoken() != TRP) + synexpect(TRP); + checkkwd = 1; + break; + case TBEGIN: + n1 = list(0); + if (readtoken() != TEND) + synexpect(TEND); + checkkwd = 1; + break; + /* Handle an empty command like other simple commands. */ + case TNL: + case TSEMI: + case TEND: + case TRP: + case TEOF: + case TWORD: + tokpushback++; + return simplecmd(rpp, redir); + default: + synexpect(-1); + } + + /* Now check for redirection which may follow command */ + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + *rpp = NULL; + if (redir) { + if (n1->type != NSUBSHELL) { + n2 = (union node *)stalloc(sizeof (struct nredir)); + n2->type = NREDIR; + n2->nredir.n = n1; + n1 = n2; + } + n1->nredir.redirect = redir; + } + return n1; +} + + +STATIC union node * +simplecmd(rpp, redir) + union node **rpp, *redir; + { + union node *args, **app; + union node **orig_rpp = rpp; + union node *n; + + /* If we don't have any redirections already, then we must reset + rpp to be the address of the local redir variable. */ + if (redir == 0) + rpp = &redir; + + args = NULL; + app = &args; + /* We save the incoming value, because we need this for shell + functions. There can not be a redirect or an argument between + the function name and the open parenthesis. */ + orig_rpp = rpp; + for (;;) { + if (readtoken() == TWORD) { + n = (union node *)stalloc(sizeof (struct narg)); + n->type = NARG; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + *app = n; + app = &n->narg.next; + } else if (lasttoken == TREDIR) { + *rpp = n = redirnode; + rpp = &n->nfile.next; + parsefname(); /* read name of redirection file */ + } else if (lasttoken == TLP && app == &args->narg.next + && rpp == orig_rpp) { + /* We have a function */ + if (readtoken() != TRP) + synexpect(TRP); +#ifdef notdef + if (! goodname(n->narg.text)) + synerror("Bad function name"); +#endif + n->type = NDEFUN; + n->narg.next = command(); + return n; + } else { + tokpushback++; + break; + } + } + *app = NULL; + *rpp = NULL; + n = (union node *)stalloc(sizeof (struct ncmd)); + n->type = NCMD; + n->ncmd.backgnd = 0; + n->ncmd.args = args; + n->ncmd.redirect = redir; + return n; +} + + +STATIC void +parsefname() { + union node *n = redirnode; + + if (readtoken() != TWORD) + synexpect(-1); + if (n->type == NHERE) { + struct heredoc *here = heredoc; + struct heredoc *p; + int i; + + if (quoteflag == 0) + n->type = NXHERE; + TRACE(("Here document %d\n", n->type)); + if (here->striptabs) { + while (*wordtext == '\t') + wordtext++; + } + if (! noexpand(wordtext) || (i = strlen(wordtext)) == 0 || i > EOFMARKLEN) + synerror("Illegal eof marker for << redirection"); + rmescapes(wordtext); + here->eofmark = wordtext; + here->next = NULL; + if (heredoclist == NULL) + heredoclist = here; + else { + for (p = heredoclist ; p->next ; p = p->next); + p->next = here; + } + } else if (n->type == NTOFD || n->type == NFROMFD) { + if (is_digit(wordtext[0])) + n->ndup.dupfd = digit_val(wordtext[0]); + else if (wordtext[0] == '-') + n->ndup.dupfd = -1; + else + goto bad; + if (wordtext[1] != '\0') { +bad: + synerror("Bad fd number"); + } + } else { + n->nfile.fname = (union node *)stalloc(sizeof (struct narg)); + n = n->nfile.fname; + n->type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + } +} + + +/* + * Input any here documents. + */ + +STATIC void +parseheredoc() { + struct heredoc *here; + union node *n; + + while (heredoclist) { + here = heredoclist; + heredoclist = here->next; + if (needprompt) { + putprompt(ps2val()); + needprompt = 0; + } + readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, + here->eofmark, here->striptabs); + n = (union node *)stalloc(sizeof (struct narg)); + n->narg.type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + here->here->nhere.doc = n; + } +} + +STATIC int +peektoken() { + int t; + + t = readtoken(); + tokpushback++; + return (t); +} + +STATIC int xxreadtoken(); + +STATIC int +readtoken() { + int t; +#if DEBUG + int alreadyseen = tokpushback; +#endif + + t = xxreadtoken(); + + if (checkkwd) { + /* + * eat newlines + */ + if (checkkwd == 2) { + checkkwd = 0; + while (t == TNL) { + parseheredoc(); + t = xxreadtoken(); + } + } else + checkkwd = 0; + /* + * check for keywords + */ + if (t == TWORD && !quoteflag) { + register char *const *pp; + + for (pp = parsekwd; *pp; pp++) { + if (**pp == *wordtext && equal(*pp, wordtext)) { + lasttoken = t = pp - parsekwd + KWDOFFSET; + TRACE(("keyword %s recognized\n", tokname[t])); + break; + } + } + } + } +#if DEBUG + if (!alreadyseen) + TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); + else + TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); +#endif + return (t); +} + + +/* + * Read the next input token. + * If the token is a word, we set backquotelist to the list of cmds in + * backquotes. We set quoteflag to true if any part of the word was + * quoted. + * If the token is TREDIR, then we set redirnode to a structure containing + * the redirection. + * In all cases, the variable startlinno is set to the number of the line + * on which the token starts. + * + * [Change comment: here documents and internal procedures] + * [Readtoken shouldn't have any arguments. Perhaps we should make the + * word parsing code into a separate routine. In this case, readtoken + * doesn't need to have any internal procedures, but parseword does. + * We could also make parseoperator in essence the main routine, and + * have parseword (readtoken1?) handle both words and redirection.] + */ + +#define RETURN(token) return lasttoken = token + +STATIC int +xxreadtoken() { + register c; + + if (tokpushback) { + tokpushback = 0; + return lasttoken; + } + if (needprompt) { + putprompt(ps2val()); + needprompt = 0; + } + startlinno = plinno; + for (;;) { /* until token or start of word found */ + c = pgetc_macro(); + if (c == ' ' || c == '\t') + continue; /* quick check for white space first */ + switch (c) { + case ' ': case '\t': + continue; + case '#': + while ((c = pgetc()) != '\n' && c != PEOF); + pungetc(); + continue; + case '\\': + if (pgetc() == '\n') { + startlinno = ++plinno; + if (doprompt) + putprompt(ps2val()); + continue; + } + pungetc(); + goto breakloop; + case '\n': + plinno++; + needprompt = doprompt; + RETURN(TNL); + case PEOF: + RETURN(TEOF); + case '&': + if (pgetc() == '&') + RETURN(TAND); + pungetc(); + RETURN(TBACKGND); + case '|': + if (pgetc() == '|') + RETURN(TOR); + pungetc(); + RETURN(TPIPE); + case ';': + if (pgetc() == ';') + RETURN(TENDCASE); + pungetc(); + RETURN(TSEMI); + case '(': + RETURN(TLP); + case ')': + RETURN(TRP); + default: + goto breakloop; + } + } +breakloop: + return readtoken1(c, BASESYNTAX, (char *)NULL, 0); +#undef RETURN +} + + + +/* + * If eofmark is NULL, read a word or a redirection symbol. If eofmark + * is not NULL, read a here document. In the latter case, eofmark is the + * word which marks the end of the document and striptabs is true if + * leading tabs should be stripped from the document. The argument firstc + * is the first character of the input token or document. + * + * Because C does not have internal subroutines, I have simulated them + * using goto's to implement the subroutine linkage. The following macros + * will run code that appears at the end of readtoken1. + */ + +#define CHECKEND() {goto checkend; checkend_return:;} +#define PARSEREDIR() {goto parseredir; parseredir_return:;} +#define PARSESUB() {goto parsesub; parsesub_return:;} +#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;} +#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;} + +STATIC int +readtoken1(firstc, syntax, eofmark, striptabs) + int firstc; + char const *syntax; + char *eofmark; + int striptabs; + { + register c = firstc; + register char *out; + int len; + char line[EOFMARKLEN + 1]; + struct nodelist *bqlist; + int quotef; + int dblquote; + int varnest; + int oldstyle; + + startlinno = plinno; + dblquote = 0; + if (syntax == DQSYNTAX) + dblquote = 1; + quotef = 0; + bqlist = NULL; + varnest = 0; + STARTSTACKSTR(out); + loop: { /* for each line, until end of word */ +#if ATTY + if (c == '\034' && doprompt + && attyset() && ! equal(termval(), "emacs")) { + attyline(); + if (syntax == BASESYNTAX) + return readtoken(); + c = pgetc(); + goto loop; + } +#endif + CHECKEND(); /* set c to PEOF if at end of here document */ + for (;;) { /* until end of line or end of word */ + CHECKSTRSPACE(3, out); /* permit 3 calls to USTPUTC */ + switch(syntax[c]) { + case CNL: /* '\n' */ + if (syntax == BASESYNTAX) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + plinno++; + if (doprompt) { + putprompt(ps2val()); + } + c = pgetc(); + goto loop; /* continue outer loop */ + case CWORD: + USTPUTC(c, out); + break; + case CCTL: + if (eofmark == NULL || dblquote) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + break; + case CBACK: /* backslash */ + c = pgetc(); + if (c == PEOF) { + USTPUTC('\\', out); + pungetc(); + } else if (c == '\n') { + if (doprompt) + putprompt(ps2val()); + } else { + if (dblquote && c != '\\' && c != '`' && c != '$' + && (c != '"' || eofmark != NULL)) + USTPUTC('\\', out); + if (SQSYNTAX[c] == CCTL) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + quotef++; + } + break; + case CSQUOTE: + syntax = SQSYNTAX; + break; + case CDQUOTE: + syntax = DQSYNTAX; + dblquote = 1; + break; + case CENDQUOTE: + if (eofmark) { + USTPUTC(c, out); + } else { + syntax = BASESYNTAX; + quotef++; + dblquote = 0; + } + break; + case CVAR: /* '$' */ + PARSESUB(); /* parse substitution */ + break; + case CENDVAR: /* '}' */ + if (varnest > 0) { + varnest--; + USTPUTC(CTLENDVAR, out); + } else { + USTPUTC(c, out); + } + break; + case CBQUOTE: /* '`' */ + PARSEBACKQOLD(); + break; + case CEOF: + goto endword; /* exit outer loop */ + default: + if (varnest == 0) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + } + c = pgetc_macro(); + } + } +endword: + if (syntax != BASESYNTAX && ! parsebackquote && eofmark == NULL) + synerror("Unterminated quoted string"); + if (varnest != 0) { + startlinno = plinno; + synerror("Missing '}'"); + } + USTPUTC('\0', out); + len = out - stackblock(); + out = stackblock(); + if (eofmark == NULL) { + if ((c == '>' || c == '<') + && quotef == 0 + && len <= 2 + && (*out == '\0' || is_digit(*out))) { + PARSEREDIR(); + return lasttoken = TREDIR; + } else { + pungetc(); + } + } + quoteflag = quotef; + backquotelist = bqlist; + grabstackblock(len); + wordtext = out; + return lasttoken = TWORD; +/* end of readtoken routine */ + + + +/* + * Check to see whether we are at the end of the here document. When this + * is called, c is set to the first character of the next input line. If + * we are at the end of the here document, this routine sets the c to PEOF. + */ + +checkend: { + if (eofmark) { + if (striptabs) { + while (c == '\t') + c = pgetc(); + } + if (c == *eofmark) { + if (pfgets(line, sizeof line) != NULL) { + register char *p, *q; + + p = line; + for (q = eofmark + 1 ; *q && *p == *q ; p++, q++); + if (*p == '\n' && *q == '\0') { + c = PEOF; + plinno++; + needprompt = doprompt; + } else { + ppushback(line, strlen(line)); + } + } + } + } + goto checkend_return; +} + + +/* + * Parse a redirection operator. The variable "out" points to a string + * specifying the fd to be redirected. The variable "c" contains the + * first character of the redirection operator. + */ + +parseredir: { + char fd = *out; + union node *np; + + np = (union node *)stalloc(sizeof (struct nfile)); + if (c == '>') { + np->nfile.fd = 1; + c = pgetc(); + if (c == '>') + np->type = NAPPEND; + else if (c == '&') + np->type = NTOFD; + else { + np->type = NTO; + pungetc(); + } + } else { /* c == '<' */ + np->nfile.fd = 0; + c = pgetc(); + if (c == '<') { + if (sizeof (struct nfile) != sizeof (struct nhere)) { + np = (union node *)stalloc(sizeof (struct nhere)); + np->nfile.fd = 0; + } + np->type = NHERE; + heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); + heredoc->here = np; + if ((c = pgetc()) == '-') { + heredoc->striptabs = 1; + } else { + heredoc->striptabs = 0; + pungetc(); + } + } else if (c == '&') + np->type = NFROMFD; + else { + np->type = NFROM; + pungetc(); + } + } + if (fd != '\0') + np->nfile.fd = digit_val(fd); + redirnode = np; + goto parseredir_return; +} + + +/* + * Parse a substitution. At this point, we have read the dollar sign + * and nothing else. + */ + +parsesub: { + int subtype; + int typeloc; + int flags; + char *p; +#ifndef GDB_HACK + static const char types[] = "}-+?="; +#endif + + c = pgetc(); + if (c != '(' && c != '{' && !is_name(c) && !is_special(c)) { + USTPUTC('$', out); + pungetc(); + } else if (c == '(') { /* $(command) */ + PARSEBACKQNEW(); + } else { + USTPUTC(CTLVAR, out); + typeloc = out - stackblock(); + USTPUTC(VSNORMAL, out); + subtype = VSNORMAL; + if (c == '{') { + c = pgetc(); + subtype = 0; + } + if (is_name(c)) { + do { + STPUTC(c, out); + c = pgetc(); + } while (is_in_name(c)); + } else { + if (! is_special(c)) +badsub: synerror("Bad substitution"); + USTPUTC(c, out); + c = pgetc(); + } + STPUTC('=', out); + flags = 0; + if (subtype == 0) { + if (c == ':') { + flags = VSNUL; + c = pgetc(); + } + p = strchr(types, c); + if (p == NULL) + goto badsub; + subtype = p - types + VSNORMAL; + } else { + pungetc(); + } + if (dblquote) + flags |= VSQUOTE; + *(stackblock() + typeloc) = subtype | flags; + if (subtype != VSNORMAL) + varnest++; + } + goto parsesub_return; +} + + +/* + * Called to parse command substitutions. Newstyle is set if the command + * is enclosed inside $(...); nlpp is a pointer to the head of the linked + * list of commands (passed by reference), and savelen is the number of + * characters on the top of the stack which must be preserved. + */ + +parsebackq: { + struct nodelist **nlpp; + int savepbq; + union node *n; + char *volatile str; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + int savelen; + int savedoprompt; + + savepbq = parsebackquote; + if (setjmp(jmploc.loc)) { + if (str) + ckfree(str); + parsebackquote = 0; + handler = savehandler; + longjmp(handler->loc, 1); + } + INTOFF; + str = NULL; + savelen = out - stackblock(); + if (savelen > 0) { + str = ckmalloc(savelen); + bcopy(stackblock(), str, savelen); + } + savehandler = handler; + handler = &jmploc; + INTON; + if (oldstyle) { + /* We must read until the closing backquote, giving special + treatment to some slashes, and then push the string and + reread it as input, interpreting it normally. */ + register char *out; + register c; + int savelen; + char *str; + + STARTSTACKSTR(out); + while ((c = pgetc ()) != '`') { + if (c == '\\') { + c = pgetc (); + if (c != '\\' && c != '`' && c != '$' + && (!dblquote || c != '"')) + STPUTC('\\', out); + } + if (c == '\n') { + plinno++; + if (doprompt) + putprompt(ps2val()); + } + STPUTC(c, out); + } + STPUTC('\0', out); + savelen = out - stackblock(); + if (savelen > 0) { + str = ckmalloc(savelen); + bcopy(stackblock(), str, savelen); + } + setinputstring(str, 1); + savedoprompt = doprompt; + doprompt = 0; /* no prompts while rereading string */ + } + nlpp = &bqlist; + while (*nlpp) + nlpp = &(*nlpp)->next; + *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + (*nlpp)->next = NULL; + parsebackquote = oldstyle; + n = list(0); + if (!oldstyle && (readtoken() != TRP)) + synexpect(TRP); + (*nlpp)->n = n; + /* Start reading from old file again. */ + if (oldstyle) { + popfile(); + doprompt = savedoprompt; + } + while (stackblocksize() <= savelen) + growstackblock(); + STARTSTACKSTR(out); + if (str) { + bcopy(str, out, savelen); + STADJUST(savelen, out); + INTOFF; + ckfree(str); + str = NULL; + INTON; + } + parsebackquote = savepbq; + handler = savehandler; + USTPUTC(CTLBACKQ + dblquote, out); + if (oldstyle) + goto parsebackq_oldreturn; + else + goto parsebackq_newreturn; +} + +} /* end of readtoken */ + + + +#ifdef mkinit +RESET { + tokpushback = 0; +} +#endif + + +#if READLINE +/* + * Remember a prompt for use with readline if input and output is a terminal. + */ + +STATIC void +putprompt(s) + char *s; + { + if (editable) { + r_use_prompt = s; + } else { + out2str(s); + } +} +#endif + +#if ATTY +/* + * Called to process a command generated by atty. We execute the line, + * and catch any errors that occur so they don't propagate outside of + * this routine. + */ + +STATIC void +attyline() { + char line[256]; + struct stackmark smark; + struct jmploc jmploc; + struct jmploc *volatile savehandler; + + if (pfgets(line, sizeof line) == NULL) + return; /* "can't happen" */ + if (setjmp(jmploc.loc)) { + if (exception == EXERROR) + out2str("\033]D\n"); + handler = savehandler; + longjmp(handler->loc, 1); + } + savehandler = handler; + handler = &jmploc; + setstackmark(&smark); + evalstring(line); + popstackmark(&smark); + handler = savehandler; + doprompt = 1; +} + + +/* + * Output a prompt for atty. We output the prompt as part of the + * appropriate escape sequence. + */ + +STATIC void +putprompt(s) + char *s; + { + register char *p; + + if (attyset() && ! equal(termval(), "emacs")) { + if (strchr(s, '\7')) + out2c('\7'); + out2str("\033]P1;"); + for (p = s ; *p ; p++) { + if ((unsigned)(*p - ' ') <= '~' - ' ') + out2c(*p); + } + out2c('\n'); + } else { + out2str(s); + } +} +#endif + + + +/* + * Returns true if the text contains nothing to expand (no dollar signs + * or backquotes). + */ + +STATIC int +noexpand(text) + char *text; + { + register char *p; + register char c; + + p = text; + while ((c = *p++) != '\0') { + if (c == CTLESC) + p++; + else if (BASESYNTAX[c] == CCTL) + return 0; + } + return 1; +} + + +/* + * Return true if the argument is a legal variable name (a letter or + * underscore followed by zero or more letters, underscores, and digits). + */ + +int +goodname(name) + char *name; + { + register char *p; + + p = name; + if (! is_name(*p)) + return 0; + while (*++p) { + if (! is_in_name(*p)) + return 0; + } + return 1; +} + + +/* + * Called when an unexpected token is read during the parse. The argument + * is the token that is expected, or -1 if more than one type of token can + * occur at this point. + */ + +STATIC void +synexpect(token) { + char msg[64]; + + if (token >= 0) { + fmtstr(msg, 64, "%s unexpected (expecting %s)", + tokname[lasttoken], tokname[token]); + } else { + fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]); + } + synerror(msg); +} + + +STATIC void +synerror(msg) + char *msg; + { + if (commandname) + outfmt(&errout, "%s: %d: ", commandname, startlinno); + outfmt(&errout, "Syntax error: %s\n", msg); + error((char *)NULL); +} diff --git a/commands/ash/parser.h b/commands/ash/parser.h new file mode 100755 index 000000000..edb130a51 --- /dev/null +++ b/commands/ash/parser.h @@ -0,0 +1,74 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)parser.h 5.1 (Berkeley) 3/7/91 + */ + +/* control characters in argument strings */ +#define CTLESC '\201' +#define CTLVAR '\202' +#define CTLENDVAR '\203' +#define CTLBACKQ '\204' +#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ + +/* variable substitution byte (follows CTLVAR) */ +#define VSTYPE 07 /* type of variable substitution */ +#define VSNUL 040 /* colon--treat the empty string as unset */ +#define VSQUOTE 0100 /* inside double quotes--suppress splitting */ + +/* values of VSTYPE field */ +#define VSNORMAL 1 /* normal variable: $var or ${var} */ +#define VSMINUS 2 /* ${var-text} */ +#define VSPLUS 3 /* ${var+text} */ +#define VSQUESTION 4 /* ${var?message} */ +#define VSASSIGN 5 /* ${var=text} */ + + +/* + * NEOF is returned by parsecmd when it encounters an end of file. It + * must be distinct from NULL, so we use the address of a variable that + * happens to be handy. + */ +extern int tokpushback; +#define NEOF ((union node *)&tokpushback) + + +#ifdef __STDC__ +union node *parsecmd(int); +int goodname(char *); +#else +union node *parsecmd(); +int goodname(); +#endif diff --git a/commands/ash/redir.c b/commands/ash/redir.c new file mode 100755 index 000000000..5cded98d6 --- /dev/null +++ b/commands/ash/redir.c @@ -0,0 +1,370 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)redir.c 5.1 (Berkeley) 3/7/91"; +#endif /* not lint */ + +/* + * Code for dealing with input/output redirection. + */ + +#include "shell.h" +#include "nodes.h" +#include "jobs.h" +#include "expand.h" +#include "redir.h" +#include "eval.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include +#include +#include +#include +#include + + +#define EMPTY -2 /* marks an unused slot in redirtab */ +#define PIPESIZE 4096 /* amount of buffering in a pipe */ + + +MKINIT +struct redirtab { + struct redirtab *next; + short renamed[10]; +}; + + +MKINIT struct redirtab *redirlist; + +/* We keep track of whether or not fd0 has been redirected. This is for + background commands, where we want to redirect fd0 to /dev/null only + if it hasn't already been redirected. */ +int fd0_redirected = 0; + +#ifdef __STDC__ +STATIC void openredirect(union node *, char *); +STATIC int openhere(union node *); +#else +STATIC void openredirect(); +STATIC int openhere(); +#endif + + + +/* + * Process a list of redirection commands. If the REDIR_PUSH flag is set, + * old file descriptors are stashed away so that the redirection can be + * undone by calling popredir. If the REDIR_BACKQ flag is set, then the + * standard output, and the standard error if it becomes a duplicate of + * stdout, is saved in memory. + */ + +void +redirect(redir, flags) + union node *redir; + int flags; + { + union node *n; + struct redirtab *sv; + int i; + int fd; + char memory[10]; /* file descriptors to write to memory */ + + for (i = 10 ; --i >= 0 ; ) + memory[i] = 0; + memory[1] = flags & REDIR_BACKQ; + if (flags & REDIR_PUSH) { + sv = ckmalloc(sizeof (struct redirtab)); + for (i = 0 ; i < 10 ; i++) + sv->renamed[i] = EMPTY; + sv->next = redirlist; + redirlist = sv; + } + for (n = redir ; n ; n = n->nfile.next) { + fd = n->nfile.fd; + if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) { + INTOFF; + if ((i = copyfd(fd, 10)) != EMPTY) { + sv->renamed[fd] = i; + close(fd); + } + INTON; + if (i == EMPTY) + error("Out of file descriptors"); + } else { + close(fd); + } + if (fd == 0) + fd0_redirected++; + openredirect(n, memory); + } + if (memory[1]) + out1 = &memout; + if (memory[2]) + out2 = &memout; +} + + +STATIC void +openredirect(redir, memory) + union node *redir; + char memory[10]; + { + int fd = redir->nfile.fd; + char *fname; + int f; + + /* Assume redirection succeeds. */ + exitstatus = 0; + + /* + * We suppress interrupts so that we won't leave open file + * descriptors around. This may not be such a good idea because + * an open of a device or a fifo can block indefinitely. + */ + INTOFF; + memory[fd] = 0; + switch (redir->nfile.type) { + case NFROM: + fname = redir->nfile.expfname; + if ((f = open(fname, O_RDONLY)) < 0) + error("cannot open %s: %s", fname, errmsg(errno, E_OPEN)); +movefd: + if (f != fd) { + copyfd(f, fd); + close(f); + } + break; + case NTO: + fname = redir->nfile.expfname; +#ifdef O_CREAT + if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +#else + if ((f = creat(fname, 0666)) < 0) + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +#endif + goto movefd; + case NAPPEND: + fname = redir->nfile.expfname; +#ifdef O_APPEND + if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); +#else + if ((f = open(fname, O_WRONLY)) < 0 + && (f = creat(fname, 0666)) < 0) + error("cannot create %s: %s", fname, errmsg(errno, E_CREAT)); + lseek(f, 0L, 2); +#endif + goto movefd; + case NTOFD: + case NFROMFD: + if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ + if (memory[redir->ndup.dupfd]) + memory[fd] = 1; + else + copyfd(redir->ndup.dupfd, fd); + } + break; + case NHERE: + case NXHERE: + f = openhere(redir); + goto movefd; + default: + abort(); + } + INTON; +} + + +/* + * Handle here documents. Normally we fork off a process to write the + * data to a pipe. If the document is short, we can stuff the data in + * the pipe without forking. + */ + +STATIC int +openhere(redir) + union node *redir; + { + int pip[2]; + int len; + + if (pipe(pip) < 0) + error("Pipe call failed"); + if (redir->type == NHERE) { + len = strlen(redir->nhere.doc->narg.text); + if (len <= PIPESIZE) { + xwrite(pip[1], redir->nhere.doc->narg.text, len); + goto out; + } + } + if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { + close(pip[0]); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGHUP, SIG_IGN); +#ifdef SIGTSTP + signal(SIGTSTP, SIG_IGN); +#endif + signal(SIGPIPE, SIG_DFL); + if (redir->type == NHERE) + xwrite(pip[1], redir->nhere.doc->narg.text, len); + else + expandhere(redir->nhere.doc, pip[1]); + _exit(0); + } +out: + close(pip[1]); + return pip[0]; +} + + + +/* + * Undo the effects of the last redirection. + */ + +void +popredir() { + register struct redirtab *rp = redirlist; + int i; + + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] != EMPTY) { + if (i == 0) + fd0_redirected--; + close(i); + if (rp->renamed[i] >= 0) { + copyfd(rp->renamed[i], i); + close(rp->renamed[i]); + } + } + } + INTOFF; + redirlist = rp->next; + ckfree(rp); + INTON; +} + + + +/* + * Undo all redirections. Called on error or interrupt. + */ + +#ifdef mkinit + +INCLUDE "redir.h" + +RESET { + while (redirlist) + popredir(); +} + +SHELLPROC { + clearredir(); +} + +#endif + + +/* + * Discard all saved file descriptors. + */ + +void +clearredir() { + register struct redirtab *rp; + int i; + + for (rp = redirlist ; rp ; rp = rp->next) { + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] >= 0) { + close(rp->renamed[i]); + } + rp->renamed[i] = EMPTY; + } + } +} + + + +/* + * Copy a file descriptor, like the F_DUPFD option of fcntl. Returns -1 + * if the source file descriptor is closed, EMPTY if there are no unused + * file descriptors left. + */ + +int +copyfd(from, to) { +#ifdef F_DUPFD + int newfd; + + newfd = fcntl(from, F_DUPFD, to); + if (newfd < 0 && errno == EMFILE) + return EMPTY; + return newfd; +#else + char toclose[32]; + int i; + int newfd; + int e; + + for (i = 0 ; i < to ; i++) + toclose[i] = 0; + INTOFF; + while ((newfd = dup(from)) >= 0 && newfd < to) + toclose[newfd] = 1; + e = errno; + for (i = 0 ; i < to ; i++) { + if (toclose[i]) + close(i); + } + INTON; + if (newfd < 0 && e == EMFILE) + return EMPTY; + return newfd; +#endif +} + +/* Return true if fd 0 has already been redirected at least once. */ +int +fd0_redirected_p () { + return fd0_redirected != 0; +} diff --git a/commands/ash/redir.h b/commands/ash/redir.h new file mode 100755 index 000000000..85ba6d12a --- /dev/null +++ b/commands/ash/redir.h @@ -0,0 +1,56 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)redir.h 5.1 (Berkeley) 3/7/91 + */ + +/* flags passed to redirect */ +#define REDIR_PUSH 01 /* save previous values of file descriptors */ +#define REDIR_BACKQ 02 /* save the command output in memory */ + +#ifdef __STDC__ +union node; +void redirect(union node *, int); +void popredir(void); +void clearredir(void); +int copyfd(int, int); +int fd0_redirected_p(void); +#else +void redirect(); +void popredir(); +void clearredir(); +int copyfd(); +int fd0_redirected_p(); +#endif diff --git a/commands/ash/shell.h b/commands/ash/shell.h new file mode 100755 index 000000000..cdf316a6f --- /dev/null +++ b/commands/ash/shell.h @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)shell.h 5.4 (Berkeley) 4/12/91 + */ + +/* + * The follow should be set to reflect the type of system you have: + * JOBS -> 1 if you have Berkeley job control, 0 otherwise. + * SYMLINKS -> 1 if your system includes symbolic links, 0 otherwise. + * DIRENT -> 1 if your system has the SVR3 directory(3X) routines. + * UDIR -> 1 if you want the shell to simulate the /u directory. + * TILDE -> 1 if you want the shell to expand ~logname. + * USEGETPW -> 1 if getpwnam() must be used to look up a name. + * ATTY -> 1 to include code for atty(1). + * SHORTNAMES -> 1 if your linker cannot handle long names. + * READLINE -> 1 if line editing by readline() should be enabled. + * define BSD if you are running 4.2 BSD or later. + * define SYSV if you are running under System V. + * define DEBUG=1 to compile in debugging (set global "debug" to turn on) + * define DEBUG=2 to compile in and turn on debugging. + * + * When debugging is on, debugging info will be written to $HOME/trace and + * a quit signal will generate a core dump. + */ + + +#define JOBS 0 +#define SYMLINKS defined(S_ISLNK) +#define DIRENT 1 +#define UDIR 0 +#define TILDE 1 +#define USEGETPW 0 +#define ATTY 0 +#define READLINE 1 +#define HASHBANG 0 +/* #define BSD */ +#define POSIX 1 +#define DEBUG 0 + +#ifdef __STDC__ +typedef void *pointer; +#ifndef NULL +#define NULL (void *)0 +#endif +#else /* not __STDC__ */ +typedef char *pointer; +#ifndef NULL +#define NULL 0 +#endif +#endif /* not __STDC__ */ +#define STATIC /* empty */ +#define MKINIT /* empty */ + +#include + +extern char nullstr[1]; /* null string */ + + +#if DEBUG +#define TRACE(param) trace param +#else +#define TRACE(param) +#endif diff --git a/commands/ash/show.c b/commands/ash/show.c new file mode 100755 index 000000000..0860ab667 --- /dev/null +++ b/commands/ash/show.c @@ -0,0 +1,377 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)show.c 5.2 (Berkeley) 4/12/91"; +#endif /* not lint */ + +#include +#include +#include "shell.h" +#include "parser.h" +#include "nodes.h" +#include "mystring.h" + + +#if DEBUG +static shtree(), shcmd(), sharg(), indent(); + + +showtree(n) + union node *n; + { + trputs("showtree called\n"); + shtree(n, 1, NULL, stdout); +} + + +static +shtree(n, ind, pfx, fp) + union node *n; + char *pfx; + FILE *fp; + { + struct nodelist *lp; + char *s; + + indent(ind, pfx, fp); + switch(n->type) { + case NSEMI: + s = "; "; + goto binop; + case NAND: + s = " && "; + goto binop; + case NOR: + s = " || "; +binop: + shtree(n->nbinary.ch1, ind, NULL, fp); + /* if (ind < 0) */ + fputs(s, fp); + shtree(n->nbinary.ch2, ind, NULL, fp); + break; + case NCMD: + shcmd(n, fp); + if (ind >= 0) + putc('\n', fp); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + shcmd(lp->n, fp); + if (lp->next) + fputs(" | ", fp); + } + if (n->npipe.backgnd) + fputs(" &", fp); + if (ind >= 0) + putc('\n', fp); + break; + default: + fprintf(fp, "", n->type); + if (ind >= 0) + putc('\n', fp); + break; + } +} + + + +static +shcmd(cmd, fp) + union node *cmd; + FILE *fp; + { + union node *np; + int first; + char *s; + int dftfd; + + first = 1; + for (np = cmd->ncmd.args ; np ; np = np->narg.next) { + if (! first) + putchar(' '); + sharg(np, fp); + first = 0; + } + for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) { + if (! first) + putchar(' '); + switch (np->nfile.type) { + case NTO: s = ">"; dftfd = 1; break; + case NAPPEND: s = ">>"; dftfd = 1; break; + case NTOFD: s = ">&"; dftfd = 1; break; + case NFROM: s = "<"; dftfd = 0; break; + case NFROMFD: s = "<&"; dftfd = 0; break; + } + if (np->nfile.fd != dftfd) + fprintf(fp, "%d", np->nfile.fd); + fputs(s, fp); + if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { + fprintf(fp, "%d", np->ndup.dupfd); + } else { + sharg(np->nfile.fname, fp); + } + first = 0; + } +} + + + +static +sharg(arg, fp) + union node *arg; + FILE *fp; + { + char *p; + struct nodelist *bqlist; + int subtype; + + if (arg->type != NARG) { + printf("\n", arg->type); + fflush(stdout); + abort(); + } + bqlist = arg->narg.backquote; + for (p = arg->narg.text ; *p ; p++) { + switch (*p) { + case CTLESC: + putc(*++p, fp); + break; + case CTLVAR: + putc('$', fp); + putc('{', fp); + subtype = *++p; + while (*p != '=') + putc(*p++, fp); + if (subtype & VSNUL) + putc(':', fp); + switch (subtype & VSTYPE) { + case VSNORMAL: + putc('}', fp); + break; + case VSMINUS: + putc('-', fp); + break; + case VSPLUS: + putc('+', fp); + break; + case VSQUESTION: + putc('?', fp); + break; + case VSASSIGN: + putc('=', fp); + break; + default: + printf("", subtype); + } + break; + case CTLENDVAR: + putc('}', fp); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + putc('$', fp); + putc('(', fp); + shtree(bqlist->n, -1, NULL, fp); + putc(')', fp); + break; + default: + putc(*p, fp); + break; + } + } +} + + +static +indent(amount, pfx, fp) + char *pfx; + FILE *fp; + { + int i; + + for (i = 0 ; i < amount ; i++) { + if (pfx && i == amount - 1) + fputs(pfx, fp); + putc('\t', fp); + } +} +#endif + + + +/* + * Debugging stuff. + */ + + +FILE *tracefile; + +#if DEBUG == 2 +int debug = 1; +#else +int debug = 0; +#endif + + +trputc(c) { +#if DEBUG + if (tracefile == NULL) + return; + putc(c, tracefile); + if (c == '\n') + fflush(tracefile); +#endif +} + + +trace(fmt, a1, a2, a3, a4, a5, a6, a7, a8) + char *fmt; + { +#if DEBUG + int e = errno; + if (tracefile == NULL) + return; + fprintf(tracefile, fmt, a1, a2, a3, a4, a5, a6, a7, a8); + if (strchr(fmt, '\n')) + fflush(tracefile); + errno = e; +#endif +} + + +trputs(s) + char *s; + { +#if DEBUG + if (tracefile == NULL) + return; + fputs(s, tracefile); + if (strchr(s, '\n')) + fflush(tracefile); +#endif +} + + +trstring(s) + char *s; + { + register char *p; + char c; + +#if DEBUG + if (tracefile == NULL) + return; + putc('"', tracefile); + for (p = s ; *p ; p++) { + switch (*p) { + case '\n': c = 'n'; goto backslash; + case '\t': c = 't'; goto backslash; + case '\r': c = 'r'; goto backslash; + case '"': c = '"'; goto backslash; + case '\\': c = '\\'; goto backslash; + case CTLESC: c = 'e'; goto backslash; + case CTLVAR: c = 'v'; goto backslash; + case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; + case CTLBACKQ: c = 'q'; goto backslash; + case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; +backslash: putc('\\', tracefile); + putc(c, tracefile); + break; + default: + if (*p >= ' ' && *p <= '~') + putc(*p, tracefile); + else { + putc('\\', tracefile); + putc(*p >> 6 & 03, tracefile); + putc(*p >> 3 & 07, tracefile); + putc(*p & 07, tracefile); + } + break; + } + } + putc('"', tracefile); +#endif +} + + +trargs(ap) + char **ap; + { +#if DEBUG + if (tracefile == NULL) + return; + while (*ap) { + trstring(*ap++); + if (*ap) + putc(' ', tracefile); + else + putc('\n', tracefile); + } + fflush(tracefile); +#endif +} + + +opentrace() { + char s[100]; + char *p; + char *getenv(); + int flags; + +#if DEBUG + if (!debug) + return; + if ((p = getenv("HOME")) == NULL) { + if (getuid() == 0) + p = "/"; + else + p = "/tmp"; + } + scopy(p, s); + strcat(s, "/trace"); + if ((tracefile = fopen(s, "a")) == NULL) { + fprintf(stderr, "Can't open %s\n", s); + return; + } +#ifdef O_APPEND + if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) + fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); +#endif + fputs("\nTracing started.\n", tracefile); + fflush(tracefile); +#endif +} diff --git a/commands/ash/sys/cdefs.h b/commands/ash/sys/cdefs.h new file mode 100755 index 000000000..0c499191f --- /dev/null +++ b/commands/ash/sys/cdefs.h @@ -0,0 +1,15 @@ +/* Replacement for something BSD has in sys/cdefs.h. */ + +#ifndef _ASH_SYS_CDEFS +#define _ASH_SYS_CDEFS + +#if __STDC__ +#define __P(params) params +#else +#define __P(params) () +#endif + +/* Probably in sys/types.h. */ +typedef void (*sig_t) __P(( int )); + +#endif /* _ASH_SYS_CDEFS */ diff --git a/commands/ash/test/malloc.c b/commands/ash/test/malloc.c new file mode 100755 index 000000000..c54d71697 --- /dev/null +++ b/commands/ash/test/malloc.c @@ -0,0 +1,1298 @@ + +/**********************************************************/ +/* +/* This was file READ_ME +/* +/**********************************************************/ + +/* + PROGRAM + malloc(), free(), realloc() + AUTHOR + Dick Grune, Free University, Amsterdam + Modified by Ceriel Jacobs, Free University, Amsterdam, + to make it faster + VERSION + $Header$ + DESCRIPTION + This is an independent rewrite of the malloc/free package; it is + fast and efficient. Free blocks are kept in doubly linked lists, + list N holding blocks with sizes between 2**N and 2**(N+1)-1. + Consequently neither malloc nor free have to do any searching: + the cost of a call of malloc() (or free()) is constant, however + many blocks you have got. + + If you switch on the NON_STANDARD macro (see param.h) every block + costs 2 pointers overhead (otherwise it's 4). +*/ +/* + There is an organisational problem here: during devellopment + I want the package divided into modules, which implies external + names for the communication. The only external names I want in + the finished product are malloc, realloc and free. This requires + some hanky-panky. +*/ + + +/**********************************************************/ +/* +/* This was file size_type.h +/* +/**********************************************************/ + +#if _EM_WSIZE == _EM_PSIZE +typedef unsigned int size_type; +#elif _EM_LSIZE == _EM_PSIZE +typedef unsigned long size_type; +#else +#error funny pointer size +#endif +#include +#include + + +/**********************************************************/ +/* +/* This was file param.h +/* +/**********************************************************/ + +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ + +# undef NON_STANDARD /* If defined, the contents of a block + will NOT be left undisturbed after it + is freed, as opposed to what it says + in the manual (malloc(2)). + Setting this option reduces the memory + overhead considerably. I personally + consider the specified behaviour an + artefact of the original + implementation. + */ + +# define ASSERT /* If defined, some inexpensive tests + will be made to ensure the + correctness of some sensitive data. + It often turns an uncontrolled crash + into a controlled one. + */ + +# define CHECK /* If defined, extensive and expensive + tests will be done, inculding a + checksum on the mallinks (chunk + information blocks). The resulting + information will be printed on a file + called mal.out . + Additionally a function + maldump(n) int n; + will be defined, which will dump + pertinent info in pseudo-readable + form; it aborts afterwards if n != 0. + */ + +# undef EXTERN /* If defined, all static names will + become extern, which is a help in + using adb(1) or prof(1) + */ + +# define STORE /* If defined, separate free lists will + be kept of chunks with small sizes, + to speed things up a little. + */ + +# undef SYSTEM /* If defined, the system module is used. + Otherwise, "sbrk" is called directly. + */ + +#define ALIGNMENT 8 + /* alignment common to all types */ +#define LOG_MIN_SIZE 3 +#define LOG_MAX_SIZE 24 + + +/**********************************************************/ +/* +/* This was file impl.h +/* +/**********************************************************/ + +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* This file essentially describes how the mallink info block + is implemented. +*/ + +#define MIN_SIZE (1< +#include + +/* Malloc space is traversed by N doubly-linked lists of chunks, each + containing a couple of house-keeping data addressed as a + 'mallink' and a piece of useful space, called the block. + The N lists are accessed through their starting pointers in + free_list[]. Free_list[n] points to a list of chunks between + 2**(n+LOG_MIN_SIZE) and 2**(n+LOG_MIN_SIZE+1)-1, which means + that the smallest chunk is 2**LOG_MIN_SIZE (== MIN_SIZE). +*/ + +#ifdef SYSTEM +#include +#define SBRK sys_break +#else +#define SBRK _sbrk +#define ILL_BREAK (void *)(-1) /* funny failure value */ +#endif +extern void *SBRK(int incr); +#ifdef STORE +#define MAX_STORE 32 +private do_free(mallink *ml), sell_out(void); +privatedata mallink *store[MAX_STORE]; +#endif /* STORE */ + +void *privious_free= (void *)-1; +void * +malloc(register size_t n) +{check_mallinks("malloc entry");{ + register mallink *ml; + register int min_class; + void *tmp; + +{ static int reent= 0; if (!reent) { reent++; printf("malloc\n"); reent--; } } +privious_free= (void *)-1; + if (n == 0) { + return NULL; + } + if (n < MIN_SIZE) n = align(MIN_SIZE); else n = align(n); +#ifdef STORE + if (n <= MAX_STORE*MIN_SIZE) { + /* look in the store first */ + register mallink **stp = &store[(n >> LOG_MIN_SIZE) - 1]; + + if (ml = *stp) { + *stp = log_next_of(ml); + set_store(ml, 0); + check_mallinks("malloc fast exit"); + assert(! in_store(ml)); + tmp= block_of_mallink(ml); +{ static int reent= 0; if (!reent) { reent++; printf("= 0x%x\n", tmp);reent--; } } + return tmp; + } + } +#endif /* STORE */ + + check_work_empty("malloc, entry"); + + /* Acquire a chunk of at least size n if at all possible; + Try everything. + */ + { + /* Inline substitution of "smallest". + */ + register size_t n1 = n; + + assert(n1 < (1L << LOG_MAX_SIZE)); + min_class = 0; + + do { + n1 >>= 1; + min_class++; + } while (n1 >= MIN_SIZE); + } + + if (min_class >= MAX_FLIST) + return NULL; /* we don't deal in blocks that big */ + ml = first_present(min_class); + if (ml == MAL_NULL) { + /* Try and extend */ + register void *p; +#define GRABSIZE 4096 /* Power of 2 */ + register size_t req = + ((MIN_SIZE<= 0) p = SBRK((int)req); + } + if (p == ILL_BREAK) { + /* Now this is bad. The system will not give us + more memory. We can only liquidate our store + and hope it helps. + */ +#ifdef STORE + sell_out(); + ml = first_present(min_class); + if (ml == MAL_NULL) { +#endif /* STORE */ + /* In this emergency we try to locate a suitable + chunk in the free_list just below the safe + one; some of these chunks may fit the job. + */ + ml = search_free_list(min_class - 1, n); + if (!ml) /* really out of space */ + return NULL; + started_working_on(ml); + unlink_free_chunk(ml); + check_mallinks("suitable_chunk, forced"); +#ifdef STORE + } + else started_working_on(ml); +#endif /* STORE */ + } + else { + assert((size_type)p == align((size_type)p)); + ml = create_chunk(p, req); + } + check_mallinks("suitable_chunk, extended"); + } + else started_working_on(ml); + + /* we have a chunk */ + set_free(ml, 0); + calc_checksum(ml); + check_mallinks("suitable_chunk, removed"); + n += mallink_size(); + if (n + MIN_SIZE <= size_of(ml)) { + truncate(ml, n); + } + stopped_working_on(ml); + check_mallinks("malloc exit"); + check_work_empty("malloc exit"); +#ifdef STORE + assert(! in_store(ml)); +#endif + tmp= block_of_mallink(ml); +{ static int reent= 0; if (!reent) { reent++; printf("= 0x%x\n", tmp);reent--; } } + return tmp; +}} + +void +free(void *addr) +{check_mallinks("free entry");{ + register mallink *ml; + +printf("free 0x%x\n", addr); +if (privious_free == addr) { fflush(stdout); fflush(stderr); abort(); } +privious_free= addr; + if (addr == NULL) { + check_mallinks("free(0) very fast exit"); + return; + } + + ml = mallink_of_block(addr); +#ifdef STORE + + if (free_of(ml) || in_store(ml)) + return; /* user frees free block */ + if (size_of(ml) <= MAX_STORE*MIN_SIZE) { + /* return to store */ + mallink **stp = &store[(size_of(ml) >> LOG_MIN_SIZE) - 1]; + + set_log_next(ml, *stp); + *stp = ml; + set_store(ml, 1); + calc_checksum(ml); + check_mallinks("free fast exit"); + } + else { + do_free(ml); + check_mallinks("free exit"); + } +}} + +private +do_free(register mallink *ml) +{{ +#endif + +#ifndef STORE + if (free_of(ml)) return; +#endif /* STORE */ + started_working_on(ml); + set_free(ml, 1); + calc_checksum(ml); + if (! last_mallink(ml)) { + register mallink *next = phys_next_of(ml); + + if (free_of(next)) coalesce_forw(ml, next); + } + + if (! first_mallink(ml)) { + register mallink *prev = phys_prev_of(ml); + + if (free_of(prev)) { + coalesce_backw(ml, prev); + ml = prev; + } + } + link_free_chunk(ml); + stopped_working_on(ml); + check_work_empty("free"); + + /* Compile-time checks on param.h */ + switch (0) { + case MIN_SIZE < OFF_SET * sizeof(mallink): break; + case 1: break; + /* If this statement does not compile due to duplicate case + entry, the minimum size block cannot hold the links for + the free blocks. Either raise LOG_MIN_SIZE or switch + off NON_STANDARD. + */ + } + switch(0) { + case sizeof(void *) != sizeof(size_type): break; + case 1: break; + /* If this statement does not compile due to duplicate + case entry, size_type is not defined correctly. + Redefine and compile again. + */ + } +}} + +void * +realloc(void *addr, register size_t n) +{check_mallinks("realloc entry");{ + register mallink *ml, *ph_next; + register size_type size; + +printf("realloc 0x%x, %d\n", addr, n); + if (addr == NULL) { + /* Behave like most Unix realloc's when handed a + null-pointer + */ + return malloc(n); + } + if (n == 0) { + free(addr); + return NULL; + } + ml = mallink_of_block(addr); + if (n < MIN_SIZE) n = align(MIN_SIZE); else n = align(n); +#ifdef STORE + if (in_store(ml)) { + register mallink *stp = store[(size_of(ml) >> LOG_MIN_SIZE) - 1]; + mallink *stp1 = NULL; + while (ml != stp) { + stp1 = stp; + stp = log_next_of(stp); + } + stp = log_next_of(stp); + if (! stp1) store[(size_of(ml) >> LOG_MIN_SIZE) - 1] = stp; + else set_log_next(stp1, stp); + set_store(ml, 0); + calc_checksum(ml); + } +#endif + if (free_of(ml)) { + unlink_free_chunk(ml); + set_free(ml, 0); /* user reallocs free block */ + } + started_working_on(ml); + size = size_of(ml); + if ( /* we can simplify the problem by adding the next chunk: */ + n > size && + !last_mallink(ml) && + (ph_next = phys_next_of(ml), free_of(ph_next)) && + n <= size + mallink_size() + size_of(ph_next) + ) { + /* add in the physically next chunk */ + unlink_free_chunk(ph_next); + combine_chunks(ml, ph_next); + size = size_of(ml); + check_mallinks("realloc, combining"); + } + if (n > size) { /* this didn't help */ + void *new; + register char *l1, *l2 = addr; + + stopped_working_on(ml); + if (!(new = l1 = malloc(n))) return NULL; /* no way */ + while (size--) *l1++ = *l2++; + free(addr); + check_work_empty("mv_realloc"); +#ifdef STORE + assert(! in_store(mallink_of_block(new))); +#endif + return new; + } + /* it helped, but maybe too well */ + n += mallink_size(); + if (n + MIN_SIZE <= size_of(ml)) { + truncate(ml, n); + } + stopped_working_on(ml); + check_mallinks("realloc exit"); + check_work_empty("realloc"); +#ifdef STORE + assert(! in_store(ml)); +#endif + return addr; +}} + +void * +calloc(size_t nmemb, size_t size) +{check_mallinks("calloc entry");{ + long *l1, *l2; + size_t n; + +printf("calloc\n"); + if (size == 0) return NULL; + if (nmemb == 0) return NULL; + + /* Check for overflow on the multiplication. The peephole-optimizer + * will eliminate all but one of the possibilities. + */ + if (sizeof(size_t) == sizeof(int)) { + if (UINT_MAX / size < nmemb) return NULL; + } else if (sizeof(size_t) == sizeof(long)) { + if (ULONG_MAX / size < nmemb) return NULL; + } else return NULL; /* can't happen, can it ? */ + + n = size * nmemb; + if (n < MIN_SIZE) n = align(MIN_SIZE); else n = align(n); + if (n >= (1L << LOG_MAX_SIZE)) return NULL; + l1 = (long *) malloc(n); + l2 = l1 + (n / sizeof(long)); /* n is at least long aligned */ + while ( l2 != l1 ) *--l2 = 0; + check_mallinks("calloc exit"); + check_work_empty("calloc exit"); + return (void *)l1; +}} +/* Auxiliary routines */ + +#ifdef STORE +private +sell_out(void) { + /* Frees all block in store. + */ + register mallink **stp; + + for (stp = &store[0]; stp < &store[MAX_STORE]; stp++) { + register mallink *ml = *stp; + + while (ml) { + *stp = log_next_of(ml); + set_store(ml, 0); + do_free(ml); + ml = *stp; + } + } + +} +#endif /* STORE */ + +#ifdef ASSERT +private +m_assert(const char *fn, int ln) +{ + char ch; + + while (*fn) + write(2, fn++, 1); + write(2, ": malloc assert failed in line ", 31); + ch = (ln / 100) + '0'; write(2, &ch, 1); ln %= 100; + ch = (ln / 10) + '0'; write(2, &ch, 1); ln %= 10; + ch = (ln / 1) + '0'; write(2, &ch, 1); + write(2, "\n", 1); + maldump(1); +} +#endif /* ASSERT */ + + +/**********************************************************/ +/* +/* This was file log.c +/* +/**********************************************************/ + +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ + +/* Logical manipulations. + The chunks are properly chained in the physical chain. +*/ + +privatedata mallink *free_list[MAX_FLIST]; + +private +link_free_chunk(register mallink *ml) +{ + /* The free chunk ml is inserted in its proper logical + chain. + */ + register mallink **mlp = &free_list[-1]; + register size_type n = size_of(ml); + register mallink *ml1; + + assert(n < (1L << LOG_MAX_SIZE)); + + do { + n >>= 1; + mlp++; + } + while (n >= MIN_SIZE); + + ml1 = *mlp; + set_log_prev(ml, MAL_NULL); + set_log_next(ml, ml1); + calc_checksum(ml); + if (ml1) { + /* link backwards + */ + set_log_prev(ml1, ml); + calc_checksum(ml1); + } + *mlp = ml; +} + +private +unlink_free_chunk(register mallink *ml) +{ + /* Unlinks a free chunk from (the middle of) the + logical chain. + */ + register mallink *next = log_next_of(ml); + register mallink *prev = log_prev_of(ml); + + if (!prev) { + /* it is the first in the chain */ + register mallink **mlp = &free_list[-1]; + register size_type n = size_of(ml); + + assert(n < (1L << LOG_MAX_SIZE)); + do { + n >>= 1; + mlp++; + } + while (n >= MIN_SIZE); + *mlp = next; + } + else { + set_log_next(prev, next); + calc_checksum(prev); + } + if (next) { + set_log_prev(next, prev); + calc_checksum(next); + } +} + +private mallink * +search_free_list(int class, size_t n) +{ + /* Searches the free_list[class] for a chunk of at least size n; + since it is searching a slightly undersized list, + such a block may not be there. + */ + register mallink *ml; + + for (ml = free_list[class]; ml; ml = log_next_of(ml)) + if (size_of(ml) >= n) + return ml; + return MAL_NULL; /* nothing found */ +} + +private mallink * +first_present(int class) +{ + /* Find the index i in free_list[] such that: + i >= class && free_list[i] != MAL_NULL. + Return MAL_NULL if no such i exists; + Otherwise, return the first block of this list, after + unlinking it. + */ + register mallink **mlp, *ml; + + for (mlp = &free_list[class]; mlp < &free_list[MAX_FLIST]; mlp++) { + if ((ml = *mlp) != MAL_NULL) { + + *mlp = log_next_of(ml); /* may be MAL_NULL */ + if (*mlp) { + /* unhook backward link + */ + set_log_prev(*mlp, MAL_NULL); + calc_checksum(*mlp); + } + return ml; + } + } + return MAL_NULL; +} + +#ifdef CHECK +private mallink * +free_list_entry(int i) { + /* To allow maldump.c access to log.c's private data. + */ + return free_list[i]; +} +#endif /* CHECK */ + + +/**********************************************************/ +/* +/* This was file phys.c +/* +/**********************************************************/ + +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#include + +/* Physical manipulations. + The blocks concerned are not in any logical chain. +*/ + +private mallink * +create_chunk(void *p, size_t n) +{ + /* The newly acquired piece of memory at p, of length n, + is turned into a free chunk, properly chained in the + physical chain. + The address of the chunk is returned. + */ + register mallink *ml; + /* All of malloc memory is followed by a virtual chunk, the + mallink of which starts mallink_size() bytes past the last + byte in memory. + Its use is prevented by testing for ml == ml_last first. + */ + register mallink *last = ml_last; + + assert(!last || p == (char *)phys_next_of(last) - mallink_size()); + ml = (mallink *)((char *)p + mallink_size()); /* bump ml */ + new_mallink(ml); + started_working_on(ml); + set_free(ml, 1); + set_phys_prev(ml, last); + ml_last = ml; + + set_phys_next(ml, (mallink *)((char *)ml + n)); + calc_checksum(ml); + assert(size_of(ml) + mallink_size() == n); + if (last && free_of(last)) { + coalesce_backw(ml, last); + ml = last; + } + check_mallinks("create_chunk, phys. linked"); + return ml; +} + +private +truncate(register mallink *ml, size_t size) +{ + /* The chunk ml is truncated. + The chunk at ml is split in two. + The remaining part is then freed. + */ + register mallink *new = (mallink *)((char *)ml + size); + register mallink *ph_next = phys_next_of(ml); + + new_mallink(new); + set_free(new, 1); + set_phys_prev(new, ml); + set_phys_next(new, ph_next); + calc_checksum(new); + if (! last_mallink(ml)) { + set_phys_prev(ph_next, new); + calc_checksum(ph_next); + if (free_of(ph_next)) coalesce_forw(new, ph_next); + } + else ml_last = new; + set_phys_next(ml, new); + calc_checksum(ml); + + started_working_on(new); + link_free_chunk(new); + stopped_working_on(new); + check_mallinks("truncate"); +} + +private +combine_chunks(register mallink *ml1, register mallink *ml2) +{ + /* The chunks ml1 and ml2 are combined. + */ + register mallink *ml3 = phys_next_of(ml2); + + set_phys_next(ml1, ml3); + calc_checksum(ml1); + if (!last_mallink(ml2)) { + set_phys_prev(ml3, ml1); + calc_checksum(ml3); + } + if (ml_last == ml2) + ml_last = ml1; +} + + +/**********************************************************/ +/* +/* This was file check.c +/* +/**********************************************************/ + +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#include + +#ifdef CHECK /* otherwise this whole file is skipped */ + +/* ??? check these later */ +private acquire_malout(void), check_ml_last(const char *s); +private dump_all_mallinks(void), dump_free_list(int i); +private dump_mallink(const char *s, mallink *ml), print_loop(mallink *ml); +private working_on(mallink *ml); +private size_type checksum(mallink *ml); +static FILE *malout; + +private mallink *free_list_entry(int i); + +#define for_free_list(i,p) \ + for (p = free_list_entry(i); p; p = log_next_of(p)) + +#define for_all_mallinks(ml) /* backwards! */ \ + for (ml = ml_last; ml; \ + ml = first_mallink(ml) ? MAL_NULL : phys_prev_of(ml)) + +/* Maldump */ + +static int pr_cnt = 0; + +maldump(int n) { + /* Dump pertinent info in pseudo-readable format; + abort afterwards if n != 0. + */ + static int dumping = 0; + int i; + + if (dumping) + return; + dumping++; + acquire_malout(); + fprintf(malout, + ">>>>>>>>>>>>>>>> DUMP OF ALL MALLINKS <<<<<<<<<<<<<<<<"); + fprintf(malout, " ml_last = %p\n", ml_last); + if (++pr_cnt == 100) pr_cnt = 0; + dump_all_mallinks(); + fprintf(malout, + ">>>>>>>>>>>>>>>> DUMP OF FREE_LISTS <<<<<<<<<<<<<<<<\n"); + if (++pr_cnt == 100) pr_cnt = 0; + for (i = 0; i < MAX_FLIST; i++) + dump_free_list(i); + fprintf(malout, + ">>>>>>>>>>>>>>>> END OF DUMP <<<<<<<<<<<<<<<<\n"); + fclose(malout); + dumping--; + if (n) + abort(); +} + +private +acquire_malout(void) { + static char buf[BUFSIZ]; + + if (!malout) { + malout = freopen("mal.out", "w", stderr); + setbuf(malout, buf); + } +} + +private +dump_all_mallinks(void) { + mallink *ml; + + for_all_mallinks (ml) { + if (print_loop(ml)) + return; + dump_mallink((char *)0, ml); + } +} + +private +dump_free_list(int i) { + mallink *ml = free_list_entry(i); + + if (!ml) + return; + fprintf(malout, "%2d: ", i); + for_free_list(i, ml) { + if (print_loop(ml)) + return; + fprintf(malout, "%p ", ml); + } + fprintf(malout, "<\n"); +} + +private int +print_loop(mallink *ml) { + if (print_of(ml) == pr_cnt) { + fprintf(malout, "... PRINT LOOP\n"); + return 1; + } + set_print(ml, pr_cnt); + return 0; +} + +private +dump_mallink(const char *s, mallink *ml) { + acquire_malout(); + if (s) + fprintf(malout, "%s: ", s); + fprintf(malout, "@: %p;", ml); + if (ml && checksum_of(ml) != checksum(ml)) + fprintf(malout, ">>>> CORRUPTED <<<<"); + if (!ml) { + fprintf(malout, "\n"); + return; + } + if (free_of(ml)) { + fprintf(malout, " l_p: %p;", _log_prev_of(ml)); + fprintf(malout, " l_n: %p;", _log_next_of(ml)); + } + fprintf(malout, " p_s: %p;", prev_size_of(ml)); + fprintf(malout, " t_s: %p;", _this_size_of(ml)); + fprintf(malout, " sz: %lu;", (unsigned long) size_of(ml)); + fprintf(malout, " fr: %d;", free_of(ml)); + fprintf(malout, "\n"); +} + +/* Check_mallinks() checks the total data structure as accessible + through free_list[] and ml_last. All check_sums should be OK, + except those held in the small array off_colour. This is a + trick to allow to continue checking even when a few mallinks + are temporarily out of order. + Check_mallinks() tests for a lot of internal consistency. +*/ + +/* Some arbitrary constants */ +#define IN_ML_LAST 93 +#define IN_FREE_LIST 57 /* and in ml_last */ +#define CLEAR 21 + +#define VRIJ 1 +#define BEZET 2 + +private +check_mallinks(const char *s) { + mallink *ml; + size_type size; + int i; + char stat; + + check_ml_last(s); + stat = BEZET; + for_all_mallinks(ml) { + if (checksum_of(ml) != checksum(ml)) + Error("mallink info at %p corrupted", s, ml); + if (working_on(ml)) { + stat = BEZET; + continue; + } + if ( !last_mallink(ml) && + phys_prev_of(phys_next_of(ml)) != ml + ) + Error("upward chain bad at %p", s, ml); + if ( !first_mallink(ml) && + phys_next_of(phys_prev_of(ml)) != ml + ) + Error("downward chain bad at %p", s, ml); + if (free_of(ml)) { + if (stat == VRIJ) + Error("free mallink at %p follows free mallink", + s, ml); + stat = VRIJ; + } + else + stat = BEZET; + set_mark(ml, IN_ML_LAST); + } + + for (i = 0, size = MIN_SIZE; i < MAX_FLIST; i++, size *= 2) { + for_free_list(i, ml) { + if (working_on(ml)) + continue; + if (!free_of(ml)) + Error("occupied mallink %p occurs in free_list", s, ml); + switch (mark_of(ml)) { + case IN_ML_LAST: + set_mark(ml, IN_FREE_LIST); + break; + case IN_FREE_LIST: + Error("mallink %p occurs in 2 free_lists", + s, ml); + default: + Error("unknown mallink %p in free_list", + s, ml); + } + if (size_of(ml) < size) + Error("size of mallink %p too small", s, ml); + if (size_of(ml) >= 2*size) + Error("size of mallink %p too large", s, ml); + } + } + for_all_mallinks (ml) { + if (working_on(ml)) + continue; + if (free_of(ml) && mark_of(ml) != IN_FREE_LIST) + Error("free mallink %p is in no free_list", s, ml); + set_mark(ml, CLEAR); + } +} + +private +check_ml_last(const char *s) { + if (ml_last && _this_size_of(ml_last) == 0) + Error("size of ml_last == 0, at %p", s, ml_last); +} + +private size_type +checksum(mallink *ml) { + size_type sum = 0; + + if (free_of(ml)) { + sum += (size_type)_log_prev_of(ml); + sum += (size_type)_log_next_of(ml); + } + sum += (size_type)prev_size_of(ml); + sum += (size_type)_this_size_of(ml); + return sum; +} + +private +calc_checksum(mallink *ml) { + set_checksum(ml, checksum(ml)); +} + +#define N_COLOUR 10 +static mallink *off_colour[N_COLOUR]; + +private +started_working_on(mallink *ml) { + int i; + + for (i = 0; i < N_COLOUR; i++) + if (off_colour[i] == MAL_NULL) { + off_colour[i] = ml; + return; + } + Error("out of off_colour array at %p", "started_working_on", ml); +} + +private +stopped_working_on(mallink *ml) { + int i; + + for (i = 0; i < N_COLOUR; i++) + if (off_colour[i] == ml) { + off_colour[i] = MAL_NULL; + return; + } + Error("stopped working on mallink %p", "stopped_working_on", ml); +} + +private int +working_on(mallink *ml) { + int i; + + for (i = 0; i < N_COLOUR; i++) + if (off_colour[i] == ml) + return 1; + return 0; +} + +private +check_work_empty(const char *s) { + int i; + int cnt = 0; + + for (i = 0; i < N_COLOUR; i++) + if (off_colour[i] != MAL_NULL) + cnt++; + if (cnt != 0) + Error("off_colour not empty", s, MAL_NULL); +} + +private int +Error(const char *fmt, const char *s, mallink *ml) { + static int already_called = 0; + + if (already_called++) return 0; + setbuf(stdout, (char *) 0); + printf("%s: ", s); + printf(fmt, (long)ml); + printf("\n"); + acquire_malout(); + fprintf(malout, "%s: ", s); + fprintf(malout, fmt, (long)ml); + fprintf(malout, "\n"); + fflush(stdout); + maldump(1); + return 0; /* to satisfy lint */ +} + +#endif /* CHECK */ + diff --git a/commands/ash/trap.c b/commands/ash/trap.c new file mode 100755 index 000000000..f4ec70fdf --- /dev/null +++ b/commands/ash/trap.c @@ -0,0 +1,328 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)trap.c 5.2 (Berkeley) 4/12/91"; +#endif /* not lint */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" /* for other headers */ +#include "eval.h" +#include "jobs.h" +#include "options.h" +#include "syntax.h" +#include "signames.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "trap.h" +#include "mystring.h" +#include +#include + + +/* + * Sigmode records the current value of the signal handlers for the various + * modes. A value of zero means that the current handler is not known. + * S_HARD_IGN indicates that the signal was ignored on entry to the shell, + */ + +#define S_DFL 1 /* default signal handling (SIG_DFL) */ +#define S_CATCH 2 /* signal is caught */ +#define S_IGN 3 /* signal is ignored (SIG_IGN) */ +#define S_HARD_IGN 4 /* signal is ignored permenantly */ + + +extern char nullstr[1]; /* null string */ + +char *trap[MAXSIG+1]; /* trap handler commands */ +MKINIT char sigmode[MAXSIG]; /* current value of signal */ +char gotsig[MAXSIG]; /* indicates specified signal received */ +int pendingsigs; /* indicates some signal received */ + +/* + * The trap builtin. + */ + +trapcmd(argc, argv) char **argv; { + char *action; + char **ap; + int signo; + + if (argc <= 1) { + for (signo = 0 ; signo <= MAXSIG ; signo++) { + if (trap[signo] != NULL) + out1fmt("%d: %s\n", signo, trap[signo]); + } + return 0; + } + ap = argv + 1; + if (is_number(*ap)) + action = NULL; + else + action = *ap++; + while (*ap) { + if ((signo = number(*ap)) < 0 || signo > MAXSIG) + error("%s: bad trap", *ap); + INTOFF; + if (action) + action = savestr(action); + if (trap[signo]) + ckfree(trap[signo]); + trap[signo] = action; + if (signo != 0) + setsignal(signo); + INTON; + ap++; + } + return 0; +} + + + +/* + * Clear traps on a fork. + */ + +void +clear_traps() { + char **tp; + + for (tp = trap ; tp <= &trap[MAXSIG] ; tp++) { + if (*tp && **tp) { /* trap not NULL or SIG_IGN */ + INTOFF; + ckfree(*tp); + *tp = NULL; + if (tp != &trap[0]) + setsignal(tp - trap); + INTON; + } + } +} + + + +/* + * Set the signal handler for the specified signal. The routine figures + * out what it should be set to. + */ + +int +setsignal(signo) { + int action; + sig_t sigact; + char *t; + extern void onsig(); + + if ((t = trap[signo]) == NULL) + action = S_DFL; + else if (*t != '\0') + action = S_CATCH; + else + action = S_IGN; + if (rootshell && action == S_DFL) { + switch (signo) { + case SIGINT: + if (iflag) + action = S_CATCH; + break; + case SIGQUIT: +#if DEBUG + { + extern int debug; + + if (debug) + break; + } +#endif + /* FALLTHROUGH */ + case SIGTERM: + if (iflag) + action = S_IGN; + break; +#if JOBS + case SIGTSTP: + case SIGTTOU: + if (jflag) + action = S_IGN; + break; +#endif + } + } + t = &sigmode[signo - 1]; + if (*t == 0) { /* current setting unknown */ + /* + * There is a race condition here if action is not S_IGN. + * A signal can be ignored that shouldn't be. + */ + if ((int)(sigact = signal(signo, SIG_IGN)) == -1) + error("Signal system call failed"); + if (sigact == SIG_IGN) { + *t = S_HARD_IGN; + } else { + *t = S_IGN; + } + } + if (*t == S_HARD_IGN || *t == action) + return 0; + switch (action) { + case S_DFL: sigact = SIG_DFL; break; + case S_CATCH: sigact = onsig; break; + case S_IGN: sigact = SIG_IGN; break; + } + *t = action; + return (int)signal(signo, sigact); +} + + +/* + * Ignore a signal. + */ + +void +ignoresig(signo) { + if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) { + signal(signo, SIG_IGN); + } + sigmode[signo - 1] = S_HARD_IGN; +} + + +#ifdef mkinit +INCLUDE "signames.h" +INCLUDE "trap.h" + +SHELLPROC { + char *sm; + + clear_traps(); + for (sm = sigmode ; sm < sigmode + MAXSIG ; sm++) { + if (*sm == S_IGN) + *sm = S_HARD_IGN; + } +} +#endif + + + +/* + * Signal handler. + */ + +void +onsig(signo) { + signal(signo, onsig); + if (signo == SIGINT && trap[SIGINT] == NULL) { + onint(); + return; + } + gotsig[signo - 1] = 1; + pendingsigs++; +} + + + +/* + * Called to execute a trap. Perhaps we should avoid entering new trap + * handlers while we are executing a trap handler. + */ + +void +dotrap() { + int i; + int savestatus; + + for (;;) { + for (i = 1 ; ; i++) { + if (gotsig[i - 1]) + break; + if (i >= MAXSIG) + goto done; + } + gotsig[i - 1] = 0; + savestatus=exitstatus; + evalstring(trap[i]); + exitstatus=savestatus; + } +done: + pendingsigs = 0; +} + + + +/* + * Controls whether the shell is interactive or not. + */ + +int is_interactive; + +void +setinteractive(on) { + if (on == is_interactive) + return; + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + is_interactive = on; +} + + + +/* + * Called to exit the shell. + */ + +void +exitshell(status) { + struct jmploc loc1, loc2; + char *p; + + TRACE(("exitshell(%d) pid=%d\n", status, getpid())); + if (setjmp(loc1.loc)) goto l1; + if (setjmp(loc2.loc)) goto l2; + handler = &loc1; + if ((p = trap[0]) != NULL && *p != '\0') { + trap[0] = NULL; + evalstring(p); + } +l1: handler = &loc2; /* probably unnecessary */ + flushall(); +#if JOBS + setjobctl(0); +#endif +l2: _exit(status); +} diff --git a/commands/ash/trap.h b/commands/ash/trap.h new file mode 100755 index 000000000..afa984cb0 --- /dev/null +++ b/commands/ash/trap.h @@ -0,0 +1,55 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)trap.h 5.1 (Berkeley) 3/7/91 + */ + +extern int pendingsigs; + +#ifdef __STDC__ +void clear_traps(void); +int setsignal(int); +void ignoresig(int); +void dotrap(void); +void setinteractive(int); +void exitshell(int); +#else +void clear_traps(); +int setsignal(); +void ignoresig(); +void dotrap(); +void setinteractive(); +void exitshell(); +#endif diff --git a/commands/ash/var.c b/commands/ash/var.c new file mode 100755 index 000000000..f6e38d05f --- /dev/null +++ b/commands/ash/var.c @@ -0,0 +1,651 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char sccsid[] = "@(#)var.c 5.3 (Berkeley) 4/12/91"; +#endif /* not lint */ + +/* + * Shell variables. + */ + +#include "shell.h" +#include "output.h" +#include "expand.h" +#include "nodes.h" /* for other headers */ +#include "eval.h" /* defines cmdenviron */ +#include "exec.h" +#include "syntax.h" +#include "options.h" +#include "mail.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" + + +#define VTABSIZE 39 + + +struct varinit { + struct var *var; + int flags; + char *text; +}; + + +#if ATTY +struct var vatty; +#endif +struct var vifs; +struct var vmail; +struct var vmpath; +struct var vpath; +struct var vps1; +struct var vps2; +struct var vpse; +struct var vvers; +#if ATTY +struct var vterm; +#endif + +const struct varinit varinit[] = { +#if ATTY + {&vatty, VSTRFIXED|VTEXTFIXED|VUNSET, "ATTY="}, +#endif + {&vifs, VSTRFIXED|VTEXTFIXED, "IFS= \t\n"}, + {&vmail, VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL="}, + {&vmpath, VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH="}, + {&vpath, VSTRFIXED|VTEXTFIXED, "PATH=:/bin:/usr/bin"}, + /* + * vps1 depends on uid + */ + {&vps2, VSTRFIXED|VTEXTFIXED, "PS2=> "}, + {&vpse, VSTRFIXED|VTEXTFIXED, "PSE=* "}, +#if ATTY + {&vterm, VSTRFIXED|VTEXTFIXED|VUNSET, "TERM="}, +#endif + {NULL, 0, NULL} +}; + +struct var *vartab[VTABSIZE]; + +STATIC void unsetvar __P((char *)); +STATIC struct var **hashvar __P((char *)); +STATIC int varequal __P((char *, char *)); + +/* + * Initialize the varable symbol tables and import the environment + */ + +#ifdef mkinit +INCLUDE "var.h" +INIT { + char **envp; + extern char **environ; + + initvar(); + for (envp = environ ; *envp ; envp++) { + if (strchr(*envp, '=')) { + setvareq(*envp, VEXPORT|VTEXTFIXED); + } + } +} +#endif + + +/* + * This routine initializes the builtin variables. It is called when the + * shell is initialized and again when a shell procedure is spawned. + */ + +void +initvar() { + const struct varinit *ip; + struct var *vp; + struct var **vpp; + + for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { + if ((vp->flags & VEXPORT) == 0) { + vpp = hashvar(ip->text); + vp->next = *vpp; + *vpp = vp; + vp->text = ip->text; + vp->flags = ip->flags; + } + } + /* + * PS1 depends on uid + */ + if ((vps1.flags & VEXPORT) == 0) { + vpp = hashvar("PS1="); + vps1.next = *vpp; + *vpp = &vps1; + vps1.text = getuid() ? "PS1=$ " : "PS1=# "; + vps1.flags = VSTRFIXED|VTEXTFIXED; + } +} + +/* + * Set the value of a variable. The flags argument is ored with the + * flags of the variable. If val is NULL, the variable is unset. + */ + +void +setvar(name, val, flags) + char *name, *val; + { + char *p, *q; + int len; + int namelen; + char *nameeq; + int isbad; + + isbad = 0; + p = name; + if (! is_name(*p++)) + isbad = 1; + for (;;) { + if (! is_in_name(*p)) { + if (*p == '\0' || *p == '=') + break; + isbad = 1; + } + p++; + } + namelen = p - name; + if (isbad) + error("%.*s: is read only", namelen, name); + len = namelen + 2; /* 2 is space for '=' and '\0' */ + if (val == NULL) { + flags |= VUNSET; + } else { + len += strlen(val); + } + p = nameeq = ckmalloc(len); + q = name; + while (--namelen >= 0) + *p++ = *q++; + *p++ = '='; + *p = '\0'; + if (val) + scopy(val, p); + setvareq(nameeq, flags); +} + + + +/* + * Same as setvar except that the variable and value are passed in + * the first argument as name=value. Since the first argument will + * be actually stored in the table, it should not be a string that + * will go away. + */ + +void +setvareq(s, flags) + char *s; + { + struct var *vp, **vpp; + + vpp = hashvar(s); + for (vp = *vpp ; vp ; vp = vp->next) { + if (varequal(s, vp->text)) { + if (vp->flags & VREADONLY) { + int len = strchr(s, '=') - s; + error("%.*s: is read only", len, s); + } + INTOFF; + if (vp == &vpath) + changepath(s + 5); /* 5 = strlen("PATH=") */ + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + vp->flags &=~ (VTEXTFIXED|VSTACK|VUNSET); + vp->flags |= flags; + vp->text = s; + if (vp == &vmpath || (vp == &vmail && ! mpathset())) + chkmail(1); + INTON; + return; + } + } + /* not found */ + vp = ckmalloc(sizeof (*vp)); + vp->flags = flags; + vp->text = s; + vp->next = *vpp; + *vpp = vp; +} + + + +/* + * Process a linked list of variable assignments. + */ + +void +listsetvar(list) + struct strlist *list; + { + struct strlist *lp; + + INTOFF; + for (lp = list ; lp ; lp = lp->next) { + setvareq(savestr(lp->text), 0); + } + INTON; +} + + + +/* + * Find the value of a variable. Returns NULL if not set. + */ + +char * +lookupvar(name) + char *name; + { + struct var *v; + + for (v = *hashvar(name) ; v ; v = v->next) { + if (varequal(v->text, name)) { + if (v->flags & VUNSET) + return NULL; + return strchr(v->text, '=') + 1; + } + } + return NULL; +} + + + +/* + * Search the environment of a builtin command. If the second argument + * is nonzero, return the value of a variable even if it hasn't been + * exported. + */ + +char * +bltinlookup(name, doall) + char *name; + { + struct strlist *sp; + struct var *v; + + for (sp = cmdenviron ; sp ; sp = sp->next) { + if (varequal(sp->text, name)) + return strchr(sp->text, '=') + 1; + } + for (v = *hashvar(name) ; v ; v = v->next) { + if (varequal(v->text, name)) { + if (v->flags & VUNSET + || ! doall && (v->flags & VEXPORT) == 0) + return NULL; + return strchr(v->text, '=') + 1; + } + } + return NULL; +} + + + +/* + * Generate a list of exported variables. This routine is used to construct + * the third argument to execve when executing a program. + */ + +char ** +environment() { + int nenv; + struct var **vpp; + struct var *vp; + char **env, **ep; + + nenv = 0; + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + nenv++; + } + ep = env = stalloc((nenv + 1) * sizeof *env); + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + *ep++ = vp->text; + } + *ep = NULL; + return env; +} + + +/* + * Called when a shell procedure is invoked to clear out nonexported + * variables. It is also necessary to reallocate variables of with + * VSTACK set since these are currently allocated on the stack. + */ + +#ifdef mkinit +MKINIT void shprocvar(); + +SHELLPROC { + shprocvar(); +} +#endif + +void +shprocvar() { + struct var **vpp; + struct var *vp, **prev; + + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (prev = vpp ; (vp = *prev) != NULL ; ) { + if ((vp->flags & VEXPORT) == 0) { + *prev = vp->next; + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + if ((vp->flags & VSTRFIXED) == 0) + ckfree(vp); + } else { + if (vp->flags & VSTACK) { + vp->text = savestr(vp->text); + vp->flags &=~ VSTACK; + } + prev = &vp->next; + } + } + } + initvar(); +} + + + +/* + * Command to list all variables which are set. Currently this command + * is invoked from the set command when the set command is called without + * any variables. + */ + +int +showvarscmd(argc, argv) char **argv; { + struct var **vpp; + struct var *vp; + + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) { + if ((vp->flags & VUNSET) == 0) + out1fmt("%s\n", vp->text); + } + } + return 0; +} + + + +/* + * The export and readonly commands. + */ + +int +exportcmd(argc, argv) char **argv; { + struct var **vpp; + struct var *vp; + char *name; + char *p; + int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; + + listsetvar(cmdenviron); + if (argc > 1) { + while ((name = *argptr++) != NULL) { + if ((p = strchr(name, '=')) != NULL) { + p++; + } else { + vpp = hashvar(name); + for (vp = *vpp ; vp ; vp = vp->next) { + if (varequal(vp->text, name)) { + vp->flags |= flag; + goto found; + } + } + } + setvar(name, p, flag); +found:; + } + } else { + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) { + if (vp->flags & flag) { + for (p = vp->text ; *p != '=' ; p++) + out1c(*p); + out1c('\n'); + } + } + } + } + return 0; +} + + +/* + * The "local" command. + */ + +localcmd(argc, argv) char **argv; { + char *name; + + if (! in_function()) + error("Not in a function"); + while ((name = *argptr++) != NULL) { + mklocal(name); + } + return 0; +} + + +/* + * Make a variable a local variable. When a variable is made local, it's + * value and flags are saved in a localvar structure. The saved values + * will be restored when the shell function returns. We handle the name + * "-" as a special case. + */ + +void +mklocal(name) + char *name; + { + struct localvar *lvp; + struct var **vpp; + struct var *vp; + + INTOFF; + lvp = ckmalloc(sizeof (struct localvar)); + if (name[0] == '-' && name[1] == '\0') { + lvp->text = ckmalloc(sizeof optval); + bcopy(optval, lvp->text, sizeof optval); + vp = NULL; + } else { + vpp = hashvar(name); + for (vp = *vpp ; vp && ! varequal(vp->text, name) ; vp = vp->next); + if (vp == NULL) { + if (strchr(name, '=')) + setvareq(savestr(name), VSTRFIXED); + else + setvar(name, NULL, VSTRFIXED); + vp = *vpp; /* the new variable */ + lvp->text = NULL; + lvp->flags = VUNSET; + } else { + lvp->text = vp->text; + lvp->flags = vp->flags; + vp->flags |= VSTRFIXED|VTEXTFIXED; + if (strchr(name, '=')) + setvareq(savestr(name), 0); + } + } + lvp->vp = vp; + lvp->next = localvars; + localvars = lvp; + INTON; +} + + +/* + * Called after a function returns. + */ + +void +poplocalvars() { + struct localvar *lvp; + struct var *vp; + + while ((lvp = localvars) != NULL) { + localvars = lvp->next; + vp = lvp->vp; + if (vp == NULL) { /* $- saved */ + bcopy(lvp->text, optval, sizeof optval); + ckfree(lvp->text); + } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { + unsetvar(vp->text); + } else { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + vp->flags = lvp->flags; + vp->text = lvp->text; + } + ckfree(lvp); + } +} + + +setvarcmd(argc, argv) char **argv; { + if (argc <= 2) + return unsetcmd(argc, argv); + else if (argc == 3) + setvar(argv[1], argv[2], 0); + else + error("List assignment not implemented"); + return 0; +} + + +/* + * The unset builtin command. We unset the function before we unset the + * variable to allow a function to be unset when there is a readonly variable + * with the same name. + */ + +unsetcmd(argc, argv) char **argv; { + char **ap; + + for (ap = argv + 1 ; *ap ; ap++) { + unsetfunc(*ap); + unsetvar(*ap); + } + return 0; +} + + +/* + * Unset the specified variable. + */ + +STATIC void +unsetvar(s) + char *s; + { + struct var **vpp; + struct var *vp; + + vpp = hashvar(s); + for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { + if (varequal(vp->text, s)) { + INTOFF; + if (*(strchr(vp->text, '=') + 1) != '\0' + || vp->flags & VREADONLY) { + setvar(s, nullstr, 0); + } + vp->flags &=~ VEXPORT; + vp->flags |= VUNSET; + if ((vp->flags & VSTRFIXED) == 0) { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + *vpp = vp->next; + ckfree(vp); + } + INTON; + return; + } + } +} + + + +/* + * Find the appropriate entry in the hash table from the name. + */ + +STATIC struct var ** +hashvar(p) + register char *p; + { + unsigned int hashval; + + hashval = *p << 4; + while (*p && *p != '=') + hashval += *p++; + return &vartab[hashval % VTABSIZE]; +} + + + +/* + * Returns true if the two strings specify the same varable. The first + * variable name is terminated by '='; the second may be terminated by + * either '=' or '\0'. + */ + +STATIC int +varequal(p, q) + register char *p, *q; + { + while (*p == *q++) { + if (*p++ == '=') + return 1; + } + if (*p == '=' && *(q - 1) == '\0') + return 1; + return 0; +} diff --git a/commands/ash/var.h b/commands/ash/var.h new file mode 100755 index 000000000..70a36065f --- /dev/null +++ b/commands/ash/var.h @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 1991 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)var.h 5.1 (Berkeley) 3/7/91 + */ + +/* + * Shell variables. + */ + +/* flags */ +#define VEXPORT 01 /* variable is exported */ +#define VREADONLY 02 /* variable cannot be modified */ +#define VSTRFIXED 04 /* variable struct is staticly allocated */ +#define VTEXTFIXED 010 /* text is staticly allocated */ +#define VSTACK 020 /* text is allocated on the stack */ +#define VUNSET 040 /* the variable is not set */ + + +struct var { + struct var *next; /* next entry in hash list */ + int flags; /* flags are defined above */ + char *text; /* name=value */ +}; + + +struct localvar { + struct localvar *next; /* next local variable in list */ + struct var *vp; /* the variable that was made local */ + int flags; /* saved flags */ + char *text; /* saved text */ +}; + + +struct localvar *localvars; + +#if ATTY +extern struct var vatty; +#endif +extern struct var vifs; +extern struct var vmail; +extern struct var vmpath; +extern struct var vpath; +extern struct var vps1; +extern struct var vps2; +extern struct var vpse; +#if ATTY +extern struct var vterm; +#endif + +/* + * The following macros access the values of the above variables. + * They have to skip over the name. They return the null string + * for unset variables. + */ + +#define ifsval() (vifs.text + 4) +#define mailval() (vmail.text + 5) +#define mpathval() (vmpath.text + 9) +#define pathval() (vpath.text + 5) +#define ps1val() (vps1.text + 4) +#define ps2val() (vps2.text + 4) +#define pseval() (vpse.text + 4) +#if ATTY +#define termval() (vterm.text + 5) +#endif + +#if ATTY +#define attyset() ((vatty.flags & VUNSET) == 0) +#endif +#define mpathset() ((vmpath.flags & VUNSET) == 0) + + +#ifdef __STDC__ +void initvar(); +void setvar(char *, char *, int); +void setvareq(char *, int); +struct strlist; +void listsetvar(struct strlist *); +char *lookupvar(char *); +char *bltinlookup(char *, int); +char **environment(); +int showvarscmd(int, char **); +void mklocal(char *); +void poplocalvars(void); +#else +void initvar(); +void setvar(); +void setvareq(); +void listsetvar(); +char *lookupvar(); +char *bltinlookup(); +char **environment(); +int showvarscmd(); +void mklocal(); +void poplocalvars(); +#endif diff --git a/commands/autil/Makefile b/commands/autil/Makefile new file mode 100755 index 000000000..25c2c954c --- /dev/null +++ b/commands/autil/Makefile @@ -0,0 +1,25 @@ +# Makefile for commands/autil + +CFLAGS = -D_MINIX -D_POSIX_SOURCE -wo +CCLD = $(CC) -i $(CFLAGS) + +all: anm asize + +anm: anm.c + $(CCLD) -o $@ $? + install -S 32kw $@ + +asize: asize.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +install: /usr/bin/anm /usr/bin/asize + +/usr/bin/anm: anm + install -cs -o bin $? $@ + +/usr/bin/asize: asize + install -cs -o bin $? $@ + +clean: + rm -f anm asize core diff --git a/commands/autil/anm.c b/commands/autil/anm.c new file mode 100755 index 000000000..e4e31ff12 --- /dev/null +++ b/commands/autil/anm.c @@ -0,0 +1,304 @@ +/* @(#)anm.c 1.6 */ +/* +** print symbol tables for +** ACK object files +** +** anm [-gopruns] [name ...] +*/ +#define ushort unsigned short + +#include "out.h" + +#include +#include +#include + +int numsort_flg; +int sectsort_flg; +int undef_flg; +int revsort_flg = 1; +int globl_flg; +int nosort_flg; +int arch_flg; +int prep_flg; +struct outhead hbuf; +struct outsect sbuf; +FILE *fi; +long off; +long s_base[S_MAX]; /* for specially encoded bases */ + +main(argc, argv) +char **argv; +{ + int narg; + int compare(); + + if (--argc>0 && argv[1][0]=='-' && argv[1][1]!=0) { + argv++; + while (*++*argv) switch (**argv) { + case 'n': /* sort numerically */ + numsort_flg++; + continue; + + case 's': /* sort in section order */ + sectsort_flg++; + continue; + + case 'g': /* globl symbols only */ + globl_flg++; + continue; + + case 'u': /* undefined symbols only */ + undef_flg++; + continue; + + case 'r': /* sort in reverse order */ + revsort_flg = -1; + continue; + + case 'p': /* don't sort -- symbol table order */ + nosort_flg++; + continue; + + case 'o': /* prepend a name to each line */ + prep_flg++; + continue; + + default: /* oops */ + fprintf(stderr, "anm: invalid argument -%c\n", *argv[0]); + exit(1); + } + argc--; + } + if (argc == 0) { + argc = 1; + argv[1] = "a.out"; + } + narg = argc; + + while(argc--) { + struct outname *nbufp = (struct outname *)NULL; + struct outname nbuf; + char *cbufp; + long fi_to_co; + long n; + unsigned readcount; + int i,j; + + fi = fopen(*++argv,"r"); + if (fi == (FILE *)NULL) { + fprintf(stderr, "anm: cannot open %s\n", *argv); + continue; + } + + getofmt((char *)&hbuf, SF_HEAD, fi); + if (BADMAGIC(hbuf)) { + fprintf(stderr, "anm: %s-- bad format\n", *argv); + fclose(fi); + continue; + } + if (narg > 1) + printf("\n%s:\n", *argv); + + n = hbuf.oh_nname; + if (n == 0) { + fprintf(stderr, "anm: %s-- no name list\n", *argv); + fclose(fi); + continue; + } + + if (hbuf.oh_nchar == 0) { + fprintf(stderr, "anm: %s-- no names\n", *argv); + fclose(fi); + continue; + } + if ((readcount = hbuf.oh_nchar) != hbuf.oh_nchar) { + fprintf(stderr, "anm: string area too big in %s\n", *argv); + exit(2); + } + + /* store special section bases */ + if (hbuf.oh_flags & HF_8086) { + for (i=0; i>12) & 03777760; + } + } + + if ((cbufp = (char *)malloc(readcount)) == (char *)NULL) { + fprintf(stderr, "anm: out of memory on %s\n", *argv); + exit(2); + } + fseek(fi, OFF_CHAR(hbuf), 0); + if (fread(cbufp, 1, readcount, fi) == 0) { + fprintf(stderr, "anm: read error on %s\n", *argv); + exit(2); + } + fi_to_co = (long)cbufp - OFF_CHAR(hbuf); + + fseek(fi, OFF_NAME(hbuf), 0); + i = 0; + while (--n >= 0) { + getofmt((char *)&nbuf, SF_NAME, fi); + + if (nbuf.on_foff == 0) + continue; /* skip entries without names */ + + if (globl_flg && (nbuf.on_type&S_EXT)==0) + continue; + + if (undef_flg + && + ((nbuf.on_type&S_TYP)!=S_UND || (nbuf.on_type&S_ETC)!=0)) + continue; + + nbuf.on_mptr = (char *)(nbuf.on_foff + fi_to_co); + + /* adjust value for specially encoded bases */ + if (hbuf.oh_flags & HF_8086) { + if (((nbuf.on_type&S_ETC) == 0) || + ((nbuf.on_type&S_ETC) == S_SCT)) { + j = nbuf.on_type&S_TYP; + if ((j>=S_MIN) && (j<=S_MAX)) + nbuf.on_valu += s_base[j]; + } + } + + if (nbufp == (struct outname *)NULL) + nbufp = (struct outname *)malloc(sizeof(struct outname)); + else + nbufp = (struct outname *)realloc(nbufp, (i+1)*sizeof(struct outname)); + if (nbufp == (struct outname *)NULL) { + fprintf(stderr, "anm: out of memory on %s\n", *argv); + exit(2); + } + nbufp[i++] = nbuf; + } + + if (nbufp && nosort_flg==0) + qsort(nbufp, i, sizeof(struct outname), compare); + + for (n=0; non_type&S_TYP) > (p2->on_type&S_TYP)) + return(revsort_flg); + if ((p1->on_type&S_TYP) < (p2->on_type&S_TYP)) + return(-revsort_flg); + } + + if (numsort_flg) { + if (p1->on_valu > p2->on_valu) + return(revsort_flg); + if (p1->on_valu < p2->on_valu) + return(-revsort_flg); + } + + i = strcmp(p1->on_mptr, p2->on_mptr); + + if (i > 0) + return(revsort_flg); + if (i < 0) + return(-revsort_flg); + + return(0); +} + +getofmt(p, s, f) +register char *p; +register char *s; +register FILE *f; +{ + register i; + register long l; + + for (;;) { + switch (*s++) { +/* case '0': p++; continue; */ + case '1': + *p++ = getc(f); + continue; + case '2': + i = getc(f); + i |= (getc(f) << 8); + *((short *)p) = i; p += sizeof(short); + continue; + case '4': + l = (long)getc(f); + l |= ((long)getc(f) << 8); + l |= ((long)getc(f) << 16); + l |= ((long)getc(f) << 24); + *((long *)p) = l; p += sizeof(long); + continue; + default: + case '\0': + break; + } + break; + } +} diff --git a/commands/autil/asize.c b/commands/autil/asize.c new file mode 100755 index 000000000..337338e78 --- /dev/null +++ b/commands/autil/asize.c @@ -0,0 +1,93 @@ +/* @(#)asize.c 1.4 */ +#define ushort unsigned short + +#include +#include "out.h" + +/* + asize -- determine object size + +*/ + +main(argc, argv) +char **argv; +{ + struct outhead buf; + struct outsect sbuf; + ushort nrsect; + long sum; + int gorp; + FILE *f; + + if (--argc == 0) { + argc = 1; + argv[1] = "a.out"; + } + gorp = argc; + + while(argc--) { + if ((f = fopen(*++argv, "r"))==NULL) { + fprintf(stderr, "asize: cannot open %s\n", *argv); + continue; + } + getofmt ((char *)&buf, SF_HEAD , f); + if(BADMAGIC(buf)) { + fprintf(stderr, "asize: %s-- bad format\n", *argv); + fclose(f); + continue; + } + nrsect = buf.oh_nsect; + if (nrsect == 0) { + fprintf(stderr, "asize: %s-- no sections\n", *argv); + fclose(f); + continue; + } + if (gorp > 1) + printf("%s: ", *argv); + + sum = 0; + while (nrsect-- > 0) { + getofmt ((char *)&sbuf, SF_SECT , f); + printf("%ld", sbuf.os_size); + sum += sbuf.os_size; + if (nrsect > 0) + putchar('+'); + } + printf(" = %ld = 0x%lx\n", sum, sum); + fclose(f); + } +} + +getofmt(p, s, f) +register char *p; +register char *s; +register FILE *f; +{ + register i; + register long l; + + for (;;) { + switch (*s++) { +/* case '0': p++; continue; */ + case '1': + *p++ = getc(f); + continue; + case '2': + i = getc(f); + i |= (getc(f) << 8); + *((short *)p) = i; p += sizeof(short); + continue; + case '4': + l = (long)getc(f); + l |= ((long)getc(f) << 8); + l |= ((long)getc(f) << 16); + l |= ((long)getc(f) << 24); + *((long *)p) = l; p += sizeof(long); + continue; + default: + case '\0': + break; + } + break; + } +} diff --git a/commands/autil/out.h b/commands/autil/out.h new file mode 100755 index 000000000..0834f665b --- /dev/null +++ b/commands/autil/out.h @@ -0,0 +1,126 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#ifndef __OUT_H_INCLUDED +#define __OUT_H_INCLUDED +/* + * output format for ACK assemblers + */ +#ifndef ushort +#define ushort unsigned short +#endif /* ushort */ + +struct outhead { + ushort oh_magic; /* magic number */ + ushort oh_stamp; /* version stamp */ + ushort oh_flags; /* several format flags */ + ushort oh_nsect; /* number of outsect structures */ + ushort oh_nrelo; /* number of outrelo structures */ + ushort oh_nname; /* number of outname structures */ + long oh_nemit; /* sum of all os_flen */ + long oh_nchar; /* size of string area */ +}; + +#define O_MAGIC 0x0201 /* magic number of output file */ +#define O_STAMP 0 /* version stamp */ +#define MAXSECT 64 /* Maximum number of sections */ + +#define HF_LINK 0x0004 /* unresolved references left */ +#define HF_8086 0x0008 /* os_base specially encoded */ + +struct outsect { + long os_base; /* startaddress in machine */ + long os_size; /* section size in machine */ + long os_foff; /* startaddress in file */ + long os_flen; /* section size in file */ + long os_lign; /* section alignment */ +}; + +struct outrelo { + char or_type; /* type of reference */ + char or_sect; /* referencing section */ + ushort or_nami; /* referenced symbol index */ + long or_addr; /* referencing address */ +}; + +struct outname { + union { + char *on_ptr; /* symbol name (in core) */ + long on_off; /* symbol name (in file) */ + } on_u; +#define on_mptr on_u.on_ptr +#define on_foff on_u.on_off + ushort on_type; /* symbol type */ + ushort on_desc; /* debug info */ + long on_valu; /* symbol value */ +}; + +/* + * relocation type bits + */ +#define RELSZ 0x07 /* relocation length */ +#define RELO1 1 /* 1 byte */ +#define RELO2 2 /* 2 bytes */ +#define RELO4 4 /* 4 bytes */ +#define RELPC 0x08 /* pc relative */ +#define RELBR 0x10 /* High order byte lowest address. */ +#define RELWR 0x20 /* High order word lowest address. */ + +/* + * section type bits and fields + */ +#define S_TYP 0x007F /* undefined, absolute or relative */ +#define S_EXT 0x0080 /* external flag */ +#define S_ETC 0x7F00 /* for symbolic debug, bypassing 'as' */ + +/* + * S_TYP field values + */ +#define S_UND 0x0000 /* undefined item */ +#define S_ABS 0x0001 /* absolute item */ +#define S_MIN 0x0002 /* first user section */ +#define S_MAX (S_TYP-1) /* last user section */ +#define S_CRS S_TYP /* on_valu is symbol index which contains value */ + +/* + * S_ETC field values + */ +#define S_SCT 0x0100 /* section names */ +#define S_LIN 0x0200 /* hll source line item */ +#define S_FIL 0x0300 /* hll source file item */ +#define S_MOD 0x0400 /* ass source file item */ +#define S_COM 0x1000 /* Common name. */ +#define S_STB 0xe000 /* entries with any of these bits set are + reserved for debuggers + */ + +/* + * structure format strings + */ +#define SF_HEAD "22222244" +#define SF_SECT "44444" +#define SF_RELO "1124" +#define SF_NAME "4224" + +/* + * structure sizes (bytes in file; add digits in SF_*) + */ +#define SZ_HEAD 20 +#define SZ_SECT 20 +#define SZ_RELO 8 +#define SZ_NAME 12 + +/* + * file access macros + */ +#define BADMAGIC(x) ((x).oh_magic!=O_MAGIC) +#define OFF_SECT(x) SZ_HEAD +#define OFF_EMIT(x) (OFF_SECT(x) + ((long)(x).oh_nsect * SZ_SECT)) +#define OFF_RELO(x) (OFF_EMIT(x) + (x).oh_nemit) +#define OFF_NAME(x) (OFF_RELO(x) + ((long)(x).oh_nrelo * SZ_RELO)) +#define OFF_CHAR(x) (OFF_NAME(x) + ((long)(x).oh_nname * SZ_NAME)) + +#endif /* __OUT_H_INCLUDED */ diff --git a/commands/awk/Makefile b/commands/awk/Makefile new file mode 100755 index 000000000..051c5adf1 --- /dev/null +++ b/commands/awk/Makefile @@ -0,0 +1,29 @@ +# Makefile for awk. + +CFLAGS = -D_MINIX -D_POSIX_SOURCE -wo -w +LDFLAGS = -i -f + +OBJS = m.o e.o n.o l.o r.o v.o y.o regexp.o k.o + +all: awk + +awk: $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) #-lm + install -S 32kw $@ + +install: /usr/bin/awk + +/usr/bin/awk: awk + install -cs -o bin $? $@ + +clean: + rm -f awk *.o a.out *.bak core + +e.o: awk.h regexp.h +l.o: awk.h +m.o: awk.h +n.o: awk.h +r.o: awk.h regexp.h +regexp.o: regexp.h +v.o: awk.h regexp.h +y.o: awk.h diff --git a/commands/awk/NOTE b/commands/awk/NOTE new file mode 100755 index 000000000..881c2d731 --- /dev/null +++ b/commands/awk/NOTE @@ -0,0 +1,38 @@ +a small AWK clone + + ‚±‚̃vƒƒOƒ‰ƒ€‚ÍAMinix 1.2 —p‚É‘‚¢‚½‚à‚Ì‚Å‚·B•‚“®¬”“_‰‰ŽZ‚ðŽg‚¢ +‚Ü‚·‚©‚çAMinix 1.5 ˆÈ‘O‚Ì ACK-C ‚ł̓Rƒ“ƒpƒCƒ‹‚Å‚«‚Ü‚¹‚ñB‚»‚Ì‚½‚ß makefile +‚Å‚ÍAPeter S. Housel‚³‚ñ‚Ì•‚“®¬”“_ƒpƒbƒP[ƒW‚ðŽg‚¤‚悤‚É‚È‚Á‚Ä‚¢‚Ü‚·B +CFLAGS ‚Ì -f ‚ªA‚»‚ÌŽwŽ¦‚Å‚·B‚±‚Ì•‚“®¬”“_ƒpƒbƒP[ƒW‚ÍA”ñí‚É‚æ‚­‚Å‚«‚Ä +‚¢‚Ü‚·‚ªAatan(x, y) ‚̈ø‚«”‚̇˜‚ª‹t‚É‚È‚Á‚Ä‚¢‚Ü‚·‚Ì‚ÅAC³‚µ‚Ä‚¨‚¢‚Ä‚­ +‚¾‚³‚¢B‘¼‚É‚à‰½‚©‚ ‚Á‚½‚悤‚È‹L‰¯‚ª‚ ‚è‚Ü‚·‚ªA–Y‚ê‚Ä‚µ‚Ü‚¢‚Ü‚µ‚½B‚à‚µA‰½ +‚©–â‘肪‚ ‚é‚悤‚Å‚µ‚½‚çA‚²˜A—‚¢‚½‚¾‚¯‚ê‚ÎAŽ„‚ªŽ©•ª‚ÅŽg‚Á‚Ä‚¢‚é‚à‚Ì‚ðŒö•\ +‚·‚é‚悤‚É‚µ‚Ü‚·B + + ‚±‚̃vƒƒOƒ‰ƒ€‚ÍAShift-JIS ‚ÌŠ¿ŽšƒR[ƒh‚ðˆµ‚¤‚悤‚É‚È‚Á‚Ä‚¢‚Ü‚·B + +EUC ‚ɑΉž‚³‚¹‚éꇂÍA³‹K•\Œ»‚̈µ‚¢“™AŠ¿ŽšƒR[ƒh‚ÉŠÖŒW‚·‚é•”•ª‚Ì‘Š·‚ª•K +—v‚Å‚·‚ªA‘債‚½ŽèŠÔ‚Å‚Í‚ ‚è‚Ü‚¹‚ñB‚Ç‚È‚½‚©•ÏX‚³‚ꂽꇂÍAƒpƒbƒ`‚ð—pˆÓ‚µ +‚Ä‚¢‚½‚¾‚¯‚é‚ÆŠð‚µ‚¢‚Å‚·B + + “–‰‚ÍAÌ‚Ì awk ‚ÌŽd—l‚É‚È‚Á‚Ä‚¢‚Ü‚µ‚½‚ªAuAWK Programming Languagev +‚ªo”Å‚³‚êAPOSIX 1003.2 ‚Ì draft ‚Å‚à nawk Žd—l‚ªÌ—p‚³‚ꂽ‚Ì‚ð‹@‰ï‚ÉAnawk +‚ÌŽd—l‚É‘‚«Š·‚¦‚Ü‚µ‚½BƒIƒŠƒWƒiƒ‹‚Ì awk ‚ƈá‚Á‚ÄAparser ‚à lexical analizer +‚à hand maid ‚Å‚·‚©‚çA64K+64K ‚̃ƒ‚ƒŠ‚ÉŽû‚Ü‚Á‚Ä‚¢‚Ü‚·B + + –{—ˆ‚ÍAMinix ‚炵‚¢A‹³Þ“I‚ȃvƒƒOƒ‰ƒ€‚É‚·‚ׂ«‚Å‚·‚µA‚»‚¤‚µ‚½‚©‚Á +‚½‚Ì‚Å‚·‚ªA‚à‚Æ‚à‚ÆAŽ©•ª‚½‚¿‚ªŽg‚¤‚½‚ß‚ÉAƒƒ‚‚à‚Ƃ炸‚ɃXƒNƒŠ[ƒ“‚ðŒ©‚È‚ª +‚ç‘‚¢‚Ä‚µ‚Ü‚Á‚½‚½‚ßA‚«‚ê‚¢‚ȃR[ƒfƒBƒ“ƒO‚É‚È‚Á‚Ä‚¢‚Ü‚¹‚ñB‚µ‚©‚µA64K+64K +‚̃ƒ‚ƒŠ‚ÅŽg‚¦‚é awk ‚ª‘¼‚É‚ ‚è‚Ü‚¹‚ñ‚Ì‚ÅA‚Æ‚è‚ ‚¦‚¸Œ»s‚Ìó‘Ô‚ÅŒöŠJ‚µ‚Ä‚¨‚­ +‚±‚Æ‚É‚µ‚Ü‚µ‚½B + + ƒvƒƒOƒ‰ƒ€‚»‚Ì‚à‚Ì‚ÍA³‹K•\Œ»‚Ì•]‰¿‡˜‚Ɉˑ¶‚·‚é‚à‚̂𜂢‚ÄAuAWK +Programming Languagev‚ÉŒfÚ‚³‚ê‚Ä‚¢‚éƒeƒXƒgƒvƒƒOƒ‰ƒ€‚ɒʂ邱‚Æ‚ðŠm”F‚µ‚Ä‚  +‚è‚Ü‚·B + + + 32-bit Minix ‚ÌꇂÍAAT&T ƒIƒWƒiƒ‹‚Ì awk ‚Æ‚© gawk, mawk ‚Æ‚¢‚Á‚½ +ƒvƒƒOƒ‰ƒ€‚ªŽg‚¦‚Ü‚·‚ªA•‚“®¬”“_‚ðƒTƒ|[ƒg‚µ‚½Ac386 ‚Å‚±‚̃vƒƒOƒ‰ƒ€‚ðƒR +ƒ“ƒpƒCƒ‹‚·‚邱‚Æ‚à‚Å‚«‚Ü‚·B + +•½—Ñ _ˆê (kh@hiraide.mogami-wire.co.jp) 1995-01-29 diff --git a/commands/awk/README b/commands/awk/README new file mode 100755 index 000000000..61fdb318f --- /dev/null +++ b/commands/awk/README @@ -0,0 +1,13 @@ +A small AWK clone for the 16-bit Minix + + We have written this program for the Minix 1.2 to fit into 64K+64K +memory. When compiling this program, you need the Peter S. Housel's +Floating Math Package with corrected the atan() function. Original atan() +function has incorrect argument sequence. + + This program supports Japanese Shift-JIS KANJI code and passes +all the test program of the "AWK programming language" written by original +AWK authors except some programs relied upon the way of regular expression +evaluation. + +Kouichi Hirabayashi (kh@mogami-wire.co.jp) diff --git a/commands/awk/awk.h b/commands/awk/awk.h new file mode 100755 index 000000000..b997e1608 --- /dev/null +++ b/commands/awk/awk.h @@ -0,0 +1,186 @@ +/* + * a small awk clone + * + * (C) 1989 Saeko Hirabauashi & Kouichi Hirabayashi + * + * Absolutely no warranty. Use this software with your own risk. + * + * Permission to use, copy, modify and distribute this software for any + * purpose and without fee is hereby granted, provided that the above + * copyright and disclaimer notice. + * + * This program was written to fit into 64K+64K memory of the Minix 1.2. + */ + +/* lexical/parser tokens and executable statements */ + +#define FIRSTP 256 +#define ARG 256 +#define ARITH 257 +#define ARRAY 258 +#define ASSIGN 259 +#define CALL 260 +#define CAT 261 +#define COND 262 +#define DELETE 263 +#define DO 264 +#define ELEMENT 265 +#define FIELD 266 +#define FOR 267 +#define FORIN 268 +#define GETLINE 269 +#define IF 270 +#define IN 271 +#define JUMP 272 +#define MATHFUN 273 +#define NULPROC 274 +#define P1STAT 275 +#define P2STAT 276 +#define PRINT 277 +#define PRINT0 278 +#define STRFUN 279 +#define SUBST 280 +#define USRFUN 281 +#define WHILE 282 +#define LASTP 282 + /* lexical token */ + +#define ADD 300 /* + */ +#define ADDEQ 301 /* += */ +#define AND 302 /* && */ +#define BEGIN 303 /* BEGIN */ +#define BINAND 304 /* & */ +#define BINOR 305 /* | */ +#define BREAK 306 /* break */ +#define CLOSE 307 /* close */ +#define CONTIN 308 /* continue */ +#define DEC 309 /* -- */ +#define DIV 310 /* / */ +#define DIVEQ 311 /* /= */ +#define ELSE 312 /* else */ +#define END 313 /* END */ +#define EOL 314 /* ; or '\n' */ +#define EQ 315 /* == */ +#define EXIT 316 /* exit */ +#define FUNC 317 /* function */ +#define GE 318 /* >= */ +#define GT 319 /* > */ +#define IDENT 320 /* identifier */ +#define INC 321 /* ++ */ +#define LE 322 /* <= */ +#define LT 323 /* < */ +#define MATCH 324 /* ~ */ +#define MOD 325 /* % */ +#define MODEQ 326 /* %= */ +#define MULT 327 /* * */ +#define MULTEQ 328 /* *= */ +#define NE 329 /* != */ +#define NEXT 330 /* next */ +#define NOMATCH 331 /* !~ */ +#define NOT 332 /* ! */ +#define NUMBER 333 /* integer or floating number */ +#define OR 334 /* || */ +#define POWEQ 335 /* ^= */ +#define POWER 336 /* ^ */ +#define PRINTF 337 /* printf */ +#define REGEXP 338 /* /REG/ */ +#define RETURN 339 /* return */ +#define SHIFTL 340 /* << */ +#define SHIFTR 341 /* >> */ +#define SPRINT 342 /* sprint */ +#define SPRINTF 343 /* sprintf */ +#define STRING 344 /* ".." */ +#define SUB 345 /* - */ +#define SUBEQ 346 /* -= */ +#define SYSTEM 347 /* system */ +#define UMINUS 348 /* - */ + +/* tokens in parser */ + +#define VALUE 400 /* value node */ +#define INCDEC 401 /* ++, -- */ +#define PRE 402 /* pre incre/decre */ +#define POST 403 /* post incre/decre */ + +/* redirect in print(f) statement */ + +#define R_OUT 410 /* > */ +#define R_APD 411 /* >> */ +#define R_PIPE 412 /* | */ +#define R_IN 413 /* < */ +#define R_PIN 414 /* | getline */ +#define R_POUT 415 /* print | */ + +/* function */ + +#define ATAN2 500 /* atan2 */ +#define COS 501 /* cos */ +#define EXP 502 /* exp */ +#define INDEX 503 /* index */ +#define INT 504 /* int */ +#define LENGTH 505 /* length */ +#define LOG 506 /* log */ +#define RAND 507 /* rand */ +#define RGSUB 508 /* gsub */ +#define RMATCH 509 /* match */ +#define RSUB 510 /* sub */ +#define SIN 511 /* sin */ +#define SPLIT 512 /* split */ +#define SQRT 513 /* sqrt */ +#define SRAND 514 /* srand */ +#define SUBSTR 515 /* substr */ + +/* print(f) options */ + +#define FORMAT 1024 /* PRINTF, SPRINTF */ +#define STROUT 2048 /* SPRINTF */ +#define PRMASK 0x3ff /* ~(FORMAT|STROUT) */ + + /* node - used in parsed tree */ + +struct node { + int n_type; /* node type */ + struct node *n_next; /* pointer to next node */ + struct node *n_arg[1]; /* argument (variable length) */ +}; + +typedef struct node NODE; + + /* object cell */ + +struct cell { + int c_type; /* cell type */ + char *c_sval; /* string value */ + double c_fval; /* floating value */ +}; + +typedef struct cell CELL; + + /* cell type */ + +#define UDF 0 /* pass parameter */ +#define VAR 1 /* variable */ +#define NUM 2 /* number */ +#define ARR 4 /* array */ +#define STR 8 /* string */ +#define REC 16 /* record */ +#define FLD 32 /* filed */ +#define PAT 64 /* pattern (compiled REGEXPR) */ +#define BRK 128 /* break */ +#define CNT 256 /* continue */ +#define NXT 512 /* next */ +#define EXT 1024 /* exit */ +#define RTN 2048 /* return */ +#define TMP 4096 /* temp cell */ +#define POS 8192 /* argument position */ +#define FUN 16384 /* function */ + + /* symbol cell - linked to symbol table */ + +struct symbol { + char *s_name; + CELL *s_val; + struct symbol *s_next; +}; + +typedef struct symbol SYMBOL; diff --git a/commands/awk/e.c b/commands/awk/e.c new file mode 100755 index 000000000..3b6be1374 --- /dev/null +++ b/commands/awk/e.c @@ -0,0 +1,952 @@ +/* + * a small awk clone + * + * (C) 1989 Saeko Hirabauashi & Kouichi Hirabayashi + * + * Absolutely no warranty. Use this software with your own risk. + * + * Permission to use, copy, modify and distribute this software for any + * purpose and without fee is hereby granted, provided that the above + * copyright and disclaimer notice. + * + * This program was written to fit into 64K+64K memory of the Minix 1.2. + */ + + +#include +#include +#include "awk.h" +#include "regexp.h" + +extern char **FS, **OFS, **ORS, **OFMT; +extern double *RSTART, *RLENGTH; +extern char record[]; +extern CELL *field[]; + +extern int r_start, r_length; + +double getfval(), atof(); +char *strsave(), *getsval(), *strcat(), *strstr(); +CELL *mkcell(), *mktmp(); +CELL *Field(), *Split(), *Forin(); +CELL *Arith(), *Assign(), *Stat(), *Mathfun(), *Strfun(), *Cond(); +CELL *Print(), *Cat(), *Array(), *Element(); +CELL *If(), *While(), *For(), *Do(), *Jump(); +CELL *P1stat(), *P2stat(), *Print0(); +CELL *Arg(), *Call(), *Ret(); +CELL *Subst(), *In(), *Getline(), *Delete(), *Close(); +CELL *Nulproc(), *Usrfun(); +CELL *_Arg(); + +FILE *getfp(); /* r.c */ + +CELL truecell = { NUM, NULL, 1.0 }; +CELL falsecell = { NUM, NULL, 0.0 }; +static CELL breakcell = { BRK, NULL, 0.0 }; +static CELL contcell = { CNT, NULL, 0.0 }; +static CELL nextcell = { NXT, NULL, 0.0 }; +static CELL retcell = { RTN, NULL, 0.0 }; + +static CELL *retval; /* function return value */ + +int pateval; /* used in P1STAT & P2STAT */ +static char *r_str; /* STR in 'str ~ STR */ +static regexp *r_pat; /* compiled pattern for STR */ + +CELL *(*proctab[])() = { + Arg, Arith, Array, Assign, Call, Cat, Cond, Delete, Do, Element, + Field, For, Forin, Getline, If, In, Jump, Mathfun, Nulproc, P1stat, + P2stat, Print, Print0, Strfun, Subst, Usrfun, While +}; + +CELL * +execute(p) NODE *p; +{ + int type, i; + CELL *r, *(*proc)(); + + type = p->n_type; + if (type == VALUE) { + if ((r = (CELL *) p->n_arg[0])->c_type & PAT && pateval) { + i = match(r->c_sval, (char *)record) ? 1 : 0; + r = mktmp(NUM, NULL, (double) i); + } + return r; + } + for ( ; p != NULL; p = p->n_next) { +#if 0 + if (p->n_type == VALUE) continue; /* neglect */ +#endif +/* + switch ((int) p->n_type) { + case ARRAY: + r = Array(p); + break; + case ARITH: + r = Arith(p); + break; + case ASSIGN: + r = Assign(p); + break; + case PRINT: + r = Print(p); + break; + case PRINT0: + r = Print0(p); + break; + case CAT: + r = Cat(p); + break; + case MATHFUN: + r = Mathfun(p); + break; + case STRFUN: + r = Strfun(p); + break; + case COND: + r = Cond(p); + break; + case IF: + r = If(p); + break; + case P1STAT: + r = P1stat(p); + break; + case P2STAT: + r = P2stat(p); + break; + case WHILE: + r = While(p); + break; + case DO: + r = Do(p); + break; + case FOR: + r = For(p); + break; + case FORIN: + r = Forin(p); + break; + case FIELD: + r = Field(p); + break; + case JUMP: + r = Jump(p); + break; + case ARG: + r = Arg(p); + break; + case CALL: + r = Call(p); + break; + case SUBST: + r = Subst(p); + break; + case ELEMENT: + r = Element(p); + break; + case IN: + r = In(p); + break; + case GETLINE: + r = Getline(p); + break; + case DELETE: + r = Delete(p); + break; + case NULPROC: + r = &truecell; + break; + default: + printf("PROGRAM ERROR ? ILLEGAL NODE TYPE(%d)\n", type); + exit(1); + break; + } +*/ + i = (int) p->n_type; + if (i < FIRSTP || i > LASTP) + error("ILLEGAL PROC (%d)", i); + proc = proctab[i - FIRSTP]; + r = (*proc)(p); + if (r->c_type & (BRK|CNT|NXT|RTN)) + return r; + if (p->n_next != NULL) + c_free(r); +#ifdef DOS + kbhit(); /* needs in MS-DOS */ +#endif + } + return r; +} + +static CELL * +Arith(p) NODE *p; +{ + int op; + CELL *r, *u, *v, *execute(); + double x, y, fmod(), pow(); + + op = (int) p->n_arg[0]; + if (op == UMINUS) { + u = execute(p->n_arg[1]); + x = - getfval(u); + } + else if (op == INCDEC) { + u = execute(p->n_arg[1]); + x = getfval(u); + setfval(u, x + (int) p->n_arg[2]); + if ((int) p->n_arg[3] == PRE) + return u; + /* return dummy */ + } + else { + u = execute(p->n_arg[1]); + v = execute(p->n_arg[2]); + x = getfval(u); + y = getfval(v); + if (op == DIV || op == MOD) { + if (y == 0.0) + fprintf(stderr, "divid by 0\n"); + } + switch (op) { + case SUB: x -= y;break; + case ADD: x += y; break; + case MULT: x *= y; break; + case DIV: + if (y == 0.0) + error("division by zero in \"/\"", (char *)0); + x /= y; break; + case MOD: + if (y == 0.0) + error("division by zero in \"%%\"", (char *)0); + x = fmod(x, y); break; + case POWER: x = pow(x, y); break; + default: printf("UNSUPPORTED ARITH OPERATOR !\n"); break; + } + c_free(v); + } + c_free(u); + r = mktmp(NUM, NULL, x); + return r; +} + +static CELL * +Assign(p) NODE *p; +{ + CELL *u, *v, *execute(); + int op; + double x, y, fmod(), pow(); + + op = (int) p->n_arg[0]; + u = execute(p->n_arg[1]); + +#if 0 + if (u->c_type == UDF) /* fix up local var */ + u->c_type |= VAR|STR; +#endif + if (!(u->c_type & (VAR|FLD|REC)) && (u->c_type != UDF)) + fprintf(stderr, "ASSIGN TO NON VARIABLE (%d)\n", u->c_type); + v = execute(p->n_arg[2]); + + if (u == v) + goto rtn; /* same node */ + + if (op == ASSIGN) { + if (v->c_type & NUM/* || isnum(v->c_sval)*/) + setfval(u, getfval(v)); + else + setsval(u, getsval(v)); + } + else { + x = getfval(u); + y = getfval(v); + switch (op) { + case ADDEQ: x += y; break; + case SUBEQ: x -= y; break; + case MULTEQ: x *= y; break; + case DIVEQ: + if (y == 0.0) + error("division by zero in \"/=\"", (char *)0); + x /= y; break; + case MODEQ: + if (y == 0.0) + error("division by zero in \"%=\"", (char *)0); + x = fmod(x, y); break; + case POWEQ: x = pow(x, y); break; + default: + synerr("illegal assign op (%d)", op); + break; + } + setfval(u, x); + } +rtn: + c_free(v); + return u; +} + +static CELL * +Cat(p) NODE *p; +{ + CELL *u; + char *s, *t, str[BUFSIZ]; + + u = execute(p->n_arg[0]); + s = getsval(u); + for (t = str; *s; ) + *t++ = *s++; + c_free(u); + u = execute(p->n_arg[1]); + s = getsval(u); + while (*s) + *t++ = *s++; + c_free(u); + *t = '\0'; + return mktmp(STR, str, 0.0); +} + +static CELL * +Print(p) NODE *p; +{ + register int i, redir, typ; + CELL *u; + char *s, str[BUFSIZ]; + char *file; + FILE *fp; + + redir = (int) p->n_arg[0]; + if (typ = redir & PRMASK) { /* redirect */ + u = execute(p->n_arg[1]); + file = getsval(u); + if (typ == R_PIPE) + typ = R_POUT; + fp = getfp(file, typ); + c_free(u); + } + else + fp = stdout; + if (redir & FORMAT) /* format */ + format(str, p); + else { + *str = '\0'; + for (i = 2; p->n_arg[i] != NULL; i++) { + if (i > 2) + strcat(str, *OFS); + u = execute(p->n_arg[i]); + s = getsval(u); + strcat(str, s); + c_free(u); + } + strcat(str, *ORS); + } + if (redir & STROUT) /* sprintf */ + return mktmp(STR, str, 0.0); + fputs(str, fp); + fflush(fp); + return &truecell; +} + +static CELL * +Mathfun(p) NODE *p; +{ + CELL *u, *v; + double x, y; + double atan2(), cos(), exp(), log(), sin(), sqrt(), modf(); + + if ((int) p->n_arg[1] == 0) { + u = NULL; + x = 0.0; + } + else { + u = execute(p->n_arg[2]); + x = getfval(u); + } + switch ((int) p->n_arg[0]) { + case ATAN2: + if ((int) p->n_arg[1] == 2) { + v = execute(p->n_arg[3]); + y = getfval(v); + x = atan2(x, y); + c_free(v); + } + else + x = 0.0; + break; + case COS: x = cos(x); break; + case EXP: x = exp(x); break; + case INT: y = modf(x, &x); break; + case LOG: x = log(x); break; + case SIN: x = sin(x); break; + case SQRT: x = sqrt(x); break; + case RAND: x = (double) rand() / 32768.0; break; + case SRAND: if (x == 0.0) + x = (double) time(0); + x = (double) srand((int) x); + break; + default: + fprintf(stderr, "unknown math function (%d)\n", p->n_arg[2]); + break; + } + if (u != NULL) + c_free(u); + return mktmp(NUM, NULL, x); +} + +static CELL * +Strfun(p) NODE *p; +{ + CELL *u, *v, *r; + char *s, *t, str[BUFSIZ]; + int i, m, n; + double x; + regexp *pat, *getpat(); + + n = (int) p->n_arg[1]; + if (n > 0 && (int) p->n_arg[0] != SPLIT) { + u = execute(p->n_arg[2]); + s = getsval(u); + } + else { + s = ""; + u = NULL; + } + switch ((int) p->n_arg[0]) { + case INDEX: + if (n > 1) { + v = execute(p->n_arg[3]); + t = getsval(v); + i = Index(s, t); + c_free(v); + } + else + i = 0; + r = mktmp(NUM, NULL, (double) i); + break; + case LENGTH: + i = (n > 0) ? jstrlen(s) : jstrlen(record); + r = mktmp(NUM, NULL, (double) i); + break; + case SPLIT: + r = Split(p); + break; + case SUBSTR: + if (n > 1) { + v = execute(p->n_arg[3]); + m = (int) getfval(v) - 1; + c_free(v); + } + else + m = 0; + if (n > 2) { + v = execute(p->n_arg[4]); + n = (int) getfval(v); + c_free(v); + } + else + n = jstrlen(s) - m; + for (t = str; *s && m-- > 0; s++) + if (isKanji(*s)) + s++; + while (*s && n-- > 0) { + if (isKanji(*s)) + *t++ = *s++; + *t++ = *s++; + } + *t = '\0'; + r = mktmp(STR, str, 0.0); + break; + case RMATCH: + if (n > 1) { + v = execute(p->n_arg[3]); + pat = getpat(v); + match(pat, s); + c_free(v); + if (r_start) { /* change only if match */ + *RSTART = (double) r_start; + *RLENGTH = (double) r_length; + } + r = mktmp(NUM, NULL, (double) r_start); + } + else + error("missing regexpr in match(str, regexpr)"); + break; + case CLOSE: + r = Close(s); + break; + case SYSTEM: + r = mktmp(NUM, NULL, system(s) == -1 ? 0.0 : 1.0); + break; + default: + fprintf(stderr, "unknown string function"); + break; + } + c_free(u); + return r; +} + +static regexp * +getpat(r) CELL *r; +{ + regexp *pat, *mkpat(); + + if (r->c_type & PAT) + pat = (regexp *) r->c_sval; + else { + if (r_str && strcmp(r_str, r->c_sval) == 0) + pat = r_pat; + else { + sfree(r_str); sfree(r_pat); + r_str = strsave(getsval(r)); + pat = r_pat = mkpat(r_str); + } + } + return pat; +} + +static CELL * +Subst(p) NODE *p; +{ + CELL *u, *v, *w; + char *s, *t, *r, str[BUFSIZ], *strcpy(); + int i, n; + + n = (int) p->n_arg[1]; + if (n > 1) { + u = execute(p->n_arg[3]); /* substitute string */ + s = getsval(u); + v = execute(p->n_arg[2]); /* expr */ + if (n > 2) { + w = execute(p->n_arg[4]); + t = getsval(w); + r = str; + } + else { + t = r = record; + w = NULL; + } + i = (int) p->n_arg[0] == RGSUB ? 0 : 1; + if (v->c_type & (PAT|STR)) + i = Sub(r, v->c_sval, (v->c_type & STR), s, t, i); + else + error("[g]sub(PAT, .. ) must be /../ or string (%d)", + w->c_type); + if (n > 2) { + if (w->c_type & REC) { + strcpy(record, str); + mkfld(record, *FS, field); + } + else + setsval(w, str); + } + else + mkfld(record, *FS, field); + c_free(u); + c_free(v); + c_free(w); + } + else + i = 0; + return mktmp(NUM, NULL, (double) i); +} + +static CELL * +Cond(p) NODE *p; +{ + CELL *u, *v; + double x, y; + int op, i, j; + char *s; + int save = pateval; + + op = (int) p->n_arg[0]; + u = execute(p->n_arg[1]); + x = getfval(u); +/* +printf("Cond(%d)(%s)\n", u->c_type, u->c_sval); +*/ + if (op == AND || op == OR || op == NOT) { + if (u->c_type & NUM) + i = (x != 0.0); + else { + s = getsval(u); + i = (s != (char *)NULL) && (*s != '\0'); + } + } + if (op == AND && !i) { + c_free(u); + return &falsecell; + } + if (op == OR && i) { + c_free(u); + return &truecell; + } + if (op == NOT) + i = i == 0 ? 1 : 0; + else { + if (op == MATCH || op == NOMATCH) + pateval = 0; + v = execute(p->n_arg[2]); + y = getfval(v); + if (op == AND || op == OR || op == BINAND || op == BINOR) { + if (v->c_type & NUM) + j = (y != 0.0); + else { + s = getsval(v); + j = (s != (char *)NULL) && (*s != '\0'); + } + switch (op) { + case AND: i = i && j; break; + case OR: i = i || j; break; + case BINAND: i = i & j; break; + case BINOR: i = i | j; break; + } + } + else if (op == MATCH || op == NOMATCH) { + char *s; + regexp *pat, *getpat(); + + s = getsval(u); + pat = getpat(v); + i = match(pat, s) == 0 ? 0 : 1; + if (op == NOMATCH) + i = i == 0 ? 1 : 0; + } + else { /* relative operator */ +/* +printf("Cond(%d)(%d)(%s)(%s)\n", u->c_type, v->c_type, u->c_sval, v->c_sval); +*/ + if ((u->c_type & NUM) && (v->c_type & NUM)) + i = x < y ? -1 : (x > y ? 1 : 0); + else + i = strcmp(getsval(u), getsval(v)); +/* +printf("Cond(%d)(%d)(%g)(%g)(%d)\n", u->c_type, v->c_type, x, y, i); +*/ + + switch (op) { + case LT: i = i < 0 ? 1 : 0; break; + case LE: i = i <= 0 ? 1 : 0; break; + case EQ: i = i == 0 ? 1 : 0; break; + case NE: i = i != 0 ? 1 : 0; break; + case GT: i = i > 0 ? 1 : 0; break; + case GE: i = i >= 0 ? 1 : 0; break; + default: + fprintf(stderr, "unknown relative operator (%d)\n", op); + break; + } + } + c_free(v); + } + c_free(u); + pateval = save; + return mktmp(NUM, NULL, (double) i); +} + +static CELL * +If(p) NODE *p; +{ + CELL *u; + int i; + char *s; + + u = execute(p->n_arg[0]); + if (u->c_type & NUM) + i = (getfval(u) != 0.0); + else { + s = getsval(u); + i = (s != (char *)NULL) && (*s != '\0'); + } + c_free(u); + if (i) + u = execute(p->n_arg[1]); + else if (p->n_arg[2]) + u = execute(p->n_arg[2]); + else + u = &truecell; + return u; +} + +static CELL * +While(p) NODE *p; +{ + CELL *u; + double x; + + for (;;) { + u = execute(p->n_arg[0]); + x = getfval(u); + if (x == 0.0) + break; + c_free(u); + u = execute(p->n_arg[1]); + switch (u->c_type) { + case BRK: + goto rtn; + case NXT: case EXT: case RTN: + return u; + } + c_free(u); + } +rtn: + c_free(u); + return &truecell; +} + +static CELL * +Do(p) NODE *p; +{ + CELL *u; + double x; + + for (;;) { + u = execute(p->n_arg[0]); + switch (u->c_type) { + case BRK: + goto rtn; + case NXT: case EXT: case RTN: + return u; + } + c_free(u); + u = execute(p->n_arg[1]); + if(getfval(u) == 0.0) + break; + c_free(u); + } +rtn: + c_free(u); + return &truecell; +} + +static CELL * +For(p) NODE *p; +{ + CELL *u; + double x; + + if (p->n_arg[0] != NULL) { + u = execute(p->n_arg[0]); + c_free(u); + } + for (;;) { + if (p->n_arg[1] != NULL) { + u = execute(p->n_arg[1]); + x = getfval(u); + c_free(u); + if (x == 0.0) + break; + } + u = execute(p->n_arg[3]); + switch (u->c_type) { + case BRK: + c_free(u); + goto rtn; + case NXT: case EXT: case RTN: + return u; + } + if (p->n_arg[2] != NULL) { + u = execute(p->n_arg[2]); + c_free(u); + } + } +rtn: + return &truecell; +} + +static CELL * +Jump(p) NODE *p; +{ + CELL *u; + int i; + + switch ((int) p->n_arg[0]) { + case BREAK: u = &breakcell; break; + case CONTIN: u = &contcell; break; + case EXIT: + if ((int) p->n_arg[1]) { + u = execute(p->n_arg[1]); + i = (int) getfval(u); + } + else + i = 0; + closeall(); + exit(i); + case RETURN: + Return(p); + u = &retcell; + break; + case NEXT: u = &nextcell; break; + } + return u; +} + +static +Return(p) NODE *p; +{ + CELL *u; + int i; + char *s, str[BUFSIZ]; + + c_free(retval); + if (p->n_arg[1] != NULL) { + if (p->n_arg[2] == NULL) { +/* +if (0) { +*/ + u = execute(p->n_arg[1]); + if (u->c_type == UDF) + retval = mktmp(STR, "", 0.0); + else + retval = mktmp(u->c_type, u->c_sval, u->c_fval); + c_free(u); + } + else { + for (i = 1; p->n_arg[i] != NULL; i++) { + if (i == 1) + *str = '\0'; + else + strcat(str, *OFS); + u = execute(p->n_arg[i]); + s = getsval(u); + strcat(str, s); + c_free(u); + } +/* +printf("Ret(%s)(%d)\n", str, isnum(str)); +*/ + if (isnum(str)) + retval = mktmp(STR|NUM, str, atof(str)); + else + retval = mktmp(STR, str, 0.0); + } + } + else + retval = &truecell; +} + +#define MAXFRAME 100 +CELL **frame[MAXFRAME]; +static int framep; + +static CELL * +Arg(p) NODE *p; +{ + CELL *u; + int i; + + u = (CELL *)p->n_arg[0]; + return _Arg((int)u->c_fval); +} + +CELL * +_Arg(i) +{ +/* +printf("Arg(%d)\n", i); +*/ + return frame[framep - 1][i]; +} + +static CELL * +Call(p) NODE *p; +{ + CELL *u, *v, *r, **arg; + NODE *q; + int i, j, k, n; + char *emalloc(); + + if (framep >= MAXFRAME - 2) + error("stack frame overflow", (char *)0); + retval = &truecell; + r = (CELL *) p->n_arg[0]; + if (r->c_type != FUN) + synerr("called function is not declared", (char *)0); + n = (int) r->c_fval; /* # of params */ + if (n > 0) { + arg = (CELL **) emalloc(sizeof(u) * n); + for (i = 2, j = 0, k = (int) p->n_arg[1]; j < k; i++) { + u = execute(p->n_arg[i]); +/* +printf("pass, j(%d)typ(%d)\n", j, u->c_type); +*/ + if (u->c_type & ARR) + v = u; /* pass by reference */ + else { /* pass by value */ + v = mkcell(UDF, u->c_sval, u->c_fval); + if (u->c_type != UDF) { +#if 0 + v->c_type = u->c_type; + if (v->c_type & (NUM|STR)) + v->c_type |= VAR; + v->c_type &= ~TMP; /* dont't free */ +#else + v->c_type |= (u->c_type & (NUM|STR))|VAR; + /*v->c_type &= ~TMP;*/ +#endif + /* Don't free original */ + } +/* +printf("pass1, j(%d)typ(%d)\n", j, v->c_type); +*/ + } + arg[j++] = v; + } + for ( ; j < n; ) /* local var */ + arg[j++] = mkcell(UDF, NULL, 0.0); + } + else + arg = NULL; + + frame[framep] = arg; + framep++; + + r = execute(r->c_sval); + c_free(r); + framep--; + if (n > 0) { + for (j = n - 1 ; j > k; j--) { /* local var */ + u = arg[j]; + if (u->c_type & ARR) + a_free(u); + else + c_free(u); + } + for ( ; j >= 0; j--) { + u = arg[j]; + if (!(u->c_type & ARR)) { +/* c_free(u);*/ + sfree(u->c_sval); + sfree(u); + } + else { + v = execute(p->n_arg[j + 2]); + if (v->c_type == UDF) { /* copy back */ +/* +printf("copy_back_UDF(%d)(%d)\n", j, u->c_type); +*/ + v->c_type = u->c_type; + sfree(v->c_sval); + v->c_sval = u->c_sval; + v->c_fval = u->c_fval; + sfree(u); + } + } + } + } + sfree(arg); +/* return retval;*/ + u = mktmp(retval->c_type, retval->c_sval, retval->c_fval); + return u; +} + +CELL *Nulproc() +{ + return &truecell; +} + +CELL * +Usrfun(p) NODE *p; +{ + CELL *u; + + u = execute(p); + return u; +} diff --git a/commands/awk/k.c b/commands/awk/k.c new file mode 100755 index 000000000..94f81e63d --- /dev/null +++ b/commands/awk/k.c @@ -0,0 +1,43 @@ +/* + * a small awk clone + * + * (C) 1989 Saeko Hirabauashi & Kouichi Hirabayashi + * + * Absolutely no warranty. Use this software with your own risk. + * + * Permission to use, copy, modify and distribute this software for any + * purpose and without fee is hereby granted, provided that the above + * copyright and disclaimer notice. + * + * This program was written to fit into 64K+64K memory of the Minix 1.2. + */ + + +#include + +isKanji(c) +{ + c &= 0xff; + return (c > 0x80 && c < 0xa0 || c > 0xdf && c < 0xfd); +} + +jstrlen(s) char *s; +{ + int i; + + for (i = 0; *s; i++, s++) + if (isKanji(*s)) + s++; + return i; +} + +char * +jStrchr(s, c) char *s; +{ + for ( ; *s; s++) + if (isKanji(*s)) + s++; + else if (*s == c) + return s; + return NULL; +} diff --git a/commands/awk/l.c b/commands/awk/l.c new file mode 100755 index 000000000..ffa5520b6 --- /dev/null +++ b/commands/awk/l.c @@ -0,0 +1,333 @@ +/* + * a small awk clone + * + * (C) 1989 Saeko Hirabauashi & Kouichi Hirabayashi + * + * Absolutely no warranty. Use this software with your own risk. + * + * Permission to use, copy, modify and distribute this software for any + * purpose and without fee is hereby granted, provided that the above + * copyright and disclaimer notice. + * + * This program was written to fit into 64K+64K memory of the Minix 1.2. + */ + + +#include +#include +#include "awk.h" + +extern char *srcprg; /* inline program */ +extern FILE *pfp; /* program file */ + +int sym; /* lexical token */ +int sym1; /* auxiliary lexical token */ +int regexflg; /* set by parser (y.c) to indicate parsing REGEXPR */ +int funflg; /* set by parser (y.c) to indicate parsing FUNCTION */ +int printflg; /* set by parser (y.c) to indicate parsing PRINT */ +int getlineflg; /* set by parser (y.c) to indicate parsing GETLINE */ +char text[BUFSIZ]; /* lexical word */ +char line[BUFSIZ]; /* program line for error message (ring buffer) */ +char *linep = line; /* line pointer */ +char funnam[128]; /* function name for error message */ +int lineno = 1; + +lex() +{ + int c, d; + char *s; + + if (regexflg) + return sym = scanreg(); +next: + while ((c = Getc()) == ' ' || c == '\t') + ; + while (c == '#') + for (c = Getc(); c != '\n'; c = Getc()) + ; + switch (c) { + case '\\': + if ((c = Getc()) == '\n') { + lineno++; + goto next; + } + break; + case '\n': + lineno++; + break; + } + switch (c) { + case EOF: return sym = 0; + case '+': return sym = follow2('=', '+', ADDEQ, INC, ADD); + case '-': return sym = follow2('=', '-', SUBEQ, DEC, SUB); + case '*': return sym = follow('=', MULTEQ, MULT); + case '/': return sym = follow('=', DIVEQ, DIV); + case '%': return sym = follow('=', MODEQ, MOD); + case '^': return sym = follow('=', POWEQ, POWER); + case '=': return sym = follow('=', EQ, ASSIGN); + case '!': return sym = follow2('=', '~', NE, NOMATCH, NOT); + case '&': return sym = follow('&', AND, BINAND); + case '|': sym = follow('|', OR, BINOR); + if (printflg && sym == BINOR) + sym = R_POUT; + return sym; + case '<': sym = follow2('=', '<', LE, SHIFTL, LT); + if (getlineflg && sym == LT) + sym = R_IN; + return sym; + case '>': sym = follow2('=', '>', GE, SHIFTR, GT); + if (printflg) { + switch (sym) { + case GT: sym = R_OUT; break; + case SHIFTR: sym = R_APD; break; + } + } + return sym; + case '~': return sym = MATCH; break; + case ';': case '\n': return sym = EOL; + } + if (isalpha(c) || c == '_') { + for (s = text; isalnum(c) || c == '_'; ) { + *s++ = c; c = Getc(); + } + Ungetc(c); + *s = '\0'; + if ((d = iskeywd(text)) == 0 && + (d = isbuiltin(text, &sym1)) == 0) { + if (c == '(') + return sym = CALL; + else if (funflg) { + if ((sym1 = isarg(text)) != -1) + return sym = ARG; + } + } + return sym = d ? d : IDENT; + } + else if (c == '.' || (isdigit(c))) { + Ungetc(c); + return sym = scannum(text); /* NUMBER */ + } + else if (c == '"') + return sym = scanstr(text); /* STRING */ + return sym = c; +} + +static +follow(c1, r1, r2) +{ + register int c; + + if ((c = Getc()) == c1) + return r1; + else { + Ungetc(c); + return r2; + } +} + +static +follow2(c1, c2, r1, r2, r3) +{ + register int c; + + if ((c = Getc()) == c1) + return r1; + else if (c == c2) + return r2; + else { + Ungetc(c); + return r3; + } +} + +static +iskeywd(s) char *s; +{ + static struct { char *kw; int token; } tab[] = { + "BEGIN", BEGIN, + "END", END, + "break", BREAK, + "continue", CONTIN, + "delete", DELETE, + "do", DO, + "else", ELSE, + "exit", EXIT, + "for", FOR, + "func", FUNC, + "function", FUNC, + "getline", GETLINE, + "if", IF, + "in", IN, + "next", NEXT, + "print", PRINT, + "printf", PRINTF, + "return", RETURN, + "sprint", SPRINT, + "sprintf", SPRINTF, + "while", WHILE, + "", 0, 0 + }; + register int i; + + for (i = 0; tab[i].token; i++) + if (strcmp(tab[i].kw, s) == 0) + break; + return tab[i].token; +} + +static +isbuiltin(s, p) char *s; int *p; +{ + static struct { char *kw; int type; int token; } tab[] = { + "atan2", MATHFUN, ATAN2, + "close", STRFUN, CLOSE, + "cos", MATHFUN, COS, + "exp", MATHFUN, EXP, + "gsub", SUBST, RGSUB, + "index", STRFUN, INDEX, + "int", MATHFUN, INT, + "length", STRFUN, LENGTH, + "log", MATHFUN, LOG, + "match", STRFUN, RMATCH, + "sin", MATHFUN, SIN, + "sqrt", MATHFUN, SQRT, + "rand", MATHFUN, RAND, + "srand", MATHFUN, SRAND, + "split", STRFUN, SPLIT, + "sub", SUBST, RSUB, + "substr", STRFUN, SUBSTR, + "system", STRFUN, SYSTEM, + "", 0, 0 + }; + register int i; + + for (i = 0; tab[i].token; i++) + if (strcmp(tab[i].kw, s) == 0) + break; + *p = tab[i].token; + return tab[i].type; +} + +static +scannum(s) char *s; +{ + register int c; + char *strchr(); + + if ((c = Getc()) && strchr("+-", c) != NULL) { + *s++ = c; c = Getc(); + } + while (isdigit(c)) { + *s++ = c; c = Getc(); + } + if (c == '.') { + *s++ = c; c = Getc(); + while (isdigit(c)) { + *s++ = c; c = Getc(); + } + } + if (c && strchr("eE", c) != NULL) { + *s++ = c; c = Getc(); + if (c && strchr("+-", c) != NULL) { + *s++ = c; c = Getc(); + } + while (isdigit(c)) { + *s++ = c; c = Getc(); + } + } + *s = '\0'; + Ungetc(c); + return NUMBER; +} + +static +scanstr(s) char *s; +{ + register int c, i, j; + + for (c = Getc(); c != EOF & c != '"'; ) { + if (c == '\\') { + switch (c = Getc()) { + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + default: + if (isdigit(c)) { + for (i = j = 0; i < 3 && isdigit(c); c = Getc(), i++) + j = j * 8 + c - '0'; + Ungetc(c); + c = j; + } + break; + } + } + *s++ = c; + if (isKanji(c)) + *s++ = Getc(); + c = Getc(); + } + *s = '\0'; + return STRING; +} + +static +scanreg() +{ + register int c; + register char *s; + + for (s = text; (c = Getc()) != '/'; ) + if (c == '\n') + error("newline in regular expression"); + else { + if (isKanji(c) || c == '\\') { + *s++ = c; c = Getc(); + } + *s++ = c; + } + *s = '\0'; + return REGEXP; +} + +static int c0; + +Ungetc(c) +{ + c0 = c; + + if (linep > line) { + if (--linep < line) + linep == line + BUFSIZ - 1; + } +} + +Getc() +{ + register int c; + char *s, *t; + + if (c0) { + c = c0; c0 = 0; + } + else if (srcprg) + c = *srcprg ? *srcprg++ : EOF; + else + c = fgetc(pfp); + +#if 0 + if (linep - line == BUFSIZ) { +printf("!!!\n"); + for (s = line; *s != '\n' && ((s - line) +#include +#include +#include "awk.h" + +extern char **FS, **FILENAME; +extern char record[]; +extern FILE *ifp; + +NODE *parse(); +CELL *execute(); +FILE *efopen(), *fopen(); +char *strsave(); + +int xargc; +char **xargv; +char *srcprg; +FILE *pfp; +char *cmd; +#if 0 +int iflg; /* interactive mode */ +#endif + +main(argc, argv) char **argv; +{ + char *s, *strpbrk(), *strchr(); + void onint(); + +#ifdef DOS + _sharg(&argc, &argv); +#endif + signal(SIGINT, onint); + signal(SIGFPE, onint); + cmd = argv[0]; + init(); + while (--argc > 0 && (*++argv)[0] == '-') + for (s = argv[0]+1; *s; s++) + if (strcmp(argv[0], "-") == 0) + break; + else + switch (*s) { +#if 0 + case 'i': + iflg++; + pfp = stdin; + interactive(); + /* no return */ +#endif + case 'F': + *FS = ++s; + break; + case 'f': + if (*(s+1)) + s++; + else { + argc--; s = *++argv; + } + pfp = efopen(s, "r"); + s += strlen(s) - 1; + break; + } + xargc = argc; xargv = argv; + if (pfp == NULL && xargc > 0) { + srcprg = *xargv++; xargc--; + } +/* + if (pfp == NULL && xargc > 0) { + if (strpbrk(xargv[0], " !$^()={}[];<>,/~") != NULL) { + sprintf(record, "%s\n", xargv[0]); + srcprg = strsave(record); + } + else { + sprintf(record, "%s.awk", xargv[0]); + if ((pfp = fopen(record, "r")) == NULL) + error("can't open %s", record); + } + xargc--; xargv++; + } +*/ + + while (*xargv != NULL && strchr(*xargv, '=') != NULL) { + setvar(*xargv++); + xargc--; + } + + initarg(cmd, xargc, xargv); + if (xargc == 0) { + ifp = stdin; *FILENAME = "-"; + } + parse(); + closeall(); + exit(0); +} + +FILE * +efopen(file, mode) char *file, *mode; +{ + FILE *fp, *fopen(); + + if ((fp = fopen(file, mode)) == NULL) + error("cannot open %s", file); + return fp; +} + +error(s, t) char *s, *t; +{ + extern double *NR; + + fprintf(stderr, "awk: "); + fprintf(stderr, s, t); + fprintf(stderr, "\n"); + if (NR != NULL) { + fprintf(stderr, "record number %g\n", *NR); + } +#ifdef DOS + closeall(); +#endif + exit(1); +} + +void +onint(i) +{ + closeall(); + exit(0x80 | i); +} diff --git a/commands/awk/n.c b/commands/awk/n.c new file mode 100755 index 000000000..489d3e5b6 --- /dev/null +++ b/commands/awk/n.c @@ -0,0 +1,149 @@ +/* + * a small awk clone + * + * (C) 1989 Saeko Hirabauashi & Kouichi Hirabayashi + * + * Absolutely no warranty. Use this software with your own risk. + * + * Permission to use, copy, modify and distribute this software for any + * purpose and without fee is hereby granted, provided that the above + * copyright and disclaimer notice. + * + * This program was written to fit into 64K+64K memory of the Minix 1.2. + */ + + +#include +#include "awk.h" + +NODE * +node0(type) +{ + NODE *p; + char *emalloc(); + + p = (NODE *) emalloc(sizeof(*p) - sizeof(p)); + p->n_type = type; + p->n_next = NULL; + return p; +} + +NODE * +node1(type, arg0) NODE *arg0; +{ + NODE *p; + char *emalloc(); + + p = (NODE *) emalloc(sizeof(*p)); + p->n_type = type; + p->n_next = NULL; + p->n_arg[0] = (NODE *) arg0; + return p; +} + +NODE * +node2(type, arg0, arg1) NODE *arg0, *arg1; +{ + NODE *p; + char *emalloc(); + + p = (NODE *) emalloc(sizeof(*p) + sizeof(p) * 1); + p->n_type = type; + p->n_next = NULL; + p->n_arg[0] = (NODE *) arg0; + p->n_arg[1] = (NODE *) arg1; + return p; +} + +NODE * +node3(type, arg0, arg1, arg2) NODE *arg0, *arg1, *arg2; +{ + NODE *p; + char *emalloc(); + + p = (NODE *) emalloc(sizeof(*p) + sizeof(p) * 2); + p->n_type = type; + p->n_next = NULL; + p->n_arg[0] = (NODE *) arg0; + p->n_arg[1] = (NODE *) arg1; + p->n_arg[2] = (NODE *) arg2; + return p; +} + +NODE * +node4(type, arg0, arg1, arg2, arg3) NODE *arg0, *arg1, *arg2, *arg3; +{ + NODE *p; + char *emalloc(); + + p = (NODE *) emalloc(sizeof(*p) + sizeof(p) * 3); + p->n_type = type; + p->n_next = NULL; + p->n_arg[0] = (NODE *) arg0; + p->n_arg[1] = (NODE *) arg1; + p->n_arg[2] = (NODE *) arg2; + p->n_arg[3] = (NODE *) arg3; + return p; +} + +CELL * +mkcell(type, sval, fval) char *sval; double fval; +{ + CELL *p; + char *emalloc(), *strsave(); + + p = (CELL *) emalloc(sizeof(*p)); + p->c_type = type; + if (sval == NULL) + p->c_sval = NULL; + else + p->c_sval = strsave(sval); + p->c_fval = fval; + return p; +} + +#ifdef TMPCELL +#define MAXTMP 25 + +CELL tmpcell[MAXTMP]; +#endif + +CELL * +mktmp(type, sval, fval) char *sval; double fval; +{ + register int i; + char *strsave(); + +#ifdef TMPCELL + for (i = 0; i < MAXTMP; i++) + if (tmpcell[i].c_type == 0) { + tmpcell[i].c_type = type | TMP; + tmpcell[i].c_sval = strsave(sval); + tmpcell[i].c_fval = fval; + return &tmpcell[i]; + } + error("formula too complex", (char *) 0); +#else + return mkcell(type | TMP, sval, fval); +#endif +} + +c_free(p) CELL *p; +{ + if ((p != NULL) && (p->c_type & TMP)) { +#ifdef TMPCELL + p->c_type = 0; + sfree(p->c_sval); + p->c_sval = (char *)NULL; + p->c_fval = 0.0; +#else + if (p->c_sval != NULL) { + Free(p->c_sval); + p->c_sval = NULL; + } + p->c_type = 0; + Free(p); + p = NULL; +#endif + } +} diff --git a/commands/awk/r.c b/commands/awk/r.c new file mode 100755 index 000000000..e9630252f --- /dev/null +++ b/commands/awk/r.c @@ -0,0 +1,627 @@ +/* + * a small awk clone + * + * (C) 1989 Saeko Hirabauashi & Kouichi Hirabayashi + * + * Absolutely no warranty. Use this software with your own risk. + * + * Permission to use, copy, modify and distribute this software for any + * purpose and without fee is hereby granted, provided that the above + * copyright and disclaimer notice. + * + * This program was written to fit into 64K+64K memory of the Minix 1.2. + */ + + +#include +#include +#include +#include +#include +#ifdef DOS +#include +#endif +#include "awk.h" +#include "regexp.h" + +#define MAXFLD 100 + +extern char **FS, **RS, **OFS, **ORS, **FILENAME; +extern double *NF, *NR; +extern double *FNR; +extern double *ARGC; +extern SYMBOL *argtab[]; +extern CELL *getvar(); + +char *strsave(), *strcpy(), *getsval(), *jStrchr(), *strchr(); +double getfval(), atof(); +CELL *mkcell(), *mktmp(), *execute(), *patexec(); +FILE *efopen(); + +extern CELL truecell, falsecell; + +extern int pateval; + +int infileno = 1; +FILE *ifp; +char record[BUFSIZ]; +CELL *field[MAXFLD]; + +char *fs_str; +regexp *fs_pat; + +CELL * +Getline(p) NODE *p; +{ + CELL *u; + char *fnam, *s, str[BUFSIZ]; + int i; + FILE *fp, *getfp(); + + if ((int) p->n_arg[0]) /* read into var */ + s = str; + else + s = NULL; + if ((int) p->n_arg[1]) { /* file name */ + u = execute(p->n_arg[1]); + fnam = getsval(u); + fp = getfp(fnam, (int) p->n_arg[2]); + c_free(u); + i = get1rec(s, fp); + } + else + i = Getrec(s); + if (s == str) { + u = execute(p->n_arg[0]); + setsval(u, str); + } + return mktmp(NUM, NULL, (double) i); +} + +static +get1rec(buf, fp) char *buf; FILE *fp; +{ + register int c; + register char rs, *s; + int mflg; + + if (buf == NULL) + buf = record; + if ((rs = **RS) == '\0') { /* multi line record */ + mflg = 1; + rs = '\n'; + } + else + mflg = 0; + + if (feof(fp) || (c = getc(fp)) == EOF) + return 0; + for (s = buf; ; ) { + for ( ; c != rs && c != EOF; c = getc(fp)) { + if (isKanji(c)) { + *s++ = c; c = getc(fp); + } + *s++ = c; + } + if (mflg) { + if ((c = getc(fp)) == '\n' || c == EOF) + break; + *s++ = '\n'; + } + else + break; + } + *s = '\0'; +#if 1 + if (buf == record) { +#else + if (buf == record && c != EOF) { +#endif + mkfld(record, *FS, field); + (*NR)++; + (*FNR)++; + } + return s > buf || c != EOF ? 1 : 0; +} + +Getrec(s) char *s; +{ + CELL *u; + char *file, str[8]; + + while (ifp == stdin || infileno < (int)*ARGC) { + if (ifp == NULL) { + *FNR = 0.0; + if (infileno == (int)*ARGC) + break; + sprintf(str, "%d", infileno); + u = getvar(str, argtab); + file = getsval(u); + if (strchr(file, '=') != NULL) { + setvar(file); + infileno++; + continue; + } + else if (strcmp(file, "") == 0) { +/* +if (infileno == (int)*ARGC - 1) + ifp = stdin; +*/ + infileno++; + continue; + } + else { + if (strcmp(file, "-") == 0) + ifp = stdin; + else + ifp = efopen(file, "r"); + *FILENAME = file; + } + } + if (get1rec(s, ifp)) + return 1; + else { + if (ifp != stdin) + fclose(ifp); + ifp = NULL; + infileno++; + } + } + ifp = stdin; /* for further "getline" */ + *FILENAME = "-"; + return 0; /* EOF */ +} + +mkfld(rec, sep, fld) char *rec, *sep; CELL *fld[]; +{ + char *s, *t; + char str[BUFSIZ]; + int i, j, n; + int skip = 0; + + if (strlen(sep) > 1) + return r_mkfld(rec, sep, fld); + + if (*sep == ' ' || *sep == '\0') { + sep = " \t\n"; skip++; + } + for (i = 1, n = (int) *NF; i <= n; i++) { + sfree(fld[i]->c_sval); + sfree(fld[i]); + fld[i] = NULL; + } + for (i = 0, s = rec; ; ) { + t = str; + if (skip) { + while (*s && strchr(" \t\n", *s)) + s++; + if (*s == '\0') + break; + } + while (*s && !jStrchr(sep, *s)) { + if (isKanji(*s)) + *t++ = *s++; + *t++ = *s++; + } + *t = '\0'; + if (isnum(str)) + fld[++i] = mkcell(FLD|STR|NUM, str, atof(str)); + else + fld[++i] = mkcell(FLD|STR, str, 0.0); + if (*s) + s++; + else + break; + } + *NF = (double) i; + return i; +} + +static +r_mkfld(rec, sep, fld) char *rec, *sep; CELL *fld[]; +{ + char *s, *t; + char str[BUFSIZ]; + int i, n; + regexp *mkpat(); + extern int r_start, r_length; + + if (strcmp(*FS, fs_str) != 0) { + sfree(fs_str); sfree(fs_pat); + fs_str = strsave(*FS); + fs_pat = mkpat(fs_str); + } + for (i = 1, n = (int) *NF; i <= n; i++) { + sfree(fld[i]->c_sval); + sfree(fld[i]); + fld[i] = NULL; + } + for (i = 0, s = rec, t = str; *s; ) { + if (match(fs_pat, s)) { + for (n = r_start; --n > 0; ) + *t++ = *s++; + } + else { + while (*s) + *t++ = *s++; + } + *t = '\0'; + t = str; + fld[++i] = mkcell(FLD|STR, str, 0.0); + if (*s) + s += r_length; + } + *NF = (double) i; + return i; +} + +mkrec(u) CELL *u; +{ + register char *s, *t; + register int i, j; + + for (j = (int)*NF, i = 1; i <= j; i++) + if (field[i] == u) + break; + if (i > j) { + for ( ; i < MAXFLD; i++) + if (field[i] == u) + break; + if (i == MAXFLD) + error("too many field (%d)", i); + *NF = (double)i; + } + for (t = record, i = 1, j = (int) *NF; i <= j; i++) { + if (i > 1) + *t++ = **OFS; + for (s = getsval(field[i]); *s; ) + *t++ = *s++; + } + *t++ = '\0'; +} + +CELL * +Field(p) NODE *p; +{ + CELL *u; + int i, j; + + u = execute(p->n_arg[0]); + i = (int) getfval(u); + c_free(u); + j = (int)*NF; + if (i > j) + for (++j; j <= i; j++) { + if (field[j] == NULL) + field[j] = mkcell(FLD|STR, "", 0.0); + } + return field[i]; +} + +CELL * +P1stat(p) NODE *p; +{ + CELL *u; + double x; + + pateval++; + u = execute(p->n_arg[0]); + pateval = 0; + x = getfval(u); + c_free(u); + if (x != 0.0) + u = execute(p->n_arg[1]); + else + u = &truecell; + return u; +} + +CELL * +P2stat(p) NODE *p; +{ + static stat = 0; + CELL *u, *v; + double x; + + switch (stat) { + case 0: + pateval++; + u = execute(p->n_arg[0]); + pateval = 0; + x = getfval(u); + c_free(u); + if (x == 0.0) { + u = &truecell; break; + } + else + stat++; + /* fall through */ + case 1: + u = execute(p->n_arg[2]); + c_free(u); + pateval++; + u = execute(p->n_arg[1]); + pateval = 0; + x = getfval(u); + if (x != 0.0) + stat = 0; + break; + default: + u = &truecell; + break; + } + return u; +} + +CELL * +Print0() +{ +/* + int i, j; + char *s, str[BUFSIZ]; + + for (*str = '\0', i = 1, j = (int) *NF; i <= j; i++) { + if (i > 1) + strcat(str, *OFS); + s = getsval(field[i]); + strcat(str, s); + } + strcat(str, *ORS); + fputs(str, stdout); +*/ + fprintf(stdout, "%s%s", record, *ORS); + return &truecell; +} + +char * +format(t, p) char *t; NODE *p; +{ + CELL *u, *v; + char *r, *s, *s0, fmt[BUFSIZ]; + double x; + int i; + + u = execute(p->n_arg[2]); + s = s0 = getsval(u); +/* +printf("fmt(%s)\n", s); +*/ + for (i = 3; *s; s++) { + if (isKanji(*s)) { + *t++ = *s++; *t++ = *s; continue; + } + if (*s != '%') { + *t++ = *s; continue; + } + else if (*(s + 1) == '%') { + *t++ = *s++; continue; + } + for (r = fmt, *r++ = *s++; *r++ = *s; s++) { + if (strchr("%cdefgosux", *s)) + break; + } + *r = '\0'; + if (p->n_arg[i] == NULL) + error("not enough args in printf(%s)", s0); + v = execute(p->n_arg[i++]); + if (*s == 's') + r = getsval(v); + else + x = getfval(v); +/* +printf("val(%d)(%s)\n", v->c_type, v->c_sval); +*/ + switch (*s) { + case 'c': + sprintf(t, fmt, (int) x); + break; + case 'd': + if (*(s - 1) != 'l') { + *--r = 'l'; *++r = 'd'; *++r = '\0'; + } + sprintf(t, fmt, (long) x); + break; + case 'e': case 'f': case 'g': + sprintf(t, fmt, x); + break; + case 'o': case 'u': case 'x': + if (*(s - 1) == 'l') + sprintf(t, fmt, (long) x); + else + sprintf(t, fmt, (int) x); + break; + case 's': + /*r = getsval(v);*/ + sprintf(t, fmt, r); + break; + default: + strcpy(t, fmt); + break; + } + c_free(v); + t += strlen(t); + } + c_free(u); + *t = '\0'; +} + +#define MAXFILE 10 +struct { + char *f_name; /* file name */ + FILE *f_fp; + int f_type; +} filetab[MAXFILE]; + +FILE * +getfp(file, type) char *file; +{ + register int i; + register char *name, *mode; + char *awktmp(); + FILE *fp, *efopen(), *epopen(); + + for (i = 0; i < MAXFILE; i++) + if (filetab[i].f_name && strcmp(filetab[i].f_name, file) == 0) + return filetab[i].f_fp; + for (i = 0; i < MAXFILE; i++) + if (!filetab[i].f_fp) + break; + if (i == MAXFILE) + error("too many files to open"); + name = file; + switch (type) { + case R_OUT: mode = "w"; break; + case R_APD: mode = "a"; break; + case R_POUT: +#ifdef DOS + name = awktmp(i); mode = "w"; /* MS-DOS */ +#else + fp = epopen(file, "w"); + goto g1; +#endif + break; + case R_IN: mode = "r"; break; + case R_PIN: +#ifdef DOS + { + int savefd, fd, result; + + name = awktmp(i); + if ((fd = open(name, + O_WRONLY|O_TEXT|O_CREAT|O_TRUNC,S_IREAD|S_IWRITE)) == -1) + error("can't open %s", name); + savefd = dup(1); dup2(fd, 1); close(fd); + if ((result = + system(file)) == -1) + error("can't exec %s", file); + dup2(savefd, 1); close(savefd); close(fd); + mode = "r"; + } +#else + fp = epopen(file,"r"); + goto g1; +#endif + break; + } + fp = efopen(name, mode); +g1: + filetab[i].f_name = strsave(file); + filetab[i].f_type = type; + filetab[i].f_fp = fp; + return fp; +} + +closeall() +{ + register int i; + + for (i = 0; i < MAXFILE; i++) + close1(i); +} + +CELL * +Close(s) char *s; +{ + register int i; + + for (i = 0; i < MAXFILE; i++) + if (strcmp(s, filetab[i].f_name) == 0) { + close1(i); + break; + } + i = (i == MAXFILE) ? 0 : 1; + return mktmp(NUM, NULL, (double) i); +} + +static +close1(i) +{ + int fd, result, savefd; + char *awktmp(); + + if (filetab[i].f_fp == NULL) + return; + switch (filetab[i].f_type) { + case R_PIN: +#ifdef DOS + fclose(filetab[i].f_fp); + unlink(awktmp(i)); +#else + pclose(filetab[i].f_fp); +#endif + break; + case R_IN: case R_OUT: case R_APD: + fclose(filetab[i].f_fp); + break; + case R_POUT: +#ifdef DOS + fclose(filetab[i].f_fp); + if ((fd = open(awktmp(i), O_RDONLY)) == NULL) + error("can't open %s", awktmp(i)); + savefd = dup(0); + dup2(fd, 0); + close(fd); + if ((result = + system(filetab[i].f_name)) == -1) +/* + spawnl(P_WAIT, "/usr/bin/sh", "sh", "-c", filetab[i].f_name, (char *) 0)) == -1) + fprintf(stderr, "can't spawn /bin/sh\n"); +*/ + error("can't exec %s", filetab[i].f_name); + dup2(savefd, 0); + close(savefd); + unlink(awktmp(i)); +#else + pclose(filetab[i].f_fp); +#endif + break; + } + sfree(filetab[i].f_name); + filetab[i].f_type = 0; + filetab[i].f_name = NULL; + filetab[i].f_fp = NULL; +} + +#ifndef DOS +FILE * +epopen(file, mod) char *file, *mod; +{ + FILE *fp, *popen(); + + if ((fp = popen(file, mod)) == NULL) + error("can't poen %s", file); + return fp; +} +#endif + +static char * +awktmp(i) +{ + static char str[16]; + + sprintf(str, "awk000%02d.tmp", i); + return str; +} + +Index(s, t) char *s, *t; +{ + register char *u, *v; + register int i; + + for (i = 1; *s; s++, i++) { + for (u = s, v = t; *v; u++, v++) { + if (isKanji(*v)) { + if (*u != *v) + break; + u++; v++; + } + if (*u != *v) + break; + } + if (*v == '\0') + return i; + if (isKanji(*s)) + s++; + } + return 0; +} diff --git a/commands/awk/regexp.c b/commands/awk/regexp.c new file mode 100755 index 000000000..353960bdf --- /dev/null +++ b/commands/awk/regexp.c @@ -0,0 +1,1440 @@ +/* + * regcomp and regexec -- regsub and regerror are elsewhere + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * Beware that some of this code is subtly aware of the way operator + * precedence is structured in regular expressions. Serious changes in + * regular-expression syntax might require a total rethink. + * + * Modified by K.Hirabayashi to accept KANJI code, memory allocation + * and to add following functions. + * isthere(), mkpat(), match(), regsub(), sjtok(), ktosj(), + * Strchr(), Strncmp(), Strlen() + */ + +#include +#include +#include "regexp.h" + +#define regerror(a) error("regular expression error: %s", a) + +int r_start, r_length; + +/* + * The first byte of the regexp internal "program" is actually this magic + * number; the start node begins in the second byte. + */ +#define MAGIC 0234 + +/* + * The "internal use only" fields in regexp.h are present to pass info from + * compile to execute that permits the execute phase to run lots faster on + * simple cases. They are: + * + * regstart char that must begin a match; '\0' if none obvious + * reganch is the match anchored (at beginning-of-line only)? + * regmust string (pointer into program) that match must include, or NULL + * regmlen length of regmust string + * + * Regstart and reganch permit very fast decisions on suitable starting points + * for a match, cutting down the work a lot. Regmust permits fast rejection + * of lines that cannot possibly match. The regmust tests are costly enough + * that regcomp() supplies a regmust only if the r.e. contains something + * potentially expensive (at present, the only such thing detected is * or + + * at the start of the r.e., which can involve a lot of backup). Regmlen is + * supplied because the test in regexec() needs it and regcomp() is computing + * it anyway. + */ + +/* + * Structure for regexp "program". This is essentially a linear encoding + * of a nondeterministic finite-state machine (aka syntax charts or + * "railroad normal form" in parsing technology). Each node is an opcode + * plus a "next" pointer, possibly plus an operand. "Next" pointers of + * all nodes except BRANCH implement concatenation; a "next" pointer with + * a BRANCH on both ends of it is connecting two alternatives. (Here we + * have one of the subtle syntax dependencies: an individual BRANCH (as + * opposed to a collection of them) is never concatenated with anything + * because of operator precedence.) The operand of some types of node is + * a literal string; for others, it is a node leading into a sub-FSM. In + * particular, the operand of a BRANCH node is the first node of the branch. + * (NB this is *not* a tree structure: the tail of the branch connects + * to the thing following the set of BRANCHes.) The opcodes are: + */ + +/* definition number opnd? meaning */ +#define END 0 /* no End of program. */ +#define BOL 1 /* no Match "" at beginning of line. */ +#define EOL 2 /* no Match "" at end of line. */ +#define ANY 3 /* no Match any one character. */ +#define ANYOF 4 /* str Match any character in this string. */ +#define ANYBUT 5 /* str Match any character not in this string. */ +#define BRANCH 6 /* node Match this alternative, or the next... */ +#define BACK 7 /* no Match "", "next" ptr points backward. */ +#define EXACTLY 8 /* str Match this string. */ +#define NOTHING 9 /* no Match empty string. */ +#define STAR 10 /* node Match this (simple) thing 0 or more times. */ +#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ +#define OPEN 20 /* no Mark this point in input as start of #n. */ + /* OPEN+1 is number 1, etc. */ +#define CLOSE 30 /* no Analogous to OPEN. */ + +/* + * Opcode notes: + * + * BRANCH The set of branches constituting a single choice are hooked + * together with their "next" pointers, since precedence prevents + * anything being concatenated to any individual branch. The + * "next" pointer of the last BRANCH in a choice points to the + * thing following the whole choice. This is also where the + * final "next" pointer of each individual branch points; each + * branch starts with the operand node of a BRANCH node. + * + * BACK Normal "next" pointers all implicitly point forward; BACK + * exists to make loop structures possible. + * + * STAR,PLUS '?', and complex '*' and '+', are implemented as circular + * BRANCH structures using BACK. Simple cases (one character + * per match) are implemented with STAR and PLUS for speed + * and to minimize recursive plunges. + * + * OPEN,CLOSE ...are numbered at compile time. + */ + +/* + * A node is one char of opcode followed by two chars of "next" pointer. + * "Next" pointers are stored as two 8-bit pieces, high order first. The + * value is a positive offset from the opcode of the node containing it. + * An operand, if any, simply follows the node. (Note that much of the + * code generation knows about this implicit relationship.) + * + * Using two bytes for the "next" pointer is vast overkill for most things, + * but allows patterns to get big without disasters. + */ +#define OP(p) (*(p)) +#define NEXT(p) (((*((p)+1)&0377)<<8) + *((p)+2)&0377) +#define OPERAND(p) ((p) + 3) + + + +/* + * Utility definitions. + */ +#ifndef CHARBITS +#define UCHARAT(p) ((int)*(ushort *)(p)) +#else +#define UCHARAT(p) ((int)*(p)&CHARBITS) +#endif + +#define FAIL(m) { regerror(m); return(NULL); } +#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') +#define META "^$.[()|?+*\\" + +/* + * Flags to be passed up and down. + */ +#define HASWIDTH 01 /* Known never to match null string. */ +#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ +#define SPSTART 04 /* Starts with * or +. */ +#define WORST 0 /* Worst case. */ + +/* + * Global work variables for regcomp(). + */ +static ushort *regparse; /* Input-scan pointer. */ +static int regnpar; /* () count. */ +static ushort regdummy; +static ushort *regcode; /* Code-emit pointer; ®dummy = don't. */ +static long regsize; /* Code size. */ + +/* + * Forward declarations for regcomp()'s friends. + */ +#ifndef STATIC +#define STATIC static +#endif +STATIC ushort *reg(); +STATIC ushort *regbranch(); +STATIC ushort *regpiece(); +STATIC ushort *regatom(); +STATIC ushort *regnode(); +STATIC ushort *regnext(); +STATIC void regc(); +STATIC void reginsert(); +STATIC void regtail(); +STATIC void regoptail(); +STATIC int Strcspn(); + +/* + - regcomp - compile a regular expression into internal code + * + * We can't allocate space until we know how big the compiled form will be, + * but we can't compile it (and thus know how big it is) until we've got a + * place to put the code. So we cheat: we compile it twice, once with code + * generation turned off and size counting turned on, and once "for real". + * This also means that we don't allocate space until we are sure that the + * thing really will compile successfully, and we never have to move the + * code and thus invalidate pointers into it. (Note that it has to be in + * one piece because free() must be able to free it all.) + * + * Beware that the optimization-preparation code in here knows about some + * of the structure of the compiled regexp. + */ +regexp * +regcomp(exp) +ushort *exp; +{ + register regexp *r; + register ushort *scan; + register ushort *longest; + register int len; + int flags; + extern char *emalloc(); + + if (exp == NULL) + FAIL("NULL argument"); + + /* First pass: determine size, legality. */ + regparse = exp; + regnpar = 1; + regsize = 0L; + regcode = ®dummy; + regc((ushort) MAGIC); + if (reg(0, &flags) == NULL) + return(NULL); + + /* Small enough for pointer-storage convention? */ + if (regsize >= 32767L) /* Probably could be 65535L. */ + FAIL("regexp too big"); + + /* Allocate space. */ + r = (regexp *)emalloc(sizeof(regexp) + (unsigned)regsize * sizeof(ushort)); + + /* Second pass: emit code. */ + regparse = exp; + regnpar = 1; + regcode = r->program; + regc((ushort) MAGIC); + if (reg(0, &flags) == NULL) + return(NULL); + + /* Dig out information for optimizations. */ + r->regstart = '\0'; /* Worst-case defaults. */ + r->reganch = 0; + r->regmust = NULL; + r->regmlen = 0; + scan = r->program+1; /* First BRANCH. */ + if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ + scan = OPERAND(scan); + + /* Starting-point info. */ + if (OP(scan) == EXACTLY) + r->regstart = *OPERAND(scan); + else if (OP(scan) == BOL) + r->reganch++; + + /* + * If there's something expensive in the r.e., find the + * longest literal string that must appear and make it the + * regmust. Resolve ties in favor of later strings, since + * the regstart check works with the beginning of the r.e. + * and avoiding duplication strengthens checking. Not a + * strong reason, but sufficient in the absence of others. + */ + if (flags&SPSTART) { + longest = NULL; + len = 0; + for (; scan != NULL; scan = regnext(scan)) + if (OP(scan) == EXACTLY && Strlen(OPERAND(scan)) >= len) { + longest = OPERAND(scan); + len = Strlen(OPERAND(scan)); + } + r->regmust = longest; + r->regmlen = len; + } + } + + return(r); +} + +/* + - reg - regular expression, i.e. main body or parenthesized thing + * + * Caller must absorb opening parenthesis. + * + * Combining parenthesis handling with the base level of regular expression + * is a trifle forced, but the need to tie the tails of the branches to what + * follows makes it hard to avoid. + */ +static ushort * +reg(paren, flagp) +int paren; /* Parenthesized? */ +int *flagp; +{ + register ushort *ret; + register ushort *br; + register ushort *ender; + register int parno; + int flags; + + *flagp = HASWIDTH; /* Tentatively. */ + + /* Make an OPEN node, if parenthesized. */ + if (paren) { + if (regnpar >= NSUBEXP) + FAIL("too many ()"); + parno = regnpar; + regnpar++; + ret = regnode(OPEN+parno); + } else + ret = NULL; + + /* Pick up the branches, linking them together. */ + br = regbranch(&flags); + if (br == NULL) + return(NULL); + if (ret != NULL) + regtail(ret, br); /* OPEN -> first. */ + else + ret = br; + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + while (*regparse == '|') { + regparse++; + br = regbranch(&flags); + if (br == NULL) + return(NULL); + regtail(ret, br); /* BRANCH -> BRANCH. */ + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + } + + /* Make a closing node, and hook it on the end. */ + ender = regnode((paren) ? CLOSE+parno : END); + regtail(ret, ender); + + /* Hook the tails of the branches to the closing node. */ + for (br = ret; br != NULL; br = regnext(br)) + regoptail(br, ender); + + /* Check for proper termination. */ + if (paren && *regparse++ != ')') { + FAIL("unmatched ()"); + } else if (!paren && *regparse != '\0') { + if (*regparse == ')') { + FAIL("unmatched ()"); + } else + FAIL("junk on end"); /* "Can't happen". */ + /* NOTREACHED */ + } + + return(ret); +} + +/* + - regbranch - one alternative of an | operator + * + * Implements the concatenation operator. + */ +static ushort * +regbranch(flagp) +int *flagp; +{ + register ushort *ret; + register ushort *chain; + register ushort *latest; + int flags; + + *flagp = WORST; /* Tentatively. */ + + ret = regnode(BRANCH); + chain = NULL; + while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { + latest = regpiece(&flags); + if (latest == NULL) + return(NULL); + *flagp |= flags&HASWIDTH; + if (chain == NULL) /* First piece. */ + *flagp |= flags&SPSTART; + else + regtail(chain, latest); + chain = latest; + } + if (chain == NULL) /* Loop ran zero times. */ + (void) regnode(NOTHING); + + return(ret); +} + +/* + - regpiece - something followed by possible [*+?] + * + * Note that the branching code sequences used for ? and the general cases + * of * and + are somewhat optimized: they use the same NOTHING node as + * both the endmarker for their branch list and the body of the last branch. + * It might seem that this node could be dispensed with entirely, but the + * endmarker role is not redundant. + */ +static ushort * +regpiece(flagp) +int *flagp; +{ + register ushort *ret; + register ushort op; + register ushort *next; + int flags; + + ret = regatom(&flags); + if (ret == NULL) + return(NULL); + + op = *regparse; + if (!ISMULT(op)) { + *flagp = flags; + return(ret); + } + + if (!(flags&HASWIDTH) && op != '?') + FAIL("*+ operand could be empty"); + *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); + + if (op == '*' && (flags&SIMPLE)) + reginsert(STAR, ret); + else if (op == '*') { + /* Emit x* as (x&|), where & means "self". */ + reginsert(BRANCH, ret); /* Either x */ + regoptail(ret, regnode(BACK)); /* and loop */ + regoptail(ret, ret); /* back */ + regtail(ret, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '+' && (flags&SIMPLE)) + reginsert(PLUS, ret); + else if (op == '+') { + /* Emit x+ as x(&|), where & means "self". */ + next = regnode(BRANCH); /* Either */ + regtail(ret, next); + regtail(regnode(BACK), ret); /* loop back */ + regtail(next, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '?') { + /* Emit x? as (x|) */ + reginsert(BRANCH, ret); /* Either x */ + regtail(ret, regnode(BRANCH)); /* or */ + next = regnode(NOTHING); /* null. */ + regtail(ret, next); + regoptail(ret, next); + } + regparse++; + if (ISMULT(*regparse)) + FAIL("nested *?+"); + + return(ret); +} + +/* + - regatom - the lowest level + * + * Optimization: gobbles an entire sequence of ordinary characters so that + * it can turn them into a single node, which is smaller to store and + * faster to run. Backslashed characters are exceptions, each becoming a + * separate node; the code is simpler that way and it's not worth fixing. + */ +static ushort * +regatom(flagp) +int *flagp; +{ + register ushort *ret; + int flags; + ushort c, d; + + *flagp = WORST; /* Tentatively. */ + + switch ((int) *regparse++) { + case '^': + ret = regnode(BOL); + break; + case '$': + ret = regnode(EOL); + break; + case '.': + ret = regnode(ANY); + *flagp |= HASWIDTH|SIMPLE; + break; + case '[': { + register int class; + register int classend; + register int c; + + if (*regparse == '^') { /* Complement of range. */ + ret = regnode(ANYBUT); + regparse++; + } else + ret = regnode(ANYOF); + if (*regparse == ']' || *regparse == '-') + regc(*regparse++); + while (*regparse != '\0' && *regparse != ']') { + if (*regparse == '-') { + regparse++; + if (*regparse == ']' || *regparse == '\0') + regc((ushort) '-'); + else { + class = UCHARAT(regparse-2)+1; + classend = UCHARAT(regparse); + if (class > classend+1) + FAIL("invalid [] range"); + regc((ushort) 0xffff); + regc((ushort) class); + regc((ushort) classend); + regparse++; + } + } else { + if ((c = *regparse++) == '\\') { + if ((c = *regparse++) == 'n') + c = '\n'; + else if (c == 't') + c = '\t'; + } + regc(c); + } + } + regc((ushort) 0); + if (*regparse != ']') + FAIL("unmatched []"); + regparse++; + *flagp |= HASWIDTH|SIMPLE; + } + break; + case '(': + ret = reg(1, &flags); + if (ret == NULL) + return(NULL); + *flagp |= flags&(HASWIDTH|SPSTART); + break; + case '\0': + case '|': + case ')': + FAIL("internal urp"); /* Supposed to be caught earlier. */ + break; + case '?': + case '+': + case '*': + FAIL("?+* follows nothing"); + break; + case '\\': + if (*regparse == '\0') + FAIL("trailing \\"); + ret = regnode(EXACTLY); +/* + regc(*regparse++); +*/ + c = *regparse++; + if (c == 'n') + c = '\n'; + else if (c == 't') + c = '\t'; + else if (c == 'f') + c = '\f'; + else if (c == '\r') + c = '\r'; + else if (c == '\b') + c = '\b'; + else if (IsDigid(c)) { + d = c - '0'; + if (IsDigid(*regparse)) { + d = d * 8 + *regparse++ - '0'; + if (IsDigid(*regparse)) + d = d * 8 + *regparse++ - '0'; + } + c = d; + } + regc(c); + regc((ushort) 0); + *flagp |= HASWIDTH|SIMPLE; + break; + default: { + register int len; + register char ender; + + regparse--; + len = Strcspn(regparse, META); + if (len <= 0) + FAIL("internal disaster"); + ender = *(regparse+len); + if (len > 1 && ISMULT(ender)) + len--; /* Back off clear of ?+* operand. */ + *flagp |= HASWIDTH; + if (len == 1) + *flagp |= SIMPLE; + ret = regnode(EXACTLY); + while (len > 0) { + regc(*regparse++); + len--; + } + regc((ushort) 0); + } + break; + } + + return(ret); +} + +IsDigid(c) ushort c; +{ + return '0' <= c && c <= '9'; +} + +/* + - regnode - emit a node + */ +static ushort * /* Location. */ +regnode(op) +ushort op; +{ + register ushort *ret; + register ushort *ptr; + + ret = regcode; + if (ret == ®dummy) { + regsize += 3; + return(ret); + } + + ptr = ret; + *ptr++ = op; + *ptr++ = '\0'; /* Null "next" pointer. */ + *ptr++ = '\0'; + regcode = ptr; + + return(ret); +} + +/* + - regc - emit (if appropriate) a byte of code + */ +static void +regc(b) +ushort b; +{ + if (regcode != ®dummy) + *regcode++ = b; + else + regsize++; +} + +/* + - reginsert - insert an operator in front of already-emitted operand + * + * Means relocating the operand. + */ +static void +reginsert(op, opnd) +ushort op; +ushort *opnd; +{ + register ushort *src; + register ushort *dst; + register ushort *place; + + if (regcode == ®dummy) { + regsize += 3; + return; + } + + src = regcode; + regcode += 3; + dst = regcode; + while (src > opnd) + *--dst = *--src; + + place = opnd; /* Op node, where operand used to be. */ + *place++ = op; + *place++ = '\0'; + *place++ = '\0'; +} + +/* + - regtail - set the next-pointer at the end of a node chain + */ +static void +regtail(p, val) +ushort *p; +ushort *val; +{ + register ushort *scan; + register ushort *temp; + register int offset; + + if (p == ®dummy) + return; + + /* Find last node. */ + scan = p; + for (;;) { + temp = regnext(scan); + if (temp == NULL) + break; + scan = temp; + } + + if (OP(scan) == BACK) + offset = scan - val; + else + offset = val - scan; + *(scan+1) = (offset>>8)&0377; + *(scan+2) = offset&0377; +} + +/* + - regoptail - regtail on operand of first argument; nop if operandless + */ +static void +regoptail(p, val) +ushort *p; +ushort *val; +{ + /* "Operandless" and "op != BRANCH" are synonymous in practice. */ + if (p == NULL || p == ®dummy || OP(p) != BRANCH) + return; + regtail(OPERAND(p), val); +} + +/* + * regexec and friends + */ + +/* + * Global work variables for regexec(). + */ +static ushort *reginput; /* String-input pointer. */ +static ushort *regbol; /* Beginning of input, for ^ check. */ +static ushort **regstartp; /* Pointer to startp array. */ +static ushort **regendp; /* Ditto for endp. */ + +/* + * Forwards. + */ +STATIC int regtry(); +STATIC int regmatch(); +STATIC int regrepeat(); + +#ifdef DEBUG +int regnarrate = 0; +void regdump(); +STATIC char *regprop(); +#endif + +/* + - regexec - match a regexp against a string + */ +int +regexec(prog, string, bolflag) +register regexp *prog; +register ushort *string; +int bolflag; +{ + register ushort *s; + extern ushort *Strchr(); + + /* Be paranoid... */ + if (prog == NULL || string == NULL) { + regerror("NULL parameter"); + return(0); + } + + /* Check validity of program. */ + if (prog->program[0] != MAGIC) { + regerror("corrupted program"); + return(0); + } + + /* If there is a "must appear" string, look for it. */ + if (prog->regmust != NULL) { + s = string; + while ((s = Strchr(s, prog->regmust[0])) != NULL) { + if (Strncmp(s, prog->regmust, prog->regmlen) == 0) + break; /* Found it. */ + s++; + } + if (s == NULL) /* Not present. */ + return(0); + } + + /* Mark beginning of line for ^ . */ + if(bolflag) + regbol = string; + else + regbol = NULL; + + /* Simplest case: anchored match need be tried only once. */ + if (prog->reganch) + return(regtry(prog, string)); + + /* Messy cases: unanchored match. */ + s = string; + if (prog->regstart != '\0') { + /* We know what char it must start with. */ + while ((s = Strchr(s, prog->regstart)) != NULL) { + if (regtry(prog, s)) + return(1); + s++; + } +} + else + /* We don't -- general case. */ + do { + if (regtry(prog, s)) + return(1); + } while (*s++ != '\0'); + + /* Failure. */ + return(0); +} + +/* + - regtry - try match at specific point + */ +static int /* 0 failure, 1 success */ +regtry(prog, string) +regexp *prog; +ushort *string; +{ + register int i; + register ushort **sp; + register ushort **ep; + + reginput = string; + regstartp = prog->startp; + regendp = prog->endp; + + sp = prog->startp; + ep = prog->endp; + for (i = NSUBEXP; i > 0; i--) { + *sp++ = NULL; + *ep++ = NULL; + } + if (regmatch(prog->program + 1)) { + prog->startp[0] = string; + prog->endp[0] = reginput; + return(1); + } else + return(0); +} + +/* + - regmatch - main matching routine + * + * Conceptually the strategy is simple: check to see whether the current + * node matches, call self recursively to see whether the rest matches, + * and then act accordingly. In practice we make some effort to avoid + * recursion, in particular by going through "ordinary" nodes (that don't + * need to know whether the rest of the match failed) by a loop instead of + * by recursion. + */ +static int /* 0 failure, 1 success */ +regmatch(prog) +ushort *prog; +{ + register ushort *scan; /* Current node. */ + ushort *next; /* Next node. */ + extern ushort *Strchr(); + + scan = prog; +#ifdef DEBUG + if (scan != NULL && regnarrate) + fprintf(stderr, "%s(\n", regprop(scan)); +#endif + while (scan != NULL) { +#ifdef DEBUG + if (regnarrate) + fprintf(stderr, "%s...\n", regprop(scan)); +#endif + next = regnext(scan); + + switch ((int) OP(scan)) { + case BOL: + if (reginput != regbol) + return(0); + break; + case EOL: + if (*reginput != '\0') + return(0); + break; + case ANY: + if (*reginput == '\0') + return(0); + reginput++; + break; + case EXACTLY: { + register int len; + register ushort *opnd; + + opnd = OPERAND(scan); + /* Inline the first character, for speed. */ + if (*opnd != *reginput) + return(0); + len = Strlen(opnd); + if (len > 1 && Strncmp(opnd, reginput, len) != 0) + return(0); + reginput += len; + } + break; + case ANYOF: + if (*reginput == '\0' || isthere(OPERAND(scan), *reginput) == 0) + return(0); + reginput++; + break; + case ANYBUT: + if (*reginput == '\0' || isthere(OPERAND(scan), *reginput) != 0) + return(0); + reginput++; + break; + case NOTHING: + break; + case BACK: + break; + case OPEN+1: + case OPEN+2: + case OPEN+3: + case OPEN+4: + case OPEN+5: + case OPEN+6: + case OPEN+7: + case OPEN+8: + case OPEN+9: { + register int no; + register ushort *save; + + no = OP(scan) - OPEN; + save = reginput; + + if (regmatch(next)) { + /* + * Don't set startp if some later + * invocation of the same parentheses + * already has. + */ + if (regstartp[no] == NULL) + regstartp[no] = save; + return(1); + } else + return(0); + } + break; + case CLOSE+1: + case CLOSE+2: + case CLOSE+3: + case CLOSE+4: + case CLOSE+5: + case CLOSE+6: + case CLOSE+7: + case CLOSE+8: + case CLOSE+9: { + register int no; + register ushort *save; + + no = OP(scan) - CLOSE; + save = reginput; + + if (regmatch(next)) { + /* + * Don't set endp if some later + * invocation of the same parentheses + * already has. + */ + if (regendp[no] == NULL) + regendp[no] = save; + return(1); + } else + return(0); + } + break; + case BRANCH: { + register ushort *save; + + if (OP(next) != BRANCH) /* No choice. */ + next = OPERAND(scan); /* Avoid recursion. */ + else { + do { + save = reginput; + if (regmatch(OPERAND(scan))) + return(1); + reginput = save; + scan = regnext(scan); + } while (scan != NULL && OP(scan) == BRANCH); + return(0); + /* NOTREACHED */ + } + } + break; + case STAR: + case PLUS: { + register ushort nextch; + register int no; + register ushort *save; + register int min; + + /* + * Lookahead to avoid useless match attempts + * when we know what character comes next. + */ + nextch = '\0'; + if (OP(next) == EXACTLY) + nextch = *OPERAND(next); + min = (OP(scan) == STAR) ? 0 : 1; + save = reginput; + no = regrepeat(OPERAND(scan)); + while (no >= min) { + /* If it could work, try it. */ + if (nextch == '\0' || *reginput == nextch) + if (regmatch(next)) + return(1); + /* Couldn't or didn't -- back up. */ + no--; + reginput = save + no; + } + return(0); + } + break; + case END: + return(1); /* Success! */ + break; + default: + regerror("memory corruption"); + return(0); + break; + } + + scan = next; + } + + /* + * We get here only if there's trouble -- normally "case END" is + * the terminating point. + */ + regerror("corrupted pointers"); + return(0); +} + +/* + - regrepeat - repeatedly match something simple, report how many + */ +static int +regrepeat(p) +ushort *p; +{ + register int count = 0; + register ushort *scan; + register ushort *opnd; + + scan = reginput; + opnd = OPERAND(p); + switch (OP(p)) { + case ANY: + count = Strlen(scan); + scan += count; + break; + case EXACTLY: + while (*opnd == *scan) { + count++; + scan++; + } + break; + case ANYOF: + while (*scan != '\0' && isthere(opnd, *scan) != 0) { + count++; + scan++; + } + break; + case ANYBUT: + while (*scan != '\0' && isthere(opnd, *scan) == 0) { + count++; + scan++; + } + break; + default: /* Oh dear. Called inappropriately. */ + regerror("internal foulup"); + count = 0; /* Best compromise. */ + break; + } + reginput = scan; + + return(count); +} + +/* + - regnext - dig the "next" pointer out of a node + */ +static ushort * +regnext(p) +register ushort *p; +{ + register int offset; + + if (p == ®dummy) + return(NULL); + + offset = NEXT(p); + if (offset == 0) + return(NULL); + + if (OP(p) == BACK) + return(p-offset); + else + return(p+offset); +} + +#ifdef DEBUG + +STATIC char *regprop(); + +/* + - regdump - dump a regexp onto stdout in vaguely comprehensible form + */ +void +regdump(r) +regexp *r; +{ + register ushort *s; + register ushort op = EXACTLY; /* Arbitrary non-END op. */ + register ushort *next; + + + s = r->program + 1; + while (op != END) { /* While that wasn't END last time... */ + op = OP(s); + printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ + next = regnext(s); + if (next == NULL) /* Next ptr. */ + printf("(0)"); + else + printf("(%d)", (s-r->program)+(next-s)); + s += 3; + if (op == ANYOF || op == ANYBUT || op == EXACTLY) { + /* Literal string, where present. */ + while (*s != '\0') { + if (*s == 0xffff) { /* range */ + kputchar(*++s); putchar('-'); ++s; + } + kputchar(*s++); + } + s++; + } + putchar('\n'); + } + + /* Header fields of interest. */ + if (r->regstart != '\0') { + fputs("start `", stdout); kputchar(r->regstart); fputs("' ", stdout); + } + if (r->reganch) + printf("anchored "); + if (r->regmust != NULL) { + fputs("must have \"", stdout); kputs(r->regmust); putchar('"'); + } + printf("\n"); +} + +kputchar(c) ushort c; +{ + if (c & 0xff00) + putchar(c >> 8); + putchar(c & 0xff); +} + +kputs(s) ushort *s; +{ + while (*s) + kputchar(*s++); +} + +/* + - regprop - printable representation of opcode + */ +static char * +regprop(op) +ushort *op; +{ + register char *p; + static char buf[50]; + + (void) strcpy(buf, ":"); + + switch ((int) OP(op)) { + case BOL: + p = "BOL"; + break; + case EOL: + p = "EOL"; + break; + case ANY: + p = "ANY"; + break; + case ANYOF: + p = "ANYOF"; + break; + case ANYBUT: + p = "ANYBUT"; + break; + case BRANCH: + p = "BRANCH"; + break; + case EXACTLY: + p = "EXACTLY"; + break; + case NOTHING: + p = "NOTHING"; + break; + case BACK: + p = "BACK"; + break; + case END: + p = "END"; + break; + case OPEN+1: + case OPEN+2: + case OPEN+3: + case OPEN+4: + case OPEN+5: + case OPEN+6: + case OPEN+7: + case OPEN+8: + case OPEN+9: + sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN); + p = NULL; + break; + case CLOSE+1: + case CLOSE+2: + case CLOSE+3: + case CLOSE+4: + case CLOSE+5: + case CLOSE+6: + case CLOSE+7: + case CLOSE+8: + case CLOSE+9: + sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE); + p = NULL; + break; + case STAR: + p = "STAR"; + break; + case PLUS: + p = "PLUS"; + break; + default: + regerror("corrupted opcode"); + break; + } + if (p != NULL) + (void) strcat(buf, p); + return(buf); +} +#endif + +/* + * The following is provided for those people who do not have strcspn() in + * their C libraries. They should get off their butts and do something + * about it; at least one public-domain implementation of those (highly + * useful) string routines has been published on Usenet. + */ +/* + * strcspn - find length of initial segment of s1 consisting entirely + * of characters not from s2 + */ + +static int +Strcspn(s1, s2) +ushort *s1; +unsigned char *s2; +{ + register ushort *scan1; + register unsigned char *scan2; + register int count; + + count = 0; + for (scan1 = s1; *scan1 != '\0'; scan1++) { + for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */ + if (*scan1 == *scan2++) + return(count); + count++; + } + return(count); +} + +isthere(s, c) ushort *s, c; +{ + register unsigned int c1, c2; + + for ( ; *s; s++) { + if (*s == 0xffff) { /* range */ + c1 = *++s; c2 = *++s; + if (c1 <= c && c <= c2) + return 1; + } + else if (*s == c) + return 1; + } + return 0; +} + +ushort * +Strchr(s, c) ushort *s, c; +{ + for ( ; *s; s++) + if (*s == c) + return s; + return NULL; +} + +Strncmp(s, t, n) ushort *s, *t; +{ + for ( ; --n > 0 && *s == *t; s++, t++) + ; + return *s - *t; +} + +Strlen(s) ushort *s; +{ + int i; + + for (i = 0; *s; i++, s++) + ; + return i; +} + +ushort kbuf[BUFSIZ]; + +char * +mkpat(s) char *s; +{ + sjtok(kbuf, s); + return (char *) regcomp(kbuf); +} + +match(p, s) regexp *p; char *s; +{ + register int i; + + sjtok(kbuf, s); + if (i = regexec(p, kbuf, 1)) { + r_start = p->startp[0] - kbuf + 1; + r_length = p->endp[0] - p->startp[0]; + } + else + r_start = r_length = 0; + return i; +} + +sjtok(s, t) ushort *s; unsigned char *t; +{ + register c; + + for ( ; *t; t++) { + if (isKanji(c = *t)) + c = (c << 8) | (*++t & 0xff); + *s++ = c; + } + *s = 0; +} + +ktosj(s, t) unsigned char *s; ushort *t; +{ + register c; + + while (*t) { + if ((c = *t++) & 0xff00) + *s++ = c >> 8; + *s++ = c & 0xff; + } + *s = '\0'; +} + +regsub(dst, exp, src, pat, pos) ushort *dst, *src, *pat; regexp *exp; +{ /* dst <-- s/src/pat/pos global substitution for pos == 0 */ + register int c, i; + register ushort *loc1, *loc2, *s, *t, *u; + register int n = 0; + + if (exp->program[0] != MAGIC) { + regerror("damaged regexp fed to regsub"); + return 0; + } + while (*src) { +next: + if (regexec(exp, src, 1) == 0) + break; + loc1 = exp->startp[0]; loc2 = exp->endp[0]; + if (pos-- > 1) { + while (src < loc2) + *dst++ = *src++; + goto next; + } + while (src < loc1) + *dst++ = *src++; + for (s = pat; c = *s++; ) { + if (c == '&') + i = 0; + else if (c == '\\' && '0' <= *s && *s <= '9') + i = *s++ - '0'; + else { + if (c == '\\' && (*s == '\\' || *s == '&')) + c = *s++; + *dst++ = c; + continue; + } + if ((t = exp->startp[i]) != NULL + && (u = exp->endp[i]) != NULL) { + while (t < u) + *dst++ = *t++; + } + } + src = loc2; + n++; + if (pos == 0) + break; + } + while (*src) + *dst++ = *src++; + *dst++ = 0; + return n; +} + +static ushort kbuf1[BUFSIZ], kbuf2[BUFSIZ]; + +Sub(u, exp, str, s, t, pos) char *exp; char *s, *t, *u; +{ + register int i; + regexp *r; + + if (str) { + sjtok(kbuf, exp); + r = regcomp(kbuf); + } + else + r = (regexp *) exp; + sjtok(kbuf, s); + sjtok(kbuf1, t); + i = regsub(kbuf2, r, kbuf1, kbuf, pos); + ktosj(u, kbuf2); + if (str) + sfree(r); + + return i; +} diff --git a/commands/awk/regexp.h b/commands/awk/regexp.h new file mode 100755 index 000000000..c5437a85d --- /dev/null +++ b/commands/awk/regexp.h @@ -0,0 +1,23 @@ +/* + * Definitions etc. for regexp(3) routines. + * + * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], + * not the System V one. + */ +#define ushort unsigned short +#define CHARBITS 0xffff +#define NSUBEXP 10 +typedef struct regexp { + ushort *startp[NSUBEXP]; + ushort *endp[NSUBEXP]; + ushort regstart; /* Internal use only. */ + ushort reganch; /* Internal use only. */ + ushort *regmust; /* Internal use only. */ + int regmlen; /* Internal use only. */ + ushort program[1]; /* Unwarranted chumminess with compiler. */ +} regexp; + +extern regexp *regcomp(); +extern int regexec(); +extern int regsub(); +extern int regerror(); diff --git a/commands/awk/v.c b/commands/awk/v.c new file mode 100755 index 000000000..7d59e3e31 --- /dev/null +++ b/commands/awk/v.c @@ -0,0 +1,689 @@ +/* + * a small awk clone + * + * (C) 1989 Saeko Hirabauashi & Kouichi Hirabayashi + * + * Absolutely no warranty. Use this software with your own risk. + * + * Permission to use, copy, modify and distribute this software for any + * purpose and without fee is hereby granted, provided that the above + * copyright and disclaimer notice. + * + * This program was written to fit into 64K+64K memory of the Minix 1.2. + */ + + +#include +#include +#include "awk.h" +#include "regexp.h" + +#define PI 3.14159265358979323846 + +#define HASHSIZE 50 +#define MAXFIELD 100 + +double atof(); +char *getsval(), *jStrchar(); +extern CELL *execute(), *_Arg(); + +extern char record[]; +extern CELL *field[]; + +extern CELL truecell, falsecell; +extern prmflg; + +SYMBOL *hashtab[HASHSIZE]; +SYMBOL *funtab[HASHSIZE]; +SYMBOL *argtab[HASHSIZE]; + +char *strsave(), *emalloc(), *strchr(); +CELL *lookup(), *install(), *_install(), *mkcell(), *mktmp(), *getvar(); + +char **FS, **RS, **OFS, **ORS, **OFMT, **FILENAME; +char **SUBSEP; +double *NR, *NF; +double *FNR, *ARGC, *RSTART, *RLENGTH; + +init() +{ + FS = &install("FS", VAR|STR, " ", 0.0, hashtab)->c_sval; + RS = &install("RS", VAR|STR, "\n", 0.0, hashtab)->c_sval; + OFS = &install("OFS", VAR|STR , " ", 0.0, hashtab)->c_sval; + ORS = &install("ORS", VAR|STR, "\n", 0.0, hashtab)->c_sval; + OFMT = &install("OFMT", VAR|STR, "%.6g", 0.0, hashtab)->c_sval; + NR = &install("NR", VAR|NUM, (char *)NULL, 0.0, hashtab)->c_fval; + NF = &install("NF", VAR|NUM, (char *)NULL, 0.0, hashtab)->c_fval; + FILENAME = &install("FILENAME", VAR|STR, (char *)NULL, 0.0, hashtab)->c_sval; + install("PI", VAR|NUM, (char *)NULL, PI, hashtab); + field[0] = mkcell(REC|STR, (char *)NULL, 0.0); /* $0 */ + field[0]->c_sval = record; + SUBSEP = &install("SUBSEP", VAR|STR, "\034", 0.0, hashtab)->c_sval; + FNR = &install("FNR", VAR|NUM, (char *)NULL, 0.0, hashtab)->c_fval; + RSTART = &install("RSTART", VAR|NUM, (char *)NULL, 0.0, hashtab)->c_fval; + RLENGTH = &install("RLENGTH", VAR|NUM, (char *)NULL, 0.0, hashtab)->c_fval; +} + +setvar(s) char *s; +{ + CELL *u; + char *t; + + for (t = s; *t && *t != '='; t++) + ; + *t++ = '\0'; + if ((u = lookup(s, hashtab)) == (CELL *)NULL) { + if (isnum(t)) + install(s, VAR|NUM|STR, t, atof(t), hashtab); + else + install(s, VAR|STR, t, 0.0, hashtab); + } + else { + if (isnum(t)) + setfval(u, atof(t)); + else + setsval(u, t); + } +} + +initarg(arg0, argc, argv) char *arg0, **argv; +{ + CELL *u; + register int i; + register char str[4]; + + ARGC = &install("ARGC", VAR|NUM, (char *)NULL, (double)argc+1, hashtab)->c_fval; + u = install("ARGV", ARR, (char *)NULL, 0.0, hashtab); + u->c_sval = (char *) argtab; + install("0", VAR|STR, arg0, 0.0, argtab); + for (i = 0; i < argc; i++) { + sprintf(str, "%d", i+1); + if (isnum(argv[i])) + install(str, VAR|STR|NUM, argv[i], atof(argv[i]), argtab); + else + install(str, VAR|STR, argv[i], 0.0, argtab); + } +} + +static +hash(s) unsigned char *s; +{ + register unsigned int h; + + for (h = 0; *s; ) + h += *s++; + return h % HASHSIZE; +} + +CELL * +lookup(s, h) char *s; SYMBOL *h[]; +{ + register SYMBOL *p; + + for (p = h[hash(s)]; p; p = p->s_next) + if (strcmp(s, p->s_name) == 0) + return p->s_val; + return (CELL *)NULL; +} + +static CELL * +install(name, type, sval, fval, h) char *name, *sval; double fval; SYMBOL *h[]; +{ + CELL *u; + + if ((u = lookup(name, h)) == (CELL *)NULL) + u = _install(name, type, sval, fval, h); + else + error("%s is doubly defined", name); + return u; +} + +static CELL * +_install(name, type, sval, fval, h) char *name, *sval; double fval; SYMBOL *h[];{ + register SYMBOL *p; + CELL *u; + int hval; + + p = (SYMBOL *) emalloc(sizeof(*p)); + u = (CELL *) emalloc(sizeof(*u)); + p->s_name = strsave(name); + p->s_val = u; + hval = hash(name); + p->s_next = h[hval]; + h[hval] = p; + u->c_type = type; + u->c_sval = strsave(sval); +#if 0 + if (!(type & NUM) && isnum(sval)) { + u->c_fval = atof(sval); + u->c_type |= NUM; + } + else +#endif + u->c_fval = fval; + return u; +} + +CELL * +getvar(s, h, typ) char *s; SYMBOL *h[]; +{ + CELL *u; + SYMBOL *p; + char *t; + int i, hval; + + if ((u = lookup(s, h)) == (CELL *)NULL) { + if (prmflg) { + u = _install(s, UDF, "", 0.0, h); + goto rtn; + } + else if (typ & ARR) { + t = emalloc(sizeof(SYMBOL *) * HASHSIZE); + for (i = 0; i < HASHSIZE; i++) + ((SYMBOL **) t)[i] = (SYMBOL *)NULL; + u = (CELL *) emalloc(sizeof(*u)); + u->c_type = typ; + u->c_sval = t; + u->c_fval = 0.0; + p = (SYMBOL *) emalloc(sizeof(*p)); + p->s_name = strsave(s); + p->s_val = u; + hval = hash(s); + p->s_next = h[hval]; + h[hval] = p; + } + else + u = _install(s, typ, "", 0.0, h); + } + else if (!prmflg && (u->c_type == UDF) && (typ != UDF)) { + /* fix up local_var/forward_function */ + if (typ == ARR) { +/* +printf("getvar_correct_to_array\n"); +*/ + u->c_type = typ; + sfree(u->c_sval); + u->c_sval = emalloc(sizeof(SYMBOL *) * HASHSIZE); + for (i = 0; i < HASHSIZE; i++) + ((SYMBOL **) u->c_sval)[i] = (SYMBOL *)NULL; + u->c_fval = 0.0; + } + else if (typ != UDF) { + u->c_type = typ; + } + } +rtn: + return u; +} + +fixarray(u) CELL *u; +{ + int i; + + if (u->c_type == UDF) { /* fix up local var */ +/* +printf("fixarray\n"); +*/ + u->c_type = ARR; + sfree(u->c_sval); + u->c_sval = emalloc(sizeof(SYMBOL *) * HASHSIZE); + for (i = 0; i < HASHSIZE; i++) + ((SYMBOL **) u->c_sval)[i] = (SYMBOL *)NULL; + u->c_fval = 0.0; + } +} + +a_free(u) CELL *u; +{ /* free local array */ + SYMBOL **h, *q, *r; + CELL *v; + int i; + + if (!(u->c_type & ARR)) + error("try to free non array variable", (char *)0); + h = (SYMBOL **) u->c_sval; + for (i = 0; i < HASHSIZE; i++) + for (q = h[i]; q; q = r) { + r = q->s_next; + sfree(q->s_name); + v = q->s_val; /* CELL */ + c_free(v); + sfree(q); /* SYMBOL */ + } + + sfree(u->c_sval); /* symbol table */ + c_free(u); +} + +CELL * +Array(p) NODE *p; +{ + CELL *u; + char str[BUFSIZ]; + int i, n; + + CELL *v; + + u = (CELL *) p->n_arg[0]; + if (u->c_type == POS) { + i = (int)u->c_fval; +/* +printf("**ARG_ARRAY(%d)*\n", i); +*/ + u = _Arg(i); + if (u->c_type == UDF) { /* fix up local array */ +/* +printf("local_var_to_array\n"); +*/ + fixarray(u); + } + } + else if (!(u->c_type & ARR)) + error("non array refference"); + arrayelm(p, str); + u = getvar(str, u->c_sval, VAR|NUM|STR); /* "rtsort in AWK book */ + return u; +} + +static +arrayelm(p, s) NODE *p; char *s; +{ + CELL *u; + int i, n; + char *t; + +/* +char *tt = s; +*/ + n = (int) p->n_arg[1] + 2; + for (i = 2; i < n; i++) { + if (i > 2) + *s++ = **SUBSEP; + u = execute(p->n_arg[i]); + for (t = getsval(u); *t; ) + *s++ = *t++; + c_free(u); + } + *s = '\0'; +/* +printf("array_elm(%s)\n", tt); +*/ +} + +CELL * +Element(p) NODE *p; +{ + char str[BUFSIZ]; + + arrayelm(p, str); + return mktmp(STR, str, 0.0); +} + +CELL * +Delete(p) NODE *p; +{ + CELL *u; + char str[BUFSIZ]; + int i; + SYMBOL *q, *r, **h; + + u = (CELL *) p->n_arg[0]; + if (!(u->c_type & ARR)) + error("can't delete non array variable"); + arrayelm(p, str); + h = (SYMBOL **) u->c_sval; + for (r = (SYMBOL *)NULL, i = hash(str), q = h[i]; q; r = q, q = q->s_next) + if (strcmp(str, q->s_name) == 0) + break; + if (q) { + sfree(q->s_val->c_sval); + sfree(q->s_name); + if (r) + r->s_next = q->s_next; + if (q == h[i]) + h[i] = (SYMBOL *)NULL; + } + return &truecell; +} + +CELL * +In(p) NODE *p; +{ + SYMBOL **h, *q; + CELL *u, *v; + char *s; + int i; + + u = (CELL *) p->n_arg[1]; /* array */ + if (!(u->c_type & ARR)) + error("%s is not an array", u->c_sval); + h = (SYMBOL **) u->c_sval; + if (u->c_sval != (char *)NULL) { + v = execute(p->n_arg[0]); /* var */ + s = getsval(v); + for (i = 0; i < HASHSIZE; i++) + for (q = h[i]; q; q = q->s_next) { + if (strcmp(s, q->s_name) == 0) { + c_free(v); + return &truecell; + } + } + c_free(v); + } + return &falsecell; +} + +CELL * +Split(p) NODE *p; +{ + CELL *u, *v, *w; + char *s, *t, *h, *name, *sep; + int i, n, skip; + char elm[8], str[BUFSIZ]; + static char *s_str; + static regexp *s_pat; + regexp *mkpat(); + extern int r_start, r_length; + + n = (int) p->n_arg[1]; + if (n > 1) { + u = execute(p->n_arg[2]); + s = getsval(u); /* str */ + v = execute(p->n_arg[3]); /* array */ + if (!(v->c_type & ARR)) { +/* +printf("Split fix_to_array(%d)\n", v->c_type); +*/ + if (v->c_type == UDF) /* fix up local array */ + fixarray(v); + else + error("split to non array variable", (char *)0); + } + h = v->c_sval; + c_free(v); + if (n > 2) { + v = execute(p->n_arg[4]); + sep = getsval(v); + } + else { + v = (CELL *)NULL; + sep = *FS; + } + if (strlen(sep) > 1) { /* reg_exp */ + if (strcmp(sep, s_str) != 0) { + sfree(s_str); sfree(s_pat); + s_str = strsave(sep); + s_pat = mkpat(s_str); + } + for (i = 0, t = str; *s; ) { + if (match(s_pat, s)) { + for (n = r_start; --n > 0; ) + *t++ = *s++; + } + else { + while(*s) + *t++ = *s++; + } + *t = '\0'; + t = str; + sprintf(elm, "%d", ++i); + w = getvar(elm, h, VAR); + if (isnum(str)) + setfval(w, atof(str)); + else + setsval(w, str); + if (*s) + s += r_length; + } + } + else { + skip = *sep == ' '; + for (i = 0; t = str, *s; ) { + if (skip) + while (jStrchr(" \t\n", *s)) + s++; + if (!(*s)) + break; + while (*s && !jStrchr(sep, *s)) { + if (isKanji(*s)) + *t++ = *s++; + *t++ = *s++; + } + *t = '\0'; + sprintf(elm, "%d", ++i); + w = getvar(elm, h, VAR); + if (isnum(str)) + setfval(w, atof(str)); + else + setsval(w, str); + if (*s && !skip) + s++; + } + } + c_free(v); /* sep */ + c_free(u); /* str may be CATed */ + } + else + i = 0; + return mktmp(NUM, (char *)NULL, (double) i); +} + +CELL * +Forin(p) NODE *p; +{ + CELL *u, *v; + SYMBOL **h, *q; + char *name; + int i; + + u = execute(p->n_arg[1]); + if (!(u->c_type & ARR)) + synerr( + "non array variable is specified in 'for (. in var)'", (char *)0); + h = (SYMBOL **) u->c_sval; + c_free(u); + u = execute(p->n_arg[0]); + if (u->c_type == UDF) { +/* +printf("Forin_fix_to_VAR|NUM\n"); +*/ + u->c_type = VAR|NUM; + } + if (!(u->c_type & VAR)) + error("'for (VAR in .)' is not variable (%d)", name, u->c_type); + for (i = 0; i < HASHSIZE; i++) { + for (q = h[i]; q; q = q->s_next) { + setsval(u, q->s_name); + v = execute(p->n_arg[2]); + c_free(v); + } + } + c_free(u); + return &truecell; +} + +char * +strsave(s) char *s; +{ + register int n; + char *emalloc(), *strcpy(); + + if (s == (char *)NULL) + return (char *)NULL; + n = strlen(s) + 1; + return strcpy(emalloc(n), s); +} + +sfree(p) char *p; +{ + if (p != (char *)NULL) + Free(p); +} + +isnum(s) char *s; +{ + char *strchr(); + + if (s == NULL || *s == '\0' || !strcmp(s, ".")) + return 0; + if (*s && strchr("+-", *s) != (char *)NULL) + s++; + if (*s == '\0') + return 0; + while (isdigit(*s)) + s++; + if (*s == '.') { + s++; + while (isdigit(*s)) + s++; + } + if (*s && strchr("eE", *s) != (char *)NULL) { + s++; + if (*s == '\0') + return 0; + if (*s && strchr("+-", *s) != (char *)NULL) + s++; + while (isdigit(*s)) + s++; + } + return *s == '\0'; +} + +setfval(u, f) CELL *u; double f; +{ + if (u->c_type == UDF) { /* fix up local var */ +/* +printf("setfval_fix_to_VAR\n"); +*/ + u->c_type |= VAR; + } + if (u->c_type & (VAR|FLD|REC|TMP)) { + u->c_type &= ~STR; + u->c_type |= NUM; + sfree(u->c_sval); + u->c_sval = (char *)NULL; + u->c_fval = f; + if (u->c_type & FLD) + mkrec(u); + } + else + fprintf(stderr, "assign to nonvariable (%d)\n", u->c_type); +} + +setsval(u, s) CELL *u; char *s; +{ + double atof(); + + if (u->c_type == UDF) { /* fix up local var */ +/* +printf("setsval_fix_to_VAR\n"); +*/ + u->c_type |= VAR; + } + if (u->c_type & (VAR|FLD|REC|TMP)) { + u->c_type &= ~NUM; + u->c_type |= STR; + sfree(u->c_sval); + u->c_sval = strsave(s); +#if 0 /* "table2" in AWK book */ + if (isnum(u->c_sval)) { + u->c_fval = atof(u->c_sval); + u->c_type |= NUM; + } + else +#endif + u->c_fval = 0.0; + if (u->c_type & FLD) + mkrec(u); + } + else + fprintf(stderr, "assign to constant (%d)\n", u->c_type); +} + +double +getfval(u) CELL *u; +{ + double x, atof(); + + if (u->c_type == UDF) { /* local var */ + u->c_type |= VAR|STR|NUM; + u->c_sval = strsave(""); + x = u->c_fval = 0.0; + } + else if (u->c_type & NUM) + x = u->c_fval; +#if 1 + else { + x = atof(u->c_sval); +#else + else { + if (isnum(u->c_sval)) + x = atof(u->c_sval); + else + x = 0.0; +#endif + } + return x; +} + +char * +getsval(u) CELL *u; +{ + char *s, str[80]; + + if (u->c_type & STR) + s = u->c_sval; + else if (u->c_type & NUM) { +/* if (u->c_fval >= -2147483648.0 && u->c_fval <= 2147483647.0)*/ + if ((long)u->c_fval == u->c_fval) + s = "%.16g"; + else + s = *OFMT; + sprintf(str, s, u->c_fval); + sfree(u->c_sval); + s = u->c_sval = strsave(str); + } +#if 1 + else if (u->c_type == UDF) { /* local var */ +/* +printf("getsval_fix_to_VAR|STR\n"); +*/ + u->c_type |= VAR|STR|NUM; + s = u->c_sval = strsave(""); + u->c_fval = 0.0; + } +#endif + else + fprintf(stderr, "abnormal value (STR|NUM == 0)(%d)\n", u->c_type); + return s; +} + +char * +emalloc(n) unsigned n; +{ + char *p; +#if 0 + char far *_fmalloc(); +#else + char *malloc(); +#endif + +#if 0 + if ((p = _fmalloc(n)) == (char *)NULL) +#else + if ((p = malloc(n)) == (char *)NULL) +#endif + error("memory over"); + return p; +} + +Free(s) char *s; +{ +#if DOS + void _ffree(); + + _ffree(s); +#else + free(s); +#endif +} diff --git a/commands/awk/y.c b/commands/awk/y.c new file mode 100755 index 000000000..d3575d2f9 --- /dev/null +++ b/commands/awk/y.c @@ -0,0 +1,1087 @@ +/* + * a small awk clone + * + * (C) 1989 Saeko Hirabauashi & Kouichi Hirabayashi + * + * Absolutely no warranty. Use this software with your own risk. + * + * Permission to use, copy, modify and distribute this software for any + * purpose and without fee is hereby granted, provided that the above + * copyright and disclaimer notice. + * + * This program was written to fit into 64K+64K memory of the Minix 1.2. + */ + + +#include +#include "awk.h" + +extern char *mkpat(); + +extern char *cmd; +extern char text[]; +extern char funnam[]; +extern int sym; +extern int sym1; +extern int regexflg; +extern int funflg; +extern int printflg; +extern int getlineflg; + +extern SYMBOL *hashtab[], *funtab[]; + +extern CELL *field[]; + +char *emalloc(), *strsave(); +NODE *node0(), *node1(), *node2(), *node3(), *node4(); +NODE *stat(), *pastat(); +NODE *expr(), *expr1(), *expr2(), *expr3(), *expr4(); +NODE *expr5(), *expr6(), *expr7(), *expr8(), *expr9(), *expr10(); +NODE *doprint(), *dofuncn(), *doif(), *dowhile(), *dofor(), *body(); +NODE *doassign(), *dodo(), *doarray(), *doreturn(), *doelement(); +CELL *mkcell(), *getvar(); +CELL *execute(), *lookup(); + +int forflg; /* parsing for(expr in array), inhibit 'expr in array' */ +int prmflg; /* parsing pass parameters */ +NODE *begin, *loop, *End; + +parse() +{ + NODE *p, *q, *r, *stat(); + CELL *u; + + lex(); + skipeol(); + while (sym) { + switch (sym) { + case BEGIN: + lex(); + begin = stat(); + break; + case END: + lex(); + if (End == NULL) + End = stat(); + else { + for (p = End; p; p = q) { + if ((q = p->n_next) == NULL) + p->n_next = stat(); + } + } + break; + case FUNC: + lex(); + dousrfun(); + break; + default: + q = loop = pastat(); + skipeol(); + while (sym && sym != BEGIN && sym != END && sym != FUNC) { + r = pastat(); + q->n_next = r; + q = r; + skipeol(); + } + break; + } + skipeol(); + } + if (begin) { + u = execute(begin); + c_free(u); + } + if (End || loop) + while (Getrec(NULL)) { + if (loop) { + u = execute(loop); + c_free(u); + } + } + if (End) { + u = execute(End); + c_free(u); + } +} + +#define MAXARG 100 +static char *argnam[MAXARG]; +static int narg; + +static +dousrfun() +{ + CELL *u; + + strcpy(funnam, text); + u = getvar(text, funtab, FUN); + lex(); + if (sym != '(') + synerr("'(' expected"); + for (lex(); sym != ')'; narg++) { + if (sym != IDENT) + synerr("argument expected"); + argnam[narg] = strsave(text); + lex(); + if (sym == ',') + lex(); + } + u->c_fval = (double) narg; + lex(); + skipeol(); + funflg++; + u->c_sval = (char *) stat(); + funflg--; + if (narg > 0) { + do { + sfree(argnam[--narg]); + } while (narg > 0); + } + skipeol(); +} + +isarg(s) char *s; +{ + int i; + + if (narg > 0) { + for (i = narg - 1; i >= 0; i--) + if (strcmp(s, argnam[i]) == 0) + break; + } + else + i = -1; + return i; +} + +/* +interactive() +{ + NODE *p, *q; + CELL *u; + + for (lex(); sym; lex()) { + p = stat(); + if (p->n_type != PRINT && !iscntl(p->n_type)) { + q = (NODE *) emalloc(sizeof(NODE) + sizeof(NODE *) * 4); + q->n_type = PRINT; + q->n_arg[0] = q->n_arg[1] = q->n_arg[3] = NULL; + q->n_arg[2] = p; + q->n_next = NULL; + p = q; + } + u = execute(p); + printf("[%g(%s)]\n", u->c_fval, u->c_sval); + c_free(u); + } + closeall(); + exit(0); +} +*/ + +static +iscntl(t) +{ + static int tab[] = { + IF, DO, WHILE, FOR, JUMP, GETLINE, 0 + }; + int i; + + for (i = 0; tab[i]; i++) + if (t == tab[i]) + break; + return tab[i]; +} + +static NODE * +pastat() +{ + NODE *p, *q, *r; + + if (sym == '{') /* action only */ + p = stat(); + else { /* exp [, expr] [{ .. }] */ + p = expr(); + if (sym == ',') { + lex(); + q = expr(); + } + else + q = NULL; + if (sym && sym != EOL) + r = stat(); + else + r = node0(PRINT0); + if (q) + p = node3(P2STAT, p, q, r); + else + p = node2(P1STAT, p, r); + } + return p; +} + +static NODE * +stat() +{ + NODE *p, *q, *r; + CELL *u, *v; + int op; + +/*printf("@stat(%d)(%s)\n", sym, text);*/ + while (sym == EOL) + lex(); + switch(sym) { + case PRINT: + p = doprint(0); + break; + case PRINTF: + p = doprint(FORMAT); + break; + case IF: + p = doif(); + break; + case WHILE: + p = dowhile(); + break; + case DO: + p = dodo(); + break; + case FOR: + p = dofor(); + break; + case RETURN: + p = doreturn(); + break; + case EXIT: + p = node2(JUMP, (NODE *)sym, (NODE *)NULL); + lex(); + if (sym == IDENT || sym == NUMBER || sym == ARG) + p->n_arg[1] = expr(); + break; + case BREAK: case CONTIN: case NEXT: + p = node1(JUMP, (NODE *)sym); + lex(); + break; + case DELETE: + lex(); + u = getvar(text, hashtab, ARR); + if (Getc() != '[') + synerr("'[' expected"); + p = doarray(u); + p->n_type = DELETE; + lex(); /* ']' */ + break; + case '{': + lex(); + skipeol(); + if (sym == '}') + p = node0(NULPROC); + else + p = q = stat(); + skipeol(); + while (sym != '}') { + r = stat(); + q->n_next = r; + q = r; + skipeol(); + } + lex(); + break; + default: + p = expr(); +#if 0 + if (sym == BINOR) { /* expr | GETLINE */ + lex(); + if (sym != GETLINE) + synerr("'GETLINE' expected"); + lex(); + if (sym == IDENT || sym == STRING || sym == ARG) { + q = expr(); + } + else + q = NULL; + p = node3(GETLINE, q, p, (NODE *)R_PIN); + } +#endif + break; + } + if (p->n_type == VALUE) + synerr("statement expected"); + return p; +} + +static +skipeol() +{ + while (sym == EOL) + lex(); +} + +static NODE * +doprint(fmt) +{ + NODE *p, *q, *r; + CELL *u; + int i, op; + int n = 0; + + printflg++; + lex(); + if (sym == '(') + lex(); + if (sym != '}' && sym != ')' && sym != EOL && sym != R_OUT && sym != R_APD + && sym != R_POUT) { + p = q = expr(); n++; + while (sym == ',') { + lex(); + skipeol(); + r = expr(); n++; + q->n_next = r; + q = r; + } + } + if (sym == ')') + lex(); + if (sym == R_OUT || sym == R_APD || sym == R_POUT) { + op = sym; + lex(); +/* q = expr10();*/ + q = expr(); /* 94-04-02 */ + } + else + q = (NODE *) (op = 0); /* stdout */ + printflg = 0; + r = (NODE *) emalloc(sizeof(*r) + sizeof(r) * (n + 3)); + r->n_type = PRINT; /* convert list to arg */ + r->n_next = NULL; + r->n_arg[0] = (NODE *) (op | fmt); + r->n_arg[1] = q; + if (n == 0) { + p = node1(VALUE, (NODE *)field[0]); + } + for (i = 2; p != NULL; i++) { + r->n_arg[i] = p; + q = p->n_next; + p->n_next = NULL; + p = q; + } + r->n_arg[i] = NULL; + return r; +} + +static NODE * +doif() +{ + NODE *p, *q, *r; + + lex(); + if (sym != '(') + synerr("'(' expected"); + lex(); + p = expr(); + if (sym != ')') + synerr("')' expected"); + lex(); + skipeol(); + q = stat(); + skipeol(); + if (sym == ELSE) { + lex(); + skipeol(); + r = stat(); + } + else + r = NULL; + return node3(IF, p, q, r); +} + +static NODE * +dowhile() +{ + NODE *p, *q; + + lex(); + if (sym != '(') + synerr("'(' expected"); + lex(); + p = stat(); + if (sym != ')') + synerr("')' expected"); + q = body(); + return node2(WHILE, p, q); +} + +static NODE * +dofor() +{ + NODE *p, *q, *r, *s; + CELL *u; + int i; + + lex(); + if (sym != '(') + synerr("'(' expected"); + lex(); + if (sym != EOL) { + forflg++; /* inhibit parsing 'expr IN array' */ + p = expr(); + forflg = 0; + } + else + p = NULL; + if (sym == IN) { + lex(); + if (sym == ARG) { +/* +printf("***FOR_IN_ARG(%d)***\n", sym); +*/ + u = mkcell(POS, NULL, (double)sym1); + q = node1(ARG, u); + } + else { + u = getvar(text, hashtab, ARR); + q = node1(VALUE, u); + } + lex(); + if (sym != ')') + synerr("')' expected"); + lex(); + skipeol(); + s = stat(); + r = node3(FORIN, p, q, s); + } + else { + if (sym != EOL) + synerr("'in' or ';' expected"); + lex(); + if (sym != EOL) + q = expr(); + else + q = NULL; + if (sym != EOL) + synerr("';' expected"); + lex(); + if (sym != ')') + r = expr(); + else + r = NULL; + if (sym != ')') + synerr("')' expected"); + s = body(); + r = node4(FOR, p, q, r, s); + } + return r; +} + +static NODE * +body() +{ + NODE *r; + + while ((sym = Getc()) == '\n' || sym == ' ' || sym == '\t') + ; + if (sym == ';') { + r = node0(NULPROC); + lex(); + } + else { + Ungetc(sym); + lex(); + r = stat(); + } + return r; +} + +static NODE * +dodo() +{ + NODE *p, *q; + + lex(); + skipeol(); + p = stat(); + skipeol(); + if (sym != WHILE) + synerr("'while' expected"); + lex(); + if (sym != '(') + synerr("'(' expected"); + lex(); + q = stat(); + if (sym != ')') + synerr("')' expected"); + lex(); + return node2(DO, p, q); +} + +static NODE * +doreturn() +{ + NODE *p, *q, *r; + int i, n = 0; + + if (lex() != EOL) { + p = q = expr(); n++; + while (sym == ',') { + lex(); skipeol(); + r = expr(); n++; + q ->n_next = r; + q = r; + } + } + else + p = (NODE *)NULL; + + r = (NODE *) emalloc(sizeof(*r) + sizeof (r) * (n + 1)); + r->n_type = JUMP; + r->n_next = NULL; + r->n_arg[0] = (NODE *) RETURN; + for (i = 1; p != NULL; i++) { + r->n_arg[i] = p; + q = p->n_next; + p->n_next = NULL; + p = q; + } + r->n_arg[i] = NULL; + return r; +} + +static NODE * +expr() +{ + NODE *p; + + p = expr1(); + if (isassign(sym)) + p = doassign(sym, p); + return p; +} + +static isassign(sym) +{ + return (sym == ASSIGN || sym == ADDEQ || sym == SUBEQ || sym == MULTEQ + || sym == DIVEQ || sym == MODEQ || sym == POWEQ); +} + +static NODE * +doassign(op, p) NODE *p; +{ /* evaluate right to left */ + NODE *q; + + lex(); + q = expr(); + if (isassign(sym)) + q = doassign(sym, q); + return node3(ASSIGN, (NODE *)op, p, q); +} + +static NODE * +expr1() +{ + NODE *p, *q; + +/* +printf("expr1(%d)(%s)\n", sym, text); +*/ + p = expr2(); + if (sym == '?') { + lex(); +#if 0 + q = stat(); + if (sym != ':') + synerr("':' expected"); + lex(); + return node3(IF, p, q, stat()); +#else + q = expr(); + if (sym != ':') + synerr("':' expected"); + lex(); + return node3(IF, p, q, expr()); +#endif + } + return p; /* 930213 */ +} + +static NODE * +expr2() +{ + NODE *p; + +/* +printf("expr2(%d)(%s)\n", sym, text); +*/ + p = expr3(); + while (sym == OR) { + lex(); + skipeol(); + p = node3(COND, (NODE *)OR, p, expr3()); + } + return p; +} + +static NODE * +expr3() +{ + NODE *p; + +/* +printf("expr3(%d)(%s)\n", sym, text); +*/ + p = expr4(); + while (sym == AND) { + lex(); + skipeol(); + p = node3(COND, (NODE *)AND, p, expr4()); + } + return p; +} + +static NODE * +expr4() +{ + NODE *p; + CELL *q; + int op; + +/* +printf("expr4(%d)(%s)\n", sym, text); +*/ + p = expr5(); + if (!forflg && sym == IN) { + lex(); + q = getvar(text, hashtab, ARR); + lex(); + return node2(IN, p, q); + } + while (sym == EQ || sym == NE || sym == LT || sym == LE || sym == GT + || sym == GE || sym == MATCH || sym == NOMATCH) { + op = sym; + lex(); + p = node3(COND, (NODE *)op, p, expr5()); + } + return p; +} + +static NODE * +expr5() +{ + NODE *p, *q; + +/* +printf("expr5(%d)(%s)\n", sym, text); +*/ + p = expr6(); + while (iscat(sym)) { + q = expr6(); + p = node2(CAT, p, q); + } + return p; +} + +static iscat(sym) +{ + static int ctab[] = { + ADD, SUB, MULT, DIV, MOD, INC, DEC, STRING, NUMBER, IDENT, '(', + MATHFUN, STRFUN, SPRINTF, '$', SUBST, ARG, CALL, 0 + }; + register int i, j; + + for (i = 0; j = ctab[i]; i++) + if (sym == j) + break; + return j; +} + +static NODE * +expr6() +{ + register int sign = sym; + NODE *p, *q; + +/* +printf("expr6(%d)(%s)\n", sym, text); +*/ + if (sym == SUB || sym == ADD) + lex(); + p = expr7(); + if (sign == SUB) + p = node2(ARITH, (NODE *)UMINUS, p); + while (sym == ADD || sym == SUB) { + sign = sym; + lex(); + q = expr7(); + if (sign == ADD) { + p = node3(ARITH, (NODE *)ADD, p, q); + } + else if (sign == SUB) { + p = node3(ARITH, (NODE *)SUB, p, q); + } + else + synerr("'+' or '-' expected"); + } + return p; +} + +static NODE * +expr7() +{ + register int op; + NODE *p, *q; + +/* +printf("expr7(%d)(%s)\n", sym, text); +*/ + p = expr8(); + while (sym == MULT || sym == DIV || sym == MOD) { + op = sym; + lex(); + q = expr8(); + switch (op) { + case MULT: p = node3(ARITH, (NODE *)MULT, p, q); break; + case DIV: p = node3(ARITH, (NODE *)DIV, p, q); break; + case MOD: p = node3(ARITH, (NODE *)MOD, p, q); break; + default: synerr("'*', '/' or '%' expected"); break; + } + } + return p; +} + +static NODE * +expr8() +{ + NODE *p; + int op; + +/* +printf("expr8(%d)(%s)\n", sym, text); +*/ + if (sym == NOT) { + lex(); + p = node2(COND, (NODE *)NOT, expr9()); + } + else { + p = expr9(); + if (sym == POWER) { + lex(); + p = node3(ARITH, (NODE *)POWER, p, expr9()); + } + } + return p; +} + +static NODE * +expr9() +{ + NODE *p, *q; + int op, sym0; + +/* +printf("expr9(%d)(%s)\n", sym, text); +*/ + if (op = isincdec(sym)) { + lex(); + if (sym != IDENT && sym != ARG) + synerr("illegal '++/--' operator"); + p = expr10(); + p = node4(ARITH, (NODE *)INCDEC, p, (NODE *)op, (NODE *)PRE); + } + else { + sym0 = sym; + p = expr10(); + if (op = isincdec(sym)) { +/*printf("POST(%d)(%d)(%s)\n", sym, sym0, text);*/ + if (sym0 == IDENT || sym0 == ARG) { + p = node4(ARITH, (NODE *)INCDEC, p, (NODE *)op, + (NODE *)POST); + lex(); + } + } + if (sym == BINOR) { /* | getline */ + lex(); + if (sym != GETLINE) + synerr("'GETLINE' expected"); + lex(); + if (sym == IDENT || sym == STRING || sym == ARG) { + q = expr(); + } + else + q = NULL; + p = node3(GETLINE, q, p, (NODE *)R_PIN); + } + } + return p; +} + +static isincdec(sym) +{ + return sym == INC ? 1 : (sym == DEC ? -1 : 0); +} + +static NODE * +expr10() +{ + NODE *p, *q; + CELL *u, *v; + int op; + int c; +int gsave, psave; + double atof(); + +/* +printf("expr10(%d)(%s)\n", sym, text); +*/ + switch (sym) { + case STRING: + u = mkcell(STR, text, 0.0); + goto g1; + case NUMBER: + u = mkcell(NUM, NULL, atof(text)); +g1: + p = node1(VALUE, u); + lex(); + break; + case IDENT: case ARG: + if ((c = Getc()) == '[') { /* array */ + /* 940403 */ + if (sym == ARG) { + u = (CELL *)emalloc(sizeof(CELL)); + u = mkcell(POS, NULL, (double)sym1); + p = doarray(u); + } + else { + u = getvar(text, hashtab, ARR); + p = doarray(u); + } + } + else { + Ungetc(c); + if (sym == ARG) { + u = mkcell(POS, NULL, (double)sym1); + p = node1(ARG, u); + } + else { /* symple variable */ + u = getvar(text, hashtab, VAR|STR|NUM); + p = node1(VALUE, u); + } + } + lex(); + break; + case '(': + /* print >(x ? y : z) needs this */ +gsave = getlineflg; psave = printflg; +getlineflg = printflg = 0; + lex(); + p = expr(); + if (sym == ',') /* (expr, expr, .. ) */ + p = doelement(p); + if (sym != ')') + synerr("')' expected"); +getlineflg = gsave; printflg = psave; + lex(); + break; + case CALL: + p = dofuncn(sym, getvar(text, funtab, UDF)); + break; + case MATHFUN: case STRFUN: case SUBST: + p = dofuncn(sym, (CELL *)sym1); + break; + case SPRINTF: + p = doprint(FORMAT|STROUT); + break; + case '$': + lex(); + switch (sym) { + case NUMBER: + u = mkcell(NUM, NULL, atof(text)); + p = node1(VALUE, u); + p = node1(FIELD, p); + lex(); + break; + case IDENT: case ARG: case '(': + p = node1(FIELD, expr10()); + break; + default: + synerr("number or identifier expected after '$'", (char *)0); + } + break; + case DIV: + regexflg++; + lex(); + regexflg = 0; + u = mkcell(PAT, NULL, 0.0); + u->c_sval = (char *) mkpat(text); + p = node1(VALUE, u); + lex(); + break; + case GETLINE: + getlineflg++; + lex(); + if (sym == IDENT || sym == STRING || sym == ARG) + q = expr10(); /* read into var */ + else + q = NULL; + getlineflg = 0; + if (sym == R_IN) { + op = R_IN; + lex(); + p = expr10(); + } + else + op = (int) (p = NULL); + p = node3(GETLINE, q, p, (NODE *)op); + break; + default: + synerr( + "identifier, number, string, argument, regexpr, call or '(' expected"); + break; + } + return p; +} + +static NODE * +dofuncn(fun, op) CELL *op; +{ + NODE *p; + int i, j; + int n = 0; + NODE *a[100]; + + if (lex() == '(') { + prmflg++; + for (lex(); sym && (sym != ')'); n++) { + if ((int)op == SPLIT && n == 1) { +/* +printf("sym(%d)sym1(%d)(%d)\n", sym, sym1, isarg(text)); +*/ + if (sym != ARG) { /*isarg(text) == -1*/ + /* make an array if not exist */ + prmflg = 0; + getvar(text, hashtab, ARR); + prmflg++; + } + } + a[n] = expr(); + if (sym == ',') + lex(); + else if (sym != ')') + synerr("',' or ')' expected"); + } + prmflg = 0; + + if (sym == ')') + lex(); + else + synerr("')' expected"); + } + p = (NODE *) emalloc(sizeof(*p) + sizeof(p) * (n + 2)); + p->n_type = fun; + p->n_next = NULL; + p->n_arg[0] = (NODE *) op; + p->n_arg[1] = (NODE *) n; + for (i = 0, j = 2; i < n; ) + p->n_arg[j++] = a[i++]; + p->n_arg[j] = NULL; + return p; +} + +static NODE * +doarray(u) CELL *u; +{ + NODE *p; + int i, j; + int n; + NODE *a[20]; + + for (lex(), n = 0; sym && sym != ']'; n++) { + a[n] = expr(); + if (sym == ',') + lex(); + } + if (sym != ']') + synerr("']' expected"); + /* left ']' for expr10() */ + p = (NODE *) emalloc(sizeof(*p) + sizeof(p) * (n + 1)); + p->n_type = ARRAY; + p->n_next = NULL; + p->n_arg[0] = (NODE *)u; + p->n_arg[1] = (NODE *) n; + for (i = 0, j = 2; i < n; ) + p->n_arg[j++] = a[i++]; + return p; +} + +static NODE * +doelement(q) NODE *q; +{ + NODE *p; + int i, j; + int n; + NODE *a[20]; + + a[0] = q; + for (lex(), n = 1; sym && sym != ')'; n++) { + a[n] = expr(); + if (sym == ',') + lex(); + else if (sym != ')') + synerr("',' or ')' expected"); + } + /* left ')' for expr10() */ + p = (NODE *) emalloc(sizeof(*p) + sizeof(p) * (n + 1)); + p->n_type = ELEMENT; + p->n_next = NULL; + p->n_arg[0] = NULL; + p->n_arg[1] = (NODE *) n; + for (i = 0, j = 2; i < n; ) + p->n_arg[j++] = a[i++]; + return p; +} + +synerr(s, t) char *s, *t; +{ + extern int lineno; + extern char line[], *linep; + int c, i; + char *u, *v; + + fprintf(stderr, "%s: Syntax error at line %d", cmd, lineno); + if (funflg) + fprintf(stderr, " in function %s", funnam); + fprintf(stderr, ":\n"); + if ((v = linep - 1) < line) + v = line + BUFSIZ - 1; + for (i = 0, u = v - 1; ; --u) { + if (u < line) { + if (line[BUFSIZ - 1] == '\0') + break; + u = line + BUFSIZ - 1; + } + if (*u == '\n' && ++i == 2) + break; + } + if (u != v) { + while (u != v) { + fputc(*u, stderr); + if ((++u - line) == BUFSIZ) + u = line; + } + if (*u != '\n') + fputc(*u, stderr); + fprintf(stderr, " <--\n\n"); +/* + fprintf(stderr, " <-- "); + while ((c = Getc()) != EOF && c != '\n') + fputc(c, stderr); + fprintf(stderr, "\n"); + if (c == EOF); + fprintf(stderr, "\n"); +*/ + } + fprintf(stderr, s, t); + fprintf(stderr, "\n"); +#ifdef DOS + closeall(); +#endif + exit(1); +} diff --git a/commands/bc/COPYING b/commands/bc/COPYING new file mode 100755 index 000000000..86cf81acb --- /dev/null +++ b/commands/bc/COPYING @@ -0,0 +1,341 @@ + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. + diff --git a/commands/bc/Makefile b/commands/bc/Makefile new file mode 100755 index 000000000..b04f1c641 --- /dev/null +++ b/commands/bc/Makefile @@ -0,0 +1,89 @@ +# Makefile for bc + +# A makefile for bc. This is part of the bc/sbc distribution. +# +# +# Make sure these have the correct directories for your machine. +# +# LIBDIR and BINDIR are where bc and libmath.b will be put. +# +PREFIX = /usr +LIBDIR = $(PREFIX)/lib +BINDIR = $(PREFIX)/bin +# +# Programs definitions for use by make. +# +SHELL = /bin/sh +YACC = yacc +#YACC = bison -y +LEX = flex -I8 +#LEX = lex +CC = cc +CFLAGS = -D_POSIX_SOURCE +LDFLAGS = -i +# +# +OFILES = scan.o util.o main.o number.o storage.o load.o execute.o +# +SUBDIRS = Examples Test +# + + +all: bc +bc: $& config.h bc.o $(OFILES) global.o + $(CC) -o bc $(LDFLAGS) bc.o $(OFILES) global.o + +sbc: sbc.o $(OFILES) global.o + $(CC) -o sbc $(LDFLAGS) sbc.o $(OFILES) global.o + +math.h: libmath.b + $(MAKE) -$(MAKEFLAGS) fbc + ./fbc -c libmath.b math.h + ./fix_math.h + rm -f ./fbc + +fbc: $(OFILES) bc.o + echo \"\" > math.h + $(CC) -c $(CFLAGS) global.c + $(CC) -o fbc $(LDFLAGS) bc.o $(OFILES) global.o + +install: $(BINDIR)/bc $(LIBDIR)/libmath.b + +$(BINDIR)/bc: bc + install -cs -o bin $? $@ + +$(LIBDIR)/libmath.b: libmath.b + install -c -o bin $? $@ + +clean: + rm -f *.o *.bak core math.h bc sbc + +scan.c: scan.l + $(LEX) scan.l + mv lex.yy.c scan.c + +scan.o: scan.c + $(CC) -c $(CFLAGS) -wa scan.c + +y.tab.h bc.c: bc.y + @echo "expect 1 shift/reduce conflict" + $(YACC) -d bc.y + mv y.tab.c bc.c + +sbc.c: sbc.y + $(YACC) -d sbc.y + mv y.tab.c sbc.c + +global.o: bcdefs.h global.h math.h +bc.o: bcdefs.h global.h +execute.o: bcdefs.h global.h +load.o: bcdefs.h global.h +main.o: bcdefs.h global.h version.h +number.o: bcdefs.h +sbc.o: bcdefs.h global.h +scan.o: y.tab.h bcdefs.h global.h +storage.o: bcdefs.h global.h +util.o: bcdefs.h global.h version.h + +bcdefs.h: number.h const.h config.h + touch bcdefs.h diff --git a/commands/bc/bc.c b/commands/bc/bc.c new file mode 100644 index 000000000..31e68e851 --- /dev/null +++ b/commands/bc/bc.c @@ -0,0 +1,1373 @@ +#ifndef lint +static char yysccsid[] = "@(#)yaccpar 1.9 (Berkeley) 02/21/93"; +#endif +#define YYBYACC 1 +#define YYMAJOR 1 +#define YYMINOR 9 +#define yyclearin (yychar=(-1)) +#define yyerrok (yyerrflag=0) +#define YYRECOVERING (yyerrflag!=0) +#define YYPREFIX "yy" +#line 2 "bc.y" +/* bc.y: The grammar for a POSIX compatable bc processor with some + extensions to the language. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "global.h" +#include "proto.h" +#line 38 "bc.y" +typedef union { + char *s_value; + char c_value; + int i_value; + arg_list *a_value; + } YYSTYPE; +#line 52 "y.tab.c" +#define NEWLINE 257 +#define AND 258 +#define OR 259 +#define NOT 260 +#define STRING 261 +#define NAME 262 +#define NUMBER 263 +#define MUL_OP 264 +#define ASSIGN_OP 265 +#define REL_OP 266 +#define INCR_DECR 267 +#define Define 268 +#define Break 269 +#define Quit 270 +#define Length 271 +#define Return 272 +#define For 273 +#define If 274 +#define While 275 +#define Sqrt 276 +#define Else 277 +#define Scale 278 +#define Ibase 279 +#define Obase 280 +#define Auto 281 +#define Read 282 +#define Warranty 283 +#define Halt 284 +#define Last 285 +#define Continue 286 +#define Print 287 +#define Limits 288 +#define UNARY_MINUS 289 +#define YYERRCODE 256 +short yylhs[] = { -1, + 0, 0, 10, 10, 10, 11, 11, 11, 11, 12, + 12, 12, 12, 12, 12, 15, 15, 13, 13, 13, + 13, 13, 13, 13, 13, 13, 13, 16, 17, 18, + 19, 13, 20, 13, 22, 23, 13, 13, 25, 13, + 24, 24, 26, 26, 21, 27, 21, 28, 14, 5, + 5, 6, 6, 6, 7, 7, 7, 7, 8, 8, + 9, 9, 9, 9, 4, 4, 2, 2, 29, 1, + 30, 1, 31, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 3, 3, 3, 3, 3, 3, +}; +short yylen[] = { 2, + 0, 2, 2, 1, 2, 0, 1, 3, 2, 0, + 1, 2, 3, 2, 3, 1, 2, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 4, 0, 0, 0, + 0, 13, 0, 7, 0, 0, 7, 3, 0, 3, + 1, 3, 1, 1, 0, 0, 3, 0, 12, 0, + 1, 0, 3, 3, 1, 3, 3, 5, 0, 1, + 1, 3, 3, 5, 0, 1, 0, 1, 0, 4, + 0, 4, 0, 4, 2, 3, 3, 3, 3, 3, + 2, 1, 1, 3, 4, 2, 2, 4, 4, 4, + 3, 1, 4, 1, 1, 1, 1, +}; +short yydefred[] = { 1, + 0, 0, 0, 21, 0, 83, 0, 0, 22, 24, + 0, 0, 28, 0, 35, 0, 0, 94, 95, 0, + 18, 25, 97, 23, 39, 19, 0, 0, 0, 0, + 0, 2, 0, 16, 4, 7, 5, 17, 0, 0, + 0, 0, 96, 86, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 81, 0, 0, 0, 11, 71, + 73, 0, 0, 0, 0, 0, 69, 87, 3, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 91, 43, 0, 40, 0, 84, + 0, 0, 38, 0, 0, 0, 0, 0, 0, 0, + 0, 8, 0, 85, 0, 93, 0, 0, 0, 88, + 27, 0, 0, 33, 0, 89, 90, 0, 13, 15, + 0, 0, 0, 62, 0, 0, 0, 0, 0, 29, + 0, 0, 42, 0, 56, 0, 0, 0, 0, 0, + 64, 0, 0, 0, 46, 34, 37, 0, 48, 58, + 30, 0, 0, 0, 0, 47, 53, 54, 0, 0, + 0, 31, 49, 0, 32, +}; +short yydgoto[] = { 1, + 30, 79, 31, 113, 108, 149, 109, 73, 74, 32, + 33, 58, 34, 35, 59, 48, 138, 155, 164, 131, + 146, 50, 132, 88, 54, 89, 152, 154, 101, 94, + 95, +}; +short yysindex[] = { 0, + -7, 58, 212, 0, -22, 0, -233, -241, 0, 0, + -8, -5, 0, -4, 0, 2, 4, 0, 0, 9, + 0, 0, 0, 0, 0, 0, 212, 212, 91, 725, + -240, 0, -29, 0, 0, 0, 0, 0, 84, 245, + 212, -57, 0, 0, 10, 212, 212, 14, 212, 16, + 212, 212, 23, 156, 0, 549, 127, -52, 0, 0, + 0, 212, 212, 212, 212, 212, 0, 0, 0, 91, + -17, 725, 24, -3, 578, -205, 562, 725, 27, 212, + 606, 212, 669, 716, 0, 0, 725, 0, 19, 0, + 91, 127, 0, 212, 212, -36, -39, -91, -91, -36, + 212, 0, 166, 0, 277, 0, -21, 36, 40, 0, + 0, 725, 28, 0, 725, 0, 0, 156, 0, 0, + 84, 540, -39, 0, -9, 725, -2, -37, -174, 0, + 127, 48, 0, 346, 0, -167, 3, 212, -185, 127, + 0, -188, 6, 37, 0, 0, 0, -205, 0, 0, + 0, 127, -42, 91, 212, 0, 0, 0, -20, 54, + 26, 0, 0, 127, 0, +}; +short yyrindex[] = { 0, + -16, 0, 0, 0, 409, 0, 0, 0, 0, 0, + 0, -58, 0, 0, 0, 0, 426, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, -50, 46, + 470, 0, 0, 0, 0, 0, 0, 0, 661, 56, + 0, 525, 0, 0, 0, 0, 59, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, -6, + 705, 7, 0, 60, 0, 61, 0, 63, 0, 49, + 0, 0, 0, 0, 0, 0, 17, 0, 78, 0, + -47, -45, 0, 0, 0, 537, 440, 620, 637, 594, + 0, 0, 0, 0, 0, 0, -33, 0, 66, 0, + 0, -19, 0, 0, 68, 0, 0, 0, 0, 0, + 667, 680, 508, 0, 705, 18, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, -31, 49, -44, 0, + 0, -40, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, 69, 0, 0, 0, 0, 0, + 13, 0, 0, 0, 0, +}; +short yygindex[] = { 0, + 958, 0, 104, -118, 0, 0, -35, 0, 0, 0, + 0, -34, 22, 0, 15, 0, 0, 0, 0, 0, + 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, + 0, +}; +#define YYTABLESIZE 1113 +short yytable[] = { 52, + 26, 129, 66, 64, 52, 65, 92, 55, 10, 57, + 55, 12, 57, 14, 45, 36, 158, 40, 52, 144, + 45, 66, 40, 38, 67, 55, 68, 57, 42, 70, + 40, 46, 28, 41, 47, 49, 160, 27, 92, 66, + 105, 51, 6, 52, 43, 18, 19, 61, 53, 76, + 61, 23, 9, 80, 66, 82, 107, 66, 63, 10, + 44, 63, 118, 85, 104, 28, 26, 111, 41, 127, + 27, 12, 93, 103, 10, 44, 128, 12, 38, 14, + 45, 134, 52, 129, 102, 136, 130, 137, 140, 142, + 135, 145, 148, 143, 162, 151, 59, 28, 150, 67, + 60, 50, 27, 68, 20, 119, 51, 65, 36, 65, + 44, 0, 153, 120, 0, 29, 133, 0, 0, 159, + 0, 0, 0, 0, 0, 0, 64, 0, 65, 0, + 28, 0, 0, 0, 0, 27, 41, 0, 0, 0, + 0, 44, 0, 0, 0, 0, 0, 0, 29, 0, + 163, 0, 139, 0, 0, 0, 0, 0, 0, 0, + 0, 147, 0, 0, 0, 0, 28, 0, 0, 0, + 20, 27, 62, 156, 0, 119, 0, 66, 0, 0, + 29, 0, 0, 0, 0, 165, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 28, 0, 0, 26, 0, + 27, 0, 41, 0, 91, 28, 10, 0, 0, 12, + 27, 14, 45, 29, 157, 52, 52, 0, 26, 52, + 52, 52, 52, 55, 62, 57, 52, 69, 52, 52, + 52, 52, 52, 52, 52, 52, 161, 52, 52, 52, + 6, 52, 52, 52, 52, 52, 52, 52, 2, 29, + 9, 28, 3, 4, 5, 6, 27, 10, 124, 7, + 8, 9, 10, 11, 12, 13, 14, 15, 16, 12, + 17, 18, 19, 44, 20, 21, 22, 23, 24, 25, + 26, 57, 0, 0, 28, 3, 4, 5, 6, 27, + 0, 0, 7, 44, 9, 10, 11, 12, 13, 14, + 15, 16, 20, 17, 18, 19, 0, 20, 21, 22, + 23, 24, 25, 26, 37, 0, 28, 3, 4, 5, + 6, 27, 20, 0, 7, 0, 9, 10, 11, 12, + 13, 14, 15, 16, 41, 17, 18, 19, 0, 20, + 21, 22, 23, 24, 25, 26, 57, 62, 0, 63, + 3, 4, 5, 6, 41, 0, 0, 7, 0, 9, + 10, 11, 12, 13, 14, 15, 16, 0, 17, 18, + 19, 0, 20, 21, 22, 23, 24, 25, 26, 0, + 0, 0, 0, 0, 0, 28, 3, 4, 5, 6, + 27, 0, 0, 7, 0, 9, 10, 11, 12, 13, + 14, 15, 16, 0, 17, 18, 19, 0, 20, 21, + 22, 23, 24, 25, 26, 3, 86, 5, 6, 0, + 0, 0, 7, 0, 0, 3, 11, 5, 6, 0, + 0, 16, 7, 17, 18, 19, 11, 20, 141, 0, + 23, 16, 0, 17, 18, 19, 0, 20, 0, 92, + 23, 92, 92, 92, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 96, 92, 96, 96, + 96, 3, 0, 5, 6, 0, 0, 0, 7, 0, + 76, 0, 11, 76, 96, 0, 0, 16, 0, 17, + 18, 19, 0, 20, 0, 0, 23, 0, 76, 0, + 0, 92, 92, 0, 3, 0, 71, 6, 0, 0, + 82, 7, 82, 82, 82, 11, 0, 0, 96, 96, + 16, 0, 17, 18, 19, 0, 20, 0, 82, 23, + 0, 0, 76, 92, 0, 0, 3, 0, 125, 6, + 0, 0, 0, 7, 0, 0, 0, 11, 70, 0, + 96, 70, 16, 0, 17, 18, 19, 0, 20, 0, + 0, 23, 82, 82, 76, 92, 70, 92, 92, 92, + 0, 0, 0, 0, 0, 0, 0, 79, 0, 79, + 79, 79, 64, 92, 65, 0, 0, 0, 0, 90, + 0, 64, 0, 65, 82, 79, 0, 0, 0, 0, + 70, 0, 110, 0, 64, 3, 65, 5, 6, 0, + 0, 0, 7, 0, 0, 0, 11, 92, 92, 0, + 64, 16, 65, 17, 18, 19, 0, 20, 0, 79, + 23, 0, 70, 66, 80, 0, 80, 80, 80, 0, + 0, 0, 66, 0, 0, 0, 114, 0, 64, 92, + 65, 0, 80, 0, 0, 66, 0, 0, 0, 0, + 77, 79, 77, 77, 77, 92, 92, 92, 0, 0, + 106, 66, 92, 92, 92, 92, 0, 78, 77, 78, + 78, 78, 96, 96, 96, 92, 80, 0, 0, 96, + 96, 96, 96, 0, 0, 78, 76, 76, 76, 66, + 0, 75, 96, 0, 75, 76, 0, 72, 0, 116, + 72, 64, 77, 65, 0, 0, 76, 0, 80, 75, + 74, 0, 0, 74, 0, 72, 82, 82, 82, 78, + 0, 0, 0, 82, 0, 82, 0, 0, 74, 0, + 0, 0, 0, 0, 77, 92, 82, 92, 92, 92, + 0, 0, 0, 75, 0, 0, 117, 0, 64, 72, + 65, 78, 66, 0, 70, 70, 70, 64, 0, 65, + 0, 0, 74, 70, 0, 0, 0, 0, 0, 0, + 0, 92, 92, 92, 70, 75, 0, 0, 92, 0, + 92, 72, 0, 79, 79, 79, 0, 60, 92, 0, + 79, 92, 79, 62, 74, 63, 60, 61, 0, 66, + 0, 0, 62, 79, 63, 0, 0, 0, 66, 60, + 61, 0, 0, 0, 0, 62, 0, 63, 0, 0, + 0, 0, 0, 0, 0, 60, 61, 0, 0, 0, + 0, 62, 0, 63, 0, 0, 0, 0, 0, 0, + 80, 80, 80, 0, 0, 0, 0, 80, 0, 80, + 0, 0, 0, 60, 61, 0, 0, 0, 0, 62, + 80, 63, 0, 0, 0, 0, 77, 77, 77, 0, + 0, 0, 0, 0, 0, 77, 0, 0, 0, 0, + 0, 0, 0, 78, 78, 78, 77, 0, 0, 0, + 0, 0, 78, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 78, 0, 0, 0, 75, 75, 75, + 0, 0, 0, 72, 72, 72, 60, 61, 0, 0, + 0, 0, 62, 0, 63, 0, 74, 75, 74, 0, + 0, 0, 0, 72, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 74, 0, 0, 0, + 39, 0, 92, 92, 0, 0, 0, 0, 92, 92, + 92, 92, 0, 60, 61, 0, 0, 0, 0, 62, + 0, 63, 60, 61, 55, 56, 0, 0, 62, 0, + 63, 0, 0, 0, 0, 0, 0, 72, 75, 0, + 0, 0, 0, 77, 78, 0, 81, 0, 83, 84, + 0, 87, 0, 0, 0, 0, 0, 0, 0, 96, + 97, 98, 99, 100, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 112, 0, 115, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 121, 122, 0, 0, 0, 0, 0, 123, 0, + 75, 0, 126, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 87, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 75, 0, 0, 0, 112, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 112, +}; +short yycheck[] = { 40, + 59, 44, 94, 43, 45, 45, 59, 41, 59, 41, + 44, 59, 44, 59, 59, 1, 59, 40, 59, 138, + 262, 41, 40, 2, 265, 59, 267, 59, 262, 59, + 40, 40, 40, 91, 40, 40, 155, 45, 59, 59, + 44, 40, 59, 40, 278, 279, 280, 41, 40, 40, + 44, 285, 59, 40, 94, 40, 262, 94, 41, 59, + 44, 44, 44, 41, 41, 40, 125, 41, 91, 91, + 45, 59, 125, 91, 125, 59, 41, 125, 57, 125, + 125, 91, 123, 44, 70, 123, 59, 262, 41, 257, + 93, 277, 281, 91, 41, 59, 41, 40, 93, 41, + 41, 41, 45, 41, 59, 91, 41, 59, 41, 41, + 7, -1, 148, 92, -1, 123, 118, -1, -1, 154, + -1, -1, -1, -1, -1, -1, 43, -1, 45, -1, + 40, -1, -1, -1, -1, 45, 59, -1, -1, -1, + -1, 125, -1, -1, -1, -1, -1, -1, 123, -1, + 125, -1, 131, -1, -1, -1, -1, -1, -1, -1, + -1, 140, -1, -1, -1, -1, 40, -1, -1, -1, + 125, 45, 264, 152, -1, 161, -1, 94, -1, -1, + 123, -1, -1, -1, -1, 164, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 40, -1, -1, 257, -1, + 45, -1, 125, -1, 257, 40, 257, -1, -1, 257, + 45, 257, 257, 123, 257, 256, 257, -1, 277, 260, + 261, 262, 263, 257, 264, 257, 267, 257, 269, 270, + 271, 272, 273, 274, 275, 276, 257, 278, 279, 280, + 257, 282, 283, 284, 285, 286, 287, 288, 256, 123, + 257, 40, 260, 261, 262, 263, 45, 257, 93, 267, + 268, 269, 270, 271, 272, 273, 274, 275, 276, 257, + 278, 279, 280, 257, 282, 283, 284, 285, 286, 287, + 288, 256, -1, -1, 40, 260, 261, 262, 263, 45, + -1, -1, 267, 277, 269, 270, 271, 272, 273, 274, + 275, 276, 257, 278, 279, 280, -1, 282, 283, 284, + 285, 286, 287, 288, 257, -1, 40, 260, 261, 262, + 263, 45, 277, -1, 267, -1, 269, 270, 271, 272, + 273, 274, 275, 276, 257, 278, 279, 280, -1, 282, + 283, 284, 285, 286, 287, 288, 256, 264, -1, 266, + 260, 261, 262, 263, 277, -1, -1, 267, -1, 269, + 270, 271, 272, 273, 274, 275, 276, -1, 278, 279, + 280, -1, 282, 283, 284, 285, 286, 287, 288, -1, + -1, -1, -1, -1, -1, 40, 260, 261, 262, 263, + 45, -1, -1, 267, -1, 269, 270, 271, 272, 273, + 274, 275, 276, -1, 278, 279, 280, -1, 282, 283, + 284, 285, 286, 287, 288, 260, 261, 262, 263, -1, + -1, -1, 267, -1, -1, 260, 271, 262, 263, -1, + -1, 276, 267, 278, 279, 280, 271, 282, 93, -1, + 285, 276, -1, 278, 279, 280, -1, 282, -1, 41, + 285, 43, 44, 45, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 41, 59, 43, 44, + 45, 260, -1, 262, 263, -1, -1, -1, 267, -1, + 41, -1, 271, 44, 59, -1, -1, 276, -1, 278, + 279, 280, -1, 282, -1, -1, 285, -1, 59, -1, + -1, 93, 94, -1, 260, -1, 262, 263, -1, -1, + 41, 267, 43, 44, 45, 271, -1, -1, 93, 94, + 276, -1, 278, 279, 280, -1, 282, -1, 59, 285, + -1, -1, 93, 125, -1, -1, 260, -1, 262, 263, + -1, -1, -1, 267, -1, -1, -1, 271, 41, -1, + 125, 44, 276, -1, 278, 279, 280, -1, 282, -1, + -1, 285, 93, 94, 125, 41, 59, 43, 44, 45, + -1, -1, -1, -1, -1, -1, -1, 41, -1, 43, + 44, 45, 43, 59, 45, -1, -1, -1, -1, 41, + -1, 43, -1, 45, 125, 59, -1, -1, -1, -1, + 93, -1, 41, -1, 43, 260, 45, 262, 263, -1, + -1, -1, 267, -1, -1, -1, 271, 93, 94, -1, + 43, 276, 45, 278, 279, 280, -1, 282, -1, 93, + 285, -1, 125, 94, 41, -1, 43, 44, 45, -1, + -1, -1, 94, -1, -1, -1, 41, -1, 43, 125, + 45, -1, 59, -1, -1, 94, -1, -1, -1, -1, + 41, 125, 43, 44, 45, 257, 258, 259, -1, -1, + 93, 94, 264, 265, 266, 267, -1, 41, 59, 43, + 44, 45, 257, 258, 259, 277, 93, -1, -1, 264, + 265, 266, 267, -1, -1, 59, 257, 258, 259, 94, + -1, 41, 277, -1, 44, 266, -1, 41, -1, 41, + 44, 43, 93, 45, -1, -1, 277, -1, 125, 59, + 41, -1, -1, 44, -1, 59, 257, 258, 259, 93, + -1, -1, -1, 264, -1, 266, -1, -1, 59, -1, + -1, -1, -1, -1, 125, 41, 277, 43, 44, 45, + -1, -1, -1, 93, -1, -1, 41, -1, 43, 93, + 45, 125, 94, -1, 257, 258, 259, 43, -1, 45, + -1, -1, 93, 266, -1, -1, -1, -1, -1, -1, + -1, 257, 258, 259, 277, 125, -1, -1, 264, -1, + 266, 125, -1, 257, 258, 259, -1, 258, 94, -1, + 264, 277, 266, 264, 125, 266, 258, 259, -1, 94, + -1, -1, 264, 277, 266, -1, -1, -1, 94, 258, + 259, -1, -1, -1, -1, 264, -1, 266, -1, -1, + -1, -1, -1, -1, -1, 258, 259, -1, -1, -1, + -1, 264, -1, 266, -1, -1, -1, -1, -1, -1, + 257, 258, 259, -1, -1, -1, -1, 264, -1, 266, + -1, -1, -1, 258, 259, -1, -1, -1, -1, 264, + 277, 266, -1, -1, -1, -1, 257, 258, 259, -1, + -1, -1, -1, -1, -1, 266, -1, -1, -1, -1, + -1, -1, -1, 257, 258, 259, 277, -1, -1, -1, + -1, -1, 266, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, 277, -1, -1, -1, 257, 258, 259, + -1, -1, -1, 257, 258, 259, 258, 259, -1, -1, + -1, -1, 264, -1, 266, -1, 257, 277, 259, -1, + -1, -1, -1, 277, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, 277, -1, -1, -1, + 3, -1, 258, 259, -1, -1, -1, -1, 264, 265, + 266, 267, -1, 258, 259, -1, -1, -1, -1, 264, + -1, 266, 258, 259, 27, 28, -1, -1, 264, -1, + 266, -1, -1, -1, -1, -1, -1, 40, 41, -1, + -1, -1, -1, 46, 47, -1, 49, -1, 51, 52, + -1, 54, -1, -1, -1, -1, -1, -1, -1, 62, + 63, 64, 65, 66, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, 80, -1, 82, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 94, 95, -1, -1, -1, -1, -1, 101, -1, + 103, -1, 105, -1, -1, -1, -1, -1, -1, -1, + -1, -1, -1, -1, -1, 118, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, 134, -1, -1, -1, 138, -1, -1, -1, -1, + -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, + -1, -1, 155, +}; +#define YYFINAL 1 +#ifndef YYDEBUG +#define YYDEBUG 0 +#endif +#define YYMAXTOKEN 289 +#if YYDEBUG +char *yyname[] = { +"end-of-file",0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,"'('","')'",0,"'+'","','","'-'",0,0,0,0,0,0,0,0,0,0,0,0,0,"';'",0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'['",0,"']'","'^'",0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,"'{'",0,"'}'",0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, +0,0,0,0,0,"NEWLINE","AND","OR","NOT","STRING","NAME","NUMBER","MUL_OP", +"ASSIGN_OP","REL_OP","INCR_DECR","Define","Break","Quit","Length","Return", +"For","If","While","Sqrt","Else","Scale","Ibase","Obase","Auto","Read", +"Warranty","Halt","Last","Continue","Print","Limits","UNARY_MINUS", +}; +char *yyrule[] = { +"$accept : program", +"program :", +"program : program input_item", +"input_item : semicolon_list NEWLINE", +"input_item : function", +"input_item : error NEWLINE", +"semicolon_list :", +"semicolon_list : statement_or_error", +"semicolon_list : semicolon_list ';' statement_or_error", +"semicolon_list : semicolon_list ';'", +"statement_list :", +"statement_list : statement_or_error", +"statement_list : statement_list NEWLINE", +"statement_list : statement_list NEWLINE statement_or_error", +"statement_list : statement_list ';'", +"statement_list : statement_list ';' statement", +"statement_or_error : statement", +"statement_or_error : error statement", +"statement : Warranty", +"statement : Limits", +"statement : expression", +"statement : STRING", +"statement : Break", +"statement : Continue", +"statement : Quit", +"statement : Halt", +"statement : Return", +"statement : Return '(' return_expression ')'", +"$$1 :", +"$$2 :", +"$$3 :", +"$$4 :", +"statement : For $$1 '(' opt_expression ';' $$2 opt_expression ';' $$3 opt_expression ')' $$4 statement", +"$$5 :", +"statement : If '(' expression ')' $$5 statement opt_else", +"$$6 :", +"$$7 :", +"statement : While $$6 '(' expression $$7 ')' statement", +"statement : '{' statement_list '}'", +"$$8 :", +"statement : Print $$8 print_list", +"print_list : print_element", +"print_list : print_element ',' print_list", +"print_element : STRING", +"print_element : expression", +"opt_else :", +"$$9 :", +"opt_else : Else $$9 statement", +"$$10 :", +"function : Define NAME '(' opt_parameter_list ')' '{' NEWLINE opt_auto_define_list $$10 statement_list NEWLINE '}'", +"opt_parameter_list :", +"opt_parameter_list : define_list", +"opt_auto_define_list :", +"opt_auto_define_list : Auto define_list NEWLINE", +"opt_auto_define_list : Auto define_list ';'", +"define_list : NAME", +"define_list : NAME '[' ']'", +"define_list : define_list ',' NAME", +"define_list : define_list ',' NAME '[' ']'", +"opt_argument_list :", +"opt_argument_list : argument_list", +"argument_list : expression", +"argument_list : NAME '[' ']'", +"argument_list : argument_list ',' expression", +"argument_list : argument_list ',' NAME '[' ']'", +"opt_expression :", +"opt_expression : expression", +"return_expression :", +"return_expression : expression", +"$$11 :", +"expression : named_expression ASSIGN_OP $$11 expression", +"$$12 :", +"expression : expression AND $$12 expression", +"$$13 :", +"expression : expression OR $$13 expression", +"expression : NOT expression", +"expression : expression REL_OP expression", +"expression : expression '+' expression", +"expression : expression '-' expression", +"expression : expression MUL_OP expression", +"expression : expression '^' expression", +"expression : '-' expression", +"expression : named_expression", +"expression : NUMBER", +"expression : '(' expression ')'", +"expression : NAME '(' opt_argument_list ')'", +"expression : INCR_DECR named_expression", +"expression : named_expression INCR_DECR", +"expression : Length '(' expression ')'", +"expression : Sqrt '(' expression ')'", +"expression : Scale '(' expression ')'", +"expression : Read '(' ')'", +"named_expression : NAME", +"named_expression : NAME '[' expression ']'", +"named_expression : Ibase", +"named_expression : Obase", +"named_expression : Scale", +"named_expression : Last", +}; +#endif +#ifdef YYSTACKSIZE +#undef YYMAXDEPTH +#define YYMAXDEPTH YYSTACKSIZE +#else +#ifdef YYMAXDEPTH +#define YYSTACKSIZE YYMAXDEPTH +#else +#define YYSTACKSIZE 500 +#define YYMAXDEPTH 500 +#endif +#endif +int yydebug; +int yynerrs; +int yyerrflag; +int yychar; +short *yyssp; +YYSTYPE *yyvsp; +YYSTYPE yyval; +YYSTYPE yylval; +short yyss[YYSTACKSIZE]; +YYSTYPE yyvs[YYSTACKSIZE]; +#define yystacksize YYSTACKSIZE +#define YYABORT goto yyabort +#define YYREJECT goto yyabort +#define YYACCEPT goto yyaccept +#define YYERROR goto yyerrlab +int +yyparse() +{ + register int yym, yyn, yystate; +#if YYDEBUG + register char *yys; + extern char *getenv(); + + if (yys = getenv("YYDEBUG")) + { + yyn = *yys; + if (yyn >= '0' && yyn <= '9') + yydebug = yyn - '0'; + } +#endif + + yynerrs = 0; + yyerrflag = 0; + yychar = (-1); + + yyssp = yyss; + yyvsp = yyvs; + *yyssp = yystate = 0; + +yyloop: + if (yyn = yydefred[yystate]) goto yyreduce; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + } + if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, shifting to state %d\n", + YYPREFIX, yystate, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + yychar = (-1); + if (yyerrflag > 0) --yyerrflag; + goto yyloop; + } + if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yychar) + { + yyn = yytable[yyn]; + goto yyreduce; + } + if (yyerrflag) goto yyinrecovery; +#ifdef lint + goto yynewerror; +#endif +yynewerror: + yyerror("syntax error"); +#ifdef lint + goto yyerrlab; +#endif +yyerrlab: + ++yynerrs; +yyinrecovery: + if (yyerrflag < 3) + { + yyerrflag = 3; + for (;;) + { + if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, error recovery shifting\ + to state %d\n", YYPREFIX, *yyssp, yytable[yyn]); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate = yytable[yyn]; + *++yyvsp = yylval; + goto yyloop; + } + else + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: error recovery discarding state %d\n", + YYPREFIX, *yyssp); +#endif + if (yyssp <= yyss) goto yyabort; + --yyssp; + --yyvsp; + } + } + } + else + { + if (yychar == 0) goto yyabort; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, error recovery discards token %d (%s)\n", + YYPREFIX, yystate, yychar, yys); + } +#endif + yychar = (-1); + goto yyloop; + } +yyreduce: +#if YYDEBUG + if (yydebug) + printf("%sdebug: state %d, reducing by rule %d (%s)\n", + YYPREFIX, yystate, yyn, yyrule[yyn]); +#endif + yym = yylen[yyn]; + yyval = yyvsp[1-yym]; + switch (yyn) + { +case 1: +#line 106 "bc.y" +{ + yyval.i_value = 0; + if (interactive) + { + printf ("%s\n", BC_VERSION); + welcome (); + } + } +break; +case 3: +#line 117 "bc.y" +{ run_code (); } +break; +case 4: +#line 119 "bc.y" +{ run_code (); } +break; +case 5: +#line 121 "bc.y" +{ + yyerrok; + init_gen (); + } +break; +case 6: +#line 127 "bc.y" +{ yyval.i_value = 0; } +break; +case 10: +#line 133 "bc.y" +{ yyval.i_value = 0; } +break; +case 17: +#line 142 "bc.y" +{ yyval.i_value = yyvsp[0].i_value; } +break; +case 18: +#line 145 "bc.y" +{ warranty (""); } +break; +case 19: +#line 147 "bc.y" +{ limits (); } +break; +case 20: +#line 149 "bc.y" +{ + if (yyvsp[0].i_value & 2) + warn ("comparison in expression"); + if (yyvsp[0].i_value & 1) + generate ("W"); + else + generate ("p"); + } +break; +case 21: +#line 158 "bc.y" +{ + yyval.i_value = 0; + generate ("w"); + generate (yyvsp[0].s_value); + free (yyvsp[0].s_value); + } +break; +case 22: +#line 165 "bc.y" +{ + if (break_label == 0) + yyerror ("Break outside a for/while"); + else + { + sprintf (genstr, "J%1d:", break_label); + generate (genstr); + } + } +break; +case 23: +#line 175 "bc.y" +{ + warn ("Continue statement"); + if (continue_label == 0) + yyerror ("Continue outside a for"); + else + { + sprintf (genstr, "J%1d:", continue_label); + generate (genstr); + } + } +break; +case 24: +#line 186 "bc.y" +{ exit (0); } +break; +case 25: +#line 188 "bc.y" +{ generate ("h"); } +break; +case 26: +#line 190 "bc.y" +{ generate ("0R"); } +break; +case 27: +#line 192 "bc.y" +{ generate ("R"); } +break; +case 28: +#line 194 "bc.y" +{ + yyvsp[0].i_value = break_label; + break_label = next_label++; + } +break; +case 29: +#line 199 "bc.y" +{ + if (yyvsp[-1].i_value > 1) + warn ("Comparison in first for expression"); + yyvsp[-1].i_value = next_label++; + if (yyvsp[-1].i_value < 0) + sprintf (genstr, "N%1d:", yyvsp[-1].i_value); + else + sprintf (genstr, "pN%1d:", yyvsp[-1].i_value); + generate (genstr); + } +break; +case 30: +#line 210 "bc.y" +{ + if (yyvsp[-1].i_value < 0) generate ("1"); + yyvsp[-1].i_value = next_label++; + sprintf (genstr, "B%1d:J%1d:", yyvsp[-1].i_value, break_label); + generate (genstr); + yyval.i_value = continue_label; + continue_label = next_label++; + sprintf (genstr, "N%1d:", continue_label); + generate (genstr); + } +break; +case 31: +#line 221 "bc.y" +{ + if (yyvsp[-1].i_value > 1) + warn ("Comparison in third for expression"); + if (yyvsp[-1].i_value < 0) + sprintf (genstr, "J%1d:N%1d:", yyvsp[-7].i_value, yyvsp[-4].i_value); + else + sprintf (genstr, "pJ%1d:N%1d:", yyvsp[-7].i_value, yyvsp[-4].i_value); + generate (genstr); + } +break; +case 32: +#line 231 "bc.y" +{ + sprintf (genstr, "J%1d:N%1d:", + continue_label, break_label); + generate (genstr); + break_label = yyvsp[-12].i_value; + continue_label = yyvsp[-4].i_value; + } +break; +case 33: +#line 239 "bc.y" +{ + yyvsp[-1].i_value = if_label; + if_label = next_label++; + sprintf (genstr, "Z%1d:", if_label); + generate (genstr); + } +break; +case 34: +#line 246 "bc.y" +{ + sprintf (genstr, "N%1d:", if_label); + generate (genstr); + if_label = yyvsp[-4].i_value; + } +break; +case 35: +#line 252 "bc.y" +{ + yyvsp[0].i_value = next_label++; + sprintf (genstr, "N%1d:", yyvsp[0].i_value); + generate (genstr); + } +break; +case 36: +#line 258 "bc.y" +{ + yyvsp[0].i_value = break_label; + break_label = next_label++; + sprintf (genstr, "Z%1d:", break_label); + generate (genstr); + } +break; +case 37: +#line 265 "bc.y" +{ + sprintf (genstr, "J%1d:N%1d:", yyvsp[-6].i_value, break_label); + generate (genstr); + break_label = yyvsp[-3].i_value; + } +break; +case 38: +#line 271 "bc.y" +{ yyval.i_value = 0; } +break; +case 39: +#line 273 "bc.y" +{ warn ("print statement"); } +break; +case 43: +#line 280 "bc.y" +{ + generate ("O"); + generate (yyvsp[0].s_value); + free (yyvsp[0].s_value); + } +break; +case 44: +#line 286 "bc.y" +{ generate ("P"); } +break; +case 46: +#line 290 "bc.y" +{ + warn ("else clause in if statement"); + yyvsp[0].i_value = next_label++; + sprintf (genstr, "J%d:N%1d:", yyvsp[0].i_value, if_label); + generate (genstr); + if_label = yyvsp[0].i_value; + } +break; +case 48: +#line 300 "bc.y" +{ + /* Check auto list against parameter list? */ + check_params (yyvsp[-4].a_value,yyvsp[0].a_value); + sprintf (genstr, "F%d,%s.%s[", lookup(yyvsp[-6].s_value,FUNCT), + arg_str (yyvsp[-4].a_value,TRUE), arg_str (yyvsp[0].a_value,TRUE)); + generate (genstr); + free_args (yyvsp[-4].a_value); + free_args (yyvsp[0].a_value); + yyvsp[-7].i_value = next_label; + next_label = 0; + } +break; +case 49: +#line 312 "bc.y" +{ + generate ("0R]"); + next_label = yyvsp[-11].i_value; + } +break; +case 50: +#line 318 "bc.y" +{ yyval.a_value = NULL; } +break; +case 52: +#line 322 "bc.y" +{ yyval.a_value = NULL; } +break; +case 53: +#line 324 "bc.y" +{ yyval.a_value = yyvsp[-1].a_value; } +break; +case 54: +#line 326 "bc.y" +{ yyval.a_value = yyvsp[-1].a_value; } +break; +case 55: +#line 329 "bc.y" +{ yyval.a_value = nextarg (NULL, lookup (yyvsp[0].s_value,SIMPLE)); } +break; +case 56: +#line 331 "bc.y" +{ yyval.a_value = nextarg (NULL, lookup (yyvsp[-2].s_value,ARRAY)); } +break; +case 57: +#line 333 "bc.y" +{ yyval.a_value = nextarg (yyvsp[-2].a_value, lookup (yyvsp[0].s_value,SIMPLE)); } +break; +case 58: +#line 335 "bc.y" +{ yyval.a_value = nextarg (yyvsp[-4].a_value, lookup (yyvsp[-2].s_value,ARRAY)); } +break; +case 59: +#line 338 "bc.y" +{ yyval.a_value = NULL; } +break; +case 61: +#line 342 "bc.y" +{ + if (yyvsp[0].i_value > 1) warn ("comparison in argument"); + yyval.a_value = nextarg (NULL,0); + } +break; +case 62: +#line 347 "bc.y" +{ + sprintf (genstr, "K%d:", -lookup (yyvsp[-2].s_value,ARRAY)); + generate (genstr); + yyval.a_value = nextarg (NULL,1); + } +break; +case 63: +#line 353 "bc.y" +{ + if (yyvsp[0].i_value > 1) warn ("comparison in argument"); + yyval.a_value = nextarg (yyvsp[-2].a_value,0); + } +break; +case 64: +#line 358 "bc.y" +{ + sprintf (genstr, "K%d:", -lookup (yyvsp[-2].s_value,ARRAY)); + generate (genstr); + yyval.a_value = nextarg (yyvsp[-4].a_value,1); + } +break; +case 65: +#line 365 "bc.y" +{ + yyval.i_value = -1; + warn ("Missing expression in for statement"); + } +break; +case 67: +#line 372 "bc.y" +{ + yyval.i_value = 0; + generate ("0"); + } +break; +case 68: +#line 377 "bc.y" +{ + if (yyvsp[0].i_value > 1) + warn ("comparison in return expresion"); + } +break; +case 69: +#line 383 "bc.y" +{ + if (yyvsp[0].c_value != '=') + { + if (yyvsp[-1].i_value < 0) + sprintf (genstr, "DL%d:", -yyvsp[-1].i_value); + else + sprintf (genstr, "l%d:", yyvsp[-1].i_value); + generate (genstr); + } + } +break; +case 70: +#line 394 "bc.y" +{ + if (yyvsp[0].i_value > 1) warn("comparison in assignment"); + if (yyvsp[-2].c_value != '=') + { + sprintf (genstr, "%c", yyvsp[-2].c_value); + generate (genstr); + } + if (yyvsp[-3].i_value < 0) + sprintf (genstr, "S%d:", -yyvsp[-3].i_value); + else + sprintf (genstr, "s%d:", yyvsp[-3].i_value); + generate (genstr); + yyval.i_value = 0; + } +break; +case 71: +#line 410 "bc.y" +{ + warn("&& operator"); + yyvsp[0].i_value = next_label++; + sprintf (genstr, "DZ%d:p", yyvsp[0].i_value); + generate (genstr); + } +break; +case 72: +#line 417 "bc.y" +{ + sprintf (genstr, "DZ%d:p1N%d:", yyvsp[-2].i_value, yyvsp[-2].i_value); + generate (genstr); + yyval.i_value = yyvsp[-3].i_value | yyvsp[0].i_value; + } +break; +case 73: +#line 423 "bc.y" +{ + warn("|| operator"); + yyvsp[0].i_value = next_label++; + sprintf (genstr, "B%d:", yyvsp[0].i_value); + generate (genstr); + } +break; +case 74: +#line 430 "bc.y" +{ + int tmplab; + tmplab = next_label++; + sprintf (genstr, "B%d:0J%d:N%d:1N%d:", + yyvsp[-2].i_value, tmplab, yyvsp[-2].i_value, tmplab); + generate (genstr); + yyval.i_value = yyvsp[-3].i_value | yyvsp[0].i_value; + } +break; +case 75: +#line 439 "bc.y" +{ + yyval.i_value = yyvsp[0].i_value; + warn("! operator"); + generate ("!"); + } +break; +case 76: +#line 445 "bc.y" +{ + yyval.i_value = 3; + switch (*(yyvsp[-1].s_value)) + { + case '=': + generate ("="); + break; + + case '!': + generate ("#"); + break; + + case '<': + if (yyvsp[-1].s_value[1] == '=') + generate ("{"); + else + generate ("<"); + break; + + case '>': + if (yyvsp[-1].s_value[1] == '=') + generate ("}"); + else + generate (">"); + break; + } + } +break; +case 77: +#line 473 "bc.y" +{ + generate ("+"); + yyval.i_value = yyvsp[-2].i_value | yyvsp[0].i_value; + } +break; +case 78: +#line 478 "bc.y" +{ + generate ("-"); + yyval.i_value = yyvsp[-2].i_value | yyvsp[0].i_value; + } +break; +case 79: +#line 483 "bc.y" +{ + genstr[0] = yyvsp[-1].c_value; + genstr[1] = 0; + generate (genstr); + yyval.i_value = yyvsp[-2].i_value | yyvsp[0].i_value; + } +break; +case 80: +#line 490 "bc.y" +{ + generate ("^"); + yyval.i_value = yyvsp[-2].i_value | yyvsp[0].i_value; + } +break; +case 81: +#line 495 "bc.y" +{ + generate ("n"); + yyval.i_value = yyvsp[0].i_value; + } +break; +case 82: +#line 500 "bc.y" +{ + yyval.i_value = 1; + if (yyvsp[0].i_value < 0) + sprintf (genstr, "L%d:", -yyvsp[0].i_value); + else + sprintf (genstr, "l%d:", yyvsp[0].i_value); + generate (genstr); + } +break; +case 83: +#line 509 "bc.y" +{ + int len = strlen(yyvsp[0].s_value); + yyval.i_value = 1; + if (len == 1 && *yyvsp[0].s_value == '0') + generate ("0"); + else if (len == 1 && *yyvsp[0].s_value == '1') + generate ("1"); + else + { + generate ("K"); + generate (yyvsp[0].s_value); + generate (":"); + } + free (yyvsp[0].s_value); + } +break; +case 84: +#line 525 "bc.y" +{ yyval.i_value = yyvsp[-1].i_value | 1; } +break; +case 85: +#line 527 "bc.y" +{ + yyval.i_value = 1; + if (yyvsp[-1].a_value != NULL) + { + sprintf (genstr, "C%d,%s:", + lookup (yyvsp[-3].s_value,FUNCT), + arg_str (yyvsp[-1].a_value,FALSE)); + free_args (yyvsp[-1].a_value); + } + else + { + sprintf (genstr, "C%d:", lookup (yyvsp[-3].s_value,FUNCT)); + } + generate (genstr); + } +break; +case 86: +#line 543 "bc.y" +{ + yyval.i_value = 1; + if (yyvsp[0].i_value < 0) + { + if (yyvsp[-1].c_value == '+') + sprintf (genstr, "DA%d:L%d:", -yyvsp[0].i_value, -yyvsp[0].i_value); + else + sprintf (genstr, "DM%d:L%d:", -yyvsp[0].i_value, -yyvsp[0].i_value); + } + else + { + if (yyvsp[-1].c_value == '+') + sprintf (genstr, "i%d:l%d:", yyvsp[0].i_value, yyvsp[0].i_value); + else + sprintf (genstr, "d%d:l%d:", yyvsp[0].i_value, yyvsp[0].i_value); + } + generate (genstr); + } +break; +case 87: +#line 562 "bc.y" +{ + yyval.i_value = 1; + if (yyvsp[-1].i_value < 0) + { + sprintf (genstr, "DL%d:x", -yyvsp[-1].i_value); + generate (genstr); + if (yyvsp[0].c_value == '+') + sprintf (genstr, "A%d:", -yyvsp[-1].i_value); + else + sprintf (genstr, "M%d:", -yyvsp[-1].i_value); + } + else + { + sprintf (genstr, "l%d:", yyvsp[-1].i_value); + generate (genstr); + if (yyvsp[0].c_value == '+') + sprintf (genstr, "i%d:", yyvsp[-1].i_value); + else + sprintf (genstr, "d%d:", yyvsp[-1].i_value); + } + generate (genstr); + } +break; +case 88: +#line 585 "bc.y" +{ generate ("cL"); yyval.i_value = 1;} +break; +case 89: +#line 587 "bc.y" +{ generate ("cR"); yyval.i_value = 1;} +break; +case 90: +#line 589 "bc.y" +{ generate ("cS"); yyval.i_value = 1;} +break; +case 91: +#line 591 "bc.y" +{ + warn ("read function"); + generate ("cI"); yyval.i_value = 1; + } +break; +case 92: +#line 597 "bc.y" +{ yyval.i_value = lookup(yyvsp[0].s_value,SIMPLE); } +break; +case 93: +#line 599 "bc.y" +{ + if (yyvsp[-1].i_value > 1) warn("comparison in subscript"); + yyval.i_value = lookup(yyvsp[-3].s_value,ARRAY); + } +break; +case 94: +#line 604 "bc.y" +{ yyval.i_value = 0; } +break; +case 95: +#line 606 "bc.y" +{ yyval.i_value = 1; } +break; +case 96: +#line 608 "bc.y" +{ yyval.i_value = 2; } +break; +case 97: +#line 610 "bc.y" +{ yyval.i_value = 3; } +break; +#line 1318 "y.tab.c" + } + yyssp -= yym; + yystate = *yyssp; + yyvsp -= yym; + yym = yylhs[yyn]; + if (yystate == 0 && yym == 0) + { +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state 0 to\ + state %d\n", YYPREFIX, YYFINAL); +#endif + yystate = YYFINAL; + *++yyssp = YYFINAL; + *++yyvsp = yyval; + if (yychar < 0) + { + if ((yychar = yylex()) < 0) yychar = 0; +#if YYDEBUG + if (yydebug) + { + yys = 0; + if (yychar <= YYMAXTOKEN) yys = yyname[yychar]; + if (!yys) yys = "illegal-symbol"; + printf("%sdebug: state %d, reading %d (%s)\n", + YYPREFIX, YYFINAL, yychar, yys); + } +#endif + } + if (yychar == 0) goto yyaccept; + goto yyloop; + } + if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 && + yyn <= YYTABLESIZE && yycheck[yyn] == yystate) + yystate = yytable[yyn]; + else + yystate = yydgoto[yym]; +#if YYDEBUG + if (yydebug) + printf("%sdebug: after reduction, shifting from state %d \ +to state %d\n", YYPREFIX, *yyssp, yystate); +#endif + if (yyssp >= yyss + yystacksize - 1) + { + goto yyoverflow; + } + *++yyssp = yystate; + *++yyvsp = yyval; + goto yyloop; +yyoverflow: + yyerror("yacc stack overflow"); +yyabort: + return (1); +yyaccept: + return (0); +} diff --git a/commands/bc/bc.y b/commands/bc/bc.y new file mode 100755 index 000000000..e67e10174 --- /dev/null +++ b/commands/bc/bc.y @@ -0,0 +1,612 @@ +%{ +/* bc.y: The grammar for a POSIX compatable bc processor with some + extensions to the language. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "global.h" +#include "proto.h" +%} + +%start program + +%union { + char *s_value; + char c_value; + int i_value; + arg_list *a_value; + } + +/* Extensions over POSIX bc. + a) NAME was LETTER. This grammer allows longer names. + Single letter names will still work. + b) Relational_expression allowed only one comparison. + This grammar has added boolean expressions with + && (and) || (or) and ! (not) and allowed all of them in + full expressions. + c) Added an else to the if. + d) Call by variable array parameters + e) read() procedure that reads a number under program control from stdin. + f) halt statement that halts the the program under program control. It + is an executed statement. + g) continue statement for for loops. + h) optional expressions in the for loop. + i) print statement to print multiple numbers per line. + j) warranty statement to print an extended warranty notice. + j) limits statement to print the processor's limits. +*/ + +%token NEWLINE AND OR NOT +%token STRING NAME NUMBER +/* '-', '+' are tokens themselves */ +%token MUL_OP +/* '*', '/', '%' */ +%token ASSIGN_OP +/* '=', '+=', '-=', '*=', '/=', '%=', '^=' */ +%token REL_OP +/* '==', '<=', '>=', '!=', '<', '>' */ +%token INCR_DECR +/* '++', '--' */ +%token Define Break Quit Length +/* 'define', 'break', 'quit', 'length' */ +%token Return For If While Sqrt Else +/* 'return', 'for', 'if', 'while', 'sqrt', 'else' */ +%token Scale Ibase Obase Auto Read +/* 'scale', 'ibase', 'obase', 'auto', 'read' */ +%token Warranty, Halt, Last, Continue, Print, Limits +/* 'warranty', 'halt', 'last', 'continue', 'print', 'limits' */ + +/* Types of all other things. */ +%type expression return_expression named_expression opt_expression +%type '+' '-' +%type opt_parameter_list opt_auto_define_list define_list +%type opt_argument_list argument_list +%type program input_item semicolon_list statement_list +%type statement function statement_or_error + +/* precedence */ +%left OR +%left AND +%nonassoc NOT +%left REL_OP +%right ASSIGN_OP +%left '+' '-' +%left MUL_OP +%right '^' +%nonassoc UNARY_MINUS +%nonassoc INCR_DECR + +%% +program : /* empty */ + { + $$ = 0; + if (interactive) + { + printf ("%s\n", BC_VERSION); + welcome (); + } + } + | program input_item + ; +input_item : semicolon_list NEWLINE + { run_code (); } + | function + { run_code (); } + | error NEWLINE + { + yyerrok; + init_gen (); + } + ; +semicolon_list : /* empty */ + { $$ = 0; } + | statement_or_error + | semicolon_list ';' statement_or_error + | semicolon_list ';' + ; +statement_list : /* empty */ + { $$ = 0; } + | statement_or_error + | statement_list NEWLINE + | statement_list NEWLINE statement_or_error + | statement_list ';' + | statement_list ';' statement + ; +statement_or_error : statement + | error statement + { $$ = $2; } + ; +statement : Warranty + { warranty (""); } + | Limits + { limits (); } + | expression + { + if ($1 & 2) + warn ("comparison in expression"); + if ($1 & 1) + generate ("W"); + else + generate ("p"); + } + | STRING + { + $$ = 0; + generate ("w"); + generate ($1); + free ($1); + } + | Break + { + if (break_label == 0) + yyerror ("Break outside a for/while"); + else + { + sprintf (genstr, "J%1d:", break_label); + generate (genstr); + } + } + | Continue + { + warn ("Continue statement"); + if (continue_label == 0) + yyerror ("Continue outside a for"); + else + { + sprintf (genstr, "J%1d:", continue_label); + generate (genstr); + } + } + | Quit + { exit (0); } + | Halt + { generate ("h"); } + | Return + { generate ("0R"); } + | Return '(' return_expression ')' + { generate ("R"); } + | For + { + $1 = break_label; + break_label = next_label++; + } + '(' opt_expression ';' + { + if ($4 > 1) + warn ("Comparison in first for expression"); + $4 = next_label++; + if ($4 < 0) + sprintf (genstr, "N%1d:", $4); + else + sprintf (genstr, "pN%1d:", $4); + generate (genstr); + } + opt_expression ';' + { + if ($7 < 0) generate ("1"); + $7 = next_label++; + sprintf (genstr, "B%1d:J%1d:", $7, break_label); + generate (genstr); + $$ = continue_label; + continue_label = next_label++; + sprintf (genstr, "N%1d:", continue_label); + generate (genstr); + } + opt_expression ')' + { + if ($10 > 1) + warn ("Comparison in third for expression"); + if ($10 < 0) + sprintf (genstr, "J%1d:N%1d:", $4, $7); + else + sprintf (genstr, "pJ%1d:N%1d:", $4, $7); + generate (genstr); + } + statement + { + sprintf (genstr, "J%1d:N%1d:", + continue_label, break_label); + generate (genstr); + break_label = $1; + continue_label = $9; + } + | If '(' expression ')' + { + $3 = if_label; + if_label = next_label++; + sprintf (genstr, "Z%1d:", if_label); + generate (genstr); + } + statement opt_else + { + sprintf (genstr, "N%1d:", if_label); + generate (genstr); + if_label = $3; + } + | While + { + $1 = next_label++; + sprintf (genstr, "N%1d:", $1); + generate (genstr); + } + '(' expression + { + $4 = break_label; + break_label = next_label++; + sprintf (genstr, "Z%1d:", break_label); + generate (genstr); + } + ')' statement + { + sprintf (genstr, "J%1d:N%1d:", $1, break_label); + generate (genstr); + break_label = $4; + } + | '{' statement_list '}' + { $$ = 0; } + | Print + { warn ("print statement"); } + print_list + ; +print_list : print_element + | print_element ',' print_list + ; +print_element : STRING + { + generate ("O"); + generate ($1); + free ($1); + } + | expression + { generate ("P"); } + ; +opt_else : /* nothing */ + | Else + { + warn ("else clause in if statement"); + $1 = next_label++; + sprintf (genstr, "J%d:N%1d:", $1, if_label); + generate (genstr); + if_label = $1; + } + statement +function : Define NAME '(' opt_parameter_list ')' '{' + NEWLINE opt_auto_define_list + { + /* Check auto list against parameter list? */ + check_params ($4,$8); + sprintf (genstr, "F%d,%s.%s[", lookup($2,FUNCT), + arg_str ($4,TRUE), arg_str ($8,TRUE)); + generate (genstr); + free_args ($4); + free_args ($8); + $1 = next_label; + next_label = 0; + } + statement_list NEWLINE '}' + { + generate ("0R]"); + next_label = $1; + } + ; +opt_parameter_list : /* empty */ + { $$ = NULL; } + | define_list + ; +opt_auto_define_list : /* empty */ + { $$ = NULL; } + | Auto define_list NEWLINE + { $$ = $2; } + | Auto define_list ';' + { $$ = $2; } + ; +define_list : NAME + { $$ = nextarg (NULL, lookup ($1,SIMPLE)); } + | NAME '[' ']' + { $$ = nextarg (NULL, lookup ($1,ARRAY)); } + | define_list ',' NAME + { $$ = nextarg ($1, lookup ($3,SIMPLE)); } + | define_list ',' NAME '[' ']' + { $$ = nextarg ($1, lookup ($3,ARRAY)); } + ; +opt_argument_list : /* empty */ + { $$ = NULL; } + | argument_list + ; +argument_list : expression + { + if ($1 > 1) warn ("comparison in argument"); + $$ = nextarg (NULL,0); + } + | NAME '[' ']' + { + sprintf (genstr, "K%d:", -lookup ($1,ARRAY)); + generate (genstr); + $$ = nextarg (NULL,1); + } + | argument_list ',' expression + { + if ($3 > 1) warn ("comparison in argument"); + $$ = nextarg ($1,0); + } + | argument_list ',' NAME '[' ']' + { + sprintf (genstr, "K%d:", -lookup ($3,ARRAY)); + generate (genstr); + $$ = nextarg ($1,1); + } + ; +opt_expression : /* empty */ + { + $$ = -1; + warn ("Missing expression in for statement"); + } + | expression + ; +return_expression : /* empty */ + { + $$ = 0; + generate ("0"); + } + | expression + { + if ($1 > 1) + warn ("comparison in return expresion"); + } + ; +expression : named_expression ASSIGN_OP + { + if ($2 != '=') + { + if ($1 < 0) + sprintf (genstr, "DL%d:", -$1); + else + sprintf (genstr, "l%d:", $1); + generate (genstr); + } + } + expression + { + if ($4 > 1) warn("comparison in assignment"); + if ($2 != '=') + { + sprintf (genstr, "%c", $2); + generate (genstr); + } + if ($1 < 0) + sprintf (genstr, "S%d:", -$1); + else + sprintf (genstr, "s%d:", $1); + generate (genstr); + $$ = 0; + } + ; + | expression AND + { + warn("&& operator"); + $2 = next_label++; + sprintf (genstr, "DZ%d:p", $2); + generate (genstr); + } + expression + { + sprintf (genstr, "DZ%d:p1N%d:", $2, $2); + generate (genstr); + $$ = $1 | $4; + } + | expression OR + { + warn("|| operator"); + $2 = next_label++; + sprintf (genstr, "B%d:", $2); + generate (genstr); + } + expression + { + int tmplab; + tmplab = next_label++; + sprintf (genstr, "B%d:0J%d:N%d:1N%d:", + $2, tmplab, $2, tmplab); + generate (genstr); + $$ = $1 | $4; + } + | NOT expression + { + $$ = $2; + warn("! operator"); + generate ("!"); + } + | expression REL_OP expression + { + $$ = 3; + switch (*($2)) + { + case '=': + generate ("="); + break; + + case '!': + generate ("#"); + break; + + case '<': + if ($2[1] == '=') + generate ("{"); + else + generate ("<"); + break; + + case '>': + if ($2[1] == '=') + generate ("}"); + else + generate (">"); + break; + } + } + | expression '+' expression + { + generate ("+"); + $$ = $1 | $3; + } + | expression '-' expression + { + generate ("-"); + $$ = $1 | $3; + } + | expression MUL_OP expression + { + genstr[0] = $2; + genstr[1] = 0; + generate (genstr); + $$ = $1 | $3; + } + | expression '^' expression + { + generate ("^"); + $$ = $1 | $3; + } + | '-' expression %prec UNARY_MINUS + { + generate ("n"); + $$ = $2; + } + | named_expression + { + $$ = 1; + if ($1 < 0) + sprintf (genstr, "L%d:", -$1); + else + sprintf (genstr, "l%d:", $1); + generate (genstr); + } + | NUMBER + { + int len = strlen($1); + $$ = 1; + if (len == 1 && *$1 == '0') + generate ("0"); + else if (len == 1 && *$1 == '1') + generate ("1"); + else + { + generate ("K"); + generate ($1); + generate (":"); + } + free ($1); + } + | '(' expression ')' + { $$ = $2 | 1; } + | NAME '(' opt_argument_list ')' + { + $$ = 1; + if ($3 != NULL) + { + sprintf (genstr, "C%d,%s:", + lookup ($1,FUNCT), + arg_str ($3,FALSE)); + free_args ($3); + } + else + { + sprintf (genstr, "C%d:", lookup ($1,FUNCT)); + } + generate (genstr); + } + | INCR_DECR named_expression + { + $$ = 1; + if ($2 < 0) + { + if ($1 == '+') + sprintf (genstr, "DA%d:L%d:", -$2, -$2); + else + sprintf (genstr, "DM%d:L%d:", -$2, -$2); + } + else + { + if ($1 == '+') + sprintf (genstr, "i%d:l%d:", $2, $2); + else + sprintf (genstr, "d%d:l%d:", $2, $2); + } + generate (genstr); + } + | named_expression INCR_DECR + { + $$ = 1; + if ($1 < 0) + { + sprintf (genstr, "DL%d:x", -$1); + generate (genstr); + if ($2 == '+') + sprintf (genstr, "A%d:", -$1); + else + sprintf (genstr, "M%d:", -$1); + } + else + { + sprintf (genstr, "l%d:", $1); + generate (genstr); + if ($2 == '+') + sprintf (genstr, "i%d:", $1); + else + sprintf (genstr, "d%d:", $1); + } + generate (genstr); + } + | Length '(' expression ')' + { generate ("cL"); $$ = 1;} + | Sqrt '(' expression ')' + { generate ("cR"); $$ = 1;} + | Scale '(' expression ')' + { generate ("cS"); $$ = 1;} + | Read '(' ')' + { + warn ("read function"); + generate ("cI"); $$ = 1; + } + ; +named_expression : NAME + { $$ = lookup($1,SIMPLE); } + | NAME '[' expression ']' + { + if ($3 > 1) warn("comparison in subscript"); + $$ = lookup($1,ARRAY); + } + | Ibase + { $$ = 0; } + | Obase + { $$ = 1; } + | Scale + { $$ = 2; } + | Last + { $$ = 3; } + ; +%% diff --git a/commands/bc/bcdefs.h b/commands/bc/bcdefs.h new file mode 100755 index 000000000..a9d217657 --- /dev/null +++ b/commands/bc/bcdefs.h @@ -0,0 +1,154 @@ +/* bcdefs.h: The single file to include all constants and type definitions. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +/* Include the configuration file. */ +#include "config.h" + +/* Standard includes for all files. */ +#include +#include +#include +#ifdef STRINGS_H +#include +#else +#include +#endif +#ifndef NO_LIMITS +#include +#endif + +/* Include the other definitions. */ +#include "const.h" +#include "number.h" + + +/* These definitions define all the structures used in + code and data storage. This includes the representation of + labels. The "guiding" principle is to make structures that + take a minimum of space when unused but can be built to contain + the full structures. */ + +/* Labels are first. Labels are generated sequentially in functions + and full code. They just "point" to a single bye in the code. The + "address" is the byte number. The byte number is used to get an + actual character pointer. */ + +typedef struct bc_label_group + { + long l_adrs [ BC_LABEL_GROUP ]; + struct bc_label_group *l_next; + } bc_label_group; + + +/* Each function has its own code segments and labels. There can be + no jumps between functions so labels are unique to a function. */ + +typedef struct arg_list + { + int av_name; + struct arg_list *next; + } arg_list; + +typedef struct + { + char f_defined; /* Is this function defined yet. */ + char *f_body[BC_MAX_SEGS]; + int f_code_size; + bc_label_group *f_label; + arg_list *f_params; + arg_list *f_autos; + } bc_function; + +/* Code addresses. */ +typedef struct { + int pc_func; + int pc_addr; + } program_counter; + + +/* Variables are "pushable" (auto) and thus we need a stack mechanism. + This is built into the variable record. */ + +typedef struct bc_var + { + bc_num v_value; + struct bc_var *v_next; + } bc_var; + + +/* bc arrays can also be "auto" variables and thus need the same + kind of stacking mechanisms. */ + +typedef struct bc_array_node + { + union + { + bc_num n_num [NODE_SIZE]; + struct bc_array_node *n_down [NODE_SIZE]; + } n_items; + } bc_array_node; + +typedef struct bc_array + { + bc_array_node *a_tree; + short a_depth; + } bc_array; + +typedef struct bc_var_array + { + bc_array *a_value; + char a_param; + struct bc_var_array *a_next; + } bc_var_array; + + +/* For the stacks, execution and function, we need records to allow + for arbitrary size. */ + +typedef struct estack_rec { + bc_num s_num; + struct estack_rec *s_next; +} estack_rec; + +typedef struct fstack_rec { + int s_val; + struct fstack_rec *s_next; +} fstack_rec; + + +/* The following are for the name tree. */ + +typedef struct id_rec { + char *id; /* The program name. */ + /* A name == 0 => nothing assigned yet. */ + int a_name; /* The array variable name (number). */ + int f_name; /* The function name (number). */ + int v_name; /* The variable name (number). */ + short balance; /* For the balanced tree. */ + struct id_rec *left, *right; /* Tree pointers. */ +} id_rec; diff --git a/commands/bc/config.h b/commands/bc/config.h new file mode 100755 index 000000000..215d54e3e --- /dev/null +++ b/commands/bc/config.h @@ -0,0 +1,4 @@ +/* config.h */ +#define SMALL_BUF +#define BC_MATH_FILE "/usr/lib/libmath.b" +#define SHORTNAMES diff --git a/commands/bc/const.h b/commands/bc/const.h new file mode 100755 index 000000000..0cee3cc62 --- /dev/null +++ b/commands/bc/const.h @@ -0,0 +1,87 @@ +/* const.h: Constants for bc. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + + +/* Define INT_MAX and LONG_MAX if not defined. Assuming 32 bits... */ + +#ifdef NO_LIMITS +#define INT_MAX 0x7FFFFFFF +#define LONG_MAX 0x7FFFFFFF +#endif + + +/* Define constants in some reasonable size. The next 4 constants are + POSIX constants. */ + +#define BC_BASE_MAX INT_MAX +#define BC_SCALE_MAX INT_MAX +#define BC_STRING_MAX INT_MAX + + +/* Definitions for arrays. */ + +#define BC_DIM_MAX 65535 /* this should be NODE_SIZE^NODE_DEPTH-1 */ + +#define NODE_SIZE 16 /* Must be a power of 2. */ +#define NODE_MASK 0xf /* Must be NODE_SIZE-1. */ +#define NODE_SHIFT 4 /* Number of 1 bits in NODE_MASK. */ +#define NODE_DEPTH 4 + + +/* Other BC limits defined but not part of POSIX. */ + +#define BC_LABEL_GROUP 64 +#define BC_LABEL_LOG 6 +#define BC_MAX_SEGS 16 /* Code segments. */ +#define BC_SEG_SIZE 1024 +#define BC_SEG_LOG 10 + +/* Maximum number of variables, arrays and functions and the + allocation increment for the dynamic arrays. */ + +#define MAX_STORE 32767 +#define STORE_INCR 32 + +/* Other interesting constants. */ + +#define FALSE 0 +#define TRUE 1 +#define SIMPLE 0 +#define ARRAY 1 +#define FUNCT 2 +#define EXTERN extern +#ifdef __STDC__ +#define CONST const +#define VOID void +#else +#define CONST +#define VOID +#endif + +/* Include the version definition. */ +#include "version.h" diff --git a/commands/bc/execute.c b/commands/bc/execute.c new file mode 100755 index 000000000..a7bc9c733 --- /dev/null +++ b/commands/bc/execute.c @@ -0,0 +1,783 @@ +/* execute.c - run a bc program. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include +#include "global.h" +#include "proto.h" + + +/* The SIGINT interrupt handling routine. */ + +int had_sigint; + +void +stop_execution (sig) + int sig; +{ + had_sigint = TRUE; + printf ("\n"); + rt_error ("interrupted execution"); +} + + +/* Get the current byte and advance the PC counter. */ + +unsigned char +byte (pc) + program_counter *pc; +{ + int seg, offset; + + seg = pc->pc_addr >> BC_SEG_LOG; + offset = pc->pc_addr++ % BC_SEG_SIZE; + return (functions[pc->pc_func].f_body[seg][offset]); +} + + +/* The routine that actually runs the machine. */ + +void +execute () +{ + int label_num, l_gp, l_off; + bc_label_group *gp; + + char inst, ch; + int new_func; + int var_name; + + int const_base; + + bc_num temp_num; + arg_list *auto_list; + + /* Initialize this run... */ + pc.pc_func = 0; + pc.pc_addr = 0; + runtime_error = FALSE; + init_num (&temp_num); + + /* Set up the interrupt mechanism for an interactive session. */ + if (interactive) + { + signal (SIGINT, stop_execution); + had_sigint = FALSE; + } + + while (pc.pc_addr < functions[pc.pc_func].f_code_size && !runtime_error) + { + inst = byte(&pc); + +#if DEBUG > 3 + { /* Print out address and the stack before each instruction.*/ + int depth; estack_rec *temp = ex_stack; + + printf ("func=%d addr=%d inst=%c\n",pc.pc_func, pc.pc_addr, inst); + if (temp == NULL) printf ("empty stack.\n", inst); + else + { + depth = 1; + while (temp != NULL) + { + printf (" %d = ", depth); + out_num (temp->s_num, 10, out_char); + depth++; + temp = temp->s_next; + } + } + } +#endif + + switch ( inst ) + { + + case 'A' : /* increment array variable (Add one). */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + incr_array (var_name); + break; + + case 'B' : /* Branch to a label if TOS != 0. Remove value on TOS. */ + case 'Z' : /* Branch to a label if TOS == 0. Remove value on TOS. */ + c_code = !is_zero (ex_stack->s_num); + pop (); + case 'J' : /* Jump to a label. */ + label_num = byte(&pc); /* Low order bits first. */ + label_num += byte(&pc) << 8; + if (inst == 'J' || (inst == 'B' && c_code) + || (inst == 'Z' && !c_code)) { + gp = functions[pc.pc_func].f_label; + l_gp = label_num >> BC_LABEL_LOG; + l_off = label_num % BC_LABEL_GROUP; + while (l_gp-- > 0) gp = gp->l_next; + pc.pc_addr = gp->l_adrs[l_off]; + } + break; + + case 'C' : /* Call a function. */ + /* Get the function number. */ + new_func = byte(&pc); + if ((new_func & 0x80) != 0) + new_func = ((new_func << 8) & 0x7f) + byte(&pc); + + /* Check to make sure it is defined. */ + if (!functions[new_func].f_defined) + { + rt_error ("Function %s not defined.", f_names[new_func]); + break; + } + + /* Check and push parameters. */ + process_params (&pc, new_func); + + /* Push auto variables. */ + for (auto_list = functions[new_func].f_autos; + auto_list != NULL; + auto_list = auto_list->next) + auto_var (auto_list->av_name); + + /* Push pc and ibase. */ + fpush (pc.pc_func); + fpush (pc.pc_addr); + fpush (i_base); + + /* Reset pc to start of function. */ + pc.pc_func = new_func; + pc.pc_addr = 0; + break; + + case 'D' : /* Duplicate top of stack */ + push_copy (ex_stack->s_num); + break; + + case 'K' : /* Push a constant */ + /* Get the input base and convert it to a bc number. */ + if (pc.pc_func == 0) + const_base = i_base; + else + const_base = fn_stack->s_val; + if (const_base == 10) + push_b10_const (&pc); + else + push_constant (prog_char, const_base); + break; + + case 'L' : /* load array variable */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + load_array (var_name); + break; + + case 'M' : /* decrement array variable (Minus!) */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + decr_array (var_name); + break; + + case 'O' : /* Write a string to the output with processing. */ + while ((ch = byte(&pc)) != '"') + if (ch != '\\') + out_char (ch); + else + { + ch = byte(&pc); + if (ch == '"') break; + switch (ch) + { + case 'n': out_char ('\n'); break; + case 't': out_char ('\t'); break; + case 'r': out_char ('\r'); break; + case 'b': out_char (007); break; + case 'f': out_char ('\f'); break; + case '\\': out_char ('\\'); break; + default: break; + } + } + if (interactive) fflush (stdout); + break; + + case 'R' : /* Return from function */ + if (pc.pc_func != 0) + { + /* "Pop" autos and parameters. */ + pop_vars(functions[pc.pc_func].f_autos); + pop_vars(functions[pc.pc_func].f_params); + /* reset the pc. */ + fpop (); + pc.pc_addr = fpop (); + pc.pc_func = fpop (); + } + else + rt_error ("Return from main program."); + break; + + case 'S' : /* store array variable */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + store_array (var_name); + break; + + case 'T' : /* Test tos for zero */ + c_code = is_zero (ex_stack->s_num); + assign (c_code); + break; + + case 'W' : /* Write the value on the top of the stack. */ + case 'P' : /* Write the value on the top of the stack. No newline. */ + out_num (ex_stack->s_num, o_base, out_char); + if (inst == 'W') out_char ('\n'); + store_var (3); /* Special variable "last". */ + if (interactive) fflush (stdout); + break; + + case 'c' : /* Call special function. */ + new_func = byte(&pc); + + switch (new_func) + { + case 'L': /* Length function. */ + /* For the number 0.xxxx, 0 is not significant. */ + if (ex_stack->s_num->n_len == 1 && + ex_stack->s_num->n_scale != 0 && + ex_stack->s_num->n_value[0] == 0 ) + int2num (&ex_stack->s_num, ex_stack->s_num->n_scale); + else + int2num (&ex_stack->s_num, ex_stack->s_num->n_len + + ex_stack->s_num->n_scale); + break; + + case 'S': /* Scale function. */ + int2num (&ex_stack->s_num, ex_stack->s_num->n_scale); + break; + + case 'R': /* Square Root function. */ + if (!bc_sqrt (&ex_stack->s_num, scale)) + rt_error ("Square root of a negative number"); + break; + + case 'I': /* Read function. */ + push_constant (input_char, i_base); + break; + } + break; + + case 'd' : /* Decrement number */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + decr_var (var_name); + break; + + case 'h' : /* Halt the machine. */ + exit (0); + + case 'i' : /* increment number */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + incr_var (var_name); + break; + + case 'l' : /* load variable */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + load_var (var_name); + break; + + case 'n' : /* Negate top of stack. */ + bc_sub (_zero_, ex_stack->s_num, &ex_stack->s_num); + break; + + case 'p' : /* Pop the execution stack. */ + pop (); + break; + + case 's' : /* store variable */ + var_name = byte(&pc); + if ((var_name & 0x80) != 0) + var_name = ((var_name << 8) & 0x7f) + byte(&pc); + store_var (var_name); + break; + + case 'w' : /* Write a string to the output. */ + while ((ch = byte(&pc)) != '"') out_char (ch); + if (interactive) fflush (stdout); + break; + + case 'x' : /* Exchange Top of Stack with the one under the tos. */ + if (check_stack(2)) { + bc_num temp = ex_stack->s_num; + ex_stack->s_num = ex_stack->s_next->s_num; + ex_stack->s_next->s_num = temp; + } + break; + + case '0' : /* Load Constant 0. */ + push_copy (_zero_); + break; + + case '1' : /* Load Constant 0. */ + push_copy (_one_); + break; + + case '!' : /* Negate the boolean value on top of the stack. */ + c_code = is_zero (ex_stack->s_num); + assign (c_code); + break; + + case '&' : /* compare greater than */ + if (check_stack(2)) + { + c_code = !is_zero (ex_stack->s_next->s_num) + && !is_zero (ex_stack->s_num); + pop (); + assign (c_code); + } + break; + + case '|' : /* compare greater than */ + if (check_stack(2)) + { + c_code = !is_zero (ex_stack->s_next->s_num) + || !is_zero (ex_stack->s_num); + pop (); + assign (c_code); + } + break; + + case '+' : /* add */ + if (check_stack(2)) + { + bc_add (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num); + pop(); + pop(); + push_num (temp_num); + init_num (&temp_num); + } + break; + + case '-' : /* subtract */ + if (check_stack(2)) + { + bc_sub (ex_stack->s_next->s_num, ex_stack->s_num, &temp_num); + pop(); + pop(); + push_num (temp_num); + init_num (&temp_num); + } + break; + + case '*' : /* multiply */ + if (check_stack(2)) + { + bc_multiply (ex_stack->s_next->s_num, ex_stack->s_num, + &temp_num, scale); + pop(); + pop(); + push_num (temp_num); + init_num (&temp_num); + } + break; + + case '/' : /* divide */ + if (check_stack(2)) + { + if (bc_divide (ex_stack->s_next->s_num, + ex_stack->s_num, &temp_num, scale) == 0) + { + pop(); + pop(); + push_num (temp_num); + init_num (&temp_num); + } + else + rt_error ("Divide by zero"); + } + break; + + case '%' : /* remainder */ + if (check_stack(2)) + { + if (is_zero (ex_stack->s_num)) + rt_error ("Modulo by zero"); + else + { + bc_modulo (ex_stack->s_next->s_num, + ex_stack->s_num, &temp_num, scale); + pop(); + pop(); + push_num (temp_num); + init_num (&temp_num); + } + } + break; + + case '^' : /* raise */ + if (check_stack(2)) + { + bc_raise (ex_stack->s_next->s_num, + ex_stack->s_num, &temp_num, scale); + if (is_zero (ex_stack->s_next->s_num) && is_neg (ex_stack->s_num)) + rt_error ("divide by zero"); + pop(); + pop(); + push_num (temp_num); + init_num (&temp_num); + } + break; + + case '=' : /* compare equal */ + if (check_stack(2)) + { + c_code = bc_compare (ex_stack->s_next->s_num, + ex_stack->s_num) == 0; + pop (); + assign (c_code); + } + break; + + case '#' : /* compare not equal */ + if (check_stack(2)) + { + c_code = bc_compare (ex_stack->s_next->s_num, + ex_stack->s_num) != 0; + pop (); + assign (c_code); + } + break; + + case '<' : /* compare less than */ + if (check_stack(2)) + { + c_code = bc_compare (ex_stack->s_next->s_num, + ex_stack->s_num) == -1; + pop (); + assign (c_code); + } + break; + + case '{' : /* compare less than or equal */ + if (check_stack(2)) + { + c_code = bc_compare (ex_stack->s_next->s_num, + ex_stack->s_num) <= 0; + pop (); + assign (c_code); + } + break; + + case '>' : /* compare greater than */ + if (check_stack(2)) + { + c_code = bc_compare (ex_stack->s_next->s_num, + ex_stack->s_num) == 1; + pop (); + assign (c_code); + } + break; + + case '}' : /* compare greater than or equal */ + if (check_stack(2)) + { + c_code = bc_compare (ex_stack->s_next->s_num, + ex_stack->s_num) >= 0; + pop (); + assign (c_code); + } + break; + + default : /* error! */ + rt_error ("bad instruction: inst=%c", inst); + } + } + + /* Clean up the function stack and pop all autos/parameters. */ + while (pc.pc_func != 0) + { + pop_vars(functions[pc.pc_func].f_autos); + pop_vars(functions[pc.pc_func].f_params); + fpop (); + pc.pc_addr = fpop (); + pc.pc_func = fpop (); + } + + /* Clean up the execution stack. */ + while (ex_stack != NULL) pop(); + + /* Clean up the interrupt stuff. */ + if (interactive) + { + signal (SIGINT, use_quit); + if (had_sigint) + printf ("Interruption completed.\n"); + } +} + + +/* Prog_char gets another byte from the program. It is used for + conversion of text constants in the code to numbers. */ + +char +prog_char () +{ + return byte(&pc); +} + + +/* Read a character from the standard input. This function is used + by the "read" function. */ + +char +input_char () +{ + char in_ch; + + /* Get a character from the standard input for the read function. */ + in_ch = getchar(); + + /* Check for a \ quoted newline. */ + if (in_ch == '\\') + { + in_ch = getchar(); + if (in_ch == '\n') + in_ch = getchar(); + } + + /* Classify and preprocess the input character. */ + if (isdigit(in_ch)) + return (in_ch - '0'); + if (in_ch >= 'A' && in_ch <= 'F') + return (in_ch + 10 - 'A'); + if (in_ch >= 'a' && in_ch <= 'f') + return (in_ch + 10 - 'a'); + if (in_ch == '.' || in_ch == '+' || in_ch == '-') + return (in_ch); + if (in_ch <= ' ') + return (' '); + + return (':'); +} + + +/* Push_constant converts a sequence of input characters as returned + by IN_CHAR into a number. The number is pushed onto the execution + stack. The number is converted as a number in base CONV_BASE. */ + +void +push_constant (in_char, conv_base) + char (*in_char)(VOID); + int conv_base; +{ + int digits; + bc_num build, temp, result, mult, divisor; + char in_ch, first_ch; + char negative; + + /* Initialize all bc numbers */ + init_num (&temp); + init_num (&result); + init_num (&mult); + build = copy_num (_zero_); + negative = FALSE; + + /* The conversion base. */ + int2num (&mult, conv_base); + + /* Get things ready. */ + in_ch = in_char(); + while (in_ch == ' ') + in_ch = in_char(); + + if (in_ch == '+') + in_ch = in_char(); + else + if (in_ch == '-') + { + negative = TRUE; + in_ch = in_char(); + } + + /* Check for the special case of a single digit. */ + if (in_ch < 16) + { + first_ch = in_ch; + in_ch = in_char(); + if (in_ch < 16 && first_ch >= conv_base) + first_ch = conv_base - 1; + int2num (&build, (int) first_ch); + } + + /* Convert the integer part. */ + while (in_ch < 16) + { + if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1; + bc_multiply (build, mult, &result, 0); + int2num (&temp, (int) in_ch); + bc_add (result, temp, &build); + in_ch = in_char(); + } + if (in_ch == '.') + { + in_ch = in_char(); + if (in_ch >= conv_base) in_ch = conv_base-1; + free_num (&result); + free_num (&temp); + divisor = copy_num (_one_); + result = copy_num (_zero_); + digits = 0; + while (in_ch < 16) + { + bc_multiply (result, mult, &result, 0); + int2num (&temp, (int) in_ch); + bc_add (result, temp, &result); + bc_multiply (divisor, mult, &divisor, 0); + digits++; + in_ch = in_char(); + if (in_ch < 16 && in_ch >= conv_base) in_ch = conv_base-1; + } + bc_divide (result, divisor, &result, digits); + bc_add (build, result, &build); + } + + /* Final work. */ + if (negative) + bc_sub (_zero_, build, &build); + + push_num (build); + free_num (&temp); + free_num (&result); + free_num (&mult); +} + + +/* When converting base 10 constants from the program, we use this + more efficient way to convert them to numbers. PC tells where + the constant starts and is expected to be advanced to after + the constant. */ + +void +push_b10_const (pc) + program_counter *pc; +{ + bc_num build; + program_counter look_pc; + int kdigits, kscale; + char inchar; + char *ptr; + + /* Count the digits and get things ready. */ + look_pc = *pc; + kdigits = 0; + kscale = 0; + inchar = byte (&look_pc); + while (inchar != '.' && inchar != ':') + { + kdigits++; + inchar = byte(&look_pc); + } + if (inchar == '.' ) + { + inchar = byte(&look_pc); + while (inchar != ':') + { + kscale++; + inchar = byte(&look_pc); + } + } + + /* Get the first character again and move the pc. */ + inchar = byte(pc); + + /* Secial cases of 0, 1, and A-F single inputs. */ + if (kdigits == 1 && kscale == 0) + { + if (inchar == 0) + { + push_copy (_zero_); + inchar = byte(pc); + return; + } + if (inchar == 1) { + push_copy (_one_); + inchar = byte(pc); + return; + } + if (inchar > 9) + { + init_num (&build); + int2num (&build, inchar); + push_num (build); + inchar = byte(pc); + return; + } + } + + /* Build the new number. */ + if (kdigits == 0) + { + build = new_num (1,kscale); + ptr = build->n_value; + *ptr++ = 0; + } + else + { + build = new_num (kdigits,kscale); + ptr = build->n_value; + } + + while (inchar != ':') + { + if (inchar != '.') + if (inchar > 9) + *ptr++ = 9; + else + *ptr++ = inchar; + inchar = byte(pc); + } + push_num (build); +} + + +/* Put the correct value on the stack for C_CODE. Frees TOS num. */ + +void +assign (c_code) + char c_code; +{ + free_num (&ex_stack->s_num); + if (c_code) + ex_stack->s_num = copy_num (_one_); + else + ex_stack->s_num = copy_num (_zero_); +} diff --git a/commands/bc/fix_math.h b/commands/bc/fix_math.h new file mode 100755 index 000000000..9a2ee84c7 --- /dev/null +++ b/commands/bc/fix_math.h @@ -0,0 +1,9 @@ +#!/bin/sh +ed - math.h < 1) { + f += 1; + x /= 2; + } + + /* Initialize the variables. */ + v = 1+x + a = x + d = 1 + + for (i=2; 1; i++) { + e = (a *= x) / (d *= i) + if (e == 0) { + if (f>0) while (f--) v = v*v; + scale = z + if (m) return (1/v); + return (v/1); + } + v += e + } +} + +/* Natural log. Uses the fact that ln(x^2) = 2*ln(x) + The series used is: + ln(x) = 2(a+a^3/3+a^5/5+...) where a=(x-1)/(x+1) +*/ + +define l(x) { + auto e, f, i, m, n, v, z + + /* return something for the special case. */ + if (x <= 0) return (1 - 10^scale) + + /* Precondition x to make .5 < x < 2.0. */ + z = scale; + scale += 4; + f = 2; + i=0 + while (x >= 2) { /* for large numbers */ + f *= 2; + x = sqrt(x); + } + while (x <= .5) { /* for small numbers */ + f *= 2; + x = sqrt(x); + } + + /* Set up the loop. */ + v = n = (x-1)/(x+1) + m = n*n + + /* Sum the series. */ + for (i=3; 1; i+=2) { + e = (n *= m) / i + if (e == 0) { + v = f*v + scale = z + return (v/1) + } + v += e + } +} + +/* Sin(x) uses the standard series: + sin(x) = x - x^3/3! + x^5/5! - x^7/7! ... */ + +define s(x) { + auto e, i, m, n, s, v, z + + /* precondition x. */ + z = scale + scale = 1.1*z + 1; + v = a(1) + if (x < 0) { + m = 1; + x = -x; + } + scale = 0 + n = (x / v + 2 )/4 + x = x - 4*n*v + if (n%2) x = -x + + /* Do the loop. */ + scale = z + 2; + v = e = x + s = -x*x + for (i=3; 1; i+=2) { + e *= s/(i*(i-1)) + if (e == 0) { + scale = z + if (m) return (-v/1); + return (v/1); + } + v += e + } +} + +/* Cosine : cos(x) = sin(x+pi/2) */ +define c(x) { + auto v; + scale += 1; + v = s(x+a(1)*2); + scale -= 1; + return (v/1); +} + +/* Arctan: Using the formula: + atan(x) = atan(c) + atan((x-c)/(1+xc)) for a small c (.2 here) + For under .2, use the series: + atan(x) = x - x^3/3 + x^5/5 - x^7/7 + ... */ + +define a(x) { + auto a, e, f, i, m, n, s, v, z + + /* Special case and for fast answers */ + if (x==1) { + if (scale <= 25) return (.7853981633974483096156608/1) + if (scale <= 40) return (.7853981633974483096156608458198757210492/1) + if (scale <= 60) \ + return (.785398163397448309615660845819875721049292349843776455243736/1) + } + if (x==.2) { + if (scale <= 25) return (.1973955598498807583700497/1) + if (scale <= 40) return (.1973955598498807583700497651947902934475/1) + if (scale <= 60) \ + return (.197395559849880758370049765194790293447585103787852101517688/1) + } + + /* Negative x? */ + if (x<0) { + m = 1; + x = -x; + } + + /* Save the scale. */ + z = scale; + + /* Note: a and f are known to be zero due to being auto vars. */ + /* Calculate atan of a known number. */ + if (x > .2) { + scale = z+4; + a = a(.2); + } + + /* Precondition x. */ + scale = z+2; + while (x > .2) { + f += 1; + x = (x-.2) / (1+x*.2); + } + + /* Initialize the series. */ + v = n = x; + s = -x*x; + + /* Calculate the series. */ + for (i=3; 1; i+=2) { + e = (n *= s) / i; + if (e == 0) { + scale = z; + if (m) return ((f*a+v)/-1); + return ((f*a+v)/1); + } + v += e + } +} + + +/* Bessel function of integer order. Uses the following: + j(-n,x) = (-1)^n*j(n,x) + j(n,x) = x^n/(2^n*n!) * (1 - x^2/(2^2*1!*(n+1)) + x^4/(2^4*2!*(n+1)*(n+2)) + - x^6/(2^6*3!*(n+1)*(n+2)*(n+3)) .... ) +*/ +define j(n,x) { + auto a, d, e, f, i, m, s, v, z + + /* Make n an integer and check for negative n. */ + z = scale; + scale = 0; + n = n/1; + if (n<0) { + n = -n; + if (n%2 == 1) m = 1; + } + + /* Compute the factor of x^n/(2^n*n!) */ + f = 1; + for (i=2; i<=n; i++) f = f*i; + scale = 1.5*z; + f = x^n / 2^n / f; + + /* Initialize the loop .*/ + v = e = 1; + s = -x*x/4 + scale = 1.5*z + + /* The Loop.... */ + for (i=1; 1; i++) { + e = e * s / i / (n+i); + if (e == 0) { + scale = z + if (m) return (-f*v/1); + return (f*v/1); + } + v += e; + } +} diff --git a/commands/bc/load.c b/commands/bc/load.c new file mode 100755 index 000000000..be4ab3a52 --- /dev/null +++ b/commands/bc/load.c @@ -0,0 +1,333 @@ +/* load.c: This code "loads" code into the code segments. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "global.h" +#include "proto.h" + +/* Load variables. */ + +program_counter load_adr; +char load_str; +char load_const; + +/* Initialize the load sequence. */ +void +init_load () +{ + clear_func(0); + load_adr.pc_func = 0; + load_adr.pc_addr = 0; + load_str = FALSE; + load_const = FALSE; +} + +/* addbyte adds one BYTE to the current code segment. */ +void +addbyte (byte) + char byte; +{ + int seg, offset, func; + + /* If there was an error, don't continue. */ + if (had_error) return; + + /* Calculate the segment and offset. */ + seg = load_adr.pc_addr >> BC_SEG_LOG; + offset = load_adr.pc_addr++ % BC_SEG_SIZE; + func = load_adr.pc_func; + + if (seg >= BC_MAX_SEGS) + { + yyerror ("Function too big."); + return; + } + + if (functions[func].f_body[seg] == NULL) + functions[func].f_body[seg] = (char *) bc_malloc (BC_SEG_SIZE); + + /* Store the byte. */ + functions[func].f_body[seg][offset] = byte; + functions[func].f_code_size++; +} + + +/* Define a label LAB to be the current program counter. */ + +void +def_label (lab) + long lab; +{ + bc_label_group *temp; + int group, offset, func; + + /* Get things ready. */ + group = lab >> BC_LABEL_LOG; + offset = lab % BC_LABEL_GROUP; + func = load_adr.pc_func; + + /* Make sure there is at least one label group. */ + if (functions[func].f_label == NULL) + { + functions[func].f_label = + (bc_label_group *) bc_malloc (sizeof(bc_label_group)); + functions[func].f_label->l_next = NULL; + } + + /* Add the label group. */ + temp = functions[func].f_label; + while (group > 0) + { + if (temp->l_next == NULL) + { + temp->l_next = (bc_label_group *) bc_malloc (sizeof(bc_label_group)); + temp->l_next->l_next = NULL; + } + temp = temp->l_next; + group --; + } + + /* Define it! */ + temp->l_adrs [offset] = load_adr.pc_addr; +} + +/* Several instructions have integers in the code. They + are all known to be legal longs. So, no error code + is added. STR is the pointer to the load string and + must be moved to the last non-digit character. */ + +long +long_val (str) + char **str; +{ int val = 0; + char neg = FALSE; + + if (**str == '-') + { + neg = TRUE; + (*str)++; + } + while (isdigit(**str)) + val = val*10 + *(*str)++ - '0'; + + if (neg) + return -val; + else + return val; +} + + +/* load_code loads the CODE into the machine. */ + +void +load_code (code) + char *code; +{ + char *str; + long ap_name; /* auto or parameter name. */ + long label_no; + long vaf_name; /* variable, array or function number. */ + long func; + program_counter save_adr; + + /* Initialize. */ + str = code; + + /* Scan the code. */ + while (*str != 0) + { + /* If there was an error, don't continue. */ + if (had_error) return; + + if (load_str) + { + if (*str == '"') load_str = FALSE; + addbyte (*str++); + } + else + if (load_const) + { + if (*str == '\n') + str++; + else + { + if (*str == ':') + { + load_const = FALSE; + addbyte (*str++); + } + else + if (*str == '.') + addbyte (*str++); + else + if (*str >= 'A') + addbyte (*str++ + 10 - 'A'); + else + addbyte (*str++ - '0'); + } + } + else + { + switch (*str) + { + + case '"': /* Starts a string. */ + load_str = TRUE; + break; + + case 'N': /* A label */ + str++; + label_no = long_val (&str); + def_label (label_no); + break; + + case 'B': /* Branch to label. */ + case 'J': /* Jump to label. */ + case 'Z': /* Branch Zero to label. */ + addbyte(*str++); + label_no = long_val (&str); + if (label_no > 65535L) + { /* Better message? */ + fprintf (stderr,"Program too big.\n"); + exit(1); + } + addbyte ( (char) label_no & 0xFF); + addbyte ( (char) label_no >> 8); + break; + + case 'F': /* A function, get the name and initialize it. */ + str++; + func = long_val (&str); + clear_func (func); +#if DEBUG > 2 + printf ("Loading function number %d\n", func); +#endif + /* get the parameters */ + while (*str++ != '.') + { + if (*str == '.') + { + str++; + break; + } + ap_name = long_val (&str); +#if DEBUG > 2 + printf ("parameter number %d\n", ap_name); +#endif + functions[(int)func].f_params = + nextarg (functions[(int)func].f_params, ap_name); + } + + /* get the auto vars */ + while (*str != '[') + { + if (*str == ',') str++; + ap_name = long_val (&str); +#if DEBUG > 2 + printf ("auto number %d\n", ap_name); +#endif + functions[(int)func].f_autos = + nextarg (functions[(int)func].f_autos, ap_name); + } + save_adr = load_adr; + load_adr.pc_func = func; + load_adr.pc_addr = 0; + break; + + case ']': /* A function end */ + functions[load_adr.pc_func].f_defined = TRUE; + load_adr = save_adr; + break; + + case 'C': /* Call a function. */ + addbyte (*str++); + func = long_val (&str); + if (func < 128) + addbyte ( (char) func); + else + { + addbyte ((func >> 8) & 0xff | 0x80); + addbyte (func & 0xff); + } + if (*str == ',') str++; + while (*str != ':') + addbyte (*str++); + addbyte (':'); + break; + + case 'c': /* Call a special function. */ + addbyte (*str++); + addbyte (*str); + break; + + case 'K': /* A constant.... may have an "F" in it. */ + addbyte (*str); + load_const = TRUE; + break; + + case 'd': /* Decrement. */ + case 'i': /* Increment. */ + case 'l': /* Load. */ + case 's': /* Store. */ + case 'A': /* Array Increment */ + case 'M': /* Array Decrement */ + case 'L': /* Array Load */ + case 'S': /* Array Store */ + addbyte (*str++); + vaf_name = long_val (&str); + if (vaf_name < 128) + addbyte (vaf_name); + else + { + addbyte ((vaf_name >> 8) & 0xff | 0x80); + addbyte (vaf_name & 0xff); + } + break; + + case '@': /* A command! */ + switch (*(++str)) + { + case 'i': + init_load (); + break; + case 'r': + execute (); + break; + } + break; + + case '\n': /* Ignore the newlines */ + break; + + default: /* Anything else */ + addbyte (*str); + } + str++; + } + } +} diff --git a/commands/bc/main.c b/commands/bc/main.c new file mode 100755 index 000000000..33827cc84 --- /dev/null +++ b/commands/bc/main.c @@ -0,0 +1,204 @@ +/* main.c: The main program for bc. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include +#include "global.h" +#include "proto.h" + +/* Variables for processing multiple files. */ +char first_file; +extern FILE *yyin; + + +/* The main program for bc. */ +int +main (argc, argv) + int argc; + char *argv[]; +{ + int ch; + + /* Initialize many variables. */ + compile_only = FALSE; + use_math = FALSE; + warn_not_std = FALSE; + std_only = FALSE; + if (isatty(0) && isatty(1)) + interactive = TRUE; + else + interactive = FALSE; + + /* Parse the command line */ + ch = getopt (argc, argv, "lcisvw"); + while (ch != EOF) + { + switch (ch) + { + case 'c': /* compile only */ + compile_only = TRUE; + break; + case 'l': /* math lib */ + use_math = TRUE; + break; + case 'i': /* force interactive */ + interactive = TRUE; + break; + case 'w': /* Non standard features give warnings. */ + warn_not_std = TRUE; + break; + case 's': /* Non standard features give errors. */ + std_only = TRUE; + break; + case 'v': /* Print the version. */ + printf ("%s\n", BC_VERSION); + break; + } + ch = getopt (argc, argv, "lcisvw"); + } + + /* Initialize the machine. */ + init_storage(); + init_load(); + + /* Set up interrupts to print a message. */ + if (interactive) + signal (SIGINT, use_quit); + + /* Initialize the front end. */ + init_tree(); + init_gen (); + g_argv = argv; + g_argc = argc; + is_std_in = FALSE; + first_file = TRUE; + if (!open_new_file ()) + exit (1); + + /* Do the parse. */ + yyparse (); + + /* End the compile only output with a newline. */ + if (compile_only) + printf ("\n"); + + exit (0); +} + + +/* This is the function that opens all the files. + It returns TRUE if the file was opened, otherwise + it returns FALSE. */ + +int +open_new_file () +{ + FILE *new_file; + + /* Set the line number. */ + line_no = 1; + + /* Check to see if we are done. */ + if (is_std_in) return (FALSE); + + /* Open the other files. */ + if (use_math && first_file) + { +#ifdef BC_MATH_FILE + /* Make the first file be the math library. */ + new_file = fopen (BC_MATH_FILE, "r"); + use_math = FALSE; + if (new_file != NULL) + { + new_yy_file (new_file); + return TRUE; + } + else + { + fprintf (stderr, "Math Library unavailable.\n"); + exit (1); + } +#else + /* Load the code from a precompiled version of the math libarary. */ + extern char libmath[]; + char tmp; + /* These MUST be in the order of first mention of each function. + That is why "a" comes before "c" even though "a" is defined after + after "c". "a" is used in "s"! */ + tmp = lookup ("e", FUNCT); + tmp = lookup ("l", FUNCT); + tmp = lookup ("s", FUNCT); + tmp = lookup ("a", FUNCT); + tmp = lookup ("c", FUNCT); + tmp = lookup ("j", FUNCT); + load_code (libmath); +#endif + } + + /* One of the argv values. */ + while (optind < g_argc) + { + new_file = fopen (g_argv[optind], "r"); + if (new_file != NULL) + { + new_yy_file (new_file); + optind++; + return TRUE; + } + fprintf (stderr, "File %s is unavailable.\n", g_argv[optind++]); + exit (1); + } + + /* If we fall through to here, we should return stdin. */ + new_yy_file (stdin); + is_std_in = TRUE; + return TRUE; +} + + +/* Set yyin to the new file. */ + +void +new_yy_file (file) + FILE *file; +{ + if (!first_file) fclose (yyin); + yyin = file; + first_file = FALSE; +} + + +/* Message to use quit. */ + +void +use_quit (sig) + int sig; +{ + printf ("\n(interrupt) use quit to exit.\n"); + signal (SIGINT, use_quit); +} diff --git a/commands/bc/number.c b/commands/bc/number.c new file mode 100755 index 000000000..346b44a4d --- /dev/null +++ b/commands/bc/number.c @@ -0,0 +1,1405 @@ +/* number.c: Implements arbitrary precision numbers. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "proto.h" + +/* Storage used for special numbers. */ +bc_num _zero_; +bc_num _one_; +bc_num _two_; + + +/* "Frees" a bc_num NUM. Actually decreases reference count and only + frees the storage if reference count is zero. */ + +void +free_num (num) + bc_num *num; +{ + if (*num == NULL) return; + (*num)->n_refs--; + if ((*num)->n_refs == 0) free(*num); + *num = NULL; +} + + +/* new_num allocates a number and sets fields to known values. */ + +bc_num +new_num (length, scale) + int length, scale; +{ + bc_num temp; + + temp = (bc_num) malloc (sizeof(bc_struct)+length+scale); + if (temp == NULL) out_of_memory (); + temp->n_sign = PLUS; + temp->n_len = length; + temp->n_scale = scale; + temp->n_refs = 1; + temp->n_value[0] = 0; + return temp; +} + + +/* Intitialize the number package! */ + +void +init_numbers () +{ + _zero_ = new_num (1,0); + _one_ = new_num (1,0); + _one_->n_value[0] = 1; + _two_ = new_num (1,0); + _two_->n_value[0] = 2; +} + + +/* Make a copy of a number! Just increments the reference count! */ + +bc_num +copy_num (num) + bc_num num; +{ + num->n_refs++; + return num; +} + + +/* Initialize a number NUM by making it a copy of zero. */ + +void +init_num (num) + bc_num *num; +{ + *num = copy_num (_zero_); +} + + +/* Convert an integer VAL to a bc number NUM. */ + +void +int2num (num, val) + bc_num *num; + int val; +{ + char buffer[30]; + char *bptr, *vptr; + int ix = 1; + char neg = 0; + + /* Sign. */ + if (val < 0) + { + neg = 1; + val = -val; + } + + /* Get things going. */ + bptr = buffer; + *bptr++ = val % 10; + val = val / 10; + + /* Extract remaining digits. */ + while (val != 0) + { + *bptr++ = val % 10; + val = val / 10; + ix++; /* Count the digits. */ + } + + /* Make the number. */ + free_num (num); + *num = new_num (ix, 0); + if (neg) (*num)->n_sign = MINUS; + + /* Assign the digits. */ + vptr = (*num)->n_value; + while (ix-- > 0) + *vptr++ = *--bptr; +} + + +/* Convert a number NUM to a long. The function returns only the integer + part of the number. For numbers that are too large to represent as + a long, this function returns a zero. This can be detected by checking + the NUM for zero after having a zero returned. */ + +long +num2long (num) + bc_num num; +{ + long val; + char *nptr; + int index; + + /* Extract the int value, ignore the fraction. */ + val = 0; + nptr = num->n_value; + for (index=num->n_len; (index>0) && (val<=(LONG_MAX/10)); index--) + val = val*10 + *nptr++; + + /* Check for overflow. If overflow, return zero. */ + if (index>0) val = 0; + if (val < 0) val = 0; + + /* Return the value. */ + if (num->n_sign == PLUS) + return (val); + else + return (-val); +} + + +/* The following are some math routines for numbers. */ +_PROTOTYPE(static int _do_compare, (bc_num n1, bc_num n2, int use_sign, + int ignore_last)); +_PROTOTYPE(static void _rm_leading_zeros, (bc_num num)); +_PROTOTYPE(static bc_num _do_add, (bc_num n1, bc_num n2)); +_PROTOTYPE(static bc_num _do_sub, (bc_num n1, bc_num n2)); +_PROTOTYPE(static void _one_mult, (unsigned char *num, int size, int digit, + unsigned char *result)); + + + +/* Compare two bc numbers. Return value is 0 if equal, -1 if N1 is less + than N2 and +1 if N1 is greater than N2. If USE_SIGN is false, just + compare the magnitudes. */ + +static int +_do_compare (n1, n2, use_sign, ignore_last) + bc_num n1, n2; + int use_sign; + int ignore_last; +{ + char *n1ptr, *n2ptr; + int count; + + /* First, compare signs. */ + if (use_sign && n1->n_sign != n2->n_sign) + { + if (n1->n_sign == PLUS) + return (1); /* Positive N1 > Negative N2 */ + else + return (-1); /* Negative N1 < Positive N1 */ + } + + /* Now compare the magnitude. */ + if (n1->n_len != n2->n_len) + { + if (n1->n_len > n2->n_len) + { + /* Magnitude of n1 > n2. */ + if (!use_sign || n1->n_sign == PLUS) + return (1); + else + return (-1); + } + else + { + /* Magnitude of n1 < n2. */ + if (!use_sign || n1->n_sign == PLUS) + return (-1); + else + return (1); + } + } + + /* If we get here, they have the same number of integer digits. + check the integer part and the equal length part of the fraction. */ + count = n1->n_len + MIN (n1->n_scale, n2->n_scale); + n1ptr = n1->n_value; + n2ptr = n2->n_value; + + while ((count > 0) && (*n1ptr == *n2ptr)) + { + n1ptr++; + n2ptr++; + count--; + } + if (ignore_last && count == 1 && n1->n_scale == n2->n_scale) + return (0); + if (count != 0) + { + if (*n1ptr > *n2ptr) + { + /* Magnitude of n1 > n2. */ + if (!use_sign || n1->n_sign == PLUS) + return (1); + else + return (-1); + } + else + { + /* Magnitude of n1 < n2. */ + if (!use_sign || n1->n_sign == PLUS) + return (-1); + else + return (1); + } + } + + /* They are equal up to the last part of the equal part of the fraction. */ + if (n1->n_scale != n2->n_scale) + if (n1->n_scale > n2->n_scale) + { + for (count = n1->n_scale-n2->n_scale; count>0; count--) + if (*n1ptr++ != 0) + { + /* Magnitude of n1 > n2. */ + if (!use_sign || n1->n_sign == PLUS) + return (1); + else + return (-1); + } + } + else + { + for (count = n2->n_scale-n1->n_scale; count>0; count--) + if (*n2ptr++ != 0) + { + /* Magnitude of n1 < n2. */ + if (!use_sign || n1->n_sign == PLUS) + return (-1); + else + return (1); + } + } + + /* They must be equal! */ + return (0); +} + + +/* This is the "user callable" routine to compare numbers N1 and N2. */ + +int +bc_compare (n1, n2) + bc_num n1, n2; +{ + return _do_compare (n1, n2, TRUE, FALSE); +} + + +/* In some places we need to check if the number NUM is zero. */ + +char +is_zero (num) + bc_num num; +{ + int count; + char *nptr; + + /* Quick check. */ + if (num == _zero_) return TRUE; + + /* Initialize */ + count = num->n_len + num->n_scale; + nptr = num->n_value; + + /* The check */ + while ((count > 0) && (*nptr++ == 0)) count--; + + if (count != 0) + return FALSE; + else + return TRUE; +} + + +/* In some places we need to check if the number is negative. */ + +char +is_neg (num) + bc_num num; +{ + return num->n_sign == MINUS; +} + + +/* For many things, we may have leading zeros in a number NUM. + _rm_leading_zeros just moves the data to the correct + place and adjusts the length. */ + +static void +_rm_leading_zeros (num) + bc_num num; +{ + int bytes; + char *dst, *src; + + /* Do a quick check to see if we need to do it. */ + if (*num->n_value != 0) return; + + /* The first digit is 0, find the first non-zero digit in the 10's or + greater place. */ + bytes = num->n_len; + src = num->n_value; + while (bytes > 1 && *src == 0) src++, bytes--; + num->n_len = bytes; + bytes += num->n_scale; + dst = num->n_value; + while (bytes-- > 0) *dst++ = *src++; + +} + + +/* Perform addition: N1 is added to N2 and the value is + returned. The signs of N1 and N2 are ignored. */ + +static bc_num +_do_add (n1, n2) + bc_num n1, n2; +{ + bc_num sum; + int sum_scale, sum_digits; + char *n1ptr, *n2ptr, *sumptr; + int carry, n1bytes, n2bytes; + + /* Prepare sum. */ + sum_scale = MAX (n1->n_scale, n2->n_scale); + sum_digits = MAX (n1->n_len, n2->n_len) + 1; + sum = new_num (sum_digits,sum_scale); + + /* Start with the fraction part. Initialize the pointers. */ + n1bytes = n1->n_scale; + n2bytes = n2->n_scale; + n1ptr = (char *) (n1->n_value + n1->n_len + n1bytes - 1); + n2ptr = (char *) (n2->n_value + n2->n_len + n2bytes - 1); + sumptr = (char *) (sum->n_value + sum_scale + sum_digits - 1); + + /* Add the fraction part. First copy the longer fraction.*/ + if (n1bytes != n2bytes) + { + if (n1bytes > n2bytes) + while (n1bytes>n2bytes) + { *sumptr-- = *n1ptr--; n1bytes--;} + else + while (n2bytes>n1bytes) + { *sumptr-- = *n2ptr--; n2bytes--;} + } + + /* Now add the remaining fraction part and equal size integer parts. */ + n1bytes += n1->n_len; + n2bytes += n2->n_len; + carry = 0; + while ((n1bytes > 0) && (n2bytes > 0)) + { + *sumptr = *n1ptr-- + *n2ptr-- + carry; + if (*sumptr > 9) + { + carry = 1; + *sumptr -= 10; + } + else + carry = 0; + sumptr--; + n1bytes--; + n2bytes--; + } + + /* Now add carry the longer integer part. */ + if (n1bytes == 0) + { n1bytes = n2bytes; n1ptr = n2ptr; } + while (n1bytes-- > 0) + { + *sumptr = *n1ptr-- + carry; + if (*sumptr > 9) + { + carry = 1; + *sumptr -= 10; + } + else + carry = 0; + sumptr--; + } + + /* Set final carry. */ + if (carry == 1) + *sumptr += 1; + + /* Adjust sum and return. */ + _rm_leading_zeros (sum); + return sum; +} + + +/* Perform subtraction: N2 is subtracted from N1 and the value is + returned. The signs of N1 and N2 are ignored. Also, N1 is + assumed to be larger than N2. */ + +static bc_num +_do_sub (n1, n2) + bc_num n1, n2; +{ + bc_num diff; + int diff_scale, diff_len; + int min_scale, min_len; + char *n1ptr, *n2ptr, *diffptr; + int borrow, count, val; + + /* Allocate temporary storage. */ + diff_len = MAX (n1->n_len, n2->n_len); + diff_scale = MAX (n1->n_scale, n2->n_scale); + min_len = MIN (n1->n_len, n2->n_len); + min_scale = MIN (n1->n_scale, n2->n_scale); + diff = new_num (diff_len, diff_scale); + + /* Initialize the subtract. */ + n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale -1); + n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale -1); + diffptr = (char *) (diff->n_value + diff_len + diff_scale -1); + + /* Subtract the numbers. */ + borrow = 0; + + /* Take care of the longer scaled number. */ + if (n1->n_scale != min_scale) + { + /* n1 has the longer scale */ + for (count = n1->n_scale - min_scale; count > 0; count--) + *diffptr-- = *n1ptr--; + } + else + { + /* n2 has the longer scale */ + for (count = n2->n_scale - min_scale; count > 0; count--) + { + val = - *n2ptr-- - borrow; + if (val < 0) + { + val += 10; + borrow = 1; + } + else + borrow = 0; + *diffptr-- = val; + } + } + + /* Now do the equal length scale and integer parts. */ + + for (count = 0; count < min_len + min_scale; count++) + { + val = *n1ptr-- - *n2ptr-- - borrow; + if (val < 0) + { + val += 10; + borrow = 1; + } + else + borrow = 0; + *diffptr-- = val; + } + + /* If n1 has more digits then n2, we now do that subtract. */ + if (diff_len != min_len) + { + for (count = diff_len - min_len; count > 0; count--) + { + val = *n1ptr-- - borrow; + if (val < 0) + { + val += 10; + borrow = 1; + } + else + borrow = 0; + *diffptr-- = val; + } + } + + /* Clean up and return. */ + _rm_leading_zeros (diff); + return diff; +} + + +/* Here is the full add routine that takes care of negative numbers. + N1 is added to N2 and the result placed into RESULT. */ + +void +bc_add ( n1, n2, result) + bc_num n1, n2, *result; +{ + bc_num sum; + int cmp_res; + + if (n1->n_sign == n2->n_sign) + { + sum = _do_add (n1, n2); + sum->n_sign = n1->n_sign; + } + else + { + /* subtraction must be done. */ + cmp_res = _do_compare (n1, n2, FALSE, FALSE); /* Compare magnitudes. */ + switch (cmp_res) + { + case -1: + /* n1 is less than n2, subtract n1 from n2. */ + sum = _do_sub (n2, n1); + sum->n_sign = n2->n_sign; + break; + case 0: + /* They are equal! return zero! */ + sum = copy_num (_zero_); + break; + case 1: + /* n2 is less than n1, subtract n2 from n1. */ + sum = _do_sub (n1, n2); + sum->n_sign = n1->n_sign; + } + } + + /* Clean up and return. */ + free_num (result); + *result = sum; +} + + +/* Here is the full subtract routine that takes care of negative numbers. + N2 is subtracted from N1 and the result placed in RESULT. */ + +void +bc_sub ( n1, n2, result) + bc_num n1, n2, *result; +{ + bc_num diff; + int cmp_res; + + if (n1->n_sign != n2->n_sign) + { + diff = _do_add (n1, n2); + diff->n_sign = n1->n_sign; + } + else + { + /* subtraction must be done. */ + cmp_res = _do_compare (n1, n2, FALSE, FALSE); /* Compare magnitudes. */ + switch (cmp_res) + { + case -1: + /* n1 is less than n2, subtract n1 from n2. */ + diff = _do_sub (n2, n1); + diff->n_sign = (n2->n_sign == PLUS ? MINUS : PLUS); + break; + case 0: + /* They are equal! return zero! */ + diff = copy_num (_zero_); + break; + case 1: + /* n2 is less than n1, subtract n2 from n1. */ + diff = _do_sub (n1, n2); + diff->n_sign = n1->n_sign; + break; + } + } + + /* Clean up and return. */ + free_num (result); + *result = diff; +} + + +/* The multiply routine. N2 time N1 is put int PROD with the scale of + the result being MIN(N2 scale+N1 scale, MAX (SCALE, N2 scale, N1 scale)). + */ + +void +bc_multiply (n1, n2, prod, scale) + bc_num n1, n2, *prod; + int scale; +{ + bc_num pval; /* For the working storage. */ + char *n1ptr, *n2ptr, *pvptr; /* Work pointers. */ + char *n1end, *n2end; /* To the end of n1 and n2. */ + + int indx; + int len1, len2, total_digits; + long sum; + int full_scale, prod_scale; + int toss; + + /* Initialize things. */ + len1 = n1->n_len + n1->n_scale; + len2 = n2->n_len + n2->n_scale; + total_digits = len1 + len2; + full_scale = n1->n_scale + n2->n_scale; + prod_scale = MIN(full_scale,MAX(scale,MAX(n1->n_scale,n2->n_scale))); + toss = full_scale - prod_scale; + pval = new_num (total_digits-full_scale, prod_scale); + pval->n_sign = ( n1->n_sign == n2->n_sign ? PLUS : MINUS ); + n1end = (char *) (n1->n_value + len1 - 1); + n2end = (char *) (n2->n_value + len2 - 1); + pvptr = (char *) (pval->n_value + total_digits - toss - 1); + sum = 0; + + /* Here are the loops... */ + for (indx = 0; indx < toss; indx++) + { + n1ptr = (char *) (n1end - MAX(0, indx-len2+1)); + n2ptr = (char *) (n2end - MIN(indx, len2-1)); + while ((n1ptr >= n1->n_value) && (n2ptr <= n2end)) + sum += *n1ptr-- * *n2ptr++; + sum = sum / 10; + } + for ( ; indx < total_digits-1; indx++) + { + n1ptr = (char *) (n1end - MAX(0, indx-len2+1)); + n2ptr = (char *) (n2end - MIN(indx, len2-1)); + while ((n1ptr >= n1->n_value) && (n2ptr <= n2end)) + sum += *n1ptr-- * *n2ptr++; + *pvptr-- = sum % 10; + sum = sum / 10; + } + *pvptr-- = sum; + + /* Assign to prod and clean up the number. */ + free_num (prod); + *prod = pval; + _rm_leading_zeros (*prod); + if (is_zero (*prod)) + (*prod)->n_sign = PLUS; +} + + +/* Some utility routines for the divide: First a one digit multiply. + NUM (with SIZE digits) is multiplied by DIGIT and the result is + placed into RESULT. It is written so that NUM and RESULT can be + the same pointers. */ + +static void +_one_mult (num, size, digit, result) + unsigned char *num; + int size, digit; + unsigned char *result; +{ + int carry, value; + unsigned char *nptr, *rptr; + + if (digit == 0) + memset (result, 0, size); + else + { + if (digit == 1) + memcpy (result, num, size); + else + { + /* Initialize */ + nptr = (unsigned char *) (num+size-1); + rptr = (unsigned char *) (result+size-1); + carry = 0; + + while (size-- > 0) + { + value = *nptr-- * digit + carry; + *rptr-- = value % 10; + carry = value / 10; + } + + if (carry != 0) *rptr = carry; + } + } +} + + +/* The full division routine. This computes N1 / N2. It returns + 0 if the division is ok and the result is in QUOT. The number of + digits after the decimal point is SCALE. It returns -1 if division + by zero is tried. The algorithm is found in Knuth Vol 2. p237. */ + +int +bc_divide (n1, n2, quot, scale) + bc_num n1, n2, *quot; + int scale; +{ + bc_num qval; + unsigned char *num1, *num2; + unsigned char *ptr1, *ptr2, *n2ptr, *qptr; + int scale1, val; + unsigned int len1, len2, scale2, qdigits, extra, count; + unsigned int qdig, qguess, borrow, carry; + unsigned char *mval; + char zero; + unsigned int norm; + + /* Test for divide by zero. */ + if (is_zero (n2)) return -1; + + /* Test for divide by 1. If it is we must truncate. */ + if (n2->n_scale == 0) + { + if (n2->n_len == 1 && *n2->n_value == 1) + { + qval = new_num (n1->n_len, scale); + qval->n_sign = (n1->n_sign == n2->n_sign ? PLUS : MINUS); + memset (&qval->n_value[n1->n_len],0,scale); + memcpy (qval->n_value, n1->n_value, + n1->n_len + MIN(n1->n_scale,scale)); + free_num (quot); + *quot = qval; + } + } + + /* Set up the divide. Move the decimal point on n1 by n2's scale. + Remember, zeros on the end of num2 are wasted effort for dividing. */ + scale2 = n2->n_scale; + n2ptr = (unsigned char *) n2->n_value+n2->n_len+scale2-1; + while ((scale2 > 0) && (*n2ptr-- == 0)) scale2--; + + len1 = n1->n_len + scale2; + scale1 = n1->n_scale - scale2; + if (scale1 < scale) + extra = scale - scale1; + else + extra = 0; + num1 = (unsigned char *) malloc (n1->n_len+n1->n_scale+extra+2); + if (num1 == NULL) out_of_memory(); + memset (num1, 0, n1->n_len+n1->n_scale+extra+2); + memcpy (num1+1, n1->n_value, n1->n_len+n1->n_scale); + + len2 = n2->n_len + scale2; + num2 = (unsigned char *) malloc (len2+1); + if (num2 == NULL) out_of_memory(); + memcpy (num2, n2->n_value, len2); + *(num2+len2) = 0; + n2ptr = num2; + while (*n2ptr == 0) + { + n2ptr++; + len2--; + } + + /* Calculate the number of quotient digits. */ + if (len2 > len1+scale) + { + qdigits = scale+1; + zero = TRUE; + } + else + { + zero = FALSE; + if (len2>len1) + qdigits = scale+1; /* One for the zero integer part. */ + else + qdigits = len1-len2+scale+1; + } + + /* Allocate and zero the storage for the quotient. */ + qval = new_num (qdigits-scale,scale); + memset (qval->n_value, 0, qdigits); + + /* Allocate storage for the temporary storage mval. */ + mval = (unsigned char *) malloc (len2+1); + if (mval == NULL) out_of_memory (); + + /* Now for the full divide algorithm. */ + if (!zero) + { + /* Normalize */ + norm = 10 / ((int)*n2ptr + 1); + if (norm != 1) + { + _one_mult (num1, len1+scale1+extra+1, norm, num1); + _one_mult (n2ptr, len2, norm, n2ptr); + } + + /* Initialize divide loop. */ + qdig = 0; + if (len2 > len1) + qptr = (unsigned char *) qval->n_value+len2-len1; + else + qptr = (unsigned char *) qval->n_value; + + /* Loop */ + while (qdig <= len1+scale-len2) + { + /* Calculate the quotient digit guess. */ + if (*n2ptr == num1[qdig]) + qguess = 9; + else + qguess = (num1[qdig]*10 + num1[qdig+1]) / *n2ptr; + + /* Test qguess. */ + if (n2ptr[1]*qguess > + (num1[qdig]*10 + num1[qdig+1] - *n2ptr*qguess)*10 + + num1[qdig+2]) + { + qguess--; + /* And again. */ + if (n2ptr[1]*qguess > + (num1[qdig]*10 + num1[qdig+1] - *n2ptr*qguess)*10 + + num1[qdig+2]) + qguess--; + } + + /* Multiply and subtract. */ + borrow = 0; + if (qguess != 0) + { + *mval = 0; + _one_mult (n2ptr, len2, qguess, mval+1); + ptr1 = (unsigned char *) num1+qdig+len2; + ptr2 = (unsigned char *) mval+len2; + for (count = 0; count < len2+1; count++) + { + val = (int) *ptr1 - (int) *ptr2-- - borrow; + if (val < 0) + { + val += 10; + borrow = 1; + } + else + borrow = 0; + *ptr1-- = val; + } + } + + /* Test for negative result. */ + if (borrow == 1) + { + qguess--; + ptr1 = (unsigned char *) num1+qdig+len2; + ptr2 = (unsigned char *) n2ptr+len2-1; + carry = 0; + for (count = 0; count < len2; count++) + { + val = (int) *ptr1 + (int) *ptr2-- + carry; + if (val > 9) + { + val -= 10; + carry = 1; + } + else + carry = 0; + *ptr1-- = val; + } + if (carry == 1) *ptr1 = (*ptr1 + 1) % 10; + } + + /* We now know the quotient digit. */ + *qptr++ = qguess; + qdig++; + } + } + + /* Clean up and return the number. */ + qval->n_sign = ( n1->n_sign == n2->n_sign ? PLUS : MINUS ); + if (is_zero (qval)) qval->n_sign = PLUS; + _rm_leading_zeros (qval); + free_num (quot); + *quot = qval; + + /* Clean up temporary storage. */ + free (mval); + free (num1); + free (num2); + + return 0; /* Everything is OK. */ +} + + +/* Modulo for numbers. This computes NUM1 % NUM2 and puts the + result in RESULT. */ + +int +bc_modulo (num1, num2, result, scale) + bc_num num1, num2, *result; + int scale; +{ + bc_num temp; + int rscale; + + /* Check for correct numbers. */ + if (is_zero (num2)) return -1; + + /* Calculate final scale. */ + rscale = MAX (num1->n_scale, num2->n_scale+scale); + init_num (&temp); + + /* Calculate it. */ + bc_divide (num1, num2, &temp, scale); + bc_multiply (temp, num2, &temp, rscale); + bc_sub (num1, temp, result); + free_num (&temp); + + return 0; /* Everything is OK. */ +} + + +/* Raise NUM1 to the NUM2 power. The result is placed in RESULT. + Maximum exponent is LONG_MAX. If a NUM2 is not an integer, + only the integer part is used. */ + +void +bc_raise (num1, num2, result, scale) + bc_num num1, num2, *result; + int scale; +{ + bc_num temp, power; + long exponent; + int rscale; + char neg; + + /* Check the exponent for scale digits and convert to a long. */ + if (num2->n_scale != 0) + rt_warn ("non-zero scale in exponent"); + exponent = num2long (num2); + if (exponent == 0 && (num2->n_len > 1 || num2->n_value[0] != 0)) + rt_error ("exponent too large in raise"); + + /* Special case if exponent is a zero. */ + if (exponent == 0) + { + free_num (result); + *result = copy_num (_one_); + return; + } + + /* Other initializations. */ + if (exponent < 0) + { + neg = TRUE; + exponent = -exponent; + rscale = scale; + } + else + { + neg = FALSE; + rscale = MIN (num1->n_scale*exponent, MAX(scale, num1->n_scale)); + } + temp = copy_num (_one_); + power = copy_num (num1); + + /* Do the calculation. */ + while (exponent != 0) + { + if (exponent & 1 != 0) + bc_multiply (temp, power, &temp, rscale); + bc_multiply (power, power, &power, rscale); + exponent = exponent >> 1; + } + + /* Assign the value. */ + if (neg) + { + bc_divide (_one_, temp, result, rscale); + free_num (&temp); + } + else + { + free_num (result); + *result = temp; + } + free_num (&power); +} + + +/* Take the square root NUM and return it in NUM with SCALE digits + after the decimal place. */ + +int +bc_sqrt (num, scale) + bc_num *num; + int scale; +{ + int rscale, cmp_res, done; + int cscale; + bc_num guess, guess1, point5; + + /* Initial checks. */ + cmp_res = bc_compare (*num, _zero_); + if (cmp_res < 0) + return 0; /* error */ + else + { + if (cmp_res == 0) + { + free_num (num); + *num = copy_num (_zero_); + return 1; + } + } + cmp_res = bc_compare (*num, _one_); + if (cmp_res == 0) + { + free_num (num); + *num = copy_num (_one_); + return 1; + } + + /* Initialize the variables. */ + rscale = MAX (scale, (*num)->n_scale); + cscale = rscale + 2; + init_num (&guess); + init_num (&guess1); + point5 = new_num (1,1); + point5->n_value[1] = 5; + + + /* Calculate the initial guess. */ + if (cmp_res < 0) + /* The number is between 0 and 1. Guess should start at 1. */ + guess = copy_num (_one_); + else + { + /* The number is greater than 1. Guess should start at 10^(exp/2). */ + int2num (&guess,10); + int2num (&guess1,(*num)->n_len); + bc_multiply (guess1, point5, &guess1, rscale); + guess1->n_scale = 0; + bc_raise (guess, guess1, &guess, rscale); + free_num (&guess1); + } + + /* Find the square root using Newton's algorithm. */ + done = FALSE; + while (!done) + { + free_num (&guess1); + guess1 = copy_num (guess); + bc_divide (*num,guess,&guess,cscale); + bc_add (guess,guess1,&guess); + bc_multiply (guess,point5,&guess,cscale); + cmp_res = _do_compare (guess,guess1,FALSE,TRUE); + if (cmp_res == 0) done = TRUE; + } + + /* Assign the number and clean up. */ + free_num (num); + bc_divide (guess,_one_,num,rscale); + free_num (&guess); + free_num (&guess1); + free_num (&point5); + return 1; +} + + +/* The following routines provide output for bcd numbers package + using the rules of POSIX bc for output. */ + +/* This structure is used for saving digits in the conversion process. */ +typedef struct stk_rec { + long digit; + struct stk_rec *next; +} stk_rec; + +/* The reference string for digits. */ +char ref_str[] = "0123456789ABCDEF"; + + +/* A special output routine for "multi-character digits." Exactly + SIZE characters must be output for the value VAL. If SPACE is + non-zero, we must output one space before the number. OUT_CHAR + is the actual routine for writing the characters. */ + +void +out_long (val, size, space, out_char) + long val; + int size, space; +#ifdef __STDC__ + void (*out_char)(int); +#else + void (*out_char)(); +#endif +{ + char digits[40]; + int len, ix; + + if (space) (*out_char) (' '); + sprintf (digits, "%ld", val); + len = strlen (digits); + while (size > len) + { + (*out_char) ('0'); + size--; + } + for (ix=0; ix < len; ix++) + (*out_char) (digits[ix]); +} + +/* Output of a bcd number. NUM is written in base O_BASE using OUT_CHAR + as the routine to do the actual output of the characters. */ + +void +out_num (num, o_base, out_char) + bc_num num; + int o_base; +#ifdef __STDC__ + void (*out_char)(int); +#else + void (*out_char)(); +#endif +{ + char *nptr; + int index, fdigit, pre_space; + stk_rec *digits, *temp; + bc_num int_part, frac_part, base, cur_dig, t_num, max_o_digit; + + /* The negative sign if needed. */ + if (num->n_sign == MINUS) (*out_char) ('-'); + + /* Output the number. */ + if (is_zero (num)) + (*out_char) ('0'); + else + if (o_base == 10) + { + /* The number is in base 10, do it the fast way. */ + nptr = num->n_value; + if (num->n_len > 1 || *nptr != 0) + for (index=num->n_len; index>0; index--) + (*out_char) (BCD_CHAR(*nptr++)); + else + nptr++; + + /* Now the fraction. */ + if (num->n_scale > 0) + { + (*out_char) ('.'); + for (index=0; indexn_scale; index++) + (*out_char) (BCD_CHAR(*nptr++)); + } + } + else + { + /* The number is some other base. */ + digits = NULL; + init_num (&int_part); + bc_divide (num, _one_, &int_part, 0); + init_num (&frac_part); + init_num (&cur_dig); + init_num (&base); + bc_sub (num, int_part, &frac_part); + int2num (&base, o_base); + init_num (&max_o_digit); + int2num (&max_o_digit, o_base-1); + + + /* Get the digits of the integer part and push them on a stack. */ + while (!is_zero (int_part)) + { + bc_modulo (int_part, base, &cur_dig, 0); + temp = (stk_rec *) malloc (sizeof(stk_rec)); + if (temp == NULL) out_of_memory(); + temp->digit = num2long (cur_dig); + temp->next = digits; + digits = temp; + bc_divide (int_part, base, &int_part, 0); + } + + /* Print the digits on the stack. */ + if (digits != NULL) + { + /* Output the digits. */ + while (digits != NULL) + { + temp = digits; + digits = digits->next; + if (o_base <= 16) + (*out_char) (ref_str[ (int) temp->digit]); + else + out_long (temp->digit, max_o_digit->n_len, 1, out_char); + free (temp); + } + } + + /* Get and print the digits of the fraction part. */ + if (num->n_scale > 0) + { + (*out_char) ('.'); + pre_space = 0; + t_num = copy_num (_one_); + while (t_num->n_len <= num->n_scale) { + bc_multiply (frac_part, base, &frac_part, num->n_scale); + fdigit = num2long (frac_part); + int2num (&int_part, fdigit); + bc_sub (frac_part, int_part, &frac_part); + if (o_base <= 16) + (*out_char) (ref_str[fdigit]); + else { + out_long (fdigit, max_o_digit->n_len, pre_space, out_char); + pre_space = 1; + } + bc_multiply (t_num, base, &t_num, 0); + } + } + + /* Clean up. */ + free_num (&int_part); + free_num (&frac_part); + free_num (&base); + free_num (&cur_dig); + } +} + + +#if DEBUG > 0 + +/* Debugging procedures. Some are just so one can call them from the + debugger. */ + +/* p_n prints the number NUM in base 10. */ + +void +p_n (num) + bc_num num; +{ + out_num (num, 10, out_char); + return 0; +} + + +/* p_b prints a character array as if it was a string of bcd digits. */ +void +p_v (name, num, len) + char *name; + unsigned char *num; + int len; +{ + int i; + printf ("%s=", name); + for (i=0; in_sign = MINUS; + ptr++; + } + else + { + (*num)->n_sign = PLUS; + if (*ptr == '+') ptr++; + } + while (*ptr == '0') ptr++; /* Skip leading zeros. */ + nptr = (*num)->n_value; + if (zero_int) + { + *nptr++ = 0; + digits = 0; + } + for (;digits > 0; digits--) + *nptr++ = CH_VAL(*ptr++); + + + /* Build the fractional part. */ + if (strscale > 0) + { + ptr++; /* skip the decimal point! */ + for (;strscale > 0; strscale--) + *nptr++ = CH_VAL(*ptr++); + } +} + +/* Convert a numbers to a string. Base 10 only.*/ + +char +*num2str (num) + bc_num num; +{ + char *str, *sptr; + char *nptr; + int index, signch; + + /* Allocate the string memory. */ + signch = ( num->n_sign == PLUS ? 0 : 1 ); /* Number of sign chars. */ + if (num->n_scale > 0) + str = (char *) malloc (num->n_len + num->n_scale + 2 + signch); + else + str = (char *) malloc (num->n_len + 1 + signch); + if (str == NULL) out_of_memory(); + + /* The negative sign if needed. */ + sptr = str; + if (signch) *sptr++ = '-'; + + /* Load the whole number. */ + nptr = num->n_value; + for (index=num->n_len; index>0; index--) + *sptr++ = BCD_CHAR(*nptr++); + + /* Now the fraction. */ + if (num->n_scale > 0) + { + *sptr++ = '.'; + for (index=0; indexn_scale; index++) + *sptr++ = BCD_CHAR(*nptr++); + } + + /* Terminate the string and return it! */ + *sptr = '\0'; + return (str); +} +#endif diff --git a/commands/bc/number.h b/commands/bc/number.h new file mode 100755 index 000000000..993851517 --- /dev/null +++ b/commands/bc/number.h @@ -0,0 +1,60 @@ +/* number.h: Arbitrary precision numbers header file. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + + +typedef enum {PLUS, MINUS} sign; + +typedef struct + { + sign n_sign; + int n_len; /* The number of digits before the decimal point. */ + int n_scale; /* The number of digits after the decimal point. */ + int n_refs; /* The number of pointers to this number. */ + char n_value[1]; /* The storage. Not zero char terminated. It is + allocated with all other fields. */ + } bc_struct; + +typedef bc_struct *bc_num; + +/* Some useful macros and constants. */ + +#define CH_VAL(c) (c - '0') +#define BCD_CHAR(d) (d + '0') + +#ifdef MIN +#undef MIN +#undef MAX +#endif +#define MAX(a,b) (a>b?a:b) +#define MIN(a,b) (a>b?b:a) +#define ODD(a) (a&1) + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif diff --git a/commands/bc/proto.h b/commands/bc/proto.h new file mode 100755 index 000000000..3e04ebc60 --- /dev/null +++ b/commands/bc/proto.h @@ -0,0 +1,165 @@ +/* proto.h: Prototype function definitions for "external" functions. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +/* For the pc version using k&r ACK. (minix1.5 and earlier.) */ +#ifdef SHORTNAMES +#define init_numbers i_numbers +#define push_constant push__constant +#define load_const in_load_const +#define yy_get_next_buffer yyget_next_buffer +#define yy_init_buffer yyinit_buffer +#define yy_last_accepting_state yylast_accepting_state +#define arglist1 arg1list +#endif + +/* Include the standard library header files. */ +#ifndef NO_UNISTD +#include +#endif +#ifndef NO_STDLIB +#ifdef __STDC__ +#include +#endif +#endif + +/* Define the _PROTOTYPE macro if it is needed. */ + +#ifndef _PROTOTYPE +#ifdef __STDC__ +#define _PROTOTYPE(func, args) func args +#else +#define _PROTOTYPE(func, args) func() +#endif +#endif + +/* From execute.c */ +_PROTOTYPE(void stop_execution, (int)); +_PROTOTYPE(unsigned char byte, (program_counter *pc)); +_PROTOTYPE(void execute, (void)); +_PROTOTYPE(char prog_char, (void)); +_PROTOTYPE(char input_char, (void)); +_PROTOTYPE(void push_constant, (char (*in_char)(void), int conv_base)); +_PROTOTYPE(void push_b10_const, (program_counter *pc)); +_PROTOTYPE(void assign, (int c_code)); + +/* From util.c */ +_PROTOTYPE(char *strcopyof, (char *str)); +_PROTOTYPE(arg_list *nextarg, (arg_list *args, int val)); +_PROTOTYPE(char *arg_str, (arg_list *args, int)); +_PROTOTYPE(void free_args, (arg_list *args)); +_PROTOTYPE(void check_params, (arg_list *params, arg_list *autos)); +_PROTOTYPE(void init_gen, (void)); +_PROTOTYPE(void generate, (char *str)); +_PROTOTYPE(void run_code, (void)); +_PROTOTYPE(void out_char, (int ch)); +_PROTOTYPE(id_rec *find_id, (id_rec *tree, char *id)); +_PROTOTYPE(int insert_id_rec, (id_rec **root, id_rec *new_id)); +_PROTOTYPE(void init_tree, (void)); +_PROTOTYPE(int lookup, (char *name, int namekind)); +_PROTOTYPE(char *bc_malloc, (int)); +_PROTOTYPE(void out_of_memory, (void)); +_PROTOTYPE(void welcome, (void)); +_PROTOTYPE(void warranty, (char *)); +_PROTOTYPE(void limits, (void)); +_PROTOTYPE(void yyerror, (char *str ,...)); +_PROTOTYPE(void warn, (char *mesg ,...)); +_PROTOTYPE(void rt_error, (char *mesg ,...)); +_PROTOTYPE(void rt_warn, (char *mesg ,...)); + +/* From load.c */ +_PROTOTYPE(void init_load, (void)); +_PROTOTYPE(void addbyte, (int byte)); +_PROTOTYPE(void def_label, (long lab)); +_PROTOTYPE(long long_val, (char **str)); +_PROTOTYPE(void load_code, (char *code)); + +/* From main.c */ +_PROTOTYPE(int main, (int argc , char *argv [])); +_PROTOTYPE(int open_new_file, (void)); +_PROTOTYPE(void new_yy_file, (FILE *file)); +_PROTOTYPE(void use_quit, (int)); + +/* From number.c */ +_PROTOTYPE(void free_num, (bc_num *num)); +_PROTOTYPE(bc_num new_num, (int length, int scale)); +_PROTOTYPE(void init_numbers, (void)); +_PROTOTYPE(bc_num copy_num, (bc_num num)); +_PROTOTYPE(void init_num, (bc_num *num)); +_PROTOTYPE(void str2num, (bc_num *num, char *str, int scale)); +_PROTOTYPE(char *num2str, (bc_num num)); +_PROTOTYPE(void int2num, (bc_num *num, int val)); +_PROTOTYPE(long num2long, (bc_num num)); +_PROTOTYPE(int bc_compare, (bc_num n1, bc_num n2)); +_PROTOTYPE(char is_zero, (bc_num num)); +_PROTOTYPE(char is_neg, (bc_num num)); +_PROTOTYPE(void bc_add, (bc_num n1, bc_num n2, bc_num *result)); +_PROTOTYPE(void bc_sub, (bc_num n1, bc_num n2, bc_num *result)); +_PROTOTYPE(void bc_multiply, (bc_num n1, bc_num n2, bc_num *prod, int scale)); +_PROTOTYPE(int bc_divide, (bc_num n1, bc_num n2, bc_num *quot, int scale)); +_PROTOTYPE(int bc_modulo, (bc_num num1, bc_num num2, bc_num *result, int scale)); +_PROTOTYPE(void bc_raise, (bc_num num1, bc_num num2, bc_num *result, int scale)); +_PROTOTYPE(int bc_sqrt, (bc_num *num, int scale)); +_PROTOTYPE(void out_long, (long val, int size, int space, + void (*out_char)(int))); +_PROTOTYPE(void out_num, (bc_num num, int o_base, void (* out_char)(int))); + + +/* From storage.c */ +_PROTOTYPE(void init_storage, (void)); +_PROTOTYPE(void more_functions, (void)); +_PROTOTYPE(void more_variables, (void)); +_PROTOTYPE(void more_arrays, (void)); +_PROTOTYPE(void clear_func, (int func )); +_PROTOTYPE(int fpop, (void)); +_PROTOTYPE(void fpush, (int val )); +_PROTOTYPE(void pop, (void)); +_PROTOTYPE(void push_copy, (bc_num num )); +_PROTOTYPE(void push_num, (bc_num num )); +_PROTOTYPE(char check_stack, (int depth )); +_PROTOTYPE(bc_var *get_var, (int var_name )); +_PROTOTYPE(bc_num *get_array_num, (int var_index, long index )); +_PROTOTYPE(void store_var, (int var_name )); +_PROTOTYPE(void store_array, (int var_name )); +_PROTOTYPE(void load_var, (int var_name )); +_PROTOTYPE(void load_array, (int var_name )); +_PROTOTYPE(void decr_var, (int var_name )); +_PROTOTYPE(void decr_array, (int var_name )); +_PROTOTYPE(void incr_var, (int var_name )); +_PROTOTYPE(void incr_array, (int var_name )); +_PROTOTYPE(void auto_var, (int name )); +_PROTOTYPE(void free_a_tree, (bc_array_node *root, int depth )); +_PROTOTYPE(void pop_vars, (arg_list *list )); +_PROTOTYPE(void process_params, (program_counter *pc, int func )); + +/* For the scanner and parser.... */ +_PROTOTYPE(int yyparse, (void)); +_PROTOTYPE(int yylex, (void)); + +/* Other things... */ +_PROTOTYPE(int getopt, (int, char * const [], const char * )); + diff --git a/commands/bc/sbc.y b/commands/bc/sbc.y new file mode 100755 index 000000000..815941063 --- /dev/null +++ b/commands/bc/sbc.y @@ -0,0 +1,448 @@ +, %{ +/* sbc.y: A POSIX bc processor written for minix with no extensions. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "global.h" /* To get the global variables. */ +#include "proto.h" +%} + +%start program + +%union { + char *s_value; + char c_value; + int i_value; + arg_list *a_value; + } + +%token NEWLINE AND OR NOT +%token STRING NAME NUMBER +/* '-', '+' are tokens themselves */ +%token MUL_OP +/* '*', '/', '%' */ +%token ASSIGN_OP +/* '=', '+=', '-=', '*=', '/=', '%=', '^=' */ +%token REL_OP +/* '==', '<=', '>=', '!=', '<', '>' */ +%token INCR_DECR +/* '++', '--' */ +%token Define Break Quit Length +/* 'define', 'break', 'quit', 'length' */ +%token Return For If While Sqrt Else +/* 'return', 'for', 'if', 'while', 'sqrt', 'else' */ +%token Scale Ibase Obase Auto Read +/* 'scale', 'ibase', 'obase', 'auto', 'read' */ +%token Warranty, Halt, Last, Continue, Print, Limits +/* 'warranty', 'halt', 'last', 'continue', 'print', 'limits' */ + +/* The types of all other non-terminals. */ +%type expression named_expression return_expression +%type opt_parameter_list parameter_list opt_auto_define_list +%type define_list opt_argument_list argument_list +%type program input_item semicolon_list statement_list +%type statement_or_error statement function relational_expression + +/* precedence */ +%nonassoc REL_OP +%right ASSIGN_OP +%left '+' '-' +%left MUL_OP +%right '^' +%nonassoc UNARY_MINUS +%nonassoc INCR_DECR + +%% +program : /* empty */ + { + $$ = 0; + std_only = TRUE; + if (interactive) + { + printf ("s%s\n", BC_VERSION); + welcome(); + } + } + | program input_item + ; +input_item : semicolon_list NEWLINE + { run_code(); } + | function + { run_code(); } + | error NEWLINE + { + yyerrok; + init_gen() ; + } + ; +semicolon_list : /* empty */ + { $$ = 0; } + | statement_or_error + | semicolon_list ';' statement_or_error + | semicolon_list ';' + ; +statement_list : /* empty */ + { $$ = 0; } + | statement + | statement_list NEWLINE + | statement_list NEWLINE statement + | statement_list ';' + | statement_list ';' statement + ; +statement_or_error : statement + | error statement + { $$ = $2; } + ; +statement : Warranty + { warranty("s"); } + | expression + { + if ($1 & 1) + generate ("W"); + else + generate ("p"); + } + | STRING + { + $$ = 0; + generate ("w"); + generate ($1); + free ($1); + } + | Break + { + if (break_label == 0) + yyerror ("Break outside a for/while"); + else + { + sprintf (genstr, "J%1d:", break_label); + generate (genstr); + } + } + | Quit + { exit(0); } + | Return + { generate ("0R"); } + | Return '(' return_expression ')' + { generate ("R"); } + | For + { + $1 = break_label; + break_label = next_label++; + } + '(' expression ';' + { + $4 = next_label++; + sprintf (genstr, "pN%1d:", $4); + generate (genstr); + } + relational_expression ';' + { + $7 = next_label++; + sprintf (genstr, "B%1d:J%1d:", $7, break_label); + generate (genstr); + $$ = next_label++; + sprintf (genstr, "N%1d:", $$); + generate (genstr); + } + expression ')' + { + sprintf (genstr, "pJ%1d:N%1d:", $4, $7); + generate (genstr); + } + statement + { + sprintf (genstr, "J%1d:N%1d:", $9, + break_label); + generate (genstr); + break_label = $1; + } + | If '(' relational_expression ')' + { + $3 = next_label++; + sprintf (genstr, "Z%1d:", $3); + generate (genstr); + } + statement + { + sprintf (genstr, "N%1d:", $3); + generate (genstr); + } + | While + { + $1 = next_label++; + sprintf (genstr, "N%1d:", $1); + generate (genstr); + } + '(' relational_expression + { + $4 = break_label; + break_label = next_label++; + sprintf (genstr, "Z%1d:", break_label); + generate (genstr); + } + ')' statement + { + sprintf (genstr, "J%1d:N%1d:", $1, break_label); + generate (genstr); + break_label = $4; + } + | '{' statement_list '}' + { $$ = 0; } + ; +function : Define NAME '(' opt_parameter_list ')' '{' + NEWLINE opt_auto_define_list + { + check_params ($4,$8); + sprintf (genstr, "F%d,%s.%s[", lookup($2,FUNCT), + arg_str ($4,TRUE), arg_str ($8,TRUE)); + generate (genstr); + free_args ($4); + free_args ($8); + $1 = next_label; + next_label = 0; + } + statement_list NEWLINE '}' + { + generate ("0R]"); + next_label = $1; + } + ; +opt_parameter_list : /* empty */ + { $$ = NULL; } + | parameter_list + ; +parameter_list : NAME + { $$ = nextarg (NULL, lookup($1,SIMPLE)); } + | define_list ',' NAME + { $$ = nextarg ($1, lookup($3,SIMPLE)); } + ; +opt_auto_define_list : /* empty */ + { $$ = NULL; } + | Auto define_list NEWLINE + { $$ = $2; } + | Auto define_list ';' + { $$ = $2; } + ; +define_list : NAME + { $$ = nextarg (NULL, lookup($1,SIMPLE)); } + | NAME '[' ']' + { $$ = nextarg (NULL, lookup($1,ARRAY)); } + | define_list ',' NAME + { $$ = nextarg ($1, lookup($3,SIMPLE)); } + | define_list ',' NAME '[' ']' + { $$ = nextarg ($1, lookup($3,ARRAY)); } + ; +opt_argument_list : /* empty */ + { $$ = NULL; } + | argument_list + ; +argument_list : expression + { $$ = nextarg (NULL,0); } + | argument_list ',' expression + { $$ = nextarg ($1,0); } + ; +relational_expression : expression + { $$ = 0; } + | expression REL_OP expression + { + $$ = 0; + switch (*($2)) + { + case '=': + generate ("="); + break; + case '!': + generate ("#"); + break; + case '<': + if ($2[1] == '=') + generate ("{"); + else + generate ("<"); + break; + case '>': + if ($2[1] == '=') + generate ("}"); + else + generate (">"); + break; + } + } + ; +return_expression : /* empty */ + { + $$ = 0; + generate ("0"); + } + | expression + ; +expression : named_expression ASSIGN_OP + { + if ($2 != '=') + { + if ($1 < 0) + sprintf (genstr, "DL%d:", -$1); + else + sprintf (genstr, "l%d:", $1); + generate (genstr); + } + } + expression + { + $$ = 0; + if ($2 != '=') + { + sprintf (genstr, "%c", $2); + generate (genstr); + } + if ($1 < 0) + sprintf (genstr, "S%d:", -$1); + else + sprintf (genstr, "s%d:", $1); + generate (genstr); + } + | expression '+' expression + { generate ("+"); } + | expression '-' expression + { generate ("-"); } + | expression MUL_OP expression + { + genstr[0] = $2; + genstr[1] = 0; + generate (genstr); + } + | expression '^' expression + { generate ("^"); } + | '-' expression %prec UNARY_MINUS + { generate ("n"); $$ = 1;} + | named_expression + { + $$ = 1; + if ($1 < 0) + sprintf (genstr, "L%d:", -$1); + else + sprintf (genstr, "l%d:", $1); + generate (genstr); + } + | NUMBER + { + int len = strlen($1); + $$ = 1; + if (len == 1 && *$1 == '0') + generate ("0"); + else + { + if (len == 1 && *$1 == '1') + generate ("1"); + else + { + generate ("K"); + generate ($1); + generate (":"); + } + free ($1); + } + } + | '(' expression ')' + { $$ = 1; } + | NAME '(' opt_argument_list ')' + { + $$ = 1; + if ($3 != NULL) + { + sprintf (genstr, "C%d,%s:", lookup($1,FUNCT), + arg_str ($3,FALSE)); + free_args ($3); + } + else + sprintf (genstr, "C%d:", lookup($1,FUNCT)); + generate (genstr); + } + | INCR_DECR named_expression + { + $$ = 1; + if ($2 < 0) + { + if ($1 == '+') + sprintf (genstr, "DA%d:L%d:", -$2, -$2); + else + sprintf (genstr, "DM%d:L%d:", -$2, -$2); + } + else + { + if ($1 == '+') + sprintf (genstr, "i%d:l%d:", $2, $2); + else + sprintf (genstr, "d%d:l%d:", $2, $2); + } + generate (genstr); + } + | named_expression INCR_DECR + { + $$ = 1; + if ($1 < 0) + { + sprintf (genstr, "DL%d:x", -$1); + generate (genstr); + if ($2 == '+') + sprintf (genstr, "A%d:", -$1); + else + sprintf (genstr, "M%d:", -$1); + } + else + { + sprintf (genstr, "l%d:", $1); + generate (genstr); + if ($2 == '+') + sprintf (genstr, "i%d:", $1); + else + sprintf (genstr, "d%d:", $1); + } + generate (genstr); + } + | Length '(' expression ')' + { generate ("cL"); $$ = 1;} + | Sqrt '(' expression ')' + { generate ("cR"); $$ = 1;} + | Scale '(' expression ')' + { generate ("cS"); $$ = 1;} + ; +named_expression : NAME + { $$ = lookup($1,SIMPLE); } + | NAME '[' expression ']' + { $$ = lookup($1,ARRAY); } + | Ibase + { $$ = 0; } + | Obase + { $$ = 1; } + | Scale + { $$ = 2; } + ; + +%% diff --git a/commands/bc/scan.c b/commands/bc/scan.c new file mode 100755 index 000000000..46731a42f --- /dev/null +++ b/commands/bc/scan.c @@ -0,0 +1,1369 @@ +/* A lexical scanner generated by flex */ + +/* scanner skeleton version: + * $Header$ + */ + +#define FLEX_SCANNER + +#include + + +/* cfront 1.2 defines "c_plusplus" instead of "__cplusplus" */ +#ifdef c_plusplus +#ifndef __cplusplus +#define __cplusplus +#endif +#endif + + +#ifdef __cplusplus + +#include +#include + +/* use prototypes in function declarations */ +#define YY_USE_PROTOS + +/* the "const" storage-class-modifier is valid */ +#define YY_USE_CONST + +#else /* ! __cplusplus */ + +#ifdef __STDC__ + +#ifdef __GNUC__ +#include +void *malloc( size_t ); +void free( void* ); +#else +#include +#endif /* __GNUC__ */ + +#define YY_USE_PROTOS +#define YY_USE_CONST + +#endif /* __STDC__ */ +#endif /* ! __cplusplus */ + + +#ifdef __TURBOC__ +#define YY_USE_CONST +#endif + + +#ifndef YY_USE_CONST +#define const +#endif + + +#ifdef YY_USE_PROTOS +#define YY_PROTO(proto) proto +#else +#define YY_PROTO(proto) () +/* we can't get here if it's an ANSI C compiler, or a C++ compiler, + * so it's got to be a K&R compiler, and therefore there's no standard + * place from which to include these definitions + */ +char *malloc(); +int free(); +int read(); +#endif + + +/* amount of stuff to slurp up with each read */ +#ifndef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 8192 +#endif + +/* returned upon end-of-file */ +#define YY_END_TOK 0 + +/* copy whatever the last rule matched to the standard output */ + +/* cast to (char *) is because for 8-bit chars, yytext is (unsigned char *) */ +/* this used to be an fputs(), but since the string might contain NUL's, + * we now use fwrite() + */ +#define ECHO (void) fwrite( (char *) yytext, yyleng, 1, yyout ) + +/* gets input and stuffs it into "buf". number of characters read, or YY_NULL, + * is returned in "result". + */ +#define YY_INPUT(buf,result,max_size) \ + if ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ + YY_FATAL_ERROR( "read() in flex scanner failed" ); +#define YY_NULL 0 + +/* no semi-colon after return; correct usage is to write "yyterminate();" - + * we don't want an extra ';' after the "return" because that will cause + * some compilers to complain about unreachable statements. + */ +#define yyterminate() return ( YY_NULL ) + +/* report a fatal error */ + +/* The funky do-while is used to turn this macro definition into + * a single C statement (which needs a semi-colon terminator). + * This avoids problems with code like: + * + * if ( something_happens ) + * YY_FATAL_ERROR( "oops, the something happened" ); + * else + * everything_okay(); + * + * Prior to using the do-while the compiler would get upset at the + * "else" because it interpreted the "if" statement as being all + * done when it reached the ';' after the YY_FATAL_ERROR() call. + */ + +#define YY_FATAL_ERROR(msg) \ + do \ + { \ + (void) fputs( msg, stderr ); \ + (void) putc( '\n', stderr ); \ + exit( 1 ); \ + } \ + while ( 0 ) + +/* default yywrap function - always treat EOF as an EOF */ +#define yywrap() 1 + +/* enter a start condition. This macro really ought to take a parameter, + * but we do it the disgusting crufty way forced on us by the ()-less + * definition of BEGIN + */ +#define BEGIN yy_start = 1 + 2 * + +/* action number for EOF rule of a given start state */ +#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1) + +/* special action meaning "start processing a new file" */ +#define YY_NEW_FILE \ + do \ + { \ + yy_init_buffer( yy_current_buffer, yyin ); \ + yy_load_buffer_state(); \ + } \ + while ( 0 ) + +/* default declaration of generated scanner - a define so the user can + * easily add parameters + */ +#define YY_DECL int yylex YY_PROTO(( void )) + +/* code executed at the end of each rule */ +#define YY_BREAK break; + +#define YY_END_OF_BUFFER_CHAR 0 + +#ifndef YY_BUF_SIZE +#define YY_BUF_SIZE (YY_READ_BUF_SIZE * 2) /* size of default input buffer */ +#endif + +typedef struct yy_buffer_state *YY_BUFFER_STATE; + +#define YY_CHAR unsigned char +# line 1 "scan.l" +#define INITIAL 0 +# line 2 "scan.l" +/* scan.l: the (f)lex description file for the scanner. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "y.tab.h" +#include "global.h" +#include "proto.h" + +/* Using flex, we can ask for a smaller input buffer. With lex, this + does nothing! */ + +#ifdef SMALL_BUF +#undef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 512 +#endif + +/* We want to define our own yywrap. */ +#undef yywrap +_PROTOTYPE(int yywrap, (void)); + +/* MINIX returns from read with < 0 if SIGINT is encountered. + In flex, we can redefine YY_INPUT to the following. In lex, this + does nothing! */ +#include +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + while ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ + if (errno != EINTR) \ + YY_FATAL_ERROR( "read() in flex scanner failed" ); + +# line 60 "scan.l" + +/* done after the current pattern has been matched and before the + * corresponding action - sets up yytext + */ +#define YY_DO_BEFORE_ACTION \ + yytext = yy_bp; \ + yyleng = yy_cp - yy_bp; \ + yy_hold_char = *yy_cp; \ + *yy_cp = '\0'; \ + yy_c_buf_p = yy_cp; + +#define EOB_ACT_CONTINUE_SCAN 0 +#define EOB_ACT_END_OF_FILE 1 +#define EOB_ACT_LAST_MATCH 2 + +/* return all but the first 'n' matched characters back to the input stream */ +#define yyless(n) \ + do \ + { \ + /* undo effects of setting up yytext */ \ + *yy_cp = yy_hold_char; \ + yy_c_buf_p = yy_cp = yy_bp + n; \ + YY_DO_BEFORE_ACTION; /* set up yytext again */ \ + } \ + while ( 0 ) + +#define unput(c) yyunput( c, yytext ) + + +struct yy_buffer_state + { + FILE *yy_input_file; + + YY_CHAR *yy_ch_buf; /* input buffer */ + YY_CHAR *yy_buf_pos; /* current position in input buffer */ + + /* size of input buffer in bytes, not including room for EOB characters*/ + int yy_buf_size; + + /* number of characters read into yy_ch_buf, not including EOB characters */ + int yy_n_chars; + + int yy_eof_status; /* whether we've seen an EOF on this buffer */ +#define EOF_NOT_SEEN 0 + /* "pending" happens when the EOF has been seen but there's still + * some text process + */ +#define EOF_PENDING 1 +#define EOF_DONE 2 + }; + +static YY_BUFFER_STATE yy_current_buffer; + +/* we provide macros for accessing buffer states in case in the + * future we want to put the buffer states in a more general + * "scanner state" + */ +#define YY_CURRENT_BUFFER yy_current_buffer + + +/* yy_hold_char holds the character lost when yytext is formed */ +static YY_CHAR yy_hold_char; + +static int yy_n_chars; /* number of characters read into yy_ch_buf */ + + + +#ifndef YY_USER_ACTION +#define YY_USER_ACTION +#endif + +#ifndef YY_USER_INIT +#define YY_USER_INIT +#endif + +extern YY_CHAR *yytext; +extern int yyleng; +extern FILE *yyin, *yyout; + +YY_CHAR *yytext; +int yyleng; + +FILE *yyin = (FILE *) 0, *yyout = (FILE *) 0; + +#define YY_END_OF_BUFFER 40 +typedef int yy_state_type; +static const short int yy_accept[144] = + { 0, + 0, 0, 40, 38, 33, 31, 25, 38, 26, 38, + 22, 26, 22, 22, 38, 26, 37, 29, 27, 29, + 38, 22, 35, 35, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 38, 33, + 29, 0, 36, 27, 23, 30, 37, 0, 34, 37, + 37, 0, 28, 32, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 7, 35, 35, 35, 35, 35, 35, + 35, 35, 35, 35, 35, 24, 37, 0, 0, 37, + 0, 35, 35, 35, 35, 35, 6, 35, 35, 35, + 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, + + 35, 13, 35, 35, 35, 14, 16, 35, 17, 35, + 35, 35, 35, 3, 15, 35, 35, 9, 35, 35, + 2, 35, 35, 11, 35, 35, 12, 20, 35, 10, + 35, 8, 35, 1, 4, 21, 5, 35, 35, 35, + 19, 18, 0 + } ; + +static const YY_CHAR yy_ec[256] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 2, 4, 5, 1, 1, 6, 7, 1, 8, + 9, 10, 11, 12, 13, 14, 15, 16, 16, 16, + 16, 16, 16, 16, 16, 16, 16, 1, 17, 18, + 19, 20, 1, 1, 21, 21, 21, 21, 21, 21, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 22, 23, 24, 25, 26, 1, 27, 28, 29, 30, + + 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 36, 48, 36, + 49, 36, 50, 51, 52, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1 + } ; + +static const YY_CHAR yy_meta[53] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, + 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, + 1, 1 + } ; + +static const short int yy_base[146] = + { 0, + 0, 0, 193, 194, 190, 194, 172, 185, 170, 181, + 194, 168, 42, 41, 41, 46, 52, 167, 61, 166, + 181, 164, 135, 137, 139, 148, 140, 136, 0, 149, + 27, 50, 147, 130, 126, 141, 40, 36, 120, 168, + 194, 164, 194, 194, 194, 194, 66, 165, 194, 72, + 76, 164, 194, 194, 0, 120, 134, 124, 131, 117, + 117, 122, 132, 0, 113, 117, 117, 128, 119, 118, + 52, 125, 107, 106, 114, 194, 80, 145, 84, 88, + 144, 105, 118, 98, 108, 111, 0, 95, 95, 93, + 105, 102, 91, 95, 88, 103, 85, 93, 84, 85, + + 90, 0, 90, 91, 85, 0, 0, 93, 0, 77, + 76, 90, 74, 0, 0, 75, 87, 0, 90, 85, + 0, 75, 83, 0, 76, 63, 0, 0, 66, 0, + 62, 0, 47, 0, 0, 0, 0, 45, 53, 29, + 0, 0, 194, 111, 56 + } ; + +static const short int yy_def[146] = + { 0, + 143, 1, 143, 143, 143, 143, 143, 144, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 143, 143, + 143, 144, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 143, 143, 143, 143, 143, + 143, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 145, 145, 145, 145, 145, 145, 145, 145, + 145, 145, 0, 143, 143 + } ; + +static const short int yy_nxt[247] = + { 0, + 4, 5, 6, 7, 8, 9, 10, 11, 11, 12, + 13, 11, 14, 15, 16, 17, 11, 18, 19, 20, + 17, 11, 21, 11, 22, 4, 23, 24, 25, 26, + 27, 28, 29, 30, 31, 29, 29, 32, 29, 29, + 33, 34, 35, 36, 37, 29, 29, 38, 29, 11, + 39, 11, 46, 46, 63, 49, 47, 55, 64, 44, + 44, 47, 74, 48, 44, 50, 53, 51, 72, 75, + 53, 53, 51, 53, 52, 53, 65, 142, 96, 41, + 66, 77, 73, 141, 67, 53, 77, 80, 78, 50, + 140, 51, 80, 139, 81, 77, 51, 97, 52, 47, + + 77, 138, 78, 80, 47, 137, 48, 136, 80, 135, + 81, 42, 42, 134, 133, 132, 131, 130, 129, 128, + 127, 126, 125, 124, 123, 122, 121, 120, 119, 118, + 117, 116, 115, 114, 113, 112, 111, 110, 109, 108, + 107, 106, 105, 104, 103, 102, 80, 77, 101, 100, + 99, 98, 95, 94, 93, 92, 91, 90, 89, 88, + 87, 86, 85, 84, 83, 82, 51, 79, 43, 40, + 76, 71, 70, 69, 68, 62, 61, 60, 59, 58, + 57, 56, 44, 54, 41, 41, 44, 45, 44, 43, + 41, 40, 143, 3, 143, 143, 143, 143, 143, 143, + + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143 + } ; + +static const short int yy_chk[247] = + { 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 13, 14, 31, 16, 15, 145, 31, 14, + 13, 15, 38, 15, 16, 17, 19, 17, 37, 38, + 19, 19, 17, 19, 17, 19, 32, 140, 71, 19, + 32, 47, 37, 139, 32, 19, 47, 50, 47, 51, + 138, 51, 50, 133, 50, 77, 51, 71, 51, 79, + + 77, 131, 77, 80, 79, 129, 79, 126, 80, 125, + 80, 144, 144, 123, 122, 120, 119, 117, 116, 113, + 112, 111, 110, 108, 105, 104, 103, 101, 100, 99, + 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, + 88, 86, 85, 84, 83, 82, 81, 78, 75, 74, + 73, 72, 70, 69, 68, 67, 66, 65, 63, 62, + 61, 60, 59, 58, 57, 56, 52, 48, 42, 40, + 39, 36, 35, 34, 33, 30, 28, 27, 26, 25, + 24, 23, 22, 21, 20, 18, 12, 10, 9, 8, + 7, 5, 3, 143, 143, 143, 143, 143, 143, 143, + + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143, 143, 143, 143, 143, + 143, 143, 143, 143, 143, 143 + } ; + +static yy_state_type yy_last_accepting_state; +static YY_CHAR *yy_last_accepting_cpos; + +/* the intent behind this definition is that it'll catch + * any uses of REJECT which flex missed + */ +#define REJECT reject_used_but_not_detected +#define yymore() yymore_used_but_not_detected +#define YY_MORE_ADJ 0 + +/* these variables are all declared out here so that section 3 code can + * manipulate them + */ +/* points to current character in buffer */ +static YY_CHAR *yy_c_buf_p = (YY_CHAR *) 0; +static int yy_init = 1; /* whether we need to initialize */ +static int yy_start = 0; /* start state number */ + +/* flag which is used to allow yywrap()'s to do buffer switches + * instead of setting up a fresh yyin. A bit of a hack ... + */ +static int yy_did_buffer_switch_on_eof; + +static yy_state_type yy_get_previous_state YY_PROTO(( void )); +static yy_state_type yy_try_NUL_trans YY_PROTO(( yy_state_type current_state )); +static int yy_get_next_buffer YY_PROTO(( void )); +static void yyunput YY_PROTO(( YY_CHAR c, YY_CHAR *buf_ptr )); +void yyrestart YY_PROTO(( FILE *input_file )); +void yy_switch_to_buffer YY_PROTO(( YY_BUFFER_STATE new_buffer )); +void yy_load_buffer_state YY_PROTO(( void )); +YY_BUFFER_STATE yy_create_buffer YY_PROTO(( FILE *file, int size )); +void yy_delete_buffer YY_PROTO(( YY_BUFFER_STATE b )); +void yy_init_buffer YY_PROTO(( YY_BUFFER_STATE b, FILE *file )); + +#define yy_new_buffer yy_create_buffer + +#ifdef __cplusplus +static int yyinput YY_PROTO(( void )); +#else +static int input YY_PROTO(( void )); +#endif + +YY_DECL + { + register yy_state_type yy_current_state; + register YY_CHAR *yy_cp, *yy_bp; + register int yy_act; + + + + if ( yy_init ) + { + YY_USER_INIT; + + if ( ! yy_start ) + yy_start = 1; /* first start state */ + + if ( ! yyin ) + yyin = stdin; + + if ( ! yyout ) + yyout = stdout; + + if ( yy_current_buffer ) + yy_init_buffer( yy_current_buffer, yyin ); + else + yy_current_buffer = yy_create_buffer( yyin, YY_BUF_SIZE ); + + yy_load_buffer_state(); + + yy_init = 0; + } + + while ( 1 ) /* loops until end-of-file is reached */ + { + yy_cp = yy_c_buf_p; + + /* support of yytext */ + *yy_cp = yy_hold_char; + + /* yy_bp points to the position in yy_ch_buf of the start of the + * current run. + */ + yy_bp = yy_cp; + + yy_current_state = yy_start; +yy_match: + do + { + register YY_CHAR yy_c = yy_ec[*yy_cp]; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = yy_def[yy_current_state]; + if ( yy_current_state >= 144 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + ++yy_cp; + } + while ( yy_base[yy_current_state] != 194 ); + +yy_find_action: + yy_act = yy_accept[yy_current_state]; + + YY_DO_BEFORE_ACTION; + YY_USER_ACTION; + +do_action: /* this label is used only to access EOF actions */ + + + switch ( yy_act ) + { + case 0: /* must backtrack */ + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + yy_cp = yy_last_accepting_cpos; + yy_current_state = yy_last_accepting_state; + goto yy_find_action; + +case 1: +# line 61 "scan.l" +return(Define); + YY_BREAK +case 2: +# line 62 "scan.l" +return(Break); + YY_BREAK +case 3: +# line 63 "scan.l" +return(Quit); + YY_BREAK +case 4: +# line 64 "scan.l" +return(Length); + YY_BREAK +case 5: +# line 65 "scan.l" +return(Return); + YY_BREAK +case 6: +# line 66 "scan.l" +return(For); + YY_BREAK +case 7: +# line 67 "scan.l" +return(If); + YY_BREAK +case 8: +# line 68 "scan.l" +return(While); + YY_BREAK +case 9: +# line 69 "scan.l" +return(Sqrt); + YY_BREAK +case 10: +# line 70 "scan.l" +return(Scale); + YY_BREAK +case 11: +# line 71 "scan.l" +return(Ibase); + YY_BREAK +case 12: +# line 72 "scan.l" +return(Obase); + YY_BREAK +case 13: +# line 73 "scan.l" +return(Auto); + YY_BREAK +case 14: +# line 74 "scan.l" +return(Else); + YY_BREAK +case 15: +# line 75 "scan.l" +return(Read); + YY_BREAK +case 16: +# line 76 "scan.l" +return(Halt); + YY_BREAK +case 17: +# line 77 "scan.l" +return(Last); + YY_BREAK +case 18: +# line 78 "scan.l" +return(Warranty); + YY_BREAK +case 19: +# line 79 "scan.l" +return(Continue); + YY_BREAK +case 20: +# line 80 "scan.l" +return(Print); + YY_BREAK +case 21: +# line 81 "scan.l" +return(Limits); + YY_BREAK +case 22: +# line 82 "scan.l" +{ yylval.c_value = yytext[0]; + return((int)yytext[0]); } + YY_BREAK +case 23: +# line 84 "scan.l" +{ return(AND); } + YY_BREAK +case 24: +# line 85 "scan.l" +{ return(OR); } + YY_BREAK +case 25: +# line 86 "scan.l" +{ return(NOT); } + YY_BREAK +case 26: +# line 87 "scan.l" +{ yylval.c_value = yytext[0]; return(MUL_OP); } + YY_BREAK +case 27: +# line 88 "scan.l" +{ yylval.c_value = yytext[0]; return(ASSIGN_OP); } + YY_BREAK +case 28: +# line 89 "scan.l" +{ +#ifdef OLD_EQ_OP + char warn_save; + warn_save = warn_not_std; + warn_not_std = TRUE; + warn ("Old fashioned ="); + warn_not_std = warn_save; + yylval.c_value = yytext[1]; +#else + yylval.c_value = '='; + yyless (1); +#endif + return(ASSIGN_OP); + } + YY_BREAK +case 29: +# line 103 "scan.l" +{ yylval.s_value = strcopyof((char *) yytext); + return(REL_OP); } + YY_BREAK +case 30: +# line 105 "scan.l" +{ yylval.c_value = yytext[0]; return(INCR_DECR); } + YY_BREAK +case 31: +# line 106 "scan.l" +{ line_no++; return(NEWLINE); } + YY_BREAK +case 32: +# line 107 "scan.l" +{ line_no++; /* ignore a "quoted" newline */ } + YY_BREAK +case 33: +# line 108 "scan.l" +{ /* ignore spaces and tabs */ } + YY_BREAK +case 34: +# line 109 "scan.l" +{ + int c; + + for (;;) + { + while ( ((c=input()) != '*') && (c != EOF)) + /* eat it */ + if (c == '\n') line_no++; + if (c == '*') + { + while ( (c=input()) == '*') /* eat it*/; + if (c == '/') break; /* at end of comment */ + if (c == '\n') line_no++; + } + if (c == EOF) + { + fprintf (stderr,"EOF encountered in a comment.\n"); + break; + } + } + } + YY_BREAK +case 35: +# line 130 "scan.l" +{ yylval.s_value = strcopyof((char *) yytext); return(NAME); } + YY_BREAK +case 36: +# line 131 "scan.l" +{ + unsigned char *look; + int count = 0; + yylval.s_value = strcopyof((char *) yytext); + for (look = yytext; *look != 0; look++) + { + if (*look == '\n') line_no++; + if (*look == '"') count++; + } + if (count != 2) yyerror ("NUL character in string."); + return(STRING); + } + YY_BREAK +case 37: +# line 143 "scan.l" +{ + unsigned char *src, *dst; + int len; + /* remove a trailing decimal point. */ + len = strlen((char *) yytext); + if (yytext[len-1] == '.') + yytext[len-1] = 0; + /* remove leading zeros. */ + src = yytext; + dst = yytext; + while (*src == '0') src++; + if (*src == 0) src--; + /* Copy strings removing the newlines. */ + while (*src != 0) + { + if (*src == '\\') + { + src++; src++; + line_no++; + } + else + *dst++ = *src++; + } + *dst = 0; + yylval.s_value = strcopyof((char *) yytext); + return(NUMBER); + } + YY_BREAK +case 38: +# line 170 "scan.l" +{ + if (yytext[0] < ' ') + yyerror ("illegal character: ^%c",yytext[0] + '@'); + else + if (yytext[0] > '~') + yyerror ("illegal character: \\%3d", (int) yytext[0]); + else + yyerror ("illegal character: %s",yytext); + } + YY_BREAK +case 39: +# line 179 "scan.l" +ECHO; + YY_BREAK +case YY_STATE_EOF(INITIAL): + yyterminate(); + + case YY_END_OF_BUFFER: + { + /* amount of text matched not including the EOB char */ + int yy_amount_of_matched_text = yy_cp - yytext - 1; + + /* undo the effects of YY_DO_BEFORE_ACTION */ + *yy_cp = yy_hold_char; + + /* note that here we test for yy_c_buf_p "<=" to the position + * of the first EOB in the buffer, since yy_c_buf_p will + * already have been incremented past the NUL character + * (since all states make transitions on EOB to the end- + * of-buffer state). Contrast this with the test in yyinput(). + */ + if ( yy_c_buf_p <= &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + { + yy_state_type yy_next_state; + + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + /* okay, we're now positioned to make the + * NUL transition. We couldn't have + * yy_get_previous_state() go ahead and do it + * for us because it doesn't know how to deal + * with the possibility of jamming (and we + * don't want to build jamming into it because + * then it will run more slowly) + */ + + yy_next_state = yy_try_NUL_trans( yy_current_state ); + + yy_bp = yytext + YY_MORE_ADJ; + + if ( yy_next_state ) + { + /* consume the NUL */ + yy_cp = ++yy_c_buf_p; + yy_current_state = yy_next_state; + goto yy_match; + } + + else + { + goto yy_find_action; + } + } + + else switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + yy_did_buffer_switch_on_eof = 0; + + if ( yywrap() ) + { + /* note: because we've taken care in + * yy_get_next_buffer() to have set up yytext, + * we can now set up yy_c_buf_p so that if some + * total hoser (like flex itself) wants + * to call the scanner after we return the + * YY_NULL, it'll still work - another YY_NULL + * will get returned. + */ + yy_c_buf_p = yytext + YY_MORE_ADJ; + + yy_act = YY_STATE_EOF((yy_start - 1) / 2); + goto do_action; + } + + else + { + if ( ! yy_did_buffer_switch_on_eof ) + YY_NEW_FILE; + } + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + yy_amount_of_matched_text; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_match; + + case EOB_ACT_LAST_MATCH: + yy_c_buf_p = + &yy_current_buffer->yy_ch_buf[yy_n_chars]; + + yy_current_state = yy_get_previous_state(); + + yy_cp = yy_c_buf_p; + yy_bp = yytext + YY_MORE_ADJ; + goto yy_find_action; + } + break; + } + + default: +#ifdef FLEX_DEBUG + printf( "action # %d\n", yy_act ); +#endif + YY_FATAL_ERROR( + "fatal flex scanner internal error--no action found" ); + } + } + } + + +/* yy_get_next_buffer - try to read in a new buffer + * + * synopsis + * int yy_get_next_buffer(); + * + * returns a code representing an action + * EOB_ACT_LAST_MATCH - + * EOB_ACT_CONTINUE_SCAN - continue scanning from current position + * EOB_ACT_END_OF_FILE - end of file + */ + +static int yy_get_next_buffer() + + { + register YY_CHAR *dest = yy_current_buffer->yy_ch_buf; + register YY_CHAR *source = yytext - 1; /* copy prev. char, too */ + register int number_to_move, i; + int ret_val; + + if ( yy_c_buf_p > &yy_current_buffer->yy_ch_buf[yy_n_chars + 1] ) + YY_FATAL_ERROR( + "fatal flex scanner internal error--end of buffer missed" ); + + /* try to read more data */ + + /* first move last chars to start of buffer */ + number_to_move = yy_c_buf_p - yytext; + + for ( i = 0; i < number_to_move; ++i ) + *(dest++) = *(source++); + + if ( yy_current_buffer->yy_eof_status != EOF_NOT_SEEN ) + /* don't do the read, it's not guaranteed to return an EOF, + * just force an EOF + */ + yy_n_chars = 0; + + else + { + int num_to_read = yy_current_buffer->yy_buf_size - number_to_move - 1; + + if ( num_to_read > YY_READ_BUF_SIZE ) + num_to_read = YY_READ_BUF_SIZE; + + else if ( num_to_read <= 0 ) + YY_FATAL_ERROR( "fatal error - scanner input buffer overflow" ); + + /* read in more data */ + YY_INPUT( (&yy_current_buffer->yy_ch_buf[number_to_move]), + yy_n_chars, num_to_read ); + } + + if ( yy_n_chars == 0 ) + { + if ( number_to_move == 1 ) + { + ret_val = EOB_ACT_END_OF_FILE; + yy_current_buffer->yy_eof_status = EOF_DONE; + } + + else + { + ret_val = EOB_ACT_LAST_MATCH; + yy_current_buffer->yy_eof_status = EOF_PENDING; + } + } + + else + ret_val = EOB_ACT_CONTINUE_SCAN; + + yy_n_chars += number_to_move; + yy_current_buffer->yy_ch_buf[yy_n_chars] = YY_END_OF_BUFFER_CHAR; + yy_current_buffer->yy_ch_buf[yy_n_chars + 1] = YY_END_OF_BUFFER_CHAR; + + /* yytext begins at the second character in yy_ch_buf; the first + * character is the one which preceded it before reading in the latest + * buffer; it needs to be kept around in case it's a newline, so + * yy_get_previous_state() will have with '^' rules active + */ + + yytext = &yy_current_buffer->yy_ch_buf[1]; + + return ( ret_val ); + } + + +/* yy_get_previous_state - get the state just before the EOB char was reached + * + * synopsis + * yy_state_type yy_get_previous_state(); + */ + +static yy_state_type yy_get_previous_state() + + { + register yy_state_type yy_current_state; + register YY_CHAR *yy_cp; + + yy_current_state = yy_start; + + for ( yy_cp = yytext + YY_MORE_ADJ; yy_cp < yy_c_buf_p; ++yy_cp ) + { + register YY_CHAR yy_c = (*yy_cp ? yy_ec[*yy_cp] : 1); + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = yy_def[yy_current_state]; + if ( yy_current_state >= 144 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + } + + return ( yy_current_state ); + } + + +/* yy_try_NUL_trans - try to make a transition on the NUL character + * + * synopsis + * next_state = yy_try_NUL_trans( current_state ); + */ + +#ifdef YY_USE_PROTOS +static yy_state_type yy_try_NUL_trans( register yy_state_type yy_current_state ) +#else +static yy_state_type yy_try_NUL_trans( yy_current_state ) +register yy_state_type yy_current_state; +#endif + + { + register int yy_is_jam; + register YY_CHAR *yy_cp = yy_c_buf_p; + + register YY_CHAR yy_c = 1; + if ( yy_accept[yy_current_state] ) + { + yy_last_accepting_state = yy_current_state; + yy_last_accepting_cpos = yy_cp; + } + while ( yy_chk[yy_base[yy_current_state] + yy_c] != yy_current_state ) + { + yy_current_state = yy_def[yy_current_state]; + if ( yy_current_state >= 144 ) + yy_c = yy_meta[yy_c]; + } + yy_current_state = yy_nxt[yy_base[yy_current_state] + yy_c]; + yy_is_jam = (yy_base[yy_current_state] == 194); + + return ( yy_is_jam ? 0 : yy_current_state ); + } + + +#ifdef YY_USE_PROTOS +static void yyunput( YY_CHAR c, register YY_CHAR *yy_bp ) +#else +static void yyunput( c, yy_bp ) +YY_CHAR c; +register YY_CHAR *yy_bp; +#endif + + { + register YY_CHAR *yy_cp = yy_c_buf_p; + + /* undo effects of setting up yytext */ + *yy_cp = yy_hold_char; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + { /* need to shift things up to make room */ + register int number_to_move = yy_n_chars + 2; /* +2 for EOB chars */ + register YY_CHAR *dest = + &yy_current_buffer->yy_ch_buf[yy_current_buffer->yy_buf_size + 2]; + register YY_CHAR *source = + &yy_current_buffer->yy_ch_buf[number_to_move]; + + while ( source > yy_current_buffer->yy_ch_buf ) + *--dest = *--source; + + yy_cp += dest - source; + yy_bp += dest - source; + yy_n_chars = yy_current_buffer->yy_buf_size; + + if ( yy_cp < yy_current_buffer->yy_ch_buf + 2 ) + YY_FATAL_ERROR( "flex scanner push-back overflow" ); + } + + if ( yy_cp > yy_bp && yy_cp[-1] == '\n' ) + yy_cp[-2] = '\n'; + + *--yy_cp = c; + + /* note: the formal parameter *must* be called "yy_bp" for this + * macro to now work correctly + */ + YY_DO_BEFORE_ACTION; /* set up yytext again */ + } + + +#ifdef __cplusplus +static int yyinput() +#else +static int input() +#endif + + { + int c; + YY_CHAR *yy_cp = yy_c_buf_p; + + *yy_cp = yy_hold_char; + + if ( *yy_c_buf_p == YY_END_OF_BUFFER_CHAR ) + { + /* yy_c_buf_p now points to the character we want to return. + * If this occurs *before* the EOB characters, then it's a + * valid NUL; if not, then we've hit the end of the buffer. + */ + if ( yy_c_buf_p < &yy_current_buffer->yy_ch_buf[yy_n_chars] ) + /* this was really a NUL */ + *yy_c_buf_p = '\0'; + + else + { /* need more input */ + yytext = yy_c_buf_p; + ++yy_c_buf_p; + + switch ( yy_get_next_buffer() ) + { + case EOB_ACT_END_OF_FILE: + { + if ( yywrap() ) + { + yy_c_buf_p = yytext + YY_MORE_ADJ; + return ( EOF ); + } + + YY_NEW_FILE; + +#ifdef __cplusplus + return ( yyinput() ); +#else + return ( input() ); +#endif + } + break; + + case EOB_ACT_CONTINUE_SCAN: + yy_c_buf_p = yytext + YY_MORE_ADJ; + break; + + case EOB_ACT_LAST_MATCH: +#ifdef __cplusplus + YY_FATAL_ERROR( "unexpected last match in yyinput()" ); +#else + YY_FATAL_ERROR( "unexpected last match in input()" ); +#endif + } + } + } + + c = *yy_c_buf_p; + yy_hold_char = *++yy_c_buf_p; + + return ( c ); + } + + +#ifdef YY_USE_PROTOS +void yyrestart( FILE *input_file ) +#else +void yyrestart( input_file ) +FILE *input_file; +#endif + + { + yy_init_buffer( yy_current_buffer, input_file ); + yy_load_buffer_state(); + } + + +#ifdef YY_USE_PROTOS +void yy_switch_to_buffer( YY_BUFFER_STATE new_buffer ) +#else +void yy_switch_to_buffer( new_buffer ) +YY_BUFFER_STATE new_buffer; +#endif + + { + if ( yy_current_buffer == new_buffer ) + return; + + if ( yy_current_buffer ) + { + /* flush out information for old buffer */ + *yy_c_buf_p = yy_hold_char; + yy_current_buffer->yy_buf_pos = yy_c_buf_p; + yy_current_buffer->yy_n_chars = yy_n_chars; + } + + yy_current_buffer = new_buffer; + yy_load_buffer_state(); + + /* we don't actually know whether we did this switch during + * EOF (yywrap()) processing, but the only time this flag + * is looked at is after yywrap() is called, so it's safe + * to go ahead and always set it. + */ + yy_did_buffer_switch_on_eof = 1; + } + + +#ifdef YY_USE_PROTOS +void yy_load_buffer_state( void ) +#else +void yy_load_buffer_state() +#endif + + { + yy_n_chars = yy_current_buffer->yy_n_chars; + yytext = yy_c_buf_p = yy_current_buffer->yy_buf_pos; + yyin = yy_current_buffer->yy_input_file; + yy_hold_char = *yy_c_buf_p; + } + + +#ifdef YY_USE_PROTOS +YY_BUFFER_STATE yy_create_buffer( FILE *file, int size ) +#else +YY_BUFFER_STATE yy_create_buffer( file, size ) +FILE *file; +int size; +#endif + + { + YY_BUFFER_STATE b; + + b = (YY_BUFFER_STATE) malloc( sizeof( struct yy_buffer_state ) ); + + if ( ! b ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + b->yy_buf_size = size; + + /* yy_ch_buf has to be 2 characters longer than the size given because + * we need to put in 2 end-of-buffer characters. + */ + b->yy_ch_buf = (YY_CHAR *) malloc( (unsigned) (b->yy_buf_size + 2) ); + + if ( ! b->yy_ch_buf ) + YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" ); + + yy_init_buffer( b, file ); + + return ( b ); + } + + +#ifdef YY_USE_PROTOS +void yy_delete_buffer( YY_BUFFER_STATE b ) +#else +void yy_delete_buffer( b ) +YY_BUFFER_STATE b; +#endif + + { + if ( b == yy_current_buffer ) + yy_current_buffer = (YY_BUFFER_STATE) 0; + + free( (char *) b->yy_ch_buf ); + free( (char *) b ); + } + + +#ifdef YY_USE_PROTOS +void yy_init_buffer( YY_BUFFER_STATE b, FILE *file ) +#else +void yy_init_buffer( b, file ) +YY_BUFFER_STATE b; +FILE *file; +#endif + + { + b->yy_input_file = file; + + /* we put in the '\n' and start reading from [1] so that an + * initial match-at-newline will be true. + */ + + b->yy_ch_buf[0] = '\n'; + b->yy_n_chars = 1; + + /* we always need two end-of-buffer characters. The first causes + * a transition to the end-of-buffer state. The second causes + * a jam in that state. + */ + b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR; + b->yy_ch_buf[2] = YY_END_OF_BUFFER_CHAR; + + b->yy_buf_pos = &b->yy_ch_buf[1]; + + b->yy_eof_status = EOF_NOT_SEEN; + } +# line 179 "scan.l" + + + + +/* This is the way to get multiple files input into lex. */ + +int +yywrap() +{ + if (!open_new_file ()) return (1); /* EOF on standard in. */ + return (0); /* We have more input. */ +} diff --git a/commands/bc/scan.l b/commands/bc/scan.l new file mode 100755 index 000000000..d8066a054 --- /dev/null +++ b/commands/bc/scan.l @@ -0,0 +1,190 @@ +%{ +/* scan.l: the (f)lex description file for the scanner. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "y.tab.h" +#include "global.h" +#include "proto.h" + +/* Using flex, we can ask for a smaller input buffer. With lex, this + does nothing! */ + +#ifdef SMALL_BUF +#undef YY_READ_BUF_SIZE +#define YY_READ_BUF_SIZE 512 +#endif + +/* We want to define our own yywrap. */ +#undef yywrap +_PROTOTYPE(int yywrap, (void)); + +/* MINIX returns from read with < 0 if SIGINT is encountered. + In flex, we can redefine YY_INPUT to the following. In lex, this + does nothing! */ +#include +#undef YY_INPUT +#define YY_INPUT(buf,result,max_size) \ + while ( (result = read( fileno(yyin), (char *) buf, max_size )) < 0 ) \ + if (errno != EINTR) \ + YY_FATAL_ERROR( "read() in flex scanner failed" ); + +%} +DIGIT [0-9A-F] +LETTER [a-z] +%% +define return(Define); +break return(Break); +quit return(Quit); +length return(Length); +return return(Return); +for return(For); +if return(If); +while return(While); +sqrt return(Sqrt); +scale return(Scale); +ibase return(Ibase); +obase return(Obase); +auto return(Auto); +else return(Else); +read return(Read); +halt return(Halt); +last return(Last); +warranty return(Warranty); +continue return(Continue); +print return(Print); +limits return(Limits); +"+"|"-"|";"|"("|")"|"{"|"}"|"["|"]"|","|"^" { yylval.c_value = yytext[0]; + return((int)yytext[0]); } +&& { return(AND); } +\|\| { return(OR); } +"!" { return(NOT); } +"*"|"/"|"%" { yylval.c_value = yytext[0]; return(MUL_OP); } +"="|\+=|-=|\*=|\/=|%=|\^= { yylval.c_value = yytext[0]; return(ASSIGN_OP); } +=\+|=-|=\*|=\/|=%|=\^ { +#ifdef OLD_EQ_OP + char warn_save; + warn_save = warn_not_std; + warn_not_std = TRUE; + warn ("Old fashioned ="); + warn_not_std = warn_save; + yylval.c_value = yytext[1]; +#else + yylval.c_value = '='; + yyless (1); +#endif + return(ASSIGN_OP); + } +==|\<=|\>=|\!=|"<"|">" { yylval.s_value = strcopyof((char *) yytext); + return(REL_OP); } +\+\+|-- { yylval.c_value = yytext[0]; return(INCR_DECR); } +"\n" { line_no++; return(NEWLINE); } +\\\n { line_no++; /* ignore a "quoted" newline */ } +[ \t]+ { /* ignore spaces and tabs */ } +"/*" { + int c; + + for (;;) + { + while ( ((c=input()) != '*') && (c != EOF)) + /* eat it */ + if (c == '\n') line_no++; + if (c == '*') + { + while ( (c=input()) == '*') /* eat it*/; + if (c == '/') break; /* at end of comment */ + if (c == '\n') line_no++; + } + if (c == EOF) + { + fprintf (stderr,"EOF encountered in a comment.\n"); + break; + } + } + } +[a-z][a-z0-9_]* { yylval.s_value = strcopyof((char *) yytext); return(NAME); } +\"[^\"]*\" { + unsigned char *look; + int count = 0; + yylval.s_value = strcopyof((char *) yytext); + for (look = yytext; *look != 0; look++) + { + if (*look == '\n') line_no++; + if (*look == '"') count++; + } + if (count != 2) yyerror ("NUL character in string."); + return(STRING); + } +{DIGIT}({DIGIT}|\\\n)*("."({DIGIT}|\\\n)*)?|"."(\\\n)*{DIGIT}({DIGIT}|\\\n)* { + unsigned char *src, *dst; + int len; + /* remove a trailing decimal point. */ + len = strlen((char *) yytext); + if (yytext[len-1] == '.') + yytext[len-1] = 0; + /* remove leading zeros. */ + src = yytext; + dst = yytext; + while (*src == '0') src++; + if (*src == 0) src--; + /* Copy strings removing the newlines. */ + while (*src != 0) + { + if (*src == '\\') + { + src++; src++; + line_no++; + } + else + *dst++ = *src++; + } + *dst = 0; + yylval.s_value = strcopyof((char *) yytext); + return(NUMBER); + } +. { + if (yytext[0] < ' ') + yyerror ("illegal character: ^%c",yytext[0] + '@'); + else + if (yytext[0] > '~') + yyerror ("illegal character: \\%3d", (int) yytext[0]); + else + yyerror ("illegal character: %s",yytext); + } +%% + + + +/* This is the way to get multiple files input into lex. */ + +int +yywrap() +{ + if (!open_new_file ()) return (1); /* EOF on standard in. */ + return (0); /* We have more input. */ +} diff --git a/commands/bc/storage.c b/commands/bc/storage.c new file mode 100755 index 000000000..1edd6e22f --- /dev/null +++ b/commands/bc/storage.c @@ -0,0 +1,967 @@ +/* storage.c: Code and data storage manipulations. This includes labels. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + +#include "bcdefs.h" +#include "global.h" +#include "proto.h" + + +/* Initialize the storage at the beginning of the run. */ + +void +init_storage () +{ + + /* Functions: we start with none and ask for more. */ + f_count = 0; + more_functions (); + f_names[0] = "(main)"; + + /* Variables. */ + v_count = 0; + more_variables (); + + /* Arrays. */ + a_count = 0; + more_arrays (); + + /* Other things... */ + ex_stack = NULL; + fn_stack = NULL; + i_base = 10; + o_base = 10; + scale = 0; + c_code = FALSE; + init_numbers(); +} + +/* Three functions for increasing the number of functions, variables, or + arrays that are needed. This adds another 32 of the requested object. */ + +void +more_functions (VOID) +{ + int old_count; + int indx1, indx2; + bc_function *old_f; + bc_function *f; + char **old_names; + + /* Save old information. */ + old_count = f_count; + old_f = functions; + old_names = f_names; + + /* Add a fixed amount and allocate new space. */ + f_count += STORE_INCR; + functions = (bc_function *) bc_malloc (f_count*sizeof (bc_function)); + f_names = (char **) bc_malloc (f_count*sizeof (char *)); + + /* Copy old ones. */ + for (indx1 = 0; indx1 < old_count; indx1++) + { + functions[indx1] = old_f[indx1]; + f_names[indx1] = old_names[indx1]; + } + + /* Initialize the new ones. */ + for (; indx1 < f_count; indx1++) + { + f = &functions[indx1]; + f->f_defined = FALSE; + for (indx2 = 0; indx2 < BC_MAX_SEGS; indx2++) + f->f_body [indx2] = NULL; + f->f_code_size = 0; + f->f_label = NULL; + f->f_autos = NULL; + f->f_params = NULL; + } + + /* Free the old elements. */ + if (old_count != 0) + { + free (old_f); + free (old_names); + } +} + +void +more_variables () +{ + int indx; + int old_count; + bc_var **old_var; + char **old_names; + + /* Save the old values. */ + old_count = v_count; + old_var = variables; + old_names = v_names; + + /* Increment by a fixed amount and allocate. */ + v_count += STORE_INCR; + variables = (bc_var **) bc_malloc (v_count*sizeof(bc_var *)); + v_names = (char **) bc_malloc (v_count*sizeof(char *)); + + /* Copy the old variables. */ + for (indx = 3; indx < old_count; indx++) + variables[indx] = old_var[indx]; + + /* Initialize the new elements. */ + for (; indx < v_count; indx++) + variables[indx] = NULL; + + /* Free the old elements. */ + if (old_count != 0) + { + free (old_var); + free (old_names); + } +} + +void +more_arrays () +{ + int indx; + int old_count; + bc_var_array **old_ary; + char **old_names; + + /* Save the old values. */ + old_count = a_count; + old_ary = arrays; + old_names = a_names; + + /* Increment by a fixed amount and allocate. */ + a_count += STORE_INCR; + arrays = (bc_var_array **) bc_malloc (a_count*sizeof(bc_var_array *)); + a_names = (char **) bc_malloc (a_count*sizeof(char *)); + + /* Copy the old arrays. */ + for (indx = 1; indx < old_count; indx++) + arrays[indx] = old_ary[indx]; + + + /* Initialize the new elements. */ + for (; indx < v_count; indx++) + arrays[indx] = NULL; + + /* Free the old elements. */ + if (old_count != 0) + { + free (old_ary); + free (old_names); + } +} + + +/* clear_func clears out function FUNC and makes it ready to redefine. */ + +void +clear_func (func) + char func; +{ + bc_function *f; + int indx; + bc_label_group *lg; + + /* Set the pointer to the function. */ + f = &functions[func]; + f->f_defined = FALSE; + + /* Clear the code segments. */ + for (indx = 0; indx < BC_MAX_SEGS; indx++) + { + if (f->f_body[indx] != NULL) + { + free (f->f_body[indx]); + f->f_body[indx] = NULL; + } + } + + f->f_code_size = 0; + if (f->f_autos != NULL) + { + free_args (f->f_autos); + f->f_autos = NULL; + } + if (f->f_params != NULL) + { + free_args (f->f_params); + f->f_params = NULL; + } + while (f->f_label != NULL) + { + lg = f->f_label->l_next; + free (f->f_label); + f->f_label = lg; + } +} + + +/* Pop the function execution stack and return the top. */ + +int +fpop() +{ + fstack_rec *temp; + int retval; + + if (fn_stack != NULL) + { + temp = fn_stack; + fn_stack = temp->s_next; + retval = temp->s_val; + free (temp); + } + return (retval); +} + + +/* Push VAL on to the function stack. */ + +void +fpush (val) + int val; +{ + fstack_rec *temp; + + temp = (fstack_rec *) bc_malloc (sizeof (fstack_rec)); + temp->s_next = fn_stack; + temp->s_val = val; + fn_stack = temp; +} + + +/* Pop and discard the top element of the regular execution stack. */ + +void +pop () +{ + estack_rec *temp; + + if (ex_stack != NULL) + { + temp = ex_stack; + ex_stack = temp->s_next; + free_num (&temp->s_num); + free (temp); + } +} + + +/* Push a copy of NUM on to the regular execution stack. */ + +void +push_copy (num) + bc_num num; +{ + estack_rec *temp; + + temp = (estack_rec *) bc_malloc (sizeof (estack_rec)); + temp->s_num = copy_num (num); + temp->s_next = ex_stack; + ex_stack = temp; +} + + +/* Push NUM on to the regular execution stack. Do NOT push a copy. */ + +void +push_num (num) + bc_num num; +{ + estack_rec *temp; + + temp = (estack_rec *) bc_malloc (sizeof (estack_rec)); + temp->s_num = num; + temp->s_next = ex_stack; + ex_stack = temp; +} + + +/* Make sure the ex_stack has at least DEPTH elements on it. + Return TRUE if it has at least DEPTH elements, otherwise + return FALSE. */ + +char +check_stack (depth) + int depth; +{ + estack_rec *temp; + + temp = ex_stack; + while ((temp != NULL) && (depth > 0)) + { + temp = temp->s_next; + depth--; + } + if (depth > 0) + { + rt_error ("Stack error."); + return FALSE; + } + return TRUE; +} + + +/* The following routines manipulate simple variables and + array variables. */ + +/* get_var returns a pointer to the variable VAR_NAME. If one does not + exist, one is created. */ + +bc_var * +get_var (var_name) + int var_name; +{ + bc_var *var_ptr; + + var_ptr = variables[var_name]; + if (var_ptr == NULL) + { + var_ptr = variables[var_name] = (bc_var *) bc_malloc (sizeof (bc_var)); + init_num (&var_ptr->v_value); + } + return var_ptr; +} + + +/* get_array_num returns the address of the bc_num in the array + structure. If more structure is requried to get to the index, + this routine does the work to create that structure. VAR_INDEX + is a zero based index into the arrays storage array. INDEX is + the index into the bc array. */ + +bc_num * +get_array_num (var_index, index) + int var_index; + long index; +{ + bc_var_array *ary_ptr; + bc_array *a_var; + bc_array_node *temp; + int log, ix, ix1; + int sub [NODE_DEPTH]; + + /* Get the array entry. */ + ary_ptr = arrays[var_index]; + if (ary_ptr == NULL) + { + ary_ptr = arrays[var_index] = + (bc_var_array *) bc_malloc (sizeof (bc_var_array)); + ary_ptr->a_value = NULL; + ary_ptr->a_next = NULL; + ary_ptr->a_param = FALSE; + } + + a_var = ary_ptr->a_value; + if (a_var == NULL) { + a_var = ary_ptr->a_value = (bc_array *) bc_malloc (sizeof (bc_array)); + a_var->a_tree = NULL; + a_var->a_depth = 0; + } + + /* Get the index variable. */ + sub[0] = index & NODE_MASK; + ix = index >> NODE_SHIFT; + log = 1; + while (ix > 0 || log < a_var->a_depth) + { + sub[log] = ix & NODE_MASK; + ix >>= NODE_SHIFT; + log++; + } + + /* Build any tree that is necessary. */ + while (log > a_var->a_depth) + { + temp = (bc_array_node *) bc_malloc (sizeof(bc_array_node)); + if (a_var->a_depth != 0) + { + temp->n_items.n_down[0] = a_var->a_tree; + for (ix=1; ix < NODE_SIZE; ix++) + temp->n_items.n_down[ix] = NULL; + } + else + { + for (ix=0; ix < NODE_SIZE; ix++) + temp->n_items.n_num[ix] = copy_num(_zero_); + } + a_var->a_tree = temp; + a_var->a_depth++; + } + + /* Find the indexed variable. */ + temp = a_var->a_tree; + while ( log-- > 1) + { + ix1 = sub[log]; + if (temp->n_items.n_down[ix1] == NULL) + { + temp->n_items.n_down[ix1] = + (bc_array_node *) bc_malloc (sizeof(bc_array_node)); + temp = temp->n_items.n_down[ix1]; + if (log > 1) + for (ix=0; ix < NODE_SIZE; ix++) + temp->n_items.n_down[ix] = NULL; + else + for (ix=0; ix < NODE_SIZE; ix++) + temp->n_items.n_num[ix] = copy_num(_zero_); + } + else + temp = temp->n_items.n_down[ix1]; + } + + /* Return the address of the indexed variable. */ + return &(temp->n_items.n_num[sub[0]]); +} + + +/* Store the top of the execution stack into VAR_NAME. + This includes the special variables ibase, obase, and scale. */ + +void +store_var (var_name) + int var_name; +{ + bc_var *var_ptr; + long temp; + char toobig; + + if (var_name > 2) + { + /* It is a simple variable. */ + var_ptr = get_var (var_name); + if (var_ptr != NULL) + { + free_num(&var_ptr->v_value); + var_ptr->v_value = copy_num (ex_stack->s_num); + } + } + else + { + /* It is a special variable... */ + toobig = FALSE; + if (is_neg (ex_stack->s_num)) + { + switch (var_name) + { + case 0: + rt_warn ("negative ibase, set to 2"); + temp = 2; + break; + case 1: + rt_warn ("negative obase, set to 2"); + temp = 2; + break; + case 2: + rt_warn ("negative scale, set to 0"); + temp = 0; + break; + } + } + else + { + temp = num2long (ex_stack->s_num); + if (!is_zero (ex_stack->s_num) && temp == 0) + toobig = TRUE; + } + switch (var_name) + { + case 0: + if (temp < 2 && !toobig) + { + i_base = 2; + rt_warn ("ibase too small, set to 2"); + } + else + if (temp > 16 || toobig) + { + i_base = 16; + rt_warn ("ibase too large, set to 16"); + } + else + i_base = (int) temp; + break; + + case 1: + if (temp < 2 && !toobig) + { + o_base = 2; + rt_warn ("obase too small, set to 2"); + } + else + if (temp > BC_BASE_MAX || toobig) + { + o_base = BC_BASE_MAX; + rt_warn ("obase too large, set to %d", BC_BASE_MAX); + } + else + o_base = (int) temp; + break; + + case 2: + /* WARNING: The following if statement may generate a compiler + warning if INT_MAX == LONG_MAX. This is NOT a problem. */ + if (temp > BC_SCALE_MAX || toobig ) + { + scale = BC_SCALE_MAX; + rt_warn ("scale too large, set to %d", BC_SCALE_MAX); + } + else + scale = (int) temp; + } + } +} + + +/* Store the top of the execution stack into array VAR_NAME. + VAR_NAME is the name of an array, and the next to the top + of stack for the index into the array. */ + +void +store_array (var_name) + int var_name; +{ + bc_num *num_ptr; + long index; + + if (!check_stack(2)) return; + index = num2long (ex_stack->s_next->s_num); + if (index < 0 || index > BC_DIM_MAX || + (index == 0 && !is_zero(ex_stack->s_next->s_num))) + rt_error ("Array %s subscript out of bounds.", a_names[var_name]); + else + { + num_ptr = get_array_num (var_name, index); + if (num_ptr != NULL) + { + free_num (num_ptr); + *num_ptr = copy_num (ex_stack->s_num); + free_num (&ex_stack->s_next->s_num); + ex_stack->s_next->s_num = ex_stack->s_num; + init_num (&ex_stack->s_num); + pop(); + } + } +} + + +/* Load a copy of VAR_NAME on to the execution stack. This includes + the special variables ibase, obase and scale. */ + +void +load_var (var_name) + int var_name; +{ + bc_var *var_ptr; + + switch (var_name) + { + + case 0: + /* Special variable ibase. */ + push_copy (_zero_); + int2num (&ex_stack->s_num, i_base); + break; + + case 1: + /* Special variable obase. */ + push_copy (_zero_); + int2num (&ex_stack->s_num, o_base); + break; + + case 2: + /* Special variable scale. */ + push_copy (_zero_); + int2num (&ex_stack->s_num, scale); + break; + + default: + /* It is a simple variable. */ + var_ptr = variables[var_name]; + if (var_ptr != NULL) + push_copy (var_ptr->v_value); + else + push_copy (_zero_); + } +} + + +/* Load a copy of VAR_NAME on to the execution stack. This includes + the special variables ibase, obase and scale. */ + +void +load_array (var_name) + int var_name; +{ + bc_num *num_ptr; + long index; + + if (!check_stack(1)) return; + index = num2long (ex_stack->s_num); + if (index < 0 || index > BC_DIM_MAX || + (index == 0 && !is_zero(ex_stack->s_num))) + rt_error ("Array %s subscript out of bounds.", a_names[var_name]); + else + { + num_ptr = get_array_num (var_name, index); + if (num_ptr != NULL) + { + pop(); + push_copy (*num_ptr); + } + } +} + + +/* Decrement VAR_NAME by one. This includes the special variables + ibase, obase, and scale. */ + +void +decr_var (var_name) + int var_name; +{ + bc_var *var_ptr; + + switch (var_name) + { + + case 0: /* ibase */ + if (i_base > 2) + i_base--; + else + rt_warn ("ibase too small in --"); + break; + + case 1: /* obase */ + if (o_base > 2) + o_base--; + else + rt_warn ("obase too small in --"); + break; + + case 2: /* scale */ + if (scale > 0) + scale--; + else + rt_warn ("scale can not be negative in -- "); + break; + + default: /* It is a simple variable. */ + var_ptr = get_var (var_name); + if (var_ptr != NULL) + bc_sub (var_ptr->v_value,_one_,&var_ptr->v_value); + } +} + + +/* Decrement VAR_NAME by one. VAR_NAME is an array, and the top of + the execution stack is the index and it is popped off the stack. */ + +void +decr_array (var_name) + char var_name; +{ + bc_num *num_ptr; + long index; + + /* It is an array variable. */ + if (!check_stack (1)) return; + index = num2long (ex_stack->s_num); + if (index < 0 || index > BC_DIM_MAX || + (index == 0 && !is_zero (ex_stack->s_num))) + rt_error ("Array %s subscript out of bounds.", a_names[var_name]); + else + { + num_ptr = get_array_num (var_name, index); + if (num_ptr != NULL) + { + pop (); + bc_sub (*num_ptr, _one_, num_ptr); + } + } +} + + +/* Increment VAR_NAME by one. This includes the special variables + ibase, obase, and scale. */ + +void +incr_var (var_name) + int var_name; +{ + bc_var *var_ptr; + + switch (var_name) + { + + case 0: /* ibase */ + if (i_base < 16) + i_base++; + else + rt_warn ("ibase too big in ++"); + break; + + case 1: /* obase */ + if (o_base < BC_BASE_MAX) + o_base++; + else + rt_warn ("obase too big in ++"); + break; + + case 2: + if (scale < BC_SCALE_MAX) + scale++; + else + rt_warn ("Scale too big in ++"); + break; + + default: /* It is a simple variable. */ + var_ptr = get_var (var_name); + if (var_ptr != NULL) + bc_add (var_ptr->v_value, _one_, &var_ptr->v_value); + + } +} + + +/* Increment VAR_NAME by one. VAR_NAME is an array and top of + execution stack is the index and is popped off the stack. */ + +void +incr_array (var_name) + int var_name; +{ + bc_num *num_ptr; + long index; + + if (!check_stack (1)) return; + index = num2long (ex_stack->s_num); + if (index < 0 || index > BC_DIM_MAX || + (index == 0 && !is_zero (ex_stack->s_num))) + rt_error ("Array %s subscript out of bounds.", a_names[var_name]); + else + { + num_ptr = get_array_num (var_name, index); + if (num_ptr != NULL) + { + pop (); + bc_add (*num_ptr, _one_, num_ptr); + } + } +} + + +/* Routines for processing autos variables and parameters. */ + +/* NAME is an auto variable that needs to be pushed on its stack. */ + +void +auto_var (name) + int name; +{ + bc_var *v_temp; + bc_var_array *a_temp; + int ix; + + if (name > 0) + { + /* A simple variable. */ + ix = name; + v_temp = (bc_var *) bc_malloc (sizeof (bc_var)); + v_temp->v_next = variables[ix]; + init_num (&v_temp->v_value); + variables[ix] = v_temp; + } + else + { + /* An array variable. */ + ix = -name; + a_temp = (bc_var_array *) bc_malloc (sizeof (bc_var_array)); + a_temp->a_next = arrays[ix]; + a_temp->a_value = NULL; + a_temp->a_param = FALSE; + arrays[ix] = a_temp; + } +} + + +/* Free_a_tree frees everything associated with an array variable tree. + This is used when popping an array variable off its auto stack. */ + +void +free_a_tree ( root, depth ) + bc_array_node *root; + int depth; +{ + int ix; + + if (root != NULL) + { + if (depth > 1) + for (ix = 0; ix < NODE_SIZE; ix++) + free_a_tree (root->n_items.n_down[ix], depth-1); + else + for (ix = 0; ix < NODE_SIZE; ix++) + free_num ( &(root->n_items.n_num[ix])); + free (root); + } +} + + +/* LIST is an NULL terminated list of varible names that need to be + popped off their auto stacks. */ + +void +pop_vars (list) + arg_list *list; +{ + bc_var *v_temp; + bc_var_array *a_temp; + int ix; + + while (list != NULL) + { + ix = list->av_name; + if (ix > 0) + { + /* A simple variable. */ + v_temp = variables[ix]; + if (v_temp != NULL) + { + variables[ix] = v_temp->v_next; + free_num (&v_temp->v_value); + free (v_temp); + } + } + else + { + /* An array variable. */ + ix = -ix; + a_temp = arrays[ix]; + if (a_temp != NULL) + { + arrays[ix] = a_temp->a_next; + if (!a_temp->a_param && a_temp->a_value != NULL) + { + free_a_tree (a_temp->a_value->a_tree, + a_temp->a_value->a_depth); + free (a_temp->a_value); + } + free (a_temp); + } + } + list = list->next; + } +} + + +/* A call is being made to FUNC. The call types are at PC. Process + the parameters by doing an auto on the parameter variable and then + store the value at the new variable or put a pointer the the array + variable. */ + +void +process_params (pc, func) + program_counter *pc; + int func; +{ + char ch; + arg_list *params; + char warned = FALSE; + int ix, ix1; + bc_var *v_temp; + bc_var_array *a_src, *a_dest; + bc_num *n_temp; + + /* Get the parameter names from the function. */ + params = functions[func].f_params; + + while ((ch = byte(pc)) != ':') + { + if (params != NULL) + { + if ((ch == '0') && params->av_name > 0) + { + /* A simple variable. */ + ix = params->av_name; + v_temp = (bc_var *) bc_malloc (sizeof(bc_var)); + v_temp->v_next = variables[ix]; + v_temp->v_value = ex_stack->s_num; + init_num (&ex_stack->s_num); + variables[ix] = v_temp; + } + else + if ((ch == '1') && (params->av_name < 0)) + { + /* The variables is an array variable. */ + + /* Compute source index and make sure some structure exists. */ + ix = (int) num2long (ex_stack->s_num); + n_temp = get_array_num (ix, 0); + + /* Push a new array and Compute Destination index */ + auto_var (params->av_name); + ix1 = -params->av_name; + + /* Set up the correct pointers in the structure. */ + if (ix == ix1) + a_src = arrays[ix]->a_next; + else + a_src = arrays[ix]; + a_dest = arrays[ix1]; + a_dest->a_param = TRUE; + a_dest->a_value = a_src->a_value; + } + else + { + if (params->av_name < 0) + rt_error ("Parameter type mismatch parameter %s.", + a_names[-params->av_name]); + else + rt_error ("Parameter type mismatch, parameter %s.", + v_names[params->av_name]); + params++; + } + pop (); + } + else + { + if (!warned) + { + rt_error ("Parameter number mismatch"); + warned = TRUE; + } + } + params = params->next; + } + if (params != NULL) + rt_error ("Parameter number mismatch"); +} diff --git a/commands/bc/util.c b/commands/bc/util.c new file mode 100755 index 000000000..c0adf914d --- /dev/null +++ b/commands/bc/util.c @@ -0,0 +1,796 @@ +/* util.c: Utility routines for bc. */ + +/* This file is part of bc written for MINIX. + Copyright (C) 1991, 1992 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License , or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; see the file COPYING. If not, write to + the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. + + You may contact the author by: + e-mail: phil@cs.wwu.edu + us-mail: Philip A. Nelson + Computer Science Department, 9062 + Western Washington University + Bellingham, WA 98226-9062 + +*************************************************************************/ + + +#include "bcdefs.h" +#ifndef VARARGS +#include +#else +#include +#endif +#include "global.h" +#include "proto.h" + + +/* strcopyof mallocs new memory and copies a string to to the new + memory. */ + +char * +strcopyof (str) + char *str; +{ + char *temp; + + temp = (char *) bc_malloc (strlen (str)+1); + return (strcpy (temp,str)); +} + + +/* nextarg adds another value to the list of arguments. */ + +arg_list * +nextarg (args, val) + arg_list *args; + char val; +{ arg_list *temp; + + temp = (arg_list *) bc_malloc (sizeof (arg_list)); + temp->av_name = val; + temp->next = args; + + return (temp); +} + + +/* For generate, we must produce a string in the form + "val,val,...,val". We also need a couple of static variables + for retaining old generated strings. It also uses a recursive + function that builds the string. */ + +static char *arglist1 = NULL, *arglist2 = NULL; + + +/* make_arg_str does the actual construction of the argument string. + ARGS is the pointer to the list and LEN is the maximum number of + characters needed. 1 char is the minimum needed. COMMAS tells + if each number should be seperated by commas.*/ + +_PROTOTYPE (static char *make_arg_str, (arg_list *args, int len, int commas)); + +static char * +make_arg_str (args, len, commas) + arg_list *args; + int len; + int commas; +{ + char *temp; + char sval[20]; + + /* Recursive call. */ + if (args != NULL) + temp = make_arg_str (args->next, len+11, commas); + else + { + temp = (char *) bc_malloc (len); + *temp = 0; + return temp; + } + + /* Add the current number to the end of the string. */ + if (len != 1 && commas) + sprintf (sval, "%d,", args->av_name); + else + sprintf (sval, "%d", args->av_name); + temp = strcat (temp, sval); + return (temp); +} + +char * +arg_str (args, commas) + arg_list *args; + int commas; +{ + if (arglist2 != NULL) + free (arglist2); + arglist2 = arglist1; + arglist1 = make_arg_str (args, 1, commas); + return (arglist1); +} + + +/* free_args frees an argument list ARGS. */ + +void +free_args (args) + arg_list *args; +{ + arg_list *temp; + + temp = args; + while (temp != NULL) + { + args = args->next; + free (temp); + temp = args; + } +} + + +/* Check for valid parameter (PARAMS) and auto (AUTOS) lists. + There must be no duplicates any where. Also, this is where + warnings are generated for array parameters. */ + +void +check_params ( params, autos ) + arg_list *params, *autos; +{ + arg_list *tmp1, *tmp2; + + /* Check for duplicate parameters. */ + if (params != NULL) + { + tmp1 = params; + while (tmp1 != NULL) + { + tmp2 = tmp1->next; + while (tmp2 != NULL) + { + if (tmp2->av_name == tmp1->av_name) + yyerror ("duplicate parameter names"); + tmp2 = tmp2->next; + } + if (tmp1->av_name < 0) + warn ("Array parameter"); + tmp1 = tmp1->next; + } + } + + /* Check for duplicate autos. */ + if (autos != NULL) + { + tmp1 = autos; + while (tmp1 != NULL) + { + tmp2 = tmp1->next; + while (tmp2 != NULL) + { + if (tmp2->av_name == tmp1->av_name) + yyerror ("duplicate auto variable names"); + tmp2 = tmp2->next; + } + tmp1 = tmp1->next; + } + } + + /* Check for duplicate between parameters and autos. */ + if ((params != NULL) && (autos != NULL)) + { + tmp1 = params; + while (tmp1 != NULL) + { + tmp2 = autos; + while (tmp2 != NULL) + { + if (tmp2->av_name == tmp1->av_name) + yyerror ("variable in both parameter and auto lists"); + tmp2 = tmp2->next; + } + tmp1 = tmp1->next; + } + } +} + + +/* Initialize the code generator the parser. */ + +void +init_gen () +{ + /* Get things ready. */ + break_label = 0; + continue_label = 0; + next_label = 1; + out_count = 2; + if (compile_only) + printf ("@i"); + else + init_load (); + had_error = FALSE; + did_gen = FALSE; +} + + +/* generate code STR for the machine. */ + +void +generate (str) + char *str; +{ + did_gen = TRUE; + if (compile_only) + { + printf ("%s",str); + out_count += strlen(str); + if (out_count > 60) + { + printf ("\n"); + out_count = 0; + } + } + else + load_code (str); +} + + +/* Execute the current code as loaded. */ + +void +run_code() +{ + /* If no compile errors run the current code. */ + if (!had_error && did_gen) + { + if (compile_only) + { + printf ("@r\n"); + out_count = 0; + } + else + execute (); + } + + /* Reinitialize the code generation and machine. */ + if (did_gen) + init_gen(); + else + had_error = FALSE; +} + + +/* Output routines: Write a character CH to the standard output. + It keeps track of the number of characters output and may + break the output with a "\". */ + +void +out_char (ch) + char ch; +{ + if (ch == '\n') + { + out_col = 0; + putchar ('\n'); + } + else + { + out_col++; + if (out_col == 70) + { + putchar ('\\'); + putchar ('\n'); + out_col = 1; + } + putchar (ch); + } +} + + +/* The following are "Symbol Table" routines for the parser. */ + +/* find_id returns a pointer to node in TREE that has the correct + ID. If there is no node in TREE with ID, NULL is returned. */ + +id_rec * +find_id (tree, id) + id_rec *tree; + char *id; +{ + int cmp_result; + + /* Check for an empty tree. */ + if (tree == NULL) + return NULL; + + /* Recursively search the tree. */ + cmp_result = strcmp (id, tree->id); + if (cmp_result == 0) + return tree; /* This is the item. */ + else if (cmp_result < 0) + return find_id (tree->left, id); + else + return find_id (tree->right, id); +} + + +/* insert_id_rec inserts a NEW_ID rec into the tree whose ROOT is + provided. insert_id_rec returns TRUE if the tree height from + ROOT down is increased otherwise it returns FALSE. This is a + recursive balanced binary tree insertion algorithm. */ + +int insert_id_rec (root, new_id) + id_rec **root; + id_rec *new_id; +{ + id_rec *A, *B; + + /* If root is NULL, this where it is to be inserted. */ + if (*root == NULL) + { + *root = new_id; + new_id->left = NULL; + new_id->right = NULL; + new_id->balance = 0; + return (TRUE); + } + + /* We need to search for a leaf. */ + if (strcmp (new_id->id, (*root)->id) < 0) + { + /* Insert it on the left. */ + if (insert_id_rec (&((*root)->left), new_id)) + { + /* The height increased. */ + (*root)->balance --; + + switch ((*root)->balance) + { + case 0: /* no height increase. */ + return (FALSE); + case -1: /* height increase. */ + return (FALSE); + case -2: /* we need to do a rebalancing act. */ + A = *root; + B = (*root)->left; + if (B->balance <= 0) + { + /* Single Rotate. */ + A->left = B->right; + B->right = A; + *root = B; + A->balance = 0; + B->balance = 0; + } + else + { + /* Double Rotate. */ + *root = B->right; + B->right = (*root)->left; + A->left = (*root)->right; + (*root)->left = B; + (*root)->right = A; + switch ((*root)->balance) + { + case -1: + A->balance = 1; + B->balance = 0; + break; + case 0: + A->balance = 0; + B->balance = 0; + break; + case 1: + A->balance = 0; + B->balance = -1; + break; + } + (*root)->balance = 0; + } + } + } + } + else + { + /* Insert it on the right. */ + if (insert_id_rec (&((*root)->right), new_id)) + { + /* The height increased. */ + (*root)->balance ++; + switch ((*root)->balance) + { + case 0: /* no height increase. */ + return (FALSE); + case 1: /* height increase. */ + return (FALSE); + case 2: /* we need to do a rebalancing act. */ + A = *root; + B = (*root)->right; + if (B->balance >= 0) + { + /* Single Rotate. */ + A->right = B->left; + B->left = A; + *root = B; + A->balance = 0; + B->balance = 0; + } + else + { + /* Double Rotate. */ + *root = B->left; + B->left = (*root)->right; + A->right = (*root)->left; + (*root)->left = A; + (*root)->right = B; + switch ((*root)->balance) + { + case -1: + A->balance = 0; + B->balance = 1; + break; + case 0: + A->balance = 0; + B->balance = 0; + break; + case 1: + A->balance = -1; + B->balance = 0; + break; + } + (*root)->balance = 0; + } + } + } + } + + /* If we fall through to here, the tree did not grow in height. */ + return (FALSE); +} + + +/* Initialize variables for the symbol table tree. */ + +void +init_tree() +{ + name_tree = NULL; + next_array = 1; + next_func = 1; + next_var = 4; /* 0 => ibase, 1 => obase, 2 => scale, 3 => last. */ +} + + +/* Lookup routines for symbol table names. */ + +int +lookup (name, namekind) + char *name; + int namekind; +{ + id_rec *id; + + /* Warn about non-standard name. */ + if (strlen(name) != 1) + warn ("multiple letter name - %s", name); + + /* Look for the id. */ + id = find_id (name_tree, name); + if (id == NULL) + { + /* We need to make a new item. */ + id = (id_rec *) bc_malloc (sizeof (id_rec)); + id->id = strcopyof (name); + id->a_name = 0; + id->f_name = 0; + id->v_name = 0; + insert_id_rec (&name_tree, id); + } + + /* Return the correct value. */ + switch (namekind) + { + + case ARRAY: + /* ARRAY variable numbers are returned as negative numbers. */ + if (id->a_name != 0) + { + free (name); + return (-id->a_name); + } + id->a_name = next_array++; + a_names[id->a_name] = name; + if (id->a_name < MAX_STORE) + { + if (id->a_name >= a_count) + more_arrays (); + return (-id->a_name); + } + yyerror ("Too many array variables"); + exit (1); + + case FUNCT: + if (id->f_name != 0) + { + free(name); + return (id->f_name); + } + id->f_name = next_func++; + f_names[id->f_name] = name; + if (id->f_name < MAX_STORE) + { + if (id->f_name >= f_count) + more_functions (); + return (id->f_name); + } + yyerror ("Too many functions"); + exit (1); + + case SIMPLE: + if (id->v_name != 0) + { + free(name); + return (id->v_name); + } + id->v_name = next_var++; + v_names[id->v_name - 1] = name; + if (id->v_name <= MAX_STORE) + { + if (id->v_name >= v_count) + more_variables (); + return (id->v_name); + } + yyerror ("Too many variables"); + exit (1); + } +} + + +/* Print the welcome banner. */ + +void +welcome() +{ +#if !__minix + printf ("This is free software with ABSOLUTELY NO WARRANTY.\n"); + printf ("For details type `warranty'. \n"); +#endif +} + + +/* Print out the warranty information. */ + +void +warranty(prefix) + char *prefix; +{ + printf ("\n%s%s\n\n", prefix, BC_VERSION); + printf ("%s%s%s%s%s%s%s%s%s%s%s", +" This program is free software; you can redistribute it and/or modify\n", +" it under the terms of the GNU General Public License as published by\n", +" the Free Software Foundation; either version 2 of the License , or\n", +" (at your option) any later version.\n\n", +" This program is distributed in the hope that it will be useful,\n", +" but WITHOUT ANY WARRANTY; without even the implied warranty of\n", +" MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n", +" GNU General Public License for more details.\n\n", +" You should have received a copy of the GNU General Public License\n", +" along with this program. If not, write to the Free Software\n", +" Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.\n\n"); +} + +/* Print out the limits of this program. */ + +void +limits() +{ + printf ("BC_BASE_MAX = %d\n", BC_BASE_MAX); + printf ("BC_DIM_MAX = %ld\n", (long) BC_DIM_MAX); + printf ("BC_SCALE_MAX = %d\n", BC_SCALE_MAX); + printf ("BC_STRING_MAX = %d\n", BC_STRING_MAX); + printf ("MAX Exponent = %ld\n", (long) LONG_MAX); + printf ("MAX code = %ld\n", (long) BC_MAX_SEGS * (long) BC_SEG_SIZE); + printf ("multiply digits = %ld\n", (long) LONG_MAX / (long) 90); + printf ("Number of vars = %ld\n", (long) MAX_STORE); +#ifdef OLD_EQ_OP + printf ("Old assignment operatiors are valid. (=-, =+, ...)\n"); +#endif +} + +/* bc_malloc will check the return value so all other places do not + have to do it! SIZE is the number of types to allocate. */ + +char * +bc_malloc (size) + int size; +{ + char *ptr; + + ptr = (char *) malloc (size); + if (ptr == NULL) + out_of_memory (); + + return ptr; +} + + +/* The following routines are error routines for various problems. */ + +/* Malloc could not get enought memory. */ + +void +out_of_memory() +{ + fprintf (stderr, "Fatal error: Out of memory for malloc.\n"); + exit (1); +} + + + +/* The standard yyerror routine. Built with variable number of argumnets. */ + +#ifndef VARARGS +#ifdef __STDC__ +void +yyerror (char *str, ...) +#else +void +yyerror (str) + char *str; +#endif +#else +void +yyerror (str, va_alist) + char *str; +#endif +{ + char *name; + va_list args; + +#ifndef VARARGS + va_start (args, str); +#else + va_start (args); +#endif + if (is_std_in) + name = "(standard_in)"; + else + name = g_argv[optind-1]; + fprintf (stderr,"%s %d: ",name,line_no); + vfprintf (stderr, str, args); + fprintf (stderr, "\n"); + had_error = TRUE; + va_end (args); +} + + +/* The routine to produce warnings about non-standard features + found during parsing. */ + +#ifndef VARARGS +#ifdef __STDC__ +void +warn (char *mesg, ...) +#else +void +warn (mesg) + char *mesg; +#endif +#else +void +warn (mesg, va_alist) + char *mesg; +#endif +{ + char *name; + va_list args; + +#ifndef VARARGS + va_start (args, mesg); +#else + va_start (args); +#endif + if (std_only) + { + if (is_std_in) + name = "(standard_in)"; + else + name = g_argv[optind-1]; + fprintf (stderr,"%s %d: ",name,line_no); + vfprintf (stderr, mesg, args); + fprintf (stderr, "\n"); + had_error = TRUE; + } + else + if (warn_not_std) + { + if (is_std_in) + name = "(standard_in)"; + else + name = g_argv[optind-1]; + fprintf (stderr,"%s %d: (Warning) ",name,line_no); + vfprintf (stderr, mesg, args); + fprintf (stderr, "\n"); + } + va_end (args); +} + +/* Runtime error will print a message and stop the machine. */ + +#ifndef VARARGS +#ifdef __STDC__ +void +rt_error (char *mesg, ...) +#else +void +rt_error (mesg) + char *mesg; +#endif +#else +void +rt_error (mesg, va_alist) + char *mesg; +#endif +{ + va_list args; + char error_mesg [255]; + +#ifndef VARARGS + va_start (args, mesg); +#else + va_start (args); +#endif + vsprintf (error_mesg, mesg, args); + va_end (args); + + fprintf (stderr, "Runtime error (func=%s, adr=%d): %s\n", + f_names[pc.pc_func], pc.pc_addr, error_mesg); + runtime_error = TRUE; +} + + +/* A runtime warning tells of some action taken by the processor that + may change the program execution but was not enough of a problem + to stop the execution. */ + +#ifndef VARARGS +#ifdef __STDC__ +void +rt_warn (char *mesg, ...) +#else +void +rt_warn (mesg) + char *mesg; +#endif +#else +void +rt_warn (mesg, va_alist) + char *mesg; +#endif +{ + va_list args; + char error_mesg [255]; + +#ifndef VARARGS + va_start (args, mesg); +#else + va_start (args); +#endif + vsprintf (error_mesg, mesg, args); + va_end (args); + + fprintf (stderr, "Runtime warning (func=%s, adr=%d): %s\n", + f_names[pc.pc_func], pc.pc_addr, error_mesg); +} diff --git a/commands/bc/version.h b/commands/bc/version.h new file mode 100755 index 000000000..6b3f90963 --- /dev/null +++ b/commands/bc/version.h @@ -0,0 +1,3 @@ +#define BC_VERSION \ + "bc 1.02 (Mar 3, 92) Copyright (C) 1991, 1992 Free Software Foundation, Inc." + diff --git a/commands/bc/y.tab.h b/commands/bc/y.tab.h new file mode 100755 index 000000000..9e65a2f20 --- /dev/null +++ b/commands/bc/y.tab.h @@ -0,0 +1,40 @@ +#define NEWLINE 257 +#define AND 258 +#define OR 259 +#define NOT 260 +#define STRING 261 +#define NAME 262 +#define NUMBER 263 +#define MUL_OP 264 +#define ASSIGN_OP 265 +#define REL_OP 266 +#define INCR_DECR 267 +#define Define 268 +#define Break 269 +#define Quit 270 +#define Length 271 +#define Return 272 +#define For 273 +#define If 274 +#define While 275 +#define Sqrt 276 +#define Else 277 +#define Scale 278 +#define Ibase 279 +#define Obase 280 +#define Auto 281 +#define Read 282 +#define Warranty 283 +#define Halt 284 +#define Last 285 +#define Continue 286 +#define Print 287 +#define Limits 288 +#define UNARY_MINUS 289 +typedef union { + char *s_value; + char c_value; + int i_value; + arg_list *a_value; + } YYSTYPE; +extern YYSTYPE yylval; diff --git a/commands/byacc/ACKNOWLEDGEMEN b/commands/byacc/ACKNOWLEDGEMEN new file mode 100755 index 000000000..b66bb2506 --- /dev/null +++ b/commands/byacc/ACKNOWLEDGEMEN @@ -0,0 +1,25 @@ + Berkeley Yacc owes much to the unflagging efforts of Keith Bostic. +His badgering kept me working on it long after I was ready to quit. + + Berkeley Yacc is based on the excellent algorithm for computing LALR(1) +lookaheads developed by Tom Pennello and Frank DeRemer. The algorithm is +described in their almost impenetrable article in TOPLAS 4,4. + + Finally, much of the credit for the latest version must go to those +who pointed out deficiencies of my earlier releases. Among the most +prolific contributors were + + Benson I. Margulies + Dave Gentzel + Antoine Verheijen + Peter S. Housel + Dale Smith + Ozan Yigit + John Campbell + Bill Sommerfeld + Paul Hilfinger + Gary Bridgewater + Dave Bakken + Dan Lanciani + Richard Sargent + Parag Patel diff --git a/commands/byacc/MANIFEST b/commands/byacc/MANIFEST new file mode 100755 index 000000000..f4f918e1f --- /dev/null +++ b/commands/byacc/MANIFEST @@ -0,0 +1,33 @@ + File Name Archive # Description +----------------------------------------------------------- + ACKNOWLEDGEMENTS 1 + CSU.diffs 1 + MANIFEST 1 This shipping list + Makefile 1 + NEW_FEATURES 1 + NOTES 1 + NO_WARRANTY 1 + README 1 + closure.c 1 + defs.h 1 + error.c 1 + lalr.c 1 + lr0.c 1 + main.c 1 + mkpar.c 1 + output.c 2 + reader.c 3 + skeleton.c 1 + symtab.c 1 + test 1 + test/error.output 1 + test/error.tab.c 1 + test/error.tab.h 1 + test/error.y 1 + test/ftp.output 2 + test/ftp.tab.c 3 + test/ftp.tab.h 1 + test/ftp.y 2 + verbose.c 1 + warshall.c 1 + yacc.1 1 diff --git a/commands/byacc/Makefile b/commands/byacc/Makefile new file mode 100755 index 000000000..e7680c4be --- /dev/null +++ b/commands/byacc/Makefile @@ -0,0 +1,84 @@ +# Makefile for Berkeley yacc. + +BINDIR = /usr/bin + +HDRS = defs.h + +CFLAGS = -DNDEBUG -D_MINIX -D_POSIX_SOURCE -wo $(OPT) + +LDFLAGS = -i + +LIBS = + +LINKER = $(CC) + +MAKEFILE = Makefile + +OBJS = closure.o \ + error.o \ + lalr.o \ + lr0.o \ + main.o \ + mkpar.o \ + output.o \ + reader.o \ + skeleton.o \ + symtab.o \ + verbose.o \ + warshall.o + +PRINT = pr -f -l88 + +PROGRAM = yacc + +SRCS = closure.c \ + error.c \ + lalr.c \ + lr0.c \ + main.c \ + mkpar.c \ + output.c \ + reader.c \ + skeleton.c \ + symtab.c \ + verbose.c \ + warshall.c + +all: $(PROGRAM) + +$(PROGRAM): $(OBJS) $(LIBS) + $(LINKER) $(LDFLAGS) -o $(PROGRAM) $(OBJS) $(LIBS) + install -S 384k $(PROGRAM) + +install: $(BINDIR)/yacc + +$(BINDIR)/yacc: $(PROGRAM) + install -cs -o bin $(PROGRAM) $@ + +clean:; rm -f $(OBJS) $(PROGRAM) core + +#depend:; @mkmf -f $(MAKEFILE) PROGRAM=$(PROGRAM) DEST=$(DEST) +# +#index:; @ctags -wx $(HDRS) $(SRCS) +# +#listing:; @$(PRINT) Makefile $(HDRS) $(SRCS) | lpr +# +#lint:; @lint $(SRCS) +# +#program: $(PROGRAM) +# +#tags: $(HDRS) $(SRCS); @ctags $(HDRS) $(SRCS) + +### +closure.o: defs.h +error.o: defs.h +lalr.o: defs.h +lr0.o: defs.h +main.o: defs.h +mkpar.o: defs.h +output.o: defs.h +reader.o: defs.h +skeleton.o: defs.h +symtab.o: defs.h +verbose.o: defs.h +warshall.o: defs.h diff --git a/commands/byacc/NEW_FEATURES b/commands/byacc/NEW_FEATURES new file mode 100755 index 000000000..b030c625b --- /dev/null +++ b/commands/byacc/NEW_FEATURES @@ -0,0 +1,46 @@ + The -r option has been implemented. The -r option tells Yacc to +put the read-only tables in y.tab.c and the code and variables in +y.code.c. Keith Bostic asked for this option so that :yyfix could be +eliminated. + + The -l and -t options have been implemented. The -l option tells +Yacc not to include #line directives in the code it produces. The -t +option causes debugging code to be included in the compiled parser. + + The code for error recovery has been changed to implement the same +algorithm as AT&T Yacc. There will still be differences in the way +error recovery works because AT&T Yacc uses more default reductions +than Berkeley Yacc. + + The environment variable TMPDIR determines the directory where +temporary files will be created. If TMPDIR is defined, temporary files +will be created in the directory whose pathname is the value of TMPDIR. +By default, temporary files are created in /tmp. + + The keywords are now case-insensitive. For example, %nonassoc, +%NONASSOC, %NonAssoc, and %nOnAsSoC are all equivalent. + + Commas and semicolons that are not part of C code are treated as +commentary. + + Line-end comments, as in BCPL, are permitted. Line-end comments +begin with // and end at the next end-of-line. Line-end comments are +permitted in C code; they are converted to C comments on output. + + The form of y.output files has been changed to look more like +those produced by AT&T Yacc. + + A new kind of declaration has been added. The form of the declaration +is + + %ident string + +where string is a sequence of characters begining with a double quote +and ending with either a double quote or the next end-of-line, whichever +comes first. The declaration will cause a #ident directive to be written +near the start of the output file. + + If a parser has been compiled with debugging code, that code can be +enabled by setting an environment variable. If the environment variable +YYDEBUG is set to 0, debugging output is suppressed. If it is set to 1, +debugging output is written to standard output. diff --git a/commands/byacc/NOTES b/commands/byacc/NOTES new file mode 100755 index 000000000..9db3c96ce --- /dev/null +++ b/commands/byacc/NOTES @@ -0,0 +1,9 @@ +Berkeley Yacc reflects its origins. The reason so many routines +use exactly six register variables is that Berkeley Yacc was +developed on a VAX using PCC. PCC placed at most six variables +in registers. I went to considerable effort to find which six +variables most belonged in registers. Changes in machines and +compilers make that effort worthless, perhaps even harmful. + +The code contains many instances where address calculations are +performed in particular ways to optimize the code for the VAX. diff --git a/commands/byacc/NO_WARRANTY b/commands/byacc/NO_WARRANTY new file mode 100755 index 000000000..06e8d93a2 --- /dev/null +++ b/commands/byacc/NO_WARRANTY @@ -0,0 +1,3 @@ + Berkeley Yacc is distributed with no warranty whatever. The author +and any other contributors take no responsibility for the consequences of +its use. diff --git a/commands/byacc/README b/commands/byacc/README new file mode 100755 index 000000000..091f23343 --- /dev/null +++ b/commands/byacc/README @@ -0,0 +1,23 @@ + Berkeley Yacc is an LALR(1) parser generator. Berkeley Yacc has been made +as compatible as possible with AT&T Yacc. Berkeley Yacc can accept any input +specification that conforms to the AT&T Yacc documentation. Specifications +that take advantage of undocumented features of AT&T Yacc will probably be +rejected. + + Berkeley Yacc is distributed with no warranty whatever. The code is certain +to contain errors. Neither the author nor any contributor takes responsibility +for any consequences of its use. + + Berkeley Yacc is in the public domain. The data structures and algorithms +used in Berkeley Yacc are all either taken from documents available to the +general public or are inventions of the author. Anyone may freely distribute +source or binary forms of Berkeley Yacc whether unchanged or modified. +Distributers may charge whatever fees they can obtain for Berkeley Yacc. +Programs generated by Berkeley Yacc may be distributed freely. + + Please report bugs to + + robert.corbett@eng.Sun.COM + +Include a small example if possible. Please include the banner string from +skeleton.c with the bug report. Do not expect rapid responses. diff --git a/commands/byacc/closure.c b/commands/byacc/closure.c new file mode 100755 index 000000000..7c8f95791 --- /dev/null +++ b/commands/byacc/closure.c @@ -0,0 +1,255 @@ +#include "defs.h" + +short *itemset; +short *itemsetend; +unsigned *ruleset; + +static unsigned *first_derives; +static unsigned *EFF; + + +set_EFF() +{ + register unsigned *row; + register int symbol; + register short *sp; + register int rowsize; + register int i; + register int rule; + + rowsize = WORDSIZE(nvars); + EFF = NEW2(nvars * rowsize, unsigned); + + row = EFF; + for (i = start_symbol; i < nsyms; i++) + { + sp = derives[i]; + for (rule = *sp; rule > 0; rule = *++sp) + { + symbol = ritem[rrhs[rule]]; + if (ISVAR(symbol)) + { + symbol -= start_symbol; + SETBIT(row, symbol); + } + } + row += rowsize; + } + + reflexive_transitive_closure(EFF, nvars); + +#ifdef DEBUG + print_EFF(); +#endif +} + + +set_first_derives() +{ + register unsigned *rrow; + register unsigned *vrow; + register int j; + register unsigned k; + register unsigned cword; + register short *rp; + + int rule; + int i; + int rulesetsize; + int varsetsize; + + rulesetsize = WORDSIZE(nrules); + varsetsize = WORDSIZE(nvars); + first_derives = NEW2(nvars * rulesetsize, unsigned) - ntokens * rulesetsize; + + set_EFF(); + + rrow = first_derives + ntokens * rulesetsize; + for (i = start_symbol; i < nsyms; i++) + { + vrow = EFF + ((i - ntokens) * varsetsize); + k = BITS_PER_WORD; + for (j = start_symbol; j < nsyms; k++, j++) + { + if (k >= BITS_PER_WORD) + { + cword = *vrow++; + k = 0; + } + + if (cword & (1 << k)) + { + rp = derives[j]; + while ((rule = *rp++) >= 0) + { + SETBIT(rrow, rule); + } + } + } + + vrow += varsetsize; + rrow += rulesetsize; + } + +#ifdef DEBUG + print_first_derives(); +#endif + + FREE(EFF); +} + + +closure(nucleus, n) +short *nucleus; +int n; +{ + register int ruleno; + register unsigned word; + register unsigned i; + register short *csp; + register unsigned *dsp; + register unsigned *rsp; + register int rulesetsize; + + short *csend; + unsigned *rsend; + int symbol; + int itemno; + + rulesetsize = WORDSIZE(nrules); + rsp = ruleset; + rsend = ruleset + rulesetsize; + for (rsp = ruleset; rsp < rsend; rsp++) + *rsp = 0; + + csend = nucleus + n; + for (csp = nucleus; csp < csend; ++csp) + { + symbol = ritem[*csp]; + if (ISVAR(symbol)) + { + dsp = first_derives + symbol * rulesetsize; + rsp = ruleset; + while (rsp < rsend) + *rsp++ |= *dsp++; + } + } + + ruleno = 0; + itemsetend = itemset; + csp = nucleus; + for (rsp = ruleset; rsp < rsend; ++rsp) + { + word = *rsp; + if (word) + { + for (i = 0; i < BITS_PER_WORD; ++i) + { + if (word & (1 << i)) + { + itemno = rrhs[ruleno+i]; + while (csp < csend && *csp < itemno) + *itemsetend++ = *csp++; + *itemsetend++ = itemno; + while (csp < csend && *csp == itemno) + ++csp; + } + } + } + ruleno += BITS_PER_WORD; + } + + while (csp < csend) + *itemsetend++ = *csp++; + +#ifdef DEBUG + print_closure(n); +#endif +} + + + +finalize_closure() +{ + FREE(itemset); + FREE(ruleset); + FREE(first_derives + ntokens * WORDSIZE(nrules)); +} + + +#ifdef DEBUG + +print_closure(n) +int n; +{ + register short *isp; + + printf("\n\nn = %d\n\n", n); + for (isp = itemset; isp < itemsetend; isp++) + printf(" %d\n", *isp); +} + + +print_EFF() +{ + register int i, j; + register unsigned *rowp; + register unsigned word; + register unsigned k; + + printf("\n\nEpsilon Free Firsts\n"); + + for (i = start_symbol; i < nsyms; i++) + { + printf("\n%s", symbol_name[i]); + rowp = EFF + ((i - start_symbol) * WORDSIZE(nvars)); + word = *rowp++; + + k = BITS_PER_WORD; + for (j = 0; j < nvars; k++, j++) + { + if (k >= BITS_PER_WORD) + { + word = *rowp++; + k = 0; + } + + if (word & (1 << k)) + printf(" %s", symbol_name[start_symbol + j]); + } + } +} + + +print_first_derives() +{ + register int i; + register int j; + register unsigned *rp; + register unsigned cword; + register unsigned k; + + printf("\n\n\nFirst Derives\n"); + + for (i = start_symbol; i < nsyms; i++) + { + printf("\n%s derives\n", symbol_name[i]); + rp = first_derives + i * WORDSIZE(nrules); + k = BITS_PER_WORD; + for (j = 0; j <= nrules; k++, j++) + { + if (k >= BITS_PER_WORD) + { + cword = *rp++; + k = 0; + } + + if (cword & (1 << k)) + printf(" %d\n", j); + } + } + + fflush(stdout); +} + +#endif diff --git a/commands/byacc/defs.h b/commands/byacc/defs.h new file mode 100755 index 000000000..c8d517917 --- /dev/null +++ b/commands/byacc/defs.h @@ -0,0 +1,298 @@ +#include +#include +#include + + +/* machine-dependent definitions */ +/* the following definitions are for the Tahoe */ +/* they might have to be changed for other machines */ + +/* MAXCHAR is the largest unsigned character value */ +/* MAXSHORT is the largest value of a C short */ +/* MINSHORT is the most negative value of a C short */ +/* MAXTABLE is the maximum table size */ +/* BITS_PER_WORD is the number of bits in a C unsigned */ +/* WORDSIZE computes the number of words needed to */ +/* store n bits */ +/* BIT returns the value of the n-th bit starting */ +/* from r (0-indexed) */ +/* SETBIT sets the n-th bit starting from r */ + +#if !__STDC__ +#define MAXCHAR 255 +#define MAXSHORT 32767 +#define MINSHORT -32768 +#define BITS_PER_WORD 32 +#else +#include +#define MAXCHAR UCHAR_MAX +#define MAXSHORT SHRT_MAX +#define MINSHORT SHRT_MIN +#define BITS_PER_WORD (INT_MAX == 32767 ? 16 : 32) +#define BITS_PER_BPW (INT_MAX == 32767 ? 4 : 5) +#endif +#define MAXTABLE 32500 +#define WORDSIZE(n) (((n)+(BITS_PER_WORD-1))/BITS_PER_WORD) +#define BIT(r, n) ((((r)[(n)>>BITS_PER_BPW])>>((n)&(BITS_PER_WORD-1)))&1) +#define SETBIT(r, n) ((r)[(n)>>BITS_PER_BPW]|=((unsigned)1<<((n)&(BITS_PER_WORD-1)))) + + +/* character names */ + +#define NUL '\0' /* the null character */ +#define NEWLINE '\n' /* line feed */ +#define SP ' ' /* space */ +#define BS '\b' /* backspace */ +#define HT '\t' /* horizontal tab */ +#define VT '\013' /* vertical tab */ +#define CR '\r' /* carriage return */ +#define FF '\f' /* form feed */ +#define QUOTE '\'' /* single quote */ +#define DOUBLE_QUOTE '\"' /* double quote */ +#define BACKSLASH '\\' /* backslash */ + + +/* defines for constructing filenames */ + +#define CODE_SUFFIX ".code.c" +#define DEFINES_SUFFIX ".tab.h" +#define OUTPUT_SUFFIX ".tab.c" +#define VERBOSE_SUFFIX ".output" + + +/* keyword codes */ + +#define TOKEN 0 +#define LEFT 1 +#define RIGHT 2 +#define NONASSOC 3 +#define MARK 4 +#define TEXT 5 +#define TYPE 6 +#define START 7 +#define UNION 8 +#define IDENT 9 + + +/* symbol classes */ + +#define UNKNOWN 0 +#define TERM 1 +#define NONTERM 2 + + +/* the undefined value */ + +#define UNDEFINED (-1) + + +/* action codes */ + +#define SHIFT 1 +#define REDUCE 2 + + +/* character macros */ + +#define IS_IDENT(c) (isalnum(c) || (c) == '_' || (c) == '.' || (c) == '$') +#define IS_OCTAL(c) ((c) >= '0' && (c) <= '7') +#define NUMERIC_VALUE(c) ((c) - '0') + + +/* symbol macros */ + +#define ISTOKEN(s) ((s) < start_symbol) +#define ISVAR(s) ((s) >= start_symbol) + + +/* storage allocation macros */ + +#define CALLOC(k,n) (calloc((unsigned)(k),(unsigned)(n))) +#define FREE(x) (free((char*)(x))) +#define MALLOC(n) (malloc((unsigned)(n))) +#define NEW(t) ((t*)allocate(sizeof(t))) +#define NEW2(n,t) ((t*)allocate((unsigned)((n)*sizeof(t)))) +#define REALLOC(p,n) (realloc((char*)(p),(unsigned)(n))) + + +/* the structure of a symbol table entry */ + +typedef struct bucket bucket; +struct bucket +{ + struct bucket *link; + struct bucket *next; + char *name; + char *tag; + short value; + short index; + short prec; + char class; + char assoc; +}; + + +/* the structure of the LR(0) state machine */ + +typedef struct core core; +struct core +{ + struct core *next; + struct core *link; + short number; + short accessing_symbol; + short nitems; + short items[1]; +}; + + +/* the structure used to record shifts */ + +typedef struct shifts shifts; +struct shifts +{ + struct shifts *next; + short number; + short nshifts; + short shift[1]; +}; + + +/* the structure used to store reductions */ + +typedef struct reductions reductions; +struct reductions +{ + struct reductions *next; + short number; + short nreds; + short rules[1]; +}; + + +/* the structure used to represent parser actions */ + +typedef struct action action; +struct action +{ + struct action *next; + short symbol; + short number; + short prec; + char action_code; + char assoc; + char suppressed; +}; + + +/* global variables */ + +extern char dflag; +extern char lflag; +extern char rflag; +extern char tflag; +extern char vflag; +extern char *symbol_prefix; + +extern char *myname; +extern char *cptr; +extern char *line; +extern int lineno; +extern int outline; + +extern char *banner[]; +extern char *tables[]; +extern char *header[]; +extern char *body[]; +extern char *trailer[]; + +extern char *action_file_name; +extern char *code_file_name; +extern char *defines_file_name; +extern char *input_file_name; +extern char *output_file_name; +extern char *text_file_name; +extern char *union_file_name; +extern char *verbose_file_name; + +extern FILE *action_file; +extern FILE *code_file; +extern FILE *defines_file; +extern FILE *input_file; +extern FILE *output_file; +extern FILE *text_file; +extern FILE *union_file; +extern FILE *verbose_file; + +extern int nitems; +extern int nrules; +extern int nsyms; +extern int ntokens; +extern int nvars; +extern int ntags; + +extern char unionized; +extern char line_format[]; + +extern int start_symbol; +extern char **symbol_name; +extern short *symbol_value; +extern short *symbol_prec; +extern char *symbol_assoc; + +extern short *ritem; +extern short *rlhs; +extern short *rrhs; +extern short *rprec; +extern char *rassoc; + +extern short **derives; +extern char *nullable; + +extern bucket *first_symbol; +extern bucket *last_symbol; + +extern int nstates; +extern core *first_state; +extern shifts *first_shift; +extern reductions *first_reduction; +extern short *accessing_symbol; +extern core **state_table; +extern shifts **shift_table; +extern reductions **reduction_table; +extern unsigned *LA; +extern short *LAruleno; +extern short *lookaheads; +extern short *goto_map; +extern short *from_state; +extern short *to_state; + +extern action **parser; +extern int SRtotal; +extern int RRtotal; +extern short *SRconflicts; +extern short *RRconflicts; +extern short *defred; +extern short *rules_used; +extern short nunused; +extern short final_state; + +/* global functions */ + +extern char *allocate(); +extern bucket *lookup(); +extern bucket *make_bucket(); + + +/* system variables */ + +extern int errno; + + +/* system functions */ + +extern void free(); +extern char *calloc(); +extern char *malloc(); +extern char *realloc(); +extern char *strcpy(); diff --git a/commands/byacc/error.c b/commands/byacc/error.c new file mode 100755 index 000000000..3ed564c62 --- /dev/null +++ b/commands/byacc/error.c @@ -0,0 +1,317 @@ +/* routines for printing error messages */ + +#include "defs.h" + + +fatal(msg) +char *msg; +{ + fprintf(stderr, "%s: f - %s\n", myname, msg); + done(2); +} + + +no_space() +{ + fprintf(stderr, "%s: f - out of space\n", myname); + done(2); +} + + +open_error(filename) +char *filename; +{ + fprintf(stderr, "%s: f - cannot open \"%s\"\n", myname, filename); + done(2); +} + + +unexpected_EOF() +{ + fprintf(stderr, "%s: e - line %d of \"%s\", unexpected end-of-file\n", + myname, lineno, input_file_name); + done(1); +} + + +print_pos(st_line, st_cptr) +char *st_line; +char *st_cptr; +{ + register char *s; + + if (st_line == 0) return; + for (s = st_line; *s != '\n'; ++s) + { + if (isprint(*s) || *s == '\t') + putc(*s, stderr); + else + putc('?', stderr); + } + putc('\n', stderr); + for (s = st_line; s < st_cptr; ++s) + { + if (*s == '\t') + putc('\t', stderr); + else + putc(' ', stderr); + } + putc('^', stderr); + putc('\n', stderr); +} + + +syntax_error(st_lineno, st_line, st_cptr) +int st_lineno; +char *st_line; +char *st_cptr; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", syntax error\n", + myname, st_lineno, input_file_name); + print_pos(st_line, st_cptr); + done(1); +} + + +unterminated_comment(c_lineno, c_line, c_cptr) +int c_lineno; +char *c_line; +char *c_cptr; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", unmatched /*\n", + myname, c_lineno, input_file_name); + print_pos(c_line, c_cptr); + done(1); +} + + +unterminated_string(s_lineno, s_line, s_cptr) +int s_lineno; +char *s_line; +char *s_cptr; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", unterminated string\n", + myname, s_lineno, input_file_name); + print_pos(s_line, s_cptr); + done(1); +} + + +unterminated_text(t_lineno, t_line, t_cptr) +int t_lineno; +char *t_line; +char *t_cptr; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", unmatched %%{\n", + myname, t_lineno, input_file_name); + print_pos(t_line, t_cptr); + done(1); +} + + +unterminated_union(u_lineno, u_line, u_cptr) +int u_lineno; +char *u_line; +char *u_cptr; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", unterminated %%union \ +declaration\n", myname, u_lineno, input_file_name); + print_pos(u_line, u_cptr); + done(1); +} + + +over_unionized(u_cptr) +char *u_cptr; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", too many %%union \ +declarations\n", myname, lineno, input_file_name); + print_pos(line, u_cptr); + done(1); +} + + +illegal_tag(t_lineno, t_line, t_cptr) +int t_lineno; +char *t_line; +char *t_cptr; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", illegal tag\n", + myname, t_lineno, input_file_name); + print_pos(t_line, t_cptr); + done(1); +} + + +illegal_character(c_cptr) +char *c_cptr; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", illegal character\n", + myname, lineno, input_file_name); + print_pos(line, c_cptr); + done(1); +} + + +used_reserved(s) +char *s; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", illegal use of reserved symbol \ +%s\n", myname, lineno, input_file_name, s); + done(1); +} + + +tokenized_start(s) +char *s; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", the start symbol %s cannot be \ +declared to be a token\n", myname, lineno, input_file_name, s); + done(1); +} + + +retyped_warning(s) +char *s; +{ + fprintf(stderr, "%s: w - line %d of \"%s\", the type of %s has been \ +redeclared\n", myname, lineno, input_file_name, s); +} + + +reprec_warning(s) +char *s; +{ + fprintf(stderr, "%s: w - line %d of \"%s\", the precedence of %s has been \ +redeclared\n", myname, lineno, input_file_name, s); +} + + +revalued_warning(s) +char *s; +{ + fprintf(stderr, "%s: w - line %d of \"%s\", the value of %s has been \ +redeclared\n", myname, lineno, input_file_name, s); +} + + +terminal_start(s) +char *s; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", the start symbol %s is a \ +token\n", myname, lineno, input_file_name, s); + done(1); +} + + +restarted_warning() +{ + fprintf(stderr, "%s: w - line %d of \"%s\", the start symbol has been \ +redeclared\n", myname, lineno, input_file_name); +} + + +no_grammar() +{ + fprintf(stderr, "%s: e - line %d of \"%s\", no grammar has been \ +specified\n", myname, lineno, input_file_name); + done(1); +} + + +terminal_lhs(s_lineno) +int s_lineno; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", a token appears on the lhs \ +of a production\n", myname, s_lineno, input_file_name); + done(1); +} + + +prec_redeclared() +{ + fprintf(stderr, "%s: w - line %d of \"%s\", conflicting %%prec \ +specifiers\n", myname, lineno, input_file_name); +} + + +unterminated_action(a_lineno, a_line, a_cptr) +int a_lineno; +char *a_line; +char *a_cptr; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", unterminated action\n", + myname, a_lineno, input_file_name); + print_pos(a_line, a_cptr); + done(1); +} + + +dollar_warning(a_lineno, i) +int a_lineno; +int i; +{ + fprintf(stderr, "%s: w - line %d of \"%s\", $%d references beyond the \ +end of the current rule\n", myname, a_lineno, input_file_name, i); +} + + +dollar_error(a_lineno, a_line, a_cptr) +int a_lineno; +char *a_line; +char *a_cptr; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", illegal $-name\n", + myname, a_lineno, input_file_name); + print_pos(a_line, a_cptr); + done(1); +} + + +untyped_lhs() +{ + fprintf(stderr, "%s: e - line %d of \"%s\", $$ is untyped\n", + myname, lineno, input_file_name); + done(1); +} + + +untyped_rhs(i, s) +int i; +char *s; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", $%d (%s) is untyped\n", + myname, lineno, input_file_name, i, s); + done(1); +} + + +unknown_rhs(i) +int i; +{ + fprintf(stderr, "%s: e - line %d of \"%s\", $%d is untyped\n", + myname, lineno, input_file_name, i); + done(1); +} + + +default_action_warning() +{ + fprintf(stderr, "%s: w - line %d of \"%s\", the default action assigns an \ +undefined value to $$\n", myname, lineno, input_file_name); +} + + +undefined_goal(s) +char *s; +{ + fprintf(stderr, "%s: e - the start symbol %s is undefined\n", myname, s); + done(1); +} + + +undefined_symbol_warning(s) +char *s; +{ + fprintf(stderr, "%s: w - the symbol %s is undefined\n", myname, s); +} diff --git a/commands/byacc/lalr.c b/commands/byacc/lalr.c new file mode 100755 index 000000000..640ddc4ca --- /dev/null +++ b/commands/byacc/lalr.c @@ -0,0 +1,638 @@ +#include "defs.h" + +typedef + struct shorts + { + struct shorts *next; + short value; + } + shorts; + +int tokensetsize; +short *lookaheads; +short *LAruleno; +unsigned *LA; +short *accessing_symbol; +core **state_table; +shifts **shift_table; +reductions **reduction_table; +short *goto_map; +short *from_state; +short *to_state; + +short **transpose(); + +static int infinity; +static int maxrhs; +static int ngotos; +static unsigned *F; +static short **includes; +static shorts **lookback; +static short **R; +static short *INDEX; +static short *VERTICES; +static int top; + + +lalr() +{ + tokensetsize = WORDSIZE(ntokens); + + set_state_table(); + set_accessing_symbol(); + set_shift_table(); + set_reduction_table(); + set_maxrhs(); + initialize_LA(); + set_goto_map(); + initialize_F(); + build_relations(); + compute_FOLLOWS(); + compute_lookaheads(); +} + + + +set_state_table() +{ + register core *sp; + + state_table = NEW2(nstates, core *); + for (sp = first_state; sp; sp = sp->next) + state_table[sp->number] = sp; +} + + + +set_accessing_symbol() +{ + register core *sp; + + accessing_symbol = NEW2(nstates, short); + for (sp = first_state; sp; sp = sp->next) + accessing_symbol[sp->number] = sp->accessing_symbol; +} + + + +set_shift_table() +{ + register shifts *sp; + + shift_table = NEW2(nstates, shifts *); + for (sp = first_shift; sp; sp = sp->next) + shift_table[sp->number] = sp; +} + + + +set_reduction_table() +{ + register reductions *rp; + + reduction_table = NEW2(nstates, reductions *); + for (rp = first_reduction; rp; rp = rp->next) + reduction_table[rp->number] = rp; +} + + + +set_maxrhs() +{ + register short *itemp; + register short *item_end; + register int length; + register int max; + + length = 0; + max = 0; + item_end = ritem + nitems; + for (itemp = ritem; itemp < item_end; itemp++) + { + if (*itemp >= 0) + { + length++; + } + else + { + if (length > max) max = length; + length = 0; + } + } + + maxrhs = max; +} + + + +initialize_LA() +{ + register int i, j, k; + register reductions *rp; + + lookaheads = NEW2(nstates + 1, short); + + k = 0; + for (i = 0; i < nstates; i++) + { + lookaheads[i] = k; + rp = reduction_table[i]; + if (rp) + k += rp->nreds; + } + lookaheads[nstates] = k; + + LA = NEW2(k * tokensetsize, unsigned); + LAruleno = NEW2(k, short); + lookback = NEW2(k, shorts *); + + k = 0; + for (i = 0; i < nstates; i++) + { + rp = reduction_table[i]; + if (rp) + { + for (j = 0; j < rp->nreds; j++) + { + LAruleno[k] = rp->rules[j]; + k++; + } + } + } +} + + +set_goto_map() +{ + register shifts *sp; + register int i; + register int symbol; + register int k; + register short *temp_map; + register int state2; + register int state1; + + goto_map = NEW2(nvars + 1, short) - ntokens; + temp_map = NEW2(nvars + 1, short) - ntokens; + + ngotos = 0; + for (sp = first_shift; sp; sp = sp->next) + { + for (i = sp->nshifts - 1; i >= 0; i--) + { + symbol = accessing_symbol[sp->shift[i]]; + + if (ISTOKEN(symbol)) break; + + if (ngotos == MAXSHORT) + fatal("too many gotos"); + + ngotos++; + goto_map[symbol]++; + } + } + + k = 0; + for (i = ntokens; i < nsyms; i++) + { + temp_map[i] = k; + k += goto_map[i]; + } + + for (i = ntokens; i < nsyms; i++) + goto_map[i] = temp_map[i]; + + goto_map[nsyms] = ngotos; + temp_map[nsyms] = ngotos; + + from_state = NEW2(ngotos, short); + to_state = NEW2(ngotos, short); + + for (sp = first_shift; sp; sp = sp->next) + { + state1 = sp->number; + for (i = sp->nshifts - 1; i >= 0; i--) + { + state2 = sp->shift[i]; + symbol = accessing_symbol[state2]; + + if (ISTOKEN(symbol)) break; + + k = temp_map[symbol]++; + from_state[k] = state1; + to_state[k] = state2; + } + } + + FREE(temp_map + ntokens); +} + + + +/* Map_goto maps a state/symbol pair into its numeric representation. */ + +int +map_goto(state, symbol) +int state; +int symbol; +{ + register int high; + register int low; + register int middle; + register int s; + + low = goto_map[symbol]; + high = goto_map[symbol + 1]; + + for (;;) + { + assert(low <= high); + middle = (low + high) >> 1; + s = from_state[middle]; + if (s == state) + return (middle); + else if (s < state) + low = middle + 1; + else + high = middle - 1; + } +} + + + +initialize_F() +{ + register int i; + register int j; + register int k; + register shifts *sp; + register short *edge; + register unsigned *rowp; + register short *rp; + register short **reads; + register int nedges; + register int stateno; + register int symbol; + register int nwords; + + nwords = ngotos * tokensetsize; + F = NEW2(nwords, unsigned); + + reads = NEW2(ngotos, short *); + edge = NEW2(ngotos + 1, short); + nedges = 0; + + rowp = F; + for (i = 0; i < ngotos; i++) + { + stateno = to_state[i]; + sp = shift_table[stateno]; + + if (sp) + { + k = sp->nshifts; + + for (j = 0; j < k; j++) + { + symbol = accessing_symbol[sp->shift[j]]; + if (ISVAR(symbol)) + break; + SETBIT(rowp, symbol); + } + + for (; j < k; j++) + { + symbol = accessing_symbol[sp->shift[j]]; + if (nullable[symbol]) + edge[nedges++] = map_goto(stateno, symbol); + } + + if (nedges) + { + reads[i] = rp = NEW2(nedges + 1, short); + + for (j = 0; j < nedges; j++) + rp[j] = edge[j]; + + rp[nedges] = -1; + nedges = 0; + } + } + + rowp += tokensetsize; + } + + SETBIT(F, 0); + digraph(reads); + + for (i = 0; i < ngotos; i++) + { + if (reads[i]) + FREE(reads[i]); + } + + FREE(reads); + FREE(edge); +} + + + +build_relations() +{ + register int i; + register int j; + register int k; + register short *rulep; + register short *rp; + register shifts *sp; + register int length; + register int nedges; + register int done; + register int state1; + register int stateno; + register int symbol1; + register int symbol2; + register short *shortp; + register short *edge; + register short *states; + register short **new_includes; + + includes = NEW2(ngotos, short *); + edge = NEW2(ngotos + 1, short); + states = NEW2(maxrhs + 1, short); + + for (i = 0; i < ngotos; i++) + { + nedges = 0; + state1 = from_state[i]; + symbol1 = accessing_symbol[to_state[i]]; + + for (rulep = derives[symbol1]; *rulep >= 0; rulep++) + { + length = 1; + states[0] = state1; + stateno = state1; + + for (rp = ritem + rrhs[*rulep]; *rp >= 0; rp++) + { + symbol2 = *rp; + sp = shift_table[stateno]; + k = sp->nshifts; + + for (j = 0; j < k; j++) + { + stateno = sp->shift[j]; + if (accessing_symbol[stateno] == symbol2) break; + } + + states[length++] = stateno; + } + + add_lookback_edge(stateno, *rulep, i); + + length--; + done = 0; + while (!done) + { + done = 1; + rp--; + if (ISVAR(*rp)) + { + stateno = states[--length]; + edge[nedges++] = map_goto(stateno, *rp); + if (nullable[*rp] && length > 0) done = 0; + } + } + } + + if (nedges) + { + includes[i] = shortp = NEW2(nedges + 1, short); + for (j = 0; j < nedges; j++) + shortp[j] = edge[j]; + shortp[nedges] = -1; + } + } + + new_includes = transpose(includes, ngotos); + + for (i = 0; i < ngotos; i++) + if (includes[i]) + FREE(includes[i]); + + FREE(includes); + + includes = new_includes; + + FREE(edge); + FREE(states); +} + + +add_lookback_edge(stateno, ruleno, gotono) +int stateno, ruleno, gotono; +{ + register int i, k; + register int found; + register shorts *sp; + + i = lookaheads[stateno]; + k = lookaheads[stateno + 1]; + found = 0; + while (!found && i < k) + { + if (LAruleno[i] == ruleno) + found = 1; + else + ++i; + } + assert(found); + + sp = NEW(shorts); + sp->next = lookback[i]; + sp->value = gotono; + lookback[i] = sp; +} + + + +short ** +transpose(R, n) +short **R; +int n; +{ + register short **new_R; + register short **temp_R; + register short *nedges; + register short *sp; + register int i; + register int k; + + nedges = NEW2(n, short); + + for (i = 0; i < n; i++) + { + sp = R[i]; + if (sp) + { + while (*sp >= 0) + nedges[*sp++]++; + } + } + + new_R = NEW2(n, short *); + temp_R = NEW2(n, short *); + + for (i = 0; i < n; i++) + { + k = nedges[i]; + if (k > 0) + { + sp = NEW2(k + 1, short); + new_R[i] = sp; + temp_R[i] = sp; + sp[k] = -1; + } + } + + FREE(nedges); + + for (i = 0; i < n; i++) + { + sp = R[i]; + if (sp) + { + while (*sp >= 0) + *temp_R[*sp++]++ = i; + } + } + + FREE(temp_R); + + return (new_R); +} + + + +compute_FOLLOWS() +{ + digraph(includes); +} + + +compute_lookaheads() +{ + register int i, n; + register unsigned *fp1, *fp2, *fp3; + register shorts *sp, *next; + register unsigned *rowp; + + rowp = LA; + n = lookaheads[nstates]; + for (i = 0; i < n; i++) + { + fp3 = rowp + tokensetsize; + for (sp = lookback[i]; sp; sp = sp->next) + { + fp1 = rowp; + fp2 = F + tokensetsize * sp->value; + while (fp1 < fp3) + *fp1++ |= *fp2++; + } + rowp = fp3; + } + + for (i = 0; i < n; i++) + for (sp = lookback[i]; sp; sp = next) + { + next = sp->next; + FREE(sp); + } + + FREE(lookback); + FREE(F); +} + + +digraph(relation) +short **relation; +{ + register int i; + + infinity = ngotos + 2; + INDEX = NEW2(ngotos + 1, short); + VERTICES = NEW2(ngotos + 1, short); + top = 0; + + R = relation; + + for (i = 0; i < ngotos; i++) + INDEX[i] = 0; + + for (i = 0; i < ngotos; i++) + { + if (INDEX[i] == 0 && R[i]) + traverse(i); + } + + FREE(INDEX); + FREE(VERTICES); +} + + + +traverse(i) +register int i; +{ + register unsigned *fp1; + register unsigned *fp2; + register unsigned *fp3; + register int j; + register short *rp; + + int height; + unsigned *base; + + VERTICES[++top] = i; + INDEX[i] = height = top; + + base = F + i * tokensetsize; + fp3 = base + tokensetsize; + + rp = R[i]; + if (rp) + { + while ((j = *rp++) >= 0) + { + if (INDEX[j] == 0) + traverse(j); + + if (INDEX[i] > INDEX[j]) + INDEX[i] = INDEX[j]; + + fp1 = base; + fp2 = F + j * tokensetsize; + + while (fp1 < fp3) + *fp1++ |= *fp2++; + } + } + + if (INDEX[i] == height) + { + for (;;) + { + j = VERTICES[top--]; + INDEX[j] = infinity; + + if (i == j) + break; + + fp1 = base; + fp2 = F + j * tokensetsize; + + while (fp1 < fp3) + *fp2++ = *fp1++; + } + } +} diff --git a/commands/byacc/lr0.c b/commands/byacc/lr0.c new file mode 100755 index 000000000..3ee42a884 --- /dev/null +++ b/commands/byacc/lr0.c @@ -0,0 +1,598 @@ + +#include "defs.h" + +extern short *itemset; +extern short *itemsetend; +extern unsigned *ruleset; + +int nstates; +core *first_state; +shifts *first_shift; +reductions *first_reduction; + +int get_state(); +core *new_state(); + +static core **state_set; +static core *this_state; +static core *last_state; +static shifts *last_shift; +static reductions *last_reduction; + +static int nshifts; +static short *shift_symbol; + +static short *redset; +static short *shiftset; + +static short **kernel_base; +static short **kernel_end; +static short *kernel_items; + + +allocate_itemsets() +{ + register short *itemp; + register short *item_end; + register int symbol; + register int i; + register int count; + register int max; + register short *symbol_count; + + count = 0; + symbol_count = NEW2(nsyms, short); + + item_end = ritem + nitems; + for (itemp = ritem; itemp < item_end; itemp++) + { + symbol = *itemp; + if (symbol >= 0) + { + count++; + symbol_count[symbol]++; + } + } + + kernel_base = NEW2(nsyms, short *); + kernel_items = NEW2(count, short); + + count = 0; + max = 0; + for (i = 0; i < nsyms; i++) + { + kernel_base[i] = kernel_items + count; + count += symbol_count[i]; + if (max < symbol_count[i]) + max = symbol_count[i]; + } + + shift_symbol = symbol_count; + kernel_end = NEW2(nsyms, short *); +} + + +allocate_storage() +{ + allocate_itemsets(); + shiftset = NEW2(nsyms, short); + redset = NEW2(nrules + 1, short); + state_set = NEW2(nitems, core *); +} + + +append_states() +{ + register int i; + register int j; + register int symbol; + +#ifdef TRACE + fprintf(stderr, "Entering append_states()\n"); +#endif + for (i = 1; i < nshifts; i++) + { + symbol = shift_symbol[i]; + j = i; + while (j > 0 && shift_symbol[j - 1] > symbol) + { + shift_symbol[j] = shift_symbol[j - 1]; + j--; + } + shift_symbol[j] = symbol; + } + + for (i = 0; i < nshifts; i++) + { + symbol = shift_symbol[i]; + shiftset[i] = get_state(symbol); + } +} + + +free_storage() +{ + FREE(shift_symbol); + FREE(redset); + FREE(shiftset); + FREE(kernel_base); + FREE(kernel_end); + FREE(kernel_items); + FREE(state_set); +} + + + +generate_states() +{ + allocate_storage(); + itemset = NEW2(nitems, short); + ruleset = NEW2(WORDSIZE(nrules), unsigned); + set_first_derives(); + initialize_states(); + + while (this_state) + { + closure(this_state->items, this_state->nitems); + save_reductions(); + new_itemsets(); + append_states(); + + if (nshifts > 0) + save_shifts(); + + this_state = this_state->next; + } + + finalize_closure(); + free_storage(); +} + + + +int +get_state(symbol) +int symbol; +{ + register int key; + register short *isp1; + register short *isp2; + register short *iend; + register core *sp; + register int found; + register int n; + +#ifdef TRACE + fprintf(stderr, "Entering get_state(%d)\n", symbol); +#endif + + isp1 = kernel_base[symbol]; + iend = kernel_end[symbol]; + n = iend - isp1; + + key = *isp1; + assert(0 <= key && key < nitems); + sp = state_set[key]; + if (sp) + { + found = 0; + while (!found) + { + if (sp->nitems == n) + { + found = 1; + isp1 = kernel_base[symbol]; + isp2 = sp->items; + + while (found && isp1 < iend) + { + if (*isp1++ != *isp2++) + found = 0; + } + } + + if (!found) + { + if (sp->link) + { + sp = sp->link; + } + else + { + sp = sp->link = new_state(symbol); + found = 1; + } + } + } + } + else + { + state_set[key] = sp = new_state(symbol); + } + + return (sp->number); +} + + + +initialize_states() +{ + register int i; + register short *start_derives; + register core *p; + + start_derives = derives[start_symbol]; + for (i = 0; start_derives[i] >= 0; ++i) + continue; + + p = (core *) MALLOC(sizeof(core) + i*sizeof(short)); + if (p == 0) no_space(); + + p->next = 0; + p->link = 0; + p->number = 0; + p->accessing_symbol = 0; + p->nitems = i; + + for (i = 0; start_derives[i] >= 0; ++i) + p->items[i] = rrhs[start_derives[i]]; + + first_state = last_state = this_state = p; + nstates = 1; +} + + +new_itemsets() +{ + register int i; + register int shiftcount; + register short *isp; + register short *ksp; + register int symbol; + + for (i = 0; i < nsyms; i++) + kernel_end[i] = 0; + + shiftcount = 0; + isp = itemset; + while (isp < itemsetend) + { + i = *isp++; + symbol = ritem[i]; + if (symbol > 0) + { + ksp = kernel_end[symbol]; + if (!ksp) + { + shift_symbol[shiftcount++] = symbol; + ksp = kernel_base[symbol]; + } + + *ksp++ = i + 1; + kernel_end[symbol] = ksp; + } + } + + nshifts = shiftcount; +} + + + +core * +new_state(symbol) +int symbol; +{ + register int n; + register core *p; + register short *isp1; + register short *isp2; + register short *iend; + +#ifdef TRACE + fprintf(stderr, "Entering new_state(%d)\n", symbol); +#endif + + if (nstates >= MAXSHORT) + fatal("too many states"); + + isp1 = kernel_base[symbol]; + iend = kernel_end[symbol]; + n = iend - isp1; + + p = (core *) allocate((unsigned) (sizeof(core) + (n - 1) * sizeof(short))); + p->accessing_symbol = symbol; + p->number = nstates; + p->nitems = n; + + isp2 = p->items; + while (isp1 < iend) + *isp2++ = *isp1++; + + last_state->next = p; + last_state = p; + + nstates++; + + return (p); +} + + +/* show_cores is used for debugging */ + +show_cores() +{ + core *p; + int i, j, k, n; + int itemno; + + k = 0; + for (p = first_state; p; ++k, p = p->next) + { + if (k) printf("\n"); + printf("state %d, number = %d, accessing symbol = %s\n", + k, p->number, symbol_name[p->accessing_symbol]); + n = p->nitems; + for (i = 0; i < n; ++i) + { + itemno = p->items[i]; + printf("%4d ", itemno); + j = itemno; + while (ritem[j] >= 0) ++j; + printf("%s :", symbol_name[rlhs[-ritem[j]]]); + j = rrhs[-ritem[j]]; + while (j < itemno) + printf(" %s", symbol_name[ritem[j++]]); + printf(" ."); + while (ritem[j] >= 0) + printf(" %s", symbol_name[ritem[j++]]); + printf("\n"); + fflush(stdout); + } + } +} + + +/* show_ritems is used for debugging */ + +show_ritems() +{ + int i; + + for (i = 0; i < nitems; ++i) + printf("ritem[%d] = %d\n", i, ritem[i]); +} + + +/* show_rrhs is used for debugging */ +show_rrhs() +{ + int i; + + for (i = 0; i < nrules; ++i) + printf("rrhs[%d] = %d\n", i, rrhs[i]); +} + + +/* show_shifts is used for debugging */ + +show_shifts() +{ + shifts *p; + int i, j, k; + + k = 0; + for (p = first_shift; p; ++k, p = p->next) + { + if (k) printf("\n"); + printf("shift %d, number = %d, nshifts = %d\n", k, p->number, + p->nshifts); + j = p->nshifts; + for (i = 0; i < j; ++i) + printf("\t%d\n", p->shift[i]); + } +} + + +save_shifts() +{ + register shifts *p; + register short *sp1; + register short *sp2; + register short *send; + + p = (shifts *) allocate((unsigned) (sizeof(shifts) + + (nshifts - 1) * sizeof(short))); + + p->number = this_state->number; + p->nshifts = nshifts; + + sp1 = shiftset; + sp2 = p->shift; + send = shiftset + nshifts; + + while (sp1 < send) + *sp2++ = *sp1++; + + if (last_shift) + { + last_shift->next = p; + last_shift = p; + } + else + { + first_shift = p; + last_shift = p; + } +} + + + +save_reductions() +{ + register short *isp; + register short *rp1; + register short *rp2; + register int item; + register int count; + register reductions *p; + register short *rend; + + count = 0; + for (isp = itemset; isp < itemsetend; isp++) + { + item = ritem[*isp]; + if (item < 0) + { + redset[count++] = -item; + } + } + + if (count) + { + p = (reductions *) allocate((unsigned) (sizeof(reductions) + + (count - 1) * sizeof(short))); + + p->number = this_state->number; + p->nreds = count; + + rp1 = redset; + rp2 = p->rules; + rend = rp1 + count; + + while (rp1 < rend) + *rp2++ = *rp1++; + + if (last_reduction) + { + last_reduction->next = p; + last_reduction = p; + } + else + { + first_reduction = p; + last_reduction = p; + } + } +} + + +set_derives() +{ + register int i, k; + register int lhs; + register short *rules; + + derives = NEW2(nsyms, short *); + rules = NEW2(nvars + nrules, short); + + k = 0; + for (lhs = start_symbol; lhs < nsyms; lhs++) + { + derives[lhs] = rules + k; + for (i = 0; i < nrules; i++) + { + if (rlhs[i] == lhs) + { + rules[k] = i; + k++; + } + } + rules[k] = -1; + k++; + } + +#ifdef DEBUG + print_derives(); +#endif +} + +free_derives() +{ + FREE(derives[start_symbol]); + FREE(derives); +} + +#ifdef DEBUG +print_derives() +{ + register int i; + register short *sp; + + printf("\nDERIVES\n\n"); + + for (i = start_symbol; i < nsyms; i++) + { + printf("%s derives ", symbol_name[i]); + for (sp = derives[i]; *sp >= 0; sp++) + { + printf(" %d", *sp); + } + putchar('\n'); + } + + putchar('\n'); +} +#endif + + +set_nullable() +{ + register int i, j; + register int empty; + int done; + + nullable = MALLOC(nsyms); + if (nullable == 0) no_space(); + + for (i = 0; i < nsyms; ++i) + nullable[i] = 0; + + done = 0; + while (!done) + { + done = 1; + for (i = 1; i < nitems; i++) + { + empty = 1; + while ((j = ritem[i]) >= 0) + { + if (!nullable[j]) + empty = 0; + ++i; + } + if (empty) + { + j = rlhs[-j]; + if (!nullable[j]) + { + nullable[j] = 1; + done = 0; + } + } + } + } + +#ifdef DEBUG + for (i = 0; i < nsyms; i++) + { + if (nullable[i]) + printf("%s is nullable\n", symbol_name[i]); + else + printf("%s is not nullable\n", symbol_name[i]); + } +#endif +} + + +free_nullable() +{ + FREE(nullable); +} + + +lr0() +{ + set_derives(); + set_nullable(); + generate_states(); +} diff --git a/commands/byacc/main.c b/commands/byacc/main.c new file mode 100755 index 000000000..e6d0d7ead --- /dev/null +++ b/commands/byacc/main.c @@ -0,0 +1,378 @@ +#include +#include +#include "defs.h" + +char dflag; +char lflag; +char rflag; +char tflag; +char vflag; + +char *symbol_prefix; +char *file_prefix = "y"; +char *myname = "yacc"; +char *temp_form = "yacc.XXXXXXX"; + +int lineno; +int outline; + +char *action_file_name; +char *code_file_name; +char *defines_file_name; +char *input_file_name = ""; +char *output_file_name; +char *text_file_name; +char *union_file_name; +char *verbose_file_name; + +FILE *action_file; /* a temp file, used to save actions associated */ + /* with rules until the parser is written */ +FILE *code_file; /* y.code.c (used when the -r option is specified) */ +FILE *defines_file; /* y.tab.h */ +FILE *input_file; /* the input file */ +FILE *output_file; /* y.tab.c */ +FILE *text_file; /* a temp file, used to save text until all */ + /* symbols have been defined */ +FILE *union_file; /* a temp file, used to save the union */ + /* definition until all symbol have been */ + /* defined */ +FILE *verbose_file; /* y.output */ + +int nitems; +int nrules; +int nsyms; +int ntokens; +int nvars; + +int start_symbol; +char **symbol_name; +short *symbol_value; +short *symbol_prec; +char *symbol_assoc; + +short *ritem; +short *rlhs; +short *rrhs; +short *rprec; +char *rassoc; +short **derives; +char *nullable; + +extern char *mktemp(); +extern char *getenv(); + + +done(k) +int k; +{ + if (action_file) { fclose(action_file); unlink(action_file_name); } + if (text_file) { fclose(text_file); unlink(text_file_name); } + if (union_file) { fclose(union_file); unlink(union_file_name); } + exit(k); +} + + +void +onintr(signo) + int signo; +{ + done(1); +} + + +set_signals() +{ +#ifdef SIGINT + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, onintr); +#endif +#ifdef SIGTERM + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) + signal(SIGTERM, onintr); +#endif +#ifdef SIGHUP + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + signal(SIGHUP, onintr); +#endif +} + + +usage() +{ + fprintf(stderr, "usage: %s [-dlrtv] [-b file_prefix] [-p symbol_prefix] filename\n", myname); + exit(1); +} + + +getargs(argc, argv) +int argc; +char *argv[]; +{ + register int i; + register char *s; + + if (argc > 0) myname = argv[0]; + for (i = 1; i < argc; ++i) + { + s = argv[i]; + if (*s != '-') break; + switch (*++s) + { + case '\0': + input_file = stdin; + if (i + 1 < argc) usage(); + return; + + case '-': + ++i; + goto no_more_options; + + case 'b': + if (*++s) + file_prefix = s; + else if (++i < argc) + file_prefix = argv[i]; + else + usage(); + continue; + + case 'd': + dflag = 1; + break; + + case 'l': + lflag = 1; + break; + + case 'p': + if (*++s) + symbol_prefix = s; + else if (++i < argc) + symbol_prefix = argv[i]; + else + usage(); + continue; + + case 'r': + rflag = 1; + break; + + case 't': + tflag = 1; + break; + + case 'v': + vflag = 1; + break; + + default: + usage(); + } + + for (;;) + { + switch (*++s) + { + case '\0': + goto end_of_option; + + case 'd': + dflag = 1; + break; + + case 'l': + lflag = 1; + break; + + case 'r': + rflag = 1; + break; + + case 't': + tflag = 1; + break; + + case 'v': + vflag = 1; + break; + + default: + usage(); + } + } +end_of_option:; + } + +no_more_options:; + if (i + 1 != argc) usage(); + input_file_name = argv[i]; +} + + +char * +allocate(n) +unsigned n; +{ + register char *p; + + p = NULL; + if (n) + { + p = CALLOC(1, n); + if (!p) no_space(); + } + return (p); +} + + +create_file_names() +{ + int i, len; + char *tmpdir; + + tmpdir = getenv("TMPDIR"); + if (tmpdir == 0) tmpdir = "/tmp"; + + len = strlen(tmpdir); + i = len + 13; + if (len && tmpdir[len-1] != '/') + ++i; + + action_file_name = MALLOC(i); + if (action_file_name == 0) no_space(); + text_file_name = MALLOC(i); + if (text_file_name == 0) no_space(); + union_file_name = MALLOC(i); + if (union_file_name == 0) no_space(); + + strcpy(action_file_name, tmpdir); + strcpy(text_file_name, tmpdir); + strcpy(union_file_name, tmpdir); + + if (len && tmpdir[len - 1] != '/') + { + action_file_name[len] = '/'; + text_file_name[len] = '/'; + union_file_name[len] = '/'; + ++len; + } + + strcpy(action_file_name + len, temp_form); + strcpy(text_file_name + len, temp_form); + strcpy(union_file_name + len, temp_form); + + action_file_name[len + 5] = 'a'; + text_file_name[len + 5] = 't'; + union_file_name[len + 5] = 'u'; + + mktemp(action_file_name); + mktemp(text_file_name); + mktemp(union_file_name); + + len = strlen(file_prefix); + + output_file_name = MALLOC(len + 7); + if (output_file_name == 0) + no_space(); + strcpy(output_file_name, file_prefix); + strcpy(output_file_name + len, OUTPUT_SUFFIX); + + if (rflag) + { + code_file_name = MALLOC(len + 8); + if (code_file_name == 0) + no_space(); + strcpy(code_file_name, file_prefix); + strcpy(code_file_name + len, CODE_SUFFIX); + } + else + code_file_name = output_file_name; + + if (dflag) + { + defines_file_name = MALLOC(len + 7); + if (defines_file_name == 0) + no_space(); + strcpy(defines_file_name, file_prefix); + strcpy(defines_file_name + len, DEFINES_SUFFIX); + } + + if (vflag) + { + verbose_file_name = MALLOC(len + 8); + if (verbose_file_name == 0) + no_space(); + strcpy(verbose_file_name, file_prefix); + strcpy(verbose_file_name + len, VERBOSE_SUFFIX); + } +} + + +open_files() +{ + create_file_names(); + + if (input_file == 0) + { + input_file = fopen(input_file_name, "r"); + if (input_file == 0) + open_error(input_file_name); + } + + action_file = fopen(action_file_name, "w"); + if (action_file == 0) + open_error(action_file_name); + + text_file = fopen(text_file_name, "w"); + if (text_file == 0) + open_error(text_file_name); + + if (vflag) + { + verbose_file = fopen(verbose_file_name, "w"); + if (verbose_file == 0) + open_error(verbose_file_name); + } + + if (dflag) + { + defines_file = fopen(defines_file_name, "w"); + if (defines_file == 0) + open_error(defines_file_name); + union_file = fopen(union_file_name, "w"); + if (union_file == 0) + open_error(union_file_name); + } + + output_file = fopen(output_file_name, "w"); + if (output_file == 0) + open_error(output_file_name); + + if (rflag) + { + code_file = fopen(code_file_name, "w"); + if (code_file == 0) + open_error(code_file_name); + } + else + code_file = output_file; +} + + +int +main(argc, argv) +int argc; +char *argv[]; +{ + set_signals(); + getargs(argc, argv); + open_files(); + reader(); + lr0(); + lalr(); + make_parser(); + verbose(); + output(); + done(0); + /*NOTREACHED*/ +} diff --git a/commands/byacc/mkpar.c b/commands/byacc/mkpar.c new file mode 100755 index 000000000..e1aef60fe --- /dev/null +++ b/commands/byacc/mkpar.c @@ -0,0 +1,357 @@ + +#include "defs.h" + +action **parser; +int SRtotal; +int RRtotal; +short *SRconflicts; +short *RRconflicts; +short *defred; +short *rules_used; +short nunused; +short final_state; + +static int SRcount; +static int RRcount; + +extern action *parse_actions(); +extern action *get_shifts(); +extern action *add_reductions(); +extern action *add_reduce(); + + +make_parser() +{ + register int i; + + parser = NEW2(nstates, action *); + for (i = 0; i < nstates; i++) + parser[i] = parse_actions(i); + + find_final_state(); + remove_conflicts(); + unused_rules(); + if (SRtotal + RRtotal > 0) total_conflicts(); + defreds(); +} + + +action * +parse_actions(stateno) +register int stateno; +{ + register action *actions; + + actions = get_shifts(stateno); + actions = add_reductions(stateno, actions); + return (actions); +} + + +action * +get_shifts(stateno) +int stateno; +{ + register action *actions, *temp; + register shifts *sp; + register short *to_state; + register int i, k; + register int symbol; + + actions = 0; + sp = shift_table[stateno]; + if (sp) + { + to_state = sp->shift; + for (i = sp->nshifts - 1; i >= 0; i--) + { + k = to_state[i]; + symbol = accessing_symbol[k]; + if (ISTOKEN(symbol)) + { + temp = NEW(action); + temp->next = actions; + temp->symbol = symbol; + temp->number = k; + temp->prec = symbol_prec[symbol]; + temp->action_code = SHIFT; + temp->assoc = symbol_assoc[symbol]; + actions = temp; + } + } + } + return (actions); +} + +action * +add_reductions(stateno, actions) +int stateno; +register action *actions; +{ + register int i, j, m, n; + register int ruleno, tokensetsize; + register unsigned *rowp; + + tokensetsize = WORDSIZE(ntokens); + m = lookaheads[stateno]; + n = lookaheads[stateno + 1]; + for (i = m; i < n; i++) + { + ruleno = LAruleno[i]; + rowp = LA + i * tokensetsize; + for (j = ntokens - 1; j >= 0; j--) + { + if (BIT(rowp, j)) + actions = add_reduce(actions, ruleno, j); + } + } + return (actions); +} + + +action * +add_reduce(actions, ruleno, symbol) +register action *actions; +register int ruleno, symbol; +{ + register action *temp, *prev, *next; + + prev = 0; + for (next = actions; next && next->symbol < symbol; next = next->next) + prev = next; + + while (next && next->symbol == symbol && next->action_code == SHIFT) + { + prev = next; + next = next->next; + } + + while (next && next->symbol == symbol && + next->action_code == REDUCE && next->number < ruleno) + { + prev = next; + next = next->next; + } + + temp = NEW(action); + temp->next = next; + temp->symbol = symbol; + temp->number = ruleno; + temp->prec = rprec[ruleno]; + temp->action_code = REDUCE; + temp->assoc = rassoc[ruleno]; + + if (prev) + prev->next = temp; + else + actions = temp; + + return (actions); +} + + +find_final_state() +{ + register int goal, i; + register short *to_state; + register shifts *p; + + p = shift_table[0]; + to_state = p->shift; + goal = ritem[1]; + for (i = p->nshifts - 1; i >= 0; --i) + { + final_state = to_state[i]; + if (accessing_symbol[final_state] == goal) break; + } +} + + +unused_rules() +{ + register int i; + register action *p; + + rules_used = (short *) MALLOC(nrules*sizeof(short)); + if (rules_used == 0) no_space(); + + for (i = 0; i < nrules; ++i) + rules_used[i] = 0; + + for (i = 0; i < nstates; ++i) + { + for (p = parser[i]; p; p = p->next) + { + if (p->action_code == REDUCE && p->suppressed == 0) + rules_used[p->number] = 1; + } + } + + nunused = 0; + for (i = 3; i < nrules; ++i) + if (!rules_used[i]) ++nunused; + + if (nunused) + if (nunused == 1) + fprintf(stderr, "%s: 1 rule never reduced\n", myname); + else + fprintf(stderr, "%s: %d rules never reduced\n", myname, nunused); +} + + +remove_conflicts() +{ + register int i; + register int symbol; + register action *p, *pref; + + SRtotal = 0; + RRtotal = 0; + SRconflicts = NEW2(nstates, short); + RRconflicts = NEW2(nstates, short); + for (i = 0; i < nstates; i++) + { + SRcount = 0; + RRcount = 0; + symbol = -1; + for (p = parser[i]; p; p = p->next) + { + if (p->symbol != symbol) + { + pref = p; + symbol = p->symbol; + } + else if (i == final_state && symbol == 0) + { + SRcount++; + p->suppressed = 1; + } + else if (pref->action_code == SHIFT) + { + if (pref->prec > 0 && p->prec > 0) + { + if (pref->prec < p->prec) + { + pref->suppressed = 2; + pref = p; + } + else if (pref->prec > p->prec) + { + p->suppressed = 2; + } + else if (pref->assoc == LEFT) + { + pref->suppressed = 2; + pref = p; + } + else if (pref->assoc == RIGHT) + { + p->suppressed = 2; + } + else + { + pref->suppressed = 2; + p->suppressed = 2; + } + } + else + { + SRcount++; + p->suppressed = 1; + } + } + else + { + RRcount++; + p->suppressed = 1; + } + } + SRtotal += SRcount; + RRtotal += RRcount; + SRconflicts[i] = SRcount; + RRconflicts[i] = RRcount; + } +} + + +total_conflicts() +{ + fprintf(stderr, "%s: ", myname); + if (SRtotal == 1) + fprintf(stderr, "1 shift/reduce conflict"); + else if (SRtotal > 1) + fprintf(stderr, "%d shift/reduce conflicts", SRtotal); + + if (SRtotal && RRtotal) + fprintf(stderr, ", "); + + if (RRtotal == 1) + fprintf(stderr, "1 reduce/reduce conflict"); + else if (RRtotal > 1) + fprintf(stderr, "%d reduce/reduce conflicts", RRtotal); + + fprintf(stderr, ".\n"); +} + + +int +sole_reduction(stateno) +int stateno; +{ + register int count, ruleno; + register action *p; + + count = 0; + ruleno = 0; + for (p = parser[stateno]; p; p = p->next) + { + if (p->action_code == SHIFT && p->suppressed == 0) + return (0); + else if (p->action_code == REDUCE && p->suppressed == 0) + { + if (ruleno > 0 && p->number != ruleno) + return (0); + if (p->symbol != 1) + ++count; + ruleno = p->number; + } + } + + if (count == 0) + return (0); + return (ruleno); +} + + +defreds() +{ + register int i; + + defred = NEW2(nstates, short); + for (i = 0; i < nstates; i++) + defred[i] = sole_reduction(i); +} + +free_action_row(p) +register action *p; +{ + register action *q; + + while (p) + { + q = p->next; + FREE(p); + p = q; + } +} + +free_parser() +{ + register int i; + + for (i = 0; i < nstates; i++) + free_action_row(parser[i]); + + FREE(parser); +} + diff --git a/commands/byacc/output.c b/commands/byacc/output.c new file mode 100755 index 000000000..6e4bb79c5 --- /dev/null +++ b/commands/byacc/output.c @@ -0,0 +1,1210 @@ +#include "defs.h" + +static int nvectors; +static int nentries; +static short **froms; +static short **tos; +static short *tally; +static short *width; +static short *state_count; +static short *order; +static short *base; +static short *pos; +static int maxtable; +static short *table; +static short *check; +static int lowzero; +static int high; + + +output() +{ + free_itemsets(); + free_shifts(); + free_reductions(); + output_prefix(); + output_stored_text(); + output_defines(); + output_rule_data(); + output_yydefred(); + output_actions(); + free_parser(); + output_debug(); + output_stype(); + if (rflag) write_section(tables); + write_section(header); + output_trailing_text(); + write_section(body); + output_semantic_actions(); + write_section(trailer); +} + + +output_prefix() +{ + if (symbol_prefix == NULL) + symbol_prefix = "yy"; + else + { + ++outline; + fprintf(code_file, "#define yyparse %sparse\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylex %slex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyerror %serror\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yychar %schar\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyval %sval\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylval %slval\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yydebug %sdebug\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yynerrs %snerrs\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyerrflag %serrflag\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyss %sss\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyssp %sssp\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyvs %svs\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyvsp %svsp\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylhs %slhs\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yylen %slen\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yydefred %sdefred\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yydgoto %sdgoto\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yysindex %ssindex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyrindex %srindex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yygindex %sgindex\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yytable %stable\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yycheck %scheck\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyname %sname\n", symbol_prefix); + ++outline; + fprintf(code_file, "#define yyrule %srule\n", symbol_prefix); + } + ++outline; + fprintf(code_file, "#define YYPREFIX \"%s\"\n", symbol_prefix); +} + + +output_rule_data() +{ + register int i; + register int j; + + + fprintf(output_file, "short %slhs[] = {%42d,", symbol_prefix, + symbol_value[start_symbol]); + + j = 10; + for (i = 3; i < nrules; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", symbol_value[rlhs[i]]); + } + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + + fprintf(output_file, "short %slen[] = {%42d,", symbol_prefix, 2); + + j = 10; + for (i = 3; i < nrules; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + j++; + + fprintf(output_file, "%5d,", rrhs[i + 1] - rrhs[i] - 1); + } + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); +} + + +output_yydefred() +{ + register int i, j; + + fprintf(output_file, "short %sdefred[] = {%39d,", symbol_prefix, + (defred[0] ? defred[0] - 2 : 0)); + + j = 10; + for (i = 1; i < nstates; i++) + { + if (j < 10) + ++j; + else + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + + fprintf(output_file, "%5d,", (defred[i] ? defred[i] - 2 : 0)); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); +} + + +output_actions() +{ + nvectors = 2*nstates + nvars; + + froms = NEW2(nvectors, short *); + tos = NEW2(nvectors, short *); + tally = NEW2(nvectors, short); + width = NEW2(nvectors, short); + + token_actions(); + FREE(lookaheads); + FREE(LA); + FREE(LAruleno); + FREE(accessing_symbol); + + goto_actions(); + FREE(goto_map + ntokens); + FREE(from_state); + FREE(to_state); + + sort_actions(); + pack_table(); + output_base(); + output_table(); + output_check(); +} + + +token_actions() +{ + register int i, j; + register int shiftcount, reducecount; + register int max, min; + register short *actionrow, *r, *s; + register action *p; + + actionrow = NEW2(2*ntokens, short); + for (i = 0; i < nstates; ++i) + { + if (parser[i]) + { + for (j = 0; j < 2*ntokens; ++j) + actionrow[j] = 0; + + shiftcount = 0; + reducecount = 0; + for (p = parser[i]; p; p = p->next) + { + if (p->suppressed == 0) + { + if (p->action_code == SHIFT) + { + ++shiftcount; + actionrow[p->symbol] = p->number; + } + else if (p->action_code == REDUCE && p->number != defred[i]) + { + ++reducecount; + actionrow[p->symbol + ntokens] = p->number; + } + } + } + + tally[i] = shiftcount; + tally[nstates+i] = reducecount; + width[i] = 0; + width[nstates+i] = 0; + if (shiftcount > 0) + { + froms[i] = r = NEW2(shiftcount, short); + tos[i] = s = NEW2(shiftcount, short); + min = MAXSHORT; + max = 0; + for (j = 0; j < ntokens; ++j) + { + if (actionrow[j]) + { + if (min > symbol_value[j]) + min = symbol_value[j]; + if (max < symbol_value[j]) + max = symbol_value[j]; + *r++ = symbol_value[j]; + *s++ = actionrow[j]; + } + } + width[i] = max - min + 1; + } + if (reducecount > 0) + { + froms[nstates+i] = r = NEW2(reducecount, short); + tos[nstates+i] = s = NEW2(reducecount, short); + min = MAXSHORT; + max = 0; + for (j = 0; j < ntokens; ++j) + { + if (actionrow[ntokens+j]) + { + if (min > symbol_value[j]) + min = symbol_value[j]; + if (max < symbol_value[j]) + max = symbol_value[j]; + *r++ = symbol_value[j]; + *s++ = actionrow[ntokens+j] - 2; + } + } + width[nstates+i] = max - min + 1; + } + } + } + FREE(actionrow); +} + +goto_actions() +{ + register int i, j, k; + + state_count = NEW2(nstates, short); + + k = default_goto(start_symbol + 1); + fprintf(output_file, "short %sdgoto[] = {%40d,", symbol_prefix, k); + save_column(start_symbol + 1, k); + + j = 10; + for (i = start_symbol + 2; i < nsyms; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + k = default_goto(i); + fprintf(output_file, "%5d,", k); + save_column(i, k); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(state_count); +} + +int +default_goto(symbol) +int symbol; +{ + register int i; + register int m; + register int n; + register int default_state; + register int max; + + m = goto_map[symbol]; + n = goto_map[symbol + 1]; + + if (m == n) return (0); + + for (i = 0; i < nstates; i++) + state_count[i] = 0; + + for (i = m; i < n; i++) + state_count[to_state[i]]++; + + max = 0; + default_state = 0; + for (i = 0; i < nstates; i++) + { + if (state_count[i] > max) + { + max = state_count[i]; + default_state = i; + } + } + + return (default_state); +} + + + +save_column(symbol, default_state) +int symbol; +int default_state; +{ + register int i; + register int m; + register int n; + register short *sp; + register short *sp1; + register short *sp2; + register int count; + register int symno; + + m = goto_map[symbol]; + n = goto_map[symbol + 1]; + + count = 0; + for (i = m; i < n; i++) + { + if (to_state[i] != default_state) + ++count; + } + if (count == 0) return; + + symno = symbol_value[symbol] + 2*nstates; + + froms[symno] = sp1 = sp = NEW2(count, short); + tos[symno] = sp2 = NEW2(count, short); + + for (i = m; i < n; i++) + { + if (to_state[i] != default_state) + { + *sp1++ = from_state[i]; + *sp2++ = to_state[i]; + } + } + + tally[symno] = count; + width[symno] = sp1[-1] - sp[0] + 1; +} + +sort_actions() +{ + register int i; + register int j; + register int k; + register int t; + register int w; + + order = NEW2(nvectors, short); + nentries = 0; + + for (i = 0; i < nvectors; i++) + { + if (tally[i] > 0) + { + t = tally[i]; + w = width[i]; + j = nentries - 1; + + while (j >= 0 && (width[order[j]] < w)) + j--; + + while (j >= 0 && (width[order[j]] == w) && (tally[order[j]] < t)) + j--; + + for (k = nentries - 1; k > j; k--) + order[k + 1] = order[k]; + + order[j + 1] = i; + nentries++; + } + } +} + + +pack_table() +{ + register int i; + register int place; + register int state; + + base = NEW2(nvectors, short); + pos = NEW2(nentries, short); + + maxtable = BITS_PER_WORD == 16 ? 400 : 1000; + table = NEW2(maxtable, short); + check = NEW2(maxtable, short); + + lowzero = 0; + high = 0; + + for (i = 0; i < maxtable; i++) + check[i] = -1; + + for (i = 0; i < nentries; i++) + { + state = matching_vector(i); + + if (state < 0) + place = pack_vector(i); + else + place = base[state]; + + pos[i] = place; + base[order[i]] = place; + } + + for (i = 0; i < nvectors; i++) + { + if (froms[i]) + FREE(froms[i]); + if (tos[i]) + FREE(tos[i]); + } + + FREE(froms); + FREE(tos); + FREE(pos); +} + + +/* The function matching_vector determines if the vector specified by */ +/* the input parameter matches a previously considered vector. The */ +/* test at the start of the function checks if the vector represents */ +/* a row of shifts over terminal symbols or a row of reductions, or a */ +/* column of shifts over a nonterminal symbol. Berkeley Yacc does not */ +/* check if a column of shifts over a nonterminal symbols matches a */ +/* previously considered vector. Because of the nature of LR parsing */ +/* tables, no two columns can match. Therefore, the only possible */ +/* match would be between a row and a column. Such matches are */ +/* unlikely. Therefore, to save time, no attempt is made to see if a */ +/* column matches a previously considered vector. */ +/* */ +/* Matching_vector is poorly designed. The test could easily be made */ +/* faster. Also, it depends on the vectors being in a specific */ +/* order. */ + +int +matching_vector(vector) +int vector; +{ + register int i; + register int j; + register int k; + register int t; + register int w; + register int match; + register int prev; + + i = order[vector]; + if (i >= 2*nstates) + return (-1); + + t = tally[i]; + w = width[i]; + + for (prev = vector - 1; prev >= 0; prev--) + { + j = order[prev]; + if (width[j] != w || tally[j] != t) + return (-1); + + match = 1; + for (k = 0; match && k < t; k++) + { + if (tos[j][k] != tos[i][k] || froms[j][k] != froms[i][k]) + match = 0; + } + + if (match) + return (j); + } + + return (-1); +} + + + +int +pack_vector(vector) +int vector; +{ + register int i, j, k, l; + register int t; + register int loc; + register int ok; + register short *from; + register short *to; + int newmax; + + i = order[vector]; + t = tally[i]; + assert(t); + + from = froms[i]; + to = tos[i]; + + j = lowzero - from[0]; + for (k = 1; k < t; ++k) + if (lowzero - from[k] > j) + j = lowzero - from[k]; + for (;; ++j) + { + if (j == 0) + continue; + ok = 1; + for (k = 0; ok && k < t; k++) + { + loc = j + from[k]; + if (loc >= maxtable) + { + if (loc >= MAXTABLE) + fatal("maximum table size exceeded"); + + newmax = maxtable; + do { newmax += 200; } while (newmax <= loc); + table = (short *) REALLOC(table, newmax*sizeof(short)); + if (table == 0) no_space(); + check = (short *) REALLOC(check, newmax*sizeof(short)); + if (check == 0) no_space(); + for (l = maxtable; l < newmax; ++l) + { + table[l] = 0; + check[l] = -1; + } + maxtable = newmax; + } + + if (check[loc] != -1) + ok = 0; + } + for (k = 0; ok && k < vector; k++) + { + if (pos[k] == j) + ok = 0; + } + if (ok) + { + for (k = 0; k < t; k++) + { + loc = j + from[k]; + table[loc] = to[k]; + check[loc] = from[k]; + if (loc > high) high = loc; + } + + while (check[lowzero] != -1) + ++lowzero; + + return (j); + } + } +} + + + +output_base() +{ + register int i, j; + + fprintf(output_file, "short %ssindex[] = {%39d,", symbol_prefix, base[0]); + + j = 10; + for (i = 1; i < nstates; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", base[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\nshort %srindex[] = {%39d,", symbol_prefix, + base[nstates]); + + j = 10; + for (i = nstates + 1; i < 2*nstates; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", base[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\nshort %sgindex[] = {%39d,", symbol_prefix, + base[2*nstates]); + + j = 10; + for (i = 2*nstates + 1; i < nvectors - 1; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", base[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(base); +} + + + +output_table() +{ + register int i; + register int j; + + ++outline; + fprintf(code_file, "#define YYTABLESIZE %d\n", high); + fprintf(output_file, "short %stable[] = {%40d,", symbol_prefix, + table[0]); + + j = 10; + for (i = 1; i <= high; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", table[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(table); +} + + + +output_check() +{ + register int i; + register int j; + + fprintf(output_file, "short %scheck[] = {%40d,", symbol_prefix, + check[0]); + + j = 10; + for (i = 1; i <= high; i++) + { + if (j >= 10) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 1; + } + else + ++j; + + fprintf(output_file, "%5d,", check[i]); + } + + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(check); +} + + +int +is_C_identifier(name) +char *name; +{ + register char *s; + register int c; + + s = name; + c = *s; + if (c == '"') + { + c = *++s; + if (!isalpha(c) && c != '_' && c != '$') + return (0); + while ((c = *++s) != '"') + { + if (!isalnum(c) && c != '_' && c != '$') + return (0); + } + return (1); + } + + if (!isalpha(c) && c != '_' && c != '$') + return (0); + while (c = *++s) + { + if (!isalnum(c) && c != '_' && c != '$') + return (0); + } + return (1); +} + + +output_defines() +{ + register int c, i; + register char *s; + + for (i = 2; i < ntokens; ++i) + { + s = symbol_name[i]; + if (is_C_identifier(s)) + { + fprintf(code_file, "#define "); + if (dflag) fprintf(defines_file, "#define "); + c = *s; + if (c == '"') + { + while ((c = *++s) != '"') + { + putc(c, code_file); + if (dflag) putc(c, defines_file); + } + } + else + { + do + { + putc(c, code_file); + if (dflag) putc(c, defines_file); + } + while (c = *++s); + } + ++outline; + fprintf(code_file, " %d\n", symbol_value[i]); + if (dflag) fprintf(defines_file, " %d\n", symbol_value[i]); + } + } + + ++outline; + fprintf(code_file, "#define YYERRCODE %d\n", symbol_value[1]); + + if (dflag && unionized) + { + fclose(union_file); + union_file = fopen(union_file_name, "r"); + if (union_file == NULL) open_error(union_file_name); + while ((c = getc(union_file)) != EOF) + putc(c, defines_file); + fprintf(defines_file, " YYSTYPE;\nextern YYSTYPE %slval;\n", + symbol_prefix); + } +} + + +output_stored_text() +{ + register int c; + register FILE *in, *out; + + fclose(text_file); + text_file = fopen(text_file_name, "r"); + if (text_file == NULL) + open_error(text_file_name); + in = text_file; + if ((c = getc(in)) == EOF) + return; + out = code_file; + if (c == '\n') + ++outline; + putc(c, out); + while ((c = getc(in)) != EOF) + { + if (c == '\n') + ++outline; + putc(c, out); + } + if (!lflag) + fprintf(out, line_format, ++outline + 1, code_file_name); +} + + +output_debug() +{ + register int i, j, k, max; + char **symnam, *s; + + ++outline; + fprintf(code_file, "#define YYFINAL %d\n", final_state); + outline += 3; + fprintf(code_file, "#ifndef YYDEBUG\n#define YYDEBUG %d\n#endif\n", + tflag); + if (rflag) + fprintf(output_file, "#ifndef YYDEBUG\n#define YYDEBUG %d\n#endif\n", + tflag); + + max = 0; + for (i = 2; i < ntokens; ++i) + if (symbol_value[i] > max) + max = symbol_value[i]; + ++outline; + fprintf(code_file, "#define YYMAXTOKEN %d\n", max); + + symnam = (char **) MALLOC((max+1)*sizeof(char *)); + if (symnam == 0) no_space(); + + /* Note that it is not necessary to initialize the element */ + /* symnam[max]. */ + for (i = 0; i < max; ++i) + symnam[i] = 0; + for (i = ntokens - 1; i >= 2; --i) + symnam[symbol_value[i]] = symbol_name[i]; + symnam[0] = "end-of-file"; + + if (!rflag) ++outline; + fprintf(output_file, "#if YYDEBUG\nchar *%sname[] = {", symbol_prefix); + j = 80; + for (i = 0; i <= max; ++i) + { + if (s = symnam[i]) + { + if (s[0] == '"') + { + k = 7; + while (*++s != '"') + { + ++k; + if (*s == '\\') + { + k += 2; + if (*++s == '\\') + ++k; + } + } + j += k; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = k; + } + fprintf(output_file, "\"\\\""); + s = symnam[i]; + while (*++s != '"') + { + if (*s == '\\') + { + fprintf(output_file, "\\\\"); + if (*++s == '\\') + fprintf(output_file, "\\\\"); + else + putc(*s, output_file); + } + else + putc(*s, output_file); + } + fprintf(output_file, "\\\"\","); + } + else if (s[0] == '\'') + { + if (s[1] == '"') + { + j += 7; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 7; + } + fprintf(output_file, "\"'\\\"'\","); + } + else + { + k = 5; + while (*++s != '\'') + { + ++k; + if (*s == '\\') + { + k += 2; + if (*++s == '\\') + ++k; + } + } + j += k; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = k; + } + fprintf(output_file, "\"'"); + s = symnam[i]; + while (*++s != '\'') + { + if (*s == '\\') + { + fprintf(output_file, "\\\\"); + if (*++s == '\\') + fprintf(output_file, "\\\\"); + else + putc(*s, output_file); + } + else + putc(*s, output_file); + } + fprintf(output_file, "'\","); + } + } + else + { + k = strlen(s) + 3; + j += k; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = k; + } + putc('"', output_file); + do { putc(*s, output_file); } while (*++s); + fprintf(output_file, "\","); + } + } + else + { + j += 2; + if (j > 80) + { + if (!rflag) ++outline; + putc('\n', output_file); + j = 2; + } + fprintf(output_file, "0,"); + } + } + if (!rflag) outline += 2; + fprintf(output_file, "\n};\n"); + FREE(symnam); + + if (!rflag) ++outline; + fprintf(output_file, "char *%srule[] = {\n", symbol_prefix); + for (i = 2; i < nrules; ++i) + { + fprintf(output_file, "\"%s :", symbol_name[rlhs[i]]); + for (j = rrhs[i]; ritem[j] > 0; ++j) + { + s = symbol_name[ritem[j]]; + if (s[0] == '"') + { + fprintf(output_file, " \\\""); + while (*++s != '"') + { + if (*s == '\\') + { + if (s[1] == '\\') + fprintf(output_file, "\\\\\\\\"); + else + fprintf(output_file, "\\\\%c", s[1]); + ++s; + } + else + putc(*s, output_file); + } + fprintf(output_file, "\\\""); + } + else if (s[0] == '\'') + { + if (s[1] == '"') + fprintf(output_file, " '\\\"'"); + else if (s[1] == '\\') + { + if (s[2] == '\\') + fprintf(output_file, " '\\\\\\\\"); + else + fprintf(output_file, " '\\\\%c", s[2]); + s += 2; + while (*++s != '\'') + putc(*s, output_file); + putc('\'', output_file); + } + else + fprintf(output_file, " '%c'", s[1]); + } + else + fprintf(output_file, " %s", s); + } + if (!rflag) ++outline; + fprintf(output_file, "\",\n"); + } + + if (!rflag) outline += 2; + fprintf(output_file, "};\n#endif\n"); +} + + +output_stype() +{ + if (!unionized && ntags == 0) + { + outline += 3; + fprintf(code_file, "#ifndef YYSTYPE\ntypedef int YYSTYPE;\n#endif\n"); + } +} + + +output_trailing_text() +{ + register int c, last; + register FILE *in, *out; + + if (line == 0) + return; + + in = input_file; + out = code_file; + c = *cptr; + if (c == '\n') + { + ++lineno; + if ((c = getc(in)) == EOF) + return; + if (!lflag) + { + ++outline; + fprintf(out, line_format, lineno, input_file_name); + } + if (c == '\n') + ++outline; + putc(c, out); + last = c; + } + else + { + if (!lflag) + { + ++outline; + fprintf(out, line_format, lineno, input_file_name); + } + do { putc(c, out); } while ((c = *++cptr) != '\n'); + ++outline; + putc('\n', out); + last = '\n'; + } + + while ((c = getc(in)) != EOF) + { + if (c == '\n') + ++outline; + putc(c, out); + last = c; + } + + if (last != '\n') + { + ++outline; + putc('\n', out); + } + if (!lflag) + fprintf(out, line_format, ++outline + 1, code_file_name); +} + + +output_semantic_actions() +{ + register int c, last; + register FILE *out; + + fclose(action_file); + action_file = fopen(action_file_name, "r"); + if (action_file == NULL) + open_error(action_file_name); + + if ((c = getc(action_file)) == EOF) + return; + + out = code_file; + last = c; + if (c == '\n') + ++outline; + putc(c, out); + while ((c = getc(action_file)) != EOF) + { + if (c == '\n') + ++outline; + putc(c, out); + last = c; + } + + if (last != '\n') + { + ++outline; + putc('\n', out); + } + + if (!lflag) + fprintf(out, line_format, ++outline + 1, code_file_name); +} + + +free_itemsets() +{ + register core *cp, *next; + + FREE(state_table); + for (cp = first_state; cp; cp = next) + { + next = cp->next; + FREE(cp); + } +} + + +free_shifts() +{ + register shifts *sp, *next; + + FREE(shift_table); + for (sp = first_shift; sp; sp = next) + { + next = sp->next; + FREE(sp); + } +} + + + +free_reductions() +{ + register reductions *rp, *next; + + FREE(reduction_table); + for (rp = first_reduction; rp; rp = next) + { + next = rp->next; + FREE(rp); + } +} diff --git a/commands/byacc/reader.c b/commands/byacc/reader.c new file mode 100755 index 000000000..059a04371 --- /dev/null +++ b/commands/byacc/reader.c @@ -0,0 +1,1770 @@ +#include "defs.h" + +/* The line size must be a positive integer. One hundred was chosen */ +/* because few lines in Yacc input grammars exceed 100 characters. */ +/* Note that if a line exceeds LINESIZE characters, the line buffer */ +/* will be expanded to accomodate it. */ + +#define LINESIZE 100 + +char *cache; +int cinc, cache_size; + +int ntags, tagmax; +char **tag_table; + +char saw_eof, unionized; +char *cptr, *line; +int linesize; + +bucket *goal; +int prec; +int gensym; +char last_was_action; + +int maxitems; +bucket **pitem; + +int maxrules; +bucket **plhs; + +int name_pool_size; +char *name_pool; + +char line_format[] = "#line %d \"%s\"\n"; + + +cachec(c) +int c; +{ + assert(cinc >= 0); + if (cinc >= cache_size) + { + cache_size += 256; + cache = REALLOC(cache, cache_size); + if (cache == 0) no_space(); + } + cache[cinc] = c; + ++cinc; +} + + +get_line() +{ + register FILE *f = input_file; + register int c; + register int i; + + if (saw_eof || (c = getc(f)) == EOF) + { + if (line) { FREE(line); line = 0; } + cptr = 0; + saw_eof = 1; + return; + } + + if (line == 0 || linesize != (LINESIZE + 1)) + { + if (line) FREE(line); + linesize = LINESIZE + 1; + line = MALLOC(linesize); + if (line == 0) no_space(); + } + + i = 0; + ++lineno; + for (;;) + { + line[i] = c; + if (c == '\n') { cptr = line; return; } + if (++i >= linesize) + { + linesize += LINESIZE; + line = REALLOC(line, linesize); + if (line == 0) no_space(); + } + c = getc(f); + if (c == EOF) + { + line[i] = '\n'; + saw_eof = 1; + cptr = line; + return; + } + } +} + + +char * +dup_line() +{ + register char *p, *s, *t; + + if (line == 0) return (0); + s = line; + while (*s != '\n') ++s; + p = MALLOC(s - line + 1); + if (p == 0) no_space(); + + s = line; + t = p; + while ((*t++ = *s++) != '\n') continue; + return (p); +} + + +skip_comment() +{ + register char *s; + + int st_lineno = lineno; + char *st_line = dup_line(); + char *st_cptr = st_line + (cptr - line); + + s = cptr + 2; + for (;;) + { + if (*s == '*' && s[1] == '/') + { + cptr = s + 2; + FREE(st_line); + return; + } + if (*s == '\n') + { + get_line(); + if (line == 0) + unterminated_comment(st_lineno, st_line, st_cptr); + s = cptr; + } + else + ++s; + } +} + + +int +nextc() +{ + register char *s; + + if (line == 0) + { + get_line(); + if (line == 0) + return (EOF); + } + + s = cptr; + for (;;) + { + switch (*s) + { + case '\n': + get_line(); + if (line == 0) return (EOF); + s = cptr; + break; + + case ' ': + case '\t': + case '\f': + case '\r': + case '\v': + case ',': + case ';': + ++s; + break; + + case '\\': + cptr = s; + return ('%'); + + case '/': + if (s[1] == '*') + { + cptr = s; + skip_comment(); + s = cptr; + break; + } + else if (s[1] == '/') + { + get_line(); + if (line == 0) return (EOF); + s = cptr; + break; + } + /* fall through */ + + default: + cptr = s; + return (*s); + } + } +} + + +int +keyword() +{ + register int c; + char *t_cptr = cptr; + + c = *++cptr; + if (isalpha(c)) + { + cinc = 0; + for (;;) + { + if (isalpha(c)) + { + if (isupper(c)) c = tolower(c); + cachec(c); + } + else if (isdigit(c) || c == '_' || c == '.' || c == '$') + cachec(c); + else + break; + c = *++cptr; + } + cachec(NUL); + + if (strcmp(cache, "token") == 0 || strcmp(cache, "term") == 0) + return (TOKEN); + if (strcmp(cache, "type") == 0) + return (TYPE); + if (strcmp(cache, "left") == 0) + return (LEFT); + if (strcmp(cache, "right") == 0) + return (RIGHT); + if (strcmp(cache, "nonassoc") == 0 || strcmp(cache, "binary") == 0) + return (NONASSOC); + if (strcmp(cache, "start") == 0) + return (START); + if (strcmp(cache, "union") == 0) + return (UNION); + if (strcmp(cache, "ident") == 0) + return (IDENT); + } + else + { + ++cptr; + if (c == '{') + return (TEXT); + if (c == '%' || c == '\\') + return (MARK); + if (c == '<') + return (LEFT); + if (c == '>') + return (RIGHT); + if (c == '0') + return (TOKEN); + if (c == '2') + return (NONASSOC); + } + syntax_error(lineno, line, t_cptr); + /*NOTREACHED*/ +} + + +copy_ident() +{ + register int c; + register FILE *f = output_file; + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != '"') syntax_error(lineno, line, cptr); + ++outline; + fprintf(f, "#ident \""); + for (;;) + { + c = *++cptr; + if (c == '\n') + { + fprintf(f, "\"\n"); + return; + } + putc(c, f); + if (c == '"') + { + putc('\n', f); + ++cptr; + return; + } + } +} + + +copy_text() +{ + register int c; + int quote; + register FILE *f = text_file; + int need_newline = 0; + int t_lineno = lineno; + char *t_line = dup_line(); + char *t_cptr = t_line + (cptr - line - 2); + + if (*cptr == '\n') + { + get_line(); + if (line == 0) + unterminated_text(t_lineno, t_line, t_cptr); + } + if (!lflag) fprintf(f, line_format, lineno, input_file_name); + +loop: + c = *cptr++; + switch (c) + { + case '\n': + next_line: + putc('\n', f); + need_newline = 0; + get_line(); + if (line) goto loop; + unterminated_text(t_lineno, t_line, t_cptr); + + case '\'': + case '"': + { + int s_lineno = lineno; + char *s_line = dup_line(); + char *s_cptr = s_line + (cptr - line - 1); + + quote = c; + putc(c, f); + for (;;) + { + c = *cptr++; + putc(c, f); + if (c == quote) + { + need_newline = 1; + FREE(s_line); + goto loop; + } + if (c == '\n') + unterminated_string(s_lineno, s_line, s_cptr); + if (c == '\\') + { + c = *cptr++; + putc(c, f); + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_string(s_lineno, s_line, s_cptr); + } + } + } + } + + case '/': + putc(c, f); + need_newline = 1; + c = *cptr; + if (c == '/') + { + putc('*', f); + while ((c = *++cptr) != '\n') + { + if (c == '*' && cptr[1] == '/') + fprintf(f, "* "); + else + putc(c, f); + } + fprintf(f, "*/"); + goto next_line; + } + if (c == '*') + { + int c_lineno = lineno; + char *c_line = dup_line(); + char *c_cptr = c_line + (cptr - line - 1); + + putc('*', f); + ++cptr; + for (;;) + { + c = *cptr++; + putc(c, f); + if (c == '*' && *cptr == '/') + { + putc('/', f); + ++cptr; + FREE(c_line); + goto loop; + } + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_comment(c_lineno, c_line, c_cptr); + } + } + } + need_newline = 1; + goto loop; + + case '%': + case '\\': + if (*cptr == '}') + { + if (need_newline) putc('\n', f); + ++cptr; + FREE(t_line); + return; + } + /* fall through */ + + default: + putc(c, f); + need_newline = 1; + goto loop; + } +} + + +copy_union() +{ + register int c; + int quote; + int depth; + int u_lineno = lineno; + char *u_line = dup_line(); + char *u_cptr = u_line + (cptr - line - 6); + + if (unionized) over_unionized(cptr - 6); + unionized = 1; + + if (!lflag) + fprintf(text_file, line_format, lineno, input_file_name); + + fprintf(text_file, "typedef union"); + if (dflag) fprintf(union_file, "typedef union"); + + depth = 0; +loop: + c = *cptr++; + putc(c, text_file); + if (dflag) putc(c, union_file); + switch (c) + { + case '\n': + next_line: + get_line(); + if (line == 0) unterminated_union(u_lineno, u_line, u_cptr); + goto loop; + + case '{': + ++depth; + goto loop; + + case '}': + if (--depth == 0) + { + fprintf(text_file, " YYSTYPE;\n"); + FREE(u_line); + return; + } + goto loop; + + case '\'': + case '"': + { + int s_lineno = lineno; + char *s_line = dup_line(); + char *s_cptr = s_line + (cptr - line - 1); + + quote = c; + for (;;) + { + c = *cptr++; + putc(c, text_file); + if (dflag) putc(c, union_file); + if (c == quote) + { + FREE(s_line); + goto loop; + } + if (c == '\n') + unterminated_string(s_lineno, s_line, s_cptr); + if (c == '\\') + { + c = *cptr++; + putc(c, text_file); + if (dflag) putc(c, union_file); + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_string(s_lineno, s_line, s_cptr); + } + } + } + } + + case '/': + c = *cptr; + if (c == '/') + { + putc('*', text_file); + if (dflag) putc('*', union_file); + while ((c = *++cptr) != '\n') + { + if (c == '*' && cptr[1] == '/') + { + fprintf(text_file, "* "); + if (dflag) fprintf(union_file, "* "); + } + else + { + putc(c, text_file); + if (dflag) putc(c, union_file); + } + } + fprintf(text_file, "*/\n"); + if (dflag) fprintf(union_file, "*/\n"); + goto next_line; + } + if (c == '*') + { + int c_lineno = lineno; + char *c_line = dup_line(); + char *c_cptr = c_line + (cptr - line - 1); + + putc('*', text_file); + if (dflag) putc('*', union_file); + ++cptr; + for (;;) + { + c = *cptr++; + putc(c, text_file); + if (dflag) putc(c, union_file); + if (c == '*' && *cptr == '/') + { + putc('/', text_file); + if (dflag) putc('/', union_file); + ++cptr; + FREE(c_line); + goto loop; + } + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_comment(c_lineno, c_line, c_cptr); + } + } + } + goto loop; + + default: + goto loop; + } +} + + +int +hexval(c) +int c; +{ + if (c >= '0' && c <= '9') + return (c - '0'); + if (c >= 'A' && c <= 'F') + return (c - 'A' + 10); + if (c >= 'a' && c <= 'f') + return (c - 'a' + 10); + return (-1); +} + + +bucket * +get_literal() +{ + register int c, quote; + register int i; + register int n; + register char *s; + register bucket *bp; + int s_lineno = lineno; + char *s_line = dup_line(); + char *s_cptr = s_line + (cptr - line); + + quote = *cptr++; + cinc = 0; + for (;;) + { + c = *cptr++; + if (c == quote) break; + if (c == '\n') unterminated_string(s_lineno, s_line, s_cptr); + if (c == '\\') + { + char *c_cptr = cptr - 1; + + c = *cptr++; + switch (c) + { + case '\n': + get_line(); + if (line == 0) unterminated_string(s_lineno, s_line, s_cptr); + continue; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + n = c - '0'; + c = *cptr; + if (IS_OCTAL(c)) + { + n = (n << 3) + (c - '0'); + c = *++cptr; + if (IS_OCTAL(c)) + { + n = (n << 3) + (c - '0'); + ++cptr; + } + } + if (n > MAXCHAR) illegal_character(c_cptr); + c = n; + break; + + case 'x': + c = *cptr++; + n = hexval(c); + if (n < 0 || n >= 16) + illegal_character(c_cptr); + for (;;) + { + c = *cptr; + i = hexval(c); + if (i < 0 || i >= 16) break; + ++cptr; + n = (n << 4) + i; + if (n > MAXCHAR) illegal_character(c_cptr); + } + c = n; + break; + + case 'a': c = 7; break; + case 'b': c = '\b'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + } + } + cachec(c); + } + FREE(s_line); + + n = cinc; + s = MALLOC(n); + if (s == 0) no_space(); + + for (i = 0; i < n; ++i) + s[i] = cache[i]; + + cinc = 0; + if (n == 1) + cachec('\''); + else + cachec('"'); + + for (i = 0; i < n; ++i) + { + c = ((unsigned char *)s)[i]; + if (c == '\\' || c == cache[0]) + { + cachec('\\'); + cachec(c); + } + else if (isprint(c)) + cachec(c); + else + { + cachec('\\'); + switch (c) + { + case 7: cachec('a'); break; + case '\b': cachec('b'); break; + case '\f': cachec('f'); break; + case '\n': cachec('n'); break; + case '\r': cachec('r'); break; + case '\t': cachec('t'); break; + case '\v': cachec('v'); break; + default: + cachec(((c >> 6) & 7) + '0'); + cachec(((c >> 3) & 7) + '0'); + cachec((c & 7) + '0'); + break; + } + } + } + + if (n == 1) + cachec('\''); + else + cachec('"'); + + cachec(NUL); + bp = lookup(cache); + bp->class = TERM; + if (n == 1 && bp->value == UNDEFINED) + bp->value = *(unsigned char *)s; + FREE(s); + + return (bp); +} + + +int +is_reserved(name) +char *name; +{ + char *s; + + if (strcmp(name, ".") == 0 || + strcmp(name, "$accept") == 0 || + strcmp(name, "$end") == 0) + return (1); + + if (name[0] == '$' && name[1] == '$' && isdigit(name[2])) + { + s = name + 3; + while (isdigit(*s)) ++s; + if (*s == NUL) return (1); + } + + return (0); +} + + +bucket * +get_name() +{ + register int c; + + cinc = 0; + for (c = *cptr; IS_IDENT(c); c = *++cptr) + cachec(c); + cachec(NUL); + + if (is_reserved(cache)) used_reserved(cache); + + return (lookup(cache)); +} + + +int +get_number() +{ + register int c; + register int n; + + n = 0; + for (c = *cptr; isdigit(c); c = *++cptr) + n = 10*n + (c - '0'); + + return (n); +} + + +char * +get_tag() +{ + register int c; + register int i; + register char *s; + int t_lineno = lineno; + char *t_line = dup_line(); + char *t_cptr = t_line + (cptr - line); + + ++cptr; + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (!isalpha(c) && c != '_' && c != '$') + illegal_tag(t_lineno, t_line, t_cptr); + + cinc = 0; + do { cachec(c); c = *++cptr; } while (IS_IDENT(c)); + cachec(NUL); + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != '>') + illegal_tag(t_lineno, t_line, t_cptr); + ++cptr; + + for (i = 0; i < ntags; ++i) + { + if (strcmp(cache, tag_table[i]) == 0) + return (tag_table[i]); + } + + if (ntags >= tagmax) + { + tagmax += 16; + tag_table = (char **) + (tag_table ? REALLOC(tag_table, tagmax*sizeof(char *)) + : MALLOC(tagmax*sizeof(char *))); + if (tag_table == 0) no_space(); + } + + s = MALLOC(cinc); + if (s == 0) no_space(); + strcpy(s, cache); + tag_table[ntags] = s; + ++ntags; + FREE(t_line); + return (s); +} + + +declare_tokens(assoc) +int assoc; +{ + register int c; + register bucket *bp; + int value; + char *tag = 0; + + if (assoc != TOKEN) ++prec; + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c == '<') + { + tag = get_tag(); + c = nextc(); + if (c == EOF) unexpected_EOF(); + } + + for (;;) + { + if (isalpha(c) || c == '_' || c == '.' || c == '$') + bp = get_name(); + else if (c == '\'' || c == '"') + bp = get_literal(); + else + return; + + if (bp == goal) tokenized_start(bp->name); + bp->class = TERM; + + if (tag) + { + if (bp->tag && tag != bp->tag) + retyped_warning(bp->name); + bp->tag = tag; + } + + if (assoc != TOKEN) + { + if (bp->prec && prec != bp->prec) + reprec_warning(bp->name); + bp->assoc = assoc; + bp->prec = prec; + } + + c = nextc(); + if (c == EOF) unexpected_EOF(); + value = UNDEFINED; + if (isdigit(c)) + { + value = get_number(); + if (bp->value != UNDEFINED && value != bp->value) + revalued_warning(bp->name); + bp->value = value; + c = nextc(); + if (c == EOF) unexpected_EOF(); + } + } +} + + +declare_types() +{ + register int c; + register bucket *bp; + char *tag; + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != '<') syntax_error(lineno, line, cptr); + tag = get_tag(); + + for (;;) + { + c = nextc(); + if (isalpha(c) || c == '_' || c == '.' || c == '$') + bp = get_name(); + else if (c == '\'' || c == '"') + bp = get_literal(); + else + return; + + if (bp->tag && tag != bp->tag) + retyped_warning(bp->name); + bp->tag = tag; + } +} + + +declare_start() +{ + register int c; + register bucket *bp; + + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (!isalpha(c) && c != '_' && c != '.' && c != '$') + syntax_error(lineno, line, cptr); + bp = get_name(); + if (bp->class == TERM) + terminal_start(bp->name); + if (goal && goal != bp) + restarted_warning(); + goal = bp; +} + + +read_declarations() +{ + register int c, k; + + cache_size = 256; + cache = MALLOC(cache_size); + if (cache == 0) no_space(); + + for (;;) + { + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != '%') syntax_error(lineno, line, cptr); + switch (k = keyword()) + { + case MARK: + return; + + case IDENT: + copy_ident(); + break; + + case TEXT: + copy_text(); + break; + + case UNION: + copy_union(); + break; + + case TOKEN: + case LEFT: + case RIGHT: + case NONASSOC: + declare_tokens(k); + break; + + case TYPE: + declare_types(); + break; + + case START: + declare_start(); + break; + } + } +} + + +initialize_grammar() +{ + nitems = 4; + maxitems = 300; + pitem = (bucket **) MALLOC(maxitems*sizeof(bucket *)); + if (pitem == 0) no_space(); + pitem[0] = 0; + pitem[1] = 0; + pitem[2] = 0; + pitem[3] = 0; + + nrules = 3; + maxrules = 100; + plhs = (bucket **) MALLOC(maxrules*sizeof(bucket *)); + if (plhs == 0) no_space(); + plhs[0] = 0; + plhs[1] = 0; + plhs[2] = 0; + rprec = (short *) MALLOC(maxrules*sizeof(short)); + if (rprec == 0) no_space(); + rprec[0] = 0; + rprec[1] = 0; + rprec[2] = 0; + rassoc = (char *) MALLOC(maxrules*sizeof(char)); + if (rassoc == 0) no_space(); + rassoc[0] = TOKEN; + rassoc[1] = TOKEN; + rassoc[2] = TOKEN; +} + + +expand_items() +{ + maxitems += 300; + pitem = (bucket **) REALLOC(pitem, maxitems*sizeof(bucket *)); + if (pitem == 0) no_space(); +} + + +expand_rules() +{ + maxrules += 100; + plhs = (bucket **) REALLOC(plhs, maxrules*sizeof(bucket *)); + if (plhs == 0) no_space(); + rprec = (short *) REALLOC(rprec, maxrules*sizeof(short)); + if (rprec == 0) no_space(); + rassoc = (char *) REALLOC(rassoc, maxrules*sizeof(char)); + if (rassoc == 0) no_space(); +} + + +advance_to_start() +{ + register int c; + register bucket *bp; + char *s_cptr; + int s_lineno; + + for (;;) + { + c = nextc(); + if (c != '%') break; + s_cptr = cptr; + switch (keyword()) + { + case MARK: + no_grammar(); + + case TEXT: + copy_text(); + break; + + case START: + declare_start(); + break; + + default: + syntax_error(lineno, line, s_cptr); + } + } + + c = nextc(); + if (!isalpha(c) && c != '_' && c != '.' && c != '_') + syntax_error(lineno, line, cptr); + bp = get_name(); + if (goal == 0) + { + if (bp->class == TERM) + terminal_start(bp->name); + goal = bp; + } + + s_lineno = lineno; + c = nextc(); + if (c == EOF) unexpected_EOF(); + if (c != ':') syntax_error(lineno, line, cptr); + start_rule(bp, s_lineno); + ++cptr; +} + + +start_rule(bp, s_lineno) +register bucket *bp; +int s_lineno; +{ + if (bp->class == TERM) + terminal_lhs(s_lineno); + bp->class = NONTERM; + if (nrules >= maxrules) + expand_rules(); + plhs[nrules] = bp; + rprec[nrules] = UNDEFINED; + rassoc[nrules] = TOKEN; +} + + +end_rule() +{ + register int i; + + if (!last_was_action && plhs[nrules]->tag) + { + for (i = nitems - 1; pitem[i]; --i) continue; + if (pitem[i+1] == 0 || pitem[i+1]->tag != plhs[nrules]->tag) + default_action_warning(); + } + + last_was_action = 0; + if (nitems >= maxitems) expand_items(); + pitem[nitems] = 0; + ++nitems; + ++nrules; +} + + +insert_empty_rule() +{ + register bucket *bp, **bpp; + + assert(cache); + sprintf(cache, "$$%d", ++gensym); + bp = make_bucket(cache); + last_symbol->next = bp; + last_symbol = bp; + bp->tag = plhs[nrules]->tag; + bp->class = NONTERM; + + if ((nitems += 2) > maxitems) + expand_items(); + bpp = pitem + nitems - 1; + *bpp-- = bp; + while (bpp[0] = bpp[-1]) --bpp; + + if (++nrules >= maxrules) + expand_rules(); + plhs[nrules] = plhs[nrules-1]; + plhs[nrules-1] = bp; + rprec[nrules] = rprec[nrules-1]; + rprec[nrules-1] = 0; + rassoc[nrules] = rassoc[nrules-1]; + rassoc[nrules-1] = TOKEN; +} + + +add_symbol() +{ + register int c; + register bucket *bp; + int s_lineno = lineno; + + c = *cptr; + if (c == '\'' || c == '"') + bp = get_literal(); + else + bp = get_name(); + + c = nextc(); + if (c == ':') + { + end_rule(); + start_rule(bp, s_lineno); + ++cptr; + return; + } + + if (last_was_action) + insert_empty_rule(); + last_was_action = 0; + + if (++nitems > maxitems) + expand_items(); + pitem[nitems-1] = bp; +} + + +copy_action() +{ + register int c; + register int i, n; + int depth; + int quote; + char *tag; + register FILE *f = action_file; + int a_lineno = lineno; + char *a_line = dup_line(); + char *a_cptr = a_line + (cptr - line); + + if (last_was_action) + insert_empty_rule(); + last_was_action = 1; + + fprintf(f, "case %d:\n", nrules - 2); + if (!lflag) + fprintf(f, line_format, lineno, input_file_name); + if (*cptr == '=') ++cptr; + + n = 0; + for (i = nitems - 1; pitem[i]; --i) ++n; + + depth = 0; +loop: + c = *cptr; + if (c == '$') + { + if (cptr[1] == '<') + { + int d_lineno = lineno; + char *d_line = dup_line(); + char *d_cptr = d_line + (cptr - line); + + ++cptr; + tag = get_tag(); + c = *cptr; + if (c == '$') + { + fprintf(f, "yyval.%s", tag); + ++cptr; + FREE(d_line); + goto loop; + } + else if (isdigit(c)) + { + i = get_number(); + if (i > n) dollar_warning(d_lineno, i); + fprintf(f, "yyvsp[%d].%s", i - n, tag); + FREE(d_line); + goto loop; + } + else if (c == '-' && isdigit(cptr[1])) + { + ++cptr; + i = -get_number() - n; + fprintf(f, "yyvsp[%d].%s", i, tag); + FREE(d_line); + goto loop; + } + else + dollar_error(d_lineno, d_line, d_cptr); + } + else if (cptr[1] == '$') + { + if (ntags) + { + tag = plhs[nrules]->tag; + if (tag == 0) untyped_lhs(); + fprintf(f, "yyval.%s", tag); + } + else + fprintf(f, "yyval"); + cptr += 2; + goto loop; + } + else if (isdigit(cptr[1])) + { + ++cptr; + i = get_number(); + if (ntags) + { + if (i <= 0 || i > n) + unknown_rhs(i); + tag = pitem[nitems + i - n - 1]->tag; + if (tag == 0) untyped_rhs(i, pitem[nitems + i - n - 1]->name); + fprintf(f, "yyvsp[%d].%s", i - n, tag); + } + else + { + if (i > n) + dollar_warning(lineno, i); + fprintf(f, "yyvsp[%d]", i - n); + } + goto loop; + } + else if (cptr[1] == '-') + { + cptr += 2; + i = get_number(); + if (ntags) + unknown_rhs(-i); + fprintf(f, "yyvsp[%d]", -i - n); + goto loop; + } + } + if (isalpha(c) || c == '_' || c == '$') + { + do + { + putc(c, f); + c = *++cptr; + } while (isalnum(c) || c == '_' || c == '$'); + goto loop; + } + putc(c, f); + ++cptr; + switch (c) + { + case '\n': + next_line: + get_line(); + if (line) goto loop; + unterminated_action(a_lineno, a_line, a_cptr); + + case ';': + if (depth > 0) goto loop; + fprintf(f, "\nbreak;\n"); + return; + + case '{': + ++depth; + goto loop; + + case '}': + if (--depth > 0) goto loop; + fprintf(f, "\nbreak;\n"); + return; + + case '\'': + case '"': + { + int s_lineno = lineno; + char *s_line = dup_line(); + char *s_cptr = s_line + (cptr - line - 1); + + quote = c; + for (;;) + { + c = *cptr++; + putc(c, f); + if (c == quote) + { + FREE(s_line); + goto loop; + } + if (c == '\n') + unterminated_string(s_lineno, s_line, s_cptr); + if (c == '\\') + { + c = *cptr++; + putc(c, f); + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_string(s_lineno, s_line, s_cptr); + } + } + } + } + + case '/': + c = *cptr; + if (c == '/') + { + putc('*', f); + while ((c = *++cptr) != '\n') + { + if (c == '*' && cptr[1] == '/') + fprintf(f, "* "); + else + putc(c, f); + } + fprintf(f, "*/\n"); + goto next_line; + } + if (c == '*') + { + int c_lineno = lineno; + char *c_line = dup_line(); + char *c_cptr = c_line + (cptr - line - 1); + + putc('*', f); + ++cptr; + for (;;) + { + c = *cptr++; + putc(c, f); + if (c == '*' && *cptr == '/') + { + putc('/', f); + ++cptr; + FREE(c_line); + goto loop; + } + if (c == '\n') + { + get_line(); + if (line == 0) + unterminated_comment(c_lineno, c_line, c_cptr); + } + } + } + goto loop; + + default: + goto loop; + } +} + + +int +mark_symbol() +{ + register int c; + register bucket *bp; + + c = cptr[1]; + if (c == '%' || c == '\\') + { + cptr += 2; + return (1); + } + + if (c == '=') + cptr += 2; + else if ((c == 'p' || c == 'P') && + ((c = cptr[2]) == 'r' || c == 'R') && + ((c = cptr[3]) == 'e' || c == 'E') && + ((c = cptr[4]) == 'c' || c == 'C') && + ((c = cptr[5], !IS_IDENT(c)))) + cptr += 5; + else + syntax_error(lineno, line, cptr); + + c = nextc(); + if (isalpha(c) || c == '_' || c == '.' || c == '$') + bp = get_name(); + else if (c == '\'' || c == '"') + bp = get_literal(); + else + { + syntax_error(lineno, line, cptr); + /*NOTREACHED*/ + } + + if (rprec[nrules] != UNDEFINED && bp->prec != rprec[nrules]) + prec_redeclared(); + + rprec[nrules] = bp->prec; + rassoc[nrules] = bp->assoc; + return (0); +} + + +read_grammar() +{ + register int c; + + initialize_grammar(); + advance_to_start(); + + for (;;) + { + c = nextc(); + if (c == EOF) break; + if (isalpha(c) || c == '_' || c == '.' || c == '$' || c == '\'' || + c == '"') + add_symbol(); + else if (c == '{' || c == '=') + copy_action(); + else if (c == '|') + { + end_rule(); + start_rule(plhs[nrules-1], 0); + ++cptr; + } + else if (c == '%') + { + if (mark_symbol()) break; + } + else + syntax_error(lineno, line, cptr); + } + end_rule(); +} + + +free_tags() +{ + register int i; + + if (tag_table == 0) return; + + for (i = 0; i < ntags; ++i) + { + assert(tag_table[i]); + FREE(tag_table[i]); + } + FREE(tag_table); +} + + +pack_names() +{ + register bucket *bp; + register char *p, *s, *t; + + name_pool_size = 13; /* 13 == sizeof("$end") + sizeof("$accept") */ + for (bp = first_symbol; bp; bp = bp->next) + name_pool_size += strlen(bp->name) + 1; + name_pool = MALLOC(name_pool_size); + if (name_pool == 0) no_space(); + + strcpy(name_pool, "$accept"); + strcpy(name_pool+8, "$end"); + t = name_pool + 13; + for (bp = first_symbol; bp; bp = bp->next) + { + p = t; + s = bp->name; + while (*t++ = *s++) continue; + FREE(bp->name); + bp->name = p; + } +} + + +check_symbols() +{ + register bucket *bp; + + if (goal->class == UNKNOWN) + undefined_goal(goal->name); + + for (bp = first_symbol; bp; bp = bp->next) + { + if (bp->class == UNKNOWN) + { + undefined_symbol_warning(bp->name); + bp->class = TERM; + } + } +} + + +pack_symbols() +{ + register bucket *bp; + register bucket **v; + register int i, j, k, n; + + nsyms = 2; + ntokens = 1; + for (bp = first_symbol; bp; bp = bp->next) + { + ++nsyms; + if (bp->class == TERM) ++ntokens; + } + start_symbol = ntokens; + nvars = nsyms - ntokens; + + symbol_name = (char **) MALLOC(nsyms*sizeof(char *)); + if (symbol_name == 0) no_space(); + symbol_value = (short *) MALLOC(nsyms*sizeof(short)); + if (symbol_value == 0) no_space(); + symbol_prec = (short *) MALLOC(nsyms*sizeof(short)); + if (symbol_prec == 0) no_space(); + symbol_assoc = MALLOC(nsyms); + if (symbol_assoc == 0) no_space(); + + v = (bucket **) MALLOC(nsyms*sizeof(bucket *)); + if (v == 0) no_space(); + + v[0] = 0; + v[start_symbol] = 0; + + i = 1; + j = start_symbol + 1; + for (bp = first_symbol; bp; bp = bp->next) + { + if (bp->class == TERM) + v[i++] = bp; + else + v[j++] = bp; + } + assert(i == ntokens && j == nsyms); + + for (i = 1; i < ntokens; ++i) + v[i]->index = i; + + goal->index = start_symbol + 1; + k = start_symbol + 2; + while (++i < nsyms) + if (v[i] != goal) + { + v[i]->index = k; + ++k; + } + + goal->value = 0; + k = 1; + for (i = start_symbol + 1; i < nsyms; ++i) + { + if (v[i] != goal) + { + v[i]->value = k; + ++k; + } + } + + k = 0; + for (i = 1; i < ntokens; ++i) + { + n = v[i]->value; + if (n > 256) + { + for (j = k++; j > 0 && symbol_value[j-1] > n; --j) + symbol_value[j] = symbol_value[j-1]; + symbol_value[j] = n; + } + } + + if (v[1]->value == UNDEFINED) + v[1]->value = 256; + + j = 0; + n = 257; + for (i = 2; i < ntokens; ++i) + { + if (v[i]->value == UNDEFINED) + { + while (j < k && n == symbol_value[j]) + { + while (++j < k && n == symbol_value[j]) continue; + ++n; + } + v[i]->value = n; + ++n; + } + } + + symbol_name[0] = name_pool + 8; + symbol_value[0] = 0; + symbol_prec[0] = 0; + symbol_assoc[0] = TOKEN; + for (i = 1; i < ntokens; ++i) + { + symbol_name[i] = v[i]->name; + symbol_value[i] = v[i]->value; + symbol_prec[i] = v[i]->prec; + symbol_assoc[i] = v[i]->assoc; + } + symbol_name[start_symbol] = name_pool; + symbol_value[start_symbol] = -1; + symbol_prec[start_symbol] = 0; + symbol_assoc[start_symbol] = TOKEN; + for (++i; i < nsyms; ++i) + { + k = v[i]->index; + symbol_name[k] = v[i]->name; + symbol_value[k] = v[i]->value; + symbol_prec[k] = v[i]->prec; + symbol_assoc[k] = v[i]->assoc; + } + + FREE(v); +} + + +pack_grammar() +{ + register int i, j; + int assoc, prec; + + ritem = (short *) MALLOC(nitems*sizeof(short)); + if (ritem == 0) no_space(); + rlhs = (short *) MALLOC(nrules*sizeof(short)); + if (rlhs == 0) no_space(); + rrhs = (short *) MALLOC((nrules+1)*sizeof(short)); + if (rrhs == 0) no_space(); + rprec = (short *) REALLOC(rprec, nrules*sizeof(short)); + if (rprec == 0) no_space(); + rassoc = REALLOC(rassoc, nrules); + if (rassoc == 0) no_space(); + + ritem[0] = -1; + ritem[1] = goal->index; + ritem[2] = 0; + ritem[3] = -2; + rlhs[0] = 0; + rlhs[1] = 0; + rlhs[2] = start_symbol; + rrhs[0] = 0; + rrhs[1] = 0; + rrhs[2] = 1; + + j = 4; + for (i = 3; i < nrules; ++i) + { + rlhs[i] = plhs[i]->index; + rrhs[i] = j; + assoc = TOKEN; + prec = 0; + while (pitem[j]) + { + ritem[j] = pitem[j]->index; + if (pitem[j]->class == TERM) + { + prec = pitem[j]->prec; + assoc = pitem[j]->assoc; + } + ++j; + } + ritem[j] = -i; + ++j; + if (rprec[i] == UNDEFINED) + { + rprec[i] = prec; + rassoc[i] = assoc; + } + } + rrhs[i] = j; + + FREE(plhs); + FREE(pitem); +} + + +print_grammar() +{ + register int i, j, k; + int spacing; + register FILE *f = verbose_file; + + if (!vflag) return; + + k = 1; + for (i = 2; i < nrules; ++i) + { + if (rlhs[i] != rlhs[i-1]) + { + if (i != 2) fprintf(f, "\n"); + fprintf(f, "%4d %s :", i - 2, symbol_name[rlhs[i]]); + spacing = strlen(symbol_name[rlhs[i]]) + 1; + } + else + { + fprintf(f, "%4d ", i - 2); + j = spacing; + while (--j >= 0) putc(' ', f); + putc('|', f); + } + + while (ritem[k] >= 0) + { + fprintf(f, " %s", symbol_name[ritem[k]]); + ++k; + } + ++k; + putc('\n', f); + } +} + + +reader() +{ + write_section(banner); + create_symbol_table(); + read_declarations(); + read_grammar(); + free_symbol_table(); + free_tags(); + pack_names(); + check_symbols(); + pack_symbols(); + pack_grammar(); + free_symbols(); + print_grammar(); +} diff --git a/commands/byacc/skeleton.c b/commands/byacc/skeleton.c new file mode 100755 index 000000000..d2b6c9f09 --- /dev/null +++ b/commands/byacc/skeleton.c @@ -0,0 +1,306 @@ +#include "defs.h" + +/* The definition of yysccsid in the banner should be replaced with */ +/* a #pragma ident directive if the target C compiler supports */ +/* #pragma ident directives. */ +/* */ +/* If the skeleton is changed, the banner should be changed so that */ +/* the altered version can be easily distinguished from the original. */ +/* */ +/* The #defines included with the banner are there because they are */ +/* useful in subsequent code. The macros #defined in the header or */ +/* the body either are not useful outside of semantic actions or */ +/* are conditional. */ + +char *banner[] = +{ + "#ifndef lint", + "static char yysccsid[] = \"@(#)yaccpar 1.9 (Berkeley) 02/21/93\";", + "#endif", + "#define YYBYACC 1", + "#define YYMAJOR 1", + "#define YYMINOR 9", + "#define yyclearin (yychar=(-1))", + "#define yyerrok (yyerrflag=0)", + "#define YYRECOVERING (yyerrflag!=0)", + 0 +}; + + +char *tables[] = +{ + "extern short yylhs[];", + "extern short yylen[];", + "extern short yydefred[];", + "extern short yydgoto[];", + "extern short yysindex[];", + "extern short yyrindex[];", + "extern short yygindex[];", + "extern short yytable[];", + "extern short yycheck[];", + "#if YYDEBUG", + "extern char *yyname[];", + "extern char *yyrule[];", + "#endif", + 0 +}; + + +char *header[] = +{ + "#ifdef YYSTACKSIZE", + "#undef YYMAXDEPTH", + "#define YYMAXDEPTH YYSTACKSIZE", + "#else", + "#ifdef YYMAXDEPTH", + "#define YYSTACKSIZE YYMAXDEPTH", + "#else", + "#define YYSTACKSIZE 500", + "#define YYMAXDEPTH 500", + "#endif", + "#endif", + "int yydebug;", + "int yynerrs;", + "int yyerrflag;", + "int yychar;", + "short *yyssp;", + "YYSTYPE *yyvsp;", + "YYSTYPE yyval;", + "YYSTYPE yylval;", + "short yyss[YYSTACKSIZE];", + "YYSTYPE yyvs[YYSTACKSIZE];", + "#define yystacksize YYSTACKSIZE", + 0 +}; + + +char *body[] = +{ + "#define YYABORT goto yyabort", + "#define YYREJECT goto yyabort", + "#define YYACCEPT goto yyaccept", + "#define YYERROR goto yyerrlab", + "int", + "yyparse()", + "{", + " register int yym, yyn, yystate;", + "#if YYDEBUG", + " register char *yys;", + " extern char *getenv();", + "", + " if (yys = getenv(\"YYDEBUG\"))", + " {", + " yyn = *yys;", + " if (yyn >= '0' && yyn <= '9')", + " yydebug = yyn - '0';", + " }", + "#endif", + "", + " yynerrs = 0;", + " yyerrflag = 0;", + " yychar = (-1);", + "", + " yyssp = yyss;", + " yyvsp = yyvs;", + " *yyssp = yystate = 0;", + "", + "yyloop:", + " if (yyn = yydefred[yystate]) goto yyreduce;", + " if (yychar < 0)", + " {", + " if ((yychar = yylex()) < 0) yychar = 0;", + "#if YYDEBUG", + " if (yydebug)", + " {", + " yys = 0;", + " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", + " if (!yys) yys = \"illegal-symbol\";", + " printf(\"%sdebug: state %d, reading %d (%s)\\n\",", + " YYPREFIX, yystate, yychar, yys);", + " }", + "#endif", + " }", + " if ((yyn = yysindex[yystate]) && (yyn += yychar) >= 0 &&", + " yyn <= YYTABLESIZE && yycheck[yyn] == yychar)", + " {", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: state %d, shifting to state %d\\n\",", + " YYPREFIX, yystate, yytable[yyn]);", + "#endif", + " if (yyssp >= yyss + yystacksize - 1)", + " {", + " goto yyoverflow;", + " }", + " *++yyssp = yystate = yytable[yyn];", + " *++yyvsp = yylval;", + " yychar = (-1);", + " if (yyerrflag > 0) --yyerrflag;", + " goto yyloop;", + " }", + " if ((yyn = yyrindex[yystate]) && (yyn += yychar) >= 0 &&", + " yyn <= YYTABLESIZE && yycheck[yyn] == yychar)", + " {", + " yyn = yytable[yyn];", + " goto yyreduce;", + " }", + " if (yyerrflag) goto yyinrecovery;", + "#ifdef lint", + " goto yynewerror;", + "#endif", + "yynewerror:", + " yyerror(\"syntax error\");", + "#ifdef lint", + " goto yyerrlab;", + "#endif", + "yyerrlab:", + " ++yynerrs;", + "yyinrecovery:", + " if (yyerrflag < 3)", + " {", + " yyerrflag = 3;", + " for (;;)", + " {", + " if ((yyn = yysindex[*yyssp]) && (yyn += YYERRCODE) >= 0 &&", + " yyn <= YYTABLESIZE && yycheck[yyn] == YYERRCODE)", + " {", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: state %d, error recovery shifting\\", + " to state %d\\n\", YYPREFIX, *yyssp, yytable[yyn]);", + "#endif", + " if (yyssp >= yyss + yystacksize - 1)", + " {", + " goto yyoverflow;", + " }", + " *++yyssp = yystate = yytable[yyn];", + " *++yyvsp = yylval;", + " goto yyloop;", + " }", + " else", + " {", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: error recovery discarding state %d\ +\\n\",", + " YYPREFIX, *yyssp);", + "#endif", + " if (yyssp <= yyss) goto yyabort;", + " --yyssp;", + " --yyvsp;", + " }", + " }", + " }", + " else", + " {", + " if (yychar == 0) goto yyabort;", + "#if YYDEBUG", + " if (yydebug)", + " {", + " yys = 0;", + " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", + " if (!yys) yys = \"illegal-symbol\";", + " printf(\"%sdebug: state %d, error recovery discards token %d\ + (%s)\\n\",", + " YYPREFIX, yystate, yychar, yys);", + " }", + "#endif", + " yychar = (-1);", + " goto yyloop;", + " }", + "yyreduce:", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: state %d, reducing by rule %d (%s)\\n\",", + " YYPREFIX, yystate, yyn, yyrule[yyn]);", + "#endif", + " yym = yylen[yyn];", + " yyval = yyvsp[1-yym];", + " switch (yyn)", + " {", + 0 +}; + + +char *trailer[] = +{ + " }", + " yyssp -= yym;", + " yystate = *yyssp;", + " yyvsp -= yym;", + " yym = yylhs[yyn];", + " if (yystate == 0 && yym == 0)", + " {", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: after reduction, shifting from state 0 to\\", + " state %d\\n\", YYPREFIX, YYFINAL);", + "#endif", + " yystate = YYFINAL;", + " *++yyssp = YYFINAL;", + " *++yyvsp = yyval;", + " if (yychar < 0)", + " {", + " if ((yychar = yylex()) < 0) yychar = 0;", + "#if YYDEBUG", + " if (yydebug)", + " {", + " yys = 0;", + " if (yychar <= YYMAXTOKEN) yys = yyname[yychar];", + " if (!yys) yys = \"illegal-symbol\";", + " printf(\"%sdebug: state %d, reading %d (%s)\\n\",", + " YYPREFIX, YYFINAL, yychar, yys);", + " }", + "#endif", + " }", + " if (yychar == 0) goto yyaccept;", + " goto yyloop;", + " }", + " if ((yyn = yygindex[yym]) && (yyn += yystate) >= 0 &&", + " yyn <= YYTABLESIZE && yycheck[yyn] == yystate)", + " yystate = yytable[yyn];", + " else", + " yystate = yydgoto[yym];", + "#if YYDEBUG", + " if (yydebug)", + " printf(\"%sdebug: after reduction, shifting from state %d \\", + "to state %d\\n\", YYPREFIX, *yyssp, yystate);", + "#endif", + " if (yyssp >= yyss + yystacksize - 1)", + " {", + " goto yyoverflow;", + " }", + " *++yyssp = yystate;", + " *++yyvsp = yyval;", + " goto yyloop;", + "yyoverflow:", + " yyerror(\"yacc stack overflow\");", + "yyabort:", + " return (1);", + "yyaccept:", + " return (0);", + "}", + 0 +}; + + +write_section(section) +char *section[]; +{ + register int c; + register int i; + register char *s; + register FILE *f; + + f = code_file; + for (i = 0; s = section[i]; ++i) + { + ++outline; + while (c = *s) + { + putc(c, f); + ++s; + } + putc('\n', f); + } +} diff --git a/commands/byacc/symtab.c b/commands/byacc/symtab.c new file mode 100755 index 000000000..c7c6ac03a --- /dev/null +++ b/commands/byacc/symtab.c @@ -0,0 +1,119 @@ +#include "defs.h" + + +/* TABLE_SIZE is the number of entries in the symbol table. */ +/* TABLE_SIZE must be a power of two. */ + +#define TABLE_SIZE 1024 + + +bucket **symbol_table; +bucket *first_symbol; +bucket *last_symbol; + + +int +hash(name) +char *name; +{ + register char *s; + register int c, k; + + assert(name && *name); + s = name; + k = *s; + while (c = *++s) + k = (31*k + c) & (TABLE_SIZE - 1); + + return (k); +} + + +bucket * +make_bucket(name) +char *name; +{ + register bucket *bp; + + assert(name); + bp = (bucket *) MALLOC(sizeof(bucket)); + if (bp == 0) no_space(); + bp->link = 0; + bp->next = 0; + bp->name = MALLOC(strlen(name) + 1); + if (bp->name == 0) no_space(); + bp->tag = 0; + bp->value = UNDEFINED; + bp->index = 0; + bp->prec = 0; + bp-> class = UNKNOWN; + bp->assoc = TOKEN; + + if (bp->name == 0) no_space(); + strcpy(bp->name, name); + + return (bp); +} + + +bucket * +lookup(name) +char *name; +{ + register bucket *bp, **bpp; + + bpp = symbol_table + hash(name); + bp = *bpp; + + while (bp) + { + if (strcmp(name, bp->name) == 0) return (bp); + bpp = &bp->link; + bp = *bpp; + } + + *bpp = bp = make_bucket(name); + last_symbol->next = bp; + last_symbol = bp; + + return (bp); +} + + +create_symbol_table() +{ + register int i; + register bucket *bp; + + symbol_table = (bucket **) MALLOC(TABLE_SIZE*sizeof(bucket *)); + if (symbol_table == 0) no_space(); + for (i = 0; i < TABLE_SIZE; i++) + symbol_table[i] = 0; + + bp = make_bucket("error"); + bp->index = 1; + bp->class = TERM; + + first_symbol = bp; + last_symbol = bp; + symbol_table[hash("error")] = bp; +} + + +free_symbol_table() +{ + FREE(symbol_table); + symbol_table = 0; +} + + +free_symbols() +{ + register bucket *p, *q; + + for (p = first_symbol; p; p = q) + { + q = p->next; + FREE(p); + } +} diff --git a/commands/byacc/verbose.c b/commands/byacc/verbose.c new file mode 100755 index 000000000..2c7cc52c7 --- /dev/null +++ b/commands/byacc/verbose.c @@ -0,0 +1,329 @@ + +#include "defs.h" + + +static short *null_rules; + +verbose() +{ + register int i; + + if (!vflag) return; + + null_rules = (short *) MALLOC(nrules*sizeof(short)); + if (null_rules == 0) no_space(); + fprintf(verbose_file, "\f\n"); + for (i = 0; i < nstates; i++) + print_state(i); + FREE(null_rules); + + if (nunused) + log_unused(); + if (SRtotal || RRtotal) + log_conflicts(); + + fprintf(verbose_file, "\n\n%d terminals, %d nonterminals\n", ntokens, + nvars); + fprintf(verbose_file, "%d grammar rules, %d states\n", nrules - 2, nstates); +} + + +log_unused() +{ + register int i; + register short *p; + + fprintf(verbose_file, "\n\nRules never reduced:\n"); + for (i = 3; i < nrules; ++i) + { + if (!rules_used[i]) + { + fprintf(verbose_file, "\t%s :", symbol_name[rlhs[i]]); + for (p = ritem + rrhs[i]; *p >= 0; ++p) + fprintf(verbose_file, " %s", symbol_name[*p]); + fprintf(verbose_file, " (%d)\n", i - 2); + } + } +} + + +log_conflicts() +{ + register int i; + + fprintf(verbose_file, "\n\n"); + for (i = 0; i < nstates; i++) + { + if (SRconflicts[i] || RRconflicts[i]) + { + fprintf(verbose_file, "State %d contains ", i); + if (SRconflicts[i] == 1) + fprintf(verbose_file, "1 shift/reduce conflict"); + else if (SRconflicts[i] > 1) + fprintf(verbose_file, "%d shift/reduce conflicts", + SRconflicts[i]); + if (SRconflicts[i] && RRconflicts[i]) + fprintf(verbose_file, ", "); + if (RRconflicts[i] == 1) + fprintf(verbose_file, "1 reduce/reduce conflict"); + else if (RRconflicts[i] > 1) + fprintf(verbose_file, "%d reduce/reduce conflicts", + RRconflicts[i]); + fprintf(verbose_file, ".\n"); + } + } +} + + +print_state(state) +int state; +{ + if (state) + fprintf(verbose_file, "\n\n"); + if (SRconflicts[state] || RRconflicts[state]) + print_conflicts(state); + fprintf(verbose_file, "state %d\n", state); + print_core(state); + print_nulls(state); + print_actions(state); +} + + +print_conflicts(state) +int state; +{ + register int symbol, act, number; + register action *p; + + symbol = -1; + for (p = parser[state]; p; p = p->next) + { + if (p->suppressed == 2) + continue; + + if (p->symbol != symbol) + { + symbol = p->symbol; + number = p->number; + if (p->action_code == SHIFT) + act = SHIFT; + else + act = REDUCE; + } + else if (p->suppressed == 1) + { + if (state == final_state && symbol == 0) + { + fprintf(verbose_file, "%d: shift/reduce conflict \ +(accept, reduce %d) on $end\n", state, p->number - 2); + } + else + { + if (act == SHIFT) + { + fprintf(verbose_file, "%d: shift/reduce conflict \ +(shift %d, reduce %d) on %s\n", state, number, p->number - 2, + symbol_name[symbol]); + } + else + { + fprintf(verbose_file, "%d: reduce/reduce conflict \ +(reduce %d, reduce %d) on %s\n", state, number - 2, p->number - 2, + symbol_name[symbol]); + } + } + } + } +} + + +print_core(state) +int state; +{ + register int i; + register int k; + register int rule; + register core *statep; + register short *sp; + register short *sp1; + + statep = state_table[state]; + k = statep->nitems; + + for (i = 0; i < k; i++) + { + sp1 = sp = ritem + statep->items[i]; + + while (*sp >= 0) ++sp; + rule = -(*sp); + fprintf(verbose_file, "\t%s : ", symbol_name[rlhs[rule]]); + + for (sp = ritem + rrhs[rule]; sp < sp1; sp++) + fprintf(verbose_file, "%s ", symbol_name[*sp]); + + putc('.', verbose_file); + + while (*sp >= 0) + { + fprintf(verbose_file, " %s", symbol_name[*sp]); + sp++; + } + fprintf(verbose_file, " (%d)\n", -2 - *sp); + } +} + + +print_nulls(state) +int state; +{ + register action *p; + register int i, j, k, nnulls; + + nnulls = 0; + for (p = parser[state]; p; p = p->next) + { + if (p->action_code == REDUCE && + (p->suppressed == 0 || p->suppressed == 1)) + { + i = p->number; + if (rrhs[i] + 1 == rrhs[i+1]) + { + for (j = 0; j < nnulls && i > null_rules[j]; ++j) + continue; + + if (j == nnulls) + { + ++nnulls; + null_rules[j] = i; + } + else if (i != null_rules[j]) + { + ++nnulls; + for (k = nnulls - 1; k > j; --k) + null_rules[k] = null_rules[k-1]; + null_rules[j] = i; + } + } + } + } + + for (i = 0; i < nnulls; ++i) + { + j = null_rules[i]; + fprintf(verbose_file, "\t%s : . (%d)\n", symbol_name[rlhs[j]], + j - 2); + } + fprintf(verbose_file, "\n"); +} + + +print_actions(stateno) +int stateno; +{ + register action *p; + register shifts *sp; + register int as; + + if (stateno == final_state) + fprintf(verbose_file, "\t$end accept\n"); + + p = parser[stateno]; + if (p) + { + print_shifts(p); + print_reductions(p, defred[stateno]); + } + + sp = shift_table[stateno]; + if (sp && sp->nshifts > 0) + { + as = accessing_symbol[sp->shift[sp->nshifts - 1]]; + if (ISVAR(as)) + print_gotos(stateno); + } +} + + +print_shifts(p) +register action *p; +{ + register int count; + register action *q; + + count = 0; + for (q = p; q; q = q->next) + { + if (q->suppressed < 2 && q->action_code == SHIFT) + ++count; + } + + if (count > 0) + { + for (; p; p = p->next) + { + if (p->action_code == SHIFT && p->suppressed == 0) + fprintf(verbose_file, "\t%s shift %d\n", + symbol_name[p->symbol], p->number); + } + } +} + + +print_reductions(p, defred) +register action *p; +register int defred; +{ + register int k, anyreds; + register action *q; + + anyreds = 0; + for (q = p; q ; q = q->next) + { + if (q->action_code == REDUCE && q->suppressed < 2) + { + anyreds = 1; + break; + } + } + + if (anyreds == 0) + fprintf(verbose_file, "\t. error\n"); + else + { + for (; p; p = p->next) + { + if (p->action_code == REDUCE && p->number != defred) + { + k = p->number - 2; + if (p->suppressed == 0) + fprintf(verbose_file, "\t%s reduce %d\n", + symbol_name[p->symbol], k); + } + } + + if (defred > 0) + fprintf(verbose_file, "\t. reduce %d\n", defred - 2); + } +} + + +print_gotos(stateno) +int stateno; +{ + register int i, k; + register int as; + register short *to_state; + register shifts *sp; + + putc('\n', verbose_file); + sp = shift_table[stateno]; + to_state = sp->shift; + for (i = 0; i < sp->nshifts; ++i) + { + k = to_state[i]; + as = accessing_symbol[k]; + if (ISVAR(as)) + fprintf(verbose_file, "\t%s goto %d\n", symbol_name[as], k); + } +} + diff --git a/commands/byacc/warshall.c b/commands/byacc/warshall.c new file mode 100755 index 000000000..b449e4e27 --- /dev/null +++ b/commands/byacc/warshall.c @@ -0,0 +1,82 @@ +#include "defs.h" + +transitive_closure(R, n) +unsigned *R; +int n; +{ + register int rowsize; + register unsigned i; + register unsigned *rowj; + register unsigned *rp; + register unsigned *rend; + register unsigned *ccol; + register unsigned *relend; + register unsigned *cword; + register unsigned *rowi; + + rowsize = WORDSIZE(n); + relend = R + n*rowsize; + + cword = R; + i = 0; + rowi = R; + while (rowi < relend) + { + ccol = cword; + rowj = R; + + while (rowj < relend) + { + if (*ccol & (1 << i)) + { + rp = rowi; + rend = rowj + rowsize; + while (rowj < rend) + *rowj++ |= *rp++; + } + else + { + rowj += rowsize; + } + + ccol += rowsize; + } + + if (++i >= BITS_PER_WORD) + { + i = 0; + cword++; + } + + rowi += rowsize; + } +} + +reflexive_transitive_closure(R, n) +unsigned *R; +int n; +{ + register int rowsize; + register unsigned i; + register unsigned *rp; + register unsigned *relend; + + transitive_closure(R, n); + + rowsize = WORDSIZE(n); + relend = R + n*rowsize; + + i = 0; + rp = R; + while (rp < relend) + { + *rp |= (1 << i); + if (++i >= BITS_PER_WORD) + { + i = 0; + rp++; + } + + rp += rowsize; + } +} diff --git a/commands/cawf/Makefile b/commands/cawf/Makefile new file mode 100755 index 000000000..98a39d529 --- /dev/null +++ b/commands/cawf/Makefile @@ -0,0 +1,105 @@ +# Makefile for cawf + +# Define UNIX for vanilla Unix systems -- e.g., older DYNIX. +# +# Define UNIX and USG for System V, BSD 4.3 and for SunOS. +# +# USG may also be needed if the required string function prototypes -- +# e.g., for strrchr() -- are in rather than . +# +#DEFS = -DUNIX -DUSG +# +# Define STDLIB for systems that have -- e.g., AIX and +# SunOS. +# +# Redefine CAWFLIB by adding -DCAWFLIB=\"...\" to DEFS. +# +#DEFS = -DUNIX -DUSG -DCAWFLIB=\"/usr/local/lib/cawf\" +# +# Customize the install rule. +# +# -ansi and -pedantic are ANSI compliance options for the gcc compiler. +# Remove them if your compiler objects. +# +# If you're using xlc 2.1 on AIX 3.2 for the RISC/SYSTEM 6000, you +# must delete the definition of __STR__ (two leading and two trailing +# underscore characters), because the xlc 2.1 compiler incorrectly +# inlines string functions when compiling pass3.c. +# +#DEFS = -DUNIX -DSTDLIB -U__STR__ +# +# Unix systems that have a need MALLOCH defined, unless +# they also have a that provides a function prototype for +# malloc() and its relatives (most do). +# +#DEFS = -DUNIX -DMALLOCH + +DEFS = -DUNIX -DUSG -DSTDLIB + +CFLAGS = -i -f -wo -O ${DEFS} + +HDR = ansi.h cawf.h cawflib.h proto.h regexp.h regmagic.h + +SRC = cawf.c device.c error.c expand.c expr.c getopt.c macsup.c nreq.c \ + output.c pass2.c pass3.c regerror.c regexp.c store.c string.c + +OBJ = cawf.o device.o error.o expand.o expr.o getopt.o macsup.o nreq.o \ + output.o pass2.o pass3.o regerror.o regexp.o store.o string.o + +all: bsfilt cawf + +bsfilt: bsfilt.c + ${CC} ${CFLAGS} bsfilt.c -o $@ + install -S 4kw $@ + +cawf: ${OBJ} + ${CC} ${CFLAGS} ${OBJ} -o $@ + install -S 56k $@ + +clean: + rm -f *.o a.out core *errs bsfilt cawf + +${OBJ}: ${HDR} + +install: \ + /usr/bin/bsfilt /usr/bin/colcrt /usr/bin/cawf \ + /usr/bin/nroff /usr/lib/cawf /usr/lib/cawf/common \ + /usr/lib/cawf/device.cf /usr/lib/cawf/dumb.dev \ + /usr/lib/cawf/man.mac /usr/lib/cawf/me.mac \ + /usr/lib/cawf/ms.mac /usr/lib/cawf/mnx.mac + +/usr/bin/bsfilt: bsfilt + install -cs -o bin bsfilt $@ + +/usr/bin/colcrt: /usr/bin/bsfilt + install -l /usr/bin/bsfilt $@ + +/usr/bin/cawf: cawf + install -cs -o bin cawf $@ + +/usr/bin/nroff: /usr/bin/cawf + install -l /usr/bin/cawf $@ + +/usr/lib/cawf: + install -d -o bin /usr/lib/cawf + +/usr/lib/cawf/common: common + install -c -o bin common $@ + +/usr/lib/cawf/device.cf: device.cf + install -c -o bin device.cf $@ + +/usr/lib/cawf/dumb.dev: dumb.dev + install -c -o bin dumb.dev $@ + +/usr/lib/cawf/man.mac: man.mac + install -c -o bin man.mac $@ + +/usr/lib/cawf/me.mac: me.mac + install -c -o bin me.mac $@ + +/usr/lib/cawf/ms.mac: ms.mac + install -c -o bin ms.mac $@ + +/usr/lib/cawf/mnx.mac: mnx.mac + install -c -o bin mnx.mac $@ diff --git a/commands/cawf/README b/commands/cawf/README new file mode 100755 index 000000000..d30b67a0c --- /dev/null +++ b/commands/cawf/README @@ -0,0 +1,264 @@ +Cawf - nroff-like text formatter + +Cawf is a C version of awf, Henry Spencer's Amazingly Workable (text) +Formatter. (Awf is written in awk and appears in comp.sources.unix, +Volume 23, Issue 27.) Cawf and awf provide a usable subset of raw nroff +capabilities and the styles of the man(7), me(7) and ms(7) macro sets. +One of cawf's virtues is that it will run on PC clones under MS-DOS. It +is also, like awf, totally independent of any licensed Unix source code. + +This distribution contains complete source, make files for Unix and +MS-DOS, documentation (raw and formatted) and MS-DOS executables for cawf +and a companion output filters, bsfilt. + +This is the fourth distribution of cawf. Changes include: + + * Some rudimentary output device support has been added, via a device + configuration file. + + * The code has been converted to use unsigned characters. + + * An attempt has been made to make the code ANSI C compliant. + + * The following bugs have been fixed: + + A bug in the locating of the device file has been corrected, + so that the code performs as documented. + + Null macro arguments are ignored. + + Some unused arguments to local functions have been more + carefully type cast to avoid portability problems. + + * The .fl and .rn requests are now supported. + + * Limited support has been added for the non-break request control + character, the acute accent ('). + + * Argument count conditionals -- operating on \n(.$ -- may now use + the >= and <= operators in addition to [<=>]. + + * Macros may be terminated with "..", ".", "''" or "'". + + * String interpolation is performed if it is specified at the start + of the .ds request argument. + + * The .tr request has been enhanced to handle named characters and + string interpolation. + + * The SS macro is now included in man.mac. + + * The cawf version number is now displayed in the help output. + + * A limited -me macro set is included in me.mac. + +Changes to cawf to run under Minix: + + * The DOS binaries and make files have been removed. + + * Tabs stops changed from per 5 to per 8. + + * Bold and underline as it should be in device.cf. + + * Added .SB and .TA to man.mac. + + * Numerous prototypes added. + + +CONTENTS +-------- + +This Minix distribution of cawf includes: + + README this file + *.c and *.h source files to build cawf and bsfilt (bsfilt + removes Backspaces from cawf output) + bsfilt.1 nroff source for the bsfilt manual page + cawf.1 nroff source for the cawf manual page + common initialization file for CAWFLIB library + device.cf output device configuration file for CAWFLIB + library + dumb.dev device description file for CAWFLIB library + Makefile Unix-style make file + man.mac man(7) macros for CAWFLIB library + me.mac me(7) macros for CAWFLIB library + ms.mac ms(7) macros for CAWFLIB library + diffs Minix patches +#ifdef PUTTING_IT_ON_THE_NET + cawf + bsfilt binaries compiled under Minix-PC 1.5 using the ACK + ANSI C compiler using software floating point +#endif + + +LIBRARY +------- + +To use cawf, you must select a location for the CAWFLIB library files. The +distributed cawf binary expects to find them in /usr/local/lib/cawf but you +can alter that with the CAWFLIB environment variable, or you can change the +CAWFLIB #define in cawf.h and rebuild cawf from the sources. + +CAWFLIB contains a minimum of six files: + + common common raw nroff commands to get cawf started + dumb.dev a set of character definitions for a plain, "dumb" + ASCII device - e. g., the console display, a CRT or + a basic line printer + device.cf the output device configuration file + man.mac the man(7) macros + me.mac the me(7) macros + ms.mac the ms(7) macros + +You may want to add your own macro files to the library. Just name them +"m[your-name].mac", following the usual nroff naming convention for macro +files. + +If you have fancy output devices with special character specifications, you +may want to generate new *.dev files for them. Follow the format of dumb.dev +in making new character specifications. To define characters for a new +device, select a name prefix for it and create a file in CAWFLIB with the +name ".dev". To use the new file, set the TERM environment variable +to - e. g., when I test cawf on Unix, I need a vt100.dev, because +my TERM environment variable value is usually vt100. All I do is make +vt100.dev a symbolic link to dumb.dev. Even that isn't even necessary, +because cawf will use dumb.dev if it can't find TERM.dev. + +In addition to the character specifications possible through the *.dev files, +cawf provides one-time font selection and bold or italic face support for +output devices via its -d and -f options. Cawf can be directed to issue +specific device codes for bold and italic characters, and one font can be +specified for the entire document. Cawf has some built-in output device +support, and addition support is contained in the device configuration file, +device.cf. Additional devices may be defined in device.cf. + +It is not necessary to generate a new *.dev file for each output device +definition. Only when you need special character definitions do you need to +create a *.dev file. The dumb.dev file is adequate for most devices you +define in device.cf. + + +SOURCES +------- + +The Unix make file has some definitions that help tune it to the local +Unix environment: + + CAWFLIB is a string that can be used in lieu of changes + to cawf.h's CWFLIB #define. + + MALLOCH is a string that should be defined when a UNIX + environment has a , unless it also has a + with protoypes for malloc() and its + relatives. In the latter case, you should define + STDLIB, but you don't need to define MALLOCH. + + STDLIB indicates that standard library function prototype + definitions may be found in . + + STDLIB must be defined for MS-DOS Quick C. + + If STDLIB is not defined, the cawf sources try to + define their own library function return values. + + __STR__ The definition of this string must be deleted when + using the xlc 1.2 compiler on the RISC/System 6000 + under AIX 3.2. Put + + -U__STR__ + + in the Makefile DEFS string. This must be done + because the xlc 1.2 compiler does not correctly inline + string functions when compiling pass3.c. + + UNIX switches the build environment to Unix. You may also + have to decide about MALLOCH, STDLIB, __STR__ and USG + when you define UNIX. + + Do not define UNIX for MS-DOS Quick-C; do define + STDLIB. + + USG adjusts for System V. (UNIX must also be defined.) + + You may also need to define USG to select the proper + header file for string function prototypes. If UNIX + and USG are defined, "proto.h" selects ; + if only UNIX, . Cawf needs the more + complete set of definitions, including strchr() and + strrchr(). If #includes , as + is sometimes the case, define only UNIX. + +I have built and tested cawf in the UNIX context under AIX 3.2 (see the +note above on __STR__), BSD4.3-Tahoe, Sequent DYNIX, ETAV (SYSV 3.0), +NeXTStep 3.0, SunOS 4.1.1 and Ultrix 2.2. If you build under another Unix +variant, you may have to adjust the source code, header files and Makefile +to fit. Check the Makefile first for hints. + + +ANSI C COMPLIANCE +----------------- + +Some effort has been devoted to making the cawf sources ANSI C compliant. +The header file proto.h contains function prototypes that enable ANSI C +argument checking. The state of definition of the __STDC__ symbol is used +to select options that depend on strict adherence to the ANSI C standard -- +e.g., the need for the isascii() test before islower() or isupper(). If +your ANSI compiler doesn't define this variable when it's acting in strict +ANSI C mode, you may have to define it in the Makefile. + + +MS-DOS CONSIDERATIONS +--------------------- + +The MS-DOS version of cawf was created to run under the KornShell of the +Mortis Kern Systems Toolkit. One ramification of using MKS' ksh is that it +supports the separate standard error and standard output streams. Hence, +cawf blithely distributes its error messages to the standard error file, and +assumes the user's shell is capable of separating them from standard output. + +If you don't use the MKS KornShell, but do want to separate the output +streams, you'll have to modify the cawf source code. As a rudimentary aid, +cawf uses a separate stream pointer, Efs, for writing error output, but sets +it to stderr. You can change that process to open a separate error file and +set Efs to point to it. + + +COPYRIGHTS AND CREDITS +---------------------- + +The sources are copyrighted, but freely distributable under usual terms - +retention of credit, etc. + +Please acknowledge: + + AT&T for their public-domain release of getopt(3) at the 1985 + UNIFORUM conference; + + Chet Creider, Bob Hardy and Ted Campbell for their contributions + to font filtering; + + Henry Spencer for awf and his regular expression package; + + Andy Tanenbaum for his help in ANSI C compliance, including his + ansi.h header file from Minix. + +Henry says about awf, "I can't believe I really wrote this." Those are +my sentiments exactly about cawf, but I also understand that necessity +sometimes forces us to do what we would prefer to avoid. + + +BUGS AND ENHANCEMENTS +--------------------- + +I'll be glad to hear about bugs and needs for enhancements, but make no +promises about delivering fixes or upgrades in response. + +Vic Abell +24 November 1992 + + +MINIX SPECIFIC TINKERING +------------------------ + +Kees J. Bot +26 November 1992 diff --git a/commands/cawf/ansi.h b/commands/cawf/ansi.h new file mode 100755 index 000000000..78fffbe63 --- /dev/null +++ b/commands/cawf/ansi.h @@ -0,0 +1,56 @@ +/* The header attempts to decide whether the compiler has enough + * conformance to Standard C for Minix to take advantage of. If so, the + * symbol _ANSI is defined (as 31415). Otherwise _ANSI is not defined + * here, but it may be defined by applications that want to bend the rules. + * The magic number in the definition is to inhibit unnecessary bending + * of the rules. (For consistency with the new '#ifdef _ANSI" tests in + * the headers, _ANSI should really be defined as nothing, but that would + * break many library routines that use "#if _ANSI".) + + * If _ANSI ends up being defined, a macro + * + * _PROTOTYPE(function, params) + * + * is defined. This macro expands in different ways, generating either + * ANSI Standard C prototypes or old-style K&R (Kernighan & Ritchie) + * prototypes, as needed. Finally, some programs use _CONST, _VOIDSTAR etc + * in such a way that they are portable over both ANSI and K&R compilers. + * The appropriate macros are defined here. + */ + +#ifndef _ANSI_H +#define _ANSI_H + +#if __STDC__ == 1 +#define _ANSI 31459 /* compiler claims full ANSI conformance */ +#endif + +#ifdef __GNUC__ +#define _ANSI 31459 /* gcc conforms enough even in non-ANSI mode */ +#endif + +#ifdef _ANSI + +/* Keep everything for ANSI prototypes. */ +#define _PROTOTYPE(function, params) function params + +#define _VOIDSTAR void * +#define _VOID void +#define _CONST const +#define _VOLATILE volatile +#define _SIZET size_t + +#else + +/* Throw away the parameters for K&R prototypes. */ +#define _PROTOTYPE(function, params) function() + +#define _VOIDSTAR void * +#define _VOID void +#define _CONST +#define _VOLATILE +#define _SIZET int + +#endif /* _ANSI */ + +#endif /* ANSI_H */ diff --git a/commands/cawf/bsfilt.c b/commands/cawf/bsfilt.c new file mode 100755 index 000000000..3231b3df2 --- /dev/null +++ b/commands/cawf/bsfilt.c @@ -0,0 +1,211 @@ +/* + * bsfilt.c - a colcrt-like processor for cawf(1) + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include + +#ifdef UNIX +# ifdef USG +#include +# else /* not USG */ +#include +# endif /* USG */ +#else /* not UNIX */ +#include +#endif /* UNIX */ + +#include + +#include "ansi.h" + +#define MAXLL 2048 /* ridiculous maximum line length */ + +int Dash = 1; /* underline with dashes */ +int Dp = 0; /* dash pending */ +int Lc = 0; /* line count */ +char *Pname; /* program name */ +unsigned char Ulb[MAXLL]; /* underline buffer */ +int Ulx = 0; /* underline buffer index */ + +_PROTOTYPE(void Putchar,(int ch)); + +main(argc, argv) + int argc; + char *argv[]; +{ + int ax = 1; /* argument index */ + unsigned char c; /* character buffer */ + FILE *fs; /* file stream */ + int nf = 0; /* number of files processed */ + unsigned char pc; /* previous character */ + int under = 0; /* underline */ +/* + * Save program name. + */ + if ((Pname = strrchr(argv[0], '/')) != NULL) + Pname++; + else if ((Pname = strrchr(argv[0], '\\')) != NULL) + Pname++; + else + Pname = argv[0]; +/* + * Process options. + */ + if (argc > 1 && argv[1][0] == '-') { + switch (argv[1][1]) { + /* + * "-U" - underline with dashes. + */ + case 'U': + Dash = 0; + under = 1; + break; + /* + * "-" - do no underlining at all. + */ + case '\0': + Dash = under = 0; + break; + default: + (void) fprintf(stderr, + "%s usage: [-] [-U] [file]\n", Pname); + exit(1); + } + ax++; + } +/* + * Process files. Read standard input if no files names. + */ + + while (ax < argc || nf == 0) { + if (ax >= argc) + fs = stdin; + else { +#ifdef UNIX + if ((fs = fopen(argv[ax], "r")) == NULL) +#else + if ((fs = fopen(argv[ax], "rt")) == NULL) +#endif + { + (void) fprintf(stderr, "%s: can't open %s\n", + Pname, argv[ax]); + exit(1); + } + ax++; + } + nf++; + /* + * Read input a character at a time. + */ + for (pc = '\0';;) { + c = (unsigned char)fgetc(fs); + if (feof(fs)) + break; + switch(c) { + + case '\n': + if (pc) + Putchar((int)pc); + Putchar('\n'); + pc = '\0'; + break; + + case '\b': + if (pc == '_') { + if (under) { + putchar(pc); + putchar('\b'); + } else if (Dash) + Dp = 1; + } + pc = '\0'; + break; + + default: + if (pc) + Putchar((int)pc); + pc = c; + } + } + if (pc) { + Putchar((int)pc); + Putchar((int)'\n'); + } + } + exit(0); +} + + +/* + * Putchar(ch) - put a character with possible underlining + */ + +void +Putchar(ch) + int ch; +{ + int i; /* temporary index */ + + if ((unsigned char)ch == '\n') { +/* + * Handle end of line. + */ + putchar('\n'); + if (Ulx) { + while (Ulx && Ulb[Ulx-1] == ' ') + Ulx--; + if (Ulx) { + for (i = 0; i < Ulx; i++) + putchar(Ulb[i]); + putchar('\n'); + } + } + Dp = Ulx = 0; + Lc++; + return; + } +/* + * Put "normal" character. + */ + putchar((unsigned char)ch); + if (Dash) { + + /* + * Handle dash-type underlining. + */ + if (Ulx >= MAXLL) { + (void) fprintf(stderr, + "%s: underline for line %d > %d characters\n", + Pname, Lc, MAXLL); + exit(1); + } + Ulb[Ulx++] = Dp ? '-' : ' '; + Dp = 0; + } +} diff --git a/commands/cawf/cawf.c b/commands/cawf/cawf.c new file mode 100755 index 000000000..dfddbf248 --- /dev/null +++ b/commands/cawf/cawf.c @@ -0,0 +1,488 @@ +/* + * cawf - a C version of Henry Spencer's awf(1), the Amazingly + * Workable (text) Formatter + * + * V. Abell, Purdue University Computing Center + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +static char Version[] = "4.0"; + +#include "cawf.h" + +#include +#ifndef UNIX +#include +#include +#include +#include +#include +#endif + + +main(argc, argv) + int argc; + char *argv[]; +{ + char *ep; /* environment pointer */ + int fff = 0; /* final form feed status */ + char **files; /* file names */ + int help = 0; /* help status */ + int i; /* temporary index */ + size_t l; /* length */ + char *lib = CAWFLIB; /* library path */ + int libl; /* library path length */ + int mac = 0; /* macro specification status */ + int nf = 0; /* number of files */ + char *np; /* name pointer */ + int pc; /* prolog count */ + struct stat sbuf; /* stat buffer */ +/* + * Save program name. + */ + if ((Pname = strrchr(argv[0], '\\')) != NULL) + Pname++; + else if ((Pname = strrchr(argv[0], '/')) != NULL) + Pname++; + else + Pname = argv[0]; +/* + * Set error file stream pointer. + */ + Efs = stderr; +/* + * Get library name. + */ + if ((np = getenv("CAWFLIB")) != NULL) + lib = np; + libl = strlen(lib); +/* + * Get device file name. + */ + for (ep = getenv("TERM");; ep = NULL) { + if (ep == NULL || *ep == '\0') + ep = "dumb"; + l = libl + 1 + strlen(ep) + strlen(".dev") + 1; + if ((np = malloc(l)) == NULL) + Error(FATAL, NOLINE, + " no string space for device file: ", ep); + (void) sprintf(np, "%s/%s.dev", lib, ep); + if (stat(np, &sbuf) == 0) + break; + if (strcmp(ep, "dumb") == 0) + Error(FATAL, NOLINE, " no dumb.dev file in ", lib); + (void) free(np); + } + if ((files = malloc((argc + 2) * sizeof(files[0]))) == NULL) + Error(FATAL, NOLINE, " no space for file list", + NULL); + files[nf++] = np; +/* + * Get common text file name. + */ + l = libl + 1 + strlen("common") + 1; + if ((np = malloc(l)) == NULL) + Error(FATAL, NOLINE, " no string space for common file name", + NULL); + (void) sprintf(np, "%s/common", lib); + files[nf++] = np; +/* + * Process options. + */ + while ((i = getopt(argc, argv, "c:d:ef:hm:")) != EOF) { + switch (i) { + /* + * -c> + */ + case 'c': + Devconf = optarg; + break; + /* + * -d -- define output device name + * + * The default output device name is NORMAL -- i.e., a device that + * does bold face with backspace and overprinting and italic face with + * underscore. NORMAL is usually a terminal device. + * + * There is a built-in device, named ANSI, that does bold face with + * the ANSI shadow mode and italic face with the ANSI underscore mode. + * ANSI is normally a terminal device that supports the ANSI shadow + * and underscore modes. + * + * There is a built-in output device, named NONE, that does nothing + * at all for the bold or italic faces. This is usually a terminal + * device. + * + * All other device names must match a stanza in the device + * configuration file. + */ + case 'd': + Device = optarg; + break; + /* + * -e -- eject: issue final form feed + */ + case 'e': + fff = 1; + break; + /* + * -f -- define font name for the output + * device (from device configuration + * file) + */ + case 'f': + Devfont = optarg; + break; + /* + * -h -- display help (usage) + */ + case 'h': + help = 1; + break; + /* + * -m + * + * Special support is provided for -man, -me and -ms. + */ + case 'm': + if (mac) { + Error(WARN, NOLINE, + "multiple macro file declaration", + NULL); + break; + } + l = libl + 2 + strlen(optarg) + strlen(".mac") + 1; + if ((np = malloc(l)) == NULL) + Error(FATAL, NOLINE, " no string space for ", + argv[1]); + (void) sprintf(np, "%s/m%s.mac", lib, optarg); + files[nf++] = np; + if (strcmp(optarg, "an") == 0) + Marg = MANMACROS; + else if (strcmp(optarg, "s") == 0 + || strcmp(optarg, "e") == 0) + Marg = MSMACROS; + mac++; + break; + /* + * Option not recognized by getopt(). + */ + case '?': + Err = 1; + } + } + if (Defdev()) + Err++; + if (help || Err) { + (void) fprintf(stderr, + "%s %s usage: [-c] [-d] [-e] [-f] [-h] [-m] file...\n", + Pname, Version); + (void) fprintf(stderr, + "\t-c is the device configuration file path\n"); + (void) fprintf(stderr, + "\t-d is the output device name\n"); + (void) fprintf(stderr, + "\t (default = NORMAL, using \\b for bold and italic)\n"); + (void) fprintf(stderr, + "\t (built-ins = ANSI, NONE and NORMAL)\n"); + (void) fprintf(stderr, + "\t-e issue eject after last page\n"); + (void) fprintf(stderr, + "\t-f is the output device font name\n"); + (void) fprintf(stderr, + "\t-h display help (this output)\n"); + (void) fprintf(stderr, + "\t-m m is the macro file name\n"); + (void) fprintf(stderr, + "\tfile ... source file names\n"); + exit(Err); + } + if (mac == 0) { + + /* + * No macroes - enable Bold, Italic, Roman and Courier fonts. + */ + for (i = 0; Fcode[i].nm; i++) { + switch (Fcode[i].nm) { + case 'B': + case 'I': + case 'R': + case 'C': + Fcode[i].status = '1'; + } + } + } +/* + * Add user-supplied file names. + */ + pc = nf; + if (optind >= argc) { + files[nf++] = NULL; /* STDIN */ + } else { + while (optind < argc) + files[nf++] = argv[optind++]; + } +/* + * Make sure all input files are accessible. + */ + for (i = 0; i < nf; i++) { + if (files[i] != NULL) { + if (stat(files[i], &sbuf) != 0) + Error(WARN, NOLINE, " can't find ", files[i]); + } + } + if (Err) + exit(1); +/* + * Miscellaneous initialization. + */ + + for (i = 0; ; i++) { + if (Pat[i].re == NULL) + break; + if ((Pat[i].pat = regcomp(Pat[i].re)) == NULL) + Error(WARN, NOLINE, Pat[i].re, " regcomp failure"); + } + if ((i = Findscale((int)'n', 0.0, 0)) < 0) + Error(WARN, NOLINE, " can't find Scale['n']", NULL); + Scalen = Scale[i].val; + if ((i = Findscale((int)'u', 0.0, 0)) < 0) + Error(WARN, NOLINE, " can't find Scale['u']", NULL); + Scaleu = Scale[i].val; + if ((i = Findscale((int)'v', 0.0, 0)) < 0) + Error(WARN, NOLINE, " can't find Scale['v']", NULL); + Scalev = Scale[i].val; + (void) Findstr((unsigned char *)"CH", (unsigned char *)"= % -", 1); + Cont = Newstr((unsigned char *)" "); + Contlen = 1; + if ((Trtbl = (unsigned char *)malloc(256)) == NULL) + Error(WARN, NOLINE, " can't allocate translate table space", + NULL); + else { + *Trtbl = ' '; + for (i = 1; i < 256; i++) + Trtbl[i] = (unsigned char) i; + } + if (Err) + exit(1); +/* + * Here begins pass1 of awf - reading input lines and expanding macros. + */ + +/* + * Output prolog. + */ + if (Fstr.i) { + for (i = 0; i < Fstr.il; i++) { + Charput((int)Fstr.i[i]); + } + } + Macro((unsigned char *)".^x"); + Macro((unsigned char *)".^b"); + Macro((unsigned char *)".^# 1 "); +/* + * Read input files. + */ + for (i = 0; i < nf; i++) { + Dowarn = (i >= pc); + if (files[i] == NULL) { + np = "stdin"; + Ifs = stdin; + } else { +#ifdef UNIX + if ((Ifs = fopen(files[i], "r")) == NULL) +#else + if ((Ifs = fopen(files[i], "rt")) == NULL) +#endif + Error(FATAL, NOLINE, " can't open ", files[i]); + np = files[i]; + } + if (i >= pc) { + (void) sprintf((char *)Line, ".^# 1 %s", np); + Macro(Line); + NR = 0; + } + Fsp = 0; + do { + while (fgets((char *)Line, MAXLINE, Ifs) != NULL) { + NR++; + if ((np = strrchr((char *)Line, '\n')) != NULL) + *np = '\0'; + else + Line[MAXLINE-1] = '\0'; + Macro(Line); + } + if (i >= pc) + Macro((unsigned char *)".^e"); + if (Ifs != stdin) + (void) fclose(Ifs); + if (Fsp > 0) { + Free(&Inname); + Inname = Inn_stk[Fsp-1]; + NR = NR_stk[Fsp-1]; + Ifs = Ifs_stk[Fsp-1]; + } + } while (Fsp-- > 0); + } + Macro(NULL); + if (fff) + Charput((int)'\f'); + exit(Err); +} + + +/* + * Macro(inp) - process a possible macro statement + * pass non-macros and macros alike to pass 2 + */ + +void +Macro(inp) + unsigned char *inp; /* possible macro statement pointer */ +{ + unsigned char c[2]; /* characters */ + int endm; /* end of macro status */ + FILE *fs; /* temporary file stream */ + int i, j, k; /* temporary indexes */ + int mx; /* Macrotab[] index */ + int req; /* request character status */ + unsigned char *s1, *s2; /* temporary string pointers */ + + if (inp == NULL) { + Pass2(NULL); + return; + } + req = (*inp == '.' || *inp == '\'') ? 1 : 0; +/* + * Check for file name designator. + */ + if (req && inp[1] == '^' && inp[2] == '#') { + Free(&Inname); + Inname = Field(3, inp, 1); + F = NULL; + Pass2(inp); + return; + } +/* + * Check for source command - "^[.']so". + */ + if (req && inp[1] == 's' && inp[2] == 'o') { + if ((s1 = Field(2, inp, 1)) == NULL) { + Error(WARN, LINE, " no file specified", NULL); + return; + } + if ((fs = fopen((char *)s1, "r")) == NULL) { + Error(WARN, LINE, " can't open", NULL); + return; + } + if (Fsp >= MAXFSTK) { + (void) fclose(fs); + Error(WARN, LINE, " nesting too deep", NULL); + return; + } + Ifs_stk[Fsp] = Ifs; + Ifs = fs; + Inn_stk[Fsp] = Inname; + Inname = F; + F = NULL; + NR_stk[Fsp++] = NR; + NR = 0; + return; + } + /* + * Check for ignore. + */ + if (req && inp[1] == 'i' && inp[2] == 'g') { + while (fgets((char *)inp, MAXLINE, Ifs) != NULL) { + NR++; + if (inp[0] == '.' && inp[1] == '.') break; + } + return; + } + /* + * Check for start of macro definition. + */ + if (req && inp[1] == 'd' && inp[2] == 'e') { + if (inp[3] != ' ' || inp[4] == '\0') { + Error(WARN, LINE, " illegal macro definition", NULL); + return; + } + c[0] = inp[4]; + c[1] = inp[5]; + Curmx = Findmacro(c, 1); + return; + } +/* + * Check for macro text. Remove double backslashes. + */ + if (req && (inp[1] == '\0' || (inp[2] == '\0' && inp[0] == inp[1]))) + endm = 1; + else + endm = 0; + if (Curmx >= 0 && !endm) { + if (Mtx >= MAXMTXT) + Error(FATAL, LINE, " out of macro text space", NULL); + if ((s1 = (unsigned char *)strchr((char *)inp, '\\')) == NULL) + Macrotxt[Mtx] = Newstr(inp); + else { + for (s1 = Pass1ln, s2 = inp;; s1++) { + if ((*s1 = *s2++) == '\0') + break; + if (*s1 == '\\' && *s2 == '\\') + s2++; + } + Macrotxt[Mtx] = Newstr(Pass1ln); + } + if (Macrotab[Curmx].bx == -1) + Macrotab[Curmx].bx = Mtx; + Mtx++; + Macrotab[Curmx].ct++; + return; + } +/* + * Check for end of macro. + */ + if (Curmx >= 0 && endm) { + Curmx = -1; + (void) sprintf((char *)Pass1ln, ".^# %d %s", NR, Inname); + Pass2(Pass1ln); + return; + } + /* + * Check for conditionals and macro expansions. + */ + if (req + && (((mx = Findmacro(inp+1, 0)) != -1) || regexec(Pat[0].pat, inp))) { + Expand(inp); + return; + } +/* + * None of the above: forward the line. + */ + Pass2(inp); +} diff --git a/commands/cawf/cawf.h b/commands/cawf/cawf.h new file mode 100755 index 000000000..3e3851838 --- /dev/null +++ b/commands/cawf/cawf.h @@ -0,0 +1,265 @@ +/* + * cawf.h - definitions for cawf(1) + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include +#ifdef UNIX +#include +#else +#include +#endif +#include "regexp.h" +#include "cawflib.h" +#include "proto.h" + +#define DEVCONFIG "device.cf" /* device configuration file */ +#define ESC '\033' /* ESCape character */ +#define MAXEXP 30 /* maximum expressions + * (and TABs) */ +#define MAXFSTK 5 /* maximum file stack + * (for .so) */ +#define MAXHYCH 10 /* maximum hyphen characters */ +#define MAXLINE 512 /* maximum line length */ +#define MAXMACRO 100 /* maximum number of macros */ +#define MAXMTXT 1024 /* maximum macro text lines */ +#define MAXNHNR 10 /* maximum ".NH" numbers + * (but 0 not used) */ +#define MAXNR 50 /* maximum number reg */ +#define MAXOLL 512 /* maximum output line length */ +#define MAXSCH 256 /* maximum special characters */ +#define MAXSP 25 /* maximum stack pointer (for + * nesting of macro calls) */ +#define MAXSTR 100 /* maximum ".ds" strings */ + +/* + * Output line adjustment modes + */ + +#define LEFTADJ 0 +#define RIGHTADJ 1 +#define BOTHADJ 2 + +/* + * Error handling codes + */ + +#define FATAL 0 /* fatal error */ +#define LINE 0 /* display line */ +#define NOLINE 1 /* don't display line */ +#define WARN 1 /* warning error */ + +/* + * Padding directions + */ + +#define PADLEFT 0 /* pad from left */ +#define PADRIGHT 1 /* pad from right */ + +/* + * Pass 3 signal codes + */ + +#define NOBREAK -1 +#define DOBREAK -2 +#define MESSAGE -3 + +/* + * Macro argument types + */ + +#define MANMACROS 1 /* -man */ +#define MSMACROS 2 /* -ms */ + + +struct fcode { + unsigned char nm; /* font name character */ + unsigned char status; /* status */ +}; + +struct fontstr { /* font control strings */ + + unsigned char *i; /* font initialization string */ + int il; /* length of *i */ + unsigned char *b; /* bold */ + int bl; /* length of *bb */ + unsigned char *it; /* italic */ + int itl; /* length of *itb */ + unsigned char *r; /* roman string */ + int rl; /* length of *r */ +}; + +struct hytab { + unsigned char font; /* font name character */ + int len; /* effective length */ + unsigned char *str; /* value string */ +}; + +struct macro { + unsigned char name[2]; /* macro name */ + int bx; /* beginning Macrotxt[] index */ + int ct; /* index count */ +}; + +struct nbr { + unsigned char nm[2]; /* register name */ + int val; /* value */ +}; + +struct parms { + char nm[2]; /* parameter name */ + char *cmd; /* pass 3 command */ + int val; /* current value */ + int prev; /* previous value */ +}; + +struct rx { + char *re; /* regular expression */ + struct regexp *pat; /* compiled pattern */ +}; + +struct scale { + unsigned char nm; /* scale factor name */ + double val; /* value */ +}; + +struct schtab { + unsigned char nm[2]; /* character name */ + int len; /* effective length */ + unsigned char *str; /* value string */ +}; + +struct str { + unsigned char nm[2]; /* string name */ + unsigned char *str; /* string value */ +}; + +extern int Adj; /* output line adjustment mode */ +extern unsigned char *Aftnxt; /* action after next line */ +extern unsigned char *Args[]; /* macro arguments */ +extern unsigned char *Argstack[]; /* stack for Expand()'s "args[]" */ +extern int Backc; /* last word ended with '\\c' */ +extern int Botmarg; /* bottom margin */ +extern int Centering; /* centering count */ +extern int Condstack[]; /* stack for Expand()'s "cond" */ +extern unsigned char *Cont; /* continue line append */ +extern int Contlen; /* continue line append length */ +extern int Curmx; /* current macro name */ +extern char *Device; /* output device name */ +extern char *Devconf; /* device configuration file path */ +extern char *Devfont; /* output device font */ +extern int Divert; /* diversion status */ +extern FILE *Efs; /* error file stream pointer */ +extern unsigned char *Eol; /* end of line information */ +extern int Eollen; /* end of line length */ +extern int Err; /* error flag */ +extern unsigned char *F; /* field value */ +extern struct fcode Fcode[]; /* font codes */ +extern int Fill; /* fill status */ +extern unsigned char Font[]; /* current font */ +extern int Fontctl; /* output font control */ +extern char Fontstat; /* output font status */ +extern int Fph; /* first page header status */ +extern int Fsp; /* files stack pointer (for .so) */ +extern struct fontstr Fstr; /* font control strings */ +extern unsigned char *Ftc; /* center footer */ +extern unsigned char *Ftl; /* left footer */ +extern unsigned char *Ftr; /* right footer */ +extern unsigned char *Hdc; /* center header */ +extern int Hdft; /* header/footer status */ +extern unsigned char *Hdl; /* left header */ +extern unsigned char *Hdr; /* right header */ +extern FILE *Ifs; /* input file stream */ +extern FILE *Ifs_stk[]; /* Ifs stack */ +extern int Ind; /* indentation amount */ +extern unsigned char *Inname; /* input file name */ +extern unsigned char *Inn_stk[]; /* Inname stack */ +extern struct hytab Hychar[]; /* hyphen characters */ +extern int LL; /* line length */ +extern unsigned char Line[]; /* input line */ +extern int Lockil; /* pass 2 line number is locked + * (processing is inside macro) */ +extern int Marg; /* macro argument - man, ms, etc. */ +extern struct macro Macrotab[]; /* macro table */ +extern int Macrostack[]; /* stack for Expand()'s "macro" */ +extern unsigned char *Macrotxt[]; /* macro text */ +extern int Mtx; /* macro text index */ +extern int Mxstack[]; /* stack for Expand()'s "mx" */ +extern int Nhnr[]; /* ".NH" numbers */ +extern int Nhy; /* number of Hychar[] entries */ +extern int Nleftstack[]; /* stack for Expand()'s "nleft" */ +extern int Nmac; /* number of macros */ +extern int Nnr; /* number of Numb[] entries */ +extern int Nospmode; /* no space mode */ +extern int Nparms; /* number of Parms[] entries */ +extern int NR; /* number of record, ala awk */ +extern int NR_stk[]; /* NR stack */ +extern int Nsch; /* number of Schar[] entries */ +extern int Nstr; /* number of entries in Str[] */ +extern int Ntabs; /* number of TAB positions */ +extern struct nbr Numb[]; /* number registers */ +extern int Nxtln; /* next line number */ +extern char *optarg; /* getopt(3) argument pointer */ +extern int optind; /* getopt(3) index */ +extern int Outll; /* output line length */ +extern unsigned char Outln[]; /* output line */ +extern int Outlx; /* output line index */ +extern int P2il; /* pass 2 input line number */ +extern unsigned char *P2name; /* pass 2 input file name */ +extern int P3fill; /* pass 3 fill status */ +extern int Padchar[]; /* padding character locations */ +extern int Padfrom; /* which end to pad from */ +extern int Padx; /* Padchar[] index */ +extern struct parms Parms[]; /* parameter registers */ +extern unsigned char Pass1ln[]; /* pass 1 output line */ +extern unsigned char Pass2ln[]; /* pass 2 output line */ +extern struct rx Pat[]; /* compiled regexp patterns */ +extern int Pglen; /* page length */ +extern int Pgoff; /* page offset */ +extern char *Pname; /* program name */ +extern unsigned char Prevfont; /* previous font */ +extern int Ptrstack[]; /* stack for Expand()'s "ptr" */ +extern struct scale Scale[]; /* scaling factors */ +extern double Scalen; /* 'n' scaling factor */ +extern double Scaleu; /* 'u' scaling factor */ +extern double Scalev; /* 'v' scaling factor */ +extern struct schtab Schar[]; /* special characters */ +extern int Sp; /* stack pointer */ +extern struct str Str[]; /* ".ds" strings */ +extern int Sx; /* string index */ +extern int Tabs[]; /* TAB positions */ +extern int Thispg; /* this page number */ +extern int Tind; /* temporary indentation amount */ +extern int Topmarg; /* top margin */ +extern unsigned char *Trtbl; /* .tr table */ +extern int Uhyph; /* hyphen usage state */ +extern int Vspace; /* vertical (inter-text-line) spacing */ +extern unsigned char Word[]; /* pass 2 word buffer */ +extern int Wordl; /* effective length of Word[] */ +extern int Wordx; /* Word[] index */ +extern int Dowarn; /* Enables warnings when true */ diff --git a/commands/cawf/cawflib.h b/commands/cawf/cawflib.h new file mode 100755 index 000000000..32bf18bcf --- /dev/null +++ b/commands/cawf/cawflib.h @@ -0,0 +1,39 @@ +/* + * cawflib.h - definition of cawf's library path + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#ifndef CAWFLIB +#ifdef UNIX +#define CAWFLIB "/usr/lib/cawf" /* UNIX library location */ +#else +#define CAWFLIB "c:/sys/lib/cawf" /* PC-DOS library location */ +#endif +#endif + /* (CAWFLIB environment + * variable over-rides it) */ diff --git a/commands/cawf/common b/commands/cawf/common new file mode 100755 index 000000000..a18c76c30 --- /dev/null +++ b/commands/cawf/common @@ -0,0 +1,7 @@ +.\" Common startup code, fully device-independent. +.\" -------------------------------- +.fi +.ce 0 +.ta +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +.in 0 +.ti 0 diff --git a/commands/cawf/device.c b/commands/cawf/device.c new file mode 100755 index 000000000..6d5387c89 --- /dev/null +++ b/commands/cawf/device.c @@ -0,0 +1,415 @@ +/* + * device.c -- cawf(1) output device support functions + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include "cawf.h" +#include + +_PROTOTYPE(static unsigned char *Convstr,(char *s, int *len)); +_PROTOTYPE(static int Convfont,(char *nm, char *s, char **fn, + unsigned char **fi)); + +#ifndef UNIX +#define strcasecmp strcmpi +#endif + + + +/* + * Convstr(s, len) - convert a string + */ + +static unsigned char * +Convstr(s, len) + char *s; /* input string */ + int *len; /* length of result */ +{ + int c; /* character assembly */ + unsigned char *cp; /* temporary character pointer */ + char *em; /* error message */ + int i; /* temporary index */ + int l; /* length */ + unsigned char *r; /* result string */ +/* + * Make space for the result. + */ + if ((r = (unsigned char *)malloc(strlen((char *)s) + 1)) == NULL) { + (void) fprintf(stderr, "%s: out of string space at %s\n", + Pname, s); + return(NULL); + } +/* + * Copy the input string to the result, processing '\\' escapes. + */ + for (cp = r, l = 0; *s;) { + switch (*s) { + + case '\\': + s++; + if (*s >= '0' && *s <= '7') { + /* + * '\xxx' -- octal form + */ + for (c = i = 0; i < 3; i++, s++) { + if (*s < '0' || *s > '7') { + em = "non-octal char"; +bad_string: + (void) fprintf(stderr, + "%s: %s : %s\n", + Pname, em, (char *)r); + return(NULL); + } + c = (c << 3) + *s - '0'; + } + if (c > 0377) { + em = "octal char > 0377"; + goto bad_string; + } + *cp++ = c; + l++; + } else if (*s == 'x') { + /* + * '\xyy' -- hexadecimal form + */ + s++; + for (c = i = 0; i < 2; i++, s++) { +#if defined(__STDC__) + if ( ! isalpha(*s) && ! isdigit(*s)) +#else + if ( ! isascii(*s) && ! isalpha(*s) + && ! isdigit(*s)) +#endif + { +non_hex_char: + em = "non-hex char"; + goto bad_string; + } + c = c << 4; + if (*s >= '0' && *s <= '9') + c += *s - '0'; + else if ((*s >= 'a' && *s <= 'f') + || (*s >= 'A' && *s <= 'F')) + c += *s + 10 - + (isupper(*s) ? 'A' : 'a'); + else + goto non_hex_char; + } + *cp++ = (unsigned char)c; + l++; + } else if (*s == 'E' || *s == 'e') { + /* + * '\E' or '\e' -- ESCape + */ + *cp++ = ESC; + l++; + s++; + } else if (*s == '\0') { + em = "no char after \\"; + goto bad_string; + } else { + /* + * escaped character (for some reason) + */ + *cp++ = *s++; + l++; + } + break; + /* + * Copy a "normal" character. + */ + default: + *cp++ = *s++; + l++; + } + } + *cp = '\0'; + *len = l; + return(r); +} + + +/* + * Convfont(nm, s, fn, fi) - convert a font for a device + */ + +static int +Convfont(nm, s, fn, fi) + char *nm; /* output device name */ + char *s; /* font definition string */ + char **fn; /* font name address */ + unsigned char **fi; /* initialization string address */ +{ + char *cp; /* temporary character pointer */ + int len; /* length */ +/* + * Get the font name, allocate space for it and allocate space for + * a font structure. + */ + if ((cp = strchr(s, '=')) == NULL) { + (void) fprintf(stderr, "%s: bad %s font line format: %s\n", + Pname, nm, s); + return(0); + } + if ((*fn = (char *)malloc(cp - s + 1)) == NULL) { + (void) fprintf(stderr, "%s: no space for %s font name %s\n", + Pname, nm, s); + return(0); + } + (void) strncpy(*fn, s, cp - s); + (*fn)[cp - s] = '\0'; +/* + * Assmble the font initialization string. + */ + if ((*fi = Convstr(cp + 1, &len)) == NULL) + return(0); + return(len); +} + + +/* + * Defdev() - define the output device + */ + +int +Defdev() +{ + unsigned char *fi = NULL; /* last font initialization string */ + char *fn = NULL; /* font name */ + int fd = 0; /* found-device flag */ + FILE *fs; /* file stream */ + int err = 0; /* errror count */ + int i; /* temporary index */ + int len; /* length */ + char line[MAXLINE]; /* line buffer */ + char *p; /* output device configuration file */ + char *s; /* temporary string pointer */ +/* + * Check for the built-in devices, ANSI, NONE or NORMAL (default). + */ + Fstr.b = Fstr.i = Fstr.it = Fstr.r = NULL; + Fstr.bl = Fstr.il = Fstr.itl = Fstr.rl = 0; + if (Device == NULL || strcasecmp(Device, "normal") == 0) { + Fontctl = 0; +check_font: + if (Devfont) { + (void) fprintf(stderr, + "%s: font %s for device %s illegal\n", + Pname, Devfont, Device ? Device : "NORMAL"); + return(1); + } + return(0); + } + Fontctl = 1; + if (strcasecmp(Device, "ansi") == 0) { + Fstr.b = Newstr((unsigned char *)"x[1m"); + Fstr.it = Newstr((unsigned char *)"x[4m"); + Fstr.r = Newstr((unsigned char *)"x[0m"); + Fstr.b[0] = Fstr.it[0] = Fstr.r[0] = ESC; + Fstr.bl = Fstr.itl = Fstr.rl = 4; + goto check_font; + } + if (strcasecmp(Device, "none") == 0) + goto check_font; +/* + * If a device configuration file path is supplied, use it. + */ + if (Devconf) + p = Devconf; + else { + + /* + * Use the CAWFLIB environment if it is defined. + */ + if ((p = getenv("CAWFLIB")) == NULL) + p = CAWFLIB; + len = strlen(p) + 1 + strlen(DEVCONFIG) + 1; + if ((s = (char *)malloc(len)) == NULL) { + (void) fprintf(stderr, "%s: no space for %s name\n", + Pname, DEVCONFIG); + return(1); + } + (void) sprintf(s, "%s/%s", p, DEVCONFIG); + p = s; + } +/* + * Open the configuration file. + */ +#ifdef UNIX + if ((fs = fopen(p, "r")) == NULL) +#else + if ((fs = fopen(p, "rt")) == NULL) +#endif + { + (void) fprintf(stderr, "%s: can't open config file: %s\n", + Pname, p); + return(1); + } + *line = ' '; +/* + * Look for a device definition line -- a line that begins with a name. + */ + while ( ! feof(fs)) { + if (*line == '\t' || *line == '#' || *line == ' ') { + (void) fgets(line, MAXLINE, fs); + continue; + } + if ((s = strrchr(line, '\n')) != NULL) + *s = '\0'; + else + line[MAXLINE-1] = '\0'; + /* + * Match device name. + */ + if (strcmp(Device, line) != 0) { + (void) fgets(line, MAXLINE, fs); + continue; + } + fd = 1; + /* + * Read the parameter lines for the device. + */ + while (fgets(line, MAXLINE, fs) != NULL) { + if (*line == ' ') { + for (i = 1; line[i] == ' '; i++) + ; + } else if (*line == '\t') + i = 1; + else + break; +#if defined(__STDC__) + if ( ! isalpha(line[i]) +#else + if ( ! isascii(line[i]) || ! isalpha(line[i]) +#endif + || line[i+1] != '=') + break; + if ((s = strrchr(line, '\n')) != NULL) + *s = '\0'; + else + line[MAXLINE-1] = '\0'; + switch (line[i]) { + /* + * \tb= + */ + case 'b': + if (Fstr.b != NULL) { + (void) fprintf(stderr, + "%s: dup bold for %s in %s: %s\n", + Pname, Device, p, line); + (void) free(Fstr.b); + Fstr.b = NULL; + } + if ((Fstr.b = Convstr(&line[i+2], &Fstr.bl)) + == NULL) + err++; + break; + /* + * \ti= + */ + case 'i': + if (Fstr.it != NULL) { + (void) fprintf(stderr, + "%s: dup italic for %s in %s: %s\n", + Pname, Device, p, line); + (void) free(Fstr.it); + Fstr.it = NULL; + } + if ((Fstr.it = Convstr(&line[i+2], &Fstr.itl)) + == NULL) + err++; + break; + /* + * \tr= + */ + case 'r': + if (Fstr.r != NULL) { + (void) fprintf(stderr, + "%s: dup roman for %s in %s: %s\n", + Pname, Device, p, line); + (void) free(Fstr.r); + Fstr.r = NULL; + } + if ((Fstr.r = Convstr(&line[i+2], &Fstr.rl)) + == NULL) + err++; + break; + /* + * \tf== + */ + case 'f': + if ( ! Devfont || Fstr.i) + break; + if ((i = Convfont(Device, &line[i+2], &fn, &fi)) + < 0) + err++; + else if (fn && strcmp(Devfont, fn) == 0) { + Fstr.i = fi; + Fstr.il = i; + fi = NULL; + } + if (fn) { + (void) free(fn); + fn = NULL; + } + if (fi) { + (void) free((char *)fi); + fi = NULL; + } + break; + /* + * ???? + */ + default: + (void) fprintf(stderr, + "%s: unknown device %s line: %s\n", + Pname, Device, line); + err++; + } + } + break; + } + (void) fclose(fs); + if (err) + return(1); +/* + * See if the device stanza was located and the font exists. + */ + if ( ! fd) { + (void) fprintf(stderr, "%s: can't find device %s in %s\n", + Pname, Device, p); + return(1); + } + if (Devfont && ! Fstr.i) { + (void) fprintf(stderr, + "%s: font %s for device %s not found in %s\n", + Pname, Devfont, Device, p); + return(1); + } + return(0); +} diff --git a/commands/cawf/device.cf b/commands/cawf/device.cf new file mode 100755 index 000000000..553002f44 --- /dev/null +++ b/commands/cawf/device.cf @@ -0,0 +1,89 @@ +# cawf(1) device configuration file +# +# device +# \tb=bold_control_sequence +# \ti=italic_control_sequence +# \tr=roman_control_sequence +# \tf=font_name=font_initialization +# +# The first font name is the default for the device. +# +# sorted in reverse alphabetical order by device name + +# VGA monochrome monitors +# italic = underline + +vgamono + b=\033[1m + i=\033[4m + r=\033[0m + + +# HP LaserJet III + +lj3 + b=\x1b(s7B + i=\x1b(s1S + r=\x1b(s0B\x1b(s0S + f=c10=\x1b&l0O\x1b(8U\x1b(s0p12h10v0s0b3T + f=c12ibm=\x1b&l0O\x1b(10U\x1b(s0p10.00h12.0v0s0b3T + f=lg12=\x1b&l0O\x1b(8U\x1b(s12h12v0s0b6T + +# Panasonic KX-P1180 +# bold = Emphasized +# +# all fonts are Near Letter Quality (NLQ) + +kxp1180 + b=\033E + i=\0334 + r=\0335\033F + f=c10=\033x1\033k0\033P + f=bps10=\033x1\033k6\033P + f=bps12=\033x1\033k6\033M + f=c12=\033x1\033k0\033M + f=p10=\033x1\033k3\033P + f=p12=\033x1\033k3\033M + f=ss10=\033x1\033k1\033P + f=ss12=\033x1\033k1\033M + +# Panasonic KX-P1124 (from Bob Hardy ) +# bold = Emphasized +# +# all fonts are Near Letter Quality (NLQ) +# +# The s10 and s12 fonts are Script, which is only available on the more +# deluxe models of the Panasonic KX-P1100 series -- e.g., Script is not +# supported on the KX-P1180, but is supported on the KX-P1124. + +kxp1124 + b=\033E + i=\0334 + r=\0335\033F + f=c10=\033x1\033k0\033P + f=bps10=\033x1\033k6\033P + f=bps12=\033x1\033k6\033M + f=c12=\033x1\033k0\033M + f=p10=\033x1\033k3\033P + f=p12=\033x1\033k3\033M + f=s10=\033x1\033k4\033P + f=s12=\033x1\033k4\033M + f=ss10=\033x1\033k1\033P + f=ss12=\033x1\033k1\033M + +# IBM Personal Printer Data Stream (PPDS) protocol +# bold = Double-strike +# italic = Underline + +ibmppds + b=\033G + i=\033-\001 + r=\033-\000\033H + +# Epson FX-86e/FX-800 +# bold = double strike + +epson + b=\033G + i=\0334 + r=\0335\033H diff --git a/commands/cawf/diffs b/commands/cawf/diffs new file mode 100755 index 000000000..7c8ce857e --- /dev/null +++ b/commands/cawf/diffs @@ -0,0 +1,211 @@ +diff -c1 ../4.0.2.dist/Makefile ./Makefile +*** ../4.0.2.dist/Makefile Thu Nov 26 09:06:28 1992 +--- ./Makefile Thu Nov 26 09:17:08 1992 +*************** +*** 36,40 **** + +! DEFS = -DUNIX -DSTDLIB -ansi -pedantic + +! CFLAGS = -O ${DEFS} + +--- 36,40 ---- + +! DEFS = -DUNIX -DUSG -DSTDLIB + +! CFLAGS = -i -s -f -O ${DEFS} + +diff -c1 ../4.0.2.dist/README ./README +*** ../4.0.2.dist/README Thu Nov 26 09:06:28 1992 +--- ./README Thu Nov 26 09:19:26 1992 +*************** +*** 54,56 **** +--- 54,68 ---- + ++ Changes to cawf to run under Minix: + ++ * The DOS binaries and make files have been removed. ++ ++ * Tabs stops changed from per 5 to per 8. ++ ++ * Bold and underline as it should be in device.cf. ++ ++ * Added .SB and .TA to man.mac. ++ ++ * Numerous prototypes added. ++ ++ + CONTENTS +*************** +*** 58,62 **** + +! This Unix distribution of cawf includes: + +! 00readme this file + *.c and *.h source files to build cawf and bsfilt (bsfilt +--- 70,74 ---- + +! This Minix distribution of cawf includes: + +! README this file + *.c and *.h source files to build cawf and bsfilt (bsfilt +*************** +*** 64,70 **** + bsfilt.1 nroff source for the bsfilt manual page +- bsfilt.exe.uue uuencoded MS-DOS bsfilt executable +- bsfilt.mak MS-DOS Quick-C make file for bsfilt + cawf.1 nroff source for the cawf manual page +- cawf.exe.uue uuencoded MS-DOS cawf executable +- cawf.mak MS-DOS Quick-C make file for cawf + common initialization file for CAWFLIB library +--- 76,78 ---- +*************** +*** 77,86 **** + ms.mac ms(7) macros for CAWFLIB library + +- Hint: to generate an MS-DOS executable, uudecode the *.exe.uue file -- e.g. + +- $ uudecode cawf.exe.uue +- +- yields a cawf.exe file. +- +- + LIBRARY +--- 85,94 ---- + ms.mac ms(7) macros for CAWFLIB library ++ diffs Minix patches ++ #ifdef PUTTING_IT_ON_THE_NET ++ cawf ++ bsfilt binaries compiled under Minix-PC 1.5 using the ACK ++ ANSI C compiler using software floating point ++ #endif + + + LIBRARY +*************** +*** 89,92 **** + To use cawf, you must select a location for the CAWFLIB library files. The +! distributed cawf.exe expects to find them in c:\sys\lib\cawf, but you can +! alter that with the CAWFLIB environment variable, or you can change the + CAWFLIB #define in cawf.h and rebuild cawf from the sources. +--- 97,100 ---- + To use cawf, you must select a location for the CAWFLIB library files. The +! distributed cawf binary expects to find them in /usr/local/lib/cawf but you +! can alter that with the CAWFLIB environment variable, or you can change the + CAWFLIB #define in cawf.h and rebuild cawf from the sources. +*************** +*** 135,139 **** + +! A Unix make file and a cawf.mak file for version 2.5 of Microsoft MS-DOS +! Quick-C are included. The Unix make file has some definitions that help +! tune it to the local Unix environment: + +--- 143,146 ---- + +! The Unix make file has some definitions that help tune it to the local +! Unix environment: + +*************** +*** 251 **** +--- 258,264 ---- + ++ ++ MINIX SPECIFIC TINKERING ++ ------------------------ ++ ++ Kees J. Bot ++ 26 November 1992 +diff -c1 ../4.0.2.dist/cawflib.h ./cawflib.h +*** ../4.0.2.dist/cawflib.h Thu Nov 26 09:06:35 1992 +--- ./cawflib.h Thu Nov 26 09:17:10 1992 +*************** +*** 32,34 **** + #ifdef UNIX +! #define CAWFLIB "/Homes/abe/lib/cawf" /* UNIX library location */ + #else +--- 32,34 ---- + #ifdef UNIX +! #define CAWFLIB "/usr/local/lib/cawf" /* UNIX library location */ + #else +diff -c1 ../4.0.2.dist/common ./common +*** ../4.0.2.dist/common Thu Nov 26 09:06:36 1992 +--- ./common Thu Nov 26 09:17:10 1992 +*************** +*** 4,6 **** + .ce 0 +! .ta +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 + .in 0 +--- 4,6 ---- + .ce 0 +! .ta +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 + .in 0 +diff -c1 ../4.0.2.dist/device.c ./device.c +*** ../4.0.2.dist/device.c Thu Nov 26 09:06:36 1992 +--- ./device.c Thu Nov 26 09:34:09 1992 +*************** +*** 229,231 **** + if (strcasecmp(Device, "ansi") == 0) { +! Fstr.b = Newstr((unsigned char *)"x[7m"); + Fstr.it = Newstr((unsigned char *)"x[4m"); +--- 229,231 ---- + if (strcasecmp(Device, "ansi") == 0) { +! Fstr.b = Newstr((unsigned char *)"x[1m"); + Fstr.it = Newstr((unsigned char *)"x[4m"); +diff -c1 ../4.0.2.dist/device.cf ./device.cf +*** ../4.0.2.dist/device.cf Thu Nov 26 09:06:37 1992 +--- ./device.cf Thu Nov 26 09:21:17 1992 +*************** +*** 13,15 **** + # VGA monochrome monitors +! # italic = reverse video + +--- 13,15 ---- + # VGA monochrome monitors +! # italic = underline + +*************** +*** 17,19 **** + b=\033[1m +! i=\033[7m + r=\033[0m +--- 17,19 ---- + b=\033[1m +! i=\033[4m + r=\033[0m +Only in .: diffs +diff -c1 ../4.0.2.dist/man.mac ./man.mac +*** ../4.0.2.dist/man.mac Thu Nov 26 09:06:42 1992 +--- ./man.mac Thu Nov 26 09:17:12 1992 +*************** +*** 5,7 **** + .ds LH "\\$1(\\$2) +! .ds CH "Unix Programmer's Manual + .ds RH "\\$1(\\$2) +--- 5,7 ---- + .ds LH "\\$1(\\$2) +! .ds CH "Minix Programmer's Manual + .ds RH "\\$1(\\$2) +*************** +*** 120,121 **** +--- 120,128 ---- + .\"----------------- ++ .de SB ++ .\" Can't reduce size, just do bold ++ .ft B ++ .it 1 fP ++ .if \\n(.$>0 \&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 ++ .. ++ .\"----------------- + .de IR +*************** +*** 150,152 **** + .de DT +! .ta +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 +5 + .. +--- 157,163 ---- + .de DT +! .ta +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +! .. +! .\"----------------- +! .de TA +! .ta \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 + .. diff --git a/commands/cawf/dumb.dev b/commands/cawf/dumb.dev new file mode 100755 index 000000000..c5e0db28f --- /dev/null +++ b/commands/cawf/dumb.dev @@ -0,0 +1,158 @@ +.\" Device-dependent but not macro-set-dependent definitions. +.\" -------------------------------- +.\" overall formatting initialization +.\" 12 cpi horizontal exploits 80-column terminal well (6.5i@12 = 78) +.^r cpi 12 6 +.\" call margin adjustment device-dependent for sake of some unusual cases +.ad +.\" page parameters +.pl 10i +.ll 6.5i +.po 0 +.\" -------------------------------- +.\" fonts, and their hyphens, last font change doubled to set up \fP +.^f R +.ft R +.^c hy 1 - +.^f I +.ft I +.^c hy 1 - +.^f B +.ft B +.^c hy 1 -\b-\b- +.^f C +.ft C +.^c hy 1 -\b-\b- +.ft R +.ft R +.\" -------------------------------- +.\" definitions of nroff special characters +.\" The character definitions here operate on the "better ugly than invisible" +.\" principle, and try to approximate the character *somehow*. They were +.\" tuned for a Teletype 40 line printer, but should give vaguely plausible +.\" results on any overprinting ASCII device. +.\" +.\" first, things that nroff considered builtins +.^c \ 1 \\ +.^c e 1 \\ +.^c ' 1 ' +.^c ` 1 ` +.^c - 1 - +.\" some things seem to assume that \+ is like \- +.^c + 1 + +.\" we do not do backslash-space here, it can't be done with .^c, but the +.\" other forms of space we can do +.^c 0 1 " +.^c | 0 +.^c ^ 0 +.^c & 0 +.\" +.\" and more normal characters +.\" note, the hyphenation logic knows about em +.^c em 2 -- +.^c en 1 - +.\" hy is a special case, see above +.^c bu 1 +\bo +.^c sq 2 [] +.^c ru 1 _ +.^c 12 3 1/2 +.^c 14 3 1/4 +.^c 34 3 3/4 +.^c de 1 '\b` +.^c dg 1 -\b! +.^c fm 1 ' +.^c ct 1 /\bc +.^c rg 3 (R) +.^c co 3 (c) +.^c pl 1 + +.^c mi 1 - +.^c eq 1 = +.^c ** 1 * +.^c sc 1 j\bf +.^c aa 1 ' +.^c ga 1 ` +.^c ul 1 _ +.^c sl 1 / +.^c *a 1 <\ba +.^c *b 1 ,\bB +.^c *g 1 ,\by +.^c *d 1 S\bo +.^c *e 1 -\bc +.^c *z 1 ,\bL +.^c *y 1 ,\bn +.^c *h 1 -\b0 +.^c *i 1 ,\bi +.^c *k 1 <\bK +.^c *l 1 \\\b> +.^c *m 1 ,\bu +.^c *n 1 ,\bv +.^c *c 1 ,\b3 +.^c *o 1 o +.^c *p 1 -\bn +.^c *r 1 p +.^c *s 1 -\bo +.^c ts 1 s +.^c *t 1 ~\bt +.^c *u 1 u +.^c *f 1 /\bo +.^c *x 1 /\b\\ +.^c *q 1 |\bu +.^c *w 1 u\bw +.^c *G 2 ~\b|~ +.^c *D 2 _\b/_\b\\ +.^c *H 1 -\bO +.^c *L 2 /\\ +.^c *C 1 _\b-\b~ +.^c *P 2 ~\b|~\b| +.^c *S 1 ~\b_\b> +.^c *U 1 Y +.^c *F 1 |\bO +.^c *Q 1 |\bU +.^c *W 2 _\b(_\b) +.^c sr 2 \\/ +.^c rn 1 ~ +.^c >= 1 _\b> +.^c <= 1 _\b< +.^c == 1 _\b= +.^c ~= 1 ~\b= +.^c ap 1 ~ +.^c != 1 /\b= +.^c -> 2 -> +.^c <- 2 <- +.^c ua 1 |\b^ +.^c da 1 |\bv +.^c mu 1 x +.^c di 1 -\b: +.^c +- 1 _\b+ +.^c cu 1 U +.^c ca 3 (^) +.^c sb 2 (_\b~ +.^c sp 2 _\b~) +.^c ib 2 (~\b_\b= +.^c ip 2 ~\b_\b=) +.^c if 2 oo +.^c pd 1 3\bo +.^c gr 1 ~\bV +.^c no 1 - +.^c is 1 '\b,\bI +.^c pt 2 oc +.^c es 1 /\bO +.^c mo 1 -\bC +.^c br 1 | +.^c dd 1 I\b| +.^c rh 1 =\b> +.^c lh 1 =\b< +.^c bs 4 (:-) +.^c or 1 | +.^c ci 1 O +.^c lt 1 ~\b( +.^c lb 1 _\b( +.^c rt 1 ~\b) +.^c rb 1 _\b) +.^c lk 1 -\b( +.^c rk 1 -\b) +.^c bv 1 | +.^c lf 1 _\b[ +.^c rf 1 _\b] +.^c lc 1 ~\b[ +.^c rc 1 ~\b] diff --git a/commands/cawf/error.c b/commands/cawf/error.c new file mode 100755 index 000000000..368292131 --- /dev/null +++ b/commands/cawf/error.c @@ -0,0 +1,97 @@ +/* + * error.c - error handling functions for cawf(1) + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include "cawf.h" + + +/* + * Error(t, l, s1, s2) - issue error message + */ + +void +Error(t, l, s1, s2) + int t; /* type: WARN or FATAL */ + int l; /* LINE: display Line[] */ + char *s1, *s2; /* optional text */ +{ + char msg[MAXLINE]; /* message */ + + if (t == WARN && !Dowarn) return; + + if (l == LINE) + (void) fprintf(Efs, "%s: (%s, %d):%s%s - %s\n", + Pname, + Inname, + NR, + (s1 == NULL) ? "" : s1, + (s2 == NULL) ? "" : s2, + Line); + else + (void) fprintf(Efs, "%s:%s%s\n", + Pname, + (s1 == NULL) ? "" : s1, + (s2 == NULL) ? "" : s2); + if (t == FATAL) + exit(1); + Err = 1; + return; +} + + +/* + * Error3(len, word, sarg, narg) - process error in pass3 + */ + +void +Error3(len, word, sarg, narg, msg) + int len; /* length (negative is special */ + char *word; /* word */ + char *sarg; /* string argument */ + int narg; /* numeric argument */ + char *msg; /* message */ +{ + if (len == MESSAGE) { + (void) fprintf(Efs, "%s: (%s, %d) %s\n", + Pname, + (word == NULL) ? "" : word, + narg, + (sarg == NULL) ? "" : sarg); + return; + } + (void) fprintf(Efs, + "%s: pass3, len=%d, word=\"%s\", sarg=\"%s\", narg=%d%s%s\n", + Pname, len, + (word == NULL) ? "" : word, + (sarg == NULL) ? "" : sarg, + narg, + (msg == NULL) ? "" : " - ", + (msg == NULL) ? "" : msg); + Err = 1; +} diff --git a/commands/cawf/expand.c b/commands/cawf/expand.c new file mode 100755 index 000000000..886c54924 --- /dev/null +++ b/commands/cawf/expand.c @@ -0,0 +1,308 @@ +/* + * expand.c - macro expansion functions for cawf(1) + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include "cawf.h" + +/* + * Expand(line) - expand macro or if/ie/el line + */ + +void +Expand(line) + unsigned char *line; +{ + unsigned char buf[2*MAXLINE]; /* line buffer */ + unsigned char cmd[4]; /* nroff command */ + int cmdl; /* command length */ + int cmdx; /* cmd index in Macrotab[] */ + int cond = 0; /* conditional statuses */ + int i, j; /* temporary indexes */ + int iflen; /* if statement length */ + int invert; /* inversion status */ + unsigned char *lp; /* line pointer */ + int mx = -1; /* Macrotab[] index */ + int n1, n2; /* temporary numbers */ + int nargs = 0; /* number of arguments */ + int nleft = 0; /* number of macro lines left */ + char op; /* comparison operator */ + int prevcond; /* previous condition (for else's) */ + int ptr = -1; /* Macrotxt[] index */ + int quote; /* quoted string status */ + unsigned char *s1, *s2; /* temporary string pointers */ + + + (void) sprintf((char *)buf, ".^= %d %s", NR, (char *)Inname); + Pass2(buf); + + for (lp = line; *lp; ) { + invert = regexec(Pat[1].pat, lp); + prevcond = cond; + cond = 0; + if (regexec(Pat[0].pat, lp) == 0) { + /* + * Not conditional: - ! "^[.'](i[ef]|el)" + */ + cond = 1; + iflen = 0; + } + + else if (regexec(Pat[2].pat, lp)) { + /* + * Argument count comparison: - + * "^[.']i[ef] !?\\n\(\.\$(>|>=|=|<|<=)[0-9] " + */ + iflen = strlen(".if \\n(.$=n ") + invert; + s1 = lp + iflen - 3; + op = *s1++; + if (*s1 == '=' && (op == '>' || op == '<')) { + s1++; + op = (op == '>') ? 'G' : 'L'; + } + n1 = (int)(*s1 - '0'); + switch (op) { + case '=': + if ((nargs - 1) == n1) + cond = 1; + break; + case '<': + if ((nargs - 1) < n1) + cond = 1; + break; + case '>': + if ((nargs - 1) > n1) + cond = 1; + break; + case 'G': /* >= */ + if ((nargs - 1) >= n1) + cond = 1; + break; + case 'L': /* <= */ + if ((nargs - 1) <= n1) + cond = 1; + } + } + + else if (regexec(Pat[3].pat, lp)) { + /* + * Argument string comparison: - "^[.']i[ef] !?'\\\$[0-9]'[^']*' " + */ + iflen = strlen(".if '\\$n'") + invert; + n1 = (int)(*(lp + iflen - 2) - '0'); + if (n1 >= 0 && n1 < nargs) + s1 = Args[n1]; + else + s1 = (unsigned char *)""; + if ((s2 = (unsigned char *)strchr((char *)lp + + iflen, '\'')) + != NULL) { + n2 = s2 - lp - iflen; + if (strncmp((char *)s1, (char *)lp + iflen, n2) + == 0) + cond = 1; + iflen += n2 + 2; + } + } + + else if (regexec(Pat[4].pat, lp)) { + /* + * Nroff or troff: - "^[.']i[ef] !?[nt] " + */ + iflen = strlen(".if n ") + invert; + if (*(lp + iflen - 2) == 'n') + cond = 1; + } + + else if ((*lp == '.' || *lp == '\'') + && strncmp((char *)lp+1, "el ", 3) == 0) { + /* + * Else clause: - "^[.']el " + */ + cond = 1 - prevcond; + iflen = 4; + } + + else { + /* + * Unknown conditional: + */ + cond = 1; + iflen = 0; + (void) sprintf((char *)buf, + ".tm unknown .if/.ie form: %s", (char *)lp); + lp = buf; + } + /* + * Handle conditional. If case is true, locate predicate. + * If predicate is an .i[ef], process it. + */ + if (invert) + cond = 1 - cond; + if (cond && iflen > 0) { + lp += iflen; + if (regexec(Pat[15].pat, lp)) + continue; + } + /* + * Do argument substitution, as necessary. + */ + if (cond && regexec(Pat[5].pat, lp)) { /* "\$[0-9]" ??? */ + for (s1 = buf;;) { + if ((n1 = Pat[5].pat->startp[0] - lp) > 0) { + (void) strncpy((char *)s1, (char *)lp, + n1); + s1 += n1; + } + *s1 = '\0'; + lp = Pat[5].pat->endp[0]; + n1 = (int)(*(lp-1) - '0'); + if (n1 >= 0 && n1 < nargs) { + (void) strcpy((char *)s1, + (char *)Args[n1]); + s1 += strlen((char *)Args[n1]); + } + if (*lp == '\0') + break; + if (regexec(Pat[5].pat, lp) == 0) { + (void) strcpy((char *)s1, (char *)lp); + break; + } + } + lp = buf; + } + /* + * Check for nroff command. + */ + if (cond) { + cmdl = 0; + if (cond && (*lp == '.' || *lp == '\'')) { + if ((*cmd = *(lp+1)) != '\0') { + cmdl++; + if ((*(cmd+1) = *(lp+2)) == ' ') + *(cmd+1) = '\0'; + else + cmdl++; + } + } + cmd[cmdl] = '\0'; + } + if (cond == 0) + i = i; /* do nothing if condition is false */ + else if (cmdl == 0 || ((cmdx = Findmacro(cmd, 0)) < 0)) + Pass2(lp); + else if (Sp >= MAXSP) { + (void) sprintf((char *)buf, " macro nesting > %d", + MAXSP); + Error(WARN, LINE, (char *)buf, NULL); + } else { + /* + * Stack macros. + */ + /* + * Push stack. + */ + Sp++; + Nleftstack[Sp] = nleft; + Ptrstack[Sp] = ptr; + Mxstack[Sp] = mx; + Condstack[Sp] = cond; + for (i = 10*Sp, j = 0; j < 10; i++, j++) { + Argstack[i] = Args[j]; + Args[j] = NULL; + } + /* + * Start new stack entry. + */ + mx = cmdx; + ptr = Macrotab[mx].bx; + cond = 0; + nleft = Macrotab[mx].ct; + Args[0] = Newstr(cmd); + /* + * Parse arguments. + */ + for (s1 = lp + cmdl + 1, nargs = 1; nargs < 10;) { + while (*s1 && (*s1 == ' ' || *s1 == '\t')) + s1++; + if (*s1 == '\0') + break; + if (*s1 == '"') { + s1++; + quote = 1; + } else + quote = 0; + for (s2 = buf;;) { + if (!quote && (*s1 == ' ' || *s1 == '\t')) { + *s2 = '\0'; + break; + } + if ((*s2 = *s1) == '\0') + break; + s1++; + if (quote && *s2 == '"') { + *s2 = '\0'; + break; + } + s2++; + } + if (buf[0]) + Args[nargs++] = Newstr(buf); + } + for (i = nargs; i < 10; i++) { + Args[i] = NULL; + } + } + /* + * Unstack completed macros. + */ + while (nleft <= 0 && Sp >= 0) { + nleft = Nleftstack[Sp]; + mx = Mxstack[Sp]; + ptr = Ptrstack[Sp]; + cond = Condstack[Sp]; + for (i = 10*Sp, j = 0, nargs = -1; j < 10; i++, j++) { + Free(&Args[j]); + if ((Args[j] = Argstack[i]) != NULL) + nargs = j; + } + Sp--; + nargs++; + } + /* + * Get next line. + */ + if (nleft > 0) { + lp = Macrotxt[ptr++]; + nleft--; + } else + lp = (unsigned char *)""; + } + (void) sprintf((char *)buf, ".^# %d %s", NR, (char *)Inname); + Pass2(buf); +} diff --git a/commands/cawf/expr.c b/commands/cawf/expr.c new file mode 100755 index 000000000..1b19e16e7 --- /dev/null +++ b/commands/cawf/expr.c @@ -0,0 +1,175 @@ +/* + * expr.c - expression support functions for cawf(1) + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include "cawf.h" + + +/* + * Asmcode(s, c) - assemble number/name code following backslash-character + * definition - e. .g, "\\nPO" + */ + +unsigned char * +Asmcode(s, c) + unsigned char **s; /* pointer to character after '\\' */ + unsigned char *c; /* code destination (c[3]) */ +{ + unsigned char *s1; + + s1 = *s + 1; + c[0] = c[1] = c[2] = '\0'; + if ((c[0] = *s1) == '(') { + s1++; + if ((c[0] = *s1) != '\0') { + s1++; + c[1] = *s1; + } + } + return(s1); +} + + +/* + * Delnum(nx) - delete number + */ + +void +Delnum(nx) + int nx; /* number index */ +{ + unsigned char buf[MAXLINE]; /* message buffer */ + + if (nx >= Nnr) { + (void) sprintf((char *)buf, " bad Delnum(%d) index", nx); + Error(FATAL, LINE, (char *)buf, NULL); + } + while (nx < (Nnr - 1)) { + Numb[nx] = Numb[nx + 1]; + nx++; + } + Nnr--; +} + + +/* + * Findnum(n, v, e) - find or optionally enter number value + */ + +Findnum(n, v, e) + unsigned char *n; /* register name */ + int v; /* value */ + int e; /* 0 = find, don't enter + * 1 = enter, don't find */ +{ + int cmp, low, hi, mid; /* binary search controls */ + unsigned char c[3]; /* name buffer */ + + c[0] = n[0]; + c[1] = (n[1] == ' ' || n[1] == '\t') ? '\0' : n[1]; + c[2] = '\0'; + low = mid = 0; + hi = Nnr - 1; + while (low <= hi) { + mid = (low + hi) / 2; + if ((cmp = strncmp((char *)c, (char *)Numb[mid].nm, 2)) < 0) + hi = mid - 1; + else if (cmp > 0) + low = mid + 1; + else { + if (e) + Numb[mid].val = v; + return(mid); + } + } + if ( ! e) + return(-1); + if (Nnr >= MAXNR) + Error(FATAL, LINE, " out of number registers at ", (char *)c); + if (Nnr) { + if (cmp > 0) + mid++; + for (hi = Nnr - 1; hi >= mid; hi--) + Numb[hi+1] = Numb[hi]; + } + Nnr++; + Numb[mid].nm[0] = c[0]; + Numb[mid].nm[1] = c[1]; + Numb[mid].val = v; + return(mid); +} + + +/* + * Findparms(n) - find parameter registers + */ + +Findparms(n) + unsigned char *n; /* parameter name */ +{ + unsigned char c[3]; /* character buffer */ + int i; /* temporary index */ + + c[0] = n[0]; + c[1] = (n[1] == ' ' || n[1] == '\t') ? '\0' : n[1]; + c[2] = '\0'; + for (i = 0; Parms[i].nm[0]; i++) { + if (c[0] == Parms[i].nm[0] && c[1] == Parms[i].nm[1]) + return(i); + } + return(-1); +} + + +/* + * Findscale(n, v, e) - find and optionally enter scaling factor value + */ + +Findscale(n, v, e) + int n; /* scaling factor name */ + double v; /* value */ + int e; /* 0 = find, don't enter + * 1 = enter, don't find */ +{ + int i; + double *pval; + + for (i = 0; Scale[i].nm; i++) { + if ((unsigned char )n == Scale[i].nm) + break; + } + if (Scale[i].nm) { + if (e) { + pval = &Scale[i].val; + *pval = v; + } + return(i); + } + return(-1); +} diff --git a/commands/cawf/getopt.c b/commands/cawf/getopt.c new file mode 100755 index 000000000..734e64b74 --- /dev/null +++ b/commands/cawf/getopt.c @@ -0,0 +1,77 @@ +/* +Newsgroups: mod.std.unix +Subject: public domain AT&T getopt source +Date: 3 Nov 85 19:34:15 GMT + +Here's something you've all been waiting for: the AT&T public domain +source for getopt(3). It is the code which was given out at the 1985 +UNIFORUM conference in Dallas. I obtained it by electronic mail +directly from AT&T. The people there assure me that it is indeed +in the public domain. +*/ + + +/*LINTLIBRARY*/ +#define NULL 0 +#define EOF (-1) +#define ERR(s, c) if(opterr){\ + extern int strlen(), write();\ + char errbuf[2];\ + errbuf[0] = c; errbuf[1] = '\n';\ + (void) write(2, argv[0], (unsigned)strlen(argv[0]));\ + (void) write(2, s, (unsigned)strlen(s));\ + (void) write(2, errbuf, 2);} + +extern int strcmp(); +extern char *strchr(); + +int opterr = 1; +int optind = 1; +int optopt; +char *optarg; + +int +getopt(argc, argv, opts) +int argc; +char **argv, *opts; +{ + static int sp = 1; + register int c; + register char *cp; + + if(sp == 1) + if(optind >= argc || + argv[optind][0] != '-' || argv[optind][1] == '\0') + return(EOF); + else if(strcmp(argv[optind], "--") == NULL) { + optind++; + return(EOF); + } + optopt = c = argv[optind][sp]; + if(c == ':' || (cp=strchr(opts, c)) == NULL) { + ERR(": illegal option -- ", c); + if(argv[optind][++sp] == '\0') { + optind++; + sp = 1; + } + return('?'); + } + if(*++cp == ':') { + if(argv[optind][sp+1] != '\0') + optarg = &argv[optind++][sp+1]; + else if(++optind >= argc) { + ERR(": option requires an argument -- ", c); + sp = 1; + return('?'); + } else + optarg = argv[optind++]; + sp = 1; + } else { + if(argv[optind][++sp] == '\0') { + sp = 1; + optind++; + } + optarg = NULL; + } + return(c); +} diff --git a/commands/cawf/macsup.c b/commands/cawf/macsup.c new file mode 100755 index 000000000..c1d11e7d0 --- /dev/null +++ b/commands/cawf/macsup.c @@ -0,0 +1,181 @@ +/* + * macsup.c - macro processing support functions for cawf(1) + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include "cawf.h" + + +/* + * Delmacro(mx) - delete macro + */ + +Delmacro(mx) + int mx; /* macro index */ +{ + unsigned char buf[MAXLINE]; /* error message buffer */ + int i, j; /* temporary indexes */ + + if (mx >= Nmac) { + (void) sprintf((char *)buf, " bad Delmacro(%d) index", mx); + Error(FATAL, LINE, (char *)buf, NULL); + } + for (i = Macrotab[mx].bx, j = i + Macrotab[mx].ct; i < j; i++) { + Free(&Macrotxt[i]); + } + for (i = mx; i < (Nmac - 1); i++) { + Macrotab[i] = Macrotab[i+1]; + } + Nmac--; +} + + +/* + * Field(n, p, c) - skip to field n in p and optionally return a copy + */ + +unsigned char * +Field(n, p, c) + int n; /* field number */ + unsigned char *p; /* pointer to line containing fields */ + int c; /* 1: make a copy of the field */ +{ + unsigned char *fs, *fe, *s; + + if (c) + Free(&F); + fe = p; + while (n) { + while (*fe == ' ' || *fe == '\t') + fe++; + fs = fe; + while (*fe && *fe != ' ' && *fe != '\t') + fe++; + if (fs == fe) + return(NULL); + if (n == 1) { + if ( ! c) + return(fs); + if ((F = (unsigned char *)malloc((size_t)(fe - fs + 1))) + == NULL) + Error(FATAL, LINE, " Field out of string space", + NULL); + (void) strncpy((char *)F, (char *)fs, (fe - fs)); + F[fe -fs] = '\0'; + return(F); + } + n--; + } + return(NULL); +} + +/* + * Findmacro(p, e) - find macro and optionally enter it + * + * return = Macrotab[] index or -1 if not found + */ + + +Findmacro(p, e) + unsigned char *p; /* pointer to 2 character macro name */ + int e; /* 0 = find, don't enter + * 1 = enter, don't find */ +{ + unsigned char c[3]; + int cmp, hi, low, mid; + + c[0] = p[0]; + c[1] = (p[1] == ' ' || p[1] == '\t') ? '\0' : p[1]; + c[2] = '\0'; + low = mid = 0; + hi = Nmac - 1; + while (low <= hi) { + mid = (low + hi) / 2; + if ((cmp = strncmp((char *)c, (char *)Macrotab[mid].name, 2)) + < 0) + hi = mid - 1; + else if (cmp > 0) + low = mid + 1; + else { + if ( ! e) + return(mid); + Error(WARN, LINE, " duplicate macro ", (char *)c); + hi = Macrotab[mid].bx + Macrotab[mid].ct; + for (low = Macrotab[mid].bx; low < hi; low++) { + Free(&Macrotxt[low]); + } + goto new_macro; + } + } + if ( ! e) + return(-1); + if (Nmac >= MAXMACRO) + Error(FATAL, LINE, " macro table full at ", (char *)c); + if (Nmac) { + if (cmp > 0) + mid++; + for (hi = Nmac - 1; hi >= mid; hi--) + Macrotab[hi+1] = Macrotab[hi]; + } + Nmac++; + Macrotab[mid].name[0] = c[0]; + Macrotab[mid].name[1] = c[1]; + +new_macro: + + Macrotab[mid].bx = -1; + Macrotab[mid].ct = 0; + return(mid); +} + +void +Free(p) + unsigned char **p; +{ + if (*p != NULL) { + (void) free(*p); + *p = NULL; + } +} + +/* + * Newstr(s) - allocate space for string + */ + +unsigned char * +Newstr(s) + unsigned char *s; +{ + unsigned char *ns; + + if ((ns = (unsigned char *)malloc((size_t)(strlen((char *)s) + 1))) + == NULL) + Error(FATAL, LINE, " Newstr out of malloc space at ", (char *)s); + (void) strcpy((char *)ns, (char *)s); + return(ns); +} diff --git a/commands/cawf/man.mac b/commands/cawf/man.mac new file mode 100755 index 000000000..d776fc4e0 --- /dev/null +++ b/commands/cawf/man.mac @@ -0,0 +1,180 @@ +.^b HF 1 +.^b fh 1 +.\"----------------- +.de TH +.ds LH "\\$1(\\$2) +.ds CH "Minix Programmer's Manual +.ds RH "\\$1(\\$2) +.ds LF " +.ds CF "\\$3 +.ds RF "% +.. +.\"----------------- +.de AT +.. +.\"----------------- +.de IX +.. +.\"----------------- +.de NB +.ds nb "\\$1 +.lF +.. +.\"----------------- +.de BY +.ds by "\\$1 +.lF +.. +.\"----------------- +.de UC +.BY "\\$1BSD" +.. +.\"----------------- +.\" common initialization for headers and paragraphs: .In need +.de In +.ne \\$1 +.sp \\n(PDu +.fi +.in 0 +.ti 0 +.in \\n(inu +.it +.ft R +.ns +.. +.\"----------------- +.de SH +.In 6 +.in 0 +.ft B +\&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.ft R +.nr in 5n +.in \\n(inu +.. +.\"----------------- +.de SS +.In 6 +.in 2n +.ft B +\&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.ft R +.in \\n(inu +.. +.\"----------------- +.de LP +.In 4 +.. +.\"----------------- +.de PP +.LP +.. +.\"----------------- +.de P +.LP +.. +.\"----------------- +.de HP +.In 4 +.if \\n(.$>0 .ti 0-\\$1 +.if \\n(.$=0 .ti 0-\\n(tpu +.. +.\"----------------- +.de TP +.In 4 +.if \\n(.$>0 .nr tp \\$1n +.in \\n(inu+\\n(tpu +.ti 0-\\n(tpu +.it 1 tP +.. +.\"----------------- +.de IP +.ie \\n(.$>1 .TP "\\$2" +.el .TP +\&\\$1 +.. +.\"----------------- +.de I +.ft I +.it 1 fR +.if \\n(.$>0 \&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.. +.\"----------------- +.de B +.ft B +.it 1 fR +.if \\n(.$>0 \&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.. +.\"----------------- +.de SB +.\" Can't reduce size, just do bold +.ft B +.it 1 fR +.if \\n(.$>0 \&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.. +.\"----------------- +.de IR +\&\\fI\\$1\\fR\\$2\\fI\\$3\\fR\\$4\\fI\\$5\\fR\\$6\\fI\\$7\\fR\\$8\\fI\\$9\\fR +.. +.\"----------------- +.de RI +\&\\$1\\fI\\$2\\fR\\$3\\fI\\$4\\fR\\$5\\fI\\$6\\fR\\$7\\fI\\$8\\fR\\$9 +.. +.\"----------------- +.de BR +\&\\fB\\$1\\fR\\$2\\fB\\$3\\fR\\$4\\fB\\$5\\fR\\$6\\fB\\$7\\fR\\$8\\fB\\$9\\fR +.. +.\"----------------- +.de RB +\&\\$1\\fB\\$2\\fR\\$3\\fB\\$4\\fR\\$5\\fB\\$6\\fR\\$7\\fB\\$8\\fR\\$9 +.. +.\"----------------- +.de BI +\&\\fB\\$1\\fI\\$2\\fB\\$3\\fI\\$4\\fB\\$5\\fI\\$6\\fB\\$7\\fI\\$8\\fB\\$9\\fR +.. +.\"----------------- +.de IB +\&\\fI\\$1\\fB\\$2\\fI\\$3\\fB\\$4\\fI\\$5\\fB\\$6\\fI\\$7\\fB\\$8\\fI\\$9\\fR +.. +.\"----------------- +.de SM +.\" no-op +.if \\n(.$>0 \&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.. +.\"----------------- +.de DT +.ta +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +8 +.. +.\"----------------- +.de TA +.ta \\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.. +.\"----------------- +.de RS +.in +5n +.. +.\"----------------- +.de RE +.in -5n +.. +.de DS +.br +.. +.de DE +.br +.. +.\"----------------- +.de PD +.ie \\n(.$=0 .nr PD 0.3v +.el .nr PD \\$1n +.. +.\"----------------- +.\" misc. initialization +.nr tp 5n +.PD +.ds lq \&" +.ds rq \&" +.ds R \(rg +.ds S " +.ds Tm (TM) +.nr )P 1v diff --git a/commands/cawf/me.mac b/commands/cawf/me.mac new file mode 100755 index 000000000..296cf3e8d --- /dev/null +++ b/commands/cawf/me.mac @@ -0,0 +1,199 @@ +.^b HF 1 +.^b fh 0 +.^b NH +.\"----------------- +.de r +.ie \\n(.$=0 .ft R +.el \\fR\\$1\\fP\\$2 +.. +.\"----------------- +.de i +.ie \\n(.$=0 .ft I +.el \\fI\\$1\\fP\\$2 +.. +.\"----------------- +.de b +.ie \\n(.$=0 .ft B +.el \\fB\\$1\\fP\\$2 +.. +.\"----------------- +.de UX +\\$2UNIX\\$1 +.. +.\"----------------- +.de DA +.ds DY "\\$1 \\$2 \\$3 +.\" keep trailing spaces out of CF +.if \\n(.$=1 .ds CF "\\$1 +.if \\n(.$=2 .ds CF "\\$1 \\$2 +.if \\n(.$>2 .ds CF "\\$1 \\$2 \\$3 +.. +.\"----------------- +.de ND +.\" it's our default, ignore it +.. +.\"----------------- +.de TL +.rs +.sp 5 +.ft B +.ce 9999 +.. +.\"----------------- +.de AU +.sp 2 +.ft R +.. +.\"----------------- +.de AI +.sp +.ft R +.. +.\"----------------- +.de AB +.sp 2 +.ce 0 +.ll -7n +.in +7n +.. +.\"----------------- +.de AE +.sp +.ll +.in +.. +.\" common initialization for headers and paragraphs: .In need extraspace +.de In +.ne \\$1 +.sp \\n(Tsu +.nr Ts 0 +.ie \\n(.$>1 .nr iN \\$2v +.el .nr iN 0 +.sp \\n(psu+\\n(iNu +.ce 0 +.ft R +.in 0 +.ti 0 +.in \\n(inu +.ll \\n(LLu +.ns +.fi +.. +.\"----------------- +.de uh +.\" unnumbered section head +.nr in 0 +.In 6 1 +.ie \\n(.$=0 .ft B +.el \\fB\\$1\\fP\\$2 +.. +.\"----------------- +.de sh +.\" numbered section head +.nr in 0 +.In 6 1 +.ft B +.\" punt to awk code to get the header numbering right +.nH \\$1 +.\" and pick up the result +\&\\*(Nh \\$2 +.ft R +.. +.\"----------------- +.de lp +.\" left-blocked paragraph +.In 4 +.. +.\"----------------- +.de pp +.\" paragraph, first line indented +.In 4 +.ti \\n(piu +.. +.\"----------------- +.de tp +.In 4 +.if \\n(.$>0 .nr tp \\$1n +.in \\n(inu+\\n(tpu +.ti 0-\\n(tpu +.\" .it 1 tP +.. +.\"----------------- +.de ip +.\" indented paragraph +.ie \\n(.$>1 .tp "\\$2" +.el .tp 4n +\&\\$1 +.. +.\"----------------- +.de np +.\" numbered paragraph +.nr $p +1 \" increment paragraph number +.ip (\n($p) +.. +.\"----------------- +.de bu +.\" bulleted paragraph +.ip \(bu +.. +.\"----------------- +.de (q +.\" begin major quote +.nr in +5n +.nr LL -5n +.In 4 +.. +.\"----------------- +.de )q +.\" end major quote +.sp +.nr in -5n +.nr LL +5n +.In 4 +.. +.\"----------------- +.de (l +.\" begin list +.In 5 +.if '\\$1'C' .ce 9999 +.if '\\$1'' .in +5n +.if '\\$1'L' .in 0 +.nf +.. +.\"----------------- +.de )l +.\" end list +.In 3 +.sp +.rs +.. +.\"----------------- +.de u +\&\\fI$1\\fP +.. +.\"----------------- +.de sm +.. +.\"----------------- +.\" exdented paragraph macro borrowed from Berkeley -ms +.de XP +.lp +.in \\n(piu +.ti +.. +.\"----------------- +.\" the -ms accent strings +.ds ' "'\b +.ds ` "`\b +.ds : ":\b +.ds ^ "^\b +.ds ~ "~\b +.ds C "v\b +.ds , ",\b +.\" post-title spacing (set to 4v if using .TL macro) +.nr Ts 0v +.\" and parameter setup +.nr LL 6i +.ll \n(LLu +.nr ps 0.3v +.nr pi 5n diff --git a/commands/cawf/mnx.mac b/commands/cawf/mnx.mac new file mode 100755 index 000000000..562f0512b --- /dev/null +++ b/commands/cawf/mnx.mac @@ -0,0 +1,137 @@ +.\" -mnx macros for the Minix "Book manual pages". Author: Kees J. Bot +.\" 19 Nov 1994 +.\" Uses -ms: +.so /usr/lib/cawf/ms.mac +.tr ~ +.ds OQ \&' +.ds CQ \&' +.ds SQ \&' +.ds M0 MINIX +.ds M1 \\s-1MINIX\\s+1 +.ds M2 \\s-2MINIX\\s+2 +.ds M9 \\s-1MINIX\\s+1 +.ds m0 minix +.de MX +\s-2MINIX\s+2\\$1 +.. +.de Ux +\s-2UNIX\s+2\\$1 +.. +.ds Mx \\s-1MINIX\\s0 +.ds Mp \\s-1MINIX-PC\\s0 +.ds Ms \\s-1MINIX-ST\\s0 +.ds M0 MINIX +.ds M1 MINIX +.ds M2 MINIX +.ds M9 MINIX +.ds Mx MINIX +.ds Mp MINIX-PC +.ds Ms MINIX-ST +.de CD +.LP +.ne 2 +.ta 11m 15m 36m +.ds SX Syntax: +.ds FL Flags: +.ds EX Examples: +.ds EY Example: +.in +16m +.ti -16m +\\fBCommand:\& \\$1\\fR +.in -16m +.br +.. +.de SX +.in +16m +.ti -16m +\\fB\*(SX\& \\$1 +.in -16m +.ds SX +.br +.. +.de FL +.in +15m +.ti -15m +\\fB\*(FL\& \\fB\\$1 \\fR\\$2 +.in -15m +.ds FL +.br +.. +.de EX +.br +.in +38m +.ti -38m +\\fB\*(EX\& \\fR\\$1 \\fR# \\$2 +.in -38m +.ds EX +.ds EY +.br +.. +.de EY +.br +.in +38m +.ti -38m +\\fB\*(EY\& \\fR\\$1 \\fR# \\$2 +.in -38m +.ds EX +.ds EY +.br +.. +.de Cx +.in +8 +.ft B +\&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.ft R +.in -8 +.. +.de DI +.ft B +\&\\$1 \\$2 \\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +.ft R +.. +.de SY +\\$3\\$1\\$2 +.. +.de HS +.sp 0.5 +.. +.de SP +.sp +.. +.\" Major section (normally numbered) +.de SE +.sp 1 +\fB\\$1\fP +.sp 1 +.. +.\" Subsection (normally numbered) +.de SS +.sp 1 +\fB\\$1\fP +.. +.de UU +.SH +\\$1 +.sp 1 +.. +.\" Start list +.de LI +.in +0.25i +.ll -0.25i +.HS +.. +.\" End list +.de LX +.sp 1 +.in -0.25i +.ll +0.25i +.LP +.. +.\" List item +.de IT +.HS +.. +.\"Short unnumbered lines +.de UN +.HS +.. diff --git a/commands/cawf/ms.mac b/commands/cawf/ms.mac new file mode 100755 index 000000000..7f97b6257 --- /dev/null +++ b/commands/cawf/ms.mac @@ -0,0 +1,210 @@ +.^b HF 1 +.^b fh 0 +.^b NH +.\"----------------- +.de R +.ie \\n(.$=0 .ft R +.el \\fR\\$1\\fP\\$2 +.. +.\"----------------- +.de I +.ie \\n(.$=0 .ft I +.el \\fI\\$1\\fP\\$2 +.. +.\"----------------- +.de B +.ie \\n(.$=0 .ft B +.el \\fB\\$1\\fP\\$2 +.. +.\"----------------- +.de UX +\\$2UNIX\\$1 +.. +.\"----------------- +.de DA +.ds DY "\\$1 \\$2 \\$3 +.\" keep trailing spaces out of CF +.if \\n(.$=1 .ds CF "\\$1 +.if \\n(.$=2 .ds CF "\\$1 \\$2 +.if \\n(.$>2 .ds CF "\\$1 \\$2 \\$3 +.. +.\"----------------- +.de ND +.\" it's our default, ignore it +.. +.\"----------------- +.de TL +.rs +.sp 5 +.ft B +.ce 9999 +.. +.\"----------------- +.de AU +.sp 2 +.ft R +.. +.\"----------------- +.de AI +.sp +.ft R +.. +.\"----------------- +.de AB +.sp 2 +.ce 0 +.ll -7n +.in +7n +.. +.\"----------------- +.de AE +.sp +.ll +.in +.. +.\"----------------- +.\" common initialization for headers and paragraphs: .In need extraspace +.de In +.ne \\$1 +.sp \\n(Tsu +.nr Ts 0 +.ie \\n(.$>1 .nr iN \\$2v +.el .nr iN 0 +.sp \\n(PDu+\\n(iNu +.ce 0 +.ft R +.in 0 +.ti 0 +.in \\n(inu +.ll \\n(LLu +.ns +.fi +.. +.\"----------------- +.de SH +.nr in 0 +.In 6 1 +.ft B +.. +.\"----------------- +.de NH +.nr in 0 +.In 6 1 +.ft B +.\" punt to awk code to get the header numbering right +.nH \\$1 +.\" and pick up the result +\&\\*(Nh +.. +.\"----------------- +.de LP +.In 4 +.. +.\"----------------- +.de PP +.In 4 +.ti \\n(PIu +.. +.\"----------------- +.de TP +.In 4 +.if \\n(.$>0 .nr tp \\$1n +.in \\n(inu+\\n(tpu +.ti 0-\\n(tpu +.it 1 tP +.. +.\"----------------- +.de IP +.ie \\n(.$>1 .TP "\\$2" +.el .TP 4n +\&\\$1 +.. +.\"----------------- +.de QP +.In 4 +.in +5n +.ll -5n +.. +.\"----------------- +.de QS +.nr in +5n +.nr LL -5n +.In 4 +.. +.\"----------------- +.de QE +.nr in -5n +.nr LL +5n +.In 4 +.. +.\"----------------- +.de DS +.In 5 +.if '\\$1'C' .ce 9999 +.if '\\$1'' .in +5n +.nf +.. +.\"----------------- +.de CD +.In 5 +.ce 9999 +.nf +.. +.\"----------------- +.de LD +.In 5 +.nf +.. +.\"----------------- +.de ID +.In 5 +.in +5n +.nf +.. +.\"----------------- +.de DE +.In 3 +.rs +.. +.\"----------------- +.de RS +.nr in +5n +.in \\n(inu +.. +.\"----------------- +.de RE +.nr in -5n +.in \\n(inu +.. +.\"----------------- +.de UL +\&\\fI$1\\fP +.. +.\"----------------- +.de RP +.. +.\"----------------- +.de LG +.. +.\"----------------- +.de SM +.. +.\"----------------- +.de NL +.. +.\"----------------- +.\" the -ms accent strings +.ds ' "'\b +.ds ` "`\b +.ds : ":\b +.ds ^ "^\b +.ds ~ "~\b +.ds C "v\b +.ds , ",\b +.\" post-title spacing +.nr Ts 4v +.\" and parameter setup +.nr LL 6i +.ll \n(LLu +.nr PD 0.3v +.nr PI 5n diff --git a/commands/cawf/nreq.c b/commands/cawf/nreq.c new file mode 100755 index 000000000..7c4e92479 --- /dev/null +++ b/commands/cawf/nreq.c @@ -0,0 +1,880 @@ +/* + * nreq.c - cawf(1) processing of nroff requests + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include "cawf.h" +#include + + +/* + * Prototypes for request processing functions. + */ + +_PROTOTYPE(static void nr_UL,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_Ub,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_Uc,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_Uf,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_Ur,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_ad,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_bp,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_br,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_ce,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_di,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_ds,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_fi,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_fl,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_ft,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_it,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_na,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_nf,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_ns,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_rm,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_rn,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_rr,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_rs,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_tm,(unsigned char *line, int brk)); +_PROTOTYPE(static void nr_tr,(unsigned char *line, int brk)); + +_PROTOTYPE(static void nr_nil,(unsigned char *line, int brk)); + + +/* + * NrReqt[] - nroff request processing table + * + * CAUTION: place new entries in their proper alphabetical order, since + * this table is processed with a binary search. + */ + +static struct nr_req { + char *nm; /* nroff request */ + void (*fun)(); /* processing function */ +} NrReqt[] = { + { "\\\"", nr_nil }, /* backslash-quote */ + { "^#", nr_UL }, + { "^=", nr_UL }, + { "^b", nr_Ub }, + { "^c", nr_Uc }, + { "^d", nr_nil }, + { "^e", nr_nil }, + { "^f", nr_Uf }, + { "^r", nr_Ur }, + { "^x", nr_nil }, + { "ad", nr_ad }, + { "bp", nr_bp }, + { "br", nr_br }, + { "ce", nr_ce }, + { "di", nr_di }, + { "ds", nr_ds }, + { "fi", nr_fi }, + { "fl", nr_fl }, + { "ft", nr_ft }, + { "hy", nr_nil }, + { "i0", nr_nil }, + { "it", nr_it }, + { "lg", nr_nil }, + { "li", nr_nil }, + { "na", nr_na }, + { "nf", nr_nf }, + { "ns", nr_ns }, + { "ps", nr_nil }, + { "rm", nr_rm }, + { "rn", nr_rn }, + { "rr", nr_rr }, + { "rs", nr_rs }, + { "tm", nr_tm }, + { "tr", nr_tr }, + { "vs", nr_nil }, +}; +/* + * Nreq(line, brk) - process miscellaneous nroff requests from line + * buffer with request status of (brk) + */ + +void +Nreq(line, brk) + unsigned char *line; + int brk; +{ + unsigned char c[3]; /* command buffer */ + int cmp, hi, low, mid; /* binary search indixes */ + + c[0] = c[1] = c[2] = '\0'; + if ((c[0] = line[1]) != '\0') + c[1] = line[2]; + + low = mid = 0; + hi = sizeof(NrReqt) / sizeof(struct nr_req); + while (low <= hi) { + mid = (low + hi) / 2; + if ((cmp = strcmp((char *)c, NrReqt[mid].nm)) < 0) + hi = mid - 1; + else if (cmp > 0) + low = mid + 1; + else { + (void) (*NrReqt[mid].fun)(line, brk); + return; + } + } + /* + * Unknown request starting with a '.' or '\''.. + */ + Error(WARN, LINE, " unknown request", NULL); +} + + +/* + * Adjust - "^[.']ad" + */ + +static void +nr_ad(line, brk) + unsigned char *line; + int brk; +{ + Pass3(NOBREAK, (unsigned char *)"both", NULL, 0); +} + + +/* +* Begin new page - "^[.']bp" +*/ + +static void +nr_bp(line, brk) + unsigned char *line; + int brk; +{ + Pass3(brk, (unsigned char *)"need", NULL, 999); +} + + +/* +* Break - "^[.']br" +*/ + +static void +nr_br(line, brk) + unsigned char *line; + int brk; +{ + Pass3(brk, (unsigned char *)"flush", NULL, 0); +} + + +/* + * Center - "^[.']ce" + */ + +static void +nr_ce(line, brk) + unsigned char *line; + int brk; +{ + unsigned char *s; /* string poiner */ + + if ((s = Field(2, line, 0)) != NULL) + Centering = atoi((char *)s); + else + Centering = 1; +} + + +/* + * Diversion on and off - "^[.']di" + */ + +static void +nr_di(line, brk) + unsigned char *line; + int brk; +{ + Pass3(DOBREAK, (unsigned char *)"flush", NULL, 0); + Divert ^= 1; +} + + +/* + * Define string - "^[.']ds" + */ + +static void +nr_ds(line, brk) + unsigned char *line; + int brk; +{ + unsigned char buf[MAXLINE]; /* temporary buffer */ + unsigned char nm[4], nm1[4]; /* name buffers */ + unsigned char *s1, *s2, *s3, /* temporary string pointers */ + *s4; + + if (Asmname(&line[3], nm) == 0) { + Error(WARN, LINE, " no name", NULL); + return; + } + s1 = Field(3, line, 0); + s2 = Findstr(nm, s1, 1); + while (*s2 == '\\' && *(s2 + 1) == '*') { + s2++; + s3 = Asmcode(&s2, nm1); + s2 = Findstr(nm1, NULL, 0); + } + if (Hdft) { + + /* + * Look for names LH, LF, CH, CF, RH, RF. + */ + if ((nm[0]=='L' || nm[0]=='C' || nm[0]=='R') + && (nm[1]=='F' || nm[1]=='H')) { + (void) sprintf((char *)buf, "%s", (char *)nm); + Pass3(NOBREAK, buf, s2, 0); + } + } +} + + + +/* + * Fill - "^[.']fi" + */ + +static void +nr_fi(line, brk) + unsigned char *line; + int brk; +{ + Fill = 1; + Pass3(brk, (unsigned char *)"flush", NULL, 0); +} + + +/* + * Flush - "^[.']fl" + */ + +static void +nr_fl(line, brk) + unsigned char *line; + int brk; +{ + Pass3(brk, (unsigned char *)"flush", NULL, 0); +} + + +/* + * Font - "^[.']ft " + */ + +static void +nr_ft(line, brk) + unsigned char *line; + int brk; +{ + int i; /* temporary index */ + + if (line[3] == '\0' || line[4] == '\0') + line[4] = 'P'; + if (line[4] == 'P') { + Font[0] = Prevfont; + return; + } + for (i = 0; Fcode[i].nm; i++) { + if (Fcode[i].nm == line[4]) + break; + } + if (Fcode[i].status == '\0') { + Error(WARN, LINE, " bad font code", NULL); + return; + } + Prevfont = Font[0]; + Font[0] = line[4]; +} + + +/* + * Input trap - "^[.']it [1 ]" + */ + +static void +nr_it(line, brk) + unsigned char *line; + int brk; + +{ + unsigned char buf[MAXLINE]; /* temporary buffer */ + int i; /* temporary index */ + unsigned char *s1, *s2; /* temporary string pointers */ + + if ((s1 = Field(2, line, 0)) == NULL) { + Free(&Aftnxt); + return; + } + if ((i = atoi((char *)s1)) != 1) { + Error(WARN, LINE, " first .it arg must be 1", NULL); + return; + } + if ((s2 = Field(3, line, 0)) == NULL) + Free(&Aftnxt); + else { + (void) sprintf((char *)buf, "%s,%s", + (Aftnxt == NULL) ? "" : (char *)Aftnxt, + (char *)s2); + Free(&Aftnxt); + Aftnxt = Newstr(buf); + } +} + + +/* + * Comment - "^[.']\\" - do nothing + * + * Debug - "^[.']\^d" - do nothing + * + * Finalization - "[.']\^e" - do nothing + * + * Error file - "^[.']\^x " - do nothing + * + * "^[.']i0", "^[.']lg" and "^[.']li" - do nothing + * + * Point size - "^[.']ps" - do nothing + * + * Vertical spacing - "^[.']vs" - do nothing + * + */ + +static void +nr_nil(line, brk) + unsigned char *line; + int brk; +{ +} + + +/* + * No adjust "^[.']na" + */ + +static void +nr_na(line, brk) + unsigned char *line; + int brk; +{ + Pass3(NOBREAK, (unsigned char *)"left", NULL, 0); +} + + +/* + * No fill - "^[.']nf" + */ + +static void +nr_nf(line, brk) + unsigned char *line; + int brk; +{ + Fill = 0; + Pass3(brk, (unsigned char *)"flush", NULL, 0); +} + + +/* + * No space - "^[.']ns" + */ + +static void +nr_ns(line, brk) + unsigned char *line; + int brk; +{ + Pass3(NOBREAK, (unsigned char *)"nospace", NULL, 0); +} + + +/* + * Remove macro or string - "^[.']rm" + */ + +static void +nr_rm(line, brk) + unsigned char *line; + int brk; +{ + int i; /* temporary index */ + unsigned char nm[4]; /* name buffer */ + + if (Asmname(&line[3], nm) == 0) { + Error(WARN, LINE, " no name", NULL); + return; + } + if ((i = Findmacro(nm, 0)) >= 0) { + Delmacro(i); + return; + } + (void) Findstr(nm, NULL, 0); + if (Sx >= 0) { + Delstr(Sx); + return; + } + Error(WARN, LINE, " no macro/string", NULL); +} + + +/* + * Rename macro or string - "^[.']rn" + */ + +static void +nr_rn(line, brk) + unsigned char *line; + int brk; +{ + int i, j; /* temporary indexes */ + unsigned char nm[4], nm1[4]; /* name buffers */ + unsigned char *s1; /* temporary string pointer */ + + if ((s1 = Field(2, line, 0)) == NULL || Asmname(s1, nm) == 0) { + Error(WARN, LINE, " no name", NULL); + return; + } + if ((s1 = Field(3, line, 0)) == NULL || Asmname(s1, nm1) == 0) { + Error(WARN, LINE, " no new name", NULL); + return; + } + if ((i = Findmacro(nm, 0)) >= 0) { + if ((j = Findmacro(nm1, 0)) >= 0) + Delmacro(j); + j = Findmacro(nm1, 1); + Macrotab[j].bx = Macrotab[i].bx; + Macrotab[i].bx = -1; + Macrotab[j].ct = Macrotab[i].ct; + Macrotab[i].ct = 0; + Delmacro(i); + return; + } + (void) Findstr(nm, NULL, 0); + if ((i = Sx) >= 0) { + (void) Findstr(nm1, Str[i].str, 1); + Delstr(i); + return; + } + if (Findmacro(nm1, 0) < 0) + (void) Findmacro(nm1, 1); +} + + +/* + * Remove register - "^[.']rr" + */ + +static void +nr_rr(line, brk) + unsigned char *line; + int brk; +{ + int i; /* temporary index */ + unsigned char nm[4]; /* name buffer */ + + if (Asmname(&line[3], nm) == 0) { + Error(WARN, LINE, " no name", NULL); + return; + } + if ((i = Findnum(nm, 0, 0)) < 0) { + Error(WARN, LINE, " no register", NULL); + return; + } + Delnum(i); +} + + +/* + * Resume space - "^[.']rs" + */ + +static void +nr_rs(line, brk) + unsigned char *line; + int brk; +{ + Pass3(NOBREAK, (unsigned char *)"yesspace", NULL, 0); +} + + +/* + * Message - "^[.']tm" + */ + +static void +nr_tm(line, brk) + unsigned char *line; + int brk; +{ + Pass3(MESSAGE, Inname, (line[3] == ' ') ? &line[4] : &line[3], NR); +} + + +/* + * Translate - "^[.']tr abcd..." + */ + +static void +nr_tr(line, brk) + unsigned char *line; + int brk; +{ + unsigned char buf[MAXLINE]; /* temporary buffer */ + int i, j; /* temporary indexes */ + unsigned char nm[4], nm1[4]; /* name buffers */ + unsigned char *s1, *s2; /* temporary string pointers */ + int trin, trout; /* types: 0 = char; 1 = named char */ + unsigned char xbuf[MAXLINE]; /* temporary buffer */ + + if (line[3] != ' ') { + Error(WARN, LINE, " unknown translation", NULL); + return; + } + for (s1 = &line[4]; *s1;) { + nm[1] = nm[2] = '\0'; + s2 = s1 + 1; + /* + * Assemble the input value. + */ + if (*s1 == '\\' && (*s2 == '*' || *s2 == '(')) { + if (*s2 == '(') { + /* + * Input is named character -- "\(xx". + */ + trin = 1; + s1 = s2 + 1; + if ((nm[0] = *s1) != '\0') { + s1++; + if ((nm[1] = *s1) != '\0') + s1++; + } + } else { + /* + * Input is interpolated string -- "\*x" or "\*(xx". + */ + s1 = Asmcode(&s2, nm); + if (*s1) + s1++; + s2 = Findstr(nm, NULL, 0); + if (*s2 != '\0') { + if ((strlen((char *)s2) + strlen((char *)s1) + 1) + >= MAXLINE) + Error(WARN, LINE, " string too long: ", (char *)nm); + else { + (void) sprintf((char *)buf, "%s%s", + (char *)s2, (char *)s1); + (void) strcpy((char *)xbuf, (char *)buf); + s1 = xbuf; + } + } + continue; + } + } else { + + /* + * Input is a simple character. + */ + trin = 0; + if ((nm[0] = *s1) != '\0') + s1++; + } + /* + * Assemble the output value. + */ + +assemble_output: + nm1[1] = nm1[2] = '\0'; + if (*s1 == '\0') { + + /* + * Supply a space if there is no output character. + */ + trout = 0; + nm1[0] = ' '; + } else { + s2 = s1 + 1; + if (*s1 == '\\' && (*s2 == '(' || *s2 == '*')) { + if (*s2 == '(') { + /* + * The output is a named character -- "\(xx". + */ + trout = 1; + s1 = s2 + 1; + if ((nm1[0] = *s1) != '\0') { + s1++; + if ((nm1[1] = *s1) != '\0') + s1++; + } + } else { + /* + * The output is an interpolated string -- * "\*x" or "\*(xx". + */ + s1 = Asmcode(&s2, nm1); + if (*s1) + s1++; + s2 = Findstr(nm1, NULL, 0); + if (*s2 != '\0') { + /* + * Interpolate a string value. + */ + if ((strlen((char *)s2) + strlen((char *)s1) + 1) + >= MAXLINE) + Error(WARN, LINE, " string too long: ", + (char *)nm); + else { + (void) sprintf((char *)buf, "%s%s", (char *)s2, + (char *)s1); + (void) strcpy((char *)xbuf, (char *)buf); + s1 = xbuf; + } + } + goto assemble_output; + } + } else { + trout = 0; + if ((nm1[0] = *s1) != '0') + s1++; + else + nm1[0] = ' '; + } + } + /* + * Do the translation. + */ + switch (trin) { + + case 0: /* simple char */ + switch (trout) { + + case 0: /* to simple char */ + Trtbl[(int)nm[0]] = nm1[0]; + break; + case 1: /* to named char */ + if ((i = Findchar(nm1, 0, NULL, 0)) < 0 + || strlen((char *)Schar[i].str) != 1) + Error(WARN, LINE, " bad named character: ", + (char *)nm1); + else + Trtbl[(int)nm[0]] = *(Schar[i].str); + break; + } + break; + case 1: /* named char */ + if ((i = Findchar(nm, 0, NULL, 0)) < 0) + Error(WARN, LINE, " unknown named character: ", (char *)nm); + else { + switch (trout) { + + case 0: /* to simple char */ + Free(&Schar[i].str); + Schar[i].str = Newstr(nm1); + Schar[i].len = 1; + break; + case 1: /* to named char */ + if ((j = Findchar(nm1, 0, NULL, 0)) < 0) + Error(WARN, LINE, " unknown named character: ", + (char *)nm1); + else + (void) Findchar(nm, Schar[j].len, Schar[j].str, 1); + break; + } + } + break; + } + } +} + + +/* + * Initialization - "^[.']\^b (fh|HF|NH) [01]" + * + * fh = first page header status + * HF = header/footer status + * NH = initialize number headers + */ + +static void +nr_Ub(line, brk) + unsigned char *line; + int brk; +{ + int i; /* temporary index */ + unsigned char *s1, *s2; /* temporary string pointers */ + + if ((s1 = Field(2, line, 0)) == NULL) + return; + if ((s2 = Field(3, line, 0)) == NULL) + i = 0; + else + i = atoi((char *)s2); + if (s1[0] == 'f' && s1[1] == 'h') + Pass3(NOBREAK, (unsigned char *)"fph", NULL, i); + else if (s1[0] == 'H' && s1[1] == 'F') + Hdft = i; + else if (s1[0] == 'N' && s1[1] == 'H') { + for (i = 0; i < MAXNHNR; i++) + Nhnr[i] = 0; + } else + Error(WARN, LINE, " unknown initialization", NULL); +} + + +/* + * Character definitions - "^[.']\^c" + */ + +static void +nr_Uc(line, brk) + unsigned char *line; + int brk; +{ + unsigned char buf[MAXLINE]; /* temporary buffer */ + int i; /* temporary index */ + unsigned char *s1, *s2, *s3, /* temporary string pointers */ + *s4, *s5; + + s2 = Field(2, line, 0); + i = atoi((char *)Field(3, line, 0)); + s4 = Field(4, line, 0); + if (i < 0 || i > MAXLINE/2 || *s2 == '\0') { + Error(WARN, LINE, " bad character definition", NULL); + return; + } + if (s4 == NULL) + s4 = (unsigned char *)""; + else if (*s4 == '"') + s4++; + s1 = buf; + while ((s5 = (unsigned char *)strchr((char *)s4, '\\')) != NULL) { + while (s5 > s4) + *s1++ = *s4++; + s4 = ++s5; + if (*s5 == '\\') + *s1++ = '\\'; + else if (*s5 == 'b') + *s1++ = '\b'; + if (*s4) + s4++; + } + while (*s1++ = *s4++) + ; + if (*s2 == 'h' && *(s2+1) == 'y') + (void) Findhy(buf, i, 1); + else + (void) Findchar(s2, i, buf, 1); +} + + +/* + * Font is OK - "[.']\^f " + */ + +static void +nr_Uf(line, brk) + unsigned char *line; + int brk; +{ + int i; /* temporary index */ + + if (line[3] != '\0' && line[4] != '\0') { + for (i = 0; Fcode[i].nm; i++) { + if (line[4] == Fcode[i].nm) { + Fcode[i].status = '1'; + return; + } + } + } + Error(WARN, LINE, " unknown font", NULL); +} + + +/* + * Resolutions - "[.']\^r cpi horizontal vertical" + */ + +static void +nr_Ur(line, brk) + unsigned char *line; + int brk; +{ + unsigned char buf[MAXLINE]; /* temporary buffer */ + int i, j; /* temporary indexes */ + double tval; /* temporary value */ + + if ((i = atoi((char *)Field(3, line, 0))) <= 0 + || (j = atoi((char *)Field(4, line, 0))) <= 0) { + Error(WARN, LINE, " bad cpi resolutions", NULL); + return; + } + tval = (double) (240.0 / (double) i); + if (Findscale((int)'m', tval, 1) < 0) + Error(FATAL, LINE, " missing Scal['m']", NULL); + Scalen = tval; + if (Scalen <= 0.0) { + (void) sprintf((char *)buf, " bad Scale['n'] (%f)", Scalen); + Error(FATAL, LINE, (char *)buf, NULL); + } + if (Findscale((int)'n', tval, 1) < 0) + Error(FATAL, LINE, " missing Scale['n']", NULL); + Scalev = (double) (240.0 / (double) j); + if (Scalev <= 0.0) { + (void) sprintf((char *)buf, " bad Scale['v'] (%f)", Scalen); + Error(FATAL, LINE, (char *)buf, NULL); + } + if (Findscale((int)'v', Scalev, 1) < 0) + Error(FATAL, LINE, " missing Scale['v']", NULL); +} + + +/* + * Set line number and file name - "^[.']\^# " + * + * Lock line number and file name - "^[.']\^= " + */ + +static void +nr_UL(line, brk) + unsigned char *line; + int brk; +{ + unsigned char *s1; /* temporary string pointer */ + + if ((s1 = Field(2, line, 0)) != NULL) + P2il = atoi((char *)s1) - 1; + else + P2il = 0; + Lockil = (line[2] == '#') ? 0 : 1; + Free(&P2name); + if (Field(3, line, 1) != NULL) { + P2name = F; + F = NULL; + } else + P2name = NULL; +} diff --git a/commands/cawf/output.c b/commands/cawf/output.c new file mode 100755 index 000000000..deace5ce3 --- /dev/null +++ b/commands/cawf/output.c @@ -0,0 +1,96 @@ +/* + * output-c - output support functions for cawf(1) + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include "cawf.h" + + +/* + * LenprtHF(s, p, t) - get length of print header or footer with page number + * interpolation + */ + +LenprtHF(s, p, t) + unsigned char *s; /* header/footer string */ + int p; /* page number */ + int t; /* type: 0 = get interpolated length + * 1 = print */ +{ + unsigned char buf[10]; /* buffer for page number */ + int len; /* line length */ + unsigned char *s1; /* temporary string pointer */ + + if (s == NULL) + return(0); + for (len = 0; *s && *s != '%'; s++) { + len++; + if (t) + Charput((int)*s); + } + if (*s) { + (void) sprintf((char *)buf, "%d", p); + for (s1 = buf; *s1; s1++) { + len++; + if (t) + Charput((int)*s1); + } + for (s++; *s; s++) { + len++; + if (t) + Charput((int)*s); + } + } + return(len); +} + + +/* + * Charput(s) - put a character to output, subject to diversion + */ + +void +Charput(c) + int c; /* character to put */ +{ + if (Divert == 0) + putchar((unsigned char)c); +} + + +/* + * Stringput(s) - put a string to output, subject to diversion + */ + +void +Stringput(s) + unsigned char *s; /* string to put */ +{ + if (Divert == 0) + fputs((char *)s, stdout); +} diff --git a/commands/cawf/pass2.c b/commands/cawf/pass2.c new file mode 100755 index 000000000..83e665fc2 --- /dev/null +++ b/commands/cawf/pass2.c @@ -0,0 +1,842 @@ +/* + * pass2.c - cawf(1) pass 2 function + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include "cawf.h" +#include + +/* + * Pass2(line) - process the nroff requests in a line and break + * text into words for pass 3 + */ + +void +Pass2(line) + unsigned char *line; +{ + int brk; /* request break status */ + unsigned char buf[MAXLINE]; /* working buffer */ + unsigned char c; /* character buffer */ + double d; /* temporary double */ + double exscale; /* expression scaling factor */ + double expr[MAXEXP]; /* expressions */ + unsigned char exsign[MAXEXP]; /* expression signs */ + int i, j; /* temporary indexes */ + int inword; /* word processing status */ + int nexpr; /* number of expressions */ + unsigned char nm[4], nm1[4]; /* names */ + int nsp; /* number of spaces */ + unsigned char op; /* expression term operator */ + unsigned char opstack[MAXSP]; /* expression operation stack */ + unsigned char period; /* end of word status */ + unsigned char *s1, *s2, *s3; /* temporary string pointers */ + double sexpr[MAXEXP]; /* signed expressions */ + int sp; /* expression stack pointer */ + unsigned char ssign; /* expression's starting sign */ + int tabpos; /* tab position */ + double tscale; /* term scaling factor */ + double tval; /* term value */ + double val; /* term value */ + double valstack[MAXSP]; /* expression value stack */ + unsigned char xbuf[MAXLINE]; /* expansion buffer */ + + if (line == NULL) { + /* + * End of macro expansion. + */ + Pass3(DOBREAK, (unsigned char *)"need", NULL, 999); + return; + } + /* + * Adjust line number. + */ + if (Lockil == 0) + P2il++; + /* + * Empty line - "^[ \t]*$" or "^\\\"". + */ + if (regexec(Pat[6].pat, line) + || strncmp((char *)line, "\\\"", 2) == 0) { + Pass3(DOBREAK, (unsigned char *)"space", NULL, 0); + return; + } + /* + * Line begins with white space. + */ + if (*line == ' ' || *line == '\t') { + Pass3(DOBREAK, (unsigned char *)"flush", NULL, 0); + Pass3(0, (unsigned char *)"", NULL, 0); + } + if (*line != '.' && *line != '\'') { + /* + * Line contains text (not an nroff request). + */ + if (Font[0] == 'R' && Backc == 0 && Aftnxt == NULL + && regexec(Pat[7].pat, line) == 0) { + /* + * The font is Roman, there is no "\\c" or "after next" + * trap pending and and the line has no '\\', '\t', '-', + * or " " (regular expression "\\|\t|-| "). + * + * Output each word of the line as " ". + */ + for (s1 = line;;) { + while (*s1 && *s1 == ' ') + s1++; + if (*s1 == '\0') + break; + for (s2 = s1, s3 = buf; *s2 && *s2 != ' ';) + *s3++ = Trtbl[(int)*s2++]; + *s3 = '\0'; + Pass3((s2 - s1), buf, NULL, 0); + s1 = *s2 ? ++s2 : s2; + } + /* + * Line terminates with punctuation and optional + * bracketing (regular expression "[.!?:][\])'\"*]*$"). + */ + if (regexec(Pat[8].pat, line)) + Pass3(NOBREAK, (unsigned char *)"gap", NULL, 2); + if (Centering > 0) { + Pass3(DOBREAK,(unsigned char *)"center", NULL, + 0); + Centering--; + } else if (Fill == 0) + Pass3(DOBREAK, (unsigned char *)"flush", NULL, + 0); + return; + } + /* + * Line must be scanned a character at a time. + */ + inword = nsp = tabpos = 0; + period = '\0'; + for (s1 = line;; s1++) { + /* + * Space or TAB causes state transition. + */ + if (*s1 == '\0' || *s1 == ' ' || *s1 == '\t') { + if (inword) { + if (!Backc) { + Endword(); + Pass3(Wordl, Word, NULL, 0); + if (Uhyph) { + Pass3(NOBREAK, + (unsigned char *)"nohyphen", + NULL, 0); + } + } + inword = 0; + nsp = 0; + } + if (*s1 == '\0') + break; + } else { + if (inword == 0) { + if (Backc == 0) { + Wordl = Wordx = 0; + Uhyph = 0; + } + Backc = 0; + inword = 1; + if (nsp > 1) { + Pass3(NOBREAK, + (unsigned char *)"gap", + NULL, nsp); + } + } + } + /* + * Process a character. + */ + switch (*s1) { + /* + * Space + */ + case ' ': + nsp++; + period = '\0'; + break; + /* + * TAB + */ + case '\t': + tabpos++; + if (tabpos <= Ntabs) { + Pass3(NOBREAK, + (unsigned char *)"tabto", NULL, + Tabs[tabpos-1]); + } + nsp = 0; + period = '\0'; + break; + /* + * Hyphen if word is being assembled + */ + case '-': + if (Wordl <= 0) + goto ordinary_char; + if ((i = Findhy(NULL, 0, 0)) < 0) { + Error(WARN, LINE, " no hyphen for font ", + (char *)Font); + return; + } + Endword(); + Pass3(Wordl, Word, NULL, Hychar[i].len); + Pass3(NOBREAK, (unsigned char *)"userhyphen", + Hychar[i].str, Hychar[i].len); + Wordl = Wordx = 0; + period = '\0'; + Uhyph = 1; + break; + /* + * Backslash + */ + case '\\': + s1++; + switch(*s1) { + /* + * Comment - "\\\"" + */ + case '"': + while (*(s1+1)) + s1++; + break; + /* + * Change font - "\\fN" + */ + case 'f': + s1 = Asmcode(&s1, nm); + if (nm[0] == 'P') { + Font[0] = Prevfont; + break; + } + for (i = 0; Fcode[i].nm; i++) { + if (Fcode[i].nm == nm[0]) + break; + } + if (Fcode[i].nm == '\0' + || nm[1] != '\0') { + Error(WARN, LINE, " unknown font ", + (char *)nm); + break; + } + if (Fcode[i].status != '1') { + Error(WARN, LINE, + " font undefined ", (char *)nm); + break; + } else { + Prevfont = Font[0]; + Font[0] = nm[0]; + } + break; + /* + * Positive horizontal motion - "\\h\\n(NN" or + * "\\h\\nN" + */ + case 'h': + if (s1[1] != '\\' || s1[2] != 'n') { + Error(WARN, LINE, + " no \\n after \\h", NULL); + break; + } + s1 +=2; + s1 = Asmcode(&s1, nm); + if ((i = Findnum(nm, 0, 0)) < 0) + goto unknown_num; + if ((j = Numb[i].val) < 0) { + Error(WARN, LINE, " \\h < 0 ", + NULL); + break; + } + if (j == 0) + break; + if ((strlen((char *)s1+1) + j + 1) + >= MAXLINE) + goto line_too_long; + for (s2 = &xbuf[1]; j; j--) + *s2++ = ' '; + (void) strcpy((char *)s2, (char *)s1+1); + s1 = xbuf; + break; + /* + * Save current position in register if "\\k" + */ + case 'k': + s1 = Asmcode(&s1, nm); + if ((i = Findnum(nm, 0, 0)) < 0) + i = Findnum(nm, 0, 1); + Numb[i].val = + (int)((double)Outll * Scalen); + break; + /* + * Interpolate number - "\\n(NN" or "\\nN" + */ + case 'n': + s1 = Asmcode(&s1, nm); + if ((i = Findnum(nm, 0, 0)) < 0) { +unknown_num: + Error(WARN, LINE, + " unknown number register ", + (char *)nm); + break; + } + (void) sprintf((char *)buf, "%d", + Numb[i].val); + if ((strlen((char *)buf) + + strlen((char *)s1+1) + 1) + >= MAXLINE) { +line_too_long: + Error(WARN, LINE, " line too long", + NULL); + break; + } + (void) sprintf((char *)buf, "%d%s", + Numb[i].val, (char *)s1+1); + (void) strcpy((char *)&xbuf[1], + (char *)buf); + s1 = xbuf; + break; + /* + * Change size - "\\s[+-][0-9]" - NOP + */ + case 's': + s1++; + if (*s1 == '+' || *s1 == '-') + s1++; + while (*s1 && isdigit(*s1)) + s1++; + s1--; + break; + /* + * Continue - "\\c" + */ + case 'c': + Backc = 1; + break; + /* + * Interpolate string - "\\*(NN" or "\\*N" + */ + case '*': + s1 = Asmcode(&s1, nm); + s2 = Findstr(nm, NULL, 0); + if (*s2 != '\0') { + if ((strlen((char *)s2) + + strlen((char *)s1+1) + 1) + >= MAXLINE) + goto line_too_long; + (void) sprintf((char *)buf, "%s%s", + (char *)s2, (char *)s1+1); + (void) strcpy((char *)&xbuf[1], + (char *)buf); + s1 = xbuf; + } + break; + /* + * Discretionary hyphen - "\\%" + */ + case '%': + if (Wordl <= 0) + break; + if ((i = Findhy(NULL, 0, 0)) < 0) { + Error(WARN, LINE, + " no hyphen for font ", + (char *)Font); + break; + } + Endword(); + Pass3(Wordl, Word, NULL, Hychar[i].len); + Pass3(NOBREAK, + (unsigned char *) "hyphen", + Hychar[i].str, Hychar[i].len); + Wordl = Wordx = 0; + Uhyph = 1; + break; + /* + * None of the above - may be special character + * name. + */ + default: + s2 = s1--; + s1 = Asmcode(&s1, nm); + if ((i = Findchar(nm, 0, NULL, 0)) < 0){ + s1 = s2; + goto ordinary_char; + } + if (strcmp((char *)nm, "em") == 0 + && Wordx > 0) { + /* + * "\\(em" is a special case when a word + * has been assembled, because of + * hyphenation. + */ + Endword(); + Pass3(Wordl, Word, NULL, + Schar[i].len); + Pass3(NOBREAK, + (unsigned char *)"userhyphen", + Schar[i].str, Schar[i].len); + Wordl = Wordx = 0; + period = '\0'; + Uhyph = 1; + } + /* + * Interpolate a special character + */ + if (Str2word(Schar[i].str, + strlen((char *)Schar[i].str)) != 0) + return; + Wordl += Schar[i].len; + period = '\0'; + } + break; + /* + * Ordinary character + */ + default: +ordinary_char: + if (Str2word(s1, 1) != 0) + return; + Wordl++; + if (*s1 == '.' || *s1 == '!' + || *s1 == '?' || *s1 == ':') + period = '.'; + else if (period == '.') { + nm[0] = *s1; + nm[1] = '\0'; + if (regexec(Pat[13].pat, nm) == 0) + period = '\0'; + } + } + } + /* + * End of line processing + */ + if (!Backc) { + if (period == '.') + Pass3(NOBREAK, (unsigned char *)"gap", NULL, 2); + if (Centering > 0) { + Pass3(DOBREAK, (unsigned char *)"center", NULL, + 0); + Centering--; + } else if (!Fill) + Pass3(DOBREAK, (unsigned char *)"flush", NULL, + 0); + } + if (Aftnxt == NULL) + return; + /* else fall through to process an "after next trap */ + } + /* + * Special -man macro handling. + */ + if (Marg == MANMACROS) { + /* + * A text line - "^[^.]" - is only processed when there is an + * "after next" directive. + */ + if (*line != '.' && *line != '\'') { + if (Aftnxt != NULL) { + if (regexec(Pat[9].pat, Aftnxt)) /* ",fP" */ + Font[0] = Prevfont; + if (regexec(Pat[16].pat, Aftnxt)) /* ",fR" */ + Font[0] = 'R'; + if (regexec(Pat[10].pat, Aftnxt)) /* ",tP" */ + Pass3(DOBREAK, + (unsigned char *)"toindent", + NULL, 0); + Free(&Aftnxt); + } + return; + } + /* + * Special footer handling - "^.lF" + */ + if (line[1] == 'l' && line[2] == 'F') { + s1 = Findstr((unsigned char *)"by", NULL, 0); + s2 = Findstr((unsigned char *)"nb", NULL, 0); + if (*s1 == '\0' || *s2 == '\0') + (void) sprintf((char *)buf, "%s%s", + (char *)s1, (char *)s2); + else + (void) sprintf((char *)buf, "%s; %s", + (char *)s1, (char *)s2); + Pass3(NOBREAK, (unsigned char *)"LF", buf, 0); + return; + } + } + /* + * Special -ms macro handling. + */ + if (Marg == MSMACROS) { + /* + * A text line - "^[^.]" - is only processed when there is an + * "after next" directive. + */ + if (*line != '.' && *line != '\'') { + if (Aftnxt != NULL) { + if (regexec(Pat[10].pat, Aftnxt)) /* ",tP" */ + Pass3(DOBREAK, + (unsigned char *)"toindent", + NULL, 0); + Free(&Aftnxt); + } + return; + } + /* + * Numbered headings - "^[.']nH" + */ + if (line[1] == 'n' && line[2] == 'H') { + s1 = Field(2, line, 0); + if (s1 != NULL) { + i = atoi((char *)s1) - 1; + if (i < 0) { + for (j = 0; j < MAXNHNR; j++) { + Nhnr[j] = 0; + } + i = 0; + } else if (i >= MAXNHNR) { + (void) sprintf((char *)buf, + " over NH limit (%d)", MAXNHNR); + Error(WARN, LINE, (char *)buf, NULL); + } + } else + i = 0; + Nhnr[i]++; + for (j = i + 1; j < MAXNHNR; j++) { + Nhnr[j] = 0; + } + s1 = buf; + for (j = 0; j <= i; j++) { + (void) sprintf((char *)s1, "%d.", Nhnr[j]); + s1 = buf + strlen((char *)buf); + } + (void) Findstr((unsigned char *)"Nh", buf, 1); + return; + } + } + /* + * Remaining lines should begin with a '.' or '\'' unless an "after next" + * trap has failed. + */ + if (*line != '.' && *line != '\'') { + if (Aftnxt != NULL) + Error(WARN, LINE, " failed .it: ", (char *)Aftnxt); + else + Error(WARN, LINE, " unrecognized line ", NULL); + return; + } + brk = (*line == '.') ? DOBREAK : NOBREAK; + /* + * Evaluate expressions for "^[.'](ta|ll|ls|in|ti|po|ne|sp|pl|nr)" + * Then process the requests. + */ + if (regexec(Pat[11].pat, &line[1])) { + /* + * Establish default scale factor. + */ + if ((line[1] == 'n' && line[2] == 'e') + || (line[1] == 's' && line[2] == 'p') + || (line[1] == 'p' && line[2] == 'l')) + exscale = Scalev; + else if (line[1] == 'n' && line[2] == 'r') + exscale = Scaleu; + else + exscale = Scalen; + /* + * Determine starting argument. + */ + if (line[1] == 'n' && line[2] == 'r') + s1 = Field(2, &line[3], 0); + else + s1 = Field(1, &line[3], 0); + /* + * Evaluate expressions. + */ + for (nexpr = 0; s1 != NULL &&*s1 != '\0'; ) { + while (*s1 == ' ' || *s1 == '\t') + s1++; + if (*s1 == '+' || *s1 == '-') + ssign = *s1++; + else + ssign = '\0'; + /* + * Process terms. + */ + val = 0.0; + sp = -1; + c = '+'; + s1--; + while (c == '+' || c == '*' || c == '%' + || c == ')' || c == '-' || c == '/') { + op = c; + s1++; + tscale = exscale; + tval = 0.0; + /* + * Pop stack on right parenthesis. + */ + if (op == ')') { + tval = val; + if (sp >= 0) { + val = valstack[sp]; + op = opstack[sp]; + sp--; + } else { + Error(WARN, LINE, + " expression stack underflow", NULL); + return; + } + tscale = Scaleu; + /* + * Push stack on left parenthesis. + */ + } else if (*s1 == '(') { + sp++; + if (sp >= MAXSP) { + Error(WARN, LINE, + " expression stack overflow", NULL); + return; + } + valstack[sp] = val; + opstack[sp] = op; + val = 0.0; + c = '+'; + continue; + } else if (*s1 == '\\') { + s1++; + switch(*s1) { + /* + * "\\"" begins a comment. + */ + case '"': + while (*s1) + s1++; + break; + /* + * Crude width calculation for "\\w" + */ + case 'w': + s2 = ++s1; + if (*s1) { + s1++; + while (*s1 && *s1 != *s2) + s1++; + tval = (double) (s1 - s2 - 1) * Scalen; + if (*s1) + s1++; + } + break; + /* + * Interpolate number register if "\\n". + */ + case 'n': + s1 = Asmcode(&s1, nm); + if ((i = Findnum(nm, 0, 0)) >= 0) + tval = Numb[i].val; + s1++; + } + /* + * Assemble numeric value. + */ + } else if (*s1 == '.' || isdigit(*s1)) { + for (i = 0; isdigit(*s1) || *s1 == '.'; s1++) { + if (*s1 == '.') { + i = 10; + continue; + } + d = (double) (*s1 - '0'); + if (i) { + tval = tval + (d / (double) i); + i = i * 10; + } else + tval = (tval * 10.0) + d; + } + } else { + /* + * It's not an expression. Ignore extra scale. + */ + if ((i = Findscale((int)*s1, 0.0, 0)) < 0) { + (void) sprintf((char *)buf, + " \"%s\" isn't an expression", + (char *)s1); + Error(WARN, LINE, (char *)buf, NULL); + } + s1++; + } + /* + * Add term to expression value. + */ + if ((i = Findscale((int)*s1, 0.0, 0)) >= 0) { + tval *= Scale[i].val; + s1++; + } else + tval *= tscale; + switch (op) { + case '+': + val += tval; + break; + case '-': + val -= tval; + break; + case '*': + val *= tval; + break; + case '/': + case '%': + i = (int) val; + j = (int) tval; + if (j == 0) { + Error(WARN, LINE, + (*s1 == '/') ? "div" : "mod", + " by 0"); + return; + } + if (op == '/') + val = (double) (i / j); + else + val = (double) (i % j); + break; + } + c = *s1; + } + /* + * Save expression value and sign. + */ + if (nexpr >= MAXEXP) { + (void) sprintf((char *)buf, + " at expression limit of %d", MAXEXP); + Error(WARN, LINE, (char *)buf, NULL); + return; + } + exsign[nexpr] = ssign; + expr[nexpr] = val; + if (ssign == '-') + sexpr[nexpr] = -1.0 * val; + else + sexpr[nexpr] = val; + nexpr++; + while (*s1 == ' ' || *s1 == '\t') + s1++; + } + /* + * Set parameters "(ll|ls|in|ti|po|pl)" + */ + if (regexec(Pat[12].pat, &line[1])) { + nm[0] = line[1]; + nm[1] = line[2]; + if ((i = Findparms(nm)) < 0) { + Error(WARN, LINE, + " can't find parameter register ", + (char *)nm); + return; + } + if (nexpr == 0 || exscale == 0.0) + j = Parms[i].prev; + else if (exsign[0] == '\0' + || (nm[0] == 't' && nm[1] == 'i')) + j = (int)(sexpr[0] / exscale); + else + j = Parms[i].val + (int)(sexpr[0] / exscale); + Parms[i].prev = Parms[i].val; + Parms[i].val = j; + nm[0] = (nexpr) ? exsign[0] : '\0'; /* for .ti */ + nm[1] = '\0'; + Pass3(brk, (unsigned char *)Parms[i].cmd, nm, j); + return; + } + if (line[1] == 'n') { + switch(line[2]) { + /* + * Need - "^[.']ne " + */ + case 'e': + if (nexpr && Scalev > 0.0) + i = (int) ((expr[0]/Scalev) + 0.99); + else + i = 0; + Pass3(DOBREAK, (unsigned char *)"need", NULL, + i); + return; + /* + * Number - "^[.']nr " + */ + case 'r': + if ((s1 = Field(2, line, 0)) == NULL) { + Error(WARN, LINE, " bad number register", + NULL); + return; + } + if ((i = Findnum(s1, 0, 0)) < 0) + i = Findnum(s1, 0, 1); + if (nexpr < 1) { + Numb[i].val = 0; + return; + } + if (exsign[0] == '\0') + Numb[i].val = (int) expr[0]; + else + Numb[i].val += (int) sexpr[0]; + return; + } + } + /* + * Space - "^[.']sp " + */ + if (line[1] == 's' && line[2] == 'p') { + if (nexpr == 0) + i = 1; + else + i = (int)((expr[0] / Scalev) + 0.99); + while (i--) + Pass3(brk, (unsigned char *)"space", NULL, 0); + return; + } + /* + * Tab positions - "^[.']ta . . ." + */ + if (line[1] == 't' && line[2] == 'a') { + tval = 0.0; + for (j = 0; j < nexpr; j++) { + if (exsign[j] == '\0') + tval = expr[j]; + else + tval += sexpr[j]; + Tabs[j] = (int) (tval / Scalen); + } + Ntabs = nexpr; + return; + } + } + /* + * Process all other nroff requests via Nreq(). + */ + (void) Nreq(line, brk); + return; +} diff --git a/commands/cawf/pass3.c b/commands/cawf/pass3.c new file mode 100755 index 000000000..abacbfa3c --- /dev/null +++ b/commands/cawf/pass3.c @@ -0,0 +1,614 @@ +/* + * pass3.c - cawf(1) pass 3 function + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include "cawf.h" + +void +Pass3(len, word, sarg, narg) + int len; /* length (negative is special) */ + unsigned char *word; /* word */ + unsigned char *sarg; /* string argument */ + int narg; /* numeric argument */ +{ + int addto; /* spaces to add to all words */ + int i, j, k; /* temporary index */ + unsigned char msg[MAXLINE]; /* message buffer */ + int n; /* temporary number */ + unsigned char *s1; /* temporary string pointer */ + int sp = 0; /* no-break spacing switch */ + int sp_Outll; /* sp-saved Outll */ + char sp_Outln; /* sp-saved Outln[0] */ + int sp_Outlx; /* sp-saved Outlx */ + int sp_Padx; /* sp-saved Padx */ + int sp_Tind; /* sp-saved Tind */ + int wl; /* real word length */ + int xsp; /* extra spaces to add */ + int vsp; /* vertical spacing status */ + + vsp = 0; + if (word != NULL) + wl = strlen((char *)word); + /* + * If not a special command, process a word. + */ + if (len >= 0 && Outll < 0) { + /* + * Enter first word. + */ + (void) strcpy((char *)Outln, (char *)word); + Outll = len; + Outlx = wl; + Padx = 0; + } else if (len >= 0 + && (Outll+Contlen+len+narg) <= (LL-Pgoff-Ind-Tind)) { + /* + * The word fits, so enter it. + */ + if ((Contlen + len) > 0) { +line_too_big: + if ((Outlx + Contlen + wl) >= MAXOLL) { + Error3(len, (char *)word, (char *)sarg, narg, + "output line too big"); + return; + } else { + if (Contlen > 0 && Cont != NULL) { + if (Contlen == 1 && *Cont == ' ') { + Padchar[Padx++] = Outlx; + Outln[Outlx++] = ' '; + } else { + (void) strcpy((char *)&Outln[Outlx], + (char *)Cont); + Outlx += Contlen; + } + } + if (len > 0) { + (void) strcpy((char *)&Outln[Outlx], + (char *)word); + Outlx += wl; + } + } + } + Outll += Contlen + len; + } else if (len == NOBREAK || len == MESSAGE) { + /* + * Do nothing (equivalent to break) + */ + } else if (len == DOBREAK && strcmp((char *)word, "need") == 0 + && (Nxtln + narg) < (Pglen + 1 - Botmarg)) { + /* + * Do nothing, because there is room on the page. + */ + } else if (len == DOBREAK && strcmp((char *)word, "toindent") == 0 + && (Ind + Tind + Outll) < Ind) { + /* + * Move to indent position with line - there is room. + */ + n = Ind - (Ind + Tind +Outll); + Outll += n; + if ((Outlx + n) >= MAXOLL) + goto line_too_big; + for (i = n; i; i--) + Outln[Outlx++] = ' '; + Padx = 0; + Free(&Cont); + Contlen = 0; + } else if (Outll >= 0 + || (len == DOBREAK && strcmp((char *)word, "need") == 0)) { + /* + * A non-empty line or a "need" forces output. + */ + vsp = 0; + +print_line: + if (Nxtln == 1) { + /* + * We're at the top of the page, so issue the header. + */ + if (Thispg > 1) + Charput((int)'\f'); + for (i = (Topmarg - 1)/2; i > 0; i--) { + Charput((int)'\n'); + Nxtln++; + } + /* + * Print the page header, as required. + */ + if (Fph || Thispg > 1) { + i = LenprtHF(Hdc, Thispg, 0) + + LenprtHF(Hdl, Thispg, 0) + + LenprtHF(Hdr, Thispg, 0) + 2; + j = (LL - i - Pgoff) / 2 + 1; + n = LL - Pgoff - i - j + 2; + for (k = 0; k < Pgoff; k++) + Charput((int)' '); + if (Hdl) + LenprtHF(Hdl, Thispg, 1); + while (j-- > 0) + Charput((int)' '); + if (Hdc) + LenprtHF(Hdc, Thispg, 1); + while (n-- > 0) + Charput((int)' '); + if (Hdr) + LenprtHF(Hdr, Thispg, 1); + Charput((int)'\n'); + } else + Charput((int)'\n'); + Nxtln++; + while(Nxtln <= Topmarg) { + Charput((int)'\n'); + Nxtln++; + } + } + /* + * Add a trailing hyphen, if mecessary. + */ + if (vsp == 0 && Eollen > 0 && Eol != NULL) { + i = strlen((char *)Eol); + if ((Outlx + i) >= MAXOLL) + goto line_too_big; + (void) strcpy((char *)&Outln[Outlx], (char *)Eol); + Outlx += i; + Outll += Eollen; + } + /* + * Trim trailing spaces from the output line. + */ + while (Outlx > 0) { + if (Outln[Outlx - 1] != ' ') + break; + if (Padx > 0 && (Outlx - 1) == Padchar[Padx - 1]) + Padx--; + Outlx--; + Outln[Outlx] = '\0'; + Outll--; + } + if (Outlx == 0) + Charput((int)'\n'); + else if (len == DOBREAK && strcmp((char *)word, "center") == 0) + { + /* + * Center the output line. + */ + i = (LL - Pgoff - Outll) / 2; + if (i < 0) + i = 0; + for (j = (Pgoff + Ind + Tind + i); j; j--) + Charput((int)' '); + Stringput(Outln); + Charput((int)'\n'); + } else if (Adj == LEFTADJ + || (Adj == BOTHADJ && (len < 0 || Padx == 0))) { + /* + * No right margin adjustment - disabled, inappropriate + * (line ended by break) or impossible. + */ + for (i = 0; i < (Pgoff + Ind + Tind); i++) + Charput((int)' '); + Stringput(Outln); + Charput((int)'\n'); + } else if (Adj == BOTHADJ) { + /* + * Adjust right margin. + */ + for (i = 0; i < (Pgoff + Ind + Tind); i++) + Charput((int)' '); + i = LL - (Pgoff + Ind + Tind); + j = i - Outll; + addto = Padx ? (j / Padx) : 0; + xsp = j - (Padx * addto); + for (i = 0, s1 = Outln; i < Padx; i++) { + while (*s1 && (s1 - Outln) <= Padchar[i]) + Charput((int)*s1++); + if (*s1 == '\0') + break; + j = addto; + if (Padfrom == PADLEFT) { + if (i < xsp) + j++; + } else if (i >= (Padx - xsp)) + j++; + while (j-- > 0) + Charput((int)' '); + } + while (*s1) + Charput((int)*s1++); + Charput((int)'\n'); + Padfrom = (Padfrom == PADLEFT) ? PADRIGHT : PADLEFT; + } + /* + * End of line housekeeping + */ + Nxtln++; + Outll = -1; + Outlx = 0; + Padx = 0; + Tind = 0; + Nospmode = 0; + if (vsp == 0 && len == DOBREAK + && strcmp((char *)word, "need") == 0) { + /* + * Break caused by "need" - satisfy it. + */ + while (Nxtln < (Pglen + 1 - Botmarg)) { + Charput((int)'\n'); + Nxtln++; + } + } + if (Nxtln >= (Pglen + 1 - Botmarg)) { + /* + * Footer required + */ + for (i = (Botmarg - 1)/2; i > 0; i--) { + Charput((int)'\n'); + Nxtln++; + } + i = LenprtHF(Ftl, Thispg, 0) + LenprtHF(Ftc, Thispg, 0) + + LenprtHF(Ftr, Thispg, 0) + 2; + j = (LL - i - Pgoff) / 2 + 1; + n = LL - Pgoff - i - j + 2; + for (k = 0; k < Pgoff; k++) + Charput((int)' '); + if (Ftl) + LenprtHF(Ftl, Thispg, 1); + while (j-- > 0) + Charput((int)' '); + if (Ftc) + LenprtHF(Ftc, Thispg, 1); + while (n-- > 0) + Charput((int)' '); + if (Ftr) + LenprtHF(Ftr, Thispg, 1); + Charput((int)'\n'); + Nxtln++; + /* + * The last blank line on the page is suppressed to assist + * printers that can't look ahead to the following FF. + */ + while (Nxtln < Pglen) { + Charput((int)'\n'); + Nxtln++; + } + Nxtln = 1; + Thispg++; + Nospmode = 1; + Padfrom = PADRIGHT; + } + /* + * Initiate any extra vertical spacing. + */ + if (++vsp < Vspace) + goto print_line; + /* + * Save any input word that might have forced output. + */ + if (len >= 0) { + (void) strcpy((char *)Outln, (char *)word); + Outll = len; + Outlx = wl; + Padx = 0; + } + } + /* + * A break causes padding reversal. + */ + if (len == DOBREAK) + Padfrom = PADRIGHT; + if (len >= 0 || strcmp((char *)word, "nohyphen") == 0) { + /* + * Reset continuation and hyphenation. + */ + if (Contlen != 1 || Cont[0] != ' ') { + Free(&Cont); + Cont = Newstr((unsigned char *)" "); + Contlen = 1; + } + if (Eollen > 0) { + Free(&Eol); + Eollen = 0; + } + return; + } + /* + * Now post-process any special commands. + */ + if (len == MESSAGE) { + Error3(len, (char *)word, (char *)sarg, narg, NULL); + return; + } + + switch (*word) { + + case 'b': /* both */ + /* + * Adjust on both margins. + */ + Adj = BOTHADJ; + return; + + case 'c': /* center */ + return; + + case 'e': /* errsto */ + /* + * "errsto" comes from awf. + */ + return; + + case 'f': /* flush and fph */ + if (word[1] == 'l') + return; + else if (word[1] == 'p') { + /* + * First page header status + */ + Fph = narg; + return; + } + break; + + case 'g': /* gap */ + /* + * Increase word gap. (Space is not paddable.) + */ + if (Outll >= 0) { + if ((Outlx + narg - 1) >= MAXOLL) + goto line_too_big; + for (i = 0; i < (narg - 1); i++) { + Outln[Outlx++] = ' '; + Outll++; + } + } + return; + + case 'h': /* hyphen */ + /* + * Set discretionary hyphen. + */ + Free(&Cont); + Contlen = 0; + Free(&Eol); + Eol = (sarg != NULL) ? Newstr(sarg) : NULL; + Eollen = narg; + return; + + case 'i': /* indent */ + /* + * Set indentation. + */ + Ind = narg; + return; + + case 'l': /* left or linelen */ + if (word[1] == 'e') { + /* + * Adjust on left margin. + */ + Adj = LEFTADJ; + return; + } else if (word[1] == 'i') { + /* + * Set line length. + */ + LL = narg; + return; + } + break; + + case 'n': /* need or nospace */ + if (word[1] == 'e') + return; /* need */ + else if (word[1] == 'o') { + /* + * Set no space mode. + */ + Nospmode = 1; + return; + } + break; + + case 'p': /* pagelen or pageoffset */ + if (strncmp((char *)&word[1], "age", 3) != 0) + break; + if (word[4] == 'l') { + /* + * Set page length. + */ + Pglen = narg; + return; + } else if (word[4] == 'o') { + /* + * Set page offset. + */ + Pgoff = narg; + return; + } + break; + + case 's': /* space */ + if (sp) { + + /* + * Restore values after NOBREAK spacing ("^'sp"). + */ + Outlx = sp_Outlx; + Outln[0] = sp_Outln; + Padx = sp_Padx; + Outll = sp_Outll; + Tind = sp_Tind; + return; + } + if (Nospmode == 0) { + if (len == NOBREAK) { + + /* + * Set up for NOBREAK spacing. + */ + sp_Outlx = Outlx; + sp_Outln = Outln[0]; + sp_Padx = Padx; + sp_Outll = Outll; + sp_Tind = Tind; + vsp = Vspace + 1; + sp = 1; + } + /* + * Generate a blank line. + */ + Outlx = 0; + Outln[0] = '\0'; + Padx = 0; + Outll = LL - 1; + if (sp) + goto print_line; + } + return; + + case 't': /* tabto, tempindent, or + * toindent */ + if (word[1] == 'a') { + /* + * Move to TAB stop. + */ + if (Outll < 0) + Outll = 0; + if ((n = narg - Outll) > 0) { + if ((Outlx + n) >= MAXOLL) + goto line_too_big; + Outll += n; + for (i = n; i > 0; i--) + Outln[Outlx++] = ' '; + Free(&Cont); + Contlen = 0; + Padx = 0; + } + return; + } else if (word[1] == 'e') { + /* + * Set temporary indentation. + */ + if (*sarg == '\0' && narg >= 0) + Tind = narg - Ind; + else + Tind = ((Ind + narg) >= 0) ? narg : -Ind; + return; + } else if (word[1] == 'o') + return; /* toindent */ + break; + + case 'u': /* userhyphen */ + /* + * Set line length. + */ + Free(&Cont); + Free(&Eol); + Contlen = Eollen = narg; + Cont = (sarg == NULL) ? NULL : Newstr(sarg); + Eol = (sarg == NULL) ? NULL : Newstr(sarg); + return; + + case 'v': /* vspace */ + /* + * Set vertical spacing. + */ + Vspace = (narg == 0) ? 1 : narg; + return; + + case 'y': /* yesspace */ + /* + * Set space mode. + */ + Nospmode = 0; + return; + } /* end of switch(*word) */ + /* + * Locate header and footer defintions. + */ + if (regexec(Pat[14].pat, word)) { + if (strcmp((char *)word, "LH") == 0) { + /* + * Left header + */ + Free(&Hdl); + if (sarg != NULL) + Hdl = Newstr(sarg); + return; + } + if (strcmp((char *)word, "CH") == 0) { + /* + * Center header + */ + Free(&Hdc); + if (sarg != NULL) + Hdc = Newstr(sarg); + return; + } + if (strcmp((char *)word, "RH") == 0) { + /* + * Right header + */ + Free(&Hdr); + if (sarg != NULL) + Hdr = Newstr(sarg); + return; + } + if (strcmp((char *)word, "LF") == 0) { + /* + * Left footer + */ + Free(&Ftl); + if (sarg != NULL) + Ftl = Newstr(sarg); + return; + } + if (strcmp((char *)word, "CF") == 0) { + /* + * Center footer + */ + Free(&Ftc); + if (sarg != NULL) + Ftc = Newstr(sarg); + return; + } + if (strcmp((char *)word, "RF") == 0) { + /* + * Right footer + */ + Free(&Ftr); + if (sarg != NULL) + Ftr = Newstr(sarg); + return; + } + } + /* + * Error on unknown arguments + */ + Error3(len, (char *)word, (char *)sarg, narg, "unknown request"); +} diff --git a/commands/cawf/proto.h b/commands/cawf/proto.h new file mode 100755 index 000000000..29d2b4ade --- /dev/null +++ b/commands/cawf/proto.h @@ -0,0 +1,117 @@ +/* + * proto.h - function prototype definitions for cawf(1) + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include "ansi.h" + +#ifdef UNIX +# ifdef USG +#include +# else /* not USG */ +#include +# endif /* USG */ +#else /* not UNIX */ +#include +#endif /* UNIX */ + +/* + * The following conditional rat's nest intends to: + * + * for MS-DOS #include and . exists in + * MS-DOS so the STDLIB symbol should be defined and UNIX + * shouldn't be. + * + * for Unix #include if the STDLIB symbol is defined. If + * STDLIB isn't defined, define a prototype for getenv(). + * If the UNIX symbol is defined (and it should be) and if + * the MALLOCH symbol is defined, #include ; else + * define a prototype for malloc() if UNIX is defined and + * MALLOCH isn't. (The Unix usually defines the + * malloc() prototype.) + * + * for ??? Define a prototype for getenv() and #include + * if neither STDLIB nor UNIX are defined. (What system is + * this?) + */ + +#ifdef STDLIB +#include +# ifndef UNIX +#include +# endif /* UNIX */ +#else /* not STDLIB */ +_PROTOTYPE(char *getenv,(char *name)); +# ifdef UNIX +# ifdef MALLOCH +#include +# else /* not MALLOCH */ +_PROTOTYPE(char *malloc,(unsigned size)); +# endif /* MALLOCH */ +# else /* not UNIX */ +#include +# endif /* UNIX */ +#endif /* STDLIB */ + +_PROTOTYPE(unsigned char *Asmcode,(unsigned char **s, unsigned char *c)); +_PROTOTYPE(int Asmname,(unsigned char *s, unsigned char *c)); +_PROTOTYPE(void Charput,(int c)); +_PROTOTYPE(int Delmacro,(int mx)); +_PROTOTYPE(int Defdev,()); +_PROTOTYPE(void Delstr,(int sx)); +_PROTOTYPE(void Error,(int t, int l, char *s1, char *s2)); +_PROTOTYPE(void Error3,(int len, char *word, char *sarg, int narg, char *msg)); +_PROTOTYPE(void Expand,(unsigned char *line)); +_PROTOTYPE(void Delnum,(int nx)); +_PROTOTYPE(unsigned char *Field,(int n, unsigned char *p, int c)); +_PROTOTYPE(void Endword,()); +_PROTOTYPE(int Findchar,(unsigned char *nm, int l, unsigned char *s, int e)); +_PROTOTYPE(int Findhy,(unsigned char *s, int l, int e)); +_PROTOTYPE(int Findmacro,(unsigned char *p, int e)); +_PROTOTYPE(int Findnum,(unsigned char *n, int v, int e)); +_PROTOTYPE(int Findparms,(unsigned char *n)); +_PROTOTYPE(int Findscale,(int n, double v, int e)); +_PROTOTYPE(unsigned char *Findstr,(unsigned char *nm, unsigned char *s, int e)); +_PROTOTYPE(int getopt,(int argc, char **argv, char *opts)); +_PROTOTYPE(int LenprtHF,(unsigned char *s, int p, int t)); +_PROTOTYPE(int main,(int argc, char *argv[])); +_PROTOTYPE(void Macro,(unsigned char *inp)); +_PROTOTYPE(void Nreq,(unsigned char *line, int brk)); +_PROTOTYPE(void Free,(unsigned char **p)); +_PROTOTYPE(unsigned char *Newstr,(unsigned char *s)); +_PROTOTYPE(void Pass2,(unsigned char *line)); +_PROTOTYPE(void Pass3,(int len, unsigned char *word, unsigned char *sarg, int narg)); +_PROTOTYPE(void regerror,(char *s)); +_PROTOTYPE(unsigned char *reg,(int paren, int *flagp)); +_PROTOTYPE(unsigned char *regatom,(int *flagp)); +_PROTOTYPE(unsigned char *regbranch,(int *flagp)); +_PROTOTYPE(regexp *regcomp,(char *exp)); +_PROTOTYPE(void regdump,(regexp *r)); +_PROTOTYPE(int regexec,(regexp *prog, unsigned char *string)); +_PROTOTYPE(void Stringput,(unsigned char *s)); +_PROTOTYPE(int Str2word,(unsigned char *s, int len)); diff --git a/commands/cawf/regerror.c b/commands/cawf/regerror.c new file mode 100755 index 000000000..01539061f --- /dev/null +++ b/commands/cawf/regerror.c @@ -0,0 +1,18 @@ +#include +#include "regexp.h" +#include "proto.h" + +void +regerror(s) +char *s; +{ +#ifndef DOSPORT +#ifdef ERRAVAIL + error("regexp: %s", s); +#else + fprintf(stderr, "regexp(3): %s", s); + exit(1); +#endif + /* NOTREACHED */ +#endif /* ifdef'd out for less's sake when reporting error inside less */ +} diff --git a/commands/cawf/regexp.c b/commands/cawf/regexp.c new file mode 100755 index 000000000..909f6b9e6 --- /dev/null +++ b/commands/cawf/regexp.c @@ -0,0 +1,1242 @@ +/* + * regcomp and regexec -- regsub and regerror are elsewhere + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * Beware that some of this code is subtly aware of the way operator + * precedence is structured in regular expressions. Serious changes in + * regular-expression syntax might require a total rethink. + */ +#include +#ifdef UNIX +#ifdef USG +#include +#else +#include +#endif +#else +#include +#endif +#include "regexp.h" +#include "regmagic.h" +#include "proto.h" + +/* + * The "internal use only" fields in regexp.h are present to pass info from + * compile to execute that permits the execute phase to run lots faster on + * simple cases. They are: + * + * regstart char that must begin a match; '\0' if none obvious + * reganch is the match anchored (at beginning-of-line only)? + * regmust string (pointer into program) that match must include, or NULL + * regmlen length of regmust string + * + * Regstart and reganch permit very fast decisions on suitable starting points + * for a match, cutting down the work a lot. Regmust permits fast rejection + * of lines that cannot possibly match. The regmust tests are costly enough + * that regcomp() supplies a regmust only if the r.e. contains something + * potentially expensive (at present, the only such thing detected is * or + + * at the start of the r.e., which can involve a lot of backup). Regmlen is + * supplied because the test in regexec() needs it and regcomp() is computing + * it anyway. + */ + +/* + * Structure for regexp "program". This is essentially a linear encoding + * of a nondeterministic finite-state machine (aka syntax charts or + * "railroad normal form" in parsing technology). Each node is an opcode + * plus a "next" pointer, possibly plus an operand. "Next" pointers of + * all nodes except BRANCH implement concatenation; a "next" pointer with + * a BRANCH on both ends of it is connecting two alternatives. (Here we + * have one of the subtle syntax dependencies: an individual BRANCH (as + * opposed to a collection of them) is never concatenated with anything + * because of operator precedence.) The operand of some types of node is + * a literal string; for others, it is a node leading into a sub-FSM. In + * particular, the operand of a BRANCH node is the first node of the branch. + * (NB this is *not* a tree structure: the tail of the branch connects + * to the thing following the set of BRANCHes.) The opcodes are: + */ + +/* definition number opnd? meaning */ +#define END 0 /* no End of program. */ +#define BOL 1 /* no Match "" at beginning of line. */ +#define EOL 2 /* no Match "" at end of line. */ +#define ANY 3 /* no Match any one character. */ +#define ANYOF 4 /* str Match any character in this string. */ +#define ANYBUT 5 /* str Match any character not in this string. */ +#define BRANCH 6 /* node Match this alternative, or the next... */ +#define BACK 7 /* no Match "", "next" ptr points backward. */ +#define EXACTLY 8 /* str Match this string. */ +#define NOTHING 9 /* no Match empty string. */ +#define STAR 10 /* node Match this (simple) thing 0 or more times. */ +#define PLUS 11 /* node Match this (simple) thing 1 or more times. */ +#define OPEN 20 /* no Mark this point in input as start of #n. */ + /* OPEN+1 is number 1, etc. */ +#define CLOSE 30 /* no Analogous to OPEN. */ + +/* + * Opcode notes: + * + * BRANCH The set of branches constituting a single choice are hooked + * together with their "next" pointers, since precedence prevents + * anything being concatenated to any individual branch. The + * "next" pointer of the last BRANCH in a choice points to the + * thing following the whole choice. This is also where the + * final "next" pointer of each individual branch points; each + * branch starts with the operand node of a BRANCH node. + * + * BACK Normal "next" pointers all implicitly point forward; BACK + * exists to make loop structures possible. + * + * STAR,PLUS '?', and complex '*' and '+', are implemented as circular + * BRANCH structures using BACK. Simple cases (one character + * per match) are implemented with STAR and PLUS for speed + * and to minimize recursive plunges. + * + * OPEN,CLOSE ...are numbered at compile time. + */ + +/* + * A node is one char of opcode followed by two chars of "next" pointer. + * "Next" pointers are stored as two 8-bit pieces, high order first. The + * value is a positive offset from the opcode of the node containing it. + * An operand, if any, simply follows the node. (Note that much of the + * code generation knows about this implicit relationship.) + * + * Using two bytes for the "next" pointer is vast overkill for most things, + * but allows patterns to get big without disasters. + */ +#define OP(p) (*(p)) +#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) +#define OPERAND(p) ((p) + 3) + +/* + * See regmagic.h for one further detail of program structure. + */ + + +/* + * Utility definitions. + */ +#ifndef CHARBITS +#define UCHARAT(p) ((int)*(unsigned char *)(p)) +#else +#define UCHARAT(p) ((int)*(p)&CHARBITS) +#endif + +#define FAIL(m) { regerror(m); return(NULL); } +#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') +#define META "^$.[()|?+*\\" + +/* + * Flags to be passed up and down. + */ +#define HASWIDTH 01 /* Known never to match null string. */ +#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ +#define SPSTART 04 /* Starts with * or +. */ +#define WORST 0 /* Worst case. */ +#define STATIC +/* + * Global work variables for regcomp(). + */ + +STATIC char *regparse; /* Input-scan pointer. */ +STATIC int regnpar; /* () count. */ +STATIC unsigned char regdummy; +STATIC unsigned char *regcode; /* Code-emit pointer; ®dummy = don't. */ +STATIC long regsize; /* Code size. */ + +/* + * Forward declarations for regcomp()'s friends. + */ +_PROTOTYPE(STATIC unsigned char *reg, (int paren, int *flagp )); +_PROTOTYPE(STATIC unsigned char *regbranch, (int *flagp )); +_PROTOTYPE(STATIC unsigned char *regpiece, (int *flagp )); +_PROTOTYPE(STATIC unsigned char *regatom, (int *flagp )); +_PROTOTYPE(STATIC unsigned char *regnode, (int op )); +_PROTOTYPE(STATIC void regc, (int b )); +_PROTOTYPE(STATIC void reginsert, (int op, unsigned char *opnd )); +_PROTOTYPE(STATIC void regtail, (unsigned char *p, unsigned char *val )); +_PROTOTYPE(STATIC void regoptail, (unsigned char *p, unsigned char *val )); +_PROTOTYPE(STATIC int regtry, (regexp *prog, unsigned char *string )); +_PROTOTYPE(STATIC int regmatch, (unsigned char *prog )); +_PROTOTYPE(STATIC int regrepeat, (unsigned char *p )); +_PROTOTYPE(STATIC unsigned char *regnext, (unsigned char *p )); +_PROTOTYPE(STATIC unsigned char *regprop, (unsigned char *op )); + +#ifdef STRCSPN +_PROTOTYPE(STATIC int strcspn, (unsigned char *s1, unsigned char *s2 )); +#endif + +/* + - regcomp - compile a regular expression into internal code + * + * We can't allocate space until we know how big the compiled form will be, + * but we can't compile it (and thus know how big it is) until we've got a + * place to put the code. So we cheat: we compile it twice, once with code + * generation turned off and size counting turned on, and once "for real". + * This also means that we don't allocate space until we are sure that the + * thing really will compile successfully, and we never have to move the + * code and thus invalidate pointers into it. (Note that it has to be in + * one piece because free() must be able to free it all.) + * + * Beware that the optimization-preparation code in here knows about some + * of the structure of the compiled regexp. + */ +regexp * +regcomp(exp) +char *exp; +{ + register regexp *r; + register unsigned char *scan; + register unsigned char *longest; + register int len; + int flags; + + if (exp == NULL) + FAIL("NULL argument"); + + /* First pass: determine size, legality. */ + regparse = exp; + regnpar = 1; + regsize = 0L; + regcode = ®dummy; + regc(MAGIC); + if (reg(0, &flags) == NULL) + return(NULL); + + /* Small enough for pointer-storage convention? */ + if (regsize >= 32767L) /* Probably could be 65535L. */ + FAIL("regexp too big"); + + /* Allocate space. */ + r = (regexp *)malloc(sizeof(regexp) + (unsigned)regsize); + if (r == NULL) + FAIL("out of space"); + + /* Second pass: emit code. */ + regparse = exp; + regnpar = 1; + regcode = r->program; + regc(MAGIC); + if (reg(0, &flags) == NULL) + return(NULL); + + /* Dig out information for optimizations. */ + r->regstart = '\0'; /* Worst-case defaults. */ + r->reganch = 0; + r->regmust = NULL; + r->regmlen = 0; + scan = r->program+1; /* First BRANCH. */ + if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ + scan = OPERAND(scan); + + /* Starting-point info. */ + if (OP(scan) == EXACTLY) + r->regstart = *OPERAND(scan); + else if (OP(scan) == BOL) + r->reganch++; + + /* + * If there's something expensive in the r.e., find the + * longest literal string that must appear and make it the + * regmust. Resolve ties in favor of later strings, since + * the regstart check works with the beginning of the r.e. + * and avoiding duplication strengthens checking. Not a + * strong reason, but sufficient in the absence of others. + */ + if (flags&SPSTART) { + longest = NULL; + len = 0; + for (; scan != NULL; scan = regnext(scan)) + if (OP(scan) == EXACTLY + && strlen((char *)OPERAND(scan)) >= len) { + longest = OPERAND(scan); + len = strlen((char *)OPERAND(scan)); + } + r->regmust = longest; + r->regmlen = len; + } + } + + return(r); +} + +/* + - reg - regular expression, i.e. main body or parenthesized thing + * + * Caller must absorb opening parenthesis. + * + * Combining parenthesis handling with the base level of regular expression + * is a trifle forced, but the need to tie the tails of the branches to what + * follows makes it hard to avoid. + */ +STATIC unsigned char * +reg(paren, flagp) +int paren; /* Parenthesized? */ +int *flagp; +{ + register unsigned char *ret; + register unsigned char *br; + register unsigned char *ender; + register int parno; + int flags; + + *flagp = HASWIDTH; /* Tentatively. */ + + /* Make an OPEN node, if parenthesized. */ + if (paren) { + if (regnpar >= NSUBEXP) + FAIL("too many ()"); + parno = regnpar; + regnpar++; + ret = regnode(OPEN+parno); + } else + ret = NULL; + + /* Pick up the branches, linking them together. */ + br = regbranch(&flags); + if (br == NULL) + return(NULL); + if (ret != NULL) + regtail(ret, br); /* OPEN -> first. */ + else + ret = br; + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + while (*regparse == '|') { + regparse++; + br = regbranch(&flags); + if (br == NULL) + return(NULL); + regtail(ret, br); /* BRANCH -> BRANCH. */ + if (!(flags&HASWIDTH)) + *flagp &= ~HASWIDTH; + *flagp |= flags&SPSTART; + } + + /* Make a closing node, and hook it on the end. */ + ender = regnode((paren) ? CLOSE+parno : END); + regtail(ret, ender); + + /* Hook the tails of the branches to the closing node. */ + for (br = ret; br != NULL; br = regnext(br)) + regoptail(br, ender); + + /* Check for proper termination. */ + if (paren && *regparse++ != ')') { + FAIL("unmatched ()"); + } else if (!paren && *regparse != '\0') { + if (*regparse == ')') { + FAIL("unmatched ()"); + } else + FAIL("junk on end"); /* "Can't happen". */ + /* NOTREACHED */ + } + + return(ret); +} + +/* + - regbranch - one alternative of an | operator + * + * Implements the concatenation operator. + */ +STATIC unsigned char * +regbranch(flagp) +int *flagp; +{ + register unsigned char *ret; + register unsigned char *chain; + register unsigned char *latest; + int flags; + + *flagp = WORST; /* Tentatively. */ + + ret = regnode(BRANCH); + chain = NULL; + while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { + latest = regpiece(&flags); + if (latest == NULL) + return(NULL); + *flagp |= flags&HASWIDTH; + if (chain == NULL) /* First piece. */ + *flagp |= flags&SPSTART; + else + regtail(chain, latest); + chain = latest; + } + if (chain == NULL) /* Loop ran zero times. */ + (void) regnode(NOTHING); + + return(ret); +} + +/* + - regpiece - something followed by possible [*+?] + * + * Note that the branching code sequences used for ? and the general cases + * of * and + are somewhat optimized: they use the same NOTHING node as + * both the endmarker for their branch list and the body of the last branch. + * It might seem that this node could be dispensed with entirely, but the + * endmarker role is not redundant. + */ +STATIC unsigned char * +regpiece(flagp) +int *flagp; +{ + register unsigned char *ret; + register unsigned char op; + register unsigned char *next; + int flags; + + ret = regatom(&flags); + if (ret == NULL) + return(NULL); + + op = *regparse; + if (!ISMULT(op)) { + *flagp = flags; + return(ret); + } + + if (!(flags&HASWIDTH) && op != '?') + FAIL("*+ operand could be empty"); + *flagp = (op != '+') ? (WORST|SPSTART) : (WORST|HASWIDTH); + + if (op == '*' && (flags&SIMPLE)) + reginsert(STAR, ret); + else if (op == '*') { + /* Emit x* as (x&|), where & means "self". */ + reginsert(BRANCH, ret); /* Either x */ + regoptail(ret, regnode(BACK)); /* and loop */ + regoptail(ret, ret); /* back */ + regtail(ret, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '+' && (flags&SIMPLE)) + reginsert(PLUS, ret); + else if (op == '+') { + /* Emit x+ as x(&|), where & means "self". */ + next = regnode(BRANCH); /* Either */ + regtail(ret, next); + regtail(regnode(BACK), ret); /* loop back */ + regtail(next, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '?') { + /* Emit x? as (x|) */ + reginsert(BRANCH, ret); /* Either x */ + regtail(ret, regnode(BRANCH)); /* or */ + next = regnode(NOTHING); /* null. */ + regtail(ret, next); + regoptail(ret, next); + } + regparse++; + if (ISMULT(*regparse)) + FAIL("nested *?+"); + + return(ret); +} + +/* + - regatom - the lowest level + * + * Optimization: gobbles an entire sequence of ordinary characters so that + * it can turn them into a single node, which is smaller to store and + * faster to run. Backslashed characters are exceptions, each becoming a + * separate node; the code is simpler that way and it's not worth fixing. + */ +STATIC unsigned char * +regatom(flagp) +int *flagp; +{ + register unsigned char *ret; + int flags; + + *flagp = WORST; /* Tentatively. */ + + switch (*regparse++) { + case '^': + ret = regnode(BOL); + break; + case '$': + ret = regnode(EOL); + break; + case '.': + ret = regnode(ANY); + *flagp |= HASWIDTH|SIMPLE; + break; + case '[': { + register int class; + register int classend; + + if (*regparse == '^') { /* Complement of range. */ + ret = regnode(ANYBUT); + regparse++; + } else + ret = regnode(ANYOF); + if (*regparse == ']' || *regparse == '-') + regc(*regparse++); + while (*regparse != '\0' && *regparse != ']') { + if (*regparse == '-') { + regparse++; + if (*regparse == ']' || *regparse == '\0') + regc('-'); + else { + class = UCHARAT(regparse-2)+1; + classend = UCHARAT(regparse); + if (class > classend+1) + FAIL("invalid [] range"); + for (; class <= classend; class++) + regc(class); + regparse++; + } + } else + regc(*regparse++); + } + regc('\0'); + if (*regparse != ']') + FAIL("unmatched []"); + regparse++; + *flagp |= HASWIDTH|SIMPLE; + } + break; + case '(': + ret = reg(1, &flags); + if (ret == NULL) + return(NULL); + *flagp |= flags&(HASWIDTH|SPSTART); + break; + case '\0': + case '|': + case ')': + FAIL("internal urp"); /* Supposed to be caught earlier. */ + break; + case '?': + case '+': + case '*': + FAIL("?+* follows nothing"); + break; + case '\\': + if (*regparse == '\0') + FAIL("trailing \\"); + ret = regnode(EXACTLY); + regc(*regparse++); + regc('\0'); + *flagp |= HASWIDTH|SIMPLE; + break; + default: { + register int len; + register unsigned char ender; + + regparse--; +#ifdef STRCSPN + len = strcspn(regparse, (unsigned char *)META); +#else + len = strcspn((char *)regparse, META); +#endif + if (len <= 0) + FAIL("internal disaster"); + ender = *(regparse+len); + if (len > 1 && ISMULT(ender)) + len--; /* Back off clear of ?+* operand. */ + *flagp |= HASWIDTH; + if (len == 1) + *flagp |= SIMPLE; + ret = regnode(EXACTLY); + while (len > 0) { + regc(*regparse++); + len--; + } + regc('\0'); + } + break; + } + + return(ret); +} + +/* + - regnode - emit a node + */ +STATIC unsigned char * /* Location. */ +regnode(iop) +int iop; +{ + register unsigned char *ret; + register unsigned char *ptr; + unsigned char op; + + op = (unsigned char) iop; + ret = regcode; + if (ret == ®dummy) { + regsize += 3; + return(ret); + } + + ptr = ret; + *ptr++ = op; + *ptr++ = '\0'; /* Null "next" pointer. */ + *ptr++ = '\0'; + regcode = ptr; + + return(ret); +} + +/* + - regc - emit (if appropriate) a byte of code + */ +STATIC void +regc(ib) +int ib; +{ + unsigned char b; + + b = (unsigned char) ib; + if (regcode != ®dummy) + *regcode++ = b; + else + regsize++; +} + +/* + - reginsert - insert an operator in front of already-emitted operand + * + * Means relocating the operand. + */ +STATIC void +reginsert(iop, opnd) +int iop; +unsigned char *opnd; +{ + register unsigned char *src; + register unsigned char *dst; + register unsigned char *place; + unsigned char op; + + op = (unsigned char) iop; + if (regcode == ®dummy) { + regsize += 3; + return; + } + + src = regcode; + regcode += 3; + dst = regcode; + while (src > opnd) + *--dst = *--src; + + place = opnd; /* Op node, where operand used to be. */ + *place++ = op; + *place++ = '\0'; + *place++ = '\0'; +} + +/* + - regtail - set the next-pointer at the end of a node chain + */ +STATIC void +regtail(p, val) +unsigned char *p; +unsigned char *val; +{ + register unsigned char *scan; + register unsigned char *temp; + register int offset; + + if (p == ®dummy) + return; + + /* Find last node. */ + scan = p; + for (;;) { + temp = regnext(scan); + if (temp == NULL) + break; + scan = temp; + } + + if (OP(scan) == BACK) + offset = scan - val; + else + offset = val - scan; + *(scan+1) = (offset>>8)&0377; + *(scan+2) = offset&0377; +} + +/* + - regoptail - regtail on operand of first argument; nop if operandless + */ +STATIC void +regoptail(p, val) +unsigned char *p; +unsigned char *val; +{ + /* "Operandless" and "op != BRANCH" are synonymous in practice. */ + if (p == NULL || p == ®dummy || OP(p) != BRANCH) + return; + regtail(OPERAND(p), val); +} + +/* + * regexec and friends + */ + +/* + * Global work variables for regexec(). + */ +STATIC unsigned char *reginput; /* String-input pointer. */ +STATIC unsigned char *regbol; /* Beginning of input, for ^ check. */ +STATIC unsigned char **regstartp; /* Pointer to startp array. */ +STATIC unsigned char **regendp; /* Ditto for endp. */ + +#ifdef DEBUG +int regnarrate = 0; +void regdump(); +STATIC unsigned char *regprop(); +#endif + +/* + - regexec - match a regexp against a string + */ +int +regexec(prog, string) +register regexp *prog; +register unsigned char *string; +{ + register unsigned char *s; +#ifndef STDLIB + extern char *strchr(); +#endif + + /* Be paranoid... */ + if (prog == NULL || string == NULL) { + regerror("NULL parameter"); + return(0); + } + + /* Check validity of program. */ + if (UCHARAT(prog->program) != MAGIC) { + regerror("corrupted program"); + return(0); + } + + /* If there is a "must appear" string, look for it. */ + if (prog->regmust != NULL) { + s = string; + while ((s = (unsigned char *)strchr((char *)s,prog->regmust[0])) + != NULL) { + if (strncmp((char *)s, (char *)prog->regmust, + prog->regmlen) + == 0) + break; /* Found it. */ + s++; + } + if (s == NULL) /* Not present. */ + return(0); + } + + /* Mark beginning of line for ^ . */ + regbol = string; + + /* Simplest case: anchored match need be tried only once. */ + if (prog->reganch) + return(regtry(prog, string)); + + /* Messy cases: unanchored match. */ + s = string; + if (prog->regstart != '\0') + /* We know what char it must start with. */ + while ((s = (unsigned char *)strchr((char *)s, prog->regstart)) + != NULL) { + if (regtry(prog, s)) + return(1); + s++; + } + else + /* We don't -- general case. */ + do { + if (regtry(prog, s)) + return(1); + } while (*s++ != '\0'); + + /* Failure. */ + return(0); +} + +/* + - regtry - try match at specific point + */ +STATIC int /* 0 failure, 1 success */ +regtry(prog, string) +regexp *prog; +unsigned char *string; +{ + register int i; + register unsigned char **sp; + register unsigned char **ep; + + reginput = string; + regstartp = prog->startp; + regendp = prog->endp; + + sp = prog->startp; + ep = prog->endp; + for (i = NSUBEXP; i > 0; i--) { + *sp++ = NULL; + *ep++ = NULL; + } + if (regmatch(prog->program + 1)) { + prog->startp[0] = string; + prog->endp[0] = reginput; + return(1); + } else + return(0); +} + +/* + - regmatch - main matching routine + * + * Conceptually the strategy is simple: check to see whether the current + * node matches, call self recursively to see whether the rest matches, + * and then act accordingly. In practice we make some effort to avoid + * recursion, in particular by going through "ordinary" nodes (that don't + * need to know whether the rest of the match failed) by a loop instead of + * by recursion. + */ +STATIC int /* 0 failure, 1 success */ +regmatch(prog) +unsigned char *prog; +{ + register unsigned char *scan; /* Current node. */ + unsigned char *next; /* Next node. */ +#ifndef STDLIB + extern char *strchr(); +#endif + + scan = prog; +#ifdef DEBUG + if (scan != NULL && regnarrate) + fprintf(stderr, "%s(\n", regprop(scan)); +#endif + while (scan != NULL) { +#ifdef DEBUG + if (regnarrate) + fprintf(stderr, "%s...\n", regprop(scan)); +#endif + next = regnext(scan); + + switch (OP(scan)) { + case BOL: + if (reginput != regbol) + return(0); + break; + case EOL: + if (*reginput != '\0') + return(0); + break; + case ANY: + if (*reginput == '\0') + return(0); + reginput++; + break; + case EXACTLY: { + register int len; + register unsigned char *opnd; + + opnd = OPERAND(scan); + /* Inline the first character, for speed. */ + if (*opnd != *reginput) + return(0); + len = strlen((char *)opnd); + if (len > 1 && strncmp((char *)opnd, + (char *)reginput, len) + != 0) + return(0); + reginput += len; + } + break; + case ANYOF: + if (*reginput == '\0' + || strchr((char *)OPERAND(scan), *reginput) == NULL) + return(0); + reginput++; + break; + case ANYBUT: + if (*reginput == '\0' + || strchr((char *)OPERAND(scan), *reginput) != NULL) + return(0); + reginput++; + break; + case NOTHING: + break; + case BACK: + break; + case OPEN+1: + case OPEN+2: + case OPEN+3: + case OPEN+4: + case OPEN+5: + case OPEN+6: + case OPEN+7: + case OPEN+8: + case OPEN+9: { + register int no; + register unsigned char *save; + + no = OP(scan) - OPEN; + save = reginput; + + if (regmatch(next)) { + /* + * Don't set startp if some later + * invocation of the same parentheses + * already has. + */ + if (regstartp[no] == NULL) + regstartp[no] = save; + return(1); + } else + return(0); + } + break; + case CLOSE+1: + case CLOSE+2: + case CLOSE+3: + case CLOSE+4: + case CLOSE+5: + case CLOSE+6: + case CLOSE+7: + case CLOSE+8: + case CLOSE+9: { + register int no; + register unsigned char *save; + + no = OP(scan) - CLOSE; + save = reginput; + + if (regmatch(next)) { + /* + * Don't set endp if some later + * invocation of the same parentheses + * already has. + */ + if (regendp[no] == NULL) + regendp[no] = save; + return(1); + } else + return(0); + } + break; + case BRANCH: { + register unsigned char *save; + + if (OP(next) != BRANCH) /* No choice. */ + next = OPERAND(scan); /* Avoid recursion. */ + else { + do { + save = reginput; + if (regmatch(OPERAND(scan))) + return(1); + reginput = save; + scan = regnext(scan); + } while (scan != NULL && OP(scan) == BRANCH); + return(0); + /* NOTREACHED */ + } + } + break; + case STAR: + case PLUS: { + register unsigned char nextch; + register int no; + register unsigned char *save; + register int min; + + /* + * Lookahead to avoid useless match attempts + * when we know what character comes next. + */ + nextch = '\0'; + if (OP(next) == EXACTLY) + nextch = *OPERAND(next); + min = (OP(scan) == STAR) ? 0 : 1; + save = reginput; + no = regrepeat(OPERAND(scan)); + while (no >= min) { + /* If it could work, try it. */ + if (nextch == '\0' || *reginput == nextch) + if (regmatch(next)) + return(1); + /* Couldn't or didn't -- back up. */ + no--; + reginput = save + no; + } + return(0); + } + break; + case END: + return(1); /* Success! */ + break; + default: + regerror("memory corruption"); + return(0); + break; + } + + scan = next; + } + + /* + * We get here only if there's trouble -- normally "case END" is + * the terminating point. + */ + regerror("corrupted pointers"); + return(0); +} + +/* + - regrepeat - repeatedly match something simple, report how many + */ +STATIC int +regrepeat(p) +unsigned char *p; +{ + register int count = 0; + register unsigned char *scan; + register unsigned char *opnd; + + scan = reginput; + opnd = OPERAND(p); + switch (OP(p)) { + case ANY: + count = strlen((char *)scan); + scan += count; + break; + case EXACTLY: + while (*opnd == *scan) { + count++; + scan++; + } + break; + case ANYOF: + while (*scan != '\0' && strchr((char *)opnd, *scan) != NULL) { + count++; + scan++; + } + break; + case ANYBUT: + while (*scan != '\0' && strchr((char *)opnd, *scan) == NULL) { + count++; + scan++; + } + break; + default: /* Oh dear. Called inappropriately. */ + regerror("internal foulup"); + count = 0; /* Best compromise. */ + break; + } + reginput = scan; + + return(count); +} + +/* + - regnext - dig the "next" pointer out of a node + */ +STATIC unsigned char * +regnext(p) +register unsigned char *p; +{ + register int offset; + + if (p == ®dummy) + return(NULL); + + offset = NEXT(p); + if (offset == 0) + return(NULL); + + if (OP(p) == BACK) + return(p-offset); + else + return(p+offset); +} + +#ifdef DEBUG + +STATIC unsigned char *regprop(); + +/* + - regdump - dump a regexp onto stdout in vaguely comprehensible form + */ +void +regdump(r) +regexp *r; +{ + register unsigned char *s; + register unsigned char op = EXACTLY; /* Arbitrary non-END op. */ + register unsigned char *next; + extern char *strchr(); + + + s = r->program + 1; + while (op != END) { /* While that wasn't END last time... */ + op = OP(s); + printf("%2d%s", s-r->program, regprop(s)); /* Where, what. */ + next = regnext(s); + if (next == NULL) /* Next ptr. */ + printf("(0)"); + else + printf("(%d)", (s-r->program)+(next-s)); + s += 3; + if (op == ANYOF || op == ANYBUT || op == EXACTLY) { + /* Literal string, where present. */ + while (*s != '\0') { + putchar(*s); + s++; + } + s++; + } + putchar('\n'); + } + + /* Header fields of interest. */ + if (r->regstart != '\0') + printf("start `%c' ", r->regstart); + if (r->reganch) + printf("anchored "); + if (r->regmust != NULL) + printf("must have \"%s\"", r->regmust); + printf("\n"); +} + +/* + - regprop - printable representation of opcode + */ +STATIC unsigned char * +regprop(op) +unsigned char *op; +{ + register unsigned char *p; + STATIC unsigned char buf[50]; + + (void) strcpy(buf, ":"); + + switch (OP(op)) { + case BOL: + p = "BOL"; + break; + case EOL: + p = "EOL"; + break; + case ANY: + p = "ANY"; + break; + case ANYOF: + p = "ANYOF"; + break; + case ANYBUT: + p = "ANYBUT"; + break; + case BRANCH: + p = "BRANCH"; + break; + case EXACTLY: + p = "EXACTLY"; + break; + case NOTHING: + p = "NOTHING"; + break; + case BACK: + p = "BACK"; + break; + case END: + p = "END"; + break; + case OPEN+1: + case OPEN+2: + case OPEN+3: + case OPEN+4: + case OPEN+5: + case OPEN+6: + case OPEN+7: + case OPEN+8: + case OPEN+9: + sprintf(buf+strlen(buf), "OPEN%d", OP(op)-OPEN); + p = NULL; + break; + case CLOSE+1: + case CLOSE+2: + case CLOSE+3: + case CLOSE+4: + case CLOSE+5: + case CLOSE+6: + case CLOSE+7: + case CLOSE+8: + case CLOSE+9: + sprintf(buf+strlen(buf), "CLOSE%d", OP(op)-CLOSE); + p = NULL; + break; + case STAR: + p = "STAR"; + break; + case PLUS: + p = "PLUS"; + break; + default: + regerror("corrupted opcode"); + break; + } + if (p != NULL) + (void) strcat(buf, p); + return(buf); +} +#endif + +/* + * The following is provided for those people who do not have strcspn() in + * their C libraries. They should get off their butts and do something + * about it; at least one public-domain implementation of those (highly + * useful) string routines has been published on Usenet. + */ +#ifdef STRCSPN +/* + * strcspn - find length of initial segment of s1 consisting entirely + * of characters not from s2 + */ + +STATIC int +strcspn(s1, s2) +unsigned char *s1; +unsigned char *s2; +{ + register unsigned char *scan1; + register unsigned char *scan2; + register int count; + + count = 0; + for (scan1 = s1; *scan1 != '\0'; scan1++) { + for (scan2 = s2; *scan2 != '\0';) /* ++ moved down. */ + if (*scan1 == *scan2++) + return(count); + count++; + } + return(count); +} +#endif diff --git a/commands/cawf/regexp.h b/commands/cawf/regexp.h new file mode 100755 index 000000000..39dc45e6c --- /dev/null +++ b/commands/cawf/regexp.h @@ -0,0 +1,17 @@ +/* + * Definitions etc. for regexp(3) routines. + * + * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], + * not the System V one. + */ +#define NSUBEXP 10 +typedef struct regexp { + unsigned char *startp[NSUBEXP]; + unsigned char *endp[NSUBEXP]; + unsigned char regstart; /* Internal use only. */ + unsigned char reganch; /* Internal use only. */ + unsigned char *regmust; /* Internal use only. */ + unsigned char regmlen; /* Internal use only. */ + unsigned char program[1]; /* Unwarranted chumminess with + * compiler. */ +} regexp; diff --git a/commands/cawf/regmagic.h b/commands/cawf/regmagic.h new file mode 100755 index 000000000..5acf4478f --- /dev/null +++ b/commands/cawf/regmagic.h @@ -0,0 +1,5 @@ +/* + * The first byte of the regexp internal "program" is actually this magic + * number; the start node begins in the second byte. + */ +#define MAGIC 0234 diff --git a/commands/cawf/store.c b/commands/cawf/store.c new file mode 100755 index 000000000..d03f33c96 --- /dev/null +++ b/commands/cawf/store.c @@ -0,0 +1,184 @@ +/* + * store.c - cawf(1) storage areas + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include "cawf.h" + +struct rx Pat[] = { + { "^[.'](i[ef]|el)", NULL}, /* 0 */ + { "^[.']i[ef] !", NULL}, /* 1 */ + { "^[.']i[ef] !?\\\\n\\(\\.\\$(>|>=|=|<|<=)[0-9] ", + NULL}, /* 2 */ + { "^[.']i[ef] !?'\\\\\\$[0-9]'[^']*' ", NULL}, /* 3 */ + { "^[.']i[ef] !?[nt] ", NULL}, /* 4 */ + { "\\\\\\$[0-9]", NULL}, /* 5 */ + { "^[ \t]*$", NULL}, /* 6 */ + { "\\\\|\t|-| ", NULL}, /* 7 */ + { "[.!?:][]\\)'\\\"\\*]*$", NULL}, /* 8 */ + { ",fP", NULL}, /* 9 */ + { ",tP", NULL}, /* 10 */ + { "^(ta|ll|ls|in|ti|po|ne|sp|pl|nr)", NULL}, /* 11 */ + { "^(ll|ls|in|ti|po|pl)", NULL}, /* 12 */ + { "[]\\)'\\\"\\*]", NULL}, /* 13 */ + { "^(LH|CH|RH|LF|CF|RF)", NULL}, /* 14 */ + { "^[.']i[ef]", NULL}, /* 15 */ + { ",fR", NULL}, /* 16 */ + { NULL, NULL} /* END */ +}; + +int Adj = BOTHADJ; /* output line adjustment mode */ +unsigned char *Aftnxt = NULL; /* action after next line */ +unsigned char *Args[] = { NULL, NULL, /* 10 macro arguments */ + NULL, NULL, + NULL, NULL, + NULL, NULL, + NULL, NULL +}; +unsigned char *Argstack[10*MAXSP]; /* stack for Expand()'s "args[]" */ +int Backc = 0; /* last word ended with '\\c' */ +int Botmarg = 5; /* bottom margin */ +int Centering = 0; /* centering count */ +int Condstack[MAXSP]; /* stack for Expand()'s "cond" */ +unsigned char *Cont = NULL; /* continue line append */ +int Contlen = 0; /* continue line append length */ +int Curmx = -1; /* current macro index */ +char *Device = NULL; /* output device name */ +char *Devconf = NULL; /* device configuration file path */ +char *Devfont = NULL; /* output device font */ +int Divert = 0; /* diversion status */ +FILE *Efs = NULL; /* error file stream */ +unsigned char *Eol = NULL; /* end of line information */ +int Eollen = 0; /* end of line length */ +int Err = 0; /* error flag */ +unsigned char *F = NULL; /* field value */ +struct fcode Fcode[] = { /* font codes */ + { 'B', '\0'}, /* Bold */ + { 'I', '\0'}, /* Italic */ + { 'R', '\0'}, /* Roman */ + { 'C', '\0'}, /* Courier */ + { '\0', '\0'} +}; +int Fill = 0; /* fill status */ +unsigned char Font[] = { '\0', '\0' }; /* current font */ +int Fontctl; /* output font control */ +char Fontstat = 'R'; /* output font status */ +int Fph = 0; /* first page header status */ +int Fsp = 0; /* files stack pointer (for .so) */ +struct fontstr Fstr; /* font control strings */ +unsigned char *Ftc = NULL; /* center footer */ +unsigned char *Ftl = NULL; /* left footer */ +unsigned char *Ftr = NULL; /* right footer */ +unsigned char *Hdc = NULL; /* center header */ +int Hdft = 0; /* header/footer status */ +unsigned char *Hdl = NULL; /* left header */ +unsigned char *Hdr = NULL; /* right header */ +struct hytab Hychar[MAXHYCH]; /* hyphen characters */ +FILE *Ifs = NULL; /* input file stream */ +FILE *Ifs_stk[MAXFSTK]; /* Ifs stack */ +int Ind = 0; /* indentation amount */ +unsigned char *Inname = NULL; /* input file name */ +unsigned char *Inn_stk[MAXFSTK]; /* Inname stack */ +int LL = 78; /* line length (default) */ +unsigned char Line[MAXLINE]; /* input line */ +int Lockil = 0; /* pass 2 line number is locked + * (processing is inside macro) */ +int Marg = 0; /* macro argument - man, ms, etc. */ +struct macro Macrotab[MAXMACRO]; /* macro table */ +unsigned char *Macrotxt[MAXMTXT]; /* macro text */ +int Mtx = 0; /* macro text index */ +int Mxstack[MAXSP]; /* stack for Expand()'s "mx" */ +int Nfc; /* number of font codes */ +int Nhnr[MAXNHNR]; /* ".NH" numbers */ +int Nhy = 0; /* number of Hychar[] entries */ +int Nleftstack[MAXSP]; /* stack for Expand()'s "nleft" */ +int Nmac = 0; /* number of macros */ +int Nnr = 0; /* number of Numb[] entries */ +int Nospmode = 1; /* no space mode */ +int Nparms = 0; /* number of Parms[] entries */ +int NR = 0; /* number of record ala awk */ +int NR_stk[MAXFSTK]; /* NR stack */ +int Nsch = 0; /* number of Schar[] entries */ +int Nstr = 0; /* number of entries in Str[] */ +int Ntabs = 0; /* number of TAB positions */ +struct nbr Numb[MAXNR]; /* number registers */ +int Nxtln = 1; /* next line number */ +int Outll = -1; /* output line length */ +unsigned char Outln[MAXOLL*2]; /* output line */ +int Outlx = 0; /* output line index */ +int P2il = 0; /* pass 2 input line number */ +unsigned char *P2name = NULL; /* pass 2 input file name */ +int P3fill = 1; /* pass 3 fill status */ +int Padchar[MAXOLL]; /* padding character locations */ +int Padfrom = PADLEFT; /* which end to pad from */ +int Padx = 0; /* Padchar[] index */ +struct parms Parms[] = { /* parameter registers */ + { {'i', 'n'}, "indent", 0, 0 }, + { {'l', 'l'}, "linelen", 0, 0 }, + { {'l', 's'}, "vspace", 0, 0 }, + { {'t', 'i'}, "tempindent", 0, 0 }, + { {'p', 'o'}, "pageoffset", 0, 0 }, + { {'p', 'l'}, "pagelen", 0, 0 }, + { {'\0', '\0'}, NULL, 0, 0 } +}; +unsigned char Pass1ln[MAXLINE]; /* pass 1 output line */ +unsigned char Pass2ln[MAXLINE]; /* pass 2 output line */ +int Pglen = 66; /* page length */ +int Pgoff = 0; /* page offset */ +char *Pname = NULL; /* program name */ +unsigned char Prevfont = '\0'; /* previous font */ +int Ptrstack[MAXSP]; /* stack for Expand()'s "ptr" */ +struct scale Scale[] = { /* scaling factors */ + { 'i', (240.0) }, + { 'c', ((240.0 * 50.0)/127.0) }, + { 'P', (240.0/6.0) }, + { 'p', (240.0/72.0) }, + { 'u', (1.0) }, + { 'm', (1.0) }, + { 'n', (1.0) }, + { 'v', (1.0) }, + { '\0', (0.0) } +}; +double Scalen = 0.0; /* 'n' scaling factor */ +double Scaleu = 0.0; /* 'u' scaling factor */ +double Scalev = 0.0; /* 'v' scaling factor */ +struct schtab Schar[MAXSCH]; /* special characters */ +int Sp = -1; /* stack pointer */ +struct str Str[MAXSTR]; /* ".ds" strings */ +int Sx = -1; /* string index */ +int Tabs[MAXEXP+1]; /* TAB positions */ +int Thispg = 1; /* this page number */ +int Tind = 0; /* temporary indentation amount */ +int Topmarg = 5; /* top margin */ +unsigned char *Trtbl = NULL; /* .tr table */ +int Uhyph = 0; /* hyphen usage state */ +int Vspace = 1; /* vertical (inter-text-line) spacing */ +unsigned char Word[MAXLINE]; /* pass 2 word buffer */ +int Wordl = 0; /* effective length of Word[] */ +int Wordx = 0; /* Word[] index */ +int Dowarn = 1; /* Enable warnings if true */ diff --git a/commands/cawf/string.c b/commands/cawf/string.c new file mode 100755 index 000000000..b0ef70c70 --- /dev/null +++ b/commands/cawf/string.c @@ -0,0 +1,351 @@ +/* + * string.c - string support functions for cawf(1) + */ + +/* + * Copyright (c) 1991 Purdue University Research Foundation, + * West Lafayette, Indiana 47907. All rights reserved. + * + * Written by Victor A. Abell , Purdue + * University Computing Center. Not derived from licensed software; + * derived from awf(1) by Henry Spencer of the University of Toronto. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to alter it and redistribute + * it freely, subject to the following restrictions: + * + * 1. The author is not responsible for any consequences of use of + * this software, even if they arise from flaws in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. Credits must appear in the + * documentation. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. Credits must + * appear in the documentation. + * + * 4. This notice may not be removed or altered. + */ + +#include "cawf.h" +#include + +_PROTOTYPE(static void Setroman,()); + + +/* + * Asmname(s, c) - assemble name + */ + +Asmname(s, c) + unsigned char *s; /* pointer to name */ + unsigned char *c; /* code destination (c[3]) */ +{ + + c[1] = c[2] = '\0'; + while (*s && *s == ' ') + s++; + if ((c[0] = *s) == '\0') + return(0); + return(((c[1] = s[1]) == '\0') ? 1 : 2); +} + + +/* + * Delstr(sx) - delete string + */ + +void +Delstr(sx) + int sx; /* string index */ +{ + char buf[MAXLINE]; /* message buffer */ + + if (sx >= Nstr) { + (void) sprintf(buf, " bad Delstr(%d) index", sx); + Error(FATAL, LINE, buf, NULL); + } + Free(&Str[sx].str); + while (sx < (Nstr - 1)) { + Str[sx] = Str[sx + 1]; + sx++; + } + Nstr--; +} + + +/* + * Endword() - end a word + */ + +void +Endword() +{ + if (Fontstat != 'R') + Setroman(); + Word[Wordx] = '\0'; +} + + +/* + * Findchar(nm, l, s, e) - find special character definition and + * optionally enter it + */ + +Findchar(nm, l, s, e) + unsigned char *nm; /* character name */ + int l; /* effective length */ + unsigned char *s; /* value string */ + int e; /* 0 = find, don't enter + * 1 = don't find, enter */ +{ + int cmp, hi, low, mid; + unsigned char c[3]; + + c[0] = nm[0]; + c[1] = (nm[1] == ' ' || nm[1] == '\t') ? '\0' : nm[1]; + c[2] = '\0'; + low = mid = 0; + hi = Nsch - 1; + while (low <= hi) { + mid = (low + hi) / 2; + if ((cmp = strncmp((char *)c, (char *)Schar[mid].nm, 2)) < 0) + hi = mid - 1; + else if (cmp > 0) + low = mid + 1; + else { + if ( ! e) + return(mid); + Free(&Schar[mid].str); + goto new_char; + } + } + if ( ! e) + return(-1); + if (Nsch >= MAXSCH) + Error(FATAL, LINE, " at character table limit", NULL); + if (Nsch) { + if (cmp > 0) + mid++; + for (hi = Nsch - 1; hi >= mid; hi--) + Schar[hi+1] = Schar[hi]; + } + Nsch++; + Schar[mid].nm[0] = c[0]; + Schar[mid].nm[1] = c[1]; + +new_char: + + Schar[mid].str = Newstr(s); + Schar[mid].len = l; + return(mid); +} + + +/* + * Findhy(s, l, e) - find and optionally enter hyphen + */ + +Findhy(s, l, e) + unsigned char *s; /* value string */ + int l; /* equivalent length */ + int e; /* 0 = find, don't enter + * 1 = enter, don't find */ +{ + int i; + + for (i = 0; i < Nhy; i++) { + if (Font[0] == Hychar[i].font) + break; + } + if (i >= Nhy) { + if ( ! e) + return(-1); + if (Nhy >= MAXHYCH) + Error(FATAL, LINE, " at hyphen limit for font ", + (char *)Font); + Hychar[i].font = Font[0]; + Nhy++; + } else { + if ( ! e) + return(i); + Error(WARN, LINE, " duplicate hyphen for font ", (char *)Font); + Free(&Hychar[i].str); + } + Hychar[i].str = Newstr(s); + Hychar[i].len = l; + return(i); +} + + +/* + * Findstr(nm, s, e) - find and optionally enter string in Str[] + */ + +unsigned char * +Findstr(nm, s, e) + unsigned char *nm; /* 2 character string name */ + unsigned char *s; /* string value */ + int e; /* 0 = find, don't enter + * 1 = enter, don't find */ +{ + unsigned char c[3]; /* character buffer */ + int cmp, hi, low, mid; /* binary search controls */ + int i; /* temporary indexes */ + unsigned char *s1, *s2; /* temporary string pointers */ + + c[0] = nm[0]; + c[1] = (nm[1] == ' ' || nm[1] == '\t') ? '\0' : nm[1]; + c[2] = '\0'; + low = mid = 0; + hi = Nstr - 1; + Sx = -1; + while (low <= hi) { + mid = (low + hi) / 2; + if ((cmp = strncmp((char *)c, (char *)Str[mid].nm, 2)) < 0) + hi = mid - 1; + else if (cmp > 0) + low = mid + 1; + else { + Sx = mid; + if ( ! e) + return(Str[mid].str); + Free(&Str[mid].str); + goto new_string; + } + } + if ( ! e) + return((unsigned char *)""); + if (Nstr >= MAXSTR) + Error(FATAL, LINE, " out of space for string ", (char *)c); + if (Nstr) { + if (cmp > 0) + mid++; + for (hi = Nstr - 1; hi >= mid; hi--) + Str[hi+1] = Str[hi]; + } + Nstr++; + Sx = mid; + Str[mid].nm[0] = c[0]; + Str[mid].nm[1] = c[1]; + +new_string: + + if (s == NULL) + return (Str[mid].str = Newstr((unsigned char *)"")); + i = (*s == '"') ? 1 : 0; + s1 = Str[mid].str = Newstr(s + i); + if (i) { + s2 = s1 + strlen((char *)s1); + if (s2 > s1 && *(s2-1) == '"') + *(s2-1) = '\0'; + } + return(s1); +} + + +/* + * Setroman() - set Roman font + */ + +static void +Setroman() +{ + int i; + + if ((Wordx + Fstr.rl) >= MAXLINE) + Error(WARN, LINE, " word too long", NULL); + else { + if (Fstr.r) { + for (i = 0; i < Fstr.rl; i++) { + Word[Wordx++] = Fstr.r[i]; + } + } + Fontstat = 'R'; + } +} + + +/* + * Str2word(s, len) - copy len characters from string to Word[] + */ + +Str2word(s, len) + unsigned char *s; + int len; +{ + int i; + + for (; len > 0; len--, s++) { + switch (Font[0]) { + case 'B': + case 'C': + if (Fontctl == 0) { + if ((Wordx + 5) >= MAXLINE) { +word_too_long: + Error(WARN, LINE, " word too long", + NULL); + return(1); + } + Word[Wordx++] = Trtbl[(int)*s]; + Word[Wordx++] = '\b'; + Word[Wordx++] = Trtbl[(int)*s]; + Word[Wordx++] = '\b'; + Word[Wordx++] = Trtbl[(int)*s]; + break; + } + if (Fontstat != Font[0]) { + if (Fontstat != 'R') + Setroman(); + if ((Wordx + Fstr.bl) >= MAXLINE) + goto word_too_long; + if (Fstr.b) { + for (i = 0; i < Fstr.bl; i++) { + Word[Wordx++] = Fstr.b[i]; + } + } + Fontstat = Font[0]; + } + if ((Wordx + 1) >= MAXLINE) + goto word_too_long; + Word[Wordx++] = Trtbl[(int)*s]; + break; + case 'I': + if (isalnum(*s)) { + if (Fontctl == 0) { + if ((Wordx + 3) >= MAXLINE) + goto word_too_long; + Word[Wordx++] = '_'; + Word[Wordx++] = '\b'; + Word[Wordx++] = Trtbl[(int)*s]; + break; + } + if (Fontstat != 'I') { + if (Fontstat != 'R') + Setroman(); + if ((Wordx + Fstr.itl) >= MAXLINE) + goto word_too_long; + if (Fstr.it) { + for (i = 0; i < Fstr.itl; i++) { + Word[Wordx++] = Fstr.it[i]; + } + } + Fontstat = 'I'; + } + if ((Wordx + 1) >= MAXLINE) + goto word_too_long; + Word[Wordx++] = Trtbl[(int)*s]; + break; + } + /* else fall through */ + default: + if (Fontstat != 'R') + Setroman(); + if ((Wordx + 1) >= MAXLINE) + goto word_too_long; + Word[Wordx++] = Trtbl[(int)*s]; + } + } + return(0); +} diff --git a/commands/cron/Makefile b/commands/cron/Makefile new file mode 100755 index 000000000..50d2d81a8 --- /dev/null +++ b/commands/cron/Makefile @@ -0,0 +1,33 @@ +# Makefile for cron. + +CFLAGS= -D_MINIX -D_POSIX_SOURCE +LDFLAGS= + +all: cron crontab + +CRON_OBJ= cron.o tab.o misc.o +CRONTAB_OBJ= crontab.o tab.o misc.o + +cron: $(CRON_OBJ) + $(CC) $(LDFLAGS) -o $@ $(CRON_OBJ) + install -S 8kw $@ + +crontab: $(CRONTAB_OBJ) + $(CC) $(LDFLAGS) -o $@ $(CRONTAB_OBJ) + install -S 4kw $@ + +install: /usr/bin/cron /usr/bin/crontab + +/usr/bin/cron: cron + install -cs $? $@ + +/usr/bin/crontab: crontab + install -cs -o root -m 4755 $? $@ + +clean: + rm -f *.o cron crontab core a.out + +# Dependencies. +cron.o crontab.o: misc.h tab.h +tab.o: misc.h tab.h +misc.o: misc.h diff --git a/commands/cron/cron.c b/commands/cron/cron.c new file mode 100755 index 000000000..a4f6fa145 --- /dev/null +++ b/commands/cron/cron.c @@ -0,0 +1,479 @@ +/* cron 1.4 - clock daemon Author: Kees J. Bot + * 7 Dec 1996 + */ + +#define _MINIX_SOURCE + +#define nil ((void*)0) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "misc.h" +#include "tab.h" + +#if __minix && !__minix_vmd +#define initgroups(name, gid) (0) +#endif + +static volatile int busy; /* Set when something is afoot, don't sleep! */ +static volatile int need_reload;/* Set if table reload required. */ +static volatile int need_quit; /* Set if cron must exit. */ +static volatile int debug; /* Debug level. */ + +static int setenv(char *var, char *val) +/* Set an environment variable. Return 0/-1 for success/failure. */ +{ + char *env; + + env= malloc((strlen(var) + strlen(val) + 2) * sizeof(env[0])); + if (env == nil) return -1; + strcpy(env, var); + strcat(env, "="); + strcat(env, val); + if (putenv(env) < 0) return -1; + free(env); + return 0; +} + +static void run_job(cronjob_t *job) +/* Execute a cron job. Register its pid in the job structure. If a job's + * crontab has an owner then its output is mailed to that owner, otherwise + * no special provisions are made, so the output will go where cron's output + * goes. This keeps root's mailbox from filling up. + */ +{ + pid_t pid; + int need_mailer; + int mailfd[2], errfd[2]; + struct passwd *pw; + crontab_t *tab= job->tab; + + need_mailer= (tab->user != nil); + + if (job->atjob) { + struct stat st; + + need_mailer= 1; + if (rename(tab->file, tab->data) < 0) { + if (errno == ENOENT) { + /* Normal error, job deleted. */ + need_reload= 1; + } else { + /* Bad error, halt processing AT jobs. */ + log(LOG_CRIT, "Can't rename %s: %s\n", + tab->file, strerror(errno)); + tab_reschedule(job); + } + return; + } + /* Will need to determine the next AT job. */ + need_reload= 1; + + if (stat(tab->data, &st) < 0) { + log(LOG_ERR, "Can't stat %s: %s\n", + tab->data, strerror(errno)); + tab_reschedule(job); + return; + } + if ((pw= getpwuid(st.st_uid)) == nil) { + log(LOG_ERR, "Unknown owner for uid %lu of AT job %s\n", + (unsigned long) st.st_uid, job->cmd); + tab_reschedule(job); + return; + } + } else { + pw= nil; + if (job->user != nil && (pw= getpwnam(job->user)) == nil) { + log(LOG_ERR, "%s: Unknown user\n", job->user); + tab_reschedule(job); + return; + } + } + + if (need_mailer) { + errfd[0]= -1; + if (pipe(errfd) < 0 || pipe(mailfd) < 0) { + log(LOG_ERR, "pipe() call failed: %s\n", + strerror(errno)); + if (errfd[0] != -1) { + close(errfd[0]); + close(errfd[1]); + } + tab_reschedule(job); + return; + } + (void) fcntl(errfd[1], F_SETFD, + fcntl(errfd[1], F_GETFD) | FD_CLOEXEC); + + if ((pid= fork()) == -1) { + log(LOG_ERR, "fork() call failed: %s\n", + strerror(errno)); + close(errfd[0]); + close(errfd[1]); + close(mailfd[0]); + close(mailfd[1]); + tab_reschedule(job); + return; + } + + if (pid == 0) { + /* Child that is to be the mailer. */ + char subject[70+20], *ps; + + close(errfd[0]); + close(mailfd[1]); + if (mailfd[0] != 0) { + dup2(mailfd[0], 0); + close(mailfd[0]); + } + + memset(subject, 0, sizeof(subject)); + sprintf(subject, + "Output from your %s job: %.50s", + job->atjob ? "AT" : "cron", + job->cmd); + if (subject[70] != 0) { + strcpy(subject+70-3, "..."); + } + for (ps= subject; *ps != 0; ps++) { + if (*ps == '\n') *ps= '%'; + } + + execl("/usr/bin/mail", "mail", "-s", subject, + pw->pw_name, (char *) nil); + write(errfd[1], &errno, sizeof(errno)); + _exit(1); + } + + close(mailfd[0]); + close(errfd[1]); + if (read(errfd[0], &errno, sizeof(errno)) > 0) { + log(LOG_ERR, "can't execute /usr/bin/mail: %s\n", + strerror(errno)); + close(errfd[0]); + close(mailfd[1]); + tab_reschedule(job); + return; + } + close(errfd[0]); + } + + if (pipe(errfd) < 0) { + log(LOG_ERR, "pipe() call failed: %s\n", strerror(errno)); + if (need_mailer) close(mailfd[1]); + tab_reschedule(job); + return; + } + (void) fcntl(errfd[1], F_SETFD, fcntl(errfd[1], F_GETFD) | FD_CLOEXEC); + + if ((pid= fork()) == -1) { + log(LOG_ERR, "fork() call failed: %s\n", strerror(errno)); + close(errfd[0]); + close(errfd[1]); + if (need_mailer) close(mailfd[1]); + tab_reschedule(job); + return; + } + + if (pid == 0) { + /* Child that is to be the cron job. */ + close(errfd[0]); + if (need_mailer) { + if (mailfd[1] != 1) { + dup2(mailfd[1], 1); + close(mailfd[1]); + } + dup2(1, 2); + } + + if (pw != nil) { + /* Change id to the owner of the job. */ + initgroups(pw->pw_name, pw->pw_gid); + setgid(pw->pw_gid); + setuid(pw->pw_uid); + chdir(pw->pw_dir); + if (setenv("USER", pw->pw_name) < 0) goto bad; + if (setenv("LOGNAME", pw->pw_name) < 0) goto bad; + if (setenv("HOME", pw->pw_dir) < 0) goto bad; + if (setenv("SHELL", pw->pw_shell[0] == 0 ? "/bin/sh" + : pw->pw_shell) < 0) goto bad; + } + + if (job->atjob) { + execl("/bin/sh", "sh", tab->data, (char *) nil); + } else { + execl("/bin/sh", "sh", "-c", job->cmd, (char *) nil); + } + bad: + write(errfd[1], &errno, sizeof(errno)); + _exit(1); + } + + if (need_mailer) close(mailfd[1]); + close(errfd[1]); + if (read(errfd[0], &errno, sizeof(errno)) > 0) { + log(LOG_ERR, "can't execute /bin/sh: %s\n", strerror(errno)); + close(errfd[0]); + tab_reschedule(job); + return; + } + close(errfd[0]); + job->pid= pid; + if (debug >= 1) fprintf(stderr, "executing >%s<, pid = %ld\n", + job->cmd, (long) job->pid); +} + +static void load_crontabs(void) +/* Load all the crontabs we like to run. We didn't bother to make a list in + * an array or something, this is too system specific to make nice. + */ +{ + DIR *spool; +#if __minix_vmd + FILE *pkgs; +#endif + + tab_parse("/usr/lib/crontab", nil); + tab_parse("/usr/local/lib/crontab", nil); + tab_parse("/var/lib/crontab", nil); + +#if __minix_vmd + if ((pkgs= fopen("/usr/lib/packages", "r")) != nil) { + char name[NAME_MAX+1]; + char *np; + int c; + char tab[sizeof("/var/opt//lib/crontab") + NAME_MAX]; + + while ((c= fgetc(pkgs)) != EOF) { + np= name; + while (c != EOF && c != '/' && c != '\n') { + if (np < name+NAME_MAX) *np++ = c; + c= fgetc(pkgs); + } + *np= 0; + while (c != EOF && c != '\n') c= fgetc(pkgs); + + if (name[0] == 0) continue; /* ? */ + + strcpy(tab, "/var/opt/"); + strcat(tab, name); + strcat(tab, "/lib/crontab"); + tab_parse(tab, nil); + } + if (ferror(pkgs)) { + log(LOG_CRIT, "/usr/lib/packages: %s\n", + strerror(errno)); + } + fclose(pkgs); + } else { + if (errno != ENOENT) { + log(LOG_ERR, "/usr/lib/packages: %s\n", + strerror(errno)); + } + } +#endif /* Minix-vmd */ + + if ((spool= opendir("/usr/spool/crontabs")) != nil) { + struct dirent *entry; + char tab[sizeof("/usr/spool/crontabs/") + NAME_MAX]; + + while ((entry= readdir(spool)) != nil) { + if (entry->d_name[0] == '.') continue; + + strcpy(tab, "/usr/spool/crontabs/"); + strcat(tab, entry->d_name); + tab_parse(tab, entry->d_name); + } + closedir(spool); + } + + /* Find the first to be executed AT job. */ + tab_find_atjob("/usr/spool/at"); + + tab_purge(); + if (debug >= 2) { + tab_print(stderr); + fprintf(stderr, "%lu memory chunks in use\n", + (unsigned long) alloc_count); + } +} + +static void handler(int sig) +{ + switch (sig) { + case SIGHUP: + need_reload= 1; + break; + case SIGINT: + case SIGTERM: + need_quit= 1; + break; + case SIGUSR1: + debug++; + break; + case SIGUSR2: + debug= 0; + break; + } + alarm(1); /* A signal may come just before a blocking call. */ + busy= 1; +} + +static void usage(void) +{ + fprintf(stderr, "Usage: %s [-d[#]]\n", prog_name); + exit(1); +} + +int main(int argc, char **argv) +{ + int i; + struct sigaction sa, osa; + FILE *pf; + int r; + + prog_name= strrchr(argv[0], '/'); + if (prog_name == nil) prog_name= argv[0]; else prog_name++; + + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++] + 1; + + if (opt[0] == '-' && opt[1] == 0) break; /* -- */ + + while (*opt != 0) switch (*opt++) { + case 'd': + if (*opt == 0) { + debug= 1; + } else { + debug= strtoul(opt, &opt, 10); + if (*opt != 0) usage(); + } + break; + default: + usage(); + } + } + if (i != argc) usage(); + + selectlog(SYSLOG); + openlog(prog_name, LOG_PID, LOG_DAEMON); + setlogmask(LOG_UPTO(LOG_INFO)); + + /* Save process id. */ + if ((pf= fopen(PIDFILE, "w")) == NULL) { + fprintf(stderr, "%s: %s\n", PIDFILE, strerror(errno)); + exit(1); + } + fprintf(pf, "%d\n", getpid()); + if (ferror(pf) || fclose(pf) == EOF) { + fprintf(stderr, "%s: %s\n", PIDFILE, strerror(errno)); + exit(1); + } + + sigemptyset(&sa.sa_mask); + sa.sa_flags= 0; + sa.sa_handler= handler; + + /* Hangup: Reload crontab files. */ + sigaction(SIGHUP, &sa, nil); + + /* User signal 1 & 2: Raise or reset debug level. */ + sigaction(SIGUSR1, &sa, nil); + sigaction(SIGUSR2, &sa, nil); + + /* Interrupt and Terminate: Cleanup and exit. */ + if (sigaction(SIGINT, nil, &osa) == 0 && osa.sa_handler != SIG_IGN) { + sigaction(SIGINT, &sa, nil); + } + if (sigaction(SIGTERM, nil, &osa) == 0 && osa.sa_handler != SIG_IGN) { + sigaction(SIGTERM, &sa, nil); + } + + /* Alarm: Wake up and run a job. */ + sigaction(SIGALRM, &sa, nil); + + /* Initialize current time and time next to do something. */ + time(&now); + next= NEVER; + + /* Table load required first time. */ + need_reload= 1; + + do { + if (need_reload) { + need_reload= 0; + load_crontabs(); + busy= 1; + } + + /* Run jobs whose time has come. */ + if (next <= now) { + cronjob_t *job; + + if ((job= tab_nextjob()) != nil) run_job(job); + busy= 1; + } + + if (busy) { + /* Did a job finish? */ + r= waitpid(-1, nil, WNOHANG); + busy= 0; + } else { + /* Sleep until the next job must be started. */ + if (next == NEVER) { + alarm(0); + } else { +#if __minix_vmd + struct timeval tvnext; + + tvnext.tv_sec= next; + tvnext.tv_usec= 0; + sysutime(UTIME_SETALARM, &tvnext); +#else + alarm((next - now) > INT_MAX + ? INT_MAX : (next - now)); +#endif + } + if (debug >= 1) fprintf(stderr, "%s: sleep until %s", + prog_name, ctime(&next)); + + closelog(); /* Don't keep resources open. */ + + /* Wait for a job to exit or a timeout. */ + r= waitpid(-1, nil, 0); + if (r == -1 && errno == ECHILD) pause(); + alarm(0); + time(&now); + } + + if (r > 0) { + /* A job has finished, reschedule it. */ + if (debug >= 1) fprintf(stderr, "pid %d has exited\n", + r); + tab_reap_job((pid_t) r); + busy= 1; + } + } while (!need_quit); + + /* Remove the pid file to signal that cron is gone. */ + unlink(PIDFILE); + + return 0; +} + +/* + * $PchId: cron.c,v 1.4 2000/07/17 19:00:35 philip Exp $ + */ diff --git a/commands/cron/crontab.c b/commands/cron/crontab.c new file mode 100755 index 000000000..13da7d390 --- /dev/null +++ b/commands/cron/crontab.c @@ -0,0 +1,255 @@ +/* crontab 1.2 - user crontab manipulation Author: Kees J. Bot + * 12 Jan 1997 + */ +#define nil ((void*)0) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "misc.h" +#include "tab.h" + +#if __minix && !__minix_vmd +#define seteuid(uid) ((uid),0) /* Minix can't fiddle with uids. */ +#endif + +static int opentab(int uid, char *file, int how) +/* Open a crontab file under the given uid. How is 'r' or 'w'. Return + * the result of open(2). + */ +{ + uid_t safe_uid; + int flags, r, err; + + switch (how) { + case 'r': flags= O_RDONLY; break; + case 'w': flags= O_WRONLY | O_CREAT | O_TRUNC; break; + default: errno= EINVAL; return -1; + } + +#if __minix && !__minix_vmd + /* Standard Minix has no saved uid, so use the lousy old access(). */ + if (uid != 0) { + if (access(file, how == 'r' ? R_OK : W_OK) < 0) return -1; + } +#endif + + safe_uid= geteuid(); + seteuid(uid); + r= open(file, flags, 0666); + err= errno; + seteuid(safe_uid); + errno= err; + return r; +} + +static void copytab(int fd_in, char *file_in, int fd_out, char *file_out) +/* Copy one open file to another. Complain and exit on errors. */ +{ + ssize_t r, w; + char buf[1024]; + + while ((r= read(fd_in, buf, sizeof(buf))) > 0) { + w= 0; + while (w < r) { + if ((r= write(fd_out, buf+w, r-w)) <= 0) { + fprintf(stderr, + "%s: Write error on %s: %s\n", + prog_name, + file_out, + r == 0 ? "End of file" + : strerror(errno)); + exit(1); + } + w+= r; + } + } + if (r < 0) { + fprintf(stderr, "%s: Read error on %s: %s\n", + prog_name, file_in, strerror(errno)); + exit(1); + } +} + +static void usage(void) +{ + fprintf(stderr, + "Usage: %s -c [user] file # Change crontab\n" + " %s -l [user] # List crontab\n" + " %s -r [user] # Remove crontab\n" + " %s -p # Tell cron to reload\n", + prog_name, prog_name, prog_name, prog_name); + exit(1); +} + +int main(int argc, char **argv) +{ + int i; + int cflag, lflag, rflag, pflag; + uid_t uid; + char *user, *file; + struct passwd *pw; + static char SPOOLDIR[]= "/usr/spool/crontabs"; + char tabfile[sizeof(SPOOLDIR) + NAME_MAX]; + + prog_name= strrchr(argv[0], '/'); + if (prog_name == nil) prog_name= argv[0]; else prog_name++; + + cflag= lflag= rflag= pflag= 0; + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++] + 1; + + if (opt[0] == '-' && opt[1] == 0) break; /* -- */ + + while (*opt != 0) switch (*opt++) { + case 'c': cflag= 1; break; + case 'l': lflag= 1; break; + case 'r': rflag= 1; break; + case 'p': pflag= 1; break; + default: usage(); + } + } + if (cflag + lflag + rflag + pflag != 1) usage(); + + user= file= nil; + if (!pflag && i < argc) user= argv[i++]; + if (cflag) { + if (user == nil) usage(); + if (i < argc) { + file= argv[i++]; + } else { + file= user; + user= nil; + } + } + if (i != argc) usage(); + + if (geteuid() != 0) { + fprintf(stderr, "%s: No root privileges?\n", prog_name); + } + uid= getuid(); + if (user == nil) { + if ((pw= getpwuid(uid)) == nil) { + fprintf(stderr, + "%s: Don't know who you (uid %lu) are!\n", + prog_name, (unsigned long) uid); + exit(1); + } + } else { + if ((pw= getpwnam(user)) == nil) { + fprintf(stderr, + "%s: Don't know who you (%s) are!\n", + prog_name, user); + exit(1); + } + } + if (uid != 0 && pw->pw_uid != uid) { + fprintf(stderr, + "%s: Only root can change the crontabs of others!\n", + prog_name); + exit(1); + } + user= pw->pw_name; + uid= pw->pw_uid; + seteuid(uid); + umask(0077); + + selectlog(STDERR); + sprintf(tabfile, "%s/%s", SPOOLDIR, user); + + if (lflag) { + int fd; + + if ((fd= opentab(0, tabfile, 'r')) < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + prog_name, tabfile, strerror(errno)); + exit(1); + } + copytab(fd, tabfile, 1, "stdout"); + close(fd); + } + + if (rflag) { + seteuid(0); + if (unlink(tabfile) < 0) { + fprintf(stderr, "%s: Can't remove %s: %s\n", + prog_name, tabfile, strerror(errno)); + exit(1); + } + seteuid(uid); + printf("Crontab of %s removed\n", user); + pflag= 1; + } + + if (cflag) { + int fd1, fd2; + + if ((fd1= opentab(uid, file, 'r')) < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + prog_name, file, strerror(errno)); + exit(1); + } + + /* Try to parse the new crontab file. If the parsing + * succeeds then 'crontabs' will be non-null. + */ + tab_parse(file, user); + tab_purge(); + if (crontabs == nil) exit(1); + + if ((fd2= opentab(0, tabfile, 'w')) < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + prog_name, tabfile, strerror(errno)); + exit(1); + } + copytab(fd1, file, fd2, tabfile); + close(fd1); + close(fd2); + printf("New crontab for %s installed\n", user); + pflag= 1; + } + + if (pflag) { + /* Alert cron to the new situation. */ + FILE *fp; + + seteuid(0); + if ((fp= fopen(PIDFILE, "r")) != NULL) { + unsigned long pid; + int c; + + pid= 0; + while ((c= fgetc(fp)) != EOF && c != '\n') { + if ((unsigned) (c - '0') >= 10) { + pid= 0; break; + } + pid= 10*pid + (c - '0'); + if (pid >= 30000) { pid= 0; break; } + } + if (pid > 1 && kill((pid_t) pid, SIGHUP) == 0) { + pflag= 0; + } + } + seteuid(uid); + if (pflag) { + fprintf(stderr, + "%s: Alerting cron has failed; cron still running?\n", + prog_name); + exit(1); + } + printf("Cron signalled to reload tables\n"); + } + return 0; +} + +/* + * $PchId: crontab.c,v 1.4 2000/07/17 18:54:50 philip Exp $ + */ diff --git a/commands/cron/misc.c b/commands/cron/misc.c new file mode 100755 index 000000000..30b0b036f --- /dev/null +++ b/commands/cron/misc.c @@ -0,0 +1,68 @@ +/* misc.c - Miscellaneous stuff for cron Author: Kees J. Bot + * 12 Jan 1997 + */ +#define nil ((void*)0) +#include +#include +#include +#include +#include +#include "misc.h" + +char *prog_name; /* Name of this program. */ +time_t now; /* Cron's idea of the current time. */ +time_t next; /* Time to run the next job. */ + +size_t alloc_count; /* # Of chunks of memory allocated. */ + +void *allocate(size_t len) +/* Checked malloc(). Better not feed it length 0. */ +{ + void *mem; + + if ((mem= malloc(len)) == nil) { + log(LOG_ALERT, "Out of memory, exiting\n"); + exit(1); + } + alloc_count++; + return mem; +} + +void deallocate(void *mem) +{ + if (mem != nil) { + free(mem); + alloc_count--; + } +} + +static enum logto logto= SYSLOG; + +void selectlog(enum logto where) +/* Select where logging output should go, syslog or stdout. */ +{ + logto= where; +} + +void log(int level, const char *fmt, ...) +/* Like syslog(), but may go to stderr. */ +{ + va_list ap; + + va_start(ap, fmt); + +#if __minix_vmd || !__minix + if (logto == SYSLOG) { + vsyslog(level, fmt, ap); + } else +#endif + { + fprintf(stderr, "%s: ", prog_name); + vfprintf(stderr, fmt, ap); + } + va_end(ap); +} + +/* + * $PchId: misc.c,v 1.3 2000/07/17 19:01:57 philip Exp $ + */ diff --git a/commands/cron/misc.h b/commands/cron/misc.h new file mode 100755 index 000000000..b8229b544 --- /dev/null +++ b/commands/cron/misc.h @@ -0,0 +1,42 @@ +/* misc.h - miscellaneous stuff Author: Kees J. Bot + * 7 Dec 1996 + */ +#ifndef MISC__H +#define MISC__H + +#include + +/* The name of the program. */ +extern char *prog_name; + +/* Where cron stores it pid. */ +#define PIDFILE "/usr/run/cron.pid" + +/* Cron's idea of the current time, and the time next to run something. */ +extern time_t now; +extern time_t next; + +/* Memory allocation. */ +void *allocate(size_t len); +void deallocate(void *mem); +extern size_t alloc_count; + +/* Logging, by syslog or to stderr. */ +#if __minix_vmd || !__minix +#include +#else +enum log_dummy { LOG_ERR, LOG_CRIT, LOG_ALERT }; +#define openlog(ident, opt, facility) ((void) 0) +#define closelog() ((void) 0) +#define setlogmask(mask) (0) +#endif + +enum logto { SYSLOG, STDERR }; +void selectlog(enum logto where); +void log(int level, const char *fmt, ...); + +#endif /* MISC__H */ + +/* + * $PchId: misc.h,v 1.3 2000/07/17 18:56:02 philip Exp $ + */ diff --git a/commands/cron/tab.c b/commands/cron/tab.c new file mode 100755 index 000000000..858d12418 --- /dev/null +++ b/commands/cron/tab.c @@ -0,0 +1,814 @@ +/* tab.c - process crontabs and create in-core crontab data + * Author: Kees J. Bot + * 7 Dec 1996 + * Changes: + * 17 Jul 2000 by Philip Homburg + * - Tab_reschedule() rewritten (and fixed). + */ +#define nil ((void*)0) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "misc.h" +#include "tab.h" + +static int nextbit(bitmap_t map, int bit) +/* Return the next bit set in 'map' from 'bit' onwards, cyclic. */ +{ + for (;;) { + bit= (bit+1) & 63; + if (bit_isset(map, bit)) break; + } + return bit; +} + +void tab_reschedule(cronjob_t *job) +/* Reschedule one job. Compute the next time to run the job in job->rtime. + */ +{ + struct tm prevtm, nexttm, tmptm; + time_t nodst_rtime, dst_rtime; + + /* AT jobs are only run once. */ + if (job->atjob) { job->rtime= NEVER; return; } + + /* Was the job scheduled late last time? */ + if (job->late) job->rtime= now; + + prevtm= *localtime(&job->rtime); + prevtm.tm_sec= 0; + + nexttm= prevtm; + nexttm.tm_min++; /* Minimal increment */ + + for (;;) + { + if (nexttm.tm_min > 59) + { + nexttm.tm_min= 0; + nexttm.tm_hour++; + } + if (nexttm.tm_hour > 23) + { + nexttm.tm_min= 0; + nexttm.tm_hour= 0; + nexttm.tm_mday++; + } + if (nexttm.tm_mday > 31) + { + nexttm.tm_hour= nexttm.tm_min= 0; + nexttm.tm_mday= 1; + nexttm.tm_mon++; + } + if (nexttm.tm_mon >= 12) + { + nexttm.tm_hour= nexttm.tm_min= 0; + nexttm.tm_mday= 1; + nexttm.tm_mon= 0; + nexttm.tm_year++; + } + + /* Verify tm_year. A crontab entry cannot restrict tm_year + * directly. However, certain dates (such as Feb, 29th) do + * not occur every year. We limit the difference between + * nexttm.tm_year and prevtm.tm_year to detect impossible dates + * (e.g, Feb, 31st). In theory every date occurs within a + * period of 4 years. However, some years at the end of a + * century are not leap years (e.g, the year 2100). An extra + * factor of 2 should be enough. + */ + if (nexttm.tm_year-prevtm.tm_year > 2*4) + { + job->rtime= NEVER; + return; /* Impossible combination */ + } + + if (!job->do_wday) + { + /* Verify the mon and mday fields. If do_wday and + * do_mday are both true we have to merge both + * schedules. This is done after the call to mktime. + */ + if (!bit_isset(job->mon, nexttm.tm_mon)) + { + /* Clear other fields */ + nexttm.tm_mday= 1; + nexttm.tm_hour= nexttm.tm_min= 0; + + nexttm.tm_mon++; + continue; + } + + /* Verify mday */ + if (!bit_isset(job->mday, nexttm.tm_mday)) + { + /* Clear other fields */ + nexttm.tm_hour= nexttm.tm_min= 0; + + nexttm.tm_mday++; + continue; + } + } + + /* Verify hour */ + if (!bit_isset(job->hour, nexttm.tm_hour)) + { + /* Clear tm_min field */ + nexttm.tm_min= 0; + + nexttm.tm_hour++; + continue; + } + + /* Verify min */ + if (!bit_isset(job->min, nexttm.tm_min)) + { + nexttm.tm_min++; + continue; + } + + /* Verify that we don't have any problem with DST. Try + * tm_isdst=0 first. */ + tmptm= nexttm; + tmptm.tm_isdst= 0; +#if 0 + fprintf(stderr, + "tab_reschedule: trying %04d-%02d-%02d %02d:%02d:%02d isdst=0\n", + 1900+nexttm.tm_year, nexttm.tm_mon+1, + nexttm.tm_mday, nexttm.tm_hour, + nexttm.tm_min, nexttm.tm_sec); +#endif + nodst_rtime= job->rtime= mktime(&tmptm); + if (job->rtime == -1) { + /* This should not happen. */ + log(LOG_ERR, + "mktime failed for %04d-%02d-%02d %02d:%02d:%02d", + 1900+nexttm.tm_year, nexttm.tm_mon+1, + nexttm.tm_mday, nexttm.tm_hour, + nexttm.tm_min, nexttm.tm_sec); + job->rtime= NEVER; + return; + } + if (tmptm.tm_hour != nexttm.tm_hour || + tmptm.tm_min != nexttm.tm_min) + { + assert(tmptm.tm_isdst); + tmptm= nexttm; + tmptm.tm_isdst= 1; +#if 0 + fprintf(stderr, + "tab_reschedule: trying %04d-%02d-%02d %02d:%02d:%02d isdst=1\n", + 1900+nexttm.tm_year, nexttm.tm_mon+1, + nexttm.tm_mday, nexttm.tm_hour, + nexttm.tm_min, nexttm.tm_sec); +#endif + dst_rtime= job->rtime= mktime(&tmptm); + if (job->rtime == -1) { + /* This should not happen. */ + log(LOG_ERR, + "mktime failed for %04d-%02d-%02d %02d:%02d:%02d\n", + 1900+nexttm.tm_year, nexttm.tm_mon+1, + nexttm.tm_mday, nexttm.tm_hour, + nexttm.tm_min, nexttm.tm_sec); + job->rtime= NEVER; + return; + } + if (tmptm.tm_hour != nexttm.tm_hour || + tmptm.tm_min != nexttm.tm_min) + { + assert(!tmptm.tm_isdst); + /* We have a problem. This time neither + * exists with DST nor without DST. + * Use the latest time, which should be + * nodst_rtime. + */ + assert(nodst_rtime > dst_rtime); + job->rtime= nodst_rtime; +#if 0 + fprintf(stderr, + "During DST trans. %04d-%02d-%02d %02d:%02d:%02d\n", + 1900+nexttm.tm_year, nexttm.tm_mon+1, + nexttm.tm_mday, nexttm.tm_hour, + nexttm.tm_min, nexttm.tm_sec); +#endif + } + } + + /* Verify this the combination (tm_year, tm_mon, tm_mday). */ + if (tmptm.tm_mday != nexttm.tm_mday || + tmptm.tm_mon != nexttm.tm_mon || + tmptm.tm_year != nexttm.tm_year) + { + /* Wrong day */ +#if 0 + fprintf(stderr, "Wrong day\n"); +#endif + nexttm.tm_hour= nexttm.tm_min= 0; + nexttm.tm_mday++; + continue; + } + + /* Check tm_wday */ + if (job->do_wday && bit_isset(job->wday, tmptm.tm_wday)) + { + /* OK, wday matched */ + break; + } + + /* Check tm_mday */ + if (job->do_mday && bit_isset(job->mon, tmptm.tm_mon) && + bit_isset(job->mday, tmptm.tm_mday)) + { + /* OK, mon and mday matched */ + break; + } + + if (!job->do_wday && !job->do_mday) + { + /* No need to match wday and mday */ + break; + } + + /* Wrong day */ +#if 0 + fprintf(stderr, "Wrong mon+mday or wday\n"); +#endif + nexttm.tm_hour= nexttm.tm_min= 0; + nexttm.tm_mday++; + } +#if 0 + fprintf(stderr, "Using %04d-%02d-%02d %02d:%02d:%02d \n", + 1900+nexttm.tm_year, nexttm.tm_mon+1, nexttm.tm_mday, + nexttm.tm_hour, nexttm.tm_min, nexttm.tm_sec); + tmptm= *localtime(&job->rtime); + fprintf(stderr, "Act. %04d-%02d-%02d %02d:%02d:%02d isdst=%d\n", + 1900+tmptm.tm_year, tmptm.tm_mon+1, tmptm.tm_mday, + tmptm.tm_hour, tmptm.tm_min, tmptm.tm_sec, + tmptm.tm_isdst); +#endif + + + /* Is job issuing lagging behind with the progress of time? */ + job->late= (job->rtime < now); + + /* The result is in job->rtime. */ + if (job->rtime < next) next= job->rtime; +} + +#define isdigit(c) ((unsigned) ((c) - '0') < 10) + +static char *get_token(char **ptr) +/* Return a pointer to the next token in a string. Move *ptr to the end of + * the token, and return a pointer to the start. If *ptr == start of token + * then we're stuck against a newline or end of string. + */ +{ + char *start, *p; + + p= *ptr; + while (*p == ' ' || *p == '\t') p++; + + start= p; + while (*p != ' ' && *p != '\t' && *p != '\n' && *p != 0) p++; + *ptr= p; + return start; +} + +static int range_parse(char *file, char *data, bitmap_t map, + int min, int max, int *wildcard) +/* Parse a comma separated series of 'n', 'n-m' or 'n:m' numbers. 'n' + * includes number 'n' in the bit map, 'n-m' includes all numbers between + * 'n' and 'm' inclusive, and 'n:m' includes 'n+k*m' for any k if in range. + * Numbers must fall between 'min' and 'max'. A '*' means all numbers. A + * '?' is allowed as a synonym for the current minute, which only makes sense + * in the minute field, i.e. max must be 59. Return true iff parsed ok. + */ +{ + char *p; + int end; + int n, m; + + /* Clear all bits. */ + for (n= 0; n < 8; n++) map[n]= 0; + + p= data; + while (*p != ' ' && *p != '\t' && *p != '\n' && *p != 0) p++; + end= *p; + *p= 0; + p= data; + + if (*p == 0) { + log(LOG_ERR, "%s: not enough time fields\n", file); + return 0; + } + + /* Is it a '*'? */ + if (p[0] == '*' && p[1] == 0) { + for (n= min; n <= max; n++) bit_set(map, n); + p[1]= end; + *wildcard= 1; + return 1; + } + *wildcard= 0; + + /* Parse a comma separated series of numbers or ranges. */ + for (;;) { + if (*p == '?' && max == 59 && p[1] != '-') { + n= localtime(&now)->tm_min; + p++; + } else { + if (!isdigit(*p)) goto syntax; + n= 0; + do { + n= 10 * n + (*p++ - '0'); + if (n > max) goto range; + } while (isdigit(*p)); + } + if (n < min) goto range; + + if (*p == '-') { /* A range of the form 'n-m'? */ + p++; + if (!isdigit(*p)) goto syntax; + m= 0; + do { + m= 10 * m + (*p++ - '0'); + if (m > max) goto range; + } while (isdigit(*p)); + if (m < n) goto range; + do { + bit_set(map, n); + } while (++n <= m); + } else + if (*p == ':') { /* A repeat of the form 'n:m'? */ + p++; + if (!isdigit(*p)) goto syntax; + m= 0; + do { + m= 10 * m + (*p++ - '0'); + if (m > (max-min+1)) goto range; + } while (isdigit(*p)); + if (m == 0) goto range; + while (n >= min) n-= m; + while ((n+= m) <= max) bit_set(map, n); + } else { + /* Simply a number */ + bit_set(map, n); + } + if (*p == 0) break; + if (*p++ != ',') goto syntax; + } + *p= end; + return 1; + syntax: + log(LOG_ERR, "%s: field '%s': bad syntax for a %d-%d time field\n", + file, data, min, max); + return 0; + range: + log(LOG_ERR, "%s: field '%s': values out of the %d-%d allowed range\n", + file, data, min, max); + return 0; +} + +void tab_parse(char *file, char *user) +/* Parse a crontab file and add its data to the tables. Handle errors by + * yourself. Table is owned by 'user' if non-null. + */ +{ + crontab_t **atab, *tab; + cronjob_t **ajob, *job; + int fd; + struct stat st; + char *p, *q; + size_t n; + ssize_t r; + int ok, wc; + + for (atab= &crontabs; (tab= *atab) != nil; atab= &tab->next) { + if (strcmp(file, tab->file) == 0) break; + } + + /* Try to open the file. */ + if ((fd= open(file, O_RDONLY)) < 0 || fstat(fd, &st) < 0) { + if (errno != ENOENT) { + log(LOG_ERR, "%s: %s\n", file, strerror(errno)); + } + if (fd != -1) close(fd); + return; + } + + /* Forget it if the file is awfully big. */ + if (st.st_size > TAB_MAX) { + log(LOG_ERR, "%s: %lu bytes is bigger than my %lu limit\n", + file, + (unsigned long) st.st_size, + (unsigned long) TAB_MAX); + return; + } + + /* If the file is the same as before then don't bother. */ + if (tab != nil && st.st_mtime == tab->mtime) { + close(fd); + tab->current= 1; + return; + } + + /* Create a new table structure. */ + tab= allocate(sizeof(*tab)); + tab->file= allocate((strlen(file) + 1) * sizeof(tab->file[0])); + strcpy(tab->file, file); + tab->user= nil; + if (user != nil) { + tab->user= allocate((strlen(user) + 1) * sizeof(tab->user[0])); + strcpy(tab->user, user); + } + tab->data= allocate((st.st_size + 1) * sizeof(tab->data[0])); + tab->jobs= nil; + tab->mtime= st.st_mtime; + tab->current= 0; + tab->next= *atab; + *atab= tab; + + /* Pull a new table in core. */ + n= 0; + while (n < st.st_size) { + if ((r = read(fd, tab->data + n, st.st_size - n)) < 0) { + log(LOG_CRIT, "%s: %s", file, strerror(errno)); + close(fd); + return; + } + if (r == 0) break; + n+= r; + } + close(fd); + tab->data[n]= 0; + if (strlen(tab->data) < n) { + log(LOG_ERR, "%s contains a null character\n", file); + return; + } + + /* Parse the file. */ + ajob= &tab->jobs; + p= tab->data; + ok= 1; + while (ok && *p != 0) { + q= get_token(&p); + if (*q == '#' || q == p) { + /* Comment or empty. */ + while (*p != 0 && *p++ != '\n') {} + continue; + } + + /* One new job coming up. */ + *ajob= job= allocate(sizeof(*job)); + *(ajob= &job->next)= nil; + job->tab= tab; + + if (!range_parse(file, q, job->min, 0, 59, &wc)) { + ok= 0; + break; + } + + q= get_token(&p); + if (!range_parse(file, q, job->hour, 0, 23, &wc)) { + ok= 0; + break; + } + + q= get_token(&p); + if (!range_parse(file, q, job->mday, 1, 31, &wc)) { + ok= 0; + break; + } + job->do_mday= !wc; + + q= get_token(&p); + if (!range_parse(file, q, job->mon, 1, 12, &wc)) { + ok= 0; + break; + } + job->do_mday |= !wc; + + q= get_token(&p); + if (!range_parse(file, q, job->wday, 0, 7, &wc)) { + ok= 0; + break; + } + job->do_wday= !wc; + + /* 7 is Sunday, but 0 is a common mistake because it is in the + * tm_wday range. We allow and even prefer it internally. + */ + if (bit_isset(job->wday, 7)) { + bit_clr(job->wday, 7); + bit_set(job->wday, 0); + } + + /* The month range is 1-12, but tm_mon likes 0-11. */ + job->mon[0] >>= 1; + if (bit_isset(job->mon, 8)) bit_set(job->mon, 7); + job->mon[1] >>= 1; + + /* Scan for options. */ + job->user= nil; + while (q= get_token(&p), *q == '-') { + q++; + if (q[0] == '-' && q+1 == p) { + /* -- */ + q= get_token(&p); + break; + } + while (q < p) switch (*q++) { + case 'u': + if (q == p) q= get_token(&p); + if (q == p) goto usage; + memmove(q-1, q, p-q); /* gross... */ + p[-1]= 0; + job->user= q-1; + q= p; + break; + default: + usage: + log(LOG_ERR, + "%s: bad option -%c, good options are: -u username\n", + file, q[-1]); + ok= 0; + goto endtab; + } + } + + /* A crontab owned by a user can only do things as that user. */ + if (tab->user != nil) job->user= tab->user; + + /* Inspect the first character of the command. */ + job->cmd= q; + if (q == p || *q == '#') { + /* Rest of the line is empty, i.e. the commands are on + * the following lines indented by one tab. + */ + while (*p != 0 && *p++ != '\n') {} + if (*p++ != '\t') { + log(LOG_ERR, "%s: contains an empty command\n", + file); + ok= 0; + goto endtab; + } + while (*p != 0) { + if ((*q = *p++) == '\n') { + if (*p != '\t') break; + p++; + } + q++; + } + } else { + /* The command is on this line. Alas we must now be + * backwards compatible and change %'s to newlines. + */ + p= q; + while (*p != 0) { + if ((*q = *p++) == '\n') break; + if (*q == '%') *q= '\n'; + q++; + } + } + *q= 0; + job->rtime= now; + job->late= 0; /* It is on time. */ + job->atjob= 0; /* True cron job. */ + job->pid= IDLE_PID; /* Not running yet. */ + tab_reschedule(job); /* Compute next time to run. */ + } + endtab: + + if (ok) tab->current= 1; +} + +void tab_find_atjob(char *atdir) +/* Find the first to be executed AT job and kludge up an crontab job for it. + * We set tab->file to "atdir/jobname", tab->data to "atdir/past/jobname", + * and job->cmd to "jobname". + */ +{ + DIR *spool; + struct dirent *entry; + time_t t0, t1; + struct tm tmnow, tmt1; + static char template[] = "96.365.1546.00"; + char firstjob[sizeof(template)]; + int i; + crontab_t *tab; + cronjob_t *job; + + if ((spool= opendir(atdir)) == nil) return; + + tmnow= *localtime(&now); + t0= NEVER; + + while ((entry= readdir(spool)) != nil) { + /* Check if the name fits the template. */ + for (i= 0; template[i] != 0; i++) { + if (template[i] == '.') { + if (entry->d_name[i] != '.') break; + } else { + if (!isdigit(entry->d_name[i])) break; + } + } + if (template[i] != 0 || entry->d_name[i] != 0) continue; + + /* Convert the name to a time. Careful with the century. */ + memset(&tmt1, 0, sizeof(tmt1)); + tmt1.tm_year= atoi(entry->d_name+0); + while (tmt1.tm_year < tmnow.tm_year-10) tmt1.tm_year+= 100; + tmt1.tm_mday= 1+atoi(entry->d_name+3); + tmt1.tm_min= atoi(entry->d_name+7); + tmt1.tm_hour= tmt1.tm_min / 100; + tmt1.tm_min%= 100; + tmt1.tm_isdst= -1; + if ((t1= mktime(&tmt1)) == -1) { + /* Illegal time? Try in winter time. */ + tmt1.tm_isdst= 0; + if ((t1= mktime(&tmt1)) == -1) continue; + } + if (t1 < t0) { + t0= t1; + strcpy(firstjob, entry->d_name); + } + } + closedir(spool); + + if (t0 == NEVER) return; /* AT job spool is empty. */ + + /* Create new table and job structures. */ + tab= allocate(sizeof(*tab)); + tab->file= allocate((strlen(atdir) + 1 + sizeof(template)) + * sizeof(tab->file[0])); + strcpy(tab->file, atdir); + strcat(tab->file, "/"); + strcat(tab->file, firstjob); + tab->data= allocate((strlen(atdir) + 6 + sizeof(template)) + * sizeof(tab->data[0])); + strcpy(tab->data, atdir); + strcat(tab->data, "/past/"); + strcat(tab->data, firstjob); + tab->user= nil; + tab->mtime= 0; + tab->current= 1; + tab->next= crontabs; + crontabs= tab; + + tab->jobs= job= allocate(sizeof(*job)); + job->next= nil; + job->tab= tab; + job->user= nil; + job->cmd= tab->data + strlen(atdir) + 6; + job->rtime= t0; + job->late= 0; + job->atjob= 1; /* One AT job disguised as a cron job. */ + job->pid= IDLE_PID; + + if (job->rtime < next) next= job->rtime; +} + +void tab_purge(void) +/* Remove table data that is no longer current. E.g. a crontab got removed. + */ +{ + crontab_t **atab, *tab; + cronjob_t *job; + + atab= &crontabs; + while ((tab= *atab) != nil) { + if (tab->current) { + /* Table is fine. */ + tab->current= 0; + atab= &tab->next; + } else { + /* Table is not, or no longer valid; delete. */ + *atab= tab->next; + while ((job= tab->jobs) != nil) { + tab->jobs= job->next; + deallocate(job); + } + deallocate(tab->data); + deallocate(tab->file); + deallocate(tab->user); + deallocate(tab); + } + } +} + +static cronjob_t *reap_or_find(pid_t pid) +/* Find a finished job or search for the next one to run. */ +{ + crontab_t *tab; + cronjob_t *job; + cronjob_t *nextjob; + + nextjob= nil; + next= NEVER; + for (tab= crontabs; tab != nil; tab= tab->next) { + for (job= tab->jobs; job != nil; job= job->next) { + if (job->pid == pid) { + job->pid= IDLE_PID; + tab_reschedule(job); + } + if (job->pid != IDLE_PID) continue; + if (job->rtime < next) next= job->rtime; + if (job->rtime <= now) nextjob= job; + } + } + return nextjob; +} + +void tab_reap_job(pid_t pid) +/* A job has finished. Try to find it among the crontab data and reschedule + * it. Recompute time next to run a job. + */ +{ + (void) reap_or_find(pid); +} + +cronjob_t *tab_nextjob(void) +/* Find a job that should be run now. If none are found return null. + * Update 'next'. + */ +{ + return reap_or_find(NO_PID); +} + +static void pr_map(FILE *fp, bitmap_t map) +{ + int last_bit= -1, bit; + char *sep; + + sep= ""; + for (bit= 0; bit < 64; bit++) { + if (bit_isset(map, bit)) { + if (last_bit == -1) last_bit= bit; + } else { + if (last_bit != -1) { + fprintf(fp, "%s%d", sep, last_bit); + if (last_bit != bit-1) { + fprintf(fp, "-%d", bit-1); + } + last_bit= -1; + sep= ","; + } + } + } +} + +void tab_print(FILE *fp) +/* Print out a stored crontab file for debugging purposes. */ +{ + crontab_t *tab; + cronjob_t *job; + char *p; + struct tm tm; + + for (tab= crontabs; tab != nil; tab= tab->next) { + fprintf(fp, "tab->file = \"%s\"\n", tab->file); + fprintf(fp, "tab->user = \"%s\"\n", + tab->user == nil ? "(root)" : tab->user); + fprintf(fp, "tab->mtime = %s", ctime(&tab->mtime)); + + for (job= tab->jobs; job != nil; job= job->next) { + if (job->atjob) { + fprintf(fp, "AT job"); + } else { + pr_map(fp, job->min); fputc(' ', fp); + pr_map(fp, job->hour); fputc(' ', fp); + pr_map(fp, job->mday); fputc(' ', fp); + pr_map(fp, job->mon); fputc(' ', fp); + pr_map(fp, job->wday); + } + if (job->user != nil && job->user != tab->user) { + fprintf(fp, " -u %s", job->user); + } + fprintf(fp, "\n\t"); + for (p= job->cmd; *p != 0; p++) { + fputc(*p, fp); + if (*p == '\n') fputc('\t', fp); + } + fputc('\n', fp); + tm= *localtime(&job->rtime); + fprintf(fp, " rtime = %.24s%s\n", asctime(&tm), + tm.tm_isdst ? " (DST)" : ""); + if (job->pid != IDLE_PID) { + fprintf(fp, " pid = %ld\n", (long) job->pid); + } + } + } +} + +/* + * $PchId: tab.c,v 1.5 2000/07/25 22:07:51 philip Exp $ + */ diff --git a/commands/cron/tab.h b/commands/cron/tab.h new file mode 100755 index 000000000..34dce6037 --- /dev/null +++ b/commands/cron/tab.h @@ -0,0 +1,72 @@ +/* tab.h - in-core crontab data Author: Kees J. Bot + * 7 Dec 1996 + */ +#ifndef TAB__H +#define TAB__H + +#include +#include + +struct crontab; + +typedef unsigned char bitmap_t[8]; + +typedef struct cronjob { /* One entry in a crontab file */ + struct cronjob *next; + struct crontab *tab; /* Associated table file. */ + bitmap_t min; /* Minute (0-59) */ + bitmap_t hour; /* Hour (0-23) */ + bitmap_t mday; /* Day of the month (1-31) */ + bitmap_t mon; /* Month (1-12) */ + bitmap_t wday; /* Weekday (0-7 with 0 = 7 = Sunday) */ + char *user; /* User to run it as (nil = root) */ + char *cmd; /* Command to run */ + time_t rtime; /* When next to run */ + char do_mday; /* True iff mon or mday is not '*' */ + char do_wday; /* True iff wday is not '*' */ + char late; /* True iff the job is late */ + char atjob; /* True iff it is an AT job */ + pid_t pid; /* Process-id of job if nonzero */ +} cronjob_t; + +typedef struct crontab { + struct crontab *next; + char *file; /* Crontab name */ + char *user; /* Owner if non-null */ + time_t mtime; /* Last modified time */ + cronjob_t *jobs; /* List of jobs in the file */ + char *data; /* File data */ + int current; /* True if current, i.e. file exists */ +} crontab_t; + +crontab_t *crontabs; /* All crontabs. */ + +/* A time as far in the future as possible. */ +#define NEVER ((time_t) ((time_t) -1 < 0 ? LONG_MAX : ULONG_MAX)) + +/* Don't trust crontabs bigger than this: */ +#define TAB_MAX ((sizeof(int) == 2 ? 8 : 128) * 1024) + +/* Pid if no process running, or a pid value you'll never see. */ +#define IDLE_PID ((pid_t) 0) +#define NO_PID ((pid_t) -1) + +/* Bitmap operations. */ +#define bit_set(map, n) ((void) ((map)[(n) >> 3] |= (1 << ((n) & 7)))) +#define bit_clr(map, n) ((void) ((map)[(n) >> 3] &= ~(1 << ((n) & 7)))) +#define bit_isset(map, n) (!!((map)[(n) >> 3] & (1 << ((n) & 7)))) + +/* Functions. */ +void tab_parse(char *file, char *user); +void tab_find_atjob(char *atdir); +void tab_purge(void); +void tab_reap_job(pid_t pid); +void tab_reschedule(cronjob_t *job); +cronjob_t *tab_nextjob(void); +void tab_print(FILE *fp); + +#endif /* TAB__H */ + +/* + * $PchId: tab.h,v 1.3 2000/07/17 07:57:27 philip Exp $ + */ diff --git a/commands/de/Makefile b/commands/de/Makefile new file mode 100755 index 000000000..448d22a51 --- /dev/null +++ b/commands/de/Makefile @@ -0,0 +1,22 @@ +# Makefile for de + +CC = cc +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -D_POSIX_1_SOURCE=2 + +all: de + +OBJS = de.o de_stdin.o de_stdout.o de_diskio.o de_recover.o + +de: $(OBJS) + $(CC) -i $(OBJS) -o de + install -S 4kw de + +install: /usr/bin/de + +/usr/bin/de: de + install -cs -o bin de $@ + +$(OBJS): de.h + +clean: + rm -f *.bak *.o de diff --git a/commands/de/README b/commands/de/README new file mode 100755 index 000000000..619fde69c --- /dev/null +++ b/commands/de/README @@ -0,0 +1,156 @@ + de - A Minix Disk Editor + + Terrence W. Holm, Jan. 1989 + + +INTRODUCTION + + The de(1) disk editor allows a system administrator to + look at and modify a Minix file system device. Commands + allow movement throughout a file system device, displaying + information in a couple of formats, writing blocks from + the device onto another file, and rewriting words on the + disk. + + A few changes to the Minix file system aid recovering files. + I-node numbers are retained in directory entries now (they + get moved to the end). And all the i-node information is not + zeroed-out when a file is unlinked. So, after a file is + accidently rm(1)'ed, you can find the old i-node, and then + manually (or automatically) go to each of the freed blocks + and write them to a new file. + + +USES FOR THE DISK EDITOR + + 1) EDUCATION. Students can look at a file system in + a painless manner. For example you don't have to + use od(1) to look at the zone numbers in i-nodes. + + A simple assignment is to change the size of an un-mounted + floppy disk file system from 360 to 300 blocks. (A more + difficult assignment is to explain why this works, even + though fsck(1) and df(1) do not report the correct number + of free blocks. :-) + + 2) ADMINISTRATION. You can visually check inconsistencies + reported by fsck(1) before letting fsck(1) fix them. + You can change any word on the disk, this greatly simplifies + editing file system information. For example, changing the + size of a block special device is actually fun, no more + "blind" writing to your partitions. + + Bit maps can be displayed with 2048 "bits" per screen, + (on the IBM/PC console), see how your zones are allocated! + + 3) RECOVERING LOST FILES. You can search a disk for an ASCII + string, once found, the block can be written out to a file. + + A one line change to fs/path.c allows users to get the i-node + number for a file after it has been removed from a directory. + + Another couple lines changed in the file system keep the + i-node information available until the i-node is reused + (normally this information is zeroed out when an i-node is + released.) This allows a de(1) user to go to a released + i-node, get all the block numbers, go to these blocks and + write them back to a new file. + + The whole recovery process is automated by running "de -r file". + So, IF a file is unlink(2)'ed (eg. "rm file"), AND IF no one + allocates a new i-node or block in the mean-time, THEN you + can recover the file. + + +RECOVERY SECURITY + + Normally Minix hard disk partitions are r/w only by the super-user, + and floppy disks are r/w by anyone. This means that only "root" + can look at hard disk partitions, but others can use de(1) to play + with their floppy disks. + + When recovering files ("de -r file"), a user requires access to + the major file system partitions. This can be done by: + + (a) Give everyone access to the hard disks. DON'T DO THIS, it + defeats all the file system protection we already have. + + (b) Make de(1) set-uid "root". This is the way to go, IF you + are running a Minix system that has NO ACCESS from the + outside. This allows anyone to execute "de -r file", but only + root to use "de /dev/hd3". De(1) does some checking when + retrieving lost blocks, eg. making sure they really are + free blocks and making sure the user owned the i-node. + BUT, file system information has been lost when the file + was unlink(2)'ed, so de(1) can not be 100% sure that a + recovered block really belonged to the user. THIS IS A + SECURITY HOLE. [Since the only access to my machine is from + observable terminals and their associated humans, I run + de(1) as set-uid root.] + + (c) Keep the disks rw-------, and don't set-uid de(1). This + means that only the super-user can recover lost files. + So, if you accidently "rm", you must tell the system + administrator to "su" and recover your file, (be sure to + inform the other users to stop whatever they are doing + until the file is restored). + + +INSTALLATION + + - Install de.1 in /usr/man/cat1. + + - Install the files: Makefile, README, de.h, de.c, de_stdin.c, + de_stdout.c, de_diskio.c and de_recover.c in commands/de. + Add -F and -T. to the Makefile, if necessary. + + - "make" de(1). If a header file is not found, don't worry: + You probably have it somewhere, just link it to what de(1) + is looking for. This program also requires the subroutine + tolower(3), see EFTH MINIX report #50, if you don't have it. + + - Do you really want set-uid root on de? + + - Patch the files fs/path.c, fs/link.c and fs/open.c. If + you don't patch the file system then the recover option + "-r" and associated commands ('x' and 'X') will not work, + but de(1) is still functional and useful. + + - "make" a new fs, using -DRECOVER. Rebuild a boot diskette. + + +USING DE(1) FOR THE FIRST TIME + + De(1) starts up in "word" mode at block 0 of the specified + device. Hit the PGDN (or space bar) a few times, observing + all the information on the screen. Each PGUP/PGDN moves to + the next 1024 byte block, (de(1) only knows about 1 block per + zone file systems). Note that "word" mode only displays 32 + bytes at a time, so you are only observing the first 32 bytes + in the first few blocks when you skip using PGDN. + + Now go back to block 3, (zone bit map), using "g 3 ENTER". + Change to "map" mode "v m", and then use the down arrow key + to check each 2 Megs in the zone bit map. + + Now change to "block" mode using "v b". And go to some data + block, eg. "g 1000 ENTER". Use PGUP/PGDN to see what data + is in each nearby block. + + Remember 'h' gives you a help page. + + Try some more commands, for example: 'END', 'I', '/'. + (Note: searching through a whole disk under Minix takes a + long time: 30-60 seconds per megabyte, depending on your + machine, drive and controller, [Minix is embarrassingly slow].) + + Don't worry about looking at a mounted device, you must specify + the "-w" option before the 's' command is operational, and + this command is the only one which will try to modify the + contents of the device. + + +MINIX-ST + + Please contact me if you are interesting in attempting a port + to MINIX-ST. diff --git a/commands/de/de.c b/commands/de/de.c new file mode 100755 index 000000000..f5b5a1ecf --- /dev/null +++ b/commands/de/de.c @@ -0,0 +1,1335 @@ +/****************************************************************/ +/* */ +/* de.c */ +/* */ +/* Main loop of the "Disk editor". */ +/* */ +/****************************************************************/ +/* origination 1989-Jan-15 Terrence W. Holm */ +/****************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#undef ERROR /* arrgghh, errno.h has this pollution */ +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../../servers/fs/const.h" +#include "../../servers/fs/type.h" +#include "../../servers/fs/inode.h" + +#include "de.h" + +static char copyright[] = "de (c) Terrence W. Holm 1989"; + + +_PROTOTYPE(void Push , (de_state *s )); +_PROTOTYPE(int Get_Base , (int *base )); +_PROTOTYPE(int Get_Filename , (de_state *s )); +_PROTOTYPE(int Get_Count , (char *units , unsigned long *result )); +_PROTOTYPE(void Exec_Shell , (void)); +_PROTOTYPE(void Sigint , (int)); + + + +/****************************************************************/ +/* */ +/* main() */ +/* */ +/* Initialize. Handle the "-r" recovery option if */ +/* specified, else enter the main processing loop. */ +/* */ +/****************************************************************/ + + +void main( argc, argv ) + int argc; + char *argv[]; + + { + static de_state s; /* it is safer not to put it on the stack + * and some things probably now rely on zero + * initialization + */ + char *command_name = argv[0]; + int recover = 0; + + + s.device_mode = O_RDONLY; + + + /* Parse arguments */ + + if ( argc == 3 && strcmp( argv[1], "-r" ) == 0 ) + { + recover = 1; + --argc; + ++argv; + } + else if ( argc == 3 && strcmp( argv[1], "-w" ) == 0 ) + { + s.device_mode = O_RDWR; + --argc; + ++argv; + } + + if ( argc != 2 || *argv[1] == '-' ) + { + fprintf( stderr, "Usage: %s [-w] /dev/device\n", command_name ); + fprintf( stderr, " %s -r lost_file_name\n", command_name ); + exit( 1 ); + } + + + /* Set the effective id to the real id. This eliminates */ + /* any increase in privilege done by a set-uid bit on the */ + /* executable file. We want to be "root" for recovering */ + /* files, because we must be able to read the device. */ + /* However, in normal usage, de(1) should not let just */ + /* anyone look at a file system, thus we drop the privilege. */ + /* */ + /* NOTE: There is a security hole when using "-r" with a */ + /* set-uid de(1). Do not use set-uid root if there is any */ + /* way to externally access your Minix system. */ + + if ( ! recover ) + { + setuid( getuid() ); + setgid( getgid() ); + } + + + /* Set terminal characteristics, and ^C interrupt handler */ + + Save_Term(); + + if ( signal( SIGINT, SIG_IGN ) != SIG_IGN ) + { + signal( SIGINT, Sigint ); + signal( SIGQUIT, Sigint ); + } + + Set_Term(); + + if ( ! Init_Termcap() ) + Error( "Requires a termcap entry" ); + + + + /* Get the device file name. If recovering, also open an output file. */ + + if ( recover ) + { + char *dir_name; + char *file_name; + struct stat device_stat; + struct stat tmp_stat; + + /* Split the path name into a directory and a file name. */ + + if ( strlen(argv[1]) > MAX_STRING ) + Error( "Path name too long" ); + + if ( ! Path_Dir_File( argv[1], &dir_name, &file_name ) ) + Error( "Recover aborted" ); + + /* Find the device holding the directory. */ + + if ( (s.device_name = File_Device( dir_name )) == NULL ) + Error( "Recover aborted" ); + + + /* The output file will be in /tmp with the same file name. */ + + strcpy( s.file_name, TMP ); + strcat( s.file_name, "/" ); + strcat( s.file_name, file_name ); + + + /* Make sure /tmp is not on the same device as the file we */ + /* are trying to recover (we don't want to use up the free */ + /* i-node and blocks before we get a chance to recover them). */ + + if ( stat( s.device_name, &device_stat ) == -1 ) + Error( "Can not stat(2) device %s", s.device_name ); + + if ( stat( TMP, &tmp_stat ) == -1 ) + Error( "Can not stat(2) directory %s", TMP ); + + if ( device_stat.st_rdev == tmp_stat.st_dev ) + Error( "Will not recover files on the same device as %s", TMP ); + + if ( access( s.file_name, F_OK ) == 0 ) + Error( "Will not overwrite file %s", s.file_name ); + + + /* Open the output file. */ + + if ( (s.file_f = fopen( s.file_name, "w" )) == NULL ) + Error( "Can not open file %s", s.file_name ); + + /* Don't let anyone else look at the recovered file */ + + chmod( s.file_name, 0700 ); + + /* If running as root then change the owner of the */ + /* restored file. If not running as root then the */ + /* chown(2) will fail. */ + + chown( s.file_name, getuid(), getgid() ); + } + else + { + s.device_name = argv[1]; + s.file_name[ 0 ] = '\0'; + } + + + /* Open the device file. */ + + { + struct stat device_stat; + off_t size; + + if ( stat( s.device_name, &device_stat ) == -1 ) + Error( "Can not find file %s", s.device_name ); + + if ( (device_stat.st_mode & S_IFMT) != S_IFBLK && + (device_stat.st_mode & S_IFMT) != S_IFREG ) + Error( "Can only edit block special or regular files" ); + + + if ( (s.device_d = open( s.device_name, s.device_mode )) == -1 ) + Error( "Can not open %s", s.device_name ); + + if ( (size = lseek( s.device_d, 0L, SEEK_END )) == -1 ) + Error( "Error seeking %s", s.device_name ); + + if ( size % K != 0 ) + { + Warning( "Device size is not a multiple of 1024" ); + Warning( "The (partial) last block will not be accessible" ); + } + } + + + /* Initialize the rest of the state record */ + + s.mode = WORD; + s.output_base = 10; + s.search_string[ 0 ] = '\0'; + + { + int i; + + for ( i = 0; i < MAX_PREV; ++i ) + { + s.prev_addr[ i ] = 0L; + s.prev_mode[ i ] = WORD; + } + } + + + sync(); + + Read_Super_Block( &s ); + + Read_Bit_Maps( &s ); + + s.address = 0L; + + + + /* Recover mode basically performs an 'x' and an 'X' */ + + if ( recover ) + { + ino_t inode = Find_Deleted_Entry( &s, argv[1] ); + off_t size; + + if ( inode == 0 ) + { + unlink( s.file_name ); + Error( "Recover aborted" ); + } + + s.address = ( (long) s.first_data - s.inode_blocks ) * K + + (long) (inode - 1) * s.inode_size; + + Read_Block( &s, s.buffer ); + + + /* Have found the lost i-node, now extract the blocks. */ + + if ( (size = Recover_Blocks( &s )) == -1L ) + { + unlink( s.file_name ); + Error( "Recover aborted" ); + } + + Reset_Term(); + + printf( "Recovered %ld bytes, written to file %s\n", size, s.file_name ); + + exit( 0 ); + } + + + /* Enter the main loop, first time redraw the screen */ + { + int rc = REDRAW; + + + do + { + if ( rc == REDRAW ) + { + Read_Block( &s, s.buffer ); + Draw_Screen( &s ); + s.last_addr = s.address; + Draw_Pointers( &s ); + } + + else if ( rc == REDRAW_POINTERS ) + { + s.offset = (unsigned) (s.address & ~ K_MASK); + Draw_Pointers( &s ); + } + + else if ( rc == ERROR ) + { + Erase_Prompt(); + putchar( BELL ); + } + } while ( (rc = Process( &s, Arrow_Esc(Get_Char()) )) != EOF ); + } + + + /* If there is an open output file that was never written to */ + /* then remove its directory entry. This occurs when no 'w' */ + /* or 'W' command occurred between a 'c' command and exiting */ + /* the program. */ + + if ( s.file_name[0] != '\0' && ! s.file_written ) + unlink( s.file_name ); + + + Reset_Term(); /* Restore terminal characteristics */ + + exit( 0 ); + } + + + +/****************************************************************/ +/* */ +/* Get_Base( base ) */ +/* */ +/* Get a new base value. */ +/* Returns REDRAW or ERROR. */ +/* */ +/****************************************************************/ + + + +int Get_Base( base ) + int *base; + { + switch ( Get_Char() ) + { + case 'h' : *base = 16; + break; + + case 'd' : *base = 10; + break; + + case 'o' : *base = 8; + break; + + case 'b' : *base = 2; + break; + + default : return( ERROR ); + } + + return( REDRAW ); + } + + + +/****************************************************************/ +/* */ +/* Process( state, input_char ) */ +/* */ +/* Determine the function requested by the */ +/* input character. Returns OK, REDRAW, */ +/* REDRAW_POINTERS, ERROR or EOF. */ +/* */ +/****************************************************************/ + + +int Process( s, c ) + de_state *s; + int c; + + { + switch ( c ) + { + case 'b' : /* Back up one block */ + case ESC_PGUP : + + if ( s->address == 0 ) + return( ERROR ); + + s->address = (s->address - K) & K_MASK; + + return( REDRAW ); + + + case 'B' : /* Back up to home */ + case ESC_HOME : + + if ( s->address == 0 ) + return( OK ); + + Push( s ); + + s->address = 0L; + + return( REDRAW ); + + + case 'c' : /* Change file name */ + + { + int rc = Get_Filename( s ); + + return( rc == OK ? REDRAW : rc ); + } + + + case 'd' : /* Down */ + case ESC_DOWN : + + { + s->last_addr = s->address; + + switch ( s->mode ) + { + case WORD : s->address += 2; + + if ( (s->address & PAGE_MASK) == 0 ) + return( REDRAW ); + + return( REDRAW_POINTERS ); + + case BLOCK : s->address += 64; + + if ( (s->last_addr & K_MASK) != + (s->address & K_MASK) ) + return( REDRAW ); + + return( REDRAW_POINTERS ); + + case MAP : s->address += 256; + + return( REDRAW ); + + default : Error( "Internal fault (mode)" ); + } + } + + + case 'f' : /* Forward one block */ + case ' ' : + case ESC_PGDN : + + if ( s->block == s->device_size - 1 ) + return( ERROR ); + + s->address = (s->address + K) & K_MASK; + + return( REDRAW ); + + + case 'F' : /* Forward to end */ + case ESC_END : + + { + off_t last_block = ( (long) s->device_size - 1 ) * K; + + if ( s->address == last_block ) + return( OK ); + + Push( s ); + + s->address = last_block; + + return( REDRAW ); + } + + + case 'g' : /* Goto block */ + + { + unsigned long block; + + if ( Get_Count( "Block?", &block ) ) + { + if ( block >= s->zones ) + { + Warning( "Block number too large" ); + return( REDRAW ); + } + + Push( s ); + + s->address = (off_t) block * K; + + return( REDRAW ); + } + else + return( ERROR ); + } + + + case 'G' : /* Goto block indirect */ + + { + unsigned block = *( (word_t *) &s->buffer[ s->offset ] ); + + if ( s->mode != WORD ) + { + Warning( "Must be in visual mode \"word\"" ); + return( REDRAW ); + } + + if ( block >= s->zones ) + { + Warning( "Block number too large" ); + return( REDRAW ); + } + + Push( s ); + + s->mode = BLOCK; + s->address = (long) block * K; + + return( REDRAW ); + } + + + case 'h' : /* Help */ + case '?' : + + Draw_Help_Screen( s ); + + Wait_For_Key(); + + return( REDRAW ); + + + case 'i' : /* Goto i-node */ + + { + unsigned long inode; + + if ( Get_Count( "I-node?", &inode ) ) + { + if ( inode < 1 || inode > s->inodes ) + { + Warning( "Illegal i-node number" ); + return( REDRAW ); + } + + Push( s ); + + s->mode = WORD; + s->address = (off_t) (s->first_data - s->inode_blocks) * K + + (off_t) (inode - 1) * s->inode_size; + + return( REDRAW ); + } + else + return( ERROR ); + } + + + case 'I' : /* Filename to i-node */ + + { + ino_t inode; + char *filename; + + Draw_Prompt( "File name?" ); + + filename = Get_Line(); + + if ( filename == NULL || filename[0] == '\0' ) + return( ERROR ); + + inode = Find_Inode( s, filename ); + + if ( inode ) + { + Push( s ); + + s->mode = WORD; + s->address = ( (long) s->first_data - s->inode_blocks ) * K + + (long) (inode - 1) * s->inode_size; + } + + return( REDRAW ); + } + + + case 'l' : /* Left */ + case ESC_LEFT : + + { + s->last_addr = s->address; + + switch ( s->mode ) + { + case WORD : s->address = s->address - 32; + + return( REDRAW ); + + case BLOCK : s->address -= 1; + + if ( (s->last_addr & K_MASK) != + (s->address & K_MASK) ) + return( REDRAW ); + + return( REDRAW_POINTERS ); + + case MAP : s->address -= 4; + + if ( (s->last_addr & ~ MAP_MASK) != + (s->address & ~ MAP_MASK) ) + return( REDRAW ); + + return( REDRAW_POINTERS ); + + default : Error( "Internal fault (mode)" ); + } + } + + + case 'm' : /* Invoke a Minix shell */ + + Reset_Term(); + + Exec_Shell(); + + Set_Term(); + + return( REDRAW ); + + + case 'n' : /* Search for next */ + + { + off_t addr; + + if ( s->search_string[0] == '\0' ) + { + Warning( "No search string defined" ); + return( REDRAW ); + } + + Draw_Prompt( "Searching..." ); + + if ( (addr = Search( s, s->search_string )) == -1L ) + { + Warning( "Search string not found" ); + + Wait_For_Key(); + + return( REDRAW ); + } + + Push( s ); + s->address = addr; + + return( REDRAW ); + } + + + case 'o' : /* Set output base */ + + Draw_Prompt( "Output base?" ); + + return( Get_Base( &s->output_base ) ); + + + case 'p' : /* Previous address */ + + { + int i; + + s->address = s->prev_addr[ 0 ]; + s->mode = s->prev_mode[ 0 ]; + + for ( i = 0; i < MAX_PREV - 1; ++i ) + { + s->prev_addr[ i ] = s->prev_addr[ i + 1 ]; + s->prev_mode[ i ] = s->prev_mode[ i + 1 ]; + } + + return( REDRAW ); + } + + + case 'q' : /* Quit */ + case EOF : + case CTRL_D : + + return( EOF ); + + + case 'r' : /* Right */ + case ESC_RIGHT : + + { + s->last_addr = s->address; + + switch ( s->mode ) + { + case WORD : s->address += 32; + + return( REDRAW ); + + case BLOCK : s->address += 1; + + if ( (s->last_addr & K_MASK) != + (s->address & K_MASK) ) + return( REDRAW ); + + return( REDRAW_POINTERS ); + + case MAP : s->address += 4; + + if ( (s->last_addr & ~ MAP_MASK) != + (s->address & ~ MAP_MASK) ) + return( REDRAW ); + + return( REDRAW_POINTERS ); + + default : Error( "Internal fault (mode)" ); + } + } + + case 's' : /* Store word */ + + { + unsigned long word; + + if ( s->mode != WORD ) + { + Warning( "Must be in visual mode \"word\"" ); + return( REDRAW ); + } + + if ( s->device_mode == O_RDONLY ) + { + Warning( "Use -w option to open device for writing" ); + return( REDRAW ); + } + + if ( Get_Count( "Store word?", &word ) ) + { + if ( word != (word_t) word ) + { + Warning( "Word is more than 16 bits" ); + return( REDRAW ); + } + Write_Word( s, (word_t) word ); + + return( REDRAW ); + } + else + return( ERROR ); + } + + + case 'u' : /* Up */ + case ESC_UP : + + { + s->last_addr = s->address; + + switch ( s->mode ) + { + case WORD : s->address -= 2; + + if ( (s->last_addr & PAGE_MASK) == 0 ) + return( REDRAW ); + + return( REDRAW_POINTERS ); + + case BLOCK : s->address -= 64; + + if ( (s->last_addr & K_MASK) != + (s->address & K_MASK) ) + return( REDRAW ); + + return( REDRAW_POINTERS ); + + case MAP : s->address -= 256; + + return( REDRAW ); + + default : Error( "Internal fault (mode)" ); + } + } + + + case 'v' : /* Visual mode */ + + Draw_Prompt( "Visual mode?" ); + + switch ( Get_Char() ) + { + case 'w' : s->mode = WORD; + break; + + case 'b' : s->mode = BLOCK; + break; + + case 'm' : { + /* Assume user knows if map mode is possible + char *tty = ttyname( 0 ); + + if ( tty == NULL || + strcmp( tty, "/dev/tty0" ) != 0 ) + Warning( "Must be at console" ); + else + */ + s->mode = MAP; + + break; + } + + default : return( ERROR ); + } + + return( REDRAW ); + + + case 'w' : /* Write ASCII block */ + + if ( s->file_name[0] == '\0' ) + { + int rc = Get_Filename( s ); + + if ( rc != OK ) + return( rc ); + } + + /* We have a successfully opened file */ + + /* Eliminate non-ASCII characters */ + { + int i; + char buf[ K ]; + char *from = s->buffer; + char *to = buf; + + for ( i = 0; i < K; ++i, ++from ) + { + *to = *from & 0x7f; + + if ( *to != '\0' && *to != '\177' ) + ++to; + } + + if ( fwrite( buf, 1, (int)(to - buf), s->file_f ) != to - buf ) + Warning( "Problem writing out buffer" ); + + s->file_written = 1; + + return( REDRAW ); + } + + + case 'W' : /* Write block exactly */ + + if ( s->file_name[0] == '\0' ) + { + int rc = Get_Filename( s ); + + if ( rc != OK ) + return( rc ); + } + + /* We have a successfully opened file */ + + if ( fwrite( s->buffer, 1, K, s->file_f ) != K ) + Warning( "Problem writing out buffer" ); + + s->file_written = 1; + + return( REDRAW ); + + + case 'x' : /* eXtract lost entry */ + + { + ino_t inode; + char *filename; + + Draw_Prompt( "Lost file name?" ); + + filename = Get_Line(); + + if ( filename == NULL || filename[0] == '\0' ) + return( ERROR ); + + inode = Find_Deleted_Entry( s, filename ); + + if ( inode ) + { + Push( s ); + + s->mode = WORD; + s->address = ( (long) s->first_data - s->inode_blocks ) * K + + (long) (inode - 1) * s->inode_size; + } + + return( REDRAW ); + } + + + case 'X' : /* eXtract lost blocks */ + + { + int rc; + + if ( s->mode != WORD ) + { + Warning( "Must be in visual mode \"word\"" ); + return( REDRAW ); + } + + + /* Force a new output file name. */ + + if ( (rc = Get_Filename( s )) != OK ) + return( rc ); + + + Draw_Strings( s ); + + Erase_Prompt(); + Draw_Prompt( "Recovering..." ); + + if ( Recover_Blocks( s ) == -1L ) + unlink( s->file_name ); + + /* Force closure of output file. */ + + fclose( s->file_f ); + s->file_name[ 0 ] = '\0'; + + return( REDRAW ); + } + + + case '/' : /* Search */ + case ESC_PLUS : + + { + off_t addr; + char *string; + + Draw_Prompt( "Search string?" ); + + string = Get_Line(); + + if ( string == NULL ) + return( ERROR ); + + if ( string[0] != '\0' ) + { + strcpy( s->search_string, string ); + Draw_Strings( s ); + } + + else if ( s->search_string[0] == '\0' ) + { + Warning( "No search string defined" ); + return( REDRAW ); + } + + Erase_Prompt(); + Draw_Prompt( "Searching..." ); + + if ( (addr = Search( s, s->search_string )) == -1L ) + { + Warning( "Search string not found" ); + + Wait_For_Key(); + + return( REDRAW ); + } + + Push( s ); + + s->mode = BLOCK; + s->address = addr; + + return( REDRAW ); + } + + + default: + return( ERROR ); + } + } + + + + + + +/****************************************************************/ +/* */ +/* Push( state ) */ +/* */ +/* Push current address and mode, used by the */ +/* commands B, F, g, G, i, I, n, x and /. This */ +/* information is popped by the 'p' command. */ +/* */ +/****************************************************************/ + + +void Push( s ) + de_state *s; + + { + int i; + + for ( i = MAX_PREV - 1; i > 0; --i ) + { + s->prev_addr[ i ] = s->prev_addr[ i - 1 ]; + s->prev_mode[ i ] = s->prev_mode[ i - 1 ]; + } + + s->prev_addr[ 0 ] = s->address; + s->prev_mode[ 0 ] = s->mode; + } + + + + + + +/****************************************************************/ +/* */ +/* Get_Filename( state ) */ +/* */ +/* Read and check a filename. */ +/* */ +/****************************************************************/ + + +int Get_Filename( s ) + de_state *s; + + { + char *filename; + char *name; + FILE *f; + + Draw_Prompt( "File name?" ); + + filename = Get_Line(); + + if ( filename == NULL || filename[0] == '\0' ) + return( ERROR ); + + + for ( name = filename; *name != '\0'; ++name ) + if ( ! isgraph( *name ) ) + { + Warning( "File name contains non-graphic characters" ); + return( REDRAW ); + } + + + if ( access( filename, F_OK ) == 0 ) + { + Warning( "Will not overwrite file %s", filename ); + return( REDRAW ); + } + + if ( (f = fopen( filename, "w" )) == NULL ) + { + Warning( "Can not open file %s", filename ); + return( REDRAW ); + } + + /* If there is already an open output file then */ + /* close it. If it was never written to then */ + /* remove its directory entry. */ + + if ( s->file_name[0] != '\0' ) + { + if ( ! s->file_written ) + unlink( s->file_name ); + + fclose( s->file_f ); + } + + strcpy( s->file_name, filename ); + s->file_f = f; + s->file_written = 0; + + return( OK ); + } + + + + + + +/****************************************************************/ +/* */ +/* Get_Count() */ +/* */ +/* Read and check a number. Returns non-zero */ +/* if successful. */ +/* */ +/****************************************************************/ + + +int Get_Count( units, result ) + char *units; + unsigned long *result; + + { + char *number; + + Draw_Prompt( units ); + + number = Get_Line(); + + if ( number == NULL || number[0] == '\0' ) + return( 0 ); + + errno = 0; + *result = strtoul( number, (char **) NULL, 0 ); + return( errno == 0 ); + } + + + + + + +/****************************************************************/ +/* */ +/* In_Use( bit, map ) */ +/* */ +/* Is the bit set in the map? */ +/* */ +/****************************************************************/ + + +int In_Use( bit, map ) + bit_t bit; + bitchunk_t *map; + + { + return( map[ (int) (bit / (CHAR_BIT * sizeof (bitchunk_t))) ] & + (1 << ((unsigned) bit % (CHAR_BIT * sizeof (bitchunk_t)))) ); + } + + + + + + +/****************************************************************/ +/* */ +/* Find_Inode( state, filename ) */ +/* */ +/* Find the i-node for the given file name. */ +/* */ +/****************************************************************/ + + +ino_t Find_Inode( s, filename ) + de_state *s; + char *filename; + + { + struct stat device_stat; + struct stat file_stat; + ino_t inode; + + + if ( fstat( s->device_d, &device_stat ) == -1 ) + Error( "Can not fstat(2) file system device" ); + +#ifdef S_IFLNK + if ( lstat( filename, &file_stat ) == -1 ) +#else + if ( stat( filename, &file_stat ) == -1 ) +#endif + { + Warning( "Can not find file %s", filename ); + return( 0 ); + } + + if ( device_stat.st_rdev != file_stat.st_dev ) + { + Warning( "File is not on device %s", s->device_name ); + return( 0 ); + } + + + inode = file_stat.st_ino; + + if ( inode < 1 || inode > s->inodes ) + { + Warning( "Illegal i-node number" ); + return( 0 ); + } + + return( inode ); + } + + + + + + +/****************************************************************/ +/* */ +/* Exec_Shell() */ +/* */ +/* Fork off a sub-process to exec() the shell. */ +/* */ +/****************************************************************/ + + +void Exec_Shell() + + { + int pid = fork(); + + if ( pid == -1 ) + return; + + + if ( pid == 0 ) + { + /* The child process */ + + extern char **environ; + char *shell = getenv( "SHELL" ); + + if ( shell == NULL ) + shell = "/bin/sh"; + + execle( shell, shell, (char *) 0, environ ); + + perror( shell ); + exit( 127 ); + } + + + /* The parent process: ignore signals, wait for sub-process */ + + signal( SIGINT, SIG_IGN ); + signal( SIGQUIT, SIG_IGN ); + + { + int status; + int w; + + while ( (w=wait(&status)) != pid && w != -1 ); + } + + signal( SIGINT, Sigint ); + signal( SIGQUIT, Sigint ); + + return; + } + + + + + + +/****************************************************************/ +/* */ +/* Sigint() */ +/* */ +/* Terminate the program on an interrupt (^C) */ +/* or quit (^\) signal. */ +/* */ +/****************************************************************/ + + +void Sigint(n) +int n; + { + Reset_Term(); /* Restore terminal characteristics */ + + putchar( '\n' ); + + exit( 1 ); + } + + + + + + +/****************************************************************/ +/* */ +/* Error( text, ... ) */ +/* */ +/* Print an error message on stderr. */ +/* */ +/****************************************************************/ + + +#if __STDC__ +void Error( const char *text, ... ) +#else +void Error( text ) + char *text; +#endif + + { + va_list argp; + + Reset_Term(); + + fprintf( stderr, "\nde: " ); + va_start( argp, text ); + vfprintf( stderr, text, argp ); + va_end( argp ); + if ( errno != 0 ) + fprintf( stderr, ": %s", strerror( errno ) ); + fprintf( stderr, "\n" ); + + exit( 1 ); + } diff --git a/commands/de/de.h b/commands/de/de.h new file mode 100755 index 000000000..f6c75951e --- /dev/null +++ b/commands/de/de.h @@ -0,0 +1,361 @@ +/****************************************************************/ +/* */ +/* de.h */ +/* */ +/* Definitions for the "Disk editor". */ +/* */ +/****************************************************************/ +/* origination 1989-Jan-15 Terrence W. Holm */ +/****************************************************************/ + + +/****************************************************************/ +/* */ +/* de(1) */ +/* */ +/* This is the MINIX disk editor. It allows the user to */ +/* observe and modify a file system. It can also be used */ +/* to recover unlink(2)'ed files */ +/* */ +/* See the de(1) man page. */ +/* */ +/****************************************************************/ + + +/****************************************************************/ +/* */ +/* de Copyright Terrence W. Holm 1989 */ +/* */ +/* This program was written for users of the Minix operating */ +/* system, and in the spirit of other public domain software */ +/* written for said system, this source code is made available */ +/* at no cost to everyone. I assume no responsibility for */ +/* damage to file systems caused by this program. */ +/* */ +/* This program (one .h, five .c's and a "man" page) may be */ +/* copied and/or modified subject to (1) no charge must be */ +/* made for distribution, other than for the medium, (2) all */ +/* modified sources must be clearly marked as such, (3) all */ +/* sources must carry this copyright. */ +/* */ +/****************************************************************/ + + +/****************************************************************/ +/* */ +/* files */ +/* */ +/* de.h Definitions */ +/* de.c The main loop */ +/* de_stdin.c Character input routines */ +/* de_stdout.c Output routines */ +/* de_diskio.c File system read/write */ +/* de_recover.c File restoration routines */ +/* */ +/* de.1 "Man" page */ +/* Makefile For "make" */ +/* README Installation help */ +/* */ +/* */ +/* fs/path.c was modified to support the 'x' command. */ +/* fs/link.c and fs/open.c were changed for 'X'. */ +/* */ +/****************************************************************/ +#undef printf +#include + +/* General constants */ + +#define MAX_STRING 60 /* For all input lines */ +#define MAX_PREV 8 /* For 'p' command */ +#define SEARCH_BUFFER (4*K) /* For '/' and 'n' */ + + +/* Files */ + +#define TMP "/tmp" /* For "-r" output */ +#define DEV "/dev" /* Where devices are */ + + +/* a.out header constants (see a.out.h, if you have it) */ + +#if (CHIP == INTEL) +#define A_OUT 0x0301 +#define SPLIT 0x0420 +#endif + +#if (CHIP == M68000) +#define A_OUT 0x0301 +#define SPLIT 0x0B20 +#endif + +#if (CHIP == SPARC) +#define A_OUT 0x0301 +#define SPLIT 0x0B20 +#endif + +/* Each buffer is 1k. In WORD mode 16 words (32 bytes) can be */ +/* displayed at once. In BLOCK mode 1K bytes can be displayed. */ +/* In MAP mode 2048 bits (256 bytes) are displayed. */ + +#define K 1024 /* STD_BLK */ +#define K_MASK (~(K-1)) /* Round to K boundary */ +#define K_SHIFT 10 /* Ie. 1<<10 = K */ +#define PAGE_MASK 0x1f /* Word mode: 32 bytes */ +#define PAGE_SHIFT 5 /* Ie. 1<<5 = 32 */ +#define MAP_BITS_PER_BLOCK (8 * K) /* 1k block, 8192 bits */ +#define MAP_MASK 0xff /* 256 bytes/screen */ + + + +/* Terminal i/o codes */ + +#define CTRL_D '\004' /* ASCII ^D */ +#define BELL '\007' /* ASCII bell code */ +#define BS '\010' /* ASCII back space */ +#define CTRL_U '\025' /* ASCII ^U */ +#define ESCAPE '\033' /* ASCII escape code */ +#define DEL '\177' /* ASCII delete code */ + + +/* Input escape codes generated by the Minix console. */ +/* Format: ESC [ X. */ + +#define ESC_HOME ('H' + 0x80) +#define ESC_UP ('A' + 0x80) +#define ESC_PGUP ('V' + 0x80) +#define ESC_LEFT ('D' + 0x80) +#define ESC_5 ('G' + 0x80) +#define ESC_RIGHT ('C' + 0x80) +#define ESC_END ('Y' + 0x80) +#define ESC_DOWN ('B' + 0x80) +#define ESC_PGDN ('U' + 0x80) +#define ESC_PLUS ('T' + 0x80) +#define ESC_MINUS ('S' + 0x80) + + +/* Graphic box codes - only applicable for console display */ +/* in visual mode "map". */ + +#if (CHIP == INTEL) +#define BOX_CLR ' ' /* Empty box */ +#define BOX_ALL '\333' /* Filled box */ +#define BOX_TOP '\337' /* Filled upper half */ +#define BOX_BOT '\334' /* Filled lower half */ +#endif + +#if (CHIP == M68000) +/* Please change these. */ +#define BOX_CLR ' ' /* Empty box */ +#define BOX_ALL '=' /* Filled box */ +#define BOX_TOP '-' /* Filled upper half */ +#define BOX_BOT '_' /* Filled lower half */ +#endif + +#if (CHIP == SPARC) +/* Please change these. */ +#define BOX_CLR ' ' /* Empty box */ +#define BOX_ALL '=' /* Filled box */ +#define BOX_TOP '-' /* Filled upper half */ +#define BOX_BOT '_' /* Filled lower half */ +#endif + +/* Move positions for the output display. */ + +#define STATUS_COLUMN 2 +#define STATUS_LINE 0 +#define BLOCK_COLUMN 4 +#define BLOCK_LINE 4 +#define INFO_COLUMN 30 +#define INFO_LINE BLOCK_LINE +#define PROMPT_COLUMN 0 +#define PROMPT_LINE 23 +#define WARNING_COLUMN 5 +#define WARNING_LINE 10 + + + +/* Values returned by Process() and Get_Filename() */ + +#define OK 0 /* No update required */ +#define REDRAW 1 /* Redraw whole screen */ +#define REDRAW_POINTERS 2 /* Redraw just ptrs */ +#define ERROR 3 /* Beep */ + + +/* Visual modes */ + +#define WORD 1 +#define BLOCK 2 +#define MAP 3 + +typedef unsigned short word_t; /* For most user i/o */ +#if _WORD_SIZE == 2 +typedef unsigned int Word_t; /* What it should always be */ +#else +typedef int Word_t; /* Unsigned promotion under ANSI C */ +#endif + +#ifndef I_MAP_SLOTS +/* Max number of inode and zone map blocks we can handle. */ +#define I_MAP_SLOTS 8 +#define Z_MAP_SLOTS (sizeof(char *) == 2 ? 16 : 128) +#endif + +typedef struct de_state /* State of disk ed. */ + { + /* Information from super block */ + /* The types here are mostly promoted types for simplicity */ + /* and efficiency. */ + + unsigned inodes; /* Number of i-nodes */ + zone_t zones; /* Total # of blocks */ + unsigned inode_maps; /* I-node map blocks */ + unsigned zone_maps; /* Zone map blocks */ + unsigned inode_blocks; /* I-node blocks */ + unsigned first_data; /* Total non-data blks */ + int magic; /* Magic number */ + + /* Numbers derived from the magic number */ + + unsigned char is_fs; /* Nonzero for good fs */ + unsigned char v1; /* Nonzero for V1 fs */ + unsigned inode_size; /* Size of disk inode */ + unsigned nr_indirects; /* # indirect blocks */ + unsigned zone_num_size; /* Size of disk z num */ + + /* Other derived numbers */ + + bit_t inodes_in_map; /* Bits in i-node map */ + bit_t zones_in_map; /* Bits in zone map */ + int ndzones; /* # direct zones in an inode */ + + /* Information from map blocks */ + + bitchunk_t inode_map[ I_MAP_SLOTS * K / sizeof (bitchunk_t) ]; + bitchunk_t zone_map[ Z_MAP_SLOTS * K / sizeof (bitchunk_t) ]; + + /* Information for current block */ + + off_t address; /* Current address */ + off_t last_addr; /* For erasing ptrs */ + zone_t block; /* Current block (1K) */ + unsigned offset; /* Offset within block */ + + char buffer[ K ]; + + /* Display state */ + + int mode; /* WORD, BLOCK or MAP */ + int output_base; /* 2, 8, 10, or 16 */ + + /* Search information */ + + char search_string[ MAX_STRING + 1 ]; /* For '/' and 'n' */ + off_t prev_addr[ MAX_PREV ]; /* For 'p' command */ + int prev_mode[ MAX_PREV ]; + + /* File information */ + + char *device_name; /* From command line */ + int device_d; + int device_mode; /* O_RDONLY or O_RDWR */ + zone_t device_size; /* Number of blocks */ + + char file_name[ MAX_STRING + 1 ]; /* For 'w' and 'W' */ + FILE *file_f; + int file_written; /* Flag if written to */ + + } de_state; + + + +/* Forward references for external routines */ + +/* de.c */ + +_PROTOTYPE(void main , (int argc , char *argv [])); +_PROTOTYPE(int Process , (de_state *s , int c )); + +#if __STDC__ +void Error( const char *text, ... ); +#else +void Error(); +#endif + +_PROTOTYPE(int In_Use , (bit_t bit , bitchunk_t *map )); +_PROTOTYPE(ino_t Find_Inode , (de_state *s , char *filename )); + + +/* de_stdin.c */ + +_PROTOTYPE(void Save_Term , (void)); +_PROTOTYPE(void Set_Term , (void)); +_PROTOTYPE(void Reset_Term , (void)); +_PROTOTYPE(int Get_Char , (void)); +_PROTOTYPE(char *Get_Line , (void)); +_PROTOTYPE(int Arrow_Esc , (int c )); + +/* de_stdout.c */ + +_PROTOTYPE(int Init_Termcap , (void)); +_PROTOTYPE(void Draw_Help_Screen , (de_state *s )); +_PROTOTYPE(void Wait_For_Key , (void)); +_PROTOTYPE(void Draw_Prompt , (char *string )); +_PROTOTYPE(void Erase_Prompt , (void)); + +_PROTOTYPE(void Draw_Screen , (de_state *s )); +_PROTOTYPE(void Draw_Strings , (de_state *s )); +_PROTOTYPE(void Draw_Pointers , (de_state *s )); +_PROTOTYPE(void Print_Ascii , (int c )); + +_PROTOTYPE(void Goto , (int column , int line )); +_PROTOTYPE(void Block_Type , (de_state *s )); +_PROTOTYPE(void Draw_Words , (de_state *s )); +_PROTOTYPE(void Draw_Info , (de_state *s )); +_PROTOTYPE(void Draw_Block , (char *block )); +_PROTOTYPE(void Draw_Map , (char *block , int max_bits )); +_PROTOTYPE(void Draw_Offset , (de_state *s )); +_PROTOTYPE(void Word_Pointers , (off_t old_addr , off_t new_addr )); +_PROTOTYPE(void Block_Pointers , (off_t old_addr , off_t new_addr )); +_PROTOTYPE(void Map_Pointers , (off_t old_addr , off_t new_addr )); +_PROTOTYPE(void Print_Number , (Word_t number , int output_base )); +_PROTOTYPE(void Draw_Zone_Numbers , (de_state *s , struct inode *inode , + int zindex , int zrow )); + +#if __STDC__ +void Warning( const char *text, ... ); +#else +void Warning(); +#endif + + +/* de_diskio.c */ + +_PROTOTYPE(void Read_Disk , (de_state *s , off_t block_addr , char *buffer )); +_PROTOTYPE(void Read_Block , (de_state *s , char *buffer )); +_PROTOTYPE(void Read_Super_Block , (de_state *s )); +_PROTOTYPE(void Read_Bit_Maps , (de_state *s )); +_PROTOTYPE(off_t Search , (de_state *s , char *string )); +_PROTOTYPE(void Write_Word , (de_state *s , Word_t word )); + + +/* de_recover.c */ + +_PROTOTYPE(int Path_Dir_File , (char *path_name , char **dir_name , + char **file_name )); +_PROTOTYPE(char *File_Device , (char *file_name )); +_PROTOTYPE(ino_t Find_Deleted_Entry , (de_state *s , char *path_name )); +_PROTOTYPE(off_t Recover_Blocks , (de_state *s )); + + +#undef printf /* Because fs/const.h */ + /* defines it. */ + + +/* Static functions are all pre-declared FORWARD but none are */ +/* declared static yet - this can wait until all functions are */ +/* declared with prototypes. */ + +#undef FORWARD +#define FORWARD /* static */ diff --git a/commands/de/de_diskio.c b/commands/de/de_diskio.c new file mode 100755 index 000000000..09e462ea6 --- /dev/null +++ b/commands/de/de_diskio.c @@ -0,0 +1,345 @@ +/****************************************************************/ +/* */ +/* de_diskio.c */ +/* */ +/* Reading and writing to a file system device. */ +/* */ +/****************************************************************/ +/* origination 1989-Jan-15 Terrence W. Holm */ +/****************************************************************/ + + +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../../servers/fs/const.h" +#include "../../servers/fs/type.h" +#include "../../servers/fs/super.h" +#include "../../servers/fs/inode.h" +#include + +#include "de.h" + + + + +/****************************************************************/ +/* */ +/* Read_Disk( state, block_addr, buffer ) */ +/* */ +/* Reads a 1k block at "block_addr" into "buffer". */ +/* */ +/****************************************************************/ + + +void Read_Disk( s, block_addr, buffer ) + de_state *s; + off_t block_addr; + char *buffer; + + { + if ( lseek( s->device_d, block_addr, SEEK_SET ) == -1 ) + Error( "Error seeking %s", s->device_name ); + + if ( read( s->device_d, buffer, K ) != K ) + Error( "Error reading %s", s->device_name ); + } + + + + + + +/****************************************************************/ +/* */ +/* Read_Block( state, buffer ) */ +/* */ +/* Reads a 1k block from "state->address" into */ +/* "buffer". Checks "address", and updates */ +/* "block" and "offset". */ +/* */ +/****************************************************************/ + + +void Read_Block( s, buffer ) + de_state *s; + char *buffer; + + { + off_t end_addr = (long) s->device_size * K - 1; + off_t block_addr; + + if ( s->address < 0 ) + s->address = 0L; + + if ( s->address > end_addr ) + s->address = end_addr; + + /* The address must be rounded off for */ + /* certain visual display modes. */ + + if ( s->mode == WORD ) + s->address &= ~1L; + else if ( s->mode == MAP ) + s->address &= ~3L; + + + block_addr = s->address & K_MASK; + + s->block = (zone_t) (block_addr >> K_SHIFT); + s->offset = (unsigned) (s->address - block_addr); + + Read_Disk( s, block_addr, buffer ); + } + + + + + + +/****************************************************************/ +/* */ +/* Read_Super_Block( state ) */ +/* */ +/* Read and check the super block. */ +/* */ +/****************************************************************/ + + +void Read_Super_Block( s ) + de_state *s; + + { + struct super_block *super = (struct super_block *) s->buffer; + unsigned inodes_per_block; + off_t size; + + Read_Disk( s, (long) 1 * K, s->buffer ); + + s->magic = super->s_magic; + if ( s->magic == SUPER_MAGIC ) + { + s->is_fs = TRUE; + s->v1 = TRUE; + s->inode_size = V1_INODE_SIZE; + inodes_per_block = V1_INODES_PER_BLOCK; + s->nr_indirects = V1_INDIRECTS; + s->zone_num_size = V1_ZONE_NUM_SIZE; + s->zones = super->s_nzones; + s->ndzones = V1_NR_DZONES; + } + else if ( s->magic == SUPER_V2 ) + { + s->is_fs = TRUE; + s->v1 = FALSE; + s->inode_size = V2_INODE_SIZE; + inodes_per_block = V2_INODES_PER_BLOCK(STATIC_BLOCK_SIZE); + s->nr_indirects = V2_INDIRECTS(STATIC_BLOCK_SIZE); + s->zone_num_size = V2_ZONE_NUM_SIZE; + s->zones = super->s_zones; + s->ndzones = V2_NR_DZONES; + } + else + { + if ( super->s_magic == SUPER_REV ) + Warning( "V1-bytes-swapped file system (?)" ); + else if ( super->s_magic == SUPER_V2_REV ) + Warning( "V2-bytes-swapped file system (?)" ); + else + Warning( "Not a Minix file system" ); + Warning( "The file system features will not be available" ); + s->zones = 100000L; + return; + } + + s->inodes = super->s_ninodes; + s->inode_maps = bitmapsize( (bit_t) s->inodes + 1 , STATIC_BLOCK_SIZE); + if ( s->inode_maps != super->s_imap_blocks ) + { + if ( s->inode_maps > super->s_imap_blocks ) + Error( "Corrupted inode map count or inode count in super block" ); + else + Warning( "Count of inode map blocks in super block suspiciously high" ); + s->inode_maps = super->s_imap_blocks; + } + + s->zone_maps = bitmapsize( (bit_t) s->zones , STATIC_BLOCK_SIZE); + if ( s->zone_maps != super->s_zmap_blocks ) + { + if ( s->zone_maps > super->s_zmap_blocks ) + Error( "Corrupted zone map count or zone count in super block" ); + else + Warning( "Count of zone map blocks in super block suspiciously high" ); + s->zone_maps = super->s_zmap_blocks; + } + + s->inode_blocks = (s->inodes + inodes_per_block - 1) / inodes_per_block; + s->first_data = 2 + s->inode_maps + s->zone_maps + s->inode_blocks; + if ( s->first_data != super->s_firstdatazone ) + { + if ( s->first_data > super->s_firstdatazone ) + Error( "Corrupted first data zone offset or inode count in super block" ); + else + Warning( "First data zone in super block suspiciously high" ); + s->first_data = super->s_firstdatazone; + } + + s->inodes_in_map = s->inodes + 1; + s->zones_in_map = s->zones + 1 - s->first_data; + + /* + if ( s->zones != s->device_size ) + Warning( "Zone count does not equal device size" ); + */ + + s->device_size = s->zones; + + if ( super->s_log_zone_size != 0 ) + Error( "Can not handle multiple blocks per zone" ); + } + + + + + + +/****************************************************************/ +/* */ +/* Read_Bit_Maps( state ) */ +/* */ +/* Read in the i-node and zone bit maps from the */ +/* specified file system device. */ +/* */ +/****************************************************************/ + + +void Read_Bit_Maps( s ) + de_state *s; + + { + int i; + + if ( s->inode_maps > I_MAP_SLOTS || s->zone_maps > Z_MAP_SLOTS ) + { + Warning( "Super block specifies too many bit map blocks" ); + return; + } + + for ( i = 0; i < s->inode_maps; ++i ) + { + Read_Disk( s, (long) (2 + i) * K, + (char *) &s->inode_map[ i * K / sizeof (bitchunk_t ) ] ); + } + + for ( i = 0; i < s->zone_maps; ++i ) + { + Read_Disk( s, (long) (2 + s->inode_maps + i) * K, + (char *) &s->zone_map[ i * K / sizeof (bitchunk_t ) ] ); + } + } + + + + + + +/****************************************************************/ +/* */ +/* Search( state, string ) */ +/* */ +/* Search from the current address for the ASCII */ +/* "string" on the device. */ +/* */ +/****************************************************************/ + + +off_t Search( s, string ) + de_state *s; + char *string; + + { + off_t address = s->address + 1; + off_t last_addr = address; + char buffer[ SEARCH_BUFFER ]; + int offset; + int tail_length = strlen( string ) - 1; + int count = SEARCH_BUFFER; + int last_offset; + + + for ( ; count == SEARCH_BUFFER; address += SEARCH_BUFFER - tail_length ) + { + if ( lseek( s->device_d, address, SEEK_SET ) == -1 ) + Error( "Error seeking %s", s->device_name ); + + if ( (count = read( s->device_d, buffer, SEARCH_BUFFER)) == -1 ) + Error( "Error reading %s", s->device_name ); + + + if ( address - last_addr >= 500L * K ) + { + putchar( '.' ); + fflush( stdout ); + + last_addr += 500L * K; + } + + + last_offset = count - tail_length; + + for ( offset = 0; offset < last_offset; ++offset ) + { + register char c = buffer[ offset ]; + + if ( c == *string ) + { + char *tail_buffer = &buffer[ offset + 1 ]; + char *tail_string = string + 1; + + do + { + if ( *tail_string == '\0' ) + return( address + offset ); + } + while ( *tail_buffer++ == *tail_string++ ); + } + } /* end for ( offset ) */ + } /* end for ( address ) */ + + return( -1L ); + } + + + + + + +/****************************************************************/ +/* */ +/* Write_Word( state, word ) */ +/* */ +/* Write a word at address. */ +/* */ +/****************************************************************/ + + +void Write_Word( s, word ) + de_state *s; + word_t word; + + { + if ( s->address & 01 ) + Error( "Internal fault (unaligned address)" ); + + if ( lseek( s->device_d, s->address, SEEK_SET ) == -1 ) + Error( "Error seeking %s", s->device_name ); + + if ( write( s->device_d, (char *) &word, sizeof word ) != sizeof word ) + Error( "Error writing %s", s->device_name ); + } diff --git a/commands/de/de_recover.c b/commands/de/de_recover.c new file mode 100755 index 000000000..96ae94c41 --- /dev/null +++ b/commands/de/de_recover.c @@ -0,0 +1,612 @@ +/****************************************************************/ +/* */ +/* de_recover.c */ +/* */ +/* File restoration routines. */ +/* */ +/****************************************************************/ +/* origination 1989-Jan-21 Terrence W. Holm */ +/* handle "holes" 1989-Jan-28 Terrence W. Holm */ +/****************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../../servers/fs/const.h" +#include "../../servers/fs/type.h" +#include "../../servers/fs/inode.h" +#include + +#include "de.h" + +_PROTOTYPE(int Indirect, (de_state *s, zone_t block, off_t *size, int dblind)); +_PROTOTYPE(int Data_Block, (de_state *s, zone_t block, off_t *file_size )); +_PROTOTYPE(int Free_Block, (de_state *s, zone_t block )); + + + + +/****************************************************************/ +/* */ +/* Path_Dir_File( path_name, dir_name, file_name ) */ +/* */ +/* Split "path_name" into a directory name and */ +/* a file name. */ +/* */ +/* Zero is returned on error conditions. */ +/* */ +/****************************************************************/ + + +int Path_Dir_File( path_name, dir_name, file_name ) + char *path_name; + char **dir_name; + char **file_name; + + { + char *p; + static char directory[ MAX_STRING + 1 ]; + static char filename[ MAX_STRING + 1 ]; + + + if ( (p = strrchr( path_name, '/' )) == NULL ) + { + strcpy( directory, "." ); + strcpy( filename, path_name ); + } + else + { + *directory = '\0'; + strncat( directory, path_name, p - path_name ); + strcpy( filename, p + 1 ); + } + + if ( *directory == '\0' ) + strcpy( directory, "/" ); + + if ( *filename == '\0' ) + { + Warning( "A file name must follow the directory name" ); + return( 0 ); + } + + *dir_name = directory; + *file_name = filename; + + return( 1 ); + } + + + + + + +/****************************************************************/ +/* */ +/* File_Device( file_name ) */ +/* */ +/* Return the name of the file system device */ +/* containing the file "file_name". */ +/* */ +/* This is used if the "-r" option was specified. */ +/* In this case we have only been given a file */ +/* name, and must determine which file system */ +/* device to open. */ +/* */ +/* NULL is returned on error conditions. */ +/* */ +/****************************************************************/ + + + +char *File_Device( file_name ) + char *file_name; + + { + struct stat file_stat; + struct stat device_stat; + int dev_d; + struct direct entry; + static char device_name[ NAME_MAX + 1 ]; + + + if ( access( file_name, R_OK ) != 0 ) + { + Warning( "Can not find %s", file_name ); + return( NULL ); + } + + + if ( stat( file_name, &file_stat ) == -1 ) + { + Warning( "Can not stat(2) %s", file_name ); + return( NULL ); + } + + + /* Open /dev for reading */ + + if ( (dev_d = open( DEV, O_RDONLY )) == -1 ) + { + Warning( "Can not read %s", DEV ); + return( NULL ); + } + + + while ( read( dev_d, (char *) &entry, sizeof(struct direct) ) + == sizeof(struct direct) ) + { + if ( entry.d_ino == 0 ) + continue; + + strcpy( device_name, DEV ); + strcat( device_name, "/" ); + strncat( device_name, entry.d_name, NAME_MAX ); + + if ( stat( device_name, &device_stat ) == -1 ) + continue; + + if ( (device_stat.st_mode & S_IFMT) != S_IFBLK ) + continue; + + if ( file_stat.st_dev == device_stat.st_rdev ) + { + close( dev_d ); + return( device_name ); + } + } + + close( dev_d ); + + Warning( "The device containing file %s is not in %s", file_name, DEV ); + + return( NULL ); + } + + + + + + +/****************************************************************/ +/* */ +/* Find_Deleted_Entry( state, path_name ) */ +/* */ +/* Split "path_name" into a directory name and */ +/* a file name. Then search the directory for */ +/* an entry that would match the deleted file */ +/* name. (Deleted entries have a zero i-node */ +/* number, but the original i-node number is */ +/* placed at the end of the file name.) */ +/* */ +/* If successful an i-node number is returned, */ +/* else zero is returned. */ +/* */ +/****************************************************************/ + + +ino_t Find_Deleted_Entry( s, path_name ) + de_state *s; + char *path_name; + + { + char *dir_name; + char *file_name; + + + /* Check if the file exists */ + + if ( access( path_name, F_OK ) == 0 ) + { + Warning( "File has not been deleted" ); + return( 0 ); + } + + + /* Split the path name into a directory and a file name */ + + if ( ! Path_Dir_File( path_name, &dir_name, &file_name ) ) + return( 0 ); + + + /* Check to make sure the user has read permission on */ + /* the directory. */ + + if ( access( dir_name, R_OK ) != 0 ) + { + Warning( "Can not find %s", dir_name ); + return( 0 ); + } + + + /* Make sure "dir_name" is really a directory. */ + { + struct stat dir_stat; + + if ( stat( dir_name, &dir_stat ) == -1 || + (dir_stat.st_mode & S_IFMT) != S_IFDIR ) + { + Warning( "Can not find directory %s", dir_name ); + return( 0 ); + } + } + + + /* Make sure the directory is on the current */ + /* file system device. */ + + if ( Find_Inode( s, dir_name ) == 0 ) + return( 0 ); + + + /* Open the directory and search for the lost file name. */ + { + int dir_d; + int count; + struct direct entry; + + if ( (dir_d = open( dir_name, O_RDONLY )) == -1 ) + { + Warning( "Can not read directory %s", dir_name ); + return( 0 ); + } + + while ( (count = read( dir_d, (char *) &entry, sizeof(struct direct) )) + == sizeof(struct direct) ) + { + if ( entry.d_ino == 0 && + strncmp( file_name, entry.d_name, NAME_MAX - sizeof(ino_t) ) == 0 ) + { + ino_t inode = *( (ino_t *) &entry.d_name[ NAME_MAX - sizeof(ino_t) ] ); + + close( dir_d ); + + if ( inode < 1 || inode > s->inodes ) + { + Warning( "Illegal i-node number" ); + return( 0 ); + } + + return( inode ); + } + } + + close( dir_d ); + + if ( count == 0 ) + Warning( "Can not find a deleted entry for %s", file_name ); + else + Warning( "Problem reading directory %s", dir_name ); + + return( 0 ); + } + } + + + + + + +/****************************************************************/ +/* */ +/* Recover_Blocks( state ) */ +/* */ +/* Try to recover all the blocks for the i-node */ +/* currently pointed to by "s->address". The */ +/* i-node and all of the blocks must be marked */ +/* as FREE in the bit maps. The owner of the */ +/* i-node must match the current real user name. */ +/* */ +/* "Holes" in the original file are maintained. */ +/* This allows moving sparse files from one device */ +/* to another. */ +/* */ +/* On any error -1L is returned, otherwise the */ +/* size of the recovered file is returned. */ +/* */ +/* */ +/* NOTE: Once a user has read access to a device, */ +/* there is a security hole, as we lose the */ +/* normal file system protection. For convenience, */ +/* de(1) is sometimes set-uid root, this allows */ +/* anyone to use the "-r" option. When recovering, */ +/* Recover_Blocks() can only superficially check */ +/* the validity of a request. */ +/* */ +/****************************************************************/ + + +off_t Recover_Blocks( s ) + de_state *s; + + { + struct inode core_inode; + d1_inode *dip1; + d2_inode *dip2; + struct inode *inode = &core_inode; + bit_t node = (s->address - (s->first_data - s->inode_blocks) * K) / + s->inode_size + 1; + + dip1 = (d1_inode *) &s->buffer[ s->offset & ~ PAGE_MASK ]; + dip2 = (d2_inode *) &s->buffer[ s->offset & ~ PAGE_MASK + & ~ (V2_INODE_SIZE-1) ]; + conv_inode( inode, dip1, dip2, READING, s->magic ); + + if ( s->block < s->first_data - s->inode_blocks || + s->block >= s->first_data ) + { + Warning( "Not in an inode block" ); + return( -1L ); + } + + + /* Is this a valid, but free i-node? */ + + if ( node > s->inodes ) + { + Warning( "Not an inode" ); + return( -1L ); + } + + if ( In_Use(node, s->inode_map) ) + { + Warning( "I-node is in use" ); + return( -1L ); + } + + + /* Only recover files that belonged to the real user. */ + + { + uid_t real_uid = getuid(); + struct passwd *user = getpwuid( real_uid ); + + if ( real_uid != SU_UID && real_uid != inode->i_uid ) + { + Warning( "I-node did not belong to user %s", user ? user->pw_name : "" ); + return( -1L ); + } + } + + + /* Recover all the blocks of the file. */ + + { + off_t file_size = inode->i_size; + int i; + + + /* Up to s->ndzones pointers are stored in the i-node. */ + + for ( i = 0; i < s->ndzones; ++i ) + { + if ( file_size == 0 ) + return( inode->i_size ); + + if ( ! Data_Block( s, inode->i_zone[ i ], &file_size ) ) + return( -1L ); + } + + if ( file_size == 0 ) + return( inode->i_size ); + + + /* An indirect block can contain up to inode->i_indirects more blk ptrs. */ + + if ( ! Indirect( s, inode->i_zone[ s->ndzones ], &file_size, 0 ) ) + return( -1L ); + + if ( file_size == 0 ) + return( inode->i_size ); + + + /* A double indirect block can contain up to inode->i_indirects blk ptrs. */ + + if ( ! Indirect( s, inode->i_zone[ s->ndzones+1 ], &file_size, 1 ) ) + return( -1L ); + + if ( file_size == 0 ) + return( inode->i_size ); + + Error( "Internal fault (file_size != 0)" ); + } + + /* NOTREACHED */ + return( -1L ); + } + + + + + + +/* Indirect( state, block, &file_size, double ) + * + * Recover all the blocks pointed to by the indirect block + * "block", up to "file_size" bytes. If "double" is true, + * then "block" is a double-indirect block pointing to + * V*_INDIRECTS indirect blocks. + * + * If a "hole" is encountered, then just seek ahead in the + * output file. + */ + + +int Indirect( s, block, file_size, dblind ) + de_state *s; + zone_t block; + off_t *file_size; + int dblind; + + { + union + { + zone1_t ind1[ V1_INDIRECTS ]; + zone_t ind2[ V2_INDIRECTS(STATIC_BLOCK_SIZE) ]; + } indirect; + int i; + zone_t zone; + + /* Check for a "hole". */ + + if ( block == NO_ZONE ) + { + off_t skip = (off_t) s->nr_indirects * K; + + if ( *file_size < skip || dblind ) + { + Warning( "File has a hole at the end" ); + return( 0 ); + } + + if ( fseek( s->file_f, skip, SEEK_CUR ) == -1 ) + { + Warning( "Problem seeking %s", s->file_name ); + return( 0 ); + } + + *file_size -= skip; + return( 1 ); + } + + + /* Not a "hole". Recover indirect block, if not in use. */ + + if ( ! Free_Block( s, block ) ) + return( 0 ); + + + Read_Disk( s, (long) block << K_SHIFT, (char *) &indirect ); + + for ( i = 0; i < s->nr_indirects; ++i ) + { + if ( *file_size == 0 ) + return( 1 ); + + zone = (s->v1 ? indirect.ind1[ i ] : indirect.ind2[ i ]); + if ( dblind ) + { + if ( ! Indirect( s, zone, file_size, 0 ) ) + return( 0 ); + } + else + { + if ( ! Data_Block( s, zone, file_size ) ) + return( 0 ); + } + } + + return( 1 ); + } + + + + + + +/* Data_Block( state, block, &file_size ) + * + * If "block" is free then write Min(file_size, k) + * bytes from it onto the current output file. + * + * If "block" is zero, this means that a 1k "hole" + * is in the file. The recovered file maintains + * the reduced size by not allocating the block. + * + * The file size is decremented accordingly. + */ + + +int Data_Block( s, block, file_size ) + de_state *s; + zone_t block; + off_t *file_size; + + { + char buffer[ K ]; + off_t block_size = *file_size > K ? K : *file_size; + + + /* Check for a "hole". */ + + if ( block == NO_ZONE ) + { + if ( block_size < K ) + { + Warning( "File has a hole at the end" ); + return( 0 ); + } + + if ( fseek( s->file_f, block_size, SEEK_CUR ) == -1 ) + { + Warning( "Problem seeking %s", s->file_name ); + return( 0 ); + } + + *file_size -= block_size; + return( 1 ); + } + + + /* Block is not a "hole". Copy it to output file, if not in use. */ + + if ( ! Free_Block( s, block ) ) + return( 0 ); + + Read_Disk( s, (long) block << K_SHIFT, buffer ); + + + if ( fwrite( buffer, 1, (size_t) block_size, s->file_f ) + != (size_t) block_size ) + { + Warning( "Problem writing %s", s->file_name ); + return( 0 ); + } + + *file_size -= block_size; + return( 1 ); + } + + + + + + +/* Free_Block( state, block ) + * + * Make sure "block" is a valid data block number, and it + * has not been allocated to another file. + */ + + +int Free_Block( s, block ) + de_state *s; + zone_t block; + + { + if ( block < s->first_data || block >= s->zones ) + { + Warning( "Illegal block number" ); + return( 0 ); + } + + if ( In_Use( (bit_t) (block - (s->first_data - 1)), s->zone_map ) ) + { + Warning( "Encountered an \"in use\" data block" ); + return( 0 ); + } + + return( 1 ); + } + diff --git a/commands/de/de_stdin.c b/commands/de/de_stdin.c new file mode 100755 index 000000000..2bd2531eb --- /dev/null +++ b/commands/de/de_stdin.c @@ -0,0 +1,298 @@ +/****************************************************************/ +/* */ +/* de_stdin.c */ +/* */ +/* Processing input from the "de" user. */ +/* */ +/****************************************************************/ +/* origination 1989-Jan-15 Terrence W. Holm */ +/****************************************************************/ + + +#include +#include +#include +#include +#include + +#include +#include +#include "../../servers/fs/const.h" +#include "../../servers/fs/inode.h" + +#include "de.h" + +FORWARD _PROTOTYPE(int Timed_Get_Char , (int time )); +FORWARD _PROTOTYPE(void Timed_Out , (int sig)); + + + + +/****************************************************************/ +/* */ +/* Save_Term() */ +/* */ +/* Save the current terminal characteristics. */ +/* */ +/* */ +/* Set_Term() */ +/* */ +/* Set up the terminal characteristics. */ +/* */ +/* */ +/* Reset_Term() */ +/* */ +/* Restore the terminal characteristics. */ +/* */ +/****************************************************************/ + + +static struct termios saved_term; + + +void Save_Term() + + { + tcgetattr( 0, &saved_term ); + } + + + + +void Set_Term() + + { + struct termios term; + + term = saved_term; + + + /* No tab expansion, no echo, don't map ^M to ^J, cbreak mode */ + + term.c_iflag &= ~ICRNL; + term.c_oflag &= ~OPOST; + term.c_lflag &= ~ICANON & ~ECHO; + + + /* Change the interrupt character to ^C */ + + term.c_cc[VINTR] = '\003'; + + tcsetattr( 0, TCSANOW, &term ); + } + + + + +void Reset_Term() + + { + tcsetattr( 0, TCSANOW, &saved_term ); + } + + + + + + +/****************************************************************/ +/* */ +/* Get_Char() */ +/* */ +/* Return the next input character. Escape */ +/* sequences are mapped to special codes. */ +/* */ +/****************************************************************/ + + +int Get_Char() + { + int c; + static int unget_char = EOF; + + + /* Flush the output to the screen before waiting */ + /* for input from the user. */ + + fflush( stdout ); + + if ( unget_char == EOF ) + { + while ( (c = Timed_Get_Char( 60 * 60 )) < EOF ) + printf( "%c", BELL ); + } + else + { + c = unget_char; + unget_char = EOF; + } + + if ( c == EOF ) + return( EOF ); + + if ( c != ESCAPE ) + return( c ); + + if ( (c = Timed_Get_Char( 1 )) <= EOF ) + return( ESCAPE ); + + if ( c != '[' ) + { + unget_char = c; + return( ESCAPE ); + } + + if ( (c = Timed_Get_Char( 1 )) <= EOF ) + { + unget_char = '['; + return( ESCAPE ); + } + + return( c | 0x80 ); /* Flag ESC [ x */ + } + + + + + + +int Timed_Get_Char( time ) + int time; + + { + char c; + int count; + + signal( SIGALRM, Timed_Out ); + + alarm( time ); + count = read( 0, &c, 1 ); + alarm( 0 ); + + if ( count <= 0 ) + return( EOF + count ); + + return( c & 0x7f ); + } + + + + + + +/****************************************************************/ +/* */ +/* Get_Line() */ +/* */ +/* Read a line from the user. Returns a pointer */ +/* to a local buffer, or NULL if DEL or a non- */ +/* ASCII character was typed. Processes ^H and */ +/* ^U. ^M terminates the input. */ +/* */ +/****************************************************************/ + + +char *Get_Line() + + { + int c; + int i; + static char line[ MAX_STRING + 1 ]; + + for ( i = 0; i <= MAX_STRING; ++i ) + { + c = Get_Char(); + + if ( c == EOF || c == DEL || (c & 0x80) ) + return( NULL ); + + if ( c == BS ) + { + if ( --i >= 0 ) + { + printf( "\b \b" ); + --i; + } + } + + else if ( c == CTRL_U ) + { + for ( --i; i >= 0; --i ) + printf( "\b \b" ); + } + + else if ( c == '\r' ) + { + line[ i ] = '\0'; + return( line ); + } + + else if ( i < MAX_STRING ) + { + line[ i ] = c; + Print_Ascii( c ); + } + + else /* Line buffer is full, don't add any more to it. */ + { + putchar( BELL ); + --i; + } + } + + Error( "Internal fault (line buffer overflow)" ); + + /* NOTREACHED */ + return( NULL ); + } + + + + + + +/****************************************************************/ +/* */ +/* Arrow_Esc( char ) */ +/* */ +/* If the keyboard does not generate Ansi escape */ +/* codes for the arrow keys, but does generate */ +/* single byte control codes, then map these */ +/* codes to the special characters we are using */ +/* to denote the Ansi escape codes. */ +/* */ +/****************************************************************/ + + +extern char Kup; /* (ku) - Up arrow key */ +extern char Kdown; /* (kd) - Down arrow key */ +extern char Kleft; /* (kl) - Left arrow key */ +extern char Kright; /* (kr) - Right arrow key */ + + +int Arrow_Esc( c ) + int c; + + { + if ( c == Kup ) + return( ESC_UP ); + + if ( c == Kdown ) + return( ESC_DOWN ); + + if ( c == Kleft ) + return( ESC_LEFT ); + + if ( c == Kright ) + return( ESC_RIGHT ); + + return( c ); + } + +void Timed_Out(sig) +int sig; + {} + +/* + * $PchHeader: /mount/hd2/minix/sys/cmd/de/RCS/de_stdin.c,v 1.3 1995/02/10 08:01:30 philip Exp $ + */ diff --git a/commands/de/de_stdout.c b/commands/de/de_stdout.c new file mode 100755 index 000000000..9bd0acec9 --- /dev/null +++ b/commands/de/de_stdout.c @@ -0,0 +1,1169 @@ +/****************************************************************/ +/* */ +/* de_stdout.c */ +/* */ +/* Displaying information from the "Disk editor". */ +/* */ +/****************************************************************/ +/* origination 1989-Jan-15 Terrence W. Holm */ +/****************************************************************/ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include "../../servers/fs/const.h" +#include "../../servers/fs/type.h" +#include "../../servers/fs/inode.h" +#include + +#include "de.h" + +#define major(x) ( (x>>8) & 0377) +#define minor(x) (x & 0377) + +/****************************************************************/ +/* Code for handling termcap */ +/****************************************************************/ + + +#define TC_BUFFER 1024 /* Size of termcap(3) buffer */ +#define TC_STRINGS 200 /* Enough room for cm,cl,so,se */ + + +static char *Tmove; /* (cm) - Format for tgoto */ +static char *Tclr_all; /* (cl) - Clear screen */ +static char *Treverse; /* (so) - Start reverse mode */ +static char *Tnormal; /* (se) - End reverse mode */ + +char Kup = 0; /* (ku) - Up arrow key */ +char Kdown = 0; /* (kd) - Down arrow key */ +char Kleft = 0; /* (kl) - Left arrow key */ +char Kright = 0; /* (kr) - Right arrow key */ + +_PROTOTYPE(void Goto , (int column , int line )); +_PROTOTYPE(void Block_Type , (de_state *s )); +_PROTOTYPE(void Draw_Words , (de_state *s )); +_PROTOTYPE(void Draw_Info , (de_state *s )); +_PROTOTYPE(void Draw_Block , (char *block )); +_PROTOTYPE(void Draw_Map , (char *block , int max_bits )); +_PROTOTYPE(void Draw_Offset , (de_state *s )); +_PROTOTYPE(void Word_Pointers , (off_t old_addr , off_t new_addr )); +_PROTOTYPE(void Block_Pointers , (off_t old_addr , off_t new_addr )); +_PROTOTYPE(void Map_Pointers , (off_t old_addr , off_t new_addr )); +_PROTOTYPE(void Print_Number , (Word_t number , int output_base )); +_PROTOTYPE(void Draw_Zone_Numbers , (de_state *s , struct inode *inode , + int zindex , int zrow )); + + + +/****************************************************************/ +/* */ +/* Init_Termcap() */ +/* */ +/* Initializes the external variables for the */ +/* current terminal. */ +/* */ +/****************************************************************/ + + +int Init_Termcap() + + { + char *term; + char buffer[ TC_BUFFER ]; + static char strings[ TC_STRINGS ]; + char *s = &strings[0]; + char *Kcode; + + + term = getenv( "TERM" ); + + if ( term == NULL ) + return( 0 ); + + if ( tgetent( buffer, term ) != 1 ) + return( 0 ); + + + if ( (Tmove = tgetstr( "cm", &s )) == NULL ) + return( 0 ); + + if ( (Tclr_all = tgetstr( "cl", &s )) == NULL ) + return( 0 ); + + if ( (Treverse = tgetstr( "so", &s )) == NULL ) + { + Treverse = Tnormal = s; + *s = '\0'; + ++s; + } + else if ( (Tnormal = tgetstr( "se", &s )) == NULL ) + return( 0 ); + + + /* See if there are single character arrow key codes */ + + if ( (Kcode = tgetstr( "ku", &s )) != NULL && strlen( Kcode ) == 1 ) + Kup = Kcode[0]; + + if ( (Kcode = tgetstr( "kd", &s )) != NULL && strlen( Kcode ) == 1 ) + Kdown = Kcode[0]; + + if ( (Kcode = tgetstr( "kl", &s )) != NULL && strlen( Kcode ) == 1 ) + Kleft = Kcode[0]; + + if ( (Kcode = tgetstr( "kr", &s )) != NULL && strlen( Kcode ) == 1 ) + Kright = Kcode[0]; + + + return( 1 ); + } + + + + + + +/****************************************************************/ +/* */ +/* Goto( column, line ) */ +/* */ +/* Use the termcap string to move the cursor. */ +/* */ +/****************************************************************/ + + +void Goto( column, line ) + int column; + int line; + + { + fputs( tgoto( Tmove, column, line ), stdout ); + } + + + + + + +/****************************************************************/ +/* Output routines */ +/****************************************************************/ + + + + +/****************************************************************/ +/* */ +/* Draw_Help_Screen() */ +/* */ +/****************************************************************/ + + +void Draw_Help_Screen( s ) + de_state *s; + + { + int down; + int right; + + switch ( s->mode ) + { + case WORD : down = 2; right = 32; break; + case BLOCK : down = 64; right = 1; break; + case MAP : down = 256; right = 4; break; + } + + printf( "%s ", Tclr_all ); + printf( "%sDE COMMANDS%s\r\n\n\n", Treverse, Tnormal ); + + + printf( " PGUP b Back one block h Help\r\n" ); + printf( " PGDN f Forward one block q Quit\r\n" ); + printf( " HOME B Goto first block m Minix shell\r\n" ); + printf( " END F Goto last block\r\n" ); + printf( " v Visual mode (w b m)\r\n" ); + printf( " g Goto specified block o Output base (h d o b)\r\n" ); + printf( " G Goto block indirectly\r\n" ); + printf( " i Goto i-node c Change file name\r\n" ); + printf( " I Filename to i-node w Write ASCII block\r\n" ); + printf( " W Write block exactly\r\n" ); + printf( " / Search\r\n" ); + printf( " n Next occurrence x Extract lost entry\r\n" ); + printf( " p Previous address X Extract lost blocks\r\n" ); + printf( " s Store word\r\n" ); + printf( " UP u Move back %d bytes\r\n", down ); + printf( " DOWN d Move forward %d bytes\r\n", down ); + printf( " LEFT l Move back %d byte%s\r\n", right, + right == 1 ? "" : "s" ); + printf( " RIGHT r Move forward %d byte%s\r\n\n\n", right, + right == 1 ? "" : "s" ); + } + + + + + + +/****************************************************************/ +/* */ +/* Wait_For_Key() */ +/* */ +/* The user must press a key to continue. */ +/* */ +/****************************************************************/ + + +void Wait_For_Key() + + { + Draw_Prompt( "Press a key to continue..." ); + + Get_Char(); + } + + + + + + +/****************************************************************/ +/* */ +/* Draw_Prompt( string ) */ +/* */ +/* Write a message in the "prompt" area. */ +/* */ +/****************************************************************/ + + +void Draw_Prompt( string ) + char *string; + + { + Goto( PROMPT_COLUMN, PROMPT_LINE ); + + printf( "%s%s%s ", Treverse, string, Tnormal ); + } + + + + + + +/****************************************************************/ +/* */ +/* Erase_Prompt() */ +/* */ +/* Erase the message in the "prompt" area. */ +/* */ +/****************************************************************/ + + +void Erase_Prompt() + + { + Goto( PROMPT_COLUMN, PROMPT_LINE ); + + printf( "%77c", ' ' ); + + Goto( PROMPT_COLUMN, PROMPT_LINE ); + } + + + + + + +/****************************************************************/ +/* */ +/* Draw_Screen( state ) */ +/* */ +/* Redraw everything, except pointers. */ +/* */ +/****************************************************************/ + + +void Draw_Screen( s ) + de_state *s; + + { + fputs( Tclr_all, stdout ); + + Draw_Strings( s ); + Block_Type( s ); + + switch ( s->mode ) + { + case WORD : Draw_Words( s ); + Draw_Info( s ); + break; + + case BLOCK : Draw_Block( s->buffer ); + break; + + case MAP : { + int max_bits = 2 * K; + + /* Don't display the bits after the end */ + /* of the i-node or zone bit maps. */ + + if ( s->block == 2 + s->inode_maps - 1 ) + max_bits = (int) + (s->inodes_in_map + - CHAR_BIT * K * (ino_t) (s->inode_maps - 1) + - CHAR_BIT * (ino_t) (s->offset & ~ MAP_MASK)); + + else if ( s->block == 2 + s->inode_maps + s->zone_maps - 1 ) + max_bits = (int) + (s->zones_in_map + - CHAR_BIT * K * (zone_t) (s->zone_maps - 1) + - CHAR_BIT * (zone_t) (s->offset & ~ MAP_MASK)); + + if ( max_bits < 0 ) + max_bits = 0; + + Draw_Map( &s->buffer[ s->offset & ~ MAP_MASK ], max_bits ); + break; + } + } + } + + + + + + +/****************************************************************/ +/* */ +/* Draw_Strings( state ) */ +/* */ +/* The first status line contains the device name, */ +/* the current write file name (if one is open) */ +/* and the current search string (if one has */ +/* been defined). */ +/* */ +/* Long strings are truncated. */ +/* */ +/****************************************************************/ + + +void Draw_Strings( s ) + de_state *s; + + { + int len; + int i; + + Goto( STATUS_COLUMN, STATUS_LINE ); + + printf( "Device %s= %-14.14s ", + s->device_mode == O_RDONLY ? "" : "(w) ", s->device_name ); + + switch ( s->magic ) + { + case SUPER_MAGIC : printf( "V1 file system "); + break; + case SUPER_REV : printf( "V1-bytes-swapped file system (?) "); + break; + case SUPER_V2 : printf( "V2 file system "); + break; + case SUPER_V2_REV : printf( "V2-bytes-swapped file system (?) "); + break; + default : printf( "not a Minix file system "); + break; + } + + len = strlen( s->file_name ); + + if ( len == 0 ) + printf( "%29s", " " ); + else if ( len <= 20 ) + printf( "File = %-20s ", s->file_name ); + else + printf( "File = ...%17.17s ", s->file_name + len - 17 ); + + + len = strlen( s->search_string ); + + if ( len == 0 ) + printf( "%20s", " " ); + else + { + printf( "Search = " ); + + if ( len <= 11 ) + { + for ( i = 0; i < len; ++i ) + Print_Ascii( s->search_string[ i ] ); + + for ( ; i < 11; ++i ) + putchar( ' ' ); + } + else + { + for ( i = 0; i < 8; ++i ) + Print_Ascii( s->search_string[ i ] ); + + printf( "..." ); + } + } + } + + + + + + +/****************************************************************/ +/* */ +/* Block_Type( state ) */ +/* */ +/* Display the current block type. */ +/* */ +/****************************************************************/ + + +void Block_Type( s ) + de_state *s; + + { + Goto( STATUS_COLUMN, STATUS_LINE + 1 ); + + printf( "Block = %5u of %-5u ", s->block, s->zones ); + + if ( !s->is_fs ) + return; + + if ( s->block == BOOT_BLOCK ) + printf( "Boot block" ); + + else if ( s->block == 1 ) + printf( "Super block" ); + + else if ( s->block < 2 + s->inode_maps ) + printf( "I-node bit map" ); + + else if ( s->block < 2 + s->inode_maps + s->zone_maps ) + printf( "Zone bit map" ); + + else if ( s->block < s->first_data ) + printf( "I-nodes" ); + + else + printf( "Data block (%sin use)", + In_Use( (bit_t) (s->block - (s->first_data - 1)), s->zone_map ) + ? "" : "not " ); + } + + + + + + +/****************************************************************/ +/* */ +/* Draw_Words( state ) */ +/* */ +/* Draw a page in word format. */ +/* */ +/****************************************************************/ + + +void Draw_Words( s ) + de_state *s; + + { + int line; + int addr = s->offset & ~ PAGE_MASK; + + + for ( line = 0; line < 16; ++line, addr += 2 ) + { + Goto( BLOCK_COLUMN, BLOCK_LINE + line ); + + printf( "%5d ", addr ); + + Print_Number( *( (word_t *) &s->buffer[ addr ] ), s->output_base ); + } + + Goto( BLOCK_COLUMN + 64, BLOCK_LINE ); + printf( "(base %d)", s->output_base ); + } + + + + + + +/****************************************************************/ +/* */ +/* Draw_Info( state ) */ +/* */ +/* Add information to a page drawn in word format. */ +/* The routine recognizes the super block, inodes, */ +/* executables and "ar" archives. If the current */ +/* page is not one of these, then ASCII characters */ +/* are printed from the data words. */ +/* */ +/****************************************************************/ + + +char *super_block_info[] = { "number of inodes", + "V1 number of zones", + "inode bit map blocks", + "zone bit map blocks", + "first data zone", + "blocks per zone shift & flags", + "maximum file size", + "", + "magic number", + "fsck magic number", + "V2 number of zones" }; + + +void Draw_Info( s ) + de_state *s; + + { + int i; + int page = s->offset >> PAGE_SHIFT; + dev_t dev; + + + if ( s->is_fs && s->block == 1 && page == 0 ) + for ( i = 0; i < 11; ++i ) + { + Goto( INFO_COLUMN, INFO_LINE + i ); + printf( "%s", super_block_info[ i ] ); + } + + else if ( s->is_fs && s->block >= s->first_data - s->inode_blocks && + s->block < s->first_data ) + { + struct inode core_inode; + d1_inode *dip1; + d2_inode *dip2; + struct inode *inode = &core_inode; + int special = 0; + int m; + struct passwd *user; + struct group *grp; + + dip1 = (d1_inode *) &s->buffer[ s->offset & ~ PAGE_MASK ]; + dip2 = (d2_inode *) &s->buffer[ s->offset & ~ PAGE_MASK + & ~ (V2_INODE_SIZE-1) ]; + conv_inode( inode, dip1, dip2, READING, s->magic ); + + user = getpwuid( inode->i_uid ); + grp = getgrgid( inode->i_gid ); + + if ( s->magic != SUPER_MAGIC && page & 1 ) + { + Draw_Zone_Numbers( s, inode, 2, 0 ); + return; + } + + Goto( INFO_COLUMN, INFO_LINE ); + + switch( inode->i_mode & S_IFMT ) + { + case S_IFDIR : printf( "directory " ); + break; + + case S_IFCHR : printf( "character " ); + special = 1; + break; + + case S_IFBLK : printf( "block " ); + special = 1; + break; + + case S_IFREG : printf( "regular " ); + break; +#ifdef S_IFIFO + case S_IFIFO : printf( "fifo " ); + break; +#endif +#ifdef S_IFLNK + case S_IFLNK : printf( "symlink " ); + break; +#endif +#ifdef S_IFSOCK + case S_IFSOCK: printf( "socket " ); + break; +#endif + default : printf( "unknown " ); + } + + for ( m = 11; m >= 0; --m ) + putchar( (inode->i_mode & (1<magic == SUPER_MAGIC ) + { + /* V1 file system */ + Goto( INFO_COLUMN, INFO_LINE + 1 ); + printf( "user %s", user ? user->pw_name : "" ); + + Goto( INFO_COLUMN, INFO_LINE + 2 ); + printf( "file size %lu", inode->i_size ); + + Goto( INFO_COLUMN, INFO_LINE + 4 ); + printf( "m_time %s", ctime( &inode->i_mtime ) ); + + Goto( INFO_COLUMN, INFO_LINE + 6 ); + printf( "links %d, group %s", + inode->i_nlinks, grp ? grp->gr_name : "" ); + + Draw_Zone_Numbers( s, inode, 0, 7 ); + } + else + { + /* V2 file system, even page. */ + Goto( INFO_COLUMN, INFO_LINE + 1 ); + printf( "links %d ", inode->i_nlinks); + + Goto( INFO_COLUMN, INFO_LINE + 2 ); + printf( "user %s", user ? user->pw_name : "" ); + + Goto( INFO_COLUMN, INFO_LINE + 3 ); + printf( "group %s", grp ? grp->gr_name : "" ); + + Goto( INFO_COLUMN, INFO_LINE + 4 ); + printf( "file size %lu", inode->i_size ); + + Goto( INFO_COLUMN, INFO_LINE + 6 ); + printf( "a_time %s", ctime( &inode->i_atime ) ); + + Goto( INFO_COLUMN, INFO_LINE + 8 ); + printf( "m_time %s", ctime( &inode->i_mtime ) ); + + Goto( INFO_COLUMN, INFO_LINE + 10 ); + printf( "c_time %s", ctime( &inode->i_ctime ) ); + + Draw_Zone_Numbers( s, inode, 0, 12 ); + } + + if ( special ) + { + Goto( INFO_COLUMN, INFO_LINE + 7 ); + dev = (dev_t) inode->i_zone[0]; + printf( "major %d, minor %d", major(dev), minor(dev) ); + } + } + + else /* Print ASCII characters for each byte in page */ + { + char *p = &s->buffer[ s->offset & ~ PAGE_MASK ]; + + for ( i = 0; i < 16; ++i ) + { + Goto( INFO_COLUMN, INFO_LINE + i ); + Print_Ascii( *p++ ); + Print_Ascii( *p++ ); + } + + if ( s->block >= s->first_data && page == 0 ) + { + unsigned magic = ((s->buffer[1] & 0xff) << 8) | (s->buffer[0] & 0xff); + unsigned second = ((s->buffer[3] & 0xff) << 8) | (s->buffer[2] & 0xff); + + /* Is this block the start of an executable file? */ + + if ( magic == (unsigned) A_OUT ) + { + Goto( INFO_COLUMN, INFO_LINE ); + printf( "executable" ); + + Goto( INFO_COLUMN, INFO_LINE + 1 ); + + if ( second == (unsigned) SPLIT ) + printf( "separate I & D" ); + else + printf( "combined I & D" ); + } + } + } + } + + + + + + +/****************************************************************/ +/* */ +/* Draw_Block( block ) */ +/* */ +/* Redraw a 1k block in character format. */ +/* */ +/****************************************************************/ + + +void Draw_Block( block ) + char *block; + + { + int line; + int column; + int reverse = 0; + int msb_flag = 0; + + + for ( line = 0; line < 16; ++line ) + { + Goto( BLOCK_COLUMN, BLOCK_LINE + line ); + + for ( column = 0; column < 64; ++column ) + { + char c = *block++; + + if ( c & 0x80 ) + { + msb_flag = 1; + c &= 0x7f; + } + + if ( c >= ' ' && c < DEL ) + { + if ( reverse ) + { fputs( Tnormal, stdout ); reverse = 0; } + + putchar( c ); + } + else + { + if ( ! reverse ) + { fputs( Treverse, stdout ); reverse = 1; } + + putchar( c == DEL ? '?' : '@' + c ); + } + } /* end for ( column ) */ + } /* end for ( line ) */ + + if ( reverse ) + { fputs( Tnormal, stdout ); reverse = 0; } + + if ( msb_flag ) + { + Goto( BLOCK_COLUMN + 68, BLOCK_LINE + 6 ); + fputs( "(MSB)", stdout ); + } + } + + + + + + +/****************************************************************/ +/* */ +/* Draw_Map( block, max_bits ) */ +/* */ +/* Redraw a block in a bit map format. */ +/* Display min( max_bits, 2048 ) bits. */ +/* */ +/* The 256 bytes in "block" are displayed from */ +/* top to bottom and left to right. Bit 0 of */ +/* a byte is towards the top of the screen. */ +/* */ +/* Special graphic codes are used to generate */ +/* two "bits" per character position. So a 16 */ +/* line by 64 column display is 32 "bits" by */ +/* 64 "bits". Or 4 bytes by 64 bytes. */ +/* */ +/****************************************************************/ + + +void Draw_Map( block, max_bits ) + char *block; + int max_bits; + + { + int line; + int column; + int bit_count = 0; + + for ( line = 0; line < 16; ++line ) + { + char *p = &block[ (line & 0xC) >> 2 ]; + int shift = (line & 0x3) << 1; + + Goto( BLOCK_COLUMN, BLOCK_LINE + line ); + + for ( column = 0; column < 64; ++column, p += 4 ) + { + char c = (*p >> shift) & 0x3; + int current_bit = ((p - block) << 3) + shift; + + /* Don't display bits past "max_bits" */ + + if ( current_bit >= max_bits ) + break; + + /* If "max_bits" occurs in between the two bits */ + /* I am trying to display as one character, then */ + /* zero off the high-order bit. */ + + if ( current_bit + 1 == max_bits ) + c &= 1; + + switch ( c ) + { + case 0 : putchar( BOX_CLR ); + break; + + case 1 : putchar( BOX_TOP ); + ++bit_count; + break; + + case 2 : putchar( BOX_BOT ); + ++bit_count; + break; + + case 3 : putchar( BOX_ALL ); + bit_count += 2; + break; + } + } /* end for ( column ) */ + } /* end for ( line ) */ + + + Goto( BLOCK_COLUMN + 68, BLOCK_LINE + 6 ); + printf( "(%d)", bit_count ); + } + + + + + + +/****************************************************************/ +/* */ +/* Draw_Pointers( state ) */ +/* */ +/* Redraw the pointers and the offset field. */ +/* The rest of the screen stays intact. */ +/* */ +/****************************************************************/ + + +void Draw_Pointers( s ) + de_state *s; + + { + Draw_Offset( s ); + + switch ( s->mode ) + { + case WORD : Word_Pointers( s->last_addr, s->address ); + break; + + case BLOCK : Block_Pointers( s->last_addr, s->address ); + break; + + case MAP : Map_Pointers( s->last_addr, s->address ); + break; + } + + Goto( PROMPT_COLUMN, PROMPT_LINE ); + } + + + + + + +/****************************************************************/ +/* */ +/* Draw_Offset( state ) */ +/* */ +/* Display the offset in the current buffer */ +/* and the relative position if within a map */ +/* or i-node block. */ +/* */ +/****************************************************************/ + + +void Draw_Offset( s ) + de_state *s; + + { + Goto( STATUS_COLUMN, STATUS_LINE + 2 ); + + printf( "Offset = %5d ", s->offset ); + + + if ( s->block < 2 ) + return; + + if ( s->block < 2 + s->inode_maps ) + { + long bit = (s->address - 2 * K) * 8; + + if ( bit < s->inodes_in_map ) + printf( "I-node %ld of %d ", bit, s->inodes ); + else + printf( "(padding) " ); + } + + else if ( s->block < 2 + s->inode_maps + s->zone_maps ) + { + long bit = (s->address - (2 + s->inode_maps) * K) * 8; + + if ( bit < s->zones_in_map ) + printf( "Block %ld of %u ", bit + s->first_data - 1, s->zones ); + else + printf( "(padding) " ); + } + + else if ( s->block < s->first_data ) + { + bit_t node = (s->address - (2 + s->inode_maps + s->zone_maps) * K) / + s->inode_size + 1; + + if ( node <= s->inodes ) + printf( "I-node %lu of %lu (%sin use) ", + (unsigned long) node, (unsigned long) s->inodes, + In_Use( node, s->inode_map ) ? "" : "not " ); + else + printf( "(padding) " ); + } + } + + + + + + +/****************************************************************/ +/* */ +/* Word_Pointers( old_addr, new_addr ) */ +/* */ +/* Block_Pointers( old_addr, new_addr ) */ +/* */ +/* Map_Pointers( old_addr, new_addr ) */ +/* */ +/* Redraw the index pointers for a each type */ +/* of display. The pointer at "old_addr" is */ +/* erased and a new pointer is positioned */ +/* for "new_addr". This makes the screen */ +/* update faster and more pleasant for the user. */ +/* */ +/****************************************************************/ + + +void Word_Pointers( old_addr, new_addr ) + off_t old_addr; + off_t new_addr; + + { + int from = ( (int) old_addr & PAGE_MASK ) >> 1; + int to = ( (int) new_addr & PAGE_MASK ) >> 1; + + Goto( BLOCK_COLUMN - 2, BLOCK_LINE + from ); + putchar( ' ' ); + + Goto( BLOCK_COLUMN - 2, BLOCK_LINE + to ); + putchar( '>' ); + } + + + + +void Block_Pointers( old_addr, new_addr ) + off_t old_addr; + off_t new_addr; + + { + int from = (int) old_addr & ~K_MASK; + int to = (int) new_addr & ~K_MASK; + + Goto( BLOCK_COLUMN - 2, BLOCK_LINE + from / 64 ); + putchar( ' ' ); + + Goto( BLOCK_COLUMN - 2, BLOCK_LINE + to / 64 ); + putchar( '>' ); + + Goto( BLOCK_COLUMN + from % 64, BLOCK_LINE + 17 ); + putchar( ' ' ); + + Goto( BLOCK_COLUMN + to % 64, BLOCK_LINE + 17 ); + putchar( '^' ); + } + + + + +void Map_Pointers( old_addr, new_addr ) + off_t old_addr; + off_t new_addr; + + { + int from = ( (int) old_addr & MAP_MASK ) >> 2; + int to = ( (int) new_addr & MAP_MASK ) >> 2; + + Goto( BLOCK_COLUMN + from, BLOCK_LINE + 17 ); + putchar( ' ' ); + + Goto( BLOCK_COLUMN + to, BLOCK_LINE + 17 ); + putchar( '^' ); + } + + + + + + +/****************************************************************/ +/* */ +/* Print_Number( number, output_base ) */ +/* */ +/* Output "number" in the output base. */ +/* */ +/****************************************************************/ + + +void Print_Number( number, output_base ) + word_t number; + int output_base; + + { + switch ( output_base ) + { + case 16 : printf( "%5x", number ); + break; + + case 10 : printf( "%7u", number ); + break; + + case 8 : printf( "%7o", number ); + break; + + case 2 : { + unsigned int mask; + char pad = ' '; + + for ( mask = 0x8000; mask > 1; mask >>= 1 ) + putchar( (mask & number) ? (pad = '0', '1') : pad ); + + putchar( (0x01 & number) ? '1' : '0' ); + + break; + } + + default : Error( "Internal fault (output_base)" ); + } + } + + + + + + +/****************************************************************/ +/* */ +/* Print_Ascii( char ) */ +/* */ +/* Display a character in reverse mode if it */ +/* is not a normal printable ASCII character. */ +/* */ +/****************************************************************/ + + +void Print_Ascii( c ) + char c; + + { + c &= 0x7f; + + if ( c < ' ' ) + printf( "%s%c%s", Treverse, '@' + c, Tnormal ); + else if ( c == DEL ) + printf( "%s?%s", Treverse, Tnormal ); + else + putchar( c ); + } + + + + + + +/****************************************************************/ +/* */ +/* Warning( text, arg1, arg2 ) */ +/* */ +/* Display a message for 2 seconds. */ +/* */ +/****************************************************************/ + + +#if __STDC__ +void Warning( const char *text, ... ) +#else +void Warning( text ) + char *text; +#endif + + { + va_list argp; + + printf( "%c%s", BELL, Tclr_all ); + + Goto( WARNING_COLUMN, WARNING_LINE ); + + printf( "%s Warning: ", Treverse ); + va_start( argp, text ); + vprintf( text, argp ); + va_end( argp ); + printf( " %s", Tnormal ); + + fflush(stdout); /* why does everyone forget this? */ + + sleep( 2 ); + } + + +void Draw_Zone_Numbers( s, inode, zindex, zrow ) + de_state *s; + struct inode *inode; + int zindex; + int zrow; + + { + static char *plurals[] = { "", "double ", "triple " }; + zone_t zone; + + for ( ; zrow < 16; + ++zindex, zrow += s->zone_num_size / sizeof (word_t) ) + { + Goto( INFO_COLUMN, INFO_LINE + zrow ); + if ( zindex < s->ndzones ) + printf( "zone %d", zindex ); + else + printf( "%sindirect", plurals[ zindex - s->ndzones ] ); + if ( s->magic != SUPER_MAGIC ) + { + zone = inode->i_zone[ zindex ]; + if ( zone != (word_t) zone ) + { + Goto( INFO_COLUMN + 16, INFO_LINE + zrow ); + printf("%ld", (long) zone ); + } + } + } + } diff --git a/commands/dhcpd/Makefile b/commands/dhcpd/Makefile new file mode 100755 index 000000000..536907a2e --- /dev/null +++ b/commands/dhcpd/Makefile @@ -0,0 +1,24 @@ +# Makefile for dhcpd. + +CFLAGS = $(OPT) -D_MINIX +LDFLAGS = + +all: dhcpd + +OBJ= dhcpd.o tags.o devices.o ether.o + +dhcpd: $(OBJ) + $(CC) $(LDFLAGS) -o $@ $(OBJ) + install -S 12kw $@ + +install: /usr/bin/dhcpd + +/usr/bin/dhcpd: dhcpd + install -c $? $@ + +clean: + rm -f *.o dhcpd core a.out + +# Dependencies. +$(OBJ): dhcpd.h +dhcpd.o ether.o: arp.h diff --git a/commands/dhcpd/arp.h b/commands/dhcpd/arp.h new file mode 100755 index 000000000..0d0b131fd --- /dev/null +++ b/commands/dhcpd/arp.h @@ -0,0 +1,26 @@ +/* arp.h - Address Resolution Protocol packet format. + * Author: Kees J. Bot + * 16 Dec 2000 + */ +#ifndef ARP_H +#define ARP_H + +typedef struct arp46 { + ether_addr_t dstaddr; + ether_addr_t srcaddr; + ether_type_t ethtype; /* ARP_PROTO. */ + u16_t hdr, pro; /* ARP_ETHERNET & ETH_IP_PROTO. */ + u8_t hln, pln; /* 6 & 4. */ + u16_t op; /* ARP_REQUEST or ARP_REPLY. */ + ether_addr_t sha; /* Source hardware address. */ + u8_t spa[4]; /* Source protocol address. */ + ether_addr_t tha; /* Likewise for the target. */ + u8_t tpa[4]; + char padding[60 - (4*6 + 2*4 + 4*2 + 2*1)]; +} arp46_t; + +#define ARP_ETHERNET 1 /* ARP on Ethernet. */ +#define ARP_REQUEST 1 /* A request for an IP address. */ +#define ARP_REPLY 2 /* A reply to a request. */ + +#endif /* ARP_H */ diff --git a/commands/dhcpd/devices.c b/commands/dhcpd/devices.c new file mode 100755 index 000000000..888ef6ce5 --- /dev/null +++ b/commands/dhcpd/devices.c @@ -0,0 +1,279 @@ +/* devices.c - Handle network devices. + * Author: Kees J. Bot + * 11 Jun 1999 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dhcpd.h" + +void get_buf(buf_t **bp) +{ + /* Allocate and return a buffer pointer iff *bp == nil. */ + if (*bp != nil) { + /* Already has one. */ + } else { + /* Get one from the heap. */ + buf_t *new= allocate(sizeof(*new)); + new->dhcp= (dhcp_t *) (new->buf + sizeof(eth_hdr_t) + + sizeof(ip_hdr_t) + sizeof(udp_hdr_t)); + new->udpio= ((udp_io_hdr_t *) new->dhcp) - 1; + new->udp= ((udp_hdr_t *) new->dhcp) - 1; + new->ip= ((ip_hdr_t *) new->udp) - 1; + new->eth= ((eth_hdr_t *) new->ip) - 1; + *bp= new; + } +} + +void put_buf(buf_t **bp) +{ + /* Return a buffer to the heap. */ + if (*bp != nil) { + free(*bp); + *bp= nil; + } +} + +void give_buf(buf_t **dbp, buf_t **sbp) +{ + /* Hand over a buffer to another variable. */ + put_buf(dbp); + *dbp= *sbp; + *sbp= nil; +} + +#if __minix_vmd +#define N_FDS 16 /* Minix-vmd can go async on many fds. */ +#else +#define N_FDS 1 /* Minix doesn't have async I/O. */ +#endif + +static fd_t fds[N_FDS]; /* List of open descriptors. */ +static struct network *fdwaitq; /* Queue of nets waiting for fds. */ + +network_t *newnetwork(void) +{ + /* Create and initialize a network structure. */ + network_t *new; + + new= allocate(sizeof(*new)); + memset(new, 0, sizeof(*new)); + new->hostname= nil; + new->solicit= NEVER; + new->sol_ct= -1; + return new; +} + +void closefd(fd_t *fdp) +{ + /* Close a descriptor. */ + if (fdp->fdtype != FT_CLOSED) { + asyn_close(&asyn, fdp->fd); + close(fdp->fd); + fdp->fdtype= FT_CLOSED; + fdp->since= 0; + put_buf(&fdp->bp); + if (debug >= 3) printf("%s: Closed\n", fdp->device); + } +} + +int opendev(network_t *np, fdtype_t fdtype, int compete) +{ + /* Make sure that a network has the proper device open and configured. + * Return true if this is made so, or false if the device doesn't exist. + * If compete is true then the caller competes for old descriptors. + * The errno value is EAGAIN if we're out of descriptors. + */ + fd_t *fdp, *fdold; + time_t oldest; + nwio_ethstat_t ethstat; + nwio_ethopt_t ethopt; + nwio_ipopt_t ipopt; + nwio_udpopt_t udpopt; + network_t **pqp; + static char devbytype[][4] = { "", "eth", "ip", "udp", "udp" }; + + /* Don't attempt to open higher level devices if not bound. */ + if (!(np->flags & NF_BOUND) && fdtype > FT_ETHERNET) { + errno= EAGAIN; + return 0; + } + + /* Check if already open / Find the oldest descriptor. */ + fdold= nil; + oldest= NEVER; + for (fdp= fds; fdp < arraylimit(fds); fdp++) { + if (fdp->n == np->n && fdp->fdtype == fdtype) { + /* Already open. */ + np->fdp= fdp; + return 1; + } + if (fdp->since <= oldest) { fdold= fdp; oldest= fdp->since; } + } + + /* None free? Then wait for one to get old if so desired. */ + if (fdold->fdtype != FT_CLOSED && !compete) { + errno= EAGAIN; + return 0; + } + + if (!(np->flags & NF_WAIT)) { + for (pqp= &fdwaitq; *pqp != nil; pqp= &(*pqp)->wait) {} + *pqp= np; + np->wait= nil; + np->flags |= NF_WAIT; + } + + /* We allow a net to keep a descriptor for half of the fast period. */ + oldest += DELTA_FAST/2; + + if (fdwaitq != np || (fdold->fdtype != FT_CLOSED && oldest > now)) { + /* This net is not the first in the queue, or the oldest isn't + * old enough. Forget it for now. + */ + if (oldest < event) event= oldest; + errno= EAGAIN; + return 0; + } + + /* The oldest is mine. */ + np->flags &= ~NF_WAIT; + fdwaitq= np->wait; + closefd(fdold); + + /* Open the proper device in the proper mode. */ + fdp= fdold; + fdp->n= np->n; + sprintf(fdp->device, "/dev/%s%d", devbytype[fdtype], np->n); + np->fdp= fdp; + + if ((fdp->fd= open(fdp->device, O_RDWR)) < 0) { + if (errno == ENOENT || errno == ENODEV || errno == ENXIO) return 0; + fatal(fdp->device); + } + + switch (fdtype) { + case FT_ETHERNET: + if (ioctl(np->fdp->fd, NWIOGETHSTAT, ðstat) < 0) { + /* Not an Ethernet. */ + close(fdp->fd); + return 0; + } + np->eth= ethstat.nwes_addr; + ethopt.nweo_flags= NWEO_COPY | NWEO_EN_LOC | NWEO_EN_BROAD + | NWEO_REMANY | NWEO_TYPEANY | NWEO_RWDATALL; + + if (ioctl(fdp->fd, NWIOSETHOPT, ðopt) < 0) { + fprintf(stderr, "%s: %s: Unable to set eth options: %s\n", + program, fdp->device, strerror(errno)); + exit(1); + } + break; + + case FT_ICMP: + ipopt.nwio_flags= NWIO_COPY | NWIO_EN_LOC | NWIO_EN_BROAD + | NWIO_REMANY | NWIO_PROTOSPEC + | NWIO_HDR_O_SPEC | NWIO_RWDATALL; + ipopt.nwio_tos= 0; + ipopt.nwio_ttl= 1; + ipopt.nwio_df= 0; + ipopt.nwio_hdropt.iho_opt_siz= 0; + ipopt.nwio_proto= IPPROTO_ICMP; + + if (ioctl(fdp->fd, NWIOSIPOPT, &ipopt) < 0) { + fprintf(stderr, "%s: %s: Unable to set IP options: %s\n", + program, fdp->device, strerror(errno)); + exit(1); + } + break; + + case FT_BOOTPC: + udpopt.nwuo_flags= NWUO_COPY | NWUO_EN_LOC | NWUO_EN_BROAD + | NWUO_RP_ANY | NWUO_RA_ANY | NWUO_RWDATALL + | NWUO_DI_IPOPT | NWUO_LP_SET; + udpopt.nwuo_locport= port_client; + goto udp; + + case FT_BOOTPS: + udpopt.nwuo_flags= NWUO_EXCL | NWUO_EN_LOC | NWUO_EN_BROAD + | NWUO_RP_ANY | NWUO_RA_ANY | NWUO_RWDATALL + | NWUO_DI_IPOPT | NWUO_LP_SET; + udpopt.nwuo_locport= port_server; + udp: + if (ioctl(fdp->fd, NWIOSUDPOPT, &udpopt) == -1) { + fprintf(stderr, "%s: %s: Unable to set UDP options: %s\n", + program, fdp->device, strerror(errno)); + exit(1); + } + break; + + default:; + } + + fdp->fdtype= fdtype; + fdp->since= now; + if (debug >= 3) printf("%s: Opened\n", fdp->device); + return 1; +} + +void closedev(network_t *np, fdtype_t fdtype) +{ + /* We no longer need a given type of device to be open. */ + fd_t *fdp; + + for (fdp= fds; fdp < arraylimit(fds); fdp++) { + if (fdp->n == np->n && (fdp->fdtype == fdtype || fdtype == FT_ALL)) { + closefd(fdp); + } + } +} + +char *ipdev(int n) +{ + /* IP device for network #n. */ + static char device[sizeof("/dev/ipNNN")]; + + sprintf(device, "/dev/ip%d", n); + return device; +} + +void set_ipconf(char *device, ipaddr_t ip, ipaddr_t mask, unsigned mtu) +{ + /* Set IP address and netmask of an IP device. */ + int fd; + nwio_ipconf_t ipconf; + + if (test > 0) return; + + if ((fd= open(device, O_RDWR)) < 0) fatal(device); + ipconf.nwic_flags= NWIC_IPADDR_SET | NWIC_NETMASK_SET; + ipconf.nwic_ipaddr= ip; + ipconf.nwic_netmask= mask; +#ifdef NWIC_MTU_SET + if (mtu != 0) { + ipconf.nwic_flags |= NWIC_MTU_SET; + ipconf.nwic_mtu= mtu; + } +#endif + if (ioctl(fd, NWIOSIPCONF, &ipconf) < 0) fatal(device); + close(fd); +} diff --git a/commands/dhcpd/dhcpd.c b/commands/dhcpd/dhcpd.c new file mode 100755 index 000000000..19b0b2c47 --- /dev/null +++ b/commands/dhcpd/dhcpd.c @@ -0,0 +1,1401 @@ +/* dhcpd 1.15 - Dynamic Host Configuration Protocol daemon. + * Author: Kees J. Bot + * 11 Jun 1999 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "arp.h" +#define EXTERN +#include "dhcpd.h" + +char *configfile= PATH_DHCPCONF; +char *poolfile= PATH_DHCPPOOL; +static char *cachefile= PATH_DHCPCACHE; +static int qflag; /* True if printing cached DHCP data. */ +static int aflag, rflag; /* True if adding or deleting pool addresses. */ + +#define BCAST_IP HTONL(0xFFFFFFFFUL) + +/* We try to play with up to this many networks. */ +#define N_NETS 32 +static unsigned n_nets; /* Actual number of networks. */ + +void report(const char *label) +{ + fprintf(stderr, "%s: %s: %s\n", program, label, strerror(errno)); +} + +void fatal(const char *label) +{ + report(label); + exit(1); +} + +void *allocate(size_t size) +{ + void *mem; + + if ((mem= malloc(size)) == nil) fatal("Can't allocate memory"); + return mem; +} + +/* Choose a DHCP xid based on the start time and network number. Not really + * random, but we don't have anything more random than the clock anyway. + */ +#define XID(np) htonl(((u32_t) (np)->start << 8) | (np)->n) + +static network_t *network[N_NETS]; + +int ifname2if(const char *name) +{ + /* Translate an interface name to a number, -1 if bad. */ + char *end; + unsigned long n; + + if (*name++ != 'i' || *name++ != 'p') return -1; + n= strtoul(name, &end, 10); + if (end == name || *end != 0) return -1; + if (n >= N_NETS) return -1; + return n; +} + +network_t *if2net(int n) +{ + /* Translate an interface number to a network struct. */ + int i; + + for (i= 0; i < n_nets; i++) { + if (network[i]->n == n) return network[i]; + } + return nil; +} + +static ipaddr_t defaultmask(ipaddr_t ip) +{ + /* Compute netmask by the oldfashioned Class rules. */ + if (B(&ip)[0] < 0x80) return HTONL(0xFF000000UL); /* Class A. */ + if (B(&ip)[0] < 0xC0) return HTONL(0xFFFF0000UL); /* Class B. */ + if (B(&ip)[0] < 0xE0) return HTONL(0xFFFFFF00UL); /* Class C. */ + return HTONL(0xFFFFFFFFUL); /* Multicast? Shouldn't happen... */ +} + +#define POOL_MAGIC HTONL(0x81F85D00UL) + +typedef struct pool { /* Dynamic pool entry. */ + u32_t magic; /* Pool file magic number. */ + ipaddr_t ip; /* IP address. */ + u32_t expire; /* When does/did the lease expire? */ + u8_t len; /* Client ID length. */ + u8_t unused[19]; /* Space for extensions. */ + u8_t clid[CLID_MAX]; /* Client ID of current/last user. */ +} pool_t; + +static int openpool(int mode) +{ + /* Open the dynamic pool and lock it, return fd on success or -1. */ + int fd; + struct flock lck; + + if ((fd= open(poolfile, mode, 0644)) < 0) { + if (errno != ENOENT) fatal(poolfile); + return -1; + } + if (mode != O_RDONLY) { + lck.l_type= F_WRLCK; + lck.l_whence= SEEK_SET; + lck.l_start= 0; + lck.l_len= 0; + if (fcntl(fd, F_SETLKW, &lck) < 0) fatal(poolfile); + } + return fd; +} + +static int readpool(int fd, pool_t *entry) +{ + /* Read one pool table entry, return true unless EOF. */ + ssize_t r; + + if ((r= read(fd, entry, sizeof(*entry))) < 0) fatal(poolfile); + if (r == 0) return 0; + + if (r != sizeof(*entry) || entry->magic != POOL_MAGIC) { + fprintf(stderr, "%s: %s: Pool table is corrupt\n", + program, poolfile); + close(fd); + return 0; + } + return 1; +} + +#if !__minix_vmd /* No fsync() for Minix. */ +#define fsync(fd) sync() +#endif + +static void writepool(int fd, pool_t *entry) +{ + /* (Over)write a pool table entry. */ + if (write(fd, entry, sizeof(*entry)) < 0 + || (entry->expire > now && fsync(fd) < 0) + ) { + fatal(poolfile); + } +} + +static ipaddr_t findpool(u8_t *client, size_t len, ipaddr_t ifip) +{ + /* Look for a client ID in the dynamic address pool within the same network + * as 'ifip'. Select an unused one for a new client if necessary. Return + * 0 if nothing is available, otherwise the IP address we can offer. + */ + int fd, found; + pool_t entry, oldest; + dhcp_t dhcp; + u8_t *pmask; + ipaddr_t mask; + + /* Any information available on the network the client is at? */ + if (!makedhcp(&dhcp, nil, 0, nil, 0, ifip, ifip, nil)) return 0; + + if ((fd= openpool(O_RDWR)) < 0) return 0; + + (void) gettag(&dhcp, DHCP_TAG_NETMASK, &pmask, nil); + memcpy(&mask, pmask, sizeof(mask)); + + oldest.expire= NEVER; + while ((found= readpool(fd, &entry))) { + /* Deleted entry? */ + if (entry.ip == 0) continue; + + /* Correct network? */ + if (((entry.ip ^ ifip) & mask) != 0) continue; + + /* Client present? */ + if (entry.len == len && memcmp(entry.clid, client, len) == 0) break; + + /* Oldest candidate for a new lease? */ + entry.expire= ntohl(entry.expire); + if (entry.expire < oldest.expire) oldest= entry; + } + close(fd); + + if (found) return entry.ip; + if (oldest.expire <= now) return oldest.ip; + return 0; +} + +static int commitpool(ipaddr_t ip, u8_t *client, size_t len, time_t expire) +{ + /* Commit a new binding to stable storage, return true on success. */ + int fd; + pool_t entry; + + if ((fd= openpool(O_RDWR)) < 0) return 0; + + do { + if (!readpool(fd, &entry)) { + close(fd); + return 0; + } + } while (entry.ip != ip); + + entry.expire= htonl(expire); + entry.len= len; + memcpy(entry.clid, client, len); + if (lseek(fd, -(off_t)sizeof(entry), SEEK_CUR) == -1) fatal(poolfile); + writepool(fd, &entry); + close(fd); + return 1; +} + +static void updatepool(int add, const char *name) +{ + /* Add a new IP address to the dynamic pool. */ + ipaddr_t ip; + int fd, i; + pool_t entry; + struct hostent *he; + off_t off, off0; + + if ((he= gethostbyname(name)) == nil || he->h_addrtype != AF_INET) { + fprintf(stderr, "%s: %s: Unknown host\n", program, name); + exit(1); + } + for (i= 0; he->h_addr_list[i] != nil; i++) {} + if (i != 1) { + fprintf(stderr, "%s: %s has %d addresses\n", program, name, i); + exit(1); + } + memcpy(&ip, he->h_addr_list[0], sizeof(ip)); + + if ((fd= openpool(O_RDWR|O_CREAT)) < 0) fatal(poolfile); + + off= 0; + off0= -1; + while (readpool(fd, &entry)) { + if (add) { + if (entry.ip == ip) { + fprintf(stderr, "%s: %s: %s is already present\n", + program, poolfile, name); + exit(1); + } + if (entry.ip == 0 && off0 == -1) off0= off; + } else { + if (entry.ip == ip) { + memset(&entry, 0, sizeof(entry)); + entry.magic= POOL_MAGIC; + entry.ip= 0; + if (lseek(fd, off, SEEK_SET) == -1) fatal(poolfile); + writepool(fd, &entry); + } + } + off+= sizeof(entry); + } + + if (add) { + if (off0 != -1 && lseek(fd, off0, SEEK_SET) == -1) fatal(poolfile); + memset(&entry, 0, sizeof(entry)); + entry.magic= POOL_MAGIC; + entry.ip= ip; + writepool(fd, &entry); + } + close(fd); +} + +static void cachedhcp(int n, dhcp_t *dp) +{ + /* Store a DHCP packet in a cache where those who care can find it. */ + static int inited; + FILE *fp; + int fd; + int mode; + + if (test > 0) return; + + if (!inited) { + /* First time, clear store and also save my pid. */ + if ((fp= fopen(PATH_DHCPPID, "w")) != nil) { + if (fprintf(fp, "%d\n", getpid()) == EOF || fclose(fp) == EOF) { + fatal(PATH_DHCPPID); + } + } + inited= 1; + mode= O_WRONLY | O_CREAT | O_TRUNC; + } else { + mode= O_WRONLY; + } + + dp->xid= htonl(now); /* To tell how old this data is. */ + + if ((fd= open(cachefile, mode, 0666)) < 0 + || lseek(fd, (off_t) n * sizeof(*dp), SEEK_SET) == -1 + || write(fd, dp, sizeof(*dp)) < 0 + || close(fd) < 0 + ) { + if (errno != ENOENT) fatal(cachefile); + } +} + +static void printdata(void) +{ + /* Show the contents of the cache and the dynamic pool. */ + int fd; + dhcp_t d; + ssize_t r; + int i; + pool_t entry; + unsigned long expire; + char delta[3*sizeof(u32_t)]; + + initdhcpconf(); + + if ((fd= open(cachefile, O_RDONLY)) < 0) fatal(cachefile); + i= 0; + while ((r= read(fd, &d, sizeof(d))) == sizeof(d)) { + if (d.yiaddr != 0) { + printf("DHCP data for network %d:\n", i); + printdhcp(&d); + } + i++; + } + if (r < 0) fatal(cachefile); + close(fd); + + if ((fd= openpool(O_RDONLY)) >= 0) { + printf("Dynamic address pool since %ld:\n", (long) now); + while (readpool(fd, &entry)) { + if (entry.ip == 0) continue; + expire= ntohl(entry.expire); + if (expire == 0) { + strcpy(delta, "unused"); + } else + if (expire == 0xFFFFFFFFUL) { + strcpy(delta, "infinite"); + } else + if (expire < now) { + sprintf(delta, "-%lu", now - expire); + } else { + sprintf(delta, "+%lu", expire - now); + } + printf("\t%-15s %8s ", inet_ntoa(entry.ip), delta); + for (i= 0; i < entry.len; i++) { + printf("%02X", entry.clid[i]); + } + fputc('\n', stdout); + } + close(fd); + } +} + +static udpport_t portbyname(const char *name) +{ + struct servent *se; + + if ((se= getservbyname(name, "udp")) == nil) { + fprintf(stderr, "%s: Unknown port \"%s\"\n", program, name); + exit(1); + } + return se->s_port; +} + +static int send(network_t *np, void *data, size_t len) +{ + /* Send out a packet using a filedescriptor that is probably in async mode, + * so first dup() a sync version, then write. Return true on success. + */ + int fd; + ssize_t r; + + if ((fd= dup(np->fdp->fd)) < 0) fatal("Can't dup()"); + if ((r= write(fd, data, len)) < 0) { + report(np->fdp->device); + sleep(10); + } + close(fd); + return r >= 0; +} + +static size_t servdhcp(network_t *np, buf_t *bp, size_t dlen) +{ + buf_t *abp= nil; + ipaddr_t cip, ifip; + u8_t defclid[1+sizeof(bp->dhcp->chaddr)]; + u8_t *pdata, *client, *class, *server, *reqip, *lease; + u32_t expire; + size_t len, cilen, calen; + int type, dyn; + u8_t atype; + static char NAKMESS[] = "IP address requested isn't yours"; + + if (test > 0) return 0; + + /* The IP address of the interface close to the client. */ + ifip= bp->dhcp->giaddr != 0 ? bp->dhcp->giaddr : np->ip; + + /* All kinds of DHCP tags. */ + if (gettag(bp->dhcp, DHCP_TAG_TYPE, &pdata, nil)) { + type= *pdata; + } else { + type= -1; /* BOOTP? */ + } + + if (!gettag(bp->dhcp, DHCP_TAG_CLIENTID, &client, &cilen)) { + defclid[0]= bp->dhcp->htype; + memcpy(defclid+1, bp->dhcp->chaddr, bp->dhcp->hlen); + client= defclid; + cilen= 1+bp->dhcp->hlen; + } + + if (!gettag(bp->dhcp, DHCP_TAG_CLASSID, &class, &calen)) { + calen= 0; + } + + if (!gettag(bp->dhcp, DHCP_TAG_SERVERID, &server, nil)) { + server= B(&np->ip); + } + + if (!gettag(bp->dhcp, DHCP_TAG_REQIP, &reqip, nil)) { + reqip= nil; + } + + /* I'm a server? Then see if I know this client. */ + if ((np->flags & NF_SERVING) + && bp->dhcp->op == DHCP_BOOTREQUEST + && between(1, bp->dhcp->hlen, sizeof(bp->dhcp->chaddr)) + && (server == nil || memcmp(server, &np->ip, sizeof(np->ip)) == 0) + ) { + get_buf(&abp); + + /* Is the client in my tables? */ + (void) makedhcp(abp->dhcp, class, calen, client, cilen, 0, ifip, nil); + cip= abp->dhcp->yiaddr; + + dyn= 0; + /* If not, do we have a dynamic address? */ + if (cip == 0 && (cip= findpool(client, cilen, ifip)) != 0) dyn= 1; + + if (type == DHCP_INFORM) { + /* The client already has an address, it just wants information. + * We only answer if we could answer a normal request (cip != 0), + * unless configured to answer anyone. + */ + if (cip != 0 || (np->flags & NF_INFORM)) cip= bp->dhcp->ciaddr; + } + + if (cip == 0 || !makedhcp(abp->dhcp, class, calen, + client, cilen, cip, ifip, nil)) { + put_buf(&abp); + } + + if (abp != nil) { + if (gettag(abp->dhcp, DHCP_TAG_LEASE, &lease, nil)) { + memcpy(&expire, lease, sizeof(expire)); + expire= now + ntohl(expire); + if (expire < now) expire= 0xFFFFFFFFUL; + } else { + if (dyn) { + /* A dynamic address must have a lease. */ + fprintf(stderr, "%s: No lease set for address %s\n", + program, inet_ntoa(cip)); + exit(1); + } + lease= nil; + expire= 0xFFFFFFFFUL; + } + + /* What does our client want, and what do we say? */ + switch (type) { + case DHCP_DISCOVER: + atype= DHCP_OFFER; + + /* Assign this address for a short moment. */ + if (dyn && !commitpool(cip, client, cilen, now + DELTA_FAST)) { + put_buf(&abp); + } + break; + + case -1:/* BOOTP */ + case DHCP_REQUEST: + case DHCP_INFORM: + atype= DHCP_ACK; + /* The address wanted must be the address we offer. */ + if ((reqip != nil && memcmp(reqip, &cip, sizeof(cip)) != 0) + || (bp->dhcp->ciaddr != 0 && bp->dhcp->ciaddr != cip) + ) { + atype= DHCP_NAK; + } else + if (dyn && type == DHCP_REQUEST) { + /* Assign this address for the duration of the lease. */ + if (!commitpool(cip, client, cilen, expire)) put_buf(&abp); + } + break; + + case DHCP_DECLINE: + /* Our client doesn't want the offered address! */ + if (dyn + && reqip != nil + && memcmp(reqip, &cip, sizeof(cip)) == 0 + ) { + int i; + + fprintf(stderr, "%s: ", program); + for (i= 0; i < cilen; i++) { + fprintf(stderr, "%02X", client[i]); + } + fprintf(stderr, " declines %s", inet_ntoa(cip)); + if (gettag(bp->dhcp, DHCP_TAG_MESSAGE, &pdata, &len)) { + fprintf(stderr, " saying: \"%.*s\"", (int)len, pdata); + } + fputc('\n', stderr); + + /* Disable address for the duration of the lease. */ + (void) commitpool(cip, nil, 0, expire); + } + put_buf(&abp); + break; + + case DHCP_RELEASE: + /* Our client is nice enough to return its address. */ + if (dyn) (void) commitpool(cip, client, cilen, now); + put_buf(&abp); + break; + + default: /* Anything else is ignored. */ + put_buf(&abp); + } + } + + if (abp != nil) { + /* Finish the return packet. */ + abp->dhcp->htype= bp->dhcp->htype; + abp->dhcp->hlen= bp->dhcp->hlen; + abp->dhcp->hops= 0; + abp->dhcp->xid= bp->dhcp->xid; + abp->dhcp->secs= 0; + abp->dhcp->flags= bp->dhcp->flags; + abp->dhcp->ciaddr= 0; + abp->dhcp->yiaddr= atype == DHCP_NAK ? 0 : cip; + if (atype == DHCP_NAK) abp->dhcp->siaddr= 0; + abp->dhcp->giaddr= bp->dhcp->giaddr; + memcpy(abp->dhcp->chaddr,bp->dhcp->chaddr,sizeof(bp->dhcp->chaddr)); + + settag(abp->dhcp, DHCP_TAG_SERVERID, &np->ip, sizeof(np->ip)); + + if (lease == nil) { + /* No lease specified? Then give an infinite lease. */ + settag(abp->dhcp, DHCP_TAG_LEASE, &expire, sizeof(expire)); + } + + if (type == DHCP_INFORM) { + /* Oops, this one has a fixed address, so no lease business. */ + abp->dhcp->yiaddr= 0; + settag(abp->dhcp, DHCP_TAG_LEASE, nil, 0); + settag(abp->dhcp, DHCP_TAG_RENEWAL, nil, 0); + settag(abp->dhcp, DHCP_TAG_REBINDING, nil, 0); + } + + if (atype == DHCP_NAK) { + /* A NAK doesn't need much. */ + memset(abp->dhcp->sname, 0, sizeof(abp->dhcp->sname)); + memset(abp->dhcp->file, 0, sizeof(abp->dhcp->file)); + memset(abp->dhcp->options, 255, sizeof(abp->dhcp->options)); + settag(abp->dhcp, DHCP_TAG_MESSAGE, NAKMESS, sizeof(NAKMESS)); + } + + settag(abp->dhcp, DHCP_TAG_TYPE, &atype, sizeof(atype)); + + /* Figure out where to send this to. */ + abp->udpio->uih_src_addr= np->ip; + abp->udpio->uih_src_port= port_server; + if (bp->dhcp->giaddr != 0) { + abp->udpio->uih_dst_addr= bp->dhcp->giaddr; + abp->udpio->uih_dst_port= port_server; + } else + if (bp->dhcp->flags & DHCP_FLAGS_BCAST) { + abp->udpio->uih_dst_addr= BCAST_IP; + abp->udpio->uih_dst_port= port_client; + } else + if (bp->udpio->uih_src_addr != 0 + && bp->udpio->uih_dst_addr == np->ip + ) { + abp->udpio->uih_dst_addr= bp->udpio->uih_src_addr; + abp->udpio->uih_dst_port= port_client; + } else { + abp->udpio->uih_dst_addr= BCAST_IP; + abp->udpio->uih_dst_port= port_client; + } + abp->udpio->uih_ip_opt_len= 0; + abp->udpio->uih_data_len= sizeof(dhcp_t); + + /* Copy the packet to the input buffer, and return the new size. */ + memcpy(bp->buf, abp->buf, sizeof(bp->buf)); + put_buf(&abp); + return sizeof(udp_io_hdr_t) + sizeof(dhcp_t); + } + } + + /* I'm a relay? If it is a not already a relayed request then relay. */ + if ((np->flags & NF_RELAYING) + && bp->dhcp->op == DHCP_BOOTREQUEST + && bp->dhcp->giaddr == 0 + ) { + bp->dhcp->giaddr= np->ip; + bp->udpio->uih_src_addr= np->ip; + bp->udpio->uih_src_port= port_server; + bp->udpio->uih_dst_addr= np->server; + bp->udpio->uih_dst_port= port_server; + return dlen; + } + + /* I'm a relay? If the server sends a reply to me then relay back. */ + if ((np->flags & NF_RELAYING) + && bp->dhcp->op == DHCP_BOOTREPLY + && bp->dhcp->giaddr == np->ip + ) { + bp->dhcp->giaddr= 0; + bp->udpio->uih_src_addr= np->ip; + bp->udpio->uih_src_port= port_server; + bp->udpio->uih_dst_addr= BCAST_IP; + bp->udpio->uih_dst_port= port_client; + return dlen; + } + + /* Don't know what to do otherwise, so doing nothing seems wise. */ + return 0; +} + +static void onsig(int sig) +{ + switch (sig) { + case SIGUSR1: debug++; break; + case SIGUSR2: debug= 0; break; + } +} + +static void usage(void) +{ + fprintf(stderr, +"Usage: %s [-qar] [-t[L]] [-d[L]] [-f config] [-c cache] [-p pool] [host ...]\n", + program); + exit(1); +} + +int main(int argc, char **argv) +{ + int i; + network_t *np; + struct sigaction sa; + ssize_t r= -1; + buf_t *bp= nil; + static struct timeval eventtv; + + program= argv[0]; + start= now= time(nil); + + debug= 0; + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++]+1; + + if (opt[0] == '-' && opt[1] == 0) break; /* -- */ + + while (*opt != 0) switch (*opt++) { + case 'f': + if (*opt == 0) { + if (i == argc) usage(); + opt= argv[i++]; + } + configfile= opt; + opt= ""; + break; + case 'c': + if (*opt == 0) { + if (i == argc) usage(); + opt= argv[i++]; + } + cachefile= opt; + opt= ""; + break; + case 'p': + if (*opt == 0) { + if (i == argc) usage(); + opt= argv[i++]; + } + poolfile= opt; + opt= ""; + break; + case 't': + test= 1; + if (between('0', *opt, '9')) test= strtoul(opt, &opt, 10); + break; + case 'd': + debug= 1; + if (between('0', *opt, '9')) debug= strtoul(opt, &opt, 10); + break; + case 'q': + qflag= 1; + break; + case 'a': + aflag= 1; + break; + case 'r': + rflag= 1; + break; + default: + usage(); + } + } + if (aflag + rflag + qflag > 1) usage(); + + if (aflag || rflag) { + /* Add or remove addresses from the dynamic pool. */ + while (i < argc) updatepool(aflag, argv[i++]); + exit(0); + } + + if (i != argc) usage(); + + if (qflag) { + /* Only show the contents of the cache and dynamic pool to the user. */ + printdata(); + exit(0); + } + + /* BOOTP ports. */ + port_server= portbyname("bootps"); + port_client= portbyname("bootpc"); + + sa.sa_handler= onsig; + sigemptyset(&sa.sa_mask); + sa.sa_flags= 0; + sigaction(SIGUSR1, &sa, nil); + sigaction(SIGUSR2, &sa, nil); + + /* Initial configuration. */ + for (i= 0; i < N_NETS; i++) { + int fd; + ipaddr_t ip, mask; + + /* Is there something there? */ + if ((fd= open(ipdev(i), O_RDWR|O_NONBLOCK)) < 0) { + if (errno != ENOENT && errno != ENODEV && errno != ENXIO) { + fatal(ipdev(i)); + } + continue; + } + close(fd); + + network[n_nets++]= np= newnetwork(); + np->n= i; + + /* Ethernet? */ + if (opendev(np, FT_ETHERNET, 1)) { + np->type= B(&np->eth)[0] != 'Z' ? NT_ETHERNET : NT_SINK; + if (debug >= 1) { + printf("%s: Ethernet address is %s%s\n", + np->fdp->device, ether_ntoa(&np->eth), + np->type == NT_SINK ? " (sink)" : ""); + } + closedev(np, FT_ETHERNET); + } + + /* Only true Ethernets worry about DHCP. */ + if (np->type != NT_ETHERNET) np->renew= np->rebind= np->lease= NEVER; + } + + /* Try to find my interfaces in the DHCP table. */ + for (i= 0; i < n_nets; i++) { + ipaddr_t cip; + u8_t clid[1+DHCP_HLEN_ETH]; + size_t cilen; + + np= network[i]; + if (np->flags & NF_BOUND) continue; + + if (np->type == NT_IP) { + cilen= 0; + } else { + ether2clid(clid, &np->eth); + cilen= 1+DHCP_HLEN_ETH; + } + + /* Try to find an Ethernet address, or the IP address of an already + * configured network. If we have data we get an IP address back. + */ + get_buf(&bp); + (void) makedhcp(bp->dhcp, (u8_t *) "Minix", 5, + clid, cilen, np->ip, 0, np); + cip= bp->dhcp->yiaddr; + + /* Gather information on the interface. */ + if (cip != 0 + && makedhcp(bp->dhcp, (u8_t *) "Minix", 5, + clid, cilen, cip, cip, np) + && test < 2 + ) { + u8_t *pdata; + u16_t mtu; + + cachedhcp(np->n, bp->dhcp); + np->ip= cip; + (void) gettag(bp->dhcp, DHCP_TAG_NETMASK, &pdata, nil); + memcpy(&np->mask, pdata, sizeof(np->mask)); + if (gettag(bp->dhcp, DHCP_TAG_GATEWAY, &pdata, nil)) { + memcpy(&np->gateway, pdata, sizeof(np->gateway)); + } else { + np->gateway= 0; + } + if (gettag(bp->dhcp, DHCP_TAG_IPMTU, &pdata, nil)) { + memcpy(&mtu, pdata, sizeof(mtu)); + mtu= ntohs(mtu); + } else { + mtu= 0; + } + set_ipconf(ipdev(np->n), np->ip, np->mask, mtu); + if (debug >= 1) { + printf("%s: IP address is %s\n", + ipdev(np->n), cidr_ntoa(np->ip, np->mask)); + } + np->flags |= NF_BOUND; + np->renew= np->rebind= np->lease= NEVER; + np->sol_ct= N_SOLICITS; + np->solicit= 0; + + /* Other (previous) interfaces may have been defined. */ + i= 0; + } + put_buf(&bp); + } + + for (;;) { + now= time(nil); + event= NEVER; + + /* Is it time to request/renew a lease? */ + for (i= 0; i < n_nets; i++) { + np= network[i]; + + if (np->renew <= now) { + u8_t type; + static u8_t taglist[] = { + DHCP_TAG_NETMASK, DHCP_TAG_GATEWAY, DHCP_TAG_DNS + }; + u8_t ethclid[1+DHCP_HLEN_ETH]; + + /* We may have lost our binding or even our lease. */ + if (np->rebind <= now) np->server= BCAST_IP; + + if (np->lease <= now) { + if (np->flags & NF_BOUND) closedev(np, FT_ALL); + + if ((np->flags & (NF_BOUND | NF_POSSESSIVE)) == NF_BOUND) { + set_ipconf(ipdev(np->n), np->ip= 0, np->mask= 0, 0); + if (debug >= 1) { + printf("%s: Interface disabled (lease expired)\n", + ipdev(np->n)); + } + } + np->flags &= ~NF_BOUND; + } + + /* See if we can open the network we need to send on. */ + if (!(np->flags & NF_BOUND)) { + if (!opendev(np, FT_ETHERNET, 1)) continue; + } else { + if (!opendev(np, FT_BOOTPC, 1)) continue; + } + + if (!(np->flags & NF_NEGOTIATING)) { + /* We need to start querying a DHCP server. */ + np->start= now; + np->delta= DELTA_FIRST; + np->flags |= NF_NEGOTIATING; + } + + /* Fill in a DHCP query packet. */ + get_buf(&bp); + dhcp_init(bp->dhcp); + bp->dhcp->op= DHCP_BOOTREQUEST; + bp->dhcp->htype= DHCP_HTYPE_ETH; + bp->dhcp->hlen= DHCP_HLEN_ETH; + bp->dhcp->xid= XID(np); + bp->dhcp->secs= htons(now - np->start > 0xFFFF + ? 0xFFFF : now - np->start); + memcpy(bp->dhcp->chaddr, &np->eth, sizeof(np->eth)); + + if (np->lease <= now) { + /* First time, or my old server is unresponsive. */ + type= DHCP_DISCOVER; + } else { + /* Request an offered address or renew an address. */ + type= DHCP_REQUEST; + if (np->flags & NF_BOUND) { + /* A renewal, I claim my current address. */ + bp->dhcp->ciaddr= np->ip; + } else { + /* Nicely ask for the address just offered. */ + settag(bp->dhcp, DHCP_TAG_REQIP, &np->ip, + sizeof(np->ip)); + settag(bp->dhcp, DHCP_TAG_SERVERID, &np->server, + sizeof(np->server)); + } + } + settag(bp->dhcp, DHCP_TAG_TYPE, &type, 1); + + /* My client ID. Simply use the default. */ + ether2clid(ethclid, &np->eth); + settag(bp->dhcp, DHCP_TAG_CLIENTID, ethclid, sizeof(ethclid)); + + /* The Class ID may serve to recognize Minix hosts. */ + settag(bp->dhcp, DHCP_TAG_CLASSID, "Minix", 5); + + /* The few tags that Minix can make good use of. */ + settag(bp->dhcp, DHCP_TAG_REQPAR, taglist, sizeof(taglist)); + + /* Some weird sites use a hostname, not a client ID. */ + if (np->hostname != nil) { + settag(bp->dhcp, DHCP_TAG_HOSTNAME, + np->hostname, strlen(np->hostname)); + } + + bp->udpio->uih_src_addr= np->ip; + bp->udpio->uih_dst_addr= np->server; + bp->udpio->uih_src_port= port_client; + bp->udpio->uih_dst_port= port_server; + bp->udpio->uih_ip_opt_len= 0; + bp->udpio->uih_data_len= sizeof(dhcp_t); + + if (!(np->flags & NF_BOUND)) { + /* Rebind over Ethernet. */ + udp2ether(bp, np); + if (send(np, bp->eth, sizeof(eth_hdr_t) + sizeof(ip_hdr_t) + + sizeof(udp_hdr_t) + sizeof(dhcp_t))) { + if (debug >= 1) { + printf("%s: Broadcast DHCP %s\n", + np->fdp->device, dhcptypename(type)); + if (debug >= 2) printdhcp(bp->dhcp); + } + } + } else { + /* Renew over UDP. */ + if (send(np, bp->udpio, sizeof(udp_io_hdr_t) + + sizeof(dhcp_t))) { + if (debug >= 1) { + printf("%s: Sent DHCP %s to %s\n", + np->fdp->device, + dhcptypename(type), + inet_ntoa(np->server)); + if (debug >= 2) printdhcp(bp->dhcp); + } + } + } + put_buf(&bp); + + /* When to continue querying a DHCP server? */ + if (np->flags & NF_BOUND) { + /* Still bound, keep halving time till next event. */ + time_t e, d; + + e= now < np->rebind ? np->rebind : np->lease; + d= (e - now) / 2; + if (d < DELTA_SLOW) d= DELTA_SLOW; + np->renew= now + d; + if (np->renew > e) np->renew= e; + } else { + /* Not bound, be desparate. */ + np->renew= now + np->delta; + if ((np->delta *= 2) > DELTA_FAST) np->delta= DELTA_FAST; + } + } + if (np->renew < event) event= np->renew; + } + + /* Read DHCP responses. */ + for (i= 0; i < n_nets; i++) { + np= network[i]; + if (!(np->flags & NF_NEGOTIATING)) continue; + + if (!(np->flags & NF_BOUND)) { + if (!opendev(np, FT_ETHERNET, 0)) continue; + get_buf(&np->fdp->bp); + r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->eth, + BUF_ETH_SIZE); + } else { + if (!opendev(np, FT_BOOTPC, 0)) continue; + get_buf(&np->fdp->bp); + r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->udpio, + BUF_UDP_SIZE); + } + if (r != -1) break; + if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) { + report(np->fdp->device); + sleep(10); + } + } + + /* Is there a response? */ + if (i < n_nets) { + give_buf(&bp, &np->fdp->bp); + if (((!(np->flags & NF_BOUND) + && r >= (sizeof(eth_hdr_t) + sizeof(ip_hdr_t) + + sizeof(udp_hdr_t) + offsetof(dhcp_t, options)) + && ether2udp(bp) + && bp->udpio->uih_dst_port == port_client) + || + ((np->flags & NF_BOUND) + && r >= sizeof(udp_io_hdr_t) + offsetof(dhcp_t, options))) + && bp->dhcp->op == DHCP_BOOTREPLY + && bp->dhcp->htype == DHCP_HTYPE_ETH + && bp->dhcp->hlen == DHCP_HLEN_ETH + && bp->dhcp->xid == XID(np) + && memcmp(bp->dhcp->chaddr, &np->eth, sizeof(np->eth)) == 0 + ) { + /* Pfew! We got a DHCP reply! */ + u8_t *pdata; + size_t len; + int type; + ipaddr_t mask, gateway, relay, server; + u16_t mtu; + u32_t lease, renew, rebind, t; + + relay= bp->udpio->uih_src_addr; + if (gettag(bp->dhcp, DHCP_TAG_SERVERID, &pdata, nil)) { + memcpy(&server, pdata, sizeof(server)); + } else { + server= relay; + } + + if (gettag(bp->dhcp, DHCP_TAG_TYPE, &pdata, nil)) { + type= pdata[0]; + } else { + type= DHCP_ACK; /* BOOTP? */ + } + + if (debug >= 1) { + printf("%s: Got a DHCP %s from %s", + np->fdp->device, dhcptypename(type), inet_ntoa(server)); + printf(relay != server ? " through %s\n" : "\n", + inet_ntoa(relay)); + if (debug >= 2) printdhcp(bp->dhcp); + } + + if (gettag(bp->dhcp, DHCP_TAG_NETMASK, &pdata, nil)) { + memcpy(&mask, pdata, sizeof(mask)); + } else { + mask= defaultmask(bp->dhcp->ciaddr); + } + + if (gettag(bp->dhcp, DHCP_TAG_IPMTU, &pdata, nil)) { + memcpy(&mtu, pdata, sizeof(mtu)); + mtu= ntohs(mtu); + } else { + mtu= 0; + } + + if (gettag(bp->dhcp, DHCP_TAG_GATEWAY, &pdata, nil)) { + memcpy(&gateway, pdata, sizeof(gateway)); + } else { + gateway= 0; + } + + lease= NEVER; + if (gettag(bp->dhcp, DHCP_TAG_LEASE, &pdata, nil)) { + memcpy(&lease, pdata, sizeof(lease)); + lease= ntohl(lease); + } + + rebind= lease - lease / 8; + if (gettag(bp->dhcp, DHCP_TAG_REBINDING, &pdata, nil)) { + memcpy(&t, pdata, sizeof(t)); + t= ntohl(t); + if (t < rebind) rebind= t; + } + + renew= lease / 2; + if (gettag(bp->dhcp, DHCP_TAG_RENEWAL, &pdata, nil)) { + memcpy(&t, pdata, sizeof(t)); + t= ntohl(t); + if (t < renew) renew= t; + } + + if (type == DHCP_OFFER && np->rebind <= np->renew) { + /* It's an offer for an address and we haven't taken one + * yet. It's all the same to us, so take this one. + */ + np->ip= bp->dhcp->yiaddr; + np->mask= mask; + np->server= server; + np->gateway= gateway; + np->delta= DELTA_FIRST; + np->renew= now; + np->rebind= np->lease= now + DELTA_FAST; + + /* Send out an ARP request to see if the offered address + * is in use already. + */ + make_arp(bp, np); + if (send(np, bp->eth, sizeof(arp46_t))) { + if (debug >= 2) { + printf("Sent ARP for %s\n", inet_ntoa(np->ip)); + } + } + np->flags &= ~NF_CONFLICT; + } + + if (type == DHCP_ACK && !(np->flags & NF_CONFLICT)) { + /* An acknowledgment. The address is all mine. */ + cachedhcp(np->n, bp->dhcp); + np->ip= bp->dhcp->yiaddr; + np->mask= mask; + np->server= server; + set_ipconf(ipdev(np->n), np->ip, np->mask, mtu); + if (debug >= 1) { + printf("%s: Address set to %s\n", + ipdev(np->n), cidr_ntoa(np->ip, np->mask)); + } + if (lease >= NEVER - now) { + /* The lease is infinite! */ + np->renew= np->rebind= np->lease= NEVER; + } else { + np->lease= now + lease; + np->renew= now + renew; + np->rebind= now + rebind; + } + if (test >= 3) { + np->renew= now + 60; + np->rebind= test >= 4 ? np->renew : np->renew + 60; + np->lease= test >= 5 ? np->rebind : np->rebind + 60; + } + if (!(np->flags & NF_IRDP)) { + np->sol_ct= (np->flags & NF_BOUND) ? 1 : N_SOLICITS; + np->solicit= 0; + } + np->flags &= ~NF_NEGOTIATING; + np->flags |= NF_BOUND; + closedev(np, FT_ETHERNET); + closedev(np, FT_BOOTPC); + } + + if (type == DHCP_ACK && (np->flags & NF_CONFLICT)) { + /* Alas there is a conflict. Decline to use the address. */ + u8_t ethclid[1+DHCP_HLEN_ETH]; + static char USED[]= "Address in use by 00:00:00:00:00:00"; + + type= DHCP_DECLINE; + dhcp_init(bp->dhcp); + bp->dhcp->op= DHCP_BOOTREQUEST; + bp->dhcp->htype= DHCP_HTYPE_ETH; + bp->dhcp->hlen= DHCP_HLEN_ETH; + bp->dhcp->xid= XID(np); + bp->dhcp->secs= 0; + memcpy(bp->dhcp->chaddr, &np->eth, sizeof(np->eth)); + settag(bp->dhcp, DHCP_TAG_REQIP, &np->ip, sizeof(np->ip)); + settag(bp->dhcp, DHCP_TAG_TYPE, &type, 1); + ether2clid(ethclid, &np->eth); + settag(bp->dhcp, DHCP_TAG_CLIENTID,ethclid,sizeof(ethclid)); + strcpy(USED+18, ether_ntoa(&np->conflict)); + settag(bp->dhcp, DHCP_TAG_MESSAGE, USED, strlen(USED)); + + bp->udpio->uih_src_port= port_client; + bp->udpio->uih_dst_port= port_server; + bp->udpio->uih_ip_opt_len= 0; + bp->udpio->uih_data_len= sizeof(dhcp_t); + udp2ether(bp, np); + + if (send(np, bp->eth, sizeof(eth_hdr_t) + sizeof(ip_hdr_t) + + sizeof(udp_hdr_t) + sizeof(dhcp_t))) { + if (debug >= 1) { + printf("%s: Broadcast DHCP %s\n", + np->fdp->device, dhcptypename(type)); + if (debug >= 2) printdhcp(bp->dhcp); + } + } + + np->renew= np->rebind= np->lease= now + DELTA_FAST; + np->delta= DELTA_FIRST; + } + + if (type == DHCP_NAK) { + /* Oops, a DHCP server doesn't like me, start over! */ + np->renew= np->rebind= np->lease= now + DELTA_FAST; + np->delta= DELTA_FIRST; + + fprintf(stderr, "%s: Got a NAK from %s", + program, inet_ntoa(server)); + if (relay != server) { + fprintf(stderr, " through %s", inet_ntoa(relay)); + } + if (gettag(bp->dhcp, DHCP_TAG_MESSAGE, &pdata, &len)) { + fprintf(stderr, " saying: \"%.*s\"", (int)len, pdata); + } + fputc('\n', stderr); + } + } else + if (!(np->flags & NF_BOUND) + && np->rebind > now + && r >= sizeof(arp46_t) + && is_arp_me(bp, np) + ) { + /* Oh no, someone else is using the address offered to me! */ + np->flags |= NF_CONFLICT; + + fprintf(stderr, "%s: %s: %s offered by ", + program, + np->fdp->device, + inet_ntoa(np->ip)); + fprintf(stderr, "%s is already in use by %s\n", + inet_ntoa(np->server), + ether_ntoa(&np->conflict)); + } + put_buf(&bp); + if (np->renew < event) event= np->renew; + } + + /* Perform router solicitations. */ + for (i= 0; i < n_nets; i++) { + np= network[i]; + if (!(np->flags & NF_BOUND)) continue; + + if (np->solicit <= now) { + if (!opendev(np, FT_ICMP, 1)) continue; + np->solicit= NEVER; + + get_buf(&bp); + if (np->gateway != 0) { + /* No IRDP response seen yet, advertise the router given + * by DHCP to my own interface. + */ + icmp_advert(bp, np); + if (send(np, bp->ip, sizeof(ip_hdr_t) + 16)) { + if (debug >= 2) { + printf("%s: Sent advert for %s to self\n", + np->fdp->device, inet_ntoa(np->gateway)); + } + } + np->solicit= now + DELTA_ADV/2; + } + + if (np->sol_ct >= 0 && --np->sol_ct >= 0) { + /* Send a router solicitation. */ + icmp_solicit(bp); + if (send(np, bp->ip, sizeof(*bp->ip) + 8)) { + if (debug >= 2) { + printf("%s: Broadcast router solicitation\n", + np->fdp->device); + } + } + np->solicit= now + DELTA_SOL; + } else { + /* No response, or not soliciting right now. */ + closedev(np, FT_ICMP); + } + + put_buf(&bp); + } + if (np->solicit < event) event= np->solicit; + } + + /* Read router adverts. */ + for (i= 0; i < n_nets; i++) { + np= network[i]; + if (!(np->flags & NF_BOUND)) continue; + if (np->sol_ct < 0) continue; + + if (!opendev(np, FT_ICMP, 0)) continue; + get_buf(&np->fdp->bp); + r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->ip, BUF_IP_SIZE); + if (r != -1) break; + if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) { + report(np->fdp->device); + sleep(10); + } + } + + /* Is there an advert? */ + if (i < n_nets && r >= sizeof(ip_hdr_t) + 8) { + ipaddr_t router; + + give_buf(&bp, &np->fdp->bp); + if ((router= icmp_is_advert(bp)) != 0) { + if (debug >= 2) { + printf("%s: Router advert received from %s\n", + np->fdp->device, inet_ntoa(router)); + } + np->solicit= NEVER; + np->sol_ct= -1; + np->flags |= NF_IRDP; + closedev(np, FT_ICMP); + } + put_buf(&bp); + } + + /* We start serving if all the interfaces so marked are configured. */ + for (i= 0; i < n_nets; i++) { + np= network[i]; + if ((np->flags & NF_RELAYING) && (np->flags & NF_BOUND)) { + if (((np->ip ^ np->server) & np->mask) == 0) { + /* Don't relay to a server that is on this same net. */ + np->flags &= ~NF_RELAYING; + } + } + if (!(np->flags & (NF_SERVING|NF_RELAYING))) continue; + if (!(np->flags & NF_BOUND)) { serving= 0; break; } + serving= 1; + } + + /* Read DHCP requests. */ + for (i= 0; i < n_nets; i++) { + np= network[i]; + if (!(np->flags & NF_BOUND)) continue; + if (!(np->flags & (NF_SERVING|NF_RELAYING)) || !serving) continue; + + if (!opendev(np, FT_BOOTPS, 0)) continue; + get_buf(&np->fdp->bp); + r= asyn_read(&asyn, np->fdp->fd, np->fdp->bp->udpio, BUF_UDP_SIZE); + + if (r != -1) break; + if (errno != ASYN_INPROGRESS && errno != EPACKSIZE) { + report(np->fdp->device); + sleep(10); + } + } + + /* Is there a request? */ + if (i < n_nets + && r >= sizeof(udp_io_hdr_t) + offsetof(dhcp_t, options) + ) { + give_buf(&bp, &np->fdp->bp); + + if (debug >= 1) { + printf("%s: Got DHCP packet from %s to ", + np->fdp->device, inet_ntoa(bp->udpio->uih_src_addr)); + printf("%s\n", inet_ntoa(bp->udpio->uih_dst_addr)); + if (debug >= 2) printdhcp(bp->dhcp); + } + + /* Can we do something with this DHCP packet? */ + if ((r= servdhcp(np, bp, r)) > 0) { + /* Yes, we have something to send somewhere. */ + if (send(np, bp->udpio, r)) { + if (debug >= 1) { + printf("%s: Sent DHCP packet to %s\n", + np->fdp->device, + inet_ntoa(bp->udpio->uih_dst_addr)); + if (debug >= 2) printdhcp(bp->dhcp); + } + } + } + put_buf(&bp); + } + + if (debug >= 1) { + static char *lastbrk; + extern char _end; + + if (sbrk(0) != lastbrk) { + lastbrk= sbrk(0); + printf("Memory use = %lu\n", + (unsigned long) (lastbrk - &_end)); + } + fflush(stdout); + } + + /* Bail out if not a server, and there is nothing else to do ever. */ + if (!serving && event == NEVER) break; + + /* Wait for something to do. */ + eventtv.tv_sec= event; + if (asyn_wait(&asyn, 0, event == NEVER ? nil : &eventtv) < 0) { + if (errno != EINTR) { + report("asyn_wait()"); + sleep(10); + } + } + } + if (debug >= 1) printf("Nothing more to do! Bailing out...\n"); + return 0; +} diff --git a/commands/dhcpd/dhcpd.h b/commands/dhcpd/dhcpd.h new file mode 100755 index 000000000..06badf55a --- /dev/null +++ b/commands/dhcpd/dhcpd.h @@ -0,0 +1,157 @@ +/* dhcpd.h - Dynamic Host Configuration Protocol daemon. + * Author: Kees J. Bot + * 16 Dec 2000 + */ + +#define nil ((void*)0) + +/* Paths to files. */ +#define PATH_DHCPCONF "/etc/dhcp.conf" +#define PATH_DHCPPID "/usr/run/dhcpd.pid" +#define PATH_DHCPCACHE "/usr/adm/dhcp.cache" +#define PATH_DHCPPOOL "/usr/adm/dhcp.pool" + +#define CLID_MAX 32 /* Maximum client ID length. */ + +#ifndef EXTERN +#define EXTERN extern +#endif + +EXTERN char *program; /* This program's name. */ +extern char *configfile; /* Configuration file. */ +extern char *poolfile; /* Dynamic address pool. */ +EXTERN int serving; /* True if being a DHCP server. */ +EXTERN unsigned test; /* Test level. */ +EXTERN unsigned debug; /* Debug level. */ +EXTERN asynchio_t asyn; /* Bookkeeping for all async I/O. */ + +/* BOOTP UDP ports: (That they are different is quite stupid.) */ +EXTERN u16_t port_server; /* Port server listens on. */ +EXTERN u16_t port_client; /* Port client listens on. */ + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) +#define between(a,c,z) (sizeof(c) <= sizeof(unsigned) ? \ + (unsigned) (c) - (a) <= (unsigned) (z) - (a) : \ + (unsigned long) (c) - (a) <= (unsigned long) (z) - (a)) + +/* To treat objects as octet arrays: */ +#define B(a) ((u8_t *) (a)) + +/* Times. */ +EXTERN time_t start, now; /* Start and current time. */ +EXTERN time_t event; /* Time of the next timed event. */ + +/* Special times and periods: */ +#define NEVER (sizeof(time_t) <= sizeof(int) ? INT_MAX : LONG_MAX) +#define DELTA_FIRST 4 /* Between first and second query. */ +#define DELTA_FAST 64 /* Unbound queries this often. */ +#define DELTA_SLOW 512 /* Bound queries are more relaxed. */ +#define N_SOLICITS 3 /* Number of solicitations. */ +#define DELTA_SOL 3 /* Time between solicitations. */ +#define DELTA_ADV 2048 /* Router adverts to self lifetime. */ + +/* Buffers for packets. */ +typedef struct buf { + eth_hdr_t *eth; /* Ethernet header in payload. */ + ip_hdr_t *ip; /* IP header in payload. */ + udp_hdr_t *udp; /* UDP header in payload. */ + udp_io_hdr_t *udpio; /* UDP I/O header in payload. */ + dhcp_t *dhcp; /* DHCP data in payload. */ + u8_t pad[2]; /* buf[] must start at 2 mod 4. */ + /* Payload: */ + u8_t buf[ETH_MAX_PACK_SIZE]; +} buf_t; + +#define BUF_ETH_SIZE (ETH_MAX_PACK_SIZE) +#define BUF_IP_SIZE (BUF_ETH_SIZE - sizeof(eth_hdr_t)) +#define BUF_UDP_SIZE (BUF_IP_SIZE - sizeof(ip_hdr_t) - sizeof(udp_hdr_t) \ + + sizeof(udp_io_hdr_t)) + +/* Type of network device open: Ethernet, ICMP, BOOTP client, BOOTP server. */ +typedef enum { FT_CLOSED, FT_ETHERNET, FT_ICMP, FT_BOOTPC, FT_BOOTPS } fdtype_t; + +#define FT_ALL FT_CLOSED /* To close all open descriptors at once. */ + +typedef struct fd { /* An open descriptor. */ + i8_t fd; /* Open descriptor. */ + u8_t fdtype; /* Type of network open. */ + char device[sizeof("/dev/eth###")]; /* Device name. */ + u8_t n; /* Network that owns it. */ + buf_t *bp; /* Associated packet buffer. */ + time_t since; /* Open since when? */ +} fd_t; + +/* Network state: Any IP device, Ethernet in sink mode, True Ethernet. */ +typedef enum { NT_IP, NT_SINK, NT_ETHERNET } nettype_t; + +typedef struct network { /* Information on a network. */ + u8_t n; /* Network number. */ + ether_addr_t eth; /* Ethernet address of this net. */ + u8_t type; /* What kind of net is this? */ + i8_t sol_ct; /* Router solicitation count. */ + ether_addr_t conflict; /* Address conflict with this one. */ + unsigned flags; /* Various flags. */ + fd_t *fdp; /* Current open device. */ + struct network *wait; /* Wait for a resource list. */ + ipaddr_t ip; /* IP address of this net. */ + ipaddr_t mask; /* Associated netmask. */ + ipaddr_t gateway; /* My router. */ + ipaddr_t server; /* My DHCP server. */ + const char *hostname; /* Optional hostname to query for. */ + time_t start; /* Query or lease start time. */ + time_t delta; /* Query again after delta seconds. */ + time_t renew; /* Next query or go into renewal. */ + time_t rebind; /* When to go into rebind. */ + time_t lease; /* When our lease expires. */ + time_t solicit; /* Time to do a router solicitation. */ +} network_t; + +/* Flags. */ +#define NF_NEGOTIATING 0x001 /* Negotiating with a DHCP server. */ +#define NF_BOUND 0x002 /* Address configured through DHCP. */ +#define NF_SERVING 0x004 /* I'm a server on this network. */ +#define NF_RELAYING 0x008 /* I'm relaying for this network. */ +#define NF_WAIT 0x010 /* Wait for a resource to free up. */ +#define NF_IRDP 0x020 /* IRDP is used on this net. */ +#define NF_CONFLICT 0x040 /* There is an address conflict. */ +#define NF_POSSESSIVE 0x080 /* Keep address if lease expires. */ +#define NF_INFORM 0x100 /* It's ok to answer DHCPINFORM. */ + +/* Functions defined in dhcpd.c. */ +void report(const char *label); +void fatal(const char *label); +void *allocate(size_t size); +int ifname2if(const char *name); +network_t *if2net(int n); + +/* Devices.c */ +void get_buf(buf_t **bp); +void put_buf(buf_t **bp); +void give_buf(buf_t **dbp, buf_t **sbp); +network_t *newnetwork(void); +void closefd(fd_t *fdp); +int opendev(network_t *np, fdtype_t fdtype, int compete); +void closedev(network_t *np, fdtype_t fdtype); +char *ipdev(int n); +void set_ipconf(char *device, ipaddr_t ip, ipaddr_t mask, unsigned mtu); + +/* Ether.c */ +void udp2ether(buf_t *bp, network_t *np); +int ether2udp(buf_t *bp); +void make_arp(buf_t *bp, network_t *np); +int is_arp_me(buf_t *bp, network_t *np); +void icmp_solicit(buf_t *bp); +void icmp_advert(buf_t *bp, network_t *np); +ipaddr_t icmp_is_advert(buf_t *bp); + +/* Tags.c */ +#define gettag(dp, st, pd, pl) dhcp_gettag((dp), (st), (pd), (pl)) +void settag(dhcp_t *dp, int tag, void *data, size_t len); +char *cidr_ntoa(ipaddr_t addr, ipaddr_t mask); +void ether2clid(u8_t *clid, ether_addr_t *eth); +void initdhcpconf(void); +int makedhcp(dhcp_t *dp, u8_t *class, size_t calen, u8_t *client, size_t cilen, + ipaddr_t ip, ipaddr_t ifip, network_t *np); +char *dhcptypename(int type); +void printdhcp(dhcp_t *dp); diff --git a/commands/dhcpd/ether.c b/commands/dhcpd/ether.c new file mode 100755 index 000000000..c17661b0f --- /dev/null +++ b/commands/dhcpd/ether.c @@ -0,0 +1,203 @@ +/* ether.c - Raw Ethernet stuff + * Author: Kees J. Bot + * 16 Dec 2000 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "arp.h" +#include "dhcpd.h" + +static ether_addr_t BCAST_ETH = {{ 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }}; +#define BCAST_IP HTONL(0xFFFFFFFFUL) +#define LOCALHOST HTONL(0x7F000001UL) + +static u16_t udp_cksum(ipaddr_t src, ipaddr_t dst, udp_hdr_t *udp) +{ + /* Compute the checksum of an UDP packet plus data. */ + struct udp_pseudo { + ipaddr_t src, dst; + u8_t zero, proto; + u16_t length; + } pseudo; + size_t len; + + /* Fill in the UDP pseudo header that must be prefixed to the UDP + * packet to compute the checksum of the whole thing. + */ + pseudo.src= src; + pseudo.dst= dst; + pseudo.zero= 0; + pseudo.proto= IPPROTO_UDP; + pseudo.length= udp->uh_length; + + len= ntohs(udp->uh_length); + if (len & 1) { + /* Length is odd? Pad with a zero. */ + B(udp)[len++]= 0; + } + return oneC_sum(oneC_sum(0, &pseudo, sizeof(pseudo)), udp, len); +} + +void udp2ether(buf_t *bp, network_t *np) +{ + /* Transform a packet in UDP format to raw Ethernet. Ignore the UDP + * addresses, always broadcast from 0.0.0.0. + */ + udp_io_hdr_t udpio; + + /* Save the UDP I/O header. */ + udpio= *bp->udpio; + + /* Fill in the Ethernet, IP and UDP headers. */ + bp->eth->eh_dst= BCAST_ETH; + bp->eth->eh_src= np->eth; + bp->eth->eh_proto= HTONS(ETH_IP_PROTO); + bp->ip->ih_vers_ihl= 0x45; + bp->ip->ih_tos= 0; + bp->ip->ih_length= htons(sizeof(ip_hdr_t) + + sizeof(udp_hdr_t) + udpio.uih_data_len); + bp->ip->ih_id= 0; + bp->ip->ih_flags_fragoff= NTOHS(0x4000); + bp->ip->ih_ttl= IP_MAX_TTL; + bp->ip->ih_proto= IPPROTO_UDP; + bp->ip->ih_hdr_chk= 0; + bp->ip->ih_src= 0; + bp->ip->ih_dst= BCAST_IP; + bp->ip->ih_hdr_chk= ~oneC_sum(0, bp->ip, sizeof(*bp->ip)); + bp->udp->uh_src_port= udpio.uih_src_port; + bp->udp->uh_dst_port= udpio.uih_dst_port; + bp->udp->uh_length= htons(sizeof(udp_hdr_t) + udpio.uih_data_len); + bp->udp->uh_chksum= 0; + bp->udp->uh_chksum= ~udp_cksum(bp->ip->ih_src, bp->ip->ih_dst, bp->udp); +} + +int ether2udp(buf_t *bp) +{ + /* Transform an UDP packet read from raw Ethernet to normal UDP. + * Return true iff the packet is indeed UDP and has no errors. + */ + udp_io_hdr_t udpio; + + if (bp->eth->eh_proto != HTONS(ETH_IP_PROTO) + || bp->ip->ih_vers_ihl != 0x45 + || bp->ip->ih_proto != IPPROTO_UDP + || oneC_sum(0, bp->ip, 20) != (u16_t) ~0 + || udp_cksum(bp->ip->ih_src, bp->ip->ih_dst, bp->udp) != (u16_t) ~0 + ) { + /* Not UDP/IP or checksums bad. */ + return 0; + } + udpio.uih_src_addr= bp->ip->ih_src; + udpio.uih_dst_addr= bp->ip->ih_dst; + udpio.uih_src_port= bp->udp->uh_src_port; + udpio.uih_dst_port= bp->udp->uh_dst_port; + udpio.uih_ip_opt_len= 0; + udpio.uih_data_len= ntohs(bp->udp->uh_length) - sizeof(udp_hdr_t); + *bp->udpio= udpio; + return 1; +} + +void make_arp(buf_t *bp, network_t *np) +{ + /* Create an ARP packet to query for my IP address. */ + arp46_t *arp= (arp46_t *) bp->eth; + + memset(arp, 0, sizeof(*arp)); + arp->dstaddr= BCAST_ETH; + arp->srcaddr= np->eth; + arp->ethtype= HTONS(ETH_ARP_PROTO); + arp->hdr= HTONS(ARP_ETHERNET); + arp->pro= HTONS(ETH_IP_PROTO); + arp->hln= 6; + arp->pln= 4; + arp->op= HTONS(ARP_REQUEST); + arp->sha= np->eth; + memcpy(arp->spa, &np->ip, sizeof(np->ip)); + memcpy(arp->tpa, &np->ip, sizeof(np->ip)); +} + +int is_arp_me(buf_t *bp, network_t *np) +{ + /* True iff an ARP packet is a reply from someone else with an address I + * thought was mine. (That's like, bad.) + */ + arp46_t *arp= (arp46_t *) bp->eth; + + if (arp->ethtype == HTONS(ETH_ARP_PROTO) + && arp->hdr == HTONS(ARP_ETHERNET) + && arp->pro == HTONS(ETH_IP_PROTO) + && arp->op == HTONS(ARP_REPLY) + && memcmp(&arp->spa, &np->ip, sizeof(np->ip)) == 0 + && memcmp(&arp->sha, &np->eth, sizeof(np->eth)) != 0 + ) { + np->conflict= arp->sha; + return 1; + } + return 0; +} + +void icmp_solicit(buf_t *bp) +{ + /* Fill in a router solicitation ICMP packet. */ + icmp_hdr_t *icmp= (icmp_hdr_t *) (bp->ip + 1); + + bp->ip->ih_vers_ihl= 0x45; + bp->ip->ih_dst= BCAST_IP; + + icmp->ih_type= ICMP_TYPE_ROUTE_SOL; + icmp->ih_code= 0; + icmp->ih_hun.ihh_unused= 0; + icmp->ih_chksum= 0; + icmp->ih_chksum= ~oneC_sum(0, icmp, 8); +} + +void icmp_advert(buf_t *bp, network_t *np) +{ + /* Fill in a router advert to be sent to my own interface. */ + icmp_hdr_t *icmp= (icmp_hdr_t *) (bp->ip + 1); + + bp->ip->ih_vers_ihl= 0x45; + bp->ip->ih_dst= LOCALHOST; + + icmp->ih_type= ICMP_TYPE_ROUTER_ADVER; + icmp->ih_code= 0; + icmp->ih_hun.ihh_ram.iram_na= 1; + icmp->ih_hun.ihh_ram.iram_aes= 2; + icmp->ih_hun.ihh_ram.iram_lt= htons(DELTA_ADV); + ((u32_t *) icmp->ih_dun.uhd_data)[0] = np->gateway; + ((u32_t *) icmp->ih_dun.uhd_data)[1] = HTONL((u32_t) -9999); + icmp->ih_chksum= 0; + icmp->ih_chksum= ~oneC_sum(0, icmp, 16); +} + +ipaddr_t icmp_is_advert(buf_t *bp) +{ + /* Check if an IP packet is a router advertisement, and if it's genuine, + * i.e. the sender is mentioned in the packet. + */ + icmp_hdr_t *icmp= (icmp_hdr_t *) (bp->ip + 1); + int i; + + if (icmp->ih_type == ICMP_TYPE_ROUTER_ADVER) { + for (i= 0; i < icmp->ih_hun.ihh_ram.iram_na; i++) { + if (((u32_t *) icmp->ih_dun.uhd_data)[2*i] == bp->ip->ih_src) { + /* It's a router! */ + return bp->ip->ih_src; + } + } + } + return 0; +} diff --git a/commands/dhcpd/tags.c b/commands/dhcpd/tags.c new file mode 100755 index 000000000..e16e2b292 --- /dev/null +++ b/commands/dhcpd/tags.c @@ -0,0 +1,924 @@ +/* tags.c - Obtain DHCP tags from the config file + * Author: Kees J. Bot + * 16 Dec 2000 + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dhcpd.h" + +#define doff(field) offsetof(dhcp_t, field) + +void settag(dhcp_t *dp, int tag, void *data, size_t len) +{ + if (!dhcp_settag(dp, tag, data, len)) { + /* Oops, it didn't fit? Is this really Minix??? */ + fprintf(stderr, + "%s: DHCP packet too big, please trim the configuration\n", + program); + exit(1); + } +} + +static int name2ip(ipaddr_t *pip, const char *name, ipaddr_t ifip) +{ + /* Translate a name to an IP address, preferably from the hosts file, + * but also from the DNS if being a server. Prefer the address closest + * to the interface with IP address 'ifip' if there are choices.. + */ + extern struct hostent *_gethostent(void); /* File reading versions. */ + extern void _endhostent(void); + struct hostent *he; + size_t len= strlen(name); + u32_t d, distance= -1; + ipaddr_t ip; + int i; + char *hn; + + /* Already an IP address? */ + if (inet_aton(name, pip)) return 1; + + /* In the hosts file? */ + while ((he= _gethostent()) != nil) { + hn= he->h_name; + i= -1; + do { + if (strncasecmp(name, hn, len) == 0 + && (hn[len] == 0 || hn[len] == '.') + ) { + memcpy(&ip, he->h_addr, sizeof(ip)); + d= ntohl(ip) ^ ntohl(ifip); + if (d < distance) { + *pip= ip; + distance= d; + } + break; + } + } while ((hn= he->h_aliases[++i]) != nil); + } + _endhostent(); + if (distance < -1) return 1; + + /* Nothing? Try the real DNS if being a server. */ + if (serving) { + if ((he= gethostbyname(name)) != nil && he->h_addrtype == AF_INET) { + /* Select the address closest to 'ifip'. */ + for (i= 0; he->h_addr_list[i] != nil; i++) { + memcpy(&ip, he->h_addr_list[i], sizeof(ip)); + d= ntohl(ip) ^ ntohl(ifip); + if (d < distance) { + *pip= ip; + distance= d; + } + } + return 1; + } + } + return 0; +} + +static char *ip2name(ipaddr_t ip) +{ + /* Translate an IP address to a name, etc, etc. */ + extern struct hostent *_gethostent(void); /* File reading versions. */ + extern void _endhostent(void); + struct hostent *he; + + /* In the hosts file? */ + while ((he= _gethostent()) != nil) { + if (memcmp(he->h_addr, &ip, sizeof(ip)) == 0) break; + } + _endhostent(); + + /* Nothing? Try the real DNS if being a server. */ + if (he == nil && serving) { + he= gethostbyaddr((char *) &ip, sizeof(ip), AF_INET); + } + return he != nil ? he->h_name : nil; +} + +static int cidr_aton(const char *cidr, ipaddr_t *addr, ipaddr_t *mask) +{ + char *slash, *check; + ipaddr_t a; + int ok; + unsigned long len; + + if ((slash= strchr(cidr, '/')) == nil) return 0; + + *slash++= 0; + ok= inet_aton(cidr, &a); + + len= strtoul(slash, &check, 10); + if (check == slash || *check != 0 || len > 32) ok= 0; + + *--slash= '/'; + if (!ok) return 0; + *addr= a; + *mask= htonl(len == 0 ? 0 : (0xFFFFFFFFUL << (32-len)) & 0xFFFFFFFFUL); + return 1; +} + +char *cidr_ntoa(ipaddr_t addr, ipaddr_t mask) +{ + ipaddr_t testmask= 0xFFFFFFFFUL; + int n; + static char result[sizeof("255.255.255.255/255.255.255.255")]; + + for (n= 32; n >= 0; n--) { + if (mask == htonl(testmask)) break; + testmask= (testmask << 1) & 0xFFFFFFFFUL; + } + + sprintf(result, "%s/%-2d", inet_ntoa(addr), n); + if (n == -1) strcpy(strchr(result, '/')+1, inet_ntoa(mask)); + return result; +} + +static size_t ascii2octet(u8_t *b, size_t size, const char *a) +{ + /* Convert a series of hex digit pairs to an octet (binary) array at + * 'b' with length 'size'. Return the number of octets in 'a' or + * -1 on error. + */ + size_t len; + int n, c; + + len= 0; + n= 0; + while ((c= *a++) != 0) { + if (between('0', c, '9')) c= (c - '0') + 0x0; + else + if (between('a', c, 'f')) c= (c - 'a') + 0xa; + else + if (between('A', c, 'F')) c= (c - 'A') + 0xA; + else { + return -1; + } + + if (n == 0) { + if (len < size) b[len] = c << 4; + } else { + if (len < size) b[len] |= c; + len++; + } + n ^= 1; + } + return n == 0 ? len : -1; +} + +void ether2clid(u8_t *clid, ether_addr_t *eth) +{ + /* Convert an Ethernet address to the default client ID form. */ + clid[0]= DHCP_HTYPE_ETH; + memcpy(clid+1, eth, DHCP_HLEN_ETH); +} + +static size_t ascii2clid(u8_t *clid, const char *a) +{ + /* Convert an ethernet address, or a series of hex digits to a client ID. + * Return its length if ok, otherwise -1. + */ + size_t len; + ether_addr_t *eth; + + if ((eth= ether_aton(a)) != nil) { + ether2clid(clid, eth); + len= 1+DHCP_HLEN_ETH; + } else { + len= ascii2octet(clid, CLID_MAX, a); + } + return len; +} + +static config_t *dhcpconf; /* In-core DHCP configuration. */ + +/* DHCP tag types. */ +typedef enum { TT_ASCII, TT_BOOLEAN, TT_IP, TT_NUMBER, TT_OCTET } tagtype_t; + +/* DHCP/BOOTP tag definitions. */ +typedef struct tagdef { + u8_t tag; /* Tag number. */ + u8_t type; /* Type and flags. */ + u8_t gran; /* Granularity. */ + u8_t max; /* Maximum number of arguments. */ + const char *name; /* Defined name. */ +} tagdef_t; + +#define TF_TYPE 0x07 /* To mask out the type. */ +#define TF_STATIC 0x08 /* "Static", i.e. a struct field. */ +#define TF_RO 0x10 /* Read-only, user can't set. */ + +/* List of static DHCP fields. The tag field is misused here as an offset + * into the DHCP structure. + */ +static tagdef_t statictag[] = { + { doff(op), TT_NUMBER|TF_STATIC|TF_RO, 1, 1, "op" }, + { doff(htype), TT_NUMBER|TF_STATIC|TF_RO, 1, 1, "htype" }, + { doff(hlen), TT_NUMBER|TF_STATIC|TF_RO, 1, 1, "hlen" }, + { doff(hops), TT_NUMBER|TF_STATIC|TF_RO, 1, 1, "hops" }, + { doff(xid), TT_NUMBER|TF_STATIC|TF_RO, 4, 1, "xid" }, + { doff(secs), TT_NUMBER|TF_STATIC|TF_RO, 2, 1, "secs" }, + { doff(flags), TT_NUMBER|TF_STATIC|TF_RO, 2, 1, "flags" }, + { doff(ciaddr), TT_IP|TF_STATIC|TF_RO, 1, 1, "ciaddr" }, + { doff(yiaddr), TT_IP|TF_STATIC|TF_RO, 1, 1, "yiaddr" }, + { doff(siaddr), TT_IP|TF_STATIC, 1, 1, "siaddr" }, + { doff(giaddr), TT_IP|TF_STATIC|TF_RO, 1, 1, "giaddr" }, + { doff(chaddr), TT_OCTET|TF_STATIC|TF_RO, 1, 16, "chaddr" }, + { doff(sname), TT_ASCII|TF_STATIC, 1, 64, "sname" }, + { doff(file), TT_ASCII|TF_STATIC, 1, 128, "file" }, +}; +#define N_STATIC arraysize(statictag) + +static tagdef_t alltagdef[N_STATIC + 254]; /* List of tag definitions. */ +#define tagdef (alltagdef+N_STATIC-1) /* Just the optional ones. */ + +#define tagdefined(tp) ((tp)->name != nil) + +static void inittagdef(void) +{ + /* Initialize the tag definitions from the "tag" commands in the config + * file. + */ + int t; + tagdef_t *tp; + static tagdef_t predef[] = { + { DHCP_TAG_NETMASK, TT_IP, 1, 1, "netmask" }, + { DHCP_TAG_GATEWAY, TT_IP, 1, 255, "gateway" }, + { DHCP_TAG_DNS, TT_IP, 1, 255, "DNSserver" }, + }; + static char *typenames[] = { "ascii", "boolean", "ip", "number", "octet" }; + config_t *cfg; + static u8_t rotags[] = { + DHCP_TAG_REQIP, DHCP_TAG_OVERLOAD, DHCP_TAG_TYPE, DHCP_TAG_SERVERID, + DHCP_TAG_REQPAR, DHCP_TAG_MESSAGE, DHCP_TAG_MAXDHCP + }; + + for (t= 1; t <= 254; t++) { + tp= &tagdef[t]; + tp->tag= t; + tp->type= TT_OCTET; + tp->name= nil; + } + + /* Set the static and "all Minix needs" tags. */ + memcpy(alltagdef, statictag, sizeof(statictag)); + for (tp= predef; tp < arraylimit(predef); tp++) tagdef[tp->tag] = *tp; + + /* Search for tag definitions in the config file. */ + for (cfg= dhcpconf; cfg != nil; cfg= cfg->next) { + config_t *cmd= cfg->list; + + if (strcasecmp(cmd->word, "tag") == 0) { + if (config_length(cmd) == 6 + && (cmd->next->flags & CFG_DULONG) + && config_isatom(cmd->next->next) + && config_isatom(cmd->next->next->next) + && (cmd->next->next->next->next->flags & CFG_DULONG) + && (cmd->next->next->next->next->next->flags & CFG_DULONG) + ) { + unsigned long tag, gran, max; + const char *name, *typename; + unsigned type; + + tag= strtoul(cmd->next->word, nil, 10); + name= cmd->next->next->word; + typename= cmd->next->next->next->word; + gran= strtoul(cmd->next->next->next->next->word, nil, 10); + max= strtoul(cmd->next->next->next->next->next->word, nil, 10); + + for (type= 0; type < arraysize(typenames); type++) { + if (strcasecmp(typename, typenames[type]) == 0) break; + } + + if (!(1 <= tag && tag <= 254) + || !(type < arraysize(typenames)) + || !((type == TT_NUMBER + && (gran == 1 || gran == 2 || gran == 4)) + || (type != TT_NUMBER && 1 <= gran && gran <= 16)) + || !(max <= 255) + ) { + fprintf(stderr, + "\"%s\", line %u: Tag definition is incorrect\n", + cmd->file, cmd->line); + exit(1); + } + + tp= &tagdef[(int)tag]; + tp->type= type; + tp->name= name; + tp->gran= gran; + tp->max= max; + } else { + fprintf(stderr, + "\"%s\", line %u: Usage: tag number name type granularity max\n", + cmd->file, cmd->line); + exit(1); + } + } + } + + /* Many DHCP tags are not for the user to play with. */ + for (t= 0; t < arraysize(rotags); t++) tagdef[rotags[t]].type |= TF_RO; +} + +static tagdef_t *tagdefbyname(const char *name) +{ + /* Find a tag definition by the name of the tag. Return null if not + * defined. + */ + tagdef_t *tp; + + for (tp= alltagdef; tp < arraylimit(alltagdef); tp++) { + if (tagdefined(tp) && strcasecmp(tp->name, name) == 0) return tp; + } + return nil; +} + +void initdhcpconf(void) +{ + /* Read/refresh configuration from the DHCP configuration file. */ + dhcpconf= config_read(configfile, 0, dhcpconf); + if (config_renewed(dhcpconf)) inittagdef(); +} + +static void configtag(dhcp_t *dp, config_t *cmd, ipaddr_t ifip) +{ + /* Add a tag to a DHCP packet from the config file. */ + tagdef_t *tp; + u8_t data[260], *d; + size_t i; + int delete= 0; + + if (strcasecmp(cmd->word, "no") == 0) { + if (config_length(cmd) != 2 || !config_isatom(cmd->next)) { + fprintf(stderr, "\"%s\", line %u: Usage: no tag-name\n", + cmd->file, cmd->line); + exit(1); + } + cmd= cmd->next; + delete= 1; + } + + if ((tp= tagdefbyname(cmd->word)) == nil) { + fprintf(stderr, "\"%s\", line %u: Unknown tag '%s'\n", + cmd->file, cmd->line, cmd->word); + exit(1); + } + + if (tp->type & TF_RO) { + fprintf(stderr, "\"%s\", line %u: Tag '%s' can't be configured\n", + cmd->file, cmd->line, cmd->word); + exit(1); + } + + i= 0; + d= data; + if (!delete) { + config_t *arg= cmd->next; + do { + switch (tp->type & TF_TYPE) { + case TT_ASCII: { + if (arg == nil || !config_isatom(arg) || arg->next != nil) { + fprintf(stderr, "\"%s\", line %u: Usage: %s string\n", + cmd->file, cmd->line, cmd->word); + exit(1); + } + strncpy((char *) data, arg->word, sizeof(data)); + d += i = strnlen((char *) data, sizeof(data)); + break;} + case TT_BOOLEAN: { + if (arg == nil || !config_isatom(arg) + || !(strcasecmp(arg->word, "false") == 0 + || strcasecmp(arg->word, "true") == 0) + ) { + fprintf(stderr, + "\"%s\", line %u: Usage: %s false|true ...\n", + cmd->file, cmd->line, cmd->word); + exit(1); + } + if (d < arraylimit(data)) { + *d++ = (arg->word[0] != 'f' && arg->word[0] != 'F'); + } + i++; + break;} + case TT_IP: { + ipaddr_t ip; + unsigned long len; + char *end; + + if (arg == nil || !config_isatom(arg)) { + fprintf(stderr, "\"%s\", line %u: Usage: %s host ...\n", + cmd->file, cmd->line, cmd->word); + exit(1); + } + if (arg->word[0] == '/' + && between(1, len= strtoul(arg->word+1, &end, 10), 31) + && *end == 0 + ) { + ip= htonl((0xFFFFFFFFUL << (32-len)) & 0xFFFFFFFFUL); + } else + if (!name2ip(&ip, arg->word, ifip)) { + fprintf(stderr, + "\"%s\", line %u: Can't translate %s to an IP address\n", + arg->file, arg->line, arg->word); + exit(1); + } + if (d <= arraylimit(data) - sizeof(ip)) { + memcpy(d, &ip, sizeof(ip)); + d += sizeof(ip); + } + i++; + break;} + case TT_NUMBER: { + unsigned long n; + int g; + + if (arg == nil || !(arg->flags & CFG_CLONG)) { + fprintf(stderr, "\"%s\", line %u: Usage: %s number ...\n", + cmd->file, cmd->line, cmd->word); + exit(1); + } + n= strtoul(arg->word, nil, 0); + g= tp->gran; + do { + if (d <= arraylimit(data)) *d++ = (n >> (--g * 8)) & 0xFF; + } while (g != 0); + i++; + break;} + case TT_OCTET: { + if (arg == nil || !config_isatom(arg) || arg->next != nil) { + fprintf(stderr, "\"%s\", line %u: Usage: %s hexdigits\n", + cmd->file, cmd->line, cmd->word); + exit(1); + } + i= ascii2octet(data, sizeof(data), arg->word); + if (i == -1) { + fprintf(stderr, + "\"%s\", line %u: %s: Bad hexdigit string\n", + arg->file, arg->line, arg->word); + exit(1); + } + d= data + i; + break;} + } + } while ((arg= arg->next) != nil); + + if (d > data + 255) { + fprintf(stderr, "\"%s\", line %u: Tag value is way too big\n", + cmd->file, cmd->line); + exit(1); + } + if ((tp->type & TF_TYPE) != TT_NUMBER && (i % tp->gran) != 0) { + fprintf(stderr, + "\"%s\", line %u: Expected a multiple of %d initializers\n", + cmd->file, cmd->line, tp->gran); + exit(1); + } + if (tp->max != 0 && i > tp->max) { + fprintf(stderr, + "\"%s\", line %u: Got %d initializers, can have only %d\n", + cmd->file, cmd->line, (int) i, tp->max); + exit(1); + } + } + if (tp->type & TF_STATIC) { + size_t len= tp->gran * tp->max; + if ((tp->type & TF_TYPE) == TT_IP) len *= sizeof(ipaddr_t); + memset(B(dp) + tp->tag, 0, len); + memcpy(B(dp) + tp->tag, data, (d - data)); + } else { + settag(dp, tp->tag, data, (d - data)); + } +} + +int makedhcp(dhcp_t *dp, u8_t *class, size_t calen, u8_t *client, size_t cilen, + ipaddr_t ip, ipaddr_t ifip, network_t *np) +{ + /* Fill in a DHCP packet at 'dp' for the host identified by the + * (class, client, ip) combination. Makedhcp is normally called twice, + * once to find the IP address (so ip == 0) and once again to find all + * data that goes with that IP address (ip != 0). On the first call the + * return value of this function should be ignored and only 'yiaddr' + * checked and used as 'ip' on the next pass. True is returned iff there + * is information for the client on the network at interface address + * 'ifip', by checking if the 'ip' and 'ifip' are on the same network. + * If np is nonnull then we are working for one of our own interfaces, so + * options can be set and adjourning interfaces can be programmed. + */ + config_t *todo[16]; + size_t ntodo= 0; + ipaddr_t hip, mask; + u8_t *pmask; + char *hostname; + u32_t distance= -1; + + initdhcpconf(); + + /* Start creating a packet. */ + dhcp_init(dp); + dp->op= DHCP_BOOTREPLY; + + /* The initial TODO list is the whole DHCP config. */ + todo[ntodo++]= dhcpconf; + + while (ntodo > 0) { + config_t *cmd, *follow; + + if (todo[ntodo-1] == nil) { ntodo--; continue; } + cmd= todo[ntodo-1]->list; + todo[ntodo-1]= todo[ntodo-1]->next; + + follow= nil; /* Macro or list to follow next? */ + + if (strcasecmp(cmd->word, "client") == 0) { + u8_t cfgid[CLID_MAX]; + size_t cfglen; + char *name; + int ifno; + u32_t d; + + if (between(3, config_length(cmd), 5) + && config_isatom(cmd->next) + && (cfglen= ascii2clid(cfgid, cmd->next->word)) != -1 + && config_isatom(cmd->next->next) + && (((ifno= ifname2if(cmd->next->next->word)) == -1 + && config_length(cmd) <= 4) + || ((ifno= ifname2if(cmd->next->next->word)) != -1 + && config_length(cmd) >= 4 + && config_isatom(cmd->next->next->next))) + ) { + if (cilen == cfglen && memcmp(client, cfgid, cilen) == 0 + && (ifno == -1 || np == nil || ifno == np->n) + ) { + config_t *atname= cmd->next->next; + if (ifno != -1) atname= atname->next; + name= atname->word; + + if (name2ip(&hip, name, ifip) && (ip == 0 || ip == hip)) { + d= ntohl(hip) ^ ntohl(ifip); + if (d < distance) { + dp->yiaddr= hip; + follow= atname->next; + distance= d; + } + } + } + } else { + fprintf(stderr, + "\"%s\", line %u: Usage: client ID [ip#] host [macro|{params}]\n", + cmd->file, cmd->line); + exit(1); + } + } else + if (strcasecmp(cmd->word, "class") == 0) { + config_t *clist; + int match; + + match= 0; + for (clist= cmd->next; clist != nil + && clist->next != nil + && config_isatom(clist); clist= clist->next) { + if (calen > 0 + && strncmp(clist->word, (char *) class, calen) == 0 + ) { + match= 1; + } + } + if (clist == cmd->next || clist->next != nil) { + fprintf(stderr, + "\"%s\", line %u: Usage: class class-name ... macro|{params}\n", + cmd->file, cmd->line); + } + if (match) follow= clist; + } else + if (strcasecmp(cmd->word, "host") == 0) { + if (config_length(cmd) == 3 + && config_isatom(cmd->next) + ) { + if (ip != 0) { + if (cidr_aton(cmd->next->word, &hip, &mask)) { + if (((hip ^ ip) & mask) == 0) { + if (!gettag(dp, DHCP_TAG_NETMASK, nil, nil)) { + settag(dp, DHCP_TAG_NETMASK, + &mask, sizeof(mask)); + } + dp->yiaddr= ip; + follow= cmd->next->next; + } + } else + if (name2ip(&hip, cmd->next->word, ifip)) { + if (hip == ip) { + dp->yiaddr= ip; + follow= cmd->next->next; + } + } + } + } else { + fprintf(stderr, + "\"%s\", line %u: Usage: host host-spec macro|{params}\n", + cmd->file, cmd->line); + exit(1); + } + } else + if (strcasecmp(cmd->word, "interface") == 0) { + if (between(3, config_length(cmd), 4) + && config_isatom(cmd->next) + && config_isatom(cmd->next->next) + ) { + network_t *ifnp; + + if (np != nil) { + if ((ifnp= if2net(ifname2if(cmd->next->word))) == nil) { + fprintf(stderr, + "\"%s\", line %u: Can't find interface %s\n", + cmd->next->file, cmd->next->line, cmd->next->word); + exit(1); + } + if (!name2ip(&hip, cmd->next->next->word, 0)) { + fprintf(stderr, + "\"%s\", line %u: Can't find IP address of %s\n", + cmd->next->next->file, cmd->next->next->line, + cmd->next->next->word); + exit(1); + } + ifnp->ip= hip; + if (ifnp == np) { + dp->yiaddr= hip; + follow= cmd->next->next->next; + } + } + } else { + fprintf(stderr, + "\"%s\", line %u: Usage: interface ip# host%s\n", + cmd->file, cmd->line, ntodo==1 ? " [macro|{params}]" : ""); + exit(1); + } + } else + if (strcasecmp(cmd->word, "macro") == 0) { + if (config_length(cmd) == 2 && config_isatom(cmd->next)) { + follow= cmd->next; + } else + if (ntodo > 1) { + fprintf(stderr, "\"%s\", line %u: Usage: macro macro-name\n", + cmd->file, cmd->line); + exit(1); + } + } else + if (strcasecmp(cmd->word, "tag") == 0) { + if (ntodo > 1) { + fprintf(stderr, + "\"%s\", line %u: A %s can't be defined here\n", + cmd->file, cmd->line, cmd->word); + exit(1); + } + } else + if (strcasecmp(cmd->word, "option") == 0) { + int ifno; + network_t *ifnp; + config_t *opt; + + if ((opt= cmd->next) != nil + && config_isatom(opt) + && (ifno= ifname2if(opt->word)) != -1 + ) { + if ((ifnp= if2net(ifno)) == nil) { + fprintf(stderr, + "\"%s\", line %u: Interface %s is not enabled\n", + opt->file, opt->line, opt->word); + exit(1); + } + opt= opt->next; + } else { + ifnp= np; + } + + if (between(1, config_length(opt), 2) + && config_isatom(opt) + && strcasecmp(opt->word, "server") == 0 + && (opt->next == nil + || strcasecmp(opt->next->word, "inform") == 0) + ) { + if (np != nil) { + ifnp->flags |= NF_SERVING; + if (opt->next != nil) ifnp->flags |= NF_INFORM; + } + } else + if (config_length(opt) == 2 + && config_isatom(opt) + && strcasecmp(opt->word, "relay") == 0 + && config_isatom(opt->next) + ) { + if (np != nil) { + if (!name2ip(&hip, opt->next->word, ifip)) { + fprintf(stderr, + "\"%s\", line %u: Can't find IP address of %s\n", + opt->next->file, opt->next->line, + opt->next->word); + exit(1); + } + ifnp->flags |= NF_RELAYING; + ifnp->server= hip; + } + } else + if (config_length(opt) == 1 + && config_isatom(opt) + && strcasecmp(opt->word, "possessive") == 0 + ) { + if (np != nil) ifnp->flags |= NF_POSSESSIVE; + } else + if (config_length(opt) == 2 + && config_isatom(opt) + && strcasecmp(opt->word, "hostname") == 0 + && config_isatom(opt->next) + ) { + if (np != nil) np->hostname= opt->next->word; + } else { + fprintf(stderr, "\"%s\", line %u: Unknown option\n", + cmd->file, cmd->line); + exit(1); + } + } else { + /* Must be an actual data carrying tag. */ + configtag(dp, cmd, ifip); + } + + if (follow != nil) { + /* A client/class/host entry selects a macro or list that must + * be followed next. + */ + config_t *macro; + + if (config_isatom(follow)) { /* Macro name */ + config_t *cfg; + + for (cfg= dhcpconf; cfg != nil; cfg= cfg->next) { + macro= cfg->list; + + if (strcasecmp(macro->word, "macro") == 0) { + if (config_length(macro) == 3 + && config_isatom(macro->next) + && config_issub(macro->next->next) + ) { + if (strcasecmp(macro->next->word, follow->word) == 0 + ) { + break; + } + } else { + fprintf(stderr, + "\"%s\", line %u: Usage: macro macro-name {params}\n", + macro->file, macro->line); + } + } + } + follow= cfg == nil ? nil : macro->next->next->list; + } else { + /* Simply a list of more tags and stuff. */ + follow= follow->list; + } + + if (ntodo == arraysize(todo)) { + fprintf(stderr, "\"%s\", line %u: Nesting is too deep\n", + follow->file, follow->line); + exit(1); + } + todo[ntodo++]= follow; + } + } + + /* Check if the IP and netmask are OK for the interface. */ + if (!gettag(dp, DHCP_TAG_NETMASK, &pmask, nil)) return 0; + memcpy(&mask, pmask, sizeof(mask)); + if (((ip ^ ifip) & mask) != 0) return 0; + + /* Fill in the hostname and/or domain. */ + if ((hostname= ip2name(ip)) != nil) { + char *domain; + + if ((domain= strchr(hostname, '.')) != nil) *domain++ = 0; + + if (!gettag(dp, DHCP_TAG_HOSTNAME, nil, nil)) { + settag(dp, DHCP_TAG_HOSTNAME, hostname, strlen(hostname)); + } + + if (domain != nil && !gettag(dp, DHCP_TAG_DOMAIN, nil, nil)) { + settag(dp, DHCP_TAG_DOMAIN, domain, strlen(domain)); + } + } + + return 1; +} + +static char *dhcpopname(int op) +{ + static char *onames[] = { "??\?", "REQUEST", "REPLY" }; + return onames[op < arraysize(onames) ? op : 0]; +} + +char *dhcptypename(int type) +{ + static char *tnames[] = { + "??\?", "DISCOVER", "OFFER", "REQUEST", "DECLINE", + "ACK", "NAK", "RELEASE", "INFORM" + }; + return tnames[type < arraysize(tnames) ? type : 0]; +} + +void printdhcp(dhcp_t *dp) +{ + /* Print the contents of a DHCP packet, usually for debug purposes. */ + tagdef_t *tp; + u8_t *data, *ovld; + size_t i, len; + + for (tp= alltagdef; tp < arraylimit(alltagdef); tp++) { + if (tp->type & TF_STATIC) { + data= B(dp) + tp->tag; + len= tp->gran * tp->max; + if ((tp->type & TF_TYPE) == TT_IP) len *= sizeof(ipaddr_t); + if (tp->tag == doff(chaddr)) len= dp->hlen; + + /* Don't show uninteresting stuff. */ + if (tp->tag == doff(htype) && dp->htype == DHCP_HTYPE_ETH) continue; + + if (tp->tag == doff(hlen) && dp->hlen == DHCP_HLEN_ETH) continue; + + if ((tp->tag == doff(file) || tp->tag == doff(sname)) + && gettag(dp, DHCP_TAG_OVERLOAD, &ovld, nil) + && (ovld[0] & (tp->tag == doff(file) ? 1 : 2)) + ) { + continue; + } + for (i= 0; i < len && data[i] == 0; i++) {} + if (i == len) continue; + } else { + if (!gettag(dp, tp->tag, &data, &len)) continue; + } + + if (tagdefined(tp)) { + printf("\t%s =", tp->name); + } else { + printf("\tT%d =", tp->tag); + } + + i= 0; + while (i < len) { + switch (tp->type & TF_TYPE) { + case TT_ASCII: { + printf(" \"%.*s\"", (int) len, data); + i= len; + break;} + case TT_BOOLEAN: { + printf(data[i++] == 0 ? " false" : " true"); + break;} + case TT_IP: { + ipaddr_t ip; + memcpy(&ip, data+i, sizeof(ip)); + printf(" %s", inet_ntoa(ip)); + i += sizeof(ip); + break;} + case TT_NUMBER: { + u32_t n= 0; + int g= tp->gran; + + do n= (n << 8) | data[i++]; while (--g != 0); + printf(" %lu", (unsigned long) n); + if ((tp->type & TF_STATIC) && tp->tag == doff(op)) { + printf(" (%s)", dhcpopname(n)); + } + if (!(tp->type & TF_STATIC) && tp->tag == DHCP_TAG_TYPE) { + printf(" (%s)", dhcptypename(n)); + } + break;} + case TT_OCTET: { + if (i == 0) fputc(' ', stdout); + printf("%02X", data[i++]); + break;} + } + } + fputc('\n', stdout); + } +} diff --git a/commands/dis88/Makefile b/commands/dis88/Makefile new file mode 100755 index 000000000..05d248871 --- /dev/null +++ b/commands/dis88/Makefile @@ -0,0 +1,49 @@ +# Makefile for dis + +# @(#) Makefile, Ver. 2.1 created 00:00:00 87/09/01 +# Makefile for 8088 symbolic disassembler + +# Copyright (C) 1987 G. M. Harding, all rights reserved. +# Permission to copy and redistribute is hereby granted, +# provided full source code, with all copyright notices, +# accompanies any redistribution. + +# This Makefile automates the process of compiling and linking +# a symbolic object-file disassembler program for the Intel +# 8088 CPU. Relatively machine-independent code is contained in +# the file dismain.c; lookup tables and handler routines, which +# are by their nature machine-specific, are contained in two +# files named distabs.c and dishand.c, respectively. (A third +# machine-specific file, disfp.c, contains handler routines for +# floating-point coprocessor opcodes.) A header file, dis.h, +# attempts to mediate between the machine-specific and machine- +# independent portions of the code. An attempt has been made to +# isolate machine dependencies and to deal with them in fairly +# straightforward ways. Thus, it should be possible to target a +# different CPU by rewriting the handler routines and changing +# the initialization data in the lookup tables. It should not +# be necessary to alter the formats of the tables. + +CFLAGS =-O -wo +OBJ = disrel.o dismain.o distabs.o dishand.o disfp.o + +all: dis88 + +dis88: $(OBJ) + cc -i -o dis88 $(OBJ) + install -S 5kw dis88 + +install: /usr/bin/dis88 + +/usr/bin/dis88: dis88 + install -cs -o bin dis88 $@ + +disrel.o: disrel.c +dismain.o: dismain.c dis.h +distabs.o: distabs.c dis.h +dishand.o: dishand.c dis.h +disfp.o: disfp.c dis.h + + +clean: + rm -f *.bak *.o core dis88 diff --git a/commands/dis88/README b/commands/dis88/README new file mode 100755 index 000000000..a00763408 --- /dev/null +++ b/commands/dis88/README @@ -0,0 +1,239 @@ + dis88 + Beta Release + 87/09/01 + --- + G. M. HARDING + POB 4142 + Santa Clara CA 95054-0142 + + + "Dis88" is a symbolic disassembler for the Intel 8088 CPU, + designed to run under the PC/IX operating system on an IBM XT + or fully-compatible clone. Its output is in the format of, and + is completely compatible with, the PC/IX assembler, "as". The + program is copyrighted by its author, but may be copied and re- + distributed freely provided that complete source code, with all + copyright notices, accompanies any distribution. This provision + also applies to any modifications you may make. You are urged + to comment such changes, giving, as a miminum, your name and + complete address. + + This release of the program is a beta release, which means + that it has been extensively, but not exhaustively, tested. + User comments, recommendations, and bug fixes are welcome. The + principal features of the current release are: + + (a) The ability to disassemble any file in PC/IX object + format, making full use of symbol and relocation information if + it is present, regardless of whether the file is executable or + linkable, and regardless of whether it has continuous or split + I/D space; + + (b) Automatic generation of synthetic labels when no sym- + bol table is available; and + + (c) Optional output of address and object-code informa- + tion as assembler comment text. + + Limitations of the current release are: + + (a) Numeric co-processor (i.e., 8087) mnemonics are not + supported. Instructions for the co-processor are disassembled + as CPU escape sequences, or as interrupts, depending on how + they were assembled in the first place. This limitation will be + addressed in a future release. + + (b) Symbolic references within the object file's data + segment are not supported. Thus, for example, if a data segment + location is initialized to point to a text segment address, no + reference to a text segment symbol will be detected. This limi- + tation is likely to remain in future releases, because object + code does not, in most cases, contain sufficient information to + allow meaningful interpretation of pure data. (Note, however, + that symbolic references to the data segment from within the + text segment are always supported.) + + As a final caveat, be aware that the PC/IX assembler does + not recognize the "esc" mnemonic, even though it refers to a + completely valid CPU operation which is documented in all the + Intel literature. Thus, the corresponding opcodes (0xd8 through + 0xdf) are disassembled as .byte directives. For reference, how- + ever, the syntactically-correct "esc" instruction is output as + a comment. + + To build the disassembler program, transfer all the source + files, together with the Makefile, to a suitable (preferably + empty) PC/IX directory. Then, simply type "make". + + To use dis88, place it in a directory which appears in + your $PATH list. It may then be invoked by name from whatever + directory you happen to be in. As a minimum, the program must + be invoked with one command-line argument: the name of the ob- + ject file to be disassembled. (Dis88 will complain if the file + specified is not an object file.) Optionally, you may specify + an output file; stdout is the default. One command-line switch + is available: "-o", which makes the program display addresses + and object code along with its mnemonic disassembly. + + The "-o" option is useful primarily for verifying the cor- + rectness of the program's output. In particular, it may be used + to check the accuracy of local relative jump opcodes. These + jumps often target local labels, which are lost at assembly + time; thus, the disassembly may contain cryptic instructions + like "jnz .+39". As a user convenience, all relative jump and + call opcodes are output with a comment which identifies the + physical target address. + + By convention, the release level of the program as a whole + is the SID of the file disrel.c, and this SID string appears in + each disassembly. Release 2.1 of the program is the first beta + release to be distributed on Usenet. + + +.TH dis88 1 LOCAL +.SH "NAME" +dis88 \- 8088 symbolic disassembler +.SH "SYNOPSIS" +\fBdis88\fP [ -o ] ifile [ ofile ] +.SH "DESCRIPTION" +Dis88 reads ifile, which must be in PC/IX a.out format. +It interprets the binary opcodes and data locations, and +writes corresponding assembler source code to stdout, or +to ofile if specified. The program's output is in the +format of, and fully compatible with, the PC/IX assembler, +as(1). If a symbol table is present in ifile, labels and +references will be symbolic in the output. If the input +file lacks a symbol table, the fact will be noted, and the +disassembly will proceed, with the disassembler generating +synthetic labels as needed. If the input file has split +I/D space, or if it is executable, the disassembler will +make all necessary adjustments in address-reference calculations. +.PP +If the "-o" option appears, object code will be included +in comments during disassembly of the text segment. This +feature is used primarily for debugging the disassembler +itself, but may provide information of passing interest +to users. +.PP +The program always outputs the current machine address +before disassembling an opcode. If a symbol table is +present, this address is output as an assembler comment; +otherwise, it is incorporated into the synthetic label +which is generated internally. Since relative jumps, +especially short ones, may target unlabelled locations, +the program always outputs the physical target address +as a comment, to assist the user in following the code. +.PP +The text segment of an object file is always padded to +an even machine address. In addition, if the file has +split I/D space, the text segment will be padded to a +paragraph boundary (i.e., an address divisible by 16). +As a result of this padding, the disassembler may produce +a few spurious, but harmless, instructions at the +end of the text segment. +.PP +Disassembly of the data segment is a difficult matter. +The information to which initialized data refers cannot +be inferred from context, except in the special case +of an external data or address reference, which will be +reflected in the relocation table. Internal data and +address references will already be resolved in the object file, +and cannot be recreated. Therefore, the data +segment is disassembled as a byte stream, with long +stretches of null data represented by an appropriate +".zerow" pseudo-op. This limitation notwithstanding, +labels (as opposed to symbolic references) are always +output at appropriate points within the data segment. +.PP +If disassembly of the data segment is difficult, disassembly of the +bss segment is quite easy, because uninitialized data is all +zero by definition. No data +is output in the bss segment, but symbolic labels are +output as appropriate. +.PP +For each opcode which takes an operand, a particular +symbol type (text, data, or bss) is appropriate. This +tidy correspondence is complicated somewhat, however, +by the existence of assembler symbolic constants and +segment override opcodes. Therefore, the disassembler's +symbol lookup routine attempts to apply a certain amount +of intelligence when it is asked to find a symbol. If +it cannot match on a symbol of the preferred type, it +may return a symbol of some other type, depending on +preassigned (and somewhat arbitrary) rankings within +each type. Finally, if all else fails, it returns a +string containing the address sought as a hex constant; +this behavior allows calling routines to use the output +of the lookup function regardless of the success of its +search. +.PP +It is worth noting, at this point, that the symbol lookup +routine operates linearly, and has not been optimized in +any way. Execution time is thus likely to increase +geometrically with input file size. The disassembler is +internally limited to 1500 symbol table entries and 1500 +relocation table entries; while these limits are generous +(/unix, itself, has fewer than 800 symbols), they are not +guaranteed to be adequate in all cases. If the symbol +table or the relocation table overflows, the disassembly +aborts. +.PP +Finally, users should be aware of a bug in the assembler, +which causes it not to parse the "esc" mnemonic, even +though "esc" is a completely legitimate opcode which is +documented in all the Intel literature. To accommodate +this deficiency, the disassembler translates opcodes of +the "esc" family to .byte directives, but notes the +correct mnemonic in a comment for reference. +.PP +In all cases, it should be possible to submit the output +of the disassembler program to the assembler, and assemble +it without error. In most cases, the resulting object +code will be identical to the original; in any event, it +will be functionally equivalent. +.SH "SEE ALSO" +adb(1), as(1), cc(1), ld(1). +.br +"Assembler Reference Manual" in the PC/IX Programmer's +Guide. +.SH "DIAGNOSTICS" +"can't access input file" if the input file cannot be +found, opened, or read. +.sp +"can't open output file" if the output file cannot be +created. +.sp +"warning: host/cpu clash" if the program is run on a +machine with a different CPU. +.sp +"input file not in object format" if the magic number +does not correspond to that of a PC/IX object file. +.sp +"not an 8086/8088 object file" if the CPU ID of the +file header is incorrect. +.sp +"reloc table overflow" if there are more than 1500 +entries in the relocation table. +.sp +"symbol table overflow" if there are more than 1500 +entries in the symbol table. +.sp +"lseek error" if the input file is corrupted (should +never happen). +.sp +"warning: no symbols" if the symbol table is missing. +.sp +"can't reopen input file" if the input file is removed +or altered during program execution (should never happen). +.SH "BUGS" +Numeric co-processor (i.e., 8087) mnemonics are not currently supported. +Instructions for the co-processor are +disassembled as CPU escape sequences, or as interrupts, +depending on how they were assembled in the first place. +.sp +Despite the program's best efforts, a symbol retrieved +from the symbol table may sometimes be different from +the symbol used in the original assembly. +.sp +The disassembler's internal tables are of fixed size, +and the program aborts if they overflow. diff --git a/commands/dis88/dis.h b/commands/dis88/dis.h new file mode 100755 index 000000000..d8fc245f1 --- /dev/null +++ b/commands/dis88/dis.h @@ -0,0 +1,167 @@ + /* + ** @(#) dis.h, Ver. 2.1 created 00:00:00 87/09/01 + */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 1987 G. M. Harding, all rights reserved * + * * + * Permission to copy and redistribute is hereby granted, * + * provided full source code, with all copyright notices, * + * accompanies any redistribution. * + * * + * This file contains declarations and definitions used by * + * the 8088 disassembler program. The program was designed * + * for execution on a machine of its own type (i.e., it is * + * not designed as a cross-disassembler); consequently, A * + * SIXTEEN-BIT INTEGER SIZE HAS BEEN ASSUMED. This assump- * + * tion is not particularly important, however, except in * + * the machine-specific portions of the code (i.e., the * + * handler routines and the optab[] array). It should be * + * possible to override this assumption, for execution on * + * 32-bit machines, by use of a pre-processor directive * + * (see below); however, this has not been tested. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include +#include /* Object file format definitions */ +#include /* System file-control definitions */ +#include +#include +#include +#include /* System standard I/O definitions */ + +#define MAXSYM 1500 /* Maximum entries in symbol table */ + +extern struct nlist /* Array to hold the symbol table */ + symtab[MAXSYM]; + +extern struct reloc /* Array to hold relocation table */ + relo[MAXSYM]; + +extern int symptr; /* Index into the symtab[] array */ +extern int relptr; /* Index into the relo[] array */ + +struct opcode /* Format for opcode data records */ +{ + char *text; /* Pointer to mnemonic text */ + void (*func)(); /* Pointer to handler routine */ + unsigned min; /* Minimum # of object bytes */ + unsigned max; /* Maximum # of object bytes */ +}; + +extern struct opcode /* Array to hold the opcode table */ + optab[256]; + +extern char *REGS[]; /* Table of register names */ +extern char *REGS0[]; /* Mode 0 register name table */ +extern char *REGS1[]; /* Mode 1 register name table */ + +#define AL REGS[0] /* CPU register manifests */ +#define CL REGS[1] +#define DL REGS[2] +#define BL REGS[3] +#define AH REGS[4] +#define CH REGS[5] +#define DH REGS[6] +#define BH REGS[7] +#define AX REGS[8] +#define CX REGS[9] +#define DX REGS[10] +#define BX REGS[11] +#define SP REGS[12] +#define BP REGS[13] +#define SI REGS[14] +#define DI REGS[15] +#define ES REGS[16] +#define CS REGS[17] +#define SS REGS[18] +#define DS REGS[19] +#define BX_SI REGS0[0] +#define BX_DI REGS0[1] +#define BP_SI REGS0[2] +#define BP_DI REGS0[3] + +extern int symrank[6][6]; /* Symbol type/rank matrix */ +extern unsigned long PC; /* Current program counter */ +extern int segflg; /* Flag: segment override in effect */ +extern int objflg; /* Flag: output object as a comment */ + +#define OBJMAX 8 /* Size of the object code buffer */ + +extern unsigned char /* Internal buffer for object code */ + objbuf[OBJMAX]; + +extern int objptr; /* Index into the objbuf[] array */ + +extern char ADD[], /* Opcode family mnemonic strings */ + OR[], + ADC[], + SBB[], + AND[], + SUB[], + XOR[], + CMP[], + NOT[], + NEG[], + MUL[], + DIV[], + MOV[], + ESC[], + TEST[], + AMBIG[]; + +extern char *OPFAM[]; /* Indexed mnemonic family table */ +extern struct exec HDR; /* Holds the object file's header */ + +#define LOOK_ABS 0 /* Arguments to lookup() function */ +#define LOOK_REL 1 +#define LOOK_LNG 2 + +#define TR_STD 0 /* Arguments to mtrans() function */ +#define TR_SEG 8 + + /* Macro for byte input primitive */ +#define FETCH(p) ++PC; p = getchar() & 0xff; objbuf[objptr++] = p + + +/* disfp.c */ +_PROTOTYPE(void eshand, (int j )); +_PROTOTYPE(void fphand, (int j )); +_PROTOTYPE(void inhand, (int j )); + +/* dishand.c */ +_PROTOTYPE(void objini, (int j )); +_PROTOTYPE(void objout, (void)); +_PROTOTYPE(void badseq, (int j, int k )); +_PROTOTYPE(void dfhand, (int j )); +_PROTOTYPE(void sbhand, (int j )); +_PROTOTYPE(void aohand, (int j )); +_PROTOTYPE(void sjhand, (int j )); +_PROTOTYPE(void imhand, (int j )); +_PROTOTYPE(void mvhand, (int j )); +_PROTOTYPE(void mshand, (int j )); +_PROTOTYPE(void pohand, (int j )); +_PROTOTYPE(void cihand, (int j )); +_PROTOTYPE(void mihand, (int j )); +_PROTOTYPE(void mqhand, (int j )); +_PROTOTYPE(void tqhand, (int j )); +_PROTOTYPE(void rehand, (int j )); +_PROTOTYPE(void mmhand, (int j )); +_PROTOTYPE(void srhand, (int j )); +_PROTOTYPE(void aahand, (int j )); +_PROTOTYPE(void iohand, (int j )); +_PROTOTYPE(void ljhand, (int j )); +_PROTOTYPE(void mahand, (int j )); +_PROTOTYPE(void mjhand, (int j )); + +/* dismain.c */ +_PROTOTYPE(void main, (int argc, char **argv )); + +/* distabs.c */ +_PROTOTYPE(char *getnam, (int k )); +_PROTOTYPE(int lookext, (long off, long loc, char *buf )); +_PROTOTYPE(char *lookup, (long addr, int type, int kind, long ext )); +_PROTOTYPE(char *mtrans, (int c, int m, int type )); +_PROTOTYPE(void mtrunc, (char *a )); diff --git a/commands/dis88/disfp.c b/commands/dis88/disfp.c new file mode 100755 index 000000000..c6dc0da1b --- /dev/null +++ b/commands/dis88/disfp.c @@ -0,0 +1,157 @@ +static char *sccsid = + "@(#) disfp.c, Ver. 2.1 created 00:00:00 87/09/01"; + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 1987 G. M. Harding, all rights reserved * + * * + * Permission to copy and redistribute is hereby granted, * + * provided full source code, with all copyright notices, * + * accompanies any redistribution. * + * * + * This file contains handler routines for the numeric op- * + * codes of the 8087 co-processor, as well as a few other * + * opcodes which are related to 8087 emulation. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "dis.h" /* Disassembler declarations */ + +#define FPINT0 0xd8 /* Floating-point interrupts */ +#define FPINT1 0xd9 +#define FPINT2 0xda +#define FPINT3 0xdb +#define FPINT4 0xdc +#define FPINT5 0xdd +#define FPINT6 0xde +#define FPINT7 0xdf + + /* Test for floating opcodes */ +#define ISFLOP(x) \ + (((x) >= FPINT0) && ((x) <= FPINT7)) + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for the escape family of opcodes. * + * These opcodes place the contents of a specified memory * + * location on the system bus, for access by a peripheral * + * or by a co-processor such as the 8087. (The 8087 NDP is * + * accessed only via bus escapes.) Due to a bug in the * + * PC/IX assembler, the "esc" mnemonic is not recognized; * + * consequently, escape opcodes are disassembled as .byte * + * directives, with the appropriate mnemonic and operand * + * included as a comment. FOR NOW, those escape sequences * + * corresponding to 8087 opcodes are treated as simple * + * escapes. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +eshand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF eshand() * * * * * * * * * */ + + register char *a; + register int k; + + objini(j); + + FETCH(k); + + a = mtrans((j & 0xfd),(k & 0xc7),TR_STD); + + mtrunc(a); + + printf("\t.byte\t0x%02.2x\t\t| esc\t%s\n",j,a); + + for (k = 1; k < objptr; ++k) + printf("\t.byte\t0x%02.2x\n",objbuf[k]); + +}/* * * * * * * * * * * END OF eshand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler routine for floating-point opcodes. * + * Since PC/IX must accommodate systems with and without * + * 8087 co-processors, it allows floating-point operations * + * to be initiated in either of two ways: by a software * + * interrput whose type is in the range 0xd8 through 0xdf, * + * or by a CPU escape sequence, which is invoked by an op- * + * code in the same range. In either case, the subsequent * + * byte determines the actual numeric operation to be per- * + * formed. However, depending on the method of access, * + * either one or two code bytes will precede that byte, * + * and the fphand() routine has no way of knowing whether * + * it was invoked by interrupt or by an escape sequence. * + * Therefore, unlike all of the other handler routines ex- * + * cept dfhand(), fphand() does not initialize the object * + * buffer, leaving that chore to the caller. * + * * + * FOR NOW, fphand() does not disassemble floating-point * + * opcodes to floating mnemonics, but simply outputs the * + * object code as .byte directives. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +fphand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF fphand() * * * * * * * * * */ + + register int k; + + segflg = 0; + + FETCH(k); + + printf("\t.byte\t0x%02.2x\t\t| 8087 code sequence\n", + objbuf[0]); + + for (k = 1; k < objptr; ++k) + printf("\t.byte\t0x%02.2x\n",objbuf[k]); + +/* objout(); FOR NOW */ + +}/* * * * * * * * * * * END OF fphand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for variable software interrupt * + * opcodes. It is included in this file because PC/IX im- * + * plements its software floating-point emulation by means * + * of interrupts. Any interrupt in the range 0xd8 through * + * 0xdf is an NDP-emulation interrupt, and is specially * + * handled by the assembler. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +inhand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF inhand() * * * * * * * * * */ + + register int k; + + objini(j); + + FETCH(k); + + if (ISFLOP(k)) + { + fphand(k); + return; + } + + printf("%s\t%d\n",optab[j].text,k); + + objout(); + +}/* * * * * * * * * * * END OF inhand() * * * * * * * * * * */ + + diff --git a/commands/dis88/dishand.c b/commands/dis88/dishand.c new file mode 100755 index 000000000..56c63210d --- /dev/null +++ b/commands/dis88/dishand.c @@ -0,0 +1,1049 @@ +static char *sccsid = + "@(#) dishand.c, Ver. 2.1 created 00:00:00 87/09/01"; + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 1987 G. M. Harding, all rights reserved * + * * + * Permission to copy and redistribute is hereby granted, * + * provided full source code, with all copyright notices, * + * accompanies any redistribution. * + * * + * This file contains the source code for most of the spe- * + * cialized handler routines of the disassembler program. * + * (The file disfp.c contains handler routines specific to * + * the 8087 numeric co-processor.) Each handler routine * + * interprets the opcode byte (and subsequent data bytes, * + * if any) of a particular family of opcodes, and is re- * + * sponsible for generating appropriate output. All of the * + * code in this file is highly MACHINE-SPECIFIC, and would * + * have to be rewritten for a different CPU. The handler * + * routines are accessed only via pointers in the optab[] * + * array, however, so machine dependencies are confined to * + * this file, its sister file "disfp.c", and the data file * + * "distabs.c". * + * * + * All of the code in this file is based on the assumption * + * of sixteen-bit integers. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "dis.h" /* Disassembler declarations */ + +int segflg; /* Segment-override flag */ + +unsigned char objbuf[OBJMAX]; /* Buffer for object code */ + +int objptr; /* Index into objbuf[] */ + +unsigned long PC; /* Current program counter */ + + /* * * * * * MISCELLANEOUS SUPPORTING ROUTINES * * * * * */ + + +void +objini(j) /* Object code init routine */ + + register int j; + +{ + if ((segflg == 1) || (segflg == 2)) + segflg *= 3; + else + segflg = 0; + objptr = 0; + objbuf[objptr++] = (unsigned char)(j); +} + + +void +objout() /* Object-code output routine */ + +{ + register int k; + + if ( ! objflg ) + return; + else + { + printf("\t|"); + if (symptr >= 0) + printf(" %05.5lx:",(PC + 1L - (long)(objptr))); + for (k = 0; k < objptr; ++k) + printf(" %02.2x",objbuf[k]); + putchar('\n'); + } +} + + +void +badseq(j,k) /* Invalid-sequence routine */ + + register int j, k; + +{ + printf("\t.byte\t0x%02.2x\t\t| invalid code sequence\n",j); + printf("\t.byte\t0x%02.2x\n",k); +} + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This routine is the first of several opcode-specific * + * handlers, each of which is dedicated to a particular * + * opcode family. A pointer to a handler routine is con- * + * tained in the second field of each optab[] entry. The * + * dfhand() routine is the default handler, invoked when * + * no other handler is appropriate (generally, when an in- * + * valid opcode is encountered). * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +dfhand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF dfhand() * * * * * * * * * */ + + segflg = 0; + + printf("\t.byte\t0x%02.2x",j); + + if (optab[j].min || optab[j].max) + putchar('\n'); + else + printf("\t\t| unimplemented opcode\n"); + +}/* * * * * * * * * * * END OF dfhand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the single-byte handler, invoked whenever a * + * one-byte opcode is encountered. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +sbhand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF sbhand() * * * * * * * * * */ + + objini(j); + + if (j == 0x2e) /* seg cs */ + segflg = 1; + + if ((j == 0x26) /* seg es */ + || (j == 0x36) /* seg ss */ + || (j == 0x3e)) /* seg ds */ + segflg = 2; + + printf("%s\n",optab[j].text); + + objout(); + +}/* * * * * * * * * * * END OF sbhand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for most of the processor's regular * + * arithmetic operations. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +aohand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF aohand() * * * * * * * * * */ + + register int k; + int m, n; + char b[64]; + + objini(j); + + switch (j & 7) + { + case 0 : + case 1 : + case 2 : + case 3 : + printf("%s\t",optab[j].text); + FETCH(k); + printf("%s\n",mtrans(j,k,TR_STD)); + break; + case 4 : + FETCH(k); + printf("%s\tal,*0x%02.2x\n",optab[j].text,k); + break; + case 5 : + FETCH(m); + FETCH(n); + k = (n << 8) | m; + if (lookext((long)(k),(PC - 1),b)) + printf("%s\tax,#%s\n",optab[j].text,b); + else + printf("%s\tax,#0x%04.4x\n",optab[j].text,k); + break; + default : + dfhand(j); + break; + } + + objout(); + +}/* * * * * * * * * * * END OF aohand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for opcodes which perform short * + * (eight-bit) relative jumps. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +sjhand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF sjhand() * * * * * * * * * */ + + register int k; + int m; + + objini(j); + + FETCH(m); + + if (m & 0x80) + k = 0xff00; + else + k = 0; + + k |= m; + + printf("%s\t%s\t\t| loc %05.5lx\n",optab[j].text, + lookup((PC + k + 1L),N_TEXT,LOOK_REL,-1L), + (PC + k + 1L)); + + objout(); + +}/* * * * * * * * * * * END OF sjhand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for a loosely-knit family of op- * + * codes which perform arithmetic and logical operations, * + * and which take immediate data. The routine's logic is * + * rather complex, so, in an effort to avoid additional * + * complexity, the search for external references in the * + * relocation table has been dispensed with. Eager hackers * + * can try their hand at coding such a search. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +imhand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF imhand() * * * * * * * * * */ + + unsigned long pc; + register int k; + int offset, oflag, immed, iflag, mod, opi, w, rm; + int m, n; + static char a[100], b[30]; + + objini(j); + + FETCH(k); + + pc = PC + 1; + + offset = 0; + mod = (k & 0xc0) >> 6; + opi = (k & 0x38) >> 3; + w = j & 1; + rm = k & 7; + + if ((j & 2) + && ((opi == 1) + || (opi == 4) + || (opi == 6))) + { + badseq(j,k); + return; + } + + strcpy(a,OPFAM[opi]); + + if ( ! w ) + strcat(a,"b"); + + if ((oflag = mod) > 2) + oflag = 0; + + if ((mod == 0) && (rm == 6)) + { + FETCH(m); + FETCH(n); + offset = (n << 8) | m; + } + else if (oflag) + if (oflag == 2) + { + FETCH(m); + FETCH(n); + offset = (n << 8) | m; + } + else + { + FETCH(m); + if (m & 0x80) + n = 0xff00; + else + n = 0; + offset = n | m; + } + + switch (j & 3) + { + case 0 : + case 2 : + FETCH(immed); + iflag = 0; + break; + case 1 : + FETCH(m); + FETCH(n); + immed = (n << 8) | m; + iflag = 1; + break; + case 3 : + FETCH(immed); + if (immed & 0x80) + immed &= 0xff00; + iflag = 0; + break; + } + + strcat(a,"\t"); + + switch (mod) + { + case 0 : + if (rm == 6) + strcat(a, + lookup((long)(offset),N_DATA,LOOK_ABS,pc)); + else + { + sprintf(b,"(%s)",REGS0[rm]); + strcat(a,b); + } + break; + case 1 : + case 2 : + if (mod == 1) + strcat(a,"*"); + else + strcat(a,"#"); + sprintf(b,"%d(",offset); + strcat(a,b); + strcat(a,REGS1[rm]); + strcat(a,")"); + break; + case 3 : + strcat(a,REGS[(w << 3) | rm]); + break; + } + + strcat(a,","); + if (iflag) + strcat(a,"#"); + else + strcat(a,"*"); + sprintf(b,"%d",immed); + strcat(a,b); + + printf("%s\n",a); + + objout(); + +}/* * * * * * * * * * * END OF imhand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for various "mov"-type opcodes * + * which use the mod, reg, and r/m fields of the second * + * code byte in a standard, straightforward way. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +mvhand(j) + + int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF mvhand() * * * * * * * * * */ + + register int k, m = j; + + objini(j); + + FETCH(k); + + if ((m == 0x84) || (m == 0x85) /* Kind of kludgey */ + || (m == 0xc4) || (m == 0xc5) + || (m == 0x8d)) + if (m & 0x40) + m |= 0x03; + else + m |= 0x02; + + printf("%s\t%s\n",optab[j].text,mtrans(m,k,TR_STD)); + + objout(); + +}/* * * * * * * * * * * END OF mvhand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for segment-register "mov" opcodes. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +mshand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF mshand() * * * * * * * * * */ + + register int k; + + objini(j); + + FETCH(k); + + if (k & 0x20) + { + badseq(j,k); + return; + } + + printf("%s\t%s\n",optab[j].text,mtrans(j,k,TR_SEG)); + + objout(); + +}/* * * * * * * * * * * END OF mshand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for pops, other than single-byte * + * pops. (The 8088 allows popping into any register, or * + * directly into memory, accessed either immediately or * + * through a register and an index.) * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +pohand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF pohand() * * * * * * * * * */ + + char *a; + register int k; + + objini(j); + + FETCH(k); + + if (k & 0x38) + { + badseq(j,k); + return; + } + + printf("%s\t",optab[j].text); + + a = mtrans((j & 0xfd),k,TR_STD); + + mtrunc(a); + + printf("%s\n",a); + + objout(); + +}/* * * * * * * * * * * END OF pohand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler routine for intersegment calls and * + * jumps. Its output is never symbolic, because the host * + * linker does not allow symbolic intersegment address * + * references except by means of symbolic constants, and * + * any such constants in the symbol table, even if they * + * are of the appropriate value, may be misleading. In * + * compiled code, intersegment references should not be * + * encountered, and even in assembled code, they should * + * occur infrequently. If and when they do occur, however, * + * they will be disassembled in absolute form. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +cihand(j) + + int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF cihand() * * * * * * * * * */ + + register int m, n; + + objini(j); + + printf("%s\t",optab[j].text); + + FETCH(m); + FETCH(n); + + printf("#0x%04.4x,",((n << 8) | m)); + + FETCH(m); + FETCH(n); + + printf("#0x%04.4x\n",((n << 8) | m)); + + objout(); + +}/* * * * * * * * * * * END OF cihand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for "mov" opcodes with immediate * + * data. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +mihand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF mihand() * * * * * * * * * */ + + register int k; + int m, n; + char b[64]; + + objini(j); + + printf("%s",optab[j].text); + + if (j & 8) + { + FETCH(m); + FETCH(n); + k = ((n << 8) | m); + if (lookext((long)(k),(PC - 1),b)) + printf("#%s\n",b); + else + printf("#%d\n",k); + } + else + { + FETCH(m); + printf("*%d\n",m); + } + + objout(); + +}/* * * * * * * * * * * END OF mihand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for a family of quick-move opcodes. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +mqhand(j) + + int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF mqhand() * * * * * * * * * */ + + unsigned long pc; + register int m, n; + + objini(j); + + pc = PC + 1; + + FETCH(m); + FETCH(n); + + m = (n << 8) | m; + + printf("%s\t",optab[j].text); + + if (j & 2) + printf("%s,%s\n", + lookup((long)(m),N_DATA,LOOK_ABS,pc), + REGS[(j & 1) << 3]); + else + printf("%s,%s\n", + REGS[(j & 1) << 3], + lookup((long)(m),N_DATA,LOOK_ABS,pc)); + + objout(); + +}/* * * * * * * * * * * END OF mqhand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for a family of quick-test opcodes. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +tqhand(j) + + int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF tqhand() * * * * * * * * * */ + + register int m, n; + int k; + char b[64]; + + objini(j); + + printf("%s\t%s,",optab[j].text,REGS[(j & 1) << 3]); + + FETCH(m); + + if (j & 1) + { + FETCH(n); + k = ((n << 8) | m); + if (lookext((long)(k),(PC - 1),b)) + printf("#%s\n",b); + else + printf("#%d\n",k); + } + else + { + if (m & 80) + m |= 0xff00; + printf("*%d\n",m); + } + + objout(); + +}/* * * * * * * * * * * END OF tqhand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for multiple-byte "return" opcodes. * + * The 8088 allows returns to take an optional 16-bit ar- * + * gument, which reflects the amount to be added to SP * + * after the pop of the return address. The idea is to * + * facilitate the use of local parameters on the stack. * + * After some rumination, it was decided to disassemble * + * any such arguments as absolute quantities, rather than * + * rummaging through the symbol table for possible corre- * + * sponding constants. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +rehand(j) + + int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF rehand() * * * * * * * * * */ + + register int m, n; + + objini(j); + + FETCH(m); + FETCH(n); + + m = (n << 8) | m; + + printf("%s\t#0x%04.4x\n",optab[j].text,m); + + objout(); + +}/* * * * * * * * * * * END OF rehand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for "mov" opcodes involving memory * + * and immediate data. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +mmhand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF mmhand() * * * * * * * * * */ + + char *a; + register int k; + char b[64]; + + objini(j); + + FETCH(k); + + if (k & 0x38) + { + badseq(j,k); + return; + } + + printf("%s",optab[j].text); + + if ( ! (j & 1) ) + putchar('b'); + + a = mtrans((j & 0xfd),(k & 0xc7),TR_STD); + + mtrunc(a); + + printf("\t%s,",a); + + if (j & 1) + { + FETCH(j); + FETCH(k); + k = (k << 8) | j; + if (lookext((long)(k),(PC - 1),b)) + printf("#%s\n",b); + else + printf("#%d\n",k); + } + else + { + FETCH(k); + printf("*%d\n",k); + } + + objout(); + +}/* * * * * * * * * * * END OF mmhand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for the 8088 family of shift and * + * rotate instructions. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +srhand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF srhand() * * * * * * * * * */ + + char *a; + register int k; + + objini(j); + + FETCH(k); + + if ((k & 0x38) == 0x30) + { + badseq(j,k); + return; + } + + printf("%s",OPFAM[((k & 0x38) >> 3) + 16]); + + if ( ! (j & 1) ) + putchar('b'); + + a = mtrans((j & 0xfd),(k & 0xc7),TR_STD); + + mtrunc(a); + + printf("\t%s",a); + + if (j & 2) + printf(",cl\n"); + else + printf(",*1\n"); + + objout(); + +}/* * * * * * * * * * * END OF srhand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for the ASCII-adjust opcodes. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +aahand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF aahand() * * * * * * * * * */ + + register int k; + + objini(j); + + FETCH(k); + + if (k != 0x0a) + { + badseq(j,k); + return; + } + + printf("%s\n",optab[j].text); + + objout(); + +}/* * * * * * * * * * * END OF aahand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for port I/O opcodes which specify * + * the port address as an immediate operand. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +iohand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF iohand() * * * * * * * * * */ + + register int k; + + objini(j); + + FETCH(k); + + printf("%s\t0x%02.2x\n",optab[j].text,k); + + objout(); + +}/* * * * * * * * * * * END OF iohand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for opcodes which perform long * + * (sixteen-bit) relative jumps and calls. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +ljhand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF ljhand() * * * * * * * * * */ + + register int k; + int m, n; + + objini(j); + + FETCH(m); + FETCH(n); + + k = (n << 8) | m; + + printf("%s\t%s\t\t| loc %05.5lx\n",optab[j].text, + lookup((PC + k + 1L),N_TEXT,LOOK_LNG,(PC - 1L)), + (PC + k + 1L)); + + objout(); + +}/* * * * * * * * * * * END OF ljhand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for a pair of oddball opcodes (0xf6 * + * and 0xf7) which perform miscellaneous arithmetic opera- * + * tions not dealt with elsewhere. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +mahand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF mahand() * * * * * * * * * */ + + char *a; + register int k; + char b[64]; + + objini(j); + + FETCH(k); + + a = mtrans((j & 0xfd),(k & 0xc7),TR_STD); + + mtrunc(a); + + switch (((k = objbuf[1]) & 0x38) >> 3) + { + case 0 : + printf("\ttest"); + break; + case 1 : + badseq(j,k); + return; + case 2 : + printf("\tnot"); + break; + case 3 : + printf("\tneg"); + break; + case 4 : + printf("\tmul"); + break; + case 5 : + printf("\timul"); + break; + case 6 : + printf("\tdiv"); + break; + case 7 : + printf("\tidiv"); + break; + } + + if ( ! (j & 1) ) + putchar('b'); + + printf("\t%s",a); + + if (k & 0x38) + putchar('\n'); + else + if (j & 1) + { + FETCH(j); + FETCH(k); + k = (k << 8) | j; + if (lookext((long)(k),(PC - 1),b)) + printf(",#%s\n",b); + else + printf(",#%d\n",k); + } + else + { + FETCH(k); + printf(",*%d\n",k); + } + + objout(); + +}/* * * * * * * * * * * END OF mahand() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the handler for miscellaneous jump, call, push, * + * and increment/decrement opcodes (0xfe and 0xff) which * + * are not dealt with elsewhere. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +mjhand(j) + + register int j; /* Pointer to optab[] entry */ + +{/* * * * * * * * * * START OF mjhand() * * * * * * * * * */ + + char *a; + register int k; + + objini(j); + + FETCH(k); + + a = mtrans((j & 0xfd),(k & 0xc7),TR_STD); + + mtrunc(a); + + switch (((k = objbuf[1]) & 0x38) >> 3) + { + case 0 : + printf("\tinc"); + if ( ! (j & 1) ) + putchar('b'); + putchar('\t'); + break; + case 1 : + printf("\tdec"); + if ( ! (j & 1) ) + putchar('b'); + putchar('\t'); + break; + case 2 : + if (j & 1) + printf("\tcall\t@"); + else + goto BAD; + break; + case 3 : + if (j & 1) + printf("\tcalli\t@"); + else + goto BAD; + break; + case 4 : + if (j & 1) + printf("\tjmp\t@"); + else + goto BAD; + break; + case 5 : + if (j & 1) + printf("\tjmpi\t@"); + else + goto BAD; + break; + case 6 : + if (j & 1) + printf("\tpush\t"); + else + goto BAD; + break; + case 7 : + BAD : + badseq(j,k); + return; + } + + printf("%s\n",a); + + objout(); + +}/* * * * * * * * * * * END OF mjhand() * * * * * * * * * * */ + + diff --git a/commands/dis88/dismain.c b/commands/dis88/dismain.c new file mode 100755 index 000000000..d317f0062 --- /dev/null +++ b/commands/dis88/dismain.c @@ -0,0 +1,631 @@ +static char *sccsid = "@(#) dismain.c, Ver. 2.1 created 00:00:00 87/09/01"; + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 1987 G. M. Harding, all rights reserved * + * * + * Permission to copy and redistribute is hereby granted, * + * provided full source code, with all copyright notices, * + * accompanies any redistribution. * + * * + * This file contains the source code for the machine- * + * independent portions of a disassembler program to run * + * in a Unix (System III) environment. It expects, as its * + * input, a file in standard a.out format, optionally con- * + * taining symbol table information. If a symbol table is * + * present, it will be used in the disassembly; otherwise, * + * all address references will be literal (absolute). * + * * + * The disassembler program was originally written for an * + * Intel 8088 CPU. However, all details of the actual CPU * + * architecture are hidden in three machine-specific files * + * named distabs.c, dishand.c, and disfp.c (the latter * + * file is specific to the 8087 numeric co-processor). The * + * code in this file is generic, and should require mini- * + * mal revision if a different CPU is to be targeted. If a * + * different version of Unix is to be targeted, changes to * + * this file may be necessary, and if a completely differ- * + * ent OS is to be targeted, all bets are off. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "dis.h" /* Disassembler declarations */ + +extern char *release; /* Contains release string */ +static char *IFILE = NULL; /* Points to input file name */ +static char *OFILE = NULL; /* Points to output file name */ +static char *PRG; /* Name of invoking program */ +static unsigned long zcount; /* Consecutive "0" byte count */ +int objflg = 0; /* Flag: output object bytes */ + +#define unix 1 +#define i8086 1 +#define ibmpc 1 + +#if unix && i8086 && ibmpc /* Set the CPU identifier */ +static int cpuid = 1; +#else +static int cpuid = 0; +#endif + +_PROTOTYPE(static void usage, (char *s )); +_PROTOTYPE(static void fatal, (char *s, char *t )); +_PROTOTYPE(static void zdump, (unsigned long beg )); +_PROTOTYPE(static void prolog, (void)); +_PROTOTYPE(static void distext, (void)); +_PROTOTYPE(static void disdata, (void)); +_PROTOTYPE(static void disbss, (void)); + +_PROTOTYPE(static char *invoker, (char *s)); +_PROTOTYPE(static int objdump, (char *c)); +_PROTOTYPE(static char *getlab, (int type)); +_PROTOTYPE(static void prolog, (void)); + + /* * * * * * * MISCELLANEOUS UTILITY FUNCTIONS * * * * * * */ + +static void +usage(s) + register char *s; +{ + fprintf(stderr,"Usage: %s [-o] ifile [ofile]\n",s); + exit(-1); +} + +static void +fatal(s,t) + register char *s, *t; +{ + fprintf(stderr,"\07%s: %s\n",s,t); + exit(-1); +} + +static void +zdump(beg) + unsigned long beg; +{ + beg = PC - beg; + if (beg > 1L) + printf("\t.zerow\t%ld\n",(beg >> 1)); + if (beg & 1L) + printf("\t.byte\t0\n"); +} + +static char * +invoker(s) + register char *s; +{ + register int k; + + k = strlen(s); + + while (k--) + if (s[k] == '/') + { + s += k; + ++s; + break; + } + + return (s); +} + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This rather tricky routine supports the disdata() func- * + * tion. Its job is to output the code for a sequence of * + * data bytes whenever the object buffer is full, or when * + * a symbolic label is to be output. However, it must also * + * keep track of consecutive zero words so that lengthy * + * stretches of null data can be compressed by the use of * + * an appropriate assembler pseudo-op. It does this by * + * setting and testing a file-wide flag which counts suc- * + * cessive full buffers of null data. The function returns * + * a logical TRUE value if it outputs anything, logical * + * FALSE otherwise. (This enables disdata() to determine * + * whether to output a new synthetic label when there is * + * no symbol table.) * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static int +objdump(c) + + register char *c; + +{/* * * * * * * * * * START OF objdump() * * * * * * * * * */ + + register int k; + int retval = 0; + + if (objptr == OBJMAX) + { + for (k = 0; k < OBJMAX; ++k) + if (objbuf[k]) + break; + if (k == OBJMAX) + { + zcount += k; + objptr = 0; + if (c == NULL) + return (retval); + } + } + + if (zcount) + { + printf("\t.zerow\t%ld\n",(zcount >> 1)); + ++retval; + zcount = 0L; + } + + if (objptr) + { + printf("\t.byte\t"); + ++retval; + } + else + return (retval); + + for (k = 0; k < objptr; ++k) + { + printf("0x%02.2x",objbuf[k]); + if (k < (objptr - 1)) + putchar(','); + else + putchar('\n'); + } + + objptr = 0; + + return (retval); + +}/* * * * * * * * * * END OF objdump() * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This routine, called at the beginning of the input * + * cycle for each object byte, and before any interpreta- * + * tion is attempted, searches the symbol table for any * + * symbolic name with a value corresponding to the cur- * + * rent PC and a type corresponding to the segment type * + * (i.e., text, data, or bss) specified by the function's * + * argument. If any such name is found, a pointer to it is * + * returned; otherwise, a NULL pointer is returned. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static char * +getlab(type) +register int type; +{/* * * * * * * * * * START OF getlab() * * * * * * * * * */ + + register int k; + static char b[32], c[10]; + + if (symptr < 0) + if ((type == N_TEXT) + || ((type == N_DATA) && ( ! objptr ) && ( ! zcount ))) + { + if (type == N_TEXT) + sprintf(b,"T%05.5lx:",PC); + else + sprintf(b,"D%05.5lx:",PC); + return (b); + } + else + return (NULL); + + for (k = 0; k <= symptr; ++k) + if ((symtab[k].n_value == PC) + && ((symtab[k].n_sclass & N_SECT) == type)) + { + sprintf(b,"%s:\n",getnam(k)); + if (objflg && (type != N_TEXT)) + sprintf(c,"| %05.5lx\n",PC); + strcat(b,c); + return (b); + } + + return (NULL); + +}/* * * * * * * * * * * END OF getlab() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This routine performs a preliminary scan of the symbol * + * table, before disassembly begins, and outputs declara- * + * tions of globals and constants. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static void +prolog() + +{/* * * * * * * * * * START OF prolog() * * * * * * * * * */ + + register int j, flag; + + if (symptr < 0) + return; + + for (j = flag = 0; j <= symptr; ++j) + if ((symtab[j].n_sclass & N_CLASS) == C_EXT) + if (((symtab[j].n_sclass & N_SECT) > N_UNDF) + && ((symtab[j].n_sclass & N_SECT) < N_COMM)) + { + char *c = getnam(j); + printf("\t.globl\t%s",c); + if (++flag == 1) + { + putchar('\t'); + if (strlen(c) < 8) + putchar('\t'); + printf("| Internal global\n"); + } + else + putchar('\n'); + } + else + if (symtab[j].n_value) + { + char *c = getnam(j); + printf("\t.comm\t%s,0x%08.8lx",c, + symtab[j].n_value); + if (++flag == 1) + printf("\t| Internal global\n"); + else + putchar('\n'); + } + + if (flag) + putchar('\n'); + + for (j = flag = 0; j <= relptr; ++j) + if (relo[j].r_symndx < S_BSS) + { + char *c = getnam(relo[j].r_symndx); + ++flag; + printf("\t.globl\t%s",c); + putchar('\t'); + if (strlen(c) < 8) + putchar('\t'); + printf("| Undef: %05.5lx\n",relo[j].r_vaddr); + } + + if (flag) + putchar('\n'); + + for (j = flag = 0; j <= symptr; ++j) + if ((symtab[j].n_sclass & N_SECT) == N_ABS) + { + char *c = getnam(j); + printf("%s=0x%08.8lx",c,symtab[j].n_value); + if (++flag == 1) + { + printf("\t\t"); + if (strlen(c) < 5) + putchar('\t'); + printf("| Literal\n"); + } + else + putchar('\n'); + } + + if (flag) + putchar('\n'); + +}/* * * * * * * * * * * END OF prolog() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This function is responsible for disassembly of the * + * object file's text segment. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static void +distext() + +{/* * * * * * * * * * START OF distext() * * * * * * * * * */ + + char *c; + register int j; + register void (*f)(); + + for (j = 0; j < (int)(HDR.a_hdrlen); ++j) + getchar(); + + printf("| %s, %s\n\n",PRG,release); + + printf("| @("); + + printf("#)\tDisassembly of %s",IFILE); + + if (symptr < 0) + printf(" (no symbols)\n\n"); + else + printf("\n\n"); + + if (HDR.a_flags & A_EXEC) + printf("| File is executable\n\n"); + + if (HDR.a_flags & A_SEP) + { + printf("| File has split I/D space, and may have\n"); + printf("| extraneous instructions in text segment\n\n"); + } + + prolog(); + + printf("\t.text\t\t\t| loc = %05.5lx, size = %05.5lx\n\n", + PC,HDR.a_text); + + segflg = 0; + + for (PC = 0L; PC < HDR.a_text; ++PC) + { + j = getchar() & 0xff; + if ((j == 0) && ((PC + 1L) == HDR.a_text)) + { + ++PC; + break; + } + if ((c = getlab(N_TEXT)) != NULL) + printf("%s",c); + f = optab[j].func; + (*f)(j); + } + +}/* * * * * * * * * * END OF distext() * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This function handles the object file's data segment. * + * There is no good way to disassemble a data segment, be- * + * cause it is impossible to tell, from the object code * + * alone, what each data byte refers to. If it refers to * + * an external symbol, the reference can be resolved from * + * the relocation table, if there is one. However, if it * + * refers to a static symbol, it cannot be distinguished * + * from numeric, character, or other pointer data. In some * + * cases, one might make a semi-educated guess as to the * + * nature of the data, but such guesses are inherently * + * haphazard, and they are bound to be wrong a good por- * + * tion of the time. Consequently, the data segment is * + * disassembled as a byte stream, which will satisfy no * + * one but which, at least, will never mislead anyone. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static void +disdata() + +{/* * * * * * * * * * START OF disdata() * * * * * * * * * */ + + register char *c; + register int j; + unsigned long end; + + putchar('\n'); + + if (HDR.a_flags & A_SEP) + { + PC = 0L; + end = HDR.a_data; + } + else + end = HDR.a_text + HDR.a_data; + + printf("\t.data\t\t\t| loc = %05.5lx, size = %05.5lx\n\n", + PC,HDR.a_data); + + segflg = 0; + + for (objptr = 0, zcount = 0L; PC < end; ++PC) + { + if ((c = getlab(N_DATA)) != NULL) + { + objdump(c); + printf("%s",c); + } + if (objptr >= OBJMAX) + if (objdump(NULL) && (symptr < 0)) + printf("D%05.5lx:",PC); + j = getchar() & 0xff; + objbuf[objptr++] = j; + } + + objdump(""); + +}/* * * * * * * * * * END OF disdata() * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This function handles the object file's bss segment. * + * Disassembly of the bss segment is easy, because every- * + * thing in it is zero by definition. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +static void disbss() + +{/* * * * * * * * * * START OF disbss() * * * * * * * * * */ + + register int j; + register char *c; + unsigned long beg, end; + + putchar('\n'); + + if (HDR.a_flags & A_SEP) + end = HDR.a_data + HDR.a_bss; + else + end = HDR.a_text + HDR.a_data + HDR.a_bss; + + printf("\t.bss\t\t\t| loc = %05.5lx, size = %05.5lx\n\n", + PC,HDR.a_bss); + + segflg = 0; + + for (beg = PC; PC < end; ++PC) + if ((c = getlab(N_BSS)) != NULL) + { + if (PC > beg) + { + zdump(beg); + beg = PC; + } + printf("%s",c); + } + + if (PC > beg) + zdump(beg); + +}/* * * * * * * * * * * END OF disbss() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This is the program entry point. The command line is * + * searched for an input file name, which must be present. * + * An optional output file name is also permitted; if none * + * is found, standard output is the default. One command- * + * line option is available: "-o", which causes the pro- * + * gram to include object code in comments along with its * + * mnemonic output. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +main(argc,argv) + + int argc; /* Command-line args from OS */ + register char **argv; + +{/* * * * * * * * * * * START OF main() * * * * * * * * * * */ + + char a[1024]; + register int fd; + long taboff, tabnum; + long reloff, relnum; + + PRG = invoker(*argv); + + while (*++argv != NULL) /* Process command-line args */ + if (**argv == '-') + switch (*++*argv) + { + case 'o' : + if (*++*argv) + usage(PRG); + else + ++objflg; + break; + default : + usage(PRG); + } + else + if (IFILE == NULL) + IFILE = *argv; + else if (OFILE == NULL) + OFILE = *argv; + else + usage(PRG); + + if (IFILE == NULL) + usage(PRG); + else + if ((fd = open(IFILE,0)) < 0) + { + sprintf(a,"can't access input file %s",IFILE); + fatal(PRG,a); + } + + if (OFILE != NULL) + if (freopen(OFILE,"w",stdout) == NULL) + { + sprintf(a,"can't open output file %s",OFILE); + fatal(PRG,a); + } + + if ( ! cpuid ) + fprintf(stderr,"\07%s: warning: host/cpu clash\n",PRG); + + read(fd, (char *) &HDR,sizeof(struct exec)); + + if (BADMAG(HDR)) + { + sprintf(a,"input file %s not in object format",IFILE); + fatal(PRG,a); + } + + if (HDR.a_cpu != A_I8086) + { + sprintf(a,"%s is not an 8086/8088 object file",IFILE); + fatal(PRG,a); + } + + if (HDR.a_hdrlen <= A_MINHDR) + HDR.a_trsize = HDR.a_drsize = 0L; + HDR.a_tbase = HDR.a_dbase = 0L; +/* AST emergency patch + HDR.a_lnums = HDR.a_toffs = 0L; +*/ + + reloff = HDR.a_text /* Compute reloc data offset */ + + HDR.a_data + + (long)(HDR.a_hdrlen); + + relnum = + (HDR.a_trsize + HDR.a_drsize) / sizeof(struct reloc); + + taboff = reloff /* Compute name table offset */ + + HDR.a_trsize + + HDR.a_drsize; + + tabnum = HDR.a_syms / sizeof(struct nlist); + + if (relnum > MAXSYM) + fatal(PRG,"reloc table overflow"); + + if (tabnum > MAXSYM) + fatal(PRG,"symbol table overflow"); + + if (relnum) /* Get reloc data */ + if (lseek(fd,reloff,0) != reloff) + fatal(PRG,"lseek error"); + else + { + for (relptr = 0; relptr < relnum; ++relptr) + read(fd, (char *) &relo[relptr],sizeof(struct reloc)); + relptr--; + } + + if (tabnum) /* Read in symtab */ + if (lseek(fd,taboff,0) != taboff) + fatal(PRG,"lseek error"); + else + { + for (symptr = 0; symptr < tabnum; ++symptr) + read(fd, (char *) &symtab[symptr],sizeof(struct nlist)); + symptr--; + } + else + fprintf(stderr,"\07%s: warning: no symbols\n",PRG); + + close(fd); + + if (freopen(IFILE,"r",stdin) == NULL) + { + sprintf(a,"can't reopen input file %s",IFILE); + fatal(PRG,a); + } + + distext(); + + disdata(); + + disbss(); + + exit(0); + +}/* * * * * * * * * * * END OF main() * * * * * * * * * * */ diff --git a/commands/dis88/disrel.c b/commands/dis88/disrel.c new file mode 100755 index 000000000..37f322123 --- /dev/null +++ b/commands/dis88/disrel.c @@ -0,0 +1,30 @@ +static char *copyright = + "@(#) Copyright (C) 1987 G. M. Harding, all rights reserved"; + +static char *sccsid = + "@(#) disrel.c, Ver. 2.1 created 00:00:00 87/09/01"; + +char *release = + "release 2.1 (MINIX)"; + + /* + ** + ** This file documents the major revisions to the 8088 sym- + ** bolic disassembler. It also contains the release string + ** which is output at the head of each disassembly, and the + ** copyright string which must be incorporated in any code + ** distribution. + ** + ** Permission to copy and redistribute is hereby granted, + ** provided full source code, with all copyright notices, + ** accompanies any redistribution. + ** + ** REVISION HISTORY: + ** + ** SEP 87: + ** After internal shakeout, released on Usenet. + ** + ** JUN 88: + ** Ported to MINIX + */ + diff --git a/commands/dis88/distabs.c b/commands/dis88/distabs.c new file mode 100755 index 000000000..6390cecc7 --- /dev/null +++ b/commands/dis88/distabs.c @@ -0,0 +1,708 @@ +static char *sccsid = + "@(#) distabs.c, Ver. 2.1 created 00:00:00 87/09/01"; + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * Copyright (C) 1987 G. M. Harding, all rights reserved * + * * + * Permission to copy and redistribute is hereby granted, * + * provided full source code, with all copyright notices, * + * accompanies any redistribution. * + * * + * This file contains the lookup tables and other data * + * structures for the Intel 8088 symbolic disassembler. It * + * also contains a few global routines which facilitate * + * access to the tables, for use primarily by the handler * + * functions. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +#include "dis.h" /* Disassembler declarations */ + +struct exec HDR; /* Used to hold header info */ + +struct nlist symtab[MAXSYM]; /* Array of symbol table info */ + +struct reloc relo[MAXSYM]; /* Array of relocation info */ + +int symptr = -1, /* Index into symtab[] */ + relptr = -1; /* Index into relo[] */ + +char *REGS[] = /* Table of register names */ + { + "al", "cl", "dl", "bl", "ah", "ch", "dh", "bh", + "ax", "cx", "dx", "bx", "sp", "bp", "si", "di", + "es", "cs", "ss", "ds" + }; + +char *REGS0[] = /* Mode 0 register name table */ + { + "bx_si", "bx_di", "bp_si", "bp_di", "si", "di", "", "bx" + }; + +char *REGS1[] = /* Mode 1 register name table */ + { + "bx_si", "bx_di", "bp_si", "bp_di", "si", "di", "bp", "bx" + }; + +int symrank[6][6] = /* Symbol type/rank matrix */ + { + /* UND ABS TXT DAT BSS COM */ + /* UND */ 5, 4, 1, 2, 3, 0, + /* ABS */ 1, 5, 4, 3, 2, 0, + /* TXT */ 4, 1, 5, 3, 2, 0, + /* DAT */ 3, 1, 2, 5, 4, 0, + /* BSS */ 3, 1, 2, 4, 5, 0, + /* COM */ 2, 0, 1, 3, 4, 5 + }; + + /* * * * * * * * * * * * OPCODE DATA * * * * * * * * * * * */ + +char ADD[] = "\tadd", /* Mnemonics by family */ + OR[] = "\tor", + ADC[] = "\tadc", + SBB[] = "\tsbb", + AND[] = "\tand", + SUB[] = "\tsub", + XOR[] = "\txor", + CMP[] = "\tcmp", + NOT[] = "\tnot", + NEG[] = "\tneg", + MUL[] = "\tmul", + DIV[] = "\tdiv", + MOV[] = "\tmov", + ESC[] = "\tesc", + TEST[] = "\ttest", + AMBIG[] = "", + ROL[] = "\trol", + ROR[] = "\tror", + RCL[] = "\trcl", + RCR[] = "\trcr", + SAL[] = "\tsal", + SHR[] = "\tshr", + SHL[] = "\tshl", + SAR[] = "\tsar"; + +char *OPFAM[] = /* Family lookup table */ + { + ADD, OR, ADC, SBB, AND, SUB, XOR, CMP, + NOT, NEG, MUL, DIV, MOV, ESC, TEST, AMBIG, + ROL, ROR, RCL, RCR, SAL, SHR, SHL, SAR + }; + +struct opcode optab[] = /* Table of opcode data */ + { + ADD, aohand, 2, 4, /* 0x00 */ + ADD, aohand, 2, 4, /* 0x01 */ + ADD, aohand, 2, 4, /* 0x02 */ + ADD, aohand, 2, 4, /* 0x03 */ + ADD, aohand, 2, 2, /* 0x04 */ + ADD, aohand, 3, 3, /* 0x05 */ + "\tpush\tes", sbhand, 1, 1, /* 0x06 */ + "\tpop\tes", sbhand, 1, 1, /* 0x07 */ + OR, aohand, 2, 4, /* 0x08 */ + OR, aohand, 2, 4, /* 0x09 */ + OR, aohand, 2, 4, /* 0x0a */ + OR, aohand, 2, 4, /* 0x0b */ + OR, aohand, 2, 2, /* 0x0c */ + OR, aohand, 3, 3, /* 0x0d */ + "\tpush\tcs", sbhand, 1, 1, /* 0x0e */ + NULL, dfhand, 0, 0, /* 0x0f */ + ADC, aohand, 2, 4, /* 0x10 */ + ADC, aohand, 2, 4, /* 0x11 */ + ADC, aohand, 2, 4, /* 0x12 */ + ADC, aohand, 2, 4, /* 0x13 */ + ADC, aohand, 2, 2, /* 0x14 */ + ADC, aohand, 3, 3, /* 0x15 */ + "\tpush\tss", sbhand, 1, 1, /* 0x16 */ + "\tpop\tss", sbhand, 1, 1, /* 0x17 */ + SBB, aohand, 2, 4, /* 0x18 */ + SBB, aohand, 2, 4, /* 0x19 */ + SBB, aohand, 2, 4, /* 0x1a */ + SBB, aohand, 2, 4, /* 0x1b */ + SBB, aohand, 2, 2, /* 0x1c */ + SBB, aohand, 3, 3, /* 0x1d */ + "\tpush\tds", sbhand, 1, 1, /* 0x1e */ + "\tpop\tds", sbhand, 1, 1, /* 0x1f */ + AND, aohand, 2, 4, /* 0x20 */ + AND, aohand, 2, 4, /* 0x21 */ + AND, aohand, 2, 4, /* 0x22 */ + AND, aohand, 2, 4, /* 0x23 */ + AND, aohand, 2, 2, /* 0x24 */ + AND, aohand, 3, 3, /* 0x25 */ + "\tseg\tes", sbhand, 1, 1, /* 0x26 */ + "\tdaa", sbhand, 1, 1, /* 0x27 */ + SUB, aohand, 2, 4, /* 0x28 */ + SUB, aohand, 2, 4, /* 0x29 */ + SUB, aohand, 2, 4, /* 0x2a */ + SUB, aohand, 2, 4, /* 0x2b */ + SUB, aohand, 2, 2, /* 0x2c */ + SUB, aohand, 3, 3, /* 0x2d */ + "\tseg\tcs", sbhand, 1, 1, /* 0x2e */ + "\tdas", sbhand, 1, 1, /* 0x2f */ + XOR, aohand, 2, 4, /* 0x30 */ + XOR, aohand, 2, 4, /* 0x31 */ + XOR, aohand, 2, 4, /* 0x32 */ + XOR, aohand, 2, 4, /* 0x33 */ + XOR, aohand, 2, 2, /* 0x34 */ + XOR, aohand, 3, 3, /* 0x35 */ + "\tseg\tss", sbhand, 1, 1, /* 0x36 */ + "\taaa", sbhand, 1, 1, /* 0x37 */ + CMP, aohand, 2, 4, /* 0x38 */ + CMP, aohand, 2, 4, /* 0x39 */ + CMP, aohand, 2, 4, /* 0x3a */ + CMP, aohand, 2, 4, /* 0x3b */ + CMP, aohand, 2, 2, /* 0x3c */ + CMP, aohand, 3, 3, /* 0x3d */ + "\tseg\tds", sbhand, 1, 1, /* 0x3e */ + "\taas", sbhand, 1, 1, /* 0x3f */ + "\tinc\tax", sbhand, 1, 1, /* 0x40 */ + "\tinc\tcx", sbhand, 1, 1, /* 0x41 */ + "\tinc\tdx", sbhand, 1, 1, /* 0x42 */ + "\tinc\tbx", sbhand, 1, 1, /* 0x43 */ + "\tinc\tsp", sbhand, 1, 1, /* 0x44 */ + "\tinc\tbp", sbhand, 1, 1, /* 0x45 */ + "\tinc\tsi", sbhand, 1, 1, /* 0x46 */ + "\tinc\tdi", sbhand, 1, 1, /* 0x47 */ + "\tdec\tax", sbhand, 1, 1, /* 0x48 */ + "\tdec\tcx", sbhand, 1, 1, /* 0x49 */ + "\tdec\tdx", sbhand, 1, 1, /* 0x4a */ + "\tdec\tbx", sbhand, 1, 1, /* 0x4b */ + "\tdec\tsp", sbhand, 1, 1, /* 0x4c */ + "\tdec\tbp", sbhand, 1, 1, /* 0x4d */ + "\tdec\tsi", sbhand, 1, 1, /* 0x4e */ + "\tdec\tdi", sbhand, 1, 1, /* 0x4f */ + "\tpush\tax", sbhand, 1, 1, /* 0x50 */ + "\tpush\tcx", sbhand, 1, 1, /* 0x51 */ + "\tpush\tdx", sbhand, 1, 1, /* 0x52 */ + "\tpush\tbx", sbhand, 1, 1, /* 0x53 */ + "\tpush\tsp", sbhand, 1, 1, /* 0x54 */ + "\tpush\tbp", sbhand, 1, 1, /* 0x55 */ + "\tpush\tsi", sbhand, 1, 1, /* 0x56 */ + "\tpush\tdi", sbhand, 1, 1, /* 0x57 */ + "\tpop\tax", sbhand, 1, 1, /* 0x58 */ + "\tpop\tcx", sbhand, 1, 1, /* 0x59 */ + "\tpop\tdx", sbhand, 1, 1, /* 0x5a */ + "\tpop\tbx", sbhand, 1, 1, /* 0x5b */ + "\tpop\tsp", sbhand, 1, 1, /* 0x5c */ + "\tpop\tbp", sbhand, 1, 1, /* 0x5d */ + "\tpop\tsi", sbhand, 1, 1, /* 0x5e */ + "\tpop\tdi", sbhand, 1, 1, /* 0x5f */ + NULL, dfhand, 0, 0, /* 0x60 */ + NULL, dfhand, 0, 0, /* 0x61 */ + NULL, dfhand, 0, 0, /* 0x62 */ + NULL, dfhand, 0, 0, /* 0x63 */ + NULL, dfhand, 0, 0, /* 0x64 */ + NULL, dfhand, 0, 0, /* 0x65 */ + NULL, dfhand, 0, 0, /* 0x66 */ + NULL, dfhand, 0, 0, /* 0x67 */ + NULL, dfhand, 0, 0, /* 0x68 */ + NULL, dfhand, 0, 0, /* 0x69 */ + NULL, dfhand, 0, 0, /* 0x6a */ + NULL, dfhand, 0, 0, /* 0x6b */ + NULL, dfhand, 0, 0, /* 0x6c */ + NULL, dfhand, 0, 0, /* 0x6d */ + NULL, dfhand, 0, 0, /* 0x6e */ + NULL, dfhand, 0, 0, /* 0x6f */ + "\tjo", sjhand, 2, 2, /* 0x70 */ + "\tjno", sjhand, 2, 2, /* 0x71 */ + "\tjc", sjhand, 2, 2, /* 0x72 */ + "\tjnc", sjhand, 2, 2, /* 0x73 */ + "\tjz", sjhand, 2, 2, /* 0x74 */ + "\tjnz", sjhand, 2, 2, /* 0x75 */ + "\tjna", sjhand, 2, 2, /* 0x76 */ + "\tja", sjhand, 2, 2, /* 0x77 */ + "\tjs", sjhand, 2, 2, /* 0x78 */ + "\tjns", sjhand, 2, 2, /* 0x79 */ + "\tjp", sjhand, 2, 2, /* 0x7a */ + "\tjnp", sjhand, 2, 2, /* 0x7b */ + "\tjl", sjhand, 2, 2, /* 0x7c */ + "\tjnl", sjhand, 2, 2, /* 0x7d */ + "\tjng", sjhand, 2, 2, /* 0x7e */ + "\tjg", sjhand, 2, 2, /* 0x7f */ + AMBIG, imhand, 3, 5, /* 0x80 */ + AMBIG, imhand, 4, 6, /* 0x81 */ + AMBIG, imhand, 3, 5, /* 0x82 */ + AMBIG, imhand, 3, 5, /* 0x83 */ + TEST, mvhand, 2, 4, /* 0x84 */ + TEST, mvhand, 2, 4, /* 0x85 */ + "\txchg", mvhand, 2, 4, /* 0x86 */ + "\txchg", mvhand, 2, 4, /* 0x87 */ + MOV, mvhand, 2, 4, /* 0x88 */ + MOV, mvhand, 2, 4, /* 0x89 */ + MOV, mvhand, 2, 4, /* 0x8a */ + MOV, mvhand, 2, 4, /* 0x8b */ + MOV, mshand, 2, 4, /* 0x8c */ + "\tlea", mvhand, 2, 4, /* 0x8d */ + MOV, mshand, 2, 4, /* 0x8e */ + "\tpop", pohand, 2, 4, /* 0x8f */ + "\tnop", sbhand, 1, 1, /* 0x90 */ + "\txchg\tax,cx", sbhand, 1, 1, /* 0x91 */ + "\txchg\tax,dx", sbhand, 1, 1, /* 0x92 */ + "\txchg\tax,bx", sbhand, 1, 1, /* 0x93 */ + "\txchg\tax,sp", sbhand, 1, 1, /* 0x94 */ + "\txchg\tax,bp", sbhand, 1, 1, /* 0x95 */ + "\txchg\tax,si", sbhand, 1, 1, /* 0x96 */ + "\txchg\tax,di", sbhand, 1, 1, /* 0x97 */ + "\tcbw", sbhand, 1, 1, /* 0x98 */ + "\tcwd", sbhand, 1, 1, /* 0x99 */ + "\tcalli", cihand, 5, 5, /* 0x9a */ + "\twait", sbhand, 1, 1, /* 0x9b */ + "\tpushf", sbhand, 1, 1, /* 0x9c */ + "\tpopf", sbhand, 1, 1, /* 0x9d */ + "\tsahf", sbhand, 1, 1, /* 0x9e */ + "\tlahf", sbhand, 1, 1, /* 0x9f */ + MOV, mqhand, 3, 3, /* 0xa0 */ + MOV, mqhand, 3, 3, /* 0xa1 */ + MOV, mqhand, 3, 3, /* 0xa2 */ + MOV, mqhand, 3, 3, /* 0xa3 */ + "\tmovb", sbhand, 1, 1, /* 0xa4 */ + "\tmovw", sbhand, 1, 1, /* 0xa5 */ + "\tcmpb", sbhand, 1, 1, /* 0xa6 */ + "\tcmpw", sbhand, 1, 1, /* 0xa7 */ + TEST, tqhand, 2, 2, /* 0xa8 */ + TEST, tqhand, 3, 3, /* 0xa9 */ + "\tstob", sbhand, 1, 1, /* 0xaa */ + "\tstow", sbhand, 1, 1, /* 0xab */ + "\tlodb", sbhand, 1, 1, /* 0xac */ + "\tlodw", sbhand, 1, 1, /* 0xad */ + "\tscab", sbhand, 1, 1, /* 0xae */ + "\tscaw", sbhand, 1, 1, /* 0xaf */ + "\tmov\tal,", mihand, 2, 2, /* 0xb0 */ + "\tmov\tcl,", mihand, 2, 2, /* 0xb1 */ + "\tmov\tdl,", mihand, 2, 2, /* 0xb2 */ + "\tmov\tbl,", mihand, 2, 2, /* 0xb3 */ + "\tmov\tah,", mihand, 2, 2, /* 0xb4 */ + "\tmov\tch,", mihand, 2, 2, /* 0xb5 */ + "\tmov\tdh,", mihand, 2, 2, /* 0xb6 */ + "\tmov\tbh,", mihand, 2, 2, /* 0xb7 */ + "\tmov\tax,", mihand, 3, 3, /* 0xb8 */ + "\tmov\tcx,", mihand, 3, 3, /* 0xb9 */ + "\tmov\tdx,", mihand, 3, 3, /* 0xba */ + "\tmov\tbx,", mihand, 3, 3, /* 0xbb */ + "\tmov\tsp,", mihand, 3, 3, /* 0xbc */ + "\tmov\tbp,", mihand, 3, 3, /* 0xbd */ + "\tmov\tsi,", mihand, 3, 3, /* 0xbe */ + "\tmov\tdi,", mihand, 3, 3, /* 0xbf */ + NULL, dfhand, 0, 0, /* 0xc0 */ + NULL, dfhand, 0, 0, /* 0xc1 */ + "\tret", rehand, 3, 3, /* 0xc2 */ + "\tret", sbhand, 1, 1, /* 0xc3 */ + "\tles", mvhand, 2, 4, /* 0xc4 */ + "\tlds", mvhand, 2, 4, /* 0xc5 */ + MOV, mmhand, 3, 5, /* 0xc6 */ + MOV, mmhand, 4, 6, /* 0xc7 */ + NULL, dfhand, 0, 0, /* 0xc8 */ + NULL, dfhand, 0, 0, /* 0xc9 */ + "\treti", rehand, 3, 3, /* 0xca */ + "\treti", sbhand, 1, 1, /* 0xcb */ + "\tint", sbhand, 1, 1, /* 0xcc */ + "\tint", inhand, 2, 2, /* 0xcd */ + "\tinto", sbhand, 1, 1, /* 0xce */ + "\tiret", sbhand, 1, 1, /* 0xcf */ + AMBIG, srhand, 2, 4, /* 0xd0 */ + AMBIG, srhand, 2, 4, /* 0xd1 */ + AMBIG, srhand, 2, 4, /* 0xd2 */ + AMBIG, srhand, 2, 4, /* 0xd3 */ + "\taam", aahand, 2, 2, /* 0xd4 */ + "\taad", aahand, 2, 2, /* 0xd5 */ + NULL, dfhand, 0, 0, /* 0xd6 */ + "\txlat", sbhand, 1, 1, /* 0xd7 */ + ESC, eshand, 2, 2, /* 0xd8 */ + ESC, eshand, 2, 2, /* 0xd9 */ + ESC, eshand, 2, 2, /* 0xda */ + ESC, eshand, 2, 2, /* 0xdb */ + ESC, eshand, 2, 2, /* 0xdc */ + ESC, eshand, 2, 2, /* 0xdd */ + ESC, eshand, 2, 2, /* 0xde */ + ESC, eshand, 2, 2, /* 0xdf */ + "\tloopne", sjhand, 2, 2, /* 0xe0 */ + "\tloope", sjhand, 2, 2, /* 0xe1 */ + "\tloop", sjhand, 2, 2, /* 0xe2 */ + "\tjcxz", sjhand, 2, 2, /* 0xe3 */ + "\tin", iohand, 2, 2, /* 0xe4 */ + "\tinw", iohand, 2, 2, /* 0xe5 */ + "\tout", iohand, 2, 2, /* 0xe6 */ + "\toutw", iohand, 2, 2, /* 0xe7 */ + "\tcall", ljhand, 3, 3, /* 0xe8 */ + "\tjmp", ljhand, 3, 3, /* 0xe9 */ + "\tjmpi", cihand, 5, 5, /* 0xea */ + "\tj", sjhand, 2, 2, /* 0xeb */ + "\tin", sbhand, 1, 1, /* 0xec */ + "\tinw", sbhand, 1, 1, /* 0xed */ + "\tout", sbhand, 1, 1, /* 0xee */ + "\toutw", sbhand, 1, 1, /* 0xef */ + "\tlock", sbhand, 1, 1, /* 0xf0 */ + NULL, dfhand, 0, 0, /* 0xf1 */ + "\trepnz", sbhand, 1, 1, /* 0xf2 */ + "\trepz", sbhand, 1, 1, /* 0xf3 */ + "\thlt", sbhand, 1, 1, /* 0xf4 */ + "\tcmc", sbhand, 1, 1, /* 0xf5 */ + AMBIG, mahand, 2, 5, /* 0xf6 */ + AMBIG, mahand, 2, 6, /* 0xf7 */ + "\tclc", sbhand, 1, 1, /* 0xf8 */ + "\tstc", sbhand, 1, 1, /* 0xf9 */ + "\tcli", sbhand, 1, 1, /* 0xfa */ + "\tsti", sbhand, 1, 1, /* 0xfb */ + "\tcld", sbhand, 1, 1, /* 0xfc */ + "\tstd", sbhand, 1, 1, /* 0xfd */ + AMBIG, mjhand, 2, 4, /* 0xfe */ + AMBIG, mjhand, 2, 4 /* 0xff */ + }; + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This simple routine returns the name field of a symbol * + * table entry as a printable string. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +char * +getnam(k) + + register int k; + +{/* * * * * * * * * * START OF getnam() * * * * * * * * * */ + + register int j; + static char a[9]; + + for (j = 0; j < 8; ++j) + if ( ! symtab[k].n_name[j] ) + break; + else + a[j] = symtab[k].n_name[j]; + + a[j] = '\0'; + + return (a); + +}/* * * * * * * * * * * END OF getnam() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This function is responsible for mucking through the * + * relocation table in search of externally referenced * + * symbols to be output as operands. It accepts two long * + * arguments: the code-segment location at which an extern * + * reference is expected, and the offset value which is * + * embedded in the object code and used at link time to * + * bias the external value. In the most typical case, the * + * function will be called by lookup(), which always makes * + * a check for external names before searching the symbol * + * table proper. However, it may also be called directly * + * by any function (such as the move-immediate handler) * + * which wants to make an independent check for externals. * + * The caller is expected to supply, as the third argument * + * to the function, a pointer to a character buffer large * + * enough to hold any possible output string. Lookext() * + * will fill this buffer and return a logical TRUE if it * + * finds an extern reference; otherwise, it will return a * + * logical FALSE, leaving the buffer undisturbed. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +int +lookext(off,loc,buf) + + long off, loc; + char *buf; + +{/* * * * * * * * * * START OF lookext() * * * * * * * * * */ + + register int k; + char c[16]; + + if ((loc != -1L) && (relptr >= 0)) + for (k = 0; k <= relptr; ++k) + if ((relo[k].r_vaddr == loc) + && (relo[k].r_symndx < S_BSS)) + { + strcpy(buf,getnam(relo[k].r_symndx)); + if (off) + { + if (off < 0) + sprintf(c,"%ld",off); + else + sprintf(c,"+%ld",off); + strcat(buf,c); + } + return (1); + } + + return (0); + +}/* * * * * * * * * * END OF lookext() * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This function finds an entry in the symbol table by * + * value. Its input is a (long) machine address, and its * + * output is a pointer to a string containing the corre- * + * sponding symbolic name. The function first searches the * + * relocation table for a possible external reference; if * + * none is found, a linear search of the symbol table is * + * undertaken. If no matching symbol has been found at the * + * end of these searches, the function returns a pointer * + * to a string containing the ASCII equivalent of the ad- * + * dress which was to be located, so that, regardless of * + * the success of the search, the function's return value * + * is suitable for use as a memory-reference operand. The * + * caller specifies the type of symbol to be found (text, * + * data, bss, undefined, absolute, or common) by means of * + * the function's second parameter. The third parameter * + * specifies the format to be used in the event of a nu- * + * meric output: zero for absolute format, one for short * + * relative format, two for long relative format. The * + * fourth parameter is the address which would appear in * + * the relocation table for the reference in question, or * + * -1 if the relocation table is not to be searched. The * + * function attempts to apply a certain amount of intelli- * + * gence in its selection of symbols, so it is possible * + * that, in the absence of a type match, a symbol of the * + * correct value but different type will be returned. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +char * +lookup(addr,type,kind,ext) + + long addr; /* Machine address to be located */ + + int type, /* Type of symbol to be matched */ + kind; /* Addressing output mode to use */ + + long ext; /* Value for extern ref, if any */ + +{/* * * * * * * * * * START OF lookup() * * * * * * * * * */ + + register int j, k; + static char b[64]; + + struct + { + int i; + int t; + } + best; + + if (lookext(addr,ext,b)) + return (b); + + if (segflg) + if (segflg & 1) + type = N_TEXT; + else + type = N_DATA; + + for (k = 0, best.i = -1; k <= symptr; ++k) + if (symtab[k].n_value == addr) + if ((j = symtab[k].n_sclass & N_SECT) == type) + { + best.t = j; + best.i = k; + break; + } + else if (segflg || (HDR.a_flags & A_SEP)) + continue; + else if (best.i < 0) + best.t = j, best.i = k; + else if (symrank[type][j] > symrank[type][best.t]) + best.t = j, best.i = k; + + if (best.i >= 0) + return (getnam(best.i)); + + if (kind == LOOK_ABS) + sprintf(b,"0x%05.5x",addr); + else + { + long x = addr - (PC - kind); + if (x < 0) + sprintf(b,".%ld",x); + else + sprintf(b,".+%ld",x); + } + + return (b); + +}/* * * * * * * * * * * END OF lookup() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This function translates an 8088 addressing mode byte * + * to an equivalent assembler string, returning a pointer * + * thereto. If necessary, it performs successive inputs * + * of bytes from the object file in order to obtain offset * + * data, adjusting PC accordingly. (The addressing mode * + * byte appears in several 8088 opcodes; it is used to * + * specify source and destination operand locations.) The * + * third argument to the function is zero if the standard * + * registers are to be used, or eight if the segment reg- * + * isters are to be used; these constants are defined sym- * + * bolically in dis.h. NOTE: The mtrans() function must * + * NEVER be called except immediately after fetching the * + * mode byte. If any additional object bytes are fetched * + * after the fetch of the mode byte, mtrans() will not * + * produce correct output! * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +char * +mtrans(c,m,type) + + register int c; /* Primary instruction byte */ + register int m; /* Addressing mode byte */ + + int type; /* Type code: standard or seg */ + +{/* * * * * * * * * * START OF mtrans() * * * * * * * * * */ + + unsigned long pc; + int offset, oflag, dir, w, mod, reg, rm; + static char a[100]; + static char b[30]; + + offset = 0; + dir = c & 2; + w = c & 1; + mod = (m & 0xc0) >> 6; + reg = (m & 0x38) >> 3; + rm = m & 7; + pc = PC + 1; + + if (type) + w = 1; + + if ((oflag = mod) > 2) + oflag = 0; + + if (oflag) + { + int j, k; + if (oflag == 2) + { + FETCH(j); + FETCH(k); + offset = (k << 8) | j; + } + else + { + FETCH(j); + if (j & 0x80) + k = 0xff00; + else + k = 0; + offset = k | j; + } + } + + if (dir) + { + strcpy(a,REGS[type + ((w << 3) | reg)]); + strcat(a,","); + switch (mod) + { + case 0 : + if (rm == 6) + { + int j, k; + FETCH(j); + FETCH(k); + offset = (k << 8) | j; + strcat(a, + lookup((long)(offset),N_DATA,LOOK_ABS,pc)); + } + else + { + sprintf(b,"(%s)",REGS0[rm]); + strcat(a,b); + } + break; + case 1 : + case 2 : + if (mod == 1) + strcat(a,"*"); + else + strcat(a,"#"); + sprintf(b,"%d(",offset); + strcat(a,b); + strcat(a,REGS1[rm]); + strcat(a,")"); + break; + case 3 : + strcat(a,REGS[(w << 3) | rm]); + break; + } + } + else + { + switch (mod) + { + case 0 : + if (rm == 6) + { + int j, k; + FETCH(j); + FETCH(k); + offset = (k << 8) | j; + strcpy(a, + lookup((long)(offset),N_DATA,LOOK_ABS,pc)); + } + else + { + sprintf(b,"(%s)",REGS0[rm]); + strcpy(a,b); + } + break; + case 1 : + case 2 : + if (mod == 1) + strcpy(a,"*"); + else + strcpy(a,"#"); + sprintf(b,"%d(",offset); + strcat(a,b); + strcat(a,REGS1[rm]); + strcat(a,")"); + break; + case 3 : + strcpy(a,REGS[(w << 3) | rm]); + break; + } + strcat(a,","); + strcat(a,REGS[type + ((w << 3) | reg)]); + } + + return (a); + +}/* * * * * * * * * * * END OF mtrans() * * * * * * * * * * */ + + /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * + * * + * This simple routine truncates a string returned by the * + * mtrans() function, removing its source operand. This is * + * useful in handlers which ignore the "reg" field of the * + * mode byte. * + * * + * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ + +void +mtrunc(a) + + register char *a; /* Ptr. to string to truncate */ + +{/* * * * * * * * * * START OF mtrunc() * * * * * * * * * */ + + register int k; + + for (k = strlen(a) - 1; k >= 0; --k) + if (a[k] == ',') + { + a[k] = '\0'; + break; + } + +}/* * * * * * * * * * * END OF mtrunc() * * * * * * * * * * */ + + diff --git a/commands/elle/Makefile b/commands/elle/Makefile new file mode 100755 index 000000000..fc87cd71b --- /dev/null +++ b/commands/elle/Makefile @@ -0,0 +1,56 @@ +# Makefile for elle + +CFLAGS = -O -DIGN_JOB_CONTROL -D_POSIX_SOURCE -wa +LDFLAGS= -i + +all: elle ellec + +OBJ = eemain.o eecmds.o eesite.o eevini.o eedisp.o eeterm.o eeerr.o \ + eeques.o eebuff.o eefile.o eefed.o eeedit.o eebit.o eef1.o \ + eef2.o eefd.o eehelp.o eekmac.o eef3.o eesrch.o eequer.o \ + eefill.o eediag.o sbstr.o sbm.o sberr.o sbbcpy.o + +# It probably isn't necessary to make all this stuff all the time, but it +# is fairly easy and makes the whole process simpler. If this is not done, +# the dependencies are very complicated because some of the .c and .h files +# are made dynamically. +elle: ellec $(OBJ) $(FUN_OFILES) elle.h eesite.h +# $(CC) $(CFLAGS) -c defprf.c # depends on the new *.h files + $(CC) $(LDFLAGS) -o $@ $(OBJ) + install -S 64k $@ + +defprf.c: deffun.e + cat deffun.e defprf.e | ellec -Pconf > defprf.c + +eefdef.h: deffun.e + cat deffun.e defprf.e | ellec -Fconf > eefdef.h + +eefidx.h: deffun.e + cat deffun.e defprf.e | ellec -FXconf > eefidx.h + +# Don't flush these files if interrupted, dammit! +.PRECIOUS: ellec deffun.e defprf.e + +# The following files must be recompiled if eefidx.h is changed +eecmds.o eebuff.o eeerr.o eehelp.o eejust.o eemain.o eeques.o eef1.o: eefidx.h + +# ELLE profile compiler. +# Although eefdef.h and defprf.c are included by ELLEC, they +# are not listed as dependencies in order to avoid loops (see +# their target entries). That is OK because their information is not +# used when generating the makecf files; it only furnishes default +# values needed when an ELLE user compiles a user profile. +ellec: ellec.c + $(CC) $(LDFLAGS) $(CFLAGS) -o $@ ellec.c + install -S 8kw $@ + +install: /usr/bin/elle /usr/bin/ellec + +/usr/bin/elle: elle + install -cs -o bin elle $@ + +/usr/bin/ellec: ellec + install -cs -o bin ellec $@ + +clean: + rm -f *.o *.bak core elle ellec diff --git a/commands/elle/deffun.e b/commands/elle/deffun.e new file mode 100755 index 000000000..e42381d30 --- /dev/null +++ b/commands/elle/deffun.e @@ -0,0 +1,201 @@ +;;; +;;; ELLE Master Function Definition file - "deffun.e" +;;; +;;; This file serves as input to the ellec program. It defines +;;; all ELLE functions which may serve as keyboard-bound user commands. +;;; +;;; Format: (efun ) +;;; Index - an unique index # (used only within ELLE) +;;; Name - an unique string identifying this function to the user. +;;; Routine - the C routine implementing the function within ELLE. +;;; Module - the name of the C source file that the routine is in. +;;; +;;; The following definitions are roughly organized by object. +;;; All functions that emulate EMACS functions are given names identical +;;; to the EMACS function names. For historical reasons these names +;;; are not as consistent as they could be (sigh). +;;; Those which have no exact counterpart in EMACS are identified by comments. + +(undefall) ; Ensure all predefined stuff is cleared out. + +; Simple Insertion +(efun 1 "Insert Self" f_insself eef1) +(efun 2 "Quoted Insert" f_quotins eef1) +(efun 3 "CRLF" f_crlf eef1) + +; Characters +(efun 4 "Forward Character" f_fchar eef1) +(efun 5 "Backward Character" f_bchar eef1) +(efun 6 "Delete Character" f_dchar eef1) +(efun 7 "Backward Delete Character" f_bdchar eef1) +(efun 8 "Delete Horizontal Space" f_delspc eef1) +(efun 9 "Transpose Characters" f_tchars eef1) + +; Words +(efun 10 "Forward Word" f_fword eef1) +(efun 11 "Backward Word" f_bword eef1) +(efun 12 "Kill Word" f_kword eef1) +(efun 13 "Backward Kill Word" f_bkword eef1) +(efun 14 "Transpose Words" f_twords eef1) +(efun 15 "Uppercase Word" f_ucword eef1) +(efun 16 "Lowercase Word" f_lcword eef1) +(efun 17 "Uppercase Initial" f_uciword eef1) + ; 18-19 reserved + +; Lines +(efun 20 "Beginning of Line" f_begline eef2) +(efun 21 "End of Line" f_endline eef2) +(efun 22 "Next Line" f_nxtline eef2) +(efun 23 "Previous Line" f_prvline eef2) +(efun 24 "Down Real Line" f_dnrline eef2) +(efun 25 "Up Real Line" f_uprline eef2) +(efun 26 "Open Line" f_oline eef2) +(efun 27 "Delete Blank Lines" f_delblines eef2) +(efun 28 "Kill Line" f_kline eef2) +(efun 29 "Backward Kill Line" f_bkline eef2) ; not EMACS +(efun 30 "Goto Line" f_goline eef2) ; not EMACS + ; 31-34 reserved + +; Regions +(efun 35 "Set/Pop Mark" f_setmark eef2) +(efun 36 "Exchange Point and Mark" f_exchmark eef2) +(efun 37 "Kill Region" f_kregion eef2) +(efun 38 "Copy Region" f_copreg eef2) +(efun 39 "Uppercase Region" f_ucreg eef2) +(efun 40 "Lowercase Region" f_lcreg eef2) +(efun 41 "Fill Region" f_fillreg eef2) + ; 42-44 reserved + +; Paragraphs +(efun 45 "Forward Paragraph" f_fpara eef2) +(efun 46 "Backward Paragraph" f_bpara eef2) +(efun 47 "Mark Paragraph" f_mrkpara eef2) +(efun 48 "Fill Paragraph" f_fillpara eef2) + ; 49 reserved + +; Buffers +(efun 50 "Select Buffer" f_selbuffer eebuff) +(efun 51 "Select Existing Buffer" f_selxbuffer eebuff) ; not EMACS +(efun 52 "Kill Buffer" f_kbuffer eebuff) +(efun 53 "List Buffers" f_listbufs eebuff) +(efun 54 "Buffer Not Modified" f_bufnotmod eebuff) +(efun 55 "EOL CRLF Mode" f_eolmode eebuff) ; ELLE +(efun 56 "Goto Beginning" f_gobeg eebuff) +(efun 57 "Goto End" f_goend eebuff) +(efun 58 "What Page" f_whatpage eebuff) + ; 59 reserved + +; Files +(efun 60 "Find File" f_ffile eefile) +(efun 61 "Read File" f_rfile eefile) +(efun 62 "Visit File" f_vfile eefile) +(efun 63 "Insert File" f_ifile eefile) +(efun 64 "Save File" f_sfile eefile) +(efun 65 "Save All Files" f_savefiles eebuff) +(efun 66 "Write File" f_wfile eefile) +(efun 67 "Write Region" f_wreg eefile) +(efun 68 "Write Last Kill" f_wlastkill eefile) ; not EMACS + ; 69 reserved + +; Windows +(efun 70 "Two Windows" f_2winds eebuff) +(efun 71 "One Window" f_1wind eebuff) +(efun 72 "Other Window" f_othwind eebuff) +(efun 73 "Grow Window" f_growind eebuff) +(efun 74 "Shrink Window" f_shrinkwind eebuff) ; not EMACS +(efun 75 "Delete Window" f_delwind eebuff) ; not EMACS +(efun 76 "Standout Window" f_sowind eebuff) ; ELLE +(efun 77 "Two Mode Windows" f_2modewinds eebuff) ; ELLE + +; Window Positioning +(efun 78 "New Window" f_newwin eefd) +(efun 79 "Next Screen" f_nscreen eefd) +(efun 80 "Previous Screen" f_pscreen eefd) +(efun 81 "Other New Screen" f_othnscreen eefd) ; not EMACS +(efun 82 "Line to Window Border" f_lwindbord eefd) ; not EMACS +(efun 83 "Scroll Window Up" f_scupwind eefd) ; not EMACS +(efun 84 "Scroll Window Down" f_scdnwind eefd) ; not EMACS +(efun 85 "Move to Window Top" f_mvwtop eefd) ; not EMACS +(efun 86 "Move to Window Bottom" f_mvwbot eefd) ; not EMACS + ; 87-89 reserved + +; Command Input +(efun 90 "Set Profile" f_setprof eecmds) ; ELLE +(efun 91 "Prefix Meta" f_pfxmeta eecmds) +(efun 92 "Prefix Extend" f_pfxext eecmds) +(efun 93 "Universal Arg" f_uarg eecmds) +(efun 94 "Negative Argument" f_negarg eecmds) +(efun 95 "Argument Digit" f_argdig eecmds) +(efun 96 "VT100 Button Hack" f_vtbuttons eecmds) ; not EMACS + +; Help +(efun 97 "Describe" f_describe eehelp) + ; 98-99 reserved + +; Keyboard Macros +(efun 100 "Start Kbd Macro" f_skmac eekmac) +(efun 101 "End Kbd Macro" f_ekmac eekmac) +(efun 102 "Execute Kbd Macro" f_xkmac eekmac) +(efun 103 "View Kbd Macro" f_vkmac eekmac) + ; 104 reserved + +; Killing +(efun 105 "Un-kill" f_unkill eef3) +(efun 106 "Un-kill Pop" f_unkpop eef3) +(efun 107 "Append Next Kill" f_appnkill eef3) + ; 108-109 reserved + +; Searching +(efun 110 "String Search" f_srch eesrch) +(efun 111 "Reverse String Search" f_rsrch eesrch) +(efun 112 "Incremental Search" f_isrch eesrch) +(efun 113 "Reverse Search" f_risrch eesrch) + +; Query Replace & friends +(efun 114 "Replace String" f_repstr eequer) +(efun 115 "Query Replace" f_querep eequer) +(efun 116 "Replace in Line" f_repline eequer) ; not EMACS + +; Fill Mode +(efun 117 "Set Fill Column" f_sfcol eefill) +(efun 118 "Set Fill Prefix" f_sfpref eefill) +(efun 119 "Auto Fill Mode" f_fillmode eefill) +(efun 120 "Text Mode" f_textmode eefill) ; IMAGEN + +; Indentation +(efun 121 "Indent According to Mode" f_indatm eef3) +(efun 122 "Indent New Line" f_indnl eef3) +(efun 123 "Back to Indentation" f_backind eef3) +(efun 124 "Indent for Comment" f_indcomm eef3) +(efun 125 "Indent Relative" f_indrel eef3) + ; 126-128 reserved + +; Miscellaneous +(efun 129 "Match Bracket" f_matchbrack eef3) ; not EMACS + +; Process Control +(efun 130 "Push to Inferior" f_pshinf eemain) +(efun 131 "Return to Superior" f_retsup eemain) +(efun 132 "Write File Exit" f_wfexit eemain) ; not EMACS + ; 133-139 reserved + +; ELLE Debugging +(efun 140 "Hit Breakpoint" f_bkpt eeerr) ; ELLE +(efun 141 "Debug Mode" f_debug eediag) ; ELLE + ; 142-149 reserved +;--------------------------------------------------------------- + +; IMAGEN configuration only +(efun 150 "Execute Unix Command" f_xucmd eemake) ; IMAGEN +(efun 151 "Execute Make" f_make eemake) ; IMAGEN +(efun 152 "Find Next Error" f_nxterr eemake) ; IMAGEN + +; ICONOGRAPHICS-specific +(efun 153 "ICO Extend Command" f_icoxcmd eefico) ; ICONOGRAPHICS +(efun 154 "ICO Typeset Funs" f_icotypfns eefico) ; ICONOGRAPHICS +(efun 155 "ICO Spec Input Funs" f_icospifns eefico) ; ICONOGRAPHICS + +; SUN Mouse functions +(efun 156 "Stuff Selection" f_stuffsel eesun) ; SUN +(efun 157 "Select Region" f_selregion eesun) ; SUN + diff --git a/commands/elle/defprf.c b/commands/elle/defprf.c new file mode 100755 index 000000000..928db35a3 --- /dev/null +++ b/commands/elle/defprf.c @@ -0,0 +1,231 @@ +/* This file defines the initial data for ELLE's default user profile. +** It is automatically generated by ELLEC, and should not be edited. +*/ +char charmap[] = { + 35, /* ( 0) ^@ Set/Pop Mark */ + 20, /* ( 1) ^A Beginning of Line */ + 5, /* ( 2) ^B Backward Character */ + 0, /* ( 3) ^C unknown function */ + 6, /* ( 4) ^D Delete Character */ + 21, /* ( 5) ^E End of Line */ + 4, /* ( 6) ^F Forward Character */ + 0, /* ( 7) ^G unknown function */ + 5, /* ( 10) ^H Backward Character */ + 121, /* ( 11) ^I Indent According to Mode */ + 122, /* ( 12) ^J Indent New Line */ + 28, /* ( 13) ^K Kill Line */ + 78, /* ( 14) ^L New Window */ + 3, /* ( 15) ^M CRLF */ + 24, /* ( 16) ^N Down Real Line */ + 26, /* ( 17) ^O Open Line */ + 25, /* ( 20) ^P Up Real Line */ + 2, /* ( 21) ^Q Quoted Insert */ + 113, /* ( 22) ^R Reverse Search */ + 112, /* ( 23) ^S Incremental Search */ + 9, /* ( 24) ^T Transpose Characters */ + 93, /* ( 25) ^U Universal Arg */ + 79, /* ( 26) ^V Next Screen */ + 37, /* ( 27) ^W Kill Region */ + 92, /* ( 30) ^X Prefix Extend */ + 105, /* ( 31) ^Y Un-kill */ + 0, /* ( 32) ^Z unknown function */ + 91, /* ( 33) ^[ Prefix Meta */ + 141, /* ( 34) ^\ Debug Mode */ + 0, /* ( 35) ^] unknown function */ + 140, /* ( 36) ^^ Hit Breakpoint */ + 97, /* ( 37) ^_ Describe */ + 1, /* ( 40) Insert Self */ + 1, /* ( 41) ! Insert Self */ + 1, /* ( 42) " Insert Self */ + 1, /* ( 43) # Insert Self */ + 1, /* ( 44) $ Insert Self */ + 1, /* ( 45) % Insert Self */ + 1, /* ( 46) & Insert Self */ + 1, /* ( 47) ' Insert Self */ + 1, /* ( 50) ( Insert Self */ + 1, /* ( 51) ) Insert Self */ + 1, /* ( 52) * Insert Self */ + 1, /* ( 53) + Insert Self */ + 1, /* ( 54) , Insert Self */ + 1, /* ( 55) - Insert Self */ + 1, /* ( 56) . Insert Self */ + 1, /* ( 57) / Insert Self */ + 1, /* ( 60) 0 Insert Self */ + 1, /* ( 61) 1 Insert Self */ + 1, /* ( 62) 2 Insert Self */ + 1, /* ( 63) 3 Insert Self */ + 1, /* ( 64) 4 Insert Self */ + 1, /* ( 65) 5 Insert Self */ + 1, /* ( 66) 6 Insert Self */ + 1, /* ( 67) 7 Insert Self */ + 1, /* ( 70) 8 Insert Self */ + 1, /* ( 71) 9 Insert Self */ + 1, /* ( 72) : Insert Self */ + 1, /* ( 73) ; Insert Self */ + 1, /* ( 74) < Insert Self */ + 1, /* ( 75) = Insert Self */ + 1, /* ( 76) > Insert Self */ + 1, /* ( 77) ? Insert Self */ + 1, /* (100) @ Insert Self */ + 1, /* (101) A Insert Self */ + 1, /* (102) B Insert Self */ + 1, /* (103) C Insert Self */ + 1, /* (104) D Insert Self */ + 1, /* (105) E Insert Self */ + 1, /* (106) F Insert Self */ + 1, /* (107) G Insert Self */ + 1, /* (110) H Insert Self */ + 1, /* (111) I Insert Self */ + 1, /* (112) J Insert Self */ + 1, /* (113) K Insert Self */ + 1, /* (114) L Insert Self */ + 1, /* (115) M Insert Self */ + 1, /* (116) N Insert Self */ + 1, /* (117) O Insert Self */ + 1, /* (120) P Insert Self */ + 1, /* (121) Q Insert Self */ + 1, /* (122) R Insert Self */ + 1, /* (123) S Insert Self */ + 1, /* (124) T Insert Self */ + 1, /* (125) U Insert Self */ + 1, /* (126) V Insert Self */ + 1, /* (127) W Insert Self */ + 1, /* (130) X Insert Self */ + 1, /* (131) Y Insert Self */ + 1, /* (132) Z Insert Self */ + 1, /* (133) [ Insert Self */ + 1, /* (134) \ Insert Self */ + 1, /* (135) ] Insert Self */ + 1, /* (136) ^ Insert Self */ + 1, /* (137) _ Insert Self */ + 1, /* (140) ` Insert Self */ + 1, /* (141) a Insert Self */ + 1, /* (142) b Insert Self */ + 1, /* (143) c Insert Self */ + 1, /* (144) d Insert Self */ + 1, /* (145) e Insert Self */ + 1, /* (146) f Insert Self */ + 1, /* (147) g Insert Self */ + 1, /* (150) h Insert Self */ + 1, /* (151) i Insert Self */ + 1, /* (152) j Insert Self */ + 1, /* (153) k Insert Self */ + 1, /* (154) l Insert Self */ + 1, /* (155) m Insert Self */ + 1, /* (156) n Insert Self */ + 1, /* (157) o Insert Self */ + 1, /* (160) p Insert Self */ + 1, /* (161) q Insert Self */ + 1, /* (162) r Insert Self */ + 1, /* (163) s Insert Self */ + 1, /* (164) t Insert Self */ + 1, /* (165) u Insert Self */ + 1, /* (166) v Insert Self */ + 1, /* (167) w Insert Self */ + 1, /* (170) x Insert Self */ + 1, /* (171) y Insert Self */ + 1, /* (172) z Insert Self */ + 1, /* (173) { Insert Self */ + 1, /* (174) | Insert Self */ + 1, /* (175) } Insert Self */ + 1, /* (176) ~ Insert Self */ + 7, /* (177) DEL Backward Delete Character */ +}; + char metamap[] = { + 02 , 86, /* M-^B Move to Window Bottom */ + 014 , 30, /* M-^L Goto Line */ + 016 , 84, /* M-^N Scroll Window Down */ + 020 , 83, /* M-^P Scroll Window Up */ + 022 ,111, /* M-^R Reverse String Search */ + 023 ,110, /* M-^S String Search */ + 024 , 85, /* M-^T Move to Window Top */ + 027 ,107, /* M-^W Append Next Kill */ + 030 , 51, /* M-^X Select Existing Buffer */ + 036 , 74, /* M-^^ Shrink Window */ + 045 ,115, /* M-% Query Replace */ + 055 , 94, /* M-- Negative Argument */ + 060 , 95, /* M-0 Argument Digit */ + 061 , 95, /* M-1 Argument Digit */ + 062 , 95, /* M-2 Argument Digit */ + 063 , 95, /* M-3 Argument Digit */ + 064 , 95, /* M-4 Argument Digit */ + 065 , 95, /* M-5 Argument Digit */ + 066 , 95, /* M-6 Argument Digit */ + 067 , 95, /* M-7 Argument Digit */ + 070 , 95, /* M-8 Argument Digit */ + 071 , 95, /* M-9 Argument Digit */ + 073 ,124, /* M-; Indent for Comment */ + 074 , 56, /* M-< Goto Beginning */ + 076 , 57, /* M-> Goto End */ + 0133, 46, /* M-[ Backward Paragraph */ + 0134, 8, /* M-\ Delete Horizontal Space */ + 0135, 45, /* M-] Forward Paragraph */ + 0102, 11, /* M-B Backward Word */ + 0103, 17, /* M-C Uppercase Initial */ + 0104, 12, /* M-D Kill Word */ + 0106, 10, /* M-F Forward Word */ + 0107, 41, /* M-G Fill Region */ + 0110, 47, /* M-H Mark Paragraph */ + 0111,125, /* M-I Indent Relative */ + 0114, 16, /* M-L Lowercase Word */ + 0115,123, /* M-M Back to Indentation */ + 0116, 22, /* M-N Next Line */ + 0117, 96, /* M-O VT100 Button Hack */ + 0120, 23, /* M-P Previous Line */ + 0121, 48, /* M-Q Fill Paragraph */ + 0124, 14, /* M-T Transpose Words */ + 0125, 15, /* M-U Uppercase Word */ + 0126, 80, /* M-V Previous Screen */ + 0127, 38, /* M-W Copy Region */ + 0131,106, /* M-Y Un-kill Pop */ + 0176, 54, /* M-~ Buffer Not Modified */ + 0177, 13, /* M-DEL Backward Kill Word */ +}; + char extmap[] = { + 02 , 53, /* X-^B List Buffers */ + 03 ,132, /* X-^C Write File Exit */ + 05 , 67, /* X-^E Write Region */ + 06 , 60, /* X-^F Find File */ + 013 , 68, /* X-^K Write Last Kill */ + 014 , 40, /* X-^L Lowercase Region */ + 015 , 55, /* X-^M EOL CRLF Mode */ + 017 , 27, /* X-^O Delete Blank Lines */ + 020 , 90, /* X-^P Set Profile */ + 022 , 61, /* X-^R Read File */ + 023 , 64, /* X-^S Save File */ + 025 , 39, /* X-^U Uppercase Region */ + 026 , 62, /* X-^V Visit File */ + 027 , 66, /* X-^W Write File */ + 030 , 36, /* X-^X Exchange Point and Mark */ + 032 ,131, /* X-^Z Return to Superior */ + 041 ,130, /* X-! Push to Inferior */ + 044 ,116, /* X-$ Replace in Line */ + 045 ,114, /* X-% Replace String */ + 050 ,100, /* X-( Start Kbd Macro */ + 051 ,101, /* X-) End Kbd Macro */ + 052 ,103, /* X-* View Kbd Macro */ + 056 ,118, /* X-. Set Fill Prefix */ + 060 , 75, /* X-0 Delete Window */ + 061 , 71, /* X-1 One Window */ + 062 , 70, /* X-2 Two Windows */ + 070 , 76, /* X-8 Standout Window */ + 071 , 77, /* X-9 Two Mode Windows */ + 075 , 58, /* X-= What Page */ + 0136, 73, /* X-^ Grow Window */ + 0102, 50, /* X-B Select Buffer */ + 0105,102, /* X-E Execute Kbd Macro */ + 0106,117, /* X-F Set Fill Column */ + 0111, 63, /* X-I Insert File */ + 0113, 52, /* X-K Kill Buffer */ + 0117, 72, /* X-O Other Window */ + 0123, 65, /* X-S Save All Files */ + 0124,119, /* X-T Auto Fill Mode */ + 0177, 29, /* X-DEL Backward Kill Line */ +}; +struct profile def_prof = { + 1, /* Ver */ + sizeof(charmap), charmap, + sizeof(metamap)/2, metamap, + sizeof(extmap)/2, extmap, + 0, 0 +}; diff --git a/commands/elle/defprf.e b/commands/elle/defprf.e new file mode 100755 index 000000000..6bbecf60f --- /dev/null +++ b/commands/elle/defprf.e @@ -0,0 +1,258 @@ +;;; +;;; ELLE Default Command Profile - "defprf.e" +;;; +;;; This file is input to the ellec program. It defines the default +;;; command key bindings that ELLE uses, in the absence of an individual +;;; user profile. +;;; These defaults attempt to emulate the default EMACS command key +;;; bindings. Differences, where known, are commented. +;;; +;;; "ELLE" means the function is unique to ELLE. +;;; E/G: (cmd altnam) "thisname"; +;;; "E:" refers to TOPS-20 EMACS, "G:" refers to Gnu Emacs. +;;; (cmd) This function exists but is bound to "cmd" instead. +;;; (*) function exists but is not bound to any specific key. +;;; () function does not exist. +;;; (=) function exists, with same binding (normally omitted) +;;; altnam Name by which this function is known. +;;; "thisname" - name of function bound to this command. +;;; - means the command is unbound (undefined). + +(keyallunbind) ; Flush any predefined bindings + +(keybind ^@ "Set/Pop Mark") +(keybind ^A "Beginning of Line") +(keybind ^B "Backward Character") +; ^C not bound. ; E: ()- G: mode-specific-command-prefix +(keybind ^D "Delete Character") +(keybind ^E "End of Line") +(keybind ^F "Forward Character") +(keybind ^H "Backward Character") ; G: (^B) help-command +(keybind ^I "Indent According to Mode") +(keybind ^J "Indent New Line") +(keybind ^K "Kill Line") +(keybind ^L "New Window") +(keybind ^M "CRLF") +(keybind ^N "Down Real Line") +(keybind ^O "Open Line") +(keybind ^P "Up Real Line") +(keybind ^Q "Quoted Insert") +(keybind ^R "Reverse Search") +(keybind ^S "Incremental Search") +(keybind ^T "Transpose Characters") +(keybind ^U "Universal Arg") +(keybind ^V "Next Screen") +(keybind ^W "Kill Region") +(keybind ^X "Prefix Extend") +(keybind ^Y "Un-kill") +; ^Z not bound ; E: Prefix Control-Meta; G: suspend-emacs +(keybind ^[ "Prefix Meta") +(keybind "^\" "Debug Mode") ; ELLE. E: () Prefix Meta; G: () - +; ^] not bound. ; E+G: Abort Recursive Edit +(keybind ^^ "Hit Breakpoint") ; ELLE. E: () Prefix Control; G: () - +(keybind ^_ "Describe") ; E: (M-?) Help; G: (^H-k) undo +(keybind " " "Insert Self") +(keybind ! "Insert Self") +(keybind """" "Insert Self") +(keybind # "Insert Self") +(keybind $ "Insert Self") +(keybind % "Insert Self") +(keybind & "Insert Self") +(keybind ' "Insert Self") +(keybind "(" "Insert Self") +(keybind ")" "Insert Self") +(keybind * "Insert Self") +(keybind + "Insert Self") +(keybind , "Insert Self") +(keybind - "Insert Self") +(keybind . "Insert Self") +(keybind / "Insert Self") +(keybind 0 "Insert Self") +(keybind 1 "Insert Self") +(keybind 2 "Insert Self") +(keybind 3 "Insert Self") +(keybind 4 "Insert Self") +(keybind 5 "Insert Self") +(keybind 6 "Insert Self") +(keybind 7 "Insert Self") +(keybind 8 "Insert Self") +(keybind 9 "Insert Self") +(keybind : "Insert Self") +(keybind ";" "Insert Self") +(keybind < "Insert Self") +(keybind = "Insert Self") +(keybind > "Insert Self") +(keybind ? "Insert Self") +(keybind @ "Insert Self") +(keybind A "Insert Self") +(keybind B "Insert Self") +(keybind C "Insert Self") +(keybind D "Insert Self") +(keybind E "Insert Self") +(keybind F "Insert Self") +(keybind G "Insert Self") +(keybind H "Insert Self") +(keybind I "Insert Self") +(keybind J "Insert Self") +(keybind K "Insert Self") +(keybind L "Insert Self") +(keybind M "Insert Self") +(keybind N "Insert Self") +(keybind O "Insert Self") +(keybind P "Insert Self") +(keybind Q "Insert Self") +(keybind R "Insert Self") +(keybind S "Insert Self") +(keybind T "Insert Self") +(keybind U "Insert Self") +(keybind V "Insert Self") +(keybind W "Insert Self") +(keybind X "Insert Self") +(keybind Y "Insert Self") +(keybind Z "Insert Self") +(keybind [ "Insert Self") +(keybind "\" "Insert Self") +(keybind ] "Insert Self") +(keybind ^ "Insert Self") +(keybind _ "Insert Self") +(keybind ` "Insert Self") +(keybind a "Insert Self") +(keybind b "Insert Self") +(keybind c "Insert Self") +(keybind d "Insert Self") +(keybind e "Insert Self") +(keybind f "Insert Self") +(keybind g "Insert Self") +(keybind h "Insert Self") +(keybind i "Insert Self") +(keybind j "Insert Self") +(keybind k "Insert Self") +(keybind l "Insert Self") +(keybind m "Insert Self") +(keybind n "Insert Self") +(keybind o "Insert Self") +(keybind p "Insert Self") +(keybind q "Insert Self") +(keybind r "Insert Self") +(keybind s "Insert Self") +(keybind t "Insert Self") +(keybind u "Insert Self") +(keybind v "Insert Self") +(keybind w "Insert Self") +(keybind x "Insert Self") +(keybind y "Insert Self") +(keybind z "Insert Self") +(keybind { "Insert Self") +(keybind | "Insert Self") +(keybind } "Insert Self") +(keybind ~ "Insert Self") +(keybind DEL "Backward Delete Character") + +; Meta chars + +(keybind M-^B "Move to Window Bottom") ; ELLE (ima). E+G:()- +(keybind M-^L "Goto Line") ; E:(); G:(* goto-line) - +(keybind M-^N "Scroll Window Down") ; ELLE (ima). E+G:()- forward-list +(keybind M-^P "Scroll Window Up") ; ELLE (ima). E+G:()- backward-list +(keybind M-^R "Reverse String Search") ; E:(*); G:(* search-backward) - +(keybind M-^S "String Search") ; E:(*); G:(* search-forward) isearch-forward-regexp +(keybind M-^T "Move to Window Top") ; ELLE (ima). E+G:()- +(keybind M-^W "Append Next Kill") +(keybind M-^X "Select Existing Buffer") ; ELLE (ima). E+G:()- +(keybind M-^^ "Shrink Window") ; ELLE (ima). E+G:()- +(keybind M-% "Query Replace") +(keybind M-- "Negative Argument") +(keybind M-0 "Argument Digit") +(keybind M-1 "Argument Digit") +(keybind M-2 "Argument Digit") +(keybind M-3 "Argument Digit") +(keybind M-4 "Argument Digit") +(keybind M-5 "Argument Digit") +(keybind M-6 "Argument Digit") +(keybind M-7 "Argument Digit") +(keybind M-8 "Argument Digit") +(keybind M-9 "Argument Digit") +(keybind "M-;" "Indent for Comment") +(keybind M-< "Goto Beginning") +(keybind M-> "Goto End") +(keybind M-[ "Backward Paragraph") +(keybind "M-\" "Delete Horizontal Space") +(keybind M-] "Forward Paragraph") +(keybind M-B "Backward Word") +(keybind M-C "Uppercase Initial") +(keybind M-D "Kill Word") +(keybind M-F "Forward Word") +(keybind M-G "Fill Region") +(keybind M-H "Mark Paragraph") +(keybind M-I "Indent Relative") ; E+G: (*) Tab to Tab Stop +(keybind M-L "Lowercase Word") +(keybind M-M "Back to Indentation") +(keybind M-N "Next Line") ; E:(*); G:(* forward-line) - +(keybind M-O "VT100 button hack") ; ELLE. E+G: () - +(keybind M-P "Previous Line") ; E:(*); G:() - +(keybind M-Q "Fill Paragraph") +(keybind M-T "Transpose Words") +(keybind M-U "Uppercase Word") +(keybind M-V "Previous Screen") +(keybind M-W "Copy Region") +(keybind M-Y "Un-kill Pop") +(keybind M-~ "Buffer Not Modified") +(keybind M-DEL "Backward Kill Word") + +; Extended commands + +(keybind X-^B "List Buffers") +(keybind X-^C "Write File Exit") ; ELLE (ima). E:()-; G: (= save-buffers-kill-emacs) +(keybind X-^E "Write Region") ; E:(*)-; G:(*) eval-last-sexp +(keybind X-^F "Find File") +(keybind X-^K "Write Last Kill") ; ELLE (mnx). E+G:()- +(keybind X-^L "Lowercase Region") +(keybind X-^M "EOL CRLF Mode") ; ELLE. E+G: ()- +(keybind X-^O "Delete Blank Lines") +(keybind X-^P "Set Profile") ; ELLE. E+G: () Mark Page +(keybind X-^R "Read File") +(keybind X-^S "Save File") +(keybind X-^U "Uppercase Region") +(keybind X-^V "Visit File") +(keybind X-^W "Write File") +(keybind X-^X "Exchange Point and Mark") +(keybind X-^Z "Return to Superior") ; G:() suspend-emacs +(keybind X-! "Push to Inferior") ; ELLE. E:(*)-; G:()- +(keybind X-$ "Replace in Line") ; ELLE (mnx). E+G:()- +(keybind X-% "Replace String") ; E+G: (*) - +(keybind "X-(" "Start Kbd Macro") +(keybind "X-)" "End Kbd Macro") +(keybind X-* "View Kbd Macro") ; E: (*)-; G: ()- +(keybind X-. "Set Fill Prefix") +(keybind X-0 "Delete Window") ; E: ()- +(keybind X-1 "One Window") +(keybind X-2 "Two Windows") +(keybind X-8 "Standout Window") ; ELLE. E+G:()- +(keybind X-9 "Two Mode Windows") ; ELLE. E+G:()- +(keybind X-= "What Page") ; E+G: (*) What Cursor Position +(keybind X-^ "Grow Window") +(keybind X-B "Select Buffer") +(keybind X-E "Execute Kbd Macro") +(keybind X-F "Set Fill Column") +(keybind X-I "Insert File") ; E: (*) Info +(keybind X-K "Kill Buffer") +(keybind X-O "Other Window") +(keybind X-S "Save All Files") ; E:(*)-; G:(= save-some-buffers) +(keybind X-T "Auto Fill Mode") ; E:(*) Transpose Regions; G:(*)- +(keybind X-DEL "Backward Kill Line") ; ELLE(ico) E+G:() Backward Kill Sentence + +; IMAGEN-specific functions, not bound. +;(keybind "" "Text Mode") ; IMAGEN E:(*); G:(*) +;(keybind "" "Execute Unix Command") ; IMAGEN E:(); G:(M-! shell-command) +;(keybind "" "Execute Make") ; IMAGEN E:(* Compile); G:(* compile) +;(keybind "" "Find Next Error") ; IMAGEN E:(); G:(X-` next-error) + +; SUN Mouse functions, for menuitem selection. +;(menuitem "Stuff Selection") ; SUN +;(menuitem "Select Region") ; SUN + +; Forget completely about these. +;(keybind "" "ICO Extend Command") ; ICONOGRAPHICS +;(keybind "" "ICO Typeset Funs") ; ICONOGRAPHICS +;(keybind "" "ICO Spec Input Funs") ; ICONOGRAPHICS + diff --git a/commands/elle/eebit.c b/commands/elle/eebit.c new file mode 100755 index 000000000..0a187daab --- /dev/null +++ b/commands/elle/eebit.c @@ -0,0 +1,47 @@ +/* ELLE - Copyright 1985, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* + * EEBIT Bit Array functions + */ +#include "sb.h" + +/* Char-bit array functions. All assume that there are at least 8 bits + * in a byte, and that the number of bytes per word is a power of 2. + */ +/* CHBBITS represents log 2 of the # of bits stored per chbit-array word. + * WDBITS already has log2 of the # of bytes per word, and we are + * assuming each byte has at least 8 bits, so log2(8) = 3. + */ +#define CHBSIZE (WDSIZE*8) /* # bits per word */ +#define CHBBITS (WDBITS+3) /* log2(CHBSIZE) */ +#define CHBMASK (CHBSIZE-1) +#define CHBARYSIZ (128/CHBSIZE) /* # words per ASCII array */ + +/* CHBALLOC(size) - Allocates a char-bit array */ +int * +chballoc(size) +int size; +{ return((int *)calloc((size + CHBSIZE-1)/CHBSIZE, (sizeof(int)))); +} + +/* CHBIT(array, char) - Tests bit in char-bit array + */ +chbit(array,c) +register int *array, c; +{ return(array[c >> CHBBITS] & (1 << (c & CHBMASK))); +} +/* CHBIS (array, char) - Sets bit in char-bit array + */ +chbis(array,c) +register int *array, c; +{ array[c >> CHBBITS] |= (1 << (c & CHBMASK)); +} +/* CHBIC (array, char) - Clears bit in char-bit array + */ +chbic(array,c) +register int *array, c; +{ array[c >> CHBBITS] &= ~(1 << (c & CHBMASK)); +} diff --git a/commands/elle/eebuff.c b/commands/elle/eebuff.c new file mode 100755 index 000000000..7eeae5f31 --- /dev/null +++ b/commands/elle/eebuff.c @@ -0,0 +1,1072 @@ +/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ + +/* EEBUFF Buffer and Window functions. + * Each buffer is an independent SB-string described by a + * buffer structure. All buffer structures are allocated dynamically + * and chained together starting from buf_head. + */ + +#include "elle.h" + +#if FX_FILLMODE +extern int fill_mode; +#endif +#if FX_SKMAC +extern int kdef_mode; +#endif + +struct buffer *make_buf(), *find_buf(), *sel_mbuf(), *sel_nbuf(); +struct window *make_win(); + +/* EFUN: "Select Buffer" */ +/* Select old buffer or create a new one. Defaults to previously + * used buffer. + */ +f_selbuffer() +{ register char *ans; + register struct buffer *b; + + if((b = last_buf) == cur_buf) /* If default same as current, */ + if(!(b = sel_mbuf(b))) /* try to pick a more useful one. */ + b = sel_nbuf(cur_buf); + + ans = ask("Select buffer (%s): ",b->b_name); + if (ans == 0) /* he aborted */ + return; + if (*ans != '\0') /* Null string => use last buff */ + { b = find_buf (ans); /* Else find/create one */ + if (b == 0) + b = make_buf (ans); + } + sel_buf(b); + chkfree(ans); +} + +#if FX_SELXBUFFER +/* EFUN: "Select Existing Buffer" (not EMACS) - from IMAGEN config */ + +static int findstr(); + +f_selxbuffer() +{ register char *ans; + register struct buffer *b; + + b = last_buf; /* This is default */ + ans = ask("Select existing buffer (%s): ", b->b_name); + if (ans == 0) /* Aborted */ + return; + if (*ans != 0) + { for (b = buf_head; b != 0; b = b->b_next) + if (findstr(ans, b->b_name)) + break; + if (b == 0) + ding("That isn't a substring of any buffer name!"); + } + chkfree(ans); + if (b != 0) + { saytoo(" => "); + sayntoo(b->b_name); + sel_buf(b); + } +} + +static int +findstr(str, instr) /* Find "str" in string "instr" */ +register char *str, *instr; +{ register char *sp, *isp; + + while (*instr) + { sp = str; + isp = instr; + while (*sp) + if (*sp++ != *isp++) + goto next; + return(1); +next: ++instr; + } + return(0); +} +#endif /*FX_SELXBUFFER*/ + + +/* EFUN: "Kill Buffer" */ +/* Kill specified buffer - defaults to current buffer. + * This code assumes a killed buffer will never be on a window list unless it + * is pointed to by cur_buf or oth_win->w_buf!!!! + */ +f_kbuffer() +{ register struct buffer *b, *ob; + register char *ans; + + if((ans = ask("Kill buffer: ")) == 0) + return; + if(*ans == 0) b = cur_buf; + else if(*ans == SP) b = 0; + else b = find_buf(ans); + + chkfree(ans); + if(!b) + { ding("No such buffer"); + return; + } +#if IMAGEN + if (b->b_flags & B_PERMANENT) + { ding("Permanent buffer--cannot kill!"); + return; + } + if (b->b_flags & B_MODIFIED) + { if ((ans == ask("Buffer is modified; are you sure? ")) == 0) + return; + if(upcase(*ans) != 'Y') + { chkfree(ans); + return; + } + chkfree(ans); + } +#endif /*IMAGEN*/ + if(b == cur_buf || (oth_win && (oth_win->w_buf == b))) + { ob = last_buf; + do + { + /* If default same as doomed buffer, try to pick + * a more useful alternative. */ + if((b == ob) && !(ob = sel_mbuf(b))) + ob = sel_nbuf(b); + + ans = ask("Killing in-use buffer; select which other buffer (%s): ", + ob->b_name); + if(ans == 0) return; + if(*ans) + { if(*ans == SP) ob = 0; + else ob = find_buf(ans); + } + chkfree(ans); + if(!ob) + { ding("No such buffer"); + return; + } + } while (b == ob); + + /* B is buffer to kill, OB is buffer to replace it with */ + if(oth_win && (oth_win->w_buf == b)) + { f_othwind(); /* Select other one */ + chg_buf(ob); /* Change to new buffer */ + f_othwind(); + } + if(cur_buf == b) + chg_buf(ob); + } + + kill_buf(b); /* Die!!!! */ + if(last_buf == b) + last_buf = cur_buf; +} + +/* EFUN: "List Buffers" */ +/* Display a list of all user buffers. Internal buffers, whose names + * start with a space, are not shown. + */ +f_listbufs() +{ + register struct buffer *b; + register char *cp; + register int i; + struct buffer *tbuf, *savbuf; + char temp[20]; + + /* First must set up special buffer... */ + savbuf = cur_buf; + chg_buf(tbuf = make_buf(" **SHOW**")); + e_sputz("Buffers in this ELLE:\n\n"); + for(b = buf_head; b; b = b->b_next) + { cp = b->b_name; + if(*cp == SP) continue; /* Ignore internal buffs */ + e_sputz((b->b_flags&B_MODIFIED) ? "* " : " "); + e_sputz(cp); /* Insert buffer name */ + dottoa(temp,ex_blen(b)); /* Get buff-length string */ + if((i = ((FNAMELEN > 14) ? 30 : 20) + - strlen(cp) - strlen(temp)) > 0) + e_insn(SP, i); + e_sputz(" ("); + e_sputz(temp); + e_sputz(") "); + if(cp = b->b_fn) + e_sputz(cp); +#if IMAGEN + if (b->b_flags & B_CMODE) + e_sputz(" (C)"); + else if (b->b_flags & B_TEXTMODE) + e_sputz(" (Text)"); + else + e_sputz(" (Fundamental)"); +#endif /*IMAGEN*/ + e_putc(LF); + } + mk_showin(tbuf); /* Show this buffer in temp window */ + chg_buf(savbuf); /* Return to real current buffer */ + kill_buf(tbuf); +} + +/* EFUN: "Buffer Not Modified" */ +/* Mark the current buffer as not modified. + */ +f_bufnotmod() +{ + cur_buf -> b_flags &= ~B_MODIFIED; + redp(RD_MODE); +} + +#if FX_EOLMODE +/* EFUN: "EOL CRLF Mode" (not EMACS) */ +/* Toggle the EOL mode of the current buffer. +** LF EOL Mode means LF alone is an EOL. +** CRLF EOL Mode means CRLF together is an EOL. +*/ +f_eolmode() +{ + cur_buf->b_flags ^= B_EOLCRLF; /* Flip this bit */ + say((cur_buf->b_flags & B_EOLCRLF) + ? "EOL Mode is CRLF" /* If now on */ + : "EOL Mode is LF"); /* If now off */ + + redp(RD_WINRES); /* Redo window for this buf */ +} +#endif /*FX_EOLMODE*/ + +#if FX_GOBEG +/* EFUN: "Goto Beginning" */ +f_gobeg() +{ e_gobob(); + ed_setcur(); +} +#endif /*FX_GOBEG*/ + +#if FX_GOEND +/* EFUN: "Goto End" */ +f_goend() +{ e_goeob(); + ed_setcur(); +} +#endif /*FX_GOEND*/ + +#if FX_WHATPAGE +/* EFUN: "What Page" */ +/* Extra info added as per earlier ICONOGRAPHICS "What Buffer Position" +** Reports on current position as follows: +** Dot=, Page Line (line of ) +*/ +f_whatpage() +{ + register chroff cnt; + register int c; + register int page, line; + int lineatp; + char tempstr[12], *dottoa (); + + saynow("Dot="); + dottoa(tempstr, cur_dot); + sayntoo(tempstr); + + e_gobob(); + page = line = lineatp = 1; + for (cnt = cur_dot; --cnt >= 0;) + if ((c = e_getc()) == LF) + ++line; + else if (c == FF) + { ++page; + lineatp = line; + } + + saytoo(", Page "); + dottoa(tempstr, (chroff)page); + saytoo(tempstr); + saytoo(" Line "); + dottoa(tempstr, (chroff)(1 + line - lineatp)); + saytoo(tempstr); + saytoo(" Col "); + dottoa(tempstr, (chroff)indtion(cur_dot)); + saytoo(tempstr); + saytoo(" [line "); + dottoa(tempstr, (chroff)line); + saytoo(tempstr); + sayntoo(" of "); /* Force out while scan rest */ + + for(e_gocur(); e_gonl() ; ++line) ; /* Count lines until EOF */ + c = e_rgetc(); /* Remember what last char is */ + dottoa(tempstr, (chroff)line); + saytoo(tempstr); + if (c != LF) /* Check last char */ + saytoo(" (no EOL at EOF!)"); + sayntoo("]"); + e_gocur(); /* Back to original position */ +} +#endif /*FX_WHATPAGE*/ + +init_buf () /* init buffer stuff */ +{ + buf_head = 0; + lines_buf = cur_buf = make_buf(" **LINES**"); /* For sep_win */ + e_insn('-',scr_wid-2); /* Fill with dashes */ + last_buf = cur_buf = make_buf ("Main"); /* Make Main buffer */ + init_win(); /* Now can init windows */ +} + +struct buffer * +make_buf(bname) /* create buffer "bname" if it doesn't exist */ +char *bname; +{ register struct buffer *b; + register char *name; + + b = find_buf(name = bname); + if (b) /* if it exists already */ + return(b); + b = (struct buffer *) memalloc(sizeof (struct buffer)); + b -> b_next = buf_head; /* link it in */ + buf_head = b; + b->b_name = strdup(name); /* Allocate copy of name string */ + b->b_dot = 0; /* Set dot to beg */ + sb_open(b,(SBSTR *)0); /* Open buffer with null initial sbstring */ + b->b_fn = 0; + b->b_flags = 0; + b->b_mode = cur_mode; /* Inherit current mode */ + return(b); +} + + +struct buffer * +find_buf(name) /* returns pointer to buffer of that name or 0 */ +char *name; +{ register struct buffer *b = buf_head; + + while (b && strcmp(b->b_name, name)) + b = b -> b_next; + return(b); +} + +sel_buf(b) /* select buffer, saving last */ +struct buffer *b; +{ + if(b != cur_buf) last_buf = cur_buf; + chg_buf(b); +} + +chg_buf (newbuf) /* change current buffer to newbuf */ +struct buffer *newbuf; +{ register struct buffer *obuf, *nbuf; + + if ((nbuf = newbuf) == (obuf = cur_buf)) + return; /* Do nothing if same buffers */ + obuf->b_dot = cur_dot; + cur_buf = nbuf; + cur_mode = nbuf->b_mode; + e_gosetcur(nbuf->b_dot); /* Set cur_dot and go there */ + cur_win->w_buf = nbuf; + cur_win->w_dot = cur_dot; +#if IMAGEN + cur_win->w_redp = RD_WINRES|RD_REDO; +#else + cur_win->w_redp = RD_WINRES; /* Reset flags - do complete update */ +#endif /*-IMAGEN*/ + unlk_buf(obuf); /* Unlock previous buffer if can */ + mark_p = 0; /* this is lazy */ + redp(RD_MODE|RD_WINRES); +} + +/* See if specified buffer belongs to any active window, and + * if not then get it into an idle, unlocked state; this helps the + * edit package compact and swap stuff out while it's not being used. + * Assumes proper state of dot has been stored into b_dot. + */ +unlk_buf(bufp) +struct buffer *bufp; +{ register struct buffer *b; + register struct window *w; + + b = bufp; + for(w = win_head; w; w = w->w_next) + if(b == w->w_buf) + return; /* Buffer is actively being shown */ + sb_rewind((SBBUF *)b); /* Return to idle state */ +} + +/* SEL_NBUF(buf) - Select next user buffer. Ignores internal buffers. + * Arg of 0 starts at beg of buffer list. Always returns + * a buffer pointer - returns argument (which may be 0) + * if found no other user buffers. + * + * SEL_MBUF(buf) - Select next modified buffer. + * Returns buffer ptr to "next" modified buffer, if any. + * Arg of 0 starts at beg of buffer list and scans all of them. + * Returns 0 if no other modified buffers exist (unlike SEL_NBUF!) + * Ignores internal buffers, whose names start with a space. + */ +/* struct buffer *buf_mptr; */ +#if 0 +struct buffer * +sel_mbuf(buf) +struct buffer *buf; +{ register struct buffer *b; + register int sweep; + + sweep = 0; /* Make 2 sweeps only */ + if(b = buf) b = b->b_next; + do { + if(b == 0) /* Initialize if needed */ + b = buf_head; + for(; b; b = b->b_next) + if((b->b_flags & B_MODIFIED) && (*b->b_name != SP)) + return((b == buf) ? 0 : b); + } while(sweep++ != 0); + return(0); +} +#endif /*COMMENT*/ + +struct buffer * +sel_mbuf(buf) +register struct buffer *buf; +{ register struct buffer *b, *b2; + b = b2 = sel_nbuf(buf); + do { if(b == buf) break; + if(b->b_flags & B_MODIFIED) + return(b); + } while((b = sel_nbuf(b)) != b2); + + return(0); +} + +struct buffer * +sel_nbuf(buf) +register struct buffer *buf; +{ register struct buffer *b; + + b = buf; + do { + if(!b || !(b = b->b_next)) + b = buf_head; + if(*b->b_name != SP) + break; + } while (b != buf); + return(b); +} + + +kill_buf(buf) +struct buffer *buf; +{ register struct buffer *b, *b1, *bt; + + b = buf; + b1 = 0; + for(bt = buf_head; bt && bt != b; bt = bt -> b_next) + b1 = bt; + if(bt == 0) + { ring_bell(); + errbarf("No such buffer"); /* Internal error */ + return; + } + if (b1 == 0) + buf_head = b->b_next; + else + b1->b_next = b->b_next; + sbs_del(sb_close((SBBUF *)b)); /* Close buffer & delete sbstring */ + sb_fdcls(-1); /* Make sweep for unused FD's */ + if(b->b_fn) + chkfree(b->b_fn); /* Flush filename if one */ + chkfree(b->b_name); /* Flush name */ + chkfree((char *)b); /* Flush buffer */ +} + +/* ZAP_BUFFER - Delete all of the buffer, but if it's been modified, + * ask first. Returns 0 if user aborts. + */ +zap_buffer() +{ +#if IMAGEN + extern struct buffer *exec_buf; /* in e_make.c */ + + if(cur_buf != exec_buf && cur_buf -> b_flags & B_MODIFIED) +#else + if(cur_buf -> b_flags & B_MODIFIED) +#endif /*-IMAGEN*/ + if(ask_kbuf(cur_buf) <= 0) + return(0); /* Aborted */ + ed_reset(); /* This takes care of redisplay too */ + mark_p = 0; +#if IMAGEN + cur_buf->b_flags &= ~B_BACKEDUP; /* Clear backed-up flag */ +#endif + return(1); +} + + + +/* ASK_KBUF - Ask user "are you sure?" before killing a buffer. + * Returns +1 if user says "yes" - OK to kill. + * 0 if user aborts (^G) + * -1 if user says "no". + */ +ask_kbuf(buf) +struct buffer *buf; +{ register struct buffer *b; + register char *s; + register int ans; + + b = buf; + s = ask("Buffer %s contains changes - forget them? ", b->b_name); + if(s == 0) return(0); + ans = (upcase(*s) == 'Y') ? 1 : -1; + chkfree(s); + return(ans); +} + +/* Window stuff */ + +/* Like EMACS, ELLE only provides at most two user windows. + * The current user window is pointed to by user_win; + * the "other" one is oth_win. If oth_win == 0, there is only one user + * window. + */ + +#if FX_2MODEWINDS +int sepmode_p = 0; /* Set true if separator window is a 2nd mode win */ +#endif + +/* EFUN: "Two Windows" */ +/* Divide the current window in half, put the current buffer in the + * other window, and go to the new window. + */ +f_2winds() +{ register int h, t; + register struct window *w; + + if (oth_win) + { +#if !(IMAGEN) + ding("Already 2 windows"); +#endif /*-IMAGEN*/ + return; + } + w = cur_win; + d_fixcur(); /* Stabilize current window */ + h = (w->w_ht) / 2; + t = w->w_pos + h; /* Pos of dividing window */ + sep_win = make_win(t, 1, lines_buf); + /* assume using dashes to separate */ + oth_win = make_win(t + 1, w->w_ht - (h + 1), cur_buf); + /* Other window has balance */ +#if FX_SOWIND + oth_win->w_flags |= cur_win->w_flags&W_STANDOUT; + sep_win->w_flags |= mode_win->w_flags&W_STANDOUT; +#endif +#if FX_2MODEWINDS + chk2modws(); /* Update 2-mode-wind status */ +#endif + w->w_ht = h; /* Decrease current window to half */ + + /* Minimize redisplay by moving each window's dot into + * a currently displayed area */ + if(cur_dot < (oth_win->w_topldot = scr[t+1]->sl_boff)) + oth_win->w_dot = oth_win->w_topldot; /* Adj bottom win */ + else /* Adjust top window */ + { oth_win->w_dot = cur_dot; /* Bottom keeps dot */ + cur_dot = scr[t-1]->sl_boff; /* but top needs new one. */ + } + f_othwind(); /* switch to other window */ + redp(RD_WINDS); /* Update all windows */ +} + + +/* EFUN: "One Window" */ +/* Revert to using only one window; use the current buffer (unlike + * EMACS which always selects the top window's buffer) + * Ensures that current window's vars are correctly set for + * new dimensions (w_pos, w_ht, plus w_topldot to minimize redisplay), + * then kills unneeded windows. + */ +f_1wind() +{ register struct window *w; + + if (oth_win == 0) + { +#if (!IMAGEN) + ding("Only 1 window"); +#endif /*-IMAGEN*/ + return; + } + w = cur_win; + if(w->w_pos) /* If not top window */ + { d_fixcur(); /* Ensure screen-line data correct */ + e_go(w->w_topldot); /* Beginning from top of window, */ + d_fgoloff(-w->w_pos); /* Move back enough lines */ + w->w_topldot = e_dot(); /* To set new start of window */ + e_gocur(); /* Then move back to orig place */ + w->w_pos = 0; + } + w->w_ht += oth_win -> w_ht + 1; + kill_win (oth_win); + kill_win (sep_win); + oth_win = sep_win = 0; +#if FX_2MODEWINDS + chk2modws(); /* Update notion of whether have 2 mode winds */ +#endif + redp(RD_FIXWIN|RD_WINDS|RD_MODE); /* New topldot for this window, + * and check all remaining windows */ +} + +/* EFUN: "Other Window" */ +/* Move to the "other" user window. + */ +f_othwind () +{ if (oth_win == 0) + { +#if !(IMAGEN) + ding("Only 1 window"); +#endif /*-IMAGEN*/ + return; + } + chg_win(oth_win); + oth_win = user_win; + user_win = cur_win; + redp(RD_MODE); +} + +/* EFUN: "Grow Window" */ +/* Grow the current window - while in two window mode, + * increase the size of the current window by the arg + * and decrease the other accordingly + */ +f_growind() +{ register struct window *cw, *ow; + register int e; + + if ((ow = oth_win) == 0) + { +#if !(IMAGEN) + ding("Only 1 window"); +#endif /*-IMAGEN*/ + return; + } + e = exp; + if((cw = cur_win)->w_pos != 0) /* If current window is on bottom */ + { cw = ow; /* Then fake code to think it's top */ + ow = cur_win; + e = -e; + } + if( cw->w_ht + e < 1 + || ow->w_ht + e < 1) + { ding("Too much"); + return; + } + cw -> w_ht += e; + ow -> w_pos += e; + ow -> w_ht -= e; + sep_win -> w_pos += e; + redp(RD_WINDS | RD_MODE); /* Update all windows */ +} + +#if FX_SHRINKWIND +/* EFUN: "Shrink Window" (not EMACS) - from IMAGEN config */ +f_shrinkwind() +{ + if (! oth_win) + return; + f_othwind(); + f_growind(); + f_othwind(); +} +#endif /*FX_SHRINKWIND*/ + +#if FX_DELWIND +/* EFUN: "Delete Window" (not EMACS) - from IMAGEN config */ +f_delwind() +{ + if(!oth_win) + return; + f_othwind(); + f_1wind(); +} +#endif /*FX_DELWIND*/ + +#if FX_SOWIND +/* EFUN: "Standout Window" (not EMACS) */ +/* Toggles the display standout mode for the current window. +** With argument of 4, toggles the standout mode for the non-buffer +** parts of the screen, such as the ELLE mode line. +** (This corresponds to FS INVMOD$ in EMACS) +** With argument of 0, turns standout mode off for all windows. +*/ +/* It suffices to set the window flag bit and force a RD_WINRES for that + * window; the redisplay code will do the rest. +*/ +static void tgso_wind(); + +f_sowind() +{ + register struct window *w; + switch(exp) + { default: /* Toggle current window */ + tgso_wind(cur_win); + break; + case 4: /* Toggle mode & separator windows */ + tgso_wind(mode_win); + tgso_wind(sep_win); /* This may not exist */ + break; + case 0: /* Turn off standout for all winds */ + for(w = win_head; w; w = w->w_next) + if(w->w_flags&W_STANDOUT) + tgso_wind(w); + } +#if FX_2MODEWINDS + chk2modws(); /* Update notion of whether have 2 mode winds */ +#endif +} + +static void +tgso_wind(w) /* Toggle standout mode for given window */ +register struct window *w; +{ + if (w == 0) return; /* For case of no sep_win */ + if (w->w_flags & W_STANDOUT) + w->w_flags &= ~W_STANDOUT; + else w->w_flags |= W_STANDOUT; + w->w_redp |= RD_WINRES; /* Re-do this particular window */ + redp(RD_CHKALL); /* Check all windows for changes */ +} +#endif /*FX_SOWIND*/ + + +#if FX_2MODEWINDS +/* EFUN: "Two Mode Windows" (not EMACS) */ +/* With arg, sets ev_2modws to that value (0, 1, or 2). +** No arg, toggles current setting between 0 and 2. +*/ + +f_2modewinds() +{ + ev_2modws = exp_p ? exp : (ev_2modws ? 0 : 2); + chk2modws(); +} + +/* CHK2MODWS - Called after anything changes which might affect +** whether 2 mode windows are in effect or not. Fixes up +** sep_win to either be or not be a mode window. +*/ +chk2modws() +{ register struct window *w; + static struct buffer *sep_buf = 0; + + if(!(w = sep_win)) + { sepmode_p = 0; /* Don't have 2 windows at all */ + return; + } + sepmode_p = (ev_2modws == 1) + ? (mode_win->w_flags&W_STANDOUT) + : ev_2modws; + + if(sepmode_p) /* Turn 2-mode-winds on? */ + { + if(!sep_buf) + sep_buf = make_buf(" **SEPMODE**"); + w->w_buf = sep_buf; + w->w_flags |= W_MODE; + } + else /* Turn 2-mode-winds off */ + { w->w_buf = lines_buf; + w->w_flags &= ~W_MODE; + redp(RD_CHKALL); /* No longer a mode win, so must */ + /* check all to ensure it's updated */ + } + w->w_redp |= RD_WINRES; + redp(RD_MODE); +} +#endif /*FX_2MODEWINDS*/ + +init_win () +{ + win_head = 0; + oth_win = 0; + user_win = make_win(0, scr_ht - (ECHOLINES+1), cur_buf); /* Main */ + mode_win = make_win(scr_ht - (ECHOLINES+1), 1, make_buf(" **MODE**")); + ask_win = make_win(scr_ht - ECHOLINES, 1, make_buf(" **ASK**")); +#if FX_SOWIND + if(ev_modwso) + mode_win->w_flags |= W_STANDOUT; +#endif + + cur_win = user_win; +} + +chg_win(newwin) /* change current window to newwin */ +struct window *newwin; +{ + cur_win->w_dot = cur_dot; /* Save window's current dot */ + cur_win->w_redp |= rd_type&RDS_WINFLGS; /* and its redisplay flags */ + cur_win = newwin; /* OK, switch to new current window */ + cur_buf = newwin->w_buf; /* Set new buffer from win */ + e_gosetcur(newwin->w_dot); /* Set new cur_dot from win too */ + /* Note done this way to canonicalize the location + ** (may be past new EOB) and ensure SB buffer + ** internals agree with cur_dot. + */ + rd_type &= ~RDS_WINFLGS; /* Remove old per-window flags */ + redp(RD_WINRES|RD_MODE); /* Maybe caller shd handle? */ + /* Note WINRES must be set in case we are pointing + * to a buffer that was modified while we were in + * the other window! + */ +} + + +struct window * +make_win (pos, ht, buf) +int pos, ht; +struct buffer *buf; +{ register struct window *w; + register struct buffer *b; + + b = buf; + w = (struct window *) memalloc(sizeof (struct window)); + w->w_flags = 0; + w->w_pos = pos; + w->w_ht = ht; + w->w_buf = b; + w->w_dot = b->b_dot; /* Set dot from buffer value */ + w->w_topldot = 0; /* Set top of window to beg of buffer */ + w->w_pct = 200; /* Assume "ALL" */ + w->w_bmod = 0; + w->w_emod = 0; + w->w_oldz = 0; + w->w_redp = RD_WINRES; /* Window will need complete update */ + w->w_next = win_head; /* Done, now link it in */ + win_head = w; + return (w); +} + +kill_win (win) +struct window *win; +{ register struct window *w, *w1, *kw; + + kw = win; + w1 = 0; + for (w = win_head; w && w != kw; w = w -> w_next) + w1 = w; + if (w == 0) + { ring_bell(); + errbarf("No such window"); /* Internal error */ + return; + } + if (w1 == 0) + win_head = w -> w_next; + else + w1 -> w_next = w -> w_next; + kw->w_buf->b_dot = (kw == cur_win) ? cur_dot : kw->w_dot; + chkfree (kw); +#if IMAGEN /* Not needed? */ + redp (RD_WINRES|RD_WINDS|RD_REDO); +#endif /*IMAGEN*/ +} + +/* + * "Show-window" routines, used to set up, step through, and close a + * temporary "show" window. + * MK_SHOWIN(bufp) + * UP_SHOWIN() + * KL_SHOWIN() + */ + +/* MK_SHOWIN(bufp) - Temporarily display a buffer + */ +mk_showin(b) +struct buffer *b; +{ register struct window *w; + register int i; + int moreflg, intflg; /* Interrupt flag */ + struct window *savwin; + + /* First must set up special window... */ + savwin = cur_win; + chg_win(w = make_win(0, scr_ht-(ECHOLINES+3), b)); + redo: + d_fixcur(); /* Fix up screen image of current window */ + + /* Find how many lines actually used, and reduce size to that */ + i = w->w_ht; + while(--i >= 0) + { + if(scr[i]->sl_boff != w->w_oldz) break; + } + if(++i <= 0) + goto skipit; /* Punt the whole thing */ + if(!(moreflg = (i >= w->w_ht))) + w->w_ht = i; /* Reduce size of window */ + + intflg = upd_wind(w); /* Update the window! */ + if(!intflg) /* Unless input waiting, add prompt. */ + { + yellat( moreflg ? + "--MORE-- (type Space for more, or type any command to flush)" : + "------------------------------------------------ (Hit space to continue)--", + w->w_ht); + + } + tbufls(); /* Ensure all output forced out */ + i = cmd_read(); /* then wait for user to input a char */ + if(i == SP) + { if(moreflg) + { yellat("", w->w_ht); + d_screen(1); + w->w_redp |= RD_WINRES; + goto redo; + } + } +#if !(IMAGEN) /* IMAGEN - always ignore what was typed */ + else unrchf = i; +#endif /*-IMAGEN*/ +skipit: chg_win(savwin); + kill_win(w); + redp(RD_WINDS); /* Update all remaining windows */ +} + +/* Mode Line generation */ + +struct window * +make_mode(bw) +register struct window *bw; /* Base window we are reporting status of */ +{ + register struct buffer *b; /* Buffer of this window */ + struct window *mw, *savew; /* Save current window */ + struct buffer *saveb; /* and current buffer (in case different) */ + char temp[20]; + + saveb = cur_buf; /* Save values prior to context switch */ + savew = cur_win; + b = bw->w_buf; /* Get buffer for that window */ + +#if FX_2MODEWINDS + if((mw = sep_win) && (mw->w_flags&W_MODE) && + (bw->w_pos == 0)) /* Base window is top window? */ + { /* Use sep_win as mode wind */ + } + else +#endif + mw = mode_win; /* Default is normal mode window */ + chg_win(mw); /* Go to mode line window */ + e_gobob(); /* go to beginning */ + e_reset(); /* Flush buffer */ +#if IMAGEN + e_sputz(" "); + e_sputz(b->b_name); + if (b -> b_flags & B_MODIFIED) + e_sputz("*"); + e_sputz(" ("); + if (b->b_flags & B_QUERYREP) + e_sputz("[Query Replace] "); + if (b->b_flags & B_CMODE) + e_sputz("C"); + else if (b->b_flags & B_TEXTMODE) + e_sputz("Text"); + else + e_sputz("Fundamental"); + e_sputz(") "); + if (b->b_fn) + e_sputz(b->b_fn); + e_sputz(" "); +#else + e_sputz(ev_verstr); /* Editor name/version */ + e_sputz(" ("); + e_sputz(cur_mode->mjm_name); /* insert major mode name */ +#if FX_FILLMODE + if(fill_mode) e_sputz(" Fill"); +#endif /*FX_FILLMODE*/ +#if FX_SKMAC + if(kdef_mode) e_sputz(" MacroDef"); +#endif /*FX_SKMAC*/ + e_sputz(") "); + e_sputz(b->b_name); /* buffer name */ + e_sputz(": "); + if (b->b_fn) + e_sputz(b->b_fn); /* file name */ + if (b->b_flags & B_MODIFIED) + e_sputz(" *"); + else e_sputz(" "); +#endif /*-IMAGEN*/ + if(bw->w_pct < 200) /* Not ALL? */ + { e_sputz(" --"); + switch(bw->w_pct) + { case -1: + e_sputz("TOP"); + break; + case 150: + e_sputz("BOT"); + break; + default: + dottoa(&temp[0],(chroff)bw->w_pct); + e_sputz(&temp[0]); + e_putc('%'); + } + e_sputz("--"); + } +#if FX_SOWIND + if(mw->w_flags&W_STANDOUT) + e_insn(SP, (int)(scr_wd0 - e_blen())); /* Stuff out with spaces */ +#endif + + redp(RD_WINRES); + chg_win(savew); /* Restore context */ + chg_buf(saveb); + return(mw); /* Return mode window */ +} + + +buf_mod() +{ register struct buffer *b; + + b = cur_buf; + if((b->b_flags & B_MODIFIED) == 0) + { b->b_flags |= B_MODIFIED; + redp(RD_MODE); + } +} + +/* BUF_TMOD - called when text modified in buffer, to set all + * the appropriate indicators so that redisplay works right. + * Changed text is everything from CUR_DOT to the given offset + * from same. If stuff was deleted, offset should be 0. + * BUF_TMAT - similar but argument is location of other end of range, + * when caller knows that and wants life easy. + */ + +buf_tmat(dot) +chroff dot; +{ buf_tmod(dot - cur_dot); /* Convert to offset */ +} +buf_tmod(offset) +chroff offset; +{ register struct window *w; + chroff a, b, tmp; + + w = cur_win; + a = cur_dot; + b = a + offset; + if(a > b) /* Get into right order */ + { tmp = a; + a = b; + b = tmp; + } + b = e_blen() - b; /* Make upper bound relative to EOB */ + if(w->w_bmod < 0) /* Have range vars been set yet? */ + { w->w_bmod = a; /* Nope, so can just set 'em now. */ + w->w_emod = b; + } + else + { if(a < w->w_bmod) + w->w_bmod = a; + if(b < w->w_emod) + w->w_emod = b; + } + buf_mod(); /* Maybe later just insert here? */ + redp(RD_TMOD); +} diff --git a/commands/elle/eecmds.c b/commands/elle/eecmds.c new file mode 100755 index 000000000..b92183f59 --- /dev/null +++ b/commands/elle/eecmds.c @@ -0,0 +1,319 @@ +/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ + +/* EECMDS Command table lookup and profile code + */ + +#include "elle.h" + +/* Function table, see the included file for explanation. */ + + /* First must pre-declare function addrs */ +#define EFUN(rtn,rtnstr,name) int rtn(); +#define EFUNHOLE +#include "eefdef.h" + + /* Now re-insert to define function table */ +int (*funtab[])() = +{ +#undef EFUN /* Avoid redefinition error message */ +#undef EFUNHOLE +#define EFUN(rtn,rtnstr,name) rtn, +#define EFUNHOLE 0, +#include "eefdef.h" +}; +int funmax = sizeof(funtab)/sizeof(funtab[0]); /* 1st illegal function # */ + +/* Insert default command char map tables and profile structure */ + +#include "defprf.c" + +/* EFUN: "Prefix Meta" */ +/* Meta-prefix command. + * For now, very simple. Perhaps later try to hair up with + * time-out "M-" prompt? + */ +f_pfxmeta() +{ return(cmd_xct(cmd_read()|CB_META)); +} + +/* EFUN: "Prefix Extend" */ +/* Extended-prefix command. + * Likewise trivial; perhaps later hair up with timeout "^X-" prompt? + */ +f_pfxext() +{ return(cmd_xct(cmd_read()|CB_EXT)); +} + +/* EFUN: "Universal Arg" */ +/* This routine is also called by "Argument Digit" with a special arg + * of -1 in order to share code. Since that invocation always sets unrchf, + * it should always complete at least one digit read loop. + * Note that exp and exp_p are set to 1 and 0 at the top-level command + * loop. + */ +f_uarg(ch) +int ch; +{ register int c, oc, i; + + /* Set distinguishing exp_p value depending on whether invoked + * by CTRL-U or another function (Argument Digit, Negative Argument) + */ + exp_p = (ch < 0) ? 1 : 4; + i = 0; /* Read numerical arg if any follows */ + for(;;) + { oc = cmd_read(); /* Get next input char */ + c = oc & 0177; + if(c == '-' && !i) + { exp_p = -1; + exp = 1; /* Set in case no digits follow */ + } + else if('0' <= c && c <= '9') /* If it's a digit too, */ + { i = (i * 10) + c - '0'; /* add digit in. */ + if(exp_p >= 0) exp_p = 1; + exp = i; + } + else break; + } + exp *= exp_p; /* Multiply arg appropriately */ + unrchf = oc; /* Not a digit, re-read it next. */ + + this_cmd = ARGCMD; +} + +/* EFUN: "Negative Argument" */ +f_negarg(ch) +int ch; +{ f_uarg(-1); /* Invoke code from Universal Arg */ + exp = -exp; +} + +/* EFUN: "Argument Digit" */ +f_argdig(ch) +int ch; +{ unrchf = ch; /* Re-read the digit */ + f_uarg(-1); /* Invoke code from Universal Arg */ +} + +/* EFUN: "Set Profile" */ +/* Asks for a profile file and sets profile from it. + */ +f_setprof() +{ int set_profile(); + hack_file("Set Profile: ", set_profile); +} + +#if FX_VTBUTTONS +/* EFUN: "VT100 Button Hack" */ +/* This must be bound to Meta-O if anything, because the VT100 sends + * an ESC O prefix when the function buttons are used. + */ +f_vtbuttons () /* vt100 function buttons */ +{ + switch(cmd_read()) + { case ('A'): + return (f_uprline ()); + case ('B'): + return (f_dnrline ()); + case ('C'): + return (f_fword ()); + case ('D'): + return (f_bword ()); + case ('Q'): /* PF1 */ + return (f_kregion()); + default: + ring_bell (); + break; + } +} +#endif /*FX_VTBUTTONS*/ + +/* CMD_WAIT() - Return TRUE if any command input waiting. +*/ +cmd_wait() +{ return(unrchf >= 0 +#if FX_SKMAC + || km_inwait() /* Check for kbdmac input waiting */ +#endif /*FX_SKMAC*/ + || tinwait()); +} + +/* CMD_READ() - Read a command (single char) from user, and return it. +*/ +cmd_read() +{ register int c; + + if((c = unrchf) >= 0) /* Re-reading last char? */ + { unrchf = -1; + return(c); + } +#if FX_SKMAC /* Hacking keyboard macros? */ + return(km_getc()); /* Yes. This calls tgetc if no kbd macro */ +#else + return(tgetc()); +#endif /*-FX_SKMAC*/ +} + +/* CMD_XCT(ch) - Command Execution dispatch routine. +** Takes char and executes the function (efun) bound to that command key. +*/ +cmd_xct(ch) +int ch; +{ register int (*funct) (); + register int c; + int (*(cmd_fun())) (); + + if(funct = cmd_fun(c = ch)) /* Get function to run */ + return((*funct) (c&0177)); /* Invoke with char arg */ + ring_bell(); /* Undefined command char, error. */ +} + +/* CMD_FUN(ch) - Return function for char, 0 if none +*/ +int (*cmd_fun(c))() +int c; +{ + return(funtab[cmd_idx(c)]); +} + +/* CMD_IDX(ch) - Given command char, return function index for it +*/ +cmd_idx(c) +register int c; +{ register char *cp; + register int i; + + if(c&CB_EXT) + { cp = def_prof.extvec; + i = def_prof.extvcnt; + goto inlup; + } + if(c&CB_META) + { cp = def_prof.metavec; + i = def_prof.metavcnt; + inlup: c = upcase(c); + do { if(*cp++ != c) cp++; + else + { i = *cp&0377; + break; + } + } while(--i); /* If counts out, will return 0! */ + } + else i = def_prof.chrvec[c&0177]&0377; + if(i >= funmax) + return(0); + return(i); +} + +/* Profile hacking */ + +#if TOPS20 +#include /* for O_BINARY */ +#endif + +set_profile(filename) +char *filename; +{ char pfile[200]; + char psfile[200]; + register int pfd, len; + chroff sbx_fdlen(); + register char *profptr; + struct stored_profile st_prof; + + if(filename) strcpy(pfile,filename); + else /* Check for user's profile */ + { + strcat(strcat(strcpy(pfile,homedir),"/"),ev_profile); + } + if((pfd = open(pfile, +#if TOPS20 + O_BINARY +#else + 0 +#endif + )) < 0) + { if(filename) + { ding("Cannot open file"); + } + return; + } + if((len = (int)sbx_fdlen(pfd)) < sizeof(struct stored_profile)) + goto badfil; + profptr = memalloc((SBMO)len); + if(read(pfd,profptr,len) != len) + goto badfmt; + + /* Have read profile into memory, now set up ptrs etc */ + bcopy((SBMA)profptr,(SBMA)&st_prof,sizeof(struct stored_profile)); + def_prof.version = prof_upack(st_prof.version); + if(def_prof.version != 1) + goto badfmt; + def_prof.chrvcnt = prof_upack(st_prof.chrvcnt); + def_prof.chrvec = profptr + prof_upack(st_prof.chrvec); + def_prof.metavcnt = prof_upack(st_prof.metavcnt); + def_prof.metavec = profptr + prof_upack(st_prof.metavec); + def_prof.extvcnt = prof_upack(st_prof.extvcnt); + def_prof.extvec = profptr + prof_upack(st_prof.extvec); +#if SUN + def_prof.menuvcnt = prof_upack(st_prof.menuvcnt); + def_prof.menuvec = profptr + prof_upack(st_prof.menuvec); +#endif /*SUN*/ + goto done; + +badfmt: chkfree(profptr); +badfil: ding("Bad profile format"); +done: close(pfd); +} + +#if SUN +/* SUN Menu profile hacking. + * This is here, instead of e_sun.c, because + * the profile format is still evolving and for the time being I want to + * keep all profile-hacking code in one place. --KLH + */ +#include "suntool/tool_hs.h" +#include "suntool/menu.h" + +#define MENUMAX 16 + +/* Defined in eesun.c */ +extern struct menu *menuptr; +extern struct menu menu; + +char *funamtab[] = { +#undef EFUN +#undef EFUNHOLE +#define EFUN(rtn,rtnstr,name) name, +#define EFUNHOLE 0, +#include "eefdef.h" +}; + +init_menu() /* initialize the menu for elle from user profile */ +{ + register struct menuitem *mi; + register int n, i, fni; + + if((n = def_prof.menuvcnt) <= 0) + return; + if(n > MENUMAX) n = MENUMAX; + mi = menu.m_items = (struct menuitem *) calloc(n, sizeof *mi); + + menu.m_itemcount = 0; + for(i = 0; i < n; i++) + { fni = def_prof.menuvec[i]&0377; + if(fni >= funmax) continue; + if(funtab[fni] && funamtab[fni]) + { mi->mi_data = (caddr_t) funtab[fni]; + mi->mi_imagedata = (caddr_t) strdup(funamtab[fni]); + mi->mi_imagetype = MENU_IMAGESTRING; + mi++; + menu.m_itemcount++; + } + } + if(menu.m_itemcount) + menuptr = &menu; +} +#endif /*SUN*/ diff --git a/commands/elle/eediag.c b/commands/elle/eediag.c new file mode 100755 index 000000000..1b7b3da64 --- /dev/null +++ b/commands/elle/eediag.c @@ -0,0 +1,352 @@ +/* ELLE - Copyright 1982, 1985, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* EEDIAG - Error diagnostics and testing routines + */ + +#include "elle.h" + +#if FX_DEBUG + +/* EFUN: "Debug Mode" */ +/* With no arg, toggles self-checking on and off. + * With arg of 4 (^U), enters special debug/diagnostics mode. + */ + +f_debug(ch) +int ch; +{ extern int (*vfy_vec)(); /* In E_MAIN.C */ + char *vfy_data(); + + if(ch < 0) /* Internal call? */ + { dbg_diag(); + return; + } + if(exp == 4) + { askerr(); + return; + } + if(vfy_vec) vfy_vec = 0; /* Toggle current value */ + else vfy_vec = (int (*)())vfy_data; + say(vfy_vec ? "Self-checking on" : "Self-checking off"); +} + +char * +vfy_data(flag) /* Flag = 0 for quiet check */ +int flag; +{ + register char *res, *mess; + char *sbe_mvfy(), *sbe_sbvfy(), *sbe_svfy(); + + if(res = sbe_mvfy(0)) mess = "Mem mgt"; + else if(res = sbe_sbvfy(cur_buf,0)) mess = "SBBUF"; + else if(res = sbe_svfy(0)) mess = "SD list"; + else return(0); /* Success */ + + if(flag) + { int ostate = clean_exit(); + printf("\n%s error: %s !!!\n",mess,res); + askerr(); + if(ostate > 0) set_tty(); + } + return(res); /* Error seen */ +} + +extern char *asklin(); +extern int sbx_nfl,sbm_nfl; + +char diaghelp[] = "\ +Q - Quit diag mode\n\ +! - Goto subshell\n\ +V - Verify Mem & SD lists\n\ +MF - Mem Freelist\n\ +M - Mem list\n\ +B - Current buffer SB\n\ +DF - SD Freelist\n\ +D - SDs in use\n\ +DL - SD Logical lists\n\ +DP - SD Physical lists\n\ +C n - Compact; 0-7=sbx_comp(n), 8=SM freelist, 9=SD freelist.\n\ +W - Window printout\n\ +X n - Xercise randomly (GC every n)\n\ +Z n - like X but with notes\n"; + +dbg_diag() +{ register char *cp; + register int c; + char linbuf[100]; + char *sbe_mfl(); + char *sbe_sfl(); + char *sbe_sbs(); + char *sbe_sdlist(); + + for(;;) + { printf("D>"); + asklin(cp = linbuf); /* Read a line of input */ + switch(upcase(*cp++)) + { + case '?': + writez(1,diaghelp); /* Too long for printf */ + continue; + case '!': + f_pshinf(); /* Invoke inferior subshell */ + clean_exit(); /* Restore normal modes */ + continue; + case 'Q': /* Quit */ + return; + + case 'B': /* Print current SBBUF */ + sbe_sbs(cur_buf,1); + continue; + + case 'C': /* C n - Compact */ + c = atoi(&linbuf[1]); + if(c == 8) + sbm_ngc(); /* GC the SM nodes */ +#if 0 /* This doesn't work, dangerous to invoke. */ + else if(c == 9) + sbm_xngc(&sbx_nfl,sizeof(struct sdblk), + SM_DNODS); +#endif + else + sbx_comp(512,c); + continue; + + case 'D': /* Print all SD blocks in mem order */ + switch(upcase(*cp)) + { + case 0: /* D - all SDs in mem order */ + sbe_sds(); + continue; + case 'F': /* DF - SD freelist */ + sbe_sfl(1); + continue; + case 'L': /* DL - SD logical list */ + sbe_sdlist(1,0); + continue; + case 'P': /* DP - SD physical list */ + sbe_sdlist(1,1); + continue; + } + break; /* failure */ + + case 'M': + switch(upcase(*cp)) + { + case 0: /* M - all mem alloc info */ + sbe_mem(); + continue; + case 'F': /* MF - mem freelist */ + sbe_mfl(1); + continue; + } + break; /* failure */ + + case 'V': /* Verify */ + if(cp = vfy_data(0)) + printf(" Failed: %s\n",cp); + else printf(" OK\n"); + continue; + case 'W': /* Print out current window */ + db_prwind(cur_win); + continue; + case 'X': /* Xercise */ + c = atoi(&linbuf[1]); + vfy_exer(0, c ? c : 100); + continue; + case 'Z': /* Zercise */ + c = atoi(&linbuf[1]); + vfy_exer(1, c ? c : 100); + continue; + + } /* End of switch */ + + printf("?? Type ? for help\n"); + } /* Loop forever */ +} + + +/* VFY_EXER - a "random" editor exerciser. It creates a buffer, + * fills it with some patterned stuff, and then edits it + * pseudo-randomly in ways which retain the basic pattern. + * Frequent GC's and self-checks are done, and execution + * halted either when an error is seen or when typein is detected. + */ +char *xer_strs [] = { + "throne", "too", "sky", "fore", "fingers", "sex", "stone", + "010", "nazgul", "base" +}; + + +vfy_exer(pf, gcfrq) +int pf; /* Nonzero to print notes as we go */ +int gcfrq; /* Frequency of GC invocation (# passes per GC) */ +{ register int i, k, c; + long npass; + char *res, linbuf[100]; + chroff lbeg, lend; + struct buffer *bfp, *make_buf(); + + /* Clean out kill buffer first */ + for(i = 0; i < KILL_LEN; ++i) + kill_push((SBSTR *)0); + + bfp = make_buf("**EXORCISE**"); + chg_buf(bfp); + i = 2000; + e_gobol(); + do { + ed_sins("Line "); + ed_sins(xer_strs[i%10]); + e_putc(LF); + } while(--i); + if(pf) printf("Bufflen: %ld\n", e_blen()); + + /* Buffer now has stuff in it, start hacking. */ + npass = 0; + srand(1); /* Start random seed */ + for(;;) + { if(tinwait() && (*asklin(linbuf))) + { printf("Typein stop.\n"); + break; + } + ++npass; + printf(" Pass %ld",npass); + if(npass%gcfrq == 0) /* Time to do a GC? */ + { + i = rand(); /* Level between 0-4 */ + i = (i < 0 ? -i : i) % 5; + printf(" - GC lev %d\n", i); + sbx_comp(512,i); + goto xerchk; + } + + k = (i = rand())%1024; + if (i&020000) k = -k; + e_igoff(k); /* Move randomly */ + e_gobol(); /* Get stuff to flush */ + lbeg = e_dot(); + k = (i = rand())%64; + if(i&010000) k = -k; + e_igoff(k); + lend = e_nldot(); + if(pf) printf(" Kill %ld/ %d;", lbeg, k); + ed_kill(lbeg, lend); + if(res = vfy_data(0)) + { printf("XERR after kill: %s\n",res); + break; + } + k = (i = rand())%2048; + if(i&04000) k = -k; + e_igoff(k); + e_gobol(); + e_setcur(); + if(pf) printf(" Yank %ld;", e_dot()); + f_unkill(); /* Yank back */ + if(res = vfy_data(0)) + { printf("XERR after yank: %s\n",res); + break; + } + last_cmd = YANKCMD; + for(i = rand()%4; i >= 0; --i) + { if(pf) printf(" Pop;"); + f_unkpop(); /* Do meta-Y */ + if(res = vfy_data(0)) + { printf("XERR after pop: %s\n",res); + goto out; + } + } + if(rand()&07) /* Slowly add stuff */ + { if(pf) printf(" Add"); + ed_sins("Line "); + ed_sins(xer_strs[rand()%10]); + e_putc(LF); + if(res = vfy_data(0)) + { printf("XERR after ins: %s\n",res); + break; + } + } + printf("\n"); + + /* Okay, done with this pass edits, run through the + * file to ensure pattern is still there + */ + xerchk: e_gobob(); + while((c = e_getc()) != EOF) + if(c == LF && (c = e_getc()) != EOF) + { if( c != 'L' + || e_getc() != 'i' + || e_getc() != 'n' + || e_getc() != 'e' + || e_getc() != ' ') + { printf("XERR in pattern!\n"); + goto out; + } + } + } + /* User typein or error, stop. */ +out: e_setcur(); + redp(RD_SCREEN); + printf("Loop count = %ld\n",npass); +} + +/* DB_PRWIND(win) - Print out stuff about given window + */ +db_prwind(w) +register struct window *w; +{ register struct scr_line *s; + register int i; + char tstr[MAXLINE+MAXCHAR]; + char *db_scflgs(); + + printf("cur_dot/ %ld cur_buf/ %o cur_win/ %o\n", + cur_dot, cur_buf, cur_win); + + printf("Window %o:\n", w); + printf(" next/ %o\n", w->w_next); + printf(" buf / %o\n", w->w_buf); + printf(" redp/ %o\n", w->w_redp); + + printf(" topldot/ %ld\n", w->w_topldot); + printf(" dot / %ld\n", w->w_dot); + printf(" bmod/ %ld\n", w->w_bmod); + printf(" emod/ %ld\n", w->w_emod); + printf(" oldz/ %ld\n", w->w_oldz); + + printf(" pos / %d\n", w->w_pos); + printf(" ht / %d\n", w->w_ht); + printf("\ +# Flags Boff Len ! Cols Line\n"); + for(i = w->w_pos; i < w->w_pos + w->w_ht; ++i) + { s = scr[i]; + printf("%2d %-5.5s %6ld %3d %1d %4d ", + i, db_scflgs(s->sl_flg), s->sl_boff, s->sl_len, + s->sl_cont, s->sl_col); + strncpy(tstr, s->sl_line, MAXLINE); + tstr[s->sl_col] = 0; + printf("%-40.40s\n", tstr); + if(s->sl_flg&SL_MOD) + { printf("%26d ", s->sl_ncol); + strncpy(tstr, s->sl_nlin, MAXLINE); + tstr[s->sl_ncol] = 0; + printf("%-40.40s\n", tstr); + } + } +} + +char * +db_scflgs(flags) +int flags; +{ static char retstr[10]; + register char *cp; + + cp = retstr; + if(flags&SL_MOD) *cp++ = 'M'; + if(flags&SL_EOL) *cp++ = 'E'; + *cp = 0; + return(retstr); +} + +#endif /*FX_DEBUG*/ diff --git a/commands/elle/eedisp.c b/commands/elle/eedisp.c new file mode 100755 index 000000000..3827bb71e --- /dev/null +++ b/commands/elle/eedisp.c @@ -0,0 +1,1714 @@ +/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* EEDISP Redisplay and screen image routines + */ + +#if 0 + +Note that there are several different types of "efficiency" criteria +involved with respect to display updating: + (1) Terminal speed: minimize # characters output. + (2) Program speed: minimize CPU time used. + (3) Program size: minimize code and memory usage. + (4) Program modularity: minimize "hooks" between edit/display rtns. +The current algorithms necessarily represent a compromise among all of +these objectives. + + The cursor is always located at CUR_DOT in the buffer CUR_BUF +of the current window CUR_WIN. This may not be true during function +execution, but is always true at the top-level loop of command +execution and redisplay. In order to minimize update overhead, there +are various flags or variables that the edit functions can use to +communicate with "redisplay" and tell it how extensive the updates +really need to be. + + The entire known screen is always represented by a linked list +of "windows"; updating the entire screen consists of separately +updating every window on the list. Windows can only be defined +horizontally (as a range of lines), and must not overlap. Each window +has a buffer associated with it; the redisplay routines are responsible +for displaying the contents of this buffer. + + The lowest level data structure for the screen consists of an +array of SCR_LINE structures, one for each possible physical screen +line. Each line structure has some flags, and pointers to three different +representations of what should be on the line: + (1) SL_BOFF, SL_LEN - Defines the range of the buffer data which + this screen line should represent. + If the flag SL_EOL is set, this range ends with (and includes) + an EOL character. + (2) SL_LINE, SL_COL - Always keeps a copy of the current physical + screen line image. Each byte is a character which occupies + only one column position on the screen. + If the flag SL_CSO is set, the line is in standout mode. + (3) SL_NLIN, SL_NCOL - The desired "new" screen line image. + This is only valid if the SL_MOD flag is set for the line, + indicating that these variables are set and point to the + new image of what the screen line should be. + If the flag SL_NSO is set, the new line should be in standout + mode. + + Lastly there is a variable SL_CONT, which is needed for +continuation of too-long logical lines over several physical lines. If +SL_CONT is: + 0 = logical line fits entirely on the screen. + Either SL_EOL is set, or this line is ended by EOF + (end of the buffer). + 1 = logical line is too long, but the last buffer char fits + entirely on this physical line. SL_EOL is never set. + >1 = logical line is too long, and the last buffer char + "overruns" the end of the physical image; that is, part of + its representation is at the end of this line, but the + rest of it is at the start of the next line. This can + only happen with "big" characters like TAB, ^A, ~^A, etc. + that need more than one column of representation. + There are SL_CONT-1 chars of overrun stored at the + end of SL_LINE (SL_NLIN if SL_MOD is set). + SL_EOL is never set. + +Note that if a line contains any overrun, and the next line is also +part of the same window, the next line''s screen image will start with +the SL_CONT-1 chars of overrun, rather than with the representation of +that line''s first buffer char. + + The "EOL" character on Unix systems is normally the new-line +character '\n' (ASCII LF). However, on other systems EOL may be +indicated by a two-character CR-LF sequence, with either CR or LF alone +considered to be "stray". For this reason, the buffer flag B_EOLCRLF +exists to control handling and display of EOLs. If the flag is off, +the EOL mode is LF, and there are no problems of splitting up characters. +If the flag is on, however, the EOL mode is CRLF and the following rules +hold: + EOL is the sequence CR-LF only. + LF without preceding CR is a "stray" LF, displayed as ^J. + CR without following LF is a "stray" CR, displayed as ^M. + Stray LFs and CRs do not terminate a logical line. + "End of Line" as a position is the dot just before the CR of a CR-LF. + "Beg of Line" as a position is the dot just after the LF of a CR-LF. + If the current dot is between a CR and LF, it is positioned at + the beginning of the physical screen line. + + +SL_LINE and SL_COL are always accurate at every stage of processing. +The other variables are accurate only after fix_wind has been called +to "fix up" the line structures within a window. If either +RD_WINRES or RD_TMOD is set, none of these "other variables" should +be depended on. Any functions which are screen-relative (d_ type) +must be sure that fix_wind is called if necessary, and must give +preference to the "new" representation in SL_NLINE and SL_NCOL if +SL_MOD is set. + +The flag RD_UPDWIN will be set by fix_wind if any lines have been +modified. Because fix_wind does not perform any actual display update, +it is possible for functions to continue operating on the buffer and +screen image without requiring that changes be displayed until there is +nothing else left to do. The routine upd_wind performs the actual +terminal I/O necessary to update all the screen lines which have SL_MOD +set. Although the process of updating each line is currently +non-interruptible, it is possible for upd_wind to interrupt itself +between line updates if it detects that user input has happened, and it will +return with the window only partially updated. The screen image state +will be completely consistent, however, and the RD_UPDWIN flag will +remain set. + +Communication between the editing functions and the redisplay routines +is limited as much as possible to the flags in the global RD_TYPE. +Each window has its own copy of these flags in W_REDP, so that if +windows are changed, the update hints for that window will be +preserved. The flags that can be set are listed below. Those marked +with "*" are global in nature; all others apply only within a single +window (normally the current window). + +* RD_SCREEN - Total refresh. Clears entire screen and redisplays all + windows. +* RD_MODE - Mode line has changed, update it. +* RD_CHKALL - Check ALL windows for any redisplay flags, and perform + any updates necessary. Otherwise only the current (or specified) + window flags are checked. +* RD_WINDS - Updates all windows. Like RD_WINRES applied to all windows. + RD_WINRES - Update window (assume completely changed). + RD_TMOD - Text changed in this window. The range of changes is + specified by W_BMOD and W_EMOD in combination with W_OLDZ. + Redisplay checking will limit itself to this range. + These vars are set by buf_tmod in the main command loop, and + reset by fix_wind when the window is fixed up. + RD_MOVE - Cursor has moved within current window; may have moved outside + the window. W_DOT or CUR_DOT specifies where it should be. + RD_ILIN - Hint: Line insert done. Currently no function sets this. + RD_DLIN - Hint: Line delete done. Currently no function sets this. + +Internal flags: + RD_UPDWIN - Window needs updating. Used by fix_wind and upd_wind only. + Set when window has been "fixed up" and at least one screen + line was modified. + RD_FIXWIN - Supposed to mean window needs fixing (via call to fix_wind). + Not really used. + +Not implemented, may never be, but comments retained: + RD_WINCLR - Clear window (not entire screen) + RD_NEWWIN - Window has moved. (not needed? Random stuff here) + a. to follow cursor; redisplay selects a new TOPLDOT. + b. randomly; new TOPLDOT furnished, use unless cursor out (then a). + c. find new TOPLDOT as directed (move up/down N screen lines) + For now, assume that (c) doesn''t apply (ie C-V uses (b) and sets + TOPLDOT itself). So fix_wind selects new one only if cursor + won''t fit. topldot takes precedence over sl_boff. + +#endif /*COMMENT*/ + +/* Declarations and stuff */ + +#include "elle.h" + +static int sctr(); + +int trm_mode; /* 0 = TTY in normal, non-edit mode. + * 1 = TTY in edit mode. + * -1 = TTY detached (hung up). + * This flag is only used by the 3 routines below, + * plus hup_exit. + */ + +/* REDP_INIT() - Called once-only at startup to initialize redisplay + * and terminal + */ +redp_init () +{ + trm_mode = 0; /* Ensure flag says not in edit mode */ + ts_init(); /* Get sys term info, set up stuff */ + if (trm_ospeed == 0) /* Default speed to 9600 if unknown */ + trm_ospeed = 13; + t_init(); /* Identify term type, set term-dep stuff */ + set_scr(); /* Set up software screen image */ + set_tty(); /* Enter editing mode! */ + redp(RD_SCREEN|RD_MODE); /* Force full re-display, new mode line */ +} + +/* SET_TTY() - Set up terminal modes for editing */ + +set_tty() +{ if(trm_mode) return; /* Ignore if detached or in edit mode */ + trm_mode++; + ts_enter(); /* Set up system's ideas about terminal */ + t_enter(); /* Set terminal up for editing */ +} + +/* CLEAN_EXIT() - Restore original terminal modes. + * Returns previous state. + */ +clean_exit () +{ register int prevstate = trm_mode; + + if(prevstate > 0) /* Ignore unless in editing mode */ + { trm_mode = 0; + t_curpos(scr_ht-1, 0); /* Go to screen bottom */ + t_exit(); /* Clean up the terminal */ + tbufls(); /* Force out all buffered output */ + ts_exit(); /* Restore system's old term state */ +#if ! IMAGEN + writez(1,"\n"); /* Get fresh line using OS output */ +#endif /*-IMAGEN*/ + } + return prevstate; +} + +/* SET_SCR() - Allocate screen image, set up screenline pointer table */ + +set_scr() +{ register struct scr_line **scrp, *stp; + register scrsiz; + char *sbuf; + + scr_wd0 = scr_wid - 1; + scrsiz = scr_ht*(scr_wid+MAXCHAR); + if( scr_ht > MAXHT || scr_wid > MAXLINE) + { clean_exit(); + printf("ELLE: %dx%d screen too big\n",scr_ht,scr_wid); + exit(1); + } + if((stp = (struct scr_line *) calloc(scr_ht*sizeof(struct scr_line) + + scrsiz*2,1)) == 0) + { clean_exit(); + printf("ELLE: not enough memory\n"); + exit(1); + } + sbuf = (char *)stp + scr_ht*sizeof(struct scr_line); + for(scrp = &scr[0]; scrp < &scr[scr_ht]; sbuf += scr_wid+MAXCHAR) + { stp->sl_line = sbuf; + stp->sl_nlin = sbuf + scrsiz; + *scrp++ = stp++; + } +} + +/* REDISPLAY() + * Main function of redisplay routines. Called every time ELLE + * forces update of the terminal screen. "rd_type" contains hints + * as to what has changed or needs updating, to avoid wasting time + * on things which don't need attention. + */ +redisplay () +{ register struct window *w; + register i; + struct window *make_mode(); + + w = cur_win; + w->w_redp |= rd_type&RDS_WINFLGS; /* Set cur_win's flags */ + rd_type &= ~RDS_WINFLGS; /* Leave only globals */ + + if (rd_type & RD_SCREEN) /* Clear and refresh? */ + { + t_clear (); /* Clear the screen */ + for(i = scr_ht; --i >= 0;) /* Clear screen image */ + scr[i]->sl_col = 0; + if(w != ask_win) /* If not in ask-window */ + { chg_win(ask_win); + e_reset(); /* Then flush its contents */ + chg_win(w); + } + redp(RD_WINDS); /* Update all windows */ + rd_type &= ~RD_SCREEN; /* If redisplay is interrupted, */ + /* don't do it all over again */ + } + if (rd_type & RD_WINDS) /* Update all windows? */ + { redp(RD_CHKALL); + for (w = win_head; w; w = w -> w_next) /* For each win */ + w->w_redp |= RD_WINRES; + rd_type &= ~RD_WINDS; + } + if (rd_type & RD_CHKALL) /* Check all windows for changes? */ + { for (w = win_head; w; w = w->w_next) /* For each win */ + if(!(w->w_flags&W_MODE)) /* skip mode wins */ + if(w->w_redp && upd_wind(w)) + return; /* May be interrupted */ + + } + + /* See if ask-window needs updating (to avoid RD_CHKALL in SAY) */ + if((w = ask_win)->w_redp && upd_wind(w)) + return; /* May be interrupted */ + + /* Check current window for changes */ + if((w = cur_win)->w_redp && upd_wind(w)) + return; /* May be interrupted */ + + /* Now update mode line(s) if necessary */ + if(rd_type&RD_MODE) + { + fupd_wind(w = make_mode(user_win)); +#if FX_2MODEWINDS + if (sep_win /* If 2 windows */ + && (sep_win->w_flags&W_MODE) /* and 2 mode windows */ + && (sep_win->w_redp || mode_win->w_redp)) /* Check */ + fupd_wind(make_mode(oth_win)); /* Must update both */ +#endif + } + + /* Finally, leave cursor in right place. */ + if(upd_curs(cur_dot)==0) /* If something screwed up, */ + errbarf("Cursor out of window"); /* Complain, */ + /* and leave cursor at bot */ + rd_type = 0; + tbufls(); /* Force out all terminal output */ +} + +fupd_wind(w) /* Force window update */ +register struct window *w; +{ + w->w_redp |= RD_WINRES; + if(fix_wind(w)) + upd_wind(w); +} + +/* + * UPD_CURS + * Move screen cursor to position of specified dot within current window. + * Returns 0 if dot was not within window (and cursor was not moved), + * otherwise returns 1 for success. + */ +upd_curs(adot) +chroff adot; +{ register struct scr_line *s; + register int y, x; + chroff savdot; + + if((y = d_line(adot)) < 0) + return(0); /* Fail, not within window */ + s = scr[y]; /* Now have line that dot is on */ + + /* Get proper offset for any continuation chars from prev line */ + if(y > cur_win->w_pos) + { if((x = scr[y-1]->sl_cont) > 0) + x--; + } + else x = 0; + + savdot = e_dot(); + e_go(s->sl_boff); + if((x = d_ncols((int)(adot - s->sl_boff),x)) < 0) + { /* If lost, assume it's because we are just after a char + ** which has its representation continued onto next line. + ** Move cursor to end of that continuation. + ** d_line should have ensured that this is safe, but + ** we double-check just to make sure. + */ + if((x = s->sl_cont) > 0) /* Set X to end of cont */ + --x; + /* and on next line down */ + if(++y >= (cur_win->w_pos + cur_win->w_ht)) + { e_go(savdot); /* Failed, below window */ + return(0); + } + } + e_go(savdot); + t_move(y, x); /* Move cursor cleverly */ + return(1); /* Return success! */ +} + +/* Return line # for given dot, -1 if out of current window */ +d_line(cdot) +chroff cdot; +{ register struct scr_line *s; + register struct window *w; + register int i; + chroff savdot; + int bot; + + w = cur_win; + i = w->w_pos; + bot = i + w->w_ht; + for(; i < bot; i++) + { s = scr[i]; + if(cdot <= s->sl_boff) + goto gotl; + } + /* End of window, repeat test specially for last line */ + savdot = s->sl_boff + (chroff)s->sl_len; + if(cdot > savdot) /* If past last char of last line */ + return(-1); /* then clearly outside */ + --i; /* Make i match s (bottom line) */ + if(savdot != cdot) /* If not exactly at end */ + return(i); /* Then we're inside for sure */ + goto linbet; + +gotl: if(s->sl_boff != cdot) /* Are we on line boundary? */ + { if(i <= w->w_pos) /* No, off top of window? */ + return(-1); /* Above top, out for sure */ + return(--i); + } + + /* Here, dot is exactly on line boundary, have to decide which + * line it really belongs to. + * Get S = pointer to line which cursor is at the end of. + */ + if(i <= w->w_pos) /* Quick chk of trivial case, empty buffer */ + return(i); + s = scr[--i]; +linbet: + if((s->sl_flg&SL_EOL) /* If line has LF */ + || (s->sl_cont > 1)) /* or a continued char */ + if(++i >= bot) /* Then cursor is on next line */ + return(-1); + return(i); +} + +/* D_NCOLS - auxiliary for UPD_CURS. (also called by indtion() in EEFD) +** We are positioned at a place in the current buffer corresponding to +** the beginning of the screen line, and given: +** lcnt - # of chars in buffer to move forward over +** ccol - current column position +** Returns the new column position. There are some special cases: +** Hits EOF: returns normally (new column position) +** Hits EOL: returns -1 +** Position is past end of screen: returns -1 +** The buffer position has changed, but this is irrelevant as upd_curs +** restores it just after the call. +*/ +d_ncols(lcnt, ccol) +int lcnt; +int ccol; +{ register int col, i; + register SBBUF *sb; + int c; + char tmp[MAXCHAR*2]; /* MAXCHAR is enough, but *2 just in case */ + + col = ccol; + sb = (SBBUF *) cur_buf; + if((i = lcnt) > 0) + do { if((c = sb_getc(sb)) == EOF) + break; + /* Check to see if we've run into an EOL */ +#if FX_EOLMODE + if(c == CR) + { if(eolcrlf(sb)) + { if((c = sb_getc(sb)) == LF) /* EOL? */ + /* Real EOL. Fail unless point + ** is between CR and LF, in which case + ** we return 0 (left margin). + */ + return (i==1 ? 0 : -1); + /* Stray CR, back up & fall thru */ + if(c != EOF) + sb_backc(sb); + c = CR; + } + } else if (c == LF) + { if(!eolcrlf(sb)) /* Real EOL? */ + return -1; /* Yes, fail */ + /* If EOL mode is CRLF then hitting a LF + ** can only happen for stray LFs (the + ** previous check for CR takes care of + ** CRLFs, and we never start scanning + ** from the middle of a CRLF. + ** Drop thru to show stray LF. + */ + } +#else + if(c == LF) + return(-1); +#endif /*-FX_EOLMODE*/ + col += sctr(c, tmp, col); + } while(--i); + if(col > scr_wd0) + return(-1); + return(col); +} + +/* D_LUPD - called from command level to completely redisplay a + * specific line on the screen. + */ +d_lupd(w, idx) +struct window *w; /* Window this line belongs to, if known */ +int idx; +{ t_curpos(idx, 0); + t_docleol(); /* Zap physical screen line */ + scr[idx]->sl_col = 0; /* Reflect it on phys screen image */ + if(w) /* Mark window for updating */ + w->w_redp |= RD_WINRES; + else redp(RD_WINDS); /* No window given, assume global */ + redp(RD_MOVE); /* Cursor has moved */ +} + +/* Clear a window completely the "quickest possible way" */ +clear_wind(w) +register struct window *w; +{ + register int i = w->w_pos; /* Top line of window */ + register int bot = i + w->w_ht; /* Bottom line (plus 1) of window */ + + for ( ; i < bot; ++i) + d_lupd(w, i); /* Zap that line */ +} + +/* FIX_WIND - Sets up window screen image. Does not generate any + * terminal output, but completely specifies what the new screen + * image should look like. + * Only the following 4 flags (lumped together in RDS_DOFIX) + * provoke fix_wind to do something: + * RD_MOVE - cursor has moved, must make sure still within + * window, and select new one if not. + * RD_TMOD - Text has been changed somewhere. + * RD_FIXWIN - Something requested that fix_wind fix things. + * Normally this is set when a new w_topldot is set. + * RD_WINRES - Window needs to be completely regenerated. + * Results: + * Verifies that the current dot for the window (w_dot) exists. + * If it is past the end of buffer, it is reset to EOB, and if this is + * the current window, also updates cur_dot. Otherwise, w_dot is never + * adjusted; it is fix_wind's responsibility to make sure that the window + * displays w_dot. + * Verifies that current w_topldot setting will result in cursor + * (specified by w_dot) appearing within window. If not, resets w_topldot + * to an appropriate value (1/3 of way down from top, unless + * moving up in which case 1/3 of way up from bottom). + * Makes sure that sl_boff, sl_len, sl_flg, and sl_cont + * are set properly for all lines in window. SL_MOD is set + * for any lines requiring screen updates; these lines + * also have sl_nlin and sl_ncol properly set. + * Note that sl_line and sl_col are NOT updated or changed, because + * the physical screen has not been altered! + * + * Returns 0 if no physical screen updates are needed (other than + * cursor moving and mode line updating). + * Returns 1 if screen updates are needed; RD_UPDWIN is set in w_redp, + * indicating that UPD_WIND should be called. + */ + +fix_wind (win) +struct window *win; +{ + register struct window *w; + register int i; + register struct scr_line *s; + chroff cdot, bdelta, updot, sdot, newz; + chroff savdot; + struct buffer *savbuf; + int bot, nlmod, savi, contf, ocontf, randomflg; + int newpct; + + if(!(w = win)) + return(0); + if(!(w->w_redp&RDS_DOFIX)) /* Anything we need to do? */ + return(0); /* Nope, just ignore */ + + /* Find current dot for this window, and set up other stuff */ + cdot = (w == cur_win) ? cur_dot : w->w_dot; + bot = w->w_pos + w->w_ht; + savbuf = cur_buf; + cur_buf = w->w_buf; + savdot = e_dot(); + nlmod = 0; /* No screen image changes so far */ + + /* Dot (ie cursor) is before current top? If so, must move + * backwards to find a new topldot. Note also that buffer may have + * changed so that either cdot or topldot points past EOF. + */ + if(w->w_topldot > cdot) + { /* Yes, must search backwards scrht/3 screen lines */ + /* from cdot in order to find topldot. */ + /* Don't bother updating scr stuff beforehand since we'll + * have to revise everything anyway and can do it on the fly. + */ + i = (ev_mvpct * w->w_ht) / 100; + goto skipdn; + + finddn: i = ((100 - ev_mvpct) * w->w_ht) / 100; + skipdn: if(i <= 0) i = 1; /* Ensure # is reasonable */ + else if(i >= w->w_ht) i = w->w_ht-1; + e_go(cdot); /* Start here (may normalize to EOF)*/ + d_backup(i ? i : 1); /* Try to back up cleverly */ + w->w_topldot = e_dot(); + randomflg = 0; /* We have some idea where we are */ + fixall: /* Entry point for later recheck, with randomflg==1 */ + newz = e_blen(); + if(newz < cdot) /* Part of buf may have gone away */ + { /* So normalize dot to EOF */ + w->w_dot = cdot = newz; + if(w == cur_win) /* Special check for fixing */ + cur_dot = newz; /* up cur_dot too! */ + goto finddn; /* and get a new top-of-window loc */ + } + retry: i = w->w_pos; + contf = 0; + s = 0; + for(; i < bot; i++) + { nlmod++; + fix_line(scr[i], s); /* s = 0 the first time */ + s = scr[i]; +#if FX_SOWIND + if(w->w_flags & W_STANDOUT) + s->sl_flg |= SL_NSO; + else s->sl_flg &= ~SL_NSO; +#endif + } + if(inwinp(w,cdot)) /* Ensure in window */ + goto mdone; + if(randomflg) /* If jumped randomly, */ + { i = (ev_nwpct * w->w_ht) / 100; + goto skipdn; /* Try to select new window */ + } + + /* We tried to back up and went too far. */ + if(cdot < w->w_topldot) /* Verify place is ahead */ + { errbarf("fix_wind failed"); /* Didn't back up?? */ + goto finddn; + } + /* Move down one line and try again */ + if(w->w_ht > 1) + w->w_topldot = scr[w->w_pos+1]->sl_boff; + else + { s = scr[w->w_pos]; + w->w_topldot = s->sl_boff + s->sl_len; + } + e_go(w->w_topldot); + goto retry; + } + + /* At some future point, could separate out processing for + * RD_WINRES and RD_FIXWIN. Latter flag implies only w_topldot + * has changed (new window selected). Former implies whole + * buffer has been munged, and everything is completely redone. + */ + if(w->w_redp&(RD_WINRES|RD_FIXWIN)) /* If re-figuring whole window */ + { e_go(w->w_topldot); /* Start here, and */ + randomflg = 1; /* set up flag saying random jump */ + goto fixall; /* and go crunch all lines. */ + } + if((w->w_redp&RD_TMOD)==0) /* If claims no text mods, */ + { if(inwinp(w,cdot)==0) /* Just verify cursor loc. */ + goto finddn; /* Sigh.... */ + newz = w->w_oldz; /* Win, set up for exit. */ + goto done; + } + /* Here only when RD_TMOD is set, indicating changes are + * between range variables. + */ + /* Find upper bound of any mods. This is a little gross in the + * speed dept and some faster way should perhaps be devised. + * In particular the main loop should incrementally keep track of + * buffer size, and should set a flag RD_TEXT if anything has + * actually been changed. Edit routines should have lots of + * flags available to tell main loop more precisely what they did, + * so main loop can take care of updating b/emod and stuff. + */ + if((newz = e_blen()) == 0) + goto finddn; /* Ensure blank window is cleared */ + bdelta = newz - w->w_oldz; + if((updot = newz) > w->w_emod) + updot -= w->w_emod; + if(bdelta == 0 && (updot == w->w_bmod)) + goto inwinq; + + /* Could also check for updot < w_topldot (changes above win) + * or sl_boff+sl_len < w_bmod (changes below win) but those + * cases are probably pretty rare. + */ + /* First find line where changes start */ + for(i = w->w_pos; i < bot; i++) + { s = scr[i]; + if(w->w_bmod <= s->sl_boff) /* Changes prior to this? */ + break; + } + if(i >= bot) /* Test last line specially */ + { if(w->w_bmod > (s->sl_boff + (chroff)s->sl_len)) + goto inwinq; /* Outside window */ + /* Last line changed, hack it */ + } + if(i > w->w_pos /* If we have a prev line */ + && (s->sl_len == 0 /* and we're at EOF, */ + || w->w_bmod != s->sl_boff /* or not at start of line */ + || scr[i-1]->sl_cont)) /* or prev line is continuation */ + s = scr[--i]; /* then it's prev line we want */ + + /* I has index for screen line changes begin on; S has ptr. + * This piece of code handles case where buffer has been modified + * starting at BMOD, and BDELTA chars have been inserted/deleted; + * range of changes ends at UPDOT. + */ + savi = i; + while(++i < bot) + scr[i]->sl_boff += bdelta; + i = savi; + + /* Now start with 1st changed line and start figuring new line + * lengths. Stop when hit end, or past updot and boff is correct + * for start of line. + */ + /* can improve this by jumping out when past emod, and testing for + * an EOL - then know stuff has to match someplace, so look for that. + * could then simply update lengths or something? + */ + if(i > w->w_pos) /* Find # cols already there from prev line*/ + contf = scr[i-1]->sl_cont; + else contf = 0; + ocontf = 1; /* Fake it so always update 1st line*/ + e_go(sdot = s->sl_boff); + for(; i < bot; i++) + { s = scr[i]; + if(updot <= sdot /* If past changed stuff */ + && sdot == s->sl_boff /* and locs are lined up */ + && contf == 0 /* and previous line clean */ + && ocontf == 0) /* (both old and new images) */ + break; /* Then done. */ + nlmod++; + ocontf = s->sl_cont; /* Save old-image contf value */ + fix_line(s, (i > w->w_pos) ? scr[i-1] : 0); +#if FX_SOWIND + if(w->w_flags & W_STANDOUT) + s->sl_flg |= SL_NSO; + else s->sl_flg &= ~SL_NSO; +#endif + sdot = e_dot(); + contf = s->sl_cont; /* Get new-image contf value */ + } + if(inwinp(w,cdot)) /* OK, screen fixed, see if cursor inside */ + goto mdone; + goto finddn; + + /* Test if still in window and dispatch appropriately */ +inwinq: if(inwinp(w,cdot)) + goto done; + else goto finddn; + + /* Come here when done, after mods made to window. + * Calculate new %-of-buffer position for window's view, and + * see if it's changed from current %. + */ +mdone: if(w != cur_win) goto done; /* If not current window, ignore */ + s = scr[bot-1]; + if((s->sl_boff + (chroff)s->sl_len) >= newz) + if(w->w_topldot) newpct = 150; /* BOT */ + else newpct = 200; /* ALL */ + else if(w->w_topldot == 0) + newpct = -1; /* TOP */ + else /* NOTE: This won't work if topldot is huge */ + newpct = (w->w_topldot*100)/newz; /* nn% */ + if(newpct != w->w_pct) /* OK, now compare with old % */ + { w->w_pct = newpct; /* Different, must set and */ + redp(RD_MODE); /* invoke redisplay of mode line! */ + } + +done: w->w_bmod = -1; /* To indicate vars not set */ + w->w_oldz = newz; + w->w_redp &= ~RDS_DOFIX; /* Clear flags that invoked us */ + if(nlmod) + w->w_redp |= RD_UPDWIN; /* Say stuff to be updated */ + e_go(savdot); + cur_buf = savbuf; + return(nlmod); +} + +/* INWINP - Returns true if given dot is inside given window. + */ +inwinp(win,cdot) +struct window *win; +chroff cdot; +{ register struct scr_line *s; + register struct window *w; + chroff sdot; + + w = win; + if(cdot < w->w_topldot) + return(0); + s = scr[(w->w_pos + w->w_ht) - 1]; + sdot = s->sl_boff + (chroff)s->sl_len; + if(cdot < sdot) + return(1); /* Yup, inside window. */ + if(cdot > sdot) + return(0); + + /* Dot is exactly at end of window, must check further. */ + if(s->sl_len /* If line exists, */ + && ((s->sl_flg&SL_EOL) /* and ends in LF, */ + || s->sl_cont > 1)) /* or sl_cont > 1, lose. */ + return(0); + return(1); /* Else inside, win. */ +} + +/* + * UPD_WIND + * If argument 0, assumes cur_win and DOESN'T interrupt if input + * detected. + */ + +upd_wind(win) +struct window *win; +{ register int i, n; + register struct scr_line *s; + struct window *w; + int top, bot, dspf, num, isave, noicost, nodcost, iline, dline; +#if FX_SOWIND + int oldso; +#endif +#if IMAGEN + int origdspf; + char redpmsg[128]; +#endif /*IMAGEN*/ + + if((w=win)==0) + w = cur_win; + dspf = w->w_redp; /* Get update flags for window */ +#if IMAGEN + origdspf = dspf; +#endif /*IMAGEN*/ + if(w == cur_win) /* If updating current window, */ + dspf |= rd_type; /* merge in global flags */ + if((dspf &= RDS_WINFLGS) == 0) /* Well, it might happen sometimes */ + goto zdone; + w->w_redp = dspf; + if(dspf&(RD_WINRES|RD_TMOD|RD_MOVE|RD_FIXWIN)) + { fix_wind(w); /* May set some flags, so */ + dspf = w->w_redp; /* get them back... */ + } + if((dspf&RD_UPDWIN)==0) /* Must ask for update! */ + goto zdone; +#if IMAGEN + if (dbg_redp) + { sprintf(redpmsg, + "buffer: %14s, rd_type: %06o, w_redp: %06o, dspf: %06o", + w->w_buf->b_name, rd_type, origdspf, dspf); + barf2(redpmsg); + } +#endif /*IMAGEN*/ + + /* Assume screen structure set up by FIX_WIND, just go + * effect change for every line modified. + */ +#if FX_SOWIND + oldso = t_dostandout((w->w_flags&W_STANDOUT)? 1:0); +#endif + top = w->w_pos; + bot = top + w->w_ht; + for(i = top; i < bot; ++i) + if((s = scr[i])->sl_flg&SL_MOD) + { if(win && tinwait()) /* If OK, stop if any chars typed */ + { tbufls(); + w->w_redp = dspf; +#if FX_SOWIND + t_dostandout(oldso); +#endif + return(1); /* Return immediately, say int'd */ + } + if(slineq(s,s)) /* Compare old with new */ + goto ldone; /* Lines equal, no update needed */ + +#if IMAGEN + /* If hint says redo entirely */ + if (dspf & RD_REDO) + { s->sl_flg |= SL_REDO; /* Do "fast update" */ + goto nodel; /* Just go update line */ + } +#endif /*IMAGEN*/ + if((trm_flags&TF_IDLIN)==0) + goto nodel; /* Just go update line */ + + + /* Check for I/D line. If no hints exist, check for both + * insert and delete. + */ + if((dspf&(RD_ILIN|RD_DLIN))==0) + dspf |= RD_ILIN|RD_DLIN; + noicost = 0; + nodcost = 0; + + /* Check for insert line. See if the current old screen + * line is duplicated among any of the new lines which + * follow it. If a match is found, keep looking and add + * up the number of characters in the matching lines. + */ + if(dspf&RD_ILIN) + { + /* See if this old screen line is needed elsewhere */ + if(s->sl_col == 0) /* Ignore if blank */ + goto noins; + + for(n = i+1; n < bot; n++) + { if((scr[n]->sl_flg&SL_MOD)==0) + break; + if(slineq(s, scr[n])) /* Old, new */ + { if(!noicost) iline = n; /* 1st time */ + noicost += s->sl_col; + s++; + } + else if(noicost) break; + } + if(!noicost) /* If no match, forget it */ + goto noins; /* S will not have changed. */ + s = scr[i]; /* Restore S */ + n = iline; /* Have matches, get index + * of first matching line */ + + /* Heuristic to decide whether to perform + * insert-line operation. Kind of stupid, but + * good enough for now. + */ + num = (n-i)*(tvc_ldn+tvc_lin) + (tvc_li + tvc_ld); + if((n-i) >= (scr_ht-(ECHOLINES+3)) + /* Don't move lines all the + * way down full screen! */ + || num >= noicost) /* Compare cost with estimated + * cost of not doing insert.*/ + goto noins; + + /* Insert lines! */ + dspf &= ~RD_ILIN; + inslin(i, n - i, w); + for(; i < n; i++) /* Update intervening lines */ + upd_line (i); + goto ldone; + } +noins: + + /* Check for delete line. See if the new screen line + * is duplicated among any of the old lines already on + * the screen. If a match is found, keep looking and add + * up the number of characters in the matching lines. + */ + if(dspf&RD_DLIN) + { + /* See if the new line already exists elsewhere */ + if(s->sl_ncol == 0) /* Ignore blank lines */ + goto nodel; + for (n = i + 1; n < bot; n++) + { if((scr[n]->sl_flg&SL_MOD)==0) + break; + if(slineq(scr[n],s)) /* Old, new */ + { if(!nodcost) dline = n; /* 1st time */ + nodcost += s->sl_ncol; + s++; + } + else if(nodcost) break; + } + if(!nodcost) /* If no match, forget it */ + goto nodel; /* S will not have changed. */ + s = scr[i]; /* Restore S */ + n = dline; /* Index of 1st match */ + + /* Heuristic to decide whether to perform + * delete-line operation. Same hack as for + * insert-line. + */ + num = (n-i)*(tvc_ldn+tvc_lin) + (tvc_li + tvc_ld); + if((n-i) >= (scr_ht-(ECHOLINES+3)) + || num >= nodcost) + goto nodel; + + /* Delete lines! */ + dspf &= ~RD_DLIN; + dellin(i, n - i, w); + goto ldone; + } +nodel: + /* All failed, so just update line */ + upd_line(i); +ldone: s->sl_flg &= ~SL_MOD; /* Clear mod flag */ + } +done: +#if FX_SOWIND + t_dostandout(oldso); /* Back to previous mode */ +#endif +zdone: w->w_redp = 0; + return(0); /* Say completed */ +} + + +/* + * SLINEQ - Compare old, new screen image lines. If new line doesn't + * have the modified flag set, use its old image. + * If the standout mode differs, always fails. + */ + +slineq(olds, news) +struct scr_line *olds; +struct scr_line *news; +{ register char *cpo, *cpn; + register int cnt; + + cpo = (char *)news; + if(((struct scr_line *)cpo)->sl_flg&SL_MOD) + { cnt = ((struct scr_line *)cpo)->sl_ncol; + cpn = ((struct scr_line *)cpo)->sl_nlin; +#if FX_SOWIND /* Mode of old must match mode of new */ + if(((olds->sl_flg & SL_CSO)==0) != + ((((struct scr_line *)cpo)->sl_flg & SL_NSO)==0)) + return 0; +#endif + } + else + { cnt = ((struct scr_line *)cpo)->sl_col; + cpn = ((struct scr_line *)cpo)->sl_line; +#if FX_SOWIND /* Modes of current lines must match */ + if((olds->sl_flg & SL_CSO) != + (((struct scr_line *)cpo)->sl_flg & SL_CSO)) + return 0; +#endif + } + + /* Crufty match stuff */ + if(cnt != olds->sl_col) + return(0); + if(cnt) + { cpo = olds->sl_line; + do { if(*cpo++ != *cpn++) + return(0); + } while(--cnt); + } + return(1); +} + +/* UPD_LINE(lineno) - Effects the update of a physical screen line, + * assuming that the screen line structure for that line has been + * properly set up by fix_wind. It cannot be interrupted by typein. + * Does a lot of work to check out optimization for char I/D. + * Someday it could also check out the possibility of doing a CLEOL at + * some point to reduce the number of spaces that need to be output. + */ + +upd_line(y) +int y; +{ register i; + register char *sci, *cp; + struct scr_line *s; + + int xpos; /* actual screen position */ + int c, c2, p2, cmpcost, delcost; + int savc, ocol, ncol; + char *savcp, *savsci; +#if FX_SOWIND + int oldso, newso; + int writall = 0; +#endif + + s = scr[y]; + savsci = sci = s->sl_line; /* What is currently on the screen */ +#if IMAGEN + if (s->sl_flg & SL_REDO) + { /* Check for line-redo flag */ + s->sl_flg &= ~SL_REDO; /* Clear it: we are handling it */ + writall = 1; /* Re-do this line completely */ + t_move(y, 0); + t_docleol(); + s->sl_col = 0; + } +#endif /*IMAGEN*/ + +#if FX_SOWIND + /* See whether modes of the lines are the same or not. */ + newso = (s->sl_flg & SL_NSO)!=0; /* Get new mode (true if SO)*/ + if(((s->sl_flg & SL_CSO)!=0) != newso) + { t_move(y, 0); /* Not same, must zap existing line */ + t_docleol(); + s->sl_col = 0; + writall = newso; /* Output all if SO is new mode */ + } + oldso = t_dostandout(newso); /* Get in right mode */ +#endif + + ocol = s->sl_col; + savcp = cp = s->sl_nlin; + ncol = s->sl_ncol; + + /* Find leading equalness */ + i = ocol; + if(i > ncol) i = ncol; /* Use minimum count */ + if(i) + { do { if(*cp++ != *sci++) + { --cp; + break; + } + } while(--i); + i = cp - savcp; + sci = savsci; /* Restore ptr to beg of cur line */ + } + + /* From here on, "i" is now the x-coordinate (column addr) + * of the first position that doesn't match. "cp" points to + * the first nonmatching char in the new line image. + */ +#if COHERENT /* Has direct video interface capability */ + if(trm_flags&TF_DIRVID) + { if(ncol < ocol) + { /* Flesh out new line to completely replace old */ + fillsp(&s->sl_nlin[ncol], ocol-ncol); + ncol = ocol; + } + /* Spit out changed stuff. t_direct will handle the + * case where i == ncol (ie no changes needed). + */ + t_direct(y,i,cp,ncol-i); + goto done; + } +#endif /*COHERENT*/ + + if(i == ncol) /* Matched up to end of new line? */ + goto idone; /* Yes, can skip big loop! */ + +#if FX_SOWIND + if(writall) /* If simply writing everything...*/ + { t_move(y, 0); + tputn(cp, ncol); /* Output them all */ + curs_col = ncol; /* Update cursor position */ + goto idone; /* then wrap up! */ + } +#endif + + /* Now must fill out remainder of old line with blanks. */ + if(ocol < scr_wid) + { +#if FX_SOWIND + if(newso) fillset(&sci[ocol], scr_wid-ocol, 0); + else +#endif + fillsp(&sci[ocol],scr_wid-ocol); /* Fill out */ + } + + /****** Main update loop. ******/ + for (; i < ncol; i++) + { c = *cp++; /* Note *CP will point to next */ + if(c == sci[i]) + continue; + if(i >= ocol) /* Past EOL of old line? */ + { +putin: sci[i] = c; + if(y != curs_lin || i != curs_col) + t_move(y, i); + tput(c); + curs_col++; + continue; + } + + if((trm_flags&TF_IDCHR)==0) /* Replace */ + goto putin; + + /* Do checking to see whether char I/D operations should + * be invoked. This code is quite CPU intensive and + * can cause noticeable pauses if run on a slow CPU with + * a fast (9600) terminal line. The optimization tradeoff + * seems worthwhile most of the time, however. + */ + cmpcost = 0; /* Default is don't compare */ + if(ncol == ocol) /* If line lengths same, must chk */ + { +/* if(ncol >= scr_wid) */ /* If line overrun, compare */ + cmpcost++; + } +#if 0 +If ncol == ocol, have problem with tabs: + If don''t use I/D char, but tabs exist, lots of wasteful update. + If DO use I/D char, and no tabs exist, potential for mistakenly + using I/D when didn''t have to. Not too bad, though? + If DO use I/D char, then mild screw when inserting/deleting + just before a tab, since could have just overwritten, + but I/D insists on jerking things around. + Insert test: + If old char was space, replace? Problem: will cause cursor + jump if really should have shifted a long run of spaces. + But that is probably okay. + Delete test: + If new char is space, replace? again, will cause cursor jump + with long run of spaces. +#endif /*COMMENT*/ + + if(ncol < ocol || cmpcost) /* Try delete-char */ + { + /* Search old for match of c and nextc */ +dodel: savc = c; + if(i >= ncol-1) + goto putin; + c2 = *cp; + if(c == SP && ncol == ocol) + goto tryins; + p2 = i; + for(;;) + { if(c == sci[i] && c2 == sci[i+1]) + break; + if(++i < ocol) + continue; + i = p2; + if(cmpcost) {cmpcost = 0; goto tryins;} + goto putin; + } + /* Find # chars that match (i.e. will be saved) */ + for(c=1; (i+c < ncol) && (sci[i+c] == cp[c-1]); c++); + delcost = tvc_cd + tvc_cdn*(i - p2); + if(delcost >= c) + { c = savc; + i = p2; + if(cmpcost) { cmpcost = 0; goto tryins;} + goto putin; /* Punt */ + } + if(cmpcost) + { c = savc; i = p2; + goto tryins; + } + t_move(y, p2); + c = i - p2; /* Find # chars to flush */ + strncpy(&sci[p2],&sci[i], ocol-i); + ocol -= c; + fillsp(&sci[ocol], c); + i = p2; /* Restore i */ + t_delchr(c); /* Flush this many cols */ + continue; + } + + /* Try ins-char */ + /* Search new for match of i and i+1 */ + /* Note this cannot be used while in standout mode, since + ** the new spaces created will probably be in the wrong mode. + */ +tryins: +#if FX_SOWIND + if(newso) goto putin; +#endif + if(i+1 >= ocol) + goto putin; + + savc = c; + savcp = cp; + c2 = sci[i+1]; + if(sci[i] == SP && ncol == ocol) + goto putin; + xpos = i; /* save current col */ + i++; + for(;;) + { if(i >= ncol) goto puntx; + c = *cp++; +inlp2: if(c != sci[xpos]) + { if(i > scr_wid) goto puntx; + i++; + continue; + } + if(i >= ncol) goto puntx; + c = *cp++; + if(c != c2) + { i++; /* Allow for previous c */ + goto inlp2; /* which is always 1 */ + } + break; + } + if(i >= scr_wid) goto puntx; + + /* Find how many chars match (i.e. will be saved) */ + for(c = 2; xpos+c < ncol && sci[xpos+c] == *cp++; c++); + if((p2 = tvc_ci + tvc_cin*(i - xpos)) >= c) + goto puntx; /* Not worth it... */ + if(cmpcost && p2 >= delcost) + goto puntx; /* Do delchr instead */ + + /* We've decided to insert some chars! */ + i -= xpos; /* Get # char positions to insert */ + cp = savcp; /* Get ptr to newline string */ + --cp; /* Point at 1st char to insert */ + /* Make room in scr array */ + inspc(&sci[xpos], + &sci[(ocol+i >= scr_wid) ? scr_wid-i : ocol], i); + ocol += i; /* Update size of old line */ + strncpy(&sci[xpos], cp, i); /* Copy all inserted chars */ + + t_move(y, xpos); /* Now ensure in right place */ + t_inschr(i, cp); /* and insert string onto screen! */ + + cp += i; /* Update source ptr */ + cp++; /* Point to next char */ + i += xpos; + continue; /* Now continue loop! */ + + puntx: i = xpos; + c = savc; + cp = savcp; + if(cmpcost) { cmpcost = 0; goto dodel;} + goto putin; + } + + /* All done putting up new stuff. Now see if any remaining old + ** stuff needs to be cleared from end of line. + */ +idone: if(i < ocol) /* if still have text to right, */ + { t_move(y,i); /* move there */ + t_docleol(); /* and clear old stuff. */ + } + +done: s->sl_line = s->sl_nlin; /* Replace old image by new */ + s->sl_col = s->sl_ncol; + s->sl_nlin = sci; + s->sl_flg &= ~SL_MOD; +#if FX_SOWIND /* Copy standout mode to current */ + if(newso) s->sl_flg |= SL_CSO; + else s->sl_flg &= ~SL_CSO; +#endif +} + +#if FX_SOWIND +fillset(str,cnt,c) +char *str; +int cnt; +int c; +{ register int n; + register char *cp; + if((n = cnt) <= 0) return; + cp = str; + do{ *cp++ = c; + } while(--n); +} +#endif + +fillsp(str,cnt) +char *str; +int cnt; +{ register int n; + register char *cp; + if((n = cnt) <= 0) return; + cp = str; + do{ *cp++ = SP; + } while(--n); +} +inspc(cp0, cpl, cnt) +char *cp0, *cpl; +int cnt; +{ register char *cp, *cp2; + register n; + if((n = cnt) <= 0) return; + cp = cpl; /* ptr to last+1 char in string */ + cp2 = cp+n; /* ptr to loc+1 to move to */ + n = cp - cp0; /* # chars to move */ + do *--cp2 = *--cp; + while(--n); + n = cnt; /* Now fill gap with spaces */ + do *cp++ = SP; + while(--n); +} + +/* FIX_LINE - Fixes up new screen image for a single line. Does not + * do any actual terminal I/O, and does not change the old screen + * image. Assumes that previous line (if any is furnished) has + * already been properly set up. + */ + +int sctreol = 0; /* Ugly crock for talking to sctrin() */ + /* 0 = no EOL seen, 1 = EOL seen, -1 = EOF seen */ +fix_line(slp, olds) +struct scr_line *slp; +struct scr_line *olds; +{ register struct scr_line *s; + register int col, scrw; + char *cp; + int ch; + + col = 0; + scrw = scr_wid; + cp = slp->sl_nlin; + if((s = olds) && (col = s->sl_cont)) + { if(--col) + strncpy(cp, (s->sl_flg&SL_MOD) ? + &s->sl_nlin[scrw] + : &s->sl_line[scrw], col); + cp += col; + } + scrw--; /* Note now using scr_wd0 !! */ + s = slp; + s->sl_boff = e_dot(); + col = sctrin(cp, scrw, col); + if (col < scrw || sctreol) /* Does line need continuation mark? */ + s->sl_cont = 0; /* No, say no cont chars */ + else { + /* Yes, find # cols of overflow. If not 0, must be > 0 */ + /* and char is a biggie. Make room for continuation chars */ + if(col -= scrw) + inspc(&s->sl_nlin[scrw],&s->sl_nlin[scrw+col], 1); + s->sl_cont = col+1; /* # cont chars, plus 1 */ + s->sl_nlin[scrw] = CI_CLINE; /* Display "contin" mark */ + col = scrw+1; + } + + s->sl_ncol = col; + s->sl_len = e_dot() - s->sl_boff; + s->sl_flg |= (SL_MOD|SL_EOL); /* Say new, and assume line has EOL */ + if(sctreol <= 0) /* unless it doesn't really */ + s->sl_flg &= ~SL_EOL; /* in which case turn off flag */ + return; +} + +/* SCTRIN - auxiliary for FIX_LINE. + * lim - # cols chars are allowed to use + * ccol - current column (0 = bol) + * Returns when see EOL or EOF, or + * when all columns have been filled up. Retval-ccol = # overflow. + * Note that any overflow is indivisible (i.e. a char with a + * multi-col representation is responsible for the overflow). + * So, overflow = 0 means next char would be in 1st non-ex column + * and overflow > 0 means last char read has extra columns, but + * it did start within bounds. + */ +sctrin(to, lim, ccol) +char *to; +int lim; +int ccol; +{ register SBBUF *sb; + register col, cnt; + + sb = (SBBUF *) cur_buf; + col = ccol; + sctreol = 0; /* No EOL or EOF seen */ + do + { cnt = sb_getc(sb); + if(cnt == EOF) + { --sctreol; /* Say EOF seen! */ + return(col); + } +#if FX_EOLMODE + if(cnt == CR) /* Possible EOL? */ + { if(eolcrlf(sb)) + { if((cnt = sb_getc(sb)) == LF) /* Real EOL? */ + { sctreol++; + return col; /* Yes, return */ + } + /* Stray CR, back up & fall thru */ + if(cnt != EOF) + sb_backc(sb); + cnt = CR; /* Show stray CR */ + } + } else if (cnt == LF) + { if(!eolcrlf(sb)) /* Real EOL? */ + { sctreol++; + return col; /* Yes, return */ + } + /* If EOL mode is CRLF then hitting a LF + ** can only happen for stray LFs (the + ** previous check for CR takes care of + ** CRLFs, and we never start scanning + ** from the middle of a CRLF. + ** Drop thru to show stray LF. + */ + } +#else + if(cnt == LF) + { sctreol++; /* Say EOL seen */ + return col; + } +#endif /*_FX_EOLMODE*/ + cnt = sctr(cnt, to, col); + to += cnt; + col += cnt; + } while(col < lim); + + /* If we're stopping because last char put us precisely at the + ** end of the line, make a further check to see whether an EOL + ** is next. If so, we can include that in the line since it + ** doesn't need any more columns for representation! + */ + if (col == lim) /* If stopping exactly at edge of screen */ + switch (sb_getc(sb)) /* Check out next char */ + { case EOF: + --sctreol; /* Yes, note EOF seen */ + break; /* and can return immed */ +#if FX_EOLMODE + case CR: /* Possible EOL? */ + if(eolcrlf(sb)) + { if((cnt = sb_getc(sb)) == LF) /* Real EOL? */ + { sctreol++; /* Yes, set flag */ + break; /* and return */ + } + /* Stray CR, back up & fall thru */ + if(cnt != EOF) /* Back up char that */ + sb_backc(sb); /* came after the CR */ + sb_rgetc(sb); /* Then back over CR */ + break; + } + sb_backc(sb); + break; + case LF: + if(!eolcrlf(sb)) /* Real EOL? */ + { sctreol++; /* Yes, set flag */ + break; /* and return */ + } + /* If EOL mode is CRLF then hitting a LF + ** can only happen for stray LFs (the + ** previous check for CR takes care of + ** CRLFs, and we never start scanning + ** from the middle of a CRLF. + ** Drop thru into default to back up over LF. + */ +#else + case LF: + sctreol++; /* Say EOL seen */ + break; /* and return */ +#endif /*-FX_EOLMODE*/ + default: + sb_backc(sb); /* Back up over random char */ + break; + } + return(col); +} + +/* SCTR - Screen Char TRanslation routine. +** This routine is completely responsible for the way a buffer char is +** displayed on the screen. Given a char and the current column position, +** it stores the representation using the given pointer and returns +** the number of chars (columns) used by the representation. +** Normal printing chars (plus space) are simply themselves. +** TAB is a variable number of spaces depending on the column pos. +** (we use standard tabstops of 8) +** All control chars are uparrow followed by a printing char. +** e.g. ctrl-A = ^A +** This includes ESC which is ^[. +** DEL is shown as ^?. +** Chars with the 8th bit set have the prefix CI_META (currently ~) and +** the rest of the representation is as above (except for TAB). +** Chars with the 9th bit set have the prefix CI_TOP (currently |) and +** the rest of the representation is as above (except for TAB). +** This only exists for systems with 9-bit chars such as TOPS-20. +*/ + +static int +sctr(ch, to, ccol) +int ch; /* Buffer char to translate */ +char *to; /* Place to deposit translation in */ +int ccol; /* Current column position */ +{ register char *cp; + register c, n; + + c = ch; + if(037 < c && c < 0177) /* Most common case */ + { *to = c; + return(1); + } + cp = to; + if(c == TAB) /* Next most common case */ + { n = 010 - (ccol&07); /* Tab stops are every 8 cols */ + ccol = n; /* Save value */ + do *cp++ = SP; + while (--n); + return(ccol); + } + ccol = 1; /* Re-use var */ +#if TOPS20 + if(c&0400) /* 9th bit set? */ + { *cp++ = CI_TOP; + ccol++; + } +#endif /*TOPS20*/ + if(c&0200) + { *cp++ = CI_META; + ccol++; + } + if((c &= 0177) <= 037 || c == 0177) + { *cp++ = CI_CNTRL; + c ^= 0100; /* Transform cntrl char */ + ccol++; + } + *cp = c; + return(ccol); +} + +/* INSLIN(line, N, wind) - Insert lines + * DELLIN(line, N, wind) - Delete lines + * Both routines insert/delete N lines at "line" in window "wind" + * and update the screen image accordingly. + */ + +inslin (line, n, win) +int line; /* line number to insert BEFORE */ +int n; /* number of lines to insert */ +struct window *win; /* window we are in */ +{ register int i; + register int bot; + register char **savp; + char *savscr[MAXHT]; + + bot = win -> w_ht + win -> w_pos; + t_curpos (line, 0); + t_inslin (n, bot); /* do the insertion on the screen */ + savp = &savscr[0]; + for (i = 1; i <= n; i++) /* free lines that fall off-screen */ + *savp++ = scr[bot - i]->sl_line; + + for (i = bot - 1; i >= line + n; i--) /* move down lines */ + { scr[i]->sl_line = scr[i - n]->sl_line; /* below the insertion */ + scr[i]->sl_col = scr[i - n]->sl_col; + } + savp = &savscr[0]; + for (i = line + n - 1; i >= line; i--) + /* blank lines where inserted */ + { scr[i]->sl_line = *savp++; + scr[i]->sl_col = 0; + } + for(i = line; i < bot; ++i) + scr[i]->sl_flg |= SL_MOD; +} + +dellin (line, n, win) +int line; /* first line to be deleted */ +int n; /* number of lines to be deleted */ +struct window *win; /* window we are in */ +{ register int i; + register int bot; + register char **savp; + char *savscr[MAXHT]; + + bot = win -> w_ht + win -> w_pos; + + t_curpos (line, 0); + t_dellin (n, bot); /* do the deletion on the screen */ + savp = &savscr[0]; + for (i = line; i < line + n; i++) /* free the deleted lines */ + *savp++ = scr[i]->sl_line; + for (i = line; i < bot - n; i++) /* move lines up to fill */ + { scr[i]->sl_line = scr[i + n]->sl_line; /* deleted spaces */ + scr[i]->sl_col = scr[i + n]->sl_col; + } + + savp = &savscr[0]; + for (i = bot - n; i < bot; i++) /* blank lines at bottom */ + { scr[i]->sl_line = *savp++; + scr[i]->sl_col = 0; + } + for(i = line; i < bot; ++i) + scr[i]->sl_flg |= SL_MOD; +} + +/* T_ Terminal functions - these are similar to the terminal-dependent + * routines in EETERM (which they call) but rely on some knowledge of + * the screen image in order to do their job cleverly. + */ + +#if FX_SOWIND + +/* T_DOSTANDOUT(on) - Turn standout mode on or off, cleverly. +** Returns previous state. +*/ +static int curso = 0; /* Current state (initially off) */ +int +t_dostandout(on) +int on; +{ + int oldso; + + if ((oldso = curso) != on) /* If desired state doesn't match, */ + { t_standout(on); /* invoke new state. */ + curso = on; + } + return oldso; +} +#endif + + +t_move(y,x) +register int y,x; +{ register int d; + + if(y != curs_lin) /* No vertical smarts yet */ + { t_curpos(y, x); + return; + } + if((d = (x - curs_col)) >= 0) /* Find diff in column position */ + { if(d == 0) return; /* If none, nothing to do! */ + + /* Moving right. If distance is less than abs-move cost, + * do clever right-move by copying screen image */ + if(d < tvc_pos) +#if FX_SOWIND /* Ensure not in standout mode */ + if((scr[y]->sl_flg&(SL_CSO|SL_NSO))==0) +#endif + { + tputn(&scr[y]->sl_line[curs_col], d); + curs_col = x; + return; + } + } + /* Moving to left, try to do clever left-move by backspacing + * instead of using abs move. + */ + else if((d = -d)*tvc_bs < tvc_pos) + { do { t_backspace(); + } while(--d); + return; + } + /* No luck with cleverness, just move. */ + t_curpos(y, x); +} + +t_docleol() +{ register struct scr_line *s; + register int cnt, ocol; + + if(trm_flags&TF_CLEOL) t_cleol(); /* Winning */ + else /* Losing */ + { s = scr[curs_lin]; + if((cnt = s->sl_col - curs_col) > 0) + { +#if FX_SOWIND + int oldso = t_dostandout(0); +#endif + ocol = curs_col; + do { tput(SP); curs_col++; + } while(--cnt); +#if FX_SOWIND + t_dostandout(oldso); +#endif + t_move(curs_lin, ocol); + } + } +} + diff --git a/commands/elle/eeedit.c b/commands/elle/eeedit.c new file mode 100755 index 000000000..bda677814 --- /dev/null +++ b/commands/elle/eeedit.c @@ -0,0 +1,717 @@ +/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* EEEDIT - E-type routines */ + +#include "elle.h" + +/* E_ - Operate on cur_buf. Do not change value of cur_dot unless + * unavoidable side effect (also e_setcur). + * EX_ - Like E_ but take SB ptr value. Never touch cur_dot. + * ED_ - Like E_, operate on cur_buf, update cur_dot and display stuff. + * D_ - Perform necessary display update for given operations. + * + * Note that "dot" refers to the current read/write pointer for a sbbuffer. + * The name comes from EMACS/TECO where "." represents this value. + */ + +#define CURSBB (SBBUF *)cur_buf /* Shorthand for current SB buffer */ + +e_reset() /* Reset current buffer */ +{ ex_reset(CURSBB); + cur_dot = 0; +} + +/* Basic functions - apply SB routines to current buffer. + * There is some optimization here which knows that certain SB functions + * are macros. + */ +e_rgetc() /* Read/move 1 char backward */ +{ return(sb_rgetc((CURSBB))); +} +e_rdelc() /* Delete 1 char backward */ +{ return(sb_rdelc((CURSBB))); +} +e_delc() /* Delete 1 char forward */ +{ return(sb_deln(CURSBB,(chroff)1)); +} +e_getc() /* Read/move 1 char forward */ +{ register SBBUF *sb; + sb = CURSBB; /* Macro: use reg */ + return(sb_getc(sb)); +} +e_backc() /* Move 1 char backward */ +{ register SBBUF *sb; + sb = CURSBB; /* Macro: use reg */ + sb_backc(sb); /* No value returned */ +} +e_putc(c) /* Insert/write 1 char forward */ +char c; +{ register SBBUF *sb; + sb = CURSBB; /* Macro: use reg */ + return(sb_putc(sb, c)); +} +e_peekc() /* Read 1 char forward (no move) */ +{ register SBBUF *sb; + sb = CURSBB; /* Macro: use reg */ + return(sb_peekc(sb)); +} +e_ovwc(ch) /* Overwrite 1 char forward */ +char ch; +{ + sb_setovw(CURSBB); /* Turn on overwrite mode */ + e_putc(ch); + sb_clrovw(CURSBB); /* Turn off overwrite mode */ +} + +SBSTR * +e_copyn(off) /* Copy N chars forward/backward, return SD to sbstring */ +chroff off; +{ return(sb_cpyn(CURSBB,off)); +} +e_deln(off) /* Delete N chars forward/backward */ +chroff off; +{ return(sb_deln(CURSBB, off)); +} + +/* E_SETCUR() - set cur_dot to current position (dot). This is the only + * E_ routine that mungs cur_dot except for e_reset. + */ +e_setcur() +{ cur_dot = e_dot(); +} +e_gosetcur(dot) /* Go to specified dot and set cur_dot as well */ +chroff dot; +{ sb_seek(CURSBB,dot,0); + e_setcur(); /* Not cur_dot = dot since want canonicalization */ +} + +/* E_GO(dot) - Move to specified location. */ +/* These "GO" routines all move to the location specified, returning + * 0 if successful and -1 on error. "cur_dot" is never changed, + * with the exception of e_gosetcur. + * Note that other "GO" routines (eg E_GONL) will return 1 if successful + * and 0 if stopped by EOF. + */ + +e_gocur() { return(e_go(cur_dot)); } /* Move to cur_dot */ +e_gobob() { return(e_go((chroff) 0)); } /* Move to Beg Of Buffer */ +e_goeob() { return(sb_seek(CURSBB,(chroff)0,2)); } /* Move to End Of Buffer */ +e_go(dot) /* Move to specified location. */ +chroff dot; +{ return(sb_seek(CURSBB,dot,0)); +} +e_igoff(ioff) /* Move (int) N chars forward/backward */ +int ioff; +{ return(sb_seek(CURSBB,(chroff)ioff,1)); +} + +e_goff(off) /* Move (full) N chars forward/backward */ +chroff off; +{ return(sb_seek(CURSBB,off,1)); +} + +int ex_gonl(), ex_gopl(), ex_gobol(), ex_goeol(); + +e_gobol() { return(ex_gobol(CURSBB)); } /* Move to beg of this line */ +e_goeol() { return(ex_goeol(CURSBB)); } /* Move to end of this line */ +e_gonl() { return(ex_gonl(CURSBB)); } /* Move to beg of next line */ +e_gopl() { return(ex_gopl(CURSBB)); } /* Move to beg of prev line */ + + +/* E_DOT() - Return current value of dot. */ +chroff e_dot() { return(sb_tell(CURSBB)); } /* Current pos */ +chroff e_nldot() { return(e_alldot(CURSBB,ex_gonl)); } /* Beg of next line */ +chroff e_pldot() { return(e_alldot(CURSBB,ex_gopl)); } /* Beg of prev line */ +chroff e_boldot(){ return(e_alldot(CURSBB,ex_gobol));} /* Beg of this line */ +chroff e_eoldot(){ return(e_alldot(CURSBB,ex_goeol));} /* End of this line */ + +chroff +e_alldot(sbp,rtn) /* Auxiliary for above stuff */ +SBBUF *sbp; +int (*rtn)(); +{ return(ex_alldot(sbp,rtn,e_dot())); +} + +/* E_BLEN - Return length of current buffer */ +chroff +e_blen() { return(ex_blen(CURSBB)); } + +/* EX_ routines - similar to E_ but take a buffer/sbbuf argument + * instead of assuming current buffer. + */ + +/* EX_RESET - Reset a given buffer */ +ex_reset(b) +struct buffer *b; +{ sbs_del(sb_close((SBBUF *)b)); + sb_open((SBBUF *)b,(SBSTR *)0); +} + +ex_go(sbp,loc) /* Move to given dot in specified sbbuf */ +chroff loc; +SBBUF *sbp; +{ return(sb_seek(sbp,loc,0)); +} + +chroff +ex_dot(sbp) /* Return current position in specified sbbuf */ +SBBUF *sbp; +{ + return(sb_tell(sbp)); +} + + +chroff +ex_boldot(sbp,dot) /* Return dot for BOL of specified sbbuf */ +SBBUF *sbp; +chroff dot; +{ return(ex_alldot(sbp,ex_gobol,dot)); +} + +chroff +ex_alldot(sbp,rtn,dot) /* Auxiliary for some e_ stuff */ +SBBUF *sbp; +int (*rtn)(); +chroff dot; +{ register SBBUF *sb; + chroff savloc, retloc; + + savloc = sb_tell(sb = sbp); + sb_seek(sb,dot,0); + (*rtn)(sb); + retloc = sb_tell(sb); + sb_seek(sb,savloc,0); + return(retloc); +} + +/* GO (forward) to Next Line of specified sbbuf - returns 0 if stopped at EOF + * before an EOL is seen. */ +ex_gonl(sbp) +SBBUF *sbp; +{ register SBBUF *sb; + register int c; + sb = sbp; +#if FX_EOLMODE + if(eolcrlf(sb)) + while((c = sb_getc(sb)) != EOF) + { if(c == LF) /* Possible EOL? */ + { sb_backc(sb); /* See if prev char was CR */ + if((c = sb_rgetc(sb)) != EOF) + sb_getc(sb); + sb_getc(sb); /* Must restore position */ + if(c == CR) /* Now test for CR */ + return(1); /* Won, CR-LF! */ + } + } + else +#endif + while((c = sb_getc(sb)) != EOF) + if(c == LF) + return(1); + return(0); +} + +/* GO (forward) to End Of Line of specified sbbuf - returns 0 if stopped at + * EOF before an EOL is seen. */ +ex_goeol(sbp) +SBBUF *sbp; +{ register SBBUF *sb; + register int c; + sb = sbp; +#if FX_EOLMODE + if(eolcrlf(sb)) + while((c = sb_getc(sb)) != EOF) + { if(c == LF) /* Possible EOL? */ + { sb_backc(sb); /* See if prev char was CR */ + if((c = sb_rgetc(sb)) == CR) + return(1); /* Won, CR-LF! */ + if(c != EOF) /* No, must restore position */ + sb_getc(sb); /* Skip over */ + sb_getc(sb); /* Skip over LF */ + } + } + else +#endif + while((c = sb_getc(sb)) != EOF) + if(c == LF) + { sb_backc(sb); + return(1); + } + return(0); +} + +/* GO (backward) to Beg Of Line of specified sbbuf - returns 0 if stopped + * at EOF + */ +ex_gobol(sbp) +SBBUF *sbp; +{ register SBBUF *sb; + register int c; + sb = sbp; +#if FX_EOLMODE + if(eolcrlf(sb)) + while((c = sb_rgetc(sb)) != EOF) + { if(c == LF) /* Possible EOL? */ + { if((c = sb_rgetc(sb)) == CR) + { sb_getc(sb); /* Won, CR-LF! */ + sb_getc(sb); /* Move back */ + return(1); + } + if(c != EOF) /* No, must restore position */ + sb_getc(sb); /* Undo the rgetc */ + } + } + else +#endif + while((c = sb_rgetc(sb)) != EOF) + if(c == LF) + { sb_getc(sb); + return(1); + } + return(0); +} + +/* GO (backward) to Previous Line of specified sbbuf - returns 0 if stopped + * at EOF before an EOL is seen (i.e. if already on 1st line of buffer) + */ +ex_gopl(sbp) +SBBUF *sbp; +{ register SBBUF *sb; + register int c; + sb = sbp; +#if FX_EOLMODE + if(eolcrlf(sb)) + while((c = sb_rgetc(sb)) != EOF) + { if(c == LF) /* Possible EOL? */ + { if((c = sb_rgetc(sb)) == CR) + { ex_gobol(sb); + return(1); /* Won! */ + } + if(c != EOF) /* No, must restore position */ + sb_getc(sb); /* Undo the rgetc */ + } + } + else +#endif + while((c = sb_rgetc(sb)) != EOF) + if(c == LF) + { ex_gobol(sb); + return(1); /* Won! */ + } + return(0); +} + + +chroff +ex_blen(sbp) /* Return length of specified sbbuf */ +SBBUF *sbp; +{ + return(sb_tell(sbp)+sb_ztell(sbp)); +} + +/* Miscellaneous stuff */ + +/* E_GOFWSP() - Forward over whitespace */ +e_gofwsp() +{ register int c; + while((c = e_getc()) == SP || c == TAB); + if(c != EOF) e_backc(); +} + +/* E_GOBWSP() - Backward over whitespace */ +e_gobwsp() +{ register int c; + while((c = e_rgetc()) == SP || c == TAB); + if(c != EOF) e_getc(); +} + + +/* E_GOLINE(n) - Goes N lines forward (or backward). +** If N == 0, goes to beginning of current line. +** Returns 0 if hit EOF. +*/ +e_goline(i) +register int i; +{ + if(i > 0) + { do { if(!e_gonl()) return(0); } + while(--i); + } + else if(i < 0) + { i = -i; + do { if(!e_gopl()) return(0); } + while(--i); + } + else e_gobol(); /* arg of 0 */ + return 1; +} + +/* E_LBLANKP() - Returns true if all characters in rest of line are blank. + * Moves to beginning of next line as side effect, unless fails. + */ +e_lblankp() +{ register int c; + for(;;) switch(e_getc()) + { + case SP: + case TAB: + continue; + case LF: /* Normally drop thru to return 1 as for EOF */ +#if FX_EOLMODE + if(eolcrlf(cur_buf)) + { e_rgetc(); + if((c = e_rgetc()) != EOF) /* Get prev char */ + e_getc(); + e_getc(); + if(c != CR) /* Now examine */ + continue; /* Not CR-LF, go on */ + } /* Else drop thru to return win */ +#endif + case EOF: + return(1); + default: + return(0); + } + /* Never drops out */ +} + + +e_insn(ch, cnt) +int ch; +int cnt; +{ register int i; + if((i = cnt) > 0) + do { e_putc(ch); + } while(--i); +} + +e_sputz(acp) +char *acp; +{ register SBBUF *sb; + register char *cp; + register int c; + if(cp = acp) + { sb = CURSBB; + while(c = *cp++) + sb_putc(sb,c); + } +} + +/* BOLEQ - Returns TRUE if 2 dots are on the same line + * (i.e. have the same Beg-Of-Line) + */ +boleq(dot1,dot2) +chroff dot1,dot2; +{ return( (ex_boldot(CURSBB,dot1) == ex_boldot(CURSBB,dot2))); +} + + +char * +dottoa(str,val) +char *str; +chroff val; +{ register char *s; + + s = str; + if(val < 0) + { *s++ = '-'; + val = -val; + } + if(val >= 10) + s = dottoa(s, val/10); + *s++ = '0' + (int)(val%10); + *s = 0; + return(s); +} + + +/* Paragraph utilities */ + +#if FX_FPARA || FX_BPARA || FX_MRKPARA || FX_FILLPARA + +#if FX_SFPREF +extern char *fill_prefix; /* Defined in eefill.c for now */ +extern int fill_plen; /* Ditto */ +#endif /*FX_SFPREF*/ + +#if ICONOGRAPHICS +int para_mode = PARABLOCK; /* eexcmd.c only other file that refs this */ +#endif /*ICONOGRAPHICS*/ + + +/* Go to beginning of paragraph */ +e_gobpa() +{ register int c; + chroff savdot; + + savdot = e_dot(); + e_bwsp(); + while((c = e_rgetc()) != EOF) + if(c == LF) /* Went past line? */ + { e_getc(); /* Back up and check */ +#if FX_SFPREF + if(fill_plen) + if(tstfillp(fill_plen)) + { e_igoff(-(fill_plen+1)); + continue; + } + else break; +#endif /*FX_SFPREF*/ +#if ICONOGRAPHICS + c = e_peekc (); + + if (para_mode == PARABLOCK) + if (c == LF) + break; + + if (para_mode == PARALINE) + if (c_wsp (c)) + break; +#else + if(c_pwsp(e_peekc())) /* Check 1st chr for wsp */ + break; /* If wsp, done */ +#endif /*ICONOGRAPHICS*/ + e_rgetc(); /* Nope, continue */ + } + if((c = e_peekc()) == '.' || c == '-') + { e_gonl(); + if(e_dot() >= savdot) + e_gopl(); + } +} + +/* Go to end of paragraph */ +e_goepa() +{ register int c; + + e_gobol(); /* First go to beg of cur line */ + e_fwsp(); + while((c = e_getc()) != EOF) + if (c == LF) + { +#if FX_SFPREF + if(fill_plen) /* If Fill Prefix is defined */ + if(tstfillp(fill_plen)) /* then must start with it */ + continue; + else break; /* or else we're done */ +#endif /*FX_SFPREF*/ +#if ICONOGRAPHICS + if (para_mode == PARABLOCK) + if (e_peekc () == LF) + break; + + if (para_mode == PARALINE) + if (c_wsp (e_peekc ())) + break; +#else + if(c_pwsp(e_peekc())) + break; +#endif /*-ICONOGRAPHICS*/ + } +} + +exp_do(rpos, rneg) +int (*rpos)(), (*rneg)(); +{ register int e; + register int (*rtn)(); + + if((e = exp) == 0) + return; + rtn = rpos; + if(e < 0) + { rtn = rneg; + e = -e; + } + do { (*rtn)(); + } while(--e); +} + + +e_fwsp() +{ register int c; + while(c_wsp(c = e_getc())); + if(c != EOF) e_backc(); +} +e_bwsp() +{ register int c; + while(c_wsp(c = e_rgetc())); + if(c != EOF) e_getc(); +} + + +c_wsp(ch) +int ch; +{ register int c; + c = ch; + if(c == SP || c == TAB || c == LF || c == FF) + return(1); + return(0); +} +c_pwsp(ch) +int ch; +{ register int c; + c = ch; + if(c == '.' || c == '-') + return(1); + return(c_wsp(c)); +} + +#endif /* FX_FPARA || FX_BPARA || FX_MRKPARA || FX_FILLPARA */ + +/* Word function auxiliaries */ + +/* Returns true if this character is a delimiter. */ +delimp(c) +int c; +{ static int delim_tab[] = + { + 0177777, 0177777, /* All controls */ + 0177777, 0176000, /* All punct but 0-9 */ + 0000001, 0074000, /* All punct but A-Z and _ */ + 0000001, 0174000 /* All punct but a-z */ + }; + return (delim_tab[c >> 4] & (1 << (c & 017))); +} + +e_wding(adot,n) +register chroff *adot; +int n; +{ chroff savdot; + savdot = e_dot(); + e_gowd(n); + *adot = e_dot(); + e_go(savdot); + if(*adot == savdot) + { ring_bell(); + return(0); + } + return(1); +} +chroff +e_wdot(dot,n) +chroff dot; +int n; +{ chroff savdot, retdot; + savdot = e_dot(); + e_go(dot); + e_gowd(n); + retdot = e_dot(); + e_go(savdot); + return(retdot); +} +e_gowd(n) +int n; +{ register int (*gch)(), c, cnt; + int e_getc(), e_rgetc(); + chroff ret_dot; + + if((cnt = n) == 0) + return; + if(cnt > 0) + gch = e_getc; /* Forward routine */ + else + { gch = e_rgetc; /* Backward routine */ + cnt = -cnt; + } + do + { ret_dot = e_dot(); /* Remember dot for last word found */ + while((c = (*gch)()) != EOF && delimp(c)); + if(c == EOF) + { e_go(ret_dot); /* Use last word found */ + break; + } + while((c = (*gch)()) != EOF && !delimp(c)); + if(c == EOF) + break; + if(n < 0) e_getc(); else e_backc(); + } while(--cnt); +} + +/* Searching */ + +e_search(mstr,mlen,backwards) +char *mstr; +int mlen; +int backwards; +{ register SBBUF *sb; + register char *cp; + register int c; + char *savcp; + int cnt, scnt; +#if IMAGEN + register int c1; + register int caseless = (cur_buf->b_flags & B_TEXTMODE); +#endif /*IMAGEN*/ + + sb = (SBBUF *) cur_buf; + if (!backwards) + { /* Search forwards */ + sfwd: cp = mstr; + while((c = sb_getc(sb)) != EOF) + { +#if IMAGEN + if((!caseless && c != *cp) || + (caseless && upcase(c) != upcase(*cp))) continue; +#else + if(c != *cp) continue; +#endif /*-IMAGEN*/ + cp++; + cnt = mlen; + while(--cnt > 0) + { +#if IMAGEN + c1 = *cp++; + c = e_getc(); + if ((!caseless && c1 != c) || + (caseless && upcase(c1) != upcase(c))) +#else + if(*cp++ != (c = e_getc())) +#endif /*-IMAGEN*/ + { if(c == EOF) return(0); + sb_seek(sb,(chroff)(cnt-mlen),1); + goto sfwd; + } + } + return(1); + } + } + else + { /* Search backwards */ + scnt = mlen - 1; + savcp = mstr + scnt; /* Point to end of string */ + + sbck: cp = savcp; + while((c = sb_rgetc(sb)) != EOF) + { +#if IMAGEN + if((!caseless && c != *cp) || + (caseless && upcase(c) != upcase(*cp))) continue; +#else + if(c != *cp) continue; +#endif /*-IMAGEN*/ + cp--; + if((cnt = scnt) == 0) return(1); + do + { +#if IMAGEN + c1 = *cp--; + c = e_rgetc(); + if ((!caseless && c1 != c) || + (caseless && upcase(c1) != upcase(c))) +#else + if(*cp-- != (c = e_rgetc())) +#endif /*-IMAGEN*/ + { if(c == EOF) return(0); + sb_seek(sb,(chroff)(mlen-cnt),1); + goto sbck; + } + } + while(--cnt); + return(1); + } + } + return(0); /* Failed */ +} diff --git a/commands/elle/eeerr.c b/commands/elle/eeerr.c new file mode 100755 index 000000000..aa471ca03 --- /dev/null +++ b/commands/elle/eeerr.c @@ -0,0 +1,183 @@ +/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* EEERR - Error handling & testing routines + */ + +#include "elle.h" + +#if V6 +#include "eesigs.h" +#else +#include +#endif + +/* EFUN: "Hit Breakpoint" */ +f_bkpt() +{ clean_exit(); + bpt(); + set_tty(); +} +bpt() {} /* Put a DDT/ADB breakpoint here */ + +#if !(STRERROR) /* If strerror() not supported, we provide it. */ +extern int sys_nerr; /* Max index into sys_errlist */ +extern char *sys_errlist[]; + +char * +strerror(num) +int num; +{ + static char badbuf[30]; + if (num > 0 && num <= sys_nerr) + return (sys_errlist[num]); + sprintf(badbuf, "unknown error %d", num); + return badbuf; +} +#endif /* -STRERROR */ + + +errsbm(type,adr,str,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12) +register int type; /* Type, flags */ +int (*adr)(); /* Addr called from */ +char *str; /* Printf string */ +{ register struct buffer *b; + int oldttystate; + + oldttystate = clean_exit(); /* Ensure not in editing mode */ + if(type == SBFERR) /* File overwrite error? A0 is FD */ + { printf("WARNING - FILE CORRUPTED!\nBuffers affected:\n"); + for(b = buf_head; b; b = b->b_next) + { if(sb_fdinp((SBBUF *)b, a0)) + printf((b->b_fn ? " %s: %s\n" : " %s\n"), + b->b_name, b->b_fn); + } + if (oldttystate > 0) set_tty(); + return(1); /* Try to continue normally */ + } + printf("%sERR: %o ", (type ? "SBX" : "SBM"), adr); + printf(str,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12); + askerr(); +} + +/* + * Bite_bag -- Try to save our rear ends after a catastrophe. + * This routine is mainly called from "interrupt" + * level when a memory fault or bus error occurs. + * We try to save the buffer to the file "ELLE.crash" + * in the current working directory. If it loses, well + * then you have really lost. Note: this routine does + * not reset the appropriate signal handler, so it is + * never re-entered. If a fault repeats once in this + * code, then the world dies. + */ + +bite_bag(fault) /* We come here on any memory error */ +int fault; +{ + int ostate; + /* Some systems, such as BSD4.x and SUN, do not reset caught signals + * to SIG_DFL. + * This is a win, but isn't what vanilla UNIX code expects. + * Since it doesn't hurt to do it explicitly, we always turn it off + * explicitly... + */ + signal(fault, SIG_DFL); /* Reinstate default handling */ + + ostate = clean_exit(); /* Fix up the terminal modes first! */ + printf("ELLE stopped by fatal interrupt (%d)!\n\ +Type S or W to try saving your work.\n",fault); + askerr(); + if(ostate > 0) set_tty(); + signal(fault, bite_bag); /* If continued, re-enable signal */ +} + +/* HUP_EXIT - Called by a SIGHUP hangup signal. + * Tries to save all modified buffers before exiting. + * Note that the TTY is not touched at all, although the terminal mode + * flag is set just in case further error handling routines are invoked. + */ +hup_exit() +{ extern int trm_mode; /* See e_disp.c */ + + trm_mode = -1; /* Say TTY is now detached */ + saveworld((struct buffer *)0, 0); /* Save world, w/o feedback */ + exit(1); +} + +errint() /* Routine provided for ADB jumps */ +{ askerr(); +} +char askh1[] = "\ +A - Abort process\n\ +B - Breakpoint (must have \"bpt:b\" set in ADB)\n\ +C - Continue\n\ +D - Diagnostic command mode\n"; +char askh2[] = "\ +S - Try to save current buffer\n\ +W - Try to save world (all modified buffers)\n"; + +int bsaving = 0; /* Set while in middle of saving buffer(s) */ + +askerr() +{ register struct buffer *b; + char linbuf[100]; + char *asklin(); + extern int (*funtab[])(); /* In E_CMDS.C */ + int ostate; + + ostate = clean_exit(); /* Clean up TTY if not already done */ +reask: + printf("(A,B,C,D,S,W,?)"); + switch(upcase(*asklin(linbuf))) + { + case '?': + writez(1,askh1); /* Too long for &$@! printf */ + writez(1,askh2); /* Too long for &$@! V6 C */ + break; /* optimizer (/lib/c2) */ + case 'A': + abort(); + break; + case 'B': + bpt(); + break; + case 'Q': + case 'C': + goto done; + case 'D': + if(funtab[FN_DEBUG]) + (*funtab[FN_DEBUG])(-1); + else printf("Sorry, no diagnostics\n"); + break; + case 'S': /* Try to save current buffer only */ + b = cur_buf; + goto savb; + case 'W': /* Try to save all modified buffers */ + b = 0; + savb: if(bsaving++) + { printf("Already saving -- continued"); + goto done; + } + saveworld(b, 1); /* Save, with feedback */ + bsaving = 0; + break; + } + goto reask; +done: + if(ostate > 0) + set_tty(); +} + +char * +asklin(acp) +char *acp; +{ register char *cp; + register int c; + cp = acp; + while((c = tgetc()) != LF) + *cp++ = c; + *cp++ = 0; + return(acp); +} diff --git a/commands/elle/eef1.c b/commands/elle/eef1.c new file mode 100755 index 000000000..ee493fcce --- /dev/null +++ b/commands/elle/eef1.c @@ -0,0 +1,278 @@ +/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* + * EEF1 Various functions + * Char move/ins/del + * Case change + * Char/word transpose + */ + +#include "elle.h" + +/* EFUN: "Insert Self" */ +f_insself (c) +int c; +{ +#if IMAGEN + fim_insself(c); +#else +#if FX_FILLMODE + extern int fill_mode; + + if(fill_mode) fx_insfill(c); + else +#endif /*FX_FILLMODE*/ + ed_insn(c, exp); /* Normal stuff */ +#endif /*-IMAGEN*/ +} + +/* EFUN: "Quoted Insert" +** Inserts next char directly, number of times. +** Does not check anything about the char and does not do anything +** depending on the mode. In particular, CR is not turned into EOL. +*/ +f_quotins() +{ + ed_insn(cmd_read(), exp); /* Insert next char directly */ +} + +#if FX_CRLF +/* EFUN: "CRLF" */ +f_crlf() +{ +#if IMAGEN + fim_crlf(); +#else + register int i; + + if(e_goeol() == cur_dot /* If at end of current line */ + && exp == 1 /* and inserting only 1 new line */ + && e_lblankp() && e_lblankp()) /* and next 2 lines blank */ + { e_gocur(); /* Then just re-use next line. */ + e_gonl(); /* Go to its start */ + e_setcur(); /* and establish cur_dot there. */ + ed_delete(e_dot(), e_eoldot()); /* Ensure any blanks flushed */ + } + else + { e_gocur(); /* Otherwise back to original place */ + if((i = exp) > 0) /* and simply insert newlines */ + do ed_crins(); + while(--i); + } +#endif /*-IMAGEN*/ +} +#endif /*FX_CRLF*/ + +/* EFUN: "Forward Character" */ +f_fchar() +{ ed_igoff(exp); +} + +/* EFUN: "Backward Character" */ +f_bchar() +{ ed_igoff(-exp); +} + +/* EFUN: "Delete Character" */ +f_dchar () +{ +#if IMAGEN + fim_dchar(); +#else + ef_deln(exp); +#endif /*-IMAGEN*/ +} + +/* EFUN: "Backward Delete Character" */ +f_bdchar () +{ +#if IMAGEN + fim_bdchar(); +#else + ef_deln(-exp); +#endif /*-IMAGEN*/ +} + +/* Delete forward or backward N characters. + * If arg, kills instead of deleting. + */ +ef_deln(x) +int x; +{ + e_igoff(x); + if(exp_p) ed_kill(cur_dot, e_dot()); + else ed_delete(cur_dot, e_dot()); +} + +#if FX_DELSPC +/* EFUN: "Delete Horizontal Space" */ +/* Delete spaces/tabs around point. + */ +f_delspc() +{ chroff dot1; + + e_gobwsp(); /* Move backward over whitespace */ + dot1 = e_dot(); /* Save point */ + e_gofwsp(); /* Move forward over whitespace */ + ed_delete(dot1,e_dot()); /* Delete twixt start and here */ +} +#endif /*FX_DELSPC*/ + +#if FX_TCHARS +/* EFUN: "Transpose Characters" + * Transpose chars before and after cursor. Doesn't hack args yet. + * EMACS: With positive arg, exchs chars before & after cursor, moves right, + * and repeats the specified # of times, dragging the char to the + * left of the cursor right. + * With negative arg, transposes 2 chars to left of cursor, moves + * between them, and repeats the specified # of times, exactly undoing + * the positive arg form. With zero arg, transposes chars at point + * and mark. + * HOWEVER: at the end of a line, with no arg, the preceding 2 chars + * are transposed. + */ +f_tchars() +{ register int c, c2; +#if IMAGEN + c = e_rgetc(); /* Gosmacs style: twiddle prev 2 */ + if (c == EOF) + return(e_gocur()); /* Do nothing at beginning of bfr */ +#else + + if((c = e_getc()) == EOF /* If at EOF */ + || e_rgetc() == LF) /* or at end of line, */ + c = e_rgetc(); /* use preceding 2 chars */ +#endif /*-IMAGEN*/ + + if((c2 = e_rgetc()) == EOF) /* At beginning of buffer? */ + return(e_gocur()); /* Yes, do nothing */ + e_ovwc(c); + e_ovwc(c2); + e_setcur(); + buf_tmod((chroff)-2); /* Munged these 2 chars */ +} +#endif /*FX_TCHARS*/ + +#if FX_FWORD +/* EFUN: "Forward Word" */ +f_fword() +{ chroff retdot; + if(e_wding(&retdot, exp)) + ed_go(retdot); +} +#endif + +#if FX_BWORD +/* EFUN: "Backward Word" */ +f_bword() +{ exp = -exp; + f_fword(); +} +#endif + +#if FX_KWORD +/* EFUN: "Kill Word" */ +f_kword() +{ chroff retdot; + + if(e_wding(&retdot,exp)) + { ed_kill(cur_dot,retdot); + this_cmd = KILLCMD; + } +} +#endif + +#if FX_BKWORD +/* EFUN: "Backward Kill Word" */ +f_bkword() +{ exp = -exp; + f_kword(); +} +#endif + +#if FX_TWORDS +/* EFUN: "Transpose Words" */ +/* Transpose word. Urk! + */ +f_twords() +{ register SBSTR *sd1, *sd2; + register SBBUF *sb; + chroff begwd1, endwd1, begwd2, endwd2; + + endwd2 = e_wdot(cur_dot, 1); /* Go to end of 2nd word */ + begwd2 = e_wdot(endwd2, -1); /* Go to beg of 2nd word */ + if(begwd2 >= endwd2) /* If no 2nd word, punt. */ + return; + begwd1 = e_wdot(begwd2, -1); /* Go to beg of 1st word */ + endwd1 = e_wdot(begwd1, 1); /* Go to end of 1st word */ + if(begwd1 >= endwd1) /* If no 1st word, punt. */ + return; + if(endwd1 > begwd2) /* Avoid possible overlap */ + return; + + e_go(begwd2); + sb = (SBBUF *)cur_buf; + sd2 = sb_killn(sb, endwd2 - begwd2); /* Excise wd 2 first */ + e_go(begwd1); + sd1 = sb_killn(sb, endwd1 - begwd1); /* Excise wd 1 */ + sb_sins(sb, sd2); /* Replace with wd 2 */ + e_goff(begwd2 - endwd1); /* Move past between stuff */ + sb_sins(sb, sd1); /* Insert wd 1 */ + e_setcur(); + buf_tmat(begwd1); /* Modified this range */ +} +#endif /*FX_TWORDS*/ + +/* Case hacking functions and support */ + +#if FX_UCWORD +/* EFUN: "Uppercase Word" */ +f_ucword() +{ case_word(0); +} +#endif /*FX_UCWORD*/ + +#if FX_LCWORD +/* EFUN: "Lowercase Word" */ +f_lcword() +{ case_word(1); +} +#endif /*FX_LCWORD*/ + +#if FX_UCIWORD +/* EFUN: "Uppercase Initial" */ +f_uciword() +{ case_word(2); +} +#endif /*FX_UCIWORD*/ + +#if FX_UCWORD||FX_LCWORD||FX_UCIWORD +case_word (downp) +{ chroff retdot; +#if IMAGEN + chroff startdot; + + /* Normalize our position to beginning of "current" word, + * where "current" is defined to be the current word we are in, + * or else the previous word if we are not in any word. + * All this silly nonsense just to perpetuate Gosmacs's + * wrong behaviour! + */ + startdot = cur_dot; /* Save current position */ + e_getc(); /* If at beg of word, ensure we get inside it */ + e_gowd(-1); /* Go to start of this or prev word */ + e_setcur(); /* Set cur_dot */ +#endif /*IMAGEN*/ + + if(e_wding(&retdot, exp)) + { ed_case(cur_dot,retdot,downp); + e_gosetcur(retdot); + } +#if IMAGEN + e_gosetcur(startdot); +#endif /*IMAGEN*/ +} +#endif /* any case_word caller */ diff --git a/commands/elle/eef2.c b/commands/elle/eef2.c new file mode 100755 index 000000000..ee9d3cc86 --- /dev/null +++ b/commands/elle/eef2.c @@ -0,0 +1,311 @@ +/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* + * EEF2 Various functions + */ + +#include "elle.h" + +/* Line Handling functions */ + +/* EFUN: "Beginning of Line" */ +f_begline() +{ e_gobol(); + ed_setcur(); +} + +/* EFUN: "End of Line" */ +f_endline() +{ e_goeol(); + ed_setcur(); +} + +/* EFUN: "Next Line" */ +/* Goes to beginning of next line */ +f_nxtline() +{ return(down_bline(exp)); +} + +/* EFUN: "Previous Line" */ +/* Goes to beginning of previous line */ +f_prvline() +{ return(down_bline(-exp)); +} + +/* EFUN: "Down Real Line" */ +f_dnrline () +{ down_line(exp); +} + +/* EFUN: "Up Real Line" */ +f_uprline () +{ down_line(-exp); +} + +#if FX_OLINE +/* EFUN: "Open Line" */ +f_oline() +{ register int i; + chroff savdot; + + savdot = cur_dot; + if((i = exp) > 0) + do { ed_crins(); } + while(--i); + e_gosetcur(savdot); +} +#endif /*FX_OLINE*/ + +#if FX_DELBLINES +/* EFUN: "Delete Blank Lines" */ +/* Delete blank lines around point. + */ +f_delblines() +{ register int c; + chroff dot1, dot2, oldcur; + + oldcur = cur_dot; + do { e_gobwsp(); } + while ((c = e_rgetc()) == LF); + if (c != EOF) + e_gonl(); + dot1 = e_dot(); + if(dot1 > oldcur) return; + do { e_gofwsp(); } + while ((c = e_getc()) == LF); + if(c != EOF) + e_gobol(); + dot2 = e_dot(); + if(dot2 < oldcur) return; + ed_delete(dot1,dot2); +} +#endif /*FX_DELBLINES*/ + +#if FX_KLINE +/* EFUN: "Kill Line" */ +f_kline() +{ + if(exp_p) + e_goline(exp); /* Move that many lines */ + /* (if 0, goes to BOL) */ + else /* No arg, handle specially */ + { if(e_lblankp()) /* Is rest of line blank? */ + ; /* Yes, now at next line! */ + else e_goeol(); /* No, go to EOL rather than NL */ + } + ed_kill(cur_dot,e_dot()); + e_setcur(); + this_cmd = KILLCMD; +} +#endif /*FX_KLINE*/ + +#if FX_BKLINE +/* EFUN: "Backward Kill Line" (not EMACS) */ +/* Originally an Iconographics function. +*/ +f_bkline() +{ + if(exp_p) exp = -exp; /* If arg, invert it */ + else + { exp = 0; /* No arg, furnish 0 */ + exp_p = 1; + } + f_kline(); /* Invoke "Kill Line" */ +} +#endif /*FX_BKLINE*/ + +#if FX_GOLINE +/* EFUN: "Goto Line" (not EMACS) (GNU goto-line) */ +f_goline() +{ + e_gobob(); + down_bline(exp-1); /* already at line 1 */ +} +#endif /*FX_GOLINE*/ + +down_bline(arg) +int arg; +{ + if(arg) + e_goline(arg); + ed_setcur(); +} + +#if FX_DNRLINE || FX_UPRLINE +down_line (x) +int x; +{ register int i, res; + + res = x ? e_goline(x) : 1; /* Move that many lines */ + goal = 0; + if(res == 0) /* Hit buffer limits (EOF)? */ + { if(x > 0) /* Moving downwards? */ + { +#if !(IMAGEN) /* If IMAGEN, do not extend!! */ + if(x == 1) ed_crins(); /* Yeah, maybe extend */ + else +#endif + goal = indtion(cur_dot); + goto done; + } + } + + if(last_cmd == LINECMD /* If previous cmd also a line move */ + && pgoal != -1) /* and we have a previous goal col */ + goal = pgoal; /* then make it the current goal */ + else goal = indtion(cur_dot); /* Else invent goal from current pos */ + + i = inindex(e_dot(), goal); /* See # chars needed to reach goal */ + if(i == -1) /* If off edge of line, */ + e_goeol(); /* just move to end of this line */ + else e_igoff(i); /* else move to goal. */ + +done: pgoal = goal; + this_cmd = LINECMD; + ed_setcur(); +} +#endif /*FX_DNRLINE || FX_UPRLINE*/ + + + +/* Region Handling functions */ + +/* EFUN: "Set/Pop Mark" */ +f_setmark() +{ + mark_dot = e_dot(); + mark_p = 1; + if(ev_markshow) /* If have one, show indicator */ + saytoo(ev_markshow); /* that mark was set. */ +} + +/* EFUN: "Exchange Point and Mark" */ +f_exchmark() +{ chroff tmpdot; + + if(chkmark()) + { tmpdot = mark_dot; + mark_dot = cur_dot; + ed_go(tmpdot); /* Set cur_dot and go there */ + } +} + +/* EFUN: "Kill Region" */ +f_kregion() +{ + if(chkmark()) + { ed_kill(cur_dot,mark_dot); /* Will adj cur_dot, mark_dot */ + e_gocur(); + this_cmd = KILLCMD; + } +} + +/* EFUN: "Copy Region" */ +f_copreg() +{ + if(chkmark()) + { e_gocur(); + kill_push(e_copyn(mark_dot - cur_dot)); + e_gocur(); + } +} + + +/* EFUN: "Uppercase Region" */ +f_ucreg() +{ ef_creg(0); +} + +/* EFUN: "Lowercase Region" */ +f_lcreg() +{ ef_creg(1); +} + +ef_creg(downp) +int downp; +{ + if(chkmark()) + ed_case(cur_dot,mark_dot,downp); +} + +#if FX_FILLREG +/* EFUN: "Fill Region" */ +f_fillreg() +{ if(chkmark()) + ed_fill(mark_dot,cur_dot,0); +} +#endif /*FX_FILLREG*/ + +/* CHKMARK() - minor utility for region-hacking functions. + * Returns TRUE if mark exists. + * Otherwise complains to user and returns 0. + */ +chkmark() +{ if(mark_p == 0) + ding("No mark!"); + return(mark_p); +} + +/* Paragraph functions */ + +#if FX_FPARA +/* EFUN: "Forward Paragraph" */ +f_fpara() +{ int e_gobpa(), e_goepa(); + + exp_do(e_goepa, e_gobpa); + ed_setcur(); +} +#endif /*FX_FPARA*/ + +#if FX_BPARA +/* EFUN: "Backward Paragraph" */ +/* Go to beginning of paragraph. + * Skip all whitespace until text seen, then stop at beginning of + * 1st line starting with whitespace. + */ +f_bpara() +{ int e_gobpa(), e_goepa(); + + exp_do(e_gobpa, e_goepa); + ed_setcur(); +} +#endif /*FX_BPARA*/ + +#if FX_MRKPARA +/* EFUN: "Mark Paragraph" */ +f_mrkpara() +{ + f_fpara(); /* Go to end of paragraph */ + f_setmark(); /* Put mark there */ + f_bpara(); /* Then back to start of paragraph */ +} +#endif /*FX_MRKPARA*/ + +#if FX_FILLPARA +/* EFUN: "Fill Paragraph" */ +f_fillpara() +{ + chroff savloc, endloc; + + savloc = cur_dot; +#if ICONOGRAPHICS + e_getc(); /* DON'T go to next para if at end */ + e_gobpa(); /* of this one!! */ +#endif /*ICONOGRAPHICS*/ + e_goepa(); /* Go to end of parag */ + if(e_rgetc() != LF) /* If last char EOL, back over it. */ + e_getc(); + endloc = e_dot(); /* Remember where end is */ + e_gobpa(); /* Go to beg of parag */ +#if ICONOGRAPHICS + kill_push(e_copyn(endloc - e_dot ())); + /* put old version on kill ring */ +#endif /*ICONOGRAPHICS*/ + e_fwsp(); /* Move over initial whitespace */ + ed_fill(e_dot(), endloc, 0); /* Fill this region, return to dot */ +} +#endif /*FX_FILLPARA*/ diff --git a/commands/elle/eef3.c b/commands/elle/eef3.c new file mode 100755 index 000000000..bbf8a193d --- /dev/null +++ b/commands/elle/eef3.c @@ -0,0 +1,282 @@ +/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* + * EEF3 Various Functions (Yanking, Indentation, miscellaneous) + */ + +#include "elle.h" + +#if FX_APPNKILL +/* EFUN: "Append Next Kill" */ +f_appnkill() +{ this_cmd = KILLCMD; /* Fake out next call to ed_kill */ +} +#endif /*FX_APPNKILL*/ + +#if FX_UNKILL +/* EFUN: "Un-kill" */ +f_unkill() +{ register SBSTR *sd; + + if((sd = kill_ring[kill_ptr]) == 0) + { ring_bell(); + return; + } + mark_dot = cur_dot; /* Set mark at old location */ + mark_p = 1; /* Mark's been set */ + sb_sins((SBBUF *)cur_buf,sbs_cpy(sd)); /* Insert copy of stuff */ + cur_dot = e_dot(); /* We're now after the new stuff */ + buf_tmat(mark_dot); /* Say modified from here to cur_dot*/ + this_cmd = YANKCMD; +} +#endif /*FX_UNKILL*/ + +#if FX_UNKPOP +/* EFUN: "Un-kill Pop" */ +f_unkpop() +{ register SBSTR *sd; + register int i; + + if (last_cmd != YANKCMD) + { ring_bell (); + return; + } + ed_delete(cur_dot,mark_dot); + if(cur_dot > mark_dot) + cur_dot = mark_dot; + i = KILL_LEN; + do { + if(--kill_ptr < 0) + kill_ptr = KILL_LEN-1; + if(sd = kill_ring[kill_ptr]) + break; + } while(--i); + + /* kill_ptr now pointing to right place; effect the yank. */ + e_gocur(); /* Make sure point at right place too! */ + return(f_unkill()); +} +#endif /*FX_UNKPOP*/ + +/* Indentation routines - still not polished */ + +#if FX_INDATM +/* EFUN: "Indent According to Mode" */ +/* In Fundamental mode, just inserts a tab. +*/ +f_indatm() +{ f_insself(TAB); /* This takes care of mode checking */ +} +#endif /*FX_INDATM*/ + +#if FX_INDNL +/* EFUN: "Indent New Line" */ +f_indnl() /* execute CR followed by tab */ +{ +#if IMAGEN + /* Not dispatch-based, but rather hard-wired to do Gosmacs thing */ + ed_crins(); + f_indund(); +#else + cmd_xct(CR); + cmd_xct(TAB); +#endif /*-IMAGEN*/ +} +#endif /*FX_INDNL*/ + + +#if FX_BACKIND +/* EFUN: "Back to Indentation" +** Moves to end of current line's indentation. +*/ +f_backind() +{ e_gobol(); /* First move to beg of line */ + e_gofwsp(); /* Then forward over whitespace */ + ed_setcur(); +} +#endif /*FX_BACKIND*/ + + +#if FX_INDCOMM + +static char *comm_beg = "/* "; +static char *comm_end = " */"; + +/* EFUN: "Indent for Comment" */ +f_indcomm() +{ + f_endline(); + if(indtion(cur_dot) < ev_ccolumn) + ed_indto(ev_ccolumn); + else ed_sins(" "); + ed_sins (comm_beg); + ed_sins (comm_end); + e_igoff(-strlen (comm_end)); /* back over end string */ + e_setcur(); +} +#endif /*FX_INDCOMM*/ + +#if FX_INDREL +/* EFUN: "Indent Relative" */ +/* This used to mistakenly be called Indent Under. +** Still not fully implemented. +** If at beginning of line, looks back at previous indented line, +** and indents this line that much. If there is no preceding indented +** line or not at beginning of line, insert a tab. +*/ +f_indrel() +{ register int c; + register n; +#if IMAGEN + chroff savdot; +#endif /*IMAGEN*/ +#if ICONOGRAPHICS + chroff savdot; + int curind, newind, morebuf; +#endif /*ICONOGRAPHICS*/ + + if((c = e_rgetc()) == EOF) +#if IMAGEN + return(f_insself(TAB)); /* Do mode-based tabbing */ +#else + return(ed_insert(TAB)); +#endif /*-IMAGEN*/ + + if(c == LF) + { e_gobol(); + e_gofwsp(); + n = d_curind(); + e_gonl(); /* Return to orig pos */ + if(n) + { ed_indto(n); +#if IMAGEN + savdot = e_dot(); + e_gofwsp(); + ed_delete(savdot, e_dot()); +#endif /*IMAGEN*/ + return; + } + } +#if ICONOGRAPHICS + else + { e_igoff (1); + curind = indtion (savdot = e_dot ()); + /* get current dot and indentation */ + while (1) /* find a prev line that extends rightward */ + { morebuf = e_gopl (); + e_goeol (); + if ((newind = d_curind()) > curind) break; + if (morebuf == 0) /* hit beginning of buffer */ + { e_go (savdot); + f_delspc(); + return (1); + } + } + + e_gobol (); + e_igoff (inindex (e_dot (), curind)); + if (d_curind() > curind) + e_rgetc (); /* pushed ahead by tab */ + + while (c_wsp (e_getc ()) == 0) ; + e_backc (); + e_fwsp (); + newind = d_curind(); + e_go (savdot); + f_delspc(); + ed_indto (newind); + } +#else + else e_getc(); +#if IMAGEN + f_insself(TAB); /* Do mode-based tabbing */ +#else + ed_insert(TAB); +#endif /*-IMAGEN*/ +#endif /*-ICONOGRAPHICS*/ +} +#endif /*FX_INDREL*/ + + +/* Paren matching stuff. Note that this stuff will be very painful unless +** tinwait() works properly. +*/ +#if 0 +/* EFUN: "Self-Insert and Match" (intended to be bound to brackets) */ +/* (KLH: Evidently this was never finished) +*/ +insertmatch(c) +register int c; +{ + +} +#endif + +/* Should change this to Matching Paren */ +#if FX_MATCHBRACK +/* EFUN: "Match Bracket" (not EMACS) - from IMAGEN config + * Show the matching bracket for the character right before dot + */ +f_matchbrack() +{ + chroff savdot; + register int i, mc, secs; + + if (exp_p) + secs = exp; + else + secs = 1; + savdot = cur_dot; /* Save our location */ + mc = e_rgetc(); /* Pick up character before dot */ + if (mc != ')' && mc != ']' && mc != '}') + { e_getc(); /* Nothing, try at dot instead */ + e_getc(); + mc = e_rgetc(); + if (mc != ')' && mc != ']' && mc != '}') + { ding("What bracket?"); + e_go(savdot); + return; + } + } + if (! matchonelevel(mc)) + ring_bell(); + else + { ed_setcur(); + if (d_line(cur_dot) < 0) + secs = 10; /* Wait longer if off-screen */ + redisplay(); /* Wish it were simple upd_wind() */ + for (i = 1; i <= secs; ++i) + { if (tinwait()) + break; + sleep(1); + } + } + e_gosetcur(savdot); /* Back to origin */ + redp(RD_MOVE); /* Cursor has moved */ +} + + +/* Try to match 'mc', return true iff found it */ +matchonelevel(mc) +register int mc; +{ + register int c; + + while ((c = e_rgetc()) != EOF) + { if (c == /*[*/ ']' || c == /*(*/ ')' || c == /*{*/ '}') + { if (! matchonelevel(c)) + break; + } + else if (c == '(' /*)*/) + return(mc == /*(*/ ')'); + else if (c == '[' /*]*/) + return(mc == /*[*/ ']'); + else if (c == '{' /*}*/) + return(mc == /*{*/ '}'); + } + return(0); +} +#endif /*FX_MATCHBRACK*/ diff --git a/commands/elle/eefd.c b/commands/elle/eefd.c new file mode 100755 index 000000000..8c169fbdb --- /dev/null +++ b/commands/elle/eefd.c @@ -0,0 +1,434 @@ +/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ + +/* EEFD Display control functions + */ + +#include "elle.h" + + +#if FX_NEWWIN +/* EFUN: "New Window" */ +/* Clear current window and set as requested. + * ^L - clear current window and redisplay it (default top) + * ^L - select new window so that current line is + * the 'th from top of window (0 = top line) + * ^U^L - clear current line and redisplay. + */ +f_newwin() +{ register int i, n; + register struct window *w; + + d_fixcur(); /* Ensure screen vars correct */ + w = cur_win; + if (exp_p) + { if((n = exp) == 4 && exp_p == 4 /* CTRL-U? */ + && (i = d_line(cur_dot)) >= 0) /* On valid line? */ + { d_lupd(w, i); /* Update it */ + return; + } + } + else /* No argument given */ + { redp(RD_SCREEN); /* Clear whole screen (later just window? */ +#if IMAGEN + return; +#else + n = (ev_nwpct*w->w_ht)/100; /* Set new window using % */ +#endif /*-IMAGEN*/ + } + if (n < 0) n = 0; /* Ensure # is reasonable */ + else if (n >= w->w_ht) + n = w->w_ht - 1; + d_fgoloff(-n); /* Go up given # of lines */ + w->w_topldot = e_dot(); /* Set new top-line dot */ + e_gocur(); /* Move back to cur_dot */ + redp(RD_FIXWIN); /* Say to re-hack window */ +} +#endif /*FX_NEWWIN*/ + +#if FX_NSCREEN +/* EFUN: "Next Screen" */ +f_nscreen() +{ d_screen( exp); +} +#endif /*FX_NSCREEN*/ + +#if FX_PSCREEN +/* EFUN: "Previous Screen" */ +f_pscreen() +{ d_screen(-exp); +} +#endif /*FX_PSCREEN*/ + +#if FX_OTHNSCREEN +/* EFUN: "Other New Screen" (not EMACS) - from IMAGEN config */ +f_othnscreen() +{ + if (! oth_win) + return; + f_othwind(); + if (exp_p) /* With arg, back up */ + d_screen(-1); + else + d_screen(1); + f_othwind(); + redp(RD_WINDS); /* Look at all windows */ +} +#endif /*FX_OTHNSCREEN*/ + + +#if FX_LWINDBORD +/* EFUN: "Line to Window Border" (not EMACS) - from IMAGEN config */ +f_lwindbord() +{ + if (exp_p) + /* With arg, means "to bottom" */ + exp = cur_win->w_ht - 1; + else + /* Else, to top */ + exp = 0; + + /* Just a "front end" for ^L */ + exp_p = 1; + f_newwin(); +} +#endif /*FX_LWINDBORD*/ + +#if FX_SCUPWIND +/* EFUN: "Scroll Window Up" (not EMACS) - from IMAGEN config */ +f_scupwind() +{ + scroll_win(exp); +} +#endif /*FX_SCUPWIND*/ + +#if FX_SCDNWIND +/* EFUN: "Scroll Window Down" (not EMACS) - from IMAGEN config */ +f_scdnwind() +{ + scroll_win(-exp); +} +#endif /*FX_SCDNWIND*/ + + +#if FX_MVWTOP +/* EFUN: "Move to Window Top" (not EMACS) - from IMAGEN config */ +f_mvwtop() +{ + extern moveborder(); + moveborder(1); +} +#endif /*FX_MVWTOP*/ + +#if FX_MVWBOT +/* EFUN: "Move to Window Bottom" (not EMACS) - from IMAGEN config */ +f_mvwbot() +{ + extern moveborder(); + moveborder(0); +} +#endif /*FX_MVWBOT*/ + + + +#if FX_NSCREEN || FX_PSCREEN || FX_OTHNSCREEN +/* Move to new loc by N screenfuls. + * If moving downward, keep bottom 2 lines of current screen on top of next. + * If moving up, keep top 2 lines of current screen on bottom of next. + */ +d_screen(rep) +int rep; +{ + register int i; + register struct window *w; + chroff newdot; + + w = cur_win; + if((i = w->w_ht - 2) <= 0) /* Just-in-case check */ + i = 1; + if((i *= rep) == 0) + return; + d_fixcur(); /* Ensure window fixed up */ + e_go(w->w_topldot); /* Start at top of screen */ + d_fgoloff(i); + + /* Find where we are now, and make that the new top of window. */ + if((newdot = e_dot()) != e_blen()) /* If not at EOF, */ + w->w_topldot = newdot; /* set new top of window! */ + else w->w_topldot = 0; /* Else let fix_wind select top. */ + + e_setcur(); /* Ensure cur_dot set to real loc */ +#if IMAGEN + redp(RD_WINRES|RD_REDO); /* HINT: just repaint screen */ +#else + redp(RD_FIXWIN|RD_MOVE); +#endif /*-IMAGEN*/ +} +#endif /*FX_NSCREEN || FX_PSCREEN || FX_OTHNSCREEN*/ + +#if FX_SCUPWIND || FX_SCDNWIND /* If want scroll-window function */ +scroll_win(n) +register int n; +{ register struct window *w = cur_win; + chroff savdot; + + if (n == 0) return; + d_fixcur(); /* Ensure screen vars for win all set up */ + e_go(w->w_topldot); /* Go to top of current window */ + d_fgoloff(n); /* Move given # of display lines */ + w->w_topldot = e_dot(); /* Set new top of window */ + redp(RD_FIXWIN); /* Say new window needs fixing up */ + + /* Now adjust position of current dot so it is still within window */ + if (n > 0) + { /* Moving screen text "up" (win down) */ + if (cur_dot < w->w_topldot) /* See if scrolled off top */ + e_setcur(); /* yes, make dot be win top */ + } + else { /* Moving screen text "down" (win up) */ + savdot = cur_dot; /* Save since must temporarily */ + e_setcur(); /* set current dot within window, */ + d_fixcur(); /* so screen can be fixed up. */ + if (inwinp(w, savdot)) /* Now see if old dot in new win */ + cur_dot = savdot; /* Yes, just restore it! */ + else /* No, make it beg of bottom line. */ + cur_dot = scr[w->w_pos + w->w_ht - 1]->sl_boff; + } + e_gocur(); /* Make current pos be cur_dot */ +} +#endif /* FX_SC%%WIND */ + +#if FX_MVWTOP || FX_MVWBOT /* Guts for above two functions */ +static +moveborder(top) +int top; +{ + d_fixcur(); /* Ensure current win screen image fixed up */ + e_gosetcur(top ? cur_win->w_topldot + : scr[cur_win->w_pos + cur_win->w_ht - 1]->sl_boff); + + redp(RD_MOVE); /* Should only be cursor adjustment */ +} +#endif /*FX_MVW%%%*/ + +/* Given a line and a position in that line, return the xpos. + * NOTE CAREFULLY that when line extends over several screen lines, + * the value returned is the screen X position even though it + * may be some lines down from the start of the logical line! + * Also note this won't work very well if tabs exist on the extra + * lines. This rtn should not be used for cursor positioning. + * Also note: d_ncols() will never return -1 meaning EOL because the + * setup guarantees there is no EOL within the range checked. + */ +d_curind() /* Find current indentation */ +{ indtion(e_dot()); +} +indtion(lin) +chroff lin; +{ register int i, col; + chroff savdot; + chroff nchars; + + savdot = e_dot(); /* Save current position */ + e_go(lin); /* Go to line caller wants */ + e_gobol(); /* Go to its beginning */ + col = 0; /* Start at left margin */ + if((nchars = lin - e_dot()) > 0) + do { + if(nchars < (i = scr_wd0)) + i = nchars; + if((col = d_ncols(i, col)) < 0) /* Hit edge of screen? */ + col = 0; /* Reset to left margin */ + } while((nchars -= i) > 0); + e_go(savdot); /* Restore current position */ + return(col); +} + +/* ININDEX - How many positions in lin must we go to get to xpos? + * Returns -1 if can't be done. Assumes "lin" is at beginning of a line! + */ + +inindex (lin, xpos) +chroff lin; +int xpos; +{ register int col, x; + chroff savdot; + char tmp[MAXLINE+MAXCHAR]; + extern int sctreol; /* From EEDISP */ + + if((x = xpos) <= 0) return(0); + if(x >= MAXLINE) return(-1); /* ?!? */ + col = 0; + savdot = e_dot(); + e_go(lin); /* Assumes this is start of line */ + col = sctrin(tmp, x, 0); /* Translate from sb_getc input */ + if((col - x) >= 0) /* Exact count-out or past it? */ + { x = e_dot() - lin; /* Yup, win. */ + if (sctreol > 0) /* Did we hit (and include) EOL? */ +#if FX_EOLMODE /* If so, back up over the EOL. */ + x -= eolcrlf(cur_buf) ? 2 : 1; +#else + --x; +#endif + } + else x = -1; /* Nope, EOL or EOF hit too soon. */ + e_go(savdot); + return(x); +} + +/* + * D_ ROUTINES - display-relative functions. Similar to E_, but + * a "line" is defined as one line of the screen rather than + * as a logical text line. Also, for efficiency reasons + * arguments are given saying how many lines to hack. + */ + +d_gopl() { return(d_goloff(-1)); } +d_gonl() { return(d_goloff( 1)); } + +/* D_GOLOFF(i) - Go to beginning of a display line + * D_FGOLOFF(i) - ditto, but assumes screen image of window already fixed up. + * i - # of lines offset. Negative moves up, positive down. + * Zero arg goes to beginning of current display line. + * Side effects: screen image of window is fixed up at + * start of routine, but is NOT updated by the move to new location. + */ +d_goloff(cnt) +int cnt; +{ d_fixcur(); + d_fgoloff(cnt); /* Now can invoke fixed-up fast version */ +} +d_fgoloff(cnt) +register int cnt; +{ register int y; + struct scr_line l; + char line[MAXLINE+MAXCHAR]; + int top, bot; + + /* Find current position in window, since can save time + * by using stuff already in fixed-up screen image. + */ + if((y = d_line(e_dot())) < 0) /* Get current Y position */ + { + errbarf("Dot out of window"); + y = 0; + } + top = cur_win->w_pos; /* 1st line of window */ + bot = top + cur_win->w_ht; /* 1st line not in window */ + l.sl_boff = scr[y]->sl_boff; + l.sl_nlin = &line[0]; + l.sl_cont = 0; + + if(cnt > 0) goto down; + + /* Go upwards. This is hairy because we want to be clever about + * huge logical lines -- avoid going all the way back to BOL. + */ + if((y+cnt) >= top) /* Fits? */ + goto onscr; /* Hurray, hack it! */ + cnt += y - top; /* Sigh, find # lines to skip */ + y = top; + l.sl_boff = scr[y]->sl_boff; + e_go(l.sl_boff); + + /* Okay, here's the hairy part. Must go backwards from top + * line; if no EOL within scr_wid*cnt chars, then simply assume one is + * seen. + */ + cnt = -cnt; + d_backup(cnt); + return; /* Really should re-adjust stuff, but... */ + + /* Go downwards. Not too bad... */ +down: + if((y+cnt) <= bot) /* Fits? */ + goto onscr; /* Hurray, hack it! */ + cnt -= bot - y; /* Sigh, find # lines can skip */ + y = bot - 1; + l.sl_boff = scr[y]->sl_boff + scr[y]->sl_len; + if(y > top + && (l.sl_cont = scr[y-1]->sl_cont)) + l.sl_line = scr[y-1]->sl_line; + e_go(l.sl_boff); + + do { + fix_line(&l,&l); + } while(--cnt > 0 && l.sl_len); + return; + +onscr: if((y += cnt) >= bot) + { --y; + e_go(scr[y]->sl_boff + scr[y]->sl_len); + } + else e_go(scr[y]->sl_boff); +} + +/* D_FIXCUR() - Ensure current window is fixed up, with + * current location (not necessarily cur_dot)! + * Ensure cur_dot reflects real loc so that fix_wind will work, + * and always call fix_wind to ensure that screen image vars + * are set properly. Note any active redisplay flags must be carried + * on into window redisplay flags, so fix_wind will notice them. + */ +d_fixcur() +{ register struct window *w; + chroff savedot; + + w = cur_win; + savedot = cur_dot; + e_setcur(); + w->w_redp |= rd_type&RDS_WINFLGS; + fix_wind(w); /* Always ensure window is set up! */ + redp(w->w_redp); /* Add back new flags */ + rd_type &= ~RDS_DOFIX; /* and flush fix-invoking ones */ + cur_dot = savedot; /* Restore cur_dot, no longer hacked. */ +} + +d_backup(nlin) /* Try to back up by nlin screen lines */ +int nlin; +{ register int cnt, n, c; + int eolstop; + + if((cnt = nlin+1) <= 0) return; + c = 0; + do + { n = scr_wid; + eolstop = 0; /* Not yet stopped at EOL */ + do { if((c = e_rgetc()) == EOF) + return; + if(c == LF) + { +#if FX_EOLMODE + if(eolcrlf(cur_buf)) + { if((c = e_rgetc()) == CR) + { eolstop++; + break; + } + if(c != EOF) + e_getc(); + } + else +#endif + { eolstop++; + break; + } + } + } while(--n); + } while(--cnt); + if(eolstop) + { +#if FX_EOLMODE + if(eolcrlf(cur_buf)) e_getc(); /* Skip back over CR */ +#endif + e_getc(); /* Skip back over LF */ + } + + /* At this point, dot is guaranteed to be less than goal, + * which is the important thing for fix_wind, which can handle + * things okay if dot is off bottom of window. + */ + return(1); /* Say always test result */ +} diff --git a/commands/elle/eefdef.h b/commands/elle/eefdef.h new file mode 100755 index 000000000..4972a9736 --- /dev/null +++ b/commands/elle/eefdef.h @@ -0,0 +1,159 @@ +/* .H Function Definition file, generated by ELLEC */ +/* 0 */ EFUNHOLE /* Always undefined */ +/* 1 */ EFUN( f_insself , "f_insself" , "Insert Self") +/* 2 */ EFUN( f_quotins , "f_quotins" , "Quoted Insert") +/* 3 */ EFUN( f_crlf , "f_crlf" , "CRLF") +/* 4 */ EFUN( f_fchar , "f_fchar" , "Forward Character") +/* 5 */ EFUN( f_bchar , "f_bchar" , "Backward Character") +/* 6 */ EFUN( f_dchar , "f_dchar" , "Delete Character") +/* 7 */ EFUN( f_bdchar , "f_bdchar" , "Backward Delete Character") +/* 8 */ EFUN( f_delspc , "f_delspc" , "Delete Horizontal Space") +/* 9 */ EFUN( f_tchars , "f_tchars" , "Transpose Characters") +/* 10 */ EFUN( f_fword , "f_fword" , "Forward Word") +/* 11 */ EFUN( f_bword , "f_bword" , "Backward Word") +/* 12 */ EFUN( f_kword , "f_kword" , "Kill Word") +/* 13 */ EFUN( f_bkword , "f_bkword" , "Backward Kill Word") +/* 14 */ EFUN( f_twords , "f_twords" , "Transpose Words") +/* 15 */ EFUN( f_ucword , "f_ucword" , "Uppercase Word") +/* 16 */ EFUN( f_lcword , "f_lcword" , "Lowercase Word") +/* 17 */ EFUN( f_uciword , "f_uciword" , "Uppercase Initial") +/* 18 */ EFUNHOLE +/* 19 */ EFUNHOLE +/* 20 */ EFUN( f_begline , "f_begline" , "Beginning of Line") +/* 21 */ EFUN( f_endline , "f_endline" , "End of Line") +/* 22 */ EFUN( f_nxtline , "f_nxtline" , "Next Line") +/* 23 */ EFUN( f_prvline , "f_prvline" , "Previous Line") +/* 24 */ EFUN( f_dnrline , "f_dnrline" , "Down Real Line") +/* 25 */ EFUN( f_uprline , "f_uprline" , "Up Real Line") +/* 26 */ EFUN( f_oline , "f_oline" , "Open Line") +/* 27 */ EFUN( f_delblines , "f_delblines" , "Delete Blank Lines") +/* 28 */ EFUN( f_kline , "f_kline" , "Kill Line") +/* 29 */ EFUN( f_bkline , "f_bkline" , "Backward Kill Line") +/* 30 */ EFUN( f_goline , "f_goline" , "Goto Line") +/* 31 */ EFUNHOLE +/* 32 */ EFUNHOLE +/* 33 */ EFUNHOLE +/* 34 */ EFUNHOLE +/* 35 */ EFUN( f_setmark , "f_setmark" , "Set/Pop Mark") +/* 36 */ EFUN( f_exchmark , "f_exchmark" , "Exchange Point and Mark") +/* 37 */ EFUN( f_kregion , "f_kregion" , "Kill Region") +/* 38 */ EFUN( f_copreg , "f_copreg" , "Copy Region") +/* 39 */ EFUN( f_ucreg , "f_ucreg" , "Uppercase Region") +/* 40 */ EFUN( f_lcreg , "f_lcreg" , "Lowercase Region") +/* 41 */ EFUN( f_fillreg , "f_fillreg" , "Fill Region") +/* 42 */ EFUNHOLE +/* 43 */ EFUNHOLE +/* 44 */ EFUNHOLE +/* 45 */ EFUN( f_fpara , "f_fpara" , "Forward Paragraph") +/* 46 */ EFUN( f_bpara , "f_bpara" , "Backward Paragraph") +/* 47 */ EFUN( f_mrkpara , "f_mrkpara" , "Mark Paragraph") +/* 48 */ EFUN( f_fillpara , "f_fillpara" , "Fill Paragraph") +/* 49 */ EFUNHOLE +/* 50 */ EFUN( f_selbuffer , "f_selbuffer" , "Select Buffer") +/* 51 */ EFUN( f_selxbuffer, "f_selxbuffer", "Select Existing Buffer") +/* 52 */ EFUN( f_kbuffer , "f_kbuffer" , "Kill Buffer") +/* 53 */ EFUN( f_listbufs , "f_listbufs" , "List Buffers") +/* 54 */ EFUN( f_bufnotmod , "f_bufnotmod" , "Buffer Not Modified") +/* 55 */ EFUN( f_eolmode , "f_eolmode" , "EOL CRLF Mode") +/* 56 */ EFUN( f_gobeg , "f_gobeg" , "Goto Beginning") +/* 57 */ EFUN( f_goend , "f_goend" , "Goto End") +/* 58 */ EFUN( f_whatpage , "f_whatpage" , "What Page") +/* 59 */ EFUNHOLE +/* 60 */ EFUN( f_ffile , "f_ffile" , "Find File") +/* 61 */ EFUN( f_rfile , "f_rfile" , "Read File") +/* 62 */ EFUN( f_vfile , "f_vfile" , "Visit File") +/* 63 */ EFUN( f_ifile , "f_ifile" , "Insert File") +/* 64 */ EFUN( f_sfile , "f_sfile" , "Save File") +/* 65 */ EFUN( f_savefiles , "f_savefiles" , "Save All Files") +/* 66 */ EFUN( f_wfile , "f_wfile" , "Write File") +/* 67 */ EFUN( f_wreg , "f_wreg" , "Write Region") +/* 68 */ EFUN( f_wlastkill , "f_wlastkill" , "Write Last Kill") +/* 69 */ EFUNHOLE +/* 70 */ EFUN( f_2winds , "f_2winds" , "Two Windows") +/* 71 */ EFUN( f_1wind , "f_1wind" , "One Window") +/* 72 */ EFUN( f_othwind , "f_othwind" , "Other Window") +/* 73 */ EFUN( f_growind , "f_growind" , "Grow Window") +/* 74 */ EFUN( f_shrinkwind, "f_shrinkwind", "Shrink Window") +/* 75 */ EFUN( f_delwind , "f_delwind" , "Delete Window") +/* 76 */ EFUN( f_sowind , "f_sowind" , "Standout Window") +/* 77 */ EFUN( f_2modewinds, "f_2modewinds", "Two Mode Windows") +/* 78 */ EFUN( f_newwin , "f_newwin" , "New Window") +/* 79 */ EFUN( f_nscreen , "f_nscreen" , "Next Screen") +/* 80 */ EFUN( f_pscreen , "f_pscreen" , "Previous Screen") +/* 81 */ EFUNHOLE +/* 82 */ EFUNHOLE +/* 83 */ EFUN( f_scupwind , "f_scupwind" , "Scroll Window Up") +/* 84 */ EFUN( f_scdnwind , "f_scdnwind" , "Scroll Window Down") +/* 85 */ EFUN( f_mvwtop , "f_mvwtop" , "Move to Window Top") +/* 86 */ EFUN( f_mvwbot , "f_mvwbot" , "Move to Window Bottom") +/* 87 */ EFUNHOLE +/* 88 */ EFUNHOLE +/* 89 */ EFUNHOLE +/* 90 */ EFUN( f_setprof , "f_setprof" , "Set Profile") +/* 91 */ EFUN( f_pfxmeta , "f_pfxmeta" , "Prefix Meta") +/* 92 */ EFUN( f_pfxext , "f_pfxext" , "Prefix Extend") +/* 93 */ EFUN( f_uarg , "f_uarg" , "Universal Arg") +/* 94 */ EFUN( f_negarg , "f_negarg" , "Negative Argument") +/* 95 */ EFUN( f_argdig , "f_argdig" , "Argument Digit") +/* 96 */ EFUN( f_vtbuttons , "f_vtbuttons" , "VT100 Button Hack") +/* 97 */ EFUN( f_describe , "f_describe" , "Describe") +/* 98 */ EFUNHOLE +/* 99 */ EFUNHOLE +/* 100 */ EFUN( f_skmac , "f_skmac" , "Start Kbd Macro") +/* 101 */ EFUN( f_ekmac , "f_ekmac" , "End Kbd Macro") +/* 102 */ EFUN( f_xkmac , "f_xkmac" , "Execute Kbd Macro") +/* 103 */ EFUN( f_vkmac , "f_vkmac" , "View Kbd Macro") +/* 104 */ EFUNHOLE +/* 105 */ EFUN( f_unkill , "f_unkill" , "Un-kill") +/* 106 */ EFUN( f_unkpop , "f_unkpop" , "Un-kill Pop") +/* 107 */ EFUN( f_appnkill , "f_appnkill" , "Append Next Kill") +/* 108 */ EFUNHOLE +/* 109 */ EFUNHOLE +/* 110 */ EFUN( f_srch , "f_srch" , "String Search") +/* 111 */ EFUN( f_rsrch , "f_rsrch" , "Reverse String Search") +/* 112 */ EFUN( f_isrch , "f_isrch" , "Incremental Search") +/* 113 */ EFUN( f_risrch , "f_risrch" , "Reverse Search") +/* 114 */ EFUN( f_repstr , "f_repstr" , "Replace String") +/* 115 */ EFUN( f_querep , "f_querep" , "Query Replace") +/* 116 */ EFUN( f_repline , "f_repline" , "Replace in Line") +/* 117 */ EFUN( f_sfcol , "f_sfcol" , "Set Fill Column") +/* 118 */ EFUN( f_sfpref , "f_sfpref" , "Set Fill Prefix") +/* 119 */ EFUN( f_fillmode , "f_fillmode" , "Auto Fill Mode") +/* 120 */ EFUNHOLE +/* 121 */ EFUN( f_indatm , "f_indatm" , "Indent According to Mode") +/* 122 */ EFUN( f_indnl , "f_indnl" , "Indent New Line") +/* 123 */ EFUN( f_backind , "f_backind" , "Back to Indentation") +/* 124 */ EFUN( f_indcomm , "f_indcomm" , "Indent for Comment") +/* 125 */ EFUN( f_indrel , "f_indrel" , "Indent Relative") +/* 126 */ EFUNHOLE +/* 127 */ EFUNHOLE +/* 128 */ EFUNHOLE +/* 129 */ EFUNHOLE +/* 130 */ EFUN( f_pshinf , "f_pshinf" , "Push to Inferior") +/* 131 */ EFUN( f_retsup , "f_retsup" , "Return to Superior") +/* 132 */ EFUN( f_wfexit , "f_wfexit" , "Write File Exit") +/* 133 */ EFUNHOLE +/* 134 */ EFUNHOLE +/* 135 */ EFUNHOLE +/* 136 */ EFUNHOLE +/* 137 */ EFUNHOLE +/* 138 */ EFUNHOLE +/* 139 */ EFUNHOLE +/* 140 */ EFUN( f_bkpt , "f_bkpt" , "Hit Breakpoint") +/* 141 */ EFUN( f_debug , "f_debug" , "Debug Mode") +/* 142 */ EFUNHOLE +/* 143 */ EFUNHOLE +/* 144 */ EFUNHOLE +/* 145 */ EFUNHOLE +/* 146 */ EFUNHOLE +/* 147 */ EFUNHOLE +/* 148 */ EFUNHOLE +/* 149 */ EFUNHOLE +/* 150 */ EFUNHOLE +/* 151 */ EFUNHOLE +/* 152 */ EFUNHOLE +/* 153 */ EFUNHOLE +/* 154 */ EFUNHOLE +/* 155 */ EFUNHOLE +/* 156 */ EFUNHOLE +/* 157 */ EFUNHOLE diff --git a/commands/elle/eefed.c b/commands/elle/eefed.c new file mode 100755 index 000000000..00b7c28c9 --- /dev/null +++ b/commands/elle/eefed.c @@ -0,0 +1,283 @@ +/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* EEFED - ED-type functions + */ +#include "elle.h" + +/* + * ED_INSERT -- Insert character given as argument. + */ + +ed_insert(c) +int c; +{ register SBBUF *sb; + + sb = (SBBUF *) cur_buf; /* For a little speed */ + sb_putc(sb,c); /* Insert the char */ + cur_dot++; /* Advance dot */ + buf_tmod((chroff)-1); /* Mark buffer modified, for redisplay etc. */ + /* Perhaps later use specialized routine? */ +} + +ed_insn(ch, cnt) +int ch, cnt; +{ register int i; + if((i = cnt) > 0) + do { ed_insert(ch); + } while(--i); +} + +ed_crins() +{ +#if FX_EOLMODE + if (eolcrlf(cur_buf)) /* If EOL is made of CR-LF */ + ed_insert(CR); /* then first insert CR, then drop down to */ +#endif + ed_insert(LF); /* Insert LF */ +} + + +ed_sins (s) /* insert this string */ +register char *s; +{ register c; + while (c = *s++) + ed_insert (c); +} + +ed_nsins (s, i) /* Insert string of N chars */ +register char *s; +register int i; +{ if(i > 0) + do { ed_insert(*s++); } while(--i); +} + +/* ED_INDTO(col) - Indent to specified column. +** Finds current cursor position, and inserts tabs and spaces +** so cursor ends up at column goal. Does nothing if already at or past +** specified column. +*/ + +ed_indto(goal) +register int goal; +{ register int ng; + + ng = goal & ~07; /* Get distance to tab stop */ + ed_insn(TAB, ((ng - (d_curind() & ~07)) >> 3)); + ed_insn(SP, goal-ng); +} + +/* Oddball routine - Set cur_dot to actual I/O location and + * tell display that cursor probably moved. This is not really a + * function of itself; it provides support for real functions. + */ +ed_setcur() +{ e_setcur(); /* Set cur_dot */ + redp(RD_MOVE); /* Alert redisplay to check cursor loc */ +} + +/* Go to given dot */ +ed_go (dot) +chroff dot; +{ e_go(dot); + ed_setcur(); +} + +/* Go to given offset from current location */ +ed_goff(off) +chroff off; +{ e_goff(off); + ed_setcur(); +} + +/* Go to given INTEGER offset from current location */ +ed_igoff(ioff) +int ioff; +{ e_igoff(ioff); + ed_setcur(); +} + +/* Reset (delete all of) Buffer + * Should buffer be marked modified or not? Currently isn't. + */ +ed_reset() +{ if(e_blen() == 0) + return; /* Already empty */ + e_reset(); + cur_dot = 0; + cur_win->w_topldot = 0; /* Is this necessary? */ +#if IMAGEN + redp(RD_WINRES|RD_REDO); +#else + redp(RD_WINRES); /* This window needs complete update */ +#endif /*-IMAGEN*/ + +/* buf_mod(); */ /* Mark modified ?? */ +/* mark_p = 0; */ /* Say no mark set ?? */ +} + +ed_deln(off) +chroff off; +{ chroff dot; + dot = e_dot(); + e_goff(off); + ed_delete(e_dot(), dot); +} + +/* ED_DELETE(dot1,dot2) - Delete all characters between the two + * positions indicated by dot1 and dot2. Their order does not + * matter; cur_dot and mark_dot are updated as necessary. + */ +ed_delete(dot1,dot2) +chroff dot1,dot2; +{ chroff tmpdot, savdot; + + if(dot1 > dot2) + { tmpdot = dot1; + dot1 = dot2; + dot2 = tmpdot; + } + e_go(dot1); + tmpdot = dot2-dot1; + sb_deln((SBBUF *)cur_buf,tmpdot); + + savdot = cur_dot; /* Save cur_dot value */ + cur_dot = dot1; /* so can set up for */ + buf_tmod((chroff)0); /* call to update disp-change vars */ + cur_dot = savdot; + + if(cur_dot >= dot2) + cur_dot -= tmpdot; + else if(cur_dot > dot1) + cur_dot = dot1; + if(mark_dot >= dot2) + mark_dot -= tmpdot; + else if(mark_dot > dot1) + mark_dot = dot1; + e_gocur(); +} + +/* ED_KILL(dot1,dot2) - Kill (save and delete) text between two places in + * the buffer. + * We assume we are deleting from dot1 to dot2, thus if dot1 > dot2 + * then backwards deletion is implied, and the saved text is prefixed + * (instead of appended) to any previously killed text. + */ +ed_kill(dot1,dot2) +chroff dot1,dot2; +{ register SBSTR *sd, *sdo; + SBSTR *e_copyn(); + + e_go(dot1); + sd = e_copyn(dot2-dot1); + if(sd == 0) return; + if(last_cmd == KILLCMD && (sdo = kill_ring[kill_ptr])) + { if(dot1 > dot2) /* Prefix new killed stuff onto old stuff */ + { sbs_app(sd,sdo); + kill_ring[kill_ptr] = sd; + } + else /* Append new stuff to old stuff */ + sbs_app(sdo,sd); + } + else kill_push(sd); + ed_delete(dot1,dot2); +} + +kill_push(sdp) +SBSTR *sdp; +{ register SBSTR *sd; + + if(++kill_ptr >= KILL_LEN) kill_ptr = 0; + if(sd = kill_ring[kill_ptr]) + sbs_del(sd); + kill_ring[kill_ptr] = sdp; +} + + +#define isupper(c) (('A' <= c) && (c <= 'Z')) +#define islower(c) (('a' <= c) && (c <= 'z')) +#define toupper(c) (c + ('A' - 'a')) +#define tolower(c) (c + ('a' - 'A')) + +#if FX_UCWORD||FX_LCWORD||FX_UCIWORD||FX_UCREG||FX_LCREG + +/* ED_CASE(dot1,dot2,downp) - Change the case within a region. + * downp = 0 for uppercase, 1 for lowercase, 2 for capitalize. + */ +ed_case(dot1, dot2, downp) +chroff dot1, dot2; +int downp; +{ chroff dcnt; + register int c, a, z; + int modflg; + + modflg = 0; + if((dcnt = dot2 - dot1) < 0) + { dcnt = dot1; + dot1 = dot2; + dot2 = dcnt; + dcnt -= dot1; + } + e_go(dot1); + + if(downp==2) + { a = 0; /* 0 looking for wd, 1 in word */ + while(--dcnt >= 0) + { if(delimp(c = e_getc())) /* Char in wd? */ + { a = 0; /* No */ + continue; + } + if(a) /* If already inside word */ + { if(isupper(c)) + c = tolower(c); + else continue; + } + else /* If encountered start of word */ + { a = 1; + if(islower(c)) + c = toupper(c); + else continue; + } + e_backc(); + e_ovwc(c); + modflg++; + } + goto casdon; + } + if(downp==0) + { a = 'a'; /* Convert to lower case */ + z = 'z'; + downp = -040; + } + else + { a = 'A'; /* Convert to upper case */ + z = 'Z'; + downp = 040; + } + while(--dcnt >= 0) + { if(a <= (c = e_getc()) && c <= z) + { e_backc(); + e_ovwc(c+downp); + modflg++; + } + } + +casdon: dot2 = cur_dot; /* Save dot */ + e_setcur(); /* Set up for modification range chk*/ + if(modflg) + buf_tmat(dot1); /* Stuff munged from there to here */ + ed_go(dot2); +} +#endif /* any ed_case caller */ + + +/* UPCASE(c) - Return upper-case version of character */ +upcase(ch) +int ch; +{ register int c; + c = ch&0177; + return(islower(c) ? toupper(c) : c); +} + diff --git a/commands/elle/eefidx.h b/commands/elle/eefidx.h new file mode 100755 index 000000000..5ed0e9c9a --- /dev/null +++ b/commands/elle/eefidx.h @@ -0,0 +1,241 @@ +/* .H Function Index Definition file, generated by ELLEC */ +/* FN_ defines Function Numbers (indices) for all known functions */ +/* FX_ defines Function eXistence in this ELLE configuration */ +#define FN_INSSELF 1 /* Insert Self */ +#define FX_INSSELF 1 +#define FN_QUOTINS 2 /* Quoted Insert */ +#define FX_QUOTINS 2 +#define FN_CRLF 3 /* CRLF */ +#define FX_CRLF 3 +#define FN_FCHAR 4 /* Forward Character */ +#define FX_FCHAR 4 +#define FN_BCHAR 5 /* Backward Character */ +#define FX_BCHAR 5 +#define FN_DCHAR 6 /* Delete Character */ +#define FX_DCHAR 6 +#define FN_BDCHAR 7 /* Backward Delete Character */ +#define FX_BDCHAR 7 +#define FN_DELSPC 8 /* Delete Horizontal Space */ +#define FX_DELSPC 8 +#define FN_TCHARS 9 /* Transpose Characters */ +#define FX_TCHARS 9 +#define FN_FWORD 10 /* Forward Word */ +#define FX_FWORD 10 +#define FN_BWORD 11 /* Backward Word */ +#define FX_BWORD 11 +#define FN_KWORD 12 /* Kill Word */ +#define FX_KWORD 12 +#define FN_BKWORD 13 /* Backward Kill Word */ +#define FX_BKWORD 13 +#define FN_TWORDS 14 /* Transpose Words */ +#define FX_TWORDS 14 +#define FN_UCWORD 15 /* Uppercase Word */ +#define FX_UCWORD 15 +#define FN_LCWORD 16 /* Lowercase Word */ +#define FX_LCWORD 16 +#define FN_UCIWORD 17 /* Uppercase Initial */ +#define FX_UCIWORD 17 +#define FN_BEGLINE 20 /* Beginning of Line */ +#define FX_BEGLINE 20 +#define FN_ENDLINE 21 /* End of Line */ +#define FX_ENDLINE 21 +#define FN_NXTLINE 22 /* Next Line */ +#define FX_NXTLINE 22 +#define FN_PRVLINE 23 /* Previous Line */ +#define FX_PRVLINE 23 +#define FN_DNRLINE 24 /* Down Real Line */ +#define FX_DNRLINE 24 +#define FN_UPRLINE 25 /* Up Real Line */ +#define FX_UPRLINE 25 +#define FN_OLINE 26 /* Open Line */ +#define FX_OLINE 26 +#define FN_DELBLINES 27 /* Delete Blank Lines */ +#define FX_DELBLINES 27 +#define FN_KLINE 28 /* Kill Line */ +#define FX_KLINE 28 +#define FN_BKLINE 29 /* Backward Kill Line */ +#define FX_BKLINE 29 +#define FN_GOLINE 30 /* Goto Line */ +#define FX_GOLINE 30 +#define FN_SETMARK 35 /* Set/Pop Mark */ +#define FX_SETMARK 35 +#define FN_EXCHMARK 36 /* Exchange Point and Mark */ +#define FX_EXCHMARK 36 +#define FN_KREGION 37 /* Kill Region */ +#define FX_KREGION 37 +#define FN_COPREG 38 /* Copy Region */ +#define FX_COPREG 38 +#define FN_UCREG 39 /* Uppercase Region */ +#define FX_UCREG 39 +#define FN_LCREG 40 /* Lowercase Region */ +#define FX_LCREG 40 +#define FN_FILLREG 41 /* Fill Region */ +#define FX_FILLREG 41 +#define FN_FPARA 45 /* Forward Paragraph */ +#define FX_FPARA 45 +#define FN_BPARA 46 /* Backward Paragraph */ +#define FX_BPARA 46 +#define FN_MRKPARA 47 /* Mark Paragraph */ +#define FX_MRKPARA 47 +#define FN_FILLPARA 48 /* Fill Paragraph */ +#define FX_FILLPARA 48 +#define FN_SELBUFFER 50 /* Select Buffer */ +#define FX_SELBUFFER 50 +#define FN_SELXBUFFER 51 /* Select Existing Buffer */ +#define FX_SELXBUFFER 51 +#define FN_KBUFFER 52 /* Kill Buffer */ +#define FX_KBUFFER 52 +#define FN_LISTBUFS 53 /* List Buffers */ +#define FX_LISTBUFS 53 +#define FN_BUFNOTMOD 54 /* Buffer Not Modified */ +#define FX_BUFNOTMOD 54 +#define FN_EOLMODE 55 /* EOL CRLF Mode */ +#define FX_EOLMODE 55 +#define FN_GOBEG 56 /* Goto Beginning */ +#define FX_GOBEG 56 +#define FN_GOEND 57 /* Goto End */ +#define FX_GOEND 57 +#define FN_WHATPAGE 58 /* What Page */ +#define FX_WHATPAGE 58 +#define FN_FFILE 60 /* Find File */ +#define FX_FFILE 60 +#define FN_RFILE 61 /* Read File */ +#define FX_RFILE 61 +#define FN_VFILE 62 /* Visit File */ +#define FX_VFILE 62 +#define FN_IFILE 63 /* Insert File */ +#define FX_IFILE 63 +#define FN_SFILE 64 /* Save File */ +#define FX_SFILE 64 +#define FN_SAVEFILES 65 /* Save All Files */ +#define FX_SAVEFILES 65 +#define FN_WFILE 66 /* Write File */ +#define FX_WFILE 66 +#define FN_WREG 67 /* Write Region */ +#define FX_WREG 67 +#define FN_WLASTKILL 68 /* Write Last Kill */ +#define FX_WLASTKILL 68 +#define FN_2WINDS 70 /* Two Windows */ +#define FX_2WINDS 70 +#define FN_1WIND 71 /* One Window */ +#define FX_1WIND 71 +#define FN_OTHWIND 72 /* Other Window */ +#define FX_OTHWIND 72 +#define FN_GROWIND 73 /* Grow Window */ +#define FX_GROWIND 73 +#define FN_SHRINKWIND 74 /* Shrink Window */ +#define FX_SHRINKWIND 74 +#define FN_DELWIND 75 /* Delete Window */ +#define FX_DELWIND 75 +#define FN_SOWIND 76 /* Standout Window */ +#define FX_SOWIND 76 +#define FN_2MODEWINDS 77 /* Two Mode Windows */ +#define FX_2MODEWINDS 77 +#define FN_NEWWIN 78 /* New Window */ +#define FX_NEWWIN 78 +#define FN_NSCREEN 79 /* Next Screen */ +#define FX_NSCREEN 79 +#define FN_PSCREEN 80 /* Previous Screen */ +#define FX_PSCREEN 80 +#define FN_OTHNSCREEN 81 /* Other New Screen */ +#define FX_OTHNSCREEN 0 +#define FN_LWINDBORD 82 /* Line to Window Border */ +#define FX_LWINDBORD 0 +#define FN_SCUPWIND 83 /* Scroll Window Up */ +#define FX_SCUPWIND 83 +#define FN_SCDNWIND 84 /* Scroll Window Down */ +#define FX_SCDNWIND 84 +#define FN_MVWTOP 85 /* Move to Window Top */ +#define FX_MVWTOP 85 +#define FN_MVWBOT 86 /* Move to Window Bottom */ +#define FX_MVWBOT 86 +#define FN_SETPROF 90 /* Set Profile */ +#define FX_SETPROF 90 +#define FN_PFXMETA 91 /* Prefix Meta */ +#define FX_PFXMETA 91 +#define FN_PFXEXT 92 /* Prefix Extend */ +#define FX_PFXEXT 92 +#define FN_UARG 93 /* Universal Arg */ +#define FX_UARG 93 +#define FN_NEGARG 94 /* Negative Argument */ +#define FX_NEGARG 94 +#define FN_ARGDIG 95 /* Argument Digit */ +#define FX_ARGDIG 95 +#define FN_VTBUTTONS 96 /* VT100 Button Hack */ +#define FX_VTBUTTONS 96 +#define FN_DESCRIBE 97 /* Describe */ +#define FX_DESCRIBE 97 +#define FN_SKMAC 100 /* Start Kbd Macro */ +#define FX_SKMAC 100 +#define FN_EKMAC 101 /* End Kbd Macro */ +#define FX_EKMAC 101 +#define FN_XKMAC 102 /* Execute Kbd Macro */ +#define FX_XKMAC 102 +#define FN_VKMAC 103 /* View Kbd Macro */ +#define FX_VKMAC 103 +#define FN_UNKILL 105 /* Un-kill */ +#define FX_UNKILL 105 +#define FN_UNKPOP 106 /* Un-kill Pop */ +#define FX_UNKPOP 106 +#define FN_APPNKILL 107 /* Append Next Kill */ +#define FX_APPNKILL 107 +#define FN_SRCH 110 /* String Search */ +#define FX_SRCH 110 +#define FN_RSRCH 111 /* Reverse String Search */ +#define FX_RSRCH 111 +#define FN_ISRCH 112 /* Incremental Search */ +#define FX_ISRCH 112 +#define FN_RISRCH 113 /* Reverse Search */ +#define FX_RISRCH 113 +#define FN_REPSTR 114 /* Replace String */ +#define FX_REPSTR 114 +#define FN_QUEREP 115 /* Query Replace */ +#define FX_QUEREP 115 +#define FN_REPLINE 116 /* Replace in Line */ +#define FX_REPLINE 116 +#define FN_SFCOL 117 /* Set Fill Column */ +#define FX_SFCOL 117 +#define FN_SFPREF 118 /* Set Fill Prefix */ +#define FX_SFPREF 118 +#define FN_FILLMODE 119 /* Auto Fill Mode */ +#define FX_FILLMODE 119 +#define FN_TEXTMODE 120 /* Text Mode */ +#define FX_TEXTMODE 0 +#define FN_INDATM 121 /* Indent According to Mode */ +#define FX_INDATM 121 +#define FN_INDNL 122 /* Indent New Line */ +#define FX_INDNL 122 +#define FN_BACKIND 123 /* Back to Indentation */ +#define FX_BACKIND 123 +#define FN_INDCOMM 124 /* Indent for Comment */ +#define FX_INDCOMM 124 +#define FN_INDREL 125 /* Indent Relative */ +#define FX_INDREL 125 +#define FN_MATCHBRACK 129 /* Match Bracket */ +#define FX_MATCHBRACK 0 +#define FN_PSHINF 130 /* Push to Inferior */ +#define FX_PSHINF 130 +#define FN_RETSUP 131 /* Return to Superior */ +#define FX_RETSUP 131 +#define FN_WFEXIT 132 /* Write File Exit */ +#define FX_WFEXIT 132 +#define FN_BKPT 140 /* Hit Breakpoint */ +#define FX_BKPT 140 +#define FN_DEBUG 141 /* Debug Mode */ +#define FX_DEBUG 141 +#define FN_XUCMD 150 /* Execute Unix Command */ +#define FX_XUCMD 0 +#define FN_MAKE 151 /* Execute Make */ +#define FX_MAKE 0 +#define FN_NXTERR 152 /* Find Next Error */ +#define FX_NXTERR 0 +#define FN_ICOXCMD 153 /* ICO Extend Command */ +#define FX_ICOXCMD 0 +#define FN_ICOTYPFNS 154 /* ICO Typeset Funs */ +#define FX_ICOTYPFNS 0 +#define FN_ICOSPIFNS 155 /* ICO Spec Input Funs */ +#define FX_ICOSPIFNS 0 +#define FN_STUFFSEL 156 /* Stuff Selection */ +#define FX_STUFFSEL 0 +#define FN_SELREGION 157 /* Select Region */ +#define FX_SELREGION 0 diff --git a/commands/elle/eefile.c b/commands/elle/eefile.c new file mode 100755 index 000000000..ff4fe4aff --- /dev/null +++ b/commands/elle/eefile.c @@ -0,0 +1,826 @@ +/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* + * EEFILE File reading/writing functions + */ + +#include "elle.h" +#include /* Use "standard" I/O package for writing */ +#ifndef BUFSIZ +#define BUFSIZ BUFSIZE /* Some places use wrong name in stdio.h */ +#endif /*-BUFSIZ*/ +#if V6 + struct stat { + int st_dev; + int st_ino; + char *st_mode; + char st_nlink; + char st_uid; + char st_gid; + char st_size0; + char st_size; + int st_addr[8]; + long st_atime; + long st_mtime; + }; +#define ENOENT (2) /* Syscall error - no such file or dir */ +#else +#include +#include +#include +#endif /*-V6*/ + +#if TOPS20 +#include /* Get open mode bits */ +#endif + +extern char *strerror(); /* Return error string for errno */ +extern struct buffer *make_buf(), *find_buf(); + +char *fncons(), *last_fname(); + +int hoardfd = -1; /* Retain a FD here to ensure we can always write */ + +/* Flags for iwritfile() */ +#define WF_SMASK 07 /* Source Mask */ +#define WF_SBUFF 0 /* source: Buffer */ +#define WF_SREG 1 /* source: Region */ +#define WF_SKILL 2 /* source: Last Kill */ +#define WF_ASK 010 /* Ask for filename to write to */ +static int iwritfile(); + +/* EFUN: "Find File" */ +/* Ask user for a filename and do a find_file for it. + * If buffer exists for that filename, select that buffer. + * Else create a buffer for it, and read in the file if it exists. + */ +f_ffile() +{ int find_file(); +#if IMAGEN + hack_file("Visit file: ", find_file); +#else + hack_file("Find file: ", find_file); +#endif /*-IMAGEN*/ +} + +/* EFUN: "Read File" */ +/* User read_file function, asks user for a filename and reads it + */ +f_rfile() { u_r_file("Read file: "); } + +/* EFUN: "Visit File" */ +/* Same as Read File, with different prompt. + */ +f_vfile() { u_r_file("Visit file: "); } + + +u_r_file(prompt) +char *prompt; +{ register char *f_name; + register struct buffer *b; + + if((f_name = ask (prompt))==0) /* prompt user for filename */ + return; /* user punted... */ + b = cur_buf; + if(*f_name == '\0') + { if (b -> b_fn == 0) + ding("No default file name."); + else read_file(b -> b_fn); + } + else read_file(f_name); + chkfree(f_name); +} + +/* EFUN: "Insert File" */ +/* Asks for a filename and inserts the file at current location. + * Point is left at beginning, and the mark at the end. + */ +f_ifile() +{ int ins_file(); + hack_file("Insert file: ", ins_file); +} + +/* EFUN: "Save File" */ +/* Save current buffer to its default file name + */ +f_sfile() +{ if(cur_buf->b_flags&B_MODIFIED) + return(iwritfile(WF_SBUFF)); /* Write buffer, don't ask */ + else + { saynow("(No changes need to be written)"); + return(1); + } +} + +#if FX_SAVEFILES || FX_WFEXIT +/* EFUN: "Save All Files" */ +/* F_SAVEFILES - Offer to save all modified files. + * With argument, doesn't ask. + * Returns 0 if user aborts or if an error happened. + */ +f_savefiles() +{ register struct buffer *b, *savb; + register int res = 1; + char *ans; + + savb = cur_buf; + for (b = buf_head; res && b; b = b->b_next) + if ((b->b_flags & B_MODIFIED) && b->b_fn) + { if(exp_p) /* If arg, */ + { chg_buf(b); /* just save, don't ask */ + res = f_sfile(); + continue; /* Check next buffer */ + } + /* Ask user whether to save */ + ans = ask("Buffer %s contains changes - write out? ", + b->b_name); + if(ans == 0) + { res = 0; /* User aborted */ + break; + } + if (upcase(*ans) == 'Y') + { chg_buf(b); + res = f_sfile(); /* Save File */ + } + chkfree(ans); + } + chg_buf(savb); + return(res); +} +#endif /*FX_SAVEFILES||FX_WFEXIT*/ + +/* EFUN: "Write File" */ +/* Write out the buffer to an output file. + */ +f_wfile() +{ return iwritfile(WF_ASK|WF_SBUFF); +} + +/* EFUN: "Write Region" */ +/* Write region out to a file + */ +f_wreg() +{ return iwritfile(WF_ASK|WF_SREG); /* Ask, write region */ +} + +#if FX_WLASTKILL +/* EFUN: "Write Last Kill" (not EMACS) */ +/* Write current kill buffer out to a file. +** This is mainly for MINIX. +*/ +extern int kill_ptr; /* From EEF3 */ +extern SBSTR *kill_ring[]; + +f_wlastkill() +{ return iwritfile(WF_ASK|WF_SKILL); +} +#endif + + +/* HACK_FILE - intermediate subroutine + */ +hack_file(prompt, rtn) +char *prompt; +int (*rtn)(); +{ register char *f_name; + + if((f_name = ask(prompt)) == 0) + return; + if (*f_name != '\0') /* Check for null answer */ + (*rtn)(f_name); + chkfree(f_name); +} + +/* FIND_FILE(f_name) + * If there is a buffer whose fn == f_name, select that buffer. + * Else create one with name of the last section of f_name and + * read the file into that buffer. + */ +find_file(f_name) +register char *f_name; +{ register struct buffer *b; + register char *ans; + char *lastn; + int fd; + +#if IMAGEN + char real_name[128]; /* File name w/ expanded ~ and $ */ + expand_file(real_name, f_name); + f_name = real_name; +#endif /*IMAGEN*/ + + for (b = buf_head; b; b = b -> b_next) + if(b->b_fn && (strcmp (b -> b_fn, f_name) == 0)) + break; + if (b) /* if we found one */ + { sel_buf(b); /* go there */ + return; /* and we are done */ + } + if((fd = open(f_name,0)) < 0) /* See if file exists */ + { if(errno != ENOENT) /* No, check reason */ + { ferr_ropn(); /* Strange error, complain */ + return; /* and do nothing else. */ + } + } + else close(fd); /* Found! Close FD, since the */ + /* read_file rtn will re-open. */ + + lastn = last_fname(f_name); /* Find buffer name */ + b = find_buf(lastn); /* Is there a buffer of that name? */ + if (b && (ex_blen(b) || b->b_fn)) + { ans = ask("Buffer %s contains %s, which buffer shall I use? ", + b -> b_name, b->b_fn ? b->b_fn : "something"); + if(ans == 0) return; /* Aborted */ + if (*ans != '\0') /* if null answer, use b */ + b = make_buf(ans); /* else use ans */ + chkfree(ans); + } + else + b = make_buf(lastn); + sel_buf(b); + if(fd < 0) /* If file doesn't exist, */ + { set_fn(f_name); /* just say "new" and set filename */ + return; /* and return right away. */ + } + if (read_file(f_name)==0) /* File exists, read it in! */ + { if(b->b_fn) /* Failed... if filename, */ + { chkfree(b->b_fn); /* flush the filename. */ + b->b_fn = 0; + } + } +} + +/* READ_FILE(f_name) + * Reads file into current buffer, flushing any + * previous contents (if buffer modified, will ask about saving) + * Returns 0 if failed. + */ +read_file(f_name) +char *f_name; +{ +#if IMAGEN + struct stat s; + char real_name[128]; /* File name w/ expanded ~ and $ */ +#endif /*IMAGEN*/ + + if(!zap_buffer()) /* Flush the whole buffer */ + return; /* Unless user aborts */ +#if IMAGEN + expand_file(real_name, f_name); + f_name = real_name; /* Hack, hack! */ +#endif /*IMAGEN*/ + set_fn(f_name); + if (ins_file(f_name)==0) + return 0; + f_bufnotmod(); /* Say not modified now */ +#if IMAGEN + stat(f_name, &s); /* Get file stat */ + cur_buf->b_mtime = s.st_mtime; /* and pick out last-modified time */ +#endif /*IMAGEN*/ + return 1; +} + +/* INS_FILE(f_name) + * Inserts file named f_name into current buffer at current point + * Point is not moved; mark is set to end of inserted stuff. + * Returns 0 if failed, 1 if won. + */ +ins_file (f_name) +char *f_name; +{ register int ifd; + register SBSTR *sd; + chroff insdot; /* To check for range of mods */ + +#if IMAGEN + char real_name[128]; /* File name w/ expanded ~ and $ */ + expand_file(real_name, f_name); + f_name = real_name; +#endif /*IMAGEN*/ +#if !(TOPS20) + if((ifd = open(f_name,0)) < 0) +#else + if((ifd = open(f_name,O_RDONLY|O_UNCONVERTED)) < 0) +#endif /*TOPS20*/ + { ferr_ropn(); /* Can't open, complain */ + return 0; /* no redisplay */ + } + errno = 0; + if((sd = sb_fduse(ifd)) == 0) + { if (ifd >= SB_NFILES) + dingtoo(" Cannot read - too many internal files"); + else if (errno) + ferr_ropn(); + else errbarf("SB rtn cannot read file?"); + close(ifd); + return 0; + } + sb_sins(cur_buf,sd); + insdot = e_dot(); + f_setmark(); /* Set mark at current ptr */ + if(cur_dot != insdot) /* If pointer was advanced, */ + buf_tmat(insdot); /* then stuff was inserted */ + e_gocur(); + return 1; +} + +ferr_ropn() { ferr(" Cannot read"); } +ferr_wopn() { ferr(" Cannot write"); } +ferr(str) +char *str; +{ saytoo(str); + saytoo(" - "); + dingtoo(strerror(errno)); +} + + +/* IWRITFILE - auxiliary for writing files. +** Returns 1 if write successful, 0 if not. +*/ +static int +iwritfile(flags) +int flags; +{ register struct buffer *b; + register char *o_name; /* output file name */ + int styp = flags & WF_SMASK; /* Source type, one of WF_Sxxx */ + char *prompt; +#ifdef STDWRITE + register FILE *o_file; /* output file pointer */ + char obuf[BUFSIZ]; + chroff dotcnt; +#endif /*STDWRITE*/ + int ofd; /* output file FD */ + SBSTR *sd; + char fname[FNAMSIZ]; /* To avoid chkfree hassle */ + char newname[FNAMSIZ]; /* for robustness */ + char oldname[FNAMSIZ]; /* ditto */ + int res; + struct stat statb; + int statres; +#if IMAGEN + struct stat s; + char real_name[128]; /* File name w/ expanded ~ and $ */ +#endif /*IMAGEN*/ + res = 1; /* Let's keep track of success */ + + /* Check for existence of source, and set prompt string */ + switch(styp) + { + case WF_SBUFF: + prompt = "Write File: "; + break; + case WF_SREG: + if(!mark_p) + { dingtoo(" No Mark!"); + return(0); + } + prompt = "Write Region: "; + break; +#if FX_WLASTKILL + case WF_SKILL: + if(!kill_ring[kill_ptr]) + { dingtoo("No killed stuff"); + return(0); + } + prompt = "Write Last Kill: "; + break; +#endif + default: /* Internal error */ + errbarf("bad iwritfile arg"); + return 0; + } + + if (flags&WF_ASK) + { if((o_name = ask(prompt))==0) + return(0); /* User punted. */ + strcpy(&fname[0], o_name); /* Copy filename onto stack */ + chkfree(o_name); + } + o_name = &fname[0]; + b = cur_buf; + if (!(flags&WF_ASK) || (*o_name == '\0')) + { if (b->b_fn == 0) + { ding("No default file name."); + return(0); + } + strcpy(o_name, b->b_fn); + } + +#if IMAGEN + expand_file(real_name, o_name); + o_name = real_name; /* Hack, hack */ +#endif /*IMAGEN*/ + + statres = stat(o_name,&statb); /* Get old file's info (if any) */ + +#if IMAGEN + /* Now, make sure someone hasn't written the file behind our backs */ + if ((styp==WF_SBUFF) && !(flags&WF_ASK) + && b->b_fn && stat(b->b_fn, &s) >= 0) + if (s.st_mtime != b->b_mtime) + { char *ans; + ans = ask("Since you last read \"%s\", someone has changed it.\nDo you want to write it anyway (NOT RECOMMENDED!)? ", + b->b_fn); + if (ans == 0 || upcase(*ans) != 'Y') + { + ding("I suggest you either read it again, or\nwrite it to a temporary file, and merge the two versions manually."); + if (ans) chkfree(ans); + return(0); + } + if (ans) chkfree(ans); + } +#endif /*IMAGEN*/ + + /* Try to get around major UNIX screw of smashing files. + * This still isn't perfect (screws up with long filenames) but... + * 1. Write out to + * 2. Rename to (may have to delete existing ) + * 3. Rename to . + */ + fncons(oldname,ev_fno1,o_name,ev_fno2); /* Set up "old" filename */ + fncons(newname,ev_fnn1,o_name,ev_fnn2); /* Set up "new" filename */ + unlink(newname); /* Ensure we don't clobber */ + unhoard(); /* Now give up saved FD */ +#if !(V6) /* Standard V6 doesn't have access call */ + if(statres >= 0) /* If file exists, */ + { if(access(o_name, 2) != 0) /* check for write access */ + { ferr_wopn(); + res = 0; /* Failure */ + goto wdone; + } + } +#endif /*-V6*/ +#ifdef STDWRITE + if(flags&WF_ASK) + { if((o_file = fopen(newname, "w")) ==0) /* Create new output file */ + { ferr_wopn(); + res = 0; /* Failure */ + goto wdone; + } + setbuf(o_file,obuf); /* Ensure always have buffer */ + } + else /* New stuff */ +#endif /*STDWRITE*/ + { +#if !(TOPS20) + if((ofd = creat(newname,ev_filmod)) < 0) +#else + if((ofd = open(newname,O_WRONLY|O_UNCONVERTED)) < 0) +#endif /*TOPS20*/ + { ferr_wopn(); + res = 0; /* Failure */ + goto wdone; + } + } + if (styp==WF_SBUFF) + set_fn(o_name); /* Won, so set default fn for buff */ +#if IMAGEN + saynow("Writing "); + switch(styp) + { case WF_SBUFF: saytoo(b->b_fn); break; + case WF_SREG: saytoo("region"); break; +#if FX_WLASTKILL + case WF_SKILL: saytoo("last kill"); break; +#endif + } + sayntoo("..."); +#else + saynow("Writing..."); +#endif /*-IMAGEN*/ + +#if !(TOPS20) /* T20 does all this already */ + if(statres >= 0) /* Get old file's modes */ + { /* Try to duplicate them */ + /* Do chmod first since after changing owner we may not + ** have permission to change mode, at least on V6. + */ + chmod(newname,statb.st_mode & 07777); +#if V6 + chown(newname, (statb.st_gid<<8)|(statb.st_uid&0377)); +#else + chown(newname,statb.st_uid,statb.st_gid); +#endif /*-V6*/ + } +#if V6 + /* If no old file existed, and we are a V6 system, try to set + * the modes explicitly. On V7 we're OK because the user can + * diddle "umask" to get whatever is desired. + * On TOPS-20 of course everything is all peachy. + */ + else chmod(newname, ev_filmod); +#endif /*V6*/ +#endif /*TOPS20*/ + + +#ifdef STDWRITE + if(flags&WF_ASK) + { switch(styp) + { + case WF_SBUFF: + dotcnt = e_blen(); + e_gobob(); + break; + case WF_SREG: + if((dotcnt = mark_dot - cur_dot) < 0) + { e_goff(dotcnt); + dotcnt = -dotcnt; + } + else e_gocur(); + break; + /* WF_SKILL not implemented here */ + } + while(--dotcnt >= 0) + putc(sb_getc(((SBBUF *)b)), o_file); + e_gocur(); + fflush(o_file); /* Force everything out */ + res = ferror(o_file); /* Save result of stuff */ + fclose(o_file); /* Now flush FD */ + } + else /* New stuff */ +#endif /*STDWRITE*/ + { + switch(styp) + { + case WF_SBUFF: + res = sb_fsave((SBBUF *)b, ofd); + break; + case WF_SREG: + e_gocur(); + sd = e_copyn((chroff)(mark_dot - cur_dot)); + res = sbx_aout(sd, 2, ofd); + sbs_del(sd); + break; +#if FX_WLASTKILL + case WF_SKILL: + res = sbx_aout(kill_ring[kill_ptr], 2, ofd); + break; +#endif + } + close(ofd); + } + if(errno = res) + { ferr(" Output error"); + res = 0; /* Failure */ + goto wdone; + } + else + res = 1; /* Success so far */ + if(styp == WF_SBUFF) + f_bufnotmod(); /* Reset "buffer modified" flag */ + + /* Here we effect the screw-prevention steps explained earlier. */ + /* TOPS-20, with generation numbers, need not worry about this. */ +#if TOPS20 + saynow("Written"); + +#else /*-TOPS20*/ +#if IMAGEN /* KLH -- This conditional bracketting is prone to lossage */ + /* Only create the .BAK file once per editing session!! */ + if ((styp==WF_SBUFF) || !(b->b_flags & B_BACKEDUP)) + { if (styp==WF_SBUFF) + b->b_flags |= B_BACKEDUP; +#endif /*IMAGEN*/ + unlink(oldname); /* remove any existing "old" file */ + if(link(o_name,oldname) == 0) /* Rename current to "old" */ + unlink(o_name); + /* Here is the critical point... if we stop here, there is no + * longer any file with the appropriate filename!!! + */ +#if IMAGEN + } + else + unlink(o_name); +#endif /*IMAGEN*/ + if(link(newname,o_name) == 0) /* Rename "new" to current */ + { unlink(newname); +#if IMAGEN + sayntoo("OK"); +#else + saynow("Written"); +#endif /*-IMAGEN*/ + } + else + { dingtoo("rename error!"); + res = 0; + } +#endif /*-TOPS20*/ + +#if IMAGEN + /* Update the last-modified time for the file in this buffer */ + if ((styp == WF_SBUFF) && b->b_fn) + { stat(b->b_fn, &s); + b->b_mtime = s.st_mtime; + } +#endif /*IMAGEN*/ + +wdone: + hoard(); /* Get back a retained FD */ + return(res); +} + +/* FNCONS(dest,pre,f_name,post) + * Specialized routine to cons up a filename string into "dest", + * given prefix and postfix strings to be added onto last component of + * filename. + */ +char * +fncons(dest, pre, f_name, post) +char *dest,*pre,*f_name,*post; +{ register char *cp, *cp2; + char *last_fname(); + + cp = dest; + *cp = 0; /* Make dest string null initially */ + cp2 = last_fname(f_name); /* Get pointer to beg of last name */ + strncat(cp,f_name,cp2-f_name); /* Copy first part of filename */ + if(pre) strcat(cp, pre); /* If prefix exists, add it on */ + cp = last_fname(cp); /* Recheck in case levels added */ + strcat(cp, cp2); /* Now add last name */ + if(cp2 = post) /* If there's a postfix, must check */ + { cp[FNAMELEN-strlen(cp2)] = 0; /* and cut dest so postfix */ + strcat(cp, cp2); /* will fit on end. */ + } + return(dest); +} + +/* LAST_FNAME(string) + * Get the last component of a file name. Returns pointer to + * start of component; does NOT copy string! + */ +char * +last_fname(f_name) +char *f_name; +{ register char *cp, *p; + register int c; + + p = f_name; /* pointer to last slash */ + cp = p; + while(c = *cp++) + if(c == '/') + p = cp; /* point to after the slash */ + return(p); +} + +/* SET_FN(string) + * Set the default filename for current buffer to "string". + */ +set_fn (string) +char *string; +{ register struct buffer *b; + register char *str; +#if IMAGEN + register char *cp; + register int len; +#endif /*IMAGEN*/ + char *strdup(); + + b = cur_buf; + str = strdup(string); /* Copy now in case copying self */ + if(b->b_fn) + chkfree(b->b_fn); + b -> b_fn = str; +#if IMAGEN + /* Do mode determination based on file name (HACK HACK) */ + len = strlen(str); + b->b_flags &= ~(B_CMODE|B_TEXTMODE); + if (len > 4) + { if (strcmp(&str[len - 5], "draft") == 0) + b->b_flags |= B_TEXTMODE; + else + { cp = &str[len - 4]; + if (strcmp(cp, ".txt") == 0 || + strcmp(cp, ".mss") == 0) + b->b_flags |= B_TEXTMODE; + } + } + if (len > 2) + { cp = &str[len - 2]; + if (strcmp(cp, ".h") == 0 || strcmp(cp, ".c") == 0) + b->b_flags |= B_CMODE; + } +#endif /*IMAGEN*/ + redp(RD_MODE); +} + +/* SAVEWORLD - Attempt to save all changes user has made. + * Currently this amounts to writing out all modified buffers + * to the files $HOME/+buffername. If a buffer is given as argument, + * only that buffer is saved. + * This is only called from the error handling routines with + * the TTY either gone or in normal (non-edit) mode. The "grunt" + * flag says whether to output feedback during the saving process. + */ +saveworld(bp, grunt) +struct buffer *bp; +int grunt; +{ register struct buffer *b; + register int wfd; + char sfname[FNAMSIZ]; + struct buffer *sel_mbuf(); + + unhoard(); /* Ensure a FD is free for writing */ + if(b = bp) goto once; + while(!bp && (b = sel_mbuf(b))) + { + once: strcat(strcat(strcpy(sfname,homedir),"/+"),b->b_name); + if(grunt) printf("Saving %s...",sfname); +#if !(TOPS20) + if((wfd = creat(sfname, ev_filmod)) < 0) +#else + if((wfd = open(sfname,O_WRONLY|O_UNCONVERTED)) < 0) +#endif /*TOPS20*/ + { if(grunt) + printf(" error - %s\n", strerror(errno)); + } + else + { sb_fsave((SBBUF *)b, wfd); + close(wfd); + if(grunt) printf("\n"); + } + b->b_flags &= ~B_MODIFIED; + } + hoard(); +} + +/* HOARD, UNHOARD - Routines to save a FD for writing, to make sure + * that we can always write out a buffer no matter how many + * file descriptors we are currently using. + */ +hoard() /* Stash away a FD */ +{ if(hoardfd <= 0) +#if !(TOPS20) + hoardfd = open("nul:", 1); +#else + hoardfd = open("/dev/null", 1); +#endif +} +unhoard() /* Give up our stashed FD so it can be re-used */ +{ close(hoardfd); + hoardfd = -1; +} + +#if IMAGEN +#include +#include + +/* + * expand_file: expand any ~user-name/ or $env-var/ prefixes in sfn, + * producing the full name in dfn + */ +expand_file(dfn, sfn) +register char *dfn, *sfn; +{ + register char *sp, *tp; + register int c; + register struct passwd *pw; + char ts[128]; + + /* HORRIBLE, GROSS, DISGUSTING HACK: if the destination and + * source strings are identical (same pointer), then do not + * do any expansion--this happens to work with the current + * structure very well, since multiple expansions may happen. + */ + if (dfn == sfn) + return; + + ts[0] = 0; + + /* If have a leading $, then expand environment variable */ + if (*sfn == '$') + { ++sfn; + tp = ts; + while (*tp++ = *sfn) + if (!isalnum(*sfn)) + break; + else + ++sfn; + *--tp = 0; /* Just in case */ + strcpy(ts, getenv(ts)); /* MARGINAL!! */ + } + /* If have leading ~, then expand login name (null means $HOME) */ + else if (*sfn == '~') + { ++sfn; + if (*sfn == '/' || *sfn == 0) + strcpy(ts, getenv("HOME")); + else + { tp = ts; + while (*sfn && *sfn != '/') + *tp++ = *sfn++; + *tp = 0; + pw = (struct passwd *)getpwnam(ts); + if (! pw) + strcpy(ts, "???"); + else + strcpy(ts, pw->pw_dir); + } + } + + /* Now, ts is either empty or contains the expansion; + * sfn has been updated correctly. + */ + strcpy(dfn, ts); + strcat(dfn, sfn); +} +#endif /*IMAGEN*/ diff --git a/commands/elle/eefill.c b/commands/elle/eefill.c new file mode 100755 index 000000000..f46a6506a --- /dev/null +++ b/commands/elle/eefill.c @@ -0,0 +1,642 @@ +/* ELLE - Copyright 1982, 1985, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* + * EEFILL Fill Mode functions + */ + +#include "elle.h" + +extern int ev_fcolumn; /* Fill Column variable (defined in EEVINI) */ +#if FX_SFPREF +char *fill_prefix; /* Fill Prefix variable */ +int fill_plen; /* Length of Fill Prefix (0 = no prefix) */ +#endif /*FX_SFPREF*/ + +#if FX_FILLMODE +int fill_mode = 0; /* TRUE when Auto Fill Mode is on */ +int *fill_trig; /* Pointer to fill-trigger chbit array */ +static char *fill_initrig = " \t.,;:)!"; +#endif /*FX_FILLMODE*/ + +/* Following stuff for testing routines on */ +/* + + 1 2 3 4 5 6 7 +0123456789012345678901234567890123456789012345678901234567890123456789012345 + +Okay... more stuff to hack. Okay. 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 dfsd stuff to hack 01234 Okay testing +more stuff to hack. Okay... more stuff to hack more stuff to +hack. Okay... more stuff to line long stuff to hack. Okay... +even more gap and. period. okay, end of stuff. + This is another fence. +*/ + + +#if FX_SFCOL +/* EFUN: "Set Fill Column" */ +f_sfcol() +{ register int linel; + char temp[20]; + + linel = exp_p ? exp : d_curind(); + if(linel < 0) linel = 0; + say("Fill column = "); + dottoa(temp,(chroff)linel); + saytoo(temp); + ev_fcolumn = linel; +} +#endif /*FX_SFCOL*/ + + +#if FX_SFPREF +/* EFUN: "Set Fill Prefix" */ +f_sfpref() +{ register int i; + register char *cp; + + if((i = cur_dot - e_boldot()) > MAXLINE) + { ding("Absurd Fill Prefix"); + return; + } + if(fill_prefix) + { chkfree(fill_prefix); + fill_plen = 0; + } + if(i <= 0) + { fill_prefix = 0; + cp = ""; + } + else + { fill_prefix = cp = memalloc((SBMO)(i+1)); + fill_plen = i; + e_gobol(); + do { *cp++ = e_getc(); } + while(--i); + *cp = 0; + cp = fill_prefix; + } + say("Fill Prefix = \""); + saytoo(cp); + saytoo("\""); +} + + +/* TSTFILLP(lim) - Check for existence of Fill Prefix at current dot. If + * not there, returns 0 without changing dot. If there, returns + * 1 and leaves dot immediately after the Fill Prefix. + * Lim = # of chars allowed to scan from buffer. + */ +tstfillp(lim) +int lim; +{ register int i, c; + register char *cp; + chroff savdot; + + if(!(i = fill_plen) || (i > lim)) + return(0); + savdot = e_dot(); + cp = fill_prefix; + do { if(*cp++ != e_getc()) + { e_go(savdot); + return(0); + } + } while(--i); + return(1); +} +#endif /*FX_SFPREF*/ + +#if FX_FILLREG || FX_FILLPARA + +/* ED_FILL(start, end, flag) - Fill a region. + * Flag 0 for full filling; extra whitespace is flushed. First + * word is always retained. + * 1 for skimpy filling such as Auto-Fill likes. + * Extra whitespace is NOT flushed, except at + * beginning of a newly created line. + * This is not yet implemented however. + * Note: updates cur_dot to compensate for changes in buffer, and returns + * there when done! + * Note: Checks for Fill Prefix when it exists. + */ +ed_fill(begloc, endloc, flag) +chroff begloc, endloc; +int flag; +{ register int c; + register int len, lastc; + chroff savloc; + int lastbrk; + int parlen; + + parlen = endloc - begloc; + if(parlen < 0) + { begloc = endloc; + parlen = -parlen; + } + e_go(begloc); + len = d_curind(); /* Set up current col */ + +#if FX_SFPREF + /* If at beg of line, check for fill prefix and skip over it */ + if((len == 0) && tstfillp(parlen)) + { parlen -= fill_plen; + len = d_curind(); + } +#endif /*FX_SFPREF*/ + lastbrk = 0; /* Put next word on no matter what. */ + c = 0; + for(;;) + { +#if ICONOGRAPHICS + if (c != ')' && c != '"') /* allow for two sp after .) or ." */ +#endif /*ICONOGRAPHICS*/ + lastc = c; + if(--parlen < 0) break; + c = e_getc(); + if(c == EOF) + break; +#if FX_SFPREF + /* If at beg of line, check for fill prefix and flush it */ + if((c == LF) && tstfillp(parlen)) + { e_igoff(-(fill_plen+1)); + e_ovwc(c = SP); + e_deln((chroff)fill_plen); + parlen -= fill_plen; + if(cur_dot >= e_dot()) + cur_dot -= fill_plen; + } +#endif /*FX_SFPREF*/ + if(c == TAB || c == LF) /* Replace tabs+eols by sps */ + { e_backc(); /* Back up 1 */ + e_ovwc(c = SP); + } + if(c == SP) + { if(lastc == SP) + { e_rdelc(); + if(cur_dot > e_dot()) --cur_dot; + continue; + } + lastbrk = len; + if(lastc == '.' || lastc == '!' || lastc == '?' +#if ICONOGRAPHICS + || lastc == ':' +#endif /*ICONOGRAPHICS*/ + ) + { if(--parlen < 0) goto done; + if((c = e_getc()) == EOF) + goto done; + len++; + if(c != SP) + { e_backc(); + e_putc(c = SP); + if(cur_dot >= e_dot()) ++cur_dot; + } + } + } +#if ICONOGRAPHICS + if (c == BS) /* adjust for backspaces */ + if ((len -= 2) < 0) len = 0; +#endif /*ICONOGRAPHICS*/ + /* Normal char */ + if(++len > ev_fcolumn && lastbrk) /* If went too far */ + { c = lastbrk - len; /* Must put EOL at last SP */ + e_igoff(c); + parlen -= c; /* C is negative, actually adding */ + parlen--; + e_ovwc(LF); + lastbrk = 0; + len = 0; + c = SP; /* Pretend this char was space */ +#if FX_SFPREF + if(fill_plen) + { if(cur_dot >= e_dot()) + cur_dot += fill_plen; + /* Better hope no nulls in prefix! */ + e_sputz(fill_prefix); + len = d_curind(); + } +#endif /*FX_SFPREF*/ + } + } +done: savloc = cur_dot; + e_setcur(); /* Reached paragraph end, set cur_dot temporarily */ + buf_tmod(begloc-cur_dot); /* So that proper range is marked */ + e_gosetcur(savloc); /* Then restore original cur_dot */ +} +#endif /*FX_FILLREG || FX_FILLPARA*/ + +#if FX_FILLMODE + +/* EFUN: "Auto Fill Mode" */ +/* Toggles Auto Fill Mode (a minor mode). */ +f_fillmode() +{ register char *cp; + int *chballoc(); + + fill_mode = fill_mode ? 0 : 1; + if(!fill_trig) + { fill_trig = chballoc(128); + for(cp = fill_initrig; *cp; ++cp) + chbis(fill_trig, *cp); + } + redp(RD_MODE); +} + +/* Called by F_INSSELF to handle char insertion in Auto Fill mode */ +fx_insfill(c) +int c; +{ + ed_insn(c,exp); + if(chbit(fill_trig, c)) + { fill_cur_line(); + + } +} + + +fill_cur_line() +{ + register int foundit, i; + chroff lastbrkdot, boldot, eoldot; + + boldot = e_boldot(); + + /* First back up to find place to make first break. */ + e_bwsp(); + lastbrkdot = e_dot(); + foundit = 0; + for(foundit = 0; foundit >= 0;) + { if((i = d_curind()) <= ev_fcolumn) + { if(foundit) + foundit = -1; + else break; + } + else ++foundit; + while (!c_wsp (e_rgetc ())) ; + e_bwsp(); + lastbrkdot = e_dot(); + if(lastbrkdot <= boldot) + { lastbrkdot = boldot; + break; + } + } + + if(foundit) + ed_fill(lastbrkdot, e_eoldot(), 1); +} +#endif /*FX_FILLMODE*/ + +#if IMAGEN + +#if FX_TEXTMODE +/* EFUN: "Text Mode Toggle" (not EMACS) */ +f_textmode() +{ + cur_buf->b_flags ^= B_TEXTMODE; + redp(RD_MODE); +} +#endif /*FX_TEXTMODE*/ + +int curr_indent = -1; /* Current indent (for text mode autowrap) */ + /* (misnomered: actually current column) */ +chroff best_break; /* Best break point so far */ + + +/* Fill-mode version of "Insert Self" */ + +fim_insself(c) +int c; +{ + register int ind, flags = cur_buf->b_flags; + + /* In Text mode, auto-wrap happens at spaces after fill column */ + if (c == SP && flags & B_TEXTMODE && exp == 1 && magic_wrap(c)) + return; + + /* In C-mode, tab stops are every 4 columns */ + else if (c == TAB && flags & B_CMODE && + (ind = magic_backto_bol()) >= 0) + ed_indto((ind + 4) & ~3); + else + { ed_insn(c, exp); + + /* Keep track of indent, once we have a grip on it */ + if (last_cmd == INSCMD && curr_indent != -1) + { this_cmd = INSCMD; /* Keep the ball rolling */ + if (c == TAB) + curr_indent = ((curr_indent + 8) & ~7) + + 8 * (exp - 1); + else if (c == '\n') + curr_indent = 0; + else if (c < SP || c > 0176) + curr_indent += (2 * exp); + else + curr_indent += exp; + } + } +} + +/* Fill-mode version of "Delete Character" */ + +fim_dchar() +{ /* In C mode, deleting at BOL should do fake TAB preservation */ + if (cur_buf->b_flags & B_CMODE) + { chroff savdot; + register int c, indent; + + if (e_rgetc() != LF) + { /* Only hack this at BOL */ + e_getc(); + goto normal; + } + e_getc(); + savdot = e_dot(); + indent = 0; + while ((c = e_getc()) == SP || c == TAB) + if (c == SP) + ++indent; + else + indent = (indent + 8) & ~7; + e_rgetc(); + if (indent >= 4) + { ed_delete(savdot, e_dot()); + ed_indto((indent - 4) & ~3); + f_begline(); /* HACK!!!! */ + } + else + { e_go(savdot); + ef_deln(exp); + } + } + else + normal: return (ef_deln(exp)); +} + +/* Fill-mode version of "Backward Delete Character" */ + +fim_bdchar() +{ register int ind; + + /* If in C mode, and deleting into white space at BOL, hack tabs */ + if (exp == 1 && cur_buf->b_flags & B_CMODE && + (ind = magic_backto_bol()) > 0) + ed_indto(ind < 4 ? ind - 1 : ((ind - 4) & ~3)); + else + return (ef_deln (-exp)); +} + +/* Fill-mode version of "CRLF" */ +fim_crlf() +{ register int i; + + if(e_getc() == LF + && exp == 1 + && e_lblankp() && e_lblankp()) + { e_gocur(); + e_gonl(); + e_setcur(); + ed_delete(e_dot(), e_eoldot()); + } + else + { e_gocur(); +#if IMAGEN + if (cur_buf->b_flags & B_TEXTMODE && exp == 1 && + magic_wrap('\n')) + return; + else +#endif /*IMAGEN*/ + if((i = exp) > 0) + do ed_crins(); + while(--i); + } +} + +/* Do all magic for auto-wrap in Text mode: + * return as did wrap (i.e., everything is taken care of) + */ +magic_wrap(tc) +int tc; /* "trigger char" */ +{ + register int c, indent, i, nc; + chroff savdot, modstart, breakdot; + + savdot = e_dot(); + nc = 0; + if (last_cmd == INSCMD && curr_indent != -1) + { indent = curr_indent; /* Already know our indent */ + breakdot = best_break; + } + else + { +#ifdef INDENTDEBUG + barf2("Full indent calculation"); +#endif + for (nc = 0; (c = e_rgetc()) != EOF && c != '\n'; ++nc) + ; /* nc: # chars to look at */ + if (c == '\n') /* Go back over NL */ + e_getc(); + indent = 0; + + /* Search for last line break point, leaving it in breakdot */ + breakdot = (chroff)0; + while (--nc >= 0) + { c = e_getc(); + if (c == TAB) + indent = (indent + 8) & ~7; + else if (c < SP || c > 0176) + indent += 2; + else + ++indent; + if ((c == SP || c == TAB) && + (breakdot == (chroff)0 || (indent <= ev_fcolumn))) + breakdot = e_dot(); + } + } + + /* If there is nothing to do, get out */ + if (indent <= ev_fcolumn) + { e_go(savdot); + if (tc == SP) + { curr_indent = indent; + best_break = (chroff)(savdot + 1); /* Remember here, also */ + this_cmd = INSCMD; /* We do know current indent */ + } + else if (tc == '\n') + { curr_indent = 0; + best_break = (chroff)0; + this_cmd = INSCMD; + } + else + errbarf("bad trigger"); + return(0); + } + + if (breakdot == (chroff)0) + { + /* No breakpoint found or none needed, just break line at end + */ + e_go(savdot); + modstart = savdot; + e_putc('\n'); + } + else + { + /* Get to breakpoint and replace with newline + */ + e_go(breakdot); + e_rdelc(); + modstart = e_dot(); /* Remember where changes start */ + e_putc('\n'); /* Insert line break */ + e_go(savdot); /* Get back to trigger point */ + } + if (e_rgetc() != '\n') + { /* If not at line start, */ + e_getc(); + e_putc(tc); /* insert trigger character */ + + /* Once again, compute new indent by backing up to BOL */ + for (nc = 0; (c = e_rgetc()) != EOF && c != '\n'; ++nc) + ; + if (c == '\n') /* Go back over NL */ + e_getc(); + indent = 0; + breakdot = (chroff)0; + while (--nc >= 0) + { /* Get back to current dot */ + c = e_getc(); + if (c == TAB) + indent = (indent + 8) & ~7; + else if (c < SP || c > 0176) + indent += 2; + else + ++indent; + if ((c == SP || c == TAB) && + (breakdot == (chroff)0 || (indent <= ev_fcolumn))) + breakdot = e_dot(); + } + if (breakdot == (chroff)0) /* If no good break found, use dot */ + breakdot = e_dot(); + curr_indent = indent; /* Now we know where we are */ + if (tc == '\n') /* If trigger was NL */ + best_break = (chroff)0; /* indent is 0, and no best break */ + else + best_break = breakdot; /* This is best break so far */ + } + else + { e_getc(); + curr_indent = 0; /* At line start, no indent */ + best_break = (chroff)0; /* Do not have a best break so far */ + } + ed_setcur(); + buf_tmat(modstart); /* Alert to potential changes */ + this_cmd = INSCMD; /* Say we know where we are */ + return(1); +} + +/* Do lots of magic things for C-mode indent: + * erase back to BOL iff we are looking back at white space only, + * returning the indent level of the original dot + * (< 0 means no erasure done) + */ +/*#define MYDEBUG /* */ +#ifdef MYDEBUG +reveal(msg, v1, v2, v3) +char *msg; +{ + char ahint[128]; + sprintf(ahint, msg, v1, v2, v3); + barf2(ahint); +} +#endif + +magic_backto_bol() +{ + chroff savdot; + register int c, indent, nc, i; + + savdot = e_dot(); + nc = 0; + while ((c = e_rgetc()) != EOF && c != LF) + { ++nc; /* Count # chars */ + if (c != SP && c != TAB) + { e_go(savdot); +#ifdef MYDEBUG + reveal("fail: nc: %d", nc); +#endif + return -1; + } + } + if (c == LF) /* Go back over the LF */ + e_getc(); + indent = 0; /* (zero-based indent) */ + savdot = e_dot(); /* BOL is now origin for delete */ + for (i = 1; i <= nc; ++i) + if ((c = e_getc()) == SP) + ++indent; + else /* (tab) */ + indent = (indent + 8) & ~7; + if (nc > 0) /* Don't bother deleting nothing */ + ed_delete(savdot, e_dot()); +#ifdef MYDEBUG + reveal("indent: %d, nc: %d, foo: %d", indent, nc, 234); +#endif + return(indent); +} +#endif /*IMAGEN*/ + +#if ICONOGRAPHICS +/* Iconographics hack for Auto-Fill mode. Too big and clumsy, but + * retained for posterity in case it has some obscure feature. + */ + +fill_current_line () +{ + chroff startpos, endpos, savepos, limitpos; + int i, foundit; + SBSTR *savep; + + foundit = 0; + while (d_curind() > ev_fcolumn) + { + foundit = 1; + startpos = e_dot (); + e_bwsp (); + while (d_curind() > ev_fcolumn) /* back up to ends of wds*/ + { /* until <= fill column */ + while (!c_wsp (e_rgetc ())) ; + e_bwsp (); + } + if (e_dot () == e_boldot ()) + { /* ding ("Word does not fit in fill column"); */ + return(0); + } + savep = e_copyn (startpos - e_dot ()); + e_setcur (); /* ed_delete does gocur */ + ed_delete (savepos = e_dot (), startpos); + + f_crlf(); /* Now insert newline */ + e_sputz(fill_prefix); /* With fill prefix */ + startpos += e_dot () - savepos; + if (d_curind() > ev_fcolumn) + { ed_delete (savepos, e_dot ()); + sb_sins (cur_buf, savep); + e_setcur (); + ding ("Fill prefix > fill column???"); + return(0); + } + savepos = e_dot (); /* gun inherited initial whitespace */ + sb_sins (cur_buf, savep); + e_go (savepos); + e_fwsp (); + if ((limitpos = e_dot ()) > startpos) limitpos = startpos; + /* in case rest of line was white */ + ed_delete (savepos, limitpos); + e_gosetcur (startpos + savepos - limitpos); + } + + return foundit; + } +#endif /*ICONOGRAPHICS*/ diff --git a/commands/elle/eehelp.c b/commands/elle/eehelp.c new file mode 100755 index 000000000..11dc42660 --- /dev/null +++ b/commands/elle/eehelp.c @@ -0,0 +1,81 @@ +/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* EEHELP - Help function + */ + +#include "elle.h" /* include structure definitions */ + + +#if FX_DESCRIBE +/* EFUN: "Describe" */ +/* DESCRIBE - Help-command hack. +** Crude approximation of EMACS function. +*/ +static struct buffer *help_buf; + +f_describe() +{ register char *cp; + register int i, c; + char str[10]; + struct buffer *savbuf, *b, *make_buf(); + chroff bdot; + + saynow("Help for command: "); + i = cmd_idx(c = cmd_read()); /* Get function idx for cmd */ + if(c&CB_META) sayntoo("M-"); + if(i == FN_PFXMETA) + { sayntoo("M-"); + i = cmd_idx(c = (cmd_read() | CB_META)); + } + else if(i == FN_PFXEXT) + { sayntoo("^X-"); + i = cmd_idx(c = (cmd_read() | CB_EXT)); + } + str[0] = c&0177; + str[1] = 0; + sayntoo(str); + + /* Now read in the help file, if necessary */ + savbuf = cur_buf; + if(help_buf) + chg_buf(help_buf); + else + { + saynow("Loading "); + sayntoo(ev_helpfile); + sayntoo("..."); + chg_buf(help_buf = make_buf(" **HELP**")); + if(read_file(ev_helpfile) == 0) + { chg_buf(savbuf); + kill_buf(help_buf); + help_buf = 0; + return; + } + } + + + /* Find function index in current buffer */ + cp = str; + *cp++ = '<'; + *cp++ = 'F'; + cp = dottoa(cp, (chroff)i); + *cp++ = '>'; + e_gobob(); + if(e_search(str, cp-str, 0) == 0) + sayntoo(" No help found"); + else + { + bdot = e_dot(); + while(!e_lblankp()) e_gonl(); /* Move past 1st blank line */ + b = make_buf(" *SHOW*"); + sb_sins((SBBUF *)b, e_copyn(bdot - e_dot())); + mk_showin(b); /* Show the stuff */ + kill_buf(b); + sayclr(); + } + chg_buf(savbuf); +} +#endif /*FX_DESCRIBE*/ diff --git a/commands/elle/eekmac.c b/commands/elle/eekmac.c new file mode 100755 index 000000000..983c765f8 --- /dev/null +++ b/commands/elle/eekmac.c @@ -0,0 +1,179 @@ +/* ELLE - Copyright 1982, 1985, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* EEKMAC - Keyboard Macro routines + * Modelled after the "e_macro.c" for ICONOGRAPHICS + * by C. D. Tavares, 9/11/82 + */ + +#include "elle.h" + +#if FX_SKMAC /* Entire file is under this conditional! */ + +int kdef_mode; /* Set when collecting (a "minor mode") */ +static int km_flag = 0; /* 1 = executing, -1 collecting, 0 neither */ +static int km_exp; /* Arg to "Execute Kbd Macro" - # times more to xct */ +static struct buffer *km_buf; + +/* EFUN: "Start Kbd Macro" */ + +f_skmac() +{ register struct buffer *b; + struct buffer *make_buf(); + + if(km_flag) + { ding("Kbd macro active, ignoring \"Start Kbd Macro\""); + return; + } + if((b = km_buf) == 0) + b = km_buf = make_buf(" *KBDMAC*"); + ex_reset(b); + km_flag = -1; /* Say starting macro collection */ + kdef_mode = 1; + redp(RD_MODE); +} + +/* EFUN: "End Kbd Macro" */ + +f_ekmac() +{ + if(km_flag > 0 && (--km_exp >= 0)) + { ex_go((SBBUF *)km_buf, (chroff)0); + } + else if(km_flag) + { km_flag = 0; + kdef_mode = 0; /* Flush minor mode */ + redp(RD_MODE); + } +} + +/* EFUN: "Execute Kbd Macro" */ + +f_xkmac() +{ + if(km_flag) + ding("Already in kbd macro!"); + else if(km_buf == 0) + ding("No kbd macro defined"); + else if((km_exp = exp-1) >= 0) + { + ex_go((SBBUF *)km_buf, (chroff) 0); + km_flag = 1; /* Start macro execution */ + } +} + +/* EFUN: "View Kbd Macro" */ + +f_vkmac() +{ register struct buffer *b, *savbuf; + chroff prmplen; + + if(!(b = km_buf)) + { ding("No kbd macro defined"); + return; + } + savbuf = cur_buf; + chg_buf(b); + e_gobob(); + e_sputz("Current Kbd macro:\n\n"); + prmplen = e_dot(); + mk_showin(b); /* Show the macro buffer temporarily */ + e_gobob(); + chg_buf(savbuf); + sb_deln((SBBUF *)b, prmplen); /* Flush the prompt */ +} + +/* KM_GETC - return next command char from kbd macro being executed. +** This is < 0 if not executing kbd macro. Also responsible for +** gathering input for kbd macro. +*/ +km_getc() +{ register int c; + + while (km_flag > 0) /* Executing macro? */ + { c = sb_getc(((SBBUF *)km_buf)); /* Yes, get char */ + if(c != EOF) + return(c); /* and return as cmd */ + + if(--km_exp >= 0) /* Macro done. Repeat? */ + ex_go((SBBUF *)km_buf, (chroff)0); /* Yes */ + else km_flag = 0; /* No, stop execution */ + } + c = tgetc(); /* Get char from user (TTY) */ + if(km_flag < 0) /* Save it if collecting macro */ + { sb_putc(((SBBUF *)km_buf), c); + } + return(c); +} + +/* KM_INWAIT() - Return TRUE if any keyboard-macro input waiting. + */ +km_inwait() +{ register int c; + if(km_flag > 0) + if((c = sb_getc(((SBBUF *)km_buf))) != EOF || (km_exp > 0)) + { sb_backc(((SBBUF *)km_buf)); + return(1); + } + return(0); +} + +km_abort () +{ + if(km_flag > 0) /* Executing? */ + km_flag = 0; /* Stop */ + else if(km_flag < 0) /* Collecting? */ + f_ekmac(); /* Close it out */ +} + +#endif /*FX_SKMAC*/ + +#if 0 /* Old unused stuff */ +static char mode_buf [60]; + +add_mode (mode) + char *mode; + { + register char *cur, *c, *m; + + if (cur_mode != mode_buf) + { + strcpy (mode_buf, cur_mode); + cur_mode = mode_buf; + } + + if (cur_mode [0]) strcat (cur_mode, ", "); + strcat (cur_mode, mode); + make_mode (); + } + +remove_mode (mode) + char *mode; + { + register char *cur, *c, *m; + + if (*cur_mode == 0) return; + + if (cur_mode != mode_buf) + { + strcpy (mode_buf, cur_mode); + cur_mode = mode_buf; + } + + for (cur = cur_mode ; *cur ; cur++) + if (*cur == *mode) /* 1st char matches */ + { + for (c = cur, m = mode ; *m && (*m == *c) ; m++, c++) ; + if (!(*m)) /* ok, mode matched */ + { /* kill leading ", " */ + if (*(cur - 1) == ' ') --cur; + if (*(cur - 1) == ',') --cur; + for ( ; *cur = *c ; cur++, c++) ; /* recopy to end */ + make_mode (); + return; + } + } + } +#endif /*COMMENT*/ diff --git a/commands/elle/eemain.c b/commands/elle/eemain.c new file mode 100755 index 000000000..f457858ee --- /dev/null +++ b/commands/elle/eemain.c @@ -0,0 +1,510 @@ +/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* + * EEMAIN ELLE Main Command Loop + */ + +#include "elle.h" + +#include +#if !(V6) +#include +#else +#include "eesigs.h" /* Use this on V6 system */ +#endif /*V6*/ + +char *argfile[MAXARGFILES]; /* Filename args at startup */ + +extern int (*sbm_debug)(); +extern int (*sbv_debug)(); +int (*vfy_vec)(); /* If non-zero, routine to verify data + * after each main-loop command */ + +main (argc, argv) +int argc; +char **argv; +{ + register int c; /* Current command character */ + register int i; + static int waitct; + extern int errsbm(); +#if SUN + extern int sun_rdevf; /* from EESUN */ +#endif +#ifdef STKMEM + char stackm[STKMEM]; /* Allocate some unused stack space */ +#endif /*STKMEM*/ + + sbv_debug = errsbm; /* Load with addrs of routine to */ + sbm_debug = errsbm; /* process SB and SBM errors. */ + +#ifdef STKMEM + sbm_init(&stackm[0],(SBMO)STKMEM); /* Initialize mem alloc rtns */ +#endif /*STKMEM*/ +#if SUN + sun_main(&argc, argv); /* On SUN, invoke window startup */ +#endif /*SUN*/ + + setbuf(stdout, (char *)NULL); /* Remove all stdio buffering */ + setbuf(stderr, (char *)NULL); /* in case of error reports. */ + + waitct = 0; /* debugging */ + doargs(argc,argv); /* Set up args */ + initialize (); /* Initialize the editor */ + + if (argfile[0]) /* shell line arg */ + find_file(argfile[0]); +#if MAXARGFILES > 1 + if(argfile[1]) + { f_2winds(); /* Make 2 windows, go to 2nd */ + i = 1; +#if MAXARGFILES > 2 + for (; i < MAXARGFILES; ++i) +#endif /* > 2 files */ + find_file(argfile[i]); /* Get further file(s) */ + f_othwind(); /* Move back to 1st window */ + } +#endif /* > 1 file */ + + redp(RD_SCREEN|RD_MODE); /* Clear and show mode line */ + setexit(0); /* catch for ints, ^G throws */ + +/* ----------------------------------------------------------- +** ELLE MAIN LOOP +** +*/ + for (;;) + { + /* First set up default arg unless last cmd specified it */ + if(this_cmd != ARGCMD) + { exp = 1; /* Default arg is 1 */ + exp_p = 0; /* Say no explicit arg */ + last_cmd = this_cmd; + } + this_cmd = 0; + + askclr(); /* If stuff asked, say to clear it */ + if(cmd_wait()) + waitct++; + else if(rd_type != 0) + redisplay(); /* Redisplay if needed and no input */ +#if SUN + sun_rdevf = 1; /* Allow mouse events on this input */ +#endif + c = cmd_read(); /* Read an editor command */ + sayclr(); /* Ask to clear echo area cleverly */ + +#if SUN + if(c != -1) /* SUN may not have real input */ +#endif /* if mouse event happened. */ + cmd_xct(c); /* Execute the command char! */ + + if(vfy_vec) /* If debugging, */ + (*vfy_vec)(1); /* verify data structs right away */ + } +} + +char *prof_file; /* Can specify user profile filename */ + +doargs(argc,argv) +int argc; +char **argv; +{ register int cnt, c; + register char **av; + extern int tibfmsk; + int argfiles = 0; + int argsignored = 0; + + av = argv; + cnt = argc; + +#if V6 /* V6 doesn't have environment thus no TERM var */ + /* Hack to force terminal type; analyze pgm name to get + * possible ".type" suffix. + */ + if(cnt && (c = strlen(*av))) + while(--c >= 0) + { switch(av[0][c]) + { case '.': + tv_stype = &av[0][c+1]; + case '/': + break; + default: continue; + } + break; + } +#endif /*V6*/ + + while(--cnt > 0) + { ++av; + if(*av[0] != '-') /* If not switch, */ + { /* assume it's an input filename */ + if (argfiles < MAXARGFILES) + argfile[argfiles++] = *av; + else + ++argsignored; + continue; + } + c = upcase(av[0][1]); + switch(c) /* Switches without args */ + { case 'I': /* Allow debug ints */ + dbg_isw = 1; + continue; + case '8': /* Ask for 8-bit input */ + tibfmsk = 0377; + continue; + case '7': /* Ask for 7-bit input */ + tibfmsk = 0177; + continue; +#if IMAGEN + case 'R': /* Debug redisplay stuff */ + dbg_redp = 1; + continue; +#endif /*IMAGEN*/ + } + if(--cnt <= 0) + goto stop; + ++av; + switch(c) /* Switches with args */ + { case 'T': /* Terminal type */ + tv_stype = *av; + break; + case 'P': + prof_file = *av; + default: + goto stop; + } + continue; + stop: printf("ELLE: bad switch: %s\n",*av); + exit(1); + } + if (argsignored > 0) + { printf("ELLE: more than %d file args, %d ignored.\n", + MAXARGFILES, argsignored); + sleep(2); /* Complain but continue after pause */ + } +} + +int f_throw(); /* throw function */ +int bite_bag(); /* Error handling routine */ +int hup_exit(); /* Hangup handling routine */ + +struct majmode ifunmode = { "Fundamental" }; + +initialize () /* Initialization */ +{ +#if SUN + extern int sun_winfd; +#endif + cur_mode = fun_mode = &ifunmode; /* Set current major mode */ + unrchf = pgoal = -1; + if(!homedir) + { +#if V6 + extern char *logdir(); + homedir = logdir(); +#else /* V7 */ + homedir = getenv("HOME"); +#endif /*-V6*/ + } + + sbx_tset((chroff)0,0); /* Create swapout file */ + /* (Temporary hack, fix up later) */ + hoard(); /* Hoard a FD for write purposes */ + + redp_init(); /* Set up the display routines */ + init_buf(); /* Set up initial buffers */ + set_profile(prof_file); /* Set up user profile */ + +#if SUN + if(sun_winfd) sun_init(); +#endif /*SUN*/ + + /* Set up signal handlers */ +#if 0 /* not really used */ + signal (SIGQUIT, f_throw); /* Quit - on ^G */ +#endif +#if !(MINIX) + signal (SIGSYS, bite_bag); /* Bad arg to Sys call */ +#endif + signal (SIGSEGV, bite_bag); /* Segmentation Violation */ +#if !(COHERENT) + signal (SIGILL, bite_bag); /* Illegal Instruction interrupt */ + signal (SIGBUS, bite_bag); /* Bus Error interrupt */ +#endif /*-COHERENT*/ +#if !(TOPS20) /* T20 just detaches job */ + signal (SIGHUP, hup_exit); /* Terminal Hangup interrupt */ +#endif /*-TOPS20*/ +} + + +/* NOTE: This routine is not actually used, because ELLE does not + * allow interrupts to do anything. + */ +/* EFUN: "Error Throw" */ +f_throw () /* abort whatever is going on */ +{ + ring_bell (); + curs_lin = -1000; /* make t_curpos do something */ + redp(RD_MOVE); /* crock: cursor seems to move, so fix it */ + signal(SIGQUIT, f_throw); /* rearm signal */ +/* unwind_stack(main); */ + reset(1); /* throw to main loop */ +} + +/* RING_BELL - General-purpose feeper when something goes wrong with + * a function. + */ +ring_bell() +{ t_bell(); /* Tell user something's wrong */ + +#if FX_SKMAC + f_ekmac(); /* Stop collecting keyboard macro if any */ +#endif /*FX_SKMAC*/ +} + +/* EFUN: "Return to Superior" + * Behavior here is somewhat system-dependent. If it is possible to + * suspend the process and continue later, we do not ask about modified + * buffers. Otherwise, we do. Questioning can always be forced by using + * the prefix ^U. + * Note that here we try to be very careful about not letting the user + * exit while buffers are still modified, since UNIX flushes the process + * if we exit. Also, the code here conspires with sel_mbuf to rotate + * through all modified buffers, complaining about a different one each time, + * so that the user need not even know how to select a buffer! + */ +f_retsup() +{ register char *reply; + register int c; + register struct buffer *b, *b2; + extern struct buffer *sel_mbuf(); + extern int tsf_pause; + + /* If we have capability of pausing and later continuing, do that, + * except if CTRL-U forces us into question/save/quit behavior. + */ + if(tsf_pause && (exp_p != 4)) + { clean_exit(); /* Return TTY to normal mode */ + ts_pause(); /* Pause this inferior */ + set_tty(); /* Continued, return to edit mode */ + redp(RD_SCREEN); + return; + } + + /* Sigh, do more typical "Are you sure" questioning prior to + * killing the editor permanently. + */ + b = cur_buf; + if((b = sel_mbuf(b)) || (b = sel_mbuf((struct buffer *)0)) ) + { if(b2 = sel_mbuf(b)) + reply = ask( + "Quit: buffers %s, %s,... still have changes - forget them? ", + b->b_name, b2->b_name); + else + reply = ask( + "Quit: buffer %s still has changes - forget them? ", + b->b_name); + + } + else + { +#if IMAGEN /* Do not ask further if nothing modified */ + barf("Bye"); + clean_exit(); + exit(0); +#else + reply = ask("Quit? "); +#endif /*-IMAGEN*/ + } + + if (reply == 0) + return; /* Aborted, just return */ + + c = upcase(*reply); /* Get 1st char of reply */ + chkfree(reply); + + switch(c) + { case 'Y': +#if IMAGEN + barf("Bye"); +#endif /*IMAGEN*/ + clean_exit(); + exit(0); +#if 0 + case 'S': /* Suspend command for debugging */ + bkpt(); + return; +#endif /*COMMENT*/ + default: /* Complain */ + ring_bell(); + case 'N': + if(b) /* B set if we have any modified buffers */ + { sel_buf(b); + if(b->b_fn) + saynow("Use ^X ^S to save buffer"); + else saynow("Use ^X ^W to write out buffer"); + } + } +} + + +#if FX_WFEXIT +/* EFUN: "Write File Exit" (not EMACS) - from IMAGEN config */ +f_wfexit() +{ + exp_p = 1; /* Ensure f_savefiles asks no questions */ + if (! f_savefiles()) /* Save all modified buffers, but */ + return; /* stay here if any save fails */ + saynow("Bye"); + clean_exit(); + exit(0); +} +#endif /*FX_WFEXIT*/ + +/* Subprocess-handling stuff; put here for time being. */ + +/* EFUN: "Push to Inferior" */ +#if TOPS20 +#include /* Support for KCC forkexec() call */ +#endif +f_pshinf() +{ + register int res; + register int (*sav2)(), (*sav3)(); + int pid, status; + char *shellname; +#if IMAGEN + char fullshell[64]; +#endif /*IMAGEN*/ + + sav2 = signal(SIGINT, SIG_IGN); /* Ignore TTY interrupts */ + sav3 = signal(SIGQUIT, SIG_IGN); /* Ditto TTY "quit"s */ + clean_exit(); /* Restore normal TTY modes */ + +#if TOPS20 + { + struct frkxec fx; + fx.fx_flags = FX_WAIT | FX_T20_PGMNAME; + fx.fx_name = "SYS:EXEC.EXE"; + fx.fx_argv = fx.fx_envp = NULL; + if (forkexec(&fx) < 0) + writerr("Cannot run EXEC"); + } +#else /*-TOPS20*/ + switch(pid = fork()) + { case -1: + writerr("Cannot fork"); + break; + case 0: /* We're the child */ + for(res = 3; res < 20;) /* Don't let inf hack fd's */ + close(res++); +#if V6 + execl("/bin/sh","-sh",0); +#else + signal(SIGINT, SIG_DFL); /* V7 shell wants this?? */ + signal(SIGQUIT, SIG_DFL); /* */ +#if IMAGEN + if((shellname = getenv("SHELL")) == 0) + shellname = "sh"; + strcpy(fullshell, "/bin/"); + strcat(fullshell, shellname); + shellname = fullshell; +#else + if((shellname = getenv("SHELL")) == 0) + shellname = "/bin/sh"; +#endif /*-IMAGEN*/ + + if((shellname = getenv("SHELL")) == 0) + shellname = "/bin/sh"; + execl(shellname, shellname, 0); +#endif /*-V6*/ + writerr("No shell!"); + exit(1); + break; + default: + while((res = wait(&status)) != pid && res != -1); + break; + } +#endif /*-TOPS20*/ + + signal(SIGINT, sav2); /* Restore signal settings */ + signal(SIGQUIT, sav3); + set_tty(); /* Restore editor TTY modes */ + redp(RD_SCREEN|RD_MODE); /* Done, redisplay */ +} + +/* Miscellaneous utility routines - memory alloc/free and string hacking. + * If this page becomes overly large, it can be split off into a separate + * file called E_MISC. + */ +char * +strdup(s) +char *s; /* Note that STRCPY's return val must be its 1st arg */ +{ char *strcpy(); + return(strcpy(memalloc((SBMO)(strlen(s)+1)), s)); +} + +char * +memalloc(size) +SBMO size; +{ register SBMA ptr; + extern SBMA sbx_malloc(); + + if ((ptr = (SBMA)sbx_malloc(size)) != 0) + return((char *)ptr); + barf("ELLE: No memory left"); + askerr(); + return(0); /* If we dare to continue... */ +} + +chkfree (ptr) +SBMA ptr; +{ + if(!free(ptr)) + { errbarf("Something overwrote an allocated block!"); + askerr(); + } +} + + +/* USTRCMP - Uppercase String Compare. + * Returns 0 if mismatch, + * 1 if full match, + * -1 if str1 runs out first (partial match) + */ +ustrcmp(str1,str2) +char *str1, *str2; +{ register char *s1, *s2; + register int c; + s1 = str1; s2 = str2; + while(c = *s1++) + { if(c != *s2 && upcase(c) != upcase(*s2)) + return(0); + s2++; + } + return(c == *s2 ? 1 : -1); +} + + +/* WRITERR(str) - Output string to standard error output. +** This is a separate routine to save a little space on calls. +*/ +writerr(str) +char *str; +{ return(writez(2, str)); +} + +/* WRITEZ(fd, str) - Miscellaneous general-purpose string output. + */ +writez(fd,acp) +int fd; +char *acp; +{ register char *cp; + cp = acp; + while(*cp++); + write(fd,acp,cp-acp-1); +} diff --git a/commands/elle/eemake.c b/commands/elle/eemake.c new file mode 100755 index 000000000..c3420bfde --- /dev/null +++ b/commands/elle/eemake.c @@ -0,0 +1,308 @@ +/* ELLE - Copyright 1982, 1985, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* EEMAKE - IMAGEN configuration functions for interfacing to "make". + * Written by (I think) Chris Ryland at IMAGEN, who also + * wrote other functions scattered through ELLE. These are either + * conditionalized or are commented as being derived from the IMAGEN + * configuration. + * + * KLH: An attempt has been made to keep these routines updated as ELLE + * changed, but their workings cannot be guaranteed. + */ + + +/* + * eemake: "make" (and other program) support + * + * Next-error depends on programs writing error messages of the form: + * "file", line n: message + * which is a de facto standard, at least in some worlds. + */ + +#include "elle.h" + +#if !(IMAGEN) /* Only with IMAGEN config for now */ +f_xucmd() {} +f_make() {} +f_nxterr() {} +#else + +#include + +struct buffer *exec_buf; /* Ptr to "Execution" buffer */ + /* Only external ref is from e_buff.c */ + +#define MSGLENGTH (scr_wid - 11) /* Max length of message */ +int fresh_make = 1; /* Fresh Execution buffer contents */ +chroff last_error_BOL; /* Dot for last-error's BOL */ + +/* EFUN: "Execute Unix Command" */ +f_xucmd() +{ + make_or_unix_cmd(0); +} + +/* EFUN: "Execute Make" */ +f_make() +{ + make_or_unix_cmd(1); +} + +/* EFUN: "Find Next Error" */ +f_nxterr() +{ + register struct sbstr *sb; + register char *cp; + register int c; + char file[64], line[32]; +#ifdef ONEWINDOW + char msg[512]; +#endif + chroff linedot; + int lineno; + register int len; + + sel_execbuf(); + if (! fresh_make) + { e_go(last_error_BOL); + e_gonl(); + } + else + { fresh_make = 0; + e_gobob(); + last_error_BOL = e_dot(); + } + + /* Looking for `"file", line n: msg' */ + if (! e_search("\", line ", 8, 0)) + goto failed; + linedot = e_dot(); + e_gobol(); /* Found something, get to BOL */ + if (e_getc() != '"') + goto failed; /* Insist on beginning "file" */ + cp = file; /* Pick up filename */ + while ((c = e_getc()) != '"') + *cp++ = c; + *cp = 0; + e_go(linedot); /* Back to after "line " */ + cp = line; + while ((c = e_getc()) >= '0' && c <= '9') + *cp++ = c; + *cp = 0; + lineno = atoi(line); /* Pick up line number */ +#ifdef ONEWINDOW + cp = msg; /* Now get rest of line to msg */ + len = 0; /* But keep length <= MSGLENGTH */ + e_getc(); /* Go past purported space */ + while ((c = e_getc()) != LF && c != EOF && len <= MSGLENGTH) + { if (c == '\t') + len = (len + 8) & ~07; + else if (c < ' ' || c > 0176) + len += 2; + else + ++len; + *cp++ = c; + } + *cp = 0; + if (len > MSGLENGTH) + strcat(msg, "..."); +#ifdef DEBUG + say("file "); + saytoo(file); + saytoo(", line "); + saytoo(line); + saytoo(", msg: "); + sayntoo(msg); +#else + say(line); + saytoo(": "); + sayntoo(msg); +#endif /*DEBUG*/ +#else /* -ONEWINDOW */ + f_begline(); /* Get to start of line */ + last_error_BOL = e_dot(); /* Remember this position */ + exp_p = 1; /* Make this the new top line */ + exp = 0; + f_newwin(); + upd_wind(0); +#endif /*ONEWINDOW*/ + + /* Now, visit the given file and line */ +#ifdef ONEWINDOW +#else + f_othwind(); /* To other window */ +#endif + find_file(file); + f_gobeg(); + down_line(lineno - 1); +#ifdef READJUST /* NAW */ + exp_p = 1; + exp = cur_win->w_ht / 4; /* Adjust how we look at "error" */ + f_newwin(); +#endif /*READJUST*/ + return; + +failed: ding("No more errors!"); + fresh_make = 1; /* Fake-out: pretend starting over */ + return; +} + + +/* Do the "cmd" and put its output in the Execution buffer */ +do_exec(cmd, nicely) +char *cmd; +int nicely; +{ + register int n; + int status, res, pid, fd[2]; + char nicecmd[256]; + char pipebuff[512]; + struct buffer *b; + + b = cur_buf; + sel_execbuf(); /* Get our execution buffer up */ + ed_reset(); /* Clear it out */ + fresh_make = 1; + upd_wind(0); + if (nicely) + sayntoo(" ...starting up..."); + else + sayntoo(" ...starting up (nasty person)..."); + pipe(fd); + switch (pid = fork()) + { + case -1: + /* Fork failed, in parent */ + ding("Cannot fork!?!"); + break; + + case 0: /* In child */ + for (n = 0; n < 20; ++n) + if (n != fd[0] && n != fd[1]) + close(n); + open("/dev/null", 0); /* Give ourselves empty stdin */ + dup(fd[1]); + dup(fd[1]); /* stdout, stderr out to pipe */ + close(fd[1]); /* Close the pipe itself */ + close(fd[0]); + if (nicely) + { strcpy(nicecmd, "nice -4 "); + strcat(nicecmd, cmd); + execl("/bin/sh", "sh", "-c", nicecmd, 0); + } + else + execl("/bin/sh", "sh", "-c", cmd, 0); + write(1, "Cannot execute!", 15); + _exit(-1); + break; + + default: + /* Parent */ + close(fd[1]); /* Close the output direction */ + while ((n = read(fd[0], pipebuff, sizeof(pipebuff))) > 0) + { ed_nsins(pipebuff, n); + upd_wind(0); + saynow("Chugging along..."); + } + close(fd[0]); + while ((res = wait(&status)) != pid && res != -1) + ; /* Wait for this fork to die */ + f_bufnotmod(); /* Buffer is fresh */ + saynow("Done!"); + break; + } + f_othwind(); /* Back to other window */ + chg_buf(b); /* Back to original buffer */ +} + +char last_make_cmd[256]; /* Last Unix/make command */ +int have_last_make_cmd = 0; + +make_or_unix_cmd(domake) +int domake; +{ +#if APOLLO + register int nicely = exp == 16; /* modification for apollo */ +#else + register int nicely = exp != 16; +#endif /*-APOLLO*/ + register char *reply, *cmd = 0; + + if (domake) /* If "make"-style, */ + { int savp = exp_p; + exp_p = 1; + f_savefiles(); /* write modified files quietly */ + exp_p = savp; + } + if (exp_p || ! domake) + { /* Arg given make, or Unix command */ + reply = ask((! domake) ? "Unix command: " : "Command: "); + if (! reply) + return; + if (*reply == 0) + { if (have_last_make_cmd) + cmd = &last_make_cmd[0]; + else + { chkfree(reply); + ding("No previous command!"); + return; + } + } + else + cmd = reply; + if (cmd != &last_make_cmd[0]) /* Update last command */ + strcpy(last_make_cmd, cmd); + have_last_make_cmd = 1; + say("Command: "); + sayntoo(cmd); + do_exec(cmd, nicely); + chkfree(reply); + } + else if (have_last_make_cmd) + { say("Command: "); + sayntoo(last_make_cmd); + do_exec(last_make_cmd, nicely); + } + else + { saynow("Command: make"); + do_exec("make", nicely); + } +} + +sel_execbuf() +{ if(!exec_buf) + { /* Make execution buffer; don't let anyone kill it */ + exec_buf = make_buf("Execution"); + exec_buf->b_flags |= B_PERMANENT; + } + popto_buf(exec_buf); +} + +/* Utility: pop the given buffer to a window, getting into 2-window mode */ +popto_buf(b) +register struct buffer *b; +{ + /* See if we already have this buffer in a visible window */ + if (b == cur_win->w_buf) + { if (oth_win == 0) + { f_2winds(); + f_othwind(); /* Get back to our window */ + } + } + else if (oth_win != 0 && b == oth_win->w_buf) + f_othwind(); + else if (oth_win == 0) + { /* One window mode */ + f_2winds(); /* Get two, get into second */ + sel_buf(b); /* Select our new buffer */ + } + else + { f_othwind(); /* Get to other window */ + sel_buf(b); /* and select our buffer */ + } +} + +#endif /*IMAGEN*/ diff --git a/commands/elle/eeprof.h b/commands/elle/eeprof.h new file mode 100755 index 000000000..5859b08f6 --- /dev/null +++ b/commands/elle/eeprof.h @@ -0,0 +1,20 @@ +#define PROF_VER (1) + +struct profile { +int version; +int chrvcnt; char *chrvec; +int metavcnt; char *metavec; +int extvcnt; char *extvec; +int menuvcnt; char *menuvec; +}; + +struct stored_profile { +unsigned char version[2]; +unsigned char chrvcnt[2], chrvec[2]; +unsigned char metavcnt[2], metavec[2]; +unsigned char extvcnt[2], extvec[2]; +unsigned char menuvcnt[2], menuvec[2]; +}; + +#define prof_pack(p, n) ((p)[0] = (n) & 0xFF, (p)[1] = (n) >> 8) +#define prof_upack(p) ((p)[0] | ((p)[1] << 8)) diff --git a/commands/elle/eeproto.h b/commands/elle/eeproto.h new file mode 100755 index 000000000..f1c5220e2 --- /dev/null +++ b/commands/elle/eeproto.h @@ -0,0 +1,435 @@ +#ifndef _ANSI +#include +#endif + +/* eebit.c */ +_PROTOTYPE( int *chballoc, (int size) ); +_PROTOTYPE( int chbit, (int *array, int c) ); +_PROTOTYPE( int chbis, (int *array, int c) ); +_PROTOTYPE( int chbic, (int *array, int c) ); + +/* eebuff.c */ +_PROTOTYPE( int f_selbuffer, (void) ); +_PROTOTYPE( int f_selxbuffer, (void) ); +_PROTOTYPE( int f_kbuffer, (void) ); +_PROTOTYPE( int f_listbufs, (void) ); +_PROTOTYPE( int f_bufnotmod, (void) ); +_PROTOTYPE( int f_eolmode, (void) ); +_PROTOTYPE( int f_gobeg, (void) ); +_PROTOTYPE( int f_goend, (void) ); +_PROTOTYPE( int f_whatpage, (void) ); +_PROTOTYPE( int init_buf, (void) ); +_PROTOTYPE( struct buffer *make_buf, (char *bname) ); +_PROTOTYPE( struct buffer *find_buf, (char *name) ); +_PROTOTYPE( int sel_buf, (struct buffer *b) ); +_PROTOTYPE( int chg_buf, (struct buffer *newbuf) ); +_PROTOTYPE( int unlk_buf, (struct buffer *bufp) ); +_PROTOTYPE( struct buffer *sel_mbuf, (struct buffer *buf) ); +_PROTOTYPE( struct buffer *sel_mbuf, (struct buffer *buf) ); +_PROTOTYPE( struct buffer *sel_nbuf, (struct buffer *buf) ); +_PROTOTYPE( int kill_buf, (struct buffer *buf) ); +_PROTOTYPE( int zap_buffer, (void) ); +_PROTOTYPE( int ask_kbuf, (struct buffer *buf) ); +_PROTOTYPE( int f_2winds, (void) ); +_PROTOTYPE( int f_1wind, (void) ); +_PROTOTYPE( int f_othwind, (void) ); +_PROTOTYPE( int f_growind, (void) ); +_PROTOTYPE( int f_shrinkwind, (void) ); +_PROTOTYPE( int f_delwind, (void) ); +_PROTOTYPE( int f_sowind, (void) ); +_PROTOTYPE( int f_2modewinds, (void) ); +_PROTOTYPE( int chk2modws, (void) ); +_PROTOTYPE( int init_win, (void) ); +_PROTOTYPE( int chg_win, (struct window *newwin) ); +_PROTOTYPE( struct window *make_win, (int pos, int ht, struct buffer *buf) ); +_PROTOTYPE( int kill_win, (struct window *win) ); +_PROTOTYPE( int mk_showin, (struct buffer *b) ); +_PROTOTYPE( struct window *make_mode, (struct window *bw) ); +_PROTOTYPE( int buf_mod, (void) ); +_PROTOTYPE( int buf_tmat, (chroff dot) ); +_PROTOTYPE( int buf_tmod, (chroff offset) ); + +/* eecmds.c */ +_PROTOTYPE( int f_pfxmeta, (void) ); +_PROTOTYPE( int f_pfxext, (void) ); +_PROTOTYPE( int f_uarg, (int ch) ); +_PROTOTYPE( int f_negarg, (int ch) ); +_PROTOTYPE( int f_argdig, (int ch) ); +_PROTOTYPE( int f_setprof, (void) ); +_PROTOTYPE( int f_vtbuttons, (void) ); +_PROTOTYPE( int cmd_wait, (void) ); +_PROTOTYPE( int cmd_read, (void) ); +_PROTOTYPE( int cmd_xct, (int ch) ); +_PROTOTYPE( int cmd_idx, (int c) ); +_PROTOTYPE( int set_profile, (char *filename) ); +_PROTOTYPE( int init_menu, (void) ); + +/* eediag.c */ +_PROTOTYPE( int f_debug, (int ch) ); +_PROTOTYPE( char *vfy_data, (int flag) ); +_PROTOTYPE( int dbg_diag, (void) ); +_PROTOTYPE( int vfy_exer, (int pf, int gcfrq) ); +_PROTOTYPE( int db_prwind, (struct window *w) ); +_PROTOTYPE( char *db_scflgs, (int flags) ); + +/* eedisp.c */ +_PROTOTYPE( int set_tty, (void) ); +_PROTOTYPE( int clean_exit, (void) ); +_PROTOTYPE( int set_scr, (void) ); +_PROTOTYPE( int redisplay, (void) ); +_PROTOTYPE( int fupd_wind, (struct window *w) ); +_PROTOTYPE( int upd_curs, (chroff adot) ); +_PROTOTYPE( int d_line, (chroff cdot) ); +_PROTOTYPE( int d_ncols, (int lcnt, int ccol) ); +_PROTOTYPE( int d_lupd, (struct window *w, int idx) ); +_PROTOTYPE( int clear_wind, (struct window *w) ); +_PROTOTYPE( int fix_wind, (struct window *win) ); +_PROTOTYPE( int inwinp, (struct window *win, chroff cdot) ); +_PROTOTYPE( int upd_wind, (struct window *win) ); +_PROTOTYPE( int slineq, (struct scr_line *olds, struct scr_line *news) ); +_PROTOTYPE( int upd_line, (int y) ); +_PROTOTYPE( int fillset, (char *str, int cnt, int c) ); +_PROTOTYPE( int fillsp, (char *str, int cnt) ); +_PROTOTYPE( int inspc, (char *cp0, char *cpl, int cnt) ); +_PROTOTYPE( int fix_line, (struct scr_line *slp, struct scr_line *olds) ); +_PROTOTYPE( int sctrin, (char *to, int lim, int ccol) ); +_PROTOTYPE( int inslin, (int line, int n, struct window *win) ); +_PROTOTYPE( int dellin, (int line, int n, struct window *win) ); +_PROTOTYPE( int t_dostandout, (int on) ); +_PROTOTYPE( int t_move, (int y, int x) ); +_PROTOTYPE( int t_docleol, (void) ); + +/* eeedit.c */ +_PROTOTYPE( int e_reset, (void) ); +_PROTOTYPE( int e_rgetc, (void) ); +_PROTOTYPE( int e_rdelc, (void) ); +_PROTOTYPE( int e_delc, (void) ); +_PROTOTYPE( int e_getc, (void) ); +_PROTOTYPE( int e_backc, (void) ); +_PROTOTYPE( int e_putc, (int c) ); +_PROTOTYPE( int e_peekc, (void) ); +_PROTOTYPE( int e_ovwc, (int ch) ); +_PROTOTYPE( SBSTR *e_copyn, (chroff off) ); +_PROTOTYPE( int e_deln, (chroff off) ); +_PROTOTYPE( int e_setcur, (void) ); +_PROTOTYPE( int e_gosetcur, (chroff dot) ); +_PROTOTYPE( int e_gocur, (void) ); +_PROTOTYPE( int e_gobob, (void) ); +_PROTOTYPE( int e_goeob, (void) ); +_PROTOTYPE( int e_go, (chroff dot) ); +_PROTOTYPE( int e_igoff, (int ioff) ); +_PROTOTYPE( int e_goff, (chroff off) ); +_PROTOTYPE( int e_gobol, (void) ); +_PROTOTYPE( int e_goeol, (void) ); +_PROTOTYPE( int e_gonl, (void) ); +_PROTOTYPE( int e_gopl, (void) ); +_PROTOTYPE( chroff e_dot, (void) ); +_PROTOTYPE( chroff e_nldot, (void) ); +_PROTOTYPE( chroff e_pldot, (void) ); +_PROTOTYPE( chroff e_boldot, (void) ); +_PROTOTYPE( chroff e_eoldot, (void) ); +_PROTOTYPE( chroff e_alldot, (SBBUF *sbp, int (*rtn )()) ); +_PROTOTYPE( chroff e_blen, (void) ); +_PROTOTYPE( int ex_reset, (struct buffer *b) ); +_PROTOTYPE( int ex_go, (SBBUF *sbp, chroff loc) ); +_PROTOTYPE( chroff ex_dot, (SBBUF *sbp) ); +_PROTOTYPE( chroff ex_boldot, (SBBUF *sbp, chroff dot) ); +_PROTOTYPE( chroff ex_alldot, (SBBUF *sbp, int (*rtn )(), chroff dot) ); +_PROTOTYPE( int ex_gonl, (SBBUF *sbp) ); +_PROTOTYPE( int ex_goeol, (SBBUF *sbp) ); +_PROTOTYPE( int ex_gobol, (SBBUF *sbp) ); +_PROTOTYPE( int ex_gopl, (SBBUF *sbp) ); +_PROTOTYPE( chroff ex_blen, (SBBUF *sbp) ); +_PROTOTYPE( int e_gofwsp, (void) ); +_PROTOTYPE( int e_gobwsp, (void) ); +_PROTOTYPE( int e_goline, (int i) ); +_PROTOTYPE( int e_lblankp, (void) ); +_PROTOTYPE( int e_insn, (int ch, int cnt) ); +_PROTOTYPE( int e_sputz, (char *acp) ); +_PROTOTYPE( int boleq, (chroff dot1, chroff dot2) ); +_PROTOTYPE( char *dottoa, (char *str, chroff val) ); +_PROTOTYPE( int e_gobpa, (void) ); +_PROTOTYPE( int e_goepa, (void) ); +_PROTOTYPE( int exp_do, (int (*rpos )(), int (*rneg )()) ); +_PROTOTYPE( int e_fwsp, (void) ); +_PROTOTYPE( int e_bwsp, (void) ); +_PROTOTYPE( int c_wsp, (int ch) ); +_PROTOTYPE( int c_pwsp, (int ch) ); +_PROTOTYPE( int delimp, (int c) ); +_PROTOTYPE( int e_wding, (chroff *adot, int n) ); +_PROTOTYPE( chroff e_wdot, (chroff dot, int n) ); +_PROTOTYPE( int e_gowd, (int n) ); +_PROTOTYPE( int e_search, (char *mstr, int mlen, int backwards) ); + +/* eeerr.c */ +_PROTOTYPE( int f_bkpt, (void) ); +_PROTOTYPE( int bpt, (void) ); +_PROTOTYPE( char *strerror, (int num) ); +_PROTOTYPE( int errsbm, (int type, int (*adr )(), char *str, int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12) ); +_PROTOTYPE( int bite_bag, (int fault) ); +_PROTOTYPE( int hup_exit, (void) ); +_PROTOTYPE( int errint, (void) ); +_PROTOTYPE( int askerr, (void) ); +_PROTOTYPE( char *asklin, (char *acp) ); + +/* eef1.c */ +_PROTOTYPE( int f_insself, (int c) ); +_PROTOTYPE( int f_quotins, (void) ); +_PROTOTYPE( int f_crlf, (void) ); +_PROTOTYPE( int f_fchar, (void) ); +_PROTOTYPE( int f_bchar, (void) ); +_PROTOTYPE( int f_dchar, (void) ); +_PROTOTYPE( int f_bdchar, (void) ); +_PROTOTYPE( int ef_deln, (int x) ); +_PROTOTYPE( int f_delspc, (void) ); +_PROTOTYPE( int f_tchars, (void) ); +_PROTOTYPE( int f_fword, (void) ); +_PROTOTYPE( int f_bword, (void) ); +_PROTOTYPE( int f_kword, (void) ); +_PROTOTYPE( int f_bkword, (void) ); +_PROTOTYPE( int f_twords, (void) ); +_PROTOTYPE( int f_ucword, (void) ); +_PROTOTYPE( int f_lcword, (void) ); +_PROTOTYPE( int f_uciword, (void) ); +_PROTOTYPE( int case_word, (int downp) ); + +/* eef2.c */ +_PROTOTYPE( int f_begline, (void) ); +_PROTOTYPE( int f_endline, (void) ); +_PROTOTYPE( int f_nxtline, (void) ); +_PROTOTYPE( int f_prvline, (void) ); +_PROTOTYPE( int f_dnrline, (void) ); +_PROTOTYPE( int f_uprline, (void) ); +_PROTOTYPE( int f_oline, (void) ); +_PROTOTYPE( int f_delblines, (void) ); +_PROTOTYPE( int f_kline, (void) ); +_PROTOTYPE( int f_bkline, (void) ); +_PROTOTYPE( int f_goline, (void) ); +_PROTOTYPE( int down_bline, (int arg) ); +_PROTOTYPE( int down_line, (int x) ); +_PROTOTYPE( int f_setmark, (void) ); +_PROTOTYPE( int f_exchmark, (void) ); +_PROTOTYPE( int f_kregion, (void) ); +_PROTOTYPE( int f_copreg, (void) ); +_PROTOTYPE( int f_ucreg, (void) ); +_PROTOTYPE( int f_lcreg, (void) ); +_PROTOTYPE( int ef_creg, (int downp) ); +_PROTOTYPE( int f_fillreg, (void) ); +_PROTOTYPE( int chkmark, (void) ); +_PROTOTYPE( int f_fpara, (void) ); +_PROTOTYPE( int f_bpara, (void) ); +_PROTOTYPE( int f_mrkpara, (void) ); +_PROTOTYPE( int f_fillpara, (void) ); + +/* eef3.c */ +_PROTOTYPE( int f_appnkill, (void) ); +_PROTOTYPE( int f_unkill, (void) ); +_PROTOTYPE( int f_unkpop, (void) ); +_PROTOTYPE( int f_indatm, (void) ); +_PROTOTYPE( int f_indnl, (void) ); +_PROTOTYPE( int f_backind, (void) ); +_PROTOTYPE( int f_indcomm, (void) ); +_PROTOTYPE( int f_indrel, (void) ); +_PROTOTYPE( int insertmatch, (int c) ); +_PROTOTYPE( int f_matchbrack, (void) ); +_PROTOTYPE( int matchonelevel, (int mc) ); + +/* eefd.c */ +_PROTOTYPE( int f_newwin, (void) ); +_PROTOTYPE( int f_nscreen, (void) ); +_PROTOTYPE( int f_pscreen, (void) ); +_PROTOTYPE( int f_othnscreen, (void) ); +_PROTOTYPE( int f_lwindbord, (void) ); +_PROTOTYPE( int f_scupwind, (void) ); +_PROTOTYPE( int f_scdnwind, (void) ); +_PROTOTYPE( int f_mvwtop, (void) ); +_PROTOTYPE( int f_mvwbot, (void) ); +_PROTOTYPE( int d_screen, (int rep) ); +_PROTOTYPE( int scroll_win, (int n) ); +_PROTOTYPE( int d_curind, (void) ); +_PROTOTYPE( int indtion, (chroff lin) ); +_PROTOTYPE( int inindex, (chroff lin, int xpos) ); +_PROTOTYPE( int d_gopl, (void) ); +_PROTOTYPE( int d_gonl, (void) ); +_PROTOTYPE( int d_goloff, (int cnt) ); +_PROTOTYPE( int d_fgoloff, (int cnt) ); +_PROTOTYPE( int d_fixcur, (void) ); +_PROTOTYPE( int d_backup, (int nlin) ); + +/* eefed.c */ +_PROTOTYPE( int ed_insert, (int c) ); +_PROTOTYPE( int ed_insn, (int ch, int cnt) ); +_PROTOTYPE( int ed_crins, (void) ); +_PROTOTYPE( int ed_sins, (char *s) ); +_PROTOTYPE( int ed_nsins, (char *s, int i) ); +_PROTOTYPE( int ed_indto, (int goal) ); +_PROTOTYPE( int ed_setcur, (void) ); +_PROTOTYPE( int ed_go, (chroff dot) ); +_PROTOTYPE( int ed_goff, (chroff off) ); +_PROTOTYPE( int ed_igoff, (int ioff) ); +_PROTOTYPE( int ed_reset, (void) ); +_PROTOTYPE( int ed_deln, (chroff off) ); +_PROTOTYPE( int ed_delete, (chroff dot1, chroff dot2) ); +_PROTOTYPE( int ed_kill, (chroff dot1, chroff dot2) ); +_PROTOTYPE( int kill_push, (SBSTR *sdp) ); +_PROTOTYPE( int ed_case, (chroff dot1, chroff dot2, int downp) ); +_PROTOTYPE( int upcase, (int ch) ); + +/* eefile.c */ +_PROTOTYPE( int f_ffile, (void) ); +_PROTOTYPE( int f_rfile, (void) ); +_PROTOTYPE( int f_vfile, (void) ); +_PROTOTYPE( int u_r_file, (char *prompt) ); +_PROTOTYPE( int f_ifile, (void) ); +_PROTOTYPE( int f_sfile, (void) ); +_PROTOTYPE( int f_savefiles, (void) ); +_PROTOTYPE( int f_wfile, (void) ); +_PROTOTYPE( int f_wreg, (void) ); +_PROTOTYPE( int f_wlastkill, (void) ); +_PROTOTYPE( int hack_file, (char *prompt, int (*rtn )()) ); +_PROTOTYPE( int find_file, (char *f_name) ); +_PROTOTYPE( int read_file, (char *f_name) ); +_PROTOTYPE( int ins_file, (char *f_name) ); +_PROTOTYPE( int ferr_ropn, (void) ); +_PROTOTYPE( int ferr_wopn, (void) ); +_PROTOTYPE( int ferr, (char *str) ); +_PROTOTYPE( char *fncons, (char *dest, char *pre, char *f_name, char *post) ); +_PROTOTYPE( char *last_fname, (char *f_name) ); +_PROTOTYPE( int set_fn, (char *string) ); +_PROTOTYPE( int saveworld, (struct buffer *bp, int grunt) ); +_PROTOTYPE( int hoard, (void) ); +_PROTOTYPE( int unhoard, (void) ); +_PROTOTYPE( int expand_file, (char *dfn, char *sfn) ); + +/* eefill.c */ +_PROTOTYPE( int f_sfcol, (void) ); +_PROTOTYPE( int f_sfpref, (void) ); +_PROTOTYPE( int tstfillp, (int lim) ); +_PROTOTYPE( int ed_fill, (chroff begloc, chroff endloc, int flag) ); +_PROTOTYPE( int f_fillmode, (void) ); +_PROTOTYPE( int fx_insfill, (int c) ); +_PROTOTYPE( int fill_cur_line, (void) ); +_PROTOTYPE( int f_textmode, (void) ); +_PROTOTYPE( int fim_insself, (int c) ); +_PROTOTYPE( int fim_dchar, (void) ); +_PROTOTYPE( int fim_bdchar, (void) ); +_PROTOTYPE( int fim_crlf, (void) ); +_PROTOTYPE( int magic_wrap, (int tc) ); +_PROTOTYPE( int reveal, (char *msg, int v1, int v2, int v3) ); +_PROTOTYPE( int magic_backto_bol, (void) ); +_PROTOTYPE( int fill_current_line, (void) ); + +/* eehelp.c */ +_PROTOTYPE( int f_describe, (void) ); + +/* eekmac.c */ +_PROTOTYPE( int f_skmac, (void) ); +_PROTOTYPE( int f_ekmac, (void) ); +_PROTOTYPE( int f_xkmac, (void) ); +_PROTOTYPE( int f_vkmac, (void) ); +_PROTOTYPE( int km_getc, (void) ); +_PROTOTYPE( int km_inwait, (void) ); +_PROTOTYPE( int km_abort, (void) ); +_PROTOTYPE( int add_mode, (char *mode) ); +_PROTOTYPE( int remove_mode, (char *mode) ); + +/* eemain.c */ +_PROTOTYPE( int doargs, (int argc, char **argv) ); +_PROTOTYPE( int initialize, (void) ); +_PROTOTYPE( int f_throw, (void) ); +_PROTOTYPE( int ring_bell, (void) ); +_PROTOTYPE( int f_retsup, (void) ); +_PROTOTYPE( int f_wfexit, (void) ); +_PROTOTYPE( int f_pshinf, (void) ); +_PROTOTYPE( char *strdup, (char *s) ); +_PROTOTYPE( char *memalloc, (SBMO size) ); +_PROTOTYPE( int chkfree, (SBMA ptr) ); +_PROTOTYPE( int ustrcmp, (char *str1, char *str2) ); +_PROTOTYPE( int writerr, (char *str) ); +_PROTOTYPE( int writez, (int fd, char *acp) ); + +/* eemake.c */ +_PROTOTYPE( int f_xucmd, (void) ); +_PROTOTYPE( int f_make, (void) ); +_PROTOTYPE( int f_nxterr, (void) ); +_PROTOTYPE( int f_xucmd, (void) ); +_PROTOTYPE( int f_make, (void) ); +_PROTOTYPE( int f_nxterr, (void) ); +_PROTOTYPE( int do_exec, (char *cmd, int nicely) ); +_PROTOTYPE( int make_or_unix_cmd, (int domake) ); +_PROTOTYPE( int sel_execbuf, (void) ); +_PROTOTYPE( int popto_buf, (struct buffer *b) ); + +/* eequer.c */ +_PROTOTYPE( int f_querep, (void) ); +_PROTOTYPE( int f_repstr, (void) ); +_PROTOTYPE( int f_repline, (void) ); +_PROTOTYPE( int ed_dorep, (int type, struct majmode *mode) ); + +/* eeques.c */ +#if 0 +_PROTOTYPE( char *ask, (char *string, char *arg1, char *arg2, char *arg3) ); +#else +char *ask(); +#endif +_PROTOTYPE( int askclr, (void) ); +_PROTOTYPE( int say, (char *str) ); +_PROTOTYPE( int saynow, (char *str) ); +_PROTOTYPE( int saytoo, (char *str) ); +_PROTOTYPE( int sayntoo, (char *str) ); +_PROTOTYPE( int ding, (char *str) ); +_PROTOTYPE( int dingtoo, (char *str) ); +_PROTOTYPE( int saylntoo, (char *str, int n) ); +_PROTOTYPE( int sayclr, (void) ); +#if 0 +_PROTOTYPE( int sayall, (char *str, int flags, int len) ); +#else +int sayall(); +#endif +_PROTOTYPE( int yellat, (char *str, int line) ); +_PROTOTYPE( int yelltoo, (char *str) ); +_PROTOTYPE( int errbarf, (char *str) ); +_PROTOTYPE( int barf, (char *str) ); +_PROTOTYPE( int barf2, (char *str) ); + +/* eesite.c */ +_PROTOTYPE( int ts_inp, (void) ); +_PROTOTYPE( int ts_init, (void) ); +_PROTOTYPE( int ts_enter, (void) ); +_PROTOTYPE( int ts_exit, (void) ); +_PROTOTYPE( int tpoke, (int cmd, int bn, int val) ); +_PROTOTYPE( int ts_pause, (void) ); + +/* eesrch.c */ +_PROTOTYPE( int f_srch, (void) ); +_PROTOTYPE( int f_rsrch, (void) ); +_PROTOTYPE( int lin_search, (int backwards) ); +_PROTOTYPE( int srchint, (void) ); +_PROTOTYPE( char *srch_ask, (char *prompt) ); +_PROTOTYPE( int f_risrch, (void) ); +_PROTOTYPE( int i_search, (int back) ); + +/* eeterm.c */ +_PROTOTYPE( int t_init, (void) ); +_PROTOTYPE( int t_fatal, (char *str) ); +_PROTOTYPE( int t_enter, (void) ); +_PROTOTYPE( int t_exit, (void) ); +_PROTOTYPE( int t_clear, (void) ); +_PROTOTYPE( int t_curpos, (int lin, int col) ); +_PROTOTYPE( int t_backspace, (void) ); +_PROTOTYPE( int t_bell, (void) ); +_PROTOTYPE( int t_cleol, (void) ); +_PROTOTYPE( int t_inslin, (int n, int bot) ); +_PROTOTYPE( int t_dellin, (int n, int bot) ); +_PROTOTYPE( int t_inschr, (int n, char *str) ); +_PROTOTYPE( int t_delchr, (int n) ); +_PROTOTYPE( int t_standout, (int on) ); +_PROTOTYPE( int t_direct, (int lin, int col, char *str, int len) ); +_PROTOTYPE( int tput, (int ch) ); +_PROTOTYPE( int tputz, (char *str) ); +_PROTOTYPE( int tputn, (char *str, int cnt) ); +_PROTOTYPE( int tbufls, (void) ); +_PROTOTYPE( int tgetc, (void) ); +_PROTOTYPE( int tinwait, (void) ); + +/* eevini.c */ diff --git a/commands/elle/eequer.c b/commands/elle/eequer.c new file mode 100755 index 000000000..155fcd7cc --- /dev/null +++ b/commands/elle/eequer.c @@ -0,0 +1,189 @@ +/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* + * EEQUER Query-Replace and Replace-String functions + */ + +#include "elle.h" /* include structure definitions */ + +/* EFUN: "Query Replace" */ +/* Crude approximation of EMACS function. + */ +f_querep() +{ static struct majmode iqrpmode = { "Query Replace" }; + ed_dorep(0, &iqrpmode); +} + +/* EFUN: "Replace String" */ +/* Similar to Query Replace and uses same code. + */ +f_repstr() +{ static struct majmode irepmode = { "Replace String" }; + ed_dorep(1, &irepmode); +} + +#if FX_REPLINE +/* EFUN: "Replace in Line" (not EMACS) */ +/* Acts like Replace String but only operates on current line. +** Currently a big crock. +** Feature of crockishness is that Unkill Pop (M-Y) will restore old +** line. +*/ +f_repline() +{ + extern struct buffer *make_buf(); + struct buffer *b, *oldb = cur_buf; + static struct majmode rlmode = { "Replace in Line" }; + + if(!(b = make_buf(" **LINE**"))) + { ring_bell(); + return; + } + f_kline(); /* Kill line(s) from original buffer */ + chg_buf(b); /* Switch to temp buffer */ + f_unkill(); /* Get killed stuff into temp buffer */ + e_gosetcur((chroff)0); /* Starting at beginning, */ + ed_dorep(1, &rlmode); /* Execute Replace String on it. */ + ed_kill((chroff)0, e_blen()); /* Now kill everything in it, */ + chg_buf(oldb); /* switch back to original buffer, */ + f_unkill(); /* and restore new stuff! */ + kill_buf(b); /* Now flush temporary buffer. */ +} +#endif + + +/* Note that the major mode is set without changing the buffer's major + * mode. When the function is done, the current major mode is reset + * from the buffer mode. + */ +ed_dorep(type, mode) /* 0 = Query Replace, 1 = Replace String */ +int type; +struct majmode *mode; +{ register int c; + register int olen, allflg; + char *srch_ask(); + char *opromp, *npromp; + char *nstr, *ostr; /* Note ostr is == to srch_str */ + int nlen; + chroff last_loc; +#if IMAGEN + int nrepled = 0; + char replmsg[64]; +#endif /*IMAGEN*/ + + /* Set mode, then get search string and replace string */ +#if IMAGEN + cur_win->w_buf->b_flags |= B_QUERYREP; +#else + cur_mode = mode; /* Set major mode pointer */ +#endif /*-IMAGEN*/ + + redp(RD_MODE); + nstr = 0; +#if IMAGEN + opromp = "Old string: "; + npromp = "New string: "; +#else + opromp = "Replace string: "; + npromp = "with string: "; +#endif /*-IMAGEN*/ + if((ostr = srch_ask(opromp)) == 0) + goto done; + olen = srch_len; /* srch_ask sets this! */ + if((nstr = ask("%s%s %s", opromp, ostr, npromp)) == 0) + goto done; + nlen = ask_len; + + /* Now enter search and subcommand loop */ + allflg = type; /* Unless 0 for Query Rep, replace all */ + for(;;) + { last_loc = cur_dot; + if(e_search(ostr,olen,0) == 0) + break; + ed_setcur(); /* Cursor moved */ + redisp: + if(!allflg) redisplay(); /* Update screen */ + getcmd: + if(!allflg) c = cmd_read(); + else c = SP; + switch(c) + { +#if IMAGEN + case 'n': +#endif /*IMAGEN*/ + case DEL: /* Don't replace, go on */ + continue; +#if IMAGEN + case ',': +#endif /*IMAGEN*/ + case '.': /* Replace and exit */ + case SP: /* Replace, go on */ + ed_delete(cur_dot,(chroff)(cur_dot-olen)); + ed_nsins(nstr,nlen); +#if IMAGEN + ++nrepled; +#endif /*IMAGEN*/ + if(c == '.') goto done; + continue; +#if IMAGEN + default: +#endif /*IMAGEN*/ + case '?': /* Show options */ +#if IMAGEN + saynow("\ +' '=>change, 'n'=>don't, '.'=>change, quit, '!'=>change rest, '^'=>back up"); +#else + saynow("\ +SP=Replace, DEL=Don't, ESC=Stop, !=Replace all, ^=Back up, .=Replace & Stop"); +#endif /*-IMAGEN*/ + goto getcmd; + case '^': /* Return to last place found */ + ed_go(last_loc); + goto redisp; + + case CTRL('G'): + case ESC: /* Exit where we are */ + goto done; + + case CTRL('L'): /* Redisplay */ + redp(RD_SCREEN); + goto redisp; + + case '!': /* Replace all the rest */ + allflg++; + goto getcmd; + +#if !(IMAGEN) + case ',': /* Replace and show */ + case CTRL('R'): /* Enter edit mode recursively */ + case CTRL('W'): /* Delete once and ^R */ + saynow("not implemented"); + goto getcmd; + default: /* Exit and re-read char */ + unrchf = c; + goto done; +#endif /*-IMAGEN*/ + } + } +done: +#if IMAGEN + cur_win->w_buf->b_flags &= ~B_QUERYREP; +#else + cur_mode = cur_buf->b_mode; +#endif /*-IMAGEN*/ + + redp(RD_MODE); + if(nstr) /* Free nstr (but not ostr, it's == srch_str!) */ + chkfree(nstr); +#if IMAGEN + if (nrepled <= 0) + saynow("No replacements done"); + else + { sprintf(replmsg, "Replaced %d occurences", nrepled); + saynow(replmsg); + } +#endif /*IMAGEN*/ +} diff --git a/commands/elle/eeques.c b/commands/elle/eeques.c new file mode 100755 index 000000000..6d729f2c8 --- /dev/null +++ b/commands/elle/eeques.c @@ -0,0 +1,325 @@ +/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* EEQUES - Handle queries and status displays + */ +#include "elle.h" + +/* + * Ask -- ask the user for some input on the lowest line of the screen + * + * The arg is a string in printf form, followed by up to three args + * for the printf string + * + * The string is read into a sort of mini buffer, only the + * last line of which is visible on the screen. All editing + * features are available to the user to edit the input string. + * When the delim character is typed, input is terminated and + * The input string is passed back to the caller. + * The delim is either an escape or a cr. + * IT IS UP TO THE CALLER TO FREE THIS MEMORY. + * + * Note that the actual length of the returned string can be found + * in the global variable ask_len. This is a crock but allows + * callers to hack nulls in arg strings if they want to. + */ + +int chg_win(); +struct window *make_mode(); + +static int ask_lin; /* Saved cursor location when ask is done */ +static int ask_blen; /* Non-zero if buffer contains something */ +static int ask_cnt; /* Incremented by ask(), cleared by askclr() */ + +/* Table of allowed functions during ASK */ +static char askftab[] = { + FN_PFXMETA, FN_INSSELF, FN_BEGLINE, FN_ENDLINE, FN_BCHAR, FN_FCHAR, + FN_DCHAR, FN_BDCHAR, FN_TCHARS, FN_QUOTINS, FN_UARG, FN_BKPT, + FN_DEBUG, + FN_GOBEG, FN_GOEND, FN_FWORD, FN_BWORD, FN_KWORD, FN_BKWORD, + FN_UCWORD, FN_LCWORD, FN_UCIWORD, FN_ARGDIG, FN_NEWWIN, FN_KLINE, + FN_UNKILL, FN_BKLINE, + 0 +}; + +char * +ask (string, arg1, arg2, arg3) +char *string, *arg1, *arg2, *arg3; + +{ register int i, c; + register char *s; + struct window *oldwin; + char *newline; /* where output line goes */ + char cbuf[200]; /* For prompt string creation */ + int p_length; /* length of prompt */ + chroff anslen; /* Length of answer */ + int funnum; /* Crock stuff */ +#if FX_FILLMODE + extern int fill_mode; + int ofillmode = fill_mode; /* Gotta turn this one off */ + fill_mode = 0; +#endif /*FX_FILLMODE*/ + + oldwin = cur_win; + chg_win (ask_win); + ed_reset(); /* Flush contents & request redisp */ + ask_lin = cur_win->w_pos; /* Set here in case never redisp */ + ask_cnt++; /* Bump # of times called */ + + /* copy 'string' into line */ + cbuf[0] = 0; +asklp: sprintf (&cbuf[strlen(cbuf)], string, arg1, arg2, arg3); + p_length = strlen(cbuf); /* Find how long it is */ + + /* now let the user type in */ + for(;;) + { + if ((rd_type & (RDS_WINFLGS|RD_MODE)) && tinwait () == 0) + { + e_gobob(); /* Gross crock: insert prompt */ + e_sputz(cbuf); /* Ugh, bletch */ + cur_dot += p_length; /* Temporarily update loc */ + redp(RD_WINRES); /* Do complete re-crunch */ + upd_wind((struct window *)0); /* Don't interrupt */ + /* Ensure mode line is spiffy too. This should + ** only have to be invoked the first time ask_win + ** redisplay is done, and never thereafter. + */ + if(rd_type&RD_MODE) /* If mode also needs it, */ + fupd_wind(make_mode(user_win)); /* do it */ + + upd_curs(cur_dot); + rd_type &= ~(RDS_WINFLGS|RD_MODE); + ask_lin = curs_lin; /* Remember line cursor on */ + tbufls(); + + e_gobob(); /* More crock: Remove prompt */ + sb_deln((SBBUF *)cur_buf,(chroff)p_length); /* Ugh etc. */ + cur_dot -= p_length; /* Restore loc */ + e_gocur(); + } + exp = 1; + exp_p = 0; + cont: this_cmd = 0; + c = cmd_read(); + + if ( +#if !(ICONOGRAPHICS) + c == ESC || +#endif /*-ICONOGRAPHICS*/ + c == LF || c == CR) + break; + if (c == BELL) /* ^G means punt.. */ + { chg_win(oldwin); + ask_blen = 1; /* Assume buffer has something */ + ding((char *)0); /* Clear echo window */ + ask_cnt = 0; /* Nothing for askclr to do */ +#if FX_SKMAC + km_abort(); +#endif /*FX_SKMAC*/ +#if FX_FILLMODE + fill_mode = ofillmode; +#endif /*FX_FILLMODE*/ + return(0); /* Return 0 to indicate we quit */ + } + /* This censoring section is a real crock! */ + funnum = cmd_idx(c); /* Map key to command */ + while(funnum == FN_PFXMETA) /* Allow META prefix */ + funnum = cmd_idx(c = CB_META|cmd_read()); + for(s = askftab; (i = *s&0377); ++s) + if(funnum == i) break; + switch(i) + { default: /* Permissible function */ + cmd_xct(c); + break; + case FN_NEWWIN: /* Wants redisplay, do specially */ + clear_wind(ask_win); + break; + case 0: /* Illegal function */ + ring_bell(); +#if FX_SKMAC + km_abort(); +#endif /*FX_SKMAC*/ + continue; + } + if(this_cmd == ARGCMD) + goto cont; + } + + if((anslen = e_blen()) > 255) /* Ridiculously long? */ + { strcpy(cbuf,"Huh? Try again - "); +#if FX_SKMAC + km_abort(); +#endif /*FX_SKMAC*/ + goto asklp; + } + i = anslen; + e_gobob(); /* Go to start of buffer */ + e_sputz(cbuf); /* Re-insert prompt so buffer == screen */ + ask_blen = i + 1; /* Say buffer has something in it */ + + s = memalloc((SBMO)(i + 1)); /* Allocate fixed loc for answer */ + newline = s; /* Return ptr to allocated string */ + ask_len = i; /* And return (via global) length of string */ + if(i) do { *s++ = e_getc(); } + while(--i); + *s = '\0'; /* make sure string terminated */ + chg_win(oldwin); +#if FX_FILLMODE + fill_mode = ofillmode; +#endif /*FX_FILLMODE*/ + return (newline); /* return pointer to data */ +} + +/* ASKCLR - Clears the echo area (but not immediately) if the last thing +** done to it was an ask() call. Note that invoking a SAY routine +** specifically causes this to be a no-op; SAYCLR must be done instead. +*/ +askclr() +{ + if(ask_cnt) sayclr(); /* Zap if need be */ +} + +/* SAY - put up some text on bottom line. + * Does this intelligently; text stays up until next SAY or + * screen refresh. + * SAYNOW - like SAY but forces display right away + * SAYTOO - adds to existing stuff + * SAYNTOO - ditto but forces output right away. + * DING - Ring_bell then SAYNOW + * DINGTOO - is to DING as SAYNTOO is to SAYNOW. + * SAYCLR - Clears echo area (but not immediately) + */ +#define SAY_NOW 01 /* Force display immediately */ +#define SAY_TOO 02 /* Add to existing stuff */ +#define SAY_BEL 04 /* Ding bell prior to text */ +#define SAY_LEN 010 /* String length specified by 3rd arg */ + +say(str) char *str; { sayall(str, 0); } +saynow(str) char *str; { sayall(str, SAY_NOW); } +saytoo(str) char *str; { sayall(str, SAY_TOO); } +sayntoo(str) char *str; { sayall(str, SAY_NOW|SAY_TOO); } +ding(str) char *str; { sayall(str, SAY_NOW|SAY_BEL); } +dingtoo(str) char *str; { sayall(str, SAY_NOW|SAY_TOO|SAY_BEL); } +saylntoo(str,n) char *str; { sayall(str, SAY_NOW|SAY_TOO|SAY_LEN, n); } +sayclr() { sayall((char *)0, 0); } + +sayall(str,flags,len) +char *str; +int flags, len; +{ register struct window *w; + register f; + + f = flags; + w = cur_win; + + ask_cnt = 0; /* Always reset this */ + if(str == 0 && ask_blen == 0) /* If clearing, and buff empty */ + return; /* nothing to do. */ + chg_win(ask_win); + if(f&SAY_TOO) + e_goeob(); /* Add to end of existing stuff */ + else e_reset(); /* Flush previous stuff if any */ + if(str) + { if(f&SAY_LEN) /* Insert string to post up */ + ed_nsins(str,len); + else e_sputz(str); + } + ask_blen = e_dot(); /* Remember whether buffer has something */ + + e_setcur(); /* and remember to set dot */ + if(f&SAY_NOW) + { if(f&SAY_BEL) + ring_bell(); + redp(RD_WINRES); + upd_wind((struct window *)0); + tbufls(); + } + else redp(RD_WINRES); /* Set for this window */ + chg_win(w); /* Back to previous window */ + + /* redisplay() does a special check for ask_win->w_redp, so we + ** don't need to set a global flag like RD_CHKALL. + */ +} + +/* YELLAT -- post string on specified line of screen, immediately. + * Differs from SAYNOW and SAYNTOO in that NO buffer + * manipulation is done; screen image is hacked directly. + */ + +yellat(str, line) +char *str; +register int line; +{ register struct scr_line *s; + + s = scr[line]; + strncpy(s->sl_nlin, str, scr_wd0); + s->sl_ncol = strlen(str); +#if IMAGEN + s->sl_flg |= SL_REDO; +#endif + upd_line(line); + tbufls(); +} + +/* YELLTOO -- Append string to previous echo line of screen, immediately. +** Uses the ask_lin variable which is set by ask(). +** Currently this function is only needed for srchint() in EESRCH. +*/ +yelltoo(str) +char *str; +{ register int i; + register struct scr_line *s; + char nstr[MAXLINE]; + + s = scr[ask_lin]; + i = s->sl_col; + nstr[0] = 0; + strncat(strncat(nstr, s->sl_line, i), /* Make new string */ + str, MAXLINE - i); + yellat(nstr, ask_lin); /* Post it */ +} + +/* BARF - output a message on the bottom line of the screen, +** bypassing everything (window, buffer, screen image). +** Does NOT know about SAY's stuff and does not update it! +** Use only in dire straits... +** ERRBARF - same but uses a standard error-message prefix. +*/ + +errbarf(str) +char *str; +{ + barf("\007ELLE Internal Error: "); + tputz(str); + tbufls(); +} + +barf(str) +char *str; +{ + ask_cnt = 0; /* Ensure askclr() disabled */ + t_curpos(scr_ht - ECHOLINES, 0); /* goto echo area */ + t_cleol(); + tputz(str); + tbufls(); + curs_col = -1000; /* Say we dunno where cursor is now */ +} + +#if IMAGEN +/* Same, but do it far from harm's way */ +barf2(str) +char *str; +{ + t_curpos (scr_ht - 1, scr_wid - strlen(str) - 8); + t_cleol (); + tputz(str); + tputz(" --M--"); + tbufls(); + tgetc(); /* Read any char & discard */ + curs_col = -1000; /* Say we dunno where cursor is now */ +} +#endif /*IMAGEN*/ diff --git a/commands/elle/eesigs.h b/commands/elle/eesigs.h new file mode 100755 index 000000000..b29f73b5e --- /dev/null +++ b/commands/elle/eesigs.h @@ -0,0 +1,32 @@ +/* ELLE - Copyright 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* EESIGS.H + * This file is only provided for inclusion only by V6 systems, where + * the standard /usr/include/signal.h file may not exist and thus we + * need to do our own definitions. + */ + +/* Signals marked with "*" cause a core image dump + * if not caught or ignored. */ + +#define SIGHUP 1 /* Hangup (eg dialup carrier lost) */ +#define SIGINT 2 /* Interrupt (user TTY interrupt) */ +#define SIGQUIT 3 /* * Quit (user TTY interrupt) */ +#define SIGILL 4 /* * Illegal Instruction (not reset when caught) */ +#define SIGTRAP 5 /* * Trace Trap (not reset when caught) */ +#define SIGIOT 6 /* * IOT instruction */ +#define SIGEMT 7 /* * EMT instruction */ +#define SIGFPE 8 /* * Floating Point Exception */ +#define SIGKILL 9 /* Kill (cannot be caught or ignored) */ +#define SIGBUS 10 /* * Bus Error */ +#define SIGSEGV 11 /* * Segmentation Violation */ +#define SIGSYS 12 /* * Bad argument to system call */ +#define SIGPIPE 13 /* Write on a pipe with no one to read it */ +#define SIGALRM 14 /* Alarm Clock */ +#define SIGTERM 15 /* Software termination signal (from "kill" pgm) */ + +#define SIG_DFL (int (*)())0 /* Arg to "signal" to resume default action */ +#define SIG_IGN (int (*)())1 /* Arg to "signal" to ignore this sig */ diff --git a/commands/elle/eesite.c b/commands/elle/eesite.c new file mode 100755 index 000000000..660c4b2c4 --- /dev/null +++ b/commands/elle/eesite.c @@ -0,0 +1,364 @@ +/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* EESITE Site dependent frobs + * Primarily TS_ routines for TTY control. Most site-dependent + * routine is TS_INP for detection of TTY input. + */ + +#include "elle.h" + +#if !(V6) +#include /* For SIGTSTP in ts_pause */ +#else +#include "eesigs.h" +#endif + +int tsf_pause = 0; /* Set if ts_pause works. Ref'd by equit in e_main */ + +#if !(SYSV || BBN) /* SYSV and BBN have weird tty calls */ + +#if MINIX +#include +struct termios origterm, newterm; +#else +#if V6 + /* Normal V6 declarations, must provide explicitly */ +struct sgttyb { + char sg_ispeed; + char sg_ospeed; + char sg_erase; + char sg_kill; + int sg_flags; +}; +#define ECHO (010) +#define CRMOD (020) +#define RAW (040) +#else + /* Normal V7 UNIX declarations, can use include file */ +#include +#endif + +struct sgttyb nstate; /* Both V6 and V7 */ +struct sgttyb ostate; /* Both V6 and V7 */ +#endif /*!(SYSV || BBN)*/ +#endif /*!MINIX*/ + + +#if BBN /* BBN system frobs */ +#include "/sys/sys/h/modtty.h" +struct modes nstate; +struct modes ostate; +#endif /*BBN*/ + +#if DNTTY /* DN TTY frobs */ +#include +char partab[2]; /* to satisfy obscene ref in tty.h */ +#endif /*DNTTY*/ + + +#if (UCB || TOPS20) /* UCB, TOPS20 additional frobs */ +#include /* For ts_inp() and tldisc */ +#if IMAGEN +struct tchars otchars, ntchars; /* Original and new tchars */ +#endif /*IMAGEN*/ +#endif /*(UCB || TOPS20)*/ + +#if SYSV /* System V (and PC/IX) crocks */ +#include +#include + +struct termio /* terminal i/o status flags */ + origterm, /* status of terminal at start of ELLE */ + newterm; /* status of terminal when using ELLE */ +#endif /*SYSV*/ + +/* TS_INP + * Ask system if terminal input is available (on file descriptor 0). + * Returns non-zero if so, else returns zero. + * Very important that this call NOT hang or block in any way, + * because it is used to detect type-ahead by the user; + * return should be immediate whether or not input is waiting. + */ +ts_inp() +{ +#if BBN /* Idiosyncratic */ + int cap_buf[2]; + capac (0, &cap_buf[0], 4); + return (cap_buf[0]); +#endif /*BBN*/ + +#if (DNTTY || ONYX) /* Have "empty()" syscall */ + return(empty(0) ? 0 : 1); +#endif /*DNTTY || ONYX*/ +#if (UCB || TOPS20) /* Have FIONREAD ioctl */ + long retval; + if(ioctl(0,FIONREAD,&retval)) /* If this call fails, */ + return(0); /* assume no input waiting */ + return((retval ? 1 : 0)); +#endif /*UCB || TOPS20*/ +#if COHERENT + int retval; + ioctl(0, TIOCQUERY, &retval); + return((retval ? 1 : 0)); +#endif /*COHERENT*/ +#if VENIX86 + struct sgttyb iocbuf; + ioctl(0, TIOCQCNT, &iocbuf); + return(iocbuf.sg_ispeed != 0 ); +#endif /*VENIX86*/ + +#if !(BBN||COHERENT||DNTTY||ONYX||TOPS20||UCB||VENIX86) + return(0); /* Default - never any type-ahead, sigh */ +#endif +} + + +/* TS_INIT() + * Get terminal information from system, initialize things for + * ts_enter and ts_exit. This is called before t_init. + * Must set "trm_ospeed". + */ +ts_init() +{ +#if DNTTY + signal(16,1); /* DN peculiar - turn off ctl-A */ +#endif /*DNTTY*/ + +#if !(MINIX || SYSV || BBN) /* Normal UNIX stuff */ + ioctl(1, TIOCGETP, &ostate); /* Remember old state */ + nstate = ostate; /* Set up edit-mode state vars */ + nstate.sg_flags |= RAW; /* We'll want raw mode */ + nstate.sg_flags &= ~(ECHO|CRMOD); /* with no echoing */ + trm_ospeed = ostate.sg_ospeed; + +#if (IMAGEN && UCB) + /* Get around 4.1+ remote/local flow control bug (from Gosmacs) */ + ioctl(0, TIOCGETC, &otchars); /* Save original tchars */ + ntchars = otchars; + ntchars.t_startc = -1; /* Kill start/stop */ + ntchars.t_stopc = -1; + ioctl(0, TIOCSETC, &ntchars); +#endif /*IMAGEN && UCB*/ +#endif /*!(SYSV || BBN)*/ + +#if BBN + modtty(1, M_GET | M_MODES, &ostate, sizeof(ostate)); /* Save old */ + modtty(1, M_GET | M_MODES, &nstate, sizeof(nstate)); /* Setup new */ + nstate.t_erase = nstate.t_kill = nstate.t_intr = nstate.t_esc = + nstate.t_eof = nstate.t_replay = 0377; + nstate.t_quit = BELL; /* ^G */ + nstate.t_breaks = TB_ALL; /* break on all */ + nstate.t_iflags &= ~TI_ECHO & ~TI_NOSPCL & ~TI_CRMOD; + /* no echos, specials on, no CR -> LF*/ + nstate.t_iflags |= TI_CLR_MSB; /* ignore parity */ + nstate.t_oflags &= ~TO_CRMOD & ~TO_AUTONL; /* no CR -> NL */ + if (trm_flags & NOXONOFF) + nstate.t_oflags &= ~TO_XONXOFF; + else + nstate.t_oflags |= TO_XONXOFF; + + nstate.t_oflags |= TO_CLR_MSB; /* no special high bits */ + nstate.t_pagelen = 0; /* no paging of output */ + trm_ospeed = ostate.t_ospeed; +#endif /*BBN*/ + +#if MINIX + tcgetattr(0, &origterm); /* How things are now */ + newterm = origterm; /* Save them for restore on exit */ + + /* input flags */ + newterm.c_iflag |= IGNBRK; /* Ignore break conditions.*/ + newterm.c_iflag &= ~INLCR; /* Don't map NL to CR on input */ + newterm.c_iflag &= ~ICRNL; /* Don't map CR to NL on input */ + newterm.c_iflag &= ~BRKINT; /* Do not signal on break.*/ + newterm.c_iflag &= ~IXON; /* Disable start/stop output control.*/ + newterm.c_iflag &= ~IXOFF; /* Disable start/stop input control.*/ + + /* output flags */ + newterm.c_oflag &= ~OPOST; /* Disable output processing */ + + /* line discipline */ + newterm.c_lflag &= ~ISIG; /* Disable signals.*/ + newterm.c_lflag &= ~ICANON; /* Want to disable canonical I/O */ + newterm.c_lflag &= ~ECHO; /* Disable echo.*/ + newterm.c_lflag &= ~ECHONL; /* Disable separate NL echo.*/ + newterm.c_lflag &= ~IEXTEN; /* Disable input extensions.*/ + + newterm.c_cc[VMIN] = 1; /* Min. chars. on input (immed) */ + newterm.c_cc[VTIME] = 0; /* Min. time delay on input (immed) */ + + /* Make it stick */ + tcsetattr(0, TCSANOW, &newterm); +#endif /*MINIX*/ + +#if SYSV + ioctl(0, TCGETA, &origterm); /* How things are now */ + newterm = origterm; /* Save them for restore on exit */ + + /* input flags */ + newterm.c_iflag |= IGNBRK; /* Ignore break conditions.*/ + newterm.c_iflag &= ~INLCR; /* Don't map NL to CR on input */ + newterm.c_iflag &= ~ICRNL; /* Don't map CR to NL on input */ + newterm.c_iflag &= ~BRKINT; /* Do not signal on break.*/ + newterm.c_iflag &= ~IXON; /* Disable start/stop output control.*/ + newterm.c_iflag &= ~IXOFF; /* Disable start/stop input control.*/ + + /* line discipline */ + newterm.c_lflag &= ~ISIG; /* Disable signals.*/ + newterm.c_lflag &= ~ICANON; /* Want to disable canonical I/O */ + newterm.c_lflag &= ~ECHO; /* Disable echo.*/ + + newterm.c_cc[4] = 1; /* Min. chars. on input (immed) */ + newterm.c_cc[5] = 1; /* Min. time delay on input (immed) */ + + /* Make it stick */ + ioctl(0, TCSETA, &newterm); +#endif /*SYSV*/ + +#if (UCB || TOPS20) + { int tldisc; + ioctl(0, TIOCGETD, &tldisc); /* Find line discipline */ + +/* The flag IGN_JOB_CONTROL has been introduced to allow job control haters + * to simply ignore the whole thing. When ELLE is compiled with + * -DIGN_JOB_CONTROL, it will exit properly when the Return to Superior + * command is executed. +*/ +#if SIGTSTP +#ifndef IGN_JOB_CONTROL + if(tldisc == NTTYDISC) tsf_pause = 1; +#endif +#endif /*SIGTSTP*/ + + } +#endif /*UCB || TOPS20*/ +} + +/* TS_ENTER() + * Tell system to enter right terminal mode for editing. + * This is called before t_enter. + */ +ts_enter() +{ +#if !(MINIX || SYSV || BBN) + ioctl(1, TIOCSETP, &nstate); +#if IMAGEN && UCB + ioctl(0, TIOCSETC, &ntchars); /* Restore new tchars */ +#endif /*IMAGEN && UCB*/ +#endif /*!(SYSV||BBN)*/ + +#if BBN + modtty (1, M_SET | M_MODES, &nstate, sizeof (nstate)); +#endif /*BBN*/ + +#if MINIX + /* Make it behave as previously defined in ts_init */ + tcsetattr(0, TCSANOW, &newterm); +#endif /*SYSV*/ + +#if SYSV + /* Make it behave as previously defined in ts_init */ + ioctl(0, TCSETA, &newterm); +#endif /*SYSV*/ + +#if DNTTY /* DN hackery! Enable 8-bit input so as to read meta bit. */ + if(dbg_isw) + { tpoke(TH_CSET,T_2FLGS2,EEI); /* Enable ints */ + tpoke(TH_CSETB,T_QUIT, 0377); /* Turn off QUIT intrpt */ + } + else if(trm_flags & TF_METAKEY) + tpoke(TH_CSET,T_2FLGS2,T2_LITIN); /* Turn on 8-bit input! */ +#endif /*DNTTY*/ +} + +/* TS_EXIT + * Tell system to restore old terminal mode (we are leaving edit mode). + * This is called after t_exit. + */ +ts_exit() +{ +#if DNTTY + if(dbg_isw) + tpoke(TH_CCLR,T_2FLGS2,EEI); /* Turn off EEI bit */ + else if(trm_flags & TF_METAKEY) + tpoke(TH_CCLR,T_2FLGS2,T2_LITIN); /* Turn off 8-bit input */ +#endif /*DNTTY*/ + +#if !(MINIX || SYSV || BBN) + ioctl(1, TIOCSETP, &ostate); /* SYSV and BBN don't use stty */ +#if IMAGEN && UCB + ioctl(0, TIOCSETC, &otchars); /* Restore original tchars */ +#endif /*IMAGEN && UCB*/ +#endif /*!(SYSV || BBN)*/ + +#if BBN + modtty (1, M_SET | M_MODES, &ostate, sizeof (ostate)); +#endif /*BBN*/ + +#if MINIX + tcsetattr(0, TCSANOW, &origterm); +#endif /*MINIX*/ + +#if SYSV + ioctl(0, TCSETA, &origterm); +#endif /*SYSV*/ +} + +#if DNTTY +int thkcmd[] { 0, 0, -1 }; +tpoke(cmd,bn,val) +int cmd, bn, val; +{ + thkcmd[0] = cmd|bn; + thkcmd[1] = val; + if(ttyhak(0,&thkcmd) < 0) + return(-1); + else return(thkcmd[1]); +} +#endif /*DNTTY*/ + + +/* TS_PAUSE - Stop process and return control of TTY to superior. + * There is also a flag variable, TSF_PAUSE, which indicates + * whether or not this routine will actually do anything. + */ +#if TOPS20 +#include +#endif + +ts_pause() +{ +#if TOPS20 + int acs[5]; + jsys(HALTF, acs); +#endif + +#if UCB +#if SIGTSTP + signal(SIGTSTP, SIG_DFL); +#if BSD4_2 +#define mask(s) (1 << ((s)-1)) + sigsetmask(sigblock(0) &~ mask(SIGTSTP)); +#endif /*BSD4_2*/ + kill(0, SIGTSTP); +#if BSD4_2 + sigblock(mask(SIGTSTP)); +#endif /*BSD4_2*/ +#endif /*SIGTSTP*/ +#endif /*UCB*/ +} + +ts_winsize() +{ +#ifdef TIOCGWINSZ + struct winsize winsize; + + if (ioctl(1, TIOCGWINSZ, &winsize) == 0) { + if (winsize.ws_row != 0) scr_ht = winsize.ws_row; + if (winsize.ws_col != 0) scr_wid = winsize.ws_col; + } +#endif +} diff --git a/commands/elle/eesite.h b/commands/elle/eesite.h new file mode 100755 index 000000000..79169949f --- /dev/null +++ b/commands/elle/eesite.h @@ -0,0 +1,184 @@ +/* ELLE - Copyright 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* + * EESITE.H Site-dependent switches & definitions + */ + +/* CONDITIONAL COMPILATION SWITCHES */ + +#define V6 0 /* Running on V6 system (else V7 assumed) */ + +#define APOLLO 0 /* Running on an Apollo system */ +#define BBN 0 /* Running on BBN system (tty stuff) */ +#define BSD4_2 0 /* Running on 4.2BSD system */ +#define COHERENT 0 /* Running on Coherent IBM-PC system */ +#define DNTTY 0 /* Running on SRI V6 Deafnet system (tty stuff) */ +#define HPUX 0 /* Running on Hewlett-Packard System V + */ +#define MINIX 1 /* Running on MINIX (IBM-PC) system */ +#define ONYX 0 /* Running on ONYX Z8000 system */ +#define PCIX 0 /* Running on PC/IX (IBM-PC) system */ +#define SUN 0 /* Running on SUN workstation system */ +#define SYSV 0 /* Running on Unix System V (or perhaps Sys III) */ +#define TOPS20 0 /* Running on TOPS-20 KCC C implementation */ +#define UCB 0 /* Running on 2.8, 2.9, or 4.x BSD sys (tty stuff) */ +#define VENIX86 0 /* Running on Venix86 (IBM-PC) system */ + +#define ICONOGRAPHICS 0 /* Using Iconographics configuration version */ +#define IMAGEN 0 /* Using Imagen configuration version */ + +/* Resolve system dependencies */ +#if SUN +#undef BSD4_2 +#define BSD4_2 1 /* SUN uses 4.2BSD */ +#endif + +#if BSD4_2 +#undef UCB +#define UCB 1 /* 4.2 is special case of general UCB stuff */ +#endif /*BSD4_2*/ + +#if (PCIX || HPUX) +#undef SYSV +#define SYSV 1 /* PC/IX & HP-UX are based on System III & V (resp) */ +#endif + +/* Set system or site dependent stuff here */ + +#if V6 +#define void int /* May need this for other systems too */ +#endif + +/* Changes to parameters (elle.h) or variable defaults (e_vinit.c) */ + +#if COHERENT +#define EVFNO2 0 /* "Old" filename postfix - use no old file! */ +#define EVFNN2 "+" /* "New" filename postfix */ +#define TX_COHIBM 1 /* Ensure Coherent IBM-PC console support included */ +#endif /*COHERENT*/ + +#if DNTTY +#define EVLLEN 60 /* Short line length for TDDs */ +#endif /*DNTTY*/ + +#if HPUX +#define EVFNO2 "~" /* Same as CCA Emacs. Sorts last in listing. */ +#endif /*HPUX*/ + +#if MINIX +#define EVFNO2 ".bak" /* "Old" filename postfix */ +#define EVMARKSHOW "Mark set" +#define EVCCOL (33) /* Use this as Comment Column */ +#define EVMVPCT 1 /* 1% - Try to use minimal window repositioning */ +#define EVMODWSO 1 /* Use mode window standout if can */ +#define STRERROR 1 /* Say that implementation provides strerror() */ + +#include +#include +#include +#include +#endif /*MINIX*/ + +#if ONYX +#define STKMEM (4*512) /* ONYX Z8000 seems to have less room avail */ +#endif /*ONYX*/ + +#if BSD4_2 +#define FNAMELEN 255 /* Max size of last filename component */ +#define FNAMSIZ 400 /* Max size of complete filename */ +#endif /*BSD4_2*/ + +#if TOPS20 +#define EVHELPFILE "elle:help.dat" /* T20 ELLE help file */ +#define EVPROFBINFILE "ellep.b1" /* T20 binary profile file */ +#define EVPROFTEXTFILE "ellep.e" /* T20 ASCII profile file */ +#define EVFNO2 0 /* No old filename postfix (T20 has generations) */ +#define EVFNN2 0 /* No new filename postfix (T20 has generations) */ +#define FNAMELEN (40*3) /* Max size of non-directory filename component */ +#define FNAMSIZ (40*5) /* Max size of complete filename */ +#define STRERROR 1 /* Say that implementation provides strerror() */ +#endif /*TOPS20*/ + +#if VENIX86 +#define TIBFSIZ 1 /* Venix86 block reads in raw mode */ +#endif /*VENIX86*/ + +/* Configuration settings */ + +#if ICONOGRAPHICS +#define EVFNO2 "@" /* "Old" filename postfix */ +#define EVMARKSHOW "Set." +#define PARABLOCK 1 /* Values meaningful only for ICONOGRAPHICS */ +#define PARALINE 2 +#define TXC_VISBEL 1 /* Use visible bell if possible */ +#endif /*ICONOGRAPHICS*/ + +#if IMAGEN +#define EVFNO2 ".BAK" /* "Old" filename postfix */ +#define EVMARKSHOW "Mark set" +#define TOBFSIZ (10*80) /* Size of TTY output buffer */ +#define ECHOLINES 2 /* Use 2 echo-area lines, not 1 */ +#define MAXARGFILES 10 /* Several startup filename args */ +#endif /*IMAGEN*/ + +/* Now set any defaults for things not already defined */ + +/* TERMINAL SUPPORT SWITCHES */ +/* Only those terminals which have a switch defined here */ +/* will be included in ELLE's "hardwired" support. */ +/* Switch name: Compiles support for: */ +#ifndef TX_TERMCAP +#define TX_TERMCAP 1 /* * - most TERMCAP-defined terminals */ +#endif +#ifndef TX_H19 +#define TX_H19 1 /* "H19" - Heath/Zenith-19 */ +#endif +#ifndef TX_DM2500 +#define TX_DM2500 1 /* "DM2500","DM3025" - Datamedia 2500 */ +#endif +#ifndef TX_COHIBM +#define TX_COHIBM 0 /* "COHIBM" - Coherent IBM-PC console */ +#endif +#ifndef TX_TVI925 +#define TX_TVI925 0 /* "TVI925" - TeleVideo 925 */ +#endif +#ifndef TX_OM8025 +#define TX_OM8025 0 /* "OM8025" - Omron 8025AG */ +#endif + +#ifndef TXC_VISBEL /* Non-zero if want to use visible bell */ +#define TXC_VISBEL 0 +#endif + +/* Default terminal type string, used if ELLE cannot get type either +** from $TERM or from startup args. +*/ +#ifndef TXS_DEFAULT +#define TXS_DEFAULT "H19" /* Default terminal type string */ +#endif + +/* Combination parameter/switch definitions */ + +/* STKMEM - System-dependent stack allocation crock, defines amount of + * stack memory to grab for general-purpose use. This is mainly + * useful for PDP-11s or machines with similarly brain-damaged + * address space hardware. A PDP-11 memory segment is 8K bytes, + * or 16 512-byte blocks, and the stack segment quarantines all of + * this space even though the actual stack may only use a miniscule + * portion of it. + */ + +/* Use this if compiling for a PDP11 system, otherwise leave undefined.. */ +#if (V6 || 0) +#define STKMEM (8*512) /* Use half a PDP11 segment */ +#endif + +/* These defaults are in eesite.h so ELLEC can get at them too. */ +#ifndef EVPROFBINFILE /* Location of binary user profile, relative to HOME */ +#define EVPROFBINFILE ".ellepro.b1" +#endif +#ifndef EVPROFTEXTFILE /* Location of ASCII user profile (used by ELLEC) */ +#define EVPROFTEXTFILE ".ellepro.e" +#endif diff --git a/commands/elle/eesrch.c b/commands/elle/eesrch.c new file mode 100755 index 000000000..c54c4bf2e --- /dev/null +++ b/commands/elle/eesrch.c @@ -0,0 +1,276 @@ +/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* + * EESRCH Searching functions + */ + +#include "elle.h" +#if !(V6) +#include +#else +#include "eesigs.h" /* Use this on V6 system */ +#endif /*V6*/ + +/* + * Buffer String Search routines + * + * If no search string is provided, a string that was previously + * used in the last search is once again used. + */ + +/* EFUN: "String Search" */ +f_srch() +{ return (lin_search (0)); +} + +/* EFUN: "Reverse String Search" */ +f_rsrch() +{ return (lin_search (1)); +} + +/* LIN_SEARCH - Main routine for non-incremental String Search. Asks for + * a search string and looks for it. + */ +lin_search (backwards) +int backwards; +{ register char *mem; /* item to be searched for */ + register int res; + int srchint(), (*sav_srchalarm)(); + char *srch_ask(); + chroff savdot; + + savdot = cur_dot; /* Save original loc */ + +#if ICONOGRAPHICS + if((mem = srch_ask(backwards ? "Reverse Search%s%s%s" + : "Search%s%s%s"))==0) + return; +#else + if((mem = srch_ask(backwards ? "Reverse Search: " : "Search: "))==0) + return; +#endif /*-ICONOGRAPHICS*/ + sav_srchalarm = signal(SIGALRM,/*&*/srchint); /* Handle timeout */ + alarm(1); /* One sec from now */ + + res = e_search(mem,srch_len,backwards); /* Search for str! */ + + alarm(0); /* Turn off alarm */ + signal(SIGALRM,sav_srchalarm); /* Restore old handler */ + + if(res) /* Search won? */ + { ed_setcur(); + return; + } + + /* Search failed */ + e_gosetcur(savdot); + ding("Search Failed"); +} + +srchint() +{ yelltoo(" ..."); +} + +char * +srch_ask(prompt) +char *prompt; +{ register char *ans, *old; + +#if ICONOGRAPHICS + if (srch_str) + ans = ask(prompt, " (", srch_str, "): "); + else ans = ask (prompt, ": ", "", ""); + if (ans == 0) return (0); +#else + if((ans = ask(prompt)) == 0) + return(0); /* user punted ... */ +#endif /*-ICONOGRAPHICS*/ + old = srch_str; + if (*ans == '\0') + { chkfree(ans); + if ((ans = old) == 0) /* no string specified */ + { dingtoo("Nothing to search for"); + return(0); + } +#if !(ICONOGRAPHICS) + saylntoo(old, srch_len); /* Show what old string is */ +#endif /*-ICONOGRAPHICS*/ + } + else + { if (old) + chkfree(old); /* free up old srch string */ + srch_str = ans; + srch_len = ask_len; + } + return(ans); +} + +#if 0 + Incremental Search stuff. +Description of EMACS behavior: + ^Q quotes next char. + DEL cancels last char. If this cancelled a match, point is moved + to previous match. + If not all of input can be found, it is not discarded. Can rub out, + discard unmatched stuff with ^G, exit, etc. + ^S repeats search forward; ^R repeats backward. + If empty string, either + changes direction (if not same) + or brings back previous string + ESC exits. If empty string, changes to non-incremental string search. + ^G of a winning search aborts, exits, and moves point back to origin. + ^G of a failing search discards the input that wasn''t found. + Other C- or M- chars exit and are executed. +ELLE also interprets ^H (BS) as DEL, because some keyboards make it hard to + type DEL and there is no way the user can + re-bind the incremental-search commands. +#endif /*COMMENT*/ + +#if FX_ISRCH +/* EFUN: "Incremental Search" */ +f_isrch() { i_search(0); } +#endif /*FX_ISRCH*/ + +#if FX_RISRCH +/* EFUN: "Reverse Search" */ +f_risrch() { i_search(1); } +#endif /*FX_RISRCH*/ + +#if FX_ISRCH || FX_RISRCH + +i_search(back) +int back; /* Current mode: 0 if forward, 1 if backward */ +{ register int c; + register int inpcnt; /* # chars in current input srch str */ + int inpgood; /* Length of last winning string */ + char inpstr[ISRCHLIM]; /* Holds current input search string */ + chroff inpdot[ISRCHLIM]; /* Holds winning addrs for each */ + struct window *savwin; + int winning; /* 1 = currently winning, 0 = currently failing */ + int pref, shown; + int f_insself(), (*(cmd_fun()))(); + + winning = 1; + inpcnt = 0; + inpgood = 0; + inpdot[0] = cur_dot; + savwin = cur_win; + + /* Set up prompt and read all TTY input thus far */ + shown = 0; + sloop: c = cmd_wait(); /* See if any command input waiting */ + if(shown || !c) + { e_setcur(); /* Assume we moved around, so set cur_dot */ + chg_win(ask_win); + ed_reset(); /* Flush contents & invoke redisplay */ + ed_sins(back ? "R-search: " : "I-search: "); + ed_nsins(inpstr, inpcnt); + if(!winning) ed_sins("\t(FAILING)"); + upd_wind((struct window *)0); /* Force ask_win update */ + if(c) + { upd_curs(cur_dot); + tbufls(); + } + chg_win(savwin); + shown = 1; /* Say search prompt has been shown */ + } + if(!c) /* If no user input waiting, show buffer */ + { redp(RD_MOVE); /* Cursor moved in window */ + redisplay(); + } + c = cmd_read(); /* Get input char */ + switch(c) + { case DEL: /* Cancel last char */ + case BS: /* Hard to type DEL on some kbds */ + if(inpcnt <= 0) goto sloop; + if(--inpcnt > inpgood) goto sloop; + winning = 1; + if(inpcnt == inpgood) goto sloop; + inpgood--; + ed_go(inpdot[inpcnt]); + goto sloop; + + case CTRL('Q'): + c = cmd_read(); /* Quote next char */ + break; + case CTRL('S'): + pref = 0; + goto ctlsr; + case CTRL('R'): + pref = 1; + goto ctlsr; + + case CTRL('G'): + if(winning) + { ed_go(inpdot[0]); + goto sdone; + } + inpcnt = inpgood; + winning = 1; + goto sloop; + case ESC: + case CR: + if(inpcnt) + goto sdone; + lin_search(back); + return; + default: + if(f_insself != cmd_fun(c)) + { unrchf = c; + goto sdone; + } + case TAB: /* Strange self-inserting char */ + break; + } + if(inpcnt >= ISRCHLIM-1) + { ding("I-search str too long"); + sleep(1); + goto sdone; + } + inpstr[inpcnt++] = c; + if(!winning) goto sloop; + + /* Now search for string. (Arm alarm interrupt?) */ + /* cur_dot has current location cursor is at; we want to back off + * from this so a repeated search will find the same location if + * appropriate. */ + e_igoff(back ? inpcnt : -(inpcnt-1)); +dosrch: + winning = e_search(inpstr,inpcnt,back); + if (winning) + { inpgood = inpcnt; /* Remember last win length */ + inpdot[inpcnt] = e_dot(); /* and location */ + } + else e_gocur(); /* Back to start position */ + goto sloop; + + ctlsr: if (pref != back) + { back = pref; + if(inpcnt <= 0) goto sloop; + } + if(inpcnt <= 0) + { if(!srch_str || (inpcnt = srch_len) <= 0) + goto sloop; + bcopy((SBMA)srch_str, (SBMA)inpstr, srch_len); + inpcnt = srch_len; + unrchf = c; /* Repeat cmd after display */ + shown = 1; /* Force search-string display */ + goto sloop; + } + goto dosrch; + + sdone: + if(srch_str) chkfree(srch_str); + srch_str = memalloc((SBMO)(inpcnt+1)); + bcopy((SBMA)inpstr,(SBMA)srch_str,inpcnt); /* Copy into srch_str */ + srch_len = inpcnt; + e_setcur(); + chg_win(ask_win); + ed_reset(); + chg_win(savwin); + redp(RD_CHKALL); +} +#endif /*FX_ISRCH || FX_RISRCH*/ diff --git a/commands/elle/eeterm.c b/commands/elle/eeterm.c new file mode 100755 index 000000000..99429c255 --- /dev/null +++ b/commands/elle/eeterm.c @@ -0,0 +1,1283 @@ +/* ELLE - Copyright 1982, 1984, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* + * EETERM ELLE Terminal Driver. + * Directly supports DM2500, H-19, Omron 8025AG, Coherent/IBM-PC, TVI925. + * Others also supported if using TX_TERMCAP. + */ + +#include "elle.h" + +/* Define terminal indices (there may be holes but C preprocessor is too + * stupid to let us close them). Should be one TN_ definition for every + * hardwired terminal type, even though whether or not it is actually + * compiled depends on which TX_ switches are defined. + */ +#define TN_TERMCAP 0 +#define TN_DM2500 1 +#define TN_H19 2 +#define TN_OM8025 3 +#define TN_COHIBM 4 /* Coherent IBM-PC console */ +#define TN_TVI925 5 + +#if TX_COHIBM && !(TX_H19) /* Ensure H19 defined if COHIBM is. */ +#define TX_H19 1 +#endif + +#ifndef TXS_DEFAULT /* If no default is explicitly specified */ +#define TXS_DEFAULT "H19" /* Then settle for H-19 */ +#endif /*TXS_DEFAULT*/ + + + +extern char *tv_stype; /* If set, specifies terminal type */ +extern int tibfmsk; /* Crock to mask off parity (meta) bit */ +static int tv_padc; /* Pad character to use */ +static int tv_cspeed; /* # msec per char (set from trm_ospeed) */ +static int tv_type; /* Index of selected terminal type */ + +/* Internal functions */ +static void tpadn(), tpad(); + +/* Character speed table, indexed by system output speed value (0-017). + * Value in table is 100 * <# msec used per character>. + */ +static int cspdtab[] = +{ /* Val Idx Baud CPS Time/char in msec */ + 0, /* 0 Hangup - ---- */ + 13333, /* 1 50 7.5 133.33 (Baudot) */ + 10000, /* 2 75 10 100.0 (Baudot) */ + 10000, /* 3 110 10 100.0 */ + 8200, /* 4 134.5 12.2 82.0 (IBM2741) */ + 6666, /* 5 150 15 66.6666 */ + 5000, /* 6 200 20 50.0 */ + 3333, /* 7 300 30 33.3333 */ + 1666, /* 8 600 60 16.6666 */ + 833, /* 9 1200 120 8.3333 */ + 555, /* 10 1800 180 5.5555 */ + 416, /* 11 2400 240 4.1666 */ + 208, /* 12 4800 480 2.0833 */ + 104, /* 13 9600 960 1.04166 */ + 0, /* 14 Ext A ? ? */ + 0 /* 15 Ext B ? ? */ +}; + +#if TX_TERMCAP +/* Declarations for TERMCAP stuff. Only EETERM knows about them. */ + +/* Termcap routines */ +extern int tgetent(), tgetnum(), tgetflag(), tputs(); +extern char *tgetstr(), *tgoto(); +static int getcap(); /* Internal routines */ +static void putpad(), putnpad(), putpar(); + +/* Gross disgusting externals that must be defined for TERMCAP rtns */ +char PC; /* Pad char */ +char *BC; /* Backspace to use, if not ^H */ +char *UP; /* Cursor up */ +short ospeed; /* Terminal output speed */ + +/* Termcap numerical values/flags */ +static int + tc_am, /* TRUE if has auto-wrap */ + tc_km; /* TRUE if meta key exists */ + +/* Termcap capability strings we want to know about */ + +struct tcap { char tcicod1, tcicod2, *tccap; }; +static struct tcap tcap[] = { +#define TC_al tcap[0].tccap /* Add (insert) line */ + {'a','l', 0}, +#define TC_AL tcap[1].tccap /* Add N lines */ + {'A','L', 0}, +#define TC_bc tcap[2].tccap /* Backspace Char (for BC) */ + {'b','c', 0}, +#define TC_ce tcap[3].tccap /* Erase to end of line (CLEOL) */ + {'c','e', 0}, +#define TC_cl tcap[4].tccap /* Clear screen */ + {'c','l', 0}, +#define TC_cm tcap[5].tccap /* Cursor motion */ + {'c','m', 0}, +#define TC_dc tcap[6].tccap /* Delete char */ + {'d','c', 0}, +#define TC_DC tcap[7].tccap /* Delete N chars */ + {'D','C', 0}, +#define TC_dl tcap[8].tccap /* Delete line */ + {'d','l', 0}, +#define TC_DL tcap[9].tccap /* Delete N lines */ + {'D','L', 0}, +#define TC_dm tcap[10].tccap /* Delete mode on */ + {'d','m', 0}, +#define TC_ed tcap[11].tccap /* Delete mode off */ + {'e','d', 0}, +#define TC_ei tcap[12].tccap /* Insert mode off */ + {'e','i', 0}, +#define TC_ia tcap[13].tccap /* Add line while in insert mode (see note) */ + {'i','a', 0}, +#define TC_ic tcap[14].tccap /* Insert blank char */ + {'i','c', 0}, +#define TC_IC tcap[15].tccap /* Insert N blank chars */ + {'I','C', 0}, +#define TC_id tcap[16].tccap /* Delete line while in del mode (see note) */ + {'i','d', 0}, +#define TC_im tcap[17].tccap /* Insert mode on */ + {'i','m', 0}, +#define TC_ip tcap[18].tccap /* Padding to send after char insertion */ + {'i','p', 0}, +#define TC_mm tcap[19].tccap /* String to set (turn on) meta-key mode */ + {'m','m', 0}, +#define TC_mo tcap[20].tccap /* String to reset (turn off) meta-key mode */ + {'m','o', 0}, +#define TC_pc tcap[21].tccap /* Pad Char (for PC) */ + {'p','c', 0}, +#define TC_se tcap[22].tccap /* End standout mode */ + {'s','e', 0}, +#define TC_so tcap[23].tccap /* Enter standout mode */ + {'s','o', 0}, +#define TC_te tcap[24].tccap /* String to end programs that use termcap */ + {'t','e', 0}, +#define TC_ti tcap[25].tccap /* String to beg programs that use termcap */ + {'t','i', 0}, +#define TC_up tcap[26].tccap /* Move cursor up (for UP) */ + {'u','p', 0}, +#define TC_vb tcap[27].tccap /* Visible bell */ + {'v','b', 0}, +}; +#define NTCAPS ((sizeof(tcap))/(sizeof(struct tcap))) /* # entries */ + +/* + * There are many other things that must be taken into account. + * The termcap code here will probably not work for many termcap entries, + * but the only sure way to find out which ones they are is to try them. + */ +/* Note that the "ia" and "id" strings are not defined by the TERMCAP doc; + * their usage here is derived from examining other TERMCAP-using programs. + * Sigh!!!! + */ +#endif /*TX_TERMCAP*/ + +/* T_INIT is called once only at program startup, to identify the + * terminal type and set up any one-time things. + * T_FATAL is only called if some routine detects an error related to the + * terminal specification, before any initialization is done. + * It prints a short error message and exits the program. + * T_ENTER is called after TS_ENTER to set the terminal parameters for + * editing (as opposed to normal typeout). It may be called + * several times. + * T_EXIT is called before TS_EXIT to restore normal typeout modes. + * It is called on exit from the program, and perhaps other times. + */ +t_init() +{ + char *getenv(); + + /* Set some default parameters */ + scr_ht = 24; + scr_wid = 79; + trm_flags = 0; + tvc_cin = 1; /* Assume 1 char per char I/D pos */ + tvc_cdn = 1; + tvc_pos = 4; /* Default abs-move cost is 4 chars */ + tvc_bs = 1; /* Default backspace cost is 1 char */ + tv_cspeed = cspdtab[trm_ospeed]; /* Find # msec per char */ + + /* First must determine terminal type, and check for terminals + * that are hardwired into ELLE. */ + if(!tv_stype /* String set in command line args? */ +#if !(V6) + && !(tv_stype = getenv("TERM")) /* or given by TERM var? */ +#endif /*-V6*/ + ) tv_stype = TXS_DEFAULT; /* No, try using default */ + if(0) ; /* Sigh, stupid construct */ +#if TX_H19 + else if(ustrcmp(tv_stype,"H19")) tv_type = TN_H19; +#endif /*TX_H19*/ +#if TX_OM8025 + else if(ustrcmp(tv_stype,"OM8025")) tv_type = TN_OM8025; +#endif /*TX_OM8025*/ +#if TX_DM2500 + else if(ustrcmp(tv_stype,"DM2500")) tv_type = TN_DM2500; + else if(ustrcmp(tv_stype,"DM3025")) tv_type = TN_DM2500; +#endif /*TX_DM2500*/ +#if TX_COHIBM + else if(ustrcmp(tv_stype,"COHIBM")) tv_type = TN_COHIBM; +#endif /*TX_COHIBM*/ +#if TX_TVI925 + else if(ustrcmp(tv_stype,"TVI925")) tv_type = TN_TVI925; +#endif /*TX_TVI925*/ +#if TX_TERMCAP /* This should be last thing */ + else if(getcap(tv_stype)) tv_type = TN_TERMCAP; +#endif /*TX_TERMCAP*/ + else t_fatal("type unknown"); /* Ugh, barf and exit */ + + /* Terminal selected, now initialize parameters for it. */ + switch(tv_type) + { +#if TX_DM2500 + case TN_DM2500: + tv_padc = 0177; /* Use rubout for pad */ + tvc_pos = 3; /* Only 3 chars for abs mov */ + tvc_ci = 2; + /* tvc_cin = 1; */ /* Default is OK */ + tvc_cd = 2; + /* tvc_cdn = 1; */ /* Default is OK */ + tvc_ld = 2; + tvc_ldn = 1; + tvc_li = 2; + tvc_lin = 1; + if(trm_ospeed == 13) /* If 9600, */ + { tvc_cin = 5; /* Sigh, high cost */ + tvc_cdn = 2; + tvc_lin = 18; + tvc_ldn = 2; + } + trm_flags |= TF_IDLIN|TF_IDCHR|TF_CLEOL|TF_METAKEY; + break; +#endif /*TX_DM2500*/ +#if TX_H19 + case TN_H19: + trm_flags |= TF_IDLIN|TF_IDCHR|TF_CLEOL; + tvc_ci = 8; + /* tvc_cin = 1; */ /* default is ok */ + tvc_cd = 0; + tvc_cdn = 2; + /* tvc_ld = 0; */ /* Default is OK */ + tvc_ldn = 1 << (trm_ospeed - 7); + /* tvc_li = 0; */ /* Default is OK */ + tvc_lin = tvc_ldn; + break; +#endif /*TX_H19*/ +#if TX_COHIBM + case TN_COHIBM: + trm_flags |= TF_IDLIN|TF_IDCHR|TF_CLEOL|TF_METAKEY|TF_DIRVID; + /* Always use lowest possible costs */ + /* tvc_ci = 0; */ /* Default */ + tvc_cin = 2; + /* tvc_cd = 0; */ /* Default */ + tvc_cdn = 2; + /* tvc_ld = 0; */ /* Default */ + tvc_ldn = 2; + /* tvc_li = 0; */ /* Default */ + tvc_lin = 2; + break; +#endif /*TX_COHIBM*/ +#if TX_OM8025 + case TN_OM8025: + trm_flags |= TF_IDLIN|TF_IDCHR|TF_CLEOL; + tvc_pos = 6; + /* tvc_ci = tvc_cd = 0; */ /* Default */ + tvc_cin = 4; + tvc_cdn = 2; + /* tvc_ld = tvc_li = 0; */ /* Default */ + tvc_ldn = 10; /* Crude approx */ + tvc_lin = 10; + if(trm_ospeed > 7) /* If faster than 300 baud */ + trm_flags &= ~TF_IDLIN; /* Turn off LID */ + break; +#endif /*TX_OM8025*/ +#if TX_TVI925 + case TN_TVI925: + trm_flags |= TF_IDLIN|TF_IDCHR|TF_CLEOL; + tvc_ci = tvc_cd = tvc_cin = tvc_cdn + = tvc_ldn = tvc_lin = 2; + break; +#endif /*TX_TVI925*/ + } + if(tibfmsk < 0) /* If mask is still default -1, set it. */ + tibfmsk = ((trm_flags&TF_METAKEY) ? 0377 : 0177); +} + +/* T_FATAL(str) - prints error message and exits. +*/ +t_fatal(str) +char *str; +{ writerr("ELLE: \""); + writerr(tv_stype); + writerr("\" terminal "); + writerr(str); + writerr("\n"); + exit(1); /* Terminate with prejudice */ +} + +/* T_ENTER is called after TS_ENTER to set the terminal parameters for + * editing (as opposed to normal typeout). + * Standout mode must initially be off. + */ + +t_enter() +{ switch(tv_type) + { +#if TX_TERMCAP + case TN_TERMCAP: + putpad(TC_ti); + if(tc_km) putpad(TC_mm); /* Use meta if poss */ +#if FX_SOWIND + t_standout(0); /* Ensure standout mode off */ +#endif + break; +#endif /*TX_TERMCAP*/ +#if TX_DM2500 + case TN_DM2500: + tput(030); /* Just in case, flush stray modes */ + break; +#endif /*TX_DM2500*/ +#if TX_COHIBM + case TN_COHIBM: /* Note TN_H19 will exist too */ +#endif /*TX_COHIBM*/ +#if TX_H19 + case TN_H19: + /* Enter ZDS (Heath) mode, then + * Exit graphics mode (G) Exit ins-char mode (O) + * exit rev video mode (q) exit hold-screen mode (\) + * set cursor on (y5) + */ + tputz("\033[?2h\033G\033O\033q\033\\\033y5"); + /* Set Discard-at-EOL (w) + * Set no auto-CR (y9) + * Enable 25th line (x1) + */ + tputz("\033w\033y9\033x1"); + break; +#endif /*TX_H19*/ + } +} + +/* T_EXIT - Leave editing modes. This function should restore +** the terminal's modes to what they were before ELLE was started. +** Standout mode is turned off. +*/ + +t_exit() +{ + switch(tv_type) + { +#if TX_TERMCAP + case TN_TERMCAP: + if(tc_km) putpad(TC_mo); /* Turn off meta */ + putpad(TC_te); + break; +#endif /*TX_TERMCAP*/ +#if TX_DM2500 + case TN_DM2500: + tput(035); /* Turn on roll mode */ + break; +#endif /*TX_DM2500*/ +#if TX_COHIBM + case TN_COHIBM: /* If this exists, TN_H19 will too */ +#endif /*TX_COHIBM*/ +#if TX_H19 + case TN_H19: + tputz("\033v"); /* Turn EOL-wrap back on */ +#if DNTTY + tputz("\033<"); /* Return to ANSI mode */ +#endif /*DNTTY*/ + break; +#endif /*TX_H19*/ + } +} + +/* T_CLEAR() - Clears the screen and homes the cursor. + * Always valid - ELLE refuses to support terminals without this. + */ + +t_clear () +{ switch(tv_type) + { +#if TX_TERMCAP + case TN_TERMCAP: + putnpad(TC_cl,scr_ht); + break; +#endif /*TX_TERMCAP*/ +#if TX_DM2500 + case TN_DM2500: + tputz("\036\036"); /* Double Master Clear */ + break; +#endif /*TX_DM2500*/ +#if TX_COHIBM + case TN_COHIBM: /* Note TN_H19 will exist too */ +#endif /*TX_COHIBM*/ +#if TX_H19 + case TN_H19: + tputz("\033E"); + /* tputn(zpadstr,9); */ + break; +#endif /*TX_H19*/ +#if TX_OM8025 + case TN_OM8025: + tputz("\033H\033J"); /* Home then CLEOS */ + tpad(1000); /* One second!!!! */ + break; +#endif /*TX_OM8025*/ +#if TX_TVI925 + case TN_TVI925: + tput(032); /* ^Z */ + break; +#endif /*TX_TVI925*/ + } + curs_lin = curs_col = 0; +} + +/* T_CURPOS(y, x) - Absolute move. Place cursor in given position + * regardless of where it currently is. + * Updates curs_lin, curs_col. + * Always valid -- ELLE refuses to support terminals without this. + */ + +t_curpos (lin, col) +register int lin, col; +{ + if(col > scr_wid) /* Easiest to catch here */ + col = scr_wid; + + /* Do absolute positioning */ + switch(tv_type) + { +#if TX_TERMCAP + case TN_TERMCAP: + putpad(tgoto(TC_cm, col, lin)); + break; +#endif /*TX_TERMCAP*/ +#if TX_DM2500 + case TN_DM2500: + tput(014); + tput(col^0140); + tput(lin^0140); + break; +#endif /*TX_DM2500*/ +#if TX_COHIBM + case TN_COHIBM: /* If this exists, TN_H19 will too */ +#endif /*TX_COHIBM*/ +#if TX_H19 + case TN_H19: + tputz("\033Y"); + tput(lin+040); + tput(col+040); + break; +#endif /*TX_H19*/ +#if TX_OM8025 + case TN_OM8025: + tputz("\033\175"); + tput(0100+((lin+1)>>4)); + tput(0100+((lin+1)&017)); + tput(0100+((col+1)>>4)); + tput(0100+((col+1)&017)); + break; +#endif /*TX_OM8025*/ +#if TX_TVI925 + case TN_TVI925: + tputz("\033="); + tput(lin+040); + tput(col+040); + break; +#endif /*TX_TVI925*/ + } + curs_lin = lin; + curs_col = col; +} + +/* T_BACKSPACE() - Back up 1 character position. + * Updates curs_col. + * Only valid if tvc_bs has a "reasonable" value ( < 1000) + */ + +t_backspace() +{ +#if TX_TERMCAP + if(BC) tputz(BC); /* Use alternate BS */ + else +#endif + tput('\010'); /* Send BS */ + --curs_col; +} + +/* T_BELL() - Ring terminal's bell (or flash something, or whatever). + * Forces out all output thus far, to ensure immediate attention. + * This used to be an unbuffered feep, but was changed to use normal + * output path in order to avoid messing up terminal escape sequences. + */ +t_bell() +{ +#if TXC_VISBEL && TX_TERMCAP + if(TC_vb) + tputz(TC_vb); /* Do visible bell if possible */ + else +#endif + tput(BELL); + tbufls(); /* Force it out */ +} + +/* T_CLEOL() - Clear to End Of Line. + * Only valid if trm_flags has TF_CLEOL set. + */ + +t_cleol () +{ + switch(tv_type) + { +#if TX_TERMCAP + case TN_TERMCAP: + putpad(TC_ce); + break; +#endif /*TX_TERMCAP*/ +#if TX_DM2500 + case TN_DM2500: + tput(027); + break; +#endif /*TX_DM2500*/ +#if TX_COHIBM + case TN_COHIBM: /* If this exists, TN_H19 will too */ +#endif /*TX_COHIBM*/ +#if TX_H19 + case TN_H19: + tputz("\033K"); + break; +#endif /*TX_H19*/ +#if TX_OM8025 + case TN_OM8025: + tputz("\033K"); + tpad(41); /* 1/25 sec padding */ + break; +#endif /*TX_OM8025*/ +#if TX_TVI925 + case TN_TVI925: + tputz("\033T"); + break; +#endif /*TX_TVI925*/ + } +} + +/* T_INSLIN(n, bot) - Insert lines in window. + * n - # blank lines to insert. + * bot - # of last line of current window + * + * The current line is moved down and N blank lines inserted. + * Lines which are moved past bot are lost. + * May leave cursor in random place. + * Only valid if trm_flags has TF_IDLIN set. + */ + +t_inslin (n, bot) +int n; /* number of lines */ +int bot; /* line number of last line in window */ +{ register i, j; + int savc,savl; + + if((i = n) <= 0) return; + if(bot < (scr_ht-1)) + { savc = curs_col; + savl = curs_lin; + t_curpos(bot-i, 0); + t_dellin(i, scr_ht); + t_curpos(savl, savc); + } + switch(tv_type) + { +#if TX_TERMCAP + case TN_TERMCAP: + if(TC_AL) + putpar(TC_AL, i, i); + else if(TC_ia) + { putpad(TC_im); + do { putpad(TC_ia); + } while(--i); + putpad(TC_ei); + } + else + do { putnpad(TC_al, scr_ht - curs_lin); + } while(--i); + break; +#endif /*TX_TERMCAP*/ +#if TX_DM2500 + case TN_DM2500: + tput(020); /* Enter I/D mode */ + do { tput(012); /* Insert line */ + switch(trm_ospeed) + { case 13: j = 17; break; /* 9600 */ + case 12: j = 8; break; /* 4800 */ + case 11: j = 4; break; /* 2400 */ + case 9: j = 2; break; /* 1200 */ + default: j = 0; break; + } + tpadn(j); + } while(--i); + tput(030); /* Exit I/D mode */ + break; +#endif /*TX_DM2500*/ +#if TX_H19 + /* NOTE: H19 supposedly requires 19 ms for each line during line I/D + * operations. + * In actual practice, at 9600 baud 25 pads are necessary (24 wont work!) + * for both I and D. Plus esc-E needs 9 pads. + */ + case TN_H19: + do { tputz("\033L"); + switch(trm_ospeed) + { case 13: j = 25; break; + case 9: j = 4; break; + case 7: j = 1; break; + default: j = 0; break; + } + tpadn(j); + } while(--i); + break; +#endif /*TX_H19*/ +#if TX_COHIBM + case TN_COHIBM: + do { tputz("\033L"); /* no padding required */ + } while(--i); + break; +#endif /*TX_COHIBM*/ +#if TX_OM8025 + case TN_OM8025: + do { tputz("\033L"); + tpad(100*(scr_ht - curs_lin)); /* .1 per moved line*/ + } while(--i); + break; +#endif /*TX_OM8025*/ +#if TX_TVI925 + case TN_TVI925: + do tputz("\033E"); + while(--i); + break; +#endif /*TX_TVI925*/ + } +} + +/* T_DELLIN(n, bot) - Delete lines from window. + * n - # lines to delete. + * bot - # of last line of current window. + * The current line, and N-1 following lines, are deleted. + * Blank lines are inserted past bot. + * Cursor should be left at original position. + * Only valid if trm_flags has TF_IDLIN set. + */ +t_dellin (n, bot) +int n; /* number of lines */ +int bot; /* line number of last line in window */ +{ register i, j; + int savl, savc; + + if((i = n) <= 0) return; + switch(tv_type) + { +#if TX_TERMCAP + case TN_TERMCAP: + if(TC_DL) + putpar(TC_DL, i, i); + else if(TC_id) + { putpad(TC_dm); + do putpad(TC_id); + while(--i); + putpad(TC_ed); + } + else + do { putnpad(TC_dl,scr_ht - curs_lin); + } while(--i); + + break; +#endif /*TX_TERMCAP*/ +#if TX_DM2500 + case TN_DM2500: + tput(020); + do { tput(032); + if(trm_ospeed >= 13) /* 9600 */ + tput(0177); + } while(--i); + tput(030); + break; +#endif /*TX_DM2500*/ +#if TX_H19 + case TN_H19: + do { tputz("\033M"); + switch(trm_ospeed){ + case 13: j = 25; break; + case 9: j = 4; break; + case 7: j = 1; break; + default: j = 0; break; + } + tpadn(j); + } while(--i); + break; +#endif /*TX_H19*/ +#if TX_COHIBM + case TN_COHIBM: + do { tputz("\033M"); /* no padding required */ + } while(--i); + break; +#endif /*TX_COHIBM*/ +#if TX_OM8025 + case TN_OM8025: + do { tputz("\033M"); + tpad(100*(scr_ht - curs_lin)); + } while(--i); + break; +#endif /*TX_OM8025*/ +#if TX_TVI925 + case TN_TVI925: + do { tputz("\033R"); + } while(--i); + break; +#endif /*TX_TVI925*/ + } + if(bot < (scr_ht-1)) + { savl = curs_lin; + savc = curs_col; + t_curpos(bot-n,0); + t_inslin(n,scr_ht); + t_curpos(savl,savc); + } +} + +/* T_INSCHR(n, str) - Insert n chars in current line + * n - # characters to insert + * str - Pointer to char string. If 0, insert spaces. + * + * Insert N characters from string str at current position. + * The cursor may move but curs_col must be updated. + * Only valid if trm_flags has TF_IDCHR set. + */ +t_inschr(n, str) +int n; +char *str; +{ register int i; + register char *cp; + + if((i = n) <= 0) return; + cp = str; + switch(tv_type) + { +#if TX_TERMCAP + case TN_TERMCAP: + putpad(TC_im); /* Go into insert mode */ + if(TC_IC) + { putpar(TC_IC, i, 1); + if(cp) tputn(cp, i); + else do tput(SP); while(--i); + } + else do { + if(TC_ic) putpad(TC_ic); + if(cp) tput(*cp++); + else tput(SP); + if(TC_ip) putpad(TC_ip); + } while(--i); + putpad(TC_ei); /* Exit insert mode */ + curs_col += n; + break; +#endif /*TX_TERMCAP*/ +#if TX_COHIBM + case TN_COHIBM: /* If this exists, TN_H19 will too */ +#endif /*TX_COHIBM*/ +#if TX_H19 + case TN_H19: + tputz("\033@"); /* Enter ins char mode */ + do { if(cp) tput(*cp++); + else tput(SP); + } while(--i); + tputz("\033O"); /* Exit ins char mode */ + curs_col += n; + break; +#endif /*TX_H19*/ +#if TX_DM2500 + case TN_DM2500: + tput(020); /* Enter I/D mode */ + if(trm_ospeed == 13) /* 9600 baud lossage */ + { do { + tputz(" \177"); /* SP and DEL */ + } while(--i); + tput(030); + i = n; + if(i < 3) /* If close enough, */ + tputn("\010\010", i); /* use BSes */ + else t_curpos(curs_lin, curs_col); + } + else /* Not 9600, can win */ + { do { tput(034); + } while(--i); + tput(030); + if(cp == 0) return; + i = n; + } + + do { if(cp) tput(*cp++); + else tput(SP); + } while(--i); + curs_col += n; + break; +#endif /*TX_DM2500*/ +#if TX_OM8025 + case TN_OM8025: + do { + tputz("\033@"); + if(cp) tput(*cp++); + else tput(SP); + } while(--i); + curs_col += n; + break; +#endif /*TX_OM8025*/ +#if TX_TVI925 + case TN_TVI925: + do { tputz("\033Q"); + } while(--i); + if(cp) + { tputn(cp, n); + curs_col += n; + } + break; +#endif /*TX_TVI925*/ + } +} + +/* T_DELCHR(n) - Delete N chars in current line. + * Deletes the N characters to the right of the cursor. Remaining + * chars are shifted left. The cursor should not move. + * Only valid if trm_flags has TF_IDCHR set. + */ +t_delchr(n) /* Delete N chars at current loc */ +int n; +{ register int i; + + if((i = n) <= 0) return; + switch(tv_type) + { +#if TX_TERMCAP + case TN_TERMCAP: + putpad(TC_dm); /* Enter delete mode */ + if(TC_DC) + putpar(TC_DC, i, 1); + else do { /* Delete char while in del mode */ + putpad(TC_dc); + } while(--i); + putpad(TC_ed); /* Exit delete mode */ + break; +#endif /*TX_TERMCAP*/ +#if TX_COHIBM + case TN_COHIBM: /* If this exists, TN_H19 will too */ +#endif /*TX_COHIBM*/ +#if TX_H19 + case TN_H19: + do tputz("\033N"); + while(--i); + break; +#endif /*TX_H19*/ +#if TX_DM2500 + case TN_DM2500: + tput(020); /* Enter I/D mode */ + do if(trm_ospeed == 13) /* 9600? */ + tputz("\010\177"); /* BS and DEL */ + else tput(010); + while(--i); + tput(030); /* Exit I/D mode */ + break; +#endif /*TX_DM2500*/ +#if TX_OM8025 + case TN_OM8025: + do tputz("\033P"); + while (--i); + break; +#endif /*TX_OM8025*/ +#if TX_TVI925 + case TN_TVI925: + do { tputz("\033W"); + } while(--i); +#endif /*TX_TVI925*/ + } +} + +#if FX_SOWIND + +/* T_STANDOUT(n) - Enter or leave standout mode. + * n - 0 to return to normal display mode, + * 1 to enter standout display mode. + * This is usually reverse video but may be something else. + * + * Only valid if trm_flags has TF_SO set. + */ + +t_standout(on) +int on; +{ + switch(tv_type) + { +#if TX_TERMCAP + case TN_TERMCAP: + putpad(on ? TC_so : TC_se); + break; +#endif /*TX_TERMCAP*/ + +#if TX_COHIBM + case TN_COHIBM: /* Note TN_H19 will exist too */ +#endif /*TX_COHIBM*/ +#if TX_H19 + case TN_H19: + tputz(on ? "\033p" : "\033q"); + break; +#endif /*TX_H19*/ + } +} +#endif /*FX_SOWIND*/ + + +/* TPADN(n) - Output N pad chars. + */ +static void +tpadn(n) +int n; +{ register int i, pad; + if((i = n) > 0) + { pad = tv_padc; + do { tput(pad); + } while(--i); + } +} + +/* TPAD(msec) - Output padding for given # of milliseconds. + */ +static void +tpad(n) +int n; +{ register int i, i2; + + i = n; + while(i > 0) + { if((i2 = 320) < i) /* So can use integers */ + i2 = i; + i -= i2; + i2 *= 100; + while((i2 -= tv_cspeed) > 0) + tput(tv_padc); + } +} +#if TX_TERMCAP +/* + * Print the string str, interpreting padding. + */ +int tput(); /* Our output function */ +static void +putpad(str) +char *str; +{ if(str) tputs(str, 1, tput); /* Invoke TERMCAP function */ +} +static void +putnpad(str,n) +char *str; +int n; +{ if(str) tputs(str, n, tput); +} +static void +putpar(str, par, n) /* Wish we had tparm() */ +char *str; +int par,n; +{ putnpad(tgoto(str, 0, par), n); +} +#endif /*TX_TERMCAP*/ + +/* + * Read in the stuff from termcap upon startup. + */ + +#if TX_TERMCAP +static int tstrlen(), tstrlp(); + +#ifndef TCAPSLEN +#define TCAPSLEN 1024 /* Default size of buffer for TERMCAP strings */ +#endif /*-TCAPSLEN*/ + +static int +getcap(stype) +char *stype; +{ register char *t; + register int i; + int buflen; + char *tcbuf, *tcbptr; /* Pointers into termcap buffer */ + char tmpstr[4]; + char tmpbuf[TCAPSLEN]; /* Allocate from stack */ + char *malloc(); + char *realloc(); + + /* First see if can find the terminal type. */ + if((tgetent(tmpbuf, stype)) != 1) + return(0); + + /* Found it! Set up a string buffer to save the caps. */ + if(!(tcbuf = malloc(TCAPSLEN))) /* Get permanent buffer */ + t_fatal(" - cannot allocate termcap buffer"); + tcbptr = tcbuf; + + /* Now gobble all the string caps that ELLE wants to know about. */ + tmpstr[3] = '\0'; + i = NTCAPS; + do { + tmpstr[0] = tcap[i].tcicod1; /* Make str of the code */ + tmpstr[1] = tcap[i].tcicod2; + tcap[i].tccap = tgetstr(tmpstr, &tcbptr); /* Get cap */ + } while(--i); + buflen = tcbptr - tcbuf; /* String buffer done, finalize */ + if(buflen >= TCAPSLEN) + t_fatal("description too big!"); + realloc(tcbuf, buflen); /* Free up unused part of buffer */ + /* (this better not move it!!!) */ + + /* Now get the number/flag stuff that ELLE needs. */ + tc_am = tgetflag("am"); /* auto wrap */ + if (tgetflag("xn")) tc_am = 0; /* auto wrap at 81st char, nice! */ + tc_km = (tgetflag("km") /* TTY has meta key */ + || tgetflag("MT")); /* Alternate version of "km"?? */ + scr_ht = tgetnum("li"); /* Set screen height (# lines) */ + scr_wid = tgetnum("co"); /* Set screen width (# cols) */ + ts_winsize(); + + /* Now initialize the stupid external vars that TERMCAP rtns want. */ + if(TC_pc) PC = *TC_pc; /* Pad char */ + BC = TC_bc; /* Backspace str (if no BS) */ + UP = TC_up; /* Cursor up */ + ospeed = trm_ospeed; /* Put output speed here */ + + + /* Basic data extracted, now mull over it and set the remaining + * ELLE variables + */ +#if FX_SOWIND + if(tgetnum("sg") <= 0) /* If no magic cookie problems */ + { if (TC_so && TC_se) /* And have standout caps, */ + trm_flags |= TF_SO; /* Say has standout cap */ + } +#endif + + if (!(TC_cm && TC_cl)) + t_fatal("lacks cursor addressing or clear screen."); + tvc_pos = tstrlen(TC_cm); /* Find cost of abs move */ + if(BC) /* Find cost of backspace */ + tvc_bs = tstrlen(BC); + + /* Find costs for doing I/D char operations */ + if ((TC_im||TC_ic) && (TC_dm||TC_dc)) + { trm_flags |= TF_IDCHR; + tvc_ci = tstrlen(TC_im)+tstrlen(TC_ei); + tvc_cin = tstrlen(TC_ic)+1+tstrlen(TC_ip); + if(TC_IC) /* If have multi-IC, use it */ + { tvc_ci += tstrlp(TC_IC, 1); + tvc_cin = 1; + } + tvc_cd = tstrlen(TC_dm)+tstrlen(TC_ed); + tvc_cdn = tstrlen(TC_dc); + if(TC_DC) /* If have multi-DC, use it */ + { tvc_cd += tstrlp(TC_DC, 1); + tvc_cdn = 0; + } + } + + /* Find costs for doing I/D line operations */ + if ((TC_ia || TC_al) && (TC_id || TC_dl)) + { trm_flags |= TF_IDLIN; + tvc_li = 0; /* Usual case */ + tvc_lin = tstrlen(TC_al); + if(TC_AL) /* If have multi-IL, use it */ + { tvc_li = tstrlp(TC_AL, 1); + tvc_lin = tstrlp(TC_AL, 2) - tvc_lin; + } + else if(TC_ia) + { tvc_li = tstrlen(TC_im)+tstrlen(TC_ei); + tvc_lin = tstrlen(TC_ia); + } + + tvc_ld = 0; /* Usual case */ + tvc_ldn = tstrlen(TC_dl); + if(TC_DL) /* If have multi-DL, use it */ + { tvc_ld = tstrlp(TC_DL, 1); + tvc_ldn = tstrlp(TC_DL, 2) - tvc_ld; + } + else if(TC_id) + { tvc_ld = tstrlen(TC_dm)+tstrlen(TC_ed); + tvc_ldn = tstrlen(TC_id); + } + } + + if (tc_am) + { scr_wid--; /* For now, avoid invoking wrap. */ +#if 0 + trm_flags |= AUTOWRAP; /* */ +#endif + } + if (TC_ce) trm_flags |= TF_CLEOL; /* Term has CLEOL? */ + if (tc_km) trm_flags |= TF_METAKEY; /* Term has meta key? */ + + return(1); +} + +/* Pair of routines which conspire in order to find # chars actually output + * by a particular termcap string. + */ +static int _tslen; /* Stored count */ +static void _tslinc(ch) { _tslen++; } +static int +tstrlen(str) +char *str; +{ _tslen = 0; + if(str && str[0]) + tputs(str, 1, _tslinc); /* Mult padding by just 1 */ + return(_tslen); +} + +static int +tstrlp(str, par) /* Same but with parameter */ +char *str; +int par; +{ +#if 0 + if(str) + { char *cp = tgoto(str, 0, par); + int i = strlen(cp); + while(--i >= 0) + printf(" %o", *cp++); + printf("\n"); + } +#endif + return !str ? 0 : tstrlen(tgoto(str, 0, par)); +} +#endif /*TX_TERMCAP*/ + +/* Direct-Video terminal output routine + * Currently only COHERENT has this capability. + */ + +#if COHERENT +#include + +struct vidctl { + int v_position; /* Position in video memory */ + int v_count; /* Number of characters to transfer */ + char *v_buffer; /* Character buffer to read/write */ +}; +/* + * Attribute masks for TIOVPUTB - attributes occupy odd addresses + * in video memory. + */ +#define VNORM 0x07 /* Ordinary Video */ +#define VINTE 0x08 /* Intense video */ +#define VBLIN 0x80 /* Blinking video */ +#define VREVE 0x70 /* Reverse video */ +#define VUNDE 0x01 /* Underline video (mono board) */ + +/* T_DIRECT(line, col, string, len) - Do direct-video output of string. + * Puts the string ("len" chars in length) on the screen starting at + * the X,Y character position given by col, line. + * This routine is only called if terminal has the "TF_DIRVID" flag set. + */ +t_direct(lin, col, str, len) +int lin, col; +register char *str; +register int len; +{ register char *cp; + char vbuf[MAXLINE*2]; + struct vidctl v; + + if(len <= 0) return; + tbufls(); /* Ensure normal output is forced out */ + v.v_position = (lin*80 + col)*2; + v.v_count = len*2; + v.v_buffer = cp = vbuf; + do { + *cp++ = *str++; + *cp++ = VNORM; + } while(--len); + ioctl(1, TIOVPUTB, &v); +} +#endif /*COHERENT*/ + +/* + * Terminal Output buffering routines + */ + +static char tbuf[TOBFSIZ]; /* Output buffer */ +static int tbufcnt = 0; /* # chars of room left in buffer */ +static char *tbufp = 0; /* Pointer to deposit in buffer */ + +tput(ch) +int ch; +{ if(--tbufcnt < 0) + tbufls(); + *tbufp++ = ch; +} + +tputz(str) +char *str; +{ register int c; + register char *cp, *tp; + cp = str; + tp = tbufp; + while(c = *cp++) + { if(--tbufcnt < 0) + { tbufp = tp; + tbufls(); + tp = tbufp; + } + *tp++ = c; + } + tbufp = tp; +} + +tputn(str,cnt) +char *str; +int cnt; +{ register int c; + register char *cp, *tp; + cp = str; + tp = tbufp; + if((c = cnt) > 0) + do { + if(--tbufcnt < 0) + { + tbufp = tp; + tbufls(); + tp = tbufp; + } + *tp++ = *cp++; + } while(--c); + tbufp = tp; +} + +tbufls() +{ register int cnt; + + if(tbufp + && (cnt = tbufp - tbuf) > 0) /* # chars written */ + write(1, tbuf, cnt); /* Out they go */ + tbufp = tbuf; + tbufcnt = TOBFSIZ-1; /* Allow for usual expected decrement */ +} + +/* + * Terminal Input buffering routines + */ + +int tibfmsk = -1; /* Mask AND'ed with input chars (external) */ +static char tibuf[TIBFSIZ]; /* TTY input buffer */ +static char *tibfp; /* Pointer to read from buffer */ +static int tibfcnt = 0; /* # chars left to be read from buffer */ + +tgetc() +{ +#if SUN + register int c; + extern int sun_winfd, sun_rdevf; + + if(sun_winfd) + { if(!sun_rdevf) + return(sun_input(1)&tibfmsk); + sun_rdevf = 0; /* Check mouse too, but only once! */ + c = sun_input(0); + if(c != -1) c &= tibfmsk; + return(c); + } +#endif /*SUN*/ + while(--tibfcnt < 0) + tibfcnt = read(0,(tibfp = tibuf),TIBFSIZ); + return((*tibfp++)&tibfmsk); +} + +tinwait() +{ return(tibfcnt > 0 || ts_inp()); +} diff --git a/commands/elle/eevini.c b/commands/elle/eevini.c new file mode 100755 index 000000000..187e626f6 --- /dev/null +++ b/commands/elle/eevini.c @@ -0,0 +1,100 @@ +/* ELLE - Copyright 1982, 1987 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* EEVINI - ELLE initialized variables and array storage. + * Initial values are defined here, but the vars must be + * declared in ELLE.H as well so that references from all modules will + * compile correctly. + * Arrays are also allocated here, so size re-definitions only require + * re-compiling this single small module. + */ + +#define EXT /* Allocate storage for non-initialized vars */ +#include "elle.h" + +#ifndef EVFILMOD +#if V6 +#define EVFILMOD (0600) /* (int) Default file creation mode on V6 */ +#else +#define EVFILMOD (0666) /* (int) Default file creation mode on V7, note */ +#endif /*-V6*/ /* paranoids can use V7 "umask" in shell. */ +#endif +#ifndef EVFNO1 +#define EVFNO1 0 /* (char *) "Old" filename prefix */ +#endif +#ifndef EVFNN1 +#define EVFNN1 0 /* (char *) "New" filename prefix */ +#endif +#ifndef EVFNO2 +#define EVFNO2 "O" /* (char *) "Old" filename postfix */ +#endif +#ifndef EVFNN2 +#define EVFNN2 "N" /* (char *) "New" filename postfix */ +#endif +#ifndef EVFCOL +#define EVFCOL (71) /* (int) Initial value for Fill Column */ +#endif +#ifndef EVCCOL +#define EVCCOL (40) /* (int) Initial value for Comment Column */ +#endif +#ifndef EVNWPCT +#define EVNWPCT 50 /* (int) 50% For random New Window, center cursor. */ +#endif +#ifndef EVMVPCT +#define EVMVPCT 67 /* (int) 67% When move off edge, show 67% new stuff */ +#endif +#ifndef EVMODWSO +#define EVMODWSO 0 /* (bool) Initial mode window standout mode */ +#endif +#ifndef EV2MODEWS +#define EV2MODEWS 1 /* 2-mode-window flag. 0=Never, 1=if SO, 2=always */ +#endif +#ifndef EVMARKSHOW +#define EVMARKSHOW 0 /* (char *) String shown for Set Mark */ +#endif +#ifndef EVHELPFILE /* (char *) Location of ELLE help file. */ +#define EVHELPFILE "/usr/src/elle/help.dat" +#endif + +char *ev_verstr = "ELLE 4.1b"; /* String: Editor name and version # */ +int ev_filmod = EVFILMOD; /* Default file creation mode */ +char *ev_fno1 = EVFNO1; /* "Old" filename prefix */ +char *ev_fnn1 = EVFNN1; /* "New" filename prefix */ +char *ev_fno2 = EVFNO2; /* "Old" filename postfix */ +char *ev_fnn2 = EVFNN2; /* "New" filename postfix */ + +int ev_fcolumn = EVFCOL; /* Initial value for Fill Column */ +#if FX_INDCOMM +int ev_ccolumn = EVCCOL; /* Initial value for Comment Column */ +#endif + +/* New window selection parameters. +** Both are expressed as an integer % of window lines (where the whole +** window is 100), and apply when a new window is selected. +** ev_nwpct - when "New Window" is called, % of lines between top and cursor. +** Also applies when screen has changed randomly. +** ev_mvpct - when cursor moves out of window, this is the % of lines +** between top and cursor (if moved off top) or between bottom and +** cursor (if moved off bottom). +*/ +int ev_nwpct = EVNWPCT; /* New Window cursor loc preference (%) */ +int ev_mvpct = EVMVPCT; /* Moved cursor loc preference (%) */ + +#if FX_SOWIND +int ev_modwso = EVMODWSO; /* Initial mode window standout flag */ +#endif +#if FX_2MODEWINDS +int ev_2modws = EV2MODEWS; /* Initial 2-mode-wind flag */ +#endif +char *ev_markshow = EVMARKSHOW; /* String to display when Set Mark done */ + +char *ev_helpfile = EVHELPFILE; /* Location of ELLE help file */ +char *ev_profile = EVPROFBINFILE; /* Location of ELLE binary user profile */ + /* Note ELLE doesn't use EVPROFTEXTFILE. */ + +/* Array allocations */ + +struct scr_line *scr[MAXHT]; /* Array of screen line structures */ +SBSTR *kill_ring[KILL_LEN]; /* Kill ring table */ diff --git a/commands/elle/elle.h b/commands/elle/elle.h new file mode 100755 index 000000000..b9277dc7c --- /dev/null +++ b/commands/elle/elle.h @@ -0,0 +1,364 @@ +/* ELLE - Copyright 1982, 1984 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* + * ELLE.H Global ELLE definitions + */ + +#ifndef EXT +#define EXT extern /* Default assumes these are referencing decls */ +#endif + +/* Make identifiers unique in 1st 6 chars as per ANSI rule for externals */ +#define tvc_cin tvccin +#define tvc_cdn tvccdn +#define tvc_lin tvclin +#define tvc_ldn tvcldn +#define ev_fno1 evfno1 +#define ev_fno2 evfno2 +#define ev_fnn1 evfnn1 +#define ev_fnn2 evfnn2 + +#define ask_sall asksal /* eebuff.c */ +#define ask_save asksav +#define buf_tmod buftmo +#define buf_tmat buftma +#define e_gobob egobob /* eeedit.c */ +#define e_gobol egobol +#define e_goeob egoeob +#define e_goeol egoeol +#define fill_prefix filpfx /* eefill.c */ +#define fill_plen filpln +#define fill_cur_line filcln +#define kill_ptr kilptr /* eef3.c */ +#define kill_push kilpsh +#define ed_insert edinst /* eefed.c */ +#define ed_insn edinsn +#define ed_deln eddeln +#define ed_delete eddele +#define f_fillreg ffilrg /* eejust.c */ +#define f_fillpara ffilpa + +#include "eesite.h" /* Insert site-dependent flags and parameters */ +#include "sb.h" /* Insert SB package definitions */ +#include "eeprof.h" /* Insert user profile definition. This is a + * separate file so ELLEC can use it too. */ +#include "eefidx.h" /* Insert desired function defs */ + +/* ELLE Compile-time parameter defaults */ + +#ifndef KILL_LEN +#define KILL_LEN 8 /* Size of kill ring */ +#endif +#ifndef MAXHT +#define MAXHT 72 /* Height (# lines) of largest screen we'll suport */ +#endif +#ifndef MAXLINE +#define MAXLINE 132 /* Width (# chars) of largest screen we'll support */ +#endif +#ifndef FNAMELEN +#define FNAMELEN 14 /* Sys-dep: Max size of last filename component */ +#endif /* Check FNAMSIZ if you change this. */ +#ifndef FNAMSIZ +#define FNAMSIZ 100 /* Sys-dep: Max size of complete filename */ +#endif /* This must be at least as large as FNAMELEN! */ +#ifndef ISRCHLIM +#define ISRCHLIM 50 /* Max # of chars to allow I-search on */ +#endif +#ifndef TOBFSIZ +#define TOBFSIZ 80 /* Size of TTY output buffer */ +#endif +#ifndef TIBFSIZ +#define TIBFSIZ 50 /* Size of TTY input buffer */ +#endif +#ifndef ECHOLINES +#define ECHOLINES 1 /* # of lines for echo area (below mode line) */ +#endif +#ifndef MAXARGFILES +#define MAXARGFILES 2 /* # of filename args OK at startup */ +#endif + +/* ELLE initialized variables. + * Initial values are defined in EEVINI.C, but the vars must be + * declared here as well so that references from all modules will + * compile correctly. + */ + +extern char *ev_verstr; /* String: Editor name and version # */ +extern int ev_filmod; /* Default file creation mode */ +extern char *ev_fno1,*ev_fno2; /* Pre, postfix for "old" filenames */ +extern char *ev_fnn1,*ev_fnn2; /* Pre, postfix for "new" filenames */ +extern int ev_fcolumn; /* Fill Column variable */ +#if FX_INDCOMM +extern int ev_ccolumn; /* Comment Column variable */ +#endif +extern int ev_nwpct, ev_mvpct; /* New window selection percentages */ +#if FX_SOWIND +extern int ev_modwso; /* Initial mode window standout flag */ +#endif +#if FX_2MODEWINDS +extern int ev_2modws; /* Initial setting of 2-mode-window flag */ +#endif +extern char *ev_markshow; /* String to show when Set Mark done */ +extern char *ev_helpfile; /* Location of ELLE help file */ +extern char *ev_profile; /* Filename of ELLE binary user profile */ +extern struct profile def_prof; /* ELLE default user profile */ + +/* Global variables */ + +EXT chroff cur_dot; /* Current dot */ +EXT chroff mark_dot; /* Dot for mark */ +EXT int mark_p; /* flag indicating whether mark exists */ +EXT int this_cmd, last_cmd; /* Command type */ +EXT int unrchf; /* Stuffed character back for readcommand */ +EXT int exp; /* Numeric argument for commands */ +EXT int exp_p; /* Flag meaning an arg was given */ +EXT int pgoal; /* Permanent goal column */ +EXT int goal; +EXT char *srch_str; /* Last search string specified (0 = none) */ +EXT int srch_len; /* Length of srch_str string */ +EXT int ask_len; /* Length of last string returned by "ask" */ +EXT char *homedir; /* User's home directory */ +EXT int kill_ptr; /* Index into kill ring */ +extern SBSTR *kill_ring[]; /* Kill ring table (allocated in eevini) */ + +/* Editor Command types */ + +#define KILLCMD 1 /* Kill command, for kill merging */ +#define ARGCMD 2 /* Argument-setter, for main loop */ +#define YANKCMD 3 /* Yank command, for yankpop */ +#define LINECMD 4 /* Next or previous line goal hacking */ +#if IMAGEN +#define INSCMD 5 /* Simple char-insert command, for autowrap */ +#endif /*IMAGEN*/ + +/* Misc char definitions */ +#define CTRL(ch) (037&ch) +#define BELL ('\007') /* Will become \a in ANSI */ +#define BS ('\b') +#define TAB ('\t') +#define LF ('\n') +#define FF ('\f') +#define CR ('\r') +#define ESC ('\033') +#define SP (' ') +#define DEL ('\177') + +#define CB_META (0200) /* Meta bit in command char */ +#define CB_EXT (0400) /* Extend bit in command char */ +#define METIZER ESC +#define EXTIZER CTRL('X') + +/* Terminal parameters - set at runtime startup */ + +EXT char *tv_stype; /* Terminal type string specified by user/system */ +EXT int scr_ht; /* # lines of main screen area */ +EXT int scr_wid; /* # columns of screen */ +EXT int scr_wd0; /* scr_wid - 1 (for 0-origin stuff) */ +EXT int trm_ospeed; /* Output speed index */ +EXT int tvc_pos; /* Cost for absolute move (# of output chars) */ +EXT int tvc_bs; /* Cost for backspace */ +EXT int tvc_ci, tvc_cin; /* Char ins cost per call, cost per column */ +EXT int tvc_cd, tvc_cdn; /* Char del " " " " " " */ +EXT int tvc_li, tvc_lin; /* Line ins cost per call, cost per line */ +EXT int tvc_ld, tvc_ldn; /* Line del " " " " " " */ + +EXT int trm_flags; /* Terminal capabilities - bit flags */ + /* Maybe change to word vars someday (faster) */ +#define TF_IDLIN 01 /* Has I/D line */ +#define TF_IDCHR 02 /* Has I/D char */ +#define TF_SO 04 /* Has usable standout mode */ +#define TF_CLEOL 010 /* Has clear-to-eol */ +#define TF_METAKEY 020 /* Has meta key */ +#define TF_DIRVID 040 /* Has direct-video type interface */ + + +/* Redisplay definitions */ + +EXT int curs_lin; /* Line # of current cursor (0 origin) */ +EXT int curs_col; /* Column # of current cursor (0 origin) */ + +EXT int rd_type; /* Global var: holds redisplay "hints" */ +#define redp(n) rd_type |= (n) + +#define RD_SCREEN 01 /* Clear everything and redisplay */ +#define RD_WINDS 02 /* Check all windows for changes (b/emod) */ +#define RD_MODE 04 /* Mode line has changed, update it. */ +#define RD_WINRES 0400 /* Assume all of window was changed (clear b/emod) */ +#define RD_MOVE 010 /* Cursor has moved */ +#define RD_UPDWIN 020 /* Window fixed, must update modified screen lines */ +/*#define RD_ICHR 0 *//* Hint: Char insert done */ +/*#define RD_DCHR 0 *//* Hint: Char del done */ +#define RD_ILIN 0100 /* Hint: Line insert done */ +#define RD_DLIN 0200 /* Hint: Line del done */ + +/* #define RD_MOVWIN 02000 *//* Window should be re-positioned */ +#define RD_FIXWIN 02000 /* Window needs fixing (call fix_wind) */ +#define RD_TMOD 04000 /* Text changed in this window, check it. */ +#define RD_WINCLR 010000 /* Clear window with CLEOLs (not yet) */ +#define RD_CHKALL 020000 /* Check all windows for redisplay flags */ +#if IMAGEN +#define RD_REDO 040000 /* Just re-do the entire window, don't think */ +#endif /*IMAGEN*/ + + /* Flags with global effects, only seen in rd_type */ +#define RDS_GLOBALS (RD_SCREEN|RD_MODE|RD_WINDS|RD_CHKALL) + /* Flags which are allowed per-window (in w_redp) */ +#define RDS_WINFLGS (~RDS_GLOBALS) + /* Flags which force FIX_WIND() to do something */ +#define RDS_DOFIX (RD_WINRES|RD_TMOD|RD_FIXWIN|RD_MOVE) + +#define CI_CLINE '!' /* Char indicator for continued line */ +#define CI_CNTRL '^' /* Char indicator for control chars */ +#define CI_META '~' /* Char indicator for meta-bit (8th) set */ +#define CI_TOP '|' /* Char indicator for top-bit (9th) set */ +#define MAXCHAR (8+3) /* Longest char representation (TAB) + slop */ + +/* Definitions for screen structures */ + +struct scr_line { + chroff sl_boff; /* Ptr to start of line's text in buffer */ + int sl_len; /* # buffer chars in line (incl NL) */ + char *sl_line; /* Ptr to screen image of line */ + int sl_col; /* # chars in image == # columns used */ + char sl_flg; /* Flags - set if this line modified */ + char sl_cont; /* If line being continued on next, this */ + /* contains 1 plus # extra chars (if any) */ + /* stored at end of this line which shd be */ + /* put at beg of next line. */ + char *sl_nlin; /* New screen image line if modified flag set */ + int sl_ncol; +}; + /* sl_flg definitions */ +#define SL_MOD 01 /* New line exists, must update to it */ +#define SL_EOL 02 /* Buffer line ends with EOL */ +#define SL_CSO 04 /* Current screen line is in standout mode */ +#define SL_NSO 010 /* New screen line is in standout mode */ +#if IMAGEN +#define SL_REDO 0100 /* Line should be redone completely */ +#endif /*IMAGEN*/ + +extern struct scr_line *scr[]; /* Screen line ptrs (allocated in e_vinit) */ + + +/* Buffer stuff */ + +struct buffer +{ SBBUF b_sb; /* MUST be 1st thing! */ + struct buffer *b_next; /* ptr to next in chain */ + char *b_name; /* text name */ + char *b_fn; /* filename */ + chroff b_dot; /* point (dot) */ + int b_flags; /* misc. bits */ + struct majmode *b_mode; /* Mode of buffer */ +#if IMAGEN + long b_mtime; /* Last file modification time */ +#endif /*IMAGEN*/ +}; + /* b_flags definitions */ +#define B_MODIFIED 01 /* Buffer is modified */ +#define B_EOLCRLF 0200 /* On = CRLF mode, off = LF mode */ +#if IMAGEN +#define B_PERMANENT 002 /* buffer cannot be killed */ +#define B_CMODE 004 /* "C" mode (HACK HACK) */ +#define B_BACKEDUP 010 /* Buffer has been backed up once */ +#define B_TEXTMODE 020 /* Text mode (auto-wrap, basically) */ +#define B_QUERYREP 040 /* Query-replace mode (qualifier) */ +#endif /*IMAGEN*/ + +/* Handy macro to check EOL mode */ +#define eolcrlf(buf) (((struct buffer *)buf)->b_flags&B_EOLCRLF) + +/* Buffer pointers */ + +EXT struct buffer + *buf_head, /* head of list of all buffers */ + *cur_buf, /* buffer we are editing now */ + *last_buf, /* buffer we were editing before */ + *lines_buf; /* buffer for sep_win */ + +/* Window stuff */ + +struct window +{ struct window *w_next; /* ptr to next in chain */ + int w_flags; /* Window flags */ + int w_pos; /* index of top line */ + int w_ht; /* number of lines */ + struct buffer *w_buf; /* buffer in this window */ + int w_pct; /* % of buffer window is at */ + int w_redp; /* Redisplay hints */ + chroff w_topldot; /* line currently at top of window */ + chroff w_dot; /* Saved dot while not cur_win */ + chroff w_bmod; /* Lower bound of modified text */ + chroff w_emod; /* Upper bound of modified text */ + /* (offset from end of buffer!) */ + chroff w_oldz; /* Buffer len as of last update */ +}; + +/* Window flags */ +#define W_STANDOUT 01 /* Use terminal's standout mode for window */ +#define W_MODE 02 /* This is a mode window */ + +/* Window pointers */ + +EXT struct window + *win_head, /* head of list of all windows */ + *cur_win, /* window we are now in */ + *user_win, /* current user window */ + *oth_win, /* "other" user window */ + *mode_win, /* window for mode line */ + *ask_win, /* window for ask (echo) area */ + *sep_win; /* window for separation dashes */ + +/* Major Mode stuff. Each buffer has its own major mode. + * Only one major mode may be in effect at any time. + */ +struct majmode { + char *mjm_name; /* Simple for now */ +}; +EXT struct majmode *fun_mode; /* Fundamental mode - the default */ +EXT struct majmode *cur_mode; /* Current major mode */ + +/* Minor modes are currently implemented by means of flag variables + * which have global effects (regardless of buffer or major mode). + * Each variable has the name "x_mode" where x is the name of the minor + * mode. These are declared in the modules containing their support code. + * In the future this may be generalized along the lines of major modes. + */ + + +/* Miscellaneous debug stuff */ + +EXT int dbgval; /* Set nonzero to do verify stuff */ +EXT int dbg_isw; /* Set to enable interrupts if possible */ +#if IMAGEN +EXT int dbg_redp; /* Set to debug redisplay algorithms */ +#endif /*IMAGEN*/ +extern int errno; + +/* V7 routines for setexit/reset emulation */ + +#if !(V6) +#include +EXT jmp_buf env_main; +#define setexit(a) setjmp(env_main) +#define reset(a) longjmp(env_main,a) +#endif /*-V6*/ + +/* Declare functions returning CHROFF values (offsets into a buffer) */ + +extern chroff e_dot(),e_nldot(),e_pldot(),e_boldot(),e_eoldot(), + e_alldot(),ex_boldot(),ex_alldot(), + ex_blen(),e_blen(),ex_dot(),e_wdot(); + +extern SBSTR *e_copyn(); + +/* Some other commonly needed declarations */ + +extern char *memalloc(), *ask(), *dottoa(), *strdup(); +#if !(V6) +extern char *getenv(); +#endif /*-V6*/ +#include "eeproto.h" /* function prototypes */ diff --git a/commands/elle/ellec.c b/commands/elle/ellec.c new file mode 100755 index 000000000..195f2a068 --- /dev/null +++ b/commands/elle/ellec.c @@ -0,0 +1,1404 @@ +/* ELLEC - Copyright 1983 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. + */ +/* ELLEC - ELLE Compiler + * Invoked with no arguments, acts as general ELLE compiler filter; + * reads ASCII profile input, and outputs a binary profile. + * Otherwise: + * -Profile Compiles user's profile. + * HOME/.ellepro.e -> HOME/.ellepro.bN (N = fmt version #) + * NOTE: on V6, "HOME/" is left out. + * -Pconf Outputs defprf.c for ELLE (accepts std in) (old -E) + * -Fconf Outputs eefdef.h for ELLE (accepts std in) + * -FXconf Outputs eefidx.h for ELLE (accepts std in) + * -CMconf Outputs config makefile for ELLE ( " ) + * -CSconf arg Outputs config file using "arg" - for V6 config.v6 file. + * -Pdump Outputs defprf.e user profile (old -P) + * -Fdump Outputs deffun.e + */ + +#if 0 +The ASCII profile file associates command chars with functions. +It is simply a series of lisp-like expressions of the form + (keybind ) + + e.g. (keybind "C-Y" "Yank Pop") + +Command char specification: + Allowed prefixes: + The char itself + C- Controlify (zap bits 140) + ^ Ditto + M- Meta (add bit 200) - case ignored + X- Extended (add bit) - case ignored + To quote a char or char spec, use quoted-string syntax. + +Function name specification: + Function names are specified by quoted strings containing the entire + long-form ASCII function name. Matching is done case-independently. + +#endif /*COMMENT*/ + + +#include "eesite.h" /* Site specific stuff if any */ +#include +#include +#include +#include "eeprof.h" /* Profile structure definition */ + + +#define EFUNMAX 400 /* Maximum # (+1) of functions that can be defined */ +#define KBTSIZ (128*2) /* Initial size of key binding tables */ + + +/* EFUN Function definition table. +** Functions that were copied from the pre-defined table will +** have a value of NULL in ef_mod. +*/ +struct fun { + int ef_idx; /* Function index (same as index of entry) */ + char *ef_name; /* Function name */ + char *ef_adr; /* C routine name in ELLE */ + char *ef_mod; /* Source module that C routine lives in */ +}; +struct fun efuntab[EFUNMAX]; +int efxmax = 0; /* Largest function idx used */ + +int format_ver = PROF_VER; /* Current version # of binary profile fmt */ + +/* Keybind mapping tables. There are four separate tables: +** Simple character. Always 128 single-byte items, indexed by the simple +** command char. Each item is the corresponding function index. +** Meta char. Variable number of 2-byte items; first is a command char +** and second is its function index. +** Extended char. Same format as Meta char. +** Menu item (SUN only). Variable number of single-byte items, each +** a function index. +** +*/ +char *chrptr; /* Pointer to simple-char table */ +int chrsiz = 128; /* Current size of table */ +int chrcnt = 0; /* # bytes actually used */ + +char *mtaptr; /* Pointer to meta-char table */ +int mtasiz = KBTSIZ; /* Current size (twice the # of items) */ +int mtacnt = 0; /* # bytes actually used */ + +char *extptr; /* Pointer to ext-char table */ +int extsiz = KBTSIZ; /* Current size (twice the # of items) */ +int extcnt = 0; /* # bytes actually used */ + +char *mnuptr; /* Pointer to menu-item table (SUN only) */ +int mnusiz = KBTSIZ; /* Current size */ +int mnucnt = 0; /* # bytes actually used */ + + +#define CB_EXT 0400 /* "X-" prefix on command char */ +#define CB_META 0200 /* "M-" prefix on command char */ + + +/* Set up the pre-defined data areas. This includes the +** predefined function table plus the default profile (keyboard mappings). +** Note this only contains entries for ef_name and ef_adr. +*/ + +struct fun pdfuntab[] = { /* Pre-Defined Function table */ +#define EFUN(rtn,rtnstr,name) 0, name, rtnstr, 0, +#define EFUNHOLE 0, 0, 0, 0, +#include "eefdef.h" +}; +int npdfuns = sizeof(pdfuntab)/sizeof(struct fun); /* # of entries */ + +#include "defprf.c" /* Insert default profile mapping */ + /* This defines charmap, metamap, and extmap. */ + +/* Stuff for feeble-minded list processor */ +#define NIL ((struct lnode *)0) +#define LTRUE (<ruenode) + +#define LT_VAL 0 +#define LT_STR 1 +#define LT_LIST 2 +#define LT_ATOM 3 /* Should use this later instead of LT_STR */ + +struct lnode { + struct lnode *lnxt; + int ltyp; + union { + int lvi; + char *lvs; + struct lnode *lvl; + } lval; +}; + +struct lnode ltruenode; /* Constant TRUE */ + +_PROTOTYPE(int main , (int argc , char **argv )); +_PROTOTYPE(int doargs , (int argc , char **argv )); +_PROTOTYPE(char **findkey , (char *cp , char ***aretp , char **tabp , int tabsiz , int elsize )); +_PROTOTYPE(int nstrcmp , (char *s1 , char *s2 )); +_PROTOTYPE(int ustrcmp , (char *s1 , char *s2 )); +_PROTOTYPE(int strueq , (char *s1 , char *s2 )); +_PROTOTYPE(int do_opcod , (void)); +_PROTOTYPE(int do_opasc , (void)); +_PROTOTYPE(int outkbind , (int c , int fx )); +_PROTOTYPE(int do_obprof , (void)); +_PROTOTYPE(int mupcase , (int ch )); +_PROTOTYPE(int upcase , (int ch )); +_PROTOTYPE(char *qstr , (char *str )); +_PROTOTYPE(char *charep , (int c )); +_PROTOTYPE(int do_ocnf , (char *str )); +_PROTOTYPE(int do_ofcod , (void)); +_PROTOTYPE(int do_ofasc , (void)); +_PROTOTYPE(int do_ofxcod , (void)); +_PROTOTYPE(int compile_stdin , (void)); +_PROTOTYPE(int lrch , (void)); +_PROTOTYPE(struct lnode *lread , (void)); +_PROTOTYPE(int wspfls , (void)); +_PROTOTYPE(struct lnode *lrstr , (int flg )); +_PROTOTYPE(int islword , (int c )); +_PROTOTYPE(struct lnode *eval , (struct lnode *lp )); +_PROTOTYPE(struct lnode *undefall , (struct lnode *lp )); +_PROTOTYPE(struct lnode *efun , (struct lnode *lp )); +_PROTOTYPE(struct lnode *keybind , (struct lnode *lp )); +_PROTOTYPE(struct lnode *keyallun , (void)); +_PROTOTYPE(struct lnode *menuitem , (struct lnode *lp )); +_PROTOTYPE(int repchar , (char *str )); +_PROTOTYPE(struct lnode *getln , (void)); +_PROTOTYPE(int numcvt , (char *str , int *anum )); +_PROTOTYPE(int listcnt , (struct lnode *lp )); +_PROTOTYPE(char *funname , (int i )); +_PROTOTYPE(int findfun , (char *name )); +_PROTOTYPE(int funcnt , (int *arr )); +_PROTOTYPE(int scpy , (char *from , char *to , int cnt )); +_PROTOTYPE(char *stripsp , (char *cp )); + +int warn(); +int lerr(); +int fatal(); + + +/* ELLEC argument stuff */ +char *argfile; +char *outfile; +int swfilter; /* If no args */ +int swprof; /* -P */ +int swelle; /* -E */ + +int uproflg; /* Do compilation of user's profile */ +int swpcnf; /* Output defprf.c (C initialization of profile) */ +int swfcnf; /* Output eefdef.h */ +int swfxcnf; /* Output eefidx.h */ +int swcmcnf; /* Output config makefile (makecf.fun) */ +char *swcscnf; /* Output config specially (for V6) */ +int swallc; /* Do all of config stuff */ +int swfdmp; /* Output deffun.e */ +int nfiles; /* # file specs seen */ + +main(argc,argv) +int argc; +char **argv; +{ register int i; + register char *cp; + char temp[300]; + + /* Initialize LTRUE + ** (cannot portably initialize a union at compile time) + */ + ltruenode.ltyp = LT_VAL; /* Set type (other fields zero) */ + + /* Process switches */ + if(argc <= 1) swfilter++; /* If no args, assume filter */ + else doargs(argc,argv); + + /* Initialize function definitions and key bindings from predefs */ + chrptr = malloc(chrsiz); + mtaptr = malloc(mtasiz); + extptr = malloc(extsiz); + mnuptr = malloc(mnusiz); + if (!chrptr || !mtaptr || !extptr || !mnuptr) + fatal("cannot init, no memory"); + + scpy(charmap, chrptr, (chrcnt = sizeof(charmap))); + scpy(metamap, mtaptr, (mtacnt = sizeof(metamap))); + scpy( extmap, extptr, (extcnt = sizeof(extmap))); + if(def_prof.menuvec) + scpy(def_prof.menuvec, mnuptr, (mnucnt = def_prof.menuvcnt)); + + for(i = 0; i < npdfuns; ++i) /* Initialize function defs */ + if(pdfuntab[i].ef_name) + { efuntab[i].ef_idx = i; + efuntab[i].ef_name = pdfuntab[i].ef_name; + efuntab[i].ef_adr = stripsp(pdfuntab[i].ef_adr); + if(efxmax < i) efxmax = i; + } + + + /* Routines expect input from stdin and output their results + * to stdout. + */ + if(argfile) + if(freopen(argfile,"r",stdin) == NULL) + fatal("cannot open input file \"%s\"",argfile); + if(outfile) + if(freopen(outfile,"w",stdout) == NULL) + fatal("cannot open output file \"%s\"",outfile); + + + /* Check for general compilation */ + if(swfilter) + { /* Not really implemented yet */ + fatal("bad usage, see doc"); + } + + /* Do profile hacking of some kind */ + if(swprof || swelle) + { if (compile_stdin()) /* Compile input profile */ + { if(swprof) + do_opasc(); /* Output ASCII profile (.e) */ + else if(swelle) + do_opcod(); /* Output bin profile (.b1) */ + } + exit(0); + } + + /* Check for variousness */ + if(swpcnf) + { if(compile_stdin()) /* Compile input */ + do_opcod(); /* Output the C initialization code */ + exit(0); + } + if(swfxcnf) + { if(compile_stdin()) /* Compile input */ + do_ofxcod(); /* Output the eefidx.h code */ + exit(0); + } + if(swfcnf) + { if(compile_stdin()) /* Compile input */ + do_ofcod(); /* Output the eefdef.h code */ + exit(0); + } + if(swcmcnf || swcscnf) + { if(compile_stdin()) /* Compile input */ + do_ocnf(swcscnf); /* Output the makecf.fun code */ + exit(0); + } + if(swfdmp) + { if(compile_stdin()) /* Compile input */ + do_ofasc(); /* Output the deffun.e code */ + exit(0); + } + + + /* Hack user's profile */ + if(!uproflg) exit(0); + if(!argfile) + { + temp[0] = 0; +#if !V6 + if (cp = getenv("HOME")) + strcat(temp, cp); +#if !TOPS20 + strcat(temp,"/"); +#endif /*-TOPS20*/ +#endif /*-V6*/ + strcat(temp, EVPROFTEXTFILE); + if(freopen(temp,"r",stdin) == NULL) + fatal("cannot open profile \"%s\"",temp); + } + if(!outfile) + { + temp[0] = 0; +#if !V6 + if (cp = getenv("HOME")) + strcat(temp, cp); +#if !TOPS20 + strcat(temp,"/"); +#endif /*-TOPS20*/ +#endif /*-V6*/ + strcat(temp, EVPROFBINFILE); + if(freopen(temp,"wb",stdout) == NULL /* Try binary 1st */ + && freopen(temp,"w",stdout) == NULL) + fatal("cannot open output profile \"%s\"",temp); + + } + /* Hack user's profile */ + if(compile_stdin()) /* Compile input profile */ + do_obprof(); /* Output the binary */ + +} + +#define SW_FLG 0 +#define SW_VAR 1 +#define SW_STR 2 +struct swarg { + char *sw_name; + long sw_type; + int *sw_avar; + char **sw_astr; +} swtab[] = { + "P", SW_FLG, &swprof, 0, /* Old stuff */ + "E", SW_FLG, &swelle, 0, + "Profile", SW_FLG, &uproflg, 0, + "Pconf", SW_FLG, &swpcnf, 0, + "Fconf", SW_FLG, &swfcnf, 0, + "FXconf", SW_FLG, &swfxcnf, 0, + "CMconf", SW_FLG, &swcmcnf, 0, + "CSconf", SW_STR, 0, &swcscnf, + "Allconf", SW_FLG, &swallc, 0, + "Pdump", SW_FLG, &swprof, 0, + "Fdump", SW_FLG, &swfdmp, 0 +}; + +doargs(argc,argv) +int argc; +char **argv; +{ register int cnt, c; + register char **av; + register int i; + register struct swarg *swp; + struct swarg *swp2; + int swerrs = 0; + + av = argv; + cnt = argc; + nfiles = 0; + + while(--cnt > 0) + { ++av; + if(*av[0] != '-') /* If not switch, */ + { /* assume it's an input filename */ + nfiles++; + continue; + } + av[0]++; + + /* Try to look up switch in table */ + swp = (struct swarg *)findkey(av[0], &swp2, swtab, + (sizeof(swtab))/(sizeof(struct swarg)), + (sizeof(struct swarg))/(sizeof(char *))); + if(swp2) + { fprintf(stderr,"ellec: ambiguous switch: -%s = %s or %s\n", + av[0], swp->sw_name, swp2->sw_name); + goto swdone; + } + if(swp) switch(swp->sw_type) + { case SW_FLG: + *(swp->sw_avar) = 1; + goto swdone; + case SW_VAR: + *(swp->sw_avar) = 1; + if(cnt <= 1) goto swdone; + if(isdigit(*av[1])) + { *(swp->sw_avar) = atoi(av[1]); + --cnt; + goto swargdone; + } + goto swdone; + + case SW_STR: + if(cnt <= 1) goto swdone; + *(swp->sw_astr) = av[1]; + goto swargdone; + + default: + fprintf(stderr,"ellec: bad switch type: %s\n", + av[0]); + swerrs++; + } + + stop: fprintf(stderr,"ellec: bad switch: %s\n",*av); + swerrs++; + goto swdone; + swargdone: + av[0] = 0; + av++; + swdone: av[0] = 0; + } + if(swerrs) exit(1); /* Stop if any problems */ +} + +char ** +findkey(cp, aretp, tabp, tabsiz, elsize) +register char *cp; +char ***aretp; +register char **tabp; +int tabsiz, elsize; +{ register char **mp1, **mp2; + register int i, res; + + *aretp = mp1 = mp2 = 0; + for(i = 0; i < tabsiz; ++i, tabp += elsize) + { if(res = ustrcmp(cp,*tabp)) + { if(res > 0) return(tabp); + if(!mp1) mp1 = tabp; + else mp2 = tabp; + } + } + if(mp2) + *aretp = mp2; /* Ambiguous */ + return(mp1); +} + +/* NSTRCMP - New string compare. + * Returns: + * 2 if str2 > str1 (mismatch) + * 1 if str2 counted out (str1 > str2) + * 0 if full match + * -1 if str1 counted out (str1 < str2) + * -2 if str1 < str2 (mismatch) + */ + +nstrcmp(s1,s2) +register char *s1, *s2; +{ register int c, d; + + while(c = *s1++) + { if(c != *s2) + { if((d = upcase(c) - upcase(*s2)) != 0) + return(*s2==0 ? 1 : (d > 0 ? 2 : -2)); + } + ++s2; + } + return(*s2 ? -1 : 0); +} + +/* USTRCMP - uppercase string compare. + * Returns 0 if mismatch, + * 1 if full match, + * -1 if str1 runs out first (partial match) + */ +ustrcmp(s1,s2) +register char *s1, *s2; +{ register int c; + + if ( ! s1 || ! s2 ) return ( 0 ); /* Check for null ptr */ + while(c = *s1++) + { if(c != *s2) + { if(((c ^ *s2) != 040) + || (upcase(c) != upcase(*s2))) + return(0); + } + s2++; + } + return(c == *s2 ? 1 : -1); +} + +strueq(s1,s2) +char *s1; +char *s2; +{ return (ustrcmp(s1, s2) > 0 ? 1 : 0); +} + +/* Output C initialization code for default profile (defprf.c) */ + +do_opcod() +{ register int i, c, f; + + printf("\ +/* This file defines the initial data for ELLE's default user profile.\n\ +** It is automatically generated by ELLEC, and should not be edited.\n\ +*/\n\ +char charmap[] = {\n"); + for(i=0; i < chrcnt; i++) + { printf("\t%2d,",(f = chrptr[i]&0377)); + printf("\t/* (%3o) %3s",i,charep(i)); + printf(" %s",funname(f)); + printf(" */\n"); + } + + printf("};\n char metamap[] = {\n"); + for(i = 0; i < mtacnt; i += 2) + { printf("\t0%-3o,%3d,",(c = mtaptr[i]&0377),(f = mtaptr[i+1]&0377)); + printf("\t/* %4s",charep(c|CB_META)); + printf(" %s",funname(f)); + printf(" */\n"); + } + + printf("};\n char extmap[] = {\n"); + for(i = 0; i < extcnt; i += 2) + { printf("\t0%-3o,%3d,",(c = extptr[i]&0377),(f = extptr[i+1]&0377)); + printf("\t/* %4s",charep(c|CB_EXT)); + printf(" %s",funname(f)); + printf(" */\n"); + } + printf("};\n"); + printf("struct profile def_prof = {\n"); + printf(" %d, /* Ver */\n", format_ver); + printf(" sizeof(charmap), charmap,\n"); + printf(" sizeof(metamap)/2, metamap,\n"); + printf(" sizeof(extmap)/2, extmap, \n"); + printf(" 0, 0\n"); + printf("};\n"); + +} + +/* Output ASCII version of default profile */ + +int oerrs; + +do_opasc() +{ register int i, c, f; + + oerrs = 0; + printf("; ELLE default ASCII profile\n\n"); + printf("(keyallunbind) ; To flush all existing bindings\n\n"); + for(i=0; i < chrcnt; i++) + outkbind(i, chrptr[i]&0377); + + printf("\n; Meta chars\n\n"); + for(i = 0; i < mtacnt; i += 2) + outkbind(CB_META | (mtaptr[i]&0377), mtaptr[i+1]&0377); + + printf("\n ; Extended commands\n\n"); + for(i = 0; i < extcnt; i += 2) + outkbind(CB_EXT | (extptr[i]&0377), extptr[i+1]&0377); + + printf("\n"); + if(oerrs) + fatal("%d errors encountered, check output file.", oerrs); +} + +outkbind(c, fx) +{ + if(fx == 0) /* Allow key to be mapped to nothing. */ + return; + if(fx <= 0 || fx > efxmax) + printf(";INTERNAL ERROR: Bad function index %d for key %s\n", + fx, charep(c)); + else if(efuntab[fx].ef_name == NULL) + printf(";INTERNAL ERROR: No name for function %d while mapping key %s\n", + fx, charep(c)); + else { + printf("(keybind %s \"%s\")\n", + qstr(charep(c)),efuntab[fx].ef_name); + return; + } + oerrs++; +} + +/* Output binary user profile */ + +do_obprof() +{ register unsigned int rp; /* Relative "pointer" */ + struct stored_profile st_prof; + + rp = sizeof(st_prof); /* Initialize */ + + /* Fixed by Kochin Chang, July 1995 */ + /* format version should be the first field in compiled profile */ + prof_pack(st_prof.version, format_ver); + + prof_pack(st_prof.chrvec, rp); + prof_pack(st_prof.chrvcnt, chrcnt); + rp += chrcnt; + + prof_pack(st_prof.metavec, rp); + prof_pack(st_prof.metavcnt, mtacnt/2); + rp += mtacnt; + + prof_pack(st_prof.extvec, rp); + prof_pack(st_prof.extvcnt, extcnt/2); + rp += extcnt; + + prof_pack(st_prof.menuvec, rp); + prof_pack(st_prof.menuvcnt, mnucnt); + rp += mnucnt; + + fwrite((char *)&st_prof,sizeof(st_prof),1,stdout); + fwrite(chrptr,sizeof(char),chrcnt,stdout); + if(mtacnt) fwrite(mtaptr, sizeof(*mtaptr), mtacnt, stdout); + if(extcnt) fwrite(extptr, sizeof(*extptr), extcnt, stdout); + if(mnucnt) fwrite(mnuptr,sizeof(*mnuptr),mnucnt,stdout); +} + +/* Return upper-case version of character */ +mupcase(ch) +register int ch; +{ return((ch&(~0177)) | upcase(ch)); +} +upcase (ch) +{ register int c; + c = ch&0177; + return((('a' <= c) && (c <= 'z')) ? (c - ('a'-'A')) : c); +} + +char * +qstr(str) +register char *str; +{ register int c; + static char qstrbuf[100]; + register char *cp; + cp = str; + while((c = *cp++) && islword(c)); + if(c == 0) return(str); /* No quoting needed */ + + cp = qstrbuf; + *cp++ = '"'; + while(*cp++ = c = *str++) + if(c == '"') *cp++ = c; /* Double the quotes */ + cp[-1] = '"'; + *cp = 0; + return(qstrbuf); +} + +char * +charep(c) +register int c; +{ static char chrbuf[10]; + register char *cp; + cp = chrbuf; + if(c&CB_EXT) + { *cp++ = 'X'; + *cp++ = '-'; + c &= ~CB_EXT; + } + if(c&CB_META) + { *cp++ = 'M'; + *cp++ = '-'; + c &= ~CB_META; + } + if(c <= 037) + { *cp++ = '^'; + c += 0100; + } + if(c == 0177) + { *cp++ = 'D'; + *cp++ = 'E'; + *cp++ = 'L'; + } + else *cp++ = c; + *cp = 0; + return(chrbuf); +} + +/* Output config Makefile (makecf.fun) + * If argument is 0 (NULL), does Makefile type output. + * Otherwise uses string for special-purpose output. + */ +do_ocnf(str) +char *str; +{ register struct fun *fnp; + register int i, mi; + register char *cp; + int fmtab[EFUNMAX]; + int nfmods; + char *modtab[EFUNMAX]; + char *unknown = "unknown-module"; + + if(str == NULL) /* If not V6 version */ + { printf("# Function module definition file, generated by ELLEC\n"); + printf("FUN_OFILES = "); + } + + nfmods = 0; + + funcnt(fmtab); /* Count function occs */ + + for(i = 1; i <= efxmax; ++i) + { if(fmtab[i] == 0) continue; + fnp = &efuntab[i]; + + if(fnp->ef_name == 0) + fatal("internal error - no name for function %d", i); + + /* Got a function, store its module name if not a dup */ + if ((cp = fnp->ef_mod) == NULL) /* Substitute if undef */ + cp = unknown; + for(mi=0; mi < nfmods; ++mi) + if(ustrcmp(cp, modtab[mi]) > 0) + break; + if(mi < nfmods) continue; + modtab[nfmods++] = cp; + } + + /* Now have table of all modules used. Crunch them into output. */ + for(mi=0; mi < nfmods; ++mi) + if (modtab[mi] != unknown) + { if(str != NULL) /* V6 version? */ + printf("%s %s\n", str, modtab[mi]); + else printf("\\\n\t%s.o", modtab[mi]); + } + printf("\n"); +} + +/* Output eefdef.h */ + +do_ofcod() +{ register struct fun *fnp; + register int i; + char temp[40]; + int fmtab[EFUNMAX]; + + printf("/* .H Function Definition file, generated by ELLEC */\n"); + printf("/* 0 */ EFUNHOLE /* Always undefined */\n"); + + funcnt(fmtab); /* Count function occs */ + + for(i = 1; i <= efxmax ; ++i) + { + fnp = &efuntab[i]; + printf("/* %3d */ ", i); + if(fmtab[i] == 0 || fnp->ef_name == 0) + printf("EFUNHOLE\n"); + else + { sprintf(temp, "\"%s\"", fnp->ef_adr); + printf("EFUN( %-12s, %-14s, \"%s\")\n", fnp->ef_adr, + temp, fnp->ef_name); + } + } + +} + +/* Output ascii version of function def file +*/ +do_ofasc() +{ register struct fun *fnp; + register int i; + register char *fa, *fm; + + printf("; Master Function Definition file\n"); + + for(i = 1; i <= efxmax ; ++i) + { + fnp = &efuntab[i]; + if(fnp->ef_idx == 0) /* No definition for this index? */ + continue; + if(fnp->ef_name == 0) + { warn("internal error - no name for function %d", i); + continue; + } + + if ((fa = fnp->ef_adr) == NULL) + fa = "unknown-addr"; + if ((fm = fnp->ef_mod) == NULL) + fm = "unknown-module"; + printf("(efun %d \"%s\" %s %s)\n", + fnp->ef_idx, fnp->ef_name, fa, fm); + } +} + +/* Output eefidx.h */ + +do_ofxcod() +{ register struct fun *fnp; + register int i; + register char *cp, *cp2; + int fmtab[EFUNMAX]; + char tmpstr[100]; + + printf("\ +/* .H Function Index Definition file, generated by ELLEC */\n"); + printf("\ +/* FN_ defines Function Numbers (indices) for all known functions */\n"); + printf("\ +/* FX_ defines Function eXistence in this ELLE configuration */\n"); + + funcnt(fmtab); /* Count function occs */ + + for(i = 1; i <= efxmax ; ++i) + { + fnp = &efuntab[i]; + if(fnp->ef_idx == 0) /* No definition for this index? */ + continue; + if(fnp->ef_adr == 0 || fnp->ef_name == 0) + { warn("internal error - no addr/name for function %d", i); + continue; + } + + cp2 = fnp->ef_adr; + cp = tmpstr; + while(*cp++ = upcase(*cp2++)); + cp = tmpstr; + if((*cp++ != 'F') || (*cp++ != '_')) + cp = tmpstr; + + /* Always define FN_ as index */ + printf("#define FN_%-14s %3d /* %s */\n", + cp, i, fnp->ef_name); + /* Define FX_ as 0 if unused, else same as FN_ */ + printf("#define FX_%-14s %3d\n", cp, + (fmtab[i] == 0) ? 0 : i); /* 0 if unused */ + } + +} + +/* Compile input! */ + +compile_stdin() +{ register struct lnode *lp; + + while((lp = lread()) != NIL) + eval(lp); + + return(1); +} + +#define MAXLINE 300 +int llstch = -1; +int leofflg; +#define unlrch(c) llstch = c + +int lineno = 0; +char linebuf[MAXLINE]; +char *linecp = linebuf; + +lrch() +{ register int c; + if((c = llstch) >= 0) + { if(c == 0 && leofflg) + return(EOF); + llstch = -1; + return(c); + } + if((c = getc(stdin)) == EOF) + { leofflg = 1; + llstch = 0; + *linecp = 0; + linecp = linebuf; + return(c); + } + if(c == '\n') + { lineno++; + linecp = linebuf; + } + else *linecp++ = c; + return(c); +} + +struct lnode * +lread() +{ register int c; + register struct lnode *lp, *lp2; + struct lnode *head; + + wspfls(); + if((c = lrch())== EOF) + return(NIL); + if(c == ')') /* End of a list? */ + return(NIL); + if(c == '(') /* Start of a list? */ + { head = lp = getln(); + lp->ltyp = LT_LIST; + if((head->lval.lvl = lp = lread()) == NIL) + return(head); /* Return empty list */ + while(lp2 = lread()) + { lp->lnxt = lp2; + lp = lp2; + } + return(head); + } + + /* Atom of some kind */ + if(c=='"') + { return(lrstr(1)); + } + unlrch(c); + return(lrstr(0)); +} + +wspfls() +{ register int c; + for(;;) + { c = lrch(); + if(isspace(c)) continue; + if(c == ';') + while((c = lrch()) != '\n') + if(c == EOF) return; + break; + } + if(c != EOF) unlrch(c); +} + +#define LSMAX 300 /* Max # chars in atom string */ +struct lnode * +lrstr(flg) +{ char cbuf[LSMAX]; + register char *cp; + register int c, i; + struct lnode *lp; + + cp = cbuf; + i = 0; + + while((c = lrch()) != EOF) + if (flg) switch(c) + { case '"': + if((c = lrch()) == EOF) + return(NIL); + if(c != '"') + { unlrch(c); + goto out; + } + default: + ok: + if(++i > LSMAX) + break; + *cp++ = c; + continue; + } + else + { if(islword(c)) goto ok; + unlrch(c); + break; + } + out: + lp = getln(); + lp->ltyp = LT_STR; + lp->lval.lvs = malloc(i+1); + *cp = 0; + strcpy(lp->lval.lvs, cbuf); + return(lp); +} +islword(c) +{ return((040 < c && c < 0177 + && c != '(' && c !=')' && c != ';' + && c != '"' && c != '\\') ? 1 : 0); +} + + +struct lnode *keybind(), *keyallun(), *menuitem(), *efun(), + *undefall(); + +struct lfun { + char *lfname; /* Name of list function */ + struct lnode * (*lfrtn)(); /* Function address */ +} lfntab[] = { + "keybind", keybind, + "efun", efun, + "menuitem", menuitem, + "keyallunbind", keyallun, +/* "keyunbind", keyunbind, */ /* Not yet */ + "undefall", undefall, +/* "undef", undef, */ /* Not yet */ + 0, 0 +}; + +struct lnode * +eval(lp) +register struct lnode *lp; +{ register struct lnode *flp; + register struct lfun *lfent; + + if(lp->ltyp != LT_LIST) + return(lp); + if((flp = lp->lval.lvl) == NIL) + return(NIL); + if(flp->ltyp != LT_STR) + return(NIL); + + /* Look up list function and invoke it */ + for(lfent = lfntab; lfent->lfname; lfent++) + if(strueq(flp->lval.lvs, lfent->lfname)) + return((*(lfent->lfrtn))(flp->lnxt)); + + lerr("unknown op: (%s)", flp->lval.lvs); + return(NIL); +} + + +/* UNDEFALL - (undefall) +** Undefines all functions. Typically used to clear out +** predefined functions prior to compiling a set of new efuns. +*/ +struct lnode * +undefall(lp) +register struct lnode *lp; +{ + register int i; + efxmax = 0; /* Say nothing in function def table! */ + for(i = 0; i < EFUNMAX; ++i) + { efuntab[i].ef_idx = 0; + efuntab[i].ef_name = 0; + efuntab[i].ef_adr = 0; + efuntab[i].ef_mod = 0; + } + return(LTRUE); +} + +/* EFUN - (efun
) +** Checks out the args and if no problems, stores the function +** definition in efuntab. +*/ +struct lnode * +efun(lp) +register struct lnode *lp; +{ struct lnode *nlp; + register int c, i; + register struct fun *fnp; + char *fname, *faddr, *fmod; + int fni, num; + + if(listcnt(lp) < 4) + { lerr("efun - not enough args"); + return(NIL); + } + + /* First thing should be function index */ + switch(lp->ltyp) + { case LT_VAL: + fni = lp->lval.lvi; + break; + case LT_STR: + if(numcvt(lp->lval.lvs, &num)) + { fni = num; + break; + } + default: + lerr("efun - non-value function index"); + return(NIL); + } + + /* Next thing should be function name */ + lp = lp->lnxt; + if(lp->ltyp != LT_STR) /* Function name not a string */ + { lerr("efun - non-string function name"); + return(NIL); + } + fname = lp->lval.lvs; + + /* Next thing should be function addr */ + lp = lp->lnxt; + if(lp->ltyp != LT_STR) /* Function addr not a string */ + { lerr("efun - non-string function addr"); + return(NIL); + } + faddr = lp->lval.lvs; + + /* Next thing should be function module */ + lp = lp->lnxt; + if(lp->ltyp != LT_STR) /* Function module not a string */ + { lerr("efun - non-string function module"); + return(NIL); + } + fmod = lp->lval.lvs; + + /* Now see if already exists or anything */ + if(fni <= 0 || fni > EFUNMAX) + { lerr("efun - bad function index %d", fni); + return(NIL); + } + fnp = &efuntab[fni]; + if(fnp->ef_idx != 0) + { + if (fnp->ef_idx == fni + && strueq(fnp->ef_name, fname) + && strueq(fnp->ef_adr, faddr) + && (fnp->ef_mod == NULL || strueq(fnp->ef_mod, fmod))) + goto win; /* Benign redefinition */ + +lerr("efun - redefining function (%d \"%s\" %s %s)", + fnp->ef_idx, fnp->ef_name, fnp->ef_adr, + (fnp->ef_mod ? fnp->ef_mod : "unknown-module")); + } + for(i = 0; i < EFUNMAX; ++i) + { if(efuntab[i].ef_idx == 0) continue; + if(ustrcmp(efuntab[i].ef_adr,faddr) > 0 + || ustrcmp(efuntab[i].ef_name, fname) > 0) + { if(i == fni) continue; + lerr("efun - name or address dup! \"%s\"", fname); + return(NIL); + } + } + + /* No problems, store the function def in efuntab! */ +win: fnp->ef_idx = fni; + fnp->ef_mod = fmod; + fnp->ef_adr = faddr; + fnp->ef_name = fname; + + if(efxmax < fni) efxmax = fni; + return(LTRUE); +} + + +/* KEYBIND - (keybind ) */ + +struct lnode * +keybind(lp) +register struct lnode *lp; +{ struct lnode *nlp; + register int c, i; + int fni; + + if(lp == NIL || (nlp = lp->lnxt)== NIL) + return(NIL); + switch(lp->ltyp) + { case LT_VAL: + c = lp->lval.lvi; + break; + case LT_LIST: + return(NIL); + case LT_STR: + c = repchar(lp->lval.lvs); + break; + } + if(c == -1) + return(NIL); /* No such command char name */ + + lp = nlp; + if(lp->ltyp != LT_STR) /* Function name not a string */ + { lerr("(keybind) non-string function name"); + return(NIL); + } + fni = findfun(lp->lval.lvs); + if(fni == 0) /* No such function name */ + { lerr("(keybind) no such function - \"%s\"", lp->lval.lvs); + return(NIL); + } + if(c & CB_EXT) + { c &= ~CB_EXT; + + /* Check for redefinition */ + for(i = 0; i < extcnt; i += 2) + if(c == (extptr[i]&0377)) /* Already there? */ + { if((extptr[i+1]&0377) != fni) /* Yes, check fn */ + lerr("(keybind) redefining X-%s as %d=\"%s\"", + charep(c), fni, lp->lval.lvs); + break; + } + if(i >= extcnt) /* Didn't find? */ + { if(extcnt >= extsiz) + { lerr("(keybind) too many X- commands"); + return(NIL); /* Too many EXT cmds */ + } + i = extcnt; /* Increase size of table */ + extcnt += 2; + } + /* Now store new binding */ + extptr[i] = c; + extptr[i+1] = fni; + } + else if(c&CB_META) + { c &= ~CB_META; + + /* Check for redefinition */ + for(i = 0; i < mtacnt; i += 2) + if(c == (mtaptr[i]&0377)) /* Already there? */ + { if((mtaptr[i+1]&0377) != fni) /* Yes, check fn */ + lerr("(keybind) redefining M-%s as %d=\"%s\"", + charep(c), fni, lp->lval.lvs); + break; + } + if(i >= mtacnt) /* Didn't find? */ + { if(mtacnt >= mtasiz) + { lerr("(keybind) too many M- commands"); + return(NIL); /* Too many META cmds */ + } + i = mtacnt; /* Increase size of table */ + mtacnt += 2; + } + /* Now store new binding */ + mtaptr[i] = c; + mtaptr[i+1] = fni; + } + else { + i = c & 0177; + if (chrptr[i] && (chrptr[i]&0377) != fni) + lerr("(keybind) redefining %s as %d=\"%s\"", + charep(c), fni, lp->lval.lvs); + chrptr[i] = fni; + } + return(LTRUE); +} + +/* KEYALLUNBIND - (keyallunbind) */ +struct lnode * +keyallun() +{ register int i; + register char *cp; + +/* fprintf(stderr, "ellec: clearing all key definitions\n"); */ + for(i = 0, cp = chrptr; i < chrcnt; i++) + *cp++ = 0; + mtacnt = extcnt = mnucnt = 0; + return(LTRUE); +} + +/* MENUITEM - (menuitem ) */ + +struct lnode * +menuitem(lp) +register struct lnode *lp; +{ register int i, fni; + + if(lp == NIL) + return(NIL); + switch(lp->ltyp) + { case LT_VAL: + fni = lp->lval.lvi; + break; + case LT_LIST: + return(NIL); + case LT_STR: + fni = findfun(lp->lval.lvs); + break; + } + if(fni == 0) return(NIL); /* Bad val or no such function name */ + for(i = 0; i < mnusiz; i++) + if(fni == (mnuptr[i]&0377) || mnuptr[i] == 0) + { mnuptr[i] = fni; + mnucnt++; + return(LTRUE); + } + return(NIL); /* Too many menu items */ +} + +repchar(str) +register char *str; +{ register int c; + register int i, l; + + if (str == 0) return (-1); + i = 0; + l = strlen(str); + c = (*str++)&0377; + if(l == 0) return(-1); + if(l == 1) return(c); /* One-char representation */ + if(c == '^') + if(l == 2) return((~0140) & mupcase(*str)); + else return(-1); + c = mupcase(c); + if (*str == '-') + { if(*++str == 0) return(-1); + switch(c) + { case 'X': return(CB_EXT | mupcase(repchar(str))); + case 'M': return(CB_META | mupcase(repchar(str))); + case 'C': return((~0140) & repchar(str)); + } + } + if(c == 'S' && upcase(*str) == 'P' && l == 2) + return(' '); + if(c == 'D' && upcase(*str++) == 'E' && upcase(*str++) == 'L' + && *str == 0) + return(0177); + return(-1); +} + +struct lnode * +getln() +{ return((struct lnode *)calloc(1,sizeof(struct lnode))); +} + +numcvt(str, anum) +char *str; +int *anum; +{ register char *cp; + register int i, c, sign; + if((cp = str) == 0) + return 0; + i = sign = 0; + if(*cp == '-') + cp++, sign++; + while(c = *cp++) + if(!isdigit(c)) return(0); + else i = 10*i + (c - '0'); + *anum = sign ? -i : i; + return(1); +} + + + +listcnt(lp) +register struct lnode *lp; +{ register int i; + i = 0; + while(lp) + ++i, lp = lp->lnxt; + return(i); +} + +/* FUNNAME - Given function index, return function name. +** Always wins; returns "unknown" for bad indices. +*/ +char * +funname(i) +register int i; +{ + register char *cp = NULL; + if(0 < i && i <= efxmax && (cp = efuntab[i].ef_name)) + return cp; + return("unknown function"); +} + +findfun(name) +register char *name; +{ register int i; + if((i = efxmax) > 0) + { do { if(strueq(name, efuntab[i].ef_name)) + return(i); + } while(--i); + return(0); + } + return(0); +} + + +/* FUNCNT - Scan all key bindings, counting each occurrence of every +** function index. +** This is used to determine which functions are actually used. +*/ +funcnt(arr) +register int *arr; /* Pointer to array of EFUNMAX ints */ +{ + register int i; + + for(i = 0; i < EFUNMAX; ++i) /* Clear the array */ + arr[i] = 0; + + for(i = 0; i < chrcnt; ++i) /* Scan bindings */ + arr[chrptr[i]&0377]++; + for(i = 0; i < mtacnt; i += 2) + arr[mtaptr[i+1]&0377]++; + for(i = 0; i < extcnt; i += 2) + arr[extptr[i+1]&0377]++; +} + +scpy(from,to,cnt) +register char *from,*to; +register int cnt; +{ if(cnt > 0) + do { *to++ = *from++; } + while(--cnt); +} + +/* STRIPSP - strip spaces from string. Returns ptr to start. */ +char * +stripsp(cp) +register char *cp; +{ + register char *ep, *lastp; + while(*cp == ' ') ++cp; + if (*cp) + { ep = cp + strlen(cp); /* Point to null ending the str */ + while (*--ep == ' '); + *++ep = 0; /* Tie it off */ + } + return cp; +} + +warn(str,a,b,c,d,e,f,g,h,i) +char *str; +{ + fprintf(stderr, "ellec: "); + fprintf(stderr, str, a,b,c,d,e,f,g,h,i); + fprintf(stderr, "\n"); +} + +lerr(str,a,b,c,d,e,f,g,h,i) +char *str; +{ + warn(str, a,b,c,d,e,f,g,h,i); + *linecp = 0; /* Tie off current line buffer */ + fprintf(stderr, " Line %d: %s\n", lineno, linebuf); +} + +fatal(str,a,b,c,d,e,f,g,h,i) +char *str; +{ + warn(str, a,b,c,d,e,f,g,h,i); + exit(1); +} + diff --git a/commands/elle/sb.h b/commands/elle/sb.h new file mode 100755 index 000000000..a4808f7d5 --- /dev/null +++ b/commands/elle/sb.h @@ -0,0 +1,281 @@ +/* SB - Copyright 1982 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. In all cases + * the source code and any modifications thereto must remain + * available to any user. + * + * This is part of the SB library package. + * Any software using the SB library must likewise be made + * quasi-public, with freely available sources. + */ + +#ifdef COMMENT + +The initials "SB" stand for "String Block" or "String Buffer". + +SBBUFFER - A SB buffer containing a sbstring opened for editing. +SBFILE - A structure holding file-specific information for all + SDBLKs pointing to that file. +SBSTRING - A SB string; conceptually a single string, but actually + a linked list of SDBLKs. Unless opened by a SBBUFFER, + only a few operations are allowed on SBSTRINGs (creating, + copying, deleting). +SDBLK - One of the linked nodes constituting a sbstring. Each SDBLK + node points to a continuous string either in memory or + on disk, or both. +SBLK - Another name for SDBLK. +SMBLK - An allocated chunk of memory. Also refers to the node structure + maintained by the SBM memory management routines, which + points to the actual chunk of memory. +SBM - Name of the memory management package. SBM routines are used + to allocate memory in general, and are not just for + use by SB routines. + +************ MACHINE DEPENDENT DEFINITIONS ********** + + The following compile time definitions represent machine +dependent parameters which are intended mainly for use only by SBM and +SBSTR routines. Other programs should use them with caution. Note +that a great deal of code assumes that type "int" corresponds to a basic +machine word (as per C Reference Manual). + + The current definitions will only work for machines which have +1, 2, 4, or 8 "char" bytes in a machine word. Any other size will +require some changes to the definitions and possibly to some places +using them. + +WORD - integer-type definition corresponding to machine word. +WDSIZE - # addressable char bytes in a machine word. (1, 2, 4, 8) +WDBITS - # low order bits in an address, ie log2(WDSIZE). (0, 1, 2, 3) +WDMASK - Mask for low order bits of address (0, 1, 3, 7) +CHAR_MASK - If defined, machine does sign-extension on chars, and + they must be masked with this value. + + Note that the macro for WDBITS has no mathematical significance +other than being an expression which happens to evaluate into the right +constant for the 4 allowed values of WDSIZE, and in fact it is this +crock which restricts WDSIZE! If C had a base 2 logarithm expression +then any power of 2 could be used. + +Values for machines + WORD WDSIZE WDBITS WDMASK + PDP11, Z8000, I8086 int 2 1 01 + VAX11, M68000, PDP10 int 4 2 03 + +#endif /* COMMENT */ + +/* First try to define a few things in a semi-portable way +*/ +#include "eesite.h" +#ifdef __STDC__ /* Implementation supports ANSI stuff? */ +#include /* Get sizes for char stuff */ +#define _SBMUCHAR 1 /* Can use "unsigned char" */ +#define _SBMCHARSIGN (CHAR_MIN < 0) /* True if "char" is sign-extended */ +#define CHAR_MASK (UCHAR_MAX) + +#else /* not ANSI */ +#ifndef _SBMUCHAR /* Default assumes no "unsigned char" */ +#define _SBMUCHAR 0 +#endif +#ifndef _SBMCHARSIGN /* Default assumes "char" is sign-extended */ +#define _SBMCHARSIGN 1 +#endif +#ifndef CHAR_MASK /* Default assumes "char" is 8 bits */ +#define CHAR_MASK 0377 +#endif +#endif /* not ANSI */ + +/* Define "sb_uchartoint" as a macro which ensures that an unsigned +** character value is converted properly to an int value. +*/ +#if (_SBMUCHAR || (_SBMCHARSIGN==0)) +#define sb_uchartoint(a) (a) /* No fear of sign extension */ +#else +#define sb_uchartoint(a) ((a)&CHAR_MASK) /* Bah, sign extension */ +#endif + + +/* Defs for machines with a base-2 WDSIZE. Yes, the (int) is indeed necessary + * (to allow implicit conversion to long where needed - the PDP11 compiler + * is known to lose without it, because sizeof is cast as "unsigned int" + * which loses big in long masks!) + */ +#define WORD int +#define WDSIZE ((int)(sizeof(WORD))) +#define WDMASK (WDSIZE-1) +#define WDBITS ((WDSIZE>>2)+(1&WDMASK)) + +#define rnddiv(a) ((a)>>WDBITS) /* # words, rounded down */ +#define rndrem(a) ((a)&WDMASK) /* # bytes remaining past wd bndary */ +#define rnddwn(a) ((a)&~WDMASK) /* Round down to word boundary */ +#define rndup(a) rnddwn((a)+WDSIZE-1) /* Round up to word boundary */ + +#ifdef COMMENT /* The following are for machines without a base-2 WDSIZE */ +#define rnddiv(a) ((a)/WDSIZE) +#define rndrem(a) ((a)%WDSIZE) +#define rnddwn(a) ((a)-rndrem(a)) +#define rndup(a) rnddwn((a)+WDSIZE-1) +#undef WDMASK /* These become meaningless and anything */ +#undef WDBITS /* which uses them should be changed! */ +#endif /* COMMENT */ + +/* The following 3 definitions are somewhat machine-dependent, + * but are specifically intended for general use and work for all + * currently known C implementations. + * SBMO must be an integer-type object large enough to hold + * the largest difference in SBMA pointers, and must not be + * used in signed comparisons. + */ + +typedef long chroff; /* CHROFF - Char offset in disk/sbstr */ +typedef unsigned int SBMO; /* SBMO - Char offset in memory */ +typedef +#if _SBMUCHAR + unsigned +#endif + char *SBMA; /* SBMA - Pointer to char loc in memory */ + + + +/* The following definitions tend to be system-dependent. Only the + * SBM and SBSTR routines use them. + */ +#define SB_NFILES 32 /* # of open files we can hack. Actually + * this is max FD value plus 1. */ +#define SB_BUFSIZ 512 /* Optimal buffer size (system block size) */ +#define SB_SLOP (16*WDSIZE) /* # slop chars to tolerate for allocations */ + +#define SMNODES (20) /* # SM or SD nodes to create when needed */ +#define SMCHUNKSIZ (16*512) /* # bytes of mem to create (via sbrk) " " */ +#define MAXSBMO ((SBMO)-1) /* Used in SBM only */ + /* MAXSBMO should be the largest possible SBMO value. */ + +#define EOF (-1) +#define SBFILE struct sbfile +#define SBBUF struct sbbuffer +#define SBSTR struct sdblk /* Start of a sbstring */ + +struct sbfile { + int sfflags; /* Various flags */ + int sffd; /* FD for file (-1 if none) */ + struct sdblk *sfptr1; /* Ptr to 1st node in phys list */ + chroff sflen; /* Original length of file FD is for */ +}; + + /* Definition of SBBUF string/buffer */ +struct sbbuffer { + SBMA sbiop; /* I/O pointer into in-core text */ + int sbrleft; /* # chars left for reading */ + int sbwleft; /* # chars left for writing */ + int sbflags; /* Various flags */ + chroff sbdot; /* Logical pos for start of current sdblk */ + chroff sboff; /* Offset into current sdblk (if no smblk)*/ + struct sdblk *sbcur; /* Pointer to current SD block of string */ +}; + /* Flags for "sbflags" */ +#define SB_OVW 01 /* Over-write mode */ +#define SB_WRIT 02 /* Written; smuse needs to be updated from sbiop */ + + /* NOTE: An unused sbbuf structure should be completely zeroed. + * This will cause routines to handle it properly + * if they are accidentally pointed at it. + */ + + /* Definition of SDBLK */ +struct sdblk { + struct sdblk *slforw; /* Logical sequence forward link */ + struct sdblk *slback; /* Logical sequence backward link */ + int sdflags; + struct sdblk *sdforw; /* Physical sequence (disk) */ + struct sdblk *sdback; /* ditto - backptr for easy flushing */ + struct smblk *sdmem; /* Mem pointer, 0 if no in-core version */ + SBFILE *sdfile; /* File pointer, 0 if no disk version */ + chroff sdlen; /* # chars in disk text */ + chroff sdaddr; /* Disk address of text */ +}; + /* Flags for "sdflags" */ +#define SD_LOCK 0100000 /* Locked because opened by a SBBUF */ +#define SD_LCK2 0040000 /* Locked for other reasons */ +#define SD_MOD 0020000 /* Modified, mem blk is real stuff */ +#define SD_NID 0323 /* Node ID marks active (not on freelist) */ +#define SD_LOCKS (SD_LOCK|SD_LCK2) + +/* Note sdback is ONLY needed for fixing up phys list when a sdblk is + * deleted (so as to find previous blk in phys list). Perhaps it shd + * be flushed (ie only use SDFORW)? How to do deletions - use circular + * list? Sigh. + */ + + /* Definition of SMBLK (used by SBM routines) */ +struct smblk { + struct smblk *smforw; /* Links to other mem blks, in phys order */ + struct smblk *smback; + int smflags; /* Type, in-use flags */ + SBMA smaddr; /* Mem address of text */ + SBMO smlen; /* # bytes in mem block */ + SBMO smuse; /* # bytes "used" in block */ +}; + /* Flags for "smflags" */ +#define SM_USE 0100000 /* Block is in use (mem free if off) */ +#define SM_NXM 040000 /* Block mem is non-existent */ +#define SM_EXT 020000 /* Block mem owned by external (non-SBM) rtn*/ +#define SM_MNODS 010000 /* Block holds SMBLK nodes */ +#define SM_DNODS 04000 /* Block holds SDBLK nodes */ +#define SM_NID 0315 /* Node in-use identifier (low byte) */ + +/* Error handler type values */ +#define SBMERR 0 /* Error in SBM package */ +#define SBXERR 1 /* Error in SBSTR package */ +#define SBFERR 2 /* "Error" - SBSTR package found a file overwritten. + * Non-zero return will continue normally. */ + + +/* Redefine certain external symbols to be unique in the first 6 chars +** to conform with ANSI requirements. +*/ +#define sbm_nfre sbmnfre /* SBM stuff */ +#define sbm_nfor sbmnfor +#define sbm_nmov sbmnmov +#define sbm_ngc sbmngc +#define sbx_ndget sbxndg /* SBSTR stuff */ +#define sbx_ndel sbxnde +#define sbx_ndfre sbxndf +#define sbx_sdcpy sbxsdc +#define sbx_sdgc sbxsdg +#define sbe_sdlist sbesls /* SBERR stuff */ +#define sbe_sdtab sbestb +#define sbe_sds sbesds +#define sbe_sbvfy sbesbv +#define sbe_sbs sbesbs + +/* Forward declarations */ +extern SBMA sbm_lowaddr; /* For roundoff purposes */ + +extern SBFILE sbv_tf; /* SBFILE for temp swapout file */ +extern int (*sbv_debug)(); /* Error handler address */ +extern off_t lseek(); /* For sbstr code mostly */ +extern char *mktemp(); +extern char *malloc(); +extern char *calloc(); +extern SBBUF *sb_open(); +extern SBSTR *sb_close(), *sb_fduse(), *sbs_cpy(), *sbs_app(), *sb_cpyn(), + *sb_killn(); +extern struct sdblk *sbx_ready(); +extern chroff sb_tell(), sb_ztell(), sbs_len(); + +/* Definition of SB_GETC, SB_PUTC, SB_BACKC macros */ + +#define sb_putc(s,c) (--((s)->sbwleft) >= 0 ? \ + (*(s)->sbiop++ = c) : sb_sputc(s,c)) +#define sb_getc(s) (--((s)->sbrleft) >= 0 ? \ + sb_uchartoint(*(s)->sbiop++) : sb_sgetc(s)) +#define sb_peekc(s) ((s)->sbrleft > 0 ? \ + sb_uchartoint(*(s)->sbiop) : sb_speekc(s)) + +/* WARNING - sb_backc must ONLY be used if last operation was a + * successful sb_getc!! For slow but sure invocation use sb_rgetc. + */ +#define sb_backc(s) (++(s->sbrleft), --(s->sbiop)) + +#include "sbproto.h" /* function prototypes */ diff --git a/commands/elle/sbbcpy.c b/commands/elle/sbbcpy.c new file mode 100755 index 000000000..463bd88ee --- /dev/null +++ b/commands/elle/sbbcpy.c @@ -0,0 +1,54 @@ + +#include "sb.h" + +/* BCOPY(from,to,cnt) - Copy string of bytes. + * Normally this routine is an assembly-language library routine, + * but not all systems have it. Hence this C-language version + * which tries to be fairly machine-independent. + * Attempts to be clever about using word moves instead of byte moves. + * Does not hack overlapping backward moves. + */ +bcopy(from, to, cnt) /* Copy count bytes from -> to */ +register SBMA from; +register SBMA to; +register unsigned cnt; +{ + if(!cnt) + return; + while(rndrem((int)from)) /* Get source aligned */ + { *to++ = *from++; + if(--cnt == 0) return; + } + if(rndrem((int)to) == 0) /* Do word move if dest now aligned */ + { register unsigned tmp; + tmp = cnt; + if((cnt = rnddiv(cnt)) > 4) + { sbm_wcpy((int *)from, (int *)to, cnt); + if((cnt = rndrem(tmp)) == 0) + return; /* No leftover bytes, all done */ + tmp -= cnt; /* Ugh, must update pointers */ + from += tmp; + to += tmp; + } + else cnt = tmp; /* Not worth call overhead */ + } + do { *to++ = *from++; } /* Finish up with byte loop */ + while(--cnt); +} + +/* SBM_WCPY - word-move auxiliary routine. + * This is a separate routine so that machines with only a few + * registers have a chance to use them for the word copy loop. + * This cannot be made part of BCOPY without doing some + * unnecessary pointer conversions and using extra variables + * (since most compilers will not accept type casts on lvalues, + * which are needed to treat (char *) as (int *)). + */ +sbm_wcpy(from, to, cnt) +register int *from, *to; +register unsigned cnt; +{ + if(cnt) + do { *to++ = *from++; } + while(--cnt); +} diff --git a/commands/elle/sberr.c b/commands/elle/sberr.c new file mode 100755 index 000000000..c707532b6 --- /dev/null +++ b/commands/elle/sberr.c @@ -0,0 +1,679 @@ +/* SB - Copyright 1982 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. In all cases + * the source code and any modifications thereto must remain + * available to any user. + * + * This is part of the SB library package. + * Any software using the SB library must likewise be made + * quasi-public, with freely available sources. + */ + +#define PRINT /* Include printout stuff */ + +#include "sb.h" +#include + +extern struct smblk *sbm_nfl; +extern struct smblk *sbm_list; +extern struct sdblk *sbx_nfl; + +#ifdef PRINT +#define PRF(stmt) {if(p) stmt;} +#define PRFBUG(str,stmt) {if(p) stmt;else return(str);} +#define PRFBAD(str,stmt) {if(p) stmt; return(str);} +#else +#define PRF(stmt) ; +#define PRFBUG(str,stmt) return(str); +#define PRFBAD(str,stmt) return(str); +#endif + +#ifndef NPTRS +#define NPTRS (1000) /* Catch loops of period less than this. */ +#endif + +int sbe_dec = 0; /* Set nonzero to use decimal printout */ + +struct ptab { + int pt_pflag; /* Printflag value */ + char *pt_err; /* Error string return */ + int pt_xerr; /* Error index return */ + int pt_hidx; /* Highest freelist entry */ + int pt_nsto; /* # entries stored in table */ + int pt_cnt; /* # of entry store attempts */ + struct smblk *pt_tab[NPTRS]; +}; + +_PROTOTYPE( char *sbe_sdtab, (struct ptab *pt, int p, int phys) ); +_PROTOTYPE( char *sbe_schk, (struct sdblk *sd, struct ptab *pt) ); +_PROTOTYPE( int sbe_tbent, (struct ptab *pt, struct smblk *sm) ); + +#define PTF_PRF 01 /* Do printout stuff */ +#define PTF_OVFERR 02 /* Complain if table overflows */ +#define PTF_SDPHYS 04 /* Follow SD phys links (else logical links) */ + +struct flgt { + int flg_bit; + int flg_chr; +}; + +_PROTOTYPE( char *sbe_fstr, (int flags, struct flgt *fp) ); + +char *sbe_mvfy(), *sbe_mfl(), *sbe_mlst(); /* SBM */ +char *sbe_sbvfy(), *sbe_sbs(); /* SBBUF */ +char *sbe_svfy(), *sbe_sdlist(), *sbe_sdtab(), *sbe_schk(); /* SD */ +char *sbe_fstr(); /* Misc utility */ + + +/* SBE_MEM() - Print out memory usage list +*/ +sbe_mem() +{ + printf("\nMemory Usage:\n"); + printf("\tsbm_nfl : %6o\n",sbm_nfl); + printf("\tsbm_list: %6o\n",sbm_list); + printf("\tsmblk nodes are %o bytes long.\n",sizeof (struct smblk)); + + sbe_mlst(1); /* Scan mem list, printing stuff. */ +} + +/* SBE_MVFY() - Verify memory allocation structures + * Returns error message (0 if no errors found). + */ +char * +sbe_mvfy() +{ register char *res; + + if((res = sbe_mfl(0)) + || (res = sbe_mlst(0))) + return(res); + return(0); +} + +/* SBM Debugging Routines */ + +struct flgt smflgtab[] = { + SM_USE, 'U', + SM_NXM, 'N', + SM_EXT, 'E', + SM_MNODS,'M', + SM_DNODS,'D', + 0,0 +}; + +static char smfhelp[] = "U-Used, N-NXM, E-External, M-SMnodes, D-SDnodes"; +static char smhdline[] = "\ + SM: back smaddr smlen smuse smflags"; + +/* SBE_MFL(printflag) - Verify/Print memory freelist + * Returns error message (0 if no errors found). + */ +char * +sbe_mfl(p) +int p; +{ register struct smblk *sm; + register int i; + struct ptab smtab; /* For loop detection */ + + PRF(printf("Tracing SM node freelist --\n")) + PRF(printf(" Maximum loop detection size is %d.", NPTRS)) + if((sm = sbm_nfl) == 0) + { PRF(printf("\n\tNo list.\n")) + return(0); /* Null freelist is ok */ + } + smtab.pt_pflag = p ? PTF_PRF : 0; + smtab.pt_nsto = smtab.pt_cnt = 0; + i = 0; /* Print 8 addrs/line */ + for(; sm; sm = sm->smforw) + { + PRF(printf("%s%7o->", (i==0 ? "\n " : ""), sm)) + if(++i >= 8) i = 0; + if(sbe_tbent(&smtab, sm) < 0) /* If hit loop, stop */ + PRFBAD("SM freelist loop", + printf("\nLOOP - %o seen as node %d!!\n", + sm, smtab.pt_xerr)) + if(sm->smflags) + { PRF((i = 0, printf("\nFreelist node has flags:\n"))) + PRFBUG("Free SM flagged", sbe_smp(sm, 0)) + } + } + PRF(printf("\nEnd - %d nodes on SM freelist.\n", smtab.pt_cnt)) + return(0); +} + +/* SBE_MLST(printflag) - Verify/Print allocated memory list. + * Returns error message (0 if no errors found). + */ +char * +sbe_mlst(p) +int p; +{ register struct smblk *sm, *smf, *smb; + char *nextaddr; + int i; + struct ptab smtab; /* For loop detection */ + + PRF(printf("Tracing mem list -- \n")) + if((sm = sbm_list) == 0) + { PRF(printf("\tNo list?!\n")) + if(sbm_nfl) /* Ensure rest are 0 too */ + return("No mem list?!!"); + return(0); + } + + smtab.pt_pflag = p; + smtab.pt_cnt = smtab.pt_nsto = 0; + smb = 0; + PRF(printf(" Flags: %s\n%s\n", smfhelp, smhdline)) + for(; sm; sm = smf) + { PRF(printf(" %6o: ",sm)) + if(sbe_tbent(&smtab, sm) < 0) + PRFBAD("Loop in mem list!!", + printf("LOOP - seen as node %d!!\n", smtab.pt_xerr)) + + if(sm->smback == smb) + PRF(printf("^ ")) /* Back ptr OK */ + + else PRFBUG("Bad back ptr!", + printf("%6o BAD Backptr!!\n\t ",sm->smback)) + + if((sm->smflags&0377)!= SM_NID) + PRFBUG("SM: bad node ID", + printf("BAD - no node ID!\n\t ")) + PRF(printf((sm->smflags&SM_USE) ? " " : "FREE ")) + if(sm->smlen == 0) + PRFBUG("SM: len 0", + printf("Zero-length area!")) + if((sm->smflags&SM_USE)==0 + && rndrem(sm->smaddr - sbm_lowaddr)) + PRFBUG("Bad free-mem block", + printf("Bad free-mem block")) + PRF(sbe_smp(sm, 1)) /* Print out rest of info */ + + if(nextaddr != sm->smaddr + && smtab.pt_cnt != 1) /* 1st time needs init */ + { PRFBUG("Alignment error!", + printf("\t BAD!! %6o expected; ",nextaddr)) +#if !(MINIX) + PRF((i = sm->smaddr - nextaddr) > 0 + ? printf("%d skipped.\n",i) + : printf("%d overlapped.\n",-i)) +#endif + } + nextaddr = sm->smaddr + sm->smlen; + smf = sm->smforw; + smb = sm; /* Save ptr to back */ + } + PRF(printf("End = %6o\n",nextaddr)) + return(0); +} + +#ifdef PRINT +sbe_smp(sm,type) +register struct smblk *sm; +int type; +{ + if(type==0) + printf(" %6o: %s ", sm, + ((sm->smflags&SM_USE) ? " " : "FREE")); + printf("%6o: ", sm->smaddr); + printf((sbe_dec ? "%5d. %5d." : "%6o %6o"), sm->smlen, sm->smuse); + printf(" %7o = %s\n", sm->smflags, sbe_fstr(sm->smflags, smflgtab)); +} +#endif /*PRINT*/ + +/* SD (SBSTR) debugging routines */ + +struct flgt sdflgtab[] = { + SD_LOCK, 'L', + SD_LCK2, 'T', + SD_MOD, '*', + 0,0 +}; + +static char sdfhelp[] = "\ + flags: *-MOD (disk outofdate), L-LOCK, T-LCK2 (temp)"; +static char sdhdline[] = "\ + SD: slforw slback sdflgs sdforw sdback sdmem sdfile sdaddr sdlen"; + + +/* SBE_SFL(printflag) - Verify/Print SD freelist + * Returns error message (0 if no errors found). + */ +char * +sbe_sfl(p) +int p; +{ register struct sdblk *sd; + register int i; + struct ptab sdtab; /* For loop detection */ + + PRF(printf("Tracing SDBLK node freelist --\n")) + PRF(printf(" Maximum loop detection size is %d.", NPTRS)) + if((sd = sbx_nfl) == 0) + { PRF(printf("\n\tNo list.\n")) + return(0); /* Null freelist is ok */ + } + sdtab.pt_pflag = p ? PTF_PRF : 0; + sdtab.pt_nsto = sdtab.pt_cnt = 0; + i = 0; /* Print 8 addrs/line */ + for(; sd; sd = sd->slforw) + { + PRF(printf("%s%7o->", (i==0 ? "\n " : ""), sd)) + if(++i >= 8) i = 0; + if(sbe_tbent(&sdtab, sd) < 0) /* If hit loop, stop */ + PRFBAD("SD freelist loop", + printf("\nLOOP - %o seen as node %d!!", + sd, sdtab.pt_xerr)) + if(sd->sdflags) + { PRF((i = 0, printf("\nFreelist node has flags:\n"))) + PRFBUG("Free SD flagged", sbe_psd(sd)) + } + } + PRF(printf("\nEnd - %d nodes on SD freelist.\n", sdtab.pt_cnt)) + return(0); +} + + + +/* SBE_SDS() - Print out all sdblk data stuff + */ +sbe_sds() +{ int sbe_psd(); + + printf("Printout of all in-use SDBLKs:\n"); + printf(" %s\n", sdfhelp); + printf("%s\n", sdhdline); + sbm_nfor(SM_DNODS,sizeof(struct sdblk),sbe_psd,0); + printf("\n"); +} + +/* SBE_PSD - Auxiliary for invocation by SBE_SDS above. */ +sbe_psd(sd) +register struct sdblk *sd; +{ register int flags; + + flags = sd->sdflags; + printf("%c%c%c", + ((flags&SD_MOD) ? '*' : ' '), + ((flags&SD_LOCK) ? 'L' : ' '), + ((flags&SD_LCK2) ? 'T' : ' ')); + + printf(" %7o: %6o %6o %6o %6o %6o %6o %6o %7lo %5ld.\n", sd, + sd->slforw, sd->slback, sd->sdflags, + sd->sdforw, sd->sdback, sd->sdmem, + sd->sdfile, sd->sdaddr, sd->sdlen); + return(0); +} + +/* SBE_SVFY() - Verify all SD blocks + * Returns error message (0 if no errors found). + */ +char * +sbe_svfy() +{ register char *res; + return((res = sbe_sdlist(0,0)) ? res : sbe_sdlist(0,1)); +} + +/* SBE_SDLIST(printflag, physflag) - Verify/Print all SD blocks. + * Show logical lists if physflag 0 + * Show physical lists otherwise + * Returns error message (0 if no errors found). + */ +char * +sbe_sdlist(p,phys) +int p, phys; +{ register char *res; + struct ptab sdtab; /* The SDLIST table to use */ + + /* First put freelist in table, then scan for all + * SD nodes. Each active node (not in table) gets + * its entire list traced forward/backward and added to table. + */ + if(res = sbe_sdtab(&sdtab, p, phys)) /* Set up freelist table */ + return(res); + + /* Freelist entered in table, now scan all SD's */ + res = (char *)sbm_nfor(SM_DNODS,sizeof(struct sdblk), + sbe_schk, &sdtab); + + PRF(printf("\n")) + return(res); +} + +/* SBE_SDTAB(tableptr, printflag, physflag) - Auxiliary for SBE_SDLIST. + * Stuffs all freelist SDBLK addresses in table for dup detection. + * Returns error message (0 if no errors found). + */ +char * +sbe_sdtab(pt, p, phys) +register struct ptab *pt; +int p, phys; +{ register struct sdblk *sd; + register int res; + + pt->pt_pflag = (p ? PTF_PRF : 0) | (phys ? PTF_SDPHYS : 0) + | PTF_OVFERR; + pt->pt_cnt = pt->pt_nsto = 0; /* Initialize */ + + /* Stick freelist in table */ + for(sd = sbx_nfl; sd; sd = sd->slforw) + { if(sbe_tbent(pt, sd) < 0) + { if(pt->pt_xerr < 0) + PRFBAD("SD freelist too long", + printf("SD freelist too long (%d)\n", + NPTRS)) + PRFBAD("SD freelist loop", + printf("SD freelist loop at %o\n", pt->pt_xerr)) + } + + if(sd->sdflags) + { + PRF(printf("Bad free SD, non-zero flag:\n")) + PRFBUG("Free SD flagged", sbe_psd(sd)) + } + } + pt->pt_hidx = pt->pt_nsto; /* Set idx of 1st non-FL entry */ + return(0); +} + +/* SBE_SCHK(SDptr, tableptr) - Auxiliary for SBE_SDLIST. + * If SD not already in table, verifies or prints + * the complete physical or logical list it's on, and enters all + * of its SDs into table (to prevent doing it again). + * Returns 0 if no errors, else error string. +** There is a problem when the table overflows. The tbent routine +** wants to add it (wrapping around at bottom) in that case, because +** that still helps detect loops. But this routine wants to reset +** the table back (after scanning to head of list) and once it starts +** scanning forward again it will fail, because some of the SDs are +** still in the table due to the wraparound! Thus PTF_OVFERR is always +** set, in order to at least give the right error message. +*/ +char * +sbe_schk(sd, pt) +register struct sdblk *sd; +struct ptab *pt; +{ register struct sdblk *sdx; + register struct smblk *sm; + struct sbfile *savfile; + chroff lastaddr; + int p, res, savidx, phys; + + phys = pt->pt_pflag&PTF_SDPHYS; /* Set up physflag */ + if(phys && (sd->sdfile == 0)) /* Ignore non-phys stuff if phys */ + return(0); + p = pt->pt_pflag&PTF_PRF; /* Set up printflag */ + savidx = pt->pt_nsto; /* Remember initial extent of table */ + + if(sbe_tbent(pt, sd) < 0) + { if(pt->pt_xerr >= 0) /* OK if already in table */ + return(0); + PRFBAD("Too many SDs", + printf("Too many SDs for table (%d)\n", NPTRS)) + } + + /* Now search backward for start of list */ + while(sdx = (phys ? sd->sdback : sd->slback)) + if(sbe_tbent(pt,sdx) >= 0) + sd = sdx; + else break; + if(sdx) + { if(pt->pt_xerr < 0) /* Table error? */ + PRFBAD("Too many SDs", + printf("Too many SDs for table (%d)\n",NPTRS)) + PRF(printf("Backlist loop!! Dup'd node:%s\n", + (pt->pt_xerr < pt->pt_hidx) ? + "(on freelist!)" : "" )) + PRFBUG((phys ? "Phys SD loop" : "SD loop"), sbe_psd(sdx)) + } + /* Reset table to flush nodes backed over */ + pt->pt_cnt = pt->pt_nsto = savidx; + + /* SD now points to start of list. Begin stepping thru list... */ + PRF(printf("---- %sList started: ", (phys ? "Phys " : ""))) + if(phys) + { savfile = sd->sdfile; + PRF(printf(" SF: %o, fd= %d, ln= %ld\n", + savfile,savfile->sffd,savfile->sflen)) + if(savfile->sfptr1 != sd) + PRFBUG("SFPTR1 bad", + printf(" BAD!! Sfptr1 %o doesn't match SD %o!!\n", + savfile->sfptr1, sd)) + lastaddr = 0; + } + else PRF(printf("\n")) + + PRF(printf("%s\n", sdhdline)) + for(sdx = 0; sd; (sdx = sd, sd = (phys ? sd->sdforw : sd->slforw))) + { + PRF(sbe_psd(sd)) /* Print it out */ + if(sdx != (phys ? sd->sdback : sd->slback)) + { if(phys) + PRFBUG("PSD bad sdback",printf("\tBad phys backptr\n")) + else + PRFBUG("SD bad slback",printf("\tBad backptr\n")) + } + + if((sd->sdflags&0377) != SD_NID) + PRFBUG("Bad SD node ID", printf("\tBad node ID!\n")) + + + if(sd->sdfile && (sd->sdlen < 0 || sd->sdaddr < 0)) + PRFBUG("SD: neg len/addr", + printf("\tNeg disk len/addr\n")) + if(phys) goto dophys; + + /* Do special stuff for logical list */ + if(sm = sd->sdmem) + { if((sm->smflags&0377) != SM_NID) + PRFBUG("SD: bad SM", + printf("\nBad SMBLK ptr\n")) + if((sd->sdflags&SD_MOD)==0 + && sd->sdlen != sm->smuse) + PRFBUG("SD != SM", + printf("\tBad SMBLK? Len conflict\n")) + if(sm->smlen < sm->smuse) + PRFBUG("SD: SM len < use", + printf("\tBad SMBLK, len < use\n")) + } + goto doboth; /* Skip phys stuff */ + + /* Do special stuff for phys list */ + dophys: if(sd->sdfile != savfile) + PRFBUG("SD: bad sdfile", + printf("\tBad sdfile ptr! Shd be %o\n", + savfile)) + if(sd->sdaddr < lastaddr) + PRFBUG("SD addr out of order", + printf("\tBad disk addr, not in order!\n")) + lastaddr = sd->sdaddr; + /* Done with special phys stuff */ + + doboth: if(sbe_tbent(pt, sd) < 0) + { if(pt->pt_xerr < 0) + PRFBAD("Too many SDs", + printf("Too many SDs for table (%d)\n",NPTRS)) + + PRFBUG("SD loop", + printf("\tLOOP!! This SD already seen%s.\n", + (pt->pt_xerr < pt->pt_hidx) ? + " (on freelist!)" : "" )) + break; + } + } + PRF(printf("-----------\n")) + return(0); +} + +/* SBE_DSK(SFptr) - Print out disk usage list for specific file + */ + +sbe_dsk(sfp) +SBFILE *sfp; +{ + printf("SBFILE printout not coded: %o\n",sfp); +} + +/* SBBUF structure debugging routines */ + +struct flgt sbflgtab[] = { + SB_OVW, 'O', + SB_WRIT,'W', + 0,0 +}; +static char sbfhelp[] = "O-Overwrite, W-Write"; + +/* SBE_SBVFY(SBptr) - Verify a SB-string. + * Returns error message (0 if no errors found). + */ +char * +sbe_sbvfy(sbp) +SBBUF *sbp; +{ return(sbe_sbs(sbp,0)); +} + +/* SBE_SBS(SBptr, printflag) - Verify/Print SBSTR data stuff + * Returns error message (0 if no errors found). + */ +char * +sbe_sbs(sbp,p) +SBBUF *sbp; +int p; +{ register SBBUF *sb; + register struct smblk *sm; + register struct sdblk *sd; + + sb = sbp; + PRF(printf("SBSTR %o: ",sb)) + if(sb == 0) + PRFBUG(0,printf("Zero pointer???\n")) + + /* First print out cryptic summary in case pointers bomb + * out farther on. */ + PRF(printf(" (io,cur,r,w,f,.,+ = %o,%o,%d,%d,%o,%lo,%lo)\n", + sb->sbiop, sb->sbcur, sb->sbrleft, sb->sbwleft, + sb->sbflags, sb->sbdot, sb->sboff)) + + PRF(printf(" sbflags %5o = %s (%s)\n", + sb->sbflags, sbe_fstr(sb->sbflags,sbflgtab), + sbfhelp)) + + if(sd = sb->sbcur) /* Okay, now try getting pointers */ + sm = sd->sdmem; + else sm = 0; + + PRF(printf(" sbcur %6o",sd)) + if(sd) + { + PRF(printf("\n %s\n ", sdhdline)) + PRF(sbe_psd(sd)) + + if((sd->sdflags&0377) != SD_NID) + PRFBUG("SBCUR not SD?",printf(" BAD SDBLK ID!! \n")) + if(sm) + { + PRF(printf(" %s\n ", smhdline)) + PRF(sbe_smp(sm,0)) + if((sm->smflags&0377) != SM_NID) + PRFBUG("SBCUR has bad SM", + printf(" BAD SMBLK ID!!\n")) + } + } + + + PRF(printf(" sbiop %6o",sb->sbiop)) + if(sb->sbiop) + { if(!sm || sb->sbiop < sm->smaddr + || sb->sbiop > (sm->smaddr + sm->smlen)) + PRFBUG("Bad SBIOP", printf(" BAD")) + } + else if(sb->sbrleft > 0 || sb->sbwleft > 0) + PRFBUG("Bad SBIOP/cnts", printf(" BAD")) + PRF(printf("\n")) + + PRF(printf(" sbrleft %5o = %5d.",sb->sbrleft, sb->sbrleft)) + if(sb->sbrleft + && ( !sm + || sb->sbwleft + || (sb->sbflags&SB_WRIT) + || (sb->sbrleft != (sm->smuse - (sb->sbiop - sm->smaddr))) + )) + PRFBUG("Bad sbrleft", printf(" BAD")) + PRF(printf("\n")) + + PRF(printf(" sbwleft %5o = %5d.", sb->sbwleft, sb->sbwleft)) + if(sb->sbwleft + && ( !sm + || (sb->sbflags&SB_WRIT) == 0 + || (sb->sbwleft > (sm->smlen - (sb->sbiop - sm->smaddr))) + )) + PRFBUG("Bad sbwleft", printf(" BAD")) + PRF(printf("\n")) + + PRF(printf(" sbdot %7lo = %7ld.", sb->sbdot, sb->sbdot)) + if(sb->sbdot < 0) + PRFBUG("Bad sbdot", printf(" BAD")) + + PRF(printf("\n sboff %7lo = %7ld.\n", sb->sboff, sb->sboff)) + PRF(printf(" I/O ptr loc: %ld.\n\n", sb_tell(sb))) + + return(0); +} + +/* SBE_TBENT() - Auxiliary to add and check entries to a pointer table. + * Note we assume here that smblk ptrs are used, although sdblks + * can also be hacked. This wins as long as the two kinds of ptrs + * are basically identical (saves horrible casting problems). + * Returns index # if successful (between 0 and NPTRS-1 inclusive). + * Otherwise an error (-1), with relevant info in pt_xerr: + * -1 if out of room and flag set making it an error + * 0-n if entry already existed. + */ +sbe_tbent(pt, sm) +register struct ptab *pt; +struct smblk *sm; +{ register struct smblk **smt; + register int i; + int p; + + p = pt->pt_pflag&PTF_PRF; /* Set up print flag */ + smt = &(pt->pt_tab[0]); + if(i = pt->pt_nsto) + { do { + if(sm == *smt++) + { pt->pt_xerr = pt->pt_nsto - i; + return(-1); + } + } while(--i); + --smt; + } + + i = pt->pt_cnt++; + if(++(pt->pt_nsto) > NPTRS) + { if(pt->pt_pflag&PTF_OVFERR) + { pt->pt_err = "Ptrtab overflow"; + pt->pt_xerr = -1; + return(-1); + } + pt->pt_nsto = NPTRS; + i %= NPTRS; + } + pt->pt_tab[i] = sm; + return(i); +} + +/* SBE_FSTR(flags, flagtab) - Auxiliary to convert flag word to a string + * and return pointer to it. Handy for printfs. + */ +char * +sbe_fstr(flags, fp) +register int flags; +register struct flgt *fp; +{ static char retstr[17]; /* Max of 16 flags */ + register char *cp; + cp = retstr; + for(; fp->flg_bit; ++fp) + *cp++ = (fp->flg_bit&flags) ? fp->flg_chr : ' '; + *cp = 0; + return(retstr); +} diff --git a/commands/elle/sbm.c b/commands/elle/sbm.c new file mode 100755 index 000000000..e3149715e --- /dev/null +++ b/commands/elle/sbm.c @@ -0,0 +1,915 @@ +/* SB - Copyright 1982 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. In all cases + * the source code and any modifications thereto must remain + * available to any user. + * + * This is part of the SB library package. + * Any software using the SB library must likewise be made + * quasi-public, with freely available sources. + */ + +#if 0 + This file contains the low-level memory allocation +subroutines which are used by the SBLK routines. The code here +is quite machine-dependent, and the definitions in "sb.h" should be +carefully checked to verify that they are correct for the target +machine. + + The ultimate low-level routine is "sbrk()" which must be +provided by the system''s C library. SBM expects that successive calls +to sbrk() will return contiguous areas of memory with progressively +higher addresses. Also, the very first call to sbrk() is assumed to +return a word-aligned address. +#endif /*COMMENT*/ + +#include "sb.h" + +#define FUDGE (sizeof(struct smblk)) /* Allow this much fudge in + allocation, to prevent undue fragmentation */ + +char *(*sbm_debug)(); /* Debug switch - user-furnished routine */ + +struct smblk *sbm_nfl; /* Pointer to node freelist */ +struct smblk *sbm_nxtra; /* Reserved extra free node */ +struct smblk *sbm_list; /* Pointer to smblk memory alloc list. + * ALL smblks are strung onto this list + * except for the freelist! + */ +SBMA sbm_lowaddr; /* Lowest word-aligned address we know about.*/ + +/* If compiling with debug switch set, use special routine in place of + * sbrk so we can pretend we have a very limited area of free memory. + */ +#ifdef DBG_SIZE +#define SBM_SBRK sbm_brk +char *sbm_brk(); +#else +#define SBM_SBRK sbrk +#endif /*DBG_SIZE*/ + +/* Forward routine declarations */ +char *sbrk(); +struct smblk *sbm_nmak(), *sbm_nget(), *sbm_mget(), *sbm_split(); +struct smblk *sbm_lmak(), *sbm_err(); + +/* SBM_INIT - Initialize storage management. + * If args are zero, normal initialization is done. Otherwise, + * args are understood to be pointers to an area of memory allocated + * on the stack (eg by an "int mem[2000]" declaration in MAIN) and + * initialization will include this area in addition to the + * unused space between "_end" and the start of the stack segment. + * This is mostly of use for PDP11s which would otherwise waste a lot + * of address space. + * Maybe should have a SBM_RESET() function? + */ + +struct smblk * +sbm_init(xaddr,xlen) +SBMA xaddr; /* Address of allocated stack area if any */ +SBMO xlen; /* Size of this area */ +{ register struct smblk *sm, *sml; + register char *cp; + + /* Get initial chunk of memory from standard system rtn */ + if((cp = SBM_SBRK(SMNODES*sizeof(struct smblk))) == 0 + || (int) cp == -1) + return(sbm_err(0,"Can't sbrk")); + sm = (struct smblk *)cp; /* Better be word-aligned! */ + sbm_lmak(sm,(SBMO)sizeof(struct smblk),SMNODES); /* Make list */ + sbm_nfl = sm; /* Point freelist at it */ + sbm_lowaddr = (SBMA)sm; /* Remember lowest addr seen */ + + /* Set up 1st node pointing to all memory from here on up. + * We don't know exactly how much will be available at this point, + * so we just pretend we have the maximum possible. + */ + sbm_list = sml = sbm_nget(); + sml->smforw = sml->smback = 0; + sml->smflags = SM_USE|SM_NID; /* Initial flags */ + sml->smaddr = (SBMA) sml; + sml->smlen = MAXSBMO; /* Pretend we have lots */ + sml->smuse = (SMNODES * sizeof(struct smblk)); + + /* Now split off everything above initial allocation as NXM. */ + sm = sbm_split(sml, sm->smuse); + sml->smflags |= SM_MNODS; /* Mark 1st node as having SM nodes */ + sm->smflags |= SM_NXM; /* Mark 2nd node as NXM */ + + /* Now possibly set up extra nodes, if stack mem is being allocated + * (From our viewpoint it looks as if a chunk in the middle of + * the initial NXM section has been declared usable) + */ + if(xlen) + { /* Allow for "extra" static stack memory */ + /* Will lose if xaddr <= 1st NXM! */ + sml = sbm_split(sm, (SBMO)(xaddr - sm->smaddr)); + sbm_split(sml, xlen); /* Split off following NXM */ + sml->smflags &= ~(SM_USE|SM_NXM); /* This node is free mem! */ + } + + /* Now set up a small additional node which points to the NXM + * that we cannot get from SBRK. At this stage, this is just + * a place-holder, to reserve the node so we don't have to + * worry about running out of nodes at the same time sbrk stops + * returning memory. + * SM points to the NXM that we expect SBRK to dig into. + */ + sbm_split(sm, sm->smlen - WDSIZE); /* Chop off teensy bit */ + sm->smflags &= ~SM_USE; /* Now mark NXM "free"!! */ + + /* Finally, reserve an "extra" SM node for use by sbm_nget + * when it is allocating more freelist node chunks. + */ + sbm_nxtra = sbm_nget(); + + return(sbm_list); +} + +/* SBM_NGET() - Get a free SM node. + * Note the hair to provide a spare SM node when + * we are allocating memory for more SM nodes. This is necessary + * because sbm_mget and sbm_nget call each other recursively and + * sbm_mget cannot create any new memory without a SM node to point + * at the allocated chunk. + */ +struct smblk * +sbm_nget() +{ register struct smblk *sm, *sml; + + if(!(sm = sbm_nfl)) /* Get a node from freelist */ + { /* Freelist is empty, try to allocate more nodes. */ + + /* Put our "spare" smblk on freelist temporarily so that + * sbm_mget has a chance of winning. + * Infinite recursion is avoided by a test + * in sbm_mget which checks sbm_nfl and sbm_nxtra. + */ + if(!(sm = sbm_nxtra)) + return(sbm_err(0,"Zero sbm_nxtra!")); + sm->smforw = 0; + sbm_nfl = sm; + sbm_nxtra = 0; + + /* Try to allocate another chunk of SM nodes. */ + sml = sbm_nmak(sizeof(struct smblk),SM_MNODS); + + /* Put the new free nodes (if any) on freelist. + * Done this way because freelist may have had one or two + * nodes added to it by sbm_mget, so can't just stick + * a new pointer in sbm_nfl. + */ + while(sm = sml) + { sml = sm->smforw; + sbm_nfre(sm); + } + + /* Now reserve an extra node again. + * It is an error if there is nothing on freelist here, + * because even if sbm_mget failed the "extra node" should + * still be on freelist. The check for a zero sbm_nxtra + * above will catch such an error. + */ + sbm_nxtra = sbm_nget(); + + /* Now see if anything to return */ + if(!(sm = sbm_nfl)) /* If freelist empty again, */ + return(0); /* give up. */ + } + sbm_nfl = sm->smforw; /* If win, take it off freelist */ + return(sm); /* Return ptr or 0 if none */ +} + +/* SBM_NFRE(sm) - Return a SM node to the SM freelist. + */ +sbm_nfre(smp) +struct smblk *smp; +{ register struct smblk *sm; + (sm = smp)->smflags = 0; + sm->smforw = sbm_nfl; + sbm_nfl = sm; +} + +/* SBM_NMAK(elsize, flag) - Make (allocate & build) a typeless node freelist. + */ +struct smblk * +sbm_nmak(elsize, flag) +SBMO elsize; +unsigned flag; +{ register struct smblk *sm, *smp; + register int cnt; + + if((sm = sbm_mget(SMNODES*elsize,SMNODES*elsize)) == 0) + return(0); + + sm->smflags |= flag; /* Indicate type of nodes */ + cnt = sm->smlen/elsize; /* Find # nodes that will fit */ + sm->smuse = cnt * elsize; /* Actual size used */ + smp = (struct smblk *)(sm->smaddr); /* Ptr to 1st loc of mem */ + sbm_lmak(smp, (SBMO)elsize, cnt); /* Build freelist */ + return(smp); /* Return 1st free node. Caller is */ + /* responsible for setting freelist ptr. */ +} + +/* SBM_LMAK - Build freelist of typeless nodes. + * Note this does not allocate memory, it just converts an already + * allocated memory area. + */ +struct smblk * +sbm_lmak(addr, elsize, num) +SBMA addr; +SBMO elsize; +int num; +{ register struct smblk *sm, *smp; + register int cnt; + + smp = (struct smblk *) addr; + if((cnt = num) <= 0) + return(0); + do { sm = smp; /* Save ptr */ + sm->smforw = (smp = (struct smblk *) ((SBMA)smp + elsize)); + sm->smflags = 0; + } while(--cnt); + sm->smforw = 0; /* Last node points to nothing */ + return(sm); /* Return ptr to last node */ +} + +/* SBM_NMOV(sm1, sm2, begp, elsize) - Move a typeless node. + * Copy sm1 to sm2, adjust ptrs, leave sm1 free. + */ +sbm_nmov(smp1,smp2,begp,elsize) +struct smblk *smp1, *smp2, **begp; +int elsize; +{ register struct smblk *sm; + + bcopy((SBMA)smp1,(SBMA)(sm = smp2), elsize); /* Copy the stuff */ + if(sm->smforw) sm->smforw->smback = sm; /* Fix up links */ + if(sm->smback) sm->smback->smforw = sm; + else *begp = sm; +} + +/* SBM_MGET(min,max) - Get a SMBLK with specified amount of memory. + * Returns 0 if none available. + * Memory is guaranteed to start on word boundary, but may not + * end on one. Note that sbm_mfree is responsible for + * ensuring that free mem starts word-aligned. + * A subtle but major concern of this code is the number of freelist + * nodes gobbled by a single call. If the freelist happens to not have + * enough nodes, then a recursive call to sbm_mget is made (via sbm_nget) + * in order to allocate a new batch of freelist nodes! sbm_nget will + * always provide a single "spare" node during such an allocation, but + * there is only one and it is essential that sbm_mget gobble only ONE + * (if any) during such a call, which is indicated by sbm_nxtra==0. + * The maximum # of freelist nodes that sbm_mget can gobble is + * 2, when (1) NXM memory is obtained, and a SM is needed to point at + * the new free mem, plus (2) the resulting SM is too big, and has to + * be split up, which requires another SM for the remainder. + * The "used-NXM" smblk is set up at init time precisely in order to + * avoid the necessity of creating it here when sbrk stops winning, since + * that would require yet another freelist node and make it possible for + * sbm_mget to gobble 3 during one call -- too many. + * Further note: the sbm_nfl checks are necessary in order + * to ensure that a SM node is available for use by sbm_split. Otherwise + * the calls to sbm_split might create a new SM freelist by gobbling the + * very memory which we are hoping to return! + */ +SBMO sbm_chksiz = SMCHUNKSIZ; /* Current chunk size to feed sbrk */ + +struct smblk * +sbm_mget(cmin,cmax) +SBMO cmin,cmax; +{ register struct smblk *sm, *sml; + register SBMO csiz; + register SBMA addr, xaddr; + + if((sm = sbm_list) == 0 /* If never done, */ + && (sm = sbm_init((SBMA)0,(SBMO)0)) == 0) /* initialize mem alloc stuff. */ + return(0); /* Can't init??? */ + + /* Round up sizes to word boundary */ + if(rndrem(cmin)) cmin = rndup(cmin); + if(rndrem(cmax)) cmax = rndup(cmax); + + /* Search for a free block having enough memory. + * If run into a free-NXM block, always "win", since there may be + * a combination of preceding free-mem and new mem which will satisfy + * the request. If it turns out this didn't work, we'll just fail + * a little farther on. + */ +retry: csiz = cmin; /* Set size that will satisfy us */ + do { + if( ((sm->smflags&SM_USE) == 0) + && ((sm->smlen >= csiz) || (sm->smflags&SM_NXM)) ) + break; + } while(sm = sm->smforw); + if(sm == 0) + return(0); /* Found none that minimum would fit */ + + if(sm->smflags&SM_NXM) + { /* Found free area, but it's marked NXM and the system + * must be persuaded (via sbrk) to let us use that portion + * of our address space. Grab a good-sized chunk. + */ + if(sbm_nfl == 0) /* Verify a spare SM node is avail */ + goto getnod; /* Nope, must get one. */ + + /* Decide amount of mem to ask system for, via sbrk. + * The fine point here is the check of sbm_nxtra to make sure + * that, when building more freelist nodes, we don't have + * to use more than one SM node in the process. If we + * asked for too much mem, we'd have to use a SM node + * to hold the excess after splitting. + */ + csiz = cmax; + if(sbm_nxtra /* If normal then try for big chunk */ + && csiz < sbm_chksiz) csiz = sbm_chksiz; /* Max */ + if (csiz > sm->smlen) csiz = sm->smlen; /* Min */ + + /* Get the NXM mem */ + if((addr = (SBMA)SBM_SBRK(csiz)) != sm->smaddr) + { /* Unexpected value returned from SBRK! */ + + if((int)addr != 0 && (int)addr != -1) + { return(sbm_err(0,"SBRK %o != %o", addr, + sm->smaddr)); +#if 0 + /* If value indicates couldn't get the stuff, then + * we have probably hit our limit and the rest of + * NXM should be declared "used" to prevent further + * hopeless sbrk calls. We split off the portion + * of NXM that is known for sure to be unavailable, + * and mark it "used". If a "used NXM" area already + * exists following this one, the two are merged. + * The chunk size is then reduced by half, so + * only log2(SMCHUNKSIZ) attempts will be made, and + * we try again. + */ + /* If returned some mem which starts outside + * the NXM then something is screwed up. */ + if(addr < sm->smaddr + || (addr >= sm->smaddr+sm->smlen)) + return(sbm_err(0,"SBRK %o != %o", + addr, sm->smaddr)); + /* Got some mem, falls within NXM. + * Presumably someone else has called sbrk + * since last time, so we need to fence off + * the intervening area. */ + sm = sbm_split((sml=sm),(addr - sm->smaddr)); + sml->smflags |= SM_USE|SM_EXT; + return(sbm_mget(cmin,cmax)); +#endif /*COMMENT*/ + } + + /* Handle case of SBRK claiming no more memory. + * Gobble as much as we can, and then turn this NXM + * block into a free-mem block, and leave the + * remainder in the used-NXM block (which should + * immediately follow this free-NXM block!) + */ + if(!(sml = sm->smforw) /* Ensure have used-NXM blk */ + || (sml->smflags&(SM_USE|SM_NXM)) + != (SM_USE|SM_NXM)) + return(sbm_err(0,"No uNXM node!")); + xaddr = sm->smaddr; /* Use this for checking */ + sm->smuse = 0; /* Use this for sum */ + for(csiz = sm->smlen; csiz > 0;) + { addr = SBM_SBRK(csiz); + if((int)addr == 0 || (int)addr == -1) + { csiz >>= 1; + continue; + } + if(addr != xaddr) + return(sbm_err(0,"SBRK %o != %o", addr, + xaddr)); + sm->smuse += csiz; + xaddr += csiz; + } + + /* Have gobbled as much from SBRK as we could. + * Turn the free-NXM block into a free-mem block, + * unless we got nothing, in which case just merge + * it into the used-NXM block and continue + * searching from this point. + */ + if(!(csiz = sm->smuse)) /* Get total added */ + { sm->smflags = sml->smflags; /* Ugh. */ + sbm_mmrg(sm); + goto retry; /* Keep looking */ + } + else + { sml->smaddr = sm->smaddr + csiz; + sml->smlen += sm->smlen - csiz; + sm->smlen = csiz; + sm->smflags &= ~SM_NXM; /* No longer NXM */ + } + } + + /* Here when we've acquired CSIZ more memory from sbrk. + * If preceding mem area is not in use, merge new mem + * into it. + */ + if((sml = sm->smback) && + (sml->smflags&(SM_USE|SM_NXM))==0) /* Previous free? */ + { sml->smlen += csiz; /* Yes, simple! */ + sm->smaddr += csiz; /* Fix up */ + if((sm->smlen -= csiz) == 0) /* If no NXM left,*/ + sbm_mmrg(sml); /* Merge NXM node w/prev */ + sm = sml; /* Prev is now winning node */ + } + else + { /* Prev node isn't a free area. Split up the NXM + * node to account for acquired mem, unless we + * gobbled all the mem available. + */ + if(sm->smlen > csiz /* Split unless all used */ + && !sbm_split(sm,csiz)) /* Call shd always win */ + return(sbm_err(0,"getsplit err: %o",sm)); + sm->smflags &= ~SM_NXM; /* Node is now real mem */ + } + + /* Now make a final check that we have enough memory. + * This can fail because SBRK may not have been able + * to gobble enough memory, either because (1) not + * as much NXM was available as we thought, + * or (2) we noticed the free-NXM area and immediately + * gambled on trying it without checking any lengths. + * In any case, we try again starting from the current SM + * because there may be more free mem higher up (eg on + * stack). + */ + if(sm->smlen < cmin) + goto retry; + } + + /* Check to see if node has too much mem. This is especially true + * for memory just acquired via sbrk, which gobbles a huge chunk each + * time. If there's too much, we split up the area. + */ + if(sm->smlen > cmax+FUDGE) /* Got too much? (Allow some fudge)*/ + /* Yes, split up so don't gobble too much. */ + if(sbm_nfl) /* If success guaranteed, */ + sbm_split(sm,cmax); /* split it, all's well. */ + else goto getnod; + + sm->smuse = 0; + sm->smflags |= SM_USE; /* Finally seize it by marking "in-use". */ + return(sm); + + /* Come here when we will need to get another SM node but the + * SM freelist is empty. We have to forget about using the area + * we just found, because sbm_nget may gobble it for the + * freelist. So, we first force a refill of the freelist, and then + * invoke ourselves again on what's left. + */ +getnod: + if(sml = sbm_nget()) /* Try to build freelist */ + { sbm_nfre(sml); /* Won, give node back, */ + sm = sbm_list; /* and retry, starting over! */ + goto retry; + } + /* Failed. Not enough memory for both this request + * and one more block of SM nodes. Since such a SM_MNODS + * block isn't very big, we are so close to the limits that it + * isn't worth trying to do something fancy here to satisfy the + * original request. So we just fail. + */ + return(0); +} + +#ifdef DBG_SIZE +/* Code for debugging stuff by imposing an artificial limitation on size + * of available memory. + */ +SBMO sbm_dlim = MAXSBMO; /* Amount of mem to allow (default is max) */ + +char * +sbm_brk(size) +unsigned size; +{ register char *addr; + + if(size > sbm_dlim) return(0); + addr = sbrk(size); + if((int)addr == 0 || (int)addr == -1) + return(0); + sbm_dlim -= size; + return(addr); +} +#endif /*DBG_SIZE*/ + +/* SBM_MFREE(sm) - Free up an allocated memory area. + */ +sbm_mfree(sm) +register struct smblk *sm; +{ register struct smblk *smx; + register SBMO crem; + + sm->smflags &= ~SM_USE; /* Say mem is free */ + if((smx = sm->smback) /* Check preceding mem */ + && (smx->smflags&(SM_USE|SM_NXM))==0) /* If it's free, */ + sbm_mmrg(sm = smx); /* then merge 'em. */ + if((smx = sm->smforw) /* Check following mem */ + && (smx->smflags&(SM_USE|SM_NXM))==0) /* Again, if free, */ + sbm_mmrg(sm); /* merge them. */ + + if(sm->smlen == 0) /* Just in case, chk for null blk */ + { if(smx = sm->smback) /* If pred exists, */ + sbm_mmrg(smx); /* merge quietly. */ + else { + sbm_list = sm->smforw; /* 1st node on list, so */ + sbm_nfre(sm); /* simply flush it. */ + } + return; + } + + /* This code is slightly over-general for some machines. + * The pointer subtraction is done in order to get a valid integer + * offset value regardless of the internal representation of a pointer. + * We cannot reliably force alignment via casts; some C implementations + * treat that as a no-op. + */ + if(crem = rndrem(sm->smaddr - sbm_lowaddr)) /* On word bndry? */ + { /* No -- must adjust. All free mem blks MUST, by fiat, + * start on word boundary. Here we fix things by + * making the leftover bytes belong to the previous blk, + * no matter what it is used for. Prev blk is guaranteed to + * (1) Exist (this cannot be 1st blk since 1st is known to + * start on wd boundary) and to be (2) Non-free (else it would + * have been merged). + */ + if((smx = sm->smback) == 0) /* Get ptr to prev blk */ + { sbm_err(0,"Align err"); /* Catch screws */ + return; + } + crem = WDSIZE - crem; /* Find # bytes to flush */ + if(crem >= sm->smlen) /* Make sure node has that many */ + { sbm_mmrg(smx); /* Flush node to avoid zero length */ + return; + } + smx->smlen += crem; /* Make stray bytes part of prev */ + sm->smaddr += crem; /* And flush from current. */ + sm->smlen -= crem; + } +} + +/* SBM_EXP - Expand (or shrink) size of an allocated memory chunk. + * "nsize" is desired new size; may be larger or smaller than current + * size. + */ +struct smblk * +sbm_exp(sm,size) +register struct smblk *sm; +register SBMO size; +{ register struct smblk *smf; + register SBMO mexp, pred, succ; + + if(sm->smlen >= size) /* Do we want truncation? */ + goto realo2; /* Yup, go split block */ + + /* Block is expanding. */ + mexp = size - sm->smlen; /* Get # bytes to expand by */ + pred = succ = 0; + if((smf = sm->smforw) /* See if free mem follows */ + && (smf->smflags&(SM_USE|SM_NXM)) == 0) + if((succ = smf->smlen) >= mexp) + goto realo1; /* Quick stuff if succ OK */ + + if((smf = sm->smback) /* See if free mem precedes */ + && (smf->smflags&(SM_USE|SM_NXM)) == 0) + pred = smf->smlen; + + /* If not enough free space combined on both sides of this chunk, + * we have to look for a completely new block. + */ + if(pred+succ < mexp) + { if((smf = sbm_mget(size,size)) == 0) + return(0); /* Couldn't find one */ + else pred = 0; /* Won, indicate new block */ + } + + /* OK, must copy either into new block or down into predecessor + * (overlap is OK as long as bcopy moves 1st byte first) + */ + bcopy(sm->smaddr, smf->smaddr, sm->smlen); + smf->smflags = sm->smflags; /* Copy extra attribs */ + smf->smuse = sm->smuse; + if(!pred) /* If invoked sbm_mget */ + { sbm_mfree(sm); /* then must free up old area */ + return(smf); /* and can return immediately. */ + } + sbm_mmrg(smf); /* Merge current into pred blk */ + sm = smf; /* Now pred is current blk. */ + + if(succ) +realo1: sbm_mmrg(sm); /* Merge succ into current blk */ +realo2: if(sm->smlen > size /* If now have too much, */ + && sbm_split(sm, size)) /* split up and possibly */ + sbm_mfree(sm->smforw); /* free up unused space. */ + return(sm); + + /* Note that sbm_split can fail if it can't get a free node, + * which is only possible if we are reducing the size of an area. + * If it fails, we just return anyway without truncating the area. + */ +} + +/* SBM_MMRG(sm) - Merge a memory area with the area following it. + * The node (and memory area) following the SM pointed to are + * merged in and the successor node freed up. The flags + * and smuse of the current SM (which is not moved or anything) + * remain the same. + */ +sbm_mmrg(smp) +struct smblk *smp; +{ register struct smblk *sm, *sm2; + + sm = smp; + sm->smlen += (sm2 = sm->smforw)->smlen; /* Add succ's len */ + if(sm->smforw = sm2->smforw) /* and fix linkages */ + sm->smforw->smback = sm; + sbm_nfre(sm2); /* now can flush succ node */ +} + +/* SBM_SPLIT - Split up an area (gets a new smblk to point to split-off + * portion.) + * Note returned value is ptr to 2nd smblk, since this is a new one. + * Ptr to 1st remains valid since original smblk stays where it is. + * NOTE: Beware of splitting up free mem (SM_USE == 0) since sbm_nget may + * steal it out from under unless precautions are taken! See comments + * at sbm_mget related to this. + */ +struct smblk * +sbm_split(smp,coff) +struct smblk *smp; +SBMO coff; +{ register struct smblk *sm, *smx; + register SBMO csiz; + + if((sm = smp)->smlen <= (csiz = coff)) + return(0); + if((smx = sbm_nget()) == 0) + return(0); + smx->smlen = sm->smlen - csiz; /* Set 2nd size */ + smx->smaddr = sm->smaddr + csiz; /* Set 2nd addr */ + sm->smlen = csiz; /* Take from 1st size */ + smx->smflags = sm->smflags; /* Copy flags */ + if(smx->smforw = sm->smforw) /* Splice 2nd after 1 */ + smx->smforw->smback = smx; + smx->smback = sm; + sm->smforw = smx; /* Put 2nd into chain */ + return(smx); /* Return ptr to 2nd smblk */ +} + +#if 0 /* Replaced by "bcopy" for system-dep efficiency */ +/* SBM_SCPY - Copy string of bytes. Somewhat machine-dependent; + * Tries to be clever about using word moves instead of byte moves. + */ +sbm_scpy(from, to, count) /* Copy count bytes from -> to */ +char *from, *to; +unsigned count; +{ register char *s1, *s2; + register unsigned cnt; + int tmp; + + if((cnt = count) == 0) + return; + s1 = from; + s2 = to; + while(rndrem((int)s1)) /* Get 1st ptr aligned */ + { *s2++ = *s1++; + if(--cnt == 0) return; + } + if(rndrem((int)s2) == 0) /* Do wd move if ptr 2 now aligned */ + { +#ifdef DUMBPCC /* Code for dumber (Portable C type) compiler */ + register WORD *ap, *bp; + tmp = cnt; + ap = (WORD *) s1; + bp = (WORD *) s2; + if(cnt = rnddiv(cnt)) + do { *bp++ = *ap++; } + while(--cnt); + if ((cnt = rndrem(tmp)) ==0) + return; + s1 = (char *) ap; + s2 = (char *) bp; +#else + /* Tight loop for efficient copying on 11s */ + tmp = cnt; + if(cnt = rnddiv(cnt)) + do { *((WORD *)s2)++ = *((WORD *)s1)++; } + while(--cnt); + if((cnt = rndrem(tmp)) == 0) + return; +#endif /*-DUMBPCC*/ + } + do { *s2++ = *s1++; } /* Finish up with byte loop */ + while(--cnt); +} +#endif /*COMMENT*/ + +struct smblk * /* If it returns at all, this is most common type */ +sbm_err(val,str,a0,a1,a2,a3) +char *str; +struct smblk *val; +{ int *sptr; + + sptr = (int *) &sptr; /* Point to self on stack */ + sptr += 5; /* Point to return addr */ + if((int)sbm_debug==1) + abort(); + if(sbm_debug) + (*sbm_debug)(0,*sptr,str,a0,a1,a2,a3); + return(val); +} + +/* These routines correspond to the V7 LIBC routines as described + * in the V7 UPM (3). They should provide satisfactory emulation + * if the documentation is correct. Replacement is necessary since + * the SBM routines are jealous and cannot tolerate competition for + * calls of SBRK; i.e. the memory being managed must be contiguous. + */ + +/* Guaranteed to return word-aligned pointer to area of AT LEAST + * requested size. Area size is rounded up to word boundary. + */ + +char * +malloc(size) +unsigned size; +{ register struct smblk *sm, **sma; + register SBMO siz; + + siz = rndup(size + sizeof (struct smblk *)); /* Make room for ptr */ + if((sm = sbm_mget(siz,siz)) == 0) + return(0); + *(sma = (struct smblk **)sm->smaddr) = sm; /* Store ptr in addr-1 */ + return((char *)++sma); +} + +char * +alloc(size) /* For V6 programs - note different failure value! */ +unsigned size; +{ register char *addr; + return((addr = malloc(size)) ? addr : (char *) -1); +} + +free(ptr) +char *ptr; +{ register struct smblk *sm, **smp; + + smp = &((struct smblk **)ptr)[-1]; /* Point to addr-1 */ + sm = *smp; /* Pluck SM ptr therefrom */ + if(((sm->smflags&0377) != SM_NID) || sm->smaddr != (SBMA)smp) + return((int)sbm_err(0,"free: bad arg %o", ptr)); + sbm_mfree(sm); + return(1); +} + +char * +realloc(ptr,size) +char *ptr; +unsigned size; +{ register struct smblk *sm, **smp; + + smp = &((struct smblk **)ptr)[-1]; /* Point to addr-1 */ + sm = *smp; /* Pluck SM ptr therefrom */ + if(((sm->smflags&0377) != SM_NID) || (sm->smaddr != (SBMA)smp)) + return((char *)sbm_err(0,"realloc: bad arg %o",ptr)); + if((sm = sbm_exp(sm, rndup(size+(sizeof(struct smblk *))))) == 0) + return(0); + *(smp = (struct smblk **)sm->smaddr) = sm; /* Save smblk ptr */ + return((char *)++smp); +} + +char * +calloc(nelem,elsize) +unsigned nelem, elsize; +{ register SBMO cmin; + register WORD *ip; /* Clear in units of words */ + register char *addr; + + if((cmin = nelem*elsize) == 0 /* Find # bytes to get */ + || (addr = malloc(cmin)) == 0) /* Get it */ + return(0); + ip = (WORD *) addr; /* Set up ptr to area */ + cmin = rnddiv(cmin+WDSIZE-1); /* Find # words to clear */ + do { *ip++ = 0; } while (--cmin); /* Zap the area */ + return(addr); +} + +/* SBM_NGC() - Specific routine for GC'ing SMBLK nodes. + * + * SBM_XNGC(begp, elsize, type) - Compact nodes of specified type. + * Scans allocated mem from low to high to find chunks with nodes of + * the specified type. + * Flushes current freelist and rebuilds it as scan progresses, + * such that 1st thing on list is lowest-addr node. When a node is + * seen that can be moved, new node is acquired from freelist if + * it exists, otherwise no move is made. If a chunk has been scanned + * and no active nodes remain, it is flushed and freelist updated. + * NOTE: This has not yet been verified to work with nodes of any + * type other than SMBLK. + */ + +sbm_ngc() +{ register struct smblk *sm; + if(!(sm = sbm_nxtra)) + return((int)sbm_err(0,"Zero sbm_nxtra")); + sm->smflags |= SM_USE; /* Ensure this one isn't GC'd */ + sbm_xngc(&sbm_nfl, sizeof(struct smblk), SM_MNODS); + sm->smflags = 0; /* Flush temporary crock */ +} +sbm_xngc(begp, elsize, flag) +struct smblk **begp; +unsigned elsize, flag; +{ register struct smblk *sm, *chk, *smf; + struct smblk *ftail, *savtail; + int cnt, inuse; + + *begp = ftail = 0; /* Flush node freelist */ + for(chk = sbm_list; chk; chk = chk->smforw) + if(chk->smflags&flag) + { sm = (struct smblk *) chk->smaddr; + cnt = (chk->smuse)/elsize; + savtail = ftail; + inuse = 0; + smf = *begp; + /* Set up ptr to 1st freelist node */ + while(--cnt >= 0) + { /* Here decide if movable */ + if(sm->smflags && smf /* Live and have copy place */ + && ( + (sm->smflags&SM_USE) == 0 /* Free mem? */ + || (sm->smflags&(SM_MNODS|SM_DNODS)) + ) + && sm->smback) /* has backptr (see ncpy) */ + { /* Move the node */ + *begp = smf->smforw; /* Get free node */ + if(smf == ftail) + ftail = 0; + if(smf == savtail) + savtail = 0; + /* Move node. Already checked for back ptr + * of 0 since no obvious way to tell where + * the ptr to list is kept. Sigh. + */ + sbm_nmov(sm,smf,(struct smblk **)0,elsize); + /* Get ptr to new freelist node. Note + * check to ensure that it is not in this + * same chunk (if it is, no point in moving + * any nodes!) + */ + if((smf = *begp) >= chk) + smf = 0; /* Zero if same chk */ + sm->smflags = 0; /* Make node free */ + } + /* At this point, not movable */ + if(sm->smflags == 0) /* Free node? */ + { if(ftail) /* Add to freelist */ + ftail->smforw = sm; + ftail = sm; + if(*begp == 0) + *begp = sm; + sm->smforw = 0; + } + else inuse++; + sm = (struct smblk *)((SBMA)sm + elsize); + } + if(inuse == 0 /* All free? */ + && (sm = chk->smback)) /* & not 1st? */ + { if(savtail) /* Edit freelist */ + (ftail = savtail)->smforw = 0; + else *begp = ftail = 0; + sbm_mfree(chk); + chk = sm; + } + } +} + +/* + * Note that proc must return a zero value, or loop aborts and + * returns that selfsame value. + */ +sbm_nfor(flag,nodsiz,proc,arg) +int flag; +int (*proc)(); +int nodsiz; +struct sbfile *arg; +{ register struct smblk *sm, *np; + register int cnt; + int res; + + for(sm = sbm_list; sm; sm = sm->smforw) + if(sm->smflags&flag) + { np = (struct smblk *) sm->smaddr; + cnt = sm->smuse/nodsiz; + do { + if(np->smflags) + if(res = (*proc)(np,arg)) + return(res); + np = (struct smblk *)((SBMA)np + nodsiz); + } while(--cnt); + } + return(0); +} diff --git a/commands/elle/sbproto.h b/commands/elle/sbproto.h new file mode 100755 index 000000000..911a9e033 --- /dev/null +++ b/commands/elle/sbproto.h @@ -0,0 +1,120 @@ +#ifndef _ANSI +#include +#endif + +/* sbbcpy.c */ +_PROTOTYPE( int bcopy, (SBMA from, SBMA to, unsigned cnt) ); +_PROTOTYPE( int sbm_wcpy, (int *from, int *to, unsigned cnt) ); + +/* sberr.c */ +_PROTOTYPE( int sbe_mem, (void) ); +_PROTOTYPE( char *sbe_mvfy, (void) ); +_PROTOTYPE( char *sbe_mfl, (int p) ); +_PROTOTYPE( char *sbe_mlst, (int p) ); +_PROTOTYPE( int sbe_smp, (struct smblk *sm, int type) ); +_PROTOTYPE( char *sbe_sfl, (int p) ); +_PROTOTYPE( int sbe_sds, (void) ); +_PROTOTYPE( int sbe_psd, (struct sdblk *sd) ); +_PROTOTYPE( char *sbe_svfy, (void) ); +_PROTOTYPE( char *sbe_sdlist, (int p, int phys) ); +_PROTOTYPE( int sbe_dsk, (SBFILE *sfp) ); +_PROTOTYPE( char *sbe_sbvfy, (SBBUF *sbp) ); +_PROTOTYPE( char *sbe_sbs, (SBBUF *sbp, int p) ); + +/* sbm.c */ +_PROTOTYPE( struct smblk *sbm_init, (SBMA xaddr, SBMO xlen) ); +_PROTOTYPE( struct smblk *sbm_nget, (void) ); +_PROTOTYPE( int sbm_nfre, (struct smblk *smp) ); +_PROTOTYPE( struct smblk *sbm_nmak, (SBMO elsize, unsigned flag) ); +_PROTOTYPE( struct smblk *sbm_lmak, (SBMA addr, SBMO elsize, int num) ); +_PROTOTYPE( int sbm_nmov, (struct smblk *smp1, struct smblk *smp2, struct smblk **begp, int elsize) ); +_PROTOTYPE( struct smblk *sbm_mget, (SBMO cmin, SBMO cmax) ); +_PROTOTYPE( char *sbm_brk, (unsigned size) ); +_PROTOTYPE( int sbm_mfree, (struct smblk *sm) ); +_PROTOTYPE( struct smblk *sbm_exp, (struct smblk *sm, SBMO size) ); +_PROTOTYPE( int sbm_mmrg, (struct smblk *smp) ); +_PROTOTYPE( struct smblk *sbm_split, (struct smblk *smp, SBMO coff) ); +_PROTOTYPE( int sbm_scpy, (char *from, char *to, unsigned count) ); +#if 0 +_PROTOTYPE( struct smblk *sbm_err, (struct smblk *val, char *str, int a0, int a1, int a2, int a3) ); +#else +struct smblk *sbm_err(); +#endif +_PROTOTYPE( char *malloc, (unsigned size) ); +_PROTOTYPE( char *alloc, (unsigned size) ); +_PROTOTYPE( int free, (char *ptr) ); +_PROTOTYPE( char *realloc, (char *ptr, unsigned size) ); +_PROTOTYPE( char *calloc, (unsigned nelem, unsigned elsize) ); +_PROTOTYPE( int sbm_ngc, (void) ); +_PROTOTYPE( int sbm_xngc, (struct smblk **begp, unsigned elsize, unsigned flag) ); +_PROTOTYPE( int sbm_nfor, (int flag, int nodsiz, int (*proc )(), struct sbfile *arg) ); + +/* sbstr.c */ +_PROTOTYPE( SBSTR *sb_close, (SBBUF *sbp) ); +_PROTOTYPE( int sb_setovw, (SBBUF *sbp) ); +_PROTOTYPE( int sb_clrovw, (SBBUF *sbp) ); +_PROTOTYPE( chroff sbx_fdlen, (int fd) ); +_PROTOTYPE( SBSTR *sb_fduse, (int ifd) ); +_PROTOTYPE( int sb_fdcls, (int ifd) ); +_PROTOTYPE( int sbx_fcls, (struct sbfile *sfp) ); +_PROTOTYPE( int sb_fdinp, (SBBUF *sb, int fd) ); +_PROTOTYPE( int sb_fsave, (SBBUF *sb, int fd) ); +_PROTOTYPE( int sb_sgetc, (SBBUF *sb) ); +_PROTOTYPE( int sb_sputc, (SBBUF *sb, int ch) ); +_PROTOTYPE( int sb_speekc, (SBBUF *sb) ); +_PROTOTYPE( int sb_rgetc, (SBBUF *sb) ); +_PROTOTYPE( int sb_rdelc, (SBBUF *sbp) ); +_PROTOTYPE( int sb_deln, (SBBUF *sbp, chroff num) ); +_PROTOTYPE( struct sdblk *sb_killn, (SBBUF *sbp, chroff num) ); +_PROTOTYPE( SBSTR *sb_cpyn, (SBBUF *sbp, chroff num) ); +_PROTOTYPE( int sb_sins, (SBBUF *sbp, struct sdblk *sdp) ); +_PROTOTYPE( SBSTR *sbs_cpy, (SBSTR *sdp) ); +_PROTOTYPE( int sbs_del, (SBSTR *sdp) ); +_PROTOTYPE( SBSTR *sbs_app, (struct sdblk *sdp, struct sdblk *sdp2) ); +_PROTOTYPE( chroff sbs_len, (SBSTR *sdp) ); +_PROTOTYPE( int sb_seek, (SBBUF *sbp, chroff coff, int flg) ); +_PROTOTYPE( int sb_rewind, (SBBUF *sbp) ); +_PROTOTYPE( chroff sb_tell, (SBBUF *sbp) ); +_PROTOTYPE( chroff sb_ztell, (SBBUF *sbp) ); +#if 0 +_PROTOTYPE( struct sdblk *sbx_ready, (SBBUF *sbp, int type, SBMO cmin, SBMO cmax) ); +#else +struct sdblk *sbx_ready(); +#endif +_PROTOTYPE( struct sdblk *sbx_next, (SBBUF *sbp) ); +_PROTOTYPE( struct sdblk *sbx_norm, (SBBUF *sbp, int mode) ); +_PROTOTYPE( struct sdblk *sbx_beg, (struct sdblk *sdp) ); +_PROTOTYPE( int sbx_smdisc, (SBBUF *sbp) ); +_PROTOTYPE( int sbx_sbrdy, (SBBUF *sbp) ); +_PROTOTYPE( struct sdblk *sbx_scpy, (struct sdblk *sdp, struct sdblk *sdlast) ); +_PROTOTYPE( struct sdblk *sbx_sdcpy, (struct sdblk *sdp) ); +_PROTOTYPE( struct sdblk *sbx_xcis, (SBBUF *sbp, chroff num, struct sdblk **asd2, chroff *adot) ); +_PROTOTYPE( struct sdblk *sbx_split, (struct sdblk *sdp, chroff coff) ); +_PROTOTYPE( struct smblk *sbx_msplit, (struct smblk *smp, SBMO size) ); +_PROTOTYPE( struct sdblk *sbx_ndel, (struct sdblk *sdp) ); +_PROTOTYPE( int sbx_npdel, (struct sdblk *sdp) ); +_PROTOTYPE( struct sdblk *sbx_ndget, (void) ); +_PROTOTYPE( int sbx_ndfre, (struct sdblk *sdp) ); +_PROTOTYPE( SBMA sbx_malloc, (unsigned size) ); +_PROTOTYPE( struct smblk *sbx_mget, (SBMO cmin, SBMO cmax) ); +_PROTOTYPE( int sbx_comp, (int cmin, int lev) ); +_PROTOTYPE( int sbx_sdgc, (struct sdblk *sdp, int lev) ); +#if 0 +_PROTOTYPE( int sbx_aout, (struct sdblk *sdp, int flag, int fd) ); +#else +int sbx_aout(); +#endif +_PROTOTYPE( chroff sbx_qlen, (struct sdblk *sdp) ); +_PROTOTYPE( int sbx_tset, (chroff loff, int align) ); +_PROTOTYPE( struct sdblk *sbx_ffnd, (SBFILE *sfp, chroff size, chroff *aloc) ); +_PROTOTYPE( int sbx_rdf, (int fd, char *addr, int cnt, int skflg, chroff loc) ); +_PROTOTYPE( int sbx_rugpull, (int fd) ); +_PROTOTYPE( int sbx_unpur, (struct sdblk *sd, struct sbfile *sf) ); +#if 0 +_PROTOTYPE( int sbx_err, (int val, char *str, int a0, int a1, int a2, int a3, int a4, int a5, int a6, int a7, int a8, int a9, int a10, int a11, int a12) ); +#else +int sbx_err(); +#endif + +/* sbvall.c */ +_PROTOTYPE( char *valloc, (unsigned size) ); diff --git a/commands/elle/sbstr.c b/commands/elle/sbstr.c new file mode 100755 index 000000000..a0bc95dec --- /dev/null +++ b/commands/elle/sbstr.c @@ -0,0 +1,2195 @@ +/* SB - Copyright 1982 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. In all cases + * the source code and any modifications thereto must remain + * available to any user. + * + * This is part of the SB library package. + * Any software using the SB library must likewise be made + * quasi-public, with freely available sources. + */ + +#if 0 +Todo stuff: + New definitions: + sbbuffer - old sbstr. Abbrev & struct "sbbuff". Macro SBBUFF + (or SBBUF?) + sbstring - list of sds. Abbrev sbstr. Macro SBSTR. + Should *sbstr == *sdblk? Yeah. + sbfile - as before. Macro SBFILE. (or SBFIL?) + + Try to get zero-length sdblks flushed on the fly, + rather than waiting for moby GC. Also, need to set + up compaction of SD freelist, as well as SM freelist. + Make SM freelist compact self-invoked by SBM_MGET? + Any need for phys disk ptrs other than for tempfile? + Can do sbm_forn through SDblks to find active sdfiles + so list isn''t needed for that. + Can sdback be flushed? (not needed for keeping list sorted, + or for searching it -- only used when linking + blocks in or out of list.) Perhaps use circular list? + If list only used for tmpfile, then to link in/out could + always start from sfptr1 of tmpfile? Sure, but slow? + Last SD on phys list could belong to no logical list, + and denote free space on tmpfile? + + -------------------------- + + An "open" SBBUFFER will allow one to read, write, insert into, +and delete from a sbstring (a logical character string). "Dot" refers +to the current logical character position, which is where all +operations must happen; sb_fseek must be used to change this location. +There are several states that the I/O can be in: +!SBCUR ----CLOSED---- + All other elements, including SBIOP, should also be 0. + Dot is 0. +SBCUR && !SBIOP ----OPEN/IDLE---- + SBCUR points to a SD block (its SDMEM may or may not exist) + SBIOP==0 (otherwise it would be open/ready) + Dot is SBDOT + SBOFF. + R/Wleft must be 0. +SBCUR && SBIOP ----OPEN/READY---- + SBCUR points to a SDBLK (SDMEM must exist!) + SBIOP exists. + Dot is SBDOT + offset into SMBLK. SBOFF is ignored! + SB_WRIT flag is set if "smuse" must be updated. + The R/Wleft counts are set up: + 1. Rleft 0, Wleft 0 -- Since SBIOP is set, must assume + counts are too. + So this means at end of text, no room left. + Otherwise would imply that setup needs doing. + 2. Rleft N, Wleft 0 -- At beg or middle of text + 3. Rleft 0, Wleft N -- At end of text + 4. Rleft N, Wleft N -- Shouldn''t ever happen + + Note that Rleft is always correct. Wleft is sometimes + set 0 in order to force a call to determine real state. + +Note that SBIOP alone is a sufficient test for being OPEN/READY. + +The important thing about updating the smblk is to ensure that the "smuse" +field is correct. This can only be changed by writing or deleting. We assume +that deletions always update immediately, thus to determine if an update +is necessary, see if SB_WRIT is set. If so, update smuse before doing +anything but more writing!!!! + +The SDBLK must be marked "modified" whenever a write operation is +done. We try to do this only the first time, by keeping Wleft zero +until after the first write. This is also when SB_WRIT gets set. +However, if in overwrite mode, Wleft must be kept zero in order to +force the proper actions; SB_WRIT is also not turned on since smuse +will not change. Note that at EOF, overwrite becomes the same thing +as insert and is treated identically... + + If a SBLK has an in-core copy but no disk copy, it can be +freely modified. Otherwise, modifications should preferably split +the block so as to retain "pure" blocks as long as possible. "Pure" blocks +can always have their in-core versions flushed immediately (unless for +compaction purposes they''ll need to be written out in the same GC pass). +Alternatively, mods can simply mark the disk copy "free" and go +ahead as if no such copy existed. + No additions or changes to a pure block are allowed, but +deletions from the end or beginning are always allowed. All other +changes must split or insert new blocks to accomplish the changes. + +Locking: + SDBLKs are subject to unpredictable relocation, compaction, +and garbage collecting. There are three ways in which a SDBLK can +remain fixed: + + 1. The SDBLK has the SD_LOCK flag set. This flag is used whenever + a SBBUF''s SBCUR is pointing to this SDBLK. + 2. The SDBLK has the SD_LCK2 flag set. This flag is used only + during execution of various internal routines and should + not be seen anywhere during execution of user code. + 3. The SDBLK has no back-pointer (is first block in a sbstring). + Such SDBLKs cannot be relocated (since it is not known + what may be pointing to them) but unlike the other 2 cases + they are still subject to compaction with succeeding SDBLKs. + +The SDBLK must be locked with SD_LOCK for as long as it is being +pointed to by SBCUR. The sole exception is when a SBBUF in the +OPEN/IDLE state is pointing to the first SDBLK of a sbstring; this +sdblk is guaranteed not to be moved, since sdblks without a +back-pointer are never moved. SD_LOCK is asserted as soon as the state +changes to OPEN/READY, of course. The internal routines take pains to +always move SD_LOCK as appropriate. Note that only one SD in a +sbstring can ever have SD_LOCK turned on. SD_LCK2 is an auxiliary flag +which may appear in more than one SDBLK, for use by low-level routines +for various temporary reasons; either will prevent the SDBLK from being +modified in any way by the storage compactor. + +SEEKs are a problem because it''s unclear at seek time what will happen +next, so the excision of the smblk can''t be optimized. If the seek +happens to land in a sdblk with an existing smblk, there''s no problem; +but if it''s a sdblk alone, how to decide which part of it to read in??? +If next action is: + write - split up sdblk and create new one. Read nothing in. + read - read in 512 bytes starting at disk blk boundary if possible + else read in 128 bytes starting with selected char + (include beg of sdblk if less than 64 chars away) + overwrite - as for read. + backread - like read but position at end of sdblk. + delete - split up sdblk, read nothing in. + +We solve this through the OPEN/IDLE state, where SBIOP == 0 means SBOFF +points to logical offset from start of current sdblk, so that the seek +need not take any action. Only when a specific operation is requested +will the transition to OPEN/READY take place, at which time we''ll know +what the optimal excision strategy is. The routine SBX_READY performs +this function. + +The physical links (SDFORW and SDBACK) are only valid when SDFILE is +set (likewise for SDLEN and SDADDR). In other words, mungs to a sdblk +must check SDFILE to see whether or not the phys links should be +altered. Normally they aren''t except during sdblk creation, deletion, +or swapout, no matter how much the sdblk gets shuffled around +logically. The disk physical list is kept sorted in order of starting +addresses. The text blocks indicated can overlap. When a GC is +necessary, the code must figure out how much space is actually free. + +-------------- Old woolgathering, ignore rest of this page --------------- + +Question: should 512-byte buffers be maintained, one for each SBFILE? +Or should the in-core text be hacked up to serve for buffering? +Question is where to point the READ/WRITE system calls. Currently, +they are pointed directly at the in-core text, and there are no +auxiliary buffers. + +If use auxiliary buffers: + How to handle flushing, when changing location etc? + Could be clever about reading from large disk block, only + get part of it into buffer instead of splitting up in order to + read a "whole" block. + Problem: sbstrings can include pieces of several different files. + Hard to maintain just one buffer per FD without hacking + done on one sbstring screwing that on another. +If don''t use buffers: + Need to have a "chars-left" field in mem blocks, so know how + much more can be added. Will need heuristics for how much + extra space to allocate. +#endif /*COMMENT*/ + +/* Includes, initial definitions */ + +#include +#include "sb.h" + +#ifndef V6 +#define V6 0 +#endif + +#if V6 +#include +#else +#include +#include +#if MINIX +#include /* For open() flags */ +#else +#include /* For open() flags */ +#endif /* MINIX */ +#endif /*-V6*/ + +extern int errno; +extern char *strerror(); /* From ANSI */ + +/* Allocation decls */ +SBFILE sbv_tf; /* SBFILE for temp swapout file */ +int (*sbv_debug)(); /* Error handler address */ + + +/* SBX_READY argument flags (internal to SBSTR routines only) + * The following values should all be unique; the exact value + * doesn't matter as long as the right SKM flags are given. + */ +#define SK_READF 0 /* 0-skip fwd, align BOB */ +#define SK_READB (0|SKM_0BACK|SKM_EOB) /* 0-skip bkwd, align EOB */ +#define SK_WRITEF (0|SKM_EOB) /* 0-skip fwd, align EOB */ +#define SK_DELF (4|SKM_0BACK) /* 0-skip bkwd, align BOB */ +#define SK_DELB (4|SKM_EOB) /* 0-skip fwd, align EOB */ +#define SKM_0BACK 01 /* Zero-skip direction: 0 = fwd, set = backwd + * Don't ever change this value! See SBX_NORM. */ +#define SKM_EOB 02 /* Alignment: 0 = Beg-Of-Buf, set = End-Of-Buf */ + +/* Note on routine names: + * "SB_" User callable, deals with sbbufs (usually). + * "SBS_" User callable, deals with sbstrings only. + * "SBX_" Internal routine, not meant for external use. + * "SBM_" Routine handling mem alloc, usually user callable. + */ + +/* SBBUF Opening, Closing, Mode setting */ + +/* SB_OPEN(sb,sd) - Sets up SBBUF given pointer to first SD of a sbstring. + * If SD == 0 then creates null sbstring. + * Any previous contents of SBBUF are totally ignored!!! If you + * want to save the stuff, use SB_UNSET. + * Sets I/O ptr to start of sbstring. + * Returns 0 if error, else the given SB. + */ +SBBUF * +sb_open(sbp,sdp) +SBBUF *sbp; +SBSTR *sdp; +{ register struct sdblk *sd; + register int cnt; + register WORD *clrp; + + if(!sbp) return((SBBUF *)0); + if((sd = sdp) == 0) + { sd = sbx_ndget(); /* Get a fresh node */ + clrp = (WORD *) sd; /* Clear it all */ + cnt = rnddiv(sizeof(struct sdblk)); + do { *clrp++ = 0; } while(--cnt); + sd->sdflags = SD_NID; /* Except flags of course */ + } + else if(sd->slback) /* Must be first thing in sbstring */ + return((SBBUF *)0); /* Perhaps could normalize tho */ + + clrp = (WORD *) sbp; /* Clear sbbuffer stuff */ + cnt = rnddiv(sizeof(SBBUF)); + do { *clrp++ = 0; } while(--cnt); + + sbp->sbcur = sd; + /* Note that SD_LOCK need not be set, because first SDBLK has no + * backptr. This is desirable to allow storage compactor maximum + * freedom in merging sdblks. + */ + /* sd->sdflags |= SD_LOCK; */ /* Lock this one */ + return(sbp); +} + + +/* SB_CLOSE(sb) - Close a SBBUF. + * Returns pointer to start of sbstring (first SD). + * Returns 0 if error. + */ +SBSTR * +sb_close(sbp) +SBBUF *sbp; +{ register SBBUF *sb; + register struct sdblk *sd; + + if((sb = sbp) == 0) /* Verify pointer */ + return((SBSTR *)0); + sb_rewind(sb); /* Do most of the work, including unlock */ + sd = sb->sbcur; /* Save ptr to sbstring */ + sb->sbcur = 0; /* Now reset the sbbuffer structure */ + sb->sbflags = 0; + return(sd); +} + + +/* SB_SETOVW(sbp) - Set SBBUF Over-write mode for PUTC's. + * SB_CLROVW(sbp) - Clear ditto. + */ +sb_setovw(sbp) +SBBUF *sbp; +{ register SBBUF *sb; + if(sb=sbp) + { sb->sbflags |= SB_OVW; + sb->sbwleft = 0; + } +} + +sb_clrovw(sbp) +SBBUF *sbp; +{ register SBBUF *sb; + if(sb=sbp) sb->sbflags &= ~SB_OVW; +} + +/* SBSTRING file system operations (see also sb_fsave) */ + +/* SB_FDUSE(fd) - Make a sbstring for given file. + * FD is an open file descriptor. + * Returns pointer to a SBSTR containing the given file, or 0 if error. + * The FD must not be closed until a SB_FDCLS is done to + * purge memory of any blocks pointing at the file. + * ** Maybe allocate sbfile structs with sbx_ndget, i.e. overlay on + * ** top of sdblk node?? Wd this screw verify, GC, etc? Maybe not if + * ** SD_LCK2 set... + */ + +struct sbfile *sbv_ftab[SB_NFILES]; + +chroff +sbx_fdlen(fd) +int fd; +{ +#if !V6 + struct stat statb; +#else + struct statb statb; + chroff len; + struct {int hiwd ; int lowd;} foo; +#endif /*V6*/ + + if(fstat(fd,&statb) < 0) return((chroff)-1); +#if V6 + len = statb.i_size1; + len.hiwd = statb.i_size0 & 0377; + return(len); +#else + return((chroff)statb.st_size); +#endif /*-V6*/ +} + +SBSTR * +sb_fduse(ifd) +int ifd; +{ register struct sdblk *sd; + register struct sbfile *sf; + register int fd; + chroff len; + + if((fd = ifd) < 0 || SB_NFILES <= fd /* Check for absurd FD */ + || sbv_ftab[fd]) /* and slot already in use */ + return((SBSTR *)0); + if((len = sbx_fdlen(fd)) < 0) return((SBSTR *)0); + sbv_ftab[fd]= sf = (struct sbfile *)sbx_malloc(sizeof(struct sbfile)); + sf->sffd = fd; + sf->sfptr1 = sd = sbx_ndget(); + sf->sflen = len; + sd->slforw = 0; + sd->slback = 0; + sd->sdforw = 0; + sd->sdback = 0; + sd->sdmem = 0; + sd->sdfile = sf; + sd->sdlen = len; + sd->sdaddr = 0; + return(sd); +} + +/* SB_FDCLS(fd) - Close a file descriptor being used by sbstrings. + * If arg is -1, closes all FD's that are unused (a "sweep"). + * For specific arg, returns 0 if couldn't close FD because still in use. + * Perhaps later version of routine could have option to copy + * still-used SD's to tempfile, and force the FD closed? + */ +sb_fdcls(ifd) +int ifd; +{ register int fd; + + if((fd = ifd) >= 0) + { if(fd >= SB_NFILES) return(0); /* Error of sorts */ + return(sbx_fcls(sbv_ftab[fd])); + } + fd = SB_NFILES-1; + do { + sbx_fcls(sbv_ftab[fd]); + } while(--fd); /* Doesn't try FD 0 ! */ + return(1); +} + +sbx_fcls(sfp) +struct sbfile *sfp; +{ register struct sbfile *sf; + register int fd; + + if((sf = sfp)==0 /* Ignore null args */ + || sf == &sbv_tf) /* and never close our tempfile! */ + return(0); + fd = sf->sffd; /* Find sys file descriptor */ + if(sbv_ftab[fd] != sf) /* Ensure consistency */ + return(sbx_err(0,"SF table inconsistency")); + if(sf->sfptr1) /* Any phys list still exists? */ + return(0); /* Yes, still in use, can't close */ + close(fd); /* Maybe do this when list gone? */ + sbv_ftab[fd] = 0; /* Remove from table */ + free(sf); /* Remove sbfile struct from mem */ +} + +/* SB_FDINP(sb,fd) - Returns TRUE if specified fd is still in use + * by specified sbbuffer. + */ +sb_fdinp(sb, fd) +register SBBUF *sb; +int fd; +{ register struct sdblk *sd; + register struct sbfile *sf; + + if((sf = sbv_ftab[fd]) == 0 + || (sd = sb->sbcur) == 0) + return(0); + sd = sbx_beg(sd); /* Move to beginning of sbstring */ + for(; sd; sd = sd->slforw) /* Scan thru all blocks in string */ + if(sd->sdfile == sf) /* If any of them match, */ + return(1); /* Return tally-ho */ + return(0); +} + +/* SB_FSAVE(sb,fd) - Write entire SBBUF out to specified FD. + * Returns 0 if successful, else system call error number. + */ +sb_fsave(sb,fd) /* Write all of given sbbuf to given fd */ +register SBBUF *sb; +int fd; +{ + sbx_smdisc(sb); + return(sbx_aout(sbx_beg(sb->sbcur), 2, fd)); +} + +/* SBBUF Character Operations */ + +/* SB_GETC(sb) - Get next char from sbstring. + * Returns char at current location and advances I/O ptr. + * Returns EOF on error or end-of-string. + */ +int +sb_sgetc(sb) +register SBBUF *sb; +{ + if(--(sb->sbrleft) >= 0) + return sb_uchartoint(*sb->sbiop++); + + /* Must do hard stuff -- check ptrs, get next blk */ + sb->sbrleft = 0; /* Reset cnt to zero */ + if(sb->sbcur == 0 /* Make sure sbbuffer there */ + || (int)sbx_ready(sb,SK_READF,0,SB_BUFSIZ) <= 0) /* Normalize & gobble */ + return(EOF); + return(sb_sgetc(sb)); /* Try again */ +} /* Loop wd be faster, but PDL OV will catch infinite-loop bugs */ + + +/* SB_PUTC(sb,ch) - Put char into sbstring. + * Inserts char at current location. + * Returns EOF on error, else the char value. + */ +int +sb_sputc(sb,ch) +register SBBUF *sb; +int ch; +{ + register struct sdblk *sd; + + if(--(sb->sbwleft) >= 0) return(*sb->sbiop++ = ch); + + sb->sbwleft = 0; /* Reset cnt to avoid overflow */ + if((sd = sb->sbcur) == 0) /* Verify string is there */ + return(EOF); /* Could perhaps create it?? */ + if(sb->sbflags&SB_OVW) /* If overwriting, handle std case */ + { if(sb->sbiop && + --sb->sbrleft >= 0) /* Use this for real count */ + { sd->sdflags |= SD_MOD; /* Win, munging... */ + return(*sb->sbiop++ = ch); + } + /* Overwriting and hit end of this block. */ + if((int)sbx_ready(sb,SK_READF,0,SB_BUFSIZ) > 0) /* Re-normalize */ + return(sb_sputc(sb,ch)); + + /* No blks left, fall through to insert stuff at end */ + } + + /* Do canonical setup with heavy artillery */ + if((int)sbx_ready(sb,SK_WRITEF,SB_SLOP,SB_BUFSIZ) <= 0) /* Get room */ + return(EOF); /* Should never happen, but... */ + sb->sbflags |= SB_WRIT; + sb->sbcur->sdflags |= SD_MOD; + return(sb_sputc(sb,ch)); /* Try again */ +} /* Loop wd be faster, but PDL OV will catch infinite-loop bugs */ + + +/* SB_PEEKC(sb) - Peek at next char from sbstring. + * Returns char that sb_getc would next return, but without + * changing I/O ptr. + * Returns EOF on error or end-of-string. + */ +int +sb_speekc(sb) +register SBBUF *sb; +{ + if (sb->sbrleft <= 0) /* See if OK to read */ + { if (sb_sgetc(sb) == EOF) /* No, try hard to get next */ + return EOF; /* Failed, return EOF */ + sb_backc(sb); /* Won, back up */ + } + return sb_uchartoint(*sb->sbiop); +} + +/* SB_RGETC(sb) - Get previous char from sbstring. + * Returns char prior to current location and backs up I/O ptr. + * Returns EOF on error or beginning-of-string. + */ +int +sb_rgetc(sb) +register SBBUF *sb; +{ + register struct smblk *sm; + register struct sdblk *sd; + + if((sd=sb->sbcur) && (sm = sd->sdmem) + && sb->sbiop > sm->smaddr) + { if(sb->sbflags&SB_WRIT) + { sm->smuse = sb->sbiop - sm->smaddr; + sb->sbwleft = 0; + sb->sbflags &= ~SB_WRIT; + } + sb->sbrleft++; + return sb_uchartoint(*--sb->sbiop); /* Return char */ + } + if((int)sbx_ready(sb,SK_READB,SB_BUFSIZ,0) <= 0) + return(EOF); + return(sb_rgetc(sb)); +} + +/* SB_RDELC(sb) - Delete backwards one char. + * Returns nothing. + */ +sb_rdelc(sbp) +SBBUF *sbp; +{ register SBBUF *sb; + register struct sdblk *sd; + + if(((sb=sbp)->sbflags&SB_WRIT) /* Handle simple case fast */ + && sb->sbiop > (sd = sb->sbcur)->sdmem->smaddr) + { sb->sbwleft++; + sb->sbiop--; + sd->sdflags |= SD_MOD; + return; + } + else sb_deln(sb,(chroff) -1); /* Else punt... */ +} + +/* SB_DELC(sb) - Delete one char forward? */ +/* SB_INSC(sb,ch) - Insert char? (instead of or in addition to PUTC) */ + + +/* SBBUF string or N-char operations */ + +/* SB_DELN(sb,chroff) - delete N chars. Negative N means backwards. + * Differs from sb_killn in that it flushes the text forever, + * and doesn't return anything. + */ + +sb_deln(sbp, num) +SBBUF *sbp; +chroff num; +{ + register struct sdblk *sd; + + if(sd = sb_killn(sbp,num)) + sbs_del(sd); /* Punt */ +} + +/* SB_KILLN(sb,chroff) - delete N chars, saving. Negative N means backwards. + * Returns SD pointer to beginning of saved sbstring. + */ +struct sdblk * +sb_killn(sbp, num) +SBBUF *sbp; +chroff num; +{ register SBBUF *sb; + register struct sdblk *sd, *sd2; + struct sdblk *sdr, *sdx; + chroff savdot; + + if((sd = sbx_xcis((sb=sbp),num,&sdr,&savdot)) == 0) + return((struct sdblk *)0); + + sb->sbcur->sdflags &= ~SD_LOCK; /* Now can flush sbcur lock */ + + /* SD and SD2 now delimit bounds of stuff to excise. + * First do direction dependent fixups + */ + if(num >= 0) /* If deleting forward, */ + sb->sbdot = savdot; /* must reset dot to initial loc */ + + /* SD and SD2 now in first/last order. Complete SBCUR fixup. */ + sd2 = sdr; /* sdr has ptr to end of stuff */ + if(sd2 = sd2->slforw) /* More stuff after killed list? */ + { sb->sbcur = sd2; /* Yes, point at it */ + sb->sboff = 0; /* Dot already set right */ + } + else if(sdx = sd->slback) /* See if any prior to killed list */ + { sb->sbcur = sdx; /* Yes, point at it */ + sb->sboff = (sdx->sdmem ? /* Get len of prev blk */ + sdx->sdmem->smuse : sdx->sdlen); + sb->sbdot -= sb->sboff; + } + else sb_open(sb,(SBSTR *)0); /* No stuff left! Create null sbstring */ + + /* Fix up logical links. Note SD2 points to succ of killed stuff */ + if(sd->slback) /* If previous exists */ + { if(sd->slback->slforw = sd2) /* Point it to succ, and */ + sd2->slback = sd->slback; /* thence to self */ + sd->slback = 0; /* Now init killed list */ + } + else if(sd2) sd2->slback = 0; /* No prev, clean rest */ + (sd2 = sdr)->slforw = 0; /* Finish killed list */ + + sb->sbcur->sdflags |= SD_LOCK; /* Ensure current SD now locked */ + sd->sdflags &= ~SD_LCK2; /* And unlock killed list */ + sd2->sdflags &= ~SD_LCK2; + return(sd); +} + +/* SB_CPYN(sbp,num) - Copy num characters, returns SD to sbstring. + * Like SB_KILLN but doesn't take chars out of original sbstring. + */ +SBSTR * +sb_cpyn(sbp,num) +SBBUF *sbp; +chroff num; +{ register SBBUF *sb; + register struct sdblk *sd, *sd2; + struct sdblk *sdr; + chroff savloc; + + sb = sbp; + if((sd = sbx_xcis(sb,num,&sdr,&savloc)) == 0) + return((SBSTR *)0); + sd2 = sbx_scpy(sd,sdr); + sb_seek(sb,-num,1); /* Return to original loc */ + return(sd2); /* Return val is ptr to head of copy. + * It needn't be locked, because GC will + * never move list heads! + */ +} + +/* SB_SINS(sb,sd) - Insert sbstring at current location + * + */ +sb_sins(sbp,sdp) +SBBUF *sbp; +struct sdblk *sdp; +{ register SBBUF *sb; + register struct sdblk *sd, *sdx; + chroff inslen; + + if((sb = sbp)==0 + || (sd = sdp) == 0) + return(0); + if(sd->slback) /* Perhaps normalize to beg? */ + return(0); + if((sdx = (struct sdblk *)sbx_ready(sb,SK_DELB)) == 0) /* Get cur pos ready */ + return(0); + inslen = sbs_len(sd); /* Save length of inserted stuff */ + + sd->slback = sdx; /* Fix up links */ + if(sdx->slforw) + { while(sd->slforw) /* Hunt for end of inserted sbstring */ + sd = sd->slforw; + sd->slforw = sdx->slforw; + sd->slforw->slback = sd; + } + sdx->slforw = sdp; + sb->sboff += inslen; /* Set IO ptr to end of new stuff */ + return(1); +} + +/* SBSTRING routines - operate on "bare" sbstrings. */ + +/* SBS_CPY(sd) - Copies given sbstring, returns ptr to new sbstring. + */ +SBSTR * +sbs_cpy(sdp) +SBSTR *sdp; +{ return(sbx_scpy(sdp,(struct sdblk *)0)); +} + +/* SBS_DEL(sd) - Flush a sbstring. + */ +sbs_del(sdp) +SBSTR *sdp; +{ register struct sdblk *sd; + + if(sd = sdp) + while(sd = sbx_ndel(sd)); +} + + +/* SBS_APP(sd1,sd2) - Appends sbstring sd2 at end of sbstring sd1. + * Returns sd1 (pointer to new sbstring). + */ + +SBSTR * +sbs_app(sdp,sdp2) +struct sdblk *sdp,*sdp2; +{ register struct sdblk *sd, *sdx; + + if(sd = sdp) + { while(sdx = sd->slforw) + sd = sdx; + if(sd->slforw = sdx = sdp2) + sdx->slback = sd; + } + return(sdp); +} + +/* SBS_LEN(sd) - Find length of sbstring. + */ +chroff +sbs_len(sdp) +SBSTR *sdp; +{ register struct sdblk *sd; + register struct smblk *sm; + chroff len; + + if((sd = sdp)==0) return((chroff)0); + len = 0; + for(; sd ; sd = sd->slforw) + { if(sm = sd->sdmem) + len += (chroff)sm->smuse; + else len += sd->sdlen; + } + return(len); +} + +/* SBBUF I/O pointer ("dot") routines */ + +/* SB_SEEK(sb,chroff,flag) - Like FSEEK. Changes I/O ptr value as + * indicated by "flag": + * 0 - offset from beg + * 1 - offset from current pos + * 2 - offset from EOF + * Returns -1 on errors. + * Seeking beyond beginning or end of sbbuf will leave pointer + * at the beginning or end respectively. + * Returns 0 unless error (then returns -1). + */ +sb_seek(sbp, coff, flg) +SBBUF *sbp; +chroff coff; +int flg; +{ register SBBUF *sb; + register struct smblk *sm; + register struct sdblk *sd; + SBMO moff; + + sb = sbp; + if((sd = sb->sbcur) == 0) return(-1); + if(sb->sbiop == 0) + { switch(flg) + { case 0: if(coff == 0) /* Optimize common case */ + return(sb_rewind(sb)); + sb->sboff = coff - sb->sbdot; /* Abs */ + break; + case 1: sb->sboff += coff; /* Rel */ + break; + case 2: sb->sboff += sb_ztell(sb) + coff; + break; + default: return(-1); + } + sbx_norm(sb,0); + return(0); + } + if((sm = sd->sdmem) == 0) + return(sbx_err(-1,"SDMEM 0")); + moff = sb->sbiop - sm->smaddr; /* Get cur smblk offset */ + if(sb->sbflags&SB_WRIT) /* Update since moving out */ + { sm->smuse = moff; + sb->sbflags &= ~SB_WRIT; + } + sb->sbwleft = 0; /* Always gets zapped */ + switch(flg) + { case 0: /* Offset from beginning */ + coff -= sb->sbdot + (chroff)moff; /* Make rel */ + + case 1: /* Offset from current loc */ + break; + + case 2: /* Offset from end */ + coff += sb_ztell(sb); + break; + default: return(-1); + } + + /* COFF now has relative offset from current location */ + if (-(chroff)moff <= coff && coff <= sb->sbrleft) + { /* Win! Handle repos-within-smblk */ + sb->sbiop += coff; + sb->sbrleft -= coff; /* Set r; wleft already 0 */ + return(0); + } + + /* Come here when moving to a different sdblk. */ + sb->sbrleft = 0; + sb->sbiop = 0; + sb->sboff = coff + (chroff)moff; + sbx_norm(sb,0); + return(0); +} + +/* SB_REWIND(sb) - Go to beginning of sbbuffer. + * Much faster than using sb_seek. Note that this leaves the sbbuffer + * in an open/idle state which is maximally easy to compact. + */ +sb_rewind(sbp) +SBBUF *sbp; +{ register SBBUF *sb; + register struct sdblk *sd; + + if((sb = sbp)==0) return; + sbx_smdisc(sb); /* Ensure I/O disconnected */ + (sd = sb->sbcur)->sdflags &= ~SD_LOCK; /* Unlock current blk */ + sd = sbx_beg(sd); /* Move to beg of sbstring */ + /* Need not lock - see sb_open comments, also sb_close */ + /* sd->sdflags |= SD_LOCK; */ /* Lock onto this one */ + sb->sbcur = sd; + sb->sbdot = 0; + sb->sboff = 0; +} + +/* SB_TELL(sb) - Get I/O ptr value for SBBUF. + * Returns -1 on errors. + */ + +chroff +sb_tell(sbp) +SBBUF *sbp; +{ register SBBUF *sb; + register struct smblk *sm; + register struct sdblk *sd; + + if((sd = (sb=sbp)->sbcur) == 0) + return((chroff)-1); + if(sb->sbiop == 0) + return(sb->sbdot + sb->sboff); + if((sm = sd->sdmem) == 0) + return(sbx_err(0,"SDMEM 0")); + return(sb->sbdot + (unsigned)(sb->sbiop - sm->smaddr)); +} + +/* SB_ZTELL(sb) - Get I/O ptr relative to "Z" (EOF). + * Returns # chars from current location to EOF; 0 if any errors. + */ +chroff +sb_ztell(sbp) +SBBUF *sbp; +{ register SBBUF *sb; + register struct smblk *sm; + register struct sdblk *sd; + + if((sd = (sb=sbp)->sbcur) == 0) + return((chroff)0); + if(sb->sbiop && (sm = sd->sdmem)) + { if(sb->sbflags&SB_WRIT) /* If actively writing, */ + return(sbs_len(sd->slforw)); /* ignore this blk. */ + /* Note that previous code makes it unnecessary + * to invoke sbx_smdisc. (otherwise wrong + * smuse would confuse sbs_len). + */ + return(sbs_len(sd) - (sb->sbiop - sm->smaddr)); + } + else + return(sbs_len(sd) - sb->sboff); +} + +/* Code past this point should insofar as possible be INTERNAL. */ + +/* SBX_READY(sb,type,cmin,cmax) - Set up SBBUF for reading or writing. + * + * If no current smblk: + * reading - set up for reading + * writing - set up for splitting? + * If current smblk: + * reading - if can read, OK. Else position at beg of next sdblk + * writing - if can write, OK. Else position at end of prev sdblk, + * or set up for splitting? + * Types: + * 0 - Read forward (BOB) + * 1 - Read backward (EOB) + * 3 - Write (insert forward) (EOB) + * 4 - Delete forward (return SD, force BOB-aligned) + * 5 - Delete backward (return SD, force EOB-aligned) + * Connected SD is always locked. + * Returns 0 if error, -1 if EOF-type error, 1 for success. + * + * For types 0,1: + * CMIN,CMAX represent max # chars to read in to left and right of + * I/O ptr (prev and post). Actual amount read in may be + * much less, but will never be zero. + * Successful return guarantees that SBIOP etc. are ready. + * For type 3: + * If new block is allocated, CMIN and CMAX represent min, max sizes + * of the block. + * Successful return guarantees that SBIOP etc. are ready, but + * NOTE that SB_WRIT and SD_MOD are not set! If not going to use + * for writing, be sure to clear sbwleft on return! + * For types 4,5: + * CMIN, CMAX are ignored. + * SBIOP is always cleared. SBOFF is guaranteed to be 0 for + * type 4, SMUSE for type 5. + * Return value is a SD ptr; 0 indicates error. -1 isn't used. + */ + +struct sdblk * +sbx_ready(sbp,type,cmin,cmax) +SBBUF *sbp; +int type; +SBMO cmin,cmax; +{ register SBBUF *sb; + register struct sdblk *sd; + register struct smblk *sm; + int cnt, slop, rem; + SBMO moff; + + if((sd = (sb=sbp)->sbcur) == 0) + return(0); + if(sb->sbiop) /* Canonicalize for given operation */ + { if((sm = sd->sdmem)==0) + return(0); + moff = sb->sbiop - sm->smaddr; /* Current block offset */ + switch(type) + { + case SK_READF: /* Read Forward */ + if(sb->sbrleft > 0) /* Already set up? */ + return(1); /* Yup, fast return */ + sbx_smdisc(sb); /* None left, disc to get next */ + if((sd = sbx_next(sb)) == 0) /* Try to get next blk */ + return(-1); /* At EOF */ + break; + + case SK_READB: /* Read Backward */ + if(moff) /* Stuff there to read? */ + { if(sb->sbflags&SB_WRIT) /* Yup, turn writes off */ + { sm->smuse = moff; + sb->sbflags &= ~SB_WRIT; + } + sb->sbwleft = 0; + return(1); + } + sbx_smdisc(sb); + break; + + case SK_WRITEF: /* Writing */ + if(sb->sbrleft <= 0) + sb->sbwleft = sm->smlen - moff; + if(sb->sbwleft > 0) + return(1); /* OK to write now */ + /* NOTE: flags not set!!! */ + sbx_smdisc(sb); + break; + + case SK_DELF: /* Delete forward - force BOB */ + if(sb->sbrleft <= 0) /* At end of blk? */ + { sbx_smdisc(sb); /* Win, unhook */ + return(sbx_next(sb)); /* Return next or 0 if EOF */ + } + sbx_smdisc(sb); /* Not at end, but see if */ + if(moff == 0) /* at beg of blk? */ + return(sd); /* Fast win! */ + break; + + case SK_DELB: /* Delete backward - force EOB */ + if(sb->sbrleft <= 0) /* Win if already EOB */ + { sbx_smdisc(sb); + return(sd); + } + sbx_smdisc(sb); + break; + + default: + return(0); + } + } + + /* Schnarf in the text, or whatever. + * SD points to current sdblk (must be SD_LOCKed) + * SBDOT must have correct value for this SD + * SBOFF has offset from there to put I/O ptr at. + * + * After normalization, SBOFF is guaranteed to point within + * the SD. Other guarantees apply to boundary cases, depending + * on the mode (type) bits. + */ + sd = sbx_norm(sb,type); /* Normalize I/O pos appropriately */ + sm = sd->sdmem; + switch(type) + { + case SK_READB: /* Read Backward */ + if(sb->sboff == 0) /* Due to normalize, if 0 seen */ + return(-1); /* then we know it's BOF */ + if(sm) goto sekr2; + else goto sekr1; + + case SK_READF: /* Read Forward */ + if(sm) goto sekr2; + if(sb->sboff == sd->sdlen) /* Normalize means if EOB */ + return(-1); /* then at EOF. */ + sekr1: slop = SB_SLOP; + sekr3: if(sb->sboff > cmin+slop) /* Too much leading text? */ + { /* Split off leading txt */ + sbx_split(sd,(chroff)(sb->sboff - cmin)); + sd = sbx_next(sb); /* Point to next sdblk */ + sb->sboff = cmin; /* Set correct offset */ + /* (sbx_next assumes 0) */ + } + if(sd->sdlen > sb->sboff+cmax+slop) /* Too much trailing txt? */ + sbx_split(sd,(chroff)(sb->sboff+cmax)); + + /* ----- Try to get mem blk to read stuff into ----- */ + /* Note alignment hack for extra efficiency. This ensures + * that all reads from disk to memory are made with the same + * source and destination word alignment, so the system kernel + * only needs byte-moves for the first or last bytes; all + * others can be word-moves. + * This works because sbx_mget always returns word-aligned + * storage, and we use sbx_msplit to trim off the right number + * of bytes from the start. + */ + cnt = sd->sdlen; /* Get # bytes we'd like */ + if(rem = rndrem(sd->sdaddr)) /* If disk not word-aligned */ + cnt += rem; /* allow extra for aligning.*/ + if(sm == 0) /* Always true 1st time */ + { sm = sbx_mget(SB_SLOP,cnt); /* Get room (may GC!)*/ + if(sm->smlen < cnt) /* Got what we wanted? */ + { slop = 0; /* NO!! Impose stricter */ + cmin = 0; /* limits. Allow for new */ + cmax = sm->smlen - (WDSIZE-1); /* rem. */ + if(type == SK_READB) + { cmin = cmax; cmax = 0; } + goto sekr3; /* Go try again, sigh. */ + } + } + else if(sm->smlen < cnt) /* 2nd time shd always win */ + { sbx_err(0,"Readin blksiz err"); /* Internal error, */ + if((cmax /= 2) > 0) goto sekr3; /* w/crude recovery */ + return(0); + } + if(rem) /* If disk not word-aligned, hack stuff */ + { sm = sbx_msplit(sm, (SBMO)rem); /* Trim off from beg*/ + sbm_mfree(sm->smback); /* Free the excess */ + } + sd->sdmem = sm; + sm->smuse = sd->sdlen; + + if(sd->sdfile == 0) + return(sbx_err(0,"No file")); /* Gasp? */ + if(!sbx_rdf(sd->sdfile->sffd, sm->smaddr, sm->smuse, + 1, sd->sdaddr)) + return(sbx_err(0,"Readin SD: %o", sd)); + /* ------- */ + + sekr2: sbx_sbrdy(sb); /* Make it current, pt to beg */ + sb->sbwleft = 0; /* Ensure not set (esp if READB) */ + break; + + case SK_WRITEF: /* Write-type seek */ + if(sm == 0) + { /* Block is on disk, so always split (avoid readin) */ + if(sd->sdlen) /* May be empty */ + { sbx_split(sd, sb->sboff); /* Split at IO ptr */ + sd = sbx_next(sb); /* Move to 2nd part */ + if(sd->sdlen) /* If stuff there, */ + /* split it again. */ + sbx_split(sd, (chroff) 0); + } + goto sekwget; + } + + /* Block in memory */ + moff = sm->smuse; + if(sb->sboff == moff) /* At end of the block? */ + { if(sm->smlen > moff) /* Yes, have room? */ + goto sekw; /* Win, go setup and ret */ + if(sm->smforw /* If next mem blk */ + && (sm->smforw->smflags /* Can have bytes */ + & (SM_USE|SM_NXM))==0 /* stolen from it */ + && (sd->sdflags&SD_MOD) /* and we ain't pure*/ + && sm->smlen < cmax) /* and not too big */ + { /* Then steal some core!! Note that without + * the size test, a stream of putc's could + * create a monster block gobbling all mem. + */ + cmin = cmax - sm->smlen; + if(cmin&01) cmin++; /* Ensure wd-align */ + if(sm->smforw->smlen <= cmin) + { sbm_mmrg(sm); + goto sekw; + } + sm->smforw->smlen -= cmin; + sm->smforw->smaddr += cmin; + sm->smlen += cmin; + goto sekw; + } + /* Last try... check next logical blk for room */ + if(sd->slforw && (sm = sd->slforw->sdmem) + && sm->smuse == 0 + && sm->smlen) + { sd = sbx_next(sb); /* Yup, go there */ + goto sekw; + } + } + + /* Middle of block, split up to insert */ + sbx_split(sd, sb->sboff); /* Split at IO ptr */ + if(sd->sdmem) /* Unless blk now empty, */ + { sd = sbx_next(sb); /* move to next. */ + if(sd->sdmem) /* If not empty either */ + sbx_split(sd, (chroff) 0); /* Split again */ + } + + /* Have empty SD block, get some mem for it */ + sekwget: sd->sdmem = sm = sbx_mget(cmin,cmax); + sm->smuse = 0; + sekw: sbx_sbrdy(sb); /* Sets up sbwleft... */ + return(1); + + case SK_DELF: /* Delete forward */ + if(sb->sboff == 0) /* At block beg already? */ + return(sd); /* Win, return it */ + sbx_split(sd, sb->sboff); /* No, split up and */ + return(sbx_next(sb)); /* return ptr to 2nd part */ + + case SK_DELB: /* Delete backward (force EOB align) */ + if(sb->sboff != /* If not at EOB already, */ + (sm ? (chroff)(sm->smuse) : sd->sdlen)) + sbx_split(sd, sb->sboff); /* Then split */ + return(sd); /* And return ptr to 1st part */ + break; + + default: + return(0); + } /* End of switch */ + return(1); +} + +struct sdblk * +sbx_next (sbp) +SBBUF *sbp; +{ register SBBUF *sb; + register struct sdblk *sd, *sdf; + if((sdf = (sd = (sb=sbp)->sbcur)->slforw) == 0) + return((struct sdblk *)0); + sb->sbdot += (sd->sdmem ? (chroff)sd->sdmem->smuse : sd->sdlen); + sb->sboff = 0; + sd->sdflags &= ~SD_LOCK; /* Unlock current */ + sdf->sdflags |= SD_LOCK; /* Lock next */ + sb->sbcur = sdf; + return(sdf); +} + +/* SBX_NORM(sb,mode) - Normalizes I/O position as desired. + * The SBBUF must have I/O disconnected (SBIOP==0). + * Adjusts SBCUR, SBDOT, and SBOFF so that SBOFF is guaranteed + * to point to a location in the current SD block. + * The mode flags determine action when there is more than + * one possible SD that could be pointed to, as is the case + * when the I/O pos falls on a block boundary (possibly with + * adjacent zero-length blocks as well). + * SKM_0BACK - Zero-skip direction. + * 0 = Skip forward over zero-length blocks. + * set = Skip backward over zero-length blocks. + * SKM_EOB - Block-end selection (applies after skipping done). + * 0 = Point to BOB (Beginning Of Block). + * set = Point to EOB (End Of Block). + * Returns the new current SD as a convenience. + * Notes: + * The SKM_0BACK flag value is a special hack to search in + * the right direction when SBOFF is initially 0. + * None of the mode flags have any effect if the I/O pos falls + * within a block. + * Perhaps this routine should flush the zero-length blks it + * finds, if they're not locked?? + */ +struct sdblk * +sbx_norm(sbp,mode) +SBBUF *sbp; +int mode; +{ register struct sdblk *sd; + register struct smblk *sm; + register SBBUF *sb; + chroff len; + + if((sd = (sb=sbp)->sbcur) == 0) + { sb->sbdot = 0; + sb->sboff = 0; + return(sd); + } + sd->sdflags &= ~SD_LOCK; /* Unlock current blk */ + + if(sb->sboff >= (mode&01)) /* Hack hack to get right skip */ + for(;;) /* Scan forwards */ + { if(sm = sd->sdmem) /* Get length of this blk */ + len = sm->smuse; + else len = sd->sdlen; + if(sb->sboff <= len) + if(sb->sboff < len /* If == and fwd 0-skip, continue */ + || (mode&SKM_0BACK)) + { if((mode&SKM_EOB) /* Done, adjust to EOB? */ + && sb->sboff == 0 /* Yes, are we at BOB? */ + && sd->slback) /* and can do it? */ + { sd = sd->slback; /* Move to EOB */ + sb->sboff = (sm = sd->sdmem) + ? (chroff)(sm->smuse) : sd->sdlen; + sb->sbdot -= sb->sboff; + } + break; + } + if(sd->slforw == 0) /* At EOF? */ + { sb->sboff = len; + break; + } + sd = sd->slforw; + sb->sboff -= len; + sb->sbdot += len; + } + else /* Scan backwards */ + for(;;) + { if(sd->slback == 0) /* At BOF? */ + { sb->sboff = 0; + sb->sbdot = 0; /* Should already be 0, but... */ + break; + } + sd = sd->slback; + if(sm = sd->sdmem) /* Get length of this blk */ + len = sm->smuse; + else len = sd->sdlen; + sb->sbdot -= len; + if((sb->sboff += len) >= 0) + if(sb->sboff > 0 /* If == 0 and bkwd 0-skip, continue */ + || !(mode&SKM_0BACK)) + { if((mode&SKM_EOB) == 0 /* Done, adjust to BOB? */ + && sb->sboff == len /* Yes, are we at EOB? */ + && sd->slforw) /* and can do it? */ + { sd = sd->slforw; /* Move to BOB */ + sb->sboff = 0; + sb->sbdot += len; + } + break; + } + } + sb->sbcur = sd; + sd->sdflags |= SD_LOCK; + return(sd); +} + + +struct sdblk * +sbx_beg(sdp) +struct sdblk *sdp; +{ register struct sdblk *sd, *sdx; + if(sd = sdp) + while(sdx = sd->slback) + sd = sdx; + return(sd); +} + + +sbx_smdisc(sbp) +SBBUF *sbp; +{ register SBBUF *sb; + register struct smblk *sm; + register struct sdblk *sd; + + sb = sbp; + if((sd = sb->sbcur) == 0 + || (sm = sd->sdmem) == 0) + return; + if(sb->sbflags&SB_WRIT) + { sm->smuse = sb->sbiop - sm->smaddr; + sb->sbflags &= ~SB_WRIT; + } + sb->sboff = sb->sbiop - sm->smaddr; + sb->sbiop = 0; + sb->sbrleft = sb->sbwleft = 0; +} + +sbx_sbrdy(sbp) /* Sets up SBIOP, SBRLEFT, SBWLEFT */ +SBBUF *sbp; +{ register SBBUF *sb; + register struct sdblk *sd; + register struct smblk *sm; + + if((sd = (sb=sbp)->sbcur) == 0 + || (sm = sd->sdmem) == 0) + return; + sd->sdflags |= SD_LOCK; + sb->sbiop = sm->smaddr + sb->sboff; + if(sb->sbrleft = sm->smuse - sb->sboff) + sb->sbwleft = 0; + else sb->sbwleft = sm->smlen - sm->smuse; +} + + +/* SBX_SCPY(sd,sdl) - Copies given sbstring, returns ptr to new sbstring. + * Only goes as far as sdl (last copied blk); 0 for entire sbstring. + */ +struct sdblk * +sbx_scpy(sdp,sdlast) +struct sdblk *sdp, *sdlast; +{ register struct sdblk *sd, *sd2, *sdn; + struct sdblk *sdr; + + if((sd = sdp) == 0) return((struct sdblk *)0); + sdn = 0; + do { + sd->sdflags |= SD_LCK2; + sd2 = sbx_sdcpy(sd); + if(sd2->slback = sdn) + { sdn->slforw = sd2; + sdn->sdflags &= ~SD_LOCKS; + } + else sdr = sd2; /* Save 1st */ + sdn = sd2; + sd->sdflags &= ~SD_LCK2; + } while(sd != sdlast && (sd = sd->slforw)); + sd2->slforw = 0; + sd2->sdflags &= ~SD_LOCKS; + return(sdr); +} + + +/* SBX_SDCPY(sd) - Copies given sdblk, returns ptr to new blk. + * Does not set locks, assumes caller does this (which it MUST, + * to avoid compaction lossage!) + */ + +struct sdblk * +sbx_sdcpy(sdp) +struct sdblk *sdp; +{ register struct sdblk *sd, *sd2; + register struct smblk *sm, *sm2; + + if((sd = sdp) == 0) return((struct sdblk *)0); + sd2 = sbx_ndget(); /* Get a free sdblk */ + bcopy((SBMA)sd, (SBMA)sd2, sizeof(struct sdblk)); /* Copy sdblk data */ + sd2->slforw = 0; /* Don't let it think it's on a list */ + sd2->slback = 0; + if(sd2->sdfile) /* If has disk copy, */ + { sd->sdforw = sd2; /* Fix phys list ptrs */ + sd2->sdback = sd; + if(sd2->sdforw) + sd2->sdforw->sdback = sd2; + } + if(sm = sd2->sdmem) /* If has in-core copy, try to */ + { if(sm2 = sbm_mget(sm->smuse,sm->smuse)) /* get mem for it */ + { bcopy(sm->smaddr,sm2->smaddr,sm->smuse); + sm2->smuse = sm->smuse; + sd2->sdmem = sm2; /* Point new sd to copy */ + } + else /* Can't get mem... */ + { if(sd2->sdflags&SD_MOD) + sbx_aout(sd2,1); /* Swap out the blk */ + sd2->sdmem = 0; /* Don't have incore copy */ + } + } + return(sd2); +} + +/* SBX_XCIS(sbp,coff,&sdp2,adot) - Internal routine to excise a sbstring, + * defined as everything between current location and given offset. + * SD to first sdblk is returned (0 if error) + * SD2 (address passed as 3rd arg) is set to last sdblk. + * Both are locked with LCK2 to ensure that pointers are valid. + * The current location at time of call is also returned via adot. + */ +struct sdblk * +sbx_xcis(sbp,num,asd2,adot) +SBBUF *sbp; +chroff num, *adot; +struct sdblk **asd2; +{ register SBBUF *sb; + register struct sdblk *sd, *sd2; + int dirb; + + if((sb = sbp) == 0) return((struct sdblk *)0); + dirb = 0; /* Delete forward */ + if(num == 0) return((struct sdblk *)0); /* Delete nothing */ + if(num < 0) dirb++; /* Delete backward */ + + if((sd = (struct sdblk *) + sbx_ready(sb, (dirb ? SK_DELB : SK_DELF))) == 0) + return((struct sdblk *)0); /* Maybe nothing there */ + sd->sdflags |= SD_LCK2; /* Lock up returned SD */ + *adot = sb->sbdot; /* Save current location */ + sb->sboff += num; /* Move to other end of range */ + + if((sd2 = (struct sdblk *) + sbx_ready(sb,(dirb ? SK_DELF : SK_DELB))) == 0) + { sd->sdflags &= ~SD_LCK2; /* This shd never happen if */ + return( /* we got this far, but... */ + (struct sdblk *)sbx_err(0,"KILLN SD2 failed")); + } + sd2->sdflags |= SD_LCK2; /* Lock up other end of stuff */ + + /* SD and SD2 now delimit bounds of stuff to excise. + * Now do direction dependent fixups + */ + if(dirb) + { /* Backward, current sbdot is ok but must get SD/SD2 + * into first/last order. Also, due to nature of block + * splitups, a backward delete within single block will leave + * SD actually pointing at predecessor block. + */ + if(sd->slforw == sd2) /* If SD became pred, fix things. */ + { sd->sdflags &= ~SD_LCK2; /* Oops, unlock! */ + sd = sd2; + } + else /* Just need to swap SD, SD2 ptrs. */ + { /* Goddamit why doesn't C have an */ + /* exchange operator??? */ + *asd2 = sd; + return(sd2); + } + } + *asd2 = sd2; + return(sd); +} + +/* SBX_SPLIT(sd,chroff) - Splits block SD at point CHROFF (offset from + * start of block). SD remains valid; it is left locked. + * The smblk is split too, if one exists, and SMUSE adjusted. + * If offset 0, or equal to block length, the 1st or 2nd SD respectively + * will not have a smblk and its sdlen will be 0. + * (Note that if a smblk exists, a zero sdlen doesn't indicate much) + */ +struct sdblk * +sbx_split(sdp, coff) +struct sdblk *sdp; +chroff coff; +{ register struct sdblk *sd, *sdf, *sdx; + + if((sd=sdp) == 0) + return((struct sdblk *)0); + sd->sdflags |= SD_LOCK; + if(sd->sdflags&SD_MOD) /* If block has been munged, */ + sbx_npdel(sd); /* Flush from phys list now. */ + sdf = sbx_ndget(); /* Get a sdblk node */ + bcopy((SBMA)sd, (SBMA)sdf, (sizeof (struct sdblk))); /* Copy node */ + /* Note that the flags are copied, so both sdblks are locked and + * safe from possible GC compaction during call to sbx_msplit... + */ + if(coff == 0) /* If offset was 0, */ + { /* then 1st SD becomes null */ + if(sdf->sdfile) /* Fix up phys links here */ + { if(sdx = sdf->sdback) + sdx->sdforw = sdf; + else sdf->sdfile->sfptr1 = sdf; + if(sdx = sdf->sdforw) + sdx->sdback = sdf; + } + sdx = sd; + goto nulsdx; + } + else if(sd->sdmem) + if(coff >= sd->sdmem->smuse) + goto nulsdf; + else sdf->sdmem = sbx_msplit(sd->sdmem, (SBMO)coff); + else if(coff >= sd->sdlen) +nulsdf: { sdx = sdf; +nulsdx: sdx->sdforw = 0; + sdx->sdback = 0; + sdx->sdmem = 0; + sdx->sdfile = 0; + sdx->sdlen = 0; + sdx->sdaddr = 0; + goto nulskp; + } + if(sd->sdfile) + { sdf->sdlen -= coff; /* Set size of remainder */ + sdf->sdaddr += coff; /* and address */ + sd->sdlen = coff; /* Set size of 1st part */ + + /* Link 2nd block into proper place in physical sequence. + * 1st block is already in right place. Search forward until + * find a block with same or higher disk address, and insert + * in front of it. If sdlen is zero, just flush the links, + * which is OK since the 1st block is what's pointed to anyway. + */ + if(sdf->sdlen > 0) + { while((sdx = sd->sdforw) /* Find place to insert */ + && sdf->sdaddr > sdx->sdaddr) + sd = sdx; + sdf->sdback = sd; /* Link following sd. */ + if(sdf->sdforw = sd->sdforw) + sdf->sdforw->sdback = sdf; + sd->sdforw = sdf; + sd = sdp; /* Restore pointer */ + } + else + { sdf->sdforw = 0; + sdf->sdback = 0; + sdf->sdfile = 0; /* Say no disk */ + } + } + +nulskp: sdf->slback = sd; /* Link in logical sequence */ + if(sd->slforw) + sd->slforw->slback = sdf; + sd->slforw = sdf; + + sdf->sdflags &= ~SD_LOCKS; /* Unlock 2nd but not 1st */ + return(sd); /* Note sd, not sdf */ +} + +/* SBX_MSPLIT - Like sbm_split but never fails, and sets + * SMUSE values appropriately + */ +struct smblk * +sbx_msplit(smp, size) +struct smblk *smp; +SBMO size; +{ register struct smblk *sm, *smx; + register int lev; + + lev = 0; + while((smx = sbm_split((sm = smp), size)) == 0) + sbx_comp(SB_BUFSIZ,lev++); /* Need to get some smblk nodes */ + if(sm->smlen >= sm->smuse) /* Split across used portion? */ + smx->smuse = 0; /* Nope, new blk is all free */ + else + { smx->smuse = sm->smuse - sm->smlen; + sm->smuse = sm->smlen; + } + return(smx); +} + +/* SBX_NDEL - flush a SD and associated SM. Fixes up logical + * and physical links properly. Returns ptr to next logical SD. + * NOTE: if sd->slback does not exist, the returned SD is your + * only hold on the list, since the SD gets flushed anyway! + */ +struct sdblk * +sbx_ndel(sdp) +struct sdblk *sdp; +{ register struct sdblk *sd, *sdx; + register struct smblk *sm; + + sd = sdp; + if(sm = sd->sdmem) /* If smblk exists, */ + { sbm_mfree(sm); /* flush it. */ + sd->sdmem = 0; + } + if(sdx = sd->slback) + sdx->slforw = sd->slforw; + if(sd->slforw) + sd->slforw->slback = sdx; /* May be zero */ + + /* Logical links done, now hack phys links */ + if(sd->sdfile) /* Have phys links? */ + sbx_npdel(sd); /* Yes, flush from phys list */ + + sdx = sd->slforw; + sbx_ndfre(sd); + return(sdx); +} + +sbx_npdel(sdp) +struct sdblk *sdp; +{ register struct sdblk *sd, *sdx; + register struct sbfile *sf; + + if((sf = (sd=sdp)->sdfile) == 0) + return; + if(sdx = sd->sdback) /* Start of disk file? */ + sdx->sdforw = sd->sdforw; + else + sf->sfptr1 = sd->sdforw; + if(sdx = sd->sdforw) + sdx->sdback = sd->sdback; + sd->sdfile = 0; + sd->sdlen = 0; +} + + +struct sdblk *sbx_nfl; /* Pointer to sdblk node freelist */ + +struct sdblk * +sbx_ndget() /* Like sbm_nget but never fails! */ +{ register struct sdblk *sd; + register int lev; + + lev = 0; + while((sd = sbx_nfl) == 0 /* Get a node */ + /* If fail, make more */ + && (sd = sbm_nmak((sizeof (struct sdblk)),SM_DNODS)) == 0) + /* If still fail, try GC */ + sbx_comp(sizeof(struct sdblk)*SM_DNODS,lev++); + + sbx_nfl = sd->slforw; /* Take it off freelist */ + sd->sdflags = SD_NID; + return(sd); /* Return ptr to it */ +} + +sbx_ndfre(sdp) +struct sdblk *sdp; +{ register struct sdblk *sd; + (sd = sdp)->sdflags = 0; + sd->slforw = sbx_nfl; + sbx_nfl = sd; +} + +SBMA +sbx_malloc(size) +unsigned size; +{ + register int lev; + register SBMA res; + + lev = 0; + while((res = (SBMA)malloc(size)) == 0) + sbx_comp(size,lev++); + return(res); +} + +struct smblk * +sbx_mget(cmin,cmax) /* like sbm_mget but never fails! */ +SBMO cmin, cmax; +{ register struct smblk *sm; + register int lev, csiz; + + lev = 0; + csiz = cmax; + for(;;) + { if(sm = sbm_mget(csiz,cmax)) + return(sm); /* Won right off... */ + sbx_comp(csiz,lev++); /* Barf, invoke GC */ + if(sm = sbm_mget(csiz,cmax)) /* Then try again */ + return(sm); + if((csiz >>= 1) < cmin) /* If still short, reduce */ + csiz = cmin; /* request down to min */ + } +} + +chroff sbv_taddr; /* Disk addr of place to write into (set by TSET) */ +struct sdblk *sbv_tsd; /* SD that disk addr comes after (set by TSET) */ + +#define sbx_qlk(sd) (sd->sdflags&SD_LOCKS) + +#if 0 + This is the compaction routine, which is the key to the +entire scheme. Paging text to and from disk is trivial, but the +ability to merge blocks is the important thing since it allows +flushing the pointer information as well as the actual text! This +eliminates fragmentation as a fatal problem. + There are a variety of ways that storage can be reclaimed: + +- "pure" in-core blocks can be flushed instantly. +- "impure" incore blocks can be written to tempfile storage and flushed. +- The SM node freelist can be compacted so as to flush memory which is + used for nothing but holding free nodes. +- The SD node freelist can be compacted, ditto. +- SBBUFs can be compacted, by: + - Merging logically & physically adjacent on-disk pieces + - merging logically & physically adjacent in-core pieces + - merging logically adjacent in-core pieces + - merging logically adjacent disk pieces, by reading in + and then writing out to tempfile storage. + Worst case would reduce whole sbstr to single tempfile block. + +Problems: + What is "optimal" algorithm for typical usage? + Must go over all code to make sure right things get locked + and unlocked to avoid having rug pulled out from under. + Could have optional "registration table" for sbstruc; if exist + in table, can check during GC. If find one, can first + do sbx_smdisc and then repoint sbcur to 1st block, + with sbdot of 0 and sboff of sb_tell(). This allows + reducing whole thing to one block even tho "locked". + Never touch stuff locked with SD_LCK2, though. + Also may need way to protect the sbstr SD actually being + pointed to by current sbx routine processing. + Could have count of # nodes free for SM and SD; don''t GC + unless # is some number greater than size of a node block! + Have different levels of compaction; pass level # down thru calls + so as to invoke progressively sterner compaction measures. + Can invoke sbx_comp with any particular level! + Must have list somewhere of SBBUFs? or maybe OK to scan core + for SM_DNODS, then scan sdblks? + Screw: could happen that stuff gets flushed (cuz pure) or even + written out to tempfile, and then we have to read it back + in so as to compact more stuff into tempfile... how to avoid? + If pure stuff small and next to impure stuff, merge? + Some calls just want to get another free node and don''t need + new core. How to indicate this? How to decide between + freeing up used nodes, and creating new node freelist? +#endif /*COMMENT*/ +/* Compact stuff. + * General algorithm for getting storage is: + * 1) allocate from freelist if enough there + * 2) find unlocked pure smblk to free up + * 3) find unlocked impure smblks, write out. + * 4) Compact stuff by reducing # of sdblks. This is key to scheme! + * Otherwise fragmentation will kill program. + * Maybe put age cnt in each sbstr? Bump global and set cntr each time + * sbstr gets major hacking (not just getc/putc). + */ +extern struct smblk *sbm_list; +sbx_comp(cmin,lev) +int cmin, lev; +{ int sbx_sdgc(); + + if(lev > 100) /* If program has no way to handle this, */ + abort(); /* then simply blow up. */ + if(lev > 10) /* Too many iterations? Try to warn. */ + return(sbx_err(0,"GC loop, cannot free block of size %d", + cmin)); + + /* Step thru core hunting for SD node blocks */ + sbm_nfor(SM_DNODS,sizeof(struct sdblk),sbx_sdgc,lev); +} + +/* Do GC stuff on a sdblk. Guaranteed to exist, but may be locked */ +sbx_sdgc(sdp,lev) +struct sdblk *sdp; +int lev; +{ register struct sdblk *sd, *sdf; + register struct smblk *sm; + struct smblk *smf, *sbm_exp (); + SBMO more; + + sd = sdp; + if(sbx_qlk(sd)) return(0); + sm = sd->sdmem; + sdf = sd->slforw; + if (lev < 4) goto lev3; + + /* Level 4 - write out everything possible */ + /* Back up to start of sbstr */ + while((sdf = sd->slback) && !sbx_qlk(sdf)) + sd = sdf; + if((sdf = sd->slforw) == 0 /* If only 1 blk, ensure on disk */ + || sbx_qlk(sdf)) + { if(sm = sd->sdmem) + { if(sd->sdflags&SD_MOD) /* If impure, */ + sbx_aout(sd, 1); /* swap out the SD */ + sbm_mfree(sm); + sd->sdmem = 0; + } + return(0); + } + /* At least two blocks in string. Set up for flushout. */ + sbx_aout(sd, 0); /* Swapout as much of sbstring as possible */ + return(0); + +lev3: /* Level 3 - write out more */ +lev2: /* Level 2 - write out all impure & small pure */ +lev1: if(lev >= 1) /* Level 1 - merge small impure & small pure */ + { if(!sm || !sdf) return(0); + while(((smf = sdf->sdmem) && !(sdf->sdflags&SD_LOCKS) + && (more = smf->smuse + sm->smuse) < SB_BUFSIZ) ) + { if(sm->smforw != smf + && more > sm->smlen) /* If need more rm */ + { sm = sbm_exp(sm,more); /* Get it */ + if(!sm) return(0); /* If none, stop */ + sd->sdmem = sm; + } + bcopy(smf->smaddr, + sm->smaddr + sm->smuse, smf->smuse); + sm->smuse = more; + if(sm->smforw == smf) + { sdf->sdmem = 0; + sbm_mmrg(sm); /* Merge */ + if(sm->smlen > more+SB_SLOP) + sbm_mfree(sbm_split(sm, more)); + /* Guaranteed to win since mmrg + * just freed a mem node */ + } + sd->sdflags |= SD_MOD; + if(sdf = sbx_ndel(sdf)) + continue; + return(0); + } + } + + if(lev <= 0) /* Level 0 - free up large pure blocks */ + /* Also merge blocks which are adjacent on disk */ + { if(sm) + { if(sm->smuse == 0) + sd->sdlen = 0; + else if((sd->sdflags&SD_MOD) == 0 + && sm->smuse > 64) + { sbm_mfree(sm); + sd->sdmem = 0; + goto lev0adj; + } + else goto lev0adj; + } + + if(sd->sdlen == 0 /* Free zero blocks */ + && sd->slback) /* Make sure don't lose list */ + { sbx_ndel(sd); + if((sd = sdf) == 0) + return(0); + sdf = sd->slforw; + } + lev0adj: /* Merge blocks if adjacent on disk */ + /* This is common after reading thru large chunks + * of a file but not modifying it much. + */ + if((sd->sdflags&SD_MOD) == 0 /* Pure */ + && sdf && (sdf->sdflags&(SD_LOCKS|SD_MOD)) == 0 + && sd->sdfile && (sd->sdfile == sdf->sdfile) + && (sd->sdaddr + sd->sdlen) == sdf->sdaddr ) + { sd->sdlen += sdf->sdlen; + sbx_ndel(sdf); /* Flush 2nd */ + if(sm = sd->sdmem) + { sbm_mfree(sm); + sd->sdmem = 0; + } + } + return(0); + } + return(0); +} + +/* SBX_AOUT - output ALL of a hackable sbstring starting at given sdblk. + * Note that code is careful to do things so that an abort at any + * time (e.g. write error) will still leave sbstring in valid state. + * Flag value: + * 0 - Writes out as many unlocked sdblks as possible, and merges + * so that resulting sdblk (same one pointed to by arg) + * incorporates all stuff written out. + * 1 - Writes out single sdblk indicated, whether unlocked or not. + * Doesn't free mem or merge anything; does update physlist + * and flags. + * 2 - Writes out all sdblks to specified FD/offset, no mods at all, + * not even to physlist or flags. Good for saving files + * when something seems wrong. (How to pass fd/off args?) + * (offset arg not implemented, no need yet; 0 assumed) + * Returns 0 if successful, else UNIX system call error number. + */ + +sbx_aout(sdp,flag,fd) +struct sdblk *sdp; +int flag, fd; +{ register struct sdblk *sd; + register struct smblk *sm; + register int cnt; + int ifd, ofd, skflg, rem; + chroff inlen; + extern SBMA sbm_lowaddr; /* Need this from SBM for rndrem */ + char buf[SB_BUFSIZ+16]; /* Get buffer space from stack! */ + /* Allow extra for word-align reads. */ + /* This should be +WDSIZE, but some */ + /* C compilers (eg XENIX) can't handle */ + /* "sizeof" arith in allocation stmts! */ + + /* This flag and the two ptrs below are needed because UNIX + * maintains only one I/O ptr per open file, and we can sometimes + * be reading from/writing to the swapout file at same time. + * Using DUP() to get a new FD (to avoid seeking back and forth) + * won't help since both FD's will use the same I/O ptr!!! + * Lastly, can't depend on returned value of LSEEK to push/pop + * ptr, since V6 systems don't implement tell() or lseek() directly. + * So we have to do it by hand... + */ + int botchflg; + chroff outptr, inptr; + + if((sd = sdp)==0) return; + ofd = sbv_tf.sffd; /* Default output FD */ + if(flag==0) + { sbx_tset(sbx_qlen(sd),0);/* Find place for whole string */ + outptr = sbv_taddr; /* We'll have to update wrt ptr */ + } + else if (flag==1) /* Single SD block, so it's reasonable to + * try aligning the output with the input. */ + { if(sm = sd->sdmem) + { cnt = rndrem(sm->smaddr - sbm_lowaddr); + sbx_tset((chroff)(sm->smuse),cnt); + } + else + { cnt = rndrem(sd->sdaddr); + sbx_tset(sd->sdlen, cnt); + } + outptr = sbv_taddr; /* We'll have to update wrt ptr */ + } + else /* Outputting a whole sbstring to a file */ + { ofd = fd; + outptr = 0; + } + + for(; sd;) + { if(flag==0 && sbx_qlk(sd)) + break; /* Stop when hit locked sdblk */ + if(sm = sd->sdmem) + { if(cnt = sm->smuse) + if(write(ofd, sm->smaddr, cnt) != cnt) + return(sbx_err(errno,"Swapout wrt err")); + outptr += cnt; + if(flag==0) + { sd->sdmem = 0; /* Flush the mem used */ + sbm_mfree(sm); + } + inlen = cnt; + } + else if(inlen = sd->sdlen) + { if(sd->sdfile == 0) + return(sbx_err(errno,"Sdfile 0, SD %o",sd)); + /* Foo on UNIX */ + botchflg = ((ifd = sd->sdfile->sffd) == ofd) ? 1 : 0; + skflg = 1; /* Always seek first time */ + inptr = sd->sdaddr; + /* Efficiency hack - set up for first read so that + * transfer is word-aligned and terminates at end + * of a disk block. + */ + rem = rndrem(inptr); /* Get alignment */ + cnt = SB_BUFSIZ - (int)(inptr%SB_BUFSIZ); + while(inlen > 0) + { + if(inlen < cnt) cnt = inlen; + if(!sbx_rdf(ifd, buf+rem, cnt, skflg, inptr)) + return(sbx_err(errno,"Swapout err, SD %o",sd)); + /* Further seeks depend on botch setting */ + if(skflg = botchflg) + { if(lseek(ofd,outptr,0) < 0) + return(sbx_err(errno, + "Swapout sk err")); + inptr += cnt; + } + if(write(ofd, buf+rem, cnt) != cnt) + return(sbx_err(errno, + "Swapout wrt err")); + outptr += cnt; + inlen -= cnt; + cnt = SB_BUFSIZ; /* Now can use full blocks */ + rem = 0; /* Aligned nicely, too! */ + } + inlen = sd->sdlen; + } + + /* Text written out, now merge block in */ + if(flag == 2) /* No merge if saving file */ + goto donxt; + if(sd != sdp) /* First block? */ + { sdp->sdlen += inlen; /* No, simple merge */ + sd = sbx_ndel(sd); /* Flush, get next */ + continue; + } + + /* Handle 1st block specially */ + if(sd->sdfile /* Unlink from phys list */ + && sd != sbv_tsd) /* Don't unlink if self */ + sbx_npdel(sd); + sd->sdlen = inlen; + sd->sdfile = &sbv_tf; + sd->sdaddr = sbv_taddr; /* Set from sbx_tset val */ + sd->sdflags &= ~SD_MOD; /* On disk, no longer modified */ + + /* Now insert into phys list at specified place */ + if(sd == sbv_tsd) /* If already same place */ + goto next; /* Skip linkin. */ + if(sd->sdback = sbv_tsd) + { sd->sdforw = sbv_tsd->sdforw; + sd->sdback->sdforw = sd; + } + else + { sd->sdforw = sbv_tf.sfptr1; + sbv_tf.sfptr1 = sd; + } + if(sd->sdforw) + sd->sdforw->sdback = sd; + + next: if(flag==1) /* If only doing 1 sdblk, */ + break; /* stop here. */ + donxt: sd = sd->slforw; /* Done with 1st, get next */ + } + return(0); /* Win return, no errors */ +} + +/* Returns hackable length of a sbstring (ends at EOF or locked block) */ +chroff +sbx_qlen(sdp) +struct sdblk *sdp; +{ register struct sdblk *sd; + register struct smblk *sm; + chroff len; + + len = 0; + for(sd = sdp; sd && !sbx_qlk(sd); sd = sd->slforw) + if(sm = sd->sdmem) + len += (chroff)sm->smuse; + else len += sd->sdlen; + return(len); +} + + +/* SBX_TSET - finds a place on temp swapout file big enough to hold + * given # of chars. Sets SBV_TADDR to that location, as well + * as seeking to it so the next write call will output there. + * This location is guaranteed to have the requested + * byte alignment (0 = word-aligned). + */ +sbx_tset(loff, align) +chroff loff; +int align; +{ register int fd; + + if(sbv_tf.sffd <= 0) + { /* Must open the temp file! */ +/* Temporary file mechanism is system-dependent. Eventually this +** will probably require inclusion of a true c-env header file; for the +** time being, we can cheat a little by checking O_T20_WILD, which will +** be defined by on TOPS-20. Otherwise, we assume we are +** on a real Unix. +*/ +#ifdef O_T20_WILD + extern char *tmpnam(); /* Use ANSI function */ + fd = open(tmpnam((char *)NULL), + O_RDWR | O_CREAT | O_TRUNC | O_BINARY); + if(fd < 0) + return(sbx_err(0,"Swapout creat err")); +#else /* Real Unix */ + static char fcp[] = "/tmp/sbd.XXXXXX"; + if((fd = creat(mktemp(fcp),0600)) < 0) + return(sbx_err(0,"Swapout creat err")); + /* Must re-open so that we can both read and write to it */ + close(fd); + if((fd = open(fcp,2)) < 0) + return(sbx_err(0,"Swapout open err")); + unlink(fcp); /* Set so it vanishes when we do */ +#endif + + sbv_tf.sffd = fd; /* Initialize the sbfile struct */ + sbv_tf.sfptr1 = 0; + sbv_ftab[fd] = &sbv_tf; /* Record in table of all sbfiles */ + sbv_taddr = 0; /* "Return" this value */ + return; /* Ignore alignment for now */ + } + sbv_tsd = sbx_ffnd(&sbv_tf, loff+align, &sbv_taddr); + sbv_taddr += align; + if(lseek(sbv_tf.sffd, sbv_taddr, 0) < 0) + return(sbx_err(0,"Swapout seek err: (%d,%ld,0) %d %s", + sbv_tf.sffd, sbv_taddr, errno, strerror(errno))); + +} + +/* SBX_FFND - searches disk list of given file for free space of + * at least size chars. Note that list must be sorted by ascending + * disk addrs in order for this to work! If sdaddrs are only + * changed in SBX_SPLIT this will be true. + * Sets "aloc" to disk address for writing (this is guaranteed to + * be word-aligned, for efficiency), and returns SD ptr to + * block which this addr should follow in the physical list. If ptr + * is 0, it means addr should be 1st thing in list. + */ +struct sdblk * +sbx_ffnd(sfp, size, aloc) +SBFILE *sfp; +chroff size, *aloc; +{ register struct sdblk *sd, *sds, *sdl; + chroff cur; + + cur = 0; + sds = 0; + sd = sfp->sfptr1; +redo: for(; sd ; sd = (sds=sd)->sdforw) + { if(cur < sd->sdaddr) /* Gap seen? */ + { if(size <= (sd->sdaddr - cur)) /* Yes, big enuf? */ + break; /* Yup! */ + } /* No, bump. */ + else if(cur >=(sd->sdaddr + sd->sdlen)) /* No gap but chk */ + continue; /* No overlap, ok */ + /* Bump to next possible gap. */ + cur = sd->sdaddr + sd->sdlen; + cur = (long)rndup(cur); /* Round up to word boundary! */ + } + *aloc = cur; /* Return winning addr */ + + /* Perform verification check -- make sure this really is OK + * and complain if not. If this never blows up, eventually can + * take the check out. + */ + sdl = sd; + for(sd = sfp->sfptr1; sd; sd = sd->sdforw) + { if(cur < sd->sdaddr) + { if(size <= (sd->sdaddr - cur)) + continue; + } + else if(cur >= (sd->sdaddr + sd->sdlen)) + continue; + + sbx_err(0,"FFND blew it, but recovered. SD %o siz %ld", + sd, size); + sd = (sds = sdl)->sdforw; + goto redo; + } + + + return(sds); /* Return ptr to block this addr follows */ +} + +sbx_rdf(fd,addr,cnt,skflg,loc) +register int fd; +char *addr; +int skflg; +chroff loc; +{ register int rres, eres; + long lres; + char *errtyp, *ftyp; + chroff curlen; + + errno = 0; + if(skflg && (lres = lseek(fd, (long)loc, 0)) == -1) + { errtyp = "Sk err"; + goto errhan; + } + if((rres = read(fd, addr, cnt)) != cnt) + { lres = rres; + errtyp = "Rd err"; + goto errhan; + } + return(rres); +errhan: /* Handle read or seek error */ + eres = errno; + if(fd == sbv_tf.sffd) /* See if dealing with swapout file */ + { ftyp = "(swap)"; + curlen = 0; + } + else { /* No, normal buffer file. */ + ftyp = ""; + curlen = sbx_fdlen(fd); + if(sbv_ftab[fd] && + (curlen != sbv_ftab[fd]->sflen)) /* File changed? */ + if(sbx_rugpull(fd)) /* Yes, handle special case */ + return(cnt); /* Allow "win" return */ + } + sbx_err(0,"%s %d:%s, %ld:(%d%s,%o,%d)=%ld (fl %ld)", + errtyp, eres, strerror(eres), + loc, fd, ftyp, addr, cnt, lres, + curlen); + return(0); +} + +/* SBX_RUGPULL(fd) - Special routine called when package detects that + * the file indicated by the fd has changed since its original + * opening. This can happen when a file is over-written by some + * other program (ED, for example). + * This means that all sdblks which reference this fd + * are probably bad. Pass special error back up to the calling + * program to give it a chance at doing something. + * Extra credit: scan all sdblks and unpurify all which point to this + * file, so as to protect everything we still remember about it. + * Otherwise a GC could flush pure in-core portions. + */ +sbx_rugpull(fd) /* FD already known to have entry in sbv_ftab */ +register int fd; +{ int sbx_unpur(); + + /* First scan all sdblks to save what we still have. */ + /* This does NOT remove the sdfile pointer, so we can still + * find blocks that are affected. */ + sbm_nfor(SM_DNODS, sizeof(struct sdblk), sbx_unpur, sbv_ftab[fd]); + + if((int)sbv_debug == 1 || !sbv_debug) + return(0); /* Nothing else we can do */ + return((*sbv_debug)(2,(int *)0,"",fd)); /* Let caller handle it */ +} +sbx_unpur(sd, sf) /* Auxiliary routine for SBX_RUGPULL */ +register struct sdblk *sd; +register struct sbfile *sf; +{ if(sd->sdfile == sf /* If sdblk belongs to affected file */ + && sd->sdmem) /* and has in-core version of text, */ + sd->sdflags |= SD_MOD; /* then ensure core version is used */ +} + +sbx_err(val,str,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12) +char *str; +{ int *sptr; + + sptr = (int *) &sptr; /* Point to self on stack */ + sptr += 5; /* Point to return addr */ + if((int)sbv_debug == 1) + { abort(); + } + if(sbv_debug) + (*sbv_debug)(1,*sptr,str,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9,a10,a11,a12); + return(val); +} diff --git a/commands/elle/sbvall.c b/commands/elle/sbvall.c new file mode 100755 index 000000000..ce5172829 --- /dev/null +++ b/commands/elle/sbvall.c @@ -0,0 +1,42 @@ +/* VALLOC - Aligned memory allocator + * Emulation of the 4.2BSD library routine of the same name. + * Copyright 1985 by Ken Harrenstien, SRI International + * This software is quasi-public; it may be used freely with + * like software, but may NOT be sold or made part of licensed + * products without permission of the author. In all cases + * the source code and any modifications thereto must remain + * available to any user. + * + * This is part of the SB library package. + * Any software using the SB library must likewise be made + * quasi-public, with freely available sources. + */ + +#include "sb.h" + +char * +valloc(size) +unsigned size; +{ register int pagmsk; + register SBMO i; + register struct smblk *sm, *smr; + struct smblk *sbm_mget(), *sbm_split(); + + pagmsk = getpagesize() - 1; /* Get page size in bytes, less 1 */ + if(!(sm = sbm_mget(size+pagmsk, size+pagmsk))) /* Get area big enuf */ + return(0); + /* Now find # bytes prior to 1st page boundary. + * This expression gives 0 if already at boundary, else #-1. + */ + i = pagmsk - ((int)(sm->smaddr) & pagmsk); + if(i) /* If need to split off preceding stuff, */ + { smr = sbm_split(sm, i+1); /* do so (note i adjusted) */ + sbm_mfree(sm); /* Release preceding mem */ + if(!(sm = smr)) return(0); /* If couldn't split, fail */ + } + if(i = (sm->smlen - size)) /* See if any trailing stuff */ + { smr = sbm_split(sm, size); /* Yeah, split it off too */ + if(smr) sbm_mfree(smr); /* If couldn't split, excess OK. */ + } + return((char *)(sm->smaddr)); +} diff --git a/commands/elvis/Doc.sh b/commands/elvis/Doc.sh new file mode 100755 index 000000000..88479f19d --- /dev/null +++ b/commands/elvis/Doc.sh @@ -0,0 +1,3314 @@ +echo x - cflags.ms +sed '/^X/s///' > cflags.ms << '/' +X.Go 9 "CFLAGS" +X.PP +X\*E uses many preprocessor symbols to control compilation. +XSome of these control the sizes of buffers and such. +XThe "-DNO_XXXX" options remove small sets of related features. +X.PP +XMost \*E users will probably want to keep all features available. +XMinix-PC users, though, will have to sacrifice some sets because otherwise +X\*E would be too bulky to compile. +XThe "asld" phase of the compiler craps out. +X.IP "-DM_SYSV, -Dbsd, -DTOS, -DCOHERENT, -Damiga" +XThese flags tell the compiler that \*E is being compiled for +XSystem-V UNIX, BSD UNIX, Atari TOS, Coherent, or AmigaDos, respectively. +XFor other systems, the config.h file can generally figure it out automatically. +X.IP -DRAINBOW +XFor MS-DOS systems, this causes support for the DEC Rainbow to be compiled +Xinto \*E. +X.IP -DS5WINSIZE +XSome versions of SysV UNIX don't support support the "winsize" +Xstyle of screen-size testing, +Xso elvis ignores window size changes by default. +X.IP +XHowever, many of the newer SysV systems defines "winsize" in the +Xfile "/usr/include/sys/ptem.h". +XIf your SysV system has "winsize" then you should add +X-DS5SWINSIZE to the CFLAGS setting. +X.IP -DTERMIOS +XPOSIX is a SysV-derived specification which uses a terminal control +Xpackage called "termios", instead of "termio". +XSome other SysV systems may also use termios. +XYou can make elvis uses termios instead of the more common termio +Xby adding -DTERMIOS to CFLAGS. +X(Note: This hasn't been tested very well.) +X.IP -DNBUFS=\fInumber\fP +X\*E keeps most of your text in a temporary file; +Xonly a small amount is actually stored in RAM. +XThis flag allows you to control how much of the file can be in RAM at any time. +XThe default is 5 blocks, and the minimum is 3 blocks. +X(See the -DBLKSIZE flag, below.) +X.IP +XMore RAM allows global changes to happen a little faster. +X f you're just making many small changes in one section of a file, though, +Xextra RAM won't help much. +X.IP -DBLKSIZE=\fInumber\fP +XThis controls the size of blocks that \*E uses internally. +XThe value of BLKSIZE must be a power of two. +XEvery time you double BLKSIZE, you quadruple the size of a text file that +X\*E can handle, but you also cause the temporary file to grow faster. +XFor MS-DOS, Coherent, and Minix-PC, the default value is 1024, which allows +Xyou to edit files up to almost 512K bytes long. +XFor all other systems, the default value is 2048, which allows you to edit +Xfiles that are nearly 2 megabytes long. +X.IP +XThe BLKSIZE also determines the maximum line length, and a few other limits. +XBLKSIZE should be either 256, 512, 1024, or 2048. +XValues other than these can lead to strange behaviour. +X.IP -DTMPDIR=\fIstring\fP +XThis sets the default value of the "directory" option, which specifies where +Xthe temporary files should reside. +XThe value of TMPDIR must be a string, so be sure your value includes the +Xquote characters on each end. +X.IP "-DEXRC=\fIstr\fP, -DHMEXRC=\fIstr\fP, -DSYSEXRC=\fIstr\fP, -DEXINIT=\fIstr\fP" +XThis lets you control the names of the initialization files. +XTheir values must be strings, so be careful about quoting. +X.IP +XEXRC is the name of the initialization file in the current directory. +XIts default value is ".exrc" on UNIX systems -- the same as the real vi. +XSince that isn't a legal DOS filename, under DOS the default is "elvis.rc". +XFor other systems, check the config.h file. +X.IP +XHMEXRC is the name of the initialization file in your home directory. +XBy default, it is the same as EXRC. +X\*E will automatically prepend the name of your home directory to HMEXRC +Xat run time, so don't give a full path name. +X.IP +XSYSEXRC is the name of a system-wide initialization file. +XIt has no default value; +Xif you don't define a value for it, then +Xthe code that supports SYSEXRC just isn't compiled. +XThe value of SYSEXRC should be a full pathname, in quotes. +X.IP +XEXINIT is the name of an environment variable that can contain initialization +Xcommands. +XNormally, its value is "EXINIT". +X.IP -DKEYWORDPRG=\fIstring\fP +XThis flag determines the default value of the "keywordprg" option. +XIts value must be a string, so be careful about quoting. +XThe default value of this flag is "ref", which is a C reference program. +X.IP "-DCC_COMMAND=\fIstring\fP -DMAKE_COMMAND=\fIstring\fP -DERRLIST=\fIstring\fP" +XThese control the names of the C compiler, the "make" utility, and the +Xerror output file, respectively. +XThey are only used if -DNO_ERRLIST is not given. +X.IP +XThe default value of CC_COMMAND depends on the Operating System and compiler +Xthat you use to compile elvis; +Xfor UNIX, the default is "cc". +XThe default values of MAKE_COMMAND and ERRLIST are "make" and "errlist", +Xrespectively. +X.IP -DMAXRCLEN=\fInumber\fP +XThis determines how large a :@ macro command can be (measured in bytes). +XThe default is 1000 bytes. +XIf you increase this value significantly, +Xthen you may need to allocate extra memory for the stack. +XSee the "CHMEM" setting in the Makefile. +X.IP -DSHELL=\fIstring\fP +XThis is the default value of the "shell" option, and hence +Xthe default shell used from within \*E. +XThis only controls the default; +Xthe value you give here may be overridden at run-time by setting +Xan environment variable named SHELL (or COMSPEC for MS-DOS). +XIts value must be a string constant, so be careful about quoting. +X.IP -DTAGS=\fIstring\fP +XThis sets the name of the "tags" file, +Xwhich is used by the :tag command. +XIts value must be a string constant, so be careful about quoting. +X.IP "-DCS_IBMPC -DCS_LATIN1 -DCS_SPECIAL" +XThe digraph table and flipcase option will normally start out empty. +XHowever, if you add -DCS_IBMPC or -DCS_LATIN1 to your CFLAGS, +Xthen they will start out filled with values that are appropriate for the +XIBM PC character set or the ISO Latin-1 character set, respectively. +X.IP +XYou can also use -DCS_IBMPC and -DCS_SPECIAL together to get digraphs +Xthat produce the PC's graphic characters. +X.IP "-DDEBUG -DEBUG2" +X-DDEBUG adds the ":debug" and ":validate" commands, +Xand also adds many internal consistency checks. +XIt increases the size of the ".text" segment by about 6K. +X.IP +X-DDEBUG2 causes a line to be appended to a file called "debug.out" +Xeverytime any change is made to the edit buffer. +X.IP -DCRUNCH +XThis flag removes some non-critical code, so that \*E is smaller. +XFor example, it removes a short-cut from the regexp package, so that +Xtext searches are slower. +XAlso, screen updates are not as efficient. +XA couple of obscure features are disabled by this, too. +X.IP -DNO_MKEXRC +XThis removes the ":mkexrc" command, +Xso you have to create any .exrc files manually. +XThe size of the .text segment will be reduced by about 600 bytes. +X.IP -DNO_CHARATTR +XPermanently disables the charattr option. +XThis reduces the size of your ".text" segment by about 850 bytes. +X.IP -DNO_RECYCLE +XNormally, \*E will recycle space (from the temporary file) which contains +Xtotally obsolete text. +XThis flag disables this recycling. +XWithout recycling, the ".text" segment is about 1K smaller +Xthan it would otherwise be, +Xbut the tmp file grows much faster. +XIf you have a lot of free space on your hard disk, +Xbut \*E is too bulky to run with recycling, +Xthen try it without recycling. +X.IP +XWhen using a version of \*E that has been compiled with -DNO_RECYCLE, +Xyou should be careful to avoid making many small changes to a file +Xbecause each individual change will cause the tmp file to grow by at least 1k. +XHitting "x" thirty times counts as thirty changes, +Xbut typing "30x" counts as one change. +XAlso, you should occasionally do a ":w" followed by a ":e" to start with a +Xfresh tmp file. +X.IP +XInterestingly, the real vi never recycles space from its temporary file. +X.IP -DNO_SENTENCE +XLeaves out the "(" and ")" visual mode commands. +XAlso, the "[[", "]]", "{", and "}" commands will not recognize *roff macros. +XThe sections and paragraphs options go away. +XThis saves about 650 bytes in the ".text" segment. +X.IP -DNO_CHARSEARCH +XLeaves out the visual commands which locate a given character +Xin the current line: +X"f", "t", "F", "T", "," and ";". +XThis saves about 900 bytes. +X.IP -DNO_EXTENSIONS +XLeaves out the "K" and "#" visual commands. +XAlso, the arrow keys will no longer work in input mode. +XRegular expressions will no longer recognize the \\{\\} operator. +X(Other extensions are either inherent in the design of \*E, +Xor are controlled by more specific flags, +Xor are too tiny to be worth removing.) +XThis saves about 250 bytes. +X.IP -DNO_MAGIC +XPermanently disables the "magic" option, so that most meta-characters +Xin a regular expression are *NOT* recognized. +XThis saves about 3k of space in the ".text" segment, because +Xthe complex regular expression code can be replaced by much simpler code. +X.IP -DNO_SHOWMODE +XPermanently disables the "showmode" option, saving about 250 bytes. +X.IP -DNO_CURSORSHAPE +XNormally, \*E tries to adjust the shape of the cursor as a reminder +Xof which mode you're in. +XThe -DNO_CURSORSHAPE flag disables this, saving about 150 bytes. +X.IP -DNO_DIGRAPH +XTo allow entry of non-ASCII characters, \*E supports digraphs. +XA digraph is a single (non-ASCII) character which is entered as a +Xcombination of two other (ASCII) characters. +XIf you don't need to input non-ASCII characters, +Xor if your keyboard supports a better way of entering non-ASCII characters, +Xthen you can disable the digraph code and save about 450 bytes. +X.IP -DNO_ERRLIST +X\*E adds a ":errlist" command, which is useful to programmers. +XIf you don't need this feature, you can disable it via the -DNO_ERRLIST flag. +XThis will reduce the .text segment by about 900 bytes, and the .bss segment +Xby about 300 bytes. +X.IP -DNO_ABBR +XThe -DNO_ABBR flag disables the ":abbr" command, +Xand reduces the size of \*E by about 250 bytes. +X.IP -DNO_OPTCOLS +XWhen \*E displays the current options settings via the ":set" command, +Xthe options are normally sorted into columns. +XThe -DNO_OPTCOLS flag causes the options to be sorted across the rows, +Xwhich is much simpler for the computer. +XThe -DNO_OPTCOLS flag will reduce the size of your .text segment by about +X500 bytes. +X.IP -DNO_MODELINES +XThis removes all support for modelines. +X.IP -DNO_TAG +XThis disables tag lookup. +XIt reduces the size of the .text segment by about 750 bytes. +X.IP "-DNO_ALT_FKEY -DNO_CTRL_FKEY -DNO_SHIFT_FKEY -DNO_FKEY" +XThese remove explicit support of function keys. +X-DNO_ALT_FKEY removes support for the versions function keys. +X-DNO_CTRL_FKEY removes support for the and versions function keys. +X-DNO_SHIFT_FKEY removes support for the , , and versions function keys. +X-DNO_FKEY removes all support of function keys. +X.IP +X\*E's ":map" command normally allows you to use the special sequence "#" +Xto map function key . +XFor example, ":map #1 {!}fmt^M" will cause the key to reformat a paragraph. +X\*E checks the :k1=: field in the termcap description of your terminal +Xto figure out what code is sent by the key. +XThis is handy because it allows you to create a .exrc file which maps function +Xkeys the same way regardless of what type of terminal you use. +X.IP +XThat behaviour is standard; most implementations of the real vi supports it too. +X\*E extends this to allow you to use "#1s" to refer to +, +X"#1c" to refer to +, and +X"#1a" to refer to +. +XThe termcap description for the terminal should have fields named +X:s1=:c1=:a1=: respectively, to define the code sent by these key conbinations. +X(You should also have :k2=:s2=:c2=:a2=: for the key, and so on.) +X.IP +XBut there may be problems. +XThe terminfo database doesn't support :s1=:c1=:a1=:, so no terminfo terminal +Xdescription could ever support shift/control/alt function keys; +Xso you might as well add -DNO_SHIFT_FKEY to CFLAGS if you're using terminfo. +X.IP +XNote that, even if you have -DNO_FKEYS, you can still configure \*E to use +Xyour function keys my mapping the literal character codes sent by the key. +XYou just couldn't do it in a terminal-independent way. +XTERM_925 +X.IP "-DTERM_AMIGA -DTERM_VT100 -DTERM_VT52 etc." +XThe tinytcap.c file contains descriptions of several terminal types. +XFor each system that uses tinytcap, a reasonable subset of the available +Xdescriptions is actually compiled into \*E. +XIf you wish to enlarge this subset, then you can add the appropriate -DTERM_XXX +Xflag to your CFLAGS settings. +X.IP +XFor a list of the available terminal types, check the tinytcap.c file. +X.IP -DINTERNAL_TAGS +XNormally, \*E uses the "ref" program to perform tag lookup. +XThis is more powerful than the real vi's tag lookup, +Xbut it can be much slower. +X.IP +XIf you add -DINTERNAL_TAGS to your CFLAGS setting, +Xthen \* will use its own internal tag lookup code, which is faster. +X.IP -DPRSVDIR=\fIdirectory\fR +XThis controls where preserved files will be placed. +XAn appropriate default has been chosen for each Operating System, +Xso you probably don't need to worry about it. +X.IP -DFILEPERMS=\fInumber\fR +XThis affects the attributes of files that are created by \*E; +Xit is used as the second argument to the creat() function. +XThe default is 0666 which (on UNIX systems at least) means that +Xanybody can read or write the new file, but nobody can execute it. +XOn UNIX systems, the creat() call modifies this via the umask setting. +X.IP -DKEYBUFSIZE=\fInumber\fR +XThis determines the size of the type-ahead buffer that elvis uses. +XIt also limits the size of keymaps that it can handle. +XThe default is 1000 characters, which should be plenty. +/ +echo x - cutbufs.ms +sed '/^X/s///' > cutbufs.ms << '/' +X.Go 6 "CUT BUFFERS" +X.PP +XWhen \*E deletes text, it stores that text in a cut buffer. +XThis happens in both visual mode and EX mode. +XThere is no practical limit to how much text a cut buffer can hold. +X.PP +XThere are 36 cut buffers: +X26 named buffers ("a through "z), +X9 anonymous buffers ("1 through "9), +Xand 1 extra cut buffer (".). +X.PP +XIn EX mode, the :move and :copy commands use a cut buffer to temporarily +Xhold the text to be moved/copied. +X.NH 2 +XPutting text into a Cut Buffer +X.PP +XIn visual mode, text is copied into a cut buffer when you use the +Xd, y, c, C, s, or x commands. +XThere are also a few others. +X.PP +XBy default, the text goes into the "1 buffer. +XThe text that used to be in "1 gets shifted into "2, +X"2 gets shifted into "3, and so on. +XThe text that used to be in "9 is lost. +XThis way, the last 9 things you deleted are still accessible. +X.PP +XYou can also put the text into a named buffer -- "a through "z. +XTo do this, you should type the buffer's name +X(two keystrokes: a double-quote and a lowercase letter) +Xbefore the command that will cut the text. +XWhen you do this, "1 through "9 are not affected by the cut. +X.PP +XYou can append text to one of the named buffers. +XTo do this, type the buffer's name in uppercase +X(a double-quote and an uppercase letter) +Xbefore the d/y/c/C/s/x command. +X.PP +XThe ". buffer is special. +XIt isn't affected by the d/y/c/C/s/x command. +XInstead, it stores the text that you typed in +Xthe last time you were in input mode. +XIt is used to implement the . visual command, +Xand ^A in input mode. +X.PP +XIn EX mode (also known as colon mode), +Xthe :delete, :change, and :yank commands all copy text into a cut buffer. +XLike the visual commands, these EX commands normally use the "1 buffer, +Xbut you can use one of the named buffers by giving its name after the command. +XFor example, +X.sp 1 +X.ti +0.5i +X:20,30y a +X.sp +X.LP +Xwill copy lines 20 through 30 into cut buffer "a. +X.PP +XYou can't directly put text into the ". buffer, or the "2 through "9 buffers. +X.NH 2 +XPasting from a Cut Buffer +X.PP +XThere are two styles of pasting: +Xline-mode and character-mode. +XIf a cut buffer contains whole lines (from a command like "dd") +Xthen line-mode pasting is used; +Xif it contains partial lines (from a command like "dw") +Xthen character-mode pasting is used. +XThe EX commands always cut whole lines. +X.PP +XCharacter-mode pasting causes the text to be inserted into the line that +Xthe cursor is on. +X.PP +XLine-mode pasting inserts the text on a new line above or below the line +Xthat the cursor is on. +XIt doesn't affect the cursor's line at all. +X.PP +XIn visual mode, the p and P commands insert text from a cut buffer. +XUppercase P will insert it before the cursor, +Xand lowercase p will insert it after the cursor. +XNormally, these commands will paste from the "1 buffer, but you can +Xspecify any other buffer to paste from. +XJust type its name (a double-quote and another character) +Xbefore you type the P or p. +X.PP +XIn EX mode, the (pu)t command pastes text after a given line. +XTo paste from a buffer other that "1, +Xenter its name after the command. +X.NH 2 +XMacros +X.PP +XThe contents of a named cut buffer can be executed as a series of +Xex/vi commands. +X.PP +XTo put the instructions into the cut buffer, you must first insert +Xthem into the file, and then delete them into a named cut buffer. +X.PP +XTo execute a cut buffer's contents as EX commands, +Xyou should give the EX command "@" and the name of the buffer. +XFor example, :@z will execute "z as a series of EX commands. +X.PP +XTo execute a cut buffer's contents as visual commands, +Xyou should give the visual command "@" and the letter of the buffer's name. +XThe visual "@" command is different from the EX "@" command. +XThey interpret the cut buffer's contents differently. +X.PP +XThe visual @ command can be rather finicky. +XEach character in the buffer is interpretted as a keystroke. +XIf you load the instructions into the cut buffer via a "zdd command, +Xthen the newline character at the end of the line will be executed just +Xlike any other character, so the cursor would be moved down 1 line. +XIf you don't want the cursor to move down 1 line at the end of each +X@z command, then you should load the cut buffer by saying 0"zD instead. +X.PP +XAlthough cut buffers can hold any amount of text, +X\*E can only \fIexecute\fR small buffers. +XThe size limit is roughly 1000 characters, for either EX macros or VI macros. +XIf a buffer is too large to execute, an error message is displayed. +X.PP +XYou can't nest :@ commands. +XYou can't run :@ commands from your .exrc file, +Xor any other :source file either. +XSimilarly, you can't run a :source command from within an @ command. +XHopefully, these restrictions will be lifted in a later version. +X.NH 2 +XThe Effect of Switching Files +X.PP +XWhen \*E first starts up, all cut buffers are empty. +XWhen you switch to a different file +X(via the :n or :e commands perhaps) +Xthe 9 anonymous cut buffers are emptied again, +Xbut the other 27 buffers ("a through "z, and ".) retain their text. +/ +echo x - differ.ms +sed '/^X/s///' > differ.ms << '/' +X.Go 7 "DIFFERENCES BETWEEN \*E & BSD VI/EX" +X.PP +X\*E is not 100% compatible with the real vi/ex. +X\*E has many small extensions, some omissions, and a few features which +Xare implemented in a slightly different manner. +X.NH 2 +XExtensions +X.IP "Save Configuration" 1i +XThe :mkexrc command saves the current :set and :map configurations in +Xthe ".exrc" file in your current directory. +X.IP "Previous File" 1i +XThe :N or :prev command moves backwards through the args list. +X.IP "Center Current Row" 1i +XIn visual command mode, the (lowercase) "zz" command will center the current +Xline on the screen, like "z=". +X.IP "Changing Repeat Count" 1i +XThe default count value for . is the same as the previous command +Xwhich . is meant to repeat. +XHowever, you can supply a new count if you wish. +XFor example, after "3dw", "." will delete 3 words, +Xbut "5." will delete 5 words. +X.IP "Previous Text" 1i +XThe text which was most recently input +X(via a "cw" command, or something similar) +Xis saved in a cut buffer called ". (which +Xis a pretty hard name to write in an English sentence). +X.IP "Keyword Lookup" 1i +XIn visual command mode, you can move the cursor onto a word and press +Xshift-K to have \*E run a reference program to look that word up. +XThis command alone is worth the price of admission! +XSee the ctags and ref programs. +X.IP "Increment/Decrement" 1i +XIn visual command mode, you can move the cursor onto a number and +Xthen hit ## or #+ to increment that number by 1. +XTo increment it by a larger amount, +Xtype in the increment value before hitting the initial #. +XThe number can also be decremented or set by hitting #- or #=, respectively. +X.IP "Input Mode" 1i +XYou can backspace past the beginning of the line. +X.IP "" 1i +XThe arrow keys work in input mode. +X.IP "" 1i +XIf you type control-A, then the text that you input last time is inserted. +XYou will remain in input mode, so you can backspace over part of it, +Xor add more to it. +X(This is sort of like control-@ on the real vi, +Xexcept that control-A really works.) +X.IP "" 1i +XControl-P will insert the contents of the cut buffer. +X.IP "" 1i +XReal vi can only remember up to 128 characters of input, +Xbut \*E can remember any amount. +X.IP "" 1i +XThe ^T and ^D keys can adjust the indent of a line no matter where +Xthe cursor happens to be in that line. +X.IP "" 1i +XYou can save your file and exit \*E directly from input mode by hitting +Xcontrol-Z twice. +X.IP "" 1i +X\*E supports digraphs as a way to enter non-ASCII characters. +X.IP "Start in Input Mode" 1i +XIf you ":set inputmode" in your .exrc file, then \*E will start up in +Xinput mode instead of visual command mode. +X.IP "Visible Fonts" 1i +XWith ":set charattr", \*E can display "backslash-f" style character attributes on the +Xscreen as you edit. +XThe following example shows the recognized atributes: +X.sp +X.ti +0.5i +Xnormal \\fBboldface\\fR \\fIitalics\\fR \\fUunderlined\\fR normal +X.sp +XNOTE: you must compile \*E without the -DNO_CHARATTR flag for +Xthis to work. +X.IP "File Syncing" 1i +XAfter a crash, you can usually recover the altered form of the file +Xfrom the temporary file that \*E uses -- unless the temporary file was +Xcorrupted. +X.IP "" 1i +XUNIX systems use a delayed-write cache, which means that when \*E tries to +Xwrite to the temporary file, the information might still be in RAM instead +Xof on the disk. +XA power failure at that time would cause the in-RAM information to be lost. +XUNIX's sync() call will force all such information to disk. +X.IP "" 1i +XMS-DOS and Atari TOS don't write a file's length to disk until that file +Xis closed. +XConsequently, the temporary file would appear to be 0 bytes long if power +Xfailed when we were editing. +XTo avoid this problem, a sync() function has been written which will close +Xthe temporary file and then immediately reopen it. +X.IP "Cursor Shape" 1i +X\*E changes the shape of the cursor to indicate which mode you're in, +Xif your terminal's termcap entry includes the necessary capabilities. +X.IP "Hide nroff Lines" 1i +XTh ":set hideformat" option hides nroff format control lines. +X(They are displayed on the screen as blank lines.) +X.ne 7 +X.IP "Compiler Interface" 1i +X\*E is clever enough to parse the error messages emitted by many compilers. +XTo use this feature, +Xyou should collect your compiler's error messages into a file called "errlist"; +X\*E will read this file, +Xdetermine which source file caused the error messages, +Xstart editing that file, +Xmove the cursor to the line where the error was detected, +Xand display the error message on the status line. +XNifty! +X.IP "Visible Text Selection" 1i +XIn visual command mode, 'v' starts visibly selecting characters and +X\&'V' starts visibly selecting whole lines. +XThe character or line where the cursor is located becomes one +Xendpoint of the selection. +XYou can then use the standard cursor movement commands to move the cursor +Xto the other endpoint, and then press one of the operator commands +X(c/d/y//!/=/\\). +XThe operator will then immediately be applied to the selected text. +X.IP "Pop-up Menu Operator" 1i +XThe '\\' key is a new operator, +Xsimilar in operation to the c/d/y//! operators +XIt conjures up a menu, from which you can select any of the other +Xoperators plus a few other common commands. +X.IP "Preset Filter Operator" 1i +XThe '=' key is another new operator. +XIt is similar to the '!' operator, except that while +X\&'!' asks you to type in a filter command each time, +X\&'=' assumes it should always run the command stored in the \fIequalprg\fR option. +X.IP "Move to a Given Percentage" 1i +XThe '%' movement key can now accept an optional count. +XWithout a count, the '%' key still moves to a matching parenthesis +Xlike it always did. +XWith a count somewhere between 1 and 100, though, it moves the cursor to +Xapproximately a given percentage of the way through the file. +XFor example, typing "50%" will move the cursor to the middle of the file. +X.IP "Regular Expressions" +XIn regular expressions, several new forms of closure operators are supported: +X\\{\fIn\fR}, \\{\fIn\fR,\fIm\fR}, \\+, and \\?. +X.NH 2 +XOmissions +X.PP +XThe replace mode is a hack. +XIt doesn't save the text that it overwrites. +X.PP +XLong lines are displayed differently -- where the real vi would +Xwrap a long line onto several rows of the screen, \*E simply +Xdisplays part of the line, and allows you to scroll the screen +Xsideways to see the rest of it. +X.PP +XThe ":preserve" and ":recover" commands are missing. +XSo is the -r flag. +XI've never had a good reason to use ":preserve", +Xand since ":recover" is used so rarely +XI decided to implement it as a separate program. +XThere's no need to load the recovery code into memory every +Xtime you edit a file, I figured. +X.PP +XLISP support is missing. +XHowever, the = key is still an operator that reformats lines of text. +XBy default, it reformats lines by sending them through the \fIfmt\fP filter, +Xbut you could write your own LISP beautifier and configure elvis to use it. +XKey mappings could take care of most other differences. +XAuto-indent is the only thing that is irrecoverably lost. +X.PP +XAutoindent mode acts a little different from the real vi, anyway. +XIt doesn't handle ^^D or 0^D correctly. +XOn the other hand, it \fIdoes\fP allow ^D and ^T to be used anywhere in the +Xline, to adjust the indentation for the whole line. +/ +echo x - environ.ms +sed '/^X/s///' > environ.ms << '/' +X.Go 11 "ENVIRONMENT VARIABLES" +X.PP +X\*E examines several environment variables when it starts up. +XThe values of these variables are used internally for a variety +Xof purposes. +XYou don't need to define all of these; +Xon most systems, \*E only requires TERM to be defined. +XOn AmigaDOS, MS-DOS or TOS systems, even that is optional. +X.SH +XTERM, TERMCAP +X.PP +XTERM tells \*E the name of the termcap entry to use. +XTERMCAP may contain either the entire termcap entry, +Xor the full pathname of the termcap file to search through. +X.PP +XIf your version of \*E is using tinytcap instead of the full termcap library, +Xthen the value of TERMCAP \fIcannot\fR contain any backslash escapes (\\E, \\r, etc.) +Xor carat escapes (^[, ^M, etc.), because tinytcap doesn't understand them. +XInstead, you should embed the actual control character into the string. +X.SH +XTMP, TEMP +X.PP +XThese only work for AmigaDOS, MS-DOS and Atari TOS. +XEither of these variables may be used to set the "directory" option, +Xwhich controls where temporary files are stored. +XIf you define them both, then TMP is used, and TEMP is ignored. +X.SH +XLINES, COLUMNS +X.PP +XThe termcap entry for your terminal should specify the size of your screen. +XIf you're using a windowing interface, then there is an ioctl() call which +Xwill provide the size of the window; the ioctl() values will override the +Xvalues in the termcap entry. +XThe LINES and COLUMNS environment variables (if defined) +Xwill override either of these sources. +XThey, in turn, can be overridden by a ":set" command. +X.PP +XNormally, the LINES and COLUMNS variables shouldn't need to be defined. +X.SH +XEXINIT +X.PP +XThis variable's value may contain one or more colon-mode commands, +Xwhich will be executed after all of the ".exrc" files +Xbut before interactive editing begins. +X.PP +XTo put more than one command in EXINIT, you can separate the commands +Xwith either a newline or a '|' character. +X.SH +XSHELL, COMSPEC +X.PP +XYou can use COMSPEC in MS-DOS, or SHELL in any other system, +Xto specify which shell should be used for executing commands and +Xexpanding wildcards. +X.SH +XHOME +X.PP +XThis variable should give the full pathname of your home directory. +X\*E needs to know the name of your home directory so it can locate +Xthe ".exrc" file there. +X.SH +XTAGPATH +X.PP +XThis variable is used by the "ref" program. +XIt contains a list of directories that might contain a relevent "tags" file. +XUnder AmigaDOS, MS-DOS or Atari TOS, the names of the directories should be separated by +Xsemicolons (";"). +XUnder other operating systems, the names should be separated by colons (":"). +X.PP +XIf you don't define TAGPATH, then "ref" will use a default list which includes +Xthe current directory and a few other likely places. +XSee the definition of DEFTAGPATH at the start of ref.c for an accurate list. +/ +echo x - ex.ms +sed '/^X/s///' > ex.ms << '/' +X.Go 3 "COLON MODE COMMANDS" +X.ID +X.ps +X.in 0.8i +X.ta 2i 3.i +X.\" NOTE: The following macro is used to output a single line of the +X.\" command chart. Its usage is: +X.\" +X.\" .Cm ... +X.\" +X.de Cm +X.if "\\$1"0" \t\\$2\t\\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +X.if "\\$1"1" \s-2[line]\s+2\t\\$2\t\\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +X.if "\\$1"2" \s-2[line][,line]\s+2\t\\$2\t\\$3 \\$4 \\$5 \\$6 \\$7 \\$8 \\$9 +X.. +X.if t .ds Q `` +X.if t .ds U '' +X.if n .ds Q " +X.if n .ds U " +X\s+2LINES COMMAND ARGUMENTS\s-2 +X.Cm 0 ab[br] [short] [expanded form] +X.Cm 1 a[ppend][!] +X.Cm 0 ar[gs] [files] +X.Cm 0 cc [files] +X.Cm 0 cd[!] [directory] +X.Cm 2 c[hange] +X.Cm 0 chd[ir][!] [directory] +X.Cm 2 co[py] line +X.Cm 0 col[or] [when] [[\*Qlight\*U] color] [\*Qon\*U color] +X.Cm 2 d[elete] [\*Ux] +X.Cm 0 dig[raph][!] [XX [Y]] +X.Cm 0 e[dit][!] [file] +X.Cm 0 er[rlist][!] [errlist] +X.Cm 0 f[ile] [file] +X.Cm 2 g[lobal] /regexp/ command +X.Cm 1 i[nsert] +X.Cm 2 j[oin][!] +X.Cm 2 l[ist] +X.Cm 0 mak[e] [target] +X.Cm 0 map[!] key mapped_to +X.Cm 1 ma[rk] \*Ux +X.Cm 0 mk[exrc] +X.Cm 2 m[ove] line +X.Cm 0 n[ext][!] [files] +X.Cm 0 N[ext][!] +X.Cm 2 nu[mber] +X.Cm 2 p[rint] +X.Cm 1 pu[t] [\*Ux] +X.Cm 0 q[uit][!] +X.Cm 1 r[ead] file +X.Cm 0 rew[ind][!] +X.Cm 0 se[t] [options] +X.Cm 0 so[urce] file +X.Cm 2 s[ubstitute] /regexp/replacement/[p][g][c] +X.Cm 0 ta[g][!] tagname +X.Cm 0 una[bbr] [short] +X.Cm 0 u[ndo] +X.Cm 0 unm[ap][!] key +X.Cm 0 ve[rsion] +X.Cm 2 v[global] /regexp/ command +X.Cm 0 vi[sual] [filename] +X.Cm 0 wq +X.Cm 2 w[rite][!] [[>>]file] +X.Cm 0 x[it][!] +X.Cm 2 y[ank] [\*Ux] +X.Cm 2 ! command +X.Cm 2 < +X.Cm 2 = +X.Cm 2 > +X.Cm 2 & +X.Cm 0 @ "" \*Ux +X.DE +X.TA +X.PP +XTo use colon mode commands, you must switch from visual command +Xmode to colon command mode. +XThe visual mode commands to do this are ":" for a single colon command, +Xor "Q" for many colon mode commands. +X.NH 2 +XLine Specifiers +X.PP +XLine specifiers are always optional. +XThe first line specifier of most commands usually defaults to the current line. +XThe second line specifier usually defaults to be the same +Xas the first line specifier. +XExceptions are :write, :global, and :vglobal, which act on all lines of the +Xfile by default, and :!, which acts on no lines by default. +X.PP +XLine specifiers consist of an absolute part and a relative part. +XThe absolute part of a line specifier may be either an explicit line number, +Xa mark, a dot to denote the current line, a dollar sign to denote the last +Xline of the file, or a forward or backward search. +X.PP +XAn explicit line number is simply a decimal number, expressed as a +Xstring of digits. +X.PP +XA mark is typed in as an apostrophe followed by a letter. +XMarks must be set before they can be used. +XYou can set a mark in visual command mode by typing "m" and a letter, +Xor you can set it in colon command mode via the "mark" command. +X.PP +XA forward search is typed in as a regular expression surrounded by +Xslash characters; searching begins at the default line. +XA backward search is typed in as a regular expression surrounded by +Xquestion marks; searching begins at the line before the default line. +X.PP +XIf you omit the absolute part, then the default line is used. +X.PP +XThe relative part of a line specifier is typed as a "+" or "-" character +Xfollowed by a decimal number. +XThe number is added to or subtracted from the absolute part +Xof the line specifier to produce the final line number. +X.PP +XAs a special case, the % character may be used to specify all lines of the file. +XIt is roughly equivelent to saying 1,$. +XThis can be a handy shortcut. +X.PP +XSome examples: +X.LD +X.ps +X.ta 0.5i 1.8i +X :p print the current line +X :37p print line 37 +X :'gp print the line which contains mark g +X :/foo/p print the next line that contains "foo" +X :$p print the last line of the file +X :20,30p print lines 20 through 30 +X :1,$p print all lines of the file +X :%p print all lines of the file +X :/foo/-2,+4p print 5 lines around the next "foo" +X.TA +X.DE +X.NH 2 +XText Entry Commands +X.if n .ul 0 +X.ID +X.ps +X[line] append +X[line][,line] change ["x] +X[line] insert +X.DE +X.PP +XThe \fBa\fRppend command inserts text after the specified line. +X.PP +XThe \fBi\fRnsert command inserts text before the specified line. +X.PP +XThe \fBc\fRhange command copies the range of lines into a cut buffer, +Xdeletes them, and inserts new text where the old text used to be. +X.PP +XFor all of these commands, you indicate the end of the text you're +Xinserting by hitting ^D or by entering a line which contains only a +Xperiod. +X.NH 2 +XCut & Paste Commands +X.if n .ul 0 +X.ID +X.ps +X[line][,line] delete ["x] +X[line][,line] yank ["x] +X[line] put ["x] +X[line][,line] copy line +X[line][,line] to line +X[line][,line] move line +X.DE +X.PP +XThe \fBd\fRelete command copies the specified range of lines into a +Xcut buffer, and then deletes them. +X.PP +XThe \fBy\fRank command copies the specified range of lines into a cut +Xbuffer, but does *not* delete them. +X.PP +XThe \fBpu\fRt command inserts text from a cut buffer after the +Xspecified line. +X.PP +XThe \fBco\fRpy and \fBt\fRo commands yank the specified range of lines and +Xthen immediately paste them after some other line. +X.PP +XThe \fBm\fRove command deletes the specified range of lines and then +Ximmediately pastes them after some other line. +XIf the destination line comes after the deleted text, +Xthen it will be adjusted automatically to account for the deleted lines. +X.NH 2 +XDisplay Text Commands +X.if n .ul 0 +X.ID +X.ps +X[line][,line] print +X[line][,line] list +X[line][,line] number +X.DE +X.PP +XThe \fBp\fRrint command displays the specified range of lines. +X.PP +XThe \fBnu\fRmber command displays the lines, with line numbers. +X.PP +XThe \fBl\fRist command also displays them, but it is careful to make +Xcontrol characters visible. +X.NH 2 +XGlobal Operations Commands +X.if n .ul 0 +X.ID +X.ps +X[line][,line] global /regexp/ command +X[line][,line] vglobal /regexp/ command +X.DE +X.PP +XThe \fBg\fRlobal command searches through the lines of the specified range +X(or through the whole file if no range is specified) +Xfor lines that contain a given regular expression. +XIt then moves the cursor to each of these lines and +Xruns some other command on them. +X.PP +XThe \fBv\fRglobal command is similar, but it searches for lines that \fIdon't\fR +Xcontain the regular expression. +X.NH 2 +XLine Editing Commands +X.if n .ul 0 +X.ID +X.ps +X[line][,line] join[!] +X[line][,line] ! program +X[line][,line] < +X[line][,line] > +X[line][,line] substitute /regexp/replacement/[p][g][c] +X[line][,line] & +X.DE +X.PP +XThe \fBj\fRoin command catenates all lines in the specified range together +Xto form one big line. +XIf only a single line is specified, then the following line is catenated +Xonto it. +XThe normal ":join" inserts one or two spaces between the lines; +Xthe ":join!" variation (with a '!') doesn't insert spaces. +X.PP +XThe \fB!\fR command runs an external filter program, +Xand feeds the specified range of lines to it's stdin. +XThe lines are then replaced by the output of the filter. +XA typical example would be ":'a,'z!sort" to sort the lines 'a,'z. +X.PP +XThe \fB<\fR and \fB>\fR commands shift the specified range of lines left or right, +Xnormally by the width of 1 tab character. +XThe "shiftwidth" option determines the shifting amount. +X.PP +XThe \fBs\fRubstitute command finds the regular expression in each line, +Xand replaces it with the replacement text. +XThe "p" option causes the altered lines to be printed. +XThe "g" option permits all instances of the regular expression +Xto be found & replaced. +X(Without "g", only the first occurrence in each line is replaced.) +XThe "c" option asks for confirmation before each substitution. +X.PP +XThe \fB&\fR command repeats the previous substitution command. +XActually, "&" is equivelent to "s//~/" with the same options as last time. +XIt searches for the last regular expression that you specified for any purpose, +Xand replaces it with the the same text +Xthat was used in the previous substitution. +X.NH 2 +XUndo Command +X.if n .ul 0 +X.ID +X.ps +Xundo +X.DE +X.PP +XThe \fBu\fRndo command restores the file to the state it was in before +Xyour most recent command which changed text. +X.NH 2 +XConfiguration & Status Commands +X.if n .ul 0 +X.ID +X.ps +Xmap[!] [key mapped_to] +Xunmap[!] key +Xabbr [word expanded_form_of_word] +Xunabbr word +Xdigraph[!] [XX [Y]] +Xset [options] +Xmkexrc +X[line] mark "x +Xvisual +Xversion +X[line][,line] = +Xfile [file] +Xsource file +X@ "x +Xcolor [when] [["light"] color] ["on" color] +X.DE +X.PP +XThe \fBma\fRp command allows you to configure \*E to recognize your function keys, +Xand treat them as though they transmitted some other sequence of characters. +XNormally this mapping is done only when in the visual command mode, +Xbut with the [!] present it will map keys under input and replace modes as well. +XWhen this command is given with no arguments, +Xit prints a table showing all mappings currently in effect. +XWhen called with two arguments, the first is the sequence that your +Xfunction key really sends, and the second is the sequence that you want +X\*E to treat it as having sent. +XAs a special case, if the first argument is a number then \*E will map the +Xcorresponding function key; +Xfor example, ":map 7 dd" will cause the key to delete a line. +X.PP +XThe \fBunm\fRap command removes key definitions that were made via the map command. +X.PP +XThe \fBab\fRbr command is used to define/list a table of abbreviations. +XThe table contains both the abbreviated form and the fully spelled-out form. +XWhen you're in visual input mode, and you type in the abbreviated form, +X\*E will replace the abbreviated form with the fully spelled-out form. +XWhen this command is called without arguments, it lists the table; +Xwith two or more arguments, the first argument is taken as the abbreviated +Xform, and the rest of the command line is the fully-spelled out form. +X.PP +XThe \fBuna\fRbbr command deletes entries from the abbr table. +X.PP +XThe \fBdi\fRgraph command allows you to display the set of digraphs that \*E is +Xusing, or add/remove a digraph. +XTo list the set of digraphs, use the digraph command with no arguments. +XTo add a digraph, you should give the digraph command two arguments. +XThe first argument is the two ASCII characters that are to be combined; +Xthe second is the non-ASCII character that they represent. +XThe non-ASCII character's most significant bit is automatically set by the +Xdigraph command, unless to append a ! to the command name. +XRemoval of a digraph is similar to adding a digraph, except that you should +Xleave off the second argument. +X.PP +XThe \fBse\fRt command allows you examine or set various options. +XWith no arguments, it displays the values of options that have been changed. +XWith the single argument "all" it displays the values of all options, +Xregardless of whether they've been explicitly set or not. +XOtherwise, the arguments are treated as options to be set. +X.PP +XThe \fBmk\fRexrc command saves the current configuration to a file +Xcalled ".exrc" in the current directory. +X.PP +XThe mar\fBk\fR command defines a named mark to refer to a specific place +Xin the file. +XThis mark may be used later to specify lines for other commands. +X.PP +XThe \fBvi\fRsual command puts the editor into visual mode. +XInstead of emulating ex, \*E will start emulating vi. +X.PP +XThe \fBve\fRrsion command tells you that what version of \*E this is. +X.PP +XThe \fB=\fR command tells you what line you specified, or, +Xif you specified a range of lines, it will tell you both endpoints and +Xthe number of lines included in the range. +X.PP +XThe \fBf\fRile command tells you the name of the file, +Xwhether it has been modified, +Xthe number of lines in the file, +Xand the current line number. +XYou can also use it to change the name of the current file. +X.PP +XThe \fBso\fRurce command reads a sequence of colon mode commands from a file, +Xand interprets them. +X.PP +XThe \fB@\fR command executes the contents of a cut-buffer as EX commands. +X.PP +XThe \fBcol\fRor command only works under MS-DOS, or if you have an ANSI-compatible +Xcolor terminal. +XIt allows you to set the foreground and background colors +Xfor different types of text: +Xnormal, bold, italic, underlined, standout, pop-up menu, and visible selection. +XBy default, it changes the "normal" colors; +Xto change other colors, the first argument to the :color command should be +Xthe first letter of the type of text you want. +XThe syntax for the colors themselves is fairly intuitive. +XFor example, ":color light cyan on blue" causes normal text to be displayed +Xin light cyan on a blue background, and +X":color b bright white" causes bold text to be displayed in bright white on +Xa blue background. +XThe background color always defaults to the current background color of +Xnormal text. +XYour first :color command \fImust\fP specify both the foreground and background +Xfor normal text. +X.NH 2 +XMultiple File Commands +X.if n .ul 0 +X.ID +X.ps +Xargs [files] +Xnext[!] [files] +XNext[!] +Xprevious[!] +Xrewind[!] +X.DE +X.PP +XWhen you invoke \*E from your shell's command line, +Xany filenames that you give to \*E as arguments are stored in the args list. +XThe \fBar\fRgs command will display this list, or define a new one. +X.PP +XThe \fBn\fRext command switches from the current file to the next one +Xin the args list. +XYou may specify a new args list here, too. +X.PP +XThe \fBN\fRext and \fBpre\fRvious commands +X(they're really aliases for the same command) +Xswitch from the current file to the preceding file in the args list. +X.PP +XThe \fBrew\fRind command switches from the current file to the first file +Xin the args list. +X.NH 2 +XSwitching Files +X.if n .ul 0 +X.ID +X.ps +Xedit[!] [file] +Xtag[!] tagname +X.DE +X.PP +XThe \fBe\fRdit command allows to switch from the current file to some other file. +XThis has nothing to do with the args list, by the way. +X.PP +XThe \fBta\fRg command looks up a given tagname in a file called "tags". +XThis tells it which file the tag is in, and how to find it in that file. +X\*E then switches to the tag's file and finds the tag. +X.NH 2 +XWorking with a Compiler +X.if n .ul 0 +X.ID +X.ps +Xcc [files] +Xmake [target] +Xerrlist[!] [errlist] +X.DE +X.PP +XThe \fBcc\fR and \fBmak\fRe commands execute your compiler or "make" utility +Xand redirect any error messages into a file called "errlist". +XBy default, cc is run on the current file. +X(You should write it before running cc.) +XThe contents of the "errlist" file are then scanned for error messages. +XIf an error message is found, then the cursor is moved to the line where +Xthe error was detected, +Xand the description of the error is displayed on the status line. +X.PP +XAfter you've fixed one error, the \fBer\fRrlist command will move +Xthe cursor to the next error. +XIn visual command mode, +Xhitting `*' will do this, too. +X.PP +XYou can also create an "errlist" file from outside of \*E, +Xand use "\*E -m" to start elvis and have the cursor moved to the +Xfirst error. +XNote that you don't need to supply a filename with "\*E -m" because +Xthe error messages always say which source file an error is in. +X.PP +XNote: +XWhen you use errlist repeatedly to fix several errors in a single file, +Xit will attempt to adjust the reported line numbers to allow for lines +Xthat you have inserted or deleted. +XThese adjustments are made with the assumption that you will work though +Xthe file from the beginning to the end. +X.NH 2 +XExit Commands +X.if n .ul 0 +X.ID +X.ps +Xquit[!] +Xwq +Xxit +X.DE +X.PP +XThe \fBq\fRuit command exits from the editor without saving your file. +X.PP +XThe \fBwq\fR command writes your file out, then then exits. +X.PP +XThe \fBx\fRit command is similar to the \fBwq\fR command, except that +X\fBx\fRit won't bother to write your file if you haven't modified it. +X.NH 2 +XFile I/O Commands +X.if n .ul 0 +X.ID +X.ps +X[line] read file +X[line][,line] write[!] [[>>]file] +X.DE +X.PP +XThe \fBr\fRead command gets text from another file and inserts it +Xafter the specified line. +XIt can also read the output of a program; +Xsimply precede the program name by a '!' and use it in place of the file name. +X.PP +XThe \fBw\fRrite command writes the whole file, or just part of it, +Xto some other file. +XThe !, if present, will permit the lines to be written even if you've set +Xthe readonly option. +XIf you precede the filename by >> then the lines will be appended to the file. +XYou can send the lines to the standard input of a program by replacing the +Xfilename with a '!' followed by the command and its arguments. +X.PP +XNote: Be careful not to confuse ":w!filename" and ":w !command". +XTo write to a program, you must have at least one blank before the '!'. +X.NH 2 +XDirectory Commands +X.if n .ul 0 +X.ID +X.ps +Xcd [directory] +Xchdir [directory] +Xshell +X.DE +X.PP +XThe \fBcd\fR and \fBchd\fRir commands +X(really two names for one command) +Xswitch the current working directory. +X.PP +XThe \fBsh\fRell command starts an interactive shell. +X.NH 2 +XDebugging Commands +X.if n .ul 0 +X.ID +X.ps +X[line][,line] debug[!] +Xvalidate[!] +X.DE +X.PP +XThese commands are only available if you compile \*E with the -DDEBUG flag. +X.PP +XThe de\fBb\fRug command lists statistics for the blocks which contain +Xthe specified range of lines. +XIf the ! is present, then the contents of those blocks is displayed, too. +X.PP +XThe \fBva\fRlidate command checks certain variables for internal consistency. +XNormally it doesn't output anything unless it detects a problem. +XWith the !, though, it will always produce *some* output. +/ +echo x - index.ms +sed '/^X/s///' > index.ms << '/' +X.XS 1 +XINTRODUCTION +XWhat E\s-2LVIS\s+2 does, +XCopyright, +XHow to compile E\s-2LVIS\s+2, +XOverview +X.XA 2 +XVISUAL MODE COMMANDS +XNormal interactive editing, +XInput mode, +XArrow keys, +XDigraphs, +XAbbreviations, +XAuto-indentation +X.XA 3 +XCOLON MODE COMMANDS +XLine specifiers, +XText entry, +XCut & paste, +XDisplay text, +XGlobal operations, +XLine editing, +XUndo, +XConfiguration & status, +XMultiple files, +XSwitching files, +XWorking with a compiler, +XExiting, +XFile I/O, +XDirectory & shell, +XDebugging +X.XA 4 +XREGULAR EXPRESSIONS +XSyntax, +XOptions, +XSubstitutions, +XExamples +X.XA 5 +XOPTIONS +XAutoindent, +XAutoprint, +Xetc. +X.XA 6 +XCUT BUFFERS +XPutting text into a cut buffer, +XPasting from a cut buffer, +XMacros, +XThe effect of switching files +X.XA 7 +XDIFFERENCES BETWEEN E\s-2LVIS\s+2 AND THE REAL VI/EX +XExtensions, +XOmissions +X.XA 8 +XINTERNAL +XFor programmers only, +XThe temporary file, +XImplementation of editing, +XMarks and the cursor, +XColon command interpretation, +XScreen control, +XPortability +X.XA 9 +XCFLAGS +X.XA 10 +XTERMCAP +X.XA 11 +XENVIRONMENT VARIABLES +X.XA 12 +XVERSIONS +X.XA 13 +XQUESTIONS & ANSWERS +X.XE +X.PX +X.sp 0.3i +X.ce 1 +XUNIX-style "man" pages appear at the end of this manual. +/ +echo x - internal.ms +sed '/^X/s///' > internal.ms << '/' +X.Go 8 "INTERNAL" +X.PP +XYou don't need to know the material in this section to use \*E. +XYou only need it if you intend to modify \*E. +X.PP +XYou should also check out the CFLAGS, TERMCAP, ENVIRONMENT VARIABLES, +XVERSIONS, and QUIESTIONS & ANSWERS sections of this manual. +X.NH 2 +XThe temporary file +X.PP +XThe temporary file is divided into blocks of 1024 bytes each. +XThe functions in "blk.c" maintain a cache of the five most recently used blocks, +Xto minimize file I/O. +X.PP +XWhen \*E starts up, the file is copied into the temporary file +Xby the function \fBtmpstart()\fR in "tmp.c". +XSmall amounts of extra space are inserted into the temporary file to +Xinsure that no text lines cross block boundaries. +XThis speeds up processing and simplifies storage management. +XThe extra space is filled with NUL characters. +Xthe input file must not contain any NULs, to avoid confusion. +XThis also limits lines to a length of 1023 characters or less. +X.PP +XThe data blocks aren't necessarily stored in sequence. +XFor example, it is entirely possible that the data block containing +Xthe first lines of text will be stored after the block containing the +Xlast lines of text. +X.PP +XIn RAM, \*E maintains two lists: one that describes the "proper" +Xorder of the disk blocks, and another that records the line number of +Xthe last line in each block. +XWhen \*E needs to fetch a given line of text, it uses these tables +Xto locate the data block which contains that line. +X.PP +XBefore each change is made to the file, these lists are copied. +XThe copies can be used to "undo" the change. +XAlso, the first list +X-- the one that lists the data blocks in their proper order -- +Xis written to the first data block of the temp file. +XThis list can be used during file recovery. +X.PP +XWhen blocks are altered, they are rewritten to a \fIdifferent\fR block in the file, +Xand the order list is updated accordingly. +XThe original block is left intact, so that "undo" can be performed easily. +X\*E will eventually reclaim the original block, when it is no longer needed. +X.NH 2 +XImplementation of Editing +X.PP +XThere are three basic operations which affect text: +X.ID +X\(bu delete text - delete(from, to) +X\(bu add text - add(at, text) +X\(bu yank text - cut(from, to) +X.DE +X.PP +XTo yank text, all text between two text positions is copied into a cut buffer. +XThe original text is not changed. +XTo copy the text into a cut buffer, +Xyou need only remember which physical blocks that contain the cut text, +Xthe offset into the first block of the start of the cut, +Xthe offset into the last block of the end of the cut, +Xand what kind of cut it was. +X(Cuts may be either character cuts or line cuts; +Xthe kind of a cut affects the way it is later "put".) +XYanking is implemented in the function \fBcut()\fR, +Xand pasting is implemented in the function \fBpaste()\fR. +XThese functions are defined in "cut.c". +X.PP +XTo delete text, you must modify the first and last blocks, and +Xremove any reference to the intervening blocks in the header's list. +XThe text to be deleted is specified by two marks. +XThis is implemented in the function \fBdelete()\fR. +X.PP +XTo add text, you must specify +Xthe text to insert (as a NUL-terminated string) +Xand the place to insert it (as a mark). +XThe block into which the text is to be inserted may need to be split into +Xas many as four blocks, with new intervening blocks needed as well... +Xor it could be as simple as modifying a single block. +XThis is implemented in the function \fBadd()\fR. +X.PP +XThere is also a \fBchange()\fR function, +Xwhich generally just calls delete() and add(). +XFor the special case where a single character is being replaced by another +Xsingle character, though, change() will optimize things somewhat. +XThe add(), delete(), and change() functions are all defined in "modify.c". +X.PP +XThe \fBinput()\fR function reads text from a user and inserts it into the file. +XIt makes heavy use of the add(), delete(), and change() functions. +XIt inserts characters one at a time, as they are typed. +X.PP +XWhen text is modified, an internal file-revision counter, called \fBchanges\fR, +Xis incremented. +XThis counter is used to detect when certain caches are out of date. +X(The "changes" counter is also incremented when we switch to a different file, +Xand also in one or two similar situations -- all related to invalidating caches.) +X.NH 2 +XMarks and the Cursor +X.PP +XMarks are places within the text. +XThey are represented internally as 32-bit values which are split +Xinto two bitfields: +Xa line number and a character index. +XLine numbers start with 1, and character indexes start with 0. +XLines can be up to 1023 characters long, so the character index is 10 bits +Xwide and the line number fills the remaining 22 bits in the long int. +X.PP +XSince line numbers start with 1, +Xit is impossible for a valid mark to have a value of 0L. +X0L is therefore used to represent unset marks. +X.PP +XWhen you do the "delete text" change, any marks that were part of +Xthe deleted text are unset, and any marks that were set to points +Xafter it are adjusted. +XMarks are adjusted similarly after new text is inserted. +X.PP +XThe cursor is represented as a mark. +X.NH 2 +XColon Command Interpretation +X.PP +XColon commands are parsed, and the command name is looked up in an array +Xof structures which also contain a pointer to the function that implements +Xthe command, and a description of the arguments that the command can take. +XIf the command is recognized and its arguments are legal, +Xthen the function is called. +X.PP +XEach function performs its task; this may cause the cursor to be +Xmoved to a different line, or whatever. +X.NH 2 +XScreen Control +X.PP +XIn input mode or visual command mode, +Xthe screen is redrawn by a function called \fBredraw()\fR. +XThis function is called in the getkey() function before each keystroke is +Xread in, if necessary. +X.PP +XRedraw() write to the screen via a package which looks like the "curses" +Xlibrary, but isn't. +XIt is actually much simpler. +XMost curses operations are implemented as macros which copy characters +Xinto a large I/O buffer, which is then written with a single large +Xwrite() call as part of the refresh() operation. +X.PP +X(Note: Under MS-DOS, the pseudo-curses macros check to see whether you're +Xusing the pcbios interface. If you are, then the macros call functions +Xin "pc.c" to implement screen updates.) +X.PP +XThe low-level functions which modify text (namely add(), delete(), and change()) +Xsupply redraw() with clues to help redraw() decide which parts of the +Xscreen must be redrawn. +XThe clues are given via a function called \fBredrawrange()\fR. +X.PP +XMost EX commands use the pseudo-curses package to perform their output, +Xlike redraw(). +X.PP +XThere is also a function called \fBmsg()\fR which uses the same syntax as printf(). +XIn EX mode, msg() writes message to the screen and automatically adds a +Xnewline. +XIn VI mode, msg() writes the message on the bottom line of the screen +Xwith the "standout" character attribute turned on. +X.NH 2 +XOptions +X.PP +XFor each option available through the ":set" command, +X\*E contains a character array variable, named "o_\fIoption\fR". +XFor example, the "lines" option uses a variable called "o_lines". +X.PP +XFor boolean options, the array has a dimension of 1. +XThe first (and only) character of the array will be NUL if the +Xvariable's value is FALSE, and some other value if it is TRUE. +XTo check the value, just by dereference the array name, +Xas in "if (*o_autoindent)". +X.PP +XFor number options, the array has a dimension of 3. +XThe array is treated as three unsigned one-byte integers. +XThe first byte is the current value of the option. +XThe second and third bytes are the lower and upper bounds of that +Xoption. +X.PP +XFor string options, the array usually has a dimension of about 60 +Xbut this may vary. +XThe option's value is stored as a normal NUL-terminated string. +X.PP +XAll of the options are declared in "opts.c". +XMost are initialized to their default values; +Xthe \fBinitopts()\fR function is used to perform any environment-specific +Xinitialization. +X.NH 2 +XPortability +X.PP +XTo improve portability, \*E collects as many of the system-dependent +Xdefinitions as possible into the "config.h" file. +XThis file begins with some preprocessor instructions which attempt to +Xdetermine which compiler and operating system you have. +XAfter that, it conditionally defines some macros and constants for your system. +X.PP +XOne of the more significant macros is \fBttyread()\fR. +XThis macro is used to read raw characters from the keyboard, possibly +Xwith timeout. +XFor UNIX systems, this basically reads bytes from stdin. +XFor MSDOS, TOS, and OS9, ttyread() is a function defined in curses.c. +XThere is also a \fBttywrite()\fR macro. +X.PP +XThe \fBtread()\fR and \fBtwrite()\fR macros are versions of read() and write() that are +Xused for text files. +XOn UNIX systems, these are equivelent to read() and write(). +XOn MS-DOS, these are also equivelent to read() and write(), +Xsince DOS libraries are generally clever enough to convert newline characters +Xautomatically. +XFor Atari TOS, though, the MWC library is too stupid to do this, +Xso we had to do the conversion explicitly. +X.PP +XOther macros may substitute index() for strchr(), or bcopy() for memcpy(), +Xor map the "void" data type to "int", or whatever. +X.PP +XThe file "tinytcap.c" contains a set of functions that emulate the termcap +Xlibrary for a small set of terminal types. +XThe terminal-specific info is hard-coded into this file. +XIt is only used for systems that don't support real termcap. +XAnother alternative for screen control can be seen in +Xthe "curses.h" and "pc.c" files. +XHere, macros named VOIDBIOS and CHECKBIOS are used to indirectly call +Xfunctions which perform low-level screen manipulation via BIOS calls. +X.PP +XThe stat() function must be able to come up with UNIX-style major/minor/inode +Xnumbers that uniquely identify a file or directory. +X.PP +XPlease try to keep you changes localized, +Xand wrap them in #if/#endif pairs, +Xso that \*E can still be compiled on other systems. +XAnd PLEASE let me know about it, so I can incorporate your changes into +Xmy latest-and-greatest version of \*E. +/ +echo x - intro.ms +sed '/^X/s///' > intro.ms << '/' +X.Go 1 "INTRODUCTION" +X.PP +X\*E is a clone of vi/ex, the standard UNIX editor. +X\*E supports nearly all of the vi/ex commands, +Xin both visual mode and colon mode. +X.PP +XLike vi/ex, \*E stores most of the text in a temporary file, instead of RAM. +XThis allows it to edit files that are too large to fit +Xin a single process' data space. +XAlso, the edit buffer can survive a power failure or crash. +X.PP +X\*E runs under BSD UNIX, AT&T SysV UNIX, Minix, MS-DOS, Atari TOS, +XCoherent, OS9/68000, VMS and AmigaDos. +XThe next version is also expected to add MS-Windows, OS/2 and MacOS. +XContact me before you start porting it to some other OS, +Xbecause somebody else may have already done it for you. +X.PP +X\*E is freely redistributable, in either source form or executable form. +XThere are no restrictions on how you may use it. +X.NH 2 +XCompiling +X.PP +XSee the "Versions" section of this manual for instructions on how to compile +X\*E. +X.PP +XIf you want to port \*E to another O.S. or compiler, then +Xyou should start be reading the "Portability" part of the "Internal" section. +X.NH 2 +XOverview of \*E +X.PP +XThe user interface of \*E/vi/ex is weird. +XThere are two major command modes in \*E, and a few text input modes as well. +XEach command mode has a command which allows you to switch to the other mode. +X.PP +XYou will probably use the \fIvisual command mode\fR +Xmost of the time. +XThis is the mode that \*E normally starts up in. +X.PP +XIn visual command mode, the entire screen is filled with lines of text +Xfrom your file. +XEach keystroke is interpretted as part of a visual command. +XIf you start typing text, it will \fInot\fR be inserted, +Xit will be treated as part of a command. +XTo insert text, you must first give an "insert text" command. +XThis will take some getting used to. +X(An alternative exists. +XLookup the "inputmode" option.) +X.PP +XThe \fIcolon mode\fR is quite different. +X\*E displays a ":" character on the bottom line of the screen, as a prompt. +XYou are then expected to type in a command line and hit the key. +XThe set of commands recognized in the colon mode is different +Xfrom visual mode's. +/ +echo x - options.ms +sed '/^X/s///' > options.ms << '/' +X.Go 5 "OPTIONS" +X.PP +XOptions may be set or examined via the colon command "set". +XThe values of options will affect the operation of later commands. +X.PP +XFor convenience, options have both a long descriptive name and a short name +Xwhich is easy to type. +XYou may use either name interchangably. +XI like the short names, myself. +X.PP +XThere are three types of options: Boolean, string, and numeric. +XBoolean options are made TRUE by giving the name of the option as an +Xargument to the "set" command; +Xthey are made FALSE by prefixing the name with "no". +XFor example, "set autoindent" makes the autoindent option TRUE, +Xand "set noautoindent" makes it FALSE. +X\*E also allows boolean options to be toggled by prefixing the name with "neg". +XSo, ":map g :set neglist^M" will cause the key to alternately toggle the +X"list" option on and off. +X(The "neg" prefix is an extension; the real vi doesn't support it.) +X.PP +XTo change the value of a string or numeric option, pass the "set" command +Xthe name of the option, followed by an "=" sign and the option's new value. +XFor example, "set tabstop=8" will give the tabstop option a value of 8. +XFor string options, you may enclose the new value in quotes. +X.LD +X.ta 1.9i 2.4i 3.8i +X.ps +2 +X\fBNAMES TYPE DEFAULT MEANING\fP +X.ps +Xautoindent, ai Bool noai auto-indent during input +Xautoprint, ap Bool ap in EX, print the current line +Xautotab, at Bool at auto-indent allowed to use tabs? +Xautowrite, aw Bool noaw auto-write when switching files +Xbeautify, bf Bool nobf strip control chars from file? +Xcharattr, ca Bool noca interpret \\fX sequences? +Xcc, cc Str cc="cc -c" name of the C compiler +Xcolumns, co Num co=80 width of the screen +Xdigraph, dig Bool nodig recognize digraphs? +Xdirectory, dir Str dir="/usr/tmp" where tmp files are kept +Xedcompatible, ed Bool noed remember ":s//" options +Xequalprg, ep Bool ep="fmt" program to run for = operator +Xerrorbells, eb Bool eb ring bell on error +Xexrc, exrc Bool noexrc read "./.exrc" file? +Xexrefresh, er Bool er write lines indiviually in EX +Xflash, vbell Bool flash use visible alternative to bell +Xflipcase, fc Str fc="" non-ASCII chars flipped by ~ +Xhideformat, hf Bool hf hide text formatter commands +Xignorecase, ic Bool noic upper/lowercase match in search +Xinputmode, im Bool noim start vi in insert mode? +Xkeytime, kt Num kt=2 timeout for mapped key entry +Xkeywordprg, kp Str kp="ref" full pathname of shift-K prog +Xlines, ln Num ln=25 number of lines on the screen +Xlist, li Bool noli display lines in "list" mode +Xmagic, ma Bool ma use regular expression in search +Xmake, mk Str mk="make" name of the "make" program +Xmesg, ms Bool ms allow messages from other users? +Xmodelines, ml Bool noml are modelines processed? +Xmore, more Bool more pause between messages? +Xnovice, nov Bool nonovice set options for ease of use +Xparagraphs, para Str para="PPppIPLPQP" names of "paragraph" nroff cmd +Xprompt, pr Bool pr show ':' prompt in \fIex\fR mode +Xreadonly, ro Bool noro prevent overwriting of orig file +Xremap, rem Bool remap allow key maps to call key maps +Xreport, re Num re=5 report when 5 or more changes +Xruler, ru Bool noru display line/column numbers +Xscroll, sc Num sc=12 scroll amount for ^U and ^D +Xsections, sect Str sect="NHSHSSSEse" names of "section" nroff cmd +Xshell, sh Str sh="/bin/sh" full pathname of the shell +Xshowmatch, sm Bool nosm show matching ()[]{} +Xshowmode, smd Bool nosmd say when we're in input mode +Xshiftwidth, sw Num sw=8 shift amount for < and > +Xsidescroll, ss Num ss=8 amount of sideways scrolling +Xsync, sy Bool nosy call sync() often +Xtabstop, ts Num ts=8 width of tab characters +Xtaglength, tl Num tl=0 significant chars in tag name +Xterm, te Str te="$TERM" name of the termcap entry +Xterse, tr Bool notr give shorter error messages +Xtimeout, to Bool to distinguish from ? +Xwarn, wa Bool wa warn for ! if file modified +Xwindow, wi Num wi=24 lines to redraw after long move +Xwrapmargin, wm Num wm=0 wrap long lines in input mode +Xwrapscan, ws Bool ws at EOF, searches wrap to line 1 +Xwriteany, wr Bool nowr allow :w to clobber files +X.DE +X.TA +X.ne 6 +X.IP "autoindent, ai" +XDuring input mode, the autoindent option will cause each added line +Xto begin with the same amount of leading whitespace as the line above it. +XWithout autoindent, added lines are initially empty. +X.IP "autoprint, ap" +XThis option only affects EX mode. +XIf the autoprint option on, +Xand either the cursor has moved to a different line +Xor the previous command modified the file, +Xthen \*E will print the current line. +X.IP "autotab, at" +XThis option affects the behaviour of the autoindent mode. +XIf autoindent is turned off, then autotab has no effect. +X.IP +XWhen autotab is turned on, elvis will use a mixture of spaces and tabs +Xto create the proper amount of indentation. +XThis is the default. +X.IP +XWhen autotab is turned off, elvis will only use spaces for auto-indent. +X\*E will still insert a real tab character when you hit the key, though; +Xthe autotab option only affects \fIautomatic\fR indentation. +X.IP "autowrite, aw" +XWhen you're editing one file and decide to switch to another +X\- via the :tag command, or :next command, perhaps \- +Xif your current file has been modified, +Xthen \*E will normally print an error message and refuse to switch. +X.IP +XHowever, if the autowrite option is on, +Xthen \*E will write the modified version of the current file +Xand successfully switch to the new file. +X.IP "beautify, bf" +XThis option causes all control characters to be deleted from the text file, +Xat the time when you start editing it. +XIf you're already editing a file when you turn on the beautify option, +Xthen that file won't be affected. +X.IP cc +XThe :cc command runs the C compiler. +XThis option should be set to the name of your compiler. +X.IP "charattr, ca" +XMany text formatting programs allow you to designate portions of +Xyour text to be underlined, italicized, or boldface by embedding +Xthe special strings \\fU, \\fI, and \\fB in your text. +XThe special string \\fP marks the end of underlined or boldface text. +X.IP +X\*E normally treats those special strings just like any other text. +X.IP +XHowever, if the charattr option is on, then \*E will interpret +Xthose special strings correctly, +Xto display underlined or boldface text on the screen. +X(This only works, of course, if your terminal can display +Xunderlined and boldface, and if the TERMCAP entry says how to do it.) +X.IP "columns, co" +XThis option shows how wide your screen is. +X.IP "digraph, dig" +XThis option is used to enable/disable recognition of digraphs. +XThe default value is nodigraph, which means that digraphs will not be +Xrecognized. +X.IP "directory, dir" +X\*E stores text in temporary files. +XThis option allows you to control which directory those temporary files will +Xappear in. +XThe default is /usr/tmp. +X.IP +XThis option can only be set in a .exrc file; +Xafter that, \*E will have already started making temporary files +Xin some other directory, so it would be too late. +X.IP "edcompatible, ed" +XThis option affects the behaviour of the ":s/regexp/text/options" command. +XIt is normally off (:se noed) which causes all of the substitution options +Xto be off unless explicitly given. +X.IP +XHowever, with edcompatible on (:se ed), the substitution command remembers +Xwhich options you used last time. +XThose same options will continue to be used until you change them. +XIn edcompatible mode, when you explicitly give the name of a +Xsubstitution option, you will toggle the state of that option. +X.IP +XThis all seems very strange to me, but its implementation was almost free +Xwhen I added the ":&" command to repeat the previous substitution, +Xso there it is. +X.IP "equalprg, ep" +XThis holds the name & arguments of the external filter program +Xused the the visual = operator. +XThe defualt value is "fmt", +Xso the = operator will adjust line breaks in text. +X.IP "errorbells, eb" +X\*E normally rings a bell when you do something wrong. +XThis option lets you disable the bell. +X.IP exrc +XThis option specifies whether a .exrc file in the current directory +Xshould be executed. +XBy default, this option is off (":set noexrc") which prevents elvis from +Xexecuting .exrc in the current directory. +XIf the .exrc file in your home directory turns this option on (":set exrc") +Xthen the \*E will attempt to execute the .exrc file in the current directory. +X.IP +XThis option exist mainly for security reasons. +XA mean-spirited person could do something like +X.br +X echo >/tmp/.exrc '!rm -rf $HOME' +X.br +Xand then anybody who attempted to edit or view a file in the /tmp directory +Xwould lose most of their files. +XWith the exrc option turned off, this couldn't happen to you. +X.IP "exrefresh, er" +XThe EX mode of \*E writes many lines to the screen. +XYou can make \*E either write each line to the screen separately, +Xor save up many lines and write them all at once. +X.IP +XThe exrefresh option is normally on, so each line is written to the +Xscreen separately. +X.IP +XYou may wish to turn the exrefresh option off (:se noer) if the +X"write" system call is costly on your machine, or if you're using a +Xwindowing environment. +X(Windowing environments scroll text a lot faster when you write +Xmany lines at once.) +X.IP +XThis option has no effect in visual command mode or input mode. +X.IP "flash, vbell" +XIf your termcap entry describes a visible alternative to ringing +Xyour terminal's bell, then this option will say whether the visible +Xversion gets used or not. +XNormally it will be. +X.IP +XIf your termcap does NOT include a visible bell capability, +Xthen the flash option will be off, and you can't turn it on. +X.IP "flipcase, fc" +XThe flipcase option allows you to control how the non-ASCII characters are +Xaltered by the "~" command. +X.IP +XThe string is divided into pairs of characters. +XWhen "~" is applied to a non-ASCII character, +X\*E looks up the character in the flipcase string to see which pair it's in, +Xand replaces it by the other character of the pair. +X.IP "hideformat, hf" +XMany text formatters require you to embed format commands in your text, +Xon lines that start with a "." character. +X\*E normally displays these lines like any other text, +Xbut if the hideformat option is on, +Xthen format lines are displayed as blank lines. +X.IP "ignorecase, ic" +XNormally, when \*E searches for text, it treats uppercase letters +Xas being different for lowercase letters. +X.IP +XWhen the ignorecase option is on, uppercase and lowercase are treated as equal. +X.IP "inputmode, im" +XThis option allows you to have \*E start up in insert mode. +XYou can still exit insert mode at any time by hitting the ESC key, as usual. +XUsually, this option would be set in your ".exrc" file. +X.IP "keytime, kt" +XThe arrow keys of most terminals send a multi-character sequence. +XIt takes a measurable amount of time for these sequences to be transmitted. +XThe keytime option allows you to control the maximum amount of time +Xto allow for an arrow key (or other mapped key) to be received in full. +X.IP +XOn most systems, the setting is the number of tenths of a second to allow +Xbetween characters. +XOn some other systems, the setting is in whole seconds. +X.IP +XTry to avoid setting keytime=1. +XMost systems just count clock beats, so if you tried to read a character +Xshortly before a clock beat, you could allow almost no time at all for +Xreading the characters. +XFor higher keytime settings, the difference is less critical. +X.IP +XIf your system's response time is poor, you might want to increase the keytime. +XIn particular, I've found that when keystrokes must be sent through a network +X(via X windows, rlogin, or telnet, for example) the keytime should be set to +Xat least 1 second. +X.IP +XAs a special case, +Xyou can set keytime to 0 to disable this time limit stuff altogether. +XThe big problem here is: +XIf your arrow keys' sequences start with an ESC, +Xthen every time you hit your ESC key \*E will wait... and wait... +Xto see if maybe that ESC was part of an arrow key's sequence. +X.IP +XNOTE: this option is a generalization of the timeout option of the real vi. +X.IP "keywordprg, kp" +X\*E has a special keyword lookup feature. +XYou move the cursor onto a word, and hit shift-K, +Xand \*E uses another program to look up the word +Xand display information about it. +X.IP +XThis option says which program gets run. +X.IP +XThe default value of this option is "ref", +Xwhich is a program that looks up the definition of a function in C. +XIt looks up the function name in a file called "refs" which is created by ctags. +X.IP +XYou can subtitute other programs, such as an English dictionary program +Xor the online manual. +X\*E runs the program, using the keyword as its only argument. +XThe program should write information to stdout. +XThe program's exit status should be 0, unless you want \*E to print +X"<<< failed >>>". +X.IP "lines, ln" +XThis option says how many lines you screen has. +X.IP "list, li" +XIn nolist mode (the default), \*E displays text in a "normal" manner +X-- with tabs expanded to an appropriate number of spaces, etc. +X.IP +XHowever, sometimes it is useful to have tab characters displayed differently. +XIn list mode, tabs are displayed as "^I", +Xand a "$" is displayed at the end of each line. +X.IP "magic, ma" +XThe search mechanism in \*E can accept "regular expressions" +X-- strings in which certain characters have special meaning. +X.IP +XThe magic option is normally on, which causes these characters to be treated +Xspecially. +X.IP +XIf you turn the magic option off (:se noma), +Xthen all characters except ^ and $ are treated literally. +X^ and $ retain their special meanings regardless of the setting of magic. +X.IP "make, mk" +XThe :make command runs your "make" program. +XThis option defines the name of your "make" program. +X.IP mesg +XWith the real vi, running under real UNIX, +X":set nomesg" would prevent other users from sending you messages. +X\*E ignores it, though. +X.IP "modelines, ml" +X\*E supports modelines. +XModelines are lines near the beginning or end of your text file which +Xcontain "ex:yowza:", +Xwhere "yowza" is any EX command. +XA typical "yowza" would be something like "set ts=5 ca kp=spell wm=15". +XOther text may also appear on a modeline, +Xso you can place the "ex:yowza:" in a comment: +X.br +X.ID +X/* ex:set sw=4 ai: */ +X.DE +X.IP +XNormally these lines are ignored, for security reasons, +Xbut if you have "set modelines" in your .exrc file +Xthen "yowza" is executed. +X.IP "novice, nov" +XThe command ":set novice" is equivelent to ":set nomagic report=1 showmode". +X.IP "paragraphs, pa" +XThe { and } commands move the cursor forward or backward in increments +Xof one paragraph. +XParagraphs may be separated by blank lines, or by a "dot" command of +Xa text formatter. +XDifferent text formatters use different "dot" commands. +XThis option allows you to configure \*E to work with your text formatter. +X.IP +XIt is assumed that your formatter uses commands that start with a +X"." character at the front of a line, +Xand then have a one- or two-character command name. +X.IP +XThe value of the paragraphs option is a string in which each pair +Xof characters is one possible form of your text formatter's paragraph +Xcommand. +X.IP "more" +XWhen \*E must display a sequence of messages at the bottom line of the screen +Xin visual mode, it normally pauses after all but the last one, so you have +Xtime to read them all. +X.IP +XIf you turn off the "more" option, then \*E will not pause. +XThis means you can only read the last message, but it is usually the most +Ximportant one anyway. +X.IP "prompt, pr" +XIf you ":set noprompt", then \*E will no longer emit a ':' when it +Xexpects you to type in an \fIex\fR command. +XThis is slightly useful if you're using an astonishingly slow UNIX machine, +Xbut the rest of us can just ignore this one. +X.IP "readonly, ro" +XNormally, \*E will let you write back any file to which you have +Xwrite permission. +XIf you don't have write permission, then you can only write the changed +Xversion of the file to a \fIdifferent\fP file. +X.IP +XIf you set the readonly option, +Xthen \*E will pretend you don't have write permission to \fIany\fP file you edit. +XIt is useful when you really only mean to use \*E to look at a file, +Xnot to change it. +XThis way you can't change it accidentally. +X.IP +XThis option is normally off, unless you use the "view" alias of \*E. +X"View" is like "vi" except that the readonly option is on. +X.IP "remap" +XThe ":map" command allows you to convert one key sequence into another. +XThe remap option allows you to specify what should happen if portions of +Xthat other sequence are also in the map table. +XIf remap is on, then those portions will also be mapped, just as if they +Xhad been typed on the keyboard. +XIf remap is off, then the matching portions will not be mapped. +X.IP +XFor example, if you enter the commands ":map A B" and ":map B C", +Xthen when remap is on, A will be converted to C. +XBut when remap is off, A will be converted only to B. +X.IP "report, re" +XCommands in \*E may affect many lines. +XFor commands that affect a lot of lines, \*E will output a message saying +Xwhat was done and how many lines were affected. +XThis option allows you to define what "a lot of lines" means. +XThe default is 5, so any command which affects 5 or more lines will cause +Xa message to be shown. +X.IP "ruler, ru" +XThis option is normally off. +XIf you turn it on, then \*E will constantly display the line/column numbers +Xof the cursor, at the bottom of the screen. +X.IP "scroll, sc" +XThe ^U and ^D keys normally scroll backward or forward by half a screenful, +Xbut this is adjustable. +XThe value of this option says how many lines those keys should scroll by. +XIf you invoke ^U or ^D with a count argument (for example, "33^D") then +Xthis option's value is set to the count. +X.IP "sections, se" +XThe [[ and ]] commands move the cursor backward or forward in increments of +X1 section. +XSections may be delimited by a { character in column 1 +X(which is useful for C source code) +Xor by means of a text formatter's "dot" commands. +X.IP +XThis option allows you to configure \*E to work with your text formatter's +X"section" command, in exectly the same way that the paragraphs option makes +Xit work with the formatter's "paragraphs" command. +X.IP "shell, sh" +XWhen \*E forks a shell +X(perhaps for the :! or :shell commands) +Xthis is the program that is uses as a shell. +XThis is "/bin/sh" by default, +Xunless you have set the SHELL (or COMSPEC, for MS-DOS) environment variable, +Xit which case the default value is copied from the environment. +X.IP "shiftwidth, sw" +XThe < and > commands shift text left or right by some uniform number of columns. +XThe shiftwidth option defines that "uniform number". +XThe default is 8. +X.IP "showmatch, sm" +XWith showmatch set, +Xin input mode every time you hit one of )}], +X\*E will momentarily move the cursor to the matching ({[. +X.IP "showmode, smd" +XIn visual mode, it is easy to forget whether you're in the visual command mode +Xor input/replace mode. +XNormally, the showmode option is off, and you haven't a clue as to which mode +Xyou're in. +XIf you turn the showmode option on, though, a little message will appear in the +Xlower right-hand corner of your screen, telling you which mode you're in. +X.IP "sidescroll, ss" +XFor long lines, \*E scrolls sideways. +X(This is different from the real vi, +Xwhich wraps a single long line onto several rows of the screen.) +X.IP +XTo minimize the number of scrolls needed, +X\*E moves the screen sideways by several characters at a time. +XThe value of this option says how many characters' widths to scroll at a time. +X.IP +XGenerally, the faster your screen can be redrawn, +Xthe lower the value you will want in this option. +X.IP "sync, sy" +XIf the system crashes during an edit session, then most of your work +Xcan be recovered from the temporary file that \*E uses to store +Xchanges. +XHowever, sometimes the OS will not copy changes to the +Xhard disk immediately, so recovery might not be possible. +XThe [no]sync option lets you control this. +X.IP +XIn nosync mode (which is the default, for UNIX), \*E lets the operating system +Xcontrol when data is written to the disk. +XThis is generally faster. +X.IP +XIn sync mode (which is the default for MS-DOS, AmigaDos, and Atari TOS), +X\*E forces all changes out +Xto disk every time you make a change. +XThis is generally safer, but slower. +XIt can also be a rather rude thing to do on a multi-user system. +X.IP "tabstop, ts" +XTab characters are normally 8 characters wide, +Xbut you can change their widths by means of this option. +X.IP "taglength, tl" +XThis option allows you to specify how many characters of a tag's name +Xmust match when performing tag lookup. +XAs a special case, ":set taglength=0" means that all characters of a tag's +Xname must match. +X.IP +XNote: some configurations of \*E don't support this option. +X.IP "term, te" +XThis read-only option shows the name of the termcap entry that +X\*E is using for your terminal. +X.IP "terse, tr" +XThe real vi uses this option to select longer vs. shorter error messages. +X\*E has only one set of error messages, though, so this option has no effect. +X.IP "timeout, to" +XThe command ":set notimeout" is equivelent to ":set keytime=0", +Xand ":set timeout" is equivelent to ":set keytime=1". +XThis affects the behaviour of the key. +XSee the discussion of the "keytime" option for more information. +X.IP "warn, wa" +XIf you have modified a file but not yet written it back to disk, then +X\*E will normally print a warning before executing a ":!cmd" command. +XHowever, in nowarn mode, this warning is not given. +X.IP +X\*E also normally prints a message after a successful search that +Xwrapped at EOF. +XThe [no]warn option can also disable this warning. +X.IP "window, wi" +XThis option controls how many lines are redrawn after a long move. +X.IP +XOn fast terminals, this is usually set to the number of rows that the +Xterminal can display, minus one. +XThis causes the entire screen to be filled with text around the cursor. +X.IP +XOn slow terminals, you may wish to reduce this value to about 7 or so. +XThat way, if you're doing something like repeatedly hitting 'n' to search +Xfor each occurrence of some string and trying to find a particular occurrence, +Xthen you don't need to wait as long for \*E to redraw the screen after each +Xsearch. +X.IP "wrapmargin, wm" +XNormally (with wrapmargin=0) \*E will let you type in extremely long +Xlines, if you wish. +X.IP +XHowever, with warpmargin set to something other that 0 (wrapmargin=10 +Xis nice), \*E will automatically cause long lines to be "wrapped" +Xon a word break for lines come too close to the right-hand margin. +XFor example: On an 80-column screen, ":set wm=10" will cause lines to +Xwrap when their length exceeds 70 columns. +X.IP "wrapscan, ws" +XNormally, when you search for something, \*E will find it no matter +Xwhere it is in the file. +X\*E starts at the cursor position, and searches forward. +XIf \*E hits EOF without finding what you're looking for, +Xthen it wraps around to continue searching from line 1. +XIf you turn off the wrapscan option (:se nows), +Xthen when \*E hits EOF during a search, it will stop and say so. +X.IP "writeany, wr" +XWith "writeany" turned off, elvis will prevent you from accidentally +Xoverwriting a file. +XFor example, if "foo" exists then ":w foo" will fail. +XIf you turn on the "writeany" option, then ":w foo" will work. +X.IP +XRegardless of the setting of "writeany", though, ":w! foo" will work. +XThe '!' forces the ":w" command to write the file unless the operating system +Xwon't allow it. +/ +echo x - question.ms +sed '/^X/s///' > question.ms << '/' +X.nr Qn 0 1 +X.de QQ +X.sp +X.IP \fB\\n+(Qn) 0.3i +X.. +X.de AA +X.IP \fR 0.75i +X.. +X.Go 13 "QUESTIONS & ANSWERS" +X.QQ +XHow can I make elvis run faster under DOS? +X.AA +XThere are several things you can do. +XThe first thing to do is get a good screen driver such as NANSI.SYS. +XThis can speed up screen redrawing by as much as a factor of eight! +XThe DOS-specific part of section 12 tells you how to do this. +X.AA +XYou might also consider reducing the size of the blocks that elvis uses. +XYou'll need to recompile \*E to do this. +XThe default BLKSIZE is 1024 byte for the DOS version of \*E, which means +Xthat for each keystroke that you insert, elvis must shift an average of +Xabout 500 bytes. +XThat's a lot to ask from a little old 5MHz 8088. +XA BLKSIZE of 512 bytes might be more appropriate. +X.AA +XIf you're \fIreally\fR desperate for more speed, you might want to make +X\*E store its temporary files on a RAM disk. +XHowever, this limits the size of the file you can edit, and it eliminates any +Xchance you may have had to recover your work after a power failure +Xor system crash, but it might be worth it; you decide. +XTo do this, add ":set dir=R:\\" (or whatever your RAM disk's name is) +Xto the \fIelvis.rc\fP file. +X.AA +XNext, consider turning off the "sync" option. +XWhen the sync option is turned on, \*E will close the temporary file +Xand reopen it after every change, in order to force DOS to update +Xthe file's directory entry. +XIf you put ":set nosync" into the \fIelvis.rc\fP file, then elvis will +Xonly close the file when you start editing a different text file, or +Xwhen you're exiting \*E. +XConsequently, there is no chance that you'll be able to recover your +Xchanges after a power failure... so if you're going to this, then you +Xmight as well store the temp files on the RAM disk, too. +X.QQ +XWhere's the key on a DEC keyboard? +X.AA +XI don't know. Maybe the key? +XYou could always use ":map!" to make some other key act like the key. +XIf all else fails, use <[>. +X.QQ +XIs there a way to show which keys do what? +X.AA +XYes. The command ":map" will show what each key does in command mode, +Xand ":map!" (with an exclamation mark) shows what each key does in +Xinput mode. +X.AA +XThe table is divided into three columns: the key's label, the characters +Xthat it sends, and the characters that \*E pretends you typed. +X.QQ +XHow can I make \*E display long lines like the real vi? +X.AA +XYou can't yet. +XThe next version of \*E shouldsupport this, though. +X.QQ +XI can't recover my text [under MS-DOS or Atari TOS]. +XAccording to the directory listing, the temporary file is 0 bytes long. +XWhat went wrong? +X.AA +XMS-DOS and TOS only update a file's directory entry when the file is closed. +XIf the system crashes while the file is still open, then the file's length +Xis stored as 0 bytes. +XThe ":set sync" option is supposed to prevent this; +Xyou probably turned it off in the interest of speed, right? +X.AA +XUnder MS-DOS [I don't know about TOS], you should delete the empty +Xtemporary file, and then run CHKDSK/F. +XThis \fImight\fP find the data that belonged in the empty file, +Xand place it in a new file with a name like "000001.CHK" -- something like that. +XYou can then try to extract the text from that temporary file by giving the +Xcommand "elvprsv -R 000001.chk >goodnews.txt". +XIf you're lucky, then your text might be in GOODNEWS.TXT. +X.QQ +XWhat is the most current version of \*E? +X.AA +XEach version of \*E that is released to the public has a version number +Xof the form "number point number". +XAs I write this, the most current version of elvis is 1.5. +X.AA +XThe intermediate steps between one release and the next are labeled with +Xthe \fInext\fP version number, with a letter appended. +XFor example, after 1.4 was released, I started working on 1.5a. +XI am currently working on 2.0a. +XWhen \*E reaches a stable state, I'll call it 2.0 and release it. +X.AA +XSometimes a beta-test version of elvis will be available via anonymous FTP +Xfrom m2xenix.psg.com, in the directory "pub/elvis/beta". +X.QQ +XI only got executables, but now I want the source code. +XWhere can I get it? +X.AA +XIf you have access to the Internet, then you should be able to fetch it +Xfrom one of the public archives such as \fBplains.nodak.edu\fP. +XIt is accessible via anonymous FTP, or via an email server named +X"archive-server@plains.nodak.edu". +XElvis is located in the directory "/pub/Minix/all.contrib". +X.AA +XI will also offer it to the C Users' Group. +XThey sell C source code for us$8 per diskette +X(or slightly more outside North America). +XTheir phone number is (913) 841-1631, +Xand their address is: +X.ID +XThe C Users' Group +XPO Box 3127 +XLawrence KS 66046-0127 +X.DE +X.QQ +XIs this shareware, or public domain, or what? +X.AA +XIt is not public domain; it is copyrighted by me, Steve Kirkendall. +XHowever, this particular version is freely redistributable, in either +Xsource form or executable form. +X(I would prefer that you give copies away for free, complete with the +Xfull source code... but I'm not going to force you.) +X.AA +XIt is not shareware; you aren't expected to send me anything. +XYou can use it without guilt. +X.AA +XIt is not "copylefted." +XI hold a copyright, but currently I have not added any of the usual restrictions +Xthat you would find on copylefted software. +XIf people start doing really obnoxious things to \*E, then I will start +Xadding restrictions to subsequent versions, but earlier versions won't +Xbe affected. +X(So far, everybody has been pretty good about this so no restrictions +Xhave been necessary.) +X.QQ +XCan I reuse parts of your source code? +X.AA +XYes. Please be careful, though, to make sure that the code really is mine. +XSome of the code was contributed by other people, and I don't have the +Xauthority to give you permission to use it. +XThe author's name can be found near the top of each source file. +XIf it says "Steve Kirkendall" then you may use it; +Xotherwise, you'd better contact the author first. +X.AA +XPlease don't remove my name from the source code. +XIf you modify the source, please make a note of that fact in a comment +Xnear the top of the source code. +XAnd, finally, please mention my name in your documentation. +X.QQ +XCan \*E work with non-ASCII files? +X.AA +X\*E can't edit binary files because it can't handle the NUL character, +Xand because of line-length limitations. +XHowever, it is 8-bit clean so you should be able to edit any European +Xextended ASCII file without any surprises. +X.AA +X\*E has also been modified to work with 16-bit character sets. +XYongguang Zhang (ygz@cs.purdue.edu) has created a Chinese version of \*E +Xthat uses 16-bit characters and runs under cxterm (Chinese X-term) +Xon X-windows systems. +XJunichiro Itoh (itojun@foretune.co.jp) has modified \*E to edit Japanese +Xtext under MS-DOS. +/ +echo x - regexp.ms +sed '/^X/s///' > regexp.ms << '/' +X.Go 4 "REGULAR EXPRESSIONS" +X +X.PP +X\*E uses regular expressions for searching and substututions. +XA regular expression is a text string in which some characters have +Xspecial meanings. +XThis is much more powerful than simple text matching. +X.SH +XSyntax +X.PP +X\*E' regexp package treats the following one- or two-character +Xstrings (called meta-characters) in special ways: +X.IP "\\\\\\\\(\fIsubexpression\fP\\\\\\\\)" 0.8i +XThe \\( and \\) metacharacters are used to delimit subexpressions. +XWhen the regular expression matches a particular chunk of text, +X\*E will remember which portion of that chunk matched the \fIsubexpression\fP. +XThe :s/regexp/newtext/ command makes use of this feature. +X.IP "^" 0.8i +XThe ^ metacharacter matches the beginning of a line. +XIf, for example, you wanted to find "foo" at the beginning of a line, +Xyou would use a regular expression such as /^foo/. +XNote that ^ is only a metacharacter if it occurs +Xat the beginning of a regular expression; +Xanyplace else, it is treated as a normal character. +X.IP "$" 0.8i +XThe $ metacharacter matches the end of a line. +XIt is only a metacharacter when it occurs at the end of a regular expression; +Xelsewhere, it is treated as a normal character. +XFor example, the regular expression /$$/ will search for a dollar sign at +Xthe end of a line. +X.IP "\\\\\\\\<" 0.8i +XThe \\< metacharacter matches a zero-length string at the beginning of +Xa word. +XA word is considered to be a string of 1 or more letters and digits. +XA word can begin at the beginning of a line +Xor after 1 or more non-alphanumeric characters. +X.IP "\\\\\\\\>" 0.8i +XThe \\> metacharacter matches a zero-length string at the end of a word. +XA word can end at the end of the line +Xor before 1 or more non-alphanumeric characters. +XFor example, /\\/ would find any instance of the word "end", +Xbut would ignore any instances of e-n-d inside another word +Xsuch as "calendar". +X.IP "\&." 0.8i +XThe . metacharacter matches any single character. +X.IP "[\fIcharacter-list\fP]" 0.8i +XThis matches any single character from the \fIcharacter-list\fP. +XInside the \fIcharacter-list\fP, you can denote a span of characters +Xby writing only the first and last characters, with a hyphen between +Xthem. +XIf the \fIcharacter-list\fP is preceded by a ^ character, then the +Xlist is inverted -- it will match character that \fIisn't\fP mentioned +Xin the list. +XFor example, /[a-zA-Z]/ matches any letter, and /[^ ]/ matches anything +Xother than a blank. +X.IP "\\\\\\\\{\fIn\fP\\\\\\\\}" 0.8i +XThis is a closure operator, +Xwhich means that it can only be placed after something that matches a +Xsingle character. +XIt controls the number of times that the single-character expression +Xshould be repeated. +X.IP "" 0.8i +XThe \\{\fIn\fP\\} operator, in particular, means that the preceding +Xexpression should be repeated exactly \fIn\fP times. +XFor example, /^-\\{80\\}$/ matches a line of eighty hyphens, and +X/\\<[a-zA-Z]\\{4\\}\\>/ matches any four-letter word. +X.IP "\\\\\\\\{\fIn\fP,\fIm\fP\\\\\\\\}" 0.8i +XThis is a closure operator which means that the preceding single-character +Xexpression should be repeated between \fIn\fP and \fIm\fP times, inclusive. +XIf the \fIm\fP is omitted (but the comma is present) then \fIm\fP is +Xtaken to be inifinity. +XFor example, /"[^"]\\{3,5\\}"/ matches any pair of quotes which contains +Xthree, four, or five non-quote characters. +X.IP "*" 0.8i +XThe * metacharacter is a closure operator which means that the preceding +Xsingle-character expression can be repeated zero or more times. +XIt is equivelent to \\{0,\\}. +XFor example, /.*/ matches a whole line. +X.IP "\\\\\\\\+" 0.8i +XThe \\+ metacharacter is a closure operator which means that the preceding +Xsingle-character expression can be repeated one or more times. +XIt is equivelent to \\{1,\\}. +XFor example, /.\\+/ matches a whole line, but only if the line contains +Xat least one character. +XIt doesn't match empty lines. +X.IP "\\\\\\\\?" 0.8i +XThe \\? metacharacter is a closure operator which indicates that the +Xpreceding single-character expression is optional -- that is, that it +Xcan occur 0 or 1 times. +XIt is equivelent to \\{0,1\\}. +XFor example, /no[ -]\\?one/ matches "no one", "no-one", or "noone". +X.PP +XAnything else is treated as a normal character which must exactly match +Xa character from the scanned text. +XThe special strings may all be preceded by a backslash to +Xforce them to be treated normally. +X.SH +XSubstitutions +X.PP +XThe :s command has at least two arguments: a regular expression, +Xand a substitution string. +XThe text that matched the regular expression is replaced by text +Xwhich is derived from the substitution string. +X.br +X.ne 15 \" so we don't mess up the table +X.PP +XMost characters in the substitution string are copied into the +Xtext literally but a few have special meaning: +X.LD +X.ta 0.75i 1.3i +X & Insert a copy of the original text +X ~ Insert a copy of the previous replacement text +X \\1 Insert a copy of that portion of the original text which +X matched the first set of \\( \\) parentheses +X \\2-\\9 Do the same for the second (etc.) pair of \\( \\) +X \\U Convert all chars of any later & or \\# to uppercase +X \\L Convert all chars of any later & or \\# to lowercase +X \\E End the effect of \\U or \\L +X \\u Convert the first char of the next & or \\# to uppercase +X \\l Convert the first char of the next & or \\# to lowercase +X.TA +X.DE +X.PP +XThese may be preceded by a backslash to force them to be treated normally. +XIf "nomagic" mode is in effect, +Xthen & and ~ will be treated normally, +Xand you must write them as \\& and \\~ for them to have special meaning. +X.SH +XOptions +X.PP +X\*E has two options which affect the way regular expressions are used. +XThese options may be examined or set via the :set command. +X.PP +XThe first option is called "[no]magic". +XThis is a boolean option, and it is "magic" (TRUE) by default. +XWhile in magic mode, all of the meta-characters behave as described above. +XIn nomagic mode, only ^ and $ retain their special meaning. +X.PP +XThe second option is called "[no]ignorecase". +XThis is a boolean option, and it is "noignorecase" (FALSE) by default. +XWhile in ignorecase mode, the searching mechanism will not distinguish between +Xan uppercase letter and its lowercase form. +XIn noignorecase mode, uppercase and lowercase are treated as being different. +X.PP +XAlso, the "[no]wrapscan" option affects searches. +X.SH +XExamples +X.PP +XThis example changes every occurence of "utilize" to "use": +X.sp +X.ti +1i +X:%s/utilize/use/g +X.PP +XThis example deletes all whitespace that occurs at the end of a line anywhere +Xin the file. +X(The brackets contain a single space and a single tab.): +X.sp +X.ti +1i +X:%s/[ ]\\+$// +X.PP +XThis example converts the current line to uppercase: +X.sp +X.ti +1i +X:s/.*/\\U&/ +X.PP +XThis example underlines each letter in the current line, +Xby changing it into an "underscore backspace letter" sequence. +X(The ^H is entered as "control-V backspace".): +X.sp +X.ti +1i +X:s/[a-zA-Z]/_^H&/g +X.PP +XThis example locates the last colon in a line, +Xand swaps the text before the colon with the text after the colon. +XThe first \\( \\) pair is used to delimit the stuff before the colon, +Xand the second pair delimit the stuff after. +XIn the substitution text, \\1 and \\2 are given in reverse order +Xto perform the swap: +X.sp +X.ti +1i +X:s/\\(.*\\):\\(.*\\)/\\2:\\1/ +/ +echo x - termcap.ms +sed '/^X/s///' > termcap.ms << '/' +X.Go 10 "TERMCAP" +X.PP +X\*E uses fairly standard termcap fields for most things. +XI invented the cursor shape names +Xbut other than that there should be few surprises. +X.SH +XRequired numeric fields +X.if n .ul 0 +X.ID +X:co#: number of columns on the screen (chars per line) +X:li#: number of lines on the screen +X.DE +X.SH +XRequired string fields +X.ID +X.if n .ul 0 +X:ce=: clear to end-of-line +X:cl=: home the cursor & clear the screen +X:cm=: move the cursor to a given row/column +X:up=: move the cursor up one line +X.DE +X.SH +XBoolean fields +X.if n .ul 0 +X.ID +X:am: auto margins - wrap when char is written in last column? +X:xn: brain-damaged auto margins - newline ignored after wrap +X:pt: physical tabs? +X.DE +X.SH +XOptional string fields +X.if n .ul 0 +X.ID +X:al=: insert a blank row on the screen +X:dl=: delete a row from the screen +X:cd=: clear to end of display +X:ei=: end insert mode +X:ic=: insert a blank character +X:im=: start insert mode +X:dc=: delete a character +X:sr=: scroll reverse (insert row at top of screen) +X:vb=: visible bell +X:ti=: terminal initialization string, to start full-screen mode +X:te=: terminal termination, to end full-screen mode +X:ks=: enables the cursor keypad +X:ke=: disables the cursor keypad +X.DE +X.SH +XOptional strings received from the keyboard +X.if n .ul 0 +X.ID +X:kd=: sequence sent by the key +X:kl=: sequence sent by the key +X:kr=: sequence sent by the key +X:ku=: sequence sent by the key +X:kP=: sequence sent by the key +X:kN=: sequence sent by the key +X:kh=: sequence sent by the key +X:kH=: sequence sent by the key +X:kI=: sequence sent by the key +X.DE +X.PP +XOriginally, termcap didn't have any names for the , , , +Xand keys. +XAlthough the capability names shown in the table above are the most common, +Xthey are \fInot\fR universal. +XSCO Xenix uses :PU=:PD=:HM=:EN=: for those keys. +XAlso, if the four arrow keys happen to be part of a 3x3 keypad, +Xthen the five non-arrow keys may be named :K1=: through :K5=:, +Xso an IBM PC keyboard may be described using those names instead. +X\*E can find any of these names. +X.SH +XOptional strings sent by function keys +X.if n .ul 0 +X.ID +X:k1=:...:k9=:k0=: codes sent by through keys +X:s1=:...:s9=:s0=: codes sent by ... +X:c1=:...:c9=:c0=: codes sent by ... +X:a1=:...:a9=:a0=: codes sent by ... +X.DE +X.PP +XNote that :k0=: is used to describe the key. +XSome termcap documents recommend :ka=: or even :k;=: for describing +Xthe key, but \*E doesn't support that. +X.PP +XAlso, the :s1=:..., :c1=:..., and :a1=:... codes are very non-standard. +XThe terminfo library doesn't support them. +X.SH +XOptional fields that describe character attributes +X.if n .ul 0 +X.ID +X:so=:se=: start/end standout mode (We don't care about :sg#:) +X:us=:ue=: start/end underlined mode +X:md=:me=: start/end boldface mode +X:as=:ae=: start/end alternate character set (italics) +X:ug#: visible gap left by :us=:ue=:md=:me=:as=:ae=: +X.DE +X.SH +XOptional fields that affect the cursor's shape +X.PP +XThe :cQ=: string is used by \*E immediately before exiting to undo +Xthe effects of the other cursor shape strings. +XIf :cQ=: is not given, then all other cursor shape strings are ignored. +X.ID +X:cQ=: normal cursor +X:cX=: cursor used for reading EX command +X:cV=: cursor used for reading VI commands +X:cI=: cursor used during VI input mode +X:cR=: cursor used during VI replace mode +X.DE +X.PP +XIf the capabilities above aren't given, then \*E will try to use the +Xfollowing values instead. +X.ID +X:ve=: normal cursor, used as :cQ=:cX=:cI=:cR=: +X:vs=: gaudy cursor, used as :cV=: +X.DE +X.SH +XAn example +X.PP +XHere's the termcap entry I use on my Minix-ST system. +XSome of the fields in it have nothing to do with \*E. +XSome can only work on my system; +XI have modified my kernel's screen driver. +X.sp +X.LD +X.ne 14 +Xmx|minix|minixst|ansi:\\ +X :is=\\E[0~:co#80:li#25:bs:pt:\\ +X :cm=\\E[%i%d;%dH:up=\\E[A:do=^J:nd=\\E[C:sr=\\EM:\\ +X :cd=\\E[J:ce=\\E[K:cl=\\E[H\\E[J:\\ +X :al=\\E[L:dl=\\E[M:ic=\\E[@:dc=\\E[P:im=:ei=:\\ +X :so=\\E[7m:se=\\E[m:us=\\E[4m:ue=\\E[m:\\ +X :md=\\E[1m:me=\\E[m:as=\\E[1;3m:ae=\\E[m:\\ +X :ku=\\E[A:kd=\\E[B:kr=\\E[C:kl=\\E[D:\\ +X :k1=\\E[1~:k2=\\E[2~:k3=\\E[3~:k4=\\E[4~:k5=\\E[5~:\\ +X :k6=\\E[6~:k7=\\E[17~:k8=\\E[18~:k9=\\E[19~:k0=\\E[20~:\\ +X :kU=\\E[36~:kQ=\\E[32~:kH=\\E[28~:\\ +X :GV=3:GH=D:G1=?:G2=Z:G3=@:G4=Y:GC=E:GL=4:GR=C:GU=A:GD=B:\\ +X :cQ=\\E[k:cX=\\E[2;0k:cV=\\E[16;0k:cI=\\E[k:cR=\\E[16;20k: +X.DE +/ +echo x - title.ms +sed '/^X/s///' > title.ms << '/' +X.de tE +X.ps 80 +X.ce 1 +X\*E +X.. +X.de nE +X.ce 7 +X####### +X# # # # # #### +X# # # # # # +X##### # # # # #### +X# # # # # # +X# # # # # # # +X####### ###### ## # #### +X.. +X.sp |2i +X.if t .tE +X.if n .nE +X.ps 10 +X.sp 1 +X.ce 2 +X- a clone of vi/ex - +Xversion \*V +X.sp |7.5i +X.IP Author: 0.9i +XSteve Kirkendall +X.br +X14407 SW Teal Blvd., Apt C +X.br +XBeaverton, OR 97005 +X.IP E-Mail: 0.9i +Xkirkenda@cs.pdx.edu +X.IP Phone: 0.9i +X(503) 643-6980 +/ +echo x - ver.ms +sed '/^X/s///' > ver.ms << '/' +X.ds V 1.5j-betatest +X.if t .ds E E\s-2LVIS\s+2 +X.if n .ds E Elvis +X.\" +X.\" usage: .Go +X.de Go +X.ds LH "\\$1-\\\\n% +X.ds RH "\\$1-\\\\n% +X.ds CH "\\$2 +X.NH S \\$1 +X\\$2 +X.\"if !\\n%=1 .bp 1 +X.if n .ul 0 +X.. +/ +echo x - versions.ms +sed '/^X/s///' > versions.ms << '/' +X.Go 12 "VERSIONS" +X.PP +X\*E currently works under BSD UNIX, AT&T System-V UNIX, SCO XENIX, +XMinix, Coherent, MS-DOS, Atari TOS, OS9/68k, VAX/VMS, and AmigaDos. +XThis section of the manual provides special information that applies to each +Xparticular version of \*E. +X.PP +XFor all versions except MS-DOS, +Xthe file "Makefile.mix" should be copied to "Makefile", +Xand then edited to select the correct set of options for your system. +XThere is more information about this embedded in the file itself. +X.NH 2 +XBSD UNIX +X.PP +XTemporary files are stored in /tmp. +X.PP +XYou should modify /etc/rc so that +Xthe temp files are preserved when the system is rebooted. +XFind a line in /etc/rc which reads +X.br +X.ti +0.5i +Xex4.3preserve /tmp +X.PP +Xor something like that, and append the following line after it: +X.br +X.ti +0.5i +Xelvprsv /tmp/elv* +X.PP +XIf you do not have permission to modify /etc/rc, don't fret. +XThe above modification is only needed to allow you to recover your changes +Xafter a system crash. +XYou can still run \*E without that modification, +Xand you can still recover your changes when \*E crashes +Xor when your dialup modem looses the carrier signal, or something like that. +XOnly a system crash or power failure could hurt you. +X.PP +XBoth \*E and the real Vi +Xread initialization commands from a file called ".exrc", +Xbut the commands in that file might work on one but not the other. +XFor example, "set keywordprg=man" will work for \*E, +Xbut Vi will complain because it doesn't have a "keywordprg" option. +XIf the warning messages annoy you, then you can edit the config.h file +Xto change the name of the initialization file ".exrc" to something else, +Xsuch as ".elvisrc". +X.PP +XIf you use X windows, you may wish to add "-DCS_LATIN1" to CFLAGS. +XThis will cause the digraph table and the flipcase option to have default +Xvalues that are appropriate for the LATIN-1 character set. +XThat's the standard character set for X. +X.PP +XThe default keyboard macro time-out value is larger for BSD than it is for +Xsome other systems, because I've had trouble running \*E via rlogin or Xterm. +XI guess it takes a while for those keystokes to squirt through the net. +X.NH 2 +XSystem-V UNIX +X.PP +XMost SysV UNIX systems use terminfo instead of termcap, +Xbut the terminfo library doesn't seem to have a standard name. +XAs shipped, Elvis' Makefile.mix is configured with "LIBS=-lterm". +XYou may need to change it to "LIBS=-ltermcap" or "LIBS=-lterminfo" +Xor even "LIBS=-lcurses". +X.PP +XThe /etc/rc file should be modified as described for BSD systems, above. +XThe only difference is that SysV systems tend to have directories for +Xinitialization, instead of a single large /etc/rc file. +XEditor recovery is usually done somewhere in the /etc/rc2.d directory. +X.PP +XThe potential trouble with ".exrc" described above for BSD UNIX applies +Xto System-V UNIX as well. +X.PP +X\*E uses control-C as the interrupt key, not Delete. +X.NH 2 +XSCO Xenix +X.PP +XFor Xenix-386, you can use the generic System-V settings. +XYou may wish to add "-DCS_IBMPC" to CFLAGS, to have the digraph table and +Xflipcase option start up in a mode that is appropriate for the console. +X +XThere is a separate group of settings for use with Xenix-286. +XIt already has "-DCS_IBMPC" in CFLAGS. +X.PP +XBecause Xenix is so similar to System-V, everything I said earlier about +XSystem-V applies to the Xenix version too, except that editor recovery +Xprobably belongs in a directory called /etc/rc.d/8. +X.NH 2 +XMinix +X.PP +XThere are separate settings in Makefile.mix for Minix-PC and Minix-68k. +XThe differences between these two are that +Xthe 68k version uses ".o" for the object file extension where +Xthe PC version uses ".s", and +Xthe PC version has some extra flags in CFLAGS to reduce the size of \*E. +XThe PC version also uses tinytcap (instead of the full termcap) to make it smaller. +X.PP +XMinix-PC users should read the CFLAGS section of this manual very carefully. +XYou have some choices to make... +X.PP +XThe temporary files are stored in /usr/tmp. +XThe /usr/tmp directory must exist before you run \*E, +Xand it must be readable/writable by everybody. +XWe use /usr/tmp instead of /tmp because +Xafter a system crash or power failure, +Xyou can recover the altered version of a file from the temporary file +Xin /usr/tmp. +XIf it was stored in /tmp, though, then it would be lost because /tmp is +Xnormally located on the RAM disk. +X.PP +X\*E uses control-C as the interrupt key, not Delete. +X.NH 2 +XCoherent +X.PP +X\*E was ported to Coherent by Esa Ahola. +X.PP +X\*E is too large to run under Coherent unless you eliminate some +Xfeatures via the CFLAGS setting. +XThe recommended settings, in Makefile.mix, produce a working version +Xof \*E which emulates Vi faithfully, but lacks most of the extensions. +XYou should read the CFLAGS section of this manual carefully. +X.PP +XYou can probably reduce the size of \*E by using tinytcap.c instead of -lterm. +XThis would allow you to keep most features of \*E, +Xat the expense of terminal independence. +X(Tinytcap.c has ANSI escape sequences hard-coded into it.) +XTo use tinytcap, just add "tinytcap.o" to the "EXTRA=" line in the Makefile, +Xand remove "-lterm" from the "LIBS=" line. +X.PP +XThe temporary files are stored in /tmp. +XYou should modify your /etc/rc file as described for BSD earlier. +X.NH 2 +XMS-DOS +X.PP +X\*E was ported to MS-DOS by Guntram Blohm and Martin Patzel. +XWillett Kempton added support for the DEC Rainbow. +X.PP +XIdeally, \*E should be compiled with Microsoft C 5.10 and the standard +XMicrosoft Make utility, +Xvia the command "make elvis.mak". +XThis will compile \*E and all related utilities. +X.PP +XWith Microsoft C 6.00, you may have trouble compiling regexp.c. +XIf so, try compiling it without optimization. +X.PP +XThe "Makefile.mix" file contains a set of suggested settings for compiling +Xelvis with Turbo-C or Borland C. +X(If you have Turbo-C, but not the Make utility, +Xthen you can \fIalmost\fR use the "\*E.prj" file to compile \*E, +Xbut you must explicitly force Turbo-C to compile it with the "medium" memory model. +XMost of the related programs [ctags, ref, virec, refont, and wildcard] are +Xonly one file long, so you should have no trouble compiling them.) +XThe "alias.c" file is meant to be compiled once into an executable named +X"ex.exe". +XYou should then copy "ex.exe" to "vi.exe" and "view.exe". +X.PP +X\*E stores its temporary files in C:\\tmp. +XIf this is not satisfactory, then you should edit the CFLAGS line of +Xyour Makefile to change TMPDIR to something else before compiling. +XYou can also control the name of the temp directory via an environment +Xvariable named TMP or TEMP. +XThe directory must exist before you can run \*E. +X.PP +XThe TERM environment variable determines how elvis will write to the screen. +XIt can be set to any one of the following values: +X.LD +X.ta 1.5i 2.5i +X pcbios Use BIOS calls on an IBM-PC clone. +X rainbow Use DEC Rainbow interface. +X ansi Use ANSI.SYS driver. +X nansi User faster NANSI.SYS driver. +X.DE +X.PP +XIf the TERM variable isn't set, then elvis will automatically select either +Xthe "rainbow" interface (when run on a Rainbow) or "pcbios" (on an IBM clone). +X.PP +XYou may prefer to use NANSI.SYS for speed; +Xor you may NEED to use ANSI.SYS for a non-clone, such as a lap-top. +XIf so, you should +Xinstall one of these drivers by adding "driver = nansi.sys" (or whatever) +Xto your CONFIG.SYS file, +Xand then you should define TERM to be "nansi" (or whatever) by adding +X"set TERM=nansi" to your AUTOEXEC.BAT file. +XYou must then reboot for these changes to take effect. +XAfter that, \*E will notice the "TERM" setting and use the driver. +X.PP +XSince ".exrc" is not a valid DOS filename, +Xthe name of the initialization file has been changed to "elvis.rc". +XElvis will look for an "elvis.rc" file first in your home directory, +Xand then in the current directory. +XNote that you must set an environment variable named "HOME" to the +Xfull pathname of your home directory, for Elvis to check there; +Xif "HOME" isn't set, then Elvis will only look in the current directory. +XTo set "HOME", you would typically add the following line to your +XAUTOEXEC.BAT file: +X.br +X.ti +0.5i +Xset HOME c:\\ +X.PP +XAn extra program, called "wildcard", is needed for MS-DOS. +XIt expands wildcard characters in file names. +XIf \*E flashes a "Bad command or filename" message when it starts, +Xthen you've probably lost the WILDCARD.EXE program somehow. +X.PP +X\*E can run under Windows, but only in full-screen mode. +XAlso, Windows uses an environment variable called TEMP which interferes with +Xelvis' usage of TEMP; +Xto work around this, you can simply set an environment variable named +XTMP (with no 'E') to the name of elvis' temporary directory. +XWhen TEMP and TMP are both set, \*E uses TMP and ignored TEMP. +X.NH 2 +XAtari TOS +X.PP +X\*E was ported to Atari TOS by Guntram Blohm and Martin Patzel. +XIt is very similar to the MS-DOS version. +XIt has been tested with the Mark Williams C compiler and also GNU-C. +X.PP +XThe TERM environment variable is ignored; +Xthe ST port always assumes that TERM=vt52. +XThe SHELL (not COMSPEC!) variable should be set to +Xthe name of a line-oriented shell. +X.PP +XA simple shell in included with \*E. +XIts source is in "shell.c", and the name of the executable is "shell.ttp". +XThe file "profile.sh" should contain a set of instructions to be executed +Xwhen the shell first starts up. +XAn example of this file is included, but you will almost certainly want to +Xedit it right away to match your configuration. +X(If you already have a command-line shell, +Xthen you'll probably want to continue using it. +XThe shell that comes with \*E is very limited.) +X.PP +XCurrently, character attributes cannot be displayed on the screen. +X.PP +X\*E runs under MiNT (a free multi-tasking extension to TOS) +Xbut it can be a CPU hog because of the way that \*E reads from the +Xkeyboard with timeout. +XAlso, \*E doesn't use any of the special features of MiNT. +XI have received a set of patches that optimize \*E for MiNT, +Xbut they arrived too late to integrate into this release. +X.NH 2 +XOS9/68k +X.PP +X\*E was ported to OS9/68k by Peter Reinig. +X.PP +XThe Makefile is currently configured to install \*E and the related +Xprograms in /dd/usr/cmds +XIf this this is unacceptable, then you should change the BIN setting +Xto some other directory. +XSimilarly, it expects the source code to reside in /dd/usr/src/elvis; +Xthe ODIR setting is used to control this. +X.PP +XTemporary files are stored in the /dd/tmp directory. +XYour /dd/startup file may need to be modified +Xto prevent it from deleting \*E' temporary files; +Xmake /dd/startup run the \fIelvprsv\fR program before it wipes out /dd/tmp. +X.PP +XThe program in alias.c is linked repeatedly to produce the +X"vi", "view", and "input" aliases for \*E. +XSadly, the "ex" alias is impossible to implement under OS9 +Xbecause the shell has a built-in command by that name. +X.PP +XFor some purposes, +Xyou must give `make' the "-b" option. +XSpecifically, you need this for "make -b clean" and "make -b install". +X.NH 2 +XVAX/VMS +X.PP +XJohn Campbell ported \*E to VAX/VMS. +X.PP +XA heavily laden VAX can take half an hour to compile elvis. +XThis is normal. +XDon't panic. +X.PP +XWhile running, elvis will create temporary files in SYS$SCRATCH. +XEnter SHOW LOGICAL SYS$SCRATCH to see what actual directory you are using. +XMany sites have SYS$SCRATCH equivalenced to SYS$LOGIN. +XThe elvis temporary files look like the following on VMS while elvis is running: +X.br +X.ti 0.75i +XELV_1123A.1;1 ELV_1123A.2;1 SO070202.;1 +X.PP +XAlso, filtering commands (like !!dir and !}fmt) should work on VMS. +XThis assumes, however, that you can create temporary mailboxes and that +Xyour mailbox quota (a sysgen parameter) is at least 256 bytes for a +Xsingle write to the mailbox. +XThis is the default sysgen parameter, +Xso there should be few people who experience filter problems. +X.PP +XAdditionally, an attempt was made to support the standard terminals on VMS: +X"vt52", "vt100", "vt200", "vt300", "vt101", "vt102". +XNon-standard terminals could be supported by setting your terminal type to +XUNKNOWN (by entering SET TERM/UNKNOWN) +Xand defining the logical name ELVIS_TERM. +XWhatever ELVIS_TERM translates to, however, will have to be included in +Xtinytcap.c. +XNote that the upper/lowercase distinctions are significant, +Xand that DCL will upshift characters that are not quoted strings, so +Xenter DEFINE ELVIS_TERM "hp2621a". +XAs distributed, it would probably not be a good idea to have more than the +Xstandard terminals in tinytcap.c (else it wouldn't be tiny, would it?). +XChanges here, of course, would require a recompilation to take effect. +X.PP +XIf you have a version of the "termcap" library and database on your system, +Xthen you may wish to replace tinytcap with the real termcap. +X.NH 2 +XAmigaDOS +X.PP +XMike Rieser and Dale Rahn ported \*E to AmigaDOS. +X.PP +XThe port was done using Manx Aztec C version 5.2b. +X\*E uses about as much space as it can and still be small code and data. +X\*E should also compile under DICE, though there may be a little trouble with +Xsigned versus unsigned chars. +X.PP +XThe port has been done so the same binary will run under both versions of AmigaDOS. +XUnder AmigaDOS 2.04, \*E supports all the documented features. +XIt also uses an external program ref to do tag lookup. +XSo, the accompanying programs: ref and ctags are recommended. +XUnder AmigaDOS 1.2/1.3 \*E works, buts lacks the more advanced features. +X.PP +XFor the port to AmigaDOS 2.04, we tried to use as many Native AmigaDOS +Xcalls as we could. +XThis should increase Elvis's chances at being compiled with other compilers. +XDICE seems to have a different default char type. +XYou may need to use the UCHAR() macro in tio.c. +XTo test it, try the :map command; if it looks right, things are cool. +X.PP +XFor the port to AmigaDOS 1.3, we tried to make sure the program was at +Xleast usable. +XMany features are missing, most notably running commands in subshells. +XAlso, what we could get working, we used Aztec functions to support them, +Xso this part is little more compiler dependent. +X.PP +XAztec is compatible with the SAS libcall #pragma. +XI personally prefer using the includes that come from Commodore over the ones +Xsupplied with Aztec, but for people with a straight Aztec installation, +XI went with the default names for the Aztec pragmas. +X.PP +XOne include you'll need is <sys/types.h>. +XIts a common include when porting software just make yourself one. +XIts a two line file that saves a lot of hassle especially in the elvis source. +XSo, make a directory where your includes are located called `sys' +Xand in a file below that type: +X.br +X.ti +0.8i +X/* sys/types.h */ +X.br +X.ti +0.8i +X#include <exec/types.h> +X.PP +XWhen setting environment variables (either local or global) for +Xvariables that specify a directory, make sure the variable ends in `:' +Xor `/'. +XThis saved from having to change much of the way elvis works. +XThe default temporary directory (if TEMP and TMP aren't specified) is "T:". +XThe default if HOME directory (if no HOME environment variable is set) is "S:". +X.PP +XTo avoid conlict with other uses, \*E uses elvis.rc instead of .exrc or +Xwhere it looks for macros. +X.NH 2 +XOther Systems +X.PP +XFor Sun workstations, use the BSD configuration. +XEarlier versions of elvis didn't link correctly due to a quirk in Sun's +Xversion of the "make" utility, but this version of elvis has a work-around +Xfor that quirk so you should have no trouble at all. +X.PP +XFor Linux, use the SysV settings. +XYou can probably just remove the "-lterm" from the "LIBS= -lterm" line, +Xsince linux keeps the termcap functions in the standard C library. +X.PP +XFor other UNIXoid systems, I suggest you start with the Minix-68k settings +Xand then grow from that. +XMinix is a nice starting point because it is a clone of Version 7 UNIX, +Xwhich was the last common ancestor of BSD UNIX and SysV UNIX. +XAny Operating System which claims any UNIX compatibility what so ever +Xwill therefore support V7/Minix code. +XYou may need to fiddle with #include directives or something, though. +XMinix-68k is a better starting point than Minix-PC because the PC compiler +Xhas some severe quirks. +/ +echo x - visual.ms +sed '/^X/s///' > visual.ms << '/' +X.Go 2 "VISUAL MODE COMMANDS" +X.PP +XMost visual mode commands are one keystroke long. +XThe following table lists the operation performed by each keystroke, +Xand also denotes any options or arguments that it accepts. +XNotes at the end of the table describe the notation used in this table. +X.PP +XIn addition to the keys listed here, your keyboard's "arrow" keys +Xwill be interpretted as the appropriate cursor movement commands. +XThe same goes for <PgUp> and <PgDn>, if your keyboard has them. +XThe <Insert> key will toggle between insert mode and replace mode. +XThere is a colon mode command (":map", to be described later) +Xwhich will allow you to define other keys, such as function keys. +X.PP +XA tip: visual command mode looks a lot like text input mode. +XIf you forget which mode you're in, just hit the <Esc> key. +XIf \*E beeps, then you're in visual command mode. +XIf \*E does not beep, then you were in input mode, +Xbut by hitting <Esc> you will have switched to visual command mode. +XSo, one way or another, after <Esc> \*E will be ready for a command. +X.LD +X.ta 0.7i 1.3i +X\s+2COMMAND DESCRIPTION\s-2 +X ^A Search for next occurence of word at cursor (MOVE)(EXT) +X ^B Move toward the top of the file by 1 screenful +X ^C --- (usually sends SIGINT, to interupt a command) +Xcount ^D Scroll down <count> lines (default 1/2 screen) +Xcount ^E Scroll up <count> lines +X ^F Move toward the bottom of the file by 1 screenful +X ^G Show file status, and the current line # +Xcount ^H Move left, like h (MOVE) +X ^I --- +Xcount ^J Move down (MOVE) +X ^K --- +X ^L Redraw the screen +Xcount ^M Move to the front of the next line (MOVE) +Xcount ^N Move down (MOVE) +X ^O --- +Xcount ^P Move up (MOVE) +X ^Q --- (typically XON, which restarts screen updates) +X ^R Redraw the screen +X ^S --- (typically XOFF, which stops screen updates) +X ^T --- +Xcount ^U Scroll up <count> lines (default 1/2 screen) +X ^V --- +X ^W --- +Xcount ^X Move to a physical column number on the screen (MOVE) (EXT) +Xcount ^Y Scroll down <count> lines +X ^Z --- (sometimes sends SIGSUSP, to suspend execution) +X ESC --- +X ^\\ --- (usually sends SIGQUIT, which is ignored) +X ^] If the cursor is on a tag name, go to that tag +X ^^ Switch to the previous file, like ":e #" +X ^_ --- +Xcount SPC Move right,like l (MOVE) +X ! \s-2mv\s+2 Run the selected lines thru an external filter program +X " \s-2key\s+2 Select which cut buffer to use next +Xcount # \s-2+\s+2 Increment a number (EDIT) (EXT) +X $ Move to the rear of the current line (MOVE) +Xcount % Move to matching (){}[] or to a given % of file (MOVE) (EXT) +Xcount & Repeat the previous ":s//" command here (EDIT) +X ' \s-2key\s+2 Move to a marked line (MOVE) +Xcount ( Move backward <count> sentences (MOVE) +Xcount ) Move forward <count> sentences (MOVE) +X * Go to the next error in the errlist (EXT) +Xcount + Move to the front of the next line (MOVE) +Xcount , Repeat the previous [fFtT] but in the other direction (MOVE) +Xcount - Move to the front of the preceding line (MOVE) +Xcount . Repeat the previous "edit" command +X / \s-2text\s+2 Search forward for a given regular expression (MOVE) +X 0 If not part of count, move to 1st char of this line (MOVE) +X 1 Part of count +X 2 Part of count +X 3 Part of count +X 4 Part of count +X 5 Part of count +X 6 Part of count +X 7 Part of count +X 8 Part of count +X 9 Part of count +X : \s-2text\s+2 Run single EX cmd +Xcount ; Repeat the previous [fFtT] cmd (MOVE) +X < \s-2mv\s+2 Shift text left (EDIT) +X = \s-2mv\s+2 Reformat +X > \s-2mv\s+2 Shift text right (EDIT) +X ? \s-2text\s+2 Search backward for a given regular expression (MOVE) +X @ \s-2key\s+2 Execute the contents of a cut-buffer as VI commands +Xcount A \s-2inp\s+2 Append at end of the line (EDIT) +Xcount B Move back Word (MOVE) +X C \s-2inp\s+2 Change text from the cursor through the end of the line (EDIT) +X D Delete text from the cursor through the end of the line (EDIT) +Xcount E Move end of Word (MOVE) +Xcount F \s-2key\s+2 Move leftward to a given character (MOVE) +Xcount G Move to line #<count> (default is the bottom line) (MOVE) +Xcount H Move to home row (the line at the top of the screen) +Xcount I \s-2inp\s+2 Insert at the front of the line (after indents) (EDIT) +Xcount J Join lines, to form one big line (EDIT) +X K Look up keyword (EXT) +Xcount L Move to last row (the line at the bottom of the screen) +X M Move to middle row +X N Repeat previous search, but in the opposite direction (MOVE) +Xcount O \s-2inp\s+2 Open up a new line above the current line (EDIT) +X P Paste text before the cursor (EDIT) +X Q Quit to EX mode +X R \s-2inp\s+2 Overtype (EDIT) +Xcount S \s-2inp\s+2 Change lines, like <count>cc +Xcount T \s-2key\s+2 Move leftward *almost* to a given character (MOVE) +X U Undo all recent changes to the current line +X V Start marking lines for c/d/y/</>/!/\\ (EXT) +Xcount W Move forward <count> Words (MOVE) +Xcount X Delete the character(s) to the left of the cursor (EDIT) +Xcount Y Yank text line(s) (copy them into a cut buffer) +X Z Z Save the file & exit +X [ [ Move back 1 section (MOVE) +X \\ \s-2mv\s+2 Pop-up menu for modifying text (EXT) +X ] ] Move forward 1 section (MOVE) +X ^ Move to the front of the current line (after indent) (MOVE) +Xcount _ Move to the current line +X ` \s-2key\s+2 Move to a marked character (MOVE) +Xcount a \s-2inp\s+2 Insert text after the cursor (EDIT) +Xcount b Move back <count> words (MOVE) +X c \s-2mv\s+2 Change text (EDIT) +X d \s-2mv\s+2 Delete text (EDIT) +Xcount e Move forward to the end of the current word (MOVE) +Xcount f \s-2key\s+2 Move rightward to a given character (MOVE) +X g --- +Xcount h Move left (MOVE) +Xcount i \s-2inp\s+2 Insert text at the cursor (EDIT) +Xcount j Move down (MOVE) +Xcount k Move up (MOVE) +Xcount l Move right (MOVE) +X m \s-2key\s+2 Mark a line or character +X n Repeat the previous search (MOVE) +Xcount o \s-2inp\s+2 Open a new line below the current line (EDIT) +X p Paste text after the cursor (EDIT) +X q --- +Xcount r \s-2key\s+2 Replace <count> chars by a given character (EDIT) +Xcount s \s-2inp\s+2 Replace <count> chars with text from the user (EDIT) +Xcount t \s-2key\s+2 Move rightward *almost* to a given character (MOVE) +X u Undo the previous edit command +X v Start marking characters for c/d/y/</>/!/\\ (EXT) +Xcount w Move forward <count> words (MOVE) +Xcount x Delete the character that the cursor's on (EDIT) +X y \s-2mv\s+2 Yank text (copy it into a cut buffer) +X z \s-2key\s+2 Scroll current line to the screen's +=top -=bottom .=middle +Xcount { Move back <count> paragraphs (MOVE) +Xcount | Move to column <count> (the leftmost column is 1) +Xcount } Move forward <count> paragraphs (MOVE) +Xcount ~ Switch a character between uppercase & lowercase (EDIT) +X DEL --- (usually mapped to shift-X, so it deletes one character) +X.DE +X.IP count +XMany commands may be preceded by a count. This is a sequence of digits +Xrepresenting a decimal number. For most commands that use a count, +Xthe command is repeated <count> times. The count is always optional, +Xand usually defaults to 1. +X.IP key +XSome commands require two keystrokes. The first key always determines +Xwhich command is to be executed. The second key is used as a parameter +Xto the command. +X.IP mv +XSome commands (! < > c d y \\ =) operate on text between the cursor and some +Xother position. +XThere are three ways that you can specifify that other position. +X.IP +XThe first way is to follow the command keystroke with a movement command. +XFor example, "dw" deletes a single word. +X"d3w" and "3dw" both delete three words. +X.IP +XThe second way is to type the command keystroke twice. +XThis causes whole lines to be acted upon. +XFor example, ">>" indents the current line. +X"3>>" indents the current line and the following two lines. +X.IP +XThe last way is to move the cursor to one end of the text, +Xtype 'v' or 'V' to start marking, +Xmove the cursor to the other end, +Xand then type the desired command key. +X.IP inp +XMany commands allow the user to interactively enter text. +XSee the discussion of "input mode" in the following section. +X.IP (EXT) +XThese commands are extensions -- the real vi doesn't have them. +X.IP (EDIT) +XThese commands affect text, and may be repeated by the "." command. +X.IP (MOVE) +XThese commands move the cursor, and may be used to specify the extent +Xof a member of the "mv" class of commands. +X.NH 2 +XInput Mode +X.PP +XYou can't type text into your file directly from visual command mode. +XInstead, you must first give a command which will put you into input mode. +XThe commands to do this are A/C/I/O/R/S/a/i/o/s. +X.PP +XThe S/s/C/c commands temporarily place a $ at the end of the text that +Xthey are going to change. +X.PP +XIn input mode, all keystrokes are inserted into the text at the +Xcursor's position, except for the following: +X.ID +X^A insert a copy of the last input text +X^D delete one indent character +X^H (backspace) erase the character before the cursor +X^L redraw the screen +X^M (carriage return) insert a newline (^J, linefeed) +X^O execute next key as a visual command (limited!) +X^P insert the contents of the cut buffer +X^R redraw the screen, like ^L +X^T insert an indent character +X^U backspace to the beginning of the line +X^V insert the following keystroke, even if special +X^W backspace to the beginning of the current word +X^Z^Z write the file & exit \*E +X^[ (ESCape) exit from input mode, back to command mode +X.DE +X.PP +XAlso, on some systems, ^S may stop output, ^Q may restart output, +Xand ^C may interupt execution. +X^@ (the NUL character) cannot be inserted. +X.PP +XThe R visual command puts you in overtype mode, +Xwhich is a slightly different form of input mode. +XIn overtype mode, each time you insert a character, +Xone of the old characters is deleted from the file. +X.NH 2 +XArrow keys in Input Mode +X.PP +XThe arrow keys can be used to move the cursor in input mode. +X(This is an extension; the real Vi doesn't support arrow keys in input mode.) +XThe <PgUp>, <PgDn>, <Home>, and <End> keys work in input mode, too. +XThe <Delete> key deletes a single character in input mode. +XThe <Insert> key toggles between input mode and replace mode. +X.PP +XThe best thing about allowing arrow keys to work in input mode is that +Xas long as you're in input mode, +X\*E seems to have a fairly ordinary user interface. +XWith most other text editors, you are always in either insert mode or +Xreplace mode, and you can use the arrow keys at any time to move the cursor. +XNow, \*E can act like that, too. +XIn fact, with the new "inputmode" option and the "control-Z control-Z" input +Xcommand, you may never have to go into visual command mode for simple edit +Xsessions. +X.NH 2 +XDigraphs +X.PP +X\*E supports digraphs as a way to enter non-ASCII characters. +XA digraph is a character which is composed of two other characters. +XFor example, an apostrophe and the letter i could be defined as a digraph +Xwhich is to be stored & displayed as an accented i. +X.PP +XThere is no single standard for extended ASCII character sets. +X\*E can be compiled to fill the digraph with values appropriate for +Xeither the IBM PC character set, or the LATIN-1 character set used by +XX windows, or neither. +X(See the discussions of -DCS_IBMPC and -DCS_LATIN1 in the CFLAGS section +Xof this manual.) +XYou can view or edit the digraph table via the ":digraph" colon command. +X.PP +XDigraphs will not be recognized until you've entered ":set digraph". +X.PP +XTo actually use a digraph +Xtype the first character, then hit <Backspace>, and then type the +Xsecond character. +X\*E will then substitute the non-ASCII character in their place. +X.NH 2 +XAbbreviations +X.PP +X\*E can expand abbreviations for you. +XYou define an abbreviation with the :abbr command, +Xand then whenever you type in the abbreviated form while in input mode, +X\*E will immediately replace it with the long form. +XCOBOL programmers should find this useful. :-) +X.PP +X\*E doesn't perform the substitution until you type a non-alphanumeric +Xcharacter to mark the end of the word. +XIf you type a control-V before that non-alphanumeric character, then +X\*E will not perform the substitution. +X.NH 2 +XAuto-Indent +X.PP +XWith the ":set autoindent" option turned on, +X\*E will automatically insert leading whitespace at the beginning of each +Xnew line that you type in. +XThe leading whitespace is copied from the preceding line. +X.PP +XTo add more leading whitespace, type control-T. +XTo remove some whitespace, type control-D. +X.PP +XIf you ":set noautotab", then the whitespace generated by control-T will +Xalways consist of spaces -- never tabs. +XSome people seem to prefer this. +X.PP +X\*E' autoindent mode isn't 100% compatible with vi's. +XIn \*E, 0^D and ^^D don't work, +X^U can wipeout all indentation, +Xand sometimes \*E will use a different amount of indentation than vi would. +/ diff --git a/commands/elvis/Knownbug.txt b/commands/elvis/Knownbug.txt new file mode 100755 index 000000000..740074ddd --- /dev/null +++ b/commands/elvis/Knownbug.txt @@ -0,0 +1,68 @@ +The following options are missing: + [no]optimize - affects screen redrawing method + [no]redraw - simulate character insertion by redrawing line + [no]slowopen - don't use character insertion + tags="tags" - list of tags, used as TAGPATH + +I'd like to improve the versatility of the options whose value is a command: +cc, make, kp, and ep. I'd like to add some notation that allows you to say +where to insert the current filename or current word. +------------------------------------------------------------------------------- +Currently, elvis is configured to look for | only in .exrc files. It doesn't +look for | in any interactively entered command lines, yet. +------------------------------------------------------------------------------- +The 'p', '#', and 'l' flags aren't supported. Also, ex commands don't accept +counts; e.g., ":c5" can't be used to change five lines. +------------------------------------------------------------------------------- +The following have been reported, but have not been verified. If you have +experienced any of the following, and haven't reported it yet, then please +report it now! I need more information about these bugs. + +[Bugs that are not in this list should also be reported, of course.] + +- Under VMS on an 80-column screen, after scolling sideways to approximately + column 110, a ^L will not redraw the part of the line after the cursor. +- On an Atari ST running under TOS: some ASCII keys seem to send '#' plus + another key. (This is normal for non-ASCII keys like <F1> or <Help>, but + ASCII keys should always send a single ASCII character.) +------------------------------------------------------------------------------- +BIG JOBS: + Desirable extension: merge input mode and visual command mode. + Display long lines by wrapping, like the real vi (if ":set sidescroll=0") +------------------------------------------------------------------------------- + +- In the ":w >>filename" command, elvis doesn't allow any whitespace between + the ">>" and "filename". + +- Elvis doesn't allow "backslash newline" inside a single EX command. + +- VMS intercepts the control-T character, which is normally used to increase + indentation. The <Tab> key works, but it doesn't do quite the same thing. + (":map! ^I ^T" helps.) + +- Under VMS, file I/O is very slow. Looking over the vmsio.c file, I get the + impression that it is rather over-done for elvis. Its speed could + probably be inproved. + +- The errlist feature doesn't seem to work with the Borland compilers. Perhaps + they write to stderr instead of stdout? This will probably be easy to solve + once I modify the "cc" and "make" options, as described earlier. + +- The command ":0" should move the cursor to line 1. Currently, it doesn't + move the cursor at all. + +- File preservation is still flakey. On DOS/TOS/VMS systems, it is also more + complex that it should be. + +- The act of appending to a cut buffer (as in "Ayy) sets file modification + flag. It shouldn't! + +- The .exrc file is limited to BLKSIZE bytes -- 2048 on most systems, but + 1024 on Minicx-PC, Coherent, and MS-DOS. + +- I *still* haven't quite perfected the screen update code. If you suspect + that the screen doesn't accurately reflect the contents of the edit buffer, + then you should try doing a control-L. + + I'll be overhauling the screen update code soon to make it wrap long lines + like the real vi. I expect to fix this bug then. diff --git a/commands/elvis/Makedoc b/commands/elvis/Makedoc new file mode 100755 index 000000000..23202638e --- /dev/null +++ b/commands/elvis/Makedoc @@ -0,0 +1,87 @@ +# This is the Makefile for Elvis' "doc" directory. It makes use of a +# troff-like formatter called mroff. Since you probably don't have mroff, +# you'll need to edit this Makefile before you can fully use it. It can +# also use nroff, though, so you should be able to get something out of it. +# +# make Use nroff to create an ASCII version of the manual. +# make foo.doc Use nroff to create an ASCII version of foo.man or foo.ms +# make manual Use MROFF to print a typeset manual on a laser printer +# make foo.1200 Use MROFF to print a typeset version of foo.man or foo.ms +# make foo.100 Use MROFF to print a draft-quality version of foo.man or foo.ms +# make foo.more Use MROFF to preview foo.man or foo.more on your terminal +# + +############################################################################### +# Definitions... + +MAN= ctags.man elvis.man elvprsv.man elvrec.man fmt.man ref.man +MS= title.ms index.ms intro.ms visual.ms ex.ms regexp.ms options.ms\ + cutbufs.ms differ.ms internal.ms cflags.ms termcap.ms environ.ms\ + versions.ms question.ms +ASC= title.doc index.doc intro.doc visual.doc ex.doc regexp.doc options.doc\ + cutbufs.doc differ.doc internal.doc cflags.doc termcap.doc environ.doc\ + versions.doc question.doc\ + ctags.doc elvis.doc elvprsv.doc elvrec.doc fmt.doc ref.doc +MANUAL= title.1200 index.1200 intro.1200 visual.1200 ex.1200 regexp.1200 options.1200\ + cutbufs.1200 differ.1200 internal.1200 cflags.1200 termcap.1200 environ.1200\ + versions.1200\ + ctags.1200 elvis.1200 elvprsv.1200 elvrec.1200 fmt.1200 ref.1200 +VER= ver.ms +TROFF= mroff +NROFF= nroff + +############################################################################### +# Rules... + +.SUFFIXES: .tmp .100 .1200 .more .doc .man .ms .vga .vgas + +.ms.tmp: + $(TROFF) -ms $(VER) $< >tmp + +.man.tmp: + $(TROFF) -man $< >tmp + +.ms.more: + $(TROFF) -ms $(VER) $< | draft | more + +.man.more: + $(TROFF) -man $< | draft | more + +.ms.1200: + $(TROFF) -ms $(VER) $< | hp2 1200 | lp -og $(PRINTER) + +.man.1200: + $(TROFF) -man $< | hp2 1200 | lp -og $(PRINTER) + +.ms.100: + $(TROFF) -ms $(VER) $< | hp2 100 | lp -og $(PRINTER) + +.man.100: + $(TROFF) -man $< | hp2 100 | lp -og $(PRINTER) + +.ms.doc: + $(NROFF) -ms $(VER) $< >$@ + +.man.doc: + $(NROFF) -man $< >$@ + +.ms.vga: + $(TROFF) -ms $(VER) $< >/tmp/foo + -vga /tmp/foo + rm /tmp/foo + +.ms.vgas: + $(TROFF) -ms $(VER) $< >/tmp/foo + -vgas /tmp/foo + rm /tmp/foo + +############################################################################# +# Targets... + +asc: $(ASC) + cat $(ASC) >asc + +manual: $(MANUAL) + +clean: + rm -f *.doc *.sh diff --git a/commands/elvis/Makefile b/commands/elvis/Makefile new file mode 100755 index 000000000..f5aae998b --- /dev/null +++ b/commands/elvis/Makefile @@ -0,0 +1,90 @@ +# Makefile for elvis +# +# Several groups of Makefile settings are included below. Choose *ONE* group +# of settings for your particular system, and leave the others commented out. +# The meanings of these settings are: +# EXTRA version-specific object files used in elvis +# CC the C compiler command, possibly with "memory model" flags +# CFLAGS compiler flags used to select compile-time options +# PROGS the list of all programs +# SORT if the "tags" file must be sorted, then SORT=-DSORT + +PROGS= elvis ctags ref elvrec fmt elvprsv + +#---- These settings are recommended for Minix-PC ---- +EXTRA= tinytcap.o tinyprnt.o +CFLAGS= -O -w -D_POSIX_SOURCE -D_MINIX -DCRUNCH \ + -DNO_MKEXRC -DNO_CURSORSHAPE -DNO_CHARATTR -DNO_SHOWMODE \ + -DNO_MODELINE -DNO_OPTCOLS -DNO_DIGRAPH -DNO_EXTENSIONS \ + -DNO_ERRLIST -DNO_FKEY -DNO_VISIBLE -DNO_COLOR -DNO_POPUP +LDFLAGS=-i + +#---- These settings are recommended for Minix-ST ---- +#EXTRA= +#CFLAGS= + +########################################################################### +### The rest of this Makefile contains no user-serviceable parts ### +########################################################################### + +OBJ=blk.o cmd1.o cmd2.o ctype.o curses.o cut.o ex.o input.o \ + main.o misc.o modify.o move1.o move2.o move3.o move4.o move5.o \ + opts.o recycle.o redraw.o regexp.o regsub.o system.o tio.o tmp.o \ + unix.o vars.o vcmd.o vi.o + +all: $(PROGS) + +elvis: $(OBJ) + $(CC) $(LDFLAGS) -o elvis $(OBJ) + install -S 18kw elvis + +ctags: ctags.c + $(CC) $(CFLAGS) $(SORT) $(LDFLAGS) -o ctags ctags.c + install -S 4kw $@ + +ref: ref.c + $(CC) $(CFLAGS) $(LDFLAGS) -o ref ref.c + install -S 4kw $@ + +elvrec: elvrec.c + $(CC) $(CFLAGS) $(LDFLAGS) -o elvrec elvrec.c + install -S 4kw $@ + +fmt: fmt.c + $(CC) $(CFLAGS) $(LDFLAGS) -o fmt fmt.c + install -S 4kw $@ + +elvprsv: elvprsv.c + $(CC) $(CFLAGS) $(LDFLAGS) -o elvprsv elvprsv.c ctype.o + install -S 4kw $@ + +install: /usr/bin/elvis /usr/bin/ex /usr/bin/vi \ + /usr/bin/ctags /usr/bin/ref /usr/bin/fmt \ + /usr/bin/elvrec /usr/bin/elvprsv + +/usr/bin/elvis: elvis + install -cs -o bin elvis $@ + +/usr/bin/ex /usr/bin/vi: /usr/bin/elvis + install -l /usr/bin/elvis $@ + +/usr/bin/ctags: ctags + install -cs -o bin ctags $@ + +/usr/bin/ref: ref + install -cs -o bin ref $@ + +/usr/bin/fmt: fmt + install -cs -o bin fmt $@ + +/usr/bin/elvrec: elvrec + install -cs -o root -m 4755 elvrec $@ + +/usr/bin/elvprsv: elvprsv + install -cs -o root -m 4755 elvprsv $@ + +# Dependencies +$(OBJ): vi.h curses.h config.h regexp.h ctype.h + +clean: + rm -f *.o ctags ref elvrec fmt elvprsv elvis diff --git a/commands/elvis/Man.sh b/commands/elvis/Man.sh new file mode 100755 index 000000000..5dc7f875b --- /dev/null +++ b/commands/elvis/Man.sh @@ -0,0 +1,414 @@ +echo x - ctags.man +sed '/^X/s///' > ctags.man << '/' +X.TH CTAGS 1 +X.SH NAME +Xctags - Generates "tags" and (optionally) "refs" files +X.SH SYNOPSIS +X\fBctags\fP [\fB-stvra\fP] \fIfilesnames\fP... +X.SH DESCRIPTION +X\fIctags\fP generates the "tags" and "refs" files +Xfrom a group of C source files. +XThe "tags" file is used by Elvis' ":tag" command, +Xcontrol-] command, +Xand -t option. +XThe "refs" file is sometimes used by the \fIref(1)\fP program. +X.PP +XEach C source file is scanned for #define statements and +Xglobal function definitions. +XThe name of the macro or function becomes the name of a tag. +XFor each tag, a line is added to the "tags" file which contains: +X.RS +X.nf +X - the name of the tag +X - a tab character +X - the name of the file containing the tag +X - a tab character +X - a way to find the particular line within the file. +X.RE +X.fi +X.PP +XThe filenames list will typically be the names of all C source +Xfiles in the current directory, like this: +X.RS +X.nf +X$ ctags -stv *.[ch] +X.RE +X.fi +X.SH OPTIONS +X.IP \fB-t\fR +XInclude typedefs. +XA tag will be generated for each user-defined type. +XAlso tags will be generated for struct and enum names. +XTypes are considered to be global if they are defined in a header file, +Xand static if they are defined in a C source file. +X.IP \fB-v\fR +XInclude variable declarations. +XA tag will be generated for each variable, except for those that are declared +Xinside the body of a function. +X.IP \fB-s\fR +XInclude static tags. +X\fICtags\fR will normally put global tags in the "tags" file, and silently ignore +Xthe static tags. +XThis flag causes both global and static tags to be added. +XThe name of a static tag is generated by prefixing the name of the declared +Xitem with the name of the file where it is defined, with a colon in between. +XFor example, "static foo(){}" in "bar.c" results in a tag named "bar.c:foo". +X.IP \fB-r\fP +XThis causes \fIctags\fP to generate both "tags" and "refs". +XWithout \fB-r\fP, it would only generate "tags". +X.IP \fB-a\fR +XAppend to "tags", and maybe "refs". +XNormally, \fIctags\fR overwrites these files each time it is invoked. +XThis flag is useful when you have to many files in the current directory +Xfor you to list them on a single command-line; +Xit allows you to split the arguments among several invocations. +X.SH FILES +X.IP tags +XA cross-reference that lists each tag name, the name of the source file that +Xcontains it, and a way to locate a particular line in the source file. +X.IP refs +XThe "refs" file contains the definitions for each tag in the "tags" file, +Xand very little else. +XThis file can be useful, for example, when licensing restrictions prevent +Xyou from making the source code to the standard C library readable by everybody, +Xbut you still everybody to know what arguments the library functions need. +X.SH BUGS +X.PP +X\fIctags\fR is sensitive to indenting and line breaks. +XConsequently, it might not discover all of the tags in a file that +Xis formatted in an unusual way. +X.SH "SEE ALSO" +Xelvis(1), refs(1) +X.SH AUTHOR +X.nf +XSteve Kirkendall +Xkirkenda@cs.pdx.edu +X.fi +/ +echo x - elvis.man +sed '/^X/s///' > elvis.man << '/' +X.TH ELVIS 1 +X.SH NAME +Xelvis, ex, vi, view, input - The editor +X.SH SYNOPSIS +X\fBelvis\fP [\fIflags\fP] [\fB+\fP\fIcmd\fP] [\fIfiles\fP...] +X.SH DESCRIPTION +X\fIElvis\fP is a text editor which emulates \fIvi\fP/\fIex\fP. +X.PP +XOn systems which pass the program name as an argument, such as Unix and Minix, +Xyou may also install \fIelvis\fP under the names "ex", "vi", "view", and "input". +XThese extra names would normally be links to elvis; +Xsee the "ln" shell command. +X.PP +XWhen \fIelvis\fP is invoked as "vi", +Xit behaves exactly as though it was invoked as "elvis". +XHowever, if you invoke \fIelvis\fP as "view", +Xthen the readonly option is set as though you had given it the "-R" flag. +XIf you invoke \fIelvis\fP as "ex", +Xthen \fIelvis\fP will start up in the colon command mode +Xinstead of the visual command mode, +Xas though you had given it the "-e" flag. +XIf you invoke \fIelvis\fP as "input" or "edit", +Xthen \fIelvis\fP will start up in input mode, +Xas though the "-i" flag was given. +X.SH OPTIONS +X.IP \fB-r\fP +XTo the real vi, this flag means that a previous edit should be recovered. +X\fIElvis\fP, though, has a separate program, called \fIelvrec(1)\fP, for recovering +Xfiles. +XWhen you invoke \fIelvis\fP with -r, \fIelvis\fP will tell you to run \fIelvrec\fP. +X.IP \fB-R\fP +XThis sets the "readonly" option, +Xso you won't accidentally overwrite a file. +X.IP "\fB-t\fP \fItag\fP" +XThis causes \fIelvis\fP to start editing at the given tag. +X.IP "\fB-m\fP [\fIfile\fP]" +X\fIElvis\fP will search through \fIfile\fP for something that looks like +Xan error message from a compiler. +XIt will then begin editing the source file that caused the error, +Xwith the cursor sitting on the line where the error was detected. +XIf you don't explicitly name a \fIfile\fP, then "errlist" is assumed. +X.IP \fB-e\fP +X\fIElvis\fP will start up in colon command mode. +X.IP \fB-v\fP +X\fIElvis\fP will start up in visual command mode. +X.IP \fB-i\fP +X\fIElvis\fP will start up in input mode. +X.IP "\fB-w\fR \fIwinsize\fR" +XSets the "window" option's value to \fIwinsize\fR. +X.IP "\fB+\fP\fIcommand\fP or \fB-c\fP \fIcommand\fP" +XIf you use the +\fIcommand\fP parameter, +Xthen after the first file is loaded +X\fIcommand\fP is executed as an EX command. +XA typical example would be "elvis +237 foo", +Xwhich would cause \fIelvis\fP to start editing foo and +Xthen move directly to line 237. +XThe "-c \fIcommand\fP" variant was added for UNIX SysV compatibility. +X.SH FILES +X.IP /tmp/elv* +XDuring editing, +X\fIelvis\fP stores text in a temporary file. +XFor UNIX, this file will usually be stored in the /tmp directory, +Xand the first three characters will be "elv". +XFor other systems, the temporary files may be stored someplace else; +Xsee the version-specific section of the documentation. +X.IP tags +XThis is the database used by the \fI:tags\fP command and the \fB-t\fP option. +XIt is usually created by the \fIctags(1)\fP program. +X.IP ".exrc or elvis.rc" +XOn UNIX-like systems, a file called ".exrc" in your home directory +Xis executed as a series of \fIex\fR commands. +XA file by the same name may be executed in the current directory, too. +XOn non-UNIX systems, ".exrc" is usually an invalid file name; +Xthere, the initialization file is called "elvis.rc" instead. +X.SH "SEE ALSO" +Xctags(1), ref(1), virec(1) +X.PP +X\fIElvis - A Clone of Vi/Ex\fP, the complete \fIelvis\fP documentation. +X.SH BUGS +XThere is no LISP support. +XCertain other features are missing, too. +X.PP +XAuto-indent mode is not quite compatible with the real vi. +XAmong other things, 0^D and ^^D don't do what you might expect. +X.PP +XLong lines are displayed differently. +XThe real vi wraps long lines onto multiple rows of the screen, +Xbut \fIelvis\fP scrolls sideways. +X.SH AUTHOR +X.nf +XSteve Kirkendall +Xkirkenda@cs.pdx.edu +X.fi +X.PP +XMany other people have worked to port \fIelvis\fP to various operating systems. +XTo see who deserves credit, run the \fI:version\fP command from within \fIelvis\fP, +Xor look in the system-specific section of the complete documentation. +/ +echo x - elvprsv.man +sed '/^X/s///' > elvprsv.man << '/' +X.TH ELVPRSV 1 +X.SH NAME +Xelvprsv - Preserve the the modified version of a file after a crash. +X.SH SYNOPSIS +X.nf +X\fB\fBelvprsv\fP ["-\fIwhy elvis died\fP"] /tmp/\fIfilename\fP... +X\fB\fBelvprsv\fP -R /tmp/\fIfilename\fP... +X.fi +X.SH DESCRIPTION +X.PP +X\fIelvprsv\fP preserves your edited text after \fIelvis\fP dies. +XThe text can be recovered later, via the \fIelvprsv\fP program. +X.PP +XFor UNIX-like systems, +Xyou should never need to run this program from the command line. +XIt is run automatically when \fIelvis\fP is about to die, +Xand it should be run (via /etc/rc) when the computer is booted. +XTHAT'S ALL! +X.PP +XFor non-UNIX systems such as MS-DOS, you can either use \fIelvprsv\fP +Xthe same way as under UNIX systems (by running it from your AUTOEXEC.BAT file), +Xor you can run it separately with the "-R" flag to recover the files +Xin one step. +X.PP +XIf you're editing a file when \fIelvis\fP dies +X(due to a bug, system crash, power failure, etc.) +Xthen \fIelvprsv\fP will preserve the most recent version of your text. +XThe preserved text is stored in a special directory; it does NOT overwrite +Xyour text file automatically. +X.PP +X\fIelvprsv\fP will send mail to any user whose work it preserves, +Xif your operating system normally supports mail. +X.SH FILES +X.IP /tmp/elv* +XThe temporary file that \fIelvis\fP was using when it died. +X.IP /usr/preserve/p* +XThe text that is preserved by \fIelvprsv\fP. +X.IP /usr/preserve/Index +XA text file which lists the names of all preserved files, and the names +Xof the /usr/preserve/p* files which contain their preserved text. +X.SH BUGS +X.PP +XDue to the permissions on the /usr/preserve directory, on UNIX systems +X\fIelvprsv\fP must be run as superuser. +XThis is accomplished by making the \fIelvprsv\fP executable be owned by "root" +Xand turning on its "set user id" bit. +X.PP +XIf you're editing a nameless buffer when \fIelvis\fP dies, then \fIelvprsv\fP will pretend +Xthat the file was named "foo". +X.SH AUTHOR +X.nf +XSteve Kirkendall +Xkirkenda@cs.pdx.edu +X.fi +/ +echo x - elvrec.man +sed '/^X/s///' > elvrec.man << '/' +X.TH ELVREC 1 +X.SH NAME +Xelvrec - Recover the modified version of a file after a crash +X.SH SYNOPSIS +X.nf +X\fBelvrec\fP [\fIpreservedfile\fP [\fInewfile\fR]] +X.fi +X.SH DESCRIPTION +X.PP +XIf you're editing a file when \fIelvis\fP dies, the system crashes, or power fails, +Xthe most recent version of your text will be preserved. +XThe preserved text is stored in a special directory; it does NOT overwrite +Xyour text file automatically. +X.PP +XThe \fIelvrec\fP program locates the preserved version of a given file, +Xand writes it over the top of your text file -- or to a new file, if you prefer. +XThe recovered file will have nearly all of your changes. +X.PP +XTo see a list of all recoverable files, run \fIelvrec\fP with no arguments. +X.SH FILES +X.IP /usr/preserve/p* +XThe text that was preserved when \fIelvis\fP died. +X.IP /usr/preserve/Index +XA text file which lists the names of all preserved files, and the names +Xof the /usr/preserve/p* files which contain their preserved text. +X.SH BUGS +X.PP +X\fIelvrec\fP is very picky about filenames. +XYou must tell it to recover the file using exactly the same pathname as +Xwhen you were editing it. +XThe simplest way to do this is to go into the same directory that you were +Xediting, and invoke \fIelvrec\fP with the same filename as \fIelvis\fP. +XIf that doesn't work, then try running \fIelvrec\fP with no arguments, +Xto see exactly which pathname it is using for the desired file. +X.PP +XDue to the permissions on the /usr/preserve directory, on UNIX systems +X\fIelvrec\fP must be run as superuser. +XThis is accomplished by making the \fIelvrec\fP executable be owned by "root" +Xand setting its "set user id" bit. +X.PP +XIf you're editing a nameless buffer when \fIelvis\fP dies, then \fIelvrec\fP +Xwill pretend that the file was named "foo". +X.SH AUTHOR +X.nf +XSteve Kirkendall +Xkirkenda@cs.pdx.edu +X.fi +/ +echo x - fmt.man +sed '/^X/s///' > fmt.man << '/' +X.TH FMT 1 +X.SH NAME +Xfmt - adjust line-length for paragraphs of text +X.SH SYNOPSIS +X\fBfmt\fP [\-\fIwidth\fP] [\fIfiles\fP]... +X.SH DESCRIPTION +X\fIfmt\fR is a simple text formatter. +XIt inserts or deletes newlines, as necessary, to make all lines in a +Xparagraph be approximately the same width. +XIt preserves indentation and word spacing. +X.PP +XThe default line width is 72 characters. +XYou can override this with the \-\fIwidth\fR flag. +XIf you don't name any files on the command line, +Xthen \fIfmt\fR will read from stdin. +X.PP +XIt is typically used from within \fIvi\fR to adjust the line breaks +Xin a single paragraph. +XTo do this, move the cursor to the top of the paragraph, +Xtype "!}fmt", and +Xhit <Return>. +X.SH AUTHOR +X.nf +XSteve Kirkendall +Xkirkenda@cs.pdx.edu +X.fi +/ +echo x - ref.man +sed '/^X/s///' > ref.man << '/' +X.TH REF 1 +X.SH NAME +Xref - Display a C function header +X.SH SYNOPSIS +X\fBref\fR [-t] [-c \fIclass\fR]... [-f \fIfile\fR]... \fItag\fR +X.SH DESCRIPTION +X\fIref\fP quickly locates and displays the header of a function. +XTo do this, \fIref\fR +Xlooks in the "tags" file for the line that describes the function, and then +Xscans the source file for the function. +XWhen it locates the function, it displays an introductory comment +X(if there is one), the function's declaration, and the declarations of all +Xarguments. +X.SH "SEARCH METHOD" +X.PP +X\fIref\fR uses a fairly sophisticated tag look-up algorithm. +XIf you supply a filename via \fB-f\fR \fIfile\fR, then elvis first scans +Xthe tags file for a static tag from that file. +XThis search is limited to the tags file in the current directory. +X.PP +XIf you supply a classname via \fB-c\fR \fIclass\fR, then elvis searches +Xfor a tag from that class. +XThis search is not limited to the current directory; +XYou can supply a list of directories in the environment variable \fITAGPATH\fR, +Xand \fIref\fR will search through the "tags" file in each directory until it finds +Xa tag in the desired class. +X.PP +XIf that fails, \fIref\fR will then try to look up an ordinary global tag. +XThis search checks all of the directories listed in \fITAGPATH\fR, too. +X.PP +XIf you've given the \fB-t\fR flag, then \fIref\fR will simply output the tag line that +Xit found, and then exit. +XWithout \fB-t\fR, though, \fIref\fR will search for the tag line. +XIt will try to open the source file, which should be in the same directory +Xas the tags file where the tag was discovered. +XIf the source file doesn't exist, or is unreadable, then \fIref\fR will try to open +Xa file called "\fIrefs\fR" in that directory. +XEither way, \fIref\fR will try to locate the tag, and display whatever it finds. +X.SH "INTERACTION WITH ELVIS" +X.PP +X\fIref\fP is used by \fIelvis\fR' shift-K command. +XIf the cursor is located on a word such as "splat", in the file "foo.c", +Xthen \fIelvis\fR will invoke \fIref\fR with the command "ref -f foo.c splat". +X.PP +XIf \fIelvis\fR has been compiled with the -DEXTERNAL_TAGS flag, then \fIelvis\fR will +Xuse \fIref\fR \fB\fRto scan the tags files. +XThis is slower than the built-in tag searching, but it allows \fIelvis\fR to access +Xthe more sophisticated tag lookup provided by \fIref\fR. +XOther than that, external tags should act exactly like internal tags. +X.SH OPTIONS +X.IP \fB-t\fR +XOutput tag info, instead of the function header. +X.IP "\fB-f\fR \fIfile\fR" +XThe tag might be a static function in \fIfile\fR. +XYou can use several -f flags to have \fIref\fR consider static tags from more than one file. +X.IP "\fB-c\fR \fIclass\fR" +XThe tag might be a member of class \fIclass\fR. +XYou can use several -c flags to have \fIref\fR consider tags from more than one class. +X.SH FILES +X.IP \fBtags\fR +XList of function names and their locations, generated by \fIctags\fR. +X.IP \fBrefs\fR +XFunction headers extracted from source files (optional). +X.SH ENVIRONMENT +X.IP \fBTAGPATH\fR +XList of directories to be searched. +XThe elements in the list are separated by either +Xsemicolons (for MS-DOS, Atari TOS, and AmigaDos), or +Xby colons (every other operating system). +XFor each operating system, \fIref\fR has a built-in default which is probably +Xadequate. +X.SH NOTES +X.PP +XYou might want to generate a "tags" file the directory that contains the +Xsource code for standard C library on your system. +XIf licensing restrictions prevent you from making the library source readable +Xby everybody, then you can have \fIctags\fR generate a "refs" file, +Xand make "refs" readable by everybody. +X.PP +XIf your system doesn't come with the library source code, then perhaps you +Xcan produce something workable from the \fIlint\fR libraries. +X.SH "SEE ALSO" +Xelvis(1), ctags(1) +X.SH AUTHOR +X.nf +XSteve Kirkendall +Xkirkenda@cs.pdx.edu +X.fi +/ diff --git a/commands/elvis/Readme.txt b/commands/elvis/Readme.txt new file mode 100755 index 000000000..dc7656f99 --- /dev/null +++ b/commands/elvis/Readme.txt @@ -0,0 +1,31 @@ +Elvis is a clone of vi/ex, the standard UNIX editor. Elvis supports +nearly all of the vi/ex commands, in both visual mode and colon mode. + +Elvis runs under BSD UNIX, AT&T SysV UNIX, SCO Xenix, Minix, MS-DOS +(Turbo-C or MSC 5.1), Atari TOS, OS9/68000, Coherent, VMS, and AmigaDos. +Ports to other operating systems are in progress; contact me before you +start porting it to some other OS, because somebody else may have +already done it for you. + +Elvis is freely redistributable, in either source form or executable +form. There are no restrictions on how you may use it. + +The file "elvisman.txt" contains the manual for elvis. It is a plain +ASCII file with nothing more exotic than a newline character. It is +formatted for 66-line, 80-column pages. There may also be an archive of +"*.ms" and "*.man" files, which contain the TROFF source text used to +generate that manual. + +The file named "Makefile.mix" is used to compile elvis for all systems +except VMS and possibly MS-DOS. You should copy "Makefile.mix" to +"Makefile", and then edit "Makefile" to select the appropriate group of +settings for your system. + + +Author: Steve Kirkendall + 14407 SW Teal Blvd. #C + Beaverton, OR 97005 + +E-mail: kirkenda@cs.pdx.edu + +Phone: (503) 643-6980 diff --git a/commands/elvis/blk.c b/commands/elvis/blk.c new file mode 100755 index 000000000..db41d9320 --- /dev/null +++ b/commands/elvis/blk.c @@ -0,0 +1,469 @@ +/* blk.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains the functions that get/put blocks from the temp file. + * It also contains the "do" and "undo" functions. + */ + +#include "config.h" +#include "vi.h" + +#ifndef NBUFS +# define NBUFS 5 /* must be at least 3 -- more is better */ +#endif + + +/*------------------------------------------------------------------------*/ + +BLK hdr; /* buffer for the header block */ + +static int b4cnt; /* used to count context of beforedo/afterdo */ +static struct _blkbuf +{ + BLK buf; /* contents of a text block */ + unsigned short logical; /* logical block number */ + int dirty; /* must the buffer be rewritten? */ +} + blk[NBUFS], /* buffers for text[?] blocks */ + *toonew, /* buffer which shouldn't be recycled yet */ + *newtoo, /* another buffer which should be recycled */ + *recycle = blk; /* next block to be recycled */ + + + + + +/* This function wipes out all buffers */ +void blkinit() +{ + int i; + + for (i = 0; i < NBUFS; i++) + { + blk[i].logical = 0; + blk[i].dirty = FALSE; + } + for (i = 0; i < MAXBLKS; i++) + { + hdr.n[i] = 0; + } +} + +/* This function allocates a buffer and fills it with a given block's text */ +BLK *blkget(logical) + int logical; /* logical block number to fetch */ +{ + REG struct _blkbuf *this; /* used to step through blk[] */ + REG int i; + + /* if logical is 0, just return the hdr buffer */ + if (logical == 0) + { + return &hdr; + } + + /* see if we have that block in mem already */ + for (this = blk; this < &blk[NBUFS]; this++) + { + if (this->logical == logical) + { + newtoo = toonew; + toonew = this; + return &this->buf; + } + } + + /* choose a block to be recycled */ + do + { + this = recycle++; + if (recycle == &blk[NBUFS]) + { + recycle = blk; + } + } while (this == toonew || this == newtoo); + + /* if it contains a block, flush that block */ + blkflush(this); + + /* fill this buffer with the desired block */ + this->logical = logical; + if (hdr.n[logical]) + { + /* it has been used before - fill it from tmp file */ + lseek(tmpfd, (long)hdr.n[logical] * (long)BLKSIZE, 0); + if (read(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE) + { + msg("Error reading back from tmp file!"); + } + } + else + { + /* it is new - zero it */ + for (i = 0; i < BLKSIZE; i++) + { + this->buf.c[i] = 0; + } + } + + /* This isn't really a change, but it does potentially invalidate + * the kinds of shortcuts that the "changes" variable is supposed + * to protect us from... so count it as a change. + */ + changes++; + + /* mark it as being "not dirty" */ + this->dirty = 0; + + /* return it */ + newtoo = toonew; + toonew = this; + return &this->buf; +} + + + +/* This function writes a block out to the temporary file */ +void blkflush(this) + REG struct _blkbuf *this; /* the buffer to flush */ +{ + long seekpos; /* seek position of the new block */ + unsigned short physical; /* physical block number */ + + /* if its empty (an orphan blkadd() maybe?) then make it dirty */ + if (this->logical && !*this->buf.c) + { + blkdirty(&this->buf); + } + + /* if it's an empty buffer or a clean version is on disk, quit */ + if (!this->logical || hdr.n[this->logical] && !this->dirty) + { + return; + } + + /* find a free place in the file */ +#ifndef NO_RECYCLE + seekpos = allocate(); + lseek(tmpfd, seekpos, 0); +#else + seekpos = lseek(tmpfd, 0L, 2); +#endif + physical = seekpos / BLKSIZE; + + /* put the block there */ + if (write(tmpfd, this->buf.c, (unsigned)BLKSIZE) != BLKSIZE) + { + msg("Trouble writing to tmp file"); + } + this->dirty = FALSE; + + /* update the header so it knows we put it there */ + hdr.n[this->logical] = physical; +} + + +/* This function sets a block's "dirty" flag or deletes empty blocks */ +void blkdirty(bp) + BLK *bp; /* buffer returned by blkget() */ +{ + REG int i, j; + REG char *scan; + REG int k; + + /* find the buffer */ + for (i = 0; i < NBUFS && bp != &blk[i].buf; i++) + { + } +#ifdef DEBUG + if (i >= NBUFS) + { + msg("blkdirty() called with unknown buffer at 0x%lx", bp); + return; + } + if (blk[i].logical == 0) + { + msg("blkdirty called with freed buffer"); + return; + } +#endif + + /* if this block ends with line# INFINITY, then it must have been + * allocated unnecessarily during tmpstart(). Forget it. + */ + if (lnum[blk[i].logical] == INFINITY) + { +#ifdef DEBUG + if (blk[i].buf.c[0]) + { + msg("bkldirty called with non-empty extra BLK"); + } +#endif + blk[i].logical = 0; + blk[i].dirty = FALSE; + return; + } + + /* count lines in this block */ + for (j = 0, scan = bp->c; *scan && scan < bp->c + BLKSIZE; scan++) + { + if (*scan == '\n') + { + j++; + } + } + + /* adjust lnum, if necessary */ + k = blk[i].logical; + j += (lnum[k - 1] - lnum[k]); + if (j != 0) + { + nlines += j; + while (k < MAXBLKS && lnum[k] != INFINITY) + { + lnum[k++] += j; + } + } + + /* if it still has text, mark it as dirty */ + if (*bp->c) + { + blk[i].dirty = TRUE; + } + else /* empty block, so delete it */ + { + /* adjust the cache */ + k = blk[i].logical; + for (j = 0; j < NBUFS; j++) + { + if (blk[j].logical >= k) + { + blk[j].logical--; + } + } + + /* delete it from hdr.n[] and lnum[] */ + blk[i].logical = 0; + blk[i].dirty = FALSE; + while (k < MAXBLKS - 1) + { + hdr.n[k] = hdr.n[k + 1]; + lnum[k] = lnum[k + 1]; + k++; + } + hdr.n[MAXBLKS - 1] = 0; + lnum[MAXBLKS - 1] = INFINITY; + } +} + + +/* insert a new block into hdr, and adjust the cache */ +BLK *blkadd(logical) + int logical; /* where to insert the new block */ +{ + REG int i; + + /* adjust hdr and lnum[] */ + for (i = MAXBLKS - 1; i > logical; i--) + { + hdr.n[i] = hdr.n[i - 1]; + lnum[i] = lnum[i - 1]; + } + hdr.n[logical] = 0; + lnum[logical] = lnum[logical - 1]; + + /* adjust the cache */ + for (i = 0; i < NBUFS; i++) + { + if (blk[i].logical >= logical) + { + blk[i].logical++; + } + } + + /* return the new block, via blkget() */ + return blkget(logical); +} + + +/* This function forces all dirty blocks out to disk */ +void blksync() +{ + int i; + + for (i = 0; i < NBUFS; i++) + { + /* blk[i].dirty = TRUE; */ + blkflush(&blk[i]); + } + if (*o_sync) + { + sync(); + } +} + +/*------------------------------------------------------------------------*/ + +static MARK undocurs; /* where the cursor should go if undone */ +static long oldnlines; +static long oldlnum[MAXBLKS]; + + +/* This function should be called before each command that changes the text. + * It defines the state that undo() will reset the file to. + */ +void beforedo(forundo) + int forundo; /* boolean: is this for an undo? */ +{ + REG int i; + REG long l; + + /* if this is a nested call to beforedo, quit! Use larger context */ + if (b4cnt++ > 0) + { + return; + } + + /* force all block buffers to disk */ + blksync(); + +#ifndef NO_RECYCLE + /* perform garbage collection on blocks from tmp file */ + garbage(); +#endif + + /* force the header out to disk */ + lseek(tmpfd, 0L, 0); + if (write(tmpfd, hdr.c, (unsigned)BLKSIZE) != BLKSIZE) + { + msg("Trouble writing header to tmp file "); + } + + /* copy or swap oldnlines <--> nlines, oldlnum <--> lnum */ + if (forundo) + { + for (i = 0; i < MAXBLKS; i++) + { + l = lnum[i]; + lnum[i] = oldlnum[i]; + oldlnum[i] = l; + } + l = nlines; + nlines = oldnlines; + oldnlines = l; + } + else + { + for (i = 0; i < MAXBLKS; i++) + { + oldlnum[i] = lnum[i]; + } + oldnlines = nlines; + } + + /* save the cursor position */ + undocurs = cursor; + + /* upon return, the calling function continues and makes changes... */ +} + +/* This function marks the end of a (nested?) change to the file */ +void afterdo() +{ + if (--b4cnt) + { + /* after abortdo(), b4cnt may decribe nested beforedo/afterdo + * pairs incorrectly. If it is decremented to often, then + * keep b4cnt sane but don't do anything else. + */ + if (b4cnt < 0) + b4cnt = 0; + + return; + } + + /* make sure the cursor wasn't left stranded in deleted text */ + if (markline(cursor) > nlines) + { + cursor = MARK_LAST; + } + /* NOTE: it is still possible that markidx(cursor) is after the + * end of a line, so the Vi mode will have to take care of that + * itself */ + + /* if a significant change has been made to this file, then set the + * MODIFIED flag. + */ + if (significant) + { + setflag(file, MODIFIED); + setflag(file, UNDOABLE); + } +} + +/* This function cuts short the current set of changes. It is called after + * a SIGINT. + */ +void abortdo() +{ + /* finish the operation immediately. */ + if (b4cnt > 0) + { + b4cnt = 1; + afterdo(); + } + + /* in visual mode, the screen is probably screwed up */ + if (mode == MODE_COLON) + { + mode = MODE_VI; + } + if (mode == MODE_VI) + { + redraw(MARK_UNSET, FALSE); + } +} + +/* This function discards all changes made since the last call to beforedo() */ +int undo() +{ + BLK oldhdr; + + /* if beforedo() has never been run, fail */ + if (!tstflag(file, UNDOABLE)) + { + msg("You haven't modified this file yet."); + return FALSE; + } + + /* read the old header form the tmp file */ + lseek(tmpfd, 0L, 0); + if (read(tmpfd, oldhdr.c, (unsigned)BLKSIZE) != BLKSIZE) + { + msg("Trouble rereading the old header from tmp file"); + } + + /* "do" the changed version, so we can undo the "undo" */ + cursor = undocurs; + beforedo(TRUE); + afterdo(); + + /* wipe out the block buffers - we can't assume they're correct */ + blkinit(); + + /* use the old header -- and therefore the old text blocks */ + hdr = oldhdr; + + /* This is a change */ + significant = TRUE; + changes++; + + return TRUE; +} diff --git a/commands/elvis/cmd1.c b/commands/elvis/cmd1.c new file mode 100755 index 000000000..493005267 --- /dev/null +++ b/commands/elvis/cmd1.c @@ -0,0 +1,1774 @@ +/* cmd1.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains some of the EX commands - mostly ones that deal with + * files, options, etc. -- anything except text. + */ + +#include "config.h" +#include "ctype.h" +#include "vi.h" +#include "regexp.h" + +#ifdef DEBUG +/* print the selected lines with info on the blocks */ +/*ARGSUSED*/ +void cmd_debug(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + REG char *scan; + REG long l; + REG int i; + int len; + + /* scan lnum[] to determine which block its in */ + l = markline(frommark); + for (i = 1; l > lnum[i]; i++) + { + } + + do + { + /* fetch text of the block containing that line */ + scan = blkget(i)->c; + + /* calculate its length */ + if (scan[BLKSIZE - 1]) + { + len = BLKSIZE; + } + else + { + len = strlen(scan); + } + + /* print block stats */ + msg("##### hdr[%d]=%d, lnum[%d-1]=%ld, lnum[%d]=%ld (%ld lines)", + i, hdr.n[i], i, lnum[i-1], i, lnum[i], lnum[i] - lnum[i - 1]); + msg("##### len=%d, buf=0x%lx, %sdirty", + len, scan, ((int *)scan)[MAXBLKS + 1] ? "" : "not "); + if (bang) + { + while (--len >= 0) + { + addch(*scan); + scan++; + } + } + exrefresh(); + + /* next block */ + i++; + } while (i < MAXBLKS && lnum[i] && lnum[i - 1] < markline(tomark)); +} + + +/* This function checks a lot of conditions to make sure they aren't screwy */ +/*ARGSUSED*/ +void cmd_validate(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + char *scan; + int i; + int nlcnt; /* used to count newlines */ + int len; /* counts non-NUL characters */ + + /* check lnum[0] */ + if (lnum[0] != 0L) + { + msg("lnum[0] = %ld", lnum[0]); + } + + /* check each block */ + for (i = 1; lnum[i] <= nlines; i++) + { + scan = blkget(i)->c; + if (scan[BLKSIZE - 1]) + { + msg("block %d has no NUL at the end", i); + } + else + { + for (nlcnt = len = 0; *scan; scan++, len++) + { + if (*scan == '\n') + { + nlcnt++; + } + } + if (scan[-1] != '\n') + { + msg("block %d doesn't end with '\\n' (length %d)", i, len); + } + if (bang || nlcnt != lnum[i] - lnum[i - 1]) + { + msg("block %d (line %ld?) has %d lines, but should have %ld", + i, lnum[i - 1] + 1L, nlcnt, lnum[i] - lnum[i - 1]); + } + } + exrefresh(); + } + + /* check lnum again */ + if (lnum[i] != INFINITY) + { + msg("hdr.n[%d] = %d, but lnum[%d] = %ld", + i, hdr.n[i], i, lnum[i]); + } + + msg("# = \"%s\", %% = \"%s\"", prevorig, origname); + msg("V_from=%ld.%d, cursor=%ld.%d", markline(V_from), markidx(V_from), markline(cursor), markidx(cursor)); +} +#endif /* DEBUG */ + + +/*ARGSUSED*/ +void cmd_mark(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + /* validate the name of the mark */ + if (*extra == '"') + { + extra++; + } + /* valid mark names are lowercase ascii characters */ + if (!isascii(*extra) || !islower(*extra) || extra[1]) + { + msg("Invalid mark name"); + return; + } + + mark[*extra - 'a'] = tomark; +} + +/*ARGSUSED*/ +void cmd_write(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + int fd; + int append; /* boolean: write in "append" mode? */ + REG long l; + REG char *scan; + REG int i; + + /* if writing to a filter, then let filter() handle it */ + if (*extra == '!') + { + filter(frommark, tomark, extra + 1, FALSE); + return; + } + + /* if all lines are to be written, use tmpsave() */ + if (frommark == MARK_FIRST && tomark == MARK_LAST && cmd == CMD_WRITE) + { + tmpsave(extra, bang); + return; + } + + /* see if we're going to do this in append mode or not */ + append = FALSE; + if (extra[0] == '>' && extra[1] == '>') + { + extra += 2; + append = TRUE; + } + + /* either the file must not exist, or we must have a ! or be appending */ + if (access(extra, 0) == 0 && !bang && !append) + { + msg("File already exists - Use :w! to overwrite"); + return; + } + + /* else do it line-by-line, like cmd_print() */ + if (append) + { +#ifdef O_APPEND + fd = open(extra, O_WRONLY|O_APPEND); +#else + fd = open(extra, O_WRONLY); + if (fd >= 0) + { + lseek(fd, 0L, 2); + } +#endif + } + else + { + fd = -1; /* so we know the file isn't open yet */ + } + + if (fd < 0) + { + fd = creat(extra, FILEPERMS); + if (fd < 0) + { + msg("Can't write to \"%s\"", extra); + return; + } + } + for (l = markline(frommark); l <= markline(tomark); l++) + { + /* get the next line */ + scan = fetchline(l); + i = strlen(scan); + scan[i++] = '\n'; + + /* print the line */ + if (twrite(fd, scan, i) < i) + { + msg("Write failed"); + break; + } + } + rptlines = markline(tomark) - markline(frommark) + 1; + rptlabel = "written"; + close(fd); +} + + +/*ARGSUSED*/ +void cmd_shell(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ + static char prevextra[80]; + + /* special case: ":sh" means ":!sh" */ + if (cmd == CMD_SHELL) + { + extra = o_shell; + frommark = tomark = 0L; + } + + /* if extra is "!", substitute previous command */ + if (*extra == '!') + { + if (!*prevextra) + { + msg("No previous shell command to substitute for '!'"); + return; + } + extra = prevextra; + } + else if (cmd == CMD_BANG && strlen(extra) < sizeof(prevextra) - 1) + { + strcpy(prevextra, extra); + } + + /* warn the user if the file hasn't been saved yet */ + if (*o_warn && tstflag(file, MODIFIED)) + { + if (mode == MODE_VI) + { + mode = MODE_COLON; + } + msg("Warning: \"%s\" has been modified but not yet saved", origname); + } + + /* if no lines were specified, just run the command */ + suspend_curses(); + if (frommark == 0L) + { + system(extra); + } + else /* pipe lines from the file through the command */ + { + filter(frommark, tomark, extra, TRUE); + } + + /* resume curses quietly for MODE_EX, but noisily otherwise */ + resume_curses(mode == MODE_EX); +} + + +/*ARGSUSED*/ +void cmd_global(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; /* rest of the command line */ +{ + char *cmdptr; /* the command from the command line */ + char cmdln[100]; /* copy of the command from the command line */ + char *line; /* a line from the file */ + long l; /* used as a counter to move through lines */ + long lqty; /* quantity of lines to be scanned */ + long nchanged; /* number of lines changed */ + regexp *re; /* the compiled search expression */ + + /* can't nest global commands */ + if (doingglobal) + { + msg("Can't nest global commands."); + rptlines = -1L; + return; + } + + /* ":g! ..." is the same as ":v ..." */ + if (bang) + { + cmd = CMD_VGLOBAL; + } + + /* make sure we got a search pattern */ + if (*extra != '/' && *extra != '?') + { + msg("Usage: %c /regular expression/ command", cmd == CMD_GLOBAL ? 'g' : 'v'); + return; + } + + /* parse & compile the search pattern */ + cmdptr = parseptrn(extra); + if (!extra[1]) + { + msg("Can't use empty regular expression with '%c' command", cmd == CMD_GLOBAL ? 'g' : 'v'); + return; + } + re = regcomp(extra + 1); + if (!re) + { + /* regcomp found & described an error */ + return; + } + + /* for each line in the range */ + doingglobal = TRUE; + ChangeText + { + /* NOTE: we have to go through the lines in a forward order, + * otherwise "g/re/p" would look funny. *BUT* for "g/re/d" + * to work, simply adding 1 to the line# on each loop won't + * work. The solution: count lines relative to the end of + * the file. Think about it. + */ + for (l = nlines - markline(frommark), + lqty = markline(tomark) - markline(frommark) + 1L, + nchanged = 0L; + lqty > 0 && nlines - l >= 0 && nchanged >= 0L; + l--, lqty--) + { + /* fetch the line */ + line = fetchline(nlines - l); + + /* if it contains the search pattern... */ + if ((!regexec(re, line, 1)) == (cmd != CMD_GLOBAL)) + { + /* move the cursor to that line */ + cursor = MARK_AT_LINE(nlines - l); + + /* do the ex command (without mucking up + * the original copy of the command line) + */ + strcpy(cmdln, cmdptr); + rptlines = 0L; + doexcmd(cmdln); + nchanged += rptlines; + } + } + } + doingglobal = FALSE; + + /* free the regexp */ + free(re); + + /* Reporting...*/ + rptlines = nchanged; +} + + +/*ARGSUSED*/ +void cmd_file(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ +#ifndef CRUNCH + /* if we're given a new filename, use it as this file's name */ + if (extra && *extra) + { + strcpy(origname, extra); + storename(origname); + setflag(file, NOTEDITED); + } +#endif + if (cmd == CMD_FILE) + { +#ifndef CRUNCH + msg("\"%s\" %s%s%s %ld lines, line %ld [%ld%%]", +#else + msg("\"%s\" %s%s %ld lines, line %ld [%ld%%]", +#endif + *origname ? origname : "[NO FILE]", + tstflag(file, MODIFIED) ? "[MODIFIED]" : "", +#ifndef CRUNCH + tstflag(file, NOTEDITED) ?"[NOT EDITED]":"", +#endif + tstflag(file, READONLY) ? "[READONLY]" : "", + nlines, + markline(frommark), + markline(frommark) * 100 / nlines); + } +#ifndef CRUNCH + else if (markline(frommark) != markline(tomark)) + { + msg("range \"%ld,%ld\" contains %ld lines", + markline(frommark), + markline(tomark), + markline(tomark) - markline(frommark) + 1L); + } +#endif + else + { + msg("%ld", markline(frommark)); + } +} + + +/*ARGSUSED*/ +void cmd_edit(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ + long line = 1L; /* might be set to prevline */ +#ifndef CRUNCH + char *init = (char *)0; +#endif + + + /* if ":vi", then switch to visual mode, and if no file is named + * then don't switch files. + */ + if (cmd == CMD_VISUAL) + { + mode = MODE_VI; + msg(""); + if (!*extra) + { + return; + } + } + + /* Editing previous file? Then start at previous line */ + if (!strcmp(extra, prevorig)) + { + line = prevline; + } + +#ifndef CRUNCH + /* if we were given an explicit starting line, then start there */ + if (*extra == '+') + { + for (init = ++extra; !isspace(*extra); extra++) + { + } + while (isspace(*extra)) + { + *extra++ = '\0'; + } + if (!*init) + { + init = "$"; + } + if (!extra) + { + extra = origname; + } + } +#endif /* not CRUNCH */ + + /* switch files */ + if (tmpabort(bang)) + { + tmpstart(extra); + if (line <= nlines && line >= 1L) + { + cursor = MARK_AT_LINE(line); + } +#ifndef CRUNCH + if (init) + { + doexcmd(init); + } +#endif + } + else + { + msg("Use edit! to abort changes, or w to save changes"); + + /* so we can say ":e!#" next time... */ + strcpy(prevorig, extra); + prevline = 1L; + } +} + +/* This code is also used for rewind -- GB */ + +/*ARGSUSED*/ +void cmd_next(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ + int i, j; + char *scan; + + /* if extra stuff given, use ":args" to define a new args list */ + if (cmd == CMD_NEXT && extra && *extra) + { + cmd_args(frommark, tomark, cmd, bang, extra); + } + + /* move to the next arg */ + if (cmd == CMD_NEXT) + { + i = argno + 1; + } + else if (cmd == CMD_PREVIOUS) + { + i = argno - 1; + } + else /* cmd == CMD_REWIND */ + { + i = 0; + } + if (i < 0 || i >= nargs) + { + msg("No %sfiles to edit", cmd == CMD_REWIND ? "" : "more "); + return; + } + + /* find & isolate the name of the file to edit */ + for (j = i, scan = args; j > 0; j--) + { + while(*scan++) + { + } + } + + /* switch to the next file */ + if (tmpabort(bang)) + { + tmpstart(scan); + argno = i; + } + else + { + msg("Use :%s! to abort changes, or w to save changes", + cmd == CMD_NEXT ? "next" : + cmd == CMD_PREVIOUS ? "previous" : + "rewind"); + } +} + +/* also called from :wq -- always writes back in this case */ + +/*ARGSUSED*/ +void cmd_xit(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ + static long whenwarned; /* when the user was last warned of extra files */ + int oldflag; + + /* if there are more files to edit, then warn user */ + if (argno >= 0 && argno + 1 < nargs && whenwarned != changes && (!bang || cmd != CMD_QUIT)) + { + msg("More files to edit -- Use \":n\" to go to next file"); + whenwarned = changes; + return; + } + + if (cmd == CMD_QUIT) + { + oldflag = *o_autowrite; + *o_autowrite = FALSE; + if (tmpabort(bang)) + { + mode = MODE_QUIT; + } + else + { + msg("Use q! to abort changes, or wq to save changes"); + } + *o_autowrite = oldflag; + } + else + { + /* else try to save this file */ + oldflag = tstflag(file, MODIFIED); + if (cmd == CMD_WQUIT) + setflag(file, MODIFIED); + if (tmpend(bang)) + { + mode = MODE_QUIT; + } + else + { + msg("Could not save file -- use quit! to abort changes, or w filename"); + } + if (!oldflag) + clrflag(file, MODIFIED); + } +} + + +/*ARGSUSED*/ +void cmd_args(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ + char *scan; + int col; + int arg; + int scrolled = FALSE; + int width; + + /* if no extra names given, or just current name, then report the args + * we have now. + */ + if (!extra || !*extra) + { + /* empty args list? */ + if (nargs == 1 && !*args) + { + return; + } + + /* list the arguments */ + for (scan = args, col = arg = 0; + arg < nargs; + scan += width + 1, col += width, arg++) + { + width = strlen(scan); + if (col + width >= COLS - 4) + { + addch('\n'); + col = 0; + scrolled = TRUE; + } + else if (col > 0) + { + addch(' '); + col++; + } + if (arg == argno) + { + addch('['); + addstr(scan); + addch(']'); + col += 2; + } + else + { + addstr(scan); + } + } + + /* write a trailing newline */ + if ((mode == MODE_EX || mode == MODE_COLON || scrolled) && col) + { + addch('\n'); + } + exrefresh(); + } + else /* new args list given */ + { + for (scan = args, nargs = 1; *extra; ) + { + if (isspace(*extra)) + { + *scan++ = '\0'; + while (isspace(*extra)) + { + extra++; + } + if (*extra) + { + nargs++; + } + } + else + { + *scan++ = *extra++; + } + } + *scan = '\0'; + + /* reset argno to before the first, so :next will go to first */ + argno = -1; + + if (nargs != 1) + { + msg("%d files to edit", nargs); + } + } +} + + +/*ARGSUSED*/ +void cmd_cd(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ + char *getenv(); + +#ifndef CRUNCH + /* if current file is modified, and no '!' was given, then error */ + if (tstflag(file, MODIFIED) && !bang) + { + msg("File modified; use \"cd! %s\" to switch anyway", extra); + } +#endif + + /* default directory name is $HOME */ + if (!*extra) + { + extra = getenv("HOME"); + if (!extra) + { + msg("environment variable $HOME not set"); + return; + } + } + + /* go to the directory */ + if (chdir(extra) < 0) + { + perror(extra); + } +} + + +/*ARGSUSED*/ +void cmd_map(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ + char *mapto; + char *build, *scan; +#ifndef NO_FKEY + static char *fnames[NFKEYS] = + { + "#10", "#1", "#2", "#3", "#4", + "#5", "#6", "#7", "#8", "#9", +# ifndef NO_SHIFT_FKEY + "#10s", "#1s", "#2s", "#3s", "#4s", + "#5s", "#6s", "#7s", "#8s", "#9s", +# ifndef NO_CTRL_FKEY + "#10c", "#1c", "#2c", "#3c", "#4c", + "#5c", "#6c", "#7c", "#8c", "#9c", +# ifndef NO_ALT_FKEY + "#10a", "#1a", "#2a", "#3a", "#4a", + "#5a", "#6a", "#7a", "#8a", "#9a", +# endif +# endif +# endif + }; + int key; +#endif + + /* "map" with no extra will dump the map table contents */ + if (!*extra) + { +#ifndef NO_ABBR + if (cmd == CMD_ABBR) + { + dumpkey(bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP, TRUE); + } + else +#endif + { + dumpkey(bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, FALSE); + } + } + else + { + /* "extra" is key to map, followed by what it maps to */ + + /* handle quoting inside the "raw" string */ + for (build = mapto = extra; + *mapto && (*mapto != ' ' && *mapto != '\t'); + *build++ = *mapto++) + { + if (*mapto == ctrl('V') && mapto[1]) + { + mapto++; + } + } + + /* skip whitespace, and mark the end of the "raw" string */ + while ((*mapto == ' ' || *mapto == '\t')) + { + *mapto++ = '\0'; + } + *build = '\0'; + + /* strip ^Vs from the "cooked" string */ + for (scan = build = mapto; *scan; *build++ = *scan++) + { + if (*scan == ctrl('V') && scan[1]) + { + scan++; + } + } + *build = '\0'; + +#ifndef NO_FKEY + /* if the mapped string is '#' and a number, then assume + * the user wanted that function key + */ + if (extra[0] == '#' && isdigit(extra[1])) + { + key = atoi(extra + 1) % 10; +# ifndef NO_SHIFT_FKEY + build = extra + strlen(extra) - 1; + if (*build == 's') + key += 10; +# ifndef NO_CTRL_FKEY + else if (*build == 'c') + key += 20; +# ifndef NO_ALT_FKEY + else if (*build == 'a') + key += 30; +# endif +# endif +# endif + if (FKEY[key]) + mapkey(FKEY[key], mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, fnames[key]); + else + msg("This terminal has no %s key", fnames[key]); + } + else +#endif +#ifndef NO_ABBR + if (cmd == CMD_ABBR || cmd == CMD_UNABBR) + { + mapkey(extra, mapto, bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP, "abbr"); + } + else +#endif + { + mapkey(extra, mapto, bang ? WHEN_VIINP|WHEN_VIREP : WHEN_VICMD, (char *)0); + } + } +} + + +/*ARGSUSED*/ +void cmd_set(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ + if (!*extra) + { + dumpopts(FALSE);/* "FALSE" means "don't dump all" - only set */ + } + else if (!strcmp(extra, "all")) + { + dumpopts(TRUE); /* "TRUE" means "dump all" - even unset vars */ + } + else + { + setopts(extra); + + /* That option may have affected the appearence of text */ + changes++; + } +} + +/*ARGSUSED*/ +void cmd_tag(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ + int fd; /* file descriptor used to read the file */ + char *scan; /* used to scan through the tmpblk.c */ +#ifdef INTERNAL_TAGS + char *cmp; /* char of tag name we're comparing, or NULL */ + char *end; /* marks the end of chars in tmpblk.c */ +#else + int i; +#endif +#ifndef NO_MAGIC + char wasmagic; /* preserves the original state of o_magic */ +#endif + static char prevtag[30]; + + /* if no tag is given, use the previous tag */ + if (!extra || !*extra) + { + if (!*prevtag) + { + msg("No previous tag"); + return; + } + extra = prevtag; + } + else + { + strncpy(prevtag, extra, sizeof prevtag); + prevtag[sizeof prevtag - 1] = '\0'; + } + +#ifndef INTERNAL_TAGS + /* use "ref" to look up the tag info for this tag */ + sprintf(tmpblk.c, "ref -t %s%s %s", (*origname ? "-f" : ""),origname, prevtag); + fd = rpipe(tmpblk.c, 0); + if (fd < 0) + { + msg("Can't run \"%s\"", tmpblk.c); + return; + } + + /* try to read the tag info */ + for (scan = tmpblk.c; + (i = tread(fd, scan, scan - tmpblk.c + BLKSIZE)) > 0; + scan += i) + { + } + *scan = '\0'; + + /* close the pipe. abort if error */ + if (rpclose(fd) != 0 || scan < tmpblk.c + 3) + { + msg("tag \"%s\" not found", extra); + return; + } + +#else /* use internal code to look up the tag */ + /* open the tags file */ + fd = open(TAGS, O_RDONLY); + if (fd < 0) + { + msg("No tags file"); + return; + } + + /* Hmmm... this would have been a lot easier with <stdio.h> */ + + /* find the line with our tag in it */ + for(scan = end = tmpblk.c, cmp = extra; ; scan++) + { + /* read a block, if necessary */ + if (scan >= end) + { + end = tmpblk.c + tread(fd, tmpblk.c, BLKSIZE); + scan = tmpblk.c; + if (scan >= end) + { + msg("tag \"%s\" not found", extra); + close(fd); + return; + } + } + + /* if we're comparing, compare... */ + if (cmp) + { + /* matched??? wow! */ + if (!*cmp && *scan == '\t') + { + break; + } + if (*cmp++ != *scan) + { + /* failed! skip to newline */ + cmp = (char *)0; + } + } + + /* if we're skipping to newline, do it fast! */ + if (!cmp) + { + while (scan < end && *scan != '\n') + { + scan++; + } + if (scan < end) + { + cmp = extra; + } + } + } + + /* found it! get the rest of the line into memory */ + for (cmp = tmpblk.c, scan++; scan < end && *scan != '\n'; ) + { + *cmp++ = *scan++; + } + if (scan == end) + { + tread(fd, cmp, BLKSIZE - (int)(cmp - tmpblk.c)); + } + else + *cmp = *scan; + + /* we can close the tags file now */ + close(fd); +#endif /* INTERNAL_TAGS */ + + /* extract the filename from the line, and edit the file */ + for (scan = tmpblk.c; *scan != '\t'; scan++) + { + } + *scan++ = '\0'; + if (strcmp(origname, tmpblk.c) != 0) + { + if (!tmpabort(bang)) + { + msg("Use :tag! to abort changes, or :w to save changes"); + return; + } + tmpstart(tmpblk.c); + } + + /* move to the desired line (or to line 1 if that fails) */ +#ifndef NO_MAGIC + wasmagic = *o_magic; + *o_magic = FALSE; +#endif + cursor = MARK_FIRST; + linespec(scan, &cursor); + if (cursor == MARK_UNSET) + { + cursor = MARK_FIRST; + msg("Tag's address is out of date"); + } +#ifndef NO_MAGIC + *o_magic = wasmagic; +#endif +} + + + + + +/* describe this version of the program */ +/*ARGSUSED*/ +void cmd_version(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + msg("%s", VERSION); +#ifdef CREDIT + msg("%s", CREDIT); +#endif +#ifdef CREDIT2 + msg("%s", CREDIT2); +#endif +#ifdef COMPILED_BY + msg("Compiled by %s", COMPILED_BY); +#endif +#ifdef COPYING + msg("%s", COPYING); +#endif +} + + +#ifndef NO_MKEXRC +/* make a .exrc file which describes the current configuration */ +/*ARGSUSED*/ +void cmd_mkexrc(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + int fd; + + /* the default name for the .exrc file EXRC */ + if (!*extra) + { + extra = EXRC; + } + + /* create the .exrc file */ + fd = creat(extra, FILEPERMS); + if (fd < 0) + { + msg("Couldn't create a new \"%s\" file", extra); + return; + } + + /* save stuff */ + saveopts(fd); + savemaps(fd, FALSE); +#ifndef NO_ABBR + savemaps(fd, TRUE); +#endif +#ifndef NO_DIGRAPH + savedigs(fd); +#endif +#ifndef NO_COLOR + savecolor(fd); +#endif + + /* close the file */ + close(fd); + msg("Configuration saved"); +} +#endif + +#ifndef NO_DIGRAPH +/*ARGSUSED*/ +void cmd_digraph(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + do_digraph(bang, extra); +} +#endif + + +#ifndef NO_ERRLIST +static char errfile[256]; /* the name of a file containing an error */ +static long errline; /* the line number for an error */ +static int errfd = -2; /* fd of the errlist file */ + +/* This static function tries to parse an error message. + * + * For most compilers, the first word is taken to be the name of the erroneous + * file, and the first number after that is taken to be the line number where + * the error was detected. The description of the error follows, possibly + * preceded by an "error ... :" or "warning ... :" label which is skipped. + * + * For Coherent, error messages look like "line#: filename: message". + * + * For non-error lines, or unparsable error lines, this function returns NULL. + * Normally, though, it alters errfile and errline, and returns a pointer to + * the description. + */ +static char *parse_errmsg(text) + REG char *text; +{ + REG char *cpy; + long atol(); +# if COHERENT || TOS /* any Mark Williams compiler */ + /* Get the line number. If no line number, then ignore this line. */ + errline = atol(text); + if (errline == 0L) + return (char *)0; + + /* Skip to the start of the filename */ + while (*text && *text++ != ':') + { + } + if (!*text++) + return (char *)0; + + /* copy the filename to errfile */ + for (cpy = errfile; *text && (*cpy++ = *text++) != ':'; ) + { + } + if (!*text++) + return (char *)0; + cpy[-1] = '\0'; + + return text; +# else /* not a Mark Williams compiler */ + char *errmsg; + + /* the error message is the whole line, by default */ + errmsg = text; + + /* skip leading garbage */ + while (*text && !isalnum(*text)) + { + text++; + } + + /* copy over the filename */ + cpy = errfile; + while(isalnum(*text) || *text == '.') + { + *cpy++ = *text++; + } + *cpy = '\0'; + + /* ignore the name "Error" and filenames that contain a '/' */ + if (*text == '/' || !*errfile || !strcmp(errfile + 1, "rror") || access(errfile, 0) < 0) + { + return (char *)0; + } + + /* skip garbage between filename and line number */ + while (*text && !isdigit(*text)) + { + text++; + } + + /* if the number is part of a larger word, then ignore this line */ + if (*text && isalpha(text[-1])) + { + return (char *)0; + } + + /* get the error line */ + errline = 0L; + while (isdigit(*text)) + { + errline *= 10; + errline += (*text - '0'); + text++; + } + + /* any line which lacks a filename or line number should be ignored */ + if (!errfile[0] || !errline) + { + return (char *)0; + } + + /* locate the beginning of the error description */ + while (*text && !isspace(*text)) + { + text++; + } + while (*text) + { +# ifndef CRUNCH + /* skip "error #:" and "warning #:" clauses */ + if (!strncmp(text + 1, "rror ", 5) + || !strncmp(text + 1, "arning ", 7) + || !strncmp(text + 1, "atal error", 10)) + { + do + { + text++; + } while (*text && *text != ':'); + continue; + } +# endif + + /* anything other than whitespace or a colon is important */ + if (!isspace(*text) && *text != ':') + { + errmsg = text; + break; + } + + /* else keep looking... */ + text++; + } + + return errmsg; +# endif /* not COHERENT */ +} + +/*ARGSUSED*/ +void cmd_errlist(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ + static long endline;/* original number of lines in this file */ + static long offset; /* offset of the next line in the errlist file */ + int i; + char *errmsg; + + /* if a new errlist file is named, open it */ + if (extra && extra[0]) + { + /* close the old one */ + if (errfd >= 0) + { + close(errfd); + } + + /* open the new one */ + errfd = open(extra, O_RDONLY); + offset = 0L; + endline = nlines; + } + else if (errfd < 0) + { + /* open the default file */ + errfd = open(ERRLIST, O_RDONLY); + offset = 0L; + endline = nlines; + } + + /* do we have an errlist file now? */ + if (errfd < 0) + { + msg("There is no errlist file"); + beep(); + return; + } + + /* find the next error message in the file */ + do + { + /* read the next line from the errlist */ + lseek(errfd, offset, 0); + if (tread(errfd, tmpblk.c, (unsigned)BLKSIZE) <= 0) + { + msg("No more errors"); + beep(); + close(errfd); + errfd = -2; + return; + } + for (i = 0; tmpblk.c[i] != '\n'; i++) + { + } + tmpblk.c[i++] = 0; + + /* look for an error message in the line */ + errmsg = parse_errmsg(tmpblk.c); + if (!errmsg) + { + offset += i; + } + + } while (!errmsg); + + /* switch to the file containing the error, if this isn't it */ + if (strcmp(origname, errfile)) + { + if (!tmpabort(bang)) + { + msg("Use :er! to abort changes, or :w to save changes"); + beep(); + return; + } + tmpstart(errfile); + endline = nlines; + } + else if (endline == 0L) + { + endline = nlines; + } + + /* go to the line where the error was detected */ + cursor = MARK_AT_LINE(errline + (nlines - endline)); + if (cursor > MARK_LAST) + { + cursor = MARK_LAST; + } + if (mode == MODE_VI) + { + redraw(cursor, FALSE); + } + + /* display the error message */ +#ifdef CRUNCH + msg("%.70s", errmsg); +#else + if (nlines > endline) + { + msg("line %ld(+%ld): %.60s", errline, nlines - endline, errmsg); + } + else if (nlines < endline) + { + msg("line %ld(-%ld): %.60s", errline, endline - nlines, errmsg); + } + else + { + msg("line %ld: %.65s", errline, errmsg); + } +#endif + + /* remember where the NEXT error line will start */ + offset += i; +} + + +/*ARGSUSED*/ +void cmd_make(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ + BLK buf; + + /* if the file hasn't been saved, then complain unless ! */ + if (tstflag(file, MODIFIED) && !bang) + { + msg("\"%s\" not saved yet", origname); + return; + } + + /* build the command */ + sprintf(buf.c, "%s %s %s%s", (cmd == CMD_CC ? o_cc : o_make), extra, REDIRECT, ERRLIST); + qaddstr(buf.c); + addch('\n'); + + /* close the old errlist file, if any */ + if (errfd >= 0) + { + close(errfd); + errfd = -3; + } + + /* run the command, with curses temporarily disabled */ + suspend_curses(); + system(buf.c); + resume_curses(mode == MODE_EX); + if (mode == MODE_COLON) + mode = MODE_VI; + + /* run the "errlist" command */ + cmd_errlist(MARK_UNSET, MARK_UNSET, cmd, bang, ERRLIST); +} +#endif + + + +#ifndef NO_COLOR + +/* figure out the number of text colors we use with this configuration */ +# ifndef NO_POPUP +# ifndef NO_VISIBLE +# define NCOLORS 7 +# else +# define NCOLORS 6 +# endif +# else +# ifndef NO_VISIBLE +# define NCOLORS 6 +# else +# define NCOLORS 5 +# endif +# endif + +/* the attribute bytes used in each of "when"s */ +static char bytes[NCOLORS]; + +static struct +{ + char *word; /* a legal word */ + int type; /* what type of word this is */ + int val; /* some other value */ +} + words[] = +{ + {"normal", 1, A_NORMAL}, /* all "when" names must come */ + {"standout", 1, A_STANDOUT}, /* at the top of the list. */ + {"bold", 1, A_BOLD}, /* The first 3 must be normal,*/ + {"underlined", 1, A_UNDERLINE}, /* standout, and bold; the */ + {"italics", 1, A_ALTCHARSET}, /* remaining names follow. */ +#ifndef NO_POPUP + {"popup", 1, A_POPUP}, +#endif +#ifndef NO_VISIBLE + {"visible", 1, A_VISIBLE}, +#endif + + {"black", 3, 0x00}, /* The color names start right*/ + {"blue", 3, 0x01}, /* after the "when" names. */ + {"green", 3, 0x02}, + {"cyan", 3, 0x03}, + {"red", 3, 0x04}, + {"magenta", 3, 0x05}, + {"brown", 3, 0x06}, + {"white", 3, 0x07}, + {"yellow", 3, 0x0E}, /* bright brown */ + {"gray", 3, 0x08}, /* bright black? of course! */ + {"grey", 3, 0x08}, + + {"bright", 2, 0x08}, + {"light", 2, 0x08}, + {"blinking", 2, 0x80}, + {"on", 0, 0}, + {"n", 1, A_NORMAL}, + {"s", 1, A_STANDOUT}, + {"b", 1, A_BOLD}, + {"u", 1, A_UNDERLINE}, + {"i", 1, A_ALTCHARSET}, +#ifndef NO_POPUP + {"p", 1, A_POPUP}, + {"menu", 1, A_POPUP}, +#endif +#ifndef NO_VISIBLE + {"v", 1, A_VISIBLE}, +#endif + {(char *)0, 0, 0} +}; + +/*ARGSUSED*/ +void cmd_color(frommark, tomark, cmd, bang, extra) + MARK frommark, tomark; + CMD cmd; + int bang; + char *extra; +{ + int attrbyte; + int cmode; + int nowbg; /* BOOLEAN: is the next color background? */ + + REG char *scan; + REG i; + + +#ifndef CRUNCH + /* if no args are given, then report the current colors */ + if (!*extra) + { + /* if no colors are set, then say so */ + if (!bytes[0]) + { + msg("no colors have been set"); + return; + } + + /* report all five color combinations */ + for (i = 0; i < NCOLORS; i++) + { + qaddstr("color "); + qaddstr(words[i].word); + qaddch(' '); + if (bytes[i] & 0x80) + qaddstr("blinking "); + switch (bytes[i] & 0xf) + { + case 0x08: qaddstr("gray"); break; + case 0x0e: qaddstr("yellow"); break; + case 0x0f: qaddstr("bright white");break; + default: + if (bytes[i] & 0x08) + qaddstr("light "); + qaddstr(words[(bytes[i] & 0x07) + NCOLORS].word); + } + qaddstr(" on "); + qaddstr(words[((bytes[i] >> 4) & 0x07) + NCOLORS].word); + addch('\n'); + exrefresh(); + } + return; + } +#endif + + /* The default background color is the same as "normal" chars. + * There is no default foreground color. + */ + cmode = A_NORMAL; + attrbyte = bytes[0] & 0x70; + nowbg = FALSE; + + /* parse each word in the "extra" text */ + for (scan = extra; *extra; extra = scan) + { + /* locate the end of the word */ + while (*scan && *scan != ' ') + { + scan++; + } + + /* skip whitespace at the end of the word */ + while(*scan == ' ') + { + *scan++ = '\0'; + } + + /* lookup the word */ + for (i = 0; words[i].word && strcmp(words[i].word, extra); i++) + { + } + + /* if not a word, then complain */ + if (!words[i].word) + { + msg("Invalid color name: %s", extra); + return; + } + + /* process the word */ + switch (words[i].type) + { + case 1: + cmode = words[i].val; + break; + + case 2: + attrbyte |= words[i].val; + break; + + case 3: + if (nowbg) + attrbyte = ((attrbyte & ~0x70) | ((words[i].val & 0x07) << 4)); + else + attrbyte |= words[i].val; + nowbg = TRUE; + break; + } + } + + /* if nowbg isn't set now, then we were never given a foreground color */ + if (!nowbg) + { + msg("usage: color [when] [\"bright\"] [\"blinking\"] foreground [background]"); + return; + } + + /* the first ":color" command MUST define the "normal" colors */ + if (!bytes[0]) + cmode = A_NORMAL; + + /* we should now have a cmode and an attribute byte... */ + + /* set the color */ + setcolor(cmode, attrbyte); + + /* remember what we just did */ + bytes[cmode] = attrbyte; + + /* if the other colors haven't been set yet, then set them to defaults */ + if (!bytes[1]) + { + /* standout is the opposite of normal */ + bytes[1] = ((attrbyte << 4) & 0x70 | (attrbyte >> 4) & 0x07); + setcolor(A_STANDOUT, bytes[1]); + + /* if "normal" isn't bright, then bold defaults to normal+bright + * else bold defaults to bright white. + */ + bytes[2] = attrbyte | ((attrbyte & 0x08) ? 0x0f : 0x08); + setcolor(A_BOLD, bytes[2]); + + /* all others default to the "standout" colors, without blinking */ + for (i = 3; i < NCOLORS; i++) + { + bytes[i] = (bytes[1] & 0x7f); + setcolor(words[i].val, bytes[i]); + } + } + + /* force a redraw, so we see the new colors */ + redraw(MARK_UNSET, FALSE); +} + + + +void savecolor(fd) + int fd; /* file descriptor to write colors to */ +{ + int i; + char buf[80]; + + /* if no colors are set, then return */ + if (!bytes[0]) + { + return; + } + + /* save all five color combinations */ + for (i = 0; i < NCOLORS; i++) + { + strcpy(buf, "color "); + strcat(buf, words[i].word); + strcat(buf, " "); + if (bytes[i] & 0x80) + strcat(buf, "blinking "); + switch (bytes[i] & 0xf) + { + case 0x08: strcat(buf, "gray"); break; + case 0x0e: strcat(buf, "yellow"); break; + case 0x0f: strcat(buf, "bright white");break; + default: + if (bytes[i] & 0x08) + strcat(buf, "light "); + strcat(buf, words[(bytes[i] & 0x07) + NCOLORS].word); + } + strcat(buf, " on "); + strcat(buf, words[((bytes[i] >> 4) & 0x07) + NCOLORS].word); + strcat(buf, "\n"); + twrite(fd, buf, (unsigned)strlen(buf)); + } +} +#endif + +#ifdef SIGTSTP +/* temporarily suspend elvis */ +/*ARGSUSED*/ +void cmd_suspend(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + void (*func)(); /* stores the previous setting of SIGTSTP */ + +#if ANY_UNIX + /* the Bourne shell can't handle ^Z */ + if (!strcmp(o_shell, "/bin/sh")) + { + msg("The /bin/sh shell doesn't support ^Z"); + return; + } +#endif + + move(LINES - 1, 0); + if (tstflag(file, MODIFIED)) + { + addstr("Warning: \""); + addstr(origname); + addstr("\" modified but not yet saved"); + clrtoeol(); + } + refresh(); + suspend_curses(); + func = signal(SIGTSTP, SIG_DFL); + kill (0, SIGTSTP); + + /* the process stops and resumes here */ + + signal(SIGTSTP, func); + resume_curses(TRUE); + if (mode == MODE_VI || mode == MODE_COLON) + redraw(MARK_UNSET, FALSE); + else + refresh (); +} +#endif diff --git a/commands/elvis/cmd2.c b/commands/elvis/cmd2.c new file mode 100755 index 000000000..5d88acc1f --- /dev/null +++ b/commands/elvis/cmd2.c @@ -0,0 +1,942 @@ +/* cmd2.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains some of the commands - mostly ones that change text */ + +#include "config.h" +#include "ctype.h" +#include "vi.h" +#include "regexp.h" +#if TOS +# include <stat.h> +#else +# if OSK +# include "osk.h" +# else +# if AMIGA +# include "amistat.h" +# else +# include <sys/stat.h> +# endif +# endif +#endif + + +/*ARGSUSED*/ +void cmd_substitute(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; /* rest of the command line */ +{ + char *line; /* a line from the file */ + regexp *re; /* the compiled search expression */ + char *subst; /* the substitution string */ + char *opt; /* substitution options */ + long l; /* a line number */ + char *s, *d; /* used during subtitutions */ + char *conf; /* used during confirmation */ + long chline; /* # of lines changed */ + long chsub; /* # of substitutions made */ + static optp; /* boolean option: print when done? */ + static optg; /* boolean option: substitute globally in line? */ + static optc; /* boolean option: confirm before subst? */ +#ifndef CRUNCH + long oldnlines; +#endif + + + /* for now, assume this will fail */ + rptlines = -1L; + + if (cmd == CMD_SUBAGAIN) + { +#ifndef NO_MAGIC + if (*o_magic) + subst = "~"; + else +#endif + subst = "\\~"; + re = regcomp(""); + + /* if visual "&", then turn off the "p" and "c" options */ + if (bang) + { + optp = optc = FALSE; + } + } + else /* CMD_SUBSTITUTE */ + { + /* make sure we got a search pattern */ + if (*extra != '/' && *extra != '?') + { + msg("Usage: s/regular expression/new text/"); + return; + } + + /* parse & compile the search pattern */ + subst = parseptrn(extra); + re = regcomp(extra + 1); + } + + /* abort if RE error -- error message already given by regcomp() */ + if (!re) + { + return; + } + + if (cmd == CMD_SUBSTITUTE) + { + /* parse the substitution string & find the option string */ + for (opt = subst; *opt && *opt != *extra; opt++) + { + if (*opt == '\\' && opt[1]) + { + opt++; + } + } + if (*opt) + { + *opt++ = '\0'; + } + + /* analyse the option string */ + if (!*o_edcompatible) + { + optp = optg = optc = FALSE; + } + while (*opt) + { + switch (*opt++) + { + case 'p': optp = !optp; break; + case 'g': optg = !optg; break; + case 'c': optc = !optc; break; + case ' ': + case '\t': break; + default: + msg("Subst options are p, c, and g -- not %c", opt[-1]); + return; + } + } + } + + /* if "c" or "p" flag was given, and we're in visual mode, then NEWLINE */ + if ((optc || optp) && mode == MODE_VI) + { + addch('\n'); + exrefresh(); + } + + ChangeText + { + /* reset the change counters */ + chline = chsub = 0L; + + /* for each selected line */ + for (l = markline(frommark); l <= markline(tomark); l++) + { + /* fetch the line */ + line = fetchline(l); + + /* if it contains the search pattern... */ + if (regexec(re, line, TRUE)) + { + /* increment the line change counter */ + chline++; + + /* initialize the pointers */ + s = line; + d = tmpblk.c; + + /* do once or globally ... */ + do + { +#ifndef CRUNCH + /* confirm, if necessary */ + if (optc) + { + for (conf = line; conf < re->startp[0]; conf++) + addch(*conf); + standout(); + for ( ; conf < re->endp[0]; conf++) + addch(*conf); + standend(); + for (; *conf; conf++) + addch(*conf); + addch('\n'); + exrefresh(); + if (getkey(0) != 'y') + { + /* copy accross the original chars */ + while (s < re->endp[0]) + *d++ = *s++; + + /* skip to next match on this line, if any */ + goto Continue; + } + } +#endif /* not CRUNCH */ + + /* increment the substitution change counter */ + chsub++; + + /* copy stuff from before the match */ + while (s < re->startp[0]) + { + *d++ = *s++; + } + + /* substitute for the matched part */ + regsub(re, subst, d); + s = re->endp[0]; + d += strlen(d); + +Continue: + /* if this regexp could conceivably match + * a zero-length string, then require at + * least 1 unmatched character between + * matches. + */ + if (re->minlen == 0) + { + if (!*s) + break; + *d++ = *s++; + } + + } while (optg && regexec(re, s, FALSE)); + + /* copy stuff from after the match */ + while (*d++ = *s++) /* yes, ASSIGNMENT! */ + { + } + +#ifndef CRUNCH + /* NOTE: since the substitution text is allowed to have ^Ms which are + * translated into newlines, it is possible that the number of lines + * in the file will increase after each line has been substituted. + * we need to adjust for this. + */ + oldnlines = nlines; +#endif + + /* replace the old version of the line with the new */ + d[-1] = '\n'; + d[0] = '\0'; + change(MARK_AT_LINE(l), MARK_AT_LINE(l + 1), tmpblk.c); + +#ifndef CRUNCH + l += nlines - oldnlines; + tomark += MARK_AT_LINE(nlines - oldnlines); +#endif + + /* if supposed to print it, do so */ + if (optp) + { + addstr(tmpblk.c); + exrefresh(); + } + + /* move the cursor to that line */ + cursor = MARK_AT_LINE(l); + } + } + } + + /* free the regexp */ + free(re); + + /* if done from within a ":g" command, then finish silently */ + if (doingglobal) + { + rptlines = chline; + rptlabel = "changed"; + return; + } + + /* Reporting */ + if (chsub == 0) + { + msg("Substitution failed"); + } + else if (chline >= *o_report) + { + msg("%ld substitutions on %ld lines", chsub, chline); + } + rptlines = 0L; +} + + + + +/*ARGSUSED*/ +void cmd_delete(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + MARK curs2; /* an altered form of the cursor */ + + /* choose your cut buffer */ + if (*extra == '"') + { + extra++; + } + if (*extra) + { + cutname(*extra); + } + + /* make sure we're talking about whole lines here */ + frommark = frommark & ~(BLKSIZE - 1); + tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE; + + /* yank the lines */ + cut(frommark, tomark); + + /* if CMD_DELETE then delete the lines */ + if (cmd != CMD_YANK) + { + curs2 = cursor; + ChangeText + { + /* delete the lines */ + delete(frommark, tomark); + } + if (curs2 > tomark) + { + cursor = curs2 - tomark + frommark; + } + else if (curs2 > frommark) + { + cursor = frommark; + } + } +} + + +/*ARGSUSED*/ +void cmd_append(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + long l; /* line counter */ + +#ifndef CRUNCH + /* if '!' then toggle auto-indent */ + if (bang) + { + *o_autoindent = !*o_autoindent; + } +#endif + + ChangeText + { + /* if we're doing a change, delete the old version */ + if (cmd == CMD_CHANGE) + { + /* delete 'em */ + cmd_delete(frommark, tomark, cmd, bang, extra); + } + + /* new lines start at the frommark line, or after it */ + l = markline(frommark); + if (cmd == CMD_APPEND) + { + l++; + } + + /* get lines until no more lines, or "." line, and insert them */ + while (vgets('\0', tmpblk.c, BLKSIZE) >= 0) + { + addch('\n'); + if (!strcmp(tmpblk.c, ".")) + { + break; + } + + strcat(tmpblk.c, "\n"); + add(MARK_AT_LINE(l), tmpblk.c); + l++; + } + } + + /* on the odd chance that we're calling this from vi mode ... */ + redraw(MARK_UNSET, FALSE); +} + + +/*ARGSUSED*/ +void cmd_put(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + /* choose your cut buffer */ + if (*extra == '"') + { + extra++; + } + if (*extra) + { + cutname(*extra); + } + + /* paste it */ + ChangeText + { + cursor = paste(frommark, TRUE, FALSE); + } +} + + +/*ARGSUSED*/ +void cmd_join(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + long l; + char *scan; + int len; /* length of the new line */ + + /* if only one line is specified, assume the following one joins too */ + if (markline(frommark) == nlines) + { + msg("Nothing to join with this line"); + return; + } + if (markline(frommark) == markline(tomark)) + { + tomark += BLKSIZE; + } + + /* get the first line */ + l = markline(frommark); + strcpy(tmpblk.c, fetchline(l)); + len = strlen(tmpblk.c); + + /* build the longer line */ + while (++l <= markline(tomark)) + { + /* get the next line */ + scan = fetchline(l); + + /* remove any leading whitespace */ + while (*scan == '\t' || *scan == ' ') + { + scan++; + } + + /* see if the line will fit */ + if (strlen(scan) + len + 3 > BLKSIZE) + { + msg("Can't join -- the resulting line would be too long"); + return; + } + + /* catenate it, with a space (or two) in between */ + if (!bang) + { + if (len >= 1) + { + if (tmpblk.c[len - 1] == '.' + || tmpblk.c[len - 1] == '?' + || tmpblk.c[len - 1] == '!') + { + tmpblk.c[len++] = ' '; + } + tmpblk.c[len++] = ' '; + } + } + strcpy(tmpblk.c + len, scan); + len += strlen(scan); + } + tmpblk.c[len++] = '\n'; + tmpblk.c[len] = '\0'; + + /* make the change */ + ChangeText + { + frommark &= ~(BLKSIZE - 1); + tomark &= ~(BLKSIZE - 1); + tomark += BLKSIZE; + change(frommark, tomark, tmpblk.c); + } + + /* Reporting... */ + rptlines = markline(tomark) - markline(frommark) - 1L; + rptlabel = "joined"; +} + + + +/*ARGSUSED*/ +void cmd_shift(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + long l; /* line number counter */ + int oldidx; /* number of chars previously used for indent */ + int newidx; /* number of chars in the new indent string */ + int oldcol; /* previous indent amount */ + int newcol; /* new indent amount */ + char *text; /* pointer to the old line's text */ + + ChangeText + { + /* for each line to shift... */ + for (l = markline(frommark); l <= markline(tomark); l++) + { + /* get the line - ignore empty lines unless ! mode */ + text = fetchline(l); + if (!*text && !bang) + continue; + + /* calc oldidx and oldcol */ + for (oldidx = 0, oldcol = 0; + text[oldidx] == ' ' || text[oldidx] == '\t'; + oldidx++) + { + if (text[oldidx] == ' ') + { + oldcol += 1; + } + else + { + oldcol += *o_tabstop - (oldcol % *o_tabstop); + } + } + + /* calc newcol */ + if (cmd == CMD_SHIFTR) + { + newcol = oldcol + (*o_shiftwidth & 0xff); + } + else + { + newcol = oldcol - (*o_shiftwidth & 0xff); + if (newcol < 0) + newcol = 0; + } + + /* if no change, then skip to next line */ + if (oldcol == newcol) + continue; + + /* build a new indent string */ + newidx = 0; + if (*o_autotab) + { + while (newcol >= *o_tabstop) + { + tmpblk.c[newidx++] = '\t'; + newcol -= *o_tabstop; + } + } + while (newcol > 0) + { + tmpblk.c[newidx++] = ' '; + newcol--; + } + tmpblk.c[newidx] = '\0'; + + /* change the old indent string into the new */ + change(MARK_AT_LINE(l), MARK_AT_LINE(l) + oldidx, tmpblk.c); + } + } + + /* Reporting... */ + rptlines = markline(tomark) - markline(frommark) + 1L; + if (cmd == CMD_SHIFTR) + { + rptlabel = ">ed"; + } + else + { + rptlabel = "<ed"; + } +} + + +/*ARGSUSED*/ +void cmd_read(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + int fd, rc; /* used while reading from the file */ + char *scan; /* used for finding NUL characters */ + int hadnul; /* boolean: any NULs found? */ + int addnl; /* boolean: forced to add newlines? */ + int len; /* number of chars in current line */ + long lines; /* number of lines in current block */ + struct stat statb; + + /* special case: if ":r !cmd" then let the filter() function do it */ + if (extra[0] == '!') + { + filter(frommark, MARK_UNSET, extra + 1, TRUE); + return; + } + + /* open the file */ + fd = open(extra, O_RDONLY); + if (fd < 0) + { + msg("Can't open \"%s\"", extra); + return; + } + +#ifndef CRUNCH + if (stat(extra, &statb) < 0) + { + msg("Can't stat \"%s\"", extra); + } +# if TOS + if (statb.st_mode & S_IJDIR) +# else +# if OSK + if (statb.st_mode & S_IFDIR) +# else + if ((statb.st_mode & S_IFMT) != S_IFREG) +# endif +# endif + { + msg("\"%s\" is not a regular file", extra); + return; + } +#endif /* not CRUNCH */ + + /* get blocks from the file, and add them */ + ChangeText + { + /* insertion starts at the line following frommark */ + tomark = frommark = (frommark | (BLKSIZE - 1L)) + 1L; + len = 0; + hadnul = addnl = FALSE; + + /* add an extra newline, so partial lines at the end of + * the file don't trip us up + */ + add(tomark, "\n"); + + /* for each chunk of text... */ + while ((rc = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0) + { + /* count newlines, convert NULs, etc. ... */ + for (lines = 0, scan = tmpblk.c; rc > 0; rc--, scan++) + { + /* break up long lines */ + if (*scan != '\n' && len + 2 > BLKSIZE) + { + *scan = '\n'; + addnl = TRUE; + } + + /* protect against NUL chars in file */ + if (!*scan) + { + *scan = 0x80; + hadnul = TRUE; + } + + /* starting a new line? */ + if (*scan == '\n') + { + /* reset length at newline */ + len = 0; + lines++; + } + else + { + len++; + } + } + + /* add the text */ + *scan = '\0'; + add(tomark, tmpblk.c); + tomark += MARK_AT_LINE(lines) + len - markidx(tomark); + } + + /* if partial last line, then retain that first newline */ + if (len > 0) + { + msg("Last line had no newline"); + tomark += BLKSIZE; /* <- for the rptlines calc */ + } + else /* delete that first newline */ + { + delete(tomark, (tomark | (BLKSIZE - 1L)) + 1L); + } + } + + /* close the file */ + close(fd); + + /* Reporting... */ + rptlines = markline(tomark) - markline(frommark); + rptlabel = "read"; + if (mode == MODE_EX) + { + cursor = (tomark & ~BLKSIZE) - BLKSIZE; + } + else + { + cursor = frommark; + } + + if (addnl) + msg("Newlines were added to break up long lines"); + if (hadnul) + msg("NULs were converted to 0x80"); +} + + + +/*ARGSUSED*/ +void cmd_undo(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + undo(); +} + + +/* print the selected lines */ +/*ARGSUSED*/ +void cmd_print(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + REG char *scan; + REG long l; + REG int col; + + for (l = markline(frommark); l <= markline(tomark); l++) + { + /* display a line number, if CMD_NUMBER */ + if (cmd == CMD_NUMBER) + { + sprintf(tmpblk.c, "%6ld ", l); + qaddstr(tmpblk.c); + col = 8; + } + else + { + col = 0; + } + + /* get the next line & display it */ + for (scan = fetchline(l); *scan; scan++) + { + /* expand tabs to the proper width */ + if (*scan == '\t' && cmd != CMD_LIST) + { + do + { + qaddch(' '); + col++; + } while (col % *o_tabstop != 0); + } + else if (*scan > 0 && *scan < ' ' || *scan == '\177') + { + qaddch('^'); + qaddch(*scan ^ 0x40); + col += 2; + } + else if ((*scan & 0x80) && cmd == CMD_LIST) + { + sprintf(tmpblk.c, "\\%03o", UCHAR(*scan)); + qaddstr(tmpblk.c); + col += 4; + } + else + { + qaddch(*scan); + col++; + } + + /* wrap at the edge of the screen */ + if (!has_AM && col >= COLS) + { + addch('\n'); + col -= COLS; + } + } + if (cmd == CMD_LIST) + { + qaddch('$'); + } + addch('\n'); + exrefresh(); + } +} + + +/* move or copy selected lines */ +/*ARGSUSED*/ +void cmd_move(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + MARK destmark; + + /* parse the destination linespec. No defaults. Line 0 is okay */ + destmark = cursor; + if (!strcmp(extra, "0")) + { + destmark = 0L; + } + else if (linespec(extra, &destmark) == extra || !destmark) + { + msg("invalid destination address"); + return; + } + + /* flesh the marks out to encompass whole lines */ + frommark &= ~(BLKSIZE - 1); + tomark = (tomark & ~(BLKSIZE - 1)) + BLKSIZE; + destmark = (destmark & ~(BLKSIZE - 1)) + BLKSIZE; + + /* make sure the destination is valid */ + if (cmd == CMD_MOVE && destmark >= frommark && destmark < tomark) + { + msg("invalid destination address"); + } + + /* Do it */ + ChangeText + { + /* save the text to a cut buffer */ + cutname('\0'); + cut(frommark, tomark); + + /* if we're not copying, delete the old text & adjust destmark */ + if (cmd != CMD_COPY) + { + delete(frommark, tomark); + if (destmark >= frommark) + { + destmark -= (tomark - frommark); + } + } + + /* add the new text */ + paste(destmark, FALSE, FALSE); + } + + /* move the cursor to the last line of the moved text */ + cursor = destmark + (tomark - frommark) - BLKSIZE; + if (cursor < MARK_FIRST || cursor >= MARK_LAST + BLKSIZE) + { + cursor = MARK_LAST; + } + + /* Reporting... */ + rptlabel = ( (cmd == CMD_COPY) ? "copied" : "moved" ); +} + + + +/* execute EX commands from a file */ +/*ARGSUSED*/ +void cmd_source(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + /* must have a filename */ + if (!*extra) + { + msg("\"source\" requires a filename"); + return; + } + + doexrc(extra); +} + + +#ifndef NO_AT +/*ARGSUSED*/ +void cmd_at(frommark, tomark, cmd, bang, extra) + MARK frommark; + MARK tomark; + CMD cmd; + int bang; + char *extra; +{ + static nest = FALSE; + int result; + char buf[MAXRCLEN]; + + /* don't allow nested macros */ + if (nest) + { + msg("@ macros can't be nested"); + return; + } + nest = TRUE; + + /* require a buffer name */ + if (*extra == '"') + extra++; + if (!*extra || !isascii(*extra) ||!islower(*extra)) + { + msg("@ requires a cut buffer name (a-z)"); + } + + /* get the contents of the buffer */ + result = cb2str(*extra, buf, (unsigned)(sizeof buf)); + if (result <= 0) + { + msg("buffer \"%c is empty", *extra); + } + else if (result >= sizeof buf) + { + msg("buffer \"%c is too large to execute", *extra); + } + else + { + /* execute the contents of the buffer as ex commands */ + exstring(buf, result, '\\'); + } + + nest = FALSE; +} +#endif diff --git a/commands/elvis/config.h b/commands/elvis/config.h new file mode 100755 index 000000000..a4481397d --- /dev/null +++ b/commands/elvis/config.h @@ -0,0 +1,527 @@ +/* + * vi configuration file + * We try to automatically configure to various compilers and operating + * systems. Extend the autoconf section as needed. + */ + +#ifndef _CONFIG_H +# define _CONFIG_H + +/*************************** autoconf section ************************/ + +/* Commodore-Amiga */ +#ifdef amiga +# define AMIGA 1 +# define COMPILED_BY "Manx Aztec C 5.2b" +#endif + +/* standard unix V (?) */ +#ifdef M_SYSV +# define UNIXV 1 +#endif + +/* xelos system, University of Ulm */ +#ifdef xelos +# define UNIXV 1 +#endif + +/* BSD UNIX? */ +#ifdef bsd +# define BSD 1 +#else +# ifdef sun +# define BSD 1 +# endif +#endif + +/* Microsoft C: sorry, Watcom does the same thing */ +#ifdef M_I86 +# ifndef M_SYSV +# define MSDOS 1 +# ifdef IBMC2 +# define COMPILED_BY "IBM C/2 1.00" +# else +# define MICROSOFT 1 +# define COMPILED_BY "Microsoft C 5.10" +# endif +# endif +#endif + +/* Borland's Turbo C */ +#ifdef __TURBOC__ +# define MSDOS 1 +# define TURBOC 1 +# ifdef __BORLANDC__ +# define COMPILED_BY "Borland C 2.00" +# else +# define COMPILED_BY (__TURBOC__ >= 661 ? "Turbo C++ 1.00" : "Turbo C 2.00") +# endif +#endif + +/* Tos Mark-Williams */ +#ifdef M68000 +# define TOS 1 +# define COMPILED_BY "Mark Williams C" +#endif + +/* Tos GNU-C */ +#if defined(__atarist__) && defined(__gem__) +# define TOS 1 +# define COMPILED_BY "GNU-C " __VERSION__ +#endif + +/* OS9/68000 */ +#ifdef OSK +# define COMPILED_BY "Microware C V2.3 Edition 40" +#endif + +/* DEC Rainbow, running MS-DOS (handled by earlier MS-DOS tests) */ +/* (would need -DRAINBOW in CFLAGS to compile a Rainbow-compatible .EXE) */ + +#ifdef VMS +# define COMPILED_BY "VAX/VMS VAXC compiler" +# undef VMS +# define VMS 1 +#endif + +/*************************** end of autoconf section ************************/ + +/* All undefined symbols are defined to zero here, to allow for older */ +/* compilers which dont understand #if defined() or #if UNDEFINED_SYMBOL */ + +/*************************** operating systems *****************************/ + +#ifndef BSD +# define BSD 0 /* UNIX - Berkeley 4.x */ +#endif + +#ifndef UNIXV +# define UNIXV 0 /* UNIX - AT&T SYSV */ +#endif + +#ifndef UNIX7 +# define UNIX7 0 /* UNIX - version 7 */ +#endif + +#ifndef MSDOS +# define MSDOS 0 /* PC */ +#endif + +#ifndef TOS +# define TOS 0 /* Atari ST */ +#endif + +#ifndef AMIGA +# define AMIGA 0 /* Commodore Amiga */ +#endif + +#ifndef OSK +# define OSK 0 /* OS-9 / 68k */ +#endif + +#ifndef COHERENT +# define COHERENT 0 /* Coherent */ +#endif + +#ifndef RAINBOW /* DEC Rainbow support, under MS-DOS */ +# define RAINBOW 0 +#endif + +#ifndef VMS +# define VMS 0 /* VAX/VMS */ +#endif + /* Minix has no predefines */ +#if !BSD && !UNIXV && !UNIX7 && !MSDOS && !TOS && !AMIGA && !OSK && !COHERENT && !VMS +# define MINIX 1 +#else +# define MINIX 0 +#endif + + /* generic combination of Unices */ +#if UNIXV || UNIX7 || BSD || MINIX || COHERENT +# define ANY_UNIX 1 +#else +# define ANY_UNIX 0 +#endif + +/*************************** compilers **************************************/ + +#ifndef AZTEC_C +# define AZTEC_C 0 +#endif + +#ifndef MICROSOFT +# define MICROSOFT 0 +#endif + +#ifndef TURBOC +# define TURBOC 0 +#endif + +/******************************* Credit ************************************/ + +#if MSDOS +# define CREDIT "Ported to MS-DOS by Guntram Blohm & Martin Patzel" +# if RAINBOW +# define CREDIT2 "Rainbow support added by Willett Kempton" +# endif +#endif + +#if AMIGA +# define CREDIT "Ported to AmigaDOS 2.04 by Mike Rieser & Dale Rahn" +#endif + +#if TOS +# define CREDIT "Ported to Atari/TOS by Guntram Blohm & Martin Patzel" +#endif + +#if OSK +# define CREDIT "Ported to Microware OS9/68k by Peter Reinig" +#endif + +#if COHERENT +# define CREDIT "Ported to Coherent by Esa Ahola" +#endif + +#if VMS +# define CREDIT "Ported to VAX/VMS by John Campbell" +#endif +/*************************** functions depending on OS *********************/ + +/* There are two terminal-related functions that we need: ttyread() and + * ttywrite(). The ttyread() function implements read-with-timeout and is + * a true function on all systems. The ttywrite() function is almost always + * just a macro... + */ +#if !TOS && !AMIGA +# define ttywrite(buf, len) write(1, buf, (unsigned)(len)) /* raw write */ +#endif + +/* The strchr() function is an official standard now, so everybody has it + * except Unix version 7 (which is old) and BSD Unix (which is academic). + * Those guys use something called index() to do the same thing. + */ +#if BSD || UNIX7 || OSK +# define strchr index +#endif +extern char *strchr(); + +/* BSD uses bcopy() instead of memcpy() */ +#if BSD +# define memcpy(dest, src, siz) bcopy(src, dest, siz) +#endif + +/* BSD uses getwd() instead of getcwd(). The arguments are a little different, + * but we'll ignore that and hope for the best; adding arguments to the macro + * would mess up an "extern" declaration of the function. + */ +#if BSD || COHERENT +# define getcwd getwd +#endif +extern char *getcwd(); + +/* text versa binary mode for read/write */ +#if !TOS +#define tread(fd,buf,n) read(fd,buf,(unsigned)(n)) +#define twrite(fd,buf,n) write(fd,buf,(unsigned)(n)) +#endif + +/**************************** Compiler quirks *********************************/ + +/* the UNIX version 7 and (some) TOS compilers, don't allow "void" */ +#if UNIX7 || TOS +# define void int +#endif + +/* as far as I know, all compilers except version 7 support unsigned char */ +/* NEWFLASH: the Minix-ST compiler has subtle problems with unsigned char */ +#if UNIX7 || MINIX +# define UCHAR(c) ((c) & 0xff) +# define uchar char +#else +# define UCHAR(c) ((unsigned char)(c)) +# define uchar unsigned char +#endif + +/* Some compilers prefer to have malloc declared as returning a (void *) */ +#if BSD || AMIGA +extern void *malloc(); +#else +extern char *malloc(); +#endif + +/* everybody but Amiga wants lseek declared here */ +#if !AMIGA +extern long lseek(); +#endif + +/******************* Names of files and environment vars **********************/ + +#if ANY_UNIX +# ifndef TMPDIR +# if MINIX +# define TMPDIR "/usr/tmp" /* Keep elvis' temp files off RAM disk! */ +# else +# define TMPDIR "/tmp" /* directory where temp files live */ +# endif +# endif +# ifndef PRSVDIR +# define PRSVDIR "/usr/preserve" /* directory where preserved file live */ +# endif +# ifndef PRSVINDEX +# define PRSVINDEX "/usr/preserve/Index" /* index of files in PRSVDIR */ +# endif +# ifndef EXRC +# define EXRC ".exrc" /* init file in current directory */ +# endif +# define SCRATCHOUT "%s/soXXXXXX" /* temp file used as input to filter */ +# ifndef SHELL +# define SHELL "/bin/sh" /* default shell */ +# endif +# if COHERENT +# ifndef REDIRECT +# define REDIRECT ">" /* Coherent CC writes errors to stdout */ +# endif +# endif +#endif + +#if AMIGA /* Specify AMIGA environment */ +# ifndef CC_COMMAND +# define CC_COMMAND "cc" /* generic C compiler */ +# endif +# ifndef COLON +# define COLON ':' /* Amiga files can also end in `:' */ +# endif +# ifndef SYSEXRC +# define SYSEXRC "S:" EXRC /* name of ".exrc" file in system dir */ +# endif +# ifndef MAXRCLEN +# define MAXRCLEN 2048 /* max size of a .exrc file */ +# endif +# ifndef NBUFS +# define NBUFS 10 /* must be at least 3 -- more is better */ +# endif +# ifndef NEEDSYNC +# define NEEDSYNC TRUE /* assume ":se sync" by default */ +# endif +# ifndef PRSVDIR +# define PRSVDIR "Elvis:" /* directory where preserved file live */ +# endif +# ifndef PRSVINDEX +# define PRSVINDEX "Elvis:Index" /* index of files in PRSVDIR */ +# endif +# ifndef REDIRECT +# define REDIRECT ">" /* Amiga writes errors to stdout */ +# endif +# ifndef SCRATCHIN +# define SCRATCHIN "%sSIXXXXXX" +# endif +# ifndef SCRATCHOUT +# define SCRATCHOUT "%sSOXXXXXX" +# endif +# ifndef SHELL +# define SHELL "newshell" /* default shell */ +# endif +# ifndef TERMTYPE +# define TERMTYPE "amiga" /* default termtype */ +# endif +# ifndef TMPDIR /* for AMIGA should end in `:' or `/' */ +# define TMPDIR "T:" /* directory where temp files live */ +# endif +# ifndef TMPNAME +# define TMPNAME "%selv_%x.%x" /* format of names for temp files */ +# endif +#endif + +#if MSDOS || TOS +/* do not change TMPNAME and SCRATCH*: they MUST begin with '%s\\'! */ +# ifndef TMPDIR +# define TMPDIR "C:\\tmp" /* directory where temp files live */ +# endif +# ifndef PRSVDIR +# define PRSVDIR "C:\\preserve" /* directory where preserved file live */ +# endif +# ifndef PRSVINDEX +# define PRSVINDEX "C:\\preserve\\Index" /* index of files in PRSVDIR */ +# endif +# define TMPNAME "%s\\elv_%x.%x" /* temp file */ +# if MSDOS +# if MICROSOFT +# define CC_COMMAND "cl -c" /* C compiler */ +# else +# if __BORLANDC__ /* Borland C */ +# define CC_COMMAND "bcc" /* C compiler */ +# else +# if TURBOC /* Turbo C */ +# define CC_COMMAND "tcc" /* C compiler */ +# endif /* TURBOC */ +# endif /* BORLANDC */ +# endif /* MICROSOFT */ +# endif /* MSDOS */ +# define SCRATCHIN "%s\\siXXXXXX" /* DOS ONLY - output of filter program */ +# define SCRATCHOUT "%s\\soXXXXXX" /* temp file used as input to filter */ +# define SLASH '\\' +# ifndef SHELL +# if TOS +# define SHELL "shell.ttp" /* default shell */ +# else +# define SHELL "command.com" /* default shell */ +# endif +# endif +# define NEEDSYNC TRUE /* assume ":se sync" by default */ +# if TOS && __GNUC__ /* probably on other systems, too */ +# define REDIRECT "2>" /* GNUC reports on 2, others on 1 */ +# define CC_COMMAND "gcc -c" +# else +# define REDIRECT ">" /* shell's redirection of stderr */ +# endif +#endif + +#if VMS +/* do not change TMPNAME, and SCRATCH*: they MUST begin with '%s\\'! */ +# ifndef TMPDIR +# define TMPDIR "sys$scratch:" /* directory where temp files live */ +# endif +# define TMPNAME "%selv_%x.%x;1" /* temp file */ +# define SCRATCHIN "%ssiXXXXXX" /* DOS ONLY - output of filter program */ +# define SCRATCHOUT "%ssoXXXXXX" /* temp file used as input to filter */ +# define SLASH '\:' /* Worry point... jdc */ +# ifndef SHELL +# define SHELL "" /* default shell */ +# endif +# define REDIRECT ">" /* shell's redirection of stderr */ +# define tread(fd,buf,n) vms_read(fd,buf,(unsigned)(n)) +# define close vms_close +# define lseek vms_lseek +# define unlink vms_delete +# define delete __delete /* local routine conflicts w/VMS rtl routine. */ +# define rpipe vms_rpipe +# define rpclose vms_rpclose +# define ttyread vms_ttyread +/* There is no sync() on vms */ +# define sync() +/* jdc -- seems VMS external symbols are case insensitive */ +# define m_fWord m_fw_ord +# define m_bWord m_bw_ord +# define m_eWord m_ew_ord +# define m_Nsrch m_n_srch +# define m_Fch m_f_ch +# define m_Tch m_t_ch +# define v_Xchar v_x_char +/* jdc -- also, braindead vms curses always found by linker. */ +# define LINES elvis_LINES +# define COLS elvis_COLS +# define curscr elvis_curscr +# define stdscr elvis_stdscr +# define initscr elvis_initscr +# define endwin elvis_endwin +# define wrefresh elvis_wrefresh +#endif + +#if OSK +# ifndef TMPDIR +# define TMPDIR "/dd/tmp" /* directory where temp files live */ +# endif +# ifndef PRSVDIR +# define PRSVDIR "/dd/usr/preserve" /* directory where preserved file live */ +# endif +# ifndef PRSVINDEX +# define PRSVINDEX "/dd/usr/preserve/Index" /* index of files in PRSVDIR */ +# endif +# ifndef CC_COMMAND +# define CC_COMMAND "cc -r" /* name of the compiler */ +# endif +# ifndef EXRC +# define EXRC ".exrc" /* init file in current directory */ +# endif +# define SCRATCHOUT "%s/soXXXXXX" /* temp file used as input to filter */ +# ifndef SHELL +# define SHELL "shell" /* default shell */ +# endif +# define FILEPERMS (S_IREAD|S_IWRITE) /* file permissions used for creat() */ +# define REDIRECT ">>-" /* shell's redirection of stderr */ +# define sync() /* OS9 doesn't need a sync() */ +#endif + +#ifndef TAGS +# define TAGS "tags" /* name of the tags file */ +#endif + +#ifndef TMPNAME +# define TMPNAME "%s/elv_%x.%x" /* format of names for temp files */ +#endif + +#ifndef EXINIT +# define EXINIT "EXINIT" /* name of EXINIT environment variable */ +#endif + +#ifndef EXRC +# define EXRC "elvis.rc" /* name of ".exrc" file in current dir */ +#endif + +#ifndef HMEXRC +# define HMEXRC EXRC /* name of ".exrc" file in home dir */ +#endif + +#ifndef KEYWORDPRG +# define KEYWORDPRG "ref" +#endif + +#ifndef SCRATCHOUT +# define SCRATCHIN "%s/SIXXXXXX" +# define SCRATCHOUT "%s/SOXXXXXX" +#endif + +#ifndef ERRLIST +# define ERRLIST "errlist" +#endif + +#ifndef SLASH +# define SLASH '/' +#endif + +#ifndef SHELL +# define SHELL "shell" +#endif + +#ifndef REG +# define REG register +#endif + +#ifndef NEEDSYNC +# define NEEDSYNC FALSE +#endif + +#ifndef FILEPERMS +# define FILEPERMS 0666 +#endif + +#ifndef PRESERVE +# define PRESERVE "elvprsv" /* name of the "preserve" program */ +#endif + +#ifndef CC_COMMAND +# define CC_COMMAND "cc -c" +#endif + +#ifndef MAKE_COMMAND +# define MAKE_COMMAND "make" +#endif + +#ifndef REDIRECT +# define REDIRECT "2>" +#endif + +#ifndef BLKSIZE +# ifdef CRUNCH +# define BLKSIZE 1024 +# else +# define BLKSIZE 2048 +# endif +#endif + +#ifndef KEYBUFSIZE +# define KEYBUFSIZE 1000 +#endif + +#endif /* ndef _CONFIG_H */ diff --git a/commands/elvis/ctags.c b/commands/elvis/ctags.c new file mode 100755 index 000000000..b7424e5b7 --- /dev/null +++ b/commands/elvis/ctags.c @@ -0,0 +1,819 @@ +/* ctags.c */ + +/* This is a reimplementation of the ctags(1) program. It supports ANSI C, + * and has heaps o' flags. It is meant to be distributed with elvis. + */ + +#include <stdio.h> +#include "config.h" +#ifndef FALSE +# define FALSE 0 +# define TRUE 1 +#endif +#ifndef TAGS +# define TAGS "tags" +#endif +#ifndef REFS +# define REFS "refs" +#endif +#ifndef BLKSIZE +# define BLKSIZE 1024 +#endif + +#include "ctype.c" /* yes, that really is the .c file, not the .h one. */ + +/* -------------------------------------------------------------------------- */ +/* Some global variables */ + +/* The following boolean variables are set according to command line flags */ +int incl_static; /* -s include static tags */ +int incl_types; /* -t include typedefs and structs */ +int incl_vars; /* -v include variables */ +int make_refs; /* -r generate a "refs" file */ +int append_files; /* -a append to "tags" [and "refs"] files */ + +/* The following are used for outputting to the "tags" and "refs" files */ +FILE *tags; /* used for writing to the "tags" file */ +FILE *refs; /* used for writing to the "refs" file */ + +/* -------------------------------------------------------------------------- */ +/* These are used for reading a source file. It keeps track of line numbers */ +char *file_name; /* name of the current file */ +FILE *file_fp; /* stream used for reading the file */ +long file_lnum; /* line number in the current file */ +long file_seek; /* fseek() offset to the start of current line */ +int file_afternl; /* boolean: was previous character a newline? */ +int file_prevch; /* a single character that was ungotten */ +int file_header; /* boolean: is the current file a header file? */ + +/* This function opens a file, and resets the line counter. If it fails, it + * it will display an error message and leave the file_fp set to NULL. + */ +void file_open(name) + char *name; /* name of file to be opened */ +{ + /* if another file was already open, then close it */ + if (file_fp) + { + fclose(file_fp); + } + + /* try to open the file for reading. The file must be opened in + * "binary" mode because otherwise fseek() would misbehave under DOS. + */ +#if MSDOS || TOS + file_fp = fopen(name, "rb"); +#else + file_fp = fopen(name, "r"); +#endif + if (!file_fp) + { + perror(name); + } + + /* reset the name & line number */ + file_name = name; + file_lnum = 0L; + file_seek = 0L; + file_afternl = TRUE; + + /* determine whether this is a header file */ + file_header = FALSE; + name += strlen(name) - 2; + if (name >= file_name && name[0] == '.' && (name[1] == 'h' || name[1] == 'H')) + { + file_header = TRUE; + } +} + +/* This function reads a single character from the stream. If the *previous* + * character was a newline, then it also increments file_lnum and sets + * file_offset. + */ +int file_getc() +{ + int ch; + + /* if there is an ungotten character, then return it. Don't do any + * other processing on it, though, because we already did that the + * first time it was read. + */ + if (file_prevch) + { + ch = file_prevch; + file_prevch = 0; + return ch; + } + + /* if previous character was a newline, then we're starting a line */ + if (file_afternl) + { + file_afternl = FALSE; + file_seek = ftell(file_fp); + file_lnum++; + } + + /* Get a character. If no file is open, then return EOF */ + ch = (file_fp ? getc(file_fp) : EOF); + + /* if it is a newline, then remember that fact */ + if (ch == '\n') + { + file_afternl = TRUE; + } + + /* return the character */ + return ch; +} + +/* This function ungets a character from the current source file */ +void file_ungetc(ch) + int ch; /* character to be ungotten */ +{ + file_prevch = ch; +} + +/* This function copies the current line out some other fp. It has no effect + * on the file_getc() function. During copying, any '\' characters are doubled + * and a leading '^' or trailing '$' is also quoted. The newline character is + * not copied. + * + * This is meant to be used when generating a tag line. + */ +void file_copyline(seek, fp) + long seek; /* where the lines starts in the source file */ + FILE *fp; /* the output stream to copy it to */ +{ + long oldseek;/* where the file's pointer was before we messed it up */ + char ch; /* a single character from the file */ + char next; /* the next character from this file */ + + /* go to the start of the line */ + oldseek = ftell(file_fp); + fseek(file_fp, seek, 0); + + /* if first character is '^', then emit \^ */ + ch = getc(file_fp); + if (ch == '^') + { + putc('\\', fp); + putc('^', fp); + ch = getc(file_fp); + } + + /* write everything up to, but not including, the newline */ + while (ch != '\n') + { + /* preread the next character from this file */ + next = getc(file_fp); + + /* if character is '\', or a terminal '$', then quote it */ + if (ch == '\\' || (ch == '$' && next == '\n')) + { + putc('\\', fp); + } + putc(ch, fp); + + /* next character... */ + ch = next; + } + + /* seek back to the old position */ + fseek(file_fp, oldseek, 0); +} + +/* -------------------------------------------------------------------------- */ +/* This section handles preprocessor directives. It strips out all of the + * directives, and may emit a tag for #define directives. + */ + +int cpp_afternl; /* boolean: look for '#' character? */ +int cpp_prevch; /* an ungotten character, if any */ +int cpp_refsok; /* boolean: can we echo characters out to "refs"? */ + +/* This function opens the file & resets variables */ +void cpp_open(name) + char *name; /* name of source file to be opened */ +{ + /* use the lower-level file_open function to open the file */ + file_open(name); + + /* reset variables */ + cpp_afternl = TRUE; + cpp_refsok = TRUE; +} + +/* This function copies a character from the source file to the "refs" file */ +void cpp_echo(ch) + int ch; /* the character to copy */ +{ + static wasnl; + + /* echo non-EOF chars, unless not making "ref", or echo turned off */ + if (ch != EOF && make_refs && cpp_refsok && !file_header) + { + /* try to avoid blank lines */ + if (ch == '\n') + { + if (wasnl) + { + return; + } + wasnl = TRUE; + } + else + { + wasnl = FALSE; + } + + /* add the character */ + putc(ch, refs); + } +} + +/* This function returns the next character which isn't part of a directive */ +int cpp_getc() +{ + static + int ch; /* the next input character */ + char *scan; + + /* if we have an ungotten character, then return it */ + if (cpp_prevch) + { + ch = cpp_prevch; + cpp_prevch = 0; + return ch; + } + + /* Get a character from the file. Return it if not special '#' */ + ch = file_getc(); + if (ch == '\n') + { + cpp_afternl = TRUE; + cpp_echo(ch); + return ch; + } + else if (ch != '#' || !cpp_afternl) + { + /* normal character. Any non-whitespace should turn off afternl */ + if (ch != ' ' && ch != '\t') + { + cpp_afternl = FALSE; + } + cpp_echo(ch); + return ch; + } + + /* Yikes! We found a directive */ + + /* see whether this is a #define line */ + scan = " define "; + while (*scan) + { + if (*scan == ' ') + { + /* space character matches any whitespace */ + do + { + ch = file_getc(); + } while (ch == ' ' || ch == '\t'); + file_ungetc(ch); + } + else + { + /* other characters should match exactly */ + ch = file_getc(); + if (ch != *scan) + { + file_ungetc(ch); + break; + } + } + scan++; + } + + /* is this a #define line? and should we generate a tag for it? */ + if (!*scan && (file_header || incl_static)) + { + /* if not a header, then this will be a static tag */ + if (!file_header) + { + fputs(file_name, tags); + putc(':', tags); + } + + /* output the tag name */ + for (ch = file_getc(); isalnum(ch) || ch == '_'; ch = file_getc()) + { + putc(ch, tags); + } + + /* output a tab, the filename, another tab, and the line number */ + fprintf(tags, "\t%s\t%ld\n", file_name, file_lnum); + } + + /* skip to the end of the directive -- a newline that isn't preceded + * by a '\' character. + */ + while (ch != EOF && ch != '\n') + { + if (ch == '\\') + { + ch = file_getc(); + } + ch = file_getc(); + } + + /* return the newline that we found at the end of the directive */ + cpp_echo(ch); + return ch; +} + +/* This puts a character back into the input queue for the source file */ +cpp_ungetc(ch) + int ch; /* a character to be ungotten */ +{ + cpp_prevch = ch; +} + + +/* -------------------------------------------------------------------------- */ +/* This is the lexical analyser. It gets characters from the preprocessor, + * and gives tokens to the parser. Some special codes are... + * (deleted) /*...* / (comments) + * (deleted) //...\n (comments) + * (deleted) (* (parens used in complex declaration) + * (deleted) [...] (array subscript, when ... contains no ]) + * (deleted) struct (intro to structure declaration) + * BODY {...} ('{' can occur anywhere, '}' only at BOW if ... has '{') + * ARGS (...{ (args of function, not extern or forward) + * ARGS (...); (args of an extern/forward function declaration) + * COMMA , (separate declarations that have same scope) + * SEMICOLON ; (separate declarations that have different scope) + * SEMICOLON =...; (initializer) + * TYPEDEF typedef (the "typedef" keyword) + * STATIC static (the "static" keyword) + * STATIC private (the "static" keyword) + * STATIC PRIVATE (the "static" keyword) + * NAME [a-z]+ (really any valid name that isn't reserved word) + */ + +/* #define EOF -1 */ +#define DELETED 0 +#define BODY 1 +#define ARGS 2 +#define COMMA 3 +#define SEMICOLON 4 +#define TYPEDEF 5 +#define STATIC 6 +#define EXTERN 7 +#define NAME 8 + +char lex_name[BLKSIZE]; /* the name of a "NAME" token */ +long lex_seek; /* start of line that contains lex_name */ + +lex_gettoken() +{ + int ch; /* a character from the preprocessor */ + int next; /* the next character */ + int token; /* the token that we'll return */ + int i; + + /* loop until we get a token that isn't "DELETED" */ + do + { + /* get the next character */ + ch = cpp_getc(); + + /* process the character */ + switch (ch) + { + case ',': + token = COMMA; + break; + + case ';': + token = SEMICOLON; + break; + + case '/': + /* get the next character */ + ch = cpp_getc(); + switch (ch) + { + case '*': /* start of C comment */ + ch = cpp_getc(); + next = cpp_getc(); + while (next != EOF && (ch != '*' || next != '/')) + { + ch = next; + next = cpp_getc(); + } + break; + + case '/': /* start of a C++ comment */ + do + { + ch = cpp_getc(); + } while (ch != '\n' && ch != EOF); + break; + + default: /* some other slash */ + cpp_ungetc(ch); + } + token = DELETED; + break; + + case '(': + ch = cpp_getc(); + if (ch == '*') + { + token = DELETED; + } + else + { + next = cpp_getc(); + while (ch != '{' && ch != EOF && (ch != ')' || next != ';'))/*}*/ + { + ch = next; + next = cpp_getc(); + } + if (ch == '{')/*}*/ + { + cpp_ungetc(ch); + } + else if (next == ';') + { + cpp_ungetc(next); + } + token = ARGS; + } + break; + + case '{':/*}*/ + /* don't send the next characters to "refs" */ + cpp_refsok = FALSE; + + /* skip ahead to closing '}', or to embedded '{' */ + do + { + ch = cpp_getc(); + } while (ch != '{' && ch != '}' && ch != EOF); + + /* if has embedded '{', then skip to '}' in column 1 */ + if (ch == '{') /*}*/ + { + ch = cpp_getc(); + next = cpp_getc(); + while (ch != EOF && (ch != '\n' || next != '}'))/*{*/ + { + ch = next; + next = cpp_getc(); + } + } + + /* resume "refs" processing */ + cpp_refsok = TRUE; + cpp_echo('}'); + + token = BODY; + break; + + case '[': + /* skip to matching ']' */ + do + { + ch = cpp_getc(); + } while (ch != ']' && ch != EOF); + token = DELETED; + break; + + case '=': + /* skip to next ';' */ + do + { + ch = cpp_getc(); + + /* leave array initializers out of "refs" */ + if (ch == '{') + { + cpp_refsok = FALSE; + } + } while (ch != ';' && ch != EOF); + + /* resume echoing to "refs" */ + if (!cpp_refsok) + { + cpp_refsok = TRUE; + cpp_echo('}'); + cpp_echo(';'); + } + token = SEMICOLON; + break; + + case EOF: + token = EOF; + break; + + default: + /* is this the start of a name/keyword? */ + if (isalpha(ch) || ch == '_') + { + /* collect the whole word */ + lex_name[0] = ch; + for (i = 1, ch = cpp_getc(); + i < BLKSIZE - 1 && (isalnum(ch) || ch == '_'); + i++, ch = cpp_getc()) + { + lex_name[i] = ch; + } + lex_name[i] = '\0'; + cpp_ungetc(ch); + + /* is it a reserved word? */ + if (!strcmp(lex_name, "typedef")) + { + token = TYPEDEF; + lex_seek = -1L; + } + else if (!strcmp(lex_name, "static") + || !strcmp(lex_name, "private") + || !strcmp(lex_name, "PRIVATE")) + { + token = STATIC; + lex_seek = -1L; + } + else if (!strcmp(lex_name, "extern") + || !strcmp(lex_name, "EXTERN") + || !strcmp(lex_name, "FORWARD")) + { + token = EXTERN; + lex_seek = -1L; + } + else + { + token = NAME; + lex_seek = file_seek; + } + } + else /* not part of a name/keyword */ + { + token = DELETED; + } + + } /* end switch(ch) */ + + } while (token == DELETED); + + return token; +} + +/* -------------------------------------------------------------------------- */ +/* This is the parser. It locates tag candidates, and then decides whether to + * generate a tag for them. + */ + +/* This function generates a tag for the object in lex_name, whose tag line is + * located at a given seek offset. + */ +void maketag(scope, seek) + int scope; /* 0 if global, or STATIC if static */ + long seek; /* the seek offset of the line */ +{ + /* output the tagname and filename fields */ + if (scope == EXTERN) + { + /* whoa! we should *never* output a tag for "extern" decl */ + return; + } + else if (scope == STATIC) + { + fprintf(tags, "%s:%s\t%s\t", file_name, lex_name, file_name); + } + else + { + fprintf(tags, "%s\t%s\t", lex_name, file_name); + } + + /* output the target line */ + putc('/', tags); + putc('^', tags); + file_copyline(seek, tags); + putc('$', tags); + putc('/', tags); + putc('\n', tags); +} + + +/* This function parses a source file, adding any tags that it finds */ +void ctags(name) + char *name; /* the name of a source file to be checked */ +{ + int prev; /* the previous token from the source file */ + int token; /* the current token from the source file */ + int scope; /* normally 0, but could be a TYPEDEF or STATIC token */ + int gotname;/* boolean: does lex_name contain a tag candidate? */ + long tagseek;/* start of line that contains lex_name */ + + /* open the file */ + cpp_open(name); + + /* reset */ + scope = 0; + gotname = FALSE; + token = SEMICOLON; + + /* parse until the end of the file */ + while (prev = token, (token = lex_gettoken()) != EOF) + { + /* scope keyword? */ + if (token == TYPEDEF || token == STATIC || token == EXTERN) + { + scope = token; + gotname = FALSE; + continue; + } + + /* name of a possible tag candidate? */ + if (token == NAME) + { + tagseek = file_seek; + gotname = TRUE; + continue; + } + + /* if NAME BODY, without ARGS, then NAME is a struct tag */ + if (gotname && token == BODY && prev != ARGS) + { + gotname = FALSE; + + /* ignore if in typedef -- better name is coming soon */ + if (scope == TYPEDEF) + { + continue; + } + + /* generate a tag, if -t and maybe -s */ + if (incl_types && (file_header || incl_static)) + { + maketag(file_header ? 0 : STATIC, tagseek); + } + } + + /* If NAME ARGS BODY, then NAME is a function */ + if (gotname && prev == ARGS && token == BODY) + { + gotname = FALSE; + + /* generate a tag, maybe checking -s */ + if (scope != STATIC || incl_static) + { + maketag(scope, tagseek); + } + } + + /* If NAME SEMICOLON or NAME COMMA, then NAME is var/typedef */ + if (gotname && (token == SEMICOLON || token == COMMA)) + { + gotname = FALSE; + + /* generate a tag, if -v/-t and maybe -s */ + if (scope == TYPEDEF && incl_types && (file_header || incl_static) + || scope == STATIC && incl_vars && incl_static + || incl_vars) + { + /* a TYPEDEF outside of a header is STATIC */ + if (scope == TYPEDEF && !file_header) + { + maketag(STATIC, tagseek); + } + else /* use whatever scope was declared */ + { + maketag(scope, tagseek); + } + } + } + + /* reset after a semicolon or ARGS BODY pair */ + if (token == SEMICOLON || (prev == ARGS && token == BODY)) + { + scope = 0; + gotname = FALSE; + } + } + + /* The source file will be automatically closed */ +} + +/* -------------------------------------------------------------------------- */ + +void usage() +{ + fprintf(stderr, "usage: ctags [flags] filenames...\n"); + fprintf(stderr, "\t-s include static functions\n"); + fprintf(stderr, "\t-t include typedefs\n"); + fprintf(stderr, "\t-v include variable declarations\n"); + fprintf(stderr, "\t-r generate a \"refs\" file, too\n"); + fprintf(stderr, "\t-a append to \"tags\", instead of overwriting\n"); + exit(2); +} + + + +#if AMIGA +# include "amiwild.c" +#endif + +#if VMS +# include "vmswild.c" +#endif + +main(argc, argv) + int argc; + char **argv; +{ + int i, j; + +#if MSDOS || TOS + char **wildexpand(); + argv = wildexpand(&argc, argv); +#endif + + /* build the tables used by the ctype macros */ + _ct_init(""); + + /* parse the option flags */ + for (i = 1; i < argc && argv[i][0] == '-'; i++) + { + for (j = 1; argv[i][j]; j++) + { + switch (argv[i][j]) + { + case 's': incl_static = TRUE; break; + case 't': incl_types = TRUE; break; + case 'v': incl_vars = TRUE; break; + case 'r': make_refs = TRUE; break; + case 'a': append_files = TRUE; break; + default: usage(); + } + } + } + + /* There should always be at least one source file named in args */ + if (i == argc) + { + usage(); + } + + /* open the "tags" and maybe "refs" files */ + tags = fopen(TAGS, append_files ? "a" : "w"); + if (!tags) + { + perror(TAGS); + exit(3); + } + if (make_refs) + { + refs = fopen(REFS, append_files ? "a" : "w"); + if (!refs) + { + perror(REFS); + exit(4); + } + } + + /* parse each source file */ + for (; i < argc; i++) + { + ctags(argv[i]); + } + + /* close "tags" and maybe "refs" */ + fclose(tags); + if (make_refs) + { + fclose(refs); + } + +#ifdef SORT + /* This is a hack which will sort the tags list. It should + * on UNIX and OS-9. You may have trouble with csh. Note + * that the tags list only has to be sorted if you intend to + * use it with the real vi; elvis permits unsorted tags. + */ +# if OSK + system("qsort tags >-_tags; -nx; del tags; rename _tags tags"); +# else + system("sort tags >_tags$$; mv _tags$$ tags"); +# endif +#endif + + exit(0); + /*NOTREACHED*/ +} + +#if MSDOS || TOS +# define WILDCARD_NO_MAIN +# include "wildcard.c" +#endif diff --git a/commands/elvis/ctype.c b/commands/elvis/ctype.c new file mode 100755 index 000000000..ac6118307 --- /dev/null +++ b/commands/elvis/ctype.c @@ -0,0 +1,74 @@ +/* ctype.c */ + +/* This file contains the tables and initialization function for elvis' + * version of <ctype.h>. It should be portable. + */ + +#include "config.h" +#include "ctype.h" + +uchar _ct_toupper[256]; +uchar _ct_tolower[256]; +uchar _ct_ctypes[256]; + +/* This function initializes the tables used by the ctype macros. It should + * be called at the start of the program. It can be called again anytime you + * wish to change the non-standard "flipcase" list. The "flipcase" list is + * a string of characters which are taken to be lowercase/uppercase pairs. + * If you don't want to use any special flipcase characters, then pass an + * empty string. + */ +void _ct_init(flipcase) + uchar *flipcase; /* list of non-standard lower/upper letter pairs */ +{ + int i; + uchar *scan; + + /* reset all of the tables */ + for (i = 0; i < 256; i++) + { + _ct_toupper[i] = _ct_tolower[i] = i; + _ct_ctypes[i] = 0; + } + + /* add the digits */ + for (scan = (uchar *)"0123456789"; *scan; scan++) + { + _ct_ctypes[*scan] |= _CT_DIGIT | _CT_ALNUM; + } + + /* add the whitespace */ + for (scan = (uchar *)" \t\n\r\f"; *scan; scan++) + { + _ct_ctypes[*scan] |= _CT_SPACE; + } + + /* add the standard ASCII letters */ + for (scan = (uchar *)"aAbBcCdDeEfFgGhHiIjJkKlLmMnNoOpPqQrRsStTuUvVwWxXyYzZ"; *scan; scan += 2) + { + _ct_ctypes[scan[0]] |= _CT_LOWER | _CT_ALNUM; + _ct_ctypes[scan[1]] |= _CT_UPPER | _CT_ALNUM; + _ct_toupper[scan[0]] = scan[1]; + _ct_tolower[scan[1]] = scan[0]; + } + + /* add the flipcase letters */ + for (scan = flipcase; scan[0] && scan[1]; scan += 2) + { + _ct_ctypes[scan[0]] |= _CT_LOWER | _CT_ALNUM; + _ct_ctypes[scan[1]] |= _CT_UPPER | _CT_ALNUM; + _ct_toupper[scan[0]] = scan[1]; + _ct_tolower[scan[1]] = scan[0]; + } + + /* include '_' in the isalnum() list */ + _ct_ctypes[UCHAR('_')] |= _CT_ALNUM; + + /* !!! find the control characters in an ASCII-dependent way */ + for (i = 0; i < ' '; i++) + { + _ct_ctypes[i] |= _CT_CNTRL; + } + _ct_ctypes[127] |= _CT_CNTRL; + _ct_ctypes[255] |= _CT_CNTRL; +} diff --git a/commands/elvis/ctype.h b/commands/elvis/ctype.h new file mode 100755 index 000000000..1a46fd379 --- /dev/null +++ b/commands/elvis/ctype.h @@ -0,0 +1,40 @@ +/* ctype.h */ + +/* This file contains macros definitions and extern declarations for a + * version of <ctype.h> which is aware of the o_flipcase letters used in + * elvis. + * + * This file uses the "uchar" data type and "UCHAR" conversion macro which + * are defined in "config.h". Consequently, any file that includes this + * header must include config.h first. + */ + +#ifndef _CT_UPPER + +#define _CT_UPPER 0x01 +#define _CT_LOWER 0x02 +#define _CT_SPACE 0x04 +#define _CT_DIGIT 0x08 +#define _CT_ALNUM 0x10 +#define _CT_CNTRL 0x20 + +#define isalnum(c) (_ct_ctypes[UCHAR(c)] & _CT_ALNUM) +#define isalpha(c) (_ct_ctypes[UCHAR(c)] & (_CT_LOWER|_CT_UPPER)) +#define isdigit(c) (_ct_ctypes[UCHAR(c)] & _CT_DIGIT) +#define islower(c) (_ct_ctypes[UCHAR(c)] & _CT_LOWER) +#define isspace(c) (_ct_ctypes[UCHAR(c)] & _CT_SPACE) +#define isupper(c) (_ct_ctypes[UCHAR(c)] & _CT_UPPER) +#define iscntrl(c) (_ct_ctypes[UCHAR(c)] & _CT_CNTRL) +#define ispunct(c) (!_ct_ctypes[UCHAR(c)]) /* punct = "none of the above" */ + +#define isascii(c) (!((c) & 0x80)) + +#define toupper(c) _ct_toupper[UCHAR(c)] +#define tolower(c) _ct_tolower[UCHAR(c)] + +extern uchar _ct_toupper[]; +extern uchar _ct_tolower[]; +extern uchar _ct_ctypes[]; +extern void _ct_init(/* char *flipcase */); + +#endif /* ndef _CT_UPPER */ diff --git a/commands/elvis/curses.c b/commands/elvis/curses.c new file mode 100755 index 000000000..cff84ebd4 --- /dev/null +++ b/commands/elvis/curses.c @@ -0,0 +1,930 @@ +/* curses.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains the functions & variables needed for a tiny subset of + * curses. The principle advantage of this version of curses is its + * extreme speed. Disadvantages are potentially larger code, few supported + * functions, limited compatibility with full curses, and only stdscr. + */ + +#include "config.h" +#include "vi.h" + +#if ANY_UNIX +/* The termios/termio/sgtty #ifdefs were a mess, so I removed all but termios. + * (KJB) + */ +# include <termios.h> +# if MINIX +# include <sys/ioctl.h> +# endif +#endif + +#if TOS +# include <osbind.h> +#endif + +#if OSK +# include <sgstat.h> +#endif + +#if VMS +extern int VMS_read_raw; /* Set in initscr() */ +#endif + + +extern char *getenv(); +static void starttcap(); + +/* variables, publicly available & used in the macros */ +char *termtype; /* name of terminal entry */ +short ospeed; /* speed of the tty, eg B2400 */ +#if OSK +char PC_; /* Pad char */ +char *BC; /* backspace character string */ +#else +char PC; /* Pad char */ +#endif +WINDOW *stdscr; /* pointer into kbuf[] */ +WINDOW kbuf[KBSIZ]; /* a very large output buffer */ +int LINES; /* :li#: number of rows */ +int COLS; /* :co#: number of columns */ +int AM; /* :am: boolean: auto margins? */ +int PT; /* :pt: boolean: physical tabs? */ +char *VB; /* :vb=: visible bell */ +char *UP; /* :up=: move cursor up */ +char *SO = ""; /* :so=: standout start */ +char *SE = ""; /* :se=: standout end */ +char *US = ""; /* :us=: underline start */ +char *UE = ""; /* :ue=: underline end */ +char *MD = ""; /* :md=: bold start */ +char *ME = ""; /* :me=: bold end */ +char *AS = ""; /* :as=: alternate (italic) start */ +char *AE = ""; /* :ae=: alternate (italic) end */ +#ifndef NO_VISIBLE +char *MV; /* :mv=: "visible" selection start */ +#endif +char *CM; /* :cm=: cursor movement */ +char *CE; /* :ce=: clear to end of line */ +char *CD; /* :cd=: clear to end of screen */ +char *AL; /* :al=: add a line */ +char *DL; /* :dl=: delete a line */ +#if OSK +char *SR_; /* :sr=: scroll reverse */ +#else +char *SR; /* :sr=: scroll reverse */ +#endif +char *KS = ""; /* :ks=: init string for cursor */ +char *KE = ""; /* :ke=: restore string for cursor */ +char *KU; /* :ku=: key sequence sent by up arrow */ +char *KD; /* :kd=: key sequence sent by down arrow */ +char *KL; /* :kl=: key sequence sent by left arrow */ +char *KR; /* :kr=: key sequence sent by right arrow */ +char *HM; /* :HM=: key sequence sent by the <Home> key */ +char *EN; /* :EN=: key sequence sent by the <End> key */ +char *PU; /* :PU=: key sequence sent by the <PgUp> key */ +char *PD; /* :PD=: key sequence sent by the <PgDn> key */ +char *KI; /* :kI=: key sequence sent by the <Insert> key */ +#ifndef NO_FKEY +char *FKEY[NFKEYS]; /* :k0=: ... :k9=: sequences sent by function keys */ +#endif +char *IM = ""; /* :im=: insert mode start */ +char *IC = ""; /* :ic=: insert the following character */ +char *EI = ""; /* :ei=: insert mode end */ +char *DC; /* :dc=: delete a character */ +char *TI = ""; /* :ti=: terminal init */ /* GB */ +char *TE = ""; /* :te=: terminal exit */ /* GB */ +#ifndef NO_CURSORSHAPE +#if 1 +char *CQ = (char *)0;/* :cQ=: normal cursor */ +char *CX = (char *)1;/* :cX=: cursor used for EX command/entry */ +char *CV = (char *)2;/* :cV=: cursor used for VI command mode */ +char *CI = (char *)3;/* :cI=: cursor used for VI input mode */ +char *CR = (char *)4;/* :cR=: cursor used for VI replace mode */ +#else +char *CQ = ""; /* :cQ=: normal cursor */ +char *CX = ""; /* :cX=: cursor used for EX command/entry */ +char *CV = ""; /* :cV=: cursor used for VI command mode */ +char *CI = ""; /* :cI=: cursor used for VI input mode */ +char *CR = ""; /* :cR=: cursor used for VI replace mode */ +#endif +#endif +char *aend = ""; /* end an attribute -- either UE or ME */ +char ERASEKEY; /* backspace key taken from ioctl structure */ +#ifndef NO_COLOR +char normalcolor[16]; +char SOcolor[16]; +char SEcolor[16]; +char UScolor[16]; +char UEcolor[16]; +char MDcolor[16]; +char MEcolor[16]; +char AScolor[16]; +char AEcolor[16]; +# ifndef NO_POPUP +char POPUPcolor[16]; +# endif +# ifndef NO_VISIBLE +char VISIBLEcolor[16]; +# endif +#endif + +#if ANY_UNIX +static struct termios oldtermio; /* original tty mode */ +static struct termios newtermio; /* cbreak/noecho tty mode */ +#endif + +#if OSK +static struct sgbuf oldsgttyb; /* orginal tty mode */ +static struct sgbuf newsgttyb; /* noecho tty mode */ +#endif + +static char *capbuf; /* capability string buffer */ + + +/* Initialize the Curses package. */ +void initscr() +{ + /* make sure TERM variable is set */ + termtype = getenv("TERM"); + +#if VMS + /* VMS getenv() handles TERM as a environment setting. Foreign + * terminal support can be implemented by setting the ELVIS_TERM + * logical or symbol to match a tinytcap entry. + */ + if (!strcmp(termtype,"unknown")) + termtype = getenv("ELVIS_TERM"); +#endif +#if MSDOS + /* For MS-DOS, if TERM is unset we can default to "pcbios", or + * maybe "rainbow". + */ + if (!termtype) + { +#ifdef RAINBOW + if (*(unsigned char far*)(0xffff000eL) == 6 /* Rainbow 100a */ + || *(unsigned char far*)(0xffff000eL) == 148)/* Rainbow 100b */ + { + termtype = "rainbow"; + } + else +#endif + termtype = "pcbios"; + } + if (!strcmp(termtype, "pcbios")) +#else + if (!termtype) +#endif + { +#if ANY_UNIX + write(2, "Environment variable TERM must be set\n", (unsigned)38); + exit(1); +#endif +#if OSK + writeln(2, "Environment variable TERM must be set\n", (unsigned)38); + exit(1); +#endif +#if AMIGA + termtype = TERMTYPE; + starttcap(termtype); +#endif +#if MSDOS + starttcap("pcbios"); +#endif +#if TOS + termtype = "vt52"; + starttcap(termtype); +#endif +#if VMS + write(2, "UNKNOWN terminal: define ELVIS_TERM\n", (unsigned)36); + exit(1); +#endif + } + else + { +#if MSDOS + *o_pcbios = 0; +#endif + /* start termcap stuff */ + starttcap(termtype); + } + + /* create stdscr and curscr */ + stdscr = kbuf; + + /* change the terminal mode to cbreak/noecho */ +#if ANY_UNIX + tcgetattr(2, &oldtermio); +#endif + +#if OSK + _gs_opt(0, &oldsgttyb); +#endif + +#if VMS + VMS_read_raw = 1; /* cbreak/noecho */ + vms_open_tty(); +#endif + resume_curses(TRUE); +} + +/* Shut down the Curses package. */ +void endwin() +{ + /* change the terminal mode back the way it was */ + suspend_curses(); +#if AMIGA + amiclosewin(); +#endif +} + + +static int curses_active = FALSE; + +/* Send any required termination strings. Turn off "raw" mode. */ +void suspend_curses() +{ +#ifndef NO_CURSORSHAPE + if (has_CQ) + { + do_CQ(); + } +#endif + if (has_TE) /* GB */ + { + do_TE(); + } + if (has_KE) + { + do_KE(); + } +#ifndef NO_COLOR + quitcolor(); +#endif + refresh(); + + /* change the terminal mode back the way it was */ +#if ANY_UNIX + tcsetattr(2, TCSADRAIN, &oldtermio); +#endif +#if OSK + _ss_opt(0, &oldsgttyb); +#endif +#if AMIGA + ttyshutdown(); +#endif +#if MSDOS + raw_set_stdio(FALSE); +#endif + +#if VMS + VMS_read_raw = 0; +#endif + curses_active = FALSE; +} + + +/* put the terminal in RAW mode. If "quietly" is FALSE, then ask the user + * to hit a key, and wait for keystroke before returning. + */ +void resume_curses(quietly) + int quietly; +{ + if (!curses_active) + { + /* change the terminal mode to cbreak/noecho */ +#if ANY_UNIX + ospeed = cfgetospeed(&oldtermio); + ERASEKEY = oldtermio.c_cc[VERASE]; + newtermio = oldtermio; + newtermio.c_iflag &= (IXON|IXOFF|IXANY|ISTRIP|IGNBRK); + newtermio.c_oflag &= ~OPOST; + newtermio.c_lflag &= ISIG; + newtermio.c_cc[VINTR] = ctrl('C'); /* always use ^C for interrupts */ + newtermio.c_cc[VMIN] = 1; + newtermio.c_cc[VTIME] = 0; + newtermio.c_cc[VSUSP] = 0; + tcsetattr(2, TCSADRAIN, &newtermio); +#endif +#if OSK + newsgttyb = oldsgttyb; + newsgttyb.sg_echo = 0; + newsgttyb.sg_eofch = 0; + newsgttyb.sg_kbach = 0; + newsgttyb.sg_kbich = ctrl('C'); + _ss_opt(0, &newsgttyb); + ospeed = oldsgttyb.sg_baud; + ERASEKEY = oldsgttyb.sg_bspch; +#endif +#if AMIGA + /* turn on window resize and RAW */ + ttysetup(); +#endif +#if MSDOS + raw_set_stdio(TRUE); +#endif + +#if VMS + VMS_read_raw = 1; + { int c; + read(0,&c,0); /* Flush the tty buffer. */ + } + ERASEKEY = '\177'; /* Accept <DEL> as <^H> for VMS */ +#endif + + if (has_TI) /* GB */ + { + do_TI(); + } + if (has_KS) + { + do_KS(); + } + + curses_active = TRUE; + } + + /* If we're supposed to quit quietly, then we're done */ + if (quietly) + { + return; + } + + signal(SIGINT, SIG_IGN); + + move(LINES - 1, 0); + do_SO(); +#if VMS + qaddstr("\n[Press <RETURN> to continue]"); +#else + qaddstr("[Press <RETURN> to continue]"); +#endif + do_SE(); + refresh(); + ttyread(kbuf, 20, 0); /* in RAW mode, so <20 is very likely */ + if (kbuf[0] == ':') + { + mode = MODE_COLON; + addch('\n'); + refresh(); + } + else + { + mode = MODE_VI; + redraw(MARK_UNSET, FALSE); + } + exwrote = FALSE; + +#if TURBOC || __GNUC__ || _ANSI + signal(SIGINT, (void(*)()) trapint); +#else + signal(SIGINT, trapint); +#endif +} + +/* This function fetches an optional string from termcap */ +static void mayhave(T, s) + char **T; /* where to store the returned pointer */ + char *s; /* name of the capability */ +{ + char *val; + + val = tgetstr(s, &capbuf); + if (val) + { + *T = val; + } +} + + +/* This function fetches a required string from termcap */ +static void musthave(T, s) + char **T; /* where to store the returned pointer */ + char *s; /* name of the capability */ +{ + mayhave(T, s); + if (!*T) + { + write(2, "This termcap entry lacks the :", (unsigned)30); + write(2, s, (unsigned)2); + write(2, "=: capability\n", (unsigned)14); +#if OSK + write(2, "\l", 1); +#endif + exit(1); + } +} + + +/* This function fetches a pair of strings from termcap. If one of them is + * missing, then the other one is ignored. + */ +static void pair(T, U, sT, sU) + char **T; /* where to store the first pointer */ + char **U; /* where to store the second pointer */ + char *sT; /* name of the first capability */ + char *sU; /* name of the second capability */ +{ + mayhave(T, sT); + mayhave(U, sU); + if (!**T || !**U) + { + *T = *U = ""; + } +} + + + +/* Read everything from termcap */ +static void starttcap(term) + char *term; +{ + static char cbmem[800]; + + /* allocate memory for capbuf */ + capbuf = cbmem; + + /* get the termcap entry */ + switch (tgetent(kbuf, term)) + { + case -1: + write(2, "Can't read /etc/termcap\n", (unsigned)24); +#if OSK + write(2, "\l", 1); +#endif + exit(2); + + case 0: + write(2, "Unrecognized TERM type\n", (unsigned)23); +#if OSK + write(2, "\l", 1); +#endif + exit(3); + } + + /* get strings */ + musthave(&UP, "up"); + mayhave(&VB, "vb"); + musthave(&CM, "cm"); + pair(&SO, &SE, "so", "se"); + mayhave(&TI, "ti"); + mayhave(&TE, "te"); + if (tgetnum("ug") <= 0) + { + pair(&US, &UE, "us", "ue"); + pair(&MD, &ME, "md", "me"); + + /* get italics, or have it default to underline */ + pair(&AS, &AE, "as", "ae"); + if (!*AS) + { + AS = US; + AE = UE; + } + } +#ifndef NO_VISIBLE + MV = SO; /* by default */ + mayhave(&MV, "mv"); +#endif + mayhave(&AL, "al"); + mayhave(&DL, "dl"); + musthave(&CE, "ce"); + mayhave(&CD, "cd"); +#if OSK + mayhave(&SR_, "sr"); +#else + mayhave(&SR, "sr"); +#endif + pair(&IM, &EI, "im", "ei"); + mayhave(&IC, "ic"); + mayhave(&DC, "dc"); + + /* other termcap stuff */ + AM = (tgetflag("am") && !tgetflag("xn")); + PT = tgetflag("pt"); +#if AMIGA + amiopenwin(termtype); /* Must run this before ttysetup(); */ + ttysetup(); /* Must run this before getsize(0); */ +#endif + getsize(0); + + /* Key sequences */ + pair(&KS, &KE, "ks", "ke"); + mayhave(&KU, "ku"); /* up */ + mayhave(&KD, "kd"); /* down */ + mayhave(&KL, "kl"); /* left */ + mayhave(&KR, "kr"); /* right */ + mayhave(&PU, "kP"); /* PgUp */ + mayhave(&PD, "kN"); /* PgDn */ + mayhave(&HM, "kh"); /* Home */ + mayhave(&EN, "kH"); /* End */ + mayhave(&KI, "kI"); /* Insert */ +#ifndef CRUNCH + if (!PU) mayhave(&PU, "K2"); /* "3x3 pad" names for PgUp, etc. */ + if (!PD) mayhave(&PD, "K5"); + if (!HM) mayhave(&HM, "K1"); + if (!EN) mayhave(&EN, "K4"); + + mayhave(&PU, "PU"); /* old XENIX names for PgUp, etc. */ + mayhave(&PD, "PD"); /* (overrides others, if used.) */ + mayhave(&HM, "HM"); + mayhave(&EN, "EN"); +#endif +#ifndef NO_FKEY + mayhave(&FKEY[0], "k0"); /* function key codes */ + mayhave(&FKEY[1], "k1"); + mayhave(&FKEY[2], "k2"); + mayhave(&FKEY[3], "k3"); + mayhave(&FKEY[4], "k4"); + mayhave(&FKEY[5], "k5"); + mayhave(&FKEY[6], "k6"); + mayhave(&FKEY[7], "k7"); + mayhave(&FKEY[8], "k8"); + mayhave(&FKEY[9], "k9"); +# ifndef NO_SHIFT_FKEY + mayhave(&FKEY[10], "s0"); /* shift function key codes */ + mayhave(&FKEY[11], "s1"); + mayhave(&FKEY[12], "s2"); + mayhave(&FKEY[13], "s3"); + mayhave(&FKEY[14], "s4"); + mayhave(&FKEY[15], "s5"); + mayhave(&FKEY[16], "s6"); + mayhave(&FKEY[17], "s7"); + mayhave(&FKEY[18], "s8"); + mayhave(&FKEY[19], "s9"); +# ifndef NO_CTRL_FKEY + mayhave(&FKEY[20], "c0"); /* control function key codes */ + mayhave(&FKEY[21], "c1"); + mayhave(&FKEY[22], "c2"); + mayhave(&FKEY[23], "c3"); + mayhave(&FKEY[24], "c4"); + mayhave(&FKEY[25], "c5"); + mayhave(&FKEY[26], "c6"); + mayhave(&FKEY[27], "c7"); + mayhave(&FKEY[28], "c8"); + mayhave(&FKEY[29], "c9"); +# ifndef NO_ALT_FKEY + mayhave(&FKEY[30], "a0"); /* alt function key codes */ + mayhave(&FKEY[31], "a1"); + mayhave(&FKEY[32], "a2"); + mayhave(&FKEY[33], "a3"); + mayhave(&FKEY[34], "a4"); + mayhave(&FKEY[35], "a5"); + mayhave(&FKEY[36], "a6"); + mayhave(&FKEY[37], "a7"); + mayhave(&FKEY[38], "a8"); + mayhave(&FKEY[39], "a9"); +# endif +# endif +# endif +#endif + +#ifndef NO_CURSORSHAPE + /* cursor shapes */ + CQ = tgetstr("cQ", &capbuf); + if (has_CQ) + { + CX = tgetstr("cX", &capbuf); + if (!CX) CX = CQ; + CV = tgetstr("cV", &capbuf); + if (!CV) CV = CQ; + CI = tgetstr("cI", &capbuf); + if (!CI) CI = CQ; + CR = tgetstr("cR", &capbuf); + if (!CR) CR = CQ; + } +# ifndef CRUNCH + else + { + CQ = CV = ""; + pair(&CQ, &CV, "ve", "vs"); + CX = CI = CR = CQ; + } +# endif /* !CRUNCH */ +#endif /* !NO_CURSORSHAPE */ + +#ifndef NO_COLOR + strcpy(SOcolor, SO); + strcpy(SEcolor, SE); + strcpy(AScolor, AS); + strcpy(AEcolor, AE); + strcpy(MDcolor, MD); + strcpy(MEcolor, ME); + strcpy(UScolor, US); + strcpy(UEcolor, UE); +# ifndef NO_POPUP + strcpy(POPUPcolor, SO); +# endif +# ifndef NO_VISIBLE + strcpy(VISIBLEcolor, MV); +# endif +#endif + +} + + +/* This function gets the window size. It uses the TIOCGWINSZ ioctl call if + * your system has it, or tgetnum("li") and tgetnum("co") if it doesn't. + * This function is called once during initialization, and thereafter it is + * called whenever the SIGWINCH signal is sent to this process. + */ +int getsize(signo) + int signo; +{ + int lines; + int cols; +#ifdef TIOCGWINSZ + struct winsize size; +#endif + +#ifdef SIGWINCH + /* reset the signal vector */ + signal(SIGWINCH, getsize); +#endif + + /* get the window size, one way or another. */ + lines = cols = 0; +#ifdef TIOCGWINSZ + if (ioctl(2, TIOCGWINSZ, &size) >= 0) + { + lines = size.ws_row; + cols = size.ws_col; + } +#endif +#if AMIGA + /* Amiga gets window size by asking the console.device */ + if (!strcmp(TERMTYPE, termtype)) + { + auto long len; + auto char buf[30]; + + Write(Output(), "\2330 q", 4); /* Ask the console.device */ + len = Read(Input(), buf, 29); + buf[len] = '\000'; + sscanf(&buf[5], "%d;%d", &lines, &cols); + } +#endif + if ((lines == 0 || cols == 0) && signo == 0) + { + LINES = tgetnum("li"); + COLS = tgetnum("co"); + } +#if MSDOS +# ifdef RAINBOW + if (!strcmp(termtype, "rainbow")) + { + /* Determine whether Rainbow is in 80-column or 132-column mode */ + cols = *(unsigned char far *)0xee000f57L; + } + else +# endif + { + lines = v_rows(); + cols = v_cols(); + } +#endif + if (lines >= 2) + { + LINES = lines; + } + + if (cols >= 30) + { + COLS = cols; + } + + /* Make sure we got values that we can live with */ + if (LINES < 2 || COLS < 30) + { + write(2, "Screen too small\n", (unsigned)17); +#if OSK + write(2, "\l", 1); +#endif + endwin(); + exit(2); + } + +#if AMIGA + if (*o_lines != LINES || *o_columns != COLS) + { + *o_lines = LINES; + *o_columns = COLS; + } +#endif + + return 0; +} + + +/* This is a function version of addch() -- it is used by tputs() */ +int faddch(ch) + int ch; +{ + addch(ch); + + return 0; +} + +/* This function quickly adds a string to the output queue. It does *NOT* + * convert \n into <CR><LF>. + */ +void qaddstr(str) + char *str; +{ + REG char *s_, *d_; + +#if MSDOS + if (o_pcbios[0]) + { + while (*str) + qaddch(*str++); + return; + } +#endif + for (s_=(str), d_=stdscr; *d_++ = *s_++; ) + { + } + stdscr = d_ - 1; +} + +/* Output the ESC sequence needed to go into any video mode, if supported */ +void attrset(a) + int a; +{ + do_aend(); + if (a == A_BOLD) + { + do_MD(); + aend = ME; + } + else if (a == A_UNDERLINE) + { + do_US(); + aend = UE; + } + else if (a == A_ALTCHARSET) + { + do_AS(); + aend = AE; + } + else + { + aend = ""; + } +} + + +/* Insert a single character into the display */ +void insch(ch) + int ch; +{ + if (has_IM) + do_IM(); + do_IC(); + qaddch(ch); + if (has_EI) + do_EI(); +} + +void wrefresh() +{ + if (stdscr != kbuf) + { + VOIDBIOS(;,ttywrite(kbuf, (unsigned)(stdscr - kbuf))); + stdscr = kbuf; + } +} + +void wqrefresh() +{ + if (stdscr - kbuf > 2000) + { + VOIDBIOS(stdscr = kbuf, + { + ttywrite(kbuf, (unsigned)(stdscr - kbuf)); + stdscr = kbuf; + }); + } +} + +#ifndef NO_COLOR +/* This function is called during termination. It resets color modes */ +int ansiquit() +{ + /* if ANSI color terminal, then reset the colors */ + if (!strcmp(UP, "\033[A")) + { + tputs("\033[37;40m\033[m", 1, faddch); + clrtoeol(); + return 1; + } + return 0; +} + +/* This sets the color strings that work for ANSI terminals. If the TERMCAP + * doesn't look like an ANSI terminal, then it returns FALSE. If the colors + * aren't understood, it also returns FALSE. If all goes well, it returns TRUE + */ +int ansicolor(cmode, attrbyte) + int cmode; /* mode to set, e.g. A_NORMAL */ + int attrbyte; /* IBM PC attribute byte */ +{ + char temp[16]; /* hold the new mode string */ + + /* if not ANSI-ish, then fail */ + if (strcmp(UP, "\033[A") && strcmp(UP, "\033OA")) + { + msg("Don't know how to set colors for this terminal"); + return 0; + } + + /* construct the color string */ + sprintf(temp, "\033[m\033[3%c;4%c%s%sm", + "04261537"[attrbyte & 0x07], + "04261537"[(attrbyte >> 4) & 0x07], + (attrbyte & 0x08) ? ";1" : "", + (attrbyte & 0x80) ? ";5" : ""); + + /* stick it in the right place */ + switch (cmode) + { + case A_NORMAL: + if (!strcmp(MEcolor, normalcolor)) + strcpy(MEcolor, temp); + if (!strcmp(UEcolor, normalcolor)) + strcpy(UEcolor, temp); + if (!strcmp(AEcolor, normalcolor)) + strcpy(AEcolor, temp); + if (!strcmp(SEcolor, normalcolor)) + strcpy(SEcolor, temp); + + strcpy(normalcolor, temp); + tputs(normalcolor, 1, faddch); + break; + + case A_BOLD: + strcpy(MDcolor, temp); + strcpy(MEcolor, normalcolor); + break; + + case A_UNDERLINE: + strcpy(UScolor, temp); + strcpy(UEcolor, normalcolor); + break; + + case A_ALTCHARSET: + strcpy(AScolor, temp); + strcpy(AEcolor, normalcolor); + break; + + case A_STANDOUT: + strcpy(SOcolor, temp); + strcpy(SEcolor, normalcolor); + break; + +#ifndef NO_POPUP + case A_POPUP: + strcpy(POPUPcolor, temp); + break; +#endif + +#ifndef NO_VISIBLE + case A_VISIBLE: + strcpy(VISIBLEcolor, temp); + break; +#endif + } + + return 1; +} + + +/* This function outputs the ESC sequence needed to switch the screen back + * to "normal" mode. On color terminals which haven't had their color set + * yet, this is one of the termcap strings; for color terminals that really + * have had colors defined, we just the "normal color" escape sequence. + */ +endcolor() +{ + if (aend == ME) + tputs(MEcolor, 1, faddch); + else if (aend == UE) + tputs(UEcolor, 1, faddch); + else if (aend == AE) + tputs(AEcolor, 1, faddch); + else if (aend == SE) + tputs(SEcolor, 1, faddch); + aend = ""; + return 0; +} + + +#endif /* !NO_COLOR */ diff --git a/commands/elvis/curses.h b/commands/elvis/curses.h new file mode 100755 index 000000000..84c897cf7 --- /dev/null +++ b/commands/elvis/curses.h @@ -0,0 +1,319 @@ +/* curses.h */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This is the header file for a small, fast, fake curses package */ + +/* termcap stuff */ +extern char *tgoto(); +extern char *tgetstr(); +extern void tputs(); + +#if MSDOS +/* BIOS interface used instead of termcap for MS-DOS */ +extern int vmode; +extern void v_up(); +extern void v_cb(); +extern void v_cs(); +extern void v_ce(); +extern void v_cl(); +extern void v_cd(); +extern void v_al(); +extern void v_dl(); +extern void v_sr(); +extern void v_move(); +#endif + +/* faddch() is a function. a pointer to it is passed to tputs() */ +extern int faddch(); + +/* data types */ +#define WINDOW char + +/* CONSTANTS & SYMBOLS */ +#define TRUE 1 +#define FALSE 0 +#define A_NORMAL 0 +#define A_STANDOUT 1 +#define A_BOLD 2 +#define A_UNDERLINE 3 +#define A_ALTCHARSET 4 +#define A_POPUP 5 +#define A_VISIBLE 6 +#define KBSIZ 4096 + +/* figure out how many function keys we need to allow. */ +#ifndef NO_FKEY +# ifdef NO_SHIFT_FKEY +# define NFKEYS 10 +# else +# ifdef NO_CTRL_FKEY +# define NFKEYS 20 +# else +# ifdef NO_ALT_FKEY +# define NFKEYS 30 +# else +# define NFKEYS 40 +# endif +# endif +# endif +extern char *FKEY[NFKEYS]; /* :k0=:...:k9=: codes sent by function keys */ +#endif + +/* extern variables, defined in curses.c */ +extern char *termtype; /* name of terminal entry */ +extern short ospeed; /* tty speed, eg B2400 */ +#if OSK +extern char PC_; /* Pad char */ +extern char *BC; /* Backspace char string */ +#else +extern char PC; /* Pad char */ +#endif +extern WINDOW *stdscr; /* pointer into kbuf[] */ +extern WINDOW kbuf[KBSIZ]; /* a very large output buffer */ +extern int LINES; /* :li#: number of rows */ +extern int COLS; /* :co#: number of columns */ +extern int AM; /* :am: boolean: auto margins? */ +extern int PT; /* :pt: boolean: physical tabs? */ +extern char *VB; /* :vb=: visible bell */ +extern char *UP; /* :up=: move cursor up */ +extern char *SO; /* :so=: standout start */ +extern char *SE; /* :se=: standout end */ +extern char *US; /* :us=: underline start */ +extern char *UE; /* :ue=: underline end */ +extern char *MD; /* :md=: bold start */ +extern char *ME; /* :me=: bold end */ +extern char *AS; /* :as=: alternate (italic) start */ +extern char *AE; /* :ae=: alternate (italic) end */ +#ifndef NO_VISIBLE +extern char *MV; /* :mv=: "visible" selection start */ +#endif +extern char *CM; /* :cm=: cursor movement */ +extern char *CE; /* :ce=: clear to end of line */ +extern char *CD; /* :cd=: clear to end of screen */ +extern char *AL; /* :al=: add a line */ +extern char *DL; /* :dl=: delete a line */ +#if OSK +extern char *SR_; /* :sr=: scroll reverse */ +#else +extern char *SR; /* :sr=: scroll reverse */ +#endif +extern char *KS; /* :ks=: init string for cursor */ +extern char *KE; /* :ke=: restore string for cursor */ +extern char *KU; /* :ku=: sequence sent by up key */ +extern char *KD; /* :kd=: sequence sent by down key */ +extern char *KL; /* :kl=: sequence sent by left key */ +extern char *KR; /* :kr=: sequence sent by right key */ +extern char *PU; /* :PU=: key sequence sent by PgUp key */ +extern char *PD; /* :PD=: key sequence sent by PgDn key */ +extern char *HM; /* :HM=: key sequence sent by Home key */ +extern char *EN; /* :EN=: key sequence sent by End key */ +extern char *KI; /* :kI=: key sequence sent by Insert key */ +extern char *IM; /* :im=: insert mode start */ +extern char *IC; /* :ic=: insert following char */ +extern char *EI; /* :ei=: insert mode end */ +extern char *DC; /* :dc=: delete a character */ +extern char *TI; /* :ti=: terminal init */ /* GB */ +extern char *TE; /* :te=: terminal exit */ /* GB */ +#ifndef NO_CURSORSHAPE +extern char *CQ; /* :cQ=: normal cursor */ +extern char *CX; /* :cX=: cursor used for EX command/entry */ +extern char *CV; /* :cV=: cursor used for VI command mode */ +extern char *CI; /* :cI=: cursor used for VI input mode */ +extern char *CR; /* :cR=: cursor used for VI replace mode */ +#endif +extern char *aend; /* end an attribute -- either UE or ME */ +extern char ERASEKEY; /* taken from the ioctl structure */ +#ifndef NO_COLOR +extern char SOcolor[]; +extern char SEcolor[]; +extern char UScolor[]; +extern char UEcolor[]; +extern char MDcolor[]; +extern char MEcolor[]; +extern char AScolor[]; +extern char AEcolor[]; +# ifndef NO_POPUP +extern char POPUPcolor[]; +# endif +# ifndef NO_VISIBLE +extern char VISIBLEcolor[]; +# endif +extern char normalcolor[]; +#endif /* undef NO_COLOR */ + +/* Msdos-versions may use bios; others always termcap. + * Will emit some 'code has no effect' warnings in unix. + */ + +#if MSDOS +extern char o_pcbios[1]; /* BAH! */ +#define CHECKBIOS(x,y) (*o_pcbios ? (x) : (y)) +#define VOIDBIOS(x,y) {if (*o_pcbios) {x;} else {y;}} +#else +#define CHECKBIOS(x,y) (y) +#define VOIDBIOS(x,y) {y;} +#endif + +#ifndef NO_COLOR +# define setcolor(m,a) CHECKBIOS(bioscolor(m,a), ansicolor(m,a)) +# define fixcolor() VOIDBIOS(;, tputs(normalcolor, 1, faddch)) +# define quitcolor() CHECKBIOS(biosquit(), ansiquit()) +# define do_SO() VOIDBIOS((vmode=A_STANDOUT), tputs(SOcolor, 1, faddch)) +# define do_SE() VOIDBIOS((vmode=A_NORMAL), tputs(SEcolor, 1, faddch)) +# define do_US() VOIDBIOS((vmode=A_UNDERLINE), tputs(UScolor, 1, faddch)) +# define do_UE() VOIDBIOS((vmode=A_NORMAL), tputs(UEcolor, 1, faddch)) +# define do_MD() VOIDBIOS((vmode=A_BOLD), tputs(MDcolor, 1, faddch)) +# define do_ME() VOIDBIOS((vmode=A_NORMAL), tputs(MEcolor, 1, faddch)) +# define do_AS() VOIDBIOS((vmode=A_ALTCHARSET), tputs(AScolor, 1, faddch)) +# define do_AE() VOIDBIOS((vmode=A_NORMAL), tputs(AEcolor, 1, faddch)) +# define do_POPUP() VOIDBIOS((vmode=A_POPUP), tputs(POPUPcolor, 1, faddch)) +# define do_VISIBLE() VOIDBIOS((vmode=A_VISIBLE), tputs(VISIBLEcolor, 1, faddch)) +#else +# define do_SO() VOIDBIOS((vmode=A_STANDOUT), tputs(SO, 1, faddch)) +# define do_SE() VOIDBIOS((vmode=A_NORMAL), tputs(SE, 1, faddch)) +# define do_US() VOIDBIOS((vmode=A_UNDERLINE), tputs(US, 1, faddch)) +# define do_UE() VOIDBIOS((vmode=A_NORMAL), tputs(UE, 1, faddch)) +# define do_MD() VOIDBIOS((vmode=A_BOLD), tputs(MD, 1, faddch)) +# define do_ME() VOIDBIOS((vmode=A_NORMAL), tputs(ME, 1, faddch)) +# define do_AS() VOIDBIOS((vmode=A_ALTCHARSET), tputs(AS, 1, faddch)) +# define do_AE() VOIDBIOS((vmode=A_NORMAL), tputs(AE, 1, faddch)) +# define do_POPUP() VOIDBIOS((vmode=A_POPUP), tputs(SO, 1, faddch)) +# define do_VISIBLE() VOIDBIOS((vmode=A_VISIBLE), tputs(MV, 1, faddch)) +#endif + +#define do_VB() VOIDBIOS(;, tputs(VB, 1, faddch)) +#define do_UP() VOIDBIOS(v_up(), tputs(UP, 1, faddch)) +#undef do_CM /* move */ +#define do_CE() VOIDBIOS(v_ce(), tputs(CE, 1, faddch)) +#define do_CD() VOIDBIOS(v_cd(), tputs(CD, 1, faddch)) +#define do_AL() VOIDBIOS(v_al(), tputs(AL, LINES, faddch)) +#define do_DL() VOIDBIOS(v_dl(), tputs(DL, LINES, faddch)) +#if OSK +#define do_SR() VOIDBIOS(v_sr(), tputs(SR_, 1, faddch)) +#else +#define do_SR() VOIDBIOS(v_sr(), tputs(SR, 1, faddch)) +#endif +#define do_KS() VOIDBIOS(1, tputs(KS, 1, faddch)) +#define do_KE() VOIDBIOS(1, tputs(KE, 1, faddch)) +#define do_IM() VOIDBIOS(;, tputs(IM, 1, faddch)) +#define do_IC() VOIDBIOS(;, tputs(IC, 1, faddch)) +#define do_EI() VOIDBIOS(;, tputs(EI, 1, faddch)) +#define do_DC() VOIDBIOS(;, tputs(DC, COLS, faddch)) +#define do_TI() VOIDBIOS(;, (void)ttywrite(TI, (unsigned)strlen(TI))) +#define do_TE() VOIDBIOS(;, (void)ttywrite(TE, (unsigned)strlen(TE))) +#ifndef NO_CURSORSHAPE +# define do_CQ() VOIDBIOS(v_cs(), tputs(CQ, 1, faddch)) +# define do_CX() VOIDBIOS(v_cs(), tputs(CX, 1, faddch)) +# define do_CV() VOIDBIOS(v_cs(), tputs(CV, 1, faddch)) +# define do_CI() VOIDBIOS(v_cb(), tputs(CI, 1, faddch)) +# define do_CR() VOIDBIOS(v_cb(), tputs(CR, 1, faddch)) +#endif +#ifndef NO_COLOR +# define do_aend() VOIDBIOS((vmode=A_NORMAL), endcolor()) +#else +# define do_aend() VOIDBIOS((vmode=A_NORMAL), tputs(aend, 1, faddch)) +#endif + +#define has_AM CHECKBIOS(1, AM) +#define has_PT CHECKBIOS(0, PT) +#define has_VB CHECKBIOS((char *)0, VB) +#define has_UP CHECKBIOS((char *)1, UP) +#define has_SO CHECKBIOS((char)1, (*SO)) +#define has_SE CHECKBIOS((char)1, (*SE)) +#define has_US CHECKBIOS((char)1, (*US)) +#define has_UE CHECKBIOS((char)1, (*UE)) +#define has_MD CHECKBIOS((char)1, (*MD)) +#define has_ME CHECKBIOS((char)1, (*ME)) +#define has_AS CHECKBIOS((char)1, (*AS)) +#define has_AE CHECKBIOS((char)1, (*AE)) +#undef has_CM /* cursor move: don't need */ +#define has_CB CHECKBIOS(1, 0) +#define has_CS CHECKBIOS(1, 0) +#define has_CE CHECKBIOS((char *)1, CE) +#define has_CD CHECKBIOS((char *)1, CD) +#define has_AL CHECKBIOS((char *)1, AL) +#define has_DL CHECKBIOS((char *)1, DL) +#if OSK +#define has_SR CHECKBIOS((char *)1, SR_) +#else +#define has_SR CHECKBIOS((char *)1, SR) +#endif +#define has_KS CHECKBIOS((char)1, (*KS)) +#define has_KE CHECKBIOS((char)1, (*KE)) +#define has_KU KU +#define has_KD KD +#define has_KL KL +#define has_KR KR +#define has_HM HM +#define has_EN EN +#define has_PU PU +#define has_PD PD +#define has_KI KI +#define has_IM CHECKBIOS((char)0, (*IM)) +#define has_IC CHECKBIOS((char)0, (*IC)) +#define has_EI CHECKBIOS((char)0, (*EI)) +#define has_DC CHECKBIOS((char *)0, DC) +#define has_TI CHECKBIOS((char)0, (*TI)) +#define has_TE CHECKBIOS((char)0, (*TE)) +#ifndef NO_CURSORSHAPE +#define has_CQ CHECKBIOS((char *)1, CQ) +#endif + +/* (pseudo)-Curses-functions */ + +#ifdef lint +# define _addCR VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\r') : (stdscr[-1] = '\n'))) +#else +# if OSK +# define _addCR VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\l') : (stdscr[-1] = stdscr[-1]))) +# else +# define _addCR VOIDBIOS(;, (stdscr[-1] == '\n' ? qaddch('\r') : 0)) +# endif +#endif + +#ifdef AZTEC_C +# define qaddch(ch) CHECKBIOS(v_put(ch), (*stdscr = (ch), *stdscr++)) +#else +#define qaddch(ch) CHECKBIOS(v_put(ch), (*stdscr++ = (ch))) +#endif + +#if OSK +#define addch(ch) if (qaddch(ch) == '\n') qaddch('\l'); else +#else +#define addch(ch) if (qaddch(ch) == '\n') qaddch('\r'); else +#endif + +extern void initscr(); +extern void endwin(); +extern void suspend_curses(); +extern void resume_curses(); +extern void attrset(); +extern void insch(); +extern void qaddstr(); +extern void wrefresh(); +extern void wqrefresh(); +#define addstr(str) {qaddstr(str); _addCR;} +#define move(y,x) VOIDBIOS(v_move(x,y), tputs(tgoto(CM, x, y), 1, faddch)) +#define mvaddch(y,x,ch) {move(y,x); addch(ch);} +#define refresh() VOIDBIOS(;, wrefresh()) +#define standout() do_SO() +#define standend() do_SE() +#define clrtoeol() do_CE() +#define clrtobot() do_CD() +#define insertln() do_AL() +#define deleteln() do_DL() +#define delch() do_DC() +#define scrollok(w,b) +#define raw() +#define echo() +#define cbreak() +#define noraw() +#define noecho() +#define nocbreak() diff --git a/commands/elvis/cut.c b/commands/elvis/cut.c new file mode 100755 index 000000000..0e0bb393f --- /dev/null +++ b/commands/elvis/cut.c @@ -0,0 +1,700 @@ +/* cut.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains function which manipulate the cut buffers. */ + +#include "config.h" +#include "vi.h" +#if TURBOC +#include <process.h> /* needed for getpid */ +#endif +#if TOS +#include <osbind.h> +#define rename(a,b) Frename(0,a,b) +#endif + +# define NANONS 9 /* number of anonymous buffers */ + +static struct cutbuf +{ + short *phys; /* pointer to an array of #s of BLKs containing text */ + int nblks; /* number of blocks in phys[] array */ + int start; /* offset into first block of start of cut */ + int end; /* offset into last block of end of cut */ + int tmpnum; /* ID number of the temp file */ + char lnmode; /* boolean: line-mode cut? (as opposed to char-mode) */ +} + named[27], /* cut buffers "a through "z and ". */ + anon[NANONS]; /* anonymous cut buffers */ + +static char cbname; /* name chosen for next cut/paste operation */ +static char dotcb; /* cut buffer to use if "doingdot" is set */ + + +#ifndef NO_RECYCLE +/* This function builds a list of all blocks needed in the current tmp file + * for the contents of cut buffers. + * !!! WARNING: if you have more than ~450000 bytes of text in all of the + * cut buffers, then this will fail disastrously, because buffer overflow + * is *not* allowed for. + */ +int cutneeds(need) + BLK *need; /* this is where we deposit the list */ +{ + struct cutbuf *cb; /* used to count through cut buffers */ + int i; /* used to count through blocks of a cut buffer */ + int n; /* total number of blocks in list */ + + n = 0; + + /* first the named buffers... */ + for (cb = named; cb < &named[27]; cb++) + { + if (cb->tmpnum != tmpnum) + continue; + + for (i = cb->nblks; i-- > 0; ) + { + need->n[n++] = cb->phys[i]; + } + } + + /* then the anonymous buffers */ + for (cb = anon; cb < &anon[NANONS]; cb++) + { + if (cb->tmpnum != tmpnum) + continue; + + for (i = cb->nblks; i-- > 0; ) + { + need->n[n++] = cb->phys[i]; + } + } + + /* return the length of the list */ + return n; +} +#endif + +static void maybezap(num) + int num; /* the tmpnum of the temporary file to [maybe] delete */ +{ + char cutfname[80]; + int i; + + /* if this is the current tmp file, then we'd better keep it! */ + if (tmpfd >= 0 && num == tmpnum) + { + return; + } + + /* see if anybody else needs this tmp file */ + for (i = 27; --i >= 0; ) + { + if (named[i].nblks > 0 && named[i].tmpnum == num) + { + break; + } + } + if (i < 0) + { + for (i = NANONS; --i >= 0 ; ) + { + if (anon[i].nblks > 0 && anon[i].tmpnum == num) + { + break; + } + } + } + + /* if nobody else needs it, then discard the tmp file */ + if (i < 0) + { +#if MSDOS || TOS + strcpy(cutfname, o_directory); + if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i - 1])) + cutfname[i++] = SLASH; + sprintf(cutfname + i, TMPNAME + 3, getpid(), num); +#else + sprintf(cutfname, TMPNAME, o_directory, getpid(), num); +#endif + unlink(cutfname); + } +} + +/* This function frees a cut buffer. If it was the last cut buffer that + * refered to an old temp file, then it will delete the temp file. */ +static void cutfree(buf) + struct cutbuf *buf; +{ + int num; + + /* return immediately if the buffer is already empty */ + if (buf->nblks <= 0) + { + return; + } + + /* else free up stuff */ + num = buf->tmpnum; + buf->nblks = 0; +#ifdef DEBUG + if (!buf->phys) + msg("cutfree() tried to free a NULL buf->phys pointer."); + else +#endif + free((char *)buf->phys); + + /* maybe delete the temp file */ + maybezap(num); +} + +/* This function is called when we are about to abort a tmp file. + * + * To minimize the number of extra files lying around, only named cut buffers + * are preserved in a file switch; the anonymous buffers just go away. + */ +void cutswitch() +{ + int i; + + /* mark the current temp file as being "obsolete", and close it. */ + storename((char *)0); + close(tmpfd); + tmpfd = -1; + + /* discard all anonymous cut buffers */ + for (i = 0; i < NANONS; i++) + { + cutfree(&anon[i]); + } + + /* delete the temp file, if we don't really need it */ + maybezap(tmpnum); +} + +/* This function should be called just before termination of vi */ +void cutend() +{ + int i; + + /* free the anonymous buffers, if they aren't already free */ + cutswitch(); + + /* free all named cut buffers, since they might be forcing an older + * tmp file to be retained. + */ + for (i = 0; i < 27; i++) + { + cutfree(&named[i]); + } + + /* delete the temp file */ + maybezap(tmpnum); +} + + +/* This function is used to select the cut buffer to be used next */ +void cutname(name) + int name; /* a single character */ +{ + cbname = name; +} + + + + +/* This function copies a selected segment of text to a cut buffer */ +void cut(from, to) + MARK from; /* start of text to cut */ + MARK to; /* end of text to cut */ +{ + int first; /* logical number of first block in cut */ + int last; /* logical number of last block used in cut */ + long line; /* a line number */ + int lnmode; /* boolean: will this be a line-mode cut? */ + MARK delthru;/* end of text temporarily inserted for apnd */ + REG struct cutbuf *cb; + REG long l; + REG int i; + REG char *scan; + char *blkc; + + /* detect whether this must be a line-mode cut or char-mode cut */ + if (markidx(from) == 0 && markidx(to) == 0) + lnmode = TRUE; + else + lnmode = FALSE; + + /* by default, we don't "delthru" anything */ + delthru = MARK_UNSET; + + /* handle the "doingdot" quirks */ + if (doingdot) + { + if (!cbname) + { + cbname = dotcb; + } + } + else if (cbname != '.') + { + dotcb = cbname; + } + + /* decide which cut buffer to use */ + if (!cbname) + { + /* free up the last anonymous cut buffer */ + cutfree(&anon[NANONS - 1]); + + /* shift the anonymous cut buffers */ + for (i = NANONS - 1; i > 0; i--) + { + anon[i] = anon[i - 1]; + } + + /* use the first anonymous cut buffer */ + cb = anon; + cb->nblks = 0; + } + else if (cbname >= 'a' && cbname <= 'z') + { + cb = &named[cbname - 'a']; + cutfree(cb); + } +#ifndef CRUNCH + else if (cbname >= 'A' && cbname <= 'Z') + { + cb = &named[cbname - 'A']; + if (cb->nblks > 0) + { + /* resolve linemode/charmode differences */ + if (!lnmode && cb->lnmode) + { + from &= ~(BLKSIZE - 1); + if (markidx(to) != 0 || to == from) + { + to = to + BLKSIZE - markidx(to); + } + lnmode = TRUE; + } + + /* insert the old cut-buffer before the new text */ + mark[28] = to; + delthru = paste(from, FALSE, TRUE); + if (delthru == MARK_UNSET) + { + return; + } + delthru++; + to = mark[28]; + } + cutfree(cb); + } +#endif /* not CRUNCH */ + else if (cbname == '.') + { + cb = &named[26]; + cutfree(cb); + } + else + { + msg("Invalid cut buffer name: \"%c", cbname); + dotcb = cbname = '\0'; + return; + } + cbname = '\0'; + cb->tmpnum = tmpnum; + + /* detect whether we're doing a line mode cut */ + cb->lnmode = lnmode; + + /* ---------- */ + + /* Reporting... */ + if (markidx(from) == 0 && markidx(to) == 0) + { + rptlines = markline(to) - markline(from); + rptlabel = "yanked"; + } + + /* ---------- */ + + /* make sure each block has a physical disk address */ + blksync(); + + /* find the first block in the cut */ + line = markline(from); + for (first = 1; line > lnum[first]; first++) + { + } + + /* fetch text of the block containing that line */ + blkc = scan = blkget(first)->c; + + /* find the mark in the block */ + for (l = lnum[first - 1]; ++l < line; ) + { + while (*scan++ != '\n') + { + } + } + scan += markidx(from); + + /* remember the offset of the start */ + cb->start = scan - blkc; + + /* ---------- */ + + /* find the last block in the cut */ + line = markline(to); + for (last = first; line > lnum[last]; last++) + { + } + + /* fetch text of the block containing that line */ + if (last != first) + { + blkc = scan = blkget(last)->c; + } + else + { + scan = blkc; + } + + /* find the mark in the block */ + for (l = lnum[last - 1]; ++l < line; ) + { + while (*scan++ != '\n') + { + } + } + if (markline(to) <= nlines) + { + scan += markidx(to); + } + + /* remember the offset of the end */ + cb->end = scan - blkc; + + /* ------- */ + + /* remember the physical block numbers of all included blocks */ + cb->nblks = last - first; + if (cb->end > 0) + { + cb->nblks++; + } +#ifdef lint + cb->phys = (short *)0; +#else + cb->phys = (short *)malloc((unsigned)(cb->nblks * sizeof(short))); +#endif + for (i = 0; i < cb->nblks; i++) + { + cb->phys[i] = hdr.n[first++]; + } + +#ifndef CRUNCH + /* if we temporarily inserted text for appending, then delete that + * text now -- before the user sees it. + */ + if (delthru) + { + line = rptlines; + delete(from, delthru); + rptlines = line; + rptlabel = "yanked"; + } +#endif /* not CRUNCH */ +} + + +static void readcutblk(cb, blkno) + struct cutbuf *cb; + int blkno; +{ + char cutfname[50];/* name of an old temp file */ + int fd; /* either tmpfd or the result of open() */ +#if MSDOS || TOS + int i; +#endif + + /* decide which fd to use */ + if (cb->tmpnum == tmpnum) + { + fd = tmpfd; + } + else + { +#if MSDOS || TOS + strcpy(cutfname, o_directory); + if ((i = strlen(cutfname)) && !strchr(":/\\", cutfname[i-1])) + cutfname[i++]=SLASH; + sprintf(cutfname+i, TMPNAME+3, getpid(), cb->tmpnum); +#else + sprintf(cutfname, TMPNAME, o_directory, getpid(), cb->tmpnum); +#endif + fd = open(cutfname, O_RDONLY); + } + + /* get the block */ + lseek(fd, (long)cb->phys[blkno] * (long)BLKSIZE, 0); + if (read(fd, tmpblk.c, (unsigned)BLKSIZE) != BLKSIZE) + { + msg("Error reading back from tmp file for pasting!"); + } + + /* close the fd, if it isn't tmpfd */ + if (fd != tmpfd) + { + close(fd); + } +} + + +/* This function inserts text from a cut buffer, and returns the MARK where + * insertion ended. Return MARK_UNSET on errors. + */ +MARK paste(at, after, retend) + MARK at; /* where to insert the text */ + int after; /* boolean: insert after mark? (rather than before) */ + int retend; /* boolean: return end of text? (rather than start) */ +{ + REG struct cutbuf *cb; + REG int i; + + /* handle the "doingdot" quirks */ + if (doingdot) + { + if (!cbname) + { + if (dotcb >= '1' && dotcb < '1' + NANONS - 1) + { + dotcb++; + } + cbname = dotcb; + } + } + else if (cbname != '.') + { + dotcb = cbname; + } + + /* decide which cut buffer to use */ + if (cbname >= 'A' && cbname <= 'Z') + { + cb = &named[cbname - 'A']; + } + else if (cbname >= 'a' && cbname <= 'z') + { + cb = &named[cbname - 'a']; + } + else if (cbname >= '1' && cbname <= '9') + { + cb = &anon[cbname - '1']; + } + else if (cbname == '.') + { + cb = &named[26]; + } + else if (!cbname) + { + cb = anon; + } + else + { + msg("Invalid cut buffer name: \"%c", cbname); + cbname = '\0'; + return MARK_UNSET; + } + + /* make sure it isn't empty */ + if (cb->nblks == 0) + { + if (cbname) + msg("Cut buffer \"%c is empty", cbname); + else + msg("Cut buffer is empty"); + cbname = '\0'; + return MARK_UNSET; + } + cbname = '\0'; + + /* adjust the insertion MARK for "after" and line-mode cuts */ + if (cb->lnmode) + { + at &= ~(BLKSIZE - 1); + if (after) + { + at += BLKSIZE; + } + } + else if (after) + { + /* careful! if markidx(at) == 0 we might be pasting into an + * empty line -- so we can't blindly increment "at". + */ + if (markidx(at) == 0) + { + pfetch(markline(at)); + if (plen != 0) + { + at++; + } + } + else + { + at++; + } + } + + /* put a copy of the "at" mark in the mark[] array, so it stays in + * sync with changes made via add(). + */ + mark[27] = at; + + /* simple one-block paste? */ + if (cb->nblks == 1) + { + /* get the block */ + readcutblk(cb, 0); + + /* isolate the text we need within it */ + if (cb->end) + { + tmpblk.c[cb->end] = '\0'; + } + + /* insert it */ + ChangeText + { + add(at, &tmpblk.c[cb->start]); + } + } + else + { + /* multi-block paste */ + + ChangeText + { + i = cb->nblks - 1; + + /* add text from the last block first */ + if (cb->end > 0) + { + readcutblk(cb, i); + tmpblk.c[cb->end] = '\0'; + add(at, tmpblk.c); + i--; + } + + /* add intervening blocks */ + while (i > 0) + { + readcutblk(cb, i); + add(at, tmpblk.c); + i--; + } + + /* add text from the first cut block */ + readcutblk(cb, 0); + add(at, &tmpblk.c[cb->start]); + } + } + + /* Reporting... */ + rptlines = markline(mark[27]) - markline(at); + rptlabel = "pasted"; + + /* return the mark at the beginning/end of inserted text */ + if (retend) + { + return mark[27] - 1L; + } + return at; +} + + + + +#ifndef NO_AT + +/* This function copies characters from a cut buffer into a string. + * It returns the number of characters in the cut buffer. If the cut + * buffer is too large to fit in the string (i.e. if cb2str() returns + * a number >= size) then the characters will not have been copied. + * It returns 0 if the cut buffer is empty, and -1 for invalid cut buffers. + */ +int cb2str(name, buf, size) + int name; /* the name of a cut-buffer to get: a-z only! */ + char *buf; /* where to put the string */ + unsigned size; /* size of buf */ +{ + REG struct cutbuf *cb; + REG char *src; + REG char *dest; + + /* decide which cut buffer to use */ + if (name >= 'a' && name <= 'z') + { + cb = &named[name - 'a']; + } + else + { + return -1; + } + + /* if the buffer is empty, return 0 */ + if (cb->nblks == 0) + { + return 0; + } + + /* !!! if not a single-block cut, then fail */ + if (cb->nblks != 1) + { + return size; + } + + /* if too big, return the size now, without doing anything */ + if (cb->end - cb->start >= size) + { + return cb->end - cb->start; + } + + /* get the block */ + readcutblk(cb, 0); + + /* isolate the string within that blk */ + if (cb->start == 0) + { + tmpblk.c[cb->end] = '\0'; + } + else + { + for (dest = tmpblk.c, src = dest + cb->start; src < tmpblk.c + cb->end; ) + { + *dest++ = *src++; + } + *dest = '\0'; + } + + /* copy the string into the buffer */ + if (buf != tmpblk.c) + { + strcpy(buf, tmpblk.c); + } + + /* return the length */ + return cb->end - cb->start; +} +#endif diff --git a/commands/elvis/elvprsv.c b/commands/elvis/elvprsv.c new file mode 100755 index 000000000..f5ee575f3 --- /dev/null +++ b/commands/elvis/elvprsv.c @@ -0,0 +1,287 @@ +/* elvprsv.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains the portable sources for the "elvprsv" program. + * "Elvprsv" is run by Elvis when Elvis is about to die. It is also + * run when the computer boots up. It is not intended to be run directly + * by the user, ever. + * + * Basically, this program does the following four things: + * - It extracts the text from the temporary file, and places the text in + * a file in the /usr/preserve directory. + * - It adds a line to the /usr/preserve/Index file, describing the file + * that it just preserved. + * - It removes the temporary file. + * - It sends mail to the owner of the file, saying that the file was + * preserved, and how it can be recovered. + * + * The /usr/preserve/Index file is a log file that contains one line for each + * file that has ever been preserved. Each line of this file describes one + * preserved file. The first word on the line is the name of the file that + * contains the preserved text. The second word is the full pathname of the + * file that was being edited; for anonymous buffers, this is the directory + * name plus "/foo". + * + * If elvprsv's first argument (after the command name) starts with a hyphen, + * then the characters after the hyphen are used as a description of when + * the editor went away. This is optional. + * + * The remaining arguments are all the names of temporary files that are + * to be preserved. For example, on a UNIX system, the /etc/rc file might + * invoke it this way: + * + * elvprsv "-the system went down" /tmp/elv_*.* + * + * This file contains only the portable parts of the preserve program. + * It must #include a system-specific file. The system-specific file is + * expected to define the following functions: + * + * char *ownername(char *filename) - returns name of person who owns file + * + * void mail(char *user, char *name, char *when) + * - tell user that file was preserved + */ + +#include <stdio.h> +#include "config.h" +#include "vi.h" + +#if AMIGA +BLK tmpblk; +#error AMIGA here DEBUG +# include "amiwild.c" +# include "amiprsv.c" +#endif + +#if OSK +# undef sprintf +#endif + +#if ANY_UNIX || OSK +# include "prsvunix.c" +#endif + +#if MSDOS || TOS +# include "prsvdos.c" +# define WILDCARD_NO_MAIN +# include "wildcard.c" +#endif + + +BLK buf; +BLK hdr; +BLK name; +int rewrite_now; /* boolean: should we send text directly to orig file? */ + + + +/* This function preserves a single file, and announces its success/failure + * via an e-mail message. + */ +void preserve(tname, when) + char *tname; /* name of a temp file to be preserved */ + char *when; /* description of when the editor died */ +{ + int infd; /* fd used for reading from the temp file */ + FILE *outfp; /* fp used for writing to the recovery file */ + FILE *index; /* fp used for appending to index file */ + char outname[100]; /* the name of the recovery file */ + char *user; /* name of the owner of the temp file */ +#if AMIGA + char *prsvdir; +#endif + int i; + + /* open the temp file */ + infd = open(tname, O_RDONLY|O_BINARY); + if (infd < 0) + { + /* if we can't open the file, then we should assume that + * the filename contains wildcard characters that weren't + * expanded... and also assume that they weren't expanded + * because there are no files that need to be preserved. + * THEREFORE... we should silently ignore it. + * (Or loudly ignore it if the user was using -R) + */ + if (rewrite_now) + { + perror(tname); + } + return; + } + + /* read the header and name from the file */ + if (read(infd, hdr.c, BLKSIZE) != BLKSIZE + || read(infd, name.c, BLKSIZE) != BLKSIZE) + { + /* something wrong with the file - sorry */ + fprintf(stderr, "%s: trucated header blocks\n", tname); + close(infd); + return; + } + + /* If the filename block contains an empty string, then Elvis was + * only keeping the temp file around because it contained some text + * that was needed for a named cut buffer. The user doesn't care + * about that kind of temp file, so we should silently delete it. + */ + if (name.c[0] == '\0' && name.c[1] == '\177') + { + close(infd); + unlink(tname); + return; + } + + if (rewrite_now) + { + /* we don't need to open the index file */ + index = (FILE *)0; + + /* make sure we can read every block! */ + for (i = 1; i < MAXBLKS && hdr.n[i]; i++) + { + lseek(infd, (long)hdr.n[i] * (long)BLKSIZE, 0); + if (read(infd, buf.c, BLKSIZE) != BLKSIZE) + { + /* messed up header */ + fprintf(stderr, "%s: unrecoverable -- header trashed\n", name.c); + close(infd); + return; + } + } + + /* open the user's file for writing */ + outfp = fopen(name.c, "w"); + if (!outfp) + { + perror(name.c); + close(infd); + return; + } + } + else + { + /* open/create the index file */ + index = fopen(PRSVINDEX, "a"); + if (!index) + { + perror(PRSVINDEX); + exit(1); + } + + /* create the recovery file in the PRESVDIR directory */ +#if AMIGA + prsvdir = &PRSVDIR[strlen(PRSVDIR) - 1]; + if (*prsvdir == '/' || *prsvdir == ':') + { + sprintf(outname, "%sp%ld", PRSVDIR, ftell(index)); + } + else +#endif + sprintf(outname, "%s%cp%ld", PRSVDIR, SLASH, ftell(index)); + outfp = fopen(outname, "w"); + if (!outfp) + { + perror(outname); + close(infd); + fclose(index); + return; + } + } + + /* write the text of the file out to the recovery file */ + for (i = 1; i < MAXBLKS && hdr.n[i]; i++) + { + lseek(infd, (long)hdr.n[i] * (long)BLKSIZE, 0); + if (read(infd, buf.c, BLKSIZE) != BLKSIZE) + { + /* messed up header */ + fprintf(stderr, "%s: unrecoverable -- header trashed\n", name.c); + fclose(outfp); + close(infd); + if (index) + { + fclose(index); + } + unlink(outname); + return; + } + fputs(buf.c, outfp); + } + + /* add a line to the index file */ + if (index) + { + fprintf(index, "%s %s\n", outname, name.c); + } + + /* close everything */ + close(infd); + fclose(outfp); + if (index) + { + fclose(index); + } + + /* Are we doing this due to something more frightening than just + * a ":preserve" command? + */ + if (*when) + { + /* send a mail message */ + mail(ownername(tname), name.c, when); + + /* remove the temp file -- the editor has died already */ + unlink(tname); + } +} + +main(argc, argv) + int argc; + char **argv; +{ + int i; + char *when = "the editor went away"; + +#if MSDOS || TOS + /* expand any wildcards in the command line */ + argv = wildexpand(&argc, argv); +#endif + + /* do we have a "when" argument? */ + i = 1; + if (argc >= i + 1 && !strcmp(argv[i], "-R")) + { + rewrite_now = 1; + when = ""; + i++; +#if ANY_UNIX + setuid(geteuid()); +#endif + } +#if OSK + else + { + setuid(0); + } +#endif + if (argc >= i + 1 && argv[i][0] == '-') + { + when = argv[i] + 1; + i++; + } + + /* preserve everything we're supposed to */ + while (i < argc) + { + preserve(argv[i], when); + i++; + } +} diff --git a/commands/elvis/elvrec.c b/commands/elvis/elvrec.c new file mode 100755 index 000000000..4e9c389ea --- /dev/null +++ b/commands/elvis/elvrec.c @@ -0,0 +1,199 @@ +/* elvrec.c */ + +/* This file contains the file recovery program */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +#include <stdio.h> +#include "config.h" +#include "vi.h" +#include <sys/stat.h> + +void recover(basename, outname) + char *basename; /* the name of the file to recover */ + char *outname; /* the name of the file to write to */ +{ + char pathname[500]; /* full pathname of the file to recover */ + char line[600]; /* a line from the /usr/preserve/Index file */ + int ch; /* a character from the text being recovered */ + FILE *from; /* the /usr/preserve file, or /usr/preserve/Index */ + FILE *to; /* the user's text file */ + char *ptr; + struct stat st; +#if OSK + int uid; +#endif + + /* convert basename to a full pathname */ + if (basename) + { +#ifndef CRUNCH +# if MSDOS || TOS + if (!basename[0] || basename[1] != ':') +# else + if (basename[0] != SLASH) +# endif + { + ptr = getcwd(pathname, sizeof pathname); + if (ptr != pathname) + { + strcpy(pathname, ptr); + } + ptr = pathname + strlen(pathname); + *ptr++ = SLASH; + strcpy(ptr, basename); + } + else +#endif + { + strcpy(pathname, basename); + } + } + +#if OSK + uid = getuid(); + if(setuid(0)) + exit(_errmsg(errno, "Can't set uid\n")); +#endif + /* scan the /usr/preserve/Index file, for the *oldest* unrecovered + * version of this file. + */ + from = fopen(PRSVINDEX, "r"); + while (from && fgets(line, sizeof line, from)) + { + /* strip off the newline from the end of the string */ + line[strlen(line) - 1] = '\0'; + + /* parse the line into a "preserve" name and a "text" name */ + for (ptr = line; *ptr != ' '; ptr++) + { + } + *ptr++ = '\0'; + + /* If the "preserve" file is missing, then ignore this line + * because it describes a file that has already been recovered. + */ + if (stat(line, &st) < 0) + { + continue; + } + + /* are we looking for a specific file? */ + if (basename) + { + /* quit if we found it */ + if (!strcmp(ptr, pathname)) + { + break; + } + } + else + { + /* list this file as "available for recovery" */ + puts(ptr); + } + } + + /* file not found? */ + if (!basename || !from || feof(from)) + { + if (from != NULL) fclose(from); + if (basename) + { + fprintf(stderr, "%s: no recovered file has that exact name\n", pathname); + } + return; + } + if (from != NULL) fclose(from); + + /* copy the recovered text back into the user's file... */ + + /* open the /usr/preserve file for reading */ + from = fopen(line, "r"); + if (!from) + { + perror(line); + exit(2); + } + +#if ANY_UNIX + /* Be careful about user-id. We want to be running under the user's + * real id when we open/create the user's text file... but we want + * to be superuser when we delete the /usr/preserve file. For UNIX, + * we accomplish this by deleting the /usr/preserve file *now*, + * when it is open but before we've read it. Then we revert to the + * user's real id. + */ + unlink(line); + setuid(getuid()); +#endif +#if OSK + setuid(uid); +#endif + + if (outname == NULL) return; + + /* open the user's file for writing */ + to = fopen(outname, "w"); + if (!to) + { + perror(ptr); + exit(2); + } + + /* copy the text */ + while ((ch = getc(from)) != EOF) + { + putc(ch, to); + } + +#if !ANY_UNIX +#if OSK + fclose(from); + setuid(0); +#endif + /* delete the /usr/preserve file */ + unlink(line); +#if OSK + setuid(uid); +#endif +#endif +} + +main(argc, argv) + int argc; + char **argv; +{ + /* check arguments */ + if (argc > 3) + { + fprintf(stderr, "usage: %s [preserved_file [recovery_file]]\n", argv[0]); + exit(1); + } + + /* recover the requested file, or list recoverable files */ + if (argc == 3) + { + /* recover the file, but write it to a different filename */ + recover (argv[1], argv[2]); + } + else if (argc == 2) + { + /* recover the file */ + recover(argv[1], argv[1]); + } + else + { + /* list the recoverable files */ + recover((char *)0, (char *)0); + } + + /* success! */ + exit(0); +} diff --git a/commands/elvis/ex.c b/commands/elvis/ex.c new file mode 100755 index 000000000..064e66208 --- /dev/null +++ b/commands/elvis/ex.c @@ -0,0 +1,722 @@ +/* ex.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains the code for reading ex commands. */ + +#include "config.h" +#include "ctype.h" +#include "vi.h" + +/* This data type is used to describe the possible argument combinations */ +typedef short ARGT; +#define FROM 1 /* allow a linespec */ +#define TO 2 /* allow a second linespec */ +#define BANG 4 /* allow a ! after the command name */ +#define EXTRA 8 /* allow extra args after command name */ +#define XFILE 16 /* expand wildcards in extra part */ +#define NOSPC 32 /* no spaces allowed in the extra part */ +#define DFLALL 64 /* default file range is 1,$ */ +#define DFLNONE 128 /* no default file range */ +#define NODFL 256 /* do not default to the current file name */ +#define EXRCOK 512 /* can be in a .exrc file */ +#define NL 1024 /* if mode!=MODE_EX, then write a newline first */ +#define PLUS 2048 /* allow a line number, as in ":e +32 foo" */ +#define ZERO 4096 /* allow 0 to be given as a line number */ +#define NOBAR 8192 /* treat following '|' chars as normal */ +#define FILES (XFILE + EXTRA) /* multiple extra files allowed */ +#define WORD1 (EXTRA + NOSPC) /* one extra word allowed */ +#define FILE1 (FILES + NOSPC) /* 1 file allowed, defaults to current file */ +#define NAMEDF (FILE1 + NODFL) /* 1 file allowed, defaults to "" */ +#define NAMEDFS (FILES + NODFL) /* multiple files allowed, default is "" */ +#define RANGE (FROM + TO) /* range of linespecs allowed */ +#define NONE 0 /* no args allowed at all */ + +/* This array maps ex command names to command codes. The order in which + * command names are listed below is significant -- ambiguous abbreviations + * are always resolved to be the first possible match. (e.g. "r" is taken + * to mean "read", not "rewind", because "read" comes before "rewind") + */ +static struct +{ + char *name; /* name of the command */ + CMD code; /* enum code of the command */ + void (*fn)();/* function which executes the command */ + ARGT argt; /* command line arguments permitted/needed/used */ +} + cmdnames[] = +{ /* cmd name cmd code function arguments */ + {"append", CMD_APPEND, cmd_append, FROM+ZERO+BANG }, +#ifdef DEBUG + {"bug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL}, +#endif + {"change", CMD_CHANGE, cmd_append, RANGE+BANG }, + {"delete", CMD_DELETE, cmd_delete, RANGE+WORD1 }, + {"edit", CMD_EDIT, cmd_edit, BANG+FILE1+PLUS }, + {"file", CMD_FILE, cmd_file, NAMEDF }, + {"global", CMD_GLOBAL, cmd_global, RANGE+BANG+EXTRA+DFLALL+NOBAR}, + {"insert", CMD_INSERT, cmd_append, FROM+BANG }, + {"join", CMD_INSERT, cmd_join, RANGE+BANG }, + {"k", CMD_MARK, cmd_mark, FROM+WORD1 }, + {"list", CMD_LIST, cmd_print, RANGE+NL }, + {"move", CMD_MOVE, cmd_move, RANGE+EXTRA }, + {"next", CMD_NEXT, cmd_next, BANG+NAMEDFS }, + {"Next", CMD_PREVIOUS, cmd_next, BANG }, + {"print", CMD_PRINT, cmd_print, RANGE+NL }, + {"quit", CMD_QUIT, cmd_xit, BANG }, + {"read", CMD_READ, cmd_read, FROM+ZERO+NAMEDF}, + {"substitute", CMD_SUBSTITUTE, cmd_substitute, RANGE+EXTRA }, + {"to", CMD_COPY, cmd_move, RANGE+EXTRA }, + {"undo", CMD_UNDO, cmd_undo, NONE }, + {"vglobal", CMD_VGLOBAL, cmd_global, RANGE+EXTRA+DFLALL+NOBAR}, + {"write", CMD_WRITE, cmd_write, RANGE+BANG+FILE1+DFLALL}, + {"xit", CMD_XIT, cmd_xit, BANG+NL }, + {"yank", CMD_YANK, cmd_delete, RANGE+WORD1 }, + + {"!", CMD_BANG, cmd_shell, EXRCOK+RANGE+NAMEDFS+DFLNONE+NL+NOBAR}, + {"#", CMD_NUMBER, cmd_print, RANGE+NL }, + {"<", CMD_SHIFTL, cmd_shift, RANGE }, + {">", CMD_SHIFTR, cmd_shift, RANGE }, + {"=", CMD_EQUAL, cmd_file, RANGE }, + {"&", CMD_SUBAGAIN, cmd_substitute, RANGE }, +#ifndef NO_AT + {"@", CMD_AT, cmd_at, EXTRA }, +#endif + +#ifndef NO_ABBR + {"abbreviate", CMD_ABBR, cmd_map, EXRCOK+BANG+EXTRA}, +#endif + {"args", CMD_ARGS, cmd_args, EXRCOK+NAMEDFS }, +#ifndef NO_ERRLIST + {"cc", CMD_CC, cmd_make, BANG+FILES }, +#endif + {"cd", CMD_CD, cmd_cd, EXRCOK+BANG+NAMEDF}, + {"copy", CMD_COPY, cmd_move, RANGE+EXTRA }, +#ifndef NO_DIGRAPH + {"digraph", CMD_DIGRAPH, cmd_digraph, EXRCOK+BANG+EXTRA}, +#endif +#ifndef NO_ERRLIST + {"errlist", CMD_ERRLIST, cmd_errlist, BANG+NAMEDF }, +#endif + {"ex", CMD_EDIT, cmd_edit, BANG+FILE1 }, + {"mark", CMD_MARK, cmd_mark, FROM+WORD1 }, +#ifndef NO_MKEXRC + {"mkexrc", CMD_MKEXRC, cmd_mkexrc, NAMEDF }, +#endif + {"number", CMD_NUMBER, cmd_print, RANGE+NL }, + {"put", CMD_PUT, cmd_put, FROM+ZERO+WORD1 }, + {"set", CMD_SET, cmd_set, EXRCOK+EXTRA }, + {"shell", CMD_SHELL, cmd_shell, NL }, + {"source", CMD_SOURCE, cmd_source, EXRCOK+NAMEDF }, +#ifdef SIGTSTP + {"stop", CMD_STOP, cmd_suspend, NONE }, +#endif + {"tag", CMD_TAG, cmd_tag, BANG+WORD1 }, + {"version", CMD_VERSION, cmd_version, EXRCOK+NONE }, + {"visual", CMD_VISUAL, cmd_edit, BANG+NAMEDF }, + {"wq", CMD_WQUIT, cmd_xit, NL }, + +#ifdef DEBUG + {"debug", CMD_DEBUG, cmd_debug, RANGE+BANG+EXTRA+NL}, + {"validate", CMD_VALIDATE, cmd_validate, BANG+NL }, +#endif + {"chdir", CMD_CD, cmd_cd, EXRCOK+BANG+NAMEDF}, +#ifndef NO_COLOR + {"color", CMD_COLOR, cmd_color, EXRCOK+EXTRA }, +#endif +#ifndef NO_ERRLIST + {"make", CMD_MAKE, cmd_make, BANG+NAMEDFS }, +#endif + {"map", CMD_MAP, cmd_map, EXRCOK+BANG+EXTRA}, + {"previous", CMD_PREVIOUS, cmd_next, BANG }, + {"rewind", CMD_REWIND, cmd_next, BANG }, +#ifdef SIGTSTP + {"suspend", CMD_SUSPEND, cmd_suspend, NONE }, +#endif + {"unmap", CMD_UNMAP, cmd_map, EXRCOK+BANG+EXTRA}, +#ifndef NO_ABBR + {"unabbreviate",CMD_UNABBR, cmd_map, EXRCOK+WORD1 }, +#endif + + {(char *)0} +}; + + +/* This function parses a search pattern - given a pointer to a / or ?, + * it replaces the ending / or ? with a \0, and returns a pointer to the + * stuff that came after the pattern. + */ +char *parseptrn(ptrn) + REG char *ptrn; +{ + REG char *scan; + + for (scan = ptrn + 1; + *scan && *scan != *ptrn; + scan++) + { + /* allow backslashed versions of / and ? in the pattern */ + if (*scan == '\\' && scan[1] != '\0') + { + scan++; + } + } + if (*scan) + { + *scan++ = '\0'; + } + + return scan; +} + + +/* This function parses a line specifier for ex commands */ +char *linespec(s, markptr) + REG char *s; /* start of the line specifier */ + MARK *markptr; /* where to store the mark's value */ +{ + long num; + REG char *t; + + /* parse each ;-delimited clause of this linespec */ + do + { + /* skip an initial ';', if any */ + if (*s == ';') + { + s++; + } + + /* skip leading spaces */ + while (isspace(*s)) + { + s++; + } + + /* dot means current position */ + if (*s == '.') + { + s++; + *markptr = cursor; + } + /* '$' means the last line */ + else if (*s == '$') + { + s++; + *markptr = MARK_LAST; + } + /* digit means an absolute line number */ + else if (isdigit(*s)) + { + for (num = 0; isdigit(*s); s++) + { + num = num * 10 + *s - '0'; + } + *markptr = MARK_AT_LINE(num); + } + /* appostrophe means go to a set mark */ + else if (*s == '\'') + { + s++; + *markptr = m_tomark(cursor, 1L, (int)*s); + s++; + } + /* slash means do a search */ + else if (*s == '/' || *s == '?') + { + /* put a '\0' at the end of the search pattern */ + t = parseptrn(s); + + /* search for the pattern */ + *markptr &= ~(BLKSIZE - 1); + if (*s == '/') + { + pfetch(markline(*markptr)); + if (plen > 0) + *markptr += plen - 1; + *markptr = m_fsrch(*markptr, s); + } + else + { + *markptr = m_bsrch(*markptr, s); + } + + /* adjust command string pointer */ + s = t; + } + + /* if linespec was faulty, quit now */ + if (!*markptr) + { + return s; + } + + /* maybe add an offset */ + t = s; + if (*t == '-' || *t == '+') + { + s++; + for (num = 0; isdigit(*s); s++) + { + num = num * 10 + *s - '0'; + } + if (num == 0) + { + num = 1; + } + *markptr = m_updnto(*markptr, num, *t); + } + } while (*s == ';' || *s == '+' || *s == '-'); + + /* protect against invalid line numbers */ + num = markline(*markptr); + if (num < 1L || num > nlines) + { + msg("Invalid line number -- must be from 1 to %ld", nlines); + *markptr = MARK_UNSET; + } + + return s; +} + + + +/* This function reads an ex command and executes it. */ +void ex() +{ + char cmdbuf[150]; + REG int cmdlen; + static long oldline; + + significant = FALSE; + oldline = markline(cursor); + + while (mode == MODE_EX) + { + /* read a line */ +#ifdef CRUNCH + cmdlen = vgets(':', cmdbuf, sizeof(cmdbuf)); +#else + cmdlen = vgets(*o_prompt ? ':' : '\0', cmdbuf, sizeof(cmdbuf)); +#endif + if (cmdlen < 0) + { + return; + } + + /* if empty line, assume ".+1" */ + if (cmdlen == 0) + { + strcpy(cmdbuf, ".+1"); + qaddch('\r'); + clrtoeol(); + } + else + { + addch('\n'); + } + refresh(); + + /* parse & execute the command */ + doexcmd(cmdbuf); + + /* handle autoprint */ + if (significant || markline(cursor) != oldline) + { + significant = FALSE; + oldline = markline(cursor); + if (*o_autoprint && mode == MODE_EX) + { + cmd_print(cursor, cursor, CMD_PRINT, FALSE, ""); + } + } + } +} + +void doexcmd(cmdbuf) + char *cmdbuf; /* string containing an ex command */ +{ + REG char *scan; /* used to scan thru cmdbuf */ + MARK frommark; /* first linespec */ + MARK tomark; /* second linespec */ + REG int cmdlen; /* length of the command name given */ + CMD cmd; /* what command is this? */ + ARGT argt; /* argument types for this command */ + short forceit; /* bang version of a command? */ + REG int cmdidx; /* index of command */ + REG char *build; /* used while copying filenames */ + int iswild; /* boolean: filenames use wildcards? */ + int isdfl; /* using default line ranges? */ + int didsub; /* did we substitute file names for % or # */ + + /* ex commands can't be undone via the shift-U command */ + U_line = 0L; + + /* permit extra colons at the start of the line */ + for (; *cmdbuf == ':'; cmdbuf++) + { + } + + /* ignore command lines that start with a double-quote */ + if (*cmdbuf == '"') + { + return; + } + scan = cmdbuf; + + /* parse the line specifier */ + if (nlines < 1) + { + /* no file, so don't allow addresses */ + } + else if (*scan == '%') + { + /* '%' means all lines */ + frommark = MARK_FIRST; + tomark = MARK_LAST; + scan++; + } + else if (*scan == '0') + { + frommark = tomark = MARK_UNSET; + scan++; + } + else + { + frommark = cursor; + scan = linespec(scan, &frommark); + tomark = frommark; + if (frommark && *scan == ',') + { + scan++; + scan = linespec(scan, &tomark); + } + if (!tomark) + { + /* faulty line spec -- fault already described */ + return; + } + if (frommark > tomark) + { + msg("first address exceeds the second"); + return; + } + } + isdfl = (scan == cmdbuf); + + /* skip whitespace */ + while (isspace(*scan)) + { + scan++; + } + + /* if no command, then just move the cursor to the mark */ + if (!*scan) + { + if (tomark != MARK_UNSET) + cursor = tomark; + return; + } + + /* figure out how long the command name is */ + if (!isalpha(*scan)) + { + cmdlen = 1; + } + else + { + for (cmdlen = 1; + isalpha(scan[cmdlen]); + cmdlen++) + { + } + } + + /* lookup the command code */ + for (cmdidx = 0; + cmdnames[cmdidx].name && strncmp(scan, cmdnames[cmdidx].name, cmdlen); + cmdidx++) + { + } + argt = cmdnames[cmdidx].argt; + cmd = cmdnames[cmdidx].code; + if (cmd == CMD_NULL) + { + msg("Unknown command \"%.*s\"", cmdlen, scan); + return; + } + + /* !!! if the command doesn't have NOBAR set, then replace | with \0 */ + + /* if the command ended with a bang, set the forceit flag */ + scan += cmdlen; + if ((argt & BANG) && *scan == '!') + { + scan++; + forceit = 1; + } + else + { + forceit = 0; + } + + /* skip any more whitespace, to leave scan pointing to arguments */ + while (isspace(*scan)) + { + scan++; + } + + /* a couple of special cases for filenames */ + if (argt & XFILE) + { + /* if names were given, process them */ + if (*scan) + { + for (build = tmpblk.c, iswild = didsub = FALSE; *scan; scan++) + { + switch (*scan) + { + case '\\': + if (scan[1] == '\\' || scan[1] == '%' || scan[1] == '#') + { + *build++ = *++scan; + } + else + { + *build++ = '\\'; + } + break; + + case '%': + if (!*origname) + { + msg("No filename to substitute for %%"); + return; + } + strcpy(build, origname); + while (*build) + { + build++; + } + didsub = TRUE; + break; + + case '#': + if (!*prevorig) + { + msg("No filename to substitute for #"); + return; + } + strcpy(build, prevorig); + while (*build) + { + build++; + } + didsub = TRUE; + break; + + case '*': + case '?': +#if !(MSDOS || TOS) + case '[': + case '`': + case '{': /* } */ + case '$': + case '~': +#endif + *build++ = *scan; + iswild = TRUE; + break; + + default: + *build++ = *scan; + } + } + *build = '\0'; + + if (cmd == CMD_BANG + || cmd == CMD_READ && tmpblk.c[0] == '!' + || cmd == CMD_WRITE && tmpblk.c[0] == '!') + { + if (didsub) + { + if (mode != MODE_EX) + { + addch('\n'); + } + addstr(tmpblk.c); + addch('\n'); + exrefresh(); + } + } + else + { + if (iswild && tmpblk.c[0] != '>') + { + scan = wildcard(tmpblk.c); + } + } + } + else /* no names given, maybe assume origname */ + { + if (!(argt & NODFL)) + { + strcpy(tmpblk.c, origname); + } + else + { + *tmpblk.c = '\0'; + } + } + + scan = tmpblk.c; + } + + /* bad arguments? */ + if (!(argt & EXRCOK) && nlines < 1L) + { + msg("Can't use the \"%s\" command in a %s file", cmdnames[cmdidx].name, EXRC); + return; + } + if (!(argt & (ZERO | EXRCOK)) && frommark == MARK_UNSET) + { + msg("Can't use address 0 with \"%s\" command.", cmdnames[cmdidx].name); + return; + } + if (!(argt & FROM) && frommark != cursor && nlines >= 1L) + { + msg("Can't use address with \"%s\" command.", cmdnames[cmdidx].name); + return; + } + if (!(argt & TO) && tomark != frommark && nlines >= 1L) + { + msg("Can't use a range with \"%s\" command.", cmdnames[cmdidx].name); + return; + } + if (!(argt & EXTRA) && *scan) + { + msg("Extra characters after \"%s\" command.", cmdnames[cmdidx].name); + return; + } + if ((argt & NOSPC) && !(cmd == CMD_READ && (forceit || *scan == '!'))) + { + build = scan; +#ifndef CRUNCH + if ((argt & PLUS) && *build == '+') + { + while (*build && !isspace(*build)) + { + build++; + } + while (*build && isspace(*build)) + { + build++; + } + } +#endif /* not CRUNCH */ + for (; *build; build++) + { + if (isspace(*build)) + { + msg("Too many %s to \"%s\" command.", + (argt & XFILE) ? "filenames" : "arguments", + cmdnames[cmdidx].name); + return; + } + } + } + + /* some commands have special default ranges */ + if (isdfl && (argt & DFLALL)) + { + frommark = MARK_FIRST; + tomark = MARK_LAST; + } + else if (isdfl && (argt & DFLNONE)) + { + frommark = tomark = 0L; + } + + /* write a newline if called from visual mode */ + if ((argt & NL) && mode != MODE_EX && !exwrote) + { + addch('\n'); + exrefresh(); + } + + /* act on the command */ + (*cmdnames[cmdidx].fn)(frommark, tomark, cmd, forceit, scan); +} + + +/* This function executes EX commands from a file. It returns 1 normally, or + * 0 if the file could not be opened for reading. + */ +int doexrc(filename) + char *filename; /* name of a ".exrc" file */ +{ + int fd; /* file descriptor */ + int len; /* length of the ".exrc" file */ + + /* !!! kludge: we use U_text as the buffer. This has the side-effect + * of interfering with the shift-U visual command. Disable shift-U. + */ + U_line = 0L; + + /* open the file, read it, and close */ + fd = open(filename, O_RDONLY); + if (fd < 0) + { + return 0; + } + len = tread(fd, U_text, BLKSIZE); + close(fd); + + /* execute the string */ + exstring(U_text, len, ctrl('V')); + + return 1; +} + +/* This function executes EX commands from a string. The commands may be + * separated by newlines or by | characters. It also handles quoting. + * Each individual command is limited to 132 bytes, but the total string + * may be longer. + */ +void exstring(buf, len, qchar) + char *buf; /* the commands to execute */ + int len; /* the length of the string */ + int qchar; /* the quote character -- ^V for file, or \ for kbd */ +{ + char single[133]; /* a single command */ + char *src, *dest; + int i; + + /* find & do each command */ + for (src = buf; src < &buf[len]; src++) + { + /* Copy a single command into single[]. Convert any quoted | + * into a normal |, and stop at a newline or unquoted |. + */ + for (dest = single, i = 0; + i < 132 && src < &buf[len] && *src != '\n' && *src != '|'; + src++, i++) + { + if (src[0] == qchar && src[1] == '|') + { + src++; + } + *dest++ = *src; + } + *dest = '\0'; + + /* do it */ + doexcmd(single); + } +} diff --git a/commands/elvis/fmt.c b/commands/elvis/fmt.c new file mode 100755 index 000000000..72fe85848 --- /dev/null +++ b/commands/elvis/fmt.c @@ -0,0 +1,266 @@ +/* fmt.c */ + +/* usage: fmt [-width] [files]... + * + * Fmt rearrages text in order to make each line have roughly the + * same width. Indentation and word spacing is preserved. + * + * The default width is 72 characters, but you can override that via -width. + * If no files are given on the command line, then it reads stdin. + */ + +#include <stdio.h> + +#ifndef TRUE +# define TRUE 1 +# define FALSE 0 +#endif + + + +int width = 72; /* the desired line width */ +int isblank; /* is the current output line blank? */ +int indent; /* width of the indentation */ +char ind[512]; /* indentation text */ +char word[1024]; /* word buffer */ + +/* This function displays a usage message and quits */ +void usage() +{ + fprintf(stderr, "usage: fmt [-width] [files]...\n"); + exit(2); +} + + + +/* This function outputs a single word. It takes care of spacing and the + * newlines within a paragraph. + */ +void putword() +{ + int i; /* index into word[], or whatever */ + int ww; /* width of the word */ + int sw; /* width of spacing after word */ + static int psw; /* space width of previous word */ + static int tab; /* the width of text already written */ + + + /* separate the word and its spacing */ + for (ww = 0; word[ww] && word[ww] != ' '; ww++) + { + } + sw = strlen(word) - ww; + word[ww] = '\0'; + + /* if no spacing (that is, the word was at the end of the line) then + * assume 1 space unless the last char of the word was punctuation + */ + if (sw == 0) + { + sw = 1; + if (word[ww - 1] == '.' || word[ww - 1] == '?' || word[ww - 1] == '!') + sw = 2; + } + + /* if this is the first word on the line... */ + if (isblank) + { + /* output the indentation first */ + fputs(ind, stdout); + tab = indent; + } + else /* text has already been written to this output line */ + { + /* will the word fit on this line? */ + if (psw + ww + tab <= width) + { + /* yes - so write the previous word's spacing */ + for (i = 0; i < psw; i++) + { + putchar(' '); + } + tab += psw; + } + else + { + /* no, so write a newline and the indentation */ + putchar('\n'); + fputs(ind, stdout); + tab = indent; + } + } + + /* write the word itself */ + fputs(word, stdout); + tab += ww; + + /* remember this word's spacing */ + psw = sw; + + /* this output line isn't blank anymore. */ + isblank = FALSE; +} + + + +/* This function reformats text. */ +void fmt(in) + FILE *in; /* the name of the input stream */ +{ + int ch; /* character from input stream */ + int prevch; /* the previous character in the loop */ + int i; /* index into ind[] or word[] */ + int inword; /* boolean: are we between indent & newline? */ + + + /* for each character in the stream... */ + for (indent = -1, isblank = TRUE, inword = FALSE, i = 0, prevch = '\n'; + (ch = getc(in)) != EOF; + prevch = ch) + { + /* is this the end of a line? */ + if (ch == '\n') + { + /* if end of last word in the input line */ + if (inword) + { + /* if it really is a word */ + if (i > 0) + { + /* output it */ + word[i] = '\0'; + putword(); + } + } + else /* blank line in input */ + { + /* finish the previous paragraph */ + if (!isblank) + { + putchar('\n'); + isblank = TRUE; + } + + /* output a blank line */ + putchar('\n'); + } + + /* continue with next input line... */ + indent = -1; + i = 0; + inword = FALSE; + continue; + } + + /* if we're expecting indentation now... */ + if (indent < 0) + { + /* if this is part of the indentation... */ + if (ch == ' ' || ch == '\t') + { + /* remember it */ + ind[i++] = ch; + } + else /* end of indentation */ + { + /* mark the end of the indentation string */ + ind[i] = '\0'; + + /* calculate the width of the indentation */ + for (i = indent = 0; ind[i]; i++) + { + if (ind[i] == '\t') + indent = (indent | 7) + 1; + else + indent++; + } + + /* reset the word index */ + i = 0; + + /* reprocess that last character */ + ungetc(ch, in); + } + + /* continue in the for-loop */ + continue; + } + + /* if we get here, we're either in a word or in the space + * after a word. + */ + inword = TRUE; + + /* is this the start of a new word? */ + if (ch != ' ' && prevch == ' ') + { + /* yes! output the previous word */ + word[i] = '\0'; + putword(); + + /* reset `i' to the start of the word[] buffer */ + i = 0; + } + word[i++] = ch; + } + + /* if necessary, write a final newline */ + if (!isblank) + { + putchar('\n'); + isblank = TRUE; + } +} + + + + + +int main(argc, argv) + int argc; + char **argv; +{ + FILE *in; /* an input stream */ + int error; /* if non-zero, then an error occurred */ + int i; + + + /* handle the -width flag, if given */ + if (argc > 1 && argv[1][0] == '-') + { + width = atoi(argv[1] + 1); + if (width <= 0) + { + usage(); + } + argc--; + argv++; + } + + /* if no filenames given, then process stdin */ + if (argc == 1) + { + fmt(stdin); + } + else /* one or more filenames given */ + { + for (error = 0, i = 1; i < argc; i++) + { + in = fopen(argv[i], "r"); + if (!in) + { + perror(argv[i]); + error = 3; + } + else + { + fmt(in); + fclose(in); + } + } + } + + /* exit, possibly indicating an error */ + exit(error); + /*NOTREACHED*/ +} diff --git a/commands/elvis/input.c b/commands/elvis/input.c new file mode 100755 index 000000000..563f64351 --- /dev/null +++ b/commands/elvis/input.c @@ -0,0 +1,852 @@ +/* input.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains the input() function, which implements vi's INPUT mode. + * It also contains the code that supports digraphs. + */ + +#include "config.h" +#include "ctype.h" +#include "vi.h" + + +#ifndef NO_DIGRAPH +static struct _DIG +{ + struct _DIG *next; + char key1; + char key2; + char dig; + char save; +} *digs; + +char digraph(key1, key2) + char key1; /* the underlying character */ + char key2; /* the second character */ +{ + int newkey; + REG struct _DIG *dp; + + /* if digraphs are disabled, then just return the new char */ + if (!*o_digraph) + { + return key2; + } + + /* remember the new key, so we can return it if this isn't a digraph */ + newkey = key2; + + /* sort key1 and key2, so that their original order won't matter */ + if (key1 > key2) + { + key2 = key1; + key1 = newkey; + } + + /* scan through the digraph chart */ + for (dp = digs; + dp && (dp->key1 != key1 || dp->key2 != key2); + dp = dp->next) + { + } + + /* if this combination isn't in there, just use the new key */ + if (!dp) + { + return newkey; + } + + /* else use the digraph key */ + return dp->dig; +} + +/* this function lists or defines digraphs */ +void do_digraph(bang, extra) + int bang; + char extra[]; +{ + int dig; + REG struct _DIG *dp; + struct _DIG *prev; + static int user_defined = FALSE; /* boolean: are all later digraphs user-defined? */ + char listbuf[8]; + + /* if "extra" is NULL, then we've reached the end of the built-ins */ + if (!extra) + { + user_defined = TRUE; + return; + } + + /* if no args, then display the existing digraphs */ + if (*extra < ' ') + { + listbuf[0] = listbuf[1] = listbuf[2] = listbuf[5] = ' '; + listbuf[7] = '\0'; + for (dig = 0, dp = digs; dp; dp = dp->next) + { + if (dp->save || bang) + { + dig += 7; + if (dig >= COLS) + { + addch('\n'); + exrefresh(); + dig = 7; + } + listbuf[3] = dp->key1; + listbuf[4] = dp->key2; + listbuf[6] = dp->dig; + qaddstr(listbuf); + } + } + addch('\n'); + exrefresh(); + return; + } + + /* make sure we have at least two characters */ + if (!extra[1]) + { + msg("Digraphs must be composed of two characters"); + return; + } + + /* sort key1 and key2, so that their original order won't matter */ + if (extra[0] > extra[1]) + { + dig = extra[0]; + extra[0] = extra[1]; + extra[1] = dig; + } + + /* locate the new digraph character */ + for (dig = 2; extra[dig] == ' ' || extra[dig] == '\t'; dig++) + { + } + dig = extra[dig]; + if (!bang && dig) + { + dig |= 0x80; + } + + /* search for the digraph */ + for (prev = (struct _DIG *)0, dp = digs; + dp && (dp->key1 != extra[0] || dp->key2 != extra[1]); + prev = dp, dp = dp->next) + { + } + + /* deleting the digraph? */ + if (!dig) + { + if (!dp) + { +#ifndef CRUNCH + msg("%c%c not a digraph", extra[0], extra[1]); +#endif + return; + } + if (prev) + prev->next = dp->next; + else + digs = dp->next; + free(dp); + return; + } + + /* if necessary, create a new digraph struct for the new digraph */ + if (dig && !dp) + { + dp = (struct _DIG *)malloc(sizeof *dp); + if (!dp) + { + msg("Out of space in the digraph table"); + return; + } + if (prev) + prev->next = dp; + else + digs = dp; + dp->next = (struct _DIG *)0; + } + + /* assign it the new digraph value */ + dp->key1 = extra[0]; + dp->key2 = extra[1]; + dp->dig = dig; + dp->save = user_defined; +} + +# ifndef NO_MKEXRC +void savedigs(fd) + int fd; +{ + static char buf[] = "digraph! XX Y\n"; + REG struct _DIG *dp; + + for (dp = digs; dp; dp = dp->next) + { + if (dp->save) + { + buf[9] = dp->key1; + buf[10] = dp->key2; + buf[12] = dp->dig; + write(fd, buf, (unsigned)14); + } + } +} +# endif +#endif + + +/* This function allows the user to replace an existing (possibly zero-length) + * chunk of text with typed-in text. It returns the MARK of the last character + * that the user typed in. + */ +MARK input(from, to, when, above) + MARK from; /* where to start inserting text */ + MARK to; /* extent of text to delete */ + int when; /* either WHEN_VIINP or WHEN_VIREP */ + int above; /* boolean: take indentation from lower line? */ +{ + char key[2]; /* key char followed by '\0' char */ + char *build; /* used in building a newline+indent string */ + char *scan; /* used while looking at the indent chars of a line */ + MARK m; /* some place in the text */ +#ifndef NO_EXTENSIONS + int quit = FALSE; /* boolean: are we exiting after this? */ + int inchg; /* boolean: have we done a "beforedo()" yet? */ +#endif + +#ifdef DEBUG + /* if "from" and "to" are reversed, complain */ + if (from > to) + { + msg("ERROR: input(%ld:%d, %ld:%d)", + markline(from), markidx(from), + markline(to), markidx(to)); + return MARK_UNSET; + } +#endif + + key[1] = 0; + + /* if we're replacing text with new text, save the old stuff */ + /* (Alas, there is no easy way to save text for replace mode) */ + if (from != to) + { + cut(from, to); + } + + /* if doing a dot command, then reuse the previous text */ + if (doingdot) + { + ChangeText + { + /* delete the text that's there now */ + if (from != to) + { + delete(from, to); + } + + /* insert the previous text */ + cutname('.'); + cursor = paste(from, FALSE, TRUE) + 1L; + } + } + else /* interactive version */ + { + /* assume that whoever called this already did a beforedo() */ +#ifndef NO_EXTENSIONS + inchg = TRUE; +#endif + + /* if doing a change within the line... */ + if (from != to && markline(from) == markline(to)) + { + /* mark the end of the text with a "$" */ + change(to - 1, to, "$"); + } + else + { + /* delete the old text right off */ + if (from != to) + { + delete(from, to); + } + to = from; + } + + /* handle autoindent of the first line, maybe */ + cursor = from; + m = (above ? (cursor + BLKSIZE) : (cursor - BLKSIZE)); + if (*o_autoindent && markidx(m) == 0 + && markline(m) >= 1L && markline(m) <= nlines) + { + /* Only autoindent blank lines. */ + pfetch(markline(cursor)); + if (plen == 0) + { + /* Okay, we really want to autoindent */ + pfetch(markline(m)); + for (scan = ptext, build = tmpblk.c; + *scan == ' ' || *scan == '\t'; + ) + { + *build++ = *scan++; + } + if (build > tmpblk.c) + { + *build = '\0'; + add(cursor, tmpblk.c); + cursor += (build - tmpblk.c); + if (cursor > to) + to = cursor; + } + } + } + + /* repeatedly add characters from the user */ + for (;;) + { + /* Get a character */ + redraw(cursor, TRUE); +#ifdef DEBUG2 + msg("cursor=%ld.%d, to=%ld.%d", + markline(cursor), markidx(cursor), + markline(to), markidx(to)); +#endif +#ifndef NO_ABBR + pfetch(markline(cursor)); + build = ptext; + if (pline == markline(from)) + build += markidx(from); + for (scan = ptext + markidx(cursor); --scan >= build && isalnum(*scan); ) + { + } + scan++; + key[0] = getabkey(when, scan, (int)(ptext + markidx(cursor) - scan)); +#else + key[0] = getkey(when); +#endif +#ifndef NO_VISIBLE + if (key[0] != '\0' && V_from != MARK_UNSET) + { + msg("Can't modify text during a selection"); + beep(); + continue; + } +#endif + +#ifndef NO_EXTENSIONS + if (key[0] == ctrl('O')) + { + if (inchg) + { + if (cursor < to) + { + delete(cursor, to); + redraw(cursor, TRUE); + } + afterdo(); + inchg = FALSE; + } + } + else if (key[0] != ctrl('[')) + { + if (!inchg) + { + beforedo(FALSE); + inchg = TRUE; + } + } +#endif + +#ifndef CRUNCH + /* if wrapmargin is set & we're past the + * warpmargin, then change the last whitespace + * characters on line into a newline + */ + if (*o_wrapmargin != 0) + { + pfetch(markline(cursor)); + if (idx2col(cursor, ptext, TRUE) > COLS - (*o_wrapmargin & 0xff)) + { + build = tmpblk.c; + *build++ = '\n'; + if (*o_autoindent) + { + /* figure out indent for next line */ + for (scan = ptext; *scan == ' ' || *scan == '\t'; ) + { + *build++ = *scan++; + } + } + *build = '\0'; + + scan = ptext + plen; + m = cursor & ~(BLKSIZE - 1); + while (ptext < scan) + { + scan--; + if (*scan != ' ' && *scan != '\t') + continue; + + /*break up line, and we do autoindent if needed*/ + change(m + (scan - ptext), m + (scan - ptext) + 1, tmpblk.c); + cursor = (cursor & ~(BLKSIZE - 1)) + + BLKSIZE + + strlen(tmpblk.c) - 1 + + plen - (scan - ptext) - 1; + + /*remove trailing spaces on previous line*/ + pfetch(markline(m)); + scan = ptext + plen; + while (ptext < scan) + { + scan--; + if (*scan != ' ' && *scan != '\t') + break; + } + delete(m + (scan-ptext) + 1, m + plen); + + break; + } + } + } +#endif /* !CRUNCH */ + + /* process it */ + switch (*key) + { +#ifndef NO_EXTENSIONS + case ctrl('O'): /* special movement mapped keys */ + *key = getkey(0); + switch (*key) + { + case 'h': m = m_left(cursor, 0L); break; + case 'j': + case 'k': m = m_updnto(cursor, 0L, *key); break; + case 'l': m = cursor + 1; break; + case 'B': + case 'b': m = m_bword(cursor, 0L, *key); break; + case 'W': + case 'w': m = m_fword(cursor, 0L, *key, '\0'); break; + case '^': m = m_front(cursor, 0L); break; + case '$': m = m_rear(cursor, 0L); break; + case ctrl('B'): + case ctrl('F'): + m = m_scroll(cursor, 0L, *key); break; + case 'x': +#ifndef NO_VISIBLE + if (V_from) + beep(); + else +#endif + ChangeText + { + m = v_xchar(cursor, 0L, 'x'); + } + break; + case 'i': m = to = from = cursor; + when = WHEN_VIINP + WHEN_VIREP - when; + break; + case 'K': + pfetch(markline(cursor)); + changes++; /* <- after this, we can alter ptext */ + ptext[markidx(cursor)] = 0; + for (scan = ptext + markidx(cursor) - 1; + scan >= ptext && isalnum(*scan); + scan--) + { + } + scan++; + m = (*scan ? v_keyword(scan, cursor, 0L) : cursor); + break; + +# ifndef NO_VISIBLE + case 'v': + case 'V': + if (V_from) + V_from = MARK_UNSET; + else + V_from = cursor; + m = from = to = cursor; + V_linemd = (*key == 'V'); + break; + + case 'd': + case 'y': + case '\\': + /* do nothing if unmarked */ + if (!V_from) + { + msg("You must mark the text first"); + beep(); + break; + } + + /* "from" must come before "to" */ + if (V_from < cursor) + { + from = V_from; + to = cursor; + } + else + { + from = cursor; + to = V_from; + } + + /* we don't need V_from anymore */ + V_from = MARK_UNSET; + + if (V_linemd) + { + /* adjust for line mode */ + from &= ~(BLKSIZE - 1); + to |= (BLKSIZE - 1); + } + else + { + /* in character mode, we must + * worry about deleting the newline + * at the end of the last line + */ + pfetch(markline(to)); + if (markidx(to) == plen) + to |= (BLKSIZE - 1); + } + to++; + + switch (*key) + { + case 'y': + cut(from, to); + break; + + case 'd': + ChangeText + { + cut(from, to); + delete(from, to); + } + cursor = from; + break; + +#ifndef NO_POPUP + case '\\': + ChangeText + { + cursor = v_popup(from, to); + } + break; +#endif + } + m = from = to = cursor; + break; + + case 'p': + case 'P': + V_from = MARK_UNSET; + ChangeText + { + m = from = to = cursor = paste(cursor, (*key == 'p'), FALSE); + } + break; +# endif /* !NO_VISIBLE */ + default: m = MARK_UNSET; + } + + /* adjust the moved cursor */ + if (m != cursor) + { + m = adjmove(cursor, m, (*key == 'j' || *key == 'k' ? 0x20 : 0)); + if (*key == '$' || (*key == 'l' && m <= cursor)) + { + m++; + } + } + + /* if the cursor is reasonable, use it */ + if (m == MARK_UNSET) + { + beep(); + } + else + { + from = to = cursor = m; + } + break; + + case ctrl('Z'): + if (getkey(0) == ctrl('Z')) + { + quit = TRUE; + goto BreakBreak; + } + break; +#endif + + case ctrl('['): + /* if last line contains only whitespace, then remove whitespace */ + if (*o_autoindent) + { + pfetch(markline(cursor)); + for (scan = ptext; isspace(*scan); scan++) + { + } + if (scan > ptext && !*scan) + { + cursor &= ~(BLKSIZE - 1L); + if (to < cursor + plen) + { + to = cursor + plen; + } + } + } + goto BreakBreak; + + case ctrl('U'): + if (markline(cursor) == markline(from)) + { + cursor = from; + } + else + { + cursor &= ~(BLKSIZE - 1); + } + break; + + case ctrl('D'): + case ctrl('T'): + if (to > cursor) + { + delete(cursor, to); + } + mark[27] = cursor; + cmd_shift(cursor, cursor, *key == ctrl('D') ? CMD_SHIFTL : CMD_SHIFTR, TRUE, ""); + if (mark[27]) + { + cursor = mark[27]; + } + else + { + cursor = m_front(cursor, 0L); + } + to = cursor; + break; + + case '\b': + if (cursor <= from) + { + beep(); + } + else if (markidx(cursor) == 0) + { + cursor -= BLKSIZE; + pfetch(markline(cursor)); + cursor += plen; + } + else + { + cursor--; + } + break; + + case ctrl('W'): + m = m_bword(cursor, 1L, 'b'); + if (markline(m) == markline(cursor) && m >= from) + { + cursor = m; + if (from > cursor) + { + from = cursor; + } + } + else + { + beep(); + } + break; + + case '\n': +#if OSK + case '\l': +#else + case '\r': +#endif + build = tmpblk.c; + *build++ = '\n'; + if (*o_autoindent) + { + /* figure out indent for next line */ + pfetch(markline(cursor)); + for (scan = ptext; *scan == ' ' || *scan == '\t'; ) + { + *build++ = *scan++; + } + + /* remove indent from this line, if blank */ + if ((scan - ptext) >= markidx(cursor) && plen > 0) + { + to = cursor &= ~(BLKSIZE - 1); + delete(cursor, cursor + plen); + } + } + *build = 0; + if (cursor >= to && when != WHEN_VIREP) + { + add(cursor, tmpblk.c); + } + else + { + change(cursor, to, tmpblk.c); + } + redraw(cursor, TRUE); + to = cursor = (cursor & ~(BLKSIZE - 1)) + + BLKSIZE + + (int)(build - tmpblk.c) - 1; + break; + + case ctrl('A'): + case ctrl('P'): + if (cursor < to) + { + delete(cursor, to); + } + if (*key == ctrl('A')) + { + cutname('.'); + } + to = cursor = paste(cursor, FALSE, TRUE) + 1L; + break; + + case ctrl('V'): + if (cursor >= to && when != WHEN_VIREP) + { + add(cursor, "^"); + } + else + { + change(cursor, to, "^"); + to = cursor + 1; + } + redraw(cursor, TRUE); + *key = getkey(0); + if (*key == '\n') + { + /* '\n' too hard to handle */ +#if OSK + *key = '\l'; +#else + *key = '\r'; +#endif + } + change(cursor, cursor + 1, key); + cursor++; + if (cursor > to) + { + to = cursor; + } + break; + + case ctrl('L'): + case ctrl('R'): + redraw(MARK_UNSET, FALSE); + break; + + default: + if (cursor >= to && when != WHEN_VIREP) + { + add(cursor, key); + cursor++; + to = cursor; + } + else + { + pfetch(markline(cursor)); + if (markidx(cursor) == plen) + { + add(cursor, key); + } + else + { +#ifndef NO_DIGRAPH + *key = digraph(ptext[markidx(cursor)], *key); +#endif + change(cursor, cursor + 1, key); + } + cursor++; + } +#ifndef NO_SHOWMATCH + /* show matching "({[" if necessary */ + if (*o_showmatch && strchr(")}]", *key)) + { + redraw(cursor, TRUE); + m = m_match(cursor - 1, 0L); + if (markline(m) >= topline + && markline(m) <= botline) + { + redraw(m, TRUE); + refresh(); + sleep(1); + } + } +#endif + } /* end switch(*key) */ + } /* end for(;;) */ +BreakBreak:; + /* delete any excess characters */ + if (cursor < to) + { +#ifndef NO_EXTENSIONS + /* if we aren't in the middle of a change, start one! */ + if (!inchg) + { + beforedo(FALSE); + inchg = TRUE; + } +#endif + delete(cursor, to); + } + + } /* end if doingdot else */ + + /* put the new text into a cut buffer for possible reuse */ + if (!doingdot) + { + blksync(); + cutname('.'); + cut(from, cursor); + } + + /* move to last char that we inputted, unless it was newline */ + if (markidx(cursor) != 0) + { + cursor--; + } + redraw(cursor, FALSE); + +#ifndef NO_EXTENSIONS + if (quit) + { + /* if this is a nested "do", then cut it short */ + abortdo(); + + /* exit, unless we can't write out the file */ + cursor = v_xit(cursor, 0L, 'Z'); + } +#endif + + rptlines = 0L; + return cursor; +} diff --git a/commands/elvis/main.c b/commands/elvis/main.c new file mode 100755 index 000000000..68ed2e672 --- /dev/null +++ b/commands/elvis/main.c @@ -0,0 +1,516 @@ +/* main.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains the main() function of vi */ + +/* HACK! bcc needs to disable use of precompiled headers for this file, + or else command line args will not be passed to elvis */ +#if __BORLANDC__ +#include "borland.h" +#endif + +#include "config.h" +#include <setjmp.h> +#include "vi.h" + +extern trapint(); /* defined below */ +extern char *getenv(); +jmp_buf jmpenv; + +#ifndef NO_DIGRAPH +static init_digraphs(); +#endif + +/*---------------------------------------------------------------------*/ + +#if AMIGA +# include "amiwild.c" +main (argc, argv) +#else +# if VMS +# include "vmswild.c" +main (argc, argv) +# else +void main(argc, argv) +# endif +#endif + int argc; + char *argv[]; +{ + int i; + char *cmd = (char *)0; + char *err = (char *)0; + char *str; + char *tag = (char *)0; + + /* set mode to MODE_VI or MODE_EX depending on program name */ + switch (argv[0][strlen(argv[0]) - 1]) + { + case 'x': /* "ex" */ + mode = MODE_EX; + break; + + case 'w': /* "view" */ + mode = MODE_VI; + *o_readonly = TRUE; + break; +#ifndef NO_EXTENSIONS + case 't': /* "edit" or "input" */ + mode = MODE_VI; + *o_inputmode = TRUE; + break; +#endif + default: /* "vi" or "elvis" */ + mode = MODE_VI; + } + +#ifndef DEBUG +# ifdef SIGQUIT + /* normally, we ignore SIGQUIT. SIGINT is trapped later */ + signal(SIGQUIT, SIG_IGN); +# endif +#endif + + /* temporarily ignore SIGINT */ + signal(SIGINT, SIG_IGN); + + /* start curses */ + initscr(); + cbreak(); + noecho(); + scrollok(stdscr, TRUE); + + /* arrange for deadly signals to be caught */ +# ifdef SIGHUP + signal(SIGHUP, (void(*)()) deathtrap); +# endif +# ifndef DEBUG +# ifdef SIGILL + signal(SIGILL, (void(*)()) deathtrap); +# endif +# ifdef SIGBUS + signal(SIGBUS, (void(*)()) deathtrap); +# endif +# ifdef SIGSEGV + signal(SIGSEGV, (void(*)()) deathtrap); +# endif +# ifdef SIGSYS + signal(SIGSYS, (void(*)()) deathtrap); +# endif +# endif /* !DEBUG */ +# ifdef SIGPIPE + signal(SIGPIPE, (void(*)()) deathtrap); +# endif +# ifdef SIGTERM + signal(SIGTERM, (void(*)()) deathtrap); +# endif +# ifdef SIGUSR1 + signal(SIGUSR1, (void(*)()) deathtrap); +# endif +# ifdef SIGUSR2 + signal(SIGUSR2, (void(*)()) deathtrap); +# endif + + /* initialize the options - must be done after initscr(), so that + * we can alter LINES and COLS if necessary. + */ + initopts(); + + /* map the arrow keys. The KU,KD,KL,and KR variables correspond to + * the :ku=: (etc.) termcap capabilities. The variables are defined + * as part of the curses package. + */ + if (has_KU) mapkey(has_KU, "k", WHEN_VICMD|WHEN_INMV, "<Up>"); + if (has_KD) mapkey(has_KD, "j", WHEN_VICMD|WHEN_INMV, "<Down>"); + if (has_KL) mapkey(has_KL, "h", WHEN_VICMD|WHEN_INMV, "<Left>"); + if (has_KR) mapkey(has_KR, "l", WHEN_VICMD|WHEN_INMV, "<Right>"); + if (has_HM) mapkey(has_HM, "^", WHEN_VICMD|WHEN_INMV, "<Home>"); + if (has_EN) mapkey(has_EN, "$", WHEN_VICMD|WHEN_INMV, "<End>"); + if (has_PU) mapkey(has_PU, "\002", WHEN_VICMD|WHEN_INMV, "<PageUp>"); + if (has_PD) mapkey(has_PD, "\006", WHEN_VICMD|WHEN_INMV, "<PageDn>"); + if (has_KI) mapkey(has_KI, "i", WHEN_VICMD|WHEN_INMV, "<Insert>"); +#if MSDOS +# if RAINBOW + if (!strcmp("rainbow", o_term)) + { + mapkey("\033[1~", "/", WHEN_VICMD, "<Find>"); + mapkey("\033[3~", "x", WHEN_VICMD|WHEN_INMV, "<Remove>"); + mapkey("\033[4~", "v", WHEN_VICMD|WHEN_INMV, "<Select>"); + mapkey("\033[17~", ":sh\n", WHEN_VICMD, "<Intrpt>"); + mapkey("\033[19~", ":q\n", WHEN_VICMD, "<Cancel>"); + mapkey("\033[21~", "ZZ", WHEN_VICMD, "<Exit>"); + mapkey("\033[26~", "V", WHEN_VICMD|WHEN_INMV, "<AddlOp>"); + mapkey("\033[28~", "\\", WHEN_VICMD|WHEN_INMV, "<Help>"); + mapkey("\033[29~", "K", WHEN_VICMD|WHEN_INMV, "<Do>"); + } + else +# endif /* RAINBOW */ + { + mapkey("#S", "x", WHEN_VICMD|WHEN_INMV, "<Delete>"); + mapkey("#s", "B", WHEN_VICMD|WHEN_INMV, "^<Left>"); + mapkey("#t", "W", WHEN_VICMD|WHEN_INMV, "^<Right>"); + } +#else /* not MSDOS */ +# if COHERENT + mapkey("\033[P", "x", WHEN_VICMD|WHEN_INMV, "<Del>"); +# else +#if AMIGA + mapkey("\233?~", "\\", WHEN_VICMD|WHEN_INMV, "<Help>"); +#endif + + if (ERASEKEY != '\177') + { + mapkey("\177", "x", WHEN_VICMD|WHEN_INMV, "<Del>"); + } +# endif +#endif + +#ifndef NO_DIGRAPH + init_digraphs(); +#endif /* NO_DIGRAPH */ + + /* process any flags */ + for (i = 1; i < argc && *argv[i] == '-'; i++) + { + switch (argv[i][1]) + { + case 'R': /* readonly */ + *o_readonly = TRUE; + break; + + case 'L': + case 'r': /* recover */ + msg("Use the `elvrec` program to recover lost files"); + endmsgs(); + refresh(); + endwin(); + exit(0); + break; + + case 't': /* tag */ + if (argv[i][2]) + { + tag = argv[i] + 2; + } + else + { + tag = argv[++i]; + } + break; + + case 'v': /* vi mode */ + mode = MODE_VI; + break; + + case 'e': /* ex mode */ + mode = MODE_EX; + break; +#ifndef NO_EXTENSIONS + case 'i': /* input mode */ + *o_inputmode = TRUE; + break; +#endif +#ifndef NO_ERRLIST + case 'm': /* use "errlist" as the errlist */ + if (argv[i][2]) + { + err = argv[i] + 2; + } + else if (i + 1 < argc) + { + err = argv[++i]; + } + else + { + err = ""; + } + break; +#endif +#ifndef CRUNCH + case 'c': /* run the following command, later */ + if (argv[i][2]) + { + cmd = argv[i] + 2; + } + else + { + cmd = argv[++i]; + } + break; + + case 'w': /* set the window size */ + if (argv[i][2]) + { + *o_window = atoi(argv[i] + 2); + wset = TRUE; + } + else + { + *o_window = atoi(argv[++i]); + wset = TRUE; + } + break; +#endif + default: + msg("Ignoring unknown flag \"%s\"", argv[i]); + } + } + + /* if we were given an initial ex command, save it... */ + if (i < argc && *argv[i] == '+') + { + if (argv[i][1]) + { + cmd = argv[i++] + 1; + } + else + { + cmd = "$"; /* "vi + file" means start at EOF */ + i++; + } + } + + /* the remaining args are file names. */ + if (i < argc) + { + strcpy(args, argv[i]); + while (++i < argc && strlen(args) + 1 + strlen(argv[i]) < sizeof args) + { + strcat(args, " "); + strcat(args, argv[i]); + } +#if MSDOS || TOS + /* expand wildcard characters, if necessary */ + if (strchr(args, '*') || strchr(args, '?')) + { + strcpy(args, wildcard(args)); + } +#endif + strcpy(tmpblk.c, args); + cmd_args(MARK_UNSET, MARK_UNSET, CMD_ARGS, TRUE, tmpblk.c); + } + else + { + /* empty args list */ + args[0] = '\0'; + nargs = 1; + argno = -1; + } + + /* perform the .exrc files and EXINIT environment variable */ +#ifdef SYSEXRC + doexrc(SYSEXRC); +#endif +#ifdef HMEXRC + str = getenv("HOME"); + if (str && *str) + { + strcpy(tmpblk.c, str); + str = tmpblk.c + strlen(tmpblk.c); +#if !VMS +# if AMIGA /* Don't SLASH a device. "Elvis:.exrc" */ + if (str[-1] != COLON && str[-1] != SLASH) +# else + if (str[-1] != SLASH) +# endif + { + *str++ = SLASH; + } +#endif + strcpy(str, HMEXRC); + doexrc(tmpblk.c); + } +#endif +#ifndef CRUNCH + if (*o_exrc) +#endif + { + doexrc(EXRC); + } +#ifdef EXINIT + str = getenv(EXINIT); + if (str) + { + exstring(str, strlen(str), ctrl('V')); + } +#endif + + /* search for a tag (or an error) now, if desired */ + blkinit(); + if (tag) + { + cmd_tag(MARK_FIRST, MARK_FIRST, CMD_TAG, 0, tag); + } +#ifndef NO_ERRLIST + else if (err) + { + cmd_errlist(MARK_FIRST, MARK_FIRST, CMD_ERRLIST, 0, err); + } +#endif + + /* if no tag/err, or tag failed, then start with first arg */ + if (tmpfd < 0) + { + /* start with first arg */ + cmd_next(MARK_UNSET, MARK_UNSET, CMD_NEXT, FALSE, ""); + + /* pretend to do something, just to force a recoverable + * version of the file out to disk + */ + ChangeText + { + } + clrflag(file, MODIFIED); + } + + /* now we do the immediate ex command that we noticed before */ + if (cmd) + { + doexcmd(cmd); + } + + /* repeatedly call ex() or vi() (depending on the mode) until the + * mode is set to MODE_QUIT + */ + while (mode != MODE_QUIT) + { + if (setjmp(jmpenv)) + { + /* Maybe we just aborted a change? */ + abortdo(); + } + signal(SIGINT, (void(*)()) trapint); + + switch (mode) + { + case MODE_VI: + vi(); + break; + + case MODE_EX: + ex(); + break; +#ifdef DEBUG + default: + msg("mode = %d?", mode); + mode = MODE_QUIT; +#endif + } + } + + /* free up the cut buffers */ + cutend(); + + /* end curses */ +#ifndef NO_CURSORSHAPE + if (has_CQ) + do_CQ(); +#endif + endmsgs(); + move(LINES - 1, 0); + clrtoeol(); + refresh(); + endwin(); + + exit(0); + /*NOTREACHED*/ +} + + +/*ARGSUSED*/ +int trapint(signo) + int signo; +{ + beep(); + resume_curses(FALSE); + abortdo(); +#if OSK + sigmask(-1); +#endif +#if TURBOC || __GNUC__ || _ANSI + signal(signo, (void (*)())trapint); +#else + signal(signo, trapint); +#endif + doingglobal = FALSE; + + longjmp(jmpenv, 1); + + return 0; +} + + + +#ifndef NO_DIGRAPH + +/* This stuff us used to build the default digraphs table. */ +static char digtable[][4] = +{ +# ifdef CS_IBMPC + "C,\200", "u\"\1", "e'\2", "a^\3", + "a\"\4", "a`\5", "a@\6", "c,\7", + "e^\10", "e\"\211", "e`\12", "i\"\13", + "i^\14", "i`\15", "A\"\16", "A@\17", + "E'\20", "ae\21", "AE\22", "o^\23", + "o\"\24", "o`\25", "u^\26", "u`\27", + "y\"\30", "O\"\31", "U\"\32", "a'\240", + "i'!", "o'\"", "u'#", "n~$", + "N~%", "a-&", "o-'", "~?(", + "~!-", "\"<.", "\">/", +# ifdef CS_SPECIAL + "2/+", "4/,", "^+;", "^q<", + "^c=", "^r>", "^t?", "pp]", + "^^^", "oo_", "*a`", "*ba", + "*pc", "*Sd", "*se", "*uf", + "*tg", "*Ph", "*Ti", "*Oj", + "*dk", "*Hl", "*hm", "*En", + "*No", "eqp", "pmq", "ger", + "les", "*It", "*iu", "*/v", + "*=w", "sq{", "^n|", "^2}", + "^3~", "^_\377", +# endif /* CS_SPECIAL */ +# endif /* CS_IBMPC */ +# ifdef CS_LATIN1 + "~!!", "a-*", "\">+", "o-:", + "\"<>", "~??", + + "A`@", "A'A", "A^B", "A~C", + "A\"D", "A@E", "AEF", "C,G", + "E`H", "E'I", "E^J", "E\"K", + "I`L", "I'M", "I^N", "I\"O", + "-DP", "N~Q", "O`R", "O'S", + "O^T", "O~U", "O\"V", "O/X", + "U`Y", "U'Z", "U^[", "U\"\\", + "Y'_", + + "a``", "a'a", "a^b", "a~c", + "a\"d", "a@e", "aef", "c,g", + "e`h", "e'i", "e^j", "e\"k", + "i`l", "i'm", "i^n", "i\"o", + "-dp", "n~q", "o`r", "o's", + "o^t", "o~u", "o\"v", "o/x", + "u`y", "u'z", "u^{", "u\"|", + "y'~", +# endif /* CS_LATIN1 */ + "" +}; + +static init_digraphs() +{ + int i; + + for (i = 0; *digtable[i]; i++) + { + do_digraph(FALSE, digtable[i]); + } + do_digraph(FALSE, (char *)0); + return 0; +} +#endif /* NO_DIGRAPH */ diff --git a/commands/elvis/misc.c b/commands/elvis/misc.c new file mode 100755 index 000000000..c9e0f689d --- /dev/null +++ b/commands/elvis/misc.c @@ -0,0 +1,103 @@ +/* misc.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains functions which didn't seem happy anywhere else */ + +#include "config.h" +#include "vi.h" + + +/* find a particular line & return a pointer to a copy of its text */ +char *fetchline(line) + long line; /* line number of the line to fetch */ +{ + int i; + REG char *scan; /* used to search for the line in a BLK */ + long l; /* line number counter */ + static BLK buf; /* holds ONLY the selected line (as string) */ + REG char *cpy; /* used while copying the line */ + static long nextline; /* } These four variables are used */ + static long chglevel; /* } to implement a shortcut when */ + static char *nextscan; /* } consecutive lines are fetched */ + static long nextlnum; /* } */ + + /* can we do a shortcut? */ + if (changes == chglevel && line == nextline) + { + scan = nextscan; + } + else + { + /* scan lnum[] to determine which block its in */ + for (i = 1; line > lnum[i]; i++) + { + } + nextlnum = lnum[i]; + + /* fetch text of the block containing that line */ + scan = blkget(i)->c; + + /* find the line in the block */ + for (l = lnum[i - 1]; ++l < line; ) + { + while (*scan++ != '\n') + { + } + } + } + + /* copy it into a block by itself, with no newline */ + for (cpy = buf.c; *scan != '\n'; ) + { + *cpy++ = *scan++; + } + *cpy = '\0'; + + /* maybe speed up the next call to fetchline() ? */ + if (line < nextlnum) + { + nextline = line + 1; + chglevel = changes; + nextscan = scan + 1; + } + else + { + nextline = 0; + } + + /* Calls to fetchline() interfere with calls to pfetch(). Make sure + * that pfetch() resets itself on its next invocation. + */ + pchgs = 0L; + + /* Return a pointer to the line's text */ + return buf.c; +} + + +/* error message from the regexp code */ +void regerror(txt) + char *txt; /* an error message */ +{ + msg("RE error: %s", txt); +} + +/* This function is equivelent to the pfetch() macro */ +void pfetch(l) + long l; /* line number of line to fetch */ +{ + if(l != pline || changes != pchgs) + { + pline = (l); + ptext = fetchline(pline); + plen = strlen(ptext); + pchgs = changes; + } +} diff --git a/commands/elvis/modify.c b/commands/elvis/modify.c new file mode 100755 index 000000000..af7914601 --- /dev/null +++ b/commands/elvis/modify.c @@ -0,0 +1,474 @@ +/* modify.c */ + +/* This file contains the low-level file modification functions: + * delete(frommark, tomark) - removes line or portions of lines + * add(frommark, text) - inserts new text + * change(frommark, tomark, text) - delete, then add + */ + +#include "config.h" +#include "vi.h" + +#ifdef DEBUG2 +# include <stdio.h> +static FILE *dbg; + +/*VARARGS1*/ +debout(msg, arg1, arg2, arg3, arg4, arg5) + char *msg, *arg1, *arg2, *arg3, *arg4, *arg5; +{ + if (!dbg) + { + dbg = fopen("debug.out", "w"); + if (!dbg) + return; + setbuf(dbg, (FILE *)0); + } + fprintf(dbg, msg, arg1, arg2, arg3, arg4, arg5); +} +#endif /* DEBUG2 */ + +/* delete a range of text from the file */ +void delete(frommark, tomark) + MARK frommark; /* first char to be deleted */ + MARK tomark; /* AFTER last char to be deleted */ +{ + int i; /* used to move thru logical blocks */ + REG char *scan; /* used to scan thru text of the blk */ + REG char *cpy; /* used when copying chars */ + BLK *blk; /* a text block */ + long l; /* a line number */ + MARK m; /* a traveling version of frommark */ + +#ifdef DEBUG2 + debout("delete(%ld.%d, %ld.%d)\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark)); +#endif + + /* if not deleting anything, quit now */ + if (frommark == tomark) + { + return; + } + + /* This is a change */ + changes++; + significant = TRUE; + + /* supply clues to the redraw module */ + redrawrange(markline(frommark), markline(tomark), markline(frommark)); + + /* adjust marks 'a through 'z and '' as needed */ + l = markline(tomark); + for (i = 0; i < NMARKS; i++) + { + if (mark[i] < frommark) + { + continue; + } + else if (mark[i] < tomark) + { + mark[i] = MARK_UNSET; + } + else if (markline(mark[i]) == l) + { + if (markline(frommark) == l) + { + mark[i] -= markidx(tomark) - markidx(frommark); + } + else + { + mark[i] -= markidx(tomark); + } + } + else + { + mark[i] -= MARK_AT_LINE(l - markline(frommark)); + } + } + + /* Reporting... */ + if (markidx(frommark) == 0 && markidx(tomark) == 0) + { + rptlines = markline(tomark) - markline(frommark); + rptlabel = "deleted"; + } + + /* find the block containing frommark */ + l = markline(frommark); + for (i = 1; lnum[i] < l; i++) + { + } + + /* process each affected block... */ + for (m = frommark; + m < tomark && lnum[i] < INFINITY; + m = MARK_AT_LINE(lnum[i - 1] + 1)) + { + /* fetch the block */ + blk = blkget(i); + + /* find the mark in the block */ + scan = blk->c; + for (l = markline(m) - lnum[i - 1] - 1; l > 0; l--) + { + while (*scan++ != '\n') + { + } + } + scan += markidx(m); + + /* figure out where the changes to this block end */ + if (markline(tomark) > lnum[i]) + { + cpy = blk->c + BLKSIZE; + } + else if (markline(tomark) == markline(m)) + { + cpy = scan - markidx(m) + markidx(tomark); + } + else + { + cpy = scan; + for (l = markline(tomark) - markline(m); + l > 0; + l--) + { + while (*cpy++ != '\n') + { + } + } + cpy += markidx(tomark); + } + + /* delete the stuff by moving chars within this block */ + while (cpy < blk->c + BLKSIZE) + { + *scan++ = *cpy++; + } + while (scan < blk->c + BLKSIZE) + { + *scan++ = '\0'; + } + + /* adjust tomark to allow for lines deleted from this block */ + tomark -= MARK_AT_LINE(lnum[i] + 1 - markline(m)); + + /* if this block isn't empty now, then advance i */ + if (*blk->c) + { + i++; + } + + /* the buffer has changed. Update hdr and lnum. */ + blkdirty(blk); + } + + /* must have at least 1 line */ + if (nlines == 0) + { + blk = blkadd(1); + blk->c[0] = '\n'; + blkdirty(blk); + cursor = MARK_FIRST; + } +} + + +/* add some text at a specific place in the file */ +void add(atmark, newtext) + MARK atmark; /* where to insert the new text */ + char *newtext; /* NUL-terminated string to insert */ +{ + REG char *scan; /* used to move through string */ + REG char *build; /* used while copying chars */ + int addlines; /* number of lines we're adding */ + int lastpart; /* size of last partial line */ + BLK *blk; /* the block to be modified */ + int blkno; /* the logical block# of (*blk) */ + REG char *newptr; /* where new text starts in blk */ + BLK buf; /* holds chars from orig blk */ + BLK linebuf; /* holds part of line that didn't fit */ + BLK *following; /* the BLK following the last BLK */ + int i; + long l; + +#ifdef DEBUG2 + debout("add(%ld.%d, \"%s\")\n", markline(atmark), markidx(atmark), newtext); +#endif +#ifdef lint + buf.c[0] = 0; +#endif + /* if not adding anything, return now */ + if (!*newtext) + { + return; + } + + /* This is a change */ + changes++; + significant = TRUE; + + /* count the number of lines in the new text */ + for (scan = newtext, lastpart = addlines = 0; *scan; ) + { + if (*scan++ == '\n') + { + addlines++; + lastpart = 0; + } + else + { + lastpart++; + } + } + + /* Reporting... */ + if (lastpart == 0 && markidx(atmark) == 0) + { + rptlines = addlines; + rptlabel = "added"; + } + + /* extract the line# from atmark */ + l = markline(atmark); + + /* supply clues to the redraw module */ + if ((markidx(atmark) == 0 && lastpart == 0) || addlines == 0) + { + redrawrange(l, l, l + addlines); + } + else + { + /* make sure the last line gets redrawn -- it was + * split, so its appearance has changed + */ + redrawrange(l, l + 1L, l + addlines + 1L); + } + + /* adjust marks 'a through 'z and '' as needed */ + for (i = 0; i < NMARKS; i++) + { + if (mark[i] < atmark) + { + /* earlier line, or earlier in same line: no change */ + continue; + } + else if (markline(mark[i]) > l) + { + /* later line: move down a whole number of lines */ + mark[i] += MARK_AT_LINE(addlines); + } + else + { + /* later in same line */ + if (addlines > 0) + { + /* multi-line add, which split this line: + * move down, and possibly left or right, + * depending on where the split was and how + * much text was inserted after the last \n + */ + mark[i] += MARK_AT_LINE(addlines) + lastpart - markidx(atmark); + } + else + { + /* totally within this line: move right */ + mark[i] += lastpart; + } + } + } + + /* get the block to be modified */ + for (blkno = 1; lnum[blkno] < l && lnum[blkno + 1] < INFINITY; blkno++) + { + } + blk = blkget(blkno); + buf = *blk; + + /* figure out where the new text starts */ + for (newptr = buf.c, l = markline(atmark) - lnum[blkno - 1] - 1; + l > 0; + l--) + { + while (*newptr++ != '\n') + { + } + } + newptr += markidx(atmark); + + /* keep start of old block */ + build = blk->c + (int)(newptr - buf.c); + + /* fill this block (or blocks) from the newtext string */ + while (*newtext) + { + while (*newtext && build < blk->c + BLKSIZE - 1) + { + *build++ = *newtext++; + } + if (*newtext) + { + /* save the excess */ + for (scan = linebuf.c + BLKSIZE; + build > blk->c && build[-1] != '\n'; + ) + { + *--scan = *--build; + } + + /* write the block */ + while (build < blk->c + BLKSIZE) + { + *build++ = '\0'; + } + blkdirty(blk); + + /* add another block */ + blkno++; + blk = blkadd(blkno); + + /* copy in the excess from last time */ + for (build = blk->c; scan < linebuf.c + BLKSIZE; ) + { + *build++ = *scan++; + } + } + } + + /* fill this block(s) from remainder of orig block */ + while (newptr < buf.c + BLKSIZE && *newptr) + { + while (newptr < buf.c + BLKSIZE + && *newptr + && build < blk->c + BLKSIZE - 1) + { + *build++ = *newptr++; + } + if (newptr < buf.c + BLKSIZE && *newptr) + { + /* save the excess */ + for (scan = linebuf.c + BLKSIZE; + build > blk->c && build[-1] != '\n'; + ) + { + *--scan = *--build; + } + + /* write the block */ + while (build < blk->c + BLKSIZE) + { + *build++ = '\0'; + } + blkdirty(blk); + + /* add another block */ + blkno++; + blk = blkadd(blkno); + + /* copy in the excess from last time */ + for (build = blk->c; scan < linebuf.c + BLKSIZE; ) + { + *build++ = *scan++; + } + } + } + + /* see if we can combine our last block with the following block */ + if (lnum[blkno] < nlines && lnum[blkno + 1] - lnum[blkno] < (BLKSIZE >> 6)) + { + /* hey, we probably can! Get the following block & see... */ + following = blkget(blkno + 1); + if (strlen(following->c) + (build - blk->c) < BLKSIZE - 1) + { + /* we can! Copy text from following to blk */ + for (scan = following->c; *scan; ) + { + *build++ = *scan++; + } + while (build < blk->c + BLKSIZE) + { + *build++ = '\0'; + } + blkdirty(blk); + + /* pretend the following was the last blk */ + blk = following; + build = blk->c; + } + } + + /* that last block is dirty by now */ + while (build < blk->c + BLKSIZE) + { + *build++ = '\0'; + } + blkdirty(blk); +} + + +/* change the text of a file */ +void change(frommark, tomark, newtext) + MARK frommark, tomark; + char *newtext; +{ + int i; + long l; + char *text; + BLK *blk; + +#ifdef DEBUG2 + debout("change(%ld.%d, %ld.%d, \"%s\")\n", markline(frommark), markidx(frommark), markline(tomark), markidx(tomark), newtext); +#endif + + /* optimize for single-character replacement */ + if (frommark + 1 == tomark && newtext[0] && !newtext[1] && newtext[0] != '\n') + { + /* find the block containing frommark */ + l = markline(frommark); + for (i = 1; lnum[i] < l; i++) + { + } + + /* get the block */ + blk = blkget(i); + + /* find the line within the block */ + for (text = blk->c, i = l - lnum[i - 1] - 1; i > 0; text++) + { + if (*text == '\n') + { + i--; + } + } + + /* replace the char */ + text += markidx(frommark); + if (*text == newtext[0]) + { + /* no change was needed - same char */ + return; + } + else if (*text != '\n') + { + /* This is a change */ + changes++; + significant = TRUE; + ChangeText + { + *text = newtext[0]; + blkdirty(blk); + } + redrawrange(markline(frommark), markline(tomark), markline(frommark)); + return; + } + /* else it is a complex change involving newline... */ + } + + /* couldn't optimize, so do delete & add */ + ChangeText + { + delete(frommark, tomark); + add(frommark, newtext); + rptlabel = "changed"; + } +} diff --git a/commands/elvis/move1.c b/commands/elvis/move1.c new file mode 100755 index 000000000..f57ab9a0a --- /dev/null +++ b/commands/elvis/move1.c @@ -0,0 +1,585 @@ +/* move1.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains most movement functions */ + +#include "config.h" +#include "vi.h" +#include "ctype.h" + +MARK m_updnto(m, cnt, cmd) + MARK m; /* movement is relative to this mark */ + long cnt; /* a numeric argument */ + char cmd; /* the command character */ +{ + DEFAULT(cmd == 'G' ? nlines : 1L); + + /* move up or down 'cnt' lines */ + switch (cmd) + { + case ctrl('P'): + case '-': + case 'k': + m -= MARK_AT_LINE(cnt); + break; + + case 'G': + if (cnt < 1L || cnt > nlines) + { + msg("Only %ld lines", nlines); + return MARK_UNSET; + } + m = MARK_AT_LINE(cnt); + break; + + case '_': + cnt--; + /* fall through... */ + + default: + m += MARK_AT_LINE(cnt); + } + + /* if that left us screwed up, then fail */ + if (m < MARK_FIRST || markline(m) > nlines) + { + return MARK_UNSET; + } + + return m; +} + +/*ARGSUSED*/ +MARK m_right(m, cnt, key, prevkey) + MARK m; /* movement is relative to this mark */ + long cnt; /* a numeric argument */ + int key; /* movement keystroke */ + int prevkey;/* operator keystroke, or 0 if none */ +{ + int idx; /* index of the new cursor position */ + + DEFAULT(1); + + /* If used with an operator, then move 1 less character, since the 'l' + * command includes the character that it moves onto. + */ + if (prevkey != '\0') + { + cnt--; + } + + /* move to right, if that's OK */ + pfetch(markline(m)); + idx = markidx(m) + cnt; + if (idx < plen) + { + m += cnt; + } + else + { + return MARK_UNSET; + } + + return m; +} + +/*ARGSUSED*/ +MARK m_left(m, cnt) + MARK m; /* movement is relative to this mark */ + long cnt; /* a numeric argument */ +{ + DEFAULT(1); + + /* move to the left, if that's OK */ + if (markidx(m) >= cnt) + { + m -= cnt; + } + else + { + return MARK_UNSET; + } + + return m; +} + +/*ARGSUSED*/ +MARK m_tocol(m, cnt, cmd) + MARK m; /* movement is relative to this mark */ + long cnt; /* a numeric argument */ + int cmd; /* either ctrl('X') or '|' */ +{ + char *text; /* text of the line */ + int col; /* column number */ + int idx; /* index into the line */ + + + /* if doing ^X, then adjust for sideways scrolling */ + if (cmd == ctrl('X')) + { + DEFAULT(*o_columns & 0xff); + cnt += leftcol; + } + else + { + DEFAULT(1); + } + + /* internally, columns are numbered 0..COLS-1, not 1..COLS */ + cnt--; + + /* if 0, that's easy */ + if (cnt == 0) + { + m &= ~(BLKSIZE - 1); + return m; + } + + /* find that column within the line */ + pfetch(markline(m)); + text = ptext; + for (col = idx = 0; col < cnt && *text; text++, idx++) + { + if (*text == '\t' && !*o_list) + { + col += *o_tabstop; + col -= col % *o_tabstop; + } + else if (UCHAR(*text) < ' ' || *text == '\177') + { + col += 2; + } +#ifndef NO_CHARATTR + else if (text[0] == '\\' && text[1] == 'f' && text[2] && *o_charattr) + { + text += 2; /* plus one more as part of for loop */ + } +#endif + else + { + col++; + } + } + if (!*text) + { + /* the desired column was past the end of the line, so + * act like the user pressed "$" instead. + */ + return m | (BLKSIZE - 1); + } + else + { + m = (m & ~(BLKSIZE - 1)) + idx; + } + return m; +} + +/*ARGSUSED*/ +MARK m_front(m, cnt) + MARK m; /* movement is relative to this mark */ + long cnt; /* a numeric argument (ignored) */ +{ + char *scan; + + /* move to the first non-whitespace character */ + pfetch(markline(m)); + scan = ptext; + m &= ~(BLKSIZE - 1); + while (*scan == ' ' || *scan == '\t') + { + scan++; + m++; + } + + return m; +} + +/*ARGSUSED*/ +MARK m_rear(m, cnt) + MARK m; /* movement is relative to this mark */ + long cnt; /* a numeric argument (ignored) */ +{ + /* Try to move *EXTREMELY* far to the right. It is fervently hoped + * that other code will convert this to a more reasonable MARK before + * anything tries to actually use it. (See adjmove() in vi.c) + */ + return m | (BLKSIZE - 1); +} + +#ifndef NO_SENTENCE +static int isperiod(ptr) + char *ptr; /* pointer to possible sentence-ender */ +{ + /* if not '.', '?', or '!', then it isn't a sentence ender */ + if (*ptr != '.' && *ptr != '?' && *ptr != '!') + { + return FALSE; + } + + /* skip any intervening ')', ']', or '"' characters */ + do + { + ptr++; + } while (*ptr == ')' || *ptr == ']' || *ptr == '"'); + + /* do we have two spaces or EOL? */ + if (!*ptr || ptr[0] == ' ' && ptr[1] == ' ') + { + return TRUE; + } + return FALSE; +} + +/*ARGSUSED*/ +MARK m_sentence(m, cnt, cmd) + MARK m; /* movement is relative to this mark */ + long cnt; /* a numeric argument */ + int cmd; /* either '(' or ')' */ +{ + REG char *text; + REG long l; + + DEFAULT(1); + + /* If '(' command, then move back one word, so that if we hit '(' at + * the start of a sentence we don't simply stop at the end of the + * previous sentence and bounce back to the start of this one again. + */ + if (cmd == '(') + { + m = m_bword(m, 1L, 'b'); + if (!m) + { + return m; + } + } + + /* get the current line */ + l = markline(m); + pfetch(l); + text = ptext + markidx(m); + + /* for each requested sentence... */ + while (cnt-- > 0) + { + /* search forward for one of [.?!] followed by spaces or EOL */ + do + { + if (cmd == ')') + { + /* move forward, wrap at end of line */ + if (!text[0]) + { + if (l >= nlines) + { + return MARK_UNSET; + } + l++; + pfetch(l); + text = ptext; + } + else + { + text++; + } + } + else + { + /* move backward, wrap at beginning of line */ + if (text == ptext) + { + do + { + if (l <= 1) + { + return MARK_FIRST; + } + l--; + pfetch(l); + } while (!*ptext); + text = ptext + plen - 1; + } + else + { + text--; + } + } + } while (!isperiod(text)); + } + + /* construct a mark for this location */ + m = buildmark(text); + + /* move forward to the first word of the next sentence */ + m = m_fword(m, 1L, 'w', '\0'); + + return m; +} +#endif + +MARK m_paragraph(m, cnt, cmd) + MARK m; /* movement is relative to this mark */ + long cnt; /* a numeric argument */ + int cmd; /* either '{' or '}' */ +{ + char *text; /* text of the current line */ + char *pscn; /* used to scan thru value of "paragraphs" option */ + long l, ol; /* current line number, original line number */ + int dir; /* -1 if we're moving up, or 1 if down */ + char col0; /* character to expect in column 0 */ +#ifndef NO_SENTENCE +# define SENTENCE(x) (x) + char *list; /* either o_sections or o_paragraph */ +#else +# define SENTENCE(x) +#endif + + DEFAULT(1); + + /* set the direction, based on the command */ + switch (cmd) + { + case '{': + dir = -1; + col0 = '\0'; + SENTENCE(list = o_paragraphs); + break; + + case '}': + dir = 1; + col0 = '\0'; + SENTENCE(list = o_paragraphs); + break; + + case '[': + if (getkey(0) != '[') + { + return MARK_UNSET; + } + dir = -1; + col0 = '{'; + SENTENCE(list = o_sections); + break; + + case ']': + if (getkey(0) != ']') + { + return MARK_UNSET; + } + dir = 1; + col0 = '{'; + SENTENCE(list = o_sections); + break; + } + ol = l = markline(m); + + /* for each paragraph that we want to travel through... */ + while (l > 0 && l <= nlines && cnt-- > 0) + { + /* skip blank lines between paragraphs */ + while (l > 0 && l <= nlines && col0 == *(text = fetchline(l))) + { + l += dir; + } + + /* skip non-blank lines that aren't paragraph separators + */ + do + { +#ifndef NO_SENTENCE + if (*text == '.' && l != ol) + { + for (pscn = list; pscn[0] && pscn[1]; pscn += 2) + { + if (pscn[0] == text[1] && pscn[1] == text[2]) + { + pscn = (char *)0; + goto BreakBreak; + } + } + } +#endif + l += dir; + } while (l > 0 && l <= nlines && col0 != *(text = fetchline(l))); +BreakBreak: ; + } + + if (l > nlines) + { + m = MARK_LAST; + } + else if (l <= 0) + { + m = MARK_FIRST; + } + else + { + m = MARK_AT_LINE(l); + } + return m; +} + + +/*ARGSUSED*/ +MARK m_match(m, cnt) + MARK m; /* movement is relative to this mark */ + long cnt; /* a numeric argument (normally 0) */ +{ + long l; + REG char *text; + REG char match; + REG char nest; + REG int count; + +#ifndef NO_EXTENSIONS + /* if we're given a number, then treat it as a percentage of the file */ + if (cnt > 0) + { + /* make sure it is a reasonable number */ + if (cnt > 100) + { + msg("can only be from 1%% to 100%%"); + return MARK_UNSET; + } + + /* return the appropriate line number */ + l = (nlines - 1L) * cnt / 100L + 1L; + return MARK_AT_LINE(l); + } +#endif /* undef NO_EXTENSIONS */ + + /* get the current line */ + l = markline(m); + pfetch(l); + text = ptext + markidx(m); + + /* search forward within line for one of "[](){}" */ + for (match = '\0'; !match && *text; text++) + { + /* tricky way to recognize 'em in ASCII */ + nest = *text; + if ((nest & 0xdf) == ']' || (nest & 0xdf) == '[') + { + match = nest ^ ('[' ^ ']'); + } + else if ((nest & 0xfe) == '(') + { + match = nest ^ ('(' ^ ')'); + } + else + { + match = 0; + } + } + if (!match) + { + return MARK_UNSET; + } + text--; + + /* search forward or backward for match */ + if (match == '(' || match == '[' || match == '{') + { + /* search backward */ + for (count = 1; count > 0; ) + { + /* wrap at beginning of line */ + if (text == ptext) + { + do + { + if (l <= 1L) + { + return MARK_UNSET; + } + l--; + pfetch(l); + } while (!*ptext); + text = ptext + plen - 1; + } + else + { + text--; + } + + /* check the char */ + if (*text == match) + count--; + else if (*text == nest) + count++; + } + } + else + { + /* search forward */ + for (count = 1; count > 0; ) + { + /* wrap at end of line */ + if (!*text) + { + if (l >= nlines) + { + return MARK_UNSET; + } + l++; + pfetch(l); + text = ptext; + } + else + { + text++; + } + + /* check the char */ + if (*text == match) + count--; + else if (*text == nest) + count++; + } + } + + /* construct a mark for this place */ + m = buildmark(text); + return m; +} + +/*ARGSUSED*/ +MARK m_tomark(m, cnt, key) + MARK m; /* movement is relative to this mark */ + long cnt; /* (ignored) */ + int key; /* keystroke - the mark to move to */ +{ + /* mark '' is a special case */ + if (key == '\'' || key == '`') + { + if (mark[26] == MARK_UNSET) + { + return MARK_FIRST; + } + else + { + return mark[26]; + } + } + + /* if not a valid mark number, don't move */ + if (key < 'a' || key > 'z') + { + return MARK_UNSET; + } + + /* return the selected mark -- may be MARK_UNSET */ + if (!mark[key - 'a']) + { + msg("mark '%c is unset", key); + } + return mark[key - 'a']; +} + diff --git a/commands/elvis/move2.c b/commands/elvis/move2.c new file mode 100755 index 000000000..b664153cb --- /dev/null +++ b/commands/elvis/move2.c @@ -0,0 +1,291 @@ +/* move2.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This function contains the movement functions that perform RE searching */ + +#include "config.h" +#include "vi.h" +#include "regexp.h" + +extern long atol(); + +static regexp *re; /* compiled version of the pattern to search for */ +static prevsf; /* boolean: previous search direction was forward? */ + +#ifndef NO_EXTENSIONS +/*ARGSUSED*/ +MARK m_wsrch(word, m, cnt) + char *word; /* the word to search for */ + MARK m; /* the starting point */ + int cnt; /* ignored */ +{ + char buffer[30]; + + /* wrap \< and \> around the word */ + strcpy(buffer, "/\\<"); + strcat(buffer, word); + strcat(buffer, "\\>"); + + /* show the searched-for word on the bottom line */ + move(LINES - 1, 0); + qaddstr(buffer); + clrtoeol(); + refresh(); + + /* search for the word */ + return m_fsrch(m, buffer); +} +#endif + +MARK m_nsrch(m) + MARK m; /* where to start searching */ +{ + if (prevsf) + { + m = m_fsrch(m, (char *)0); + prevsf = TRUE; + } + else + { + m = m_bsrch(m, (char *)0); + prevsf = FALSE; + } + return m; +} + +MARK m_Nsrch(m) + MARK m; /* where to start searching */ +{ + if (prevsf) + { + m = m_bsrch(m, (char *)0); + prevsf = TRUE; + } + else + { + m = m_fsrch(m, (char *)0); + prevsf = FALSE; + } + return m; +} + +MARK m_fsrch(m, ptrn) + MARK m; /* where to start searching */ + char *ptrn; /* pattern to search for */ +{ + long l; /* line# of line to be searched */ + char *line; /* text of line to be searched */ + int wrapped;/* boolean: has our search wrapped yet? */ + int pos; /* where we are in the line */ +#ifndef CRUNCH + long delta = INFINITY;/* line offset, for things like "/foo/+1" */ +#endif + + /* remember: "previous search was forward" */ + prevsf = TRUE; + + if (ptrn && *ptrn) + { + /* locate the closing '/', if any */ + line = parseptrn(ptrn); +#ifndef CRUNCH + if (*line) + { + delta = atol(line); + } +#endif + ptrn++; + + /* free the previous pattern */ + if (re) free(re); + + /* compile the pattern */ + re = regcomp(ptrn); + if (!re) + { + return MARK_UNSET; + } + } + else if (!re) + { + msg("No previous expression"); + return MARK_UNSET; + } + + /* search forward for the pattern */ + pos = markidx(m) + 1; + pfetch(markline(m)); + if (pos >= plen) + { + pos = 0; + m = (m | (BLKSIZE - 1)) + 1; + } + wrapped = FALSE; + for (l = markline(m); l != markline(m) + 1 || !wrapped; l++) + { + /* wrap search */ + if (l > nlines) + { + /* if we wrapped once already, then the search failed */ + if (wrapped) + { + break; + } + + /* else maybe we should wrap now? */ + if (*o_wrapscan) + { + l = 0; + wrapped = TRUE; + continue; + } + else + { + break; + } + } + + /* get this line */ + line = fetchline(l); + + /* check this line */ + if (regexec(re, &line[pos], (pos == 0))) + { + /* match! */ + if (wrapped && *o_warn) + msg("(wrapped)"); +#ifndef CRUNCH + if (delta != INFINITY) + { + l += delta; + if (l < 1 || l > nlines) + { + msg("search offset too big"); + return MARK_UNSET; + } + force_flags = LNMD|INCL; + return MARK_AT_LINE(l); + } +#endif + return MARK_AT_LINE(l) + (int)(re->startp[0] - line); + } + pos = 0; + } + + /* not found */ + msg(*o_wrapscan ? "Not found" : "Hit bottom without finding RE"); + return MARK_UNSET; +} + +MARK m_bsrch(m, ptrn) + MARK m; /* where to start searching */ + char *ptrn; /* pattern to search for */ +{ + long l; /* line# of line to be searched */ + char *line; /* text of line to be searched */ + int wrapped;/* boolean: has our search wrapped yet? */ + int pos; /* last acceptable idx for a match on this line */ + int last; /* remembered idx of the last acceptable match on this line */ + int try; /* an idx at which we strat searching for another match */ +#ifndef CRUNCH + long delta = INFINITY;/* line offset, for things like "/foo/+1" */ +#endif + + /* remember: "previous search was not forward" */ + prevsf = FALSE; + + if (ptrn && *ptrn) + { + /* locate the closing '?', if any */ + line = parseptrn(ptrn); +#ifndef CRUNCH + if (*line) + { + delta = atol(line); + } +#endif + ptrn++; + + /* free the previous pattern, if any */ + if (re) free(re); + + /* compile the pattern */ + re = regcomp(ptrn); + if (!re) + { + return MARK_UNSET; + } + } + else if (!re) + { + msg("No previous expression"); + return MARK_UNSET; + } + + /* search backward for the pattern */ + pos = markidx(m); + wrapped = FALSE; + for (l = markline(m); l != markline(m) - 1 || !wrapped; l--) + { + /* wrap search */ + if (l < 1) + { + if (*o_wrapscan) + { + l = nlines + 1; + wrapped = TRUE; + continue; + } + else + { + break; + } + } + + /* get this line */ + line = fetchline(l); + + /* check this line */ + if (regexec(re, line, 1) && (int)(re->startp[0] - line) < pos) + { + /* match! now find the last acceptable one in this line */ + do + { + last = (int)(re->startp[0] - line); + try = (int)(re->endp[0] - line); + } while (try > 0 + && regexec(re, &line[try], FALSE) + && (int)(re->startp[0] - line) < pos); + + if (wrapped && *o_warn) + msg("(wrapped)"); +#ifndef CRUNCH + if (delta != INFINITY) + { + l += delta; + if (l < 1 || l > nlines) + { + msg("search offset too big"); + return MARK_UNSET; + } + force_flags = LNMD|INCL; + return MARK_AT_LINE(l); + } +#endif + return MARK_AT_LINE(l) + last; + } + pos = BLKSIZE; + } + + /* not found */ + msg(*o_wrapscan ? "Not found" : "Hit top without finding RE"); + return MARK_UNSET; +} + diff --git a/commands/elvis/move3.c b/commands/elvis/move3.c new file mode 100755 index 000000000..a9e46ea8a --- /dev/null +++ b/commands/elvis/move3.c @@ -0,0 +1,163 @@ +/* move3.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains movement functions that perform character searches */ + +#include "config.h" +#include "vi.h" + +#ifndef NO_CHARSEARCH +static MARK (*prevfwdfn)(); /* function to search in same direction */ +static MARK (*prevrevfn)(); /* function to search in opposite direction */ +static char prev_key; /* sought cvhar from previous [fFtT] */ + +MARK m__ch(m, cnt, cmd) + MARK m; /* current position */ + long cnt; + char cmd; /* command: either ',' or ';' */ +{ + MARK (*tmp)(); + + if (!prevfwdfn) + { + msg("No previous f, F, t, or T command"); + return MARK_UNSET; + } + + if (cmd == ',') + { + m = (*prevrevfn)(m, cnt, prev_key); + + /* Oops! we didn't want to change the prev*fn vars! */ + tmp = prevfwdfn; + prevfwdfn = prevrevfn; + prevrevfn = tmp; + + return m; + } + else + { + return (*prevfwdfn)(m, cnt, prev_key); + } +} + +/* move forward within this line to next occurrence of key */ +MARK m_fch(m, cnt, key) + MARK m; /* where to search from */ + long cnt; + char key; /* what to search for */ +{ + REG char *text; + + DEFAULT(1); + + prevfwdfn = m_fch; + prevrevfn = m_Fch; + prev_key = key; + + pfetch(markline(m)); + text = ptext + markidx(m); + while (cnt-- > 0) + { + do + { + m++; + text++; + } while (*text && *text != key); + } + if (!*text) + { + return MARK_UNSET; + } + return m; +} + +/* move backward within this line to previous occurrence of key */ +MARK m_Fch(m, cnt, key) + MARK m; /* where to search from */ + long cnt; + char key; /* what to search for */ +{ + REG char *text; + + DEFAULT(1); + + prevfwdfn = m_Fch; + prevrevfn = m_fch; + prev_key = key; + + pfetch(markline(m)); + text = ptext + markidx(m); + while (cnt-- > 0) + { + do + { + m--; + text--; + } while (text >= ptext && *text != key); + } + if (text < ptext) + { + return MARK_UNSET; + } + return m; +} + +/* move forward within this line almost to next occurrence of key */ +MARK m_tch(m, cnt, key) + MARK m; /* where to search from */ + long cnt; + char key; /* what to search for */ +{ + /* skip the adjacent char */ + pfetch(markline(m)); + if (plen <= markidx(m)) + { + return MARK_UNSET; + } + m++; + + m = m_fch(m, cnt, key); + if (m == MARK_UNSET) + { + return MARK_UNSET; + } + + prevfwdfn = m_tch; + prevrevfn = m_Tch; + + return m - 1; +} + +/* move backward within this line almost to previous occurrence of key */ +MARK m_Tch(m, cnt, key) + MARK m; /* where to search from */ + long cnt; + char key; /* what to search for */ +{ + /* skip the adjacent char */ + if (markidx(m) == 0) + { + return MARK_UNSET; + } + m--; + + m = m_Fch(m, cnt, key); + if (m == MARK_UNSET) + { + return MARK_UNSET; + } + + prevfwdfn = m_Tch; + prevrevfn = m_tch; + + return m + 1; +} +#endif diff --git a/commands/elvis/move4.c b/commands/elvis/move4.c new file mode 100755 index 000000000..a31ef881f --- /dev/null +++ b/commands/elvis/move4.c @@ -0,0 +1,211 @@ +/* move4.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains movement functions which are screen-relative */ + +#include "config.h" +#include "vi.h" + +/* This moves the cursor to a particular row on the screen */ +/*ARGSUSED*/ +MARK m_row(m, cnt, key) + MARK m; /* the cursor position */ + long cnt; /* the row we'll move to */ + int key; /* the keystroke of this move - H/L/M */ +{ + DEFAULT(1); + + /* calculate destination line based on key */ + cnt--; + switch (key) + { + case 'H': + cnt = topline + cnt; + break; + + case 'M': + cnt = topline + (LINES - 1) / 2; + break; + + case 'L': + cnt = botline - cnt; + break; + } + + /* return the mark of the destination line */ + return MARK_AT_LINE(cnt); +} + + +/* This function repositions the current line to show on a given row */ +MARK m_z(m, cnt, key) + MARK m; /* the cursor */ + long cnt; /* the line number we're repositioning */ + int key; /* key struck after the z */ +{ + long newtop; + int i; + + /* Which line are we talking about? */ + if (cnt < 0 || cnt > nlines) + { + return MARK_UNSET; + } + if (cnt) + { + m = MARK_AT_LINE(cnt); + newtop = cnt; + } + else + { + newtop = markline(m); + } + + /* allow a "window size" number to be entered */ + for (i = 0; key >= '0' && key <= '9'; key = getkey(0)) + { + i = i * 10 + key - '0'; + } +#ifndef CRUNCH + if (i > 0 && i <= LINES - 1) + { + *o_window = i; + wset = TRUE; + } +#else + /* the number is ignored if -DCRUNCH */ +#endif + + /* figure out which line will have to be at the top of the screen */ + switch (key) + { + case '\n': +#if OSK + case '\l': +#else + case '\r': +#endif + case '+': + break; + + case '.': + case 'z': + newtop -= LINES / 2; + break; + + case '-': + newtop -= LINES - 1; + break; + + default: + return MARK_UNSET; + } + + /* make the new topline take effect */ + redraw(MARK_UNSET, FALSE); + if (newtop >= 1) + { + topline = newtop; + } + else + { + topline = 1L; + } + redrawrange(0L, INFINITY, INFINITY); + + /* The cursor doesn't move */ + return m; +} + + +/* This function scrolls the screen. It does this by calling redraw() with + * an off-screen line as the argument. It will move the cursor if necessary + * so that the cursor is on the new screen. + */ +/*ARGSUSED*/ +MARK m_scroll(m, cnt, key) + MARK m; /* the cursor position */ + long cnt; /* for some keys: the number of lines to scroll */ + int key; /* keystroke that causes this movement */ +{ + MARK tmp; /* a temporary mark, used as arg to redraw() */ + + /* adjust cnt, and maybe *o_scroll, depending of key */ + switch (key) + { + case ctrl('F'): + case ctrl('B'): + DEFAULT(1); + redrawrange(0L, INFINITY, INFINITY); /* force complete redraw */ + cnt = cnt * (LINES - 1) - 2; /* keeps two old lines on screen */ + break; + + case ctrl('E'): + case ctrl('Y'): + DEFAULT(1); + break; + + case ctrl('U'): + case ctrl('D'): + if (cnt == 0) /* default */ + { + cnt = *o_scroll; + } + else + { + if (cnt > LINES - 1) + { + cnt = LINES - 1; + } + *o_scroll = cnt; + } + break; + } + + /* scroll up or down, depending on key */ + switch (key) + { + case ctrl('B'): + case ctrl('Y'): + case ctrl('U'): + cnt = topline - cnt; + if (cnt < 1L) + { + cnt = 1L; + m = MARK_FIRST; + } + tmp = MARK_AT_LINE(cnt) + markidx(m); + redraw(tmp, FALSE); + if (markline(m) > botline) + { + m = MARK_AT_LINE(botline); + } + break; + + case ctrl('F'): + case ctrl('E'): + case ctrl('D'): + cnt = botline + cnt; + if (cnt > nlines) + { + cnt = nlines; + m = MARK_LAST; + } + tmp = MARK_AT_LINE(cnt) + markidx(m); + redraw(tmp, FALSE); + if (markline(m) < topline) + { + m = MARK_AT_LINE(topline); + } + break; + } + + return m; +} diff --git a/commands/elvis/move5.c b/commands/elvis/move5.c new file mode 100755 index 000000000..aec5e785b --- /dev/null +++ b/commands/elvis/move5.c @@ -0,0 +1,256 @@ +/* move5.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains the word-oriented movement functions */ + +#include "config.h" +#include "ctype.h" +#include "vi.h" + +MARK m_fword(m, cnt, cmd, prevkey) + MARK m; /* movement is relative to this mark */ + long cnt; /* a numeric argument */ + int cmd; /* either 'w' or 'W' */ + int prevkey;/* previous command... if 'c' then exclude whitespace */ +{ + REG long l; + REG char *text; + REG int i; + + DEFAULT(1); + + l = markline(m); + pfetch(l); + text = ptext + markidx(m); + +#ifndef CRUNCH + /* As a special case, "cw" or "cW" on whitespace without a count + * treats the single whitespace character under the cursor as a word. + */ + if (cnt == 1L && prevkey == 'c' && isspace(*text)) + { + return m + 1L; + } +#endif + + while (cnt-- > 0) /* yes, ASSIGNMENT! */ + { + i = *text++; + + if (cmd == 'W') + { + /* include any non-whitespace */ + while (i && !isspace(i)) + { + i = *text++; + } + } + else if (isalnum(i) || i == '_') + { + /* include an alphanumeric word */ + while (i && isalnum(i)) + { + i = *text++; + } + } + else + { + /* include contiguous punctuation */ + while (i && !isalnum(i) && !isspace(i)) + { + i = *text++; + } + } + + /* if not part of "cw" or "cW" command... */ + if (prevkey != 'c' || cnt > 0) + { + /* include trailing whitespace */ + while (!i || isspace(i)) + { + /* did we hit the end of this line? */ + if (!i) + { + /* "dw" shouldn't delete newline after word */ + if (prevkey && cnt == 0) + { + break; + } + + /* move to next line, if there is one */ + l++; + if (l > nlines) + { + return MARK_UNSET; + } + pfetch(l); + text = ptext; + } + + i = *text++; + } + } + text--; + } + + /* if argument to operator, then back off 1 char since "w" and "W" + * include the last char in the affected text. + */ + if (prevkey) + { + text--; + } + + /* construct a MARK for this place */ + m = buildmark(text); + return m; +} + + +MARK m_bword(m, cnt, cmd) + MARK m; /* movement is relative to this mark */ + long cnt; /* a numeric argument */ + int cmd; /* either 'b' or 'B' */ +{ + REG long l; + REG char *text; + + DEFAULT(1); + + l = markline(m); + pfetch(l); + text = ptext + markidx(m); + while (cnt-- > 0) /* yes, ASSIGNMENT! */ + { + text--; + + /* include preceding whitespace */ + while (text < ptext || isspace(*text)) + { + /* did we hit the end of this line? */ + if (text < ptext) + { + /* move to preceding line, if there is one */ + l--; + if (l <= 0) + { + return MARK_UNSET; + } + pfetch(l); + text = ptext + plen - 1; + } + else + { + text--; + } + } + + if (cmd == 'B') + { + /* include any non-whitespace */ + while (text >= ptext && !isspace(*text)) + { + text--; + } + } + else if (isalnum(*text) || *text == '_') + { + /* include an alphanumeric word */ + while (text >= ptext && isalnum(*text)) + { + text--; + } + } + else + { + /* include contiguous punctuation */ + while (text >= ptext && !isalnum(*text) && !isspace(*text)) + { + text--; + } + } + text++; + } + + /* construct a MARK for this place */ + m = buildmark(text); + return m; +} + +MARK m_eword(m, cnt, cmd) + MARK m; /* movement is relative to this mark */ + long cnt; /* a numeric argument */ + int cmd; /* either 'e' or 'E' */ +{ + REG long l; + REG char *text; + REG int i; + + DEFAULT(1); + + l = markline(m); + pfetch(l); + text = ptext + markidx(m); + while (cnt-- > 0) /* yes, ASSIGNMENT! */ + { + if (*text) + text++; + i = *text++; + + /* include preceding whitespace */ + while (!i || isspace(i)) + { + /* did we hit the end of this line? */ + if (!i) + { + /* move to next line, if there is one */ + l++; + if (l > nlines) + { + return MARK_UNSET; + } + pfetch(l); + text = ptext; + } + + i = *text++; + } + + if (cmd == 'E') + { + /* include any non-whitespace */ + while (i && !isspace(i)) + { + i = *text++; + } + } + else if (isalnum(i) || i == '_') + { + /* include an alphanumeric word */ + while (i && isalnum(i)) + { + i = *text++; + } + } + else + { + /* include contiguous punctuation */ + while (i && !isalnum(i) && !isspace(i)) + { + i = *text++; + } + } + text -= 2; + } + + /* construct a MARK for this place */ + m = buildmark(text); + return m; +} diff --git a/commands/elvis/opts.c b/commands/elvis/opts.c new file mode 100755 index 000000000..0d72ba58d --- /dev/null +++ b/commands/elvis/opts.c @@ -0,0 +1,797 @@ +/* opts.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains the code that manages the run-time options -- The + * values that can be modified via the "set" command. + */ + +#include "config.h" +#include "vi.h" +#include "ctype.h" +#ifndef NULL +#define NULL (char *)0 +#endif +extern char *getenv(); + +/* maximum width to permit for strings, including ="" */ +#define MAXWIDTH 20 + +/* These are the default values of all options */ +char o_autoindent[1] = {FALSE}; +char o_autoprint[1] = {TRUE}; +char o_autotab[1] = {TRUE}; +char o_autowrite[1] = {FALSE}; +char o_columns[3] = {80, 32, 255}; +char o_directory[30] = TMPDIR; +char o_edcompatible[1] = {FALSE}; +char o_equalprg[80] = {"fmt"}; +char o_errorbells[1] = {TRUE}; +char o_exrefresh[1] = {TRUE}; +char o_ignorecase[1] = {FALSE}; +char o_keytime[3] = {1, 0, 50}; +char o_keywordprg[80] = {KEYWORDPRG}; +char o_lines[3] = {25, 2, 96}; +char o_list[1] = {FALSE}; +char o_number[1] = {FALSE}; +char o_readonly[1] = {FALSE}; +char o_remap[1] = {TRUE}; +char o_report[3] = {5, 1, 127}; +char o_scroll[3] = {12, 1, 127}; +char o_shell[60] = SHELL; +char o_shiftwidth[3] = {8, 1, 255}; +char o_sidescroll[3] = {8, 1, 40}; +char o_sync[1] = {NEEDSYNC}; +char o_tabstop[3] = {8, 1, 40}; +char o_term[30] = "?"; +char o_flash[1] = {TRUE}; +char o_warn[1] = {TRUE}; +char o_wrapscan[1] = {TRUE}; + +#ifndef CRUNCH +char o_beautify[1] = {FALSE}; +char o_exrc[1] = {FALSE}; +char o_mesg[1] = {TRUE}; +char o_more[1] = {TRUE}; +char o_novice[1] = {FALSE}; +char o_prompt[1] = {TRUE}; +char o_taglength[3] = {0, 0, 30}; +char o_terse[1] = {FALSE}; +char o_window[3] = {0, 1, 24}; +char o_wrapmargin[3] = {0, 0, 255}; +char o_writeany[1] = {FALSE}; +#endif + +#ifndef NO_ERRLIST +char o_cc[30] = {CC_COMMAND}; +char o_make[30] = {MAKE_COMMAND}; +#endif + +#ifndef NO_CHARATTR +char o_charattr[1] = {FALSE}; +#endif + +#ifndef NO_DIGRAPH +char o_digraph[1] = {FALSE}; +char o_flipcase[80] +# ifdef CS_IBMPC + = {"\207\200\201\232\202\220\204\216\206\217\221\222\224\231\244\245"} +# endif +# ifdef CS_LATIN1 + /* initialized by initopts() */ +# endif + ; +#endif + +#ifndef NO_SENTENCE +char o_hideformat[1] = {FALSE}; +#endif + +#ifndef NO_EXTENSIONS +char o_inputmode[1] = {FALSE}; +char o_ruler[1] = {FALSE}; +#endif + +#ifndef NO_MAGIC +char o_magic[1] = {TRUE}; +#endif + +#ifndef NO_MODELINES +char o_modelines[1] = {FALSE}; +#endif + +#ifndef NO_SENTENCE +char o_paragraphs[30] = "PPppIPLPQP"; +char o_sections[30] = "NHSHSSSEse"; +#endif + +#if MSDOS +char o_pcbios[1] = {TRUE}; +#endif + +#ifndef NO_SHOWMATCH +char o_showmatch[1] = {FALSE}; +#endif + +#ifndef NO_SHOWMODE +char o_smd[1] = {FALSE}; +#endif + + +/* The following describes the names & types of all options */ +#define BOOL 0 +#define NUM 1 +#define STR 2 +#define SET 0x01 /* this option has had its value altered */ +#define CANSET 0x02 /* this option can be set at any time */ +#define RCSET 0x06 /* this option can be set in a .exrc file only */ +#define NOSAVE 0x0a /* this option should never be saved by mkexrc */ +#define WSET 0x20 /* is this the "window" size option? */ +#define MR 0x40 /* does this option affect the way text is displayed? */ +struct +{ + char *name; /* name of an option */ + char *nm; /* short name of an option */ + char type; /* type of an option */ + char flags; /* boolean: has this option been set? */ + char *value; /* value */ +} + opts[] = +{ + /* name type flags value */ + { "autoindent", "ai", BOOL, CANSET, o_autoindent }, + { "autoprint", "ap", BOOL, CANSET, o_autoprint }, + { "autotab", "at", BOOL, CANSET, o_autotab }, + { "autowrite", "aw", BOOL, CANSET, o_autowrite }, +#ifndef CRUNCH + { "beautify", "bf", BOOL, CANSET, o_beautify }, +#endif +#ifndef NO_ERRLIST + { "cc", "cc", STR, CANSET, o_cc }, +#endif +#ifndef NO_CHARATTR + { "charattr", "ca", BOOL, CANSET|MR, o_charattr }, +#endif + { "columns", "co", NUM, SET|NOSAVE|MR, o_columns }, +#ifndef NO_DIGRAPH + { "digraph", "dig", BOOL, CANSET, o_digraph }, +#endif + { "directory", "dir", STR, RCSET, o_directory }, + { "edcompatible","ed", BOOL, CANSET, o_edcompatible }, + { "equalprg", "ep", STR, CANSET, o_equalprg }, + { "errorbells", "eb", BOOL, CANSET, o_errorbells }, +#ifndef CRUNCH + { "exrc", "exrc", BOOL, CANSET, o_exrc }, +#endif + { "exrefresh", "er", BOOL, CANSET, o_exrefresh }, + { "flash", "vbell",BOOL, CANSET, o_flash }, +#ifndef NO_DIGRAPH + { "flipcase", "fc", STR, CANSET, o_flipcase }, +#endif +#ifndef NO_SENTENCE + { "hideformat", "hf", BOOL, CANSET|MR, o_hideformat }, +#endif + { "ignorecase", "ic", BOOL, CANSET, o_ignorecase }, +#ifndef NO_EXTENSIONS + { "inputmode", "im", BOOL, CANSET, o_inputmode }, +#endif + { "keytime", "kt", NUM, CANSET, o_keytime }, + { "keywordprg", "kp", STR, CANSET, o_keywordprg }, + { "lines", "ls", NUM, SET|NOSAVE|MR, o_lines }, + { "list", "li", BOOL, CANSET|MR, o_list }, +#ifndef NO_MAGIC + { "magic", "ma", BOOL, CANSET, o_magic }, +#endif +#ifndef NO_ERRLIST + { "make", "mk", STR, CANSET, o_make }, +#endif +#ifndef CRUNCH + { "mesg", "me", BOOL, CANSET, o_mesg }, +#endif +#ifndef NO_MODELINES + { "modelines", "ml", BOOL, CANSET, o_modelines }, +#endif +#ifndef CRUNCH + { "more", "mo", BOOL, CANSET, o_more }, + { "novice", "nov", BOOL, CANSET, o_novice }, +#endif + { "number", "nu", BOOL, CANSET|MR, o_number }, +#ifndef NO_SENTENCE + { "paragraphs", "para", STR, CANSET, o_paragraphs }, +#endif +#if MSDOS + { "pcbios", "pc", BOOL, SET|NOSAVE, o_pcbios }, +#endif +#ifndef CRUNCH + { "prompt", "pr", BOOL, CANSET, o_prompt }, +#endif + { "readonly", "ro", BOOL, CANSET, o_readonly }, + { "remap", "remap",BOOL, CANSET, o_remap }, + { "report", "re", NUM, CANSET, o_report }, +#ifndef NO_EXTENSIONS + { "ruler", "ru", BOOL, CANSET, o_ruler }, +#endif + { "scroll", "sc", NUM, CANSET, o_scroll }, +#ifndef NO_SENTENCE + { "sections", "sect", STR, CANSET, o_sections }, +#endif + { "shell", "sh", STR, CANSET, o_shell }, +#ifndef NO_SHOWMATCH + { "showmatch", "sm", BOOL, CANSET, o_showmatch }, +#endif +#ifndef NO_SHOWMODE + { "showmode", "smd", BOOL, CANSET, o_smd }, +#endif + { "shiftwidth", "sw", NUM, CANSET, o_shiftwidth }, + { "sidescroll", "ss", NUM, CANSET, o_sidescroll }, + { "sync", "sy", BOOL, CANSET, o_sync }, + { "tabstop", "ts", NUM, CANSET|MR, o_tabstop }, +#ifndef CRUNCH + { "taglength", "tl", NUM, CANSET, o_taglength }, +#endif + { "term", "te", STR, SET, o_term }, +#ifndef CRUNCH + { "terse", "tr", BOOL, CANSET, o_terse }, + { "timeout", "to", BOOL, CANSET, o_keytime }, +#endif +#ifndef CRUNCH + { "window", "wi", NUM, CANSET|MR|WSET, o_window }, + { "wrapmargin", "wm", NUM, CANSET, o_wrapmargin }, +#endif + { "wrapscan", "ws", BOOL, CANSET, o_wrapscan }, +#ifndef CRUNCH + { "writeany", "wr", BOOL, CANSET, o_writeany }, +#endif + { NULL, NULL, 0, CANSET, NULL } +}; + + +/* This function initializes certain options from environment variables, etc. */ +void initopts() +{ + char *val; + int i; + + /* set some stuff from environment variables */ +#if MSDOS + if (val = getenv("COMSPEC")) /* yes, ASSIGNMENT! */ +#else + if (val = getenv("SHELL")) /* yes, ASSIGNMENT! */ +#endif + { + strcpy(o_shell, val); + } + + strcpy(o_term, termtype); +#if MSDOS + if (strcmp(termtype, "pcbios")) + { + o_pcbios[0] = FALSE; + } + else + { + o_pcbios[0] = TRUE; + } +#endif + +#if AMIGA || MSDOS || TOS + if ((val = getenv("TMP")) /* yes, ASSIGNMENT! */ + || (val = getenv("TEMP"))) + strcpy(o_directory, val); +#endif + +#ifndef CRUNCH + if ((val = getenv("LINES")) && atoi(val) > 4) /* yes, ASSIGNMENT! */ + { + LINES = atoi(val); + } + if ((val = getenv("COLUMNS")) && atoi(val) > 30) /* yes, ASSIGNMENT! */ + { + COLS = atoi(val); + } +#endif + *o_lines = LINES; + *o_columns = COLS; + *o_scroll = LINES / 2 - 1; +#ifndef CRUNCH + if (o_window[0] == 0) + { + o_window[0] = o_window[2] = *o_lines; + } +#endif + + /* disable the flash option if we don't know how to do a flash */ + if (!has_VB) + { + for (i = 0; opts[i].value != o_flash; i++) + { + } + opts[i].flags &= ~CANSET; + *o_flash = FALSE; + } + +#ifndef NO_DIGRAPH +# ifdef CS_LATIN1 + for (i = 0, val = o_flipcase; i < 32; i++) + { + /* leave out the multiply/divide symbols */ + if (i == 23) + continue; + + /* add lower/uppercase pair */ + *val++ = i + 0xe0; + *val++ = i + 0xc0; + } + *val = '\0'; +# endif /* CS_LATIN1 */ + + /* initialize the ctype package */ + _ct_init(o_flipcase); +#else + _ct_init(""); +#endif /* not NO_DIGRAPH */ +} + +/* This function lists the current values of all options */ +void dumpopts(all) + int all; /* boolean: dump all options, or just set ones? */ +{ +#ifndef NO_OPTCOLS + int i, j, k; + char nbuf[4]; /* used for converting numbers to ASCII */ + int widths[5]; /* width of each column, including gap */ + int ncols; /* number of columns */ + int nrows; /* number of options per column */ + int nset; /* number of options to be output */ + int width; /* width of a particular option */ + int todump[60]; /* indicies of options to be dumped */ + + /* step 1: count the number of set options */ + for (nset = i = 0; opts[i].name; i++) + { + if (all || (opts[i].flags & SET)) + { + todump[nset++] = i; + } + } + + /* step two: try to use as many columns as possible */ + for (ncols = (nset > 5 ? 5 : nset); ncols > 1; ncols--) + { + /* how many would go in this column? */ + nrows = (nset + ncols - 1) / ncols; + + /* figure out the width of each column */ + for (i = 0; i < ncols; i++) + { + widths[i] = 0; + for (j = 0, k = nrows * i; j < nrows && k < nset; j++, k++) + { + /* figure out the width of a particular option */ + switch (opts[todump[k]].type) + { + case BOOL: + if (!*opts[todump[k]].value) + width = 2; + else + width = 0; + break; + + case STR: + width = 3 + strlen(opts[todump[k]].value); + if (width > MAXWIDTH) + width = MAXWIDTH; + break; + + case NUM: + width = 4; + break; + } + width += strlen(opts[todump[k]].name); + + /* if this is the widest so far, widen col */ + if (width > widths[i]) + { + widths[i] = width; + } + } + + } + + /* if the total width is narrow enough, then use it */ + for (width = -2, i = 0; i < ncols; i++) + { + width += widths[i] + 2; + } + if (width < COLS - 1) + { + break; + } + } + + /* step 3: output the columns */ + nrows = (nset + ncols - 1) / ncols; + for (i = 0; i < nrows; i++) + { + for (j = 0; j < ncols; j++) + { + /* if we hit the end of the options, quit */ + k = i + j * nrows; + if (k >= nset) + { + break; + } + + /* output this option's value */ + width = 0; + switch (opts[todump[k]].type) + { + case BOOL: + if (!*opts[todump[k]].value) + { + qaddch('n'); + qaddch('o'); + width = 2; + } + qaddstr(opts[todump[k]].name); + width += strlen(opts[todump[k]].name); + break; + + case NUM: + sprintf(nbuf, "%-3d", UCHAR(*opts[todump[k]].value)); + qaddstr(opts[todump[k]].name); + qaddch('='); + qaddstr(nbuf); + width = 4 + strlen(opts[todump[k]].name); + break; + + case STR: + qaddstr(opts[todump[k]].name); + qaddch('='); + qaddch('"'); + strcpy(tmpblk.c, opts[todump[k]].value); + width = 3 + strlen(tmpblk.c); + if (width > MAXWIDTH) + { + width = MAXWIDTH; + strcpy(tmpblk.c + MAXWIDTH - 6, "..."); + } + qaddstr(tmpblk.c); + qaddch('"'); + width += strlen(opts[todump[k]].name); + break; + } + + /* pad the field to the correct size */ + if (k + nrows <= nset) + { + while (width < widths[j] + 2) + { + qaddch(' '); + width++; + } + } + } + addch('\n'); + exrefresh(); + } +#else + int i; + int col; + char nbuf[4]; + + for (i = col = 0; opts[i].name; i++) + { + /* if not set and not all, ignore this option */ + if (!all && !(opts[i].flags & SET)) + { + continue; + } + + /* align this option in one of the columns */ + if (col > 52) + { + addch('\n'); + col = 0; + } + else if (col > 26) + { + while (col < 52) + { + qaddch(' '); + col++; + } + } + else if (col > 0) + { + while (col < 26) + { + qaddch(' '); + col++; + } + } + + switch (opts[i].type) + { + case BOOL: + if (!*opts[i].value) + { + qaddch('n'); + qaddch('o'); + col += 2; + } + qaddstr(opts[i].name); + col += strlen(opts[i].name); + break; + + case NUM: + sprintf(nbuf, "%-3d", UCHAR(*opts[i].value)); + qaddstr(opts[i].name); + qaddch('='); + qaddstr(nbuf); + col += 4 + strlen(opts[i].name); + break; + + case STR: + qaddstr(opts[i].name); + qaddch('='); + qaddch('"'); + qaddstr(opts[i].value); + qaddch('"'); + col += 3 + strlen(opts[i].name) + strlen(opts[i].value); + break; + } + exrefresh(); + } + if (col > 0) + { + addch('\n'); + exrefresh(); + } +#endif +} + +#ifndef NO_MKEXRC +/* This function saves the current configuration of options to a file */ +void saveopts(fd) + int fd; /* file descriptor to write to */ +{ + int i; + char buf[256], *pos; + + /* write each set options */ + for (i = 0; opts[i].name; i++) + { + /* if unset or unsettable, ignore this option */ + if ((opts[i].flags & (SET|CANSET|NOSAVE)) != (SET|CANSET)) + { + continue; + } + + strcpy(buf, "set "); + pos = &buf[4]; + switch (opts[i].type) + { + case BOOL: + if (!*opts[i].value) + { + *pos++='n'; + *pos++='o'; + } + strcpy(pos, opts[i].name); + strcat(pos, "\n"); + break; + + case NUM: + sprintf(pos, "%s=%-3d\n", opts[i].name, *opts[i].value & 0xff); + break; + + case STR: + sprintf(pos, "%s=\"%s\"\n", opts[i].name, opts[i].value); + break; + } + twrite(fd, buf, (unsigned)strlen(buf)); + } +} +#endif + + +/* This function changes the values of one or more options. */ +void setopts(assignments) + char *assignments; /* a string containing option assignments */ +{ + char *name; /* name of variable in assignments */ + char *value; /* value of the variable */ + char *scan; /* used for moving through strings */ + char *build; /* used for copying chars from "scan" */ + char *prefix; /* pointer to "neg" or "no" at front of a boolean */ + int quote; /* boolean: inside '"' quotes? */ + int i, j; + +#ifndef CRUNCH + /* reset the upper limit of "window" option to lines-1 */ + *o_window = *o_lines - 1; +#endif + + /* for each assignment... */ + for (name = assignments; *name; ) + { + /* skip whitespace */ + if (*name == ' ' || *name == '\t') + { + name++; + continue; + } + + /* after the name, find the value (if any) */ + for (scan = name; isalnum(*scan); scan++) + { + } + if (*scan == '=') + { + *scan++ = '\0'; + value = build = scan; + for (quote = FALSE; *scan && (quote || !isspace(*scan)); scan++) + { + if (*scan == '"') + { + quote = !quote; + } + else if (*scan == '\\' && scan[1]) + { + *build++ = *++scan; + } + else + { + *build++ = *scan; + } + } + if (*scan) + scan++; + *build = '\0'; + } + else /* no "=" so it is probably boolean... */ + { + if (*scan) + { + *scan++ = '\0'; + } + value = NULL; + prefix = name; +#ifndef CRUNCH + if (!strcmp(name, "novice")) + /* don't check for a "no" prefix */; + else +#endif + if (prefix[0] == 'n' && prefix[1] == 'o') + name += 2; + else if (prefix[0] == 'n' && prefix[1] == 'e' && prefix[2] == 'g') + name += 3; + } + + /* find the variable */ + for (i = 0; + opts[i].name && strcmp(opts[i].name, name) && strcmp(opts[i].nm, name); + i++) + { + } + + /* change the variable */ + if (!opts[i].name) + { + msg("invalid option name \"%s\"", name); + } + else if ((opts[i].flags & CANSET) != CANSET) + { + msg("option \"%s\" can't be altered", name); + } + else if ((opts[i].flags & RCSET) != CANSET && nlines >= 1L) + { + msg("option \"%s\" can only be set in a %s file", name, EXRC); + } + else if (value) + { + switch (opts[i].type) + { + case BOOL: + msg("option \"[no]%s\" is boolean", name); + break; + + case NUM: + j = atoi(value); + if (j == 0 && *value != '0') + { + msg("option \"%s\" must have a numeric value", name); + } + else if (j < opts[i].value[1] || j > (opts[i].value[2] & 0xff)) + { + msg("option \"%s\" must have a value between %d and %d", + name, opts[i].value[1], opts[i].value[2] & 0xff); + } + else + { + *opts[i].value = atoi(value); + opts[i].flags |= SET; + } + break; + + case STR: + strcpy(opts[i].value, value); + opts[i].flags |= SET; + break; + } + if (opts[i].flags & MR) + { + redraw(MARK_UNSET, FALSE); + } +#ifndef CRUNCH + if (opts[i].flags & WSET) + { + wset = TRUE; + } +#endif + } + else /* valid option, no value */ + { + if (opts[i].type == BOOL) + { + if (prefix == name) + *opts[i].value = TRUE; + else if (prefix[1] == 'o') + *opts[i].value = FALSE; + else + *opts[i].value = !*opts[i].value; + + opts[i].flags |= SET; + if (opts[i].flags & MR) + { + redraw(MARK_UNSET, FALSE); + } + } + else + { + msg("option \"%s\" must be given a value", name); + } + } + + /* move on to the next option */ + name = scan; + } + + /* special processing ... */ + +#ifndef CRUNCH + /* if "novice" is set, then ":set report=1 showmode nomagic" */ + if (*o_novice) + { + *o_report = 1; +# ifndef NO_SHOWMODE + *o_smd = TRUE; +# endif +# ifndef NO_MAGIC + *o_magic = FALSE; +# endif + } +#endif + + /* if "readonly" then set the READONLY flag for this file */ + if (*o_readonly) + { + setflag(file, READONLY); + } + +#ifndef NO_DIGRAPH + /* re-initialize the ctype package */ + _ct_init(o_flipcase); +#endif /* not NO_DIGRAPH */ + + /* copy o_lines and o_columns into LINES and COLS */ + LINES = (*o_lines & 255); + COLS = (*o_columns & 255); +} diff --git a/commands/elvis/prsvunix.c b/commands/elvis/prsvunix.c new file mode 100755 index 000000000..c42e66b1a --- /dev/null +++ b/commands/elvis/prsvunix.c @@ -0,0 +1,106 @@ +/* prsvunix.c */ + +/* This file contains the UNIX-specific parts of the "elvprsv" program. */ + +#if OSK +#define ELVPRSV +#include "osk.c" +#else +#include <sys/stat.h> +#include <pwd.h> +#endif +extern struct passwd *getpwuid(); + +/* This variable is used to add extra error messages for mail sent to root */ +char *ps; + +/* This function returns the login name of the owner of a file */ +char *ownername(filename) + char *filename; /* name of a file */ +{ + struct stat st; + struct passwd *pw; + + /* stat the file, to get its uid */ + if (stat(filename, &st) < 0) + { + ps = "stat() failed"; + return "root"; + } + + /* get the /etc/passwd entry for that user */ + pw = getpwuid(st.st_uid); + if (!pw) + { + ps = "uid not found in password file"; + return "root"; + } + + /* return the user's name */ + return pw->pw_name; +} + + +/* This function sends a mail message to a given user, saying that a file + * has been preserved. + */ +void mail(user, file, when) + char *user; /* name of user who should receive the mail */ + char *file; /* name of original text file that was preserved */ + char *when; /* description of why the file was preserved */ +{ + char cmd[80];/* buffer used for constructing a "mail" command */ + FILE *m, *popen(); /* stream used for giving text to the "mail" program */ + char *base; /* basename of the file */ + + /* separate the directory name from the basename. */ + for (base = file + strlen(file); --base > file && *base != SLASH; ) + { + } + if (*base == SLASH) + { + *base++ = '\0'; + } + + /* for anonymous buffers, pretend the name was "foo" */ + if (!strcmp(base, "*")) + { + base = "foo"; + } + + /* open a pipe to the "mail" program */ +#if OSK + sprintf(cmd, "mail \"-s=%s preserved!\" %s", base, user); +#else /* ANY_UNIX */ + sprintf(cmd, "mail %s >/dev/null 2>/dev/null", user); +#endif + m = popen(cmd, "w"); + if (!m) + { + /* Can't send mail! Hope the user figures it out. */ + return; + } + + /* Tell the user that the file was preserved */ + fprintf(m, "A version of your file \"%s%c%s\"\n", file, SLASH, base); + fprintf(m, "was preserved when %s.\n", when); + fprintf(m, "To recover this file, do the following:\n"); + fprintf(m, "\n"); +#if OSK + fprintf(m, " chd %s\n", file); +#else /* ANY_UNIX */ + fprintf(m, " cd %s\n", file); +#endif + fprintf(m, " elvrec %s\n", base); + fprintf(m, "\n"); + fprintf(m, "With fond wishes for a speedy recovery,\n"); + fprintf(m, " Elvis\n"); + if (ps) + { + fprintf(m, "\nP.S. %s\n", ps); + ps = (char *)0; + } + + /* close the stream */ + pclose(m); +} diff --git a/commands/elvis/recycle.c b/commands/elvis/recycle.c new file mode 100755 index 000000000..421603610 --- /dev/null +++ b/commands/elvis/recycle.c @@ -0,0 +1,183 @@ +/* recycle.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains the functions perform garbage collection and allocate + * reusable blocks. + */ + +#include "config.h" +#include "vi.h" + +#ifndef NO_RECYCLE +/* this whole file would have be skipped if NO_RECYCLE is defined */ + + +#define BTST(bitno, byte) ((byte) & (1 << (bitno))) +#define BSET(bitno, byte) ((byte) |= (1 << (bitno))) +#define BCLR(bitno, byte) ((byte) &= ~(1 << (bitno))) + +#define TST(blkno) ((blkno) < MAXBIT ? BTST((blkno) & 7, bitmap[(blkno) >> 3]) : 1) +#define SET(blkno) if ((blkno) < MAXBIT) BSET((blkno) & 7, bitmap[(blkno) >> 3]) +#define CLR(blkno) if ((blkno) < MAXBIT) BCLR((blkno) & 7, bitmap[(blkno) >> 3]) + +/* bitmap of free blocks in first 4096k of tmp file */ +static unsigned char bitmap[512]; +#define MAXBIT (sizeof bitmap << 3) + +/* this function locates all free blocks in the current tmp file */ +void garbage() +{ + int i; + BLK oldhdr; + + /* start by assuming every block is free */ + for (i = 0; i < sizeof bitmap; i++) + { + bitmap[i] = 255; + } + + /* header blocks aren't free */ +#ifndef lint + CLR(0); + CLR(1); +#endif + + /* blocks needed for current hdr aren't free */ + for (i = 1; i < MAXBLKS; i++) + { + CLR(hdr.n[i]); + } + + /* blocks needed for undo version aren't free */ + lseek(tmpfd, 0L, 0); + if (read(tmpfd, &oldhdr, (unsigned)sizeof oldhdr) != sizeof oldhdr) + { + msg("garbage() failed to read oldhdr??"); + for (i = 0; i < sizeof bitmap; i++) + { + bitmap[i] = 0; + } + return; + } + for (i = 1; i < MAXBLKS; i++) + { + CLR(oldhdr.n[i]); + } + + /* blocks needed for cut buffers aren't free */ + for (i = cutneeds(&oldhdr) - 1; i >= 0; i--) + { + CLR(oldhdr.n[i]); + } +} + +/* This function allocates the first available block in the tmp file */ +long allocate() +{ + int i; + long offset; + + /* search for the first byte with a free bit set */ + for (i = 0; i < sizeof bitmap && bitmap[i] == 0; i++) + { + } + + /* if we hit the end of the bitmap, return the end of the file */ + if (i == sizeof bitmap) + { + offset = lseek(tmpfd, 0L, 2); + } + else /* compute the offset for the free block */ + { + for (i <<= 3; TST(i) == 0; i++) + { + } + offset = (long)i * (long)BLKSIZE; + + /* mark the block as "allocated" */ + CLR(i); + } + + return offset; +} + +#endif + +#ifdef DEBUG +# include <stdio.h> +# undef malloc +# undef free +# define MEMMAGIC 0x19f72cc0L +# define MAXALLOC 800 +static char *allocated[MAXALLOC]; +static char *fromfile[MAXALLOC]; +static int fromline[MAXALLOC]; +static int sizes[MAXALLOC]; + +char *dbmalloc(size, file, line) + int size; + char *file; + int line; +{ + char *ret; + int i; + + size = size + sizeof(long) - (size % sizeof(long)); + ret = (char *)malloc(size + 2 * sizeof(long)) + sizeof(long); + for (i = 0; i < MAXALLOC && allocated[i]; i++) + { + } + if (i == MAXALLOC) + { + endwin(); + fprintf(stderr, "\r\n%s(%d): Too many malloc calls!\n", file, line); + abort(); + } + sizes[i] = size/sizeof(long); + allocated[i] = ret; + fromfile[i] = file; + fromline[i] = line; + ((long *)ret)[-1] = MEMMAGIC; + ((long *)ret)[sizes[i]] = MEMMAGIC; + return ret; +} + +dbfree(ptr, file, line) + char *ptr; + char *file; + int line; +{ + int i; + + for (i = 0; i < MAXALLOC && allocated[i] != ptr; i++) + { + } + if (i == MAXALLOC) + { + endwin(); + fprintf(stderr, "\r\n%s(%d): attempt to free mem that wasn't allocated\n", file, line); + abort(); + } + allocated[i] = (char *)0; + if (((long *)ptr)[-1] != MEMMAGIC) + { + endwin(); + fprintf(stderr, "\r\n%s(%d): underflowed malloc space, allocated at %s(%d)\n", file, line, fromfile[i], fromline[i]); + abort(); + } + if (((long *)ptr)[sizes[i]] != MEMMAGIC) + { + endwin(); + fprintf(stderr, "\r\n%s(%d): overflowed malloc space, allocated at %s(%d)\n", file, line, fromfile[i], fromline[i]); + abort(); + } + free(ptr - sizeof(long)); +} +#endif diff --git a/commands/elvis/redraw.c b/commands/elvis/redraw.c new file mode 100755 index 000000000..ae923cf60 --- /dev/null +++ b/commands/elvis/redraw.c @@ -0,0 +1,1295 @@ +/* redraw.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains functions that draw text on the screen. The major entry + * points are: + * redrawrange() - called from modify.c to give hints about what parts + * of the screen need to be redrawn. + * redraw() - redraws the screen (or part of it) and positions + * the cursor where it belongs. + * idx2col() - converts a markidx() value to a logical column number. + */ + +#include "config.h" +#include "vi.h" + +/* This variable contains the line number that smartdrawtext() knows best */ +static long smartlno; + +/* This function remembers where changes were made, so that the screen can be + * redraw in a more efficient manner. + */ +static long redrawafter; /* line# of first line that must be redrawn */ +static long preredraw; /* line# of last line changed, before change */ +static long postredraw; /* line# of last line changed, after change */ +static int mustredraw; /* boolean: anything forcing a screen update? */ +void redrawrange(after, pre, post) + long after; /* lower bound of redrawafter */ + long pre; /* upper bound of preredraw */ + long post; /* upper bound of postredraw */ +{ + if (after == redrawafter) + { + /* multiple insertions/deletions at the same place -- combine + * them + */ + preredraw -= (post - pre); + if (postredraw < post) + { + preredraw += (post - postredraw); + postredraw = post; + } + if (redrawafter > preredraw) + { + redrawafter = preredraw; + } + if (redrawafter < 1L) + { + redrawafter = 0L; + preredraw = postredraw = INFINITY; + } + } + else if (postredraw > 0L) + { + /* multiple changes in different places -- redraw everything + * after "after". + */ + postredraw = preredraw = INFINITY; + if (after < redrawafter) + redrawafter = after; + } + else + { + /* first change */ + redrawafter = after; + preredraw = pre; + postredraw = post; + } + mustredraw = TRUE; +} + + +#ifndef NO_CHARATTR +/* see if a given line uses character attribute strings */ +static int hasattr(lno, text) + long lno; /* the line# of the cursor */ + REG char *text; /* the text of the line, from fetchline */ +{ + static long plno; /* previous line number */ + static long chgs; /* previous value of changes counter */ + static int panswer;/* previous answer */ + char *scan; + + /* if charattr is off, then the answer is "no, it doesn't" */ + if (!*o_charattr) + { + chgs = 0; /* <- forces us to check if charattr is later set */ + return FALSE; + } + + /* if we already know the answer, return it... */ + if (lno == plno && chgs == changes) + { + return panswer; + } + + /* get the line & look for "\fX" */ + if (!text[0] || !text[1] || !text[2]) + { + panswer = FALSE; + } + else + { + for (scan = text; scan[2] && !(scan[0] == '\\' && scan[1] == 'f'); scan++) + { + } + panswer = (scan[2] != '\0'); + } + + /* save the results */ + plno = lno; + chgs = changes; + + /* return the results */ + return panswer; +} +#endif + + +#ifndef NO_VISIBLE +/* This function checks to make sure that the correct lines are shown in + * reverse-video. This is used to handle the "v" and "V" commands. + */ +static long vizlow, vizhigh; /* the starting and ending lines */ +static int vizleft, vizright; /* starting & ending indicies */ +static int vizchange; /* boolean: must use stupid drawtext? */ +static void setviz(curs) + MARK curs; +{ + long newlow, newhigh; + long extra = 0L; + + /* for now, assume the worst... */ + vizchange = TRUE; + + /* set newlow & newhigh according to V_from and cursor */ + if (!V_from) + { + /* no lines should have reverse-video */ + if (vizlow) + { + redrawrange(vizlow, vizhigh + 1L, vizhigh + 1L); + vizlow = vizhigh = 0L; + } + else + { + vizchange = FALSE; + } + return; + } + + /* figure out which lines *SHOULD* have hilites */ + if (V_from < curs) + { + newlow = markline(V_from); + newhigh = markline(curs); + vizleft = markidx(V_from); + vizright = markidx(curs) + 1; + } + else + { + newlow = markline(curs); + newhigh = markline(V_from); + vizleft = markidx(curs); + vizright = markidx(V_from) + 1; + } + + /* adjust for line-mode hiliting */ + if (V_linemd) + { + vizleft = 0; + vizright = BLKSIZE - 1; + } + else + { + extra = 1L; + } + + /* arrange for the necessary lines to be redrawn */ + if (vizlow == 0L) + { + /* just starting to redraw */ + redrawrange(newlow, newhigh, newhigh); + } + else + { + /* Were new lines added/removed at the front? */ + if (newlow != vizlow) + { + if (newlow < vizlow) + redrawrange(newlow, vizlow + extra, vizlow + extra); + else + redrawrange(vizlow, newlow + extra, newlow + extra); + } + + /* Were new lines added/removed at the back? */ + if (newhigh != vizhigh) + { + if (newhigh < vizhigh) + redrawrange(newhigh + 1L - extra, vizhigh + 1L, vizhigh + 1L); + else + redrawrange(vizhigh + 1L - extra, newhigh, newhigh); + } + } + + /* remember which lines will contain hilighted text now */ + vizlow = newlow; + vizhigh = newhigh; +} +#endif /* !NO_VISIBLE */ + + +/* This function converts a MARK to a column number. It doesn't automatically + * adjust for leftcol; that must be done by the calling function + */ +int idx2col(curs, text, inputting) + MARK curs; /* the line# & index# of the cursor */ + REG char *text; /* the text of the line, from fetchline */ + int inputting; /* boolean: called from input() ? */ +{ + static MARK pcursor;/* previous cursor, for possible shortcut */ + static MARK pcol; /* column number for pcol */ + static long chgs; /* previous value of changes counter */ + REG int col; /* used to count column numbers */ + REG int idx; /* used to count down the index */ + REG int i; + + /* for now, assume we have to start counting at the left edge */ + col = 0; + idx = markidx(curs); + + /* if the file hasn't changed & line number is the same & it has no + * embedded character attribute strings, can we do shortcuts? + */ + if (chgs == changes + && !((curs ^ pcursor) & ~(BLKSIZE - 1)) +#ifndef NO_CHARATTR + && !hasattr(markline(curs), text) +#endif + ) + { + /* no movement? */ + if (curs == pcursor) + { + /* return the column of the char; for tabs, return its last column */ + if (text[idx] == '\t' && !inputting && !*o_list) + { + return pcol + *o_tabstop - (pcol % *o_tabstop) - 1; + } + else + { + return pcol; + } + } + + /* movement to right? */ + if (curs > pcursor) + { + /* start counting from previous place */ + col = pcol; + idx = markidx(curs) - markidx(pcursor); + text += markidx(pcursor); + } + } + + /* count over to the char after the idx position */ + while (idx > 0 && (i = *text)) /* yes, ASSIGNMENT! */ + { + if (i == '\t' && !*o_list) + { + col += *o_tabstop; + col -= col % *o_tabstop; + } + else if (i >= '\0' && i < ' ' || i == '\177') + { + col += 2; + } +#ifndef NO_CHARATTR + else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr) + { + text += 2; /* plus one more at bottom of loop */ + idx -= 2; + } +#endif + else + { + col++; + } + text++; + idx--; + } + + /* save stuff to speed next call */ + pcursor = curs; + pcol = col; + chgs = changes; + + /* return the column of the char; for tabs, return its last column */ + if (*text == '\t' && !inputting && !*o_list) + { + return col + *o_tabstop - (col % *o_tabstop) - 1; + } + else + { + return col; + } +} + + +/* This function is similar to idx2col except that it takes care of sideways + * scrolling - for the given line, at least. + */ +int mark2phys(m, text, inputting) + MARK m; /* a mark to convert */ + char *text; /* the line that m refers to */ + int inputting; /* boolean: caled from input() ? */ +{ + int i; + + i = idx2col(m, text, inputting); + while (i < leftcol) + { + leftcol -= *o_sidescroll; + mustredraw = TRUE; + redrawrange(1L, INFINITY, INFINITY); + } + while (i > rightcol) + { + leftcol += *o_sidescroll; + mustredraw = TRUE; + redrawrange(1L, INFINITY, INFINITY); + } + physrow = markline(m) - topline; + physcol = i - leftcol; + if (*o_number) + physcol += 8; + + return physcol; +} + +/* This function draws a single line of text on the screen. The screen's + * cursor is assumed to be located at the leftmost column of the appropriate + * row. + */ +static void drawtext(text, lno, clr) + REG char *text; /* the text to draw */ + long lno; /* the number of the line to draw */ + int clr; /* boolean: do a clrtoeol? */ +{ + REG int col; /* column number */ + REG int i; + REG int tabstop; /* *o_tabstop */ + REG int limitcol; /* leftcol or leftcol + COLS */ + int abnormal; /* boolean: charattr != A_NORMAL? */ +#ifndef NO_VISIBLE + int rev; /* boolean: standout mode, too? */ + int idx = 0; +#endif + char numstr[9]; + + /* show the line number, if necessary */ + if (*o_number) + { + sprintf(numstr, "%6ld |", lno); + qaddstr(numstr); + } + +#ifndef NO_SENTENCE + /* if we're hiding format lines, and this is one of them, then hide it */ + if (*o_hideformat && *text == '.') + { + clrtoeol(); +#if OSK + qaddch('\l'); +#else + qaddch('\n'); +#endif + return; + } +#endif + + /* move some things into registers... */ + limitcol = leftcol; + tabstop = *o_tabstop; + abnormal = FALSE; + +#ifndef CRUNCH + if (clr) + clrtoeol(); +#endif + + /* skip stuff that was scrolled off left edge */ + for (col = 0; + (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */ + text++) + { +#ifndef NO_VISIBLE + idx++; +#endif + if (i == '\t' && !*o_list) + { + col = col + tabstop - (col % tabstop); + } + else if (i >= 0 && i < ' ' || i == '\177') + { + col += 2; + } +#ifndef NO_CHARATTR + else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr) + { + text += 2; /* plus one more as part of "for" loop */ + + /* since this attribute might carry over, we need it */ + switch (*text) + { + case 'R': + case 'P': + attrset(A_NORMAL); + abnormal = FALSE; + break; + + case 'B': + attrset(A_BOLD); + abnormal = TRUE; + break; + + case 'U': + attrset(A_UNDERLINE); + abnormal = TRUE; + break; + + case 'I': + attrset(A_ALTCHARSET); + abnormal = TRUE; + break; + } + } +#endif + else + { + col++; + } + } + +#ifndef NO_VISIBLE + /* Should we start hiliting at the first char of this line? */ + if ((lno > vizlow && lno <= vizhigh + || lno == vizlow && vizleft < idx) + && !(lno == vizhigh && vizright < idx)) + { + do_VISIBLE(); + rev = TRUE; + } +#endif + + /* adjust for control char that was partially visible */ + while (col > limitcol) + { + qaddch(' '); + limitcol++; + } + + /* now for the visible characters */ + limitcol = leftcol + COLS; + if (*o_number) + limitcol -= 8; + for (; (i = *text) && col < limitcol; text++) + { +#ifndef NO_VISIBLE + /* maybe turn hilite on/off in the middle of the line */ + if (lno == vizlow && vizleft == idx) + { + do_VISIBLE(); + rev = TRUE; + } + if (lno == vizhigh && vizright == idx) + { + do_SE(); + rev = FALSE; + } + idx++; + + /* if hiliting, never emit physical tabs */ + if (rev && i == '\t' && !*o_list) + { + i = col + tabstop - (col % tabstop); + do + { + qaddch(' '); + col++; + } while (col < i); + } + else +#endif /* !NO_VISIBLE */ + if (i == '\t' && !*o_list) + { + i = col + tabstop - (col % tabstop); + if (i < limitcol) + { +#ifdef CRUNCH + if (!clr && has_PT && !((i - leftcol) & 7)) +#else + if (has_PT && !((i - leftcol) & 7)) +#endif + { + do + { + qaddch('\t'); + col += 8; /* not exact! */ + } while (col < i); + col = i; /* NOW it is exact */ + } + else + { + do + { + qaddch(' '); + col++; + } while (col < i); + } + } + else /* tab ending after screen? next line! */ + { + col = limitcol; + if (has_AM) + { + addch('\n'); /* GB */ + } + } + } + else if (i >= 0 && i < ' ' || i == '\177') + { + col += 2; + qaddch('^'); + if (col <= limitcol) + { + qaddch(i ^ '@'); + } + } +#ifndef NO_CHARATTR + else if (i == '\\' && text[1] == 'f' && text[2] && *o_charattr) + { + text += 2; /* plus one more as part of "for" loop */ + switch (*text) + { + case 'R': + case 'P': + attrset(A_NORMAL); + abnormal = FALSE; + break; + + case 'B': + attrset(A_BOLD); + abnormal = TRUE; + break; + + case 'U': + attrset(A_UNDERLINE); + abnormal = TRUE; + break; + + case 'I': + attrset(A_ALTCHARSET); + abnormal = TRUE; + break; + } + } +#endif + else + { + col++; + qaddch(i); + } + } + + /* get ready for the next line */ +#ifndef NO_CHARATTR + if (abnormal) + { + attrset(A_NORMAL); + } +#endif + if (*o_list && col < limitcol) + { + qaddch('$'); + col++; + } + +#ifndef NO_VISIBLE + /* did we hilite this whole line? If so, STOP! */ + if (rev) + { + do_SE(); + } +#endif + +#ifdef CRUNCH + if (clr && col < limitcol) + { + clrtoeol(); + } +#endif + if (!has_AM || col < limitcol) + { + addch('\n'); + } + + wqrefresh(); +} + + +#ifndef CRUNCH +static void nudgecursor(same, scan, new, lno) + int same; /* number of chars to be skipped over */ + char *scan; /* where the same chars end */ + char *new; /* where the visible part of the line starts */ + long lno; /* line number of this line */ +{ + int col; + + if (same > 0) + { + if (same < 5) + { + /* move the cursor by overwriting */ + while (same > 0) + { + qaddch(scan[-same]); + same--; + } + } + else + { + /* move the cursor by calling move() */ + col = (int)(scan - new); + if (*o_number) + col += 8; + move((int)(lno - topline), col); + } + } +} +#endif /* not CRUNCH */ + +/* This function draws a single line of text on the screen, possibly with + * some cursor optimization. The cursor is repositioned before drawing + * begins, so its position before doesn't really matter. + */ +static void smartdrawtext(text, lno, showit) + REG char *text; /* the text to draw */ + long lno; /* line number of the text */ + int showit; /* boolean: output line? (else just remember it) */ +{ +#ifdef CRUNCH + move((int)(lno - topline), 0); + if (showit) + { + drawtext(text, lno, TRUE); + } +#else /* not CRUNCH */ + static char old[256]; /* how the line looked last time */ + char new[256]; /* how it looks now */ + char *build; /* used to put chars into new[] */ + char *scan; /* used for moving thru new[] or old[] */ + char *end; /* last non-blank changed char */ + char *shift; /* used to insert/delete chars */ + int same; /* length of a run of unchanged chars */ + int limitcol; + int col; + int i; + char numstr[9]; + +# ifndef NO_CHARATTR + /* if this line has attributes, do it the dumb way instead */ + if (hasattr(lno, text)) + { + move((int)(lno - topline), 0); + drawtext(text, lno, TRUE); + return; + } +# endif +# ifndef NO_SENTENCE + /* if this line is a format line, & we're hiding format lines, then + * let the dumb drawtext() function handle it + */ + if (*o_hideformat && *text == '.') + { + move((int)(lno - topline), 0); + drawtext(text, lno, TRUE); + return; + } +# endif +# ifndef NO_VISIBLE + if (vizchange) + { + move((int)(lno - topline), 0); + drawtext(text, lno, TRUE); + smartlno = 0L; + return; + } +# endif + + /* skip stuff that was scrolled off left edge */ + limitcol = leftcol; + for (col = 0; + (i = *text) && col < limitcol; /* yes, ASSIGNMENT! */ + text++) + { + if (i == '\t' && !*o_list) + { + col = col + *o_tabstop - (col % *o_tabstop); + } + else if (i >= 0 && i < ' ' || i == '\177') + { + col += 2; + } + else + { + col++; + } + } + + /* adjust for control char that was partially visible */ + build = new; + while (col > limitcol) + { + *build++ = ' '; + limitcol++; + } + + /* now for the visible characters */ + limitcol = leftcol + COLS; + if (*o_number) + limitcol -= 8; + for (; (i = *text) && col < limitcol; text++) + { + if (i == '\t' && !*o_list) + { + i = col + *o_tabstop - (col % *o_tabstop); + while (col < i && col < limitcol) + { + *build++ = ' '; + col++; + } + } + else if (i >= 0 && i < ' ' || i == '\177') + { + col += 2; + *build++ = '^'; + if (col <= limitcol) + { + *build++ = (i ^ '@'); + } + } + else + { + col++; + *build++ = i; + } + } + if (col < limitcol && *o_list) + { + *build++ = '$'; + col++; + } + end = build; + while (col < limitcol) + { + *build++ = ' '; + col++; + } + + /* if we're just supposed to remember this line, then remember it */ + if (!showit) + { + smartlno = lno; + strncpy(old, new, COLS); + return; + } + + /* locate the last non-blank character */ + while (end > new && end[-1] == ' ') + { + end--; + } + + /* can we optimize the displaying of this line? */ + if (lno != smartlno) + { + /* nope, can't optimize - different line */ + move((int)(lno - topline), 0); + + /* show the line number, if necessary */ + if (*o_number) + { + sprintf(numstr, "%6ld |", lno); + qaddstr(numstr); + } + + /* show the new line */ + for (scan = new, build = old; scan < end; ) + { + qaddch(*scan); + *build++ = *scan++; + } + if (end < new + COLS - (*o_number ? 8 : 0)) + { + clrtoeol(); + while (build < old + COLS) + { + *build++ = ' '; + } + } + smartlno = lno; + return; + } + + /* skip any initial unchanged characters */ + for (scan = new, build = old; scan < end && *scan == *build; scan++, build++) + { + } + i = (scan - new); + if (*o_number) + i += 8; + move((int)(lno - topline), i); + + /* The in-between characters must be changed */ + same = 0; + while (scan < end) + { + /* is this character a match? */ + if (scan[0] == build[0]) + { + same++; + } + else /* do we want to insert? */ + if (scan < end - 1 && scan[1] == build[0] && (has_IC || has_IM)) + { + nudgecursor(same, scan, new, lno); + same = 0; + + insch(*scan); + for (shift = old + COLS; --shift > build; ) + { + shift[0] = shift[-1]; + } + *build = *scan; + } + else /* do we want to delete? */ + if (build < old + COLS - 1 && scan[0] == build[1] && has_DC) + { + nudgecursor(same, scan, new, lno); + same = 0; + + delch(); + same++; + for (shift = build; shift < old + COLS - 1; shift++) + { + shift[0] = shift[1]; + } + if (*o_number) + shift -= 8; + *shift = ' '; + } + else /* we must overwrite */ + { + nudgecursor(same, scan, new, lno); + same = 0; + + addch(*scan); + *build = *scan; + } + + build++; + scan++; + } + + /* maybe clear to EOL */ + end = old + COLS - (*o_number ? 8 : 0); + while (build < end && *build == ' ') + { + build++; + } + if (build < end) + { + nudgecursor(same, scan, new, lno); + same = 0; + + clrtoeol(); + while (build < old + COLS) + { + *build++ = ' '; + } + } +#endif /* not CRUNCH */ +} + + +/* This function is used in visual mode for drawing the screen (or just parts + * of the screen, if that's all thats needed). It also takes care of + * scrolling. + */ +void redraw(curs, inputting) + MARK curs; /* where to leave the screen's cursor */ + int inputting; /* boolean: being called from input() ? */ +{ + char *text; /* a line of text to display */ + static long chgs; /* previous changes level */ + long l; + int i; +#ifndef CRUNCH + static long showtop; /* top line in window */ + static long showbottom; /* bottom line in window */ +#endif + + /* if curs == MARK_UNSET, then we should reset internal vars */ + if (curs == MARK_UNSET) + { + if (topline < 1 || topline > nlines) + { + topline = 1L; + } + else + { + move(LINES - 1, 0); + clrtoeol(); + } + leftcol = 0; + mustredraw = TRUE; + redrawafter = INFINITY; + preredraw = 0L; + postredraw = 0L; + chgs = 0; + smartlno = 0L; +#ifndef NO_VISIBLE + vizlow = vizhigh = 0L; + vizchange = FALSE; +#endif +#ifndef CRUNCH + showtop = 0; + showbottom = INFINITY; +#endif + return; + } + +#ifndef NO_VISIBLE + /* adjustments to hilited area may force extra lines to be redrawn. */ + setviz(curs); +#endif + + /* figure out which column the cursor will be in */ + l = markline(curs); + text = fetchline(l); + mark2phys(curs, text, inputting); + +#ifndef NO_COLOR + fixcolor(); +#endif + + /* adjust topline, if necessary, to get the cursor on the screen */ + if (l >= topline && l <= botline) + { + /* it is on the screen already */ + + /* if the file was changed but !mustredraw, then redraw line */ + if (!mustredraw && (chgs != changes +#ifndef NO_VISIBLE + || V_from +#endif +#ifndef CRUNCH + || l < showtop || l > showbottom +#endif + )) + { + smartdrawtext(text, l, (chgs != changes)); + } + } + else if (l < topline && l > topline - LINES && (has_SR || has_AL)) + { + /* near top - scroll down */ + if (!mustredraw) + { + move(0,0); + while (l < topline) + { + topline--; + if (has_SR) + { + do_SR(); + } + else + { + insertln(); + } + text = fetchline(topline); + drawtext(text, topline, FALSE); + do_UP(); + } + + /* blank out the last line */ + move(LINES - 1, 0); + clrtoeol(); + } + else + { + topline = l; + redrawrange(0L, INFINITY, INFINITY); + } + } + else if (l > topline && l < botline + LINES) + { + /* near bottom -- scroll up */ + if (!mustredraw) + { + move(LINES - 1,0); + clrtoeol(); + while (l > botline) + { + topline++; /* <-- also adjusts botline */ + text = fetchline(botline); + drawtext(text, botline, FALSE); + } +#ifndef CRUNCH + showbottom = l; +#endif + } + else + { + topline = l - (LINES - 2); + redrawrange(0L, INFINITY, INFINITY); + } + } + else + { + /* distant line - center it & force a redraw */ + topline = l - (LINES / 2) - 1; + if (topline < 1) + { + topline = 1; + } + redrawrange(0L, INFINITY, INFINITY); + changes++; + } + +#ifndef CRUNCH + /* make sure the current line is included in the "window" */ + if (l < showtop) + { + redrawrange(l, showtop, showtop); + showtop = l; + } + if (l > showbottom) + { + redrawrange(showbottom, l, l); + showbottom = l; + } +#endif + + + /* Now... do we really have to redraw? */ + if (mustredraw) + { + /* If redrawfter (and friends) aren't set, assume we should + * redraw everything. + */ + if (redrawafter == INFINITY) + { + redrawafter = 0L; + preredraw = postredraw = INFINITY; + } + +#ifndef CRUNCH + /* shrink the window, if possible */ + if (showtop < topline) + { + showtop = topline; + } + if (showbottom > botline) + { + showbottom = botline; + } + if (postredraw == INFINITY) + { + /* these will be set to more reasonable values later */ + showtop = INFINITY; + showbottom = 0L; + } +#endif + + /* adjust smartlno to correspond with inserted/deleted lines */ + if (smartlno >= redrawafter) + { + if (smartlno < preredraw && postredraw != preredraw) /*!!!*/ + { + smartlno = 0L; + } + else + { + smartlno += (postredraw - preredraw); + } + } + + /* should we insert some lines into the screen? */ + if (preredraw < postredraw && preredraw <= botline) + { + /* lines were inserted into the file */ + + /* decide where insertion should start */ + if (preredraw < topline) + { + l = topline; + } + else + { + l = preredraw; + } + + /* insert the lines... maybe */ + if (l + postredraw - preredraw > botline || !has_AL || *o_number) + { + /* Whoa! a whole screen full - just redraw */ + preredraw = postredraw = INFINITY; + } + else + { + /* really insert 'em */ + move((int)(l - topline), 0); + for (i = postredraw - preredraw; i > 0; i--) + { + insertln(); + } + + /* NOTE: the contents of those lines will be + * drawn as part of the regular redraw loop. + */ + + /* clear the last line */ + move(LINES - 1, 0); + clrtoeol(); + } + } + + /* do we want to delete some lines from the screen? */ + if (preredraw > postredraw && postredraw <= botline) + { + if (preredraw > botline || !has_DL || *o_number) + { + postredraw = preredraw = INFINITY; + } + else /* we'd best delete some lines from the screen */ + { + /* clear the last line, so it doesn't look + * ugly as it gets pulled up into the screen + */ + move(LINES - 1, 0); + clrtoeol(); + + /* delete the lines */ + move((int)(postredraw - topline), 0); + for (l = postredraw; + l < preredraw && l <= botline; + l++) + { + deleteln(); + } + + /* draw the lines that are now newly visible + * at the bottom of the screen + */ + i = LINES - 1 + (postredraw - preredraw); + move(i, 0); + for (l = topline + i; l <= botline; l++) + { + /* clear this line */ + clrtoeol(); + + /* draw the line, or ~ for non-lines */ + if (l <= nlines) + { + text = fetchline(l); + drawtext(text, l, FALSE); + } + else + { + addstr("~\n"); + } + } + } + } + + /* redraw the current line */ + l = markline(curs); + pfetch(l); + smartdrawtext(ptext, l, TRUE); + +#ifndef CRUNCH + /* decide which lines must be in the "window" around the cursor */ + l = markline(curs); + if ((*o_window & 0xff) + 1 == LINES) + { + showtop = 1; + showbottom = INFINITY; + } + else if (l < showtop || l > showbottom) + { + l -= (*o_window & 0xff) / 2; + if (l < topline) + { + l = topline; + } + if (l < showtop) + { + showtop = l; + } + l += (*o_window & 0xff) - 1; + if (l > botline) + { + showtop = showtop - l + botline; + l = botline; + } + if (l > showbottom) + { + showbottom = l; + } + } +#endif + + /* decide where we should start redrawing from */ + if (redrawafter < topline) + { + l = topline; + } + else + { + l = redrawafter; + } + if (l <= botline && l < postredraw && (l != smartlno || botline != smartlno)) + { + /* draw the other lines */ + move((int)(l - topline), 0); + for (; l <= botline && l < postredraw; l++) + { + /* we already drew the current line, so skip it now */ + if (l == smartlno) + { +#if OSK + qaddch('\l'); +#else + qaddch('\n'); +#endif + continue; + } + + /* draw the line, or ~ for non-lines */ + if (l > nlines) + { + qaddch('~'); + clrtoeol(); + addch('\n'); + } +#ifndef CRUNCH + else if (l < showtop || l > showbottom) + { + qaddch('@'); + clrtoeol(); + addch('\n'); + } +#endif + else + { + text = fetchline(l); + drawtext(text, l, TRUE); + } + } + } + + mustredraw = FALSE; + } + + /* force total (non-partial) redraw next time if not set */ + redrawafter = INFINITY; + preredraw = 0L; + postredraw = 0L; + + /* move the cursor to where it belongs */ + move((int)(markline(curs) - topline), physcol); + wqrefresh(); + + chgs = changes; +} diff --git a/commands/elvis/ref.c b/commands/elvis/ref.c new file mode 100755 index 000000000..8c6b57aa0 --- /dev/null +++ b/commands/elvis/ref.c @@ -0,0 +1,521 @@ +/* ref2.c */ + +/* This is a totally rewritten version of ref. This version looks for the + * desired function name in the "tags" file, and then reads the header out + * from the source file. There is no longer any need for a "refs" file. + * + * Usage: ref [-a] [-t] [-f file] [-c class] tag + * Options: -t output tag info, not the description + * -f file default filename for static functions + * -c class default class names for class functions + */ + +#include <stdio.h> +#include "config.h" +extern char *getenv(); +extern char *fgets(); + + +/* This is the default path that is searched for tags */ +#if OSK +# define DEFTAGPATH ".:/dd/defs:/dd/defs/sys:/dd/usr/src/lib:../lib:/dd/usr/lib" +#else +# if ANY_UNIX +# define DEFTAGPATH ".:/usr/include:/usr/include/sys:/usr/src/lib:../lib:/usr/local/lib" +# else +# if MSDOS || TOS +# define DEFTAGPATH ".;C:\\include;C:\\include\\sys;C:\\lib;..\\lib" +# define SEP ';' +# else +# if AMIGA +# define DEFTAGPATH ".;Include:;Include:sys" +# define SEP ';' +# else /* any other OS */ +# define DEFTAGPATH "." +# endif +# endif +# endif +#endif + +#ifndef SEP +# define SEP ':' +#endif + + +/* These variables reflect the command-line options given by the user. */ +int taginfo; /* boolean: give only the tag info? (not header?) */ +char *def_file; /* default filename for static functions */ +char *def_class; /* default classname for class members */ +int colons; /* #colons in tag: 0=normal, 1=static, 2=member */ + +/* This function checks for a tag in the "tags" file of given directory. + * If the tag is found, then it returns a pointer to a static buffer which + * contains the filename, a tab character, and a linespec for finding the + * the tag. If the tag is not found in the "tags" file, or if the "tags" + * file cannot be opened or doesn't exist, then this function returns NULL. + */ +char *cktagdir(tag, dir) + char *tag; /* name of the tag to look for */ + char *dir; /* name of the directory to check */ +{ + char buf[BLKSIZE]; + static char found[BLKSIZE]; + FILE *tfile; + int len; + +#if AMIGA + if (dir[strlen(dir) - 1] == COLON) + sprintf(buf, "%s%s", dir, TAGS); /* no slash after colon. */ + else +#endif + /* construct the name of the "tags" file in this directory */ + sprintf(buf, "%s%c%s", dir, SLASH, TAGS); + + /* Try to open the tags file. Return NULL if can't open */ +#if AMIGA + if (buf[0] == '.' && buf[1] == SLASH) + tfile = fopen(&buf[2], "r"); + else +#endif + tfile = fopen(buf, "r"); + if (!tfile) + { + return (char *)0; + } + + /* compute the length of the tagname once */ + len = strlen(tag); + + /* read lines until we get the one for this tag */ + found[0] = '\0'; + while (fgets(buf, sizeof buf, tfile)) + { + /* is this the one we want? */ + if (!strncmp(buf, tag, len) && buf[len] == '\t') + { + /* we've found a match -- remember it */ + strcpy(found, buf); + + /* if there is no default file, or this match is in + * the default file, then we've definitely found the + * one we want. Break out of the loop now. + */ + if (!def_file || !strncmp(&buf[len + 1], def_file, strlen(def_file))) + { + break; + } + } + } + + /* we're through reading */ + fclose(tfile); + + /* if there's anything in found[], use it */ + if (found[0]) + { + return &found[len + 1]; + } + + /* else we didn't find it */ + return (char *)0; +} + +/* This function reads a single textline from a binary file. It returns + * the number of bytes read, or 0 at EOF. + */ +int getline(buf, limit, fp) + char *buf; /* buffer to read into */ + int limit; /* maximum characters to read */ + FILE *fp; /* binary stream to read from */ +{ + int bytes; /* number of bytes read so far */ + int ch; /* single character from file */ + + for (bytes = 0, ch = 0; ch != '\n' && --limit > 0 && (ch = getc(fp)) != EOF; bytes++) + { +#if MSDOS || TOS + /* since this is a binary file, we'll need to manually strip CR's */ + if (ch == '\r') + { + continue; + } +#endif + *buf++ = ch; + } + *buf = '\0'; + + return bytes; +} + + +/* This function reads a source file, looking for a given tag. If it finds + * the tag, then it displays it and returns TRUE. Otherwise it returns FALSE. + * To display the tag, it attempts to output any introductory comment, the + * tag line itself, and any arguments. Arguments are assumed to immediately + * follow the tag line, and start with whitespace. Comments are assumed to + * start with lines that begin with "/*", "//", "(*", or "--", and end at the + * tag line or at a blank line. + */ +int lookup(dir, entry) + char *dir; /* name of the directory that contains the source */ + char *entry; /* source filename, <Tab>, linespec */ +{ + char buf[BLKSIZE]; /* pathname of sourcefile */ + long lnum; /* line number */ + long here; /* seek position where current line began */ + long comment; /* seek position of introductory comment, or -1L */ + FILE *sfile; /* used for reading the source file */ + int len; /* length of string */ + char *ptr; + + + /* construct the pathname of the source file */ + strcpy(buf, dir); + ptr = buf + strlen(buf); +#if AMIGA + if (ptr[-1] != COLON) +#endif + *ptr++ = SLASH; + while (*entry != '\t') + { + *ptr++ = *entry++; + } + *ptr = '\0'; + entry++; + + /* searching for string or number? */ + if (*entry >= '0' && *entry <= '9') + { + /* given a specific line number */ + lnum = atol(entry); + entry = (char *)0; + } + else + { + /* given a string -- strip off "/^" and "$/\n" */ + entry += 2; + len = strlen(entry) - 2; + if (entry[len - 1] == '$') + { + entry[len - 1] = '\n'; + } + lnum = 0L; + } + + /* Open the file. Note that we open the file in binary mode even + * though we know it is a text file, because ftell() and fseek() + * don't work on text files. + */ +#if MSDOS || TOS + sfile = fopen(buf, "rb"); +#else +# if AMIGA + if (buf[0] == '.' && buf[1] == SLASH) + sfile = fopen(&buf[2], "r"); + else +# endif + sfile = fopen(buf, "r"); +#endif + if (!sfile) + { + /* can't open the real source file. Try "refs" instead */ +#if AMIGA + if (dir[strlen(dir) - 1] == COLON) + sprintf(buf, "%srefs", dir); + else +#endif + sprintf(buf, "%s%crefs", dir, SLASH); +#if MSDOS || TOS + sfile = fopen(buf, "rb"); +#else +# if AMIGA + if (buf[0] == '.' && buf[1] == SLASH) + sfile = fopen(&buf[2], "r"); + else +# endif + sfile = fopen(buf, "r"); +#endif + if (!sfile) + { + /* failed! */ + return 0; + } + } + + /* search the file */ + for (comment = -1L; here = ftell(sfile), getline(buf, BLKSIZE, sfile) > 0; ) + { + /* Is this the start/end of a comment? */ + if (comment == -1L) + { + /* starting a comment? */ + if (buf[0] == '/' && buf[1] == '*' + || buf[0] == '/' && buf[1] == '/' + || buf[0] == '(' && buf[1] == '*' + || buf[0] == '-' && buf[1] == '-') + { + comment = here; + } + } + else + { + /* ending a comment? */ + if (buf[0] == '\n' || buf[0] == '#') + { + comment = -1L; + } + } + + /* is this the tag line? */ + if (--lnum == 0L || (entry && !strncmp(buf, entry, len))) + { + /* if there were introductory comments, show them */ + if (comment != -1L) + { + fseek(sfile, comment, 0); + while (comment != here) + { + getline(buf, BLKSIZE, sfile); + fputs(buf, stdout); + comment = ftell(sfile); + } + + /* re-fetch the tag line */ + fgets(buf, BLKSIZE, sfile); + } + + /* show the tag line */ + fputs(buf, stdout); + + /* show any argument lines */ + while (getline(buf, BLKSIZE, sfile) > 0 + && buf[0] != '#' + && strchr(buf, '{') == (char *)0) + { + fputs(buf, stdout); + } + + /* Done! Close the file, and return TRUE */ + fclose(sfile); + return 1; + } + } + + /* not found -- return FALSE */ + return 0; +} + +/* This function searches through the entire search path for a given tag. + * If it finds the tag, then it displays the info and returns TRUE; + * otherwise it returns FALSE. + */ +int find(tag) + char *tag; /* the tag to look up */ +{ + char *tagpath; + char dir[80]; + char *ptr; + int len; + + if (colons == 1) + { + /* looking for static function -- only look in current dir */ + tagpath = "."; + } + else + { + /* get the tagpath from the environment. Default to DEFTAGPATH */ + tagpath = getenv("TAGPATH"); + if (!tagpath) + { + tagpath = DEFTAGPATH; + } + } + + /* for each entry in the path... */ + while (*tagpath) + { + /* Copy the entry into the dir[] buffer */ + for (ptr = dir; *tagpath && *tagpath != SEP; tagpath++) + { + *ptr++ = *tagpath; + } + if (*tagpath == SEP) + { + tagpath++; + } + + /* if the entry ended with "/tags", then strip that off */ + len = strlen(TAGS); + if (&dir[len] < ptr && ptr[-len - 1] == SLASH && !strncmp(&ptr[-len], TAGS, len)) + { + ptr -= len + 1; + } + + /* if the entry is now an empty string, then assume "." */ + if (ptr == dir) + { + *ptr++ = '.'; + } + *ptr = '\0'; + + /* look for the tag in this path. If found, then display it + * and exit. + */ + ptr = cktagdir(tag, dir); + if (ptr) + { + /* just supposed to display tag info? */ + if (taginfo) + { + /* then do only that! */ + if (strcmp(dir, ".")) + { + printf("%s%c%s", dir, SLASH, ptr); + } + else + { + /* avoid leading "./" if possible */ + fputs(ptr, stdout); + } + return 1; + } + else + { + /* else look up the declaration of the thing */ + return lookup(dir, ptr); + } + } + } + + /* if we get here, then the tag wasn't found anywhere */ + return 0; +} + +void usage() +{ + fputs("usage: ref [-a] [-t] [-c class] [-f file] tag\n", stderr); + fputs(" -a function's args may be flush against left margin\n", stderr); + fputs(" -t output tag info, instead of the function header\n", stderr); + fputs(" -f File tag might be a static function in File\n", stderr); + fputs(" -c Class tag might be a member of class Class\n", stderr); + exit(2); +} + + +int countcolons(str) + char *str; +{ + while (*str != ':' && *str) + { + str++; + } + if (str[0] != ':') + { + return 0; + } + else if (str[1] != ':') + { + return 1; + } + return 2; +} + +int main(argc, argv) + int argc; + char **argv; +{ + char def_tag[100]; /* used to build tag name with default file/class */ + int i; + + /* parse flags */ + for (i = 1; i < argc && argv[i][0] == '-'; i++) + { + switch (argv[i][1]) + { + case 't': + taginfo = 1; + break; + + case 'f': + if (argv[i][2]) + { + def_file = &argv[i][2]; + } + else if (++i < argc) + { + def_file = argv[i]; + } + else + { + usage(); + } + break; + + case 'c': + if (argv[i][2]) + { + def_class = &argv[i][2]; + } + else if (++i < argc) + { + def_class = argv[i]; + } + else + { + usage(); + } + break; + + default: + usage(); + } + } + + /* if no tag was given, complain */ + if (i + 1 != argc) + { + usage(); + } + + /* does the tag have an explicit class or file? */ + colons = countcolons(argv[i]); + + /* if not, then maybe try some defaults */ + if (colons == 0) + { + /* try a static function in the file first */ + if (def_file) + { + sprintf(def_tag, "%s:%s", def_file, argv[i]); + colons = 1; + if (find(def_tag)) + { + exit(0); + } + } + + /* try a member function for a class */ + if (def_class) + { + sprintf(def_tag, "%s::%s", def_class, argv[i]); + colons = 2; + if (find(def_tag)) + { + exit(0); + } + } + + /* oh, well */ + colons = 0; + } + + /* find the tag */ + if (find(argv[i])) + { + exit(0); + } + + exit(1); + /*NOTREACHED*/ +} diff --git a/commands/elvis/regexp.c b/commands/elvis/regexp.c new file mode 100755 index 000000000..91db71af8 --- /dev/null +++ b/commands/elvis/regexp.c @@ -0,0 +1,934 @@ +/* regexp.c */ + +/* This file contains the code that compiles regular expressions and executes + * them. It supports the same syntax and features as vi's regular expression + * code. Specifically, the meta characters are: + * ^ matches the beginning of a line + * $ matches the end of a line + * \< matches the beginning of a word + * \> matches the end of a word + * . matches any single character + * [] matches any character in a character class + * \( delimits the start of a subexpression + * \) delimits the end of a subexpression + * * repeats the preceding 0 or more times + * NOTE: You cannot follow a \) with a *. + * + * The physical structure of a compiled RE is as follows: + * - First, there is a one-byte value that says how many character classes + * are used in this regular expression + * - Next, each character class is stored as a bitmap that is 256 bits + * (32 bytes) long. + * - A mixture of literal characters and compiled meta characters follows. + * This begins with M_BEGIN(0) and ends with M_END(0). All meta chars + * are stored as a \n followed by a one-byte code, so they take up two + * bytes apiece. Literal characters take up one byte apiece. \n can't + * be used as a literal character. + * + * If NO_MAGIC is defined, then a different set of functions is used instead. + * That right, this file contains TWO versions of the code. + */ + +#include <setjmp.h> +#include "config.h" +#include "ctype.h" +#include "vi.h" +#include "regexp.h" + + + +static char *previous; /* the previous regexp, used when null regexp is given */ + + +#ifndef NO_MAGIC +/* THE REAL REGEXP PACKAGE IS USED UNLESS "NO_MAGIC" IS DEFINED */ + +/* These are used to classify or recognize meta-characters */ +#define META '\0' +#define BASE_META(m) ((m) - 256) +#define INT_META(c) ((c) + 256) +#define IS_META(m) ((m) >= 256) +#define IS_CLASS(m) ((m) >= M_CLASS(0) && (m) <= M_CLASS(9)) +#define IS_START(m) ((m) >= M_START(0) && (m) <= M_START(9)) +#define IS_END(m) ((m) >= M_END(0) && (m) <= M_END(9)) +#define IS_CLOSURE(m) ((m) >= M_SPLAT && (m) <= M_RANGE) +#define ADD_META(s,m) (*(s)++ = META, *(s)++ = BASE_META(m)) +#define GET_META(s) (*(s) == META ? INT_META(*++(s)) : *s) + +/* These are the internal codes used for each type of meta-character */ +#define M_BEGLINE 256 /* internal code for ^ */ +#define M_ENDLINE 257 /* internal code for $ */ +#define M_BEGWORD 258 /* internal code for \< */ +#define M_ENDWORD 259 /* internal code for \> */ +#define M_ANY 260 /* internal code for . */ +#define M_SPLAT 261 /* internal code for * */ +#define M_PLUS 262 /* internal code for \+ */ +#define M_QMARK 263 /* internal code for \? */ +#define M_RANGE 264 /* internal code for \{ */ +#define M_CLASS(n) (265+(n)) /* internal code for [] */ +#define M_START(n) (275+(n)) /* internal code for \( */ +#define M_END(n) (285+(n)) /* internal code for \) */ + +/* These are used during compilation */ +static int class_cnt; /* used to assign class IDs */ +static int start_cnt; /* used to assign start IDs */ +static int end_stk[NSUBEXP];/* used to assign end IDs */ +static int end_sp; +static char *retext; /* points to the text being compiled */ + +/* error-handling stuff */ +jmp_buf errorhandler; +#define FAIL(why) regerror(why); longjmp(errorhandler, 1) + + + + + +/* This function builds a bitmap for a particular class */ +static char *makeclass(text, bmap) + REG char *text; /* start of the class */ + REG char *bmap; /* the bitmap */ +{ + REG int i; + int complement = 0; + + + /* zero the bitmap */ + for (i = 0; bmap && i < 32; i++) + { + bmap[i] = 0; + } + + /* see if we're going to complement this class */ + if (*text == '^') + { + text++; + complement = 1; + } + + /* add in the characters */ + while (*text && *text != ']') + { + /* is this a span of characters? */ + if (text[1] == '-' && text[2]) + { + /* spans can't be backwards */ + if (text[0] > text[2]) + { + FAIL("Backwards span in []"); + } + + /* add each character in the span to the bitmap */ + for (i = text[0]; bmap && i <= text[2]; i++) + { + bmap[i >> 3] |= (1 << (i & 7)); + } + + /* move past this span */ + text += 3; + } + else + { + /* add this single character to the span */ + i = *text++; + if (bmap) + { + bmap[i >> 3] |= (1 << (i & 7)); + } + } + } + + /* make sure the closing ] is missing */ + if (*text++ != ']') + { + FAIL("] missing"); + } + + /* if we're supposed to complement this class, then do so */ + if (complement && bmap) + { + for (i = 0; i < 32; i++) + { + bmap[i] = ~bmap[i]; + } + } + + return text; +} + + + + +/* This function gets the next character or meta character from a string. + * The pointer is incremented by 1, or by 2 for \-quoted characters. For [], + * a bitmap is generated via makeclass() (if re is given), and the + * character-class text is skipped. + */ +static int gettoken(sptr, re) + char **sptr; + regexp *re; +{ + int c; + + c = **sptr; + ++*sptr; + if (c == '\\') + { + c = **sptr; + ++*sptr; + switch (c) + { + case '<': + return M_BEGWORD; + + case '>': + return M_ENDWORD; + + case '(': + if (start_cnt >= NSUBEXP) + { + FAIL("Too many \\(s"); + } + end_stk[end_sp++] = start_cnt; + return M_START(start_cnt++); + + case ')': + if (end_sp <= 0) + { + FAIL("Mismatched \\)"); + } + return M_END(end_stk[--end_sp]); + + case '*': + return (*o_magic ? c : M_SPLAT); + + case '.': + return (*o_magic ? c : M_ANY); + + case '+': + return M_PLUS; + + case '?': + return M_QMARK; +#ifndef CRUNCH + case '{': + return M_RANGE; +#endif + default: + return c; + } + } + else if (*o_magic) + { + switch (c) + { + case '^': + if (*sptr == retext + 1) + { + return M_BEGLINE; + } + return c; + + case '$': + if (!**sptr) + { + return M_ENDLINE; + } + return c; + + case '.': + return M_ANY; + + case '*': + return M_SPLAT; + + case '[': + /* make sure we don't have too many classes */ + if (class_cnt >= 10) + { + FAIL("Too many []s"); + } + + /* process the character list for this class */ + if (re) + { + /* generate the bitmap for this class */ + *sptr = makeclass(*sptr, re->program + 1 + 32 * class_cnt); + } + else + { + /* skip to end of the class */ + *sptr = makeclass(*sptr, (char *)0); + } + return M_CLASS(class_cnt++); + + default: + return c; + } + } + else /* unquoted nomagic */ + { + switch (c) + { + case '^': + if (*sptr == retext + 1) + { + return M_BEGLINE; + } + return c; + + case '$': + if (!**sptr) + { + return M_ENDLINE; + } + return c; + + default: + return c; + } + } + /*NOTREACHED*/ +} + + + + +/* This function calculates the number of bytes that will be needed for a + * compiled RE. Its argument is the uncompiled version. It is not clever + * about catching syntax errors; that is done in a later pass. + */ +static unsigned calcsize(text) + char *text; +{ + unsigned size; + int token; + + retext = text; + class_cnt = 0; + start_cnt = 1; + end_sp = 0; + size = 5; + while ((token = gettoken(&text, (regexp *)0)) != 0) + { + if (IS_CLASS(token)) + { + size += 34; + } +#ifndef CRUNCH + else if (token == M_RANGE) + { + size += 4; + while ((token = gettoken(&text, (regexp *)0)) != 0 + && token != '}') + { + } + if (!token) + { + return size; + } + } +#endif + else if (IS_META(token)) + { + size += 2; + } + else + { + size++; + } + } + + return size; +} + + + +/* This function compiles a regexp. */ +regexp *regcomp(exp) + char *exp; +{ + int needfirst; + unsigned size; + int token; + int peek; + char *build; + regexp *re; +#ifndef CRUNCH + int from; + int to; + int digit; +#endif + + + /* prepare for error handling */ + re = (regexp *)0; + if (setjmp(errorhandler)) + { + if (re) + { + free(re); + } + return (regexp *)0; + } + + /* if an empty regexp string was given, use the previous one */ + if (*exp == 0) + { + if (!previous) + { + FAIL("No previous RE"); + } + exp = previous; + } + else /* non-empty regexp given, so remember it */ + { + if (previous) + free(previous); + previous = (char *)malloc((unsigned)(strlen(exp) + 1)); + if (previous) + strcpy(previous, exp); + } + + /* allocate memory */ + class_cnt = 0; + start_cnt = 1; + end_sp = 0; + retext = exp; + size = calcsize(exp) + sizeof(regexp) + 10; /* !!! 10 bytes for slop */ +#ifdef lint + re = ((regexp *)0) + size; +#else + re = (regexp *)malloc((unsigned)size); +#endif + if (!re) + { + FAIL("Not enough memory for this RE"); + } + + /* compile it */ + build = &re->program[1 + 32 * class_cnt]; + re->program[0] = class_cnt; + for (token = 0; token < NSUBEXP; token++) + { + re->startp[token] = re->endp[token] = (char *)0; + } + re->first = 0; + re->bol = 0; + re->minlen = 0; + needfirst = 1; + class_cnt = 0; + start_cnt = 1; + end_sp = 0; + retext = exp; + for (token = M_START(0), peek = gettoken(&exp, re); + token; + token = peek, peek = gettoken(&exp, re)) + { + /* special processing for the closure operator */ + if (IS_CLOSURE(peek)) + { + /* detect misuse of closure operator */ + if (IS_START(token)) + { + FAIL("Closure operator follows nothing"); + } + else if (IS_META(token) && token != M_ANY && !IS_CLASS(token)) + { + FAIL("Closure operators can only follow a normal character or . or []"); + } + +#ifndef CRUNCH + /* if \{ \} then read the range */ + if (peek == M_RANGE) + { + from = 0; + for (digit = gettoken(&exp, re); + !IS_META(digit) && isdigit(digit); + digit = gettoken(&exp, re)) + { + from = from * 10 + digit - '0'; + } + if (digit == '}') + { + to = from; + } + else if (digit == ',') + { + to = 0; + for (digit = gettoken(&exp, re); + !IS_META(digit) && isdigit(digit); + digit = gettoken(&exp, re)) + { + to = to * 10 + digit - '0'; + } + if (to == 0) + { + to = 255; + } + } + if (digit != '}') + { + FAIL("Bad characters after \\{"); + } + else if (to < from || to == 0 || from >= 255) + { + FAIL("Invalid range for \\{ \\}"); + } + re->minlen += from; + } + else +#endif + if (peek != M_SPLAT) + { + re->minlen++; + } + + /* it is okay -- make it prefix instead of postfix */ + ADD_META(build, peek); +#ifndef CRUNCH + if (peek == M_RANGE) + { + *build++ = from; + *build++ = (to < 255 ? to : 255); + } +#endif + + + /* take care of "needfirst" - is this the first char? */ + if (needfirst && peek == M_PLUS && !IS_META(token)) + { + re->first = token; + } + needfirst = 0; + + /* we used "peek" -- need to refill it */ + peek = gettoken(&exp, re); + if (IS_CLOSURE(peek)) + { + FAIL("* or \\+ or \\? doubled up"); + } + } + else if (!IS_META(token)) + { + /* normal char is NOT argument of closure */ + if (needfirst) + { + re->first = token; + needfirst = 0; + } + re->minlen++; + } + else if (token == M_ANY || IS_CLASS(token)) + { + /* . or [] is NOT argument of closure */ + needfirst = 0; + re->minlen++; + } + + /* the "token" character is not closure -- process it normally */ + if (token == M_BEGLINE) + { + /* set the BOL flag instead of storing M_BEGLINE */ + re->bol = 1; + } + else if (IS_META(token)) + { + ADD_META(build, token); + } + else + { + *build++ = token; + } + } + + /* end it with a \) which MUST MATCH the opening \( */ + ADD_META(build, M_END(0)); + if (end_sp > 0) + { + FAIL("Not enough \\)s"); + } + + return re; +} + + + +/*---------------------------------------------------------------------------*/ + + +/* This function checks for a match between a character and a token which is + * known to represent a single character. It returns 0 if they match, or + * 1 if they don't. + */ +int match1(re, ch, token) + regexp *re; + REG char ch; + REG int token; +{ + if (!ch) + { + /* the end of a line can't match any RE of width 1 */ + return 1; + } + if (token == M_ANY) + { + return 0; + } + else if (IS_CLASS(token)) + { + if (re->program[1 + 32 * (token - M_CLASS(0)) + (ch >> 3)] & (1 << (ch & 7))) + return 0; + } + else if (ch == token || *o_ignorecase && tolower(ch) == tolower(token)) + { + return 0; + } + return 1; +} + + + +/* This function checks characters up to and including the next closure, at + * which point it does a recursive call to check the rest of it. This function + * returns 0 if everything matches, or 1 if something doesn't match. + */ +int match(re, str, prog, here) + regexp *re; /* the regular expression */ + char *str; /* the string */ + REG char *prog; /* a portion of re->program, an compiled RE */ + REG char *here; /* a portion of str, the string to compare it to */ +{ + REG int token; /* the roken pointed to by prog */ + REG int nmatched;/* counter, used during closure matching */ + REG int closure;/* the token denoting the type of closure */ + int from; /* minimum number of matches in closure */ + int to; /* maximum number of matches in closure */ + + for (token = GET_META(prog); !IS_CLOSURE(token); prog++, token = GET_META(prog)) + { + switch (token) + { + /*case M_BEGLINE: can't happen; re->bol is used instead */ + case M_ENDLINE: + if (*here) + return 1; + break; + + case M_BEGWORD: + if (here != str && + (here[-1] == '_' || isalnum(here[-1]))) + return 1; + break; + + case M_ENDWORD: + if (here[0] == '_' || isalnum(here[0])) + return 1; + break; + + case M_START(0): + case M_START(1): + case M_START(2): + case M_START(3): + case M_START(4): + case M_START(5): + case M_START(6): + case M_START(7): + case M_START(8): + case M_START(9): + re->startp[token - M_START(0)] = (char *)here; + break; + + case M_END(0): + case M_END(1): + case M_END(2): + case M_END(3): + case M_END(4): + case M_END(5): + case M_END(6): + case M_END(7): + case M_END(8): + case M_END(9): + re->endp[token - M_END(0)] = (char *)here; + if (token == M_END(0)) + { + return 0; + } + break; + + default: /* literal, M_CLASS(n), or M_ANY */ + if (match1(re, *here, token) != 0) + return 1; + here++; + } + } + + /* C L O S U R E */ + + /* step 1: see what we have to match against, and move "prog" to point + * to the remainder of the compiled RE. + */ + closure = token; + prog++; + switch (closure) + { + case M_SPLAT: + from = 0; + to = strlen(str); /* infinity */ + break; + + case M_PLUS: + from = 1; + to = strlen(str); /* infinity */ + break; + + case M_QMARK: + from = 0; + to = 1; + break; + +#ifndef CRUNCH + case M_RANGE: + from = UCHAR(*prog++); + to = UCHAR(*prog++); + if (to == 255) + { + to = strlen(str); /* infinity */ + } + break; +#endif + } + token = GET_META(prog); + prog++; + + /* step 2: see how many times we can match that token against the string */ + for (nmatched = 0; + nmatched < to && *here && match1(re, *here, token) == 0; + nmatched++, here++) + { + } + + /* step 3: try to match the remainder, and back off if it doesn't */ + while (nmatched >= from && match(re, str, prog, here) != 0) + { + nmatched--; + here--; + } + + /* so how did it work out? */ + if (nmatched >= from) + return 0; + return 1; +} + + + +/* This function searches through a string for text that matches an RE. */ +int regexec(re, str, bol) + regexp *re; /* the compiled regexp to search for */ + char *str; /* the string to search through */ + int bol; /* boolean: does str start at the beginning of a line? */ +{ + char *prog; /* the entry point of re->program */ + int len; /* length of the string */ + REG char *here; + + /* if must start at the beginning of a line, and this isn't, then fail */ + if (re->bol && !bol) + { + return 0; + } + + len = strlen(str); + prog = re->program + 1 + 32 * re->program[0]; + + /* search for the RE in the string */ + if (re->bol) + { + /* must occur at BOL */ + if ((re->first + && match1(re, *(char *)str, re->first))/* wrong first letter? */ + || len < re->minlen /* not long enough? */ + || match(re, (char *)str, prog, str)) /* doesn't match? */ + return 0; /* THEN FAIL! */ + } +#ifndef CRUNCH + else if (!*o_ignorecase) + { + /* can occur anywhere in the line, noignorecase */ + for (here = (char *)str; + (re->first && re->first != *here) + || match(re, (char *)str, prog, here); + here++, len--) + { + if (len < re->minlen) + return 0; + } + } +#endif + else + { + /* can occur anywhere in the line, ignorecase */ + for (here = (char *)str; + (re->first && match1(re, *here, (int)re->first)) + || match(re, (char *)str, prog, here); + here++, len--) + { + if (len < re->minlen) + return 0; + } + } + + /* if we didn't fail, then we must have succeeded */ + return 1; +} + +/*============================================================================*/ +#else /* NO_MAGIC */ + +regexp *regcomp(exp) + char *exp; +{ + char *src; + char *dest; + regexp *re; + int i; + + /* allocate a big enough regexp structure */ +#ifdef lint + re = (regexp *)0; +#else + re = (regexp *)malloc((unsigned)(strlen(exp) + 1 + sizeof(struct regexp))); +#endif + if (!re) + { + regerror("Could not malloc a regexp structure"); + return (regexp *)0; + } + + /* initialize all fields of the structure */ + for (i = 0; i < NSUBEXP; i++) + { + re->startp[i] = re->endp[i] = (char *)0; + } + re->minlen = 0; + re->first = 0; + re->bol = 0; + + /* copy the string into it, translating ^ and $ as needed */ + for (src = exp, dest = re->program + 1; *src; src++) + { + switch (*src) + { + case '^': + if (src == exp) + { + re->bol += 1; + } + else + { + *dest++ = '^'; + re->minlen++; + } + break; + + case '$': + if (!src[1]) + { + re->bol += 2; + } + else + { + *dest++ = '$'; + re->minlen++; + } + break; + + case '\\': + if (src[1]) + { + *dest++ = *++src; + re->minlen++; + } + else + { + regerror("extra \\ at end of regular expression"); + } + break; + + default: + *dest++ = *src; + re->minlen++; + } + } + *dest = '\0'; + + return re; +} + + +/* This "helper" function checks for a match at a given location. It returns + * 1 if it matches, 0 if it doesn't match here but might match later on in the + * string, or -1 if it could not possibly match + */ +static int reghelp(prog, string, bolflag) + struct regexp *prog; + char *string; + int bolflag; +{ + char *scan; + char *str; + + /* if ^, then require bolflag */ + if ((prog->bol & 1) && !bolflag) + { + return -1; + } + + /* if it matches, then it will start here */ + prog->startp[0] = string; + + /* compare, possibly ignoring case */ + if (*o_ignorecase) + { + for (scan = &prog->program[1]; *scan; scan++, string++) + if (tolower(*scan) != tolower(*string)) + return *string ? 0 : -1; + } + else + { + for (scan = &prog->program[1]; *scan; scan++, string++) + if (*scan != *string) + return *string ? 0 : -1; + } + + /* if $, then require string to end here, too */ + if ((prog->bol & 2) && *string) + { + return 0; + } + + /* if we get to here, it matches */ + prog->endp[0] = string; + return 1; +} + + + +int regexec(prog, string, bolflag) + struct regexp *prog; + char *string; + int bolflag; +{ + int rc; + + /* keep trying to match it */ + for (rc = reghelp(prog, string, bolflag); rc == 0; rc = reghelp(prog, string, 0)) + { + string++; + } + + /* did we match? */ + return rc == 1; +} +#endif diff --git a/commands/elvis/regexp.h b/commands/elvis/regexp.h new file mode 100755 index 000000000..6d043b027 --- /dev/null +++ b/commands/elvis/regexp.h @@ -0,0 +1,21 @@ +/* + * Definitions etc. for regexp(3) routines. + * + * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof], + * not the System V one. + */ +#define NSUBEXP 10 + +typedef struct regexp { + char *startp[NSUBEXP]; + char *endp[NSUBEXP]; + int minlen; /* length of shortest possible match */ + char first; /* first character, if known; else \0 */ + char bol; /* boolean: must start at beginning of line? */ + char program[1]; /* Unwarranted chumminess with compiler. */ +} regexp; + +extern regexp *regcomp(); +extern int regexec(); +extern void regsub(); +extern void regerror(); diff --git a/commands/elvis/regsub.c b/commands/elvis/regsub.c new file mode 100755 index 000000000..d9ea1a153 --- /dev/null +++ b/commands/elvis/regsub.c @@ -0,0 +1,243 @@ +/* regsub.c */ + +/* This file contains the regsub() function, which performs substitutions + * after a regexp match has been found. + */ + +#include "config.h" +#include "ctype.h" +#include "vi.h" +#include "regexp.h" + + +/* perform substitutions after a regexp match */ +void regsub(re, src, dst) + regexp *re; /* the regexp with pointers into matched text */ + REG char *src; /* the replacement string */ + REG char *dst; /* where to put the result of the subst */ +{ + REG char *cpy; /* pointer to start of text to copy */ + REG char *end; /* pointer to end of text to copy */ + REG char c; + char *start; +#ifndef CRUNCH + int mod = 0;/* used to track \U, \L, \u, \l, and \E */ + int len; /* used to calculate length of subst string */ + static char *prev; /* a copy of the text from the previous subst */ + + /* replace \~ (or maybe ~) by previous substitution text */ + + /* step 1: calculate the length of the new substitution text */ + for (len = strlen(src), c = '\0', cpy = src; *cpy; cpy++) + { +# ifdef NO_MAGIC + if (c == '\\' && *cpy == '~') +# else + if (c == (*o_magic ? '\0' : '\\') && *cpy == '~') +# endif + { + if (!prev) + { + regerror("No prev text to substitute for ~"); + return; + } + len += strlen(prev) - 1; +# ifndef NO_MAGIC + if (!*o_magic) +# endif + len -= 1; /* because we lose the \ too */ + } + + /* watch backslash quoting */ + if (c != '\\' && *cpy == '\\') + c = '\\'; + else + c = '\0'; + } + + /* allocate memory for the ~ed version of src */ + start = cpy = (char *)malloc((unsigned)(len + 1)); + if (!cpy) + { + regerror("Not enough memory for ~ expansion"); + return; + } + + /* copy src into start, replacing the ~s by the previous text */ + while (*src) + { +# ifndef NO_MAGIC + if (*o_magic && *src == '~') + { + strcpy(cpy, prev); + cpy += strlen(prev); + src++; + } + else if (!*o_magic && *src == '\\' && *(src + 1) == '~') +# else /* NO_MAGIC */ + if (*src == '\\' && *(src + 1) == '~') +# endif /* NO_MAGIC */ + { + strcpy(cpy, prev); + cpy += strlen(prev); + src += 2; + } + else + { + *cpy++ = *src++; + } + } + *cpy = '\0'; +#ifdef DEBUG + if ((int)(cpy - start) != len) + { + msg("Bug in regsub.c! Predicted length = %d, Actual length = %d", len, (int)(cpy - start)); + } +#endif + + /* remember this as the "previous" for next time */ + if (prev) + free(prev); + prev = src = start; + +#endif /* undef CRUNCH */ + + start = src; + while ((c = *src++) != '\0') + { +#ifndef NO_MAGIC + /* recognize any meta characters */ + if (c == '&' && *o_magic) + { + cpy = re->startp[0]; + end = re->endp[0]; + } + else +#endif /* not NO_MAGIC */ + if (c == '\\') + { + c = *src++; + switch (c) + { +#ifndef NO_MAGIC + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + /* \0 thru \9 mean "copy subexpression" */ + c -= '0'; + cpy = re->startp[c]; + end = re->endp[c]; + break; +# ifndef CRUNCH + case 'U': + case 'u': + case 'L': + case 'l': + /* \U and \L mean "convert to upper/lowercase" */ + mod = c; + continue; + + case 'E': + case 'e': + /* \E ends the \U or \L */ + mod = 0; + continue; +# endif /* not CRUNCH */ + case '&': + /* "\&" means "original text" */ + if (*o_magic) + { + *dst++ = c; + continue; + } + cpy = re->startp[0]; + end = re->endp[0]; + break; + +#else /* NO_MAGIC */ + case '&': + /* "\&" means "original text" */ + cpy = re->startp[0]; + end = re->endp[0]; + break; +#endif /* NO_MAGIC */ + default: + /* ordinary char preceded by backslash */ + *dst++ = c; + continue; + } + } +#ifndef CRUNCH +# if OSK + else if (c == '\l') +# else + else if (c == '\r') +# endif + { + /* transliterate ^M into newline */ + *dst++ = '\n'; + continue; + } +#endif /* !CRUNCH */ + else + { + /* ordinary character, so just copy it */ + *dst++ = c; + continue; + } + + /* Note: to reach this point in the code, we must have evaded + * all "continue" statements. To do that, we must have hit + * a metacharacter that involves copying. + */ + + /* if there is nothing to copy, loop */ + if (!cpy) + continue; + + /* copy over a portion of the original */ + while (cpy < end) + { +#ifndef NO_MAGIC +# ifndef CRUNCH + switch (mod) + { + case 'U': + case 'u': + /* convert to uppercase */ + *dst++ = toupper(*cpy++); + break; + + case 'L': + case 'l': + /* convert to lowercase */ + *dst++ = tolower(*cpy++); + break; + + default: + /* copy without any conversion */ + *dst++ = *cpy++; + } + + /* \u and \l end automatically after the first char */ + if (mod && (mod == 'u' || mod == 'l')) + { + mod = 0; + } +# else /* CRUNCH */ + *dst++ = *cpy++; +# endif /* CRUNCH */ +#else /* NO_MAGIC */ + *dst++ = *cpy++; +#endif /* NO_MAGIC */ + } + } + *dst = '\0'; +} diff --git a/commands/elvis/system.c b/commands/elvis/system.c new file mode 100755 index 000000000..676d788c1 --- /dev/null +++ b/commands/elvis/system.c @@ -0,0 +1,423 @@ +/* system.c -- UNIX version */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains a new version of the system() function and related stuff. + * + * Entry points are: + * system(cmd) - run a single shell command + * wildcard(names) - expand wildcard characters in filanames + * filter(m,n,cmd,back) - run text lines through a filter program + * + * This is probably the single least portable file in the program. The code + * shown here should work correctly if it links at all; it will work on UNIX + * and any O.S./Compiler combination which adheres to UNIX forking conventions. + */ + +#include "config.h" +#include "vi.h" +extern char **environ; + +#if ANY_UNIX + +/* This is a new version of the system() function. The only difference + * between this one and the library one is: this one uses the o_shell option. + */ +int system(cmd) + char *cmd; /* a command to run */ +{ + int pid; /* process ID of child */ + int died; + int status; /* exit status of the command */ + + + signal(SIGINT, SIG_IGN); + pid = fork(); + switch (pid) + { + case -1: /* error */ + msg("fork() failed"); + status = -1; + break; + + case 0: /* child */ + /* for the child, close all files except stdin/out/err */ + for (status = 3; status < 60 && (close(status), errno != EINVAL); status++) + { + } + + signal(SIGINT, SIG_DFL); + if (cmd == o_shell) + { + execle(o_shell, o_shell, (char *)0, environ); + } + else + { + execle(o_shell, o_shell, "-c", cmd, (char *)0, environ); + } + msg("execle(\"%s\", ...) failed", o_shell); + exit(1); /* if we get here, the exec failed */ + + default: /* parent */ + do + { + died = wait(&status); + } while (died >= 0 && died != pid); + if (died < 0) + { + status = -1; + } +#if __GNUC__ || _ANSI + signal(SIGINT, (void (*)()) trapint); +#else + signal(SIGINT, trapint); +#endif + } + + return status; +} + +/* This private function opens a pipe from a filter. It is similar to the + * system() function above, and to popen(cmd, "r"). + */ +int rpipe(cmd, in) + char *cmd; /* the filter command to use */ + int in; /* the fd to use for stdin */ +{ + int r0w1[2];/* the pipe fd's */ + + /* make the pipe */ + if (pipe(r0w1) < 0) + { + return -1; /* pipe failed */ + } + + /* The parent process (elvis) ignores signals while the filter runs. + * The child (the filter program) will reset this, so that it can + * catch the signal. + */ + signal(SIGINT, SIG_IGN); + + switch (fork()) + { + case -1: /* error */ + return -1; + + case 0: /* child */ + /* close the "read" end of the pipe */ + close(r0w1[0]); + + /* redirect stdout to go to the "write" end of the pipe */ + close(1); + dup(r0w1[1]); + close(2); + dup(r0w1[1]); + close(r0w1[1]); + + /* redirect stdin */ + if (in != 0) + { + close(0); + dup(in); + close(in); + } + + /* the filter should accept SIGINT signals */ + signal(SIGINT, SIG_DFL); + + /* exec the shell to run the command */ + execle(o_shell, o_shell, "-c", cmd, (char *)0, environ); + exit(1); /* if we get here, exec failed */ + + default: /* parent */ + /* close the "write" end of the pipe */ + close(r0w1[1]); + + return r0w1[0]; + } +} + +#endif /* non-DOS */ + +#if OSK + +/* This private function opens a pipe from a filter. It is similar to the + * system() function above, and to popen(cmd, "r"). + */ +int rpipe(cmd, in) + char *cmd; /* the filter command to use */ + int in; /* the fd to use for stdin */ +{ + return osk_popen(cmd, "r", in, 0); +} +#endif + +#if ANY_UNIX || OSK + +/* This function closes the pipe opened by rpipe(), and returns 0 for success */ +int rpclose(fd) + int fd; +{ + int status; + + close(fd); + wait(&status); +#if __GNUC__ || _ANSI + signal(SIGINT, (void (*)()) trapint); +#else + signal(SIGINT, trapint); +#endif + return status; +} + +#endif /* non-DOS */ + +/* This function expands wildcards in a filename or filenames. It does this + * by running the "echo" command on the filenames via the shell; it is assumed + * that the shell will expand the names for you. If for any reason it can't + * run echo, then it returns the names unmodified. + */ + +#if MSDOS || TOS +#define PROG "wildcard " +#define PROGLEN 9 +#include <string.h> +#else +#define PROG "echo " +#define PROGLEN 5 +#endif + +#if !AMIGA +char *wildcard(names) + char *names; +{ + +# if VMS +/* + We could use expand() [vmswild.c], but what's the point on VMS? + Anyway, echo is the wrong thing to do, it takes too long to build + a subprocess on VMS and any "echo" program would have to be supplied + by elvis. More importantly, many VMS utilities expand names + themselves (the shell doesn't do any expansion) so the concept is + non-native. jdc +*/ + return names; +# else + + int i, j, fd; + REG char *s, *d; + + + /* build the echo command */ + if (names != tmpblk.c) + { + /* the names aren't in tmpblk.c, so we can do it the easy way */ + strcpy(tmpblk.c, PROG); + strcat(tmpblk.c, names); + } + else + { + /* the names are already in tmpblk.c, so shift them to make + * room for the word "echo " + */ + for (s = names + strlen(names) + 1, d = s + PROGLEN; s > names; ) + { + *--d = *--s; + } + strncpy(names, PROG, PROGLEN); + } + + /* run the command & read the resulting names */ + fd = rpipe(tmpblk.c, 0); + if (fd < 0) return names; + i = 0; + do + { + j = tread(fd, tmpblk.c + i, BLKSIZE - i); + i += j; + } while (j > 0); + + /* successful? */ + if (rpclose(fd) == 0 && j == 0 && i < BLKSIZE && i > 0) + { + tmpblk.c[i-1] = '\0'; /* "i-1" so we clip off the newline */ + return tmpblk.c; + } + else + { + return names; + } +# endif +} +#endif + +/* This function runs a range of lines through a filter program, and replaces + * the original text with the filtered version. As a special case, if "to" + * is MARK_UNSET, then it runs the filter program with stdin coming from + * /dev/null, and inserts any output lines. + */ +int filter(from, to, cmd, back) + MARK from, to; /* the range of lines to filter */ + char *cmd; /* the filter command */ + int back; /* boolean: will we read lines back? */ +{ + int scratch; /* fd of the scratch file */ + int fd; /* fd of the pipe from the filter */ + char scrout[50]; /* name of the scratch out file */ + MARK new; /* place where new text should go */ + long sent, rcvd; /* number of lines sent/received */ + int i, j; + + /* write the lines (if specified) to a temp file */ + if (to) + { + /* we have lines */ +#if MSDOS || TOS + strcpy(scrout, o_directory); + if ((i=strlen(scrout)) && !strchr("\\/:", scrout[i-1])) + scrout[i++]=SLASH; + strcpy(scrout+i, SCRATCHOUT+3); +#else + sprintf(scrout, SCRATCHOUT, o_directory); +#endif + mktemp(scrout); + cmd_write(from, to, CMD_BANG, FALSE, scrout); + sent = markline(to) - markline(from) + 1L; + + /* use those lines as stdin */ + scratch = open(scrout, O_RDONLY); + if (scratch < 0) + { + unlink(scrout); + return -1; + } + } + else + { + scratch = 0; + sent = 0L; + } + + /* start the filter program */ +#if VMS + /* + VMS doesn't know a thing about file descriptor 0. The rpipe + concept is non-portable. Hence we need a file name argument. + */ + fd = rpipe(cmd, scratch, scrout); +#else + fd = rpipe(cmd, scratch); +#endif + if (fd < 0) + { + if (to) + { + close(scratch); + unlink(scrout); + } + return -1; + } + + if (back) + { + ChangeText + { + /* adjust MARKs for whole lines, and set "new" */ + from &= ~(BLKSIZE - 1); + if (to) + { + to &= ~(BLKSIZE - 1); + to += BLKSIZE; + new = to; + } + else + { + new = from + BLKSIZE; + } + +#if VMS +/* Reading from a VMS mailbox (pipe) is record oriented... */ +# define tread vms_pread +#endif + + /* repeatedly read in new text and add it */ + rcvd = 0L; + while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0) + { + tmpblk.c[i] = '\0'; + add(new, tmpblk.c); +#if VMS + /* What! An advantage to record oriented reads? */ + new += (i - 1); + new = (new & ~(BLKSIZE - 1)) + BLKSIZE; + rcvd++; +#else + for (i = 0; tmpblk.c[i]; i++) + { + if (tmpblk.c[i] == '\n') + { + new = (new & ~(BLKSIZE - 1)) + BLKSIZE; + rcvd++; + } + else + { + new++; + } + } +#endif + } + } + + /* delete old text, if any */ + if (to) + { + cut(from, to); + delete(from, to); + } + } + else + { + /* read the command's output, and copy it to the screen */ + while ((i = tread(fd, tmpblk.c, BLKSIZE - 1)) > 0) + { + for (j = 0; j < i; j++) + { + addch(tmpblk.c[j]); + } + } + rcvd = 0; + } + + /* Reporting... */ + if (sent >= *o_report || rcvd >= *o_report) + { + if (sent > 0L && rcvd > 0L) + { + msg("%ld lines out, %ld lines back", sent, rcvd); + } + else if (sent > 0) + { + msg("%ld lines written to filter", sent); + } + else + { + msg("%ld lines read from filter", rcvd); + } + } + rptlines = 0L; + + /* cleanup */ + rpclose(fd); + if (to) + { + close(scratch); + unlink(scrout); + } + return 0; +} diff --git a/commands/elvis/tinyprnt.c b/commands/elvis/tinyprnt.c new file mode 100755 index 000000000..c67add651 --- /dev/null +++ b/commands/elvis/tinyprnt.c @@ -0,0 +1,230 @@ +/* tinyprnt.c */ + +#if OSK +#define sprintf Sprintf +#endif + +/* This is a limited version of sprintf(). It is useful for Minix-PC and + * Coherent-286 because those systems are both limited to 64k+64k and the + * standard sprintf() is just too damn big. + * + * It should also be useful for OS-9 because OS-9's sprintf() doesn't + * understand the true meaning of asterisks in a format string. This one + * does. + */ + +/* Place-holders in format strings look like "%<pad><clip><type>". + * + * The <pad> adds space to the front (or, if negative, to the back) of the + * output value, to pad it to a given width. If <pad> is absent, then 0 is + * assumed. If <pad> is an asterisk, then the next argument is assumed to + * be an (int) which used as the pad width. + * + * The <clip> string can be absent, in which case no clipping is done. + * However, if it is present, then it should be either a "." followed by + * a number, or a "." followed by an asterisk. The asterisk means that the + * next argument is an (int) which should be used as the pad width. Clipping + * only affects strings; for other data types it is ignored. + * + * The <type> is one of "s" for strings, "c" for characters (really ints that + * are assumed to be legal char values), "d" for ints, "ld" for long ints, or + * "%" to output a percent sign. + */ + +/* NOTE: Variable argument lists are handled by direct stack-twiddling. Sorry! */ + +static void cvtnum(buf, num, base) + char *buf; /* where to store the number */ + unsigned long num; /* the number to convert */ + int base; /* either 8, 10, or 16 */ +{ + static char digits[] = "0123456789abcdef"; + unsigned long tmp; + + /* if the number is 0, then just stuff a "0" into the buffer */ + if (num == 0L) + { + buf[0] = '0'; + buf[1] = '\0'; + return; + } + + /* use tmp to figure out how many digits we'll need */ + for (tmp = num; tmp > 0; tmp /= base) + { + buf++; + } + + /* mark the spot that will be the end of the string */ + *buf = '\0'; + + /* generate all digits, as needed */ + for (tmp = num; tmp > 0; tmp /= base) + { + *--buf = digits[tmp % base]; + } +} + +int sprintf(buf, fmt, argref) + char *buf; /* where to deposit the formatted output */ + char *fmt; /* the format string */ + int argref; /* the first argument is located at &argref */ +{ + char *argptr;/* pointer to next argument on the stack */ + int pad; /* value of the pad string */ + int clip; /* value of the clip string */ + long num; /* a binary number being converted to ASCII digits */ + long digit; /* used during conversion */ + char *src, *dst; + + /* make argptr point to the first argument after the format string */ + argptr = (char *)&argref; + + /* loop through the whole format string */ + while (*fmt) + { + /* if not part of a place-holder, then copy it literally */ + if (*fmt != '%') + { + *buf++ = *fmt++; + continue; + } + + /* found a place-holder! Get <pad> value */ + fmt++; + if ('*' == *fmt) + { + pad = *((int *)argptr)++; + fmt++; + } + else if (*fmt == '-' || (*fmt >= '0' && *fmt <= '9')) + { + pad = atol(fmt); + do + { + fmt++; + } while (*fmt >= '0' && *fmt <= '9'); + } + else + { + pad = 0; + } + + /* get a <clip> value */ + if (*fmt == '.') + { + fmt++; + if ('*' == *fmt) + { + clip = *((int *)argptr)++; + fmt++; + } + else if (*fmt >= '0' && *fmt <= '9') + { + clip = atol(fmt); + do + { + fmt++; + } while (*fmt >= '0' && *fmt <= '9'); + } + } + else + { + clip = 0; + } + + /* handle <type>, possibly noticing <clip> */ + switch (*fmt++) + { + case 'c': + buf[0] = *((int *)argptr)++; + buf[1] = '\0'; + break; + + case 's': + src = *((char **)argptr)++; + if (!src) + { + src = "(null)"; + } + if (clip) + { + strncpy(buf, src, clip); + buf[clip] = '\0'; + } + else + { + strcpy(buf, src); + } + break; + + case 'l': + fmt++; /* to skip the "d" in "%ld" */ + num = *((long *)argptr)++; + dst = buf; + if (num < 0) + { + *dst++ = '-'; + num = -num; + } + cvtnum(dst, num, 10); + break; + + case 'x': + num = *((int *)argptr)++; + cvtnum(buf, num, 16); + break; + + case 'd': + num = *((int *)argptr)++; + dst = buf; + if (num < 0) + { + *dst++ = '-'; + num = -num; + } + cvtnum(dst, num, 10); + break; + + default: + buf[0] = fmt[-1]; + buf[1] = '\0'; + } + + /* now fix the padding, if the value is too short */ + clip = strlen(buf); + if (pad < 0) + { + /* add spaces after the value */ + pad = -pad - clip; + for (buf += clip; pad > 0; pad--) + { + *buf++ = ' '; + } + *buf = '\0'; + } + else + { + /* add spaces before the value */ + pad -= clip; + if (pad > 0) + { + src = buf + clip; + dst = src + pad; + *dst = '\0'; + while (src > buf) + { + *--dst = *--src; + } + while (dst > buf) + { + *--dst = ' '; + } + } + buf += strlen(buf); + } + } + + /* mark the end of the output string */ + *buf = '\0'; +} diff --git a/commands/elvis/tinytcap.c b/commands/elvis/tinytcap.c new file mode 100755 index 000000000..922260fe9 --- /dev/null +++ b/commands/elvis/tinytcap.c @@ -0,0 +1,388 @@ +/* tinytcap.c */ + +/* This file contains functions which simulate the termcap functions. + * + * It doesn't access a "termcap" file. Instead, it uses an initialized array + * of strings to store the entries. Any string that doesn't start with a ':' + * is taken to be the name of a type of terminal. Any string that does start + * with a ':' is interpretted as the list of fields describing all of the + * terminal types that precede it. + * + * Note: since these are C strings, you can't use special sequences like + * ^M or \E in the fields; your C compiler won't understand them. Also, + * at run time there is no way to tell the difference between ':' and '\072' + * so I sure hope your terminal definition doesn't require a ':' character. + * + * getenv(TERM) on VMS checks the SET TERM device setting. To implement + * non-standard terminals set the logical ELVIS_TERM in VMS. (jdc) + * + * Other possible terminal types are... + * TERM_WYSE925 - "wyse925", a Wyse 50 terminal emulating Televideo 925 + * ... or you could set $TERMCAP to the terminal's description string, which + * $TERM set up to match it. + * + * Note that you can include several terminal types at the same time. Elvis + * chooses which entry to use at runtime, based primarily on the value of $TERM. + */ + + +#include "config.h" +extern char *getenv(); + +/* decide which terminal descriptions should *always* be included. */ +#if MSDOS +# define TERM_NANSI +# define TERM_DOSANSI +# if RAINBOW +# define TERM_RAINBOW +# endif +#endif + +#if VMS +# define TERM_VT100 +# define TERM_VT100W +# define TERM_VT52 +#endif + +#if AMIGA +# define TERM_AMIGA /* Internal Amiga termcap entry */ +/* # define TERM_VT52 /* The rest of these are here for those */ +# define TERM_VT100 /* people who want to use elvis over an */ +/* # define TERM_NANSI /* AUX: port (serial.device). */ +/* # define TERM_DOSANSI /* Take out all but AMIGA to save memory. */ +/* # define TERM_MINIX /* Vanilla ANSI? */ +/* # define TERM_925 /* Hang a terminal off your Amiga */ +#endif + +#if MINIX || UNIXV +# define TERM_MINIX +#endif + +#if COHERENT +# define TERM_COHERENT +#endif + +#if TOS +# define TERM_ATARI +#endif + +static char *termcap[] = +{ +#ifdef TERM_AMIGA +"AA", +"amiga", +"Amiga ANSI", +/* Amiga termcap modified from version 1.3 by Kent Polk */ +":co#80:li#24:am:bs:bw:xn:\ +:AL=\233%dL:DC=\233%dP:DL=\233%dM:DO=\233%dB:\ +:LE=\233%dD:RI=\233%dC:SF=\233%dS:SR=\233%dT:UP=\233%dA:IC=\233%d@:\ +:ae=\2330m:al=\233L:as=\2333m:bl=\007:bt=\233Z:cd=\233J:\ +:ce=\233K:cl=\013:cm=\233%i%d;%dH:dc=\233P:dl=\233M:do=\233B:\ +:kb=^H:ho=\233H:ic=\233@:is=\23320l:\ +:mb=\2337;2m:md=\2331m:me=\2330m:mh=\2332m:mk=\2338m:mr=\2337m:nd=\233C:\ +:rs=\033c:se=\2330m:sf=\233S:so=\2337m:sb=\233T:sr=\233T:ue=\23323m:\ +:up=\233A:us=\2334m:vb=\007:ve=\233\040p:vi=\2330\040p:\ +:k1=\2330~:k2=\2331~:k3=\2332~:k4=\2333~:k5=\2334~:\ +:k6=\2335~:k7=\2336~:k8=\2337~:k9=\2338~:k0=\2339~:\ +:s1=\23310~:s2=\23311~:s3=\23312~:s4=\23313~:s5=\23314~:\ +:s6=\23315~:s7=\23316~:s8=\23317~:s9=\23318~:s0=\23319~:\ +:kd=\233B:kl=\233D:kn#10:kr=\233C:ku=\233A:le=\233D:\ +:kP=\233T:kN=\233S:kh=\233\040A:kH=\233\040@:", +#endif + +#ifdef TERM_NANSI +"fansi", +"nnansi", +"nansi", +"pcbios", +":al=\033[L:dl=\033[M:am:bs:ce=\033[K:cl=\033[2J:\ +:cm=\033[%i%d;%dH:co#80:do=\033[B:\ +:k1=#;:k2=#<:k3=#=:k4=#>:k5=#?:k6=#@:k7=#A:k8=#B:k9=#C:k0=#D:\ +:s1=#T:s2=#U:s3=#V:s4=#W:s5=#X:s6=#Y:s7=#Z:s8=#[:s9=#\\:s0=#]:\ +:c1=#^:c2=#_:c3=#`:c4=#a:c5=#b:c6=#c:c7=#d:c8=#e:c9=#f:c0=#g:\ +:a1=#h:a2=#i:a3=#j:a4=#k:a5=#l:a6=#m:a7=#n:a8=#o:a9=#p:a0=#q:\ +:kd=#P:kh=#G:kH=#O:kI=#R:kl=#K:kN=#Q:kP=#I:kr=#M:ku=#H:\ +:li#25:md=\033[1m:me=\033[m:nd=\033[C:se=\033[m:so=\033[7m:\ +:ue=\033[m:up=\033[A:us=\033[4m:", +#endif + +#ifdef TERM_DOSANSI +#if !ANY_UNIX +"ansi", +#endif +"dosansi", +":am:bs:ce=\033[K:cl=\033[2J:\ +:cm=\033[%i%d;%dH:co#80:do=\033[B:\ +:k1=#;:k2=#<:k3=#=:k4=#>:k5=#?:k6=#@:k7=#A:k8=#B:k9=#C:k0=#D:\ +:s1=#T:s2=#U:s3=#V:s4=#W:s5=#X:s6=#Y:s7=#Z:s8=#[:s9=#\\:s0=#]:\ +:c1=#^:c2=#_:c3=#`:c4=#a:c5=#b:c6=#c:c7=#d:c8=#e:c9=#f:c0=#g:\ +:a1=#h:a2=#i:a3=#j:a4=#k:a5=#l:a6=#m:a7=#n:a8=#o:a9=#p:a0=#q:\ +:kd=#P:kh=#G:kH=#O:kI=#R:kl=#K:kN=#Q:kP=#I:kr=#M:ku=#H:\ +:li#25:md=\033[1m:me=\033[m:nd=\033[C:se=\033[m:so=\033[7m:\ +:ue=\033[m:up=\033[A:us=\033[4m:", +#endif + +#ifdef TERM_RAINBOW +"vt220", +"rainbow", +":al=\033[L:dl=\033[M:am:bs:ce=\033[K:cl=\033[2J:\ +:cm=\033[%i%d;%dH:co#80:do=\033[B:kd=\033[B:kl=\033[D:\ +:kr=\033[C:ku=\033[A:kP=\033[5~:kN=\033[6~:kI=\033[2~:\ +:li#24:md=\033[1m:me=\033[m:nd=\033[C:se=\033[m:so=\033[7m:\ +:ue=\033[m:up=\033[A:us=\033[4m:xn:", +#endif + +#ifdef TERM_VT100 +"vt100-80", +"vt200-80", +"vt300-80", +"vt101-80", +"vt102-80", +":al=\033[L:am:bs:ce=\033[K:cl=\033[2J:cm=\033[%i%d;%dH:\ +:co#80:dl=\033[M:do=\033[B:k0=\033[20~:k1=\033[1~:\ +:k2=\033[2~:k3=\033[3~:k4=\033[4~:k5=\033[5~:k6=\033[6~:\ +:k7=\033[17~:k8=\033[18~:k9=\033[19~:kd=\033[B:kh=\033[H:\ +:kH=\033[Y:kI=\033[I:kl=\033[D:kN=\033[U:kP=\033[V:\ +:kr=\033[C:ku=\033[A:li#24:md=\033[1m:me=\033[m:nd=\033[C:\ +:se=\033[m:so=\033[7m:ti=\033[1;24r\033[24;1H:\ +:ue=\033[m:up=\033[A:us=\033[4m:xn:", +#endif + +#ifdef TERM_VT100W +"vt100-w", +"vt200-w", +"vt300-w", +"vt101-w", +"vt102-w", +"vt100-132", +"vt200-132", +"vt300-132", +"vt101-132", +"vt102-132", +":al=\033[L:am:bs:ce=\033[K:cl=\033[2J:cm=\033[%i%d;%dH:\ +:co#132:dl=\033[M:do=\033[B:k0=\033[20~:k1=\033[1~:\ +:k2=\033[2~:k3=\033[3~:k4=\033[4~:k5=\033[5~:k6=\033[6~:\ +:k7=\033[17~:k8=\033[18~:k9=\033[19~:kd=\033[B:kh=\033[H:\ +:kH=\033[Y:kI=\033[I:kl=\033[D:kN=\033[U:kP=\033[V:\ +:kr=\033[C:ku=\033[A:li#24:md=\033[1m:me=\033[m:nd=\033[C:\ +:se=\033[m:so=\033[7m:ti=\033[1;24r\033[24;1H:\ +:ue=\033[m:up=\033[A:us=\033[4m:xn:", +#endif + +#ifdef TERM_VT52 +"vt52", +":do=\n:le=\b:up=\033A:nd=\033C:cm=\033Y%+ %+ :ti=\033e\033v:\ +:sr=\033I:cd=\033J:ce=\033K:cl=\033H\033J:co#80:li#24:\ +:ku=\033A:kd=\033B:kr=\033C:kl=\033D:kb=\b:pt:am:xn:bs:", +#endif + +#ifdef TERM_MINIX +"minix", +"ansi", +"AT386", +":al=\033[L:am:bs:ce=\033[K:cl=\033[2J:cm=\033[%i%d;%dH:\ +:co#80:dl=\033[M:do=\033[B:k0=\033[20~:k1=\033[1~:\ +:k2=\033[2~:k3=\033[3~:k4=\033[4~:k5=\033[5~:k6=\033[6~:\ +:k7=\033[17~:k8=\033[18~:k9=\033[19~:kd=\033[B:kh=\033[H:\ +:kH=\033[Y:kI=\033[I:kl=\033[D:kN=\033[U:kP=\033[V:\ +:kr=\033[C:ku=\033[A:li#25:md=\033[1m:me=\033[m:nd=\033[C:\ +:se=\033[m:so=\033[7m:ue=\033[m:up=\033[A:us=\033[4m:", +#endif /* MINIX */ + +#ifdef TERM_COHERENT +"coherent", +"ansipc", +":al=\033[L:am:bs:ce=\033[K:cl=\033[2J:cm=\033[%i%d;%dH:\ +:co#80:dl=\033[M:do=\033[B:k0=\033[0x:k1=\033[1x:k2=\033[2x:\ +:k3=\033[3x:k4=\033[4x:k5=\033[5x:k6=\033[6x:\ +:k7=\033[7x:k8=\033[8x:k9=\033[9x:kd=\033[B:kh=\033[H:\ +:kH=\033[24H:kI=\033[@:kl=\033[D:kN=\033[U:kP=\033[V:\ +:kr=\033[C:ku=\033[A:li#24:md=\033[1m:me=\033[m:\ +:nd=\033[C:se=\033[m:so=\033[7m:ue=\033[m:up=\033[A:\ +:us=\033[4m:", +#endif /* COHERENT */ + +#ifdef TERM_ATARI +"atari-st", +"vt52", +":al=\033L:am:bs:ce=\033K:cl=\033E:cm=\033Y%i%+ %+ :\ +:co#80:dl=\033M:do=\033B:\ +:k1=#;:k2=#<:k3=#=:k4=#>:k5=#?:k6=#@:k7=#A:k8=#B:k9=#C:k0=#D:\ +:s1=#T:s2=#U:s3=#V:s4=#W:s5=#X:s6=#Y:s7=#Z:s8=#[:s9=#\\:s0=#]:\ +:c1=#^:c2=#_:c3=#`:c4=#a:c5=#b:c6=#c:c7=#d:c8=#e:c9=#f:c0=#g:\ +:a1=#h:a2=#i:a3=#j:a4=#k:a5=#l:a6=#m:a7=#n:a8=#o:a9=#p:a0=#q:\ +kd=#P:kh=#G:kI=#R:kl=#K:kr=#M:ku=#H:li#25:nd=\033C:se=\033q:\ +:so=\033p:te=:ti=\033e\033v:up=\033A:", +#endif + +#ifdef TERM_925 +"wyse925", +":xn@:\ +:hs:am:bs:co#80:li#24:cm=\033=%+ %+ :cl=\033*:cd=\033y:\ +:ce=\033t:is=\033l\033\":\ +:al=\033E:dl=\033R:im=:ei=:ic=\033Q:dc=\033W:\ +:ho=\036:nd=\014:bt=\033I:pt:so=\033G4:se=\033G0:sg#1:us=\033G8:ue=\033G0:ug#1:\ +:up=\013:do=\026:kb=\010:ku=\013:kd=\026:kl=\010:kr=\014:\ +:kh=\036:ma=\026\012\014 :\ +:k1=\001@\r:k2=\001A\r:k3=\001B\r:k4=\001C\r:k5=\001D\r:k6=\001E\r:k7=\001F\r:\ +:k8=\001G\r:k9=\001H\r:k0=\001I\r:ko=ic,dc,al,dl,cl,ce,cd,bt:\ +:ts=\033f:fs=\033g:ds=\033h:sr=\033j:", /* was :xn: for tvi925 alone*/ +#endif + +(char *)0 +}; + + +static char *fields; + + +/*ARGSUSED*/ +int tgetent(bp, name) + char *bp; /* buffer for storing the entry -- ignored */ + char *name; /* name of the entry */ +{ + int i; + + /* if TERMCAP is defined, and seems to match, then use it */ + fields = getenv("TERMCAP"); + if (fields) + { + for (i = 0; fields[i] && fields[i] != ':'; i++) + { + if (!strncmp(fields + i, name, strlen(name))) + { + return 1; + } + } + } + + /* locate the entry in termcap[] */ + for (i = 0; termcap[i] && strcmp(termcap[i], name); i++) + { + } + if (!termcap[i]) + { + return 0; + } + + /* search forward for fields */ + while (termcap[i][0] != ':') + { + i++; + } + fields = termcap[i]; + return 1; +} + + +static char *find(id, vtype) + char *id; /* name of a value to locate */ + int vtype; /* '=' for strings, '#' for numbers, or 0 for bools */ +{ + int i; + + /* search for a ':' followed by the two-letter id */ + for (i = 0; fields[i]; i++) + { + if (fields[i] == ':' + && fields[i + 1] == id[0] + && fields[i + 2] == id[1]) + { + /* if correct type, then return its value */ + if (fields[i + 3] == vtype) + return &fields[i + 4]; + else + return (char *)0; + } + } + return (char *)0; +} + +int tgetnum(id) + char *id; +{ + id = find(id, '#'); + if (id) + { + return atoi(id); + } + return -1; +} + +int tgetflag(id) + char *id; +{ + if (find(id, ':')) + { + return 1; + } + return 0; +} + +/*ARGSUSED*/ +char *tgetstr(id, bp) + char *id; + char **bp; /* pointer to pointer to buffer - ignored */ +{ + char *cpy; + + /* find the string */ + id = find(id, '='); + if (!id) + { + return (char *)0; + } + + /* copy it into the buffer, and terminate it with NUL */ + for (cpy = *bp; *id != ':'; ) + { + if (id[0] == '\\' && id[1] == 'E') + *cpy++ = '\033', id += 2; + else + *cpy++ = *id++; + } + *cpy++ = '\0'; + + /* update the bp pointer */ + id = *bp; + *bp = cpy; + + /* return a pointer to the copy of the string */ + return id; +} + +/*ARGSUSED*/ +char *tgoto(cm, destcol, destrow) + char *cm; /* cursor movement string -- ignored */ + int destcol;/* destination column, 0 - 79 */ + int destrow;/* destination row, 0 - 24 */ +{ + static char buf[30]; + +#ifdef CRUNCH +# if TOS + sprintf(buf, "\033Y%c%c", ' ' + destrow, ' ' + destcol); +# else + sprintf(buf, "\033[%d;%dH", destrow + 1, destcol + 1); +# endif +#else + if (cm[1] == 'Y' || cm[1] == '=') + sprintf(buf, "\033%c%c%c", cm[1], ' ' + destrow, ' ' + destcol); + else + sprintf(buf, "\033[%d;%dH", destrow + 1, destcol + 1); +#endif + return buf; +} + +/*ARGSUSED*/ +void tputs(cp, affcnt, outfn) + char *cp; /* the string to output */ + int affcnt; /* number of affected lines -- ignored */ + int (*outfn)(); /* the output function */ +{ + while (*cp) + { + (*outfn)(*cp); + cp++; + } +} diff --git a/commands/elvis/tio.c b/commands/elvis/tio.c new file mode 100755 index 000000000..66bd26183 --- /dev/null +++ b/commands/elvis/tio.c @@ -0,0 +1,1026 @@ +/* tio.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains terminal I/O functions */ + +#include "config.h" +#include "vi.h" +#include "ctype.h" + + +/* This function reads in a line from the terminal. */ +int vgets(prompt, buf, bsize) + char prompt; /* the prompt character, or '\0' for none */ + char *buf; /* buffer into which the string is read */ + int bsize; /* size of the buffer */ +{ + int len; /* how much we've read so far */ + int ch; /* a character from the user */ + int quoted; /* is the next char quoted? */ + int tab; /* column position of cursor */ + char widths[132]; /* widths of characters */ + int word; /* index of first letter of word */ +#ifndef NO_DIGRAPH + int erased; /* 0, or first char of a digraph */ +#endif + + /* show the prompt */ + move(LINES - 1, 0); + tab = 0; + if (prompt) + { + addch(prompt); + tab = 1; + } + clrtoeol(); + refresh(); + + /* read in the line */ +#ifndef NO_DIGRAPH + erased = +#endif + quoted = len = 0; + for (;;) + { +#ifndef NO_ABBR + if (quoted || mode == MODE_EX) + { + ch = getkey(0); + } + else + { + /* maybe expand an abbreviation while getting key */ + for (word = len; --word >= 0 && isalnum(buf[word]); ) + { + } + word++; + ch = getabkey(WHEN_EX, &buf[word], len - word); + } +#else + ch = getkey(0); +#endif +#ifndef NO_EXTENSIONS + if (ch == ctrl('O')) + { + ch = getkey(quoted ? 0 : WHEN_EX); + } +#endif + + /* some special conversions */ + if (ch == ctrl('D') && len == 0) + ch = ctrl('['); +#ifndef NO_DIGRAPH + if (*o_digraph && erased != 0 && ch != '\b') + { + ch = digraph(erased, ch); + erased = 0; + } +#endif + + /* inhibit detection of special chars (except ^J) after a ^V */ + if (quoted && ch != '\n') + { + ch |= 256; + } + + /* process the character */ + switch(ch) + { + case ctrl('V'): + qaddch('^'); + qaddch('\b'); + quoted = TRUE; + break; + + case ctrl('['): + return -1; + + case '\n': +#if OSK + case '\l': +#else + case '\r': +#endif + clrtoeol(); + goto BreakBreak; + + case '\b': + if (len > 0) + { + len--; +#ifndef NO_DIGRAPH + erased = buf[len]; +#endif + for (ch = widths[len]; ch > 0; ch--) + addch('\b'); + if (mode == MODE_EX) + { + clrtoeol(); + } + tab -= widths[len]; + } + else + { + return -1; + } + break; + + default: + /* strip off quotation bit */ + if (ch & 256) + { + ch &= ~256; + qaddch(' '); + qaddch('\b'); + } + + /* add & echo the char */ + if (len < bsize - 1) + { + if (ch == '\t' && !quoted) + { + widths[len] = *o_tabstop - (tab % *o_tabstop); + addstr(" " + 8 - widths[len]); + tab += widths[len]; + } + else if (ch > 0 && ch < ' ') /* > 0 by GB */ + { + addch('^'); + addch(ch + '@'); + widths[len] = 2; + tab += 2; + } + else if (ch == '\177') + { + addch('^'); + addch('?'); + widths[len] = 2; + tab += 2; + } + else + { + addch(ch); + widths[len] = 1; + tab++; + } + buf[len++] = ch; + } + else + { + beep(); + } + quoted = FALSE; + } + } +BreakBreak: + refresh(); + buf[len] = '\0'; + return len; +} + + +static int manymsgs; /* This variable keeps msgs from overwriting each other */ +static char pmsg[80]; /* previous message (waiting to be displayed) */ + + +static int showmsg() +{ + /* if there is no message to show, then don't */ + if (!manymsgs) + return FALSE; + + /* display the message */ + move(LINES - 1, 0); + if (*pmsg) + { + standout(); + qaddch(' '); + qaddstr(pmsg); + qaddch(' '); + standend(); + } + clrtoeol(); + + manymsgs = FALSE; + return TRUE; +} + + +void endmsgs() +{ + if (manymsgs) + { + showmsg(); + addch('\n'); + } +} + +/* Write a message in an appropriate way. This should really be a varargs + * function, but there is no such thing as vwprintw. Hack!!! + * + * In MODE_EX or MODE_COLON, the message is written immediately, with a + * newline at the end. + * + * In MODE_VI, the message is stored in a character buffer. It is not + * displayed until getkey() is called. msg() will call getkey() itself, + * if necessary, to prevent messages from being lost. + * + * msg("") - clears the message line + * msg("%s %d", ...) - does a printf onto the message line + */ +/*VARARGS1*/ +void msg(fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7) + char *fmt; + long arg1, arg2, arg3, arg4, arg5, arg6, arg7; +{ + if (mode != MODE_VI) + { + sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + qaddstr(pmsg); + addch('\n'); + exrefresh(); + } + else + { + /* wait for keypress between consecutive msgs */ + if (manymsgs) + { + getkey(WHEN_MSG); + } + + /* real message */ + sprintf(pmsg, fmt, arg1, arg2, arg3, arg4, arg5, arg6, arg7); + if (*fmt) + { + manymsgs = TRUE; + } + } +} + + +/* This function calls refresh() if the option exrefresh is set */ +void exrefresh() +{ + char *scan; + + /* If this ex command wrote ANYTHING set exwrote so vi's : command + * can tell that it must wait for a user keystroke before redrawing. + */ + for (scan=kbuf; scan<stdscr; scan++) + if (*scan == '\n') + exwrote = TRUE; + + /* now we do the refresh thing */ + if (*o_exrefresh) + { + refresh(); + } + else + { + wqrefresh(); + } + if (mode != MODE_VI) + { + manymsgs = FALSE; + } +} + + +/* This structure is used to store maps and abbreviations. The distinction + * between them is that maps are stored in the list referenced by the "maps" + * pointer, while abbreviations are referenced by the "abbrs" pointer. + */ +typedef struct _map +{ + struct _map *next; /* another abbreviation */ + short len; /* length of the "rawin" characters */ + short flags; /* various flags */ + char *label; /* label of the map/abbr, or NULL */ + char *rawin; /* the "rawin" characters */ + char *cooked;/* the "cooked" characters */ +} MAP; + +static char keybuf[KEYBUFSIZE]; +static int cend; /* end of input characters */ +static int user; /* from user through end are chars typed by user */ +static int next; /* index of the next character to be returned */ +static MAP *match; /* the matching map, found by countmatch() */ +static MAP *maps; /* the map table */ +#ifndef NO_ABBR +static MAP *abbrs; /* the abbreviation table */ +#endif + + + +/* ring the terminal's bell */ +void beep() +{ + /* do a visible/audible bell */ + if (*o_flash) + { + do_VB(); + refresh(); + } + else if (*o_errorbells) + { + ttywrite("\007", 1); + } + + /* discard any buffered input, and abort macros */ + next = user = cend; +} + + + +/* This function replaces a "rawin" character sequence with the "cooked" version, + * by modifying the internal type-ahead buffer. + */ +void execmap(rawlen, cookedstr, visual) + int rawlen; /* length of rawin text -- string to delete */ + char *cookedstr; /* the cooked text -- string to insert */ + int visual; /* boolean -- chars to be executed in visual mode? */ +{ + int cookedlen; + char *src, *dst; + int i; + + /* find the length of the cooked string */ + cookedlen = strlen(cookedstr); +#ifndef NO_EXTENSIONS + if (visual) + { + cookedlen *= 2; + } +#endif + + /* if too big to fit in type-ahead buffer, then don't do it */ + if (cookedlen + (cend - next) - rawlen > KEYBUFSIZE) + { + return; + } + + /* shift to make room for cookedstr at the front of keybuf */ + src = &keybuf[next + rawlen]; + dst = &keybuf[cookedlen]; + i = cend - (next + rawlen); + if (src >= dst) + { + while (i-- > 0) + { + *dst++ = *src++; + } + } + else + { + src += i; + dst += i; + while (i-- > 0) + { + *--dst = *--src; + } + } + + /* insert cookedstr, and adjust offsets */ + cend += cookedlen - rawlen - next; + user += cookedlen - rawlen - next; + next = 0; + for (dst = keybuf, src = cookedstr; *src; ) + { +#ifndef NO_EXTENSIONS + if (visual) + { + *dst++ = ctrl('O'); + cookedlen--; + } +#endif + *dst++ = *src++; + } + +#ifdef DEBUG2 + { +#include <stdio.h> + FILE *debout; + int i; + + debout = fopen("debug.out", "a"); + fprintf(debout, "After execmap(%d, \"%s\", %d)...\n", rawlen, cookedstr, visual); + for (i = 0; i < cend; i++) + { + if (i == next) fprintf(debout, "(next)"); + if (i == user) fprintf(debout, "(user)"); + if (UCHAR(keybuf[i]) < ' ') + fprintf(debout, "^%c", keybuf[i] ^ '@'); + else + fprintf(debout, "%c", keybuf[i]); + } + fprintf(debout, "(end)\n"); + fclose(debout); + } +#endif +} + +/* This function calls ttyread(). If necessary, it will also redraw the screen, + * change the cursor shape, display the mode, and update the ruler. If the + * number of characters read is 0, and we didn't time-out, then it exits because + * we've apparently reached the end of an EX script. + */ +static int fillkeybuf(when, timeout) + int when; /* mixture of WHEN_XXX flags */ + int timeout;/* timeout in 1/10 second increments, or 0 */ +{ + int nkeys; +#ifndef NO_SHOWMODE + static int oldwhen; /* "when" from last time */ + static int oldleft; + static long oldtop; + static long oldnlines; + char *str; +#endif +#ifndef NO_CURSORSHAPE + static int oldcurs; +#endif + +#ifdef DEBUG + watch(); +#endif + + +#ifndef NO_CURSORSHAPE + /* make sure the cursor is the right shape */ + if (has_CQ) + { + if (when != oldcurs) + { + switch (when) + { + case WHEN_EX: do_CX(); break; + case WHEN_VICMD: do_CV(); break; + case WHEN_VIINP: do_CI(); break; + case WHEN_VIREP: do_CR(); break; + } + oldcurs = when; + } + } +#endif + +#ifndef NO_SHOWMODE + /* if "showmode" then say which mode we're in */ + if (*o_smd && (when & WHENMASK)) + { + /* redraw the screen before we check to see whether the + * "showmode" message needs to be redrawn. + */ + redraw(cursor, !(when & WHEN_VICMD)); + + /* now the "topline" test should be valid */ + if (when != oldwhen || topline != oldtop || leftcol != oldleft || nlines != oldnlines) + { + oldwhen = when; + oldtop = topline; + oldleft = leftcol; + oldnlines = nlines; + + if (when & WHEN_VICMD) str = "Command"; + else if (when & WHEN_VIINP) str = " Input "; + else if (when & WHEN_VIREP) str = "Replace"; + else if (when & WHEN_REP1) str = " Rep 1 "; + else if (when & WHEN_CUT) str = "BufName"; + else if (when & WHEN_MARK) str = "Mark AZ"; + else if (when & WHEN_CHAR) str = "Dest Ch"; + else str = (char *)0; + + if (str) + { + move(LINES - 1, COLS - 10); + standout(); + qaddstr(str); + standend(); + } + } + } +#endif + +#ifndef NO_EXTENSIONS + /* maybe display the ruler */ + if (*o_ruler && (when & (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP))) + { + char buf[20]; + + redraw(cursor, !(when & WHEN_VICMD)); + pfetch(markline(cursor)); + sprintf(buf, "%7ld,%-4d", markline(cursor), 1 + idx2col(cursor, ptext, when & (WHEN_VIINP|WHEN_VIREP))); + move(LINES - 1, COLS - 22); + addstr(buf); + } +#endif + + /* redraw, so the cursor is in the right place */ + if (when & WHENMASK) + { + redraw(cursor, !(when & (WHENMASK & ~(WHEN_VIREP|WHEN_VIINP)))); + } + + /* Okay, now we can finally read the rawin keystrokes */ + refresh(); + nkeys = ttyread(keybuf + cend, sizeof keybuf - cend, timeout); + + /* if nkeys == 0 then we've reached EOF of an ex script. */ + if (nkeys == 0 && timeout == 0) + { + tmpabort(TRUE); + move(LINES - 1, 0); + clrtoeol(); + refresh(); + endwin(); + exit(1); + } + + cend += nkeys; + user += nkeys; + return nkeys; +} + + +/* This function counts the number of maps that could match the characters + * between &keybuf[next] and &keybuf[cend], including incomplete matches. + * The longest comlete match is remembered via the "match" variable. + */ +static int countmatch(when) + int when; /* mixture of WHEN_XXX flags */ +{ + MAP *map; + int count; + + /* clear the "match" variable */ + match = (MAP *)0; + + /* check every map */ + for (count = 0, map = maps; map; map = map->next) + { + /* can't match if wrong mode */ + if ((map->flags & when) == 0) + { + continue; + } + + /* would this be a complete match? */ + if (map->len <= cend - next) + { + /* Yes, it would be. Now does it really match? */ + if (!strncmp(map->rawin, &keybuf[next], map->len)) + { + count++; + + /* if this is the longest complete match, + * then remember it. + */ + if (!match || match->len < map->len) + { + match = map; + } + } + } + else + { + /* No, it wouldn't. But check for partial match */ + if (!strncmp(map->rawin, &keybuf[next], cend - next)) + { + count++; + } + } + } + return count; +} + + +#ifndef NO_ABBR +/* This function checks to see whether a word is an abbreviation. If it is, + * then an appropriate number of backspoace characters is inserted into the + * type-ahead buffer, followed by the expanded form of the abbreviation. + */ +static void expandabbr(word, wlen) + char *word; + int wlen; +{ + MAP *abbr; + + /* if the next character wouldn't end the word, then don't expand */ + if (isalnum(keybuf[next]) || keybuf[next] == ctrl('V')) + { + return; + } + + /* find the abbreviation, if any */ + for (abbr = abbrs; + abbr && (abbr->len != wlen || strncmp(abbr->rawin, word, wlen)); + abbr = abbr->next) + { + } + + /* If an abbreviation was found, then expand it by inserting the long + * version into the type-ahead buffer, and then inserting (in front of + * the long version) enough backspaces to erase to the short version. + */ + if (abbr) + { + execmap(0, abbr->cooked, FALSE); + while (wlen > 15) + { + execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b", FALSE); + wlen -= 15; + } + if (wlen > 0) + { + execmap(0, "\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b" + 15 - wlen, FALSE); + } + } +} +#endif + + +/* This function calls getabkey() without attempting to expand abbreviations */ +int getkey(when) + int when; /* mixture of WHEN_XXX flags */ +{ + return getabkey(when, "", 0); +} + + +/* This is it. This function returns keystrokes one-at-a-time, after mapping + * and abbreviations have been taken into account. + */ +int getabkey(when, word, wlen) + int when; /* mixture of WHEN_XXX flags */ + char *word; /* a word that may need to be expanded as an abbr */ + int wlen; /* length of "word" -- since "word" might not have \0 */ +{ + int matches; + + /* if this key is needed for delay between multiple error messages, + * then reset the manymsgs flag and abort any mapped key sequence. + */ + if (showmsg()) + { + if (when == WHEN_MSG) + { +#ifndef CRUNCH + if (!*o_more) + { + refresh(); + return ' '; + } +#endif + qaddstr("[More...]"); + refresh(); + execmap(user, "", FALSE); + } + } + +#ifdef DEBUG + /* periodically check for screwed up internal tables */ + watch(); +#endif + + /* if buffer empty, read some characters without timeout */ + if (next >= cend) + { + next = user = cend = 0; + fillkeybuf(when, 0); + } + + /* try to map the key, unless already mapped and not ":set noremap" */ + if (next >= user || *o_remap) + { + do + { + do + { + matches = countmatch(when); + } while (matches > 1 && fillkeybuf(when, *o_keytime) > 0); + if (matches == 1) + { + execmap(match->len, match->cooked, + (match->flags & WHEN_INMV) != 0 + && (when & (WHEN_VIINP|WHEN_VIREP)) != 0); + } + } while (*o_remap && matches == 1); + } + +#ifndef NO_ABBR + /* try to expand an abbreviation, except in visual command mode */ + if (wlen > 0 && (mode & (WHEN_EX|WHEN_VIINP|WHEN_VIREP)) != 0) + { + expandabbr(word, wlen); + } +#endif + + /* ERASEKEY should always be mapped to '\b'. */ + if (keybuf[next] == ERASEKEY) + { + keybuf[next] = '\b'; + } + + /* return the next key */ + return keybuf[next++]; +} + +/* This function maps or unmaps a key */ +void mapkey(rawin, cooked, when, name) + char *rawin; /* the input key sequence, before mapping */ + char *cooked;/* after mapping -- or NULL to remove map */ + short when; /* bitmap of when mapping should happen */ + char *name; /* name of the key, NULL for no name, "abbr" for abbr */ +{ + MAP **head; /* head of list of maps or abbreviations */ + MAP *scan; /* used for scanning through the list */ + MAP *prev; /* used during deletions */ + + /* Is this a map or an abbreviation? Choose the right list. */ +#ifndef NO_ABBR + head = ((!name || strcmp(name, "abbr")) ? &maps : &abbrs); +#else + head = &maps; +#endif + + /* try to find the map in the list */ + for (scan = *head, prev = (MAP *)0; + scan && (strcmp(rawin, scan->rawin) || + !(scan->flags & when & (WHEN_EX|WHEN_VICMD|WHEN_VIINP|WHEN_VIREP))); + prev = scan, scan = scan->next) + { + } + + /* trying to map? (not unmap) */ + if (cooked && *cooked) + { + /* if map starts with "visual ", then mark it as a visual map */ + if (head == &maps && !strncmp(cooked, "visual ", 7)) + { + cooked += 7; + when |= WHEN_INMV; + } + + /* "visual" maps always work in input mode */ + if (when & WHEN_INMV) + { + when |= WHEN_VIINP|WHEN_VIREP|WHEN_POPUP; + } + + /* if not already in the list, then allocate a new structure */ + if (!scan) + { + scan = (MAP *)malloc(sizeof(MAP)); + scan->len = strlen(rawin); + scan->rawin = malloc(scan->len + 1); + strcpy(scan->rawin, rawin); + scan->flags = when; + scan->label = name; + if (*head) + { + prev->next = scan; + } + else + { + *head = scan; + } + scan->next = (MAP *)0; + } + else /* recycle old structure */ + { + free(scan->cooked); + } + scan->cooked = malloc(strlen(cooked) + 1); + strcpy(scan->cooked, cooked); + } + else /* unmapping */ + { + /* if nothing to unmap, then exit silently */ + if (!scan) + { + return; + } + + /* unlink the structure from the list */ + if (prev) + { + prev->next = scan->next; + } + else + { + *head = scan->next; + } + + /* free it, and the strings that it refers to */ + free(scan->rawin); + free(scan->cooked); + free(scan); + } +} + + +/* This function returns a printable version of a string. It uses tmpblk.c */ +char *printable(str) + char *str; /* the string to convert */ +{ + char *build; /* used for building the string */ + + for (build = tmpblk.c; *str; str++) + { +#if AMIGA + if (*str == '\233') + { + *build++ = '<'; + *build++ = 'C'; + *build++ = 'S'; + *build++ = 'I'; + *build++ = '>'; + } else +#endif + if (UCHAR(*str) < ' ' || *str == '\177') + { + *build++ = '^'; + *build++ = *str ^ '@'; + } + else + { + *build++ = *str; + } + } + *build = '\0'; + return tmpblk.c; +} + +/* This function displays the contents of either the map table or the + * abbreviation table. User commands call this function as follows: + * :map dumpkey(WHEN_VICMD, FALSE); + * :map! dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE); + * :abbr dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE); + * :abbr! dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE); + */ +void dumpkey(when, abbr) + int when; /* WHEN_XXXX of mappings to be dumped */ + int abbr; /* boolean: dump abbreviations instead of maps? */ +{ + MAP *scan; + char *str; + int len; + +#ifndef NO_ABBR + for (scan = (abbr ? abbrs : maps); scan; scan = scan->next) +#else + for (scan = maps; scan; scan = scan->next) +#endif + { + /* skip entries that don't match "when" */ + if ((scan->flags & when) == 0) + { + continue; + } + + /* dump the key label, if any */ + if (!abbr) + { + len = 8; + if (scan->label) + { + qaddstr(scan->label); + len -= strlen(scan->label); + } + do + { + qaddch(' '); + } while (len-- > 0); + } + + /* dump the rawin version */ + str = printable(scan->rawin); + qaddstr(str); + len = strlen(str); + do + { + qaddch(' '); + } while (len++ < 8); + + /* dump the mapped version */ +#ifndef NO_EXTENSIONS + if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP))) + { + qaddstr("visual "); + } +#endif + str = printable(scan->cooked); + qaddstr(str); + addch('\n'); + exrefresh(); + } +} + +#ifndef NO_MKEXRC + +static safequote(str) + char *str; +{ + char *build; + + build = tmpblk.c + strlen(tmpblk.c); + while (*str) + { + if (*str <= ' ' && *str >= 1 || *str == '|') + { + *build++ = ctrl('V'); + } + *build++ = *str++; + } + *build = '\0'; +} + +/* This function saves the contents of either the map table or the + * abbreviation table into a file. Both the "bang" and "no bang" versions + * are saved. + * :map dumpkey(WHEN_VICMD, FALSE); + * :map! dumpkey(WHEN_VIREP|WHEN_VIINP, FALSE); + * :abbr dumpkey(WHEN_VIINP|WHEN_VIREP, TRUE); + * :abbr! dumpkey(WHEN_EX|WHEN_VIINP|WHEN_VIREP, TRUE); + */ +savemaps(fd, abbr) + int fd; /* file descriptor of an open file to write to */ + int abbr; /* boolean: do abbr table? (else do map table) */ +{ + MAP *scan; + char *str; + int bang; + int when; + int len; + +# ifndef NO_ABBR + for (scan = (abbr ? abbrs : maps); scan; scan = scan->next) +# else + for (scan = maps; scan; scan = scan->next) +# endif + { + /* skip maps that have labels, except for function keys */ + if (scan->label && *scan->label != '#') + { + continue; + } + + for (bang = 0; bang < 2; bang++) + { + /* decide which "when" flags we want */ +# ifndef NO_ABBR + if (abbr) + when = (bang ? WHEN_EX|WHEN_VIINP|WHEN_VIREP : WHEN_VIINP|WHEN_VIREP); + else +# endif + when = (bang ? WHEN_VIREP|WHEN_VIINP : WHEN_VICMD); + + /* skip entries that don't match "when" */ + if ((scan->flags & when) == 0) + { + continue; + } + + /* write a "map" or "abbr" command name */ +# ifndef NO_ABBR + if (abbr) + strcpy(tmpblk.c, "abbr"); + else +# endif + strcpy(tmpblk.c, "map"); + + /* maybe write a bang. Definitely write a space */ + if (bang) + strcat(tmpblk.c, "! "); + else + strcat(tmpblk.c, " "); + + /* write the rawin version */ +# ifndef NO_FKEY + if (scan->label) + strcat(tmpblk.c, scan->label); + else +# endif + safequote(scan->rawin); + strcat(tmpblk.c, " "); + + /* dump the mapped version */ +# ifndef NO_EXTENSIONS + if ((scan->flags & WHEN_INMV) && (when & (WHEN_VIINP|WHEN_VIREP))) + { + strcat(tmpblk.c, "visual "); + } +# endif + safequote(scan->cooked); + strcat(tmpblk.c, "\n"); + twrite(fd, tmpblk.c, strlen(tmpblk.c)); + } + } +} +#endif diff --git a/commands/elvis/tmp.c b/commands/elvis/tmp.c new file mode 100755 index 000000000..77ae0ef88 --- /dev/null +++ b/commands/elvis/tmp.c @@ -0,0 +1,759 @@ +/* tmp.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains functions which create & readback a TMPFILE */ + + +#include "config.h" +#include "vi.h" +#if TOS +# include <stat.h> +#else +# if OSK +# include "osk.h" +# else +# if AMIGA +# include "amistat.h" +# else +# include <sys/stat.h> +# endif +# endif +#endif +#if TURBOC +# include <process.h> +#endif + +#ifndef NO_MODELINES +static void do_modelines(l, stop) + long l; /* line number to start at */ + long stop; /* line number to stop at */ +{ + char *str; /* used to scan through the line */ + char *start; /* points to the start of the line */ + char buf[80]; + + /* if modelines are disabled, then do nothing */ + if (!*o_modelines) + { + return; + } + + /* for each line... */ + for (; l <= stop; l++) + { + /* for each position in the line.. */ + for (str = fetchline(l); *str; str++) + { + /* if it is the start of a modeline command... */ + if ((str[0] == 'e' && str[1] == 'x' + || str[0] == 'v' && str[1] == 'i') + && str[2] == ':') + { + start = str += 3; + + /* find the end */ + for (str = start + strlen(start); *--str != ':'; ) + { + } + + /* if it is a well-formed modeline, execute it */ + if (str > start && str - start < sizeof buf) + { + strncpy(buf, start, (int)(str - start)); + exstring(buf, str - start, '\\'); + break; + } + } + } + } +} +#endif + + +/* The FAIL() macro prints an error message and then exits. */ +#define FAIL(why,arg) mode = MODE_EX; msg(why, arg); endwin(); exit(9) + +/* This is the name of the temp file */ +static char tmpname[80]; + +/* This function creates the temp file and copies the original file into it. + * Returns if successful, or stops execution if it fails. + */ +int tmpstart(filename) + char *filename; /* name of the original file */ +{ + int origfd; /* fd used for reading the original file */ + struct stat statb; /* stat buffer, used to examine inode */ + REG BLK *this; /* pointer to the current block buffer */ + REG BLK *next; /* pointer to the next block buffer */ + int inbuf; /* number of characters in a buffer */ + int nread; /* number of bytes read */ + REG int j, k; + int i; + long nbytes; + + /* switching to a different file certainly counts as a change */ + changes++; + redraw(MARK_UNSET, FALSE); + + /* open the original file for reading */ + *origname = '\0'; + if (filename && *filename) + { + strcpy(origname, filename); + origfd = open(origname, O_RDONLY); + if (origfd < 0 && errno != ENOENT) + { + msg("Can't open \"%s\"", origname); + return tmpstart(""); + } + if (origfd >= 0) + { + if (stat(origname, &statb) < 0) + { + FAIL("Can't stat \"%s\"", origname); + } +#if TOS + if (origfd >= 0 && (statb.st_mode & S_IJDIR)) +#else +# if OSK + if (origfd >= 0 && (statb.st_mode & S_IFDIR)) +# else + if (origfd >= 0 && (statb.st_mode & S_IFMT) != S_IFREG) +# endif +#endif + { + msg("\"%s\" is not a regular file", origname); + return tmpstart(""); + } + } + else + { + stat(".", &statb); + } + if (origfd >= 0) + { + origtime = statb.st_mtime; +#if OSK + if (*o_readonly || !(statb.st_mode & + ((getuid() >> 16) == 0 ? S_IOWRITE | S_IWRITE : + ((statb.st_gid != (getuid() >> 16) ? S_IOWRITE : S_IWRITE))))) +#endif +#if AMIGA || MSDOS || (TOS && defined(__GNUC__)) + if (*o_readonly || !(statb.st_mode & S_IWRITE)) +#endif +#if TOS && !defined(__GNUC__) + if (*o_readonly || (statb.st_mode & S_IJRON)) +#endif +#if ANY_UNIX + if (*o_readonly || !(statb.st_mode & + ((geteuid() == 0) ? 0222 : + ((statb.st_uid != geteuid() ? 0022 : 0200))))) +#endif +#if VMS + if (*o_readonly) +#endif + { + setflag(file, READONLY); + } + } + else + { + origtime = 0L; + } + } + else + { + setflag(file, NOFILE); + origfd = -1; + origtime = 0L; + stat(".", &statb); + } + + /* make a name for the tmp file */ + tmpnum++; +#if MSDOS || TOS + /* MS-Dos doesn't allow multiple slashes, but supports drives + * with current directories. + * This relies on TMPNAME beginning with "%s\\"!!!! + */ + strcpy(tmpname, o_directory); + if ((i = strlen(tmpname)) && !strchr(":/\\", tmpname[i-1])) + tmpname[i++]=SLASH; + sprintf(tmpname+i, TMPNAME+3, getpid(), tmpnum); +#else + sprintf(tmpname, TMPNAME, o_directory, getpid(), tmpnum); +#endif + + /* make sure nobody else is editing the same file */ + if (access(tmpname, 0) == 0) + { + FAIL("Temp file \"%s\" already exists?", tmpname); + } + + /* create the temp file */ +#if ANY_UNIX + close(creat(tmpname, 0600)); /* only we can read it */ +#else + close(creat(tmpname, FILEPERMS)); /* anybody body can read it, alas */ +#endif + tmpfd = open(tmpname, O_RDWR | O_BINARY); + if (tmpfd < 0) + { + FAIL("Can't create temp file... Does directory \"%s\" exist?", o_directory); + return 1; + } + + /* allocate space for the header in the file */ + write(tmpfd, hdr.c, (unsigned)BLKSIZE); + write(tmpfd, tmpblk.c, (unsigned)BLKSIZE); + +#ifndef NO_RECYCLE + /* initialize the block allocator */ + /* This must already be done here, before the first attempt + * to write to the new file! GB */ + garbage(); +#endif + + /* initialize lnum[] */ + for (i = 1; i < MAXBLKS; i++) + { + lnum[i] = INFINITY; + } + lnum[0] = 0; + + /* if there is no original file, then create a 1-line file */ + if (origfd < 0) + { + hdr.n[0] = 0; /* invalid inode# denotes new file */ + + this = blkget(1); /* get the new text block */ + strcpy(this->c, "\n"); /* put a line in it */ + + lnum[1] = 1L; /* block 1 ends with line 1 */ + nlines = 1L; /* there is 1 line in the file */ + nbytes = 1L; + + if (*origname) + { + msg("\"%s\" [NEW FILE] 1 line, 1 char", origname); + } + else + { + msg("\"[NO FILE]\" 1 line, 1 char"); + } + } + else /* there is an original file -- read it in */ + { + nbytes = nlines = 0; + + /* preallocate 1 "next" buffer */ + i = 1; + next = blkget(i); + inbuf = 0; + + /* loop, moving blocks from orig to tmp */ + for (;;) + { + /* "next" buffer becomes "this" buffer */ + this = next; + + /* read [more] text into this block */ + nread = tread(origfd, &this->c[inbuf], BLKSIZE - 1 - inbuf); + if (nread < 0) + { + close(origfd); + close(tmpfd); + tmpfd = -1; + unlink(tmpname); + FAIL("Error reading \"%s\"", origname); + } + + /* convert NUL characters to something else */ + for (j = k = inbuf; k < inbuf + nread; k++) + { + if (!this->c[k]) + { + setflag(file, HADNUL); + this->c[j++] = 0x80; + } +#ifndef CRUNCH + else if (*o_beautify && this->c[k] < ' ' && this->c[k] > 0) + { + if (this->c[k] == '\t' + || this->c[k] == '\n' + || this->c[k] == '\f') + { + this->c[j++] = this->c[k]; + } + else if (this->c[k] == '\b') + { + /* delete '\b', but complain */ + setflag(file, HADBS); + } + /* else silently delete control char */ + } +#endif + else + { + this->c[j++] = this->c[k]; + } + } + inbuf = j; + + /* if the buffer is empty, quit */ + if (inbuf == 0) + { + goto FoundEOF; + } + +#if MSDOS || TOS +/* BAH! MS text mode read fills inbuf, then compresses eliminating \r + but leaving garbage at end of buf. The same is true for TURBOC. GB. */ + + memset(this->c + inbuf, '\0', BLKSIZE - inbuf); +#endif + + /* search backward for last newline */ + for (k = inbuf; --k >= 0 && this->c[k] != '\n';) + { + } + if (k++ < 0) + { + if (inbuf >= BLKSIZE - 1) + { + k = 80; + } + else + { + k = inbuf; + } + } + + /* allocate next buffer */ + next = blkget(++i); + + /* move fragmentary last line to next buffer */ + inbuf -= k; + for (j = 0; k < BLKSIZE; j++, k++) + { + next->c[j] = this->c[k]; + this->c[k] = 0; + } + + /* if necessary, add a newline to this buf */ + for (k = BLKSIZE - inbuf; --k >= 0 && !this->c[k]; ) + { + } + if (this->c[k] != '\n') + { + setflag(file, ADDEDNL); + this->c[k + 1] = '\n'; + } + + /* count the lines in this block */ + for (k = 0; k < BLKSIZE && this->c[k]; k++) + { + if (this->c[k] == '\n') + { + nlines++; + } + nbytes++; + } + lnum[i - 1] = nlines; + } +FoundEOF: + + /* if this is a zero-length file, add 1 line */ + if (nlines == 0) + { + this = blkget(1); /* get the new text block */ + strcpy(this->c, "\n"); /* put a line in it */ + + lnum[1] = 1; /* block 1 ends with line 1 */ + nlines = 1; /* there is 1 line in the file */ + nbytes = 1; + } + +#if MSDOS || TOS + /* each line has an extra CR that we didn't count yet */ + nbytes += nlines; +#endif + + /* report the number of lines in the file */ + msg("\"%s\" %s %ld line%s, %ld char%s", + origname, + (tstflag(file, READONLY) ? "[READONLY]" : ""), + nlines, + nlines == 1 ? "" : "s", + nbytes, + nbytes == 1 ? "" : "s"); + } + + /* initialize the cursor to start of line 1 */ + cursor = MARK_FIRST; + + /* close the original file */ + close(origfd); + + /* any other messages? */ + if (tstflag(file, HADNUL)) + { + msg("This file contained NULs. They've been changed to \\x80 chars"); + } + if (tstflag(file, ADDEDNL)) + { + msg("Newline characters have been inserted to break up long lines"); + } +#ifndef CRUNCH + if (tstflag(file, HADBS)) + { + msg("Backspace characters deleted due to ':set beautify'"); + } +#endif + + storename(origname); + +#ifndef NO_MODELINES + if (nlines > 10) + { + do_modelines(1L, 5L); + do_modelines(nlines - 4L, nlines); + } + else + { + do_modelines(1L, nlines); + } +#endif + + /* force all blocks out onto the disk, to support file recovery */ + blksync(); + + return 0; +} + + + +/* This function copies the temp file back onto an original file. + * Returns TRUE if successful, or FALSE if the file could NOT be saved. + */ +int tmpsave(filename, bang) + char *filename; /* the name to save it to */ + int bang; /* forced write? */ +{ + int fd; /* fd of the file we're writing to */ + REG int len; /* length of a text block */ + REG BLK *this; /* a text block */ + long bytes; /* byte counter */ + REG int i; + + /* if no filename is given, assume the original file name */ + if (!filename || !*filename) + { + filename = origname; + } + + /* if still no file name, then fail */ + if (!*filename) + { + msg("Don't know a name for this file -- NOT WRITTEN"); + return FALSE; + } + + /* can't rewrite a READONLY file */ +#if AMIGA + if (!strcmp(filename, origname) && tstflag(file, READONLY) && !bang) +#else + if (!strcmp(filename, origname) && *o_readonly && !bang) +#endif + { + msg("\"%s\" [READONLY] -- NOT WRITTEN", filename); + return FALSE; + } + + /* open the file */ + if (*filename == '>' && filename[1] == '>') + { + filename += 2; + while (*filename == ' ' || *filename == '\t') + { + filename++; + } +#ifdef O_APPEND + fd = open(filename, O_WRONLY|O_APPEND); +#else + fd = open(filename, O_WRONLY); + lseek(fd, 0L, 2); +#endif + } + else + { + /* either the file must not exist, or it must be the original + * file, or we must have a bang, or "writeany" must be set. + */ + if (strcmp(filename, origname) && access(filename, 0) == 0 && !bang +#ifndef CRUNCH + && !*o_writeany +#endif + ) + { + msg("File already exists - Use :w! to overwrite"); + return FALSE; + } +#if VMS + /* Create a new VMS version of this file. */ + { + char *strrchr(), *ptr = strrchr(filename,';'); + if (ptr) *ptr = '\0'; /* Snip off any ;number in the name */ + } +#endif + fd = creat(filename, FILEPERMS); + } + if (fd < 0) + { + msg("Can't write to \"%s\" -- NOT WRITTEN", filename); + return FALSE; + } + + /* write each text block to the file */ + bytes = 0L; + for (i = 1; i < MAXBLKS && (this = blkget(i)) && this->c[0]; i++) + { + for (len = 0; len < BLKSIZE && this->c[len]; len++) + { + } + if (twrite(fd, this->c, len) < len) + { + msg("Trouble writing to \"%s\"", filename); + if (!strcmp(filename, origname)) + { + setflag(file, MODIFIED); + } + close(fd); + return FALSE; + } + bytes += len; + } + + /* reset the "modified" flag, but not the "undoable" flag */ + clrflag(file, MODIFIED); + significant = FALSE; + + /* report lines & characters */ +#if MSDOS || TOS + bytes += nlines; /* for the inserted carriage returns */ +#endif + msg("Wrote \"%s\" %ld lines, %ld characters", filename, nlines, bytes); + + /* close the file */ + close(fd); + + return TRUE; +} + + +/* This function deletes the temporary file. If the file has been modified + * and "bang" is FALSE, then it returns FALSE without doing anything; else + * it returns TRUE. + * + * If the "autowrite" option is set, then instead of returning FALSE when + * the file has been modified and "bang" is false, it will call tmpend(). + */ +int tmpabort(bang) + int bang; +{ + /* if there is no file, return successfully */ + if (tmpfd < 0) + { + return TRUE; + } + + /* see if we must return FALSE -- can't quit */ + if (!bang && tstflag(file, MODIFIED)) + { + /* if "autowrite" is set, then act like tmpend() */ + if (*o_autowrite) + return tmpend(bang); + else + return FALSE; + } + + /* delete the tmp file */ + cutswitch(); + strcpy(prevorig, origname); + prevline = markline(cursor); + *origname = '\0'; + origtime = 0L; + blkinit(); + nlines = 0; + initflags(); + return TRUE; +} + +/* This function saves the file if it has been modified, and then deletes + * the temporary file. Returns TRUE if successful, or FALSE if the file + * needs to be saved but can't be. When it returns FALSE, it will not have + * deleted the tmp file, either. + */ +int tmpend(bang) + int bang; +{ + /* save the file if it has been modified */ + if (tstflag(file, MODIFIED) && !tmpsave((char *)0, FALSE) && !bang) + { + return FALSE; + } + + /* delete the tmp file */ + tmpabort(TRUE); + + return TRUE; +} + + +/* If the tmp file has been changed, then this function will force those + * changes to be written to the disk, so that the tmp file will survive a + * system crash or power failure. + */ +#if AMIGA || MSDOS || TOS +sync() +{ + /* MS-DOS and TOS don't flush their buffers until the file is closed, + * so here we close the tmp file and then immediately reopen it. + */ + close(tmpfd); + tmpfd = open(tmpname, O_RDWR | O_BINARY); + return 0; +} +#endif + + +/* This function stores the file's name in the second block of the temp file. + * SLEAZE ALERT! SLEAZE ALERT! The "tmpblk" buffer is probably being used + * to store the arguments to a command, so we can't use it here. Instead, + * we'll borrow the buffer that is used for "shift-U". + */ +storename(name) + char *name; /* the name of the file - normally origname */ +{ +#ifndef CRUNCH + int len; + char *ptr; +#endif + + /* we're going to clobber the U_text buffer, so reset U_line */ + U_line = 0L; + + if (!name) + { + strncpy(U_text, "", BLKSIZE); + U_text[1] = 127; + } +#ifndef CRUNCH + else if (*name != SLASH) + { + /* get the directory name */ + ptr = getcwd(U_text, BLKSIZE); + if (ptr != U_text) + { + strcpy(U_text, ptr); + } + + /* append a slash to the directory name */ + len = strlen(U_text); + U_text[len++] = SLASH; + + /* append the filename, padded with heaps o' NULs */ + strncpy(U_text + len, *name ? name : "foo", BLKSIZE - len); + } +#endif + else + { + /* copy the filename into U_text */ + strncpy(U_text, *name ? name : "foo", BLKSIZE); + } + + if (tmpfd >= 0) + { + /* write the name out to second block of the temp file */ + lseek(tmpfd, (long)BLKSIZE, 0); + write(tmpfd, U_text, (unsigned)BLKSIZE); + } + return 0; +} + + + +/* This function handles deadly signals. It restores sanity to the terminal + * preserves the current temp file, and deletes any old temp files. + */ +int deathtrap(sig) + int sig; /* the deadly signal that we caught */ +{ + char *why; + + /* restore the terminal's sanity */ + endwin(); + +#ifdef CRUNCH + why = "-Elvis died"; +#else + /* give a more specific description of how Elvis died */ + switch (sig) + { +# ifdef SIGHUP + case SIGHUP: why = "-the modem lost its carrier"; break; +# endif +# ifndef DEBUG +# ifdef SIGILL + case SIGILL: why = "-Elvis hit an illegal instruction"; break; +# endif +# ifdef SIGBUS + case SIGBUS: why = "-Elvis had a bus error"; break; +# endif +# if defined(SIGSEGV) && !defined(TOS) + case SIGSEGV: why = "-Elvis had a segmentation violation"; break; +# endif +# ifdef SIGSYS + case SIGSYS: why = "-Elvis munged a system call"; break; +# endif +# endif /* !DEBUG */ +# ifdef SIGPIPE + case SIGPIPE: why = "-the pipe reader died"; break; +# endif +# ifdef SIGTERM + case SIGTERM: why = "-Elvis was terminated"; break; +# endif +# if !MINIX +# ifdef SIGUSR1 + case SIGUSR1: why = "-Elvis was killed via SIGUSR1"; break; +# endif +# ifdef SIGUSR2 + case SIGUSR2: why = "-Elvis was killed via SIGUSR2"; break; +# endif +# endif + default: why = "-Elvis died"; break; + } +#endif + + /* if we had a temp file going, then preserve it */ + if (tmpnum > 0 && tmpfd >= 0) + { + close(tmpfd); + sprintf(tmpblk.c, "%s \"%s\" %s", PRESERVE, why, tmpname); + system(tmpblk.c); + } + + /* delete any old temp files */ + cutend(); + + /* exit with the proper exit status */ + exit(sig); +} diff --git a/commands/elvis/unix.c b/commands/elvis/unix.c new file mode 100755 index 000000000..cf101261d --- /dev/null +++ b/commands/elvis/unix.c @@ -0,0 +1,226 @@ +/* unix.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains the unix-specific versions the ttyread() functions. + * There are actually three versions of ttyread() defined here, because + * BSD, SysV, and V7 all need quite different implementations. + */ + +#include "config.h" +#if ANY_UNIX +# include "vi.h" + +# if BSD +/* For BSD, we use select() to wait for characters to become available, + * and then do a read() to actually get the characters. We also try to + * handle SIGWINCH -- if the signal arrives during the select() call, then + * we adjust the o_columns and o_lines variables, and fake a control-L. + */ +# include <sys/types.h> +# include <sys/time.h> +int ttyread(buf, len, time) + char *buf; /* where to store the gotten characters */ + int len; /* maximum number of characters to read */ + int time; /* maximum time to allow for reading */ +{ + fd_set rd; /* the file descriptors that we want to read from */ + static tty; /* 'y' if reading from tty, or 'n' if not a tty */ + int i; + struct timeval t; + struct timeval *tp; + + + /* do we know whether this is a tty or not? */ + if (!tty) + { + tty = (isatty(0) ? 'y' : 'n'); + } + + /* compute the timeout value */ + if (time) + { + t.tv_sec = time / 10; + t.tv_usec = (time % 10) * 100000L; + tp = &t; + } + else + { + tp = (struct timeval *)0; + } + + /* loop until we get characters or a definite EOF */ + for (;;) + { + if (tty == 'y') + { + /* wait until timeout or characters are available */ + FD_ZERO(&rd); + FD_SET(0, &rd); + i = select(1, &rd, (fd_set *)0, (fd_set *)0, tp); + } + else + { + /* if reading from a file or pipe, never timeout! + * (This also affects the way that EOF is detected) + */ + i = 1; + } + + /* react accordingly... */ + switch (i) + { + case -1: /* assume we got an EINTR because of SIGWINCH */ + if (*o_lines != LINES || *o_columns != COLS) + { + *o_lines = LINES; + *o_columns = COLS; +#ifndef CRUNCH + if (!wset) + { + *o_window = LINES - 1; + } +#endif + if (mode != MODE_EX) + { + /* pretend the user hit ^L */ + *buf = ctrl('L'); + return 1; + } + } + break; + + case 0: /* timeout */ + return 0; + + default: /* characters available */ + return read(0, buf, len); + } + } +} +# else + +# if M_SYSV +/* For System-V or Coherent, we use VMIN/VTIME to implement the timeout. + * For no timeout, VMIN should be 1 and VTIME should be 0; for timeout, + * VMIN should be 0 and VTIME should be the timeout value. + */ +# include <termio.h> +int ttyread(buf, len, time) + char *buf; /* where to store the gotten characters */ + int len; /* maximum number of characters to read */ + int time; /* maximum time to allow for reading */ +{ + struct termio tio; + int bytes; /* number of bytes actually read */ + + /* arrange for timeout */ + ioctl(0, TCGETA, &tio); + if (time) + { + tio.c_cc[VMIN] = 0; + tio.c_cc[VTIME] = time; + } + else + { + tio.c_cc[VMIN] = 1; + tio.c_cc[VTIME] = 0; + } + ioctl(0, TCSETA, &tio); + + /* Perform the read. Loop if EINTR error happens */ + while ((bytes = read(0, buf, len)) < 0) + { + /* probably EINTR error because a SIGWINCH was received */ + if (*o_lines != LINES || *o_columns != COLS) + { + *o_lines = LINES; + *o_columns = COLS; +#ifndef CRUNCH + if (!wset) + { + *o_window = LINES - 1; + } +#endif + if (mode != MODE_EX) + { + /* pretend the user hit ^L */ + *buf = ctrl('L'); + return 1; + } + } + } + + /* return the number of bytes read */ + return bytes; + + /* NOTE: The terminal may be left in a timeout-mode after this function + * returns. This shouldn't be a problem since Elvis *NEVER* tries to + * read from the keyboard except through this function. + */ +} + +# else /* any other version of UNIX, assume it is V7 compatible */ + +/* For V7 UNIX (including Minix) we set an alarm() before doing a blocking + * read(), and assume that the SIGALRM signal will cause the read() function + * to give up. + */ + +#include <setjmp.h> + +static jmp_buf env; + +/*ARGSUSED*/ +int dummy(signo) + int signo; +{ + longjmp(env, 1); +} +int ttyread(buf, len, time) + char *buf; /* where to store the gotten characters */ + int len; /* maximum number of characters to read */ + int time; /* maximum time to allow for reading */ +{ + /* arrange for timeout */ +#if __GNUC__ || _ANSI + signal(SIGALRM, (void (*)()) dummy); +#else + signal(SIGALRM, dummy); +#endif + alarm(time); + + /* perform the blocking read */ + if (setjmp(env) == 0) + { + len = read(0, buf, len); + } + else /* I guess we timed out */ + { + len = 0; + } + + /* cancel the alarm */ +#if _ANSI + signal(SIGALRM, (void (*)())dummy); /* work around a bug in Minix */ +#else + signal(SIGALRM, dummy); /* work around a bug in Minix */ +#endif + alarm(0); + + /* return the number of bytes read */ + if (len < 0) + len = 0; + return len; +} + +# endif /* !(M_SYSV || COHERENT) */ +# endif /* !BSD */ + +#endif /* ANY_UNIX */ diff --git a/commands/elvis/vars.c b/commands/elvis/vars.c new file mode 100755 index 000000000..19b5d193a --- /dev/null +++ b/commands/elvis/vars.c @@ -0,0 +1,115 @@ +/* vars.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains variables which weren't happy anyplace else */ + +#include "config.h" +#include "vi.h" + +/*------------------------------------------------------------------------*/ + +/* used to remember whether the file has been modified */ +struct _viflags viflags; + +/* used to access the tmp file */ +long lnum[MAXBLKS]; +long nlines; +int tmpfd = -1; +int tmpnum; +#ifndef CRUNCH +int wset = FALSE; +#endif + +/* used to keep track of the current file & alternate file */ +long origtime; +char origname[256]; +char prevorig[256]; +long prevline = 1; + +/* used to track various places in the text */ +MARK mark[NMARKS]; /* marks 'a through 'z, plus mark '' */ +MARK cursor; /* the cursor position within the file */ + +/* which mode of the editor we're in */ +int mode; /* vi mode? ex mode? quitting? */ + +/* used to manage the args list */ +char args[BLKSIZE]; /* list of filenames to edit */ +int argno; /* index of current file in args list */ +int nargs; /* number of filenames in args[] */ + +/* dummy var, never explicitly referenced */ +int bavar; /* used only in BeforeAfter macros */ + +/* used to detect changes that invalidate cached text/blocks */ +long changes; /* incremented when file is changed */ +int significant; /* boolean: was a *REAL* change made? */ + +/* used to support the pfetch() macro */ +int plen; /* length of the line */ +long pline; /* line number that len refers to */ +long pchgs; /* "changes" level that len refers to */ +char *ptext; /* text of previous line, if valid */ + +/* misc temporary storage - mostly for strings */ +BLK tmpblk; /* a block used to accumulate changes */ + +/* screen oriented stuff */ +long topline; /* file line number of top line */ +int leftcol; /* column number of left col */ +int physcol; /* physical column number that cursor is on */ +int physrow; /* physical row number that cursor is on */ + +/* used to help minimize that "[Hit a key to continue]" message */ +int exwrote; /* Boolean: was the last ex command wordy? */ + +/* This variable affects the behaviour of certain functions -- most importantly + * the input function. + */ +int doingdot; /* boolean: are we doing the "." command? */ + +/* This variable affects the behaviour of the ":s" command, and it is also + * used to detect & prohibit nesting of ":g" commands + */ +int doingglobal; /* boolean: are doing a ":g" command? */ + +/* This variable is zeroed before a command executes, and later ORed with the + * command's flags after the command has been executed. It is used to force + * certain flags to be TRUE for *some* invocations of a particular command. + * For example, "/regexp/+offset" forces the LNMD flag, and sometimes a "p" + * or "P" command will force FRNT. + */ +int force_flags; + +/* These are used for reporting multi-line changes to the user */ +long rptlines; /* number of lines affected by a command */ +char *rptlabel; /* description of how lines were affected */ + +/* These store info that pertains to the shift-U command */ +long U_line; /* line# of the undoable line, or 0l for none */ +char U_text[BLKSIZE]; /* contents of the undoable line */ + + +#ifndef NO_VISIBLE +/* These are used to implement the 'v' and 'V' commands */ +MARK V_from; /* starting point for v or V */ +int V_linemd; /* boolean: doing line-mode version? (V, not v) */ +#endif + +/* Bigger stack req'ed for TOS and TURBOC */ + +#if TOS +long _stksize = 16384; +#endif + +#if TURBOC +#include <dos.h> +extern unsigned _stklen = 16384U; +#endif diff --git a/commands/elvis/vcmd.c b/commands/elvis/vcmd.c new file mode 100755 index 000000000..494f7e1f7 --- /dev/null +++ b/commands/elvis/vcmd.c @@ -0,0 +1,975 @@ +/* vcmd.c */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +/* This file contains the functions that handle VI commands */ + + +#include "config.h" +#include "ctype.h" +#include "vi.h" +#if MSDOS +# include <process.h> +# include <string.h> +#endif +#if TOS +# include <osbind.h> +# include <string.h> +#endif +#if OSK +# include <stdio.h> +#endif + + +/* This function puts the editor in EX mode */ +MARK v_quit() +{ + move(LINES - 1, 0); + mode = MODE_EX; + return cursor; +} + +/* This function causes the screen to be redrawn */ +MARK v_redraw() +{ + redraw(MARK_UNSET, FALSE); + return cursor; +} + +/* This function executes a string of EX commands, and waits for a user keystroke + * before returning to the VI screen. If that keystroke is another ':', then + * another EX command is read and executed. + */ +/*ARGSUSED*/ +MARK v_1ex(m, text) + MARK m; /* the current line */ + char *text; /* the first command to execute */ +{ + /* run the command. be careful about modes & output */ + exwrote = (mode == MODE_COLON); + doexcmd(text); + exrefresh(); + + /* if mode is no longer MODE_VI, then we should quit right away! */ + if (mode != MODE_VI && mode != MODE_COLON) + return cursor; + + /* The command did some output. Wait for a keystoke. */ + if (exwrote) + { + mode = MODE_VI; + msg("[Hit <RETURN> to continue]"); + if (getkey(0) == ':') + { mode = MODE_COLON; + addch('\n'); + } + else + redraw(MARK_UNSET, FALSE); + } + + return cursor; +} + +/* This function undoes the last change */ +/*ARGSUSED*/ +MARK v_undo(m) + MARK m; /* (ignored) */ +{ + if (undo()) + { + redraw(MARK_UNSET, FALSE); + } + return cursor; +} + +/* This function deletes the character(s) that the cursor is on */ +MARK v_xchar(m, cnt, cmd) + MARK m; /* where to start deletions */ + long cnt; /* number of chars to delete */ + int cmd; /* either 'x' or 'X' */ +{ + DEFAULT(1); + + /* for 'X', adjust so chars are deleted *BEFORE* cursor */ + if (cmd == 'X') + { + if (markidx(m) < cnt) + return MARK_UNSET; + m -= cnt; + } + + /* make sure we don't try to delete more thars than there are */ + pfetch(markline(m)); + if (markidx(m + cnt) > plen) + { + cnt = plen - markidx(m); + } + if (cnt == 0L) + { + return MARK_UNSET; + } + + /* do it */ + ChangeText + { + cut(m, m + cnt); + delete(m, m + cnt); + } + return m; +} + +/* This function defines a mark */ +/*ARGSUSED*/ +MARK v_mark(m, count, key) + MARK m; /* where the mark will be */ + long count; /* (ignored) */ + int key; /* the ASCII label of the mark */ +{ + if (key < 'a' || key > 'z') + { + msg("Marks must be from a to z"); + } + else + { + mark[key - 'a'] = m; + } + return m; +} + +/* This function toggles upper & lower case letters */ +MARK v_ulcase(m, cnt) + MARK m; /* where to make the change */ + long cnt; /* number of chars to flip */ +{ + REG char *pos; + REG int j; + + DEFAULT(1); + + /* fetch the current version of the line */ + pfetch(markline(m)); + + /* for each position in the line */ + for (j = 0, pos = &ptext[markidx(m)]; j < cnt && *pos; j++, pos++) + { + if (isupper(*pos)) + { + tmpblk.c[j] = tolower(*pos); + } + else + { + tmpblk.c[j] = toupper(*pos); + } + } + + /* if the new text is different from the old, then change it */ + if (strncmp(tmpblk.c, &ptext[markidx(m)], j)) + { + ChangeText + { + tmpblk.c[j] = '\0'; + change(m, m + j, tmpblk.c); + } + } + + return m + j; +} + + +MARK v_replace(m, cnt, key) + MARK m; /* first char to be replaced */ + long cnt; /* number of chars to replace */ + int key; /* what to replace them with */ +{ + REG char *text; + REG int i; + + DEFAULT(1); + + /* map ^M to '\n' */ + if (key == '\r') + { + key = '\n'; + } + + /* make sure the resulting line isn't too long */ + if (cnt > BLKSIZE - 2 - markidx(m)) + { + cnt = BLKSIZE - 2 - markidx(m); + } + + /* build a string of the desired character with the desired length */ + for (text = tmpblk.c, i = cnt; i > 0; i--) + { + *text++ = key; + } + *text = '\0'; + + /* make sure cnt doesn't extend past EOL */ + pfetch(markline(m)); + key = markidx(m); + if (key + cnt > plen) + { + cnt = plen - key; + } + + /* do the replacement */ + ChangeText + { + change(m, m + cnt, tmpblk.c); + } + + if (*tmpblk.c == '\n') + { + return (m & ~(BLKSIZE - 1)) + cnt * BLKSIZE; + } + else + { + return m + cnt - 1; + } +} + +MARK v_overtype(m) + MARK m; /* where to start overtyping */ +{ + MARK end; /* end of a substitution */ + static long width; /* width of a single-line replace */ + + /* the "doingdot" version of replace is really a substitution */ + if (doingdot) + { + /* was the last one really repeatable? */ + if (width < 0) + { + msg("Can't repeat a multi-line overtype command"); + return MARK_UNSET; + } + + /* replacing nothing by nothing? Don't bother */ + if (width == 0) + { + return m; + } + + /* replace some chars by repeated text */ + return v_subst(m, width); + } + + /* Normally, we input starting here, in replace mode */ + ChangeText + { + end = input(m, m, WHEN_VIREP, FALSE); + } + + /* if we ended on the same line we started on, then this + * overtype is repeatable via the dot key. + */ + if (markline(end) == markline(m) && end >= m - 1L) + { + width = end - m + 1L; + } + else /* it isn't repeatable */ + { + width = -1L; + } + + return end; +} + + +/* This function selects which cut buffer to use */ +/*ARGSUSED*/ +MARK v_selcut(m, cnt, key) + MARK m; + long cnt; + int key; +{ + cutname(key); + return m; +} + +/* This function pastes text from a cut buffer */ +/*ARGSUSED*/ +MARK v_paste(m, cnt, cmd) + MARK m; /* where to paste the text */ + long cnt; /* (ignored) */ + int cmd; /* either 'p' or 'P' */ +{ + MARK dest; + + ChangeText + { + /* paste the text, and find out where it ends */ + dest = paste(m, cmd == 'p', TRUE); + + /* was that a line-mode paste? */ + if (dest && markline(dest) != markline(m)) + { + /* line-mode pastes leave the cursor at the front + * of the first pasted line. + */ + dest = m; + if (cmd == 'p') + { + dest += BLKSIZE; + } + force_flags |= FRNT; + } + } + return dest; +} + +/* This function yanks text into a cut buffer */ +MARK v_yank(m, n) + MARK m, n; /* range of text to yank */ +{ + cut(m, n); + return m; +} + +/* This function deletes a range of text */ +MARK v_delete(m, n) + MARK m, n; /* range of text to delete */ +{ + /* illegal to try and delete nothing */ + if (n <= m) + { + return MARK_UNSET; + } + + /* Do it */ + ChangeText + { + cut(m, n); + delete(m, n); + } + return m; +} + + +/* This starts input mode without deleting anything */ +MARK v_insert(m, cnt, key) + MARK m; /* where to start (sort of) */ + long cnt; /* repeat how many times? */ + int key; /* what command is this for? {a,A,i,I,o,O} */ +{ + int wasdot; + long reps; + int above; /* boolean: new line going above old line? */ + + DEFAULT(1); + + ChangeText + { + /* tweak the insertion point, based on command key */ + above = FALSE; + switch (key) + { + case 'i': + break; + + case 'a': + pfetch(markline(m)); + if (plen > 0) + { + m++; + } + break; + + case 'I': + m = m_front(m, 1L); + break; + + case 'A': + pfetch(markline(m)); + m = (m & ~(BLKSIZE - 1)) + plen; + break; + + case 'O': + m &= ~(BLKSIZE - 1); + add(m, "\n"); + above = TRUE; + break; + + case 'o': + m = (m & ~(BLKSIZE - 1)) + BLKSIZE; + add(m, "\n"); + break; + } + + /* insert the same text once or more */ + for (reps = cnt, wasdot = doingdot; reps > 0; reps--, doingdot = TRUE) + { + m = input(m, m, WHEN_VIINP, above) + 1; + } + if (markidx(m) > 0) + { + m--; + } + + doingdot = wasdot; + } + +#ifndef CRUNCH +# ifndef NO_EXTENSIONS + if (key == 'i' && *o_inputmode && mode == MODE_VI) + { + msg("Now in command mode! To return to input mode, hit <i>"); + } +# endif +#endif + + return m; +} + +/* This starts input mode with some text deleted */ +MARK v_change(m, n) + MARK m, n; /* the range of text to change */ +{ + int lnmode; /* is this a line-mode change? */ + + /* swap them if they're in reverse order */ + if (m > n) + { + MARK tmp; + tmp = m; + m = n; + n = tmp; + } + + /* for line mode, retain the last newline char */ + lnmode = (markidx(m) == 0 && markidx(n) == 0 && m != n); + if (lnmode) + { + n -= BLKSIZE; + pfetch(markline(n)); + n = (n & ~(BLKSIZE - 1)) + plen; + } + + ChangeText + { + cut(m, n); + m = input(m, n, WHEN_VIINP, FALSE); + } + + return m; +} + +/* This function replaces a given number of characters with input */ +MARK v_subst(m, cnt) + MARK m; /* where substitutions start */ + long cnt; /* number of chars to replace */ +{ + DEFAULT(1); + + /* make sure we don't try replacing past EOL */ + pfetch(markline(m)); + if (markidx(m) + cnt > plen) + { + cnt = plen - markidx(m); + } + + /* Go for it! */ + ChangeText + { + cut(m, m + cnt); + m = input(m, m + cnt, WHEN_VIINP, FALSE); + } + return m; +} + +/* This calls the ex "join" command to join some lines together */ +MARK v_join(m, cnt) + MARK m; /* the first line to be joined */ + long cnt; /* number of other lines to join */ +{ + MARK joint; /* where the lines were joined */ + + DEFAULT(1); + + /* figure out where the joint will be */ + pfetch(markline(m)); + joint = (m & ~(BLKSIZE - 1)) + plen; + + /* join the lines */ + cmd_join(m, m + MARK_AT_LINE(cnt), CMD_JOIN, 0, ""); + + /* the cursor should be left at the joint */ + return joint; +} + + +/* This calls the ex "<" command to shift some lines left */ +MARK v_lshift(m, n) + MARK m, n; /* range of lines to shift */ +{ + /* adjust for inclusive endmarks in ex */ + n -= BLKSIZE; + + cmd_shift(m, n, CMD_SHIFTL, FALSE, (char *)0); + + return m; +} + +/* This calls the ex ">" command to shift some lines right */ +MARK v_rshift(m, n) + MARK m, n; /* range of lines to shift */ +{ + /* adjust for inclusive endmarks in ex */ + n -= BLKSIZE; + + cmd_shift(m, n, CMD_SHIFTR, FALSE, (char *)0); + + return m; +} + +/* This filters some lines through a preset program, to reformat them */ +MARK v_reformat(m, n) + MARK m, n; /* range of lines to shift */ +{ + /* adjust for inclusive endmarks in ex */ + n -= BLKSIZE; + + /* run the filter command */ + filter(m, n, o_equalprg, TRUE); + + redraw(MARK_UNSET, FALSE); + return m; +} + + +/* This runs some lines through a filter program */ +MARK v_filter(m, n) + MARK m, n; /* range of lines to shift */ +{ + char cmdln[150]; /* a shell command line */ + + /* adjust for inclusive endmarks in ex */ + n -= BLKSIZE; + + if (vgets('!', cmdln, sizeof(cmdln)) > 0) + { + filter(m, n, cmdln, TRUE); + } + + redraw(MARK_UNSET, FALSE); + return m; +} + + +/* This function runs the ex "file" command to show the file's status */ +MARK v_status() +{ + cmd_file(cursor, cursor, CMD_FILE, 0, ""); + return cursor; +} + + +/* This function runs the ":&" command to repeat the previous :s// */ +MARK v_again(m, n) + MARK m, n; +{ + cmd_substitute(m, n - BLKSIZE, CMD_SUBAGAIN, TRUE, ""); + return cursor; +} + + + +/* This function switches to the previous file, if possible */ +MARK v_switch() +{ + if (!*prevorig) + msg("No previous file"); + else + { strcpy(tmpblk.c, prevorig); + cmd_edit(cursor, cursor, CMD_EDIT, 0, tmpblk.c); + } + return cursor; +} + +/* This function does a tag search on a keyword */ +/*ARGSUSED*/ +MARK v_tag(keyword, m, cnt) + char *keyword; + MARK m; + long cnt; +{ + /* move the cursor to the start of the tag name, where m is */ + cursor = m; + + /* perform the tag search */ + cmd_tag(cursor, cursor, CMD_TAG, 0, keyword); + + return cursor; +} + +#ifndef NO_EXTENSIONS +/* This function looks up a keyword by calling the helpprog program */ +/*ARGSUSED*/ +MARK v_keyword(keyword, m, cnt) + char *keyword; + MARK m; + long cnt; +{ + int waswarn; + char cmdline[130]; + + move(LINES - 1, 0); + addstr("---------------------------------------------------------\n"); + clrtoeol(); + refresh(); + sprintf(cmdline, "%s %s", o_keywordprg, keyword); + waswarn = *o_warn; + *o_warn = FALSE; + suspend_curses(); + if (system(cmdline)) + { + addstr("<<< failed >>>\n"); + } + resume_curses(FALSE); + mode = MODE_VI; + redraw(MARK_UNSET, FALSE); + *o_warn = waswarn; + + return m; +} + + + +MARK v_increment(keyword, m, cnt) + char *keyword; + MARK m; + long cnt; +{ + static sign; + char newval[12]; + long atol(); + + DEFAULT(1); + + /* get one more keystroke, unless doingdot */ + if (!doingdot) + { + sign = getkey(0); + } + + /* adjust the number, based on that second keystroke */ + switch (sign) + { + case '+': + case '#': + cnt = atol(keyword) + cnt; + break; + + case '-': + cnt = atol(keyword) - cnt; + break; + + case '=': + break; + + default: + return MARK_UNSET; + } + sprintf(newval, "%ld", cnt); + + ChangeText + { + change(m, m + strlen(keyword), newval); + } + + return m; +} +#endif + + +/* This function acts like the EX command "xit" */ +/*ARGSUSED*/ +MARK v_xit(m, cnt, key) + MARK m; /* ignored */ + long cnt; /* ignored */ + int key; /* must be a second 'Z' */ +{ + /* if second char wasn't 'Z', fail */ + if (key != 'Z') + { + return MARK_UNSET; + } + + /* move the cursor to the bottom of the screen */ + move(LINES - 1, 0); + clrtoeol(); + + /* do the xit command */ + cmd_xit(m, m, CMD_XIT, FALSE, ""); + + /* return the cursor */ + return m; +} + + +/* This function undoes changes to a single line, if possible */ +MARK v_undoline(m) + MARK m; /* where we hope to undo the change */ +{ + /* make sure we have the right line in the buffer */ + if (markline(m) != U_line) + { + return MARK_UNSET; + } + + /* fix it */ + ChangeText + { + strcat(U_text, "\n"); + change(MARK_AT_LINE(U_line), MARK_AT_LINE(U_line + 1), U_text); + } + + /* nothing in the buffer anymore */ + U_line = -1L; + + /* return, with the cursor at the front of the line */ + return m & ~(BLKSIZE - 1); +} + + +#ifndef NO_ERRLIST +MARK v_errlist(m) + MARK m; +{ + cmd_errlist(m, m, CMD_ERRLIST, FALSE, ""); + return cursor; +} +#endif + + +#ifndef NO_AT +/*ARGSUSED*/ +MARK v_at(m, cnt, key) + MARK m; + long cnt; + int key; +{ + int size; + + size = cb2str(key, tmpblk.c, BLKSIZE); + if (size <= 0 || size == BLKSIZE) + { + return MARK_UNSET; + } + + execmap(0, tmpblk.c, FALSE); + return cursor; +} +#endif + + +#ifdef SIGTSTP +MARK v_suspend() +{ + cmd_suspend(MARK_UNSET, MARK_UNSET, CMD_SUSPEND, FALSE, ""); + return MARK_UNSET; +} +#endif + + +#ifndef NO_VISIBLE +/*ARGSUSED*/ +MARK v_start(m, cnt, cmd) + MARK m; /* starting point for a v or V command */ + long cnt; /* ignored */ + int cmd; /* either 'v' or 'V' */ +{ + if (V_from) + { + V_from = MARK_UNSET; + } + else + { + V_from = m; + V_linemd = isupper(cmd); + } + return m; +} +#endif + +#ifndef NO_POPUP +# define MENU_HEIGHT 11 +# define MENU_WIDTH 23 +MARK v_popup(m, n) + MARK m, n; /* the range of text to change */ +{ + int i; + int y, x; /* position where the window will pop up at */ + int key; /* keystroke from the user */ + int sel; /* index of the selected operation */ + static int dfltsel;/* default value of sel */ + static char *labels[11] = + { + "ESC cancel! ", + " d delete (cut) ", + " y yank (copy) ", + " p paste after ", + " P paste before ", + " > more indented ", + " < less indented ", + " = reformat ", + " ! external filter ", + " ZZ save & exit ", + " u undo previous " + }; + + /* try to position the menu near the cursor */ + x = physcol - (MENU_WIDTH / 2); + if (x < 0) + x = 0; + else if (x + MENU_WIDTH + 2 > COLS) + x = COLS - MENU_WIDTH - 2; + if (markline(cursor) < topline || markline(cursor) > botline) + y = 0; + else if (markline(cursor) + 1L + MENU_HEIGHT > botline) + y = (int)(markline(cursor) - topline) - MENU_HEIGHT; + else + y = (int)(markline(cursor) - topline) + 1L; + + /* draw the menu */ + for (sel = 0; sel < MENU_HEIGHT; sel++) + { + move(y + sel, x); + do_POPUP(); + if (sel == dfltsel) + qaddstr("-->"); + else + qaddstr(" "); + qaddstr(labels[sel]); + do_SE(); + } + + /* get a selection */ + move(y + dfltsel, x + 4); + for (sel = dfltsel; (key = getkey(WHEN_POPUP)) != '\\' && key != '\r'; ) + { + /* erase the selection arrow */ + move(y + sel, x); + do_POPUP(); + qaddstr(" "); + qaddstr(labels[sel]); + do_SE(); + + /* process the user's keystroke */ + if (key == 'j' && sel < MENU_HEIGHT - 1) + { + sel++; + } + else if (key == 'k' && sel > 0) + { + sel--; + } + else if (key == '\033') + { + sel = 0; + break; + } + else + { + for (i = 1; i < MENU_HEIGHT && labels[i][1] != key; i++) + { + } + if (i < MENU_HEIGHT) + { + sel = i; + break; + } + } + + /* redraw the arrow, possibly in a new place */ + move(y + sel, x); + do_POPUP(); + qaddstr("-->"); + qaddstr(labels[sel]); + do_SE(); + move(y + sel, x + 4); + } + + /* in most cases, the default selection is "paste after" */ + dfltsel = 3; + + /* perform the appropriate action */ + switch (sel) + { + case 0: + m = cursor; + dfltsel = 0; + break; + + case 1: /* delete (cut) */ + m = v_delete(m, n); + break; + + case 2: /* yank (copy) */ + m = v_yank(m, n); + break; + + case 3: /* paste after */ + m = v_paste(n, 1L, 'P'); + break; + + case 4: /* paste before */ + m = v_paste(m, 1L, 'P'); + dfltsel = 4; + break; + + case 5: /* more indented */ + m = v_rshift(m, n); + dfltsel = 5; + break; + + case 6: /* less indented */ + m = v_lshift(m, n); + dfltsel = 6; + break; + + case 7: /* reformat */ + m = v_reformat(m, n); + dfltsel = 7; + break; + + case 8: /* external filter */ + m = v_filter(m, n); + dfltsel = 0; + break; + + case 9: /* save & exit */ + /* get confirmation first */ + do + { + key = getkey(0); + } while (key != '\\' && key != 'Z' && key != '\r' /* good */ + && key != '\033'); /* bad */ + if (key != '\033') + { + m = v_xit(m, 0L, 'Z'); + } + break; + + case 10: /* undo previous */ + m = v_undo(m); + dfltsel = 9; + break; + } + + /* arrange for the menu to be erased (except that "chg from kbd" + * already erased it, and "save & exit" doesn't care) + */ + if (sel != 5 && sel != 9) + redraw(MARK_UNSET, FALSE); + + return m; +} +#endif /* undef NO_POPUP */ diff --git a/commands/elvis/vi.c b/commands/elvis/vi.c new file mode 100755 index 000000000..24c46dc3f --- /dev/null +++ b/commands/elvis/vi.c @@ -0,0 +1,815 @@ +/* vi.c */ + +/* Author: + * Steve Kirkendall + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + + +#include "config.h" +#include "ctype.h" +#include "vi.h" + + + +/* This array describes what each key does */ +#define NO_FUNC (MARK (*)())0 + +#define NO_ARGS 0 +#define CURSOR 1 +#define CURSOR_CNT_KEY 2 +#define CURSOR_MOVED 3 +#define CURSOR_EOL 4 +#define ZERO 5 +#define DIGIT 6 +#define CURSOR_TEXT 7 +#define KEYWORD 8 +#define ARGSMASK 0x0f +#define C_C_K_REP1 (CURSOR_CNT_KEY | 0x10) +#define C_C_K_CUT (CURSOR_CNT_KEY | 0x20) +#define C_C_K_MARK (CURSOR_CNT_KEY | 0x30) +#define C_C_K_CHAR (CURSOR_CNT_KEY | 0x40) +#ifndef NO_SHOWMODE +static int keymodes[] = {0, WHEN_REP1, WHEN_CUT, WHEN_MARK, WHEN_CHAR}; +# define KEYMODE(args) (keymodes[(args) >> 4]) +#else +# define KEYMODE(args) 0 +#endif + +static struct keystru +{ + MARK (*func)(); /* the function to run */ + uchar args; /* description of the args needed */ +#ifndef NO_VISIBLE + short flags; +#else + uchar flags; /* other stuff */ +#endif +} + vikeys[] = +{ +/* NUL not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#ifndef NO_EXTENSIONS +/* ^A find cursor word */ {m_wsrch, KEYWORD, MVMT|NREL|VIZ}, +#else +/* ^A not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* ^B page backward */ {m_scroll, CURSOR, FRNT|VIZ}, +/* ^C not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* ^D scroll dn 1/2page*/ {m_scroll, CURSOR, NCOL|VIZ}, +/* ^E scroll up */ {m_scroll, CURSOR, NCOL|VIZ}, +/* ^F page forward */ {m_scroll, CURSOR, FRNT|VIZ}, +/* ^G show file status */ {v_status, NO_ARGS, NO_FLAGS}, +/* ^H move left, like h*/ {m_left, CURSOR, MVMT|VIZ}, +/* ^I not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* ^J move down */ {m_updnto, CURSOR, MVMT|LNMD|VIZ|INCL}, +/* ^K not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* ^L redraw screen */ {v_redraw, NO_ARGS, NO_FLAGS|VIZ}, +/* ^M mv front next ln */ {m_updnto, CURSOR, MVMT|FRNT|LNMD|VIZ|INCL}, +/* ^N move down */ {m_updnto, CURSOR, MVMT|LNMD|VIZ|INCL|NCOL}, +/* ^O not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* ^P move up */ {m_updnto, CURSOR, MVMT|LNMD|VIZ|INCL|NCOL}, +/* ^Q not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* ^R redraw screen */ {v_redraw, NO_ARGS, NO_FLAGS|VIZ}, +/* ^S not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* ^T not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* ^U scroll up 1/2page*/ {m_scroll, CURSOR, NCOL|VIZ}, +/* ^V not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* ^W not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* ^X move to phys col */ {m_tocol, CURSOR, MVMT|NREL|VIZ}, +/* ^Y scroll down */ {m_scroll, CURSOR, NCOL|VIZ}, +#ifdef SIGTSTP +/* ^Z suspend elvis */ {v_suspend, NO_ARGS, NO_FLAGS}, +#else +/* ^Z not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* ESC not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* ^\ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* ^] keyword is tag */ {v_tag, KEYWORD, NO_FLAGS}, +/* ^^ previous file */ {v_switch, CURSOR, NO_FLAGS}, +/* ^_ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* SPC move right,like l*/ {m_right, CURSOR, MVMT|INCL|VIZ}, +/* ! run thru filter */ {v_filter, CURSOR_MOVED, FRNT|LNMD|INCL|VIZ}, +/* " select cut buffer*/ {v_selcut, C_C_K_CUT, PTMV|VIZ}, +#ifndef NO_EXTENSIONS +/* # increment number */ {v_increment, KEYWORD, SDOT}, +#else +/* # not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* $ move to rear */ {m_rear, CURSOR, MVMT|INCL|VIZ}, +/* % move to match */ {m_match, CURSOR, MVMT|INCL|VIZ}, +/* & repeat subst */ {v_again, CURSOR_MOVED, SDOT|NCOL|LNMD|INCL}, +/* ' move to a mark */ {m_tomark, C_C_K_MARK, MVMT|FRNT|NREL|LNMD|INCL|VIZ}, +#ifndef NO_SENTENCE +/* ( mv back sentence */ {m_sentence, CURSOR, MVMT|VIZ}, +/* ) mv fwd sentence */ {m_sentence, CURSOR, MVMT|VIZ}, +#else +/* ( not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* ) not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +#ifndef NO_ERRLIST +/* * errlist */ {v_errlist, CURSOR, FRNT|NREL}, +#else +/* * not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* + mv front next ln */ {m_updnto, CURSOR, MVMT|FRNT|LNMD|VIZ|INCL}, +#ifndef NO_CHARSEARCH +/* , reverse [fFtT] cmd*/ {m__ch, CURSOR, MVMT|INCL|VIZ}, +#else +/* , not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* - mv front prev ln */ {m_updnto, CURSOR, MVMT|FRNT|LNMD|VIZ|INCL}, +/* . special... */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* / forward search */ {m_fsrch, CURSOR_TEXT, MVMT|NREL|VIZ}, +/* 0 part of count? */ {NO_FUNC, ZERO, MVMT|PTMV|VIZ}, +/* 1 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, +/* 2 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, +/* 3 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, +/* 4 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, +/* 5 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, +/* 6 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, +/* 7 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, +/* 8 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, +/* 9 part of count */ {NO_FUNC, DIGIT, PTMV|VIZ}, +/* : run single EX cmd*/ {v_1ex, CURSOR_TEXT, NO_FLAGS}, +#ifndef NO_CHARSEARCH +/* ; repeat [fFtT] cmd*/ {m__ch, CURSOR, MVMT|INCL|VIZ}, +#else +/* ; not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS|VIZ}, +#endif +/* < shift text left */ {v_lshift, CURSOR_MOVED, SDOT|FRNT|LNMD|INCL|VIZ}, +/* = preset filter */ {v_reformat, CURSOR_MOVED, SDOT|FRNT|LNMD|INCL|VIZ}, +/* > shift text right */ {v_rshift, CURSOR_MOVED, SDOT|FRNT|LNMD|INCL|VIZ}, +/* ? backward search */ {m_bsrch, CURSOR_TEXT, MVMT|NREL|VIZ}, +#ifndef NO_AT +/* @ execute a cutbuf */ {v_at, C_C_K_CUT, NO_FLAGS}, +#else +/* @ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* A append at EOL */ {v_insert, CURSOR, SDOT}, +/* B move back Word */ {m_bword, CURSOR, MVMT|VIZ}, +/* C change to EOL */ {v_change, CURSOR_EOL, SDOT}, +/* D delete to EOL */ {v_delete, CURSOR_EOL, SDOT}, +/* E move end of Word */ {m_eword, CURSOR, MVMT|INCL|VIZ}, +#ifndef NO_CHARSEARCH +/* F move bk to char */ {m_Fch, C_C_K_CHAR, MVMT|INCL|VIZ}, +#else +/* F not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* G move to line # */ {m_updnto, CURSOR, MVMT|NREL|LNMD|FRNT|INCL|VIZ}, +/* H move to row */ {m_row, CURSOR, MVMT|LNMD|FRNT|VIZ|INCL}, +/* I insert at front */ {v_insert, CURSOR, SDOT}, +/* J join lines */ {v_join, CURSOR, SDOT}, +#ifndef NO_EXTENSIONS +/* K look up keyword */ {v_keyword, KEYWORD, NO_FLAGS}, +#else +/* K not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* L move to last row */ {m_row, CURSOR, MVMT|LNMD|FRNT|VIZ|INCL}, +/* M move to mid row */ {m_row, CURSOR, MVMT|LNMD|FRNT|VIZ|INCL}, +/* N reverse prev srch*/ {m_Nsrch, CURSOR, MVMT|NREL|VIZ}, +/* O insert above line*/ {v_insert, CURSOR, SDOT}, +/* P paste before */ {v_paste, CURSOR, SDOT}, +/* Q quit to EX mode */ {v_quit, NO_ARGS, NO_FLAGS}, +/* R overtype */ {v_overtype, CURSOR, SDOT}, +/* S change line */ {v_change, CURSOR_MOVED, SDOT}, +#ifndef NO_CHARSEARCH +/* T move bk to char */ {m_Tch, C_C_K_CHAR, MVMT|INCL|VIZ}, +#else +/* T not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* U undo whole line */ {v_undoline, CURSOR, FRNT}, +#ifndef NO_VISIBLE +/* V start visible */ {v_start, CURSOR, INCL|LNMD|VIZ}, +#else +/* V not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* W move forward Word*/ {m_fword, CURSOR, MVMT|INCL|VIZ}, +/* X delete to left */ {v_xchar, CURSOR, SDOT}, +/* Y yank text */ {v_yank, CURSOR_MOVED, NCOL}, +/* Z save file & exit */ {v_xit, CURSOR_CNT_KEY, NO_FLAGS}, +/* [ move back section*/ {m_paragraph, CURSOR, MVMT|LNMD|NREL|VIZ}, +#ifndef NO_POPUP +/* \ pop-up menu */ {v_popup, CURSOR_MOVED, VIZ}, +#else +/* \ not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* ] move fwd section */ {m_paragraph, CURSOR, MVMT|LNMD|NREL|VIZ}, +/* ^ move to front */ {m_front, CURSOR, MVMT|VIZ}, +/* _ current line */ {m_updnto, CURSOR, MVMT|LNMD|FRNT|INCL}, +/* ` move to mark */ {m_tomark, C_C_K_MARK, MVMT|NREL|VIZ}, +/* a append at cursor */ {v_insert, CURSOR, SDOT}, +/* b move back word */ {m_bword, CURSOR, MVMT|VIZ}, +/* c change text */ {v_change, CURSOR_MOVED, SDOT|VIZ}, +/* d delete op */ {v_delete, CURSOR_MOVED, SDOT|VIZ}, +/* e move end word */ {m_eword, CURSOR, MVMT|INCL|VIZ}, +#ifndef NO_CHARSEARCH +/* f move fwd for char*/ {m_fch, C_C_K_CHAR, MVMT|INCL|VIZ}, +#else +/* f not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* g not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* h move left */ {m_left, CURSOR, MVMT|VIZ}, +/* i insert at cursor */ {v_insert, CURSOR, SDOT}, +/* j move down */ {m_updnto, CURSOR, MVMT|NCOL|LNMD|VIZ|INCL}, +/* k move up */ {m_updnto, CURSOR, MVMT|NCOL|LNMD|VIZ|INCL}, +/* l move right */ {m_right, CURSOR, MVMT|INCL|VIZ}, +/* m define a mark */ {v_mark, C_C_K_MARK, NO_FLAGS}, +/* n repeat prev srch */ {m_nsrch, CURSOR, MVMT|NREL|VIZ}, +/* o insert below line*/ {v_insert, CURSOR, SDOT}, +/* p paste after */ {v_paste, CURSOR, SDOT}, +/* q not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +/* r replace chars */ {v_replace, C_C_K_REP1, SDOT}, +/* s subst N chars */ {v_subst, CURSOR, SDOT}, +#ifndef NO_CHARSEARCH +/* t move fwd to char */ {m_tch, C_C_K_CHAR, MVMT|INCL|VIZ}, +#else +/* t not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* u undo */ {v_undo, CURSOR, NO_FLAGS}, +#ifndef NO_VISIBLE +/* v start visible */ {v_start, CURSOR, INCL|VIZ}, +#else +/* v not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS}, +#endif +/* w move fwd word */ {m_fword, CURSOR, MVMT|INCL|VIZ}, +/* x delete character */ {v_xchar, CURSOR, SDOT}, +/* y yank text */ {v_yank, CURSOR_MOVED, NCOL|VIZ}, +/* z adjust scrn row */ {m_z, CURSOR_CNT_KEY, NCOL|VIZ}, +/* { back paragraph */ {m_paragraph, CURSOR, MVMT|LNMD|VIZ}, +/* | move to column */ {m_tocol, CURSOR, MVMT|NREL|VIZ}, +/* } fwd paragraph */ {m_paragraph, CURSOR, MVMT|LNMD|VIZ}, +/* ~ upper/lowercase */ {v_ulcase, CURSOR, SDOT}, +/* DEL not defined */ {NO_FUNC, NO_ARGS, NO_FLAGS} +}; + + + +void vi() +{ + REG int key; /* keystroke from user */ + long count; /* numeric argument to some functions */ + REG struct keystru *keyptr;/* pointer to vikeys[] element */ + MARK tcurs; /* temporary cursor */ + int prevkey;/* previous key, if d/c/y/</>/! */ + MARK range; /* start of range for d/c/y/</>/! */ + char text[132]; + int dotkey; /* last "key" of a change */ + int dotpkey;/* last "prevkey" of a change */ + int dotkey2;/* last extra "getkey()" of a change */ + int dotcnt; /* last "count" of a change */ + int firstkey; + REG int i; + + /* tell the redraw() function to start from scratch */ + redraw(MARK_UNSET, FALSE); + +#ifdef lint + /* lint says that "range" might be used before it is set. This + * can't really happen due to the way "range" and "prevkey" are used, + * but lint doesn't know that. This line is here ONLY to keep lint + * happy. + */ + range = 0L; +#endif + + /* safeguard against '.' with no previous command */ + dotkey = dotpkey = dotkey2 = dotcnt = 0; + + /* go immediately into insert mode, if ":set inputmode" */ + firstkey = 0; +#ifndef NO_EXTENSIONS + if (*o_inputmode) + { + firstkey = 'i'; + } +#endif + + /* Repeatedly handle VI commands */ + for (count = 0, prevkey = '\0'; mode == MODE_VI; ) + { + /* if we've moved off the undoable line, then we can't undo it at all */ + if (markline(cursor) != U_line) + { + U_line = 0L; + } + + /* report any changes from the previous command */ + if (rptlines >= *o_report) + { + redraw(cursor, FALSE); + msg("%ld line%s %s", rptlines, (rptlines==1?"":"s"), rptlabel); + } + rptlines = 0L; + + /* get the next command key. It must be ASCII */ + if (firstkey) + { + key = firstkey; + firstkey = 0; + } + else + { + do + { + key = getkey(WHEN_VICMD); + } while (key < 0 || key > 127); + } + + /* Convert a doubled-up operator such as "dd" into "d_" */ + if (prevkey && key == prevkey) + { + key = '_'; + } + + /* look up the structure describing this command */ + keyptr = &vikeys[key]; + + /* '&' and uppercase operators always act like doubled */ + if (!prevkey && keyptr->args == CURSOR_MOVED + && (key == '&' || isupper(key))) + { + range = cursor; + prevkey = key; + key = '_'; + keyptr = &vikeys[key]; + } + +#ifndef NO_VISIBLE + /* if we're in the middle of a v/V command, reject commands + * that aren't operators or movement commands + */ + if (V_from && !(keyptr->flags & VIZ)) + { + beep(); + prevkey = 0; + count = 0; + continue; + } +#endif + + /* if we're in the middle of a d/c/y/</>/! command, reject + * anything but movement. + */ + if (prevkey && !(keyptr->flags & (MVMT|PTMV))) + { + beep(); + prevkey = 0; + count = 0; + continue; + } + + /* set the "dot" variables, if we're supposed to */ + if (((keyptr->flags & SDOT) + || (prevkey && vikeys[prevkey].flags & SDOT)) +#ifndef NO_VISIBLE + && !V_from +#endif + ) + { + dotkey = key; + dotpkey = prevkey; + dotkey2 = '\0'; + dotcnt = count; + + /* remember the line before any changes are made */ + if (U_line != markline(cursor)) + { + U_line = markline(cursor); + strcpy(U_text, fetchline(U_line)); + } + } + + /* if this is "." then set other vars from the "dot" vars */ + if (key == '.') + { + key = dotkey; + keyptr = &vikeys[key]; + prevkey = dotpkey; + if (prevkey) + { + range = cursor; + } + if (count == 0) + { + count = dotcnt; + } + doingdot = TRUE; + + /* remember the line before any changes are made */ + if (U_line != markline(cursor)) + { + U_line = markline(cursor); + strcpy(U_text, fetchline(U_line)); + } + } + else + { + doingdot = FALSE; + } + + /* process the key as a command */ + tcurs = cursor; + force_flags = NO_FLAGS; + switch (keyptr->args & ARGSMASK) + { + case ZERO: + if (count == 0) + { + tcurs = cursor & ~(BLKSIZE - 1); + break; + } + /* else fall through & treat like other digits... */ + + case DIGIT: + count = count * 10 + key - '0'; + break; + + case KEYWORD: + /* if not on a keyword, fail */ + pfetch(markline(cursor)); + key = markidx(cursor); + if (!isalnum(ptext[key])) + { + tcurs = MARK_UNSET; + break; + } + + /* find the start of the keyword */ + while (key > 0 && isalnum(ptext[key - 1])) + { + key--; + } + tcurs = (cursor & ~(BLKSIZE - 1)) + key; + + /* copy it into a buffer, and NUL-terminate it */ + i = 0; + do + { + text[i++] = ptext[key++]; + } while (isalnum(ptext[key])); + text[i] = '\0'; + + /* call the function */ + tcurs = (*keyptr->func)(text, tcurs, count); + count = 0L; + break; + + case NO_ARGS: + if (keyptr->func) + { + (*keyptr->func)(); + } + else + { + beep(); + } + count = 0L; + break; + + case CURSOR: + tcurs = (*keyptr->func)(cursor, count, key, prevkey); + count = 0L; + break; + + case CURSOR_CNT_KEY: + if (doingdot) + { + tcurs = (*keyptr->func)(cursor, count, dotkey2); + } + else + { + /* get a key */ + i = getkey(KEYMODE(keyptr->args)); + if (i == '\033') /* ESC */ + { + count = 0; + tcurs = MARK_UNSET; + break; /* exit from "case CURSOR_CNT_KEY" */ + } + else if (i == ctrl('V')) + { + i = getkey(0); + } + + /* if part of an SDOT command, remember it */ + if (keyptr->flags & SDOT + || (prevkey && vikeys[prevkey].flags & SDOT)) + { + dotkey2 = i; + } + + /* do it */ + tcurs = (*keyptr->func)(cursor, count, i); + } + count = 0L; + break; + + case CURSOR_MOVED: +#ifndef NO_VISIBLE + if (V_from) + { + range = cursor; + tcurs = V_from; + count = 0L; + prevkey = key; + key = (V_linemd ? 'V' : 'v'); + keyptr = &vikeys[key]; + } + else +#endif + { + prevkey = key; + range = cursor; + force_flags = LNMD|INCL; + } + break; + + case CURSOR_EOL: + prevkey = key; + /* a zero-length line needs special treatment */ + pfetch(markline(cursor)); + if (plen == 0) + { + /* act on a zero-length section of text */ + range = tcurs = cursor; + key = '0'; + } + else + { + /* act like CURSOR_MOVED with '$' movement */ + range = cursor; + tcurs = m_rear(cursor, 1L); + key = '$'; + } + count = 0L; + keyptr = &vikeys[key]; + break; + + case CURSOR_TEXT: + do + { + text[0] = key; + if (vgets(key, text + 1, sizeof text - 1) >= 0) + { + /* reassure user that <CR> was hit */ + qaddch('\r'); + refresh(); + + /* call the function with the text */ + tcurs = (*keyptr->func)(cursor, text); + } + else + { + if (exwrote || mode == MODE_COLON) + { + redraw(MARK_UNSET, FALSE); + } + mode = MODE_VI; + } + } while (mode == MODE_COLON); + count = 0L; + break; + } + + /* if that command took us out of vi mode, then exit the loop + * NOW, without tweaking the cursor or anything. This is very + * important when mode == MODE_QUIT. + */ + if (mode != MODE_VI) + { + break; + } + + /* now move the cursor, as appropriate */ + if (keyptr->args == CURSOR_MOVED) + { + /* the < and > keys have FRNT, + * but it shouldn't be applied yet + */ + tcurs = adjmove(cursor, tcurs, 0); + } + else + { + tcurs = adjmove(cursor, tcurs, (int)keyptr->flags | force_flags); + } + + /* was that the end of a d/c/y/</>/! command? */ + if (prevkey && ((keyptr->flags & MVMT) +#ifndef NO_VISIBLE + || V_from +#endif + ) && count == 0L) + { +#ifndef NO_VISIBLE + /* turn off the hilight */ + V_from = 0L; +#endif + + /* if the movement command failed, cancel operation */ + if (tcurs == MARK_UNSET) + { + prevkey = 0; + count = 0; + continue; + } + + /* make sure range=front and tcurs=rear. Either way, + * leave cursor=range since that's where we started. + */ + cursor = range; + if (tcurs < range) + { + range = tcurs; + tcurs = cursor; + } + + /* The 'w' and 'W' destinations should never take us + * to the front of a line. Instead, they should take + * us only to the end of the preceding line. + */ + if ((keyptr->flags & (MVMT|NREL|LNMD|FRNT|INCL)) == MVMT + && markline(range) < markline(tcurs) + && (markline(tcurs) > nlines || tcurs == m_front(tcurs, 0L))) + { + tcurs = (tcurs & ~(BLKSIZE - 1)) - BLKSIZE; + pfetch(markline(tcurs)); + tcurs += plen; + } + + /* adjust for line mode & inclusion of last char/line */ + i = (keyptr->flags | vikeys[prevkey].flags); + switch ((i | force_flags) & (INCL|LNMD)) + { + case INCL: + tcurs++; + break; + + case INCL|LNMD: + tcurs += BLKSIZE; + /* fall through... */ + + case LNMD: + range &= ~(BLKSIZE - 1); + tcurs &= ~(BLKSIZE - 1); + break; + } + + /* run the function */ + tcurs = (*vikeys[prevkey].func)(range, tcurs); + if (mode == MODE_VI) + { + (void)adjmove(cursor, cursor, 0); + cursor = adjmove(cursor, tcurs, (int)vikeys[prevkey].flags); + } + + /* cleanup */ + prevkey = 0; + } + else if (!prevkey) + { + if (tcurs != MARK_UNSET) + cursor = tcurs; + } + } +} + +/* This function adjusts the MARK value that they return; here we make sure + * it isn't past the end of the line, and that the column hasn't been + * *accidentally* changed. + */ +MARK adjmove(old, new, flags) + MARK old; /* the cursor position before the command */ + REG MARK new; /* the cursor position after the command */ + int flags; /* various flags regarding cursor mvmt */ +{ + static int colno; /* the column number that we want */ + REG char *text; /* used to scan through the line's text */ + REG int i; + +#ifdef DEBUG + watch(); +#endif + + /* if the command failed, bag it! */ + if (new == MARK_UNSET) + { + beep(); + return old; + } + + /* if this is a non-relative movement, set the '' mark */ + if (flags & NREL) + { + mark[26] = old; + } + + /* make sure it isn't past the end of the file */ + if (markline(new) < 1) + { + new = MARK_FIRST; + } + else if (markline(new) > nlines) + { + new = MARK_LAST; + } + + /* fetch the new line */ + pfetch(markline(new)); + + /* move to the front, if we're supposed to */ + if (flags & FRNT) + { + new = m_front(new, 1L); + } + + /* change the column#, or change the mark to suit the column# */ + if (!(flags & NCOL)) + { + /* change the column# */ + i = markidx(new); + if (i == BLKSIZE - 1) + { + new &= ~(BLKSIZE - 1); + if (plen > 0) + { + new += plen - 1; + } + colno = BLKSIZE * 8; /* one heck of a big colno */ + } + else if (plen > 0) + { + if (i >= plen) + { + new = (new & ~(BLKSIZE - 1)) + plen - 1; + } + colno = idx2col(new, ptext, FALSE); + } + else + { + new &= ~(BLKSIZE - 1); + colno = 0; + } + } + else + { + /* adjust the mark to get as close as possible to column# */ + for (i = 0, text = ptext; i <= colno && *text; text++) + { + if (*text == '\t' && !*o_list) + { + i += *o_tabstop - (i % *o_tabstop); + } + else if (UCHAR(*text) < ' ' || *text == 127) + { + i += 2; + } +#ifndef NO_CHARATTR + else if (*o_charattr && text[0] == '\\' && text[1] == 'f' && text[2]) + { + text += 2; /* plus one more in "for()" stmt */ + } +#endif + else + { + i++; + } + } + if (text > ptext) + { + text--; + } + new = (new & ~(BLKSIZE - 1)) + (int)(text - ptext); + } + + return new; +} + + +#ifdef DEBUG +watch() +{ + static wasset; + + if (*origname) + { + wasset = TRUE; + } + else if (wasset) + { + mode = MODE_EX; + msg("origname was clobbered"); + endwin(); + abort(); + } + + if (wasset && nlines == 0) + { + mode = MODE_EX; + msg("nlines=0"); + endwin(); + abort(); + } +} +#endif diff --git a/commands/elvis/vi.h b/commands/elvis/vi.h new file mode 100755 index 000000000..4c42dd2fc --- /dev/null +++ b/commands/elvis/vi.h @@ -0,0 +1,596 @@ +/* vi.h */ + +/* Author: + * Steve Kirkendall + * 14407 SW Teal Blvd. #C + * Beaverton, OR 97005 + * kirkenda@cs.pdx.edu + */ + +#define VERSION "ELVIS 1.5, by Steve Kirkendall (23 March 1992)" +#define COPYING "This version of ELVIS is freely redistributable." + +#include <errno.h> +extern int errno; +#if TOS && !defined(__GNUC__) +#define ENOENT (-AEFILNF) +#endif + +#if TOS || VMS +# include <types.h> +# define O_RDONLY 0 +# define O_WRONLY 1 +# define O_RDWR 2 +# ifdef __GNUC__ +# define S_IJDIR S_IFDIR +# endif +#else +# if OSK +# include <modes.h> +# define O_RDONLY S_IREAD +# define O_WRONLY S_IWRITE +# define O_RDWR (S_IREAD | S_IWRITE) +# define ENOENT E_PNNF +# define sprintf Sprintf +# else +# include <sys/types.h> +# if COHERENT +# include <sys/fcntl.h> +# else +# include <fcntl.h> +# endif +# endif +#endif + +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +#include "curses.h" + +#include <signal.h> + +/*------------------------------------------------------------------------*/ +/* Miscellaneous constants. */ + +#define INFINITY 2000000001L /* a very large integer */ +#define LONGKEY 10 /* longest possible raw :map key */ +#ifndef MAXRCLEN +# define MAXRCLEN 1000 /* longest possible :@ command */ +#endif + +/*------------------------------------------------------------------------*/ +/* These describe how temporary files are divided into blocks */ + +#define MAXBLKS (BLKSIZE / sizeof(unsigned short)) +typedef union +{ + char c[BLKSIZE]; /* for text blocks */ + unsigned short n[MAXBLKS]; /* for the header block */ +} + BLK; + +/*------------------------------------------------------------------------*/ +/* These are used manipulate BLK buffers. */ + +extern BLK hdr; /* buffer for the header block */ +extern BLK *blkget(); /* given index into hdr.c[], reads block */ +extern BLK *blkadd(); /* inserts a new block into hdr.c[] */ + +/*------------------------------------------------------------------------*/ +/* These are used to keep track of various flags */ +extern struct _viflags +{ + short file; /* file flags */ +} + viflags; + +/* file flags */ +#define NEWFILE 0x0001 /* the file was just created */ +#define READONLY 0x0002 /* the file is read-only */ +#define HADNUL 0x0004 /* the file contained NUL characters */ +#define MODIFIED 0x0008 /* the file has been modified, but not saved */ +#define NOFILE 0x0010 /* no name is known for the current text */ +#define ADDEDNL 0x0020 /* newlines were added to the file */ +#define HADBS 0x0040 /* backspace chars were lost from the file */ +#define UNDOABLE 0x0080 /* file has been modified */ +#define NOTEDITED 0x0100 /* the :file command has been used */ + +/* macros used to set/clear/test flags */ +#define setflag(x,y) viflags.x |= y +#define clrflag(x,y) viflags.x &= ~y +#define tstflag(x,y) (viflags.x & y) +#define initflags() viflags.file = 0; + +/* The options */ +extern char o_autoindent[1]; +extern char o_autoprint[1]; +extern char o_autotab[1]; +extern char o_autowrite[1]; +extern char o_columns[3]; +extern char o_directory[30]; +extern char o_edcompatible[1]; +extern char o_equalprg[80]; +extern char o_errorbells[1]; +extern char o_exrefresh[1]; +extern char o_ignorecase[1]; +extern char o_keytime[3]; +extern char o_keywordprg[80]; +extern char o_lines[3]; +extern char o_list[1]; +extern char o_number[1]; +extern char o_readonly[1]; +extern char o_remap[1]; +extern char o_report[3]; +extern char o_scroll[3]; +extern char o_shell[60]; +extern char o_shiftwidth[3]; +extern char o_sidescroll[3]; +extern char o_sync[1]; +extern char o_tabstop[3]; +extern char o_term[30]; +extern char o_flash[1]; +extern char o_warn[1]; +extern char o_wrapscan[1]; + +#ifndef CRUNCH +extern char o_beautify[1]; +extern char o_exrc[1]; +extern char o_mesg[1]; +extern char o_more[1]; +extern char o_novice[1]; +extern char o_prompt[1]; +extern char o_taglength[3]; +extern char o_terse[1]; +extern char o_window[3]; +extern char o_wrapmargin[3]; +extern char o_writeany[1]; +#endif + +#ifndef NO_ERRLIST +extern char o_cc[30]; +extern char o_make[30]; +#endif + +#ifndef NO_CHARATTR +extern char o_charattr[1]; +#endif + +#ifndef NO_DIGRAPH +extern char o_digraph[1]; +extern char o_flipcase[80]; +#endif + +#ifndef NO_SENTENCE +extern char o_hideformat[1]; +#endif + +#ifndef NO_EXTENSIONS +extern char o_inputmode[1]; +extern char o_ruler[1]; +#endif + +#ifndef NO_MAGIC +extern char o_magic[1]; +#endif + +#ifndef NO_MODELINES +extern char o_modelines[1]; +#endif + +#ifndef NO_SENTENCE +extern char o_paragraphs[30]; +extern char o_sections[30]; +#endif + +#if MSDOS +extern char o_pcbios[1]; +#endif + +#ifndef NO_SHOWMATCH +extern char o_showmatch[1]; +#endif + +#ifndef NO_SHOWMODE +extern char o_smd[1]; +#endif + +/*------------------------------------------------------------------------*/ +/* These help support the single-line multi-change "undo" -- shift-U */ + +extern char U_text[BLKSIZE]; +extern long U_line; + +/*------------------------------------------------------------------------*/ +/* These are used to refer to places in the text */ + +typedef long MARK; +#define markline(x) (long)((x) / BLKSIZE) +#define markidx(x) (int)((x) & (BLKSIZE - 1)) +#define MARK_UNSET ((MARK)0) +#define MARK_FIRST ((MARK)BLKSIZE) +#define MARK_LAST ((MARK)(nlines * BLKSIZE)) +#define MARK_AT_LINE(x) ((MARK)(x) * BLKSIZE) + +#define NMARKS 29 +extern MARK mark[NMARKS]; /* marks a-z, plus mark ' and two temps */ +extern MARK cursor; /* mark where line is */ + +/*------------------------------------------------------------------------*/ +/* These are used to keep track of the current & previous files. */ + +extern long origtime; /* modification date&time of the current file */ +extern char origname[256]; /* name of the current file */ +extern char prevorig[256]; /* name of the preceding file */ +extern long prevline; /* line number from preceding file */ + +/*------------------------------------------------------------------------*/ +/* misc housekeeping variables & functions */ + +extern int tmpfd; /* fd used to access the tmp file */ +extern int tmpnum; /* counter used to generate unique filenames */ +extern long lnum[MAXBLKS]; /* last line# of each block */ +extern long nlines; /* number of lines in the file */ +extern char args[BLKSIZE]; /* file names given on the command line */ +extern int argno; /* the current element of args[] */ +extern int nargs; /* number of filenames in args */ +extern long changes; /* counts changes, to prohibit short-cuts */ +extern int significant; /* boolean: was a *REAL* change made? */ +extern BLK tmpblk; /* a block used to accumulate changes */ +extern long topline; /* file line number of top line */ +extern int leftcol; /* column number of left col */ +#define botline (topline + LINES - 2) +#define rightcol (leftcol + COLS - (*o_number ? 9 : 1)) +extern int physcol; /* physical column number that cursor is on */ +extern int physrow; /* physical row number that cursor is on */ +extern int exwrote; /* used to detect verbose ex commands */ +extern int doingdot; /* boolean: are we doing the "." command? */ +extern int doingglobal; /* boolean: are doing a ":g" command? */ +extern long rptlines; /* number of lines affected by a command */ +extern char *rptlabel; /* description of how lines were affected */ +extern char *fetchline(); /* read a given line from tmp file */ +extern char *parseptrn(); /* isolate a regexp in a line */ +extern MARK paste(); /* paste from cut buffer to a given point */ +extern char *wildcard(); /* expand wildcards in filenames */ +extern MARK input(); /* inserts characters from keyboard */ +extern char *linespec(); /* finds the end of a /regexp/ string */ +#define ctrl(ch) ((ch)&037) +#ifndef NO_RECYCLE +extern long allocate(); /* allocate a free block of the tmp file */ +#endif +extern int trapint(); /* trap handler for SIGINT */ +extern int deathtrap(); /* trap handler for deadly signals */ +extern void blkdirty(); /* marks a block as being "dirty" */ +extern void blkflush(); /* writes a single dirty block to the disk */ +extern void blksync(); /* forces all "dirty" blocks to disk */ +extern void blkinit(); /* resets the block cache to "empty" state */ +extern void beep(); /* rings the terminal's bell */ +extern void exrefresh(); /* writes text to the screen */ +extern void msg(); /* writes a printf-style message to the screen */ +extern void endmsgs(); /* if "manymsgs" is set, then scroll up 1 line */ +extern void garbage(); /* reclaims any garbage blocks */ +extern void redraw(); /* updates the screen after a change */ +extern void resume_curses();/* puts the terminal in "cbreak" mode */ +extern void beforedo(); /* saves current revision before a new change */ +extern void afterdo(); /* marks end of a beforedo() change */ +extern void abortdo(); /* like "afterdo()" followed by "undo()" */ +extern int undo(); /* restores file to previous undo() */ +extern void dumpkey(); /* lists key mappings to the screen */ +extern void mapkey(); /* defines a new key mapping */ +extern void savekeys(); /* lists key mappings to a file */ +extern void redrawrange(); /* records clues from modify.c */ +extern void cut(); /* saves text in a cut buffer */ +extern void delete(); /* deletes text */ +extern void add(); /* adds text */ +extern void change(); /* deletes text, and then adds other text */ +extern void cutswitch(); /* updates cut buffers when we switch files */ +extern void do_abbr(); /* defines or lists abbreviations */ +extern void do_digraph(); /* defines or lists digraphs */ +extern void exstring(); /* execute a string as EX commands */ +extern void dumpopts(); +extern void setopts(); +extern void saveopts(); +extern void savedigs(); +extern void saveabbr(); +extern void savecolor(); +extern void cutname(); +extern void cutname(); +extern void initopts(); +extern void cutend(); +#ifndef CRUNCH +extern int wset; /* boolean: has the "window" size been set? */ +#endif + +/*------------------------------------------------------------------------*/ +/* macros that are used as control structures */ + +#define BeforeAfter(before, after) for((before),bavar=1;bavar;(after),bavar=0) +#define ChangeText BeforeAfter(beforedo(FALSE),afterdo()) + +extern int bavar; /* used only in BeforeAfter macros */ + +/*------------------------------------------------------------------------*/ +/* These are the movement commands. Each accepts a mark for the starting */ +/* location & number and returns a mark for the destination. */ + +extern MARK m_updnto(); /* k j G */ +extern MARK m_right(); /* h */ +extern MARK m_left(); /* l */ +extern MARK m_tocol(); /* | */ +extern MARK m_front(); /* ^ */ +extern MARK m_rear(); /* $ */ +extern MARK m_fword(); /* w */ +extern MARK m_bword(); /* b */ +extern MARK m_eword(); /* e */ +extern MARK m_paragraph(); /* { } [[ ]] */ +extern MARK m_match(); /* % */ +#ifndef NO_SENTENCE + extern MARK m_sentence(); /* ( ) */ +#endif +extern MARK m_tomark(); /* 'm */ +#ifndef NO_EXTENSIONS +extern MARK m_wsrch(); /* ^A */ +#endif +extern MARK m_nsrch(); /* n */ +extern MARK m_Nsrch(); /* N */ +extern MARK m_fsrch(); /* /regexp */ +extern MARK m_bsrch(); /* ?regexp */ +#ifndef NO_CHARSEARCH + extern MARK m__ch(); /* ; , */ + extern MARK m_fch(); /* f */ + extern MARK m_tch(); /* t */ + extern MARK m_Fch(); /* F */ + extern MARK m_Tch(); /* T */ +#endif +extern MARK m_row(); /* H L M */ +extern MARK m_z(); /* z */ +extern MARK m_scroll(); /* ^B ^F ^E ^Y ^U ^D */ + +/* Some stuff that is used by movement functions... */ + +extern MARK adjmove(); /* a helper fn, used by move fns */ + +/* This macro is used to set the default value of cnt */ +#define DEFAULT(val) if (cnt < 1) cnt = (val) + +/* These are used to minimize calls to fetchline() */ +extern int plen; /* length of the line */ +extern long pline; /* line number that len refers to */ +extern long pchgs; /* "changes" level that len refers to */ +extern char *ptext; /* text of previous line, if valid */ +extern void pfetch(); +extern char digraph(); + +/* This is used to build a MARK that corresponds to a specific point in the + * line that was most recently pfetch'ed. + */ +#define buildmark(text) (MARK)(BLKSIZE * pline + (int)((text) - ptext)) + + +/*------------------------------------------------------------------------*/ +/* These are used to handle EX commands. */ + +#define CMD_NULL 0 /* NOT A VALID COMMAND */ +#define CMD_ABBR 1 /* "define an abbreviation" */ +#define CMD_ARGS 2 /* "show me the args" */ +#define CMD_APPEND 3 /* "insert lines after this line" */ +#define CMD_AT 4 /* "execute a cut buffer's contents via EX" */ +#define CMD_BANG 5 /* "run a single shell command" */ +#define CMD_CC 6 /* "run `cc` and then do CMD_ERRLIST" */ +#define CMD_CD 7 /* "change directories" */ +#define CMD_CHANGE 8 /* "change some lines" */ +#define CMD_COLOR 9 /* "change the default colors" */ +#define CMD_COPY 10 /* "copy the selected text to a given place" */ +#define CMD_DELETE 11 /* "delete the selected text" */ +#define CMD_DIGRAPH 12 /* "add a digraph, or display them all" */ +#define CMD_EDIT 13 /* "switch to a different file" */ +#define CMD_EQUAL 14 /* "display a line number" */ +#define CMD_ERRLIST 15 /* "locate the next error in a list" */ +#define CMD_FILE 16 /* "show the file's status" */ +#define CMD_GLOBAL 17 /* "globally search & do a command" */ +#define CMD_INSERT 18 /* "insert lines before the current line" */ +#define CMD_JOIN 19 /* "join the selected line & the one after" */ +#define CMD_LIST 20 /* "print lines, making control chars visible" */ +#define CMD_MAKE 21 /* "run `make` and then do CMD_ERRLIST" */ +#define CMD_MAP 22 /* "adjust the keyboard map" */ +#define CMD_MARK 23 /* "mark this line" */ +#define CMD_MKEXRC 24 /* "make a .exrc file" */ +#define CMD_MOVE 25 /* "move the selected text to a given place" */ +#define CMD_NEXT 26 /* "switch to next file in args" */ +#define CMD_NUMBER 27 /* "print lines from the file w/ line numbers" */ +#define CMD_PRESERVE 28 /* "act as though vi crashed" */ +#define CMD_PREVIOUS 29 /* "switch to the previous file in args" */ +#define CMD_PRINT 30 /* "print the selected text" */ +#define CMD_PUT 31 /* "insert any cut lines before this line" */ +#define CMD_QUIT 32 /* "quit without writing the file" */ +#define CMD_READ 33 /* "append the given file after this line */ +#define CMD_RECOVER 34 /* "recover file after vi crashes" - USE -r FLAG */ +#define CMD_REWIND 35 /* "rewind to first file" */ +#define CMD_SET 36 /* "set a variable's value" */ +#define CMD_SHELL 37 /* "run some lines through a command" */ +#define CMD_SHIFTL 38 /* "shift lines left" */ +#define CMD_SHIFTR 39 /* "shift lines right" */ +#define CMD_SOURCE 40 /* "interpret a file's contents as ex commands" */ +#define CMD_STOP 41 /* same as CMD_SUSPEND */ +#define CMD_SUBAGAIN 42 /* "repeat the previous substitution" */ +#define CMD_SUBSTITUTE 43 /* "substitute text in this line" */ +#define CMD_SUSPEND 44 /* "suspend the vi session" */ +#define CMD_TR 45 /* "transliterate chars in the selected lines" */ +#define CMD_TAG 46 /* "go to a particular tag" */ +#define CMD_UNABBR 47 /* "remove an abbreviation definition" */ +#define CMD_UNDO 48 /* "undo the previous command" */ +#define CMD_UNMAP 49 /* "remove a key sequence map */ +#define CMD_VERSION 50 /* "describe which version this is" */ +#define CMD_VGLOBAL 51 /* "apply a cmd to lines NOT containing an RE" */ +#define CMD_VISUAL 52 /* "go into visual mode" */ +#define CMD_WQUIT 53 /* "write this file out (any case) & quit" */ +#define CMD_WRITE 54 /* "write the selected(?) text to a given file" */ +#define CMD_XIT 55 /* "write this file out (if modified) & quit" */ +#define CMD_YANK 56 /* "copy the selected text into the cut buffer" */ +#ifdef DEBUG +# define CMD_DEBUG 57 /* access to internal data structures */ +# define CMD_VALIDATE 58 /* check for internal consistency */ +#endif +typedef int CMD; + +extern void ex(); +extern void vi(); +extern void doexcmd(); + +extern void cmd_append(); +extern void cmd_args(); +#ifndef NO_AT +extern void cmd_at(); +#endif +extern void cmd_cd(); +#ifndef NO_COLOR +extern void cmd_color(); +#endif +extern void cmd_delete(); +#ifndef NO_DIGRAPH +extern void cmd_digraph(); +#endif +extern void cmd_edit(); +#ifndef NO_ERRLIST +extern void cmd_errlist(); +#endif +extern void cmd_file(); +extern void cmd_global(); +extern void cmd_join(); +extern void cmd_mark(); +#ifndef NO_ERRLIST +extern void cmd_make(); +#endif +extern void cmd_map(); +#ifndef NO_MKEXRC +extern void cmd_mkexrc(); +#endif +extern void cmd_next(); +extern void cmd_print(); +extern void cmd_put(); +extern void cmd_read(); +extern void cmd_set(); +extern void cmd_shell(); +extern void cmd_shift(); +extern void cmd_source(); +extern void cmd_substitute(); +extern void cmd_tag(); +extern void cmd_undo(); +extern void cmd_version(); +extern void cmd_write(); +extern void cmd_xit(); +extern void cmd_move(); +#ifdef DEBUG +extern void cmd_debug(); +extern void cmd_validate(); +#endif +#ifdef SIGTSTP +extern void cmd_suspend(); +#endif + +/*----------------------------------------------------------------------*/ +/* These are used to handle VI commands */ + +extern MARK v_1ex(); /* : */ +extern MARK v_mark(); /* m */ +extern MARK v_quit(); /* Q */ +extern MARK v_redraw(); /* ^L ^R */ +extern MARK v_ulcase(); /* ~ */ +extern MARK v_undo(); /* u */ +extern MARK v_xchar(); /* x X */ +extern MARK v_replace(); /* r */ +extern MARK v_overtype(); /* R */ +extern MARK v_selcut(); /* " */ +extern MARK v_paste(); /* p P */ +extern MARK v_yank(); /* y Y */ +extern MARK v_delete(); /* d D */ +extern MARK v_join(); /* J */ +extern MARK v_insert(); /* a A i I o O */ +extern MARK v_change(); /* c C */ +extern MARK v_subst(); /* s */ +extern MARK v_lshift(); /* < */ +extern MARK v_rshift(); /* > */ +extern MARK v_reformat(); /* = */ +extern MARK v_filter(); /* ! */ +extern MARK v_status(); /* ^G */ +extern MARK v_switch(); /* ^^ */ +extern MARK v_tag(); /* ^] */ +extern MARK v_xit(); /* ZZ */ +extern MARK v_undoline(); /* U */ +extern MARK v_again(); /* & */ +#ifndef NO_EXTENSIONS + extern MARK v_keyword(); /* K */ + extern MARK v_increment(); /* * */ +#endif +#ifndef NO_ERRLIST + extern MARK v_errlist(); /* * */ +#endif +#ifndef NO_AT + extern MARK v_at(); /* @ */ +#endif +#ifdef SIGTSTP + extern MARK v_suspend(); /* ^Z */ +#endif +#ifndef NO_POPUP + extern MARK v_popup(); /* \ */ +#endif + +/*----------------------------------------------------------------------*/ +/* These flags describe the quirks of the individual visual commands */ +#define NO_FLAGS 0x00 +#define MVMT 0x01 /* this is a movement command */ +#define PTMV 0x02 /* this can be *part* of a movement command */ +#define FRNT 0x04 /* after move, go to front of line */ +#define INCL 0x08 /* include last char when used with c/d/y */ +#define LNMD 0x10 /* use line mode of c/d/y */ +#define NCOL 0x20 /* this command can't change the column# */ +#define NREL 0x40 /* this is "non-relative" -- set the '' mark */ +#define SDOT 0x80 /* set the "dot" variables, for the "." cmd */ +#ifndef NO_VISIBLE +# define VIZ 0x100 /* commands which can be used with 'v' */ +#else +# define VIZ 0 +#endif + +/* This variable is zeroed before a command executes, and later ORed with the + * command's flags after the command has been executed. It is used to force + * certain flags to be TRUE for *some* invocations of a particular command. + * For example, "/regexp/+offset" forces the LNMD flag, and sometimes a "p" + * or "P" command will force FRNT. + */ +extern int force_flags; + +/*----------------------------------------------------------------------*/ +/* These describe what mode we're in */ + +#define MODE_EX 1 /* executing ex commands */ +#define MODE_VI 2 /* executing vi commands */ +#define MODE_COLON 3 /* executing an ex command from vi mode */ +#define MODE_QUIT 4 +extern int mode; + +#define WHEN_VICMD 1 /* getkey: we're reading a VI command */ +#define WHEN_VIINP 2 /* getkey: we're in VI's INPUT mode */ +#define WHEN_VIREP 4 /* getkey: we're in VI's REPLACE mode */ +#define WHEN_EX 8 /* getkey: we're in EX mode */ +#define WHEN_MSG 16 /* getkey: we're at a "more" prompt */ +#define WHEN_POPUP 32 /* getkey: we're in the pop-up menu */ +#define WHEN_REP1 64 /* getkey: we're getting a single char for 'r' */ +#define WHEN_CUT 128 /* getkey: we're getting a cut buffer name */ +#define WHEN_MARK 256 /* getkey: we're getting a mark name */ +#define WHEN_CHAR 512 /* getkey: we're getting a destination for f/F/t/T */ +#define WHEN_INMV 4096 /* in input mode, interpret the key in VICMD mode */ +#define WHEN_FREE 8192 /* free the keymap after doing it once */ +#define WHENMASK (WHEN_VICMD|WHEN_VIINP|WHEN_VIREP|WHEN_REP1|WHEN_CUT|WHEN_MARK|WHEN_CHAR) + +#ifndef NO_VISIBLE +extern MARK V_from; +extern int V_linemd; +extern MARK v_start(); +#endif + +#ifdef DEBUG +# define malloc(size) dbmalloc(size, __FILE__, __LINE__) +# define free(ptr) dbfree(ptr, __FILE__, __LINE__) +extern char *dbmalloc(); +#endif diff --git a/commands/ftp/Makefile b/commands/ftp/Makefile new file mode 100755 index 000000000..13feabd1d --- /dev/null +++ b/commands/ftp/Makefile @@ -0,0 +1,31 @@ +# Makefile for ftp +# +# 01/25/96 Initial Release Michael Temari, <temari@ix.netcom.com> +# + +CFLAGS= -O -D_MINIX -D_POSIX_SOURCE +LDFLAGS=-i +BINDIR=/usr/bin +PROG= ftp + +OBJS= ftp.o local.o file.o other.o net.o + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) + install -S 8kw $@ + +clean: + rm -f $(PROG) $(OBJS) + +install: $(BINDIR)/$(PROG) + +$(BINDIR)/$(PROG): $(PROG) + install -cs -o bin $? $@ + +ftp.o: ftp.c ftp.h local.h file.h other.h net.h +local.o: local.c ftp.h local.h +file.o: file.c ftp.h file.h net.h +other.o: other.c ftp.h other.h +net.o: net.c ftp.h file.h net.h diff --git a/commands/ftp/file.c b/commands/ftp/file.c new file mode 100755 index 000000000..ab2c6f882 --- /dev/null +++ b/commands/ftp/file.c @@ -0,0 +1,922 @@ +/* file.c + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, <temari@ix.netcom.com> + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> + +#include "ftp.h" +#include "file.h" +#include "net.h" + +_PROTOTYPE(static char *dir, (char *path, int full)); +_PROTOTYPE(static int asciisend, (int fd, int fdout)); +_PROTOTYPE(static int binarysend, (int fd, int fdout)); +_PROTOTYPE(static int asciirecv, (int fd, int fdin)); +_PROTOTYPE(static int binaryrecv, (int fd, int fdin)); +_PROTOTYPE(static int asciisize, (int fd, off_t *filesize)); +_PROTOTYPE(static off_t asciisetsize, (int fd, off_t filesize)); + +static char buffer[512 << sizeof(char *)]; +static char bufout[512 << sizeof(char *)]; +static char line2[512]; + +static char *dir(path, full) +char *path; +int full; +{ +char cmd[128]; +static char name[32]; + + tmpnam(name); + + if(full) + sprintf(cmd, "ls -l %s > %s", path, name); + else + sprintf(cmd, "ls %s > %s", path, name); + + system(cmd); + + return(name); +} + +static int asciisend(fd, fdout) +int fd; +int fdout; +{ +int s, len; +char c; +char *p; +char *op, *ope; +unsigned long total=0L; + + if(atty) { + printf("Sent "); + fflush(stdout); + } + + op = bufout; + ope = bufout + sizeof(bufout) - 3; + + while((s = read(fd, buffer, sizeof(buffer))) > 0) { + total += (long)s; + p = buffer; + while(s-- > 0) { + c = *p++; + if(c == '\r') { + *op++ = '\r'; + total++; + } + *op++ = c; + if(op >= ope) { + write(fdout, bufout, op - bufout); + op = bufout; + } + } + if(atty) { + printf("%8lu bytes\b\b\b\b\b\b\b\b\b\b\b\b\b\b", total); + fflush(stdout); + } + } + if(op > bufout) + write(fdout, bufout, op - bufout); + if(atty) { + printf("\n"); + fflush(stdout); + } + + return(s); +} + +static int binarysend(fd, fdout) +int fd; +int fdout; +{ +int s; +unsigned long total=0L; + + if(atty) { + printf("Sent "); + fflush(stdout); + } + + while((s = read(fd, buffer, sizeof(buffer))) > 0) { + write(fdout, buffer, s); + total += (long)s; + if(atty) { + printf("%8lu bytes\b\b\b\b\b\b\b\b\b\b\b\b\b\b", total); + fflush(stdout); + } + } + if(atty) { + printf("\n"); + fflush(stdout); + } + + return(s); +} + +int sendfile(fd, fdout) +int fd; +int fdout; +{ +int s; + + switch(type) { + case TYPE_A: + s = asciisend(fd, fdout); + break; + default: + s = binarysend(fd, fdout); + } + + if(s < 0) + return(-1); + else + return(0); +} + +static int asciirecv(fd, fdin) +int fd; +int fdin; +{ +int s, len; +int gotcr; +char c; +char *p; +char *op, *ope; +unsigned long total=0L; + + if(isatty && fd > 2) { + printf("Received "); + fflush(stdout); + } + gotcr = 0; + op = bufout; ope = bufout + sizeof(bufout) - 3; + while((s = read(fdin, buffer, sizeof(buffer))) > 0) { + p = buffer; + total += (long)s; + while(s-- > 0) { + c = *p++; + if(gotcr) { + gotcr = 0; + if(c != '\n') + *op++ = '\r'; + } + if(c == '\r') + gotcr = 1; + else + *op++ = c; + if(op >= ope) { + write(fd, bufout, op - bufout); + op = bufout; + } + } + if(atty && fd > 2) { + printf("%8lu bytes\b\b\b\b\b\b\b\b\b\b\b\b\b\b", total); + fflush(stdout); + } + } + if(gotcr) + *op++ = '\r'; + if(op > bufout) + write(fd, bufout, op - bufout); + if(atty && fd > 2) { + printf("\n"); + fflush(stdout); + } + return(s); +} + +static binaryrecv(fd, fdin) +int fd; +int fdin; +{ +int s; +unsigned long total=0L; + + if(atty && fd > 2) { + printf("Received "); + fflush(stdout); + } + while((s = read(fdin, buffer, sizeof(buffer))) > 0) { + write(fd, buffer, s); + total += (long)s; + if(atty && fd > 2) { + printf("%8lu bytes\b\b\b\b\b\b\b\b\b\b\b\b\b\b", total); + fflush(stdout); + } + } + if(atty && fd > 2) { + printf("\n"); + fflush(stdout); + } + return(s); +} + +int recvfile(fd, fdin) +int fd; +int fdin; +{ +int s; + + switch(type) { + case TYPE_A: + s = asciirecv(fd, fdin); + break; + default: + s = binaryrecv(fd, fdin); + } + + if(s < 0) + return(-1); + else + return(0); +} + +int DOascii() +{ +int s; + + if(DOcmdcheck()) + return(0); + + s = DOcommand("TYPE", "A"); + + type = TYPE_A; + + return(s); +} + +int DObinary() +{ +int s; + + if(DOcmdcheck()) + return(0); + + s = DOcommand("TYPE", "I"); + + type = TYPE_I; + + return(s); +} + +int DOpwd() +{ +int s; + + if(DOcmdcheck()) + return(0); + + s = DOcommand("PWD", ""); + + if(s == 500 || s == 502) + s = DOcommand("XPWD", ""); + + return(s); +} + +int DOcd() +{ +char *path; +int s; + + if(DOcmdcheck()) + return(0); + + path = cmdargv[1]; + + if(cmdargc < 2) { + readline("Path: ", line2, sizeof(line2)); + path = line2; + } + + if(!strcmp(path, "..")) + s = DOcommand("CDUP", ""); + else + s = DOcommand("CWD", path); + + if(s == 500 || s == 502) { + if(!strcmp(path, "..")) + s = DOcommand("XCUP", ""); + else + s = DOcommand("XCWD", path); + } + + return(s); +} + +int DOmkdir() +{ +char *path; +int s; + + if(DOcmdcheck()) + return(0); + + path = cmdargv[1]; + + if(cmdargc < 2) { + readline("Directory: ", line2, sizeof(line2)); + path = line2; + } + + s = DOcommand("MKD", path); + + if(s == 500 || s == 502) + s = DOcommand("XMKD", path); + + return(s); +} + +int DOrmdir() +{ +char *path; +int s; + + if(DOcmdcheck()) + return(0); + + path = cmdargv[1]; + + if(cmdargc < 2) { + readline("Directory: ", line2, sizeof(line2)); + path = line2; + } + + s = DOcommand("RMD", path); + + if(s == 500 || s == 502) + s = DOcommand("XRMD", path); + + return(s); +} + +int DOdelete() +{ +char *file; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + readline("File: ", line2, sizeof(line2)); + file = line2; + } + + return(DOcommand("DELE", file)); +} + +int DOmdtm() +{ +char *file; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + readline("File: ", line2, sizeof(line2)); + file = line2; + } + + return(DOcommand("MDTM", file)); +} + +int DOsize() +{ +char *file; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + readline("File: ", line2, sizeof(line2)); + file = line2; + } + + return(DOcommand("SIZE", file)); +} + +int DOstat() +{ +char *file; + + if(cmdargc < 2) + if(!linkopen) { + printf("You must \"OPEN\" a connection first.\n"); + return(0); + } else + return(DOcommand("STAT", "")); + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + readline("File: ", line2, sizeof(line2)); + file = line2; + } + + return(DOcommand("STAT", file)); +} + +int DOlist() +{ +char *path; +char *local; +int fd; +int s; + + if(DOcmdcheck()) + return(0); + + path = cmdargv[1]; + + if(cmdargc < 2) + path = ""; + + if(cmdargc < 3) + local = ""; + else + local = cmdargv[2]; + + if(*local == '\0') + fd = 1; + else + fd = open(local, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", local, strerror(errno)); + return(0); + } + + s = DOdata("LIST", path, RETR, fd); + + if(fd > 2) + close(fd); + + return(s); +} + +int DOnlst() +{ +char *path; +char *local; +int fd; +int s; + + if(DOcmdcheck()) + return(0); + + path = cmdargv[1]; + + if(cmdargc < 2) + path = ""; + + if(cmdargc < 3) + local = ""; + else + local = cmdargv[2]; + + if(*local == '\0') + fd = 1; + else + fd = open(local, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", local, strerror(errno)); + return(0); + } + + s = DOdata("NLST", path, RETR, fd); + + if(fd > 2) + close(fd); + + return(s); +} + +int DOretr() +{ +char *file, *localfile; +int fd; +int s; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + readline("Remote File: ", line2, sizeof(line2)); + file = line2; + } + + if(cmdargc < 3) + localfile = file; + else + localfile = cmdargv[2]; + + fd = open(localfile, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", localfile, strerror(errno)); + return(0); + } + + s = DOdata("RETR", file, RETR, fd); + + close(fd); + + return(s); +} + +int DOrretr() +{ +char *file, *localfile; +int fd; +int s; +off_t filesize; +char restart[16]; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + readline("Remote File: ", line2, sizeof(line2)); + file = line2; + } + + if(cmdargc < 3) + localfile = file; + else + localfile = cmdargv[2]; + + fd = open(localfile, O_RDWR); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", localfile, strerror(errno)); + return(0); + } + + if(type == TYPE_A) { + if(asciisize(fd, &filesize)) { + printf("Could not determine ascii file size of %s\n", localfile); + close(fd); + return(0); + } + } else + filesize = lseek(fd, 0, SEEK_END); + + sprintf(restart, "%lu", filesize); + + s = DOcommand("REST", restart); + + if(s != 350) { + close(fd); + return(s); + } + + s = DOdata("RETR", file, RETR, fd); + + close(fd); + + return(s); +} + +int DOMretr() +{ +char *files; +int fd, s; +FILE *fp; +char name[32]; + + if(DOcmdcheck()) + return(0); + + files = cmdargv[1]; + + if(cmdargc < 2) { + readline("Files: ", line2, sizeof(line2)); + files = line2; + } + + tmpnam(name); + + fd = open(name, O_WRONLY | O_CREAT | O_TRUNC, 0666); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", name, strerror(errno)); + return(0); + } + + s = DOdata("NLST", files, RETR, fd); + + close(fd); + + if(s == 226) { + fp = fopen(name, "r"); + unlink(name); + if(fp == (FILE *)NULL) { + printf("Unable to open file listing.\n"); + return(0); + } + while(fgets(line2, sizeof(line2), fp) != (char *)NULL) { + line2[strlen(line2)-1] = '\0'; + printf("Retrieving file: %s\n", line2); fflush(stdout); + fd = open(line2, O_WRONLY | O_CREAT | O_TRUNC, 0666); + if(fd < 0) + printf("Unable to open local file %s\n", line2); + else { + s = DOdata("RETR", line2, RETR, fd); + close(fd); + if(s < 0) break; + } + } + fclose(fp); + } else + unlink(name); + + return(s); +} + +int DOappe() +{ +char *file, *remotefile; +int fd; +int s; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + readline("Local File: ", line2, sizeof(line2)); + file = line2; + } + + if(cmdargc < 3) + remotefile = file; + else + remotefile = cmdargv[2]; + + fd = open(file, O_RDONLY); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", file, strerror(errno)); + return(0); + } + + s = DOdata("APPE", remotefile, STOR, fd); + + close(fd); + + return(s); +} + +int DOstor() +{ +char *file, *remotefile; +int fd; +int s; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + readline("Local File: ", line2, sizeof(line2)); + file = line2; + } + + if(cmdargc < 3) + remotefile = file; + else + remotefile = cmdargv[2]; + + fd = open(file, O_RDONLY); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", file, strerror(errno)); + return(0); + } + + s = DOdata("STOR", remotefile, STOR, fd); + + close(fd); + + return(s); +} + +int DOrstor() +{ +char *file, *remotefile; +int fd; +int s; +off_t filesize, rmtsize; +char restart[16]; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + readline("Local File: ", line2, sizeof(line2)); + file = line2; + } + + if(cmdargc < 3) + remotefile = file; + else + remotefile = cmdargv[2]; + + s = DOcommand("SIZE", remotefile); + + if(s != 215) + return(s); + + rmtsize = atol(reply+4); + + fd = open(file, O_RDONLY); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", file, strerror(errno)); + return(0); + } + + if(type == TYPE_A) + filesize = asciisetsize(fd, rmtsize); + else + filesize = lseek(fd, rmtsize, SEEK_SET); + + if(filesize != rmtsize) { + printf("Could not set file start of %s\n", file); + close(fd); + return(0); + } + + sprintf(restart, "%lu", rmtsize); + + s = DOcommand("REST", restart); + + if(s != 350) { + close(fd); + return(s); + } + + s = DOdata("STOR", remotefile, STOR, fd); + + close(fd); + + return(s); +} + +int DOstou() +{ +char *file, *remotefile; +int fd; +int s; + + if(DOcmdcheck()) + return(0); + + file = cmdargv[1]; + + if(cmdargc < 2) { + readline("Local File: ", line2, sizeof(line2)); + file = line2; + } + + if(cmdargc < 3) + remotefile = file; + else + remotefile = cmdargv[2]; + + fd = open(file, O_RDONLY); + + if(fd < 0) { + printf("Could not open local file %s. Error %s\n", file, strerror(errno)); + return(0); + } + + s = DOdata("STOU", remotefile, STOR, fd); + + close(fd); + + return(s); +} + +int DOMstor() +{ +char *files; +char *name; +int fd, s; +FILE *fp; + + if(DOcmdcheck()) + return(0); + + files = cmdargv[1]; + + if(cmdargc < 2) { + readline("Files: ", line2, sizeof(line2)); + files = line2; + } + + name = dir(files, 0); + + fp = fopen(name, "r"); + + if(fp == (FILE *)NULL) { + printf("Unable to open listing file.\n"); + return(0); + } + + while(fgets(line2, sizeof(line2), fp) != (char *)NULL) { + line2[strlen(line2)-1] = '\0'; + printf("Sending file: %s\n", line2); fflush(stdout); + fd = open(line2, O_RDONLY); + if(fd < 0) + printf("Unable to open local file %s\n", line2); + else { + s = DOdata("STOR", line2, STOR, fd); + close(fd); + if(s < 0) break; + } + } + fclose(fp); + unlink(name); + + return(s); +} + +static int asciisize(fd, filesize) +int fd; +off_t *filesize; +{ +unsigned long count; +char *p, *pp; +int cnt; + + count = 0; + + while((cnt = read(fd, buffer, sizeof(buffer))) > 0) { + p = buffer; pp = buffer + cnt; + count += cnt; + while(p < pp) + if(*p++ == '\n') + count++; + } + + if(cnt == 0) { + *filesize = count; + return(0); + } + + return(1); +} + +static off_t asciisetsize(fd, filesize) +int fd; +off_t filesize; +{ +off_t sp; +int s; + + sp = 0; + + while(sp < filesize) { + s = read(fd, buffer, 1); + if(s < 0) + return(-1); + if(s == 0) break; + sp++; + if(*buffer == '\n') + sp++; + } + + return(sp); +} diff --git a/commands/ftp/file.h b/commands/ftp/file.h new file mode 100755 index 000000000..3e79e65a6 --- /dev/null +++ b/commands/ftp/file.h @@ -0,0 +1,30 @@ +/* file.h + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, <temari@ix.netcom.com> + */ + +_PROTOTYPE(int recvfile, (int fd, int fdin)); +_PROTOTYPE(int sendfile, (int fd, int fdout)); +_PROTOTYPE(int DOascii, (void)); +_PROTOTYPE(int DObinary, (void)); +_PROTOTYPE(int DOpwd, (void)); +_PROTOTYPE(int DOcd, (void)); +_PROTOTYPE(int DOmkdir, (void)); +_PROTOTYPE(int DOrmdir, (void)); +_PROTOTYPE(int DOdelete, (void)); +_PROTOTYPE(int DOmdtm, (void)); +_PROTOTYPE(int DOsize, (void)); +_PROTOTYPE(int DOstat, (void)); +_PROTOTYPE(int DOlist, (void)); +_PROTOTYPE(int DOnlst, (void)); +_PROTOTYPE(int DOretr, (void)); +_PROTOTYPE(int DOrretr, (void)); +_PROTOTYPE(int DOMretr, (void)); +_PROTOTYPE(int DOappe, (void)); +_PROTOTYPE(int DOstor, (void)); +_PROTOTYPE(int DOrstor, (void)); +_PROTOTYPE(int DOstou, (void)); +_PROTOTYPE(int DOMstor, (void)); diff --git a/commands/ftp/ftp.c b/commands/ftp/ftp.c new file mode 100755 index 000000000..464e40c99 --- /dev/null +++ b/commands/ftp/ftp.c @@ -0,0 +1,312 @@ +/* ftp.c by Michael Temari 06/21/92 + * + * ftp An ftp client program for use with TNET. + * + * Usage: ftp [[host] [port]] + * + * Version: 0.10 06/21/92 (pre-release not yet completed) + * 0.20 07/01/92 + * 0.30 01/15/96 (Minix 1.7.1 initial release) + * 0.40 08/27/96 + * + * Author: Michael Temari, <temari@ix.netcom.com> + */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> + +#include "ftp.h" +#include "local.h" +#include "file.h" +#include "other.h" +#include "net.h" + +FILE *fpcommin; +FILE *fpcommout; + +int linkopen; +int loggedin; +int type; +int format; +int mode; +int structure; +int passive; +int atty; + +int cmdargc; +char *cmdargv[NUMARGS]; + +char reply[1024]; + +_PROTOTYPE(static int makeargs, (char *buff)); +_PROTOTYPE(int DOhelp, (void)); +_PROTOTYPE(int main, (int argc, char *argv[])); + +static int makeargs(buff) +char *buff; +{ +char *p; +int i; + + for(i = 0; i < NUMARGS; i++) + cmdargv[i] = (char *)0; + + p = buff + strlen(buff) - 1; + while(p != buff) + if(*p == '\r' || *p == '\n' || isspace(*p)) + *p-- = '\0'; + else + break; + + p = buff; + cmdargc = 0; + while(cmdargc < NUMARGS) { + while(*p && isspace(*p)) + p++; + if(*p == '\0') + break; + cmdargv[cmdargc++] = p; + while(*p && !isspace(*p)) { + if(cmdargc == 1) + *p = tolower(*p); + p++; + } + if(*p == '\0') + break; + *p = '\0'; + p++; + } +} + +int readline(prompt, buff, len) +char *prompt; +char *buff; +int len; +{ + printf(prompt); fflush(stdout); + + if(fgets(buff, len, stdin) == (char *)NULL) { + printf("\nEnd of file on input!\n"); + exit(1); + } + + *strchr(buff, '\n') = 0; + + if(!atty) { + printf("%s\n", buff); + fflush(stdout); + } + + return(0); +} + +int DOgetreply() +{ +char *p; +char buff[6]; +int s; +int firsttime; + + do { + firsttime = 1; + do { + if(fgets(reply, sizeof(reply), fpcommin) == (char *)0) + return(-1); + p = reply + strlen(reply) - 1; + while(p != reply) + if(*p == '\r' || *p == '\n' || isspace(*p)) + *p-- = '\0'; + else + break; + printf("%s\n", reply); fflush(stdout); + if(firsttime) { + firsttime = 0; + strncpy(buff, reply, 4); + buff[3] = ' '; + } + } while(strncmp(reply, buff, 3) || reply[3] == '-'); + s = atoi(buff); + } while(s < 200 && s != 125 & s != 150); + + return(s); +} + +int DOcmdcheck() +{ + if(!linkopen) { + printf("You must \"OPEN\" a connection first.\n"); + return(1); + } + + if(!loggedin) { + printf("You must login first.\n"); + return(1); + } + + return(0); +} + +int DOcommand(ftpcommand, ftparg) +char *ftpcommand; +char *ftparg; +{ + if(*ftparg) + fprintf(fpcommout, "%s %s\r\n", ftpcommand, ftparg); + else + fprintf(fpcommout, "%s\r\n", ftpcommand); + + fflush(fpcommout); + + return(DOgetreply()); +} + +int DOhelp() +{ +char junk[10]; + + printf("Command: Description\n"); + printf("! Escape to a shell\n"); + printf("append Append a file to remote host\n"); + printf("ascii Set file transfer mode to ascii\n"); + printf("binary Set file transfer mode to binary\n"); + printf("bye Close connection and exit\n"); + printf("cd Change directory on remote host\n"); + printf("close Close connection\n"); + printf("del Remove file on remote host\n"); + printf("dir Display long form remote host directory listing\n"); + printf("exit Close connection and exit\n"); + printf("get Retrieve a file from remote host\n"); + printf("help Display this text\n"); + printf("lcd Change directory on local host\n"); + printf("ldir Display long form local host directory listing\n"); + printf("lls Display local host directory listing\n"); + printf("lmkdir Create directory on local host\n"); + printf("lpwd Display current directory on local host\n"); + printf("lrmdir Remove directory on local host\n"); + printf("ls Display remote host directory listing\n"); + printf("mget Retrieve multiple files from remote host\n"); + printf("mkdir Create directory on remote host\n"); + printf("mod Get file modification time\n"); + + readline("Press ENTER to continue... ", junk, sizeof(junk)); + + printf("mput Send multiple files to remote host\n"); + printf("noop Send the ftp NOOP command\n"); + printf("open Open connection to remote host\n"); + printf("pass Enter remote user password\n"); + printf("passive Toggle passive mode\n"); + printf("put Send a file to remote host\n"); + printf("putu Send a file to remote host(unique)\n"); + printf("pwd Display current directory on remote host\n"); + printf("quit Close connection and exit\n"); + printf("quote Send raw ftp command to remote host\n"); + printf("reget Restart a partial file retrieve from remote host\n"); + printf("remotehelp Display ftp commands implemented on remote host\n"); + printf("reput Restart a partial file send to remote host\n"); + printf("rm Remove file on remote host\n"); + printf("rmdir Remove directory on remote host\n"); + printf("site Send a site specific command\n"); + printf("size Get file size information\n"); + printf("status Get connection/file status information\n"); + printf("system Get remote system type information\n"); + printf("user Enter remote user information\n"); + + return(0); +} + +struct commands { + char *name; + _PROTOTYPE(int (*func), (void)); +}; + +static struct commands commands[] = { + "!", DOlshell, + "append", DOappe, + "ascii", DOascii, + "binary", DObinary, + "bin", DObinary, + "bye", DOquit, + "cd", DOcd, + "close", DOclose, + "del", DOdelete, + "dir", DOlist, + "exit", DOquit, + "get", DOretr, + "help", DOhelp, + "lcd", DOlcd, + "ldir", DOllist, + "lls", DOlnlst, + "lmkdir", DOlmkdir, + "lpwd", DOlpwd, + "lrmdir", DOlrmdir, + "ls", DOnlst, + "mget", DOMretr, + "mkdir", DOmkdir, + "mod", DOmdtm, + "mput", DOMstor, + "noop", DOnoop, + "open", DOopen, + "pass", DOpass, + "passive", DOpassive, + "put", DOstor, + "putu", DOstou, + "pwd", DOpwd, + "quit", DOquit, + "quote", DOquote, + "reget", DOrretr, + "remotehelp", DOremotehelp, + "reput", DOrstor, + "rm", DOdelete, + "rmdir", DOrmdir, + "site", DOsite, + "size", DOsize, + "status", DOstat, + "system", DOsyst, + "user", DOuser, + "", (int (*)())0 +}; + +int main(argc, argv) +int argc; +char *argv[]; +{ +int s; +struct commands *cmd; +static char buffer[128]; + + NETinit(); + + FTPinit(); + + s = 0; + + if(argc > 1) { + sprintf(buffer, "open %s ", argv[1]); + makeargs(buffer); + s = DOopen(); + if(atty && s > 0) { + sprintf(buffer, "user"); + makeargs(buffer); + s = DOuser(); + } + } + + while(s >= 0) { + readline("ftp>", buffer, sizeof(buffer)); + makeargs(buffer); + if(cmdargc == 0) continue; + for(cmd = commands; *cmd->name != '\0'; cmd++) + if(!strcmp(cmdargv[0], cmd->name)) + break; + if(*cmd->name != '\0') + s = (*cmd->func)(); + else { + s = 0; + printf("Command \"%s\" not recognized.\n", cmdargv[0]); + } + } + + return(0); +} diff --git a/commands/ftp/ftp.h b/commands/ftp/ftp.h new file mode 100755 index 000000000..b0c234511 --- /dev/null +++ b/commands/ftp/ftp.h @@ -0,0 +1,36 @@ +/* ftp.h + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, <temari@ix.netcom.com> + */ + +extern FILE *fpcommin; +extern FILE *fpcommout; + +extern int linkopen; +extern int loggedin; +extern int type; +extern int format; +extern int mode; +extern int structure; +extern int passive; +extern int atty; + +#define NUMARGS 10 +extern int cmdargc; +extern char *cmdargv[NUMARGS]; + +extern char reply[1024]; + +#define RETR 0 +#define STOR 1 + +#define TYPE_A 0 +#define TYPE_I 1 + +_PROTOTYPE(int readline, (char *prompt, char *buff, int len)); +_PROTOTYPE(int DOgetreply, (void)); +_PROTOTYPE(int DOcmdcheck, (void)); +_PROTOTYPE(int DOcommand, (char *ftpcommand, char *ftparg)); diff --git a/commands/ftp/local.c b/commands/ftp/local.c new file mode 100755 index 000000000..7f2143d9e --- /dev/null +++ b/commands/ftp/local.c @@ -0,0 +1,128 @@ +/* local.c + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, <temari@ix.netcom.com> + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <errno.h> + +#include "ftp.h" +#include "local.h" + +static char line2[512]; + +_PROTOTYPE(static void dodir, (char *path, int full)); + +int DOlpwd() +{ + if(getcwd(line2, sizeof(line2)) == (char *)NULL) + printf("Could not determine local directory. %s\n", strerror(errno)); + else + printf("Current local directory: %s\n", line2); + + return(0); +} + +int DOlcd() +{ +char *path; +int s; + + path = cmdargv[1]; + + if(cmdargc < 2) { + readline("Path: ", line2, sizeof(line2)); + path = line2; + } + + if(chdir(path)) + printf("Could not change local directory. %s\n", strerror(errno)); + else + DOlpwd(); + + return(0); +} + +int DOlmkdir() +{ +char *path; +int s; + + path = cmdargv[1]; + + if(cmdargc < 2) { + readline("Directory: ", line2, sizeof(line2)); + path = line2; + } + + if(mkdir(path, 0777)) + printf("Could not make directory %s. %s\n", path, strerror(errno)); + else + printf("Directory created.\n"); + + return(0); +} + +int DOlrmdir() +{ +char *path; +int s; + + path = cmdargv[1]; + + if(cmdargc < 2) { + readline("Directory: ", line2, sizeof(line2)); + path = line2; + } + + if(rmdir(path)) + printf("Could not remove directory %s. %s\n", path, strerror(errno)); + else + printf("Directory removed.\n"); + + return(0); +} + +int DOllist(void) +{ + dodir(".", 1); +} + +int DOlnlst(void) +{ + dodir(".", 0); +} + +int DOlshell(void) +{ + system("$SHELL"); +} + +static void dodir(path, full) +char *path; +int full; +{ +char cmd[128]; +static char name[32]; + + tmpnam(name); + + if(full) + sprintf(cmd, "ls -l %s > %s", path, name); + else + sprintf(cmd, "ls %s > %s", path, name); + + system(cmd); + sprintf(cmd, "more %s", name); + system(cmd); + sprintf(cmd, "rm %s", name); + system(cmd); +} diff --git a/commands/ftp/local.h b/commands/ftp/local.h new file mode 100755 index 000000000..ceb70693f --- /dev/null +++ b/commands/ftp/local.h @@ -0,0 +1,15 @@ +/* local.h + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, <temari@ix.netcom.com> + */ + +_PROTOTYPE(int DOlpwd, (void)); +_PROTOTYPE(int DOlcd, (void)); +_PROTOTYPE(int DOlmkdir, (void)); +_PROTOTYPE(int DOlrmdir, (void)); +_PROTOTYPE(int DOllist, (void)); +_PROTOTYPE(int DOlnlst, (void)); +_PROTOTYPE(int DOlshell, (void)); diff --git a/commands/ftp/net.c b/commands/ftp/net.c new file mode 100755 index 000000000..c01b2d114 --- /dev/null +++ b/commands/ftp/net.c @@ -0,0 +1,421 @@ +/* net.c + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, <temari@ix.netcom.com> + */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <signal.h> +#include <errno.h> +#include <net/netlib.h> +#include <net/hton.h> +#include <net/gen/netdb.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> + +#include "ftp.h" +#include "file.h" +#include "net.h" + +_PROTOTYPE(void donothing, (int sig)); + +static int ftpcomm_fd; +static ipaddr_t myip; +static ipaddr_t hostip; +static char host[256]; +static int lpid; + +void NETinit() +{ +int s; +char *tcp_device; +int tcp_fd; +nwio_tcpconf_t nwio_tcpconf; + + /* All this just to get our ip address */ + + if((tcp_device = getenv("TCP_DEVICE")) == (char *)NULL) + tcp_device = TCP_DEVICE; + + tcp_fd = open(tcp_device, O_RDWR); + if(tcp_fd < 0) { + perror("ftp: Could not open tcp_device"); + exit(-1); + } + s = ioctl(tcp_fd, NWIOGTCPCONF, &nwio_tcpconf); + if(s < 0) { + perror("ftp: Could not get tcp configuration"); + exit(-1); + } + + myip = nwio_tcpconf.nwtc_locaddr; + + close(tcp_fd); +} + +int DOopen() +{ +nwio_tcpconf_t tcpconf; +nwio_tcpcl_t tcpcopt; +char *tcp_device; +tcpport_t port; +int s; +struct hostent *hp; +struct servent *servent; + + if(linkopen) { + printf("Use \"CLOSE\" to close the connection first.\n"); + return(0); + } + + if(cmdargc < 2) + readline("Host: ", host, sizeof(host)); + else + strncpy(host, cmdargv[1], sizeof(host)); + + if((servent = getservbyname("ftp", "tcp")) == (struct servent *)NULL) { + fprintf(stderr, "ftp: Could not find ftp tcp service\n"); + return(-1); + } + port = (tcpport_t)servent->s_port; + + hp = gethostbyname(host); + if (hp == (struct hostent *)NULL) { + hostip = (ipaddr_t)0; + printf("Unresolved host %s\n", host); + return(0); + } else + memcpy((char *) &hostip, (char *) hp->h_addr, hp->h_length); + + /* This HACK allows the server to establish data connections correctly */ + /* when using the loopback device to talk to ourselves */ + if(hostip == inet_addr("127.0.0.1")) + hostip = myip; + + if((tcp_device = getenv("TCP_DEVICE")) == NULL) + tcp_device = "/dev/tcp"; + + if((ftpcomm_fd = open(tcp_device, O_RDWR)) < 0) { + perror("ftp: open error on tcp device"); + return(-1); + } + + tcpconf.nwtc_flags = NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; + tcpconf.nwtc_remaddr = hostip; + tcpconf.nwtc_remport = port; + + s = ioctl(ftpcomm_fd, NWIOSTCPCONF, &tcpconf); + if(s < 0) { + perror("ftp: ioctl error on NWIOSTCPCONF"); + close(ftpcomm_fd); + return(s); + } + + tcpcopt.nwtcl_flags = 0; + + s = ioctl(ftpcomm_fd, NWIOTCPCONN, &tcpcopt); + if(s < 0) { + perror("ftp: ioctl error on NWIOTCPCONN"); + close(ftpcomm_fd); + return(s); + } + + s = ioctl(ftpcomm_fd, NWIOGTCPCONF, &tcpconf); + if(s < 0) { + perror("ftp: ioctl error on NWIOGTCPCONF"); + close(ftpcomm_fd); + return(s); + } + + fpcommin = fdopen(ftpcomm_fd, "r"); + fpcommout = fdopen(ftpcomm_fd, "w"); + + s = DOgetreply(); + + if(s < 0) { + fclose(fpcommin); + fclose(fpcommout); + close(ftpcomm_fd); + return(s); + } + + if(s != 220) { + fclose(fpcommin); + fclose(fpcommout); + close(ftpcomm_fd); + return(0); + } + + linkopen = 1; + + return(s); +} + +int DOclose() +{ + if(!linkopen) { + printf("You can't close a connection that isn't open.\n"); + return(0); + } + + fclose(fpcommin); + fclose(fpcommout); + close(ftpcomm_fd); + + linkopen = 0; + loggedin = 0; + + return(0); +} + +int DOquit() +{ +int s; + + if(linkopen) { + s = DOcommand("QUIT", ""); + s = DOclose(); + } + + printf("FTP done.\n"); + + exit(0); +} + +void donothing(sig) +int sig; +{ +} + +int DOdata(datacom, file, direction, fd) +char *datacom; +char *file; +int direction; /* RETR or STOR */ +int fd; +{ +nwio_tcpconf_t tcpconf; +nwio_tcpcl_t tcplopt, tcpcopt; +char *tcp_device; +int ftpdata_fd; +char *buff; +ipaddr_t ripaddr; +tcpport_t rport; +static tcpport_t lport = HTONS(0xF000); +int s; +int i; +int cs; +int pfd[2]; +char dummy; +char port[32]; + + ripaddr = hostip; + rport = HTONS(20); + + /* here we set up a connection to listen on if not passive mode */ + /* otherwise we use this to connect for passive mode */ + + if((tcp_device = getenv("TCP_DEVICE")) == NULL) + tcp_device = "/dev/tcp"; + + if((ftpdata_fd = open(tcp_device, O_RDWR)) < 0) { + perror("ftp: open error on tcp device"); + return(-1); + } + + if(passive) { + s = DOcommand("PASV", ""); + if(s != 227) { + close(ftpdata_fd); + return(s); + } + /* decode host and port */ + buff = reply; + while(*buff && (*buff != '(')) buff++; + buff++; + ripaddr = (ipaddr_t)0; + for(i = 0; i < 4; i++) { + ripaddr = (ripaddr << 8) + (ipaddr_t)atoi(buff); + if((buff = strchr(buff, ',')) == (char *)0) { + printf("Could not parse PASV reply\n"); + return(-1); + } + buff++; + } + rport = (tcpport_t)atoi(buff); + if((buff = strchr(buff, ',')) == (char *)0) { + printf("Could not parse PASV reply\n"); + return(-1); + } + buff++; + rport = (rport << 8) + (tcpport_t)atoi(buff); + ripaddr = ntohl(ripaddr); + rport = ntohs(rport); + } + + for (;;) { + tcpconf.nwtc_flags = NWTC_SET_RA | NWTC_SET_RP; + if (passive || ntohs(lport) >= 0xF000) { + tcpconf.nwtc_flags |= NWTC_LP_SEL; + } else { + /* For no good reason Sun hosts don't like it if they have to + * connect to the same port twice in a short time... + */ + lport = htons(ntohs(lport) + 1); + tcpconf.nwtc_flags |= NWTC_LP_SET; + tcpconf.nwtc_locport = lport; + } + + tcpconf.nwtc_remaddr = ripaddr; + tcpconf.nwtc_remport = rport; + + s = ioctl(ftpdata_fd, NWIOSTCPCONF, &tcpconf); + if(s < 0) { + if (errno == EADDRINUSE) continue; + perror("ftp: ioctl error on NWIOSTCPCONF"); + close(ftpdata_fd); + return(s); + } + break; + } + + s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf); + if(s < 0) { + perror("ftp: ioctl error on NWIOGTCPCONF"); + close(ftpdata_fd); + return(s); + } + lport = tcpconf.nwtc_locport; + + if(passive) { + tcplopt.nwtcl_flags = 0; + s = ioctl(ftpdata_fd, NWIOTCPCONN, &tcpcopt); + if(s < 0) { + perror("ftp: error on ioctl NWIOTCPCONN"); + close(ftpdata_fd); + return(0); + } + s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf); + if(s < 0) { + perror("ftp: error on ioctl NWIOGTCPCONF"); + close(ftpdata_fd); + return(0); + } + } else { + tcplopt.nwtcl_flags = 0; + + if (pipe(pfd) < 0) { + perror("ftp: could not create a pipe"); + return(s); + } + lpid = fork(); + if(lpid < 0) { + perror("ftp: could not fork listener"); + close(ftpdata_fd); + close(pfd[0]); + close(pfd[1]); + return(s); + } else if(lpid == 0) { + close(pfd[0]); + signal(SIGALRM, donothing); + alarm(15); + close(pfd[1]); + s = ioctl(ftpdata_fd, NWIOTCPLISTEN, &tcplopt); + alarm(0); + if(s < 0) + if(errno == EINTR) + exit(1); /* timed out */ + else + exit(-1); /* error */ + else + exit(0); /* connection made */ + } + /* Wait for the pipe to close, then the listener is ready (almost). */ + close(pfd[1]); + (void) read(pfd[0], &dummy, 1); + close(pfd[0]); + while(1) { + signal(SIGALRM, donothing); + alarm(1); + s = ioctl(ftpdata_fd, NWIOGTCPCONF, &tcpconf); + alarm(0); + if(s == -1) break; + } + } + +#define hiword(x) ((u16_t)((x) >> 16)) +#define loword(x) ((u16_t)(x & 0xffff)) +#define hibyte(x) (((x) >> 8) & 0xff) +#define lobyte(x) ((x) & 0xff) + + if(!passive) { + sprintf(port, "%u,%u,%u,%u,%u,%u", + hibyte(hiword(ntohl(myip))), lobyte(hiword(ntohl(myip))), + hibyte(loword(ntohl(myip))), lobyte(loword(ntohl(myip))), + hibyte(ntohs(lport)), lobyte(ntohs(lport))); + s = DOcommand("PORT", port); + if(s != 200) { + close(ftpdata_fd); + kill(lpid, SIGKILL); + return(s); + } + } + + s = DOcommand(datacom, file); + if(s == 125 || s == 150) { + if(!passive) { + while(1) { + s = wait(&cs); + if(s < 0 || s == lpid) + break; + } + if(s < 0) { + perror("wait error:"); + close(ftpdata_fd); + kill(lpid, SIGKILL); + return(s); + } + if((cs & 0x00ff)) { + printf("Child listener failed %04x\n", cs); + close(ftpdata_fd); + return(-1); + } + cs = (cs >> 8) & 0x00ff; + if(cs == 1) { + printf("Child listener timed out\n"); + return(DOgetreply()); + } else if(cs) { + printf("Child listener returned %02x\n", cs); + close(ftpdata_fd); + return(-1); + } + } + switch(direction) { + case RETR: + s = recvfile(fd, ftpdata_fd); + break; + case STOR: + s = sendfile(fd, ftpdata_fd); + break; + } + close(ftpdata_fd); + s = DOgetreply(); + } else { + if(!passive) + kill(lpid, SIGKILL); + close(ftpdata_fd); + } + + return(s); +} diff --git a/commands/ftp/net.h b/commands/ftp/net.h new file mode 100755 index 000000000..fa196be3a --- /dev/null +++ b/commands/ftp/net.h @@ -0,0 +1,13 @@ +/* net.h + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, <temari@ix.netcom.com> + */ + +_PROTOTYPE(void NETinit, (void)); +_PROTOTYPE(int DOopen, (void)); +_PROTOTYPE(int DOclose, (void)); +_PROTOTYPE(int DOquit, (void)); +_PROTOTYPE(int DOdata, (char *datacom, char *file, int direction, int fd)); diff --git a/commands/ftp/other.c b/commands/ftp/other.c new file mode 100755 index 000000000..249797c00 --- /dev/null +++ b/commands/ftp/other.c @@ -0,0 +1,163 @@ +/* other.c by Michael Temari 06/21/92 + * + * ftp An ftp client program for use with TNET. + * + * Author: Michael Temari, <temari@ix.netcom.com> + */ + +#include <sys/types.h> +#include <stdio.h> +#include <ctype.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> + +#include "ftp.h" +#include "other.h" + +void FTPinit() +{ + linkopen = 0; + loggedin = 0; + type = TYPE_A; + format = 0; + mode = 0; + structure = 0; + passive = 0; + atty = isatty(0); +} + +int DOpass() +{ +int s; +struct termios oldtty, newtty; +char *pass; +char password[64]; + + if(!linkopen) { + printf("You must \"OPEN\" a connection first.\n"); + return(0); + } + + pass = cmdargv[1]; + + if(cmdargc < 2) { + tcgetattr(fileno(stdout), &oldtty); + newtty = oldtty; + newtty.c_lflag &= ~ECHO; + tcsetattr(fileno(stdout), TCSANOW, &newtty); + readline("Password: ", password, sizeof(password)); + tcsetattr(fileno(stdout), TCSANOW, &oldtty); + printf("\n"); + pass = password; + } + + s = DOcommand("PASS", pass); + + if(s == 230) + loggedin = 1; + + return(s); +} + +int DOuser() +{ +char *user; +int s; +char username[64]; + + if(!linkopen) { + printf("You must \"OPEN\" a connection first.\n"); + return(0); + } + + loggedin = 0; + + user = cmdargv[1]; + + if(cmdargc < 2) { + readline("Username: ", username, sizeof(username)); + user = username; + } + + s = DOcommand("USER", user); + + if(atty && s == 331) { + cmdargv[0] = "password"; + cmdargc = 1; + return(DOpass()); + } + + if(s == 230) + loggedin = 1; + + return(s); +} + +int DOnoop() +{ + if(DOcmdcheck()) + return(0); + + return(DOcommand("NOOP", "")); +} + +int DOpassive() +{ + passive = 1 - passive; + + printf("Passive mode is now %s\n", (passive ? "ON" : "OFF")); + + return(0); +} + +int DOsyst() +{ + if(DOcmdcheck()) + return(0); + + return(DOcommand("SYST", "")); +} + +int DOremotehelp() +{ + if(!linkopen) { + printf("You must \"OPEN\" a connection first.\n"); + return(0); + } + + return(DOcommand("HELP", "")); +} + +int DOquote() +{ +int i; +static char args[512]; + + args[0] = '\0'; + + for(i = 2; i < cmdargc; i++) { + if(i != 2) + strcat(args, " "); + strcat(args, cmdargv[i]); + } + + return(DOcommand(cmdargv[1], args)); +} + +int DOsite() +{ +int i; +static char args[512]; + + args[0] = '\0'; + + for(i = 1; i < cmdargc; i++) { + if(i != 1) + strcat(args, " "); + strcat(args, cmdargv[i]); + } + + return(DOcommand("SITE", args)); +} diff --git a/commands/ftp/other.h b/commands/ftp/other.h new file mode 100755 index 000000000..27e760e7a --- /dev/null +++ b/commands/ftp/other.h @@ -0,0 +1,17 @@ +/* other.h + * + * This file is part of ftp. + * + * + * 01/25/96 Initial Release Michael Temari, <temari@ix.netcom.com> + */ + +_PROTOTYPE(void FTPinit, (void)); +_PROTOTYPE(int DOpass, (void)); +_PROTOTYPE(int DOuser, (void)); +_PROTOTYPE(int DOnoop, (void)); +_PROTOTYPE(int DOpassive, (void)); +_PROTOTYPE(int DOsyst, (void)); +_PROTOTYPE(int DOremotehelp, (void)); +_PROTOTYPE(int DOquote, (void)); +_PROTOTYPE(int DOsite, (void)); diff --git a/commands/i386/Makefile b/commands/i386/Makefile new file mode 100755 index 000000000..21c81d8ec --- /dev/null +++ b/commands/i386/Makefile @@ -0,0 +1,26 @@ +# Makefile for commands/i386. + +CFLAGS = -D_MINIX -D_POSIX_SOURCE +CCLD = $(CC) -i $(CFLAGS) +MAKE = exec make -$(MAKEFLAGS) + +all:: acd + +acd: acd.c + $(CCLD) -o $@ -DARCH=\"`arch`\" -DDESCR=\"/usr/lib/descr\" $? + install -S 25kw $@ + +install:: /usr/bin/acd /usr/bin/cc /usr/bin/m2 /usr/bin/pc + +/usr/bin/acd: acd + install -cs -o bin $? $@ + +/usr/bin/cc /usr/bin/m2 /usr/bin/pc: /usr/bin/acd + install -l $? $@ + +clean:: + rm -rf a.out core acd + +all install clean:: + cd asmconv && $(MAKE) $@ + cd mtools-3.9.7 && $(MAKE) $@ diff --git a/commands/i386/acd.c b/commands/i386/acd.c new file mode 100755 index 000000000..1bdba9a54 --- /dev/null +++ b/commands/i386/acd.c @@ -0,0 +1,2701 @@ +/* acd 1.10 - A compiler driver Author: Kees J. Bot + * 7 Jan 1993 + * Needs about 25kw heap + stack. + */ +char version[] = "1.9"; + +#define nil 0 +#define _POSIX_SOURCE 1 +#include <sys/types.h> +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <signal.h> +#include <errno.h> +#include <ctype.h> +#include <assert.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#ifndef LIB +#define LIB "/usr/lib" /* Default library directory. */ +#endif + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +char *program; /* Call name. */ + +int verbose= 0; /* -v0: Silent. + * -v1: Show abbreviated pass names. + * -v2: Show executed UNIX commands. + * -v3: Show executed ACD commands. + * -v4: Show descr file as it is read. + */ + +int action= 2; /* 0: An error occured, don't do anything anymore. + * 1: (-vn) Do not execute, play-act. + * 2: Execute UNIX commands. + */ + +void report(char *label) +{ + if (label == nil || label[0] == 0) { + fprintf(stderr, "%s: %s\n", program, strerror(errno)); + } else { + fprintf(stderr, "%s: %s: %s\n", + program, label, strerror(errno)); + } + action= 0; +} + +void quit(int exit_code); + +void fatal(char *label) +{ + report(label); + quit(-1); +} + +size_t heap_chunks= 0; + +void *allocate(void *mem, size_t size) +/* Safe malloc/realloc. (I have heard that one can call realloc with a + * null first argument with the effect below, but that is of course to + * ridiculous to believe.) + */ +{ + assert(size > 0); + + if (mem != nil) { + mem= realloc(mem, size); + } else { + mem= malloc(size); + heap_chunks++; + } + if (mem == nil) fatal(nil); + return mem; +} + +void deallocate(void *mem) +{ + if (mem != nil) { + free(mem); + heap_chunks--; + } +} + +char *copystr(const char *s) +{ + char *c; + c= allocate(nil, (strlen(s)+1) * sizeof(*c)); + strcpy(c, s); + return c; +} + +/* Every object, list, letter, or variable, is made with cells. */ +typedef struct cell { + unsigned short refc; /* Reference count. */ + char type; /* Type of object. */ + unsigned char letter; /* Simply a letter. */ + char *name; /* Name of a word. */ + struct cell *hash; /* Hash chain. */ + struct cell *car, *cdr; /* To form lists. */ + +/* For a word: */ +# define value car /* Value of a variable. */ +# define base cdr /* Base-name in transformations. */ +# define suffix cdr /* Suffix in a treat-as. */ +# define flags letter /* Special flags. */ + +/* A substitution: */ +# define subst car + +} cell_t; + +typedef enum type { + CELL, /* A list cell. */ + STRING, /* To make a list of characters and substs. */ + SUBST, /* Variable to substitute. */ + /* Unique objects. */ + LETTER, /* A letter. */ + WORD, /* A string collapses to a word. */ + EQUALS, /* = operator, etc. */ + OPEN, + CLOSE, + PLUS, + MINUS, + STAR, + INPUT, + OUTPUT, + WHITE, + COMMENT, + SEMI, + EOLN, + N_TYPES /* number of different types */ +} type_t; + +#define is_unique(type) ((type) >= LETTER) + +/* Flags on a word. */ +#define W_SET 0x01 /* Not undefined, e.g. assigned to. */ +#define W_RDONLY 0x02 /* Read only. */ +#define W_LOCAL 0x04 /* Local variable, immediate substitution. */ +#define W_TEMP 0x08 /* Name of a temporary file, delete on quit. */ +#define W_SUFF 0x10 /* Has a suffix set on it. */ + +void princhar(int c) +/* Print a character, escaped if important to the shell *within* quotes. */ +{ + if (strchr("\\'\"<>();~$^&*|{}[]?", c) != nil) fputc('\\', stdout); + putchar(c); +} + +void prinstr(char *s) +/* Print a string, in quotes if the shell might not like it. */ +{ + int q= 0; + char *s2= s; + + while (*s2 != 0) + if (strchr("~`$^&*()=\\|[]{};'\"<>?", *s2++) != nil) q= 1; + + if (q) fputc('"', stdout); + while (*s != 0) princhar(*s++); + if (q) fputc('"', stdout); +} + +void prin2(cell_t *p); + +void prin1(cell_t *p) +/* Print a cell structure for debugging purposes. */ +{ + if (p == nil) { + printf("(\b(\b()\b)\b)"); + return; + } + + switch (p->type) { + case CELL: + printf("(\b(\b("); + prin2(p); + printf(")\b)\b)"); + break; + case STRING: + printf("\"\b\"\b\""); + prin2(p); + printf("\"\b\"\b\""); + break; + case SUBST: + printf("$\b$\b${%s}", p->subst->name); + break; + case LETTER: + princhar(p->letter); + break; + case WORD: + prinstr(p->name); + break; + case EQUALS: + printf("=\b=\b="); + break; + case PLUS: + printf("+\b+\b+"); + break; + case MINUS: + printf("-\b-\b-"); + break; + case STAR: + printf("*\b*\b*"); + break; + case INPUT: + printf(verbose >= 3 ? "<\b<\b<" : "<"); + break; + case OUTPUT: + printf(verbose >= 3 ? ">\b>\b>" : ">"); + break; + default: + assert(0); + } +} + +void prin2(cell_t *p) +/* Print a list for debugging purposes. */ +{ + while (p != nil && p->type <= STRING) { + prin1(p->car); + + if (p->type == CELL && p->cdr != nil) fputc(' ', stdout); + + p= p->cdr; + } + if (p != nil) prin1(p); /* Dotted pair? */ +} + +void prin1n(cell_t *p) { prin1(p); fputc('\n', stdout); } + +void prin2n(cell_t *p) { prin2(p); fputc('\n', stdout); } + +/* A program is consists of a series of lists at a certain indentation level. */ +typedef struct program { + struct program *next; + cell_t *file; /* Associated description file. */ + unsigned indent; /* Line indentation level. */ + unsigned lineno; /* Line number where this is found. */ + cell_t *line; /* One line of tokens. */ +} program_t; + +program_t *pc; /* Program Counter (what else?) */ +program_t *nextpc; /* Next line to execute. */ + +cell_t *oldcells; /* Keep a list of old cells, don't deallocate. */ + +cell_t *newcell(void) +/* Make a new empty cell. */ +{ + cell_t *p; + + if (oldcells != nil) { + p= oldcells; + oldcells= p->cdr; + heap_chunks++; + } else { + p= allocate(nil, sizeof(*p)); + } + + p->refc= 0; + p->type= CELL; + p->letter= 0; + p->name= nil; + p->car= nil; + p->cdr= nil; + return p; +} + +#define N_CHARS (1 + (unsigned char) -1) +#define HASHDENSE 0x400 + +cell_t *oblist[HASHDENSE + N_CHARS + N_TYPES]; + +unsigned hashfun(cell_t *p) +/* Use a blender on a cell. */ +{ + unsigned h; + char *name; + + switch (p->type) { + case WORD: + h= 0; + name= p->name; + while (*name != 0) h= (h * 0x1111) + *name++; + return h % HASHDENSE; + case LETTER: + return HASHDENSE + p->letter; + default: + return HASHDENSE + N_CHARS + p->type; + } +} + +cell_t *search(cell_t *p, cell_t ***hook) +/* Search for *p, return the one found. *hook may be used to insert or + * delete. + */ +{ + cell_t *sp; + + sp= *(*hook= &oblist[hashfun(p)]); + + if (p->type == WORD) { + /* More than one name per hash slot. */ + int cmp= 0; + + while (sp != nil && (cmp= strcmp(p->name, sp->name)) > 0) + sp= *(*hook= &sp->hash); + + if (cmp != 0) sp= nil; + } + return sp; +} + +void dec(cell_t *p) +/* Decrease the number of references to p, if zero delete and recurse. */ +{ + if (p == nil || --p->refc > 0) return; + + if (is_unique(p->type)) { + /* Remove p from the oblist. */ + cell_t *o, **hook; + + o= search(p, &hook); + + if (o == p) { + /* It's there, remove it. */ + *hook= p->hash; + p->hash= nil; + } + + if (p->type == WORD && (p->flags & W_TEMP)) { + /* A filename to remove. */ + if (verbose >= 2) { + printf("rm -f "); + prinstr(p->name); + fputc('\n', stdout); + } + if (unlink(p->name) < 0 && errno != ENOENT) + report(p->name); + } + } + deallocate(p->name); + dec(p->car); + dec(p->cdr); + p->cdr= oldcells; + oldcells= p; + heap_chunks--; +} + +cell_t *inc(cell_t *p) +/* Increase the number of references to p. */ +{ + cell_t *o, **hook; + + if (p == nil) return nil; + + if (++p->refc > 1 || !is_unique(p->type)) return p; + + /* First appearance, put p on the oblist. */ + o= search(p, &hook); + + if (o == nil) { + /* Not there yet, add it. */ + p->hash= *hook; + *hook= p; + } else { + /* There is another object already there with the same info. */ + o->refc++; + dec(p); + p= o; + } + return p; +} + +cell_t *go(cell_t *p, cell_t *field) +/* Often happening: You've got p, you want p->field. */ +{ + field= inc(field); + dec(p); + return field; +} + +cell_t *cons(type_t type, cell_t *p) +/* P is to be added to a list (or a string). */ +{ + cell_t *l= newcell(); + l->type= type; + l->refc++; + l->car= p; + return l; +} + +cell_t *append(type_t type, cell_t *p) +/* P is to be appended to a list (or a string). */ +{ + return p == nil || p->type == type ? p : cons(type, p); +} + +cell_t *findnword(char *name, size_t n) +/* Find the word with the given name of length n. */ +{ + cell_t *w= newcell(); + w->type= WORD; + w->name= allocate(nil, (n+1) * sizeof(*w->name)); + memcpy(w->name, name, n); + w->name[n]= 0; + return inc(w); +} + +cell_t *findword(char *name) +/* Find the word with the given null-terminated name. */ +{ + return findnword(name, strlen(name)); +} + +void quit(int exstat) +/* Remove all temporary names, then exit. */ +{ + cell_t **op, *p, *v, *b; + size_t chunks; + + /* Remove cycles, like X = X. */ + for (op= oblist; op < oblist + HASHDENSE; op++) { + p= *op; + while (p != nil) { + if (p->value != nil || p->base != nil) { + v= p->value; + b= p->base; + p->value= nil; + p->base= nil; + p= *op; + dec(v); + dec(b); + } else { + p= p->hash; + } + } + } + chunks= heap_chunks; + + /* Something may remain on an early quit: tempfiles. */ + for (op= oblist; op < oblist + HASHDENSE; op++) { + + while (*op != nil) { (*op)->refc= 1; dec(*op); } + } + + if (exstat != -1 && chunks > 0) { + fprintf(stderr, + "%s: internal fault: %d chunks still on the heap\n", + program, chunks); + } + exit(exstat); +} + +void interrupt(int sig) +{ + signal(sig, interrupt); + if (verbose >= 2) write(1, "# interrupt\n", 12); + action= 0; +} + +int extalnum(int c) +/* Uppercase, lowercase, digit, underscore or anything non-American. */ +{ + return isalnum(c) || c == '_' || c >= 0200; +} + +char *descr; /* Name of current description file. */ +FILE *dfp; /* Open description file. */ +int dch; /* Input character. */ +unsigned lineno; /* Line number in file. */ +unsigned indent; /* Indentation level. */ + +void getdesc(void) +{ + if (dch == EOF) return; + + if (dch == '\n') { lineno++; indent= 0; } + + if ((dch = getc(dfp)) == EOF && ferror(dfp)) fatal(descr); + + if (dch == 0) { + fprintf(stderr, "%s: %s is a binary file.\n", program, descr); + quit(-1); + } +} + +#define E_BASH 0x01 /* Escaped by backslash. */ +#define E_QUOTE 0x02 /* Escaped by double quote. */ +#define E_SIMPLE 0x04 /* More simple characters? */ + +cell_t *get_token(void) +/* Read one token from the description file. */ +{ + int whitetype= 0; + static int escape= 0; + cell_t *tok; + char *name; + int n, i; + + if (escape & E_SIMPLE) { + /* More simple characters? (Note: performance hack.) */ + if (isalnum(dch)) { + tok= newcell(); + tok->type= LETTER; + tok->letter= dch; + getdesc(); + return inc(tok); + } + escape&= ~E_SIMPLE; + } + + /* Gather whitespace. */ + for (;;) { + if (dch == '\\' && whitetype == 0) { + getdesc(); + if (isspace(dch)) { + /* \ whitespace: remove. */ + do { + getdesc(); + if (dch == '#' && !(escape & E_QUOTE)) { + /* \ # comment */ + do + getdesc(); + while (dch != '\n' + && dch != EOF); + } + } while (isspace(dch)); + continue; + } + escape|= E_BASH; /* Escaped character. */ + } + + if (escape != 0) break; + + if (dch == '#' && (indent == 0 || whitetype != 0)) { + /* # Comment. */ + do getdesc(); while (dch != '\n' && dch != EOF); + whitetype= COMMENT; + break; + } + + if (!isspace(dch) || dch == '\n' || dch == EOF) break; + + whitetype= WHITE; + + indent++; + if (dch == '\t') indent= (indent + 7) & ~7; + + getdesc(); + } + + if (dch == EOF) return nil; + + /* Make a token. */ + tok= newcell(); + + if (whitetype != 0) { + tok->type= whitetype; + return inc(tok); + } + + if (!(escape & E_BASH) && dch == '"') { + getdesc(); + if (!(escape & E_QUOTE)) { + /* Start of a string, signal this with a string cell. */ + escape|= E_QUOTE; + tok->type= STRING; + return inc(tok); + } else { + /* End of a string, back to normal mode. */ + escape&= ~E_QUOTE; + deallocate(tok); + return get_token(); + } + } + + if (escape & E_BASH + || strchr(escape & E_QUOTE ? "$" : "$=()+-*<>;\n", dch) == nil + ) { + if (dch == '\n') { + fprintf(stderr, + "\"%s\", line %u: missing closing quote\n", + descr, lineno); + escape&= ~E_QUOTE; + action= 0; + } + if (escape & E_BASH && dch == 'n') dch= '\n'; + escape&= ~E_BASH; + + /* A simple character. */ + tok->type= LETTER; + tok->letter= dch; + getdesc(); + escape|= E_SIMPLE; + return inc(tok); + } + + if (dch != '$') { + /* Single character token. */ + switch (dch) { + case '=': tok->type= EQUALS; break; + case '(': tok->type= OPEN; break; + case ')': tok->type= CLOSE; break; + case '+': tok->type= PLUS; break; + case '-': tok->type= MINUS; break; + case '*': tok->type= STAR; break; + case '<': tok->type= INPUT; break; + case '>': tok->type= OUTPUT; break; + case ';': tok->type= SEMI; break; + case '\n': tok->type= EOLN; break; + } + getdesc(); + return inc(tok); + } + + /* Substitution. */ + getdesc(); + if (dch == EOF || isspace(dch)) { + fprintf(stderr, "\"%s\", line %u: Word expected after '$'\n", + descr, lineno); + action= 0; + deallocate(tok); + return get_token(); + } + + name= allocate(nil, (n= 16) * sizeof(*name)); + i= 0; + + if (dch == '{' || dch == '(' /* )} */ ) { + /* $(X), ${X} */ + int lpar= dch; /* ( */ + int rpar= lpar == '{' ? '}' : ')'; + + for (;;) { + getdesc(); + if (dch == rpar) { getdesc(); break; } + if (isspace(dch) || dch == EOF) { + fprintf(stderr, + "\"%s\", line %u: $%c unmatched, no '%c'\n", + descr, lineno, lpar, rpar); + action= 0; + break; + } + name[i++]= dch; + if (i == n) + name= allocate(name, (n*= 2) * sizeof(char)); + } + } else + if (extalnum(dch)) { + /* $X */ + do { + name[i++]= dch; + if (i == n) + name= allocate(name, (n*= 2) * sizeof(char)); + getdesc(); + } while (extalnum(dch)); + } else { + /* $* */ + name[i++]= dch; + getdesc(); + } + name[i++]= 0; + name= allocate(name, i * sizeof(char)); + tok->type= SUBST; + tok->subst= newcell(); + tok->subst->type= WORD; + tok->subst->name= name; + tok->subst= inc(tok->subst); + return inc(tok); +} + +typedef enum how { SUPERFICIAL, PARTIAL, FULL, EXPLODE, IMPLODE } how_t; + +cell_t *explode(cell_t *p, how_t how); + +cell_t *get_string(cell_t **pp) +/* Get a string: A series of letters and substs. Special tokens '=', '+', '-' + * and '*' are also recognized if on their own. A finished string is "exploded" + * to a word if it consists of letters only. + */ +{ + cell_t *p= *pp, *s= nil, **ps= &s; + int quoted= 0; + + while (p != nil) { + switch (p->type) { + case STRING: + quoted= 1; + dec(p); + break; + case EQUALS: + case PLUS: + case MINUS: + case STAR: + case SUBST: + case LETTER: + *ps= cons(STRING, p); + ps= &(*ps)->cdr; + break; + default: + goto got_string; + } + p= get_token(); + } + got_string: + *pp= p; + + /* A single special token must be folded up. */ + if (!quoted && s != nil && s->cdr == nil) { + switch (s->car->type) { + case EQUALS: + case PLUS: + case MINUS: + case STAR: + case SUBST: + return go(s, s->car); + } + } + + /* Go over the string changing '=', '+', '-', '*' to letters. */ + for (p= s; p != nil; p= p->cdr) { + int c= 0; + + switch (p->car->type) { + case EQUALS: + c= '='; break; + case PLUS: + c= '+'; break; + case MINUS: + c= '-'; break; + case STAR: + c= '*'; break; + } + if (c != 0) { + dec(p->car); + p->car= newcell(); + p->car->type= LETTER; + p->car->letter= c; + p->car= inc(p->car); + } + } + return explode(s, SUPERFICIAL); +} + +cell_t *get_list(cell_t **pp, type_t stop) +/* Read a series of tokens upto a token of type "stop". */ +{ + cell_t *p= *pp, *l= nil, **pl= &l; + + while (p != nil && p->type != stop + && !(stop == EOLN && p->type == SEMI)) { + switch (p->type) { + case WHITE: + case COMMENT: + case SEMI: + case EOLN: + dec(p); + p= get_token(); + break; + case OPEN: + /* '(' words ')'. */ + dec(p); + p= get_token(); + *pl= cons(CELL, get_list(&p, CLOSE)); + pl= &(*pl)->cdr; + dec(p); + p= get_token(); + break; + case CLOSE: + /* Unexpected closing parenthesis. (*/ + fprintf(stderr, "\"%s\", line %u: unmatched ')'\n", + descr, lineno); + action= 0; + dec(p); + p= get_token(); + break; + case INPUT: + case OUTPUT: + *pl= cons(CELL, p); + pl= &(*pl)->cdr; + p= get_token(); + break; + case STRING: + case EQUALS: + case PLUS: + case MINUS: + case STAR: + case LETTER: + case SUBST: + *pl= cons(CELL, get_string(&p)); + pl= &(*pl)->cdr; + break; + default: + assert(0); + } + } + + if (p == nil && stop == CLOSE) { + /* Couldn't get the closing parenthesis. */ + fprintf(stderr, "\"%s\", lines %u-%u: unmatched '('\n", /*)*/ + descr, pc->lineno, lineno); + action= 0; + } + *pp= p; + return l; +} + +program_t *get_line(cell_t *file) +{ + program_t *l; + cell_t *p; + static keep_indent= 0; + static unsigned old_indent= 0; + + /* Skip leading whitespace to determine the indentation level. */ + indent= 0; + while ((p= get_token()) != nil && p->type == WHITE) dec(p); + + if (p == nil) return nil; /* EOF */ + + if (p->type == EOLN) indent= old_indent; /* Empty line. */ + + /* Make a program line. */ + pc= l= allocate(nil, sizeof(*l)); + + l->next= nil; + l->file= inc(file); + l->indent= keep_indent ? old_indent : indent; + l->lineno= lineno; + + l->line= get_list(&p, EOLN); + + /* If the line ended in a semicolon then keep the indentation level. */ + keep_indent= (p != nil && p->type == SEMI); + old_indent= l->indent; + + dec(p); + + if (verbose >= 4) { + if (l->line == nil) + fputc('\n', stdout); + else { + printf("%*s", (int) l->indent, ""); + prin2n(l->line); + } + } + return l; +} + +program_t *get_prog(void) +/* Read the description file into core. */ +{ + cell_t *file; + program_t *prog, **ppg= &prog; + + descr= copystr(descr); + + if (descr[0] == '-' && descr[1] == 0) { + /* -descr -: Read from standard input. */ + deallocate(descr); + descr= copystr("stdin"); + dfp= stdin; + } else { + char *d= descr; + + if (*d == '.' && *++d == '.') d++; + if (*d != '/') { + /* -descr name: Read /usr/lib/<name>/descr. */ + + d= allocate(nil, sizeof(LIB) + + (strlen(descr) + 7) * sizeof(*d)); + sprintf(d, "%s/%s/descr", LIB, descr); + deallocate(descr); + descr= d; + } + if ((dfp= fopen(descr, "r")) == nil) fatal(descr); + } + file= findword(descr); + deallocate(descr); + descr= file->name; + + /* Preread the first character. */ + dch= 0; + lineno= 1; + indent= 0; + getdesc(); + + while ((*ppg= get_line(file)) != nil) ppg= &(*ppg)->next; + + if (dfp != stdin) (void) fclose(dfp); + dec(file); + + return prog; +} + +void makenames(cell_t ***ppr, cell_t *s, char **name, size_t i, size_t *n) +/* Turn a string of letters and lists into words. A list denotes a choice + * between several paths, like a search on $PATH. + */ +{ + cell_t *p, *q; + size_t len; + + /* Simply add letters, skip empty lists. */ + while (s != nil && (s->car == nil || s->car->type == LETTER)) { + if (s->car != nil) { + if (i == *n) *name= allocate(*name, + (*n *= 2) * sizeof(**name)); + (*name)[i++]= s->car->letter; + } + s= s->cdr; + } + + /* If the end is reached then make a word out of the result. */ + if (s == nil) { + **ppr= cons(CELL, findnword(*name, i)); + *ppr= &(**ppr)->cdr; + return; + } + + /* Elements of a list must be tried one by one. */ + p= s->car; + s= s->cdr; + + while (p != nil) { + if (p->type == WORD) { + q= p; p= nil; + } else { + assert(p->type == CELL); + q= p->car; p= p->cdr; + assert(q != nil); + assert(q->type == WORD); + } + len= strlen(q->name); + if (i + len > *n) *name= allocate(*name, + (*n += i + len) * sizeof(**name)); + memcpy(*name + i, q->name, len); + + makenames(ppr, s, name, i+len, n); + } +} + +int constant(cell_t *p) +/* See if a string has been partially evaluated to a constant so that it + * can be imploded to a word. + */ +{ + while (p != nil) { + switch (p->type) { + case CELL: + case STRING: + if (!constant(p->car)) return 0; + p= p->cdr; + break; + case SUBST: + return 0; + default: + return 1; + } + } + return 1; +} + +cell_t *evaluate(cell_t *p, how_t how); + +cell_t *explode(cell_t *s, how_t how) +/* Explode a string with several choices to just one list of choices. */ +{ + cell_t *t, *r= nil, **pr= &r; + size_t i, n; + char *name; + struct stat st; + + if (how >= PARTIAL) { + /* Evaluate the string, expanding substitutions. */ + while (s != nil) { + assert(s->type == STRING); + t= inc(s->car); + s= go(s, s->cdr); + + t= evaluate(t, how == IMPLODE ? EXPLODE : how); + + /* A list of one element becomes that element. */ + if (t != nil && t->type == CELL && t->cdr == nil) + t= go(t, t->car); + + /* Append the result, trying to flatten it. */ + *pr= t; + + /* Find the end of what has just been added. */ + while ((*pr) != nil) { + *pr= append(STRING, *pr); + pr= &(*pr)->cdr; + } + } + s= r; + } + + /* Is the result a simple string of constants? */ + if (how <= PARTIAL && !constant(s)) return s; + + /* Explode the string to all possible choices, by now the string is + * a series of characters, words and lists of words. + */ + r= nil; pr= &r; + name= allocate(nil, (n= 16) * sizeof(char)); + i= 0; + + makenames(&pr, s, &name, i, &n); + deallocate(name); + assert(r != nil); + dec(s); + s= r; + + /* "How" may specify that a choice must be made. */ + if (how == IMPLODE) { + if (s->cdr != nil) { + /* More than one choice, find the file. */ + do { + assert(s->car->type == WORD); + if (stat(s->car->name, &st) >= 0) + return go(r, s->car); /* Found. */ + } while ((s= s->cdr) != nil); + } + /* The first name is the default if nothing is found. */ + return go(r, r->car); + } + + /* If the result is a list of one word then return that word, otherwise + * turn it into a string again unless this explode has been called + * by another explode. (Exploding a string inside a string, the joys + * of recursion.) + */ + if (s->cdr == nil) return go(s, s->car); + + return how >= EXPLODE ? s : cons(STRING, s); +} + +void modify(cell_t **pp, cell_t *p, type_t mode) +/* Add or remove the element p from the list *pp. */ +{ + while (*pp != nil) { + *pp= append(CELL, *pp); + + if ((*pp)->car == p) { + /* Found it, if adding then exit, else remove. */ + if (mode == PLUS) break; + *pp= go(*pp, (*pp)->cdr); + } else + pp= &(*pp)->cdr; + } + + if (*pp == nil && mode == PLUS) { + /* Not found, add it. */ + *pp= cons(CELL, p); + } else + dec(p); +} + +int tainted(cell_t *p) +/* A variable is tainted (must be substituted) if either it is marked as a + * local variable, or some subst in its value is. + */ +{ + if (p == nil) return 0; + + switch (p->type) { + case CELL: + case STRING: + return tainted(p->car) || tainted(p->cdr); + case SUBST: + return p->subst->flags & W_LOCAL || tainted(p->subst->value); + default: + return 0; + } +} + +cell_t *evaluate(cell_t *p, how_t how) +/* Evaluate an expression, usually the right hand side of an assignment. */ +{ + cell_t *q, *t, *r= nil, **pr= &r; + type_t mode; + + if (p == nil) return nil; + + switch (p->type) { + case CELL: + break; /* see below */ + case STRING: + return explode(p, how); + case SUBST: + if (how >= FULL || tainted(p)) + p= evaluate(go(p, p->subst->value), how); + return p; + case EQUALS: + fprintf(stderr, + "\"%s\", line %u: Can't do nested assignments\n", + descr, pc->lineno); + action= 0; + dec(p); + return nil; + case LETTER: + case WORD: + case INPUT: + case OUTPUT: + case PLUS: + case MINUS: + return p; + default: + assert(0); + } + + /* It's a list, see if there is a '*' there forcing a full expansion, + * or a '+' or '-' forcing an implosive expansion. (Yeah, right.) + * Otherwise evaluate each element. + */ + q = inc(p); + while (p != nil) { + if ((t= p->car) != nil) { + if (t->type == STAR) { + if (how < FULL) how= FULL; + dec(q); + *pr= evaluate(go(p, p->cdr), how); + return r; + } + if (how>=FULL && (t->type == PLUS || t->type == MINUS)) + break; + } + + t= evaluate(inc(t), how); + assert(p->type == CELL); + p= go(p, p->cdr); + + if (how >= FULL) { + /* Flatten the list. */ + *pr= t; + } else { + /* Keep the nested list structure. */ + *pr= cons(CELL, t); + } + + /* Find the end of what has just been added. */ + while ((*pr) != nil) { + *pr= append(CELL, *pr); + pr= &(*pr)->cdr; + } + } + + if (p == nil) { + /* No PLUS or MINUS: done. */ + dec(q); + return r; + } + + /* A PLUS or MINUS, reevaluate the original list implosively. */ + if (how < IMPLODE) { + dec(r); + dec(p); + return evaluate(q, IMPLODE); + } + dec(q); + + /* Execute the PLUSes and MINUSes. */ + while (p != nil) { + t= inc(p->car); + p= go(p, p->cdr); + + if (t != nil && (t->type == PLUS || t->type == MINUS)) { + /* Change the add/subtract mode. */ + mode= t->type; + dec(t); + continue; + } + + t= evaluate(t, IMPLODE); + + /* Add or remove all elements of t to/from r. */ + while (t != nil) { + if (t->type == CELL) { + modify(&r, inc(t->car), mode); + } else { + modify(&r, t, mode); + break; + } + t= go(t, t->cdr); + } + } + return r; +} + +/* An ACD program can be in three phases: Initialization (the first run + * of the program), argument scanning, and compilation. + */ +typedef enum phase { INIT, SCAN, COMPILE } phase_t; + +phase_t phase; + +typedef struct rule { /* Transformation rule. */ + struct rule *next; + char type; /* arg, transform, combine */ + char flags; + unsigned short npaths; /* Number of paths running through. */ +# define match from /* Arg matching strings. */ + cell_t *from; /* Transformation source suffixe(s) */ + cell_t *to; /* Destination suffix. */ + cell_t *wait; /* Files waiting to be transformed. */ + program_t *prog; /* Program to execute. */ + struct rule *path; /* Transformation path. */ +} rule_t; + +typedef enum ruletype { ARG, PREFER, TRANSFORM, COMBINE } ruletype_t; + +#define R_PREFER 0x01 /* A preferred transformation. */ + +rule_t *rules= nil; + +void newrule(ruletype_t type, cell_t *from, cell_t *to) +/* Make a new rule cell. */ +{ + rule_t *r= nil, **pr= &rules; + + /* See if there is a rule with the same suffixes, probably a matching + * transform and prefer, or a re-execution of the same arg command. + */ + while ((r= *pr) != nil) { + if (r->from == from && r->to == to) break; + pr= &r->next; + } + + if (*pr == nil) { + /* Add a new rule. */ + *pr= r= allocate(nil, sizeof(*r)); + + r->next= nil; + r->type= type; + r->flags= 0; + r->from= r->to= r->wait= nil; + r->path= nil; + } + if (type == TRANSFORM) r->type= TRANSFORM; + if (type == PREFER) r->flags|= R_PREFER; + if (type != PREFER) r->prog= pc; + dec(r->from); r->from= from; + dec(r->to); r->to= to; +} + +int talk(void) +/* True if verbose and if so indent what is to come. */ +{ + if (verbose < 3) return 0; + printf("%*s", (int) pc->indent, ""); + return 1; +} + +void unix_exec(cell_t *c) +/* Execute the list of words p as a UNIX command. */ +{ + cell_t *v, *a; + int fd[2]; + int *pf; + char **argv; + int i, n; + int r, pid, status; + + if (action == 0) return; /* Error mode. */ + + if (talk() || verbose >= 2) prin2n(c); + + fd[0]= fd[1]= -1; + + argv= allocate(nil, (n= 16) * sizeof(*argv)); + i= 0; + + /* Gather argv[] and scan for I/O redirection. */ + for (v= c; v != nil; v= v->cdr) { + a= v->car; + pf= nil; + if (a->type == INPUT) pf= &fd[0]; + if (a->type == OUTPUT) pf= &fd[1]; + + if (pf == nil) { + /* An argument. */ + argv[i++]= a->name; + if (i==n) argv= allocate(argv, (n*= 2) * sizeof(*argv)); + continue; + } + /* I/O redirection. */ + if ((v= v->cdr) == nil || (a= v->car)->type != WORD) { + fprintf(stderr, + "\"%s\", line %u: I/O redirection without a file\n", + descr, pc->lineno); + action= 0; + if (v == nil) break; + } + if (*pf >= 0) close(*pf); + + if (action >= 2 + && (*pf= open(a->name, pf == &fd[0] ? O_RDONLY + : O_WRONLY | O_CREAT | O_TRUNC, 0666)) < 0 + ) { + report(a->name); + action= 0; + } + } + argv[i]= nil; + + if (i >= 0 && action > 0 && verbose == 1) { + char *name= strrchr(argv[0], '/'); + + if (name == nil) name= argv[0]; else name++; + + printf("%s\n", name); + } + if (i >= 0 && action >= 2) { + /* Really execute the command. */ + fflush(stdout); + switch (pid= fork()) { + case -1: + fatal("fork()"); + case 0: + if (fd[0] >= 0) { dup2(fd[0], 0); close(fd[0]); } + if (fd[1] >= 0) { dup2(fd[1], 1); close(fd[1]); } + execvp(argv[0], argv); + report(argv[0]); + exit(-1); + } + } + if (fd[0] >= 0) close(fd[0]); + if (fd[1] >= 0) close(fd[1]); + + if (i >= 0 && action >= 2) { + /* Wait for the command to terminate. */ + while ((r= wait(&status)) != pid && (r >= 0 || errno == EINTR)); + + if (status != 0) { + int sig= WTERMSIG(status); + + if (!WIFEXITED(status) + && sig != SIGINT && sig != SIGPIPE) { + fprintf(stderr, "%s: %s: Signal %d%s\n", + program, argv[0], sig, + status & 0x80 ? " - core dumped" : ""); + } + action= 0; + } + } + deallocate(argv); +} + +/* Special read-only variables ($*) and lists. */ +cell_t *V_star, **pV_star; +cell_t *L_files, **pL_files= &L_files; +cell_t *V_in, *V_out, *V_stop, *L_args, *L_predef; + +typedef enum exec { DOIT, DONT } exec_t; + +void execute(exec_t how, unsigned indent); + +int equal(cell_t *p, cell_t *q) +/* Two lists are equal if they contain each others elements. */ +{ + cell_t *t, *m1, *m2; + + t= inc(newcell()); + t->cdr= inc(newcell()); + t->cdr->cdr= inc(newcell()); + t->cdr->car= newcell(); + t->cdr->car->type= MINUS; + t->cdr->car= inc(t->cdr->car); + + /* Compute p - q. */ + t->car= inc(p); + t->cdr->cdr->car= inc(q); + m1= evaluate(inc(t), IMPLODE); + dec(m1); + + /* Compute q - p. */ + t->car= q; + t->cdr->cdr->car= p; + m2= evaluate(t, IMPLODE); + dec(m2); + + /* Both results must be empty. */ + return m1 == nil && m2 == nil; +} + +int wordlist(cell_t **pw, int atom) +/* Check if p is a list of words, typically an imploded list. Return + * the number of words seen, -1 if they are not words (INPUT/OUTPUT?). + * If atom is true than a list of one word is turned into a word. + */ +{ + int n= 0; + cell_t *p, **pp= pw; + + while (*pp != nil) { + *pp= append(CELL, *pp); + p= (*pp)->car; + n= n >= 0 && p != nil && p->type == WORD ? n+1 : -1; + pp= &(*pp)->cdr; + } + if (atom && n == 1) *pw= go(*pw, (*pw)->car); + return n; +} + +char *template; /* Current name of a temporary file. */ +static char *tp; /* Current place withing the tempfile. */ + +char *maketemp(void) +/* Return a name that can be used as a temporary filename. */ +{ + int i= 0; + + if (tp == nil) { + size_t len= strlen(template); + + template= allocate(template, (len+20) * sizeof(*template)); + sprintf(template+len, "/acd%d", getpid()); + tp= template + strlen(template); + } + + for (;;) { + switch (tp[i]) { + case 0: tp[i]= 'a'; + tp[i+1]= 0; return template; + case 'z': tp[i++]= 'a'; break; + default: tp[i]++; return template; + } + } +} + +void inittemp(char *tmpdir) +/* Initialize the temporary filename generator. */ +{ + template= allocate(nil, (strlen(tmpdir)+20) * sizeof(*template)); + sprintf(template, "%s/acd%d", tmpdir, getpid()); + tp= template + strlen(template); + + /* Create a directory within tempdir that we can safely play in. */ + while (action != 1 && mkdir(template, 0700) < 0) { + if (errno == EEXIST) { + (void) maketemp(); + } else { + report(template); + action= 0; + } + } + if (verbose >= 2) printf("mkdir %s\n", template); + while (*tp != 0) tp++; + *tp++= '/'; + *tp= 0; +} + +void deltemp(void) +/* Remove our temporary temporaries directory. */ +{ + while (*--tp != '/') {} + *tp = 0; + if (rmdir(template) < 0 && errno != ENOENT) report(template); + if (verbose >= 2) printf("rmdir %s\n", template); + deallocate(template); +} + +cell_t *splitenv(char *env) +/* Split a string from the environment into several words at whitespace + * and colons. Two colons (::) become a dot. + */ +{ + cell_t *r= nil, **pr= &r; + char *p; + + do { + while (*env != 0 && isspace(*env)) env++; + + if (*env == 0) break; + + p= env; + while (*p != 0 && !isspace(*p) && *p != ':') p++; + + *pr= cons(CELL, + p == env ? findword(".") : findnword(env, p-env)); + pr= &(*pr)->cdr; + env= p; + } while (*env++ != 0); + return r; +} + +void key_usage(char *how) +{ + fprintf(stderr, "\"%s\", line %u: Usage: %s %s\n", + descr, pc->lineno, pc->line->car->name, how); + action= 0; +} + +void inappropriate(void) +{ + fprintf(stderr, "\"%s\", line %u: wrong execution phase for '%s'\n", + descr, pc->lineno, pc->line->car->name); + action= 0; +} + +int readonly(cell_t *v) +{ + if (v->flags & W_RDONLY) { + fprintf(stderr, "\"%s\", line %u: %s is read-only\n", + descr, pc->lineno, v->name); + action= 0; + return 1; + } + return 0; +} + +void complain(cell_t *err) +/* acd: err ... */ +{ + cell_t *w; + + fprintf(stderr, "%s:", program); + + while (err != nil) { + if (err->type == CELL) { + w= err->car; err= err->cdr; + } else { + w= err; err= nil; + } + fprintf(stderr, " %s", w->name); + } + action= 0; +} + +int keyword(char *name) +/* True if the current line is headed by the given keyword. */ +{ + cell_t *t; + + return (t= pc->line) != nil && t->type == CELL + && (t= t->car) != nil && t->type == WORD + && strcmp(t->name, name) == 0; +} + +cell_t *getvar(cell_t *v) +/* Return a word or the word referenced by a subst. */ +{ + if (v == nil) return nil; + if (v->type == WORD) return v; + if (v->type == SUBST) return v->subst; + return nil; +} + +void argscan(void), compile(void); +void transform(rule_t *); + +void exec_one(void) +/* Execute one line of the program. */ +{ + cell_t *v, *p, *q, *r, *t; + unsigned n= 0; + static int last_if= 1; + + /* Description file this line came from. */ + descr= pc->file->name; + + for (p= pc->line; p != nil; p= p->cdr) n++; + + if (n == 0) return; /* Null statement. */ + + p= pc->line; + q= p->cdr; + r= q == nil ? nil : q->cdr; + + /* Try one by one all the different commands. */ + + if (n >= 2 && q->car != nil && q->car->type == EQUALS) { + /* An assignment. */ + int flags; + + if ((v= getvar(p->car)) == nil) { + fprintf(stderr, + "\"%s\", line %u: Usage: <var> = expr ...\n", + descr, pc->lineno); + action= 0; + return; + } + + if (readonly(v)) return; + + flags= v->flags; + v->flags|= W_LOCAL|W_RDONLY; + t= evaluate(inc(r), PARTIAL); + dec(v->value); + v->value= t; + v->flags= flags | W_SET; + if (talk()) { + printf("%s =\b=\b= ", v->name); + prin2n(t); + } + } else + if (keyword("unset")) { + /* Set a variable to "undefined". */ + + if (n != 2 || (v= getvar(q->car)) == nil) { + key_usage("<var>"); + return; + } + if (readonly(v)) return; + + if (talk()) prin2n(p); + + dec(v->value); + v->value= nil; + v->flags&= ~W_SET; + } else + if (keyword("import")) { + /* Import a variable from the UNIX environment. */ + char *env; + + if (n != 2 || (v= getvar(q->car)) == nil) { + key_usage("<var>"); + return; + } + if (readonly(v)) return; + + if ((env= getenv(v->name)) == nil) return; + + if (talk()) printf("import %s=%s\n", v->name, env); + + t= splitenv(env); + dec(v->value); + v->value= t; + v->flags|= W_SET; + } else + if (keyword("mktemp")) { + /* Assign a variable the name of a temporary file. */ + char *tmp, *suff; + + r= evaluate(inc(r), IMPLODE); + if (n == 3 && wordlist(&r, 1) != 1) n= 0; + + if ((n != 2 && n != 3) || (v= getvar(q->car)) == nil) { + dec(r); + key_usage("<var> [<suffix>]"); + return; + } + if (readonly(v)) { dec(r); return; } + + tmp= maketemp(); + suff= r == nil ? "" : r->name; + + t= newcell(); + t->type= WORD; + t->name= allocate(nil, + (strlen(tmp) + strlen(suff) + 1) * sizeof(*t->name)); + strcpy(t->name, tmp); + strcat(t->name, suff); + t= inc(t); + dec(r); + dec(v->value); + v->value= t; + v->flags|= W_SET; + t->flags|= W_TEMP; + if (talk()) printf("mktemp %s=%s\n", v->name, t->name); + } else + if (keyword("temporary")) { + /* Mark a word as a temporary file. */ + cell_t *tmp; + + tmp= evaluate(inc(q), IMPLODE); + + if (wordlist(&tmp, 1) < 0) { + dec(tmp); + key_usage("<word>"); + return; + } + if (talk()) printf("temporary %s\n", tmp->name); + + tmp->flags|= W_TEMP; + dec(tmp); + } else + if (keyword("stop")) { + /* Set the suffix to stop the transformation on. */ + cell_t *suff; + + if (phase > SCAN) { inappropriate(); return; } + + suff= evaluate(inc(q), IMPLODE); + + if (wordlist(&suff, 1) != 1) { + dec(suff); + key_usage("<suffix>"); + return; + } + dec(V_stop); + V_stop= suff; + if (talk()) printf("stop %s\n", suff->name); + } else + if (keyword("numeric")) { + /* Check if a string denotes a number, like $n in -O$n. */ + cell_t *num; + char *pn; + + num= evaluate(inc(q), IMPLODE); + + if (wordlist(&num, 1) != 1) { + dec(num); + key_usage("<arg>"); + return; + } + if (talk()) printf("numeric %s\n", num->name); + + (void) strtoul(num->name, &pn, 10); + if (*pn != 0) { + complain(phase == SCAN ? V_star->value : nil); + if (phase == SCAN) fputc(':', stderr); + fprintf(stderr, " '%s' is not a number\n", num->name); + } + dec(num); + } else + if (keyword("error")) { + /* Signal an error. */ + cell_t *err; + + err= evaluate(inc(q), IMPLODE); + + if (wordlist(&err, 0) < 1) { + dec(err); + key_usage("expr ..."); + return; + } + + if (talk()) { printf("error "); prin2n(err); } + + complain(err); + fputc('\n', stderr); + dec(err); + } else + if (keyword("if")) { + /* if (list) = (list) using set comparison. */ + int eq; + + if (n != 4 || r->car == nil || r->car->type != EQUALS) { + key_usage("<expr> = <expr>"); + execute(DONT, pc->indent+1); + last_if= 1; + return; + } + q= q->car; + r= r->cdr->car; + if (talk()) { + printf("if "); + prin1(t= evaluate(inc(q), IMPLODE)); + dec(t); + printf(" = "); + prin1n(t= evaluate(inc(r), IMPLODE)); + dec(t); + } + eq= equal(q, r); + execute(eq ? DOIT : DONT, pc->indent+1); + last_if= eq; + } else + if (keyword("ifdef") || keyword("ifndef")) { + /* Is a variable defined or undefined? */ + int doit; + + if (n != 2 || (v= getvar(q->car)) == nil) { + key_usage("<var>"); + execute(DONT, pc->indent+1); + last_if= 1; + return; + } + if (talk()) prin2n(p); + + doit= ((v->flags & W_SET) != 0) ^ (p->car->name[2] == 'n'); + execute(doit ? DOIT : DONT, pc->indent+1); + last_if= doit; + } else + if (keyword("iftemp") || keyword("ifhash")) { + /* Is a file a temporary file? */ + /* Does a file need preprocessing? */ + cell_t *file; + int doit= 0; + + file= evaluate(inc(q), IMPLODE); + + if (wordlist(&file, 1) != 1) { + dec(file); + key_usage("<arg>"); + return; + } + if (talk()) printf("%s %s\n", p->car->name, file->name); + + if (p->car->name[2] == 't') { + /* iftemp file */ + if (file->flags & W_TEMP) doit= 1; + } else { + /* ifhash file */ + int fd; + char hash; + + if ((fd= open(file->name, O_RDONLY)) >= 0) { + if (read(fd, &hash, 1) == 1 && hash == '#') + doit= 1; + close(fd); + } + } + dec(file); + + execute(doit ? DOIT : DONT, pc->indent+1); + last_if= doit; + } else + if (keyword("else")) { + /* Else clause for an if, ifdef, or ifndef. */ + if (n != 1) { + key_usage(""); + execute(DONT, pc->indent+1); + return; + } + if (talk()) prin2n(p); + + execute(!last_if ? DOIT : DONT, pc->indent+1); + } else + if (keyword("treat")) { + /* Treat a file as having a certain suffix. */ + + if (phase > SCAN) { inappropriate(); return; } + + if (n == 3) { + q= evaluate(inc(q->car), IMPLODE); + r= evaluate(inc(r->car), IMPLODE); + } + if (n != 3 || wordlist(&q, 1) != 1 || wordlist(&r, 1) != 1) { + if (n == 3) { dec(q); dec(r); } + key_usage("<file> <suffix>"); + return; + } + if (talk()) printf("treat %s %s\n", q->name, r->name); + + dec(q->suffix); + q->suffix= r; + q->flags|= W_SUFF; + dec(q); + } else + if (keyword("apply")) { + /* Apply a transformation rule to the current input file. */ + rule_t *rule, *sav_path; + cell_t *sav_wait, *sav_in, *sav_out; + program_t *sav_next; + + if (phase != COMPILE) { inappropriate(); return; } + + if (V_star->value->cdr != nil) { + fprintf(stderr, "\"%s\", line %u: $* is not one file\n", + descr, pc->lineno); + action= 0; + return; + } + if (n == 3) { + q= evaluate(inc(q->car), IMPLODE); + r= evaluate(inc(r->car), IMPLODE); + } + if (n != 3 || wordlist(&q, 1) != 1 || wordlist(&r, 1) != 1) { + if (n == 3) { dec(q); dec(r); } + key_usage("<file> <suffix>"); + return; + } + if (talk()) printf("apply %s %s\n", q->name, r->name); + + /* Find a rule */ + for (rule= rules; rule != nil; rule= rule->next) { + if (rule->type == TRANSFORM + && rule->from == q && rule->to == r) break; + } + if (rule == nil) { + fprintf(stderr, + "\"%s\", line %u: no %s %s transformation\n", + descr, pc->lineno, q->name, r->name); + action= 0; + } + dec(q); + dec(r); + if (rule == nil) return; + + /* Save the world. */ + sav_path= rule->path; + sav_wait= rule->wait; + sav_in= V_in->value; + sav_out= V_out->value; + sav_next= nextpc; + + /* Isolate the rule and give it new input. */ + rule->path= rule; + rule->wait= V_star->value; + V_star->value= nil; + V_in->value= nil; + V_out->value= nil; + + transform(rule); + + /* Retrieve the new $* and repair. */ + V_star->value= rule->wait; + rule->path= sav_path; + rule->wait= sav_wait; + V_in->value= sav_in; + V_out->value= sav_out; + V_out->flags= W_SET|W_LOCAL; + nextpc= sav_next; + } else + if (keyword("include")) { + /* Include another description file into this program. */ + cell_t *file; + program_t *incl, *prog, **ppg= &prog; + + file= evaluate(inc(q), IMPLODE); + + if (wordlist(&file, 1) != 1) { + dec(file); + key_usage("<file>"); + return; + } + if (talk()) printf("include %s\n", file->name); + descr= file->name; + incl= pc; + prog= get_prog(); + dec(file); + + /* Raise the program to the include's indent level. */ + while (*ppg != nil) { + (*ppg)->indent += incl->indent; + ppg= &(*ppg)->next; + } + + /* Kill the include and splice the included program in. */ + dec(incl->line); + incl->line= nil; + *ppg= incl->next; + incl->next= prog; + pc= incl; + nextpc= prog; + } else + if (keyword("arg")) { + /* An argument scanning rule. */ + + if (phase > SCAN) { inappropriate(); return; } + + if (n < 2) { + key_usage("<string> ..."); + execute(DONT, pc->indent+1); + return; + } + if (talk()) prin2n(p); + + newrule(ARG, inc(q), nil); + + /* Always skip the body, it comes later. */ + execute(DONT, pc->indent+1); + } else + if (keyword("transform")) { + /* A file transformation rule. */ + + if (phase > SCAN) { inappropriate(); return; } + + if (n == 3) { + q= evaluate(inc(q->car), IMPLODE); + r= evaluate(inc(r->car), IMPLODE); + } + if (n != 3 || wordlist(&q, 1) != 1 || wordlist(&r, 1) != 1) { + if (n == 3) { dec(q); dec(r); } + key_usage("<suffix1> <suffix2>"); + execute(DONT, pc->indent+1); + return; + } + if (talk()) printf("transform %s %s\n", q->name, r->name); + + newrule(TRANSFORM, q, r); + + /* Body comes later. */ + execute(DONT, pc->indent+1); + } else + if (keyword("prefer")) { + /* Prefer a transformation over others. */ + + if (phase > SCAN) { inappropriate(); return; } + + if (n == 3) { + q= evaluate(inc(q->car), IMPLODE); + r= evaluate(inc(r->car), IMPLODE); + } + if (n != 3 || wordlist(&q, 1) != 1 || wordlist(&r, 1) != 1) { + if (n == 3) { dec(q); dec(r); } + key_usage("<suffix1> <suffix2>"); + return; + } + if (talk()) printf("prefer %s %s\n", q->name, r->name); + + newrule(PREFER, q, r); + } else + if (keyword("combine")) { + /* A file combination (loader) rule. */ + + if (phase > SCAN) { inappropriate(); return; } + + if (n == 3) { + q= evaluate(inc(q->car), IMPLODE); + r= evaluate(inc(r->car), IMPLODE); + } + if (n != 3 || wordlist(&q, 0) < 1 || wordlist(&r, 1) != 1) { + if (n == 3) { dec(q); dec(r); } + key_usage("<suffix-list> <suffix>"); + execute(DONT, pc->indent+1); + return; + } + if (talk()) { + printf("combine "); + prin1(q); + printf(" %s\n", r->name); + } + + newrule(COMBINE, q, r); + + /* Body comes later. */ + execute(DONT, pc->indent+1); + } else + if (keyword("scan") || keyword("compile")) { + program_t *next= nextpc; + + if (n != 1) { key_usage(""); return; } + if (phase != INIT) { inappropriate(); return; } + + if (talk()) prin2n(p); + + argscan(); + if (p->car->name[0] == 'c') compile(); + nextpc= next; + } else { + /* A UNIX command. */ + t= evaluate(inc(pc->line), IMPLODE); + unix_exec(t); + dec(t); + } +} + +void execute(exec_t how, unsigned indent) +/* Execute (or skip) all lines with at least the given indent. */ +{ + int work= 0; /* Need to execute at least one line. */ + unsigned firstline; + unsigned nice_indent= 0; /* 0 = Don't know what's nice yet. */ + + if (pc == nil) return; /* End of program. */ + + firstline= pc->lineno; + + if (how == DONT) { + /* Skipping a body, but is there another guard? */ + pc= pc->next; + if (pc != nil && pc->indent < indent && pc->line != nil) { + /* There is one! Bail out, then it get's executed. */ + return; + } + } else { + /* Skip lines with a lesser indentation, they are guards for + * the same substatements. Don't go past empty lines. + */ + while (pc != nil && pc->indent < indent && pc->line != nil) + pc= pc->next; + } + + /* Execute all lines with an indentation of at least "indent". */ + while (pc != nil && pc->indent >= indent) { + if (pc->indent != nice_indent && how == DOIT) { + if (nice_indent != 0) { + fprintf(stderr, + "\"%s\", line %u: (warning) sudden indentation shift\n", + descr, pc->lineno); + } + nice_indent= pc->indent; + } + nextpc= pc->next; + if (how == DOIT) exec_one(); + pc= nextpc; + work= 1; + } + + if (indent > 0 && !work) { + fprintf(stderr, "\"%s\", line %u: empty body, no statements\n", + descr, firstline); + action= 0; + } +} + +int argmatch(int shift, cell_t *match, cell_t *match1, char *arg1) +/* Try to match an arg rule to the input file list L_args. Execute the arg + * body (pc is set to it) on success. + */ +{ + cell_t *oldval, *v; + int m, oldflags; + size_t i, len; + int minus= 0; + + if (shift) { + /* An argument has been accepted and may be shifted to $*. */ + cell_t **oldpstar= pV_star; + *pV_star= L_args; + L_args= *(pV_star= &L_args->cdr); + *pV_star= nil; + + if (argmatch(0, match->cdr, nil, nil)) return 1; + + /* Undo the damage. */ + *pV_star= L_args; + L_args= *(pV_star= oldpstar); + *pV_star= nil; + return 0; + } + + if (match == nil) { + /* A full match, execute the arg body. */ + + /* Enable $>. */ + V_out->flags= W_SET|W_LOCAL; + + if (verbose >= 3) { + prin2(pc->line); + printf(" =\b=\b= "); + prin2n(V_star->value); + } + execute(DOIT, pc->indent+1); + + /* Append $> to the file list. */ + if (V_out->value != nil) { + *pL_files= cons(CELL, V_out->value); + pL_files= &(*pL_files)->cdr; + } + + /* Disable $>. */ + V_out->value= nil; + V_out->flags= W_SET|W_LOCAL|W_RDONLY; + + return 1; + } + + if (L_args == nil) return 0; /* Out of arguments to match. */ + + /* Match is a list of words, substs and strings containing letters and + * substs. Match1 is the current element of the first element of match. + * Arg1 is the current character of the first element of L_args. + */ + if (match1 == nil) { + /* match1 is at the end of a string, then arg1 must also. */ + if (arg1 != nil) { + if (*arg1 != 0) return 0; + return argmatch(1, match, nil, nil); + } + /* If both are nil: Initialize. */ + match1= match->car; + arg1= L_args->car->name; + + /* A subst may not match a leading '-'. */ + if (arg1[0] == '-') minus= 1; + } + + if (match1->type == WORD && strcmp(match1->name, arg1) == 0) { + /* A simple match of an argument. */ + + return argmatch(1, match, nil, nil); + } + + if (match1->type == SUBST && !minus) { + /* A simple match of a subst. */ + + /* The variable gets the first of the arguments as its value. */ + v= match1->subst; + if (v->flags & W_RDONLY) return 0; /* ouch */ + oldflags= v->flags; + v->flags= W_SET|W_LOCAL|W_RDONLY; + oldval= v->value; + v->value= inc(L_args->car); + + m= argmatch(1, match, nil, nil); + + /* Recover the value of the variable. */ + dec(v->value); + v->flags= oldflags; + v->value= oldval; + return m; + } + if (match1->type != STRING) return 0; + + /* Match the first item in the string. */ + if (match1->car == nil) return 0; + + if (match1->car->type == LETTER + && match1->car->letter == (unsigned char) *arg1) { + /* A letter matches, try the rest of the string. */ + + return argmatch(0, match, match1->cdr, arg1+1); + } + + /* It can only be a subst in a string now. */ + len= strlen(arg1); + if (match1->car->type != SUBST || minus || len == 0) return 0; + + /* The variable can match from 1 character to all of the argument. + * Matching as few characters as possible happens to be the Right Thing. + */ + v= match1->car->subst; + if (v->flags & W_RDONLY) return 0; /* ouch */ + oldflags= v->flags; + v->flags= W_SET|W_LOCAL|W_RDONLY; + oldval= v->value; + + m= 0; + for (i= match1->cdr == nil ? len : 1; !m && i <= len; i++) { + v->value= findnword(arg1, i); + + m= argmatch(0, match, match1->cdr, arg1+i); + + dec(v->value); + } + /* Recover the value of the variable. */ + v->flags= oldflags; + v->value= oldval; + return m; +} + +void argscan(void) +/* Match all the arguments to the arg rules, those that don't match are + * used as files for transformation. + */ +{ + rule_t *rule; + int m; + + phase= SCAN; + + /* Process all the arguments. */ + while (L_args != nil) { + pV_star= &V_star->value; + + /* Try all the arg rules. */ + m= 0; + for (rule= rules; !m && rule != nil; rule= rule->next) { + if (rule->type != ARG) continue; + + pc= rule->prog; + + m= argmatch(0, rule->match, nil, nil); + } + dec(V_star->value); + V_star->value= nil; + + /* On failure, add the first argument to the list of files. */ + if (!m) { + *pL_files= L_args; + L_args= *(pL_files= &L_args->cdr); + *pL_files= nil; + } + } + phase= INIT; +} + +int member(cell_t *p, cell_t *l) +/* True if p is a member of list l. */ +{ + while (l != nil && l->type == CELL) { + if (p == l->car) return 1; + l= l->cdr; + } + return p == l; +} + +long basefind(cell_t *f, cell_t *l) +/* See if f has a suffix in list l + set the base name of f. + * -1 if not found, preference number for a short basename otherwise. */ +{ + cell_t *suff; + size_t blen, slen; + char *base; + + /* Determine base name of f, with suffix. */ + if ((base= strrchr(f->name, '/')) == nil) base= f->name; else base++; + blen= strlen(base); + + /* Try suffixes. */ + while (l != nil) { + if (l->type == CELL) { + suff= l->car; l= l->cdr; + } else { + suff= l; l= nil; + } + if (f->flags & W_SUFF) { + /* F has a suffix imposed on it. */ + if (f->suffix == suff) return 0; + continue; + } + slen= strlen(suff->name); + if (slen < blen && strcmp(base+blen-slen, suff->name) == 0) { + /* Got it! */ + dec(f->base); + f->base= findnword(base, blen-slen); + return 10000L * (blen - slen); + } + } + return -1; +} + +#define NO_PATH 2000000000 /* No path found yet. */ + +long shortest; /* Length of the shortest path as yet. */ + +rule_t *findpath(long depth, int seek, cell_t *file, rule_t *start) +/* Find the path of the shortest transformation to the stop suffix. */ +{ + rule_t *rule; + + if (action == 0) return nil; + + if (start == nil) { + /* No starting point defined, find one using "file". */ + + for (rule= rules; rule != nil; rule= rule->next) { + if (rule->type < TRANSFORM) continue; + + if ((depth= basefind(file, rule->from)) >= 0) { + if (findpath(depth, seek, nil, rule) != nil) + return rule; + } + } + return nil; + } + + /* Cycle? */ + if (start->path != nil) { + /* We can't have cycles through combines. */ + if (start->type == COMBINE) { + fprintf(stderr, + "\"%s\": contains a combine-combine cycle\n", + descr); + action= 0; + } + return nil; + } + + /* Preferred transformations are cheap. */ + if (start->flags & R_PREFER) depth-= 100; + + /* Try to go from start closer to the stop suffix. */ + for (rule= rules; rule != nil; rule= rule->next) { + if (rule->type < TRANSFORM) continue; + + if (member(start->to, rule->from)) { + start->path= rule; + rule->npaths++; + if (findpath(depth+1, seek, nil, rule) != nil) + return start; + start->path= nil; + rule->npaths--; + } + } + + if (V_stop == nil) { + fprintf(stderr, "\"%s\": no stop suffix has been defined\n", + descr); + action= 0; + return nil; + } + + /* End of the line? */ + if (start->to == V_stop) { + /* Got it. */ + if (seek) { + /* Second hunt, do we find the shortest? */ + if (depth == shortest) return start; + } else { + /* Is this path shorter than the last one? */ + if (depth < shortest) shortest= depth; + } + } + return nil; /* Fail. */ +} + +void transform(rule_t *rule) +/* Transform the file(s) connected to the rule according to the rule. */ +{ + cell_t *file, *in, *out; + char *base; + + /* Let $* be the list of input files. */ + while (rule->wait != nil) { + file= rule->wait; + rule->wait= file->cdr; + file->cdr= V_star->value; + V_star->value= file; + } + + /* Set $< to the basename of the first input file. */ + file= file->car; + V_in->value= in= inc(file->flags & W_SUFF ? file : file->base); + file->flags&= ~W_SUFF; + + /* Set $> to the output file name of the transformation. */ + out= newcell(); + out->type= WORD; + base= rule->path == nil ? in->name : maketemp(); + out->name= allocate(nil, + (strlen(base)+strlen(rule->to->name)+1) * sizeof(*out->name)); + strcpy(out->name, base); + if (rule->path == nil || strchr(rule->to->name, '/') == nil) + strcat(out->name, rule->to->name); + out= inc(out); + if (rule->path != nil) out->flags|= W_TEMP; + + V_out->value= out; + V_out->flags= W_SET|W_LOCAL; + + /* Do a transformation. (Finally) */ + if (verbose >= 3) { + printf("%s ", rule->type==TRANSFORM ? "transform" : "combine"); + prin2(V_star->value); + printf(" %s\n", out->name); + } + pc= rule->prog; + execute(DOIT, pc->indent+1); + + /* Hand $> over to the next rule, it must be a single word. */ + out= evaluate(V_out->value, IMPLODE); + if (wordlist(&out, 1) != 1) { + fprintf(stderr, + "\"%s\", line %u: $> should be returned as a single word\n", + descr, rule->prog->lineno); + action= 0; + } + + if ((rule= rule->path) != nil) { + /* There is a next rule. */ + dec(out->base); + out->base= in; /* Basename of input file. */ + file= inc(newcell()); + file->car= out; + file->cdr= rule->wait; + rule->wait= file; + } else { + dec(in); + dec(out); + } + + /* Undo the damage to $*, $<, and $>. */ + dec(V_star->value); + V_star->value= nil; + V_in->value= nil; + V_out->value= nil; + V_out->flags= W_SET|W_LOCAL|W_RDONLY; +} + +void compile(void) +{ + rule_t *rule; + cell_t *file, *t; + + phase= COMPILE; + + /* Implode the files list. */ + L_files= evaluate(L_files, IMPLODE); + if (wordlist(&L_files, 0) < 0) { + fprintf(stderr, "\"%s\": An assignment to $> contained junk\n", + descr); + action= 0; + } + + while (action != 0 && L_files != nil) { + file= L_files->car; + + /* Initialize. */ + shortest= NO_PATH; + for (rule= rules; rule != nil; rule= rule->next) + rule->path= nil; + + /* Try all possible transformation paths. */ + (void) findpath(0L, 0, file, nil); + + if (shortest == NO_PATH) { /* Can't match the file. */ + fprintf(stderr, + "%s: %s: can't compile, no transformation applies\n", + program, file->name); + action= 0; + return; + } + + /* Find the first short path. */ + if ((rule= findpath(0L, 1, file, nil)) == nil) return; + + /* Transform the file until you hit a combine. */ + t= inc(newcell()); + t->car= inc(file); + L_files= go(L_files, L_files->cdr); + t->cdr= rule->wait; + rule->wait= t; + while (action != 0 && rule != nil && rule->type != COMBINE) { + transform(rule); + rule= rule->path; + } + } + + /* All input files have been transformed to combine rule(s). Now + * we need to find the combine rule with the least number of paths + * running through it (this combine may be followed by another) and + * transform from there. + */ + while (action != 0) { + int least; + rule_t *comb= nil; + + for (rule= rules; rule != nil; rule= rule->next) { + rule->path= nil; + + if (rule->type != COMBINE || rule->wait == nil) + continue; + + if (comb == nil || rule->npaths < least) { + least= rule->npaths; + comb= rule; + } + } + + /* No combine? Then we're done. */ + if (comb == nil) break; + + /* Initialize. */ + shortest= NO_PATH; + + /* Try all possible transformation paths. */ + (void) findpath(0L, 0, nil, comb); + + if (shortest == NO_PATH) break; + + /* Find the first short path. */ + if ((rule= findpath(0L, 1, nil, comb)) == nil) return; + + /* Transform until you hit another combine. */ + do { + transform(rule); + rule= rule->path; + } while (action != 0 && rule != nil && rule->type != COMBINE); + } + phase= INIT; +} + +cell_t *predef(char *var, char *val) +/* A predefined variable var with value val, or a special variable. */ +{ + cell_t *p, *t; + + p= findword(var); + if (val != nil) { /* Predefined. */ + t= findword(val); + dec(p->value); + p->value= t; + p->flags|= W_SET; + if (verbose >= 3) { + prin1(p); + printf(" =\b=\b= "); + prin2n(t); + } + } else { /* Special: $* and such. */ + p->flags= W_SET|W_LOCAL|W_RDONLY; + } + t= inc(newcell()); + t->car= p; + t->cdr= L_predef; + L_predef= t; + return p; +} + +void usage(void) +{ + fprintf(stderr, + "Usage: %s -v<n> -vn<n> -name <name> -descr <descr> -T <dir> ...\n", + program); + exit(-1); +} + +int main(int argc, char **argv) +{ + char *tmpdir; + program_t *prog; + cell_t **pa; + int i; + + /* Call name of the program, decides which description to use. */ + if ((program= strrchr(argv[0], '/')) == nil) + program= argv[0]; + else + program++; + + /* Directory for temporary files. */ + if ((tmpdir= getenv("TMPDIR")) == nil || *tmpdir == 0) + tmpdir= "/tmp"; + + /* Transform arguments to a list, processing the few ACD options. */ + pa= &L_args; + for (i= 1; i < argc; i++) { + if (argv[i][0] == '-' && argv[i][1] == 'v') { + char *a= argv[i]+2; + + if (*a == 'n') { a++; action= 1; } + verbose= 2; + + if (*a != 0) { + verbose= strtoul(a, &a, 10); + if (*a != 0) usage(); + } + } else + if (strcmp(argv[i], "-name") == 0) { + if (++i == argc) usage(); + program= argv[i]; + } else + if (strcmp(argv[i], "-descr") == 0) { + if (++i == argc) usage(); + descr= argv[i]; + } else + if (argv[i][0] == '-' && argv[i][1] == 'T') { + if (argv[i][2] == 0) { + if (++i == argc) usage(); + tmpdir= argv[i]; + } else + tmpdir= argv[i]+2; + } else { + /* Any other argument must be processed. */ + *pa= cons(CELL, findword(argv[i])); + pa= &(*pa)->cdr; + } + } +#ifndef DESCR + /* Default description file is based on the program name. */ + if (descr == nil) descr= program; +#else + /* Default description file is predefined. */ + if (descr == nil) descr= DESCR; +#endif + + inittemp(tmpdir); + + /* Catch user signals. */ + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) signal(SIGHUP, interrupt); + if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, interrupt); + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) signal(SIGTERM, interrupt); + + /* Predefined or special variables. */ + predef("PROGRAM", program); + predef("VERSION", version); +#ifdef ARCH + predef("ARCH", ARCH); /* Cross-compilers like this. */ +#endif + V_star= predef("*", nil); + V_in= predef("<", nil); + V_out= predef(">", nil); + + /* Read the description file. */ + if (verbose >= 3) printf("include %s\n", descr); + prog= get_prog(); + + phase= INIT; + pc= prog; + execute(DOIT, 0); + + argscan(); + compile(); + + /* Delete all allocated data to test inc/dec balance. */ + while (prog != nil) { + program_t *junk= prog; + prog= junk->next; + dec(junk->file); + dec(junk->line); + deallocate(junk); + } + while (rules != nil) { + rule_t *junk= rules; + rules= junk->next; + dec(junk->from); + dec(junk->to); + dec(junk->wait); + deallocate(junk); + } + deltemp(); + dec(V_stop); + dec(L_args); + dec(L_files); + dec(L_predef); + + quit(action == 0 ? 1 : 0); +} diff --git a/commands/i386/asmconv/Makefile b/commands/i386/asmconv/Makefile new file mode 100755 index 000000000..9cf02da4b --- /dev/null +++ b/commands/i386/asmconv/Makefile @@ -0,0 +1,31 @@ +# Makefile for asmconv. + +CFLAGS= $(OPT) +LDFLAGS= -i + +all: asmconv + +OBJ= asm86.o asmconv.o parse_ack.o parse_gnu.o parse_bas.o \ + tokenize.o emit_ack.o emit_gnu.o + +asmconv: $(OBJ) + $(CC) $(LDFLAGS) -o $@ $(OBJ) + install -S 8kw $@ + +install: /usr/lib/asmconv + +/usr/lib/asmconv: asmconv + install -cs -o bin asmconv $@ + +clean: + rm -f $(OBJ) asmconv core + +# Dependencies. +asm86.o: asm86.h asmconv.h token.h +asmconv.o: asmconv.h languages.h asm86.h +parse_ack.o: asmconv.h languages.h token.h asm86.h +parse_gnu.o: asmconv.h languages.h token.h asm86.h +parse_bas.o: asmconv.h languages.h token.h asm86.h +tokenize.o: asmconv.h token.h +emit_ack.o: asmconv.h languages.h token.h asm86.h +emit_gnu.o: asmconv.h languages.h token.h asm86.h diff --git a/commands/i386/asmconv/asm86.c b/commands/i386/asmconv/asm86.c new file mode 100755 index 000000000..f0850e50a --- /dev/null +++ b/commands/i386/asmconv/asm86.c @@ -0,0 +1,85 @@ +/* asm86.c - 80X86 assembly intermediate Author: Kees J. Bot + * 24 Dec 1993 + */ +#define nil 0 +#include <stddef.h> +#include <string.h> +#include <assert.h> +#include "asm86.h" +#include "asmconv.h" +#include "token.h" + +expression_t *new_expr(void) +/* Make a new cell to build an expression. */ +{ + expression_t *e; + + e= allocate(nil, sizeof(*e)); + e->operator= -1; + e->left= e->middle= e->right= nil; + e->name= nil; + e->magic= 31624; + return e; +} + +void del_expr(expression_t *e) +/* Delete an expression tree. */ +{ + if (e != nil) { + assert(e->magic == 31624); + e->magic= 0; + deallocate(e->name); + del_expr(e->left); + del_expr(e->middle); + del_expr(e->right); + deallocate(e); + } +} + +asm86_t *new_asm86(void) +/* Make a new cell to hold an 80X86 instruction. */ +{ + asm86_t *a; + + a= allocate(nil, sizeof(*a)); + a->opcode= -1; + get_file(&a->file, &a->line); + a->optype= -1; + a->oaz= 0; + a->rep= ONCE; + a->seg= DEFSEG; + a->args= nil; + a->magic= 37937; + return a; +} + +void del_asm86(asm86_t *a) +/* Delete an 80X86 instruction. */ +{ + assert(a != nil); + assert(a->magic == 37937); + a->magic= 0; + del_expr(a->args); + deallocate(a); +} + +int isregister(const char *name) +/* True if the string is a register name. Return its size. */ +{ + static char *regs[] = { + "al", "bl", "cl", "dl", "ah", "bh", "ch", "dh", + "ax", "bx", "cx", "dx", "si", "di", "bp", "sp", + "cs", "ds", "es", "fs", "gs", "ss", + "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp", + "cr0", "cr1", "cr2", "cr3", + "st", + }; + int reg; + + for (reg= 0; reg < arraysize(regs); reg++) { + if (strcmp(name, regs[reg]) == 0) { + return reg < 8 ? 1 : reg < 22 ? 2 : 4; + } + } + return 0; +} diff --git a/commands/i386/asmconv/asm86.h b/commands/i386/asmconv/asm86.h new file mode 100755 index 000000000..a85abc4af --- /dev/null +++ b/commands/i386/asmconv/asm86.h @@ -0,0 +1,250 @@ +/* asm86.h - 80X86 assembly intermediate Author: Kees J. Bot + * 27 Jun 1993 + */ + +typedef enum opcode { /* 80486 opcodes, from the i486 reference manual. + * Synonyms left out, some new words invented. + */ + DOT_ALIGN, + DOT_ASCII, DOT_ASCIZ, + DOT_ASSERT, /* Pseudo's invented */ + DOT_BASE, + DOT_COMM, DOT_LCOMM, + DOT_DATA1, + DOT_DATA2, + DOT_DATA4, + DOT_DEFINE, DOT_EXTERN, + DOT_EQU, + DOT_FILE, DOT_LINE, + DOT_LABEL, + DOT_LIST, DOT_NOLIST, + DOT_SPACE, + DOT_SYMB, + DOT_TEXT, DOT_ROM, DOT_DATA, DOT_BSS, DOT_END, + DOT_USE16, DOT_USE32, + AAA, + AAD, + AAM, + AAS, + ADC, + ADD, + AND, + ARPL, + BOUND, + BSF, + BSR, + BSWAP, + BT, + BTC, + BTR, + BTS, + CALL, CALLF, /* CALLF added */ + CBW, + CLC, + CLD, + CLI, + CLTS, + CMC, + CMP, + CMPS, + CMPXCHG, + CWD, + DAA, + DAS, + DEC, + DIV, + ENTER, + F2XM1, + FABS, + FADD, FADDD, FADDS, FADDP, FIADDL, FIADDS, + FBLD, + FBSTP, + FCHS, + FCLEX, + FCOMD, FCOMS, FCOMPD, FCOMPS, FCOMPP, + FCOS, + FDECSTP, + FDIVD, FDIVS, FDIVP, FIDIVL, FIDIVS, + FDIVRD, FDIVRS, FDIVRP, FIDIVRL, FIDIVRS, + FFREE, + FICOM, FICOMP, + FILDQ, FILDL, FILDS, + FINCSTP, + FINIT, + FISTL, FISTS, FISTP, + FLDX, FLDD, FLDS, + FLD1, FLDL2T, FLDL2E, FLDPI, FLDLG2, FLDLN2, FLDZ, + FLDCW, + FLDENV, + FMULD, FMULS, FMULP, FIMULL, FIMULS, + FNOP, + FPATAN, + FPREM, + FPREM1, + FPTAN, + FRNDINT, + FRSTOR, + FSAVE, + FSCALE, + FSIN, + FSINCOS, + FSQRT, + FSTD, FSTS, FSTPX, FSTPD, FSTPS, + FSTCW, + FSTENV, + FSTSW, + FSUBD, FSUBS, FSUBP, FISUBL, FISUBS, + FSUBRD, FSUBRS, FSUBPR, FISUBRL, FISUBRS, + FTST, + FUCOM, FUCOMP, FUCOMPP, + FXAM, + FXCH, + FXTRACT, + FYL2X, + FYL2XP1, + HLT, + IDIV, + IMUL, + IN, + INC, + INS, + INT, INTO, + INVD, + INVLPG, + IRET, IRETD, + JA, JAE, JB, JBE, JCXZ, JE, JG, JGE, JL, + JLE, JNE, JNO, JNP, JNS, JO, JP, JS, + JMP, JMPF, /* JMPF added */ + LAHF, + LAR, + LEA, + LEAVE, + LGDT, LIDT, + LGS, LSS, LDS, LES, LFS, + LLDT, + LMSW, + LOCK, + LODS, + LOOP, LOOPE, LOOPNE, + LSL, + LTR, + MOV, + MOVS, + MOVSX, + MOVSXB, + MOVZX, + MOVZXB, + MUL, + NEG, + NOP, + NOT, + OR, + OUT, + OUTS, + POP, + POPA, + POPF, + PUSH, + PUSHA, + PUSHF, + RCL, RCR, ROL, ROR, + RET, RETF, /* RETF added */ + SAHF, + SAL, SAR, SHL, SHR, + SBB, + SCAS, + SETA, SETAE, SETB, SETBE, SETE, SETG, SETGE, SETL, + SETLE, SETNE, SETNO, SETNP, SETNS, SETO, SETP, SETS, + SGDT, SIDT, + SHLD, + SHRD, + SLDT, + SMSW, + STC, + STD, + STI, + STOS, + STR, + SUB, + TEST, + VERR, VERW, + WAIT, + WBINVD, + XADD, + XCHG, + XLAT, + XOR +} opcode_t; + +#define is_pseudo(o) ((o) <= DOT_USE32) +#define N_OPCODES ((int) XOR + 1) + +#define OPZ 0x01 /* Operand size prefix. */ +#define ADZ 0x02 /* Address size prefix. */ + +typedef enum optype { + PSEUDO, JUMP, BYTE, WORD, OWORD /* Ordered list! */ +} optype_t; + +typedef enum repeat { + ONCE, REP, REPE, REPNE +} repeat_t; + +typedef enum segment { + DEFSEG, CSEG, DSEG, ESEG, FSEG, GSEG, SSEG +} segment_t; + +typedef struct expression { + int operator; + struct expression *left, *middle, *right; + char *name; + size_t len; + unsigned magic; +} expression_t; + +typedef struct asm86 { + opcode_t opcode; /* DOT_TEXT, MOV, ... */ + char *file; /* Name of the file it is found in. */ + long line; /* Line number. */ + optype_t optype; /* Type of operands: byte, word... */ + int oaz; /* Operand/address size prefix? */ + repeat_t rep; /* Repeat prefix used on this instr. */ + segment_t seg; /* Segment override. */ + expression_t *args; /* Arguments in ACK order. */ + unsigned magic; +} asm86_t; + +expression_t *new_expr(void); +void del_expr(expression_t *a); +asm86_t *new_asm86(void); +void del_asm86(asm86_t *a); +int isregister(const char *name); + +/* + * Format of the arguments of the asm86_t structure: + * + * + * ACK assembly operands expression_t cell: + * or part of operand: {operator, left, middle, right, name, len} + * + * [expr] {'[', nil, expr, nil} + * word {'W', nil, nil, nil, word} + * "string" {'S', nil, nil, nil, "string", strlen("string")} + * label = expr {'=', nil, expr, nil, label} + * expr * expr {'*', expr, nil, expr} + * - expr {'-', nil, expr, nil} + * (memory) {'(', nil, memory, nil} + * offset(base)(index*n) {'O', offset, base, index*n} + * base {'B', nil, nil, nil, base} + * index*4 {'4', nil, nil, nil, index} + * operand, oplist {',', operand, nil, oplist} + * label : {':', nil, nil, nil, label} + * + * The precedence of operators is ignored. The expression is simply copied + * as is, including parentheses. Problems like missing operators in the + * target language will have to be handled by rewriting the source language. + * 16-bit or 32-bit registers must be used where they are required by the + * target assembler even though ACK makes no difference between 'ax' and + * 'eax'. Asmconv is smart enough to transform compiler output. Human made + * assembly can be fixed up to be transformable. + */ diff --git a/commands/i386/asmconv/asmconv.c b/commands/i386/asmconv/asmconv.c new file mode 100755 index 000000000..2ee3b7805 --- /dev/null +++ b/commands/i386/asmconv/asmconv.c @@ -0,0 +1,157 @@ +/* asmconv 1.11 - convert 80X86 assembly Author: Kees J. Bot + * 24 Dec 1993 + */ +static char version[] = "1.11"; + +#define nil 0 +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include "asmconv.h" +#include "asm86.h" +#include "languages.h" + +void fatal(char *label) +{ + fprintf(stderr, "asmconv: %s: %s\n", label, strerror(errno)); + exit(EXIT_FAILURE); +} + +void *allocate(void *mem, size_t size) +/* A checked malloc/realloc(). Yes, I know ISO C allows realloc(NULL, size). */ +{ + mem= mem == nil ? malloc(size) : realloc(mem, size); + if (mem == nil) fatal("malloc()"); + return mem; +} + +void deallocate(void *mem) +/* Free a malloc()d cell. (Yes I know ISO C allows free(NULL) */ +{ + if (mem != nil) free(mem); +} + +char *copystr(const char *s) +{ + char *c; + + c= allocate(nil, (strlen(s) + 1) * sizeof(s[0])); + strcpy(c, s); + return c; +} + +int isanumber(const char *s) +/* True if s can be turned into a number. */ +{ + char *end; + + (void) strtol(s, &end, 0); + return end != s && *end == 0; +} + +/* "Invisible" globals. */ +int asm_mode32= (sizeof(int) == 4); +int err_code= EXIT_SUCCESS; + +int main(int argc, char **argv) +{ + void (*parse_init)(char *file); + asm86_t *(*get_instruction)(void); + void (*emit_init)(char *file, const char *banner); + void (*emit_instruction)(asm86_t *instr); + char *lang_parse, *lang_emit, *input_file, *output_file; + asm86_t *instr; + char banner[80]; + + if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'm') { + if (strcmp(argv[1], "-mi86") == 0) { + set_use16(); + } else + if (strcmp(argv[1], "-mi386") == 0) { + set_use32(); + } else { + fprintf(stderr, "asmconv: '%s': unknown machine\n", + argv[1]+2); + } + argc--; + argv++; + } + + if (argc < 3 || argc > 5) { + fprintf(stderr, +"Usage: asmconv <input-type> <output-type> [input-file [output-file]]\n"); + exit(EXIT_FAILURE); + } + + lang_parse= argv[1]; + lang_emit= argv[2]; + input_file= argc < 4 ? nil : argv[3]; + output_file= argc < 5 ? nil : argv[4]; + + /* Choose the parsing routines. */ + if (strcmp(lang_parse, "ack") == 0) { + /* Standard ACK. */ + parse_init= ack_parse_init; + get_instruction= ack_get_instruction; + } else + if (strcmp(lang_parse, "ncc") == 0) { + /* ACK Xenix assembly, a black sheep among ACK assemblies. */ + parse_init= ncc_parse_init; + get_instruction= ncc_get_instruction; + } else + if (strcmp(lang_parse, "gnu") == 0) { + /* GNU assembly. Parser by R.S. Veldema. */ + parse_init= gnu_parse_init; + get_instruction= gnu_get_instruction; + } else + if (strcmp(lang_parse, "bas") == 0) { + /* Bruce Evans' assembler. */ + parse_init= bas_parse_init; + get_instruction= bas_get_instruction; + } else { + fprintf(stderr, "asmconv: '%s': unknown input language\n", + lang_parse); + exit(EXIT_FAILURE); + } + + /* Choose the output language. */ + if (strcmp(lang_emit, "ack") == 0) { + /* Standard ACK. */ + emit_init= ack_emit_init; + emit_instruction= ack_emit_instruction; + } else + if (strcmp(lang_emit, "ncc") == 0) { + /* ACK Xenix assembly, can be read by BAS and the 8086 ACK + * ANSI C compiler. (Allows us to compile the Boot Monitor.) + */ + emit_init= ncc_emit_init; + emit_instruction= ncc_emit_instruction; + } else + if (strcmp(lang_emit, "gnu") == 0) { + /* GNU assembler. So we can assemble the ACK stuff among the + * kernel sources and in the library. + */ + emit_init= gnu_emit_init; + emit_instruction= gnu_emit_instruction; + } else { + fprintf(stderr, "asmconv: '%s': unknown output language\n", + lang_emit); + exit(EXIT_FAILURE); + } + + sprintf(banner, "Translated from %s to %s by asmconv %s", + lang_parse, lang_emit, version); + + (*parse_init)(input_file); + (*emit_init)(output_file, banner); + for (;;) { + instr= (*get_instruction)(); + (*emit_instruction)(instr); + if (instr == nil) break; + del_asm86(instr); + } + exit(err_code); +} diff --git a/commands/i386/asmconv/asmconv.h b/commands/i386/asmconv/asmconv.h new file mode 100755 index 000000000..4ef2eaae2 --- /dev/null +++ b/commands/i386/asmconv/asmconv.h @@ -0,0 +1,24 @@ +/* asmconv.h - shared functions Author: Kees J. Bot + * 19 Dec 1993 + */ + +#define arraysize(a) (sizeof(a)/sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) +#define between(a, c, z) \ + ((unsigned)((c) - (a)) <= (unsigned)((z) - (a))) + +void *allocate(void *mem, size_t size); +void deallocate(void *mem); +void fatal(char *label); +char *copystr(const char *s); +int isanumber(const char *s); + +extern int asm_mode32; /* In 32 bit mode if true. */ + +#define use16() (!asm_mode32) +#define use32() ((int) asm_mode32) +#define set_use16() ((void) (asm_mode32= 0)) +#define set_use32() ((void) (asm_mode32= 1)) + +extern int err_code; /* Exit code. */ +#define set_error() ((void) (err_code= EXIT_FAILURE)) diff --git a/commands/i386/asmconv/emit_ack.c b/commands/i386/asmconv/emit_ack.c new file mode 100755 index 000000000..48f9c3988 --- /dev/null +++ b/commands/i386/asmconv/emit_ack.c @@ -0,0 +1,621 @@ +/* emit_ack.c - emit ACK assembly Author: Kees J. Bot + * emit NCC assembly 27 Dec 1993 + */ +#define nil 0 +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <assert.h> +#include "asmconv.h" +#include "token.h" +#include "asm86.h" +#include "languages.h" + +typedef struct mnemonic { /* ACK as86 mnemonics translation table. */ + opcode_t opcode; + char *name; +} mnemonic_t; + +static mnemonic_t mnemtab[] = { + { AAA, "aaa" }, + { AAD, "aad" }, + { AAM, "aam" }, + { AAS, "aas" }, + { ADC, "adc%" }, + { ADD, "add%" }, + { AND, "and%" }, + { ARPL, "arpl" }, + { BOUND, "bound" }, + { BSF, "bsf" }, + { BSR, "bsr" }, + { BSWAP, "bswap" }, + { BT, "bt" }, + { BTC, "btc" }, + { BTR, "btr" }, + { BTS, "bts" }, + { CALL, "call" }, + { CALLF, "callf" }, + { CBW, "cbw" }, + { CLC, "clc" }, + { CLD, "cld" }, + { CLI, "cli" }, + { CLTS, "clts" }, + { CMC, "cmc" }, + { CMP, "cmp%" }, + { CMPS, "cmps%" }, + { CMPXCHG, "cmpxchg" }, + { CWD, "cwd" }, + { DAA, "daa" }, + { DAS, "das" }, + { DEC, "dec%" }, + { DIV, "div%" }, + { DOT_ALIGN, ".align" }, + { DOT_ASCII, ".ascii" }, + { DOT_ASCIZ, ".asciz" }, + { DOT_ASSERT, ".assert" }, + { DOT_BASE, ".base" }, + { DOT_BSS, ".sect .bss" }, + { DOT_COMM, ".comm" }, + { DOT_DATA, ".sect .data" }, + { DOT_DATA1, ".data1" }, + { DOT_DATA2, ".data2" }, + { DOT_DATA4, ".data4" }, + { DOT_DEFINE, ".define" }, + { DOT_END, ".sect .end" }, + { DOT_EXTERN, ".extern" }, + { DOT_FILE, ".file" }, + { DOT_LCOMM, ".comm" }, + { DOT_LINE, ".line" }, + { DOT_LIST, ".list" }, + { DOT_NOLIST, ".nolist" }, + { DOT_ROM, ".sect .rom" }, + { DOT_SPACE, ".space" }, + { DOT_SYMB, ".symb" }, + { DOT_TEXT, ".sect .text" }, + { DOT_USE16, ".use16" }, + { DOT_USE32, ".use32" }, + { ENTER, "enter" }, + { F2XM1, "f2xm1" }, + { FABS, "fabs" }, + { FADD, "fadd" }, + { FADDD, "faddd" }, + { FADDP, "faddp" }, + { FADDS, "fadds" }, + { FBLD, "fbld" }, + { FBSTP, "fbstp" }, + { FCHS, "fchs" }, + { FCLEX, "fclex" }, + { FCOMD, "fcomd" }, + { FCOMPD, "fcompd" }, + { FCOMPP, "fcompp" }, + { FCOMPS, "fcomps" }, + { FCOMS, "fcoms" }, + { FCOS, "fcos" }, + { FDECSTP, "fdecstp" }, + { FDIVD, "fdivd" }, + { FDIVP, "fdivp" }, + { FDIVRD, "fdivrd" }, + { FDIVRP, "fdivrp" }, + { FDIVRS, "fdivrs" }, + { FDIVS, "fdivs" }, + { FFREE, "ffree" }, + { FIADDL, "fiaddl" }, + { FIADDS, "fiadds" }, + { FICOM, "ficom" }, + { FICOMP, "ficomp" }, + { FIDIVL, "fidivl" }, + { FIDIVRL, "fidivrl" }, + { FIDIVRS, "fidivrs" }, + { FIDIVS, "fidivs" }, + { FILDL, "fildl" }, + { FILDQ, "fildq" }, + { FILDS, "filds" }, + { FIMULL, "fimull" }, + { FIMULS, "fimuls" }, + { FINCSTP, "fincstp" }, + { FINIT, "finit" }, + { FISTL, "fistl" }, + { FISTP, "fistp" }, + { FISTS, "fists" }, + { FISUBL, "fisubl" }, + { FISUBRL, "fisubrl" }, + { FISUBRS, "fisubrs" }, + { FISUBS, "fisubs" }, + { FLD1, "fld1" }, + { FLDCW, "fldcw" }, + { FLDD, "fldd" }, + { FLDENV, "fldenv" }, + { FLDL2E, "fldl2e" }, + { FLDL2T, "fldl2t" }, + { FLDLG2, "fldlg2" }, + { FLDLN2, "fldln2" }, + { FLDPI, "fldpi" }, + { FLDS, "flds" }, + { FLDX, "fldx" }, + { FLDZ, "fldz" }, + { FMULD, "fmuld" }, + { FMULP, "fmulp" }, + { FMULS, "fmuls" }, + { FNOP, "fnop" }, + { FPATAN, "fpatan" }, + { FPREM, "fprem" }, + { FPREM1, "fprem1" }, + { FPTAN, "fptan" }, + { FRNDINT, "frndint" }, + { FRSTOR, "frstor" }, + { FSAVE, "fsave" }, + { FSCALE, "fscale" }, + { FSIN, "fsin" }, + { FSINCOS, "fsincos" }, + { FSQRT, "fsqrt" }, + { FSTCW, "fstcw" }, + { FSTD, "fstd" }, + { FSTENV, "fstenv" }, + { FSTPD, "fstpd" }, + { FSTPS, "fstps" }, + { FSTPX, "fstpx" }, + { FSTS, "fsts" }, + { FSTSW, "fstsw" }, + { FSUBD, "fsubd" }, + { FSUBP, "fsubp" }, + { FSUBPR, "fsubpr" }, + { FSUBRD, "fsubrd" }, + { FSUBRS, "fsubrs" }, + { FSUBS, "fsubs" }, + { FTST, "ftst" }, + { FUCOM, "fucom" }, + { FUCOMP, "fucomp" }, + { FUCOMPP, "fucompp" }, + { FXAM, "fxam" }, + { FXCH, "fxch" }, + { FXTRACT, "fxtract" }, + { FYL2X, "fyl2x" }, + { FYL2XP1, "fyl2xp1" }, + { HLT, "hlt" }, + { IDIV, "idiv%" }, + { IMUL, "imul%" }, + { IN, "in%" }, + { INC, "inc%" }, + { INS, "ins%" }, + { INT, "int" }, + { INTO, "into" }, + { INVD, "invd" }, + { INVLPG, "invlpg" }, + { IRET, "iret" }, + { IRETD, "iretd" }, + { JA, "ja" }, + { JAE, "jae" }, + { JB, "jb" }, + { JBE, "jbe" }, + { JCXZ, "jcxz" }, + { JE, "je" }, + { JG, "jg" }, + { JGE, "jge" }, + { JL, "jl" }, + { JLE, "jle" }, + { JMP, "jmp" }, + { JMPF, "jmpf" }, + { JNE, "jne" }, + { JNO, "jno" }, + { JNP, "jnp" }, + { JNS, "jns" }, + { JO, "jo" }, + { JP, "jp" }, + { JS, "js" }, + { LAHF, "lahf" }, + { LAR, "lar" }, + { LDS, "lds" }, + { LEA, "lea" }, + { LEAVE, "leave" }, + { LES, "les" }, + { LFS, "lfs" }, + { LGDT, "lgdt" }, + { LGS, "lgs" }, + { LIDT, "lidt" }, + { LLDT, "lldt" }, + { LMSW, "lmsw" }, + { LOCK, "lock" }, + { LODS, "lods%" }, + { LOOP, "loop" }, + { LOOPE, "loope" }, + { LOOPNE, "loopne" }, + { LSL, "lsl" }, + { LSS, "lss" }, + { LTR, "ltr" }, + { MOV, "mov%" }, + { MOVS, "movs%" }, + { MOVSX, "movsx" }, + { MOVSXB, "movsxb" }, + { MOVZX, "movzx" }, + { MOVZXB, "movzxb" }, + { MUL, "mul%" }, + { NEG, "neg%" }, + { NOP, "nop" }, + { NOT, "not%" }, + { OR, "or%" }, + { OUT, "out%" }, + { OUTS, "outs%" }, + { POP, "pop" }, + { POPA, "popa" }, + { POPF, "popf" }, + { PUSH, "push" }, + { PUSHA, "pusha" }, + { PUSHF, "pushf" }, + { RCL, "rcl%" }, + { RCR, "rcr%" }, + { RET, "ret" }, + { RETF, "retf" }, + { ROL, "rol%" }, + { ROR, "ror%" }, + { SAHF, "sahf" }, + { SAL, "sal%" }, + { SAR, "sar%" }, + { SBB, "sbb%" }, + { SCAS, "scas%" }, + { SETA, "seta" }, + { SETAE, "setae" }, + { SETB, "setb" }, + { SETBE, "setbe" }, + { SETE, "sete" }, + { SETG, "setg" }, + { SETGE, "setge" }, + { SETL, "setl" }, + { SETLE, "setle" }, + { SETNE, "setne" }, + { SETNO, "setno" }, + { SETNP, "setnp" }, + { SETNS, "setns" }, + { SETO, "seto" }, + { SETP, "setp" }, + { SETS, "sets" }, + { SGDT, "sgdt" }, + { SHL, "shl%" }, + { SHLD, "shld" }, + { SHR, "shr%" }, + { SHRD, "shrd" }, + { SIDT, "sidt" }, + { SLDT, "sldt" }, + { SMSW, "smsw" }, + { STC, "stc" }, + { STD, "std" }, + { STI, "sti" }, + { STOS, "stos%" }, + { STR, "str" }, + { SUB, "sub%" }, + { TEST, "test%" }, + { VERR, "verr" }, + { VERW, "verw" }, + { WAIT, "wait" }, + { WBINVD, "wbinvd" }, + { XADD, "xadd" }, + { XCHG, "xchg%" }, + { XLAT, "xlat" }, + { XOR, "xor%" }, +}; + +#define farjmp(o) ((o) == JMPF || (o) == CALLF) + +static FILE *ef; +static long eline= 1; +static char *efile; +static char *orig_efile; +static char *opcode2name_tab[N_OPCODES]; +static enum dialect { ACK, NCC } dialect= ACK; + +static void ack_putchar(int c) +/* LOOK, this programmer checks the return code of putc! What an idiot, noone + * does that! + */ +{ + if (putc(c, ef) == EOF) fatal(orig_efile); +} + +static void ack_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (vfprintf(ef, fmt, ap) == EOF) fatal(orig_efile); + va_end(ap); +} + +void ack_emit_init(char *file, const char *banner) +/* Prepare producing an ACK assembly file. */ +{ + mnemonic_t *mp; + + if (file == nil) { + file= "stdout"; + ef= stdout; + } else { + if ((ef= fopen(file, "w")) == nil) fatal(file); + } + orig_efile= file; + efile= file; + ack_printf("! %s", banner); + if (dialect == ACK) { + /* Declare the four sections used under Minix. */ + ack_printf( + "\n.sect .text; .sect .rom; .sect .data; .sect .bss\n.sect .text"); + } + + /* Initialize the opcode to mnemonic translation table. */ + for (mp= mnemtab; mp < arraylimit(mnemtab); mp++) { + assert(opcode2name_tab[mp->opcode] == nil); + opcode2name_tab[mp->opcode]= mp->name; + } +} + +#define opcode2name(op) (opcode2name_tab[op] + 0) + +static void ack_put_string(const char *s, size_t n) +/* Emit a string with weird characters quoted. */ +{ + while (n > 0) { + int c= *s; + + if (c < ' ' || c > 0177) { + ack_printf("\\%03o", c & 0xFF); + } else + if (c == '"' || c == '\\') { + ack_printf("\\%c", c); + } else { + ack_putchar(c); + } + s++; + n--; + } +} + +static void ack_put_expression(asm86_t *a, expression_t *e, int deref) +/* Send an expression, i.e. instruction operands, to the output file. Deref + * is true when the rewrite for the ncc dialect may be made. + */ +{ + assert(e != nil); + + switch (e->operator) { + case ',': + if (dialect == NCC && farjmp(a->opcode)) { + /* ACK jmpf seg:off -> NCC jmpf off,seg */ + ack_put_expression(a, e->right, deref); + ack_printf(", "); + ack_put_expression(a, e->left, deref); + } else { + ack_put_expression(a, e->left, deref); + ack_printf(farjmp(a->opcode) ? ":" : ", "); + ack_put_expression(a, e->right, deref); + } + break; + case 'O': + if (deref && a->optype == JUMP) ack_putchar('@'); + if (e->left != nil) ack_put_expression(a, e->left, 0); + if (e->middle != nil) ack_put_expression(a, e->middle, 0); + if (e->right != nil) ack_put_expression(a, e->right, 0); + break; + case '(': + if (deref && a->optype == JUMP) ack_putchar('@'); + if (!deref) ack_putchar('('); + ack_put_expression(a, e->middle, 0); + if (!deref) ack_putchar(')'); + break; + case 'B': + ack_printf("(%s)", e->name); + break; + case '1': + case '2': + case '4': + case '8': + ack_printf((use16() && e->operator == '1') + ? "(%s)" : "(%s*%c)", e->name, e->operator); + break; + case '+': + case '-': + case '~': + if (e->middle != nil) { + if (deref && a->optype != JUMP) ack_putchar('#'); + ack_putchar(e->operator); + ack_put_expression(a, e->middle, 0); + break; + } + /*FALL THROUGH*/ + case '*': + case '/': + case '%': + case '&': + case '|': + case '^': + case S_LEFTSHIFT: + case S_RIGHTSHIFT: + if (deref && a->optype != JUMP) ack_putchar('#'); + ack_put_expression(a, e->left, 0); + if (e->operator == S_LEFTSHIFT) { + ack_printf("<<"); + } else + if (e->operator == S_RIGHTSHIFT) { + ack_printf(">>"); + } else { + ack_putchar(e->operator); + } + ack_put_expression(a, e->right, 0); + break; + case '[': + if (deref && a->optype != JUMP) ack_putchar('#'); + ack_putchar('['); + ack_put_expression(a, e->middle, 0); + ack_putchar(']'); + break; + case 'W': + if (deref && a->optype == JUMP && isregister(e->name)) + { + ack_printf("(%s)", e->name); + break; + } + if (deref && a->optype != JUMP && !isregister(e->name)) { + ack_putchar('#'); + } + ack_printf("%s", e->name); + break; + case 'S': + ack_putchar('"'); + ack_put_string(e->name, e->len); + ack_putchar('"'); + break; + default: + fprintf(stderr, + "asmconv: internal error, unknown expression operator '%d'\n", + e->operator); + exit(EXIT_FAILURE); + } +} + +void ack_emit_instruction(asm86_t *a) +/* Output one instruction and its operands. */ +{ + int same= 0; + char *p; + static int high_seg; + int deref; + + if (a == nil) { + /* Last call */ + ack_putchar('\n'); + return; + } + + /* Make sure the line number of the line to be emitted is ok. */ + if ((a->file != efile && strcmp(a->file, efile) != 0) + || a->line < eline || a->line > eline+10) { + ack_putchar('\n'); + ack_printf("# %ld \"%s\"\n", a->line, a->file); + efile= a->file; + eline= a->line; + } else { + if (a->line == eline) { + ack_printf("; "); + same= 1; + } + while (eline < a->line) { + ack_putchar('\n'); + eline++; + } + } + + if (a->opcode == DOT_LABEL) { + assert(a->args->operator == ':'); + ack_printf("%s:", a->args->name); + } else + if (a->opcode == DOT_EQU) { + assert(a->args->operator == '='); + ack_printf("\t%s = ", a->args->name); + ack_put_expression(a, a->args->middle, 0); + } else + if ((p= opcode2name(a->opcode)) != nil) { + char *sep= dialect == ACK ? "" : ";"; + + if (!is_pseudo(a->opcode) && !same) ack_putchar('\t'); + + switch (a->rep) { + case ONCE: break; + case REP: ack_printf("rep"); break; + case REPE: ack_printf("repe"); break; + case REPNE: ack_printf("repne"); break; + default: assert(0); + } + if (a->rep != ONCE) { + ack_printf(dialect == ACK ? " " : "; "); + } + switch (a->seg) { + case DEFSEG: break; + case CSEG: ack_printf("cseg"); break; + case DSEG: ack_printf("dseg"); break; + case ESEG: ack_printf("eseg"); break; + case FSEG: ack_printf("fseg"); break; + case GSEG: ack_printf("gseg"); break; + case SSEG: ack_printf("sseg"); break; + default: assert(0); + } + if (a->seg != DEFSEG) { + ack_printf(dialect == ACK ? " " : "; "); + } + if (a->oaz & OPZ) ack_printf(use16() ? "o32 " : "o16 "); + if (a->oaz & ADZ) ack_printf(use16() ? "a32 " : "a16 "); + + if (a->opcode == CBW) { + p= !(a->oaz & OPZ) == use16() ? "cbw" : "cwde"; + } + + if (a->opcode == CWD) { + p= !(a->oaz & OPZ) == use16() ? "cwd" : "cdq"; + } + + if (a->opcode == DOT_COMM && a->args != nil + && a->args->operator == ',' + && a->args->left->operator == 'W' + ) { + ack_printf(".define\t%s; ", a->args->left->name); + } + while (*p != 0) { + if (*p == '%') { + if (a->optype == BYTE) ack_putchar('b'); + } else { + ack_putchar(*p); + } + p++; + } + if (a->args != nil) { + ack_putchar('\t'); + switch (a->opcode) { + case IN: + case OUT: + case INT: + deref= 0; + break; + default: + deref= (dialect == NCC && a->optype != PSEUDO); + } + ack_put_expression(a, a->args, deref); + } + if (a->opcode == DOT_USE16) set_use16(); + if (a->opcode == DOT_USE32) set_use32(); + } else { + fprintf(stderr, + "asmconv: internal error, unknown opcode '%d'\n", + a->opcode); + exit(EXIT_FAILURE); + } +} + +/* A few ncc mnemonics are different. */ +static mnemonic_t ncc_mnemtab[] = { + { DOT_BSS, ".bss" }, + { DOT_DATA, ".data" }, + { DOT_END, ".end" }, + { DOT_ROM, ".rom" }, + { DOT_TEXT, ".text" }, +}; + +void ncc_emit_init(char *file, const char *banner) +/* The assembly produced by the Minix ACK ANSI C compiler for the 8086 is + * different from the normal ACK assembly, and different from the old K&R + * assembler. This brings us endless joy. (It was supposed to make + * translation of the assembly used by the old K&R assembler easier by + * not deviating too much from that dialect.) + */ +{ + mnemonic_t *mp; + + dialect= NCC; + ack_emit_init(file, banner); + + /* Replace a few mnemonics. */ + for (mp= ncc_mnemtab; mp < arraylimit(ncc_mnemtab); mp++) { + opcode2name_tab[mp->opcode]= mp->name; + } +} + +void ncc_emit_instruction(asm86_t *a) +{ + ack_emit_instruction(a); +} diff --git a/commands/i386/asmconv/emit_gnu.c b/commands/i386/asmconv/emit_gnu.c new file mode 100755 index 000000000..7456aff8f --- /dev/null +++ b/commands/i386/asmconv/emit_gnu.c @@ -0,0 +1,674 @@ +/* emit_gnu.c - emit GNU assembly Author: Kees J. Bot + * 28 Dec 1993 + */ +#define nil 0 +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <assert.h> +#include "asmconv.h" +#include "token.h" +#include "asm86.h" +#include "languages.h" + +typedef struct mnemonic { /* GNU as386 mnemonics translation table. */ + opcode_t opcode; + char *name; +} mnemonic_t; + +static mnemonic_t mnemtab[] = { + { AAA, "aaa" }, + { AAD, "aad" }, + { AAM, "aam" }, + { AAS, "aas" }, + { ADC, "adc%" }, + { ADD, "add%" }, + { AND, "and%" }, + { ARPL, "arpl" }, + { BOUND, "bound%" }, + { BSF, "bsf%" }, + { BSR, "bsr%" }, + { BSWAP, "bswap" }, + { BT, "bt%" }, + { BTC, "btc%" }, + { BTR, "btr%" }, + { BTS, "bts%" }, + { CALL, "call" }, + { CALLF, "lcall" }, + { CBW, "cbtw" }, + { CLC, "clc" }, + { CLD, "cld" }, + { CLI, "cli" }, + { CLTS, "clts" }, + { CMC, "cmc" }, + { CMP, "cmp%" }, + { CMPS, "cmps%" }, + { CMPXCHG, "cmpxchg" }, + { CWD, "cwtd" }, + { DAA, "daa" }, + { DAS, "das" }, + { DEC, "dec%" }, + { DIV, "div%" }, + { DOT_ALIGN, ".align" }, + { DOT_ASCII, ".ascii" }, + { DOT_ASCIZ, ".asciz" }, + { DOT_ASSERT, ".assert" }, + { DOT_BASE, ".base" }, + { DOT_BSS, ".bss" }, + { DOT_COMM, ".comm" }, + { DOT_DATA, ".data" }, + { DOT_DATA1, ".byte" }, + { DOT_DATA2, ".short" }, + { DOT_DATA4, ".long" }, + { DOT_DEFINE, ".globl" }, + { DOT_EXTERN, ".globl" }, + { DOT_FILE, ".file" }, + { DOT_LCOMM, ".lcomm" }, + { DOT_LINE, ".line" }, + { DOT_LIST, ".list" }, + { DOT_NOLIST, ".nolist" }, + { DOT_ROM, ".data" }, /* Minix -- separate I&D. */ + { DOT_SPACE, ".space" }, + { DOT_SYMB, ".symb" }, + { DOT_TEXT, ".text" }, + { DOT_USE16, ".use16" }, + { DOT_USE32, ".use32" }, + { ENTER, "enter" }, + { F2XM1, "f2xm1" }, + { FABS, "fabs" }, + { FADD, "fadd" }, + { FADDD, "faddl" }, + { FADDP, "faddp" }, + { FADDS, "fadds" }, + { FBLD, "fbld" }, + { FBSTP, "fbstp" }, + { FCHS, "fchs" }, + { FCLEX, "fnclex" }, + { FCOMD, "fcoml" }, + { FCOMPD, "fcompl" }, + { FCOMPP, "fcompp" }, + { FCOMPS, "fcomps" }, + { FCOMS, "fcoms" }, + { FCOS, "fcos" }, + { FDECSTP, "fdecstp" }, + { FDIVD, "fdivl" }, + { FDIVP, "fdivp" }, + { FDIVRD, "fdivrl" }, + { FDIVRP, "fdivrp" }, + { FDIVRS, "fdivrs" }, + { FDIVS, "fdivs" }, + { FFREE, "ffree" }, + { FIADDL, "fiaddl" }, + { FIADDS, "fiadds" }, + { FICOM, "ficom" }, + { FICOMP, "ficomp" }, + { FIDIVL, "fidivl" }, + { FIDIVRL, "fidivrl" }, + { FIDIVRS, "fidivrs" }, + { FIDIVS, "fidivs" }, + { FILDL, "fildl" }, + { FILDQ, "fildq" }, + { FILDS, "filds" }, + { FIMULL, "fimull" }, + { FIMULS, "fimuls" }, + { FINCSTP, "fincstp" }, + { FINIT, "fninit" }, + { FISTL, "fistl" }, + { FISTP, "fistp" }, + { FISTS, "fists" }, + { FISUBL, "fisubl" }, + { FISUBRL, "fisubrl" }, + { FISUBRS, "fisubrs" }, + { FISUBS, "fisubs" }, + { FLD1, "fld1" }, + { FLDCW, "fldcw" }, + { FLDD, "fldl" }, + { FLDENV, "fldenv" }, + { FLDL2E, "fldl2e" }, + { FLDL2T, "fldl2t" }, + { FLDLG2, "fldlg2" }, + { FLDLN2, "fldln2" }, + { FLDPI, "fldpi" }, + { FLDS, "flds" }, + { FLDX, "fldt" }, + { FLDZ, "fldz" }, + { FMULD, "fmull" }, + { FMULP, "fmulp" }, + { FMULS, "fmuls" }, + { FNOP, "fnop" }, + { FPATAN, "fpatan" }, + { FPREM, "fprem" }, + { FPREM1, "fprem1" }, + { FPTAN, "fptan" }, + { FRNDINT, "frndint" }, + { FRSTOR, "frstor" }, + { FSAVE, "fnsave" }, + { FSCALE, "fscale" }, + { FSIN, "fsin" }, + { FSINCOS, "fsincos" }, + { FSQRT, "fsqrt" }, + { FSTCW, "fnstcw" }, + { FSTD, "fstl" }, + { FSTENV, "fnstenv" }, + { FSTPD, "fstpl" }, + { FSTPS, "fstps" }, + { FSTPX, "fstpt" }, + { FSTS, "fsts" }, + { FSTSW, "fstsw" }, + { FSUBD, "fsubl" }, + { FSUBP, "fsubp" }, + { FSUBPR, "fsubpr" }, + { FSUBRD, "fsubrl" }, + { FSUBRS, "fsubrs" }, + { FSUBS, "fsubs" }, + { FTST, "ftst" }, + { FUCOM, "fucom" }, + { FUCOMP, "fucomp" }, + { FUCOMPP, "fucompp" }, + { FXAM, "fxam" }, + { FXCH, "fxch" }, + { FXTRACT, "fxtract" }, + { FYL2X, "fyl2x" }, + { FYL2XP1, "fyl2xp1" }, + { HLT, "hlt" }, + { IDIV, "idiv%" }, + { IMUL, "imul%" }, + { IN, "in%" }, + { INC, "inc%" }, + { INS, "ins%" }, + { INT, "int" }, + { INTO, "into" }, + { INVD, "invd" }, + { INVLPG, "invlpg" }, + { IRET, "iret" }, + { IRETD, "iret" }, + { JA, "ja" }, + { JAE, "jae" }, + { JB, "jb" }, + { JBE, "jbe" }, + { JCXZ, "jcxz" }, + { JE, "je" }, + { JG, "jg" }, + { JGE, "jge" }, + { JL, "jl" }, + { JLE, "jle" }, + { JMP, "jmp" }, + { JMPF, "ljmp" }, + { JNE, "jne" }, + { JNO, "jno" }, + { JNP, "jnp" }, + { JNS, "jns" }, + { JO, "jo" }, + { JP, "jp" }, + { JS, "js" }, + { LAHF, "lahf" }, + { LAR, "lar" }, + { LDS, "lds" }, + { LEA, "lea%" }, + { LEAVE, "leave" }, + { LES, "les" }, + { LFS, "lfs" }, + { LGDT, "lgdt" }, + { LGS, "lgs" }, + { LIDT, "lidt" }, + { LLDT, "lldt" }, + { LMSW, "lmsw" }, + { LOCK, "lock" }, + { LODS, "lods%" }, + { LOOP, "loop" }, + { LOOPE, "loope" }, + { LOOPNE, "loopne" }, + { LSL, "lsl" }, + { LSS, "lss" }, + { LTR, "ltr" }, + { MOV, "mov%" }, + { MOVS, "movs%" }, + { MOVSX, "movswl" }, + { MOVSXB, "movsb%" }, + { MOVZX, "movzwl" }, + { MOVZXB, "movzb%" }, + { MUL, "mul%" }, + { NEG, "neg%" }, + { NOP, "nop" }, + { NOT, "not%" }, + { OR, "or%" }, + { OUT, "out%" }, + { OUTS, "outs%" }, + { POP, "pop%" }, + { POPA, "popa%" }, + { POPF, "popf%" }, + { PUSH, "push%" }, + { PUSHA, "pusha%" }, + { PUSHF, "pushf%" }, + { RCL, "rcl%" }, + { RCR, "rcr%" }, + { RET, "ret" }, + { RETF, "lret" }, + { ROL, "rol%" }, + { ROR, "ror%" }, + { SAHF, "sahf" }, + { SAL, "sal%" }, + { SAR, "sar%" }, + { SBB, "sbb%" }, + { SCAS, "scas%" }, + { SETA, "setab" }, + { SETAE, "setaeb" }, + { SETB, "setbb" }, + { SETBE, "setbeb" }, + { SETE, "seteb" }, + { SETG, "setgb" }, + { SETGE, "setgeb" }, + { SETL, "setlb" }, + { SETLE, "setleb" }, + { SETNE, "setneb" }, + { SETNO, "setnob" }, + { SETNP, "setnpb" }, + { SETNS, "setnsb" }, + { SETO, "setob" }, + { SETP, "setpb" }, + { SETS, "setsb" }, + { SGDT, "sgdt" }, + { SHL, "shl%" }, + { SHLD, "shld%" }, + { SHR, "shr%" }, + { SHRD, "shrd%" }, + { SIDT, "sidt" }, + { SLDT, "sldt" }, + { SMSW, "smsw" }, + { STC, "stc" }, + { STD, "std" }, + { STI, "sti" }, + { STOS, "stos%" }, + { STR, "str" }, + { SUB, "sub%" }, + { TEST, "test%" }, + { VERR, "verr" }, + { VERW, "verw" }, + { WAIT, "wait" }, + { WBINVD, "wbinvd" }, + { XADD, "xadd" }, + { XCHG, "xchg%" }, + { XLAT, "xlat" }, + { XOR, "xor%" }, +}; + +static FILE *ef; +static long eline= 1; +static char *efile; +static char *orig_efile; +static char *opcode2name_tab[N_OPCODES]; + +static void gnu_putchar(int c) +/* LOOK, this programmer checks the return code of putc! What an idiot, noone + * does that! + */ +{ + if (putc(c, ef) == EOF) fatal(orig_efile); +} + +static void gnu_printf(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + if (vfprintf(ef, fmt, ap) == EOF) fatal(orig_efile); + va_end(ap); +} + +void gnu_emit_init(char *file, const char *banner) +/* Prepare producing a GNU assembly file. */ +{ + mnemonic_t *mp; + + if (file == nil) { + file= "stdout"; + ef= stdout; + } else { + if ((ef= fopen(file, "w")) == nil) fatal(file); + } + orig_efile= file; + efile= file; + gnu_printf("/ %s", banner); + + /* Initialize the opcode to mnemonic translation table. */ + for (mp= mnemtab; mp < arraylimit(mnemtab); mp++) { + assert(opcode2name_tab[mp->opcode] == nil); + opcode2name_tab[mp->opcode]= mp->name; + } +} + +#define opcode2name(op) (opcode2name_tab[op] + 0) + +static void gnu_put_string(const char *s, size_t n) +/* Emit a string with weird characters quoted. */ +{ + while (n > 0) { + int c= *s; + + if (c < ' ' || c > 0177) { + gnu_printf("\\%03o", c); + } else + if (c == '"' || c == '\\') { + gnu_printf("\\%c", c & 0xFF); + } else { + gnu_putchar(c); + } + s++; + n--; + } +} + +static void gnu_put_expression(asm86_t *a, expression_t *e, int deref) +/* Send an expression, i.e. instruction operands, to the output file. Deref + * is true when the rewrite of "x" -> "#x" or "(x)" -> "x" may be made. + */ +{ + assert(e != nil); + + switch (e->operator) { + case ',': + if (is_pseudo(a->opcode)) { + /* Pseudo's are normal. */ + gnu_put_expression(a, e->left, deref); + gnu_printf(", "); + gnu_put_expression(a, e->right, deref); + } else { + /* He who invented GNU assembly has seen one VAX too + * many, operands are given in the wrong order. This + * makes coding from an Intel databook a real delight. + * A good thing this program allows us to write the + * more normal ACK assembly. + */ + gnu_put_expression(a, e->right, deref); + gnu_printf(", "); + gnu_put_expression(a, e->left, deref); + } + break; + case 'O': + if (deref && a->optype == JUMP) gnu_putchar('*'); + if (e->left != nil) gnu_put_expression(a, e->left, 0); + gnu_putchar('('); + if (e->middle != nil) gnu_put_expression(a, e->middle, 0); + if (e->right != nil) { + gnu_putchar(','); + gnu_put_expression(a, e->right, 0); + } + gnu_putchar(')'); + break; + case '(': + if (!deref) gnu_putchar('('); + if (deref && a->optype == JUMP) gnu_putchar('*'); + gnu_put_expression(a, e->middle, 0); + if (!deref) gnu_putchar(')'); + break; + case 'B': + gnu_printf("%%%s", e->name); + break; + case '1': + case '2': + case '4': + case '8': + gnu_printf("%%%s,%c", e->name, e->operator); + break; + case '+': + case '-': + case '~': + if (e->middle != nil) { + if (deref && a->optype >= BYTE) gnu_putchar('$'); + gnu_putchar(e->operator); + gnu_put_expression(a, e->middle, 0); + break; + } + /*FALL THROUGH*/ + case '*': + case '/': + case '%': + case '&': + case '|': + case '^': + case S_LEFTSHIFT: + case S_RIGHTSHIFT: + if (deref && a->optype >= BYTE) gnu_putchar('$'); + gnu_put_expression(a, e->left, 0); + if (e->operator == S_LEFTSHIFT) { + gnu_printf("<<"); + } else + if (e->operator == S_RIGHTSHIFT) { + gnu_printf(">>"); + } else { + gnu_putchar(e->operator); + } + gnu_put_expression(a, e->right, 0); + break; + case '[': + if (deref && a->optype >= BYTE) gnu_putchar('$'); + gnu_putchar('('); + gnu_put_expression(a, e->middle, 0); + gnu_putchar(')'); + break; + case 'W': + if (isregister(e->name)) { + if (a->optype == JUMP) gnu_putchar('*'); + gnu_printf("%%%s", e->name); + } else { + if (deref && a->optype >= BYTE) gnu_putchar('$'); + gnu_printf("%s", e->name); + } + break; + case 'S': + gnu_putchar('"'); + gnu_put_string(e->name, e->len); + gnu_putchar('"'); + break; + default: + fprintf(stderr, + "asmconv: internal error, unknown expression operator '%d'\n", + e->operator); + exit(EXIT_FAILURE); + } +} + +void gnu_emit_instruction(asm86_t *a) +/* Output one instruction and its operands. */ +{ + int same= 0; + char *p; + + if (a == nil) { + /* Last call */ + gnu_putchar('\n'); + return; + } + + if (use16()) { + fprintf(stderr, + "asmconv: the GNU assembler can't translate 8086 code\n"); + exit(EXIT_FAILURE); + } + + /* Make sure the line number of the line to be emitted is ok. */ + if ((a->file != efile && strcmp(a->file, efile) != 0) + || a->line < eline || a->line > eline+10) { + gnu_putchar('\n'); + gnu_printf("# %ld \"%s\"\n", a->line, a->file); + efile= a->file; + eline= a->line; + } else { + if (a->line == eline) { + gnu_printf("; "); + same= 1; + } + while (eline < a->line) { + gnu_putchar('\n'); + eline++; + } + } + + if (a->opcode == DOT_LABEL) { + assert(a->args->operator == ':'); + gnu_printf("%s:", a->args->name); + } else + if (a->opcode == DOT_EQU) { + assert(a->args->operator == '='); + gnu_printf("\t%s = ", a->args->name); + gnu_put_expression(a, a->args->middle, 0); + } else + if (a->opcode == DOT_ALIGN) { + /* GNU .align thinks in powers of two. */ + unsigned long n; + unsigned s; + + assert(a->args->operator == 'W' && isanumber(a->args->name)); + n= strtoul(a->args->name, nil, 0); + for (s= 0; s <= 4 && (1 << s) < n; s++) {} + gnu_printf(".align\t%u", s); + } else + if ((p= opcode2name(a->opcode)) != nil) { + if (!is_pseudo(a->opcode) && !same) gnu_putchar('\t'); + + switch (a->rep) { + case ONCE: break; + case REP: gnu_printf("rep; "); break; + case REPE: gnu_printf("repe; "); break; + case REPNE: gnu_printf("repne; "); break; + default: assert(0); + } + switch (a->seg) { + /* Kludge to avoid knowing where to put the "%es:" */ + case DEFSEG: break; + case CSEG: gnu_printf(".byte 0x2e; "); break; + case DSEG: gnu_printf(".byte 0x3e; "); break; + case ESEG: gnu_printf(".byte 0x26; "); break; + case FSEG: gnu_printf(".byte 0x64; "); break; + case GSEG: gnu_printf(".byte 0x65; "); break; + case SSEG: gnu_printf(".byte 0x36; "); break; + default: assert(0); + } + + /* Exceptions, exceptions... */ + if (a->opcode == CBW) { + if (!(a->oaz & OPZ)) p= "cwtl"; + a->oaz&= ~OPZ; + } + if (a->opcode == CWD) { + if (!(a->oaz & OPZ)) p= "cltd"; + a->oaz&= ~OPZ; + } + + if (a->opcode == RET || a->opcode == RETF) { + /* Argument of RET needs a '$'. */ + a->optype= WORD; + } + + if (a->opcode == MUL && a->args != nil + && a->args->operator == ',') { + /* Two operand MUL is an IMUL? */ + p="imul%"; + } + + /* GAS doesn't understand the interesting combinations. */ + if (a->oaz & ADZ) gnu_printf(".byte 0x67; "); + if (a->oaz & OPZ && strchr(p, '%') == nil) + gnu_printf(".byte 0x66; "); + + /* Unsupported instructions that Minix code needs. */ + if (a->opcode == JMPF && a->args != nil + && a->args->operator == ',') { + /* JMPF seg:off. */ + gnu_printf(".byte 0xEA; .long "); + gnu_put_expression(a, a->args->right, 0); + gnu_printf("; .short "); + gnu_put_expression(a, a->args->left, 0); + return; + } + if (a->opcode == JMPF && a->args != nil + && a->args->operator == 'O' + && a->args->left != nil + && a->args->right == nil + && a->args->middle != nil + && a->args->middle->operator == 'B' + && strcmp(a->args->middle->name, "esp") == 0 + ) { + /* JMPF offset(ESP). */ + gnu_printf(".byte 0xFF,0x6C,0x24,"); + gnu_put_expression(a, a->args->left, 0); + return; + } + if (a->opcode == MOV && a->args != nil + && a->args->operator == ',' + && a->args->left != nil + && a->args->left->operator == 'W' + && (strcmp(a->args->left->name, "ds") == 0 + || strcmp(a->args->left->name, "es") == 0) + && a->args->right->operator == 'O' + && a->args->right->left != nil + && a->args->right->right == nil + && a->args->right->middle != nil + && a->args->right->middle->operator == 'B' + && strcmp(a->args->right->middle->name, "esp") == 0 + ) { + /* MOV DS, offset(ESP); MOV ES, offset(ESP) */ + gnu_printf(".byte 0x8E,0x%02X,0x24,", + a->args->left->name[0] == 'd' ? 0x5C : 0x44); + gnu_put_expression(a, a->args->right->left, 0); + return; + } + if (a->opcode == MOV && a->args != nil + && a->args->operator == ',' + && a->args->left != nil + && a->args->left->operator == 'W' + && (strcmp(a->args->left->name, "ds") == 0 + || strcmp(a->args->left->name, "es") == 0) + && a->args->right->operator == '(' + && a->args->right->middle != nil + ) { + /* MOV DS, (memory); MOV ES, (memory) */ + gnu_printf(".byte 0x8E,0x%02X; .long ", + a->args->left->name[0] == 'd' ? 0x1D : 0x05); + gnu_put_expression(a, a->args->right->middle, 0); + return; + } + + while (*p != 0) { + if (*p == '%') { + if (a->optype == BYTE) { + gnu_putchar('b'); + } else + if (a->optype == WORD) { + gnu_putchar((a->oaz & OPZ) ? 'w' : 'l'); + } else { + assert(0); + } + } else { + gnu_putchar(*p); + } + p++; + } + + if (a->args != nil) { + static char *aregs[] = { "al", "ax", "eax" }; + + gnu_putchar('\t'); + switch (a->opcode) { + case IN: + gnu_put_expression(a, a->args, 1); + gnu_printf(", %%%s", aregs[a->optype - BYTE]); + break; + case OUT: + gnu_printf("%%%s, ", aregs[a->optype - BYTE]); + gnu_put_expression(a, a->args, 1); + break; + default: + gnu_put_expression(a, a->args, 1); + } + } + if (a->opcode == DOT_USE16) set_use16(); + if (a->opcode == DOT_USE32) set_use32(); + } else { + fprintf(stderr, + "asmconv: internal error, unknown opcode '%d'\n", + a->opcode); + exit(EXIT_FAILURE); + } +} diff --git a/commands/i386/asmconv/languages.h b/commands/i386/asmconv/languages.h new file mode 100755 index 000000000..a34ec9fe4 --- /dev/null +++ b/commands/i386/asmconv/languages.h @@ -0,0 +1,25 @@ +/* languages.h - functions that parse or emit assembly + * Author: Kees J. Bot + * 27 Dec 1993 + */ + +void ack_parse_init(char *file); +asm86_t *ack_get_instruction(void); + +void ncc_parse_init(char *file); +asm86_t *ncc_get_instruction(void); + +void gnu_parse_init(char *file); +asm86_t *gnu_get_instruction(void); + +void bas_parse_init(char *file); +asm86_t *bas_get_instruction(void); + +void ack_emit_init(char *file, const char *banner); +void ack_emit_instruction(asm86_t *instr); + +void ncc_emit_init(char *file, const char *banner); +void ncc_emit_instruction(asm86_t *instr); + +void gnu_emit_init(char *file, const char *banner); +void gnu_emit_instruction(asm86_t *instr); diff --git a/commands/i386/asmconv/parse_ack.c b/commands/i386/asmconv/parse_ack.c new file mode 100755 index 000000000..f5518684c --- /dev/null +++ b/commands/i386/asmconv/parse_ack.c @@ -0,0 +1,910 @@ +/* parse_ack.c - parse ACK assembly Author: Kees J. Bot + * parse NCC assembly 18 Dec 1993 + */ +#define nil 0 +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include "asmconv.h" +#include "token.h" +#include "asm86.h" +#include "languages.h" + +typedef struct mnemonic { /* ACK as86 mnemonics translation table. */ + char *name; + opcode_t opcode; + optype_t optype; +} mnemonic_t; + +static mnemonic_t mnemtab[] = { /* This array is sorted. */ + { ".align", DOT_ALIGN, PSEUDO }, + { ".ascii", DOT_ASCII, PSEUDO }, + { ".asciz", DOT_ASCIZ, PSEUDO }, + { ".assert", DOT_ASSERT, PSEUDO }, + { ".base", DOT_BASE, PSEUDO }, + { ".bss", DOT_BSS, PSEUDO }, + { ".comm", DOT_LCOMM, PSEUDO }, + { ".data", DOT_DATA, PSEUDO }, + { ".data1", DOT_DATA1, PSEUDO }, + { ".data2", DOT_DATA2, PSEUDO }, + { ".data4", DOT_DATA4, PSEUDO }, + { ".define", DOT_DEFINE, PSEUDO }, + { ".end", DOT_END, PSEUDO }, + { ".extern", DOT_EXTERN, PSEUDO }, + { ".file", DOT_FILE, PSEUDO }, + { ".line", DOT_LINE, PSEUDO }, + { ".list", DOT_LIST, PSEUDO }, + { ".nolist", DOT_NOLIST, PSEUDO }, + { ".rom", DOT_ROM, PSEUDO }, + { ".space", DOT_SPACE, PSEUDO }, + { ".symb", DOT_SYMB, PSEUDO }, + { ".text", DOT_TEXT, PSEUDO }, + { ".use16", DOT_USE16, PSEUDO }, + { ".use32", DOT_USE32, PSEUDO }, + { "aaa", AAA, WORD }, + { "aad", AAD, WORD }, + { "aam", AAM, WORD }, + { "aas", AAS, WORD }, + { "adc", ADC, WORD }, + { "adcb", ADC, BYTE }, + { "add", ADD, WORD }, + { "addb", ADD, BYTE }, + { "and", AND, WORD }, + { "andb", AND, BYTE }, + { "arpl", ARPL, WORD }, + { "bound", BOUND, WORD }, + { "bsf", BSF, WORD }, + { "bsr", BSR, WORD }, + { "bswap", BSWAP, WORD }, + { "bt", BT, WORD }, + { "btc", BTC, WORD }, + { "btr", BTR, WORD }, + { "bts", BTS, WORD }, + { "call", CALL, JUMP }, + { "callf", CALLF, JUMP }, + { "cbw", CBW, WORD }, + { "cdq", CWD, WORD }, + { "clc", CLC, WORD }, + { "cld", CLD, WORD }, + { "cli", CLI, WORD }, + { "clts", CLTS, WORD }, + { "cmc", CMC, WORD }, + { "cmp", CMP, WORD }, + { "cmpb", CMP, BYTE }, + { "cmps", CMPS, WORD }, + { "cmpsb", CMPS, BYTE }, + { "cmpxchg", CMPXCHG, WORD }, + { "cwd", CWD, WORD }, + { "cwde", CBW, WORD }, + { "daa", DAA, WORD }, + { "das", DAS, WORD }, + { "dec", DEC, WORD }, + { "decb", DEC, BYTE }, + { "div", DIV, WORD }, + { "divb", DIV, BYTE }, + { "enter", ENTER, WORD }, + { "f2xm1", F2XM1, WORD }, + { "fabs", FABS, WORD }, + { "fadd", FADD, WORD }, + { "faddd", FADDD, WORD }, + { "faddp", FADDP, WORD }, + { "fadds", FADDS, WORD }, + { "fbld", FBLD, WORD }, + { "fbstp", FBSTP, WORD }, + { "fchs", FCHS, WORD }, + { "fclex", FCLEX, WORD }, + { "fcomd", FCOMD, WORD }, + { "fcompd", FCOMPD, WORD }, + { "fcompp", FCOMPP, WORD }, + { "fcomps", FCOMPS, WORD }, + { "fcoms", FCOMS, WORD }, + { "fcos", FCOS, WORD }, + { "fdecstp", FDECSTP, WORD }, + { "fdivd", FDIVD, WORD }, + { "fdivp", FDIVP, WORD }, + { "fdivrd", FDIVRD, WORD }, + { "fdivrp", FDIVRP, WORD }, + { "fdivrs", FDIVRS, WORD }, + { "fdivs", FDIVS, WORD }, + { "ffree", FFREE, WORD }, + { "fiaddl", FIADDL, WORD }, + { "fiadds", FIADDS, WORD }, + { "ficom", FICOM, WORD }, + { "ficomp", FICOMP, WORD }, + { "fidivl", FIDIVL, WORD }, + { "fidivrl", FIDIVRL, WORD }, + { "fidivrs", FIDIVRS, WORD }, + { "fidivs", FIDIVS, WORD }, + { "fildl", FILDL, WORD }, + { "fildq", FILDQ, WORD }, + { "filds", FILDS, WORD }, + { "fimull", FIMULL, WORD }, + { "fimuls", FIMULS, WORD }, + { "fincstp", FINCSTP, WORD }, + { "finit", FINIT, WORD }, + { "fistl", FISTL, WORD }, + { "fistp", FISTP, WORD }, + { "fists", FISTS, WORD }, + { "fisubl", FISUBL, WORD }, + { "fisubrl", FISUBRL, WORD }, + { "fisubrs", FISUBRS, WORD }, + { "fisubs", FISUBS, WORD }, + { "fld1", FLD1, WORD }, + { "fldcw", FLDCW, WORD }, + { "fldd", FLDD, WORD }, + { "fldenv", FLDENV, WORD }, + { "fldl2e", FLDL2E, WORD }, + { "fldl2t", FLDL2T, WORD }, + { "fldlg2", FLDLG2, WORD }, + { "fldln2", FLDLN2, WORD }, + { "fldpi", FLDPI, WORD }, + { "flds", FLDS, WORD }, + { "fldx", FLDX, WORD }, + { "fldz", FLDZ, WORD }, + { "fmuld", FMULD, WORD }, + { "fmulp", FMULP, WORD }, + { "fmuls", FMULS, WORD }, + { "fnop", FNOP, WORD }, + { "fpatan", FPATAN, WORD }, + { "fprem", FPREM, WORD }, + { "fprem1", FPREM1, WORD }, + { "fptan", FPTAN, WORD }, + { "frndint", FRNDINT, WORD }, + { "frstor", FRSTOR, WORD }, + { "fsave", FSAVE, WORD }, + { "fscale", FSCALE, WORD }, + { "fsin", FSIN, WORD }, + { "fsincos", FSINCOS, WORD }, + { "fsqrt", FSQRT, WORD }, + { "fstcw", FSTCW, WORD }, + { "fstd", FSTD, WORD }, + { "fstenv", FSTENV, WORD }, + { "fstpd", FSTPD, WORD }, + { "fstps", FSTPS, WORD }, + { "fstpx", FSTPX, WORD }, + { "fsts", FSTS, WORD }, + { "fstsw", FSTSW, WORD }, + { "fsubd", FSUBD, WORD }, + { "fsubp", FSUBP, WORD }, + { "fsubpr", FSUBPR, WORD }, + { "fsubrd", FSUBRD, WORD }, + { "fsubrs", FSUBRS, WORD }, + { "fsubs", FSUBS, WORD }, + { "ftst", FTST, WORD }, + { "fucom", FUCOM, WORD }, + { "fucomp", FUCOMP, WORD }, + { "fucompp", FUCOMPP, WORD }, + { "fxam", FXAM, WORD }, + { "fxch", FXCH, WORD }, + { "fxtract", FXTRACT, WORD }, + { "fyl2x", FYL2X, WORD }, + { "fyl2xp1", FYL2XP1, WORD }, + { "hlt", HLT, WORD }, + { "idiv", IDIV, WORD }, + { "idivb", IDIV, BYTE }, + { "imul", IMUL, WORD }, + { "imulb", IMUL, BYTE }, + { "in", IN, WORD }, + { "inb", IN, BYTE }, + { "inc", INC, WORD }, + { "incb", INC, BYTE }, + { "ins", INS, WORD }, + { "insb", INS, BYTE }, + { "int", INT, WORD }, + { "into", INTO, JUMP }, + { "invd", INVD, WORD }, + { "invlpg", INVLPG, WORD }, + { "iret", IRET, JUMP }, + { "iretd", IRETD, JUMP }, + { "ja", JA, JUMP }, + { "jae", JAE, JUMP }, + { "jb", JB, JUMP }, + { "jbe", JBE, JUMP }, + { "jc", JB, JUMP }, + { "jcxz", JCXZ, JUMP }, + { "je", JE, JUMP }, + { "jecxz", JCXZ, JUMP }, + { "jg", JG, JUMP }, + { "jge", JGE, JUMP }, + { "jl", JL, JUMP }, + { "jle", JLE, JUMP }, + { "jmp", JMP, JUMP }, + { "jmpf", JMPF, JUMP }, + { "jna", JBE, JUMP }, + { "jnae", JB, JUMP }, + { "jnb", JAE, JUMP }, + { "jnbe", JA, JUMP }, + { "jnc", JAE, JUMP }, + { "jne", JNE, JUMP }, + { "jng", JLE, JUMP }, + { "jnge", JL, JUMP }, + { "jnl", JGE, JUMP }, + { "jnle", JG, JUMP }, + { "jno", JNO, JUMP }, + { "jnp", JNP, JUMP }, + { "jns", JNS, JUMP }, + { "jnz", JNE, JUMP }, + { "jo", JO, JUMP }, + { "jp", JP, JUMP }, + { "js", JS, JUMP }, + { "jz", JE, JUMP }, + { "lahf", LAHF, WORD }, + { "lar", LAR, WORD }, + { "lds", LDS, WORD }, + { "lea", LEA, WORD }, + { "leave", LEAVE, WORD }, + { "les", LES, WORD }, + { "lfs", LFS, WORD }, + { "lgdt", LGDT, WORD }, + { "lgs", LGS, WORD }, + { "lidt", LIDT, WORD }, + { "lldt", LLDT, WORD }, + { "lmsw", LMSW, WORD }, + { "lock", LOCK, WORD }, + { "lods", LODS, WORD }, + { "lodsb", LODS, BYTE }, + { "loop", LOOP, JUMP }, + { "loope", LOOPE, JUMP }, + { "loopne", LOOPNE, JUMP }, + { "loopnz", LOOPNE, JUMP }, + { "loopz", LOOPE, JUMP }, + { "lsl", LSL, WORD }, + { "lss", LSS, WORD }, + { "ltr", LTR, WORD }, + { "mov", MOV, WORD }, + { "movb", MOV, BYTE }, + { "movs", MOVS, WORD }, + { "movsb", MOVS, BYTE }, + { "movsx", MOVSX, WORD }, + { "movsxb", MOVSXB, WORD }, + { "movzx", MOVZX, WORD }, + { "movzxb", MOVZXB, WORD }, + { "mul", MUL, WORD }, + { "mulb", MUL, BYTE }, + { "neg", NEG, WORD }, + { "negb", NEG, BYTE }, + { "nop", NOP, WORD }, + { "not", NOT, WORD }, + { "notb", NOT, BYTE }, + { "or", OR, WORD }, + { "orb", OR, BYTE }, + { "out", OUT, WORD }, + { "outb", OUT, BYTE }, + { "outs", OUTS, WORD }, + { "outsb", OUTS, BYTE }, + { "pop", POP, WORD }, + { "popa", POPA, WORD }, + { "popad", POPA, WORD }, + { "popf", POPF, WORD }, + { "push", PUSH, WORD }, + { "pusha", PUSHA, WORD }, + { "pushad", PUSHA, WORD }, + { "pushf", PUSHF, WORD }, + { "rcl", RCL, WORD }, + { "rclb", RCL, BYTE }, + { "rcr", RCR, WORD }, + { "rcrb", RCR, BYTE }, + { "ret", RET, JUMP }, + { "retf", RETF, JUMP }, + { "rol", ROL, WORD }, + { "rolb", ROL, BYTE }, + { "ror", ROR, WORD }, + { "rorb", ROR, BYTE }, + { "sahf", SAHF, WORD }, + { "sal", SAL, WORD }, + { "salb", SAL, BYTE }, + { "sar", SAR, WORD }, + { "sarb", SAR, BYTE }, + { "sbb", SBB, WORD }, + { "sbbb", SBB, BYTE }, + { "scas", SCAS, WORD }, + { "scasb", SCAS, BYTE }, + { "seta", SETA, BYTE }, + { "setae", SETAE, BYTE }, + { "setb", SETB, BYTE }, + { "setbe", SETBE, BYTE }, + { "sete", SETE, BYTE }, + { "setg", SETG, BYTE }, + { "setge", SETGE, BYTE }, + { "setl", SETL, BYTE }, + { "setna", SETBE, BYTE }, + { "setnae", SETB, BYTE }, + { "setnb", SETAE, BYTE }, + { "setnbe", SETA, BYTE }, + { "setne", SETNE, BYTE }, + { "setng", SETLE, BYTE }, + { "setnge", SETL, BYTE }, + { "setnl", SETGE, BYTE }, + { "setnle", SETG, BYTE }, + { "setno", SETNO, BYTE }, + { "setnp", SETNP, BYTE }, + { "setns", SETNS, BYTE }, + { "seto", SETO, BYTE }, + { "setp", SETP, BYTE }, + { "sets", SETS, BYTE }, + { "setz", SETE, BYTE }, + { "sgdt", SGDT, WORD }, + { "shl", SHL, WORD }, + { "shlb", SHL, BYTE }, + { "shld", SHLD, WORD }, + { "shr", SHR, WORD }, + { "shrb", SHR, BYTE }, + { "shrd", SHRD, WORD }, + { "sidt", SIDT, WORD }, + { "sldt", SLDT, WORD }, + { "smsw", SMSW, WORD }, + { "stc", STC, WORD }, + { "std", STD, WORD }, + { "sti", STI, WORD }, + { "stos", STOS, WORD }, + { "stosb", STOS, BYTE }, + { "str", STR, WORD }, + { "sub", SUB, WORD }, + { "subb", SUB, BYTE }, + { "test", TEST, WORD }, + { "testb", TEST, BYTE }, + { "verr", VERR, WORD }, + { "verw", VERW, WORD }, + { "wait", WAIT, WORD }, + { "wbinvd", WBINVD, WORD }, + { "xadd", XADD, WORD }, + { "xchg", XCHG, WORD }, + { "xchgb", XCHG, BYTE }, + { "xlat", XLAT, WORD }, + { "xor", XOR, WORD }, + { "xorb", XOR, BYTE }, +}; + +static enum dialect { ACK, NCC } dialect= ACK; + +void ack_parse_init(char *file) +/* Prepare parsing of an ACK assembly file. */ +{ + tok_init(file, '!'); +} + +void ncc_parse_init(char *file) +/* Prepare parsing of an ACK Xenix assembly file. See emit_ack.c for comments + * on this fine assembly dialect. + */ +{ + dialect= NCC; + ack_parse_init(file); +} + +static void zap(void) +/* An error, zap the rest of the line. */ +{ + token_t *t; + + while ((t= get_token(0))->type != T_EOF && t->symbol != ';') + skip_token(1); +} + +static mnemonic_t *search_mnem(char *name) +/* Binary search for a mnemonic. (That's why the table is sorted.) */ +{ + int low, mid, high; + int cmp; + mnemonic_t *m; + + low= 0; + high= arraysize(mnemtab)-1; + while (low <= high) { + mid= (low + high) / 2; + m= &mnemtab[mid]; + + if ((cmp= strcmp(name, m->name)) == 0) return m; + + if (cmp < 0) high= mid-1; else low= mid+1; + } + return nil; +} + +static expression_t *ack_get_C_expression(int *pn) +/* Read a "C-like" expression. Note that we don't worry about precedence, + * the expression is printed later like it is read. If the target language + * does not have all the operators (like ~) then this has to be repaired by + * changing the source file. (No problem, you still have one source file + * to maintain, not two.) + */ +{ + expression_t *e, *a1, *a2; + token_t *t; + + if ((t= get_token(*pn))->symbol == '[') { + /* [ expr ]: grouping. */ + (*pn)++; + if ((a1= ack_get_C_expression(pn)) == nil) return nil; + if (get_token(*pn)->symbol != ']') { + parse_err(1, t, "missing ]\n"); + del_expr(a1); + return nil; + } + (*pn)++; + e= new_expr(); + e->operator= '['; + e->middle= a1; + } else + if (t->type == T_WORD || t->type == T_STRING) { + /* Label, number, or string. */ + e= new_expr(); + e->operator= t->type == T_WORD ? 'W' : 'S'; + e->name= allocate(nil, (t->len+1) * sizeof(e->name[0])); + memcpy(e->name, t->name, t->len+1); + e->len= t->len; + (*pn)++; + } else + if (t->symbol == '+' || t->symbol == '-' || t->symbol == '~') { + /* Unary operator. */ + (*pn)++; + if ((a1= ack_get_C_expression(pn)) == nil) return nil; + e= new_expr(); + e->operator= t->symbol; + e->middle= a1; + } else { + parse_err(1, t, "expression syntax error\n"); + return nil; + } + + switch ((t= get_token(*pn))->symbol) { + case '+': + case '-': + case '*': + case '/': + case '%': + case '&': + case '|': + case '^': + case S_LEFTSHIFT: + case S_RIGHTSHIFT: + (*pn)++; + a1= e; + if ((a2= ack_get_C_expression(pn)) == nil) { + del_expr(a1); + return nil; + } + e= new_expr(); + e->operator= t->symbol; + e->left= a1; + e->right= a2; + } + return e; +} + +static expression_t *ack_get_operand(int *pn, int deref) +/* Get something like: (memory), offset(base)(index*scale), or simpler. */ +{ + expression_t *e, *offset, *base, *index; + token_t *t; + int c; + + /* Is it (memory)? */ + if (get_token(*pn)->symbol == '(' + && ((t= get_token(*pn + 1))->type != T_WORD + || !isregister(t->name)) + ) { + /* A memory dereference. */ + (*pn)++; + if ((offset= ack_get_C_expression(pn)) == nil) return nil; + if (get_token(*pn)->symbol != ')') { + parse_err(1, t, "operand syntax error\n"); + del_expr(offset); + return nil; + } + (*pn)++; + e= new_expr(); + e->operator= '('; + e->middle= offset; + return e; + } + + /* #constant? */ + if (dialect == NCC && deref + && ((c= get_token(*pn)->symbol) == '#' || c == '*')) { + /* NCC: mov ax,#constant -> ACK: mov ax,constant */ + (*pn)++; + return ack_get_C_expression(pn); + } + + /* @address? */ + if (dialect == NCC && get_token(*pn)->symbol == '@') { + /* NCC: jmp @address -> ACK: jmp (address) */ + (*pn)++; + if ((offset= ack_get_operand(pn, deref)) == nil) return nil; + e= new_expr(); + e->operator= '('; + e->middle= offset; + return e; + } + + /* Offset? */ + if (get_token(*pn)->symbol != '(') { + /* There is an offset. */ + if ((offset= ack_get_C_expression(pn)) == nil) return nil; + } else { + /* No offset. */ + offset= nil; + } + + /* (base)? */ + if (get_token(*pn)->symbol == '(' + && (t= get_token(*pn + 1))->type == T_WORD + && isregister(t->name) + && get_token(*pn + 2)->symbol == ')' + ) { + /* A base register expression. */ + base= new_expr(); + base->operator= 'B'; + base->name= copystr(t->name); + (*pn)+= 3; + } else { + /* No base register expression. */ + base= nil; + } + + /* (index*scale)? */ + if (get_token(*pn)->symbol == '(') { + /* An index most likely. */ + token_t *m= nil; + + if (!( /* This must be true: */ + (t= get_token(*pn + 1))->type == T_WORD + && isregister(t->name) + && (get_token(*pn + 2)->symbol == ')' || ( + get_token(*pn + 2)->symbol == '*' + && (m= get_token(*pn + 3))->type == T_WORD + && strchr("1248", m->name[0]) != nil + && m->name[1] == 0 + && get_token(*pn + 4)->symbol == ')' + )) + )) { + /* Alas it isn't */ + parse_err(1, t, "operand syntax error\n"); + del_expr(offset); + del_expr(base); + return nil; + } + /* Found an index. */ + index= new_expr(); + index->operator= m == nil ? '1' : m->name[0]; + index->name= copystr(t->name); + (*pn)+= (m == nil ? 3 : 5); + } else { + /* No index. */ + index= nil; + } + + if (dialect == NCC && deref && base == nil && index == nil + && !(offset != nil && offset->operator == 'W' + && isregister(offset->name)) + ) { + /* NCC: mov ax,thing -> ACK mov ax,(thing) */ + e= new_expr(); + e->operator= '('; + e->middle= offset; + return e; + } + + if (base == nil && index == nil) { + /* Return a lone offset as is. */ + e= offset; + } else { + e= new_expr(); + e->operator= 'O'; + e->left= offset; + e->middle= base; + e->right= index; + } + return e; +} + +static expression_t *ack_get_oplist(int *pn, int deref) +/* Get a comma (or colon for jmpf and callf) separated list of instruction + * operands. + */ +{ + expression_t *e, *o1, *o2; + token_t *t; + + if ((e= ack_get_operand(pn, deref)) == nil) return nil; + + if ((t= get_token(*pn))->symbol == ',' || t->symbol == ':') { + o1= e; + (*pn)++; + if ((o2= ack_get_oplist(pn, deref)) == nil) { + del_expr(o1); + return nil; + } + e= new_expr(); + e->operator= ','; + e->left= o1; + e->right= o2; + } + return e; +} + +static asm86_t *ack_get_statement(void) +/* Get a pseudo op or machine instruction with arguments. */ +{ + token_t *t= get_token(0); + asm86_t *a; + mnemonic_t *m; + int n; + int prefix_seen; + int oaz_prefix; + int deref; + + assert(t->type == T_WORD); + + if (strcmp(t->name, ".sect") == 0) { + /* .sect .text etc. Accept only four segment names. */ + skip_token(1); + t= get_token(0); + if (t->type != T_WORD || ( + strcmp(t->name, ".text") != 0 + && strcmp(t->name, ".rom") != 0 + && strcmp(t->name, ".data") != 0 + && strcmp(t->name, ".bss") != 0 + && strcmp(t->name, ".end") != 0 + )) { + parse_err(1, t, "weird section name to .sect\n"); + return nil; + } + } + a= new_asm86(); + + /* Process instruction prefixes. */ + oaz_prefix= 0; + for (prefix_seen= 0;; prefix_seen= 1) { + if (strcmp(t->name, "o16") == 0) { + if (use16()) { + parse_err(1, t, "o16 in an 8086 section\n"); + } + oaz_prefix|= OPZ; + } else + if (strcmp(t->name, "o32") == 0) { + if (use32()) { + parse_err(1, t, "o32 in an 80386 section\n"); + } + oaz_prefix|= OPZ; + } else + if (strcmp(t->name, "a16") == 0) { + if (use16()) { + parse_err(1, t, "a16 in an 8086 section\n"); + } + oaz_prefix|= ADZ; + } else + if (strcmp(t->name, "a32") == 0) { + if (use32()) { + parse_err(1, t, "a32 in an 80386 section\n"); + } + oaz_prefix|= ADZ; + } else + if (strcmp(t->name, "rep") == 0 + || strcmp(t->name, "repe") == 0 + || strcmp(t->name, "repne") == 0 + || strcmp(t->name, "repz") == 0 + || strcmp(t->name, "repnz") == 0 + ) { + if (a->rep != ONCE) { + parse_err(1, t, + "can't have more than one rep\n"); + } + switch (t->name[3]) { + case 0: a->rep= REP; break; + case 'e': + case 'z': a->rep= REPE; break; + case 'n': a->rep= REPNE; break; + } + } else + if (strchr("cdefgs", t->name[0]) != nil + && strcmp(t->name+1, "seg") == 0) { + if (a->seg != DEFSEG) { + parse_err(1, t, + "can't have more than one segment prefix\n"); + } + switch (t->name[0]) { + case 'c': a->seg= CSEG; break; + case 'd': a->seg= DSEG; break; + case 'e': a->seg= ESEG; break; + case 'f': a->seg= FSEG; break; + case 'g': a->seg= GSEG; break; + case 's': a->seg= SSEG; break; + } + } else + if (!prefix_seen) { + /* No prefix here, get out! */ + break; + } else { + /* No more prefixes, next must be an instruction. */ + if (t->type != T_WORD + || (m= search_mnem(t->name)) == nil + || m->optype == PSEUDO + ) { + parse_err(1, t, + "machine instruction expected after instruction prefix\n"); + del_asm86(a); + return nil; + } + if (oaz_prefix != 0 && m->optype != JUMP + && m->optype != WORD) { + parse_err(1, t, + "'%s' can't have an operand size prefix\n", m->name); + } + break; + } + + /* Skip the prefix and extra newlines. */ + do { + skip_token(1); + } while ((t= get_token(0))->symbol == ';'); + } + + /* All the readahead being done upsets the line counter. */ + a->line= t->line; + + /* Read a machine instruction or pseudo op. */ + if ((m= search_mnem(t->name)) == nil) { + parse_err(1, t, "unknown instruction '%s'\n", t->name); + del_asm86(a); + return nil; + } + a->opcode= m->opcode; + a->optype= m->optype; + a->oaz= oaz_prefix; + + switch (a->opcode) { + case IN: + case OUT: + case INT: + deref= 0; + break; + default: + deref= (a->optype >= BYTE); + } + n= 1; + if (get_token(1)->symbol != ';' + && (a->args= ack_get_oplist(&n, deref)) == nil) { + del_asm86(a); + return nil; + } + if (get_token(n)->symbol != ';') { + parse_err(1, t, "garbage at end of instruction\n"); + del_asm86(a); + return nil; + } + switch (a->opcode) { + case DOT_ALIGN: + /* Restrict .align to have a single numeric argument, some + * assemblers think of the argument as a power of two, so + * we need to be able to change the value. + */ + if (a->args == nil || a->args->operator != 'W' + || !isanumber(a->args->name)) { + parse_err(1, t, + ".align is restricted to one numeric argument\n"); + del_asm86(a); + return nil; + } + break; + case JMPF: + case CALLF: + /* NCC jmpf off,seg -> ACK jmpf seg:off */ + if (dialect == NCC && a->args != nil + && a->args->operator == ',') { + expression_t *t; + + t= a->args->left; + a->args->left= a->args->right; + a->args->right= t; + break; + } + /*FALL THROUGH*/ + case JMP: + case CALL: + /* NCC jmp @(reg) -> ACK jmp (reg) */ + if (dialect == NCC && a->args != nil && ( + (a->args->operator == '(' + && a->args->middle != nil + && a->args->middle->operator == 'O') + || (a->args->operator == 'O' + && a->args->left == nil + && a->args->middle != nil + && a->args->right == nil) + )) { + expression_t *t; + + t= a->args; + a->args= a->args->middle; + t->middle= nil; + del_expr(t); + if (a->args->operator == 'B') a->args->operator= 'W'; + } + break; + default:; + } + skip_token(n+1); + return a; +} + +asm86_t *ack_get_instruction(void) +{ + asm86_t *a= nil; + expression_t *e; + token_t *t; + + while ((t= get_token(0))->symbol == ';') + skip_token(1); + + if (t->type == T_EOF) return nil; + + if (t->symbol == '#') { + /* Preprocessor line and file change. */ + + if ((t= get_token(1))->type != T_WORD || !isanumber(t->name) + || get_token(2)->type != T_STRING + ) { + parse_err(1, t, "file not preprocessed?\n"); + zap(); + } else { + set_file(get_token(2)->name, + strtol(get_token(1)->name, nil, 0) - 1); + + /* GNU CPP adds extra cruft, simply zap the line. */ + zap(); + } + a= ack_get_instruction(); + } else + if (t->type == T_WORD && get_token(1)->symbol == ':') { + /* A label definition. */ + a= new_asm86(); + a->line= t->line; + a->opcode= DOT_LABEL; + a->optype= PSEUDO; + a->args= e= new_expr(); + e->operator= ':'; + e->name= copystr(t->name); + skip_token(2); + } else + if (t->type == T_WORD && get_token(1)->symbol == '=') { + int n= 2; + + if ((e= ack_get_C_expression(&n)) == nil) { + zap(); + a= ack_get_instruction(); + } else + if (get_token(n)->symbol != ';') { + parse_err(1, t, "garbage after assignment\n"); + zap(); + a= ack_get_instruction(); + } else { + a= new_asm86(); + a->line= t->line; + a->opcode= DOT_EQU; + a->optype= PSEUDO; + a->args= new_expr(); + a->args->operator= '='; + a->args->name= copystr(t->name); + a->args->middle= e; + skip_token(n+1); + } + } else + if (t->type == T_WORD) { + if ((a= ack_get_statement()) == nil) { + zap(); + a= ack_get_instruction(); + } + } else { + parse_err(1, t, "syntax error\n"); + zap(); + a= ack_get_instruction(); + } + return a; +} + +asm86_t *ncc_get_instruction(void) +{ + return ack_get_instruction(); +} diff --git a/commands/i386/asmconv/parse_bas.c b/commands/i386/asmconv/parse_bas.c new file mode 100755 index 000000000..3eacc892c --- /dev/null +++ b/commands/i386/asmconv/parse_bas.c @@ -0,0 +1,940 @@ +/* parse_bas.c - parse BCC AS assembly Author: Kees J. Bot + * 13 Nov 1994 + */ +#define nil 0 +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include "asmconv.h" +#include "token.h" +#include "asm86.h" +#include "languages.h" + +typedef struct mnemonic { /* BAS mnemonics translation table. */ + char *name; + opcode_t opcode; + optype_t optype; +} mnemonic_t; + +static mnemonic_t mnemtab[] = { /* This array is sorted. */ + { ".align", DOT_ALIGN, PSEUDO }, + { ".ascii", DOT_ASCII, PSEUDO }, + { ".asciz", DOT_ASCIZ, PSEUDO }, + { ".assert", DOT_ASSERT, PSEUDO }, + { ".base", DOT_BASE, PSEUDO }, + { ".blkb", DOT_SPACE, PSEUDO }, + { ".bss", DOT_BSS, PSEUDO }, + { ".byte", DOT_DATA1, PSEUDO }, + { ".comm", DOT_COMM, PSEUDO }, + { ".data", DOT_DATA, PSEUDO }, + { ".define", DOT_DEFINE, PSEUDO }, + { ".end", DOT_END, PSEUDO }, + { ".even", DOT_ALIGN, PSEUDO }, + { ".extern", DOT_EXTERN, PSEUDO }, + { ".file", DOT_FILE, PSEUDO }, + { ".globl", DOT_DEFINE, PSEUDO }, + { ".lcomm", DOT_LCOMM, PSEUDO }, + { ".line", DOT_LINE, PSEUDO }, + { ".list", DOT_LIST, PSEUDO }, + { ".long", DOT_DATA4, PSEUDO }, + { ".nolist", DOT_NOLIST, PSEUDO }, + { ".rom", DOT_ROM, PSEUDO }, + { ".space", DOT_SPACE, PSEUDO }, + { ".symb", DOT_SYMB, PSEUDO }, + { ".text", DOT_TEXT, PSEUDO }, + { ".use16", DOT_USE16, PSEUDO }, + { ".use32", DOT_USE32, PSEUDO }, + { ".word", DOT_DATA2, PSEUDO }, + { ".zerob", DOT_SPACE, PSEUDO }, + { ".zerow", DOT_SPACE, PSEUDO }, + { "aaa", AAA, WORD }, + { "aad", AAD, WORD }, + { "aam", AAM, WORD }, + { "aas", AAS, WORD }, + { "adc", ADC, WORD }, + { "add", ADD, WORD }, + { "and", AND, WORD }, + { "arpl", ARPL, WORD }, + { "bc", JB, JUMP }, + { "beq", JE, JUMP }, + { "bge", JGE, JUMP }, + { "bgt", JG, JUMP }, + { "bhi", JA, JUMP }, + { "bhis", JAE, JUMP }, + { "ble", JLE, JUMP }, + { "blo", JB, JUMP }, + { "blos", JBE, JUMP }, + { "blt", JL, JUMP }, + { "bnc", JAE, JUMP }, + { "bne", JNE, JUMP }, + { "bound", BOUND, WORD }, + { "br", JMP, JUMP }, + { "bsf", BSF, WORD }, + { "bsr", BSR, WORD }, + { "bswap", BSWAP, WORD }, + { "bt", BT, WORD }, + { "btc", BTC, WORD }, + { "btr", BTR, WORD }, + { "bts", BTS, WORD }, + { "bz", JE, JUMP }, + { "call", CALL, JUMP }, + { "callf", CALLF, JUMP }, + { "cbw", CBW, WORD }, + { "cdq", CWD, WORD }, + { "clc", CLC, WORD }, + { "cld", CLD, WORD }, + { "cli", CLI, WORD }, + { "clts", CLTS, WORD }, + { "cmc", CMC, WORD }, + { "cmp", CMP, WORD }, + { "cmps", CMPS, WORD }, + { "cmpsb", CMPS, BYTE }, + { "cmpxchg", CMPXCHG, WORD }, + { "cwd", CWD, WORD }, + { "cwde", CBW, WORD }, + { "daa", DAA, WORD }, + { "das", DAS, WORD }, + { "dd", DOT_DATA4, PSEUDO }, + { "dec", DEC, WORD }, + { "div", DIV, WORD }, + { "enter", ENTER, WORD }, + { "export", DOT_DEFINE, PSEUDO }, + { "f2xm1", F2XM1, WORD }, + { "fabs", FABS, WORD }, + { "fadd", FADD, WORD }, + { "faddd", FADDD, WORD }, + { "faddp", FADDP, WORD }, + { "fadds", FADDS, WORD }, + { "fbld", FBLD, WORD }, + { "fbstp", FBSTP, WORD }, + { "fchs", FCHS, WORD }, + { "fclex", FCLEX, WORD }, + { "fcomd", FCOMD, WORD }, + { "fcompd", FCOMPD, WORD }, + { "fcompp", FCOMPP, WORD }, + { "fcomps", FCOMPS, WORD }, + { "fcoms", FCOMS, WORD }, + { "fcos", FCOS, WORD }, + { "fdecstp", FDECSTP, WORD }, + { "fdivd", FDIVD, WORD }, + { "fdivp", FDIVP, WORD }, + { "fdivrd", FDIVRD, WORD }, + { "fdivrp", FDIVRP, WORD }, + { "fdivrs", FDIVRS, WORD }, + { "fdivs", FDIVS, WORD }, + { "ffree", FFREE, WORD }, + { "fiaddl", FIADDL, WORD }, + { "fiadds", FIADDS, WORD }, + { "ficom", FICOM, WORD }, + { "ficomp", FICOMP, WORD }, + { "fidivl", FIDIVL, WORD }, + { "fidivrl", FIDIVRL, WORD }, + { "fidivrs", FIDIVRS, WORD }, + { "fidivs", FIDIVS, WORD }, + { "fildl", FILDL, WORD }, + { "fildq", FILDQ, WORD }, + { "filds", FILDS, WORD }, + { "fimull", FIMULL, WORD }, + { "fimuls", FIMULS, WORD }, + { "fincstp", FINCSTP, WORD }, + { "finit", FINIT, WORD }, + { "fistl", FISTL, WORD }, + { "fistp", FISTP, WORD }, + { "fists", FISTS, WORD }, + { "fisubl", FISUBL, WORD }, + { "fisubrl", FISUBRL, WORD }, + { "fisubrs", FISUBRS, WORD }, + { "fisubs", FISUBS, WORD }, + { "fld1", FLD1, WORD }, + { "fldcw", FLDCW, WORD }, + { "fldd", FLDD, WORD }, + { "fldenv", FLDENV, WORD }, + { "fldl2e", FLDL2E, WORD }, + { "fldl2t", FLDL2T, WORD }, + { "fldlg2", FLDLG2, WORD }, + { "fldln2", FLDLN2, WORD }, + { "fldpi", FLDPI, WORD }, + { "flds", FLDS, WORD }, + { "fldx", FLDX, WORD }, + { "fldz", FLDZ, WORD }, + { "fmuld", FMULD, WORD }, + { "fmulp", FMULP, WORD }, + { "fmuls", FMULS, WORD }, + { "fnop", FNOP, WORD }, + { "fpatan", FPATAN, WORD }, + { "fprem", FPREM, WORD }, + { "fprem1", FPREM1, WORD }, + { "fptan", FPTAN, WORD }, + { "frndint", FRNDINT, WORD }, + { "frstor", FRSTOR, WORD }, + { "fsave", FSAVE, WORD }, + { "fscale", FSCALE, WORD }, + { "fsin", FSIN, WORD }, + { "fsincos", FSINCOS, WORD }, + { "fsqrt", FSQRT, WORD }, + { "fstcw", FSTCW, WORD }, + { "fstd", FSTD, WORD }, + { "fstenv", FSTENV, WORD }, + { "fstpd", FSTPD, WORD }, + { "fstps", FSTPS, WORD }, + { "fstpx", FSTPX, WORD }, + { "fsts", FSTS, WORD }, + { "fstsw", FSTSW, WORD }, + { "fsubd", FSUBD, WORD }, + { "fsubp", FSUBP, WORD }, + { "fsubpr", FSUBPR, WORD }, + { "fsubrd", FSUBRD, WORD }, + { "fsubrs", FSUBRS, WORD }, + { "fsubs", FSUBS, WORD }, + { "ftst", FTST, WORD }, + { "fucom", FUCOM, WORD }, + { "fucomp", FUCOMP, WORD }, + { "fucompp", FUCOMPP, WORD }, + { "fxam", FXAM, WORD }, + { "fxch", FXCH, WORD }, + { "fxtract", FXTRACT, WORD }, + { "fyl2x", FYL2X, WORD }, + { "fyl2xp1", FYL2XP1, WORD }, + { "hlt", HLT, WORD }, + { "idiv", IDIV, WORD }, + { "imul", IMUL, WORD }, + { "in", IN, WORD }, + { "inb", IN, BYTE }, + { "inc", INC, WORD }, + { "ins", INS, WORD }, + { "insb", INS, BYTE }, + { "int", INT, WORD }, + { "into", INTO, JUMP }, + { "invd", INVD, WORD }, + { "invlpg", INVLPG, WORD }, + { "iret", IRET, JUMP }, + { "iretd", IRETD, JUMP }, + { "j", JMP, JUMP }, + { "ja", JA, JUMP }, + { "jae", JAE, JUMP }, + { "jb", JB, JUMP }, + { "jbe", JBE, JUMP }, + { "jc", JB, JUMP }, + { "jcxz", JCXZ, JUMP }, + { "je", JE, JUMP }, + { "jecxz", JCXZ, JUMP }, + { "jeq", JE, JUMP }, + { "jg", JG, JUMP }, + { "jge", JGE, JUMP }, + { "jgt", JG, JUMP }, + { "jhi", JA, JUMP }, + { "jhis", JAE, JUMP }, + { "jl", JL, JUMP }, + { "jle", JLE, JUMP }, + { "jlo", JB, JUMP }, + { "jlos", JBE, JUMP }, + { "jlt", JL, JUMP }, + { "jmp", JMP, JUMP }, + { "jmpf", JMPF, JUMP }, + { "jna", JBE, JUMP }, + { "jnae", JB, JUMP }, + { "jnb", JAE, JUMP }, + { "jnbe", JA, JUMP }, + { "jnc", JAE, JUMP }, + { "jne", JNE, JUMP }, + { "jng", JLE, JUMP }, + { "jnge", JL, JUMP }, + { "jnl", JGE, JUMP }, + { "jnle", JG, JUMP }, + { "jno", JNO, JUMP }, + { "jnp", JNP, JUMP }, + { "jns", JNS, JUMP }, + { "jnz", JNE, JUMP }, + { "jo", JO, JUMP }, + { "jp", JP, JUMP }, + { "js", JS, JUMP }, + { "jz", JE, JUMP }, + { "lahf", LAHF, WORD }, + { "lar", LAR, WORD }, + { "lds", LDS, WORD }, + { "lea", LEA, WORD }, + { "leave", LEAVE, WORD }, + { "les", LES, WORD }, + { "lfs", LFS, WORD }, + { "lgdt", LGDT, WORD }, + { "lgs", LGS, WORD }, + { "lidt", LIDT, WORD }, + { "lldt", LLDT, WORD }, + { "lmsw", LMSW, WORD }, + { "lock", LOCK, WORD }, + { "lods", LODS, WORD }, + { "lodsb", LODS, BYTE }, + { "loop", LOOP, JUMP }, + { "loope", LOOPE, JUMP }, + { "loopne", LOOPNE, JUMP }, + { "loopnz", LOOPNE, JUMP }, + { "loopz", LOOPE, JUMP }, + { "lsl", LSL, WORD }, + { "lss", LSS, WORD }, + { "ltr", LTR, WORD }, + { "mov", MOV, WORD }, + { "movs", MOVS, WORD }, + { "movsb", MOVS, BYTE }, + { "movsx", MOVSX, WORD }, + { "movzx", MOVZX, WORD }, + { "mul", MUL, WORD }, + { "neg", NEG, WORD }, + { "nop", NOP, WORD }, + { "not", NOT, WORD }, + { "or", OR, WORD }, + { "out", OUT, WORD }, + { "outb", OUT, BYTE }, + { "outs", OUTS, WORD }, + { "outsb", OUTS, BYTE }, + { "pop", POP, WORD }, + { "popa", POPA, WORD }, + { "popad", POPA, WORD }, + { "popf", POPF, WORD }, + { "popfd", POPF, WORD }, + { "push", PUSH, WORD }, + { "pusha", PUSHA, WORD }, + { "pushad", PUSHA, WORD }, + { "pushf", PUSHF, WORD }, + { "pushfd", PUSHF, WORD }, + { "rcl", RCL, WORD }, + { "rcr", RCR, WORD }, + { "ret", RET, JUMP }, + { "retf", RETF, JUMP }, + { "rol", ROL, WORD }, + { "ror", ROR, WORD }, + { "sahf", SAHF, WORD }, + { "sal", SAL, WORD }, + { "sar", SAR, WORD }, + { "sbb", SBB, WORD }, + { "scas", SCAS, WORD }, + { "seta", SETA, BYTE }, + { "setae", SETAE, BYTE }, + { "setb", SETB, BYTE }, + { "setbe", SETBE, BYTE }, + { "sete", SETE, BYTE }, + { "setg", SETG, BYTE }, + { "setge", SETGE, BYTE }, + { "setl", SETL, BYTE }, + { "setna", SETBE, BYTE }, + { "setnae", SETB, BYTE }, + { "setnb", SETAE, BYTE }, + { "setnbe", SETA, BYTE }, + { "setne", SETNE, BYTE }, + { "setng", SETLE, BYTE }, + { "setnge", SETL, BYTE }, + { "setnl", SETGE, BYTE }, + { "setnle", SETG, BYTE }, + { "setno", SETNO, BYTE }, + { "setnp", SETNP, BYTE }, + { "setns", SETNS, BYTE }, + { "seto", SETO, BYTE }, + { "setp", SETP, BYTE }, + { "sets", SETS, BYTE }, + { "setz", SETE, BYTE }, + { "sgdt", SGDT, WORD }, + { "shl", SHL, WORD }, + { "shld", SHLD, WORD }, + { "shr", SHR, WORD }, + { "shrd", SHRD, WORD }, + { "sidt", SIDT, WORD }, + { "sldt", SLDT, WORD }, + { "smsw", SMSW, WORD }, + { "stc", STC, WORD }, + { "std", STD, WORD }, + { "sti", STI, WORD }, + { "stos", STOS, WORD }, + { "stosb", STOS, BYTE }, + { "str", STR, WORD }, + { "sub", SUB, WORD }, + { "test", TEST, WORD }, + { "verr", VERR, WORD }, + { "verw", VERW, WORD }, + { "wait", WAIT, WORD }, + { "wbinvd", WBINVD, WORD }, + { "xadd", XADD, WORD }, + { "xchg", XCHG, WORD }, + { "xlat", XLAT, WORD }, + { "xor", XOR, WORD }, +}; + +void bas_parse_init(char *file) +/* Prepare parsing of an BAS assembly file. */ +{ + tok_init(file, '!'); +} + +static void zap(void) +/* An error, zap the rest of the line. */ +{ + token_t *t; + + while ((t= get_token(0))->type != T_EOF && t->symbol != ';') + skip_token(1); +} + +static mnemonic_t *search_mnem(char *name) +/* Binary search for a mnemonic. (That's why the table is sorted.) */ +{ + int low, mid, high; + int cmp; + mnemonic_t *m; + + low= 0; + high= arraysize(mnemtab)-1; + while (low <= high) { + mid= (low + high) / 2; + m= &mnemtab[mid]; + + if ((cmp= strcmp(name, m->name)) == 0) return m; + + if (cmp < 0) high= mid-1; else low= mid+1; + } + return nil; +} + +static expression_t *bas_get_C_expression(int *pn) +/* Read a "C-like" expression. Note that we don't worry about precedence, + * the expression is printed later like it is read. If the target language + * does not have all the operators (like ~) then this has to be repaired by + * changing the source file. (No problem, you still have one source file + * to maintain, not two.) + */ +{ + expression_t *e, *a1, *a2; + token_t *t; + + if ((t= get_token(*pn))->symbol == '(') { + /* ( expr ): grouping. */ + (*pn)++; + if ((a1= bas_get_C_expression(pn)) == nil) return nil; + if (get_token(*pn)->symbol != ')') { + parse_err(1, t, "missing )\n"); + del_expr(a1); + return nil; + } + (*pn)++; + e= new_expr(); + e->operator= '['; + e->middle= a1; + } else + if (t->type == T_WORD || t->type == T_STRING) { + /* Label, number, or string. */ + e= new_expr(); + e->operator= t->type == T_WORD ? 'W' : 'S'; + e->name= allocate(nil, (t->len+1) * sizeof(e->name[0])); + memcpy(e->name, t->name, t->len+1); + e->len= t->len; + (*pn)++; + } else + if (t->symbol == '+' || t->symbol == '-' || t->symbol == '~') { + /* Unary operator. */ + (*pn)++; + if ((a1= bas_get_C_expression(pn)) == nil) return nil; + e= new_expr(); + e->operator= t->symbol; + e->middle= a1; + } else + if (t->symbol == '$' && get_token(*pn + 1)->type == T_WORD) { + /* A hexadecimal number. */ + t= get_token(*pn + 1); + e= new_expr(); + e->operator= 'W'; + e->name= allocate(nil, (t->len+3) * sizeof(e->name[0])); + strcpy(e->name, "0x"); + memcpy(e->name+2, t->name, t->len+1); + e->len= t->len+2; + (*pn)+= 2; + } else { + parse_err(1, t, "expression syntax error\n"); + return nil; + } + + switch ((t= get_token(*pn))->symbol) { + case '+': + case '-': + case '*': + case '/': + case '%': + case '&': + case '|': + case '^': + case S_LEFTSHIFT: + case S_RIGHTSHIFT: + (*pn)++; + a1= e; + if ((a2= bas_get_C_expression(pn)) == nil) { + del_expr(a1); + return nil; + } + e= new_expr(); + e->operator= t->symbol; + e->left= a1; + e->right= a2; + } + return e; +} + +/* We want to know the sizes of the first two operands. */ +static optype_t optypes[2]; +static int op_idx; + +static expression_t *bas_get_operand(int *pn) +/* Get something like: [memory], offset[base+index*scale], or simpler. */ +{ + expression_t *e, *offset, *base, *index; + token_t *t; + int c; + optype_t optype; + + /* Prefixed by 'byte', 'word' or 'dword'? */ + if ((t= get_token(*pn))->type == T_WORD && ( + strcmp(t->name, "byte") == 0 + || strcmp(t->name, "word") == 0 + || strcmp(t->name, "dword") == 0) + ) { + switch (t->name[0]) { + case 'b': optype= BYTE; break; + case 'w': optype= use16() ? WORD : OWORD; break; + case 'd': optype= use32() ? WORD : OWORD; break; + } + if (op_idx < arraysize(optypes)) optypes[op_idx++]= optype; + (*pn)++; + + /* It may even be "byte ptr"... */ + if ((t= get_token(*pn))->type == T_WORD + && strcmp(t->name, "ptr") == 0) { + (*pn)++; + } + } + + /* Is it [memory]? */ + if (get_token(*pn)->symbol == '[' + && ((t= get_token(*pn + 1))->type != T_WORD + || !isregister(t->name)) + ) { + /* A memory dereference. */ + (*pn)++; + if ((offset= bas_get_C_expression(pn)) == nil) return nil; + if (get_token(*pn)->symbol != ']') { + parse_err(1, t, "operand syntax error\n"); + del_expr(offset); + return nil; + } + (*pn)++; + e= new_expr(); + e->operator= '('; + e->middle= offset; + return e; + } + + /* #something? *something? */ + if ((c= get_token(*pn)->symbol) == '#' || c == '*') { + /* '#' and '*' are often used to introduce some constant. */ + (*pn)++; + } + + /* Offset? */ + if (get_token(*pn)->symbol != '[') { + /* There is an offset. */ + if ((offset= bas_get_C_expression(pn)) == nil) return nil; + } else { + /* No offset. */ + offset= nil; + } + + /* [base]? [base+? base-? */ + c= 0; + if (get_token(*pn)->symbol == '[' + && (t= get_token(*pn + 1))->type == T_WORD + && isregister(t->name) + && ((c= get_token(*pn + 2)->symbol) == ']' || c=='+' || c=='-') + ) { + /* A base register expression. */ + base= new_expr(); + base->operator= 'B'; + base->name= copystr(t->name); + (*pn)+= c == ']' ? 3 : 2; + } else { + /* No base register expression. */ + base= nil; + } + + /* +offset]? -offset]? */ + if (offset == nil + && (c == '+' || c == '-') + && (t= get_token(*pn + 1))->type == T_WORD + && !isregister(t->name) + ) { + (*pn)++; + if ((offset= bas_get_C_expression(pn)) == nil) return nil; + if (get_token(*pn)->symbol != ']') { + parse_err(1, t, "operand syntax error\n"); + del_expr(offset); + del_expr(base); + return nil; + } + (*pn)++; + c= 0; + } + + /* [index*scale]? +index*scale]? */ + if (c == '+' || get_token(*pn)->symbol == '[') { + /* An index most likely. */ + token_t *m= nil; + + if (!( /* This must be true: */ + (t= get_token(*pn + 1))->type == T_WORD + && isregister(t->name) + && (get_token(*pn + 2)->symbol == ']' || ( + get_token(*pn + 2)->symbol == '*' + && (m= get_token(*pn + 3))->type == T_WORD + && strchr("1248", m->name[0]) != nil + && m->name[1] == 0 + && get_token(*pn + 4)->symbol == ']' + )) + )) { + /* Alas it isn't */ + parse_err(1, t, "operand syntax error\n"); + del_expr(offset); + del_expr(base); + return nil; + } + /* Found an index. */ + index= new_expr(); + index->operator= m == nil ? '1' : m->name[0]; + index->name= copystr(t->name); + (*pn)+= (m == nil ? 3 : 5); + } else { + /* No index. */ + index= nil; + } + + if (base == nil && index == nil) { + /* Return a lone offset as is. */ + e= offset; + + /* Lone registers tell operand size. */ + if (offset->operator == 'W' && isregister(offset->name)) { + switch (isregister(offset->name)) { + case 1: optype= BYTE; break; + case 2: optype= use16() ? WORD : OWORD; break; + case 4: optype= use32() ? WORD : OWORD; break; + } + if (op_idx < arraysize(optypes)) + optypes[op_idx++]= optype; + } + } else { + e= new_expr(); + e->operator= 'O'; + e->left= offset; + e->middle= base; + e->right= index; + } + return e; +} + +static expression_t *bas_get_oplist(int *pn) +/* Get a comma (or colon for jmpf and callf) separated list of instruction + * operands. + */ +{ + expression_t *e, *o1, *o2; + token_t *t; + + if ((e= bas_get_operand(pn)) == nil) return nil; + + if ((t= get_token(*pn))->symbol == ',' || t->symbol == ':') { + o1= e; + (*pn)++; + if ((o2= bas_get_oplist(pn)) == nil) { + del_expr(o1); + return nil; + } + e= new_expr(); + e->operator= ','; + e->left= o1; + e->right= o2; + } + return e; +} + +static asm86_t *bas_get_statement(void) +/* Get a pseudo op or machine instruction with arguments. */ +{ + token_t *t= get_token(0); + asm86_t *a; + mnemonic_t *m; + int n; + int prefix_seen; + + + assert(t->type == T_WORD); + + if (strcmp(t->name, ".sect") == 0) { + /* .sect .text etc. Accept only four segment names. */ + skip_token(1); + t= get_token(0); + if (t->type != T_WORD || ( + strcmp(t->name, ".text") != 0 + && strcmp(t->name, ".rom") != 0 + && strcmp(t->name, ".data") != 0 + && strcmp(t->name, ".bss") != 0 + && strcmp(t->name, ".end") != 0 + )) { + parse_err(1, t, "weird section name to .sect\n"); + return nil; + } + } + a= new_asm86(); + + /* Process instruction prefixes. */ + for (prefix_seen= 0;; prefix_seen= 1) { + if (strcmp(t->name, "rep") == 0 + || strcmp(t->name, "repe") == 0 + || strcmp(t->name, "repne") == 0 + || strcmp(t->name, "repz") == 0 + || strcmp(t->name, "repnz") == 0 + ) { + if (a->rep != ONCE) { + parse_err(1, t, + "can't have more than one rep\n"); + } + switch (t->name[3]) { + case 0: a->rep= REP; break; + case 'e': + case 'z': a->rep= REPE; break; + case 'n': a->rep= REPNE; break; + } + } else + if (strcmp(t->name, "seg") == 0 + && get_token(1)->type == T_WORD) { + if (a->seg != DEFSEG) { + parse_err(1, t, + "can't have more than one segment prefix\n"); + } + switch (get_token(1)->name[0]) { + case 'c': a->seg= CSEG; break; + case 'd': a->seg= DSEG; break; + case 'e': a->seg= ESEG; break; + case 'f': a->seg= FSEG; break; + case 'g': a->seg= GSEG; break; + case 's': a->seg= SSEG; break; + } + skip_token(1); + } else + if (!prefix_seen) { + /* No prefix here, get out! */ + break; + } else { + /* No more prefixes, next must be an instruction. */ + if (t->type != T_WORD + || (m= search_mnem(t->name)) == nil + || m->optype == PSEUDO + ) { + parse_err(1, t, + "machine instruction expected after instruction prefix\n"); + del_asm86(a); + return nil; + } + break; + } + + /* Skip the prefix and extra newlines. */ + do { + skip_token(1); + } while ((t= get_token(0))->symbol == ';'); + } + + /* All the readahead being done upsets the line counter. */ + a->line= t->line; + + /* Read a machine instruction or pseudo op. */ + if ((m= search_mnem(t->name)) == nil) { + parse_err(1, t, "unknown instruction '%s'\n", t->name); + del_asm86(a); + return nil; + } + a->opcode= m->opcode; + a->optype= m->optype; + if (a->opcode == CBW || a->opcode == CWD) { + a->optype= (strcmp(t->name, "cbw") == 0 + || strcmp(t->name, "cwd") == 0) == use16() ? WORD : OWORD; + } + for (op_idx= 0; op_idx < arraysize(optypes); op_idx++) + optypes[op_idx]= m->optype; + op_idx= 0; + + n= 1; + if (get_token(1)->symbol != ';' + && (a->args= bas_get_oplist(&n)) == nil) { + del_asm86(a); + return nil; + } + + if (m->optype == WORD) { + /* Does one of the operands overide the optype? */ + for (op_idx= 0; op_idx < arraysize(optypes); op_idx++) { + if (optypes[op_idx] != m->optype) + a->optype= optypes[op_idx]; + } + } + + if (get_token(n)->symbol != ';') { + parse_err(1, t, "garbage at end of instruction\n"); + del_asm86(a); + return nil; + } + switch (a->opcode) { + case DOT_ALIGN: + /* Restrict .align to have a single numeric argument, some + * assemblers think of the argument as a power of two, so + * we need to be able to change the value. + */ + if (strcmp(t->name, ".even") == 0 && a->args == nil) { + /* .even becomes .align 2. */ + expression_t *e; + a->args= e= new_expr(); + e->operator= 'W'; + e->name= copystr("2"); + e->len= 2; + } + if (a->args == nil || a->args->operator != 'W' + || !isanumber(a->args->name)) { + parse_err(1, t, + ".align is restricted to one numeric argument\n"); + del_asm86(a); + return nil; + } + break; + case MOVSX: + case MOVZX: + /* Types of both operands tell the instruction type. */ + a->optype= optypes[0]; + if (optypes[1] == BYTE) { + a->opcode= a->opcode == MOVSX ? MOVSXB : MOVZXB; + } + break; + case SAL: + case SAR: + case SHL: + case SHR: + case RCL: + case RCR: + case ROL: + case ROR: + /* Only the first operand tells the operand size. */ + a->optype= optypes[0]; + break; + default:; + } + skip_token(n+1); + return a; +} + +asm86_t *bas_get_instruction(void) +{ + asm86_t *a= nil; + expression_t *e; + token_t *t; + + while ((t= get_token(0))->symbol == ';') + skip_token(1); + + if (t->type == T_EOF) return nil; + + if (t->symbol == '#') { + /* Preprocessor line and file change. */ + + if ((t= get_token(1))->type != T_WORD || !isanumber(t->name) + || get_token(2)->type != T_STRING + ) { + parse_err(1, t, "file not preprocessed?\n"); + zap(); + } else { + set_file(get_token(2)->name, + strtol(get_token(1)->name, nil, 0) - 1); + + /* GNU CPP adds extra cruft, simply zap the line. */ + zap(); + } + a= bas_get_instruction(); + } else + if (t->type == T_WORD && get_token(1)->symbol == ':') { + /* A label definition. */ + a= new_asm86(); + a->line= t->line; + a->opcode= DOT_LABEL; + a->optype= PSEUDO; + a->args= e= new_expr(); + e->operator= ':'; + e->name= copystr(t->name); + skip_token(2); + } else + if (t->type == T_WORD && get_token(1)->symbol == '=') { + int n= 2; + + if ((e= bas_get_C_expression(&n)) == nil) { + zap(); + a= bas_get_instruction(); + } else + if (get_token(n)->symbol != ';') { + parse_err(1, t, "garbage after assignment\n"); + zap(); + a= bas_get_instruction(); + } else { + a= new_asm86(); + a->line= t->line; + a->opcode= DOT_EQU; + a->optype= PSEUDO; + a->args= new_expr(); + a->args->operator= '='; + a->args->name= copystr(t->name); + a->args->middle= e; + skip_token(n+1); + } + } else + if (t->type == T_WORD && get_token(1)->type == T_WORD + && strcmp(get_token(1)->name, "lcomm") == 0) { + /* Local common block definition. */ + int n= 2; + + if ((e= bas_get_C_expression(&n)) == nil) { + zap(); + a= bas_get_instruction(); + } else + if (get_token(n)->symbol != ';') { + parse_err(1, t, "garbage after lcomm\n"); + zap(); + a= bas_get_instruction(); + } else { + a= new_asm86(); + a->line= t->line; + a->opcode= DOT_LCOMM; + a->optype= PSEUDO; + a->args= new_expr(); + a->args->operator= ','; + a->args->right= e; + a->args->left= e= new_expr(); + e->operator= 'W'; + e->name= copystr(t->name); + e->len= strlen(e->name)+1; + skip_token(n+1); + } + } else + if (t->type == T_WORD) { + if ((a= bas_get_statement()) == nil) { + zap(); + a= bas_get_instruction(); + } + } else { + parse_err(1, t, "syntax error\n"); + zap(); + a= bas_get_instruction(); + } + if (a->optype == OWORD) { + a->optype= WORD; + a->oaz|= OPZ; + } + return a; +} diff --git a/commands/i386/asmconv/parse_gnu.c b/commands/i386/asmconv/parse_gnu.c new file mode 100755 index 000000000..bac4a6fd2 --- /dev/null +++ b/commands/i386/asmconv/parse_gnu.c @@ -0,0 +1,879 @@ +/* parse_ack.c - parse GNU assembly Author: R.S. Veldema + * <rveldema@cs.vu.nl> + * 26 Aug 1996 + */ +#define nil 0 +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <assert.h> +#include "asmconv.h" +#include "token.h" +#include "asm86.h" +#include "languages.h" + +typedef struct mnemonic { /* GNU as86 mnemonics translation table. */ + char *name; + opcode_t opcode; + optype_t optype; +} mnemonic_t; + +static mnemonic_t mnemtab[] = { /* This array is sorted. */ + { ".align", DOT_ALIGN, PSEUDO }, + { ".ascii", DOT_ASCII, PSEUDO }, + { ".asciz", DOT_ASCIZ, PSEUDO }, + { ".assert", DOT_ASSERT, PSEUDO }, + { ".base", DOT_BASE, PSEUDO }, + { ".bss", DOT_BSS, PSEUDO }, + { ".byte", DOT_DATA1, PSEUDO }, + { ".comm", DOT_COMM, PSEUDO }, + { ".data", DOT_DATA, PSEUDO }, + { ".data1", DOT_DATA1, PSEUDO }, + { ".data2", DOT_DATA2, PSEUDO }, + { ".data4", DOT_DATA4, PSEUDO }, + { ".end", DOT_END, PSEUDO }, + { ".extern", DOT_EXTERN, PSEUDO }, + { ".file", DOT_FILE, PSEUDO }, + { ".globl", DOT_DEFINE, PSEUDO }, + { ".lcomm", DOT_LCOMM, PSEUDO }, + { ".line", DOT_LINE, PSEUDO }, + { ".list", DOT_LIST, PSEUDO }, + { ".long", DOT_DATA4, PSEUDO }, + { ".nolist", DOT_NOLIST, PSEUDO }, + { ".rom", DOT_ROM, PSEUDO }, + { ".space", DOT_SPACE, PSEUDO }, + { ".symb", DOT_SYMB, PSEUDO }, + { ".text", DOT_TEXT, PSEUDO }, + { ".word", DOT_DATA2, PSEUDO }, + { "aaa", AAA, WORD }, + { "aad", AAD, WORD }, + { "aam", AAM, WORD }, + { "aas", AAS, WORD }, + { "adcb", ADC, BYTE }, + { "adcl", ADC, WORD }, + { "adcw", ADC, OWORD }, + { "addb", ADD, BYTE }, + { "addl", ADD, WORD }, + { "addw", ADD, OWORD }, + { "andb", AND, BYTE }, + { "andl", AND, WORD }, + { "andw", AND, OWORD }, + { "arpl", ARPL, WORD }, + { "bound", BOUND, WORD }, + { "bsf", BSF, WORD }, + { "bsr", BSR, WORD }, + { "bswap", BSWAP, WORD }, + { "btc", BTC, WORD }, + { "btl", BT, WORD }, + { "btr", BTR, WORD }, + { "bts", BTS, WORD }, + { "btw", BT, OWORD }, + { "call", CALL, JUMP }, + { "callf", CALLF, JUMP }, + { "cbtw", CBW, OWORD }, + { "cbw", CBW, WORD }, + { "cdq", CWD, WORD }, + { "clc", CLC, WORD }, + { "cld", CLD, WORD }, + { "cli", CLI, WORD }, + { "cltd", CWD, WORD }, + { "clts", CLTS, WORD }, + { "cmc", CMC, WORD }, + { "cmpb", CMP, BYTE }, + { "cmpl", CMP, WORD }, + { "cmps", CMPS, WORD }, + { "cmpsb", CMPS, BYTE }, + { "cmpw", CMP, OWORD }, + { "cmpxchg", CMPXCHG, WORD }, + { "cwd", CWD, WORD }, + { "cwde", CBW, WORD }, + { "cwtd", CWD, OWORD }, + { "cwtl", CBW, WORD }, + { "daa", DAA, WORD }, + { "das", DAS, WORD }, + { "decb", DEC, BYTE }, + { "decl", DEC, WORD }, + { "decw", DEC, OWORD }, + { "divb", DIV, BYTE }, + { "divl", DIV, WORD }, + { "divw", DIV, OWORD }, + { "enter", ENTER, WORD }, + { "f2xm1", F2XM1, WORD }, + { "fabs", FABS, WORD }, + { "fadd", FADD, WORD }, + { "faddd", FADDD, WORD }, + { "faddp", FADDP, WORD }, + { "fadds", FADDS, WORD }, + { "fbld", FBLD, WORD }, + { "fbstp", FBSTP, WORD }, + { "fchs", FCHS, WORD }, + { "fcomd", FCOMD, WORD }, + { "fcompd", FCOMPD, WORD }, + { "fcompp", FCOMPP, WORD }, + { "fcomps", FCOMPS, WORD }, + { "fcoms", FCOMS, WORD }, + { "fcos", FCOS, WORD }, + { "fdecstp", FDECSTP, WORD }, + { "fdivd", FDIVD, WORD }, + { "fdivp", FDIVP, WORD }, + { "fdivrd", FDIVRD, WORD }, + { "fdivrp", FDIVRP, WORD }, + { "fdivrs", FDIVRS, WORD }, + { "fdivs", FDIVS, WORD }, + { "ffree", FFREE, WORD }, + { "fiaddl", FIADDL, WORD }, + { "fiadds", FIADDS, WORD }, + { "ficom", FICOM, WORD }, + { "ficomp", FICOMP, WORD }, + { "fidivl", FIDIVL, WORD }, + { "fidivrl", FIDIVRL, WORD }, + { "fidivrs", FIDIVRS, WORD }, + { "fidivs", FIDIVS, WORD }, + { "fildl", FILDL, WORD }, + { "fildq", FILDQ, WORD }, + { "filds", FILDS, WORD }, + { "fimull", FIMULL, WORD }, + { "fimuls", FIMULS, WORD }, + { "fincstp", FINCSTP, WORD }, + { "fistl", FISTL, WORD }, + { "fistp", FISTP, WORD }, + { "fists", FISTS, WORD }, + { "fisubl", FISUBL, WORD }, + { "fisubrl", FISUBRL, WORD }, + { "fisubrs", FISUBRS, WORD }, + { "fisubs", FISUBS, WORD }, + { "fld1", FLD1, WORD }, + { "fldcw", FLDCW, WORD }, + { "fldd", FLDD, WORD }, + { "fldenv", FLDENV, WORD }, + { "fldl2e", FLDL2E, WORD }, + { "fldl2t", FLDL2T, WORD }, + { "fldlg2", FLDLG2, WORD }, + { "fldln2", FLDLN2, WORD }, + { "fldpi", FLDPI, WORD }, + { "flds", FLDS, WORD }, + { "fldx", FLDX, WORD }, + { "fldz", FLDZ, WORD }, + { "fmuld", FMULD, WORD }, + { "fmulp", FMULP, WORD }, + { "fmuls", FMULS, WORD }, + { "fnclex", FCLEX, WORD }, + { "fninit", FINIT, WORD }, + { "fnop", FNOP, WORD }, + { "fnsave", FSAVE, WORD }, + { "fnstcw", FSTCW, WORD }, + { "fnstenv", FSTENV, WORD }, + { "fpatan", FPATAN, WORD }, + { "fprem", FPREM, WORD }, + { "fprem1", FPREM1, WORD }, + { "fptan", FPTAN, WORD }, + { "frndint", FRNDINT, WORD }, + { "frstor", FRSTOR, WORD }, + { "fscale", FSCALE, WORD }, + { "fsin", FSIN, WORD }, + { "fsincos", FSINCOS, WORD }, + { "fsqrt", FSQRT, WORD }, + { "fstd", FSTD, WORD }, + { "fstpd", FSTPD, WORD }, + { "fstps", FSTPS, WORD }, + { "fstpx", FSTPX, WORD }, + { "fsts", FSTS, WORD }, + { "fstsw", FSTSW, WORD }, + { "fsubd", FSUBD, WORD }, + { "fsubp", FSUBP, WORD }, + { "fsubpr", FSUBPR, WORD }, + { "fsubrd", FSUBRD, WORD }, + { "fsubrs", FSUBRS, WORD }, + { "fsubs", FSUBS, WORD }, + { "ftst", FTST, WORD }, + { "fucom", FUCOM, WORD }, + { "fucomp", FUCOMP, WORD }, + { "fucompp", FUCOMPP, WORD }, + { "fxam", FXAM, WORD }, + { "fxch", FXCH, WORD }, + { "fxtract", FXTRACT, WORD }, + { "fyl2x", FYL2X, WORD }, + { "fyl2xp1", FYL2XP1, WORD }, + { "hlt", HLT, WORD }, + { "idivb", IDIV, BYTE }, + { "idivl", IDIV, WORD }, + { "idivw", IDIV, OWORD }, + { "imulb", IMUL, BYTE }, + { "imull", IMUL, WORD }, + { "imulw", IMUL, OWORD }, + { "inb", IN, BYTE }, + { "incb", INC, BYTE }, + { "incl", INC, WORD }, + { "incw", INC, OWORD }, + { "inl", IN, WORD }, + { "insb", INS, BYTE }, + { "insl", INS, WORD }, + { "insw", INS, OWORD }, + { "int", INT, WORD }, + { "into", INTO, JUMP }, + { "invd", INVD, WORD }, + { "invlpg", INVLPG, WORD }, + { "inw", IN, OWORD }, + { "iret", IRET, JUMP }, + { "iretd", IRETD, JUMP }, + { "ja", JA, JUMP }, + { "jae", JAE, JUMP }, + { "jb", JB, JUMP }, + { "jbe", JBE, JUMP }, + { "jc", JB, JUMP }, + { "jcxz", JCXZ, JUMP }, + { "je", JE, JUMP }, + { "jecxz", JCXZ, JUMP }, + { "jg", JG, JUMP }, + { "jge", JGE, JUMP }, + { "jl", JL, JUMP }, + { "jle", JLE, JUMP }, + { "jmp", JMP, JUMP }, + { "jmpf", JMPF, JUMP }, + { "jna", JBE, JUMP }, + { "jnae", JB, JUMP }, + { "jnb", JAE, JUMP }, + { "jnbe", JA, JUMP }, + { "jnc", JAE, JUMP }, + { "jne", JNE, JUMP }, + { "jng", JLE, JUMP }, + { "jnge", JL, JUMP }, + { "jnl", JGE, JUMP }, + { "jnle", JG, JUMP }, + { "jno", JNO, JUMP }, + { "jnp", JNP, JUMP }, + { "jns", JNS, JUMP }, + { "jnz", JNE, JUMP }, + { "jo", JO, JUMP }, + { "jp", JP, JUMP }, + { "js", JS, JUMP }, + { "jz", JE, JUMP }, + { "lahf", LAHF, WORD }, + { "lar", LAR, WORD }, + { "lds", LDS, WORD }, + { "leal", LEA, WORD }, + { "leave", LEAVE, WORD }, + { "leaw", LEA, OWORD }, + { "les", LES, WORD }, + { "lfs", LFS, WORD }, + { "lgdt", LGDT, WORD }, + { "lgs", LGS, WORD }, + { "lidt", LIDT, WORD }, + { "lldt", LLDT, WORD }, + { "lmsw", LMSW, WORD }, + { "lock", LOCK, WORD }, + { "lods", LODS, WORD }, + { "lodsb", LODS, BYTE }, + { "loop", LOOP, JUMP }, + { "loope", LOOPE, JUMP }, + { "loopne", LOOPNE, JUMP }, + { "loopnz", LOOPNE, JUMP }, + { "loopz", LOOPE, JUMP }, + { "lsl", LSL, WORD }, + { "lss", LSS, WORD }, + { "ltr", LTR, WORD }, + { "movb", MOV, BYTE }, + { "movl", MOV, WORD }, + { "movsb", MOVS, BYTE }, + { "movsbl", MOVSXB, WORD }, + { "movsbw", MOVSXB, OWORD }, + { "movsl", MOVS, WORD }, + { "movsw", MOVS, OWORD }, + { "movswl", MOVSX, WORD }, + { "movw", MOV, OWORD }, + { "movzbl", MOVZXB, WORD }, + { "movzbw", MOVZXB, OWORD }, + { "movzwl", MOVZX, WORD }, + { "mulb", MUL, BYTE }, + { "mull", MUL, WORD }, + { "mulw", MUL, OWORD }, + { "negb", NEG, BYTE }, + { "negl", NEG, WORD }, + { "negw", NEG, OWORD }, + { "nop", NOP, WORD }, + { "notb", NOT, BYTE }, + { "notl", NOT, WORD }, + { "notw", NOT, OWORD }, + { "orb", OR, BYTE }, + { "orl", OR, WORD }, + { "orw", OR, OWORD }, + { "outb", OUT, BYTE }, + { "outl", OUT, WORD }, + { "outsb", OUTS, BYTE }, + { "outsl", OUTS, WORD }, + { "outsw", OUTS, OWORD }, + { "outw", OUT, OWORD }, + { "pop", POP, WORD }, + { "popa", POPA, WORD }, + { "popad", POPA, WORD }, + { "popf", POPF, WORD }, + { "popl", POP, WORD }, + { "push", PUSH, WORD }, + { "pusha", PUSHA, WORD }, + { "pushad", PUSHA, WORD }, + { "pushf", PUSHF, WORD }, + { "pushl", PUSH, WORD }, + { "rclb", RCL, BYTE }, + { "rcll", RCL, WORD }, + { "rclw", RCL, OWORD }, + { "rcrb", RCR, BYTE }, + { "rcrl", RCR, WORD }, + { "rcrw", RCR, OWORD }, + { "ret", RET, JUMP }, + { "retf", RETF, JUMP }, + { "rolb", ROL, BYTE }, + { "roll", ROL, WORD }, + { "rolw", ROL, OWORD }, + { "rorb", ROR, BYTE }, + { "rorl", ROR, WORD }, + { "rorw", ROR, OWORD }, + { "sahf", SAHF, WORD }, + { "salb", SAL, BYTE }, + { "sall", SAL, WORD }, + { "salw", SAL, OWORD }, + { "sarb", SAR, BYTE }, + { "sarl", SAR, WORD }, + { "sarw", SAR, OWORD }, + { "sbbb", SBB, BYTE }, + { "sbbl", SBB, WORD }, + { "sbbw", SBB, OWORD }, + { "scasb", SCAS, BYTE }, + { "scasl", SCAS, WORD }, + { "scasw", SCAS, OWORD }, + { "seta", SETA, BYTE }, + { "setae", SETAE, BYTE }, + { "setb", SETB, BYTE }, + { "setbe", SETBE, BYTE }, + { "sete", SETE, BYTE }, + { "setg", SETG, BYTE }, + { "setge", SETGE, BYTE }, + { "setl", SETL, BYTE }, + { "setna", SETBE, BYTE }, + { "setnae", SETB, BYTE }, + { "setnb", SETAE, BYTE }, + { "setnbe", SETA, BYTE }, + { "setne", SETNE, BYTE }, + { "setng", SETLE, BYTE }, + { "setnge", SETL, BYTE }, + { "setnl", SETGE, BYTE }, + { "setnle", SETG, BYTE }, + { "setno", SETNO, BYTE }, + { "setnp", SETNP, BYTE }, + { "setns", SETNS, BYTE }, + { "seto", SETO, BYTE }, + { "setp", SETP, BYTE }, + { "sets", SETS, BYTE }, + { "setz", SETE, BYTE }, + { "sgdt", SGDT, WORD }, + { "shlb", SHL, BYTE }, + { "shldl", SHLD, WORD }, + { "shll", SHL, WORD }, + { "shlw", SHL, OWORD }, + { "shrb", SHR, BYTE }, + { "shrdl", SHRD, WORD }, + { "shrl", SHR, WORD }, + { "shrw", SHR, OWORD }, + { "sidt", SIDT, WORD }, + { "sldt", SLDT, WORD }, + { "smsw", SMSW, WORD }, + { "stc", STC, WORD }, + { "std", STD, WORD }, + { "sti", STI, WORD }, + { "stosb", STOS, BYTE }, + { "stosl", STOS, WORD }, + { "stosw", STOS, OWORD }, + { "str", STR, WORD }, + { "subb", SUB, BYTE }, + { "subl", SUB, WORD }, + { "subw", SUB, OWORD }, + { "testb", TEST, BYTE }, + { "testl", TEST, WORD }, + { "testw", TEST, OWORD }, + { "verr", VERR, WORD }, + { "verw", VERW, WORD }, + { "wait", WAIT, WORD }, + { "wbinvd", WBINVD, WORD }, + { "xadd", XADD, WORD }, + { "xchgb", XCHG, BYTE }, + { "xchgl", XCHG, WORD }, + { "xchgw", XCHG, OWORD }, + { "xlat", XLAT, WORD }, + { "xorb", XOR, BYTE }, + { "xorl", XOR, WORD }, + { "xorw", XOR, OWORD }, +}; + +void gnu_parse_init(char *file) +/* Prepare parsing of an GNU assembly file. */ +{ + tok_init(file, '#'); +} + +static void zap(void) +/* An error, zap the rest of the line. */ +{ + token_t *t; + + while ((t= get_token(0))->type != T_EOF && t->symbol != ';') + skip_token(1); +} + +static mnemonic_t *search_mnem(char *name) +/* Binary search for a mnemonic. (That's why the table is sorted.) */ +{ + int low, mid, high; + int cmp; + mnemonic_t *m; + + low= 0; + high= arraysize(mnemtab)-1; + while (low <= high) { + mid= (low + high) / 2; + m= &mnemtab[mid]; + + if ((cmp= strcmp(name, m->name)) == 0) return m; + + if (cmp < 0) high= mid-1; else low= mid+1; + } + return nil; +} + +static expression_t *gnu_get_C_expression(int *pn) +/* Read a "C-like" expression. Note that we don't worry about precedence, + * the expression is printed later like it is read. If the target language + * does not have all the operators (like ~) then this has to be repaired by + * changing the source file. (No problem, you still have one source file + * to maintain, not two.) + */ +{ + expression_t *e, *a1, *a2; + token_t *t; + + if ((t= get_token(*pn))->symbol == '(') { + /* ( expr ): grouping. */ + (*pn)++; + if ((a1= gnu_get_C_expression(pn)) == nil) return nil; + if (get_token(*pn)->symbol != ')') { + parse_err(1, t, "missing )\n"); + del_expr(a1); + return nil; + } + (*pn)++; + e= new_expr(); + e->operator= '['; + e->middle= a1; + } else + if (t->type == T_WORD || t->type == T_STRING) { + /* Label, number, or string. */ + e= new_expr(); + e->operator= t->type == T_WORD ? 'W' : 'S'; + e->name= allocate(nil, (t->len+1) * sizeof(e->name[0])); + memcpy(e->name, t->name , t->len+1); + e->len= t->len; + (*pn)++; + } else + if (t->symbol == '+' || t->symbol == '-' || t->symbol == '~') { + /* Unary operator. */ + (*pn)++; + if ((a1= gnu_get_C_expression(pn)) == nil) return nil; + e= new_expr(); + e->operator= t->symbol; + e->middle= a1; + } else { + parse_err(1, t, "expression syntax error\n"); + return nil; + } + + switch ((t= get_token(*pn))->symbol) { + case '%': + case '+': + case '-': + case '*': + case '/': + case '&': + case '|': + case '^': + case S_LEFTSHIFT: + case S_RIGHTSHIFT: + (*pn)++; + a1= e; + if ((a2= gnu_get_C_expression(pn)) == nil) { + del_expr(a1); + return nil; + } + e= new_expr(); + e->operator= t->symbol; + e->left= a1; + e->right= a2; + } + return e; +} + +static expression_t *gnu_get_operand(int *pn, int deref) +/* Get something like: $immed, memory, offset(%base,%index,scale), or simpler. */ +{ + expression_t *e, *offset, *base, *index; + token_t *t; + int c; + + if (get_token(*pn)->symbol == '$') { + /* An immediate value. */ + (*pn)++; + return gnu_get_C_expression(pn); + } + + if (get_token(*pn)->symbol == '*') { + /* Indirection. */ + (*pn)++; + if ((offset= gnu_get_operand(pn, deref)) == nil) return nil; + e= new_expr(); + e->operator= '('; + e->middle= offset; + return e; + } + + if ((get_token(*pn)->symbol == '%') + && (t= get_token(*pn + 1))->type == T_WORD + && isregister(t->name) + ) { + /* A register operand. */ + (*pn)+= 2; + e= new_expr(); + e->operator= 'W'; + e->name= copystr(t->name); + return e; + } + + /* Offset? */ + if (get_token(*pn)->symbol != '(' + || get_token(*pn + 1)->symbol != '%') { + /* There is an offset. */ + if ((offset= gnu_get_C_expression(pn)) == nil) return nil; + } else { + /* No offset. */ + offset= nil; + } + + /* (%base,%index,scale) ? */ + base= index= nil; + if (get_token(*pn)->symbol == '(') { + (*pn)++; + + /* %base ? */ + if (get_token(*pn)->symbol == '%' + && (t= get_token(*pn + 1))->type == T_WORD + && isregister(t->name) + ) { + /* A base register expression. */ + base= new_expr(); + base->operator= 'B'; + base->name= copystr(t->name); + (*pn)+= 2; + } + + if (get_token(*pn)->symbol == ',') (*pn)++; + + /* %index ? */ + if (get_token(*pn)->symbol == '%' + && (t= get_token(*pn + 1))->type == T_WORD + && isregister(t->name) + ) { + /* A index register expression. */ + index= new_expr(); + index->operator= '1'; /* for now */ + index->name= copystr(t->name); + (*pn)+= 2; + } + + if (get_token(*pn)->symbol == ',') (*pn)++; + + /* scale ? */ + if ((base != nil || index != nil) + && (t= get_token(*pn))->type == T_WORD + && strchr("1248", t->name[0]) != nil + && t->name[1] == 0 + ) { + if (index == nil) { + /* Base is really an index register. */ + index= base; + base= nil; + } + index->operator= t->name[0]; + (*pn)++; + } + + if (get_token(*pn)->symbol == ')') { + /* Ending paren. */ + (*pn)++; + } else { + /* Alas. */ + parse_err(1, t, "operand syntax error\n"); + del_expr(offset); + del_expr(base); + del_expr(index); + return nil; + } + } + + if (base == nil && index == nil) { + if (deref) { + /* Return a lone offset as (offset). */ + e= new_expr(); + e->operator= '('; + e->middle= offset; + } else { + /* Return a lone offset as is. */ + e= offset; + } + } else { + e= new_expr(); + e->operator= 'O'; + e->left= offset; + + e->middle= base; + e->right= index; + } + return e; +} + +static expression_t *gnu_get_oplist(int *pn, int deref) +/* Get a comma (or colon for jmpf and callf) separated list of instruction + * operands. + */ +{ + expression_t *e, *o1, *o2; + token_t *t; + + if ((e= gnu_get_operand(pn, deref)) == nil) return nil; + + if ((t= get_token(*pn))->symbol == ',' || t->symbol == ':') { + o1= e; + (*pn)++; + if ((o2= gnu_get_oplist(pn, deref)) == nil) { + del_expr(o1); + return nil; + } + e= new_expr(); + e->operator= ','; + e->left= o1; + e->right= o2; + } + return e; +} + + +static asm86_t *gnu_get_statement(void) +/* Get a pseudo op or machine instruction with arguments. */ +{ + token_t *t= get_token(0); + asm86_t *a; + mnemonic_t *m; + int n; + int prefix_seen; + int deref; + + assert(t->type == T_WORD); + + a= new_asm86(); + + /* Process instruction prefixes. */ + for (prefix_seen= 0;; prefix_seen= 1) { + if (strcmp(t->name, "rep") == 0 + || strcmp(t->name, "repe") == 0 + || strcmp(t->name, "repne") == 0 + || strcmp(t->name, "repz") == 0 + || strcmp(t->name, "repnz") == 0 + ) { + if (a->rep != ONCE) { + parse_err(1, t, + "can't have more than one rep\n"); + } + switch (t->name[3]) { + case 0: a->rep= REP; break; + case 'e': + case 'z': a->rep= REPE; break; + case 'n': a->rep= REPNE; break; + } + } else + if (!prefix_seen) { + /* No prefix here, get out! */ + break; + } else { + /* No more prefixes, next must be an instruction. */ + if (t->type != T_WORD + || (m= search_mnem(t->name)) == nil + || m->optype == PSEUDO + ) { + parse_err(1, t, + "machine instruction expected after instruction prefix\n"); + del_asm86(a); + return nil; + } + break; + } + + /* Skip the prefix and extra newlines. */ + do { + skip_token(1); + } while ((t= get_token(0))->symbol == ';'); + } + + /* All the readahead being done upsets the line counter. */ + a->line= t->line; + + /* Read a machine instruction or pseudo op. */ + if ((m= search_mnem(t->name)) == nil) { + parse_err(1, t, "unknown instruction '%s'\n", t->name); + del_asm86(a); + return nil; + } + a->opcode= m->opcode; + a->optype= m->optype; + a->oaz= 0; + if (a->optype == OWORD) { + a->oaz|= OPZ; + a->optype= WORD; + } + + switch (a->opcode) { + case IN: + case OUT: + case INT: + deref= 0; + break; + default: + deref= (a->optype >= BYTE); + } + n= 1; + if (get_token(1)->symbol != ';' + && (a->args= gnu_get_oplist(&n, deref)) == nil) { + del_asm86(a); + return nil; + } + if (get_token(n)->symbol != ';') { + parse_err(1, t, "garbage at end of instruction\n"); + del_asm86(a); + return nil; + } + if (!is_pseudo(a->opcode)) { + /* GNU operand order is the other way around. */ + expression_t *e, *t; + + e= a->args; + while (e != nil && e->operator == ',') { + t= e->right; e->right= e->left; e->left= t; + e= e->left; + } + } + switch (a->opcode) { + case DOT_ALIGN: + /* Delete two argument .align, because ACK can't do it. + * Raise 2 to the power of .align's argument. + */ + if (a->args == nil || a->args->operator != 'W') { + del_asm86(a); + return nil; + } + if (a->args != nil && a->args->operator == 'W' + && isanumber(a->args->name) + ) { + unsigned n; + char num[sizeof(int) * CHAR_BIT / 3 + 1]; + + n= 1 << strtoul(a->args->name, nil, 0); + sprintf(num, "%u", n); + deallocate(a->args->name); + a->args->name= copystr(num); + } + break; + case JMPF: + case CALLF: + /*FALL THROUGH*/ + case JMP: + case CALL: + break; + default:; + } + skip_token(n+1); + return a; +} + + +asm86_t *gnu_get_instruction(void) +{ + asm86_t *a= nil; + expression_t *e; + token_t *t; + + while ((t= get_token(0))->symbol == ';' || t->symbol == '/') { + zap(); /* if a comment started by a '/' */ + skip_token(1); + } + + if (t->type == T_EOF) return nil; + + if (t->symbol == '#') { + /* Preprocessor line and file change. */ + + if ((t= get_token(1))->type != T_WORD || !isanumber(t->name) + || get_token(2)->type != T_STRING + ) { + parse_err(1, t, "file not preprocessed?\n"); + zap(); + } else { + set_file(get_token(2)->name, + strtol(get_token(1)->name, nil, 0) - 1); + + /* GNU CPP adds extra cruft, simply zap the line. */ + zap(); + } + a= gnu_get_instruction(); + } else + if (t->type == T_WORD && get_token(1)->symbol == ':') { + /* A label definition. */ + + a= new_asm86(); + a->line= t->line; + a->opcode= DOT_LABEL; + a->optype= PSEUDO; + a->args= e= new_expr(); + e->operator= ':'; + e->name= copystr(t->name); + skip_token(2); + } else + if (t->type == T_WORD && get_token(1)->symbol == '=') { + int n= 2; + + if ((e= gnu_get_C_expression(&n)) == nil) { + zap(); + a= gnu_get_instruction(); + } else + if (get_token(n)->symbol != ';') { + parse_err(1, t, "garbage after assignment\n"); + zap(); + a= gnu_get_instruction(); + } else { + a= new_asm86(); + a->line= t->line; + a->opcode= DOT_EQU; + a->optype= PSEUDO; + a->args= new_expr(); + a->args->operator= '='; + a->args->name= copystr(t->name); + a->args->middle= e; + skip_token(n+1); + } + } else + if (t->type == T_WORD) { + if ((a= gnu_get_statement()) == nil) { + zap(); + a= gnu_get_instruction(); + } + } else { + parse_err(1, t, "syntax error\n"); + zap(); + a= gnu_get_instruction(); + } + return a; +} diff --git a/commands/i386/asmconv/syntax.ack b/commands/i386/asmconv/syntax.ack new file mode 100755 index 000000000..8281b7c27 --- /dev/null +++ b/commands/i386/asmconv/syntax.ack @@ -0,0 +1,107 @@ +asmprog: + comment ? + statement + asmprog ; asmprog + asmprog comment ? \n asmprog + +letter: + [._a-zA-Z] + +digit: + [0-9] + +identifier: + letter (letter | digit)* + digit [bf] + +string: + 'C-like string sequence' + "C-like string sequence" + +number: + C-like number + +comment: + ! .* + +statement: + label-definition statement + empty + assignment + instruction + pseudo-instruction + +label-definition: + identifier : + digit : + +assignment: + identifier = expression + +instruction: + iX86-instruction + +pseudo-instruction: + .extern identifier (, identifier)* + .define identifier (, identifier)* + .data1 expression (, expression)* + .data2 expression (, expression)* + .data4 expression (, expression)* + .ascii string + .asciz string + .align expression + .space expression + .comm identifier , expression + .sect identifier + .base expression + .assert expression + .symb XXX + .line XXX + .file XXX + .nolist + .list + iX86-pseudo + +expression: + C-like expression with [ and ] for grouping + +iX86-instruction: + prefix + prefix iX86-instruction + identifier + identifier iX86operand + identifier iX86operand , iX86operand + identifier iX86operand : iX86operand + +prefix: + o16 + o32 + a16 + a32 + rep + repz + repnz + repe + repne + cseg | dseg | eseg | fseg | gseg | sseg + +iX86operand: + register + ( register ) + expression + ( expression ) + expression ( register ) + expression ( register * [1248] ) + expression ? ( register ) ( register ) + expression ? ( register ) ( register * [1248] ) + +register: + al | bl | cl | dl | ah | bh | ch | dh + ax | bx | cx | dx | si | di | bp | sp + eax | ebx | ecx | edx | esi | edi | ebp | esp + cs | ds | es | fs | gs | ss + cr0 | cr1 | cr2 | cr3 + +iX86-pseudo: + .use16 + .use32 diff --git a/commands/i386/asmconv/token.h b/commands/i386/asmconv/token.h new file mode 100755 index 000000000..f6720d18b --- /dev/null +++ b/commands/i386/asmconv/token.h @@ -0,0 +1,29 @@ +/* token.h - token definition Author: Kees J. Bot + * 13 Dec 1993 + */ + +typedef enum toktype { + T_EOF, + T_CHAR, + T_WORD, + T_STRING +} toktype_t; + +typedef struct token { + struct token *next; + long line; + toktype_t type; + int symbol; /* Single character symbol. */ + char *name; /* Word, number, etc. */ + size_t len; /* Length of string. */ +} token_t; + +#define S_LEFTSHIFT 0x100 /* << */ +#define S_RIGHTSHIFT 0x101 /* >> */ + +void set_file(char *file, long line); +void get_file(char **file, long *line); +void parse_err(int err, token_t *where, const char *fmt, ...); +void tok_init(char *file, int comment); +token_t *get_token(int n); +void skip_token(int n); diff --git a/commands/i386/asmconv/tokenize.c b/commands/i386/asmconv/tokenize.c new file mode 100755 index 000000000..222c52581 --- /dev/null +++ b/commands/i386/asmconv/tokenize.c @@ -0,0 +1,306 @@ +/* tokenize.c - split input into tokens Author: Kees J. Bot + * 13 Dec 1993 + */ +#define nil 0 +#include <stdio.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include "asmconv.h" +#include "token.h" + +static FILE *tf; +static char *tfile; +static char *orig_tfile; +static int tcomment; +static int tc; +static long tline; +static token_t *tq; + +static void readtc(void) +/* Read one character from the input file and put it in the global 'tc'. */ +{ + static int nl= 0; + + if (nl) tline++; + if ((tc= getc(tf)) == EOF && ferror(tf)) fatal(orig_tfile); + nl= (tc == '\n'); +} + +void set_file(char *file, long line) +/* Set file name and line number, changed by a preprocessor trick. */ +{ + deallocate(tfile); + tfile= allocate(nil, (strlen(file) + 1) * sizeof(tfile[0])); + strcpy(tfile, file); + tline= line; +} + +void get_file(char **file, long *line) +/* Get file name and line number. */ +{ + *file= tfile; + *line= tline; +} + +void parse_err(int err, token_t *t, const char *fmt, ...) +/* Report a parsing error. */ +{ + va_list ap; + + fprintf(stderr, "\"%s\", line %ld: ", tfile, + t == nil ? tline : t->line); + va_start(ap, fmt); + vfprintf(stderr, fmt, ap); + va_end(ap); + if (err) set_error(); +} + +void tok_init(char *file, int comment) +/* Open the file to tokenize and initialize the tokenizer. */ +{ + if (file == nil) { + file= "stdin"; + tf= stdin; + } else { + if ((tf= fopen(file, "r")) == nil) fatal(file); + } + orig_tfile= file; + set_file(file, 1); + readtc(); + tcomment= comment; +} + +static int isspace(int c) +{ + return between('\0', c, ' ') && c != '\n'; +} + +#define iscomment(c) ((c) == tcomment) + +static int isidentchar(int c) +{ + return between('a', c, 'z') + || between('A', c, 'Z') + || between('0', c, '9') + || c == '.' + || c == '_' + ; +} + +static token_t *new_token(void) +{ + token_t *new; + + new= allocate(nil, sizeof(*new)); + new->next= nil; + new->line= tline; + new->name= nil; + new->symbol= -1; + return new; +} + +static token_t *get_word(void) +/* Read one word, an identifier, a number, a label, or a mnemonic. */ +{ + token_t *w; + char *name; + size_t i, len; + + i= 0; + len= 16; + name= allocate(nil, len * sizeof(name[0])); + + while (isidentchar(tc)) { + name[i++]= tc; + readtc(); + if (i == len) name= allocate(name, (len*= 2) * sizeof(name[0])); + } + name[i]= 0; + name= allocate(name, (i+1) * sizeof(name[0])); + w= new_token(); + w->type= T_WORD; + w->name= name; + w->len= i; + return w; +} + +static token_t *get_string(void) +/* Read a single or double quotes delimited string. */ +{ + token_t *s; + int quote; + char *str; + size_t i, len; + int n, j; + int seen; + + quote= tc; + readtc(); + + i= 0; + len= 16; + str= allocate(nil, len * sizeof(str[0])); + + while (tc != quote && tc != '\n' && tc != EOF) { + seen= -1; + if (tc == '\\') { + readtc(); + if (tc == '\n' || tc == EOF) break; + + switch (tc) { + case 'a': tc= '\a'; break; + case 'b': tc= '\b'; break; + case 'f': tc= '\f'; break; + case 'n': tc= '\n'; break; + case 'r': tc= '\r'; break; + case 't': tc= '\t'; break; + case 'v': tc= '\v'; break; + case 'x': + n= 0; + for (j= 0; j < 3; j++) { + readtc(); + if (between('0', tc, '9')) + tc-= '0' + 0x0; + else + if (between('A', tc, 'A')) + tc-= 'A' + 0xA; + else + if (between('a', tc, 'a')) + tc-= 'a' + 0xa; + else { + seen= tc; + break; + } + n= n*0x10 + tc; + } + tc= n; + break; + default: + if (!between('0', tc, '9')) break; + n= 0; + for (j= 0; j < 3; j++) { + if (between('0', tc, '9')) + tc-= '0'; + else { + seen= tc; + break; + } + n= n*010 + tc; + readtc(); + } + tc= n; + } + } + str[i++]= tc; + if (i == len) str= allocate(str, (len*= 2) * sizeof(str[0])); + + if (seen < 0) readtc(); else tc= seen; + } + + if (tc == quote) { + readtc(); + } else { + parse_err(1, nil, "string contains newline\n"); + } + str[i]= 0; + str= allocate(str, (i+1) * sizeof(str[0])); + s= new_token(); + s->type= T_STRING; + s->name= str; + s->len= i; + return s; +} + +static int old_n= 0; /* To speed up n, n+1, n+2, ... accesses. */ +static token_t **old_ptq= &tq; + +token_t *get_token(int n) +/* Return the n-th token on the input queue. */ +{ + token_t *t, **ptq; + + assert(n >= 0); + + if (0 && n >= old_n) { + /* Go forward from the previous point. */ + n-= old_n; + old_n+= n; + ptq= old_ptq; + } else { + /* Restart from the head of the queue. */ + old_n= n; + ptq= &tq; + } + + for (;;) { + if ((t= *ptq) == nil) { + /* Token queue doesn't have element <n>, read a + * new token from the input stream. + */ + while (isspace(tc) || iscomment(tc)) { + if (iscomment(tc)) { + while (tc != '\n' && tc != EOF) + readtc(); + } else { + readtc(); + } + } + + if (tc == EOF) { + t= new_token(); + t->type= T_EOF; + } else + if (isidentchar(tc)) { + t= get_word(); + } else + if (tc == '\'' || tc == '"') { + t= get_string(); + } else { + if (tc == '\n') tc= ';'; + t= new_token(); + t->type= T_CHAR; + t->symbol= tc; + readtc(); + if (t->symbol == '<' && tc == '<') { + t->symbol= S_LEFTSHIFT; + readtc(); + } else + if (t->symbol == '>' && tc == '>') { + t->symbol= S_RIGHTSHIFT; + readtc(); + } + } + *ptq= t; + } + if (n == 0) break; + n--; + ptq= &t->next; + } + old_ptq= ptq; + return t; +} + +void skip_token(int n) +/* Remove n tokens from the input queue. One is not allowed to skip unread + * tokens. + */ +{ + token_t *junk; + + assert(n >= 0); + + while (n > 0) { + assert(tq != nil); + + junk= tq; + tq= tq->next; + deallocate(junk->name); + deallocate(junk); + n--; + } + /* Reset the old reference. */ + old_n= 0; + old_ptq= &tq; +} diff --git a/commands/i386/mtools-3.9.7/COPYING b/commands/i386/mtools-3.9.7/COPYING new file mode 100755 index 000000000..a4d5375a4 --- /dev/null +++ b/commands/i386/mtools-3.9.7/COPYING @@ -0,0 +1,346 @@ +Copyright (C) 1995 Alain Knaff. + You may use, distribute and copy this program according to the terms of the +GNU General Public License version 2 or later. + + Alain Knaff +---------------------------------------- + + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) 19yy <name of author> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/commands/i386/mtools-3.9.7/Makefile b/commands/i386/mtools-3.9.7/Makefile new file mode 100755 index 000000000..e39632777 --- /dev/null +++ b/commands/i386/mtools-3.9.7/Makefile @@ -0,0 +1,399 @@ +# Generated automatically from Makefile.in by configure. +# +# Makefile for Mtools +# +# check the Configure file for some examples of device-specific setups +# Berkeley flavors of Unix should include -DBSD in the CFLAGS. Pick +# a lock method... either -DLOCKF, -DFLOCK, or -DFCNTL and put that +# string in the CFLAGS line below. + +# User specified flags +USERCFLAGS = +USERLDFLAGS = -stack 11m +USERLDLIBS = + +MAKEINFO = makeinfo +TEXI2DVI = texi2dvi +TEXI2HTML = texi2html + + +# do not edit below this line +# ============================================================================= + +SHELL = /bin/sh + +top_srcdir=. +srcdir=. + +prefix = /usr +exec_prefix = ${prefix} +bindir = ${exec_prefix}/bin +infodir = ${prefix}/info +mandir = ${prefix}/man +infodir = ${prefix}/info +sysconfdir = /etc + +CC = cc -D_MINIX +CXX = @CXX@ +MYCFLAGS = +MYCXXFLAGS = +CPPFLAGS = +HOST_ID = -DCPU_i386 -DVENDOR_pc -DOS_Minix +DEFS = -DHAVE_CONFIG_H -DNO_CONFIG $(HOST_ID) + +LDFLAGS = +LIBS = +SHLIB = +MACHDEPLIBS = +LN_S = ln -s + +INSTALL = /usr/bin/install -cs +INSTALL_PROGRAM = ${INSTALL} +INSTALL_DATA = ${INSTALL} -m 644 +INSTALL_INFO = + +.SUFFIXES: +.SUFFIXES: .o .c +.SUFFIXES: .o .c + +MAN1 = floppyd.1 mattrib.1 mbadblocks.1 mcat.1 mcd.1 mcopy.1 mdel.1 mdeltree.1 mdir.1 \ +mdu.1 mformat.1 mkmanifest.1 mlabel.1 mmd.1 mmount.1 mmove.1 mpartition.1 \ +mrd.1 mread.1 mren.1 mshowfat.1 mtoolstest.1 mtools.1 mtype.1 mzip.1 +MAN1EXT = 1 +MAN1DIR = $(mandir)/man${MAN1EXT} +MAN5 = mtools.5 +MAN5EXT = 5 +MAN5DIR = $(mandir)/man${MAN5EXT} + +# all files in this directory included in the distribution +DIST = \ +COPYING Changelog INSTALL Makefile Makefile.in README Release.notes \ +buffer.c buffer.h codepage.c codepage.h codepages.c config.c \ +config.guess config.h.in config.log config.sub configure configure.in \ +copyfile.c devices.c devices.h dirCache.c dirCache.h directory.c direntry.c \ +expand.c fat.c \ +fat_free.c file.c file.h file_name.c file_read.c files filter.c floppyd.1 \ +floppyd.c floppyd_io.c floppyd_io.h force_io.c fs.h fsP.h \ +getopt.h hash.c htable.h init.c llong.c mainloop.c match.c mattrib.1 \ +mattrib.c mbadblocks.1 mbadblocks.c mcat.1 mcat.c mcd.1 mcd.c mcopy.1 \ +mcopy.c mdel.1 mdel.c mdeltree.1 mdir.1 mdir.c mdu.c mdu.1 mformat.1 \ +mformat.c minfo.c \ +misc.c tty.c scsi.c missFuncs.c mk_direntry.c mkmanifest.1 mkmanifest.c \ +mlabel.1 mlabel.c mmd.1 mmd.c mmount.1 mmount.c mmove.1 mmove.c \ +mpartition.1 mpartition.c mrd.1 \ +mread.1 mren.1 msdos.h mshowfat.1 mtoolstest.1 mtools.1 mtools.5 mtools.c \ +mtools.conf mtools.h mtype.1 nameclash.h patchlevel.c \ +plain_io.c plain_io.h precmd.c privileges.c scripts signal.c stream.c stream.h \ +streamcache.c streamcache.h subdir.c sysincludes.h unixdir.c todo toupper.c \ +vfat.c vfat.h xdf_io.c xdf_io.h + +#OBJS1 = buffer.o codepage.o codepages.o config.o copyfile.o devices.o \ +#dirCache.o directory.o direntry.o expand.o fat.o fat_free.o file.o file_name.o \ +#file_read.o filter.o floppyd_io.o force_io.o hash.o init.o llong.o match.o \ +#mainloop.o mattrib.o mbadblocks.o mcat.o mcd.o mcopy.o mdel.o mdir.o \ +#mdoctorfat.o mdu.o \ +#mformat.o minfo.o misc.o missFuncs.o mk_direntry.o mlabel.o mmd.o mmount.o \ +#mmove.o mpartition.o mshowfat.o mzip.o mtools.o patchlevel.o plain_io.o \ +#precmd.o privileges.o scsi.o signal.o stream.o streamcache.o subdir.o \ +#unixdir.o toupper.o tty.o vfat.o xdf_io.o + +OBJS1 = buffer.o config.o copyfile.o devices.o \ +dirCache.o directory.o direntry.o expand.o fat.o fat_free.o file.o file_name.o \ +file_read.o filter.o floppyd_io.o force_io.o hash.o init.o llong.o match.o \ +mainloop.o mattrib.o mbadblocks.o mcat.o mcd.o mcopy.o mdel.o mdir.o \ +mdoctorfat.o mdu.o \ +mformat.o minfo.o misc.o missFuncs.o mk_direntry.o mlabel.o mmd.o mmount.o \ +mmove.o mpartition.o mshowfat.o mtools.o patchlevel.o plain_io.o \ +precmd.o privileges.o scsi.o signal.o stream.o streamcache.o subdir.o \ +unixdir.o toupper.o tty.o vfat.o xdf_io.o + +OBJS2 = missFuncs.o mkmanifest.o misc.o patchlevel.o + +SRCS3 = floppyd.c + +OBJS4 = floppyd_installtest.o misc.o expand.o privileges.o + +SRCS = buffer.c codepage.c codepages.c config.c copyfile.c devices.c \ +dirCache.c directory.c direntry.c expand.c fat.c fat_free.c file.c file_name.c \ +file_read.c filter.c floppyd_io.c force_io.c hash.c init.c match.c mainloop.c \ +mattrib.c mbadblocks.c mcat.c mcd.c mcopy.c mdel.c mdir.c mdu.c mdoctorfat.c \ +mformat.c minfo.c misc.c \ +missFuncs.c mk_direntry.c mlabel.c mmd.c mmount.c mmove.c mpartition.c \ +mshowfat.c mzip.c mtools.c plain_io.c precmd.c privileges.c scsi.o \ +signal.c stream.c streamcache.c subdir.c unixdir.c toupper.c tty.o vfat.c \ +xdf_io.c mkmanifest.c + + +SCRIPTS = mcheck mxtar uz tgz mcomp + +LINKS=mattrib mcat mcd mcopy mdel mdeltree mdir mdu mformat minfo mlabel \ +mmd mmount mmove mpartition mrd mread mren mtype mtoolstest mshowfat \ +mbadblocks mzip + +X_CFLAGS = +X_LIBS = +X_EXTRA_LIBS = +X_PRE_LIBS = +CFLAGS = $(CPPFLAGS) $(DEFS) $(MYCFLAGS) -I. -I. $(USERCFLAGS) +CXXFLAGS = $(CPPFLAGS) $(DEFS) $(MYCXXFLAGS) -I. -I. $(USERCFLAGS) +LINK = $(CC) $(LDFLAGS) $(USERLDFLAGS) +ALLLIBS = $(USERLDLIBS) $(MACHDEPLIBS) $(SHLIB) $(LIBS) +X_LDFLAGS = $(X_EXTRA_LIBS) $(X_LIBS) $(X_PRE_LIBS) -lXau -lX11 $(LIBS) +X_CCFLAGS = $(X_CFLAGS) $(CFLAGS) + +all: mtools + +%.o: %.c + $(CC) $(CFLAGS) -c $< + +#%.o: %.cpp +# $(CXX) $(CXXFLAGS) -c $< + +mtools: $(OBJS1) + $(LINK) $(OBJS1) -o $@ $(ALLLIBS) + +mkmanifest: $(OBJS2) + $(LINK) $(OBJS2) -o $@ $(ALLLIBS) + +floppyd.o: floppyd.c + $(CC) $(X_CCFLAGS) -c $< + +floppyd: floppyd.o + $(LINK) $< -o $@ $(X_LDFLAGS) +floppyd_installtest: $(OBJS4) + $(LINK) $(OBJS4) -o $@ $(ALLLIBS) + + +$(LINKS): mtools + rm -f $@ && $(LN_S) mtools $@ + +mostlyclean: + -rm -f *~ *.orig *.o a.out core 2>/dev/null + +clean: mostlyclean + -rm -f mtools $(LINKS) floppyd floppyd_installtest mkmanifest *.info* *.dvi *.html 2>/dev/null + + +texclean: + -rm mtools.aux mtools.toc mtools.log + -rm mtools.cps mtools.pgs mtools.vrs + -rm mtools.cp mtools.fn mtools.ky + -rm mtools.pg mtools.tp mtools.vr + +info: mtools.info +%.info: %.texi + $(MAKEINFO) -I$(srcdir) $< --no-split --output=$@ + +dvi: mtools.dvi +%.dvi: %.texi + $(TEXI2DVI) $< + +ps: mtools.ps +%.ps: %.dvi + dvips -f < $< > $@ + +pdf: mtools.pdf +%.pdf: %.ps + ps2pdf $< $@ + + +html: mtools.html mtools_toc.html +%.html %_toc.html: %.texi + $(TEXI2HTML) $< + +# Don't cd, to avoid breaking install-sh references. +install-info: info + $(top_srcdir)/mkinstalldirs $(infodir) + if test -f mtools.info; then \ + for i in mtools.info*; do \ + $(INSTALL_DATA) $$i $(infodir)/$$i; \ + done; \ + else \ + for i in $(srcdir)/mtools.info*; do \ + $(INSTALL_DATA) $$i $(infodir)/`echo $$i | sed 's|^$(srcdir)/||'`; \ + done; \ + fi; \ + if [ -n "$(INSTALL_INFO)" ] ; then \ + if [ -f $(infodir)/dir.info ] ; then \ + $(INSTALL_INFO) $(infodir)/mtools.info $(infodir)/dir.info; \ + fi; \ + if [ -f $(infodir)/dir ] ; then \ + $(INSTALL_INFO) $(infodir)/mtools.info $(infodir)/dir; \ + fi; \ + fi + +uninstall-info: + cd $(infodir) && rm -f mtools.info* + +install: $(bindir)/mtools + +# The manual pages are of such horrible quality that one is better off without +# them. (Frankly this whole package is horrible.) Using -? hopefully gives +# enough clues for use. -- kjb +dontinstall: + $(MAN1DIR)/mattrib.1 $(MAN1DIR)/mbadblocks.1 \ + $(MAN1DIR)/mcd.1 $(MAN1DIR)/mcopy.1 $(MAN1DIR)/mdel.1 \ + $(MAN1DIR)/mdeltree.1 $(MAN1DIR)/mdir.1 $(MAN1DIR)/mdu.1 \ + $(MAN1DIR)/mformat.1 $(MAN1DIR)/mlabel.1 \ + $(MAN1DIR)/mmd.1 $(MAN1DIR)/mmove.1 $(MAN1DIR)/mrd.1 \ + $(MAN1DIR)/mread.1 $(MAN1DIR)/mren.1 \ + $(MAN1DIR)/mshowfat.1 $(MAN1DIR)/mtools.1 \ + $(MAN1DIR)/mtype.1 $(MAN1DIR)/mzip.1 + +$(bindir)/mtools: mtools + install -c $? $@ + +$(MAN1DIR)/mattrib.1: mattrib.1 + install -lc $? $@ + +$(MAN1DIR)/mbadblocks.1: mbadblocks.1 + install -lc $? $@ + +$(MAN1DIR)/mcd.1: mcd.1 + install -lc $? $@ + +$(MAN1DIR)/mcopy.1: mcopy.1 + install -lc $? $@ + +$(MAN1DIR)/mdel.1: mdel.1 + install -lc $? $@ + +$(MAN1DIR)/mdeltree.1: mdeltree.1 + install -lc $? $@ + +$(MAN1DIR)/mdir.1: mdir.1 + install -lc $? $@ + +$(MAN1DIR)/mdu.1: mdu.1 + install -lc $? $@ + +$(MAN1DIR)/mformat.1: mformat.1 + install -lc $? $@ + +$(MAN1DIR)/mlabel.1: mlabel.1 + install -lc $? $@ + +$(MAN1DIR)/mmd.1: mmd.1 + install -lc $? $@ + +$(MAN1DIR)/mmove.1: mmove.1 + install -lc $? $@ + +$(MAN1DIR)/mrd.1: mrd.1 + install -lc $? $@ + +$(MAN1DIR)/mread.1: mread.1 + install -lc $? $@ + +$(MAN1DIR)/mren.1: mren.1 + install -lc $? $@ + +$(MAN1DIR)/mshowfat.1: mshowfat.1 + install -lc $? $@ + +$(MAN1DIR)/mtools.1: mtools.1 + install -lc $? $@ + +$(MAN1DIR)/mtype.1: mtype.1 + install -lc $? $@ + +$(MAN1DIR)/mzip.1: mzip.1 + install -lc $? $@ + +#install: $(bindir)/mtools install-man install-links \ +# $(bindir)/mkmanifest install-scripts install-info +# +#uninstall: uninstall-bin uninstall-man uninstall-links \ +# uninstall-scripts + +distclean: clean texclean + rm -f config.cache config.h config.status config.log Makefile +maintainer-clean: distclean + + +#$(bindir)/floppyd: floppyd +# $(top_srcdir)/mkinstalldirs $(bindir) +# $(INSTALL_PROGRAM) floppyd $(bindir)/floppyd +# +#$(bindir)/floppyd_installtest: floppyd_installtest +# $(top_srcdir)/mkinstalldirs $(bindir) +# $(INSTALL_PROGRAM) floppyd_installtest $(bindir)/floppyd_installtest +# +#$(bindir)/mtools: mtools +# $(top_srcdir)/mkinstalldirs $(bindir) +# $(INSTALL_PROGRAM) mtools $(bindir)/mtools +# +#$(bindir)/mkmanifest: mkmanifest +# $(top_srcdir)/mkinstalldirs $(bindir) +# $(INSTALL_PROGRAM) mkmanifest $(bindir)/mkmanifest + +#$(ETCDIR)/mtools: mtools.etc +# cp mtools.etc $(ETCDIR)/mtools + +install-links: $(bindir)/mtools + @for j in $(LINKS); do \ + rm -f $(bindir)/$$j ; \ + $(LN_S) mtools $(bindir)/$$j ; \ + echo $(bindir)/$$j ; \ + done + +## "z" is the older version of "gz"; the name is just *too* short +install-scripts: $(bindir)/mtools + @$(top_srcdir)/mkinstalldirs $(bindir) + @for j in $(SCRIPTS) ; do \ + $(INSTALL_PROGRAM) $(srcdir)/scripts/$$j $(bindir)/$$j ; \ + echo $(bindir)/$$j ; \ + done + rm -f $(bindir)/lz + $(LN_S) uz $(bindir)/lz + +install-man: + @$(top_srcdir)/mkinstalldirs $(MAN1DIR) + @for j in $(MAN1); do \ + $(INSTALL_DATA) $(srcdir)/$$j $(MAN1DIR)/$$j ; \ + echo $(MAN1DIR)/$$j ; \ + done + @$(top_srcdir)/mkinstalldirs $(MAN5DIR) + @for j in $(MAN5); do \ + $(INSTALL_DATA) $(srcdir)/$$j $(MAN5DIR)/$$j ; \ + echo $(MAN5DIR)/$$j ; \ + done + +uninstall-bin: + @for j in mtools mkmanifest; do \ + rm -f $(bindir)/$$j ; \ + echo $(bindir)/$$j ; \ + done + +uninstall-scripts: + @for j in $(SCRIPTS); do \ + rm -f $(bindir)/$$j ; \ + echo $(bindir)/$$j ; \ + done + +uninstall-man: + @for j in $(MAN1); do \ + rm -f $(MAN1DIR)/$$j ; \ + echo $(MAN1DIR)/$$j ; \ + done + @for j in $(MAN5); do \ + rm -f $(MAN5DIR)/$$j ; \ + echo $(MAN5DIR)/$$j ; \ + done + +uninstall-links: + @for j in $(LINKS); \ + do rm -f $(bindir)/$$j ; \ + echo $(bindir)/$$j ; \ + done + +depend: $(SRCS) + makedepend -- $(CFLAGS) -- $^ + +check: + echo No self tests included +# check target needed even if empty, in order to make life easyer for +# automatic tools to install GNU soft + + +# DO NOT DELETE THIS LINE -- make depend depends on it. diff --git a/commands/i386/mtools-3.9.7/README b/commands/i386/mtools-3.9.7/README new file mode 100755 index 000000000..17aae7c55 --- /dev/null +++ b/commands/i386/mtools-3.9.7/README @@ -0,0 +1,53 @@ +Compilation +----------- + + Type 'make install'. + + This package has been heavily modified for Minix, and then minimized to +only those files needed for Minix. Don't use this as a basis for anything +else, get the original package instead. -- kjb + +Doc +--- + + The most uptodate doc of this package is the texinfo doc. Type 'make +info' to get online info doc, and 'make dvi ; dvips mtools.dvi' to get +a printed copy. The info doc has a concept index. Make use of it. + You may get an info copy using the following command 'make info'. +This can then be viewed using emacs' info mode, or using a standalone +info viewer. + Man pages are still present, but contain less information, and are +less uptodate than the texinfo documentation. + If you do not have the necessary tools to view the texinfo doc, you +may also find it on the Workd Wide Web at the following locations: + http://mtools.linux.lu/mtools_toc.html + http://www.tux.org/pub/knaff/mtools/mtools_toc.html + +Compiler +-------- + + Mtools should be compiled with an Ansi compiler, preferably gcc + +Authors +------- + +Original code (versions through 2.0.7?) by Emmet P. Gray (Texas, USA), who +no longer appears to be reachable by Internet e-mail. Viktor Dukhovni (at +Princeton, USA) had major input into v2.0. + +Since 2.0.7: maintained primarily and until now informally by Alain +Knaff (Luxembourg) and David Niemi (Reston, Virginia, USA). + +Please report bugs to the mtools mailing list at mtools@www.tux.org. +Before reporting any problems, check whether they have already been +fixed in the Alpha patches at http://mtools.linux.lu and +http://www.tux.org/pub/knaff + +You may subscribe to the mtools mailing list by sending a message +containing 'subscribe mtools' in its body to majordomo@www.tux.org + + +Current Status +-------------- + +Stable release 3.3. diff --git a/commands/i386/mtools-3.9.7/buffer.c b/commands/i386/mtools-3.9.7/buffer.c new file mode 100755 index 000000000..deccfea78 --- /dev/null +++ b/commands/i386/mtools-3.9.7/buffer.c @@ -0,0 +1,365 @@ +/* + * Buffer read/write module + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "buffer.h" + +typedef struct Buffer_t { + Class_t *Class; + int refs; + Stream_t *Next; + Stream_t *Buffer; + + size_t size; /* size of read/write buffer */ + int dirty; /* is the buffer dirty? */ + + int sectorSize; /* sector size: all operations happen + * in multiples of this */ + int cylinderSize; /* cylinder size: preferred alignemnt, + * but for efficiency, less data may be read */ + int ever_dirty; /* was the buffer ever dirty? */ + int dirty_pos; + int dirty_end; + mt_off_t current; /* first sector in buffer */ + size_t cur_size; /* the current size */ + char *buf; /* disk read/write buffer */ +} Buffer_t; + +/* + * Flush a dirty buffer to disk. Resets Buffer->dirty to zero. + * All errors are fatal. + */ + +static int _buf_flush(Buffer_t *Buffer) +{ + int ret; + + if (!Buffer->Next || !Buffer->dirty) + return 0; + if(Buffer->current < 0L) { + fprintf(stderr,"Should not happen\n"); + return -1; + } +#ifdef DEBUG + fprintf(stderr, "write %08x -- %02x %08x %08x\n", + Buffer, + (unsigned char) Buffer->buf[0], + Buffer->current + Buffer->dirty_pos, + Buffer->dirty_end - Buffer->dirty_pos); +#endif + + ret = force_write(Buffer->Next, + Buffer->buf + Buffer->dirty_pos, + Buffer->current + Buffer->dirty_pos, + Buffer->dirty_end - Buffer->dirty_pos); + if(ret != Buffer->dirty_end - Buffer->dirty_pos) { + if(ret < 0) + perror("buffer_flush: write"); + else + fprintf(stderr,"buffer_flush: short write\n"); + return -1; + } + Buffer->dirty = 0; + Buffer->dirty_end = 0; + Buffer->dirty_pos = 0; + return 0; +} + +static int invalidate_buffer(Buffer_t *Buffer, mt_off_t start) +{ + /*fprintf(stderr, "invalidate %x\n", Buffer);*/ + if(Buffer->sectorSize == 32) { + fprintf(stderr, "refreshing directory\n"); + } + + if(_buf_flush(Buffer) < 0) + return -1; + + /* start reading at the beginning of start's sector + * don't start reading too early, or we might not even reach + * start */ + Buffer->current = ROUND_DOWN(start, Buffer->sectorSize); + Buffer->cur_size = 0; + return 0; +} + +#undef OFFSET +#define OFFSET (start - This->current) + +typedef enum position_t { + OUTSIDE, + APPEND, + INSIDE, + ERROR +} position_t; + +static position_t isInBuffer(Buffer_t *This, mt_off_t start, size_t *len) +{ + if(start >= This->current && + start < This->current + This->cur_size) { + maximize(*len, This->cur_size - OFFSET); + return INSIDE; + } else if(start == This->current + This->cur_size && + This->cur_size < This->size && + *len >= This->sectorSize) { + /* append to the buffer for this, three conditions have to + * be met: + * 1. The start falls exactly at the end of the currently + * loaded data + * 2. There is still space + * 3. We append at least one sector + */ + maximize(*len, This->size - This->cur_size); + *len = ROUND_DOWN(*len, This->sectorSize); + return APPEND; + } else { + if(invalidate_buffer(This, start) < 0) + return ERROR; + maximize(*len, This->cylinderSize - OFFSET); + maximize(*len, This->cylinderSize - This->current % This->cylinderSize); + return OUTSIDE; + } +} + +static int buf_read(Stream_t *Stream, char *buf, mt_off_t start, size_t len) +{ + size_t length; + int offset; + char *disk_ptr; + int ret; + DeclareThis(Buffer_t); + + if(!len) + return 0; + + /*fprintf(stderr, "buf read %x %x %x\n", Stream, start, len);*/ + switch(isInBuffer(This, start, &len)) { + case OUTSIDE: + case APPEND: + /* always load until the end of the cylinder */ + length = This->cylinderSize - + (This->current + This->cur_size) % This->cylinderSize; + maximize(length, This->size - This->cur_size); + + /* read it! */ + ret=READS(This->Next, + This->buf + This->cur_size, + This->current + This->cur_size, + length); + if ( ret < 0 ) + return ret; + This->cur_size += ret; + if (This->current+This->cur_size < start) { + fprintf(stderr, "Short buffer fill\n"); + exit(1); + } + break; + case INSIDE: + /* nothing to do */ + break; + case ERROR: + return -1; + } + + offset = OFFSET; + disk_ptr = This->buf + offset; + maximize(len, This->cur_size - offset); + memcpy(buf, disk_ptr, len); + return len; +} + +static int buf_write(Stream_t *Stream, char *buf, mt_off_t start, size_t len) +{ + char *disk_ptr; + DeclareThis(Buffer_t); + int offset, ret; + + if(!len) + return 0; + + This->ever_dirty = 1; + +#ifdef DEBUG + fprintf(stderr, "buf write %x %02x %08x %08x -- %08x %08x -- %08x\n", + Stream, (unsigned char) This->buf[0], + start, len, This->current, This->cur_size, This->size); + fprintf(stderr, "%d %d %d %x %x\n", + start == This->current + This->cur_size, + This->cur_size < This->size, + len >= This->sectorSize, len, This->sectorSize); +#endif + switch(isInBuffer(This, start, &len)) { + case OUTSIDE: +#ifdef DEBUG + fprintf(stderr, "outside\n"); +#endif + if(start % This->cylinderSize || + len < This->sectorSize) { + size_t readSize; + + readSize = This->cylinderSize - + This->current % This->cylinderSize; + + ret=READS(This->Next, This->buf, This->current, readSize); + /* read it! */ + if ( ret < 0 ) + return ret; + This->cur_size = ret; + /* for dosemu. Autoextend size */ + if(!This->cur_size) { + memset(This->buf,0,readSize); + This->cur_size = readSize; + } + offset = OFFSET; + break; + } + /* FALL THROUGH */ + case APPEND: +#ifdef DEBUG + fprintf(stderr, "append\n"); +#endif + len = ROUND_DOWN(len, This->sectorSize); + offset = OFFSET; + maximize(len, This->size - offset); + This->cur_size += len; + if(This->Next->Class->pre_allocate) + PRE_ALLOCATE(This->Next, + This->current + This->cur_size); + break; + case INSIDE: + /* nothing to do */ +#ifdef DEBUG + fprintf(stderr, "inside\n"); +#endif + offset = OFFSET; + maximize(len, This->cur_size - offset); + break; + case ERROR: + return -1; + default: +#ifdef DEBUG + fprintf(stderr, "Should not happen\n"); +#endif + exit(1); + } + + disk_ptr = This->buf + offset; + + /* extend if we write beyond end */ + if(offset + len > This->cur_size) { + len -= (offset + len) % This->sectorSize; + This->cur_size = len + offset; + } + + memcpy(disk_ptr, buf, len); + if(!This->dirty || offset < This->dirty_pos) + This->dirty_pos = ROUND_DOWN(offset, This->sectorSize); + if(!This->dirty || offset + len > This->dirty_end) + This->dirty_end = ROUND_UP(offset + len, This->sectorSize); + + if(This->dirty_end > This->cur_size) { + fprintf(stderr, + "Internal error, dirty end too big %x %x %x %d %x\n", + This->dirty_end, (unsigned int) This->cur_size, (unsigned int) len, + (int) offset, (int) This->sectorSize); + fprintf(stderr, "offset + len + grain - 1 = %x\n", + (int) (offset + len + This->sectorSize - 1)); + fprintf(stderr, "ROUNDOWN(offset + len + grain - 1) = %x\n", + (int)ROUND_DOWN(offset + len + This->sectorSize - 1, + This->sectorSize)); + fprintf(stderr, "This->dirty = %d\n", This->dirty); + exit(1); + } + + This->dirty = 1; + return len; +} + +static int buf_flush(Stream_t *Stream) +{ + int ret; + DeclareThis(Buffer_t); + + if (!This->ever_dirty) + return 0; + ret = _buf_flush(This); + if(ret == 0) + This->ever_dirty = 0; + return ret; +} + + +static int buf_free(Stream_t *Stream) +{ + DeclareThis(Buffer_t); + + if(This->buf) + free(This->buf); + This->buf = 0; + return 0; +} + +static Class_t BufferClass = { + buf_read, + buf_write, + buf_flush, + buf_free, + 0, /* set_geom */ + get_data_pass_through, /* get_data */ + 0, /* pre-allocate */ +}; + +Stream_t *buf_init(Stream_t *Next, int size, + int cylinderSize, + int sectorSize) +{ + Buffer_t *Buffer; + Stream_t *Stream; + + + if(size % cylinderSize != 0) { + fprintf(stderr, "size not multiple of cylinder size\n"); + exit(1); + } + if(cylinderSize % sectorSize != 0) { + fprintf(stderr, "cylinder size not multiple of sector size\n"); + exit(1); + } + + if(Next->Buffer){ + Next->refs--; + Next->Buffer->refs++; + return Next->Buffer; + } + + Stream = (Stream_t *) malloc (sizeof(Buffer_t)); + if(!Stream) + return 0; + Buffer = (Buffer_t *) Stream; + Buffer->buf = malloc(size); + if ( !Buffer->buf){ + Free(Stream); + return 0; + } + Buffer->size = size; + Buffer->dirty = 0; + Buffer->cylinderSize = cylinderSize; + Buffer->sectorSize = sectorSize; + + Buffer->ever_dirty = 0; + Buffer->dirty_pos = 0; + Buffer->dirty_end = 0; + Buffer->current = 0; + Buffer->cur_size = 0; /* buffer currently empty */ + + Buffer->Next = Next; + Buffer->Class = &BufferClass; + Buffer->refs = 1; + Buffer->Buffer = 0; + Buffer->Next->Buffer = (Stream_t *) Buffer; + return Stream; +} + diff --git a/commands/i386/mtools-3.9.7/buffer.h b/commands/i386/mtools-3.9.7/buffer.h new file mode 100755 index 000000000..858164aa7 --- /dev/null +++ b/commands/i386/mtools-3.9.7/buffer.h @@ -0,0 +1,11 @@ +#ifndef MTOOLS_BUFFER_H +#define MTOOLS_BUFFER_H + +#include "stream.h" + +Stream_t *buf_init(Stream_t *Next, + int size, + int cylinderSize, + int sectorSize); + +#endif diff --git a/commands/i386/mtools-3.9.7/codepage.h b/commands/i386/mtools-3.9.7/codepage.h new file mode 100755 index 000000000..d4c651b90 --- /dev/null +++ b/commands/i386/mtools-3.9.7/codepage.h @@ -0,0 +1,35 @@ +typedef struct Codepage_l { + int nr; + unsigned char tounix[128]; +} Codepage_t; + + +typedef struct country_l { + int country; + int codepage; + int default_codepage; + int to_upper; +} country_t; + + +#ifndef NO_CONFIG +void init_codepage(void); +unsigned char to_dos(unsigned char c); +void to_unix(char *a, int n); +#define mstoupper(c) mstoupper[(c) & 0x7F] + +#else /* NO_CONFIG */ + +/* Imagine a codepage with 128 uppercase letters for the top 128 characters. */ +#define mstoupper(c) (c) +#define to_dos(c) (c) +#define to_unix(a, n) ((void) 0) +#define mstoupper(c) (c) +#endif + +extern Codepage_t *Codepage; +extern char *mstoupper; +extern country_t countries[]; +extern unsigned char toucase[][128]; +extern Codepage_t codepages[]; +extern char *country_string; diff --git a/commands/i386/mtools-3.9.7/config.c b/commands/i386/mtools-3.9.7/config.c new file mode 100755 index 000000000..be6a96fb2 --- /dev/null +++ b/commands/i386/mtools-3.9.7/config.c @@ -0,0 +1,808 @@ +#include "sysincludes.h" +#include "mtools.h" +#include "codepage.h" +#include "mtoolsPaths.h" + +/* global variables */ +/* they are not really harmful here, because there is only one configuration + * file per invocations */ + +#ifndef NO_CONFIG + +#define MAX_LINE_LEN 256 + +/* scanner */ +static char buffer[MAX_LINE_LEN+1]; /* buffer for the whole line */ +static char *pos; /* position in line */ +static char *token; /* last scanned token */ +static int token_length; /* length of the token */ +static FILE *fp; /* file pointer for configuration file */ +static int linenumber; /* current line number. Only used for printing + * error messages */ +static int lastTokenLinenumber; /* line numnber for last token */ +static const char *filename; /* current file name. Only used for printing + * error messages */ +static int file_nr=0; + + +static int flag_mask; /* mask of currently set flags */ + +/* devices */ +static int cur_devs; /* current number of defined devices */ +static int cur_dev; /* device being filled in. If negative, none */ +static int trusted=0; /* is the currently parsed device entry trusted? */ +static int nr_dev; /* number of devices that the current table can hold */ +static int token_nr; /* number of tokens in line */ +static char letters[][2] = { /* drive letter to letter-as-a-string */ + "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", +}; + +#endif /* !NO_CONFIG */ + +struct device *devices; /* the device table */ + +/* "environment" variables */ +unsigned int mtools_skip_check=0; +unsigned int mtools_fat_compatibility=0; +unsigned int mtools_ignore_short_case=0; +unsigned int mtools_rate_0=0; +unsigned int mtools_rate_any=0; +unsigned int mtools_no_vfat=0; +unsigned int mtools_numeric_tail=1; +unsigned int mtools_dotted_dir=0; +unsigned int mtools_twenty_four_hour_clock=1; +char *mtools_date_string="mm-dd-yyyy"; +char *country_string=0; + +#ifndef NO_CONFIG + +typedef struct switches_l { + const char *name; + caddr_t address; + enum { + T_INT, + T_STRING, + T_UINT + } type; +} switches_t; + +static switches_t switches[] = { + { "MTOOLS_LOWER_CASE", (caddr_t) & mtools_ignore_short_case, T_UINT }, + { "MTOOLS_FAT_COMPATIBILITY", (caddr_t) & mtools_fat_compatibility, T_UINT }, + { "MTOOLS_SKIP_CHECK", (caddr_t) & mtools_skip_check, T_UINT }, + { "MTOOLS_NO_VFAT", (caddr_t) & mtools_no_vfat, T_UINT }, + { "MTOOLS_RATE_0", (caddr_t) &mtools_rate_0, T_UINT }, + { "MTOOLS_RATE_ANY", (caddr_t) &mtools_rate_any, T_UINT }, + { "MTOOLS_NAME_NUMERIC_TAIL", (caddr_t) &mtools_numeric_tail, T_UINT }, + { "MTOOLS_DOTTED_DIR", (caddr_t) &mtools_dotted_dir, T_UINT }, + { "MTOOLS_TWENTY_FOUR_HOUR_CLOCK", + (caddr_t) &mtools_twenty_four_hour_clock, T_UINT }, + { "MTOOLS_DATE_STRING", + (caddr_t) &mtools_date_string, T_STRING }, + { "COUNTRY", (caddr_t) &country_string, T_STRING } +}; + +typedef struct { + const char *name; + int flag; +} flags_t; + +static flags_t openflags[] = { +#ifdef O_SYNC + { "sync", O_SYNC }, +#endif +#ifdef O_NDELAY + { "nodelay", O_NDELAY }, +#endif +#ifdef O_EXCL + { "exclusive", O_EXCL }, +#endif + { "none", 0 } /* hack for those compilers that choke on commas + * after the last element of an array */ +}; + +static flags_t misc_flags[] = { +#ifdef USE_XDF + { "use_xdf", USE_XDF_FLAG }, +#endif + { "scsi", SCSI_FLAG }, + { "nolock", NOLOCK_FLAG }, + { "mformat_only", MFORMAT_ONLY_FLAG }, + { "filter", FILTER_FLAG }, + { "privileged", PRIV_FLAG }, + { "vold", VOLD_FLAG }, + { "remote", FLOPPYD_FLAG } +}; + +static struct { + const char *name; + signed char fat_bits; + int tracks; + unsigned short heads; + unsigned short sectors; +} default_formats[] = { + { "hd514", 12, 80, 2, 15 }, + { "high-density-5-1/4", 12, 80, 2, 15 }, + { "1.2m", 12, 80, 2, 15 }, + + { "hd312", 12, 80, 2, 18 }, + { "high-density-3-1/2", 12, 80, 2, 18 }, + { "1.44m", 12, 80, 2, 18 }, + + { "dd312", 12, 80, 2, 9 }, + { "double-density-3-1/2", 12, 80, 2, 9 }, + { "720k", 12, 80, 2, 9 }, + + { "dd514", 12, 40, 2, 9 }, + { "double-density-5-1/4", 12, 40, 2, 9 }, + { "360k", 12, 40, 2, 9 }, + + { "320k", 12, 40, 2, 8 }, + { "180k", 12, 40, 1, 9 }, + { "160k", 12, 40, 1, 8 } +}; + +#define OFFS(x) ((caddr_t)&((struct device *)0)->x) + +static switches_t dswitches[]= { + { "FILE", OFFS(name), T_STRING }, + { "OFFSET", OFFS(offset), T_UINT }, + { "PARTITION", OFFS(partition), T_UINT }, + { "FAT", OFFS(fat_bits), T_INT }, + { "FAT_BITS", OFFS(fat_bits), T_UINT }, + { "MODE", OFFS(mode), T_UINT }, + { "TRACKS", OFFS(tracks), T_UINT }, + { "CYLINDERS", OFFS(tracks), T_UINT }, + { "HEADS", OFFS(heads), T_UINT }, + { "SECTORS", OFFS(sectors), T_UINT }, + { "HIDDEN", OFFS(hidden), T_UINT }, + { "PRECMD", OFFS(precmd), T_STRING }, + { "BLOCKSIZE", OFFS(blocksize), T_UINT } +}; + +static void syntax(const char *msg, int thisLine) +{ + char *drive=NULL; + if(thisLine) + lastTokenLinenumber = linenumber; + if(cur_dev >= 0) + drive = devices[cur_dev].drive; + fprintf(stderr,"Syntax error at line %d ", lastTokenLinenumber); + if(drive) fprintf(stderr, "for drive %s: ", drive); + if(token) fprintf(stderr, "column %ld ", (long)(token - buffer)); + fprintf(stderr, "in file %s: %s\n", filename, msg); + exit(1); +} + +static void get_env_conf(void) +{ + char *s; + int i; + + for(i=0; i< sizeof(switches) / sizeof(*switches); i++) { + s = getenv(switches[i].name); + if(s) { + if(switches[i].type == T_INT) + * ((int *)switches[i].address) = (int) strtol(s,0,0); + if(switches[i].type == T_UINT) + * ((int *)switches[i].address) = (unsigned int) strtoul(s,0,0); + else if (switches[i].type == T_STRING) + * ((char **)switches[i].address) = s; + } + } +} + +static int mtools_getline(void) +{ + if(!fgets(buffer, MAX_LINE_LEN, fp)) + return -1; + linenumber++; + pos = buffer; + token_nr = 0; + buffer[MAX_LINE_LEN] = '\0'; + if(strlen(buffer) == MAX_LINE_LEN) + syntax("line too long", 1); + return 0; +} + +static void skip_junk(int expect) +{ + lastTokenLinenumber = linenumber; + while(!pos || !*pos || strchr(" #\n\t", *pos)) { + if(!pos || !*pos || *pos == '#') { + if(mtools_getline()) { + pos = 0; + if(expect) + syntax("end of file unexpected", 1); + return; + } + } else + pos++; + } + token_nr++; +} + +/* get the next token */ +static char *get_next_token(void) +{ + skip_junk(0); + if(!pos) { + token_length = 0; + token = 0; + return 0; + } + token = pos; + token_length = strcspn(token, " \t\n#:="); + pos += token_length; + return token; +} + +static int match_token(const char *template) +{ + return (strlen(template) == token_length && + !strncasecmp(template, token, token_length)); +} + +static void expect_char(char c) +{ + char buf[11]; + + skip_junk(1); + if(*pos != c) { + sprintf(buf, "expected %c", c); + syntax(buf, 1); + } + pos++; +} + +static char *get_string(void) +{ + char *end, *str; + + skip_junk(1); + if(*pos != '"') + syntax(" \" expected", 0); + str = pos+1; + end = strchr(str, '\"'); + if(!end) + syntax("unterminated string constant", 1); + *end = '\0'; + pos = end+1; + return str; +} + +static unsigned int get_unumber(void) +{ + char *last; + unsigned int n; + + skip_junk(1); + last = pos; + n=(unsigned int) strtoul(pos, &pos, 0); + if(last == pos) + syntax("numeral expected", 0); + pos++; + token_nr++; + return n; +} + +static unsigned int get_number(void) +{ + char *last; + int n; + + skip_junk(1); + last = pos; + n=(int) strtol(pos, &pos, 0); + if(last == pos) + syntax("numeral expected", 0); + pos++; + token_nr++; + return n; +} + +/* purge all entries pertaining to a given drive from the table */ +static void purge(char drive, int fn) +{ + int i,j; + + drive = toupper(drive); + for(j=0, i=0; i < cur_devs; i++) { + if(devices[i].drive[0] != drive || + devices[i].drive[1] != 0 || + devices[i].file_nr == fn) + devices[j++] = devices[i]; + } + cur_devs = j; +} + +static void grow(void) +{ + if(cur_devs >= nr_dev - 2) { + nr_dev = (cur_devs + 2) << 1; + if(!(devices=Grow(devices, nr_dev, struct device))){ + printOom(); + exit(1); + } + } +} + + +static void init_drive(void) +{ + memset((char *)&devices[cur_dev], 0, sizeof(struct device)); + devices[cur_dev].ssize = 2; +} + +/* prepends a device to the table */ +static void prepend(void) +{ + int i; + + grow(); + for(i=cur_devs; i>0; i--) + devices[i] = devices[i-1]; + cur_dev = 0; + cur_devs++; + init_drive(); +} + + +/* appends a device to the table */ +static void append(void) +{ + grow(); + cur_dev = cur_devs; + cur_devs++; + init_drive(); +} + + +static void finish_drive_clause(void) +{ + char *drive; + if(cur_dev == -1) { + trusted = 0; + return; + } + drive = devices[cur_dev].drive; + if(!devices[cur_dev].name) + syntax("missing filename", 0); + if(devices[cur_dev].tracks || + devices[cur_dev].heads || + devices[cur_dev].sectors) { + if(!devices[cur_dev].tracks || + !devices[cur_dev].heads || + !devices[cur_dev].sectors) + syntax("incomplete geometry: either indicate all of track/heads/sectors or none of them", 0); + if(!(devices[cur_dev].misc_flags & + (MFORMAT_ONLY_FLAG | FILTER_FLAG))) + syntax("if you supply a geometry, you also must supply one of the `mformat_only' or `filter' flags", 0); + } + devices[cur_dev].file_nr = file_nr; + devices[cur_dev].cfg_filename = filename; + if(! (flag_mask & PRIV_FLAG) && IS_SCSI(&devices[cur_dev])) + devices[cur_dev].misc_flags |= PRIV_FLAG; + if(!trusted && (devices[cur_dev].misc_flags & PRIV_FLAG)) { + fprintf(stderr, + "Warning: privileged flag ignored for drive %s: defined in file %s\n", + devices[cur_dev].drive, filename); + devices[cur_dev].misc_flags &= ~PRIV_FLAG; + } + trusted = 0; + cur_dev = -1; +} + +static int set_var(struct switches_l *switches, int nr, + caddr_t base_address) +{ + int i; + for(i=0; i < nr; i++) { + if(match_token(switches[i].name)) { + expect_char('='); + if(switches[i].type == T_UINT) + * ((int *)((long)switches[i].address+base_address)) = + get_unumber(); + if(switches[i].type == T_INT) + * ((int *)((long)switches[i].address+base_address)) = + get_number(); + else if (switches[i].type == T_STRING) + * ((char**)((long)switches[i].address+base_address))= + strdup(get_string()); + return 0; + } + } + return 1; +} + +static int set_openflags(struct device *dev) +{ + int i; + + for(i=0; i < sizeof(openflags) / sizeof(*openflags); i++) { + if(match_token(openflags[i].name)) { + dev->mode |= openflags[i].flag; + return 0; + } + } + return 1; +} + +static int set_misc_flags(struct device *dev) +{ + int i; + + for(i=0; i < sizeof(misc_flags) / sizeof(*misc_flags); i++) { + if(match_token(misc_flags[i].name)) { + flag_mask |= misc_flags[i].flag; + skip_junk(0); + if(pos && *pos == '=') { + pos++; + switch(get_number()) { + case 0: + return 0; + case 1: + break; + default: + syntax("expected 0 or 1", 0); + } + } + dev->misc_flags |= misc_flags[i].flag; + return 0; + } + } + return 1; +} + +static int set_def_format(struct device *dev) +{ + int i; + + for(i=0; i < sizeof(default_formats)/sizeof(*default_formats); i++) { + if(match_token(default_formats[i].name)) { + if(!dev->ssize) + dev->ssize = 2; + if(!dev->tracks) + dev->tracks = default_formats[i].tracks; + if(!dev->heads) + dev->heads = default_formats[i].heads; + if(!dev->sectors) + dev->sectors = default_formats[i].sectors; + if(!dev->fat_bits) + dev->fat_bits = default_formats[i].fat_bits; + return 0; + } + } + return 1; +} + +static void get_codepage(void) +{ + int i; + unsigned short n; + + if(!Codepage) + Codepage = New(Codepage_t); + for(i=0; i<128; i++) { + n = get_number(); + if(n > 0xff) + n = 0x5f; + Codepage->tounix[i] = n; + } +} + +static void get_toupper(void) +{ + int i; + + if(!mstoupper) + mstoupper = safe_malloc(128); + for(i=0; i<128; i++) + mstoupper[i] = get_number(); +} + +static void parse_old_device_line(char drive) +{ + char name[MAXPATHLEN]; + int items; + long offset; + char newdrive; + + /* finish any old drive */ + finish_drive_clause(); + + /* purge out data of old configuration files */ + purge(drive, file_nr); + + /* reserve slot */ + append(); + items = sscanf(token,"%c %s %i %i %i %i %li", + &newdrive,name,&devices[cur_dev].fat_bits, + &devices[cur_dev].tracks,&devices[cur_dev].heads, + &devices[cur_dev].sectors, &offset); + devices[cur_dev].offset = (off_t) offset; + switch(items){ + case 2: + devices[cur_dev].fat_bits = 0; + /* fall thru */ + case 3: + devices[cur_dev].sectors = 0; + devices[cur_dev].heads = 0; + devices[cur_dev].tracks = 0; + /* fall thru */ + case 6: + devices[cur_dev].offset = 0; + /* fall thru */ + default: + break; + case 0: + case 1: + case 4: + case 5: + syntax("bad number of parameters", 1); + exit(1); + } + if(!devices[cur_dev].tracks){ + devices[cur_dev].sectors = 0; + devices[cur_dev].heads = 0; + } + + devices[cur_dev].drive = letters[toupper(newdrive) - 'A']; + if (!(devices[cur_dev].name = strdup(name))) { + printOom(); + exit(1); + } + finish_drive_clause(); + pos=0; +} + +static int parse_one(int privilege) +{ + int action=0; + + get_next_token(); + if(!token) + return 0; + + if((match_token("drive") && ((action = 1)))|| + (match_token("drive+") && ((action = 2))) || + (match_token("+drive") && ((action = 3))) || + (match_token("clear_drive") && ((action = 4))) ) { + /* finish off the previous drive */ + finish_drive_clause(); + + get_next_token(); + if(token_length != 1) + syntax("drive letter expected", 0); + + if(action==1 || action==4) + /* replace existing drive */ + purge(token[0], file_nr); + if(action==4) + return 1; + if(action==3) + prepend(); + else + append(); + memset((char*)(devices+cur_dev), 0, sizeof(*devices)); + trusted = privilege; + flag_mask = 0; + devices[cur_dev].drive = letters[toupper(token[0]) - 'A']; + expect_char(':'); + return 1; + } + if(token_nr == 1 && token_length == 1) { + parse_old_device_line(token[0]); + return 1; + } + if(match_token("default_fucase")) { + free(mstoupper); + mstoupper=0; + } + if(match_token("default_tounix")) { + Free(Codepage); + Codepage = 0; + } + if(match_token("fucase")) { + expect_char(':'); + get_toupper(); + return 1; + } + if(match_token("tounix")) { + expect_char(':'); + get_codepage(); + return 1; + } + + if((cur_dev < 0 || + (set_var(dswitches, + sizeof(dswitches)/sizeof(*dswitches), + (caddr_t)&devices[cur_dev]) && + set_openflags(&devices[cur_dev]) && + set_misc_flags(&devices[cur_dev]) && + set_def_format(&devices[cur_dev]))) && + set_var(switches, + sizeof(switches)/sizeof(*switches), 0)) + syntax("unrecognized keyword", 1); + return 1; +} + +static int parse(const char *name, int privilege) +{ + fp = fopen(name, "r"); + if(!fp) + return 0; + file_nr++; + filename = strdup(name); + linenumber = 0; + lastTokenLinenumber = 0; + pos = 0; + token = 0; + cur_dev = -1; /* no current device */ + + while(parse_one(privilege)); + finish_drive_clause(); + fclose(fp); + return 1; +} + +void read_config(void) +{ + char *homedir; + char *envConfFile; + char conf_file[MAXPATHLEN+sizeof(CFG_FILE1)]; + + + /* copy compiled-in devices */ + file_nr = 0; + cur_devs = nr_const_devices; + nr_dev = nr_const_devices + 2; + devices = NewArray(nr_dev, struct device); + if(!devices) { + printOom(); + exit(1); + } + if(nr_const_devices) + memcpy(devices, const_devices, + nr_const_devices*sizeof(struct device)); + + (void) ((parse(CONF_FILE,1) | + parse(LOCAL_CONF_FILE,1) | + parse(SYS_CONF_FILE,1)) || + (parse(OLD_CONF_FILE,1) | + parse(OLD_LOCAL_CONF_FILE,1))); + /* the old-name configuration files only get executed if none of the + * new-name config files were used */ + + homedir = get_homedir(); + if ( homedir ){ + strncpy(conf_file, homedir, MAXPATHLEN ); + conf_file[MAXPATHLEN]='\0'; + strcat( conf_file, CFG_FILE1); + parse(conf_file,0); + } + memset((char *)&devices[cur_devs],0,sizeof(struct device)); + + envConfFile = getenv("MTOOLSRC"); + if(envConfFile) + parse(envConfFile,0); + + /* environmental variables */ + get_env_conf(); + if(mtools_skip_check) + mtools_fat_compatibility=1; + init_codepage(); +} + +void mtoolstest(int argc, char **argv, int type) +{ + /* testing purposes only */ + struct device *dev; + int i,j; + char *drive=NULL; + char *path; + + if (argc > 1 && (path = skip_drive(argv[1])) > argv[1]) { + drive = get_drive(argv[1], NULL); + } + + for (dev=devices; dev->name; dev++) { + if(drive && strcmp(drive, dev->drive) != 0) + continue; + printf("drive %s:\n", dev->drive); + printf("\t#fn=%d mode=%d ", + dev->file_nr, dev->mode); + if(dev->cfg_filename) + printf("defined in %s\n", dev->cfg_filename); + else + printf("builtin\n"); + printf("\tfile=\"%s\" fat_bits=%d \n", + dev->name,dev->fat_bits); + printf("\ttracks=%d heads=%d sectors=%d hidden=%d\n", + dev->tracks, dev->heads, dev->sectors, dev->hidden); + printf("\toffset=0x%lx\n", (long) dev->offset); + printf("\tpartition=%d\n", dev->partition); + + if(dev->misc_flags) + printf("\t"); + + if(IS_SCSI(dev)) + printf("scsi "); + if(IS_PRIVILEGED(dev)) + printf("privileged"); + if(IS_MFORMAT_ONLY(dev)) + printf("mformat_only "); + if(SHOULD_USE_VOLD(dev)) + printf("vold "); +#ifdef USE_XDF + if(SHOULD_USE_XDF(dev)) + printf("use_xdf "); +#endif + if(dev->misc_flags) + printf("\n"); + + if(dev->mode) + printf("\t"); +#ifdef O_SYNC + if(dev->mode & O_SYNC) + printf("sync "); +#endif +#ifdef O_NDELAY + if((dev->mode & O_NDELAY)) + printf("nodelay "); +#endif +#ifdef O_EXCL + if((dev->mode & O_EXCL)) + printf("exclusive "); +#endif + if(dev->mode) + printf("\n"); + + if(dev->precmd) + printf("\tprecmd=%s\n", dev->precmd); + + printf("\n"); + } + + printf("tounix:\n"); + for(i=0; i < 16; i++) { + putchar('\t'); + for(j=0; j<8; j++) + printf("0x%02x ", + (unsigned char)Codepage->tounix[i*8+j]); + putchar('\n'); + } + printf("\nfucase:\n"); + for(i=0; i < 16; i++) { + putchar('\t'); + for(j=0; j<8; j++) + printf("0x%02x ", + (unsigned char)mstoupper[i*8+j]); + putchar('\n'); + } + if(country_string) + printf("COUNTRY=%s\n", country_string); + printf("mtools_fat_compatibility=%d\n",mtools_fat_compatibility); + printf("mtools_skip_check=%d\n",mtools_skip_check); + printf("mtools_lower_case=%d\n",mtools_ignore_short_case); + + exit(0); +} + +#else /* NO_CONFIG */ + +void read_config(void) +{ + /* only compiled-in devices */ + devices = NewArray(nr_const_devices + 1, struct device); + if(!devices) { + fprintf(stderr,"Out of memory error\n"); + exit(1); + } + if(nr_const_devices) + memcpy(devices, const_devices, + nr_const_devices*sizeof(struct device)); +} + +#endif /* NO_CONFIG */ diff --git a/commands/i386/mtools-3.9.7/config.h b/commands/i386/mtools-3.9.7/config.h new file mode 100755 index 000000000..3123d6a90 --- /dev/null +++ b/commands/i386/mtools-3.9.7/config.h @@ -0,0 +1,301 @@ +/* config.h. Generated automatically by configure. */ +/* config.h.in. Generated automatically from configure.in by autoheader. */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define to empty if the keyword does not work. */ +/* #undef const */ + +/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */ +#define HAVE_SYS_WAIT_H 1 + +/* Define as __inline if that's what the C compiler calls it. */ +#define inline + +/* Define if on MINIX. */ +#define _MINIX 1 + +/* Define if the system does not provide POSIX.1 features except + with this defined. */ +#define _POSIX_1_SOURCE 2 + +/* Define if you need to in order for stat and other things to work. */ +#define _POSIX_SOURCE 1 + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if the `setpgrp' function takes no argument. */ +#define SETPGRP_VOID 1 + +/* Define to `unsigned' if <sys/types.h> doesn't define. */ +/* #undef size_t */ + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you can safely include both <sys/time.h> and <time.h>. */ +/* #undef TIME_WITH_SYS_TIME */ + +/* Define if your <sys/time.h> declares struct tm. */ +/* #undef TM_IN_SYS_TIME */ + +/* Define if the X Window System is missing or not being used. */ +#define X_DISPLAY_MISSING 1 + +/* Define this if you want to use Xdf */ +#define USE_XDF 1 + +/* Define this if you use mtools together with Solaris' vold */ +/* #undef USING_VOLD */ + +/* Define this if you use mtools together with the new Solaris' vold + * support */ +/* #undef USING_NEW_VOLD */ + +/* Define for debugging messages */ +/* #undef DEBUG */ + +/* Define on non Unix OS'es which don't have the concept of tty's */ +/* #undef USE_RAWTERM */ + +/* Define when sys_errlist is defined in the standard include files */ +/* #undef DECL_SYS_ERRLIST */ + +/* Define when you want to include floppyd support */ +/* #undef USE_FLOPPYD */ + +/* Define when the compiler supports LOFF_T type */ +/* #undef HAVE_LOFF_T */ + +/* Define when the compiler supports OFFSET_T type */ +/* #undef HAVE_OFFSET_T */ + +/* Define when the compiler supports LONG_LONG type */ +/* #undef HAVE_LONG_LONG */ + +/* Define when the system has a 64 bit off_t type */ +/* #undef HAVE_OFF_T_64 */ + +/* Define when you have an LLSEEK prototype */ +/* #undef HAVE_LLSEEK_PROTOTYPE */ + +/* Define if you have the atexit function. */ +#define HAVE_ATEXIT 1 + +/* Define if you have the basename function. */ +/* #undef HAVE_BASENAME */ + +/* Define if you have the fchdir function. */ +#ifdef __minix_vmd +#define HAVE_FCHDIR 1 +#endif + +/* Define if you have the flock function. */ +/* #undef HAVE_FLOCK */ + +/* Define if you have the getpass function. */ +#define HAVE_GETPASS 1 + +/* Define if you have the gettimeofday function. */ +#define HAVE_GETTIMEOFDAY 1 + +/* Define if you have the htons function. */ +/* #undef HAVE_HTONS */ + +/* Define if you have the llseek function. */ +/* #undef HAVE_LLSEEK */ + +/* Define if you have the lockf function. */ +#define HAVE_LOCKF 1 + +/* Define if you have the lseek64 function. */ +/* #undef HAVE_LSEEK64 */ + +/* Define if you have the media_oldaliases function. */ +/* #undef HAVE_MEDIA_OLDALIASES */ + +/* Define if you have the memcpy function. */ +#define HAVE_MEMCPY 1 + +/* Define if you have the memmove function. */ +#define HAVE_MEMMOVE 1 + +/* Define if you have the memset function. */ +#define HAVE_MEMSET 1 + +/* Define if you have the on_exit function. */ +/* #undef HAVE_ON_EXIT */ + +/* Define if you have the random function. */ +#define HAVE_RANDOM 1 + +/* Define if you have the seteuid function. */ +/* #undef HAVE_SETEUID */ + +/* Define if you have the setresuid function. */ +/* #undef HAVE_SETRESUID */ + +/* Define if you have the snprintf function. */ +#define HAVE_SNPRINTF 1 + +/* Define if you have the srandom function. */ +#define HAVE_SRANDOM 1 + +/* Define if you have the strcasecmp function. */ +#define HAVE_STRCASECMP 1 + +/* Define if you have the strchr function. */ +#define HAVE_STRCHR 1 + +/* Define if you have the strcspn function. */ +#define HAVE_STRCSPN 1 + +/* Define if you have the strdup function. */ +/* #undef HAVE_STRDUP */ + +/* Define if you have the strerror function. */ +#define HAVE_STRERROR 1 + +/* Define if you have the strncasecmp function. */ +#define HAVE_STRNCASECMP 1 + +/* Define if you have the strpbrk function. */ +#define HAVE_STRPBRK 1 + +/* Define if you have the strrchr function. */ +#define HAVE_STRRCHR 1 + +/* Define if you have the strspn function. */ +#define HAVE_STRSPN 1 + +/* Define if you have the strtol function. */ +#define HAVE_STRTOL 1 + +/* Define if you have the strtoul function. */ +#define HAVE_STRTOUL 1 + +/* Define if you have the tcflush function. */ +#define HAVE_TCFLUSH 1 + +/* Define if you have the tcsetattr function. */ +#define HAVE_TCSETATTR 1 + +/* Define if you have the tzset function. */ +#define HAVE_TZSET 1 + +/* Define if you have the utime function. */ +#define HAVE_UTIME 1 + +/* Define if you have the utimes function. */ +/* #undef HAVE_UTIMES */ + +/* Define if you have the <arpa/inet.h> header file. */ +/* #undef HAVE_ARPA_INET_H */ + +/* Define if you have the <fcntl.h> header file. */ +#define HAVE_FCNTL_H 1 + +/* Define if you have the <getopt.h> header file. */ +/* #undef HAVE_GETOPT_H */ + +/* Define if you have the <libc.h> header file. */ +/* #undef HAVE_LIBC_H */ + +/* Define if you have the <limits.h> header file. */ +#define HAVE_LIMITS_H 1 + +/* Define if you have the <linux/unistd.h> header file. */ +/* #undef HAVE_LINUX_UNISTD_H */ + +/* Define if you have the <malloc.h> header file. */ +/* #undef HAVE_MALLOC_H */ + +/* Define if you have the <memory.h> header file. */ +/* #undef HAVE_MEMORY_H */ + +/* Define if you have the <mntent.h> header file. */ +/* #undef HAVE_MNTENT_H */ + +/* Define if you have the <netdb.h> header file. */ +/* #undef HAVE_NETDB_H */ + +/* Define if you have the <netinet/in.h> header file. */ +/* #undef HAVE_NETINET_IN_H */ + +/* Define if you have the <sgtty.h> header file. */ +#define HAVE_SGTTY_H 1 + +/* Define if you have the <signal.h> header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the <strings.h> header file. */ +/* #undef HAVE_STRINGS_H */ + +/* Define if you have the <sys/file.h> header file. */ +/* #undef HAVE_SYS_FILE_H */ + +/* Define if you have the <sys/floppy.h> header file. */ +/* #undef HAVE_SYS_FLOPPY_H */ + +/* Define if you have the <sys/ioctl.h> header file. */ +#define HAVE_SYS_IOCTL_H 1 + +/* Define if you have the <sys/param.h> header file. */ +/* #undef HAVE_SYS_PARAM_H */ + +/* Define if you have the <sys/signal.h> header file. */ +/* #undef HAVE_SYS_SIGNAL_H */ + +/* Define if you have the <sys/socket.h> header file. */ +/* #undef HAVE_SYS_SOCKET_H */ + +/* Define if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define if you have the <sys/sysmacros.h> header file. */ +/* #undef HAVE_SYS_SYSMACROS_H */ + +/* Define if you have the <sys/termio.h> header file. */ +/* #undef HAVE_SYS_TERMIO_H */ + +/* Define if you have the <sys/termios.h> header file. */ +/* #undef HAVE_SYS_TERMIOS_H */ + +/* Define if you have the <sys/time.h> header file. */ +/* #undef HAVE_SYS_TIME_H */ + +/* Define if you have the <termio.h> header file. */ +/* #undef HAVE_TERMIO_H */ + +/* Define if you have the <termios.h> header file. */ +#define HAVE_TERMIOS_H 1 + +/* Define if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the <utime.h> header file. */ +#define HAVE_UTIME_H 1 + +/* Define if you have the cam library (-lcam). */ +/* #undef HAVE_LIBCAM */ + +/* Define if you have the nsl library (-lnsl). */ +/* #undef HAVE_LIBNSL */ + +/* Define if you have the socket library (-lsocket). */ +/* #undef HAVE_LIBSOCKET */ + +/* Define if you have the sun library (-lsun). */ +/* #undef HAVE_LIBSUN */ diff --git a/commands/i386/mtools-3.9.7/copyfile.c b/commands/i386/mtools-3.9.7/copyfile.c new file mode 100755 index 000000000..8cb66c6e1 --- /dev/null +++ b/commands/i386/mtools-3.9.7/copyfile.c @@ -0,0 +1,62 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "file.h" +#include "llong.h" + +/* + * Copy the data from source to target + */ + +int copyfile(Stream_t *Source, Stream_t *Target) +{ + char buffer[8*16384]; + int pos; + int ret, retw; + size_t len; + mt_size_t mt_len; + + if (!Source){ + fprintf(stderr,"Couldn't open source file\n"); + return -1; + } + + if (!Target){ + fprintf(stderr,"Couldn't open target file\n"); + return -1; + } + + pos = 0; + GET_DATA(Source, 0, &mt_len, 0, 0); + if (mt_len & ~max_off_t_31) { + fprintf(stderr, "File too big\n"); + return -1; + } + len = truncBytes32(mt_len); + while(1){ + ret = READS(Source, buffer, (mt_off_t) pos, 8*16384); + if (ret < 0 ){ + perror("file read"); + return -1; + } + if(!ret) + break; + if(got_signal) + return -1; + if (ret == 0) + break; + if ((retw = force_write(Target, buffer, (mt_off_t) pos, ret)) != ret){ + if(retw < 0 ) + perror("write in copy"); + else + fprintf(stderr, + "Short write %d instead of %d\n", retw, + ret); + if(errno == ENOSPC) + got_signal = 1; + return ret; + } + pos += ret; + } + return pos; +} diff --git a/commands/i386/mtools-3.9.7/devices.c b/commands/i386/mtools-3.9.7/devices.c new file mode 100755 index 000000000..fb3559dc6 --- /dev/null +++ b/commands/i386/mtools-3.9.7/devices.c @@ -0,0 +1,1115 @@ +/* + * This file is modified to perform on the UXP/DS operating system + * by FUJITSU Limited on 1996.6.4 + */ + +/* + * Device tables. See the Configure file for a complete description. + */ + +#define NO_TERMIO +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "devices.h" + +#define INIT_NOOP + +#define DEF_ARG1(x) (x), 0x2,0,(char *)0, 0, 0 +#define DEF_ARG0(x) 0,DEF_ARG1(x) + +#define MDEF_ARG 0L,DEF_ARG0(MFORMAT_ONLY_FLAG) +#define FDEF_ARG 0L,DEF_ARG0(0) +#define VOLD_DEF_ARG 0L,DEF_ARG0(VOLD_FLAG|MFORMAT_ONLY_FLAG) + +#define MED312 12,0,80,2,36,0,MDEF_ARG /* 3 1/2 extra density */ +#define MHD312 12,0,80,2,18,0,MDEF_ARG /* 3 1/2 high density */ +#define MDD312 12,0,80,2, 9,0,MDEF_ARG /* 3 1/2 double density */ +#define MHD514 12,0,80,2,15,0,MDEF_ARG /* 5 1/4 high density */ +#define MDD514 12,0,40,2, 9,0,MDEF_ARG /* 5 1/4 double density (360k) */ +#define MSS514 12,0,40,1, 9,0,MDEF_ARG /* 5 1/4 single sided DD, (180k) */ +#define MDDsmall 12,0,40,2, 8,0,MDEF_ARG /* 5 1/4 double density (320k) */ +#define MSSsmall 12,0,40,1, 8,0,MDEF_ARG /* 5 1/4 single sided DD, (160k) */ + +#define FED312 12,0,80,2,36,0,FDEF_ARG /* 3 1/2 extra density */ +#define FHD312 12,0,80,2,18,0,FDEF_ARG /* 3 1/2 high density */ +#define FDD312 12,0,80,2, 9,0,FDEF_ARG /* 3 1/2 double density */ +#define FHD514 12,0,80,2,15,0,FDEF_ARG /* 5 1/4 high density */ +#define FDD514 12,0,40,2, 9,0,FDEF_ARG /* 5 1/4 double density (360k) */ +#define FSS514 12,0,40,1, 9,0,FDEF_ARG /* 5 1/4 single sided DD, (180k) */ +#define FDDsmall 12,0,40,2, 8,0,FDEF_ARG /* 5 1/4 double density (320k) */ +#define FSSsmall 12,0,40,1, 8,0,FDEF_ARG /* 5 1/4 single sided DD, (160k) */ + +#define GENHD 16,0, 0,0, 0,0,MDEF_ARG /* Generic 16 bit FAT fs */ +#define GENFD 12,0,80,2,18,0,MDEF_ARG /* Generic 12 bit FAT fs */ +#define VOLDFD 12,0,80,2,18,0,VOLD_DEF_ARG /* Generic 12 bit FAT fs with vold */ +#define GEN 0,0, 0,0, 0,0,MDEF_ARG /* Generic fs of any FAT bits */ + +#define ZIPJAZ(x,c,h,s,y) 16,(x),(c),(h),(s),(s),0L, 4, \ + DEF_ARG1((y)|MFORMAT_ONLY_FLAG) /* Jaz disks */ + +#define JAZ(x) ZIPJAZ(x,1021, 64, 32, 0) +#define RJAZ(x) ZIPJAZ(x,1021, 64, 32, SCSI_FLAG|PRIV_FLAG) +#define ZIP(x) ZIPJAZ(x,96, 64, 32, 0) +#define RZIP(x) ZIPJAZ(x,96, 64, 32, SCSI_FLAG|PRIV_FLAG) + +#define REMOTE {"$DISPLAY", 'X', 0,0, 0,0, 0,0,0L, DEF_ARG0(FLOPPYD_FLAG)} + + + +#if defined(INIT_GENERIC) || defined(INIT_NOOP) +static int compare_geom(struct device *dev, struct device *orig_dev) +{ + if(IS_MFORMAT_ONLY(orig_dev)) + return 0; /* geometry only for mformatting ==> ok */ + if(!orig_dev || !orig_dev->tracks || !dev || !dev->tracks) + return 0; /* no original device. This is ok */ + return(orig_dev->tracks != dev->tracks || + orig_dev->heads != dev->heads || + orig_dev->sectors != dev->sectors); +} +#endif + +#define devices const_devices + + +#ifdef OS_aux +#define predefined_devices +struct device devices[] = { + {"/dev/floppy0", "A", GENFD }, + {"/dev/rdsk/c104d0s31", "J", JAZ(O_EXCL) }, + {"/dev/rdsk/c105d0s31", "Z", ZIP(O_EXCL) }, + REMOTE +}; +#endif /* aux */ + + +#ifdef OS_lynxos +#define predefined_devices +struct device devices[] = { + {"/dev/fd1440.0", "A", MHD312 }, + REMOTE +}; +#endif + + +#ifdef __BEOS__ +#define predefined_devices +struct device devices[] = { + {"/dev/disk/floppy/raw", "A", MHD312 }, + REMOTE +}; +#endif /* BEBOX */ + + +#ifdef OS_hpux + +#define predefined_devices +struct device devices[] = { +#ifdef OS_hpux10 +/* hpux10 uses different device names according to Frank Maritato + * <frank@math.hmc.edu> */ + {"/dev/floppy/c0t0d0", "A", MHD312 }, + {"/dev/floppy/c0t0d1", "B", MHD312 }, /* guessed by me */ + {"/dev/rscsi", "C", GENHD }, /* guessed by me */ +#else +/* Use rfloppy, according to Simao Campos <simao@iris.ctd.comsat.com> */ + {"/dev/rfloppy/c201d0s0", "A", FHD312 }, + {"/dev/rfloppy/c20Ad0s0", "A", FHD312 }, + {"/dev/rfloppy/c201d1s0", "B", FHD312 }, + {"/dev/rfloppy/c20Ad1s0", "B", FHD312 }, + {"/dev/rscsi", "C", GENHD }, +#endif + {"/dev/rdsk/c201d4", "J", RJAZ(O_EXCL) }, + {"/dev/rdsk/c201d4s0", "J", RJAZ(O_EXCL) }, + {"/dev/rdsk/c201d5", "Z", RZIP(O_EXCL) }, + {"/dev/rdsk/c201d5s0", "Z", RZIP(O_EXCL) }, + REMOTE +}; + +#ifdef HAVE_SYS_FLOPPY +/* geometry setting ioctl's contributed by Paolo Zeppegno + * <paolo@to.sem.it>, may cause "Not a typewriter" messages on other + * versions according to support@vital.com */ + +#include <sys/floppy.h> +#undef SSIZE + +struct generic_floppy_struct +{ + struct floppy_geometry fg; +}; + +#define BLOCK_MAJOR 24 +#define CHAR_MAJOR 112 + +static inline int get_parameters(int fd, struct generic_floppy_struct *floppy) +{ + if (ioctl(fd, FLOPPY_GET_GEOMETRY, &(floppy->fg)) != 0) { + perror("FLOPPY_GET_GEOMETRY"); + return(1); + } + + return 0; +} + +#define TRACKS(floppy) floppy.fg.tracks +#define HEADS(floppy) floppy.fg.heads +#define SECTORS(floppy) floppy.fg.sectors +#define FD_SECTSIZE(floppy) floppy.fg.sector_size +#define FD_SET_SECTSIZE(floppy,v) { floppy.fg.sector_size = v; } + +static inline int set_parameters(int fd, struct generic_floppy_struct *floppy, + struct stat *buf) +{ + if (ioctl(fd, FLOPPY_SET_GEOMETRY, &(floppy->fg)) != 0) { + perror(""); + return(1); + } + + return 0; +} +#define INIT_GENERIC +#endif + +#endif /* hpux */ + + +#if (defined(OS_sinix) || defined(VENDOR_sni) || defined(SNI)) +#define predefined_devices +struct device devices[] = { +#ifdef CPU_mips /* for Siemens Nixdorf's SINIX-N/O (mips) 5.4x SVR4 */ + { "/dev/at/flp/f0t", "A", FHD312}, + { "/dev/fd0", "A", GENFD}, +#else +#ifdef CPU_i386 /* for Siemens Nixdorf's SINIX-D/L (intel) 5.4x SVR4 */ + { "/dev/fd0135ds18", "A", FHD312}, + { "/dev/fd0135ds9", "A", FDD312}, + { "/dev/fd0", "A", GENFD}, + { "/dev/fd1135ds15", "B", FHD514}, + { "/dev/fd1135ds9", "B", FDD514}, + { "/dev/fd1", "B", GENFD}, +#endif /* CPU_i386 */ +#endif /*mips*/ + REMOTE +}; +#endif + +#ifdef OS_ultrix +#define predefined_devices +struct device devices[] = { + {"/dev/rfd0a", "A", GENFD}, /* guessed */ + {"/dev/rfd0c", "A", GENFD}, /* guessed */ + REMOTE +}; + +#endif + + +#ifdef OS_isc +#define predefined_devices +#if (defined(OS_isc2) && defined(OLDSTUFF)) +struct device devices[] = { + {"/dev/rdsk/f0d9dt", "A", FDD514}, + {"/dev/rdsk/f0q15dt", "A", FHD514}, + {"/dev/rdsk/f0d8dt", "A", FDDsmall}, + {"/dev/rdsk/f13ht", "B", FHD312}, + {"/dev/rdsk/f13dt", "B", FDD312}, + {"/dev/rdsk/0p1", "C", GENHD}, + {"/usr/vpix/defaults/C:","D",12, 0, 0, 0, 0,8704L,DEF_ARG0}, + {"$HOME/vpix/C:", "E", 12, 0, 0, 0, 0,8704L,MDEF_ARG}, + REMOTE +}; +#else +/* contributed by larry.jones@sdrc.com (Larry Jones) */ +struct device devices[] = { + {"/dev/rfd0", "A", GEN}, + {"/dev/rfd1", "B", GEN}, + {"/dev/rdsk/0p1", "C", GEN}, + {"/usr/vpix/defaults/C:","D", GEN, 1}, + {"$HOME/vpix/C:", "E", GEN, 1}, + REMOTE +}; + +#include <sys/vtoc.h> +#include <sys/sysmacros.h> +#undef SSIZE +#define BLOCK_MAJOR 1 +#define CHAR_MAJOR 1 +#define generic_floppy_struct disk_parms +int ioctl(int, int, void *); + +static int get_parameters(int fd, struct generic_floppy_struct *floppy) +{ + mt_off_t off; + char buf[512]; + + off = lseek(fd, 0, SEEK_CUR); + if(off < 0) { + perror("device seek 1"); + exit(1); + } + if (off == 0) { + /* need to read at least 1 sector to get correct info */ + read(fd, buf, sizeof buf); + if(lseek(fd, 0, SEEK_SET) < 0) { + perror("device seek 2"); + exit(1); + } + } + return ioctl(fd, V_GETPARMS, floppy); +} + +#define TRACKS(floppy) (floppy).dp_cyls +#define HEADS(floppy) (floppy).dp_heads +#define SECTORS(floppy) (floppy).dp_sectors +#define FD_SECTSIZE(floppy) (floppy).dp_secsiz +#define FD_SET_SECTSIZE(floppy,v) { (floppy).dp_secsiz = (v); } + +static int set_parameters(int fd, struct generic_floppy_struct *floppy, + struct stat *buf) +{ + return 1; +} + +#define INIT_GENERIC +#endif +#endif /* isc */ + +#ifdef CPU_i370 +#define predefined_devices +struct device devices[] = { + {"/dev/rfd0", "A", GENFD}, + REMOTE +}; +#endif /* CPU_i370 */ + +#ifdef OS_aix +/* modified by Federico Bianchi */ +#define predefined_devices +struct device devices[] = { + {"/dev/fd0","A",GENFD}, + REMOTE +}; +#endif /* aix */ + + +#ifdef OS_osf4 +/* modified by Chris Samuel <chris@rivers.dra.hmg.gb> */ +#define predefined_devices +struct device devices[] = { + {"/dev/fd0c","A",GENFD}, + REMOTE +}; +#endif /* OS_osf4 */ + + +#ifdef OS_solaris + +#ifdef USING_NEW_VOLD + +char *alias_name = NULL; + +extern char *media_oldaliases(char *); +extern char *media_findname(char *); + +char *getVoldName(struct device *dev, char *name) +{ + char *rname; + + if(!SHOULD_USE_VOLD(dev)) + return name; + + /*** + * Solaris specific routines to use the volume management + * daemon and libraries to get the correct device name... + ***/ + rname = media_findname(name); +#ifdef HAVE_MEDIA_OLDALIASES + if (rname == NULL) { + if ((alias_name = media_oldaliases(name)) != NULL) + rname = media_findname(alias_name); + } +#endif + if (rname == NULL) { + fprintf(stderr, + "No such volume or no media in device: %s.\n", + name); + exit(1); + } + return rname; +} +#endif /* USING_NEW_VOLD */ + +#define predefined_devices +struct device devices[] = { +#ifdef USING_NEW_VOLD + {"floppy", "A", VOLDFD }, +#elif USING_VOLD + {"/vol/dev/aliases/floppy0", "A", GENFD}, + {"/dev/rdiskette", "B", GENFD}, +#else /* ! USING_VOLD */ + {"/dev/rdiskette", "A", GENFD}, + {"/vol/dev/aliases/floppy0", "B", GENFD}, +#endif /* USING_VOLD */ + {"/dev/rdsk/c0t4d0s2", "J", RJAZ(O_NDELAY)}, + {"/dev/rdsk/c0t5d0s2", "Z", RZIP(O_NDELAY)}, + REMOTE +}; + + + +/* + * Ofer Licht <ofer@stat.Berkeley.EDU>, May 14, 1997. + */ + +#define INIT_GENERIC + +#include <sys/fdio.h> +#include <sys/mkdev.h> /* for major() */ + +struct generic_floppy_struct +{ + struct fd_char fdchar; +}; + +#define BLOCK_MAJOR 36 +#define CHAR_MAJOR 36 + +static inline int get_parameters(int fd, struct generic_floppy_struct *floppy) +{ + if (ioctl(fd, FDIOGCHAR, &(floppy->fdchar)) != 0) { + perror(""); + ioctl(fd, FDEJECT, NULL); + return(1); + } + return 0; +} + +#define TRACKS(floppy) floppy.fdchar.fdc_ncyl +#define HEADS(floppy) floppy.fdchar.fdc_nhead +#define SECTORS(floppy) floppy.fdchar.fdc_secptrack +/* SECTORS_PER_DISK(floppy) not used */ +#define FD_SECTSIZE(floppy) floppy.fdchar.fdc_sec_size +#define FD_SET_SECTSIZE(floppy,v) { floppy.fdchar.fdc_sec_size = v; } + +static inline int set_parameters(int fd, struct generic_floppy_struct *floppy, + struct stat *buf) +{ + if (ioctl(fd, FDIOSCHAR, &(floppy->fdchar)) != 0) { + ioctl(fd, FDEJECT, NULL); + perror(""); + return(1); + } + return 0; +} +#define INIT_GENERIC +#endif /* solaris */ + +#ifdef OS_sunos3 +#define predefined_devices +struct device devices[] = { + {"/dev/rfdl0c", "A", FDD312}, + {"/dev/rfd0c", "A", FHD312}, + REMOTE +}; +#endif /* OS_sunos3 */ + +#ifdef OS_xenix +#define predefined_devices +struct device devices[] = { + {"/dev/fd096ds15", "A", FHD514}, + {"/dev/fd048ds9", "A", FDD514}, + {"/dev/fd1135ds18", "B", FHD312}, + {"/dev/fd1135ds9", "B", FDD312}, + {"/dev/hd0d", "C", GENHD}, + REMOTE +}; +#endif /* OS_xenix */ + +#ifdef OS_sco +#define predefined_devices +struct device devices[] = { + { "/dev/fd0135ds18", "A", FHD312}, + { "/dev/fd0135ds9", "A", FDD312}, + { "/dev/fd0", "A", GENFD}, + { "/dev/fd1135ds15", "B", FHD514}, + { "/dev/fd1135ds9", "B", FDD514}, + { "/dev/fd1", "B", GENFD}, + { "/dev/hd0d", "C", GENHD}, + REMOTE +}; +#endif /* OS_sco */ + + +#ifdef OS_irix +#define predefined_devices +struct device devices[] = { + { "/dev/rdsk/fds0d2.3.5hi", "A", FHD312}, + { "/dev/rdsk/fds0d2.3.5", "A", FDD312}, + { "/dev/rdsk/fds0d2.96", "A", FHD514}, + {"/dev/rdsk/fds0d2.48", "A", FDD514}, + REMOTE +}; +#endif /* OS_irix */ + + +#ifdef OS_sunos4 +#include <sys/ioctl.h> +#include <sun/dkio.h> + +#define predefined_devices +struct device devices[] = { + {"/dev/rfd0c", "A", GENFD}, + {"/dev/rsd4c", "J", RJAZ(O_NDELAY)}, + {"/dev/rsd5c", "Z", RZIP(O_NDELAY)}, + REMOTE +}; + +/* + * Stuffing back the floppy parameters into the driver allows for gems + * like 10 sector or single sided floppies from Atari ST systems. + * + * Martin Schulz, Universite de Moncton, N.B., Canada, March 11, 1991. + */ + +#define INIT_GENERIC + +struct generic_floppy_struct +{ + struct fdk_char dkbuf; + struct dk_map dkmap; +}; + +#define BLOCK_MAJOR 16 +#define CHAR_MAJOR 54 + +static inline int get_parameters(int fd, struct generic_floppy_struct *floppy) +{ + if (ioctl(fd, DKIOCGPART, &(floppy->dkmap)) != 0) { + perror("DKIOCGPART"); + ioctl(fd, FDKEJECT, NULL); + return(1); + } + + if (ioctl(fd, FDKIOGCHAR, &( floppy->dkbuf)) != 0) { + perror(""); + ioctl(fd, FDKEJECT, NULL); + return(1); + } + return 0; +} + +#define TRACKS(floppy) floppy.dkbuf.ncyl +#define HEADS(floppy) floppy.dkbuf.nhead +#define SECTORS(floppy) floppy.dkbuf.secptrack +#define SECTORS_PER_DISK(floppy) floppy.dkmap.dkl_nblk +#define FD_SECTSIZE(floppy) floppy.dkbuf.sec_size +#define FD_SET_SECTSIZE(floppy,v) { floppy.dkbuf.sec_size = v; } + +static inline int set_parameters(int fd, struct generic_floppy_struct *floppy, + struct stat *buf) +{ + if (ioctl(fd, FDKIOSCHAR, &(floppy->dkbuf)) != 0) { + ioctl(fd, FDKEJECT, NULL); + perror(""); + return(1); + } + + if (ioctl(fd, ( unsigned int) DKIOCSPART, &(floppy->dkmap)) != 0) { + ioctl(fd, FDKEJECT, NULL); + perror(""); + return(1); + } + return 0; +} +#define INIT_GENERIC +#endif /* sparc && sunos */ + + +#ifdef DPX1000 +#define predefined_devices +struct device devices[] = { + /* [block device]: DPX1000 has /dev/flbm60, DPX2 has /dev/easyfb */ + {"/dev/flbm60", "A", MHD514}; + {"/dev/flbm60", "B", MDD514}, + {"/dev/flbm60", "C", MDDsmall}, + {"/dev/flbm60", "D", MSS}, + {"/dev/flbm60", "E", MSSsmall}, + REMOTE +}; +#endif /* DPX1000 */ + +#ifdef OS_bosx +#define predefined_devices +struct device devices[] = { + /* [block device]: DPX1000 has /dev/flbm60, DPX2 has /dev/easyfb */ + {"/dev/easyfb", "A", MHD514}, + {"/dev/easyfb", "B", MDD514}, + {"/dev/easyfb", "C", MDDsmall}, + {"/dev/easyfb", "D", MSS}, + {"/dev/easyfb", "E", MSSsmall}, + REMOTE +}; +#endif /* OS_bosx */ + +#ifdef OS_linux + +const char *error_msg[22]={ +"Missing Data Address Mark", +"Bad cylinder", +"Scan not satisfied", +"Scan equal hit", +"Wrong cylinder", +"CRC error in data field", +"Control Mark = deleted", +0, + +"Missing Address Mark", +"Write Protect", +"No Data - unreadable", +0, +"OverRun", +"CRC error in data or address", +0, +"End Of Cylinder", + +0, +0, +0, +"Not ready", +"Equipment check error", +"Seek end" }; + + +static inline void print_message(RawRequest_t *raw_cmd,const char *message) +{ + int i, code; + if(!message) + return; + + fprintf(stderr," "); + for (i=0; i< raw_cmd->cmd_count; i++) + fprintf(stderr,"%2.2x ", + (int)raw_cmd->cmd[i] ); + fprintf(stderr,"\n"); + for (i=0; i< raw_cmd->reply_count; i++) + fprintf(stderr,"%2.2x ", + (int)raw_cmd->reply[i] ); + fprintf(stderr,"\n"); + code = (raw_cmd->reply[0] <<16) + + (raw_cmd->reply[1] << 8) + + raw_cmd->reply[2]; + for(i=0; i<22; i++){ + if ((code & (1 << i)) && error_msg[i]) + fprintf(stderr,"%s\n", + error_msg[i]); + } +} + + +/* return values: + * -1: Fatal error, don't bother retrying. + * 0: OK + * 1: minor error, retry + */ + +int send_one_cmd(int fd, RawRequest_t *raw_cmd, const char *message) +{ + if (ioctl( fd, FDRAWCMD, raw_cmd) >= 0) { + if (raw_cmd->reply_count < 7) { + fprintf(stderr,"Short reply from FDC\n"); + return -1; + } + return 0; + } + + switch(errno) { + case EBUSY: + fprintf(stderr, "FDC busy, sleeping for a second\n"); + sleep(1); + return 1; + case EIO: + fprintf(stderr,"resetting controller\n"); + if(ioctl(fd, FDRESET, 2) < 0){ + perror("reset"); + return -1; + } + return 1; + default: + perror(message); + return -1; + } +} + + +/* + * return values + * -1: error + * 0: OK, last sector + * 1: more raw commands follow + */ + +int analyze_one_reply(RawRequest_t *raw_cmd, int *bytes, int do_print) +{ + + if(raw_cmd->reply_count == 7) { + int end; + + if (raw_cmd->reply[3] != raw_cmd->cmd[2]) { + /* end of cylinder */ + end = raw_cmd->cmd[6] + 1; + } else { + end = raw_cmd->reply[5]; + } + + *bytes = end - raw_cmd->cmd[4]; + /* FIXME: over/under run */ + *bytes = *bytes << (7 + raw_cmd->cmd[5]); + } else + *bytes = 0; + + switch(raw_cmd->reply[0] & 0xc0){ + case 0x40: + if ((raw_cmd->reply[0] & 0x38) == 0 && + (raw_cmd->reply[1]) == 0x80 && + (raw_cmd->reply[2]) == 0) { + *bytes += 1 << (7 + raw_cmd->cmd[5]); + break; + } + + if ( raw_cmd->reply[1] & ST1_WP ){ + *bytes = 0; + fprintf(stderr, + "This disk is write protected\n"); + return -1; + } + if(!*bytes && do_print) + print_message(raw_cmd, ""); + return -1; + case 0x80: + *bytes = 0; + fprintf(stderr, + "invalid command given\n"); + return -1; + case 0xc0: + *bytes = 0; + fprintf(stderr, + "abnormal termination caused by polling\n"); + return -1; + default: + break; + } +#ifdef FD_RAW_MORE + if(raw_cmd->flags & FD_RAW_MORE) + return 1; +#endif + return 0; +} + +#define predefined_devices +struct device devices[] = { + {"/dev/fd0", "A", 0, O_EXCL, 80,2, 18,0, MDEF_ARG}, + {"/dev/fd1", "B", 0, O_EXCL, 0,0, 0,0, FDEF_ARG}, + /* we assume that the Zip or Jaz drive is the second on the SCSI bus */ + {"/dev/sdb4","J", GENHD }, + {"/dev/sdb4","Z", GENHD }, + /* {"/dev/sda4","D", GENHD },*/ + REMOTE +}; + +/* + * Stuffing back the floppy parameters into the driver allows for gems + * like 21 sector or single sided floppies from Atari ST systems. + * + * Alain Knaff, Université Joseph Fourier, France, November 12, 1993. + */ + + +#define INIT_GENERIC +#define generic_floppy_struct floppy_struct +#define BLOCK_MAJOR 2 +#define SECTORS(floppy) floppy.sect +#define TRACKS(floppy) floppy.track +#define HEADS(floppy) floppy.head +#define SECTORS_PER_DISK(floppy) floppy.size +#define STRETCH(floppy) floppy.stretch +#define USE_2M(floppy) ((floppy.rate & FD_2M) ? 0xff : 0x80 ) +#define SSIZE(floppy) ((((floppy.rate & 0x38) >> 3 ) + 2) % 8) + +static inline void set_2m(struct floppy_struct *floppy, int value) +{ + if (value & 0x7f) + value = FD_2M; + else + value = 0; + floppy->rate = (floppy->rate & ~FD_2M) | value; +} +#define SET_2M set_2m + +static inline void set_ssize(struct floppy_struct *floppy, int value) +{ + value = (( (value & 7) + 6 ) % 8) << 3; + + floppy->rate = (floppy->rate & ~0x38) | value; +} + +#define SET_SSIZE set_ssize + +static inline int set_parameters(int fd, struct floppy_struct *floppy, + struct stat *buf) +{ + if ( ( MINOR(buf->st_rdev ) & 0x7f ) > 3 ) + return 1; + + return ioctl(fd, FDSETPRM, floppy); +} + +static inline int get_parameters(int fd, struct floppy_struct *floppy) +{ + return ioctl(fd, FDGETPRM, floppy); +} + +#endif /* linux */ + + +/* OS/2, gcc+emx */ +#ifdef __EMX__ +#define predefined_devices +struct device devices[] = { + {"A:", "A", GENFD}, + {"B:", "B", GENFD}, +}; +#define INIT_NOOP +#endif + + + +/*** /jes -- for D.O.S. 486 BL DX2/80 ***/ +#ifdef OS_freebsd +#define predefined_devices +struct device devices[] = { + {"/dev/rfd0.1440", "A", FHD312}, + {"/dev/rfd0.720", "A", FDD312}, + {"/dev/rfd1.1200", "B", MHD514}, + {"/dev/sd0s1", "C", GENHD}, + REMOTE +}; +#endif /* __FreeBSD__ */ + +/*** /jes -- for ALR 486 DX4/100 ***/ +#if defined(OS_netbsd) +#define predefined_devices +struct device devices[] = { + {"/dev/rfd0a", "A", FHD312}, + {"/dev/rfd0f", "A", FDD312}, + {"/dev/rfd0f", "S", MDD312}, + {"/dev/rfd1a", "B", FHD514}, + {"/dev/rfd1d", "B", FDD514}, + {"/dev/rfd1d", "T", MDD514}, + {"/dev/rwd0d", "C", 16, 0, 0, 0, 0, 0, 63L*512L, DEF_ARG0(0)}, + REMOTE +}; +#endif /* OS_NetBSD */ + +/* fgsch@openbsd.org 2000/05/19 */ +#if defined(OS_openbsd) +#define predefined_devices +struct device devices[] = { + {"/dev/rfd0Bc", "A", FHD312}, + {"/dev/rfd0Fc", "A", FDD312}, + {"/dev/rfd1Cc", "B", FHD514}, + {"/dev/rfd1Dc", "B", FDD514}, + {"/dev/rwd0c", "C", 16, 0, 0, 0, 0, 0, 63L*512L, DEF_ARG0(0)}, + REMOTE +}; +#endif /* OS_openbsd */ + + + +#if (!defined(predefined_devices) && defined (CPU_m68000) && defined (OS_sysv)) +#include <sys/gdioctl.h> + +#define predefined_devices +struct device devices[] = { + {"/dev/rfp020", "A", 12,O_NDELAY,40,2, 9, 0, MDEF_ARG}, + {"/usr/bin/DOS/dvd000", "C", GENFD}, + REMOTE +}; + +#undef INIT_NOOP +int init_geom(int fd, struct device *dev, struct device *orig_dev, + struct stat *stat) +{ + struct gdctl gdbuf; + + if (ioctl(fd, GDGETA, &gdbuf) == -1) { + ioctl(fd, GDDISMNT, &gdbuf); + return 1; + } + if((dev->use_2m & 0x7f) || (dev->ssize & 0x7f)) + return 1; + + SET_INT(gdbuf.params.cyls,dev->ntracks); + SET_INT(gdbuf.params.heads,dev->nheads); + SET_INT(gdbuf.params.psectrk,dev->nsect); + dev->ntracks = gdbuf.params.cyls; + dev->nheads = gdbuf.params.heads; + dev->nsect = gdbuf.params.psectrk; + dev->use_2m = 0x80; + dev->ssize = 0x82; + + gdbuf.params.pseccyl = gdbuf.params.psectrk * gdbuf.params.heads; + gdbuf.params.flags = 1; /* disk type flag */ + gdbuf.params.step = 0; /* step rate for controller */ + gdbuf.params.sectorsz = 512; /* sector size */ + + if (ioctl(fd, GDSETA, &gdbuf) < 0) { + ioctl(fd, GDDISMNT, &gdbuf); + return(1); + } + return(0); +} +#endif /* (defined (m68000) && defined (sysv))*/ + +#ifdef CPU_alpha +#ifndef OS_osf4 +#ifdef __osf__ +#include <sys/fcntl.h> +#define predefined_devices +struct device devices[] = { + {"/dev/rfd0c", "A", GENFD}, + REMOTE +}; +#endif +#endif +#endif + +#ifdef OS_osf +#ifndef predefined_devices +#define predefined_devices +struct device devices[] = { + {"/dev/fd0a", "A", MHD312 } }; + REMOTE +#endif +#endif + + +#ifdef OS_nextstep +#define predefined_devices +struct device devices[] = { +#ifdef CPU_m68k + {"/dev/rfd0b", "A", MED312 }, + REMOTE +#else + {"/dev/rfd0b", "A", MHD312 }, + REMOTE +#endif +}; +#endif + + +#if (!defined(predefined_devices) && defined(OS_sysv4)) +#ifdef __uxp__ +#define predefined_devices +struct device devices[] = { + {"/dev/fpd0", "A", FHD312}, + {"/dev/fpd0", "A", FDD312}, + REMOTE +}; +#else +#define predefined_devices +struct device devices[] = { + {"/dev/rdsk/f1q15dt", "B", FHD514}, + {"/dev/rdsk/f1d9dt", "B", FDD514}, + {"/dev/rdsk/f1d8dt", "B", FDDsmall}, + {"/dev/rdsk/f03ht", "A", FHD312}, + {"/dev/rdsk/f03dt", "A", FDD312}, + {"/dev/rdsk/dos", "C", GENHD}, + REMOTE +}; +#endif +#endif /* sysv4 */ + +#ifdef OS_Minix +/* Minix and Minix-vmd device list. Only present to attach the A: and B: + * drive letters to the floppies by default. Other devices can be given + * a drive letter by linking the device file to /dev/dosX, where X is a + * drive letter. Or one can use something like 'fd0:' for a drive name. + * Kees J. Bot <kjb@cs.vu.nl> + */ +#include <minix/partition.h> +#include <minix/u64.h> + +#define predefined_devices +struct device devices[] = { + {"/dev/fd0", "A", GEN}, + {"/dev/fd1", "B", GEN}, +}; + +#undef INIT_NOOP +int init_geom(int fd, struct device *dev, struct device *orig_dev, + struct stat *stat) +{ + /* Try to obtain the device parameters from the device driver. + * Don't fret if you can't, mtools will use the DOS boot block. + */ + struct partition geom; + unsigned long tot_sectors; + + if (ioctl(fd, DIOCGETP, &geom) == 0) { + dev->hidden = div64u(geom.base, 512); + tot_sectors = div64u(geom.size, 512); + dev->tracks = tot_sectors / (geom.heads * geom.sectors); + dev->heads = geom.heads; + dev->sectors = geom.sectors; + } + return(0); +} +#endif /* OS_Minix */ + +#ifdef INIT_GENERIC + +#ifndef USE_2M +#define USE_2M(x) 0x80 +#endif + +#ifndef SSIZE +#define SSIZE(x) 0x82 +#endif + +#ifndef SET_2M +#define SET_2M(x,y) return -1 +#endif + +#ifndef SET_SSIZE +#define SET_SSIZE(x,y) return -1 +#endif + +#undef INIT_NOOP +int init_geom(int fd, struct device *dev, struct device *orig_dev, + struct stat *stat) +{ + struct generic_floppy_struct floppy; + int change; + + /* + * succeed if we don't have a floppy + * this is the case for dosemu floppy image files for instance + */ + if (!((S_ISBLK(stat->st_mode) && major(stat->st_rdev) == BLOCK_MAJOR) +#ifdef CHAR_MAJOR + || (S_ISCHR(stat->st_mode) && major(stat->st_rdev) == CHAR_MAJOR) +#endif + )) + return compare_geom(dev, orig_dev); + + /* + * We first try to get the current floppy parameters from the kernel. + * This allows us to + * 1. get the rate + * 2. skip the parameter setting if the parameters are already o.k. + */ + + if (get_parameters( fd, & floppy ) ) + /* + * autodetection failure. + * This mostly occurs because of an absent or unformatted disks. + * + * It might also occur because of bizarre formats (for example + * rate 1 on a 3 1/2 disk). + + * If this is the case, the user should do an explicit + * setfdprm before calling mtools + * + * Another cause might be pre-existing wrong parameters. The + * user should do an setfdprm -c to repair this situation. + * + * ...fail immediately... ( Theoretically, we could try to save + * the situation by trying out all rates, but it would be slow + * and awkward) + */ + return 1; + + + /* + * if we have already have the correct parameters, keep them. + * the number of tracks doesn't need to match exactly, it may be bigger. + * the number of heads and sectors must match exactly, to avoid + * miscalculation of the location of a block on the disk + */ + change = 0; + if(compare(dev->sectors, SECTORS(floppy))){ + SECTORS(floppy) = dev->sectors; + change = 1; + } else + dev->sectors = SECTORS(floppy); + + if(compare(dev->heads, HEADS(floppy))){ + HEADS(floppy) = dev->heads; + change = 1; + } else + dev->heads = HEADS(floppy); + + if(compare(dev->tracks, TRACKS(floppy))){ + TRACKS(floppy) = dev->tracks; + change = 1; + } else + dev->tracks = TRACKS(floppy); + + + if(compare(dev->use_2m, USE_2M(floppy))){ + SET_2M(&floppy, dev->use_2m); + change = 1; + } else + dev->use_2m = USE_2M(floppy); + + if( ! (dev->ssize & 0x80) ) + dev->ssize = 0; + if(compare(dev->ssize, SSIZE(floppy) + 128)){ + SET_SSIZE(&floppy, dev->ssize); + change = 1; + } else + dev->ssize = SSIZE(floppy); + + if(!change) + /* no change, succeed */ + return 0; + +#ifdef SECTORS_PER_TRACK + SECTORS_PER_TRACK(floppy) = dev->sectors * dev->heads; +#endif + +#ifdef SECTORS_PER_DISK + SECTORS_PER_DISK(floppy) = dev->sectors * dev->heads * dev->tracks; +#endif + +#ifdef STRETCH + /* ... and the stretch */ + if ( dev->tracks > 41 ) + STRETCH(floppy) = 0; + else + STRETCH(floppy) = 1; +#endif + + return set_parameters( fd, &floppy, stat) ; +} +#endif /* INIT_GENERIC */ + +#ifdef INIT_NOOP +int init_geom(int fd, struct device *dev, struct device *orig_dev, + struct stat *stat) +{ + return compare_geom(dev, orig_dev); +} +#endif + +#ifdef predefined_devices +const int nr_const_devices = sizeof(const_devices) / sizeof(*const_devices); +#else +struct device devices[]={ + {"/dev/fd0", "A", 0, O_EXCL, 0,0, 0,0, MDEF_ARG}, + /* to shut up Ultrix's native compiler, we can't make this empty :( */ +}; +const nr_const_devices = 0; +#endif diff --git a/commands/i386/mtools-3.9.7/devices.h b/commands/i386/mtools-3.9.7/devices.h new file mode 100755 index 000000000..b904ba93d --- /dev/null +++ b/commands/i386/mtools-3.9.7/devices.h @@ -0,0 +1,169 @@ +#ifdef OS_linux + +#ifdef HAVE_SYS_SYSMACROS_H + +#include <sys/sysmacros.h> +#ifndef MAJOR +#define MAJOR(dev) major(dev) +#endif /* MAJOR not defined */ +#ifndef MINOR +#define MINOR(dev) minor(dev) +#endif /* MINOR not defined */ + +#else + +#include <linux/fs.h> /* get MAJOR/MINOR from Linux kernel */ +#ifndef major +#define major(x) MAJOR(x) +#endif + +#endif /* HAVE_SYS_SYSMACROS_H */ + +#include <linux/fd.h> +#include <linux/fdreg.h> +#include <linux/major.h> + + +typedef struct floppy_raw_cmd RawRequest_t; + +UNUSED(static inline void RR_INIT(struct floppy_raw_cmd *request)) +{ + request->data = 0; + request->length = 0; + request->cmd_count = 9; + request->flags = FD_RAW_INTR | FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK +#ifdef FD_RAW_SOFTFAILUE + | FD_RAW_SOFTFAILURE | FD_RAW_STOP_IF_FAILURE +#endif + ; + request->cmd[1] = 0; + request->cmd[6] = 0; + request->cmd[7] = 0x1b; + request->cmd[8] = 0xff; + request->reply_count = 0; +} + +UNUSED(static inline void RR_SETRATE(struct floppy_raw_cmd *request, int rate)) +{ + request->rate = rate; +} + +UNUSED(static inline void RR_SETDRIVE(struct floppy_raw_cmd *request,int drive)) +{ + request->cmd[1] = (request->cmd[1] & ~3) | (drive & 3); +} + +UNUSED(static inline void RR_SETTRACK(struct floppy_raw_cmd *request,int track)) +{ + request->cmd[2] = track; +} + +UNUSED(static inline void RR_SETPTRACK(struct floppy_raw_cmd *request, + int track)) +{ + request->track = track; +} + +UNUSED(static inline void RR_SETHEAD(struct floppy_raw_cmd *request, int head)) +{ + if(head) + request->cmd[1] |= 4; + else + request->cmd[1] &= ~4; + request->cmd[3] = head; +} + +UNUSED(static inline void RR_SETSECTOR(struct floppy_raw_cmd *request, + int sector)) +{ + request->cmd[4] = sector; + request->cmd[6] = sector-1; +} + +UNUSED(static inline void RR_SETSIZECODE(struct floppy_raw_cmd *request, + int sizecode)) +{ + request->cmd[5] = sizecode; + request->cmd[6]++; + request->length += 128 << sizecode; +} + +#if 0 +static inline void RR_SETEND(struct floppy_raw_cmd *request, int end) +{ + request->cmd[6] = end; +} +#endif + +UNUSED(static inline void RR_SETDIRECTION(struct floppy_raw_cmd *request, + int direction)) +{ + if(direction == MT_READ) { + request->flags |= FD_RAW_READ; + request->cmd[0] = FD_READ & ~0x80; + } else { + request->flags |= FD_RAW_WRITE; + request->cmd[0] = FD_WRITE & ~0x80; + } +} + + +UNUSED(static inline void RR_SETDATA(struct floppy_raw_cmd *request, + caddr_t data)) +{ + request->data = data; +} + + +#if 0 +static inline void RR_SETLENGTH(struct floppy_raw_cmd *request, int length) +{ + request->length += length; +} +#endif + +UNUSED(static inline void RR_SETCONT(struct floppy_raw_cmd *request)) +{ +#ifdef FD_RAW_MORE + request->flags |= FD_RAW_MORE; +#endif +} + + +UNUSED(static inline int RR_SIZECODE(struct floppy_raw_cmd *request)) +{ + return request->cmd[5]; +} + + + +UNUSED(static inline int RR_TRACK(struct floppy_raw_cmd *request)) +{ + return request->cmd[2]; +} + + +UNUSED(static inline int GET_DRIVE(int fd)) +{ + struct stat statbuf; + + if (fstat(fd, &statbuf) < 0 ){ + perror("stat"); + return -1; + } + + if (!S_ISBLK(statbuf.st_mode) || + MAJOR(statbuf.st_rdev) != FLOPPY_MAJOR) + return -1; + + return MINOR( statbuf.st_rdev ); +} + + + +/* void print_message(RawRequest_t *raw_cmd,char *message);*/ +int send_one_cmd(int fd, RawRequest_t *raw_cmd, const char *message); +int analyze_one_reply(RawRequest_t *raw_cmd, int *bytes, int do_print); + + +#endif diff --git a/commands/i386/mtools-3.9.7/dirCache.c b/commands/i386/mtools-3.9.7/dirCache.c new file mode 100755 index 000000000..f8360b8a3 --- /dev/null +++ b/commands/i386/mtools-3.9.7/dirCache.c @@ -0,0 +1,329 @@ +#include "sysincludes.h" +#include "vfat.h" +#include "dirCache.h" + + +void myfree(void *a) +{ + free(a); +} + +#define free myfree + + +#define BITS_PER_INT (sizeof(unsigned int) * 8) + + +static inline unsigned int rol(unsigned int arg, int shift) +{ + arg &= 0xffffffff; /* for 64 bit machines */ + return (arg << shift) | (arg >> (32 - shift)); +} + + +static int calcHash(char *name) +{ + unsigned long hash; + int i; + unsigned char c; + + hash = 0; + i = 0; + while(*name) { + /* rotate it */ + hash = rol(hash,5); /* a shift of 5 makes sure we spread quickly + * over the whole width, moreover, 5 is + * prime with 32, which makes sure that + * successive letters cannot cover each + * other easily */ + c = toupper(*name); + hash ^= (c * (c+2)) ^ (i * (i+2)); + hash &= 0xffffffff; + i++, name++; + } + hash = hash * (hash + 2); + /* the following two xors make sure all info is spread evenly over all + * bytes. Important if we only keep the low order bits later on */ + hash ^= (hash & 0xfff) << 12; + hash ^= (hash & 0xff000) << 24; + return hash; +} + +static int addBit(unsigned int *bitmap, int hash, int checkOnly) +{ + int bit, entry; + + bit = 1 << (hash % BITS_PER_INT); + entry = (hash / BITS_PER_INT) % DC_BITMAP_SIZE; + + if(checkOnly) + return bitmap[entry] & bit; + else { + bitmap[entry] |= bit; + return 1; + } +} + +static int _addHash(dirCache_t *cache, unsigned int hash, int checkOnly) +{ + return + addBit(cache->bm0, hash, checkOnly) && + addBit(cache->bm1, rol(hash,12), checkOnly) && + addBit(cache->bm2, rol(hash,24), checkOnly); +} + + +static void addNameToHash(dirCache_t *cache, char *name) +{ + _addHash(cache, calcHash(name), 0); +} + +static void hashDce(dirCache_t *cache, dirCacheEntry_t *dce) +{ + if(dce->beginSlot != cache->nrHashed) + return; + cache->nrHashed = dce->endSlot; + if(dce->longName) + addNameToHash(cache, dce->longName); + addNameToHash(cache, dce->shortName); +} + +int isHashed(dirCache_t *cache, char *name) +{ + int ret; + + ret = _addHash(cache, calcHash(name), 1); + return ret; +} + +void checkXYZ(dirCache_t *cache) +{ + if(cache->entries[2]) + printf(" at 2 = %d\n", cache->entries[2]->beginSlot); +} + + +int growDirCache(dirCache_t *cache, int slot) +{ + if(slot < 0) { + fprintf(stderr, "Bad slot %d\n", slot); + exit(1); + } + + if( cache->nr_entries <= slot) { + int i; + + cache->entries = realloc(cache->entries, + (slot+1) * 2 * + sizeof(dirCacheEntry_t *)); + if(!cache->entries) + return -1; + for(i= cache->nr_entries; i < (slot+1) * 2; i++) { + cache->entries[i] = 0; + } + cache->nr_entries = (slot+1) * 2; + } + return 0; +} + +dirCache_t *allocDirCache(Stream_t *Stream, int slot) +{ + dirCache_t **dcp; + + if(slot < 0) { + fprintf(stderr, "Bad slot %d\n", slot); + exit(1); + } + + dcp = getDirCacheP(Stream); + if(!*dcp) { + *dcp = New(dirCache_t); + if(!*dcp) + return 0; + (*dcp)->entries = NewArray((slot+1)*2+5, dirCacheEntry_t *); + if(!(*dcp)->entries) { + free(*dcp); + return 0; + } + (*dcp)->nr_entries = (slot+1) * 2; + memset( (*dcp)->bm0, 0, DC_BITMAP_SIZE); + memset( (*dcp)->bm1, 0, DC_BITMAP_SIZE); + memset( (*dcp)->bm2, 0, DC_BITMAP_SIZE); + (*dcp)->nrHashed = 0; + } else + if(growDirCache(*dcp, slot) < 0) + return 0; + return *dcp; +} + +static void freeDirCacheRange(dirCache_t *cache, int beginSlot, int endSlot) +{ + dirCacheEntry_t *entry; + int clearBegin; + int clearEnd; + int i; + + if(endSlot < beginSlot) { + fprintf(stderr, "Bad slots %d %d in free range\n", + beginSlot, endSlot); + exit(1); + } + + while(beginSlot < endSlot) { + entry = cache->entries[beginSlot]; + if(!entry) { + beginSlot++; + continue; + } + + clearEnd = entry->endSlot; + if(clearEnd > endSlot) + clearEnd = endSlot; + clearBegin = beginSlot; + + for(i = clearBegin; i <clearEnd; i++) + cache->entries[i] = 0; + + if(entry->endSlot == endSlot) + entry->endSlot = beginSlot; + else if(entry->beginSlot == beginSlot) + entry->beginSlot = endSlot; + else { + fprintf(stderr, + "Internal error, non contiguous de-allocation\n"); + fprintf(stderr, "%d %d\n", beginSlot, endSlot); + fprintf(stderr, "%d %d\n", entry->beginSlot, + entry->endSlot); + exit(1); + } + + if(entry->beginSlot == entry->endSlot) { + if(entry->longName) + free(entry->longName); + if(entry->shortName) + free(entry->shortName); + free(entry); + } + + beginSlot = clearEnd; + } +} + +static dirCacheEntry_t *allocDirCacheEntry(dirCache_t *cache, int beginSlot, + int endSlot, + dirCacheEntryType_t type) +{ + dirCacheEntry_t *entry; + int i; + + if(growDirCache(cache, endSlot) < 0) + return 0; + + entry = New(dirCacheEntry_t); + if(!entry) + return 0; + entry->type = type; + entry->longName = 0; + entry->shortName = 0; + entry->beginSlot = beginSlot; + entry->endSlot = endSlot; + + freeDirCacheRange(cache, beginSlot, endSlot); + for(i=beginSlot; i<endSlot; i++) { + cache->entries[i] = entry; + } + return entry; +} + +dirCacheEntry_t *addUsedEntry(dirCache_t *cache, int beginSlot, int endSlot, + char *longName, char *shortName, + struct directory *dir) +{ + dirCacheEntry_t *entry; + + if(endSlot < beginSlot) { + fprintf(stderr, + "Bad slots %d %d in add used entry\n", + beginSlot, endSlot); + exit(1); + } + + + entry = allocDirCacheEntry(cache, beginSlot, endSlot, DCET_USED); + if(!entry) + return 0; + + entry->beginSlot = beginSlot; + entry->endSlot = endSlot; + if(longName) + entry->longName = strdup(longName); + entry->shortName = strdup(shortName); + entry->dir = *dir; + hashDce(cache, entry); + return entry; +} + +static void mergeFreeSlots(dirCache_t *cache, int slot) +{ + dirCacheEntry_t *previous, *next; + int i; + + if(slot == 0) + return; + previous = cache->entries[slot-1]; + next = cache->entries[slot]; + if(next && next->type == DCET_FREE && + previous && previous->type == DCET_FREE) { + for(i=next->beginSlot; i < next->endSlot; i++) + cache->entries[i] = previous; + previous->endSlot = next->endSlot; + free(next); + } +} + +dirCacheEntry_t *addFreeEntry(dirCache_t *cache, int beginSlot, int endSlot) +{ + dirCacheEntry_t *entry; + + if(beginSlot < cache->nrHashed) + cache->nrHashed = beginSlot; + + if(endSlot < beginSlot) { + fprintf(stderr, "Bad slots %d %d in add free entry\n", + beginSlot, endSlot); + exit(1); + } + + if(endSlot == beginSlot) + return 0; + entry = allocDirCacheEntry(cache, beginSlot, endSlot, DCET_FREE); + mergeFreeSlots(cache, beginSlot); + mergeFreeSlots(cache, endSlot); + return cache->entries[beginSlot]; +} + + +dirCacheEntry_t *addEndEntry(dirCache_t *cache, int pos) +{ + return allocDirCacheEntry(cache, pos, pos+1, DCET_END); +} + +dirCacheEntry_t *lookupInDircache(dirCache_t *cache, int pos) +{ + if(growDirCache(cache, pos+1) < 0) + return 0; + return cache->entries[pos]; +} + +void freeDirCache(Stream_t *Stream) +{ + dirCache_t *cache, **dcp; + + dcp = getDirCacheP(Stream); + cache = *dcp; + if(cache) { + freeDirCacheRange(cache, 0, cache->nr_entries); + free(cache); + *dcp = 0; + } +} diff --git a/commands/i386/mtools-3.9.7/dirCache.h b/commands/i386/mtools-3.9.7/dirCache.h new file mode 100755 index 000000000..529190be7 --- /dev/null +++ b/commands/i386/mtools-3.9.7/dirCache.h @@ -0,0 +1,40 @@ +#ifndef MTOOLS_DIRCACHE_H +#define MTOOLS_DIRCACHE_H + +typedef enum { + DCET_FREE, + DCET_USED, + DCET_END +} dirCacheEntryType_t; + +#define DC_BITMAP_SIZE 128 + +typedef struct dirCacheEntry_t { + dirCacheEntryType_t type; + int beginSlot; + int endSlot; + char *shortName; + char *longName; + struct directory dir; +} dirCacheEntry_t; + +typedef struct dirCache_t { + struct dirCacheEntry_t **entries; + int nr_entries; + unsigned int nrHashed; + unsigned int bm0[DC_BITMAP_SIZE]; + unsigned int bm1[DC_BITMAP_SIZE]; + unsigned int bm2[DC_BITMAP_SIZE]; +} dirCache_t; + +int isHashed(dirCache_t *cache, char *name); +int growDirCache(dirCache_t *cache, int slot); +dirCache_t *allocDirCache(Stream_t *Stream, int slot); +dirCacheEntry_t *addUsedEntry(dirCache_t *Stream, int begin, int end, + char *longName, char *shortName, + struct directory *dir); +void freeDirCache(Stream_t *Stream); +dirCacheEntry_t *addFreeEntry(dirCache_t *Stream, int begin, int end); +dirCacheEntry_t *addEndEntry(dirCache_t *Stream, int pos); +dirCacheEntry_t *lookupInDircache(dirCache_t *Stream, int pos); +#endif diff --git a/commands/i386/mtools-3.9.7/directory.c b/commands/i386/mtools-3.9.7/directory.c new file mode 100755 index 000000000..efc020a43 --- /dev/null +++ b/commands/i386/mtools-3.9.7/directory.c @@ -0,0 +1,106 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "stream.h" +#include "mtools.h" +#include "file.h" +#include "fs.h" + +/* #define DEBUG */ + +/* + * Read a directory entry into caller supplied buffer + */ +struct directory *dir_read(direntry_t *entry, int *error) +{ + int n; + *error = 0; + if((n=force_read(entry->Dir, (char *) (&entry->dir), + (mt_off_t) entry->entry * MDIR_SIZE, + MDIR_SIZE)) != MDIR_SIZE) { + if (n < 0) { + *error = -1; + } + return NULL; + } + return &entry->dir; +} + +/* + * Make a subdirectory grow in length. Only subdirectories (not root) + * may grow. Returns a 0 on success, 1 on failure (disk full), or -1 + * on error. + */ + +int dir_grow(Stream_t *Dir, int size) +{ + Stream_t *Stream = GetFs(Dir); + DeclareThis(FsPublic_t); + int ret; + int buflen; + char *buffer; + + if (!getfreeMinClusters(Dir, 1)) + return -1; + + buflen = This->cluster_size * This->sector_size; + + if(! (buffer=malloc(buflen)) ){ + perror("dir_grow: malloc"); + return -1; + } + + memset((char *) buffer, '\0', buflen); + ret = force_write(Dir, buffer, (mt_off_t) size * MDIR_SIZE, buflen); + free(buffer); + if(ret < buflen) + return -1; + return 0; +} + + +void low_level_dir_write(direntry_t *entry) +{ + force_write(entry->Dir, + (char *) (&entry->dir), + (mt_off_t) entry->entry * MDIR_SIZE, MDIR_SIZE); +} + + +/* + * Make a directory entry. Builds a directory entry based on the + * name, attribute, starting cluster number, and size. Returns a pointer + * to a static directory structure. + */ + +struct directory *mk_entry(const char *filename, char attr, + unsigned int fat, size_t size, time_t date, + struct directory *ndir) +{ + struct tm *now; + time_t date2 = date; + unsigned char hour, min_hi, min_low, sec; + unsigned char year, month_hi, month_low, day; + + now = localtime(&date2); + strncpy(ndir->name, filename, 8); + strncpy(ndir->ext, filename + 8, 3); + ndir->attr = attr; + ndir->ctime_ms = 0; + hour = now->tm_hour << 3; + min_hi = now->tm_min >> 3; + min_low = now->tm_min << 5; + sec = now->tm_sec / 2; + ndir->ctime[1] = ndir->time[1] = hour + min_hi; + ndir->ctime[0] = ndir->time[0] = min_low + sec; + year = (now->tm_year - 80) << 1; + month_hi = (now->tm_mon + 1) >> 3; + month_low = (now->tm_mon + 1) << 5; + day = now->tm_mday; + ndir -> adate[1] = ndir->cdate[1] = ndir->date[1] = year + month_hi; + ndir -> adate[0] = ndir->cdate[0] = ndir->date[0] = month_low + day; + + set_word(ndir->start, fat & 0xffff); + set_word(ndir->startHi, fat >> 16); + set_dword(ndir->size, size); + return ndir; +} diff --git a/commands/i386/mtools-3.9.7/direntry.c b/commands/i386/mtools-3.9.7/direntry.c new file mode 100755 index 000000000..84c411adc --- /dev/null +++ b/commands/i386/mtools-3.9.7/direntry.c @@ -0,0 +1,119 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "stream.h" +#include "file.h" +#include "mtoolsDirent.h" + +void initializeDirentry(direntry_t *entry, Stream_t *Dir) +{ + entry->entry = -1; +/* entry->parent = getDirentry(Dir);*/ + entry->Dir = Dir; + entry->beginSlot = 0; + entry->endSlot = 0; +} + +int isNotFound(direntry_t *entry) +{ + return entry->entry == -2; +} + +void rewindEntry(direntry_t *entry) +{ + entry->entry = -1; +} + + +direntry_t *getParent(direntry_t *entry) +{ + return getDirentry(entry->Dir); +} + + +static int getPathLen(direntry_t *entry) +{ + int length=0; + + while(1) { + if(entry->entry == -3) /* rootDir */ + return strlen(getDrive(entry->Dir)) + 1 + length + 1; + + length += 1 + strlen(entry->name); + entry = getDirentry(entry->Dir); + } +} + +static char *sprintPwd(direntry_t *entry, char *ptr) +{ + if(entry->entry == -3) { + strcpy(ptr, getDrive(entry->Dir)); + strcat(ptr, ":/"); + ptr = strchr(ptr, 0); + } else { + ptr = sprintPwd(getDirentry(entry->Dir), ptr); + if(ptr[-1] != '/') + *ptr++ = '/'; + strcpy(ptr, entry->name); + ptr += strlen(entry->name); + } + return ptr; +} + + +#define NEED_ESCAPE "\"$\\" + +static void _fprintPwd(FILE *f, direntry_t *entry, int recurs, int escape) +{ + if(entry->entry == -3) { + fputs(getDrive(entry->Dir), f); + putc(':', f); + if(!recurs) + putc('/', f); + } else { + _fprintPwd(f, getDirentry(entry->Dir), 1, escape); + if (escape && strpbrk(entry->name, NEED_ESCAPE)) { + char *ptr; + for(ptr = entry->name; *ptr; ptr++) { + if (strchr(NEED_ESCAPE, *ptr)) + putc('\\', f); + putc(*ptr, f); + } + } else { + fprintf(f, "/%s", entry->name); + } + } +} + +void fprintPwd(FILE *f, direntry_t *entry, int escape) +{ + if (escape) + putc('"', f); + _fprintPwd(f, entry, 0, escape); + if(escape) + putc('"', f); +} + +char *getPwd(direntry_t *entry) +{ + int size; + char *ret; + + size = getPathLen(entry); + ret = malloc(size+1); + if(!ret) + return 0; + sprintPwd(entry, ret); + return ret; +} + +int isSubdirOf(Stream_t *inside, Stream_t *outside) +{ + while(1) { + if(inside == outside) /* both are the same */ + return 1; + if(getDirentry(inside)->entry == -3) /* root directory */ + return 0; + /* look further up */ + inside = getDirentry(inside)->Dir; + } +} diff --git a/commands/i386/mtools-3.9.7/expand.c b/commands/i386/mtools-3.9.7/expand.c new file mode 100755 index 000000000..a87d14eee --- /dev/null +++ b/commands/i386/mtools-3.9.7/expand.c @@ -0,0 +1,83 @@ +/* + * Do filename expansion with the shell. + */ + +#define EXPAND_BUF 2048 + +#include "sysincludes.h" +#include "mtools.h" + + +int safePopenOut(char **command, char *output, int len) +{ + int pipefd[2]; + pid_t pid; + int status; + int last; + + if(pipe(pipefd)) { + return -2; + } + switch((pid=fork())){ + case -1: + return -2; + case 0: /* the son */ + close(pipefd[0]); + destroy_privs(); + close(1); + close(2); /* avoid nasty error messages on stderr */ + dup(pipefd[1]); + close(pipefd[1]); + execvp(command[0], command+1); + exit(1); + default: + close(pipefd[1]); + break; + } + last=read(pipefd[0], output, len); + kill(pid,9); + wait(&status); + if(last<0) { + return -1; + } + return last; +} + + + +const char *expand(const char *input, char *ans) +{ + int last; + char buf[256]; + char *command[] = { "/bin/sh", "sh", "-c", 0, 0 }; + + ans[EXPAND_BUF-1]='\0'; + + if (input == NULL) + return(NULL); + if (*input == '\0') + return(""); + /* any thing to expand? */ + if (!strpbrk(input, "$*(){}[]\\?`~")) { + strncpy(ans, input, EXPAND_BUF-1); + return(ans); + } + /* popen an echo */ +#ifdef HAVE_SNPRINTF + snprintf(buf, 255, "echo %s", input); +#else + sprintf(buf, "echo %s", input); +#endif + + command[3]=buf; + last=safePopenOut(command, ans, EXPAND_BUF-1); + if(last<0) { + perror("Pipe read error"); + exit(1); + } + if(last) + ans[last-1] = '\0'; + else + strncpy(ans, input, EXPAND_BUF-1); + return ans; +} diff --git a/commands/i386/mtools-3.9.7/fat.c b/commands/i386/mtools-3.9.7/fat.c new file mode 100755 index 000000000..189ebcbd3 --- /dev/null +++ b/commands/i386/mtools-3.9.7/fat.c @@ -0,0 +1,929 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "stream.h" +#include "mtools.h" +#include "fsP.h" + +extern Stream_t *default_drive; + +#ifdef HAVE_LONG_LONG +typedef long long fatBitMask; +#else +typedef long fatBitMask; +#endif + +typedef struct FatMap_t { + unsigned char *data; + fatBitMask dirty; + fatBitMask valid; +} FatMap_t; + +#define SECT_PER_ENTRY (sizeof(fatBitMask)*8) +#define ONE ((fatBitMask) 1) + +static inline int readSector(Fs_t *This, char *buf, unsigned int off, + size_t size) +{ + return READS(This->Next, buf, sectorsToBytes((Stream_t *)This, off), + size << This->sectorShift); +} + + +static inline int forceReadSector(Fs_t *This, char *buf, unsigned int off, + size_t size) +{ + return force_read(This->Next, buf, sectorsToBytes((Stream_t *)This, off), + size << This->sectorShift); +} + + +static inline int writeSector(Fs_t *This, char *buf, unsigned int off, + size_t size) +{ + return WRITES(This->Next, buf, sectorsToBytes((Stream_t*)This, off), + size << This->sectorShift); +} + +static inline int forceWriteSector(Fs_t *This, char *buf, unsigned int off, + size_t size) +{ + return force_write(This->Next, buf, sectorsToBytes((Stream_t*)This, off), + size << This->sectorShift); +} + + +static FatMap_t *GetFatMap(Fs_t *Stream) +{ + int nr_entries,i; + FatMap_t *map; + + Stream->fat_error = 0; + nr_entries = (Stream->fat_len + SECT_PER_ENTRY - 1) / SECT_PER_ENTRY; + map = NewArray(nr_entries, FatMap_t); + if(!map) + return 0; + + for(i=0; i< nr_entries; i++) { + map[i].data = 0; + map[i].valid = 0; + map[i].dirty = 0; + } + + return map; +} + +static inline int locate(Fs_t *Stream, int offset, int *slot, int *bit) +{ + if(offset >= Stream->fat_len) + return -1; + *slot = offset / SECT_PER_ENTRY; + *bit = offset % SECT_PER_ENTRY; + return 0; +} + +static inline int fatReadSector(Fs_t *This, int sector, int slot, + int bit, int dupe) +{ + int fat_start, ret; + + dupe = (dupe + This->primaryFat) % This->num_fat; + fat_start = This->fat_start + This->fat_len * dupe; + + /* first, read as much as the buffer can give us */ + ret = readSector(This, + (char *)(This->FatMap[slot].data+(bit<<This->sectorShift)), + fat_start+sector, + (SECT_PER_ENTRY - bit%SECT_PER_ENTRY)); + if(ret < 0) + return 0; + + if(ret < This->sector_size) { + /* if we got less than one sector's worth, insist to get at + * least one sector */ + ret = forceReadSector(This, + (char *) (This->FatMap[slot].data + + (bit << This->sectorShift)), + fat_start+sector, 1); + if(ret < This->sector_size) + return 0; + return 1; + } + + return ret >> This->sectorShift; +} + + +static int fatWriteSector(Fs_t *This, int sector, int slot, int bit, int dupe) +{ + int fat_start; + + dupe = (dupe + This->primaryFat) % This->num_fat; + if(dupe && !This->writeAllFats) + return This->sector_size; + + fat_start = This->fat_start + This->fat_len * dupe; + + return forceWriteSector(This, + (char *) + (This->FatMap[slot].data + bit * This->sector_size), + fat_start+sector, 1); +} + +static unsigned char *loadSector(Fs_t *This, + unsigned int sector, fatAccessMode_t mode, + int recurs) +{ + int slot, bit, i, ret; + + if(locate(This,sector, &slot, &bit) < 0) + return 0; +#if 0 + if (((This->fat_len + SECT_PER_ENTRY - 1) / SECT_PER_ENTRY) <= slot) { + fprintf(stderr,"This should not happen\n"); + fprintf(stderr, "fat_len = %d\n", This->fat_len); + fprintf(stderr, "SECT_PER_ENTRY=%d\n", (int)SECT_PER_ENTRY); + fprintf(stderr, "sector = %d slot = %d bit=%d\n", + sector, slot, bit); + fprintf(stderr, "left = %d",(int) + ((This->fat_len+SECT_PER_ENTRY-1) / SECT_PER_ENTRY)); + return 0; + } +#endif + if(!This->FatMap[slot].data) { + /* allocate the storage space */ + This->FatMap[slot].data = + malloc(This->sector_size * SECT_PER_ENTRY); + if(!This->FatMap[slot].data) + return 0; + memset(This->FatMap[slot].data, 0xee, + This->sector_size * SECT_PER_ENTRY); + } + + if(! (This->FatMap[slot].valid & (ONE << bit))) { + ret = -1; + for(i=0; i< This->num_fat; i++) { + /* read the sector */ + ret = fatReadSector(This, sector, slot, bit, i); + + if(ret == 0) { + fprintf(stderr, + "Error reading fat number %d\n", i); + continue; + } + break; + } + + /* all copies bad. Return error */ + if(ret == 0) + return 0; + + for(i=0; i < ret; i++) + This->FatMap[slot].valid |= ONE << (bit + i); + + if(!recurs && ret == 1) + /* do some prefetching, if we happened to only + * get one sector */ + loadSector(This, sector+1, mode, 1); + if(!recurs && batchmode) + for(i=0; i < 1024; i++) + loadSector(This, sector+i, mode, 1); + } + + if(mode == FAT_ACCESS_WRITE) { + This->FatMap[slot].dirty |= ONE << bit; + This->fat_dirty = 1; + } + return This->FatMap[slot].data + (bit << This->sectorShift); +} + + +static unsigned char *getAddress(Fs_t *Stream, + unsigned int num, fatAccessMode_t mode) +{ + unsigned char *ret; + int sector; + int offset; + + sector = num >> Stream->sectorShift; + ret = 0; + if(sector == Stream->lastFatSectorNr && + Stream->lastFatAccessMode >= mode) + ret = Stream->lastFatSectorData; + if(!ret) { + ret = loadSector(Stream, sector, mode, 0); + if(!ret) + return 0; + Stream->lastFatSectorNr = sector; + Stream->lastFatSectorData = ret; + Stream->lastFatAccessMode = mode; + } + offset = num & Stream->sectorMask; + return ret+offset; +} + + +static int readByte(Fs_t *Stream, int start) +{ + unsigned char *address; + + address = getAddress(Stream, start, FAT_ACCESS_READ); + if(!address) + return -1; + return *address; +} + + +/* + * Fat 12 encoding: + * | byte n | byte n+1 | byte n+2 | + * |7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0| + * | | | | | | | | | | | | | | | | | | | | | | | | | + * | n+0.0 | n+0.5 | n+1.0 | n+1.5 | n+2.0 | n+2.5 | + * \_____ \____ \______/________/_____ / + * ____\______\________/ _____/ ____\_/ + * / \ \ / / \ + * | n+1.5 | n+0.0 | n+0.5 | n+2.0 | n+2.5 | n+1.0 | + * | FAT entry k | FAT entry k+1 | + */ + + /* + * Get and decode a FAT (file allocation table) entry. Returns the cluster + * number on success or 1 on failure. + */ + +static unsigned int fat12_decode(Fs_t *Stream, unsigned int num) +{ + unsigned int start = num * 3 / 2; + int byte0 = readByte(Stream, start); + int byte1 = readByte(Stream, start+1); + + if (num < 2 || byte0 < 0 || byte1 < 0 || num > Stream->num_clus+1) { + fprintf(stderr,"[1] Bad address %d\n", num); + return 1; + } + + if (num & 1) + return (byte1 << 4) | ((byte0 & 0xf0)>>4); + else + return ((byte1 & 0xf) << 8) | byte0; +} + + +/* + * Puts a code into the FAT table. Is the opposite of fat_decode(). No + * sanity checking is done on the code. Returns a 1 on error. + */ +static void fat12_encode(Fs_t *Stream, unsigned int num, unsigned int code) +{ + int start = num * 3 / 2; + unsigned char *address0 = getAddress(Stream, start, FAT_ACCESS_WRITE); + unsigned char *address1 = getAddress(Stream, start+1, FAT_ACCESS_WRITE); + + if (num & 1) { + /* (odd) not on byte boundary */ + *address0 = (*address0 & 0x0f) | ((code << 4) & 0xf0); + *address1 = (code >> 4) & 0xff; + } else { + /* (even) on byte boundary */ + *address0 = code & 0xff; + *address1 = (*address1 & 0xf0) | ((code >> 8) & 0x0f); + } +} + + +/* + * Fat 16 encoding: + * | byte n | byte n+1 | + * |7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0| + * | | | | | | | | | | | | | | | | | + * | FAT entry k | + */ + +static unsigned int fat16_decode(Fs_t *Stream, unsigned int num) +{ + unsigned char *address = getAddress(Stream, num << 1, FAT_ACCESS_READ); + return _WORD(address); +} + +static void fat16_encode(Fs_t *Stream, unsigned int num, unsigned int code) +{ + unsigned char *address = getAddress(Stream, num << 1, FAT_ACCESS_WRITE); + set_word(address, code); +} + + +static unsigned int fast_fat16_decode(Fs_t *Stream, unsigned int num) +{ + unsigned short *address = + (unsigned short *) getAddress(Stream, num << 1, + FAT_ACCESS_READ); + return *address; +} + +static void fast_fat16_encode(Fs_t *Stream, unsigned int num, unsigned int code) +{ + unsigned short *address = + (unsigned short *) getAddress(Stream, num << 1, + FAT_ACCESS_WRITE); + *address = code; +} + + + + +/* + * Fat 32 encoding + */ +static unsigned int fat32_decode(Fs_t *Stream, unsigned int num) +{ + unsigned char *address = getAddress(Stream, num << 2, FAT_ACCESS_READ); + return _DWORD(address); +} + +static void fat32_encode(Fs_t *Stream, unsigned int num, unsigned int code) +{ + unsigned char *address = getAddress(Stream, num << 2, FAT_ACCESS_WRITE); + set_dword(address, code); +} + + +static unsigned int fast_fat32_decode(Fs_t *Stream, unsigned int num) +{ + unsigned int *address = + (unsigned int *) getAddress(Stream, num << 2, + FAT_ACCESS_READ); + return *address; +} + +static void fast_fat32_encode(Fs_t *Stream, unsigned int num, unsigned int code) +{ + unsigned int *address = + (unsigned int *) getAddress(Stream, num << 2, + FAT_ACCESS_WRITE); + *address = code; +} + + +/* + * Write the FAT table to the disk. Up to now the FAT manipulation has + * been done in memory. All errors are fatal. (Might not be too smart + * to wait till the end of the program to write the table. Oh well...) + */ + +void fat_write(Fs_t *This) +{ + int i, j, dups, ret, bit, slot; + int fat_start; + + /*fprintf(stderr, "Fat write\n");*/ + + if (!This->fat_dirty) + return; + + dups = This->num_fat; + if (This->fat_error) + dups = 1; + + + for(i=0; i<dups; i++){ + j = 0; + fat_start = This->fat_start + i*This->fat_len; + for(slot=0;j<This->fat_len;slot++) { + if(!This->FatMap[slot].dirty) { + j += SECT_PER_ENTRY; + continue; + } + for(bit=0; + bit < SECT_PER_ENTRY && j<This->fat_len; + bit++,j++) { + if(!(This->FatMap[slot].dirty & (ONE << bit))) + continue; + ret = fatWriteSector(This,j,slot, bit, i); + if (ret < This->sector_size){ + if (ret < 0 ){ + perror("error in fat_write"); + exit(1); + } else { + fprintf(stderr, + "end of file in fat_write\n"); + exit(1); + } + } + /* if last dupe, zero it out */ + if(i==dups-1) + This->FatMap[slot].dirty &= ~(1<<bit); + } + } + } + /* write the info sector, if any */ + if(This->infoSectorLoc && This->infoSectorLoc != MAX32) { + /* initialize info sector */ + InfoSector_t *infoSector; + infoSector = (InfoSector_t *) safe_malloc(This->sector_size); + set_dword(infoSector->signature1, INFOSECT_SIGNATURE1); + memset(infoSector->filler1, sizeof(infoSector->filler1),0); + memset(infoSector->filler2, sizeof(infoSector->filler2),0); + set_dword(infoSector->signature2, INFOSECT_SIGNATURE2); + set_dword(infoSector->pos, This->last); + set_dword(infoSector->count, This->freeSpace); + set_dword(infoSector->signature3, 0xaa55); + if(forceWriteSector(This, (char *)infoSector, This->infoSectorLoc, 1) != + This->sector_size) + fprintf(stderr,"Trouble writing the info sector\n"); + free(infoSector); + } + This->fat_dirty = 0; + This->lastFatAccessMode = FAT_ACCESS_READ; +} + + + +/* + * Zero-Fat + * Used by mformat. + */ +int zero_fat(Fs_t *Stream, int media_descriptor) +{ + int i, j; + int fat_start; + unsigned char *buf; + + buf = malloc(Stream->sector_size); + if(!buf) { + perror("alloc fat sector buffer"); + return -1; + } + for(i=0; i< Stream->num_fat; i++) { + fat_start = Stream->fat_start + i*Stream->fat_len; + for(j = 0; j < Stream->fat_len; j++) { + if(j <= 1) + memset(buf, 0, Stream->sector_size); + if(!j) { + buf[0] = media_descriptor; + buf[2] = buf[1] = 0xff; + if(Stream->fat_bits > 12) + buf[3] = 0xff; + if(Stream->fat_bits > 16) { + buf[4] = 0xff; + buf[5] = 0xff; + buf[6] = 0xff; + buf[7] = 0x0f; + } + } + + if(forceWriteSector(Stream, (char *)buf, + fat_start + j, 1) != + Stream->sector_size) { + fprintf(stderr, + "Trouble initializing a FAT sector\n"); + free(buf); + return -1; + } + } + } + + free(buf); + Stream->FatMap = GetFatMap(Stream); + if (Stream->FatMap == NULL) { + perror("alloc fat map"); + return -1; + } + return 0; +} + + +void set_fat12(Fs_t *This) +{ + This->fat_bits = 12; + This->end_fat = 0xfff; + This->last_fat = 0xff6; + This->fat_decode = fat12_decode; + This->fat_encode = fat12_encode; +} + +static char word_endian_test[] = { 0x34, 0x12 }; + +void set_fat16(Fs_t *This) +{ + This->fat_bits = 16; + This->end_fat = 0xffff; + This->last_fat = 0xfff6; + + if(sizeof(unsigned short) == 2 && + * (unsigned short *) word_endian_test == 0x1234) { + This->fat_decode = fast_fat16_decode; + This->fat_encode = fast_fat16_encode; + } else { + This->fat_decode = fat16_decode; + This->fat_encode = fat16_encode; + } +} + +static char dword_endian_test[] = { 0x78, 0x56, 0x34, 0x12 }; + +void set_fat32(Fs_t *This) +{ + This->fat_bits = 32; + This->end_fat = 0xfffffff; + This->last_fat = 0xffffff6; + + if(sizeof(unsigned int) == 4 && + * (unsigned int *) dword_endian_test == 0x12345678) { + This->fat_decode = fast_fat32_decode; + This->fat_encode = fast_fat32_encode; + } else { + This->fat_decode = fat32_decode; + This->fat_encode = fat32_encode; + } +} + + +static int check_fat(Fs_t *This) +{ + /* + * This is only a sanity check. For disks with really big FATs, + * there is no point in checking the whole FAT. + */ + + int i, f, tocheck; + if(mtools_skip_check) + return 0; + + /* too few sectors in the FAT */ + if(This->fat_len < NEEDED_FAT_SIZE(This)) + return -1; + /* we do not warn about too much sectors in FAT, which may + * happen when a partition has been shrunk using FIPS, or on + * other occurrences */ + + tocheck = This->num_clus; + if (tocheck < 0 || tocheck + 1 >= This->last_fat) { + fprintf(stderr, "Too many clusters in FAT\n"); + return -1; + } + + if(tocheck > 4096) + tocheck = 4096; + + for ( i= 3 ; i < tocheck; i++){ + f = This->fat_decode(This,i); + if (f == 1 || (f < This->last_fat && f > This->num_clus)){ + fprintf(stderr, + "Cluster # at %d too big(%#x)\n", i,f); + fprintf(stderr,"Probably non MS-DOS disk\n"); + return -1; + } + } + return 0; +} + + +/* + * Read the first sector of FAT table into memory. Crude error detection on + * wrong FAT encoding scheme. + */ +static int check_media_type(Fs_t *This, struct bootsector *boot, + unsigned int tot_sectors) +{ + unsigned char *address; + + This->num_clus = (tot_sectors - This->clus_start) / This->cluster_size; + + This->FatMap = GetFatMap(This); + if (This->FatMap == NULL) { + perror("alloc fat map"); + return -1; + } + + address = getAddress(This, 0, FAT_ACCESS_READ); + if(!address) { + fprintf(stderr, + "Could not read first FAT sector\n"); + return -1; + } + + if(mtools_skip_check) + return 0; + + if(!address[0] && !address[1] && !address[2]) + /* Some Atari disks have zeroes where Dos has media descriptor + * and 0xff. Do not consider this as an error */ + return 0; + + if((address[0] != boot->descr && boot->descr >= 0xf0 && + ((address[0] != 0xf9 && address[0] != 0xf7) + || boot->descr != 0xf0)) || address[0] < 0xf0) { + fprintf(stderr, + "Bad media types %02x/%02x, probably non-MSDOS disk\n", + address[0], + boot->descr); + return -1; + } + + if(address[1] != 0xff || address[2] != 0xff){ + fprintf(stderr,"Initial byte of fat is not 0xff\n"); + return -1; + } + + return 0; +} + +static int fat_32_read(Fs_t *This, struct bootsector *boot, + unsigned int tot_sectors) +{ + int size; + + This->fat_len = DWORD(ext.fat32.bigFat); + This->writeAllFats = !(boot->ext.fat32.extFlags[0] & 0x80); + This->primaryFat = boot->ext.fat32.extFlags[0] & 0xf; + This->rootCluster = DWORD(ext.fat32.rootCluster); + This->clus_start = This->fat_start + This->num_fat * This->fat_len; + + /* read the info sector */ + size = This->sector_size; + This->infoSectorLoc = WORD(ext.fat32.infoSector); + if(This->sector_size >= 512 && + This->infoSectorLoc && This->infoSectorLoc != MAX32) { + InfoSector_t *infoSector; + infoSector = (InfoSector_t *) safe_malloc(size); + if(forceReadSector(This, (char *)infoSector, + This->infoSectorLoc, 1) == This->sector_size && + _DWORD(infoSector->signature1) == INFOSECT_SIGNATURE1 && + _DWORD(infoSector->signature2) == INFOSECT_SIGNATURE2) { + This->freeSpace = _DWORD(infoSector->count); + This->last = _DWORD(infoSector->pos); + } + free(infoSector); + } + + set_fat32(This); + return(check_media_type(This,boot, tot_sectors) || + check_fat(This)); +} + + +static int old_fat_read(Fs_t *This, struct bootsector *boot, + int config_fat_bits, + size_t tot_sectors, int nodups) +{ + This->writeAllFats = 1; + This->primaryFat = 0; + This->dir_start = This->fat_start + This->num_fat * This->fat_len; + This->clus_start = This->dir_start + This->dir_len; + This->infoSectorLoc = MAX32; + + if(nodups) + This->num_fat = 1; + + if(check_media_type(This,boot, tot_sectors)) + return -1; + + if(This->num_clus > FAT12) { + set_fat16(This); + /* third FAT byte must be 0xff */ + if(!mtools_skip_check && readByte(This, 3) != 0xff) + return -1; + } else + set_fat12(This); + + return check_fat(This); +} + +/* + * Read the first sector of the FAT table into memory and initialize + * structures. + */ +int fat_read(Fs_t *This, struct bootsector *boot, int fat_bits, + size_t tot_sectors, int nodups) +{ + This->fat_error = 0; + This->fat_dirty = 0; + This->last = MAX32; + This->freeSpace = MAX32; + This->lastFatSectorNr = 0; + This->lastFatSectorData = 0; + + if(This->fat_len) + return old_fat_read(This, boot, fat_bits, tot_sectors, nodups); + else + return fat_32_read(This, boot, tot_sectors); +} + + +unsigned int fatDecode(Fs_t *This, unsigned int pos) +{ + int ret; + + ret = This->fat_decode(This, pos); + if(ret && (ret < 2 || ret > This->num_clus+1) && ret < This->last_fat) { + fprintf(stderr, "Bad FAT entry %d at %d\n", ret, pos); + This->fat_error++; + } + return ret; +} + +/* append a new cluster */ +void fatAppend(Fs_t *This, unsigned int pos, unsigned int newpos) +{ + This->fat_encode(This, pos, newpos); + This->fat_encode(This, newpos, This->end_fat); + if(This->freeSpace != MAX32) + This->freeSpace--; +} + +/* de-allocates the given cluster */ +void fatDeallocate(Fs_t *This, unsigned int pos) +{ + This->fat_encode(This, pos, 0); + if(This->freeSpace != MAX32) + This->freeSpace++; +} + +/* allocate a new cluster */ +void fatAllocate(Fs_t *This, unsigned int pos, unsigned int value) +{ + This->fat_encode(This, pos, value); + if(This->freeSpace != MAX32) + This->freeSpace--; +} + +void fatEncode(Fs_t *This, unsigned int pos, unsigned int value) +{ + unsigned int oldvalue = This->fat_decode(This, pos); + This->fat_encode(This, pos, value); + if(This->freeSpace != MAX32) { + if(oldvalue) + This->freeSpace++; + if(value) + This->freeSpace--; + } +} + +unsigned int get_next_free_cluster(Fs_t *This, unsigned int last) +{ + int i; + + if(This->last != MAX32) + last = This->last; + + if (last < 2 || + last >= This->num_clus+1) + last = 1; + + for (i=last+1; i< This->num_clus+2; i++) { + if (!fatDecode(This, i)) { + This->last = i; + return i; + } + } + + for(i=2; i < last+1; i++) { + if (!fatDecode(This, i)) { + This->last = i; + return i; + } + } + + + fprintf(stderr,"No free cluster %d %d\n", This->preallocatedClusters, + This->last); + return 1; +} + +int fat_error(Stream_t *Dir) +{ + Stream_t *Stream = GetFs(Dir); + DeclareThis(Fs_t); + + if(This->fat_error) + fprintf(stderr,"Fat error detected\n"); + + return This->fat_error; +} + +int fat32RootCluster(Stream_t *Dir) +{ + Stream_t *Stream = GetFs(Dir); + DeclareThis(Fs_t); + + if(This->fat_bits == 32) + return This->rootCluster; + else + return 0; +} + + +/* + * Get the amount of free space on the diskette + */ + +mt_size_t getfree(Stream_t *Dir) +{ + Stream_t *Stream = GetFs(Dir); + DeclareThis(Fs_t); + + if(This->freeSpace == MAX32 || This->freeSpace == 0) { + register unsigned int i; + size_t total; + + total = 0L; + for (i = 2; i < This->num_clus + 2; i++) + if (!fatDecode(This,i)) + total++; + This->freeSpace = total; + } + return sectorsToBytes((Stream_t*)This, + This->freeSpace * This->cluster_size); +} + + +/* + * Ensure that there is a minimum of total sectors free + */ +int getfreeMinClusters(Stream_t *Dir, size_t size) +{ + Stream_t *Stream = GetFs(Dir); + DeclareThis(Fs_t); + register unsigned int i, last; + size_t total; + + if(batchmode && This->freeSpace == MAX32) + getfree(Stream); + + if(This->freeSpace != MAX32) { + if(This->freeSpace >= size) + return 1; + else { + fprintf(stderr, "Disk full\n"); + got_signal = 1; + return 0; + } + } + + total = 0L; + + /* we start at the same place where we'll start later to actually + * allocate the sectors. That way, the same sectors of the FAT, which + * are already loaded during getfreeMin will be able to be reused + * during get_next_free_cluster */ + last = This->last; + + if ( last < 2 || last >= This->num_clus + 2) + last = 1; + for (i=last+1; i< This->num_clus+2; i++){ + if (!fatDecode(This, i)) + total++; + if(total >= size) + return 1; + } + for(i=2; i < last+1; i++){ + if (!fatDecode(This, i)) + total++; + if(total >= size) + return 1; + } + fprintf(stderr, "Disk full\n"); + got_signal = 1; + return 0; +} + + +int getfreeMinBytes(Stream_t *Dir, mt_size_t size) +{ + Stream_t *Stream = GetFs(Dir); + DeclareThis(Fs_t); + size_t size2; + + size2 = size / (This->sector_size * This->cluster_size); + if(size % (This->sector_size * This->cluster_size)) + size2++; + return getfreeMinClusters(Dir, size2); +} + + +unsigned int getStart(Stream_t *Dir, struct directory *dir) +{ + Stream_t *Stream = GetFs(Dir); + unsigned int first; + + first = START(dir); + if(fat32RootCluster(Stream)) + first |= STARTHI(dir) << 16; + return first; +} + +int fs_free(Stream_t *Stream) +{ + DeclareThis(Fs_t); + + if(This->FatMap) { + int i, nr_entries; + nr_entries = (This->fat_len + SECT_PER_ENTRY - 1) / + SECT_PER_ENTRY; + for(i=0; i< nr_entries; i++) + if(This->FatMap[i].data) + free(This->FatMap[i].data); + free(This->FatMap); + } + return 0; +} diff --git a/commands/i386/mtools-3.9.7/fat_free.c b/commands/i386/mtools-3.9.7/fat_free.c new file mode 100755 index 000000000..65dacb17d --- /dev/null +++ b/commands/i386/mtools-3.9.7/fat_free.c @@ -0,0 +1,55 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "fsP.h" +#include "mtoolsDirent.h" + +/* + * Remove a string of FAT entries (delete the file). The argument is + * the beginning of the string. Does not consider the file length, so + * if FAT is corrupted, watch out! + */ + +int fat_free(Stream_t *Dir, unsigned int fat) +{ + Stream_t *Stream = GetFs(Dir); + DeclareThis(Fs_t); + unsigned int next_no_step; + /* a zero length file? */ + if (fat == 0) + return(0); + + /* CONSTCOND */ + while (!This->fat_error) { + /* get next cluster number */ + next_no_step = fatDecode(This,fat); + /* mark current cluster as empty */ + fatDeallocate(This,fat); + if (next_no_step >= This->last_fat) + break; + fat = next_no_step; + } + return(0); +} + +int fatFreeWithDir(Stream_t *Dir, struct directory *dir) +{ + unsigned int first; + + if((!strncmp(dir->name,". ",8) || + !strncmp(dir->name,".. ",8)) && + !strncmp(dir->ext," ",3)) { + fprintf(stderr,"Trying to remove . or .. entry\n"); + return -1; + } + + first = START(dir); + if(fat32RootCluster(Dir)) + first |= STARTHI(dir) << 16; + return fat_free(Dir, first); +} + +int fatFreeWithDirentry(direntry_t *entry) +{ + return fatFreeWithDir(entry->Dir, &entry->dir); +} + diff --git a/commands/i386/mtools-3.9.7/file.c b/commands/i386/mtools-3.9.7/file.c new file mode 100755 index 000000000..4bcfc7c2c --- /dev/null +++ b/commands/i386/mtools-3.9.7/file.c @@ -0,0 +1,676 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "stream.h" +#include "mtools.h" +#include "fsP.h" +#include "file.h" +#include "htable.h" +#include "dirCache.h" + +typedef struct File_t { + Class_t *Class; + int refs; + struct Fs_t *Fs; /* Filesystem that this fat file belongs to */ + Stream_t *Buffer; + + int (*map)(struct File_t *this, off_t where, size_t *len, int mode, + mt_off_t *res); + size_t FileSize; + + size_t preallocatedSize; + int preallocatedClusters; + + /* Absolute position of first cluster of file */ + unsigned int FirstAbsCluNr; + + /* Absolute position of previous cluster */ + unsigned int PreviousAbsCluNr; + + /* Relative position of previous cluster */ + unsigned int PreviousRelCluNr; + direntry_t direntry; + int hint; + struct dirCache_t *dcp; + + unsigned int loopDetectRel; + unsigned int loopDetectAbs; +} File_t; + +static Class_t FileClass; +T_HashTable *filehash; + +static File_t *getUnbufferedFile(Stream_t *Stream) +{ + while(Stream->Class != &FileClass) + Stream = Stream->Next; + return (File_t *) Stream; +} + +Fs_t *getFs(Stream_t *Stream) +{ + return getUnbufferedFile(Stream)->Fs; +} + +struct dirCache_t **getDirCacheP(Stream_t *Stream) +{ + return &getUnbufferedFile(Stream)->dcp; +} + +direntry_t *getDirentry(Stream_t *Stream) +{ + return &getUnbufferedFile(Stream)->direntry; +} + + +static int recalcPreallocSize(File_t *This) +{ + size_t currentClusters, neededClusters; + int clus_size; + int neededPrealloc; + Fs_t *Fs = This->Fs; + int r; + + if(This->FileSize & 0xc0000000) { + fprintf(stderr, "Bad filesize\n"); + } + if(This->preallocatedSize & 0xc0000000) { + fprintf(stderr, "Bad preallocated size %x\n", + (int) This->preallocatedSize); + } + + clus_size = Fs->cluster_size * Fs->sector_size; + + currentClusters = (This->FileSize + clus_size - 1) / clus_size; + neededClusters = (This->preallocatedSize + clus_size - 1) / clus_size; + neededPrealloc = neededClusters - currentClusters; + if(neededPrealloc < 0) + neededPrealloc = 0; + r = fsPreallocateClusters(Fs, neededPrealloc - This->preallocatedClusters); + if(r) + return r; + This->preallocatedClusters = neededPrealloc; + return 0; +} + +static int _loopDetect(unsigned int *oldrel, unsigned int rel, + unsigned int *oldabs, unsigned int abs) +{ + if(*oldrel && rel > *oldrel && abs == *oldabs) { + fprintf(stderr, "loop detected! oldrel=%d newrel=%d abs=%d\n", + *oldrel, rel, abs); + return -1; + } + + if(rel >= 2 * *oldrel + 1) { + *oldrel = rel; + *oldabs = abs; + } + return 0; +} + + +static int loopDetect(File_t *This, unsigned int rel, unsigned int abs) +{ + return _loopDetect(&This->loopDetectRel, rel, &This->loopDetectAbs, abs); +} + +static unsigned int _countBlocks(Fs_t *This, unsigned int block) +{ + unsigned int blocks; + unsigned int rel, oldabs, oldrel; + + blocks = 0; + + oldabs = oldrel = rel = 0; + + while (block <= This->last_fat && block != 1 && block) { + blocks++; + block = fatDecode(This, block); + rel++; + if(_loopDetect(&oldrel, rel, &oldabs, block) < 0) + block = -1; + } + return blocks; +} + +unsigned int countBlocks(Stream_t *Dir, unsigned int block) +{ + Stream_t *Stream = GetFs(Dir); + DeclareThis(Fs_t); + + return _countBlocks(This, block); +} + +/* returns number of bytes in a directory. Represents a file size, and + * can hence be not bigger than 2^32 + */ +static size_t countBytes(Stream_t *Dir, unsigned int block) +{ + Stream_t *Stream = GetFs(Dir); + DeclareThis(Fs_t); + + return _countBlocks(This, block) * + This->sector_size * This->cluster_size; +} + +void printFat(Stream_t *Stream) +{ + File_t *This = getUnbufferedFile(Stream); + unsigned long n; + int rel; + unsigned long begin, end; + int first; + + n = This->FirstAbsCluNr; + if(!n) { + printf("Root directory or empty file\n"); + return; + } + + rel = 0; + first = 1; + begin = end = 0; + do { + if (first || n != end+1) { + if (!first) { + if (begin != end) + printf("-%lu", end); + printf("> "); + } + begin = end = n; + printf("<%lu", begin); + } else { + end++; + } + first = 0; + n = fatDecode(This->Fs, n); + rel++; + if(loopDetect(This, rel, n) < 0) + n = 1; + } while (n <= This->Fs->last_fat && n != 1); + if(!first) { + if (begin != end) + printf("-%lu", end); + printf(">"); + } +} + +static int normal_map(File_t *This, off_t where, size_t *len, int mode, + mt_off_t *res) +{ + int offset; + off_t end; + int NrClu; /* number of clusters to read */ + unsigned int RelCluNr; + unsigned int CurCluNr; + unsigned int NewCluNr; + unsigned int AbsCluNr; + int clus_size; + Fs_t *Fs = This->Fs; + + *res = 0; + clus_size = Fs->cluster_size * Fs->sector_size; + offset = where % clus_size; + + if (mode == MT_READ) + maximize(*len, This->FileSize - where); + if (*len == 0 ) + return 0; + + if (This->FirstAbsCluNr < 2){ + if( mode == MT_READ || *len == 0){ + *len = 0; + return 0; + } + NewCluNr = get_next_free_cluster(This->Fs, 1); + if (NewCluNr == 1 ){ + errno = ENOSPC; + return -2; + } + hash_remove(filehash, (void *) This, This->hint); + This->FirstAbsCluNr = NewCluNr; + hash_add(filehash, (void *) This, &This->hint); + fatAllocate(This->Fs, NewCluNr, Fs->end_fat); + } + + RelCluNr = where / clus_size; + + if (RelCluNr >= This->PreviousRelCluNr){ + CurCluNr = This->PreviousRelCluNr; + AbsCluNr = This->PreviousAbsCluNr; + } else { + CurCluNr = 0; + AbsCluNr = This->FirstAbsCluNr; + } + + + NrClu = (offset + *len - 1) / clus_size; + while (CurCluNr <= RelCluNr + NrClu){ + if (CurCluNr == RelCluNr){ + /* we have reached the beginning of our zone. Save + * coordinates */ + This->PreviousRelCluNr = RelCluNr; + This->PreviousAbsCluNr = AbsCluNr; + } + NewCluNr = fatDecode(This->Fs, AbsCluNr); + if (NewCluNr == 1 || NewCluNr == 0){ + fprintf(stderr,"Fat problem while decoding %d %x\n", + AbsCluNr, NewCluNr); + exit(1); + } + if(CurCluNr == RelCluNr + NrClu) + break; + if (NewCluNr > Fs->last_fat && mode == MT_WRITE){ + /* if at end, and writing, extend it */ + NewCluNr = get_next_free_cluster(This->Fs, AbsCluNr); + if (NewCluNr == 1 ){ /* no more space */ + errno = ENOSPC; + return -2; + } + fatAppend(This->Fs, AbsCluNr, NewCluNr); + } + + if (CurCluNr < RelCluNr && NewCluNr > Fs->last_fat){ + *len = 0; + return 0; + } + + if (CurCluNr >= RelCluNr && NewCluNr != AbsCluNr + 1) + break; + CurCluNr++; + AbsCluNr = NewCluNr; + if(loopDetect(This, CurCluNr, AbsCluNr)) { + errno = EIO; + return -2; + } + } + + maximize(*len, (1 + CurCluNr - RelCluNr) * clus_size - offset); + + end = where + *len; + if(batchmode && mode == MT_WRITE && end >= This->FileSize) { + *len += ROUND_UP(end, clus_size) - end; + } + + if((*len + offset) / clus_size + This->PreviousAbsCluNr-2 > + Fs->num_clus) { + fprintf(stderr, "cluster too big\n"); + exit(1); + } + + *res = sectorsToBytes((Stream_t*)Fs, + (This->PreviousAbsCluNr-2) * Fs->cluster_size + + Fs->clus_start) + offset; + return 1; +} + + +static int root_map(File_t *This, off_t where, size_t *len, int mode, + mt_off_t *res) +{ + Fs_t *Fs = This->Fs; + + if(Fs->dir_len * Fs->sector_size < where) { + *len = 0; + errno = ENOSPC; + return -2; + } + + maximize(*len, Fs->dir_len * Fs->sector_size - where); + if (*len == 0) + return 0; + + *res = sectorsToBytes((Stream_t*)Fs, Fs->dir_start) + where; + return 1; +} + + +static int read_file(Stream_t *Stream, char *buf, mt_off_t iwhere, + size_t len) +{ + DeclareThis(File_t); + mt_off_t pos; + int err; + off_t where = truncBytes32(iwhere); + + Stream_t *Disk = This->Fs->Next; + + err = This->map(This, where, &len, MT_READ, &pos); + if(err <= 0) + return err; + return READS(Disk, buf, pos, len); +} + +static int write_file(Stream_t *Stream, char *buf, mt_off_t iwhere, size_t len) +{ + DeclareThis(File_t); + mt_off_t pos; + int ret; + size_t requestedLen; + Stream_t *Disk = This->Fs->Next; + off_t where = truncBytes32(iwhere); + int err; + + requestedLen = len; + err = This->map(This, where, &len, MT_WRITE, &pos); + if( err <= 0) + return err; + if(batchmode) + ret = force_write(Disk, buf, pos, len); + else + ret = WRITES(Disk, buf, pos, len); + if(ret > requestedLen) + ret = requestedLen; + if (ret > 0 && where + ret > This->FileSize ) + This->FileSize = where + ret; + recalcPreallocSize(This); + return ret; +} + + +/* + * Convert an MSDOS time & date stamp to the Unix time() format + */ + +static int month[] = {0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, + 0, 0, 0 }; +static inline time_t conv_stamp(struct directory *dir) +{ + struct tm *tmbuf; + long tzone, dst; + time_t accum, tmp; + + accum = DOS_YEAR(dir) - 1970; /* years past */ + + /* days passed */ + accum = accum * 365L + month[DOS_MONTH(dir)-1] + DOS_DAY(dir); + + /* leap years */ + accum += (DOS_YEAR(dir) - 1972) / 4L; + + /* back off 1 day if before 29 Feb */ + if (!(DOS_YEAR(dir) % 4) && DOS_MONTH(dir) < 3) + accum--; + accum = accum * 24L + DOS_HOUR(dir); /* hours passed */ + accum = accum * 60L + DOS_MINUTE(dir); /* minutes passed */ + accum = accum * 60L + DOS_SEC(dir); /* seconds passed */ + +#ifndef OS_Minix + /* correct for Time Zone */ +#ifdef HAVE_GETTIMEOFDAY + { + struct timeval tv; + struct timezone tz; + + gettimeofday(&tv, &tz); + tzone = tz.tz_minuteswest * 60L; + } +#else +#ifdef HAVE_TZSET + { +#ifndef OS_ultrix + /* Ultrix defines this to be a different type */ + extern long timezone; +#endif + tzset(); + tzone = (long) timezone; + } +#else + tzone = 0; +#endif /* HAVE_TZSET */ +#endif /* HAVE_GETTIMEOFDAY */ + + accum += tzone; +#endif /* OS_Minix */ + + /* correct for Daylight Saving Time */ + tmp = accum; + tmbuf = localtime(&tmp); +#ifndef OS_Minix + dst = (tmbuf->tm_isdst) ? (-60L * 60L) : 0L; + accum += dst; +#endif + + return accum; +} + + +static int get_file_data(Stream_t *Stream, time_t *date, mt_size_t *size, + int *type, int *address) +{ + DeclareThis(File_t); + + if(date) + *date = conv_stamp(& This->direntry.dir); + if(size) + *size = (mt_size_t) This->FileSize; + if(type) + *type = This->direntry.dir.attr & ATTR_DIR; + if(address) + *address = This->FirstAbsCluNr; + return 0; +} + + +static int free_file(Stream_t *Stream) +{ + DeclareThis(File_t); + Fs_t *Fs = This->Fs; + fsPreallocateClusters(Fs, -This->preallocatedClusters); + FREE(&This->direntry.Dir); + freeDirCache(Stream); + return hash_remove(filehash, (void *) Stream, This->hint); +} + + +static int flush_file(Stream_t *Stream) +{ + DeclareThis(File_t); + direntry_t *entry = &This->direntry; + + if(isRootDir(Stream)) { + return 0; + } + + if(This->FirstAbsCluNr != getStart(entry->Dir, &entry->dir)) { + set_word(entry->dir.start, This->FirstAbsCluNr & 0xffff); + set_word(entry->dir.startHi, This->FirstAbsCluNr >> 16); + dir_write(entry); + } + return 0; +} + + +static int pre_allocate_file(Stream_t *Stream, mt_size_t isize) +{ + DeclareThis(File_t); + + size_t size = truncBytes32(isize); + + if(size > This->FileSize && + size > This->preallocatedSize) { + This->preallocatedSize = size; + return recalcPreallocSize(This); + } else + return 0; +} + +static Class_t FileClass = { + read_file, + write_file, + flush_file, /* flush */ + free_file, /* free */ + 0, /* get_geom */ + get_file_data, + pre_allocate_file +}; + +static unsigned int getAbsCluNr(File_t *This) +{ + if(This->FirstAbsCluNr) + return This->FirstAbsCluNr; + if(isRootDir((Stream_t *) This)) + return 0; + return 1; +} + +static unsigned int func1(void *Stream) +{ + DeclareThis(File_t); + + return getAbsCluNr(This) ^ (long) This->Fs; +} + +static unsigned int func2(void *Stream) +{ + DeclareThis(File_t); + + return getAbsCluNr(This); +} + +static int comp(void *Stream, void *Stream2) +{ + DeclareThis(File_t); + + File_t *This2 = (File_t *) Stream2; + + return This->Fs != This2->Fs || + getAbsCluNr(This) != getAbsCluNr(This2); +} + +static void init_hash(void) +{ + static int is_initialised=0; + + if(!is_initialised){ + make_ht(func1, func2, comp, 20, &filehash); + is_initialised = 1; + } +} + + +static Stream_t *_internalFileOpen(Stream_t *Dir, unsigned int first, + size_t size, direntry_t *entry) +{ + Stream_t *Stream = GetFs(Dir); + DeclareThis(Fs_t); + File_t Pattern; + File_t *File; + + init_hash(); + This->refs++; + + if(first != 1){ + /* we use the illegal cluster 1 to mark newly created files. + * do not manage those by hashtable */ + Pattern.Fs = This; + Pattern.Class = &FileClass; + if(first || (entry && !IS_DIR(entry))) + Pattern.map = normal_map; + else + Pattern.map = root_map; + Pattern.FirstAbsCluNr = first; + Pattern.loopDetectRel = 0; + Pattern.loopDetectAbs = first; + if(!hash_lookup(filehash, (T_HashTableEl) &Pattern, + (T_HashTableEl **)&File, 0)){ + File->refs++; + This->refs--; + return (Stream_t *) File; + } + } + + File = New(File_t); + if (!File) + return NULL; + File->dcp = 0; + File->preallocatedClusters = 0; + File->preallocatedSize = 0; + /* memorize dir for date and attrib */ + File->direntry = *entry; + if(entry->entry == -3) + File->direntry.Dir = (Stream_t *) File; /* root directory */ + else + COPY(File->direntry.Dir); + + File->Class = &FileClass; + File->Fs = This; + if(first || (entry && !IS_DIR(entry))) + File->map = normal_map; + else + File->map = root_map; /* FAT 12/16 root directory */ + if(first == 1) + File->FirstAbsCluNr = 0; + else + File->FirstAbsCluNr = first; + + File->loopDetectRel = 0; + File->loopDetectAbs = 0; + + File->PreviousRelCluNr = 0xffff; + File->FileSize = size; + File->refs = 1; + File->Buffer = 0; + hash_add(filehash, (void *) File, &File->hint); + return (Stream_t *) File; +} + +Stream_t *OpenRoot(Stream_t *Dir) +{ + unsigned int num; + direntry_t entry; + size_t size; + Stream_t *file; + + memset(&entry, 0, sizeof(direntry_t)); + + num = fat32RootCluster(Dir); + + /* make the directory entry */ + entry.entry = -3; + entry.name[0] = '\0'; + mk_entry("/", ATTR_DIR, num, 0, 0, &entry.dir); + + if(num) + size = countBytes(Dir, num); + else { + Fs_t *Fs = (Fs_t *) GetFs(Dir); + size = Fs->dir_len * Fs->sector_size; + } + file = _internalFileOpen(Dir, num, size, &entry); + bufferize(&file); + return file; +} + + +Stream_t *OpenFileByDirentry(direntry_t *entry) +{ + Stream_t *file; + unsigned int first; + size_t size; + + first = getStart(entry->Dir, &entry->dir); + + if(!first && IS_DIR(entry)) + return OpenRoot(entry->Dir); + if (IS_DIR(entry)) + size = countBytes(entry->Dir, first); + else + size = FILE_SIZE(&entry->dir); + file = _internalFileOpen(entry->Dir, first, size, entry); + if(IS_DIR(entry)) { + bufferize(&file); + if(first == 1) + dir_grow(file, 0); + } + + return file; +} + + +int isRootDir(Stream_t *Stream) +{ + File_t *This = getUnbufferedFile(Stream); + + return This->map == root_map; +} diff --git a/commands/i386/mtools-3.9.7/file.h b/commands/i386/mtools-3.9.7/file.h new file mode 100755 index 000000000..39d4dd543 --- /dev/null +++ b/commands/i386/mtools-3.9.7/file.h @@ -0,0 +1,11 @@ +#ifndef MTOOLS_FILE_H +#define MTOOLS_FILE_H + +#include "stream.h" +#include "mtoolsDirent.h" + +Stream_t *OpenFileByDirentry(direntry_t *entry); +Stream_t *OpenRoot(Stream_t *Dir); +void printFat(Stream_t *Stream); +direntry_t *getDirentry(Stream_t *Stream); +#endif diff --git a/commands/i386/mtools-3.9.7/file_name.c b/commands/i386/mtools-3.9.7/file_name.c new file mode 100755 index 000000000..d5da96ddd --- /dev/null +++ b/commands/i386/mtools-3.9.7/file_name.c @@ -0,0 +1,203 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "vfat.h" +#include "codepage.h" + +/* Write a DOS name + extension into a legal unix-style name. */ +char *unix_normalize (char *ans, char *name, char *ext) +{ + char *a; + int j; + + for (a=ans,j=0; (j<8) && (name[j] > ' '); ++j,++a) + *a = name[j]; + if(*ext > ' ') { + *a++ = '.'; + for (j=0; j<3 && ext[j] > ' '; ++j,++a) + *a = ext[j]; + } + *a++ = '\0'; + return ans; +} + +typedef enum Case_l { + NONE, + UPPER, + LOWER +} Case_t; + +static void TranslateToDos(const char *s, char *t, int count, + char *end, Case_t *Case, int *mangled) +{ + *Case = NONE; + for( ; *s && (s < end || !end); s++) { + if(!count) { + *mangled |= 3; + break; + } + /* skip spaces & dots */ + if(*s == ' ' || *s == '.') { + *mangled |= 3; + continue; + } + + /* convert to dos */ + if((*s) & 0x80) { + *mangled |= 1; + *t = to_dos(*s); + } + + if ((*s & 0x7f) < ' ' ) { + *mangled |= 3; + *t = '_'; + } else if (islower((unsigned char)*s)) { + *t = toupper(*s); + if(*Case == UPPER && !mtools_no_vfat) + *mangled |= 1; + else + *Case = LOWER; + } else if (isupper((unsigned char)*s)) { + *t = *s; + if(*Case == LOWER && !mtools_no_vfat) + *mangled |= 1; + else + *Case = UPPER; + } else if((*s) & 0x80) + *t = mstoupper(*t); /* upper case */ + else + *t = *s; + count--; + t++; + } +} + +/* dos_name + * + * Convert a Unix-style filename to a legal MSDOS name and extension. + * Will truncate file and extension names, will substitute + * the character '~' for any illegal character(s) in the name. + */ +char *dos_name(char *name, int verbose, int *mangled, char *ans) +{ + char *s, *ext; + register int i; + Case_t BaseCase, ExtCase; + + *mangled = 0; + + /* skip drive letter */ + name = skip_drive(name); + + /* zap the leading path */ + name = (char *) _basename(name); + if ((s = strrchr(name, '\\'))) + name = s + 1; + + memset(ans, ' ', 11); + ans[11]='\0'; + + /* skip leading dots and spaces */ + i = strspn(name, ". "); + if(i) { + name += i; + *mangled = 3; + } + + ext = strrchr(name, '.'); + + /* main name */ + TranslateToDos(name, ans, 8, ext, &BaseCase, mangled); + if(ext) + TranslateToDos(ext+1, ans+8, 3, 0, &ExtCase, mangled); + + if(*mangled & 2) + autorename_short(ans, 0); + + if(!*mangled) { + if(BaseCase == LOWER) + *mangled |= BASECASE; + if(ExtCase == LOWER) + *mangled |= EXTCASE; + if((BaseCase == LOWER || ExtCase == LOWER) && + !mtools_no_vfat) { + *mangled |= 1; + } + } + return ans; +} + + +/* + * Get rid of spaces in an MSDOS 'raw' name (one that has come from the + * directory structure) so that it can be used for regular expression + * matching with a Unix filename. Also used to 'unfix' a name that has + * been altered by dos_name(). + */ + +char *unix_name(char *name, char *ext, char Case, char *ans) +{ + char *s, tname[9], text[4]; + int i; + + strncpy(tname, (char *) name, 8); + tname[8] = '\0'; + if ((s = strchr(tname, ' '))) + *s = '\0'; + + if(!(Case & (BASECASE | EXTCASE)) && mtools_ignore_short_case) + Case |= BASECASE | EXTCASE; + + if(Case & BASECASE) + for(i=0;i<8 && tname[i];i++) + tname[i] = tolower(tname[i]); + + strncpy(text, (char *) ext, 3); + text[3] = '\0'; + if ((s = strchr(text, ' '))) + *s = '\0'; + + if(Case & EXTCASE) + for(i=0;i<3 && text[i];i++) + text[i] = tolower(text[i]); + + if (*text) { + strcpy(ans, tname); + strcat(ans, "."); + strcat(ans, text); + } else + strcpy(ans, tname); + + /* fix special characters (above 0x80) */ + to_unix(ans,11); + return(ans); +} + +/* If null encountered, set *end to 0x40 and write nulls rest of way + * 950820: Win95 does not like this! It complains about bad characters. + * So, instead: If null encountered, set *end to 0x40, write the null, and + * write 0xff the rest of the way (that is what Win95 seems to do; hopefully + * that will make it happy) + */ +/* Always return num */ +int unicode_write(char *in, struct unicode_char *out, int num, int *end_p) +{ + int j; + + for (j=0; j<num; ++j) { + out->uchar = '\0'; /* Hard coded to ASCII */ + if (*end_p) + /* Fill with 0xff */ + out->uchar = out->lchar = (char) 0xff; + else { + out->lchar = *in; + if (! *in) { + *end_p = VSE_LAST; + } + } + + ++out; + ++in; + } + return num; +} diff --git a/commands/i386/mtools-3.9.7/file_read.c b/commands/i386/mtools-3.9.7/file_read.c new file mode 100755 index 000000000..aba5cb121 --- /dev/null +++ b/commands/i386/mtools-3.9.7/file_read.c @@ -0,0 +1,37 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "file.h" + +/* + * Read the clusters given the beginning FAT entry. Returns 0 on success. + */ + +int file_read(FILE *fp, Stream_t *Source, int textmode, int stripmode) +{ + char buffer[16384]; + int pos; + int ret; + + if (!Source){ + fprintf(stderr,"Couldn't open source file\n"); + return -1; + } + + pos = 0; + while(1){ + ret = Source->Class->read(Source, buffer, (mt_off_t) pos, 16384); + if (ret < 0 ){ + perror("file read"); + return -1; + } + if ( ret == 0) + break; + if(!fwrite(buffer, 1, ret, fp)){ + perror("write"); + return -1; + } + pos += ret; + } + return 0; +} diff --git a/commands/i386/mtools-3.9.7/filter.c b/commands/i386/mtools-3.9.7/filter.c new file mode 100755 index 000000000..fd49f1574 --- /dev/null +++ b/commands/i386/mtools-3.9.7/filter.c @@ -0,0 +1,140 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" + +typedef struct Filter_t { + Class_t *Class; + int refs; + Stream_t *Next; + Stream_t *Buffer; + + int dospos; + int unixpos; + int mode; + int rw; + int lastchar; +} Filter_t; + +#define F_READ 1 +#define F_WRITE 2 + +/* read filter filters out messy dos' bizarre end of lines and final 0x1a's */ + +static int read_filter(Stream_t *Stream, char *buf, mt_off_t iwhere, size_t len) +{ + DeclareThis(Filter_t); + int i,j,ret; + + off_t where = truncBytes32(iwhere); + + if ( where != This->unixpos ){ + fprintf(stderr,"Bad offset\n"); + exit(1); + } + if (This->rw == F_WRITE){ + fprintf(stderr,"Change of transfer direction!\n"); + exit(1); + } + This->rw = F_READ; + + ret = READS(This->Next, buf, (mt_off_t) This->dospos, len); + if ( ret < 0 ) + return ret; + + j = 0; + for (i=0; i< ret; i++){ + if ( buf[i] == '\r' ) + continue; + if (buf[i] == 0x1a) + break; + This->lastchar = buf[j++] = buf[i]; + } + + This->dospos += i; + This->unixpos += j; + return j; +} + +static int write_filter(Stream_t *Stream, char *buf, mt_off_t iwhere, + size_t len) +{ + DeclareThis(Filter_t); + int i,j,ret; + char buffer[1025]; + + off_t where = truncBytes32(iwhere); + + if(This->unixpos == -1) + return -1; + + if (where != This->unixpos ){ + fprintf(stderr,"Bad offset\n"); + exit(1); + } + + if (This->rw == F_READ){ + fprintf(stderr,"Change of transfer direction!\n"); + exit(1); + } + This->rw = F_WRITE; + + j=i=0; + while(i < 1024 && j < len){ + if (buf[j] == '\n' ){ + buffer[i++] = '\r'; + buffer[i++] = '\n'; + j++; + continue; + } + buffer[i++] = buf[j++]; + } + This->unixpos += j; + + ret = force_write(This->Next, buffer, (mt_off_t) This->dospos, i); + if(ret >0 ) + This->dospos += ret; + if ( ret != i ){ + /* no space on target file ? */ + This->unixpos = -1; + return -1; + } + return j; +} + +static int free_filter(Stream_t *Stream) +{ + DeclareThis(Filter_t); + char buffer=0x1a; + + /* write end of file */ + if (This->rw == F_WRITE) + return force_write(This->Next, &buffer, (mt_off_t) This->dospos, 1); + else + return 0; +} + +static Class_t FilterClass = { + read_filter, + write_filter, + 0, /* flush */ + free_filter, + 0, /* set geometry */ + get_data_pass_through, + 0 +}; + +Stream_t *open_filter(Stream_t *Next) +{ + Filter_t *This; + + This = New(Filter_t); + if (!This) + return NULL; + This->Class = &FilterClass; + This->dospos = This->unixpos = This->rw = 0; + This->Next = Next; + This->refs = 1; + This->Buffer = 0; + + return (Stream_t *) This; +} diff --git a/commands/i386/mtools-3.9.7/floppyd_io.c b/commands/i386/mtools-3.9.7/floppyd_io.c new file mode 100755 index 000000000..e050827b5 --- /dev/null +++ b/commands/i386/mtools-3.9.7/floppyd_io.c @@ -0,0 +1,559 @@ +/* + * IO to the floppyd daemon running on the local X-Server Host + * + * written by: + * + * Peter Schlaile + * + * udbz@rz.uni-karlsruhe.de + * + */ + +#include "sysincludes.h" +#include "stream.h" +#include "mtools.h" +#include "msdos.h" +#include "scsi.h" +#include "partition.h" +#include "floppyd_io.h" + +#ifdef USE_FLOPPYD + +/* ######################################################################## */ + + +typedef unsigned char Byte; +typedef unsigned long Dword; + +char* AuthErrors[] = { + "Auth success!", + "Auth failed: Packet oversized!", + "Auth failed: X-Cookie doesn't match!", + "Auth failed: Wrong transmission protocol version!", + "Auth failed: Device locked!" +}; + + +typedef struct RemoteFile_t { + Class_t *Class; + int refs; + Stream_t *Next; + Stream_t *Buffer; + int fd; + mt_off_t offset; + mt_off_t lastwhere; + mt_off_t size; +} RemoteFile_t; + + +#ifndef HAVE_HTONS +unsigned short myhtons(unsigned short parm) +{ + Byte val[2]; + + val[0] = (parm >> 8) & 0xff; + val[1] = parm & 0xff; + + return *((unsigned short*) (val)); +} +#endif + +Dword byte2dword(Byte* val) +{ + Dword l; + l = (val[0] << 24) + (val[1] << 16) + (val[2] << 8) + val[3]; + + return l; +} + +void dword2byte(Dword parm, Byte* rval) +{ + rval[0] = (parm >> 24) & 0xff; + rval[1] = (parm >> 16) & 0xff; + rval[2] = (parm >> 8) & 0xff; + rval[3] = parm & 0xff; +} + +Dword read_dword(int handle) +{ + Byte val[4]; + + read(handle, val, 4); + + return byte2dword(val); +} + +void write_dword(int handle, Dword parm) +{ + Byte val[4]; + + dword2byte(parm, val); + + write(handle, val, 4); +} + + +/* ######################################################################## */ + +int authenticate_to_floppyd(int sock, char *display) +{ + off_t filelen; + Byte buf[16]; + char *command[] = { "xauth", "xauth", "extract", "-", 0, 0 }; + char *xcookie; + Dword errcode; + + command[4] = display; + + filelen=strlen(display); + filelen += 100; + + xcookie = (char *) safe_malloc(filelen+4); + filelen = safePopenOut(command, xcookie+4, filelen); + if(filelen < 1) + return AUTH_AUTHFAILED; + + dword2byte(4,buf); + dword2byte(FLOPPYD_PROTOCOL_VERSION,buf+4); + write(sock, buf, 8); + + if (read_dword(sock) != 4) { + return AUTH_WRONGVERSION; + } + + errcode = read_dword(sock); + + if (errcode != AUTH_SUCCESS) { + return errcode; + } + + dword2byte(filelen, xcookie); + write(sock, xcookie, filelen+4); + + if (read_dword(sock) != 4) { + return AUTH_PACKETOVERSIZE; + } + + errcode = read_dword(sock); + + return errcode; +} + + +static int floppyd_reader(int fd, char* buffer, int len) +{ + Dword errcode; + Dword gotlen; + int l; + int start; + Byte buf[16]; + + dword2byte(1, buf); + buf[4] = OP_READ; + dword2byte(4, buf+5); + dword2byte(len, buf+9); + write(fd, buf, 13); + + if (read_dword(fd) != 8) { + errno = EIO; + return -1; + } + + gotlen = read_dword(fd); + errcode = read_dword(fd); + + if (gotlen != -1) { + if (read_dword(fd) != gotlen) { + errno = EIO; + return -1; + } + for (start = 0, l = 0; start < gotlen; start += l) { + l = read(fd, buffer+start, gotlen-start); + if (l == 0) { + errno = EIO; + return -1; + } + } + } else { + errno = errcode; + } + return gotlen; +} + +static int floppyd_writer(int fd, char* buffer, int len) +{ + Dword errcode; + Dword gotlen; + Byte buf[16]; + + dword2byte(1, buf); + buf[4] = OP_WRITE; + dword2byte(len, buf+5); + + write(fd, buf, 9); + write(fd, buffer, len); + + if (read_dword(fd) != 8) { + errno = EIO; + return -1; + } + + gotlen = read_dword(fd); + errcode = read_dword(fd); + + errno = errcode; + + return gotlen; +} + +static int floppyd_lseek(int fd, mt_off_t offset, int whence) +{ + Dword errcode; + Dword gotlen; + Byte buf[32]; + + dword2byte(1, buf); + buf[4] = OP_SEEK; + + dword2byte(8, buf+5); + dword2byte(truncBytes32(offset), buf+9); + dword2byte(whence, buf+13); + + write(fd, buf, 17); + + if (read_dword(fd) != 8) { + errno = EIO; + return -1; + } + + gotlen = read_dword(fd); + errcode = read_dword(fd); + + errno = errcode; + + return gotlen; +} + +/* ######################################################################## */ + +typedef int (*iofn) (int, char *, int); + +static int floppyd_io(Stream_t *Stream, char *buf, mt_off_t where, int len, + iofn io) +{ + DeclareThis(RemoteFile_t); + int ret; + + where += This->offset; + + if (where != This->lastwhere ){ + if(floppyd_lseek( This->fd, where, SEEK_SET) < 0 ){ + perror("floppyd_lseek"); + This->lastwhere = (mt_off_t) -1; + return -1; + } + } + ret = io(This->fd, buf, len); + if ( ret == -1 ){ + perror("floppyd_io"); + This->lastwhere = (mt_off_t) -1; + return -1; + } + This->lastwhere = where + ret; + return ret; +} + +static int floppyd_read(Stream_t *Stream, char *buf, mt_off_t where, size_t len) +{ + return floppyd_io(Stream, buf, where, len, (iofn) floppyd_reader); +} + +static int floppyd_write(Stream_t *Stream, char *buf, mt_off_t where, size_t len) +{ + return floppyd_io(Stream, buf, where, len, (iofn) floppyd_writer); +} + +static int floppyd_flush(Stream_t *Stream) +{ +#if 0 + Byte buf[16]; + + DeclareThis(RemoteFile_t); + + dword2byte(1, buf); + buf[4] = OP_FLUSH; + + write(This->fd, buf, 5); + + if (read_dword(This->fd) != 8) { + errno = EIO; + return -1; + } + + read_dword(This->fd); + read_dword(This->fd); +#endif + return 0; +} + +static int floppyd_free(Stream_t *Stream) +{ + Byte buf[16]; + + DeclareThis(RemoteFile_t); + + if (This->fd > 2) { + dword2byte(1, buf); + buf[4] = OP_CLOSE; + write(This->fd, buf, 5); + return close(This->fd); + } else { + return 0; + } +} + +static int floppyd_geom(Stream_t *Stream, struct device *dev, + struct device *orig_dev, + int media, struct bootsector *boot) +{ + size_t tot_sectors; + int sect_per_track; + DeclareThis(RemoteFile_t); + + dev->ssize = 2; /* allow for init_geom to change it */ + dev->use_2m = 0x80; /* disable 2m mode to begin */ + + if(media == 0xf0 || media >= 0x100){ + dev->heads = WORD(nheads); + dev->sectors = WORD(nsect); + tot_sectors = DWORD(bigsect); + SET_INT(tot_sectors, WORD(psect)); + sect_per_track = dev->heads * dev->sectors; + tot_sectors += sect_per_track - 1; /* round size up */ + dev->tracks = tot_sectors / sect_per_track; + + } else if (media >= 0xf8){ + media &= 3; + dev->heads = old_dos[media].heads; + dev->tracks = old_dos[media].tracks; + dev->sectors = old_dos[media].sectors; + dev->ssize = 0x80; + dev->use_2m = ~1; + } else { + fprintf(stderr,"Unknown media type\n"); + exit(1); + } + + This->size = (mt_off_t) 512 * dev->sectors * dev->tracks * dev->heads; + + return 0; +} + + +static int floppyd_data(Stream_t *Stream, time_t *date, mt_size_t *size, + int *type, int *address) +{ + DeclareThis(RemoteFile_t); + + if(date) + /* unknown, and irrelevant anyways */ + *date = 0; + if(size) + /* the size derived from the geometry */ + *size = (mt_size_t) This->size; + if(type) + *type = 0; /* not a directory */ + if(address) + *address = 0; + return 0; +} + +/* ######################################################################## */ + +static Class_t FloppydFileClass = { + floppyd_read, + floppyd_write, + floppyd_flush, + floppyd_free, + floppyd_geom, + floppyd_data +}; + +/* ######################################################################## */ + +int get_host_and_port(const char* name, char** hostname, char **display, + short* port) +{ + char* newname = strdup(name); + char* p; + char* p2; + + p = newname; + while (*p != '/' && *p) p++; + p2 = p; + if (*p) p++; + *p2 = 0; + + *port = atoi(p); + if (*port == 0) { + *port = FLOPPYD_DEFAULT_PORT; + } + + *display = strdup(newname); + + p = newname; + while (*p != ':' && *p) p++; + p2 = p; + if (*p) p++; + *p2 = 0; + + *port += atoi(p); /* add display number to the port */ + + if (!*newname || strcmp(newname, "unix") == 0) { + free(newname); + newname = strdup("localhost"); + } + + *hostname = newname; + return 1; +} + +/* + * * Return the IP address of the specified host. + * */ +static IPaddr_t getipaddress(char *ipaddr) +{ + + struct hostent *host; + IPaddr_t ip; + + if (((ip = inet_addr(ipaddr)) == INADDR_NONE) && + (strcmp(ipaddr, "255.255.255.255") != 0)) { + + if ((host = gethostbyname(ipaddr)) != NULL) { + memcpy(&ip, host->h_addr, sizeof(ip)); + } + + endhostent(); + } + +#ifdef DEBUG + fprintf(stderr, "IP lookup %s -> 0x%08lx\n", ipaddr, ip); +#endif + + return (ip); +} + +/* + * * Connect to the floppyd server. + * */ +static int connect_to_server(IPaddr_t ip, short port) +{ + + struct sockaddr_in addr; + int sock; + + /* + * Allocate a socket. + */ + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) { + return (-1); + } + + /* + * Set the address to connect to. + */ + + addr.sin_family = AF_INET; +#ifndef HAVE_HTONS + addr.sin_port = myhtons(port); +#else + addr.sin_port = htons(port); +#endif + addr.sin_addr.s_addr = ip; + + /* + * Connect our socket to the above address. + */ + if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) { + return (-1); + } + + /* + * Set the keepalive socket option to on. + */ + { + int on = 1; + setsockopt(sock, SOL_SOCKET, SO_KEEPALIVE, + (char *)&on, sizeof(on)); + } + + return (sock); +} + +static int ConnectToFloppyd(const char* name); + +Stream_t *FloppydOpen(struct device *dev, struct device *dev2, + char *name, int mode, char *errmsg, + int mode2, int locked) +{ + RemoteFile_t *This; + + if (!dev || !(dev->misc_flags & FLOPPYD_FLAG)) + return NULL; + + This = New(RemoteFile_t); + if (!This){ + printOom(); + return NULL; + } + This->Class = &FloppydFileClass; + This->Next = 0; + This->offset = 0; + This->lastwhere = 0; + This->refs = 1; + This->Buffer = 0; + + This->fd = ConnectToFloppyd(name); + if (This->fd == -1) { + Free(This); + return NULL; + } + return (Stream_t *) This; +} + +static int ConnectToFloppyd(const char* name) +{ + char* hostname; + char* display; + short port; + int rval = get_host_and_port(name, &hostname, &display, &port); + int sock; + int reply; + + if (!rval) return -1; + + sock = connect_to_server(getipaddress(hostname), port); + + if (sock == -1) { + fprintf(stderr, + "Can't connect to floppyd server on %s, port %i!\n", + hostname, port); + return -1; + } + + reply = authenticate_to_floppyd(sock, display); + + if (reply != 0) { + fprintf(stderr, + "Permission denied, authentication failed!\n" + "%s\n", AuthErrors[reply]); + return -1; + } + + free(hostname); + free(display); + + return sock; +} +#endif diff --git a/commands/i386/mtools-3.9.7/floppyd_io.h b/commands/i386/mtools-3.9.7/floppyd_io.h new file mode 100755 index 000000000..99e61296d --- /dev/null +++ b/commands/i386/mtools-3.9.7/floppyd_io.h @@ -0,0 +1,37 @@ +#ifndef MTOOLS_FLOPPYDIO_H +#define MTOOLS_FLOPPYDIO_H + +#ifdef USE_FLOPPYD + +#include "stream.h" + +/*extern int ConnectToFloppyd(const char* name, Class_t** ioclass);*/ +Stream_t *FloppydOpen(struct device *dev, struct device *dev2, + char *name, int mode, char *errmsg, + int mode2, int locked); + +#define FLOPPYD_DEFAULT_PORT 5703 +#define FLOPPYD_PROTOCOL_VERSION 10 + +enum FloppydOpcodes { + OP_READ, + OP_WRITE, + OP_SEEK, + OP_FLUSH, + OP_CLOSE, + OP_IOCTL +}; + +enum AuthErrorsEnum { + AUTH_SUCCESS, + AUTH_PACKETOVERSIZE, + AUTH_AUTHFAILED, + AUTH_WRONGVERSION, + AUTH_DEVLOCKED, + AUTH_BADPACKET +}; + +typedef unsigned long IPaddr_t; + +#endif +#endif diff --git a/commands/i386/mtools-3.9.7/force_io.c b/commands/i386/mtools-3.9.7/force_io.c new file mode 100755 index 000000000..ba90d1241 --- /dev/null +++ b/commands/i386/mtools-3.9.7/force_io.c @@ -0,0 +1,48 @@ +/* + * Force I/O to be done to complete transfer length + * + * written by: + * + * Alain L. Knaff + * alain@linux.lu + * + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "stream.h" + +static int force_io(Stream_t *Stream, + char *buf, mt_off_t start, size_t len, + int (*io)(Stream_t *, char *, mt_off_t, size_t)) +{ + int ret; + int done=0; + + while(len){ + ret = io(Stream, buf, start, len); + if ( ret <= 0 ){ + if (done) + return done; + else + return ret; + } + start += ret; + done += ret; + len -= ret; + buf += ret; + } + return done; +} + +int force_write(Stream_t *Stream, char *buf, mt_off_t start, size_t len) +{ + return force_io(Stream, buf, start, len, + Stream->Class->write); +} + +int force_read(Stream_t *Stream, char *buf, mt_off_t start, size_t len) +{ + return force_io(Stream, buf, start, len, + Stream->Class->read); +} diff --git a/commands/i386/mtools-3.9.7/fs.h b/commands/i386/mtools-3.9.7/fs.h new file mode 100755 index 000000000..8e21cedb6 --- /dev/null +++ b/commands/i386/mtools-3.9.7/fs.h @@ -0,0 +1,26 @@ +#ifndef MTOOLS_FS_H +#define MTOOLS_FS_H + +#include "stream.h" + + +typedef struct FsPublic_t { + Class_t *Class; + int refs; + Stream_t *Next; + Stream_t *Buffer; + + int serialized; + unsigned long serial_number; + int cluster_size; + unsigned int sector_size; +} FsPublic_t; + +Stream_t *fs_init(char *drive, int mode); +int fat_free(Stream_t *Dir, unsigned int fat); +int fatFreeWithDir(Stream_t *Dir, struct directory *dir); +int fat_error(Stream_t *Dir); +int fat32RootCluster(Stream_t *Dir); +char *getDrive(Stream_t *Stream); + +#endif diff --git a/commands/i386/mtools-3.9.7/fsP.h b/commands/i386/mtools-3.9.7/fsP.h new file mode 100755 index 000000000..700886d2d --- /dev/null +++ b/commands/i386/mtools-3.9.7/fsP.h @@ -0,0 +1,84 @@ +#ifndef MTOOLS_FSP_H +#define MTOOLS_FSP_H + +#include "stream.h" +#include "msdos.h" +#include "fs.h" + +typedef enum fatAccessMode_t { + FAT_ACCESS_READ, + FAT_ACCESS_WRITE +} fatAccessMode_t; + +typedef struct Fs_t { + Class_t *Class; + int refs; + Stream_t *Next; + Stream_t *Buffer; + + int serialized; + unsigned long serial_number; + int cluster_size; + unsigned int sector_size; + int fat_error; + + unsigned int (*fat_decode)(struct Fs_t *This, unsigned int num); + void (*fat_encode)(struct Fs_t *This, unsigned int num, + unsigned int code); + + Stream_t *Direct; + int fat_dirty; + unsigned int fat_start; + unsigned int fat_len; + + int num_fat; + unsigned int end_fat; + unsigned int last_fat; + int fat_bits; + struct FatMap_t *FatMap; + + int dir_start; + int dir_len; + int clus_start; + + int num_clus; + char *drive; /* for error messages */ + + /* fat 32 */ + unsigned int primaryFat; + unsigned int writeAllFats; + unsigned int rootCluster; + int infoSectorLoc; + unsigned int last; /* last sector allocated, or MAX32 if unknown */ + unsigned int freeSpace; /* free space, or MAX32 if unknown */ + int preallocatedClusters; + + int lastFatSectorNr; + unsigned char *lastFatSectorData; + fatAccessMode_t lastFatAccessMode; + int sectorMask; + int sectorShift; +} Fs_t; + +int fs_free(Stream_t *Stream); + +void set_fat12(Fs_t *Fs); +void set_fat16(Fs_t *Fs); +void set_fat32(Fs_t *Fs); +unsigned int get_next_free_cluster(Fs_t *Fs, unsigned int last); +unsigned int fatDecode(Fs_t *This, unsigned int pos); +void fatAppend(Fs_t *This, unsigned int pos, unsigned int newpos); +void fatDeallocate(Fs_t *This, unsigned int pos); +void fatAllocate(Fs_t *This, unsigned int pos, unsigned int value); +void fatEncode(Fs_t *This, unsigned int pos, unsigned int value); + +int fat_read(Fs_t *This, struct bootsector *boot, int fat_bits, + size_t tot_sectors, int nodups); +void fat_write(Fs_t *This); +int zero_fat(Fs_t *Fs, int media_descriptor); +extern Class_t FsClass; +int fsPreallocateClusters(Fs_t *Fs, long); +Fs_t *getFs(Stream_t *Stream); + + +#endif diff --git a/commands/i386/mtools-3.9.7/hash.c b/commands/i386/mtools-3.9.7/hash.c new file mode 100755 index 000000000..828a03012 --- /dev/null +++ b/commands/i386/mtools-3.9.7/hash.c @@ -0,0 +1,205 @@ +/* + * hash.c - hash table. + */ + +#include "sysincludes.h" +#include "htable.h" +#include "mtools.h" + +struct hashtable { + T_HashFunc f1,f2; + T_ComparFunc compar; + int size; /* actual size of the array */ + int fill; /* number of deleted or in use slots */ + int inuse; /* number of slots in use */ + int max; /* maximal number of elements to keep efficient */ + T_HashTableEl *entries; +}; + +static int sizes[]={5, 11, 23, 47, 97, 197, 397, 797, 1597, 3203, 6421, 12853, + 25717, 51437, 102877, 205759, 411527, 823117, 1646237, + 3292489, 6584983, 13169977, 26339969, 52679969, 105359939, + 210719881, 421439783, 842879579, 1685759167, 0 }; +static int deleted=0; +static int unallocated=0; + +static int alloc_ht(T_HashTable *H, int size) +{ + int i; + + for(i=0; sizes[i]; i++) + if (sizes[i] > size*4 ) + break; + if (!sizes[i]) + for(i=0; sizes[i]; i++) + if (sizes[i] > size*2 ) + break; + if (!sizes[i]) + for(i=0; sizes[i]; i++) + if (sizes[i] > size) + break; + if(!sizes[i]) + return -1; + size = sizes[i]; + if(size < H->size) + size = H->size; /* never shrink the table */ + H->max = size * 4 / 5 - 2; + H->size = size; + H->fill = 0; + H->inuse = 0; + H->entries = NewArray(size, T_HashTableEl); + if (H->entries == NULL) + return -1; /* out of memory error */ + + for(i=0; i < size; i++) + H->entries[i] = &unallocated; + return 0; +} + +int make_ht(T_HashFunc f1, T_HashFunc f2, T_ComparFunc c, int size, + T_HashTable **H) +{ + *H = New(T_HashTable); + if (*H == NULL){ + return -1; /* out of memory error */ + } + + (*H)->f1 = f1; + (*H)->f2 = f2; + (*H)->compar = c; + (*H)->size = 0; + if(alloc_ht(*H,size)) + return -1; + return 0; +} + +int free_ht(T_HashTable *H, T_HashFunc entry_free) +{ + int i; + if(entry_free) + for(i=0; i< H->size; i++) + if (H->entries[i] != &unallocated && + H->entries[i] != &deleted) + entry_free(H->entries[i]); + Free(H->entries); + Free(H); + return 0; +} + +/* add into hash table without checking for repeats */ +static int _hash_add(T_HashTable *H,T_HashTableEl *E, int *hint) +{ + int f2, pos, ctr; + + pos = H->f1(E) % H->size; + f2 = -1; + ctr = 0; + while(H->entries[pos] != &unallocated && + H->entries[pos] != &deleted){ + if (f2 == -1) + f2 = H->f2(E) % (H->size - 1); + pos = (pos+f2+1) % H->size; + ctr++; + } + if(H->entries[pos] == &unallocated) + H->fill++; /* only increase fill if the previous element was not yet + * counted, i.e. unallocated */ + H->inuse++; + H->entries[pos] = E; + if(hint) + *hint = pos; + return 0; +} + +static int rehash(T_HashTable *H) +{ + int size,i; + T_HashTableEl *oldentries; + /* resize the table */ + + size = H->size; + oldentries = H->entries; + if(alloc_ht(H,((H->inuse+1)*4+H->fill)/5)) + return -1; + + for(i=0; i < size; i++){ + if(oldentries[i] != &unallocated && oldentries[i] != &deleted) + _hash_add(H, oldentries[i], 0); + } + Free(oldentries); + return 0; +} + +int hash_add(T_HashTable *H, T_HashTableEl *E, int *hint) +{ + if (H->fill >= H->max) + rehash(H); + if (H->fill == H->size) + return -1; /*out of memory error */ + return _hash_add(H,E, hint); +} + + +/* add into hash table without checking for repeats */ +static int _hash_lookup(T_HashTable *H,T_HashTableEl *E, T_HashTableEl **E2, + int *hint, int isIdentity) +{ + int f2, pos, upos, ttl; + + pos = H->f1(E) % H->size; + ttl = H->size; + f2 = -1; + upos = -1; + while(ttl && + H->entries[pos] != &unallocated && + (H->entries[pos] == &deleted || + ((isIdentity || H->compar(H->entries[pos], E) != 0) && + (!isIdentity || H->entries[pos] != E)))){ + if (f2 == -1) + f2 = H->f2(E) % (H->size - 1); + if (upos == -1 && H->entries[pos] == &deleted) + upos = pos; + pos = (pos+f2+1) % H->size; + ttl--; + } + if(H->entries[pos] == &unallocated || !ttl) + return -1; + if (upos != -1){ + H->entries[upos] = H->entries[pos]; + H->entries[pos] = &deleted; + pos = upos; + } + if(hint) + *hint = pos; + *E2= H->entries[pos]; + return 0; +} + + +int hash_lookup(T_HashTable *H,T_HashTableEl *E, T_HashTableEl **E2, + int *hint) +{ + return _hash_lookup(H, E, E2, hint, 0); +} + +/* add into hash table without checking for repeats */ +int hash_remove(T_HashTable *H,T_HashTableEl *E, int hint) +{ + T_HashTableEl *E2; + + if (hint >=0 && hint < H->size && + H->entries[hint] == E){ + H->inuse--; + H->entries[hint] = &deleted; + return 0; + } + + if(_hash_lookup(H, E, &E2, &hint, 1)) { + fprintf(stderr, "Removing non-existent entry\n"); + exit(1); + return -1; + } + H->inuse--; + H->entries[hint] = &deleted; + return 0; +} diff --git a/commands/i386/mtools-3.9.7/htable.h b/commands/i386/mtools-3.9.7/htable.h new file mode 100755 index 000000000..fe61e481d --- /dev/null +++ b/commands/i386/mtools-3.9.7/htable.h @@ -0,0 +1,17 @@ +/* + * hashtable + */ + +typedef struct hashtable T_HashTable; +typedef void *T_HashTableEl; +typedef unsigned int (*T_HashFunc)(void *); +typedef int (*T_ComparFunc)(void *, void *); + + +int make_ht(T_HashFunc f1, T_HashFunc f2, T_ComparFunc c, int size, T_HashTable **H); +int hash_add(T_HashTable *H, T_HashTableEl *E, int *hint); +int hash_remove(T_HashTable *H, T_HashTableEl *E, int hint); +int hash_lookup(T_HashTable *H, T_HashTableEl *E, T_HashTableEl **E2, + int *hint); +int free_ht(T_HashTable *H, T_HashFunc entry_free); + diff --git a/commands/i386/mtools-3.9.7/init.c b/commands/i386/mtools-3.9.7/init.c new file mode 100755 index 000000000..5beeaaf22 --- /dev/null +++ b/commands/i386/mtools-3.9.7/init.c @@ -0,0 +1,414 @@ +/* + * Initialize an MSDOS diskette. Read the boot sector, and switch to the + * proper floppy disk device to match the format on the disk. Sets a bunch + * of global variables. Returns 0 on success, or 1 on failure. + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "stream.h" +#include "mtools.h" +#include "fsP.h" +#include "plain_io.h" +#include "floppyd_io.h" +#include "xdf_io.h" +#include "buffer.h" + +extern int errno; + + +#ifndef OS_Minix /* Minix is memory starved. */ +#define FULL_CYL +#endif + +unsigned int num_clus; /* total number of cluster */ + + +/* + * Read the boot sector. We glean the disk parameters from this sector. + */ +static int read_boot(Stream_t *Stream, struct bootsector * boot, int size) +{ + /* read the first sector, or part of it */ + if(!size) + size = BOOTSIZE; + if(size > 1024) + size = 1024; + + if (force_read(Stream, (char *) boot, 0, size) != size) + return -1; + return 0; +} + +static int fs_flush(Stream_t *Stream) +{ + DeclareThis(Fs_t); + + fat_write(This); + return 0; +} + +Class_t FsClass = { + read_pass_through, /* read */ + write_pass_through, /* write */ + fs_flush, + fs_free, /* free */ + 0, /* set geometry */ + get_data_pass_through, + 0 /* pre allocate */ +}; + +static int get_media_type(Stream_t *St, struct bootsector *boot) +{ + int media; + + media = boot->descr; + if(media < 0xf0){ + char temp[512]; + /* old DOS disk. Media descriptor in the first FAT byte */ + /* old DOS disk always have 512-byte sectors */ + if (force_read(St,temp,(mt_off_t) 512,512) == 512) + media = (unsigned char) temp[0]; + else + media = 0; + } else + media += 0x100; + return media; +} + + +Stream_t *GetFs(Stream_t *Fs) +{ + while(Fs && Fs->Class != &FsClass) + Fs = Fs->Next; + return Fs; +} + +Stream_t *find_device(char *drive, int mode, struct device *out_dev, + struct bootsector *boot, + char *name, int *media, mt_size_t *maxSize) +{ + char errmsg[200]; + Stream_t *Stream; + struct device *dev; + int r; +#ifdef OS_Minix + static char *devname; + struct device onedevice[2]; + struct stat stbuf; + + free(devname); + devname = safe_malloc((9 + strlen(drive)) * sizeof(devname[0])); + strcpy(devname, "/dev/dosX"); + if (isupper(drive[0]) && drive[1] == 0) { + /* single letter device name, use /dev/dos$drive */ + devname[8]= drive[0]; + } else + if (strchr(drive, '/') == NULL) { + /* a simple name, use /dev/$drive */ + strcpy(devname+5, drive); + } else { + /* a pathname, use as is. */ + strcpy(devname, drive); + } + if (stat(devname, &stbuf) != -1) { + memset(onedevice, 0, sizeof(onedevice)); + onedevice[0].name = devname; + onedevice[0].drive = drive; + onedevice[1].name = NULL; + onedevice[1].drive = NULL; + dev = onedevice; + } else { + dev = devices; + } +#else + dev = devices; +#endif + + Stream = NULL; + sprintf(errmsg, "Drive '%s:' not supported", drive); + /* open the device */ + for (; dev->name; dev++) { + FREE(&Stream); + if (strcmp(dev->drive, drive) != 0) + continue; + *out_dev = *dev; + expand(dev->name,name); +#ifdef USING_NEW_VOLD + strcpy(name, getVoldName(dev, name)); +#endif + + Stream = 0; +#ifdef USE_XDF + Stream = XdfOpen(out_dev, name, mode, errmsg, 0); + if(Stream) { + out_dev->use_2m = 0x7f; + if(maxSize) + *maxSize = max_off_t_31; + } +#endif + +#ifdef USE_FLOPPYD + if(!Stream) { + Stream = FloppydOpen(out_dev, dev, name, mode, errmsg, 0, 1); + if(Stream && maxSize) + *maxSize = max_off_t_31; + } +#endif + + if (!Stream) + Stream = SimpleFileOpen(out_dev, dev, name, mode, + errmsg, 0, 1, maxSize); + + if( !Stream) + continue; + + /* read the boot sector */ + if ((r=read_boot(Stream, boot, out_dev->blocksize)) < 0){ + sprintf(errmsg, + "init %s: could not read boot sector", + drive); + continue; + } + + if((*media= get_media_type(Stream, boot)) <= 0xf0 ){ + if (boot->jump[2]=='L') + sprintf(errmsg, + "diskette %s: is Linux LILO, not DOS", + drive); + else + sprintf(errmsg,"init %s: non DOS media", drive); + continue; + } + + /* set new parameters, if needed */ + errno = 0; + if(SET_GEOM(Stream, out_dev, dev, *media, boot)){ + if(errno) +#ifdef HAVE_SNPRINTF + snprintf(errmsg, 199, + "Can't set disk parameters for %s: %s", + drive, strerror(errno)); +#else + sprintf(errmsg, + "Can't set disk parameters for %s: %s", + drive, strerror(errno)); +#endif + else + sprintf(errmsg, + "Can't set disk parameters for %s", + drive); + continue; + } + break; + } + + /* print error msg if needed */ + if ( dev->drive == 0 ){ + FREE(&Stream); + fprintf(stderr,"%s\n",errmsg); + return NULL; + } +#ifdef OS_Minix + /* Minix can lseek up to 4G. */ + if (maxSize) *maxSize = 0xFFFFFFFFUL; +#endif + return Stream; +} + + +Stream_t *fs_init(char *drive, int mode) +{ + int blocksize; + int media,i; + int nhs; + int disk_size = 0; /* In case we don't happen to set this below */ + size_t tot_sectors; + char name[EXPAND_BUF]; + int cylinder_size; + struct device dev; + mt_size_t maxSize; + + struct bootsector boot0; +#define boot (&boot0) + Fs_t *This; + + This = New(Fs_t); + if (!This) + return NULL; + + This->Direct = NULL; + This->Next = NULL; + This->refs = 1; + This->Buffer = 0; + This->Class = &FsClass; + This->preallocatedClusters = 0; + This->lastFatSectorNr = 0; + This->lastFatAccessMode = 0; + This->lastFatSectorData = 0; + This->drive = drive; + This->last = 0; + + This->Direct = find_device(drive, mode, &dev, &boot0, name, &media, + &maxSize); + if(!This->Direct) + return NULL; + + This->sector_size = WORD(secsiz); + if(This->sector_size > MAX_SECTOR){ + fprintf(stderr,"init %s: sector size too big\n", drive); + return NULL; + } + + i = log_2(This->sector_size); + + if(i == 24) { + fprintf(stderr, + "init %c: sector size (%d) not a small power of two\n", + drive, This->sector_size); + return NULL; + } + This->sectorShift = i; + This->sectorMask = This->sector_size - 1; + + + cylinder_size = dev.heads * dev.sectors; + if (!tot_sectors) tot_sectors = dev.tracks * cylinder_size; + + This->serialized = 0; + if ((media & ~7) == 0xf8){ + i = media & 3; + This->cluster_size = old_dos[i].cluster_size; + tot_sectors = cylinder_size * old_dos[i].tracks; + This->fat_start = 1; + This->fat_len = old_dos[i].fat_len; + This->dir_len = old_dos[i].dir_len; + This->num_fat = 2; + This->sector_size = 512; + This->sectorShift = 9; + This->sectorMask = 511; + This->fat_bits = 12; + nhs = 0; + } else { + struct label_blk_t *labelBlock; + /* + * all numbers are in sectors, except num_clus + * (which is in clusters) + */ + tot_sectors = WORD(psect); + if(!tot_sectors) { + tot_sectors = DWORD(bigsect); + nhs = DWORD(nhs); + } else + nhs = WORD(nhs); + + + This->cluster_size = boot0.clsiz; + This->fat_start = WORD(nrsvsect); + This->fat_len = WORD(fatlen); + This->dir_len = WORD(dirents) * MDIR_SIZE / This->sector_size; + This->num_fat = boot0.nfat; + + if (This->fat_len) { + labelBlock = &boot0.ext.old.labelBlock; + } else { + labelBlock = &boot0.ext.fat32.labelBlock; + } + + if(labelBlock->dos4 == 0x29) { + This->serialized = 1; + This->serial_number = _DWORD(labelBlock->serial); + } + } + + if (tot_sectors >= (maxSize >> This->sectorShift)) { + fprintf(stderr, "Big disks not supported on this architecture\n"); + exit(1); + } + +#ifndef OS_Minix /* Strange check, MS-DOS isn't that picky. */ + + if(!mtools_skip_check && (tot_sectors % dev.sectors)){ + fprintf(stderr, + "Total number of sectors not a multiple of" + " sectors per track!\n"); + fprintf(stderr, + "Add mtools_skip_check=1 to your .mtoolsrc file " + "to skip this test\n"); + exit(1); + } +#endif + + /* full cylinder buffering */ +#ifdef FULL_CYL + disk_size = (dev.tracks) ? cylinder_size : 512; +#else /* FULL_CYL */ + disk_size = (dev.tracks) ? dev.sectors : 512; +#endif /* FULL_CYL */ + +#if (defined OS_sysv4 && !defined OS_solaris) + /* + * The driver in Dell's SVR4 v2.01 is unreliable with large writes. + */ + disk_size = 0; +#endif /* (defined sysv4 && !defined(solaris)) */ + +#ifdef OS_linux + disk_size = cylinder_size; +#endif + +#if 1 + if(disk_size > 256) { + disk_size = dev.sectors; + if(dev.sectors % 2) + disk_size <<= 1; + } +#endif + if (disk_size % 2) + disk_size *= 2; + + if(!dev.blocksize || dev.blocksize < This->sector_size) + blocksize = This->sector_size; + else + blocksize = dev.blocksize; + if (disk_size) + This->Next = buf_init(This->Direct, + 8 * disk_size * blocksize, + disk_size * blocksize, + This->sector_size); + else + This->Next = This->Direct; + + if (This->Next == NULL) { + perror("init: allocate buffer"); + This->Next = This->Direct; + } + + /* read the FAT sectors */ + if(fat_read(This, &boot0, dev.fat_bits, tot_sectors, dev.use_2m&0x7f)){ + This->num_fat = 1; + FREE(&This->Next); + Free(This->Next); + return NULL; + } + return (Stream_t *) This; +} + +char *getDrive(Stream_t *Stream) +{ + DeclareThis(Fs_t); + + if(This->Class != &FsClass) + return getDrive(GetFs(Stream)); + else + return This->drive; +} + +int fsPreallocateClusters(Fs_t *Fs, long size) +{ + if(size > 0 && getfreeMinClusters((Stream_t *)Fs, size) != 1) + return -1; + + Fs->preallocatedClusters += size; + return 0; +} diff --git a/commands/i386/mtools-3.9.7/llong.c b/commands/i386/mtools-3.9.7/llong.c new file mode 100755 index 000000000..5d00abc99 --- /dev/null +++ b/commands/i386/mtools-3.9.7/llong.c @@ -0,0 +1,84 @@ +#include "sysincludes.h" +#include "stream.h" +#include "fsP.h" +#include "llong.h" +#include "mtools.h" + +/* Warnings about integer overflow in expression can be ignored. These are + * due to the way that maximal values for those integers are computed: + * intentional overflow from smallest negative number (1000...) to highest + * positive number (0111...) by substraction of 1 */ +#ifdef __GNUC__ +/* +#warning "The following warnings about integer overflow in expression can be safely ignored" +*/ +#endif + +#if 1 +const mt_off_t max_off_t_31 = MAX_OFF_T_B(31); /* Floppyd */ +const mt_off_t max_off_t_41 = MAX_OFF_T_B(41); /* SCSI */ +const mt_off_t max_off_t_seek = MAX_OFF_T_B(SEEK_BITS); /* SCSI */ +#else +const mt_off_t max_off_t_31 = MAX_OFF_T_B(10); /* Floppyd */ +const mt_off_t max_off_t_41 = MAX_OFF_T_B(10); /* SCSI */ +const mt_off_t max_off_t_seek = MAX_OFF_T_B(10); /* SCSI */ +#endif + +off_t truncBytes32(mt_off_t off) +{ + if (off & ~max_off_t_31) { + fprintf(stderr, "Internal error, offset too big\n"); + exit(1); + } + return (off_t) off; +} + +mt_off_t sectorsToBytes(Stream_t *Stream, off_t off) +{ + DeclareThis(Fs_t); + return (mt_off_t) off << This->sectorShift; +} + +#if defined HAVE_LLSEEK +# ifndef HAVE_LLSEEK_PROTOTYPE +extern long long llseek (int fd, long long offset, int origin); +# endif +#endif + +#if defined HAVE_LSEEK64 +# ifndef HAVE_LSEEK64_PROTOTYPE +extern long long lseek64 (int fd, long long offset, int origin); +# endif +#endif + + +int mt_lseek(int fd, mt_off_t where, int whence) +{ +#if defined HAVE_LSEEK64 + if(lseek64(fd, where, whence) >= 0) + return 0; + else + return -1; +#elif defined HAVE_LLSEEK + if(llseek(fd, where, whence) >= 0) + return 0; + else + return -1; +#else + if (lseek(fd, (off_t) where, whence) >= 0) + return 0; + else + return 1; +#endif +} + +int log_2(int size) +{ + int i; + + for(i=0; i<24; i++) { + if(1 << i == size) + return i; + } + return 24; +} diff --git a/commands/i386/mtools-3.9.7/llong.h b/commands/i386/mtools-3.9.7/llong.h new file mode 100755 index 000000000..32580987c --- /dev/null +++ b/commands/i386/mtools-3.9.7/llong.h @@ -0,0 +1,81 @@ +#ifndef MTOOLS_LLONG_H +#define MTOOLS_LLONG_H + +#if 1 + + +#ifdef HAVE_OFF_T_64 +/* if off_t is already 64 bits, be happy, and don't worry about the + * loff_t and llseek stuff */ +#define MT_OFF_T off_t +#endif + +#ifndef MT_OFF_T +# ifdef HAVE_LLSEEK +/* we have llseek. Now, what's its type called? loff_t or offset_t ? */ +# ifdef HAVE_LOFF_T +# define MT_OFF_T loff_t +# else +# ifdef HAVE_OFFSET_T +# define MT_OFF_T offset_t +# endif +# endif +# endif +#endif + +#ifndef MT_OFF_T +/* we still don't have a suitable mt_off_t type...*/ +# ifdef HAVE_LONG_LONG +/* ... first try long long ... */ +# define MT_OFF_T long long +# else +/* ... and if that fails, fall back on good ole' off_t */ +# define MT_OFF_T off_t +# endif +#endif + +typedef MT_OFF_T mt_off_t; + +#else +/* testing: meant to flag dubious assignments between 32 bit length types + * and 64 bit ones */ +typedef struct { + int lo; + int high; +} *mt_off_t; + + +#endif + +typedef mt_off_t mt_size_t; + +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define MAX_OFF_T_B(bits) \ + (((mt_off_t) 1 << min(bits, sizeof(mt_off_t)*8 - 1)) - 1) + +#ifdef HAVE_LLSEEK +# define SEEK_BITS 63 +#else +# define SEEK_BITS (sizeof(off_t) * 8 - 1) +#endif + +extern const mt_off_t max_off_t_31; +extern const mt_off_t max_off_t_41; +extern const mt_off_t max_off_t_seek; + +extern off_t truncBytes32(mt_off_t off); +mt_off_t sectorsToBytes(Stream_t *This, off_t off); + +mt_size_t getfree(Stream_t *Stream); +int getfreeMinBytes(Stream_t *Stream, mt_size_t ref); + +Stream_t *find_device(char *drive, int mode, struct device *out_dev, + struct bootsector *boot, + char *name, int *media, mt_size_t *maxSize); + +int mt_lseek(int fd, mt_off_t where, int whence); + + +int log_2(int); + +#endif diff --git a/commands/i386/mtools-3.9.7/mainloop.c b/commands/i386/mtools-3.9.7/mainloop.c new file mode 100755 index 000000000..6f1d6c2cc --- /dev/null +++ b/commands/i386/mtools-3.9.7/mainloop.c @@ -0,0 +1,557 @@ +/* + * mainloop.c + * Iterating over all the command line parameters, and matching patterns + * where needed + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "vfat.h" +#include "fs.h" +#include "mainloop.h" +#include "plain_io.h" +#include "file.h" + + +int unix_dir_loop(Stream_t *Stream, MainParam_t *mp); +int unix_loop(Stream_t *Stream, MainParam_t *mp, char *arg, + int follow_dir_link); + +static int _unix_loop(Stream_t *Dir, MainParam_t *mp, const char *filename) +{ + unix_dir_loop(Dir, mp); + return GOT_ONE; +} + +int unix_loop(Stream_t *Stream, MainParam_t *mp, char *arg, int follow_dir_link) +{ + int ret; + int isdir; + + mp->File = NULL; + mp->direntry = NULL; + mp->unixSourceName = arg; + /* mp->dir.attr = ATTR_ARCHIVE;*/ + mp->loop = _unix_loop; + if((mp->lookupflags & DO_OPEN)){ + mp->File = SimpleFileOpen(0, 0, arg, O_RDONLY, 0, 0, 0, 0); + if(!mp->File){ + perror(arg); +#if 0 + tmp = _basename(arg); + strncpy(mp->filename, tmp, VBUFSIZE); + mp->filename[VBUFSIZE-1] = '\0'; +#endif + return ERROR_ONE; + } + GET_DATA(mp->File, 0, 0, &isdir, 0); + if(isdir) { + struct stat buf; + + FREE(&mp->File); +#ifdef S_ISLNK + if(!follow_dir_link && + lstat(arg, &buf) == 0 && + S_ISLNK(buf.st_mode)) { + /* skip links to directories in order to avoid + * infinite loops */ + fprintf(stderr, + "skipping directory symlink %s\n", + arg); + return 0; + } +#endif + if(! (mp->lookupflags & ACCEPT_DIR)) + return 0; + mp->File = OpenDir(Stream, arg); + } + } + + if(isdir) + ret = mp->dirCallback(0, mp); + else + ret = mp->unixcallback(mp); + FREE(&mp->File); + return ret; +} + + +int isSpecial(const char *name) +{ + if(name[0] == '\0') + return 1; + if(!strcmp(name,".")) + return 1; + if(!strcmp(name,"..")) + return 1; + return 0; +} + + +static int checkForDot(int lookupflags, const char *name) +{ + return (lookupflags & NO_DOTS) && isSpecial(name); +} + + +typedef struct lookupState_t { + Stream_t *container; + int nbContainers; + Stream_t *Dir; + int nbDirs; + const char *filename; +} lookupState_t; + +static int isUniqueTarget(const char *name) +{ + return name && strcmp(name, "-"); +} + +static int handle_leaf(direntry_t *direntry, MainParam_t *mp, + lookupState_t *lookupState) +{ + Stream_t *MyFile=0; + int ret; + + if(got_signal) + return ERROR_ONE; + if(lookupState) { + /* we are looking for a "target" file */ + switch(lookupState->nbDirs) { + case 0: /* no directory yet, open it */ + lookupState->Dir = OpenFileByDirentry(direntry); + lookupState->nbDirs++; + /* dump the container, we have + * better now */ + FREE(&lookupState->container); + return 0; + case 1: /* we have already a directory */ + FREE(&lookupState->Dir); + fprintf(stderr,"Ambigous\n"); + return STOP_NOW | ERROR_ONE; + default: + return STOP_NOW | ERROR_ONE; + } + } + + mp->direntry = direntry; + if(IS_DIR(direntry)) { + if(mp->lookupflags & (DO_OPEN | DO_OPEN_DIRS)) + MyFile = mp->File = OpenFileByDirentry(direntry); + ret = mp->dirCallback(direntry, mp); + } else { + if(mp->lookupflags & DO_OPEN) + MyFile = mp->File = OpenFileByDirentry(direntry); + ret = mp->callback(direntry, mp); + } + FREE(&MyFile); + if(isUniqueTarget(mp->targetName)) + ret |= STOP_NOW; + return ret; +} + +static int _dos_loop(Stream_t *Dir, MainParam_t *mp, const char *filename) +{ + Stream_t *MyFile=0; + direntry_t entry; + int ret; + int r; + + ret = 0; + r=0; + initializeDirentry(&entry, Dir); + while(!got_signal && + (r=vfat_lookup(&entry, filename, -1, + mp->lookupflags, mp->shortname, + mp->longname)) == 0 ){ + mp->File = NULL; + if(!checkForDot(mp->lookupflags,entry.name)) { + MyFile = 0; + if((mp->lookupflags & DO_OPEN) || + (IS_DIR(&entry) && + (mp->lookupflags & DO_OPEN_DIRS))) { + MyFile = mp->File = OpenFileByDirentry(&entry); + } + if(got_signal) + break; + mp->direntry = &entry; + if(IS_DIR(&entry)) + ret |= mp->dirCallback(&entry,mp); + else + ret |= mp->callback(&entry, mp); + FREE(&MyFile); + } + if (fat_error(Dir)) + ret |= ERROR_ONE; + if(mp->fast_quit && (ret & ERROR_ONE)) + break; + } + if (r == -2) + return ERROR_ONE; + if(got_signal) + ret |= ERROR_ONE; + return ret; +} + +static int recurs_dos_loop(MainParam_t *mp, const char *filename0, + const char *filename1, + lookupState_t *lookupState) +{ + /* Dir is de-allocated by the same entity which allocated it */ + const char *ptr; + direntry_t entry; + int length; + int lookupflags; + int ret; + int have_one; + int doing_mcwd; + int r; + + while(1) { + /* strip dots and // */ + if(!strncmp(filename0,"./", 2)) { + filename0 += 2; + continue; + } + if(!strcmp(filename0,".") && filename1) { + filename0 ++; + continue; + } + if(filename0[0] == '/') { + filename0++; + continue; + } + if(!filename0[0]) { + if(!filename1) + break; + filename0 = filename1; + filename1 = 0; + continue; + } + break; + } + + if(!strncmp(filename0,"../", 3) || + (!strcmp(filename0, "..") && filename1)) { + /* up one level */ + mp->File = getDirentry(mp->File)->Dir; + return recurs_dos_loop(mp, filename0+2, filename1, lookupState); + } + + doing_mcwd = !!filename1; + + ptr = strchr(filename0, '/'); + if(!ptr) { + length = strlen(filename0); + ptr = filename1; + filename1 = 0; + } else { + length = ptr - filename0; + ptr++; + } + if(!ptr) { + if(mp->lookupflags & OPEN_PARENT) { + mp->targetName = filename0; + ret = handle_leaf(getDirentry(mp->File), mp, + lookupState); + mp->targetName = 0; + return ret; + } + + if(!strcmp(filename0, ".") || !filename0[0]) { + return handle_leaf(getDirentry(mp->File), + mp, lookupState); + } + + if(!strcmp(filename0, "..")) { + return handle_leaf(getParent(getDirentry(mp->File)), mp, + lookupState); + } + + lookupflags = mp->lookupflags; + + if(lookupState) { + lookupState->filename = filename0; + if(lookupState->nbContainers + lookupState->nbDirs > 0){ + /* we have already one target, don't bother + * with this one. */ + FREE(&lookupState->container); + } else { + /* no match yet. Remember this container for + * later use */ + lookupState->container = COPY(mp->File); + } + lookupState->nbContainers++; + } + } else + lookupflags = ACCEPT_DIR | DO_OPEN | NO_DOTS; + + ret = 0; + r = 0; + have_one = 0; + initializeDirentry(&entry, mp->File); + while(!(ret & STOP_NOW) && + !got_signal && + (r=vfat_lookup(&entry, filename0, length, + lookupflags | NO_MSG, + mp->shortname, mp->longname)) == 0 ){ + if(checkForDot(lookupflags, entry.name)) + /* while following the path, ignore the + * special entries if they were not + * explicitly given */ + continue; + have_one = 1; + if(ptr) { + Stream_t *SubDir; + SubDir = mp->File = OpenFileByDirentry(&entry); + ret |= recurs_dos_loop(mp, ptr, filename1, lookupState); + FREE(&SubDir); + } else { + ret |= handle_leaf(&entry, mp, lookupState); + if(isUniqueTarget(mp->targetName)) + return ret | STOP_NOW; + } + if(doing_mcwd) + break; + } + if (r == -2) + return ERROR_ONE; + if(got_signal) + return ret | ERROR_ONE; + if(doing_mcwd & !have_one) + return NO_CWD; + return ret; +} + +static int common_dos_loop(MainParam_t *mp, const char *pathname, + lookupState_t *lookupState, int open_mode) + +{ + Stream_t *RootDir; + char *cwd; + char *drive; + char *rest; + + int ret; + mp->loop = _dos_loop; + + drive='\0'; + cwd = ""; + if((rest = skip_drive(pathname)) > pathname) { + drive = get_drive(pathname, NULL); + if (strncmp(pathname, mp->mcwd, rest - pathname) == 0) + cwd = skip_drive(mp->mcwd); + pathname = rest; + } else { + drive = get_drive(mp->mcwd, NULL); + cwd = skip_drive(mp->mcwd); + } + + if(*pathname=='/') /* absolute path name */ + cwd = ""; + + RootDir = mp->File = open_root_dir(drive, open_mode); + if(!mp->File) + return ERROR_ONE; + + ret = recurs_dos_loop(mp, cwd, pathname, lookupState); + if(ret & NO_CWD) { + /* no CWD */ + *mp->mcwd = '\0'; + unlink_mcwd(); + ret = recurs_dos_loop(mp, "", pathname, lookupState); + } + FREE(&RootDir); + return ret; +} + +static int dos_loop(MainParam_t *mp, const char *arg) +{ + return common_dos_loop(mp, arg, 0, mp->openflags); +} + + +static int dos_target_lookup(MainParam_t *mp, const char *arg) +{ + lookupState_t lookupState; + int ret; + int lookupflags; + + lookupState.nbDirs = 0; + lookupState.Dir = 0; + lookupState.nbContainers = 0; + lookupState.container = 0; + + lookupflags = mp->lookupflags; + mp->lookupflags = DO_OPEN | ACCEPT_DIR; + ret = common_dos_loop(mp, arg, &lookupState, O_RDWR); + mp->lookupflags = lookupflags; + if(ret & ERROR_ONE) + return ret; + + if(lookupState.nbDirs) { + mp->targetName = 0; + mp->targetDir = lookupState.Dir; + FREE(&lookupState.container); /* container no longer needed */ + return ret; + } + + switch(lookupState.nbContainers) { + case 0: + /* no match */ + fprintf(stderr,"%s: no match for target\n", arg); + return MISSED_ONE; + case 1: + mp->targetName = strdup(lookupState.filename); + mp->targetDir = lookupState.container; + return ret; + default: + /* too much */ + fprintf(stderr, "Ambigous %s\n", arg); + return ERROR_ONE; + } +} + +int unix_target_lookup(MainParam_t *mp, const char *arg) +{ + char *ptr; + mp->unixTarget = strdup(arg); + /* try complete filename */ + if(access(mp->unixTarget, F_OK) == 0) + return GOT_ONE; + ptr = strrchr(mp->unixTarget, '/'); + if(!ptr) { + mp->targetName = mp->unixTarget; + mp->unixTarget = strdup("."); + return GOT_ONE; + } else { + *ptr = '\0'; + mp->targetName = ptr+1; + return GOT_ONE; + } +} + +int target_lookup(MainParam_t *mp, const char *arg) +{ + if((mp->lookupflags & NO_UNIX) || skip_drive(arg) > arg) + return dos_target_lookup(mp, arg); + else + return unix_target_lookup(mp, arg); +} + +int main_loop(MainParam_t *mp, char **argv, int argc) +{ + int i; + int ret, Bret; + + Bret = 0; + + if(argc != 1 && mp->targetName) { + fprintf(stderr, + "Several file names given, but last argument (%s) not a directory\n", mp->targetName); + } + + for (i = 0; i < argc; i++) { + if ( got_signal ) + break; + mp->originalArg = argv[i]; + mp->basenameHasWildcard = strpbrk(_basename(mp->originalArg), + "*[?") != 0; + if (mp->unixcallback && skip_drive(argv[i]) == argv[i]) + ret = unix_loop(0, mp, argv[i], 1); + else + ret = dos_loop(mp, argv[i]); + + if (! (ret & (GOT_ONE | ERROR_ONE)) ) { + /* one argument was unmatched */ + fprintf(stderr, "%s: File \"%s\" not found\n", + progname, argv[i]); + ret |= ERROR_ONE; + } + Bret |= ret; + if(mp->fast_quit && (Bret & (MISSED_ONE | ERROR_ONE))) + break; + } + FREE(&mp->targetDir); + if(Bret & ERROR_ONE) + return 1; + if ((Bret & GOT_ONE) && ( Bret & MISSED_ONE)) + return 2; + if (Bret & MISSED_ONE) + return 1; + return 0; +} + +static int dispatchToFile(direntry_t *entry, MainParam_t *mp) +{ + if(entry) + return mp->callback(entry, mp); + else + return mp->unixcallback(mp); +} + + +void init_mp(MainParam_t *mp) +{ + fix_mcwd(mp->mcwd); + mp->openflags = 0; + mp->targetName = 0; + mp->targetDir = 0; + mp->unixTarget = 0; + mp->dirCallback = dispatchToFile; + mp->unixcallback = NULL; + mp->shortname = mp->longname = 0; + mp->File = 0; + mp->fast_quit = 0; +} + +const char *mpGetBasename(MainParam_t *mp) +{ + if(mp->direntry) + return mp->direntry->name; + else + return _basename(mp->unixSourceName); +} + +void mpPrintFilename(FILE *fp, MainParam_t *mp) +{ + if(mp->direntry) + fprintPwd(fp, mp->direntry, 0); + else + fprintf(fp,"%s",mp->originalArg); +} + +const char *mpPickTargetName(MainParam_t *mp) +{ + /* picks the target name: either the one explicitly given by the + * user, or the same as the source */ + if(mp->targetName) + return mp->targetName; + else + return mpGetBasename(mp); +} + +char *mpBuildUnixFilename(MainParam_t *mp) +{ + const char *target; + char *ret; + + target = mpPickTargetName(mp); + ret = malloc(strlen(mp->unixTarget) + 2 + strlen(target)); + if(!ret) + return 0; + strcpy(ret, mp->unixTarget); + if(*target) { +#if 1 /* fix for 'mcopy -n x:file existingfile' -- H. Lermen 980816 */ + if(!mp->targetName && !mp->targetDir) { + struct stat buf; + if (!stat(ret, &buf) && !S_ISDIR(buf.st_mode)) + return ret; + } +#endif + strcat(ret, "/"); + strcat(ret, target); + } + return ret; +} diff --git a/commands/i386/mtools-3.9.7/mainloop.h b/commands/i386/mtools-3.9.7/mainloop.h new file mode 100755 index 000000000..a92d22105 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mainloop.h @@ -0,0 +1,79 @@ +#ifndef MTOOLS_MAINLOOP_H +#define MTOOLS_MAINLOOP_H + +#ifndef OS_Minix +#include <sys/param.h> +#endif +#include "vfat.h" +#include "mtoolsDirent.h" + +typedef struct MainParam_t { + /* stuff needing to be initialised by the caller */ + int (*loop)(Stream_t *Dir, struct MainParam_t *mp, + const char *filename); + int (*dirCallback)(direntry_t *, struct MainParam_t *); + int (*callback)(direntry_t *, struct MainParam_t *); + int (*unixcallback)(struct MainParam_t *mp); + + void *arg; /* command-specific parameters + * to be passed to callback */ + + int openflags; /* flags used to open disk */ + int lookupflags; /* flags used to lookup up using vfat_lookup */ + int fast_quit; /* for commands manipulating multiple files, quit + * as soon as even _one_ file has a problem */ + + char *shortname; /* where to put the short name of the matched file */ + char *longname; /* where to put the long name of the matched file */ + + /* out parameters */ + Stream_t *File; + + direntry_t *direntry; /* dir of this entry */ + char *unixSourceName; /* filename of the last opened Unix source + * file (Unix equiv of Dos direntry) */ + + Stream_t *targetDir; /* directory where to place files */ + char *unixTarget; /* directory on Unix where to put files */ + + const char *targetName; /* basename of target file, or NULL if same + * basename as source should be conserved */ + + char *originalArg; /* original argument, complete with wildcards */ + int basenameHasWildcard; /* true if there are wildcards in the + * basename */ + + + /* internal data */ + char mcwd[MAX_PATH+4]; + + char *fileName; /* resolved Unix filename */ +} MainParam_t; + +void init_mp(MainParam_t *MainParam); +int main_loop(MainParam_t *MainParam, char **argv, int argc); + +int target_lookup(MainParam_t *mp, const char *arg); + +Stream_t *open_root_dir(char *drivename, int flags); + +const char *mpGetBasename(MainParam_t *mp); /* statically allocated + * string */ + +void mpPrintFilename(FILE *file, MainParam_t *mp); +const char *mpPickTargetName(MainParam_t *mp); /* statically allocated string */ + +char *mpBuildUnixFilename(MainParam_t *mp); /* dynamically allocated, must + * be freed */ + +int isSpecial(const char *name); + +#define MISSED_ONE 2 /* set if one cmd line argument didn't match any files */ +#define GOT_ONE 4 /* set if a match was found, used for exit status */ +#define NO_CWD 8 /* file not found while looking for current working + * directory */ +#define ERROR_ONE 16 /* flat out error, such as problems with target file, + interrupt by user, etc. */ +#define STOP_NOW 32 /* stop as soon as possible, not necessarily an error */ + +#endif diff --git a/commands/i386/mtools-3.9.7/match.c b/commands/i386/mtools-3.9.7/match.c new file mode 100755 index 000000000..af0c5fd8d --- /dev/null +++ b/commands/i386/mtools-3.9.7/match.c @@ -0,0 +1,142 @@ +/* + * Do shell-style pattern matching for '?', '\', '[..]', and '*' wildcards. + * Returns 1 if match, 0 if not. + */ + +#include "sysincludes.h" +#include "mtools.h" + + +static int casecmp(char a,char b) +{ + return toupper(a) == toupper(b); +} + +static int exactcmp(char a,char b) +{ + return a == b; +} + + +static int parse_range(const char **p, const char *s, char *out, + int (*compfn)(char a, char b)) +{ + char table[256]; + int reverse; + int i; + short first, last; + + if (**p == '^') { + reverse = 1; + (*p)++; + } else + reverse=0; + for(i=0; i<256; i++) + table[i]=0; + while(**p != ']') { + if(!**p) + return 0; + if((*p)[1] == '-') { + first = **p; + (*p)+=2; + if(**p == ']') + last = 256; + else + last = *((*p)++); + for(i=first; i<last; i++) + table[i] = 1; + } else + table[(int) *((*p)++)] = 1; + } + if(out) + *out = *s; + if(table[(int) *s]) + return 1 ^ reverse; + if(compfn == exactcmp) + return reverse; + if(table[tolower(*s)]) { + if(out) + *out = tolower(*s); + return 1 ^ reverse; + } + if(table[toupper(*s)]) { + if(out) + *out = toupper(*s); + return 1 ^ reverse; + } + return reverse; +} + + +static int _match(const char *s, const char *p, char *out, int Case, + int length, + int (*compfn) (char a, char b)) +{ + for (; *p != '\0' && length; ) { + switch (*p) { + case '?': /* match any one character */ + if (*s == '\0') + return(0); + if(out) + *(out++) = *s; + break; + case '*': /* match everything */ + while (*p == '*' && length) { + p++; + length--; + } + + /* search for next char in pattern */ + while(*s) { + if(_match(s, p, out, Case, length, + compfn)) + return 1; + if(out) + *out++ = *s; + s++; + } + continue; + case '[': /* match range of characters */ + p++; + length--; + if(!parse_range(&p, s, out++, compfn)) + return 0; + break; + case '\\': /* Literal match with next character */ + p++; + length--; + /* fall thru */ + default: + if (!compfn(*s,*p)) + return(0); + if(out) + *(out++) = *p; + break; + } + p++; + length--; + s++; + } + if(out) + *out = '\0'; + + /* string ended prematurely ? */ + if (*s != '\0') + return(0); + else + return(1); +} + + +int match(const char *s, const char *p, char *out, int Case, int length) +{ + int (*compfn)(char a, char b); + + if(Case) + compfn = casecmp; + else + /*compfn = exactcmp;*/ + compfn = casecmp; + return _match(s, p, out, Case, length, compfn); +} + diff --git a/commands/i386/mtools-3.9.7/mattrib.c b/commands/i386/mtools-3.9.7/mattrib.c new file mode 100755 index 000000000..41e1ecfc9 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mattrib.c @@ -0,0 +1,233 @@ +/* + * mattrib.c + * Change MSDOS file attribute flags + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "mainloop.h" + +typedef struct Arg_t { + char add; + unsigned char remove; + struct MainParam_t mp; + int recursive; + int doPrintName; +} Arg_t; + +int concise; + +static int attrib_file(direntry_t *entry, MainParam_t *mp) +{ + Arg_t *arg=(Arg_t *) mp->arg; + + if(entry->entry != -3) { + /* if not root directory, change it */ + entry->dir.attr = (entry->dir.attr & arg->remove) | arg->add; + dir_write(entry); + } + return GOT_ONE; +} + +static int replay_attrib(direntry_t *entry, MainParam_t *mp) +{ + if ( (IS_ARCHIVE(entry) && IS_DIR(entry)) || + (!IS_ARCHIVE(entry) && !IS_DIR(entry)) || + IS_SYSTEM(entry) || IS_HIDDEN(entry)) { + + printf("mattrib "); + + if (IS_ARCHIVE(entry) && IS_DIR(entry)) { + printf("+a "); + } + + if (!IS_ARCHIVE(entry) && !IS_DIR(entry)) { + printf("-a "); + } + + if (IS_SYSTEM(entry)) { + printf("+s "); + } + + if (IS_HIDDEN(entry)) { + printf("+h "); + } + + fprintPwd(stdout, entry, 1); + printf("\n"); + } + return GOT_ONE; +} + + + +static int view_attrib(direntry_t *entry, MainParam_t *mp) +{ + printf(" "); + if(IS_ARCHIVE(entry)) + putchar('A'); + else + putchar(' '); + fputs(" ",stdout); + if(IS_SYSTEM(entry)) + putchar('S'); + else + putchar(' '); + if(IS_HIDDEN(entry)) + putchar('H'); + else + putchar(' '); + if(IS_READONLY(entry)) + putchar('R'); + else + putchar(' '); + printf(" "); + fprintPwd(stdout, entry, 0); + printf("\n"); + return GOT_ONE; +} + + +static int concise_view_attrib(direntry_t *entry, MainParam_t *mp) +{ + Arg_t *arg=(Arg_t *) mp->arg; + + if(IS_ARCHIVE(entry)) + putchar('A'); + if(IS_DIR(entry)) + putchar('D'); + if(IS_SYSTEM(entry)) + putchar('S'); + if(IS_HIDDEN(entry)) + putchar('H'); + if(IS_READONLY(entry)) + putchar('R'); + if(arg->doPrintName) { + putchar(' '); + fprintPwd(stdout, entry, 0); + } + putchar('\n'); + return GOT_ONE; +} + +static int recursive_attrib(direntry_t *entry, MainParam_t *mp) +{ + mp->callback(entry, mp); + return mp->loop(mp->File, mp, "*"); +} + + +static void usage(void) NORETURN; +static void usage(void) +{ + fprintf(stderr, "Mtools version %s, dated %s\n", + mversion, mdate); + fprintf(stderr, + "Usage: %s [-p/X] [-a|+a] [-h|+h] [-r|+r] [-s|+s] msdosfile [msdosfiles...]\n" + "\t-p Replay how mattrib would set up attributes\n" + "\t-/ Recursive\n" + "\t-X Concise\n", + progname); + exit(1); +} + +static int letterToCode(int letter) +{ + switch (toupper(letter)) { + case 'A': + return ATTR_ARCHIVE; + case 'H': + return ATTR_HIDDEN; + case 'R': + return ATTR_READONLY; + case 'S': + return ATTR_SYSTEM; + default: + usage(); + } +} + + +void mattrib(int argc, char **argv, int type) +{ + Arg_t arg; + int view; + int c; + int concise; + int replay; + char *ptr; + + arg.add = 0; + arg.remove = 0xff; + arg.recursive = 0; + arg.doPrintName = 1; + view = 0; + concise = 0; + replay = 0; + + while ((c = getopt(argc, argv, "/ahrsAHRSXp")) != EOF) { + switch (c) { + default: + arg.remove &= ~letterToCode(c); + break; + case 'p': + replay = 1; + break; + case '/': + arg.recursive = 1; + break; + case 'X': + concise = 1; + break; + case '?': + usage(); + } + } + + for(;optind < argc;optind++) { + switch(argv[optind][0]) { + case '+': + for(ptr = argv[optind] + 1; *ptr; ptr++) + arg.add |= letterToCode(*ptr); + continue; + case '-': + for(ptr = argv[optind] + 1; *ptr; ptr++) + arg.remove &= ~letterToCode(*ptr); + continue; + } + break; + } + + if(arg.remove == 0xff && !arg.add) + view = 1; + + if (optind >= argc) + usage(); + + init_mp(&arg.mp); + if(view){ + if(concise) { + arg.mp.callback = concise_view_attrib; + arg.doPrintName = (argc - optind > 1 || + arg.recursive || + strpbrk(argv[optind], "*[?") != 0); + } else if (replay) { + arg.mp.callback = replay_attrib; + } else + arg.mp.callback = view_attrib; + arg.mp.openflags = O_RDONLY; + } else { + arg.mp.callback = attrib_file; + arg.mp.openflags = O_RDWR; + } + + if(arg.recursive) + arg.mp.dirCallback = recursive_attrib; + + arg.mp.arg = (void *) &arg; + arg.mp.lookupflags = ACCEPT_PLAIN | ACCEPT_DIR; + if(arg.recursive) + arg.mp.lookupflags |= DO_OPEN_DIRS | NO_DOTS; + exit(main_loop(&arg.mp, argv + optind, argc - optind)); +} diff --git a/commands/i386/mtools-3.9.7/mbadblocks.c b/commands/i386/mtools-3.9.7/mbadblocks.c new file mode 100755 index 000000000..d2db0c157 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mbadblocks.c @@ -0,0 +1,77 @@ +/* + * mbadblocks.c + * Mark bad blocks on disk + * + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "mainloop.h" +#include "fsP.h" + +void mbadblocks(int argc, char **argv, int type) +{ + int i; + char *in_buf; + int in_len; + off_t start; + struct MainParam_t mp; + Fs_t *Fs; + Stream_t *Dir; + int ret; + + if (argc != 2 || skip_drive(argv[1]) == argv[1]) { + fprintf(stderr, "Mtools version %s, dated %s\n", + mversion, mdate); + fprintf(stderr, "Usage: %s drive:\n", argv[0]); + exit(1); + } + + init_mp(&mp); + + Dir = open_root_dir(get_drive(argv[1], NULL), O_RDWR); + if (!Dir) { + fprintf(stderr,"%s: Cannot initialize drive\n", argv[0]); + exit(1); + } + + Fs = (Fs_t *)GetFs(Dir); + in_len = Fs->cluster_size * Fs->sector_size; + in_buf = malloc(in_len); + if(!in_buf) { + FREE(&Dir); + printOom(); + exit(1); + } + for(i=0; i < Fs->clus_start; i++ ){ + ret = READS(Fs->Next, + in_buf, sectorsToBytes((Stream_t*)Fs, i), Fs->sector_size); + if( ret < 0 ){ + perror("early error"); + exit(1); + } + if(ret < Fs->sector_size){ + fprintf(stderr,"end of file in file_read\n"); + exit(1); + } + } + + in_len = Fs->cluster_size * Fs->sector_size; + for(i=2; i< Fs->num_clus + 2; i++){ + if(got_signal) + break; + if(Fs->fat_decode((Fs_t*)Fs,i)) + continue; + start = (i - 2) * Fs->cluster_size + Fs->clus_start; + ret = force_read(Fs->Next, in_buf, + sectorsToBytes((Stream_t*)Fs, start), in_len); + if(ret < in_len ){ + printf("Bad cluster %d found\n", i); + fatEncode((Fs_t*)Fs, i, 0xfff7); + continue; + } + } + FREE(&Dir); + exit(0); +} diff --git a/commands/i386/mtools-3.9.7/mcat.c b/commands/i386/mtools-3.9.7/mcat.c new file mode 100755 index 000000000..fd8594b3d --- /dev/null +++ b/commands/i386/mtools-3.9.7/mcat.c @@ -0,0 +1,129 @@ +/* + * mcat.c + * Same thing as cat /dev/fd0 or cat file >/dev/fd0 + * Something, that isn't possible with floppyd anymore. + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "mainloop.h" +#include "fsP.h" +#include "xdf_io.h" +#include "floppyd_io.h" +#include "plain_io.h" + +void usage(void) +{ + fprintf(stderr, "Mtools version %s, dated %s\n", + mversion, mdate); + fprintf(stderr, "Usage: mcat [-w] device\n"); + fprintf(stderr, " -w write on device else read\n"); + exit(1); +} + +#define BUF_SIZE 16000 + +void mcat(int argc, char **argv, int type) +{ + struct device *dev; + struct device out_dev; + char *drive, name[EXPAND_BUF]; + char errmsg[200]; + Stream_t *Stream; + char buf[BUF_SIZE]; + + mt_off_t address = 0; + + char mode = O_RDONLY; + int optindex = 1; + size_t len; + + noPrivileges = 1; + + if (argc < 2) { + usage(); + } + + if (argv[1][0] == '-') { + if (argv[1][1] != 'w') { + usage(); + } + mode = O_WRONLY; + optindex++; + } + + if (argc - optindex < 1) + usage(); + + + if (skip_drive(argv[optindex]) == argv[optindex]) + usage(); + + drive = get_drive(argv[optindex], NULL); + + /* check out a drive whose letter and parameters match */ + sprintf(errmsg, "Drive '%s:' not supported", drive); + Stream = NULL; + for (dev=devices; dev->name; dev++) { + FREE(&Stream); + if (strcmp(dev->drive, drive) != 0) + continue; + out_dev = *dev; + expand(dev->name,name); +#ifdef USING_NEW_VOLD + strcpy(name, getVoldName(dev, name)); +#endif + + Stream = 0; +#ifdef USE_XDF + Stream = XdfOpen(&out_dev, name, mode, errmsg, 0); + if(Stream) + out_dev.use_2m = 0x7f; + +#endif + +#ifdef USE_FLOPPYD + if(!Stream) + Stream = FloppydOpen(&out_dev, dev, name, + mode, errmsg, 0, 1); +#endif + + + if (!Stream) + Stream = SimpleFileOpen(&out_dev, dev, name, mode, + errmsg, 0, 1, 0); + + if( !Stream) + continue; + break; + } + + /* print error msg if needed */ + if ( dev->drive == 0 ){ + FREE(&Stream); + fprintf(stderr,"%s\n",errmsg); + exit(1); + } + + if (mode == O_WRONLY) { + while ((len = fread(buf, 1, BUF_SIZE, stdin)) + == BUF_SIZE) { + WRITES(Stream, buf, address, BUF_SIZE); + address += BUF_SIZE; + } + if (len) + WRITES(Stream, buf, address, len); + } else { + while ((len = READS(Stream, buf, address, BUF_SIZE)) + == BUF_SIZE) { + fwrite(buf, 1, BUF_SIZE, stdout); + address += BUF_SIZE; + } + if (len) + fwrite(buf, 1, len, stdout); + } + + FREE(&Stream); + exit(0); +} diff --git a/commands/i386/mtools-3.9.7/mcd.c b/commands/i386/mtools-3.9.7/mcd.c new file mode 100755 index 000000000..0213cb98a --- /dev/null +++ b/commands/i386/mtools-3.9.7/mcd.c @@ -0,0 +1,46 @@ +/* + * mcd.c: Change MSDOS directories + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mainloop.h" +#include "mtools.h" + + +static int mcd_callback(direntry_t *entry, MainParam_t *mp) +{ + FILE *fp; + + if (!(fp = open_mcwd("w"))){ + fprintf(stderr,"mcd: Can't open mcwd .file for writing\n"); + return ERROR_ONE; + } + + fprintPwd(fp, entry,0); + fprintf(fp, "\n"); + fclose(fp); + return GOT_ONE | STOP_NOW; +} + + +void mcd(int argc, char **argv, int type) +{ + struct MainParam_t mp; + + if (argc > 2) { + fprintf(stderr, "Mtools version %s, dated %s\n", + mversion, mdate); + fprintf(stderr, "Usage: %s: msdosdirectory\n", argv[0]); + exit(1); + } + + init_mp(&mp); + mp.lookupflags = ACCEPT_DIR | NO_DOTS; + mp.dirCallback = mcd_callback; + if (argc == 1) { + printf("%s\n", mp.mcwd); + exit(0); + } else + exit(main_loop(&mp, argv + 1, 1)); +} diff --git a/commands/i386/mtools-3.9.7/mcopy.c b/commands/i386/mtools-3.9.7/mcopy.c new file mode 100755 index 000000000..52b8b6140 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mcopy.c @@ -0,0 +1,584 @@ +/* + * mcopy.c + * Copy an MSDOS files to and from Unix + * + */ + + +#define LOWERCASE + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "vfat.h" +#include "mainloop.h" +#include "plain_io.h" +#include "nameclash.h" +#include "file.h" +#include "fs.h" + + +/* + * Preserve the file modification times after the fclose() + */ + +static void set_mtime(const char *target, time_t mtime) +{ + if (target && strcmp(target, "-") && mtime != 0L) { +#ifdef HAVE_UTIMES + struct timeval tv[2]; + tv[0].tv_sec = mtime; + tv[0].tv_usec = 0; + tv[1].tv_sec = mtime; + tv[1].tv_usec = 0; + utimes((char *)target, tv); +#else +#ifdef HAVE_UTIME + struct utimbuf utbuf; + + utbuf.actime = mtime; + utbuf.modtime = mtime; + utime(target, &utbuf); +#endif +#endif + } + return; +} + +typedef struct Arg_t { + int recursive; + int preserveAttributes; + int preserveTime; + unsigned char attr; + char *path; + int textmode; + int needfilter; + int nowarn; + int verbose; + int type; + MainParam_t mp; + ClashHandling_t ch; +} Arg_t; + +/* Write the Unix file */ +static int unix_write(direntry_t *entry, MainParam_t *mp, int needfilter) +{ + Arg_t *arg=(Arg_t *) mp->arg; + time_t mtime; + Stream_t *File=mp->File; + Stream_t *Target, *Source; + struct stat stbuf; + int ret; + char errmsg[80]; + char *unixFile; + + File->Class->get_data(File, &mtime, 0, 0, 0); + + if (!arg->preserveTime) + mtime = 0L; + + if(arg->type) + unixFile = "-"; + else + unixFile = mpBuildUnixFilename(mp); + if(!unixFile) { + printOom(); + return ERROR_ONE; + } + + /* if we are creating a file, check whether it already exists */ + if(!arg->type) { + if (!arg->nowarn && &arg->type && !access(unixFile, 0)){ + if( ask_confirmation("File \"%s\" exists, overwrite (y/n) ? ", + unixFile,0)) { + free(unixFile); + return ERROR_ONE; + } + + /* sanity checking */ + if (!stat(unixFile, &stbuf) && !S_ISREG(stbuf.st_mode)) { + fprintf(stderr,"\"%s\" is not a regular file\n", + unixFile); + + free(unixFile); + return ERROR_ONE; + } + } + } + + if(!arg->type && arg->verbose) { + fprintf(stderr,"Copying "); + mpPrintFilename(stderr,mp); + fprintf(stderr,"\n"); + } + + if(got_signal) { + free(unixFile); + return ERROR_ONE; + } + + if ((Target = SimpleFileOpen(0, 0, unixFile, + O_WRONLY | O_CREAT | O_TRUNC, + errmsg, 0, 0, 0))) { + ret = 0; + if(needfilter && arg->textmode){ + Source = open_filter(COPY(File)); + if (!Source) + ret = -1; + } else + Source = COPY(File); + + if (ret == 0 ) + ret = copyfile(Source, Target); + FREE(&Source); + FREE(&Target); + if(ret <= -1){ + if(!arg->type) { + unlink(unixFile); + free(unixFile); + } + return ERROR_ONE; + } + if(!arg->type) { + set_mtime(unixFile, mtime); + free(unixFile); + } + return GOT_ONE; + } else { + fprintf(stderr,"%s\n", errmsg); + if(!arg->type) + free(unixFile); + return ERROR_ONE; + } +} + +static int makeUnixDir(char *filename) +{ + if(!mkdir(filename, 0777)) + return 0; + if(errno == EEXIST) { + struct stat buf; + if(stat(filename, &buf) < 0) + return -1; + if(S_ISDIR(buf.st_mode)) + return 0; + errno = ENOTDIR; + } + return -1; +} + +/* Copy a directory to Unix */ +static int unix_copydir(direntry_t *entry, MainParam_t *mp) +{ + Arg_t *arg=(Arg_t *) mp->arg; + time_t mtime; + Stream_t *File=mp->File; + int ret; + char *unixFile; + + if (!arg->recursive && mp->basenameHasWildcard) + return 0; + + File->Class->get_data(File, &mtime, 0, 0, 0); + if (!arg->preserveTime) + mtime = 0L; + if(!arg->type && arg->verbose) { + fprintf(stderr,"Copying "); + fprintPwd(stderr, entry,0); + fprintf(stderr, "\n"); + } + if(got_signal) + return ERROR_ONE; + unixFile = mpBuildUnixFilename(mp); + if(!unixFile) { + printOom(); + return ERROR_ONE; + } + if(arg->type || !*mpPickTargetName(mp) || !makeUnixDir(unixFile)) { + Arg_t newArg; + + newArg = *arg; + newArg.mp.arg = (void *) &newArg; + newArg.mp.unixTarget = unixFile; + newArg.mp.targetName = 0; + newArg.mp.basenameHasWildcard = 1; + + ret = mp->loop(File, &newArg.mp, "*"); + set_mtime(unixFile, mtime); + free(unixFile); + return ret | GOT_ONE; + } else { + perror("mkdir"); + fprintf(stderr, + "Failure to make directory %s\n", + unixFile); + free(unixFile); + return ERROR_ONE; + } +} + +static int dos_to_unix(direntry_t *entry, MainParam_t *mp) +{ + return unix_write(entry, mp, 1); +} + + +static int unix_to_unix(MainParam_t *mp) +{ + return unix_write(0, mp, 0); +} + + +static int directory_dos_to_unix(direntry_t *entry, MainParam_t *mp) +{ + return unix_copydir(entry, mp); +} + +/* + * Open the named file for read, create the cluster chain, return the + * directory structure or NULL on error. + */ +static int writeit(char *dosname, + char *longname, + void *arg0, + direntry_t *entry) +{ + Stream_t *Target; + time_t now; + int type, fat, ret; + time_t date; + mt_size_t filesize, newsize; + Arg_t *arg = (Arg_t *) arg0; + + + + if (arg->mp.File->Class->get_data(arg->mp.File, + & date, &filesize, &type, 0) < 0 ){ + fprintf(stderr, "Can't stat source file\n"); + return -1; + } + + if (type){ + if (arg->verbose) + fprintf(stderr, "\"%s\" is a directory\n", longname); + return -1; + } + + /*if (!arg->single || arg->recursive)*/ + if(arg->verbose) + fprintf(stderr,"Copying %s\n", longname); + if(got_signal) + return -1; + + /* will it fit? */ + if (!getfreeMinBytes(arg->mp.targetDir, filesize)) + return -1; + + /* preserve mod time? */ + if (arg->preserveTime) + now = date; + else + getTimeNow(&now); + + mk_entry(dosname, arg->attr, 1, 0, now, &entry->dir); + + Target = OpenFileByDirentry(entry); + if(!Target){ + fprintf(stderr,"Could not open Target\n"); + exit(1); + } + if (arg->needfilter & arg->textmode) + Target = open_filter(Target); + + + + ret = copyfile(arg->mp.File, Target); + GET_DATA(Target, 0, &newsize, 0, &fat); + FREE(&Target); + if (arg->needfilter & arg->textmode) + newsize++; /* ugly hack: we gathered the size before the Ctrl-Z + * was written. Increment it manually */ + if(ret < 0 ){ + fat_free(arg->mp.targetDir, fat); + return -1; + } else { + mk_entry(dosname, arg->attr, fat, truncBytes32(newsize), + now, &entry->dir); + return 0; + } +} + + + +static int dos_write(direntry_t *entry, MainParam_t *mp, int needfilter) +/* write a messy dos file to another messy dos file */ +{ + int result; + Arg_t * arg = (Arg_t *) (mp->arg); + const char *targetName = mpPickTargetName(mp); + + if(entry && arg->preserveAttributes) + arg->attr = entry->dir.attr; + else + arg->attr = ATTR_ARCHIVE; + + arg->needfilter = needfilter; + if (entry && mp->targetDir == entry->Dir){ + arg->ch.ignore_entry = -1; + arg->ch.source = entry->entry; + } else { + arg->ch.ignore_entry = -1; + arg->ch.source = -2; + } + result = mwrite_one(mp->targetDir, targetName, 0, + writeit, (void *)arg, &arg->ch); + if(result == 1) + return GOT_ONE; + else + return ERROR_ONE; +} + +static Stream_t *subDir(Stream_t *parent, const char *filename) +{ + direntry_t entry; + initializeDirentry(&entry, parent); + + switch(vfat_lookup(&entry, filename, -1, ACCEPT_DIR, 0, 0)) { + case 0: + return OpenFileByDirentry(&entry); + case -1: + return NULL; + default: /* IO Error */ + return NULL; + } +} + +static int dos_copydir(direntry_t *entry, MainParam_t *mp) +/* copyes a directory to Dos */ +{ + Arg_t * arg = (Arg_t *) (mp->arg); + Arg_t newArg; + time_t now; + time_t date; + int ret; + const char *targetName = mpPickTargetName(mp); + + if (!arg->recursive && mp->basenameHasWildcard) + return 0; + + if(entry && isSubdirOf(mp->targetDir, mp->File)) { + fprintf(stderr, "Cannot recursively copy directory "); + fprintPwd(stderr, entry,0); + fprintf(stderr, " into one of its own subdirectories "); + fprintPwd(stderr, getDirentry(mp->targetDir),0); + fprintf(stderr, "\n"); + return ERROR_ONE; + } + + if (arg->mp.File->Class->get_data(arg->mp.File, + & date, 0, 0, 0) < 0 ){ + fprintf(stderr, "Can't stat source file\n"); + return ERROR_ONE; + } + + if(!arg->type && arg->verbose) + fprintf(stderr,"Copying %s\n", mpGetBasename(mp)); + + if(entry && arg->preserveAttributes) + arg->attr = entry->dir.attr; + else + arg->attr = 0; + + if (entry && (mp->targetDir == entry->Dir)){ + arg->ch.ignore_entry = -1; + arg->ch.source = entry->entry; + } else { + arg->ch.ignore_entry = -1; + arg->ch.source = -2; + } + + /* preserve mod time? */ + if (arg->preserveTime) + now = date; + else + getTimeNow(&now); + + newArg = *arg; + newArg.mp.arg = &newArg; + newArg.mp.targetName = 0; + newArg.mp.basenameHasWildcard = 1; + if(*targetName) { + /* maybe the directory already exist. Use it */ + newArg.mp.targetDir = subDir(mp->targetDir, targetName); + if(!newArg.mp.targetDir) + newArg.mp.targetDir = createDir(mp->targetDir, + targetName, + &arg->ch, arg->attr, + now); + } else + newArg.mp.targetDir = mp->targetDir; + + if(!newArg.mp.targetDir) + return ERROR_ONE; + + ret = mp->loop(mp->File, &newArg.mp, "*"); + if(*targetName) + FREE(&newArg.mp.targetDir); + return ret | GOT_ONE; +} + + +static int dos_to_dos(direntry_t *entry, MainParam_t *mp) +{ + return dos_write(entry, mp, 0); +} + +static int unix_to_dos(MainParam_t *mp) +{ + return dos_write(0, mp, 1); +} + + +static void usage(void) +{ + fprintf(stderr, + "Mtools version %s, dated %s\n", mversion, mdate); + fprintf(stderr, + "Usage: %s [-/spabtnmvQB] [-D clash_option] sourcefile targetfile\n", progname); + fprintf(stderr, + " %s [-/spabtnmvQB] [-D clash_option] sourcefile [sourcefiles...] targetdirectory\n", + progname); + fprintf(stderr, + "\t-/ -s Recursive\n" + "\t-p Preserve attributes\n" + "\t-a -t Textmode\n" + "\t-n Overwrite UNIX files without confirmation\n" + "\t-m Preserve file time (default under Minix)\n" + "\t-v Verbose\n" + "\t-Q Quit on the first error\n" + "\t-b -B Batch mode (faster, but less crash resistent)\n" + "\t-o Overwrite DOS files without confirmation\n"); + exit(1); +} + +void mcopy(int argc, char **argv, int mtype) +{ + Arg_t arg; + int c, ret, fastquit; + int todir; + + + /* get command line options */ + + init_clash_handling(& arg.ch); + + /* get command line options */ + todir = 0; + arg.recursive = 0; +#ifdef OS_Minix + arg.preserveTime = 1; /* Copy file time as DOS does. */ +#else + arg.preserveTime = 0; +#endif + arg.preserveAttributes = 0; + arg.nowarn = 0; + arg.textmode = 0; + arg.verbose = 0; + arg.type = mtype; + fastquit = 0; + while ((c = getopt(argc, argv, "abB/sptnmvQD:o")) != EOF) { + switch (c) { + case 's': + case '/': + arg.recursive = 1; + break; + case 'p': + arg.preserveAttributes = 1; + break; + case 'a': + case 't': + arg.textmode = 1; + break; + case 'n': + arg.nowarn = 1; + break; + case 'm': + arg.preserveTime = 1; + break; + case 'v': + arg.verbose = 1; + break; + case 'Q': + fastquit = 1; + break; + case 'B': + case 'b': + batchmode = 1; + break; + case 'o': + handle_clash_options(&arg.ch, c); + break; + case 'D': + if(handle_clash_options(&arg.ch, *optarg)) + usage(); + break; + case '?': + usage(); + default: + break; + } + } + + if (argc - optind < 1) + usage(); + + init_mp(&arg.mp); + arg.mp.lookupflags = ACCEPT_PLAIN | ACCEPT_DIR | DO_OPEN | NO_DOTS; + arg.mp.fast_quit = fastquit; + arg.mp.arg = (void *) &arg; + arg.mp.openflags = O_RDONLY; + + /* last parameter is "-", use mtype mode */ + if(!mtype && !strcmp(argv[argc-1], "-")) { + arg.type = mtype = 1; + argc--; + } + + if(mtype){ + /* Mtype = copying to stdout */ + arg.mp.targetName = strdup("-"); + arg.mp.unixTarget = strdup(""); + arg.mp.callback = dos_to_unix; + arg.mp.dirCallback = unix_copydir; + arg.mp.unixcallback = unix_to_unix; + } else { + char *target; + if (argc - optind == 1) { + /* copying to the current directory */ + target = "."; + } else { + /* target is the last item mentioned */ + argc--; + target = argv[argc]; + } + + ret = target_lookup(&arg.mp, target); + if(!arg.mp.targetDir && !arg.mp.unixTarget) { + fprintf(stderr,"Bad target %s\n", target); + exit(1); + } + + /* callback functions */ + if(arg.mp.unixTarget) { + arg.mp.callback = dos_to_unix; + arg.mp.dirCallback = directory_dos_to_unix; + arg.mp.unixcallback = unix_to_unix; + } else { + arg.mp.dirCallback = dos_copydir; + arg.mp.callback = dos_to_dos; + arg.mp.unixcallback = unix_to_dos; + } + } + + exit(main_loop(&arg.mp, argv + optind, argc - optind)); +} diff --git a/commands/i386/mtools-3.9.7/mdel.c b/commands/i386/mtools-3.9.7/mdel.c new file mode 100755 index 000000000..b3d6812e2 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mdel.c @@ -0,0 +1,173 @@ +/* + * mdel.c + * Delete an MSDOS file + * + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "stream.h" +#include "mainloop.h" +#include "fs.h" +#include "file.h" + +typedef struct Arg_t { + int deltype; + int verbose; +} Arg_t; + +static int del_entry(direntry_t *entry, MainParam_t *mp) +{ + Arg_t *arg=(Arg_t *) mp->arg; + direntry_t longNameEntry; + int i; + + if(got_signal) + return ERROR_ONE; + + if(entry->entry == -3) { + fprintf(stderr, "Cannot remove root directory\n"); + return ERROR_ONE; + } + + if (arg->verbose) { + fprintf(stderr,"Removing "); + fprintPwd(stdout, entry,0); + putchar('\n'); + } + + if ((entry->dir.attr & (ATTR_READONLY | ATTR_SYSTEM)) && + (ask_confirmation("%s: \"%s\" is read only, erase anyway (y/n) ? ", + progname, entry->name))) + return ERROR_ONE; + if (fatFreeWithDirentry(entry)) + return ERROR_ONE; + + initializeDirentry(&longNameEntry, entry->Dir); + for(i=entry->beginSlot; i< entry->endSlot; i++) { + int error; + longNameEntry.entry=i; + dir_read(&longNameEntry, &error); + if(error) + break; + longNameEntry.dir.name[0] = (char) DELMARK; + dir_write(&longNameEntry); + } + + entry->dir.name[0] = (char) DELMARK; + dir_write(entry); + return GOT_ONE; +} + +static int del_file(direntry_t *entry, MainParam_t *mp) +{ + char shortname[13]; + direntry_t subEntry; + Stream_t *SubDir; + Arg_t *arg = (Arg_t *) mp->arg; + MainParam_t sonmp; + int ret; + int r; + + sonmp = *mp; + sonmp.arg = mp->arg; + + r = 0; + if (IS_DIR(entry)){ + /* a directory */ + SubDir = OpenFileByDirentry(entry); + initializeDirentry(&subEntry, SubDir); + ret = 0; + while((r=vfat_lookup(&subEntry, "*", 1, + ACCEPT_DIR | ACCEPT_PLAIN, + shortname, NULL)) == 0 ){ + if(shortname[0] != DELMARK && + shortname[0] && + shortname[0] != '.' ){ + if(arg->deltype != 2){ + fprintf(stderr, + "Directory "); + fprintPwd(stderr, entry,0); + fprintf(stderr," non empty\n"); + ret = ERROR_ONE; + break; + } + if(got_signal) { + ret = ERROR_ONE; + break; + } + ret = del_file(&subEntry, &sonmp); + if( ret & ERROR_ONE) + break; + ret = 0; + } + } + FREE(&SubDir); + if (r == -2) + return ERROR_ONE; + if(ret) + return ret; + } + return del_entry(entry, mp); +} + + +static void usage(void) +{ + fprintf(stderr, + "Mtools version %s, dated %s\n", mversion, mdate); + fprintf(stderr, + "Usage: %s [-v] msdosfile [msdosfiles...]\n" + "\t-v Verbose\n", + progname); + exit(1); +} + +void mdel(int argc, char **argv, int deltype) +{ + Arg_t arg; + MainParam_t mp; + int c,i; + + arg.verbose = 0; + while ((c = getopt(argc, argv, "v")) != EOF) { + switch (c) { + case 'v': + arg.verbose = 1; + break; + default: + usage(); + } + } + + if(argc == optind) + usage(); + + init_mp(&mp); + mp.callback = del_file; + mp.arg = (void *) &arg; + mp.openflags = O_RDWR; + arg.deltype = deltype; + switch(deltype){ + case 0: + mp.lookupflags = ACCEPT_PLAIN; /* mdel */ + break; + case 1: + mp.lookupflags = ACCEPT_DIR; /* mrd */ + break; + case 2: + mp.lookupflags = ACCEPT_DIR | ACCEPT_PLAIN; /* mdeltree */ + break; + } + mp.lookupflags |= NO_DOTS; + for(i=optind;i<argc;i++) { + int b,l; + b = skip_drive(argv[i]) - argv[i]; + l = strlen(argv[i]+b); + if(l > 1 && argv[i][b+l-1] == '/') + argv[i][b+l-1] = '\0'; + } + + exit(main_loop(&mp, argv + optind, argc - optind)); +} diff --git a/commands/i386/mtools-3.9.7/mdir.c b/commands/i386/mtools-3.9.7/mdir.c new file mode 100755 index 000000000..42195afc7 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mdir.c @@ -0,0 +1,579 @@ +/* + * mdir.c: + * Display an MSDOS directory + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "vfat.h" +#include "mtools.h" +#include "file.h" +#include "mainloop.h" +#include "fs.h" +#include "codepage.h" + +#ifdef TEST_SIZE +#include "fsP.h" +#endif + +static int recursive; +static int wide; +static int all; +static int concise; +static int fast=0; +#if 0 +static int testmode = 0; +#endif +static char *dirPath; +static char *currentDrive; +static Stream_t *currentDir; + +static int filesInDir; /* files in current dir */ +static int filesOnDrive; /* files on drive */ + +static int dirsOnDrive; /* number of listed directories on this drive */ + +static int debug = 0; /* debug mode */ + +static mt_size_t bytesInDir; +static mt_size_t bytesOnDrive; +static Stream_t *RootDir; + + +static char shortname[13]; +static char longname[VBUFSIZE]; + + +/* + * Print an MSDOS directory date stamp. + */ +static inline void print_date(struct directory *dir) +{ + char year[5]; + char day[3]; + char month[3]; + char *p; + + sprintf(year, "%04d", DOS_YEAR(dir)); + sprintf(day, "%02d", DOS_DAY(dir)); + sprintf(month, "%02d", DOS_MONTH(dir)); + + for(p=mtools_date_string; *p; p++) { + if(!strncasecmp(p, "yyyy", 4)) { + printf("%04d", DOS_YEAR(dir)); + p+= 3; + continue; + } else if(!strncasecmp(p, "yy", 2)) { + printf("%02d", DOS_YEAR(dir) % 100); + p++; + continue; + } else if(!strncasecmp(p, "dd", 2)) { + printf("%02d", DOS_DAY(dir)); + p++; + continue; + } else if(!strncasecmp(p, "mm", 2)) { + printf("%02d", DOS_MONTH(dir)); + p++; + continue; + } + putchar(*p); + } +} + +/* + * Print an MSDOS directory time stamp. + */ +static inline void print_time(struct directory *dir) +{ + char am_pm; + int hour = DOS_HOUR(dir); + + if(!mtools_twenty_four_hour_clock) { + am_pm = (hour >= 12) ? 'p' : 'a'; + if (hour > 12) + hour = hour - 12; + if (hour == 0) + hour = 12; + } else + am_pm = ' '; + + printf("%2d:%02d%c", hour, DOS_MINUTE(dir), am_pm); +} + +/* + * Return a number in dotted notation + */ +static const char *dotted_num(mt_size_t num, int width, char **buf) +{ + int len; + register char *srcp, *dstp; + int size; + + unsigned long numlo; + unsigned long numhi; + + if (num < 0) { + /* warn about negative numbers here. They should not occur */ + fprintf(stderr, "Invalid negative number\n"); + } + + size = width + width; + *buf = malloc(size+1); + + if (*buf == NULL) + return ""; + + /* Create the number in maximum width; make sure that the string + * length is not exceeded (in %6ld, the result can be longer than 6!) + */ + + numlo = num % 1000000000; + numhi = num / 1000000000; + + if(numhi && size > 9) { + sprintf(*buf, "%.*lu%09lu", size-9, numhi, numlo); + } else { + sprintf(*buf, "%.*lu", size, numlo); + } + + for (srcp=*buf; srcp[1] != '\0'; ++srcp) + if (srcp[0] == '0') + srcp[0] = ' '; + else + break; + + len = strlen(*buf); + srcp = (*buf)+len; + dstp = (*buf)+len+1; + + for ( ; dstp >= (*buf)+4 && isdigit (srcp[-1]); ) { + srcp -= 3; /* from here we copy three digits */ + dstp -= 4; /* that's where we put these 3 digits */ + } + + /* now finally copy the 3-byte blocks to their new place */ + while (dstp < (*buf) + len) { + dstp[0] = srcp[0]; + dstp[1] = srcp[1]; + dstp[2] = srcp[2]; + if (dstp + 3 < (*buf) + len) + /* use spaces instead of dots: they please both + * Americans and Europeans */ + dstp[3] = ' '; + srcp += 3; + dstp += 4; + } + + return (*buf) + len-width; +} + +static inline int print_volume_label(Stream_t *Dir, char *drive) +{ + Stream_t *Stream = GetFs(Dir); + direntry_t entry; + DeclareThis(FsPublic_t); + char shortname[13]; + char longname[VBUFSIZE]; + int r; + + RootDir = OpenRoot(Stream); + if(concise) + return 0; + + /* find the volume label */ + + initializeDirentry(&entry, RootDir); + if((r=vfat_lookup(&entry, 0, 0, ACCEPT_LABEL | MATCH_ANY, + shortname, longname)) ) { + if (r == -2) { + /* I/O Error */ + return -1; + } + printf(" Volume in drive %s has no label", drive); + } else if (*longname) + printf(" Volume in drive %s is %s (abbr=%s)", + drive, longname, shortname); + else + printf(" Volume in drive %s is %s", + drive, shortname); + if(This->serialized) + printf("\n Volume Serial Number is %04lX-%04lX", + (This->serial_number >> 16) & 0xffff, + This->serial_number & 0xffff); + return 0; +} + + +static void printSummary(int files, mt_size_t bytes) +{ + if(!filesInDir) + printf("No files\n"); + else { + char *s1; + printf(" %3d file", files); + if(files == 1) + putchar(' '); + else + putchar('s'); + printf(" %s bytes\n", + dotted_num(bytes, 13, &s1)); + if(s1) + free(s1); + } +} + +static void leaveDirectory(int haveError); + +static void leaveDrive(int haveError) +{ + if(!currentDrive) + return; + leaveDirectory(haveError); + if(!concise && !haveError) { + char *s1; + + if(dirsOnDrive > 1) { + printf("\nTotal files listed:\n"); + printSummary(filesOnDrive, bytesOnDrive); + } + if(RootDir && !fast) { + mt_off_t bytes = getfree(RootDir); + printf(" %s bytes free\n\n", + dotted_num(bytes,17, &s1)); +#ifdef TEST_SIZE + ((Fs_t*)GetFs(RootDir))->freeSpace = 0; + bytes = getfree(RootDir); + printf(" %s bytes free\n\n", + dotted_num(bytes,17, &s1)); +#endif + } + if(s1) + free(s1); + } + FREE(&RootDir); + currentDrive = NULL; +} + + +static int enterDrive(Stream_t *Dir, char *drive) +{ + int r; + if(currentDrive != NULL && strcmp(currentDrive, drive) == 0) + return 0; /* still the same */ + + leaveDrive(0); + currentDrive = drive; + + r = print_volume_label(Dir, drive); + if (r) + return r; + + + bytesOnDrive = 0; + filesOnDrive = 0; + dirsOnDrive = 0; + return 0; +} + +static char *emptyString="<out-of-memory>"; + +static void leaveDirectory(int haveError) +{ + if(!currentDir) + return; + + if (!haveError) { + if(dirPath && dirPath != emptyString) + free(dirPath); + if(wide) + putchar('\n'); + + if(!concise) + printSummary(filesInDir, bytesInDir); + } + FREE(¤tDir); +} + +static int enterDirectory(Stream_t *Dir) +{ + int r; + char *drive; + char *slash; + + if(currentDir == Dir) + return 0; /* still the same directory */ + + leaveDirectory(0); + + drive = getDrive(Dir); + r=enterDrive(Dir, drive); + if(r) + return r; + currentDir = COPY(Dir); + + dirPath = getPwd(getDirentry(Dir)); + if(!dirPath) + dirPath=emptyString; + if(concise && + (slash = strrchr(dirPath, '/')) != NULL && slash[1] == '\0') + *slash = '\0'; + + /* print directory title */ + if(!concise) + printf("\nDirectory for %s\n", dirPath); + + if(!wide && !concise) + printf("\n"); + + dirsOnDrive++; + bytesInDir = 0; + filesInDir = 0; + return 0; +} + +static int list_file(direntry_t *entry, MainParam_t *mp) +{ + unsigned long size; + int i; + int Case; + int r; + + if(!all && (entry->dir.attr & 0x6)) + return 0; + + if(concise && isSpecial(entry->name)) + return 0; + + r=enterDirectory(entry->Dir); + if (r) + return ERROR_ONE; + if (wide) { + if(filesInDir % 5) + putchar(' '); + else + putchar('\n'); + } + + if(IS_DIR(entry)){ + size = 0; + } else + size = FILE_SIZE(&entry->dir); + + Case = entry->dir.Case; + if(!(Case & (BASECASE | EXTCASE)) && + mtools_ignore_short_case) + Case |= BASECASE | EXTCASE; + + if(Case & EXTCASE){ + for(i=0; i<3;i++) + entry->dir.ext[i] = tolower(entry->dir.ext[i]); + } + to_unix(entry->dir.ext,3); + if(Case & BASECASE){ + for(i=0; i<8;i++) + entry->dir.name[i] = tolower(entry->dir.name[i]); + } + to_unix(entry->dir.name,8); + if(wide){ + if(IS_DIR(entry)) + printf("[%s]%*s", shortname, + (int) (15 - 2 - strlen(shortname)), ""); + else + printf("%-15s", shortname); + } else if(!concise) { + /* is a subdirectory */ + if(mtools_dotted_dir) + printf("%-13s", shortname); + else + printf("%-8.8s %-3.3s ", + entry->dir.name, + entry->dir.ext); + if(IS_DIR(entry)) + printf("<DIR> "); + else + printf(" %8ld", (long) size); + printf(" "); + print_date(&entry->dir); + printf(" "); + print_time(&entry->dir); + + if(debug) + printf(" %s %d ", entry->dir.name, START(&entry->dir)); + + if(*longname) + printf(" %s", longname); + printf("\n"); + } else { + printf("%s/%s", dirPath, entry->name); + if(IS_DIR(entry)) + putchar('/'); + putchar('\n'); + } + + filesOnDrive++; + filesInDir++; + + bytesOnDrive += (mt_size_t) size; + bytesInDir += (mt_size_t) size; + return GOT_ONE; +} + +static int list_non_recurs_directory(direntry_t *entry, MainParam_t *mp) +{ + int r; + /* list top-level directory + * If this was matched by wildcard in the basename, list it as + * file, otherwise, list it as directory */ + if (mp->basenameHasWildcard) { + /* wildcard, list it as file */ + return list_file(entry, mp); + } else { + /* no wildcard, list it as directory */ + MainParam_t subMp; + + r=enterDirectory(mp->File); + if(r) + return ERROR_ONE; + + subMp = *mp; + subMp.dirCallback = subMp.callback; + return mp->loop(mp->File, &subMp, "*") | GOT_ONE; + } +} + + +static int list_recurs_directory(direntry_t *entry, MainParam_t *mp) +{ + MainParam_t subMp; + int ret; + + /* first list the files */ + subMp = *mp; + subMp.lookupflags = ACCEPT_DIR | ACCEPT_PLAIN; + subMp.dirCallback = list_file; + subMp.callback = list_file; + + ret = mp->loop(mp->File, &subMp, "*"); + + /* then list subdirectories */ + subMp = *mp; + subMp.lookupflags = ACCEPT_DIR | NO_DOTS | NO_MSG | DO_OPEN; + return ret | mp->loop(mp->File, &subMp, "*"); +} + +#if 0 +static int test_directory(direntry_t *entry, MainParam_t *mp) +{ + Stream_t *File=mp->File; + Stream_t *Target; + char errmsg[80]; + + if ((Target = SimpleFileOpen(0, 0, "-", + O_WRONLY, + errmsg, 0, 0, 0))) { + copyfile(File, Target); + FREE(&Target); + } + return GOT_ONE; +} +#endif + +static void usage(void) +{ + fprintf(stderr, "Mtools version %s, dated %s\n", + mversion, mdate); + fprintf(stderr, "Usage: %s: [-waXbfds/] msdosdirectory\n", + progname); + fprintf(stderr, + " %s: [-waXbfds/] msdosfile [msdosfiles...]\n", + progname); + fprintf(stderr, + "\t-w Wide listing\n" + "\t-a All, including hidden files\n" + "\t-b -X Concise listing\n" + "\t-f Fast, no free space summary\n" + "\t-d Debug mode\n" + "\t-s -/ Recursive\n"); + exit(1); +} + + +void mdir(int argc, char **argv, int type) +{ + int ret; + MainParam_t mp; + int faked; + int c; + char *fakedArgv[] = { "." }; + + concise = 0; + recursive = 0; + wide = all = 0; + /* first argument */ + while ((c = getopt(argc, argv, "waXbfds/")) != EOF) { + switch(c) { + case 'w': + wide = 1; + break; + case 'a': + all = 1; + break; + case 'b': + case 'X': + concise = 1; + /*recursive = 1;*/ + break; + case 's': + case '/': + recursive = 1; + break; + case 'f': + fast = 1; + break; + case 'd': + debug = 1; + break; +#if 0 + case 't': /* test mode */ + testmode = 1; + break; +#endif + default: + usage(); + } + } + + /* fake an argument */ + faked = 0; + if (optind == argc) { + argv = fakedArgv; + argc = 1; + optind = 0; + } + + init_mp(&mp); + currentDrive = '\0'; + currentDir = 0; + RootDir = 0; + dirPath = 0; +#if 0 + if (testmode) { + mp.lookupflags = ACCEPT_DIR | NO_DOTS; + mp.dirCallback = test_directory; + } else +#endif + if(recursive) { + mp.lookupflags = ACCEPT_DIR | DO_OPEN_DIRS | NO_DOTS; + mp.dirCallback = list_recurs_directory; + } else { + mp.lookupflags = ACCEPT_DIR | ACCEPT_PLAIN | DO_OPEN_DIRS; + mp.dirCallback = list_non_recurs_directory; + mp.callback = list_file; + } + mp.longname = longname; + mp.shortname = shortname; + ret=main_loop(&mp, argv + optind, argc - optind); + leaveDirectory(ret); + leaveDrive(ret); + exit(ret); +} diff --git a/commands/i386/mtools-3.9.7/mdoctorfat.c b/commands/i386/mtools-3.9.7/mdoctorfat.c new file mode 100755 index 000000000..febf6aee7 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mdoctorfat.c @@ -0,0 +1,166 @@ +/* Test program for doctoring the fat */ + + +/* + * mcopy.c + * Copy an MSDOS files to and from Unix + * + */ + + +#define LOWERCASE + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "vfat.h" +#include "mainloop.h" +#include "plain_io.h" +#include "nameclash.h" +#include "file.h" +#include "fs.h" +#include "fsP.h" + +typedef struct Arg_t { + char *target; + MainParam_t mp; + ClashHandling_t ch; + Stream_t *sourcefile; + unsigned long fat; + int markbad; + int setsize; + unsigned long size; + Fs_t *Fs; +} Arg_t; + +static int dos_doctorfat(direntry_t *entry, MainParam_t *mp) +{ + Fs_t *Fs = getFs(mp->File); + Arg_t *arg=(Arg_t *) mp->arg; + + if(!arg->markbad && entry->entry != -3) { + /* if not root directory, change it */ + set_word(entry->dir.start, arg->fat & 0xffff); + set_word(entry->dir.startHi, arg->fat >> 16); + if(arg->setsize) + set_dword(entry->dir.size, arg->size); + dir_write(entry); + } + arg->Fs = Fs; + return GOT_ONE; +} + +static int unix_doctorfat(MainParam_t *mp) +{ + fprintf(stderr,"File does not reside on a Dos fs\n"); + return ERROR_ONE; +} + + +static void usage(void) +{ + fprintf(stderr, + "Mtools version %s, dated %s\n", mversion, mdate); + fprintf(stderr, + "Usage: %s [-b] [-o offset] [-s size] file fat\n", progname); + exit(1); +} + +void mdoctorfat(int argc, char **argv, int mtype) +{ + Arg_t arg; + int c, ret; + long address, begin, end; + char *number, *eptr; + int i, j; + long offset; + + /* get command line options */ + + init_clash_handling(& arg.ch); + + offset = 0; + + arg.markbad = 0; + arg.setsize = 0; + + /* get command line options */ + while ((c = getopt(argc, argv, "bo:s:")) != EOF) { + switch (c) { + case 'b': + arg.markbad = 1; + break; + case 'o': + offset = strtoul(optarg,0,0); + break; + case 's': + arg.setsize=1; + arg.size = strtoul(optarg,0,0); + break; + case '?': + usage(); + break; + } + } + + if (argc - optind < 2) + usage(); + + + /* only 1 file to copy... */ + init_mp(&arg.mp); + arg.mp.arg = (void *) &arg; + + arg.mp.callback = dos_doctorfat; + arg.mp.unixcallback = unix_doctorfat; + + arg.mp.lookupflags = ACCEPT_PLAIN | ACCEPT_DIR | DO_OPEN; + arg.mp.openflags = O_RDWR; + arg.fat = strtoul(argv[optind+1], 0, 0) + offset; + ret=main_loop(&arg.mp, argv + optind, 1); + if(ret) + exit(ret); + address = 0; + for(i=optind+1; i < argc; i++) { + number = argv[i]; + if (*number == '<') { + number++; + } + begin = strtoul(number, &eptr, 0); + if (eptr && *eptr == '-') { + number = eptr+1; + end = strtoul(number, &eptr, 0); + } else { + end = begin; + } + if (eptr == number) { + fprintf(stderr, "Not a number: %s\n", number); + exit(-1); + } + + if (eptr && *eptr == '>') { + eptr++; + } + if (eptr && *eptr) { + fprintf(stderr, "Not a number: %s\n", eptr); + exit(-1); + } + + for (j=begin; j <= end; j++) { + if(arg.markbad) { + arg.Fs->fat_encode(arg.Fs, j+offset, arg.Fs->last_fat ^ 6 ^ 8); + } else { + if(address) { + arg.Fs->fat_encode(arg.Fs, address, j+offset); + } + address = j+offset; + } + } + } + + if (address && !arg.markbad) { + arg.Fs->fat_encode(arg.Fs, address, arg.Fs->end_fat); + } + + exit(ret); +} diff --git a/commands/i386/mtools-3.9.7/mdu.c b/commands/i386/mtools-3.9.7/mdu.c new file mode 100755 index 000000000..cce09bb3e --- /dev/null +++ b/commands/i386/mtools-3.9.7/mdu.c @@ -0,0 +1,121 @@ +/* + * mdu.c: + * Display the space occupied by an MSDOS directory + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "vfat.h" +#include "mtools.h" +#include "file.h" +#include "mainloop.h" +#include "fs.h" +#include "codepage.h" + + +typedef struct Arg_t { + int all; + int inDir; + int summary; + struct Arg_t *parent; + char *target; + char *path; + unsigned int blocks; + MainParam_t mp; +} Arg_t; + +static void usage(void) +{ + fprintf(stderr, "Mtools version %s, dated %s\n", + mversion, mdate); + fprintf(stderr, "Usage: %s [-as] msdosdirectory\n" + "\t-a All (also show individual files)\n" + "\t-s Summary for directory only\n", + progname); + exit(1); +} + +static int file_mdu(direntry_t *entry, MainParam_t *mp) +{ + unsigned int blocks; + Arg_t * arg = (Arg_t *) (mp->arg); + + blocks = countBlocks(entry->Dir,getStart(entry->Dir, &entry->dir)); + if(arg->all || !arg->inDir) { + printf("%-7d ", blocks); + fprintPwd(stdout, entry,0); + fputc('\n', stdout); + } + arg->blocks += blocks; + return GOT_ONE; +} + + +static int dir_mdu(direntry_t *entry, MainParam_t *mp) +{ + Arg_t *parentArg = (Arg_t *) (mp->arg); + Arg_t arg; + int ret; + + arg = *parentArg; + arg.mp.arg = (void *) &arg; + arg.parent = parentArg; + arg.inDir = 1; + + /* account for the space occupied by the directory itself */ + if(!isRootDir(entry->Dir)) { + arg.blocks = countBlocks(entry->Dir, + getStart(entry->Dir, &entry->dir)); + } else { + arg.blocks = 0; + } + + /* recursion */ + ret = mp->loop(mp->File, &arg.mp, "*"); + if(!arg.summary || !parentArg->inDir) { + printf("%-7d ", arg.blocks); + fprintPwd(stdout, entry,0); + fputc('\n', stdout); + } + arg.parent->blocks += arg.blocks; + return ret; +} + +void mdu(int argc, char **argv, int type) +{ + Arg_t arg; + int c; + + arg.all = 0; + arg.inDir = 0; + arg.summary = 0; + while ((c = getopt(argc, argv, "as")) != EOF) { + switch (c) { + case 'a': + arg.all = 1; + break; + case 's': + arg.summary = 1; + break; + case '?': + usage(); + } + } + + if (optind >= argc) + usage(); + + if(arg.summary && arg.all) { + fprintf(stderr,"-a and -s options are mutually exclusive\n"); + usage(); + } + + init_mp(&arg.mp); + arg.mp.callback = file_mdu; + arg.mp.openflags = O_RDONLY; + arg.mp.dirCallback = dir_mdu; + + arg.mp.arg = (void *) &arg; + arg.mp.lookupflags = ACCEPT_PLAIN | ACCEPT_DIR | DO_OPEN_DIRS | NO_DOTS; + exit(main_loop(&arg.mp, argv + optind, argc - optind)); +} diff --git a/commands/i386/mtools-3.9.7/mformat.c b/commands/i386/mtools-3.9.7/mformat.c new file mode 100755 index 000000000..aac10afda --- /dev/null +++ b/commands/i386/mtools-3.9.7/mformat.c @@ -0,0 +1,1140 @@ +/* + * mformat.c + */ + +#define DONT_NEED_WAIT + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "mainloop.h" +#include "fsP.h" +#include "file.h" +#include "plain_io.h" +#include "floppyd_io.h" +#include "nameclash.h" +#include "buffer.h" +#ifdef USE_XDF +#include "xdf_io.h" +#endif +#include "partition.h" + +#ifndef abs +#define abs(x) ((x)>0?(x):-(x)) +#endif + +#ifdef OS_linux +#include "linux/hdreg.h" + +#define _LINUX_STRING_H_ +#define kdev_t int +#include "linux/fs.h" +#undef _LINUX_STRING_H_ + +#endif + + +extern int errno; + +static int init_geometry_boot(struct bootsector *boot, struct device *dev, + int sectors0, int rate_0, int rate_any, + int *tot_sectors, int keepBoot) +{ + int i; + int nb_renum; + int sector2; + int size2; + int j; + int sum; + + set_word(boot->nsect, dev->sectors); + set_word(boot->nheads, dev->heads); + + *tot_sectors = dev->heads * dev->sectors * dev->tracks - DWORD(nhs); + + if (*tot_sectors < 0x10000){ + set_word(boot->psect, *tot_sectors); + set_dword(boot->bigsect, 0); + } else { + set_word(boot->psect, 0); + set_dword(boot->bigsect, *tot_sectors); + } + + if (dev->use_2m & 0x7f){ + int bootOffset; + strncpy(boot->banner, "2M-STV04", 8); + boot->ext.old.res_2m = 0; + boot->ext.old.fmt_2mf = 6; + if ( dev->sectors % ( ((1 << dev->ssize) + 3) >> 2 )) + boot->ext.old.wt = 1; + else + boot->ext.old.wt = 0; + boot->ext.old.rate_0= rate_0; + boot->ext.old.rate_any= rate_any; + if (boot->ext.old.rate_any== 2 ) + boot->ext.old.rate_any= 1; + i=76; + + /* Infp0 */ + set_word(boot->ext.old.Infp0, i); + boot->jump[i++] = sectors0; + boot->jump[i++] = 108; + for(j=1; j<= sectors0; j++) + boot->jump[i++] = j; + + set_word(boot->ext.old.InfpX, i); + + boot->jump[i++] = 64; + boot->jump[i++] = 3; + nb_renum = i++; + sector2 = dev->sectors; + size2 = dev->ssize; + j=1; + while( sector2 ){ + while ( sector2 < (1 << size2) >> 2 ) + size2--; + boot->jump[i++] = 128 + j; + boot->jump[i++] = j++; + boot->jump[i++] = size2; + sector2 -= (1 << size2) >> 2; + } + boot->jump[nb_renum] = ( i - nb_renum - 1 ) / 3; + + set_word(boot->ext.old.InfTm, i); + + sector2 = dev->sectors; + size2= dev->ssize; + while(sector2){ + while ( sector2 < 1 << ( size2 - 2) ) + size2--; + boot->jump[i++] = size2; + sector2 -= 1 << (size2 - 2 ); + } + + set_word(boot->ext.old.BootP,i); + bootOffset = i; + + /* checksum */ + for (sum=0, j=64; j<i; j++) + sum += boot->jump[j];/* checksum */ + boot->ext.old.CheckSum=-sum; + return bootOffset; + } else { + if(!keepBoot) { + boot->jump[0] = 0xeb; + boot->jump[1] = 0; + boot->jump[2] = 0x90; + strncpy(boot->banner, "MTOOL397", 8); + /* It looks like some versions of DOS are + * rather picky about this, and assume default + * parameters without this, ignoring any + * indication about cluster size et al. */ + } + return 0; + } +} + + +static int comp_fat_bits(Fs_t *Fs, int estimate, + unsigned int tot_sectors, int fat32) +{ + int needed_fat_bits; + + needed_fat_bits = 12; + +#define MAX_DISK_SIZE(bits,clusters) \ + TOTAL_DISK_SIZE((bits), Fs->sector_size, (clusters), \ + Fs->num_fat, MAX_SECT_PER_CLUSTER) + + if(tot_sectors > MAX_DISK_SIZE(12, FAT12)) + needed_fat_bits = 16; + if(fat32 || tot_sectors > MAX_DISK_SIZE(16, FAT16)) + needed_fat_bits = 32; + +#undef MAX_DISK_SIZE + + if(abs(estimate) && abs(estimate) < needed_fat_bits) { + if(fat32) { + fprintf(stderr, + "Contradiction between FAT size on command line and FAT size in conf file\n"); + exit(1); + } + fprintf(stderr, + "Device too big for a %d bit FAT\n", + estimate); + exit(1); + } + + if(needed_fat_bits == 32 && !fat32 && abs(estimate) !=32){ + fprintf(stderr,"Warning: Using 32 bit FAT. Drive will only be accessibly by Win95 OEM / Win98\n"); + } + + if(!estimate) { + int min_fat16_size; + + if(needed_fat_bits > 12) + return needed_fat_bits; + min_fat16_size = DISK_SIZE(16, Fs->sector_size, FAT12+1, + Fs->num_fat, 1); + if(tot_sectors < min_fat16_size) + return 12; + else if(tot_sectors >= 2* min_fat16_size) + return 16; /* heuristics */ + } + + return estimate; +} + +static void calc_fat_bits2(Fs_t *Fs, unsigned int tot_sectors, int fat_bits) +{ + unsigned int rem_sect; + + /* + * the "remaining sectors" after directory and boot + * hasve been accounted for. + */ + rem_sect = tot_sectors - Fs->dir_len - Fs->fat_start; + switch(abs(fat_bits)) { + case 0: + +#define MY_DISK_SIZE(bits,clusters) \ + DISK_SIZE( (bits), Fs->sector_size, (clusters), \ + Fs->num_fat, Fs->cluster_size) + + if(rem_sect >= MY_DISK_SIZE(16, FAT12 + 1)) + /* big enough for FAT16 */ + set_fat16(Fs); + else if(rem_sect <= MY_DISK_SIZE(12, FAT12)) + /* small enough for FAT12 */ + set_fat12(Fs); + else { + /* "between two chairs", + * augment cluster size, and + * settle it */ + if(Fs->cluster_size < MAX_SECT_PER_CLUSTER) + Fs->cluster_size <<= 1; + set_fat12(Fs); + } + break; +#undef MY_DISK_SIZE + + case 12: + set_fat12(Fs); + break; + case 16: + set_fat16(Fs); + break; + case 32: + set_fat32(Fs); + break; + } +} + +static inline void format_root(Fs_t *Fs, char *label, struct bootsector *boot) +{ + Stream_t *RootDir; + char *buf; + int i; + struct ClashHandling_t ch; + int dirlen; + + init_clash_handling(&ch); + ch.name_converter = label_name; + ch.ignore_entry = -2; + + buf = safe_malloc(Fs->sector_size); + RootDir = OpenRoot((Stream_t *)Fs); + if(!RootDir){ + fprintf(stderr,"Could not open root directory\n"); + exit(1); + } + + memset(buf, '\0', Fs->sector_size); + + if(Fs->fat_bits == 32) { + /* on a FAT32 system, we only write one sector, + * as the directory can be extended at will...*/ + dirlen = 1; + fatAllocate(Fs, Fs->rootCluster, Fs->end_fat); + } else + dirlen = Fs->dir_len; + for (i = 0; i < dirlen; i++) + WRITES(RootDir, buf, sectorsToBytes((Stream_t*)Fs, i), + Fs->sector_size); + + ch.ignore_entry = 1; + if(label[0]) + mwrite_one(RootDir,label, 0, labelit, NULL,&ch); + + FREE(&RootDir); + if(Fs->fat_bits == 32) + set_word(boot->dirents, 0); + else + set_word(boot->dirents, Fs->dir_len * (Fs->sector_size / 32)); + free(buf); +} + + +static void xdf_calc_fat_size(Fs_t *Fs, unsigned int tot_sectors, int fat_bits) +{ + unsigned int rem_sect; + + rem_sect = tot_sectors - Fs->dir_len - Fs->fat_start - 2 * Fs->fat_len; + + if(Fs->fat_len) { + /* an XDF disk, we know the fat_size and have to find + * out the rest. We start with a cluster size of 1 and + * keep doubling until everything fits into the + * FAT. This will occur eventually, as our FAT has a + * minimal size of 1 */ + for(Fs->cluster_size = 1; 1 ; Fs->cluster_size <<= 1) { + Fs->num_clus = rem_sect / Fs->cluster_size; + if(abs(fat_bits) == 16 || Fs->num_clus > FAT12) + set_fat16(Fs); + else + set_fat12(Fs); + if (Fs->fat_len >= NEEDED_FAT_SIZE(Fs)) + return; + } + } + fprintf(stderr,"Internal error while calculating Xdf fat size\n"); + exit(1); +} + + +static void calc_fat_size(Fs_t *Fs, unsigned int tot_sectors) +{ + unsigned int rem_sect; + int tries; + int occupied; + + tries=0; + /* rough estimate of fat size */ + Fs->fat_len = 1; + rem_sect = tot_sectors - Fs->dir_len - Fs->fat_start; + while(1){ + Fs->num_clus = (rem_sect - 2 * Fs->fat_len ) /Fs->cluster_size; + Fs->fat_len = NEEDED_FAT_SIZE(Fs); + occupied = 2 * Fs->fat_len + Fs->cluster_size * Fs->num_clus; + + /* if we have used up more than we have, + * we'll have to reloop */ + + if ( occupied > rem_sect ) + continue; + + + /* if we have exactly used up all + * sectors, fine */ + if ( rem_sect - occupied < Fs->cluster_size ) + break; + + /* if we have not used up all our + * sectors, try again. After the second + * try, decrease the amount of available + * space. This is to deal with the case of + * 344 or 345, ..., 1705, ... available + * sectors. */ + + switch(tries++){ + default: + /* this should never happen */ + fprintf(stderr, + "Internal error in cluster/fat repartition" + " calculation.\n"); + exit(1); + case 2: + /* FALLTHROUGH */ + case 1: + rem_sect-= Fs->cluster_size; + Fs->dir_len += Fs->cluster_size; + case 0: + continue; + } + } + + if ( Fs->num_clus > FAT12 && Fs->fat_bits == 12 ){ + fprintf(stderr,"Too many clusters for this fat size." + " Please choose a 16-bit fat in your /etc/mtools" + " or .mtoolsrc file\n"); + exit(1); + } + if ( Fs->num_clus <= FAT12 && Fs->fat_bits > 12 ){ + fprintf(stderr,"Too few clusters for this fat size." + " Please choose a 12-bit fat in your /etc/mtools" + " or .mtoolsrc file\n"); + exit(1); + } +} + + +static unsigned char bootprog[]= +{0xfa, 0x31, 0xc0, 0x8e, 0xd8, 0x8e, 0xc0, 0xfc, 0xb9, 0x00, 0x01, + 0xbe, 0x00, 0x7c, 0xbf, 0x00, 0x80, 0xf3, 0xa5, 0xea, 0x00, 0x00, + 0x00, 0x08, 0xb8, 0x01, 0x02, 0xbb, 0x00, 0x7c, 0xba, 0x80, 0x00, + 0xb9, 0x01, 0x00, 0xcd, 0x13, 0x72, 0x05, 0xea, 0x00, 0x7c, 0x00, + 0x00, 0xcd, 0x19}; + +static inline void inst_boot_prg(struct bootsector *boot, int offset) +{ + memcpy((char *) boot->jump + offset, + (char *) bootprog, sizeof(bootprog) /sizeof(bootprog[0])); + boot->jump[0] = 0xeb; + boot->jump[1] = offset - 1; + boot->jump[2] = 0x90; + set_word(boot->jump + offset + 20, offset + 24); +} + +static void calc_cluster_size(struct Fs_t *Fs, unsigned int tot_sectors, + int fat_bits) + +{ + unsigned int max_clusters; /* maximal possible number of sectors for + * this FAT entry length (12/16/32) */ + unsigned int max_fat_size; /* maximal size of the FAT for this FAT + * entry length (12/16/32) */ + unsigned int rem_sect; /* remaining sectors after we accounted for + * the root directory and boot sector(s) */ + + switch(abs(fat_bits)) { + case 12: + max_clusters = FAT12; + max_fat_size = Fs->num_fat * + FAT_SIZE(12, Fs->sector_size, max_clusters); + break; + case 16: + case 0: /* still hesititating between 12 and 16 */ + max_clusters = FAT16; + max_fat_size = Fs->num_fat * + FAT_SIZE(16, Fs->sector_size, max_clusters); + break; + case 32: + Fs->cluster_size = 8; + /* According to + * http://www.microsoft.com/kb/articles/q154/9/97.htm, + * Micro$oft does not support FAT32 with less than 4K + */ + return; + default: + fprintf(stderr,"Bad fat size\n"); + exit(1); + } + + rem_sect = tot_sectors - Fs->dir_len - Fs->fat_start; + + /* double the cluster size until we can fill up the disk with + * the maximal number of sectors of this size */ + while(Fs->cluster_size * max_clusters + max_fat_size < rem_sect) { + if(Fs->cluster_size > 64) { + /* bigger than 64. Should fit */ + fprintf(stderr, + "Internal error while calculating cluster size\n"); + exit(1); + } + Fs->cluster_size <<= 1; + } +} + + +struct OldDos_t old_dos[]={ +{ 40, 9, 1, 4, 1, 2, 0xfc }, +{ 40, 9, 2, 7, 2, 2, 0xfd }, +{ 40, 8, 1, 4, 1, 1, 0xfe }, +{ 40, 8, 2, 7, 2, 1, 0xff }, +{ 80, 9, 2, 7, 2, 3, 0xf9 }, +{ 80, 15, 2,14, 1, 7, 0xf9 }, +{ 80, 18, 2,14, 1, 9, 0xf0 }, +{ 80, 36, 2,15, 2, 9, 0xf0 }, +{ 1, 8, 1, 1, 1, 1, 0xf0 }, +}; + +static int old_dos_size_to_geom(int size, int *cyls, int *heads, int *sects) +{ + int i; + size = size * 2; + for(i=0; i < sizeof(old_dos) / sizeof(old_dos[0]); i++){ + if (old_dos[i].sectors * + old_dos[i].tracks * + old_dos[i].heads == size) { + *cyls = old_dos[i].tracks; + *heads = old_dos[i].heads; + *sects = old_dos[i].sectors; + return 0; + } + } + return 1; +} + + +static void calc_fs_parameters(struct device *dev, unsigned int tot_sectors, + struct Fs_t *Fs, struct bootsector *boot) +{ + int i; + + for(i=0; i < sizeof(old_dos) / sizeof(old_dos[0]); i++){ + if (dev->sectors == old_dos[i].sectors && + dev->tracks == old_dos[i].tracks && + dev->heads == old_dos[i].heads && + (dev->fat_bits == 0 || abs(dev->fat_bits) == 12)){ + boot->descr = old_dos[i].media; + Fs->cluster_size = old_dos[i].cluster_size; + Fs->dir_len = old_dos[i].dir_len; + Fs->fat_len = old_dos[i].fat_len; + Fs->fat_bits = 12; + break; + } + } + if (i == sizeof(old_dos) / sizeof(old_dos[0]) ){ + /* a non-standard format */ + if(DWORD(nhs)) + boot->descr = 0xf8; + else + boot->descr = 0xf0; + + + if(!Fs->cluster_size) { + if (dev->heads == 1) + Fs->cluster_size = 1; + else { + Fs->cluster_size = (tot_sectors > 2000 ) ? 1:2; + if (dev->use_2m & 0x7f) + Fs->cluster_size = 1; + } + } + + if(!Fs->dir_len) { + if (dev->heads == 1) + Fs->dir_len = 4; + else + Fs->dir_len = (tot_sectors > 2000) ? 11 : 7; + } + + calc_cluster_size(Fs, tot_sectors, dev->fat_bits); + if(Fs->fat_len) + xdf_calc_fat_size(Fs, tot_sectors, dev->fat_bits); + else { + calc_fat_bits2(Fs, tot_sectors, dev->fat_bits); + calc_fat_size(Fs, tot_sectors); + } + } + + set_word(boot->fatlen, Fs->fat_len); +} + + + +static void calc_fs_parameters_32(unsigned int tot_sectors, + struct Fs_t *Fs, struct bootsector *boot) +{ + if(DWORD(nhs)) + boot->descr = 0xf8; + else + boot->descr = 0xf0; + if(!Fs->cluster_size) + /* According to + * http://www.microsoft.com/kb/articles/q154/9/97.htm, + * Micro$oft does not support FAT32 with less than 4K + */ + Fs->cluster_size = 8; + + Fs->dir_len = 0; + Fs->num_clus = tot_sectors / Fs->cluster_size; + set_fat32(Fs); + calc_fat_size(Fs, tot_sectors); + set_word(boot->fatlen, 0); + set_dword(boot->ext.fat32.bigFat, Fs->fat_len); +} + + + + +static void usage(void) +{ + fprintf(stderr, + "Mtools version %s, dated %s\n", mversion, mdate); + fprintf(stderr, + "Usage: %s [-t tracks] [-h heads] [-n sectors] " + "[-v label] [-1] [-4] [-8] [-f size] " + "[-N serialnumber] " + "[-k] [-B bootsector] [-r root_dir_len] [-L fat_len] " + "[-F] [-I fsVersion] [-C] [-c cluster_size] " + "[-H hidden_sectors] " +#ifdef USE_XDF + "[-X] " +#endif + "[-S hardsectorsize] [-M softsectorsize] [-3] " + "[-2 track0sectors] [-0 rate0] [-A rateany] [-a]" + "device\n", progname); + exit(1); +} + +void mformat(int argc, char **argv, int dummy) +{ + int r; /* generic return value */ + Fs_t Fs; + int hs, hs_set; + int arguse_2m = 0; + int sectors0=18; /* number of sectors on track 0 */ + int create = 0; + int rate_0, rate_any; + int mangled; + int argssize=0; /* sector size */ + int msize=0; + int fat32 = 0; + struct label_blk_t *labelBlock; + int bootOffset; + +#ifdef USE_XDF + int i; + int format_xdf = 0; + struct xdf_info info; +#endif + struct bootsector *boot; + char *bootSector=0; + int c; + int keepBoot = 0; + struct device used_dev; + int argtracks, argheads, argsectors; + int tot_sectors; + int blocksize; + + char *drive, name[EXPAND_BUF]; + + char label[VBUFSIZE], buf[MAX_SECTOR], shortlabel[13]; + struct device *dev; + char errmsg[200]; + + unsigned long serial; + int serial_set; + int fsVersion; + + mt_off_t maxSize; + + int Atari = 0; /* should we add an Atari-style serial number ? */ +#ifdef OS_Minix + char *devname; + struct device onedevice[2]; + struct stat stbuf; +#endif + + hs = hs_set = 0; + argtracks = 0; + argheads = 0; + argsectors = 0; + arguse_2m = 0; + argssize = 0x2; + label[0] = '\0'; + serial_set = 0; + serial = 0; + fsVersion = 0; + + Fs.cluster_size = 0; + Fs.refs = 1; + Fs.dir_len = 0; + Fs.fat_len = 0; + Fs.Class = &FsClass; + rate_0 = mtools_rate_0; + rate_any = mtools_rate_any; + + /* get command line options */ + while ((c = getopt(argc,argv, + "148f:t:n:v:qub" + "kB:r:L:IFCc:Xh:s:l:N:H:M:S:230:Aa"))!= EOF) { + switch (c) { + /* standard DOS flags */ + case '1': + argheads = 1; + break; + case '4': + argsectors = 9; + argtracks = 40; + break; + case '8': + argsectors = 8; + argtracks = 40; + break; + case 'f': + r=old_dos_size_to_geom(atoi(optarg), + &argtracks, &argheads, + &argsectors); + if(r) { + fprintf(stderr, + "Bad size %s\n", optarg); + exit(1); + } + break; + case 't': + argtracks = atoi(optarg); + break; + + case 'n': /*non-standard*/ + case 's': + argsectors = atoi(optarg); + break; + + case 'l': /* non-standard */ + case 'v': + strncpy(label, optarg, VBUFSIZE-1); + label[VBUFSIZE-1] = '\0'; + break; + + /* flags supported by Dos but not mtools */ + case 'q': + case 'u': + case 'b': + /*case 's': leave this for compatibility */ + fprintf(stderr, + "Flag %c not supported by mtools\n",c); + exit(1); + + + + /* flags added by mtools */ + case 'F': + fat32 = 1; + break; + + + case 'S': + argssize = atoi(optarg) | 0x80; + if(argssize < 0x81) + usage(); + break; + +#ifdef USE_XDF + case 'X': + format_xdf = 1; + break; +#endif + + case '2': + arguse_2m = 0xff; + sectors0 = atoi(optarg); + break; + case '3': + arguse_2m = 0x80; + break; + + case '0': /* rate on track 0 */ + rate_0 = atoi(optarg); + break; + case 'A': /* rate on other tracks */ + rate_any = atoi(optarg); + break; + + case 'M': + msize = atoi(optarg); + if (msize % 256 || msize > 8192 ) + usage(); + break; + + case 'N': + serial = strtoul(optarg,0,16); + serial_set = 1; + break; + case 'a': /* Atari style serial number */ + Atari = 1; + break; + + case 'C': + create = O_CREAT; + break; + + case 'H': + hs = atoi(optarg); + hs_set = 1; + break; + + case 'I': + fsVersion = strtoul(optarg,0,0); + break; + + case 'c': + Fs.cluster_size = atoi(optarg); + break; + + case 'r': + Fs.dir_len = strtoul(optarg,0,0); + break; + case 'L': + Fs.fat_len = strtoul(optarg,0,0); + break; + + + case 'B': + bootSector = optarg; + break; + case 'k': + keepBoot = 1; + break; + case 'h': + argheads = atoi(optarg); + break; + + default: + usage(); + } + } + + if (argc - optind != 1 || + skip_drive(argv[optind]) == argv[optind]) + usage(); + +#ifdef USE_XDF + if(create && format_xdf) { + fprintf(stderr,"Create and XDF can't be used together\n"); + exit(1); + } +#endif + + drive = get_drive(argv[argc -1], NULL); + +#ifdef OS_Minix + devname = safe_malloc((9 + strlen(drive)) * sizeof(devname[0])); + strcpy(devname, "/dev/dosX"); + if (isupper(drive[0]) && drive[1] == 0) { + /* single letter device name, use /dev/dos$drive */ + devname[8]= drive[0]; + } else + if (strchr(drive, '/') == NULL) { + /* a simple name, use /dev/$drive */ + strcpy(devname+5, drive); + } else { + /* a pathname, use as is. */ + strcpy(devname, drive); + } + if (stat(devname, &stbuf) != -1) { + memset(onedevice, 0, sizeof(onedevice)); + onedevice[0].name = devname; + onedevice[0].drive = drive; + onedevice[1].name = NULL; + onedevice[1].drive = NULL; + dev = onedevice; + } else { + dev = devices; + } +#else + dev = devices; +#endif + + /* check out a drive whose letter and parameters match */ + sprintf(errmsg, "Drive '%s:' not supported", drive); + Fs.Direct = NULL; + blocksize = 0; + for(;dev->drive;dev++) { + FREE(&(Fs.Direct)); + /* drive name */ + if (strcmp(dev->drive, drive) != 0) + continue; + used_dev = *dev; + + SET_INT(used_dev.tracks, argtracks); + SET_INT(used_dev.heads, argheads); + SET_INT(used_dev.sectors, argsectors); + SET_INT(used_dev.use_2m, arguse_2m); + SET_INT(used_dev.ssize, argssize); + if(hs_set) + used_dev.hidden = hs; + + expand(dev->name, name); +#ifdef USING_NEW_VOLD + strcpy(name, getVoldName(dev, name)); +#endif + +#ifdef USE_XDF + if(!format_xdf) { +#endif + Fs.Direct = 0; +#ifdef USE_FLOPPYD + Fs.Direct = FloppydOpen(&used_dev, dev, name, O_RDWR | create, + errmsg, 0, 1); + if(Fs.Direct) { + maxSize = max_off_t_31; + } +#endif + if(!Fs.Direct) { + Fs.Direct = SimpleFileOpen(&used_dev, dev, name, + O_RDWR | create, + errmsg, 0, 1, &maxSize); + } +#ifdef USE_XDF + } else { + used_dev.misc_flags |= USE_XDF_FLAG; + Fs.Direct = XdfOpen(&used_dev, name, O_RDWR, + errmsg, &info); + if(Fs.Direct && !Fs.fat_len) + Fs.fat_len = info.FatSize; + if(Fs.Direct && !Fs.dir_len) + Fs.dir_len = info.RootDirSize; + } +#endif + + if (!Fs.Direct) + continue; + +#ifdef OS_linux + if ((!used_dev.tracks || !used_dev.heads || !used_dev.sectors) && + (!IS_SCSI(dev))) { + int fd= get_fd(Fs.Direct); + struct stat buf; + + if (fstat(fd, &buf) < 0) { + sprintf(errmsg, "Could not stat file (%s)", strerror(errno)); + continue; + } + + if (S_ISBLK(buf.st_mode)) { + struct hd_geometry geom; + long size; + int sect_per_track; + + if (ioctl(fd, HDIO_GETGEO, &geom) < 0) { + sprintf(errmsg, "Could not get geometry of device (%s)", + strerror(errno)); + continue; + } + + if (ioctl(fd, BLKGETSIZE, &size) < 0) { + sprintf(errmsg, "Could not get size of device (%s)", + strerror(errno)); + continue; + } + + sect_per_track = geom.heads * geom.sectors; + used_dev.heads = geom.heads; + used_dev.sectors = geom.sectors; + used_dev.hidden = geom.start % sect_per_track; + used_dev.tracks = (size + used_dev.hidden) / sect_per_track; + } + } +#endif + + /* no way to find out geometry */ + if (!used_dev.tracks || !used_dev.heads || !used_dev.sectors){ + sprintf(errmsg, + "Unknown geometry " + "(You must tell the complete geometry " + "of the disk, \neither in /etc/mtools.conf or " + "on the command line) "); + continue; + } + +#if 0 + /* set parameters, if needed */ + if(SET_GEOM(Fs.Direct, &used_dev, 0xf0, boot)){ + sprintf(errmsg,"Can't set disk parameters: %s", + strerror(errno)); + continue; + } +#endif + Fs.sector_size = 512; + if( !(used_dev.use_2m & 0x7f)) { + Fs.sector_size = 128 << (used_dev.ssize & 0x7f); + } + + SET_INT(Fs.sector_size, msize); + { + int i; + for(i = 0; i < 31; i++) { + if (Fs.sector_size == 1 << i) { + Fs.sectorShift = i; + break; + } + } + Fs.sectorMask = Fs.sector_size - 1; + } + + if(!used_dev.blocksize || used_dev.blocksize < Fs.sector_size) + blocksize = Fs.sector_size; + else + blocksize = used_dev.blocksize; + + if(blocksize > MAX_SECTOR) + blocksize = MAX_SECTOR; + + /* do a "test" read */ + if (!create && + READS(Fs.Direct, (char *) buf, 0, Fs.sector_size) != + Fs.sector_size) { + sprintf(errmsg, + "Error reading from '%s', wrong parameters?", + name); + continue; + } + break; + } + + + /* print error msg if needed */ + if ( dev->drive == 0 ){ + FREE(&Fs.Direct); + fprintf(stderr,"%s: %s\n", argv[0],errmsg); + exit(1); + } + + /* the boot sector */ + boot = (struct bootsector *) buf; + if(bootSector) { + int fd; + + fd = open(bootSector, O_RDONLY); + if(fd < 0) { + perror("open boot sector"); + exit(1); + } + read(fd, buf, blocksize); + keepBoot = 1; + } + if(!keepBoot) { + memset((char *)boot, '\0', Fs.sector_size); + if(Fs.sector_size == 512 && !used_dev.partition) { + /* install fake partition table pointing to itself */ + struct partition *partTable=(struct partition *) + (((char*) boot) + 0x1ae); + setBeginEnd(&partTable[1], 0, + used_dev.heads * used_dev.sectors * used_dev.tracks, + used_dev.heads, used_dev.sectors, 1, 0); + } + } + set_dword(boot->nhs, used_dev.hidden); + + Fs.Next = buf_init(Fs.Direct, + blocksize * used_dev.heads * used_dev.sectors, + blocksize * used_dev.heads * used_dev.sectors, + blocksize); + Fs.Buffer = 0; + + boot->nfat = Fs.num_fat = 2; + if(!keepBoot) + set_word(boot->jump + 510, 0xaa55); + + /* get the parameters */ + tot_sectors = used_dev.tracks * used_dev.heads * used_dev.sectors - + DWORD(nhs); + + set_word(boot->nsect, dev->sectors); + set_word(boot->nheads, dev->heads); + + dev->fat_bits = comp_fat_bits(&Fs,dev->fat_bits, tot_sectors, fat32); + + if(dev->fat_bits == 32) { + Fs.primaryFat = 0; + Fs.writeAllFats = 1; + Fs.fat_start = 32; + calc_fs_parameters_32(tot_sectors, &Fs, boot); + + Fs.clus_start = Fs.num_fat * Fs.fat_len + Fs.fat_start; + + /* extension flags: mirror fats, and use #0 as primary */ + set_word(boot->ext.fat32.extFlags,0); + + /* fs version. What should go here? */ + set_word(boot->ext.fat32.fsVersion,fsVersion); + + /* root directory */ + set_dword(boot->ext.fat32.rootCluster, Fs.rootCluster = 2); + + /* info sector */ + set_word(boot->ext.fat32.infoSector, Fs.infoSectorLoc = 1); + Fs.infoSectorLoc = 1; + + /* no backup boot sector */ + set_word(boot->ext.fat32.backupBoot, 6); + + labelBlock = & boot->ext.fat32.labelBlock; + } else { + Fs.infoSectorLoc = 0; + Fs.fat_start = 1; + calc_fs_parameters(&used_dev, tot_sectors, &Fs, boot); + Fs.dir_start = Fs.num_fat * Fs.fat_len + Fs.fat_start; + Fs.clus_start = Fs.dir_start + Fs.dir_len; + labelBlock = & boot->ext.old.labelBlock; + + } + + if (!keepBoot) + /* only zero out physdrive if we don't have a template + * bootsector */ + labelBlock->physdrive = 0x00; + labelBlock->reserved = 0; + labelBlock->dos4 = 0x29; + + if (!serial_set || Atari) + srandom((long)time (0)); + if (!serial_set) + serial=random(); + set_dword(labelBlock->serial, serial); + if(!label[0]) + strncpy(shortlabel, "NO NAME ",11); + else + label_name(label, 0, &mangled, shortlabel); + strncpy(labelBlock->label, shortlabel, 11); + sprintf(labelBlock->fat_type, "FAT%2.2d ", Fs.fat_bits); + labelBlock->fat_type[7] = ' '; + + set_word(boot->secsiz, Fs.sector_size); + boot->clsiz = (unsigned char) Fs.cluster_size; + set_word(boot->nrsvsect, Fs.fat_start); + + bootOffset = init_geometry_boot(boot, &used_dev, sectors0, + rate_0, rate_any, + &tot_sectors, keepBoot); + if(!bootOffset) { + bootOffset = ((char *) labelBlock) - ((char *) boot) + + sizeof(struct label_blk_t); + } + if(Atari) { + boot->banner[4] = 0; + boot->banner[5] = random(); + boot->banner[6] = random(); + boot->banner[7] = random(); + } + + if (create) { + WRITES(Fs.Direct, (char *) buf, + sectorsToBytes((Stream_t*)&Fs, tot_sectors-1), + Fs.sector_size); + } + + if(!keepBoot) + inst_boot_prg(boot, bootOffset); + if(dev->use_2m & 0x7f) + Fs.num_fat = 1; + Fs.lastFatSectorNr = 0; + Fs.lastFatSectorData = 0; + zero_fat(&Fs, boot->descr); + Fs.freeSpace = Fs.num_clus; + Fs.last = 2; + +#ifdef USE_XDF + if(format_xdf) + for(i=0; + i < (info.BadSectors+Fs.cluster_size-1)/Fs.cluster_size; + i++) + fatEncode(&Fs, i+2, 0xfff7); +#endif + + format_root(&Fs, label, boot); + WRITES((Stream_t *)&Fs, (char *) boot, (mt_off_t) 0, Fs.sector_size); + if(Fs.fat_bits == 32 && WORD(ext.fat32.backupBoot) != MAX32) { + WRITES((Stream_t *)&Fs, (char *) boot, + sectorsToBytes((Stream_t*)&Fs, WORD(ext.fat32.backupBoot)), + Fs.sector_size); + } + FLUSH((Stream_t *)&Fs); /* flushes Fs. + * This triggers the writing of the FAT */ + FREE(&Fs.Next); + Fs.Class->freeFunc((Stream_t *)&Fs); +#ifdef USE_XDF + if(format_xdf && isatty(0) && !getenv("MTOOLS_USE_XDF")) + fprintf(stderr, + "Note:\n" + "Remember to set the \"MTOOLS_USE_XDF\" environmental\n" + "variable before accessing this disk\n\n" + "Bourne shell syntax (sh, ash, bash, ksh, zsh etc):\n" + " export MTOOLS_USE_XDF=1\n\n" + "C shell syntax (csh and tcsh):\n" + " setenv MTOOLS_USE_XDF 1\n" ); +#endif + exit(0); +} diff --git a/commands/i386/mtools-3.9.7/minfo.c b/commands/i386/mtools-3.9.7/minfo.c new file mode 100755 index 000000000..96d86ab04 --- /dev/null +++ b/commands/i386/mtools-3.9.7/minfo.c @@ -0,0 +1,172 @@ +/* + * mlabel.c + * Make an MSDOS volume label + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mainloop.h" +#include "vfat.h" +#include "mtools.h" +#include "nameclash.h" + +static void usage(void) +{ + fprintf(stderr, + "Mtools version %s, dated %s\n", mversion, mdate); + fprintf(stderr, + "Usage: %s [-v] drive\n\t-v Verbose\n", progname); + exit(1); +} + + +static void displayInfosector(Stream_t *Stream, struct bootsector *boot) +{ + InfoSector_t *infosec; + + if(WORD(ext.fat32.infoSector) == MAX32) + return; + + infosec = (InfoSector_t *) safe_malloc(WORD(secsiz)); + force_read(Stream, (char *) infosec, + (mt_off_t) WORD(secsiz) * WORD(ext.fat32.infoSector), + WORD(secsiz)); + printf("\nInfosector:\n"); + printf("signature=0x%08x\n", _DWORD(infosec->signature1)); + if(_DWORD(infosec->count) != MAX32) + printf("free clusters=%u\n", _DWORD(infosec->count)); + if(_DWORD(infosec->pos) != MAX32) + printf("last allocated cluster=%u\n", _DWORD(infosec->pos)); +} + + +void minfo(int argc, char **argv, int type) +{ + struct bootsector boot0; +#define boot (&boot0) + char name[EXPAND_BUF]; + int media; + int tot_sectors; + struct device dev; + char *drive; + int verbose=0; + int c; + Stream_t *Stream; + struct label_blk_t *labelBlock; + + while ((c = getopt(argc, argv, "v")) != EOF) { + switch (c) { + case 'v': + verbose = 1; + break; + default: + usage(); + } + } + + if(argc == optind) + usage(); + + for(;optind < argc; optind++) { + if(skip_drive(argv[optind]) == argv[optind]) + usage(); + drive = get_drive(argv[optind], NULL); + + if(! (Stream = find_device(drive, O_RDONLY, &dev, boot, + name, &media, 0))) + exit(1); + + tot_sectors = DWORD(bigsect); + SET_INT(tot_sectors, WORD(psect)); + printf("device information:\n"); + printf("===================\n"); + printf("filename=\"%s\"\n", name); + printf("sectors per track: %d\n", dev.sectors); + printf("heads: %d\n", dev.heads); + printf("cylinders: %d\n\n", dev.tracks); + printf("mformat command line: mformat -t %d -h %d -s %d ", + dev.tracks, dev.heads, dev.sectors); + if(DWORD(nhs)) + printf("-H %d ", DWORD(nhs)); + printf("%s:\n", drive); + printf("\n"); + + printf("bootsector information\n"); + printf("======================\n"); + printf("banner:\"%8s\"\n", boot->banner); + printf("sector size: %d bytes\n", WORD(secsiz)); + printf("cluster size: %d sectors\n", boot->clsiz); + printf("reserved (boot) sectors: %d\n", WORD(nrsvsect)); + printf("fats: %d\n", boot->nfat); + printf("max available root directory slots: %d\n", + WORD(dirents)); + printf("small size: %d sectors\n", WORD(psect)); + printf("media descriptor byte: 0x%x\n", boot->descr); + printf("sectors per fat: %d\n", WORD(fatlen)); + printf("sectors per track: %d\n", WORD(nsect)); + printf("heads: %d\n", WORD(nheads)); + printf("hidden sectors: %d\n", DWORD(nhs)); + printf("big size: %d sectors\n", DWORD(bigsect)); + + if(WORD(fatlen)) { + labelBlock = &boot->ext.old.labelBlock; + } else { + labelBlock = &boot->ext.fat32.labelBlock; + } + + printf("physical drive id: 0x%x\n", + labelBlock->physdrive); + printf("reserved=0x%x\n", + labelBlock->reserved); + printf("dos4=0x%x\n", + labelBlock->dos4); + printf("serial number: %08X\n", + _DWORD(labelBlock->serial)); + printf("disk label=\"%11.11s\"\n", + labelBlock->label); + printf("disk type=\"%8.8s\"\n", + labelBlock->fat_type); + + if(!WORD(fatlen)){ + printf("Big fatlen=%u\n", + DWORD(ext.fat32.bigFat)); + printf("Extended flags=0x%04x\n", + WORD(ext.fat32.extFlags)); + printf("FS version=0x%04x\n", + WORD(ext.fat32.fsVersion)); + printf("rootCluster=%u\n", + DWORD(ext.fat32.rootCluster)); + if(WORD(ext.fat32.infoSector) != MAX32) + printf("infoSector location=%d\n", + WORD(ext.fat32.infoSector)); + if(WORD(ext.fat32.backupBoot) != MAX32) + printf("backup boot sector=%d\n", + WORD(ext.fat32.backupBoot)); + displayInfosector(Stream,boot); + } + + if(verbose) { + int size; + unsigned char *buf; + + printf("\n"); + size = WORD(secsiz); + + buf = (unsigned char *) malloc(size); + if(!buf) { + fprintf(stderr, "Out of memory error\n"); + exit(1); + } + + size = READS(Stream, buf, (mt_off_t) 0, size); + if(size < 0) { + perror("read boot sector"); + exit(1); + } + + print_sector("Boot sector hexdump", buf, size); + } + } + + exit(0); +} diff --git a/commands/i386/mtools-3.9.7/misc.c b/commands/i386/mtools-3.9.7/misc.c new file mode 100755 index 000000000..acfa22dbc --- /dev/null +++ b/commands/i386/mtools-3.9.7/misc.c @@ -0,0 +1,307 @@ +/* + * Miscellaneous routines. + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "stream.h" +#include "vfat.h" +#include "mtools.h" + + +void printOom(void) +{ + fprintf(stderr, "Out of memory error"); +} + +char *get_homedir(void) +{ + struct passwd *pw; + uid_t uid; + char *homedir; + char *username; + + homedir = getenv ("HOME"); + /* + * first we call getlogin. + * There might be several accounts sharing one uid + */ + if ( homedir ) + return homedir; + + pw = 0; + + username = getenv("LOGNAME"); + if ( !username ) + username = getlogin(); + if ( username ) + pw = getpwnam( username); + + if ( pw == 0 ){ + /* if we can't getlogin, look up the pwent by uid */ + uid = geteuid(); + pw = getpwuid(uid); + } + + /* we might still get no entry */ + if ( pw ) + return pw->pw_dir; + return 0; +} + + +static void get_mcwd_file_name(char *file) +{ + char *mcwd_path; + char *homedir; + + mcwd_path = getenv("MCWD"); + if (mcwd_path == NULL || *mcwd_path == '\0'){ + homedir= get_homedir(); + if(!homedir) + homedir="/tmp"; + strncpy(file, homedir, MAXPATHLEN-6); + file[MAXPATHLEN-6]='\0'; + strcat( file, "/.mcwd"); + } else { + strncpy(file, mcwd_path, MAXPATHLEN); + file[MAXPATHLEN]='\0'; + } +} + +void unlink_mcwd() +{ + char file[MAXPATHLEN+1]; + get_mcwd_file_name(file); + unlink(file); +} + +FILE *open_mcwd(const char *mode) +{ + struct stat sbuf; + char file[MAXPATHLEN+1]; + time_t now; + + get_mcwd_file_name(file); + if (*mode == 'r'){ + if (stat(file, &sbuf) < 0) + return NULL; + /* + * Ignore the info, if the file is more than 6 hours old + */ + getTimeNow(&now); + if (now - sbuf.st_mtime > 6 * 60 * 60) { + fprintf(stderr, + "Warning: \"%s\" is out of date, removing it\n", + file); + unlink(file); + return NULL; + } + } + + return fopen(file, mode); +} + + +/* Fix the info in the MCWD file to be a proper directory name. + * Always has a leading separator. Never has a trailing separator + * (unless it is the path itself). */ + +const char *fix_mcwd(char *ans) +{ + FILE *fp; + char *s; + char buf[MAX_PATH]; + + fp = open_mcwd("r"); + if(!fp){ + strcpy(ans, "A:/"); + return ans; + } + + if (!fgets(buf, MAX_PATH, fp)) + return("A:/"); + + buf[strlen(buf) -1] = '\0'; + fclose(fp); + /* drive letter present? */ + s = skip_drive(buf); + if (s > buf) { + strncpy(ans, buf, s - buf); + ans[s - buf] = '\0'; + } else + strcpy(ans, "A:"); + /* add a leading separator */ + if (*s != '/' && *s != '\\') { + strcat(ans, "/"); + strcat(ans, s); + } else + strcat(ans, s); + +#if 0 + /* translate to upper case */ + for (s = ans; *s; ++s) { + *s = toupper(*s); + if (*s == '\\') + *s = '/'; + } +#endif + /* if only drive, colon, & separator */ + if (strlen(ans) == 3) + return(ans); + /* zap the trailing separator */ + if (*--s == '/') + *s = '\0'; + return ans; +} + +void *safe_malloc(size_t size) +{ + void *p; + + p = malloc(size); + if(!p){ + printOom(); + exit(1); + } + return p; +} + +void print_sector(char *message, unsigned char *data, int size) +{ + int col; + int row; + + printf("%s:\n", message); + + for(row = 0; row * 16 < size; row++){ + printf("%03x ", row * 16); + for(col = 0; col < 16; col++) + printf("%02x ", data [row*16+col]); + for(col = 0; col < 16; col++) { + if(isprint(data [row*16+col])) + printf("%c", data [row*16+col]); + else + printf("."); + } + printf("\n"); + } +} + + +time_t getTimeNow(time_t *now) +{ + static int haveTime = 0; + static time_t sharedNow; + + if(!haveTime) { + time(&sharedNow); + haveTime = 1; + } + if(now) + *now = sharedNow; + return sharedNow; +} + +char *skip_drive(const char *filename) +{ + char *p; + + /* Skip drive name. Return pointer just after the `:', or a pointer + * to the start of the file name if there is is no drive name. + */ + p = strchr(filename, ':'); + return (p == NULL || p == filename) ? (char *) filename : p + 1; +} + +char *get_drive(const char *filename, const char *def) +{ + const char *path; + char *drive; + const char *rest; + size_t len; + + /* Return the drive name part of a full filename. */ + + path = filename; + rest = skip_drive(path); + if (rest == path) { + if (def == NULL) def = "A:"; + path = def; + rest = skip_drive(path); + if (rest == path) { + path = "A:"; + rest = path+2; + } + } + len = rest - path; + drive = safe_malloc(len * sizeof(drive[0])); + len--; + memcpy(drive, path, len); + drive[len] = 0; + if (len == 1) drive[0] = toupper(drive[0]); + return drive; +} + +#if 0 + +#undef free +#undef malloc + +static int total=0; + +void myfree(void *ptr) +{ + int *size = ((int *) ptr)-1; + total -= *size; + fprintf(stderr, "freeing %d bytes at %p total alloced=%d\n", + *size, ptr, total); + free(size); +} + +void *mymalloc(size_t size) +{ + int *ptr; + ptr = (int *)malloc(size+sizeof(int)); + if(!ptr) + return 0; + *ptr = size; + ptr++; + total += size; + fprintf(stderr, "allocating %d bytes at %p total allocated=%d\n", + size, ptr, total); + return (void *) ptr; +} + +void *mycalloc(size_t nmemb, size_t size) +{ + void *ptr = mymalloc(nmemb * size); + if(!ptr) + return 0; + memset(ptr, 0, size); + return ptr; +} + +void *myrealloc(void *ptr, size_t size) +{ + int oldsize = ((int *)ptr) [-1]; + void *new = mymalloc(size); + if(!new) + return 0; + memcpy(new, ptr, oldsize); + myfree(ptr); + return new; +} + +char *mystrdup(char *src) +{ + char *dest; + dest = mymalloc(strlen(src)+1); + if(!dest) + return 0; + strcpy(dest, src); + return dest; +} + + +#endif diff --git a/commands/i386/mtools-3.9.7/missFuncs.c b/commands/i386/mtools-3.9.7/missFuncs.c new file mode 100755 index 000000000..c6934120f --- /dev/null +++ b/commands/i386/mtools-3.9.7/missFuncs.c @@ -0,0 +1,386 @@ +/* Copyright (C) 1991 Free Software Foundation, Inc. +This file contains excerpts of the GNU C Library. + +The GNU C Library is free software; you can redistribute it and/or +modify it under the terms of the GNU Library General Public License as +published by the Free Software Foundation; either version 2 of the +License, or (at your option) any later version. + +The GNU C Library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Library General Public License for more details. + +You should have received a copy of the GNU Library General Public +License along with the GNU C Library; see the file COPYING.LIB. If +not, write to the Free Software Foundation, Inc., 675 Mass Ave, +Cambridge, MA 02139, USA. */ + +#include "sysincludes.h" +#include "mtools.h" + +#ifndef HAVE_STRDUP + + +char *strdup(const char *str) +{ + char *nstr; + + if (str == (char*)0) + return 0; + + nstr = (char*)malloc((strlen(str) + 1)); + + if (nstr == (char*)0) + { + (void)fprintf(stderr, "strdup(): not enough memory to duplicate `%s'\n", + str); + exit(1); + } + + (void)strcpy(nstr, str); + + return nstr; +} +#endif /* HAVE_STRDUP */ + + +#ifndef HAVE_MEMCPY +/* + * Copy contents of memory (with possible overlapping). + */ +char *memcpy(char *s1, const char *s2, size_t n) +{ + bcopy(s2, s1, n); + return(s1); +} +#endif + +#ifndef HAVE_MEMSET +/* + * Copies the character c, n times to string s + */ +char *memset(char *s, char c, size_t n) +{ + char *s1 = s; + + while (n > 0) { + --n; + *s++ = c; + } + return(s1); +} +#endif /* HAVE_MEMSET */ + + +#ifndef HAVE_STRCHR + +char * strchr (const char* s, int c) +{ + if (!s) return NULL; + while (*s && *s != c) s++; + if (*s) + return (char*) s; + else + return NULL; +} + +#endif + +#ifndef HAVE_STRRCHR + +char * strrchr (const char* s1, int c) +{ + char* s = (char*) s1; + char* start = (char*) s; + if (!s) return NULL; + s += strlen(s)-1; + while (*s != c && (unsigned long) s != (unsigned long) start) s--; + if ((unsigned long) s == (unsigned long) start && *s != c) + return NULL; + else + return s; +} + +#endif + +#ifndef HAVE_STRPBRK +/* + * Return ptr to first occurrence of any character from `brkset' + * in the character string `string'; NULL if none exists. + */ +char *strpbrk(const char *string, const char *brkset) +{ + register char *p; + + if (!string || !brkset) + return(0); + do { + for (p = brkset; *p != '\0' && *p != *string; ++p) + ; + if (*p != '\0') + return(string); + } + while (*string++); + return(0); +} +#endif /* HAVE_STRPBRK */ + + +#ifndef HAVE_STRTOUL +static int getdigit(char a, int max) +{ + int dig; + + if(a < '0') + return -1; + if(a <= '9') { + dig = a - '0'; + } else if(a >= 'a') + dig = a - 'a' + 10; + else if(a >= 'A') + dig = a - 'A' + 10; + if(dig >= max) + return -1; + else + return dig; +} + +unsigned long strtoul(const char *string, char **eptr, int base) +{ + int accu, dig; + + if(base < 1 || base > 36) { + if(string[0] == '0') { + switch(string[1]) { + case 'x': + case 'X': + return strtoul(string+2, eptr, 16); + case 'b': + case 'B': + return strtoul(string+2, eptr, 2); + default: + return strtoul(string, eptr, 8); + } + } + return strtoul(string, eptr, 10); + } + if(base == 16 && string[0] == '0' && + (string[1] == 'x' || string[1] == 'X')) + string += 2; + + if(base == 2 && string[0] == '0' && + (string[1] == 'b' || string[1] == 'B')) + string += 2; + accu = 0; + while( (dig = getdigit(*string, base)) != -1 ) { + accu = accu * base + dig; + string++; + } + if(eptr) + *eptr = (char *) string; + return accu; +} +#endif /* HAVE_STRTOUL */ + +#ifndef HAVE_STRTOL +long strtol(const char *string, char **eptr, int base) +{ + long l; + + if(*string == '-') { + return -(long) strtoul(string+1, eptr, base); + } else { + if (*string == '+') + string ++; + return (long) strtoul(string, eptr, base); + } +} +#endif + + + +#ifndef HAVE_STRSPN +/* Return the length of the maximum initial segment + of S which contains only characters in ACCEPT. */ +size_t strspn(const char *s, const char *accept) +{ + register char *p; + register char *a; + register size_t count = 0; + + for (p = s; *p != '\0'; ++p) + { + for (a = accept; *a != '\0'; ++a) + if (*p == *a) + break; + if (*a == '\0') + return count; + else + ++count; + } + + return count; +} +#endif /* HAVE_STRSPN */ + +#ifndef HAVE_STRCSPN +/* Return the length of the maximum inital segment of S + which contains no characters from REJECT. */ +size_t strcspn (const char *s, const char *reject) +{ + register size_t count = 0; + + while (*s != '\0') + if (strchr (reject, *s++) == NULL) + ++count; + else + return count; + + return count; +} + +#endif /* HAVE_STRCSPN */ + +#ifndef HAVE_STRERROR + +#ifndef DECL_SYS_ERRLIST +extern char *sys_errlist[]; +#endif + +char *strerror(int errno) +{ + return sys_errlist[errno]; +} +#endif + +#ifndef HAVE_STRCASECMP +/* Compare S1 and S2, ignoring case, returning less than, equal to or + greater than zero if S1 is lexiographically less than, + equal to or greater than S2. */ +int strcasecmp(const char *s1, const char *s2) +{ + register const unsigned char *p1 = (const unsigned char *) s1; + register const unsigned char *p2 = (const unsigned char *) s2; + unsigned char c1, c2; + + if (p1 == p2) + return 0; + + do + { + c1 = tolower (*p1++); + c2 = tolower (*p2++); + if (c1 == '\0') + break; + } + while (c1 == c2); + + return c1 - c2; +} +#endif + + + +#ifndef HAVE_STRCASECMP +/* Compare S1 and S2, ignoring case, returning less than, equal to or + greater than zero if S1 is lexiographically less than, + equal to or greater than S2. */ +int strncasecmp(const char *s1, const char *s2, size_t n) +{ + register const unsigned char *p1 = (const unsigned char *) s1; + register const unsigned char *p2 = (const unsigned char *) s2; + unsigned char c1, c2; + + if (p1 == p2) + return 0; + + c1 = c2 = 1; + while (c1 && c1 == c2 && n-- > 0) + { + c1 = tolower (*p1++); + c2 = tolower (*p2++); + } + + return c1 - c2; +} +#endif + +#ifndef HAVE_GETPASS +char *getpass(const char *prompt) +{ + static char password[129]; + int l; + + fprintf(stderr,"%s",prompt); + fgets(password, 128, stdin); + l = strlen(password); + if(l && password[l-1] == '\n') + password[l-1] = '\0'; + return password; + +} +#endif + +#ifndef HAVE_ATEXIT + +#ifdef HAVE_ON_EXIT +int atexit(void (*function)(void)) +{ + return on_exit( (void(*)(int,void*)) function, 0); +} +#else + +typedef struct exitCallback { + void (*function) (void); + struct exitCallback *next; +} exitCallback_t; + +static exitCallback_t *callback = 0; + +int atexit(void (*function) (void)) +{ + exitCallback_t *newCallback; + + newCallback = New(exitCallback_t); + if(!newCallback) { + printOom(); + exit(1); + } + newCallback->function = function; + newCallback->next = callback; + callback = newCallback; + return 0; +} +#undef exit + +void myexit(int code) +{ + void (*function)(void); + + while(callback) { + function = callback->function; + callback = callback->next; + function(); + } + exit(code); +} + +#endif + +#endif + +/*#ifndef HAVE_BASENAME*/ +const char *_basename(const char *filename) +{ + char *ptr; + + ptr = strrchr(filename, '/'); + if(ptr) + return ptr+1; + else + return filename; +} +/*#endif*/ + + diff --git a/commands/i386/mtools-3.9.7/mk_direntry.c b/commands/i386/mtools-3.9.7/mk_direntry.c new file mode 100755 index 000000000..28b5ced06 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mk_direntry.c @@ -0,0 +1,618 @@ +/* + * mk_direntry.c + * Make new directory entries, and handles name clashes + * + */ + +/* + * This file is used by those commands that need to create new directory entries + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "vfat.h" +#include "nameclash.h" +#include "fs.h" +#include "stream.h" +#include "mainloop.h" + +static inline int ask_rename(ClashHandling_t *ch, + char *longname, int isprimary, char *argname) +{ + char shortname[13]; + int mangled; + + /* TODO: Would be nice to suggest "autorenamed" version of name, press + * <Return> to get it. + */ +#if 0 + fprintf(stderr,"Entering ask_rename, isprimary=%d.\n", isprimary); +#endif + + if(!opentty(0)) + return 0; + +#define maxsize (isprimary ? MAX_VNAMELEN+1 : 11+1) +#define name (isprimary ? argname : shortname) + + mangled = 0; + do { + fprintf(stderr, "New %s name for \"%s\": ", + isprimary ? "primary" : "secondary", longname); + fflush(stderr); + if (! fgets(name, maxsize, opentty(0))) + return 0; + + /* Eliminate newline(s) in the file name */ + name[strlen(name)-1]='\0'; + if (!isprimary) + ch->name_converter(shortname,0, &mangled, argname); + } while (mangled & 1); + return 1; +#undef maxsize +#undef name +} + +static inline clash_action ask_namematch(char *name, int isprimary, + ClashHandling_t *ch, int no_overwrite, + int reason) +{ + char ans[10]; + clash_action a; + int perm; + char unix_shortname[13]; + + +#define EXISTS 0 +#define RESERVED 1 +#define ILLEGALS 2 + + static const char *reasons[]= { + "already exists", + "is reserved", + "contains illegal character(s)"}; + + + if (!isprimary) + name = unix_normalize(unix_shortname, name, name+8); + + a = ch->action[isprimary]; + + if(a == NAMEMATCH_NONE && !opentty(1)) { + /* no default, and no tty either . Skip the troublesome file */ + return NAMEMATCH_SKIP; + } + + perm = 0; + while (a == NAMEMATCH_NONE) { + fprintf(stderr, "%s file name \"%s\" %s.\n", + isprimary ? "Long" : "Short", name, reasons[reason]); + fprintf(stderr, + "a)utorename A)utorename-all r)ename R)ename-all "); + if(!no_overwrite) + fprintf(stderr,"o)verwrite O)verwrite-all"); + fprintf(stderr, + "\ns)kip S)kip-all q)uit (aArR"); + if(!no_overwrite) + fprintf(stderr,"oO"); + fprintf(stderr,"sSq): "); + fflush(stderr); + fflush(opentty(1)); + if (mtools_raw_tty) { + int rep; + rep = fgetc(opentty(1)); + fputs("\n", stderr); + if(rep == EOF) + ans[0] = 'q'; + else + ans[0] = rep; + } else { + fgets(ans, 9, opentty(0)); + } + perm = isupper((unsigned char)ans[0]); + switch(tolower((unsigned char)ans[0])) { + case 'a': + a = NAMEMATCH_AUTORENAME; + break; + case 'r': + if(isprimary) + a = NAMEMATCH_PRENAME; + else + a = NAMEMATCH_RENAME; + break; + case 'o': + if(no_overwrite) + continue; + a = NAMEMATCH_OVERWRITE; + break; + case 's': + a = NAMEMATCH_SKIP; + break; + case 'q': + perm = 0; + a = NAMEMATCH_QUIT; + break; + default: + perm = 0; + } + } + + /* Keep track of this action in case this file collides again */ + ch->action[isprimary] = a; + if (perm) + ch->namematch_default[isprimary] = a; + + /* if we were asked to overwrite be careful. We can't set the action + * to overwrite, else we get won't get a chance to specify another + * action, should overwrite fail. Indeed, we'll be caught in an + * infinite loop because overwrite will fail the same way for the + * second time */ + if(a == NAMEMATCH_OVERWRITE) + ch->action[isprimary] = NAMEMATCH_NONE; + return a; +} + +/* Returns: + * 2 if file is to be overwritten + * 1 if file was renamed + * 0 if it was skipped + * + * If a short name is involved, handle conversion between the 11-character + * fixed-length record DOS name and a literal null-terminated name (e.g. + * "COMMAND COM" (no null) <-> "COMMAND.COM" (null terminated)). + * + * Also, immediately copy the original name so that messages can use it. + */ +static inline clash_action process_namematch(char *name, + char *longname, + int isprimary, + ClashHandling_t *ch, + int no_overwrite, + int reason) +{ + clash_action action; + +#if 0 + fprintf(stderr, + "process_namematch: name=%s, default_action=%d, ask=%d.\n", + name, default_action, ch->ask); +#endif + + action = ask_namematch(name, isprimary, ch, no_overwrite, reason); + + switch(action){ + case NAMEMATCH_QUIT: + got_signal = 1; + return NAMEMATCH_SKIP; + case NAMEMATCH_SKIP: + return NAMEMATCH_SKIP; + case NAMEMATCH_RENAME: + case NAMEMATCH_PRENAME: + /* We need to rename the file now. This means we must pass + * back through the loop, a) ensuring there isn't a potential + * new name collision, and b) finding a big enough VSE. + * Change the name, so that it won't collide again. + */ + ask_rename(ch, longname, isprimary, name); + return action; + case NAMEMATCH_AUTORENAME: + /* Very similar to NAMEMATCH_RENAME, except that we need to + * first generate the name. + * TODO: Remember previous name so we don't + * keep trying the same one. + */ + if (isprimary) { + autorename_long(name, 1); + return NAMEMATCH_PRENAME; + } else { + autorename_short(name, 1); + return NAMEMATCH_RENAME; + } + case NAMEMATCH_OVERWRITE: + if(no_overwrite) + return NAMEMATCH_SKIP; + else + return NAMEMATCH_OVERWRITE; + default: + return NAMEMATCH_NONE; + } +} + + +static void clear_scan(char *longname, int use_longname, struct scan_state *s) +{ + s->shortmatch = s->longmatch = s->slot = -1; + s->free_end = s->got_slots = s->free_start = 0; + + if (use_longname & 1) + s->size_needed = 2 + (strlen(longname)/VSE_NAMELEN); + else + s->size_needed = 1; +} + + +static int contains_illegals(const char *string, const char *illegals) +{ + for(; *string ; string++) + if((*string < ' ' && *string != '\005' && !(*string & 0x80)) || + strchr(illegals, *string)) + return 1; + return 0; +} + +static int is_reserved(char *ans, int islong) +{ + int i; + static const char *dev3[] = {"CON", "AUX", "PRN", "NUL", " "}; + static const char *dev4[] = {"COM", "LPT" }; + + for (i = 0; i < sizeof(dev3)/sizeof(*dev3); i++) + if (!strncasecmp(ans, dev3[i], 3) && + ((islong && !ans[3]) || + (!islong && !strncmp(ans+3," ",5)))) + return 1; + + for (i = 0; i < sizeof(dev4)/sizeof(*dev4); i++) + if (!strncasecmp(ans, dev4[i], 3) && + (ans[3] >= '1' && ans[3] <= '4') && + ((islong && !ans[4]) || + (!islong && !strncmp(ans+4," ",4)))) + return 1; + + return 0; +} + +static inline clash_action get_slots(Stream_t *Dir, + char *dosname, char *longname, + struct scan_state *ssp, + ClashHandling_t *ch) +{ + int error; + clash_action ret; + int match=0; + direntry_t entry; + int isprimary; + int no_overwrite; + int reason; + int pessimisticShortRename; + + pessimisticShortRename = (ch->action[0] == NAMEMATCH_AUTORENAME); + + entry.Dir = Dir; + no_overwrite = 1; + if((is_reserved(longname,1)) || + longname[strspn(longname,". ")] == '\0'){ + reason = RESERVED; + isprimary = 1; + } else if(contains_illegals(longname,long_illegals)) { + reason = ILLEGALS; + isprimary = 1; + } else if(is_reserved(dosname,0)) { + reason = RESERVED; + ch->use_longname = 1; + isprimary = 0; + } else if(contains_illegals(dosname,short_illegals)) { + reason = ILLEGALS; + ch->use_longname = 1; + isprimary = 0; + } else { + reason = EXISTS; + clear_scan(longname, ch->use_longname, ssp); + switch (lookupForInsert(Dir, dosname, longname, ssp, + ch->ignore_entry, + ch->source_entry, + pessimisticShortRename && + ch->use_longname)) { + case -1: + return NAMEMATCH_ERROR; + + case 0: + return NAMEMATCH_SKIP; + /* Single-file error error or skip request */ + + case 5: + return NAMEMATCH_GREW; + /* Grew directory, try again */ + + case 6: + return NAMEMATCH_SUCCESS; /* Success */ + } + match = -2; + if (ssp->longmatch > -1) { + /* Primary Long Name Match */ +#ifdef debug + fprintf(stderr, + "Got longmatch=%d for name %s.\n", + longmatch, longname); +#endif + match = ssp->longmatch; + isprimary = 1; + } else if ((ch->use_longname & 1) && (ssp->shortmatch != -1)) { + /* Secondary Short Name Match */ +#ifdef debug + fprintf(stderr, + "Got secondary short name match for name %s.\n", + longname); +#endif + + match = ssp->shortmatch; + isprimary = 0; + } else if (ssp->shortmatch >= 0) { + /* Primary Short Name Match */ +#ifdef debug + fprintf(stderr, + "Got primary short name match for name %s.\n", + longname); +#endif + match = ssp->shortmatch; + isprimary = 1; + } else + return NAMEMATCH_RENAME; + + if(match > -1) { + entry.entry = match; + dir_read(&entry, &error); + if (error) + return NAMEMATCH_ERROR; + /* if we can't overwrite, don't propose it */ + no_overwrite = (match == ch->source || IS_DIR(&entry)); + } + } + ret = process_namematch(isprimary ? longname : dosname, longname, + isprimary, ch, no_overwrite, reason); + + if (ret == NAMEMATCH_OVERWRITE && match > -1){ + if((entry.dir.attr & 0x5) && + (ask_confirmation("file is read only, overwrite anyway (y/n) ? ",0,0))) + return NAMEMATCH_RENAME; + + /* Free up the file to be overwritten */ + if(fatFreeWithDirentry(&entry)) + return NAMEMATCH_ERROR; + +#if 0 + if(isprimary && + match - ssp->match_free + 1 >= ssp->size_needed){ + /* reuse old entry and old short name for overwrite */ + ssp->free_start = match - ssp->size_needed + 1; + ssp->free_size = ssp->size_needed; + ssp->slot = match; + ssp->got_slots = 1; + strncpy(dosname, dir.name, 3); + strncpy(dosname + 8, dir.ext, 3); + return ret; + } else +#endif + { + entry.dir.name[0] = DELMARK; + dir_write(&entry); + return NAMEMATCH_RENAME; + } + } + + return ret; +} + + +static inline int write_slots(Stream_t *Dir, + char *dosname, + char *longname, + struct scan_state *ssp, + write_data_callback *cb, + void *arg, + int Case) +{ + direntry_t entry; + + /* write the file */ + if (fat_error(Dir)) + return 0; + + entry.Dir = Dir; + entry.entry = ssp->slot; + strncpy(entry.name, longname, sizeof(entry.name)-1); + entry.name[sizeof(entry.name)-1]='\0'; + entry.dir.Case = Case & (EXTCASE | BASECASE); + if (cb(dosname, longname, arg, &entry) >= 0) { + if ((ssp->size_needed > 1) && + (ssp->free_end - ssp->free_start >= ssp->size_needed)) { + ssp->slot = write_vfat(Dir, dosname, longname, + ssp->free_start, &entry); + } else { + ssp->size_needed = 1; + write_vfat(Dir, dosname, 0, + ssp->free_start, &entry); + } + /* clear_vses(Dir, ssp->free_start + ssp->size_needed, + ssp->free_end); */ + } else + return 0; + + return 1; /* Successfully wrote the file */ +} + +static void stripspaces(char *name) +{ + char *p,*non_space; + + non_space = name; + for(p=name; *p; p++) + if (*p != ' ') + non_space = p; + if(name[0]) + non_space[1] = '\0'; +} + + +int _mwrite_one(Stream_t *Dir, + char *argname, + char *shortname, + write_data_callback *cb, + void *arg, + ClashHandling_t *ch) +{ + char longname[VBUFSIZE]; + const char *dstname; + char dosname[13]; + int expanded; + struct scan_state scan; + clash_action ret; + + expanded = 0; + + if(isSpecial(argname)) { + fprintf(stderr, "Cannot create entry named . or ..\n"); + return -1; + } + + if(ch->name_converter == dos_name) { + if(shortname) + stripspaces(shortname); + if(argname) + stripspaces(argname); + } + + if(shortname){ + ch->name_converter(shortname,0, &ch->use_longname, dosname); + if(ch->use_longname & 1){ + /* short name mangled, treat it as a long name */ + argname = shortname; + shortname = 0; + } + } + + /* Skip drive letter */ + dstname = skip_drive(argname); + + /* Copy original argument dstname to working value longname */ + strncpy(longname, dstname, VBUFSIZE-1); + + if(shortname) { + ch->name_converter(shortname,0, &ch->use_longname, dosname); + if(strcmp(shortname, longname)) + ch->use_longname |= 1; + } else + ch->name_converter(longname,0, &ch->use_longname, dosname); + + ch->action[0] = ch->namematch_default[0]; + ch->action[1] = ch->namematch_default[1]; + + while (1) { + switch((ret=get_slots(Dir, dosname, longname, + &scan, ch))){ + case NAMEMATCH_ERROR: + return -1; /* Non-file-specific error, + * quit */ + + case NAMEMATCH_SKIP: + return -1; /* Skip file (user request or + * error) */ + + case NAMEMATCH_PRENAME: + ch->name_converter(longname,0, + &ch->use_longname, dosname); + continue; + case NAMEMATCH_RENAME: + continue; /* Renamed file, loop again */ + + case NAMEMATCH_GREW: + /* No collision, and not enough slots. + * Try to grow the directory + */ + if (expanded) { /* Already tried this + * once, no good */ + fprintf(stderr, + "%s: No directory slots\n", + progname); + return -1; + } + expanded = 1; + + if (dir_grow(Dir, scan.max_entry)) + return -1; + continue; + case NAMEMATCH_OVERWRITE: + case NAMEMATCH_SUCCESS: + return write_slots(Dir, dosname, longname, + &scan, cb, arg, + ch->use_longname); + default: + fprintf(stderr, + "Internal error: clash_action=%d\n", + ret); + return -1; + } + + } +} + +int mwrite_one(Stream_t *Dir, + const char *_argname, + const char *_shortname, + write_data_callback *cb, + void *arg, + ClashHandling_t *ch) +{ + char *argname; + char *shortname; + int ret; + + if(_argname) + argname = strdup(_argname); + else + argname = 0; + if(_shortname) + shortname = strdup(_shortname); + else + shortname = 0; + ret = _mwrite_one(Dir, argname, shortname, cb, arg, ch); + if(argname) + free(argname); + if(shortname) + free(shortname); + return ret; +} + +void init_clash_handling(ClashHandling_t *ch) +{ + ch->ignore_entry = -1; + ch->source_entry = -2; + ch->nowarn = 0; /*Don't ask, just do default action if name collision */ + ch->namematch_default[0] = NAMEMATCH_AUTORENAME; + ch->namematch_default[1] = NAMEMATCH_NONE; + ch->name_converter = dos_name; /* changed by mlabel */ + ch->source = -2; +} + +int handle_clash_options(ClashHandling_t *ch, char c) +{ + int isprimary; + if(isupper(c)) + isprimary = 0; + else + isprimary = 1; + c = tolower(c); + switch(c) { + case 'o': + /* Overwrite if primary name matches */ + ch->namematch_default[isprimary] = NAMEMATCH_OVERWRITE; + return 0; + case 'r': + /* Rename primary name interactively */ + ch->namematch_default[isprimary] = NAMEMATCH_RENAME; + return 0; + case 's': + /* Skip file if primary name collides */ + ch->namematch_default[isprimary] = NAMEMATCH_SKIP; + return 0; + case 'm': + ch->namematch_default[isprimary] = NAMEMATCH_NONE; + return 0; + case 'a': + ch->namematch_default[isprimary] = NAMEMATCH_AUTORENAME; + return 0; + default: + return -1; + } +} diff --git a/commands/i386/mtools-3.9.7/mlabel.c b/commands/i386/mtools-3.9.7/mlabel.c new file mode 100755 index 000000000..0fdcbdfd3 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mlabel.c @@ -0,0 +1,251 @@ +/* + * mlabel.c + * Make an MSDOS volume label + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mainloop.h" +#include "vfat.h" +#include "mtools.h" +#include "nameclash.h" + +char *label_name(char *filename, int verbose, + int *mangled, char *ans) +{ + int len; + int i; + int have_lower, have_upper; + + strcpy(ans," "); + len = strlen(filename); + if(len > 11){ + *mangled = 1; + len = 11; + } else + *mangled = 0; + strncpy(ans, filename, len); + have_lower = have_upper = 0; + for(i=0; i<11; i++){ + if(islower((unsigned char)ans[i])) + have_lower = 1; + if(isupper(ans[i])) + have_upper = 1; + ans[i] = toupper((unsigned char)ans[i]); + + if(strchr("^+=/[]:,?*\\<>|\".", ans[i])){ + *mangled = 1; + ans[i] = '~'; + } + } + if (have_lower && have_upper) + *mangled = 1; + return ans; +} + +int labelit(char *dosname, + char *longname, + void *arg0, + direntry_t *entry) +{ + time_t now; + + /* find out current time */ + getTimeNow(&now); + mk_entry(dosname, 0x8, 0, 0, now, &entry->dir); + return 0; +} + +static void usage(void) +{ + fprintf(stderr, "Mtools version %s, dated %s\n", + mversion, mdate); + fprintf(stderr, "Usage: %s [-vscn] [-N serial] drive:[label]\n" + "\t-v Verbose\n" + "\t-s Show label\n" + "\t-c Clear label\n" + "\t-n New random serial number\n" + "\t-N New given serial number\n", progname); + exit(1); +} + + +void mlabel(int argc, char **argv, int type) +{ + + char *drive, *newLabel; + int verbose, clear, interactive, show, open_mode; + direntry_t entry; + int result=0; + char longname[VBUFSIZE]; + char shortname[13]; + ClashHandling_t ch; + struct MainParam_t mp; + Stream_t *RootDir; + int c; + int mangled; + enum { SER_NONE, SER_RANDOM, SER_SET } set_serial = SER_NONE; + long serial = 0; + int need_write_boot = 0; + int have_boot = 0; + char *eptr = ""; + struct bootsector boot; + Stream_t *Fs=0; + int r; + struct label_blk_t *labelBlock; + + init_clash_handling(&ch); + ch.name_converter = label_name; + ch.ignore_entry = -2; + + verbose = 0; + clear = 0; + show = 0; + + while ((c = getopt(argc, argv, "vcsnN:")) != EOF) { + switch (c) { + case 'v': + verbose = 1; + break; + case 'c': + clear = 1; + break; + case 's': + show = 1; + break; + case 'n': + set_serial = SER_RANDOM; + srandom((long)time (0)); + serial=random(); + break; + case 'N': + set_serial = SER_SET; + serial = strtol(optarg, &eptr, 16); + if(*eptr) { + fprintf(stderr, + "%s not a valid serial number\n", + optarg); + exit(1); + } + break; + default: + usage(); + } + } + + if (argc - optind != 1 || skip_drive(argv[optind]) == argv[optind]) + usage(); + + init_mp(&mp); + newLabel = skip_drive(argv[optind]); + interactive = !show && !clear &&!newLabel[0] && + (set_serial == SER_NONE); + open_mode = O_RDWR; + drive = get_drive(argv[optind], NULL); + RootDir = open_root_dir(drive, open_mode); + if(strlen(newLabel) > VBUFSIZE) { + fprintf(stderr, "Label too long\n"); + FREE(&RootDir); + exit(1); + } + + if(!RootDir && open_mode == O_RDWR && !clear && !newLabel[0] && + ( errno == EACCES || errno == EPERM) ) { + show = 1; + interactive = 0; + RootDir = open_root_dir(drive, O_RDONLY); + } + if(!RootDir) { + fprintf(stderr, "%s: Cannot initialize drive\n", argv[0]); + exit(1); + } + + initializeDirentry(&entry, RootDir); + r=vfat_lookup(&entry, 0, 0, ACCEPT_LABEL | MATCH_ANY, + shortname, longname); + if (r == -2) { + FREE(&RootDir); + exit(1); + } + + if(show || interactive){ + if(isNotFound(&entry)) + printf(" Volume has no label\n"); + else if (*longname) + printf(" Volume label is %s (abbr=%s)\n", + longname, shortname); + else + printf(" Volume label is %s\n", shortname); + + } + + /* ask for new label */ + if(interactive){ + newLabel = longname; + fprintf(stderr,"Enter the new volume label : "); + fgets(newLabel, VBUFSIZE, stdin); + if(newLabel[0]) + newLabel[strlen(newLabel)-1] = '\0'; + } + + if((!show || newLabel[0]) && !isNotFound(&entry)){ + /* if we have a label, wipe it out before putting new one */ + if(interactive && newLabel[0] == '\0') + if(ask_confirmation("Delete volume label (y/n): ",0,0)){ + FREE(&RootDir); + exit(0); + } + entry.dir.name[0] = DELMARK; + entry.dir.attr = 0; /* for old mlabel */ + dir_write(&entry); + } + + if (newLabel[0] != '\0') { + ch.ignore_entry = 1; + result = mwrite_one(RootDir,newLabel,0,labelit,NULL,&ch) ? + 0 : 1; + } + + have_boot = 0; + if( (!show || newLabel[0]) || set_serial != SER_NONE) { + Fs = GetFs(RootDir); + have_boot = (force_read(Fs,(char *)&boot,0,sizeof(boot)) == + sizeof(boot)); + } + + if(_WORD(boot.fatlen)) { + labelBlock = &boot.ext.old.labelBlock; + } else { + labelBlock = &boot.ext.fat32.labelBlock; + } + + if(!show || newLabel[0]){ + + if(!newLabel[0]) + strncpy(shortname, "NO NAME ",11); + else + label_name(newLabel, verbose, &mangled, shortname); + + if(have_boot && boot.descr >= 0xf0 && + labelBlock->dos4 == 0x29) { + strncpy(labelBlock->label, shortname, 11); + need_write_boot = 1; + + } + } + + if((set_serial != SER_NONE) & have_boot) { + if(have_boot && boot.descr >= 0xf0 && + labelBlock->dos4 == 0x29) { + set_dword(labelBlock->serial, serial); + need_write_boot = 1; + } + } + + if(need_write_boot) { + force_write(Fs, (char *)&boot, 0, sizeof(boot)); + } + + FREE(&RootDir); + exit(result); +} diff --git a/commands/i386/mtools-3.9.7/mmd.c b/commands/i386/mtools-3.9.7/mmd.c new file mode 100755 index 000000000..3c260d67d --- /dev/null +++ b/commands/i386/mtools-3.9.7/mmd.c @@ -0,0 +1,174 @@ +/* + * mmd.c + * Makes an MSDOS directory + */ + + +#define LOWERCASE + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "vfat.h" +#include "mainloop.h" +#include "plain_io.h" +#include "nameclash.h" +#include "file.h" +#include "fs.h" + +/* + * Preserve the file modification times after the fclose() + */ + +typedef struct Arg_t { + char *target; + MainParam_t mp; + + Stream_t *SrcDir; + int entry; + ClashHandling_t ch; + Stream_t *targetDir; +} Arg_t; + + +typedef struct CreateArg_t { + Stream_t *Dir; + Stream_t *NewDir; + unsigned char attr; + time_t mtime; +} CreateArg_t; + +/* + * Open the named file for read, create the cluster chain, return the + * directory structure or NULL on error. + */ +int makeit(char *dosname, + char *longname, + void *arg0, + direntry_t *targetEntry) +{ + Stream_t *Target; + CreateArg_t *arg = (CreateArg_t *) arg0; + int fat; + direntry_t subEntry; + + /* will it fit? At least one cluster must be free */ + if (!getfreeMinClusters(targetEntry->Dir, 1)) + return -1; + + mk_entry(dosname, ATTR_DIR, 1, 0, arg->mtime, &targetEntry->dir); + Target = OpenFileByDirentry(targetEntry); + if(!Target){ + fprintf(stderr,"Could not open Target\n"); + return -1; + } + + /* this allocates the first cluster for our directory */ + + initializeDirentry(&subEntry, Target); + + subEntry.entry = 1; + GET_DATA(targetEntry->Dir, 0, 0, 0, &fat); + if (fat == fat32RootCluster(targetEntry->Dir)) { + fat = 0; + } + mk_entry(".. ", ATTR_DIR, fat, 0, arg->mtime, &subEntry.dir); + dir_write(&subEntry); + + FLUSH((Stream_t *) Target); + subEntry.entry = 0; + GET_DATA(Target, 0, 0, 0, &fat); + mk_entry(". ", ATTR_DIR, fat, 0, arg->mtime, &subEntry.dir); + dir_write(&subEntry); + + mk_entry(dosname, ATTR_DIR | arg->attr, fat, 0, arg->mtime, + &targetEntry->dir); + arg->NewDir = Target; + return 0; +} + + +static void usage(void) +{ + fprintf(stderr, + "Mtools version %s, dated %s\n", mversion, mdate); + fprintf(stderr, + "Usage: %s [-D clash_option] file targetfile\n", progname); + fprintf(stderr, + " %s [-D clash_option] file [files...] target_directory\n", + progname); + exit(1); +} + +Stream_t *createDir(Stream_t *Dir, const char *filename, ClashHandling_t *ch, + unsigned char attr, time_t mtime) +{ + CreateArg_t arg; + int ret; + + arg.Dir = Dir; + arg.attr = attr; + arg.mtime = mtime; + + if (!getfreeMinClusters(Dir, 1)) + return NULL; + + ret = mwrite_one(Dir, filename,0, makeit, &arg, ch); + if(ret < 1) + return NULL; + else + return arg.NewDir; +} + +static int createDirCallback(direntry_t *entry, MainParam_t *mp) +{ + Stream_t *ret; + time_t now; + + ret = createDir(mp->File, mp->targetName, &((Arg_t *)(mp->arg))->ch, + ATTR_DIR, getTimeNow(&now)); + if(ret == NULL) + return ERROR_ONE; + else { + FREE(&ret); + return GOT_ONE; + } + +} + +void mmd(int argc, char **argv, int type) +{ + Arg_t arg; + int c; + + /* get command line options */ + + init_clash_handling(& arg.ch); + + /* get command line options */ + while ((c = getopt(argc, argv, "D:o")) != EOF) { + switch (c) { + case '?': + usage(); + case 'o': + handle_clash_options(&arg.ch, c); + break; + case 'D': + if(handle_clash_options(&arg.ch, *optarg)) + usage(); + break; + default: + break; + } + } + + if (argc - optind < 1) + usage(); + + init_mp(&arg.mp); + arg.mp.arg = (void *) &arg; + arg.mp.openflags = O_RDWR; + arg.mp.callback = createDirCallback; + arg.mp.lookupflags = OPEN_PARENT | DO_OPEN_DIRS; + exit(main_loop(&arg.mp, argv + optind, argc - optind)); +} diff --git a/commands/i386/mtools-3.9.7/mmount.c b/commands/i386/mtools-3.9.7/mmount.c new file mode 100755 index 000000000..e9face011 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mmount.c @@ -0,0 +1,85 @@ +/* + * Mount an MSDOS disk + * + * written by: + * + * Alain L. Knaff + * alain@linux.lu + * + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" + +#ifdef OS_linux +#include <sys/wait.h> +#include "mainloop.h" +#include "fs.h" + +extern int errno; + +void mmount(int argc, char **argv, int type) +{ + char drive; + int pid; + int status; + struct device dev; + char name[EXPAND_BUF]; + int media; + struct bootsector boot; + Stream_t *Stream; + + if (argc<2 || !argv[1][0] || argv[1][1] != ':' || argv[1][2]){ + fprintf(stderr,"Usage: %s -V drive:\n", argv[0]); + exit(1); + } + drive = toupper(argv[1][0]); + Stream = find_device(drive, O_RDONLY, &dev, &boot, name, &media, 0); + if(!Stream) + exit(1); + FREE(&Stream); + + destroy_privs(); + + if ( dev.partition ) { + char part_name[4]; + sprintf(part_name, "%d", dev.partition %1000); + strcat(name, part_name); + } + + /* and finally mount it */ + switch((pid=fork())){ + case -1: + fprintf(stderr,"fork failed\n"); + exit(1); + case 0: + close(2); + open("/dev/null", O_RDWR); + argv[1] = strdup("mount"); + if ( argc > 2 ) + execvp("mount", argv + 1 ); + else + execlp("mount", "mount", name, 0); + perror("exec mount"); + exit(1); + default: + while ( wait(&status) != pid ); + } + if ( WEXITSTATUS(status) == 0 ) + exit(0); + argv[0] = strdup("mount"); + argv[1] = strdup("-r"); + if(!argv[0] || !argv[1]){ + printOom(); + exit(1); + } + if ( argc > 2 ) + execvp("mount", argv); + else + execlp("mount", "mount","-r", name, 0); + exit(1); +} + +#endif /* linux */ + diff --git a/commands/i386/mtools-3.9.7/mmove.c b/commands/i386/mtools-3.9.7/mmove.c new file mode 100755 index 000000000..50a7ed34f --- /dev/null +++ b/commands/i386/mtools-3.9.7/mmove.c @@ -0,0 +1,314 @@ +/* + * mmove.c + * Renames/moves an MSDOS file + * + */ + + +#define LOWERCASE + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "vfat.h" +#include "mainloop.h" +#include "plain_io.h" +#include "nameclash.h" +#include "file.h" +#include "fs.h" + +/* + * Preserve the file modification times after the fclose() + */ + +typedef struct Arg_t { + const char *fromname; + int verbose; + MainParam_t mp; + + direntry_t *entry; + ClashHandling_t ch; +} Arg_t; + + +/* + * Open the named file for read, create the cluster chain, return the + * directory structure or NULL on error. + */ +int renameit(char *dosname, + char *longname, + void *arg0, + direntry_t *targetEntry) +{ + Arg_t *arg = (Arg_t *) arg0; + int fat; + + targetEntry->dir = arg->entry->dir; + strncpy(targetEntry->dir.name, dosname, 8); + strncpy(targetEntry->dir.ext, dosname + 8, 3); + + if(IS_DIR(targetEntry)) { + direntry_t *movedEntry; + + /* get old direntry. It is important that we do this + * on the actual direntry which is stored in the file, + * and not on a copy, because we will modify it, and the + * modification should be visible at file + * de-allocation time */ + movedEntry = getDirentry(arg->mp.File); + if(movedEntry->Dir != targetEntry->Dir) { + /* we are indeed moving it to a new directory */ + direntry_t subEntry; + Stream_t *oldDir; + /* we have a directory here. Change its parent link */ + + initializeDirentry(&subEntry, arg->mp.File); + + switch(vfat_lookup(&subEntry, "..", 2, ACCEPT_DIR, + NULL, NULL)) { + case -1: + fprintf(stderr, + " Directory has no parent entry\n"); + break; + case -2: + return ERROR_ONE; + case 0: + GET_DATA(targetEntry->Dir, 0, 0, 0, &fat); + if (fat == fat32RootCluster(targetEntry->Dir)) { + fat = 0; + } + + subEntry.dir.start[1] = (fat >> 8) & 0xff; + subEntry.dir.start[0] = fat & 0xff; + dir_write(&subEntry); + if(arg->verbose){ + fprintf(stderr, + "Easy, isn't it? I wonder why DOS can't do this.\n"); + } + break; + } + + /* wipe out original entry */ + movedEntry->dir.name[0] = DELMARK; + dir_write(movedEntry); + + /* free the old parent, allocate the new one. */ + oldDir = movedEntry->Dir; + *movedEntry = *targetEntry; + COPY(targetEntry->Dir); + FREE(&oldDir); + return 0; + } + } + + /* wipe out original entry */ + arg->mp.direntry->dir.name[0] = DELMARK; + dir_write(arg->mp.direntry); + return 0; +} + + + +static int rename_file(direntry_t *entry, MainParam_t *mp) +/* rename a messy DOS file to another messy DOS file */ +{ + int result; + Stream_t *targetDir; + char *shortname; + const char *longname; + + Arg_t * arg = (Arg_t *) (mp->arg); + + arg->entry = entry; + targetDir = mp->targetDir; + + if (targetDir == entry->Dir){ + arg->ch.ignore_entry = -1; + arg->ch.source = entry->entry; + arg->ch.source_entry = entry->entry; + } else { + arg->ch.ignore_entry = -1; + arg->ch.source = -2; + } + + longname = mpPickTargetName(mp); + shortname = 0; + result = mwrite_one(targetDir, longname, shortname, + renameit, (void *)arg, &arg->ch); + if(result == 1) + return GOT_ONE; + else + return ERROR_ONE; +} + + +static int rename_directory(direntry_t *entry, MainParam_t *mp) +{ + int ret; + + /* moves a DOS dir */ + if(isSubdirOf(mp->targetDir, mp->File)) { + fprintf(stderr, "Cannot move directory "); + fprintPwd(stderr, entry,0); + fprintf(stderr, " into one of its own subdirectories ("); + fprintPwd(stderr, getDirentry(mp->targetDir),0); + fprintf(stderr, ")\n"); + return ERROR_ONE; + } + + if(entry->entry == -3) { + fprintf(stderr, "Cannot move a root directory: "); + fprintPwd(stderr, entry,0); + return ERROR_ONE; + } + + ret = rename_file(entry, mp); + if(ret & ERROR_ONE) + return ret; + + return ret; +} + +static int rename_oldsyntax(direntry_t *entry, MainParam_t *mp) +{ + int result; + Stream_t *targetDir; + const char *shortname, *longname; + + Arg_t * arg = (Arg_t *) (mp->arg); + arg->entry = entry; + targetDir = entry->Dir; + + arg->ch.ignore_entry = -1; + arg->ch.source = entry->entry; + arg->ch.source_entry = entry->entry; + +#if 0 + if(!strcasecmp(mp->shortname, arg->fromname)){ + longname = mp->longname; + shortname = mp->targetName; + } else { +#endif + longname = mp->targetName; + shortname = 0; +#if 0 + } +#endif + result = mwrite_one(targetDir, longname, shortname, + renameit, (void *)arg, &arg->ch); + if(result == 1) + return GOT_ONE; + else + return ERROR_ONE; +} + + +static void usage(void) +{ + fprintf(stderr, + "Mtools version %s, dated %s\n", mversion, mdate); + fprintf(stderr, + "Usage: %s [-vo] [-D clash_option] file targetfile\n", progname); + fprintf(stderr, + " %s [-vo] [-D clash_option] file [files...] target_directory\n", + progname); + fprintf(stderr, "\t-v Verbose\n"); + exit(1); +} + +void mmove(int argc, char **argv, int oldsyntax) +{ + Arg_t arg; + int c; + char shortname[13]; + char longname[VBUFSIZE]; + char *def_drive; + int i; + + /* get command line options */ + + init_clash_handling(& arg.ch); + + /* get command line options */ + arg.verbose = 0; + while ((c = getopt(argc, argv, "vD:o")) != EOF) { + switch (c) { + case 'v': /* dummy option for mcopy */ + arg.verbose = 1; + break; + case '?': + usage(); + case 'o': + handle_clash_options(&arg.ch, c); + break; + case 'D': + if(handle_clash_options(&arg.ch, *optarg)) + usage(); + break; + default: + break; + } + } + + if (argc - optind < 2) + usage(); + + init_mp(&arg.mp); + arg.mp.arg = (void *) &arg; + arg.mp.openflags = O_RDWR; + + /* look for a default drive */ + def_drive = NULL; + for(i=optind; i<argc; i++) + if(skip_drive(argv[i]) > argv[i]){ + char *drive = get_drive(argv[i], NULL); + if(!def_drive) + def_drive = drive; + else if(strcmp(def_drive, drive) != 0){ + fprintf(stderr, + "Cannot move files across different drives\n"); + exit(1); + } + } + + if(def_drive) { + char mcwd[MAXPATHLEN]; + + strcpy(mcwd, skip_drive(arg.mp.mcwd)); + if(strlen(def_drive) + 1 + strlen(mcwd) + 1 > MAXPATHLEN){ + fprintf(stderr, + "Path name to current directory too long\n"); + exit(1); + } + strcpy(arg.mp.mcwd, def_drive); + strcat(arg.mp.mcwd, ":"); + strcat(arg.mp.mcwd, mcwd); + } + + if (oldsyntax && (argc - optind != 2 || strpbrk(":/", argv[argc-1]))) + oldsyntax = 0; + + arg.mp.lookupflags = + ACCEPT_PLAIN | ACCEPT_DIR | DO_OPEN_DIRS | NO_DOTS | NO_UNIX; + + if (!oldsyntax){ + target_lookup(&arg.mp, argv[argc-1]); + arg.mp.callback = rename_file; + arg.mp.dirCallback = rename_directory; + } else { + /* do not look up the target; it will be the same dir as the + * source */ + arg.fromname = _basename(skip_drive(argv[optind])); + arg.mp.targetName = strdup(argv[argc-1]); + arg.mp.callback = rename_oldsyntax; + } + + + arg.mp.longname = longname; + longname[0]='\0'; + + arg.mp.shortname = shortname; + shortname[0]='\0'; + + exit(main_loop(&arg.mp, argv + optind, argc - optind - 1)); +} diff --git a/commands/i386/mtools-3.9.7/mpartition.c b/commands/i386/mtools-3.9.7/mpartition.c new file mode 100755 index 000000000..378b81404 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mpartition.c @@ -0,0 +1,706 @@ +/* + * mformat.c + */ +#define DONT_NEED_WAIT + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "mainloop.h" +#include "fsP.h" +#include "file.h" +#include "plain_io.h" +#include "nameclash.h" +#include "buffer.h" +#include "scsi.h" +#include "partition.h" + +#ifdef OS_linux +#include "linux/hdreg.h" + +#define _LINUX_STRING_H_ +#define kdev_t int +#include "linux/fs.h" +#undef _LINUX_STRING_H_ + +#endif + +#define tolinear(x) \ +(sector(x)-1+(head(x)+cyl(x)*used_dev->heads)*used_dev->sectors) + + +static inline void print_hsc(hsc *h) +{ + printf(" h=%d s=%d c=%d\n", + head(*h), sector(*h), cyl(*h)); +} + +static void set_offset(hsc *h, int offset, int heads, int sectors) +{ + int head, sector, cyl; + + if(! heads || !sectors) + head = sector = cyl = 0; /* linear mode */ + else { + sector = offset % sectors; + offset = offset / sectors; + + head = offset % heads; + cyl = offset / heads; + if(cyl > 1023) cyl = 1023; + } + + h->head = head; + h->sector = ((sector+1) & 0x3f) | ((cyl & 0x300)>>2); + h->cyl = cyl & 0xff; +} + +void setBeginEnd(struct partition *partTable, int begin, int end, + int heads, int sectors, int activate, int type) +{ + set_offset(&partTable->start, begin, heads, sectors); + set_offset(&partTable->end, end-1, heads, sectors); + set_dword(partTable->start_sect, begin); + set_dword(partTable->nr_sects, end-begin); + if(activate) + partTable->boot_ind = 0x80; + else + partTable->boot_ind = 0; + if(!type) { + if(end-begin < 4096) + type = 1; /* DOS 12-bit FAT */ + else if(end-begin<32*2048) + type = 4; /* DOS 16-bit FAT, <32M */ + else + type = 6; /* DOS 16-bit FAT >= 32M */ + } + partTable->sys_ind = type; +} + +int consistencyCheck(struct partition *partTable, int doprint, int verbose, + int *has_activated, int *last_end, int *j, + struct device *used_dev, int target_partition) +{ + int i; + int inconsistency; + + *j = 0; + *last_end = 1; + + /* quick consistency check */ + inconsistency = 0; + *has_activated = 0; + for(i=1; i<5; i++){ + if(!partTable[i].sys_ind) + continue; + if(partTable[i].boot_ind) + (*has_activated)++; + if((used_dev && + (used_dev->heads != head(partTable[i].end)+1 || + used_dev->sectors != sector(partTable[i].end))) || + sector(partTable[i].start) != 1){ + fprintf(stderr, + "Partition %d is not aligned\n", + i); + inconsistency=1; + } + + if(*j && *last_end > BEGIN(partTable[i])) { + fprintf(stderr, + "Partitions %d and %d badly ordered or overlapping\n", + *j,i); + inconsistency=1; + } + + *last_end = END(partTable[i]); + *j = i; + + if(used_dev && + cyl(partTable[i].start) != 1023 && + tolinear(partTable[i].start) != BEGIN(partTable[i])) { + fprintf(stderr, + "Start position mismatch for partition %d\n", + i); + inconsistency=1; + } + if(used_dev && + cyl(partTable[i].end) != 1023 && + tolinear(partTable[i].end)+1 != END(partTable[i])) { + fprintf(stderr, + "End position mismatch for partition %d\n", + i); + inconsistency=1; + } + + if(doprint && verbose) { + if(i==target_partition) + putchar('*'); + else + putchar(' '); + printf("Partition %d\n",i); + + printf(" active=%x\n", partTable[i].boot_ind); + printf(" start:"); + print_hsc(&partTable[i].start); + printf(" type=0x%x\n", partTable[i].sys_ind); + printf(" end:"); + print_hsc(&partTable[i].end); + printf(" start=%d\n", BEGIN(partTable[i])); + printf(" nr=%d\n", _DWORD(partTable[i].nr_sects)); + printf("\n"); + } + } + return inconsistency; +} + +/* setsize function. Determines scsicam mapping if this cannot be inferred from + * any existing partitions. Shamelessly snarfed from the Linux kernel ;-) */ + +/* + * Function : static int setsize(unsigned long capacity,unsigned int *cyls, + * unsigned int *hds, unsigned int *secs); + * + * Purpose : to determine a near-optimal int 0x13 mapping for a + * SCSI disk in terms of lost space of size capacity, storing + * the results in *cyls, *hds, and *secs. + * + * Returns : -1 on failure, 0 on success. + * + * Extracted from + * + * WORKING X3T9.2 + * DRAFT 792D + * + * + * Revision 6 + * 10-MAR-94 + * Information technology - + * SCSI-2 Common access method + * transport and SCSI interface module + * + * ANNEX A : + * + * setsize() converts a read capacity value to int 13h + * head-cylinder-sector requirements. It minimizes the value for + * number of heads and maximizes the number of cylinders. This + * will support rather large disks before the number of heads + * will not fit in 4 bits (or 6 bits). This algorithm also + * minimizes the number of sectors that will be unused at the end + * of the disk while allowing for very large disks to be + * accommodated. This algorithm does not use physical geometry. + */ + +static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds, + unsigned int *secs) { + unsigned int rv = 0; + unsigned long heads, sectors, cylinders, temp; + + cylinders = 1024L; /* Set number of cylinders to max */ + sectors = 62L; /* Maximize sectors per track */ + + temp = cylinders * sectors; /* Compute divisor for heads */ + heads = capacity / temp; /* Compute value for number of heads */ + if (capacity % temp) { /* If no remainder, done! */ + heads++; /* Else, increment number of heads */ + temp = cylinders * heads; /* Compute divisor for sectors */ + sectors = capacity / temp; /* Compute value for sectors per + track */ + if (capacity % temp) { /* If no remainder, done! */ + sectors++; /* Else, increment number of sectors */ + temp = heads * sectors; /* Compute divisor for cylinders */ + cylinders = capacity / temp;/* Compute number of cylinders */ + } + } + if (cylinders == 0) rv=(unsigned)-1;/* Give error if 0 cylinders */ + + *cyls = (unsigned int) cylinders; /* Stuff return values */ + *secs = (unsigned int) sectors; + *hds = (unsigned int) heads; + return(rv); +} + +static void setsize0(unsigned long capacity,unsigned int *cyls, + unsigned int *hds, unsigned int *secs) +{ + int r; + + /* 1. First try "Megabyte" sizes */ + if(capacity < 1024 * 2048 && !(capacity % 1024)) { + *cyls = capacity >> 11; + *hds = 64; + *secs = 32; + return; + } + + /* then try scsicam's size */ + r = setsize(capacity,cyls,hds,secs); + if(r || *hds > 255 || *secs > 63) { + /* scsicam failed. Do megabytes anyways */ + *cyls = capacity >> 11; + *hds = 64; + *secs = 32; + return; + } +} + + +static void usage(void) +{ + fprintf(stderr, + "Mtools version %s, dated %s\n", mversion, mdate); + fprintf(stderr, + "Usage: %s [-pradcv] [-I [-B bootsect-template] [-s sectors] " + "[-t cylinders] " + "[-h heads] [-T type] [-b begin] [-l length] " + "drive\n", progname); + exit(1); +} + +void mpartition(int argc, char **argv, int dummy) +{ + Stream_t *Stream; + unsigned int dummy2; + + int i,j; + + int sec_per_cyl; + int doprint = 0; + int verbose = 0; + int create = 0; + int force = 0; + int length = 0; + int remove = 0; + int initialize = 0; + int tot_sectors=0; + int type = 0; + int begin_set = 0; + int size_set = 0; + int end_set = 0; + int last_end = 0; + int activate = 0; + int has_activated = 0; + int inconsistency=0; + int begin=0; + int end=0; + int sizetest=0; + int dirty = 0; + int open2flags = NO_OFFSET; + + int c; + struct device used_dev; + int argtracks, argheads, argsectors; + + char *drive, name[EXPAND_BUF]; + unsigned char buf[512]; + struct partition *partTable=(struct partition *)(buf+ 0x1ae); + struct device *dev; + char errmsg[200]; + char *bootSector=0; + + argtracks = 0; + argheads = 0; + argsectors = 0; + + /* get command line options */ + while ((c = getopt(argc, argv, "adprcIT:t:h:s:fvpb:l:S:B:")) != EOF) { + switch (c) { + case 'B': + bootSector = optarg; + break; + case 'a': + /* no privs, as it could be abused to + * make other partitions unbootable, or + * to boot a rogue kernel from this one */ + open2flags |= NO_PRIV; + activate = 1; + dirty = 1; + break; + case 'd': + activate = -1; + dirty = 1; + break; + case 'p': + doprint = 1; + break; + case 'r': + remove = 1; + dirty = 1; + break; + case 'I': + /* could be abused to nuke all other + * partitions */ + open2flags |= NO_PRIV; + initialize = 1; + dirty = 1; + break; + case 'c': + create = 1; + dirty = 1; + break; + + case 'T': + /* could be abused to "manually" create + * extended partitions */ + open2flags |= NO_PRIV; + type = strtoul(optarg,0,0); + break; + + case 't': + argtracks = atoi(optarg); + break; + case 'h': + argheads = atoi(optarg); + break; + case 's': + argsectors = atoi(optarg); + break; + + case 'f': + /* could be abused by creating overlapping + * partitions and other such Snafu */ + open2flags |= NO_PRIV; + force = 1; + break; + + case 'v': + verbose++; + break; + case 'S': + /* testing only */ + /* could be abused to create partitions + * extending beyond the actual size of the + * device */ + open2flags |= NO_PRIV; + tot_sectors = strtoul(optarg,0,0); + sizetest = 1; + break; + case 'b': + begin_set = 1; + begin = atoi(optarg); + break; + case 'l': + size_set = 1; + length = atoi(optarg); + break; + + default: + usage(); + } + } + + if (argc - optind != 1 || skip_drive(argv[optind]) == argv[optind]) + usage(); + + drive = get_drive(argv[optind], NULL); + + /* check out a drive whose letter and parameters match */ + sprintf(errmsg, "Drive '%s:' not supported", drive); + Stream = 0; + for(dev=devices;dev->drive;dev++) { + FREE(&(Stream)); + /* drive letter */ + if (strcmp(dev->drive, drive) != 0) + continue; + if (dev->partition < 1 || dev->partition > 4) { + sprintf(errmsg, + "Drive '%c:' is not a partition", + drive); + continue; + } + used_dev = *dev; + + SET_INT(used_dev.tracks, argtracks); + SET_INT(used_dev.heads, argheads); + SET_INT(used_dev.sectors, argsectors); + + expand(dev->name, name); + Stream = SimpleFileOpen(&used_dev, dev, name, + dirty ? O_RDWR : O_RDONLY, + errmsg, open2flags, 1, 0); + + if (!Stream) { +#ifdef HAVE_SNPRINTF + snprintf(errmsg,199,"init: open: %s", strerror(errno)); +#else + sprintf(errmsg,"init: open: %s", strerror(errno)); +#endif + continue; + } + + + /* try to find out the size */ + if(!sizetest) + tot_sectors = 0; + if(IS_SCSI(dev)) { + unsigned char cmd[10]; + unsigned char data[10]; + cmd[0] = SCSI_READ_CAPACITY; + memset ((void *) &cmd[2], 0, 8); + memset ((void *) &data[0], 137, 10); + scsi_cmd(get_fd(Stream), cmd, 10, SCSI_IO_READ, + data, 10, get_extra_data(Stream)); + + tot_sectors = 1 + + (data[0] << 24) + + (data[1] << 16) + + (data[2] << 8) + + (data[3] ); + if(verbose) + printf("%d sectors in total\n", tot_sectors); + } + +#ifdef OS_linux + if (tot_sectors == 0) { + ioctl(get_fd(Stream), BLKGETSIZE, &tot_sectors); + } +#endif + + /* read the partition table */ + if (READS(Stream, (char *) buf, 0, 512) != 512) { +#ifdef HAVE_SNPRINTF + snprintf(errmsg, 199, + "Error reading from '%s', wrong parameters?", + name); +#else + sprintf(errmsg, + "Error reading from '%s', wrong parameters?", + name); +#endif + continue; + } + if(verbose>=2) + print_sector("Read sector", buf, 512); + break; + } + + /* print error msg if needed */ + if ( dev->drive == 0 ){ + FREE(&Stream); + fprintf(stderr,"%s: %s\n", argv[0],errmsg); + exit(1); + } + + if((used_dev.sectors || used_dev.heads) && + (!used_dev.sectors || !used_dev.heads)) { + fprintf(stderr,"You should either indicate both the number of sectors and the number of heads,\n"); + fprintf(stderr," or none of them\n"); + exit(1); + } + + if(initialize) { + if (bootSector) { + int fd; + fd = open(bootSector, O_RDONLY); + if (fd < 0) { + perror("open boot sector"); + exit(1); + } + read(fd, (char *) buf, 512); + } + memset((char *)(partTable+1), 0, 4*sizeof(*partTable)); + set_dword(((unsigned char*)buf)+510, 0xaa55); + } + + /* check for boot signature, and place it if needed */ + if((buf[510] != 0x55) || (buf[511] != 0xaa)) { + fprintf(stderr,"Boot signature not set\n"); + fprintf(stderr, + "Use the -I flag to initialize the partition table, and set the boot signature\n"); + inconsistency = 1; + } + + if(remove){ + if(!partTable[dev->partition].sys_ind) + fprintf(stderr, + "Partition for drive %c: does not exist\n", + drive); + if((partTable[dev->partition].sys_ind & 0x3f) == 5) { + fprintf(stderr, + "Partition for drive %c: may be an extended partition\n", + drive); + fprintf(stderr, + "Use the -f flag to remove it anyways\n"); + inconsistency = 1; + } + memset(&partTable[dev->partition], 0, sizeof(*partTable)); + } + + if(create && partTable[dev->partition].sys_ind) { + fprintf(stderr, + "Partition for drive %c: already exists\n", drive); + fprintf(stderr, + "Use the -r flag to remove it before attempting to recreate it\n"); + } + + + /* find out number of heads and sectors, and whether there is + * any activated partition */ + has_activated = 0; + for(i=1; i<5; i++){ + if(!partTable[i].sys_ind) + continue; + + if(partTable[i].boot_ind) + has_activated++; + + /* set geometry from entry */ + if (!used_dev.heads) + used_dev.heads = head(partTable[i].end)+1; + if(!used_dev.sectors) + used_dev.sectors = sector(partTable[i].end); + if(i<dev->partition && !begin_set) + begin = END(partTable[i]); + if(i>dev->partition && !end_set && !size_set) { + end = BEGIN(partTable[i]); + end_set = 1; + } + } + +#ifdef OS_linux + if(!used_dev.sectors && !used_dev.heads) { + if(!IS_SCSI(dev)) { + struct hd_geometry geom; + if(ioctl(get_fd(Stream), HDIO_GETGEO, &geom) == 0) { + used_dev.heads = geom.heads; + used_dev.sectors = geom.sectors; + } + } + } +#endif + + if(!used_dev.sectors && !used_dev.heads) { + if(tot_sectors) + setsize0(tot_sectors,&dummy2,&used_dev.heads, + &used_dev.sectors); + else { + used_dev.heads = 64; + used_dev.sectors = 32; + } + } + + if(verbose) + fprintf(stderr,"sectors: %d heads: %d %d\n", + used_dev.sectors, used_dev.heads, tot_sectors); + + sec_per_cyl = used_dev.sectors * used_dev.heads; + if(create) { + if(!end_set && tot_sectors) { + end = tot_sectors - tot_sectors % sec_per_cyl; + end_set = 1; + } + + /* if the partition starts right at the beginning of + * the disk, keep one track unused to allow place for + * the master boot record */ + if(!begin && !begin_set) + begin = used_dev.sectors; + if(!size_set && used_dev.tracks) { + size_set = 2; + length = sec_per_cyl * used_dev.tracks; + + /* round the size in order to take + * into account any "hidden" sectors */ + + /* do we anchor this at the beginning ?*/ + if(begin_set || dev->partition <= 2 || !end_set) + length -= begin % sec_per_cyl; + else if(end - length < begin) + /* truncate any overlap */ + length = end - begin; + } + if(size_set) { + if(!begin_set && dev->partition >2 && end_set) + begin = end - length; + else + end = begin + length; + } else if(!end_set) { + fprintf(stderr,"Unknown size\n"); + exit(1); + } + + setBeginEnd(&partTable[dev->partition], begin, end, + used_dev.heads, used_dev.sectors, + !has_activated, type); + } + + if(activate) { + if(!partTable[dev->partition].sys_ind) { + fprintf(stderr, + "Partition for drive %c: does not exist\n", + drive); + } else { + switch(activate) { + case 1: + partTable[dev->partition].boot_ind=0x80; + break; + case -1: + partTable[dev->partition].boot_ind=0x00; + break; + } + } + } + + + inconsistency |= consistencyCheck(partTable, doprint, verbose, + &has_activated, &last_end, &j, + &used_dev, dev->partition); + + if(doprint && !inconsistency && partTable[dev->partition].sys_ind) { + printf("The following command will recreate the partition for drive %c:\n", + drive); + used_dev.tracks = + (_DWORD(partTable[dev->partition].nr_sects) + + (BEGIN(partTable[dev->partition]) % sec_per_cyl)) / + sec_per_cyl; + printf("mpartition -c -t %d -h %d -s %d -b %u %c:\n", + used_dev.tracks, used_dev.heads, used_dev.sectors, + BEGIN(partTable[dev->partition]), drive); + } + + if(tot_sectors && last_end >tot_sectors) { + fprintf(stderr, + "Partition %d exceeds beyond end of disk\n", + j); + exit(1); + } + + + switch(has_activated) { + case 0: + fprintf(stderr, + "Warning: no active (bootable) partition present\n"); + break; + case 1: + break; + default: + fprintf(stderr, + "Warning: %d active (bootable) partitions present\n", + has_activated); + fprintf(stderr, + "Usually, a disk should have exactly one active partition\n"); + break; + } + + if(inconsistency && !force) { + fprintf(stderr, + "inconsistency detected!\n" ); + if(dirty) + fprintf(stderr, + "Retry with the -f switch to go ahead anyways\n"); + exit(1); + } + + if(dirty) { + /* write data back to the disk */ + if(verbose>=2) + print_sector("Writing sector", buf, 512); + if (WRITES(Stream, (char *) buf, 0, 512) != 512) { + fprintf(stderr,"Error writing partition table"); + exit(1); + } + if(verbose>=3) + print_sector("Sector written", buf, 512); + FREE(&Stream); + } + exit(0); +} diff --git a/commands/i386/mtools-3.9.7/msdos.h b/commands/i386/mtools-3.9.7/msdos.h new file mode 100755 index 000000000..27cd73df0 --- /dev/null +++ b/commands/i386/mtools-3.9.7/msdos.h @@ -0,0 +1,237 @@ +#ifndef MTOOLS_MSDOS_H +#define MTOOLS_MSDOS_H + +/* + * msdos common header file + */ + +#define MAX_SECTOR 8192 /* largest sector size */ +#define MDIR_SIZE 32 /* MSDOS directory entry size in bytes*/ +#define MAX_CLUSTER 8192 /* largest cluster size */ +#define MAX_PATH 128 /* largest MSDOS path length */ +#define MAX_DIR_SECS 64 /* largest directory (in sectors) */ +#define MSECTOR_SIZE msector_size + +#define NEW 1 +#define OLD 0 + +#define _WORD(x) ((unsigned char)(x)[0] + (((unsigned char)(x)[1]) << 8)) +#define _DWORD(x) (_WORD(x) + (_WORD((x)+2) << 16)) + +#define DELMARK ((char) 0xe5) + +struct directory { + char name[8]; /* 0 file name */ + char ext[3]; /* 8 file extension */ + unsigned char attr; /* 11 attribute byte */ + unsigned char Case; /* 12 case of short filename */ + unsigned char ctime_ms; /* 13 creation time, milliseconds (?) */ + unsigned char ctime[2]; /* 14 creation time */ + unsigned char cdate[2]; /* 16 creation date */ + unsigned char adate[2]; /* 18 last access date */ + unsigned char startHi[2]; /* 20 start cluster, Hi */ + unsigned char time[2]; /* 22 time stamp */ + unsigned char date[2]; /* 24 date stamp */ + unsigned char start[2]; /* 26 starting cluster number */ + unsigned char size[4]; /* 28 size of the file */ +}; + +#define EXTCASE 0x10 +#define BASECASE 0x8 + +#define MAX32 0xffffffff +#define MAX_SIZE 0x7fffffff + +#define FILE_SIZE(dir) (_DWORD((dir)->size)) +#define START(dir) (_WORD((dir)->start)) +#define STARTHI(dir) (_WORD((dir)->startHi)) + +/* ASSUMPTION: long is at least 32 bits */ +UNUSED(static inline void set_dword(unsigned char *data, unsigned long value)) +{ + data[3] = (value >> 24) & 0xff; + data[2] = (value >> 16) & 0xff; + data[1] = (value >> 8) & 0xff; + data[0] = (value >> 0) & 0xff; +} + + +/* ASSUMPTION: short is at least 16 bits */ +UNUSED(static inline void set_word(unsigned char *data, unsigned short value)) +{ + data[1] = (value >> 8) & 0xff; + data[0] = (value >> 0) & 0xff; +} + + +/* + * hi byte | low byte + * |7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0| + * | | | | | | | | | | | | | | | | | + * \ 7 bits /\4 bits/\ 5 bits / + * year +80 month day + */ +#define DOS_YEAR(dir) (((dir)->date[1] >> 1) + 1980) +#define DOS_MONTH(dir) (((((dir)->date[1]&0x1) << 3) + ((dir)->date[0] >> 5))) +#define DOS_DAY(dir) ((dir)->date[0] & 0x1f) + +/* + * hi byte | low byte + * |7|6|5|4|3|2|1|0|7|6|5|4|3|2|1|0| + * | | | | | | | | | | | | | | | | | + * \ 5 bits /\ 6 bits /\ 5 bits / + * hour minutes sec*2 + */ +#define DOS_HOUR(dir) ((dir)->time[1] >> 3) +#define DOS_MINUTE(dir) (((((dir)->time[1]&0x7) << 3) + ((dir)->time[0] >> 5))) +#define DOS_SEC(dir) (((dir)->time[0] & 0x1f) * 2) + + +typedef struct InfoSector_t { + unsigned char signature1[4]; + unsigned char filler1[0x1e0]; + unsigned char signature2[4]; + unsigned char count[4]; + unsigned char pos[4]; + unsigned char filler2[14]; + unsigned char signature3[2]; +} InfoSector_t; + +#define INFOSECT_SIGNATURE1 0x41615252 +#define INFOSECT_SIGNATURE2 0x61417272 + + +typedef struct label_blk_t { + unsigned char physdrive; /* 36 physical drive ? */ + unsigned char reserved; /* 37 reserved */ + unsigned char dos4; /* 38 dos > 4.0 diskette */ + unsigned char serial[4]; /* 39 serial number */ + char label[11]; /* 43 disk label */ + char fat_type[8]; /* 54 FAT type */ +} label_blk_t; + +/* FAT32 specific info in the bootsector */ +typedef struct fat32_t { + unsigned char bigFat[4]; /* 36 nb of sectors per FAT */ + unsigned char extFlags[2]; /* 40 extension flags */ + unsigned char fsVersion[2]; /* 42 ? */ + unsigned char rootCluster[4]; /* 44 start cluster of root dir */ + unsigned char infoSector[2]; /* 48 changeable global info */ + unsigned char backupBoot[2]; /* 50 back up boot sector */ + unsigned char reserved[6]; /* 52 ? */ + unsigned char reserved2[6]; /* 52 ? */ + struct label_blk_t labelBlock; +} fat32; /* ends at 58 */ + +typedef struct oldboot_t { + struct label_blk_t labelBlock; + unsigned char res_2m; /* 62 reserved by 2M */ + unsigned char CheckSum; /* 63 2M checksum (not used) */ + unsigned char fmt_2mf; /* 64 2MF format version */ + unsigned char wt; /* 65 1 if write track after format */ + unsigned char rate_0; /* 66 data transfer rate on track 0 */ + unsigned char rate_any; /* 67 data transfer rate on track<>0 */ + unsigned char BootP[2]; /* 68 offset to boot program */ + unsigned char Infp0[2]; /* 70 T1: information for track 0 */ + unsigned char InfpX[2]; /* 72 T2: information for track<>0 */ + unsigned char InfTm[2]; /* 74 T3: track sectors size table */ + unsigned char DateF[2]; /* 76 Format date */ + unsigned char TimeF[2]; /* 78 Format time */ + unsigned char junk[1024 - 80]; /* 80 remaining data */ +} oldboot_t; + +struct bootsector { + unsigned char jump[3]; /* 0 Jump to boot code */ + char banner[8] PACKED; /* 3 OEM name & version */ + unsigned char secsiz[2] PACKED; /* 11 Bytes per sector hopefully 512 */ + unsigned char clsiz; /* 13 Cluster size in sectors */ + unsigned char nrsvsect[2]; /* 14 Number of reserved (boot) sectors */ + unsigned char nfat; /* 16 Number of FAT tables hopefully 2 */ + unsigned char dirents[2] PACKED;/* 17 Number of directory slots */ + unsigned char psect[2] PACKED; /* 19 Total sectors on disk */ + unsigned char descr; /* 21 Media descriptor=first byte of FAT */ + unsigned char fatlen[2]; /* 22 Sectors in FAT */ + unsigned char nsect[2]; /* 24 Sectors/track */ + unsigned char nheads[2]; /* 26 Heads */ + unsigned char nhs[4]; /* 28 number of hidden sectors */ + unsigned char bigsect[4]; /* 32 big total sectors */ + + union { + struct fat32_t fat32; + struct oldboot_t old; + } ext; +}; + +#define CHAR(x) (boot->x[0]) +#define WORD(x) (_WORD(boot->x)) +#define DWORD(x) (_DWORD(boot->x)) +#define OFFSET(x) (((char *) (boot->x)) - ((char *)(boot->jump))) + + +extern struct OldDos_t { + int tracks; + int sectors; + int heads; + + int dir_len; + int cluster_size; + int fat_len; + + int media; +} old_dos[]; + +#define FAT12 4085 /* max. number of clusters described by a 12 bit FAT */ +#define FAT16 65525 + +#define ATTR_ARCHIVE 0x20 +#define ATTR_DIR 0x10 +#define ATTR_LABEL 0x8 +#define ATTR_SYSTEM 0x4 +#define ATTR_HIDDEN 0x2 +#define ATTR_READONLY 0x1 + +#define HAS_BIT(entry,x) ((entry)->dir.attr & (x)) + +#define IS_ARCHIVE(entry) (HAS_BIT((entry),ATTR_ARCHIVE)) +#define IS_DIR(entry) (HAS_BIT((entry),ATTR_DIR)) +#define IS_LABEL(entry) (HAS_BIT((entry),ATTR_LABEL)) +#define IS_SYSTEM(entry) (HAS_BIT((entry),ATTR_SYSTEM)) +#define IS_HIDDEN(entry) (HAS_BIT((entry),ATTR_HIDDEN)) +#define IS_READONLY(entry) (HAS_BIT((entry),ATTR_READONLY)) + + +#define MAX_SECT_PER_CLUSTER 64 +/* Experimentally, it turns out that DOS only accepts cluster sizes + * which are powers of two, and less than 128 sectors (else it gets a + * divide overflow) */ + + +#define FAT_SIZE(bits, sec_siz, clusters) \ + ((((clusters)+2) * ((bits)/4) - 1) / 2 / (sec_siz) + 1) + +#define NEEDED_FAT_SIZE(x) FAT_SIZE((x)->fat_bits, (x)->sector_size, \ + (x)->num_clus) + +/* disk size taken by FAT and clusters */ +#define DISK_SIZE(bits, sec_siz, clusters, n, cluster_size) \ + ((n) * FAT_SIZE(bits, sec_siz, clusters) + \ + (clusters) * (cluster_size)) + +#define TOTAL_DISK_SIZE(bits, sec_siz, clusters, n, cluster_size) \ + (DISK_SIZE(bits, sec_siz, clusters, n, cluster_size) + 2) +/* approx. total disk size: assume 1 boot sector and one directory sector */ + +extern const char *mversion; +extern const char *mdate; + +extern char *Version; +extern char *Date; + + +int init(char drive, int mode); + +#define MT_READ 1 +#define MT_WRITE 2 + +#endif + diff --git a/commands/i386/mtools-3.9.7/mshowfat.c b/commands/i386/mtools-3.9.7/mshowfat.c new file mode 100755 index 000000000..ee69923f7 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mshowfat.c @@ -0,0 +1,87 @@ +/* + * mcopy.c + * Copy an MSDOS files to and from Unix + * + */ + + +#define LOWERCASE + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "vfat.h" +#include "mainloop.h" +#include "plain_io.h" +#include "nameclash.h" +#include "file.h" +#include "fs.h" + + + +typedef struct Arg_t { + char *target; + MainParam_t mp; + ClashHandling_t ch; + Stream_t *sourcefile; +} Arg_t; + +static int dos_showfat(direntry_t *entry, MainParam_t *mp) +{ + Stream_t *File=mp->File; + + fprintPwd(stdout, entry,0); + putchar(' '); + printFat(File); + printf("\n"); + return GOT_ONE; +} + +static int unix_showfat(MainParam_t *mp) +{ + fprintf(stderr,"File does not reside on a Dos fs\n"); + return ERROR_ONE; +} + + +static void usage(void) +{ + fprintf(stderr, + "Mtools version %s, dated %s\n", mversion, mdate); + fprintf(stderr, + "Usage: %s file ...\n", progname); + exit(1); +} + +void mshowfat(int argc, char **argv, int mtype) +{ + Arg_t arg; + int c, ret; + + /* get command line options */ + + init_clash_handling(& arg.ch); + + /* get command line options */ + while ((c = getopt(argc, argv, "")) != EOF) { + switch (c) { + case '?': + usage(); + break; + } + } + + if (argc - optind < 1) + usage(); + + /* only 1 file to copy... */ + init_mp(&arg.mp); + arg.mp.arg = (void *) &arg; + + arg.mp.callback = dos_showfat; + arg.mp.unixcallback = unix_showfat; + + arg.mp.lookupflags = ACCEPT_PLAIN | ACCEPT_DIR | DO_OPEN; + ret=main_loop(&arg.mp, argv + optind, argc - optind); + exit(ret); +} diff --git a/commands/i386/mtools-3.9.7/mtools.c b/commands/i386/mtools-3.9.7/mtools.c new file mode 100755 index 000000000..695b5581c --- /dev/null +++ b/commands/i386/mtools-3.9.7/mtools.c @@ -0,0 +1,186 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "partition.h" +#include "vfat.h" + +const char *progname; + +static const struct dispatch { + const char *cmd; + void (*fn)(int, char **, int); + int type; +} dispatch[] = { + {"attrib",mattrib, 0}, + {"badblocks",mbadblocks, 0}, + {"cat",mcat, 0}, + {"cd",mcd, 0}, + {"copy",mcopy, 0}, + {"del",mdel, 0}, + {"deltree",mdel, 2}, + {"dir",mdir, 0}, + {"doctorfat",mdoctorfat, 0}, + {"du",mdu, 0}, + {"format",mformat, 0}, + {"info", minfo, 0}, + {"label",mlabel, 0}, + {"md",mmd, 0}, + {"mkdir",mmd, 0}, +#ifdef OS_linux + {"mount",mmount, 0}, +#endif + {"partition",mpartition, 0}, + {"rd",mdel, 1}, + {"rmdir",mdel, 1}, + {"read",mcopy, 0}, + {"move",mmove, 0}, + {"ren",mmove, 1}, + {"showfat", mshowfat, 0}, +#ifndef NO_CONFIG + {"toolstest", mtoolstest, 0}, +#endif + {"type",mcopy, 1}, + {"write",mcopy, 0}, +#ifndef OS_Minix + {"zip", mzip, 0} +#endif +}; +#define NDISPATCH (sizeof dispatch / sizeof dispatch[0]) + +int main(int argc,char **argv) +{ + const char *name; + int i; + + init_privs(); +#ifdef __EMX__ + _wildcard(&argc,&argv); +#endif + +/*#define PRIV_TEST*/ + +#ifdef PRIV_TEST + { + int euid; + char command[100]; + + printf("INIT: %d %d\n", getuid(), geteuid()); + drop_privs(); + printf("DROP: %d %d\n", getuid(), geteuid()); + reclaim_privs(); + printf("RECLAIM: %d %d\n", getuid(), geteuid()); + euid = geteuid(); + if(argc & 1) { + drop_privs(); + printf("DROP: %d %d\n", getuid(), geteuid()); + } + if(!((argc-1) & 2)) { + destroy_privs(); + printf("DESTROY: %d %d\n", getuid(), geteuid()); + } + sprintf(command, "a.out %d", euid); + system(command); + return 1; + } +#endif + + +#ifdef __EMX__ + _wildcard(&argc,&argv); +#endif + + + /* check whether the compiler lays out structures in a sane way */ + if(sizeof(struct partition) != 16 || + sizeof(struct directory) != 32 || + sizeof(struct vfat_subentry) !=32) { + fprintf(stderr,"Mtools has not been correctly compiled\n"); + fprintf(stderr,"Recompile it using a more recent compiler\n"); + return 137; + } + +#ifdef __EMX__ + argv[0] = _getname(argv[0]); _remext(argv[0]); name = argv[0]; +#else + name = _basename(argv[0]); +#endif + +#if 0 + /* this allows the different tools to be called as "mtools -c <command>" + ** where <command> is mdir, mdel, mcopy etcetera + ** Mainly done for the BeOS, which doesn't support links yet. + */ + + if(argc >= 3 && + !strcmp(argv[1], "-c") && + !strcmp(name, "mtools")) { + argc-=2; + argv+=2; + name = argv[0]; + } +#endif + + /* print the version */ + if(argc >= 2 && + (strcmp(argv[1], "-V") == 0 || strcmp(argv[1], "--version") ==0)) { + printf("%c%s version %s, dated %s\n", + toupper(name[0]), name+1, + mversion, mdate); + printf("configured with the following options: "); +#ifdef USE_XDF + printf("enable-xdf "); +#else + printf("disable-xdf "); +#endif +#ifdef USING_VOLD + printf("enable-vold "); +#else + printf("disable-vold "); +#endif +#ifdef USING_NEW_VOLD + printf("enable-new-vold "); +#else + printf("disable-new-vold "); +#endif +#ifdef DEBUG + printf("enable-debug "); +#else + printf("disable-debug "); +#endif +#ifdef USE_RAWTERM + printf("enable-raw-term "); +#else + printf("disable-raw-term "); +#endif + printf("\n"); + return 0; + } + + if (argc >= 2 && strcmp(name, "mtools") == 0) { + /* mtools command ... */ + argc--; + argv++; + name = argv[0]; + } + progname = argv[0]; + + read_config(); + setup_signal(); + for (i = 0; i < NDISPATCH; i++) { + if (!strcmp(name,dispatch[i].cmd) + || (name[0] == 'm' && !strcmp(name+1,dispatch[i].cmd))) + dispatch[i].fn(argc, argv, dispatch[i].type); + } + if (strcmp(name,"mtools")) + fprintf(stderr,"Unknown mtools command '%s'\n",name); + fprintf(stderr,"Usage: mtools [-V] command [-options] arguments ...\n"); + fprintf(stderr,"Supported commands:"); + for (i = 0; i < NDISPATCH; i++) { + fprintf(stderr, i%8 == 0 ? "\n\t" : ", "); + fprintf(stderr, "%s", dispatch[i].cmd); + } + putc('\n', stderr); + fprintf(stderr, "Use 'mtools command -?' for help per command\n"); + + return 1; +} diff --git a/commands/i386/mtools-3.9.7/mtools.h b/commands/i386/mtools-3.9.7/mtools.h new file mode 100755 index 000000000..3201eb897 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mtools.h @@ -0,0 +1,234 @@ +#ifndef MTOOLS_MTOOLS_H +#define MTOOLS_MTOOLS_H + +#include "msdos.h" + +#if defined(OS_sco3) +#define MAXPATHLEN 1024 +#include <signal.h> +extern int lockf(int, int, off_t); /* SCO has no proper include file for lockf */ +#endif + +#define SCSI_FLAG 1 +#define PRIV_FLAG 2 +#define NOLOCK_FLAG 4 +#define USE_XDF_FLAG 8 +#define MFORMAT_ONLY_FLAG 16 +#define VOLD_FLAG 32 +#define FLOPPYD_FLAG 64 +#define FILTER_FLAG 128 + +#define IS_SCSI(x) ((x) && ((x)->misc_flags & SCSI_FLAG)) +#define IS_PRIVILEGED(x) ((x) && ((x)->misc_flags & PRIV_FLAG)) +#define IS_NOLOCK(x) ((x) && ((x)->misc_flags & NOLOCK_FLAG)) +#define IS_MFORMAT_ONLY(x) ((x) && ((x)->misc_flags & MFORMAT_ONLY_FLAG)) +#define SHOULD_USE_VOLD(x) ((x)&& ((x)->misc_flags & VOLD_FLAG)) +#define SHOULD_USE_XDF(x) ((x)&& ((x)->misc_flags & USE_XDF_FLAG)) + +typedef struct device { + const char *name; /* full path to device */ + + char *drive; /* the drive letter / device name */ + int fat_bits; /* FAT encoding scheme */ + + unsigned int mode; /* any special open() flags */ + unsigned int tracks; /* tracks */ + unsigned int heads; /* heads */ + unsigned int sectors; /* sectors */ + unsigned int hidden; /* number of hidden sectors. Used for + * mformatting partitioned devices */ + + off_t offset; /* skip this many bytes */ + + unsigned int partition; + + unsigned int misc_flags; + + /* Linux only stuff */ + unsigned int ssize; + unsigned int use_2m; + + char *precmd; /* command to be executed before opening + * the drive */ + + /* internal variables */ + int file_nr; /* used during parsing */ + int blocksize; /* size of disk block in bytes */ + + const char *cfg_filename; /* used for debugging purposes */ +} device_t; + + +#ifndef OS_linux +#define BOOTSIZE 512 +#else +#define BOOTSIZE 256 +#endif + +#include "stream.h" + + +extern const char *short_illegals, *long_illegals; + +#define maximize(target, max) do { \ + if(max < 0) { \ + if(target > 0) \ + target = 0; \ + } else if(target > max) { \ + target = max; \ + } \ +} while(0) + +#define minimize(target, min) do { \ + if(target < min) \ + target = min; \ +} while(0) + +int init_geom(int fd, struct device *dev, struct device *orig_dev, + struct stat *stat); + +int readwrite_sectors(int fd, /* file descriptor */ + int *drive, + int rate, + int seektrack, + int track, int head, int sector, int size, /* address */ + char *data, + int bytes, + int direction, + int retries); + +int lock_dev(int fd, int mode, struct device *dev); + +char *unix_normalize (char *ans, char *name, char *ext); +char *dos_name(char *filename, int verbose, int *mangled, char *buffer); +struct directory *mk_entry(const char *filename, char attr, + unsigned int fat, size_t size, time_t date, + struct directory *ndir); +int copyfile(Stream_t *Source, Stream_t *Target); +int getfreeMinClusters(Stream_t *Stream, size_t ref); + +FILE *opentty(int mode); + +int is_dir(Stream_t *Dir, char *path); +void bufferize(Stream_t **Dir); + +int dir_grow(Stream_t *Dir, int size); +int match(const char *, const char *, char *, int, int); + +char *unix_name(char *name, char *ext, char Case, char *answer); +void *safe_malloc(size_t size); +Stream_t *open_filter(Stream_t *Next); + +extern int got_signal; +/* int do_gotsignal(char *, int); +#define got_signal do_gotsignal(__FILE__, __LINE__) */ + +void setup_signal(void); + + +#define SET_INT(target, source) \ +if(source)target=source + + +UNUSED(static inline int compare (long ref, long testee)) +{ + return (ref && ref != testee); +} + +Stream_t *GetFs(Stream_t *Fs); + +char *label_name(char *filename, int verbose, + int *mangled, char *ans); + +/* environmental variables */ +extern unsigned int mtools_skip_check; +extern unsigned int mtools_fat_compatibility; +extern unsigned int mtools_ignore_short_case; +extern unsigned int mtools_no_vfat; +extern unsigned int mtools_numeric_tail; +extern unsigned int mtools_dotted_dir; +extern unsigned int mtools_twenty_four_hour_clock; +extern char *mtools_date_string; +extern unsigned int mtools_rate_0, mtools_rate_any; +extern int mtools_raw_tty; + +extern int batchmode; + +void read_config(void); +extern struct device *devices; +extern struct device const_devices[]; +extern const int nr_const_devices; + +#define New(type) ((type*)(malloc(sizeof(type)))) +#define Grow(adr,n,type) ((type*)(realloc((char *)adr,n*sizeof(type)))) +#define Free(adr) (free((char *)adr)); +#define NewArray(size,type) ((type*)(calloc((size),sizeof(type)))) + +void mattrib(int argc, char **argv, int type); +void mbadblocks(int argc, char **argv, int type); +void mcat(int argc, char **argv, int type); +void mcd(int argc, char **argv, int type); +void mcopy(int argc, char **argv, int type); +void mdel(int argc, char **argv, int type); +void mdir(int argc, char **argv, int type); +void mdoctorfat(int argc, char **argv, int type); +void mdu(int argc, char **argv, int type); +void mformat(int argc, char **argv, int type); +void minfo(int argc, char **argv, int type); +void mlabel(int argc, char **argv, int type); +void mmd(int argc, char **argv, int type); +void mmount(int argc, char **argv, int type); +void mmove(int argc, char **argv, int type); +void mpartition(int argc, char **argv, int type); +void mshowfat(int argc, char **argv, int mtype); +void mtoolstest(int argc, char **argv, int type); +void mzip(int argc, char **argv, int type); + +extern int noPrivileges; +void init_privs(void); +void reclaim_privs(void); +void drop_privs(void); +void destroy_privs(void); +uid_t get_real_uid(void); +void closeExec(int fd); + +extern const char *progname; + +void precmd(struct device *dev); + +void print_sector(char *message, unsigned char *data, int size); +time_t getTimeNow(time_t *now); + +#ifdef USING_NEW_VOLD +char *getVoldName(struct device *dev, char *name); +#endif + + +Stream_t *OpenDir(Stream_t *Parent, const char *filename); +/* int unix_dir_loop(Stream_t *Stream, MainParam_t *mp); +int unix_loop(MainParam_t *mp, char *arg); */ + +struct dirCache_t **getDirCacheP(Stream_t *Stream); +int isRootDir(Stream_t *Stream); +unsigned int getStart(Stream_t *Dir, struct directory *dir); +unsigned int countBlocks(Stream_t *Dir, unsigned int block); +char *getDrive(Stream_t *Stream); + + +void printOom(void); +int ask_confirmation(const char *, const char *, const char *); +char *get_homedir(void); +#define EXPAND_BUF 2048 +const char *expand(const char *, char *); +const char *fix_mcwd(char *); +FILE *open_mcwd(const char *mode); +void unlink_mcwd(void); +char *skip_drive(const char *path); +char *get_drive(const char *path, const char *def); + +int safePopenOut(char **command, char *output, int len); + +#define ROUND_DOWN(value, grain) ((value) - (value) % (grain)) +#define ROUND_UP(value, grain) ROUND_DOWN((value) + (grain)-1, (grain)) + +#endif diff --git a/commands/i386/mtools-3.9.7/mtoolsDirent.h b/commands/i386/mtools-3.9.7/mtoolsDirent.h new file mode 100755 index 000000000..d0fa7ccd4 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mtoolsDirent.h @@ -0,0 +1,40 @@ +#ifndef MTOOLS_DIRENTRY_H +#define MTOOLS_DIRENTRY_H + +#include "sysincludes.h" +#include "vfat.h" + +typedef struct direntry_t { + struct Stream_t *Dir; + /* struct direntry_t *parent; parent level */ + int entry; /* slot in parent directory (-3 if root) */ + struct directory dir; /* descriptor in parent directory (random if + * root)*/ + char name[MAX_VNAMELEN+1]; /* name in its parent directory, or + * NULL if root */ + int beginSlot; /* begin and end slot, for delete */ + int endSlot; +} direntry_t; + +#include "stream.h" + +int vfat_lookup(direntry_t *entry, const char *filename, int length, + int flags, char *shortname, char *longname); + +struct directory *dir_read(direntry_t *entry, int *error); + +void initializeDirentry(direntry_t *entry, struct Stream_t *Dir); +int isNotFound(direntry_t *entry); +direntry_t *getParent(direntry_t *entry); +void dir_write(direntry_t *entry); +void low_level_dir_write(direntry_t *entry); +int fatFreeWithDirentry(direntry_t *entry); +int labelit(char *dosname, + char *longname, + void *arg0, + direntry_t *entry); +int isSubdirOf(Stream_t *inside, Stream_t *outside); +char *getPwd(direntry_t *entry); +void fprintPwd(FILE *f, direntry_t *entry, int escape); +int write_vfat(Stream_t *, char *, char *, int, direntry_t *); +#endif diff --git a/commands/i386/mtools-3.9.7/mtoolsPaths.h b/commands/i386/mtools-3.9.7/mtoolsPaths.h new file mode 100755 index 000000000..bd79a5c29 --- /dev/null +++ b/commands/i386/mtools-3.9.7/mtoolsPaths.h @@ -0,0 +1,32 @@ +/* + * Paths of the configuration files. + * This file may be changed by the user as needed. + * There are three empty lines between each definition. + * These ensure that "local" patches and official patches have + * only a very low probability of conflicting. + */ + + +#define CONF_FILE "/etc/mtools.conf" + + +#define OLD_CONF_FILE "/etc/mtools" + + + +#define LOCAL_CONF_FILE "/etc/default/mtools.conf" +/* Use this if you like to keep the configuration file in a non-standard + * place such as /etc/default, /opt/etc, /usr/etc, /usr/local/etc ... + */ + +#define SYS_CONF_FILE SYSCONFDIR "/mtools.conf" + +#define OLD_LOCAL_CONF_FILE "/etc/default/mtools" + + + +#define CFG_FILE1 "/.mtoolsrc" + + + +/* END */ diff --git a/commands/i386/mtools-3.9.7/nameclash.h b/commands/i386/mtools-3.9.7/nameclash.h new file mode 100755 index 000000000..50e512b6b --- /dev/null +++ b/commands/i386/mtools-3.9.7/nameclash.h @@ -0,0 +1,57 @@ +#ifndef MTOOLS_NAMECLASH_H +#define MTOOLS_NAMECLASH_H + +#include "stream.h" + +typedef enum clash_action { + NAMEMATCH_NONE, + NAMEMATCH_AUTORENAME, + NAMEMATCH_QUIT, + NAMEMATCH_SKIP, + NAMEMATCH_RENAME, + NAMEMATCH_PRENAME, /* renaming of primary name */ + NAMEMATCH_OVERWRITE, + NAMEMATCH_ERROR, + NAMEMATCH_SUCCESS, + NAMEMATCH_GREW +} clash_action; + +/* clash handling structure */ +typedef struct ClashHandling_t { + clash_action action[2]; + clash_action namematch_default[2]; + + int nowarn; /* Don't ask, just do default action if name collision*/ + int got_slots; + int mod_time; + /* unsigned int dot; */ + char *myname; + unsigned char *dosname; + int single; + + int use_longname; + int ignore_entry; + int source; /* to prevent the source from overwriting itself */ + int source_entry; /* to account for the space freed up by the original + * name */ + char * (*name_converter)(char *filename, int verbose, + int *mangled, char *ans); +} ClashHandling_t; + +/* write callback */ +typedef int (write_data_callback)(char *,char *, void *, struct direntry_t *); + +int mwrite_one(Stream_t *Dir, + const char *argname, + const char *shortname, + write_data_callback *cb, + void *arg, + ClashHandling_t *ch); + +int handle_clash_options(ClashHandling_t *ch, char c); +void init_clash_handling(ClashHandling_t *ch); +Stream_t *createDir(Stream_t *Dir, const char *filename, ClashHandling_t *ch, + unsigned char attr, time_t mtime); + + +#endif diff --git a/commands/i386/mtools-3.9.7/partition.h b/commands/i386/mtools-3.9.7/partition.h new file mode 100755 index 000000000..14385e7e6 --- /dev/null +++ b/commands/i386/mtools-3.9.7/partition.h @@ -0,0 +1,31 @@ +typedef struct hsc { + unsigned char byte0; + unsigned char head; /* starting head */ + unsigned char sector; /* starting sector */ + unsigned char cyl; /* starting cylinder */ +} hsc; + +#define head(x) ((x).head) +#define sector(x) ((x).sector & 0x3f) +#define cyl(x) ((x).cyl | (((x).sector & 0xc0)<<2)) + +#define BEGIN(p) _DWORD((p).start_sect) +#define END(p) (_DWORD((p).start_sect)+(_DWORD((p).nr_sects))) + + +struct partition { + hsc start; + hsc end; + unsigned char start_sect[4]; /* starting sector counting from 0 */ + unsigned char nr_sects[4]; /* nr of sectors in partition */ +}; + +#define boot_ind start.byte0 +#define sys_ind end.byte0 + +int consistencyCheck(struct partition *partTable, int doprint, int verbose, + int *has_activated, int *last_end, int *j, + struct device *used_dev, int target_partition); + +void setBeginEnd(struct partition *partTable, int begin, int end, + int heads, int sector, int activate, int type); diff --git a/commands/i386/mtools-3.9.7/patchlevel.c b/commands/i386/mtools-3.9.7/patchlevel.c new file mode 100755 index 000000000..d6a37a073 --- /dev/null +++ b/commands/i386/mtools-3.9.7/patchlevel.c @@ -0,0 +1,2 @@ +const char *mversion="3.9.7"; +const char *mdate = "1 jun 2000"; diff --git a/commands/i386/mtools-3.9.7/plain_io.c b/commands/i386/mtools-3.9.7/plain_io.c new file mode 100755 index 000000000..e32af9f8d --- /dev/null +++ b/commands/i386/mtools-3.9.7/plain_io.c @@ -0,0 +1,749 @@ +/* + * Io to a plain file or device + * + * written by: + * + * Alain L. Knaff + * alain@linux.lu + * + */ + +#include "sysincludes.h" +#include "stream.h" +#include "mtools.h" +#include "msdos.h" +#include "plain_io.h" +#include "scsi.h" +#include "partition.h" +#include "llong.h" + +typedef struct SimpleFile_t { + Class_t *Class; + int refs; + Stream_t *Next; + Stream_t *Buffer; + struct stat stat; + int fd; + mt_off_t offset; + mt_off_t lastwhere; + int seekable; + int privileged; +#ifdef OS_hpux + int size_limited; +#endif + int scsi_sector_size; + void *extra_data; /* extra system dependant information for scsi */ +} SimpleFile_t; + + +/* + * Create an advisory lock on the device to prevent concurrent writes. + * Uses either lockf, flock, or fcntl locking methods. See the Makefile + * and the Configure files for how to specify the proper method. + */ + +int lock_dev(int fd, int mode, struct device *dev) +{ +#if (defined(HAVE_FLOCK) && defined (LOCK_EX) && defined(LOCK_NB)) + /**/ +#else /* FLOCK */ + +#if (defined(HAVE_LOCKF) && defined(F_TLOCK)) + /**/ +#else /* LOCKF */ + +#if (defined(F_SETLK) && defined(F_WRLCK)) + struct flock flk; + +#endif /* FCNTL */ +#endif /* LOCKF */ +#endif /* FLOCK */ + + if(IS_NOLOCK(dev)) + return 0; + +#if (defined(HAVE_FLOCK) && defined (LOCK_EX) && defined(LOCK_NB)) + if (flock(fd, (mode ? LOCK_EX : LOCK_SH)|LOCK_NB) < 0) +#else /* FLOCK */ + +#if (defined(HAVE_LOCKF) && defined(F_TLOCK)) + if (mode && lockf(fd, F_TLOCK, 0) < 0) +#else /* LOCKF */ + +#if (defined(F_SETLK) && defined(F_WRLCK)) + flk.l_type = mode ? F_WRLCK : F_RDLCK; + flk.l_whence = 0; + flk.l_start = 0L; + flk.l_len = 0L; + + if (fcntl(fd, F_SETLK, &flk) < 0) +#endif /* FCNTL */ +#endif /* LOCKF */ +#endif /* FLOCK */ + { + if(errno == EINVAL +#ifdef EOPNOTSUPP + || errno == EOPNOTSUPP +#endif + ) + return 0; + else + return 1; + } + return 0; +} + +typedef int (*iofn) (int, char *, int); + + + +static int file_io(Stream_t *Stream, char *buf, mt_off_t where, int len, + iofn io) +{ + DeclareThis(SimpleFile_t); + int ret; + + where += This->offset; + + if (This->seekable && where != This->lastwhere ){ + if(mt_lseek( This->fd, where, SEEK_SET) < 0 ){ + perror("seek"); + This->lastwhere = (mt_off_t) -1; + return -1; + } + } + +#ifdef OS_hpux + /* + * On HP/UX, we can not write more than MAX_LEN bytes in one go. + * If more are written, the write fails with EINVAL + */ + #define MAX_SCSI_LEN (127*1024) + if(This->size_limited && len > MAX_SCSI_LEN) + len = MAX_SCSI_LEN; +#endif + ret = io(This->fd, buf, len); + +#ifdef OS_hpux + if (ret == -1 && + errno == EINVAL && /* if we got EINVAL */ + len > MAX_SCSI_LEN) { + This->size_limited = 1; + len = MAX_SCSI_LEN; + ret = io(This->fd, buf, len); + } +#endif + + if ( ret == -1 ){ + perror("plain_io"); + This->lastwhere = (mt_off_t) -1; + return -1; + } + This->lastwhere = where + ret; + return ret; +} + + + +static int file_read(Stream_t *Stream, char *buf, mt_off_t where, size_t len) +{ + return file_io(Stream, buf, where, len, (iofn) read); +} + +static int file_write(Stream_t *Stream, char *buf, mt_off_t where, size_t len) +{ + return file_io(Stream, buf, where, len, (iofn) write); +} + +static int file_flush(Stream_t *Stream) +{ +#if 0 + DeclareThis(SimpleFile_t); + + return fsync(This->fd); +#endif + return 0; +} + +static int file_free(Stream_t *Stream) +{ + DeclareThis(SimpleFile_t); + + if (This->fd > 2) + return close(This->fd); + else + return 0; +} + +static int file_geom(Stream_t *Stream, struct device *dev, + struct device *orig_dev, + int media, struct bootsector *boot) +{ + int ret; + DeclareThis(SimpleFile_t); + size_t tot_sectors; + int BootP, Infp0, InfpX, InfTm; + int sectors, j; + unsigned char sum; + int sect_per_track; + struct label_blk_t *labelBlock; + + dev->ssize = 2; /* allow for init_geom to change it */ + dev->use_2m = 0x80; /* disable 2m mode to begin */ + + if(media == 0xf0 || media >= 0x100){ + dev->heads = WORD(nheads); + dev->sectors = WORD(nsect); + tot_sectors = DWORD(bigsect); + SET_INT(tot_sectors, WORD(psect)); + sect_per_track = dev->heads * dev->sectors; + tot_sectors += sect_per_track - 1; /* round size up */ + dev->tracks = tot_sectors / sect_per_track; + + BootP = WORD(ext.old.BootP); + Infp0 = WORD(ext.old.Infp0); + InfpX = WORD(ext.old.InfpX); + InfTm = WORD(ext.old.InfTm); + + if(WORD(fatlen)) { + labelBlock = &boot->ext.old.labelBlock; + } else { + labelBlock = &boot->ext.fat32.labelBlock; + } + + if (boot->descr >= 0xf0 && + labelBlock->dos4 == 0x29 && + strncmp( boot->banner,"2M", 2 ) == 0 && + BootP < 512 && Infp0 < 512 && InfpX < 512 && InfTm < 512 && + BootP >= InfTm + 2 && InfTm >= InfpX && InfpX >= Infp0 && + Infp0 >= 76 ){ + for (sum=0, j=63; j < BootP; j++) + sum += boot->jump[j];/* checksum */ + dev->ssize = boot->jump[InfTm]; + if (!sum && dev->ssize <= 7){ + dev->use_2m = 0xff; + dev->ssize |= 0x80; /* is set */ + } + } + } else if (media >= 0xf8){ + media &= 3; + dev->heads = old_dos[media].heads; + dev->tracks = old_dos[media].tracks; + dev->sectors = old_dos[media].sectors; + dev->ssize = 0x80; + dev->use_2m = ~1; + } else { + fprintf(stderr,"Unknown media type\n"); + exit(1); + } + + sectors = dev->sectors; + dev->sectors = dev->sectors * WORD(secsiz) / 512; + +#ifdef JPD + printf("file_geom:media=%0X=>cyl=%d,heads=%d,sects=%d,ssize=%d,use2m=%X\n", + media, dev->tracks, dev->heads, dev->sectors, dev->ssize, + dev->use_2m); +#endif + ret = init_geom(This->fd,dev, orig_dev, &This->stat); + dev->sectors = sectors; +#ifdef JPD + printf("f_geom: after init_geom(), sects=%d\n", dev->sectors); +#endif + return ret; +} + + +static int file_data(Stream_t *Stream, time_t *date, mt_size_t *size, + int *type, int *address) +{ + DeclareThis(SimpleFile_t); + + if(date) + *date = This->stat.st_mtime; + if(size) + *size = This->stat.st_size; + if(type) + *type = S_ISDIR(This->stat.st_mode); + if(address) + *address = 0; + return 0; +} + +/* ZIP or other scsi device on Solaris or SunOS system. + Since Sun won't accept a non-Sun label on a scsi disk, we must + bypass Sun's disk interface and use low-level SCSI commands to read + or write the ZIP drive. We thus replace the file_read and file_write + routines with our own scsi_read and scsi_write routines, that use the + uscsi ioctl interface. By James Dugal, jpd@usl.edu, 11-96. Tested + under Solaris 2.5 and SunOS 4.3.1_u1 using GCC. + + Note: the mtools.conf entry for a ZIP drive would look like this: +(solaris) drive C: file="/dev/rdsk/c0t5d0s2" partition=4 FAT=16 nodelay exclusive scsi=& +(sunos) drive C: file="/dev/rsd5c" partition=4 FAT=16 nodelay exclusive scsi=1 + + Note 2: Sol 2.5 wants mtools to be suid-root, to use the ioctl. SunOS is + happy if we just have access to the device, so making mtools sgid to a + group called, say, "ziprw" which has rw permission on /dev/rsd5c, is fine. + */ + +#define MAXBLKSPERCMD 255 + +static void scsi_init(SimpleFile_t *This) +{ + int fd = This->fd; + unsigned char cdb[10],buf[8]; + + memset(cdb, 0, sizeof cdb); + memset(buf,0, sizeof(buf)); + cdb[0]=SCSI_READ_CAPACITY; + if (scsi_cmd(fd, (unsigned char *)cdb, + sizeof(cdb), SCSI_IO_READ, buf, sizeof(buf), This->extra_data)==0) + { + This->scsi_sector_size= + ((unsigned)buf[5]<<16)|((unsigned)buf[6]<<8)|(unsigned)buf[7]; + if (This->scsi_sector_size != 512) + fprintf(stderr," (scsi_sector_size=%d)\n",This->scsi_sector_size); + } +} + +int scsi_io(Stream_t *Stream, char *buf, mt_off_t where, size_t len, int rwcmd) +{ + unsigned int firstblock, nsect; + int clen,r,max; + off_t offset; + unsigned char cdb[10]; + DeclareThis(SimpleFile_t); + + firstblock=truncBytes32((where + This->offset)/This->scsi_sector_size); + /* 512,1024,2048,... bytes/sector supported */ + offset=truncBytes32(where + This->offset - + firstblock*This->scsi_sector_size); + nsect=(offset+len+This->scsi_sector_size-1)/ This->scsi_sector_size; +#if defined(OS_sun) && defined(OS_i386) + if (This->scsi_sector_size>512) + firstblock*=This->scsi_sector_size/512; /* work around a uscsi bug */ +#endif /* sun && i386 */ + + if (len>512) { + /* avoid buffer overruns. The transfer MUST be smaller or + * equal to the requested size! */ + while (nsect*This->scsi_sector_size>len) + --nsect; + if(!nsect) { + fprintf(stderr,"Scsi buffer too small\n"); + exit(1); + } + if(rwcmd == SCSI_IO_WRITE && offset) { + /* there seems to be no memmove before a write */ + fprintf(stderr,"Unaligned write\n"); + exit(1); + } + /* a better implementation should use bounce buffers. + * However, in normal operation no buffer overruns or + * unaligned writes should happen anyways, as the logical + * sector size is (hopefully!) equal to the physical one + */ + } + + + max = scsi_max_length(); + + if (nsect > max) + nsect=max; + + /* set up SCSI READ/WRITE command */ + memset(cdb, 0, sizeof cdb); + + switch(rwcmd) { + case SCSI_IO_READ: + cdb[0] = SCSI_READ; + break; + case SCSI_IO_WRITE: + cdb[0] = SCSI_WRITE; + break; + } + + cdb[1] = 0; + + if (firstblock > 0x1fffff || nsect > 0xff) { + /* I suspect that the ZIP drive also understands Group 1 + * commands. If that is indeed true, we may chose Group 1 + * more agressively in the future */ + + cdb[0] |= SCSI_GROUP1; + clen=10; /* SCSI Group 1 cmd */ + + /* this is one of the rare case where explicit coding is + * more portable than macros... The meaning of scsi command + * bytes is standardised, whereas the preprocessor macros + * handling it might be not... */ + + cdb[2] = (unsigned char) (firstblock >> 24) & 0xff; + cdb[3] = (unsigned char) (firstblock >> 16) & 0xff; + cdb[4] = (unsigned char) (firstblock >> 8) & 0xff; + cdb[5] = (unsigned char) firstblock & 0xff; + cdb[6] = 0; + cdb[7] = (unsigned char) (nsect >> 8) & 0xff; + cdb[8] = (unsigned char) nsect & 0xff; + cdb[9] = 0; + } else { + clen = 6; /* SCSI Group 0 cmd */ + cdb[1] |= (unsigned char) ((firstblock >> 16) & 0x1f); + cdb[2] = (unsigned char) ((firstblock >> 8) & 0xff); + cdb[3] = (unsigned char) firstblock & 0xff; + cdb[4] = (unsigned char) nsect; + cdb[5] = 0; + } + + if(This->privileged) + reclaim_privs(); + + r=scsi_cmd(This->fd, (unsigned char *)cdb, clen, rwcmd, buf, + nsect*This->scsi_sector_size, This->extra_data); + + if(This->privileged) + drop_privs(); + + if(r) { + perror(rwcmd == SCSI_IO_READ ? "SCMD_READ" : "SCMD_WRITE"); + return -1; + } +#ifdef JPD + printf("finished %u for %u\n", firstblock, nsect); +#endif + +#ifdef JPD + printf("zip: read or write OK\n"); +#endif + if (offset>0) memmove(buf,buf+offset,nsect*This->scsi_sector_size-offset); + if (len==256) return 256; + else if (len==512) return 512; + else return nsect*This->scsi_sector_size-offset; +} + +int scsi_read(Stream_t *Stream, char *buf, mt_off_t where, size_t len) +{ + +#ifdef JPD + printf("zip: to read %d bytes at %d\n", len, where); +#endif + return scsi_io(Stream, buf, where, len, SCSI_IO_READ); +} + +int scsi_write(Stream_t *Stream, char *buf, mt_off_t where, size_t len) +{ +#ifdef JPD + Printf("zip: to write %d bytes at %d\n", len, where); +#endif + return scsi_io(Stream, buf, where, len, SCSI_IO_WRITE); +} + +static Class_t ScsiClass = { + scsi_read, + scsi_write, + file_flush, + file_free, + file_geom, + file_data, + 0 /* pre-allocate */ +}; + + +static Class_t SimpleFileClass = { + file_read, + file_write, + file_flush, + file_free, + file_geom, + file_data, + 0 /* pre_allocate */ +}; + + +Stream_t *SimpleFileOpen(struct device *dev, struct device *orig_dev, + const char *name, int mode, char *errmsg, + int mode2, int locked, mt_size_t *maxSize) +{ + SimpleFile_t *This; +#ifdef __EMX__ +HFILE FileHandle; +ULONG Action; +APIRET rc; +#endif + This = New(SimpleFile_t); + if (!This){ + printOom(); + return 0; + } + This->scsi_sector_size = 512; + This->seekable = 1; +#ifdef OS_hpux + This->size_limited = 0; +#endif + This->Class = &SimpleFileClass; + if (!name || strcmp(name,"-") == 0 ){ + if (mode == O_RDONLY) + This->fd = 0; + else + This->fd = 1; + This->seekable = 0; + This->refs = 1; + This->Next = 0; + This->Buffer = 0; + if (fstat(This->fd, &This->stat) < 0) { + Free(This); + if(errmsg) +#ifdef HAVE_SNPRINTF + snprintf(errmsg,199,"Can't stat -: %s", + strerror(errno)); +#else + sprintf(errmsg,"Can't stat -: %s", + strerror(errno)); +#endif + return NULL; + } + + return (Stream_t *) This; + } + + + if(dev) { + if(!(mode2 & NO_PRIV)) + This->privileged = IS_PRIVILEGED(dev); + mode |= dev->mode; + } + + precmd(dev); + if(IS_PRIVILEGED(dev) && !(mode2 & NO_PRIV)) + reclaim_privs(); + +#ifdef __EMX__ +#define DOSOPEN_FLAGS (OPEN_FLAGS_DASD | OPEN_FLAGS_WRITE_THROUGH | \ + OPEN_FLAGS_NOINHERIT | OPEN_FLAGS_RANDOM | \ + OPEN_FLAGS_NO_CACHE) +#define DOSOPEN_FD_ACCESS (OPEN_SHARE_DENYREADWRITE | OPEN_ACCESS_READWRITE) +#define DOSOPEN_HD_ACCESS (OPEN_SHARE_DENYNONE | OPEN_ACCESS_READONLY) + + if (skip_drive(name) > name) { + rc = DosOpen( + name, &FileHandle, &Action, 0L, FILE_NORMAL, + OPEN_ACTION_OPEN_IF_EXISTS, DOSOPEN_FLAGS | + (IS_NOLOCK(dev)?DOSOPEN_HD_ACCESS:DOSOPEN_FD_ACCESS), + 0L); +#ifdef DEBUG + if (rc != NO_ERROR) fprintf (stderr, "DosOpen() returned %d\n", rc); +#endif + if (!IS_NOLOCK(dev)) { + rc = DosDevIOCtl( + FileHandle, 0x08L, DSK_LOCKDRIVE, 0, 0, 0, 0, 0, 0); +#ifdef DEBUG + if (rc != NO_ERROR) fprintf (stderr, "DosDevIOCtl() returned %d\n", rc); +#endif + } + if (rc == NO_ERROR) + This->fd = _imphandle(FileHandle); else This->fd = -1; + } else +#endif + { + if (IS_SCSI(dev)) + This->fd = scsi_open(name, mode, IS_NOLOCK(dev)?0444:0666, + &This->extra_data); + else + This->fd = open(name, mode, IS_NOLOCK(dev)?0444:0666); + } + + if(IS_PRIVILEGED(dev) && !(mode2 & NO_PRIV)) + drop_privs(); + + if (This->fd < 0) { + Free(This); + if(errmsg) +#ifdef HAVE_SNPRINTF + snprintf(errmsg, 199, "Can't open %s: %s", + name, strerror(errno)); +#else + sprintf(errmsg, "Can't open %s: %s", + name, strerror(errno)); +#endif + return NULL; + } + + if(IS_PRIVILEGED(dev) && !(mode2 & NO_PRIV)) + closeExec(This->fd); + +#ifdef __EMX__ + if (*(name+1) != ':') +#endif + if (fstat(This->fd, &This->stat) < 0){ + Free(This); + if(errmsg) { +#ifdef HAVE_SNPRINTF + snprintf(errmsg,199,"Can't stat %s: %s", + name, strerror(errno)); +#else + if(strlen(name) > 50) { + sprintf(errmsg,"Can't stat file: %s", + strerror(errno)); + } else { + sprintf(errmsg,"Can't stat %s: %s", + name, strerror(errno)); + } +#endif + } + return NULL; + } +#ifndef __EMX__ + /* lock the device on writes */ + if (locked && lock_dev(This->fd, mode == O_RDWR, dev)) { + if(errmsg) +#ifdef HAVE_SNPRINTF + snprintf(errmsg,199, + "plain floppy: device \"%s\" busy (%s):", + dev ? dev->name : "unknown", strerror(errno)); +#else + sprintf(errmsg, + "plain floppy: device \"%s\" busy (%s):", + (dev && strlen(dev->name) < 50) ? + dev->name : "unknown", strerror(errno)); +#endif + + close(This->fd); + Free(This); + return NULL; + } +#endif + /* set default parameters, if needed */ + if (dev){ + if ((IS_MFORMAT_ONLY(dev) || !dev->tracks) && + init_geom(This->fd, dev, orig_dev, &This->stat)){ + close(This->fd); + Free(This); + if(errmsg) + sprintf(errmsg,"init: set default params"); + return NULL; + } + This->offset = (mt_off_t) dev->offset; + } else + This->offset = 0; + + This->refs = 1; + This->Next = 0; + This->Buffer = 0; + + if(maxSize) { + if (IS_SCSI(dev)) { + *maxSize = MAX_OFF_T_B(31+log_2(This->scsi_sector_size)); + } else { + *maxSize = max_off_t_seek; + } + if(This->offset > *maxSize) { + close(This->fd); + Free(This); + if(errmsg) + sprintf(errmsg,"init: Big disks not supported"); + return NULL; + } + + *maxSize -= This->offset; + } + /* partitioned drive */ + + /* jpd@usl.edu: assume a partitioned drive on these 2 systems is a ZIP*/ + /* or similar drive that must be accessed by low-level scsi commands */ + /* AK: introduce new "scsi=1" statement to specifically set + * this option. Indeed, there could conceivably be partitioned + * devices where low level scsi commands will not be needed */ + if(IS_SCSI(dev)) { + This->Class = &ScsiClass; + if(This->privileged) + reclaim_privs(); + scsi_init(This); + if(This->privileged) + drop_privs(); + } + while(!(mode2 & NO_OFFSET) && + dev && dev->partition && dev->partition <= 4) { + int has_activated, last_end, j; + unsigned char buf[2048]; + struct partition *partTable=(struct partition *)(buf+ 0x1ae); + size_t partOff; + + /* read the first sector, or part of it */ + if (force_read((Stream_t *)This, (char*) buf, 0, 512) != 512) + break; + if( _WORD(buf+510) != 0xaa55) + break; + + partOff = BEGIN(partTable[dev->partition]); + if (maxSize) { + if (partOff > *maxSize >> 9) { + close(This->fd); + Free(This); + if(errmsg) + sprintf(errmsg,"init: Big disks not supported"); + return NULL; + } + *maxSize -= (mt_off_t) partOff << 9; + } + + This->offset += (mt_off_t) partOff << 9; + if(!partTable[dev->partition].sys_ind) { + if(errmsg) + sprintf(errmsg, + "init: non-existant partition"); + close(This->fd); + Free(This); + return NULL; + } + + if(!dev->tracks) { + dev->heads = head(partTable[dev->partition].end)+1; + dev->sectors = sector(partTable[dev->partition].end); + dev->tracks = cyl(partTable[dev->partition].end) - + cyl(partTable[dev->partition].start)+1; + } + dev->hidden=dev->sectors*head(partTable[dev->partition].start); + if(!mtools_skip_check && + consistencyCheck((struct partition *)(buf+0x1ae), 0, 0, + &has_activated, &last_end, &j, dev, 0)) { + fprintf(stderr, + "Warning: inconsistent partition table\n"); + fprintf(stderr, + "Possibly unpartitioned device\n"); + fprintf(stderr, + "\n*** Maybe try without partition=%d in " + "device definition ***\n\n", + dev->partition); + fprintf(stderr, + "If this is a PCMCIA card, or a disk " + "partitioned on another computer, this " + "message may be in error: add " + "mtools_skip_check=1 to your .mtoolsrc " + "file to suppress this warning\n"); + + } + break; + /* NOTREACHED */ + } + + This->lastwhere = -This->offset; + /* provoke a seek on those devices that don't start on a partition + * boundary */ + + return (Stream_t *) This; +} + +int get_fd(Stream_t *Stream) +{ + DeclareThis(SimpleFile_t); + + return This->fd; +} + +void *get_extra_data(Stream_t *Stream) +{ + DeclareThis(SimpleFile_t); + + return This->extra_data; +} diff --git a/commands/i386/mtools-3.9.7/plain_io.h b/commands/i386/mtools-3.9.7/plain_io.h new file mode 100755 index 000000000..6f86caf22 --- /dev/null +++ b/commands/i386/mtools-3.9.7/plain_io.h @@ -0,0 +1,21 @@ +#ifndef MTOOLS_PLAINIO_H +#define MTOOLS_PLAINIO_H + +#include "stream.h" +#include "msdos.h" +#ifdef __EMX__ +#include <io.h> +#endif + +/* plain io */ +#define NO_PRIV 1 +#define NO_OFFSET 2 + +Stream_t *SimpleFileOpen(struct device *dev, struct device *orig_dev, + const char *name, int mode, char *errmsg, int mode2, + int locked, mt_size_t *maxSize); +int check_parameters(struct device *ref, struct device *testee); + +int get_fd(Stream_t *Stream); +void *get_extra_data(Stream_t *Stream); +#endif diff --git a/commands/i386/mtools-3.9.7/precmd.c b/commands/i386/mtools-3.9.7/precmd.c new file mode 100755 index 000000000..30e2ffdc1 --- /dev/null +++ b/commands/i386/mtools-3.9.7/precmd.c @@ -0,0 +1,31 @@ +/* + * Do filename expansion with the shell. + */ + +#define EXPAND_BUF 2048 + +#include "sysincludes.h" +#include "mtools.h" + +void precmd(struct device *dev) +{ + int status; + pid_t pid; + + if(!dev || !dev->precmd) + return; + + switch((pid=fork())){ + case -1: + perror("Could not fork"); + exit(1); + break; + case 0: /* the son */ + execl("/bin/sh", "sh", "-c", dev->precmd, 0); + break; + default: + wait(&status); + break; + } +} + diff --git a/commands/i386/mtools-3.9.7/privileges.c b/commands/i386/mtools-3.9.7/privileges.c new file mode 100755 index 000000000..edb51fdbc --- /dev/null +++ b/commands/i386/mtools-3.9.7/privileges.c @@ -0,0 +1,166 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" + +/*#define PRIV_DEBUG*/ + +#if 0 +#undef HAVE_SETEUID +#define HAVE_SETRESUID +#include <asm/unistd.h> +int setresuid(int a, int b, int c) +{ + syscall(164, a, b, c); + +} +#endif + +static inline void print_privs(const char *message) +{ +#ifdef PRIV_DEBUG + /* for debugging purposes only */ + fprintf(stderr,"%s egid=%d rgid=%d\n", message, getegid(), getgid()); + fprintf(stderr,"%s euid=%d ruid=%d\n", message, geteuid(), getuid()); +#endif +} + +int noPrivileges=0; + + +static gid_t rgid, egid; +static uid_t ruid, euid; + +/* privilege management routines for SunOS and Solaris. These are + * needed in order to issue raw SCSI read/write ioctls. Mtools drops + * its privileges at the beginning, and reclaims them just for the + * above-mentioned ioctl's. Before popen(), exec() or system, it + * drops its privileges completely, and issues a warning. + */ + + +/* group id handling is lots easyer, as long as we don't use group 0. + * If you want to use group id's, create a *new* group mtools or + * floppy. Chgrp any devices that you only want to be accessible to + * mtools to this group, and give them the appropriate privs. Make + * sure this group doesn't own any other files: be aware that any user + * with access to mtools may mformat these files! + */ + + +static inline void Setuid(uid_t uid) +{ +#if defined HAVE_SETEUID || defined HAVE_SETRESUID + if(euid == 0) { +#ifdef HAVE_SETEUID + seteuid(uid); +#else + setresuid(ruid, uid, euid); +#endif + } else +#endif + setuid(uid); +} + +/* In reclaim_privs and drop privs, we have to manipulate group privileges + * when having no root privileges, else we might lose them */ + +void reclaim_privs(void) +{ + if(noPrivileges) + return; + setgid(egid); + Setuid(euid); + print_privs("after reclaim privs, both uids should be 0 "); +} + +void drop_privs(void) +{ + Setuid(ruid); + setgid(rgid); + print_privs("after drop_privs, real should be 0, effective should not "); +} + +void destroy_privs(void) +{ + +#if defined HAVE_SETEUID || defined HAVE_SETRESUID + if(euid == 0) { +#ifdef HAVE_SETEUID + setuid(0); /* get the necessary privs to drop real root id */ + setuid(ruid); /* this should be enough to get rid of the three + * ids */ + seteuid(ruid); /* for good measure... just in case we came + * accross a system which implemented sane + * semantics instead of POSIXly broken + * semantics for setuid */ +#else + setresuid(ruid, ruid, ruid); +#endif + } +#endif + + /* we also destroy group privileges */ + drop_privs(); + + /* saved set [ug]id will go away by itself on exec */ + + print_privs("destroy_privs, no uid should be zero "); +} + + +uid_t get_real_uid(void) +{ + return ruid; +} + +void init_privs(void) +{ + euid = geteuid(); + ruid = getuid(); + egid = getegid(); + rgid = getgid(); + +#ifndef F_SETFD + if(euid != ruid) { + fprintf(stderr, + "Setuid installation not supported on this platform\n"); + fprintf(stderr, + "Missing F_SETFD"); + exit(1); + } +#endif + + if(euid == 0 && ruid != 0) { +#ifdef HAVE_SETEUID + setuid(0); /* set real uid to 0 */ +#else +#ifndef HAVE_SETRESUID + /* on this machine, it is not possible to reversibly drop + * root privileges. We print an error and quit */ + + /* BEOS is no longer a special case, as both euid and ruid + * return 0, and thus we do not get any longer into this + * branch */ + fprintf(stderr, + "Seteuid call not supported on this architecture.\n"); + fprintf(stderr, + "Mtools cannot be installed setuid root.\n"); + fprintf(stderr, + "However, it can be installed setuid to a non root"); + fprintf(stderr, + "user or setgid to any id.\n"); + exit(1); +#endif +#endif + } + + drop_privs(); + print_privs("after init, real should be 0, effective should not "); +} + +void closeExec(int fd) +{ +#ifdef F_SETFD + fcntl(fd, F_SETFD, 1); +#endif +} diff --git a/commands/i386/mtools-3.9.7/scsi.c b/commands/i386/mtools-3.9.7/scsi.c new file mode 100755 index 000000000..d394f8c4c --- /dev/null +++ b/commands/i386/mtools-3.9.7/scsi.c @@ -0,0 +1,274 @@ +/* + * scsi.c + * Iomega Zip/Jaz drive tool + * change protection mode and eject disk + */ + +/* scis.c by Markus Gyger <mgyger@itr.ch> */ +/* This code is based on ftp://gear.torque.net/pub/ziptool.c */ +/* by Grant R. Guenther with the following copyright notice: */ + +/* (c) 1996 Grant R. Guenther, based on work of Itai Nahshon */ +/* http://www.torque.net/ziptool.html */ + + +/* A.K. Moved this from mzip.c to a separate file in order to share with + * plain_io.c */ + +#include "sysincludes.h" +#include "mtools.h" +#include "scsi.h" + +#if defined OS_hpux +#include <sys/scsi.h> +#endif + +#ifdef OS_solaris +#include <sys/scsi/scsi.h> +#endif /* solaris */ + +#ifdef OS_sunos +#include <scsi/generic/commands.h> +#include <scsi/impl/uscsi.h> +#endif /* sunos */ + +#ifdef sgi +#include <sys/dsreq.h> +#endif + +#ifdef OS_linux +#define SCSI_IOCTL_SEND_COMMAND 1 +struct scsi_ioctl_command { + int inlen; + int outlen; + char cmd[5008]; +}; +#endif + +#ifdef _SCO_DS +#include <sys/scsicmd.h> +#endif + +#if (defined(OS_freebsd)) && (__FreeBSD__ >= 2) +#include <camlib.h> +#endif + +int scsi_max_length(void) +{ +#ifdef OS_linux + return 8; +#else + return 255; +#endif +} + +int scsi_open(const char *name, int flag, int mode, void **extra_data) +{ +#if (defined(OS_freebsd)) && (__FreeBSD__ >= 2) + struct cam_device *cam_dev; + cam_dev = cam_open_device(name, O_RDWR); + *extra_data = (void *) cam_dev; + if (cam_dev) + return cam_dev->fd; + else + return -1; +#else + return open(name, O_RDONLY +#ifdef O_NDELAY + | O_NDELAY +#endif + /* O_RDONLY | dev->mode*/); +#endif +} + +int scsi_cmd(int fd, unsigned char *cdb, int cmdlen, scsi_io_mode_t mode, + void *data, size_t len, void *extra_data) +{ +#if defined OS_hpux + struct sctl_io sctl_io; + + memset(&sctl_io, 0, sizeof sctl_io); /* clear reserved fields */ + memcpy(sctl_io.cdb, cdb, cmdlen); /* copy command */ + sctl_io.cdb_length = cmdlen; /* command length */ + sctl_io.max_msecs = 2000; /* allow 2 seconds for cmd */ + + switch (mode) { + case SCSI_IO_READ: + sctl_io.flags = SCTL_READ; + sctl_io.data_length = len; + sctl_io.data = data; + break; + case SCSI_IO_WRITE: + sctl_io.flags = 0; + sctl_io.data_length = data ? len : 0; + sctl_io.data = len ? data : 0; + break; + } + + if (ioctl(fd, SIOC_IO, &sctl_io) == -1) { + perror("scsi_io"); + return -1; + } + + return sctl_io.cdb_status; + +#elif defined OS_sunos || defined OS_solaris + struct uscsi_cmd uscsi_cmd; + memset(&uscsi_cmd, 0, sizeof uscsi_cmd); + uscsi_cmd.uscsi_cdb = (char *)cdb; + uscsi_cmd.uscsi_cdblen = cmdlen; +#ifdef OS_solaris + uscsi_cmd.uscsi_timeout = 20; /* msec? */ +#endif /* solaris */ + + uscsi_cmd.uscsi_buflen = (u_int)len; + uscsi_cmd.uscsi_bufaddr = data; + + switch (mode) { + case SCSI_IO_READ: + uscsi_cmd.uscsi_flags = USCSI_READ; + break; + case SCSI_IO_WRITE: + uscsi_cmd.uscsi_flags = USCSI_WRITE; + break; + } + + if (ioctl(fd, USCSICMD, &uscsi_cmd) == -1) { + perror("scsi_io"); + return -1; + } + + if(uscsi_cmd.uscsi_status) { + errno = 0; + fprintf(stderr,"scsi status=%x\n", + (unsigned short)uscsi_cmd.uscsi_status); + return -1; + } + + return 0; + +#elif defined OS_linux + struct scsi_ioctl_command scsi_cmd; + + + memcpy(scsi_cmd.cmd, cdb, cmdlen); /* copy command */ + + switch (mode) { + case SCSI_IO_READ: + scsi_cmd.inlen = 0; + scsi_cmd.outlen = len; + break; + case SCSI_IO_WRITE: + scsi_cmd.inlen = len; + scsi_cmd.outlen = 0; + memcpy(scsi_cmd.cmd + cmdlen,data,len); + break; + } + + if (ioctl(fd, SCSI_IOCTL_SEND_COMMAND, &scsi_cmd) < 0) { + perror("scsi_io"); + return -1; + } + + switch (mode) { + case SCSI_IO_READ: + memcpy(data, &scsi_cmd.cmd[0], len); + break; + case SCSI_IO_WRITE: + break; + } + + return 0; /* where to get scsi status? */ + +#elif defined _SCO_DS + struct scsicmd scsi_cmd; + + memset(scsi_cmd.cdb, 0, SCSICMDLEN); /* ensure zero pad */ + memcpy(scsi_cmd.cdb, cdb, cmdlen); + scsi_cmd.cdb_len = cmdlen; + scsi_cmd.data_len = len; + scsi_cmd.data_ptr = data; + scsi_cmd.is_write = mode == SCSI_IO_WRITE; + if (ioctl(fd,SCSIUSERCMD,&scsi_cmd) == -1) { + perror("scsi_io"); + printf("scsi status: host=%x; target=%x\n", + (unsigned)scsi_cmd.host_sts,(unsigned)scsi_cmd.target_sts); + return -1; + } + return 0; +#elif defined sgi + struct dsreq scsi_cmd; + + scsi_cmd.ds_cmdbuf = (char *)cdb; + scsi_cmd.ds_cmdlen = cmdlen; + scsi_cmd.ds_databuf = data; + scsi_cmd.ds_datalen = len; + switch (mode) { + case SCSI_IO_READ: + scsi_cmd.ds_flags = DSRQ_READ|DSRQ_SENSE; + break; + case SCSI_IO_WRITE: + scsi_cmd.ds_flags = DSRQ_WRITE|DSRQ_SENSE; + break; + } + scsi_cmd.ds_time = 10000; + scsi_cmd.ds_link = 0; + scsi_cmd.ds_synch =0; + scsi_cmd.ds_ret =0; + if (ioctl(fd, DS_ENTER, &scsi_cmd) == -1) { + perror("scsi_io"); + return -1; + } + + if(scsi_cmd.ds_status) { + errno = 0; + fprintf(stderr,"scsi status=%x\n", + (unsigned short)scsi_cmd.ds_status); + return -1; + } + + return 0; +#elif (defined OS_freebsd) && (__FreeBSD__ >= 2) +#define MSG_SIMPLE_Q_TAG 0x20 /* O/O */ + union ccb *ccb; + int flags; + int r; + struct cam_device *cam_dev = (struct cam_device *) extra_data; + + + if (cam_dev==NULL || cam_dev->fd!=fd) + { + fprintf(stderr,"invalid file descriptor\n"); + return -1; + } + ccb = cam_getccb(cam_dev); + + bcopy(cdb, ccb->csio.cdb_io.cdb_bytes, cmdlen); + + if (mode == SCSI_IO_READ) + flags = CAM_DIR_IN; + else if (data && len) + flags = CAM_DIR_OUT; + else + flags = CAM_DIR_NONE; + cam_fill_csio(&ccb->csio, + /* retry */ 1, + /* cbfcnp */ NULL, + flags, + /* tag_action */ MSG_SIMPLE_Q_TAG, + /*data_ptr*/ len ? data : 0, + /*data_len */ data ? len : 0, + 96, + cmdlen, + 5000); + + if (cam_send_ccb(cam_dev, ccb) < 0 || + (ccb->ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { + return -1; + } + return 0; +#else + fprintf(stderr, "scsi_io not implemented\n"); + return -1; +#endif +} diff --git a/commands/i386/mtools-3.9.7/scsi.h b/commands/i386/mtools-3.9.7/scsi.h new file mode 100755 index 000000000..10d673349 --- /dev/null +++ b/commands/i386/mtools-3.9.7/scsi.h @@ -0,0 +1,22 @@ +#ifndef __mtools_scsi_h +#define __mtools_scsi_h + + +#define SCSI_READ 0x8 +#define SCSI_WRITE 0xA +#define SCSI_IOMEGA 0xC +#define SCSI_INQUIRY 0x12 +#define SCSI_MODE_SENSE 0x1a +#define SCSI_START_STOP 0x1b +#define SCSI_ALLOW_MEDIUM_REMOVAL 0x1e +#define SCSI_GROUP1 0x20 +#define SCSI_READ_CAPACITY 0x25 + + +typedef enum { SCSI_IO_READ, SCSI_IO_WRITE } scsi_io_mode_t; +int scsi_max_length(void); +int scsi_cmd(int fd, unsigned char cdb[6], int clen, scsi_io_mode_t mode, + void *data, size_t len, void *extra_data); +int scsi_open(const char *name, int flags, int mode, void **extra_data); + +#endif /* __mtools_scsi_h */ diff --git a/commands/i386/mtools-3.9.7/signal.c b/commands/i386/mtools-3.9.7/signal.c new file mode 100755 index 000000000..130280a9b --- /dev/null +++ b/commands/i386/mtools-3.9.7/signal.c @@ -0,0 +1,35 @@ +#include "sysincludes.h" +#include "mtools.h" + +#undef got_signal + +int got_signal = 0; + +void signal_handler(int dummy) +{ + got_signal = 1; +#if 0 + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + signal(SIGQUIT, SIG_IGN); +#endif +} + +#if 0 +int do_gotsignal(char *f, int n) +{ + if(got_signal) + fprintf(stderr, "file=%s line=%d\n", f, n); + return got_signal; +} +#endif + +void setup_signal(void) +{ + /* catch signals */ + signal(SIGHUP, (SIG_CAST)signal_handler); + signal(SIGINT, (SIG_CAST)signal_handler); + signal(SIGTERM, (SIG_CAST)signal_handler); + signal(SIGQUIT, (SIG_CAST)signal_handler); +} diff --git a/commands/i386/mtools-3.9.7/stream.c b/commands/i386/mtools-3.9.7/stream.c new file mode 100755 index 000000000..3905e8d07 --- /dev/null +++ b/commands/i386/mtools-3.9.7/stream.c @@ -0,0 +1,65 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "stream.h" + +int batchmode = 0; + +int flush_stream(Stream_t *Stream) +{ + int ret=0; + if(!batchmode) { + if(Stream->Class->flush) + ret |= Stream->Class->flush(Stream); + if(Stream->Next) + ret |= flush_stream(Stream->Next); + } + return ret; +} + +Stream_t *copy_stream(Stream_t *Stream) +{ + if(Stream) + Stream->refs++; + return Stream; +} + +int free_stream(Stream_t **Stream) +{ + int ret=0; + + if(!*Stream) + return -1; + if(! --(*Stream)->refs){ + if((*Stream)->Class->flush) + ret |= (*Stream)->Class->flush(*Stream); + if((*Stream)->Class->freeFunc) + ret |= (*Stream)->Class->freeFunc(*Stream); + if((*Stream)->Next) + ret |= free_stream(&(*Stream)->Next); + Free(*Stream); + } else if ( (*Stream)->Next ) + ret |= flush_stream((*Stream)->Next); + *Stream = NULL; + return ret; +} + + +#define GET_DATA(stream, date, size, type, address) \ +(stream)->Class->get_data( (stream), (date), (size), (type), (address) ) + + +int get_data_pass_through(Stream_t *Stream, time_t *date, mt_size_t *size, + int *type, int *address) +{ + return GET_DATA(Stream->Next, date, size, type, address); +} + +int read_pass_through(Stream_t *Stream, char *buf, mt_off_t start, size_t len) +{ + return READS(Stream->Next, buf, start, len); +} + +int write_pass_through(Stream_t *Stream, char *buf, mt_off_t start, size_t len) +{ + return WRITES(Stream->Next, buf, start, len); +} diff --git a/commands/i386/mtools-3.9.7/stream.h b/commands/i386/mtools-3.9.7/stream.h new file mode 100755 index 000000000..42dd4a548 --- /dev/null +++ b/commands/i386/mtools-3.9.7/stream.h @@ -0,0 +1,71 @@ +#ifndef MTOOLS_STREAM_H +#define MTOOLS_STREAM_H + +typedef struct Stream_t { + struct Class_t *Class; + int refs; + struct Stream_t *Next; + struct Stream_t *Buffer; +} Stream_t; + +#include "mtools.h" +#include "msdos.h" + +#include "llong.h" + +typedef struct Class_t { + int (*read)(Stream_t *, char *, mt_off_t, size_t); + int (*write)(Stream_t *, char *, mt_off_t, size_t); + int (*flush)(Stream_t *); + int (*freeFunc)(Stream_t *); + int (*set_geom)(Stream_t *, device_t *, device_t *, int media, + struct bootsector *); + int (*get_data)(Stream_t *, time_t *, mt_size_t *, int *, int *); + int (*pre_allocate)(Stream_t *, mt_size_t); +} Class_t; + +#define READS(stream, buf, address, size) \ +(stream)->Class->read( (stream), (char *) (buf), (address), (size) ) + +#define WRITES(stream, buf, address, size) \ +(stream)->Class->write( (stream), (char *) (buf), (address), (size) ) + +#define SET_GEOM(stream, dev, orig_dev, media, boot) \ +(stream)->Class->set_geom( (stream), (dev), (orig_dev), (media), (boot) ) + +#define GET_DATA(stream, date, size, type, address) \ +(stream)->Class->get_data( (stream), (date), (size), (type), (address) ) + +#define PRE_ALLOCATE(stream, size) \ +(stream)->Class->pre_allocate((stream), (size)) + +int flush_stream(Stream_t *Stream); +Stream_t *copy_stream(Stream_t *Stream); +int free_stream(Stream_t **Stream); + +#define FLUSH(stream) \ +flush_stream( (stream) ) + +#define FREE(stream) \ +free_stream( (stream) ) + +#define COPY(stream) \ +copy_stream( (stream) ) + + +#define DeclareThis(x) x *This = (x *) Stream + +int force_write(Stream_t *Stream, char *buf, mt_off_t start, size_t len); +int force_read(Stream_t *Stream, char *buf, mt_off_t start, size_t len); + +extern struct Stream_t *default_drive; + +int get_data_pass_through(Stream_t *Stream, time_t *date, mt_size_t *size, + int *type, int *address); + +int read_pass_through(Stream_t *Stream, char *buf, mt_off_t start, size_t len); +int write_pass_through(Stream_t *Stream, char *buf, mt_off_t start, size_t len); + + +#endif + diff --git a/commands/i386/mtools-3.9.7/streamcache.c b/commands/i386/mtools-3.9.7/streamcache.c new file mode 100755 index 000000000..7e1850eed --- /dev/null +++ b/commands/i386/mtools-3.9.7/streamcache.c @@ -0,0 +1,77 @@ +/* + * streamcache.c + * Managing a cache of open disks + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "vfat.h" +#include "fs.h" +#include "mainloop.h" +#include "plain_io.h" +#include "file.h" + +static int is_initialized = 0; +static Stream_t *fss[256]; /* open drives */ + +static void finish_sc(void) +{ + int i; + + for(i=0; i<256; i++){ + if(fss[i] && fss[i]->refs != 1 ) + fprintf(stderr,"Streamcache allocation problem:%c %d\n", + i, fss[i]->refs); + FREE(&(fss[i])); + } +} + +static void init_streamcache(void) +{ + int i; + + if(is_initialized) + return; + is_initialized = 1; + for(i=0; i<256; i++) + fss[i]=0; + atexit(finish_sc); +} + +Stream_t *open_root_dir(char *drive, int flags) +{ + Stream_t *Fs; + int i, k; + + init_streamcache(); + + k = -1; + for(i=0; i<256; i++) { + if (fss[i] == NULL || strcmp(getDrive(fss[i]), drive) == 0) { + k = i; + break; + } + } + + if(k == -1) { + fprintf(stderr, "Cannot initialize '%s:', out of table space\n", + drive); + return NULL; + } + + /* open the drive */ + if(fss[k]) + Fs = fss[k]; + else { + Fs = fs_init(drive, flags); + if (!Fs){ + fprintf(stderr, "Cannot initialize '%s:'\n", drive); + return NULL; + } + + fss[k] = Fs; + } + + return OpenRoot(Fs); +} diff --git a/commands/i386/mtools-3.9.7/subdir.c b/commands/i386/mtools-3.9.7/subdir.c new file mode 100755 index 000000000..db6a69c26 --- /dev/null +++ b/commands/i386/mtools-3.9.7/subdir.c @@ -0,0 +1,26 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "vfat.h" +#include "file.h" +#include "buffer.h" + +/* + * Find the directory and load a new dir_chain[]. A null directory + * is OK. Returns a 1 on error. + */ + + +void bufferize(Stream_t **Dir) +{ + Stream_t *BDir; + + if(!*Dir) + return; + BDir = buf_init(*Dir, 64*16384, 512, MDIR_SIZE); + if(!BDir){ + FREE(Dir); + *Dir = NULL; + } else + *Dir = BDir; +} diff --git a/commands/i386/mtools-3.9.7/sysincludes.h b/commands/i386/mtools-3.9.7/sysincludes.h new file mode 100755 index 000000000..3fb47fe57 --- /dev/null +++ b/commands/i386/mtools-3.9.7/sysincludes.h @@ -0,0 +1,518 @@ +/* System includes for mtools */ + +#ifndef SYSINCLUDES_H +#define SYSINCLUDES_H + +#include "config.h" + +/* OS/2 needs __inline__, but for some reason is not autodetected */ +#ifdef __EMX__ +# ifndef inline +# define inline __inline__ +# endif +#endif + +/***********************************************************************/ +/* */ +/* OS dependancies which cannot be covered by the autoconfigure script */ +/* */ +/***********************************************************************/ + + +#ifdef OS_aux +/* A/UX needs POSIX_SOURCE, just as AIX does. Unlike SCO and AIX, it seems + * to prefer TERMIO over TERMIOS */ +#ifndef _POSIX_SOURCE +# define _POSIX_SOURCE +#endif +#ifndef POSIX_SOURCE +# define POSIX_SOURCE +#endif + +#endif + + +/* On AIX, we have to prefer strings.h, as string.h lacks a prototype + * for strcasecmp. On most other architectures, it's string.h which seems + * to be more complete */ +#if (defined OS_aix && defined HAVE_STRINGS_H) +# undef HAVE_STRING_H +#endif + + +#ifdef OS_ultrix +/* on ultrix, if termios present, prefer it instead of termio */ +# ifdef HAVE_TERMIOS_H +# undef HAVE_TERMIO_H +# endif +#endif + +#ifdef OS_linux_gnu +/* RMS strikes again */ +# ifndef OS_linux +# define OS_linux +# endif +#endif + +#ifdef OS_Minix +typedef unsigned char *caddr_t; +#endif + + +/***********************************************************************/ +/* */ +/* Compiler dependancies */ +/* */ +/***********************************************************************/ + + +#if defined __GNUC__ && defined __STDC__ +/* gcc -traditional doesn't have PACKED, UNUSED and NORETURN */ +# define PACKED __attribute__ ((packed)) +# if __GNUC__ == 2 && __GNUC_MINOR__ > 6 || __GNUC__ >= 3 +/* gcc 2.6.3 doesn't have "unused" */ /* mool */ +# define UNUSED(x) x __attribute__ ((unused));x +# else +# define UNUSED(x) x +# endif +# define NORETURN __attribute__ ((noreturn)) +#else +# define UNUSED(x) x +# define PACKED /* */ +# define NORETURN /* */ +#endif + + +/***********************************************************************/ +/* */ +/* Include files */ +/* */ +/***********************************************************************/ + + +#include <sys/types.h> + +#ifdef OS_Minix +typedef unsigned long uoff_t; +#define off_t uoff_t +#endif + +#ifdef HAVE_STDLIB_H +# include <stdlib.h> +#endif + +#include <stdio.h> + +#ifndef OS_Minix +# include <ctype.h> +#else +# ifdef __minix_vmd +# include <bsd/asciictype.h> /* Minix-vmd: Ignore locales on purpose. */ +# else +# include <ctype.h> /* Minix: What's that "locale" thing? */ +# endif +#endif + +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif + +#ifdef HAVE_LINUX_UNISTD_H +# include <linux/unistd.h> +#endif + +#ifdef HAVE_LIBC_H +# include <libc.h> +#endif + +#ifdef HAVE_GETOPT_H +# include <getopt.h> +#else +# ifndef OS_Minix +int getopt(); +extern char *optarg; +extern int optind, opterr; +# endif +#endif + +#ifdef HAVE_FCNTL_H +# include <fcntl.h> +#endif + +#ifdef HAVE_LIMITS_H +# include <limits.h> +#endif + +#ifdef HAVE_SYS_FILE_H +# include <sys/file.h> +#endif + +#ifdef HAVE_SYS_IOCTL_H +# ifndef sunos +# include <sys/ioctl.h> +#endif +#endif +/* if we don't have sys/ioctl.h, we rely on unistd to supply a prototype + * for it. If it doesn't, we'll only get a (harmless) warning. The idea + * is to get mtools compile on as many platforms as possible, but to not + * suppress warnings if the platform is broken, as long as these warnings do + * not prevent compilation */ + +#ifdef TIME_WITH_SYS_TIME +# include <sys/time.h> +# include <time.h> +#else +# ifdef HAVE_SYS_TIME_H +# include <sys/time.h> +# else +# include <time.h> +# endif +#endif + +#ifndef NO_TERMIO +# ifdef HAVE_TERMIO_H +# include <termio.h> +# elif defined HAVE_SYS_TERMIO_H +# include <sys/termio.h> +# endif +# if !defined OS_ultrix || !(defined HAVE_TERMIO_H || defined HAVE_TERMIO_H) +/* on Ultrix, avoid double inclusion of both termio and termios */ +# ifdef HAVE_TERMIOS_H +# include <termios.h> +# elif defined HAVE_SYS_TERMIOS_H +# include <sys/termios.h> +# endif +# endif +# ifdef HAVE_STTY_H +# include <sgtty.h> +# endif +#endif + + +#if defined(OS_aux) && !defined(_SYSV_SOURCE) +/* compiled in POSIX mode, this is left out unless SYSV */ +#define NCC 8 +struct termio { + unsigned short c_iflag; /* input modes */ + unsigned short c_oflag; /* output modes */ + unsigned short c_cflag; /* control modes */ + unsigned short c_lflag; /* line discipline modes */ + char c_line; /* line discipline */ + unsigned char c_cc[NCC]; /* control chars */ +}; +extern int ioctl(int fildes, int request, void *arg); +#endif + + +#ifdef HAVE_MNTENT_H +# include <mntent.h> +#endif + +#ifdef HAVE_SYS_PARAM_H +# include <sys/param.h> +#endif + +/* Can only be done here, as BSD is defined in sys/param.h :-( */ +#if defined BSD || defined __BEOS__ +/* on BSD and on BEOS, we prefer gettimeofday, ... */ +# ifdef HAVE_GETTIMEOFDAY +# undef HAVE_TZSET +# endif +#else /* BSD */ +/* ... elsewhere we prefer tzset */ +# ifdef HAVE_TZSET +# undef HAVE_GETTIMEOFDAY +# endif +#endif + + +#include <sys/stat.h> + +#include <errno.h> +extern int errno; + +#include <pwd.h> + + +#ifdef HAVE_STRING_H +# include <string.h> +#else +# ifdef HAVE_STRINGS_H +# include <strings.h> +# endif +#endif + +#ifdef HAVE_MEMORY_H +# include <memory.h> +#endif + +#ifdef HAVE_MALLOC_H +# include <malloc.h> +#endif + +#ifdef HAVE_SIGNAL_H +# include <signal.h> +#else +# ifdef HAVE_SYS_SIGNAL_H +# include <sys/signal.h> +# endif +#endif + +#ifdef HAVE_UTIME_H +# include <utime.h> +#endif + +#ifdef HAVE_SYS_WAIT_H +# ifndef DONT_NEED_WAIT +# include <sys/wait.h> +# endif +#endif + + +#ifdef USE_FLOPPYD + +#ifdef HAVE_SYS_SOCKET_H +#include <sys/socket.h> +#endif + +#ifdef HAVE_NETINET_IN_H +#include <netinet/in.h> +#endif + +#ifdef HAVE_ARPA_INET_H +#include <arpa/inet.h> +#endif + +#ifdef HAVE_NETDB_H +#include <netdb.h> +#endif + +#ifdef HAVE_X11_XAUTH_H +#include <X11/Xauth.h> +#endif + +#ifdef HAVE_X11_XLIB_H +#include <X11/Xlib.h> +#endif + +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE (-1) +#endif + + +#ifdef sgi +#define MSGIHACK __EXTENSIONS__ +#undef __EXTENSIONS__ +#endif +#include <math.h> +#ifdef sgi +#define __EXTENSIONS__ MSGIHACK +#undef MSGIHACK +#endif + +/* missing functions */ +#ifndef HAVE_SRANDOM +# define srandom srand48 +#endif + +#ifndef HAVE_RANDOM +# define random (long)lrand48 +#endif + +#if __minix && !__minix_vmd +# define srandom srand +# define random rand +#endif + +#ifndef HAVE_STRCHR +# define strchr index +#endif + +#ifndef HAVE_STRRCHR +# define strrchr rindex +#endif + + +#define SIG_CAST RETSIGTYPE(*)() + +#ifndef HAVE_STRDUP +extern char *strdup(const char *str); +#endif /* HAVE_STRDUP */ + + +#ifndef HAVE_MEMCPY +extern char *memcpy(char *s1, const char *s2, size_t n); +#endif + +#ifndef HAVE_MEMSET +extern char *memset(char *s, char c, size_t n); +#endif /* HAVE_MEMSET */ + + +#ifndef HAVE_STRPBRK +extern char *strpbrk(const char *string, const char *brkset); +#endif /* HAVE_STRPBRK */ + + +#ifndef HAVE_STRTOUL +unsigned long strtoul(const char *string, char **eptr, int base); +#endif /* HAVE_STRTOUL */ + +#ifndef HAVE_STRSPN +size_t strspn(const char *s, const char *accept); +#endif /* HAVE_STRSPN */ + +#ifndef HAVE_STRCSPN +size_t strcspn(const char *s, const char *reject); +#endif /* HAVE_STRCSPN */ + +#ifndef HAVE_STRERROR +char *strerror(int errno); +#endif + +#ifndef HAVE_ATEXIT +int atexit(void (*function)(void)); + +#ifndef HAVE_ON_EXIT +void myexit(int code) NORETURN; +#define exit myexit +#endif + +#endif + + +#ifndef HAVE_MEMMOVE +# define memmove(DST, SRC, N) bcopy(SRC, DST, N) +#endif + +#ifndef HAVE_STRCASECMP +int strcasecmp(const char *s1, const char *s2); +#endif + +#ifndef HAVE_STRNCASECMP +int strncasecmp(const char *s1, const char *s2, size_t n); +#endif + +#ifndef HAVE_GETPASS +char *getpass(const char *prompt); +#endif + +#if 0 +#ifndef HAVE_BASENAME +const char *basename(const char *filename); +#endif +#endif + +const char *_basename(const char *filename); + +#ifndef __STDC__ +# ifndef signed +# define signed /**/ +# endif +#endif /* !__STDC__ */ + + + +/***************************************************************************/ +/* */ +/* Prototypes for systems where the functions exist but not the prototypes */ +/* */ +/***************************************************************************/ + + + +/* prototypes which might be missing on some platforms, even if the functions + * are present. Do not declare argument types, in order to avoid conflict + * on platforms where the prototypes _are_ correct. Indeed, for most of + * these, there are _several_ "correct" parameter definitions, and not all + * platforms use the same. For instance, some use the const attribute for + * strings not modified by the function, and others do not. By using just + * the return type, which rarely changes, we avoid these problems. + */ + +/* Correction: Now it seems that even return values are not standardized :-( + For instance DEC-ALPHA, OSF/1 3.2d uses ssize_t as a return type for read + and write. NextStep uses a non-void return value for exit, etc. With the + advent of 64 bit system, we'll expect more of these problems in the future. + Better uncomment the lot, except on SunOS, which is known to have bad + incomplete files. Add other OS'es with incomplete include files as needed + */ +#if (defined OS_sunos || defined OS_ultrix) +int read(); +int write(); +int fflush(); +char *strdup(); +int strcasecmp(); +int strncasecmp(); +char *getenv(); +unsigned long strtoul(); +int pclose(); +void exit(); +char *getpass(); +int atoi(); +FILE *fdopen(); +FILE *popen(); +#endif + +#ifndef MAXPATHLEN +# ifdef PATH_MAX +# define MAXPATHLEN PATH_MAX +# else +# define MAXPATHLEN 1024 +# endif +#endif + + +#ifndef OS_linux +# undef USE_XDF +#endif + +#ifdef NO_XDF +# undef USE_XDF +#endif + +#ifdef __EMX__ +#define INCL_BASE +#define INCL_DOSDEVIOCTL +#include <os2.h> +#endif + +#ifdef OS_nextstep +/* nextstep doesn't have this. Unfortunately, we cannot test its presence + using AC_EGREP_HEADER, as we don't know _which_ header to test, and in + the general case utime.h might be non-existent */ +struct utimbuf +{ + time_t actime,modtime; +}; +#endif + +/* NeXTStep doesn't have these */ +#if !defined(S_ISREG) && defined (_S_IFMT) && defined (_S_IFREG) +#define S_ISREG(mode) (((mode) & (_S_IFMT)) == (_S_IFREG)) +#endif + +#if !defined(S_ISDIR) && defined (_S_IFMT) && defined (_S_IFDIR) +#define S_ISDIR(mode) (((mode) & (_S_IFMT)) == (_S_IFDIR)) +#endif + + +#if 0 + +#define malloc(x) mymalloc(x) +#define calloc(x,y) mycalloc(x,y) +#define free(x) myfree(x) +#define realloc(x,y) myrealloc(x,y) +#define strdup(a) mystrdup(a) + +void *mycalloc(size_t nmemb, size_t size); +void *mymalloc(size_t size); +void myfree(void *ptr); +void *myrealloc(void *ptr, size_t size); +char *mystrdup(char *a); + +#endif + +#endif diff --git a/commands/i386/mtools-3.9.7/toupper.c b/commands/i386/mtools-3.9.7/toupper.c new file mode 100755 index 000000000..655440576 --- /dev/null +++ b/commands/i386/mtools-3.9.7/toupper.c @@ -0,0 +1,491 @@ +#include "codepage.h" + +/* MS-DOS doesn't use the same ASCII code as Unix does. The appearance + * of the characters is defined using code pages. These code pages + * aren't the same for all countries. For instance, some code pages + * don't contain upper case accented characters. This affects two + * things, relating to filenames: + + * 1. upper case characters. In short names, only upper case + * characters are allowed. This also holds for accented characters. + * For instance, in a code page which doesn't contain accented + * uppercase characters, the accented lowercase characters get + * transformed into their unaccented counterparts. This is very bad + * design. Indeed, stuff like national language support should never + * affect filesystem integrity. And it does: A filename which is legal + * in one country could be illegal in another one. Bad News for + * frequent travellers. + + * 2. long file names: Micro$oft has finally come to their senses and + * uses a more standard mapping for the long file names. They use + * Unicode, which is basically a 32 bit version of ASCII. Its first + * 256 characters are identical to Unix ASCII. Thus, the code page + * also affects the correspondence between the codes used in long + * names and those used in short names. + + * Such a bad design is rather unbelievable. That's why I quoted the + * translation tables. BEGIN FAIR USE EXCERPT: + */ + +unsigned char toucase[][128]={ + /* 0 */ + /* 437 German Umlauts upcased, French accents + * upcased and lose accent */ + { 0x80, 0x9a, 0x45, 0x41, 0x8e, 0x41, 0x8f, 0x80, + 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f, + 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55, + 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + + /* 1 */ + { 0x43, 0x55, 0x45, 0x41, 0x41, 0x41, 0x86, 0x43, + 0x45, 0x45, 0x45, 0x49, 0x49, 0x8d, 0x41, 0x8f, + 0x45, 0x45, 0x45, 0x4f, 0x45, 0x49, 0x55, 0x55, + 0x98, 0x4f, 0x55, 0x9b, 0x9c, 0x55, 0x55, 0x9f, + 0xa0, 0xa1, 0x4f, 0x55, 0xa4, 0xa5, 0xa6, 0xa7, + 0x49, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + + /* 2 */ + { 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80, + 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f, + 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55, + 0x59, 0x99, 0x9a, 0x9d, 0x9c, 0x9d, 0x9e, 0x9f, + 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + + /* 3 */ + { 0x80, 0x9a, 0x90, 0x41, 0x8e, 0x41, 0x8f, 0x80, + 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x8f, + 0x90, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55, + 0x59, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + + /* 4 + * 437: all accented characters lose their accent */ + { 0x80, 0x55, 0x45, 0x41, 0x41, 0x41, 0x8f, 0x80, + 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x41, 0x8f, + 0x45, 0x92, 0x92, 0x4f, 0x4f, 0x4f, 0x55, 0x55, + 0x98, 0x4f, 0x55, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + + /* 5 */ + { 0x80, 0x9a, 0x90, 0x8f, 0x8e, 0x91, 0x86, 0x80, + 0x89, 0x89, 0x92, 0x8b, 0x8c, 0x98, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x8c, 0x99, 0xa9, 0x96, 0x9d, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0x86, 0x8b, 0x9f, 0x96, 0xa5, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + + /* 6 All accented characters lose their accent + when upcased. C loses cedilla. æ upcased. ø + loses slash. Ð, ñ, ß intact */ + { 0x43, 0x55, 0x45, 0x41, 0x41, 0x41, 0x41, 0x43, + 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x41, 0x41, + 0x45, 0x92, 0x92, 0x4f, 0x4f, 0x4f, 0x55, 0x55, + 0x59, 0x4f, 0x55, 0x4f, 0x9c, 0x4f, 0x9e, 0x9f, + 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x41, 0x41, 0x41, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0x41, 0x41, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd1, 0xd1, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, + 0x49, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0x49, 0xdf, + 0x4f, 0xe1, 0x4f, 0x4f, 0x4f, 0x4f, 0xe6, 0xe8, + 0xe8, 0x55, 0x55, 0x55, 0x59, 0x59, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + + /* 7: As 6, but German Umlauts keep their Umlaut */ + { 0x43, 0x9a, 0x45, 0x41, 0x8e, 0x41, 0x41, 0x43, + 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x8e, 0x41, + 0x45, 0x92, 0x92, 0x4f, 0x99, 0x4f, 0x55, 0x55, + 0x59, 0x99, 0x9a, 0x4f, 0x9c, 0x4f, 0x9e, 0x9f, + 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x41, 0x41, 0x41, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0x41, 0x41, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd1, 0xd1, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, + 0x49, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0x49, 0xdf, + 0x4f, 0xe1, 0x4f, 0x4f, 0x4f, 0x4f, 0xe6, 0xe8, + 0xe8, 0x55, 0x55, 0x55, 0x59, 0x59, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + + /* 8: All characters except ÿ keep their accent + */ + { 0x80, 0x9a, 0x90, 0xb6, 0x8e, 0xb7, 0x8f, 0x80, + 0xd2, 0xd3, 0xd4, 0xd8, 0xd7, 0xde, 0x8e, 0x8f, + 0x90, 0x92, 0x92, 0xe2, 0x99, 0xe3, 0xea, 0xeb, + 0x59, 0x99, 0x9a, 0x9d, 0x9c, 0x9d, 0x9e, 0x9f, + 0xb5, 0xd6, 0xe0, 0xe9, 0xa5, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc7, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd1, 0xd1, 0xd2, 0xd3, 0xd4, 0x49, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe5, 0xe5, 0xe6, 0xe8, + 0xe8, 0xe9, 0xea, 0xeb, 0xed, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + + /* 9: As 6, but Ç and Å preserved */ + { 0x80, 0x55, 0x45, 0x41, 0x41, 0x41, 0x8f, 0x80, + 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x41, 0x8f, + 0x45, 0x92, 0x92, 0x4f, 0x4f, 0x4f, 0x55, 0x55, + 0x98, 0x4f, 0x55, 0x4f, 0x9c, 0x4f, 0x9e, 0x9f, + 0x41, 0x49, 0x4f, 0x55, 0xa5, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0x41, 0x41, 0x41, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0x41, 0x41, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd1, 0xd1, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, + 0x49, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0x49, 0xdf, + 0x4f, 0xe1, 0x4f, 0x4f, 0x4f, 0x4f, 0xe6, 0xe8, + 0xe8, 0x55, 0x55, 0x55, 0x59, 0x59, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + + /* 10: every accented character keeps its accent */ + { 0x80, 0x9a, 0x90, 0xb6, 0x8e, 0xb7, 0x8f, 0x80, + 0xd2, 0xd3, 0xd4, 0xd8, 0xd7, 0xde, 0x8e, 0x8f, + 0x90, 0x92, 0x92, 0xe2, 0x99, 0xe3, 0xea, 0xeb, + 0x98, 0x99, 0x9a, 0x9d, 0x9c, 0x9d, 0x9e, 0x9f, + 0xb5, 0xd6, 0xe0, 0xe9, 0xa5, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc7, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd1, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe5, 0xe5, 0xe6, 0xe8, + 0xe8, 0xe9, 0xea, 0xeb, 0xed, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + + /* 11 */ + { 0x80, 0x9a, 0x90, 0xb6, 0x8e, 0xde, 0x8f, 0x80, + 0x9d, 0xd3, 0x8a, 0x8a, 0xd7, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x91, 0xe2, 0x99, 0x95, 0x95, 0x97, + 0x97, 0x99, 0x9a, 0x9b, 0x9b, 0x9d, 0x9e, 0xac, + 0xb5, 0xd6, 0xe0, 0xe9, 0xa4, 0xa4, 0xa6, 0xa6, + 0xa8, 0xa8, 0xaa, 0x8d, 0xac, 0xb8, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbd, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc6, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd1, 0xd1, 0xd2, 0xd3, 0xd2, 0xd5, 0xd6, 0xd7, + 0xb7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe3, 0xd5, 0xe6, 0xe6, + 0xe8, 0xe9, 0xe8, 0xeb, 0xed, 0xed, 0xdd, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xeb, 0xfc, 0xfc, 0xfe, 0xff }, + + + /* 14 All accented characters lose their accent, C loses cedilla, + * ø loses slash. æ upcased. Ð, ñ, ß intact */ + { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0xc6, 0x43, + 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, + + 0xd0, 0xd1, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0xd7, + 0x4f, 0x55, 0x55, 0x55, 0x55, 0x59, 0xde, 0xdf, + + 0x41, 0x41, 0x41, 0x41, 0x41, 0x41, 0xc6, 0x43, + 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, + + 0xd0, 0xd1, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0xf7, + 0x4f, 0x55, 0x55, 0x55, 0x55, 0x59, 0xde, 0x59 }, + + + + /* 15 as 14, but German Umlauts (ä, ö, ü) keep their Umlaut when + upcased */ + { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + + 0x41, 0x41, 0x41, 0x41, 0xc4, 0x41, 0xc6, 0x43, + 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, + + 0xd0, 0xd1, 0x4f, 0x4f, 0x4f, 0x4f, 0xd6, 0xd7, + 0x4f, 0x55, 0x55, 0x55, 0xdc, 0x59, 0xde, 0xdf, + + 0x41, 0x41, 0x41, 0x41, 0xc4, 0x41, 0xc6, 0x43, + 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, + + 0xd0, 0xd1, 0x4f, 0x4f, 0x4f, 0x4f, 0xd6, 0xf7, + 0x4f, 0x55, 0x55, 0x55, 0xdc, 0x59, 0xde, 0x59 }, + + + /* 16 every accented character except ÿ keeps its accent */ + { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0x59 }, + + + + /* 17: As 6, but Ç, Å and ÿ preserved */ + { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + + 0x41, 0x41, 0x41, 0x41, 0x41, 0xc5, 0xc6, 0xc7, + 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, + + 0xd0, 0xd1, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0xd7, + 0x4f, 0x55, 0x55, 0x55, 0x55, 0x59, 0xde, 0xdf, + + 0x41, 0x41, 0x41, 0x41, 0x41, 0xc5, 0xc6, 0xc7, + 0x45, 0x45, 0x45, 0x45, 0x49, 0x49, 0x49, 0x49, + + 0xd0, 0xd1, 0x4f, 0x4f, 0x4f, 0x4f, 0x4f, 0xf7, + 0x4f, 0x55, 0x55, 0x55, 0x55, 0x59, 0xde, 0xff }, + + + /* 18 every accented character keeps its accent */ + { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xf7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xff }, + + + /* 19 */ + { 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, + 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, + 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, + 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, + 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, + 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, + 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, + 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, + 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, + 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, + 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, + 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, + 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, + 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + +}; + +country_t countries[]={ + { 1, 437, 437, 0 }, + { 1, 850, 437, 6 }, + { 1, 819, 437, 14 }, + { 44, 437, 437, 0 }, + { 44, 850, 437, 6 }, + { 44, 819, 437, 14 }, + { 33, 437, 437, 0 }, + { 33, 850, 437, 6 }, + { 33, 819, 437, 14 }, + { 49, 437, 437, 0 }, + { 49, 850, 437, 7 }, + { 49, 819, 437, 15 }, + { 34, 850, 437, 8 }, + { 34, 819, 437, 16 }, + { 34, 437, 437, 0 }, + { 39, 437, 437, 0 }, + { 39, 850, 437, 6 }, + { 39, 819, 437, 14 }, + { 46, 437, 437, 3 }, + { 46, 850, 437, 8 }, + { 46, 819, 437, 16 }, + { 45, 850, 865, 8 }, + { 45, 819, 865, 16 }, + { 45, 865, 865, 2 }, + { 41, 850, 437, 8 }, + { 41, 819, 437, 16 }, + { 41, 437, 437, 3 }, + { 47, 850, 865, 8 }, + { 47, 819, 865, 16 }, + { 47, 865, 865, 2 }, + { 31, 437, 437, 4 }, + { 31, 850, 437, 9 }, + { 31, 819, 437, 17 }, + { 32, 850, 437, 8 }, + { 32, 819, 437, 16 }, + { 32, 437, 437, 3 }, + { 358, 850, 437, 8 }, + { 358, 819, 437, 16 }, + { 358, 437, 437, 3 }, + { 2, 863, 863, 1 }, + { 2, 850, 863, 6 }, + { 2, 819, 863, 14 }, + { 351, 850, 860, 6 }, + { 351, 819, 860, 14 }, + { 351, 860, 860, 5 }, + { 3, 850, 437, 8 }, + { 3, 819, 437, 16 }, + { 3, 437, 437, 0 }, + { 61, 437, 437, 0 }, + { 61, 850, 437, 6 }, + { 61, 819, 437, 16 }, + { 81, 437, 437, 0 }, + { 81, 819, 437, 14 }, + { 82, 437, 437, 0 }, + { 82, 819, 437, 14 }, + { 86, 437, 437, 0 }, + { 86, 819, 437, 14 }, + { 88, 437, 437, 0 }, + { 88, 819, 437, 14 }, + { 55, 850, 850, 10 }, + { 55, 819, 850, 18 }, + { 55, 437, 850, 0 }, + { 354, 850, 850, 10 }, + { 354, 819, 850, 18 }, + { 90, 850, 850, 10 }, + { 90, 819, 850, 18 }, + { 38, 852, 852, 11 }, + { 38, 850, 852, 10 }, + { 38, 819, 852, 18 }, + { 42, 852, 852, 11 }, + { 42, 850, 852, 10 }, + { 42, 819, 852, 18 }, + { 48, 852, 852, 11 }, + { 48, 850, 852, 10 }, + { 48, 819, 852, 18 }, + { 36, 852, 852, 11 }, + { 36, 850, 852, 10 }, + { 36, 819, 852, 18 }, + { 886, 950, 950, 19 }, /* for Taiwan support (Country code) */ + { 0, 0, 0, 0 } +}; + +/* END FAIR USE EXCERPT */ diff --git a/commands/i386/mtools-3.9.7/tty.c b/commands/i386/mtools-3.9.7/tty.c new file mode 100755 index 000000000..dc9eca7af --- /dev/null +++ b/commands/i386/mtools-3.9.7/tty.c @@ -0,0 +1,201 @@ +#include "sysincludes.h" +#include "mtools.h" + +static FILE *tty=NULL; +static int notty=0; +static int ttyfd=-1; +#ifdef USE_RAWTERM +int mtools_raw_tty = 1; +#else +int mtools_raw_tty = 0; +#endif + +#ifdef USE_RAWTERM +# if defined TCSANOW && defined HAVE_TCSETATTR +/* we have tcsetattr & tcgetattr. Good */ +typedef struct termios Terminal; +# define stty(a,b) (void)tcsetattr(a,TCSANOW,b) +# define gtty(a,b) (void)tcgetattr(a,b) +# define USE_TCIFLUSH + +# elif defined TCSETS && defined TCGETS +typedef struct termios Terminal; +# define stty(a,b) (void)ioctl(a,TCSETS,(char *)b) +# define gtty(a,b) (void)ioctl(a,TCGETS,(char *)b) +# define USE_TCIFLUSH + +# elif defined TCSETA && defined TCGETA +typedef struct termio Terminal; +# define stty(a,b) (void)ioctl(a,TCSETA,(char *)b) +# define gtty(a,b) (void)ioctl(a,TCGETA,(char *)b) +# define USE_TCIFLUSH + +# elif defined(HAVE_SGTTY_H) && defined(TIOCSETP) && defined(TIOCGETP) +typedef struct sgttyb Terminal; +# define stty(a,b) (void)ioctl(a,TIOCSETP,(char *)b) +# define gtty(a,b) (void)ioctl(a,TIOCGETP,(char *)b) +# define USE_SGTTY +# define discard_input(a) /**/ + +# else +/* no way to use raw terminal */ +/* +# warning Cannot use raw terminal code (disabled) +*/ +# undef USE_RAWTERM +# endif + +#endif + +#ifdef USE_TCIFLUSH +# if defined TCIFLUSH && defined HAVE_TCFLUSH +# define discard_input(a) tcflush(a,TCIFLUSH) +# else +# define discard_input(a) /**/ +# endif +#endif + +#ifdef USE_RAWTERM + +static int tty_mode = -1; /* 1 for raw, 0 for cooked, -1 for initial */ +static int need_tty_reset = 0; +static int handlerIsSet = 0; + +#define restore_tty(a) stty(STDIN,a) + + +#define STDIN ttyfd +#define FAIL (-1) +#define DONE 0 +static Terminal in_orig; + +/*--------------- Signal Handler routines -------------*/ + +static void tty_time_out(void) +{ + int exit_code; + signal(SIGALRM, SIG_IGN); + if(tty && need_tty_reset) + restore_tty (&in_orig); +#if future + if (fail_on_timeout) + exit_code=SHFAIL; + else { + if (default_choice && mode_defined) { + if (yes_no) { + if ('Y' == default_choice) + exit_code=0; + else + exit_code=1; + } else + exit_code=default_choice-minc+1; + } else + exit_code=DONE; + } +#else + exit_code = DONE; +#endif + exit(exit_code); +} + +static void cleanup_tty(void) +{ + if(tty && need_tty_reset) { + restore_tty (&in_orig); + setup_signal(); + } +} + +static void set_raw_tty(int mode) +{ + Terminal in_raw; + + if(mode != tty_mode && mode != -1) { + if(!handlerIsSet) { + /* Determine existing TTY settings */ + gtty (STDIN, &in_orig); + need_tty_reset = 1; + + /* Restore original TTY settings on exit */ + atexit(cleanup_tty); + handlerIsSet = 1; + } + + + setup_signal(); + signal (SIGALRM, (SIG_CAST) tty_time_out); + + /* Change STDIN settings to raw */ + + gtty (STDIN, &in_raw); + if(mode) { +#ifdef USE_SGTTY + in_raw.sg_flags |= CBREAK; +#else + in_raw.c_lflag &= ~ICANON; + in_raw.c_cc[VMIN]=1; + in_raw.c_cc[VTIME]=0; +#endif + stty (STDIN, &in_raw); + } else { +#ifdef USE_SGTTY + in_raw.sg_flags &= ~CBREAK; +#else + in_raw.c_lflag |= ICANON; +#endif + stty (STDIN, &in_raw); + } + tty_mode = mode; + discard_input(STDIN); + } +} +#endif + +FILE *opentty(int mode) +{ + if(notty) + return NULL; + if (tty == NULL) { + ttyfd = open("/dev/tty", O_RDONLY); + if(ttyfd >= 0) { + tty = fdopen(ttyfd, "r"); + } + } + if (tty == NULL){ + if ( !isatty(0) ){ + notty = 1; + return NULL; + } + ttyfd = 0; + tty = stdin; + } +#ifdef USE_RAWTERM + if(mtools_raw_tty) + set_raw_tty(mode); +#endif + return tty; +} + +int ask_confirmation(const char *format, const char *p1, const char *p2) +{ + char ans[10]; + + if(!opentty(-1)) + return 0; + + while (1) { + fprintf(stderr, format, p1, p2); + fflush(stderr); + fflush(opentty(-1)); + if (mtools_raw_tty) { + ans[0] = fgetc(opentty(1)); + fputs("\n", stderr); + } else { + fgets(ans,9, opentty(0)); + } + if (ans[0] == 'y' || ans[0] == 'Y') + return 0; + if (ans[0] == 'n' || ans[0] == 'N') + return -1; + } +} diff --git a/commands/i386/mtools-3.9.7/unixdir.c b/commands/i386/mtools-3.9.7/unixdir.c new file mode 100755 index 000000000..5996b45e6 --- /dev/null +++ b/commands/i386/mtools-3.9.7/unixdir.c @@ -0,0 +1,144 @@ +#include "sysincludes.h" +#include "msdos.h" +#include "stream.h" +#include "mtools.h" +#include "fsP.h" +#include "file.h" +#include "htable.h" +#include "mainloop.h" +#include <dirent.h> + +typedef struct Dir_t { + Class_t *Class; + int refs; + Stream_t *Next; + Stream_t *Buffer; + + struct stat stat; + char *pathname; + DIR *dir; +#ifdef HAVE_FCHDIR + int fd; +#endif +} Dir_t; + +/*#define FCHDIR_MODE*/ + +static int get_dir_data(Stream_t *Stream, time_t *date, mt_size_t *size, + int *type, int *address) +{ + DeclareThis(Dir_t); + + if(date) + *date = This->stat.st_mtime; + if(size) + *size = (mt_size_t) This->stat.st_size; + if(type) + *type = 1; + if(address) + *address = 0; + return 0; +} + +static int dir_free(Stream_t *Stream) +{ + DeclareThis(Dir_t); + + Free(This->pathname); + closedir(This->dir); + return 0; +} + +static Class_t DirClass = { + 0, /* read */ + 0, /* write */ + 0, /* flush */ + dir_free, /* free */ + 0, /* get_geom */ + get_dir_data , + 0 /* pre-allocate */ +}; + +#ifdef HAVE_FCHDIR +#define FCHDIR_MODE +#endif + +int unix_dir_loop(Stream_t *Stream, MainParam_t *mp); +int unix_loop(Stream_t *Stream, MainParam_t *mp, char *arg, + int follow_dir_link); + +int unix_dir_loop(Stream_t *Stream, MainParam_t *mp) +{ + DeclareThis(Dir_t); + struct dirent *entry; + char *newName; + int ret=0; + +#ifdef FCHDIR_MODE + int fd; + + fd = open(".", O_RDONLY); + chdir(This->pathname); +#endif + while((entry=readdir(This->dir)) != NULL) { + if(got_signal) + break; + if(isSpecial(entry->d_name)) + continue; +#ifndef FCHDIR_MODE + newName = malloc(strlen(This->pathname) + 1 + + strlen(entry->d_name) + 1); + if(!newName) { + ret = ERROR_ONE; + break; + } + strcpy(newName, This->pathname); + strcat(newName, "/"); + strcat(newName, entry->d_name); +#else + newName = entry->d_name; +#endif + ret |= unix_loop(Stream, mp, newName, 0); +#ifndef FCHDIR_MODE + free(newName); +#endif + } +#ifdef FCHDIR_MODE + fchdir(fd); + close(fd); +#endif + return ret; +} + +Stream_t *OpenDir(Stream_t *Stream, const char *filename) +{ + Dir_t *This; + + This = New(Dir_t); + + This->Class = &DirClass; + This->Next = 0; + This->refs = 1; + This->Buffer = 0; + This->pathname = malloc(strlen(filename)+1); + if(This->pathname == NULL) { + Free(This); + return NULL; + } + strcpy(This->pathname, filename); + + if(stat(filename, &This->stat) < 0) { + Free(This->pathname); + Free(This); + return NULL; + } + + This->dir = opendir(filename); + if(!This->dir) { + Free(This->pathname); + Free(This); + return NULL; + } + + return (Stream_t *) This; +} diff --git a/commands/i386/mtools-3.9.7/vfat.c b/commands/i386/mtools-3.9.7/vfat.c new file mode 100755 index 000000000..34f76e7ce --- /dev/null +++ b/commands/i386/mtools-3.9.7/vfat.c @@ -0,0 +1,748 @@ +/* vfat.c + * + * Miscellaneous VFAT-related functions + */ + +#include "sysincludes.h" +#include "msdos.h" +#include "mtools.h" +#include "vfat.h" +#include "file.h" +#include "dirCache.h" + +/* #define DEBUG */ + +const char *short_illegals=";+=[]',\"*\\<>/?:|"; +const char *long_illegals = "\"*\\<>/?:|\005"; + +/* Automatically derive a new name */ +static void autorename(char *name, + char tilda, char dot, const char *illegals, + int limit, int bump) +{ + int tildapos, dotpos; + unsigned int seqnum=0, maxseq=0; + char tmp; + char *p; + +#ifdef DEBUG + printf("In autorename for name=%s.\n", name); +#endif + tildapos = -1; + + for(p=name; *p ; p++) + if((*p < ' ' && *p != '\005') || strchr(illegals, *p)) { + *p = '_'; + bump = 0; + } + + for(dotpos=0; + name[dotpos] && dotpos < limit && name[dotpos] != dot ; + dotpos++) { + if(name[dotpos] == tilda) { + tildapos = dotpos; + seqnum = 0; + maxseq = 1; + } else if (name[dotpos] >= '0' && name[dotpos] <= '9') { + seqnum = seqnum * 10 + name[dotpos] - '0'; + maxseq = maxseq * 10; + } else + tildapos = -1; /* sequence number interrupted */ + } + if(tildapos == -1) { + /* no sequence number yet */ + if(dotpos > limit - 2) { + tildapos = limit - 2; + dotpos = limit; + } else { + tildapos = dotpos; + dotpos += 2; + } + seqnum = 1; + } else { + if(bump) + seqnum++; + if(seqnum > 999999) { + seqnum = 1; + tildapos = dotpos - 2; + /* this matches Win95's behavior, and also guarantees + * us that the sequence numbers never get shorter */ + } + if (seqnum == maxseq) { + if(dotpos >= limit) + tildapos--; + else + dotpos++; + } + } + + tmp = name[dotpos]; + if((bump && seqnum == 1) || seqnum > 1 || mtools_numeric_tail) + sprintf(name+tildapos,"%c%d",tilda, seqnum); + if(dot) + name[dotpos]=tmp; + /* replace the character if it wasn't a space */ +} + + +void autorename_short(char *name, int bump) +{ + autorename(name, '~', ' ', short_illegals, 8, bump); +} + +void autorename_long(char *name, int bump) +{ + autorename(name, '-', '\0', long_illegals, 255, bump); +} + + +static inline int unicode_read(struct unicode_char *in, char *out, int num) +{ + char *end_out = out+num; + + while(out < end_out) { + if (in->uchar) + *out = '_'; + else + *out = in->lchar; + ++out; + ++in; + } + return num; +} + + +void clear_vfat(struct vfat_state *v) +{ + v->subentries = 0; + v->status = 0; + v->present = 0; +} + + +/* sum_shortname + * + * Calculate the checksum that results from the short name in *dir. + * + * The sum is formed by circularly right-shifting the previous sum + * and adding in each character, from left to right, padding both + * the name and extension to maximum length with spaces and skipping + * the "." (hence always summing exactly 11 characters). + * + * This exact algorithm is required in order to remain compatible + * with Microsoft Windows-95 and Microsoft Windows NT 3.5. + * Thanks to Jeffrey Richter of Microsoft Systems Journal for + * pointing me to the correct algorithm. + * + * David C. Niemi (niemi@tux.org) 95.01.19 + */ +static inline unsigned char sum_shortname(char *name) +{ + unsigned char sum; + char *end = name+11; + + for (sum=0; name<end; ++name) + sum = ((sum & 1) ? 0x80 : 0) + (sum >> 1) + + (*name ? *name : ' '); + return(sum); +} + +/* check_vfat + * + * Inspect a directory and any associated VSEs. + * Return 1 if the VSEs comprise a valid long file name, + * 0 if not. + */ +static inline void check_vfat(struct vfat_state *v, struct directory *dir) +{ + char name[12]; + + if (! v->subentries) { +#ifdef DEBUG + fprintf(stderr, "check_vfat: no VSEs.\n"); +#endif + return; + } + + strncpy((char *)name, (char *)dir->name, 8); + strncpy((char *)name + 8, (char *)dir->ext, 3); + name[11] = '\0'; + + if (v->sum != sum_shortname(name)) + return; + + if( (v->status & ((1<<v->subentries) - 1)) != (1<<v->subentries) - 1) + return; /* missing entries */ + + /* zero out byte following last entry, for good measure */ + v->name[VSE_NAMELEN * v->subentries] = 0; + v->present = 1; +} + + +int clear_vses(Stream_t *Dir, int entrySlot, size_t last) +{ + direntry_t entry; + dirCache_t *cache; + int error; + + entry.Dir = Dir; + entry.entry = entrySlot; + + /*maximize(last, entry.entry + MAX_VFAT_SUBENTRIES);*/ + cache = allocDirCache(Dir, last); + if(!cache) { + fprintf(stderr, "Out of memory error in clear_vses\n"); + exit(1); + } + addFreeEntry(cache, entry.entry, last); + for (; entry.entry < last; ++entry.entry) { +#ifdef DEBUG + fprintf(stderr,"Clearing entry %d.\n", entry.entry); +#endif + dir_read(&entry, &error); + if(error) + return error; + if(!entry.dir.name[0] || entry.dir.name[0] == DELMARK) + break; + entry.dir.name[0] = DELMARK; + if (entry.dir.attr == 0xf) + entry.dir.attr = '\0'; + low_level_dir_write(&entry); + } + return 0; +} + +int write_vfat(Stream_t *Dir, char *shortname, char *longname, int start, + direntry_t *mainEntry) +{ + struct vfat_subentry *vse; + int vse_id, num_vses; + char *c; + direntry_t entry; + dirCache_t *cache; + char unixyName[13]; + + if(longname) { +#ifdef DEBUG + printf("Entering write_vfat with longname=\"%s\", start=%d.\n", + longname,start); +#endif + entry.Dir = Dir; + vse = (struct vfat_subentry *) &entry.dir; + /* Fill in invariant part of vse */ + vse->attribute = 0x0f; + vse->hash1 = vse->sector_l = vse->sector_u = 0; + vse->sum = sum_shortname(shortname); +#ifdef DEBUG + printf("Wrote checksum=%d for shortname %s.\n", + vse->sum,shortname); +#endif + num_vses = strlen(longname)/VSE_NAMELEN + 1; + for (vse_id = num_vses; vse_id; --vse_id) { + int end = 0; + + c = longname + (vse_id - 1) * VSE_NAMELEN; + + c += unicode_write(c, vse->text1, VSE1SIZE, &end); + c += unicode_write(c, vse->text2, VSE2SIZE, &end); + c += unicode_write(c, vse->text3, VSE3SIZE, &end); + + vse->id = (vse_id == num_vses) ? (vse_id | VSE_LAST) : vse_id; +#ifdef DEBUG + printf("Writing longname=(%s), VSE %d (%13s) at %d, end = %d.\n", + longname, vse_id, longname + (vse_id-1) * VSE_NAMELEN, + start + num_vses - vse_id, start + num_vses); +#endif + + entry.entry = start + num_vses - vse_id; + low_level_dir_write(&entry); + } + } else + num_vses = 0; + cache = allocDirCache(Dir, start + num_vses + 1); + if(!cache) { + fprintf(stderr, "Out of memory error\n"); + exit(1); + } + unix_name(shortname, shortname+8, 0, unixyName); + addUsedEntry(cache, start, start + num_vses + 1, longname, unixyName, + &mainEntry->dir); + low_level_dir_write(mainEntry); + return start + num_vses; +} + +void dir_write(direntry_t *entry) +{ + dirCacheEntry_t *dce; + dirCache_t *cache; + + if(entry->entry == -3) { + fprintf(stderr, "Attempt to write root directory pointer\n"); + exit(1); + } + + cache = allocDirCache(entry->Dir, entry->entry + 1); + if(!cache) { + fprintf(stderr, "Out of memory error in dir_write\n"); + exit(1); + } + dce = cache->entries[entry->entry]; + if(dce) { + if(entry->dir.name[0] == DELMARK) { + addFreeEntry(cache, dce->beginSlot, dce->endSlot); + } else { + dce->dir = entry->dir; + } + } + low_level_dir_write(entry); +} + + +/* + * The following function translates a series of vfat_subentries into + * data suitable for a dircache entry + */ +static inline void parse_vses(direntry_t *entry, + struct vfat_state *v) +{ + struct vfat_subentry *vse; + unsigned char id, last_flag; + char *c; + + vse = (struct vfat_subentry *) &entry->dir; + + id = vse->id & VSE_MASK; + last_flag = (vse->id & VSE_LAST); + if (id > MAX_VFAT_SUBENTRIES) { + fprintf(stderr, "parse_vses: invalid VSE ID %d at %d.\n", + id, entry->entry); + return; + } + +/* 950819: This code enforced finding the VSEs in order. Well, Win95 + * likes to write them in *reverse* order for some bizarre reason! So + * we pretty much have to tolerate them coming in any possible order. + * So skip this check, we'll do without it (What does this do, Alain?). + * + * 950820: Totally rearranged code to tolerate any order but to warn if + * they are not in reverse order like Win95 uses. + * + * 950909: Tolerate any order. We recognize new chains by mismatching + * checksums. In the event that the checksums match, new entries silently + * overwrite old entries of the same id. This should accept all valid + * entries, but may fail to reject invalid entries in some rare cases. + */ + + /* bad checksum, begin new chain */ + if(v->sum != vse->sum) { + clear_vfat(v); + v->sum = vse->sum; + } + +#ifdef DEBUG + if(v->status & (1 << (id-1))) + fprintf(stderr, + "parse_vses: duplicate VSE %d\n", vse->id); +#endif + + v->status |= 1 << (id-1); + if(last_flag) + v->subentries = id; + +#ifdef DEBUG + if (id > v->subentries) + /* simple test to detect entries preceding + * the "last" entry (really the first) */ + fprintf(stderr, + "parse_vses: new VSE %d sans LAST flag\n", + vse->id); +#endif + + c = &(v->name[VSE_NAMELEN * (id-1)]); + c += unicode_read(vse->text1, c, VSE1SIZE); + c += unicode_read(vse->text2, c, VSE2SIZE); + c += unicode_read(vse->text3, c, VSE3SIZE); +#ifdef DEBUG + printf("Read VSE %d at %d, subentries=%d, = (%13s).\n", + id,entry->entry,v->subentries,&(v->name[VSE_NAMELEN * (id-1)])); +#endif + if (last_flag) + *c = '\0'; /* Null terminate long name */ +} + + +static dirCacheEntry_t *vfat_lookup_loop_common(direntry_t *direntry, + dirCache_t *cache, + int lookForFreeSpace, + int *io_error) +{ + char newfile[13]; + int initpos = direntry->entry + 1; + struct vfat_state vfat; + char *longname; + int error; + + /* not yet cached */ + *io_error = 0; + clear_vfat(&vfat); + while(1) { + ++direntry->entry; + if(!dir_read(direntry, &error)){ + if(error) { + *io_error = error; + return NULL; + } + addFreeEntry(cache, initpos, direntry->entry); + return addEndEntry(cache, direntry->entry); + } + + if (direntry->dir.name[0] == '\0'){ + /* the end of the directory */ + if(lookForFreeSpace) + continue; + return addEndEntry(cache, direntry->entry); + } + if(direntry->dir.name[0] != DELMARK && + direntry->dir.attr == 0x0f) + parse_vses(direntry, &vfat); + else + /* the main entry */ + break; + } + + /* If we get here, it's a short name FAT entry, maybe erased. + * thus we should make sure that the vfat structure will be + * cleared before the next loop run */ + + /* deleted file */ + if (direntry->dir.name[0] == DELMARK) { + return addFreeEntry(cache, initpos, + direntry->entry + 1); + } + + check_vfat(&vfat, &direntry->dir); + if(!vfat.present) + vfat.subentries = 0; + + /* mark space between last entry and this one as free */ + addFreeEntry(cache, initpos, + direntry->entry - vfat.subentries); + + if (direntry->dir.attr & 0x8){ + strncpy(newfile, direntry->dir.name,8); + newfile[8]='\0'; + strncat(newfile, direntry->dir.ext,3); + newfile[11]='\0'; + } else + unix_name(direntry->dir.name, + direntry->dir.ext, + direntry->dir.Case, + newfile); + + if(vfat.present) + longname = vfat.name; + else + longname = 0; + + return addUsedEntry(cache, direntry->entry - vfat.subentries, + direntry->entry + 1, longname, + newfile, &direntry->dir); +} + +static inline dirCacheEntry_t *vfat_lookup_loop_for_read(direntry_t *direntry, + dirCache_t *cache, + int *io_error) +{ + int initpos = direntry->entry + 1; + dirCacheEntry_t *dce; + + *io_error = 0; + dce = cache->entries[initpos]; + if(dce) { + direntry->entry = dce->endSlot - 1; + return dce; + } else { + return vfat_lookup_loop_common(direntry, cache, 0, io_error); + } +} + + +typedef enum result_t { + RES_NOMATCH, + RES_MATCH, + RES_END, + RES_ERROR +} result_t; + + +/* + * 0 does not match + * 1 matches + * 2 end + */ +static result_t checkNameForMatch(struct direntry_t *direntry, + dirCacheEntry_t *dce, + const char *filename, + char *longname, + char *shortname, + int length, + int flags) +{ + switch(dce->type) { + case DCET_FREE: + return RES_NOMATCH; + case DCET_END: + return RES_END; + case DCET_USED: + break; + default: + fprintf(stderr, "Unexpected entry type %d\n", + dce->type); + return RES_ERROR; + } + + direntry->dir = dce->dir; + + /* make sure the entry is of an accepted type */ + if((direntry->dir.attr & 0x8) && !(flags & ACCEPT_LABEL)) + return RES_NOMATCH; + + + /*---------- multiple files ----------*/ + if(!((flags & MATCH_ANY) || + (dce->longName && + match(dce->longName, filename, direntry->name, 0, length)) || + match(dce->shortName, filename, direntry->name, 1, length))) { + + return RES_NOMATCH; + } + + /* entry of non-requested type, has to come after name + * checking because of clash handling */ + if(IS_DIR(direntry) && !(flags & ACCEPT_DIR)) { + if(!(flags & (ACCEPT_LABEL|MATCH_ANY|NO_MSG))) + fprintf(stderr, + "Skipping \"%s\", is a directory\n", + dce->shortName); + return RES_NOMATCH; + } + + if(!(direntry->dir.attr & (ATTR_LABEL | ATTR_DIR)) && + !(flags & ACCEPT_PLAIN)) { + if(!(flags & (ACCEPT_LABEL|MATCH_ANY|NO_MSG))) + fprintf(stderr, + "Skipping \"%s\", is not a directory\n", + dce->shortName); + return RES_NOMATCH; + } + + return RES_MATCH; +} + + +/* + * vfat_lookup looks for filenames in directory dir. + * if a name if found, it is returned in outname + * if applicable, the file is opened and its stream is returned in File + */ + +int vfat_lookup(direntry_t *direntry, const char *filename, int length, + int flags, char *shortname, char *longname) +{ + dirCacheEntry_t *dce; + result_t result; + dirCache_t *cache; + int io_error; + + if(length == -1 && filename) + length = strlen(filename); + + if (direntry->entry == -2) + return -1; + + cache = allocDirCache(direntry->Dir, direntry->entry+1); + if(!cache) { + fprintf(stderr, "Out of memory error in vfat_lookup [0]\n"); + exit(1); + } + + do { + dce = vfat_lookup_loop_for_read(direntry, cache, &io_error); + if(!dce) { + if (io_error) + return -2; + fprintf(stderr, "Out of memory error in vfat_lookup\n"); + exit(1); + } + result = checkNameForMatch(direntry, dce, + filename, + longname, shortname, + length, flags); + } while(result == RES_NOMATCH); + + if(result == RES_MATCH){ + if(longname){ + if(dce->longName) + strcpy(longname, dce->longName); + else + *longname ='\0'; + } + if(shortname) + strcpy(shortname, dce->shortName); + direntry->beginSlot = dce->beginSlot; + direntry->endSlot = dce->endSlot-1; + return 0; /* file found */ + } else { + direntry->entry = -2; + return -1; /* no file found */ + } +} + +static inline dirCacheEntry_t *vfat_lookup_loop_for_insert(direntry_t *direntry, + int initpos, + dirCache_t *cache) +{ + dirCacheEntry_t *dce; + int io_error; + + dce = cache->entries[initpos]; + if(dce && dce->type != DCET_END) { + return dce; + } else { + direntry->entry = initpos - 1; + dce = vfat_lookup_loop_common(direntry, cache, 1, &io_error); + if(!dce) { + if (io_error) { + return NULL; + } + fprintf(stderr, + "Out of memory error in vfat_lookup_loop\n"); + exit(1); + } + return cache->entries[initpos]; + } +} + +static void accountFreeSlots(struct scan_state *ssp, dirCacheEntry_t *dce) +{ + if(ssp->got_slots) + return; + + if(ssp->free_end != dce->beginSlot) { + ssp->free_start = dce->beginSlot; + } + ssp->free_end = dce->endSlot; + + if(ssp->free_end - ssp->free_start >= ssp->size_needed) { + ssp->got_slots = 1; + ssp->slot = ssp->free_start + ssp->size_needed - 1; + } +} + +/* lookup_for_insert replaces the old scandir function. It directly + * calls into vfat_lookup_loop, thus eliminating the overhead of the + * normal vfat_lookup + */ +int lookupForInsert(Stream_t *Dir, + char *dosname, + char *longname, + struct scan_state *ssp, + int ignore_entry, + int source_entry, + int pessimisticShortRename) +{ + direntry_t entry; + int ignore_match; + dirCacheEntry_t *dce; + dirCache_t *cache; + int pos; /* position _before_ the next answered entry */ + char shortName[13]; + + ignore_match = (ignore_entry == -2 ); + + initializeDirentry(&entry, Dir); + ssp->match_free = 0; + + /* hash bitmap of already encountered names. Speeds up batch appends + * to huge directories, because in the best case, we only need to scan + * the new entries rather than the whole directory */ + cache = allocDirCache(Dir, 1); + if(!cache) { + fprintf(stderr, "Out of memory error in lookupForInsert\n"); + exit(1); + } + + if(!ignore_match) + unix_name(dosname, dosname + 8, 0, shortName); + + pos = cache->nrHashed; + if(source_entry >= 0 || + (pos && isHashed(cache, longname))) { + pos = 0; + } else if(pos && !ignore_match && isHashed(cache, shortName)) { + if(pessimisticShortRename) { + ssp->shortmatch = -2; + return 1; + } + pos = 0; + } else if(growDirCache(cache, pos) < 0) { + fprintf(stderr, "Out of memory error in vfat_looup [0]\n"); + exit(1); + } + do { + dce = vfat_lookup_loop_for_insert(&entry, pos, cache); + switch(dce->type) { + case DCET_FREE: + accountFreeSlots(ssp, dce); + break; + case DCET_USED: + if(!(dce->dir.attr & 0x8) && + dce->endSlot - 1 == source_entry) + accountFreeSlots(ssp, dce); + + /* labels never match, neither does the + * ignored entry */ + if( (dce->dir.attr & 0x8) || + (dce->endSlot - 1 == ignore_entry) ) + break; + + /* check long name */ + if((dce->longName && + !strcasecmp(dce->longName, longname)) || + (dce->shortName && + !strcasecmp(dce->shortName, longname))) { + ssp->longmatch = dce->endSlot - 1; + /* long match is a reason for + * immediate stop */ + return 1; + } + + /* Long name or not, always check for + * short name match */ + if (!ignore_match && + !strcasecmp(shortName, dce->shortName)) + ssp->shortmatch = dce->endSlot - 1; + break; + case DCET_END: + break; + } + pos = dce->endSlot; + } while(dce->type != DCET_END); + if (ssp->shortmatch > -1) + return 1; + ssp->max_entry = dce->beginSlot; + if (ssp->got_slots) + return 6; /* Success */ + + /* Need more room. Can we grow the directory? */ + if(!isRootDir(Dir)) + return 5; /* OK, try to grow the directory */ + + fprintf(stderr, "No directory slots\n"); + return -1; +} + + + +/* End vfat.c */ diff --git a/commands/i386/mtools-3.9.7/vfat.h b/commands/i386/mtools-3.9.7/vfat.h new file mode 100755 index 000000000..0620d4723 --- /dev/null +++ b/commands/i386/mtools-3.9.7/vfat.h @@ -0,0 +1,100 @@ +#ifndef MTOOLS_VFAT_H +#define MTOOLS_VFAT_H + +#include "msdos.h" + +/* + * VFAT-related common header file + */ +#define VFAT_SUPPORT + +struct unicode_char { + char lchar; + char uchar; +}; + + +/* #define MAX_VFAT_SUBENTRIES 32 */ /* Theoretical max # of VSEs */ +#define MAX_VFAT_SUBENTRIES 20 /* Max useful # of VSEs */ +#define VSE_NAMELEN 13 + +#define VSE1SIZE 5 +#define VSE2SIZE 6 +#define VSE3SIZE 2 + +#include "stream.h" + +struct vfat_subentry { + unsigned char id; /* 0x40 = last; & 0x1f = VSE ID */ + struct unicode_char text1[VSE1SIZE] PACKED; + unsigned char attribute; /* 0x0f for VFAT */ + unsigned char hash1; /* Always 0? */ + unsigned char sum; /* Checksum of short name */ + struct unicode_char text2[VSE2SIZE] PACKED; + unsigned char sector_l; /* 0 for VFAT */ + unsigned char sector_u; /* 0 for VFAT */ + struct unicode_char text3[VSE3SIZE] PACKED; +}; + +/* Enough size for a worst case number of full VSEs plus a null */ +#define VBUFSIZE ((MAX_VFAT_SUBENTRIES*VSE_NAMELEN) + 1) + +/* Max legal length of a VFAT long name */ +#define MAX_VNAMELEN (255) + +#define VSE_PRESENT 0x01 +#define VSE_LAST 0x40 +#define VSE_MASK 0x1f + +struct vfat_state { + char name[VBUFSIZE]; + int status; /* is now a bit map of 32 bits */ + int subentries; + unsigned char sum; /* no need to remember the sum for each entry, + * it is the same anyways */ + int present; +}; + + +struct scan_state { + int match_free; + int shortmatch; + int longmatch; + int free_start; + int free_end; + int slot; + int got_slots; + int size_needed; + int max_entry; +}; + +#include "mtoolsDirent.h" + +void clear_vfat(struct vfat_state *); +int unicode_write(char *, struct unicode_char *, int num, int *end); + +int clear_vses(Stream_t *, int, size_t); +void autorename_short(char *, int); +void autorename_long(char *, int); + +int lookupForInsert(Stream_t *Dir, + char *dosname, + char *longname, + struct scan_state *ssp, + int ignore_entry, + int source_entry, + int pessimisticShortRename); + +#define DO_OPEN 1 /* open all files that are found */ +#define ACCEPT_LABEL 0x08 +#define ACCEPT_DIR 0x10 +#define ACCEPT_PLAIN 0x20 +#define MATCH_ANY 0x40 +#define NO_MSG 0x80 +#define NO_DOTS 0x100 /* accept no dots if matched by wildcard */ +#define DO_OPEN_DIRS 0x400 /* open all directories that are found */ +#define OPEN_PARENT 0x1000 /* in target lookup, open parent + * instead of file itself */ +#define NO_UNIX 0x2000 /* in target lookup, consider all files to reside on + * the DOS fs */ +#endif diff --git a/commands/i386/mtools-3.9.7/xdf_io.c b/commands/i386/mtools-3.9.7/xdf_io.c new file mode 100755 index 000000000..593880199 --- /dev/null +++ b/commands/i386/mtools-3.9.7/xdf_io.c @@ -0,0 +1,696 @@ +/* + * Io to an xdf disk + * + * written by: + * + * Alain L. Knaff + * alain@linux.lu + * + */ + + +#include "sysincludes.h" +#ifdef OS_linux +#include "msdos.h" +#include "mtools.h" +#include "devices.h" +#include "xdf_io.h" + +extern int errno; + +/* Algorithms can't be patented */ + +typedef struct sector_map { + unsigned int head:1; + unsigned int size:7; +} sector_map_t; + + +struct { + unsigned char track_size; + unsigned int track0_size:7; + unsigned int rootskip:1; + unsigned char rate; + sector_map_t map[9]; +} xdf_table[]= { + { + 19, 16, 0, 0, + { {0,3}, {0,6}, {1,2}, {0,2}, {1,6}, {1,3}, {0,0} } + }, + { + 23, 19, 0, 0, + { {0,3}, {0,4}, {1,6}, {0,2}, {1,2}, {0,6}, {1,4}, {1,3}, {0,0} } + }, + { + 46, 37, 0x43, 1, + { {0,3}, {0,4}, {0,5}, {0,7}, {1,3}, {1,4}, {1,5}, {1,7}, {0,0} } + }, + { + 24, 20, 0, 1, + { {0,5}, {1,6}, {0,6}, {1, 5} } + }, + { + 48, 41, 0, 1, + { {0,6}, {1,7}, {0,7}, {1, 6} } + } +}; + +#define NUMBER(x) (sizeof(x)/sizeof(x[0])) + +typedef struct { + unsigned char begin; /* where it begins */ + unsigned char end; + unsigned char sector; + unsigned char sizecode; + + unsigned int dirty:1; + unsigned int phantom:2; + unsigned int valid:1; + unsigned int head:1; +} TrackMap_t; + + + +typedef struct Xdf_t { + Class_t *Class; + int refs; + Stream_t *Next; + Stream_t *Buffer; + + int fd; + char *buffer; + + int current_track; + + sector_map_t *map; + + int track_size; + int track0_size; + int sector_size; + int FatSize; + int RootDirSize; + TrackMap_t *track_map; + + unsigned char last_sector; + unsigned char rate; + + unsigned int stretch:1; + unsigned int rootskip:1; + signed int drive:4; +} Xdf_t; + +typedef struct { + unsigned char head; + unsigned char sector; + unsigned char ptr; +} Compactify_t; + + +static int analyze_reply(RawRequest_t *raw_cmd, int do_print) +{ + int ret, bytes, newbytes; + + bytes = 0; + while(1) { + ret = analyze_one_reply(raw_cmd, &newbytes, do_print); + bytes += newbytes; + switch(ret) { + case 0: + return bytes; + case 1: + raw_cmd++; + break; + case -1: + if(bytes) + return bytes; + else + return 0; + } + } +} + + + +static int send_cmd(int fd, RawRequest_t *raw_cmd, int nr, + const char *message, int retries) +{ + int j; + int ret=-1; + + if(!nr) + return 0; + for (j=0; j< retries; j++){ + switch(send_one_cmd(fd, raw_cmd, message)) { + case -1: + return -1; + case 1: + j++; + continue; + case 0: + break; + } + if((ret=analyze_reply(raw_cmd, j)) > 0) + return ret; /* ok */ + } + if(j > 1 && j == retries) { + fprintf(stderr,"Too many errors, giving up\n"); + return 0; + } + return -1; +} + + + +#define REC (This->track_map[ptr]) +#define END(x) (This->track_map[(x)].end) +#define BEGIN(x) (This->track_map[(x)].begin) + +static int add_to_request(Xdf_t *This, int ptr, + RawRequest_t *request, int *nr, + int direction, Compactify_t *compactify) +{ +#if 0 + if(direction == MT_WRITE) { + printf("writing %d: %d %d %d %d [%02x]\n", + ptr, This->current_track, + REC.head, REC.sector, REC.sizecode, + *(This->buffer + ptr * This->sector_size)); + } else + printf(" load %d.%d\n", This->current_track, ptr); +#endif + if(REC.phantom) { + if(direction== MT_READ) + memset(This->buffer + ptr * This->sector_size, 0, + 128 << REC.sizecode); + return 0; + } + + if(*nr && + RR_SIZECODE(request+(*nr)-1) == REC.sizecode && + compactify->head == REC.head && + compactify->ptr + 1 == ptr && + compactify->sector +1 == REC.sector) { + RR_SETSIZECODE(request+(*nr)-1, REC.sizecode); + } else { + if(*nr) + RR_SETCONT(request+(*nr)-1); + RR_INIT(request+(*nr)); + RR_SETDRIVE(request+(*nr), This->drive); + RR_SETRATE(request+(*nr), This->rate); + RR_SETTRACK(request+(*nr), This->current_track); + RR_SETPTRACK(request+(*nr), + This->current_track << This->stretch); + RR_SETHEAD(request+(*nr), REC.head); + RR_SETSECTOR(request+(*nr), REC.sector); + RR_SETSIZECODE(request+(*nr), REC.sizecode); + RR_SETDIRECTION(request+(*nr), direction); + RR_SETDATA(request+(*nr), + (caddr_t) This->buffer + ptr * This->sector_size); + (*nr)++; + } + compactify->ptr = ptr; + compactify->head = REC.head; + compactify->sector = REC.sector; + return 0; +} + + +static void add_to_request_if_invalid(Xdf_t *This, int ptr, + RawRequest_t *request, int *nr, + Compactify_t *compactify) +{ + if(!REC.valid) + add_to_request(This, ptr, request, nr, MT_READ, compactify); + +} + + +static void adjust_bounds(Xdf_t *This, off_t *begin, off_t *end) +{ + /* translates begin and end from byte to sectors */ + *begin = *begin / This->sector_size; + *end = (*end + This->sector_size - 1) / This->sector_size; +} + + +static inline int try_flush_dirty(Xdf_t *This) +{ + int ptr, nr, bytes; + RawRequest_t requests[100]; + Compactify_t compactify; + + if(This->current_track < 0) + return 0; + + nr = 0; + for(ptr=0; ptr < This->last_sector; ptr=REC.end) + if(REC.dirty) + add_to_request(This, ptr, + requests, &nr, + MT_WRITE, &compactify); +#if 1 + bytes = send_cmd(This->fd,requests, nr, "writing", 4); + if(bytes < 0) + return bytes; +#else + bytes = 0xffffff; +#endif + for(ptr=0; ptr < This->last_sector; ptr=REC.end) + if(REC.dirty) { + if(bytes >= REC.end - REC.begin) { + bytes -= REC.end - REC.begin; + REC.dirty = 0; + } else + return 1; + } + return 0; +} + + + +static int flush_dirty(Xdf_t *This) +{ + int ret; + + while((ret = try_flush_dirty(This))) { + if(ret < 0) + return ret; + } + return 0; +} + + +static int load_data(Xdf_t *This, off_t begin, off_t end, int retries) +{ + int ptr, nr, bytes; + RawRequest_t requests[100]; + Compactify_t compactify; + + adjust_bounds(This, &begin, &end); + + ptr = begin; + nr = 0; + for(ptr=REC.begin; ptr < end ; ptr = REC.end) + add_to_request_if_invalid(This, ptr, requests, &nr, + &compactify); + bytes = send_cmd(This->fd,requests, nr, "reading", retries); + if(bytes < 0) + return bytes; + ptr = begin; + for(ptr=REC.begin; ptr < end ; ptr = REC.end) { + if(!REC.valid) { + if(bytes >= REC.end - REC.begin) { + bytes -= REC.end - REC.begin; + REC.valid = 1; + } else if(ptr > begin) + return ptr * This->sector_size; + else + return -1; + } + } + return end * This->sector_size; +} + +static void mark_dirty(Xdf_t *This, off_t begin, off_t end) +{ + int ptr; + + adjust_bounds(This, &begin, &end); + + ptr = begin; + for(ptr=REC.begin; ptr < end ; ptr = REC.end) { + REC.valid = 1; + if(!REC.phantom) + REC.dirty = 1; + } +} + + +static int load_bounds(Xdf_t *This, off_t begin, off_t end) +{ + off_t lbegin, lend; + int endp1, endp2; + + lbegin = begin; + lend = end; + + adjust_bounds(This, &lbegin, &lend); + + if(begin != BEGIN(lbegin) * This->sector_size && + end != BEGIN(lend) * This->sector_size && + lend < END(END(lbegin))) + /* contiguous end & begin, load them in one go */ + return load_data(This, begin, end, 4); + + if(begin != BEGIN(lbegin) * This->sector_size) { + endp1 = load_data(This, begin, begin, 4); + if(endp1 < 0) + return endp1; + } + + if(end != BEGIN(lend) * This->sector_size) { + endp2 = load_data(This, end, end, 4); + if(endp2 < 0) + return BEGIN(lend) * This->sector_size; + } + return lend * This->sector_size; +} + + +static int fill_t0(Xdf_t *This, int ptr, int size, int *sector, int *head) +{ + int n; + + for(n = 0; n < size; ptr++,n++) { + REC.head = *head; + REC.sector = *sector + 129; + REC.phantom = 0; + (*sector)++; + if(!*head && *sector >= This->track0_size - 8) { + *sector = 0; + *head = 1; + } + } + return ptr; +} + + +static int fill_phantoms(Xdf_t *This, int ptr, int size) +{ + int n; + + for(n = 0; n < size; ptr++,n++) + REC.phantom = 1; + return ptr; +} + +static void decompose(Xdf_t *This, int where, int len, off_t *begin, + off_t *end, int boot) +{ + int ptr, track; + sector_map_t *map; + int lbegin, lend; + + track = where / This->track_size / 1024; + + *begin = where - track * This->track_size * 1024; + *end = where + len - track * This->track_size * 1024; + maximize(*end, This->track_size * 1024); + + if(This->current_track == track && !boot) + /* already OK, return immediately */ + return; + if(!boot) + flush_dirty(This); + This->current_track = track; + + if(track) { + for(ptr=0, map=This->map; map->size; map++) { + /* iterate through all sectors */ + lbegin = ptr; + lend = ptr + (128 << map->size) / This->sector_size; + for( ; ptr < lend ; ptr++) { + REC.begin = lbegin; + REC.end = lend; + + REC.head = map->head; + REC.sector = map->size + 128; + REC.sizecode = map->size; + + REC.valid = 0; + REC.dirty = 0; + REC.phantom = 0; + } + } + REC.begin = REC.end = ptr; + } else { + int sector, head; + + head = 0; + sector = 0; + + for(ptr=boot; ptr < 2 * This->track_size; ptr++) { + REC.begin = ptr; + REC.end = ptr+1; + + REC.sizecode = 2; + + REC.valid = 0; + REC.dirty = 0; + } + + /* boot & 1st fat */ + ptr=fill_t0(This, 0, 1 + This->FatSize, §or, &head); + + /* second fat */ + ptr=fill_phantoms(This, ptr, This->FatSize); + + /* root dir */ + ptr=fill_t0(This, ptr, This->RootDirSize, §or, &head); + + /* "bad sectors" at the beginning of the fs */ + ptr=fill_phantoms(This, ptr, 5); + + if(This->rootskip) + sector++; + + /* beginning of the file system */ + ptr = fill_t0(This, ptr, + (This->track_size - This->FatSize) * 2 - + This->RootDirSize - 6, + §or, &head); + } + This->last_sector = ptr; +} + + +static int xdf_read(Stream_t *Stream, char *buf, mt_off_t where, size_t len) +{ + off_t begin, end; + size_t len2; + DeclareThis(Xdf_t); + + decompose(This, truncBytes32(where), len, &begin, &end, 0); + len2 = load_data(This, begin, end, 4); + if(len2 < 0) + return len2; + len2 -= begin; + maximize(len, len2); + memcpy(buf, This->buffer + begin, len); + return end - begin; +} + +static int xdf_write(Stream_t *Stream, char *buf, mt_off_t where, size_t len) +{ + off_t begin, end; + size_t len2; + DeclareThis(Xdf_t); + + decompose(This, truncBytes32(where), len, &begin, &end, 0); + len2 = load_bounds(This, begin, end); + if(len2 < 0) + return len2; + maximize(end, len2); + len2 -= begin; + maximize(len, len2); + memcpy(This->buffer + begin, buf, len); + mark_dirty(This, begin, end); + return end - begin; +} + +static int xdf_flush(Stream_t *Stream) +{ + DeclareThis(Xdf_t); + + return flush_dirty(This); +} + +static int xdf_free(Stream_t *Stream) +{ + DeclareThis(Xdf_t); + Free(This->track_map); + Free(This->buffer); + return close(This->fd); +} + + +static int check_geom(struct device *dev, int media, struct bootsector *boot) +{ + int sect; + + if(media >= 0xfc && media <= 0xff) + return 1; /* old DOS */ + + if (!IS_MFORMAT_ONLY(dev)) { + if(compare(dev->sectors, 19) && + compare(dev->sectors, 23) && + compare(dev->sectors, 24) && + compare(dev->sectors, 46) && + compare(dev->sectors, 48)) + return 1; + + /* check against contradictory info from configuration file */ + if(compare(dev->heads, 2)) + return 1; + } + + /* check against info from boot */ + if(boot) { + sect = WORD(nsect); + if((sect != 19 && sect != 23 && sect != 24 && + sect != 46 && sect != 48) || + (!IS_MFORMAT_ONLY(dev) && compare(dev->sectors, sect)) || + WORD(nheads) !=2) + return 1; + } + return 0; +} + +static void set_geom(struct bootsector *boot, struct device *dev) +{ + /* fill in config info to be returned to user */ + dev->heads = 2; + dev->use_2m = 0xff; + if(boot) { + dev->sectors = WORD(nsect); + if(WORD(psect)) + dev->tracks = WORD(psect) / dev->sectors / 2; + } +} + +static int config_geom(Stream_t *Stream, struct device *dev, + struct device *orig_dev, int media, + struct bootsector *boot) +{ + if(check_geom(dev, media, boot)) + return 1; + set_geom(boot,dev); + return 0; +} + +static Class_t XdfClass = { + xdf_read, + xdf_write, + xdf_flush, + xdf_free, + config_geom, + 0, /* get_data */ + 0 /* pre-allocate */ +}; + +Stream_t *XdfOpen(struct device *dev, char *name, + int mode, char *errmsg, struct xdf_info *info) +{ + Xdf_t *This; + off_t begin, end; + struct bootsector *boot; + int type; + + if(dev && (!SHOULD_USE_XDF(dev) || check_geom(dev, 0, 0))) + return NULL; + + This = New(Xdf_t); + if (!This) + return NULL; + + This->Class = &XdfClass; + This->sector_size = 512; + This->stretch = 0; + + precmd(dev); + This->fd = open(name, mode | dev->mode | O_EXCL | O_NDELAY); + if(This->fd < 0) { +#ifdef HAVE_SNPRINTF + snprintf(errmsg,199,"xdf floppy: open: \"%s\"", strerror(errno)); +#else + sprintf(errmsg,"xdf floppy: open: \"%s\"", strerror(errno)); +#endif + goto exit_0; + } + closeExec(This->fd); + + This->drive = GET_DRIVE(This->fd); + if(This->drive < 0) + goto exit_1; + + /* allocate buffer */ + This->buffer = (char *) malloc(96 * 512); + if (!This->buffer) + goto exit_1; + + This->current_track = -1; + This->track_map = (TrackMap_t *) + calloc(96, sizeof(TrackMap_t)); + if(!This->track_map) + goto exit_2; + + /* lock the device on writes */ + if (lock_dev(This->fd, mode == O_RDWR, dev)) { +#ifdef HAVE_SNPRINTF + snprintf(errmsg,199,"xdf floppy: device \"%s\" busy:", + dev->name); +#else + sprintf(errmsg,"xdf floppy: device \"%s\" busy:", + dev->name); +#endif + goto exit_3; + } + + /* Before reading the boot sector, assume dummy values suitable + * for reading at least the boot sector */ + This->track_size = 11; + This->track0_size = 6; + This->rate = 0; + This->FatSize = 9; + This->RootDirSize = 1; + decompose(This, 0, 512, &begin, &end, 0); + if (load_data(This, 0, 1, 1) < 0 ) { + This->rate = 0x43; + if(load_data(This, 0, 1, 1) < 0) + goto exit_3; + } + + boot = (struct bootsector *) This->buffer; + This->FatSize = WORD(fatlen); + This->RootDirSize = WORD(dirents)/16; + This->track_size = WORD(nsect); + for(type=0; type < NUMBER(xdf_table); type++) { + if(xdf_table[type].track_size == This->track_size) { + This->map = xdf_table[type].map; + This->track0_size = xdf_table[type].track0_size; + This->rootskip = xdf_table[type].rootskip; + break; + } + } + if(type == NUMBER(xdf_table)) + goto exit_3; + + if(info) { + info->RootDirSize = This->RootDirSize; + info->FatSize = This->FatSize; + info->BadSectors = 5; + } + decompose(This, 0, 512, &begin, &end, 1); + + This->refs = 1; + This->Next = 0; + This->Buffer = 0; + if(dev) + set_geom(boot, dev); + return (Stream_t *) This; + +exit_3: + Free(This->track_map); +exit_2: + Free(This->buffer); +exit_1: + close(This->fd); +exit_0: + Free(This); + return NULL; +} + +#endif + +/* Algorithms can't be patented */ + diff --git a/commands/i386/mtools-3.9.7/xdf_io.h b/commands/i386/mtools-3.9.7/xdf_io.h new file mode 100755 index 000000000..ea0740a2e --- /dev/null +++ b/commands/i386/mtools-3.9.7/xdf_io.h @@ -0,0 +1,16 @@ +#ifndef MTOOLS_XDFIO_H +#define MTOOLS_XDFIO_H + +#include "msdos.h" +#include "stream.h" + +struct xdf_info { + int FatSize; + int RootDirSize; + int BadSectors; +}; + +Stream_t *XdfOpen(struct device *dev, char *name, + int mode, char *errmsg, struct xdf_info *info); + +#endif diff --git a/commands/i86/Makefile b/commands/i86/Makefile new file mode 100755 index 000000000..8f89b319b --- /dev/null +++ b/commands/i86/Makefile @@ -0,0 +1,22 @@ +# Makefile for commands/i86. + +CFLAGS = -D_MINIX -D_POSIX_SOURCE +CCLD = $(CC) -i $(CFLAGS) +MAKE = exec make -$(MAKEFLAGS) + +all: cc + +cc: cc.c + $(CCLD) -o $@ $? + install -S 6kb $@ + +install: /usr/bin/cc /usr/bin/m2 /usr/bin/pc + +/usr/bin/cc: cc + install -cs -o bin $? $@ + +/usr/bin/m2 /usr/bin/pc: /usr/bin/cc + install -l $? $@ + +clean: + rm -rf a.out core cc diff --git a/commands/i86/cc.c b/commands/i86/cc.c new file mode 100755 index 000000000..f753232db --- /dev/null +++ b/commands/i86/cc.c @@ -0,0 +1,1059 @@ +/* Driver for Minix compilers. + Written june 1987 by Ceriel J.H. Jacobs, partly derived from old + cc-driver, written by Erik Baalbergen. + This driver is mostly table-driven, the table being in the form of + some global initialized structures. +*/ +/* $Header$ */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <signal.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> + +/* Paths. (Executables in /usr are first tried with /usr stripped off.) */ +#define SHELL "/bin/sh" +#define PP "/usr/lib/ncpp" +#define IRREL "/usr/lib/irrel" +#define CEM "/usr/lib/ncem" +#define M2EM "/usr/lib/nm2em" +#define ENCODE "/usr/lib/em_encode" +#define OPT "/usr/lib/nopt" +#define CG "/usr/lib/ncg" +#define AS "/usr/lib/as" +#define LD "/usr/lib/ld" +#define CV "/usr/lib/cv" +#define LIBDIR "/usr/lib" +#define CRT "/usr/lib/ncrtso.o" +#define PEM "/usr/lib/npem" +#define PRT "/usr/lib/nprtso.o" +#define M2RT "/usr/lib/nm2rtso.o" +#define LIBC "/usr/lib/libd.a", "/usr/lib/libc.a" +#define LIBP "/usr/lib/libp.a", "/usr/lib/libc.a" +#define LIBM2 "/usr/lib/libm2.a", "/usr/lib/libc.a" +#define END "/usr/lib/libe.a", "/usr/lib/end.a" +#define M2DEF "-I/usr/lib/m2" + + +/* every pass that this program knows about has associated with it + a structure, containing such information as its name, where it + resides, the flags it accepts, and the like. +*/ +struct passinfo { + char *p_name; /* name of this pass */ + char *p_path; /* where is it */ + char *p_from; /* suffix of source (comma-separated list) */ + char *p_to; /* suffix of destination */ + char *p_acceptflags; /* comma separated list; format: + flag + flag* + flag=xxx + flag*=xxx[*] + where a star matches a, possibly empty, + string + */ + int p_flags; +#define INPUT 01 /* needs input file as argument */ +#define OUTPUT 02 /* needs output file as argument */ +#define LOADER 04 /* this pass is the loader */ +#define STDIN 010 /* reads from standard input */ +#define STDOUT 020 /* writes on standard output */ +#define NOCLEAN 040 /* do not remove target if this pass fails */ +#define O_OUTPUT 0100 /* -o outputfile, hack for as */ +#define PREPALWAYS 0200 /* always to be preprocessed */ +#define PREPCOND 0400 /* preprocessed when starting with '#' */ +#define PREPNOLN 01000 /* suppress line number info (cpp -P) */ +}; + +#define MAXHEAD 10 +#define MAXTAIL 5 +#define MAXPASS 7 + +/* Every language handled by this program has a "compile" structure + associated with it, describing the start-suffix, how the driver for + this language is called, which passes must be called, which flags + and arguments must be passed to these passes, etc. + The language is determined by the suffix of the argument program. + However, if this suffix does not determine a language (DEFLANG), + the callname is used. + Notice that the 's' suffix does not determine a language, because + the input file could have been derived from f.i. a C-program. + So, if you use "cc x.s", the C-runtime system will be used, but if + you use "as x.s", it will not. +*/ +struct compile { + char *c_suffix; /* starting suffix of this list of passes */ + char *c_callname; /* affects runtime system loaded with program */ + struct pass { + char *pp_name; /* name of the pass */ + char *pp_head[MAXHEAD]; /* args in front of filename */ + char *pp_tail[MAXTAIL]; /* args after filename */ + } c_passes[MAXPASS]; + int c_flags; +#define DEFLANG 010 /* this suffix determines a language */ +}; + +struct passinfo passinfo[] = { + { "cpp", PP, "CPP", "i", "wo=o,I*,D*,U*,P", INPUT|STDOUT }, + { "irrel", IRREL, "i", "i", "m", INPUT}, + { "cem", CEM, "i,c", "k", "m=o,p,wa=a,wo=o,ws=s,w,T*", INPUT|OUTPUT|PREPALWAYS }, + { "pc", PEM, "i,p", "k", "n=L,w,a,A,R", INPUT|OUTPUT|PREPCOND }, + { "m2", M2EM, "i,mod", "k", "n=L,w*,A,R,W*,3,I*", INPUT|OUTPUT|PREPCOND }, + { "encode", ENCODE, "i,e", "k", "", INPUT|STDOUT|PREPCOND|PREPNOLN }, + { "opt", OPT, "k", "m", "", STDIN|STDOUT }, + { "cg", CG, "m", "s", "O=p4", INPUT|OUTPUT }, + { "as", AS, "i,s", "o", "T*", INPUT|O_OUTPUT|PREPCOND }, + { "ld", LD, "o", "out", "i,s", INPUT|LOADER }, /* changed */ + { "cv", CV, "out", 0, "", INPUT|OUTPUT|NOCLEAN }, /* must come after loader */ + { 0} +}; + +#define PREP_FLAGS "-D_EM_WSIZE=2", "-D_EM_PSIZE=2", "-D_EM_SSIZE=2", \ + "-D_EM_LSIZE=4", "-D_EM_FSIZE=4", "-D_EM_DSIZE=8", \ + "-D__ACK__", "-D__minix", "-D__i86" + +struct pass preprocessor = { "cpp", + { PREP_FLAGS } + , {0} + }; + +struct pass prepnoln = { "cpp", + { PREP_FLAGS, "-P" } + , {0} + }; + +struct pass irrel = { "irrel", + {0} + }; + +/* The "*" in the arguments for the loader indicates the place where the + * fp-emulation library should come. + */ +struct compile passes[] = { +{ "c", "cc", + { { "cem", {"-L"}, {0} }, /* changed */ + { "opt", {0}, {0} }, + { "cg", {0}, {0} }, + { "as", {"-"}, {0} }, + { "ld", {CRT}, /* changed */ + {LIBC, "*", END}}, + { "cv", {0}, {0} } + }, + DEFLANG +}, +{ "p", "pc", + { { "pc", {0}, {0} }, + { "opt", {0}, {0} }, + { "cg", {0}, {0} }, + { "as", {"-"}, {0} }, + { "ld", {PRT}, + {LIBP, + "*", END}}, + { "cv", {0}, {0} } + }, + DEFLANG +}, +{ "mod", "m2", + { { "m2", {M2DEF}, {0} }, + { "opt", {0}, {0} }, + { "cg", {0}, {0} }, + { "as", {"-"}, {0} }, + { "ld", {M2RT}, + {LIBM2, + "*", END}}, + { "cv", {0}, {0} } + }, + DEFLANG +}, +{ "e", "encode", + { { "encode", {0}, {0}}, + { "opt", {0}, {0} }, + { "cg", {0}, {0} }, + { "as", {"-"}, {0} }, + { "ld", {0}, {"*", END}}, + { "cv", {0}, {0} } + }, + DEFLANG +}, +{ "s", "as", + { { "as", {0}, {0}} + }, + 0 +}, +{ "CPP", "cpp", + { { "cpp", {PREP_FLAGS}, {0}} + }, + DEFLANG +}, +{ 0}, +}; + +#define MAXARGC 150 /* maximum number of arguments allowed in a list */ +#define USTR_SIZE 64 /* maximum length of string variable */ + +typedef char USTRING[USTR_SIZE]; + +struct arglist { + int al_argc; + char *al_argv[MAXARGC]; +}; + +struct arglist CALLVEC; + +int kids = -1; + +char *o_FILE = "a.out"; /* default name for executable file */ + +#define init(a) ((a)->al_argc = 1) +#define cleanup(str) (str && remove(str)) + +char *ProgCall = 0; + +int RET_CODE = 0; + +char *stopsuffix; +int v_flag = 0; +int t_flag = 0; +int noexec = 0; +int fp_lib = 1; +int E_flag = 0; +int i_flag = 1; + + +USTRING curfil; +USTRING newfil; +struct arglist SRCFILES; +struct arglist LDIRS; +struct arglist LDFILES; +struct arglist GEN_LDFILES; +struct arglist FLAGS; + +char *tmpdir = "/tmp"; +char tmpname[64]; + +struct compile *compbase; +struct pass *loader; +struct passinfo *loaderinfo; +char *source; +int maxLlen; + +_PROTOTYPE(char *library, (char *nm )); +_PROTOTYPE(void trapcc, (int sig )); +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(int remove, (char *str )); +_PROTOTYPE(char *alloc, (unsigned u )); +_PROTOTYPE(int append, (struct arglist *al, char *arg )); +_PROTOTYPE(int concat, (struct arglist *al1, struct arglist *al2 )); +_PROTOTYPE(char *mkstr, (char *dst, char *arg1, char *arg2, char *arg3 )); +_PROTOTYPE(int basename, (char *str, char *dst )); +_PROTOTYPE(char *extension, (char *fln )); +_PROTOTYPE(int runvec, (struct arglist *vec, struct passinfo *pass, char *in, char *out )); +_PROTOTYPE(int prnum, (unsigned x )); +_PROTOTYPE(int prs, (char *str )); +_PROTOTYPE(int panic, (char *str )); +_PROTOTYPE(int pr_vec, (struct arglist *vec )); +_PROTOTYPE(int ex_vec, (struct arglist *vec )); +_PROTOTYPE(int mktempname, (char *nm )); +_PROTOTYPE(int mkbase, (void)); +_PROTOTYPE(int mkloader, (void)); +_PROTOTYPE(int needsprep, (char *name )); +_PROTOTYPE(int cfile, (char *name )); +_PROTOTYPE(char *apply, (struct passinfo *pinf, struct compile *cp, char *name, int passindex, int noremove, int first, char *resultname )); +_PROTOTYPE(int applicable, (struct passinfo *pinf, char *suffix )); +_PROTOTYPE(char *process, (char *name, int noremove )); +_PROTOTYPE(int mkvec, (struct arglist *call, char *in, char *out, struct pass *pass, struct passinfo *pinf )); +_PROTOTYPE(int callld, (struct arglist *in, char *out, struct pass *pass, struct passinfo *pinf )); +_PROTOTYPE(int clean, (struct arglist *c )); +_PROTOTYPE(int scanflags, (struct arglist *call, struct passinfo *pinf )); + + + +char * +library(nm) + char *nm; +{ + static char f[512]; + int Lcount; + + for (Lcount = 0; Lcount < LDIRS.al_argc; Lcount++) { + mkstr(f, LDIRS.al_argv[Lcount], "/lib", nm); + strcat(f, ".a"); + if (access(f, 0) != 0) { + f[strlen(f)-1] = 'a'; + if (access(f, 0) != 0) continue; + } + return f; + } + mkstr(f, LIBDIR, "/lib", nm); + strcat(f, ".a"); + if (access(f, 0) != 0) { + int i = strlen(f) - 1; + f[i] = 'a'; + if (access(f, 0) != 0) f[i] = 'A'; + } + return f; +} + +void trapcc(sig) + int sig; +{ + signal(sig, SIG_IGN); + if (kids != -1) kill(kids, sig); + cleanup(newfil); + cleanup(curfil); + exit(1); +} + +main(argc, argv) + char *argv[]; +{ + char *str; + char **argvec; + int count; + char *file; + + maxLlen = strlen(LIBDIR); + ProgCall = *argv++; + + mkbase(); + + if (signal(SIGHUP, SIG_IGN) != SIG_IGN) + signal(SIGHUP, trapcc); + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, trapcc); + if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) + signal(SIGQUIT, trapcc); + while (--argc > 0) { + if (*(str = *argv++) != '-' || str[1] == 0) { + append(&SRCFILES, str); + continue; + } + + if (strcmp(str, "-com") == 0) { + i_flag = 0; + } else + if (strcmp(str, "-sep") == 0) { + i_flag = 1; + } else { + switch (str[1]) { + + case 'c': + stopsuffix = "o"; + if (str[2] == '.') stopsuffix = str + 3; + break; + case 'f': + fp_lib = (strcmp(str+2, "hard") != 0); + break; + case 'F': + case 'W': + /* Ignore. */ + break; + case 'L': + append(&LDIRS, &str[2]); + count = strlen(&str[2]); + if (count > maxLlen) maxLlen = count; + break; + case 'l': + append(&SRCFILES, library(&str[2])); + break; + case 'm': + /* Use -m, ignore -mxxx. */ + if (str[2] == 0) append(&FLAGS, str); + break; + case 'o': + if (argc-- >= 0) + o_FILE = *argv++; + break; + case 'S': + stopsuffix = "s"; + break; + case 'E': + E_flag = 1; + stopsuffix = "i"; + break; + case 'P': + stopsuffix = "i"; + append(&FLAGS, str); + break; + case 'v': + v_flag++; + if (str[2] == 'n') + noexec = 1; + break; + case 't': + /* save temporaries */ + t_flag++; + break; + case '.': + if (str[2] == 'o') { + /* no runtime start-off */ + loader->pp_head[0] = 0; + } + break; + case 'i': + i_flag++; + break; + case 'T': + tmpdir = &str[2]; + /*FALLTHROUGH*/ + default: + append(&FLAGS, str); + + } + } + } + + if (i_flag) append(&FLAGS, "-i"); + + mktempname(tmpname); + + count = SRCFILES.al_argc; + argvec = &(SRCFILES.al_argv[0]); + + while (count-- > 0) { + + file = *argvec++; + source = file; + + file = process(file, 1); + + if (file && ! stopsuffix) append(&LDFILES, file); + } + + clean(&SRCFILES); + + /* loader ... */ + if (RET_CODE == 0 && LDFILES.al_argc > 0) { + register struct passinfo *pp = passinfo; + + while (!(pp->p_flags & LOADER)) pp++; + mkstr(newfil, tmpname, pp->p_to, ""); + callld(&LDFILES, !((pp+1)->p_name) ? o_FILE : newfil, loader, pp); + if (RET_CODE == 0) { + register int i = GEN_LDFILES.al_argc; + + while (i-- > 0) { + remove(GEN_LDFILES.al_argv[i]); + free(GEN_LDFILES.al_argv[i]); + } + if ((++pp)->p_name) { + process(newfil, 0); + } + } + } + exit(RET_CODE); +} + +remove(str) + char *str; +{ + if (t_flag) + return; + if (v_flag) { + prs("rm "); + prs(str); + prs("\n"); + } + if (noexec) + return; + unlink(str); +} + +char * +alloc(u) + unsigned u; +{ + register char *p = malloc(u); + + if (p == 0) panic("no space\n"); + return p; +} + +append(al, arg) + struct arglist *al; + char *arg; +{ + char *a = alloc((unsigned) (strlen(arg) + 1)); + + strcpy(a, arg); + if (al->al_argc >= MAXARGC) + panic("argument list overflow\n"); + al->al_argv[(al->al_argc)++] = a; +} + +concat(al1, al2) + struct arglist *al1, *al2; +{ + register i = al2->al_argc; + register char **p = &(al1->al_argv[al1->al_argc]); + register char **q = &(al2->al_argv[0]); + + if ((al1->al_argc += i) >= MAXARGC) + panic("argument list overflow\n"); + while (i-- > 0) + *p++ = *q++; +} + +char * +mkstr(dst, arg1, arg2, arg3) + char *dst, *arg1, *arg2, *arg3; +{ + register char *p; + register char *q = dst; + + p = arg1; + while (*q++ = *p++); + q--; + p = arg2; + while (*q++ = *p++); + q--; + p = arg3; + while (*q++ = *p++); + q--; + return dst; +} + +basename(str, dst) + char *str; + register char *dst; +{ + register char *p1 = str; + register char *p2 = p1; + + while (*p1) + if (*p1++ == '/') + p2 = p1; + p1--; + while (*p1 != '.' && p1 > p2) p1--; + if (*p1 == '.') { + *p1 = '\0'; + while (*dst++ = *p2++); + *p1 = '.'; + } + else + while (*dst++ = *p2++); +} + +char * +extension(fln) + char *fln; +{ + register char *fn = fln; + + while (*fn) fn++; + while (fn > fln && *fn != '.') fn--; + if (fn != fln) return fn+1; + return (char *)0; +} + +runvec(vec, pass, in, out) + struct arglist *vec; + struct passinfo *pass; + char *in, *out; +{ + int pid, status; + int shifted = 0; + + if ( + strncmp(vec->al_argv[1], "/usr/", 5) == 0 + && + access(vec->al_argv[1] + 4, 1) == 0 + ) { + vec->al_argv[1] += 4; + shifted = 1; + } + + if (v_flag) { + pr_vec(vec); + if (pass->p_flags & STDIN) { + prs(" <"); + prs(in); + } + if (pass->p_flags & STDOUT && !E_flag) { + prs(" >"); + prs(out); + } + prs("\n"); + } + if (noexec) { + if (shifted) vec->al_argv[1] -= 4; + clean(vec); + return 1; + } + if ((pid = fork()) == 0) { /* start up the process */ + if (pass->p_flags & STDIN && strcmp(in, "-") != 0) { + /* redirect standard input */ + close(0); + if (open(in, 0) != 0) + panic("cannot open input file\n"); + } + if (pass->p_flags & STDOUT && !E_flag) { + /* redirect standard output */ + close(1); + if (creat(out, 0666) != 1) + panic("cannot create output file\n"); + } + ex_vec(vec); + } + if (pid == -1) + panic("no more processes\n"); + kids = pid; + wait(&status); + if (status) switch(status & 0177) { + case SIGHUP: + case SIGINT: + case SIGQUIT: + case SIGTERM: + case 0: + break; + default: + if (E_flag && (status & 0177) == SIGPIPE) break; + prs(vec->al_argv[1]); + prs(" died with signal "); + prnum(status & 0177); + prs("\n"); + } + if (shifted) vec->al_argv[1] -= 4; + clean(vec); + kids = -1; + return status ? ((RET_CODE = 1), 0) : 1; +} + +prnum(x) + register unsigned x; +{ + static char numbuf[8]; /* though it prints at most 3 characters */ + register char *cp = numbuf + sizeof(numbuf) - 1; + + *cp = '\0'; + while (x >= 10) { + *--cp = (x % 10) + '0'; + x /= 10; + } + *--cp = x + '0'; + prs(cp); + +} + +prs(str) + char *str; +{ + if (str && *str) + write(2, str, strlen(str)); +} + +panic(str) + char *str; +{ + prs(str); + trapcc(SIGINT); +} + +pr_vec(vec) + register struct arglist *vec; +{ + register char **ap = &vec->al_argv[1]; + + vec->al_argv[vec->al_argc] = 0; + prs(*ap); + while (*++ap) { + prs(" "); + if (strlen(*ap)) + prs(*ap); + else + prs("(empty)"); + } +} + +ex_vec(vec) + register struct arglist *vec; +{ + extern int errno; + + vec->al_argv[vec->al_argc] = 0; + execv(vec->al_argv[1], &(vec->al_argv[1])); + if (errno == ENOEXEC) { /* not an a.out, try it with the SHELL */ + vec->al_argv[0] = SHELL; + execv(SHELL, &(vec->al_argv[0])); + } + if (access(vec->al_argv[1], 1) == 0) { + /* File is executable. */ + prs("Cannot execute "); + prs(vec->al_argv[1]); + prs(". Not enough memory.\n"); + prs("Reduce the memory use of your system and try again\n"); + } else { + prs(vec->al_argv[1]); + prs(" is not executable\n"); + } + exit(1); +} + +mktempname(nm) + register char *nm; +{ + register int i; + register int pid = getpid(); + + mkstr(nm, tmpdir, "/", compbase->c_callname); + while (*nm) nm++; + + for (i = 9; i > 3; i--) { + *nm++ = (pid % 10) + '0'; + pid /= 10; + } + *nm++ = '.'; + *nm++ = '\0'; /* null termination */ +} + +mkbase() +{ + register struct compile *p = passes; + USTRING callname; + register int len; + + basename(ProgCall, callname); + len = strlen(callname); + while (p->c_suffix) { + if (strcmp(p->c_callname, callname+len-strlen(p->c_callname)) == 0) { + compbase = p; + mkloader(); + return; + } + p++; + } + /* we should not get here */ + panic("internal error\n"); +} + +mkloader() +{ + register struct passinfo *p = passinfo; + register struct pass *pass; + + while (!(p->p_flags & LOADER)) p++; + loaderinfo = p; + pass = &(compbase->c_passes[0]); + while (strcmp(pass->pp_name, p->p_name)) pass++; + loader = pass; +} + +needsprep(name) + char *name; +{ + int file; + char fc; + + file = open(name,0); + if (file <0) return 0; + if (read(file, &fc, 1) != 1) fc = 0; + close(file); + return fc == '#'; +} + +cfile(name) + char *name; +{ + while (*name != '\0' && *name != '.') + name++; + + if (*name == '\0') return 0; + return (*++name == 'c' && *++name == '\0'); +} + +char * +apply(pinf, cp, name, passindex, noremove, first, resultname) + register struct passinfo *pinf; + register struct compile *cp; + char *name, *resultname; +{ + /* Apply a pass, indicated by "pinf", with args in + cp->c_passes[passindex], to name "name", leaving the result + in a file with name "resultname", concatenated with result + suffix. + When neccessary, the preprocessor is run first. + If "noremove" is NOT set, the file "name" is removed. + */ + + struct arglist *call = &CALLVEC; + struct pass *pass = &(cp->c_passes[passindex]); + char *outname; + + if ( /* this pass is the first pass */ + first + && + ( /* preprocessor always needed */ + (pinf->p_flags & PREPALWAYS) + ||/* or only when "needsprep" says so */ + ( (pinf->p_flags & PREPCOND) && needsprep(name)) + ) + ) { + mkstr(newfil, tmpname, passinfo[0].p_to, ""); + mkvec(call, name, newfil, + (pinf->p_flags & PREPNOLN) ? &prepnoln : &preprocessor, + &passinfo[0]); + if (! runvec(call, &passinfo[0], name, newfil)) { + cleanup(newfil); + return 0; + } + + /* A .c file must always be mishandled by irrel. */ + if (cfile(name)) { + /* newfil is OK */ + mkvec(call, newfil, newfil, &irrel, &passinfo[1]); + if (! runvec(call, &passinfo[1], newfil, newfil)) { + cleanup(newfil); + return 0; + } + } + strcpy(curfil, newfil); + newfil[0] = '\0'; + name = curfil; + noremove = 0; + } + if (pinf->p_to) outname = mkstr(newfil, resultname, pinf->p_to, ""); + else outname = o_FILE; + mkvec(call, name, outname, pass, pinf); + if (! runvec(call, pinf, name, outname)) { + if (! (pinf->p_flags & NOCLEAN)) cleanup(outname); + if (! noremove) cleanup(name); + return 0; + } + if (! noremove) cleanup(name); + strcpy(curfil, newfil); + newfil[0] = '\0'; + return curfil; +} + +int +applicable(pinf, suffix) + struct passinfo *pinf; + char *suffix; +{ + /* Return one if the pass indicated by "pinfo" is applicable to + a file with suffix "suffix". + */ + register char *sfx = pinf->p_from; + int l; + + if (! suffix) return 0; + l = strlen(suffix); + while (*sfx) { + register char *p = sfx; + + while (*p && *p != ',') p++; + if (l == p - sfx && strncmp(sfx, suffix, l) == 0) { + return 1; + } + if (*p == ',') sfx = p+1; + else sfx = p; + } + return 0; +} + +char * +process(name, noremove) + char *name; +{ + register struct compile *cp = passes; + char *suffix = extension(name); + USTRING base; + register struct pass *pass; + register struct passinfo *pinf; + + if (E_flag) { + /* -E uses the cpp pass. */ + suffix = "CPP"; + } + + if (! suffix) return name; + + basename(name, base); + + while (cp->c_suffix) { + if ((cp->c_flags & DEFLANG) && + strcmp(cp->c_suffix, suffix) == 0) + break; + cp++; + } + if (! cp->c_suffix) cp = compbase; + pass = cp->c_passes; + while (pass->pp_name) { + int first = 1; + + for (pinf=passinfo; strcmp(pass->pp_name,pinf->p_name);pinf++) + ; + if (! (pinf->p_flags & LOADER) && applicable(pinf, suffix)) { + int cont = ! stopsuffix || ! pinf->p_to || + strcmp(stopsuffix, pinf->p_to) != 0; + name = apply(pinf, + cp, + name, + (int) (pass - cp->c_passes), + noremove, + first, + applicable(loaderinfo, pinf->p_to) || + !cont ? + strcat(base, ".") : + tmpname); + first = noremove = 0; + suffix = pinf->p_to; + if (!cont || !name) break; + } + pass++; + } + if (!noremove && name) + append(&GEN_LDFILES, name); + return name; +} + +mkvec(call, in, out, pass, pinf) + struct arglist *call; + char *in, *out; + struct pass *pass; + register struct passinfo *pinf; +{ + register int i; + + init(call); + append(call, pinf->p_path); + scanflags(call, pinf); + if (pass) for (i = 0; i < MAXHEAD; i++) + if (pass->pp_head[i]) + append(call, pass->pp_head[i]); + else break; + if (pinf->p_flags & INPUT && strcmp(in, "-") != 0) + append(call, in); + if (pinf->p_flags & OUTPUT) + append(call, out); + if (pinf->p_flags & O_OUTPUT) { + append(call, "-o"); + append(call, out); + } + if (pass) for (i = 0; i < MAXTAIL; i++) + if (pass->pp_tail[i]) + append(call, pass->pp_tail[i]); + else break; +} + +callld(in, out, pass, pinf) + struct arglist *in; + char *out; + struct pass *pass; + register struct passinfo *pinf; +{ + struct arglist *call = &CALLVEC; + register int i; + + init(call); + append(call, pinf->p_path); + scanflags(call, pinf); + append(call, "-o"); + append(call, out); + for (i = 0; i < MAXHEAD; i++) + if (pass->pp_head[i]) + append(call, pass->pp_head[i]); + else break; + if (pinf->p_flags & INPUT) + concat(call, in); + if (pinf->p_flags & OUTPUT) + append(call, out); + for (i = 0; i < MAXTAIL; i++) { + if (pass->pp_tail[i]) { + if (pass->pp_tail[i][0] == '-' && + pass->pp_tail[i][1] == 'l') { + append(call, library(&(pass->pp_tail[i][2]))); + } + else if (*(pass->pp_tail[i]) != '*') + append(call, pass->pp_tail[i]); + else if (fp_lib) + append(call, library("fp")); + } else break; + } + if (! runvec(call, pinf, (char *) 0, out)) { + cleanup(out); + RET_CODE = 1; + } +} + +clean(c) + register struct arglist *c; +{ + register int i; + + for (i = 1; i < c->al_argc; i++) { + free(c->al_argv[i]); + c->al_argv[i] = 0; + } + c->al_argc = 0; +} + +scanflags(call, pinf) + struct arglist *call; + struct passinfo *pinf; +{ + /* Find out which flags from FLAGS must be passed to pass "pinf", + and how. + Append them to "call" + */ + register int i; + USTRING flg; + + for (i = 0; i < FLAGS.al_argc; i++) { + register char *q = pinf->p_acceptflags; + + while (*q) { + register char *p = FLAGS.al_argv[i] + 1; + + while (*q && *q == *p) { + q++; p++; + } + if (*q == ',' || !*q) { + if (! *p) { + /* append literally */ + append(call, FLAGS.al_argv[i]); + } + break; + } + if (*q == '*') { + register char *s = flg; + + if (*++q != '=') { + /* append literally */ + append(call, FLAGS.al_argv[i]); + break; + } + *s++ = '-'; + if (*q) q++; /* skip ',' */ + while (*q && *q != ',' && *q != '*') { + /* copy replacement flag */ + *s++ = *q++; + } + if (*q == '*') { + /* copy rest */ + while (*p) *s++ = *p++; + } + *s = 0; + append(call, flg); + break; + } + if (*q == '=') { + /* copy replacement */ + register char *s = flg; + + *s++ = '-'; + q++; + while (*q && *q != ',') *s++ = *q++; + *s = 0; + append(call, flg); + break; + } + while (*q && *q++ != ',') + ; + } + } +} diff --git a/commands/ibm/Makefile b/commands/ibm/Makefile new file mode 100755 index 000000000..df65f8f17 --- /dev/null +++ b/commands/ibm/Makefile @@ -0,0 +1,164 @@ +# Makefile for commands/ibm. + +# See commands/simple/Makefile for a description. + +CFLAGS = -D_MINIX -D_POSIX_SOURCE +CCLD = $(CC) -i $(CFLAGS) +MAKE = exec make -$(MAKEFLAGS) + +ALL = \ + dosread \ + fdisk \ + format \ + loadfont \ + loadkeys \ + mixer \ + part \ + partition \ + playwave \ + postmort \ + readclock \ + recwave \ + repartition \ + screendump \ + sdump \ + +all: $(ALL) + +dosread: dosread.c + $(CCLD) -o $@ $? + install -S 16kw $@ + +fdisk: fdisk.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +format: format.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +loadfont: loadfont.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +loadkeys: loadkeys.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +mixer: mixer.c + $(CCLD) -o $@ $? -lcurses + install -S 16kw $@ + +part: part.c + $(CCLD) -o $@ $? + install -S 11kw $@ + +partition: partition.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +playwave: playwave.c + $(CCLD) -o $@ $? + install -S 16kw $@ + +postmort: postmort.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +readclock: readclock.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +recwave: recwave.c + $(CCLD) -o $@ $? + install -S 16kw $@ + +repartition: repartition.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +screendump: screendump.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +sdump: sdump.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +install: \ + /usr/bin/dosread \ + /usr/bin/dosdir \ + /usr/bin/doswrite \ + /usr/bin/fdisk \ + /usr/bin/format \ + /usr/bin/loadfont \ + /usr/bin/loadkeys \ + /usr/bin/mixer \ + /usr/bin/part \ + /usr/bin/partition \ + /usr/bin/playwave \ + /usr/bin/postmort \ + /usr/bin/readclock \ + /usr/bin/recwave \ + /usr/bin/repartition \ + /usr/bin/screendump \ + /usr/bin/sdump \ + /bin/loadkeys \ + /bin/readclock \ + +/usr/bin/dosread: dosread + install -cs -o bin $? $@ + +/usr/bin/dosdir /usr/bin/doswrite: /usr/bin/dosread + install -l $? $@ + +/usr/bin/fdisk: fdisk + install -cs -o bin $? $@ + +/usr/bin/format: format + install -cs -o root -m 4755 $? $@ + +/usr/bin/loadfont: loadfont + install -cs -o bin $? $@ + +/usr/bin/loadkeys: loadkeys + install -cs -o bin $? $@ + +/usr/bin/mixer: mixer + install -cs -o bin $? $@ + +/usr/bin/part: part + install -cs -o bin $? $@ + +/usr/bin/partition: partition + install -cs -o bin $? $@ + +/usr/bin/playwave: playwave + install -cs -o bin $? $@ + +/usr/bin/postmort: postmort + install -cs -o bin $? $@ + +/usr/bin/readclock: readclock + install -cs -o bin $? $@ + +/usr/bin/recwave: recwave + install -cs -o bin $? $@ + +/usr/bin/repartition: repartition + install -cs -o bin $? $@ + +/usr/bin/screendump: screendump + install -cs -o bin $? $@ + +/usr/bin/sdump: sdump + install -cs -o bin $? $@ + +/bin/loadkeys: /usr/bin/loadkeys + install -lcs $? $@ + +/bin/readclock: /usr/bin/readclock + install -lcs $? $@ + +clean: + rm -rf $(ALL) a.out core diff --git a/commands/ibm/backup.c b/commands/ibm/backup.c new file mode 100644 index 000000000..6861f03c1 --- /dev/null +++ b/commands/ibm/backup.c @@ -0,0 +1,398 @@ +/* readclock - read the real time clock Authors: T. Holm & E. Froese */ + +/************************************************************************/ +/* */ +/* readclock.c */ +/* */ +/* Read the clock value from the 64 byte CMOS RAM */ +/* area, then set system time. */ +/* */ +/* If the machine ID byte is 0xFC or 0xF8, the device */ +/* /dev/mem exists and can be opened for reading, */ +/* and no errors in the CMOS RAM are reported by the */ +/* RTC, then the time is read from the clock RAM */ +/* area maintained by the RTC. */ +/* */ +/* The clock RAM values are decoded and fed to mktime */ +/* to make a time_t value, then stime(2) is called. */ +/* */ +/* This fails if: */ +/* */ +/* If the machine ID does not match 0xFC or 0xF8 (no */ +/* error message.) */ +/* */ +/* If the machine ID is 0xFC or 0xF8 and /dev/mem */ +/* is missing, or cannot be accessed. */ +/* */ +/* If the RTC reports errors in the CMOS RAM. */ +/* */ +/************************************************************************/ +/* origination 1987-Dec-29 efth */ +/* robustness 1990-Oct-06 C. Sylvain */ +/* incorp. B. Evans ideas 1991-Jul-06 C. Sylvain */ +/* set time & calibrate 1992-Dec-17 Kees J. Bot */ +/* clock timezone 1993-Oct-10 Kees J. Bot */ +/* set CMOS clock 1994-Jun-12 Kees J. Bot */ +/************************************************************************/ + + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <errno.h> +#include <signal.h> +#include <ibm/portio.h> +#include <ibm/cmos.h> +#include <sys/svrctl.h> + +int nflag = 0; /* Tell what, but don't do it. */ +int wflag = 0; /* Set the CMOS clock. */ +int Wflag = 0; /* Also set the CMOS clock register bits. */ +int y2kflag = 0; /* Interpret 1980 as 2000 for clock with Y2K bug. */ + +char clocktz[128]; /* Timezone of the clock. */ + +#define MACH_ID_ADDR 0xFFFFE /* BIOS Machine ID at FFFF:000E */ + +#define PC_AT 0xFC /* Machine ID byte for PC/AT, + PC/XT286, and PS/2 Models 50, 60 */ +#define PS_386 0xF8 /* Machine ID byte for PS/2 Model 80 */ + +/* Manufacturers usually use the ID value of the IBM model they emulate. + * However some manufacturers, notably HP and COMPAQ, have had different + * ideas in the past. + * + * Machine ID byte information source: + * _The Programmer's PC Sourcebook_ by Thom Hogan, + * published by Microsoft Press + */ + +void errmsg(char *s); +void get_time(struct tm *t); +int read_register(int reg_addr); +void set_time(struct tm *t); +void write_register(int reg_addr, int value); +int bcd_to_dec(int n); +int dec_to_bcd(int n); +void usage(void); + +int main(int argc, char **argv) +{ + struct tm time1; + struct tm time2; + struct tm tmnow; + char date[64]; + time_t now, rtc; + int i, mem; + unsigned char mach_id, cmos_state; + struct sysgetenv sysgetenv; + + /* Open /dev/mem to get access to physical memory. */ + if ((mem = open("/dev/mem", O_RDONLY)) == -1) { + errmsg( "Permission denied." ); + exit(1); + } + if (lseek(mem, (off_t) MACH_ID_ADDR, SEEK_SET) == -1 + || read(mem, (void *) &mach_id, sizeof(mach_id)) < 0) { + mach_id = -1; + } + if (mach_id != PS_386 && mach_id != PC_AT) { + errmsg( "Machine ID unknown." ); + fprintf( stderr, "Machine ID byte = %02x\n", mach_id ); + + exit(1); + } + cmos_state = read_register(CMOS_STATUS); + if (cmos_state & (CS_LOST_POWER | CS_BAD_CHKSUM | CS_BAD_TIME)) { + errmsg( "CMOS RAM error(s) found..." ); + fprintf( stderr, "CMOS state = 0x%02x\n", cmos_state ); + + if (cmos_state & CS_LOST_POWER) + errmsg( "RTC lost power. Reset CMOS RAM with SETUP." ); + if (cmos_state & CS_BAD_CHKSUM) + errmsg( "CMOS RAM checksum is bad. Run SETUP." ); + if (cmos_state & CS_BAD_TIME) + errmsg( "Time invalid in CMOS RAM. Reset clock." ); + exit(1); + } + + /* Process options. */ + while (argc > 1) { + char *p = *++argv; + + if (*p++ != '-') usage(); + + while (*p != 0) { + switch (*p++) { + case 'n': nflag = 1; break; + case 'w': wflag = 1; break; + case 'W': Wflag = 1; break; + case '2': y2kflag = 1; break; + default: usage(); + } + } + argc--; + } + if (Wflag) wflag = 1; /* -W implies -w */ + + /* The hardware clock may run in a different time zone, likely GMT or + * winter time. Select that time zone. + */ + strcpy(clocktz, "TZ="); + sysgetenv.key = "TZ"; + sysgetenv.keylen = 2+1; + sysgetenv.val = clocktz+3; + sysgetenv.vallen = sizeof(clocktz)-3; + if (svrctl(SYSGETENV, &sysgetenv) == 0) { + putenv(clocktz); + tzset(); + } + + /* Read the CMOS real time clock. */ + for (i = 0; i < 10; i++) { + get_time(&time1); + now = time(NULL); + + time1.tm_isdst = -1; /* Do timezone calculations. */ + time2 = time1; + + rtc= mktime(&time1); /* Transform to a time_t. */ + if (rtc != -1) break; + + fprintf(stderr, +"readclock: Invalid time read from CMOS RTC: %d-%02d-%02d %02d:%02d:%02d\n", + time2.tm_year+1900, time2.tm_mon+1, time2.tm_mday, + time2.tm_hour, time2.tm_min, time2.tm_sec); + sleep(5); + } + if (i == 10) exit(1); + + if (!wflag) { + /* Set system time. */ + if (nflag) { + printf("stime(%lu)\n", (unsigned long) rtc); + } else { + if (stime(&rtc) < 0) { + errmsg( "Not allowed to set time." ); + exit(1); + } + } + tmnow = *localtime(&rtc); + if (strftime(date, sizeof(date), + "%a %b %d %H:%M:%S %Z %Y", &tmnow) != 0) { + if (date[8] == '0') date[8]= ' '; + printf("Result: %s\n", date); + } + } else { + /* Set the CMOS clock to the system time. */ + tmnow = *localtime(&now); + if (nflag) { + printf("%04d-%02d-%02d %02d:%02d:%02d\n", + tmnow.tm_year + 1900, + tmnow.tm_mon + 1, + tmnow.tm_mday, + tmnow.tm_hour, + tmnow.tm_min, + tmnow.tm_sec); + } else { + set_time(&tmnow); + } + } + exit(0); +} + +void errmsg(char *s) +{ + static char *prompt = "readclock: "; + + fprintf(stderr, "%s%s\n", prompt, s); + prompt = ""; +} + + +/***********************************************************************/ +/* */ +/* get_time( time ) */ +/* */ +/* Update the structure pointed to by time with the current time */ +/* as read from CMOS RAM of the RTC. */ +/* If necessary, the time is converted into a binary format before */ +/* being stored in the structure. */ +/* */ +/***********************************************************************/ + +int dead; +void timeout(int sig) { dead= 1; } + +void get_time(struct tm *t) +{ + int osec, n; + unsigned long i; + struct sigaction sa; + + /* Start a timer to keep us from getting stuck on a dead clock. */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = timeout; + sigaction(SIGALRM, &sa, NULL); + dead = 0; + alarm(5); + + do { + osec = -1; + n = 0; + do { + if (dead) { + fprintf(stderr, "readclock: CMOS clock appears dead\n"); + exit(1); + } + + /* Clock update in progress? */ + if (read_register(RTC_REG_A) & RTC_A_UIP) continue; + + t->tm_sec = read_register(RTC_SEC); + if (t->tm_sec != osec) { + /* Seconds changed. First from -1, then because the + * clock ticked, which is what we're waiting for to + * get a precise reading. + */ + osec = t->tm_sec; + n++; + } + } while (n < 2); + + /* Read the other registers. */ + t->tm_min = read_register(RTC_MIN); + t->tm_hour = read_register(RTC_HOUR); + t->tm_mday = read_register(RTC_MDAY); + t->tm_mon = read_register(RTC_MONTH); + t->tm_year = read_register(RTC_YEAR); + + /* Time stable? */ + } while (read_register(RTC_SEC) != t->tm_sec + || read_register(RTC_MIN) != t->tm_min + || read_register(RTC_HOUR) != t->tm_hour + || read_register(RTC_MDAY) != t->tm_mday + || read_register(RTC_MONTH) != t->tm_mon + || read_register(RTC_YEAR) != t->tm_year); + + if ((read_register(RTC_REG_B) & RTC_B_DM_BCD) == 0) { + /* Convert BCD to binary (default RTC mode). */ + t->tm_year = bcd_to_dec(t->tm_year); + t->tm_mon = bcd_to_dec(t->tm_mon); + t->tm_mday = bcd_to_dec(t->tm_mday); + t->tm_hour = bcd_to_dec(t->tm_hour); + t->tm_min = bcd_to_dec(t->tm_min); + t->tm_sec = bcd_to_dec(t->tm_sec); + } + t->tm_mon--; /* Counts from 0. */ + + /* Correct the year, good until 2080. */ + if (t->tm_year < 80) t->tm_year += 100; + + if (y2kflag) { + /* Clock with Y2K bug, interpret 1980 as 2000, good until 2020. */ + if (t->tm_year < 100) t->tm_year += 20; + } +} + + +int read_register(int reg_addr) +{ + int r; + + intr_disable(); + outb(RTC_INDEX, reg_addr); + r= inb(RTC_IO); + intr_enable(); + return r; +} + + + +/***********************************************************************/ +/* */ +/* set_time( time ) */ +/* */ +/* Set the CMOS RTC to the time found in the structure. */ +/* */ +/***********************************************************************/ + +void set_time(struct tm *t) +{ + int regA, regB; + + if (Wflag) { + /* Set A and B registers to their proper values according to the AT + * reference manual. (For if it gets messed up, but the BIOS doesn't + * repair it.) + */ + write_register(RTC_REG_A, RTC_A_DV_OK | RTC_A_RS_DEF); + write_register(RTC_REG_B, RTC_B_24); + } + + /* Inhibit updates. */ + regB= read_register(RTC_REG_B); + write_register(RTC_REG_B, regB | RTC_B_SET); + + t->tm_mon++; /* Counts from 1. */ + + if (y2kflag) { + /* Set the clock back 20 years to avoid Y2K bug, good until 2020. */ + if (t->tm_year >= 100) t->tm_year -= 20; + } + + if ((regB & 0x04) == 0) { + /* Convert binary to BCD (default RTC mode) */ + t->tm_year = dec_to_bcd(t->tm_year % 100); + t->tm_mon = dec_to_bcd(t->tm_mon); + t->tm_mday = dec_to_bcd(t->tm_mday); + t->tm_hour = dec_to_bcd(t->tm_hour); + t->tm_min = dec_to_bcd(t->tm_min); + t->tm_sec = dec_to_bcd(t->tm_sec); + } + write_register(RTC_YEAR, t->tm_year); + write_register(RTC_MONTH, t->tm_mon); + write_register(RTC_MDAY, t->tm_mday); + write_register(RTC_HOUR, t->tm_hour); + write_register(RTC_MIN, t->tm_min); + write_register(RTC_SEC, t->tm_sec); + + /* Stop the clock. */ + regA= read_register(RTC_REG_A); + write_register(RTC_REG_A, regA | RTC_A_DV_STOP); + + /* Allow updates and restart the clock. */ + write_register(RTC_REG_B, regB); + write_register(RTC_REG_A, regA); +} + + +void write_register(int reg_addr, int value) +{ + intr_disable(); + outb(RTC_INDEX, reg_addr); + outb(RTC_IO, value); + intr_enable(); +} + +int bcd_to_dec(int n) +{ + return ((n >> 4) & 0x0F) * 10 + (n & 0x0F); +} + +int dec_to_bcd(int n) +{ + return ((n / 10) << 4) | (n % 10); +} + +void usage(void) +{ + fprintf(stderr, "Usage: readclock [-nwW2]\n"); + exit(1); +} diff --git a/commands/ibm/dosread.c b/commands/ibm/dosread.c new file mode 100755 index 000000000..bb62f3286 --- /dev/null +++ b/commands/ibm/dosread.c @@ -0,0 +1,1156 @@ +/* dos{dir|read|write} - {list|read|write} MS-DOS disks Author: M. Huisjes */ + +/* Dosdir - list MS-DOS directories. doswrite - write stdin to DOS-file + * dosread - read DOS-file to stdout + * + * Author: Michiel Huisjes. + * + * Usage: dos... [-lra] drive [file/dir] + * l: Give long listing. + * r: List recursively. + * a: Set ASCII bit. + */ + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/times.h> +#include <unistd.h> + + +#define MAX_CLUSTER_SIZE 4096 +#define MAX_ROOT_ENTRIES 512 +#define FAT_START 512L /* After bootsector */ +#define ROOTADDR (FAT_START + 2L * fat_size) +#define clus_add(cl_no) ((long) (((long) cl_no - 2L) \ + * (long) cluster_size \ + + data_start \ + )) +struct dir_entry { + unsigned char d_name[8]; + unsigned char d_ext[3]; + unsigned char d_attribute; + unsigned char d_reserved[10]; + unsigned short d_time; + unsigned short d_date; + unsigned short d_cluster; + unsigned long d_size; +}; + +typedef struct dir_entry DIRECTORY; + +#define NOT_USED 0x00 +#define ERASED 0xE5 +#define DIR 0x2E +#define DIR_SIZE (sizeof (struct dir_entry)) +#define SUB_DIR 0x10 +#define NIL_DIR ((DIRECTORY *) 0) + +#define LAST_CLUSTER12 0xFFF +#define LAST_CLUSTER 0xFFFF +#define FREE 0x000 +#define BAD 0xFF0 +#define BAD16 0xFFF0 + +typedef int BOOL; + +#define TRUE 1 +#define FALSE 0 +#define NIL_PTR ((char *) 0) + +#define DOS_TIME 315532800L /* 1970 - 1980 */ + +#define READ 0 +#define WRITE 1 + +#define FIND 3 +#define LABEL 4 +#define ENTRY 5 +#define find_entry(d, e, p) directory(d, e, FIND, p) +#define list_dir(d, e, f) (void) directory(d, e, f, NIL_PTR) +#define label() directory(root, root_entries, LABEL, NIL_PTR) +#define new_entry(d, e) directory(d, e, ENTRY, NIL_PTR) + +#define is_dir(d) ((d)->d_attribute & SUB_DIR) + +#define STD_OUT 1 + +char *cmnd; + +static int disk; /* File descriptor for disk I/O */ + +static DIRECTORY root[MAX_ROOT_ENTRIES]; +static DIRECTORY save_entry; +static char drive[] = "/dev/dosX"; +#define DRIVE_NR (sizeof (drive) - 2) +static char null[MAX_CLUSTER_SIZE], *device = drive, path[128]; +static long data_start; +static long mark; /* offset of directory entry to be written */ +static unsigned short total_clusters, cluster_size, root_entries, sub_entries; +static unsigned long fat_size; + +static BOOL Rflag, Lflag, Aflag, dos_read, dos_write, dos_dir, fat_16 = 0; +static BOOL big_endian; + +/* maximum size of a cooked 12bit FAT. Also Size of 16bit FAT cache + * if not enough memory for whole FAT + */ +#define COOKED_SIZE 8192 +/* raw FAT. Only used for 12bit FAT to make conversion easier + */ +static unsigned char *raw_fat; +/* Cooked FAT. May be only part of the FAT for 16 bit FATs + */ +static unsigned short *cooked_fat; +/* lowest and highest entry in fat cache + */ +static unsigned short fat_low = USHRT_MAX, + fat_high = 0; +static BOOL fat_dirty = FALSE; +static unsigned int cache_size; + + +/* Prototypes. */ +_PROTOTYPE(void usage, (char *prog_name) ); +_PROTOTYPE(unsigned c2u2, (unsigned char *ucarray) ); +_PROTOTYPE(unsigned long c4u4, (unsigned char *ucarray) ); +_PROTOTYPE(void determine, (void)); +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(DIRECTORY *directory, (DIRECTORY *dir, int entries, BOOL function, char *pathname) ); +_PROTOTYPE(void extract, (DIRECTORY *entry) ); +_PROTOTYPE(void make_file, (DIRECTORY *dir_ptr, int entries, char *name) ); +_PROTOTYPE(void fill_date, (DIRECTORY *entry) ); +_PROTOTYPE(char *make_name, (DIRECTORY *dir_ptr, int dir_fl) ); +_PROTOTYPE(int fill, (char *buffer, size_t size) ); +_PROTOTYPE(void xmodes, (int mode) ); +_PROTOTYPE(void show, (DIRECTORY *dir_ptr, char *name) ); +_PROTOTYPE(void free_blocks, (void)); +_PROTOTYPE(DIRECTORY *read_cluster, (unsigned int cluster) ); +_PROTOTYPE(unsigned short free_cluster, (BOOL leave_fl) ); +_PROTOTYPE(void link_fat, (unsigned int cl_1, unsigned int cl_2) ); +_PROTOTYPE(unsigned short next_cluster, (unsigned int cl_no) ); +_PROTOTYPE(char *slash, (char *str) ); +_PROTOTYPE(void add_path, (char *file, BOOL slash_fl) ); +_PROTOTYPE(void disk_io, (BOOL op, unsigned long seek, void *address, unsigned bytes) ); +_PROTOTYPE(void flush_fat, (void)); +_PROTOTYPE(void read_fat, (unsigned int cl_no)); +_PROTOTYPE(BOOL free_range, (unsigned short *first, unsigned short *last)); +_PROTOTYPE(long lmin, (long a, long b)); + + +void usage(prog_name) +register char *prog_name; +{ + fprintf (stderr, "Usage: %s [%s\n", prog_name, + (dos_dir ? "-lr] drive [dir]" : "-a] drive file")); + exit(1); +} + +unsigned c2u2(ucarray) +unsigned char *ucarray; +{ + return ucarray[0] + (ucarray[1] << 8); /* parens vital */ +} + +unsigned long c4u4(ucarray) +unsigned char *ucarray; +{ + return ucarray[0] + ((unsigned long) ucarray[1] << 8) + + ((unsigned long) ucarray[2] << 16) + + ((unsigned long) ucarray[3] << 24); +} + +void determine() +{ + struct dosboot { + unsigned char cjump[2]; /* unsigneds avoid bugs */ + unsigned char nop; + unsigned char name[8]; + unsigned char cbytepers[2]; /* don't use shorts, etc */ + unsigned char secpclus; /* to avoid struct member */ + unsigned char creservsec[2]; /* alignment and byte */ + unsigned char fats; /* order bugs */ + unsigned char cdirents[2]; + unsigned char ctotsec[2]; + unsigned char media; + unsigned char csecpfat[2]; + unsigned char csecptrack[2]; + unsigned char cheads[2]; + unsigned char chiddensec[2]; + unsigned char dos4hidd2[2]; + unsigned char dos4totsec[4]; + /* Char fill[476]; */ + } boot; + unsigned short boot_magic; /* last of boot block */ + unsigned bytepers, reservsec, dirents; + unsigned secpfat, secptrack, heads, hiddensec; + unsigned long totsec; + unsigned char fat_info, fat_check; + unsigned short endiantest = 1; + int errcount = 0; + + big_endian = !(*(unsigned char *)&endiantest); + + /* Read Bios-Parameterblock */ + disk_io(READ, 0L, &boot, sizeof boot); + disk_io(READ, 0x1FEL, &boot_magic, sizeof boot_magic); + + /* Convert some arrays */ + bytepers = c2u2(boot.cbytepers); + reservsec = c2u2(boot.creservsec); + dirents = c2u2(boot.cdirents); + totsec = c2u2(boot.ctotsec); + if (totsec == 0) totsec = c4u4(boot.dos4totsec); + secpfat = c2u2(boot.csecpfat); + secptrack = c2u2(boot.csecptrack); + heads = c2u2(boot.cheads); + + /* The `hidden sectors' are the sectors before the partition. + * The calculation here is probably wrong (I think the dos4hidd2 + * bytes are the msbs), but that doesn't matter, since the + * value isn't used anyway + */ + hiddensec = c2u2(boot.chiddensec); + if (hiddensec == 0) hiddensec = c2u2 (boot.dos4hidd2); + + /* Safety checking */ + if (boot_magic != 0xAA55) { + fprintf (stderr, "%s: magic != 0xAA55\n", cmnd); + ++errcount; + } + + /* Check sectors per track instead of inadequate media byte */ + if (secptrack < 15 && /* assume > 15 hard disk & wini OK */ +#ifdef SECT10 /* BIOS modified for 10 sec/track */ + secptrack != 10 && +#endif +#ifdef SECT8 /* BIOS modified for 8 sec/track */ + secptrack != 8 && +#endif + secptrack != 9) { + fprintf (stderr, "%s: %d sectors per track not supported\n", cmnd, secptrack); + ++errcount; + } + if (bytepers == 0) { + fprintf (stderr, "%s: bytes per sector == 0\n", cmnd); + ++errcount; + } + if (boot.secpclus == 0) { + fprintf (stderr, "%s: sectors per cluster == 0\n", cmnd); + ++errcount; + } + if (boot.fats != 2 && dos_write) { + fprintf (stderr, "%s: fats != 2\n", cmnd); + ++errcount; + } + if (reservsec != 1) { + fprintf (stderr, "%s: reserved != 1\n", cmnd); + ++errcount; + } + if (errcount != 0) { + fprintf (stderr, "%s: Can't handle disk\n", cmnd); + exit(2); + } + + /* Calculate everything. */ + if (boot.secpclus == 0) boot.secpclus = 1; + total_clusters = + (totsec - boot.fats * secpfat - reservsec - + dirents * 32L / bytepers ) / boot.secpclus + 2; + /* first 2 entries in FAT aren't used */ + cluster_size = bytepers * boot.secpclus; + fat_size = (unsigned long) secpfat * (unsigned long) bytepers; + data_start = (long) bytepers + (long) boot.fats * fat_size + + (long) dirents *32L; + root_entries = dirents; + sub_entries = boot.secpclus * bytepers / 32; + if (total_clusters > 4096) fat_16 = 1; + + /* Further safety checking */ + if (cluster_size > MAX_CLUSTER_SIZE) { + fprintf (stderr, "%s: cluster size too big\n", cmnd); + ++errcount; + } + + disk_io(READ, FAT_START, &fat_info, 1); + disk_io(READ, FAT_START + fat_size, &fat_check, 1); + if (fat_check != fat_info) { + fprintf (stderr, "%s: Disk type in FAT copy differs from disk type in FAT original.\n", cmnd); + ++errcount; + } + if (errcount != 0) { + fprintf (stderr, "%s: Can't handle disk\n", cmnd); + exit(2); + } +} + +int main(argc, argv) +int argc; +register char *argv[]; +{ + register char *arg_ptr = slash(argv[0]); + DIRECTORY *entry; + short idx = 1; + char dev_nr = '0'; + + cmnd = arg_ptr; /* needed for error messages */ + if (!strcmp(arg_ptr, "dosdir")) + dos_dir = TRUE; + else if (!strcmp(arg_ptr, "dosread")) + dos_read = TRUE; + else if (!strcmp(arg_ptr, "doswrite")) + dos_write = TRUE; + else { + fprintf (stderr, "%s: Program should be named dosread, doswrite or dosdir.\n", cmnd); + exit(1); + } + + if (argc == 1) usage(argv[0]); + + if (argv[1][0] == '-') { + for (arg_ptr = &argv[1][1]; *arg_ptr; arg_ptr++) { + if (*arg_ptr == 'l' && dos_dir) { + Lflag = TRUE; + } else if (*arg_ptr == 'r' && dos_dir) { + Rflag = TRUE; + } else if (*arg_ptr == 'a' && !dos_dir) { + assert ('\n' == 10); + assert ('\r' == 13); + Aflag = TRUE; + } else { + usage(argv[0]); + } + } + idx++; + } + if (idx == argc) usage(argv[0]); + + if (strlen(argv[idx]) > 1) { + device = argv[idx++]; + + /* If the device does not contain a / we assume that it + * is the name of a device in /dev. Instead of prepending + * /dev/ we try to chdir there. + */ + if (strchr(device, '/') == NULL && chdir("/dev") < 0) { + perror("/dev"); + exit(1); + } + } else { + if ((dev_nr = toupper (*argv[idx++])) < 'A' || dev_nr > 'Z') + usage(argv[0]); + + device[DRIVE_NR] = dev_nr; + } + + if ((disk = open(device, dos_write ? O_RDWR : O_RDONLY)) < 0) { + fprintf (stderr, "%s: cannot open %s: %s\n", + cmnd, device, strerror (errno)); + exit(1); + } + determine(); + disk_io(READ, ROOTADDR, root, DIR_SIZE * root_entries); + + if (dos_dir && Lflag) { + entry = label(); + printf ("Volume in drive %c ", dev_nr); + if (entry == NIL_DIR) + printf("has no label.\n\n"); + else + printf ("is %.11s\n\n", entry->d_name); + } + if (argv[idx] == NIL_PTR) { + if (!dos_dir) usage(argv[0]); + if (Lflag) printf ("Root directory:\n"); + list_dir(root, root_entries, FALSE); + if (Lflag) free_blocks(); + fflush (stdout); + exit(0); + } + for (arg_ptr = argv[idx]; *arg_ptr; arg_ptr++) + if (*arg_ptr == '\\') *arg_ptr = '/'; + else *arg_ptr = toupper (*arg_ptr); + if (*--arg_ptr == '/') *arg_ptr = '\0'; /* skip trailing '/' */ + + add_path(argv[idx], FALSE); + add_path("/", FALSE); + + if (dos_dir && Lflag) printf ( "Directory %s:\n", path); + + entry = find_entry(root, root_entries, argv[idx]); + + if (dos_dir) { + list_dir(entry, sub_entries, FALSE); + if (Lflag) free_blocks(); + } else if (dos_read) + extract(entry); + else { + if (entry != NIL_DIR) { + fflush (stdout); + if (is_dir(entry)) + fprintf (stderr, "%s: %s is a directory.\n", cmnd, path); + else + fprintf (stderr, "%s: %s already exists.\n", cmnd, argv[idx]); + exit(1); + } + add_path(NIL_PTR, TRUE); + + if (*path) make_file(find_entry(root, root_entries, path), + sub_entries, slash(argv[idx])); + else + make_file(root, root_entries, argv[idx]); + } + + (void) close(disk); + fflush (stdout); + exit(0); + return(0); +} + + +/* General directory search routine. + * + * dir: + * Points to one or more directory entries + * entries: + * number of entries + * if entries == root_entries, dir points to the entire + * root directory. Otherwise it points to a single directory + * entry describing the directory to be searched. + * + * function: + * FIND ... find pathname relative to directory dir. + * LABEL ... find first label entry in dir. + * ENTRY ... create a new empty entry. + * FALSE ... list directory + * + * pathname: + * name of the file to be found or directory to be listed. + * must be in upper case, pathname components must be + * separated by slashes, but can be longer than than + * 8+3 characters (The rest is ignored). + */ +DIRECTORY *directory(dir, entries, function, pathname) +DIRECTORY *dir; +int entries; +int function; +register char *pathname; +{ + register DIRECTORY *dir_ptr = dir; + DIRECTORY *mem = NIL_DIR; + unsigned short cl_no = dir->d_cluster; + unsigned short type, last = 0; + char file_name[14]; + char *name; + int i = 0; + + if (function == FIND) { + while (*pathname != '/' && *pathname != '.' && *pathname && + i < 8) { + file_name[i++] = *pathname++; + } + if (*pathname == '.') { + int j = 0; + file_name[i++] = *pathname++; + while (*pathname != '/' && *pathname != '.' && *pathname && + j++ < 3) { + file_name[i++] = *pathname++; + } + } + while (*pathname != '/' && *pathname) pathname++; + file_name[i] = '\0'; + } + do { + if (entries != root_entries) { + mem = dir_ptr = read_cluster(cl_no); + last = cl_no; + cl_no = next_cluster(cl_no); + } + for (i = 0; i < entries; i++, dir_ptr++) { + type = dir_ptr->d_name[0] & 0x0FF; + if (function == ENTRY) { + if (type == NOT_USED || type == ERASED) { + if (!mem) + mark = ROOTADDR + (long) i *(long) DIR_SIZE; + else + mark = clus_add(last) + (long) i *(long) DIR_SIZE; + return dir_ptr; + } + continue; + } + if (type == NOT_USED) break; + if (dir_ptr->d_attribute & 0x08) { + if (function == LABEL) return dir_ptr; + continue; + } + if (type == DIR || type == ERASED || function == LABEL) + continue; + type = is_dir(dir_ptr); + name = make_name(dir_ptr, + (function == FIND) ? FALSE : type); + if (function == FIND) { + if (strcmp(file_name, name) != 0) continue; + if (!type) { + if (dos_dir || *pathname) { + fflush (stdout); + fprintf (stderr, "%s: Not a directory: %s\n", cmnd, file_name); + exit(1); + } + } else if (*pathname == '\0' && dos_read) { + fflush (stdout); + fprintf (stderr, "%s: %s is a directory.\n", cmnd, path); + exit(1); + } + if (*pathname) { + dir_ptr = find_entry(dir_ptr, + sub_entries, pathname + 1); + } + if (mem) { + if (dir_ptr) { + memcpy((char *)&save_entry, (char *)dir_ptr, DIR_SIZE); + dir_ptr = &save_entry; + } + free( (void *) mem); + } + return dir_ptr; + } else { + if (function == FALSE) { + show(dir_ptr, name); + } else if (type) { /* Recursive */ + printf ( "Directory %s%s:\n", path, name); + add_path(name, FALSE); + list_dir(dir_ptr, sub_entries, FALSE); + add_path(NIL_PTR, FALSE); + } + } + } + if (mem) free( (void *) mem); + } while (cl_no != LAST_CLUSTER && mem); + + switch (function) { + case FIND: + if (dos_write && *pathname == '\0') return NIL_DIR; + fflush (stdout); + fprintf (stderr, "%s: Cannot find `%s'.\n", cmnd, file_name); + exit(1); + case LABEL: + return NIL_DIR; + case ENTRY: + if (!mem) { + fflush (stdout); + fprintf (stderr, "%s: No entries left in root directory.\n", cmnd); + exit(1); + } + cl_no = free_cluster(TRUE); + link_fat(last, cl_no); + link_fat(cl_no, LAST_CLUSTER); + disk_io(WRITE, clus_add(cl_no), null, cluster_size); + + return new_entry(dir, entries); + case FALSE: + if (Rflag) { + printf ("\n"); + list_dir(dir, entries, TRUE); + } + } + return NULL; +} + +void extract(entry) +register DIRECTORY *entry; +{ + register unsigned short cl_no = entry->d_cluster; + char buffer[MAX_CLUSTER_SIZE]; + int rest, i; + + if (entry->d_size == 0) /* Empty file */ + return; + + do { + disk_io(READ, clus_add(cl_no), buffer, cluster_size); + rest = (entry->d_size > (long) cluster_size) ? cluster_size : (short) entry->d_size; + + if (Aflag) { + for (i = 0; i < rest; i ++) { + if (buffer [i] != '\r') putchar (buffer [i]); + } + if (ferror (stdout)) { + fprintf (stderr, "%s: cannot write to stdout: %s\n", + cmnd, strerror (errno)); + exit (1); + } + } else { + if (fwrite (buffer, 1, rest, stdout) != rest) { + fprintf (stderr, "%s: cannot write to stdout: %s\n", + cmnd, strerror (errno)); + exit (1); + } + } + entry->d_size -= (long) rest; + cl_no = next_cluster(cl_no); + if (cl_no == BAD16) { + fflush (stdout); + fprintf (stderr, "%s: reserved cluster value %x encountered.\n", + cmnd, cl_no); + exit (1); + } + } while (entry->d_size && cl_no != LAST_CLUSTER); + + if (cl_no != LAST_CLUSTER) + fprintf (stderr, "%s: Too many clusters allocated for file.\n", cmnd); + else if (entry->d_size != 0) + fprintf (stderr, "%s: Premature EOF: %ld bytes left.\n", cmnd, + entry->d_size); +} + + +/* Minimum of two long values + */ +long lmin (a, b) +long a, b; +{ + if (a < b) return a; + else return b; +} + + +void make_file(dir_ptr, entries, name) +DIRECTORY *dir_ptr; +int entries; +char *name; +{ + register DIRECTORY *entry = new_entry(dir_ptr, entries); + register char *ptr; + char buffer[MAX_CLUSTER_SIZE]; + unsigned short cl_no = 0; + int i, r; + long size = 0L; + unsigned short first_cluster, last_cluster; + long chunk; + + memset (&entry->d_name[0], ' ', 11); /* clear entry */ + for (i = 0, ptr = name; i < 8 && *ptr != '.' && *ptr; i++) + entry->d_name[i] = *ptr++; + while (*ptr != '.' && *ptr) ptr++; + if (*ptr == '.') ptr++; + for (i = 0; i < 3 && *ptr != '.' && *ptr; i++) entry->d_ext[i] = *ptr++; + + for (i = 0; i < 10; i++) entry->d_reserved[i] = '\0'; + entry->d_attribute = '\0'; + + entry->d_cluster = 0; + + while (free_range (&first_cluster, &last_cluster)) { + do { + unsigned short nr_clus; + + chunk = lmin ((long) (last_cluster - first_cluster + 1) * + cluster_size, + (long) MAX_CLUSTER_SIZE); + r = fill(buffer, chunk); + if (r == 0) goto done; + nr_clus = (r + cluster_size - 1) / cluster_size; + disk_io(WRITE, clus_add(first_cluster), buffer, r); + + for (i = 0; i < nr_clus; i ++) { + if (entry->d_cluster == 0) + cl_no = entry->d_cluster = first_cluster; + else { + link_fat(cl_no, first_cluster); + cl_no = first_cluster; + } + first_cluster ++; + } + + size += r; + } while (first_cluster <= last_cluster); + } + fprintf (stderr, "%s: disk full. File truncated\n", cmnd); +done: + if (entry->d_cluster != 0) link_fat(cl_no, LAST_CLUSTER); + entry->d_size = size; + fill_date(entry); + disk_io(WRITE, mark, entry, DIR_SIZE); + + if (fat_dirty) flush_fat (); + +} + + +#define SEC_MIN 60L +#define SEC_HOUR (60L * SEC_MIN) +#define SEC_DAY (24L * SEC_HOUR) +#define SEC_YEAR (365L * SEC_DAY) +#define SEC_LYEAR (366L * SEC_DAY) + +unsigned short mon_len[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; + +void fill_date(entry) +DIRECTORY *entry; +{ + register long cur_time = time((long *) 0) - DOS_TIME; + unsigned short year = 0, month = 1, day, hour, minutes, seconds; + int i; + long tmp; + + if (cur_time < 0) /* Date not set on booting ... */ + cur_time = 0; + for (;;) { + tmp = (year % 4 == 0) ? SEC_LYEAR : SEC_YEAR; + if (cur_time < tmp) break; + cur_time -= tmp; + year++; + } + + day = (unsigned short) (cur_time / SEC_DAY); + cur_time -= (long) day *SEC_DAY; + + hour = (unsigned short) (cur_time / SEC_HOUR); + cur_time -= (long) hour *SEC_HOUR; + + minutes = (unsigned short) (cur_time / SEC_MIN); + cur_time -= (long) minutes *SEC_MIN; + + seconds = (unsigned short) cur_time; + + mon_len[1] = (year % 4 == 0) ? 29 : 28; + i = 0; + while (day >= mon_len[i]) { + month++; + day -= mon_len[i++]; + } + day++; + + entry->d_date = (year << 9) | (month << 5) | day; + entry->d_time = (hour << 11) | (minutes << 5) | seconds; +} + +char *make_name(dir_ptr, dir_fl) +register DIRECTORY *dir_ptr; +short dir_fl; +{ + static char name_buf[14]; + register char *ptr = name_buf; + short i; + + for (i = 0; i < 8; i++) *ptr++ = dir_ptr->d_name[i]; + + while (*--ptr == ' '); + assert (ptr >= name_buf); + + ptr++; + if (dir_ptr->d_ext[0] != ' ') { + *ptr++ = '.'; + for (i = 0; i < 3; i++) *ptr++ = dir_ptr->d_ext[i]; + while (*--ptr == ' '); + ptr++; + } + if (dir_fl) *ptr++ = '/'; + *ptr = '\0'; + + return name_buf; +} + + +int fill(buffer, size) +register char *buffer; +size_t size; +{ + static BOOL nl_mark = FALSE; + char *last = &buffer[size]; + char *begin = buffer; + register int c; + + while (buffer < last) { + if (nl_mark) { + *buffer ++ = '\n'; + nl_mark = FALSE; + } else { + c = getchar(); + if (c == EOF) break; + if (Aflag && c == '\n') { + *buffer ++ = '\r'; + nl_mark = TRUE; + } else { + *buffer++ = c; + } + } + } + + return (buffer - begin); +} + +#define HOUR 0xF800 /* Upper 5 bits */ +#define MIN 0x07E0 /* Middle 6 bits */ +#define YEAR 0xFE00 /* Upper 7 bits */ +#define MONTH 0x01E0 /* Mid 4 bits */ +#define DAY 0x01F /* Lowest 5 bits */ + +char *month[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +void xmodes(mode) +int mode; +{ + printf ( "\t%c%c%c%c%c", (mode & SUB_DIR) ? 'd' : '-', + (mode & 02) ? 'h' : '-', (mode & 04) ? 's' : '-', + (mode & 01) ? '-' : 'w', (mode & 0x20) ? 'a' : '-'); +} + +void show(dir_ptr, name) +DIRECTORY *dir_ptr; +char *name; +{ + register unsigned short e_date = dir_ptr->d_date; + register unsigned short e_time = dir_ptr->d_time; + unsigned short next; + char bname[20]; + short i = 0; + + while (*name && *name != '/') bname[i++] = *name++; + bname[i] = '\0'; + if (!Lflag) { + printf ( "%s\n", bname); + return; + } + xmodes( (int) dir_ptr->d_attribute); + printf ( "\t%s%s", bname, strlen(bname) < 8 ? "\t\t" : "\t"); + i = 1; + if (is_dir(dir_ptr)) { + next = dir_ptr->d_cluster; + while ((next = next_cluster(next)) != LAST_CLUSTER) i++; + printf ("%8ld", (long) i * (long) cluster_size); + } else + printf ("%8ld", dir_ptr->d_size); + printf (" %02d:%02d %2d %s %d\n", ((e_time & HOUR) >> 11), + ((e_time & MIN) >> 5), (e_date & DAY), + month[((e_date & MONTH) >> 5) - 1], ((e_date & YEAR) >> 9) + 1980); +} + +void free_blocks() +{ + register unsigned short cl_no; + long nr_free = 0; + long nr_bad = 0; + + for (cl_no = 2; cl_no < total_clusters; cl_no++) { + switch (next_cluster(cl_no)) { + case FREE: nr_free++; break; + case BAD16: nr_bad++; break; + } + } + + printf ("Free space: %ld bytes.\n", nr_free * (long) cluster_size); + if (nr_bad != 0) + printf ("Bad sectors: %ld bytes.\n", nr_bad * (long) cluster_size); +} + + +DIRECTORY *read_cluster(cluster) +register unsigned int cluster; +{ + register DIRECTORY *sub_dir; + + if ((sub_dir = malloc(cluster_size)) == NULL) { + fprintf (stderr, "%s: Cannot set break!\n", cmnd); + exit(1); + } + disk_io(READ, clus_add(cluster), sub_dir, cluster_size); + + return sub_dir; +} + +static unsigned short cl_index = 2; + +/* find a range of consecutive free clusters. Return TRUE if found + * and return the first and last cluster in the |*first| and |*last|. + * If no free clusters are left, return FALSE. + * + * Warning: Assumes that all of the range is used before the next call + * to free_range or free_cluster. + */ +BOOL free_range (first, last) +unsigned short *first, *last; +{ + while (cl_index < total_clusters && next_cluster(cl_index) != FREE) + cl_index++; + if (cl_index >= total_clusters) return FALSE; + *first = cl_index; + while (cl_index < total_clusters && next_cluster(cl_index) == FREE) + cl_index++; + *last = cl_index - 1; + return TRUE; +} + + +/* find a free cluster. + * Return the number of the free cluster or a number > |total_clusters| + * if none is found. + * If |leave_fl| is TRUE, the the program will be terminated if + * no free cluster can be found + * + * Warning: Assumes that the cluster is used before the next call + * to free_range or free_cluster. + */ +unsigned short free_cluster(leave_fl) +BOOL leave_fl; +{ + while (cl_index < total_clusters && next_cluster(cl_index) != FREE) + cl_index++; + + if (leave_fl && cl_index >= total_clusters) { + fprintf (stderr, "%s: Diskette full. File not added.\n", cmnd); + exit(1); + } + return cl_index++; +} + + +/* read a portion of the fat containing |cl_no| into the cache + */ +void read_fat (cl_no) + unsigned int cl_no; +{ + + if (!cooked_fat) { + /* Read the fat for the first time. We have to allocate all the + * buffers + */ + if (fat_16) { + /* FAT consists of little endian shorts. Easy to convert + */ + if ((cooked_fat = malloc (fat_size)) == NULL) { + /* Oops, FAT doesn't fit into memory, just read + * a chunk + */ + if ((cooked_fat = malloc (COOKED_SIZE)) == NULL) { + fprintf (stderr, "%s: not enough memory for FAT cache. Use chmem\n", + cmnd); + exit (1); + } + cache_size = COOKED_SIZE / 2; + } else { + cache_size = fat_size / 2; + } + } else { + /* 12 bit FAT. Difficult encoding, but small. Keep + * both raw FAT and cooked version in memory. + */ + if ((cooked_fat = malloc (total_clusters * sizeof (short))) == NULL || + (raw_fat = malloc (fat_size)) == NULL) { + fprintf (stderr, "%s: not enough memory for FAT cache. Use chmem\n", + cmnd); + exit (1); + } + cache_size = total_clusters; + } + } + fat_low = cl_no / cache_size * cache_size; + fat_high = fat_low + cache_size - 1; + + if (!fat_16) { + unsigned short *cp; + unsigned char *rp; + unsigned short i; + + disk_io (READ, FAT_START, raw_fat, fat_size); + for (rp = raw_fat, cp = cooked_fat, i = 0; + i < cache_size; + rp += 3, i += 2) { + *cp = *rp + ((*(rp + 1) & 0x0f) << 8); + if (*cp == BAD) *cp = BAD16; + else if (*cp == LAST_CLUSTER12) *cp = LAST_CLUSTER; + cp ++; + *cp = ((*(rp + 1) & 0xf0) >> 4) + (*(rp + 2) << 4); + if (*cp == BAD) *cp = BAD16; + else if (*cp == LAST_CLUSTER12) *cp = LAST_CLUSTER; + cp ++; + } + } else { + + assert (sizeof (short) == 2); + assert (CHAR_BIT == 8); /* just in case */ + + disk_io (READ, FAT_START + fat_low * 2, (void *)cooked_fat, cache_size * 2); + if (big_endian) { + unsigned short *cp; + unsigned char *rp; + unsigned short i; + + for (i = 0, rp = (unsigned char *)cooked_fat /* sic */, cp = cooked_fat; + i < cache_size; + rp += 2, cp ++, i ++) { + *cp = c2u2 (rp); + } + } + } +} + + +/* flush the fat cache out to disk + */ +void flush_fat () +{ + if (fat_16) { + if (big_endian) { + unsigned short *cp; + unsigned char *rp; + unsigned short i; + + for (i = 0, rp = (unsigned char *)cooked_fat /* sic */, cp = cooked_fat; + i < cache_size; + rp += 2, cp ++, i ++) { + *rp = *cp; + *(rp + 1) = *cp >> 8; + } + } + disk_io (WRITE, FAT_START + fat_low * 2, (void *)cooked_fat, cache_size * 2); + disk_io (WRITE, FAT_START + fat_size + fat_low * 2, (void *)cooked_fat, cache_size * 2); + } else { + unsigned short *cp; + unsigned char *rp; + unsigned short i; + + for (rp = raw_fat, cp = cooked_fat, i = 0; + i < cache_size; + rp += 3, cp += 2, i += 2) { + *rp = *cp; + *(rp + 1) = ((*cp & 0xf00) >> 8) | + ((*(cp + 1) & 0x00f) << 4); + *(rp + 2) = ((*(cp + 1) & 0xff0) >> 4); + } + disk_io (WRITE, FAT_START, raw_fat, fat_size); + disk_io (WRITE, FAT_START + fat_size, raw_fat, fat_size); + } +} + + +/* make cl_2 the successor of cl_1 + */ +void link_fat(cl_1, cl_2) +unsigned int cl_1; +unsigned int cl_2; +{ + if (cl_1 < fat_low || cl_1 > fat_high) { + if (fat_dirty) flush_fat (); + read_fat (cl_1); + } + cooked_fat [cl_1 - fat_low] = cl_2; + fat_dirty = TRUE; +} + + +unsigned short next_cluster(cl_no) +register unsigned int cl_no; +{ + if (cl_no < fat_low || cl_no > fat_high) { + if (fat_dirty) flush_fat (); + read_fat (cl_no); + } + return cooked_fat [cl_no - fat_low]; +} + +char *slash(str) +register char *str; +{ + register char *result = str; + + while (*str) + if (*str++ == '/') result = str; + + return result; +} + +void add_path(file, slash_fl) +char *file; +BOOL slash_fl; +{ + register char *ptr = path; + + while (*ptr) ptr++; + + if (file == NIL_PTR) { + if (ptr != path) ptr--; + if (ptr != path) do { + ptr--; + } while (*ptr != '/' && ptr != path); + if (ptr != path && !slash_fl) *ptr++ = '/'; + *ptr = '\0'; + } else + strcpy (ptr, file); +} + + +void disk_io(op, seek, address, bytes) +register BOOL op; +unsigned long seek; +void *address; +register unsigned bytes; +{ + unsigned int r; + + if (lseek(disk, seek, SEEK_SET) < 0L) { + fflush (stdout); + fprintf (stderr, "%s: Bad lseek: %s\n", cmnd, strerror (errno)); + exit(1); + } + if (op == READ) + r = read(disk, (char *) address, bytes); + else { + r = write(disk, (char *) address, bytes); + } + + if (r != bytes) { + fprintf (stderr, "%s: read error: %s\n", cmnd, strerror (errno)); + exit (1); + } +} + +char dosread_c_rcs_id [] = + "$Id$"; +/* $Log$ + * Revision 1.1 2005/04/21 14:54:57 beng + * Initial revision + * +/* Revision 1.1.1.1 2005/04/20 13:33:07 beng +/* Initial import of minix 2.0.4 +/* + * Revision 1.8 1994/05/14 21:53:08 hjp + * filenames with more than 3 characters and extension work again. + * removed debugging stuff and b_copy. + * + * Revision 1.7 1994/04/09 03:09:01 hjp + * (posted to comp.os.minix) + * merged branch 1.5.387 with stem. + * changed treatment of drive parameter + * + * Revision 1.5.387.9 1994/04/09 02:07:51 hjp + * Disk full no longer produces lost clusters but a truncated file. + * Truncated file names to 8+3 before comparisons to avoid duplicate + * files and filenames containing dots in the extension. + * Replaced sbrk and brk by malloc and free (mixing brk and malloc causes + * heap corruption which sometimes lead to core dumps. It may also have + * been the cause of data corruption Kees reported). + * Made global variables static and removed some unused ones. + * Error messages now contain program name. + * + * Revision 1.5.387.8 1993/11/13 00:38:45 hjp + * Posted to comp.os.minix and included in Minix-386vm 1.6.25.1. + * Speed optimizations for 1.44 MB disks. + * Replaced lowlevel I/O by stdio. + * Simplified -a: Now only removes resp. adds CRs + * Cleaned up. + * + * Revision 1.5.387.1 1993/01/15 19:32:29 ast + * Released with 1.6.24b + */ diff --git a/commands/ibm/fdisk.c b/commands/ibm/fdisk.c new file mode 100755 index 000000000..ad5bfd599 --- /dev/null +++ b/commands/ibm/fdisk.c @@ -0,0 +1,1009 @@ +/* fdisk - partition a hard disk Author: Jakob Schripsema */ + +/* Run this with: + * + * fdisk [-hheads] [-ssectors] [device] + * + * e.g., + * + * fdisk (to get the default) + * fdisk -h4 -s17 /dev/hd0 (MINIX default) + * fdisk -h4 -s17 c: (DOS default) + * fdisk -h6 -s25 /dev/hd5 (second drive, probably RLL) + * fdisk junkfile (to experiment safely) + * + * The device is opened in read-only mode if the file permissions do not + * permit read-write mode, so it is convenient to use a login account with + * only read permission to look at the partition table safely. + * + * Compile with: + * + * cc -i -o fdisk fdisk.c (MINIX) + * cl -DDOS fdisk.c (DOS with MS C compiler) + * + * This was modified extensively by Bruce Evans 28 Dec 89. + * The new version has not been tried with DOS. The open modes are suspect + * (everyone should convert to use fcntl.h). + * + * Changed 18 Dec 92 by Kees J. Bot: Bootstrap code and geometry from device. + * + * modified 01 March 95 by asw: updated list of known partition types. Also + * changed display format slightly to allow for partition type names of + * up to 9 chars (previous format allowed for 7, but there were already + * some 8 char names in the list). +*/ + +#include <sys/types.h> +#include <ibm/partition.h> +#include <minix/partition.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <errno.h> + +#ifdef DOS +#include <dos.h> +#define DEFAULT_DEV "c:" +#define LOAD_OPEN_MODE 0x8000 +#define SAVE_CREAT_MODE 0644 +#else +#define DEFAULT_DEV "/dev/hd0" +#define LOAD_OPEN_MODE 0 +#define SAVE_CREAT_MODE 0644 +#define UNIX /* for MINIX */ +#endif + +/* Constants */ + +#define DEFAULT_NHEAD 4 /* # heads */ +#define DEFAULT_NSEC 17 /* sectors / track */ +#define SECSIZE 512 /* sector size */ +#define OK 0 +#define ERR 1 + +#define CYL_MASK 0xc0 /* mask to extract cyl bits from sec field */ +#define CYL_SHIFT 2 /* shift to extract cyl bits from sec field */ +#define SEC_MASK 0x3f /* mask to extract sec bits from sec field */ + +/* Globals */ +char rawsecbuf[SECSIZE + sizeof(long)]; +char *secbuf; +int badbases; +int badsizes; +int badorders; +char *devname; +int nhead; +int nsec; +int ncyl = 1024; +int readonly; +int override= 0; + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void getgeom, (void)); +_PROTOTYPE(int getboot, (char *buffer)); +_PROTOTYPE(int putboot, (char *buffer)); +_PROTOTYPE(int load_from_file, (void)); +_PROTOTYPE(int save_to_file, (void)); +_PROTOTYPE(int dpl_partitions, (int rawflag)); +_PROTOTYPE(int chk_table, (void)); +_PROTOTYPE(int sec_to_hst, (long logsec, unsigned char *hd, unsigned char *sec, + unsigned char *cyl)); +_PROTOTYPE(int mark_partition, (struct part_entry *pe)); +_PROTOTYPE(int change_partition, (struct part_entry *entry)); +_PROTOTYPE(int get_a_char, (void)); +_PROTOTYPE(int print_menu, (void)); +_PROTOTYPE(void adj_base, (struct part_entry *pe)); +_PROTOTYPE(void adj_size, (struct part_entry *pe)); +_PROTOTYPE(struct part_entry *ask_partition, (void)); +_PROTOTYPE(void footnotes, (void)); +_PROTOTYPE(int get_an_int, (char *prompt, int *intptr)); +_PROTOTYPE(void list_part_types, (void)); +_PROTOTYPE(void mark_npartition, (struct part_entry *pe)); +_PROTOTYPE(int mygets, (char *buf, int length)); +_PROTOTYPE(char *systype, (int type)); +_PROTOTYPE(void toggle_active, (struct part_entry *pe)); +_PROTOTYPE(void usage, (void)); + +/* One featureful master bootstrap. */ +char bootstrap[] = { +0353,0001,0000,0061,0300,0216,0330,0216,0300,0372,0216,0320,0274,0000,0174,0373, +0275,0276,0007,0211,0346,0126,0277,0000,0006,0271,0000,0001,0374,0363,0245,0352, +0044,0006,0000,0000,0264,0002,0315,0026,0250,0010,0164,0033,0350,0071,0001,0174, +0007,0060,0344,0315,0026,0242,0205,0007,0054,0060,0074,0012,0163,0363,0120,0350, +0046,0001,0205,0007,0130,0353,0012,0240,0002,0006,0204,0300,0165,0003,0351,0147, +0000,0230,0262,0005,0366,0362,0262,0200,0000,0302,0210,0340,0120,0350,0234,0000, +0163,0003,0351,0147,0000,0130,0054,0001,0175,0003,0351,0141,0000,0276,0276,0175, +0211,0357,0271,0040,0000,0363,0245,0200,0301,0004,0211,0356,0215,0174,0020,0070, +0154,0004,0164,0016,0213,0135,0010,0053,0134,0010,0213,0135,0012,0033,0134,0012, +0163,0014,0212,0044,0206,0144,0020,0210,0044,0106,0071,0376,0162,0364,0211,0376, +0201,0376,0356,0007,0162,0326,0342,0322,0211,0356,0264,0020,0366,0344,0001,0306, +0200,0174,0004,0001,0162,0026,0353,0021,0204,0322,0175,0041,0211,0356,0200,0174, +0004,0000,0164,0013,0366,0004,0200,0164,0006,0350,0070,0000,0162,0053,0303,0203, +0306,0020,0201,0376,0376,0007,0162,0346,0350,0215,0000,0211,0007,0376,0302,0204, +0322,0174,0023,0315,0021,0321,0340,0321,0340,0200,0344,0003,0070,0342,0167,0355, +0350,0011,0000,0162,0350,0303,0350,0003,0000,0162,0146,0303,0211,0356,0214,0134, +0010,0214,0134,0012,0277,0003,0000,0122,0006,0127,0264,0010,0315,0023,0137,0007, +0200,0341,0077,0376,0306,0210,0310,0366,0346,0211,0303,0213,0104,0010,0213,0124, +0012,0367,0363,0222,0210,0325,0366,0361,0060,0322,0321,0352,0321,0352,0010,0342, +0210,0321,0376,0301,0132,0210,0306,0273,0000,0174,0270,0001,0002,0315,0023,0163, +0020,0200,0374,0200,0164,0011,0117,0174,0006,0060,0344,0315,0023,0163,0270,0371, +0303,0201,0076,0376,0175,0125,0252,0165,0001,0303,0350,0013,0000,0243,0007,0353, +0005,0350,0004,0000,0227,0007,0353,0376,0136,0255,0126,0211,0306,0254,0204,0300, +0164,0011,0264,0016,0273,0001,0000,0315,0020,0353,0362,0303,0057,0144,0145,0166, +0057,0150,0144,0077,0010,0000,0015,0012,0000,0116,0157,0156,0145,0040,0141,0143, +0164,0151,0166,0145,0015,0012,0000,0122,0145,0141,0144,0040,0145,0162,0162,0157, +0162,0040,0000,0116,0157,0164,0040,0142,0157,0157,0164,0141,0142,0154,0145,0040, +0000,0000, +}; + +main(argc, argv) +int argc; +char *argv[]; +{ + int argn; + char *argp; + int ch; + + /* Init */ + + nhead = DEFAULT_NHEAD; + nsec = DEFAULT_NSEC; + for (argn = 1; argn < argc && (argp = argv[argn])[0] == '-'; ++argn) { + if (argp[1] == 'h') + nhead = atoi(argp + 2); + else + if (argp[1] == 's') nsec = atoi(argp + 2); + else + usage(); + override= 1; + } + + if (argn == argc) + devname = DEFAULT_DEV; + else if (argn == argc - 1) + devname = argv[argn]; + else + usage(); + + /* Align the sector buffer in such a way that the partition table is at + * a mod 4 offset in memory. Some weird people add alignment checks to + * their Minix! + */ + secbuf = rawsecbuf; + while ((long)(secbuf + PART_TABLE_OFF) % sizeof(long) != 0) secbuf++; + + getgeom(); + getboot(secbuf); + chk_table(); + + do { + putchar('\n'); + dpl_partitions(0); + printf( + "\n(Enter 'h' for help. A null line will abort any operation) "); + ch = get_a_char(); + putchar('\n'); + switch (ch) { + case '+': footnotes(); break; + case 'a': toggle_active(ask_partition()); break; + case 'B': adj_base(ask_partition()); break; + case 'c': change_partition(ask_partition()); break; + case 'h': print_menu(); break; + case 'l': load_from_file(); break; + case 'm': mark_partition(ask_partition()); break; + case 'n': mark_npartition(ask_partition()); break; + case 'p': dpl_partitions(1); break; + case 0: + case 'q': exit(0); + case 'S': adj_size(ask_partition()); break; + case 's': save_to_file(); break; + case 't': list_part_types(); break; + case 'v': + printf("Partition table is %svalid\n", + chk_table() == OK ? "" : "in"); + break; + case 'w': + if (readonly) + printf("Write disabled\n"); + else if(chk_table() == OK) { + putboot(secbuf); + printf( + "Partition table has been updated and the file system synced.\n"); + printf("Please reboot now.\n"); + exit(0); + } else + printf("Not written\n"); + break; + default: printf(" %c ????\n", ch); break; + } + } + while (1); +} + + +#ifdef UNIX + +void getgeom() +{ + struct partition geom; + int fd, r; + + if (override) return; + + if ((fd= open(devname, O_RDONLY)) < 0) return; + + r = ioctl(fd, DIOCGETP, &geom); + close(fd); + if (r < 0) return; + + nhead = geom.heads; + nsec = geom.sectors; + ncyl = geom.cylinders; + + printf("Geometry of %s: %dx%dx%d\n", devname, ncyl, nhead, nsec); +} + +static int devfd; + +getboot(buffer) +char *buffer; +{ + devfd = open(devname, 2); + if (devfd < 0) { + printf("No write permission on %s\n", devname); + readonly = 1; + devfd = open(devname, 0); + } + if (devfd < 0) { + printf("Cannot open device %s\n", devname); + exit(1); + } + if (read(devfd, buffer, SECSIZE) != SECSIZE) { + printf("Cannot read boot sector\n"); + exit(1); + } + if (* (unsigned short *) &buffer[510] != 0xAA55) { + printf("Invalid boot sector on %s.\n", devname); + printf("Partition table reset and boot code installed.\n"); + memset(buffer, 0, 512); + memcpy(buffer, bootstrap, sizeof(bootstrap)); + * (unsigned short *) &buffer[510] = 0xAA55; + } +} + +putboot(buffer) +char *buffer; +{ + if (lseek(devfd, 0L, 0) < 0) { + printf("Seek error during write\n"); + exit(1); + } + if (write(devfd, buffer, SECSIZE) != SECSIZE) { + printf("Write error\n"); + exit(1); + } + sync(); +} + +#endif + + +load_from_file() +{ +/* Load buffer from file */ + + char file[80]; + int fd; + + printf("Enter name of file to load from: "); + if (!mygets(file, (int) sizeof file)) return; + fd = open(file, LOAD_OPEN_MODE); + if (fd < 0) { + printf("Cannot open %s\n", file); + return; + } + if (read(fd, secbuf, SECSIZE) != SECSIZE || close(fd) != 0) { + printf("Read error\n"); + exit(1); + } + printf("Loaded from %s OK\n", file); + chk_table(); +} + + +save_to_file() +{ +/* Save to file */ + + char file[80]; + int fd; + + printf("Enter name of file to save to: "); + if (!mygets(file, (int) sizeof file)) return; + if(chk_table() != OK) printf("Saving anyway\n"); + fd = creat(file, SAVE_CREAT_MODE); +#ifdef DOS + if (fd < 0) { + printf("Cannot creat %s\n", file); + return; + } + close(fd); + fd = open(file, 0x8001); +#endif + if (fd < 0) + printf("Cannot open %s\n", file); + else if (write(fd, secbuf, SECSIZE) != SECSIZE || close(fd) != 0) + printf("Write error\n"); + else + printf("Saved to %s OK\n", file); +} + + +dpl_partitions(rawflag) +int rawflag; +{ +/* Display partition table */ + + char active[5]; + char basefootnote; + int cyl_mask; + int devnum; + char *format; + int i; + int i1; + char orderfootnote; + struct part_entry *pe; + struct part_entry *pe1; + int sec_mask; + char sizefootnote; + char type[10]; + + badbases = 0; + badsizes = 0; + badorders = 0; + if (rawflag) { + cyl_mask = 0; /* no contribution of cyl to sec */ + sec_mask = 0xff; + format = +"%2d %3d%c %4s %-9s x%02x %3d x%02x x%02x %3d x%02x %7ld%c%7ld %7ld%c\n"; + } else { + cyl_mask = CYL_MASK; + sec_mask = SEC_MASK; + format = +"%2d %3d%c %4s %-9s %4d %3d %3d %4d %3d %3d %7ld%c%7ld %7ld%c\n"; + } + printf( +" ----first---- -----last---- --------sectors-------\n" + ); + printf( +"Num Sorted Act Type Cyl Head Sec Cyl Head Sec Base Last Size\n" + ); + pe = (struct part_entry *) &secbuf[PART_TABLE_OFF]; + for (i = 1; i <= NR_PARTITIONS; i++, pe++) { + if (rawflag) { + sprintf(active, "0x%02x", pe->bootind); + sprintf(type, "0x%02x", pe->sysind); + } else { + sprintf(active, "%s", pe->bootind == ACTIVE_FLAG ? "A " : ""); + sprintf(type, "%s", systype(pe->sysind)); + } + + /* Prepare warnings about confusing setups from old versions. */ + basefootnote = orderfootnote = sizefootnote = ' '; + if (pe->sysind == MINIX_PART && pe->lowsec & 1) { + basefootnote = '+'; + ++badbases; + } + if (pe->size & 1) { + sizefootnote = '-'; + ++badsizes; + } + + /* Calculate the "device numbers" resulting from the misguided sorting + * in the wini drivers. The drivers use this conditional for + * swapping wn[j] > wn[j+1]: + * + * if ((wn[j].wn_low == 0 && wn[j+1].wn_low != 0) || + * (wn[j].wn_low > wn[j+1].wn_low && wn[j+1].wn_low != 0)) { + * + * which simplifies to: + * + * if (wn[j+1].wn_low != 0 && + * (wn[j].wn_low == 0 || wn[j].wn_low > wn[j+1].wn_low)) { + */ + devnum = 1; + for (i1 = 1, pe1 = (struct part_entry *) &secbuf[PART_TABLE_OFF]; + i1 <= NR_PARTITIONS; ++i1, ++pe1) + if (pe1->lowsec == 0 && pe->lowsec == 0 && pe1 < pe || + pe1->lowsec != 0 && + (pe->lowsec == 0 || pe->lowsec > pe1->lowsec)) + ++devnum; /* pe1 contents < pe contents */ + if (devnum != i) { + orderfootnote = '#'; + ++badorders; + } + + printf(format, + i, + devnum, + orderfootnote, + active, + type, + pe->start_cyl + ((pe->start_sec & cyl_mask) << CYL_SHIFT), + pe->start_head, + pe->start_sec & sec_mask, + pe->last_cyl + ((pe->last_sec & cyl_mask) << CYL_SHIFT), + pe->last_head, + pe->last_sec & sec_mask, + pe->lowsec, + basefootnote, + pe->lowsec + (pe->size == 0 ? 0 : pe->size - 1), + pe->size, + sizefootnote); + } +} + + +int chk_table() +{ +/* Check partition table */ + + int active; + unsigned char cylinder; + unsigned char head; + int i; + int i1; + int maxhead; + int maxsec; + struct part_entry *pe; + struct part_entry *pe1; + unsigned char sector; + int seenpart; + int status; + + active = 0; + maxhead = 0; + maxsec = 0; + pe = (struct part_entry *) &secbuf[PART_TABLE_OFF]; + seenpart = 0; + status = OK; + for (i = 1; i <= NR_PARTITIONS; i++, ++pe) { + if (pe->bootind == ACTIVE_FLAG) active++; + sec_to_hst(pe->lowsec, &head, §or, &cylinder); + if (pe->size == 0 && pe->lowsec == 0) sector = 0; + if (head != pe->start_head || sector != pe->start_sec || + cylinder != pe->start_cyl) { + printf("Inconsistent base in partition %d.\n", i); + printf("Suspect head and sector parameters.\n"); + status = ERR; + } + if (pe->size != 0 || pe->lowsec != 0) + sec_to_hst(pe->lowsec + pe->size - 1, &head, §or, &cylinder); + if (head != pe->last_head || sector != pe->last_sec || + cylinder != pe->last_cyl) { + printf("Inconsistent size in partition %d.\n", i); + printf("Suspect head and sector parameters.\n"); + status = ERR; + } + if (pe->size == 0) continue; + seenpart = 1; + for (i1 = i + 1, pe1 = pe + 1; i1 <= NR_PARTITIONS; ++i1, ++pe1) { + if (pe->lowsec >= pe1->lowsec && + pe->lowsec < pe1->lowsec + pe1->size || + pe->lowsec + pe->size - 1 >= pe1->lowsec && + pe->lowsec + pe->size - 1 < pe1->lowsec + pe1->size) + { + printf("Overlap between partitions %d and %d\n", + i, i1); + status = ERR; + } + } + if (pe->lowsec + pe->size < pe->lowsec) { + printf("Overflow from preposterous size in partition %d.\n", + i); + status = ERR; + } + if (maxhead < pe->start_head) maxhead = pe->start_head; + if (maxhead < pe->last_head) maxhead = pe->last_head; + if (maxsec < (pe->start_sec & SEC_MASK)) + maxsec = (pe->start_sec & SEC_MASK); + if (maxsec < (pe->last_sec & SEC_MASK)) + maxsec = (pe->last_sec & SEC_MASK); + } + if (seenpart) { + if (maxhead + 1 != nhead || maxsec != nsec) { + printf( + "Disk appears to have mis-specified number of heads or sectors.\n"); + printf("Try fdisk -h%d -s%d %s instead of\n", + maxhead + 1, maxsec, devname); + printf(" fdisk -h%d -s%d %s\n", nhead, nsec, devname); + seenpart = 0; + } + } else { + printf( + "Empty table - skipping test on number of heads and sectors.\n"); + printf("Assuming %d heads and %d sectors.\n", nhead, nsec); + } + if (!seenpart) printf("Do not write the table if you are not sure!.\n"); + if (active > 1) { + printf("%d active partitions\n", active); + status = ERR; + } + return(status); +} + +sec_to_hst(logsec, hd, sec, cyl) +long logsec; +unsigned char *hd, *sec, *cyl; +{ +/* Convert a logical sector number to head / sector / cylinder */ + + int bigcyl; + + bigcyl = logsec / (nhead * nsec); + *sec = (logsec % nsec) + 1 + ((bigcyl >> CYL_SHIFT) & CYL_MASK); + *cyl = bigcyl; + *hd = (logsec % (nhead * nsec)) / nsec; +} + +mark_partition(pe) +struct part_entry *pe; +{ +/* Mark a partition as being of type MINIX. */ + + if (pe != NULL) { + pe->sysind = MINIX_PART; + printf("Partition type is now MINIX\n"); + } +} + +change_partition(entry) +struct part_entry *entry; +{ +/* Get partition info : first & last cylinder */ + + int first, last; + long low, high; + int ch; + + if (entry == NULL) return; + while (1) { + if (!get_an_int("\tEnter first cylinder (an integer >= 0): ", &first)) + return; + if (first >= 0) break; + printf("\t\tThat looks like %d which is negative\n", first); + } + while (1) { + if (!get_an_int( + "\tEnter last cylinder (an integer >= the first cylinder): ", &last)) + return; + if (last >= first) break; + printf("\t\tThat looks like %d which is too small\n", last); + } + if (first == 0 && last == 0) { + entry->bootind = 0; + entry->start_head = 0; + entry->start_sec = 0; + entry->start_cyl = 0; + entry->sysind = NO_PART; + entry->last_head = 0; + entry->last_sec = 0; + entry->last_cyl = 0; + entry->lowsec = 0; + entry->size = 0; + printf("Partition deleted\n"); + return; + } + low = first & 0xffff; + low = low * nsec * nhead; + if (low == 0) low = 1; /* sec0 is master boot record */ + high = last & 0xffff; + high = (high + 1) * nsec * nhead - 1; + entry->lowsec = low; + entry->size = high - low + 1; + if (entry->size & 1) { + /* Adjust size to even since Minix works with blocks of 2 sectors. */ + --high; + --entry->size; + printf("Size reduced by 1 to make it even\n"); + } + sec_to_hst(low, &entry->start_head, &entry->start_sec, &entry->start_cyl); + sec_to_hst(high, &entry->last_head, &entry->last_sec, &entry->last_cyl); + printf("Base of partition changed to %ld, size changed to %ld\n", + entry->lowsec, entry->size); + + /* Accept the MINIX partition type. Usually ignore foreign types, so this + * fdisk can be used on foreign partitions. Don't allow NO_PART, because + * many DOS fdisks crash on it. + */ + if (entry->sysind == NO_PART) { + entry->sysind = MINIX_PART; + printf("Partition type changed from None to MINIX\n"); + } else if (entry->sysind == MINIX_PART) + printf("Leaving partition type as MINIX\n"); + else while (1) { + printf("\tChange partition type from %s to MINIX? (y/n) ", + systype(entry->sysind)); + ch = get_a_char(); + if (ch == 0 || ch == 'n') { + printf("Leaving partition type as %s\n", + systype(entry->sysind)); + break; + } else if (ch == 'y') { + entry->sysind = MINIX_PART; + printf("Partition type changed from %s to MINIX\n", + systype(entry->sysind)); + break; + } + } + + if (entry->bootind == ACTIVE_FLAG) + printf("Leaving partition active\n"); + else while (1) { + printf("\tChange partition to active? (y/n) "); + ch = get_a_char(); + if (ch == 0 || ch == 'n') { + printf("Leaving partition inactive\n"); + break; + } else if (ch == 'y') { + toggle_active(entry); + break; + } + } +} + +get_a_char() +{ +/* Read 1 character and discard rest of line */ + + char buf[80]; + int ch; + + if (!mygets(buf, (int) sizeof buf)) return(0); + return(*buf); +} + +print_menu() +{ + printf("Type a command letter, then a carriage return:\n"); + printf(" + - explain any footnotes (+, -, #)\n"); + printf(" a - toggle an active flag\n"); + printf(" B - adjust a base sector\n"); + printf(" c - change a partition\n"); + printf(" l - load boot block (including partition table) from a file\n"); + printf(" m - mark a partition as a MINIX partition\n"); + printf(" n - mark a partition as a non-MINIX partition\n"); + printf(" p - print raw partition table\n"); + printf(" q - quit without making any changes\n"); + printf(" S - adjust a size (by changing the last sector)\n"); + printf(" s - save boot block (including partition table) on a file\n"); + printf(" t - print known partition types\n"); + printf(" v - verify partition table\n"); + if (readonly) + printf(" w - write (disabled)\n"); + else + printf(" w - write changed partition table back to disk and exit\n"); +} + + +/* Here are the DOS routines for reading and writing the boot sector. */ + +#ifdef DOS + +union REGS regs; +struct SREGS sregs; +int drivenum; + +getboot(buffer) +char *buffer; +{ +/* Read boot sector */ + + segread(&sregs); /* get ds */ + + if (devname[1] != ':') { + printf("Invalid drive %s\n", devname); + exit(1); + } + if (*devname >= 'a') *devname += 'A' - 'a'; + drivenum = (*devname - 'C') & 0xff; + if (drivenum < 0 || drivenum > 7) { + printf("Funny drive number %d\n", drivenum); + exit(1); + } + regs.x.ax = 0x201; /* read 1 sectors */ + regs.h.ch = 0; /* cylinder */ + regs.h.cl = 1; /* first sector = 1 */ + regs.h.dh = 0; /* head = 0 */ + regs.h.dl = 0x80 + drivenum; /* drive = 0 */ + sregs.es = sregs.ds; /* buffer address */ + regs.x.bx = (int) buffer; + + int86x(0x13, ®s, ®s, &sregs); + if (regs.x.cflag) { + printf("Cannot read boot sector\n"); + exit(1); + } +} + + +putboot(buffer) +char *buffer; +{ +/* Write boot sector */ + + regs.x.ax = 0x301; /* read 1 sectors */ + regs.h.ch = 0; /* cylinder */ + regs.h.cl = 1; /* first sector = 1 */ + regs.h.dh = 0; /* head = 0 */ + regs.h.dl = 0x80 + drivenum; /* drive = 0 */ + sregs.es = sregs.ds; /* buffer address */ + regs.x.bx = (int) buffer; + + int86x(0x13, ®s, ®s, &sregs); + if (regs.x.cflag) { + printf("Cannot write boot sector\n"); + exit(1); + } +} + +#endif + +void adj_base(pe) +struct part_entry *pe; +{ +/* Adjust base sector of partition, usually to make it even. */ + + int adj; + + if (pe == NULL) return; + while (1) { + + if (!get_an_int("\tEnter adjustment to base (an integer): ", &adj)) + return; + if (pe->lowsec + adj < 1) + printf( + "\t\tThat would make the base %d and too small\n", pe->lowsec + adj); + else if (pe->size - adj < 1) + printf( + "\t\tThat would make the size %d and too small\n", pe->size - adj); + else + break; + } + pe->lowsec += adj; + pe->size -= adj; + sec_to_hst(pe->lowsec, &pe->start_head, &pe->start_sec, &pe->start_cyl); + printf("Base of partition adjusted to %ld, size adjusted to %ld\n", + pe->lowsec, pe->size); +} + +void adj_size(pe) +struct part_entry *pe; +{ +/* Adjust size of partition by reducing high sector. */ + + int adj; + + if (pe == NULL) return; + while (1) { + if (!get_an_int("\tEnter adjustment to size (an integer): ", &adj)) + return; + if (pe->size + adj >= 1) break; + printf("\t\tThat would make the size %d and too small \n", + pe->size + adj); + } + pe->size += adj; + sec_to_hst(pe->lowsec + pe->size - 1, + &pe->last_head, &pe->last_sec, &pe->last_cyl); + printf("Size of partition adjusted to %ld\n", pe->size); +} + +struct part_entry *ask_partition() +{ +/* Ask for a valid partition number and return its entry. */ + + int num; + + while (1) { + + if (!get_an_int("Enter partition number (1 to 4): ", &num)) + return(NULL); + if (num >= 1 && num <= NR_PARTITIONS) break; + printf("\tThat does not look like 1 to 4\n"); + } + printf("Partition %d\n", num); + return((struct part_entry *) &secbuf[PART_TABLE_OFF] + (num - 1)); +} + +void footnotes() +{ +/* Explain the footnotes. */ + + if (badbases != 0) { + printf( +"+ The old Minix wini drivers (before V1.5) discarded odd base sectors.\n"); + printf( +" This causes some old (Minix) file systems to be offset by 1 sector.\n"); + printf( +" To use these with the new drivers, increase the base by 1 using 'B'.\n"); + } + + if (badsizes != 0) { + if (badbases != 0) putchar('\n'); + printf( +"- Minix cannot access the last sector on an odd-sized partition. This\n"); + printf( +" causes trouble for programs like dosread. This program will by default\n"); + printf( +" only create partitions with even sizes. If possible, the current odd\n"); + printf( +" sizes should be decreased by 1 using 'S'. This is safe for all Minix\n"); + printf( +" partitions, and may be safe for other partitions which are about to be\n"); + printf( +" reformatted.\n"); + } + + if (badorders!= 0 ) { + if (badbases != 0 || badsizes != 0) putchar('\n'); + printf( +"# The partitions are in a funny order. This is normal if they were created\n"); + printf( +" by DOS fdisks prior to DOS 3.3. The Minix wini drivers further confuse\n"); + printf( +" the order by sorting the partitions on their base. Be careful if the\n"); + printf( +" device numbers of unchanged partitions have changed.\n"); + } +} + +int get_an_int(prompt, intptr) +char *prompt; +int *intptr; +{ +/* Read an int from the start of line of stdin, discard rest of line. */ + + char buf[80]; + + while (1) { + printf("%s", prompt); + if (!mygets(buf, (int) sizeof buf)) return(0); + if ((sscanf(buf, "%d", intptr)) == 1) return(1); + printf("\t\tThat does not look like an integer\n"); + } +} + +void list_part_types() +{ +/* Print all known partition types. */ + + int column; + int type; + + for (column = 0, type = 0; type < 0x100; ++type) + if (strcmp(systype(type), "Unknown") != 0) { + printf("0x%02x: %-9s", type, systype(type)); + column += 16; + if (column < 80) + putchar(' '); + else { + putchar('\n'); + column = 0; + } + } + if (column != 0) putchar('\n'); +} + +void mark_npartition(pe) +struct part_entry *pe; +{ +/* Mark a partition with arbitrary type. */ + + char buf[80]; + unsigned type; + + if (pe == NULL) return; + printf("\nKnown partition types are:\n\n"); + list_part_types(); + while (1) { + printf("\nEnter partition type (in 2-digit hex): "); + if (!mygets(buf, (int) sizeof buf)) return; + if (sscanf(buf, "%x", &type) != 1) + printf("Invalid hex number\n"); + else if (type >= 0x100) + printf("Hex number too large\n"); + else + break; + } + pe->sysind = type; + printf("Partition type changed to 0x%02x (%s)\n", type, systype(type)); +} + +int mygets(buf, length) +char *buf; +int length; /* as for fgets(), but must be >= 2 */ +{ +/* Get a non-empty line of maximum length 'length'. */ + + while (1) { + fflush(stdout); + if (fgets(buf, length, stdin) == NULL) { + putchar('\n'); + return(0); + } + if (strrchr(buf, '\n') != NULL) *strrchr(buf, '\n') = 0; + if (*buf != 0) return(1); + printf("Use the EOF character to create a null line.\n"); + printf("Otherwise, please type something before the newline: "); + } +} + +char *systype(type) +int type; +{ +/* Convert system indicator into system name. */ +/* asw 01.03.95: added types based on info in kjb's part.c and output + * from Linux (1.0.8) fdisk. Note comments here, there are disagreements. +*/ + switch(type) { + case NO_PART: + return("None"); + case 1: return("DOS-12"); + case 2: return("XENIX"); + case 3: return("XENIX usr"); + case 4: return("DOS-16"); + case 5: return("DOS-EXT"); + case 6: return("DOS-BIG"); + case 7: return("HPFS"); + case 8: return("AIX"); + case 9: return("COHERENT"); /* LINUX says AIX bootable */ + case 0x0a: return("OS/2"); /* LINUX says OPUS */ + case 0x10: return("OPUS"); + case 0x40: return("VENIX286"); + case 0x51: return("NOVELL?"); + case 0x52: return("MICROPORT"); + case 0x63: return("386/IX"); /*LINUX calls this GNU HURD */ + case 0x64: return("NOVELL286"); + case 0x65: return("NOVELL386"); + case 0x75: return("PC/IX"); + case 0x80: return("MINIX old"); + case 0x81: return("MINIX"); + case 0x82: return("LINUXswap"); + case 0x83: return("LINUX"); + case 0x93: return("AMOEBA"); + case 0x94: return("AMOEBAbad"); + case 0xa5: return("386BSD"); + case 0xb7: return("BSDI"); + case 0xb8: return("BSDIswap"); + case 0xc7: return("Syrinx"); + case 0xDB: return("CP/M"); + case 0xe1: return("DOS acc"); + case 0xe3: return("DOS r/o"); + case 0xf2: return("DOS 2ary"); + case 0xFF: return("Badblocks"); + default: return("Unknown"); + } +} + +void toggle_active(pe) +struct part_entry *pe; +{ +/* Toggle active flag of a partition. */ + + if (pe == NULL) return; + pe->bootind = (pe->bootind == ACTIVE_FLAG) ? 0 : ACTIVE_FLAG; + printf("Partition changed to %sactive\n", pe->bootind ? "" : "in"); +} + +void usage() +{ +/* Print usage message and exit. */ + + printf("Usage: fdisk [-hheads] [-ssectors] [device]\n"); + exit(1); +} + diff --git a/commands/ibm/format.c b/commands/ibm/format.c new file mode 100755 index 000000000..3b20dfca2 --- /dev/null +++ b/commands/ibm/format.c @@ -0,0 +1,422 @@ +/* format 1.1 - format PC floppy disk Author: Kees J. Bot + * 5 Mar 1994 + */ +#define nil 0 +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <time.h> +#include <errno.h> +#include <limits.h> +#include <ibm/diskparm.h> +#include <minix/minlib.h> + +/* Constants. */ +#define SECTOR_SIZE 512 +#define NR_HEADS 2 +#define MAX_SECTORS 18 /* 1.44Mb is the largest. */ + +/* Name error in <ibm/diskparm.h>, left over from the days floppies were + * single sided. + */ +#define sectors_per_track sectors_per_cylinder + +/* From floppy device number to drive/type/format-bit and back. See fd(4). */ +#define isfloppy(dev) (((dev) & 0xFF00) == 0x0200) +#define fl_drive(dev) (((dev) & 0x0003) >> 0) +#define fl_type(dev) (((dev) & 0x007C) >> 2) +#define fl_format(dev) (((dev) & 0x0080) >> 7) +#define fl_makedev(drive, type, fmt) \ + ((dev_t) (0x0200 | ((fmt) << 7) | ((type) << 2) | ((drive) << 0))) + +/* Recognize floppy types. */ +#define NR_TYPES 7 /* # non-auto types */ +#define isflauto(type) ((type) == 0) +#define isfltyped(type) ((unsigned) ((type) - 1) < NR_TYPES) +#define isflpart(type) ((unsigned) (type) >= 28) + +/* Formatting parameters per type. (Most of these parameters have no use + * for formatting, disk_parameter_s probably matches a BIOS parameter table.) + */ +typedef struct disk_parameter_s fmt_params_t; + +typedef struct type_parameters { + unsigned media_size; + unsigned drive_size; + fmt_params_t fmt_params; +} type_parameters_t; + +#define DC 0 /* Don't care. */ + +type_parameters_t parameters[NR_TYPES] = { + /* mediasize s1 off sec/cyl dlen fill start */ + /* drivesize s2 sizecode gap fmtgap settle */ + /* pc */ { 360, 360, { DC, DC, DC, 2, 9, DC, DC, 0x50, 0xF6, DC, DC }}, + /* at */ { 1200, 1200, { DC, DC, DC, 2, 15, DC, DC, 0x54, 0xF6, DC, DC }}, + /* qd */ { 360, 720, { DC, DC, DC, 2, 9, DC, DC, 0x50, 0xF6, DC, DC }}, + /* ps */ { 720, 720, { DC, DC, DC, 2, 9, DC, DC, 0x50, 0xF6, DC, DC }}, + /* pat */{ 360, 1200, { DC, DC, DC, 2, 9, DC, DC, 0x50, 0xF6, DC, DC }}, + /* qh */ { 720, 1200, { DC, DC, DC, 2, 9, DC, DC, 0x50, 0xF6, DC, DC }}, + /* PS */ { 1440, 1440, { DC, DC, DC, 2, 18, DC, DC, 0x54, 0xF6, DC, DC }}, +}; + +/* Per sector ID to be sent to the controller by the driver. */ +typedef struct sector_id { + unsigned char cyl; + unsigned char head; + unsigned char sector; + unsigned char sector_size_code; +} sector_id_t; + +/* Data to be "written" to the driver to format a track. (lseek to the track + * first.) The first sector contains sector ID's, the second format params. + */ + +typedef struct track_data { + sector_id_t sec_ids[SECTOR_SIZE / sizeof(sector_id_t)]; + fmt_params_t fmt_params; + char padding[SECTOR_SIZE - sizeof(fmt_params_t)]; +} track_data_t; + +void report(const char *label) +{ + fprintf(stderr, "format: %s: %s\n", label, strerror(errno)); +} + +void fatal(const char *label) +{ + report(label); + exit(1); +} + +void format_track(int ffd, unsigned type, unsigned cyl, unsigned head) +/* Format a single track on a floppy. */ +{ + type_parameters_t *tparams= ¶meters[type - 1]; + track_data_t track_data; + off_t track_pos; + unsigned sector; + unsigned nr_sectors= tparams->fmt_params.sectors_per_track; + sector_id_t *sid; + + memset(&track_data, 0, sizeof(track_data)); + + /* Set the sector id's. (Note that sectors count from 1.) */ + for (sector= 0; sector <= nr_sectors; sector++) { + sid= &track_data.sec_ids[sector]; + + sid->cyl= cyl; + sid->head= head; + sid->sector= sector + 1; + sid->sector_size_code= tparams->fmt_params.sector_size_code; + } + + /* Format parameters. */ + track_data.fmt_params= tparams->fmt_params; + + /* Seek to the right track. */ + track_pos= (off_t) (cyl * NR_HEADS + head) * nr_sectors * SECTOR_SIZE; + if (lseek(ffd, track_pos, SEEK_SET) == -1) { + fprintf(stderr, + "format: seeking to cyl %u, head %u (pos %ld) failed: %s\n", + cyl, head, track_pos, strerror(errno)); + exit(1); + } + + /* Format track. */ + if (write(ffd, &track_data, sizeof(track_data)) < 0) { + fprintf(stderr, + "format: formatting cyl %d, head %d failed: %s\n", + cyl, head, strerror(errno)); + exit(1); + } +} + +void verify_track(int vfd, unsigned type, unsigned cyl, unsigned head) +/* Verify a track by reading it. On error read sector by sector. */ +{ + type_parameters_t *tparams= ¶meters[type - 1]; + off_t track_pos; + unsigned sector; + unsigned nr_sectors= tparams->fmt_params.sectors_per_track; + size_t track_bytes; + static char buf[MAX_SECTORS * SECTOR_SIZE]; + static unsigned bad_count; + + /* Seek to the right track. */ + track_pos= (off_t) (cyl * NR_HEADS + head) * nr_sectors * SECTOR_SIZE; + if (lseek(vfd, track_pos, SEEK_SET) == -1) { + fprintf(stderr, + "format: seeking to cyl %u, head %u (pos %ld) failed: %s\n", + cyl, head, track_pos, strerror(errno)); + exit(1); + } + + /* Read the track whole. */ + track_bytes= nr_sectors * SECTOR_SIZE; + if (read(vfd, buf, track_bytes) == track_bytes) return; + + /* An error occurred, retry sector by sector. */ + for (sector= 0; sector < nr_sectors; sector++) { + if (lseek(vfd, track_pos, SEEK_SET) == -1) { + fprintf(stderr, + "format: seeking to cyl %u, head %u, sector %u (pos %ld) failed: %s\n", + cyl, head, sector, track_pos, strerror(errno)); + exit(1); + } + + switch (read(vfd, buf, SECTOR_SIZE)) { + case -1: + fprintf(stderr, + "format: bad sector at cyl %u, head %u, sector %u (pos %ld)\n", + cyl, head, sector, track_pos); + bad_count++; + break; + case SECTOR_SIZE: + /* Fine. */ + break; + default: + fprintf(stderr, "format: short read at pos %ld\n", + track_pos); + bad_count++; + } + track_pos+= SECTOR_SIZE; + if (bad_count >= nr_sectors) { + fprintf(stderr, "format: too many bad sectors, floppy unusable\n"); + exit(1); + } + } +} + +void format_device(unsigned drive, unsigned type, int verify) +{ + int ffd, vfd; + char *fmt_dev, *ver_dev; + struct stat st; + unsigned cyl, head; + unsigned nr_cyls; + type_parameters_t *tparams= ¶meters[type - 1]; + int verbose= isatty(1); + + fmt_dev= tmpnam(nil); + + if (mknod(fmt_dev, S_IFCHR | 0700, fl_makedev(drive, type, 1)) < 0) { + fprintf(stderr, "format: making format device failed: %s\n", + strerror(errno)); + exit(1); + } + + if ((ffd= open(fmt_dev, O_WRONLY)) < 0 || fstat(ffd, &st) < 0) { + report(fmt_dev); + (void) unlink(fmt_dev); + exit(1); + } + + (void) unlink(fmt_dev); + + if (st.st_rdev != fl_makedev(drive, type, 1)) { + /* Someone is trying to trick me. */ + exit(1); + } + + if (verify) { + ver_dev= tmpnam(nil); + + if (mknod(ver_dev, S_IFCHR | 0700, fl_makedev(drive, type, 0)) + < 0) { + fprintf(stderr, + "format: making verify device failed: %s\n", + strerror(errno)); + exit(1); + } + + if ((vfd= open(ver_dev, O_RDONLY)) < 0) { + report(ver_dev); + (void) unlink(ver_dev); + exit(1); + } + + (void) unlink(ver_dev); + } + + nr_cyls= tparams->media_size * (1024 / SECTOR_SIZE) / NR_HEADS + / tparams->fmt_params.sectors_per_track; + + if (verbose) { + printf("Formatting a %uk diskette in a %uk drive\n", + tparams->media_size, tparams->drive_size); + } + + for (cyl= 0; cyl < nr_cyls; cyl++) { + for (head= 0; head < NR_HEADS; head++) { + if (verbose) { + printf(" Cyl. %2u, Head %u\r", cyl, head); + fflush(stdout); + } +#if __minix_vmd + /* After formatting a track we are too late to format + * the next track. So we can sleep at most 1/6 sec to + * allow the above printf to get displayed before we + * lock Minix into the floppy driver again. + */ + usleep(50000); /* 1/20 sec will do. */ +#endif + format_track(ffd, type, cyl, head); + if (verify) verify_track(vfd, type, cyl, head); + } + } + if (verbose) fputc('\n', stdout); +} + +void usage(void) +{ + fprintf(stderr, + "Usage: format [-v] <device> [<media size> [<drive size>]]\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + char *device; + unsigned drive; + unsigned type; + unsigned media_size; + unsigned drive_size; + int verify= 0; + struct stat st0, st; + FILE *mfp; + char special[PATH_MAX + 1], mounted_on[PATH_MAX + 1]; + char version[10], rw_flag[10]; + + /* Option -v. */ + while (argc > 1 && argv[1][0] == '-') { + char *p; + + for (p= argv[1]; *p == '-' || *p == 'v'; p++) { + if (*p == 'v') verify= 1; + } + if (*p != 0) usage(); + argc--; + argv++; + if (strcmp(argv[0], "--") == 0) break; + } + + if (argc < 2 || argc > 4) usage(); + + /* Check if the caller has read-write permission. Use the access() + * call to check with the real uid & gid. This program is usually + * set-uid root. + */ + device= argv[1]; + if (stat(device, &st0) < 0 + || access(device, R_OK|W_OK) < 0 + || stat(device, &st) < 0 + || (errno= EACCES, 0) /* set errno for following tests */ + || st.st_dev != st0.st_dev + || st.st_ino != st0.st_ino + ) { + fatal(device); + } + + if ((!S_ISBLK(st.st_mode) && !S_ISCHR(st.st_mode)) + || !isfloppy(st.st_rdev)) { + fprintf(stderr, "format: %s: not a floppy device\n", device); + exit(1); + } + + drive= fl_drive(st.st_rdev); + type= fl_type(st.st_rdev); + + /* The drive should not be mounted. */ + if (load_mtab("mkfs") < 0) return; + + while (get_mtab_entry(special, mounted_on, version, rw_flag) == 0) { + if (stat(special, &st) >= 0 && isfloppy(st.st_rdev) + && fl_drive(st.st_rdev) == drive) { + fprintf(stderr, "format: %s is mounted on %s\n", + device, mounted_on); + exit(1); + } + } + + if (isflauto(type)) { + /* Auto type 0 requires size(s). */ + unsigned long lmedia, ldrive; + char *end; + + if (argc < 3) { + fprintf(stderr, + "format: no size specified for auto floppy device %s\n", + device); + usage(); + } + + lmedia= strtoul(argv[2], &end, 10); + if (end == argv[2] || *end != 0 || lmedia > 20 * 1024) + usage(); + + if (argc == 4) { + ldrive= strtoul(argv[3], &end, 10); + if (end == argv[3] || *end != 0 || ldrive > 20 * 1024) + usage(); + } else { + ldrive= lmedia; + } + + /* Silently correct wrong ordered sizes. */ + if (lmedia > ldrive) { + media_size= ldrive; + drive_size= lmedia; + } else { + media_size= lmedia; + drive_size= ldrive; + } + + /* A 1.44M drive can do 720k diskettes with no extra tricks. + * Diddle with the 720k params so it is found. + */ + if (media_size == 720 && drive_size == 1440) + parameters[4 - 1].drive_size= 1440; + + /* Translate the auto type to a known type. */ + for (type= 1; type <= NR_TYPES; type++) { + if (parameters[type - 1].media_size == media_size + && parameters[type - 1].drive_size == drive_size + ) break; + } + + if (!isfltyped(type)) { + fprintf(stderr, + "format: can't format a %uk floppy in a %uk drive\n", + media_size, drive_size); + exit(1); + } + } else + if (isfltyped(type)) { + /* No sizes needed for a non-auto type. */ + + if (argc > 2) { + fprintf(stderr, + "format: no sizes need to be specified for non-auto floppy device %s\n", + device); + usage(); + } + } else + if (isflpart(type)) { + fprintf(stderr, + "format: floppy partition %s can't be formatted\n", + device); + exit(1); + } else { + fprintf(stderr, + "format: %s: can't format strange type %d\n", + device, type); + } + + format_device(drive, type, verify); + exit(0); +} diff --git a/commands/ibm/loadfont.c b/commands/ibm/loadfont.c new file mode 100755 index 000000000..45538e021 --- /dev/null +++ b/commands/ibm/loadfont.c @@ -0,0 +1,122 @@ +/* loadfont.c - Load custom font into EGA, VGA video card + * + * Author: Hrvoje Stipetic (hs@hck.hr) Jun-1995. + * + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <termios.h> +#include <sys/ioctl.h> + +void tell(char *s) +{ + write(2, s, strlen(s)); +} + +char *itoa(unsigned i) +{ + static char a[3*sizeof(int)]; + char *p = a+sizeof(a)-1; + + do { + *--p = '0' + i%10; + } while ((i /= 10) > 0); + + return p; +} + +void report(char *say) +{ + int err = errno; + tell("loadfont: "); + if (say != NULL) { + tell(say); + tell(": "); + } + tell(strerror(err)); + tell("\n"); +} + +void usage(void) +{ + tell("Usage: loadfont fontfile\n"); + exit(1); +} + + +int main(int argc, char *argv[]) +{ + static u8_t font[256][32]; + static u8_t font_file[256 * (16+14+8) + 1]; + u8_t *ff; + int fd, size, tsize, ch, ln; + struct winsize ws; + + + if (argc != 2) + usage(); + + if ((fd = open(argv[1], O_RDONLY)) < 0) { + report(argv[1]); + exit(1); + } + + switch (read(fd, font_file, sizeof(font_file))) { + case 256 * 8: + size = 8; + break; + case 256 * 14: + size = 14; + break; + case 256 * 16: + size = 16; + break; + case 256 * (16+14+8): + size = 0; + break; + case -1: + report(argv[1]); + exit(1); + default: + tell("loadfont: "); + tell(argv[1]); + tell(": fontfile is not an 8x8, 8x14, 8x16, or compound font\n"); + exit(1); + } + close(fd); + + if (ioctl(0, TIOCGWINSZ, &ws) < 0 || (errno= ENOTTY, ws.ws_row == 0)) { + report(NULL); + exit(1); + } + tsize = ws.ws_ypixel / ws.ws_row; + + if (size == 0) { + if (tsize >= 16) { + ff = font_file + 256 * (0); + } else + if (tsize >= 14) { + ff = font_file + 256 * (16); + } else { + ff = font_file + 256 * (16 + 14); + } + size = tsize; + } else { + ff = font_file; + } + + for (ch = 0; ch < 256; ch++) { + for (ln = 0; ln < size; ln++) font[ch][ln] = ff[ch * size + ln]; + } + + if (ioctl(0, TIOCSFON, font) < 0) { + report(NULL); + exit(1); + } + exit(0); +} diff --git a/commands/ibm/loadkeys.c b/commands/ibm/loadkeys.c new file mode 100755 index 000000000..f1f0646b1 --- /dev/null +++ b/commands/ibm/loadkeys.c @@ -0,0 +1,91 @@ +/* loadkeys - load national keyboard map Author: Marcus Hampel + */ +#include <sys/types.h> +#include <sys/ioctl.h> +#include <minix/keymap.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#if __minix_vmd +#define KBD_DEVICE "/dev/kbd" +#else +#define KBD_DEVICE "/dev/console" +#endif + +u16_t keymap[NR_SCAN_CODES * MAP_COLS]; +u8_t comprmap[4 + NR_SCAN_CODES * MAP_COLS * 9/8 * 2 + 1]; + + +void tell(char *s) +{ + write(2, s, strlen(s)); +} + + +void fatal(char *say) +{ + int err = errno; + tell("loadkeys: "); + if (say != NULL) { + tell(say); + tell(": "); + } + tell(strerror(err)); + tell("\n"); + exit(1); +} + + +void usage(void) +{ + tell("Usage: loadkeys mapfile\n"); + exit(1); +} + + +int main(int argc, char *argv[]) +{ + u8_t *cm; + u16_t *km; + int fd, n, fb; + + if (argc != 2) + usage(); + + if ((fd = open(argv[1], O_RDONLY)) < 0) fatal(argv[1]); + + if (read(fd, comprmap, sizeof(comprmap)) < 0) fatal(argv[1]); + + if (memcmp(comprmap, KEY_MAGIC, 4) != 0) { + tell("loadkeys: "); + tell(argv[1]); + tell(": not a keymap file\n"); + exit(1); + } + close(fd); + + /* Decompress the keymap data. */ + cm = comprmap + 4; + n = 8; + for (km = keymap; km < keymap + NR_SCAN_CODES * MAP_COLS; km++) { + if (n == 8) { + /* Need a new flag byte. */ + fb = *cm++; + n = 0; + } + *km = *cm++; /* Low byte. */ + if (fb & (1 << n)) { + *km |= (*cm++ << 8); /* One of the few special keys. */ + } + n++; + } + + if ((fd = open(KBD_DEVICE, O_WRONLY)) < 0) fatal(KBD_DEVICE); + + if (ioctl(fd, KIOCSMAP, keymap) < 0) fatal(KBD_DEVICE); + + return 0; +} diff --git a/commands/ibm/mixer.c b/commands/ibm/mixer.c new file mode 100755 index 000000000..a02699724 --- /dev/null +++ b/commands/ibm/mixer.c @@ -0,0 +1,658 @@ +/* + * mixer + * + * Michel R. Prevenier. + */ + +#include <sys/types.h> +#include <errno.h> +#include <curses.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <minix/sound.h> + +#define CURS_CTRL '\033' +#define ESCAPE 27 +#define UP 'A' +#define DOWN 'B' +#define LEFT 'D' +#define RIGHT 'C' +#define SPACE ' ' + + +_PROTOTYPE ( int main, (int arg, char **argv)); +_PROTOTYPE ( void usage, (void)); +_PROTOTYPE ( void non_interactive, (void)); +_PROTOTYPE ( void setup_screen, (void)); +_PROTOTYPE ( int read_settings, (void)); +_PROTOTYPE ( int write_settings, (void)); +_PROTOTYPE ( void rdwr_levels, (int flag)); +_PROTOTYPE ( void rdwr_inputs, (int flag)); +_PROTOTYPE ( void rdwr_outputs, (int flag)); +_PROTOTYPE ( void create_slider, (int x, int y, enum Device device)); +_PROTOTYPE ( void show_inputs, (int x, int y)); +_PROTOTYPE ( void show_outputs, (int x, int y)); +_PROTOTYPE ( char *d_name, (enum Device device, char *name)); +_PROTOTYPE ( void user_interface, (void)); +_PROTOTYPE ( void terminate, (int s)); + +WINDOW *main_win; +int old_stdin; +int fd; +char name[9]; +char *file_name; +struct volume_level levels[9]; +struct inout_ctrl inputs_left[9]; +struct inout_ctrl inputs_right[9]; +struct inout_ctrl outputs[9]; + + +void usage() +{ + fprintf(stderr, "Usage: mixer [-r]\n"); + exit(-1); +} + + +void terminate(s) +int s; +{ + /* Restore terminal parameters and exit */ + + (void) fcntl(0,F_SETFL,old_stdin); + move(23, 0); + refresh(); + resetty(); + endwin(); + exit(1); +} + + +int write_settings() +{ + /* Write the current mixer settings to $HOME/.mixer */ + + int fd; + + if ((fd = creat(file_name, 0x124)) > 0) + { + write(fd, levels, sizeof(levels)); + write(fd, inputs_left, sizeof(inputs_left)); + write(fd, inputs_right, sizeof(inputs_right)); + write(fd, outputs, sizeof(outputs)); + close(fd); + return 1; + } + + return 0; +} + + +int read_settings() +{ + /* Restore mixer settings saved in $HOME/.mixer */ + + int fd; + + if ((fd = open(file_name, O_RDONLY)) > 0) + { + read(fd, levels, sizeof(levels)); + read(fd, inputs_left, sizeof(inputs_left)); + read(fd, inputs_right, sizeof(inputs_right)); + read(fd, outputs, sizeof(outputs)); + close(fd); + rdwr_levels(1); + rdwr_outputs(1); + rdwr_inputs(1); + return 1; + } + return 0; +} + + +void rdwr_levels(flag) +int flag; /* 0 = read, 1 = write */ +{ + /* Get or set mixer settings */ + + int i; + int cmd; + + cmd = (flag == 0 ? MIXIOGETVOLUME : MIXIOSETVOLUME); + + for(i = Master; i <= Bass; i++) + (void) (ioctl(fd, cmd, &levels[i])); +} + + +void rdwr_inputs(flag) +int flag; /* 0 = read, 1 = write */ +{ + /* Get or set input settings */ + + int i; + int cmd_left, cmd_right; + + cmd_left = (flag == 0 ? MIXIOGETINPUTLEFT : MIXIOSETINPUTLEFT); + cmd_right = (flag == 0 ? MIXIOGETINPUTRIGHT : MIXIOSETINPUTRIGHT); + + for(i = Fm; i <= Mic; i++) + { + (void) (ioctl(fd, cmd_left, &inputs_left[i])); + (void) (ioctl(fd, cmd_right, &inputs_right[i])); + } +} + + +void rdwr_outputs(flag) +int flag; /* 0 = read, 1 = write */ +{ + /* Get or set output settings */ + + int i; + int cmd; + + cmd = (flag == 0 ? MIXIOGETOUTPUT : MIXIOSETOUTPUT); + + for(i = Cd; i <= Mic; i++) + (void) (ioctl(fd, cmd, &outputs[i])); +} + + +int main(argc, argv) +int argc; +char **argv; + +{ + int i; + char *home_ptr; + int fd2; + + /* Open mixer */ + if ((fd = open("/dev/mixer",O_RDONLY)) < 0) + { + fprintf(stderr, "Cannot open /dev/mixer\n"); + exit(-1); + } + + /* Get user's home directory and construct the $HOME/.mixer + * file name + */ + home_ptr = getenv("HOME"); + file_name = malloc(strlen(home_ptr)+strlen("mixer.ini\0")); + if (file_name == (char *)0) + { + fprintf(stderr, "Not enough memory\n"); + exit(-1); + } + strncpy(file_name, home_ptr, strlen(home_ptr)); + strncpy(file_name+strlen(home_ptr), "/.mixer\0", 9); + + /* Fill in the device numbers */ + for(i = Master; i <= Bass; i++) + { + levels[i].device = i; + inputs_left[i].device = i; + inputs_right[i].device = i; + outputs[i].device = i; + } + + /* Get arguments */ + if (argc > 1) + { + if (strncmp(argv[1], "-r", 2) == 0) + { + if (read_settings()) + { + printf("Mixer settings restored\n"); + exit(0); + } + else + { + fprintf(stderr, "Could not restore mixer settings\n"); + exit(-1); + } + } + else usage(); + } + + /* Initialize windows. */ + (void) initscr(); + signal(SIGINT, terminate); + old_stdin = fcntl(0,F_GETFL); + cbreak(); + noecho(); + main_win = newwin(23,80,0,0); + scrollok(main_win, FALSE); + + /* Read all current mixer settings */ + rdwr_levels(0); + rdwr_inputs(0); + rdwr_outputs(0); + + /* Set up the user screen and handle user input */ + setup_screen(); + user_interface(); +} + + +void user_interface() +{ + /* This is the user interface. */ + + char c; + int x,y; + int right; + int input_scr, input_pos; + int output_scr, output_pos; + int max_level; + enum Device device; + int fd2; + + device = Master; + right = 0; + input_scr = 0; + output_scr = 0; + input_pos = 0; + output_pos = 0; + + while(1) + { + if (input_scr) + { + y = device + 9; + x = 51 + input_pos + (device == Mic ? 2 : 0); + } + else if (output_scr) + { + y = device + 15; + x = 53 + output_pos + (device == Mic ? 4 : 0); + } + else + { + y = (device != Speaker ? 2 : 1) + + (device - (device < Treble ? 0 : Treble)) * 3 + + (right == 0 ? 0 : 1); + if (!right) + x = 9 + levels[device].left / (device < Speaker ? 2 : 1 ) + + (device > Speaker ? 39 : 0); + else + x = 9 + levels[device].right / (device < Speaker ? 2 : 1) + + (device > Speaker ? 39 : 0); + } + + wmove(main_win,y,x); + wrefresh(main_win); + c = wgetch(main_win); + + switch(c) + { + case CURS_CTRL: + { + (void) wgetch(main_win); + c = wgetch(main_win); + + switch(c) + { + case DOWN: + { + if (output_scr) + { + if (device < Mic) + { + device++; + if (device == Mic) output_pos = 0; + } + } + else if (right || input_scr) + { + if (!input_scr) + { + if (device < Bass) + { + device++; + right = 0; + } + else + { + input_scr = 1; + input_pos = 0; + device = Fm; + } + } + else + { + if (device < Mic) + { + device++; + if (device == Mic && input_pos > 8) input_pos = 8; + } + else + { + device = Cd; + output_scr = 1; + input_scr = 0; + output_pos = 0; + } + } + } + else + { + if (device != Mic && device != Speaker) right = 1; + else { device++; right = 0; } + } + };break; + case UP: + { + if (output_scr) + { + if (device > Cd) device--; + else + { + device = Mic; + output_scr = 0; + input_scr = 1; + } + } + else if (!right || input_scr) + { + if (input_scr) + { + if (device > Fm) device--; + else + { + input_scr = 0; + device = Bass; + right = 1; + } + } + else + { + if (device > Master) + { + device--; + if (device != Mic && device != Speaker) right = 1; + } + } + } + else + right = 0; + };break; + case RIGHT: + { + if (output_scr) + { + if (output_pos < 8 && device != Mic) output_pos = 8; + } + else if (!input_scr) + { + if (device < Speaker) max_level = 31; + else if (device > Speaker) max_level = 15; + else max_level = 4; + + if (!right) + { + if (levels[device].left < max_level) levels[device].left+= + (device < Speaker ? 2 : 1); + } + else + { + if (levels[device].right < max_level) levels[device].right+= + (device < Speaker ? 2 : 1); + } + ioctl(fd, MIXIOSETVOLUME, &levels[device]); + ioctl(fd, MIXIOGETVOLUME, &levels[device]); + create_slider(1 + (device < Treble ? 0 : 39), + (device - (device < Treble ? 0 : Treble))*3 + + (device != Speaker ? 2 : 1), device); + } + else + { + if ((device != Mic && input_pos < 12) || + (device == Mic && input_pos < 8)) + input_pos += (4 + (device == Mic ? 4 : 0)); + } + };break; + case LEFT: + { + if (output_scr) + { + if (output_pos > 0) output_pos = 0; + } + else if (!input_scr) + { + if (!right) + { + if (levels[device].left > 0) levels[device].left-= + (device < Speaker ? 2 : 1); + } + else + { + if (levels[device].right > 0) levels[device].right-= + (device < Speaker ? 2 : 1); + } + ioctl(fd, MIXIOSETVOLUME, &levels[device]); + ioctl(fd, MIXIOGETVOLUME, &levels[device]); + create_slider(1 + (device < Treble ? 0 : 39), + (device - (device < Treble ? 0 : Treble))*3 + + (device != Speaker ? 2 : 1), device); + } + else + { + if (input_pos > 0) + input_pos -= (4 + (device == Mic ? 4 : 0)); + } + };break; + } + };break; + case SPACE: + { + if (output_scr) + { + switch(output_pos) + { + case 0: + case 4: + { + outputs[device].left = + (outputs[device].left == ON ? OFF : ON); + ioctl(fd, MIXIOSETOUTPUT, &outputs[device]); + };break; + case 8: + { + outputs[device].right = + (outputs[device].right == ON ? OFF : ON); + ioctl(fd, MIXIOSETOUTPUT, &outputs[device]); + };break; + } + ioctl(fd, MIXIOGETOUTPUT, &outputs[device]); + show_outputs(41,16); + } + else if (input_scr) + { + switch(input_pos) + { + case 0: + { + inputs_left[device].left = + (inputs_left[device].left == ON ? OFF : ON); + ioctl(fd, MIXIOSETINPUTLEFT, &inputs_left[device]); + };break; + case 4: + { + inputs_left[device].right = + (inputs_left[device].right == ON ? OFF : ON); + ioctl(fd, MIXIOSETINPUTLEFT, &inputs_left[device]); + };break; + case 8: + { + inputs_right[device].left = + (inputs_right[device].left == ON ? OFF : ON); + ioctl(fd, MIXIOSETINPUTRIGHT, &inputs_right[device]); + };break; + case 12: + { + inputs_right[device].right = + (inputs_right[device].right == ON ? OFF : ON); + ioctl(fd, MIXIOSETINPUTRIGHT, &inputs_right[device]); + };break; + } + ioctl(fd, MIXIOGETINPUTLEFT, &inputs_left[device]); + ioctl(fd, MIXIOGETINPUTRIGHT, &inputs_right[device]); + show_inputs(41,8); + } + };break; + case 's': + { + if (write_settings()) + mvwprintw(main_win,22,28, "mixer settings saved"); + else + mvwprintw(main_win,22,28, "error: file not saved"); + wrefresh(main_win); + sleep(1); + mvwprintw(main_win,22,28, " "); + };break; + case 'r': + { + if (read_settings()) + mvwprintw(main_win,22,28, "mixer settings restored"); + else + mvwprintw(main_win,22,28, "error: could not open"); + wrefresh(main_win); + sleep(1); + setup_screen(); + };break; + case 'e': terminate(1); + } + } +} + + +char *d_name(device, name) +enum Device device; +char *name; +{ + /* Convert the device number to a name */ + + switch (device) + { + case Master: strncpy(name, "Master \0", 9);break; + case Dac: strncpy(name, "Dac \0", 9);break; + case Fm: strncpy(name, "Fm \0", 9);break; + case Cd: strncpy(name, "CD \0", 9);break; + case Line: strncpy(name, "Line \0", 9);break; + case Mic: strncpy(name, "Mic \0", 9);break; + case Speaker: strncpy(name, "Speaker \0", 9);break; + case Treble: strncpy(name, "Treble \0", 9);break; + case Bass: strncpy(name, "Bass \0", 9);break; + } + return name; +} + + +void create_slider(x, y, device) +int x; +int y; +enum Device device; +{ + /* Create a slider on the screen */ + + int left; + int right; + int i; + + mvwprintw(main_win,y,x, "%s", d_name(device, name)); + + left = levels[device].left / (device < Speaker ? 2 : 1); + right = levels[device].right / (device < Speaker ? 2 : 1); + + for (i = 0; i < 16; i++) + { + if (device != Speaker || i < 4) + mvwprintw(main_win,y,x+i+8, (i == left ? "*" : "-")); + if (device < Mic || device > Speaker) + mvwprintw(main_win,y+1,x+i+8, (i == right ? "*" : "-")); + } + + if (device < Mic || device > Speaker) + { + mvwprintw(main_win,y,x+i+10, "left"); + mvwprintw(main_win,y+1,x+i+10, "right"); + } + wrefresh(main_win); +} + +void show_inputs(x,y) +int x; +int y; +{ + /* Show the input settings */ + + int i; + + mvwprintw(main_win,y,x, " Rec-In "); + mvwprintw(main_win,y+1,x," left right"); + mvwprintw(main_win,y+2,x," l r l r"); + for (i = Fm; i <= Line; i++) + { + mvwprintw(main_win,y+i+1,x, "%s %d %d %d %d", + d_name(i, (char *)name), + (inputs_left[i].left == ON ? 1 : 0), + (inputs_left[i].right == ON ? 1 : 0), + (inputs_right[i].left == ON ? 1 : 0), + (inputs_right[i].right == ON ? 1 : 0)); + } + mvwprintw(main_win,y+i+1,x, "%s %d %d", + d_name(Mic, (char *)name), + (inputs_left[Mic].left == ON ? 1 : 0), + (inputs_right[Mic].left == ON ? 1 : 0)); + wrefresh(main_win); +} + +void show_outputs(x,y) +int x; +int y; +{ + /* Show the output settings */ + + int i; + + mvwprintw(main_win,y,x, " Mix-Out "); + mvwprintw(main_win,y+1,x, " left right"); + for (i = Cd; i <= Line; i++) + { + mvwprintw(main_win,y+i-1,x,"%s %d %d", + d_name(i, (char *)name), + (outputs[i].left == ON ? 1 : 0), + (outputs[i].right == ON ? 1 : 0)); + } + mvwprintw(main_win,y+i-1,x,"%s %d", + d_name(Mic, (char *)name), + (outputs[Mic].left == ON ? 1 : 0)); + + wrefresh(main_win); +} + + +void setup_screen() +{ + int i; + + wclear(main_win); + mvwprintw(main_win,0,23,"------- Mixer Controls -------"); + wrefresh(main_win); + + for(i = 0; i <= Speaker; i++) + create_slider(1, i*3+(i <= Mic ? 2 : 1), i); + + create_slider(40, 2, Treble); + create_slider(40, 5, Bass); + + show_inputs(41,8); + show_outputs(41,16); +} diff --git a/commands/ibm/part.c b/commands/ibm/part.c new file mode 100755 index 000000000..c27025eb0 --- /dev/null +++ b/commands/ibm/part.c @@ -0,0 +1,2124 @@ +/* part 1.57 - Partition table editor Author: Kees J. Bot + * 13 Mar 1992 + * Needs about 22k heap+stack. + */ +#define nil 0 +#include <sys/types.h> +#include <stdio.h> +#include <termcap.h> +#include <errno.h> +#include <unistd.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <signal.h> +#include <fcntl.h> +#include <time.h> +#include <dirent.h> +#include <limits.h> +#include <a.out.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/ioctl.h> +#include <minix/config.h> +#include <minix/const.h> +#include <minix/partition.h> +#include <minix/u64.h> +#include <ibm/partition.h> +#include <termios.h> + +/* True if a partition is an extended partition. */ +#define ext_part(s) ((s) == 0x05 || (s) == 0x0F) + +/* Minix master bootstrap code. */ +static char MASTERBOOT[] = "/usr/mdec/masterboot"; + +/* Template: + ----first---- --geom/last-- ------sectors----- + Device Cyl Head Sec Cyl Head Sec Base Size Kb + /dev/c0d0 977 5 17 + /dev/c0d0:2 0 0 2 976 4 16 2 83043 41521 +Num Sort Type + 0* p0 81 MINIX 0 0 3 33 4 9 3 2880 1440 + 1 p1 81 MINIX 33 4 10 178 2 2 2883 12284 6142 + 2 p2 81 MINIX 178 2 3 976 4 16 15167 67878 33939 + 3 p3 00 None 0 0 0 0 0 -1 0 0 0 + + */ +#define MAXSIZE 99999999L /* Will 100 G be enough this year? */ +#define SECTOR_SIZE 512 +#define DEV_FD0 0x200 /* Device number of /dev/fd0 */ +#define DEV_C0D0 0x300 /* Device number of /dev/c0d0 */ + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +void report(const char *label) +{ + fprintf(stderr, "part: %s: %s\n", label, strerror(errno)); +} + +void fatal(const char *label) +{ + report(label); + exit(1); +} + +struct termios termios; + +void save_ttyflags(void) +/* Save tty attributes for later restoration. */ +{ + if (tcgetattr(0, &termios) < 0) fatal(""); +} + +void restore_ttyflags(void) +/* Reset the tty flags to how we got 'em. */ +{ + if (tcsetattr(0, TCSANOW, &termios) < 0) fatal(""); +} + +void tty_raw(void) +/* Set the terminal to raw mode, no signals, no echoing. */ +{ + struct termios rawterm; + + rawterm= termios; + rawterm.c_lflag &= ~(ICANON|ISIG|ECHO); + rawterm.c_iflag &= ~(ICRNL); + if (tcsetattr(0, TCSANOW, &rawterm) < 0) fatal(""); +} + +#define ctrl(c) ((c) == '?' ? '\177' : ((c) & '\37')) + +char t_cd[16], t_cm[32], t_so[16], t_se[16], t_md[16], t_me[16]; +int t_li, t_co; +#define STATUSROW 10 + +void init_tty(void) +/* Get terminal capabilities and set the tty to "editor" mode. */ +{ + char *term; + static char termbuf[1024]; + char *tp; + + if ((term= getenv("TERM")) == nil || tgetent(termbuf, term) != 1) { + fprintf(stderr, "part: Can't get terminal capabilities\n"); + exit(1); + } + if (tgetstr("cd", (tp= t_cd, &tp)) == nil + || tgetstr("cm", (tp= t_cm, &tp)) == nil) { + fprintf(stderr, "part: This terminal is too dumb\n"); + exit(1); + } + t_li= tgetnum("li"); + t_co= tgetnum("co"); + (void) tgetstr("so", (tp= t_so, &tp)); + (void) tgetstr("se", (tp= t_se, &tp)); + (void) tgetstr("md", (tp= t_md, &tp)); + (void) tgetstr("me", (tp= t_me, &tp)); + + save_ttyflags(); + tty_raw(); +} + +void putchr(int c) +{ + putchar(c); +} + +void putstr(char *s) +{ + int c; + + while ((c= *s++) != 0) putchr(c); +} + +void set_cursor(int row, int col) +{ + tputs(tgoto(t_cm, col, row), 1, putchr); +} + +int statusrow= STATUSROW; +int stat_ktl= 1; +int need_help= 1; + +void stat_start(int serious) +/* Prepare for printing on a fresh status line, possibly highlighted. */ +{ + set_cursor(statusrow++, 0); + tputs(t_cd, 1, putchr); + if (serious) tputs(t_so, 1, putchr); +} + +void stat_end(int ktl) +/* Closing bracket for stat_start. Sets "keystrokes to live" of message. */ +{ + tputs(t_se, 1, putchr); + stat_ktl= ktl; + need_help= 1; +} + +void stat_reset(void) +/* Reset the statusline pointer and clear old messages if expired. */ +{ + if (stat_ktl > 0 && --stat_ktl == 0) { + statusrow= STATUSROW; + need_help= 1; + } + if (need_help && statusrow < (24-2)) { + if (statusrow > STATUSROW) stat_start(0); + stat_start(0); + putstr( +"Type '+' or '-' to change, 'r' to read, '?' for more help, '!' for advice"); + } + statusrow= STATUSROW; + need_help= 0; +} + +void clear_screen(void) +{ + set_cursor(0, 0); + tputs(t_cd, 1, putchr); + stat_ktl= 1; + stat_reset(); +} + +void reset_tty(void) +/* Reset the tty to cooked mode. */ +{ + restore_ttyflags(); + set_cursor(statusrow, 0); + tputs(t_cd, 1, putchr); +} + +void *alloc(size_t n) +{ + void *m; + + if ((m= malloc(n)) == nil) { reset_tty(); fatal(""); } + + return m; +} + +#ifndef makedev /* Missing in sys/types.h */ +#define minor(dev) (((dev) >> MINOR) & BYTE) +#define major(dev) (((dev) >> MAJOR) & BYTE) +#define makedev(major, minor) \ + ((dev_t) (((major) << MAJOR) | ((minor) << MINOR))) +#endif + +typedef enum parttype { DUNNO, SUBPART, PRIMARY, FLOPPY } parttype_t; + +typedef struct device { + struct device *next, *prev; /* Circular dequeue. */ + dev_t rdev; /* Device number (sorting only). */ + char *name; /* E.g. /dev/c0d0 */ + char *subname; /* E.g. /dev/c0d0:2 */ + parttype_t parttype; +} device_t; + +device_t *firstdev= nil, *curdev; + +void newdevice(char *name, int scanning) +/* Add a device to the device list. If scanning is set then we are reading + * /dev, so insert the device in device number order and make /dev/c0d0 current. + */ +{ + device_t *new, *nextdev, *prevdev; + struct stat st; + + st.st_rdev= 0; + if (scanning) { + if (stat(name, &st) < 0 || !S_ISBLK(st.st_mode)) return; + + switch (major(st.st_rdev)) { + case 2: + /* Floppy */ + if (minor(st.st_rdev) >= 4) return; + break; + case 3: + case 8: + case 10: + case 12: + case 16: + /* Disk controller */ + if (minor(st.st_rdev) >= 0x80 + || minor(st.st_rdev) % 5 != 0) return; + break; + default: + return; + } + /* Interesting device found. */ + } else { + (void) stat(name, &st); + } + + new= alloc(sizeof(*new)); + new->rdev= st.st_rdev; + new->name= alloc((strlen(name) + 1) * sizeof(new->name[0])); + strcpy(new->name, name); + new->subname= new->name; + new->parttype= DUNNO; + if (major(st.st_rdev) == major(DEV_FD0) && minor(st.st_rdev) < 112) { + new->parttype= FLOPPY; + } else + if (st.st_rdev >= DEV_C0D0 && minor(st.st_rdev) < 128 + && minor(st.st_rdev) % 5 == 0) { + new->parttype= PRIMARY; + } + + if (firstdev == nil) { + firstdev= new; + new->next= new->prev= new; + curdev= firstdev; + return; + } + nextdev= firstdev; + while (new->rdev >= nextdev->rdev + && (nextdev= nextdev->next) != firstdev) {} + prevdev= nextdev->prev; + new->next= nextdev; + nextdev->prev= new; + new->prev= prevdev; + prevdev->next= new; + + if (new->rdev < firstdev->rdev) firstdev= new; + if (new->rdev == DEV_C0D0) curdev= new; + if (curdev->rdev != DEV_C0D0) curdev= firstdev; +} + +void getdevices(void) +/* Get all block devices from /dev that look interesting. */ +{ + DIR *d; + struct dirent *e; + char name[5 + NAME_MAX + 1]; + + if ((d= opendir("/dev")) == nil) fatal("/dev"); + + while ((e= readdir(d)) != nil) { + strcpy(name, "/dev/"); + strcpy(name + 5, e->d_name); + newdevice(name, 1); + } + (void) closedir(d); +} + +/* One featureful master bootstrap. */ +unsigned char bootstrap[] = { +0353,0001,0000,0061,0300,0216,0330,0216,0300,0372,0216,0320,0274,0000,0174,0373, +0275,0276,0007,0211,0346,0126,0277,0000,0006,0271,0000,0001,0374,0363,0245,0352, +0044,0006,0000,0000,0264,0002,0315,0026,0250,0010,0164,0033,0350,0071,0001,0174, +0007,0060,0344,0315,0026,0242,0205,0007,0054,0060,0074,0012,0163,0363,0120,0350, +0046,0001,0205,0007,0130,0353,0012,0240,0002,0006,0204,0300,0165,0003,0351,0147, +0000,0230,0262,0005,0366,0362,0262,0200,0000,0302,0210,0340,0120,0350,0234,0000, +0163,0003,0351,0147,0000,0130,0054,0001,0175,0003,0351,0141,0000,0276,0276,0175, +0211,0357,0271,0040,0000,0363,0245,0200,0301,0004,0211,0356,0215,0174,0020,0070, +0154,0004,0164,0016,0213,0135,0010,0053,0134,0010,0213,0135,0012,0033,0134,0012, +0163,0014,0212,0044,0206,0144,0020,0210,0044,0106,0071,0376,0162,0364,0211,0376, +0201,0376,0356,0007,0162,0326,0342,0322,0211,0356,0264,0020,0366,0344,0001,0306, +0200,0174,0004,0001,0162,0026,0353,0021,0204,0322,0175,0041,0211,0356,0200,0174, +0004,0000,0164,0013,0366,0004,0200,0164,0006,0350,0070,0000,0162,0053,0303,0203, +0306,0020,0201,0376,0376,0007,0162,0346,0350,0215,0000,0211,0007,0376,0302,0204, +0322,0174,0023,0315,0021,0321,0340,0321,0340,0200,0344,0003,0070,0342,0167,0355, +0350,0011,0000,0162,0350,0303,0350,0003,0000,0162,0146,0303,0211,0356,0214,0134, +0010,0214,0134,0012,0277,0003,0000,0122,0006,0127,0264,0010,0315,0023,0137,0007, +0200,0341,0077,0376,0306,0210,0310,0366,0346,0211,0303,0213,0104,0010,0213,0124, +0012,0367,0363,0222,0210,0325,0366,0361,0060,0322,0321,0352,0321,0352,0010,0342, +0210,0321,0376,0301,0132,0210,0306,0273,0000,0174,0270,0001,0002,0315,0023,0163, +0020,0200,0374,0200,0164,0011,0117,0174,0006,0060,0344,0315,0023,0163,0270,0371, +0303,0201,0076,0376,0175,0125,0252,0165,0001,0303,0350,0013,0000,0243,0007,0353, +0005,0350,0004,0000,0227,0007,0353,0376,0136,0255,0126,0211,0306,0254,0204,0300, +0164,0011,0264,0016,0273,0001,0000,0315,0020,0353,0362,0303,0057,0144,0145,0166, +0057,0150,0144,0077,0010,0000,0015,0012,0000,0116,0157,0156,0145,0040,0141,0143, +0164,0151,0166,0145,0015,0012,0000,0122,0145,0141,0144,0040,0145,0162,0162,0157, +0162,0040,0000,0116,0157,0164,0040,0142,0157,0157,0164,0141,0142,0154,0145,0040, +0000,0000, +}; + +int dirty= 0; +unsigned char bootblock[SECTOR_SIZE]; +struct part_entry table[1 + NR_PARTITIONS]; +int existing[1 + NR_PARTITIONS]; +unsigned long offset= 0, extbase= 0, extsize; +int submerged= 0; +char sort_index[1 + NR_PARTITIONS]; +unsigned cylinders= 1, heads= 1, sectors= 1, secpcyl= 1; +unsigned alt_cyls= 1, alt_heads= 1, alt_secs= 1; +int precise= 0; +int device= -1; + +unsigned long sortbase(struct part_entry *pe) +{ + return pe->sysind == NO_PART ? -1 : pe->lowsec; +} + +void sort(void) +/* Let the sort_index array show the order partitions are sorted in. */ +{ + int i, j; + int idx[1 + NR_PARTITIONS]; + + for (i= 1; i <= NR_PARTITIONS; i++) idx[i]= i; + + for (i= 1; i <= NR_PARTITIONS; i++) { + for (j= 1; j <= NR_PARTITIONS-1; j++) { + int sj= idx[j], sj1= idx[j+1]; + + if (sortbase(&table[sj]) > sortbase(&table[sj1])) { + idx[j]= sj1; + idx[j+1]= sj; + } + } + } + for (i= 1; i <= NR_PARTITIONS; i++) sort_index[idx[i]]= i; +} + +void dos2chs(unsigned char *dos, unsigned *chs) +/* Extract cylinder, head and sector from the three bytes DOS uses to address + * a sector. Note that bits 8 & 9 of the cylinder number come from bit 6 & 7 + * of the sector byte. The sector number is rebased to count from 0. + */ +{ + chs[0]= ((dos[1] & 0xC0) << 2) | dos[2]; + chs[1]= dos[0]; + chs[2]= (dos[1] & 0x3F) - 1; +} + +void abs2dos(unsigned char *dos, unsigned long pos) +/* Translate a sector offset to three DOS bytes. */ +{ + unsigned h, c, s; + + c= pos / secpcyl; + h= (pos % secpcyl) / sectors; + s= pos % sectors + 1; + + dos[0]= h; + dos[1]= s | ((c >> 2) & 0xC0); + dos[2]= c & 0xFF; +} + +void recompute0(void) +/* Recompute the partition size for the device after a geometry change. */ +{ + if (device < 0) { + cylinders= heads= sectors= 1; + memset(table, 0, sizeof(table)); + } else + if (!precise && offset == 0) { + table[0].lowsec= 0; + table[0].size= (unsigned long) cylinders * heads * sectors; + } + table[0].sysind= device < 0 ? NO_PART : MINIX_PART; + secpcyl= heads * sectors; +} + +void guess_geometry(void) +/* With a bit of work one can deduce the disk geometry from the partition + * table. This may be necessary if the driver gets it wrong. (If partition + * tables didn't have C/H/S numbers we would not care at all...) + */ +{ + int i, n; + struct part_entry *pe; + unsigned chs[3]; + unsigned long sec; + unsigned h, s; + unsigned char HS[256][8]; /* Bit map off all possible H/S */ + + alt_cyls= alt_heads= alt_secs= 0; + + /* Initially all possible H/S combinations are possible. HS[h][0] + * bit 0 is used to rule out a head value. + */ + for (h= 1; h <= 255; h++) { + for (s= 0; s < 8; s++) HS[h][s]= 0xFF; + } + + for (i= 0; i < 2*NR_PARTITIONS; i++) { + pe= &(table+1)[i >> 1]; + if (pe->sysind == NO_PART) continue; + + /* Get the end or start sector numbers (in that order). */ + if ((i & 1) == 0) { + dos2chs(&pe->last_head, chs); + sec= pe->lowsec + pe->size - 1; + } else { + dos2chs(&pe->start_head, chs); + sec= pe->lowsec; + } + + if (chs[0] >= alt_cyls) alt_cyls= chs[0]+1; + + /* Which H/S combinations can be ruled out? */ + for (h= 1; h <= 255; h++) { + if (HS[h][0] == 0) continue; + n = 0; + for (s= 1; s <= 63; s++) { + if ((chs[0] * h + chs[1]) * s + chs[2] != sec) { + HS[h][s/8] &= ~(1 << (s%8)); + } + if (HS[h][s/8] & (1 << (s%8))) n++; + } + if (n == 0) HS[h][0]= 0; + } + } + + /* See if only one remains. */ + i= 0; + for (h= 1; h <= 255; h++) { + if (HS[h][0] == 0) continue; + for (s= 1; s <= 63; s++) { + if (HS[h][s/8] & (1 << (s%8))) { + i++; + alt_heads= h; + alt_secs= s; + } + } + } + + /* Forget it if more than one choice... */ + if (i > 1) alt_cyls= alt_heads= alt_secs= 0; +} + +void geometry(void) +/* Find out the geometry of the device by querying the driver, or by looking + * at the partition table. These numbers are crosschecked to make sure that + * the geometry is correct. Master bootstraps other than the Minix one use + * the CHS numbers in the partition table to load the bootstrap of the active + * partition. + */ +{ + struct stat dst; + int err= 0; + struct partition geometry; + + if (submerged) { + /* Geometry already known. */ + sort(); + return; + } + precise= 0; + cylinders= 0; + recompute0(); + if (device < 0) return; + + /* Try to guess the geometry from the partition table. */ + guess_geometry(); + + /* Try to get the geometry from the driver. */ + (void) fstat(device, &dst); + + if (S_ISBLK(dst.st_mode) || S_ISCHR(dst.st_mode)) { + /* Try to get the drive's geometry from the driver. */ + + if (ioctl(device, DIOCGETP, &geometry) < 0) + err= errno; + else { + table[0].lowsec= div64u(geometry.base, SECTOR_SIZE); + table[0].size= div64u(geometry.size, SECTOR_SIZE); + cylinders= geometry.cylinders; + heads= geometry.heads; + sectors= geometry.sectors; + precise= 1; + } + } else { + err= ENODEV; + } + + if (err != 0) { + /* Getting the geometry from the driver failed, so use the + * alternate geometry. + */ + if (alt_heads == 0) { + alt_cyls= table[0].size / (64 * 32); + alt_heads= 64; + alt_secs= 32; + } + + cylinders= alt_cyls; + heads= alt_heads; + sectors= alt_secs; + + stat_start(1); + printf("Failure to get the geometry of %s: %s", curdev->name, + errno == ENOTTY ? "No driver support" : strerror(err)); + stat_end(5); + stat_start(0); + printf("The geometry has been guessed as %ux%ux%u", + cylinders, heads, sectors); + stat_end(5); + } else { + if (alt_heads == 0) { + alt_cyls= cylinders; + alt_heads= heads; + alt_secs= sectors; + } + + if (heads != alt_heads || sectors != alt_secs) { + stat_start(1); + printf("WARNING:"); + stat_end(10); + stat_start(0); + printf( +"The %ux%ux%u geometry obtained from the device driver does not match", + cylinders, heads, sectors); + stat_end(10); + stat_start(0); + printf( +"the %ux%ux%u geometry implied by the partition table. Hit 'X' to switch", + alt_cyls, alt_heads, alt_secs); + stat_end(10); + stat_start(0); + printf( +"between the two geometries to see what is best. Note that the geometry"); + stat_end(10); + stat_start(0); + printf( +"must be correct when the table is written or the system may not boot!"); + stat_end(10); + } + } + + /* Show the base and size of the device instead of the whole drive. + * This makes sense for subpartitioning primary partitions. + */ + if (precise && ioctl(device, DIOCGETP, &geometry) >= 0) { + table[0].lowsec= div64u(geometry.base, SECTOR_SIZE); + table[0].size= div64u(geometry.size, SECTOR_SIZE); + } else { + precise= 0; + } + recompute0(); + sort(); +} + +typedef struct indicators { /* Partition type to partition name. */ + unsigned char ind; + char name[10]; +} indicators_t; + +indicators_t ind_table[]= { + { 0x00, "None" }, + { 0x01, "FAT-12" }, + { 0x02, "XENIX /" }, + { 0x03, "XENIX usr" }, + { 0x04, "FAT-16" }, + { 0x05, "EXTENDED" }, + { 0x06, "FAT-16" }, + { 0x07, "HPFS/NTFS" }, + { 0x08, "AIX" }, + { 0x09, "COHERENT" }, + { 0x0A, "OS/2" }, + { 0x0B, "FAT-32" }, + { 0x0C, "FAT?" }, + { 0x0E, "FAT?" }, + { 0x0F, "EXTENDED" }, + { 0x10, "OPUS" }, + { 0x40, "VENIX286" }, + { 0x42, "W2000 Dyn" }, + { 0x52, "MICROPORT" }, + { 0x63, "386/IX" }, + { 0x64, "NOVELL286" }, + { 0x65, "NOVELL386" }, + { 0x75, "PC/IX" }, + { 0x80, "MINIX-OLD" }, + { 0x81, "MINIX" }, + { 0x82, "LINUXswap" }, + { 0x83, "LINUX" }, + { 0x93, "AMOEBA" }, + { 0x94, "AMOEBAbad" }, + { 0xA5, "386BSD" }, + { 0xB7, "BSDI" }, + { 0xB8, "BSDI swap" }, + { 0xC7, "SYRINX" }, + { 0xDB, "CPM" }, + { 0xFF, "BADBLOCKS" }, +}; + +char *typ2txt(int ind) +/* Translate a numeric partition indicator for human eyes. */ +{ + indicators_t *pind; + + for (pind= ind_table; pind < arraylimit(ind_table); pind++) { + if (pind->ind == ind) return pind->name; + } + return ""; +} + +int round_sysind(int ind, int delta) +/* Find the next known partition type starting with ind in direction delta. */ +{ + indicators_t *pind; + + ind= (ind + delta) & 0xFF; + + if (delta < 0) { + for (pind= arraylimit(ind_table)-1; pind->ind > ind; pind--) {} + } else { + for (pind= ind_table; pind->ind < ind; pind++) {} + } + return pind->ind; +} + +/* Objects on the screen, either simple pieces of the text or the cylinder + * number of the start of partition three. + */ +typedef enum objtype { + O_INFO, O_TEXT, O_DEV, O_SUB, + O_TYPTXT, O_SORT, O_NUM, O_TYPHEX, + O_CYL, O_HEAD, O_SEC, + O_SCYL, O_SHEAD, O_SSEC, O_LCYL, O_LHEAD, O_LSEC, O_BASE, O_SIZE, O_KB +} objtype_t; + +#define rjust(type) ((type) >= O_TYPHEX) +#define computed(type) ((type) >= O_TYPTXT) + +typedef struct object { + struct object *next; + objtype_t type; /* Text field, cylinder number, etc. */ + char flags; /* Modifiable? */ + char row; + char col; + char len; + struct part_entry *entry; /* What does the object refer to? */ + char *text; + char value[20]; /* Value when printed. */ +} object_t; + +#define OF_MOD 0x01 /* Object value is modifiable. */ +#define OF_ODD 0x02 /* It has a somewhat odd value. */ +#define OF_BAD 0x04 /* Its value is no good at all. */ + +/* Events: (Keypress events are the value of the key pressed.) */ +#define E_ENTER (-1) /* Cursor moves onto object. */ +#define E_LEAVE (-2) /* Cursor leaves object. */ +#define E_WRITE (-3) /* Write, but not by typing 'w'. */ + +/* The O_SIZE objects have a dual identity. */ +enum howend { SIZE, LAST } howend= SIZE; + +object_t *world= nil; +object_t *curobj= nil; + +object_t *newobject(objtype_t type, int flags, int row, int col, int len) +/* Make a new object given a type, flags, position and length on the screen. */ +{ + object_t *new; + object_t **aop= &world; + + new= alloc(sizeof(*new)); + + new->type= type; + new->flags= flags; + new->row= row; + new->col= col; + new->len= len; + new->entry= nil; + new->text= ""; + new->value[0]= 0; + + new->next= *aop; + *aop= new; + + return new; +} + +unsigned long entry2base(struct part_entry *pe) +/* Return the base sector of the partition if defined. */ +{ + return pe->sysind == NO_PART ? 0 : pe->lowsec; +} + +unsigned long entry2last(struct part_entry *pe) +{ + return pe->sysind == NO_PART ? -1 : pe->lowsec + pe->size - 1; +} + +unsigned long entry2size(struct part_entry *pe) +{ + return pe->sysind == NO_PART ? 0 : pe->size; +} + +int overlap(unsigned long sec) +/* See if sec is part of another partition. */ +{ + struct part_entry *pe; + + for (pe= table + 1; pe <= table + NR_PARTITIONS; pe++) { + if (pe->sysind == NO_PART) continue; + + if (pe->lowsec < sec && sec < pe->lowsec + pe->size) + return 1; + } + return 0; +} + +int aligned(unsigned long sec, unsigned unit) +/* True if sec is aligned to unit or if it is no problem if it is unaligned. */ +{ + return (offset != 0 && extbase == 0) || (sec % unit == 0); +} + +void print(object_t *op) +/* Print an object's value if it changed. */ +{ + struct part_entry *pe= op->entry; + int n; + unsigned long t; + char *name; + int oldflags; + char oldvalue[20]; + + /* Remember the old flags and value. */ + oldflags= op->flags; + strcpy(oldvalue, op->value); + + op->flags&= ~(OF_ODD | OF_BAD); + + switch (op->type) { + case O_INFO: { + /* Current field. */ + static struct field { int type; char *name; } fields[]= { + { O_DEV, "Select device" }, + { O_NUM, "Active flag" }, + { O_TYPHEX, "Hex partition type" }, + { O_TYPTXT, "Partition type" }, + { O_SCYL, "Start cylinder" }, + { O_SHEAD, "Start head" }, + { O_SSEC, "Start sector" }, + { O_CYL, "Number of cylinders" }, + { O_HEAD, "Number of heads" }, + { O_SEC, "Sectors per track" }, + { O_LCYL, "Last cylinder" }, + { O_LHEAD, "Last head" }, + { O_LSEC, "Last sector" }, + { O_BASE, "Base sector" }, + { O_SIZE, "Size in sectors" }, + { O_KB, "Size in kilobytes" }, + { -1, "?" }, + }; + struct field *fp= fields; + + while (fp->type >= 0 && fp->type != curobj->type) fp++; + strcpy(op->value, fp->name); + op->flags|= OF_ODD; + break; } + case O_TEXT: + /* Simple text field. */ + strcpy(op->value, op->text); + break; + case O_DEV: + case O_SUB: + /* Name of currently edited device. */ + name= op->type == O_DEV ? curdev->name : + offset == 0 ? "" : curdev->subname; + if ((n= strlen(name)) < op->len) n= op->len; + strcpy(op->value, name + (n - op->len)); + if (device < 0 && op->type == O_DEV) op->flags|= OF_BAD; + break; + case O_NUM: + /* Position and active flag. */ + sprintf(op->value, "%d%c", (int) (pe - table - 1), + pe->bootind & ACTIVE_FLAG ? '*' : ' '); + break; + case O_SORT: + /* Position if the driver sorts the table. */ + sprintf(op->value, "%s%d", + curdev->parttype >= PRIMARY ? "p" : + curdev->parttype == SUBPART ? "s" : "", + (curdev->parttype == SUBPART || + curdev->parttype == FLOPPY ? pe - table + : sort_index[pe - table]) - 1); + break; + case O_TYPHEX: + /* Hex partition type indicator. */ + sprintf(op->value, "%02X", pe->sysind); + break; + case O_TYPTXT: + /* Ascii partition type indicator. */ + strcpy(op->value, typ2txt(pe->sysind)); + break; + case O_SCYL: + /* Partition's start cylinder. */ + sprintf(op->value, "%lu", entry2base(pe) / secpcyl); + break; + case O_SHEAD: + /* Start head. */ + t= entry2base(pe); + sprintf(op->value, "%lu", t % secpcyl / sectors); + if (!aligned(t, secpcyl) && t != table[0].lowsec + sectors) + op->flags|= OF_ODD; + break; + case O_SSEC: + /* Start sector. */ + t= entry2base(pe); + sprintf(op->value, "%lu", t % sectors); + if (!aligned(t, sectors)) op->flags|= OF_ODD; + break; + case O_CYL: + /* Number of cylinders. */ + sprintf(op->value, "%u", cylinders); + break; + case O_HEAD: + /* Number of heads. */ + sprintf(op->value, "%u", heads); + break; + case O_SEC: + /* Number of sectors per track. */ + sprintf(op->value, "%u", sectors); + break; + case O_LCYL: + /* Partition's last cylinder. */ + t= entry2last(pe); + sprintf(op->value, "%lu", t == -1 ? 0 : t / secpcyl); + break; + case O_LHEAD: + /* Partition's last head. */ + t= entry2last(pe); + sprintf(op->value, "%lu", t == -1 ? 0 : t % secpcyl / sectors); + if (!aligned(t + 1, secpcyl)) op->flags|= OF_ODD; + break; + case O_LSEC: + /* Partition's last sector. */ + t= entry2last(pe); + sprintf(op->value, t == -1 ? "-1" : "%lu", t % sectors); + if (!aligned(t + 1, sectors)) op->flags|= OF_ODD; + break; + case O_BASE: + /* Partition's base sector. */ + sprintf(op->value, "%lu", entry2base(pe)); + if (pe->sysind != NO_PART && pe != &table[0] + && (pe->lowsec <= table[0].lowsec || overlap(pe->lowsec))) + op->flags|= OF_BAD; + break; + case O_SIZE: + /* Size of partitition in sectors. */ + t= howend == SIZE ? entry2size(pe) : entry2last(pe); + sprintf(op->value, "%lu", pe->sysind == NO_PART ? 0 : t); + if (pe->sysind != NO_PART && (pe->size == 0 + || pe->lowsec + pe->size > table[0].lowsec + table[0].size + || overlap(pe->lowsec + pe->size))) + op->flags|= OF_BAD; + break; + case O_KB: + /* Size of partitition in kilobytes. */ + sprintf(op->value, "%lu", entry2size(pe) / 2); + break; + default: + sprintf(op->value, "?? %d ??", op->type); + } + + if (device < 0 && computed(op->type)) strcpy(op->value, "?"); + + /* If a value overflows the print field then show a blank + * reverse video field. + */ + if ((n= strlen(op->value)) > op->len) { + n= 0; + op->flags|= OF_BAD; + } + + /* Right or left justified? */ + if (rjust(op->type)) { + memmove(op->value + (op->len - n), op->value, n); + memset(op->value, ' ', op->len - n); + } else { + memset(op->value + n, ' ', op->len - n); + } + op->value[op->len]= 0; + + if ((op->flags & (OF_ODD | OF_BAD)) == (oldflags & (OF_ODD | OF_BAD)) + && strcmp(op->value, oldvalue) == 0) { + /* The value did not change. */ + return; + } + + set_cursor(op->row, rjust(op->type) ? op->col - (op->len-1) : op->col); + + if (op->flags & OF_BAD) tputs(t_so, 1, putchr); + else + if (op->flags & OF_ODD) tputs(t_md, 1, putchr); + putstr(op->value); + if (op->flags & OF_BAD) tputs(t_se, 1, putchr); + else + if (op->flags & OF_ODD) tputs(t_me, 1, putchr); +} + +void display(void) +/* Repaint all objects that changed. */ +{ + object_t *op; + + for (op= world; op != nil; op= op->next) print(op); +} + +int typing; /* Set if a digit has been typed to set a value. */ +int magic; /* Changes when using the magic key. */ + +void event(int ev, object_t *op); + +void m_redraw(int ev, object_t *op) +/* Redraw the screen. */ +{ + object_t *op2; + + if (ev != ctrl('L')) return; + + clear_screen(); + for (op2= world; op2 != nil; op2= op2->next) op2->value[0]= 0; +} + +void m_toggle(int ev, object_t *op) +/* Toggle between the driver and alternate geometry. */ +{ + unsigned t; + + if (ev != 'X') return; + if (alt_cyls == cylinders && alt_heads == heads && alt_secs == sectors) + return; + + t= cylinders; cylinders= alt_cyls; alt_cyls= t; + t= heads; heads= alt_heads; alt_heads= t; + t= sectors; sectors= alt_secs; alt_secs= t; + dirty= 1; + recompute0(); +} + +char size_last[]= "Size"; + +void m_orientation(int ev, object_t *op) +{ + if (ev != ' ') return; + + switch (howend) { + case SIZE: + howend= LAST; + strcpy(size_last, "Last"); + break; + case LAST: + howend= SIZE; + strcpy(size_last, "Size"); + } +} + +void m_move(int ev, object_t *op) +/* Move to the nearest modifiably object in the intended direction. Objects + * on the same row or column are really near. + */ +{ + object_t *near, *op2; + unsigned dist, d2, dr, dc; + + if (ev != 'h' && ev != 'j' && ev != 'k' && ev != 'l' && ev != 'H') + return; + + if (device < 0) { + /* No device open? Then try to read first. */ + event('r', op); + if (device < 0) return; + } + + near= op; + dist= -1; + + for (op2= world; op2 != nil; op2= op2->next) { + if (op2 == op || !(op2->flags & OF_MOD)) continue; + + dr= abs(op2->row - op->row); + dc= abs(op2->col - op->col); + + d2= 25*dr*dr + dc*dc; + if (op2->row != op->row && op2->col != op->col) d2+= 1000; + + switch (ev) { + case 'h': /* Left */ + if (op2->col >= op->col) d2= -1; + break; + case 'j': /* Down */ + if (op2->row <= op->row) d2= -1; + break; + case 'k': /* Up */ + if (op2->row >= op->row) d2= -1; + break; + case 'l': /* Right */ + if (op2->col <= op->col) d2= -1; + break; + case 'H': /* Home */ + if (op2->type == O_DEV) d2= 0; + } + if (d2 < dist) { near= op2; dist= d2; } + } + if (near != op) event(E_LEAVE, op); + event(E_ENTER, near); +} + +void m_updown(int ev, object_t *op) +/* Move a partition table entry up or down. */ +{ + int i, j; + struct part_entry tmp; + int tmpx; + + if (ev != ctrl('K') && ev != ctrl('J')) return; + if (op->entry == nil) return; + + i= op->entry - table; + if (ev == ctrl('K')) { + if (i <= 1) return; + j= i-1; + } else { + if (i >= NR_PARTITIONS) return; + j= i+1; + } + + tmp= table[i]; table[i]= table[j]; table[j]= tmp; + tmpx= existing[i]; existing[i]= existing[j]; existing[j]= tmpx; + sort(); + dirty= 1; + event(ev == ctrl('K') ? 'k' : 'j', op); +} + +void m_enter(int ev, object_t *op) +/* We've moved onto this object. */ +{ + if (ev != E_ENTER && ev != ' ' && ev != '<' && ev != '>' && ev != 'X') + return; + curobj= op; + typing= 0; + magic= 0; +} + +void m_leave(int ev, object_t *op) +/* About to leave this object. */ +{ + if (ev != E_LEAVE) return; +} + +int within(unsigned *var, unsigned low, unsigned value, unsigned high) +/* Only set *var to value if it looks reasonable. */ +{ + if (low <= value && value <= high) { + *var= value; + return 1; + } else + return 0; +} + +int lwithin(unsigned long *var, unsigned long low, unsigned long value, + unsigned long high) +{ + if (low <= value && value <= high) { + *var= value; + return 1; + } else + return 0; +} + +int nextdevice(object_t *op, int delta) +/* Select the next or previous device from the device list. */ +{ + dev_t rdev; + + if (offset != 0) return 0; + if (dirty) event(E_WRITE, op); + if (dirty) return 0; + + if (device >= 0) { + (void) close(device); + device= -1; + } + recompute0(); + + rdev= curdev->rdev; + if (delta < 0) { + do + curdev= curdev->prev; + while (delta < -1 && major(curdev->rdev) == major(rdev) + && curdev->rdev < rdev); + } else { + do + curdev= curdev->next; + while (delta > 1 && major(curdev->rdev) == major(rdev) + && curdev->rdev > rdev); + } + return 1; +} + +void check_ind(struct part_entry *pe) +/* If there are no other partitions then make this new one active. */ +{ + struct part_entry *pe2; + + if (pe->sysind != NO_PART) return; + + for (pe2= table + 1; pe2 < table + 1 + NR_PARTITIONS; pe2++) + if (pe2->sysind != NO_PART || pe2->bootind & ACTIVE_FLAG) break; + + if (pe2 == table + 1 + NR_PARTITIONS) pe->bootind= ACTIVE_FLAG; +} + +int check_existing(struct part_entry *pe) +/* Check and if not ask if an existing partition may be modified. */ +{ + static int expert= 0; + int c; + + if (expert || pe == nil || !existing[pe - table]) return 1; + + stat_start(1); + putstr("Do you wish to modify existing partitions? (y/n) "); + fflush(stdout); + while ((c= getchar()) != 'y' && c != 'n') {} + putchr(c); + stat_end(3); + return (expert= (c == 'y')); +} + +void m_modify(int ev, object_t *op) +/* Increment, decrement, set, or toggle the value of an object, using + * arithmetic tricks the author doesn't understand either. + */ +{ + object_t *op2; + struct part_entry *pe= op->entry; + int mul, delta; + unsigned level= 1; + unsigned long surplus; + int radix= op->type == O_TYPHEX ? 0x10 : 10; + unsigned long t; + + if (device < 0 && op->type != O_DEV) return; + + switch (ev) { + case '-': + mul= radix; delta= -1; typing= 0; + break; + case '+': + mul= radix; delta= 1; typing= 0; + break; + case '\b': + if (!typing) return; + mul= 1; delta= 0; + break; + case '\r': + typing= 0; + return; + default: + if ('0' <= ev && ev <= '9') + delta= ev - '0'; + else + if (radix == 0x10 && 'a' <= ev && ev <= 'f') + delta= ev - 'a' + 10; + else + if (radix == 0x10 && 'A' <= ev && ev <= 'F') + delta= ev - 'A' + 10; + else + return; + + mul= typing ? radix*radix : 0; + typing= 1; + } + magic= 0; + + if (!check_existing(pe)) return; + + switch (op->type) { + case O_DEV: + if (ev != '-' && ev != '+') return; + if (!nextdevice(op, delta)) return; + break; + case O_CYL: + if (!within(&cylinders, 1, + cylinders * mul / radix + delta, 1024)) return; + recompute0(); + break; + case O_HEAD: + if (!within(&heads, 1, heads * mul / radix + delta, 255)) + return; + recompute0(); + break; + case O_SEC: + if (!within(§ors, 1, sectors * mul / radix + delta, 63)) + return; + recompute0(); + break; + case O_NUM: + if (ev != '-' && ev != '+') return; + for (op2= world; op2 != nil; op2= op2->next) { + if (op2->type == O_NUM && ev == '+') + op2->entry->bootind= 0; + } + op->entry->bootind= ev == '+' ? ACTIVE_FLAG : 0; + break; + case O_TYPHEX: + check_ind(pe); + pe->sysind= pe->sysind * mul / radix + delta; + break; + case O_TYPTXT: + if (ev != '-' && ev != '+') return; + check_ind(pe); + pe->sysind= round_sysind(pe->sysind, delta); + break; + case O_SCYL: + level= heads; + case O_SHEAD: + level*= sectors; + case O_SSEC: + if (op->type != O_SCYL && ev != '-' && ev != '+') return; + case O_BASE: + if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe)); + t= pe->lowsec; + surplus= t % level; + if (!lwithin(&t, 0L, + (t / level * mul / radix + delta) * level + surplus, + MAXSIZE)) return; + if (howend == LAST || op->type != O_BASE) + pe->size-= t - pe->lowsec; + pe->lowsec= t; + check_ind(pe); + if (pe->sysind == NO_PART) pe->sysind= MINIX_PART; + break; + case O_LCYL: + level= heads; + case O_LHEAD: + level*= sectors; + case O_LSEC: + if (op->type != O_LCYL && ev != '-' && ev != '+') return; + + if (pe->sysind == NO_PART) memset(pe, 0, sizeof(*pe)); + t= pe->lowsec + pe->size - 1 + level; + surplus= t % level - mul / radix * level; + if (!lwithin(&t, 0L, + (t / level * mul / radix + delta) * level + surplus, + MAXSIZE)) return; + pe->size= t - pe->lowsec + 1; + check_ind(pe); + if (pe->sysind == NO_PART) pe->sysind= MINIX_PART; + break; + case O_KB: + level= 2; + if (mul == 0) pe->size= 0; /* new value, no surplus */ + case O_SIZE: + if (pe->sysind == NO_PART) { + if (op->type == O_KB || howend == SIZE) { + /* First let loose magic to set the base. */ + event('m', op); + magic= 0; + pe->size= 0; + event(ev, op); + return; + } + memset(pe, 0, sizeof(*pe)); + } + t= (op->type == O_KB || howend == SIZE) ? pe->size + : pe->lowsec + pe->size - 1; + surplus= t % level; + if (!lwithin(&t, 0L, + (t / level * mul / radix + delta) * level + surplus, + MAXSIZE)) return; + pe->size= (op->type == O_KB || howend == SIZE) ? t : + t - pe->lowsec + 1; + check_ind(pe); + if (pe->sysind == NO_PART) pe->sysind= MINIX_PART; + break; + default: + return; + } + + /* The order among the entries may have changed. */ + sort(); + dirty= 1; +} + +unsigned long spell[3 + 4 * (1+NR_PARTITIONS)]; +int nspells; +objtype_t touching; + +void newspell(unsigned long charm) +/* Add a new spell, descending order for the base, ascending for the size. */ +{ + int i, j; + + if (charm - table[0].lowsec > table[0].size) return; + + for (i= 0; i < nspells; i++) { + if (charm == spell[i]) return; /* duplicate */ + + if (touching == O_BASE) { + if (charm == table[0].lowsec + table[0].size) return; + if ((spell[0] - charm) < (spell[0] - spell[i])) break; + } else { + if (charm == table[0].lowsec) return; + if ((charm - spell[0]) < (spell[i] - spell[0])) break; + } + } + for (j= ++nspells; j > i; j--) spell[j]= spell[j-1]; + spell[i]= charm; +} + +void m_magic(int ev, object_t *op) +/* Apply magic onto a base or size number. */ +{ + struct part_entry *pe= op->entry, *pe2; + int rough= (offset != 0 && extbase == 0); + + if (ev != 'm' || device < 0) return; + typing= 0; + + if (!check_existing(pe)) return; + + if (magic == 0) { + /* See what magic we can let loose on this value. */ + nspells= 1; + + /* First spell, the current value. */ + switch (op->type) { + case O_SCYL: + case O_SHEAD: /* Start of partition. */ + case O_SSEC: + case O_BASE: + touching= O_BASE; + spell[0]= pe->lowsec; + break; + case O_LCYL: + case O_LHEAD: + case O_LSEC: /* End of partition. */ + case O_KB: + case O_SIZE: + touching= O_SIZE; + spell[0]= pe->lowsec + pe->size; + break; + default: + return; + } + if (pe->sysind == NO_PART) { + memset(pe, 0, sizeof(*pe)); + check_ind(pe); + pe->sysind= MINIX_PART; + spell[0]= 0; + if (touching == O_SIZE) { + /* First let loose magic on the base. */ + object_t *op2; + + for (op2= world; op2 != nil; op2= op2->next) { + if (op2->row == op->row && + op2->type == O_BASE) { + event('m', op2); + } + } + magic= 0; + event('m', op); + return; + } + } + /* Avoid the first sector on the device. */ + if (spell[0] == table[0].lowsec) newspell(spell[0] + 1); + + /* Further interesting values are the the bases of other + * partitions or their ends. + */ + for (pe2= table; pe2 < table + 1 + NR_PARTITIONS; pe2++) { + if (pe2 == pe || pe2->sysind == NO_PART) continue; + if (pe2->lowsec == table[0].lowsec) + newspell(table[0].lowsec + 1); + else + newspell(pe2->lowsec); + newspell(pe2->lowsec + pe2->size); + if (touching == O_BASE && howend == SIZE) { + newspell(pe2->lowsec - pe->size); + newspell(pe2->lowsec + pe2->size - pe->size); + } + if (pe2->lowsec % sectors != 0) rough= 1; + } + /* Present values rounded up to the next cylinder unless + * the table is already a mess. Use "start + 1 track" instead + * of "start + 1 cylinder". Also add the end of the last + * cylinder. + */ + if (!rough) { + unsigned long n= spell[0]; + if (n == table[0].lowsec) n++; + n= (n + sectors - 1) / sectors * sectors; + if (n != table[0].lowsec + sectors) + n= (n + secpcyl - 1) / secpcyl * secpcyl; + newspell(n); + if (touching == O_SIZE) + newspell(table[0].size / secpcyl * secpcyl); + } + } + /* Magic has been applied, a spell needs to be chosen. */ + + if (++magic == nspells) magic= 0; + + if (touching == O_BASE) { + if (howend == LAST) pe->size-= spell[magic] - pe->lowsec; + pe->lowsec= spell[magic]; + } else + pe->size= spell[magic] - pe->lowsec; + + /* The order among the entries may have changed. */ + sort(); + dirty= 1; +} + +typedef struct diving { + struct diving *up; + struct part_entry old0; + char *oldsubname; + parttype_t oldparttype; + unsigned long oldoffset; + unsigned long oldextbase; +} diving_t; + +diving_t *diving= nil; + +void m_in(int ev, object_t *op) +/* Go down into a primary or extended partition. */ +{ + diving_t *newdiv; + struct part_entry *pe= op->entry, ext; + int n; + + if (ev != '>' || device < 0 || pe == nil || pe == &table[0] + || (!(pe->sysind == MINIX_PART && offset == 0) + && !ext_part(pe->sysind)) + || pe->size == 0) return; + + ext= *pe; + if (extbase != 0) ext.size= extbase + extsize - ext.lowsec; + + if (dirty) event(E_WRITE, op); + if (dirty) return; + if (device >= 0) { close(device); device= -1; } + + newdiv= alloc(sizeof(*newdiv)); + newdiv->old0= table[0]; + newdiv->oldsubname= curdev->subname; + newdiv->oldparttype= curdev->parttype; + newdiv->oldoffset= offset; + newdiv->oldextbase= extbase; + newdiv->up= diving; + diving= newdiv; + + table[0]= ext; + + n= strlen(diving->oldsubname); + curdev->subname= alloc((n + 3) * sizeof(curdev->subname[0])); + strcpy(curdev->subname, diving->oldsubname); + curdev->subname[n++]= ':'; + curdev->subname[n++]= '0' + (pe - table - 1); + curdev->subname[n]= 0; + + curdev->parttype= curdev->parttype == PRIMARY ? SUBPART : DUNNO; + offset= ext.lowsec; + if (ext_part(ext.sysind) && extbase == 0) { + extbase= ext.lowsec; + extsize= ext.size; + curdev->parttype= DUNNO; + } + + submerged= 1; + event('r', op); +} + +void m_out(int ev, object_t *op) +/* Go up from an extended or subpartition table to its enclosing. */ +{ + diving_t *olddiv; + + if (ev != '<' || diving == nil) return; + + if (dirty) event(E_WRITE, op); + if (dirty) return; + if (device >= 0) { close(device); device= -1; } + + olddiv= diving; + diving= olddiv->up; + + table[0]= olddiv->old0; + + free(curdev->subname); + curdev->subname= olddiv->oldsubname; + + curdev->parttype= olddiv->oldparttype; + offset= olddiv->oldoffset; + extbase= olddiv->oldextbase; + + free(olddiv); + + event('r', op); + if (diving == nil) submerged= 0; /* We surfaced. */ +} + +void installboot(unsigned char *bootblock, char *masterboot) +/* Install code from a master bootstrap into a boot block. */ +{ + FILE *mfp; + struct exec hdr; + int n; + char *err; + + if ((mfp= fopen(masterboot, "r")) == nil) { + err= strerror(errno); + goto m_err; + } + + n= fread(&hdr, sizeof(char), A_MINHDR, mfp); + if (ferror(mfp)) { + err= strerror(errno); + fclose(mfp); + goto m_err; + } + + if (n < A_MINHDR || BADMAG(hdr) || hdr.a_cpu != A_I8086) { + err= "Not an 8086 executable"; + fclose(mfp); + goto m_err; + } + + if (hdr.a_text + hdr.a_data > PART_TABLE_OFF) { + err= "Does not fit in a boot sector"; + fclose(mfp); + goto m_err; + } + + fseek(mfp, hdr.a_hdrlen, 0); + fread(bootblock, sizeof(char), (size_t) (hdr.a_text + hdr.a_data), mfp); + if (ferror(mfp)) { + err= strerror(errno); + fclose(mfp); + goto m_err; + } + fclose(mfp); + + /* Bootstrap installed. */ + return; + + m_err: + stat_start(1); + printf("%s: %s", masterboot, err); + stat_end(5); +} + +ssize_t boot_readwrite(int rw) +/* Read (0) or write (1) the boot sector. */ +{ + u64_t off64 = mul64u(offset, SECTOR_SIZE); + int r; + +#if __minix_vmd + /* Minix-vmd has a 64 bit seek. */ + if (fcntl(device, F_SEEK, off64) < 0) return -1; +#else + /* Minix has to gross things with the partition base. */ + struct partition geom0, geom_seek; + + if (offset >= (LONG_MAX / SECTOR_SIZE - 1)) { + /* Move partition base. */ + if (ioctl(device, DIOCGETP, &geom0) < 0) return -1; + geom_seek.base = add64(geom0.base, off64); + geom_seek.size = cvu64(cmp64(add64u(off64, SECTOR_SIZE), + geom0.size) <= 0 ? STATIC_BLOCK_SIZE : 0); + sync(); + if (ioctl(device, DIOCSETP, &geom_seek) < 0) return -1; + if (lseek(device, (off_t) 0, SEEK_SET) == -1) return -1; + } else { + /* Can reach this point normally. */ + if (lseek(device, (off_t) offset * SECTOR_SIZE, SEEK_SET) == -1) + return -1; + } +#endif + + switch (rw) { + case 0: r= read(device, bootblock, SECTOR_SIZE); break; + case 1: r= write(device, bootblock, SECTOR_SIZE); break; + } + +#if !__minix_vmd + if (offset >= (LONG_MAX / SECTOR_SIZE - 1)) { + /* Restore partition base and size. */ + sync(); + if (ioctl(device, DIOCSETP, &geom0) < 0) return -1; + } +#endif + return r; +} + +void m_read(int ev, object_t *op) +/* Read the partition table from the current device. */ +{ + int i, mode, n; + struct part_entry *pe; + + if (ev != 'r' || device >= 0) return; + + /* Open() may cause kernel messages: */ + stat_start(0); + fflush(stdout); + + if (((device= open(curdev->name, mode= O_RDWR, 0666)) < 0 + && (errno != EACCES + || (device= open(curdev->name, mode= O_RDONLY)) < 0)) + ) { + stat_start(1); + printf("%s: %s", curdev->name, strerror(errno)); + stat_end(5); + if (device >= 0) { close(device); device= -1; } + return; + } + + /* Assume up to five lines of kernel messages. */ + statusrow+= 5-1; + stat_end(5); + + if (mode == O_RDONLY) { + stat_start(1); + printf("%s: Readonly", curdev->name); + stat_end(5); + } + memset(bootblock, 0, sizeof(bootblock)); + + n= boot_readwrite(0); + + if (n <= 0) stat_start(1); + if (n < 0) { + printf("%s: %s", curdev->name, strerror(errno)); + close(device); + device= -1; + } else + if (n < SECTOR_SIZE) printf("%s: Unexpected EOF", curdev->subname); + if (n <= 0) stat_end(5); + + if (n < SECTOR_SIZE) n= SECTOR_SIZE; + + memcpy(table+1, bootblock+PART_TABLE_OFF, + NR_PARTITIONS * sizeof(table[1])); + for (i= 1; i <= NR_PARTITIONS; i++) { + if ((table[i].bootind & ~ACTIVE_FLAG) != 0) break; + } + if (i <= NR_PARTITIONS || bootblock[510] != 0x55 + || bootblock[511] != 0xAA) { + /* Invalid boot block, install bootstrap, wipe partition table. + */ + memset(bootblock, 0, sizeof(bootblock)); + installboot(bootblock, MASTERBOOT); + memset(table+1, 0, NR_PARTITIONS * sizeof(table[1])); + stat_start(1); + printf("%s: Invalid partition table (reset)", curdev->subname); + stat_end(5); + } + + /* Fix an extended partition table up to something mere mortals can + * understand. Record already defined partitions. + */ + for (i= 1; i <= NR_PARTITIONS; i++) { + pe= &table[i]; + if (extbase != 0 && pe->sysind != NO_PART) + pe->lowsec+= ext_part(pe->sysind) ? extbase : offset; + existing[i]= pe->sysind != NO_PART; + } + geometry(); + dirty= 0; + + /* Warn about grave dangers ahead. */ + if (extbase != 0) { + stat_start(1); + printf("Warning: You are in an extended partition."); + stat_end(5); + } +} + +void m_write(int ev, object_t *op) +/* Write the partition table back if modified. */ +{ + int c; + struct part_entry new_table[NR_PARTITIONS], *pe; + + if (ev != 'w' && ev != E_WRITE) return; + if (device < 0) { dirty= 0; return; } + if (!dirty) { + if (ev == 'w') { + stat_start(1); + printf("%s is not changed, or has already been written", + curdev->subname); + stat_end(2); + } + return; + } + + if (bootblock[510] != 0x55 || bootblock[511] != 0xAA) { + /* Invalid boot block, warn user. */ + stat_start(1); + printf("Warning: About to write a new table on %s", + curdev->subname); + stat_end(5); + } + if (extbase != 0) { + /* Will this stop the luser? Probably not... */ + stat_start(1); + printf("You have changed an extended partition. Bad Idea."); + stat_end(5); + } + stat_start(1); + putstr("Save partition table? (y/n) "); + fflush(stdout); + + while ((c= getchar()) != 'y' && c != 'n' && c != ctrl('?')) {} + + if (c == ctrl('?')) putstr("DEL"); else putchr(c); + stat_end(5); + if (c == 'n' && ev == E_WRITE) dirty= 0; + if (c != 'y') return; + + memcpy(new_table, table+1, NR_PARTITIONS * sizeof(table[1])); + for (pe= new_table; pe < new_table + NR_PARTITIONS; pe++) { + if (pe->sysind == NO_PART) { + memset(pe, 0, sizeof(*pe)); + } else { + abs2dos(&pe->start_head, pe->lowsec); + abs2dos(&pe->last_head, pe->lowsec + pe->size - 1); + + /* Fear and loathing time: */ + if (extbase != 0) + pe->lowsec-= ext_part(pe->sysind) + ? extbase : offset; + } + } + memcpy(bootblock+PART_TABLE_OFF, new_table, sizeof(new_table)); + bootblock[510]= 0x55; + bootblock[511]= 0xAA; + + if (boot_readwrite(1) < 0) { + stat_start(1); + printf("%s: %s", curdev->name, strerror(errno)); + stat_end(5); + return; + } + dirty= 0; +} + +void m_shell(int ev, object_t *op) +/* Shell escape, to do calculations for instance. */ +{ + int r, pid, status; + void (*sigint)(int), (*sigquit)(int), (*sigterm)(int); + + if (ev != 's') return; + + reset_tty(); + fflush(stdout); + + switch (pid= fork()) { + case -1: + stat_start(1); + printf("can't fork: %s\n", strerror(errno)); + stat_end(3); + break; + case 0: + if (device >= 0) (void) close(device); + execl("/bin/sh", "sh", (char *) nil); + r= errno; + stat_start(1); + printf("/bin/sh: %s\n", strerror(errno)); + stat_end(3); + exit(127); + } + sigint= signal(SIGINT, SIG_IGN); + sigquit= signal(SIGQUIT, SIG_IGN); + sigterm= signal(SIGTERM, SIG_IGN); + while (pid >= 0 && (r= wait(&status)) >= 0 && r != pid) {} + (void) signal(SIGINT, sigint); + (void) signal(SIGQUIT, sigquit); + (void) signal(SIGTERM, sigterm); + tty_raw(); + if (pid < 0) + ; + else + if (WIFEXITED(status) && WEXITSTATUS(status) == 127) + stat_start(0); /* Match the stat_start in the child. */ + else + event(ctrl('L'), op); +} + +void m_dump(int ev, object_t *op) +/* Raw dump of the partition table. */ +{ + struct part_entry table[NR_PARTITIONS], *pe; + int i; + unsigned chs[3]; + + if (ev != 'p' || device < 0) return; + + memcpy(table, bootblock+PART_TABLE_OFF, + NR_PARTITIONS * sizeof(table[0])); + for (i= 0; i < NR_PARTITIONS; i++) { + pe= &table[i]; + stat_start(0); + dos2chs(&pe->start_head, chs); + printf("%2d%c %02X%15d%5d%4d", + i+1, + pe->bootind & ACTIVE_FLAG ? '*' : ' ', + pe->sysind, + chs[0], chs[1], chs[2]); + dos2chs(&pe->last_head, chs); + printf("%6d%5d%4d%10lu%10ld%9lu", + chs[0], chs[1], chs[2], + pe->lowsec, + howend == SIZE ? pe->size : pe->size + pe->lowsec - 1, + pe->size / 2); + stat_end(10); + } + stat_start(0); + printf("(Raw dump of the original %.40s table)", + curdev->subname); + stat_end(10); +} + +int quitting= 0; + +void m_quit(int ev, object_t *op) +/* Write the partition table if modified and exit. */ +{ + if (ev != 'q' && ev != 'x') return; + + quitting= 1; + + if (dirty) event(E_WRITE, op); + if (dirty) quitting= 0; +} + +void m_help(int ev, object_t *op) +/* For people without a clue; let's hope they can find the '?' key. */ +{ + static struct help { + char *keys; + char *what; + } help[]= { + { "? !", "This help / more advice!" }, + { "+ - (= _ PgUp PgDn)","Select/increment/decrement/make active" }, + { "0-9 (a-f)", "Enter value" }, + { "hjkl (arrow keys)", "Move around" }, + { "CTRL-K CTRL-J", "Move entry up/down" }, + { "CTRL-L", "Redraw screen" }, + { ">", "Start a subpartition table" }, + { "<", "Back to the primary partition table" }, + { "m", "Cycle through magic values" }, + { "spacebar", "Show \"Size\" or \"Last\"" }, + { "r w", "Read/write partition table" }, + { "p s q x", "Raw dump / Shell escape / Quit / Exit" }, + { "y n DEL", "Answer \"yes\", \"no\", \"cancel\"" }, + }; + static char *advice[] = { +"* Choose a disk with '+' and '-', then hit 'r'.", +"* To change any value: Move to it and use '+', '-' or type the desired value.", +"* To make a new partition: Move over to the Size or Kb field of an unused", +" partition and type the size. Hit the 'm' key to pad the partition out to", +" a cylinder boundary. Hit 'm' again to pad it out to the end of the disk.", +" You can hit 'm' more than once on a base or size field to see several", +" interesting values go by. Note: Other Operating Systems can be picky about", +" partitions that are not padded to cylinder boundaries. Look for highlighted", +" head or sector numbers.", +"* To reuse a partition: Change the type to MINIX.", +"* To delete a partition: Type a zero in the hex Type field.", +"* To make a partition active: Type '+' in the Num field.", +"* To study the list of keys: Type '?'.", + }; + + if (ev == '?') { + struct help *hp; + + for (hp= help; hp < arraylimit(help); hp++) { + stat_start(0); + printf("%-25s - %s", hp->keys, hp->what); + stat_end(0); + } + stat_start(0); + putstr("Things like "); + putstr(t_so); putstr("this"); putstr(t_se); + putstr(" must be checked, but "); + putstr(t_md); putstr("this"); putstr(t_me); + putstr(" is not really a problem"); + stat_end(0); + } else + if (ev == '!') { + char **ap; + + for (ap= advice; ap < arraylimit(advice); ap++) { + stat_start(0); + putstr(*ap); + stat_end(0); + } + } +} + +void event(int ev, object_t *op) +/* Simply call all modifiers for an event, each one knows when to act. */ +{ + m_help(ev, op); + m_redraw(ev, op); + m_toggle(ev, op); + m_orientation(ev, op); + m_move(ev, op); + m_updown(ev, op); + m_enter(ev, op); + m_leave(ev, op); + m_modify(ev, op); + m_magic(ev, op); + m_in(ev, op); + m_out(ev, op); + m_read(ev, op); + m_write(ev, op); + m_shell(ev, op); + m_dump(ev, op); + m_quit(ev, op); +} + +int keypress(void) +/* Get a single keypress. Translate compound keypresses (arrow keys) to + * their simpler equivalents. + */ +{ + char ch; + int c; + int esc= 0; + + set_cursor(curobj->row, curobj->col); + fflush(stdout); + + do { + if (read(0, &ch, sizeof(ch)) < 0) fatal("stdin"); + c= (unsigned char) ch; + switch (esc) { + case 0: + switch (c) { + case ctrl('['): esc= 1; break; + case '_': c= '-'; break; + case '=': c= '+'; break; + } + break; + case 1: + esc= c == '[' ? 2 : 0; + break; + case 2: + switch (c) { + case 'D': c= 'h'; break; + case 'B': c= 'j'; break; + case 'A': c= 'k'; break; + case 'C': c= 'l'; break; + case 'H': c= 'H'; break; + case 'U': + case 'S': c= '-'; break; + case 'V': + case 'T': c= '+'; break; + } + /*FALL THROUGH*/ + default: + esc= 0; + } + } while (esc > 0); + + switch (c) { + case ctrl('B'): c= 'h'; break; + case ctrl('N'): c= 'j'; break; + case ctrl('P'): c= 'k'; break; + case ctrl('F'): c= 'l'; break; + } + + return c; +} + +void mainloop(void) +/* Get keypress, handle event, display results, reset screen, ad infinitum. */ +{ + int key; + + while (!quitting) { + stat_reset(); + + key= keypress(); + + event(key, curobj); + + display(); + } +} + +int main(int argc, char **argv) +{ + object_t *op; + int i, r, key; + struct part_entry *pe; + + /* Define a few objects to show on the screen. First text: */ + op= newobject(O_INFO, 0, 0, 2, 19); + op= newobject(O_TEXT, 0, 0, 22, 13); op->text= "----first----"; + op= newobject(O_TEXT, 0, 0, 37, 13); op->text= "--geom/last--"; + op= newobject(O_TEXT, 0, 0, 52, 18); op->text= "------sectors-----"; + op= newobject(O_TEXT, 0, 1, 4, 6); op->text= "Device"; + op= newobject(O_TEXT, 0, 1, 23, 12); op->text= "Cyl Head Sec"; + op= newobject(O_TEXT, 0, 1, 38, 12); op->text= "Cyl Head Sec"; + op= newobject(O_TEXT, 0, 1, 56, 4); op->text= "Base"; + op= newobject(O_TEXT, 0, 1, 66, 4); op->text= size_last; + op= newobject(O_TEXT, 0, 1, 78, 2); op->text= "Kb"; + op= newobject(O_TEXT, 0, 4, 0, 15); op->text= "Num Sort Type"; + + /* The device is the current object: */ + curobj= newobject(O_DEV, OF_MOD, 2, 4, 15); + op= newobject(O_SUB, 0, 3, 4, 15); + + /* Geometry: */ + op= newobject(O_CYL, OF_MOD, 2, 40, 5); op->entry= &table[0]; + op= newobject(O_HEAD, OF_MOD, 2, 45, 3); op->entry= &table[0]; + op= newobject(O_SEC, OF_MOD, 2, 49, 2); op->entry= &table[0]; + + /* Objects for the device: */ + op= newobject(O_SCYL, 0, 3, 25, 5); op->entry= &table[0]; + op= newobject(O_SHEAD, 0, 3, 30, 3); op->entry= &table[0]; + op= newobject(O_SSEC, 0, 3, 34, 2); op->entry= &table[0]; + op= newobject(O_LCYL, 0, 3, 40, 5); op->entry= &table[0]; + op= newobject(O_LHEAD, 0, 3, 45, 3); op->entry= &table[0]; + op= newobject(O_LSEC, 0, 3, 49, 2); op->entry= &table[0]; + op= newobject(O_BASE, 0, 3, 59, 9); op->entry= &table[0]; + op= newobject(O_SIZE, 0, 3, 69, 9); op->entry= &table[0]; + op= newobject(O_KB, 0, 3, 79, 9); op->entry= &table[0]; + + /* Objects for each partition: */ + for (r= 5, pe= table+1; pe <= table+NR_PARTITIONS; r++, pe++) { + op= newobject(O_NUM, OF_MOD, r, 1, 2); op->entry= pe; + op= newobject(O_SORT, 0, r, 5, 2); op->entry= pe; + op= newobject(O_TYPHEX, OF_MOD, r, 10, 2); op->entry= pe; + op= newobject(O_TYPTXT, OF_MOD, r, 12, 9); op->entry= pe; + op= newobject(O_SCYL, OF_MOD, r, 25, 5); op->entry= pe; + op= newobject(O_SHEAD, OF_MOD, r, 30, 3); op->entry= pe; + op= newobject(O_SSEC, OF_MOD, r, 34, 2); op->entry= pe; + op= newobject(O_LCYL, OF_MOD, r, 40, 5); op->entry= pe; + op= newobject(O_LHEAD, OF_MOD, r, 45, 3); op->entry= pe; + op= newobject(O_LSEC, OF_MOD, r, 49, 2); op->entry= pe; + op= newobject(O_BASE, OF_MOD, r, 59, 9); op->entry= pe; + op= newobject(O_SIZE, OF_MOD, r, 69, 9); op->entry= pe; + op= newobject(O_KB, OF_MOD, r, 79, 9); op->entry= pe; + } + + for (i= 1; i < argc; i++) newdevice(argv[i], 0); + + if (firstdev == nil) { + getdevices(); + key= ctrl('L'); + } else { + key= 'r'; + } + + if (firstdev != nil) { + init_tty(); + clear_screen(); + event(key, curobj); + display(); + mainloop(); + reset_tty(); + } + exit(0); +} diff --git a/commands/ibm/partition.c b/commands/ibm/partition.c new file mode 100755 index 000000000..bcb90ec09 --- /dev/null +++ b/commands/ibm/partition.c @@ -0,0 +1,425 @@ +/* partition 1.13 - Make a partition table Author: Kees J. Bot + * 27 Apr 1992 + */ +#define nil ((void*)0) +#include <stdio.h> +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <minix/config.h> +#include <minix/const.h> +#include <minix/partition.h> +#include <minix/u64.h> +#include <ibm/partition.h> +#include <sys/stat.h> +#include <string.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <limits.h> + +#define SECTOR_SIZE 512 + +#define arraysize(a) (sizeof(a)/sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +char *arg0; + +void report(const char *label) +{ + fprintf(stderr, "%s: %s: %s\n", arg0, label, strerror(errno)); +} + +void fatal(const char *label) +{ + report(label); + exit(1); +} + +#ifndef makedev +#define minor(dev) (((dev) >> MINOR) & BYTE) +#define major(dev) (((dev) >> MAJOR) & BYTE) +#define makedev(major, minor) \ + ((dev_t) (((major) << MAJOR) | ((minor) << MINOR))) +#endif + +int aflag; /* Add a new partition to the current table. */ +int mflag; /* Minix rules, no need for alignment. */ +int rflag; /* Report current partitions. */ +int fflag; /* Force making a table even if too small. */ +int nflag; /* Play-act, don't really do it. */ + +int cylinders, heads, sectors; /* Device's geometry */ +int pad; /* Partitions must be padded. */ + +/* Descriptions of the device to divide and the partitions to make, including + * gaps between partitions. + */ +char *device; +struct part_entry primary, table[2 * NR_PARTITIONS + 1]; +int npart; + +/* Extra flags at construction time. */ +#define EXPAND_FLAG 0x01 /* Add the remaining sectors to this one */ +#define EXIST_FLAG 0x02 /* Use existing partition */ + +void find_exist(struct part_entry *exist, int sysind, int nr) +{ + int f; + u16_t signature; + struct part_entry oldtable[NR_PARTITIONS]; + int n, i; + u32_t minlow, curlow; + struct part_entry *cur; + char *nr_s[] = { "", "second ", "third ", "fourth" }; + + if ((f= open(device, O_RDONLY)) < 0 + + || lseek(f, (off_t) PART_TABLE_OFF, SEEK_SET) == -1 + + || read(f, oldtable, sizeof(oldtable)) < 0 + + || read(f, &signature, sizeof(signature)) < 0 + + || close(f) < 0 + ) fatal(device); + + minlow= 0; + n= 0; + for (;;) { + curlow= -1; + cur= nil; + for (i= 0; i < NR_PARTITIONS; i++) { + if (signature == 0xAA55 + && oldtable[i].sysind != NO_PART + && oldtable[i].lowsec >= minlow + && oldtable[i].lowsec < curlow + ) { + cur= &oldtable[i]; + curlow= oldtable[i].lowsec; + } + } + if (n == nr) break; + n++; + minlow= curlow+1; + } + + if (cur == nil || cur->sysind != sysind) { + fprintf(stderr, + "%s: Can't find a %sexisting partition of type 0x%02X\n", + arg0, nr_s[nr], sysind); + exit(1); + } + *exist = *cur; +} + +void write_table(void) +{ + int f; + u16_t signature= 0xAA55; + struct part_entry newtable[NR_PARTITIONS]; + int i; + + if (nflag) { + printf("(Table not written)\n"); + return; + } + + for (i= 0; i < NR_PARTITIONS; i++) newtable[i]= table[1 + 2*i]; + + if ((f= open(device, O_WRONLY)) < 0 + + || lseek(f, (off_t) PART_TABLE_OFF, SEEK_SET) == -1 + + || write(f, newtable, sizeof(newtable)) < 0 + + || write(f, &signature, sizeof(signature)) < 0 + + || close(f) < 0 + ) fatal(device); +} + +void sec2dos(unsigned long sec, unsigned char *dos) +/* Translate a sector number into the three bytes DOS uses. */ +{ + unsigned secspcyl= heads * sectors; + unsigned cyl; + + cyl= sec / secspcyl; + dos[2]= cyl; + dos[1]= ((sec % sectors) + 1) | ((cyl >> 2) & 0xC0); + dos[0]= (sec % secspcyl) / sectors; +} + +void show_chs(unsigned long pos) +{ + int cyl, head, sec; + + if (pos == -1) { + cyl= head= 0; + sec= -1; + } else { + cyl= pos / (heads * sectors); + head= (pos / sectors) - (cyl * heads); + sec= pos % sectors; + } + printf(" %4d/%03d/%02d", cyl, head, sec); +} + +void show_part(struct part_entry *p) +{ + static int banner= 0; + int n; + + n= p - table; + if ((n % 2) == 0) return; + + if (!banner) { + printf( + "Part First Last Base Size Kb\n"); + banner= 1; + } + + printf("%3d ", (n-1) / 2); + show_chs(p->lowsec); + show_chs(p->lowsec + p->size - 1); + printf(" %8lu %8lu %7lu\n", p->lowsec, p->size, p->size / 2); +} + +void usage(void) +{ + fprintf(stderr, + "Usage: partition [-mfn] device [type:]length[+*] ...\n"); + exit(1); +} + +#define between(a, c, z) ((unsigned) ((c) - (a)) <= ((z) - (a))) + +void parse(char *descr) +{ + int seen= 0, sysind, flags, c; + unsigned long lowsec, size; + + lowsec= 0; + + if (strchr(descr, ':') == nil) { + /* A hole. */ + if ((npart % 2) != 0) { + fprintf(stderr, "%s: Two holes can't be adjacent.\n", + arg0); + exit(1); + } + sysind= NO_PART; + seen|= 1; + } else { + /* A partition. */ + if ((npart % 2) == 0) { + /* Need a hole before this partition. */ + if (npart == 0) { + /* First hole contains the partition table. */ + table[0].size= 1; + } + npart++; + } + sysind= 0; + for (;;) { + c= *descr++; + if (between('0', c, '9')) + c= (c - '0') + 0x0; + else + if (between('a', c, 'z')) + c= (c - 'a') + 0xa; + else + if (between('A', c, 'Z')) + c= (c - 'A') + 0xA; + else + break; + sysind= 0x10 * sysind + c; + seen|= 1; + } + if (c != ':') usage(); + } + + flags= 0; + + if (strncmp(descr, "exist", 5) == 0 && (npart % 2) == 1) { + struct part_entry exist; + + find_exist(&exist, sysind, (npart - 1) / 2); + sysind= exist.sysind; + lowsec= exist.lowsec; + size= exist.size; + flags |= EXIST_FLAG; + descr += 5; + c= *descr++; + seen|= 2; + } else { + size= 0; + while (between('0', (c= *descr++), '9')) { + size= 10 * size + (c - '0'); + seen|= 2; + } + } + + for (;;) { + if (c == '*') + flags|= ACTIVE_FLAG; + else + if (c == '+' && !(flags & EXIST_FLAG)) + flags|= EXPAND_FLAG; + else + break; + c= *descr++; + } + + if (seen != 3 || c != 0) usage(); + + if (npart == arraysize(table)) { + fprintf(stderr, "%s: too many partitions, only %d possible.\n", + arg0, NR_PARTITIONS); + exit(1); + } + table[npart].bootind= flags; + table[npart].sysind= sysind; + table[npart].lowsec= lowsec; + table[npart].size= size; + npart++; +} + +void geometry(void) +/* Get the geometry of the drive the device lives on, and the base and size + * of the device. + */ +{ + int fd; + struct partition geometry; + + if ((fd= open(device, O_RDONLY)) < 0) fatal(device); + + /* Get the geometry of the drive, and the device's base and size. */ + if (ioctl(fd, DIOCGETP, &geometry) < 0) fatal(device); + close(fd); + primary.lowsec= div64u(geometry.base, SECTOR_SIZE); + primary.size= div64u(geometry.size, SECTOR_SIZE); + cylinders= geometry.cylinders; + heads= geometry.heads; + sectors= geometry.sectors; + + /* Is this a primary partition table? If so then pad partitions. */ + pad= (!mflag && primary.lowsec == 0); +} + +void boundary(struct part_entry *pe, int exp) +/* Expand or reduce a primary partition to a track or cylinder boundary to + * avoid giving the fdisk's of simpler operating systems a fit. + */ +{ + unsigned n; + + n= !pad ? 1 : pe == &table[0] ? sectors : heads * sectors; + if (exp) pe->size+= n - 1; + pe->size= ((pe->lowsec + pe->size) / n * n) - pe->lowsec; +} + +void distribute(void) +/* Fit the partitions onto the device. Try to start and end them on a + * cylinder boundary if so required. The first partition is to start on + * track 1, not on cylinder 1. + */ +{ + struct part_entry *pe, *exp; + long count; + unsigned long base, size, oldbase; + + do { + exp= nil; + base= primary.lowsec; + count= primary.size; + + for (pe= table; pe < arraylimit(table); pe++) { + oldbase= base; + if (pe->bootind & EXIST_FLAG) { + if (base > pe->lowsec) { + fprintf(stderr, + "%s: fixed partition %d is preceded by too big partitions/holes\n", + arg0, ((pe - table) - 1) / 2); + exit(1); + } + exp= nil; /* XXX - Extend before? */ + } else { + pe->lowsec= base; + boundary(pe, 1); + if (pe->bootind & EXPAND_FLAG) exp= pe; + } + base= pe->lowsec + pe->size; + count-= base - oldbase; + } + if (count < 0) { + if (fflag) break; + fprintf(stderr, "%s: %s is %ld sectors too small\n", + arg0, device, -count); + exit(1); + } + if (exp != nil) { + /* Add leftover space to the partition marked for + * expanding. + */ + exp->size+= count; + boundary(exp, 0); + exp->bootind&= ~EXPAND_FLAG; + } + } while (exp != nil); + + for (pe= table; pe < arraylimit(table); pe++) { + if (pe->sysind == NO_PART) { + memset(pe, 0, sizeof(*pe)); + } else { + sec2dos(pe->lowsec, &pe->start_head); + sec2dos(pe->lowsec + pe->size - 1, &pe->last_head); + pe->bootind&= ACTIVE_FLAG; + } + show_part(pe); + } +} + +int main(int argc, char **argv) +{ + int i; + + if ((arg0= strrchr(argv[0], '/')) == nil) arg0= argv[0]; else arg0++; + + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++] + 1; + + if (opt[0] == '-' && opt[1] == 0) break; + + while (*opt != 0) switch (*opt++) { + case 'a': aflag= 1; break; + case 'm': mflag= 1; break; + case 'r': rflag= 1; break; + case 'f': fflag= 1; break; + case 'n': nflag= 1; break; + default: usage(); + } + } + + if (rflag) { + if (aflag) usage(); + if ((argc - i) != 1) usage(); + fprintf(stderr, "%s: -r is not yet implemented\n"); + exit(1); + } else { + if ((argc - i) < 1) usage(); + if (aflag) fprintf(stderr, "%s: -a is not yet implemented\n"); + + device= argv[i++]; + geometry(); + + while (i < argc) parse(argv[i++]); + + distribute(); + write_table(); + } + exit(0); +} diff --git a/commands/ibm/playwave.c b/commands/ibm/playwave.c new file mode 100755 index 000000000..b369fd741 --- /dev/null +++ b/commands/ibm/playwave.c @@ -0,0 +1,201 @@ +/* + * playwave.c + * + * Play sound files in wave format. Only MicroSoft PCM is supported. + * + * Michel R. Prevenier. + */ + +#include <sys/types.h> +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <sys/ioctl.h> +#include <minix/sound.h> + +_PROTOTYPE( void main, (int argc, char **argv)); +_PROTOTYPE( void usage, (void)); + +/******* Wave format definitions *********/ + +#define RIFF_ID 0x46464952 +#define WAVE_ID1 0x45564157 +#define WAVE_ID2 0x20746D66 +#define DATA_ID 0x61746164 +#define MS_PCM_FORMAT 0x0001 + +#define WORD short +#define DWORD unsigned long + +struct RIFF_fields +{ + DWORD RIFF_id; + DWORD RIFF_len; + DWORD WAVE_id1; + DWORD WAVE_id2; + DWORD data_ptr; +} r_fields; + +struct common_fields +{ + WORD FormatTag; + WORD Channels; + DWORD SamplesPerSec; + DWORD AvgBytesPerSec; + WORD BlockAlign; +} c_fields; + +struct specific_fields +{ + WORD BitsPerSample; +} s_fields; + +DWORD data_id; +DWORD data_len; + +/******** End of wave definitions *********/ + + +void usage() +{ + fprintf(stderr, "Usage: playwav [-i] file\n"); + exit(-1); +} + + +void main ( int argc, char *argv[] ) +{ + int i, audio, file; + char *buffer, *file_name; + unsigned int sign; + unsigned int fragment_size; + unsigned int channels; + unsigned int bits; + long data_pos; + int showinfo = 0; + + /* Check Parameters */ + if (argc > 2) + { + if (strncmp(argv[1], "-i", 2) == 0) + { + showinfo = 1; + file_name = argv[2]; + } + else + usage(); + } + else file_name = argv[1]; + + /* Open DSP */ + if ((audio = open("/dev/audio", O_RDWR)) < 0) + { + printf("Cannot open /dev/audio\n"); + exit(-1); + } + + /* Get maximum fragment size and try to allocate a buffer */ + ioctl(audio, DSPIOMAX, &fragment_size); + if ((buffer = malloc(fragment_size)) == (char *)0) + { + fprintf(stderr, "Cannot allocate buffer\n"); + exit(-1); + } + ioctl(audio, DSPIOSIZE, &fragment_size); + + /* Open wav file */ + if((file = open(file_name, O_RDONLY)) < 0) + { + printf("Cannot open %s\n", file_name); + exit(-1); + } + + /* Check for valid wave format */ + read(file, &r_fields, 20); + if(r_fields.RIFF_id != RIFF_ID) + { + printf("%s not in RIFF format\n", file_name); + exit(1); + } + if(r_fields.WAVE_id1 != WAVE_ID1 || r_fields.WAVE_id2 != WAVE_ID2) + { + printf("%s not in WAVE format\n", file_name); + exit(1); + } + + /* Store data_chunk position */ + data_pos = lseek(file, 0L, 1) + r_fields.data_ptr; + + /* Read the common and specific fields */ + read(file, &c_fields, 14); + read(file, &s_fields, 2); + + /* Check for valid wave format, we can only play MicroSoft PCM */ + if(c_fields.FormatTag != MS_PCM_FORMAT) + { + printf("%s not in MicroSoft PCM format\n", file_name); + exit(1); + } + + /* Set DSP parameters */ + channels = c_fields.Channels; + channels--; + bits = s_fields.BitsPerSample; + ioctl(audio, DSPIOSTEREO, &channels); + ioctl(audio, DSPIORATE, &c_fields.SamplesPerSec); + ioctl(audio, DSPIOBITS, &bits); + sign = (bits == 16 ? 1 : 0); + ioctl(audio, DSPIOSIGN, &sign); + + /* Goto data chunk */ + lseek(file, data_pos, SEEK_SET); + + /* Check for valid data chunk */ + read(file, &data_id, sizeof(data_id)); + if(data_id != DATA_ID) + { + printf("Invalid data chunk\n"); + exit(1); + } + + /* Get length of data */ + read(file, &data_len, sizeof(data_len)); + + if (showinfo) + { + printf("\nBits per sample : %d \n", s_fields.BitsPerSample); + printf("Stereo : %s \n", (c_fields.Channels == 1 ? "yes" : "no")); + printf("Samples per second: %ld \n", c_fields.SamplesPerSec); + printf("Average bytes/sec : %ld \n", c_fields.AvgBytesPerSec); + printf("Block alignment : %d \n", c_fields.BlockAlign); + printf("Datalength (bytes): %ld \n\n", data_len); + } + + /* Play data */ + while(data_len > 0) + { + if (data_len > fragment_size) + { + /* Read next fragment */ + read(file, buffer, fragment_size); + data_len-= fragment_size; + } + else + { + /* Read until end of file and fill rest of buffer with silence, + * in PCM this means: fill buffer with last played value + */ + read(file, buffer, data_len); + for (i = data_len; i< fragment_size; i++) + buffer[i] = buffer[(int)data_len-1]; + data_len = 0; + } + + /* Copy data to DSP */ + write(audio, buffer, fragment_size); + } +} diff --git a/commands/ibm/postmort.c b/commands/ibm/postmort.c new file mode 100755 index 000000000..b113c4cee --- /dev/null +++ b/commands/ibm/postmort.c @@ -0,0 +1,654 @@ +/* postmort - post mortem dump Author: C. W. Rose */ + +/* Postmort: perform post-mortem on PC Minix 1.7 core files. + * + */ + + /* The 1.5 core file structure is a struct mem_map, the segment memory map, + * followed by a struct proc, the process table, followed by a dump of the + * text, data, and stack segments. + * + * This is the 8086/Intel version; 386 and 68K will differ. It defaults to + * using the name 'core' for the core file, and 'a.out' for the symbol file. + * If there is no 'a.out', it will try and read the symbol table from + * 'symbol.out', then give up. A non-existant symbol table is not a fatal + * error unless the -s option was used. + * + * The PC 1.5 kernel dump routines are odd - they dump the memory maps twice, + * the second time as part of the kernel process table, and the kernel + * process table size must be a multiple of 4. Should a core file have a + * header with a magic number in future? + * + * The kernel include file paths need to be edited for each machine. */ + +#include <sys/types.h> +#include <minix/config.h> +#include <minix/const.h> +#include <minix/type.h> +#include <minix/ipc.h> +#include <limits.h> +#include <timers.h> +#include <signal.h> +#include <stdlib.h> + +#undef EXTERN /* <minix/const.h> defined this */ +#define EXTERN /* so we get proc & mproc */ +#include "../../kernel/const.h" +#include "../../kernel/type.h" +#include "../../kernel/proc.h" +#undef printf /* kernel's const.h defined this */ +#include "../../servers/mm/mproc.h" + +#include <a.out.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#undef NULL +#include <string.h> +#include <unistd.h> + +#define FALSE 0 +#undef TRUE +#define TRUE ~FALSE +#define OK 1 +#define FAILED -1 + +#define CORE "core" +#define AOUT "a.out" +#define SYMB "symbol.out" +#define LINE_LEN 16 +#define MAXSYM 200 +#define SYMLEN 8 + +/* Global variables */ +int opt_c = FALSE; /* name of core file */ +int opt_d = FALSE; /* dump raw data and stack segments */ +int opt_p = FALSE; /* dump the kernel process table */ +int opt_s = FALSE; /* name of symbol file */ +int opt_t = FALSE; /* trace back the stack */ +int opt_x = FALSE; /* debugging flag */ + +char progname[20]; /* program name */ +char *segment_name[] = { /* array of segment names */ + "Text", + "Data", + "Stack" +}; + +int dbglvl = 0; /* debugging level */ +int maxsym; /* maximum symbol number */ +unsigned int baseptr; /* reference copy of stack base pointer */ +unsigned int stackptr; /* reference copy of stack pointer */ +long int lengths[NR_LOCAL_SEGS]; /* segment lengths */ +long int bases[NR_LOCAL_SEGS]; /* segment base addresses */ + +struct sym { /* symbol table addresses and labels */ + unsigned int addr; + char label[SYMLEN + 1]; +} symtab[MAXSYM]; + +/* Used by getopt(3) package */ +extern int optind, opterr, optopt; +extern char *optarg; + +_PROTOTYPE(int binary, (int uc, char *sp)); +_PROTOTYPE(void dump_all_segs, (int fd)); +_PROTOTYPE(void dump_maps, (struct mem_map * mp)); +_PROTOTYPE(void dump_one_seg, (int fd, int segindex)); +_PROTOTYPE(void dump_proc_table, (struct proc * pt)); +_PROTOTYPE(void dump_registers, (struct proc * pt)); +_PROTOTYPE(void dump_sym_tab, (struct sym *st)); +_PROTOTYPE(void dump_stack, (struct stackframe_s * sp)); +_PROTOTYPE(int main, (int argc, char *argv[])); +_PROTOTYPE(int parse_line, (char *ps)); +_PROTOTYPE(int read_symbol, (int fd)); +_PROTOTYPE(void stack_trace, (int fd)); +_PROTOTYPE(void usage, (void)); + + +/* B i n a r y + * + * Produce a binary representation of an 8-bit number. + */ +int binary(ucc, sp) +int ucc; +char *sp; +{ + int j; + unsigned char k, uc; + + uc = (unsigned char) ucc; + for (k = 0x80, j = 0; j < 8; j++) { + if ((uc & k) == 0) + *sp++ = '0'; + else + *sp++ = '1'; + if (j == 3) *sp++ = '$'; + k >>= 1; + } + *sp = '\0'; + + return(0); +} + + +/* D u m p _ a l l _ s e g s + * + * Dump all the segments except for text + */ +void dump_all_segs(fd) +int fd; +{ + int j; + long int start; + + start = (long) (NR_LOCAL_SEGS * sizeof(struct mem_map)) + sizeof(struct proc); + for (j = 1; j < NR_LOCAL_SEGS; j++) { + start += lengths[j - 1]; + (void) lseek(fd, start, 0); + printf("\n"); + dump_one_seg(fd, j); + } +} + + +/* D u m p _ m a p s + * + * Dump the memory maps + */ +void dump_maps(mp) +struct mem_map *mp; +{ + int j; + long int vir, phy, len; + + printf("\t Virtual\t Physical\tLength\n"); + printf("\t address\t address\n"); + for (j = 0; j < NR_LOCAL_SEGS; j++) { + vir = (long) mp[j].mem_vir << CLICK_SHIFT; + phy = (long) mp[j].mem_phys << CLICK_SHIFT; + len = (long) mp[j].mem_len << CLICK_SHIFT; + printf("%s:\t0x%08.8lx\t0x%08.8lx\t%8ld (0x%08.8lx)\n", + segment_name[j], vir, phy, len, len); + lengths[j] = len; + bases[j] = vir; + } +} + + +/* D u m p _ o n e _ s e g + * + * Dump a single segment + */ +void dump_one_seg(fd, segindex) +int fd, segindex; +{ + unsigned char dlen[LINE_LEN]; + int i, amt, amt_read; + long int len, offset; + + printf("%s segment\n\n", segment_name[segindex]); + len = lengths[segindex]; + amt = LINE_LEN; + for (offset = 0; offset < len; offset += amt) { + if ((len - offset) < LINE_LEN) amt = (int) (len - offset); + if (dbglvl > 0) + printf("Length %ld, offset %ld, amt %d\n", len, offset, amt); + if ((amt_read = read(fd, (char *) dlen, (unsigned int) amt)) == -1) { + printf("Unexpected end of file\n"); + exit(1); + } + printf("%08.8lx: ", bases[segindex] + offset); + for (i = 0; i < amt_read; i++) { + if (i == LINE_LEN / 2) printf("- "); + printf("%02.2x ", dlen[i]); + } + printf(" "); + for (i = 0; i < amt_read; i++) { + if (isprint(dlen[i])) + (void) putchar((char) dlen[i]); + else + (void) putchar('.'); + } + (void) putchar('\n'); + if (dbglvl > 0 && amt_read != amt) + printf("wanted = %d, got = %d, offset = %ld\n", + amt, amt_read, offset); + } +} + + +/* D u m p _ p r o c _ t a b l e + * + * Dump the entire kernel proc table + */ +void dump_proc_table(pt) +struct proc *pt; +{ + printf("Kernel process table entries:\n\n"); +#if 0 + printf("Process' registers: 0x%04.4x\n", pt->p_reg); /* struct stackframe_s */ + printf("Selector in gdt: 0x%04.4x\n", pt->p_ldt_sel); /* reg_t */ + printf("Descriptors for code and data: 0x%04.4x\n", pt->p_ldt[2]); /* struct segdesc_s */ +#endif + printf("Number of this process: 0x%04.4x\n", pt->p_nr); /* int */ + printf("Nonzero if blocked by busy task: 0x%04.4x\n", pt->p_ntf_blocked); /* int */ + printf("Nonzero if held by busy syscall: 0x%04.4x\n", pt->p_ntf_held); /* int */ +#if 0 + printf("Next in chain of held-up processes: 0x%04.4x\n", pt->p_ntf_nextheld); /* struct proc * */ +#endif + printf("SENDING, RECEIVING, etc.: 0x%04.4x\n", pt->p_flags); /* int */ +#if 0 + printf("Memory map: 0x%04.4x\n", pt->p_map[NR_LOCAL_SEGS]); /* struct mem_map */ +#endif +#if DEAD_CODE + printf("Process id passed in from MM: 0x%04.4x\n", pt->p_pid); /* int */ +#endif + printf("User time in ticks: %ld\n", pt->user_time); /* time_t */ + printf("Sys time in ticks: %ld\n", pt->sys_time); /* time_t */ + printf("Cumulative user time of children: %ld\n", pt->child_utime); /* time_t */ + printf("Cumulative sys time of children: %ld\n", pt->child_stime); /* time_t */ +#if 0 + printf("Ticks used in current quantum: %d\n", pt->quantum_time); /* int */ + printf("Ticks used in last quantum: %d\n", pt->quantum_last); /* int */ + printf("Current priority of the process: %d\n", pt->curr_prio); /* int */ + printf("Base priority of the process: %d\n", pt->base_prio); /* int */ + printf("Scale for profiling, 0 = none: %u\n", pt->p_pscale); /* unsigned */ + printf("Profiling pc lower boundary: %d\n", pt->p_plow); /* vir_bytes */ + printf("Profiling pc upper boundary: %d\n", pt->p_phigh); /* vir_bytes */ + printf("Profiling buffer: %d\n", pt->p_pbuf); /* vir_bytes */ + printf("Profiling buffer size: %d\n", pt->p_psiz); /* vir_bytes */ +#endif +#if 0 + printf("First proc wishing to send: 0x%04.4x\n", pt->p_callerq); /* struct proc * */ + printf("Link to next proc wishing to send: 0x%04.4x\n", pt->p_sendlink); /* struct proc * */ + printf("Pointer to message buffer: 0x%04.4x\n", pt->p_messbuf); /* message * */ +#endif + printf("Expecting message from: 0x%04.4x\n", pt->p_getfrom); /* int */ +#if 0 + printf("Pointer to next ready process: 0x%04.4x\n", pt->p_nextready); /* struct proc * */ +#endif + printf("Bit map for pending signals 1-16: 0x%04.4x\n", pt->p_pending); /* int */ + printf("Count of pending/unfinished signals: 0x%04.4x\n", pt->p_pendcount); /* unsigned */ +} + + +/* D u m p _ r e g i s t e r s + * + * Dump the registers from the proc table + */ +void dump_registers(pt) +struct proc *pt; +{ + char buff[32]; + unsigned char uc; + + /* Print the registers */ + dump_stack(&pt->p_reg); + + /* Build up a binary representation of the signal flags */ + uc = (pt->p_pending >> 8) & 0xff; + (void) binary((int) uc, buff); + buff[9] = '$'; + uc = pt->p_pending & 0xff; + (void) binary((int) uc, buff + 10); + printf("Pending signals = %s\n", buff); +} + + +/* D u m p _ s y m _ t a b + * + * Dump the symbol table + */ +void dump_sym_tab(st) +struct sym *st; +{ + int j; + + printf("Symbol table entries (text):\n\n"); + for (j = 0; j < maxsym; j++) + printf("0x%08.8x T %s\n", symtab[j].addr, symtab[j].label); +} + + +/* D u m p _ s t a c k + * + * Dump the stack frame + */ +void dump_stack(sp) +struct stackframe_s *sp; +{ + char buff[32]; + unsigned char uc; + + /* Build up the binary PSW representation */ + uc = (sp->psw >> 8) & 0xff; + (void) binary((int) uc, buff); + uc = sp->psw & 0xff; + buff[9] = '$'; + (void) binary((int) uc, buff + 10); + + /* Print all the information */ + printf("Stack Frame:\tPC = %04.4x\t\t PSW = %s\n", + sp->pc, buff); + printf("\t\t\t\t\tStatus = ____ ODIT SZ_A _P_C\n"); + + printf(" ax bx cx dx di si\n"); + printf(" %04.4x\t%04.4x\t%04.4x\t%04.4x\t%04.4x\t%04.4x\n", + sp->retreg, sp->bx, sp->cx, sp->dx, sp->di, sp->si); + printf(" sp bp ss\n"); + printf(" %04.4x\t%04.4x\t%04.4x\n", + sp->sp, sp->fp, sp->ss); + printf(" cs ds es\n"); + printf(" %04.4x\t%04.4x\t%04.4x\n", + sp->cs, sp->ds, sp->es); + + /* Store for future reference */ + stackptr = sp->sp; + baseptr = sp->fp; + if (dbglvl > 0) + printf("\nStack pointer 0x%x, Base pointer 0x%x\n", stackptr, baseptr); +} + + +/* M a i n + * + * Main program + */ +main(argc, argv) +int argc; +char *argv[]; +{ + int j, fdc, fds; + char *cp, corefile[132], symbfile[132]; + struct proc proc_entry; + struct mem_map mp_segs[NR_LOCAL_SEGS]; + + /* Initial set up */ + if ((cp = strrchr(argv[0], '/')) == (char *) NULL) + cp = argv[0]; + else + cp++; + strncpy(progname, cp, 19); + strncpy(corefile, CORE, 131); + strncpy(symbfile, AOUT, 131); + + /* Parse arguments */ + opterr = 0; + while ((j = getopt(argc, argv, "c:dps:tx:")) != EOF) { + switch (j & 0177) { + case 'c': + opt_c = TRUE; + strncpy(corefile, optarg, 131); + break; + case 'd': opt_d = TRUE; break; + case 'p': opt_p = TRUE; break; + case 's': + opt_s = TRUE; + strncpy(symbfile, optarg, 131); + break; + case 't': opt_t = TRUE; break; + case 'x': + dbglvl = atoi(optarg); + opt_x = TRUE; + break; + case '?': + default: + usage(); + exit(1); + break; + } + } + + /* We must have a core file */ + if ((fdc = open(corefile, O_RDONLY)) == -1) { + fprintf(stderr, "Cannot open %s\n", corefile); + exit(1); + } + + /* We'd like an a.out file or a symbol table */ + if ((fds = open(symbfile, O_RDONLY)) == -1) { + if (opt_s) + j = FAILED; + else { + strncpy(symbfile, AOUT, 131); + if ((fds = open(symbfile, O_RDONLY)) == -1) + j = FAILED; + else + j = read_symbol(fds); + } + } else + j = read_symbol(fds); + + /* Only fatal if we insisted */ + if (opt_s && j == FAILED) { + fprintf(stderr, "Cannot find symbols in %s\n", symbfile); + exit(1); + } + + /* Read the process table */ + if (dbglvl > 0) { + printf("\n"); + printf("Size of mproc entry %d\n", NR_LOCAL_SEGS * sizeof(struct mem_map)); + printf("Size of process table %d\n", sizeof(proc_entry)); + } + if (read(fdc, (char *) mp_segs, sizeof(mp_segs)) != sizeof(mp_segs) || + read(fdc, (char *) &proc_entry, + sizeof(struct proc)) != sizeof(struct proc)) { + fprintf(stderr, "Cannot open %s\n", corefile); + exit(1); + } + + /* Do the work */ +#if 0 + dump_maps(mp_segs); /* duplicated in the kernel */ + printf("\n"); + /* XXX broken */ + dump_maps(proc_entry.p_map); +#endif + printf("\n"); + dump_registers(&proc_entry); + if (opt_t) { + printf("\n"); + stack_trace(fdc); + } + if (opt_p) { + printf("\n"); + dump_proc_table(&proc_entry); + } + if (opt_d) { + printf("\n"); + dump_sym_tab(symtab); + dump_all_segs(fdc); + } + + /* Wrap up */ + (void) close(fdc); + if (fds != -1) (void) close(fds); + + exit(0); + /* NOTREACHED */ +} + + +/* P a r s e _ l i n e + * + * Parse a line of the symbol table + */ +int parse_line(ps) +char *ps; +{ + char c, s[80]; + int j, k; + unsigned int u; + + /* We must have space in the table */ + if (maxsym == MAXSYM) return(FAILED); + + /* Lines must be a minimum length to contain information */ + if (strlen(ps) < 8) return(FAILED); + + /* Lines must have a definite structure */ + if (ps[1] != ' ' || ps[6] != ' ') return(FAILED); + for (j = 2; j < 6; j++) + if (!isxdigit(ps[j])) return(FAILED); + if (sscanf(ps, "%c %x %s", &c, &u, s) != 3) return (FAILED); + + if (dbglvl > 0) printf("Address 0x%04.4x, label %s\n", u, s); + + /* Load the symbol table in sorted order */ + for (j = 0; j < maxsym; j++) { + if (u < symtab[j].addr) { + for (k = maxsym; k > j; k--) symtab[k] = symtab[k - 1]; + break; + } + } + symtab[j].addr = u; + strncpy(symtab[j].label, s, SYMLEN); + maxsym++; + + return(OK); +} + + +/* R e a d _ s y m b o l + * + * Read the symbol table + */ +int read_symbol(fd) +int fd; +{ + char sym[80], buff[BUFSIZ]; + int j, k, m; + long int offset; + struct exec *ep; + struct nlist *np; + + /* We collect only text symbols, since that's all that's needed here */ + + /* Initialise the buffer */ + if ((j = read(fd, buff, BUFSIZ)) == 0 || j == -1) return(FAILED); + + k = maxsym = 0; + + /* Find out what we've got */ + ep = (struct exec *) buff; + np = (struct nlist *) buff; + if (BADMAG(*ep)) { + /* Must be a separate symbol table */ + while (TRUE) { + if (buff[k] == 'T') { + for (m = 0; m < 78; m++) { + sym[m] = buff[k]; + if (++k == j) { + if ((j = read(fd, buff, BUFSIZ)) == 0 || j == -1) + break; + k = 0; + } + if (buff[k] == '\n') break; + } + sym[m + 1] = '\0'; + (void) parse_line(sym); + } + if (++k == j) { + if ((j = read(fd, buff, BUFSIZ)) == 0 || j == -1) + break; + k = 0; + } + } + } else if (ep->a_syms != 0L) { + /* There's symbols in them thar hills */ + offset = 8 * sizeof(long) + ep->a_text + ep->a_data; + if (lseek(fd, offset, 0) == -1L) return(FAILED); + /* Symbols are in an unsorted list */ + while (read(fd, buff, sizeof(struct nlist)) == sizeof(struct nlist)) { + if (np->n_sclass == (N_TEXT + C_EXT)) { /* external text symbols */ + for (j = 0; j < maxsym; j++) { + if (np->n_value < symtab[j].addr) { + for (k = maxsym; k > j; k--) + symtab[k] = symtab[k - 1]; + break; + } + } + symtab[j].addr = np->n_value; + strncpy(symtab[j].label, np->n_name, SYMLEN); + if (maxsym++ == MAXSYM) break; + } + } + } else if (opt_s) + return(FAILED); + + if (dbglvl > 0) { + for (m = 0; m < maxsym; m++) printf("Addr 0x%04.4x, label %s\n", + symtab[m].addr, symtab[m].label); + printf("Maxsym %d\n", maxsym); + } + return(OK); +} + + +/* S t a c k _ t r a c e + * + * Trace back down the stack frames. + * + * WARNING: very, very, non-portable code + */ +void stack_trace(fd) +int fd; +{ + int j; + unsigned int framepointer, lastpointer, returnvalue, end; + long int offset, bp; + + /* Bp actually gives the offset from the base of the data segment */ + bp = (long) (NR_LOCAL_SEGS * sizeof(struct mem_map)) + sizeof(struct proc) + + lengths[0] + lengths[1] - bases[2]; + if ((offset = lseek(fd, bp + (long int) baseptr, 0)) == -1L) return; + end = (bases[2] + lengths[2] - 1) & 0xffff; + + if (dbglvl > 0) + printf("Baseptr %x, End %x, Bp %ld, Offset %ld\n", baseptr, end, bp, offset); + + /* Print the header, then try to backtrace */ + printf("Stack back trace:\n\n"); + printf("Frame address. Contents. Return address."); + if (maxsym != 0) printf(" Previous label."); + printf("\n"); + + lastpointer = baseptr; + while (TRUE) { + /* Read the frame pointer and return address values */ + if (read(fd, (char *) &framepointer, sizeof(int)) == -1 || + read(fd, (char *) &returnvalue, sizeof(int)) == -1) + break; + + /* Look up the return address - ignored if maxsym == 0 */ + for (j = 0; j < maxsym; j++) { + if (symtab[j].addr >= returnvalue) break; + } + if (j > 0) j--; + printf(" 0x%04.4x 0x%04.4x 0x%04.4x %s\n", + lastpointer, framepointer, returnvalue, + (maxsym == 0) ? "" : symtab[j].label); + + /* If the result is clearly invalid, quit */ + if (framepointer == 0 || framepointer >= end || framepointer <= lastpointer) + break; + + /* Otherwise try to move to the next frame base */ + lastpointer = framepointer; + if ((offset = lseek(fd, bp + (long int) framepointer, 0)) == -1L || offset == 0L) + break; + } +} + + +/* U s a g e + * + * Usage message + */ +void usage() +{ + fprintf(stderr, "Usage: %s [-dpt] [-c corefile] [-s symbfile]\n", progname); +} diff --git a/commands/ibm/readclock.c b/commands/ibm/readclock.c new file mode 100755 index 000000000..ce39a7ff0 --- /dev/null +++ b/commands/ibm/readclock.c @@ -0,0 +1,210 @@ +/* setime - set the system time from the real time clock + Authors: T. Holm & E. Froese + Adapted by: Jorrit .N. Herder */ + +/************************************************************************/ +/* Readclock was updated for security reasons: openeing /dev/mem no */ +/* longer automatically grants I/O privileges to the calling process */ +/* so that the CMOS' clock could not be read from this program. The */ +/* new approach is to rely on the FS to do the CMOS I/O, via the new */ +/* system call CMOSTIME (which only reads the current clock value and */ +/* cannot update the CMOS clock). */ +/* The original readclock.c is still available under backup.c. */ +/************************************************************************/ +/* */ +/* readclock.c */ +/* */ +/* Read the clock value from the 64 byte CMOS RAM */ +/* area, then set system time. */ +/* */ +/* If the machine ID byte is 0xFC or 0xF8, the device */ +/* /dev/mem exists and can be opened for reading, */ +/* and no errors in the CMOS RAM are reported by the */ +/* RTC, then the time is read from the clock RAM */ +/* area maintained by the RTC. */ +/* */ +/* The clock RAM values are decoded and fed to mktime */ +/* to make a time_t value, then stime(2) is called. */ +/* */ +/* This fails if: */ +/* */ +/* If the machine ID does not match 0xFC or 0xF8 (no */ +/* error message.) */ +/* */ +/* If the machine ID is 0xFC or 0xF8 and /dev/mem */ +/* is missing, or cannot be accessed. */ +/* */ +/* If the RTC reports errors in the CMOS RAM. */ +/* */ +/************************************************************************/ +/* origination 1987-Dec-29 efth */ +/* robustness 1990-Oct-06 C. Sylvain */ +/* incorp. B. Evans ideas 1991-Jul-06 C. Sylvain */ +/* set time & calibrate 1992-Dec-17 Kees J. Bot */ +/* clock timezone 1993-Oct-10 Kees J. Bot */ +/* set CMOS clock 1994-Jun-12 Kees J. Bot */ +/* removed set CMOS 2004-Sep-06 Jorrit N. Herder */ +/************************************************************************/ + +#include <minix/callnr.h> +#include <minix/config.h> +#include <minix/type.h> +#include <minix/const.h> +#include <minix/com.h> +#include <minix/syslib.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <errno.h> +#include <signal.h> +#include <ibm/portio.h> +#include <ibm/cmos.h> +#include <sys/svrctl.h> + +#define MAX_RETRIES 1 + +int nflag = 0; /* Tell what, but don't do it. */ +int y2kflag = 0; /* Interpret 1980 as 2000 for clock with Y2K bug. */ + +char clocktz[128]; /* Timezone of the clock. */ + +#define MACH_ID_ADDR 0xFFFFE /* BIOS Machine ID at FFFF:000E */ + +#define PC_AT 0xFC /* Machine ID byte for PC/AT, + PC/XT286, and PS/2 Models 50, 60 */ +#define PS_386 0xF8 /* Machine ID byte for PS/2 Model 80 */ + +/* Manufacturers usually use the ID value of the IBM model they emulate. + * However some manufacturers, notably HP and COMPAQ, have had different + * ideas in the past. + * + * Machine ID byte information source: + * _The Programmer's PC Sourcebook_ by Thom Hogan, + * published by Microsoft Press + */ + +void errmsg(char *s); +int bcd_to_dec(int n); +int dec_to_bcd(int n); +void usage(void); + +PUBLIC int main(int argc, char **argv) +{ + struct tm time1; + struct tm time2; + struct tm tmnow; + char date[64]; + time_t now, rtc; + int i, s, mem; + unsigned char mach_id, cmos_state; + struct sysgetenv sysgetenv; + message m; + + + /* Process options. */ + while (argc > 1) { + char *p = *++argv; + + if (*p++ != '-') usage(); + + while (*p != 0) { + switch (*p++) { + case 'n': nflag = 1; break; + case '2': y2kflag = 1; break; + default: usage(); + } + } + argc--; + } + + /* The hardware clock may run in a different time zone, likely GMT or + * winter time. Select that time zone. + */ + strcpy(clocktz, "TZ="); + sysgetenv.key = "TZ"; + sysgetenv.keylen = 2+1; + sysgetenv.val = clocktz+3; + sysgetenv.vallen = sizeof(clocktz)-3; + if (svrctl(SYSGETENV, &sysgetenv) == 0) { + putenv(clocktz); + tzset(); + } + + /* Read the CMOS real time clock. */ + for (i = 0; i < MAX_RETRIES; i++) { + + /* sleep, unless first iteration */ + if (i > 0) sleep(5); + + /* get_time(&time1); */ + m.m_type = CMOSTIME; + m.ADDRESS = (void *) &time1; + m.REQUEST = y2kflag; + if (0 != (s=sendrec(FS_PROC_NR, &m))) { + fprintf(stderr, "Couldn't get CMOS time from FS: %d.\n",s); + exit(1); + } + + now = time(NULL); + + time1.tm_isdst = -1; /* Do timezone calculations. */ + time2 = time1; + + rtc= mktime(&time1); /* Transform to a time_t. */ + if (rtc != -1) break; + + fprintf(stderr, +"readclock: Invalid time read from CMOS RTC: %d-%02d-%02d %02d:%02d:%02d\n", + time2.tm_year+1900, time2.tm_mon+1, time2.tm_mday, + time2.tm_hour, time2.tm_min, time2.tm_sec); + } + if (i >= MAX_RETRIES) exit(1); + + /* Set system time. */ + if (nflag) { + printf("stime(%lu)\n", (unsigned long) rtc); + } else { + if (stime(&rtc) < 0) { + errmsg( "Not allowed to set time." ); + exit(1); + } + } + tmnow = *localtime(&rtc); + if (strftime(date, sizeof(date), + "%a %b %d %H:%M:%S %Z %Y", &tmnow) != 0) { + if (date[8] == '0') date[8]= ' '; + printf("%s [CMOS read via FS, see command/ibm/readclock.c]\n", date); + } + exit(0); +} + +void errmsg(char *s) +{ + static char *prompt = "settime: "; + + fprintf(stderr, "%s%s\n", prompt, s); + prompt = ""; +} + + +int bcd_to_dec(int n) +{ + return ((n >> 4) & 0x0F) * 10 + (n & 0x0F); +} + +int dec_to_bcd(int n) +{ + return ((n / 10) << 4) | (n % 10); +} + +void usage(void) +{ + fprintf(stderr, "Usage: settime [-n2]\n"); + exit(1); +} diff --git a/commands/ibm/readclock2 b/commands/ibm/readclock2 new file mode 100755 index 000000000..9f26697e4 Binary files /dev/null and b/commands/ibm/readclock2 differ diff --git a/commands/ibm/recwave.c b/commands/ibm/recwave.c new file mode 100755 index 000000000..748c7c7cb --- /dev/null +++ b/commands/ibm/recwave.c @@ -0,0 +1,237 @@ +/* + * recwave.c + * + * Record sound files in wave format. Only MicroSoft PCM is supported. + * + * Michel R. Prevenier. + */ + +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <termios.h> +#include <stdlib.h> +#include <fcntl.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <minix/sound.h> + +_PROTOTYPE (void main, (int argc, char **argv)); +_PROTOTYPE (void usage, (void)); +_PROTOTYPE ( void write_wave_header, (void)); +_PROTOTYPE ( void terminate, (int s)); + + +/******* Wave format definitions *********/ + +#define RIFF_ID 0x46464952 +#define WAVE_ID1 0x45564157 +#define WAVE_ID2 0x20746D66 +#define DATA_ID 0x61746164 +#define MS_PCM_FORMAT 0x0001 + +#define WORD short +#define DWORD unsigned long + +struct RIFF_fields +{ + DWORD RIFF_id; + DWORD RIFF_len; + DWORD WAVE_id1; + DWORD WAVE_id2; + DWORD data_ptr; +} r_fields; + +struct common_fields +{ + WORD FormatTag; + WORD Channels; + DWORD SamplesPerSec; + DWORD AvgBytesPerSec; + WORD BlockAlign; +} c_fields; + +struct specific_fields +{ + WORD BitsPerSample; +} s_fields; + +DWORD data_id; +DWORD data_len; + +/******** End of wave format definitions *********/ + +/* Default recording values */ +unsigned int sign = 0; +unsigned int bits = 8; +unsigned int stereo = 0; +unsigned int rate = 22050; +unsigned int time = 10; + +int old_stdin; +struct termios old_tty, new_tty; +int audio, file; + +void usage() +{ + fprintf(stderr, "Usage: recwav [-b -s -r] file_name\n"); + exit(-1); +} + +void terminate(s) +int s; +{ + /* Restore terminal parameters */ + tcsetattr(0, TCSANOW, &old_tty); + (void) fcntl(0,F_SETFL,old_stdin); + close(audio); + close(file); + exit(0); +} + +void write_wave_header() +{ + /* RIFF fields */ + r_fields.RIFF_id = RIFF_ID; + r_fields.WAVE_id1 = WAVE_ID1; + r_fields.WAVE_id2 = WAVE_ID2; + r_fields.data_ptr = 16; + r_fields.RIFF_len = 20 + r_fields.data_ptr + data_len; + + /* MicroSoft PCM specific fields */ + s_fields.BitsPerSample = bits; + + /* Common fields */ + c_fields.FormatTag = MS_PCM_FORMAT; + c_fields.Channels = stereo + 1; + c_fields.SamplesPerSec = rate; + c_fields.AvgBytesPerSec = c_fields.Channels * rate * (bits / 8); + c_fields.BlockAlign = c_fields.Channels * (bits / 8); + + /* Data chunk */ + data_id = DATA_ID; + + /* Write wave-file header */ + lseek(file, 0L, SEEK_SET); + write(file, &r_fields, 20); + write(file, &c_fields, 14); + write(file, &s_fields, 2); + write(file, &data_id, sizeof(data_id)); + write(file, &data_len, sizeof(data_len)); +} + + +void main(argc, argv) +int argc; +char **argv; +{ + unsigned int fragment_size; + char *buffer, *file_name; + char c; + int i; + + /* Read parameters */ + if (argc < 2) usage(); + + i = 1; + while (argv[i][0] == '-' && i < argc) + { + if (strncmp(argv[i], "-b", 2) == 0) + bits = atoi(argv[i] + 2); + else if (strncmp(argv[i], "-s", 2) == 0) + stereo = atoi(argv[i] + 2); + else if (strncmp(argv[i], "-r", 2) == 0) + rate = (unsigned int) atol(argv[i] + 2); + else usage(); + i++; + } + if (i == argc) usage(); + + file_name = argv[i]; + + /* Some sanity checks */ + if ((bits != 8 && bits != 16) || + (rate < 4000 || rate > 44100) || + (stereo != 0 && stereo != 1)) + { + fprintf(stderr, "Invalid parameters\n"); + exit(-1); + } + + /* Open DSP */ + if ((audio = open("/dev/audio", O_RDWR)) < 0) + { + fprintf(stderr, "Cannot open /dev/audio\n"); + exit(-1); + } + + /* Get maximum fragment size and try to allocate a buffer */ + ioctl(audio, DSPIOMAX, &fragment_size); + if ((buffer = malloc(fragment_size)) == (char *) 0) + { + fprintf(stderr, "Cannot allocate buffer\n"); + exit(-1); + } + + /* Set sample parameters */ + ioctl(audio, DSPIOSIZE, &fragment_size); + ioctl(audio, DSPIOSTEREO, &stereo); + ioctl(audio, DSPIORATE, &rate); + ioctl(audio, DSPIOBITS, &bits); + sign = (bits == 16 ? 1 : 0); + ioctl(audio, DSPIOSIGN, &sign); + + /* Create sample file */ + if ((file = creat(file_name, 511)) < 0) + { + fprintf(stderr, "Cannot create %s\n", argv[1]); + exit(-1); + } + /* Skip wave header */ + lseek(file, (long)(sizeof(r_fields) + + sizeof(c_fields) + + sizeof(s_fields) + + sizeof(data_id) + + sizeof(data_len)), SEEK_SET); + + printf("\nBits per sample : %u\n", bits); + printf("Stereo : %s\n", (stereo == 1 ? "yes" : "no")); + printf("Samples per second: %u\n", rate); + + /* Set terminal parameters and remember the old ones */ + tcgetattr(0, &old_tty); + new_tty = old_tty; + new_tty.c_lflag &= ~(ICANON|ECHO); + old_stdin = fcntl(0, F_GETFL); + + /* Catch break signal to be able to restore terminal parameters in case + * of a user interrupt + */ + signal(SIGINT, terminate); + + /* Go to non-blocking mode */ + tcsetattr(0, TCSANOW, &new_tty); + (void) fcntl(0, F_SETFL, old_stdin | O_NONBLOCK); + + printf("\nPress spacebar to start sampling...\n"); + while(!(read(0, &c, 1) == 1 && c == ' ')); + + printf("Sampling, press spacebar to stop...\n"); + while(!(read(0, &c, 1) == 1 && c == ' ')) + { + /* Read sample fragment and write to sample file */ + read(audio, buffer, fragment_size); + write(file, buffer, fragment_size); + data_len+= fragment_size; + } + printf("%ld bytes sampled. \n\n", data_len); + + /* Construct the wave header in front of the raw sample data */ + write_wave_header(); + + /* Restore terminal parameters and exit */ + terminate(1); +} diff --git a/commands/ibm/repartition.c b/commands/ibm/repartition.c new file mode 100755 index 000000000..ea8c3beec --- /dev/null +++ b/commands/ibm/repartition.c @@ -0,0 +1,283 @@ +/* repartition 1.18 - Load a partition table Author: Kees J. Bot + * 30 Nov 1991 + */ +#define nil 0 +#include <stdio.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <minix/config.h> +#include <minix/const.h> +#include <minix/partition.h> +#include <minix/u64.h> +#include <ibm/partition.h> +#include <sys/stat.h> +#include <string.h> +#include <errno.h> +#include <dirent.h> +#include <limits.h> + +#define DEV_FD0 0x200 + +#define SECTOR_SIZE 512 + +#define arraysize(a) (sizeof(a)/sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +char *arg0; +char *dev_file; /* Device to repartition. */ + +#ifndef S_ISLNK +/* There were no symlinks in medieval times. */ +#define lstat stat +#endif + +void report(const char *label) +{ + fprintf(stderr, "%s: %s: %s\n", arg0, label, strerror(errno)); +} + +void fatal(const char *label) +{ + report(label); + exit(1); +} + +#ifndef makedev +#define minor(dev) (((dev) >> MINOR) & BYTE) +#define major(dev) (((dev) >> MAJOR) & BYTE) +#define makedev(major, minor) \ + ((dev_t) (((major) << MAJOR) | ((minor) << MINOR))) +#endif + +#define MINOR_d0p0s0 128 + +void partsort(struct part_entry *pe) +/* DOS has the misguided idea that partition tables must be sorted. */ +{ + int i,j; + struct part_entry tmp; + + for (i = 0; i < NR_PARTITIONS; i++) + for (j = 0; j < NR_PARTITIONS-1; j++) + if ((pe[j].sysind == NO_PART && pe[j+1].sysind != NO_PART) || + (pe[j].lowsec > pe[j+1].lowsec && pe[j+1].sysind != NO_PART)) { + tmp = pe[j]; + pe[j] = pe[j+1]; + pe[j+1] = tmp; + } +} + +char *finddev(dev_t device) +/* Find the device next to dev_file with the given device number. */ +{ + DIR *dp; + struct dirent *de; + static char name[PATH_MAX]; + char *np; + size_t nlen; + struct stat st; + + if ((np= strrchr(dev_file, '/')) == nil) np= dev_file; else np++; + nlen= np - dev_file; + if (nlen > PATH_MAX - NAME_MAX - 1) { + fprintf(stderr, "%s: %s: Name is way too long\n", + arg0, dev_file); + exit(1); + } + memcpy(name, dev_file, nlen); + + if ((dp= opendir("/dev")) == nil) fatal("/dev"); + while ((de= readdir(dp)) != nil) { + strcpy(name+nlen, de->d_name); + if (lstat(name, &st) == 0 + && (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) + && st.st_rdev == device + ) { + closedir(dp); + return name; + } + } + fprintf(stderr, "%s: Can't find partition devices associated with %s\n", + arg0, dev_file); + exit(1); +} + +#define DSETP 0 +#define DGETP 1 + +int diocntl(dev_t device, int request, struct partition *entry) +/* Get or set the geometry of a device. */ +{ + char *name; + int r, f, err; + struct partition geometry; + + name= finddev(device); + if ((f= open(name, O_RDONLY)) < 0) return -1; + r= ioctl(f, request == DSETP ? DIOCSETP : DIOCGETP, (void *) entry); + err= errno; + (void) close(f); + errno= err; + return r; +} + +struct partition geometry; /* Geometry of the device. */ + +void print_chs(unsigned long sector) +{ + unsigned secspcyl = geometry.heads * geometry.sectors; + int delta= 0; + + if (sector == -1) { sector= 0; delta= -1; } + + printf(" %5d/%03d/%02d", + (int) (sector / secspcyl), + (int) (sector % secspcyl) / geometry.sectors, + (int) (sector % geometry.sectors) + delta); +} + +void show_part(char *name, unsigned long base, unsigned long size) +{ + int i; + static int len= 0; + + if (len == 0) { + len= strlen(name) + 3; + printf("device"); + for (i = 6; i < len; i++) fputc(' ', stdout); + printf( + " first last base size kb\n"); + } + + printf("%s", name); + for (i = strlen(name); i < len; i++) fputc(' ', stdout); + + print_chs(base); + print_chs(base + size - 1); + printf(" %9lu %9lu %9lu\n", base, size, size / (1024/SECTOR_SIZE)); +} + +int main(int argc, char **argv) +{ + struct stat hdst; + struct partition whole, entry; + struct part_entry table[4], *pe; + int drive, par, device, incr; + int partf; + char *table_file; + int hd_major, hd_minor; + int needsort; + int shrink; /* True if partitions are shrinked to fit. */ + unsigned long base, size, limit; + + if ((arg0= strrchr(argv[0], '/')) == nil) arg0= argv[0]; else arg0++; + + if (argc < 2 || argc > 3) { + fprintf(stderr, + "Usage: %s device [partition-file]\n", arg0); + exit(1); + } + dev_file= argv[1]; + table_file= argv[argc - 1]; + shrink= (argc == 2); + + if (stat(dev_file, &hdst) < 0) fatal(dev_file); + + /* Geometry (to print nice numbers.) */ + if (diocntl(hdst.st_rdev, DGETP, &geometry) < 0) fatal(dev_file); + + if (!S_ISBLK(hdst.st_mode)) { + fprintf(stderr, "%s: %s is not a device\n", arg0, dev_file); + exit(1); + } + hd_major= major(hdst.st_rdev); + hd_minor= minor(hdst.st_rdev); + + if (hd_minor >= MINOR_d0p0s0) { + errno= EINVAL; + fatal(dev_file); + } + + if (hd_major == major(DEV_FD0)) { + /* HD is actually a floppy. */ + if (hd_minor >= 4) { + errno= EINVAL; + fatal(dev_file); + } + device= hd_minor + (28 << 2); + incr= 4; + needsort= 0; + } else + if (hd_minor % (1 + NR_PARTITIONS) == 0) { + /* Partitioning hd0, hd5, ... */ + device= hd_minor + 1; + incr= 1; + needsort= 1; + } else { + /* Subpartitioning hd[1-4], hd[6-9], ... */ + drive= hd_minor / (1 + NR_PARTITIONS); + par= hd_minor % (1 + NR_PARTITIONS) - 1; + + device= MINOR_d0p0s0 + + (drive * NR_PARTITIONS + par) * NR_PARTITIONS; + if (device + NR_PARTITIONS - 1 > BYTE) { + errno= EINVAL; + fatal(dev_file); + } + incr= 1; + needsort= 0; + } + /* Device is now the first of the minor devices to be repartitioned. */ + + /* Read the partition table from the boot block. */ + if ((partf= open(table_file, O_RDONLY)) < 0 + || lseek(partf, (off_t) PART_TABLE_OFF, SEEK_SET) == -1 + || (par= read(partf, (char *) table, (int) sizeof(table))) < 0 + ) fatal(table_file); + + if (par < sizeof(table)) { + fprintf(stderr, "%s: %s does not contain a partition table\n", + arg0, table_file); + exit(1); + } + if (needsort) partsort(table); + + /* Show the geometry of the affected drive or partition. */ + if (diocntl(hdst.st_rdev, DGETP, &whole) < 0) fatal(dev_file); + + /* Use sector numbers. */ + base = div64u(whole.base, SECTOR_SIZE); + size = div64u(whole.size, SECTOR_SIZE); + limit = base + size; + + show_part(dev_file, base, size); + + /* Send the partition table entries to the device driver. */ + for (par= 0; par < NR_PARTITIONS; par++, device+= incr) { + pe = &table[par]; + if (shrink && pe->size != 0) { + /* Shrink the partition entry to fit within the + * enclosing device just like the driver does. + */ + unsigned long part_limit= pe->lowsec + pe->size; + + if (part_limit < pe->lowsec) part_limit= limit; + if (part_limit > limit) part_limit= limit; + if (pe->lowsec < base) pe->lowsec= base; + if (part_limit < pe->lowsec) part_limit= pe->lowsec; + pe->size= part_limit - pe->lowsec; + } + + entry.base= mul64u(pe->lowsec, SECTOR_SIZE); + entry.size= mul64u(pe->size, SECTOR_SIZE); + if (diocntl(makedev(hd_major, device), DSETP, &entry) < 0) + fatal(dev_file); + + show_part(finddev(makedev(hd_major, device)), + pe->lowsec, pe->size); + } + exit(0); +} diff --git a/commands/ibm/screendump.c b/commands/ibm/screendump.c new file mode 100755 index 000000000..96cc2ae52 --- /dev/null +++ b/commands/ibm/screendump.c @@ -0,0 +1,136 @@ +/* screendump 1.2 - dump the contents of the console + * Author: Kees J. Bot + * 16 Dec 1994 + */ +#define nil 0 +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <termios.h> +#include <sys/ioctl.h> + +#define BIOS_CRTBASE 0x00463L /* BIOS parameters: CRT base. */ +#define CRTBASE_MONO 0x03B4 /* Value of CRT base for mono mode. */ + +#define MONO_BASE 0xB0000L /* Screen memory in monochrome mode. */ +#define COLOR_BASE 0xB8000L /* ... colour mode. */ + +#define DEF_COLS 80 /* Default screen geometry. */ +#define DEF_ROWS 25 + +#define MAX_COLS 132 /* Maximum screen geometry. */ +#define MAX_ROWS 60 + +char MEMORY[] = "/dev/mem"; /* Memory device to read screen. */ +int mfd; /* Open memory device. */ + +void tell(const char *message) +{ + write(2, message, strlen(message)); +} + +void fatal(const char *label) +{ + const char *err= strerror(errno); + + tell("screendump: "); + tell(label); + tell(": "); + tell(err); + tell("\n"); + exit(1); +} + +long video_base(void) +/* Is it monochrome or colour? */ +{ + static unsigned short bios_crtbase; + + if (lseek(mfd, (off_t) BIOS_CRTBASE, SEEK_SET) == -1) fatal(MEMORY); + switch (read(mfd, &bios_crtbase, sizeof(bios_crtbase))) { + case -1: + fatal(MEMORY); + default: + tell("screendump: can't obtain BIOS parameter: short read\n"); + exit(1); + case sizeof(bios_crtbase): + /* Fine */; + } + + return bios_crtbase == CRTBASE_MONO ? MONO_BASE : COLOR_BASE; +} + +void main(void) +{ + static unsigned char screen[MAX_COLS * MAX_ROWS * 2]; + unsigned char *ps; + long base; + int lfd; + int row; + int nrows, ncols; + struct winsize winsize; + + /* Open the memory device. */ + if ((mfd= open(MEMORY, O_RDONLY)) < 0) fatal(MEMORY); + + base= video_base(); + + /* Read screen memory. */ + if (lseek(mfd, base, SEEK_SET) == -1) fatal(MEMORY); + + switch (read(mfd, screen, sizeof(screen))) { + case -1: + fatal(MEMORY); + default: + tell("screendump: can't obtain screen dump: short read\n"); + exit(1); + case sizeof(screen): + /* Fine */; + } + + /* Try to obtain the screen geometry from /dev/log. */ + ncols= DEF_COLS; + nrows= DEF_ROWS; + if ((lfd= open("/dev/log", O_WRONLY)) != -1 + && ioctl(lfd, TIOCGWINSZ, &winsize) == 0 + ) { + if (40 <= winsize.ws_col && winsize.ws_col <= MAX_COLS) { + ncols= winsize.ws_col; + } + if (25 <= winsize.ws_row && winsize.ws_row <= MAX_COLS) { + nrows= winsize.ws_row; + } + } + if (lfd != -1) close(lfd); + + /* Print the contents of the screen line by line. Omit trailing + * blanks. Note that screen memory consists of pairs of characters + * and attribute bytes. + */ + ps= screen; + for (row= 0; row < nrows; row++) { + char line[MAX_COLS + 1]; + char *pl= line; + int column; + int blanks= 0; + + for (column= 0; column < ncols; column++) { + if (*ps <= ' ') { + /* Skip trailing junk. */ + blanks++; + } else { + /* Reinsert blanks and add a character. */ + while (blanks > 0) { *pl++= ' '; blanks--; } + *pl++= *ps; + } + /* Skip character and attribute byte. */ + ps+= 2; + } + *pl++= '\n'; + if (write(1, line, pl - line) < 0) fatal("stdout"); + } + exit(0); +} diff --git a/commands/ibm/sdump.c b/commands/ibm/sdump.c new file mode 100755 index 000000000..074f848c7 --- /dev/null +++ b/commands/ibm/sdump.c @@ -0,0 +1,191 @@ +/* sdump - dump memory */ + +#include <minix/config.h> +#include <sys/types.h> +#include <minix/const.h> +#include <minix/type.h> +#include <minix/ipc.h> +#include <timers.h> +#include <signal.h> + +#undef EXTERN +#define EXTERN +#include "../../servers/mm/const.h" +#include "../../servers/mm/type.h" +#include "../../servers/mm/mproc.h" +#include "../../kernel/const.h" +#include "../../kernel/type.h" +#include "../../kernel/proc.h" +#undef printf /* printf was misdefined by the sys headers */ + +#include <ctype.h> +#include <fcntl.h> +#include <limits.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +#define STACK_BYTES 30000 + +char *default_core = "core"; +int stack[STACK_BYTES / sizeof (int)]; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void read_segmap, (int fd, struct mproc *mp, long *seg_size)); +_PROTOTYPE(void read_registers, (int fd, struct proc *p)); +_PROTOTYPE(void dump_stack, (int fd, struct mproc *mp, long *seg_size)); +_PROTOTYPE(void error, (char *s1, char *s2)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int fd; + long seg_size[NR_LOCAL_SEGS]; /* segment sizes in bytes */ + struct mproc *mp; + struct proc *p; + char *file; + + if (argc > 2) error("Usage: rdump [core_file]\n", ""); + + if (argc == 1) + file = default_core; + else + file = argv[1]; + + if ( (fd = open(file, O_RDONLY)) < 0) error("Cannot open", file); + + mp = &mproc[0]; + p = &proc[0]; + read_segmap(fd, mp, seg_size); + read_registers(fd, p); + dump_stack(fd, mp, seg_size); + return(0); +} + +void read_segmap(fd, mp, seg_size) +int fd; +struct mproc *mp; +long seg_size[NR_LOCAL_SEGS]; +{ + int i, segmap_size; + + /* Read in the segment map. */ + segmap_size = sizeof mp->mp_seg; + if (read(fd, (char *) mp, segmap_size) != segmap_size) + error("Cannot read segmap map from core image file", ""); + + for (i = 0; i < NR_LOCAL_SEGS; i++) + seg_size[i] = (long) mp->mp_seg[i].mem_len << CLICK_SHIFT; + printf("Seg sizes in bytes: Text: %ld Data: %ld, Stack: %ld\n", + seg_size[T], seg_size[D], seg_size[S]); +} + +void read_registers(fd, p) +int fd; +struct proc *p; +{ + int proctblsize; + struct stackframe_s r; + + proctblsize = sizeof (struct proc); + if (read(fd, (char *) p, proctblsize) != proctblsize) + error("Cannot read process table from core image file", ""); + r = p->p_reg; + + /* Print proc table. */ + printf("\n"); +#if (CHIP == INTEL) +#if _WORD_SIZE == 4 + printf("eax=%8lX ebx=%8lX ecx=%8lX edx=%8lX\n", + r.retreg, r.bx, r.cx, r.dx); + printf("esi=%8lX edi=%8lX ebp=%8lX esp=%8lX eip=%8lX\n", + r.si, r.di, r.fp, r.sp, r.pc); + printf(" ds=%8lX es=%8lX ss=%8lX cs=%8lX\n", + r.ds, r.es, r.ss, r.cs); + printf(" fs=%8lX gs=%8lX ef=%8lX\n", + r.fs, r.gs, r.psw); +#else + printf( +"ax=%4X bx=%4X cx=%4X dx=%4X si=%4X di=%4X bp=%4X sp=%4X ip=%4X\n", + r.retreg, r.bx, r.cx, r.dx, r.si, r.di, r.fp, r.sp, r.pc); + printf( +" f=%4X ds=%4X es=%4X ss=%4X cs=%4X\n", + r.psw, r.ds, r.es, r.ss, r.cs); +#endif +#endif /* (CHIP == INTEL) */ +#if (CHIP == M68000) + printf("pc=%8lx psw=%4x\n", r.pc, r.psw); + printf("d0=%8lx d1=%8lx d2=%8lx d3=%8lx\n", r.retreg, r.d1, r.d2, r.d3); + printf("d4=%8lx d5=%8lx d6=%8lx d7=%8lx\n", r.d4, r.d5, r.d6, r.d7); + printf("a0=%8lx a1=%8lx a2=%8lx a3=%8lx\n", r.a0, r.a1, r.a2, r.a3); + printf("a4=%8lx a5=%8lx a6=%8lx a7=%8lx\n", r.a4, r.a5, r.a6, r.sp); +#endif + printf("\n"); +} + +void dump_stack(fd, mp, seg_size) +int fd; +struct mproc *mp; +long seg_size[NR_LOCAL_SEGS]; +{ + unsigned char ch; + char format[32]; + int word, i, stack_size, j; + vir_bytes v, vi; + + /* Seek past text and data segments. */ + lseek(fd, seg_size[T] + seg_size[D], SEEK_CUR); + v = mp->mp_seg[S].mem_vir << CLICK_SHIFT; + + stack_size = (int) seg_size[S]; + if (stack_size != seg_size[S] || stack_size < 0 || stack_size > STACK_BYTES) + error("Stack too large", ""); + + /* Dump the stack. */ + if (read(fd, (char *) stack, stack_size) != stack_size) + error("Error reading stack from core file", ""); + +#define BYTES(num) ((unsigned) sizeof (num)) +#define DEC_DIGITS(num) (sizeof (num) <= 2 ? 6 : 11) /* for 16/32 bit num */ +#define HEX_DIGITS(num) (((unsigned) sizeof (num) * CHAR_BIT + 3) / 4) + + printf("%*s %*s %*s %*s\n", + HEX_DIGITS(vi), "Addr", + HEX_DIGITS(word), "Hex", + DEC_DIGITS(word), "Dec", + BYTES(word), "Char"); + + /* The format string depends messily on the size of various things. */ + strcpy(format, "%*"); + if (sizeof v > sizeof (int)) strcat(format, "l"); + strcat(format, "X: %*"); + if (sizeof word > sizeof (int)) strcat(format, "l"); + strcat(format, "X %*"); + if (sizeof word > sizeof (int)) strcat(format, "l"); + strcat(format, "d "); + + for (i = stack_size / sizeof (int) - 1, vi = v + stack_size - sizeof (int); + i >= 0; --i, vi -= sizeof (int)) { + word = stack[i]; + printf(format, + HEX_DIGITS(vi), vi, + HEX_DIGITS(word), word, + DEC_DIGITS(word), word); + for (j = 0; j < BYTES(word); ++j, word >>= CHAR_BIT) { + ch = (unsigned char) word; + if (!isprint(ch)) ch = '.'; + putchar(ch); + } + putchar('\n'); + } +} + +void error(s1, s2) +char *s1, *s2; +{ + printf("%s %s\n", s1, s2); + exit(1); +} diff --git a/commands/m4/Ack.m4 b/commands/m4/Ack.m4 new file mode 100755 index 000000000..e05f24139 --- /dev/null +++ b/commands/m4/Ack.m4 @@ -0,0 +1,2 @@ +define(ack, `ifelse($1,0,incr($2),$2,0,`ack(DECR($1),1)', +`ack(DECR($1), ack($1,DECR($2)))')') diff --git a/commands/m4/Hanoi.m4 b/commands/m4/Hanoi.m4 new file mode 100755 index 000000000..6a8f1b632 --- /dev/null +++ b/commands/m4/Hanoi.m4 @@ -0,0 +1,7 @@ +define(hanoi, `trans(A, B, C, $1)') + +define(moved,`move disk from $1 to $2 +') + +define(trans, `ifelse($4,1,`moved($1,$2)', + `trans($1,$3,$2,DECR($4))moved($1,$2)trans($3,$2,$1,DECR($4))')') diff --git a/commands/m4/Hash.m4 b/commands/m4/Hash.m4 new file mode 100755 index 000000000..feb54b8a3 --- /dev/null +++ b/commands/m4/Hash.m4 @@ -0,0 +1,17 @@ +dnl This probably will not run on any m4 that cannot +dnl handle char constants in eval. +dnl +changequote(<,>) define(HASHVAL,99) dnl +define(hash,<eval(str(substr($1,1),0)%HASHVAL)>) dnl +define(str, + <ifelse($1,",$2, + <str(substr(<$1>,1),<eval($2+'substr($1,0,1)')>)>) + >) dnl +define(KEYWORD,<$1,hash($1),>) dnl +define(TSTART, +<struct prehash { + char *keyword; + int hashval; +} keytab[] = {>) dnl +define(TEND,< "",0 +};>) dnl diff --git a/commands/m4/M4.out b/commands/m4/M4.out new file mode 100755 index 000000000..5fb8693d6 --- /dev/null +++ b/commands/m4/M4.out @@ -0,0 +1,462 @@ +# +# test file for mp (not comprehensive) +# +# v7 m4 does not have `decr'. +# + +# +# include string macros +# + + + + + +# +# create some fortrash strings for an even uglier language +# +integer TEXT(5) +data TEXT(1)/LETt/ +data TEXT(2)/LETe/ +data TEXT(3)/LETx/ +data TEXT(4)/LETt/ + +data TEXT(5)/EOS/ + +integer DATA(5) +data DATA(1)/LETd/ +data DATA(2)/LETa/ +data DATA(3)/LETt/ +data DATA(4)/LETa/ + +data DATA(5)/EOS/ + +integer BEGIN(6) +data BEGIN(1)/LETb/ +data BEGIN(2)/LETe/ +data BEGIN(3)/LETg/ +data BEGIN(4)/LETi/ +data BEGIN(5)/LETn/ + +data BEGIN(6)/EOS/ + +integer END(4) +data END(1)/LETe/ +data END(2)/LETn/ +data END(3)/LETd/ + +data END(4)/EOS/ + +integer IF(3) +data IF(1)/LETi/ +data IF(2)/LETf/ + +data IF(3)/EOS/ + +integer THEN(5) +data THEN(1)/LETt/ +data THEN(2)/LETh/ +data THEN(3)/LETe/ +data THEN(4)/LETn/ + +data THEN(5)/EOS/ + +integer ELSE(5) +data ELSE(1)/LETe/ +data ELSE(2)/LETl/ +data ELSE(3)/LETs/ +data ELSE(4)/LETe/ + +data ELSE(5)/EOS/ + +integer CASE(5) +data CASE(1)/LETc/ +data CASE(2)/LETa/ +data CASE(3)/LETs/ +data CASE(4)/LETe/ + +data CASE(5)/EOS/ + +integer REPEAT(7) +data REPEAT(1)/LETr/ +data REPEAT(2)/LETe/ +data REPEAT(3)/LETp/ +data REPEAT(4)/LETe/ +data REPEAT(5)/LETa/ +data REPEAT(6)/LETt/ + +data REPEAT(7)/EOS/ + +integer WHILE(6) +data WHILE(1)/LETw/ +data WHILE(2)/LETh/ +data WHILE(3)/LETi/ +data WHILE(4)/LETl/ +data WHILE(5)/LETe/ + +data WHILE(6)/EOS/ + +integer DEFAULT(8) +data DEFAULT(1)/LETd/ +data DEFAULT(2)/LETe/ +data DEFAULT(3)/LETf/ +data DEFAULT(4)/LETa/ +data DEFAULT(5)/LETu/ +data DEFAULT(6)/LETl/ +data DEFAULT(7)/LETt/ + +data DEFAULT(8)/EOS/ + +integer UNTIL(6) +data UNTIL(1)/LETu/ +data UNTIL(2)/LETn/ +data UNTIL(3)/LETt/ +data UNTIL(4)/LETi/ +data UNTIL(5)/LETl/ + +data UNTIL(6)/EOS/ + +integer FUNCTION(9) +data FUNCTION(1)/LETf/ +data FUNCTION(2)/LETu/ +data FUNCTION(3)/LETn/ +data FUNCTION(4)/LETc/ +data FUNCTION(5)/LETt/ +data FUNCTION(6)/LETi/ +data FUNCTION(7)/LETo/ +data FUNCTION(8)/LETn/ + +data FUNCTION(9)/EOS/ + +integer PROCEDURE(10) +data PROCEDURE(1)/LETp/ +data PROCEDURE(2)/LETr/ +data PROCEDURE(3)/LETo/ +data PROCEDURE(4)/LETc/ +data PROCEDURE(5)/LETe/ +data PROCEDURE(6)/LETd/ +data PROCEDURE(7)/LETu/ +data PROCEDURE(8)/LETr/ +data PROCEDURE(9)/LETe/ + +data PROCEDURE(10)/EOS/ + +integer EXTERNAL(9) +data EXTERNAL(1)/LETe/ +data EXTERNAL(2)/LETx/ +data EXTERNAL(3)/LETt/ +data EXTERNAL(4)/LETe/ +data EXTERNAL(5)/LETr/ +data EXTERNAL(6)/LETn/ +data EXTERNAL(7)/LETa/ +data EXTERNAL(8)/LETl/ + +data EXTERNAL(9)/EOS/ + +integer FORWARD(8) +data FORWARD(1)/LETf/ +data FORWARD(2)/LETo/ +data FORWARD(3)/LETr/ +data FORWARD(4)/LETw/ +data FORWARD(5)/LETa/ +data FORWARD(6)/LETr/ +data FORWARD(7)/LETd/ + +data FORWARD(8)/EOS/ + +integer TYPE(5) +data TYPE(1)/LETt/ +data TYPE(2)/LETy/ +data TYPE(3)/LETp/ +data TYPE(4)/LETe/ + +data TYPE(5)/EOS/ + +integer VAR(4) +data VAR(1)/LETv/ +data VAR(2)/LETa/ +data VAR(3)/LETr/ + +data VAR(4)/EOS/ + +integer CONST(6) +data CONST(1)/LETc/ +data CONST(2)/LETo/ +data CONST(3)/LETn/ +data CONST(4)/LETs/ +data CONST(5)/LETt/ + +data CONST(6)/EOS/ + +integer PROGRAM(8) +data PROGRAM(1)/LETp/ +data PROGRAM(2)/LETr/ +data PROGRAM(3)/LETo/ +data PROGRAM(4)/LETg/ +data PROGRAM(5)/LETr/ +data PROGRAM(6)/LETa/ +data PROGRAM(7)/LETm/ + +data PROGRAM(8)/EOS/ + +integer INPUT(6) +data INPUT(1)/LETi/ +data INPUT(2)/LETn/ +data INPUT(3)/LETp/ +data INPUT(4)/LETu/ +data INPUT(5)/LETt/ + +data INPUT(6)/EOS/ + +integer OUTPUT(7) +data OUTPUT(1)/LETo/ +data OUTPUT(2)/LETu/ +data OUTPUT(3)/LETt/ +data OUTPUT(4)/LETp/ +data OUTPUT(5)/LETu/ +data OUTPUT(6)/LETt/ + +data OUTPUT(7)/EOS/ + +# + + +defined +# +# v7 m4 does this wrong. The right output is +# this is A vEry lon sEntEnCE +# see m4 documentation for translit. +# +this is A vEry lon sEntEnCE +# +# include towers-of-hanoi +# + + + + + + +# +# some reasonable set of disks +# +move disk from A to C +move disk from A to B +move disk from C to B +move disk from A to C +move disk from B to A +move disk from B to C +move disk from A to C +move disk from A to B +move disk from C to B +move disk from C to A +move disk from B to A +move disk from C to B +move disk from A to C +move disk from A to B +move disk from C to B +move disk from A to C +move disk from B to A +move disk from B to C +move disk from A to C +move disk from B to A +move disk from C to B +move disk from C to A +move disk from B to A +move disk from B to C +move disk from A to C +move disk from A to B +move disk from C to B +move disk from A to C +move disk from B to A +move disk from B to C +move disk from A to C +move disk from A to B +move disk from C to B +move disk from C to A +move disk from B to A +move disk from C to B +move disk from A to C +move disk from A to B +move disk from C to B +move disk from C to A +move disk from B to A +move disk from B to C +move disk from A to C +move disk from B to A +move disk from C to B +move disk from C to A +move disk from B to A +move disk from C to B +move disk from A to C +move disk from A to B +move disk from C to B +move disk from A to C +move disk from B to A +move disk from B to C +move disk from A to C +move disk from A to B +move disk from C to B +move disk from C to A +move disk from B to A +move disk from C to B +move disk from A to C +move disk from A to B +move disk from C to B + +# +# include ackermann's function +# + + +# +# something like (3,3) will blow away un*x m4. +# +9 +# +# include a square_root function for fixed nums +# + + + +# +# some square roots. +# +3 +10 +negative-square-root +146 +# +# some textual material for enjoyment. +# +[taken from the 'Clemson University Computer Newsletter', + September 1981, pp. 6-7] + +I am a wizard in the magical Kingdom of Transformation and I +slay dragons for a living. Actually, I am a systems programmer. +One of the problems with systems programming is explaining to +non-computer enthusiasts what that is. All of the terms I use to +describe my job are totally meaningless to them. Usually my response +to questions about my work is to say as little as possible. For +instance, if someone asks what happened at work this week, I say +"Nothing much" and then I change the subject. + +With the assistance of my brother, a mechanical engineer, I have devised +an analogy that everyone can understand. The analogy describes the +"Kingdom of Transformation" where travelers wander and are magically +transformed. This kingdom is the computer and the travelers are information. +The purpose of the computer is to change information to a more meaningful +forma. The law of conservation applies here: The computer never creates +and never intentionally destroys data. With no further ado, let us travel +to the Kingdom of Transformation: + +In a land far, far away, there is a magical kingdom called the Kingdom of +Transformation. A king rules over this land and employs a Council of +Wizardry. The main purpose of this kingdom is to provide a way for +neighboring kingdoms to transform citizens into more useful citizens. This +is done by allowing the citizens to enter the kingdom at one of its ports +and to travel any of the many routes in the kingdom. They are magically +transformed along the way. The income of the Kingdom of Transformation +comes from the many toll roads within its boundaries. + +The Kingdom of Transformation was created when several kingdoms got +together and discovered a mutual need for new talents and abilities for +citizens. They employed CTK, Inc. (Creators of Transformation, Inc.) to +create this kingdom. CTK designed the country, its transportation routes, +and its laws of transformation, and created the major highway system. + +Hazards +======= + +Because magic is not truly controllable, CTK invariably, but unknowingly, +creates dragons. Dragons are huge fire-breathing beasts which sometimes +injure or kill travelers. Fortunately, they do not travel, but always +remain near their den. + +Other hazards also exist which are potentially harmful. As the roads +become older and more weatherbeaten, pot-holes will develop, trees will +fall on travelers, etc. CTK maintenance men are called to fix these +problems. + +Wizards +======= + +The wizards play a major role in creating and maintaining the kingdom but +get little credit for their work because it is performed secretly. The +wizards do not wan the workers or travelers to learn their incantations +because many laws would be broken and chaos would result. + +CTK's grand design is always general enough to be applicable in many +different situations. As a result, it is often difficult to use. The +first duty of the wizards is to tailor the transformation laws so as to be +more beneficial and easier to use in their particular environment. + +After creation of the kingdom, a major duty of the wizards is to search for +and kill dragons. If travelers do not return on time or if they return +injured, the ruler of the country contacts the wizards. If the wizards +determine that the injury or death occurred due to the traveler's +negligence, they provide the traveler's country with additional warnings. +If not, they must determine if the cause was a road hazard or a dragon. If +the suspect a road hazard, they call in a CTK maintenance man to locate the +hazard and to eliminate it, as in repairing the pothole in the road. If +they think that cause was a dragon, then they must find and slay it. + +The most difficult part of eliminating a dragon is finding it. Sometimes +the wizard magically knows where the dragon's lair it, but often the wizard +must send another traveler along the same route and watch to see where he +disappears. This sounds like a failsafe method for finding dragons (and a +suicide mission for thr traveler) but the second traveler does not always +disappear. Some dragons eat any traveler who comes too close; others are +very picky. + +The wizards may call in CTK who designed the highway system and +transformation laws to help devise a way to locate the dragon. CTK also +helps provide the right spell or incantation to slay the dragon. (There is +no general spell to slay dragons; each dragon must be eliminated with a +different spell.) + +Because neither CTK nor wizards are perfect, spells to not always work +correctly. At best, nothing happens when the wrong spell is uttered. At +worst, the dragon becomes a much larger dragon or multiplies into several +smaller ones. In either case, new spells must be found. + +If all existing dragons are quiet (i.e. have eaten sufficiently), wizards +have time to do other things. They hide in castles and practice spells and +incatations. They also devise shortcuts for travelers and new laws of +transformation. + +Changes in the Kingdom +====================== + +As new transformation kingdoms are created and old ones are maintained, +CTK, Inc. is constantly learning new things. It learns ways to avoid +creating some of the dragons that they have previously created. It also +discovers new and better laws of transformation. As a result, CTK will +periodically create a new grand design which is far better than the old. +The wizards determine when is a good time to implement this new design. +This is when the tourist season is slow or when no important travelers +(VIPs) are to arrive. The kingdom must be closed for the actual +implementation and is leter reopened as a new and better place to go. + +A final question you might ask is what happens when the number of tourists +becomes too great for the kingdom to handle in a reasonable period of time +(i.e., the tourist lines at the ports are too long). The Kingdom of +Transformation has three options: (1) shorten the paths that a tourist must +travel, or (2) convince CTK to develop a faster breed of horses so that the +travelers can finish sooner, or (3) annex more territories so that the +kingdom can handle more travelers. + +Thus ends the story of the Kingdom of Transformation. I hope this has +explained my job to you: I slay dragons for a living. + +# +#should do an automatic undivert.. +# + +diversion #1 + +diversion #2 + +diversion #3 + +diversion #4 diff --git a/commands/m4/Makefile b/commands/m4/Makefile new file mode 100755 index 000000000..81505a528 --- /dev/null +++ b/commands/m4/Makefile @@ -0,0 +1,26 @@ +# Makefile for M4 + +# -DEXTENDED #if you like to get paste & spaste macros. +# -DVOID #if your C compiler does NOT support void. +# -DGETOPT #if you STILL do not have getopt in your library. +# -DDUFFCP #if you do not have fast memcpy in your library. +# + +CFLAGS = -DEXTENDED -O -D_POSIX_SOURCE -D_MINIX + +OBJ = main.o eval.o serv.o look.o misc.o expr.o +INCL = mdef.h extr.h patchlevel.h + +all: m4 + +m4: $(OBJ) $(INCL) + cc -i -o m4 $(OBJ) + install -S 4kw m4 + +install: /usr/bin/m4 + +/usr/bin/m4: m4 + install -cs -o bin m4 $@ + +clean: + rm -f *.o m4 core *bak diff --git a/commands/m4/READ_ME b/commands/m4/READ_ME new file mode 100755 index 000000000..7bb4b62f5 --- /dev/null +++ b/commands/m4/READ_ME @@ -0,0 +1,32 @@ +This code *is* PD. You (public) have all the rights to the code. [But +this also means you (singular) do not have any *extra* rights to the code, +hence it is impossible for you to restrict the use and distribution of +this code (original) in any way.] + +Dedication: + +This posting is a dedication to an old 750 that started out running 4.1BSD +and had 1.5 meg, 1 dz11, and 2 Rk07 drives. It was named yetti [sic] by +accident, and was managed by the author until its retirement two years +ago. [the name yetti now identifies a different machine] + +If you have any important fixes and/or speed improvements, I am much +interested. I am also interested in hearing about any unique applica- +tions of M4. I am NOT interested in gratuitous hacks or "neat" +kitchen-sink features. + +Author: + Usenet: uunet!utai!yunexus!oz || oz@nexus.yorku.ca + Bitnet: oz@yulibra.BITNET + Phonet: [416] 736-5257 x 3976 + + +enjoy. oz + +Testing: + +This directory contains a test file called test.m4. To use it, type + + m4 <Test.m4 >out + +The output file, out, should be identical to M4.out diff --git a/commands/m4/Sqroot.m4 b/commands/m4/Sqroot.m4 new file mode 100755 index 000000000..608790698 --- /dev/null +++ b/commands/m4/Sqroot.m4 @@ -0,0 +1,7 @@ +define(square_root, + `ifelse(eval($1<0),1,negative-square-root, + `square_root_aux($1, 1, eval(($1+1)/2))')') +define(square_root_aux, + `ifelse($3, $2, $3, + $3, eval($1/$2), $3, + `square_root_aux($1, $3, eval(($3+($1/$3))/2))')') diff --git a/commands/m4/String.m4 b/commands/m4/String.m4 new file mode 100755 index 000000000..0a2ed8127 --- /dev/null +++ b/commands/m4/String.m4 @@ -0,0 +1,8 @@ + +define(string,`integer $1(len(substr($2,1))) +str($1,substr($2,1),0) +data $1(len(substr($2,1)))/EOS/ +') + +define(str,`ifelse($2,",,data $1(incr($3))/`LET'substr($2,0,1)/ +`str($1,substr($2,1),incr($3))')') diff --git a/commands/m4/Test.m4 b/commands/m4/Test.m4 new file mode 100755 index 000000000..4fcbb7b2b --- /dev/null +++ b/commands/m4/Test.m4 @@ -0,0 +1,206 @@ +# +# test file for mp (not comprehensive) +# +# v7 m4 does not have `decr'. +# +define(DECR,`eval($1-1)') +# +# include string macros +# +include(String.m4) +# +# create some fortrash strings for an even uglier language +# +string(TEXT, "text") +string(DATA, "data") +string(BEGIN, "begin") +string(END, "end") +string(IF, "if") +string(THEN, "then") +string(ELSE, "else") +string(CASE, "case") +string(REPEAT, "repeat") +string(WHILE, "while") +string(DEFAULT, "default") +string(UNTIL, "until") +string(FUNCTION, "function") +string(PROCEDURE, "procedure") +string(EXTERNAL, "external") +string(FORWARD, "forward") +string(TYPE, "type") +string(VAR, "var") +string(CONST, "const") +string(PROGRAM, "program") +string(INPUT, "input") +string(OUTPUT, "output") +# +divert(2) +diversion #1 +divert(3) +diversion #2 +divert(4) +diversion #3 +divert(5) +diversion #4 +divert(0) +define(abc,xxx) +ifdef(`abc',defined,undefined) +# +# v7 m4 does this wrong. The right output is +# this is A vEry lon sEntEnCE +# see m4 documentation for translit. +# +translit(`this is a very long sentence', abcdefg, ABCDEF) +# +# include towers-of-hanoi +# +include(Hanoi.m4) +# +# some reasonable set of disks +# +hanoi(6) +# +# include ackermann's function +# +include(Ack.m4) +# +# something like (3,3) will blow away un*x m4. +# +ack(2,3) +# +# include a square_root function for fixed nums +# +include(Sqroot.m4) +# +# some square roots. +# +square_root(15) +square_root(100) +square_root(-4) +square_root(21372) +# +# some textual material for enjoyment. +# +[taken from the 'Clemson University Computer Newsletter', + September 1981, pp. 6-7] + +I am a wizard in the magical Kingdom of Transformation and I +slay dragons for a living. Actually, I am a systems programmer. +One of the problems with systems programming is explaining to +non-computer enthusiasts what that is. All of the terms I use to +describe my job are totally meaningless to them. Usually my response +to questions about my work is to say as little as possible. For +instance, if someone asks what happened at work this week, I say +"Nothing much" and then I change the subject. + +With the assistance of my brother, a mechanical engineer, I have devised +an analogy that everyone can understand. The analogy describes the +"Kingdom of Transformation" where travelers wander and are magically +transformed. This kingdom is the computer and the travelers are information. +The purpose of the computer is to change information to a more meaningful +forma. The law of conservation applies here: The computer never creates +and never intentionally destroys data. With no further ado, let us travel +to the Kingdom of Transformation: + +In a land far, far away, there is a magical kingdom called the Kingdom of +Transformation. A king rules over this land and employs a Council of +Wizardry. The main purpose of this kingdom is to provide a way for +neighboring kingdoms to transform citizens into more useful citizens. This +is done by allowing the citizens to enter the kingdom at one of its ports +and to travel any of the many routes in the kingdom. They are magically +transformed along the way. The income of the Kingdom of Transformation +comes from the many toll roads within its boundaries. + +The Kingdom of Transformation was created when several kingdoms got +together and discovered a mutual need for new talents and abilities for +citizens. They employed CTK, Inc. (Creators of Transformation, Inc.) to +create this kingdom. CTK designed the country, its transportation routes, +and its laws of transformation, and created the major highway system. + +Hazards +======= + +Because magic is not truly controllable, CTK invariably, but unknowingly, +creates dragons. Dragons are huge fire-breathing beasts which sometimes +injure or kill travelers. Fortunately, they do not travel, but always +remain near their den. + +Other hazards also exist which are potentially harmful. As the roads +become older and more weatherbeaten, pot-holes will develop, trees will +fall on travelers, etc. CTK maintenance men are called to fix these +problems. + +Wizards +======= + +The wizards play a major role in creating and maintaining the kingdom but +get little credit for their work because it is performed secretly. The +wizards do not wan the workers or travelers to learn their incantations +because many laws would be broken and chaos would result. + +CTK's grand design is always general enough to be applicable in many +different situations. As a result, it is often difficult to use. The +first duty of the wizards is to tailor the transformation laws so as to be +more beneficial and easier to use in their particular environment. + +After creation of the kingdom, a major duty of the wizards is to search for +and kill dragons. If travelers do not return on time or if they return +injured, the ruler of the country contacts the wizards. If the wizards +determine that the injury or death occurred due to the traveler's +negligence, they provide the traveler's country with additional warnings. +If not, they must determine if the cause was a road hazard or a dragon. If +the suspect a road hazard, they call in a CTK maintenance man to locate the +hazard and to eliminate it, as in repairing the pothole in the road. If +they think that cause was a dragon, then they must find and slay it. + +The most difficult part of eliminating a dragon is finding it. Sometimes +the wizard magically knows where the dragon's lair it, but often the wizard +must send another traveler along the same route and watch to see where he +disappears. This sounds like a failsafe method for finding dragons (and a +suicide mission for thr traveler) but the second traveler does not always +disappear. Some dragons eat any traveler who comes too close; others are +very picky. + +The wizards may call in CTK who designed the highway system and +transformation laws to help devise a way to locate the dragon. CTK also +helps provide the right spell or incantation to slay the dragon. (There is +no general spell to slay dragons; each dragon must be eliminated with a +different spell.) + +Because neither CTK nor wizards are perfect, spells to not always work +correctly. At best, nothing happens when the wrong spell is uttered. At +worst, the dragon becomes a much larger dragon or multiplies into several +smaller ones. In either case, new spells must be found. + +If all existing dragons are quiet (i.e. have eaten sufficiently), wizards +have time to do other things. They hide in castles and practice spells and +incatations. They also devise shortcuts for travelers and new laws of +transformation. + +Changes in the Kingdom +====================== + +As new transformation kingdoms are created and old ones are maintained, +CTK, Inc. is constantly learning new things. It learns ways to avoid +creating some of the dragons that they have previously created. It also +discovers new and better laws of transformation. As a result, CTK will +periodically create a new grand design which is far better than the old. +The wizards determine when is a good time to implement this new design. +This is when the tourist season is slow or when no important travelers +(VIPs) are to arrive. The kingdom must be closed for the actual +implementation and is leter reopened as a new and better place to go. + +A final question you might ask is what happens when the number of tourists +becomes too great for the kingdom to handle in a reasonable period of time +(i.e., the tourist lines at the ports are too long). The Kingdom of +Transformation has three options: (1) shorten the paths that a tourist must +travel, or (2) convince CTK to develop a faster breed of horses so that the +travelers can finish sooner, or (3) annex more territories so that the +kingdom can handle more travelers. + +Thus ends the story of the Kingdom of Transformation. I hope this has +explained my job to you: I slay dragons for a living. + +# +#should do an automatic undivert.. +# diff --git a/commands/m4/eval.c b/commands/m4/eval.c new file mode 100755 index 000000000..b1f472bd5 --- /dev/null +++ b/commands/m4/eval.c @@ -0,0 +1,331 @@ +/* + * eval.c + * Facility: m4 macro processor + * by: oz + */ + +#include "mdef.h" +#include "extr.h" + +/* + * eval - evaluate built-in macros. + * argc - number of elements in argv. + * argv - element vector : + * argv[0] = definition of a user + * macro or nil if built-in. + * argv[1] = name of the macro or + * built-in. + * argv[2] = parameters to user-defined + * . macro or built-in. + * . + * + * Note that the minimum value for argc is 3. A call in the form + * of macro-or-builtin() will result in: + * argv[0] = nullstr + * argv[1] = macro-or-builtin + * argv[2] = nullstr + * + */ + +void eval (argv, argc, td) +register char *argv[]; +register int argc; +register int td; +{ + register int c, n; + static int sysval; + +#ifdef DEBUG + printf("argc = %d\n", argc); + for (n = 0; n < argc; n++) + printf("argv[%d] = %s\n", n, argv[n]); +#endif + /* + * if argc == 3 and argv[2] is null, + * then we have macro-or-builtin() type call. + * We adjust argc to avoid further checking.. + * + */ + if (argc == 3 && !*(argv[2])) + argc--; + + switch (td & ~STATIC) { + + case DEFITYPE: + if (argc > 2) + dodefine(argv[2], (argc > 3) ? argv[3] : null); + break; + + case PUSDTYPE: + if (argc > 2) + dopushdef(argv[2], (argc > 3) ? argv[3] : null); + break; + + case DUMPTYPE: + dodump(argv, argc); + break; + + case EXPRTYPE: + /* + * doexpr - evaluate arithmetic expression + * + */ + if (argc > 2) + pbnum(expr(argv[2])); + break; + + case IFELTYPE: + if (argc > 4) + doifelse(argv, argc); + break; + + case IFDFTYPE: + /* + * doifdef - select one of two alternatives based + * on the existence of another definition + */ + if (argc > 3) { + if (lookup(argv[2]) != nil) + pbstr(argv[3]); + else if (argc > 4) + pbstr(argv[4]); + } + break; + + case LENGTYPE: + /* + * dolen - find the length of the argument + * + */ + if (argc > 2) + pbnum((argc > 2) ? strlen(argv[2]) : 0); + break; + + case INCRTYPE: + /* + * doincr - increment the value of the argument + * + */ + if (argc > 2) + pbnum(atoi(argv[2]) + 1); + break; + + case DECRTYPE: + /* + * dodecr - decrement the value of the argument + * + */ + if (argc > 2) + pbnum(atoi(argv[2]) - 1); + break; + +#if unix || vms + + case SYSCTYPE: + /* + * dosys - execute system command + * + */ + if (argc > 2) + sysval = system(argv[2]); + break; + + case SYSVTYPE: + /* + * dosysval - return value of the last system call. + * + */ + pbnum(sysval); + break; +#endif + + case INCLTYPE: + if (argc > 2) + if (!doincl(argv[2])) { + fprintf(stderr,"m4: %s: ",argv[2]); + error("cannot open for read."); + } + break; + + case SINCTYPE: + if (argc > 2) + (void) doincl(argv[2]); + break; +#ifdef EXTENDED + case PASTTYPE: + if (argc > 2) + if (!dopaste(argv[2])) { + fprintf(stderr,"m4: %s: ",argv[2]); + error("cannot open for read."); + } + break; + + case SPASTYPE: + if (argc > 2) + (void) dopaste(argv[2]); + break; +#endif + case CHNQTYPE: + dochq(argv, argc); + break; + + case CHNCTYPE: + dochc(argv, argc); + break; + + case SUBSTYPE: + /* + * dosub - select substring + * + */ + if (argc > 3) + dosub(argv,argc); + break; + + case SHIFTYPE: + /* + * doshift - push back all arguments except the + * first one (i.e. skip argv[2]) + */ + if (argc > 3) { + for (n = argc-1; n > 3; n--) { + putback(rquote); + pbstr(argv[n]); + putback(lquote); + putback(','); + } + putback(rquote); + pbstr(argv[3]); + putback(lquote); + } + break; + + case DIVRTYPE: + if (argc > 2 && (n = atoi(argv[2])) != 0) + dodiv(n); + else { + active = stdout; + oindex = 0; + } + break; + + case UNDVTYPE: + doundiv(argv, argc); + break; + + case DIVNTYPE: + /* + * dodivnum - return the number of current + * output diversion + * + */ + pbnum(oindex); + break; + + case UNDFTYPE: + /* + * doundefine - undefine a previously defined + * macro(s) or m4 keyword(s). + */ + if (argc > 2) + for (n = 2; n < argc; n++) + remhash(argv[n], ALL); + break; + + case POPDTYPE: + /* + * dopopdef - remove the topmost definitions of + * macro(s) or m4 keyword(s). + */ + if (argc > 2) + for (n = 2; n < argc; n++) + remhash(argv[n], TOP); + break; + + case MKTMTYPE: + /* + * dotemp - create a temporary file + * + */ + if (argc > 2) + pbstr(mktemp(argv[2])); + break; + + case TRNLTYPE: + /* + * dotranslit - replace all characters in the + * source string that appears in + * the "from" string with the corresponding + * characters in the "to" string. + * + */ + if (argc > 3) { + char temp[MAXTOK]; + if (argc > 4) + map(temp, argv[2], argv[3], argv[4]); + else + map(temp, argv[2], argv[3], null); + pbstr(temp); + } + else + if (argc > 2) + pbstr(argv[2]); + break; + + case INDXTYPE: + /* + * doindex - find the index of the second argument + * string in the first argument string. + * -1 if not present. + */ + pbnum((argc > 3) ? indx(argv[2], argv[3]) : -1); + break; + + case ERRPTYPE: + /* + * doerrp - print the arguments to stderr file + * + */ + if (argc > 2) { + for (n = 2; n < argc; n++) + fprintf(stderr,"%s ", argv[n]); + fprintf(stderr, "\n"); + } + break; + + case DNLNTYPE: + /* + * dodnl - eat-up-to and including newline + * + */ + while ((c = gpbc()) != '\n' && c != EOF) + ; + break; + + case M4WRTYPE: + /* + * dom4wrap - set up for wrap-up/wind-down activity + * + */ + m4wraps = (argc > 2) ? strsave(argv[2]) : null; + break; + + case EXITTYPE: + /* + * doexit - immediate exit from m4. + * + */ + exit((argc > 2) ? atoi(argv[2]) : 0); + break; + + case DEFNTYPE: + if (argc > 2) + for (n = 2; n < argc; n++) + dodefn(argv[n]); + break; + + default: + error("m4: major botch in eval."); + break; + } +} diff --git a/commands/m4/expr.c b/commands/m4/expr.c new file mode 100755 index 000000000..668bb623a --- /dev/null +++ b/commands/m4/expr.c @@ -0,0 +1,587 @@ +/* + * expression evaluator: performs a standard recursive + * descent parse to evaluate any expression permissible + * within the following grammar: + * + * expr : query EOS + * query : lor + * | lor "?" query ":" query + * lor : land { "||" land } + * land : bor { "&&" bor } + * bor : bxor { "|" bxor } + * bxor : band { "^" band } + * band : eql { "&" eql } + * eql : relat { eqrel relat } + * relat : shift { rel shift } + * shift : primary { shop primary } + * primary : term { addop term } + * term : unary { mulop unary } + * unary : factor + * | unop unary + * factor : constant + * | "(" query ")" + * constant: num + * | "'" CHAR "'" + * num : decnum + * | "0" octnum + * | "0x" hexnum + * octnum : OCTDIGIT + * | OCTDIGIT octnum + * decnum : DECDIGIT + * | DECDIGIT decnum + * hexnum : HEXDIGIT + * | HEXDIGIT hexnum + * shop : "<<" + * | ">>" + * eqlrel : "=" + * | "==" + * | "!=" + * rel : "<" + * | ">" + * | "<=" + * | ">=" + * + * + * This expression evaluator is lifted from a public-domain + * C Pre-Processor included with the DECUS C Compiler distribution. + * It is hacked somewhat to be suitable for m4. + * + * Originally by: Mike Lutz + * Bob Harper + */ + +#include "mdef.h" + +#define TRUE 1 +#define FALSE 0 +#define EOS (char) 0 +#define EQL 0 +#define NEQ 1 +#define LSS 2 +#define LEQ 3 +#define GTR 4 +#define GEQ 5 + +static char *nxtch; /* Parser scan pointer */ + +/* + * For longjmp + */ +#include <setjmp.h> +static jmp_buf expjump; + +/* + * macros: + * + * ungetch - Put back the last character examined. + * getch - return the next character from expr string. + */ +#define ungetch() nxtch-- +#define getch() *nxtch++ + +int expr(expbuf) +char *expbuf; +{ + register int rval; + + nxtch = expbuf; + if (setjmp(expjump) != 0) + return (FALSE); + rval = query(); + if (skipws() == EOS) + return(rval); + experr("Ill-formed expression"); + /* NOTREACHED */ + return(0); +} + +/* + * query : lor | lor '?' query ':' query + * + */ +int query() +{ + register int bool, true_val, false_val; + + bool = lor(); + if (skipws() != '?') { + ungetch(); + return(bool); + } + + true_val = query(); + if (skipws() != ':') + experr("Bad query"); + + false_val = query(); + return(bool ? true_val : false_val); +} + +/* + * lor : land { '||' land } + * + */ +int lor() +{ + register int c, vl, vr; + + vl = land(); + while ((c = skipws()) == '|' && getch() == '|') { + vr = land(); + vl = vl || vr; + } + + if (c == '|') + ungetch(); + ungetch(); + return(vl); +} + +/* + * land : bor { '&&' bor } + * + */ +int land() +{ + register int c, vl, vr; + + vl = bor(); + while ((c = skipws()) == '&' && getch() == '&') { + vr = bor(); + vl = vl && vr; + } + + if (c == '&') + ungetch(); + ungetch(); + return(vl); +} + +/* + * bor : bxor { '|' bxor } + * + */ +int bor() +{ + register int vl, vr, c; + + vl = bxor(); + while ((c = skipws()) == '|' && getch() != '|') { + ungetch(); + vr = bxor(); + vl |= vr; + } + + if (c == '|') + ungetch(); + ungetch(); + return(vl); +} + +/* + * bxor : band { '^' band } + * + */ +int bxor() +{ + register int vl, vr; + + vl = band(); + while (skipws() == '^') { + vr = band(); + vl ^= vr; + } + + ungetch(); + return(vl); +} + +/* + * band : eql { '&' eql } + * + */ +int band() +{ + register int vl, vr, c; + + vl = eql(); + while ((c = skipws()) == '&' && getch() != '&') { + ungetch(); + vr = eql(); + vl &= vr; + } + + if (c == '&') + ungetch(); + ungetch(); + return(vl); +} + +/* + * eql : relat { eqrel relat } + * + */ +int eql() +{ + register int vl, vr, rel; + + vl = relat(); + while ((rel = geteql()) != -1) { + vr = relat(); + + switch (rel) { + + case EQL: + vl = (vl == vr); + break; + case NEQ: + vl = (vl != vr); + break; + } + } + return(vl); +} + +/* + * relat : shift { rel shift } + * + */ +int relat() +{ + register int vl, vr, rel; + + vl = shift(); + while ((rel = getrel()) != -1) { + + vr = shift(); + switch (rel) { + + case LEQ: + vl = (vl <= vr); + break; + case LSS: + vl = (vl < vr); + break; + case GTR: + vl = (vl > vr); + break; + case GEQ: + vl = (vl >= vr); + break; + } + } + return(vl); +} + +/* + * shift : primary { shop primary } + * + */ +int shift() +{ + register int vl, vr, c; + + vl = primary(); + while (((c = skipws()) == '<' || c == '>') && c == getch()) { + vr = primary(); + + if (c == '<') + vl <<= vr; + else + vl >>= vr; + } + + if (c == '<' || c == '>') + ungetch(); + ungetch(); + return(vl); +} + +/* + * primary : term { addop term } + * + */ +int primary() +{ + register int c, vl, vr; + + vl = term(); + while ((c = skipws()) == '+' || c == '-') { + vr = term(); + if (c == '+') + vl += vr; + else + vl -= vr; + } + + ungetch(); + return(vl); +} + +/* + * <term> := <unary> { <mulop> <unary> } + * + */ +int term() +{ + register int c, vl, vr; + + vl = unary(); + while ((c = skipws()) == '*' || c == '/' || c == '%') { + vr = unary(); + + switch (c) { + case '*': + vl *= vr; + break; + case '/': + vl /= vr; + break; + case '%': + vl %= vr; + break; + } + } + ungetch(); + return(vl); +} + +/* + * unary : factor | unop unary + * + */ +int unary() +{ + register int val, c; + + if ((c = skipws()) == '!' || c == '~' || c == '-') { + val = unary(); + + switch (c) { + case '!': + return(! val); + case '~': + return(~ val); + case '-': + return(- val); + } + } + + ungetch(); + return(factor()); +} + +/* + * factor : constant | '(' query ')' + * + */ +int factor() +{ + register int val; + + if (skipws() == '(') { + val = query(); + if (skipws() != ')') + experr("Bad factor"); + return(val); + } + + ungetch(); + return(constant()); +} + +/* + * constant: num | 'char' + * + */ +int constant() +{ + /* + * Note: constant() handles multi-byte constants + */ + + register int i; + register int value; + register char c; + int v[sizeof (int)]; + + if (skipws() != '\'') { + ungetch(); + return(num()); + } + for (i = 0; i < sizeof(int); i++) { + if ((c = getch()) == '\'') { + ungetch(); + break; + } + if (c == '\\') { + switch (c = getch()) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + ungetch(); + c = num(); + break; + case 'n': + c = 012; + break; + case 'r': + c = 015; + break; + case 't': + c = 011; + break; + case 'b': + c = 010; + break; + case 'f': + c = 014; + break; + } + } + v[i] = c; + } + if (i == 0 || getch() != '\'') + experr("Illegal character constant"); + for (value = 0; --i >= 0;) { + value <<= 8; + value += v[i]; + } + return(value); +} + +/* + * num : digit | num digit + * + */ +int num() +{ + register int rval, c, base; + int ndig; + + ndig = 0; + if ((c = skipws()) == '0') { + c = getch (); + if (c == 'x' || c == 'X') { + base = 16; + c = getch (); + } else { + base = 8; + ndig = 1; + } + } else { + base = 10; + } + rval = 0; + for (;;) { + if (isdigit(c)) c -= '0'; + else if (isupper (c)) c -= ('A' - 10); + else if (islower (c)) c -= ('a' - 10); + else break; + if (c < 0 || c >= base) + break; + + rval *= base; + rval += c; + c = getch(); + ndig++; + } + ungetch(); + if (ndig) + return(rval); + experr("Bad constant"); + /* NOTREACHED */ + return(0); +} + +/* + * eqlrel : '=' | '==' | '!=' + * + */ +int geteql() +{ + register int c1, c2; + + c1 = skipws(); + c2 = getch(); + + switch (c1) { + + case '=': + if (c2 != '=') + ungetch(); + return(EQL); + + case '!': + if (c2 == '=') + return(NEQ); + ungetch(); + ungetch(); + return(-1); + + default: + ungetch(); + ungetch(); + return(-1); + } +} + +/* + * rel : '<' | '>' | '<=' | '>=' + * + */ +int getrel() +{ + register int c1, c2; + + c1 = skipws(); + c2 = getch(); + + switch (c1) { + + case '<': + if (c2 == '=') + return(LEQ); + ungetch(); + return(LSS); + + case '>': + if (c2 == '=') + return(GEQ); + ungetch(); + return(GTR); + + default: + ungetch(); + ungetch(); + return(-1); + } +} + +/* + * Skip over any white space and return terminating char. + */ +int skipws() +{ + register char c; + + while ((c = getch()) <= ' ' && c > EOS) + ; + return(c); +} + +/* + * Error handler - resets environment to eval(), prints an error, + * and returns FALSE. + */ +int experr(msg) +char *msg; +{ + printf("mp: %s\n",msg); + longjmp(expjump, -1); /* Force eval() to return FALSE */ +} diff --git a/commands/m4/extr.h b/commands/m4/extr.h new file mode 100755 index 000000000..3572b1346 --- /dev/null +++ b/commands/m4/extr.h @@ -0,0 +1,21 @@ +extern ndptr hashtab[]; /* hash table for macros etc. */ +extern char buf[]; /* push-back buffer */ +extern char *bp; /* first available character */ +extern char *endpbb; /* end of push-back buffer */ +extern stae mstack[]; /* stack of m4 machine */ +extern char *ep; /* first free char in strspace */ +extern char *endest; /* end of string space */ +extern int sp; /* current m4 stack pointer */ +extern int fp; /* m4 call frame pointer */ +extern FILE *infile[]; /* input file stack (0=stdin) */ +extern FILE *outfile[]; /* diversion array(0=bitbucket)*/ +extern FILE *active; /* active output file pointer */ +extern char *m4temp; /* filename for diversions */ +extern int ilevel; /* input file stack pointer */ +extern int oindex; /* diversion index.. */ +extern char *null; /* as it says.. just a null.. */ +extern char *m4wraps; /* m4wrap string default.. */ +extern char lquote; /* left quote character (`) */ +extern char rquote; /* right quote character (') */ +extern char scommt; /* start character for comment */ +extern char ecommt; /* end character for comment */ diff --git a/commands/m4/look.c b/commands/m4/look.c new file mode 100755 index 000000000..72d4574c1 --- /dev/null +++ b/commands/m4/look.c @@ -0,0 +1,110 @@ +/* + * look.c + * Facility: m4 macro processor + * by: oz + */ + +#include "mdef.h" +#include "extr.h" + +/* + * hash - compute hash value using the proverbial + * hashing function. Taken from K&R. + */ +int hash (name) +register char *name; +{ + register int h = 0; + while (*name) + h += *name++; + return (h % HASHSIZE); +} + +/* + * lookup - find name in the hash table + * + */ +ndptr lookup(name) +char *name; +{ + register ndptr p; + + for (p = hashtab[hash(name)]; p != nil; p = p->nxtptr) + if (strcmp(name, p->name) == 0) + break; + return (p); +} + +/* + * addent - hash and create an entry in the hash + * table. The new entry is added in front + * of a hash bucket. + */ +ndptr addent(name) +char *name; +{ + register int h; + ndptr p; + + h = hash(name); + if ((p = (ndptr) malloc(sizeof(struct ndblock))) != NULL) { + p->nxtptr = hashtab[h]; + hashtab[h] = p; + p->name = strsave(name); + } + else + error("m4: no more memory."); + return p; +} + +/* + * remhash - remove an entry from the hashtable + * + */ +void remhash(name, all) +char *name; +int all; +{ + register int h; + register ndptr xp, tp, mp; + + h = hash(name); + mp = hashtab[h]; + tp = nil; + while (mp != nil) { + if (strcmp(mp->name, name) == 0) { + mp = mp->nxtptr; + if (tp == nil) { + freent(hashtab[h]); + hashtab[h] = mp; + } + else { + xp = tp->nxtptr; + tp->nxtptr = mp; + freent(xp); + } + if (!all) + break; + } + else { + tp = mp; + mp = mp->nxtptr; + } + } +} + +/* + * freent - free a hashtable information block + * + */ +void freent(p) +ndptr p; +{ + if (!(p->type & STATIC)) { + free(p->name); + if (p->defn != null) + free(p->defn); + } + free(p); +} + diff --git a/commands/m4/main.c b/commands/m4/main.c new file mode 100755 index 000000000..d54b4aac3 --- /dev/null +++ b/commands/m4/main.c @@ -0,0 +1,445 @@ +/* + * main.c + * Facility: m4 macro processor + * by: oz + */ + +#include "mdef.h" + +/* + * m4 - macro processor + * + * PD m4 is based on the macro tool distributed with the software + * tools (VOS) package, and described in the "SOFTWARE TOOLS" and + * "SOFTWARE TOOLS IN PASCAL" books. It has been expanded to include + * most of the command set of SysV m4, the standard UN*X macro processor. + * + * Since both PD m4 and UN*X m4 are based on SOFTWARE TOOLS macro, + * there may be certain implementation similarities between + * the two. The PD m4 was produced without ANY references to m4 + * sources. + * + * References: + * + * Software Tools distribution: macro + * + * Kernighan, Brian W. and P. J. Plauger, SOFTWARE + * TOOLS IN PASCAL, Addison-Wesley, Mass. 1981 + * + * Kernighan, Brian W. and P. J. Plauger, SOFTWARE + * TOOLS, Addison-Wesley, Mass. 1976 + * + * Kernighan, Brian W. and Dennis M. Ritchie, + * THE M4 MACRO PROCESSOR, Unix Programmer's Manual, + * Seventh Edition, Vol. 2, Bell Telephone Labs, 1979 + * + * System V man page for M4 + * + * Modification History: + * + * Jan 28 1986 Oz Break the whole thing into little + * pieces, for easier (?) maintenance. + * + * Dec 12 1985 Oz Optimize the code, try to squeeze + * few microseconds out.. + * + * Dec 05 1985 Oz Add getopt interface, define (-D), + * undefine (-U) options. + * + * Oct 21 1985 Oz Clean up various bugs, add comment handling. + * + * June 7 1985 Oz Add some of SysV m4 stuff (m4wrap, pushdef, + * popdef, decr, shift etc.). + * + * June 5 1985 Oz Initial cut. + * + * Implementation Notes: + * + * [1] PD m4 uses a different (and simpler) stack mechanism than the one + * described in Software Tools and Software Tools in Pascal books. + * The triple stack nonsense is replaced with a single stack containing + * the call frames and the arguments. Each frame is back-linked to a + * previous stack frame, which enables us to rewind the stack after + * each nested call is completed. Each argument is a character pointer + * to the beginning of the argument string within the string space. + * The only exceptions to this are (*) arg 0 and arg 1, which are + * the macro definition and macro name strings, stored dynamically + * for the hash table. + * + * . . + * | . | <-- sp | . | + * +-------+ +-----+ + * | arg 3 ------------------------------->| str | + * +-------+ | . | + * | arg 2 --------------+ . + * +-------+ | + * * | | | + * +-------+ | +-----+ + * | plev | <-- fp +---------------->| str | + * +-------+ | . | + * | type | . + * +-------+ + * | prcf -----------+ plev: paren level + * +-------+ | type: call type + * | . | | prcf: prev. call frame + * . | + * +-------+ | + * | <----------+ + * +-------+ + * + * [2] We have three types of null values: + * + * nil - nodeblock pointer type 0 + * null - null string ("") + * NULL - Stdio-defined NULL + * + */ + +ndptr hashtab[HASHSIZE]; /* hash table for macros etc. */ +char buf[BUFSIZE]; /* push-back buffer */ +char *bp = buf; /* first available character */ +char *endpbb = buf+BUFSIZE; /* end of push-back buffer */ +stae mstack[STACKMAX+1]; /* stack of m4 machine */ +char strspace[STRSPMAX+1]; /* string space for evaluation */ +char *ep = strspace; /* first free char in strspace */ +char *endest= strspace+STRSPMAX;/* end of string space */ +int sp; /* current m4 stack pointer */ +int fp; /* m4 call frame pointer */ +FILE *infile[MAXINP]; /* input file stack (0=stdin) */ +FILE *outfile[MAXOUT]; /* diversion array(0=bitbucket)*/ +FILE *active; /* active output file pointer */ +char *m4temp; /* filename for diversions */ +int ilevel = 0; /* input file stack pointer */ +int oindex = 0; /* diversion index.. */ +char *null = ""; /* as it says.. just a null.. */ +char *m4wraps = ""; /* m4wrap string default.. */ +char lquote = LQUOTE; /* left quote character (`) */ +char rquote = RQUOTE; /* right quote character (') */ +char scommt = SCOMMT; /* start character for comment */ +char ecommt = ECOMMT; /* end character for comment */ +struct keyblk keywrds[] = { /* m4 keywords to be installed */ + "include", INCLTYPE, + "sinclude", SINCTYPE, + "define", DEFITYPE, + "defn", DEFNTYPE, + "divert", DIVRTYPE, + "expr", EXPRTYPE, + "eval", EXPRTYPE, + "substr", SUBSTYPE, + "ifelse", IFELTYPE, + "ifdef", IFDFTYPE, + "len", LENGTYPE, + "incr", INCRTYPE, + "decr", DECRTYPE, + "dnl", DNLNTYPE, + "changequote", CHNQTYPE, + "changecom", CHNCTYPE, + "index", INDXTYPE, +#ifdef EXTENDED + "paste", PASTTYPE, + "spaste", SPASTYPE, +#endif + "popdef", POPDTYPE, + "pushdef", PUSDTYPE, + "dumpdef", DUMPTYPE, + "shift", SHIFTYPE, + "translit", TRNLTYPE, + "undefine", UNDFTYPE, + "undivert", UNDVTYPE, + "divnum", DIVNTYPE, + "maketemp", MKTMTYPE, + "errprint", ERRPTYPE, + "m4wrap", M4WRTYPE, + "m4exit", EXITTYPE, +#if unix || vms + "syscmd", SYSCTYPE, + "sysval", SYSVTYPE, +#endif +#if unix + "unix", MACRTYPE, +#else +#if vms + "vms", MACRTYPE, +#endif +#endif +}; + +#define MAXKEYS (sizeof(keywrds)/sizeof(struct keyblk)) + +extern int optind; +extern char *optarg; + +int main(argc,argv) +int argc; +char *argv[]; +{ + register int c; + register int n; + char *p; + static char divnam[] = DIVNAM; + + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, onintr); +#ifdef NONZEROPAGES + initm4(); +#endif + initkwds(); + + while ((c = getopt(argc, argv, "tD:U:o:")) != EOF) + switch(c) { + + case 'D': /* define something..*/ + for (p = optarg; *p; p++) + if (*p == '=') + break; + if (*p) + *p++ = EOS; + dodefine(optarg, p); + break; + case 'U': /* undefine... */ + remhash(optarg, TOP); + break; + case 'o': /* specific output */ + case '?': + default: + usage(); + } + + infile[0] = stdin; /* default input (naturally) */ + active = stdout; /* default active output */ + m4temp = mktemp(divnam); /* filename for diversions */ + + sp = -1; /* stack pointer initialized */ + fp = 0; /* frame pointer initialized */ + + macro(); /* get some work done here */ + + if (*m4wraps) { /* anything for rundown ?? */ + ilevel = 0; /* in case m4wrap includes.. */ + putback(EOF); /* eof is a must !! */ + pbstr(m4wraps); /* user-defined wrapup act */ + macro(); /* last will and testament */ + } + else /* default wrap-up: undivert */ + for (n = 1; n < MAXOUT; n++) + if (outfile[n] != NULL) + getdiv(n); + + /* remove bitbucket if used */ + if (outfile[0] != NULL) { + (void) fclose(outfile[0]); + m4temp[UNIQUE] = '0'; +#if vms + (void) remove(m4temp); +#else + (void) unlink(m4temp); +#endif + } + + exit(0); +} + +/* + * macro - the work horse.. + * + */ +void macro() { + char token[MAXTOK]; + register char *s; + register int t, l; + register ndptr p; + register int nlpar; + + cycle { + if ((t = gpbc()) == '_' || isalpha(t)) { + putback(t); + if ((p = inspect(s = token)) == nil) { + if (sp < 0) + while (*s) + putc(*s++, active); + else + while (*s) + chrsave(*s++); + } + else { + /* + * real thing.. First build a call frame: + * + */ + pushf(fp); /* previous call frm */ + pushf(p->type); /* type of the call */ + pushf(0); /* parenthesis level */ + fp = sp; /* new frame pointer */ + /* + * now push the string arguments: + * + */ + pushs(p->defn); /* defn string */ + pushs(p->name); /* macro name */ + pushs(ep); /* start next..*/ + + putback(l = gpbc()); + if (l != LPAREN) { /* add bracks */ + putback(RPAREN); + putback(LPAREN); + } + } + } + else if (t == EOF) { + if (sp > -1) + error("m4: unexpected end of input"); + if (--ilevel < 0) + break; /* all done thanks.. */ + (void) fclose(infile[ilevel+1]); + continue; + } + /* + * non-alpha single-char token seen.. + * [the order of else if .. stmts is + * important.] + * + */ + else if (t == lquote) { /* strip quotes */ + nlpar = 1; + do { + if ((l = gpbc()) == rquote) + nlpar--; + else if (l == lquote) + nlpar++; + else if (l == EOF) + error("m4: missing right quote"); + if (nlpar > 0) { + if (sp < 0) + putc(l, active); + else + chrsave(l); + } + } + while (nlpar != 0); + } + + else if (sp < 0) { /* not in a macro at all */ + if (t == scommt) { /* comment handling here */ + putc(t, active); + while ((t = gpbc()) != ecommt) + putc(t, active); + } + putc(t, active); /* output directly.. */ + } + + else switch(t) { + + case LPAREN: + if (PARLEV > 0) + chrsave(t); + while (isspace(l = gpbc())) + ; /* skip blank, tab, nl.. */ + putback(l); + PARLEV++; + break; + + case RPAREN: + if (--PARLEV > 0) + chrsave(t); + else { /* end of argument list */ + chrsave(EOS); + + if (sp == STACKMAX) + error("m4: internal stack overflow"); + + if (CALTYP == MACRTYPE) + expand((char**)mstack+fp+1,(int)sp-fp); + else + eval((char**)mstack+fp+1,sp-fp,CALTYP); + + ep = PREVEP; /* flush strspace */ + sp = PREVSP; /* previous sp.. */ + fp = PREVFP; /* rewind stack...*/ + } + break; + + case COMMA: + if (PARLEV == 1) { + chrsave(EOS); /* new argument */ + while (isspace(l = gpbc())) + ; + putback(l); + pushs(ep); + } + break; + default: + chrsave(t); /* stack the char */ + break; + } + } +} + + +/* + * build an input token.. + * consider only those starting with _ or A-Za-z. This is a + * combo with lookup to speed things up. + */ +ndptr +inspect(tp) +register char *tp; +{ + register int h = 0; + register char c; + register char *name = tp; + register char *etp = tp+MAXTOK; + register ndptr p; + + while (tp < etp && (isalnum(c = gpbc()) || c == '_')) + h += (*tp++ = c); + putback(c); + if (tp == etp) + error("m4: token too long"); + *tp = EOS; + for (p = hashtab[h%HASHSIZE]; p != nil; p = p->nxtptr) + if (strcmp(name, p->name) == 0) + break; + return(p); +} + +#ifdef NONZEROPAGES +/* + * initm4 - initialize various tables. Useful only if your system + * does not know anything about demand-zero pages. + * + */ +void initm4() +{ + register int i; + + for (i = 0; i < HASHSIZE; i++) + hashtab[i] = nil; + for (i = 0; i < MAXOUT; i++) + outfile[i] = NULL; +} +#endif + +/* + * initkwds - initialise m4 keywords as fast as possible. + * This very similar to install, but without certain overheads, + * such as calling lookup. Malloc is not used for storing the + * keyword strings, since we simply use the static pointers + * within keywrds block. We also assume that there is enough memory + * to at least install the keywords (i.e. malloc won't fail). + * + */ +void initkwds() { + register int i; + register int h; + register ndptr p; + + for (i = 0; i < MAXKEYS; i++) { + h = hash(keywrds[i].knam); + p = (ndptr) malloc(sizeof(struct ndblock)); + p->nxtptr = hashtab[h]; + hashtab[h] = p; + p->name = keywrds[i].knam; + p->defn = null; + p->type = keywrds[i].ktyp | STATIC; + } +} diff --git a/commands/m4/mdef.h b/commands/m4/mdef.h new file mode 100755 index 000000000..1e3cd5924 --- /dev/null +++ b/commands/m4/mdef.h @@ -0,0 +1,276 @@ +/* + * mdef.h + * Facility: m4 macro processor + * by: oz + */ + + +#define unix 1 /* (kjb) */ + +#ifndef unix +#define unix 0 +#endif + +#ifndef vms +#define vms 0 +#endif + +#if vms + +#include stdio +#include ctype +#include signal + +#else + +#include <sys/types.h> +#include <ctype.h> +#include <signal.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#endif + +/* + * + * m4 constants.. + * + */ + +#define MACRTYPE 1 +#define DEFITYPE 2 +#define EXPRTYPE 3 +#define SUBSTYPE 4 +#define IFELTYPE 5 +#define LENGTYPE 6 +#define CHNQTYPE 7 +#define SYSCTYPE 8 +#define UNDFTYPE 9 +#define INCLTYPE 10 +#define SINCTYPE 11 +#define PASTTYPE 12 +#define SPASTYPE 13 +#define INCRTYPE 14 +#define IFDFTYPE 15 +#define PUSDTYPE 16 +#define POPDTYPE 17 +#define SHIFTYPE 18 +#define DECRTYPE 19 +#define DIVRTYPE 20 +#define UNDVTYPE 21 +#define DIVNTYPE 22 +#define MKTMTYPE 23 +#define ERRPTYPE 24 +#define M4WRTYPE 25 +#define TRNLTYPE 26 +#define DNLNTYPE 27 +#define DUMPTYPE 28 +#define CHNCTYPE 29 +#define INDXTYPE 30 +#define SYSVTYPE 31 +#define EXITTYPE 32 +#define DEFNTYPE 33 + +#define STATIC 128 + +/* + * m4 special characters + */ + +#define ARGFLAG '$' +#define LPAREN '(' +#define RPAREN ')' +#define LQUOTE '`' +#define RQUOTE '\'' +#define COMMA ',' +#define SCOMMT '#' +#define ECOMMT '\n' + +/* + * definitions of diversion files. If the name of + * the file is changed, adjust UNIQUE to point to the + * wildcard (*) character in the filename. + */ + +#if unix +#define DIVNAM "/tmp/m4*XXXXXX" /* unix diversion files */ +#define UNIQUE 7 /* unique char location */ +#else +#if vms +#define DIVNAM "sys$login:m4*XXXXXX" /* vms diversion files */ +#define UNIQUE 12 /* unique char location */ +#else +#define DIVNAM "\M4*XXXXXX" /* msdos diversion files */ +#define UNIQUE 3 /* unique char location */ +#endif +#endif + +/* + * other important constants + */ + +#define EOS (char) 0 +#define MAXINP 10 /* maximum include files */ +#define MAXOUT 10 /* maximum # of diversions */ +#define MAXSTR 512 /* maximum size of string */ +#define BUFSIZE 4096 /* size of pushback buffer */ +#define STACKMAX 1024 /* size of call stack */ +#define STRSPMAX 4096 /* size of string space */ +#define MAXTOK MAXSTR /* maximum chars in a tokn */ +#define HASHSIZE 199 /* maximum size of hashtab */ + +#define ALL 1 +#define TOP 0 + +#define TRUE 1 +#define FALSE 0 +#define cycle for(;;) + +#ifdef VOID +#define void int /* define if void is void. */ +#endif + +/* + * m4 data structures + */ + +typedef struct ndblock *ndptr; + +struct ndblock { /* hastable structure */ + char *name; /* entry name.. */ + char *defn; /* definition.. */ + int type; /* type of the entry.. */ + ndptr nxtptr; /* link to next entry.. */ +}; + +#define nil ((ndptr) 0) + +struct keyblk { + char *knam; /* keyword name */ + int ktyp; /* keyword type */ +}; + +typedef union { /* stack structure */ + int sfra; /* frame entry */ + char *sstr; /* string entry */ +} stae; + +/* + * macros for readibility and/or speed + * + * gpbc() - get a possibly pushed-back character + * min() - select the minimum of two elements + * pushf() - push a call frame entry onto stack + * pushs() - push a string pointer onto stack + */ +#define gpbc() (bp > buf) ? *--bp : getc(infile[ilevel]) +#define min(x,y) ((x > y) ? y : x) +#define pushf(x) if (sp < STACKMAX) mstack[++sp].sfra = (x) +#define pushs(x) if (sp < STACKMAX) mstack[++sp].sstr = (x) + +/* + * . . + * | . | <-- sp | . | + * +-------+ +-----+ + * | arg 3 ----------------------->| str | + * +-------+ | . | + * | arg 2 ---PREVEP-----+ . + * +-------+ | + * . | | | + * +-------+ | +-----+ + * | plev | PARLEV +-------->| str | + * +-------+ | . | + * | type | CALTYP . + * +-------+ + * | prcf ---PREVFP--+ + * +-------+ | + * | . | PREVSP | + * . | + * +-------+ | + * | <----------+ + * +-------+ + * + */ +#define PARLEV (mstack[fp].sfra) +#define CALTYP (mstack[fp-1].sfra) +#define PREVEP (mstack[fp+3].sstr) +#define PREVSP (fp-3) +#define PREVFP (mstack[fp-2].sfra) + +/* function prototypes */ + +/* eval.c */ + +_PROTOTYPE(void eval, (char *argv [], int argc, int td )); + +/* expr.c */ + +_PROTOTYPE(int expr, (char *expbuf )); +_PROTOTYPE(int query, (void)); +_PROTOTYPE(int lor, (void)); +_PROTOTYPE(int land, (void)); +_PROTOTYPE(int bor, (void)); +_PROTOTYPE(int bxor, (void)); +_PROTOTYPE(int band, (void)); +_PROTOTYPE(int eql, (void)); +_PROTOTYPE(int relat, (void)); +_PROTOTYPE(int shift, (void)); +_PROTOTYPE(int primary, (void)); +_PROTOTYPE(int term, (void)); +_PROTOTYPE(int unary, (void)); +_PROTOTYPE(int factor, (void)); +_PROTOTYPE(int constant, (void)); +_PROTOTYPE(int num, (void)); +_PROTOTYPE(int geteql, (void)); +_PROTOTYPE(int getrel, (void)); +_PROTOTYPE(int skipws, (void)); +_PROTOTYPE(int experr, (char *msg )); + +/* look.c */ + +_PROTOTYPE(int hash, (char *name )); +_PROTOTYPE(ndptr lookup, (char *name )); +_PROTOTYPE(ndptr addent, (char *name )); +_PROTOTYPE(void remhash, (char *name, int all )); +_PROTOTYPE(void freent, (ndptr p )); + +/* main.c */ + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void macro, (void)); +_PROTOTYPE(ndptr inspect, (char *tp )); +_PROTOTYPE(void initm4, (void)); +_PROTOTYPE(void initkwds, (void)); + +/* misc.c */ + +_PROTOTYPE(int indx, (char *s1, char *s2 )); +_PROTOTYPE(void putback, (int c )); +_PROTOTYPE(void pbstr, (char *s )); +_PROTOTYPE(void pbnum, (int n )); +_PROTOTYPE(void chrsave, (int c )); +_PROTOTYPE(void getdiv, (int ind )); +_PROTOTYPE(void error, (char *s )); +_PROTOTYPE(void onintr, (int s )); +_PROTOTYPE(void killdiv, (void)); +_PROTOTYPE(char *strsave, (char *s )); +_PROTOTYPE(void usage, (void)); + +/* serv.c */ + +_PROTOTYPE(void expand, (char *argv [], int argc )); +_PROTOTYPE(void dodefine, (char *name, char *defn )); +_PROTOTYPE(void dodefn, (char *name )); +_PROTOTYPE(void dopushdef, (char *name, char *defn )); +_PROTOTYPE(void dodump, (char *argv [], int argc )); +_PROTOTYPE(void doifelse, (char *argv [], int argc )); +_PROTOTYPE(int doincl, (char *ifile )); +_PROTOTYPE(int dopaste, (char *pfile )); +_PROTOTYPE(void dochq, (char *argv [], int argc )); +_PROTOTYPE(void dochc, (char *argv [], int argc )); +_PROTOTYPE(void dodiv, (int n )); +_PROTOTYPE(void doundiv, (char *argv [], int argc )); +_PROTOTYPE(void dosub, (char *argv [], int argc )); +_PROTOTYPE(void map, (char *dest, char *src, char *from, char *to )); diff --git a/commands/m4/misc.c b/commands/m4/misc.c new file mode 100755 index 000000000..a71e133a2 --- /dev/null +++ b/commands/m4/misc.c @@ -0,0 +1,292 @@ +/* + * misc.c + * Facility: m4 macro processor + * by: oz + */ + +#include "mdef.h" +#include "extr.h" + +/* + * indx - find the index of second str in the + * first str. + */ +int indx(s1, s2) +char *s1; +char *s2; +{ + register char *t; + register char *p; + register char *m; + + for (p = s1; *p; p++) { + for (t = p, m = s2; *m && *m == *t; m++, t++) + ; + if (!*m) + return(p - s1); + } + return (-1); +} + +/* + * putback - push character back onto input + * + */ +void putback (c) +char c; +{ + if (bp < endpbb) + *bp++ = c; + else + error("m4: too many characters pushed back"); +} + +/* + * pbstr - push string back onto input + * putback is replicated to improve + * performance. + * + */ +void pbstr(s) +register char *s; +{ + register char *es; + register char *zp; + + es = s; + zp = bp; + + while (*es) + es++; + es--; + while (es >= s) + if (zp < endpbb) + *zp++ = *es--; + if ((bp = zp) == endpbb) + error("m4: too many characters pushed back"); +} + +/* + * pbnum - convert number to string, push back on input. + * + */ +void pbnum (n) +int n; +{ + register int num; + + num = (n < 0) ? -n : n; + do { + putback(num % 10 + '0'); + } + while ((num /= 10) > 0); + + if (n < 0) putback('-'); +} + +/* + * chrsave - put single char on string space + * + */ +void chrsave (c) +char c; +{ +/*** if (sp < 0) + putc(c, active); + else ***/ if (ep < endest) + *ep++ = c; + else + error("m4: string space overflow"); +} + +/* + * getdiv - read in a diversion file, and + * trash it. + */ +void getdiv(ind) +int ind; +{ + register int c; + register FILE *dfil; + + if (active == outfile[ind]) + error("m4: undivert: diversion still active."); + (void) fclose(outfile[ind]); + outfile[ind] = NULL; + m4temp[UNIQUE] = ind + '0'; + if ((dfil = fopen(m4temp, "r")) == NULL) + error("m4: cannot undivert."); + else + while((c = getc(dfil)) != EOF) + putc(c, active); + (void) fclose(dfil); + +#if vms + if (remove(m4temp)) +#else + if (unlink(m4temp) == -1) +#endif + error("m4: cannot unlink."); +} + +/* + * Very fatal error. Close all files + * and die hard. + */ +void error(s) +char *s; +{ + killdiv(); + fprintf(stderr,"%s\n",s); + exit(1); +} + +/* + * Interrupt handling + */ +static char *msg = "\ninterrupted."; + +void onintr(s) +int s; /* ANSI requires the parameter */ +{ + error(msg); +} + +/* + * killdiv - get rid of the diversion files + * + */ +void killdiv() { + register int n; + + for (n = 0; n < MAXOUT; n++) + if (outfile[n] != NULL) { + (void) fclose (outfile[n]); + m4temp[UNIQUE] = n + '0'; +#if vms + (void) remove (m4temp); +#else + (void) unlink (m4temp); +#endif + } +} + +/* + * save a string somewhere.. + * + */ +char *strsave(s) +char *s; +{ + register int n; + char *p; + + n = strlen(s)+1; + p = (char *) malloc(n); + if (p != NULL) (void) memcpy(p, s, n); + return (p); +} + +void usage() { + fprintf(stderr, "Usage: m4 [-Dname[=val]] [-Uname]\n"); + exit(1); +} + +#ifdef GETOPT +/* + * H. Spencer getopt - get option letter from argv + * + * +#include <stdio.h> + * + */ + +char *optarg; /* Global argument pointer. */ +int optind = 0; /* Global argv index. */ + +static char *scan = NULL; /* Private scan pointer. */ + +int +getopt(argc, argv, optstring) +int argc; +char *argv[]; +char *optstring; +{ + register char c; + register char *place; + + optarg = NULL; + + if (scan == NULL || *scan == '\0') { + if (optind == 0) + optind++; + + if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0') + return(EOF); + if (strcmp(argv[optind], "--")==0) { + optind++; + return(EOF); + } + + scan = argv[optind]+1; + optind++; + } + + c = *scan++; + place = index(optstring, c); + + if (place == NULL || c == ':') { + fprintf(stderr, "%s: unknown option -%c\n", argv[0], c); + return('?'); + } + + place++; + if (*place == ':') { + if (*scan != '\0') { + optarg = scan; + scan = NULL; + } else { + optarg = argv[optind]; + optind++; + } + } + + return(c); +} + +#endif + +#ifdef DUFFCP +/* + * This code uses Duff's Device (tm Tom Duff) + * to unroll the copying loop: + * while (count-- > 0) + * *to++ = *from++; + */ + +#define COPYBYTE *to++ = *from++ + +void memcpy(to, from, count) +register char *from, *to; +register int count; +{ + if (count > 0) { + register int loops = (count+8-1) >> 3; /* div 8 round up */ + + switch (count&(8-1)) { /* mod 8 */ + case 0: do { + COPYBYTE; + case 7: COPYBYTE; + case 6: COPYBYTE; + case 5: COPYBYTE; + case 4: COPYBYTE; + case 3: COPYBYTE; + case 2: COPYBYTE; + case 1: COPYBYTE; + } while (--loops > 0); + } + + } +} + +#endif diff --git a/commands/m4/patchlevel.h b/commands/m4/patchlevel.h new file mode 100755 index 000000000..110c86f39 --- /dev/null +++ b/commands/m4/patchlevel.h @@ -0,0 +1 @@ +#define PATCHLEVEL 1 diff --git a/commands/m4/serv.c b/commands/m4/serv.c new file mode 100755 index 000000000..2dc6a0292 --- /dev/null +++ b/commands/m4/serv.c @@ -0,0 +1,423 @@ +/* + * serv.c + * Facility: m4 macro processor + * by: oz + */ + +#include "mdef.h" +#include "extr.h" + +char *dumpfmt = "`%s'\t`%s'\n"; /* format string for dumpdef */ + +/* + * expand - user-defined macro expansion + * + */ +void expand(argv, argc) +register char *argv[]; +register int argc; +{ + register char *t; + register char *p; + register int n; + register int argno; + + t = argv[0]; /* defn string as a whole */ + p = t; + while (*p) + p++; + p--; /* last character of defn */ + while (p > t) { + if (*(p-1) != ARGFLAG) + putback(*p); + else { + switch (*p) { + + case '#': + pbnum(argc-2); + break; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + if ((argno = *p - '0') < argc-1) + pbstr(argv[argno+1]); + break; + case '*': + for (n = argc - 1; n > 2; n--) { + pbstr(argv[n]); + putback(','); + } + pbstr(argv[2]); + break; + default : + putback(*p); + break; + } + p--; + } + p--; + } + if (p == t) /* do last character */ + putback(*p); +} + +/* + * dodefine - install definition in the table + * + */ +void dodefine(name, defn) +register char *name; +register char *defn; +{ + register ndptr p; + + if (!*name) + error("m4: null definition."); + if (strcmp(name, defn) == 0) + error("m4: recursive definition."); + if ((p = lookup(name)) == nil) + p = addent(name); + else if (p->defn != null) + free(p->defn); + if (!*defn) + p->defn = null; + else + p->defn = strsave(defn); + p->type = MACRTYPE; +} + +/* + * dodefn - push back a quoted definition of + * the given name. + */ + +void dodefn(name) +char *name; +{ + register ndptr p; + + if ((p = lookup(name)) != nil && p->defn != null) { + putback(rquote); + pbstr(p->defn); + putback(lquote); + } +} + +/* + * dopushdef - install a definition in the hash table + * without removing a previous definition. Since + * each new entry is entered in *front* of the + * hash bucket, it hides a previous definition from + * lookup. + */ +void dopushdef(name, defn) +register char *name; +register char *defn; +{ + register ndptr p; + + if (!*name) + error("m4: null definition"); + if (strcmp(name, defn) == 0) + error("m4: recursive definition."); + p = addent(name); + if (!*defn) + p->defn = null; + else + p->defn = strsave(defn); + p->type = MACRTYPE; +} + +/* + * dodumpdef - dump the specified definitions in the hash + * table to stderr. If nothing is specified, the entire + * hash table is dumped. + * + */ +void dodump(argv, argc) +register char *argv[]; +register int argc; +{ + register int n; + ndptr p; + + if (argc > 2) { + for (n = 2; n < argc; n++) + if ((p = lookup(argv[n])) != nil) + fprintf(stderr, dumpfmt, p->name, + p->defn); + } + else { + for (n = 0; n < HASHSIZE; n++) + for (p = hashtab[n]; p != nil; p = p->nxtptr) + fprintf(stderr, dumpfmt, p->name, + p->defn); + } +} + +/* + * doifelse - select one of two alternatives - loop. + * + */ +void doifelse(argv,argc) +register char *argv[]; +register int argc; +{ + cycle { + if (strcmp(argv[2], argv[3]) == 0) + pbstr(argv[4]); + else if (argc == 6) + pbstr(argv[5]); + else if (argc > 6) { + argv += 3; + argc -= 3; + continue; + } + break; + } +} + +/* + * doinclude - include a given file. + * + */ +int doincl(ifile) +char *ifile; +{ + if (ilevel+1 == MAXINP) + error("m4: too many include files."); + if ((infile[ilevel+1] = fopen(ifile, "r")) != NULL) { + ilevel++; + return (1); + } + else + return (0); +} + +#ifdef EXTENDED +/* + * dopaste - include a given file without any + * macro processing. + */ +int dopaste(pfile) +char *pfile; +{ + FILE *pf; + register int c; + + if ((pf = fopen(pfile, "r")) != NULL) { + while((c = getc(pf)) != EOF) + putc(c, active); + (void) fclose(pf); + return(1); + } + else + return(0); +} +#endif + +/* + * dochq - change quote characters + * + */ +void dochq(argv, argc) +register char *argv[]; +register int argc; +{ + if (argc > 2) { + if (*argv[2]) + lquote = *argv[2]; + if (argc > 3) { + if (*argv[3]) + rquote = *argv[3]; + } + else + rquote = lquote; + } + else { + lquote = LQUOTE; + rquote = RQUOTE; + } +} + +/* + * dochc - change comment characters + * + */ +void dochc(argv, argc) +register char *argv[]; +register int argc; +{ + if (argc > 2) { + if (*argv[2]) + scommt = *argv[2]; + if (argc > 3) { + if (*argv[3]) + ecommt = *argv[3]; + } + else + ecommt = ECOMMT; + } + else { + scommt = SCOMMT; + ecommt = ECOMMT; + } +} + +/* + * dodivert - divert the output to a temporary file + * + */ +void dodiv(n) +register int n; +{ + if (n < 0 || n >= MAXOUT) + n = 0; /* bitbucket */ + if (outfile[n] == NULL) { + m4temp[UNIQUE] = n + '0'; + if ((outfile[n] = fopen(m4temp, "w")) == NULL) + error("m4: cannot divert."); + } + oindex = n; + active = outfile[n]; +} + +/* + * doundivert - undivert a specified output, or all + * other outputs, in numerical order. + */ +void doundiv(argv, argc) +register char *argv[]; +register int argc; +{ + register int ind; + register int n; + + if (argc > 2) { + for (ind = 2; ind < argc; ind++) { + n = atoi(argv[ind]); + if (n > 0 && n < MAXOUT && outfile[n] != NULL) + getdiv(n); + + } + } + else + for (n = 1; n < MAXOUT; n++) + if (outfile[n] != NULL) + getdiv(n); +} + +/* + * dosub - select substring + * + */ +void dosub (argv, argc) +register char *argv[]; +register int argc; +{ + register char *ap, *fc, *k; + register int nc; + + if (argc < 5) + nc = MAXTOK; + else +#ifdef EXPR + nc = expr(argv[4]); +#else + nc = atoi(argv[4]); +#endif + ap = argv[2]; /* target string */ +#ifdef EXPR + fc = ap + expr(argv[3]); /* first char */ +#else + fc = ap + atoi(argv[3]); /* first char */ +#endif + if (fc >= ap && fc < ap+strlen(ap)) + for (k = fc+min(nc,strlen(fc))-1; k >= fc; k--) + putback(*k); +} + +/* + * map: + * map every character of s1 that is specified in from + * into s3 and replace in s. (source s1 remains untouched) + * + * This is a standard implementation of map(s,from,to) function of ICON + * language. Within mapvec, we replace every character of "from" with + * the corresponding character in "to". If "to" is shorter than "from", + * than the corresponding entries are null, which means that those + * characters dissapear altogether. Furthermore, imagine + * map(dest, "sourcestring", "srtin", "rn..*") type call. In this case, + * `s' maps to `r', `r' maps to `n' and `n' maps to `*'. Thus, `s' + * ultimately maps to `*'. In order to achieve this effect in an efficient + * manner (i.e. without multiple passes over the destination string), we + * loop over mapvec, starting with the initial source character. if the + * character value (dch) in this location is different than the source + * character (sch), sch becomes dch, once again to index into mapvec, until + * the character value stabilizes (i.e. sch = dch, in other words + * mapvec[n] == n). Even if the entry in the mapvec is null for an ordinary + * character, it will stabilize, since mapvec[0] == 0 at all times. At the + * end, we restore mapvec* back to normal where mapvec[n] == n for + * 0 <= n <= 127. This strategy, along with the restoration of mapvec, is + * about 5 times faster than any algorithm that makes multiple passes over + * destination string. + * + */ + +void map(dest,src,from,to) +register char *dest; +register char *src; +register char *from; +register char *to; +{ + register char *tmp; + register char sch, dch; + static char mapvec[128] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, + 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, + 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, + 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, + 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, + 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, + 120, 121, 122, 123, 124, 125, 126, 127 + }; + + if (*src) { + tmp = from; + /* + * create a mapping between "from" and "to" + */ + while (*from) + mapvec[*from++] = (*to) ? *to++ : (char) 0; + + while (*src) { + sch = *src++; + dch = mapvec[sch]; + while (dch != sch) { + sch = dch; + dch = mapvec[sch]; + } + if (*dest = dch) + dest++; + } + /* + * restore all the changed characters + */ + while (*tmp) { + mapvec[*tmp] = *tmp; + tmp++; + } + } + *dest = (char) 0; +} diff --git a/commands/make/Makefile b/commands/make/Makefile new file mode 100755 index 000000000..7b307be37 --- /dev/null +++ b/commands/make/Makefile @@ -0,0 +1,21 @@ +# Makefile for make (!) + +CFLAGS = -O -Dunix -D_MINIX -D_POSIX_SOURCE + +OBJ = check.o input.o macro.o main.o make.o reader.o rules.o archive.o + +all: make + +make : $(OBJ) + $(CC) -i -o make $(OBJ) + install -S 96k make + +install: /usr/bin/make + +/usr/bin/make: make + install -cs -o bin make $@ + +$(OBJ): h.h + +clean: + rm -f *.o *.bak core make diff --git a/commands/make/archive.c b/commands/make/archive.c new file mode 100755 index 000000000..530cb651c --- /dev/null +++ b/commands/make/archive.c @@ -0,0 +1,309 @@ +/* archive.c - archive support Author: Kees J. Bot + * 13 Nov 1993 + */ +#include "h.h" + +#ifdef unix + +#include <unistd.h> +#include <fcntl.h> + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +/* ASCII ar header. */ + +#define ASCII_ARMAG "!<arch>\n" +#define ASCII_SARMAG 8 +#define ASCII_ARFMAG "`\n" + +struct ascii_ar_hdr { + char ar_name[16]; + char ar_date[12]; + char ar_uid[6]; + char ar_gid[6]; + char ar_mode[8]; + char ar_size[10]; + char ar_fmag[2]; +}; + +/* ACK ar header. */ + +#define ACK_ARMAG 0177545 +#define ACK_AALMAG 0177454 + +struct ack_ar_hdr { + char ar_name[14]; + unsigned long ar_date; + unsigned char ar_uid; + unsigned char ar_gid; + unsigned short ar_mode; + unsigned long ar_size; +}; + +typedef struct archname { + struct archname *next; /* Next on the hash chain. */ + char name[16]; /* One archive entry. */ + time_t date; /* The timestamp. */ + /* (no need for other attibutes) */ +} archname_t; + +static size_t namelen; /* Max name length, 14 or 16. */ + +#define HASHSIZE (64 << sizeof(int)) + +static archname_t *nametab[HASHSIZE]; + +_PROTOTYPE( static int hash, (char *name) ); +_PROTOTYPE( static int searchtab, (char *name, time_t *date, int scan) ); +_PROTOTYPE( static void deltab, (void) ); +_PROTOTYPE( static long ar_atol, (char *s, size_t n) ); +_PROTOTYPE( static int read_ascii_archive, (int afd) ); +_PROTOTYPE( static int read_ack_archive, (int afd) ); + +static char *lpar, *rpar; /* Leave these at '(' and ')'. */ + +int is_archive_ref(name) char *name; +/* True if name is of the form "archive(file)". */ +{ + char *p = name; + + while (*p != 0 && *p != '(' && *p != ')') p++; + lpar = p; + if (*p++ != '(') return 0; + + while (*p != 0 && *p != '(' && *p != ')') p++; + rpar = p; + if (*p++ != ')') return 0; + + return *p == 0; +} + +static int hash(name) char *name; +/* Compute a hash value out of a name. */ +{ + unsigned h = 0; + unsigned char *p = (unsigned char *) name; + int n = namelen; + + while (*p != 0) { + h = h * 0x1111 + *p++; + if (--n == 0) break; + } + + return h % arraysize(nametab); +} + +static int searchtab(name, date, scan) char *name; time_t *date; int scan; +/* Enter a name to the table, or return the date of one already there. */ +{ + archname_t **pnp, *np; + int cmp = 1; + + pnp = &nametab[hash(name)]; + + while ((np = *pnp) != NULL + && (cmp = strncmp(name, np->name, namelen)) > 0) { + pnp= &np->next; + } + + if (cmp != 0) { + if (scan) { + errno = ENOENT; + return -1; + } + if ((np = (archname_t *) malloc(sizeof(*np))) == NULL) + fatal("No memory for archive name cache",(char *)0,0); + strncpy(np->name, name, namelen); + np->date = *date; + np->next = *pnp; + *pnp = np; + } + if (scan) *date = np->date; + return 0; +} + +static void deltab() +/* Delete the name cache, a different library is to be read. */ +{ + archname_t **pnp, *np, *junk; + + for (pnp = nametab; pnp < arraylimit(nametab); pnp++) { + for (np = *pnp; np != NULL; ) { + junk = np; + np = np->next; + free(junk); + } + *pnp = NULL; + } +} + +static long ar_atol(s, n) char *s; size_t n; +/* Transform a string into a number. Ignore the space padding. */ +{ + long l= 0; + + while (n > 0) { + if (*s != ' ') l= l * 10 + (*s - '0'); + s++; + n--; + } + return l; +} + +static int read_ascii_archive(afd) +int afd; +/* Read a modern ASCII type archive. */ +{ + struct ascii_ar_hdr hdr; + off_t pos= 8; + char *p; + time_t date; + + namelen = 16; + + for (;;) { + if (lseek(afd, pos, SEEK_SET) == -1) return -1; + + switch (read(afd, &hdr, sizeof(hdr))) { + case sizeof(hdr): + break; + case -1: + return -1; + default: + return 0; + } + + if (strncmp(hdr.ar_fmag, ASCII_ARFMAG, sizeof(hdr.ar_fmag)) != 0) { + errno= EINVAL; + return -1; + } + + /* Strings are space padded! */ + for (p= hdr.ar_name; p < hdr.ar_name + sizeof(hdr.ar_name); p++) { + if (*p == ' ') { + *p= 0; + break; + } + } + + /* Add a file to the cache. */ + date = ar_atol(hdr.ar_date, sizeof(hdr.ar_date)); + searchtab(hdr.ar_name, &date, 0); + + pos+= sizeof(hdr) + ar_atol(hdr.ar_size, sizeof(hdr.ar_size)); + pos= (pos + 1) & (~ (off_t) 1); + } +} + +static int read_ack_archive(afd) +int afd; +/* Read an ACK type archive. */ +{ + unsigned char raw_hdr[14 + 4 + 1 + 1 + 2 + 4]; + struct ack_ar_hdr hdr; + off_t pos= 2; + time_t date; + + namelen = 14; + + for (;;) { + if (lseek(afd, pos, SEEK_SET) == -1) return -1; + + switch (read(afd, raw_hdr, sizeof(raw_hdr))) { + case sizeof(raw_hdr): + break; + case -1: + return -1; + default: + return 0; + } + + /* Copy the useful fields from the raw bytes transforming PDP-11 + * style numbers to native format. + */ + memcpy(hdr.ar_name, raw_hdr + 0, 14); + hdr.ar_date= (long) raw_hdr[14 + 1] << 24 + | (long) raw_hdr[14 + 0] << 16 + | (long) raw_hdr[14 + 3] << 8 + | (long) raw_hdr[14 + 2] << 0; + hdr.ar_size= (long) raw_hdr[22 + 1] << 24 + | (long) raw_hdr[22 + 0] << 16 + | (long) raw_hdr[22 + 3] << 8 + | (long) raw_hdr[22 + 2] << 0; + + /* Add a file to the cache. */ + date = hdr.ar_date; + searchtab(hdr.ar_name, &date, 0); + + pos= (pos + 26 + hdr.ar_size + 1) & (~ (off_t) 1); + } +} + +int archive_stat(name, stp) char *name; struct stat *stp; +/* Search an archive for a file and return that file's stat info. */ +{ + int afd; + int r= -1; + char magic[8]; + char *file; + static dev_t ardev; + static ino_t arino = 0; + static time_t armtime; + + if (!is_archive_ref(name)) { errno = EINVAL; return -1; } + *lpar= 0; + *rpar= 0; + file= lpar + 1; + + if (stat(name, stp) < 0) goto bail_out; + + if (stp->st_ino != arino || stp->st_dev != ardev) { + /* Either the first (and probably only) library, or a different + * library. + */ + arino = stp->st_ino; + ardev = stp->st_dev; + armtime = stp->st_mtime; + deltab(); + + if ((afd= open(name, O_RDONLY)) < 0) goto bail_out; + + switch (read(afd, magic, sizeof(magic))) { + case 8: + if (strncmp(magic, ASCII_ARMAG, 8) == 0) { + r= read_ascii_archive(afd); + break; + } + if ((magic[0] & 0xFF) == ((ACK_AALMAG >> 0) & 0xFF) + && (magic[1] & 0xFF) == ((ACK_AALMAG >> 8) & 0xFF) + ) { + r= read_ack_archive(afd); + break; + } + /*FALL THROUGH*/ + default: + errno = EINVAL; + /*FALL THROUGH*/ + case -1: + /* r= -1 */; + } + { int e= errno; close(afd); errno= e; } + } else { + /* Library is cached. */ + r = 0; + } + + if (r == 0) { + /* Search the cache. */ + r = searchtab(file, &stp->st_mtime, 1); + if (stp->st_mtime > armtime) stp->st_mtime = armtime; + } + +bail_out: + /* Repair the name(file) thing. */ + *lpar= '('; + *rpar= ')'; + return r; +} +#endif diff --git a/commands/make/check.c b/commands/make/check.c new file mode 100755 index 000000000..1ac62c990 --- /dev/null +++ b/commands/make/check.c @@ -0,0 +1,128 @@ +/************************************************************************* + * + * m a k e : c h e c k . c + * + * debugging stuff: Check structures for make. + *======================================================================== + * Edition history + * + * # Date Comments By + * --- -------- ---------------------------------------------------- --- + * 1 ?? ?? + * 2 23.08.89 adapted to new name tree structure RAL + * 3 30.08.89 indention changed PSH,RAL + * 4 06.09.89 prt output redirected to stdout RAL + * ------------ Version 2.0 released ------------------------------- RAL + * + *************************************************************************/ + +#include "h.h" + + +/* + * Prints out the structures as defined in memory. Good for check + * that you make file does what you want (and for debugging make). + */ +void prt() +{ + register struct name *np; + register struct depend *dp; + register struct line *lp; + register struct cmd *cp; + register struct macro *mp; + + register int i; + + for (mp = macrohead; mp; mp = mp->m_next) + printf("%s = %s\n", mp->m_name, mp->m_val); + + putchar('\n'); + + for (i = 0; i <= maxsuffarray ; i++) + for (np = suffparray[i]->n_next; np; np = np->n_next) + { + if (np->n_flag & N_DOUBLE) + printf("%s::\n", np->n_name); + else + printf("%s:\n", np->n_name); + if (np == firstname) + printf("(MAIN NAME)\n"); + for (lp = np->n_line; lp; lp = lp->l_next) + { + putchar(':'); + for (dp = lp->l_dep; dp; dp = dp->d_next) + printf(" %s", dp->d_name->n_name); + putchar('\n'); + + for (cp = lp->l_cmd; cp; cp = cp->c_next) +#ifdef os9 + printf("- %s\n", cp->c_cmd); +#else + printf("-\t%s\n", cp->c_cmd); +#endif + putchar('\n'); + } + putchar('\n'); + } +} + + +/* + * Recursive routine that does the actual checking. + */ +void check(np) +struct name *np; +{ + register struct depend *dp; + register struct line *lp; + + + if (np->n_flag & N_MARK) + fatal("Circular dependency from %s", np->n_name,0); + + np->n_flag |= N_MARK; + + for (lp = np->n_line; lp; lp = lp->l_next) + for (dp = lp->l_dep; dp; dp = dp->d_next) + check(dp->d_name); + + np->n_flag &= ~N_MARK; +} + + +/* + * Look for circular dependancies. + * ie. + * a: b + * b: a + * is a circular dep + */ +void circh() +{ + register struct name *np; + register int i; + + + for (i = 0; i <= maxsuffarray ; i++) + for (np = suffparray[i]->n_next; np; np = np->n_next) + check(np); +} + + +/* + * Check the target .PRECIOUS, and mark its dependentd as precious + */ +void precious() +{ + register struct depend *dp; + register struct line *lp; + register struct name *np; + + + if (!((np = newname(".PRECIOUS"))->n_flag & N_TARG)) + return; + + for (lp = np->n_line; lp; lp = lp->l_next) + for (dp = lp->l_dep; dp; dp = dp->d_next) + dp->d_name->n_flag |= N_PREC; +} diff --git a/commands/make/h.h b/commands/make/h.h new file mode 100755 index 000000000..a009f3a2c --- /dev/null +++ b/commands/make/h.h @@ -0,0 +1,319 @@ +/************************************************************************* + * + * m a k e : h . h + * + * include file for make + *======================================================================== + * Edition history + * + * # Date Comments By + * --- -------- ---------------------------------------------------- --- + * 1 ?? ?? + * 2 23.08.89 LZ increased,N_EXISTS added,suffix as macro added RAL + * 3 30.08.89 macro flags added, indention changed PSH,RAL + * 4 03.09.89 fixed LZ eliminated, struct str added,... RAL + * 5 06.09.89 TABCHAR,M_MAKE added RAL + * 6 09.09.89 tos support added, EXTERN,INIT,PARMS added PHH,RAL + * 7 17.09.89 __STDC__ added, make1 decl. fixed , N_EXEC added RAL + * ------------ Version 2.0 released ------------------------------- RAL + * + *************************************************************************/ + +#ifdef unix +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <utime.h> +#include <stdio.h> +#include <limits.h> +#endif + +#ifdef eon +#include <sys/stat.h> +#include <sys/err.h> +#endif + +#ifdef os9 +#include <time.h> +#include <os9.h> +#include <modes.h> +#include <direct.h> +#include <errno.h> +#endif + +#ifdef tos +struct DOSTIME {short time,date; }; /* time structure of TOS */ + +#ifdef LATTICE +#include <error.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <osbind.h> +#endif /* LATTICE */ + +#ifdef TURBO +#include <tos.h> +#include <errno.h> +#include <string.h> +#endif /* TURBO */ + +#endif /* tos */ + +#include <ctype.h> +#include <stdio.h> +#include <assert.h> + +#ifdef eon +#define MNOENT ER_NOTF +#else +#define MNOENT ENOENT +#endif + +#ifndef uchar +#ifdef os9 +#define uchar char +#define void int +#define fputc putc +#else +#define uchar unsigned char +#endif +#endif + +#define bool uchar +#ifndef time_t +#define time_t long +#endif +#define TRUE (1) +#define FALSE (0) +#define max(a,b) ((a)>(b)?(a):(b)) + +#ifdef unix +#define DEFN1 "makefile" +#define DEFN2 "Makefile" +#endif +#ifdef eon +#define DEFN1 "makefile" +#define DEFN2 "Makefile" +#endif +#ifdef tos +#define DEFN1 "makefile." +#define DEFN2 (char *)0 +#endif +#ifdef os9 +#define DEFN1 "makefile" +#define DEFN2 (char *)0 +#endif + + +#ifdef os9 +#define TABCHAR ' ' +#else +#define TABCHAR '\t' +#endif + +#define LZ1 (2048) /* Initial input/expand string size */ +#define LZ2 (256) /* Initial input/expand string size */ + + + +/* + * A name. This represents a file, either to be made, or existant + */ + +struct name +{ + struct name *n_next; /* Next in the list of names */ + char *n_name; /* Called */ + struct line *n_line; /* Dependencies */ + time_t n_time; /* Modify time of this name */ + uchar n_flag; /* Info about the name */ +}; + +#define N_MARK 0x01 /* For cycle check */ +#define N_DONE 0x02 /* Name looked at */ +#define N_TARG 0x04 /* Name is a target */ +#define N_PREC 0x08 /* Target is precious */ +#define N_DOUBLE 0x10 /* Double colon target */ +#define N_EXISTS 0x20 /* File exists */ +#define N_ERROR 0x40 /* Error occured */ +#define N_EXEC 0x80 /* Commands executed */ + +/* + * Definition of a target line. + */ +struct line +{ + struct line *l_next; /* Next line (for ::) */ + struct depend *l_dep; /* Dependents for this line */ + struct cmd *l_cmd; /* Commands for this line */ +}; + + +/* + * List of dependents for a line + */ +struct depend +{ + struct depend *d_next; /* Next dependent */ + struct name *d_name; /* Name of dependent */ +}; + + +/* + * Commands for a line + */ +struct cmd +{ + struct cmd *c_next; /* Next command line */ + char *c_cmd; /* Command line */ +}; + + +/* + * Macro storage + */ +struct macro +{ + struct macro *m_next; /* Next variable */ + char *m_name; /* Called ... */ + char *m_val; /* Its value */ + uchar m_flag; /* Infinite loop check */ +}; + + +#define M_MARK 0x01 /* for infinite loop check */ +#define M_OVERRIDE 0x02 /* command-line override */ +#define M_MAKE 0x04 /* for MAKE macro */ + +/* + * String + */ +struct str +{ + char **ptr; /* ptr to real ptr. to string */ + int len; /* length of string */ + int pos; /* position */ +}; + + +/* Declaration, definition & initialization of variables */ + +#ifndef EXTERN +#define EXTERN extern +#endif + +#ifndef INIT +#define INIT(x) +#endif + +extern int errno; +extern char **environ; + +EXTERN char *myname; +EXTERN bool domake INIT(TRUE); /* Go through the motions option */ +EXTERN bool ignore INIT(FALSE); /* Ignore exit status option */ +EXTERN bool conterr INIT(FALSE); /* continue on errors */ +EXTERN bool silent INIT(FALSE); /* Silent option */ +EXTERN bool print INIT(FALSE); /* Print debuging information */ +EXTERN bool rules INIT(TRUE); /* Use inbuilt rules */ +EXTERN bool dotouch INIT(FALSE); /* Touch files instead of making */ +EXTERN bool quest INIT(FALSE); /* Question up-to-dateness of file */ +EXTERN bool useenv INIT(FALSE); /* Env. macro def. overwrite makefile def.*/ +EXTERN bool dbginfo INIT(FALSE); /* Print lot of debugging information */ +EXTERN bool ambigmac INIT(TRUE); /* guess undef. ambiguous macros (*,<) */ +EXTERN struct name *firstname; +EXTERN char *str1; +EXTERN char *str2; +EXTERN struct str str1s; +EXTERN struct str str2s; +EXTERN struct name **suffparray; /* ptr. to array of ptrs. to name chains */ +EXTERN int sizesuffarray INIT(20); /* size of suffarray */ +EXTERN int maxsuffarray INIT(0); /* last used entry in suffarray */ +EXTERN struct macro *macrohead; +EXTERN bool expmake; /* TRUE if $(MAKE) has been expanded */ +EXTERN char *makefile; /* The make file */ +EXTERN int lineno; + +#ifdef tos +#ifdef LATTICE +EXTERN int _mneed INIT(60000); /* VERY important for TOS with LATTICE C*/ +#endif /* LATTICE */ +#endif /* tos */ +#ifdef eon +#define MEMSPACE (16384) +EXTERN unsigned memspace = MEMSPACE; +#endif + +#define suffix(name) strrchr(name,(int)'.') + +EXTERN int _ctypech; +#define mylower(x) (islower(_ctypech=(x)) ? _ctypech :tolower(_ctypech)) +#define myupper(x) (isupper(_ctypech=(x)) ? _ctypech :toupper(_ctypech)) + +/* Prototypes. */ +struct sgtbuf; + +/* check.c */ +_PROTOTYPE(void prt, (void)); +_PROTOTYPE(void check, (struct name *np )); +_PROTOTYPE(void circh, (void)); +_PROTOTYPE(void precious, (void)); + +/* input.c */ +_PROTOTYPE(void init, (void)); +_PROTOTYPE(void strrealloc, (struct str *strs )); +_PROTOTYPE(struct name *newname, (char *name )); +_PROTOTYPE(struct name *testname, (char *name )); +_PROTOTYPE(struct depend *newdep, (struct name *np, struct depend *dp )); +_PROTOTYPE(struct cmd *newcmd, (char *str, struct cmd *cp )); +_PROTOTYPE(void newline, (struct name *np, struct depend *dp, struct cmd *cp, + int flag )); +_PROTOTYPE(void input, (FILE *fd )); + +/* macro.c */ +_PROTOTYPE(struct macro *getmp, (char *name )); +_PROTOTYPE(char *getmacro, (char *name )); +_PROTOTYPE(struct macro *setmacro, (char *name, char *val )); +_PROTOTYPE(void setDFmacro, (char *name, char *val )); +_PROTOTYPE(void doexp, (struct str *to, char *from )); +_PROTOTYPE(void expand, (struct str *strs )); + +/* main.c */ +_PROTOTYPE(void main, (int argc, char **argv )); +_PROTOTYPE(void setoption, (char option )); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(void fatal, (char *msg, char *a1, int a2 )); + +/* make.c */ +_PROTOTYPE(int dosh, (char *string, char *shell )); +_PROTOTYPE(int makeold, (char *name )); +_PROTOTYPE(void docmds1, (struct name *np, struct line *lp )); +_PROTOTYPE(void docmds, (struct name *np )); +_PROTOTYPE(int Tosexec, (char *string )); +_PROTOTYPE(time_t mstonix, (unsigned int date, unsigned int time )); +_PROTOTYPE(void getmdate, (int fd, struct sgtbuf *tbp )); +_PROTOTYPE(time_t cnvtime, (struct sgtbuf *tbp )); +_PROTOTYPE(void modtime, (struct name *np )); +_PROTOTYPE(void touch, (struct name *np )); +_PROTOTYPE(int make, (struct name *np, int level )); +_PROTOTYPE(void make1, (struct name *np, struct line *lp, struct depend *qdp, + char *basename, char *inputname )); +_PROTOTYPE(void implmacros, (struct name *np, struct line *lp, + char **pbasename, char **pinputname )); +_PROTOTYPE(void dbgprint, (int level, struct name *np, char *comment )); + +/* reader.c */ +_PROTOTYPE(void error, (char *msg, char *a1 )); +_PROTOTYPE(bool getline, (struct str *strs, FILE *fd )); +_PROTOTYPE(char *gettok, (char **ptr )); + +/* rules.c */ +_PROTOTYPE(bool dyndep, (struct name *np, char **pbasename,char **pinputname)); +_PROTOTYPE(void makerules, (void)); + +/* archive.c */ +_PROTOTYPE(int is_archive_ref, (char *name)); +_PROTOTYPE(int archive_stat, (char *name, struct stat *stp)); diff --git a/commands/make/input.c b/commands/make/input.c new file mode 100755 index 000000000..9eb2a8298 --- /dev/null +++ b/commands/make/input.c @@ -0,0 +1,439 @@ +/************************************************************************* + * + * m a k e : i n p u t . c + * + * Parse a makefile + *======================================================================== + * Edition history + * + * # Date Comments By + * --- -------- ---------------------------------------------------- --- + * 1 ?? ?? + * 2 23.08.89 new name tree structure introduced to speed up make, + * testname introduced to shrink the memory usage RAL + * 3 30.08.89 indention changed PSH,RAL + * 4 03.09.89 fixed LZ eliminated RAL + * 5 06.09.89 ; command added RAL + * ------------ Version 2.0 released ------------------------------- RAL + * + *************************************************************************/ + + +#include "h.h" + + +static struct name *lastrrp; +static struct name *freerp = (struct name *)NULL; + +void init() +{ + if( (suffparray = (struct name **) malloc( sizesuffarray * + sizeof(struct name *))) == (struct name **) NULL) + fatal("No memory for suffarray",(char *)0,0); + if ((*suffparray = (struct name *)malloc(sizeof (struct name))) + == (struct name *)0) + fatal("No memory for name",(char *)0,0); + (*suffparray)->n_next = (struct name *)0; + + if ((str1 = (char *) malloc(LZ1)) == ((char *)0)) + fatal("No memory for str1",(char *)0,0); + str1s.ptr = &str1; + str1s.len = LZ1; + if ((str2 = (char *) malloc(LZ2)) == (char *)0) + fatal("No memory for str2",(char *)0,0); + str2s.ptr = &str2; + str2s.len = LZ2; +} + +void strrealloc(strs) +struct str *strs; +{ + strs->len *= 2; + *strs->ptr = (char *) realloc(*strs->ptr, strs->len + 16); + if(*strs->ptr == (char *) NULL) + fatal("No memory for string reallocation",(char *)0,0); +} + +/* + * Intern a name. Return a pointer to the name struct + */ +struct name *newname(name) +char *name; +{ + register struct name *rp; + register struct name *rrp; + register char *cp; + + register int i; + register char *suff; /* ptr. to suffix in current name */ + register struct name **sp; /* ptr. to ptr. to chain of names */ + + if ( (suff = suffix(name)) != (char *)NULL) { + for (i = 1, sp = suffparray, sp++; + i <= maxsuffarray && strcmp(suff, (*sp)->n_name) != 0; + sp++,i++); + if (i > maxsuffarray) { + if ( i >= sizesuffarray) { /* must realloc suffarray */ + sizesuffarray *= 2; + if( (suffparray = (struct name **) realloc((char *) suffparray, + sizesuffarray * sizeof(struct name *))) == (struct name **) NULL) + fatal("No memory for suffarray",(char *)0,0); + } + maxsuffarray++; + sp = &suffparray[i]; + if ((*sp = (struct name *)malloc(sizeof (struct name))) + == (struct name *)0) + fatal("No memory for name",(char *)0,0); + (*sp)->n_next = (struct name *)0; + if ((cp = (char *) malloc(strlen(suff)+1)) == (char *)0) + fatal("No memory for name",(char *)0,0); + strcpy(cp, suff); + (*sp)->n_name = cp; + } + } + else + sp = suffparray; + + for ( rp = (*sp)->n_next, rrp = *sp; rp; rp = rp->n_next, rrp = rrp->n_next ) + if (strcmp(name, rp->n_name) == 0) return rp; + + if ( freerp == (struct name *)NULL) { + if ((rp = (struct name *)malloc(sizeof (struct name))) == (struct name *)0) + fatal("No memory for name",(char *)0,0); + } + else { + rp = freerp; + freerp = (struct name *)NULL; + } + rrp->n_next = rp; + rp->n_next = (struct name *)0; + if ((cp = (char *) malloc(strlen(name)+1)) == (char *)0) + fatal("No memory for name",(char *)0,0); + strcpy(cp, name); + rp->n_name = cp; + rp->n_line = (struct line *)0; + rp->n_time = (time_t)0; + rp->n_flag = 0; + lastrrp = rrp; + + return rp; +} + +/* + * Test a name. + * If the name already exists return the ptr. to its name structure. + * Else if the file exists 'intern' the name and return the ptr. + * Otherwise don't waste memory and return a NULL pointer + */ +struct name *testname(name) +char *name; +{ + register struct name *rp; + + lastrrp = (struct name *)NULL; + rp = newname( name); + if (rp->n_line || rp->n_flag & N_EXISTS) + return(rp); + modtime(rp); + if (rp->n_flag & N_EXISTS) + return(rp); + if (lastrrp != (struct name *)NULL) { + free (rp->n_name); + lastrrp->n_next = (struct name *)NULL; + freerp = rp; + } + return((struct name *)NULL); +} + + + +/* + * Add a dependant to the end of the supplied list of dependants. + * Return the new head pointer for that list. + */ +struct depend *newdep(np, dp) +struct name *np; +struct depend *dp; +{ + register struct depend *rp; + register struct depend *rrp; + + + if ((rp = (struct depend *)malloc(sizeof (struct depend))) + == (struct depend *)0) + fatal("No memory for dependant",(char *)0,0); + rp->d_next = (struct depend *)0; + rp->d_name = np; + + if (dp == (struct depend *)0) return rp; + + for (rrp = dp; rrp->d_next; rrp = rrp->d_next) ; + + rrp->d_next = rp; + + return dp; +} + + +/* + * Add a command to the end of the supplied list of commands. + * Return the new head pointer for that list. + */ +struct cmd *newcmd(str, cp) +char *str; +struct cmd *cp; +{ + register struct cmd *rp; + register struct cmd *rrp; + register char *rcp; + + + if (rcp = strrchr(str, '\n')) *rcp = '\0'; /* Loose newline */ + + while (isspace(*str)) str++; + + if (*str == '\0') return cp; /* If nothing left, the exit */ + + if ((rp = (struct cmd *)malloc(sizeof (struct cmd))) == (struct cmd *)0) + fatal("No memory for command",(char *)0,0); + rp->c_next = (struct cmd *)0; + if ((rcp = (char *) malloc(strlen(str)+1)) == (char *)0) + fatal("No memory for command",(char *)0,0); + strcpy(rcp, str); + rp->c_cmd = rcp; + + if (cp == (struct cmd *)0) return rp; + + for (rrp = cp; rrp->c_next; rrp = rrp->c_next) ; + + rrp->c_next = rp; + + return cp; +} + + +/* + * Add a new 'line' of stuff to a target. This check to see + * if commands already exist for the target. If flag is set, + * the line is a double colon target. + * + * Kludges: + * i) If the new name begins with a '.', and there are no dependents, + * then the target must cease to be a target. This is for .SUFFIXES. + * ii) If the new name begins with a '.', with no dependents and has + * commands, then replace the current commands. This is for + * redefining commands for a default rule. + * Neither of these free the space used by dependents or commands, + * since they could be used by another target. + */ + +void newline(np, dp, cp, flag) +struct name *np; +struct depend *dp; +struct cmd *cp; +int flag; +{ + bool hascmds = FALSE; /* Target has commands */ + register struct line *rp; + register struct line *rrp; + + + /* Handle the .SUFFIXES case */ + if (np->n_name[0] == '.' && !dp && !cp) { + for (rp = np->n_line; rp; rp = rrp) { + rrp = rp->l_next; + free(rp); + } + np->n_line = (struct line *)0; + np->n_flag &= ~N_TARG; + return; + } + + /* This loop must happen since rrp is used later. */ + for ( rp = np->n_line, rrp = (struct line *)0; rp; rrp = rp, rp = rp->l_next) + if (rp->l_cmd) hascmds = TRUE; + + if (hascmds && cp && !(np->n_flag & N_DOUBLE)) + /* Handle the implicit rules redefinition case */ + if (np->n_name[0] == '.' && dp == (struct depend *)0) { + np->n_line->l_cmd = cp; + return; + } + else + error("Commands defined twice for target %s", np->n_name); + if (np->n_flag & N_TARG) + if (!(np->n_flag & N_DOUBLE) != !flag) /* like xor */ + error("Inconsistent rules for target %s", np->n_name); + + if ((rp = (struct line *)malloc(sizeof (struct line))) == (struct line *)0) + fatal("No memory for line",(char *)0,0); + rp->l_next = (struct line *)0; + rp->l_dep = dp; + rp->l_cmd = cp; + + if (rrp) + rrp->l_next = rp; + else + np->n_line = rp; + + np->n_flag |= N_TARG; + if (flag) np->n_flag |= N_DOUBLE; +} + + +/* + * Parse input from the makefile, and construct a tree structure + * of it. + */ +void input(fd) +FILE *fd; +{ + char *p; /* General */ + char *q; + register char *a; + struct name *np; + struct depend *dp; + struct cmd *cp; + bool dbl; + + + if (getline(&str1s, fd)) return; /* Read the first line */ + + for(;;) { + if (*str1 == TABCHAR) /* Rules without targets */ + error("Rules not allowed here",(char *)0); + + p = str1; + + while (isspace(*p)) p++; /* Find first target */ + + + while (((q = strchr(p, '=')) != (char *)0) && + (p != q) && (q[-1] == '\\')) /* Find value */ + { + a = q - 1; /* Del \ chr; move rest back */ + p = q; + while(*a++ = *q++) + ; + } + + if (q != (char *)0) { + + *q++ = '\0'; /* Separate name and val */ + while (isspace(*q)) + q++; + if (p = strrchr(q, '\n')) + *p = '\0'; + + p = str1; + if ((a = gettok(&p)) == (char *)0) + error("No macro name",(char *)0); + + setmacro(a, q); + + if (getline(&str1s, fd)) + return; + continue; + } + + /* include? */ + p = str1; + while (isspace(*p)) p++; + if (strncmp(p, "include", 7) == 0 && isspace(p[7])) { + char *old_makefile = makefile; + int old_lineno = lineno; + FILE *ifd; + + p += 8; + memmove(str1, p, strlen(p)+1); + expand(&str1s); + p = str1; + while (isspace(*p)) p++; + + if ((q = malloc(strlen(p)+1)) == (char *)0) + fatal("No memory for include",(char *)0,0); + + strcpy(q, p); + p = q; + while ((makefile = gettok(&q)) != (char *)0) { + if ((ifd = fopen(makefile, "r")) == (FILE *)0) + fatal("Can't open %s: %s", makefile, errno); + lineno = 0; + input(ifd); + fclose(ifd); + } + free(p); + makefile = old_makefile; + lineno = old_lineno; + + if (getline(&str1s, fd)) + return; + continue; + } + + /* Search for commands on target line --- do not expand them ! */ + q = str1; + cp = (struct cmd *)0; + if ((a = strchr(q, ';')) != (char *)0) { + *a++ = '\0'; /* Separate dependents and commands */ + if ( a) cp = newcmd(a, cp); + } + + expand(&str1s); + p = str1; + + while (isspace(*p)) p++; + + while (((q = strchr(p, ':')) != (char *)0) && + (p != q) && (q[-1] == '\\')) /* Find dependents */ + { + a = q - 1; /* Del \ chr; move rest back */ + p = q; + while(*a++ = *q++) ; + } + + if (q == (char *)0) + error("No targets provided",(char *)0); + + *q++ = '\0'; /* Separate targets and dependents */ + + if (*q == ':') { /* Double colon */ + dbl = 1; + q++; + } + else + dbl = 0; + + for (dp = (struct depend *)0; ((p = gettok(&q)) != (char *)0);) + /* get list of dep's */ + { + np = newname(p); /* Intern name */ + dp = newdep(np, dp); /* Add to dep list */ + } + + *((q = str1) + strlen(str1) + 1) = '\0'; + /* Need two nulls for gettok (Remember separation) */ + + if (getline(&str2s, fd) == FALSE) { /* Get commands */ + while (*str2 == TABCHAR) { + cp = newcmd(&str2[0], cp); + if (getline(&str2s, fd)) + break; + } + } + + while ((p = gettok(&q)) != (char *)0) /* Get list of targ's */ + { + np = newname(p); /* Intern name */ + newline(np, dp, cp, dbl); + if (!firstname && p[0] != '.') + firstname = np; + } + + if (feof(fd)) /* EOF? */ + return; + + while (strlen(str2) >= str1s.len) strrealloc(&str1s); + strcpy(str1, str2); + } +} diff --git a/commands/make/macro.c b/commands/make/macro.c new file mode 100755 index 000000000..2afd93b9d --- /dev/null +++ b/commands/make/macro.c @@ -0,0 +1,197 @@ +/************************************************************************* + * + * m a k e : m a c r o . c + * + * Macro control for make + *======================================================================== + * Edition history + * + * # Date Comments By + * --- -------- ---------------------------------------------------- --- + * 1 ?? ?? + * 2 23.08.89 Error message corrected RAL + * 3 30.08.89 macro flags added, indention ch. PSH,RAL + * 4 03.09.89 fixed LZ eliminated, doexp(...) changed RAL + * 5 06.09.89 M_MAKE added, setDFmacro added RAL + * 6 20.09.89 work around for Minix PC ACK bug BE,RAL + * ------------ Version 2.0 released ------------------------------- RAL + * + *************************************************************************/ + +#include "h.h" + + +static char buf[256]; + +struct macro *getmp(name) +char *name; +{ + register struct macro *rp; + + for (rp = macrohead; rp; rp = rp->m_next) + if (strcmp(name, rp->m_name) == 0) + return rp; + return (struct macro *)0; +} + + +char *getmacro(name) +char *name; +{ + struct macro *mp; + + if (mp = getmp(name)) + return mp->m_val; +/* else*/ + return ""; +} + + +struct macro *setmacro(name, val) +char *name; +char *val; +{ + register struct macro *rp; + register char *cp; + + + /* Replace macro definition if it exists */ + for (rp = macrohead; rp; rp = rp->m_next) + if (strcmp(name, rp->m_name) == 0) { + if(rp->m_flag & M_OVERRIDE) return rp; /* mustn't change */ + free(rp->m_val); /* Free space from old */ + break; + } + + if (!rp) /* If not defined, allocate space for new */ + { + if ((rp = (struct macro *)malloc(sizeof (struct macro))) + == (struct macro *)0) + fatal("No memory for macro",(char *)0,0); + + rp->m_next = macrohead; + macrohead = rp; + rp->m_flag = FALSE; + + if ((cp = (char *) malloc(strlen(name)+1)) == (char *)0) + fatal("No memory for macro",(char *)0,0); + strcpy(cp, name); + rp->m_name = cp; + } + + if ((cp = (char *) malloc(strlen(val)+1)) == (char *)0) + fatal("No memory for macro",(char *)0,0); + strcpy(cp, val); /* Copy in new value */ + rp->m_val = cp; + + return rp; +} + + +void setDFmacro(name, val) +char *name; +char *val; +{ + char *c,*tmp; + int len; + static char filename[]="@F"; + static char dirname[] ="@D"; + + setmacro(name,val); + *filename = *name; + *dirname = *name; + /* Null string -- not defined macro */ + if ( !(*val)) { + setmacro(filename,""); + setmacro(dirname,""); + return; + } + if (!(c = strrchr(val,(int)'/'))) { + setmacro(filename,val); + setmacro(dirname,"./"); + return; + } + setmacro(filename,c+1); + len = c - val + 1; + if((tmp = (char *) malloc(len + 1)) == (char *) 0) + fatal("No memory for tmp",(char *)0,0); + strncpy(tmp,val,len); + tmp[len] = '\0'; + setmacro(dirname,tmp); + free(tmp); + return; +} + +/* + * Do the dirty work for expand + */ +void doexp(to, from) +struct str *to; +char *from; +{ + register char *rp; + register char *p; + char *q; + struct macro *mp; + + + rp = from; + p = &(*to->ptr)[to->pos]; + while (*rp) { + if (*rp != '$') { + *p++ = *rp++; + to->pos++; + } + else { + q = buf; + if (*++rp == '{') + while (*++rp && *rp != '}') + *q++ = *rp; + else if (*rp == '(') + while (*++rp && *rp != ')') + *q++ = *rp; + else if (!*rp) { + *p++ = '$'; + to->pos++; + goto bail; + } + else + *q++ = *rp; + *q = '\0'; + if (*rp) + rp++; + if (!(mp = getmp(buf))) + mp = setmacro(buf, ""); + if (mp->m_flag & M_MARK) + fatal("Infinitely recursive macro %s", mp->m_name,0); + mp->m_flag |= M_MARK; + if ( mp->m_flag & M_MAKE) expmake = TRUE; + doexp(to, mp->m_val); + p = &(*to->ptr)[to->pos]; + mp->m_flag &= ~M_MARK; + } + bail: + if (to->pos >= to->len) { + strrealloc(to); + p = &(*to->ptr)[to->pos]; + } + } + *p = '\0'; +} + + +/* + * Expand any macros in str. + */ +void expand(strs) +struct str *strs; +{ + char *a; + + if ((a = (char *) malloc(strlen(*strs->ptr)+1)) == (char *)0) + fatal("No memory for temporary string",(char *)0,0); + strcpy(a, *strs->ptr); + strs->pos = 0; + doexp(strs, a); + free(a); +} diff --git a/commands/make/main.c b/commands/make/main.c new file mode 100755 index 000000000..5cde36f71 --- /dev/null +++ b/commands/make/main.c @@ -0,0 +1,288 @@ +/************************************************************************* + * + * m a k e : m a i n . c + * + *======================================================================== + * Edition history + * + * # Date Comments By + * --- -------- ---------------------------------------------------- --- + * 1 ?? ?? + * 2 01.07.89 strcmp(makefile,..) only if makefile a valid ptr. RAL + * 3 23.08.89 initname() added RAL + * 4 30.08.89 argument parsing impr., indention ch., macro fl. add.PSH,RAL + * 5 03.09.89 k-option added, initname -> init changed RAL + * 6 06.09.89 environment, MAKEFLAGS, e,d,a options added, RAL + * 7 09.09.89 tos support added, fatal args added, fopen makefile PHH,RAL + * 8 17.09.89 setoptions fixed for __STDC__ RAL + * ------------ Version 2.0 released ------------------------------- RAL + * + *************************************************************************/ + +/* + * make: + * + * -a try to guess undefined ambiguous macros (*,<) + * -d print debugging info + * -e environment macro def. overwrite makefile def. + * -f makefile name + * -i ignore exit status + * -k continue on errors + * -n pretend to make + * -p print all macros & targets + * -q question up-to-dateness of target. Return exit status 1 if not + * -r don't not use inbuilt rules + * -s make silently + * -t touch files instead of making them + * -m Change memory requirements (EON only) + */ + +#define EXTERN +#define INIT(x) = x +#define INITARRAY +#include "h.h" + +static char version[]= "2.0"; + +static FILE *ifd; /* Input file desciptor */ +static char *ptrmakeflags; + +/* There must be enough 'space' for all possible flags ! */ +static char makeflags[] = "MAKEFLAGS= "; + +void main(argc, argv) +int argc; +char **argv; +{ + register char *p; /* For argument processing */ + int estat = 0; /* For question */ + register struct name *np; + struct macro *mp; + int targc; /* temporary for multiple scans */ + char **targv; + char **nargv; /* for removing items from argv */ + char **envp; /* enivironment ptr */ + + + ptrmakeflags = &makeflags[10]; + myname = (argc-- < 1) ? "make" : *argv++; +#ifdef tos + myname = "Make"; +#endif + + targc = argc; + targv = nargv = argv; + while (targc--) { + if((p = strchr(*targv, '=')) != (char *)NULL) { + *p = '\0'; + mp = setmacro(*targv, p + 1); + mp->m_flag |= M_OVERRIDE; + --argc; + } else + *nargv++ = *targv; + + ++targv; + } + + targc = argc; + targv = nargv = argv; + while (targc--) { + if (**targv == '-') { + --argc; + p = *targv++; + while (*++p != '\0') { + switch(mylower(*p)) { + case 'f': /* Alternate file name */ + if (*++p == '\0') { + --argc; + if (targc-- == 0) + usage(); + p = *targv++; + } + makefile = p; + goto end_of_args; +#ifdef eon + case 'm': /* Change space requirements */ + if (*++p == '\0') { + --argc; + if (targc-- <= 0) + usage(); + p = *targv++; + } + memspace = atoi(p); + goto end_of_args; +#endif + default : + setoption(*p); + break; + } + } + end_of_args:; + } else + *nargv++ = *targv++; + } + + /* evaluate and update environment MAKEFLAGS */ + if((p =getenv("MAKEFLAGS")) != (char *)0) + while(*p) setoption(*p++); + for( p = ptrmakeflags; !isspace((int)*p); p++) ; + *p = '\0'; + putenv(makeflags); + + +#ifdef eon + if (initalloc(memspace) == 0xffff) /* Must get memory for alloc */ + fatal("Cannot initalloc memory",(char *)0,0); +#endif + + if (makefile && strcmp(makefile, "-") == 0) /* use stdin as makefile */ + ifd = stdin; + else if (!makefile) { /* If no file, then use default */ + if ((ifd = fopen(makefile = DEFN1, "r")) == (FILE *)0) { + if (errno != MNOENT || !DEFN2) + fatal("Can't open %s: %s", DEFN1, errno); + else if ((ifd = fopen(makefile = DEFN2, "r")) == (FILE *)0) + fatal("Can't open %s: %s", DEFN2, errno); + } + } + else if ((ifd = fopen(makefile, "r")) == (FILE *)0) + fatal("Can't open %s: %s", makefile, errno); + + init(); + + makerules(); + + mp = setmacro("MAKE", myname); + mp->m_flag |= M_MAKE; + setmacro("$", "$"); + + /* set environment macros */ + envp = environ; /* get actual environment ptr. */ + while (*envp) { + if((p = strchr(*envp, '=')) != (char *)NULL) { + *p = '\0'; + mp = setmacro(*envp, p + 1); + *p = '='; + if (useenv) mp->m_flag |= M_OVERRIDE; + } else + fatal("invalid environment: %s",*envp,0); + + ++envp; + } + + input(ifd); /* Input all the gunga */ + fclose(ifd); /* Finished with makefile */ + lineno = 0; /* Any calls to error now print no line number */ + + if (print) + prt(); /* Print out structures */ + + np = newname(".SILENT"); + if (np->n_flag & N_TARG) silent = TRUE; + + np = newname(".IGNORE"); + if (np->n_flag & N_TARG) ignore = TRUE; + + precious(); + + if (!firstname) + fatal("No targets defined",(char *)0,0); + + circh(); /* Check circles in target definitions */ + + if (!argc) + estat = make(firstname, 0); + else + while (argc--) { + estat |= make(newname(*argv++), 0); + } + + if (quest) + exit(estat); + else + exit(0); +} + +#ifdef __STDC__ +void setoption(char option) +#else +void setoption(option) +char option; +#endif +{ + register char *c; + + option = mylower(option); + switch(option) { + case 'n': /* Pretend mode */ + domake = FALSE; + break; + case 'i': /* Ignore fault mode */ + ignore = TRUE; + break; + case 'k': /* Continue on errror */ + conterr = TRUE; + break; + case 's': /* Silent about commands */ + silent = TRUE; + break; + case 'p': + print = TRUE; + break; + case 'r': + rules = FALSE; + break; + case 't': + dotouch = TRUE; + break; + case 'q': + quest = TRUE; + break; + case 'e': + useenv = TRUE; + break; + case 'd': + dbginfo = TRUE; + break; + case 'a': + ambigmac = TRUE; + break; + default: /* Wrong option */ + usage(); + } + for( c = ptrmakeflags; !isspace((int)*c); c++) + if ( *c == option) return; + *c = option; +} + +void usage() +{ + fprintf(stderr, "Syntax: %s [{options | macro=val | target}]\n", myname); + fprintf(stderr, "Function: maintaining computer programs V%s\n",version); + fprintf(stderr, "Options : -a : try to guess undefined ambiguous macros (*,<)\n"); + fprintf(stderr, " -d : print debugging information\n"); + fprintf(stderr, " -e : environment macro def. overwrite makefile def.\n"); + fprintf(stderr, " -f filename : makefile name (default: makefile, Makefile)\n"); + fprintf(stderr, " -i : ignore exit status of executed commands\n"); + fprintf(stderr, " -k : continue with unrelated branches on errors\n"); + fprintf(stderr, " -n : pretend to make\n"); + fprintf(stderr, " -p : print all macros & targets\n"); + fprintf(stderr, " -q : question up-to-dateness of target\n"); + fprintf(stderr, " -r : don't use inbuilt rules\n"); + fprintf(stderr, " -s : make silently\n"); + fprintf(stderr, " -t : touch files instead of making them\n"); + fprintf(stderr, "Environment: MAKEFLAGS\n"); + exit(1); +} + + +void fatal(msg, a1, a2) +char *msg; +char *a1; +int a2; +{ + fprintf(stderr, "%s: ", myname); + fprintf(stderr, msg, a1, strerror(a2)); + fputc('\n', stderr); + exit(1); +} diff --git a/commands/make/make.c b/commands/make/make.c new file mode 100755 index 000000000..4ef28ca82 --- /dev/null +++ b/commands/make/make.c @@ -0,0 +1,779 @@ +/************************************************************************* + * + * m a k e : m a k e . c + * + * Do the actual making for make plus system dependent stuff + *======================================================================== + * Edition history + * + * # Date Comments By + * --- -------- ---------------------------------------------------- --- + * 1 ?? ?? + * 2 01.07.89 $<,$* bugs fixed RAL + * 3 23.08.89 (time_t)time((time_t*)0) bug fixed, N_EXISTS added RAL + * 4 30.08.89 leading sp. in cmd. output eliminated, indention ch. PSH,RAL + * 5 03.09.89 :: time fixed, error output -> stderr, N_ERROR intr. + * fixed LZ elimintaed RAL + * 6 07.09.89 implmacro, DF macros,debug stuff added RAL + * 7 09.09.89 tos support added PHH,RAL + * 8 17.09.89 make1 arg. fixed, N_EXEC introduced RAL + * ------------ Version 2.0 released ------------------------------- RAL + * 18.05.90 fixed -n bug with silent rules. (Now echos them.) PAN + * + *************************************************************************/ + +#include "h.h" +#include <sys/wait.h> +#include <unistd.h> + +_PROTOTYPE(static void tellstatus, (FILE *out, char *name, int status)); + +static bool execflag; + +/* + * Exec a shell that returns exit status correctly (/bin/esh). + * The standard EON shell returns the process number of the last + * async command, used by the debugger (ugg). + * [exec on eon is like a fork+exec on unix] + */ +int dosh(string, shell) +char *string; +char *shell; +{ + int number; + +#ifdef unix + return system(string); +#endif +#ifdef tos + return Tosexec(string); +#endif +#ifdef eon + return ((number = execl(shell, shell,"-c", string, 0)) == -1) ? + -1: /* couldn't start the shell */ + wait(number); /* return its exit status */ +#endif +#ifdef os9 + int status, pid; + + strcat(string, "\n"); + if ((number = os9fork(shell, strlen(string), string, 0, 0, 0)) == -1) + return -1; /* Couldn't start a shell */ + do { + if ((pid = wait(&status)) == -1) + return -1; /* child already died!?!? */ + } while (pid != number); + + return status; +#endif +} + + +#ifdef unix +/* + * Make a file look very outdated after an error trying to make it. + * Don't remove, this keeps hard links intact. (kjb) + */ +int makeold(name) char *name; +{ + struct utimbuf a; + + a.actime = a.modtime = 0; /* The epoch */ + + return utime(name, &a); +} +#endif + + +static void tellstatus(out, name, status) +FILE *out; +char *name; +int status; +{ + char cwd[PATH_MAX]; + + fprintf(out, "%s in %s: ", + name, getcwd(cwd, sizeof(cwd)) == NULL ? "?" : cwd); + + if (WIFEXITED(status)) { + fprintf(out, "Exit code %d", WEXITSTATUS(status)); + } else { + fprintf(out, "Signal %d%s", + WTERMSIG(status), status & 0x80 ? " - core dumped" : ""); + } +} + + +/* + * Do commands to make a target + */ +void docmds1(np, lp) +struct name *np; +struct line *lp; +{ + register char *q; + register char *p; + register struct cmd *cp; + bool ssilent; + bool signore; + int estat; + char *shell; + + + if (*(shell = getmacro("SHELL")) == '\0') +#ifdef eon + shell = ":bin/esh"; +#endif +#ifdef unix + shell = "/bin/sh"; +#endif +#ifdef os9 + shell = "shell"; +#endif +#ifdef tos + shell = "DESKTOP"; /* TOS has no shell */ +#endif + + for (cp = lp->l_cmd; cp; cp = cp->c_next) { + execflag = TRUE; + strcpy(str1, cp->c_cmd); + expmake = FALSE; + expand(&str1s); + q = str1; + ssilent = silent; + signore = ignore; + while ((*q == '@') || (*q == '-')) { + if (*q == '@') /* Specific silent */ + ssilent = TRUE; + else /* Specific ignore */ + signore = TRUE; + if (!domake) putchar(*q); /* Show all characters. */ + q++; /* Not part of the command */ + } + + for (p=q; *p; p++) { + if (*p == '\n' && p[1] != '\0') { + *p = ' '; + if (!ssilent || !domake) + fputs("\\\n", stdout); + } + else if (!ssilent || !domake) + putchar(*p); + } + if (!ssilent || !domake) + putchar('\n'); + + if (domake || expmake) { /* Get the shell to execute it */ + fflush(stdout); + if ((estat = dosh(q, shell)) != 0) { + if (estat == -1) + fatal("Couldn't execute %s", shell,0); + else if (signore) { + tellstatus(stdout, myname, estat); + printf(" (Ignored)\n"); + } else { + tellstatus(stderr, myname, estat); + fprintf(stderr, "\n"); + if (!(np->n_flag & N_PREC)) +#ifdef unix + if (makeold(np->n_name) == 0) + fprintf(stderr,"%s: made '%s' look old.\n", myname, np->n_name); +#else + if (unlink(np->n_name) == 0) + fprintf(stderr,"%s: '%s' removed.\n", myname, np->n_name); +#endif + if (!conterr) exit(estat != 0); + np->n_flag |= N_ERROR; + return; + } + } + } + } +} + + +void docmds(np) +struct name *np; +{ + register struct line *lp; + + for (lp = np->n_line; lp; lp = lp->l_next) + docmds1(np, lp); +} + +#ifdef tos +/* + * execute the command submitted by make, + * needed because TOS has no internal shell, + * so we use Pexec to do the job + * v 1.1 of 10/sep/89 by yeti + */ + +#define DELM1 ';' +#define DELM2 ' ' +#define DELM3 ',' + +int Tosexec(string) +char *string; +{ + register char *help, *help2, c; + register unsigned char l=1; + char progname[80], command[255], plain[15]; + static char **envp,*env; + register int error,i; + + /* generate strange TOS environment (RAL) */ + for ( i = 0, envp = environ; *envp; envp++) i += strlen(*envp) +1; + if ((env = malloc(i+1)) == (char *)0) + fatal("No memory for TOS environment",(char *)0,0); + for ( envp = environ, help = env; *envp; envp++) { + strcpy ( help, *envp); + while ( *(help++)) ; + } + *help = '\0'; + + help = progname; + while((*help++=*string++) != ' '); /* progname is command name */ + *--help = '\0'; + + l = strlen(string); /* build option list */ + command[0] = l; /* TOS likes it complicated */ + strcpy(&command[1],string); + if ((error = (int) Pexec(0,progname,command,env)) != -33) { + free(env); + return(error); + } + + /* could'nt find program, try to search the PATH */ + if((help=strrchr(progname,'\\')) != (char *) 0) /* just the */ + strcpy(plain,++help); /* name */ + else if((help=strrchr(progname,'/')) != (char *) 0) + strcpy(plain,++help); + else if((help=strrchr(progname,':')) != (char *) 0) + strcpy(plain,++help); + else + strcpy(plain,progname); + + if(*(help=getmacro("PATH")) == '\0') { + free(env); + return(-33); + } + c = 1; + while(c) + { help2 = &progname[-1]; + i = 0; + while((c=*help++) != '\0' && i<80 && c != DELM1 + && c != DELM2 && c != DELM3) + *++help2 = c, i++; + *++help2 = '\\'; + strcpy(++help2,plain); + if((error=(int) Pexec(0,progname,command,env))!=-33) { + free(env); + return(error); + } + } + free(env); + return(-33); +} + + +/* (stolen from ZOO -- thanks to Rahul Dehsi) +Function mstonix() accepts an MSDOS format date and time and returns +a **IX format time. No adjustment is done for timezone. +*/ + +time_t mstonix (date, time) +unsigned int date, time; +{ + int year, month, day, hour, min, sec, daycount; + time_t longtime; + /* no. of days to beginning of month for each month */ + static int dsboy[12] = { 0, 31, 59, 90, 120, 151, 181, 212, + 243, 273, 304, 334}; + + if (date == 0 && time == 0) /* special case! */ + return (0L); + + /* part of following code is common to zoolist.c */ + year = (((unsigned int) date >> 9) & 0x7f) + 1980; + month = ((unsigned int) date >> 5) & 0x0f; + day = date & 0x1f; + + hour = ((unsigned int) time >> 11)& 0x1f; + min = ((unsigned int) time >> 5) & 0x3f; + sec = ((unsigned int) time & 0x1f) * 2; + +/* DEBUG and leap year fixes thanks to Mark Alexander <uunet!amdahl!drivax!alexande>*/ +#ifdef DEBUG + printf ("mstonix: year=%d month=%d day=%d hour=%d min=%d sec=%d\n", + year, month, day, hour, min, sec); +#endif + /* Calculate days since 1970/01/01 */ + daycount = 365 * (year - 1970) + /* days due to whole years */ + (year - 1969) / 4 + /* days due to leap years */ + dsboy[month-1] + /* days since beginning of this year */ + day-1; /* days since beginning of month */ + + if (year % 4 == 0 && + year % 400 != 0 && month >= 3) /* if this is a leap year and month */ + daycount++; /* is March or later, add a day */ + + /* Knowing the days, we can find seconds */ + longtime = daycount * 24L * 60L * 60L + + hour * 60L * 60L + min * 60 + sec; + return (longtime); +} +#endif /* tos */ + +#ifdef os9 +/* + * Some stuffing around to get the modified time of a file + * in an os9 file system + */ +void getmdate(fd, tbp) +int fd; +struct sgtbuf *tbp; +{ + struct registers regs; + static struct fildes fdbuf; + + + regs.rg_a = fd; + regs.rg_b = SS_FD; + regs.rg_x = &fdbuf; + regs.rg_y = sizeof (fdbuf); + + if (_os9(I_GETSTT, ®s) == -1) { + errno = regs.rg_b & 0xff; + return -1; + } + if (tbp) + { + _strass(tbp, fdbuf.fd_date, sizeof (fdbuf.fd_date)); + tbp->t_second = 0; /* Files are only acurate to mins */ + } + return 0; +} + + +/* + * Kludge routine to return an aproximation of how many + * seconds since 1980. Dates will be in order, but will not + * be lineer + */ +time_t cnvtime(tbp) +struct sgtbuf *tbp; +{ + long acc; + + acc = tbp->t_year - 80; /* Baseyear is 1980 */ + acc = acc * 12 + tbp->t_month; + acc = acc * 31 + tbp->t_day; + acc = acc * 24 + tbp->t_hour; + acc = acc * 60 + tbp->t_minute; + acc = acc * 60 + tbp->t_second; + + return acc; +} + + +/* + * Get the current time in the internal format + */ +void time(tp) +time_t *tp; +{ + struct sgtbuf tbuf; + + + if (getime(&tbuf) < 0) + return -1; + + if (tp) + *tp = cnvtime(&tbuf); + + return 0; +} +#endif + + +/* + * Get the modification time of a file. If the first + * doesn't exist, it's modtime is set to 0. + */ +void modtime(np) +struct name *np; +{ +#ifdef unix + struct stat info; + int r; + + if (is_archive_ref(np->n_name)) { + r = archive_stat(np->n_name, &info); + } else { + r = stat(np->n_name, &info); + } + if (r < 0) { + if (errno != ENOENT) + fatal("Can't open %s: %s", np->n_name, errno); + + np->n_time = 0L; + np->n_flag &= ~N_EXISTS; + } else { + np->n_time = info.st_mtime; + np->n_flag |= N_EXISTS; + } +#endif +#ifdef tos + struct DOSTIME fm; + int fd; + + if((fd=Fopen(np->n_name,0)) < 0) { + np->n_time = 0L; + np->n_flag &= ~N_EXISTS; + } + else { + Fdatime(&fm,fd,0); + Fclose(fd); + np->n_time = mstonix((unsigned int)fm.date,(unsigned int)fm.time); + np->n_flag |= N_EXISTS; + } +#endif +#ifdef eon + struct stat info; + int fd; + + if ((fd = open(np->n_name, 0)) < 0) { + if (errno != ER_NOTF) + fatal("Can't open %s: %s", np->n_name, errno); + + np->n_time = 0L; + np->n_flag &= ~N_EXISTS; + } + else if (getstat(fd, &info) < 0) + fatal("Can't getstat %s: %s", np->n_name, errno); + else { + np->n_time = info.st_mod; + np->n_flag |= N_EXISTS; + } + + close(fd); +#endif +#ifdef os9 + struct sgtbuf info; + int fd; + + if ((fd = open(np->n_name, 0)) < 0) { + if (errno != E_PNNF) + fatal("Can't open %s: %s", np->n_name, errno); + + np->n_time = 0L; + np->n_flag &= ~N_EXISTS; + } + else if (getmdate(fd, &info) < 0) + fatal("Can't getstat %s: %s", np->n_name, errno); + else { + np->n_time = cnvtime(&info); + np->n_flag |= N_EXISTS; + } + + close(fd); +#endif +} + + +/* + * Update the mod time of a file to now. + */ +void touch(np) +struct name *np; +{ + char c; + int fd; + + if (!domake || !silent) printf("touch(%s)\n", np->n_name); + + if (domake) { +#ifdef unix + struct utimbuf a; + + a.actime = a.modtime = time((time_t *)NULL); + if (utime(np->n_name, &a) < 0) + printf("%s: '%s' not touched - non-existant\n", + myname, np->n_name); +#endif +#ifdef tos + struct DOSTIME fm; + int fd; + + if((fd=Fopen(np->n_name,0)) < 0) { + printf("%s: '%s' not touched - non-existant\n", + myname, np->n_name); + } + else { + fm.date = Tgetdate(); + fm.time = Tgettime(); + Fdatime(&fm,fd,1); + Fclose(fd); + } +#endif +#ifdef eon + if ((fd = open(np->n_name, 0)) < 0) + printf("%s: '%s' not touched - non-existant\n", + myname, np->n_name); + else + { + uread(fd, &c, 1, 0); + uwrite(fd, &c, 1); + } + close(fd); +#endif +#ifdef os9 + /* + * Strange that something almost as totally useless + * as this is easy to do in os9! + */ + if ((fd = open(np->n_name, S_IWRITE)) < 0) + printf("%s: '%s' not touched - non-existant\n", + myname, np->n_name); + close(fd); +#endif + } +} + + +/* + * Recursive routine to make a target. + */ +int make(np, level) +struct name *np; +int level; +{ + register struct depend *dp; + register struct line *lp; + register struct depend *qdp; + time_t now, t, dtime = 0; + bool dbgfirst = TRUE; + char *basename = (char *) 0; + char *inputname = (char *) 0; + + if (np->n_flag & N_DONE) { + if(dbginfo) dbgprint(level,np,"already done"); + return 0; + } + + modtime(np); /* Gets modtime of this file */ + + while (time(&now) == np->n_time) { + /* Time of target is equal to the current time. This bothers us, because + * we can't tell if it needs to be updated if we update a file it depends + * on within a second. So wait a second. (A per-second timer is too + * coarse for today's fast machines.) + */ + sleep(1); + } + + if (rules) { + for (lp = np->n_line; lp; lp = lp->l_next) + if (lp->l_cmd) + break; + if (!lp) + dyndep(np,&basename,&inputname); + } + + if (!(np->n_flag & (N_TARG | N_EXISTS))) { + fprintf(stderr,"%s: Don't know how to make %s\n", myname, np->n_name); + if (conterr) { + np->n_flag |= N_ERROR; + if (dbginfo) dbgprint(level,np,"don't know how to make"); + return 0; + } + else exit(1); + } + + for (qdp = (struct depend *)0, lp = np->n_line; lp; lp = lp->l_next) { + for (dp = lp->l_dep; dp; dp = dp->d_next) { + if(dbginfo && dbgfirst) { + dbgprint(level,np," {"); + dbgfirst = FALSE; + } + make(dp->d_name, level+1); + if (np->n_time < dp->d_name->n_time) + qdp = newdep(dp->d_name, qdp); + dtime = max(dtime, dp->d_name->n_time); + if (dp->d_name->n_flag & N_ERROR) np->n_flag |= N_ERROR; + if (dp->d_name->n_flag & N_EXEC ) np->n_flag |= N_EXEC; + } + if (!quest && (np->n_flag & N_DOUBLE) && + (np->n_time < dtime || !( np->n_flag & N_EXISTS))) { + execflag = FALSE; + make1(np, lp, qdp, basename, inputname); /* free()'s qdp */ + dtime = 0; + qdp = (struct depend *)0; + if(execflag) np->n_flag |= N_EXEC; + } + } + + np->n_flag |= N_DONE; + + if (quest) { + t = np->n_time; + np->n_time = now; + return (t < dtime); + } + else if ((np->n_time < dtime || !( np->n_flag & N_EXISTS)) + && !(np->n_flag & N_DOUBLE)) { + execflag = FALSE; + make1(np, (struct line *)0, qdp, basename, inputname); /* free()'s qdp */ + np->n_time = now; + if ( execflag) np->n_flag |= N_EXEC; + } + else if ( np->n_flag & N_EXEC ) { + np->n_time = now; + } + + if (dbginfo) { + if(dbgfirst) { + if(np->n_flag & N_ERROR) + dbgprint(level,np,"skipped because of error"); + else if(np->n_flag & N_EXEC) + dbgprint(level,np,"successfully made"); + else dbgprint(level,np,"is up to date"); + } + else { + if(np->n_flag & N_ERROR) + dbgprint(level,(struct name *)0,"} skipped because of error"); + else if(np->n_flag & N_EXEC) + dbgprint(level,(struct name *)0,"} successfully made"); + else dbgprint(level,(struct name *)0,"} is up to date"); + } + } + if (level == 0 && !(np->n_flag & N_EXEC)) + printf("%s: '%s' is up to date\n", myname, np->n_name); + + if(basename) + free(basename); + return 0; +} + + +void make1(np, lp, qdp, basename, inputname) +struct name *np; +struct line *lp; +register struct depend *qdp; +char *basename; +char *inputname; +{ + register struct depend *dp; + + + if (dotouch) + touch(np); + else if (!(np->n_flag & N_ERROR)) { + strcpy(str1, ""); + + if(!inputname) { + inputname = str1; /* default */ + if (ambigmac) implmacros(np,lp,&basename,&inputname); + } + setDFmacro("<",inputname); + + if(!basename) + basename = str1; + setDFmacro("*",basename); + + for (dp = qdp; dp; dp = qdp) { + if (strlen(str1)) + strcat(str1, " "); + strcat(str1, dp->d_name->n_name); + qdp = dp->d_next; + free(dp); + } + setmacro("?", str1); + setDFmacro("@", np->n_name); + + if (lp) /* lp set if doing a :: rule */ + docmds1(np, lp); + else + docmds(np); + } +} + +void implmacros(np,lp, pbasename,pinputname) +struct name *np; +struct line *lp; +char **pbasename; /* Name without suffix */ +char **pinputname; +{ + struct line *llp; + register char *p; + register char *q; + register char *suff; /* Old suffix */ + int baselen; + struct depend *dp; + bool dpflag = FALSE; + + /* get basename out of target name */ + p = str2; + q = np->n_name; + suff = suffix(q); + while ( *q && (q < suff || !suff)) *p++ = *q++; + *p = '\0'; + if ((*pbasename = (char *) malloc(strlen(str2)+1)) == (char *)0 ) + fatal("No memory for basename",(char *)0,0); + strcpy(*pbasename,str2); + baselen = strlen(str2); + + if ( lp) + llp = lp; + else + llp = np->n_line; + + while (llp) { + for (dp = llp->l_dep; dp; dp = dp->d_next) { + if( strncmp(*pbasename,dp->d_name->n_name,baselen) == 0) { + *pinputname = dp->d_name->n_name; + return; + } + if( !dpflag) { + *pinputname = dp->d_name->n_name; + dpflag = TRUE; + } + } + if (lp) break; + llp = llp->l_next; + } + +#if NO_WE_DO_WANT_THIS_BASENAME + free(*pbasename); /* basename ambiguous or no dependency file */ + *pbasename = (char *)0; +#endif + return; +} + +void dbgprint(level,np,comment) +int level; +struct name *np; +char *comment; +{ + char *timep; + + if(np) { + timep = ctime(&np->n_time); + timep[24] = '\0'; + fputs(&timep[4],stdout); + } + else fputs(" ",stdout); + fputs(" ",stdout); + while(level--) fputs(" ",stdout); + if (np) { + fputs(np->n_name,stdout); + if (np->n_flag & N_DOUBLE) fputs(" :: ",stdout); + else fputs(" : ",stdout); + } + fputs(comment,stdout); + putchar((int)'\n'); + fflush(stdout); + return; +} + diff --git a/commands/make/reader.c b/commands/make/reader.c new file mode 100755 index 000000000..4d0fed500 --- /dev/null +++ b/commands/make/reader.c @@ -0,0 +1,134 @@ +/************************************************************************* + * + * m a k e : r e a d e r . c + * + * Read in makefile + *======================================================================== + * Edition history + * + * # Date Comments By + * --- -------- ---------------------------------------------------- --- + * 1 ?? ?? + * 2 23.08.89 cast to NULL added RAL + * 3 30.08.89 indention changed PSH,RAL + * 4 03.09.89 fixed LZ eliminated RAL + * ------------ Version 2.0 released ------------------------------- RAL + * + *************************************************************************/ + +#include "h.h" + + +/* + * Syntax error handler. Print message, with line number, and exits. + */ +void error(msg, a1) +char *msg; +char *a1; +{ + fprintf(stderr, "%s: ", myname); + fprintf(stderr, msg, a1); + if (lineno) fprintf(stderr, " in %s near line %d", makefile, lineno); + fputc('\n', stderr); + exit(1); +} + + +/* + * Read a line into the supplied string. Remove + * comments, ignore blank lines. Deal with quoted (\) #, and + * quoted newlines. If EOF return TRUE. + * + * The comment handling code has been changed to leave comments and + * backslashes alone in shell commands (lines starting with a tab). + * This is not what POSIX wants, but what all makes do. (KJB) + */ +bool getline(strs, fd) +struct str *strs; +FILE *fd; +{ + register char *p; + char *q; + int c; + + for (;;) { + strs->pos = 0; + for (;;) { + do { + if (strs->pos >= strs->len) + strrealloc(strs); + if ((c = getc(fd)) == EOF) + return TRUE; /* EOF */ + (*strs->ptr)[strs->pos++] = c; + } while (c != '\n'); + + lineno++; + + if (strs->pos >= 2 && (*strs->ptr)[strs->pos - 2] == '\\') { + (*strs->ptr)[strs->pos - 2] = '\n'; + strs->pos--; + } else { + break; + } + } + + if (strs->pos >= strs->len) + strrealloc(strs); + (*strs->ptr)[strs->pos] = '\0'; + + p = q = *strs->ptr; + while (isspace(*q)) q++; + if (*p != '\t' || *q == '#') { + while (((q = strchr(p, '#')) != (char *)0) && + (p != q) && (q[-1] == '\\')) + { + char *a; + + a = q - 1; /* Del \ chr; move rest back */ + p = q; + while (*a++ = *q++) + ; + } + if (q != (char *)0) + { + q[0] = '\n'; + q[1] = '\0'; + } + } + + p = *strs->ptr; + while (isspace(*p)) /* Checking for blank */ + p++; + + if (*p != '\0') + return FALSE; + } +} + + +/* + * Get a word from the current line, surounded by white space. + * return a pointer to it. String returned has no white spaces + * in it. + */ +char *gettok(ptr) +register char **ptr; +{ + register char *p; + + + while (isspace(**ptr)) /* Skip spaces */ + (*ptr)++; + + if (**ptr == '\0') /* Nothing after spaces */ + return ((char *)NULL); + + p = *ptr; /* word starts here */ + + while ((**ptr != '\0') && (!isspace(**ptr))) + (*ptr)++; /* Find end of word */ + + *(*ptr)++ = '\0'; /* Terminate it */ + + return(p); +} diff --git a/commands/make/rules.c b/commands/make/rules.c new file mode 100755 index 000000000..a0335ba90 --- /dev/null +++ b/commands/make/rules.c @@ -0,0 +1,314 @@ +/************************************************************************* + * + * m a k e : r u l e s . c + * + * Control of the implicit suffix rules + *======================================================================== + * Edition history + * + * # Date Comments By + * --- -------- ---------------------------------------------------- --- + * 1 ?? ?? + * 2 01.07.89 $<,$* bugs fixed, impl. r. ending in expl. r. added RAL + * 3 23.08.89 suffix as macro, testname intr., algorithem to find + * source dep. made more intelligent (see Readme3) RAL + * 4 30.08.89 indention changed PSH,RAL + * 5 03.09.89 fixed LZ eliminated RAL + * 6 07.09.89 rules of type '.c', .DEFAULT added, dep. search impr.RAL + * ------------ Version 2.0 released ------------------------------- RAL + * + *************************************************************************/ + +#include "h.h" + + +/* + * Dynamic dependency. This routine applies the suffis rules + * to try and find a source and a set of rules for a missing + * target. If found, np is made into a target with the implicit + * source name, and rules. Returns TRUE if np was made into + * a target. + */ +bool dyndep(np,pbasename,pinputname) +struct name *np; +char **pbasename; /* Name without suffix */ +char **pinputname; +{ + register char *p; + register char *q; + register char *suff; /* Old suffix */ + struct name *op = (struct name *)0,*optmp; /* New dependent */ + struct name *sp; /* Suffix */ + struct line *lp,*nlp; + struct depend *dp,*ndp; + struct cmd *cmdp; + char *newsuff; + bool depexists = FALSE; + + + p = str1; + q = np->n_name; + suff = suffix(q); + while (*q && (q < suff || !suff)) *p++ = *q++; + *p = '\0'; + if ((*pbasename = (char *) malloc(strlen(str1)+1)) == (char *)0 ) + fatal("No memory for basename",(char *)0,0); + strcpy(*pbasename,str1); + if ( !suff) suff = p - str1 + *pbasename; /* set suffix to nullstring */ + + if (!((sp = newname(".SUFFIXES"))->n_flag & N_TARG)) return FALSE; + + /* search all .SUFFIXES lines */ + for (lp = sp->n_line; lp; lp = lp->l_next) + /* try all suffixes */ + for (dp = lp->l_dep; dp; dp = dp->d_next) { + /* compose implicit rule name (.c.o)...*/ + newsuff = dp->d_name->n_name; + while (strlen(suff)+strlen(newsuff)+1 >= str1s.len) strrealloc(&str1s); + p = str1; + q = newsuff; + while (*p++ = *q++) ; + p--; + q = suff; + while (*p++ = *q++) ; + /* look if the rule exists */ + sp = newname(str1); + if (sp->n_flag & N_TARG) { + /* compose resulting dependency name */ + while (strlen(*pbasename) + strlen(newsuff)+1 >= str1s.len) + strrealloc(&str1s); + q = *pbasename; + p = str1; + while (*p++ = *q++) ; + p--; + q = newsuff; + while (*p++ = *q++) ; + /* test if dependency file or an explicit rule exists */ + if ((optmp= testname(str1)) != (struct name *)0) { + /* store first possible dependency as default */ + if ( op == (struct name *)0) { + op = optmp; + cmdp = sp->n_line->l_cmd; + } + /* check if testname is an explicit dependency */ + for ( nlp=np->n_line; nlp; nlp=nlp->l_next) { + for( ndp=nlp->l_dep; ndp; ndp=ndp->d_next) { + if ( strcmp( ndp->d_name->n_name, str1) == 0) { + op = optmp; + cmdp = sp->n_line->l_cmd; + ndp = (struct depend *) 0; + goto found2; + } + depexists = TRUE; + } + } + /* if no explicit dependencies : accept testname */ + if (!depexists) goto found; + } + } + } + + if ( op == (struct name *)0) { + if( np->n_flag & N_TARG) { /* DEFAULT handling */ + if (!((sp = newname(".DEFAULT"))->n_flag & N_TARG)) return FALSE; + if (!(sp->n_line)) return FALSE; + cmdp = sp->n_line->l_cmd; + for ( nlp=np->n_line; nlp; nlp=nlp->l_next) { + if ( ndp=nlp->l_dep) { + op = ndp->d_name; + ndp = (struct depend *)0; + goto found2; + } + } + newline(np, (struct depend *)0, cmdp, 0); + *pinputname = (char *)0; + *pbasename = (char *)0; + return TRUE; + } + else return FALSE; + } + +found: + ndp = newdep(op, (struct depend *)0); +found2: + newline(np, ndp, cmdp, 0); + *pinputname = op->n_name; + return TRUE; +} + + +/* + * Make the default rules + */ +void makerules() +{ + struct cmd *cp; + struct name *np; + struct depend *dp; + + +#ifdef eon + setmacro("BDSCC", "asm"); + /* setmacro("BDSCFLAGS", ""); */ + cp = newcmd("$(BDSCC) $(BDSCFLAGS) -n $<", (struct cmd *)0); + np = newname(".c.o"); + newline(np, (struct depend *)0, cp, 0); + + setmacro("CC", "c"); + setmacro("CFLAGS", "-O"); + cp = newcmd("$(CC) $(CFLAGS) -c $<", (struct cmd *)0); + np = newname(".c.obj"); + newline(np, (struct depend *)0, cp, 0); + + setmacro("M80", "asm -n"); + /* setmacro("M80FLAGS", ""); */ + cp = newcmd("$(M80) $(M80FLAGS) $<", (struct cmd *)0); + np = newname(".mac.o"); + newline(np, (struct depend *)0, cp, 0); + + setmacro("AS", "zas"); + /* setmacro("ASFLAGS", ""); */ + cp = newcmd("$(ZAS) $(ASFLAGS) -o $@ $<", (struct cmd *)0); + np = newname(".as.obj"); + newline(np, (struct depend *)0, cp, 0); + + np = newname(".as"); + dp = newdep(np, (struct depend *)0); + np = newname(".obj"); + dp = newdep(np, dp); + np = newname(".c"); + dp = newdep(np, dp); + np = newname(".o"); + dp = newdep(np, dp); + np = newname(".mac"); + dp = newdep(np, dp); + np = newname(".SUFFIXES"); + newline(np, dp, (struct cmd *)0, 0); +#endif + +#ifdef tos +#define unix +#endif + +/* + * Some of the UNIX implicit rules + */ + +#ifdef unix + + setmacro("CC", "cc"); + setmacro("CFLAGS", ""); + + cp = newcmd("$(CC) -S $(CFLAGS) $<", (struct cmd *)0); + np = newname(".c.s"); + newline(np, (struct depend *)0, cp, 0); + + cp = newcmd("$(CC) -c $(CFLAGS) $<", (struct cmd *)0); + np = newname(".c.o"); + newline(np, (struct depend *)0, cp, 0); + +#if this_rule_is_a_bit_too_much_of_a_good_thing +#ifdef MINIXPC + cp = newcmd("$(CC) $(CFLAGS) -i -o $@ $<", (struct cmd *)0); +#else + cp = newcmd("$(CC) $(CFLAGS) -o $@ $<", (struct cmd *)0); +#endif /* MINIXPC */ + np = newname(".c"); + newline(np, (struct depend *)0, cp, 0); +#endif + + cp = newcmd("$(CC) -c $(CFLAGS) $<", (struct cmd *)0); + np = newname(".s.o"); + newline(np, (struct depend *)0, cp, 0); + + setmacro("YACC", "yacc"); + /*setmacro("YFLAGS", ""); */ + cp = newcmd("$(YACC) $(YFLAGS) $<", (struct cmd *)0); + cp = newcmd("mv y.tab.c $@", cp); + np = newname(".y.c"); + newline(np, (struct depend *)0, cp, 0); + + cp = newcmd("$(YACC) $(YFLAGS) $<", (struct cmd *)0); + cp = newcmd("$(CC) $(CFLAGS) -c y.tab.c", cp); + cp = newcmd("mv y.tab.o $@", cp); + np = newname(".y.o"); + cp = newcmd("rm y.tab.c", cp); + newline(np, (struct depend *)0, cp, 0); + + setmacro("FLEX", "flex"); + cp = newcmd("$(FLEX) $(FLEX_FLAGS) $<", (struct cmd *)0); + cp = newcmd("mv lex.yy.c $@", cp); + np = newname(".l.c"); + newline(np, (struct depend *)0, cp, 0); + + cp = newcmd("$(FLEX) $(FLEX_FLAGS) $<", (struct cmd *)0); + cp = newcmd("$(CC) $(CFLAGS) -c lex.yy.c", cp); + cp = newcmd("mv lex.yy.o $@", cp); + np = newname(".l.o"); + cp = newcmd("rm lex.yy.c", cp); + newline(np, (struct depend *)0, cp, 0); + + np = newname(".o"); + dp = newdep(np, (struct depend *)0); + np = newname(".s"); + dp = newdep(np, dp); + np = newname(".c"); + dp = newdep(np, dp); + np = newname(".y"); + dp = newdep(np, dp); + np = newname(".l"); + dp = newdep(np, dp); + np = newname(".SUFFIXES"); + newline(np, dp, (struct cmd *)0, 0); + +#endif /* unix */ + + +#ifdef os9 +/* + * Fairlight use an enhanced version of the C sub-system. + * They have a specialised macro pre-processor. + */ + setmacro("CC", "cc"); + setmacro("CFLAGS", "-z"); + cp = newcmd("$(CC) $(CFLAGS) -r $<", (struct cmd *)0); + + np = newname(".c.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".ca.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".a.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".o.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".mc.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".mca.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".ma.r"); + newline(np, (struct depend *)0, cp, 0); + np = newname(".mo.r"); + newline(np, (struct depend *)0, cp, 0); + + np = newname(".r"); + dp = newdep(np, (struct depend *)0); + np = newname(".mc"); + dp = newdep(np, dp); + np = newname(".mca"); + dp = newdep(np, dp); + np = newname(".c"); + dp = newdep(np, dp); + np = newname(".ca"); + dp = newdep(np, dp); + np = newname(".ma"); + dp = newdep(np, dp); + np = newname(".mo"); + dp = newdep(np, dp); + np = newname(".o"); + dp = newdep(np, dp); + np = newname(".a"); + dp = newdep(np, dp); + np = newname(".SUFFIXES"); + newline(np, dp, (struct cmd *)0, 0); +#endif +} diff --git a/commands/mined/Makefile b/commands/mined/Makefile new file mode 100755 index 000000000..0a3677a93 --- /dev/null +++ b/commands/mined/Makefile @@ -0,0 +1,21 @@ +# Makefile for mined + +CFLAGS = -O -wo -D_MINIX -D_POSIX_SOURCE + +OBJ = mined1.o mined2.o + +all: mined + +mined: $(OBJ) + $(CC) -i -o $@ $(OBJ) + install -S 64k $@ + +install: /usr/bin/mined + +/usr/bin/mined: mined + install -cs -o bin mined $@ + +$(OBJ): mined.h + +clean: + rm -f mined *.o *.s core *.bak diff --git a/commands/mined/mined.h b/commands/mined/mined.h new file mode 100755 index 000000000..48c20c630 --- /dev/null +++ b/commands/mined/mined.h @@ -0,0 +1,373 @@ +/*========================================================================* + * Mined.h * + *========================================================================*/ + +#include <minix/config.h> +#include <sys/types.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> + +#ifndef YMAX +#ifdef UNIX +#include <stdio.h> +#undef putchar +#undef getchar +#undef NULL +#undef EOF +extern char *CE, *VS, *SO, *SE, *CL, *AL, *CM; +#define YMAX 49 +#else +#define YMAX 24 /* Maximum y coordinate starting at 0 */ +/* Escape sequences. */ +extern char *enter_string; /* String printed on entering mined */ +extern char *rev_video; /* String for starting reverse video */ +extern char *normal_video; /* String for leaving reverse video */ +extern char *rev_scroll; /* String for reverse scrolling */ +extern char *pos_string; /* Absolute cursor positioning */ +#define X_PLUS ' ' /* To be added to x for cursor sequence */ +#define Y_PLUS ' ' /* To be added to y for cursor sequence */ +#endif /* UNIX */ + +#define XMAX 79 /* Maximum x coordinate starting at 0*/ +#define SCREENMAX (YMAX - 1) /* Number of lines displayed */ +#define XBREAK (XMAX - 0) /* Line shift at this coordinate */ +#define SHIFT_SIZE 25 /* Number of chars to shift */ +#define SHIFT_MARK '!' /* Char indicating line continues */ +#define MAX_CHARS 1024 /* Maximum chars on one line */ + +/* LINE_START must be rounded up to the lowest SHIFT_SIZE */ +#define LINE_START (((-MAX_CHARS - 1) / SHIFT_SIZE) * SHIFT_SIZE \ + - SHIFT_SIZE) +#define LINE_END (MAX_CHARS + 1) /* Highest x-coordinate for line */ + +#define LINE_LEN (XMAX + 1) /* Number of characters on line */ +#define SCREEN_SIZE (XMAX * YMAX) /* Size of I/O buffering */ +#define BLOCK_SIZE 1024 + +/* Return values of functions */ +#define ERRORS -1 +#define NO_LINE (ERRORS - 1) /* Must be < 0 */ +#define FINE (ERRORS + 1) +#define NO_INPUT (ERRORS + 2) + +#define STD_OUT 1 /* File descriptor for terminal */ + +#if (CHIP == INTEL) +#define MEMORY_SIZE (50 * 1024) /* Size of data space to malloc */ +#endif + +#define REPORT 2 /* Report change of lines on # lines */ + +typedef int FLAG; + +/* General flags */ +#define FALSE 0 +#define TRUE 1 +#define NOT_VALID 2 +#define VALID 3 +#define OFF 4 +#define ON 5 + +/* Expression flags */ +#define FORWARD 6 +#define REVERSE 7 + +/* Yank flags */ +#define SMALLER 8 +#define BIGGER 9 +#define SAME 10 +#define EMPTY 11 +#define NO_DELETE 12 +#define DELETE 13 +#define READ 14 +#define WRITE 15 + +/* + * The Line structure. Each line entry contains a pointer to the next line, + * a pointer to the previous line, a pointer to the text and an unsigned char + * telling at which offset of the line printing should start (usually 0). + */ +struct Line { + struct Line *next; + struct Line *prev; + char *text; + unsigned char shift_count; +}; + +typedef struct Line LINE; + +/* Dummy line indicator */ +#define DUMMY 0x80 +#define DUMMY_MASK 0x7F + +/* Expression definitions */ +#define NO_MATCH 0 +#define MATCH 1 +#define REG_ERROR 2 + +#define BEGIN_LINE (2 * REG_ERROR) +#define END_LINE (2 * BEGIN_LINE) + +/* + * The regex structure. Status can be any of 0, BEGIN_LINE or REG_ERROR. In + * the last case, the result.err_mess field is assigned. Start_ptr and end_ptr + * point to the match found. For more details see the documentation file. + */ +struct regex { + union { + char *err_mess; + int *expression; + } result; + char status; + char *start_ptr; + char *end_ptr; +}; + +typedef struct regex REGEX; + +/* NULL definitions */ +#define NIL_PTR ((char *) 0) +#define NIL_LINE ((LINE *) 0) +#define NIL_REG ((REGEX *) 0) +#define NIL_INT ((int *) 0) + +/* + * Forward declarations + */ +extern int nlines; /* Number of lines in file */ +extern LINE *header; /* Head of line list */ +extern LINE *tail; /* Last line in line list */ +extern LINE *top_line; /* First line of screen */ +extern LINE *bot_line; /* Last line of screen */ +extern LINE *cur_line; /* Current line in use */ +extern char *cur_text; /* Pointer to char on current line in use */ +extern int last_y; /* Last y of screen. Usually SCREENMAX */ +extern int ymax; +extern int screenmax; +extern char screen[SCREEN_SIZE];/* Output buffer for "writes" and "reads" */ + +extern int x, y; /* x, y coordinates on screen */ +extern FLAG modified; /* Set when file is modified */ +extern FLAG stat_visible; /* Set if status_line is visible */ +extern FLAG writable; /* Set if file cannot be written */ +extern FLAG quit; /* Set when quit character is typed */ +extern FLAG rpipe; /* Set if file should be read from stdin */ +extern int input_fd; /* Fd for command input */ +extern FLAG loading; /* Set if we're loading a file */ +extern int out_count; /* Index in output buffer */ +extern char file_name[LINE_LEN]; /* Name of file in use */ +extern char text_buffer[MAX_CHARS]; /* Buffer for modifying text */ +extern char *blank_line; /* Clear line to end */ + +extern char yank_file[]; /* Temp file for buffer */ +extern FLAG yank_status; /* Status of yank_file */ +extern long chars_saved; /* Nr of chars saved in buffer */ + +/* + * Empty output buffer + */ +#define clear_buffer() (out_count = 0) + +/* + * Print character on terminal + */ +#define putchar(c) (void) write_char(STD_OUT, (c)) + +/* + * Ring bell on terminal + */ +#define ring_bell() putchar('\07') + +/* + * Print string on terminal + */ +#define string_print(str) (void) writeline(STD_OUT, (str)) + +/* + * Flush output buffer + */ +#define flush() (void) flush_buffer(STD_OUT) + +/* + * Convert cnt to nearest tab position + */ +#define tab(cnt) (((cnt) + 8) & ~07) +#define is_tab(c) ((c) == '\t') + +/* + * Word defenitions + */ +#define white_space(c) ((c) == ' ' || (c) == '\t') +#define alpha(c) ((c) != ' ' && (c) != '\t' && (c) != '\n') + +/* + * Print line on terminal at offset 0 and clear tail of line + */ +#define line_print(line) put_line(line, 0, TRUE) + +/* + * Move to coordinates and set textp. (Don't use address) + */ +#define move_to(nx, ny) move((nx), NIL_PTR, (ny)) + +/* + * Move to coordinates on screen as indicated by textp. + */ +#define move_address(address) move(0, (address), y) + +/* + * Functions handling status_line. ON means in reverse video. + */ +#define status_line(str1, str2) (void) bottom_line(ON, (str1), \ + (str2), NIL_PTR, FALSE) +#define error(str1, str2) (void) bottom_line(ON, (str1), \ + (str2), NIL_PTR, FALSE) +#define get_string(str1,str2, fl) bottom_line(ON, (str1), NIL_PTR, (str2), fl) +#define clear_status() (void) bottom_line(OFF, NIL_PTR, NIL_PTR, \ + NIL_PTR, FALSE) + +/* + * Print info about current file and buffer. + */ +#define fstatus(mess, cnt) file_status((mess), (cnt), file_name, \ + nlines, writable, modified) + +/* + * Get real shift value. + */ +#define get_shift(cnt) ((cnt) & DUMMY_MASK) + +#endif /* YMAX */ + +/* mined1.c */ + +_PROTOTYPE(void FS, (void)); +_PROTOTYPE(void VI, (void)); +_PROTOTYPE(int WT, (void)); +_PROTOTYPE(void XWT, (void)); +_PROTOTYPE(void SH, (void)); +_PROTOTYPE(LINE *proceed, (LINE *line, int count )); +_PROTOTYPE(int bottom_line, (FLAG revfl, char *s1, char *s2, char *inbuf, FLAG statfl )); +_PROTOTYPE(int count_chars, (LINE *line )); +_PROTOTYPE(void move, (int new_x, char *new_address, int new_y )); +_PROTOTYPE(int find_x, (LINE *line, char *address )); +_PROTOTYPE(char *find_address, (LINE *line, int x_coord, int *old_x )); +_PROTOTYPE(int length_of, (char *string )); +_PROTOTYPE(void copy_string, (char *to, char *from )); +_PROTOTYPE(void reset, (LINE *head_line, int screen_y )); +_PROTOTYPE(void set_cursor, (int nx, int ny )); +_PROTOTYPE(void open_device, (void)); +_PROTOTYPE(int getchar, (void)); +_PROTOTYPE(void display, (int x_coord, int y_coord, LINE *line, int count )); +_PROTOTYPE(int write_char, (int fd, int c )); +_PROTOTYPE(int writeline, (int fd, char *text )); +_PROTOTYPE(void put_line, (LINE *line, int offset, FLAG clear_line )); +_PROTOTYPE(int flush_buffer, (int fd )); +_PROTOTYPE(void bad_write, (int fd )); +_PROTOTYPE(void catch, (int sig )); +_PROTOTYPE(void abort_mined, (void)); +_PROTOTYPE(void raw_mode, (FLAG state )); +_PROTOTYPE(void panic, (char *message )); +_PROTOTYPE(char *alloc, (int bytes )); +_PROTOTYPE(void free_space, (char *p )); +/* +#ifdef UNIX +_PROTOTYPE(void (*key_map [128]), (void)); +#else +_PROTOTYPE(void (*key_map [256]), (void)); +#endif +*/ +_PROTOTYPE(void initialize, (void)); +_PROTOTYPE(char *basename, (char *path )); +_PROTOTYPE(void load_file, (char *file )); +_PROTOTYPE(int get_line, (int fd, char *buffer )); +_PROTOTYPE(LINE *install_line, (char *buffer, int length )); +_PROTOTYPE(void main, (int argc, char *argv [])); +_PROTOTYPE(void RD, (void)); +_PROTOTYPE(void I, (void)); +_PROTOTYPE(void XT, (void)); +_PROTOTYPE(void ESC, (void)); +_PROTOTYPE(int ask_save, (void)); +_PROTOTYPE(int line_number, (void)); +_PROTOTYPE(void file_status, (char *message, long count, char *file, int lines, + FLAG writefl, FLAG changed )); +#if __STDC__ +void build_string(char *buf, char *fmt, ...); +#else +void build_string(); +#endif +_PROTOTYPE(char *num_out, (long number )); +_PROTOTYPE(int get_number, (char *message, int *result )); +_PROTOTYPE(int input, (char *inbuf, FLAG clearfl )); +_PROTOTYPE(int get_file, (char *message, char *file )); +_PROTOTYPE(int _getchar, (void)); +_PROTOTYPE(void _flush, (void)); +_PROTOTYPE(void _putchar, (int c )); +_PROTOTYPE(void get_term, (void)); + +/* mined2.c */ + +_PROTOTYPE(void UP, (void)); +_PROTOTYPE(void DN, (void)); +_PROTOTYPE(void LF, (void)); +_PROTOTYPE(void RT, (void)); +_PROTOTYPE(void HIGH, (void)); +_PROTOTYPE(void LOW, (void)); +_PROTOTYPE(void BL, (void)); +_PROTOTYPE(void EL, (void)); +_PROTOTYPE(void GOTO, (void)); +_PROTOTYPE(void PD, (void)); +_PROTOTYPE(void PU, (void)); +_PROTOTYPE(void HO, (void)); +_PROTOTYPE(void EF, (void)); +_PROTOTYPE(void SU, (void)); +_PROTOTYPE(void SD, (void)); +_PROTOTYPE(int forward_scroll, (void)); +_PROTOTYPE(int reverse_scroll, (void)); +_PROTOTYPE(void MP, (void)); +_PROTOTYPE(void move_previous_word, (FLAG remove )); +_PROTOTYPE(void MN, (void)); +_PROTOTYPE(void move_next_word, (FLAG remove )); +_PROTOTYPE(void DCC, (void)); +_PROTOTYPE(void DPC, (void)); +_PROTOTYPE(void DLN, (void)); +_PROTOTYPE(void DNW, (void)); +_PROTOTYPE(void DPW, (void)); +_PROTOTYPE(void S, (int character )); +_PROTOTYPE(void CTL, (void)); +_PROTOTYPE(void LIB, (void)); +_PROTOTYPE(LINE *line_insert, (LINE *line, char *string, int len )); +_PROTOTYPE(int insert, (LINE *line, char *location, char *string )); +_PROTOTYPE(LINE *line_delete, (LINE *line )); +_PROTOTYPE(void delete, (LINE *start_line, char *start_textp, LINE *end_line, char *end_textp )); +_PROTOTYPE(void PT, (void)); +_PROTOTYPE(void IF, (void)); +_PROTOTYPE(void file_insert, (int fd, FLAG old_pos )); +_PROTOTYPE(void WB, (void)); +_PROTOTYPE(void MA, (void)); +_PROTOTYPE(void YA, (void)); +_PROTOTYPE(void DT, (void)); +_PROTOTYPE(void set_up, (FLAG remove )); +_PROTOTYPE(FLAG checkmark, (void)); +_PROTOTYPE(int legal, (void)); +_PROTOTYPE(void yank, (LINE *start_line, char *start_textp, LINE *end_line, char *end_textp, FLAG remove )); +_PROTOTYPE(int scratch_file, (FLAG mode )); +_PROTOTYPE(void SF, (void)); +_PROTOTYPE(void SR, (void)); +_PROTOTYPE(REGEX *get_expression, (char *message )); +_PROTOTYPE(void GR, (void)); +_PROTOTYPE(void LR, (void)); +_PROTOTYPE(void change, (char *message, FLAG file )); +_PROTOTYPE(char *substitute, (LINE *line, REGEX *program, char *replacement )); +_PROTOTYPE(void search, (char *message, FLAG method )); +_PROTOTYPE(int find_y, (LINE *match_line )); +_PROTOTYPE(void finished, (REGEX *program, int *last_exp )); +_PROTOTYPE(void compile, (char *pattern, REGEX *program )); +_PROTOTYPE(LINE *match, (REGEX *program, char *string, FLAG method )); +_PROTOTYPE(int line_check, (REGEX *program, char *string, FLAG method )); +_PROTOTYPE(int check_string, (REGEX *program, char *string, int *expression )); +_PROTOTYPE(int star, (REGEX *program, char *end_position, char *string, int *expression )); +_PROTOTYPE(int in_list, (int *list, int c, int list_length, int opcode )); +_PROTOTYPE(void dummy_line, (void)); diff --git a/commands/mined/mined1.c b/commands/mined/mined1.c new file mode 100755 index 000000000..7c1f88fb7 --- /dev/null +++ b/commands/mined/mined1.c @@ -0,0 +1,1987 @@ +/* + * Part one of the mined editor. + */ + +/* + * Author: Michiel Huisjes. + * + * 1. General remarks. + * + * Mined is a screen editor designed for the MINIX operating system. + * It is meant to be used on files not larger than 50K and to be fast. + * When mined starts up, it reads the file into its memory to minimize + * disk access. The only time that disk access is needed is when certain + * save, write or copy commands are given. + * + * Mined has the style of Emacs or Jove, that means that there are no modes. + * Each character has its own entry in an 256 pointer to function array, + * which is called when that character is typed. Only ASCII characters are + * connected with a function that inserts that character at the current + * location in the file. Two execptions are <linefeed> and <tab> which are + * inserted as well. Note that the mapping between commands and functions + * called is implicit in the table. Changing the mapping just implies + * changing the pointers in this table. + * + * The display consists of SCREENMAX + 1 lines and XMAX + 1 characters. When + * a line is larger (or gets larger during editing) than XBREAK characters, + * the line is either shifted SHIFT_SIZE characters to the left (which means + * that the first SHIFT_SIZE characters are not printed) or the end of the + * line is marked with the SHIFT_MARK character and the rest of the line is + * not printed. A line can never exceed MAX_CHARS characters. Mined will + * always try to keep the cursor on the same line and same (relative) + * x-coordinate if nothing changed. So if you scroll one line up, the cursor + * stays on the same line, or when you move one line down, the cursor will + * move to the same place on the line as it was on the previous. + * Every character on the line is available for editing including the + * linefeed at the the of the line. When the linefeed is deleted, the current + * line and the next line are joined. The last character of the file (which + * is always a linefeed) can never be deleted. + * The bottomline (as indicated by YMAX + 1) is used as a status line during + * editing. This line is usually blank or contains information mined needs + * during editing. This information (or rather questions) is displayed in + * reverse video. + * + * The terminal modes are changed completely. All signals like start/stop, + * interrupt etc. are unset. The only signal that remains is the quit signal. + * The quit signal (^\) is the general abort signal for mined. Typing a ^\ + * during searching or when mined is asking for filenames, etc. will abort + * the function and mined will return to the main loop. Sending a quit + * signal during the main loop will abort the session (after confirmation) + * and the file is not (!) saved. + * The session will also be aborted when an unrecoverable error occurs. E.g + * when there is no more memory available. If the file has been modified, + * mined will ask if the file has to be saved or not. + * If there is no more space left on the disk, mined will just give an error + * message and continue. + * + * The number of system calls are minized. This is done to keep the editor + * as fast as possible. I/O is done in SCREEN_SIZE reads/writes. Accumulated + * output is also flushed at the end of each character typed. + * + * 2. Regular expressions + * + * Mined has a build in regular expression matcher, which is used for + * searching and replace routines. A regular expression consists of a + * sequence of: + * + * 1. A normal character matching that character. + * 2. A . matching any character. + * 3. A ^ matching the begin of a line. + * 4. A $ (as last character of the pattern) mathing the end of a line. + * 5. A \<character> matching <character>. + * 6. A number of characters enclosed in [] pairs matching any of these + * characters. A list of characters can be indicated by a '-'. So + * [a-z] matches any letter of the alphabet. If the first character + * after the '[' is a '^' then the set is negated (matching none of + * the characters). + * A ']', '^' or '-' can be escaped by putting a '\' in front of it. + * Of course this means that a \ must be represented by \\. + * 7. If one of the expressions as described in 1-6 is followed by a + * '*' than that expressions matches a sequence of 0 or more of + * that expression. + * + * Parsing of regular expression is done in two phases. In the first phase + * the expression is compiled into a more comprehensible form. In the second + * phase the actual matching is done. For more details see 3.6. + * + * + * 3. Implementation of mined. + * + * 3.1 Data structures. + * + * The main data structures are as follows. The whole file is kept in a + * double linked list of lines. The LINE structure looks like this: + * + * typedef struct Line { + * struct Line *next; + * struct Line *prev; + * char *text; + * unsigned char shift_count; + * } LINE; + * + * Each line entry contains a pointer to the next line, a pointer to the + * previous line and a pointer to the text of that line. A special field + * shift_count contains the number of shifts (in units of SHIFT_SIZE) + * that is performed on that line. The total size of the structure is 7 + * bytes so a file consisting of 1000 empty lines will waste a lot of + * memory. A LINE structure is allocated for each line in the file. After + * that the number of characters of the line is counted and sufficient + * space is allocated to store them (including a linefeed and a '\0'). + * The resulting address is assigned to the text field in the structure. + * + * A special structure is allocated and its address is assigned to the + * variable header as well as the variable tail. The text field of this + * structure is set to NIL_PTR. The tail->prev of this structure points + * to the last LINE of the file and the header->next to the first LINE. + * Other LINE *variables are top_line and bot_line which point to the + * first line resp. the last line on the screen. + * Two other variables are important as well. First the LINE *cur_line, + * which points to the LINE currently in use and the char *cur_text, + * which points to the character at which the cursor stands. + * Whenever an ASCII character is typed, a new line is build with this + * character inserted. Then the old data space (pointed to by + * cur_line->text) is freed, data space for the new line is allocated and + * assigned to cur_line->text. + * + * Two global variables called x and y represent the x and y coordinates + * from the cursor. The global variable nlines contains the number of + * lines in the file. Last_y indicates the maximum y coordinate of the + * screen (which is usually SCREENMAX). + * + * A few strings must be initialized by hand before compiling mined. + * These string are enter_string, which is printed upon entering mined, + * rev_video (turn on reverse video), normal_video, rev_scroll (perform a + * reverse scroll) and pos_string. The last string should hold the + * absolute position string to be printed for cursor motion. The #define + * X_PLUS and Y_PLUS should contain the characters to be added to the + * coordinates x and y (both starting at 0) to finish cursor positioning. + * + * 3.2 Starting up. + * + * Mined can be called with or without argument and the function + * load_file () is called with these arguments. load_file () checks + * if the file exists if it can be read and if it is writable and + * sets the writable flag accordingly. If the file can be read, + * load_file () reads a line from the file and stores this line into + * a structure by calling install_line () and line_insert () which + * installs the line into the double linked list, until the end of the + * file is reached. + * Lines are read by the function get_line (), which buffers the + * reading in blocks of SCREEN_SIZE. Load_file () also initializes the + * LINE *variables described above. + * + * 3.3 Moving around. + * + * Several commands are implemented for moving through the file. + * Moving up (UP), down (DN) left (LF) and right (RT) are done by the + * arrow keys. Moving one line below the screen scrolls the screen one + * line up. Moving one line above the screen scrolls the screen one line + * down. The functions forward_scroll () and reverse_scroll () take care + * of that. + * Several other move functions exist: move to begin of line (BL), end of + * line (EL) top of screen (HIGH), bottom of screen (LOW), top of file + * (HO), end of file (EF), scroll one page down (PD), scroll one page up + * (PU), scroll one line down (SD), scroll one line up (SU) and move to a + * certain line number (GOTO). + * Two functions called MN () and MP () each move one word further or + * backwards. A word is a number of non-blanks seperated by a space, a + * tab or a linefeed. + * + * 3.4 Modifying text. + * + * The modifying commands can be separated into two modes. The first + * being inserting text, and the other deleting text. Two functions are + * created for these purposes: insert () and delete (). Both are capable + * of deleting or inserting large amounts of text as well as one + * character. Insert () must be given the line and location at which + * the text must be inserted. Is doesn't make any difference whether this + * text contains linefeeds or not. Delete () must be given a pointer to + * the start line, a pointer from where deleting should start on that + * line and the same information about the end position. The last + * character of the file will never be deleted. Delete () will make the + * necessary changes to the screen after deleting, but insert () won't. + * The functions for modifying text are: insert one char (S), insert a + * file (file_insert (fd)), insert a linefeed and put cursor back to + * end of line (LIB), delete character under the cursor (DCC), delete + * before cursor (even linefeed) (DPC), delete next word (DNW), delete + * previous word (DPC) and delete to end of line (if the cursor is at + * a linefeed delete line) (DLN). + * + * 3.5 Yanking. + * + * A few utilities are provided for yanking pieces of text. The function + * MA () marks the current position in the file. This is done by setting + * LINE *mark_line and char *mark_text to the current position. Yanking + * of text can be done in two modes. The first mode just copies the text + * from the mark to the current position (or visa versa) into a buffer + * (YA) and the second also deletes the text (DT). Both functions call + * the function set_up () with the delete flag on or off. Set_up () + * checks if the marked position is still a valid one (by using + * check_mark () and legal ()), and then calls the function yank () with + * a start and end position in the file. This function copies the text + * into a scratch_file as indicated by the variable yank_file. This + * scratch_file is made uniq by the function scratch_file (). At the end + * of copying yank will (if necessary) delete the text. A global flag + * called yank_status keeps track of the buffer (or file) status. It is + * initialized on NOT_VALID and set to EMPTY (by set_up ()) or VALID (by + * yank ()). Several things can be done with the buffer. It can be + * inserted somewhere else in the file (PT) or it can be copied into + * another file (WB), which will be prompted for. + * + * 3.6 Search and replace routines. + * + * Searching for strings and replacing strings are done by regular + * expressions. For any expression the function compile () is called + * with as argument the expression to compile. Compile () returns a + * pointer to a structure which looks like this: + * + * typedef struct regex { + * union { + * char *err_mess; + * int *expression; + * } result; + * char status; + * char *start_ptr; + * char *end_ptr; + * } REGEX; + * + * If something went wrong during compiling (e.g. an illegal expression + * was given), the function reg_error () is called, which sets the status + * field to REG_ERROR and the err_mess field to the error message. If the + * match must be anchored at the beginning of the line (end of line), the + * status field is set to BEGIN_LINE (END_LINE). If none of these special + * cases are true, the field is set to 0 and the function finished () is + * called. Finished () allocates space to hold the compiled expression + * and copies this expression into the expression field of the union + * (bcopy ()). Matching is done by the routines match() and line_check(). + * Match () takes as argument the REGEX *program, a pointer to the + * startposition on the current line, and a flag indicating FORWARD or + * REVERSE search. Match () checks out the whole file until a match is + * found. If match is found it returns a pointer to the line in which the + * match was found else it returns a NIL_LINE. Line_check () takes the + * same arguments, but return either MATCH or NO_MATCH. + * During checking, the start_ptr and end_ptr fields of the REGEX + * structure are assigned to the start and end of the match. + * Both functions try to find a match by walking through the line + * character by character. For each possibility, the function + * check_string () is called with as arguments the REGEX *program and the + * string to search in. It starts walking through the expression until + * the end of the expression or the end of the string is reached. + * Whenever a * is encountered, this position of the string is marked, + * the maximum number of matches are performed and the function star () + * is called in order to try to find the longest match possible. Star () + * takes as arguments the REGEX program, the current position of the + * string, the marked position and the current position of the expression + * Star () walks from the current position of the string back to the + * marked position, and calls string_check () in order to find a match. + * It returns MATCH or NO_MATCH, just as string_check () does. + * Searching is now easy. Both search routines (forward (SF) and + * backwards search (SR)) call search () with an apropiate message and a + * flag indicating FORWARD or REVERSE search. Search () will get an + * expression from the user by calling get_expression(). Get_expression() + * returns a pointer to a REGEX structure or NIL_REG upon errors and + * prompts for the expression. If no expression if given, the previous is + * used instead. After that search will call match (), and if a match is + * found, we can move to that place in the file by the functions find_x() + * and find_y () which will find display the match on the screen. + * Replacing can be done in two ways. A global replace (GR) or a line + * replace (LR). Both functions call change () with a message an a flag + * indicating global or line replacement. Change () will prompt for the + * expression and for the replacement. Every & in the replacement pattern + * means substitute the match instead. An & can be escaped by a \. When + * a match is found, the function substitute () will perform the + * substitution. + * + * 3.6 Miscellaneous commands. + * + * A few commands haven't be discussed yet. These are redraw the screen + * (RD) fork a shell (SH), print file status (FS), write file to disc + * (WT), insert a file at current position (IF), leave editor (XT) and + * visit another file (VI). The last two functions will check if the file + * has been modified. If it has, they will ask if you want to save the + * file by calling ask_save (). + * The function ESC () will repeat a command n times. It will prompt for + * the number. Aborting the loop can be done by sending the ^\ signal. + * + * 3.7 Utility functions. + * + * Several functions exists for internal use. First allocation routines: + * alloc (bytes) and newline () will return a pointer to free data space + * if the given size. If there is no more memory available, the function + * panic () is called. + * Signal handling: The only signal that can be send to mined is the + * SIGQUIT signal. This signal, functions as a general abort command. + * Mined will abort if the signal is given during the main loop. The + * function abort_mined () takes care of that. + * Panic () is a function with as argument a error message. It will print + * the message and the error number set by the kernel (errno) and will + * ask if the file must be saved or not. It resets the terminal + * (raw_mode ()) and exits. + * String handling routines like copy_string(to, from), length_of(string) + * and build_string (buffer, format, arg1, arg2, ...). The latter takes + * a description of the string out out the format field and puts the + * result in the buffer. (It works like printf (3), but then into a + * string). The functions status_line (string1, string2), error (string1, + * string2), clear_status () and bottom_line () all print information on + * the status line. + * Get_string (message, buffer) reads a string and getchar () reads one + * character from the terminal. + * Num_out ((long) number) prints the number into a 11 digit field + * without leading zero's. It returns a pointer to the resulting string. + * File_status () prints all file information on the status line. + * Set_cursor (x, y) prints the string to put the cursor at coordinates + * x and y. + * Output is done by four functions: writeline(fd,string), clear_buffer() + * write_char (fd, c) and flush_buffer (fd). Three defines are provided + * to write on filedescriptor STD_OUT (terminal) which is used normally: + * string_print (string), putchar (c) and flush (). All these functions + * use the global I/O buffer screen and the global index for this array + * called out_count. In this way I/O can be buffered, so that reads or + * writes can be done in blocks of SCREEN_SIZE size. + * The following functions all handle internal line maintenance. The + * function proceed (start_line, count) returns the count'th line after + * start_line. If count is negative, the count'th line before the + * start_line is returned. If header or tail is encountered then that + * will be returned. Display (x, y, start_line, count) displays count + * lines starting at coordinates [x, y] and beginning at start_line. If + * the header or tail is encountered, empty lines are displayed instead. + * The function reset (head_line, ny) reset top_line, last_y, bot_line, + * cur_line and y-coordinate. This is not a neat way to do the + * maintenance, but it sure saves a lot of code. It is usually used in + * combination with display (). + * Put_line(line, offset, clear_line), prints a line (skipping characters + * according to the line->shift_size field) until XBREAK - offset + * characters are printed or a '\n' is encountered. If clear_line is + * TRUE, spaces are printed until XBREAK - offset characters. + * Line_print (line) is a #define from put_line (line, 0, TRUE). + * Moving is done by the functions move_to (x, y), move_addres (address) + * and move (x, adress, y). This function is the most important one in + * mined. New_y must be between 0 and last_y, new_x can be about + * anything, address must be a pointer to an character on the current + * line (or y). Move_to () first adjust the y coordinate together with + * cur_line. If an address is given, it finds the corresponding + * x-coordinate. If an new x-coordinate was given, it will try to locate + * the corresponding character. After that it sets the shift_count field + * of cur_line to an apropiate number according to new_x. The only thing + * left to do now is to assign the new values to cur_line, cur_text, x + * and y. + * + * 4. Summary of commands. + * + * CURSOR MOTION + * up-arrow Move cursor 1 line up. At top of screen, reverse scroll + * down-arrow Move cursor 1 line down. At bottom, scroll forward. + * left-arrow Move cursor 1 character left or to end of previous line + * right-arrow Move cursor 1 character right or to start of next line + * CTRL-A Move cursor to start of current line + * CTRL-Z Move cursor to end of current line + * CTRL-^ Move cursor to top of screen + * CTRL-_ Move cursor to bottom of screen + * CTRL-F Forward to start of next word (even to next line) + * CTRL-B Backward to first character of previous word + * + * SCREEN MOTION + * Home key Move cursor to first character of file + * End key Move cursor to last character of file + * PgUp Scroll backward 1 page. Bottom line becomes top line + * PgD Scroll backward 1 page. Top line becomes bottom line + * CTRL-D Scroll screen down one line (reverse scroll) + * CTRL-U Scroll screen up one line (forward scroll) + * + * MODIFYING TEXT + * ASCII char Self insert character at cursor + * tab Insert tab at cursor + * backspace Delete the previous char (left of cursor), even line feed + * Del Delete the character under the cursor + * CTRL-N Delete next word + * CTRL-P Delete previous word + * CTRL-O Insert line feed at cursor and back up 1 character + * CTRL-T Delete tail of line (cursor to end); if empty, delete line + * CTRL-@ Set the mark (remember the current location) + * CTRL-K Delete text from the mark to current position save on file + * CTRL-C Save the text from the mark to the current position + * CTRL-Y Insert the contents of the save file at current position + * CTRL-Q Insert the contents of the save file into a new file + * CTRL-G Insert a file at the current position + * + * MISCELLANEOUS + * CTRL-E Erase and redraw the screen + * CTRL-V Visit file (read a new file); complain if old one changed + * CTRL-W Write the current file back to the disk + * numeric + Search forward (prompt for regular expression) + * numeric - Search backward (prompt for regular expression) + * numeric 5 Print the current status of the file + * CTRL-R (Global) Replace str1 by str2 (prompts for each string) + * CTRL-L (Line) Replace string1 by string2 + * CTRL-S Fork off a shell and wait for it to finish + * CTRL-X EXIT (prompt if file modified) + * CTRL-] Go to a line. Prompts for linenumber + * CTRL-\ Abort whatever editor was doing and start again + * escape key Repeat a command count times; (prompts for count) + */ + +/* ======================================================================== * + * Utilities * + * ======================================================================== */ + +#include "mined.h" +#include <signal.h> +#include <termios.h> +#include <limits.h> +#include <errno.h> +#include <sys/wait.h> +#include <sys/ioctl.h> +#if __STDC__ +#include <stdarg.h> +#else +#include <varargs.h> +#endif + +extern int errno; +int ymax = YMAX; +int screenmax = SCREENMAX; + + +/* + * Print file status. + */ +void FS() +{ + fstatus(file_name[0] ? "" : "[buffer]", -1L); +} + +/* + * Visit (edit) another file. If the file has been modified, ask the user if + * he wants to save it. + */ +void VI() +{ + char new_file[LINE_LEN]; /* Buffer to hold new file name */ + + if (modified == TRUE && ask_save() == ERRORS) + return; + +/* Get new file name */ + if (get_file("Visit file:", new_file) == ERRORS) + return; + +/* Free old linked list, initialize global variables and load new file */ + initialize(); +#ifdef UNIX + tputs(CL, 0, _putchar); +#else + string_print (enter_string); +#endif /* UNIX */ + load_file(new_file[0] == '\0' ? NIL_PTR : new_file); +} + +/* + * Write file in core to disc. + */ +int WT() +{ + register LINE *line; + register long count = 0L; /* Nr of chars written */ + char file[LINE_LEN]; /* Buffer for new file name */ + int fd; /* Filedescriptor of file */ + + if (modified == FALSE) { + error ("Write not necessary.", NIL_PTR); + return FINE; + } + +/* Check if file_name is valid and if file can be written */ + if (file_name[0] == '\0' || writable == FALSE) { + if (get_file("Enter file name:", file) != FINE) + return ERRORS; + copy_string(file_name, file); /* Save file name */ + } + if ((fd = creat(file_name, 0644)) < 0) { /* Empty file */ + error("Cannot create ", file_name); + writable = FALSE; + return ERRORS; + } + else + writable = TRUE; + + clear_buffer(); + + status_line("Writing ", file_name); + for (line = header->next; line != tail; line = line->next) { + if (line->shift_count & DUMMY) { + if (line->next == tail && line->text[0] == '\n') + continue; + } + if (writeline(fd, line->text) == ERRORS) { + count = -1L; + break; + } + count += (long) length_of(line->text); + } + + if (count > 0L && flush_buffer(fd) == ERRORS) + count = -1L; + + (void) close(fd); + + if (count == -1L) + return ERRORS; + + modified = FALSE; + rpipe = FALSE; /* File name is now assigned */ + +/* Display how many chars (and lines) were written */ + fstatus("Wrote", count); + return FINE; +} + +/* Call WT and discard value returned. */ +void XWT() +{ + (void) WT(); +} + + + +/* + * Call an interactive shell. + */ +void SH() +{ + register int w; + int pid, status; + char *shell; + + if ((shell = getenv("SHELL")) == NIL_PTR) shell = "/bin/sh"; + + switch (pid = fork()) { + case -1: /* Error */ + error("Cannot fork.", NIL_PTR); + return; + case 0: /* This is the child */ + set_cursor(0, ymax); + putchar('\n'); + flush(); + raw_mode(OFF); + if (rpipe) { /* Fix stdin */ + close (0); + if (open("/dev/tty", 0) < 0) + exit (126); + } + execl(shell, shell, (char *) 0); + exit(127); /* Exit with 127 */ + default : /* This is the parent */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + do { + w = wait(&status); + } while (w != -1 && w != pid); + } + + raw_mode(ON); + RD(); + + if ((status >> 8) == 127) /* Child died with 127 */ + error("Cannot exec ", shell); + else if ((status >> 8) == 126) + error("Cannot open /dev/tty as fd #0", NIL_PTR); +} + +/* + * Proceed returns the count'th line after `line'. When count is negative + * it returns the count'th line before `line'. When the next (previous) + * line is the tail (header) indicating EOF (tof) it stops. + */ +LINE *proceed(line, count) +register LINE *line; +register int count; +{ + if (count < 0) + while (count++ < 0 && line != header) + line = line->prev; + else + while (count-- > 0 && line != tail) + line = line->next; + return line; +} + +/* + * Show concatenation of s1 and s2 on the status line (bottom of screen) + * If revfl is TRUE, turn on reverse video on both strings. Set stat_visible + * only if bottom_line is visible. + */ +int bottom_line(revfl, s1, s2, inbuf, statfl) +FLAG revfl; +char *s1, *s2; +char *inbuf; +FLAG statfl; +{ + int ret = FINE; + char buf[LINE_LEN]; + register char *p = buf; + + *p++ = ' '; + if (s1 != NIL_PTR) + while (*p = *s1++) + p++; + if (s2 != NIL_PTR) + while (*p = *s2++) + p++; + *p++ = ' '; + *p++ = 0; + + if (revfl == ON && stat_visible == TRUE) + clear_status (); + set_cursor(0, ymax); + if (revfl == ON) { /* Print rev. start sequence */ +#ifdef UNIX + tputs(SO, 0, _putchar); +#else + string_print(rev_video); +#endif /* UNIX */ + stat_visible = TRUE; + } + else /* Used as clear_status() */ + stat_visible = FALSE; + + string_print(buf); + + if (inbuf != NIL_PTR) + ret = input(inbuf, statfl); + + /* Print normal video */ +#ifdef UNIX + tputs(SE, 0, _putchar); + tputs(CE, 0, _putchar); +#else + string_print(normal_video); + string_print(blank_line); /* Clear the rest of the line */ +#endif /* UNIX */ + if (inbuf != NIL_PTR) + set_cursor(0, ymax); + else + set_cursor(x, y); /* Set cursor back to old position */ + flush(); /* Perform the actual write */ + if (ret != FINE) + clear_status(); + return ret; +} + +/* + * Count_chars() count the number of chars that the line would occupy on the + * screen. Counting starts at the real x-coordinate of the line. + */ +int count_chars(line) +LINE *line; +{ + register int cnt = get_shift(line->shift_count) * -SHIFT_SIZE; + register char *textp = line->text; + +/* Find begin of line on screen */ + while (cnt < 0) { + if (is_tab(*textp++)) + cnt = tab(cnt); + else + cnt++; + } + +/* Count number of chars left */ + cnt = 0; + while (*textp != '\n') { + if (is_tab(*textp++)) + cnt = tab(cnt); + else + cnt++; + } + return cnt; +} + +/* + * Move to coordinates nx, ny at screen. The caller must check that scrolling + * is not needed. + * If new_x is lower than 0 or higher than XBREAK, move_to() will check if + * the line can be shifted. If it can it sets(or resets) the shift_count field + * of the current line accordingly. + * Move also sets cur_text to the right char. + * If we're moving to the same x coordinate, try to move the the x-coordinate + * used on the other previous call. + */ +void move(new_x, new_address, new_y) +register int new_x; +int new_y; +char *new_address; +{ + register LINE *line = cur_line; /* For building new cur_line */ + int shift = 0; /* How many shifts to make */ + static int rel_x = 0; /* Remember relative x position */ + int tx = x; + +/* Check for illegal values */ + if (new_y < 0 || new_y > last_y) + return; + +/* Adjust y-coordinate and cur_line */ + if (new_y < y) + while (y != new_y) { + y--; + line = line->prev; + } + else + while (y != new_y) { + y++; + line = line->next; + } + +/* Set or unset relative x-coordinate */ + if (new_address == NIL_PTR) { + new_address = find_address(line, (new_x == x) ? rel_x : new_x , &tx); + if (new_x != x) + rel_x = tx; + new_x = tx; + } + else + rel_x = new_x = find_x(line, new_address); + +/* Adjust shift_count if new_x lower than 0 or higher than XBREAK */ + if (new_x < 0 || new_x >= XBREAK) { + if (new_x > XBREAK || (new_x == XBREAK && *new_address != '\n')) + shift = (new_x - XBREAK) / SHIFT_SIZE + 1; + else { + shift = new_x / SHIFT_SIZE; + if (new_x % SHIFT_SIZE) + shift--; + } + + if (shift != 0) { + line->shift_count += shift; + new_x = find_x(line, new_address); + set_cursor(0, y); + line_print(line); + rel_x = new_x; + } + } + +/* Assign and position cursor */ + x = new_x; + cur_text = new_address; + cur_line = line; + set_cursor(x, y); +} + +/* + * Find_x() returns the x coordinate belonging to address. + * (Tabs are expanded). + */ +int find_x(line, address) +LINE *line; +char *address; +{ + register char *textp = line->text; + register int nx = get_shift(line->shift_count) * -SHIFT_SIZE; + + while (textp != address && *textp != '\0') { + if (is_tab(*textp++)) /* Expand tabs */ + nx = tab(nx); + else + nx++; + } + return nx; +} + +/* + * Find_address() returns the pointer in the line with offset x_coord. + * (Tabs are expanded). + */ +char *find_address(line, x_coord, old_x) +LINE *line; +int x_coord; +int *old_x; +{ + register char *textp = line->text; + register int tx = get_shift(line->shift_count) * -SHIFT_SIZE; + + while (tx < x_coord && *textp != '\n') { + if (is_tab(*textp)) { + if (*old_x - x_coord == 1 && tab(tx) > x_coord) + break; /* Moving left over tab */ + else + tx = tab(tx); + } + else + tx++; + textp++; + } + + *old_x = tx; + return textp; +} + +/* + * Length_of() returns the number of characters int the string `string' + * excluding the '\0'. + */ +int length_of(string) +register char *string; +{ + register int count = 0; + + if (string != NIL_PTR) { + while (*string++ != '\0') + count++; + } + return count; +} + +/* + * Copy_string() copies the string `from' into the string `to'. `To' must be + * long enough to hold `from'. + */ +void copy_string(to, from) +register char *to; +register char *from; +{ + while (*to++ = *from++) + ; +} + +/* + * Reset assigns bot_line, top_line and cur_line according to `head_line' + * which must be the first line of the screen, and an y-coordinate, + * which will be the current y-coordinate (if it isn't larger than last_y) + */ +void reset(head_line, screen_y) +LINE *head_line; +int screen_y; +{ + register LINE *line; + + top_line = line = head_line; + +/* Search for bot_line (might be last line in file) */ + for (last_y = 0; last_y < nlines - 1 && last_y < screenmax + && line->next != tail; last_y++) + line = line->next; + + bot_line = line; + y = (screen_y > last_y) ? last_y : screen_y; + +/* Set cur_line according to the new y value */ + cur_line = proceed(top_line, y); +} + +/* + * Set cursor at coordinates x, y. + */ +void set_cursor(nx, ny) +int nx, ny; +{ +#ifdef UNIX + extern char *tgoto(); + + tputs(tgoto(CM, nx, ny), 0, _putchar); +#else + char text_buffer[10]; + + build_string(text_buffer, pos_string, ny+1, nx+1); + string_print(text_buffer); +#endif /* UNIX */ +} + +/* + * Routine to open terminal when mined is used in a pipeline. + */ +void open_device() +{ + if ((input_fd = open("/dev/tty", 0)) < 0) + panic("Cannot open /dev/tty for read"); +} + +/* + * Getchar() reads one character from the terminal. The character must be + * masked with 0377 to avoid sign extension. + */ +int getchar() +{ +#ifdef UNIX + return (_getchar() & 0377); +#else + char c; + + if (read(input_fd, &c, 1) != 1 && quit == FALSE) + panic("Can't read one char from fd #0"); + + return c & 0377; +#endif /* UNIX */ +} + +/* + * Display() shows count lines on the terminal starting at the given + * coordinates. When the tail of the list is encountered it will fill the + * rest of the screen with blank_line's. + * When count is negative, a backwards print from `line' will be done. + */ +void display(x_coord, y_coord, line, count) +int x_coord, y_coord; +register LINE *line; +register int count; +{ + set_cursor(x_coord, y_coord); + +/* Find new startline if count is negative */ + if (count < 0) { + line = proceed(line, count); + count = -count; + } + +/* Print the lines */ + while (line != tail && count-- >= 0) { + line_print(line); + line = line->next; + } + +/* Print the blank lines (if any) */ + if (loading == FALSE) { + while (count-- >= 0) { +#ifdef UNIX + tputs(CE, 0, _putchar); +#else + string_print(blank_line); +#endif /* UNIX */ + putchar('\n'); + } + } +} + +/* + * Write_char does a buffered output. + */ +int write_char(fd, c) +int fd; +char c; +{ + screen [out_count++] = c; + if (out_count == SCREEN_SIZE) /* Flush on SCREEN_SIZE chars */ + return flush_buffer(fd); + return FINE; +} + +/* + * Writeline writes the given string on the given filedescriptor. + */ +int writeline(fd, text) +register int fd; +register char *text; +{ + while(*text) + if (write_char(fd, *text++) == ERRORS) + return ERRORS; + return FINE; +} + +/* + * Put_line print the given line on the standard output. If offset is not zero + * printing will start at that x-coordinate. If the FLAG clear_line is TRUE, + * then (screen) line will be cleared when the end of the line has been + * reached. + */ +void put_line(line, offset, clear_line) +LINE *line; /* Line to print */ +int offset; /* Offset to start */ +FLAG clear_line; /* Clear to eoln if TRUE */ +{ + register char *textp = line->text; + register int count = get_shift(line->shift_count) * -SHIFT_SIZE; + int tab_count; /* Used in tab expansion */ + +/* Skip all chars as indicated by the offset and the shift_count field */ + while (count < offset) { + if (is_tab(*textp++)) + count = tab(count); + else + count++; + } + + while (*textp != '\n' && count < XBREAK) { + if (is_tab(*textp)) { /* Expand tabs to spaces */ + tab_count = tab(count); + while (count < XBREAK && count < tab_count) { + count++; + putchar(' '); + } + textp++; + } + else { + if (*textp >= '\01' && *textp <= '\037') { +#ifdef UNIX + tputs(SO, 0, _putchar); +#else + string_print (rev_video); +#endif /* UNIX */ + putchar(*textp++ + '\100'); +#ifdef UNIX + tputs(SE, 0, _putchar); +#else + string_print (normal_video); +#endif /* UNIX */ + } + else + putchar(*textp++); + count++; + } + } + +/* If line is longer than XBREAK chars, print the shift_mark */ + if (count == XBREAK && *textp != '\n') + putchar(textp[1]=='\n' ? *textp : SHIFT_MARK); + +/* Clear the rest of the line is clear_line is TRUE */ + if (clear_line == TRUE) { +#ifdef UNIX + tputs(CE, 0, _putchar); +#else + string_print(blank_line); +#endif /* UNIX */ + putchar('\n'); + } +} + +/* + * Flush the I/O buffer on filedescriptor fd. + */ +int flush_buffer(fd) +int fd; +{ + if (out_count <= 0) /* There is nothing to flush */ + return FINE; +#ifdef UNIX + if (fd == STD_OUT) { + printf("%.*s", out_count, screen); + _flush(); + } + else +#endif /* UNIX */ + if (write(fd, screen, out_count) != out_count) { + bad_write(fd); + return ERRORS; + } + clear_buffer(); /* Empty buffer */ + return FINE; +} + +/* + * Bad_write() is called when a write failed. Notify the user. + */ +void bad_write(fd) +int fd; +{ + if (fd == STD_OUT) /* Cannot write to terminal? */ + exit(1); + + clear_buffer(); + build_string(text_buffer, "Command aborted: %s (File incomplete)", + (errno == ENOSPC || errno == -ENOSPC) ? + "No space on device" : "Write error"); + error(text_buffer, NIL_PTR); +} + +/* + * Catch the SIGQUIT signal (^\) send to mined. It turns on the quitflag. + */ +void catch(sig) +int sig; +{ +/* Reset the signal */ + signal(SIGQUIT, catch); + quit = TRUE; +} + +/* + * Abort_mined() will leave mined. Confirmation is asked first. + */ +void abort_mined() +{ + quit = FALSE; + +/* Ask for confirmation */ + status_line("Really abort? ", NIL_PTR); + if (getchar() != 'y') { + clear_status(); + return; + } + +/* Reset terminal */ + raw_mode(OFF); + set_cursor(0, ymax); + putchar('\n'); + flush(); +#ifdef UNIX + abort(); +#else + exit(1); +#endif /* UNIX */ +} + +#define UNDEF _POSIX_VDISABLE + +/* + * Set and reset tty into CBREAK or old mode according to argument `state'. It + * also sets all signal characters (except for ^\) to UNDEF. ^\ is caught. + */ +void raw_mode(state) +FLAG state; +{ + static struct termios old_tty; + static struct termios new_tty; + + if (state == OFF) { + tcsetattr(input_fd, TCSANOW, &old_tty); + return; + } + +/* Save old tty settings */ + tcgetattr(input_fd, &old_tty); + +/* Set tty to CBREAK mode */ + tcgetattr(input_fd, &new_tty); + new_tty.c_lflag &= ~(ICANON|ECHO|ECHONL); + new_tty.c_iflag &= ~(IXON|IXOFF); + +/* Unset signal chars, leave only SIGQUIT set to ^\ */ + new_tty.c_cc[VINTR] = new_tty.c_cc[VSUSP] = UNDEF; + new_tty.c_cc[VQUIT] = '\\' & 037; + signal(SIGQUIT, catch); /* Which is caught */ + + tcsetattr(input_fd, TCSANOW, &new_tty); +} + +/* + * Panic() is called with an error number and a message. It is called when + * something unrecoverable has happened. + * It writes the message to the terminal, resets the tty and exits. + * Ask the user if he wants to save his file. + */ +void panic(message) +register char *message; +{ + extern char yank_file[]; + +#ifdef UNIX + tputs(CL, 0, _putchar); + build_string(text_buffer, "%s\nError code %d\n", message, errno); +#else + build_string(text_buffer, "%s%s\nError code %d\n", enter_string, message, errno); +#endif /* UNIX */ + (void) write(STD_OUT, text_buffer, length_of(text_buffer)); + + if (loading == FALSE) + XT(); /* Check if file can be saved */ + else + (void) unlink(yank_file); + raw_mode(OFF); + +#ifdef UNIX + abort(); +#else + exit(1); +#endif /* UNIX */ +} + +char *alloc(bytes) +int bytes; +{ + char *p; + + p = malloc((unsigned) bytes); + if (p == NIL_PTR) { + if (loading == TRUE) + panic("File too big."); + panic("Out of memory."); + } + return(p); +} + +void free_space(p) +char *p; +{ + free(p); +} + +/* ======================================================================== * + * Main loops * + * ======================================================================== */ + +/* The mapping between input codes and functions. */ + +void (*key_map[256])() = { /* map ASCII characters to functions */ + /* 000-017 */ MA, BL, MP, YA, SD, RD, MN, IF, DPC, S, S, DT, LR, S, DNW,LIB, + /* 020-037 */ DPW, WB, GR, SH, DLN, SU, VI, XWT, XT, PT, EL, ESC, I, GOTO, + HIGH, LOW, + /* 040-057 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, + /* 060-077 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, + /* 100-117 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, + /* 120-137 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, + /* 140-157 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, + /* 160-177 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, DCC, + /* 200-217 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, + /* 220-237 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, + /* 240-257 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, + /* 260-277 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, + /* 300-317 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, + /* 320-337 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, + /* 340-357 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, + /* 360-377 */ S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, +}; + +int nlines; /* Number of lines in file */ +LINE *header; /* Head of line list */ +LINE *tail; /* Last line in line list */ +LINE *cur_line; /* Current line in use */ +LINE *top_line; /* First line of screen */ +LINE *bot_line; /* Last line of screen */ +char *cur_text; /* Current char on current line in use */ +int last_y; /* Last y of screen. Usually SCREENMAX */ +char screen[SCREEN_SIZE]; /* Output buffer for "writes" and "reads" */ + +int x, y; /* x, y coordinates on screen */ +FLAG modified = FALSE; /* Set when file is modified */ +FLAG stat_visible; /* Set if status_line is visible */ +FLAG writable; /* Set if file cannot be written */ +FLAG loading; /* Set if we are loading a file. */ +FLAG quit = FALSE; /* Set when quit character is typed */ +FLAG rpipe = FALSE; /* Set if file should be read from stdin */ +int input_fd = 0; /* Fd for command input */ +int out_count; /* Index in output buffer */ +char file_name[LINE_LEN]; /* Name of file in use */ +char text_buffer[MAX_CHARS]; /* Buffer for modifying text */ + +/* Escape sequences. */ +#ifdef UNIX +char *CE, *VS, *SO, *SE, *CL, *AL, *CM; +#else +char *enter_string = "\033[H\033[J"; /* String printed on entering mined */ +char *pos_string = "\033[%d;%dH"; /* Absolute cursor position */ +char *rev_scroll = "\033M"; /* String for reverse scrolling */ +char *rev_video = "\033[7m"; /* String for starting reverse video */ +char *normal_video = "\033[m"; /* String for leaving reverse video */ +char *blank_line = "\033[K"; /* Clear line to end */ +#endif /* UNIX */ + +/* + * Yank variables. + */ +FLAG yank_status = NOT_VALID; /* Status of yank_file */ +char yank_file[] = "/tmp/mined.XXXXXX"; +long chars_saved; /* Nr of chars in buffer */ + +/* + * Initialize is called when a another file is edited. It free's the allocated + * space and sets modified back to FALSE and fixes the header/tail pointer. + */ +void initialize() +{ + register LINE *line, *next_line; + +/* Delete the whole list */ + for (line = header->next; line != tail; line = next_line) { + next_line = line->next; + free_space(line->text); + free_space((char*)line); + } + +/* header and tail should point to itself */ + line->next = line->prev = line; + x = y = 0; + rpipe = modified = FALSE; +} + +/* + * Basename() finds the absolute name of the file out of a given path_name. + */ +char *basename(path) +char *path; +{ + register char *ptr = path; + register char *last = NIL_PTR; + + while (*ptr != '\0') { + if (*ptr == '/') + last = ptr; + ptr++; + } + if (last == NIL_PTR) + return path; + if (*(last + 1) == '\0') { /* E.g. /usr/tmp/pipo/ */ + *last = '\0'; + return basename(path);/* Try again */ + } + return last + 1; +} + +/* + * Load_file loads the file `file' into core. If file is a NIL_PTR or the file + * couldn't be opened, just some initializations are done, and a line consisting + * of a `\n' is installed. + */ +void load_file(file) +char *file; +{ + register LINE *line = header; + register int len; + long nr_of_chars = 0L; + int fd = -1; /* Filedescriptor for file */ + + nlines = 0; /* Zero lines to start with */ + +/* Open file */ + writable = TRUE; /* Benefit of the doubt */ + if (file == NIL_PTR) { + if (rpipe == FALSE) + status_line("No file.", NIL_PTR); + else { + fd = 0; + file = "standard input"; + } + file_name[0] = '\0'; + } + else { + copy_string(file_name, file); /* Save file name */ + if (access(file, 0) < 0) /* Cannot access file. */ + status_line("New file ", file); + else if ((fd = open(file, 0)) < 0) + status_line("Cannot open ", file); + else if (access(file, 2) != 0) /* Set write flag */ + writable = FALSE; + } + +/* Read file */ + loading = TRUE; /* Loading file, so set flag */ + + if (fd >= 0) { + status_line("Reading ", file); + while ((len = get_line(fd, text_buffer)) != ERRORS) { + line = line_insert(line, text_buffer, len); + nr_of_chars += (long) len; + } + if (nlines == 0) /* The file was empty! */ + line = line_insert(line, "\n", 1); + clear_buffer(); /* Clear output buffer */ + cur_line = header->next; + fstatus("Read", nr_of_chars); + (void) close(fd); /* Close file */ + } + else /* Just install a "\n" */ + (void) line_insert(line, "\n", 1); + + reset(header->next, 0); /* Initialize pointers */ + +/* Print screen */ + display (0, 0, header->next, last_y); + move_to (0, 0); + flush(); /* Flush buffer */ + loading = FALSE; /* Stop loading, reset flag */ +} + + +/* + * Get_line reads one line from filedescriptor fd. If EOF is reached on fd, + * get_line() returns ERRORS, else it returns the length of the string. + */ +int get_line(fd, buffer) +int fd; +register char *buffer; +{ + static char *last = NIL_PTR; + static char *current = NIL_PTR; + static int read_chars; + register char *cur_pos = current; + char *begin = buffer; + + do { + if (cur_pos == last) { + if ((read_chars = read(fd, screen, SCREEN_SIZE)) <= 0) + break; + last = &screen[read_chars]; + cur_pos = screen; + } + if (*cur_pos == '\0') + *cur_pos = ' '; + } while ((*buffer++ = *cur_pos++) != '\n'); + + current = cur_pos; + if (read_chars <= 0) { + if (buffer == begin) + return ERRORS; + if (*(buffer - 1) != '\n') + if (loading == TRUE) /* Add '\n' to last line of file */ + *buffer++ = '\n'; + else { + *buffer = '\0'; + return NO_LINE; + } + } + + *buffer = '\0'; + return buffer - begin; +} + +/* + * Install_line installs the buffer into a LINE structure It returns a pointer + * to the allocated structure. + */ +LINE *install_line(buffer, length) +char *buffer; +int length; +{ + register LINE *new_line = (LINE *) alloc(sizeof(LINE)); + + new_line->text = alloc(length + 1); + new_line->shift_count = 0; + copy_string(new_line->text, buffer); + + return new_line; +} + +void main(argc, argv) +int argc; +char *argv[]; +{ +/* mined is the Minix editor. */ + + register int index; /* Index in key table */ + struct winsize winsize; + +#ifdef UNIX + get_term(); + tputs(VS, 0, _putchar); + tputs(CL, 0, _putchar); +#else + string_print(enter_string); /* Hello world */ +#endif /* UNIX */ + if (ioctl(STD_OUT, TIOCGWINSZ, &winsize) == 0 && winsize.ws_row != 0) { + ymax = winsize.ws_row - 1; + screenmax = ymax - 1; + } + + if (!isatty(0)) { /* Reading from pipe */ + if (argc != 1) { + write(2, "Cannot find terminal.\n", 22); + exit (1); + } + rpipe = TRUE; + modified = TRUE; /* Set modified so he can write */ + open_device(); + } + + raw_mode(ON); /* Set tty to appropriate mode */ + + header = tail = (LINE *) alloc(sizeof(LINE)); /* Make header of list*/ + header->text = NIL_PTR; + header->next = tail->prev = header; + +/* Load the file (if any) */ + if (argc < 2) + load_file(NIL_PTR); + else { + (void) get_file(NIL_PTR, argv[1]); /* Truncate filename */ + load_file(argv[1]); + } + + /* Main loop of the editor. */ + for (;;) { + index = getchar(); + if (stat_visible == TRUE) + clear_status(); + if (quit == TRUE) + abort_mined(); + else { /* Call the function for this key */ + (*key_map[index])(index); + flush(); /* Flush output (if any) */ + if (quit == TRUE) + quit = FALSE; + } + } + /* NOTREACHED */ +} + +/* ======================================================================== * + * Miscellaneous * + * ======================================================================== */ + +/* + * Redraw the screen + */ +void RD() +{ +/* Clear screen */ +#ifdef UNIX + tputs(VS, 0, _putchar); + tputs(CL, 0, _putchar); +#else + string_print(enter_string); +#endif /* UNIX */ + +/* Print first page */ + display(0, 0, top_line, last_y); + +/* Clear last line */ + set_cursor(0, ymax); +#ifdef UNIX + tputs(CE, 0, _putchar); +#else + string_print(blank_line); +#endif /* UNIX */ + move_to(x, y); +} + +/* + * Ignore this keystroke. + */ +void I() +{ +} + +/* + * Leave editor. If the file has changed, ask if the user wants to save it. + */ +void XT() +{ + if (modified == TRUE && ask_save() == ERRORS) + return; + + raw_mode(OFF); + set_cursor(0, ymax); + putchar('\n'); + flush(); + (void) unlink(yank_file); /* Might not be necessary */ + exit(0); +} + +void (*escfunc(c))() +int c; +{ +#if (CHIP == M68000) +#ifndef COMPAT + int ch; +#endif +#endif + if (c == '[') { + /* Start of ASCII escape sequence. */ + c = getchar(); +#if (CHIP == M68000) +#ifndef COMPAT + if ((c >= '0') && (c <= '9')) ch = getchar(); + /* ch is either a tilde or a second digit */ +#endif +#endif + switch (c) { + case 'H': return(HO); + case 'A': return(UP); + case 'B': return(DN); + case 'C': return(RT); + case 'D': return(LF); +#if (CHIP == M68000) +#ifndef COMPAT + /* F1 = ESC [ 1 ~ */ + /* F2 = ESC [ 2 ~ */ + /* F3 = ESC [ 3 ~ */ + /* F4 = ESC [ 4 ~ */ + /* F5 = ESC [ 5 ~ */ + /* F6 = ESC [ 6 ~ */ + /* F7 = ESC [ 17 ~ */ + /* F8 = ESC [ 18 ~ */ + case '1': + switch (ch) { + case '~': return(SF); + case '7': (void) getchar(); return(MA); + case '8': (void) getchar(); return(CTL); + } + case '2': return(SR); + case '3': return(PD); + case '4': return(PU); + case '5': return(FS); + case '6': return(EF); +#endif +#endif +#if (CHIP == INTEL) + case 'G': return(FS); + case 'S': return(SR); + case 'T': return(SF); + case 'U': return(PD); + case 'V': return(PU); + case 'Y': return(EF); +#endif + } + return(I); + } +#if (CHIP == M68000) +#ifdef COMPAT + if (c == 'O') { + /* Start of ASCII function key escape sequence. */ + switch (getchar()) { + case 'P': return(SF); + case 'Q': return(SR); + case 'R': return(PD); + case 'S': return(PU); + case 'T': return(FS); + case 'U': return(EF); + case 'V': return(MA); + case 'W': return(CTL); + } + } +#endif +#endif + return(I); +} + +/* + * ESC() wants a count and a command after that. It repeats the + * command count times. If a ^\ is given during repeating, stop looping and + * return to main loop. + */ +void ESC() +{ + register int count = 0; + register void (*func)(); + int index; + + index = getchar(); + while (index >= '0' && index <= '9' && quit == FALSE) { + count *= 10; + count += index - '0'; + index = getchar(); + } + if (count == 0) { + count = 1; + func = escfunc(index); + } else { + func = key_map[index]; + if (func == ESC) + func = escfunc(getchar()); + } + + if (func == I) { /* Function assigned? */ + clear_status(); + return; + } + + while (count-- > 0 && quit == FALSE) { + if (stat_visible == TRUE) + clear_status(); + (*func)(index); + flush(); + } + + if (quit == TRUE) /* Abort has been given */ + error("Aborted", NIL_PTR); +} + +/* + * Ask the user if he wants to save his file or not. + */ +int ask_save() +{ + register int c; + + status_line(file_name[0] ? basename(file_name) : "[buffer]" , + " has been modified. Save? (y/n)"); + + while((c = getchar()) != 'y' && c != 'n' && quit == FALSE) { + ring_bell(); + flush(); + } + + clear_status(); + + if (c == 'y') + return WT(); + + if (c == 'n') + return FINE; + + quit = FALSE; /* Abort character has been given */ + return ERRORS; +} + +/* + * Line_number() finds the line number we're on. + */ +int line_number() +{ + register LINE *line = header->next; + register int count = 1; + + while (line != cur_line) { + count++; + line = line->next; + } + + return count; +} + +/* + * Display a line telling how many chars and lines the file contains. Also tell + * whether the file is readonly and/or modified. + */ +void file_status(message, count, file, lines, writefl, changed) +char *message; +register long count; /* Contains number of characters in file */ +char *file; +int lines; +FLAG writefl, changed; +{ + register LINE *line; + char msg[LINE_LEN + 40];/* Buffer to hold line */ + char yank_msg[LINE_LEN];/* Buffer for msg of yank_file */ + + if (count < 0) /* Not valid. Count chars in file */ + for (line = header->next; line != tail; line = line->next) + count += length_of(line->text); + + if (yank_status != NOT_VALID) /* Append buffer info */ + build_string(yank_msg, " Buffer: %D char%s.", chars_saved, + (chars_saved == 1L) ? "" : "s"); + else + yank_msg[0] = '\0'; + + build_string(msg, "%s %s%s%s %d line%s %D char%s.%s Line %d", message, + (rpipe == TRUE && *message != '[') ? "standard input" : basename(file), + (changed == TRUE) ? "*" : "", + (writefl == FALSE) ? " (Readonly)" : "", + lines, (lines == 1) ? "" : "s", + count, (count == 1L) ? "" : "s", + yank_msg, line_number()); + + if (length_of(msg) + 1 > LINE_LEN - 4) { + msg[LINE_LEN - 4] = SHIFT_MARK; /* Overflow on status line */ + msg[LINE_LEN - 3] = '\0'; + } + status_line(msg, NIL_PTR); /* Print the information */ +} + +/* + * Build_string() prints the arguments as described in fmt, into the buffer. + * %s indicates an argument string, %d indicated an argument number. + */ +#if __STDC__ +void build_string(char *buf, char *fmt, ...) +{ +#else +void build_string(buf, fmt, va_alist) +char *buf, *fmt; +va_dcl +{ +#endif + va_list argptr; + char *scanp; + +#if __STDC__ + va_start(argptr, fmt); +#else + va_start(argptr); +#endif + + while (*fmt) { + if (*fmt == '%') { + fmt++; + switch (*fmt++) { + case 's' : + scanp = va_arg(argptr, char *); + break; + case 'd' : + scanp = num_out((long) va_arg(argptr, int)); + break; + case 'D' : + scanp = num_out((long) va_arg(argptr, long)); + break; + default : + scanp = ""; + } + while (*buf++ = *scanp++) + ; + buf--; + } + else + *buf++ = *fmt++; + } + va_end(argptr); + *buf = '\0'; +} + +/* + * Output an (unsigned) long in a 10 digit field without leading zeros. + * It returns a pointer to the first digit in the buffer. + */ +char *num_out(number) +long number; +{ + static char num_buf[11]; /* Buffer to build number */ + register long digit; /* Next digit of number */ + register long pow = 1000000000L; /* Highest ten power of long */ + FLAG digit_seen = FALSE; + int i; + + for (i = 0; i < 10; i++) { + digit = number / pow; /* Get next digit */ + if (digit == 0L && digit_seen == FALSE && i != 9) + num_buf[i] = ' '; + else { + num_buf[i] = '0' + (char) digit; + number -= digit * pow; /* Erase digit */ + digit_seen = TRUE; + } + pow /= 10L; /* Get next digit */ + } + for (i = 0; num_buf[i] == ' '; i++) /* Skip leading spaces */ + ; + return (&num_buf[i]); +} + +/* + * Get_number() read a number from the terminal. The last character typed in is + * returned. ERRORS is returned on a bad number. The resulting number is put + * into the integer the arguments points to. + */ +int get_number(message, result) +char *message; +int *result; +{ + register int index; + register int count = 0; + + status_line(message, NIL_PTR); + + index = getchar(); + if (quit == FALSE && (index < '0' || index > '9')) { + error("Bad count", NIL_PTR); + return ERRORS; + } + +/* Convert input to a decimal number */ + while (index >= '0' && index <= '9' && quit == FALSE) { + count *= 10; + count += index - '0'; + index = getchar(); + } + + if (quit == TRUE) { + clear_status(); + return ERRORS; + } + + *result = count; + return index; +} + +/* + * Input() reads a string from the terminal. When the KILL character is typed, + * it returns ERRORS. + */ +int input(inbuf, clearfl) +char *inbuf; +FLAG clearfl; +{ + register char *ptr; + register char c; /* Character read */ + + ptr = inbuf; + + *ptr = '\0'; + while (quit == FALSE) { + flush(); + switch (c = getchar()) { + case '\b' : /* Erase previous char */ + if (ptr > inbuf) { + ptr--; +#ifdef UNIX + tputs(SE, 0, _putchar); +#else + string_print(normal_video); +#endif /* UNIX */ + if (is_tab(*ptr)) + string_print(" \b\b\b \b\b"); + else + string_print(" \b\b \b"); +#ifdef UNIX + tputs(SO, 0, _putchar); +#else + string_print(rev_video); +#endif /* UNIX */ + string_print(" \b"); + *ptr = '\0'; + } + else + ring_bell(); + break; + case '\n' : /* End of input */ + /* If inbuf is empty clear status_line */ + return (ptr == inbuf && clearfl == TRUE) ? NO_INPUT :FINE; + default : /* Only read ASCII chars */ + if ((c >= ' ' && c <= '~') || c == '\t') { + *ptr++ = c; + *ptr = '\0'; + if (c == '\t') + string_print("^I"); + else + putchar(c); + string_print(" \b"); + } + else + ring_bell(); + } + } + quit = FALSE; + return ERRORS; +} + +/* + * Get_file() reads a filename from the terminal. Filenames longer than + * FILE_LENGHT chars are truncated. + */ +int get_file(message, file) +char *message, *file; +{ + char *ptr; + int ret; + + if (message == NIL_PTR || (ret = get_string(message, file, TRUE)) == FINE) { + if (length_of((ptr = basename(file))) > NAME_MAX) + ptr[NAME_MAX] = '\0'; + } + return ret; +} + +/* ======================================================================== * + * UNIX I/O Routines * + * ======================================================================== */ + +#ifdef UNIX +#undef putchar + +int _getchar() +{ + char c; + + if (read(input_fd, &c, 1) != 1 && quit == FALSE) + panic ("Cannot read 1 byte from input"); + return c & 0377; +} + +void _flush() +{ + (void) fflush(stdout); +} + +void _putchar(c) +char c; +{ + (void) write_char(STD_OUT, c); +} + +void get_term() +{ + static char termbuf[50]; + extern char *tgetstr(), *getenv(); + char *loc = termbuf; + char entry[1024]; + + if (tgetent(entry, getenv("TERM")) <= 0) { + printf("Unknown terminal.\n"); + exit(1); + } + + AL = tgetstr("al", &loc); + CE = tgetstr("ce", &loc); + VS = tgetstr("vs", &loc); + CL = tgetstr("cl", &loc); + SO = tgetstr("so", &loc); + SE = tgetstr("se", &loc); + CM = tgetstr("cm", &loc); + ymax = tgetnum("li") - 1; + screenmax = ymax - 1; + + if (!CE || !SO || !SE || !CL || !AL || !CM) { + printf("Sorry, no mined on this type of terminal\n"); + exit(1); + } +} +#endif /* UNIX */ diff --git a/commands/mined/mined2.c b/commands/mined/mined2.c new file mode 100755 index 000000000..1b16d8797 --- /dev/null +++ b/commands/mined/mined2.c @@ -0,0 +1,1724 @@ +/* + * Part 2 of the mined editor. + */ + +/* ======================================================================== * + * Move Commands * + * ======================================================================== */ + +#include "mined.h" +#include <string.h> + +/* + * Move one line up. + */ +void UP() +{ + if (y == 0) { /* Top line of screen. Scroll one line */ + (void) reverse_scroll(); + move_to(x, y); + } + else /* Move to previous line */ + move_to(x, y - 1); +} + +/* + * Move one line down. + */ +void DN() +{ + if (y == last_y) { /* Last line of screen. Scroll one line */ + if (bot_line->next == tail && bot_line->text[0] != '\n') { + dummy_line(); /* Create new empty line */ + DN(); + return; + } + else { + (void) forward_scroll(); + move_to(x, y); + } + } + else /* Move to next line */ + move_to(x, y + 1); +} + +/* + * Move left one position. + */ +void LF() +{ + if (x == 0 && get_shift(cur_line->shift_count) == 0) {/* Begin of line */ + if (cur_line->prev != header) { + UP(); /* Move one line up */ + move_to(LINE_END, y); + } + } + else + move_to(x - 1, y); +} + +/* + * Move right one position. + */ +void RT() +{ + if (*cur_text == '\n') { + if (cur_line->next != tail) { /* Last char of file */ + DN(); /* Move one line down */ + move_to(LINE_START, y); + } + } + else + move_to(x + 1, y); +} + +/* + * Move to coordinates [0, 0] on screen. + */ +void HIGH() +{ + move_to(0, 0); +} + +/* + * Move to coordinates [0, YMAX] on screen. + */ +void LOW() +{ + move_to(0, last_y); +} + +/* + * Move to begin of line. + */ +void BL() +{ + move_to(LINE_START, y); +} + +/* + * Move to end of line. + */ +void EL() +{ + move_to(LINE_END, y); +} + +/* + * GOTO() prompts for a linenumber and moves to that line. + */ +void GOTO() +{ + int number; + LINE *line; + + if (get_number("Please enter line number.", &number) == ERRORS) + return; + + if (number <= 0 || (line = proceed(header->next, number - 1)) == tail) + error("Illegal line number: ", num_out((long) number)); + else + move_to(x, find_y(line)); +} + +/* + * Scroll forward one page or to eof, whatever comes first. (Bot_line becomes + * top_line of display.) Try to leave the cursor on the same line. If this is + * not possible, leave cursor on the line halfway the page. + */ +void PD() +{ + register int i; + + for (i = 0; i < screenmax; i++) + if (forward_scroll() == ERRORS) + break; /* EOF reached */ + if (y - i < 0) /* Line no longer on screen */ + move_to(0, screenmax >> 1); + else + move_to(0, y - i); +} + + +/* + * Scroll backwards one page or to top of file, whatever comes first. (Top_line + * becomes bot_line of display). The very bottom line (YMAX) is always blank. + * Try to leave the cursor on the same line. If this is not possible, leave + * cursor on the line halfway the page. + */ +void PU() +{ + register int i; + + for (i = 0; i < screenmax; i++) + if (reverse_scroll() == ERRORS) + break; /* Top of file reached */ + set_cursor(0, ymax); /* Erase very bottom line */ +#ifdef UNIX + tputs(CE, 0, _putchar); +#else + string_print(blank_line); +#endif /* UNIX */ + if (y + i > screenmax) /* line no longer on screen */ + move_to(0, screenmax >> 1); + else + move_to(0, y + i); +} + +/* + * Go to top of file, scrolling if possible, else redrawing screen. + */ +void HO() +{ + if (proceed(top_line, -screenmax) == header) + PU(); /* It fits. Let PU do it */ + else { + reset(header->next, 0);/* Reset top_line, etc. */ + RD(); /* Display full page */ + } + move_to(LINE_START, 0); +} + +/* + * Go to last line of file, scrolling if possible, else redrawing screen + */ +void EF() +{ + if (tail->prev->text[0] != '\n') + dummy_line(); + if (proceed(bot_line, screenmax) == tail) + PD(); /* It fits. Let PD do it */ + else { + reset(proceed(tail->prev, -screenmax), screenmax); + RD(); /* Display full page */ + } + move_to(LINE_START, last_y); +} + +/* + * Scroll one line up. Leave the cursor on the same line (if possible). + */ +void SU() +{ + if (top_line->prev == header) /* Top of file. Can't scroll */ + return; + + (void) reverse_scroll(); + set_cursor(0, ymax); /* Erase very bottom line */ +#ifdef UNIX + tputs(CE, 0, _putchar); +#else + string_print(blank_line); +#endif /* UNIX */ + move_to(x, (y == screenmax) ? screenmax : y + 1); +} + +/* + * Scroll one line down. Leave the cursor on the same line (if possible). + */ +void SD() +{ + if (forward_scroll() != ERRORS) + move_to(x, (y == 0) ? 0 : y - 1); + else + set_cursor(x, y); +} + +/* + * Perform a forward scroll. It returns ERRORS if we're at the last line of the + * file. + */ +int forward_scroll() +{ + if (bot_line->next == tail) /* Last line of file. No dice */ + return ERRORS; + top_line = top_line->next; + bot_line = bot_line->next; + cur_line = cur_line->next; + set_cursor(0, ymax); + line_print(bot_line); + + return FINE; +} + +/* + * Perform a backwards scroll. It returns ERRORS if we're at the first line + * of the file. + */ +int reverse_scroll() +{ + if (top_line->prev == header) + return ERRORS; /* Top of file. Can't scroll */ + + if (last_y != screenmax) /* Reset last_y if necessary */ + last_y++; + else + bot_line = bot_line->prev; /* Else adjust bot_line */ + top_line = top_line->prev; + cur_line = cur_line->prev; + +/* Perform the scroll */ + set_cursor(0, 0); +#ifdef UNIX + tputs(AL, 0, _putchar); +#else + string_print(rev_scroll); +#endif /* UNIX */ + set_cursor(0, 0); + line_print(top_line); + + return FINE; +} + +/* + * A word is defined as a number of non-blank characters separated by tabs + * spaces or linefeeds. + */ + +/* + * MP() moves to the start of the previous word. A word is defined as a + * number of non-blank characters separated by tabs spaces or linefeeds. + */ +void MP() +{ + move_previous_word(NO_DELETE); +} + +void move_previous_word(remove) +FLAG remove; +{ + register char *begin_line; + register char *textp; + char start_char = *cur_text; + char *start_pos = cur_text; + +/* Fist check if we're at the beginning of line. */ + if (cur_text == cur_line->text) { + if (cur_line->prev == header) + return; + start_char = '\0'; + } + + LF(); + + begin_line = cur_line->text; + textp = cur_text; + +/* Check if we're in the middle of a word. */ + if (!alpha(*textp) || !alpha(start_char)) { + while (textp != begin_line && (white_space(*textp) || *textp == '\n')) + textp--; + } + +/* Now we're at the end of previous word. Skip non-blanks until a blank comes */ + while (textp != begin_line && alpha(*textp)) + textp--; + +/* Go to the next char if we're not at the beginning of the line */ + if (textp != begin_line && *textp != '\n') + textp++; + +/* Find the x-coordinate of this address, and move to it */ + move_address(textp); + if (remove == DELETE) + delete(cur_line, textp, cur_line, start_pos); +} + +/* + * MN() moves to the start of the next word. A word is defined as a number of + * non-blank characters separated by tabs spaces or linefeeds. Always keep in + * mind that the pointer shouldn't pass the '\n'. + */ +void MN() +{ + move_next_word(NO_DELETE); +} + +void move_next_word(remove) +FLAG remove; +{ + register char *textp = cur_text; + +/* Move to the end of the current word. */ + while (*textp != '\n' && alpha(*textp)) + textp++; + +/* Skip all white spaces */ + while (*textp != '\n' && white_space(*textp)) + textp++; +/* If we're deleting. delete the text in between */ + if (remove == DELETE) { + delete(cur_line, cur_text, cur_line, textp); + return; + } + +/* If we're at end of line. move to the first word on the next line. */ + if (*textp == '\n' && cur_line->next != tail) { + DN(); + move_to(LINE_START, y); + textp = cur_text; + while (*textp != '\n' && white_space(*textp)) + textp++; + } + move_address(textp); +} + +/* ======================================================================== * + * Modify Commands * + * ======================================================================== */ + +/* + * DCC deletes the character under the cursor. If this character is a '\n' the + * current line is joined with the next one. + * If this character is the only character of the line, the current line will + * be deleted. + */ +void DCC() +{ + if (*cur_text == '\n') + delete(cur_line,cur_text, cur_line->next,cur_line->next->text); + else + delete(cur_line, cur_text, cur_line, cur_text + 1); +} + +/* + * DPC deletes the character on the left side of the cursor. If the cursor is + * at the beginning of the line, the last character if the previous line is + * deleted. + */ +void DPC() +{ + if (x == 0 && cur_line->prev == header) + return; /* Top of file */ + + LF(); /* Move one left */ + DCC(); /* Delete character under cursor */ +} + +/* + * DLN deletes all characters until the end of the line. If the current + * character is a '\n', then delete that char. + */ +void DLN() +{ + if (*cur_text == '\n') + DCC(); + else + delete(cur_line, cur_text, cur_line, cur_text + length_of(cur_text) -1); +} + +/* + * DNW() deletes the next word (as described in MN()) + */ +void DNW() +{ + if (*cur_text == '\n') + DCC(); + else + move_next_word(DELETE); +} + +/* + * DPW() deletes the next word (as described in MP()) + */ +void DPW() +{ + if (cur_text == cur_line->text) + DPC(); + else + move_previous_word(DELETE); +} + +/* + * Insert character `character' at current location. + */ +void S(character) +register char character; +{ + static char buffer[2]; + + buffer[0] = character; +/* Insert the character */ + if (insert(cur_line, cur_text, buffer) == ERRORS) + return; + +/* Fix screen */ + if (character == '\n') { + set_cursor(0, y); + if (y == screenmax) { /* Can't use display */ + line_print(cur_line); + (void) forward_scroll(); + } + else { + reset(top_line, y); /* Reset pointers */ + display(0, y, cur_line, last_y - y); + } + move_to(0, (y == screenmax) ? y : y + 1); + } + else if (x + 1 == XBREAK)/* If line must be shifted, just call move_to*/ + move_to(x + 1, y); + else { /* else display rest of line */ + put_line(cur_line, x, FALSE); + move_to(x + 1, y); + } +} + +/* + * CTL inserts a control-char at the current location. A message that this + * function is called is displayed at the status line. + */ +void CTL() +{ + register char ctrl; + + status_line("Enter control character.", NIL_PTR); + if ((ctrl = getchar()) >= '\01' && ctrl <= '\037') { + S(ctrl); /* Insert the char */ + clear_status(); + } + else + error ("Unknown control character", NIL_PTR); +} + +/* + * LIB insert a line at the current position and moves back to the end of + * the previous line. + */ +void LIB() +{ + S('\n'); /* Insert the line */ + UP(); /* Move one line up */ + move_to(LINE_END, y); /* Move to end of this line */ +} + +/* + * Line_insert() inserts a new line with text pointed to by `string'. + * It returns the address of the new line. + */ +LINE *line_insert(line, string, len) +register LINE *line; +char *string; +int len; +{ + register LINE *new_line; + +/* Allocate space for LINE structure and text */ + new_line = install_line(string, len); + +/* Install the line into the double linked list */ + new_line->prev = line; + new_line->next = line->next; + line->next = new_line; + new_line->next->prev = new_line; + +/* Increment nlines */ + nlines++; + + return new_line; +} + +/* + * Insert() insert the string `string' at the given line and location. + */ +int insert(line, location, string) +register LINE *line; +char *location, *string; +{ + register char *bufp = text_buffer; /* Buffer for building line */ + register char *textp = line->text; + + if (length_of(textp) + length_of(string) >= MAX_CHARS) { + error("Line too long", NIL_PTR); + return ERRORS; + } + + modified = TRUE; /* File has been modified */ + +/* Copy part of line until `location' has been reached */ + while (textp != location) + *bufp++ = *textp++; + +/* Insert string at this location */ + while (*string != '\0') + *bufp++ = *string++; + *bufp = '\0'; + + if (*(string - 1) == '\n') /* Insert a new line */ + (void) line_insert(line, location, length_of(location)); + else /* Append last part of line */ + copy_string(bufp, location); + +/* Install the new text in this line */ + free_space(line->text); + line->text = alloc(length_of(text_buffer) + 1); + copy_string(line->text, text_buffer); + + return FINE; +} + +/* + * Line_delete() deletes the argument line out of the line list. The pointer to + * the next line is returned. + */ +LINE *line_delete(line) +register LINE *line; +{ + register LINE *next_line = line->next; + +/* Delete the line */ + line->prev->next = line->next; + line->next->prev = line->prev; + +/* Free allocated space */ + free_space(line->text); + free_space((char*)line); + +/* Decrement nlines */ + nlines--; + + return next_line; +} + +/* + * Delete() deletes all the characters (including newlines) between the + * startposition and endposition and fixes the screen accordingly. It + * returns the number of lines deleted. + */ +void delete(start_line, start_textp, end_line, end_textp) +register LINE *start_line; +LINE *end_line; +char *start_textp, *end_textp; +{ + register char *textp = start_line->text; + register char *bufp = text_buffer; /* Storage for new line->text */ + LINE *line, *stop; + int line_cnt = 0; /* Nr of lines deleted */ + int count = 0; + int shift = 0; /* Used in shift calculation */ + int nx = x; + + modified = TRUE; /* File has been modified */ + +/* Set up new line. Copy first part of start line until start_position. */ + while (textp < start_textp) { + *bufp++ = *textp++; + count++; + } + +/* Check if line doesn't exceed MAX_CHARS */ + if (count + length_of(end_textp) >= MAX_CHARS) { + error("Line too long", NIL_PTR); + return; + } + +/* Copy last part of end_line if end_line is not tail */ + copy_string(bufp, (end_textp != NIL_PTR) ? end_textp : "\n"); + +/* Delete all lines between start and end_position (including end_line) */ + line = start_line->next; + stop = end_line->next; + while (line != stop && line != tail) { + line = line_delete(line); + line_cnt++; + } + +/* Check if last line of file should be deleted */ + if (end_textp == NIL_PTR && length_of(start_line->text) == 1 && nlines > 1) { + start_line = start_line->prev; + (void) line_delete(start_line->next); + line_cnt++; + } + else { /* Install new text */ + free_space(start_line->text); + start_line->text = alloc(length_of(text_buffer) + 1); + copy_string(start_line->text, text_buffer); + } + +/* Fix screen. First check if line is shifted. Perhaps we should shift it back*/ + if (get_shift(start_line->shift_count)) { + shift = (XBREAK - count_chars(start_line)) / SHIFT_SIZE; + if (shift > 0) { /* Shift line `shift' back */ + if (shift >= get_shift(start_line->shift_count)) + start_line->shift_count = 0; + else + start_line->shift_count -= shift; + nx += shift * SHIFT_SIZE;/* Reset x value */ + } + } + + if (line_cnt == 0) { /* Check if only one line changed */ + if (shift > 0) { /* Reprint whole line */ + set_cursor(0, y); + line_print(start_line); + } + else { /* Just display last part of line */ + set_cursor(x, y); + put_line(start_line, x, TRUE); + } + move_to(nx, y); /* Reset cur_text */ + return; + } + + shift = last_y; /* Save value */ + reset(top_line, y); + display(0, y, start_line, shift - y); + move_to((line_cnt == 1) ? nx : 0, y); +} + +/* ======================================================================== * + * Yank Commands * + * ======================================================================== */ + +LINE *mark_line; /* For marking position. */ +char *mark_text; +int lines_saved; /* Nr of lines in buffer */ + +/* + * PT() inserts the buffer at the current location. + */ +void PT() +{ + register int fd; /* File descriptor for buffer */ + + if ((fd = scratch_file(READ)) == ERRORS) + error("Buffer is empty.", NIL_PTR); + else { + file_insert(fd, FALSE);/* Insert the buffer */ + (void) close(fd); + } +} + +/* + * IF() prompt for a filename and inserts the file at the current location + * in the file. + */ +void IF() +{ + register int fd; /* File descriptor of file */ + char name[LINE_LEN]; /* Buffer for file name */ + +/* Get the file name */ + if (get_file("Get and insert file:", name) != FINE) + return; + + if ((fd = open(name, 0)) < 0) + error("Cannot open ", name); + else { + file_insert(fd, TRUE); /* Insert the file */ + (void) close(fd); + } +} + +/* + * File_insert() inserts a an opened file (as given by filedescriptor fd) + * at the current location. + */ +void file_insert(fd, old_pos) +int fd; +FLAG old_pos; +{ + char line_buffer[MAX_CHARS]; /* Buffer for next line */ + register LINE *line = cur_line; + register int line_count = nlines; /* Nr of lines inserted */ + LINE *page = cur_line; + int ret = ERRORS; + +/* Get the first piece of text (might be ended with a '\n') from fd */ + if (get_line(fd, line_buffer) == ERRORS) + return; /* Empty file */ + +/* Insert this text at the current location. */ + if (insert(line, cur_text, line_buffer) == ERRORS) + return; + +/* Repeat getting lines (and inserting lines) until EOF is reached */ + while ((ret = get_line(fd, line_buffer)) != ERRORS && ret != NO_LINE) + line = line_insert(line, line_buffer, ret); + + if (ret == NO_LINE) { /* Last line read not ended by a '\n' */ + line = line->next; + (void) insert(line, line->text, line_buffer); + } + +/* Calculate nr of lines added */ + line_count = nlines - line_count; + +/* Fix the screen */ + if (line_count == 0) { /* Only one line changed */ + set_cursor(0, y); + line_print(line); + move_to((old_pos == TRUE) ? x : x + length_of(line_buffer), y); + } + else { /* Several lines changed */ + reset(top_line, y); /* Reset pointers */ + while (page != line && page != bot_line->next) + page = page->next; + if (page != bot_line->next || old_pos == TRUE) + display(0, y, cur_line, screenmax - y); + if (old_pos == TRUE) + move_to(x, y); + else if (ret == NO_LINE) + move_to(length_of(line_buffer), find_y(line)); + else + move_to(0, find_y(line->next)); + } + +/* If nr of added line >= REPORT, print the count */ + if (line_count >= REPORT) + status_line(num_out((long) line_count), " lines added."); +} + +/* + * WB() writes the buffer (yank_file) into another file, which + * is prompted for. + */ +void WB() +{ + register int new_fd; /* Filedescriptor to copy file */ + int yank_fd; /* Filedescriptor to buffer */ + register int cnt; /* Count check for read/write */ + int ret = 0; /* Error check for write */ + char file[LINE_LEN]; /* Output file */ + +/* Checkout the buffer */ + if ((yank_fd = scratch_file(READ)) == ERRORS) { + error("Buffer is empty.", NIL_PTR); + return; + } + +/* Get file name */ + if (get_file("Write buffer to file:", file) != FINE) + return; + +/* Creat the new file */ + if ((new_fd = creat(file, 0644)) < 0) { + error("Cannot create ", file); + return; + } + + status_line("Writing ", file); + +/* Copy buffer into file */ + while ((cnt = read(yank_fd, text_buffer, sizeof(text_buffer))) > 0) + if (write(new_fd, text_buffer, cnt) != cnt) { + bad_write(new_fd); + ret = ERRORS; + break; + } + +/* Clean up open files and status_line */ + (void) close(new_fd); + (void) close(yank_fd); + + if (ret != ERRORS) /* Bad write */ + file_status("Wrote", chars_saved, file, lines_saved, TRUE, FALSE); +} + +/* + * MA sets mark_line (mark_text) to the current line (text pointer). + */ +void MA() +{ + mark_line = cur_line; + mark_text = cur_text; + status_line("Mark set", NIL_PTR); +} + +/* + * YA() puts the text between the marked position and the current + * in the buffer. + */ +void YA() +{ + set_up(NO_DELETE); +} + +/* + * DT() is essentially the same as YA(), but in DT() the text is deleted. + */ +void DT() +{ + set_up(DELETE); +} + +/* + * Set_up is an interface to the actual yank. It calls checkmark () to check + * if the marked position is still valid. If it is, yank is called with the + * arguments in the right order. + */ +void set_up(remove) +FLAG remove; /* DELETE if text should be deleted */ +{ + switch (checkmark()) { + case NOT_VALID : + error("Mark not set.", NIL_PTR); + return; + case SMALLER : + yank(mark_line, mark_text, cur_line, cur_text, remove); + break; + case BIGGER : + yank(cur_line, cur_text, mark_line, mark_text, remove); + break; + case SAME : /* Ignore stupid behaviour */ + yank_status = EMPTY; + chars_saved = 0L; + status_line("0 characters saved in buffer.", NIL_PTR); + break; + } +} + +/* + * Check_mark() checks if mark_line and mark_text are still valid pointers. If + * they are it returns SMALLER if the marked position is before the current, + * BIGGER if it isn't or SAME if somebody didn't get the point. + * NOT_VALID is returned when mark_line and/or mark_text are no longer valid. + * Legal() checks if mark_text is valid on the mark_line. + */ +FLAG checkmark() +{ + register LINE *line; + FLAG cur_seen = FALSE; + +/* Special case: check is mark_line and cur_line are the same. */ + if (mark_line == cur_line) { + if (mark_text == cur_text) /* Even same place */ + return SAME; + if (legal() == ERRORS) /* mark_text out of range */ + return NOT_VALID; + return (mark_text < cur_text) ? SMALLER : BIGGER; + } + +/* Start looking for mark_line in the line structure */ + for (line = header->next; line != tail; line = line->next) { + if (line == cur_line) + cur_seen = TRUE; + else if (line == mark_line) + break; + } + +/* If we found mark_line (line != tail) check for legality of mark_text */ + if (line == tail || legal() == ERRORS) + return NOT_VALID; + +/* cur_seen is TRUE if cur_line is before mark_line */ + return (cur_seen == TRUE) ? BIGGER : SMALLER; +} + +/* + * Legal() checks if mark_text is still a valid pointer. + */ +int legal() +{ + register char *textp = mark_line->text; + +/* Locate mark_text on mark_line */ + while (textp != mark_text && *textp++ != '\0') + ; + return (*textp == '\0') ? ERRORS : FINE; +} + +/* + * Yank puts all the text between start_position and end_position into + * the buffer. + * The caller must check that the arguments to yank() are valid. (E.g. in + * the right order) + */ +void yank(start_line, start_textp, end_line, end_textp, remove) +LINE *start_line, *end_line; +char *start_textp, *end_textp; +FLAG remove; /* DELETE if text should be deleted */ +{ + register LINE *line = start_line; + register char *textp = start_textp; + int fd; + +/* Creat file to hold buffer */ + if ((fd = scratch_file(WRITE)) == ERRORS) + return; + + chars_saved = 0L; + lines_saved = 0; + status_line("Saving text.", NIL_PTR); + +/* Keep writing chars until the end_location is reached. */ + while (textp != end_textp) { + if (write_char(fd, *textp) == ERRORS) { + (void) close(fd); + return; + } + if (*textp++ == '\n') { /* Move to the next line */ + line = line->next; + textp = line->text; + lines_saved++; + } + chars_saved++; + } + +/* Flush the I/O buffer and close file */ + if (flush_buffer(fd) == ERRORS) { + (void) close(fd); + return; + } + (void) close(fd); + yank_status = VALID; + +/* + * Check if the text should be deleted as well. If it should, the following + * hack is used to save a lot of code. First move back to the start_position. + * (This might be the location we're on now!) and them delete the text. + * It might be a bit confusing the first time somebody uses it. + * Delete() will fix the screen. + */ + if (remove == DELETE) { + move_to(find_x(start_line, start_textp), find_y(start_line)); + delete(start_line, start_textp, end_line, end_textp); + } + + status_line(num_out(chars_saved), " characters saved in buffer."); +} + +/* + * Scratch_file() creates a uniq file in /usr/tmp. If the file couldn't + * be created other combinations of files are tried until a maximum + * of MAXTRAILS times. After MAXTRAILS times, an error message is given + * and ERRORS is returned. + */ + +#define MAXTRAILS 26 + +int scratch_file(mode) +FLAG mode; /* Can be READ or WRITE permission */ +{ + static int trials = 0; /* Keep track of trails */ + register char *y_ptr, *n_ptr; + int fd; /* Filedescriptor to buffer */ + +/* If yank_status == NOT_VALID, scratch_file is called for the first time */ + if (yank_status == NOT_VALID && mode == WRITE) { /* Create new file */ + /* Generate file name. */ + y_ptr = &yank_file[11]; + n_ptr = num_out((long) getpid()); + while ((*y_ptr = *n_ptr++) != '\0') + y_ptr++; + *y_ptr++ = 'a' + trials; + *y_ptr = '\0'; + /* Check file existence */ + if (access(yank_file, 0) == 0 || (fd = creat(yank_file, 0644)) < 0) { + if (trials++ >= MAXTRAILS) { + error("Unable to creat scratchfile.", NIL_PTR); + return ERRORS; + } + else + return scratch_file(mode);/* Have another go */ + } + } + else if ((mode == READ && (fd = open(yank_file, 0)) < 0) || + (mode == WRITE && (fd = creat(yank_file, 0644)) < 0)) { + yank_status = NOT_VALID; + return ERRORS; + } + + clear_buffer(); + return fd; +} + +/* ======================================================================== * + * Search Routines * + * ======================================================================== */ + +/* + * A regular expression consists of a sequence of: + * 1. A normal character matching that character. + * 2. A . matching any character. + * 3. A ^ matching the begin of a line. + * 4. A $ (as last character of the pattern) mathing the end of a line. + * 5. A \<character> matching <character>. + * 6. A number of characters enclosed in [] pairs matching any of these + * characters. A list of characters can be indicated by a '-'. So + * [a-z] matches any letter of the alphabet. If the first character + * after the '[' is a '^' then the set is negated (matching none of + * the characters). + * A ']', '^' or '-' can be escaped by putting a '\' in front of it. + * 7. If one of the expressions as described in 1-6 is followed by a + * '*' than that expressions matches a sequence of 0 or more of + * that expression. + */ + +char typed_expression[LINE_LEN]; /* Holds previous expr. */ + +/* + * SF searches forward for an expression. + */ +void SF() +{ + search("Search forward:", FORWARD); +} + +/* + * SF searches backwards for an expression. + */ +void SR() +{ + search("Search reverse:", REVERSE); +} + +/* + * Get_expression() prompts for an expression. If just a return is typed, the + * old expression is used. If the expression changed, compile() is called and + * the returning REGEX structure is returned. It returns NIL_REG upon error. + * The save flag indicates whether the expression should be appended at the + * message pointer. + */ +REGEX *get_expression(message) +char *message; +{ + static REGEX program; /* Program of expression */ + char exp_buf[LINE_LEN]; /* Buffer for new expr. */ + + if (get_string(message, exp_buf, FALSE) == ERRORS) + return NIL_REG; + + if (exp_buf[0] == '\0' && typed_expression[0] == '\0') { + error("No previous expression.", NIL_PTR); + return NIL_REG; + } + + if (exp_buf[0] != '\0') { /* A new expr. is typed */ + copy_string(typed_expression, exp_buf);/* Save expr. */ + compile(exp_buf, &program); /* Compile new expression */ + } + + if (program.status == REG_ERROR) { /* Error during compiling */ + error(program.result.err_mess, NIL_PTR); + return NIL_REG; + } + return &program; +} + +/* + * GR() a replaces all matches from the current position until the end + * of the file. + */ +void GR() +{ + change("Global replace:", VALID); +} + +/* + * LR() replaces all matches on the current line. + */ +void LR() +{ + change("Line replace:", NOT_VALID); +} + +/* + * Change() prompts for an expression and a substitution pattern and changes + * all matches of the expression into the substitution. change() start looking + * for expressions at the current line and continues until the end of the file + * if the FLAG file is VALID. + */ +void change(message, file) +char *message; /* Message to prompt for expression */ +FLAG file; +{ + char mess_buf[LINE_LEN]; /* Buffer to hold message */ + char replacement[LINE_LEN]; /* Buffer to hold subst. pattern */ + REGEX *program; /* Program resulting from compilation */ + register LINE *line = cur_line; + register char *textp; + long lines = 0L; /* Nr of lines on which subs occurred */ + long subs = 0L; /* Nr of subs made */ + int page = y; /* Index to check if line is on screen*/ + +/* Save message and get expression */ + copy_string(mess_buf, message); + if ((program = get_expression(mess_buf)) == NIL_REG) + return; + +/* Get substitution pattern */ + build_string(mess_buf, "%s %s by:", mess_buf, typed_expression); + if (get_string(mess_buf, replacement, FALSE) == ERRORS) + return; + + set_cursor(0, ymax); + flush(); +/* Substitute until end of file */ + do { + if (line_check(program, line->text, FORWARD)) { + lines++; + /* Repeat sub. on this line as long as we find a match*/ + do { + subs++; /* Increment subs */ + if ((textp = substitute(line, program,replacement)) + == NIL_PTR) + return; /* Line too long */ + } while ((program->status & BEGIN_LINE) != BEGIN_LINE && + (program->status & END_LINE) != END_LINE && + line_check(program, textp, FORWARD)); + /* Check to see if we can print the result */ + if (page <= screenmax) { + set_cursor(0, page); + line_print(line); + } + } + if (page <= screenmax) + page++; + line = line->next; + } while (line != tail && file == VALID && quit == FALSE); + + copy_string(mess_buf, (quit == TRUE) ? "(Aborted) " : ""); +/* Fix the status line */ + if (subs == 0L && quit == FALSE) + error("Pattern not found.", NIL_PTR); + else if (lines >= REPORT || quit == TRUE) { + build_string(mess_buf, "%s %D substitutions on %D lines.", mess_buf, + subs, lines); + status_line(mess_buf, NIL_PTR); + } + else if (file == NOT_VALID && subs >= REPORT) + status_line(num_out(subs), " substitutions."); + else + clear_status(); + move_to (x, y); +} + +/* + * Substitute() replaces the match on this line by the substitute pattern + * as indicated by the program. Every '&' in the replacement is replaced by + * the original match. A \ in the replacement escapes the next character. + */ +char *substitute(line, program, replacement) +LINE *line; +REGEX *program; +char *replacement; /* Contains replacement pattern */ +{ + register char *textp = text_buffer; + register char *subp = replacement; + char *linep = line->text; + char *amp; + + modified = TRUE; + +/* Copy part of line until the beginning of the match */ + while (linep != program->start_ptr) + *textp++ = *linep++; + +/* + * Replace the match by the substitution pattern. Each occurrence of '&' is + * replaced by the original match. A \ escapes the next character. + */ + while (*subp != '\0' && textp < &text_buffer[MAX_CHARS]) { + if (*subp == '&') { /* Replace the original match */ + amp = program->start_ptr; + while (amp < program->end_ptr && textp<&text_buffer[MAX_CHARS]) + *textp++ = *amp++; + subp++; + } + else { + if (*subp == '\\' && *(subp + 1) != '\0') + subp++; + *textp++ = *subp++; + } + } + +/* Check for line length not exceeding MAX_CHARS */ + if (length_of(text_buffer) + length_of(program->end_ptr) >= MAX_CHARS) { + error("Substitution result: line too big", NIL_PTR); + return NIL_PTR; + } + +/* Append last part of line to the new build line */ + copy_string(textp, program->end_ptr); + +/* Free old line and install new one */ + free_space(line->text); + line->text = alloc(length_of(text_buffer) + 1); + copy_string(line->text, text_buffer); + + return(line->text + (textp - text_buffer)); +} + +/* + * Search() calls get_expression to fetch the expression. If this went well, + * the function match() is called which returns the line with the next match. + * If this line is the NIL_LINE, it means that a match could not be found. + * Find_x() and find_y() display the right page on the screen, and return + * the right coordinates for x and y. These coordinates are passed to move_to() + */ +void search(message, method) +char *message; +FLAG method; +{ + register REGEX *program; + register LINE *match_line; + +/* Get the expression */ + if ((program = get_expression(message)) == NIL_REG) + return; + + set_cursor(0, ymax); + flush(); +/* Find the match */ + if ((match_line = match(program, cur_text, method)) == NIL_LINE) { + if (quit == TRUE) + status_line("Aborted", NIL_PTR); + else + status_line("Pattern not found.", NIL_PTR); + return; + } + + move(0, program->start_ptr, find_y(match_line)); + clear_status(); +} + +/* + * find_y() checks if the matched line is on the current page. If it is, it + * returns the new y coordinate, else it displays the correct page with the + * matched line in the middle and returns the new y value; + */ +int find_y(match_line) +LINE *match_line; +{ + register LINE *line; + register int count = 0; + +/* Check if match_line is on the same page as currently displayed. */ + for (line = top_line; line != match_line && line != bot_line->next; + line = line->next) + count++; + if (line != bot_line->next) + return count; + +/* Display new page, with match_line in center. */ + if ((line = proceed(match_line, -(screenmax >> 1))) == header) { + /* Can't display in the middle. Make first line of file top_line */ + count = 0; + for (line = header->next; line != match_line; line = line->next) + count++; + line = header->next; + } + else /* New page is displayed. Set cursor to middle of page */ + count = screenmax >> 1; + +/* Reset pointers and redraw the screen */ + reset(line, 0); + RD(); + + return count; +} + +/* Opcodes for characters */ +#define NORMAL 0x0200 +#define DOT 0x0400 +#define EOLN 0x0800 +#define STAR 0x1000 +#define BRACKET 0x2000 +#define NEGATE 0x0100 +#define DONE 0x4000 + +/* Mask for opcodes and characters */ +#define LOW_BYTE 0x00FF +#define HIGH_BYTE 0xFF00 + +/* Previous is the contents of the previous address (ptr) points to */ +#define previous(ptr) (*((ptr) - 1)) + +/* Buffer to store outcome of compilation */ +int exp_buffer[BLOCK_SIZE]; + +/* Errors often used */ +char *too_long = "Regular expression too long"; + +/* + * Reg_error() is called by compile() is something went wrong. It set the + * status of the structure to error, and assigns the error field of the union. + */ +#define reg_error(str) program->status = REG_ERROR, \ + program->result.err_mess = (str) +/* + * Finished() is called when everything went right during compilation. It + * allocates space for the expression, and copies the expression buffer into + * this field. + */ +void finished(program, last_exp) +register REGEX *program; +int *last_exp; +{ + register int length = (last_exp - exp_buffer) * sizeof(int); + +/* Allocate space */ + program->result.expression = (int *) alloc(length); +/* Copy expression. (expression consists of ints!) */ + bcopy(exp_buffer, program->result.expression, length); +} + +/* + * Compile compiles the pattern into a more comprehensible form and returns a + * REGEX structure. If something went wrong, the status field of the structure + * is set to REG_ERROR and an error message is set into the err_mess field of + * the union. If all went well the expression is saved and the expression + * pointer is set to the saved (and compiled) expression. + */ +void compile(pattern, program) +register char *pattern; /* Pointer to pattern */ +REGEX *program; +{ + register int *expression = exp_buffer; + int *prev_char; /* Pointer to previous compiled atom */ + int *acct_field; /* Pointer to last BRACKET start */ + FLAG negate; /* Negate flag for BRACKET */ + char low_char; /* Index for chars in BRACKET */ + char c; + +/* Check for begin of line */ + if (*pattern == '^') { + program->status = BEGIN_LINE; + pattern++; + } + else { + program->status = 0; +/* If the first character is a '*' we have to assign it here. */ + if (*pattern == '*') { + *expression++ = '*' + NORMAL; + pattern++; + } + } + + for (; ;) { + switch (c = *pattern++) { + case '.' : + *expression++ = DOT; + break; + case '$' : + /* + * Only means EOLN if it is the last char of the pattern + */ + if (*pattern == '\0') { + *expression++ = EOLN | DONE; + program->status |= END_LINE; + finished(program, expression); + return; + } + else + *expression++ = NORMAL + '$'; + break; + case '\0' : + *expression++ = DONE; + finished(program, expression); + return; + case '\\' : + /* If last char, it must! mean a normal '\' */ + if (*pattern == '\0') + *expression++ = NORMAL + '\\'; + else + *expression++ = NORMAL + *pattern++; + break; + case '*' : + /* + * If the previous expression was a [] find out the + * begin of the list, and adjust the opcode. + */ + prev_char = expression - 1; + if (*prev_char & BRACKET) + *(expression - (*acct_field & LOW_BYTE))|= STAR; + else + *prev_char |= STAR; + break; + case '[' : + /* + * First field in expression gives information about + * the list. + * The opcode consists of BRACKET and if necessary + * NEGATE to indicate that the list should be negated + * and/or STAR to indicate a number of sequence of this + * list. + * The lower byte contains the length of the list. + */ + acct_field = expression++; + if (*pattern == '^') { /* List must be negated */ + pattern++; + negate = TRUE; + } + else + negate = FALSE; + while (*pattern != ']') { + if (*pattern == '\0') { + reg_error("Missing ]"); + return; + } + if (*pattern == '\\') + pattern++; + *expression++ = *pattern++; + if (*pattern == '-') { + /* Make list of chars */ + low_char = previous(pattern); + pattern++; /* Skip '-' */ + if (low_char++ > *pattern) { + reg_error("Bad range in [a-z]"); + return; + } + /* Build list */ + while (low_char <= *pattern) + *expression++ = low_char++; + pattern++; + } + if (expression >= &exp_buffer[BLOCK_SIZE]) { + reg_error(too_long); + return; + } + } + pattern++; /* Skip ']' */ + /* Assign length of list in acct field */ + if ((*acct_field = (expression - acct_field)) == 1) { + reg_error("Empty []"); + return; + } + /* Assign negate and bracket field */ + *acct_field |= BRACKET; + if (negate == TRUE) + *acct_field |= NEGATE; + /* + * Add BRACKET to opcode of last char in field because + * a '*' may be following the list. + */ + previous(expression) |= BRACKET; + break; + default : + *expression++ = c + NORMAL; + } + if (expression == &exp_buffer[BLOCK_SIZE]) { + reg_error(too_long); + return; + } + } + /* NOTREACHED */ +} + +/* + * Match gets as argument the program, pointer to place in current line to + * start from and the method to search for (either FORWARD or REVERSE). + * Match() will look through the whole file until a match is found. + * NIL_LINE is returned if no match could be found. + */ +LINE *match(program, string, method) +REGEX *program; +char *string; +register FLAG method; +{ + register LINE *line = cur_line; + char old_char; /* For saving chars */ + +/* Corrupted program */ + if (program->status == REG_ERROR) + return NIL_LINE; + +/* Check part of text first */ + if (!(program->status & BEGIN_LINE)) { + if (method == FORWARD) { + if (line_check(program, string + 1, method) == MATCH) + return cur_line; /* Match found */ + } + else if (!(program->status & END_LINE)) { + old_char = *string; /* Save char and */ + *string = '\n'; /* Assign '\n' for line_check */ + if (line_check(program, line->text, method) == MATCH) { + *string = old_char; /* Restore char */ + return cur_line; /* Found match */ + } + *string = old_char; /* No match, but restore char */ + } + } + +/* No match in last (or first) part of line. Check out rest of file */ + do { + line = (method == FORWARD) ? line->next : line->prev; + if (line->text == NIL_PTR) /* Header/tail */ + continue; + if (line_check(program, line->text, method) == MATCH) + return line; + } while (line != cur_line && quit == FALSE); + +/* No match found. */ + return NIL_LINE; +} + +/* + * Line_check() checks the line (or rather string) for a match. Method + * indicates FORWARD or REVERSE search. It scans through the whole string + * until a match is found, or the end of the string is reached. + */ +int line_check(program, string, method) +register REGEX *program; +char *string; +FLAG method; +{ + register char *textp = string; + +/* Assign start_ptr field. We might find a match right away! */ + program->start_ptr = textp; + +/* If the match must be anchored, just check the string. */ + if (program->status & BEGIN_LINE) + return check_string(program, string, NIL_INT); + + if (method == REVERSE) { + /* First move to the end of the string */ + for (textp = string; *textp != '\n'; textp++) + ; + /* Start checking string until the begin of the string is met */ + while (textp >= string) { + program->start_ptr = textp; + if (check_string(program, textp--, NIL_INT)) + return MATCH; + } + } + else { + /* Move through the string until the end of is found */ + while (quit == FALSE && *textp != '\0') { + program->start_ptr = textp; + if (check_string(program, textp, NIL_INT)) + return MATCH; + if (*textp == '\n') + break; + textp++; + } + } + + return NO_MATCH; +} + +/* + * Check() checks of a match can be found in the given string. Whenever a STAR + * is found during matching, then the begin position of the string is marked + * and the maximum number of matches is performed. Then the function star() + * is called which starts to finish the match from this position of the string + * (and expression). Check() return MATCH for a match, NO_MATCH is the string + * couldn't be matched or REG_ERROR for an illegal opcode in expression. + */ +int check_string(program, string, expression) +REGEX *program; +register char *string; +int *expression; +{ + register int opcode; /* Holds opcode of next expr. atom */ + char c; /* Char that must be matched */ + char *mark; /* For marking position */ + int star_fl; /* A star has been born */ + + if (expression == NIL_INT) + expression = program->result.expression; + +/* Loop until end of string or end of expression */ + while (quit == FALSE && !(*expression & DONE) && + *string != '\0' && *string != '\n') { + c = *expression & LOW_BYTE; /* Extract match char */ + opcode = *expression & HIGH_BYTE; /* Extract opcode */ + if (star_fl = (opcode & STAR)) { /* Check star occurrence */ + opcode &= ~STAR; /* Strip opcode */ + mark = string; /* Mark current position */ + } + expression++; /* Increment expr. */ + switch (opcode) { + case NORMAL : + if (star_fl) + while (*string++ == c) /* Skip all matches */ + ; + else if (*string++ != c) + return NO_MATCH; + break; + case DOT : + string++; + if (star_fl) /* Skip to eoln */ + while (*string != '\0' && *string++ != '\n') + ; + break; + case NEGATE | BRACKET: + case BRACKET : + if (star_fl) + while (in_list(expression, *string++, c, opcode) + == MATCH) + ; + else if (in_list(expression, *string++, c, opcode) == NO_MATCH) + return NO_MATCH; + expression += c - 1; /* Add length of list */ + break; + default : + panic("Corrupted program in check_string()"); + } + if (star_fl) + return star(program, mark, string, expression); + } + if (*expression & DONE) { + program->end_ptr = string; /* Match ends here */ + /* + * We might have found a match. The last thing to do is check + * whether a '$' was given at the end of the expression, or + * the match was found on a null string. (E.g. [a-z]* always + * matches) unless a ^ or $ was included in the pattern. + */ + if ((*expression & EOLN) && *string != '\n' && *string != '\0') + return NO_MATCH; + if (string == program->start_ptr && !(program->status & BEGIN_LINE) + && !(*expression & EOLN)) + return NO_MATCH; + return MATCH; + } + return NO_MATCH; +} + +/* + * Star() calls check_string() to find out the longest match possible. + * It searches backwards until the (in check_string()) marked position + * is reached, or a match is found. + */ +int star(program, end_position, string, expression) +REGEX *program; +register char *end_position; +register char *string; +int *expression; +{ + do { + string--; + if (check_string(program, string, expression)) + return MATCH; + } while (string != end_position); + + return NO_MATCH; +} + +/* + * In_list() checks if the given character is in the list of []. If it is + * it returns MATCH. if it isn't it returns NO_MATCH. These returns values + * are reversed when the NEGATE field in the opcode is present. + */ +int in_list(list, c, list_length, opcode) +register int *list; +char c; +register int list_length; +int opcode; +{ + if (c == '\0' || c == '\n') /* End of string, never matches */ + return NO_MATCH; + while (list_length-- > 1) { /* > 1, don't check acct_field */ + if ((*list & LOW_BYTE) == c) + return (opcode & NEGATE) ? NO_MATCH : MATCH; + list++; + } + return (opcode & NEGATE) ? MATCH : NO_MATCH; +} + +/* + * Dummy_line() adds an empty line at the end of the file. This is sometimes + * useful in combination with the EF and DN command in combination with the + * Yank command set. + */ +void dummy_line() +{ + (void) line_insert(tail->prev, "\n", 1); + tail->prev->shift_count = DUMMY; + if (last_y != screenmax) { + last_y++; + bot_line = bot_line->next; + } +} diff --git a/commands/patch/EXTERN.h b/commands/patch/EXTERN.h new file mode 100755 index 000000000..5edd6a343 --- /dev/null +++ b/commands/patch/EXTERN.h @@ -0,0 +1,21 @@ +/* $Header$ + * + * $Log$ + * Revision 1.1 2005/04/21 14:55:10 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:18 beng + * Initial import of minix 2.0.4 + * + * Revision 2.0 86/09/17 15:35:37 lwall + * Baseline for netwide release. + * + */ + +#undef EXT +#define EXT extern + +#undef INIT +#define INIT(x) + +#undef DOINIT diff --git a/commands/patch/INTERN.h b/commands/patch/INTERN.h new file mode 100755 index 000000000..0fb7ad2b5 --- /dev/null +++ b/commands/patch/INTERN.h @@ -0,0 +1,21 @@ +/* $Header$ + * + * $Log$ + * Revision 1.1 2005/04/21 14:55:10 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:18 beng + * Initial import of minix 2.0.4 + * + * Revision 2.0 86/09/17 15:35:58 lwall + * Baseline for netwide release. + * + */ + +#undef EXT +#define EXT + +#undef INIT +#define INIT(x) = x + +#define DOINIT diff --git a/commands/patch/Makefile b/commands/patch/Makefile new file mode 100755 index 000000000..96bdfc554 --- /dev/null +++ b/commands/patch/Makefile @@ -0,0 +1,26 @@ +# Makefile for patch + +CFLAGS= -O -DVOIDSIG -wo -D_MINIX -D_POSIX_SOURCE -DSMALL + +OBJ = patch.o pch.o inp.o util.o version.o + +all: patch + +patch: $(OBJ) + cc -i -o patch $(OBJ) + install -S 32kw patch + +install: /usr/bin/patch + +/usr/bin/patch: patch + install -cs -o bin patch $@ + +patch.o: config.h common.h patch.c inp.h pch.h util.h version.h +pch.o: config.h common.h pch.c pch.h util.h +inp.o: config.h common.h inp.c inp.h util.h +util.o: config.h common.h util.c util.h +version.o: config.h common.h version.c version.h patchlevel.h util.h + + +clean: + rm -f *.bak *.o *.s core patch diff --git a/commands/patch/common.h b/commands/patch/common.h new file mode 100755 index 000000000..5fa84079b --- /dev/null +++ b/commands/patch/common.h @@ -0,0 +1,162 @@ +/* $Header$ + * + * $Log$ + * Revision 1.1 2005/04/21 14:55:10 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:18 beng + * Initial import of minix 2.0.4 + * + * Revision 2.0.1.2 88/06/22 20:44:53 lwall + * patch12: sprintf was declared wrong + * + * Revision 2.0.1.1 88/06/03 15:01:56 lwall + * patch10: support for shorter extensions. + * + * Revision 2.0 86/09/17 15:36:39 lwall + * Baseline for netwide release. + * + */ + +#define DEBUGGING + +#include "config.h" +#include <sys/types.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +/* shut lint up about the following when return value ignored */ + +#define Signal (void)signal +#define Unlink (void)unlink +#define Lseek (void)lseek +#define Fseek (void)fseek +#define Fstat (void)fstat +#define Pclose (void)pclose +#define Close (void)close +#define Fclose (void)fclose +#define Fflush (void)fflush +#define Sprintf (void)sprintf +#define Mktemp (void)mktemp +#define Strcpy (void)strcpy +#define Strcat (void)strcat + +#include <sys/types.h> +#include <sys/stat.h> +#include <assert.h> +#include <ctype.h> +#include <signal.h> +#include <stdio.h> + +/* constants */ + +#define TRUE (1) +#define FALSE (0) + +#define MAXHUNKSIZE 100000 /* is this enough lines? */ +#define INITHUNKMAX 125 /* initial dynamic allocation size */ +#define MAXLINELEN 1024 +#define BUFFERSIZE 1024 +#define SCCSPREFIX "s." +#define GET "get -e %s" +#define RCSSUFFIX ",v" +#define CHECKOUT "co -l %s" + +#ifdef FLEXFILENAMES +#define ORIGEXT ".orig" +#define REJEXT ".rej" +#else +#define ORIGEXT "~" +#define REJEXT "#" +#endif + +/* handy definitions */ + +#define Null(t) ((t)0) +#define Nullch Null(char *) +#define Nullfp Null(FILE *) +#define Nulline Null(LINENUM) + +#define Ctl(ch) ((ch) & 037) + +#define strNE(s1,s2) (strcmp(s1, s2)) +#define strEQ(s1,s2) (!strcmp(s1, s2)) +#define strnNE(s1,s2,l) (strncmp(s1, s2, l)) +#define strnEQ(s1,s2,l) (!strncmp(s1, s2, l)) + +/* typedefs */ + +typedef int bool; +typedef long LINENUM; /* must be signed */ +typedef unsigned MEM; /* what to feed malloc */ + +/* globals */ + +EXT int Argc; /* guess */ +EXT char **Argv; +EXT int Argc_last; /* for restarting plan_b */ +EXT char **Argv_last; + +EXT struct stat filestat; /* file statistics area */ +EXT int filemode INIT(0644); + +EXT char buf[MAXLINELEN]; /* general purpose buffer */ +EXT FILE *ofp INIT(Nullfp); /* output file pointer */ +EXT FILE *rejfp INIT(Nullfp); /* reject file pointer */ + +EXT bool using_plan_a INIT(TRUE); /* try to keep everything in memory */ +EXT bool out_of_mem INIT(FALSE); /* ran out of memory in plan a */ + +#define MAXFILEC 2 +EXT int filec INIT(0); /* how many file arguments? */ +EXT char *filearg[MAXFILEC]; +EXT bool ok_to_create_file INIT(FALSE); +EXT char *bestguess INIT(Nullch); /* guess at correct filename */ + +EXT char *outname INIT(Nullch); +EXT char rejname[128]; + +EXT char *origext INIT(Nullch); +EXT char *origprae INIT(Nullch); + +EXT char TMPOUTNAME[] INIT("/tmp/patchoXXXXXX"); +EXT char TMPINNAME[] INIT("/tmp/patchiXXXXXX"); /* might want /usr/tmp here */ +EXT char TMPREJNAME[] INIT("/tmp/patchrXXXXXX"); +EXT char TMPPATNAME[] INIT("/tmp/patchpXXXXXX"); +#ifdef SMALL +EXT char TMPSTRNAME[] INIT("/tmp/patchsXXXXXX"); +#endif +EXT bool toutkeep INIT(FALSE); +EXT bool trejkeep INIT(FALSE); + +EXT LINENUM last_offset INIT(0); +#ifdef DEBUGGING +EXT int debug INIT(0); +#endif +EXT LINENUM maxfuzz INIT(2); +EXT bool force INIT(FALSE); +EXT bool verbose INIT(TRUE); +EXT bool reverse INIT(FALSE); +EXT bool noreverse INIT(FALSE); +EXT bool skip_rest_of_patch INIT(FALSE); +EXT int strippath INIT(957); +EXT bool canonicalize INIT(FALSE); + +#define CONTEXT_DIFF 1 +#define NORMAL_DIFF 2 +#define ED_DIFF 3 +#define NEW_CONTEXT_DIFF 4 +EXT int diff_type INIT(0); + +EXT bool do_defines INIT(FALSE); /* patch using ifdef, ifndef, etc. */ +EXT char if_defined[128]; /* #ifdef xyzzy */ +EXT char not_defined[128]; /* #ifndef xyzzy */ +EXT char else_defined[] INIT("#else\n");/* #else */ +EXT char end_defined[128]; /* #endif xyzzy */ + +EXT char *revision INIT(Nullch); /* prerequisite revision, if any */ + +_PROTOTYPE(void my_exit , (int status )); diff --git a/commands/patch/config.h b/commands/patch/config.h new file mode 100755 index 000000000..1dda30369 --- /dev/null +++ b/commands/patch/config.h @@ -0,0 +1,23 @@ +/* config.h + * This file was produced by running the Configure script. + * Feel free to modify any of this as the need arises. + */ + +/* How many register declarations are paid attention to? */ + +#define Reg1 register +#define Reg2 register +#define Reg3 +#define Reg4 +#define Reg5 +#define Reg6 +#define Reg7 +#define Reg8 +#define Reg9 +#define Reg10 +#define Reg11 +#define Reg12 +#define Reg13 +#define Reg14 +#define Reg15 +#define Reg16 diff --git a/commands/patch/inp.c b/commands/patch/inp.c new file mode 100755 index 000000000..449831a5a --- /dev/null +++ b/commands/patch/inp.c @@ -0,0 +1,331 @@ +/* $Header$ + * + * $Log$ + * Revision 1.1 2005/04/21 14:55:10 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:18 beng + * Initial import of minix 2.0.4 + * + * Revision 2.0.1.1 88/06/03 15:06:13 lwall + * patch10: made a little smarter about sccs files + * + * Revision 2.0 86/09/17 15:37:02 lwall + * Baseline for netwide release. + * + */ + +#include "EXTERN.h" +#include "common.h" +#include "util.h" +#include "pch.h" +#include "INTERN.h" +#include "inp.h" + +/* Input-file-with-indexable-lines abstract type */ + +static long i_size; /* size of the input file */ +static char *i_womp; /* plan a buffer for entire file */ +static char **i_ptr; /* pointers to lines in i_womp */ + +static int tifd = -1; /* plan b virtual string array */ +static char *tibuf[2]; /* plan b buffers */ +static LINENUM tiline[2] = {-1, -1}; /* 1st line in each buffer */ +static LINENUM lines_per_buf; /* how many lines per buffer */ +static int tireclen; /* length of records in tmp file */ + +/* New patch--prepare to edit another file. */ + +void +re_input() +{ + if (using_plan_a) { + i_size = 0; +#ifndef lint + if (i_ptr != Null(char**)) + free((char *)i_ptr); +#endif + if (i_womp != Nullch) + free(i_womp); + i_womp = Nullch; + i_ptr = Null(char **); + } + else { +#ifndef SMALL + using_plan_a = TRUE; /* maybe the next one is smaller */ +#endif + Close(tifd); + tifd = -1; + free(tibuf[0]); + free(tibuf[1]); + tibuf[0] = tibuf[1] = Nullch; + tiline[0] = tiline[1] = -1; + tireclen = 0; + } +} + +/* Constuct the line index, somehow or other. */ + +void +scan_input(filename) +char *filename; +{ +#ifndef SMALL + if (!plan_a(filename)) +#endif + plan_b(filename); + if (verbose) { + say3("Patching file %s using Plan %s...\n", filename, + (using_plan_a ? "A" : "B") ); + } +} + +#ifndef SMALL +/* Try keeping everything in memory. */ + +bool +plan_a(filename) +char *filename; +{ + int ifd; + Reg1 char *s; + Reg2 LINENUM iline; + + if (ok_to_create_file && stat(filename, &filestat) < 0) { + if (verbose) + say2("(Creating file %s...)\n",filename); + makedirs(filename, TRUE); + close(creat(filename, 0666)); + } + if (stat(filename, &filestat) < 0) { + Sprintf(buf, "RCS/%s%s", filename, RCSSUFFIX); + if (stat(buf, &filestat) >= 0 || stat(buf+4, &filestat) >= 0) { + Sprintf(buf, CHECKOUT, filename); + if (verbose) + say2("Can't find %s--attempting to check it out from RCS.\n", + filename); + if (system(buf) || stat(filename, &filestat)) + fatal2("Can't check out %s.\n", filename); + } + else { + Sprintf(buf+20, "SCCS/%s%s", SCCSPREFIX, filename); + if (stat(s=buf+20, &filestat) >= 0 || + stat(s=buf+25, &filestat) >= 0) { + Sprintf(buf, GET, s); + if (verbose) + say2("Can't find %s--attempting to get it from SCCS.\n", + filename); + if (system(buf) || stat(filename, &filestat)) + fatal2("Can't get %s.\n", filename); + } + else + fatal2("Can't find %s.\n", filename); + } + } + filemode = filestat.st_mode; + if ((filemode & S_IFMT) & ~S_IFREG) + fatal2("%s is not a normal file--can't patch.\n", filename); + i_size = filestat.st_size; + if (out_of_mem) { + set_hunkmax(); /* make sure dynamic arrays are allocated */ + out_of_mem = FALSE; + return FALSE; /* force plan b because plan a bombed */ + } +#ifdef lint + i_womp = Nullch; +#else + i_womp = malloc((MEM)(i_size+2)); /* lint says this may alloc less than */ + /* i_size, but that's okay, I think. */ +#endif + if (i_womp == Nullch) + return FALSE; + if ((ifd = open(filename, 0)) < 0) + fatal2("Can't open file %s\n", filename); +#ifndef lint + if (read(ifd, i_womp, (int)i_size) != i_size) { + Close(ifd); /* probably means i_size > 15 or 16 bits worth */ + free(i_womp); /* at this point it doesn't matter if i_womp was */ + return FALSE; /* undersized. */ + } +#endif + Close(ifd); + if (i_size && i_womp[i_size-1] != '\n') + i_womp[i_size++] = '\n'; + i_womp[i_size] = '\0'; + + /* count the lines in the buffer so we know how many pointers we need */ + + iline = 0; + for (s=i_womp; *s; s++) { + if (*s == '\n') + iline++; + } +#ifdef lint + i_ptr = Null(char**); +#else + i_ptr = (char **)malloc((MEM)((iline + 2) * sizeof(char *))); +#endif + if (i_ptr == Null(char **)) { /* shucks, it was a near thing */ + free((char *)i_womp); + return FALSE; + } + + /* now scan the buffer and build pointer array */ + + iline = 1; + i_ptr[iline] = i_womp; + for (s=i_womp; *s; s++) { + if (*s == '\n') + i_ptr[++iline] = s+1; /* these are NOT null terminated */ + } + input_lines = iline - 1; + + /* now check for revision, if any */ + + if (revision != Nullch) { + if (!rev_in_string(i_womp)) { + if (force) { + if (verbose) + say2( +"Warning: this file doesn't appear to be the %s version--patching anyway.\n", + revision); + } + else { + ask2( +"This file doesn't appear to be the %s version--patch anyway? [n] ", + revision); + if (*buf != 'y') + fatal1("Aborted.\n"); + } + } + else if (verbose) + say2("Good. This file appears to be the %s version.\n", + revision); + } + return TRUE; /* plan a will work */ +} +#endif + +/* Keep (virtually) nothing in memory. */ + +void +plan_b(filename) +char *filename; +{ + Reg3 FILE *ifp; + Reg1 int i = 0; + Reg2 int maxlen = 1; + Reg4 bool found_revision = (revision == Nullch); + + using_plan_a = FALSE; + if ((ifp = fopen(filename, "r")) == Nullfp) + fatal2("Can't open file %s\n", filename); + if ((tifd = creat(TMPINNAME, 0666)) < 0) + fatal2("Can't open file %s\n", TMPINNAME); + while (fgets(buf, sizeof buf, ifp) != Nullch) { + if (revision != Nullch && !found_revision && rev_in_string(buf)) + found_revision = TRUE; + if ((i = strlen(buf)) > maxlen) + maxlen = i; /* find longest line */ + } + if (revision != Nullch) { + if (!found_revision) { + if (force) { + if (verbose) + say2( +"Warning: this file doesn't appear to be the %s version--patching anyway.\n", + revision); + } + else { + ask2( +"This file doesn't appear to be the %s version--patch anyway? [n] ", + revision); + if (*buf != 'y') + fatal1("Aborted.\n"); + } + } + else if (verbose) + say2("Good. This file appears to be the %s version.\n", + revision); + } + Fseek(ifp, 0L, 0); /* rewind file */ + lines_per_buf = BUFFERSIZE / maxlen; + tireclen = maxlen; + tibuf[0] = (char *)malloc((MEM)(BUFFERSIZE + 1)); + tibuf[1] = (char *)malloc((MEM)(BUFFERSIZE + 1)); + if (tibuf[1] == Nullch) + fatal1("Can't seem to get enough memory.\n"); + for (i=1; ; i++) { + if (! (i % lines_per_buf)) /* new block */ + if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) + fatal1("patch: can't write temp file.\n"); + if (fgets(tibuf[0] + maxlen * (i%lines_per_buf), maxlen + 1, ifp) + == Nullch) { + input_lines = i - 1; + if (i % lines_per_buf) + if (write(tifd, tibuf[0], BUFFERSIZE) < BUFFERSIZE) + fatal1("patch: can't write temp file.\n"); + break; + } + } + Fclose(ifp); + Close(tifd); + if ((tifd = open(TMPINNAME, 0)) < 0) { + fatal2("Can't reopen file %s\n", TMPINNAME); + } +} + +/* Fetch a line from the input file, \n terminated, not necessarily \0. */ + +char * +ifetch(line,whichbuf) +Reg1 LINENUM line; +int whichbuf; /* ignored when file in memory */ +{ + if (line < 1 || line > input_lines) + return ""; + if (using_plan_a) + return i_ptr[line]; + else { + LINENUM offline = line % lines_per_buf; + LINENUM baseline = line - offline; + + if (tiline[0] == baseline) + whichbuf = 0; + else if (tiline[1] == baseline) + whichbuf = 1; + else { + tiline[whichbuf] = baseline; +#ifndef lint /* complains of long accuracy */ + Lseek(tifd, (long)baseline / lines_per_buf * BUFFERSIZE, 0); +#endif + if (read(tifd, tibuf[whichbuf], BUFFERSIZE) < 0) + fatal2("Error reading tmp file %s.\n", TMPINNAME); + } + return tibuf[whichbuf] + (tireclen*offline); + } +} + +/* True if the string argument contains the revision number we want. */ + +bool +rev_in_string(string) +char *string; +{ + Reg1 char *s; + Reg2 int patlen; + + if (revision == Nullch) + return TRUE; + patlen = strlen(revision); + if (strnEQ(string,revision,patlen) && isspace(s[patlen])) + return TRUE; + for (s = string; *s; s++) { + if (isspace(*s) && strnEQ(s+1, revision, patlen) && + isspace(s[patlen+1] )) { + return TRUE; + } + } + return FALSE; +} + diff --git a/commands/patch/inp.h b/commands/patch/inp.h new file mode 100755 index 000000000..717793007 --- /dev/null +++ b/commands/patch/inp.h @@ -0,0 +1,24 @@ +/* $Header$ + * + * $Log$ + * Revision 1.1 2005/04/21 14:55:10 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:18 beng + * Initial import of minix 2.0.4 + * + * Revision 2.0 86/09/17 15:37:25 lwall + * Baseline for netwide release. + * + */ + +EXT LINENUM input_lines INIT(0); /* how long is input file in lines */ +EXT LINENUM last_frozen_line INIT(0); /* how many input lines have been */ + /* irretractibly output */ + +_PROTOTYPE(bool rev_in_string , (char *string )); +_PROTOTYPE(void scan_input , (char *filename )); +_PROTOTYPE(bool plan_a , (char *filename )); +_PROTOTYPE(void plan_b , (char *filename )); +_PROTOTYPE(char *ifetch , (Reg1 LINENUM line , int whichbuf )); +_PROTOTYPE(void re_input , (void)); diff --git a/commands/patch/patch.c b/commands/patch/patch.c new file mode 100755 index 000000000..5108d0fa9 --- /dev/null +++ b/commands/patch/patch.c @@ -0,0 +1,843 @@ +char rcsid[] = + "$Header$"; + +/* patch - a program to apply diffs to original files + * + * Copyright 1986, Larry Wall + * + * This program may be copied as long as you don't try to make any + * money off of it, or pretend that you wrote it. + * + * $Log$ + * Revision 1.1 2005/04/21 14:55:10 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:19 beng + * Initial import of minix 2.0.4 + * + * Revision 2.0.1.6 88/06/22 20:46:39 lwall + * patch12: rindex() wasn't declared + * + * Revision 2.0.1.5 88/06/03 15:09:37 lwall + * patch10: exit code improved. + * patch10: better support for non-flexfilenames. + * + * Revision 2.0.1.4 87/02/16 14:00:04 lwall + * Short replacement caused spurious "Out of sync" message. + * + * Revision 2.0.1.3 87/01/30 22:45:50 lwall + * Improved diagnostic on sync error. + * Moved do_ed_script() to pch.c. + * + * Revision 2.0.1.2 86/11/21 09:39:15 lwall + * Fuzz factor caused offset of installed lines. + * + * Revision 2.0.1.1 86/10/29 13:10:22 lwall + * Backwards search could terminate prematurely. + * + * Revision 2.0 86/09/17 15:37:32 lwall + * Baseline for netwide release. + * + * Revision 1.5 86/08/01 20:53:24 lwall + * Changed some %d's to %ld's. + * Linted. + * + * Revision 1.4 86/08/01 19:17:29 lwall + * Fixes for machines that can't vararg. + * Added fuzz factor. + * Generalized -p. + * General cleanup. + * + * 85/08/15 van%ucbmonet@berkeley + * Changes for 4.3bsd diff -c. + * + * Revision 1.3 85/03/26 15:07:43 lwall + * Frozen. + * + * Revision 1.2.1.9 85/03/12 17:03:35 lwall + * Changed pfp->_file to fileno(pfp). + * + * Revision 1.2.1.8 85/03/12 16:30:43 lwall + * Check i_ptr and i_womp to make sure they aren't null before freeing. + * Also allow ed output to be suppressed. + * + * Revision 1.2.1.7 85/03/12 15:56:13 lwall + * Added -p option from jromine@uci-750a. + * + * Revision 1.2.1.6 85/03/12 12:12:51 lwall + * Now checks for normalness of file to patch. + * + * Revision 1.2.1.5 85/03/12 11:52:12 lwall + * Added -D (#ifdef) option from joe@fluke. + * + * Revision 1.2.1.4 84/12/06 11:14:15 lwall + * Made smarter about SCCS subdirectories. + * + * Revision 1.2.1.3 84/12/05 11:18:43 lwall + * Added -l switch to do loose string comparison. + * + * Revision 1.2.1.2 84/12/04 09:47:13 lwall + * Failed hunk count not reset on multiple patch file. + * + * Revision 1.2.1.1 84/12/04 09:42:37 lwall + * Branch for sdcrdcf changes. + * + * Revision 1.2 84/11/29 13:29:51 lwall + * Linted. Identifiers uniqified. Fixed i_ptr malloc() bug. Fixed + * multiple calls to mktemp(). Will now work on machines that can only + * read 32767 chars. Added -R option for diffs with new and old swapped. + * Various cosmetic changes. + * + * Revision 1.1 84/11/09 17:03:58 lwall + * Initial revision + * + */ +/* + * 1992-01-15 + * Modified by Saeko & Kouichi Hirabayashi to fit small memory (64K+64K) + * system by adding "#if[n]def SMALL" parts. + */ + +#include "INTERN.h" +#include "common.h" +#include "EXTERN.h" +#include "version.h" +#include "util.h" +#include "pch.h" +#include "inp.h" + +/* procedures */ + +_PROTOTYPE(int main , (int argc , char **argv )); +_PROTOTYPE(void reinitialize_almost_everything , (void)); +_PROTOTYPE(void get_some_switches , (void)); +_PROTOTYPE(LINENUM locate_hunk , (LINENUM fuzz )); +_PROTOTYPE(void abort_hunk , (void)); +_PROTOTYPE(void apply_hunk , (LINENUM where )); +_PROTOTYPE(void init_output , (char *name )); +_PROTOTYPE(void init_reject , (char *name )); +_PROTOTYPE(void copy_till , (Reg1 LINENUM lastline )); +_PROTOTYPE(void spew_output , (void)); +_PROTOTYPE(void dump_line , (LINENUM line )); +_PROTOTYPE(bool patch_match , (LINENUM base , LINENUM offset , LINENUM fuzz )); +_PROTOTYPE(bool similar , (Reg1 char *a , Reg2 char *b , Reg3 int len )); + +/* Apply a set of diffs as appropriate. */ + +int main(argc,argv) +int argc; +char **argv; +{ + LINENUM where; + LINENUM newwhere; + LINENUM fuzz; + LINENUM mymaxfuzz; + int hunk = 0; + int failed = 0; + int failtotal = 0; + int i; + + setbuf(stderr, serrbuf); + for (i = 0; i<MAXFILEC; i++) + filearg[i] = Nullch; + Mktemp(TMPOUTNAME); + Mktemp(TMPINNAME); + Mktemp(TMPREJNAME); + Mktemp(TMPPATNAME); +#ifdef SMALL + Mktemp(TMPSTRNAME); +#endif + + /* parse switches */ + Argc = argc; + Argv = argv; + get_some_switches(); + + /* make sure we clean up /tmp in case of disaster */ + set_signals(0); + + for ( + open_patch_file(filearg[1]); + there_is_another_patch(); + reinitialize_almost_everything() + ) { /* for each patch in patch file */ + + if (outname == Nullch) + outname = savestr(filearg[0]); + + /* initialize the patched file */ + if (!skip_rest_of_patch) + init_output(TMPOUTNAME); + + /* for ed script just up and do it and exit */ + if (diff_type == ED_DIFF) { + do_ed_script(); + continue; + } + + /* initialize reject file */ + init_reject(TMPREJNAME); + + /* find out where all the lines are */ + if (!skip_rest_of_patch) + scan_input(filearg[0]); + + /* from here on, open no standard i/o files, because malloc */ + /* might misfire and we can't catch it easily */ + + /* apply each hunk of patch */ + hunk = 0; + failed = 0; + out_of_mem = FALSE; + while (another_hunk()) { + hunk++; + fuzz = Nulline; + mymaxfuzz = pch_context(); + if (maxfuzz < mymaxfuzz) + mymaxfuzz = maxfuzz; + if (!skip_rest_of_patch) { + do { + where = locate_hunk(fuzz); + if (hunk == 1 && where == Nulline && !force) { + /* dwim for reversed patch? */ + if (!pch_swap()) { + if (fuzz == Nulline) + say1( +"Not enough memory to try swapped hunk! Assuming unswapped.\n"); + continue; + } + reverse = !reverse; + where = locate_hunk(fuzz); /* try again */ + if (where == Nulline) { /* didn't find it swapped */ + if (!pch_swap()) /* put it back to normal */ + fatal1("Lost hunk on alloc error!\n"); + reverse = !reverse; + } + else if (noreverse) { + if (!pch_swap()) /* put it back to normal */ + fatal1("Lost hunk on alloc error!\n"); + reverse = !reverse; + say1( +"Ignoring previously applied (or reversed) patch.\n"); + skip_rest_of_patch = TRUE; + } + else { + ask3( +"%seversed (or previously applied) patch detected! %s -R? [y] ", + reverse ? "R" : "Unr", + reverse ? "Assume" : "Ignore"); + if (*buf == 'n') { + ask1("Apply anyway? [n] "); + if (*buf != 'y') + skip_rest_of_patch = TRUE; + where = Nulline; + reverse = !reverse; + if (!pch_swap()) /* put it back to normal */ + fatal1("Lost hunk on alloc error!\n"); + } + } + } + } while (!skip_rest_of_patch && where == Nulline && + ++fuzz <= mymaxfuzz); + + if (skip_rest_of_patch) { /* just got decided */ + Fclose(ofp); + ofp = Nullfp; + } + } + + newwhere = pch_newfirst() + last_offset; + if (skip_rest_of_patch) { + abort_hunk(); + failed++; + if (verbose) + say3("Hunk #%d ignored at %ld.\n", hunk, newwhere); + } + else if (where == Nulline) { + abort_hunk(); + failed++; + if (verbose) + say3("Hunk #%d failed at %ld.\n", hunk, newwhere); + } + else { + apply_hunk(where); + if (verbose) { + say3("Hunk #%d succeeded at %ld", hunk, newwhere); + if (fuzz) + say2(" with fuzz %ld", fuzz); + if (last_offset) + say3(" (offset %ld line%s)", + last_offset, last_offset==1L?"":"s"); + say1(".\n"); + } + } + } + + if (out_of_mem && using_plan_a) { + Argc = Argc_last; + Argv = Argv_last; + say1("\n\nRan out of memory using Plan A--trying again...\n\n"); + continue; + } + + assert(hunk); + + /* finish spewing out the new file */ + if (!skip_rest_of_patch) + spew_output(); + + /* and put the output where desired */ + ignore_signals(); + if (!skip_rest_of_patch) { + if (move_file(TMPOUTNAME, outname) < 0) { + toutkeep = TRUE; + chmod(TMPOUTNAME, filemode); + } + else + chmod(outname, filemode); + } + Fclose(rejfp); + rejfp = Nullfp; + if (failed) { + failtotal += failed; + if (!*rejname) { + Strcpy(rejname, outname); +#ifndef FLEXFILENAMES + { + char *s = rindex(rejname,'/'); + + if (!s) + s = rejname; + if (strlen(s) > 13) + if (s[12] == '.') /* try to preserve difference */ + s[12] = s[13]; /* between .h, .c, .y, etc. */ + s[13] = '\0'; + } +#endif + Strcat(rejname, REJEXT); + } + if (skip_rest_of_patch) { + say4("%d out of %d hunks ignored--saving rejects to %s\n", + failed, hunk, rejname); + } + else { + say4("%d out of %d hunks failed--saving rejects to %s\n", + failed, hunk, rejname); + } + if (move_file(TMPREJNAME, rejname) < 0) + trejkeep = TRUE; + } + set_signals(1); + } +#ifdef SMALL + if (sfp != Nullfp) + Fclose(sfp); +#endif + my_exit(failtotal); +} + +/* Prepare to find the next patch to do in the patch file. */ + +void +reinitialize_almost_everything() +{ + re_patch(); + re_input(); + + input_lines = 0; + last_frozen_line = 0; + + filec = 0; + if (filearg[0] != Nullch && !out_of_mem) { + free(filearg[0]); + filearg[0] = Nullch; + } + + if (outname != Nullch) { + free(outname); + outname = Nullch; + } + + last_offset = 0; + + diff_type = 0; + + if (revision != Nullch) { + free(revision); + revision = Nullch; + } + + reverse = FALSE; + skip_rest_of_patch = FALSE; + + get_some_switches(); + + if (filec >= 2) + fatal1("You may not change to a different patch file.\n"); +} + +/* Process switches and filenames up to next '+' or end of list. */ + +void +get_some_switches() +{ + Reg1 char *s; + + rejname[0] = '\0'; + Argc_last = Argc; + Argv_last = Argv; + if (!Argc) + return; + for (Argc--,Argv++; Argc; Argc--,Argv++) { + s = Argv[0]; + if (strEQ(s, "+")) { + return; /* + will be skipped by for loop */ + } + if (*s != '-' || !s[1]) { + if (filec == MAXFILEC) + fatal1("Too many file arguments.\n"); + filearg[filec++] = savestr(s); + } + else { + switch (*++s) { + case 'b': + origext = savestr(Argv[1]); + Argc--,Argv++; + break; + case 'B': + origprae = savestr(Argv[1]); + Argc--,Argv++; + break; + case 'c': + diff_type = CONTEXT_DIFF; + break; + case 'd': + if (!*++s) { + Argc--,Argv++; + s = Argv[0]; + } + if (chdir(s) < 0) + fatal2("Can't cd to %s.\n", s); + break; + case 'D': + do_defines = TRUE; + if (!*++s) { + Argc--,Argv++; + s = Argv[0]; + } + if (!isalpha(*s)) + fatal1("Argument to -D not an identifier.\n"); + Sprintf(if_defined, "#ifdef %s\n", s); + Sprintf(not_defined, "#ifndef %s\n", s); + Sprintf(end_defined, "#endif /* %s */\n", s); + break; + case 'e': + diff_type = ED_DIFF; + break; + case 'f': + force = TRUE; + break; + case 'F': + if (*++s == '=') + s++; + maxfuzz = atoi(s); + break; + case 'l': + canonicalize = TRUE; + break; + case 'n': + diff_type = NORMAL_DIFF; + break; + case 'N': + noreverse = TRUE; + break; + case 'o': + outname = savestr(Argv[1]); + Argc--,Argv++; + break; + case 'p': + if (*++s == '=') + s++; + strippath = atoi(s); + break; + case 'r': + Strcpy(rejname, Argv[1]); + Argc--,Argv++; + break; + case 'R': + reverse = TRUE; + break; + case 's': + verbose = FALSE; + break; + case 'S': + skip_rest_of_patch = TRUE; + break; + case 'v': + version(); + break; +#ifdef DEBUGGING + case 'x': + debug = atoi(s+1); + break; +#endif + default: + fatal2("Unrecognized switch: %s\n", Argv[0]); + } + } + } +} + +/* Attempt to find the right place to apply this hunk of patch. */ + +LINENUM +locate_hunk(fuzz) +LINENUM fuzz; +{ + Reg1 LINENUM first_guess = pch_first() + last_offset; + Reg2 LINENUM offset; + LINENUM pat_lines = pch_ptrn_lines(); + Reg3 LINENUM max_pos_offset = input_lines - first_guess + - pat_lines + 1; + Reg4 LINENUM max_neg_offset = first_guess - last_frozen_line - 1 + + pch_context(); + + if (!pat_lines) /* null range matches always */ + return first_guess; + if (max_neg_offset >= first_guess) /* do not try lines < 0 */ + max_neg_offset = first_guess - 1; + if (first_guess <= input_lines && patch_match(first_guess, Nulline, fuzz)) + return first_guess; + for (offset = 1; ; offset++) { + Reg5 bool check_after = (offset <= max_pos_offset); + Reg6 bool check_before = (offset <= max_neg_offset); + + if (check_after && patch_match(first_guess, offset, fuzz)) { +#ifdef DEBUGGING + if (debug & 1) + say3("Offset changing from %ld to %ld\n", last_offset, offset); +#endif + last_offset = offset; + return first_guess+offset; + } + else if (check_before && patch_match(first_guess, -offset, fuzz)) { +#ifdef DEBUGGING + if (debug & 1) + say3("Offset changing from %ld to %ld\n", last_offset, -offset); +#endif + last_offset = -offset; + return first_guess-offset; + } + else if (!check_before && !check_after) + return Nulline; + } +} + +/* We did not find the pattern, dump out the hunk so they can handle it. */ + +void +abort_hunk() +{ + Reg1 LINENUM i; + Reg2 LINENUM pat_end = pch_end(); + /* add in last_offset to guess the same as the previous successful hunk */ + LINENUM oldfirst = pch_first() + last_offset; + LINENUM newfirst = pch_newfirst() + last_offset; + LINENUM oldlast = oldfirst + pch_ptrn_lines() - 1; + LINENUM newlast = newfirst + pch_repl_lines() - 1; + char *stars = (diff_type == NEW_CONTEXT_DIFF ? " ****" : ""); + char *minuses = (diff_type == NEW_CONTEXT_DIFF ? " ----" : " -----"); + + fprintf(rejfp, "***************\n"); + for (i=0; i<=pat_end; i++) { + switch (pch_char(i)) { + case '*': + if (oldlast < oldfirst) + fprintf(rejfp, "*** 0%s\n", stars); + else if (oldlast == oldfirst) + fprintf(rejfp, "*** %ld%s\n", oldfirst, stars); + else + fprintf(rejfp, "*** %ld,%ld%s\n", oldfirst, oldlast, stars); + break; + case '=': + if (newlast < newfirst) + fprintf(rejfp, "--- 0%s\n", minuses); + else if (newlast == newfirst) + fprintf(rejfp, "--- %ld%s\n", newfirst, minuses); + else + fprintf(rejfp, "--- %ld,%ld%s\n", newfirst, newlast, minuses); + break; + case '\n': + fprintf(rejfp, "%s", pfetch(i)); + break; + case ' ': case '-': case '+': case '!': + fprintf(rejfp, "%c %s", pch_char(i), pfetch(i)); + break; + default: + say1("Fatal internal error in abort_hunk().\n"); + abort(); + } + } +} + +/* We found where to apply it (we hope), so do it. */ + +void +apply_hunk(where) +LINENUM where; +{ + Reg1 LINENUM old = 1; + Reg2 LINENUM lastline = pch_ptrn_lines(); + Reg3 LINENUM new = lastline+1; +#define OUTSIDE 0 +#define IN_IFNDEF 1 +#define IN_IFDEF 2 +#define IN_ELSE 3 + Reg4 int def_state = OUTSIDE; + Reg5 bool R_do_defines = do_defines; + Reg6 LINENUM pat_end = pch_end(); + + where--; + while (pch_char(new) == '=' || pch_char(new) == '\n') + new++; + + while (old <= lastline) { + if (pch_char(old) == '-') { + copy_till(where + old - 1); + if (R_do_defines) { + if (def_state == OUTSIDE) { + fputs(not_defined, ofp); + def_state = IN_IFNDEF; + } + else if (def_state == IN_IFDEF) { + fputs(else_defined, ofp); + def_state = IN_ELSE; + } + fputs(pfetch(old), ofp); + } + last_frozen_line++; + old++; + } + else if (new > pat_end) + break; + else if (pch_char(new) == '+') { + copy_till(where + old - 1); + if (R_do_defines) { + if (def_state == IN_IFNDEF) { + fputs(else_defined, ofp); + def_state = IN_ELSE; + } + else if (def_state == OUTSIDE) { + fputs(if_defined, ofp); + def_state = IN_IFDEF; + } + } + fputs(pfetch(new), ofp); + new++; + } + else { + if (pch_char(new) != pch_char(old)) { + say3("Out-of-sync patch, lines %ld,%ld--mangled text or line numbers, maybe?\n", + pch_hunk_beg() + old, + pch_hunk_beg() + new); +#ifdef DEBUGGING + say3("oldchar = '%c', newchar = '%c'\n", + pch_char(old), pch_char(new)); +#endif + my_exit(1); + } + if (pch_char(new) == '!') { + copy_till(where + old - 1); + if (R_do_defines) { + fputs(not_defined, ofp); + def_state = IN_IFNDEF; + } + while (pch_char(old) == '!') { + if (R_do_defines) { + fputs(pfetch(old), ofp); + } + last_frozen_line++; + old++; + } + if (R_do_defines) { + fputs(else_defined, ofp); + def_state = IN_ELSE; + } + while (pch_char(new) == '!') { + fputs(pfetch(new), ofp); + new++; + } + if (R_do_defines) { + fputs(end_defined, ofp); + def_state = OUTSIDE; + } + } + else { + assert(pch_char(new) == ' '); + old++; + new++; + } + } + } + if (new <= pat_end && pch_char(new) == '+') { + copy_till(where + old - 1); + if (R_do_defines) { + if (def_state == OUTSIDE) { + fputs(if_defined, ofp); + def_state = IN_IFDEF; + } + else if (def_state == IN_IFNDEF) { + fputs(else_defined, ofp); + def_state = IN_ELSE; + } + } + while (new <= pat_end && pch_char(new) == '+') { + fputs(pfetch(new), ofp); + new++; + } + } + if (R_do_defines && def_state != OUTSIDE) { + fputs(end_defined, ofp); + } +} + +/* Open the new file. */ + +void +init_output(name) +char *name; +{ + ofp = fopen(name, "w"); + if (ofp == Nullfp) + fatal2("patch: can't create %s.\n", name); +} + +/* Open a file to put hunks we can't locate. */ + +void +init_reject(name) +char *name; +{ + rejfp = fopen(name, "w"); + if (rejfp == Nullfp) + fatal2("patch: can't create %s.\n", name); +} + +/* Copy input file to output, up to wherever hunk is to be applied. */ + +void +copy_till(lastline) +Reg1 LINENUM lastline; +{ + Reg2 LINENUM R_last_frozen_line = last_frozen_line; + + if (R_last_frozen_line > lastline) + say1("patch: misordered hunks! output will be garbled.\n"); + while (R_last_frozen_line < lastline) { + dump_line(++R_last_frozen_line); + } + last_frozen_line = R_last_frozen_line; +} + +/* Finish copying the input file to the output file. */ + +void +spew_output() +{ +#ifdef DEBUGGING + if (debug & 256) + say3("il=%ld lfl=%ld\n",input_lines,last_frozen_line); +#endif + if (input_lines) + copy_till(input_lines); /* dump remainder of file */ + Fclose(ofp); + ofp = Nullfp; +} + +/* Copy one line from input to output. */ + +void +dump_line(line) +LINENUM line; +{ + Reg1 char *s; + Reg2 char R_newline = '\n'; + + /* Note: string is not null terminated. */ + for (s=ifetch(line, 0); putc(*s, ofp) != R_newline; s++) ; +} + +/* Does the patch pattern match at line base+offset? */ + +bool +patch_match(base, offset, fuzz) +LINENUM base; +LINENUM offset; +LINENUM fuzz; +{ + Reg1 LINENUM pline = 1 + fuzz; + Reg2 LINENUM iline; + Reg3 LINENUM pat_lines = pch_ptrn_lines() - fuzz; + + for (iline=base+offset+fuzz; pline <= pat_lines; pline++,iline++) { + if (canonicalize) { + if (!similar(ifetch(iline, (offset >= 0)), + pfetch(pline), + pch_line_len(pline) )) + return FALSE; + } + else if (strnNE(ifetch(iline, (offset >= 0)), + pfetch(pline), + pch_line_len(pline) )) + return FALSE; + } + return TRUE; +} + +/* Do two lines match with canonicalized white space? */ + +bool +similar(a,b,len) +Reg1 char *a; +Reg2 char *b; +Reg3 int len; +{ + while (len) { + if (isspace(*b)) { /* whitespace (or \n) to match? */ + if (!isspace(*a)) /* no corresponding whitespace? */ + return FALSE; + while (len && isspace(*b) && *b != '\n') + b++,len--; /* skip pattern whitespace */ + while (isspace(*a) && *a != '\n') + a++; /* skip target whitespace */ + if (*a == '\n' || *b == '\n') + return (*a == *b); /* should end in sync */ + } + else if (*a++ != *b++) /* match non-whitespace chars */ + return FALSE; + else + len--; /* probably not necessary */ + } + return TRUE; /* actually, this is not reached */ + /* since there is always a \n */ +} + +/* Exit with cleanup. */ + +void +my_exit(status) +int status; +{ + Unlink(TMPINNAME); + if (!toutkeep) { + Unlink(TMPOUTNAME); + } + if (!trejkeep) { + Unlink(TMPREJNAME); + } + Unlink(TMPPATNAME); +#ifdef SMALL + Unlink(TMPSTRNAME); +#endif + exit(status); +} diff --git a/commands/patch/patchlevel.h b/commands/patch/patchlevel.h new file mode 100755 index 000000000..bc5f1c825 --- /dev/null +++ b/commands/patch/patchlevel.h @@ -0,0 +1 @@ +#define PATCHLEVEL 12 diff --git a/commands/patch/pch.c b/commands/patch/pch.c new file mode 100755 index 000000000..7b0ce9d16 --- /dev/null +++ b/commands/patch/pch.c @@ -0,0 +1,1307 @@ +/* $Header$ + * + * $Log$ + * Revision 1.1 2005/04/21 14:55:11 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:19 beng + * Initial import of minix 2.0.4 + * + * Revision 2.0.1.7 88/06/03 15:13:28 lwall + * patch10: Can now find patches in shar scripts. + * patch10: Hunks that swapped and then swapped back could core dump. + * + * Revision 2.0.1.6 87/06/04 16:18:13 lwall + * pch_swap didn't swap p_bfake and p_efake. + * + * Revision 2.0.1.5 87/01/30 22:47:42 lwall + * Improved responses to mangled patches. + * + * Revision 2.0.1.4 87/01/05 16:59:53 lwall + * New-style context diffs caused double call to free(). + * + * Revision 2.0.1.3 86/11/14 10:08:33 lwall + * Fixed problem where a long pattern wouldn't grow the hunk. + * Also restored p_input_line when backtracking so error messages are right. + * + * Revision 2.0.1.2 86/11/03 17:49:52 lwall + * New-style delete triggers spurious assertion error. + * + * Revision 2.0.1.1 86/10/29 15:52:08 lwall + * Could falsely report new-style context diff. + * + * Revision 2.0 86/09/17 15:39:37 lwall + * Baseline for netwide release. + * + */ + +#include "EXTERN.h" +#include "common.h" +#include "util.h" +#include "INTERN.h" +#include "pch.h" + +/* Patch (diff listing) abstract type. */ + +static long p_filesize; /* size of the patch file */ +static LINENUM p_first; /* 1st line number */ +static LINENUM p_newfirst; /* 1st line number of replacement */ +static LINENUM p_ptrn_lines; /* # lines in pattern */ +static LINENUM p_repl_lines; /* # lines in replacement text */ +static LINENUM p_end = -1; /* last line in hunk */ +static LINENUM p_max; /* max allowed value of p_end */ +static LINENUM p_context = 3; /* # of context lines */ +static LINENUM p_input_line = 0; /* current line # from patch file */ +#ifdef SMALL +static long *p_line = Null(long *); /* the text of the hunk */ +#else +static char **p_line = Null(char**); /* the text of the hunk */ +#endif +static short *p_len = Null(short*); /* length of each line */ +static char *p_char = Nullch; /* +, -, and ! */ +static int hunkmax = INITHUNKMAX; /* size of above arrays to begin with */ +static int p_indent; /* indent to patch */ +static LINENUM p_base; /* where to intuit this time */ +static LINENUM p_bline; /* line # of p_base */ +static LINENUM p_start; /* where intuit found a patch */ +static LINENUM p_sline; /* and the line number for it */ +static LINENUM p_hunk_beg; /* line number of current hunk */ +static LINENUM p_efake = -1; /* end of faked up lines--don't free */ +static LINENUM p_bfake = -1; /* beg of faked up lines */ + +/* Prepare to look for the next patch in the patch file. */ + +void +re_patch() +{ + p_first = Nulline; + p_newfirst = Nulline; + p_ptrn_lines = Nulline; + p_repl_lines = Nulline; + p_end = (LINENUM)-1; + p_max = Nulline; + p_indent = 0; +} + +/* Open the patch file at the beginning of time. */ + +void +open_patch_file(filename) +char *filename; +{ + if (filename == Nullch || !*filename || strEQ(filename, "-")) { + pfp = fopen(TMPPATNAME, "w"); + if (pfp == Nullfp) + fatal2("patch: can't create %s.\n", TMPPATNAME); + while (fgets(buf, sizeof buf, stdin) != Nullch) + fputs(buf, pfp); + Fclose(pfp); + filename = TMPPATNAME; + } + pfp = fopen(filename, "r"); + if (pfp == Nullfp) + fatal2("patch file %s not found\n", filename); + Fstat(fileno(pfp), &filestat); + p_filesize = filestat.st_size; + next_intuit_at(0L,1L); /* start at the beginning */ + set_hunkmax(); +} + +/* Make sure our dynamically realloced tables are malloced to begin with. */ + +void +set_hunkmax() +{ +#ifndef lint +#ifdef SMALL + if (p_line == Null(long*)) +#else + if (p_line == Null(char**)) +#endif +#ifdef SMALL + p_line = (long *) malloc((MEM)hunkmax * sizeof(long)); +#else + p_line = (char**) malloc((MEM)hunkmax * sizeof(char *)); +#endif + if (p_len == Null(short*)) + p_len = (short*) malloc((MEM)hunkmax * sizeof(short)); +#endif + if (p_char == Nullch) + p_char = (char*) malloc((MEM)hunkmax * sizeof(char)); +} + +/* Enlarge the arrays containing the current hunk of patch. */ + +void +grow_hunkmax() +{ + hunkmax *= 2; + /* + * Note that on most systems, only the p_line array ever gets fresh memory + * since p_len can move into p_line's old space, and p_char can move into + * p_len's old space. Not on PDP-11's however. But it doesn't matter. + */ +#ifdef SMALL + assert(p_line != Null(long*) && p_len != Null(short*) && p_char != Nullch); +#else + assert(p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch); +#endif +#ifndef lint +#ifdef SMALL + p_line = (long*) realloc((char*)p_line, (MEM)hunkmax * sizeof(long)); +#else + p_line = (char**) realloc((char*)p_line, (MEM)hunkmax * sizeof(char *)); +#endif + p_len = (short*) realloc((char*)p_len, (MEM)hunkmax * sizeof(short)); + p_char = (char*) realloc((char*)p_char, (MEM)hunkmax * sizeof(char)); +#endif +#ifdef SMALL + if (p_line != Null(long*) && p_len != Null(short*) && p_char != Nullch) +#else + if (p_line != Null(char**) && p_len != Null(short*) && p_char != Nullch) +#endif + return; + if (!using_plan_a) + fatal1("patch: out of memory (grow_hunkmax)\n"); + out_of_mem = TRUE; /* whatever is null will be allocated again */ + /* from within plan_a(), of all places */ +} + +/* True if the remainder of the patch file contains a diff of some sort. */ + +bool +there_is_another_patch() +{ + if (p_base != 0L && p_base >= p_filesize) { + if (verbose) + say1("done\n"); + return FALSE; + } + if (verbose) + say1("Hmm..."); + diff_type = intuit_diff_type(); + if (!diff_type) { + if (p_base != 0L) { + if (verbose) + say1(" Ignoring the trailing garbage.\ndone\n"); + } + else + say1(" I can't seem to find a patch in there anywhere.\n"); + return FALSE; + } + if (verbose) + say3(" %sooks like %s to me...\n", + (p_base == 0L ? "L" : "The next patch l"), + diff_type == CONTEXT_DIFF ? "a context diff" : + diff_type == NEW_CONTEXT_DIFF ? "a new-style context diff" : + diff_type == NORMAL_DIFF ? "a normal diff" : + "an ed script" ); + if (p_indent && verbose) + say3("(Patch is indented %d space%s.)\n", p_indent, p_indent==1?"":"s"); + skip_to(p_start,p_sline); + while (filearg[0] == Nullch) { + if (force) { + say1("No file to patch. Skipping...\n"); + filearg[0] = savestr(bestguess); + return TRUE; + } + ask1("File to patch: "); + if (*buf != '\n') { + if (bestguess) + free(bestguess); + bestguess = savestr(buf); + filearg[0] = fetchname(buf, 0, FALSE); + } + if (filearg[0] == Nullch) { + ask1("No file found--skip this patch? [n] "); + if (*buf != 'y') { + continue; + } + if (verbose) + say1("Skipping patch...\n"); + filearg[0] = fetchname(bestguess, 0, TRUE); + skip_rest_of_patch = TRUE; + return TRUE; + } + } + return TRUE; +} + +/* Determine what kind of diff is in the remaining part of the patch file. */ + +int +intuit_diff_type() +{ + Reg4 long this_line = 0; + Reg5 long previous_line; + Reg6 long first_command_line = -1; + long fcl_line; + Reg7 bool last_line_was_command = FALSE; + Reg8 bool this_is_a_command = FALSE; + Reg9 bool stars_last_line = FALSE; + Reg10 bool stars_this_line = FALSE; + Reg3 int indent; + Reg1 char *s; + Reg2 char *t; + char *indtmp = Nullch; + char *oldtmp = Nullch; + char *newtmp = Nullch; + char *indname = Nullch; + char *oldname = Nullch; + char *newname = Nullch; + Reg11 int retval; + bool no_filearg = (filearg[0] == Nullch); + + ok_to_create_file = FALSE; + Fseek(pfp, p_base, 0); + p_input_line = p_bline - 1; + for (;;) { + previous_line = this_line; + last_line_was_command = this_is_a_command; + stars_last_line = stars_this_line; + this_line = ftell(pfp); + indent = 0; + p_input_line++; + if (fgets(buf, sizeof buf, pfp) == Nullch) { + if (first_command_line >= 0L) { + /* nothing but deletes!? */ + p_start = first_command_line; + p_sline = fcl_line; + retval = ED_DIFF; + goto scan_exit; + } + else { + p_start = this_line; + p_sline = p_input_line; + retval = 0; + goto scan_exit; + } + } + for (s = buf; *s == ' ' || *s == '\t' || *s == 'X'; s++) { + if (*s == '\t') + indent += 8 - (indent % 8); + else + indent++; + } + for (t=s; isdigit(*t) || *t == ','; t++) ; + this_is_a_command = (isdigit(*s) && + (*t == 'd' || *t == 'c' || *t == 'a') ); + if (first_command_line < 0L && this_is_a_command) { + first_command_line = this_line; + fcl_line = p_input_line; + p_indent = indent; /* assume this for now */ + } + if (!stars_last_line && strnEQ(s, "*** ", 4)) + oldtmp = savestr(s+4); + else if (strnEQ(s, "--- ", 4)) + newtmp = savestr(s+4); + else if (strnEQ(s, "Index:", 6)) + indtmp = savestr(s+6); + else if (strnEQ(s, "Prereq:", 7)) { + for (t=s+7; isspace(*t); t++) ; + revision = savestr(t); + for (t=revision; *t && !isspace(*t); t++) ; + *t = '\0'; + if (!*revision) { + free(revision); + revision = Nullch; + } + } + if ((!diff_type || diff_type == ED_DIFF) && + first_command_line >= 0L && + strEQ(s, ".\n") ) { + p_indent = indent; + p_start = first_command_line; + p_sline = fcl_line; + retval = ED_DIFF; + goto scan_exit; + } + stars_this_line = strnEQ(s, "********", 8); + if ((!diff_type || diff_type == CONTEXT_DIFF) && stars_last_line && + strnEQ(s, "*** ", 4)) { + if (!atol(s+4)) + ok_to_create_file = TRUE; + /* if this is a new context diff the character just before */ + /* the newline is a '*'. */ + while (*s != '\n') + s++; + p_indent = indent; + p_start = previous_line; + p_sline = p_input_line - 1; + retval = (*(s-1) == '*' ? NEW_CONTEXT_DIFF : CONTEXT_DIFF); + goto scan_exit; + } + if ((!diff_type || diff_type == NORMAL_DIFF) && + last_line_was_command && + (strnEQ(s, "< ", 2) || strnEQ(s, "> ", 2)) ) { + p_start = previous_line; + p_sline = p_input_line - 1; + p_indent = indent; + retval = NORMAL_DIFF; + goto scan_exit; + } + } + scan_exit: + if (no_filearg) { + if (indtmp != Nullch) + indname = fetchname(indtmp, strippath, ok_to_create_file); + if (oldtmp != Nullch) + oldname = fetchname(oldtmp, strippath, ok_to_create_file); + if (newtmp != Nullch) + newname = fetchname(newtmp, strippath, ok_to_create_file); + if (oldname && newname) { + if (strlen(oldname) < strlen(newname)) + filearg[0] = savestr(oldname); + else + filearg[0] = savestr(newname); + } + else if (oldname) + filearg[0] = savestr(oldname); + else if (newname) + filearg[0] = savestr(newname); + else if (indname) + filearg[0] = savestr(indname); + } + if (bestguess) { + free(bestguess); + bestguess = Nullch; + } + if (filearg[0] != Nullch) + bestguess = savestr(filearg[0]); + else if (indtmp != Nullch) + bestguess = fetchname(indtmp, strippath, TRUE); + else { + if (oldtmp != Nullch) + oldname = fetchname(oldtmp, strippath, TRUE); + if (newtmp != Nullch) + newname = fetchname(newtmp, strippath, TRUE); + if (oldname && newname) { + if (strlen(oldname) < strlen(newname)) + bestguess = savestr(oldname); + else + bestguess = savestr(newname); + } + else if (oldname) + bestguess = savestr(oldname); + else if (newname) + bestguess = savestr(newname); + } + if (indtmp != Nullch) + free(indtmp); + if (oldtmp != Nullch) + free(oldtmp); + if (newtmp != Nullch) + free(newtmp); + if (indname != Nullch) + free(indname); + if (oldname != Nullch) + free(oldname); + if (newname != Nullch) + free(newname); + return retval; +} + +/* Remember where this patch ends so we know where to start up again. */ + +void +next_intuit_at(file_pos,file_line) +long file_pos; +long file_line; +{ + p_base = file_pos; + p_bline = file_line; +} + +/* Basically a verbose fseek() to the actual diff listing. */ + +void +skip_to(file_pos,file_line) +long file_pos; +long file_line; +{ + char *ret; + + assert(p_base <= file_pos); + if (verbose && p_base < file_pos) { + Fseek(pfp, p_base, 0); + say1("The text leading up to this was:\n--------------------------\n"); + while (ftell(pfp) < file_pos) { + ret = fgets(buf, sizeof buf, pfp); + assert(ret != Nullch); + say2("|%s", buf); + } + say1("--------------------------\n"); + } + else + Fseek(pfp, file_pos, 0); + p_input_line = file_line - 1; +} + +/* True if there is more of the current diff listing to process. */ + +bool +another_hunk() +{ + Reg1 char *s; + Reg8 char *ret; + Reg2 int context = 0; + + while (p_end >= 0) { + if (p_end == p_efake) + p_end = p_bfake; /* don't free twice */ +#ifndef SMALL + else + free(p_line[p_end]); +#endif + p_end--; + } + assert(p_end == -1); + p_efake = -1; +#ifdef SMALL + if (sfp != Nullfp) + Fclose(sfp); + sfp = fopen(TMPSTRNAME, "w"); + if (sfp == Nullfp) + fatal2("patch: can't create %s.\n", TMPSTRNAME); +#endif + + p_max = hunkmax; /* gets reduced when --- found */ + if (diff_type == CONTEXT_DIFF || diff_type == NEW_CONTEXT_DIFF) { + long line_beginning = ftell(pfp); + /* file pos of the current line */ + LINENUM repl_beginning = 0; /* index of --- line */ + Reg4 LINENUM fillcnt = 0; /* #lines of missing ptrn or repl */ + Reg5 LINENUM fillsrc; /* index of first line to copy */ + Reg6 LINENUM filldst; /* index of first missing line */ + bool ptrn_spaces_eaten = FALSE; /* ptrn was slightly misformed */ + Reg9 bool repl_could_be_missing = TRUE; + /* no + or ! lines in this hunk */ + bool repl_missing = FALSE; /* we are now backtracking */ + long repl_backtrack_position = 0; + /* file pos of first repl line */ + LINENUM repl_patch_line; /* input line number for same */ + Reg7 LINENUM ptrn_copiable = 0; + /* # of copiable lines in ptrn */ + + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch || strnNE(buf, "********", 8)) { + next_intuit_at(line_beginning,p_input_line); + return FALSE; + } + p_context = 100; + p_hunk_beg = p_input_line + 1; + while (p_end < p_max) { + line_beginning = ftell(pfp); + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) { + if (p_max - p_end < 4) + Strcpy(buf, " \n"); /* assume blank lines got chopped */ + else { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + fatal1("Unexpected end of file in patch.\n"); + } + } + p_end++; + assert(p_end < hunkmax); + p_char[p_end] = *buf; +#ifdef zilog + p_line[(short)p_end] = Nullch; +#else +#ifdef SMALL + p_line[p_end] = -1L; + p_len[p_end] = 0; +#else + p_line[p_end] = Nullch; +#endif +#endif + switch (*buf) { + case '*': + if (strnEQ(buf, "********", 8)) { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + else + fatal2("Unexpected end of hunk at line %ld.\n", + p_input_line); + } + if (p_end != 0) { + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + fatal3("Unexpected *** at line %ld: %s", p_input_line, buf); + } + context = 0; +#ifdef SMALL + p_line[p_end] = saveStr(buf, p_len+p_end); +#else + p_line[p_end] = savestr(buf); + if (out_of_mem) { + p_end--; + return FALSE; + } +#endif + for (s=buf; *s && !isdigit(*s); s++) ; + if (!*s) + goto malformed; + if (strnEQ(s,"0,0",3)) + strcpy(s,s+2); + p_first = (LINENUM) atol(s); + while (isdigit(*s)) s++; + if (*s == ',') { + for (; *s && !isdigit(*s); s++) ; + if (!*s) + goto malformed; + p_ptrn_lines = ((LINENUM)atol(s)) - p_first + 1; + } + else if (p_first) + p_ptrn_lines = 1; + else { + p_ptrn_lines = 0; + p_first = 1; + } + p_max = p_ptrn_lines + 6; /* we need this much at least */ + while (p_max >= hunkmax) + grow_hunkmax(); + p_max = hunkmax; + break; + case '-': + if (buf[1] == '-') { + if (repl_beginning || + (p_end != p_ptrn_lines + 1 + (p_char[p_end-1] == '\n'))) + { + if (p_end == 1) { + /* `old' lines were omitted - set up to fill */ + /* them in from 'new' context lines. */ + p_end = p_ptrn_lines + 1; + fillsrc = p_end + 1; + filldst = 1; + fillcnt = p_ptrn_lines; + } + else { + if (repl_beginning) { + if (repl_could_be_missing){ + repl_missing = TRUE; + goto hunk_done; + } + fatal3( +"Duplicate \"---\" at line %ld--check line numbers at line %ld.\n", + p_input_line, p_hunk_beg + repl_beginning); + } + else { + fatal4( +"%s \"---\" at line %ld--check line numbers at line %ld.\n", + (p_end <= p_ptrn_lines + ? "Premature" + : "Overdue" ), + p_input_line, p_hunk_beg); + } + } + } + repl_beginning = p_end; + repl_backtrack_position = ftell(pfp); + repl_patch_line = p_input_line; +#ifdef SMALL + p_line[p_end] = saveStr(buf, p_len+p_end); +#else + p_line[p_end] = savestr(buf); + if (out_of_mem) { + p_end--; + return FALSE; + } +#endif + p_char[p_end] = '='; + for (s=buf; *s && !isdigit(*s); s++) ; + if (!*s) + goto malformed; + p_newfirst = (LINENUM) atol(s); + while (isdigit(*s)) s++; + if (*s == ',') { + for (; *s && !isdigit(*s); s++) ; + if (!*s) + goto malformed; + p_repl_lines = ((LINENUM)atol(s)) - p_newfirst + 1; + } + else if (p_newfirst) + p_repl_lines = 1; + else { + p_repl_lines = 0; + p_newfirst = 1; + } + p_max = p_repl_lines + p_end; + if (p_max > MAXHUNKSIZE) + fatal4("Hunk too large (%ld lines) at line %ld: %s", + p_max, p_input_line, buf); + while (p_max >= hunkmax) + grow_hunkmax(); + if (p_repl_lines != ptrn_copiable) + repl_could_be_missing = FALSE; + break; + } + goto change_line; + case '+': case '!': + repl_could_be_missing = FALSE; + change_line: + if (buf[1] == '\n' && canonicalize) + strcpy(buf+1," \n"); + if (!isspace(buf[1]) && buf[1] != '>' && buf[1] != '<' && + repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + if (context > 0) { + if (context < p_context) + p_context = context; + context = -1000; + } +#ifdef SMALL + p_line[p_end] = saveStr(buf+2, p_len+p_end); +#else + p_line[p_end] = savestr(buf+2); + if (out_of_mem) { + p_end--; + return FALSE; + } +#endif + break; + case '\t': case '\n': /* assume the 2 spaces got eaten */ + if (repl_beginning && repl_could_be_missing && + (!ptrn_spaces_eaten || diff_type == NEW_CONTEXT_DIFF) ) { + repl_missing = TRUE; + goto hunk_done; + } +#ifdef SMALL + p_line[p_end] = saveStr(buf, p_len+p_end); +#else + p_line[p_end] = savestr(buf); + if (out_of_mem) { + p_end--; + return FALSE; + } +#endif + if (p_end != p_ptrn_lines + 1) { + ptrn_spaces_eaten |= (repl_beginning != 0); + context++; + if (!repl_beginning) + ptrn_copiable++; + p_char[p_end] = ' '; + } + break; + case ' ': + if (!isspace(buf[1]) && + repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + context++; + if (!repl_beginning) + ptrn_copiable++; +#ifdef SMALL + p_line[p_end] = saveStr(buf+2, p_len+p_end); +#else + p_line[p_end] = savestr(buf+2); + if (out_of_mem) { + p_end--; + return FALSE; + } +#endif + break; + default: + if (repl_beginning && repl_could_be_missing) { + repl_missing = TRUE; + goto hunk_done; + } + goto malformed; + } + /* set up p_len for strncmp() so we don't have to */ + /* assume null termination */ +#ifndef SMALL + if (p_line[p_end]) + p_len[p_end] = strlen(p_line[p_end]); + else + p_len[p_end] = 0; +#endif + } + + hunk_done: + if (p_end >=0 && !repl_beginning) + fatal2("No --- found in patch at line %ld\n", pch_hunk_beg()); + + if (repl_missing) { + + /* reset state back to just after --- */ + p_input_line = repl_patch_line; +#ifndef SMALL + for (p_end--; p_end > repl_beginning; p_end--) + free(p_line[p_end]); +#endif + Fseek(pfp, repl_backtrack_position, 0); + + /* redundant 'new' context lines were omitted - set */ + /* up to fill them in from the old file context */ + fillsrc = 1; + filldst = repl_beginning+1; + fillcnt = p_repl_lines; + p_end = p_max; + } + + if (diff_type == CONTEXT_DIFF && + (fillcnt || (p_first > 1 && ptrn_copiable > 2*p_context)) ) { + if (verbose) + say4("%s\n%s\n%s\n", +"(Fascinating--this is really a new-style context diff but without", +"the telltale extra asterisks on the *** line that usually indicate", +"the new style...)"); + diff_type = NEW_CONTEXT_DIFF; + } + + /* if there were omitted context lines, fill them in now */ + if (fillcnt) { + p_bfake = filldst; /* remember where not to free() */ + p_efake = filldst + fillcnt - 1; + while (fillcnt-- > 0) { + while (fillsrc <= p_end && p_char[fillsrc] != ' ') + fillsrc++; + if (fillsrc > p_end) + fatal2("Replacement text or line numbers mangled in hunk at line %ld\n", + p_hunk_beg); + p_line[filldst] = p_line[fillsrc]; + p_char[filldst] = p_char[fillsrc]; + p_len[filldst] = p_len[fillsrc]; + fillsrc++; filldst++; + } + while (fillsrc <= p_end && fillsrc != repl_beginning && + p_char[fillsrc] != ' ') + fillsrc++; +#ifdef DEBUGGING + if (debug & 64) + printf("fillsrc %ld, filldst %ld, rb %ld, e+1 %ld\n", + fillsrc,filldst,repl_beginning,p_end+1); +#endif + assert(fillsrc==p_end+1 || fillsrc==repl_beginning); + assert(filldst==p_end+1 || filldst==repl_beginning); + } + } + else { /* normal diff--fake it up */ + char hunk_type; + Reg3 int i; + LINENUM min, max; + long line_beginning = ftell(pfp); + + p_context = 0; + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch || !isdigit(*buf)) { + next_intuit_at(line_beginning,p_input_line); + return FALSE; + } + p_first = (LINENUM)atol(buf); + for (s=buf; isdigit(*s); s++) ; + if (*s == ',') { + p_ptrn_lines = (LINENUM)atol(++s) - p_first + 1; + while (isdigit(*s)) s++; + } + else + p_ptrn_lines = (*s != 'a'); + hunk_type = *s; + if (hunk_type == 'a') + p_first++; /* do append rather than insert */ + min = (LINENUM)atol(++s); + for (; isdigit(*s); s++) ; + if (*s == ',') + max = (LINENUM)atol(++s); + else + max = min; + if (hunk_type == 'd') + min++; + p_end = p_ptrn_lines + 1 + max - min + 1; + if (p_end > MAXHUNKSIZE) + fatal4("Hunk too large (%ld lines) at line %ld: %s", + p_end, p_input_line, buf); + while (p_end >= hunkmax) + grow_hunkmax(); + p_newfirst = min; + p_repl_lines = max - min + 1; + Sprintf(buf, "*** %ld,%ld\n", p_first, p_first + p_ptrn_lines - 1); +#ifdef SMALL + p_line[0] = saveStr(buf, p_len); +#else + p_line[0] = savestr(buf); + if (out_of_mem) { + p_end = -1; + return FALSE; + } +#endif + p_char[0] = '*'; + for (i=1; i<=p_ptrn_lines; i++) { + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) + fatal2("Unexpected end of file in patch at line %ld.\n", + p_input_line); + if (*buf != '<') + fatal2("< expected at line %ld of patch.\n", p_input_line); +#ifdef SMALL + p_line[i] = saveStr(buf+2, p_len+i); +#else + p_line[i] = savestr(buf+2); + if (out_of_mem) { + p_end = i-1; + return FALSE; + } +#endif +#ifndef SMALL + p_len[i] = strlen(p_line[i]); +#endif + p_char[i] = '-'; + } + if (hunk_type == 'c') { + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) + fatal2("Unexpected end of file in patch at line %ld.\n", + p_input_line); + if (*buf != '-') + fatal2("--- expected at line %ld of patch.\n", p_input_line); + } + Sprintf(buf, "--- %ld,%ld\n", min, max); +#ifdef SMALL + p_line[i] = saveStr(buf, p_len+i); +#else + p_line[i] = savestr(buf); + if (out_of_mem) { + p_end = i-1; + return FALSE; + } +#endif + p_char[i] = '='; + for (i++; i<=p_end; i++) { + ret = pgets(buf, sizeof buf, pfp); + p_input_line++; + if (ret == Nullch) + fatal2("Unexpected end of file in patch at line %ld.\n", + p_input_line); + if (*buf != '>') + fatal2("> expected at line %ld of patch.\n", p_input_line); +#ifdef SMALL + p_line[i] = saveStr(buf+2, p_len+i); +#else + p_line[i] = savestr(buf+2); + if (out_of_mem) { + p_end = i-1; + return FALSE; + } +#endif +#ifndef SMALL + p_len[i] = strlen(p_line[i]); +#endif + p_char[i] = '+'; + } + } + if (reverse) /* backwards patch? */ + if (!pch_swap()) + say1("Not enough memory to swap next hunk!\n"); +#ifdef SMALL + Fclose(sfp); + sfp = fopen(TMPSTRNAME, "r"); +#endif +#ifdef DEBUGGING + if (debug & 2) { + int i; + char special; + + for (i=0; i <= p_end; i++) { + if (i == p_ptrn_lines) + special = '^'; + else + special = ' '; +#ifdef SMALL + fprintf(stderr, "%3d %c %c %s", i, p_char[i], special, pfetch(i)); +#else + fprintf(stderr, "%3d %c %c %s", i, p_char[i], special, p_line[i]); +#endif + Fflush(stderr); + } + } +#endif + if (p_end+1 < hunkmax) /* paranoia reigns supreme... */ + p_char[p_end+1] = '^'; /* add a stopper for apply_hunk */ + return TRUE; + +malformed: +#ifdef SMALL + Fclose(sfp); + sfp = Nullfp; +#endif + fatal3("Malformed patch at line %ld: %s", p_input_line, buf); + /* about as informative as "Syntax error" in C */ + return FALSE; /* for lint */ +} + +/* Input a line from the patch file, worrying about indentation. */ + +char * +pgets(bf,sz,fp) +char *bf; +int sz; +FILE *fp; +{ + char *ret = fgets(bf, sz, fp); + Reg1 char *s; + Reg2 int indent = 0; + + if (p_indent && ret != Nullch) { + for (s=buf; + indent < p_indent && (*s == ' ' || *s == '\t' || *s == 'X'); s++) { + if (*s == '\t') + indent += 8 - (indent % 7); + else + indent++; + } + if (buf != s) + Strcpy(buf, s); + } + return ret; +} + +/* Reverse the old and new portions of the current hunk. */ + +bool +pch_swap() +{ +#ifdef SMALL + long *tp_line; /* the text of the hunk */ +#else + char **tp_line; /* the text of the hunk */ +#endif + short *tp_len; /* length of each line */ + char *tp_char; /* +, -, and ! */ + Reg1 LINENUM i; + Reg2 LINENUM n; + bool blankline = FALSE; + Reg3 char *s; + + i = p_first; + p_first = p_newfirst; + p_newfirst = i; + + /* make a scratch copy */ + + tp_line = p_line; + tp_len = p_len; + tp_char = p_char; +#ifdef SMALL + p_line = Null(long*); /* force set_hunkmax to allocate again */ +#else + p_line = Null(char**); /* force set_hunkmax to allocate again */ +#endif + p_len = Null(short*); + p_char = Nullch; + set_hunkmax(); +#ifdef SMALL + if (p_line == Null(long*) || p_len == Null(short*) || p_char == Nullch) { +#else + if (p_line == Null(char**) || p_len == Null(short*) || p_char == Nullch) { +#endif +#ifndef lint +#ifdef SMALL + if (p_line == Null(long*)) +#else + if (p_line == Null(char**)) +#endif + free((char*)p_line); + p_line = tp_line; + if (p_len == Null(short*)) + free((char*)p_len); + p_len = tp_len; +#endif + if (p_char == Nullch) + free((char*)p_char); + p_char = tp_char; + return FALSE; /* not enough memory to swap hunk! */ + } + + /* now turn the new into the old */ + + i = p_ptrn_lines + 1; + if (tp_char[i] == '\n') { /* account for possible blank line */ + blankline = TRUE; + i++; + } + if (p_efake >= 0) { /* fix non-freeable ptr range */ + if (p_efake <= i) + n = p_end - i + 1; + else + n = -i; + p_efake += n; + p_bfake += n; + } + for (n=0; i <= p_end; i++,n++) { + p_line[n] = tp_line[i]; + p_char[n] = tp_char[i]; + if (p_char[n] == '+') + p_char[n] = '-'; + p_len[n] = tp_len[i]; + } + if (blankline) { + i = p_ptrn_lines + 1; + p_line[n] = tp_line[i]; + p_char[n] = tp_char[i]; + p_len[n] = tp_len[i]; + n++; + } + assert(p_char[0] == '='); + p_char[0] = '*'; +#ifdef SMALL + strEdit(p_line[0], '*', '-'); +#else + for (s=p_line[0]; *s; s++) + if (*s == '-') + *s = '*'; +#endif + + /* now turn the old into the new */ + + assert(tp_char[0] == '*'); + tp_char[0] = '='; +#ifdef SMALL + strEdit(tp_line[0], '-', '*'); +#else + for (s=tp_line[0]; *s; s++) + if (*s == '*') + *s = '-'; +#endif + for (i=0; n <= p_end; i++,n++) { + p_line[n] = tp_line[i]; + p_char[n] = tp_char[i]; + if (p_char[n] == '-') + p_char[n] = '+'; + p_len[n] = tp_len[i]; + } + assert(i == p_ptrn_lines + 1); + i = p_ptrn_lines; + p_ptrn_lines = p_repl_lines; + p_repl_lines = i; +#ifndef lint +#ifdef SMALL + if (tp_line == Null(long*)) +#else + if (tp_line == Null(char**)) +#endif + free((char*)tp_line); + if (tp_len == Null(short*)) + free((char*)tp_len); +#endif + if (tp_char == Nullch) + free((char*)tp_char); + return TRUE; +} + +/* Return the specified line position in the old file of the old context. */ + +LINENUM +pch_first() +{ + return p_first; +} + +/* Return the number of lines of old context. */ + +LINENUM +pch_ptrn_lines() +{ + return p_ptrn_lines; +} + +/* Return the probable line position in the new file of the first line. */ + +LINENUM +pch_newfirst() +{ + return p_newfirst; +} + +/* Return the number of lines in the replacement text including context. */ + +LINENUM +pch_repl_lines() +{ + return p_repl_lines; +} + +/* Return the number of lines in the whole hunk. */ + +LINENUM +pch_end() +{ + return p_end; +} + +/* Return the number of context lines before the first changed line. */ + +LINENUM +pch_context() +{ + return p_context; +} + +/* Return the length of a particular patch line. */ + +short +pch_line_len(line) +LINENUM line; +{ + return p_len[line]; +} + +/* Return the control character (+, -, *, !, etc) for a patch line. */ + +char +pch_char(line) +LINENUM line; +{ + return p_char[line]; +} + +/* Return a pointer to a particular patch line. */ + +#ifdef SMALL +long +saveStr(str, plen) +char *str; +short *plen; +{ + long pos, ftell(); + int len; + + pos = ftell(sfp); + len = strlen(str); + fwrite(str, sizeof(char), len+1, sfp); + *plen = len; + return pos; +} + +char * +pfetch(line) +LINENUM line; +{ + static char *s, strbuf[BUFSIZ]; + int i, c; + + if (p_line[line] == -1L) + return Nullch; + else { + Fseek(sfp, p_line[line], 0); + for (i = 0, s = strbuf; + i < BUFSIZ && (c = fgetc(sfp)) != EOF && c; i++) + *s++ = c; + if (i == BUFSIZ) + fatal2("too long line (%.40s ..\n", strbuf); + } + *s = '\0'; + return strbuf; +} + +void +strEdit(pos, to, from) +long pos; +int to, from; +{ + static char *s, strbuf[BUFSIZ]; + int i, c; + + if (pos != -1L) { + for (i = 0, s = strbuf; + i < BUFSIZ && (c = fgetc(sfp)) != EOF && c; i++) + *s++ = c; + for (s = strbuf; *s; s++) + if (*s == from) + *s = to; + fwrite(strbuf, sizeof(char), i+1, sfp); + } +} +#else +char * +pfetch(line) +LINENUM line; +{ + return p_line[line]; +} +#endif + +/* Return where in the patch file this hunk began, for error messages. */ + +LINENUM +pch_hunk_beg() +{ + return p_hunk_beg; +} + +/* Apply an ed script by feeding ed itself. */ + +void +do_ed_script() +{ + Reg1 char *t; + Reg2 long beginning_of_this_line; + Reg3 bool this_line_is_command = FALSE; + Reg4 FILE *pipefp; + + if (!skip_rest_of_patch) { + Unlink(TMPOUTNAME); + copy_file(filearg[0], TMPOUTNAME); + if (verbose) + Sprintf(buf, "/bin/ed %s", TMPOUTNAME); + else + Sprintf(buf, "/bin/ed - %s", TMPOUTNAME); + pipefp = popen(buf, "w"); + } + for (;;) { + beginning_of_this_line = ftell(pfp); + if (pgets(buf, sizeof buf, pfp) == Nullch) { + next_intuit_at(beginning_of_this_line,p_input_line); + break; + } + p_input_line++; + for (t=buf; isdigit(*t) || *t == ','; t++) ; + this_line_is_command = (isdigit(*buf) && + (*t == 'd' || *t == 'c' || *t == 'a') ); + if (this_line_is_command) { + if (!skip_rest_of_patch) + fputs(buf, pipefp); + if (*t != 'd') { + while (pgets(buf, sizeof buf, pfp) != Nullch) { + p_input_line++; + if (!skip_rest_of_patch) + fputs(buf, pipefp); + if (strEQ(buf, ".\n")) + break; + } + } + } + else { + next_intuit_at(beginning_of_this_line,p_input_line); + break; + } + } + if (skip_rest_of_patch) + return; + fprintf(pipefp, "w\n"); + fprintf(pipefp, "q\n"); + Fflush(pipefp); + Pclose(pipefp); + ignore_signals(); + if (move_file(TMPOUTNAME, outname) < 0) { + toutkeep = TRUE; + chmod(TMPOUTNAME, filemode); + } + else + chmod(outname, filemode); + set_signals(1); +} diff --git a/commands/patch/pch.h b/commands/patch/pch.h new file mode 100755 index 000000000..8b746e3e2 --- /dev/null +++ b/commands/patch/pch.h @@ -0,0 +1,48 @@ +/* $Header$ + * + * $Log$ + * Revision 1.1 2005/04/21 14:55:11 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:19 beng + * Initial import of minix 2.0.4 + * + * Revision 2.0.1.1 87/01/30 22:47:16 lwall + * Added do_ed_script(). + * + * Revision 2.0 86/09/17 15:39:57 lwall + * Baseline for netwide release. + * + */ + +EXT FILE *pfp INIT(Nullfp); /* patch file pointer */ +#ifdef SMALL +EXT FILE *sfp INIT(Nullfp); /* string file pointer */ +#endif + +_PROTOTYPE(void re_patch , (void)); +_PROTOTYPE(void open_patch_file , (char *filename )); +_PROTOTYPE(void set_hunkmax , (void)); +_PROTOTYPE(void grow_hunkmax , (void)); +_PROTOTYPE(bool there_is_another_patch , (void)); +_PROTOTYPE(int intuit_diff_type , (void)); +_PROTOTYPE(void next_intuit_at , (long file_pos , long file_line )); +_PROTOTYPE(void skip_to , (long file_pos , long file_line )); +_PROTOTYPE(bool another_hunk , (void)); +_PROTOTYPE(char *pgets , (char *bf , int sz , FILE *fp )); +_PROTOTYPE(bool pch_swap , (void)); +_PROTOTYPE(LINENUM pch_first , (void)); +_PROTOTYPE(LINENUM pch_ptrn_lines , (void)); +_PROTOTYPE(LINENUM pch_newfirst , (void)); +_PROTOTYPE(LINENUM pch_repl_lines , (void)); +_PROTOTYPE(LINENUM pch_end , (void)); +_PROTOTYPE(LINENUM pch_context , (void)); +_PROTOTYPE(short pch_line_len , (LINENUM line )); +_PROTOTYPE(char pch_char , (LINENUM line )); +_PROTOTYPE(char *pfetch , (LINENUM line )); +_PROTOTYPE(LINENUM pch_hunk_beg , (void)); +_PROTOTYPE(void do_ed_script , (void)); +#ifdef SMALL +_PROTOTYPE(long saveStr , (char *string , short *length )); +_PROTOTYPE(void strEdit , (long pos , int from , int to )); +#endif diff --git a/commands/patch/util.c b/commands/patch/util.c new file mode 100755 index 000000000..48a18b961 --- /dev/null +++ b/commands/patch/util.c @@ -0,0 +1,361 @@ +#include "EXTERN.h" +#include "common.h" +#include "INTERN.h" +#include "util.h" + +/* Rename a file, copying it if necessary. */ + +int +move_file(from,to) +char *from, *to; +{ + char bakname[512]; + Reg1 char *s; + Reg2 int i; + Reg3 int fromfd; + + /* to stdout? */ + + if (strEQ(to, "-")) { +#ifdef DEBUGGING + if (debug & 4) + say2("Moving %s to stdout.\n", from); +#endif + fromfd = open(from, 0); + if (fromfd < 0) + fatal2("patch: internal error, can't reopen %s\n", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(1, buf, i) != i) + fatal1("patch: write failed\n"); + Close(fromfd); + return 0; + } + + if (origprae) { + Strcpy (bakname, origprae); + Strcat(bakname, to); + } else { + Strcpy(bakname, to); + Strcat(bakname, origext?origext:ORIGEXT); + } + if (stat(to, &filestat) >= 0) { /* output file exists */ + dev_t to_device = filestat.st_dev; + ino_t to_inode = filestat.st_ino; + char *simplename = bakname; + + for (s=bakname; *s; s++) { + if (*s == '/') + simplename = s+1; + } + /* find a backup name that is not the same file */ + while (stat(bakname, &filestat) >= 0 && + to_device == filestat.st_dev && to_inode == filestat.st_ino) { + for (s=simplename; *s && !islower(*s); s++) ; + if (*s) + *s = toupper(*s); + else + Strcpy(simplename, simplename+1); + } + while (unlink(bakname) >= 0) ; /* while() is for benefit of Eunice */ +#ifdef DEBUGGING + if (debug & 4) + say3("Moving %s to %s.\n", to, bakname); +#endif + if (link(to, bakname) < 0) { + say3("patch: can't backup %s, output is in %s\n", + to, from); + return -1; + } + while (unlink(to) >= 0) ; + } +#ifdef DEBUGGING + if (debug & 4) + say3("Moving %s to %s.\n", from, to); +#endif + if (link(from, to) < 0) { /* different file system? */ + Reg4 int tofd; + + tofd = creat(to, 0666); + if (tofd < 0) { + say3("patch: can't create %s, output is in %s.\n", + to, from); + return -1; + } + fromfd = open(from, 0); + if (fromfd < 0) + fatal2("patch: internal error, can't reopen %s\n", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(tofd, buf, i) != i) + fatal1("patch: write failed\n"); + Close(fromfd); + Close(tofd); + } + Unlink(from); + return 0; +} + +/* Copy a file. */ + +void +copy_file(from,to) +char *from, *to; +{ + Reg3 int tofd; + Reg2 int fromfd; + Reg1 int i; + + tofd = creat(to, 0666); + if (tofd < 0) + fatal2("patch: can't create %s.\n", to); + fromfd = open(from, 0); + if (fromfd < 0) + fatal2("patch: internal error, can't reopen %s\n", from); + while ((i=read(fromfd, buf, sizeof buf)) > 0) + if (write(tofd, buf, i) != i) + fatal2("patch: write (%s) failed\n", to); + Close(fromfd); + Close(tofd); +} + +/* Allocate a unique area for a string. */ + +char * +savestr(s) +Reg1 char *s; +{ + Reg3 char *rv; + Reg2 char *t; + + if (!s) + s = "Oops"; + t = s; + while (*t++); + rv = (char *)malloc((MEM) (t - s)); + if (rv == Nullch) { + if (using_plan_a) + out_of_mem = TRUE; + else + fatal1("patch: out of memory (savestr)\n"); + } + else { + t = rv; + while (*t++ = *s++); + } + return rv; +} + +#if defined(lint) && defined(CANVARARG) + +/*VARARGS ARGSUSED*/ +say(pat) char *pat; { ; } +/*VARARGS ARGSUSED*/ +fatal(pat) char *pat; { ; } +/*VARARGS ARGSUSED*/ +ask(pat) char *pat; { ; } + +#else + +/* Vanilla terminal output (buffered). */ + +void +say(pat,arg1,arg2,arg3) +char *pat; +long arg1,arg2,arg3; +{ + fprintf(stderr, pat, arg1, arg2, arg3); + Fflush(stderr); +} + +/* Terminal output, pun intended. */ + +void /* very void */ +fatal(pat,arg1,arg2,arg3) +char *pat; +long arg1,arg2,arg3; +{ + say(pat, arg1, arg2, arg3); + my_exit(1); +} + +/* Get a response from the user, somehow or other. */ + +void +ask(pat,arg1,arg2,arg3) +char *pat; +long arg1,arg2,arg3; +{ + int ttyfd; + int r; + bool tty2 = isatty(2); + + Sprintf(buf, pat, arg1, arg2, arg3); + Fflush(stderr); + write(2, buf, strlen(buf)); + if (tty2) { /* might be redirected to a file */ + r = read(2, buf, sizeof buf); + } + else if (isatty(1)) { /* this may be new file output */ + Fflush(stdout); + write(1, buf, strlen(buf)); + r = read(1, buf, sizeof buf); + } + else if ((ttyfd = open("/dev/tty", 2)) >= 0 && isatty(ttyfd)) { + /* might be deleted or unwriteable */ + write(ttyfd, buf, strlen(buf)); + r = read(ttyfd, buf, sizeof buf); + Close(ttyfd); + } + else if (isatty(0)) { /* this is probably patch input */ + Fflush(stdin); + write(0, buf, strlen(buf)); + r = read(0, buf, sizeof buf); + } + else { /* no terminal at all--default it */ + buf[0] = '\n'; + r = 1; + } + if (r <= 0) + buf[0] = 0; + else + buf[r] = '\0'; + if (!tty2) + say1(buf); +} +#endif /* lint */ + +/* How to handle certain events when not in a critical region. */ + +void +set_signals(reset) +int reset; +{ +#ifndef lint +#ifdef VOIDSIG + static void (*hupval)(),(*intval)(); +#else + static int (*hupval)(),(*intval)(); +#endif + + if (!reset) { + hupval = signal(SIGHUP, SIG_IGN); + if (hupval != SIG_IGN) +#ifdef VOIDSIG + hupval = my_exit; +#else + hupval = (int(*)())my_exit; +#endif + intval = signal(SIGINT, SIG_IGN); + if (intval != SIG_IGN) +#ifdef VOIDSIG + intval = my_exit; +#else + intval = (int(*)())my_exit; +#endif + } + Signal(SIGHUP, hupval); + Signal(SIGINT, intval); +#endif +} + +/* How to handle certain events when in a critical region. */ + +void +ignore_signals() +{ +#ifndef lint + Signal(SIGHUP, SIG_IGN); + Signal(SIGINT, SIG_IGN); +#endif +} + +/* Make sure we'll have the directories to create a file. */ + +void +makedirs(filename,striplast) +Reg1 char *filename; +bool striplast; +{ + char tmpbuf[256]; + Reg2 char *s = tmpbuf; + char *dirv[20]; + Reg3 int i; + Reg4 int dirvp = 0; + + while (*filename) { + if (*filename == '/') { + filename++; + dirv[dirvp++] = s; + *s++ = '\0'; + } + else { + *s++ = *filename++; + } + } + *s = '\0'; + dirv[dirvp] = s; + if (striplast) + dirvp--; + if (dirvp < 0) + return; + strcpy(buf, "mkdir"); + s = buf; + for (i=0; i<=dirvp; i++) { + while (*s) s++; + *s++ = ' '; + strcpy(s, tmpbuf); + *dirv[i] = '/'; + } + system(buf); +} + +/* Make filenames more reasonable. */ + +char * +fetchname(at,strip_leading,assume_exists) +char *at; +int strip_leading; +int assume_exists; +{ + char *s; + char *name; + Reg1 char *t; + char tmpbuf[200]; + + if (!at) + return Nullch; + s = savestr(at); + for (t=s; isspace(*t); t++) ; + name = t; +#ifdef DEBUGGING + if (debug & 128) + say4("fetchname %s %d %d\n",name,strip_leading,assume_exists); +#endif + if (strnEQ(name, "/dev/null", 9)) /* so files can be created by diffing */ + return Nullch; /* against /dev/null. */ + for (; *t && !isspace(*t); t++) + if (*t == '/') + if (--strip_leading >= 0) + name = t+1; + *t = '\0'; + if (name != s && *s != '/') { + name[-1] = '\0'; + if (stat(s, &filestat) && filestat.st_mode & S_IFDIR) { + name[-1] = '/'; + name=s; + } + } + name = savestr(name); + Sprintf(tmpbuf, "RCS/%s", name); + free(s); + if (stat(name, &filestat) < 0 && !assume_exists) { + Strcat(tmpbuf, RCSSUFFIX); + if (stat(tmpbuf, &filestat) < 0 && stat(tmpbuf+4, &filestat) < 0) { + Sprintf(tmpbuf, "SCCS/%s%s", SCCSPREFIX, name); + if (stat(tmpbuf, &filestat) < 0 && stat(tmpbuf+5, &filestat) < 0) { + free(name); + name = Nullch; + } + } + } + return name; +} diff --git a/commands/patch/util.h b/commands/patch/util.h new file mode 100755 index 000000000..e09031b77 --- /dev/null +++ b/commands/patch/util.h @@ -0,0 +1,80 @@ +/* $Header$ + * + * $Log$ + * Revision 1.1 2005/04/21 14:55:11 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:20 beng + * Initial import of minix 2.0.4 + * + * Revision 2.0 86/09/17 15:40:06 lwall + * Baseline for netwide release. + * + */ + +/* and for those machine that can't handle a variable argument list */ + +#ifdef CANVARARG + +#define say1 say +#define say2 say +#define say3 say +#define say4 say +#define ask1 ask +#define ask2 ask +#define ask3 ask +#define ask4 ask +#define fatal1 fatal +#define fatal2 fatal +#define fatal3 fatal +#define fatal4 fatal + +#else /* hope they allow multi-line macro actual arguments */ + +#ifdef lint + +#define say1(a) say(a, 0, 0, 0) +#define say2(a,b) say(a, (b)==(b), 0, 0) +#define say3(a,b,c) say(a, (b)==(b), (c)==(c), 0) +#define say4(a,b,c,d) say(a, (b)==(b), (c)==(c), (d)==(d)) +#define ask1(a) ask(a, 0, 0, 0) +#define ask2(a,b) ask(a, (b)==(b), 0, 0) +#define ask3(a,b,c) ask(a, (b)==(b), (c)==(c), 0) +#define ask4(a,b,c,d) ask(a, (b)==(b), (c)==(c), (d)==(d)) +#define fatal1(a) fatal(a, 0, 0, 0) +#define fatal2(a,b) fatal(a, (b)==(b), 0, 0) +#define fatal3(a,b,c) fatal(a, (b)==(b), (c)==(c), 0) +#define fatal4(a,b,c,d) fatal(a, (b)==(b), (c)==(c), (d)==(d)) + +#else /* lint */ + /* if this doesn't work, try defining CANVARARG above */ +#define say1(a) say(a, Nullch, Nullch, Nullch) +#define say2(a,b) say(a, b, Nullch, Nullch) +#define say3(a,b,c) say(a, b, c, Nullch) +#define say4 say +#define ask1(a) ask(a, Nullch, Nullch, Nullch) +#define ask2(a,b) ask(a, b, Nullch, Nullch) +#define ask3(a,b,c) ask(a, b, c, Nullch) +#define ask4 ask +#define fatal1(a) fatal(a, Nullch, Nullch, Nullch) +#define fatal2(a,b) fatal(a, b, Nullch, Nullch) +#define fatal3(a,b,c) fatal(a, b, c, Nullch) +#define fatal4 fatal + +#endif /* lint */ + +/* if neither of the above work, join all multi-line macro calls. */ +#endif + +EXT char serrbuf[BUFSIZ]; /* buffer for stderr */ + +_PROTOTYPE(int move_file , (char *from , char *to )); +_PROTOTYPE(void copy_file , (char *from , char *to )); +_PROTOTYPE(char *savestr , (Reg1 char *s )); +void say(); +void fatal(); +void ask(); +_PROTOTYPE(void set_signals , (int reset )); +_PROTOTYPE(void ignore_signals , (void)); +_PROTOTYPE(void makedirs , (Reg1 char *filename , bool striplast )); +_PROTOTYPE(char *fetchname , (char *at , int strip_leading , int assume_exists )); diff --git a/commands/patch/version.c b/commands/patch/version.c new file mode 100755 index 000000000..6b1b16ce9 --- /dev/null +++ b/commands/patch/version.c @@ -0,0 +1,34 @@ +/* $Header$ + * + * $Log$ + * Revision 1.1 2005/04/21 14:55:11 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:20 beng + * Initial import of minix 2.0.4 + * + * Revision 2.0 86/09/17 15:40:11 lwall + * Baseline for netwide release. + * + */ + +#include "EXTERN.h" +#include "common.h" +#include "util.h" +#include "INTERN.h" +#include "patchlevel.h" +#include "version.h" + +/* Print out the version number and die. */ + +void +version() +{ + extern char rcsid[]; + +#ifdef lint + rcsid[0] = rcsid[0]; +#else + fatal3("%s\nPatch level: %d\n", rcsid, PATCHLEVEL); +#endif +} diff --git a/commands/patch/version.h b/commands/patch/version.h new file mode 100755 index 000000000..1a642bfa1 --- /dev/null +++ b/commands/patch/version.h @@ -0,0 +1,15 @@ +/* $Header$ + * + * $Log$ + * Revision 1.1 2005/04/21 14:55:11 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:20 beng + * Initial import of minix 2.0.4 + * + * Revision 2.0 86/09/17 15:40:14 lwall + * Baseline for netwide release. + * + */ + +_PROTOTYPE(void version , (void)); diff --git a/commands/ps/Makefile b/commands/ps/Makefile new file mode 100644 index 000000000..7cc186f26 --- /dev/null +++ b/commands/ps/Makefile @@ -0,0 +1,23 @@ +# Makefile for the process status utility. +# +u=/usr +CC= exec cc +CFLAGS= -O -D_MINIX -D_POSIX_SOURCE +MAKE= exec make -$(MAKEFLAGS) + +# process status utility +ps: ps.c /usr/include/minix/config.h /usr/include/minix/const.h \ + /usr/src/kernel/const.h /usr/src/kernel/type.h \ + /usr/src/kernel/proc.h /usr/src/servers/mm/mproc.h \ + /usr/src/servers/fs/fproc.h /usr/src/servers/fs/const.h + $(CC) -i $(CFLAGS) -o $@ ps.c + install -S 32kw $@ +install: /usr/bin/ps +/usr/bin/ps: ps + install -cs -o bin -g kmem -m 2755 $? $@ + + + +# clean up compile results +clean: + rm -f *.bak ps diff --git a/commands/ps/ps b/commands/ps/ps new file mode 100755 index 000000000..db6d79b4c Binary files /dev/null and b/commands/ps/ps differ diff --git a/commands/ps/ps.c b/commands/ps/ps.c new file mode 100644 index 000000000..e8a6bd27c --- /dev/null +++ b/commands/ps/ps.c @@ -0,0 +1,591 @@ +/* ps - print status Author: Peter Valkenburg */ + +/* Ps.c, Peter Valkenburg (valke@psy.vu.nl), january 1990. + * + * This is a V7 ps(1) look-alike for MINIX >= 1.5.0. + * It does not support the 'k' option (i.e. cannot read memory from core file). + * If you want to compile this for non-IBM PC architectures, the header files + * require that you have your CHIP, MACHINE etc. defined. + * Full syntax: + * ps [-][alx] + * Option `a' gives all processes, `l' for detailed info, `x' includes even + * processes without a terminal. + * + * VERY IMPORTANT NOTE: + * To compile ps, the kernel/, fs/ and mm/ source directories must be in + * ../ relative to the directory where ps is compiled (normally the + * tools source directory). + * + * If you want your ps to be useable by anyone, you can arrange the + * following access permissions (note the protected memory files and set + * *group* id on ps): + * -rwxr-sr-x 1 bin kmem 11916 Jul 4 15:31 /bin/ps + * crw-r----- 1 bin kmem 1, 1 Jan 1 1970 /dev/mem + * crw-r----- 1 bin kmem 1, 2 Jan 1 1970 /dev/kmem + */ + +/* Some technical comments on this implementation: + * + * Most fields are similar to V7 ps(1), except for CPU, NICE, PRI which are + * absent, RECV which replaces WCHAN, and PGRP that is an extra. + * The info is obtained from the following fields of proc, mproc and fproc: + * F - kernel status field, p_flags + * S - kernel status field, p_flags; mm status field, mp_flags (R if p_flags + * is 0; Z if mp_flags == ZOMBIE; T if mp_flags == STOPPED; else W). + * UID - mm eff uid field, mp_effuid + * PID - mm pid field, mp_pid + * PPID - mm parent process index field, mp_parent (used as index in proc). + * PGRP - mm process group field, mp_procgrp + * SZ - kernel text size + physical stack address - physical data address + * + stack size + * p_memmap[T].mem_len + p_memmap[S].mem_phys - p_memmap[D].mem_phys + * + p_memmap[S].mem_len + * RECV - kernel process index field for message receiving, p_getfrom + * If sleeping, mm's mp_flags, or fs's fp_task are used for more info. + * TTY - fs controlling tty device field, fp_tty. + * TIME - kernel user + system times fields, user_time + sys_time + * CMD - system process index (converted to mnemonic name by using the p_name + * field), or user process argument list (obtained by reading the stack + * frame; the resulting address is used to get the argument vector from + * user space and converted into a concatenated argument list). + */ + +#include <minix/config.h> +#include <limits.h> +#include <timers.h> +#include <sys/types.h> + +#include <minix/const.h> +#include <minix/type.h> +#include <minix/ipc.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include <minix/com.h> +#include <fcntl.h> +#include <a.out.h> +#include <dirent.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <signal.h> +#include <stdio.h> +#include <ttyent.h> + +#include "../../kernel/const.h" +#include "../../kernel/type.h" +#include "../../kernel/proc.h" +#undef printf /* kernel's const.h defined this */ + +#include "../../servers/mm/mproc.h" +#include "../../servers/fs/fproc.h" +#include "../../servers/fs/const.h" +#undef printf /* fs's const.h defined this */ + + +/*----- ps's local stuff below this line ------*/ + + +#define mindev(dev) (((dev)>>MINOR) & 0377) /* yield minor device */ +#define majdev(dev) (((dev)>>MAJOR) & 0377) /* yield major device */ + +#define TTY_MAJ 4 /* major device of console */ + +/* Structure for tty name info. */ +typedef struct { + char tty_name[NAME_MAX + 1]; /* file name in /dev */ + dev_t tty_dev; /* major/minor pair */ +} ttyinfo_t; + +ttyinfo_t *ttyinfo; /* ttyinfo holds actual tty info */ +size_t n_ttyinfo; /* Number of tty info slots */ + +/* Macro to convert memory offsets to rounded kilo-units */ +#define off_to_k(off) ((unsigned) (((off) + 512) / 1024)) + +/* Number of tasks and processes. */ +int nr_tasks, nr_procs; + +/* Process tables of the kernel, MM, and FS. */ +struct proc *ps_proc; +struct mproc *ps_mproc; +struct fproc *ps_fproc; + +/* Where is INIT? */ +int init_proc_nr; +#define low_user init_proc_nr + +#define KMEM_PATH "/dev/kmem" /* opened for kernel proc table */ +#define MEM_PATH "/dev/mem" /* opened for mm/fs + user processes */ + +int kmemfd, memfd; /* file descriptors of [k]mem */ + +/* Short and long listing formats: + * + * PID TTY TIME CMD + * ppppp tttmmm:ss cccccccccc... + * + * F S UID PID PPID PGRP SZ RECV TTY TIME CMD + * fff s uuu ppppp ppppp ppppp ssss rrrrrrrrrr tttmmm:ss cccccccc... + */ +#define S_HEADER " PID TTY TIME CMD\n" +#define S_FORMAT "%5s %3s %s %s\n" +#define L_HEADER " F S UID PID PPID PGRP SZ RECV TTY TIME CMD\n" +#define L_FORMAT "%3o %c %3d %5s %5d %5d %4d %10s %3s %s %s\n" + +struct pstat { /* structure filled by pstat() */ + dev_t ps_dev; /* major/minor of controlling tty */ + uid_t ps_ruid; /* real uid */ + uid_t ps_euid; /* effective uid */ + pid_t ps_pid; /* process id */ + pid_t ps_ppid; /* parent process id */ + int ps_pgrp; /* process group id */ + int ps_flags; /* kernel flags */ + int ps_mflags; /* mm flags */ + int ps_ftask; /* (possibly pseudo) fs suspend task */ + char ps_state; /* process state */ + vir_bytes ps_tsize; /* text size (in bytes) */ + vir_bytes ps_dsize; /* data size (in bytes) */ + vir_bytes ps_ssize; /* stack size (in bytes) */ + phys_bytes ps_vtext; /* virtual text offset */ + phys_bytes ps_vdata; /* virtual data offset */ + phys_bytes ps_vstack; /* virtual stack offset */ + phys_bytes ps_text; /* physical text offset */ + phys_bytes ps_data; /* physical data offset */ + phys_bytes ps_stack; /* physical stack offset */ + int ps_recv; /* process number to receive from */ + time_t ps_utime; /* accumulated user time */ + time_t ps_stime; /* accumulated system time */ + char *ps_args; /* concatenated argument string */ + vir_bytes ps_procargs; /* initial stack frame from MM */ +}; + +/* Ps_state field values in pstat struct above */ +#define Z_STATE 'Z' /* Zombie */ +#define W_STATE 'W' /* Waiting */ +#define S_STATE 'S' /* Sleeping */ +#define R_STATE 'R' /* Runnable */ +#define T_STATE 'T' /* stopped (Trace) */ + +_PROTOTYPE(char *tname, (Dev_t dev_nr )); +_PROTOTYPE(char *taskname, (int p_nr )); +_PROTOTYPE(char *prrecv, (struct pstat *bufp )); +_PROTOTYPE(void disaster, (int sig )); +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(char *get_args, (struct pstat *bufp )); +_PROTOTYPE(int pstat, (int p_nr, struct pstat *bufp )); +_PROTOTYPE(int addrread, (int fd, phys_clicks base, vir_bytes addr, + char *buf, int nbytes )); +_PROTOTYPE(void usage, (char *pname )); +_PROTOTYPE(void err, (char *s )); +_PROTOTYPE(int gettynames, (void)); + + +/* + * Tname returns mnemonic string for dev_nr. This is "?" for maj/min pairs that + * are not found. It uses the ttyinfo array (prepared by gettynames). + * Tname assumes that the first three letters of the tty's name can be omitted + * and returns the rest (except for the console, which yields "co"). + */ +char *tname(dev_nr) +Dev_t dev_nr; +{ + int i; + + if (majdev(dev_nr) == TTY_MAJ && mindev(dev_nr) == 0) return "co"; + + for (i = 0; i < n_ttyinfo && ttyinfo[i].tty_name[0] != '\0'; i++) + if (ttyinfo[i].tty_dev == dev_nr) + return ttyinfo[i].tty_name + 3; + + return "?"; +} + +/* Return canonical task name of task p_nr; overwritten on each call (yucch) */ +char *taskname(p_nr) +int p_nr; +{ + return ps_proc[p_nr + nr_tasks].p_name; +} + +/* Prrecv prints the RECV field for process with pstat buffer pointer bufp. + * This is either "ANY", "taskname", or "(blockreason) taskname". + */ +char *prrecv(bufp) +struct pstat *bufp; +{ + char *blkstr, *task; /* reason for blocking and task */ + static char recvstr[20]; + + if (bufp->ps_recv == ANY) return "ANY"; + + task = taskname(bufp->ps_recv); + if (bufp->ps_state != S_STATE) return task; + + blkstr = "?"; + if (bufp->ps_recv == MM_PROC_NR) { + if (bufp->ps_mflags & PAUSED) + blkstr = "pause"; + else if (bufp->ps_mflags & WAITING) + blkstr = "wait"; + else if (bufp->ps_mflags & SIGSUSPENDED) + blkstr = "ssusp"; + } else if (bufp->ps_recv == FS_PROC_NR) { + if (-bufp->ps_ftask == XPIPE) + blkstr = "pipe"; + else if (-bufp->ps_ftask == XPOPEN) + blkstr = "popen"; + else if (-bufp->ps_ftask == XLOCK) + blkstr = "flock"; + else + blkstr = taskname(-bufp->ps_ftask); + } + (void) sprintf(recvstr, "(%s) %s", blkstr, task); + return recvstr; +} + +/* If disaster is called some of the system parameters imported into ps are + * probably wrong. This tends to result in memory faults. + */ +void disaster(sig) +int sig; +{ + fprintf(stderr, "Ooops, got signal %d\n", sig); + fprintf(stderr, "Was ps recompiled since the last kernel change?\n"); + exit(3); +} + +/* Main interprets arguments, gets system addresses, opens [k]mem, reads in + * process tables from kernel/mm/fs and calls pstat() for relevant entries. + */ +int main(argc, argv) +int argc; +char *argv[]; +{ + int i; + struct pstat buf; + int db_fd; + int uid = getuid(); /* real uid of caller */ + char *opt; + int opt_all = FALSE; /* -a */ + int opt_long = FALSE; /* -l */ + int opt_notty = FALSE; /* -x */ + char *ke_path; /* paths of kernel, */ + char *mm_path; /* mm, */ + char *fs_path; /* and fs used in ps -U */ + struct psinfo psinfo; + char pid[2 + sizeof(pid_t) * 3]; + unsigned long ustime; + char cpu[sizeof(clock_t) * 3 + 1 + 2]; + + (void) signal(SIGSEGV, disaster); /* catch a common crash */ + + /* Parse arguments; a '-' need not be present (V7/BSD compatability) */ + for (i = 1; i < argc; i++) { + opt = argv[i]; + if (opt[0] == '-') opt++; + while (*opt != 0) switch (*opt++) { + case 'a': opt_all = TRUE; break; + case 'e': opt_all = opt_notty = TRUE; break; + case 'f': + case 'l': opt_long = TRUE; break; + case 'x': opt_notty = TRUE; break; + default: usage(argv[0]); + } + } + + /* Open memory devices and get PS info from the kernel */ + if ((kmemfd = open(KMEM_PATH, O_RDONLY)) == -1) err(KMEM_PATH); + if ((memfd = open(MEM_PATH, O_RDONLY)) == -1) err(MEM_PATH); + if (gettynames() == -1) err("Can't get tty names"); + if (ioctl(memfd, MIOCGPSINFO, (void *) &psinfo) == -1) + err("can't get PS info from kernel"); + nr_tasks = psinfo.nr_tasks; + nr_procs = psinfo.nr_procs; + + /* Allocate memory for process tables */ + ps_proc = (struct proc *) malloc((nr_tasks + nr_procs) * sizeof(ps_proc[0])); + ps_mproc = (struct mproc *) malloc(nr_procs * sizeof(ps_mproc[0])); + ps_fproc = (struct fproc *) malloc(nr_procs * sizeof(ps_fproc[0])); + if (ps_proc == NULL || ps_mproc == NULL || ps_fproc == NULL) + err("Out of memory"); + + /* Get kernel process table */ + if (addrread(kmemfd, (phys_clicks) 0, + psinfo.proc, (char *) ps_proc, + (nr_tasks + nr_procs) * sizeof(ps_proc[0])) + != (nr_tasks + nr_procs) * sizeof(ps_proc[0])) + err("Can't get kernel proc table from /dev/kmem"); + + /* Get mm/fs process tables */ + if (addrread(memfd, ps_proc[nr_tasks + MM_PROC_NR].p_memmap[D].mem_phys, + psinfo.mproc, (char *) ps_mproc, + nr_procs * sizeof(ps_mproc[0])) + != nr_procs * sizeof(ps_mproc[0])) + err("Can't get mm proc table from /dev/mem"); + if (addrread(memfd, ps_proc[nr_tasks + FS_PROC_NR].p_memmap[D].mem_phys, + psinfo.fproc, (char *) ps_fproc, + nr_procs * sizeof(ps_fproc[0])) + != nr_procs * sizeof(ps_fproc[0])) + err("Can't get fs proc table from /dev/mem"); + + /* We need to know where INIT hangs out. */ + for (i = FS_PROC_NR; i < nr_procs; i++) { + if (strcmp(ps_proc[nr_tasks + i].p_name, "INIT") == 0) break; + } + init_proc_nr = i; + + /* Now loop through process table and handle each entry */ + printf("%s", opt_long ? L_HEADER : S_HEADER); + for (i = -nr_tasks; i < nr_procs; i++) { + if (pstat(i, &buf) != -1 && + (opt_all || buf.ps_euid == uid || buf.ps_ruid == uid) && + (opt_notty || majdev(buf.ps_dev) == TTY_MAJ)) { + if (buf.ps_pid == 0) { + sprintf(pid, "(%d)", i); + } else { + sprintf(pid, "%d", buf.ps_pid); + } + + ustime = (buf.ps_utime + buf.ps_stime) / HZ; + if (ustime < 60 * 60) { + sprintf(cpu, "%2lu:%02lu", ustime / 60, ustime % 60); + } else + if (ustime < 100L * 60 * 60) { + ustime /= 60; + sprintf(cpu, "%2luh%02lu", ustime / 60, ustime % 60); + } else { + sprintf(cpu, "%4luh", ustime / 3600); + } + + if (opt_long) printf(L_FORMAT, + buf.ps_flags, buf.ps_state, + buf.ps_euid, pid, buf.ps_ppid, + buf.ps_pgrp, + off_to_k((buf.ps_tsize + + buf.ps_stack - buf.ps_data + + buf.ps_ssize)), + (buf.ps_flags & RECEIVING ? + prrecv(&buf) : + ""), + tname((Dev_t) buf.ps_dev), + cpu, + i <= init_proc_nr || buf.ps_args == NULL + ? taskname(i) : buf.ps_args); + else + printf(S_FORMAT, + pid, tname((Dev_t) buf.ps_dev), + cpu, + i <= init_proc_nr || buf.ps_args == NULL + ? taskname(i) : buf.ps_args); + } + } + return(0); +} + +char *get_args(bufp) +struct pstat *bufp; +{ + int nargv; + int cnt; /* # of bytes read from stack frame */ + int neos; /* # of '\0's seen in argv string space */ + phys_bytes iframe; + long l; + char *cp, *args; + static union stack { + vir_bytes stk_i; + char *stk_cp; + char stk_c; + } stk[ARG_MAX / sizeof(char *)]; + union stack *sp; + + /* Phys address of the original stack frame. */ + iframe = bufp->ps_procargs - bufp->ps_vstack + bufp->ps_stack; + + /* Calculate the number of bytes to read from user stack */ + l = (phys_bytes) bufp->ps_ssize - (iframe - bufp->ps_stack); + if (l > ARG_MAX) l = ARG_MAX; + cnt = l; + + /* Get cnt bytes from user initial stack to local stack buffer */ + if (lseek(memfd, (off_t) iframe, 0) < 0) + return NULL; + + if ( read(memfd, (char *)stk, cnt) != cnt ) + return NULL; + + sp = stk; + nargv = (int) sp[0].stk_i; /* number of argv arguments */ + + /* See if argv[0] is with the bytes we read in */ + l = (long) sp[1].stk_cp - (long) bufp->ps_procargs; + + if ( ( l < 0 ) || ( l > cnt ) ) + return NULL; + + /* l is the offset of the argv[0] argument */ + /* change for concatenation the '\0' to space, for nargv elements */ + + args = &((char *) stk)[(int)l]; + neos = 0; + for (cp = args; cp < &((char *) stk)[cnt]; cp++) + if (*cp == '\0') + if (++neos >= nargv) + break; + else + *cp = ' '; + if (cp == args) return NULL; + *cp = '\0'; + + return args; + +} + +/* Pstat collects info on process number p_nr and returns it in buf. + * It is assumed that tasks do not have entries in fproc/mproc. + */ +int pstat(p_nr, bufp) +int p_nr; +struct pstat *bufp; +{ + int p_ki = p_nr + nr_tasks; /* kernel proc index */ + + if (p_nr < -nr_tasks || p_nr >= nr_procs) return -1; + + if ((ps_proc[p_ki].p_type == P_NONE) + && !(ps_mproc[p_nr].mp_flags & IN_USE)) + return -1; + + bufp->ps_flags = ps_proc[p_ki].p_flags; + + if (p_nr >= low_user) { + bufp->ps_dev = ps_fproc[p_nr].fp_tty; + bufp->ps_ftask = ps_fproc[p_nr].fp_task; + } else { + bufp->ps_dev = 0; + bufp->ps_ftask = 0; + } + + if (p_nr >= low_user) { + bufp->ps_ruid = ps_mproc[p_nr].mp_realuid; + bufp->ps_euid = ps_mproc[p_nr].mp_effuid; + bufp->ps_pid = ps_mproc[p_nr].mp_pid; + bufp->ps_ppid = ps_mproc[ps_mproc[p_nr].mp_parent].mp_pid; + bufp->ps_pgrp = ps_mproc[p_nr].mp_procgrp; + bufp->ps_mflags = ps_mproc[p_nr].mp_flags; + } else { + bufp->ps_pid = 0; + bufp->ps_ppid = 0; + bufp->ps_ruid = bufp->ps_euid = 0; + bufp->ps_pgrp = 0; + bufp->ps_mflags = 0; + } + + /* State is interpretation of combined kernel/mm flags for non-tasks */ + if (p_nr >= low_user) { /* non-tasks */ + if (ps_mproc[p_nr].mp_flags & ZOMBIE) + bufp->ps_state = Z_STATE; /* zombie */ + else if (ps_mproc[p_nr].mp_flags & STOPPED) + bufp->ps_state = T_STATE; /* stopped (traced) */ + else if (ps_proc[p_ki].p_flags == 0) + bufp->ps_state = R_STATE; /* in run-queue */ + else if (ps_mproc[p_nr].mp_flags & (WAITING | PAUSED | SIGSUSPENDED) || + ps_fproc[p_nr].fp_suspended == SUSPENDED) + bufp->ps_state = S_STATE; /* sleeping */ + else + bufp->ps_state = W_STATE; /* a short wait */ + } else { /* tasks are simple */ + if (ps_proc[p_ki].p_flags == 0) + bufp->ps_state = R_STATE; /* in run-queue */ + else + bufp->ps_state = W_STATE; /* other i.e. waiting */ + } + + bufp->ps_tsize = (size_t) ps_proc[p_ki].p_memmap[T].mem_len << CLICK_SHIFT; + bufp->ps_dsize = (size_t) ps_proc[p_ki].p_memmap[D].mem_len << CLICK_SHIFT; + bufp->ps_ssize = (size_t) ps_proc[p_ki].p_memmap[S].mem_len << CLICK_SHIFT; + bufp->ps_vtext = (off_t) ps_proc[p_ki].p_memmap[T].mem_vir << CLICK_SHIFT; + bufp->ps_vdata = (off_t) ps_proc[p_ki].p_memmap[D].mem_vir << CLICK_SHIFT; + bufp->ps_vstack = (off_t) ps_proc[p_ki].p_memmap[S].mem_vir << CLICK_SHIFT; + bufp->ps_text = (off_t) ps_proc[p_ki].p_memmap[T].mem_phys << CLICK_SHIFT; + bufp->ps_data = (off_t) ps_proc[p_ki].p_memmap[D].mem_phys << CLICK_SHIFT; + bufp->ps_stack = (off_t) ps_proc[p_ki].p_memmap[S].mem_phys << CLICK_SHIFT; + + bufp->ps_recv = ps_proc[p_ki].p_getfrom; + + bufp->ps_utime = ps_proc[p_ki].user_time; + bufp->ps_stime = ps_proc[p_ki].sys_time; + + bufp->ps_procargs = ps_mproc[p_nr].mp_procargs; + + if (bufp->ps_state == Z_STATE) + bufp->ps_args = "<defunct>"; + else if (p_nr > init_proc_nr) + bufp->ps_args = get_args(bufp); + + return 0; +} + +/* Addrread reads nbytes from offset addr to click base of fd into buf. */ +int addrread(fd, base, addr, buf, nbytes) +int fd; +phys_clicks base; +vir_bytes addr; +char *buf; +int nbytes; +{ + if (lseek(fd, ((off_t) base << CLICK_SHIFT) + addr, 0) < 0) + return -1; + + return read(fd, buf, nbytes); +} + +void usage(pname) +char *pname; +{ + fprintf(stderr, "Usage: %s [-][alx]\n", pname); + exit(1); +} + +void err(s) +char *s; +{ + extern int errno; + + if (errno == 0) + fprintf(stderr, "ps: %s\n", s); + else + fprintf(stderr, "ps: %s: %s\n", s, strerror(errno)); + + exit(2); +} + +/* Fill ttyinfo by fstatting character specials in /dev. */ +int gettynames() +{ + static char dev_path[] = "/dev/"; + struct stat statbuf; + static char path[sizeof(dev_path) + NAME_MAX]; + int index; + struct ttyent *ttyp; + + index = 0; + while ((ttyp = getttyent()) != NULL) { + strcpy(path, dev_path); + strcat(path, ttyp->ty_name); + if (stat(path, &statbuf) == -1 || !S_ISCHR(statbuf.st_mode)) + continue; + if (index >= n_ttyinfo) { + n_ttyinfo= (index+16) * 2; + ttyinfo = realloc(ttyinfo, n_ttyinfo * sizeof(ttyinfo[0])); + if (ttyinfo == NULL) err("Out of memory"); + } + ttyinfo[index].tty_dev = statbuf.st_rdev; + strcpy(ttyinfo[index].tty_name, ttyp->ty_name); + index++; + } + endttyent(); + while (index < n_ttyinfo) ttyinfo[index++].tty_dev= 0; + + return 0; +} diff --git a/commands/reboot/Makefile b/commands/reboot/Makefile new file mode 100755 index 000000000..7b67fa2d0 --- /dev/null +++ b/commands/reboot/Makefile @@ -0,0 +1,42 @@ +# Makefile for shutdown / halt / reboot. + +CFLAGS=$(OPT) -D_MINIX -D_POSIX_SOURCE +LDFLAGS=-i + +PROGRAMS= shutdown halt tinyhalt +MANUALS= shutdown.8 halt.8 reboot.2 reboot.8 + +all: $(PROGRAMS) + +shutdown: shutdown.o sh_wall.o log.o + $(CC) $(LDFLAGS) -o shutdown shutdown.o sh_wall.o log.o + install -S 4kw $@ + +halt: halt.o log.o + $(CC) $(LDFLAGS) -o halt halt.o log.o + install -S 4kw $@ + +tinyhalt: tinyhalt.c + $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $? + install -S 4kw $@ + +install: /usr/bin/halt /usr/bin/reboot /usr/bin/shutdown \ + /bin/halt /bin/reboot + +/usr/bin/halt: halt + install -cs -o root -g operator -m 744 $? $@ + +/usr/bin/reboot: /usr/bin/halt + install -l $? $@ + +/usr/bin/shutdown: shutdown + install -cs -o root -g operator -m 4754 $? $@ + +/bin/halt: tinyhalt + install -cs -o root -g operator -m 744 $? $@ + +/bin/reboot: /bin/halt + install -l $? $@ + +clean: + rm -f *.o a.out core $(PROGRAMS) diff --git a/commands/reboot/README b/commands/reboot/README new file mode 100755 index 000000000..91c378b74 --- /dev/null +++ b/commands/reboot/README @@ -0,0 +1,52 @@ +This a new implementation of a shutdown procedure. It allows +the system to go down graciously with informing the +users. This package contains 3 programs: + +- halt = Immediately stop the system, no info to users +- shutdown = Inform users, close down the system properly +- wall = Vincent Archer's implementation of wall (Write all) + +Installing + +Shutdown and halt use a new systemcall, which I've added to +MM. Therefor there are several diff's which should be applied: + +callnr.hd - New callnr for reboot(2) + Diff against /usr/include/minix/callnr.h. + Those of you using a symlink package should + change the number and mm/table.c into a free + number. I used 54, LSTAT. +param.hd - Defines reboot_flag as a part of the messages +proto.hd - Add's prototype for do_reboot() +table.cd - Interpretation of the systemcall to MM +mm.cd - I have added the do_reboot code to mm/getset.c but + I couldn't find a getset.c to create a useable diff :-( + So you can add where you want it. It is pure code, no diff. + +Now edit log.c and search for ``host''. Change this into your +systemname or make it empty. + +Shutdown and halt log their actions in /usr/adm/log, edit the +makefile and undefine -DLOG if you don't want this (this at the end of +the makefile). You can change SHUT_LOG in shutdown.c and log.c if you +want it in another file. + +Then type a `make'. This will take a minute or so (13 sec. with bcc :-). +Remember to build a new image and update the ps_database. + +Type `make install' to place the program's where I've got them. +Use them, try them and let me now if you've got problems running +something. + +I have tested to sources both on 16-bits and 32-bits MINIX. I have compiled +it with gcc, bcc and ACK, so that shouldn't really give a problem. Maybe +the standard MINIX-make chokes on the makefile, atleast mine did. + +NOTE: + Make install does not place the man-pages somewhere. You should + do this yourself. + +-- +Edvard Tuinder ed@pulstar.NL.mugnet.org v892231@si.hhs.NL +Student Computer Science +Haagse Hogeschool, The Hague, The Netherlands diff --git a/commands/reboot/halt.c b/commands/reboot/halt.c new file mode 100755 index 000000000..51d8f254a --- /dev/null +++ b/commands/reboot/halt.c @@ -0,0 +1,135 @@ +/* halt / reboot - halt or reboot system (depends on name) + + halt - calling reboot() with RBT_HALT + reboot - calling reboot() with RBT_REBOOT + + author: Edvard Tuinder v892231@si.hhs.NL + + This program calls the library function reboot(2) which performs + the system-call do_reboot. + + */ + +#define _POSIX_SOURCE 1 +#include <sys/types.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <sys/stat.h> +#include <sys/wait.h> + +void write_log _ARGS(( void )); +void usage _ARGS(( void )); +int main _ARGS(( int argc, char *argv[] )); + +char *prog; + +void +usage() +{ + fprintf(stderr, "Usage: %s [-hrRf] [-x reboot-code]\n", prog); + exit(1); +} + +int +main(argc,argv) +int argc; +char **argv; +{ + int flag = -1; /* default action unknown */ + int fast = 0; /* fast halt/reboot, don't bother being nice. */ + int i; + struct stat dummy; + char *monitor_code = ""; + pid_t pid; + + if ((prog = strrchr(argv[0],'/')) == NULL) prog = argv[0]; else prog++; + + if (strcmp(prog, "halt") == 0) flag = RBT_HALT; + if (strcmp(prog, "reboot") == 0) flag = RBT_REBOOT; + + i = 1; + while (i < argc && argv[i][0] == '-') { + char *opt = argv[i++] + 1; + + if (*opt == '-' && opt[1] == 0) break; /* -- */ + + while (*opt != 0) switch (*opt++) { + case 'h': + flag = RBT_HALT; + break; + case 'r': + flag = RBT_REBOOT; + break; + case 'R': + flag = RBT_RESET; + break; + case 'f': + fast = 1; + break; + case 'x': + flag = RBT_MONITOR; + if (*opt == 0) { + if (i == argc) usage(); + opt = argv[i++]; + } + monitor_code = opt; + opt = ""; + break; + default: + usage(); + } + } + + if (i != argc) usage(); + + if (flag == -1) { + fprintf(stderr, "Don't know what to do when named '%s'\n", prog); + exit(1); + } + + if (stat("/usr/bin", &dummy) < 0) { + /* It seems that /usr isn't present, let's assume "-f." */ + fast = 1; + } + + write_log(); + + if (fast) { + /* But not too fast... */ + sleep(1); + + } else { + /* Run the shutdown scripts. */ + signal(SIGHUP, SIG_IGN); + signal(SIGTERM, SIG_IGN); + + switch ((pid = fork())) { + case -1: + fprintf(stderr, "%s: can't fork(): %s\n", prog, strerror(errno)); + exit(1); + case 0: + execl("/bin/sh", "sh", "/etc/rc", "down", (char *) NULL); + fprintf(stderr, "%s: can't execute: /bin/sh: %s\n", + prog, strerror(errno)); + exit(1); + default: + while (waitpid(pid, NULL, 0) != pid) {} + } + + /* Tell init to stop spawning getty's. */ + kill(1, SIGTERM); + + /* Give everybody a chance to die peacefully. */ + kill(-1, SIGTERM); + sleep(3); + } + + reboot(flag, monitor_code, strlen(monitor_code)); + fprintf(stderr, "%s: reboot(): %s\n", strerror(errno)); + return 1; +} diff --git a/commands/reboot/log.c b/commands/reboot/log.c new file mode 100755 index 000000000..b54957ba9 --- /dev/null +++ b/commands/reboot/log.c @@ -0,0 +1,81 @@ +/* + log - log the shutdown's and the halt's + + Author: Edvard Tuinder <v892231@si.hhs.NL> + + shutdown is logged in /usr/adm/wtmp and in /usr/adm/log (if desired) + halt is logged only in /usr/adm/wtmp as `halt' to prevent last from + reporting halt's as crashes. + + */ + +#define _POSIX_SOURCE 1 +#include <sys/types.h> +#include <stdio.h> +#include <utmp.h> +#include <pwd.h> +#include <fcntl.h> +#include <time.h> +#include <string.h> +#include <unistd.h> +#include <sys/utsname.h> +#undef WTMP + +static char WTMP[] = "/usr/adm/wtmp"; /* Record of logins and logouts. */ +static char SHUT_LOG[] = "/usr/adm/log"; + +char who[8]; +extern char *prog; +static char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; + +void write_log _ARGS(( void )); + +void write_log() +{ + int fd; + static struct utmp wtmp; + static struct passwd *pwd; + char mes[90]; + struct tm *tm; + time_t now; + struct utsname utsname; + char *host = "localhost"; + + time(&now); + tm = localtime(&now); + + if (uname(&utsname) >= 0) host = utsname.nodename; + + pwd = getpwuid(getuid()); + if (pwd == (struct passwd *)0) + strcpy (who,"root"); + else + strcpy (who,pwd->pw_name); + fd = open(WTMP,O_APPEND|O_WRONLY,1); + if (fd) { + if (strcmp(prog,"reboot")) + strcpy (wtmp.ut_user, prog); + else + strcpy (wtmp.ut_user, "shutdown"); /* last ... */ + strcpy (wtmp.ut_id, "~~"); + strcpy (wtmp.ut_line, "~"); + wtmp.ut_pid = 0; + wtmp.ut_type = BOOT_TIME; + wtmp.ut_time = now; + wtmp.ut_host[0]= '\0'; + write (fd, (char *) &wtmp,sizeof(struct utmp)); + close(fd); + } + fd = open(SHUT_LOG,O_APPEND|O_WRONLY,1); + if (!fd) + perror ("open"); + else { + sprintf (mes,"%s %02d %02d:%02d:%02d %s: system %s by %s@%s\n", + month[tm->tm_mon],tm->tm_mday,tm->tm_hour,tm->tm_min,tm->tm_sec, + prog,prog,who,host); + write (fd,mes,strlen(mes)); + close(fd); + } + return; +} diff --git a/commands/reboot/sh_wall.c b/commands/reboot/sh_wall.c new file mode 100755 index 000000000..77e9e9863 --- /dev/null +++ b/commands/reboot/sh_wall.c @@ -0,0 +1,109 @@ +/* wall - write to all logged in users Author: V. Archer */ +/* + Edvard Tuinder v892231@si.hhs.NL + Modified some things to include this with my shutdown/halt + package + */ + +#define _POSIX_SOURCE 1 +#include <sys/types.h> +#include <fcntl.h> +#include <pwd.h> +#include <string.h> +#include <stdio.h> +#include <time.h> +#include <utmp.h> +#include <unistd.h> +#include <sys/utsname.h> +#include <sys/stat.h> +#undef UTMP + +static char UTMP[] = "/etc/utmp"; /* Currently logged in users. */ + +void wall _ARGS(( char *when, char *extra )); +void crnlcat _ARGS(( char *message, char *more )); + +void +wall(when, extra) +char *when; /* When is shutdown */ +char *extra; /* If non-nil, why is the shutdown */ +{ + struct utmp utmp; + char utmptty[5 + sizeof(utmp.ut_line) + 1]; + char message[1024]; + struct passwd *pw; + int utmpfd, ttyfd; + char *ourtty, *ourname; + time_t now; + struct utsname utsname; + struct stat con_st, tty_st; + + if (ourtty = ttyname(1)) { + if (ourname = strrchr(ourtty, '/')) ourtty = ourname+1; + } else ourtty = "system task"; + if (pw = getpwuid(getuid())) ourname = pw->pw_name; + else ourname = "unknown"; + + time(&now); + if (uname(&utsname) != 0) strcpy(utsname.nodename, "?"); + sprintf(message, "\r\nBroadcast message from %s@%s (%s)\r\n%.24s...\007\007\007\r\n", + ourname, utsname.nodename, ourtty, ctime(&now)); + + crnlcat(message, when); + crnlcat(message, extra); + +/* Search the UTMP database for all logged-in users. */ + + if ((utmpfd = open(UTMP, O_RDONLY)) < 0) { + fprintf(stderr, "Cannot open utmp file\r\n"); + return; + } + + /* first the console */ + strcpy(utmptty, "/dev/console"); + if ((ttyfd = open(utmptty, O_WRONLY | O_NONBLOCK)) < 0) { + perror(utmptty); + } else { + fstat(ttyfd, &con_st); + write(ttyfd, message, strlen(message)); + close(ttyfd); + } + + while (read(utmpfd, (char *) &utmp, sizeof(utmp)) == sizeof(utmp)) { + /* is this the user we are looking for? */ + if (utmp.ut_type != USER_PROCESS) continue; + + strncpy(utmptty+5, utmp.ut_line, sizeof(utmp.ut_line)); + utmptty[5 + sizeof(utmp.ut_line) + 1] = 0; + if ((ttyfd = open(utmptty, O_WRONLY | O_NONBLOCK)) < 0) { + perror(utmptty); + continue; + } + fstat(ttyfd, &tty_st); + if (tty_st.st_rdev != con_st.st_rdev) + write(ttyfd, message, strlen(message)); + close(ttyfd); + } + close(utmpfd); + return; +} + +void +crnlcat(message, more) +char *message, *more; +{ + char *p = message; + char *m = more; + char *end = message + 1024 - 1; + + while (p < end && *p != 0) *p++; + + while (p < end && *m != 0) { + if (*m == '\n' && (p == message || p[-1] != '\n')) { + *p++ = '\r'; + if (p == end) p--; + } + *p++ = *m++; + } + *p = 0; +} diff --git a/commands/reboot/shutdown.c b/commands/reboot/shutdown.c new file mode 100755 index 000000000..71e5753b1 --- /dev/null +++ b/commands/reboot/shutdown.c @@ -0,0 +1,408 @@ +/* + shutdown - close down the system graciously + + Author: Edvard Tuinder <v892231@si.hhs.NL> + + This program informs the users that the system is going + down, when and why. After that a shutdown notice is written in + both /usr/adm/wtmp and by syslog(3). Then reboot(2) is called + to really close the system. + + This actually is a ``nice'' halt(8). + + Options are supposed to be as with BSD + -h: shutdown and halt the system + -r: shutdown and reboot + -k: stop an already running shutdown + -o: obsolete: not implemented + + New Minix options: + -C: crash check, i.e. is the last wtmp entry a shutdown entry? + -x: let the monitor execute the given code + -R: reset the system + */ + +#define _POSIX_SOURCE 1 +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <ctype.h> +#include <fcntl.h> +#include <time.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include <utmp.h> +#include <errno.h> +#undef WTMP + +static char WTMP[] = "/usr/adm/wtmp"; +static char SHUT_PID[] = "/usr/run/shutdown.pid"; +static char NOLOGIN[] = "/etc/nologin"; + +#ifndef __STDC__ +#define inform_user_time inf_time +#define inform_user inf_user +#endif + +void usage _ARGS(( void )); +void write_pid _ARGS(( void )); +int inform_user_time _ARGS(( void )); +void inform_user _ARGS(( void )); +void terminate _ARGS(( void )); +void wall _ARGS(( char *when, char *extra )); +int crash_check _ARGS(( void )); +void parse_time _ARGS(( char *arg )); +void get_message _ARGS(( void )); +void main _ARGS(( int argc, char *argv[] )); +char *itoa _ARGS(( int n )); + +long wait_time=0L; +char message[1024]; +char info[80]; +int reboot_flag='h'; /* default is halt */ +char *reboot_code=""; /* optional monitor code */ +int info_min, info_hour; +char *prog; + +void parse_time (arg) +char *arg; +{ + char *p = arg; + int hours, minutes; + time_t now; + struct tm *tm; + int delta = 0; + int bad = 0; + + if (p[0] == '+') { delta = 1; p++; } + + hours = strtoul(p, &p, 10); + if (*p == 0 && delta) { + minutes = hours; + hours = 0; + } else { + if (*p != ':' && *p != '.') + bad = 1; + else + p++; + minutes = strtoul(p, &p, 10); + if (*p != 0) bad = 1; + } + if (bad) { + fprintf(stderr,"Invalid time specification `%s'\n",arg); + usage(); + } + + time(&now); + tm = localtime(&now); + + if (!delta) { + hours -= tm->tm_hour; + minutes -= tm->tm_min; + } + + if (minutes < 0) { + minutes += 60; + hours--; + } + if (hours < 0) hours += 24; /* Time after midnight. */ + + tm->tm_hour += hours; + tm->tm_min += minutes; + (void) mktime(tm); + info_hour = tm->tm_hour; + info_min = tm->tm_min; + + sprintf(info, + "The system will shutdown in %d hour%s and %d minute%s at %02d:%02d\n\n", + hours,hours==1?"":"s",minutes,minutes==1?"":"s",info_hour,info_min); + + wait_time += hours * 3600 + minutes * 60; + return; +} + +void main(argc,argv) +int argc; +char *argv[]; +{ + int i, now = 0, nologin = 0, want_terminate = 0, want_message = 0, check = 0; + char *opt; + int tty; + static char HALT1[] = "-?"; + static char *HALT[] = { "shutdown", HALT1, NULL, NULL }; + + /* Parse options. */ + for (i = 1; i < argc && argv[i][0] == '-'; i++) { + if (argv[i][1] == '-' && argv[i][2] == 0) { + /* -- */ + i++; + break; + } + for (opt = argv[i] + 1; *opt != 0; opt++) { + switch (*opt) { + case 'k': + want_terminate = 1; + break; + case 'h': + case 'r': + case 'x': + reboot_flag = *opt; + if (reboot_flag == 'x') { + if (*++opt == 0) { + if (++i == argc) { + fprintf (stderr,"shutdown: option '-x' requires an argument\n"); + usage(); + } + opt=argv[i]; + } + reboot_code=opt; + opt=""; + } + break; + case 'R': + reboot_flag = 'R'; + break; + case 'm': + want_message = 1; + break; + case 'C': + check = 1; + break; + default: + fprintf (stderr,"shutdown: invalid option '-%c'\n",*opt); + usage(); + break; + } + } + } + if ((argc - i) > 2) usage(); + + if (check) exit(crash_check() ? 0 : 2); + + if (i == argc) { + /* No timespec, assume "now". */ + now = 1; + } else { + if (!strcmp(argv[i], "now")) + now++; + else + parse_time(argv[i]); + } + + if ((argc - i) == 2) { + /* One line message */ + strcat(message, argv[i+1]); + strcat(message, "\n"); + } + + if (want_terminate) terminate(); + if (want_message) get_message(); + + puts(info); + + prog = strrchr(*argv,'/'); + if (prog == (char *)0) + prog = *argv; + else + prog++; + + if (!now) { + /* Daemonize. */ + switch (fork()) { + case 0: + break; + case -1: + fprintf(stderr, "%s: can't fork\n", prog); + exit(1); + default: + exit(0); + } + /* Detach from the terminal (if any). */ + if ((tty = open("/dev/tty", O_RDONLY)) != -1) { + close(tty); + setsid(); + } + write_pid(); + } + + for (;;) { + if (wait_time <= 5 * 60 && !nologin && !now) { + close(creat(NOLOGIN,00644)); + nologin = 1; + } + if (wait_time <= 60) break; + if(inform_user_time()) + inform_user(); + sleep (60); + wait_time -= 60; + } + + if (!now) { + inform_user(); + sleep (30); /* Last minute before shutdown */ + wait_time -= 30; + inform_user(); + sleep (30); /* Last 30 seconds before shutdown */ + } + wait_time = 0; + inform_user(); + + unlink(SHUT_PID); /* No way of stopping anymore */ + unlink(NOLOGIN); + + HALT[1][1] = reboot_flag; + if (reboot_flag == 'x') HALT[2] = reboot_code; +#if __minix_vmd + execv("/usr/sbin/halt", HALT); +#else + execv("/usr/bin/halt", HALT); +#endif + if (errno != ENOENT) + fprintf(stderr, "Can't execute 'halt': %s\n", strerror(errno)); + + sleep(2); + reboot(RBT_HALT); + fprintf(stderr, "Reboot call failed: %s\n", strerror(errno)); + exit(1); +} + +void usage() +{ + fputs("Usage: shutdown [-hrRmk] [-x code] [time [message]]\n", stderr); + fputs(" -h -> halt system after shutdown\n", stderr); + fputs(" -r -> reboot system after shutdown\n", stderr); + fputs(" -R -> reset system after shutdown\n", stderr); + fputs(" -x -> return to the monitor doing...\n", stderr); + fputs(" -m -> read a shutdown message from standard input\n", stderr); + fputs(" -k -> stop an already running shutdown\n", stderr); + fputs(" code -> boot monitor code to be executed\n", stderr); + fputs(" time -> keyword ``now'', minutes before shutdown ``+5'',\n", stderr); + fputs(" or absolute time specification ``11:20''\n", stderr); + fputs(" message -> short shutdown message\n", stderr); + exit(1); +} + +void terminate() +{ + FILE *in; + pid_t pid; + char c_pid[5]; + char buf[80]; + + in = fopen(SHUT_PID,"r"); + if (in == (FILE *)0) { + fputs ("Can't get pid of shutdown process, probably not running shutdown\n", stderr); + exit(1); + } + fgets(c_pid,5,in); + fclose(in); + pid = atoi(c_pid); + if (kill(pid,9) == -1) + fputs("Can't kill the shutdown process, probably not running anymore\n",stderr); + else + puts("Shutdown process terminated"); + unlink(SHUT_PID); + unlink(NOLOGIN); +#ifdef not_very_useful + in = fopen (SHUT_LOG,"a"); + if (in == (FILE *)0) + exit(0); + sprintf (buf, "Shutdown with pid %d terminated\n",pid); + fputs(buf,in); + fclose(in); +#endif + exit(0); +} + +void get_message() +{ + char line[80]; + int max_lines=12; + + puts ("Type your message. End with ^D at an empty line"); + fputs ("shutdown> ",stdout);fflush(stdout); + while (fgets(line,80,stdin) != (char *)0) { + strcat (message,line); + bzero(line,strlen(line)); + fputs ("shutdown> ",stdout);fflush(stdout); + } + putc('\n',stdout);fflush(stdout); +} + +int inform_user_time() +{ + int min; + + min = wait_time /60; + + if (min == 60 || min == 30 || min == 15 || min == 10 || min <= 5) + return 1; + else + return 0; +} + +void inform_user() +{ + int hour, minute; + char mes[80]; + + hour = 0; + minute = wait_time / 60; + while (minute >= 60) { + minute -= 60; + hour++; + } + + if (hour) + sprintf(mes, + "\nThe system will shutdown in %d hour%s and %d minute%s at %.02d:%.02d\n\n", + hour,hour==1?"":"s",minute,minute==1?"":"s",info_hour,info_min); + else + if (minute > 1) + sprintf(mes, + "\nThe system will shutdown in %d minutes at %.02d:%.02d\n\n", + minute,info_hour,info_min); + else + if (wait_time > 1) + sprintf(mes, + "\nThe system will shutdown in %d seconds\n\n", + wait_time); + else + sprintf(mes, + "\nThe system will shutdown NOW\n\n"); + + wall(mes,message); +} + +void write_pid() +{ + char pid[5]; + int fd; + + fd = creat(SHUT_PID,00600); + if (!fd) + return; + strncpy (pid,itoa(getpid()), sizeof(pid)); + write (fd,pid,sizeof(pid)); + close(fd); + return; +} + +int crash_check() +{ + struct utmp last; + int fd, crashed; + struct stat st; + + if (stat(WTMP, &st) < 0 || st.st_size == 0) return 0; + if ((fd = open(WTMP, O_RDONLY)) < 0) return 0; + + crashed = (lseek(fd, - (off_t) sizeof(last), SEEK_END) == -1 + || read(fd, (void *) &last, sizeof(last)) != sizeof(last) + || last.ut_line[0] != '~' + || strncmp(last.ut_user, "shutdown", sizeof(last.ut_user))); + close(fd); + return crashed; +} diff --git a/commands/reboot/tinyhalt.c b/commands/reboot/tinyhalt.c new file mode 100755 index 000000000..1a24b0d6d --- /dev/null +++ b/commands/reboot/tinyhalt.c @@ -0,0 +1,35 @@ +/* tinyhalt 1.0 - small forerunner Author: Kees J. Bot + * + * Disk space on the root file system is a scarce resource. This little + * program sits in /sbin. It normally calls the real halt/reboot, but if + * that isn't available then it simply calls reboot(). Can't do any logging + * of the event anyhow. + */ +#define nil 0 +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> + +int main(int argc, char **argv) +{ + int flag; + char *prog; + + /* Try to run the real McCoy. */ +#if __minix_vmd + execv("/usr/sbin/halt", argv); +#else + execv("/usr/bin/halt", argv); +#endif + + if ((prog = strrchr(*argv,'/')) == nil) prog= argv[0]; else argv++; + + sleep(2); /* Not too fast. */ + + reboot(strcmp(prog, "reboot") == 0 ? RBT_REBOOT : RBT_HALT); + + write(2, "reboot call failed\n", 19); + return 1; +} diff --git a/commands/rlogind/Makefile b/commands/rlogind/Makefile new file mode 100755 index 000000000..7792cfd76 --- /dev/null +++ b/commands/rlogind/Makefile @@ -0,0 +1,23 @@ +# Makefile for rlogind. + +CFLAGS = $(OPT) -D_MINIX +LDFLAGS = + +all: rlogind + +OBJ= rlogind.o setup.o + +rlogind: $(OBJ) + $(CC) $(LDFLAGS) -o $@ $(OBJ) + install -S 4kw $@ + +install: /usr/bin/in.rlogind + +/usr/bin/in.rlogind: rlogind + install -c $? $@ + +clean: + rm -f *.o rlogind core a.out + +# Dependencies. +$(OBJ): rlogind.h diff --git a/commands/rlogind/rlogind.c b/commands/rlogind/rlogind.c new file mode 100755 index 000000000..f46b894a9 --- /dev/null +++ b/commands/rlogind/rlogind.c @@ -0,0 +1,455 @@ +/* +rlogind.c + +Created: by Philip Homburg <philip@cs.vu.nl> +Log: Utmp improvement by Kees Bot <kjb@cs.vu.nl> + Split to compile easier on i86 by kjb +*/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <time.h> +#include <unistd.h> +#include <utmp.h> +#include <net/hton.h> +#define EXTERN +#include "rlogind.h" + +char pty_str[]= "/dev/ptyXX"; +char tty_str[]= "/dev/ttyXX"; +char hex_str[16]= "0123456789abcdef"; + +char PATH_UTMP[] = "/etc/utmp"; /* current logins */ +char PATH_WTMP[] = "/usr/adm/wtmp"; /* login/logout history */ + +char term[64]= "TERM="; +#define ENVSIZE (sizeof("TERM=")-1) /* skip null for concatenation. */ +int confirmed= 0; +char *env[2]; +char *args[10]; + +static void do_child(int tty_fd, char *tty_str); +static void dealloc_term(int slot, char *tty_str, int pid); +static void wtmp(char *user, char *id, char *line, int pid, int type, int slot); +static void setup_term(int fd); +static speed_t num2speed(int num); +static int do_control(char *buf, int cnt); +static void readall(char *buf, int cnt); + +int main(int argc, char *argv[]) +{ + int error; + int i, j= 0; + int tty_fd, pty_fd; + int login_pid, write_pid; + int count, bytes, tmp_count; + char *lp= 0, *cp; + struct stat struct_stat; + int slot; + + prog_name= argv[0]; + + /* Check if the remote user is allowed in. */ + authenticate(); + + write(1, "", 1); /* Send the '\0' */ + confirmed= 1; + + /* We try to convince the other side not the do ^S/^Q, the rlogin + * protocol indicates the we only send this when XOFF is turned off + * but we don't know when this happens so we tell the other side that + * it is turned off. + */ + tcp_urg(1, 1); + + write(1, "\220", 1); + + tcp_urg(1, 0); + + /* Let's look for a pty. */ + pty_fd= -1; + for (i= 'p'; i <= 'z'; i++) + { + pty_str[sizeof(pty_str)-3]= i; + pty_str[sizeof(pty_str)-2]= '0'; + error= stat(pty_str, &struct_stat); + if (error == -1) + continue; + for (j= 0; j < 16; j++) + { + pty_str[sizeof(pty_str)-2]= hex_str[j]; + pty_fd= open(pty_str, O_RDWR); + if (pty_fd != -1) + break; + } + if (pty_fd != -1) + break; + } + if (pty_fd == -1) + { + printf("%s: out of ptys\r\n", prog_name); + exit(1); + } + tty_str[sizeof(pty_str)-3]= i; + tty_str[sizeof(pty_str)-2]= hex_str[j]; + + tty_fd= open(tty_str, O_RDWR); + if (tty_fd == -1) + { + printf("%s: unable to open '%s': %s\r\n", prog_name, tty_str, + strerror(errno)); + exit(1); + } + + slot= fttyslot(tty_fd); + + login_pid= fork(); + if (login_pid == -1) + { + printf("%s: unable to fork: %s\r\n", prog_name, + strerror(errno)); + exit(1); + } + if (login_pid == 0) + { + close(pty_fd); + wtmp("", "", tty_str, login_pid, LOGIN_PROCESS, slot); + do_child(tty_fd, tty_str); + } + close(tty_fd); + + write_pid= fork(); + if (write_pid == -1) + { + printf("%s: unable to fork: %s\r\n", prog_name, + strerror(errno)); + exit(1); + } + if (write_pid == 0) + { + dup2(pty_fd, 0); + count= 0; + for (;;) + { + if (!count) + { + count= read(0, line, sizeof(line)); + if (count <= 0) + break; + lp= line; + } + bytes= write(1, lp, count); + if (bytes <= 0 || bytes > count) + break; + lp += bytes; + count -= bytes; + } + kill(getppid(), SIGKILL); + dealloc_term(slot, tty_str, login_pid); + _exit(1); + } + + dup2(pty_fd, 1); + count= 0; + for (;;) + { + if (!count) + { + count= read(0, line, sizeof(line)); + if (count <= 0) + break; + lp= line; + } + tmp_count= count; + cp= memchr(lp, 255, count); + if (cp) + { + tmp_count= cp-lp; + if (tmp_count == 0) + { + tmp_count= do_control(lp, count); + if (tmp_count) + { + lp += tmp_count; + count -= tmp_count; + continue; + } + } + } + bytes= write(1, lp, tmp_count); + if (bytes <= 0 || bytes > count) + break; + lp += bytes; + count -= bytes; + } + kill(write_pid, SIGKILL); + dealloc_term(slot, tty_str, login_pid); + return(0); +} + +static void do_child(int tty_fd, char *tty_str) +{ + int ctty_fd, tst_fd; + FILE *tty_file; + int sav_errno; + char **argp; + + /* Set up the terminal attributes. */ + setup_term(tty_fd); + + /* Let's start the new session. */ + setsid(); + ctty_fd= open(tty_str, O_RDWR); + if (ctty_fd == -1) + { + printf("%s(do_child): unable to open '%s': %s\r\n", + prog_name, tty_str, strerror(errno)); + exit(1); + } + /* Test if we really got a controlling tty. */ + tst_fd= open("/dev/tty", O_RDWR); + if (tst_fd == -1) + { + printf( + "%s(do_child): '%s' didn't result in a controlling tty (%s)\r\n", + prog_name, tty_str, strerror(errno)); + exit(1); + } + + argp= args; + *argp++= "login"; + *argp++= "-p"; + *argp++= "-h"; + *argp++= hostname; + if (authenticated) + *argp++= "-f"; + if (lusername[0] != '\0') + *argp++= lusername; + + /* We reached the point of no return. */ + close(tst_fd); + close(tty_fd); + + if (ctty_fd != 0) + { + dup2(ctty_fd, 0); + close(ctty_fd); + ctty_fd= 0; + } + dup2(ctty_fd, 1); +#if DEBUG + fprintf(stderr, "execing login\r\n"); +#endif + dup2(ctty_fd, 2); + execve("/bin/login", args, env); + if (errno == ENOENT) execve("/usr/bin/login", args, env); + sav_errno= errno; + tty_file= fdopen(2, "w"); + if (tty_file) + { + fprintf(tty_file, "%s(do_child): unable to exec login: %s\r\n", + prog_name, strerror(sav_errno)); + fflush(tty_file); + } + _exit(1); +} + +static void dealloc_term(int slot, char *tty_str, int pid) +{ + wtmp("", "", tty_str, pid, DEAD_PROCESS, slot); + + /* Finally we reset the owner and mode of the terminal. */ + chown(tty_str, 0, 0); + chmod(tty_str, 0666); +} + +static void wtmp( + char *user, /* name of user */ + char *id, /* inittab ID */ + char *line, /* TTY name */ + int pid, /* PID of process */ + int type, /* TYPE of entry */ + int slot) /* slot number in UTMP */ +{ +/* Log an event into the UTMP and WTMP files. */ + + struct utmp utmp; /* UTMP/WTMP User Accounting */ + int fd= -1; + int log = 1; /* log in wtmp */ + char *p; + + /* Strip the /dev part of the TTY name. */ + p = strrchr(line, '/'); + if (p != 0) + line= p+1; + + if (type == DEAD_PROCESS) { + /* Don't add a logout entry for just a dying login. */ + if ((fd = open(PATH_UTMP, O_RDONLY)) < 0) return; + if (lseek(fd, (off_t) slot * sizeof(utmp), SEEK_SET) != -1 + && read(fd, (void *) &utmp, sizeof(utmp)) == sizeof(utmp)) + { + if (utmp.ut_type != INIT_PROCESS + && utmp.ut_type != USER_PROCESS) + log= 0; + } + close(fd); + } + if (type == LOGIN_PROCESS) log= 0; /* and don't log this one */ + + /* Clear the utmp record. */ + memset((void *) &utmp, 0, sizeof(utmp)); + + /* Enter new values. */ + strncpy(utmp.ut_name, user, sizeof(utmp.ut_name)); + strncpy(utmp.ut_id, id, sizeof(utmp.ut_id)); + strncpy(utmp.ut_line, line, sizeof(utmp.ut_line)); + strncpy(utmp.ut_host, hostname, sizeof(utmp.ut_host)); + utmp.ut_pid = pid; + utmp.ut_type = type; + utmp.ut_time = time((time_t *)0); + + if (log) { + if ((fd = open(PATH_WTMP, O_WRONLY | O_APPEND)) < 0) return; + write(fd, (char *) &utmp, sizeof(struct utmp)); + close(fd); + } + + /* write entry to utmp */ + if ((fd = open(PATH_UTMP, O_WRONLY)) < 0) return; + if (lseek(fd, (off_t) slot * sizeof(utmp), SEEK_SET) != -1) + write(fd, (char *) &utmp, sizeof(struct utmp)); + close(fd); +} + +void fatal(int fd, char *msg, int err) +{ + int len; + char buf[80], *bp; + + bp= buf; + if (!confirmed) + *bp++= '\1'; + if (err) + len= sprintf(bp, "rlogind: %s: %s.\r\n", msg, strerror(err)); + else + len= sprintf(bp, "rlogind: %s.\r\n", msg); + write(fd, buf, bp+len-buf); + exit(1); +} + +static void setup_term(int fd) +{ + char *cp, *speed; + struct termios tt; + speed_t spd; + int num; + char *check; + + cp= strchr(term, '/'); + if (cp) + { + tcgetattr(fd, &tt); + *cp++= '\0'; + speed= cp; + cp= strchr(speed, '/'); + if (cp) + *cp++= '\0'; + num= strtol(speed, &check, 0); + spd= num2speed(num); + if (spd != B0 && check[0] == '\0') + { + cfsetospeed(&tt, spd); + cfsetispeed(&tt, spd); + } + tcsetattr(fd, TCSANOW, &tt); + } + env[0]= term; + env[1]= 0; +} + +static speed_t num2speed(int num) +{ + static struct + { + int num; + speed_t value; + } speed_table[]= + { + { 0, B0, }, { 50, B50, }, { 75, B75, }, { 110, B110, }, + { 134, B134, }, { 150, B150, }, { 200, B200, }, { 300, B300, }, + { 600, B600, }, { 1200, B1200, }, { 1800, B1800, }, + { 2400, B2400, }, { 4800, B4800, }, { 9600, B9600, }, + { 19200, B19200, }, { 38400, B38400, }, + { -1, -1 }, + }; + int i; + + for (i= 0; speed_table[i].num != -1; i++) + { + if (speed_table[i].num == num) + return (speed_table[i].value); + } + return B0; +} + +static int do_control(char *cp, int cnt) +{ + char buf[20]; + struct winsize winsize; + + if (cnt > sizeof(buf)) + cnt= sizeof(buf); + + memcpy(buf, cp, cnt); + + /* Let's fetch the first 2 bytes. */ + if (cnt < 2) + readall(buf+cnt, 2-cnt); + if ((unsigned char)buf[1] != 255) + return 0; + + /* Let's fetch the first 4 bytes. */ + if (cnt < 4) + readall(buf+cnt, 4-cnt); + if (buf[2] != 's' || buf[3] != 's') + return 0; + + /* Let's fetch a winsize structure. */ + if (cnt < 4 + sizeof(winsize)) + readall(buf+cnt, 4 + sizeof(winsize) - cnt); + + memcpy(&winsize, buf+4, sizeof(winsize)); + winsize.ws_row= ntohs(winsize.ws_row); + winsize.ws_col= ntohs(winsize.ws_col); + winsize.ws_xpixel= ntohs(winsize.ws_xpixel); + winsize.ws_ypixel= ntohs(winsize.ws_ypixel); +#if DEBUG + fprintf(stderr, "setting window size to %d, %d\r\n", winsize.ws_row, + winsize.ws_col); +#endif + ioctl(1, TIOCSWINSZ, &winsize); + return 4 + sizeof(winsize); +} + +static void readall(char *buf, int cnt) +{ + int res; + + while(cnt) + { + res= read(0, buf, cnt); + if (res <= 0) + return; + buf += cnt; + cnt -= res; + } +} diff --git a/commands/rlogind/rlogind.h b/commands/rlogind/rlogind.h new file mode 100755 index 000000000..8c4e2dcc2 --- /dev/null +++ b/commands/rlogind/rlogind.h @@ -0,0 +1,24 @@ +/* +in.rld.h +*/ + +#define NMAX 30 + +#ifndef EXTERN +#define EXTERN extern +#endif + +EXTERN char *prog_name; +EXTERN char hostname[256+1]; +EXTERN char line[1024]; +EXTERN char lusername[NMAX+1], rusername[NMAX+1]; +EXTERN char term[64]; +EXTERN int authenticated; + +/* in.rld.c: */ +void fatal(int fd, char *msg, int err); + +/* setup.c: */ +void authenticate(void); +int do_rlogin(void); +void tcp_urg(int fd, int on); diff --git a/commands/rlogind/setup.c b/commands/rlogind/setup.c new file mode 100755 index 000000000..72a059be1 --- /dev/null +++ b/commands/rlogind/setup.c @@ -0,0 +1,92 @@ +/* +setup.c +*/ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <net/netlib.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> +#include <net/gen/netdb.h> +#include <net/gen/socket.h> +#include "rlogind.h" + +static void getstr(char *buf, int cnt, char *errmsg); + +void authenticate(void) +{ + int result; + struct nwio_tcpconf tcpconf; + struct hostent *hostent; + char c; + + /* Let's lookup the hostname for the connection. */ + result= ioctl (0, NWIOGTCPCONF, &tcpconf); + if (result<0) + { + fprintf(stderr, "%s: ioctl(NWIOTCPCONF): %s\r\n", + prog_name, strerror(errno)); + exit(1); + } + hostent= gethostbyaddr((char *)&tcpconf.nwtc_remaddr, + sizeof(tcpconf.nwtc_remaddr), AF_INET); + if (hostent) + { + strncpy(hostname, hostent->h_name, sizeof(hostname)-1); + hostname[sizeof(hostname)-1]= '\0'; + } + else + { + strcpy(hostname, inet_ntoa(tcpconf.nwtc_remaddr)); + } + + authenticated = 0; + + getstr(&c, 1, "protocol violation"); + getstr(rusername, sizeof(rusername), "remuser too long"); + getstr(lusername, sizeof(lusername), "locuser too long"); + strcpy(term, "TERM="); + getstr(term+5, sizeof(term)-5, "Terminal type too long"); + +#if DEBUG + fprintf(stderr, "got lu= %s, ru= %s, te= %s\r\n", lusername, rusername, + term); +#endif + if (iruserok(tcpconf.nwtc_remaddr, 0, rusername, lusername) == 0) + authenticated = 1; +} + +static void getstr(char *buf, int cnt, char *errmsg) +{ + char c; + + errno= 0; + do + { + if (read(0, &c, 1) != 1) + fatal(1, "read failed", errno); + cnt--; + if (cnt < 0) + fatal(1, errmsg, 0); + *buf++= c; + } while(c != 0); +} + +void tcp_urg(int fd, int on) +{ + struct nwio_tcpopt tcpopt; + + tcpopt.nwto_flags= on ? (NWTO_BSD_URG | NWTO_SND_URG) : NWTO_SND_NOTURG; + if (ioctl(1, NWIOSTCPOPT, &tcpopt) == -1) + { + fprintf(stderr, "rlogind: NWIOSTCPOPT failed: %s\r\n", + strerror(errno)); + } +} diff --git a/commands/scripts/DESCRIBE.sh b/commands/scripts/DESCRIBE.sh new file mode 100755 index 000000000..5ef749d8e --- /dev/null +++ b/commands/scripts/DESCRIBE.sh @@ -0,0 +1,188 @@ +#!/bin/sh +# +# DESCRIBE 2.2 - Describe the given devices. Author: Kees J. Bot +# +# BUGS +# - Arguments may not contain shell metacharacters. + +case $# in +0) flag=; set -$- /dev ;; +*) flag=d ;; +esac + +ls -l$flag $* | \ +sed -e '/^total/d' \ + -e '/^[^bc]/s/.* /BAD BAD /' \ + -e '/^[bc]/s/.* \([0-9][0-9]*\), *\([0-9][0-9]*\).* /\1 \2 /' \ +| { +ex=0 # exit code + +while read major minor path +do + case $path in + /*) name=`expr $path : '.*/\\(.*\\)$'` + ;; + *) name=$path + esac + dev= des= + + case $major in # One of the controllers? What is its controller nr? + 3) ctrlr=0 ;; + 8) ctrlr=1 ;; + 10) ctrlr=2 ;; + 12) ctrlr=3 ;; + esac + + case $major,$minor in + 1,0) des="RAM disk" dev=ram + ;; + 1,1) des="memory" dev=mem + ;; + 1,2) des="kernel memory" dev=kmem + ;; + 1,3) des="null device, data sink" dev=null + ;; + 2,*) drive=`expr $minor % 4` + case `expr $minor - $drive` in + 0) des='auto density' dev="fd$drive" + ;; + 4) des='360k, 5.25"' dev="pc$drive" + ;; + 8) des='1.2M, 5.25"' dev="at$drive" + ;; + 12) des='360k in 720k, 5.25"' dev="qd$drive" + ;; + 16) des='720k, 3.5"' dev="ps$drive" + ;; + 20) des='360k in 1.2M, 5.25"' dev="pat$drive" + ;; + 24) des='720k in 1.2M, 5.25"' dev="qh$drive" + ;; + 28) des='1.44M, 3.5"' dev="PS$drive" + ;; + 112) des='auto partition 0' dev="fd${drive}p0" + ;; + 116) des='auto partition 1' dev="fd${drive}p1" + ;; + 120) des='auto partition 2' dev="fd${drive}p2" + ;; + 124) des='auto partition 3' dev="fd${drive}p3" + ;; + *) dev=BAD + esac + des="floppy drive $drive ($des)" + ;; + [38],[05]|[38],[123][05]|1[02],[05]|1[02],[123][05]) + drive=`expr $minor / 5` + des="controller $ctrlr disk $drive" dev=c${ctrlr}d${drive} + ;; + [38],?|[38],[123]?|1[02],?|1[02],[123]?) + drive=`expr $minor / 5` + par=`expr $minor % 5 - 1` + des="controller $ctrlr disk $drive partition $par" + dev=c${ctrlr}d${drive}p${par} + ;; + [38],12[89]|[38],1[3-9]?|[38],2??|1[02],12[89]|1[02],1[3-9]?|1[02],2??) + drive=`expr \\( $minor - 128 \\) / 16` + par=`expr \\( \\( $minor - 128 \\) / 4 \\) % 4` + sub=`expr \\( $minor - 128 \\) % 4` + des="hard disk $drive, partition $par, subpartition $sub" + des="controller $ctrlr disk $drive partition $par slice $sub" + #par=`expr $drive '*' 5 + $par` + dev=c${ctrlr}d${drive}p${par}s${sub} + ;; + [38],6[4-9]|[38],7?|1[02],6[4-9]|1[02],7?) + tape=`expr \\( $minor - 64 \\) / 2` + case $minor in + *[02468]) + des="controller $ctrlr tape $tape (non-rewinding)" + dev=c${ctrlr}t${tape}n + ;; + *[13579]) + des="controller $ctrlr tape $tape (rewinding)" + dev=c${ctrlr}t${tape} + esac + ;; + 4,0) des="console device" dev=console + ;; + 4,[1-7])des="virtual console $minor" dev=ttyc$minor + ;; + 4,15) des="diagnostics device" dev=log + ;; + 4,1[6-9]) + line=`expr $minor - 16` + des="serial line $line" dev=tty0$line + ;; + 4,12[89]|4,1[3-8]?|4,19[01]) + p=`expr \\( $minor - 128 \\) / 16 | tr '0123' 'pqrs'` + n=`expr $minor % 16` + test $n -ge 10 && n=`expr $n - 10 | tr '012345' 'abcdef'` + des="pseudo tty `expr $minor - 128`" dev=tty$p$n + ;; + 4,???) + p=`expr \\( $minor - 192 \\) / 16 | tr '0123' 'pqrs'` + n=`expr $minor % 16` + test $n -ge 10 && n=`expr $n - 10 | tr '012345' 'abcdef'` + des="controller of tty$p$n" dev=pty$p$n + ;; + 5,0) des="anonymous tty" dev=tty + ;; + 6,0) des="line printer, parallel port" dev=lp + ;; + 7,*) + d=`expr $minor % 8` + n=`expr $minor / 8` + case $d in + 0) case $name in + psip*) + des="Pseudo IP #$n" dev=psip + ;; + *) des="raw ethernet #$n" dev=eth + esac + ;; + 1) des="raw IP #$n" dev=ip + ;; + 2) des="TCP/IP #$n" dev=tcp + ;; + 3) des="UDP #$n" dev=udp + esac + case $d in + [0123]) + if [ "$name" = "$dev" ] + then + des="$des (default)" + else + dev=$dev$n + fi + esac + ;; + 13,0) + des="audio" dev=audio + ;; + 14,0) + des="audio mixer" dev=mixer + ;; + BAD,BAD) + des= dev= + ;; + *) dev=BAD + esac + + case $name:$dev in + *:) + echo "$path: not a device" >&2 + ex=1 + ;; + *:*BAD*) + echo "$path: cannot describe: major=$major, minor=$minor" >&2 + ex=1 + ;; + $dev:*) + echo "$path: $des" + ;; + *:*) echo "$path: nonstandard name for $dev: $des" + esac +done + +exit $ex +} diff --git a/commands/scripts/M.sh b/commands/scripts/M.sh new file mode 100755 index 000000000..da6beb7cb --- /dev/null +++ b/commands/scripts/M.sh @@ -0,0 +1,28 @@ +#!/bin/sh +# +# M, U - mount or unmount standard devices. + +case $#:$2 in +1:|2:-r) ;; +*) echo "Usage: $0 <abbreviation> [-r]" >&2; exit 1 +esac + +. /etc/fstab + +dev=$1 dir=$1 + +case $1 in +0) dev=/dev/fd0 dir=fd0 ;; +1) dev=/dev/fd1 dir=fd1 ;; +PS0|at0|fd0|pat0|pc0|ps0) dev=/dev/$dev dir=fd0 ;; +PS1|at1|fd1|pat1|pc1|ps1) dev=/dev/$dev dir=fd1 ;; +root) dev=$root ;; +tmp) dev=$tmp ;; +usr) dev=$usr ;; +*) dev=/dev/$dev dir=mnt +esac + +case $0 in +*M) mount $dev /$dir $2 ;; +*U) umount $dev +esac diff --git a/commands/scripts/MAKEDEV.sh b/commands/scripts/MAKEDEV.sh new file mode 100755 index 000000000..ef1da1d9a --- /dev/null +++ b/commands/scripts/MAKEDEV.sh @@ -0,0 +1,222 @@ +#!/bin/sh +# +# MAKEDEV 3.3 - Make special devices. Author: Kees J. Bot + +case $1 in +-n) e=echo; shift ;; # Just echo when -n is given. +*) e= +esac + +case $#:$1 in +1:std) # Standard devices. + set -$- mem fd0 fd1 fd0p0 fd1p0 \ + c0d0 c0d0p0 c0d0p0s0 c0d1 c0d1p0 c0d1p0s0 \ + c0d2 c0d2p0 c0d2p0s0 c0d3 c0d3p0 c0d3p0s0 \ + tty ttyc1 ttyc2 ttyc3 tty00 tty01 ttyp0 ttyp1 ttyp2 ttyp3 eth + ;; +0:|1:-\?) + cat >&2 <<EOF +Usage: $0 [-n] key ... +Where key is one of the following: + ram mem kmem null # One of these makes all these memory devices + fd0 fd1 ... # Floppy devices for drive 0, 1, ... + fd0p0 fd1p0 ... # Make floppy partitions fd0p[0-3], fd1p[0-3], ... + c0d0 c0d1 ... # Make disks c0d0, c0d1, ... + c0d0p0 c0d1p0 ... # Make partitions c0d0p[0-3], c0d1p[0-3], ... + c0d0p0s0 c0d1p0s0 ... # Subparts c0d0p[0-3]s[0-3], c0d1p[0-3]s[0-3], ... + c1d0(p0)(s0) # Likewise for controller 1 + c0t0 c0t1 c1t0 ... # Make tape devices c0t0, c0t0n, c0t1, ... + console lp tty log # One of these makes all four + ttyc1 ... ttyc7 # Virtual consoles + tty00 ... tty03 # Make serial lines + ttyp0 ... ttyq0 ... # Make tty, pty pairs + eth ip tcp udp # One of these makes some TCP/IP devices + audio mixer # Make audio devices + std # All standard devices +EOF + exit 1 +esac + +umask 077 +ex=0 + +for dev +do + case $dev in # One of the controllers? Precompute major device nr. + c0*) maj=3 ;; + c1*) maj=8 ;; + c2*) maj=10 ;; + c3*) maj=12 ;; + esac + + case $dev in + ram|mem|kmem|null) + # Memory devices. + # + $e mknod ram b 1 0; $e chmod 600 ram + $e mknod mem c 1 1; $e chmod 640 mem + $e mknod kmem c 1 2; $e chmod 640 kmem + $e mknod null c 1 3; $e chmod 666 null + $e chgrp kmem ram mem kmem null + ;; + fd[0-3]) + # Floppy disk drive n. + # + d=`expr $dev : '.*\\(.\\)'` # Drive number. + $e mknod $dev b 2 $d + $e chmod 666 $dev + ;; + pc[0-3]|at[0-3]|qd[0-3]|ps[0-3]|pat[0-3]|qh[0-3]|PS[0-3]) + # Obsolete density locked floppy disk drive n. + # + d=`expr $dev : '.*\\(.\\)'` # Drive number. + m=$d # Minor device number. + + $e mknod pc$d b 2 $m; m=`expr $m + 4` + $e mknod at$d b 2 $m; m=`expr $m + 4` + $e mknod qd$d b 2 $m; m=`expr $m + 4` + $e mknod ps$d b 2 $m; m=`expr $m + 4` + $e mknod pat$d b 2 $m; m=`expr $m + 4` + $e mknod qh$d b 2 $m; m=`expr $m + 4` + $e mknod PS$d b 2 $m; m=`expr $m + 4` + + $e chmod 666 pc$d at$d qd$d ps$d pat$d qh$d PS$d + ;; + fd[0-3]p[0-3]) + # Floppy disk partitions. + # + n=`expr $dev : '\\(.*\\)..'` # Name prefix. + d=`expr $dev : '..\\(.\\)'` # Drive number. + m=`expr 112 + $d` # Minor of partition 0. + alldev= + + for p in 0 1 2 3 + do + m=`expr 112 + $d + $p '*' 4` # Minor of partition $p. + $e mknod ${n}p${p} b 2 $m # Make e.g. fd0p0 - fd0p3 + alldev="$alldev ${n}p${p}" + done + $e chmod 666 $alldev + ;; + c[0-3]d[0-7]) + # Whole disk devices. + d=`expr $dev : '...\\(.\\)'` # Disk number. + m=`expr $d '*' 5` # Minor device number. + $e mknod $dev b $maj $m + $e chmod 600 $dev + ;; + c[0-3]d[0-7]p[0-3]) + # Disk primary partitions. + n=`expr $dev : '\\(.*\\).'` # Name prefix. + d=`expr $dev : '...\\(.\\)'` # Disk number. + alldev= + + for p in 0 1 2 3 + do + m=`expr $d '*' 5 + 1 + $p` # Minor device number. + $e mknod $n$p b $maj $m + alldev="$alldev $n$p" + done + $e chmod 600 $alldev + ;; + c[0-3]d[0-7]p[0-3]s[0-3]) + # Disk subpartition. + n=`expr $dev : '\\(.*\\)...'` # Name prefix. + d=`expr $dev : '...\\(.\\)'` # Disk number. + alldev= + + for p in 0 1 2 3 + do + for s in 0 1 2 3 + do + m=`expr 128 + $d '*' 16 + $p '*' 4 + $s` # Minor device nr. + $e mknod ${n}${p}s${s} b $maj $m + alldev="$alldev ${n}${p}s${s}" + done + done + $e chmod 600 $alldev + ;; + c[0-3]t[0-7]|c[0-3]t[0-7]n) + # Tape devices. + n=`expr $dev : '\\(....\\)'` # Name prefix. + t=`expr $dev : '...\\(.\\)'` # Tape number. + m=`expr 64 + $t '*' 2` # Minor device number. + $e mknod ${n}n c $maj $m + $e mknod ${n} c $maj `expr $m + 1` + $e chmod 660 ${n}n ${n} + ;; + console|lp|tty|log) + # Console, line printer, anonymous tty, diagnostics device. + # + $e mknod console c 4 0 + $e chmod 600 console + $e chgrp tty console + $e mknod tty c 5 0 + $e chmod 666 tty + $e mknod lp c 6 0 + $e chown daemon lp + $e chgrp daemon lp + $e chmod 200 lp + $e mknod log c 4 15 + $e chmod 222 log + ;; + ttyc[1-7]) + # Virtual consoles. + # + m=`expr $dev : '....\\(.*\\)'` # Minor device number. + $e mknod $dev c 4 $m + $e chgrp tty $dev + $e chmod 600 $dev + ;; + tty0[0-3]) + # Serial lines. + # + n=`expr $dev : '.*\\(.\\)'` + $e mknod $dev c 4 `expr $n + 16` + $e chmod 666 $dev + $e chgrp tty $dev + ;; + tty[p-s][0-9a-f]|pty[p-s][0-9a-f]) + # Pseudo ttys. + # + dev=`expr $dev : '...\\(..\\)'` + g=`expr $dev : '\\(.\\)'` # Which group. + g=`echo $g | tr 'pqrs' '0123'` + n=`expr $dev : '.\\(.\\)'` # Which pty in the group. + case $n in + [a-f]) n=1`echo $n | tr 'abcdef' '012345'` + esac + + $e mknod tty$dev c 4 `expr $g '*' 16 + $n + 128` + $e mknod pty$dev c 4 `expr $g '*' 16 + $n + 192` + $e chgrp tty tty$dev pty$dev + $e chmod 666 tty$dev pty$dev + ;; + eth|ip|tcp|udp|eth0|ip0|tcp0|udp0) + # TCP/IP devices. + # + $e mknod eth0 c 7 0 # Network 0 (Ethernet) + $e mknod ip0 c 7 1 + $e mknod tcp0 c 7 2 + $e mknod udp0 c 7 3 + $e chmod 600 eth0 ip0 + $e chmod 666 tcp0 udp0 + $e ln -f eth0 eth # Default interface + $e ln -f ip0 ip + $e ln -f tcp0 tcp + $e ln -f udp0 udp + ;; + audio|mixer) + # Audio devices. + # + $e mknod audio c 13 0 + $e mknod mixer c 14 0 + $e chmod 666 audio mixer + ;; + *) + echo "$0: don't know about $dev" >&2 + ex=1 + esac +done + +exit $ex diff --git a/commands/scripts/Makefile b/commands/scripts/Makefile new file mode 100755 index 000000000..4478249c2 --- /dev/null +++ b/commands/scripts/Makefile @@ -0,0 +1,132 @@ +# Makefile for cmd/scripts. + +CFLAGS = -D_MINIX -D_POSIX_SOURCE +CCLD = $(CC) -i $(CFLAGS) +MAKE = exec make -$(MAKEFLAGS) install + +all: # Does nothing + +install: usr root + +# Commands on the /usr partition. +usr: \ + /usr/bin/DESCRIBE \ + /usr/bin/M \ + /usr/bin/U \ + /usr/bin/MAKEDEV \ + /usr/bin/adduser \ + /usr/bin/cd \ + /usr/bin/[ \ + /usr/bin/command \ + /usr/bin/echo \ + /usr/bin/expr \ + /usr/bin/false \ + /usr/bin/getopts \ + /usr/bin/read \ + /usr/bin/test \ + /usr/bin/true \ + /usr/bin/umask \ + /usr/bin/wait \ + /usr/bin/checkhier \ + /usr/bin/clear \ + /usr/bin/clr \ + /usr/bin/makewhatis \ + /usr/bin/mkdist \ + /usr/bin/setup \ + /usr/bin/spell \ + /usr/bin/srccrc \ + /usr/bin/svclog \ + /usr/bin/whatis \ + /usr/bin/apropos \ + /usr/bin/whereis \ + +# Commands on the root partition. +root: \ + /bin/M \ + /bin/U \ + /bin/cd \ + /bin/[ \ + /bin/command \ + /bin/echo \ + /bin/expr \ + /bin/false \ + /bin/getopts \ + /bin/read \ + /bin/test \ + /bin/true \ + /bin/umask \ + /bin/wait \ + +clean: + +/usr/bin/DESCRIBE: DESCRIBE.sh + install -c -o bin $? $@ + +/usr/bin/M: M.sh + install -c -o bin $? $@ + +/usr/bin/U: /usr/bin/M + install -l $? $@ + +/usr/bin/MAKEDEV: MAKEDEV.sh + install -c -o bin $? $@ + +/usr/bin/adduser: adduser.sh + install -c -o bin $? $@ + +/usr/bin/cd: cd.sh + install -c -o bin $? $@ + +/usr/bin/[ /usr/bin/command /usr/bin/echo /usr/bin/expr /usr/bin/false \ +/usr/bin/getopts /usr/bin/read /usr/bin/test /usr/bin/true /usr/bin/umask \ +/usr/bin/wait: /usr/bin/cd + install -l $? $@ + +/usr/bin/checkhier: checkhier.sh + install -c -o bin $? $@ + +/usr/bin/clear: clear.sh + install -c -o bin $? $@ + +/usr/bin/clr: /usr/bin/clear + install -l $? $@ + +/usr/bin/makewhatis: makewhatis.sh + install -c -o bin $? $@ + +/usr/bin/mkdist: mkdist.sh + install -c -o bin $? $@ + +/usr/bin/setup: setup.sh + install -c -o bin $? $@ + +/usr/bin/spell: spell.sh + install -c -o bin $? $@ + +/usr/bin/srccrc: srccrc.sh + install -c -o bin $? $@ + +/usr/bin/svclog: svclog.sh + install -c -o bin $? $@ + +/usr/bin/whatis: whatis.sh + install -c -o bin $? $@ + +/usr/bin/apropos: /usr/bin/whatis + install -l $? $@ + +/usr/bin/whereis: whereis.sh + install -c -o bin $? $@ + +/bin/M: /usr/bin/M + install -c -o bin $? $@ + +/bin/U: /bin/M + install -l $? $@ + +/bin/cd: /usr/bin/cd + install -lc $? $@ + +/bin/[ /bin/command /bin/echo /bin/expr /bin/false /bin/getopts \ +/bin/read /bin/test /bin/true /bin/umask /bin/wait: /bin/cd + install -l $? $@ diff --git a/commands/scripts/adduser.sh b/commands/scripts/adduser.sh new file mode 100755 index 000000000..886d0fb5f --- /dev/null +++ b/commands/scripts/adduser.sh @@ -0,0 +1,121 @@ +#!/bin/sh +# +# adduser 1.0 - add a new user to the system Author: Kees J. Bot +# 16 Jan 1996 + +# Check arguments. +case "$#" in +3) user="$1"; group="$2"; home="$3" + ;; +*) echo "Usage: adduser user group home-dir" >&2; exit 1 +esac + +# We need to be root. +case "`id`" in +'uid=0('*) + ;; +*) echo "adduser: you must be root to add users" >&2; exit 1 +esac + +# User and group names must be alphanumeric and no longer than 8 characters. +len=`expr "$user" : '[a-z][a-z0-9]*$'` +if [ "$len" -eq 0 -o "$len" -gt 8 ] +then + echo >&2 \ +"adduser: the user name must be alphanumeric and no longer than 8 characters" + exit 1 +fi + +len=`expr "$group" : '[a-z][a-z0-9]*$'` +if [ "$len" -eq 0 -o "$len" -gt 8 ] +then + echo >&2 \ +"adduser: the group name must be alphanumeric and no longer than 8 characters" + exit 1 +fi + +# The new user name must not exist, but the group must exist. +if grep "^$user:" /etc/passwd >/dev/null +then + echo "adduser: user $user already exists" >&2 + exit 1 +fi + +gid=`sed -e "/^$group:/!d" -e 's/^[^:]*:[^:]*:\\([^:]*\\):.*/\\1/' /etc/group` +if [ `expr "$gid" : '[0-9]*$'` -eq 0 ] +then + echo "adduser: group $group does not exist" >&2 + exit 1 +fi + +# Find the first free user-id of 10 or higher. +uid=10 +while grep "^[^:]*:[^:]*:$uid:.*" /etc/passwd >/dev/null +do + uid=`expr $uid + 1` +done + +# No interruptions. +trap '' 1 2 3 15 + +# Lock the password file. +ln /etc/passwd /etc/ptmp || { + echo "adduser: password file busy, try again later" + exit 1 +} + +# Make the new home directory, it should not exist already. +mkdir "$home" || { + rm -rf /etc/ptmp + exit 1 +} + +# Make the new home directory by copying the honorary home directory of our +# fearless leader. +echo cpdir /usr/ast "$home" +cpdir /usr/ast "$home" || { + rm -rf /etc/ptmp "$home" + exit 1 +} + +# Change the ownership to the new user. +echo chown -R $uid:$gid "$home" +chown -R $uid:$group "$home" || { + rm -rf /etc/ptmp "$home" + exit 1 +} + +# Is there a shadow password file? If so add an entry. +if [ -f /etc/shadow ] +then + echo "echo $user::0:0::: >>/etc/shadow" + echo "$user::0:0:::" >>/etc/shadow || { + rm -rf /etc/ptmp "$home" + exit 1 + } + pwd="##$user" +else + pwd= +fi + +# Finish up by adding a password file entry. +echo "echo $user:$pwd:$uid:$gid:$user:$home: >>/etc/passwd" +echo "$user:$pwd:$uid:$gid:$user:$home:" >>/etc/passwd || { + rm -rf /etc/ptmp "$home" + exit 1 +} + +# Remove the lock. +rm /etc/ptmp || exit + +echo " +The new user $user has been added to the system. Note that the password, +full name, and shell may be changed with the commands passwd(1), chfn(1), +and chsh(1). The password is now empty, so only console logins are possible." +if [ $gid = 0 ] +then + echo "\ +Also note that a new operator needs an executable search path (\$PATH) that +does not contain the current directory (an empty field or "." in \$PATH)." +fi +exit 0 diff --git a/commands/scripts/cd.sh b/commands/scripts/cd.sh new file mode 100755 index 000000000..e1f28e46a --- /dev/null +++ b/commands/scripts/cd.sh @@ -0,0 +1,11 @@ +#!/bin/sh +# +# cd 1.3 - equivalents for normally builtin commands. Author: Kees J. Bot + +case $0 in +*/*) command="`expr "$0" : '.*/\(.*\)'`" + ;; +*) command="$0" +esac + +"$command" "$@" diff --git a/commands/scripts/checkhier.sh b/commands/scripts/checkhier.sh new file mode 100755 index 000000000..3fea9de38 --- /dev/null +++ b/commands/scripts/checkhier.sh @@ -0,0 +1,183 @@ +#!/bin/sh +# +# checkhier 2.7 - check the directory hierarchy Author: Kees J. Bot +# 7 May 1995 + +case "`id`" in +'uid=0('*) ;; +*) echo "$0: must be run by root" >&2 + exit 1 +esac + +# List of all interesting top level files and directories, with mode, +# owner and group. Only the key files are listed, the rest is owned +# by bin, has mode 755 or 644, and is not critical to the operation of +# the system. +{ + cat <<'EOF' +drwxr-xr-x root operator / +drwxr-xr-x bin operator /bin +drwxr-xr-x root operator /dev +drwxr-xr-x root operator /etc +-rw-r--r-- root operator /etc/fstab +-rw-r--r-- root operator /etc/group +-rw-r--r-- root operator /etc/hostname.file +-rw-r--r-- root operator /etc/inet.conf +-rw-r--r-- root operator /etc/motd +-rw-r--r-- root operator /etc/mtab +-rw-r--r-- root operator /etc/passwd +-rw-r--r-- root operator /etc/profile +-rw-r--r-- root operator /etc/protocols +-rw-r--r-- root operator /etc/rc +-rw-r--r-- root operator /etc/services +-rw------- root operator /etc/shadow +-rw-r--r-- root operator /etc/termcap +-rw-r--r-- root operator /etc/ttytab +-rw-r--r-- root operator /etc/utmp +dr-xr-xr-x root operator /fd0 +dr-xr-xr-x root operator /fd1 +dr-xr-xr-x root operator /mnt +dr-xr-xr-x root operator /root +drwxrwxrwx root operator /tmp +drwxr-xr-x root operator /usr +drwxr-xr-x root operator /usr/adm +-rw-r--r-- root operator /usr/adm/lastlog +-rw-r--r-- root operator /usr/adm/wtmp +drwxr-xr-x ast other /usr/ast +drwxr-xr-x bin operator /usr/bin +drwxr-xr-x root operator /usr/etc +drwxr-xr-x bin operator /usr/include +drwxr-xr-x bin operator /usr/lib +drwxrwxr-x root operator /usr/local +drwxrwxr-x bin operator /usr/local/bin +drwxrwxr-x bin operator /usr/local/include +drwxrwxr-x bin operator /usr/local/lib +drwxrwxr-x bin operator /usr/local/man +drwxrwxr-x bin operator /usr/local/src +drwxr-xr-x bin operator /usr/man +drwxr-xr-x bin operator /usr/mdec +drwx------ root operator /usr/preserve +drwxr-xr-x root operator /usr/run +drwxr-xr-x root operator /usr/spool +drwx--x--x root operator /usr/spool/at +drwx--x--x root operator /usr/spool/at/past +drwx------ root operator /usr/spool/crontabs +drwxrwxr-x root uucp /usr/spool/locks +drwx------ daemon daemon /usr/spool/lpd +drwxr-xr-x bin operator /usr/src +drwxrwxrwx root operator /usr/tmp +-rwsr-xr-x root ? /usr/bin/at +-rwsr-xr-x root ? /usr/bin/chfn +-rwsr-xr-x root ? /usr/bin/chsh +-rwsr-xr-x root ? /usr/bin/df +-rwsr-xr-x root ? /usr/bin/elvprsv +-rwsr-xr-x root ? /usr/bin/elvrec +-rwsr-xr-x root ? /usr/bin/format +-rwsr-xr-x root ? /usr/bin/hostaddr +-rwsr-xr-x root ? /usr/bin/install +-rwsr-xr-x daemon ? /usr/bin/lpd +-rwsr-xr-x root ? /usr/bin/mail +-rwsr-xr-x root ? /usr/bin/mount +-rwsr-xr-x root ? /usr/bin/passwd +-rwsr-xr-x root ? /usr/bin/ping +-rwxr-sr-x ? kmem /usr/bin/ps +-rwsr-xr-- root ? /usr/bin/shutdown +-rwsr-xr-x root ? /usr/bin/su +-rwxr-sr-x ? uucp /usr/bin/term +-rwsr-xr-x root ? /usr/bin/umount +-rwxr-sr-x ? tty /usr/bin/write +EOF + +} | { + # Check if each file has the proper attributes. Offer a correction + # if not. + banner="\ +# List of commands to fix the top level hierarchy. Do not apply these +# commands blindly, but check and repair by hand. +" + + while read mode owner group file + do + ( # "fix" a memory leak in set... + + set -$- `ls -ld $file 2>/dev/null` '' '' '' '' + curmode=$1 curowner=$3 curgroup=$4 + test $owner = '?' && curowner=$owner + test $group = '?' && curgroup=$group + + # File types? + if [ x`expr "$mode" : '\\(.\\)'` != \ + x`expr "$curmode" : '\\(.\\)'` ] + then + case $curmode in + ?*) echo "${banner}rm -r $file" + banner= + esac + curmode= curowner= curgroup= + case $mode in + d*) echo "${banner}mkdir $file" + ;; + -*) echo "${banner}> $file" + ;; + *) echo "$0: $mode $file: unknown filetype" >&2 + exit 1 + esac + banner= + fi + + # Mode? + if [ x$mode != x$curmode ] + then + octmode= + m=$mode + for i in u g o + do + r=0 w=0 x=0 + case $m in + ?r??*) r=4 + esac + case $m in + ??w?*) w=2 + esac + case $m in + ???[xst]*) x=1 + esac + octmode=$octmode`expr $r + $w + $x` + m=`expr $m : '...\\(.*\\)'` + done + r=0 w=0 x=0 + case $mode in + ???[sS=]??????) r=4 + esac + case $mode in + ??????[sS=]???) w=2 + esac + case $mode in + ?????????[tT=]) x=1 + esac + case $r$w$x in + 000) ;; + *) octmode=`expr $r + $w + $x`$octmode + esac + + echo "${banner}chmod $octmode $file" + banner= + fi + + # Ownership? + if [ x$owner != x$curowner -o x$group != x$curgroup ] + then + echo "${banner}chown $owner:$group $file" + banner= + fi + + # The Minix shell forgets processes, so wait explicitly. + wait + + case "$banner" in '') exit 1;; *) exit 0;; esac) || banner= + done + case "$banner" in + '') exit 1 + esac + exit 0 +} diff --git a/commands/scripts/clear.sh b/commands/scripts/clear.sh new file mode 100755 index 000000000..635f5ae9a --- /dev/null +++ b/commands/scripts/clear.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# +# clear 1.0 - clear the screen Author: Kees J. Bot +# 30 Mar 1996 +export PATH=/bin:/usr/bin +exec tget -str cl diff --git a/commands/scripts/makewhatis.sh b/commands/scripts/makewhatis.sh new file mode 100755 index 000000000..71b87accc --- /dev/null +++ b/commands/scripts/makewhatis.sh @@ -0,0 +1,84 @@ +#!/bin/sh +# +# makewhatis 2.0 - make whatis(5) database. Author: Kees J. Bot. +# +# Make the whatis database of a man directory from the manual pages. + +case $1 in +-*) set -$- x x +esac + +case $# in +1) ;; +*) echo "Usage: $0 <mandir>" >&2 + exit 1 +esac + +cd $1 || exit + +{ + # First pass, gathering the .SH NAME lines in various forms. + + # First the man[1-8] directories, the titles are under the .SH NAME + # section header. + for chap in 1 2 3 4 5 6 7 8 + do + for page in man$chap/*.$chap + do + if test -f "$page"; then # (Old sh barfs on 'continue') + + sed -e 's/ / /g + /^\.SH NAME/,/^\.SH /!d + /^\.SH /d + s/\\f.//g # should not be needed + s/\\s[+-].//g + s/\\s.//g + s/\\// + '"s/ - / ($chap) - /" < "$page" + fi + done + done + + # The Minix "Book style" documents, look for .CD + for page in man9/*.9 + do + if test -f "$page"; then + + sed -e 's/ / /g + /^\.CD /!d + s/^[^"]*"// + s/"[^"]*$// + s/\\(en/-/g + s/\\f.//g + s/\\s[+-].//g + s/\\s.//g + s/\\\*(M2/MINIX/g + s/\\// + '"s/ - / (9) - /" < "$page" + fi + done + + # Some people throw extra flat text files into the cat[1-9] + # directories. It would be nice if man(1) can find them. + trap 'rm -f /tmp/mkw[cmn]$$; exit 1' 1 2 15 + for chap in 1 2 3 4 5 6 7 8 9 + do + ls cat$chap 2>/dev/null >/tmp/mkwc$$ + ls man$chap 2>/dev/null >/tmp/mkwm$$ + comm -23 /tmp/mkwc$$ /tmp/mkwm$$ >/tmp/mkwn$$ + sed -e "/.*\\.$chap\$/!d + s/\\.$chap\$/ ($chap) - ???/" < /tmp/mkwn$$ + done + rm -f /tmp/mkw[cmn]$$ +} | { + # Second pass, remove empty lines, leading and trailing spaces, + # multiple spaces to one space, remove lines without a dash. + sed -e 's/ */ /g + s/^ // + s/ $// + /^$/d + /-/!d' +} | { + # Third pass, sort by section. + sort -t'(' +1 -o whatis +} diff --git a/commands/scripts/mkdist.sh b/commands/scripts/mkdist.sh new file mode 100755 index 000000000..fc5a97ca1 --- /dev/null +++ b/commands/scripts/mkdist.sh @@ -0,0 +1,301 @@ +#!/bin/sh +# +# mkdist 3.6 - Make a Minix distribution Author: Kees J. Bot +# 20 Dec 1994 + +system=`uname` + +usage() +{ + case $system in + Minix) echo "Usage: $0" >&2 + ;; + Minix-vmd) echo "Usage: $0 base-path root-device usr-device" >&2 + esac + exit 1 +} + +# No options. +while getopts '' opt; do usage; done +shift `expr $OPTIND - 1` + +case $system:$# in +Minix:0) + # Interactive. + case "$0" in + /tmp/*) + rm -f "$0" + ;; + *) # Move out of /usr. + cp -p "$0" /tmp/mkdist + exec /tmp/mkdist + esac + std=t + base=/ + export PATH=/bin:/usr/bin + ;; +Minix-vmd:3) + # Called by an external script from Minix-vmd to help make a distribution. + std= + base="$1" rootdev="$2" usrdev="$3" +esac + +usrlist=" +bin +bin/MAKEDEV +bin/arch +bin/badblocks +bin/chmod +bin/clone +bin/compress +bin/cp +bin/cpdir +bin/df +`test -f $base/usr/bin/mtools || echo bin/dosdir bin/dosread bin/doswrite` +`test -f $base/usr/bin/mtools && echo bin/mtools` +bin/edparams +bin/getty +bin/grep +bin/installboot +bin/isodir +bin/isoinfo +bin/isoread +bin/kill +bin/ln +bin/login +bin/ls +bin/mined +bin/mkdir +bin/mkfs +bin/mknod +bin/mkswap +bin/mv +bin/od +bin/part +bin/partition +bin/readall +bin/repartition +bin/rm +bin/rmdir +bin/sed +bin/setup +bin/shutdown +bin/sleep +bin/sort +bin/stty +bin/sysenv +bin/tar +bin/uname +bin/uncompress +bin/update +bin/vol +bin/zcat +etc +etc/rc +lib +lib/keymaps +`cd $base/usr && echo lib/keymaps/*` +lib/pwdauth +mdec +mdec/boot +mdec/bootblock +mdec/jumpboot +mdec/masterboot +tmp +" + +if [ "$std" ] +then + # Find the root device, and the real root device. + . /etc/fstab + realroot=`printroot -r` + if [ $realroot = $root ] + then + rootdir=/ + else + umount $root >/dev/null 2>&1 + mount $root /root || exit + rootdir=/root + fi + + echo -n " +The installation root and /usr can be put on either one diskette of at least +1.2 Mb, or on two diskettes of at least 720 kb. + +Do you want to use a single diskette of at least 1.2 Mb? [y] "; read single + + case $single in + ''|[yY]*|sure) + single=t + ;; + *) single= + esac + + echo -n "Which drive to use? [0] "; read drive + + case $drive in + '') drive=0 + ;; + [01]) ;; + *) echo "Please type '0' or '1'" >&2; exit 1 + esac + + if [ "$single" ] + then + echo -n "Insert the root+usr diskette in drive $drive and hit RETURN" + else + echo -n "Insert the root diskette in drive $drive and hit RETURN" + fi + read ret + + rootdev=/dev/fd$drive + v1=-1 +else + rootdir=$base + v1='-t 1' +fi + +umount $rootdev 2>/dev/null +if [ "$std" ] +then + umount ${rootdev}p1 2>/dev/null + umount ${rootdev}p2 2>/dev/null +else + umount $rootdir/minix 2>/dev/null + umount $rootdir/etc 2>/dev/null +fi +mkfs $v1 -i 272 $rootdev 480 || exit +mount $rootdev /mnt || exit +if [ "$std" ] +then + partition -mf $rootdev 0 81:960 81:240 81:240 >/dev/null || exit + repartition $rootdev >/dev/null || exit + mkfs $v1 ${rootdev}p1 || exit + mkfs $v1 ${rootdev}p2 || exit + mount ${rootdev}p1 $rootdir/minix || exit # Hide /minix and /etc + mount ${rootdev}p2 $rootdir/etc 2>/dev/null # (complains about /etc/mtab) +else + install -d /tmp/.minix || exit + install -d /tmp/.etc || exit # Hide /minix and /etc + mount -t lo /tmp/.minix $rootdir/minix || exit + mount -t lo /tmp/.etc $rootdir/etc || exit +fi +cpdir -vx $rootdir /mnt || exit +install -d -o 0 -g 0 -m 755 /mnt || exit +install -d -o 0 -g 0 -m 555 /mnt/root || exit +install -d -o 0 -g 0 -m 555 /mnt/mnt || exit +install -d -o 0 -g 0 -m 555 /mnt/usr || exit +if [ "$std" ] +then + umount ${rootdev}p2 2>/dev/null # Unhide /etc + umount ${rootdev}p1 || exit # Unhide /minix +else + umount $rootdir/etc || exit # Unhide /etc + umount $rootdir/minix || exit # Unhide /minix +fi +install -d -o 2 -g 0 -m 755 /mnt/minix || exit +install -d -o 2 -g 0 -m 755 /mnt/etc || exit +set `ls -t $rootdir/minix` # Install the latest kernel +install -c $rootdir/minix/$1 /mnt/minix/`echo $1 | sed 's/r[0-9]*$//` || exit +cpdir -v $base/usr/src/etc /mnt/etc || exit # Install a fresh /etc +chown -R 0:0 /mnt/etc # Patch up owner and mode +chmod 600 /mnt/etc/shadow + +# Change /etc/fstab. +echo >/mnt/etc/fstab "\ +# Poor man's File System Table. + +root=unknown +usr=unknown" + +# How to install? +echo >/mnt/etc/issue "\ + +Login as root and run 'setup' to install Minix." + +umount $rootdev || exit +test "$std" && umount $root 2>/dev/null +installboot -d $rootdev $base/usr/mdec/bootblock boot >/dev/null + +# Partition the root floppy whether necessary or not. (Two images can be +# concatenated, or a combined image can be split later.) +partition -mf $rootdev 0 81:960 0:0 81:1440 81:480 >/dev/null || exit + +if [ "$std" ] +then + if [ "$single" ] + then + repartition $rootdev >/dev/null + usrdev=${rootdev}p2 + else + echo -n "Insert the usr diskette in drive $drive and hit RETURN" + read ret + usrdev=$rootdev + fi +fi + +mkfs $v1 -i 96 $usrdev 720 || exit +mount $usrdev /mnt || exit +install -d -o 0 -g 0 -m 755 /mnt || exit +(cd $base/usr && exec tar cfD - $usrlist) | (cd /mnt && exec tar xvfp -) || exit +umount $usrdev || exit + +# Put a "boot the other drive" bootblock on the /usr floppy. +installboot -m $usrdev /usr/mdec/masterboot >/dev/null + +# We're done for Minix-vmd here, it has its own ideas on how to package /usr. +test "$std" || exit 0 + +# Guess the size of /usr in compressed form. Assume compression down to 60% +# of the original size. Use "disk megabytes" of 1000*1024 for a safe guess. +set -$- `df | grep "^$usr"` +size=`expr \\( $4 \\* 6 / 10 + 999 \\) / 1000` + +echo -n " +You now need enough diskettes to hold /usr in compressed form, close to +$size Mb total. " + +size= +while [ -z "$size" ] +do + if [ "$single" ]; then defsize=1440; else defsize=720; fi + + echo -n "What is the size of the diskettes? [$defsize] "; read size + + case $size in + '') size=$defsize + ;; + 360|720|1200|1440) + ;; + *) echo "Sorry, I don't believe \"$size\", try again." >&2 + size= + esac +done + +drive= +while [ -z "$drive" ] +do + echo -n "What floppy drive to use? [0] "; read drive + + case $drive in + '') drive=0 + ;; + [01]) + ;; + *) echo "It must be 0 or 1, not \"$drive\"." + drive= + esac +done + +echo " +Enter the floppies in drive $drive when asked to. Mark them with the volume +numbers! +" +sleep 2 + +if [ `arch` = i86 ]; then bits=13; else bits=16; fi + +>/tmp/DONE +cd /usr && tar cvf - . /tmp/DONE \ + | compress -b$bits | vol -w $size /dev/fd$drive && +echo Done. diff --git a/commands/scripts/setup.sh b/commands/scripts/setup.sh new file mode 100755 index 000000000..0ce5b6647 --- /dev/null +++ b/commands/scripts/setup.sh @@ -0,0 +1,368 @@ +#!/bin/sh +# +# setup 4.1 - install a Minix distribution Author: Kees J. Bot +# 20 Dec 1994 + +PATH=/bin:/usr/bin +export PATH + +usage() +{ + cat >&2 <<'EOF' +Usage: setup # Install a skeleton system on the hard disk. + setup /usr # Install the rest of the system (binaries or sources). + + # To install from other things then floppies: + + urlget http://... | setup /usr # Read from a web site. + urlget ftp://... | setup /usr # Read from an FTP site. + mtools copy c0d0p0:... - | setup /usr # Read from the C: drive. + dosread c0d0p0 ... | setup /usr # Likewise if no mtools. +EOF + exit 1 +} + +# No options. +while getopts '' opt; do usage; done +shift `expr $OPTIND - 1` + +# Installing a floppy set? +case $# in +0) # No, we're installing a skeleton system on the hard disk. + ;; +1) + cd "$1" || exit + + # Annoying message still there? + grep "'setup /usr'" /etc/issue >/dev/null 2>&1 && rm -f /etc/issue + + if [ -t 0 ] + then + size=bad + while [ "$size" = bad ] + do + echo -n "\ +What is the size of the images on the diskettes? [all] "; read size + + case $size in + ''|360|720|1200|1440) + ;; + *) echo "Sorry, I don't believe \"$size\", try again." >&2 + size=bad + esac + done + + drive= + while [ -z "$drive" ] + do + echo -n "What floppy drive to use? [0] "; read drive + + case $drive in + '') drive=0 + ;; + [01]) + ;; + *) echo "It must be 0 or 1, not \"$drive\"." + drive= + esac + done + + vol -r $size /dev/fd$drive | uncompress | tar xvfp - + else + # Standard input is where we can get our files from. + uncompress | tar xvfp - + fi + + echo Done. + exit + ;; +*) + usage +esac + +# Installing Minix on the hard disk. +# Must be in / or we can't mount or umount. +case "`pwd`" in +/?*) + echo "Please type 'cd /' first, you are locking up `pwd`" >&2 + exit 1 +esac +case "$0" in +/tmp/*) + rm -f "$0" + ;; +*) cp -p "$0" /tmp/setup + exec /tmp/setup +esac + +# Find out what we are running from. +exec 9<&0 </etc/mtab # Mounted file table. +read thisroot rest # Current root (/dev/ram or /dev/fd?) +read fdusr rest # USR (/dev/fd? or /dev/fd?p2) +exec 0<&9 9<&- + +# What do we know about ROOT? +case $thisroot:$fdusr in +/dev/ram:/dev/fd0p2) fdroot=/dev/fd0 # Combined ROOT+USR in drive 0 + ;; +/dev/ram:/dev/fd1p2) fdroot=/dev/fd1 # Combined ROOT+USR in drive 1 + ;; +/dev/ram:/dev/fd*) fdroot=unknown # ROOT is some other floppy + ;; +/dev/fd*:/dev/fd*) fdroot=$thisroot # ROOT is mounted directly + ;; +*) fdroot=$thisroot # ? + echo -n "\ +It looks like Minix has been installed on disk already. Are you sure you +know what you are doing? [n] " + read yn + case "$yn" in + [yY]*|sure) ;; + *) exit + esac +esac + +echo -n "\ +This is the Minix installation script. + +Note 1: If the screen blanks suddenly then hit F3 to select \"software + scrolling\". + +Note 2: If things go wrong then hit DEL and start over. + +Note 3: The installation procedure is described in the manual page + usage(8). It will be hard without it. + +Note 4: Some questions have default answers, like this: [y] + Simply hit RETURN (or ENTER) if you want to choose that answer. + +Note 5: If you see a colon (:) then you should hit RETURN to continue. +:" +read ret + +echo " +What type of keyboard do you have? You can choose one of: +" +ls -C /usr/lib/keymaps | sed -e 's/\.map//g' -e 's/^/ /' +echo -n " +Keyboard type? [us-std] "; read keymap +test -n "$keymap" && loadkeys "/usr/lib/keymaps/$keymap.map" + +echo -n " +Minix needs one primary partition of at least 35 Mb. (It fits in 25 Mb, but +it needs 35 Mb if fully recompiled. Add more space to taste, but don't +overdo it, there are limits to the size of a file system.) + +If there is no free space on your disk then you have to back up one of the +other partitions, shrink, and reinstall. See the appropriate manuals of the +the operating systems currently installed. Restart your Minix installation +after you have made space. + +To make this partition you will be put in the editor \"part\". Follow the +advice under the '!' key to make a new partition of type MINIX. Do not +touch an existing partition unless you know precisely what you are doing! +Please note the name of the partition (e.g. c0d0p1, c0d1p3, c1d1p0) you +make. (See the devices section in usage(8) on Minix device names.) +:" +read ret + +primary= +while [ -z "$primary" ] +do + part || exit + + echo -n " +Please finish the name of the primary partition you have created: +(Just type RETURN if you want to rerun \"part\") /dev/" + read primary +done + +root=${primary}s0 +swap=${primary}s1 +usr=${primary}s2 + +hex2int() +{ + # Translate hexadecimal to integer. + local h d i + + h=$1 + i=0 + while [ -n "$h" ] + do + d=$(expr $h : '\(.\)') + h=$(expr $h : '.\(.*\)') + d=$(expr \( 0123456789ABCDEF : ".*$d" \) - 1) + i=$(expr $i \* 16 + $d) + done + echo $i +} + +# Compute the amount of memory available to Minix. +memsize=0 +ifs="$IFS" +IFS=',' +set -- $(sysenv memory) +IFS="$ifs" + +for mem +do + mem=$(expr $mem : '.*:\(.*\)') + memsize=$(expr $memsize + $(hex2int $mem) / 1024) +done + +# Compute an advised swap size. +swapadv=0 +case `arch` in +i86) + test $memsize -lt 4096 && swapadv=$(expr 4096 - $memsize) + ;; +*) test $memsize -lt 6144 && swapadv=$(expr 6144 - $memsize) +esac + +echo -n " +How much swap space would you like? Swapspace is only needed if this +system is memory starved, like a 16-bit system with less then 2M, or a +32-bit system with less then 4M. Minix swapping isn't very good yet, so +there is no need for it otherwise. + Size in kilobytes? [$swapadv] " +swapsize= +read swapsize +test -z "$swapsize" && swapsize=$swapadv + +echo -n " +You have created a partition named: /dev/$primary +The following subpartitions are about to be created on /dev/$primary: + + Root subpartition: /dev/$root 1440 kb + Swap subpartition: /dev/$swap $swapsize kb + /usr subpartition: /dev/$usr rest of $primary + +Hit return if everything looks fine, or hit DEL to bail out if you want to +think it over. The next step will destroy /dev/$primary. +:" +read ret + # Secondary master bootstrap. +installboot -m /dev/$primary /usr/mdec/masterboot >/dev/null || exit + + # Partition the primary. +p3=0:0 +test "$swapsize" -gt 0 && p3=81:`expr $swapsize \* 2` +partition /dev/$primary 1 81:2880* $p3 81:0+ >/dev/null || exit + +if [ "$swapsize" -gt 0 ] +then + # We must have that swap, now! + mkswap -f /dev/$swap || exit + mount -s /dev/$swap || exit +else + # Forget about swap. + swap= +fi + +echo " +Migrating from floppy to disk... +" + +mkfs /dev/$usr +echo "\ +Scanning /dev/$usr for bad blocks. (Hit DEL to stop the scan if are +absolutely sure that there can not be any bad blocks. Otherwise just wait.)" +trap ': nothing' 2 +readall -b /dev/$usr | sh +echo "Scan done" +sleep 2 +trap 2 + +mount /dev/$usr /mnt || exit # Mount the intended /usr. + +cpdir -v /usr /mnt || exit # Copy the usr floppy. + +umount /dev/$usr || exit # Unmount the intended /usr. + +umount $fdusr # Unmount the /usr floppy. + +mount /dev/$usr /usr || exit # A new /usr + +if [ $fdroot = unknown ] +then + echo " +By now the floppy USR has been copied to /dev/$usr, and it is now in use as +/usr. Please insert the installation ROOT floppy in a floppy drive." + + drive= + while [ -z "$drive" ] + do + echo -n "What floppy drive is it in? [0] "; read drive + + case $drive in + '') drive=0 + ;; + [01]) + ;; + *) echo "It must be 0 or 1, not \"$drive\"." + drive= + esac + done + fdroot=/dev/fd$drive +fi + +echo " +Copying $fdroot to /dev/$root +" + +mkfs /dev/$root || exit +mount /dev/$root /mnt || exit +if [ $thisroot = /dev/ram ] +then + # Running from the RAM disk, root image is on a floppy. + mount $fdroot /root || exit + cpdir -v /root /mnt || exit + umount $fdroot || exit + cpdir -f /dev /mnt/dev # Copy any extra MAKEDEV'd devices +else + # Running from the floppy itself. + cpdir -vx / /mnt || exit + chmod 555 /mnt/usr +fi + + # Change /etc/fstab. +echo >/mnt/etc/fstab "\ +# Poor man's File System Table. + +root=/dev/$root +${swap:+swap=/dev/$swap} +usr=/dev/$usr" + + # How to install further? +echo >/mnt/etc/issue "\ +Login as root and run 'setup /usr' to install floppy sets." + + # National keyboard map. +test -n "$keymap" && cp -p "/usr/lib/keymaps/$keymap.map" /mnt/etc/keymap + +umount /dev/$root || exit # Unmount the new root. + +# Compute size of the second level file block cache. +case `arch` in +i86) + cache=`expr "0$memsize" - 1024` + test $cache -lt 32 && cache=0 + test $cache -gt 512 && cache=512 + ;; +*) + cache=`expr "0$memsize" - 2560` + test $cache -lt 64 && cache=0 + test $cache -gt 1024 && cache=1024 +esac +echo "Second level file system block cache set to $cache kb." +if [ $cache -eq 0 ]; then cache=; else cache="ramsize=$cache"; fi + + # Make bootable. +installboot -d /dev/$root /usr/mdec/bootblock /boot >/dev/null || exit +edparams /dev/$root "rootdev=$root; ramimagedev=$root; $cache; save" || exit + +echo " +Please insert the installation ROOT floppy and type 'halt' to exit Minix. +You can type 'boot $primary' to try the newly installed Minix system. See +\"TESTING\" in the usage manual." diff --git a/commands/scripts/spell.sh b/commands/scripts/spell.sh new file mode 100755 index 000000000..7932b7a63 --- /dev/null +++ b/commands/scripts/spell.sh @@ -0,0 +1,35 @@ +#!/bin/sh +# +# spell 1.1 - show unknown words Author: Kees J. Bot +# 28 Apr 1995 + +dict=words + +while getopts 'd:' opt +do + case $opt in + d) dict="$OPTARG" + ;; + ?) echo "Usage: spell [-d dict] [file ...]" >&2; exit 1 + esac +done +shift `expr $OPTIND - 1` + +case "$dict" in +*/*) ;; +*) dict="/usr/lib/dict/$dict" +esac + +{ + if [ $# = 0 ] + then + prep + else + for file + do + prep "$file" + done + fi +} | { + sort -u | comm -23 - "$dict" +} diff --git a/commands/scripts/srccrc.sh b/commands/scripts/srccrc.sh new file mode 100755 index 000000000..c2a7ce702 --- /dev/null +++ b/commands/scripts/srccrc.sh @@ -0,0 +1,24 @@ +#!/bin/sh +# +# srccrc 1.0 - compute CRC checksums of the entire source tree +# Author: Kees J. Bot +cd /usr || exit + +{ + # List the file names of all files in /usr/include and /usr/src. + find include src/* -type f +} | { + # Sort the list to make them comparable. + sort +} | { + # Remove files like *.o, *.bak, etc. + sed -e '/\.o$/d + /\.a$/d + /\.bak$/d + /\/a\.out$/d + /\/core$/d + /\/bin\/[^/]*$/d' +} | { + # Compute checksums. + xargs crc +} diff --git a/commands/scripts/svclog.sh b/commands/scripts/svclog.sh new file mode 100755 index 000000000..f56cc3a6a --- /dev/null +++ b/commands/scripts/svclog.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# +svc=`basename $1 ,S`,S +if test \( ! -r $svc \) -a -d "SVC" ; then svc=SVC/$svc ; fi +grep '^#\*\*\*SVC' $svc + diff --git a/commands/scripts/whatis.sh b/commands/scripts/whatis.sh new file mode 100755 index 000000000..c43216372 --- /dev/null +++ b/commands/scripts/whatis.sh @@ -0,0 +1,53 @@ +#!/bin/sh +# +# whatis/apropos 1.3 - search whatis(5) database for commands +# Author: Kees J. Bot +# BUGS +# whatis file must be as if created by makewhatis(8). +# +# This version includes a fix by Michael Haardt originally posted to +# comp.os.minix in July 1999. Fixes for grep provided by Michael in May +# 1999 caused whatis to break, this is now fixed. (ASW 2004-12-12) + +all='exit 0' + +case "$1" in +-a) all="found='exit 0'" + shift +esac + +case $#:$0 in +1:*whatis) + ;; +1:*apropos) + all="found='exit 0'" + ;; +*) echo "Usage: `basename $0` [-a] <keyword>" >&2 + exit 1 +esac + +IFS=":$IFS" +MANPATH="${MANPATH-/usr/local/man:/usr/man}" + +found= + +for m in $MANPATH +do + for w in $m/whatis + do + test -f $w || continue + + case $0 in + *whatis) + grep '^\('$1'\|[^(]* '$1'\)[ ,][^(]*(' $w && eval $all + ;; + *apropos) + grep -i "$1" $w && eval $all + esac + done +done + +$found + +echo "`basename $0`: $1: not found" >&2 +exit 1 diff --git a/commands/scripts/whereis.sh b/commands/scripts/whereis.sh new file mode 100755 index 000000000..2dfe0c52e --- /dev/null +++ b/commands/scripts/whereis.sh @@ -0,0 +1,23 @@ +#!/bin/sh +: List all system directories containing the argument +: Author: Terrence W. Holm +if test $# -ne 1; then + echo "Usage: whereis name" + exit 1 +fi + +path="/bin /lib /etc\ + /usr/bin /usr/lib\ + /usr/include /usr/include/sys" + +for dir in $path; do + for file in $dir/$1 $dir/$1.*; do + if test -f $file; then + echo $file + elif test -d $file; then + echo $file/ + fi + done + done + +exit 0 diff --git a/commands/sh/Makefile b/commands/sh/Makefile new file mode 100755 index 000000000..d9527e29d --- /dev/null +++ b/commands/sh/Makefile @@ -0,0 +1,28 @@ +# Makefile for sh + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -wa +LDFLAGS = -i + +OBJ = sh1.o sh2.o sh3.o sh4.o sh5.o sh6.o + +all: sh + +sh: $(OBJ) + cc $(LDFLAGS) -o $@ $(OBJ) + install -S 11kw sh + +install: /usr/bin/msh + +/usr/bin/msh: sh + install -cs -o bin $? $@ + +#/usr/bin/sh: /usr/bin/msh +# install -l $? $@ +# +#/bin/sh: /usr/bin/msh +# install -lcs $? $@ + +$(OBJ): sh.h + +clean: + rm -f sh *.o *.bak core diff --git a/commands/sh/sh.h b/commands/sh/sh.h new file mode 100755 index 000000000..8f096bf2e --- /dev/null +++ b/commands/sh/sh.h @@ -0,0 +1,383 @@ +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> + +/* Need a way to have void used for ANSI, nothing for K&R. */ +#ifndef _ANSI +#undef _VOID +#define _VOID +#endif + +/* -------- sh.h -------- */ +/* + * shell + */ + +#define LINELIM 4096 +#define NPUSH 8 /* limit to input nesting */ + +#define NOFILE 20 /* Number of open files */ +#define NUFILE 10 /* Number of user-accessible files */ +#define FDBASE 10 /* First file usable by Shell */ + +/* + * values returned by wait + */ +#define WAITSIG(s) ((s)&0177) +#define WAITVAL(s) (((s)>>8)&0377) +#define WAITCORE(s) (((s)&0200)!=0) + +/* + * library and system defintions + */ +#ifdef __STDC__ +typedef void xint; /* base type of jmp_buf, for not broken compilers */ +#else +typedef char * xint; /* base type of jmp_buf, for broken compilers */ +#endif + +/* + * shell components + */ +/* #include "area.h" */ +/* #include "word.h" */ +/* #include "io.h" */ +/* #include "var.h" */ + +#define QUOTE 0200 + +#define NOBLOCK ((struct op *)NULL) +#define NOWORD ((char *)NULL) +#define NOWORDS ((char **)NULL) +#define NOPIPE ((int *)NULL) + +/* + * Description of a command or an operation on commands. + * Might eventually use a union. + */ +struct op { + int type; /* operation type, see below */ + char **words; /* arguments to a command */ + struct ioword **ioact; /* IO actions (eg, < > >>) */ + struct op *left; + struct op *right; + char *str; /* identifier for case and for */ +}; + +#define TCOM 1 /* command */ +#define TPAREN 2 /* (c-list) */ +#define TPIPE 3 /* a | b */ +#define TLIST 4 /* a [&;] b */ +#define TOR 5 /* || */ +#define TAND 6 /* && */ +#define TFOR 7 +#define TDO 8 +#define TCASE 9 +#define TIF 10 +#define TWHILE 11 +#define TUNTIL 12 +#define TELIF 13 +#define TPAT 14 /* pattern in case */ +#define TBRACE 15 /* {c-list} */ +#define TASYNC 16 /* c & */ + +/* + * actions determining the environment of a process + */ +#define BIT(i) (1<<(i)) +#define FEXEC BIT(0) /* execute without forking */ + +/* + * flags to control evaluation of words + */ +#define DOSUB 1 /* interpret $, `, and quotes */ +#define DOBLANK 2 /* perform blank interpretation */ +#define DOGLOB 4 /* interpret [?* */ +#define DOKEY 8 /* move words with `=' to 2nd arg. list */ +#define DOTRIM 16 /* trim resulting string */ + +#define DOALL (DOSUB|DOBLANK|DOGLOB|DOKEY|DOTRIM) + +Extern char **dolv; +Extern int dolc; +Extern int exstat; +Extern char gflg; +Extern int talking; /* interactive (talking-type wireless) */ +Extern int execflg; +Extern int multiline; /* \n changed to ; */ +Extern struct op *outtree; /* result from parser */ + +Extern xint *failpt; +Extern xint *errpt; + +struct brkcon { + jmp_buf brkpt; + struct brkcon *nextlev; +} ; +Extern struct brkcon *brklist; +Extern int isbreak; + +/* + * redirection + */ +struct ioword { + short io_unit; /* unit affected */ + short io_flag; /* action (below) */ + char *io_name; /* file name */ +}; +#define IOREAD 1 /* < */ +#define IOHERE 2 /* << (here file) */ +#define IOWRITE 4 /* > */ +#define IOCAT 8 /* >> */ +#define IOXHERE 16 /* ${}, ` in << */ +#define IODUP 32 /* >&digit */ +#define IOCLOSE 64 /* >&- */ + +#define IODEFAULT (-1) /* token for default IO unit */ + +Extern struct wdblock *wdlist; +Extern struct wdblock *iolist; + +/* + * parsing & execution environment + */ +extern struct env { + char *linep; + struct io *iobase; + struct io *iop; + xint *errpt; + int iofd; + struct env *oenv; +} e; + +/* + * flags: + * -e: quit on error + * -k: look for name=value everywhere on command line + * -n: no execution + * -t: exit after reading and executing one command + * -v: echo as read + * -x: trace + * -u: unset variables net diagnostic + */ +extern char *flag; + +extern char *null; /* null value for variable */ +extern int intr; /* interrupt pending */ + +Extern char *trap[_NSIG+1]; +Extern char ourtrap[_NSIG+1]; +Extern int trapset; /* trap pending */ + +extern int heedint; /* heed interrupt signals */ + +Extern int yynerrs; /* yacc */ + +Extern char line[LINELIM]; +extern char *elinep; + +/* + * other functions + */ +#ifdef __STDC__ +int (*inbuilt(char *s ))(void); +#else +int (*inbuilt())(); +#endif +_PROTOTYPE(char *rexecve , (char *c , char **v , char **envp )); +_PROTOTYPE(char *space , (int n )); +_PROTOTYPE(char *strsave , (char *s , int a )); +_PROTOTYPE(char *evalstr , (char *cp , int f )); +_PROTOTYPE(char *putn , (int n )); +_PROTOTYPE(char *itoa , (unsigned u , int n )); +_PROTOTYPE(char *unquote , (char *as )); +_PROTOTYPE(struct var *lookup , (char *n )); +_PROTOTYPE(int rlookup , (char *n )); +_PROTOTYPE(struct wdblock *glob , (char *cp , struct wdblock *wb )); +_PROTOTYPE(int subgetc , (int ec , int quoted )); +_PROTOTYPE(char **makenv , (void)); +_PROTOTYPE(char **eval , (char **ap , int f )); +_PROTOTYPE(int setstatus , (int s )); +_PROTOTYPE(int waitfor , (int lastpid , int canintr )); + +_PROTOTYPE(void onintr , (int s )); /* SIGINT handler */ + +_PROTOTYPE(int newenv , (int f )); +_PROTOTYPE(void quitenv , (void)); +_PROTOTYPE(void err , (char *s )); +_PROTOTYPE(int anys , (char *s1 , char *s2 )); +_PROTOTYPE(int any , (int c , char *s )); +_PROTOTYPE(void next , (int f )); +_PROTOTYPE(void setdash , (void)); +_PROTOTYPE(void onecommand , (void)); +_PROTOTYPE(void runtrap , (int i )); +_PROTOTYPE(void xfree , (char *s )); +_PROTOTYPE(int letter , (int c )); +_PROTOTYPE(int digit , (int c )); +_PROTOTYPE(int letnum , (int c )); +_PROTOTYPE(int gmatch , (char *s , char *p )); + +/* + * error handling + */ +_PROTOTYPE(void leave , (void)); /* abort shell (or fail in subshell) */ +_PROTOTYPE(void fail , (void)); /* fail but return to process next command */ +_PROTOTYPE(void warn , (char *s )); +_PROTOTYPE(void sig , (int i )); /* default signal handler */ + +/* -------- var.h -------- */ + +struct var { + char *value; + char *name; + struct var *next; + char status; +}; +#define COPYV 1 /* flag to setval, suggesting copy */ +#define RONLY 01 /* variable is read-only */ +#define EXPORT 02 /* variable is to be exported */ +#define GETCELL 04 /* name & value space was got with getcell */ + +Extern struct var *vlist; /* dictionary */ + +Extern struct var *homedir; /* home directory */ +Extern struct var *prompt; /* main prompt */ +Extern struct var *cprompt; /* continuation prompt */ +Extern struct var *path; /* search path for commands */ +Extern struct var *shell; /* shell to interpret command files */ +Extern struct var *ifs; /* field separators */ + +_PROTOTYPE(int yyparse , (void)); +_PROTOTYPE(struct var *lookup , (char *n )); +_PROTOTYPE(void setval , (struct var *vp , char *val )); +_PROTOTYPE(void nameval , (struct var *vp , char *val , char *name )); +_PROTOTYPE(void export , (struct var *vp )); +_PROTOTYPE(void ronly , (struct var *vp )); +_PROTOTYPE(int isassign , (char *s )); +_PROTOTYPE(int checkname , (char *cp )); +_PROTOTYPE(int assign , (char *s , int cf )); +_PROTOTYPE(void putvlist , (int f , int out )); +_PROTOTYPE(int eqname , (char *n1 , char *n2 )); + +_PROTOTYPE(int execute , (struct op *t , int *pin , int *pout , int act )); + +/* -------- io.h -------- */ +/* io buffer */ +struct iobuf { + unsigned id; /* buffer id */ + char buf[512]; /* buffer */ + char *bufp; /* pointer into buffer */ + char *ebufp; /* pointer to end of buffer */ +}; + +/* possible arguments to an IO function */ +struct ioarg { + char *aword; + char **awordlist; + int afile; /* file descriptor */ + unsigned afid; /* buffer id */ + long afpos; /* file position */ + struct iobuf *afbuf; /* buffer for this file */ +}; +Extern struct ioarg ioargstack[NPUSH]; +#define AFID_NOBUF (~0) +#define AFID_ID 0 + +/* an input generator's state */ +struct io { + int (*iofn)(_VOID); + struct ioarg *argp; + int peekc; + char prev; /* previous character read by readc() */ + char nlcount; /* for `'s */ + char xchar; /* for `'s */ + char task; /* reason for pushed IO */ +}; +Extern struct io iostack[NPUSH]; +#define XOTHER 0 /* none of the below */ +#define XDOLL 1 /* expanding ${} */ +#define XGRAVE 2 /* expanding `'s */ +#define XIO 3 /* file IO */ + +/* in substitution */ +#define INSUB() (e.iop->task == XGRAVE || e.iop->task == XDOLL) + +/* + * input generators for IO structure + */ +_PROTOTYPE(int nlchar , (struct ioarg *ap )); +_PROTOTYPE(int strchar , (struct ioarg *ap )); +_PROTOTYPE(int qstrchar , (struct ioarg *ap )); +_PROTOTYPE(int filechar , (struct ioarg *ap )); +_PROTOTYPE(int herechar , (struct ioarg *ap )); +_PROTOTYPE(int linechar , (struct ioarg *ap )); +_PROTOTYPE(int gravechar , (struct ioarg *ap , struct io *iop )); +_PROTOTYPE(int qgravechar , (struct ioarg *ap , struct io *iop )); +_PROTOTYPE(int dolchar , (struct ioarg *ap )); +_PROTOTYPE(int wdchar , (struct ioarg *ap )); +_PROTOTYPE(void scraphere , (void)); +_PROTOTYPE(void freehere , (int area )); +_PROTOTYPE(void gethere , (void)); +_PROTOTYPE(void markhere , (char *s , struct ioword *iop )); +_PROTOTYPE(int herein , (char *hname , int xdoll )); +_PROTOTYPE(int run , (struct ioarg *argp , int (*f)(_VOID))); + +/* + * IO functions + */ +_PROTOTYPE(int eofc , (void)); +_PROTOTYPE(int getc , (int ec )); +_PROTOTYPE(int readc , (void)); +_PROTOTYPE(void unget , (int c )); +_PROTOTYPE(void ioecho , (int c )); +_PROTOTYPE(void prs , (char *s )); +_PROTOTYPE(void putc , (int c )); +_PROTOTYPE(void prn , (unsigned u )); +_PROTOTYPE(void closef , (int i )); +_PROTOTYPE(void closeall , (void)); + +/* + * IO control + */ +_PROTOTYPE(void pushio , (struct ioarg *argp , int (*fn)(_VOID))); +_PROTOTYPE(int remap , (int fd )); +_PROTOTYPE(int openpipe , (int *pv )); +_PROTOTYPE(void closepipe , (int *pv )); +_PROTOTYPE(struct io *setbase , (struct io *ip )); + +extern struct ioarg temparg; /* temporary for PUSHIO */ +#define PUSHIO(what,arg,gen) ((temparg.what = (arg)),pushio(&temparg,(gen))) +#define RUN(what,arg,gen) ((temparg.what = (arg)), run(&temparg,(gen))) + +/* -------- word.h -------- */ +#ifndef WORD_H +#define WORD_H 1 +struct wdblock { + short w_bsize; + short w_nword; + /* bounds are arbitrary */ + char *w_words[1]; +}; + +_PROTOTYPE(struct wdblock *addword , (char *wd , struct wdblock *wb )); +_PROTOTYPE(struct wdblock *newword , (int nw )); +_PROTOTYPE(char **getwords , (struct wdblock *wb )); +#endif + +/* -------- area.h -------- */ + +/* + * storage allocation + */ +_PROTOTYPE(char *getcell , (unsigned nbytes )); +_PROTOTYPE(void garbage , (void)); +_PROTOTYPE(void setarea , (char *cp , int a )); +_PROTOTYPE(int getarea , (char *cp )); +_PROTOTYPE(void freearea , (int a )); +_PROTOTYPE(void freecell , (char *cp )); + +Extern int areanum; /* current allocation area */ + +#define NEW(type) (type *)getcell(sizeof(type)) +#define DELETE(obj) freecell((char *)obj) diff --git a/commands/sh/sh1.c b/commands/sh/sh1.c new file mode 100755 index 000000000..cf45644c2 --- /dev/null +++ b/commands/sh/sh1.c @@ -0,0 +1,952 @@ +#define Extern extern +#include <sys/types.h> +#include <signal.h> +#include <errno.h> +#include <setjmp.h> +#include "sh.h" +/* -------- sh.c -------- */ +/* + * shell + */ + +/* #include "sh.h" */ + +int intr; +int inparse; +char flags['z'-'a'+1]; +char *flag = flags-'a'; +char *elinep = line+sizeof(line)-5; +char *null = ""; +int heedint =1; +struct env e ={line, iostack, iostack-1, + (xint *)NULL, FDBASE, (struct env *)NULL}; + +extern char **environ; /* environment pointer */ + +/* + * default shell, search rules + */ +char shellname[] = "/bin/sh"; +char search[] = ":/bin:/usr/bin"; + +_PROTOTYPE(void (*qflag), (int)) = SIG_IGN; + +_PROTOTYPE(int main, (int argc, char **argv )); +_PROTOTYPE(int newfile, (char *s )); +_PROTOTYPE(static char *findeq, (char *cp )); +_PROTOTYPE(static char *cclass, (char *p, int sub )); +_PROTOTYPE(void initarea, (void)); + +int main(argc, argv) +int argc; +register char **argv; +{ + register int f; + register char *s; + int cflag; + char *name, **ap; + int (*iof)(); + + initarea(); + if ((ap = environ) != NULL) { + while (*ap) + assign(*ap++, !COPYV); + for (ap = environ; *ap;) + export(lookup(*ap++)); + } + closeall(); + areanum = 1; + + shell = lookup("SHELL"); + if (shell->value == null) + setval(shell, shellname); + export(shell); + + homedir = lookup("HOME"); + if (homedir->value == null) + setval(homedir, "/"); + export(homedir); + + setval(lookup("$"), itoa(getpid(), 5)); + + path = lookup("PATH"); + if (path->value == null) + setval(path, search); + export(path); + + ifs = lookup("IFS"); + if (ifs->value == null) + setval(ifs, " \t\n"); + + prompt = lookup("PS1"); + if (prompt->value == null) +#ifndef UNIXSHELL + setval(prompt, "$ "); +#else + setval(prompt, "% "); +#endif + + if (geteuid() == 0) { + setval(prompt, "# "); + prompt->status &= ~EXPORT; + } + cprompt = lookup("PS2"); + if (cprompt->value == null) + setval(cprompt, "> "); + + iof = filechar; + cflag = 0; + name = *argv++; + if (--argc >= 1) { + if(argv[0][0] == '-' && argv[0][1] != '\0') { + for (s = argv[0]+1; *s; s++) + switch (*s) { + case 'c': + prompt->status &= ~EXPORT; + cprompt->status &= ~EXPORT; + setval(prompt, ""); + setval(cprompt, ""); + cflag = 1; + if (--argc > 0) + PUSHIO(aword, *++argv, iof = nlchar); + break; + + case 'q': + qflag = SIG_DFL; + break; + + case 's': + /* standard input */ + break; + + case 't': + prompt->status &= ~EXPORT; + setval(prompt, ""); + iof = linechar; + break; + + case 'i': + talking++; + default: + if (*s>='a' && *s<='z') + flag[*s]++; + } + } else { + argv--; + argc++; + } + if (iof == filechar && --argc > 0) { + setval(prompt, ""); + setval(cprompt, ""); + prompt->status &= ~EXPORT; + cprompt->status &= ~EXPORT; + if (newfile(name = *++argv)) + exit(1); + } + } + setdash(); + if (e.iop < iostack) { + PUSHIO(afile, 0, iof); + if (isatty(0) && isatty(1) && !cflag) + talking++; + } + signal(SIGQUIT, qflag); + if (name && name[0] == '-') { + talking++; + if ((f = open(".profile", 0)) >= 0) + next(remap(f)); + if ((f = open("/etc/profile", 0)) >= 0) + next(remap(f)); + } + if (talking) + signal(SIGTERM, sig); + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + signal(SIGINT, onintr); + dolv = argv; + dolc = argc; + dolv[0] = name; + if (dolc > 1) + for (ap = ++argv; --argc > 0;) + if (assign(*ap = *argv++, !COPYV)) + dolc--; /* keyword */ + else + ap++; + setval(lookup("#"), putn((--dolc < 0) ? (dolc = 0) : dolc)); + + for (;;) { + if (talking && e.iop <= iostack) + prs(prompt->value); + onecommand(); + } +} + +void +setdash() +{ + register char *cp, c; + char m['z'-'a'+1]; + + cp = m; + for (c='a'; c<='z'; c++) + if (flag[c]) + *cp++ = c; + *cp = 0; + setval(lookup("-"), m); +} + +int +newfile(s) +register char *s; +{ + register f; + + if (strcmp(s, "-") != 0) { + f = open(s, 0); + if (f < 0) { + prs(s); + err(": cannot open"); + return(1); + } + } else + f = 0; + next(remap(f)); + return(0); +} + +void +onecommand() +{ + register i; + jmp_buf m1; + + while (e.oenv) + quitenv(); + areanum = 1; + freehere(areanum); + freearea(areanum); + garbage(); + wdlist = 0; + iolist = 0; + e.errpt = 0; + e.linep = line; + yynerrs = 0; + multiline = 0; + inparse = 1; + intr = 0; + execflg = 0; + setjmp(failpt = m1); /* Bruce Evans' fix */ + if (setjmp(failpt = m1) || yyparse() || intr) { + while (e.oenv) + quitenv(); + scraphere(); + if (!talking && intr) + leave(); + inparse = 0; + intr = 0; + return; + } + inparse = 0; + brklist = 0; + intr = 0; + execflg = 0; + if (!flag['n']) + execute(outtree, NOPIPE, NOPIPE, 0); + if (!talking && intr) { + execflg = 0; + leave(); + } + if ((i = trapset) != 0) { + trapset = 0; + runtrap(i); + } +} + +void +fail() +{ + longjmp(failpt, 1); + /* NOTREACHED */ +} + +void +leave() +{ + if (execflg) + fail(); + scraphere(); + freehere(1); + runtrap(0); + exit(exstat); + /* NOTREACHED */ +} + +void +warn(s) +register char *s; +{ + if(*s) { + prs(s); + exstat = -1; + } + prs("\n"); + if (flag['e']) + leave(); +} + +void +err(s) +char *s; +{ + warn(s); + if (flag['n']) + return; + if (!talking) + leave(); + if (e.errpt) + longjmp(e.errpt, 1); + closeall(); + e.iop = e.iobase = iostack; +} + +int +newenv(f) +int f; +{ + register struct env *ep; + + if (f) { + quitenv(); + return(1); + } + ep = (struct env *) space(sizeof(*ep)); + if (ep == NULL) { + while (e.oenv) + quitenv(); + fail(); + } + *ep = e; + e.oenv = ep; + e.errpt = errpt; + return(0); +} + +void +quitenv() +{ + register struct env *ep; + register fd; + + if ((ep = e.oenv) != NULL) { + fd = e.iofd; + e = *ep; + /* should close `'d files */ + DELETE(ep); + while (--fd >= e.iofd) + close(fd); + } +} + +/* + * Is any character from s1 in s2? + */ +int +anys(s1, s2) +register char *s1, *s2; +{ + while (*s1) + if (any(*s1++, s2)) + return(1); + return(0); +} + +/* + * Is character c in s? + */ +int +any(c, s) +register int c; +register char *s; +{ + while (*s) + if (*s++ == c) + return(1); + return(0); +} + +char * +putn(n) +register int n; +{ + return(itoa(n, -1)); +} + +char * +itoa(u, n) +register unsigned u; +int n; +{ + register char *cp; + static char s[20]; + int m; + + m = 0; + if (n < 0 && (int) u < 0) { + m++; + u = -u; + } + cp = s+sizeof(s); + *--cp = 0; + do { + *--cp = u%10 + '0'; + u /= 10; + } while (--n > 0 || u); + if (m) + *--cp = '-'; + return(cp); +} + +void +next(f) +int f; +{ + PUSHIO(afile, f, filechar); +} + +void +onintr(s) +int s; /* ANSI C requires a parameter */ +{ + signal(SIGINT, onintr); + intr = 1; + if (talking) { + if (inparse) { + prs("\n"); + fail(); + } + } + else if (heedint) { + execflg = 0; + leave(); + } +} + +int +letter(c) +register c; +{ + return((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_'); +} + +int +digit(c) +register c; +{ + return(c >= '0' && c <= '9'); +} + +int +letnum(c) +register c; +{ + return(letter(c) || digit(c)); +} + +char * +space(n) +int n; +{ + register char *cp; + + if ((cp = getcell(n)) == 0) + err("out of string space"); + return(cp); +} + +char * +strsave(s, a) +register char *s; +int a; +{ + register char *cp, *xp; + + if ((cp = space(strlen(s)+1)) != NULL) { + setarea((char *)cp, a); + for (xp = cp; (*xp++ = *s++) != '\0';) + ; + return(cp); + } + return(""); +} + +void +xfree(s) +register char *s; +{ + DELETE(s); +} + +/* + * trap handling + */ +void +sig(i) +register int i; +{ + trapset = i; + signal(i, sig); +} + +void runtrap(i) +int i; +{ + char *trapstr; + + if ((trapstr = trap[i]) == NULL) + return; + if (i == 0) + trap[i] = 0; + RUN(aword, trapstr, nlchar); +} + +/* -------- var.c -------- */ +/* #include "sh.h" */ + +/* + * Find the given name in the dictionary + * and return its value. If the name was + * not previously there, enter it now and + * return a null value. + */ +struct var * +lookup(n) +register char *n; +{ + register struct var *vp; + register char *cp; + register int c; + static struct var dummy; + + if (digit(*n)) { + dummy.name = n; + for (c = 0; digit(*n) && c < 1000; n++) + c = c*10 + *n-'0'; + dummy.status = RONLY; + dummy.value = c <= dolc? dolv[c]: null; + return(&dummy); + } + for (vp = vlist; vp; vp = vp->next) + if (eqname(vp->name, n)) + return(vp); + cp = findeq(n); + vp = (struct var *)space(sizeof(*vp)); + if (vp == 0 || (vp->name = space((int)(cp-n)+2)) == 0) { + dummy.name = dummy.value = ""; + return(&dummy); + } + for (cp = vp->name; (*cp = *n++) && *cp != '='; cp++) + ; + if (*cp == 0) + *cp = '='; + *++cp = 0; + setarea((char *)vp, 0); + setarea((char *)vp->name, 0); + vp->value = null; + vp->next = vlist; + vp->status = GETCELL; + vlist = vp; + return(vp); +} + +/* + * give variable at `vp' the value `val'. + */ +void +setval(vp, val) +struct var *vp; +char *val; +{ + nameval(vp, val, (char *)NULL); +} + +/* + * if name is not NULL, it must be + * a prefix of the space `val', + * and end with `='. + * this is all so that exporting + * values is reasonably painless. + */ +void +nameval(vp, val, name) +register struct var *vp; +char *val, *name; +{ + register char *cp, *xp; + char *nv; + int fl; + + if (vp->status & RONLY) { + for (xp = vp->name; *xp && *xp != '=';) + putc(*xp++); + err(" is read-only"); + return; + } + fl = 0; + if (name == NULL) { + xp = space(strlen(vp->name)+strlen(val)+2); + if (xp == 0) + return; + /* make string: name=value */ + setarea((char *)xp, 0); + name = xp; + for (cp = vp->name; (*xp = *cp++) && *xp!='='; xp++) + ; + if (*xp++ == 0) + xp[-1] = '='; + nv = xp; + for (cp = val; (*xp++ = *cp++) != '\0';) + ; + val = nv; + fl = GETCELL; + } + if (vp->status & GETCELL) + xfree(vp->name); /* form new string `name=value' */ + vp->name = name; + vp->value = val; + vp->status |= fl; +} + +void +export(vp) +struct var *vp; +{ + vp->status |= EXPORT; +} + +void +ronly(vp) +struct var *vp; +{ + if (letter(vp->name[0])) /* not an internal symbol ($# etc) */ + vp->status |= RONLY; +} + +int +isassign(s) +register char *s; +{ + if (!letter((int)*s)) + return(0); + for (; *s != '='; s++) + if (*s == 0 || !letnum(*s)) + return(0); + return(1); +} + +int +assign(s, cf) +register char *s; +int cf; +{ + register char *cp; + struct var *vp; + + if (!letter(*s)) + return(0); + for (cp = s; *cp != '='; cp++) + if (*cp == 0 || !letnum(*cp)) + return(0); + vp = lookup(s); + nameval(vp, ++cp, cf == COPYV? (char *)NULL: s); + if (cf != COPYV) + vp->status &= ~GETCELL; + return(1); +} + +int +checkname(cp) +register char *cp; +{ + if (!letter(*cp++)) + return(0); + while (*cp) + if (!letnum(*cp++)) + return(0); + return(1); +} + +void +putvlist(f, out) +register int f, out; +{ + register struct var *vp; + + for (vp = vlist; vp; vp = vp->next) + if (vp->status & f && letter(*vp->name)) { + if (vp->status & EXPORT) + write(out, "export ", 7); + if (vp->status & RONLY) + write(out, "readonly ", 9); + write(out, vp->name, (int)(findeq(vp->name) - vp->name)); + write(out, "\n", 1); + } +} + +int +eqname(n1, n2) +register char *n1, *n2; +{ + for (; *n1 != '=' && *n1 != 0; n1++) + if (*n2++ != *n1) + return(0); + return(*n2 == 0 || *n2 == '='); +} + +static char * +findeq(cp) +register char *cp; +{ + while (*cp != '\0' && *cp != '=') + cp++; + return(cp); +} + +/* -------- gmatch.c -------- */ +/* + * int gmatch(string, pattern) + * char *string, *pattern; + * + * Match a pattern as in sh(1). + */ + +#define CMASK 0377 +#define QUOTE 0200 +#define QMASK (CMASK&~QUOTE) +#define NOT '!' /* might use ^ */ + +int +gmatch(s, p) +register char *s, *p; +{ + register int sc, pc; + + if (s == NULL || p == NULL) + return(0); + while ((pc = *p++ & CMASK) != '\0') { + sc = *s++ & QMASK; + switch (pc) { + case '[': + if ((p = cclass(p, sc)) == NULL) + return(0); + break; + + case '?': + if (sc == 0) + return(0); + break; + + case '*': + s--; + do { + if (*p == '\0' || gmatch(s, p)) + return(1); + } while (*s++ != '\0'); + return(0); + + default: + if (sc != (pc&~QUOTE)) + return(0); + } + } + return(*s == 0); +} + +static char * +cclass(p, sub) +register char *p; +register int sub; +{ + register int c, d, not, found; + + if ((not = *p == NOT) != 0) + p++; + found = not; + do { + if (*p == '\0') + return((char *)NULL); + c = *p & CMASK; + if (p[1] == '-' && p[2] != ']') { + d = p[2] & CMASK; + p++; + } else + d = c; + if (c == sub || (c <= sub && sub <= d)) + found = !not; + } while (*++p != ']'); + return(found? p+1: (char *)NULL); +} + +/* -------- area.c -------- */ +#define REGSIZE sizeof(struct region) +#define GROWBY 256 +#undef SHRINKBY 64 +#define FREE 32767 +#define BUSY 0 +#define ALIGN (sizeof(int)-1) + +/* #include "area.h" */ + +struct region { + struct region *next; + int area; +}; + +/* + * All memory between (char *)areabot and (char *)(areatop+1) is + * exclusively administered by the area management routines. + * It is assumed that sbrk() and brk() manipulate the high end. + */ +static struct region *areabot; /* bottom of area */ +static struct region *areatop; /* top of area */ +static struct region *areanxt; /* starting point of scan */ + +void +initarea() +{ + while ((int)sbrk(0) & ALIGN) + sbrk(1); + areabot = (struct region *)sbrk(REGSIZE); + areabot->next = areabot; + areabot->area = BUSY; + areatop = areabot; + areanxt = areabot; +} + +char * +getcell(nbytes) +unsigned nbytes; +{ + register int nregio; + register struct region *p, *q; + register i; + + if (nbytes == 0) + abort(); /* silly and defeats the algorithm */ + /* + * round upwards and add administration area + */ + nregio = (nbytes+(REGSIZE-1))/REGSIZE + 1; + for (p = areanxt;;) { + if (p->area > areanum) { + /* + * merge free cells + */ + while ((q = p->next)->area > areanum && q != areanxt) + p->next = q->next; + /* + * exit loop if cell big enough + */ + if (q >= p + nregio) + goto found; + } + p = p->next; + if (p == areanxt) + break; + } + i = nregio >= GROWBY ? nregio : GROWBY; + p = (struct region *)sbrk(i * REGSIZE); + if (p == (struct region *)-1) + return((char *)NULL); + p--; + if (p != areatop) + abort(); /* allocated areas are contiguous */ + q = p + i; + p->next = q; + p->area = FREE; + q->next = areabot; + q->area = BUSY; + areatop = q; +found: + /* + * we found a FREE area big enough, pointed to by 'p', and up to 'q' + */ + areanxt = p + nregio; + if (areanxt < q) { + /* + * split into requested area and rest + */ + if (areanxt+1 > q) + abort(); /* insufficient space left for admin */ + areanxt->next = q; + areanxt->area = FREE; + p->next = areanxt; + } + p->area = areanum; + return((char *)(p+1)); +} + +void +freecell(cp) +char *cp; +{ + register struct region *p; + + if ((p = (struct region *)cp) != NULL) { + p--; + if (p < areanxt) + areanxt = p; + p->area = FREE; + } +} + +void +freearea(a) +register int a; +{ + register struct region *p, *top; + + top = areatop; + for (p = areabot; p != top; p = p->next) + if (p->area >= a) + p->area = FREE; +} + +void +setarea(cp,a) +char *cp; +int a; +{ + register struct region *p; + + if ((p = (struct region *)cp) != NULL) + (p-1)->area = a; +} + +int +getarea(cp) +char *cp; +{ + return ((struct region*)cp-1)->area; +} + +void +garbage() +{ + register struct region *p, *q, *top; + + top = areatop; + for (p = areabot; p != top; p = p->next) { + if (p->area > areanum) { + while ((q = p->next)->area > areanum) + p->next = q->next; + areanxt = p; + } + } +#ifdef SHRINKBY + if (areatop >= q + SHRINKBY && q->area > areanum) { + brk((char *)(q+1)); + q->next = areabot; + q->area = BUSY; + areatop = q; + } +#endif +} diff --git a/commands/sh/sh2.c b/commands/sh/sh2.c new file mode 100755 index 000000000..2a9f80f72 --- /dev/null +++ b/commands/sh/sh2.c @@ -0,0 +1,800 @@ +#define Extern extern +#include <sys/types.h> +#include <signal.h> +#include <errno.h> +#include <setjmp.h> +#include "sh.h" + +/* -------- csyn.c -------- */ +/* + * shell: syntax (C version) + */ + +typedef union { + char *cp; + char **wp; + int i; + struct op *o; +} YYSTYPE; +#define WORD 256 +#define LOGAND 257 +#define LOGOR 258 +#define BREAK 259 +#define IF 260 +#define THEN 261 +#define ELSE 262 +#define ELIF 263 +#define FI 264 +#define CASE 265 +#define ESAC 266 +#define FOR 267 +#define WHILE 268 +#define UNTIL 269 +#define DO 270 +#define DONE 271 +#define IN 272 +#define YYERRCODE 300 + +/* flags to yylex */ +#define CONTIN 01 /* skip new lines to complete command */ + +/* #include "sh.h" */ +#define SYNTAXERR zzerr() +static int startl; +static int peeksym; +static int nlseen; +static int iounit = IODEFAULT; + +static YYSTYPE yylval; + +_PROTOTYPE(static struct op *pipeline, (int cf )); +_PROTOTYPE(static struct op *andor, (void)); +_PROTOTYPE(static struct op *c_list, (void)); +_PROTOTYPE(static int synio, (int cf )); +_PROTOTYPE(static void musthave, (int c, int cf )); +_PROTOTYPE(static struct op *simple, (void)); +_PROTOTYPE(static struct op *nested, (int type, int mark )); +_PROTOTYPE(static struct op *command, (int cf )); +_PROTOTYPE(static struct op *dogroup, (int onlydone )); +_PROTOTYPE(static struct op *thenpart, (void)); +_PROTOTYPE(static struct op *elsepart, (void)); +_PROTOTYPE(static struct op *caselist, (void)); +_PROTOTYPE(static struct op *casepart, (void)); +_PROTOTYPE(static char **pattern, (void)); +_PROTOTYPE(static char **wordlist, (void)); +_PROTOTYPE(static struct op *list, (struct op *t1, struct op *t2 )); +_PROTOTYPE(static struct op *block, (int type, struct op *t1, struct op *t2, char **wp )); +_PROTOTYPE(static struct op *newtp, (void)); +_PROTOTYPE(static struct op *namelist, (struct op *t )); +_PROTOTYPE(static char **copyw, (void)); +_PROTOTYPE(static void word, (char *cp )); +_PROTOTYPE(static struct ioword **copyio, (void)); +_PROTOTYPE(static struct ioword *io, (int u, int f, char *cp )); +_PROTOTYPE(static void zzerr, (void)); +_PROTOTYPE(void yyerror, (char *s )); +_PROTOTYPE(static int yylex, (int cf )); +_PROTOTYPE(int collect, (int c, int c1 )); +_PROTOTYPE(int dual, (int c )); +_PROTOTYPE(static void diag, (int ec )); +_PROTOTYPE(static char *tree, (unsigned size )); +_PROTOTYPE(void printf, (char *s )); + +int +yyparse() +{ + startl = 1; + peeksym = 0; + yynerrs = 0; + outtree = c_list(); + musthave('\n', 0); + return(yynerrs!=0); +} + +static struct op * +pipeline(cf) +int cf; +{ + register struct op *t, *p; + register int c; + + t = command(cf); + if (t != NULL) { + while ((c = yylex(0)) == '|') { + if ((p = command(CONTIN)) == NULL) + SYNTAXERR; + if (t->type != TPAREN && t->type != TCOM) { + /* shell statement */ + t = block(TPAREN, t, NOBLOCK, NOWORDS); + } + t = block(TPIPE, t, p, NOWORDS); + } + peeksym = c; + } + return(t); +} + +static struct op * +andor() +{ + register struct op *t, *p; + register int c; + + t = pipeline(0); + if (t != NULL) { + while ((c = yylex(0)) == LOGAND || c == LOGOR) { + if ((p = pipeline(CONTIN)) == NULL) + SYNTAXERR; + t = block(c == LOGAND? TAND: TOR, t, p, NOWORDS); + } + peeksym = c; + } + return(t); +} + +static struct op * +c_list() +{ + register struct op *t, *p; + register int c; + + t = andor(); + if (t != NULL) { + if((peeksym = yylex(0)) == '&') + t = block(TASYNC, t, NOBLOCK, NOWORDS); + while ((c = yylex(0)) == ';' || c == '&' || (multiline && c == '\n')) { + if ((p = andor()) == NULL) + return(t); + if((peeksym = yylex(0)) == '&') + p = block(TASYNC, p, NOBLOCK, NOWORDS); + t = list(t, p); + } + peeksym = c; + } + return(t); +} + + +static int +synio(cf) +int cf; +{ + register struct ioword *iop; + register int i; + register int c; + + if ((c = yylex(cf)) != '<' && c != '>') { + peeksym = c; + return(0); + } + i = yylval.i; + musthave(WORD, 0); + iop = io(iounit, i, yylval.cp); + iounit = IODEFAULT; + if (i & IOHERE) + markhere(yylval.cp, iop); + return(1); +} + +static void +musthave(c, cf) +int c, cf; +{ + if ((peeksym = yylex(cf)) != c) + SYNTAXERR; + peeksym = 0; +} + +static struct op * +simple() +{ + register struct op *t; + + t = NULL; + for (;;) { + switch (peeksym = yylex(0)) { + case '<': + case '>': + (void) synio(0); + break; + + case WORD: + if (t == NULL) { + t = newtp(); + t->type = TCOM; + } + peeksym = 0; + word(yylval.cp); + break; + + default: + return(t); + } + } +} + +static struct op * +nested(type, mark) +int type, mark; +{ + register struct op *t; + + multiline++; + t = c_list(); + musthave(mark, 0); + multiline--; + return(block(type, t, NOBLOCK, NOWORDS)); +} + +static struct op * +command(cf) +int cf; +{ + register struct op *t; + struct wdblock *iosave; + register int c; + + iosave = iolist; + iolist = NULL; + if (multiline) + cf |= CONTIN; + while (synio(cf)) + cf = 0; + switch (c = yylex(cf)) { + default: + peeksym = c; + if ((t = simple()) == NULL) { + if (iolist == NULL) + return((struct op *)NULL); + t = newtp(); + t->type = TCOM; + } + break; + + case '(': + t = nested(TPAREN, ')'); + break; + + case '{': + t = nested(TBRACE, '}'); + break; + + case FOR: + t = newtp(); + t->type = TFOR; + musthave(WORD, 0); + startl = 1; + t->str = yylval.cp; + multiline++; + t->words = wordlist(); + if ((c = yylex(0)) != '\n' && c != ';') + peeksym = c; + t->left = dogroup(0); + multiline--; + break; + + case WHILE: + case UNTIL: + multiline++; + t = newtp(); + t->type = c == WHILE? TWHILE: TUNTIL; + t->left = c_list(); + t->right = dogroup(1); + t->words = NULL; + multiline--; + break; + + case CASE: + t = newtp(); + t->type = TCASE; + musthave(WORD, 0); + t->str = yylval.cp; + startl++; + multiline++; + musthave(IN, CONTIN); + startl++; + t->left = caselist(); + musthave(ESAC, 0); + multiline--; + break; + + case IF: + multiline++; + t = newtp(); + t->type = TIF; + t->left = c_list(); + t->right = thenpart(); + musthave(FI, 0); + multiline--; + break; + } + while (synio(0)) + ; + t = namelist(t); + iolist = iosave; + return(t); +} + +static struct op * +dogroup(onlydone) +int onlydone; +{ + register int c; + register struct op *list; + + c = yylex(CONTIN); + if (c == DONE && onlydone) + return((struct op *)NULL); + if (c != DO) + SYNTAXERR; + list = c_list(); + musthave(DONE, 0); + return(list); +} + +static struct op * +thenpart() +{ + register int c; + register struct op *t; + + if ((c = yylex(0)) != THEN) { + peeksym = c; + return((struct op *)NULL); + } + t = newtp(); + t->type = 0; + t->left = c_list(); + if (t->left == NULL) + SYNTAXERR; + t->right = elsepart(); + return(t); +} + +static struct op * +elsepart() +{ + register int c; + register struct op *t; + + switch (c = yylex(0)) { + case ELSE: + if ((t = c_list()) == NULL) + SYNTAXERR; + return(t); + + case ELIF: + t = newtp(); + t->type = TELIF; + t->left = c_list(); + t->right = thenpart(); + return(t); + + default: + peeksym = c; + return((struct op *)NULL); + } +} + +static struct op * +caselist() +{ + register struct op *t; + + t = NULL; + while ((peeksym = yylex(CONTIN)) != ESAC) + t = list(t, casepart()); + return(t); +} + +static struct op * +casepart() +{ + register struct op *t; + + t = newtp(); + t->type = TPAT; + t->words = pattern(); + musthave(')', 0); + t->left = c_list(); + if ((peeksym = yylex(CONTIN)) != ESAC) + musthave(BREAK, CONTIN); + return(t); +} + +static char ** +pattern() +{ + register int c, cf; + + cf = CONTIN; + do { + musthave(WORD, cf); + word(yylval.cp); + cf = 0; + } while ((c = yylex(0)) == '|'); + peeksym = c; + word(NOWORD); + return(copyw()); +} + +static char ** +wordlist() +{ + register int c; + + if ((c = yylex(0)) != IN) { + peeksym = c; + return((char **)NULL); + } + startl = 0; + while ((c = yylex(0)) == WORD) + word(yylval.cp); + word(NOWORD); + peeksym = c; + return(copyw()); +} + +/* + * supporting functions + */ +static struct op * +list(t1, t2) +register struct op *t1, *t2; +{ + if (t1 == NULL) + return(t2); + if (t2 == NULL) + return(t1); + return(block(TLIST, t1, t2, NOWORDS)); +} + +static struct op * +block(type, t1, t2, wp) +int type; +struct op *t1, *t2; +char **wp; +{ + register struct op *t; + + t = newtp(); + t->type = type; + t->left = t1; + t->right = t2; + t->words = wp; + return(t); +} + +struct res { + char *r_name; + int r_val; +} restab[] = { + "for", FOR, + "case", CASE, + "esac", ESAC, + "while", WHILE, + "do", DO, + "done", DONE, + "if", IF, + "in", IN, + "then", THEN, + "else", ELSE, + "elif", ELIF, + "until", UNTIL, + "fi", FI, + + ";;", BREAK, + "||", LOGOR, + "&&", LOGAND, + "{", '{', + "}", '}', + + 0, +}; + +int +rlookup(n) +register char *n; +{ + register struct res *rp; + + for (rp = restab; rp->r_name; rp++) + if (strcmp(rp->r_name, n) == 0) + return(rp->r_val); + return(0); +} + +static struct op * +newtp() +{ + register struct op *t; + + t = (struct op *)tree(sizeof(*t)); + t->type = 0; + t->words = NULL; + t->ioact = NULL; + t->left = NULL; + t->right = NULL; + t->str = NULL; + return(t); +} + +static struct op * +namelist(t) +register struct op *t; +{ + if (iolist) { + iolist = addword((char *)NULL, iolist); + t->ioact = copyio(); + } else + t->ioact = NULL; + if (t->type != TCOM) { + if (t->type != TPAREN && t->ioact != NULL) { + t = block(TPAREN, t, NOBLOCK, NOWORDS); + t->ioact = t->left->ioact; + t->left->ioact = NULL; + } + return(t); + } + word(NOWORD); + t->words = copyw(); + return(t); +} + +static char ** +copyw() +{ + register char **wd; + + wd = getwords(wdlist); + wdlist = 0; + return(wd); +} + +static void +word(cp) +char *cp; +{ + wdlist = addword(cp, wdlist); +} + +static struct ioword ** +copyio() +{ + register struct ioword **iop; + + iop = (struct ioword **) getwords(iolist); + iolist = 0; + return(iop); +} + +static struct ioword * +io(u, f, cp) +int u; +int f; +char *cp; +{ + register struct ioword *iop; + + iop = (struct ioword *) tree(sizeof(*iop)); + iop->io_unit = u; + iop->io_flag = f; + iop->io_name = cp; + iolist = addword((char *)iop, iolist); + return(iop); +} + +static void +zzerr() +{ + yyerror("syntax error"); +} + +void +yyerror(s) +char *s; +{ + yynerrs++; + if (talking && e.iop <= iostack) { + multiline = 0; + while (eofc() == 0 && yylex(0) != '\n') + ; + } + err(s); + fail(); +} + +static int +yylex(cf) +int cf; +{ + register int c, c1; + int atstart; + + if ((c = peeksym) > 0) { + peeksym = 0; + if (c == '\n') + startl = 1; + return(c); + } + nlseen = 0; + e.linep = line; + atstart = startl; + startl = 0; + yylval.i = 0; + +loop: + while ((c = getc(0)) == ' ' || c == '\t') + ; + switch (c) { + default: + if (any(c, "0123456789")) { + unget(c1 = getc(0)); + if (c1 == '<' || c1 == '>') { + iounit = c - '0'; + goto loop; + } + *e.linep++ = c; + c = c1; + } + break; + + case '#': + while ((c = getc(0)) != 0 && c != '\n') + ; + unget(c); + goto loop; + + case 0: + return(c); + + case '$': + *e.linep++ = c; + if ((c = getc(0)) == '{') { + if ((c = collect(c, '}')) != '\0') + return(c); + goto pack; + } + break; + + case '`': + case '\'': + case '"': + if ((c = collect(c, c)) != '\0') + return(c); + goto pack; + + case '|': + case '&': + case ';': + if ((c1 = dual(c)) != '\0') { + startl = 1; + return(c1); + } + startl = 1; + return(c); + case '^': + startl = 1; + return('|'); + case '>': + case '<': + diag(c); + return(c); + + case '\n': + nlseen++; + gethere(); + startl = 1; + if (multiline || cf & CONTIN) { + if (talking && e.iop <= iostack) + prs(cprompt->value); + if (cf & CONTIN) + goto loop; + } + return(c); + + case '(': + case ')': + startl = 1; + return(c); + } + + unget(c); + +pack: + while ((c = getc(0)) != 0 && !any(c, "`$ '\"\t;&<>()|^\n")) + if (e.linep >= elinep) + err("word too long"); + else + *e.linep++ = c; + unget(c); + if(any(c, "\"'`$")) + goto loop; + *e.linep++ = '\0'; + if (atstart && (c = rlookup(line))!=0) { + startl = 1; + return(c); + } + yylval.cp = strsave(line, areanum); + return(WORD); +} + +int +collect(c, c1) +register c, c1; +{ + char s[2]; + + *e.linep++ = c; + while ((c = getc(c1)) != c1) { + if (c == 0) { + unget(c); + s[0] = c1; + s[1] = 0; + prs("no closing "); yyerror(s); + return(YYERRCODE); + } + if (talking && c == '\n' && e.iop <= iostack) + prs(cprompt->value); + *e.linep++ = c; + } + *e.linep++ = c; + return(0); +} + +int +dual(c) +register c; +{ + char s[3]; + register char *cp = s; + + *cp++ = c; + *cp++ = getc(0); + *cp = 0; + if ((c = rlookup(s)) == 0) + unget(*--cp); + return(c); +} + +static void +diag(ec) +register int ec; +{ + register int c; + + c = getc(0); + if (c == '>' || c == '<') { + if (c != ec) + zzerr(); + yylval.i = ec == '>'? IOWRITE|IOCAT: IOHERE; + c = getc(0); + } else + yylval.i = ec == '>'? IOWRITE: IOREAD; + if (c != '&' || yylval.i == IOHERE) + unget(c); + else + yylval.i |= IODUP; +} + +static char * +tree(size) +unsigned size; +{ + register char *t; + + if ((t = getcell(size)) == NULL) { + prs("command line too complicated\n"); + fail(); + /* NOTREACHED */ + } + return(t); +} + +/* VARARGS1 */ +/* ARGSUSED */ +void +printf(s) /* yyparse calls it */ +char *s; +{ +} + diff --git a/commands/sh/sh3.c b/commands/sh/sh3.c new file mode 100755 index 000000000..80df511d3 --- /dev/null +++ b/commands/sh/sh3.c @@ -0,0 +1,1147 @@ +#define Extern extern +#include <sys/types.h> +#include <signal.h> +#include <errno.h> +#include <setjmp.h> +#include <stddef.h> +#include <time.h> +#include <sys/times.h> +#include <sys/stat.h> +#include <sys/wait.h> +#undef NULL +#include "sh.h" + +/* -------- exec.c -------- */ +/* #include "sh.h" */ + +/* + * execute tree + */ + +static char *signame[] = { + "Signal 0", + "Hangup", + (char *)NULL, /* interrupt */ + "Quit", + "Illegal instruction", + "Trace/BPT trap", + "Abort", + "EMT trap", + "Floating exception", + "Killed", + "Bus error", + "Memory fault", + "Bad system call", + (char *)NULL, /* broken pipe */ + "Alarm clock", + "Terminated", +}; +#define NSIGNAL (sizeof(signame)/sizeof(signame[0])) + + +_PROTOTYPE(static int forkexec, (struct op *t, int *pin, int *pout, int act, char **wp, int *pforked )); +_PROTOTYPE(static int parent, (void)); +_PROTOTYPE(int iosetup, (struct ioword *iop, int pipein, int pipeout )); +_PROTOTYPE(static void echo, (char **wp )); +_PROTOTYPE(static struct op **find1case, (struct op *t, char *w )); +_PROTOTYPE(static struct op *findcase, (struct op *t, char *w )); +_PROTOTYPE(static void brkset, (struct brkcon *bc )); +_PROTOTYPE(int dolabel, (void)); +_PROTOTYPE(int dochdir, (struct op *t )); +_PROTOTYPE(int doshift, (struct op *t )); +_PROTOTYPE(int dologin, (struct op *t )); +_PROTOTYPE(int doumask, (struct op *t )); +_PROTOTYPE(int doexec, (struct op *t )); +_PROTOTYPE(int dodot, (struct op *t )); +_PROTOTYPE(int dowait, (struct op *t )); +_PROTOTYPE(int doread, (struct op *t )); +_PROTOTYPE(int doeval, (struct op *t )); +_PROTOTYPE(int dotrap, (struct op *t )); +_PROTOTYPE(int getsig, (char *s )); +_PROTOTYPE(void setsig, (int n, void (*f)())); +_PROTOTYPE(int getn, (char *as )); +_PROTOTYPE(int dobreak, (struct op *t )); +_PROTOTYPE(int docontinue, (struct op *t )); +_PROTOTYPE(static int brkcontin, (char *cp, int val )); +_PROTOTYPE(int doexit, (struct op *t )); +_PROTOTYPE(int doexport, (struct op *t )); +_PROTOTYPE(int doreadonly, (struct op *t )); +_PROTOTYPE(static void rdexp, (char **wp, void (*f)(), int key)); +_PROTOTYPE(static void badid, (char *s )); +_PROTOTYPE(int doset, (struct op *t )); +_PROTOTYPE(void varput, (char *s, int out )); +_PROTOTYPE(int dotimes, (void)); + +int +execute(t, pin, pout, act) +register struct op *t; +int *pin, *pout; +int act; +{ + register struct op *t1; + int i, pv[2], rv, child, a; + char *cp, **wp, **wp2; + struct var *vp; + struct brkcon bc; + + if (t == NULL) + return(0); + rv = 0; + a = areanum++; + wp = (wp2 = t->words) != NULL + ? eval(wp2, t->type == TCOM ? DOALL : DOALL & ~DOKEY) + : NULL; + + switch(t->type) { + case TPAREN: + case TCOM: + rv = forkexec(t, pin, pout, act, wp, &child); + if (child) { + exstat = rv; + leave(); + } + break; + + case TPIPE: + if ((rv = openpipe(pv)) < 0) + break; + pv[0] = remap(pv[0]); + pv[1] = remap(pv[1]); + (void) execute(t->left, pin, pv, 0); + rv = execute(t->right, pv, pout, 0); + break; + + case TLIST: + (void) execute(t->left, pin, pout, 0); + rv = execute(t->right, pin, pout, 0); + break; + + case TASYNC: + i = parent(); + if (i != 0) { + if (i != -1) { + setval(lookup("!"), putn(i)); + if (pin != NULL) + closepipe(pin); + if (talking) { + prs(putn(i)); + prs("\n"); + } + } else + rv = -1; + setstatus(rv); + } else { + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + if (talking) + signal(SIGTERM, SIG_DFL); + talking = 0; + if (pin == NULL) { + close(0); + open("/dev/null", 0); + } + exit(execute(t->left, pin, pout, FEXEC)); + } + break; + + case TOR: + case TAND: + rv = execute(t->left, pin, pout, 0); + if ((t1 = t->right)!=NULL && (rv == 0) == (t->type == TAND)) + rv = execute(t1, pin, pout, 0); + break; + + case TFOR: + if (wp == NULL) { + wp = dolv+1; + if ((i = dolc) < 0) + i = 0; + } else { + i = -1; + while (*wp++ != NULL) + ; + } + vp = lookup(t->str); + while (setjmp(bc.brkpt)) + if (isbreak) + goto broken; + brkset(&bc); + for (t1 = t->left; i-- && *wp != NULL;) { + setval(vp, *wp++); + rv = execute(t1, pin, pout, 0); + } + brklist = brklist->nextlev; + break; + + case TWHILE: + case TUNTIL: + while (setjmp(bc.brkpt)) + if (isbreak) + goto broken; + brkset(&bc); + t1 = t->left; + while ((execute(t1, pin, pout, 0) == 0) == (t->type == TWHILE)) + rv = execute(t->right, pin, pout, 0); + brklist = brklist->nextlev; + break; + + case TIF: + case TELIF: + if (t->right != NULL) { + rv = !execute(t->left, pin, pout, 0) ? + execute(t->right->left, pin, pout, 0): + execute(t->right->right, pin, pout, 0); + } + break; + + case TCASE: + if ((cp = evalstr(t->str, DOSUB|DOTRIM)) == 0) + cp = ""; + if ((t1 = findcase(t->left, cp)) != NULL) + rv = execute(t1, pin, pout, 0); + break; + + case TBRACE: +/* + if (iopp = t->ioact) + while (*iopp) + if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) { + rv = -1; + break; + } +*/ + if (rv >= 0 && (t1 = t->left)) + rv = execute(t1, pin, pout, 0); + break; + } + +broken: + t->words = wp2; + isbreak = 0; + freehere(areanum); + freearea(areanum); + areanum = a; + if (talking && intr) { + closeall(); + fail(); + } + if ((i = trapset) != 0) { + trapset = 0; + runtrap(i); + } + return(rv); +} + +static int +forkexec(t, pin, pout, act, wp, pforked) +register struct op *t; +int *pin, *pout; +int act; +char **wp; +int *pforked; +{ + int i, rv, (*shcom)(); + register int f; + char *cp; + struct ioword **iopp; + int resetsig; + char **owp; + + owp = wp; + resetsig = 0; + *pforked = 0; + shcom = NULL; + rv = -1; /* system-detected error */ + if (t->type == TCOM) { + while ((cp = *wp++) != NULL) + ; + cp = *wp; + + /* strip all initial assignments */ + /* not correct wrt PATH=yyy command etc */ + if (flag['x']) + echo (cp ? wp: owp); + if (cp == NULL && t->ioact == NULL) { + while ((cp = *owp++) != NULL && assign(cp, COPYV)) + ; + return(setstatus(0)); + } + else if (cp != NULL) + shcom = inbuilt(cp); + } + t->words = wp; + f = act; + if (shcom == NULL && (f & FEXEC) == 0) { + i = parent(); + if (i != 0) { + if (i == -1) + return(rv); + if (pin != NULL) + closepipe(pin); + return(pout==NULL? setstatus(waitfor(i,0)): 0); + } + if (talking) { + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + resetsig = 1; + } + talking = 0; + intr = 0; + (*pforked)++; + brklist = 0; + execflg = 0; + } + if (owp != NULL) + while ((cp = *owp++) != NULL && assign(cp, COPYV)) + if (shcom == NULL) + export(lookup(cp)); +#ifdef COMPIPE + if ((pin != NULL || pout != NULL) && shcom != NULL && shcom != doexec) { + err("piping to/from shell builtins not yet done"); + return(-1); + } +#endif + if (pin != NULL) { + dup2(pin[0], 0); + closepipe(pin); + } + if (pout != NULL) { + dup2(pout[1], 1); + closepipe(pout); + } + if ((iopp = t->ioact) != NULL) { + if (shcom != NULL && shcom != doexec) { + prs(cp); + err(": cannot redirect shell command"); + return(-1); + } + while (*iopp) + if (iosetup(*iopp++, pin!=NULL, pout!=NULL)) + return(rv); + } + if (shcom) + return(setstatus((*shcom)(t))); + /* should use FIOCEXCL */ + for (i=FDBASE; i<NOFILE; i++) + close(i); + if (resetsig) { + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + } + if (t->type == TPAREN) + exit(execute(t->left, NOPIPE, NOPIPE, FEXEC)); + if (wp[0] == NULL) + exit(0); + cp = rexecve(wp[0], wp, makenv()); + prs(wp[0]); prs(": "); warn(cp); + if (!execflg) + trap[0] = NULL; + leave(); + /* NOTREACHED */ +} + +/* + * common actions when creating a new child + */ +static int +parent() +{ + register int i; + + i = fork(); + if (i != 0) { + if (i == -1) + warn("try again"); + } + return(i); +} + +/* + * 0< 1> are ignored as required + * within pipelines. + */ +int +iosetup(iop, pipein, pipeout) +register struct ioword *iop; +int pipein, pipeout; +{ + register u; + char *cp, *msg; + + if (iop->io_unit == IODEFAULT) /* take default */ + iop->io_unit = iop->io_flag&(IOREAD|IOHERE)? 0: 1; + if (pipein && iop->io_unit == 0) + return(0); + if (pipeout && iop->io_unit == 1) + return(0); + msg = iop->io_flag&(IOREAD|IOHERE)? "open": "create"; + if ((iop->io_flag & IOHERE) == 0) { + cp = iop->io_name; + if ((cp = evalstr(cp, DOSUB|DOTRIM)) == NULL) + return(1); + } + if (iop->io_flag & IODUP) { + if (cp[1] || (!digit(*cp) && *cp != '-')) { + prs(cp); + err(": illegal >& argument"); + return(1); + } + if (*cp == '-') + iop->io_flag = IOCLOSE; + iop->io_flag &= ~(IOREAD|IOWRITE); + } + switch (iop->io_flag) { + case IOREAD: + u = open(cp, 0); + break; + + case IOHERE: + case IOHERE|IOXHERE: + u = herein(iop->io_name, iop->io_flag&IOXHERE); + cp = "here file "; + break; + + case IOWRITE|IOCAT: + if ((u = open(cp, 1)) >= 0) { + lseek(u, (long)0, 2); + break; + } + case IOWRITE: + u = creat(cp, 0666); + break; + + case IODUP: + u = dup2(*cp-'0', iop->io_unit); + break; + + case IOCLOSE: + close(iop->io_unit); + return(0); + } + if (u < 0) { + int e=errno; + prs(cp); + if (iop->io_flag&IOHERE) prs(iop->io_name); + prs(": cannot "); + prs(msg); + prs(" ("); + prs(strerror(e)); + warn(")"); + return(1); + } else { + if (u != iop->io_unit) { + dup2(u, iop->io_unit); + close(u); + } + } + return(0); +} + +static void +echo(wp) +register char **wp; +{ + register i; + + prs("+"); + for (i=0; wp[i]; i++) { + if (i) + prs(" "); + prs(wp[i]); + } + prs("\n"); +} + +static struct op ** +find1case(t, w) +struct op *t; +char *w; +{ + register struct op *t1; + struct op **tp; + register char **wp, *cp; + + if (t == NULL) + return((struct op **)NULL); + if (t->type == TLIST) { + if ((tp = find1case(t->left, w)) != NULL) + return(tp); + t1 = t->right; /* TPAT */ + } else + t1 = t; + for (wp = t1->words; *wp;) + if ((cp = evalstr(*wp++, DOSUB)) && gmatch(w, cp)) + return(&t1->left); + return((struct op **)NULL); +} + +static struct op * +findcase(t, w) +struct op *t; +char *w; +{ + register struct op **tp; + + return((tp = find1case(t, w)) != NULL? *tp: (struct op *)NULL); +} + +/* + * Enter a new loop level (marked for break/continue). + */ +static void +brkset(bc) +struct brkcon *bc; +{ + bc->nextlev = brklist; + brklist = bc; +} + +/* + * Wait for the last process created. + * Print a message for each process found + * that was killed by a signal. + * Ignore interrupt signals while waiting + * unless `canintr' is true. + */ +int +waitfor(lastpid, canintr) +register int lastpid; +int canintr; +{ + register int pid, rv; + int s; + int oheedint = heedint; + + heedint = 0; + rv = 0; + do { + pid = wait(&s); + if (pid == -1) { + if (errno != EINTR || canintr) + break; + } else { + if ((rv = WAITSIG(s)) != 0) { + if (rv < NSIGNAL) { + if (signame[rv] != NULL) { + if (pid != lastpid) { + prn(pid); + prs(": "); + } + prs(signame[rv]); + } + } else { + if (pid != lastpid) { + prn(pid); + prs(": "); + } + prs("Signal "); prn(rv); prs(" "); + } + if (WAITCORE(s)) + prs(" - core dumped"); + if (rv >= NSIGNAL || signame[rv]) + prs("\n"); + rv = -1; + } else + rv = WAITVAL(s); + } + } while (pid != lastpid); + heedint = oheedint; + if (intr) + if (talking) { + if (canintr) + intr = 0; + } else { + if (exstat == 0) exstat = rv; + onintr(0); + } + return(rv); +} + +int +setstatus(s) +register int s; +{ + exstat = s; + setval(lookup("?"), putn(s)); + return(s); +} + +/* + * PATH-searching interface to execve. + * If getenv("PATH") were kept up-to-date, + * execvp might be used. + */ +char * +rexecve(c, v, envp) +char *c, **v, **envp; +{ + register int i; + register char *sp, *tp; + int eacces = 0, asis = 0; + + sp = any('/', c)? "": path->value; + asis = *sp == '\0'; + while (asis || *sp != '\0') { + asis = 0; + tp = e.linep; + for (; *sp != '\0'; tp++) + if ((*tp = *sp++) == ':') { + asis = *sp == '\0'; + break; + } + if (tp != e.linep) + *tp++ = '/'; + for (i = 0; (*tp++ = c[i++]) != '\0';) + ; + execve(e.linep, v, envp); + switch (errno) { + case ENOEXEC: + *v = e.linep; + tp = *--v; + *v = e.linep; + execve("/bin/sh", v, envp); + *v = tp; + return("no Shell"); + + case ENOMEM: + return("program too big"); + + case E2BIG: + return("argument list too long"); + + case EACCES: + eacces++; + break; + } + } + return(errno==ENOENT ? "not found" : "cannot execute"); +} + +/* + * Run the command produced by generator `f' + * applied to stream `arg'. + */ +int +run(argp, f) +struct ioarg *argp; +int (*f)(); +{ + struct op *otree; + struct wdblock *swdlist; + struct wdblock *siolist; + jmp_buf ev, rt; + xint *ofail; + int rv; + + areanum++; + swdlist = wdlist; + siolist = iolist; + otree = outtree; + ofail = failpt; + rv = -1; + if (newenv(setjmp(errpt = ev)) == 0) { + wdlist = 0; + iolist = 0; + pushio(argp, f); + e.iobase = e.iop; + yynerrs = 0; + if (setjmp(failpt = rt) == 0 && yyparse() == 0) + rv = execute(outtree, NOPIPE, NOPIPE, 0); + quitenv(); + } + wdlist = swdlist; + iolist = siolist; + failpt = ofail; + outtree = otree; + freearea(areanum--); + return(rv); +} + +/* -------- do.c -------- */ +/* #include "sh.h" */ + +/* + * built-in commands: doX + */ + +int +dolabel() +{ + return(0); +} + +int +dochdir(t) +register struct op *t; +{ + register char *cp, *er; + + if ((cp = t->words[1]) == NULL && (cp = homedir->value) == NULL) + er = ": no home directory"; + else if(chdir(cp) < 0) + er = ": bad directory"; + else + return(0); + prs(cp != NULL? cp: "cd"); + err(er); + return(1); +} + +int +doshift(t) +register struct op *t; +{ + register n; + + n = t->words[1]? getn(t->words[1]): 1; + if(dolc < n) { + err("nothing to shift"); + return(1); + } + dolv[n] = dolv[0]; + dolv += n; + dolc -= n; + setval(lookup("#"), putn(dolc)); + return(0); +} + +/* + * execute login and newgrp directly + */ +int +dologin(t) +struct op *t; +{ + register char *cp; + + if (talking) { + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + } + cp = rexecve(t->words[0], t->words, makenv()); + prs(t->words[0]); prs(": "); err(cp); + return(1); +} + +int +doumask(t) +register struct op *t; +{ + register int i, n; + register char *cp; + + if ((cp = t->words[1]) == NULL) { + i = umask(0); + umask(i); + for (n=3*4; (n-=3) >= 0;) + putc('0'+((i>>n)&07)); + putc('\n'); + } else { + for (n=0; *cp>='0' && *cp<='9'; cp++) + n = n*8 + (*cp-'0'); + umask(n); + } + return(0); +} + +int +doexec(t) +register struct op *t; +{ + register i; + jmp_buf ex; + xint *ofail; + + t->ioact = NULL; + for(i = 0; (t->words[i]=t->words[i+1]) != NULL; i++) + ; + if (i == 0) + return(1); + execflg = 1; + ofail = failpt; + if (setjmp(failpt = ex) == 0) + execute(t, NOPIPE, NOPIPE, FEXEC); + failpt = ofail; + execflg = 0; + return(1); +} + +int +dodot(t) +struct op *t; +{ + register i; + register char *sp, *tp; + char *cp; + + if ((cp = t->words[1]) == NULL) + return(0); + sp = any('/', cp)? ":": path->value; + while (*sp) { + tp = e.linep; + while (*sp && (*tp = *sp++) != ':') + tp++; + if (tp != e.linep) + *tp++ = '/'; + for (i = 0; (*tp++ = cp[i++]) != '\0';) + ; + if ((i = open(e.linep, 0)) >= 0) { + exstat = 0; + next(remap(i)); + return(exstat); + } + } + prs(cp); + err(": not found"); + return(-1); +} + +int +dowait(t) +struct op *t; +{ + register i; + register char *cp; + + if ((cp = t->words[1]) != NULL) { + i = getn(cp); + if (i == 0) + return(0); + } else + i = -1; + setstatus(waitfor(i, 1)); + return(0); +} + +int +doread(t) +struct op *t; +{ + register char *cp, **wp; + register nb; + register int nl = 0; + + if (t->words[1] == NULL) { + err("Usage: read name ..."); + return(1); + } + for (wp = t->words+1; *wp; wp++) { + for (cp = e.linep; !nl && cp < elinep-1; cp++) + if ((nb = read(0, cp, sizeof(*cp))) != sizeof(*cp) || + (nl = (*cp == '\n')) || + (wp[1] && any(*cp, ifs->value))) + break; + *cp = 0; + if (nb <= 0) + break; + setval(lookup(*wp), e.linep); + } + return(nb <= 0); +} + +int +doeval(t) +register struct op *t; +{ + return(RUN(awordlist, t->words+1, wdchar)); +} + +int +dotrap(t) +register struct op *t; +{ + register int n, i; + register int resetsig; + + if (t->words[1] == NULL) { + for (i=0; i<=_NSIG; i++) + if (trap[i]) { + prn(i); + prs(": "); + prs(trap[i]); + prs("\n"); + } + return(0); + } + resetsig = digit(*t->words[1]); + for (i = resetsig ? 1 : 2; t->words[i] != NULL; ++i) { + n = getsig(t->words[i]); + xfree(trap[n]); + trap[n] = 0; + if (!resetsig) { + if (*t->words[1] != '\0') { + trap[n] = strsave(t->words[1], 0); + setsig(n, sig); + } else + setsig(n, SIG_IGN); + } else { + if (talking) + if (n == SIGINT) + setsig(n, onintr); + else + setsig(n, n == SIGQUIT ? SIG_IGN + : SIG_DFL); + else + setsig(n, SIG_DFL); + } + } + return(0); +} + +int +getsig(s) +char *s; +{ + register int n; + + if ((n = getn(s)) < 0 || n > _NSIG) { + err("trap: bad signal number"); + n = 0; + } + return(n); +} + +void +setsig(n, f) +register n; +_PROTOTYPE(void (*f), (int)); +{ + if (n == 0) + return; + if (signal(n, SIG_IGN) != SIG_IGN || ourtrap[n]) { + ourtrap[n] = 1; + signal(n, f); + } +} + +int +getn(as) +char *as; +{ + register char *s; + register n, m; + + s = as; + m = 1; + if (*s == '-') { + m = -1; + s++; + } + for (n = 0; digit(*s); s++) + n = (n*10) + (*s-'0'); + if (*s) { + prs(as); + err(": bad number"); + } + return(n*m); +} + +int +dobreak(t) +struct op *t; +{ + return(brkcontin(t->words[1], 1)); +} + +int +docontinue(t) +struct op *t; +{ + return(brkcontin(t->words[1], 0)); +} + +static int +brkcontin(cp, val) +register char *cp; +int val; +{ + register struct brkcon *bc; + register nl; + + nl = cp == NULL? 1: getn(cp); + if (nl <= 0) + nl = 999; + do { + if ((bc = brklist) == NULL) + break; + brklist = bc->nextlev; + } while (--nl); + if (nl) { + err("bad break/continue level"); + return(1); + } + isbreak = val; + longjmp(bc->brkpt, 1); + /* NOTREACHED */ +} + +int +doexit(t) +struct op *t; +{ + register char *cp; + + execflg = 0; + if ((cp = t->words[1]) != NULL) + setstatus(getn(cp)); + leave(); + /* NOTREACHED */ +} + +int +doexport(t) +struct op *t; +{ + rdexp(t->words+1, export, EXPORT); + return(0); +} + +int +doreadonly(t) +struct op *t; +{ + rdexp(t->words+1, ronly, RONLY); + return(0); +} + +static void +rdexp(wp, f, key) +register char **wp; +void (*f)(); +int key; +{ + if (*wp != NULL) { + for (; *wp != NULL; wp++) + if (checkname(*wp)) + (*f)(lookup(*wp)); + else + badid(*wp); + } else + putvlist(key, 1); +} + +static void +badid(s) +register char *s; +{ + prs(s); + err(": bad identifier"); +} + +int +doset(t) +register struct op *t; +{ + register struct var *vp; + register char *cp; + register n; + + if ((cp = t->words[1]) == NULL) { + for (vp = vlist; vp; vp = vp->next) + varput(vp->name, 1); + return(0); + } + if (*cp == '-') { + /* bad: t->words++; */ + for(n = 0; (t->words[n]=t->words[n+1]) != NULL; n++) + ; + if (*++cp == 0) + flag['x'] = flag['v'] = 0; + else + for (; *cp; cp++) + switch (*cp) { + case 'e': + if (!talking) + flag['e']++; + break; + + default: + if (*cp>='a' && *cp<='z') + flag[*cp]++; + break; + } + setdash(); + } + if (t->words[1]) { + t->words[0] = dolv[0]; + for (n=1; t->words[n]; n++) + setarea((char *)t->words[n], 0); + dolc = n-1; + dolv = t->words; + setval(lookup("#"), putn(dolc)); + setarea((char *)(dolv-1), 0); + } + return(0); +} + +void +varput(s, out) +register char *s; +int out; +{ + if (letnum(*s)) { + write(out, s, strlen(s)); + write(out, "\n", 1); + } +} + + +#define SECS 60L +#define MINS 3600L + +int +dotimes() +{ + struct tms tbuf; + + times(&tbuf); + + prn((int)(tbuf.tms_cutime / MINS)); + prs("m"); + prn((int)((tbuf.tms_cutime % MINS) / SECS)); + prs("s "); + prn((int)(tbuf.tms_cstime / MINS)); + prs("m"); + prn((int)((tbuf.tms_cstime % MINS) / SECS)); + prs("s\n"); + return(0); +} + +struct builtin { + char *command; + int (*fn)(); +}; +static struct builtin builtin[] = { + ":", dolabel, + "cd", dochdir, + "shift", doshift, + "exec", doexec, + "wait", dowait, + "read", doread, + "eval", doeval, + "trap", dotrap, + "break", dobreak, + "continue", docontinue, + "exit", doexit, + "export", doexport, + "readonly", doreadonly, + "set", doset, + ".", dodot, + "umask", doumask, + "login", dologin, + "newgrp", dologin, + "times", dotimes, + 0, +}; + +int (*inbuilt(s))() +register char *s; +{ + register struct builtin *bp; + + for (bp = builtin; bp->command != NULL; bp++) + if (strcmp(bp->command, s) == 0) + return(bp->fn); + return((int(*)())NULL); +} + diff --git a/commands/sh/sh4.c b/commands/sh/sh4.c new file mode 100755 index 000000000..2603abbd6 --- /dev/null +++ b/commands/sh/sh4.c @@ -0,0 +1,779 @@ +#define Extern extern +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/dir.h> +#include <limits.h> +#include <signal.h> +#include <errno.h> +#include <setjmp.h> +#include "sh.h" + +/* -------- eval.c -------- */ +/* #include "sh.h" */ +/* #include "word.h" */ + +/* + * ${} + * `command` + * blank interpretation + * quoting + * glob + */ + +_PROTOTYPE(static int expand, (char *cp, struct wdblock **wbp, int f )); +_PROTOTYPE(static char *blank, (int f )); +_PROTOTYPE(static int dollar, (int quoted )); +_PROTOTYPE(static int grave, (int quoted )); +_PROTOTYPE(void globname, (char *we, char *pp )); +_PROTOTYPE(static char *generate, (char *start1, char *end1, char *middle, char *end )); +_PROTOTYPE(static int anyspcl, (struct wdblock *wb )); +_PROTOTYPE(static int xstrcmp, (char *p1, char *p2 )); +_PROTOTYPE(void glob0, (char *a0, unsigned int a1, int a2, int (*a3)(char *, char *))); +_PROTOTYPE(void glob1, (char *base, char *lim )); +_PROTOTYPE(void glob2, (char *i, char *j )); +_PROTOTYPE(void glob3, (char *i, char *j, char *k )); +_PROTOTYPE(char *memcopy, (char *ato, char *from, int nb )); + +char ** +eval(ap, f) +register char **ap; +int f; +{ + struct wdblock *wb; + char **wp; + char **wf; + jmp_buf ev; + + wp = NULL; + wb = NULL; + wf = NULL; + if (newenv(setjmp(errpt = ev)) == 0) { + while (*ap && isassign(*ap)) + expand(*ap++, &wb, f & ~DOGLOB); + if (flag['k']) { + for (wf = ap; *wf; wf++) { + if (isassign(*wf)) + expand(*wf, &wb, f & ~DOGLOB); + } + } + for (wb = addword((char *)0, wb); *ap; ap++) { + if (!flag['k'] || !isassign(*ap)) + expand(*ap, &wb, f & ~DOKEY); + } + wb = addword((char *)0, wb); + wp = getwords(wb); + quitenv(); + } else + gflg = 1; + return(gflg? (char **)NULL: wp); +} + +/* + * Make the exported environment from the exported + * names in the dictionary. Keyword assignments + * will already have been done. + */ +char ** +makenv() + +{ + register struct wdblock *wb; + register struct var *vp; + + wb = NULL; + for (vp = vlist; vp; vp = vp->next) + if (vp->status & EXPORT) + wb = addword(vp->name, wb); + wb = addword((char *)0, wb); + return(getwords(wb)); +} + +char * +evalstr(cp, f) +register char *cp; +int f; +{ + struct wdblock *wb; + + wb = NULL; + if (expand(cp, &wb, f)) { + if (wb == NULL || wb->w_nword == 0 || (cp = wb->w_words[0]) == NULL) + cp = ""; + DELETE(wb); + } else + cp = NULL; + return(cp); +} + +static int +expand(cp, wbp, f) +register char *cp; +register struct wdblock **wbp; +int f; +{ + jmp_buf ev; + + gflg = 0; + if (cp == NULL) + return(0); + if (!anys("$`'\"", cp) && + !anys(ifs->value, cp) && + ((f&DOGLOB)==0 || !anys("[*?", cp))) { + cp = strsave(cp, areanum); + if (f & DOTRIM) + unquote(cp); + *wbp = addword(cp, *wbp); + return(1); + } + if (newenv(setjmp(errpt = ev)) == 0) { + PUSHIO(aword, cp, strchar); + e.iobase = e.iop; + while ((cp = blank(f)) && gflg == 0) { + e.linep = cp; + cp = strsave(cp, areanum); + if ((f&DOGLOB) == 0) { + if (f & DOTRIM) + unquote(cp); + *wbp = addword(cp, *wbp); + } else + *wbp = glob(cp, *wbp); + } + quitenv(); + } else + gflg = 1; + return(gflg == 0); +} + +/* + * Blank interpretation and quoting + */ +static char * +blank(f) +int f; +{ + register c, c1; + register char *sp; + int scanequals, foundequals; + + sp = e.linep; + scanequals = f & DOKEY; + foundequals = 0; + +loop: + switch (c = subgetc('"', foundequals)) { + case 0: + if (sp == e.linep) + return(0); + *e.linep++ = 0; + return(sp); + + default: + if (f & DOBLANK && any(c, ifs->value)) + goto loop; + break; + + case '"': + case '\'': + scanequals = 0; + if (INSUB()) + break; + for (c1 = c; (c = subgetc(c1, 1)) != c1;) { + if (c == 0) + break; + if (c == '\'' || !any(c, "$`\"")) + c |= QUOTE; + *e.linep++ = c; + } + c = 0; + } + unget(c); + if (!letter(c)) + scanequals = 0; + for (;;) { + c = subgetc('"', foundequals); + if (c == 0 || + (f & DOBLANK && any(c, ifs->value)) || + (!INSUB() && any(c, "\"'"))) { + scanequals = 0; + unget(c); + if (any(c, "\"'")) + goto loop; + break; + } + if (scanequals) + if (c == '=') { + foundequals = 1; + scanequals = 0; + } + else if (!letnum(c)) + scanequals = 0; + *e.linep++ = c; + } + *e.linep++ = 0; + return(sp); +} + +/* + * Get characters, substituting for ` and $ + */ +int +subgetc(ec, quoted) +register char ec; +int quoted; +{ + register char c; + +again: + c = getc(ec); + if (!INSUB() && ec != '\'') { + if (c == '`') { + if (grave(quoted) == 0) + return(0); + e.iop->task = XGRAVE; + goto again; + } + if (c == '$' && (c = dollar(quoted)) == 0) { + e.iop->task = XDOLL; + goto again; + } + } + return(c); +} + +/* + * Prepare to generate the string returned by ${} substitution. + */ +static int +dollar(quoted) +int quoted; +{ + int otask; + struct io *oiop; + char *dolp; + register char *s, c, *cp; + struct var *vp; + + c = readc(); + s = e.linep; + if (c != '{') { + *e.linep++ = c; + if (letter(c)) { + while ((c = readc())!=0 && letnum(c)) + if (e.linep < elinep) + *e.linep++ = c; + unget(c); + } + c = 0; + } else { + oiop = e.iop; + otask = e.iop->task; + e.iop->task = XOTHER; + while ((c = subgetc('"', 0))!=0 && c!='}' && c!='\n') + if (e.linep < elinep) + *e.linep++ = c; + if (oiop == e.iop) + e.iop->task = otask; + if (c != '}') { + err("unclosed ${"); + gflg++; + return(c); + } + } + if (e.linep >= elinep) { + err("string in ${} too long"); + gflg++; + e.linep -= 10; + } + *e.linep = 0; + if (*s) + for (cp = s+1; *cp; cp++) + if (any(*cp, "=-+?")) { + c = *cp; + *cp++ = 0; + break; + } + if (s[1] == 0 && (*s == '*' || *s == '@')) { + if (dolc > 1) { + /* currently this does not distinguish $* and $@ */ + /* should check dollar */ + e.linep = s; + PUSHIO(awordlist, dolv+1, dolchar); + return(0); + } else { /* trap the nasty ${=} */ + s[0] = '1'; + s[1] = 0; + } + } + e.linep = s; + vp = lookup(s); + if ((dolp = vp->value) == null) { + switch (c) { + case '=': + if (digit(*s)) { + err("cannot use ${...=...} with $n"); + gflg++; + break; + } + cp = evalstr(strsave(cp, areanum),DOSUB); + setval(vp, cp); + dolp = vp->value; + break; + + case '-': + dolp = evalstr(strsave(cp, areanum),DOSUB); + break; + + case '?': + if (*cp == 0) { + prs("missing value for "); + err(s); + } else + err(evalstr(strsave(cp, areanum),DOSUB)); + gflg++; + break; + } + } else if (c == '+') { + dolp = evalstr(strsave(cp, areanum),DOSUB); + } + if (flag['u'] && dolp == null) { + prs("unset variable: "); + err(s); + gflg++; + } + PUSHIO(aword, dolp, quoted ? qstrchar : strchar); + return(0); +} + +/* + * Run the command in `...` and read its output. + */ +static int +grave(quoted) +int quoted; +{ + int otask; + struct io *oiop; + register char *cp,*s; + register int i,c; + int pf[2]; + + c = readc(); + s = e.linep; + *e.linep++ = c; + oiop = e.iop; + otask = e.iop->task; + e.iop->task = XOTHER; + while ((c = subgetc('\'', 0))!=0 && c!='`') + if (e.linep < elinep) + *e.linep++ = c; + if (oiop == e.iop) + e.iop->task = otask; + if (c != '`') { + err("no closing `"); + return(0); + } + if (openpipe(pf) < 0) + return(0); + if ((i = fork()) == -1) { + closepipe(pf); + err("try again"); + return(0); + } + if (i != 0) { + e.linep = s; + close(pf[1]); + PUSHIO(afile, remap(pf[0]), quoted? qgravechar: gravechar); + return(1); + } + *e.linep = 0; + /* allow trapped signals */ + for (i=0; i<=_NSIG; i++) + if (ourtrap[i] && signal(i, SIG_IGN) != SIG_IGN) + signal(i, SIG_DFL); + dup2(pf[1], 1); + closepipe(pf); + flag['e'] = 0; + flag['v'] = 0; + flag['n'] = 0; + cp = strsave(e.linep = s, 0); + areanum = 1; + inithere(); + freearea(areanum); /* free old space */ + e.oenv = NULL; + e.iop = (e.iobase = iostack) - 1; + unquote(cp); + talking = 0; + PUSHIO(aword, cp, nlchar); + onecommand(); + exit(1); +} + +char * +unquote(as) +register char *as; +{ + register char *s; + + if ((s = as) != NULL) + while (*s) + *s++ &= ~QUOTE; + return(as); +} + +/* -------- glob.c -------- */ +/* #include "sh.h" */ + +/* + * glob + */ + +#define scopy(x) strsave((x), areanum) +#define BLKSIZ 512 +#define NDENT ((BLKSIZ+sizeof(struct direct)-1)/sizeof(struct direct)) + +static struct wdblock *cl, *nl; +static char spcl[] = "[?*"; + +struct wdblock * +glob(cp, wb) +char *cp; +struct wdblock *wb; +{ + register i; + register char *pp; + + if (cp == 0) + return(wb); + i = 0; + for (pp = cp; *pp; pp++) + if (any(*pp, spcl)) + i++; + else if (!any(*pp & ~QUOTE, spcl)) + *pp &= ~QUOTE; + if (i != 0) { + for (cl = addword(scopy(cp), (struct wdblock *)0); anyspcl(cl); cl = nl) { + nl = newword(cl->w_nword*2); + for(i=0; i<cl->w_nword; i++) { /* for each argument */ + for (pp = cl->w_words[i]; *pp; pp++) + if (any(*pp, spcl)) { + globname(cl->w_words[i], pp); + break; + } + if (*pp == '\0') + nl = addword(scopy(cl->w_words[i]), nl); + } + for(i=0; i<cl->w_nword; i++) + DELETE(cl->w_words[i]); + DELETE(cl); + } + for(i=0; i<cl->w_nword; i++) + unquote(cl->w_words[i]); + glob0((char *)cl->w_words, cl->w_nword, sizeof(char *), xstrcmp); + if (cl->w_nword) { + for (i=0; i<cl->w_nword; i++) + wb = addword(cl->w_words[i], wb); + DELETE(cl); + return(wb); + } + } + wb = addword(unquote(cp), wb); + return(wb); +} + +void +globname(we, pp) +char *we; +register char *pp; +{ + register char *np, *cp; + char *name, *gp, *dp; + int dn, j, n, k; + struct direct ent[NDENT]; + char dname[NAME_MAX+1]; + struct stat dbuf; + + for (np = we; np != pp; pp--) + if (pp[-1] == '/') + break; + for (dp = cp = space((int)(pp-np)+3); np < pp;) + *cp++ = *np++; + *cp++ = '.'; + *cp = '\0'; + for (gp = cp = space(strlen(pp)+1); *np && *np != '/';) + *cp++ = *np++; + *cp = '\0'; + dn = open(dp, 0); + if (dn < 0) { + DELETE(dp); + DELETE(gp); + return; + } + dname[NAME_MAX] = '\0'; + while ((n = read(dn, (char *)ent, sizeof(ent))) >= sizeof(*ent)) { + n /= sizeof(*ent); + for (j=0; j<n; j++) { + if (ent[j].d_ino == 0) + continue; + strncpy(dname, ent[j].d_name, NAME_MAX); + if (dname[0] == '.') + if (*gp != '.') + continue; + for(k=0; k<NAME_MAX; k++) + if (any(dname[k], spcl)) + dname[k] |= QUOTE; + if (gmatch(dname, gp)) { + name = generate(we, pp, dname, np); + if (*np && !anys(np, spcl)) { + if (stat(name,&dbuf)) { + DELETE(name); + continue; + } + } + nl = addword(name, nl); + } + } + } + close(dn); + DELETE(dp); + DELETE(gp); +} + +/* + * generate a pathname as below. + * start..end1 / middle end + * the slashes come for free + */ +static char * +generate(start1, end1, middle, end) +char *start1; +register char *end1; +char *middle, *end; +{ + char *p; + register char *op, *xp; + + p = op = space((int)(end1-start1)+strlen(middle)+strlen(end)+2); + for (xp = start1; xp != end1;) + *op++ = *xp++; + for (xp = middle; (*op++ = *xp++) != '\0';) + ; + op--; + for (xp = end; (*op++ = *xp++) != '\0';) + ; + return(p); +} + +static int +anyspcl(wb) +register struct wdblock *wb; +{ + register i; + register char **wd; + + wd = wb->w_words; + for (i=0; i<wb->w_nword; i++) + if (anys(spcl, *wd++)) + return(1); + return(0); +} + +static int +xstrcmp(p1, p2) +char *p1, *p2; +{ + return(strcmp(*(char **)p1, *(char **)p2)); +} + +/* -------- word.c -------- */ +/* #include "sh.h" */ +/* #include "word.h" */ + +#define NSTART 16 /* default number of words to allow for initially */ + +struct wdblock * +newword(nw) +register int nw; +{ + register struct wdblock *wb; + + wb = (struct wdblock *) space(sizeof(*wb) + nw*sizeof(char *)); + wb->w_bsize = nw; + wb->w_nword = 0; + return(wb); +} + +struct wdblock * +addword(wd, wb) +char *wd; +register struct wdblock *wb; +{ + register struct wdblock *wb2; + register nw; + + if (wb == NULL) + wb = newword(NSTART); + if ((nw = wb->w_nword) >= wb->w_bsize) { + wb2 = newword(nw * 2); + memcopy((char *)wb2->w_words, (char *)wb->w_words, nw*sizeof(char *)); + wb2->w_nword = nw; + DELETE(wb); + wb = wb2; + } + wb->w_words[wb->w_nword++] = wd; + return(wb); +} + +char ** +getwords(wb) +register struct wdblock *wb; +{ + register char **wd; + register nb; + + if (wb == NULL) + return((char **)NULL); + if (wb->w_nword == 0) { + DELETE(wb); + return((char **)NULL); + } + wd = (char **) space(nb = sizeof(*wd) * wb->w_nword); + memcopy((char *)wd, (char *)wb->w_words, nb); + DELETE(wb); /* perhaps should done by caller */ + return(wd); +} + +_PROTOTYPE(int (*func), (char *, char *)); +int globv; + +void +glob0(a0, a1, a2, a3) +char *a0; +unsigned a1; +int a2; +_PROTOTYPE(int (*a3), (char *, char *)); +{ + func = a3; + globv = a2; + glob1(a0, a0 + a1 * a2); +} + +void +glob1(base, lim) +char *base, *lim; +{ + register char *i, *j; + int v2; + char *lptr, *hptr; + int c; + unsigned n; + + + v2 = globv; + +top: + if ((n=(int)(lim-base)) <= v2) + return; + n = v2 * (n / (2*v2)); + hptr = lptr = base+n; + i = base; + j = lim-v2; + for(;;) { + if (i < lptr) { + if ((c = (*func)(i, lptr)) == 0) { + glob2(i, lptr -= v2); + continue; + } + if (c < 0) { + i += v2; + continue; + } + } + +begin: + if (j > hptr) { + if ((c = (*func)(hptr, j)) == 0) { + glob2(hptr += v2, j); + goto begin; + } + if (c > 0) { + if (i == lptr) { + glob3(i, hptr += v2, j); + i = lptr += v2; + goto begin; + } + glob2(i, j); + j -= v2; + i += v2; + continue; + } + j -= v2; + goto begin; + } + + + if (i == lptr) { + if (lptr-base >= lim-hptr) { + glob1(hptr+v2, lim); + lim = lptr; + } else { + glob1(base, lptr); + base = hptr+v2; + } + goto top; + } + + + glob3(j, lptr -= v2, i); + j = hptr -= v2; + } +} + +void +glob2(i, j) +char *i, *j; +{ + register char *index1, *index2, c; + int m; + + m = globv; + index1 = i; + index2 = j; + do { + c = *index1; + *index1++ = *index2; + *index2++ = c; + } while(--m); +} + +void +glob3(i, j, k) +char *i, *j, *k; +{ + register char *index1, *index2, *index3; + int c; + int m; + + m = globv; + index1 = i; + index2 = j; + index3 = k; + do { + c = *index1; + *index1++ = *index3; + *index3++ = *index2; + *index2++ = c; + } while(--m); +} + +char * +memcopy(ato, from, nb) +register char *ato, *from; +register int nb; +{ + register char *to; + + to = ato; + while (--nb >= 0) + *to++ = *from++; + return(ato); +} diff --git a/commands/sh/sh5.c b/commands/sh/sh5.c new file mode 100755 index 000000000..6e454bb3a --- /dev/null +++ b/commands/sh/sh5.c @@ -0,0 +1,681 @@ +#define Extern extern +#include <sys/types.h> +#include <signal.h> +#include <errno.h> +#include <setjmp.h> +#include "sh.h" + +/* -------- io.c -------- */ +/* #include "sh.h" */ + +/* + * shell IO + */ + +static struct iobuf sharedbuf = {AFID_NOBUF}; +static struct iobuf mainbuf = {AFID_NOBUF}; +static unsigned bufid = AFID_ID; /* buffer id counter */ + +struct ioarg temparg = {0, 0, 0, AFID_NOBUF, 0}; + +_PROTOTYPE(static void readhere, (char **name, char *s, int ec )); +_PROTOTYPE(void pushio, (struct ioarg *argp, int (*fn)())); +_PROTOTYPE(static int xxchar, (struct ioarg *ap )); +_PROTOTYPE(void tempname, (char *tname )); + +int +getc(ec) +register int ec; +{ + register int c; + + if(e.linep > elinep) { + while((c=readc()) != '\n' && c) + ; + err("input line too long"); + gflg++; + return(c); + } + c = readc(); + if (ec != '\'' && e.iop->task != XGRAVE) { + if(c == '\\') { + c = readc(); + if (c == '\n' && ec != '\"') + return(getc(ec)); + c |= QUOTE; + } + } + return(c); +} + +void +unget(c) +int c; +{ + if (e.iop >= e.iobase) + e.iop->peekc = c; +} + +int +eofc() + +{ + return e.iop < e.iobase || (e.iop->peekc == 0 && e.iop->prev == 0); +} + +int +readc() +{ + register c; + + for (; e.iop >= e.iobase; e.iop--) + if ((c = e.iop->peekc) != '\0') { + e.iop->peekc = 0; + return(c); + } + else { + if (e.iop->prev != 0) { + if ((c = (*e.iop->iofn)(e.iop->argp, e.iop)) != '\0') { + if (c == -1) { + e.iop++; + continue; + } + if (e.iop == iostack) + ioecho(c); + return(e.iop->prev = c); + } + else if (e.iop->task == XIO && e.iop->prev != '\n') { + e.iop->prev = 0; + if (e.iop == iostack) + ioecho('\n'); + return '\n'; + } + } + if (e.iop->task == XIO) { + if (multiline) + return e.iop->prev = 0; + if (talking && e.iop == iostack+1) + prs(prompt->value); + } + } + if (e.iop >= iostack) + return(0); + leave(); + /* NOTREACHED */ +} + +void +ioecho(c) +char c; +{ + if (flag['v']) + write(2, &c, sizeof c); +} + +void +pushio(argp, fn) +struct ioarg *argp; +int (*fn)(); +{ + if (++e.iop >= &iostack[NPUSH]) { + e.iop--; + err("Shell input nested too deeply"); + gflg++; + return; + } + e.iop->iofn = fn; + + if (argp->afid != AFID_NOBUF) + e.iop->argp = argp; + else { + e.iop->argp = ioargstack + (e.iop - iostack); + *e.iop->argp = *argp; + e.iop->argp->afbuf = e.iop == &iostack[0] ? &mainbuf : &sharedbuf; + if (isatty(e.iop->argp->afile) == 0 && + (e.iop == &iostack[0] || + lseek(e.iop->argp->afile, 0L, 1) != -1)) { + if (++bufid == AFID_NOBUF) + bufid = AFID_ID; + e.iop->argp->afid = bufid; + } + } + + e.iop->prev = ~'\n'; + e.iop->peekc = 0; + e.iop->xchar = 0; + e.iop->nlcount = 0; + if (fn == filechar || fn == linechar) + e.iop->task = XIO; + else if (fn == gravechar || fn == qgravechar) + e.iop->task = XGRAVE; + else + e.iop->task = XOTHER; +} + +struct io * +setbase(ip) +struct io *ip; +{ + register struct io *xp; + + xp = e.iobase; + e.iobase = ip; + return(xp); +} + +/* + * Input generating functions + */ + +/* + * Produce the characters of a string, then a newline, then EOF. + */ +int +nlchar(ap) +register struct ioarg *ap; +{ + register int c; + + if (ap->aword == NULL) + return(0); + if ((c = *ap->aword++) == 0) { + ap->aword = NULL; + return('\n'); + } + return(c); +} + +/* + * Given a list of words, produce the characters + * in them, with a space after each word. + */ +int +wdchar(ap) +register struct ioarg *ap; +{ + register char c; + register char **wl; + + if ((wl = ap->awordlist) == NULL) + return(0); + if (*wl != NULL) { + if ((c = *(*wl)++) != 0) + return(c & 0177); + ap->awordlist++; + return(' '); + } + ap->awordlist = NULL; + return('\n'); +} + +/* + * Return the characters of a list of words, + * producing a space between them. + */ +int +dolchar(ap) +register struct ioarg *ap; +{ + register char *wp; + + if ((wp = *ap->awordlist++) != NULL) { + PUSHIO(aword, wp, *ap->awordlist == NULL? strchar: xxchar); + return(-1); + } + return(0); +} + +static int +xxchar(ap) +register struct ioarg *ap; +{ + register int c; + + if (ap->aword == NULL) + return(0); + if ((c = *ap->aword++) == '\0') { + ap->aword = NULL; + return(' '); + } + return(c); +} + +/* + * Produce the characters from a single word (string). + */ +int +strchar(ap) +register struct ioarg *ap; +{ + register int c; + + if (ap->aword == NULL || (c = *ap->aword++) == 0) + return(0); + return(c); +} + +/* + * Produce quoted characters from a single word (string). + */ +int +qstrchar(ap) +register struct ioarg *ap; +{ + register int c; + + if (ap->aword == NULL || (c = *ap->aword++) == 0) + return(0); + return(c|QUOTE); +} + +/* + * Return the characters from a file. + */ +int +filechar(ap) +register struct ioarg *ap; +{ + register int i; + char c; + struct iobuf *bp = ap->afbuf; + + if (ap->afid != AFID_NOBUF) { + if ((i = ap->afid != bp->id) || bp->bufp == bp->ebufp) { + if (i) + lseek(ap->afile, ap->afpos, 0); + do { + i = read(ap->afile, bp->buf, sizeof(bp->buf)); + } while (i < 0 && errno == EINTR); + if (i <= 0) { + closef(ap->afile); + return 0; + } + bp->id = ap->afid; + bp->ebufp = (bp->bufp = bp->buf) + i; + } + ap->afpos++; + return *bp->bufp++ & 0177; + } + + do { + i = read(ap->afile, &c, sizeof(c)); + } while (i < 0 && errno == EINTR); + return(i == sizeof(c)? c&0177: (closef(ap->afile), 0)); +} + +/* + * Return the characters from a here temp file. + */ +int +herechar(ap) +register struct ioarg *ap; +{ + char c; + + + if (read(ap->afile, &c, sizeof(c)) != sizeof(c)) { + close(ap->afile); + c = 0; + } + return (c); + +} + +/* + * Return the characters produced by a process (`...`). + * Quote them if required, and remove any trailing newline characters. + */ +int +gravechar(ap, iop) +struct ioarg *ap; +struct io *iop; +{ + register int c; + + if ((c = qgravechar(ap, iop)&~QUOTE) == '\n') + c = ' '; + return(c); +} + +int +qgravechar(ap, iop) +register struct ioarg *ap; +struct io *iop; +{ + register int c; + + if (iop->xchar) { + if (iop->nlcount) { + iop->nlcount--; + return('\n'|QUOTE); + } + c = iop->xchar; + iop->xchar = 0; + } else if ((c = filechar(ap)) == '\n') { + iop->nlcount = 1; + while ((c = filechar(ap)) == '\n') + iop->nlcount++; + iop->xchar = c; + if (c == 0) + return(c); + iop->nlcount--; + c = '\n'; + } + return(c!=0? c|QUOTE: 0); +} + +/* + * Return a single command (usually the first line) from a file. + */ +int +linechar(ap) +register struct ioarg *ap; +{ + register int c; + + if ((c = filechar(ap)) == '\n') { + if (!multiline) { + closef(ap->afile); + ap->afile = -1; /* illegal value */ + } + } + return(c); +} + +void +prs(s) +register char *s; +{ + if (*s) + write(2, s, strlen(s)); +} + +void +putc(c) +char c; +{ + write(2, &c, sizeof c); +} + +void +prn(u) +unsigned u; +{ + prs(itoa(u, 0)); +} + +void +closef(i) +register int i; +{ + if (i > 2) + close(i); +} + +void +closeall() +{ + register u; + + for (u=NUFILE; u<NOFILE;) + close(u++); +} + +/* + * remap fd into Shell's fd space + */ +int +remap(fd) +register int fd; +{ + register int i; + int map[NOFILE]; + + if (fd < e.iofd) { + for (i=0; i<NOFILE; i++) + map[i] = 0; + do { + map[fd] = 1; + fd = dup(fd); + } while (fd >= 0 && fd < e.iofd); + for (i=0; i<NOFILE; i++) + if (map[i]) + close(i); + if (fd < 0) + err("too many files open in shell"); + } + return(fd); +} + +int +openpipe(pv) +register int *pv; +{ + register int i; + + if ((i = pipe(pv)) < 0) + err("can't create pipe - try again"); + return(i); +} + +void +closepipe(pv) +register int *pv; +{ + if (pv != NULL) { + close(*pv++); + close(*pv); + } +} + +/* -------- here.c -------- */ +/* #include "sh.h" */ + +/* + * here documents + */ + +struct here { + char *h_tag; + int h_dosub; + struct ioword *h_iop; + struct here *h_next; +}; + +static struct here *inhere; /* list of hear docs while parsing */ +static struct here *acthere; /* list of active here documents */ + +void +inithere() +{ + inhere=acthere=(struct here*)0; +} + +void +markhere(s, iop) +register char *s; +struct ioword *iop; +{ + register struct here *h, *lh; + + h = (struct here *) space(sizeof(struct here)); + if (h == 0) + return; + h->h_tag = evalstr(s, DOSUB); + if (h->h_tag == 0) + return; + h->h_iop = iop; + iop->io_name = 0; + h->h_next = NULL; + if (inhere == 0) + inhere = h; + else + for (lh = inhere; lh!=NULL; lh = lh->h_next) + if (lh->h_next == 0) { + lh->h_next = h; + break; + } + iop->io_flag |= IOHERE|IOXHERE; + for (s = h->h_tag; *s; s++) + if (*s & QUOTE) { + iop->io_flag &= ~ IOXHERE; + *s &= ~ QUOTE; + } + h->h_dosub = iop->io_flag & IOXHERE; +} + +void +gethere() +{ + register struct here *h, *hp; + + /* Scan here files first leaving inhere list in place */ + for (hp = h = inhere; h != NULL; hp = h, h = h->h_next) + readhere(&h->h_iop->io_name, h->h_tag, h->h_dosub? 0: '\''); + + /* Make inhere list active - keep list intact for scraphere */ + if (hp != NULL) { + hp->h_next = acthere; + acthere = inhere; + inhere = NULL; + } +} + +static void +readhere(name, s, ec) +char **name; +register char *s; +int ec; +{ + int tf; + char tname[30]; + register c; + jmp_buf ev; + char line [LINELIM+1]; + char *next; + + tempname(tname); + *name = strsave(tname, areanum); + tf = creat(tname, 0600); + if (tf < 0) + return; + if (newenv(setjmp(errpt = ev)) != 0) + unlink(tname); + else { + pushio(e.iop->argp, e.iop->iofn); + e.iobase = e.iop; + for (;;) { + if (talking && e.iop <= iostack) + prs(cprompt->value); + next = line; + while ((c = readc()) != '\n' && c) { + if (next >= &line[LINELIM]) { + c = 0; + break; + } + *next++ = c; + } + *next = 0; + if (strcmp(s, line) == 0 || c == 0) + break; + *next++ = '\n'; + write (tf, line, (int)(next-line)); + } + if (c == 0) { + prs("here document `"); prs(s); err("' unclosed"); + } + quitenv(); + } + close(tf); +} + +/* + * open here temp file. + * if unquoted here, expand here temp file into second temp file. + */ +int +herein(hname, xdoll) +char *hname; +int xdoll; +{ + register hf, tf; + + if (hname == 0) + return(-1); + hf = open(hname, 0); + if (hf < 0) + return (-1); + if (xdoll) { + char c; + char tname[30]; + jmp_buf ev; + + tempname(tname); + if ((tf = creat(tname, 0600)) < 0) + return (-1); + if (newenv(setjmp(errpt = ev)) == 0) { + PUSHIO(afile, hf, herechar); + setbase(e.iop); + while ((c = subgetc(0, 0)) != 0) { + char c1 = c&~QUOTE; + + if (c"E && !any(c1,"`$\\")) + write(tf,"\\",1); + write(tf, &c1, 1); + } + quitenv(); + } else + unlink(tname); + close(tf); + tf = open(tname, 0); + unlink(tname); + return (tf); + } else + return (hf); +} + +void +scraphere() +{ + register struct here *h; + + for (h = inhere; h != NULL; h = h->h_next) { + if (h->h_iop && h->h_iop->io_name) + unlink(h->h_iop->io_name); + } + inhere = NULL; +} + +/* unlink here temp files before a freearea(area) */ +void +freehere(area) +int area; +{ + register struct here *h, *hl; + + hl = NULL; + for (h = acthere; h != NULL; h = h->h_next) + if (getarea((char *) h) >= area) { + if (h->h_iop->io_name != NULL) + unlink(h->h_iop->io_name); + if (hl == NULL) + acthere = h->h_next; + else + hl->h_next = h->h_next; + } else + hl = h; +} + +void +tempname(tname) +char *tname; +{ + static int inc; + register char *cp, *lp; + + for (cp = tname, lp = "/tmp/shtm"; (*cp = *lp++) != '\0'; cp++) + ; + lp = putn(getpid()*1000 + inc++); + for (; (*cp = *lp++) != '\0'; cp++) + ; +} diff --git a/commands/sh/sh6.c b/commands/sh/sh6.c new file mode 100755 index 000000000..802be748d --- /dev/null +++ b/commands/sh/sh6.c @@ -0,0 +1,8 @@ +#define Extern + +#include <sys/types.h> +#include <signal.h> +#include <errno.h> +#include <setjmp.h> +#include "sh.h" + diff --git a/commands/simple/Makefile b/commands/simple/Makefile new file mode 100755 index 000000000..92bb2430a --- /dev/null +++ b/commands/simple/Makefile @@ -0,0 +1,1542 @@ +# Makefile for commands/simple. + +CFLAGS = -D_MINIX -D_POSIX_SOURCE +SYS = ../.. +CCLD = $(CC) -i $(CFLAGS) + +# This Makefile is large, but that is because it lists all actions that must +# be taken to compile and install all the simple commands. If there were only +# one command then it would look like this: +# +# ALL = \ +# cat \ need the 'cat' executable +# +# all: $(ALL) default rule, make all binaries +# +# cat: cat.c 'cat' is made from 'cat.c' +# $(CCLD) -o $@ $? compile 'cat.c' ($?) to 'cat' ($@) +# install -S 4kw $@ stack size is 8k (8086) or 16k (others) +# +# install: \ rule to install all binaries +# /usr/bin/cat \ one can find 'cat' in /usr/bin +# /bin/cat \ important binaries are also in /bin +# +# /usr/bin/cat: cat +# install -cs -o bin $? $@ copy 'cat' to '/usr/bin/cat' (-c), +# strip symbol table (-s) +# +# /bin/cat: /usr/bin/cat +# install -lcs $? $@ install '/bin/cat' by linking (if possible) +# or copying (otherwise) +# +# Some of the binaries are installed under more than one name. The extra +# names are indented by one extra tab in the install rule. +# If you want to add a command then insert it at the appropriate position +# in sorted order. Search around for the command just above or below the +# the new command and add new rules for the new command near those places. +# Observe four key things: +# What to make, how to make, what to install, how to install. + +ALL = \ + add_route \ + at \ + backup \ + badblocks \ + banner \ + basename \ + cal \ + calendar \ + cat \ + cdiff \ + cgrep \ + chmem \ + chmod \ + chown \ + chroot \ + ci \ + cksum \ + cleantmp \ + cmp \ + co \ + comm \ + compress \ + cp \ + crc \ + cut \ + date \ + dd \ + decomp16 \ + df \ + dhrystone \ + diff \ + dirname \ + du \ + ed \ + eject \ + env \ + expand \ + factor \ + fgrep \ + file \ + find \ + finger \ + fix \ + fold \ + fortune \ + fsck \ + fsck1 \ + getty \ + gomoku \ + grep \ + head \ + host \ + hostaddr \ + id \ + ifconfig \ + ifdef \ + in.fingerd \ + in.rshd \ + installx \ + intr \ + irdpd \ + isoread \ + join \ + kill \ + last \ + leave \ + life \ + login \ + look \ + lp \ + lpd \ + ls \ + mail \ + man \ + mesg \ + mkdir \ + mkfifo \ + mkfs \ + mknod \ + mkproto \ + mkswap \ + modem \ + mount \ + mt \ + nm \ + nonamed \ + od \ + passwd \ + paste \ + ping \ + pr \ + pr_routes \ + prep \ + printf \ + printroot \ + proto \ + pwd \ + pwdauth \ + rarpd \ + rcp \ + rdate \ + readall \ + readfs \ + remsync \ + rget \ + rlogin \ + rmdir \ + rsh \ + sed \ + shar \ + size \ + sleep \ + slip \ + sort \ + split \ + stat \ + strings \ + strip \ + stty \ + su \ + sum \ + swapfs \ + sync \ + synctree \ + sysenv \ + tail \ + tar \ + tcpd \ + tcpdp \ + tee \ + term \ + termcap \ + tget \ + time \ + touch \ + tr \ + tsort \ + ttt \ + tty \ + umount \ + uname \ + unexpand \ + uniq \ + update \ + uud \ + uue \ + vol \ + wc \ + which \ + who \ + whoami \ + write \ + writeisofs \ + xargs \ + yes \ + # + +all: $(ALL) + +add_route: add_route.c + $(CCLD) -o $@ add_route.c + install -S 4kw $@ + +at: at.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +backup: backup.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +badblocks: badblocks.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +banner: banner.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +basename: basename.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +cal: cal.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +calendar: calendar.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +cat: cat.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +cdiff: cdiff.c + $(CCLD) -o $@ $? + install -S 28kw $@ + +cgrep: cgrep.c + $(CCLD) -o $@ $? + install -S 5kw $@ + +chmem: chmem.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +chmod: chmod.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +chown: chown.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +chroot: chroot.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +ci: ci.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +cksum: cksum.c + $(CCLD) -o $@ $? + install -S 8kw $@ + +cleantmp: cleantmp.c + $(CCLD) -o $@ $? + install -S 8kw $@ + +cmp: cmp.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +co: co.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +comm: comm.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +compress: compress.c + $(CCLD) -o $@ $? + install -S 450k $@ + +cp: cp.c + $(CCLD) -o $@ $? + install -S 16kw $@ + +crc: crc.c + $(CCLD) -o $@ $? + install -S 8kw $@ + +cut: cut.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +date: date.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +dd: dd.c + $(CCLD) -o $@ $? + install -S 20kw $@ + +decomp16: decomp16.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +df: df.c + $(CCLD) -I$(SYS) -o $@ $? + install -S 4kw $@ + +dhrystone: dhrystone.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +diff: diff.c + $(CCLD) -o $@ $? + install -S 40kw $@ + +dirname: dirname.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +du: du.c + $(CCLD) -o $@ $? + install -S 16kw $@ + +ed: ed.c + $(CCLD) -o $@ $? + install -S 32kw $@ + +eject: eject.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +env: env.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +expand: expand.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +factor: factor.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +fgrep: fgrep.c + $(CCLD) -o $@ $? + install -S 10kw $@ + +file: file.c + $(CCLD) -o $@ $? + install -S 25kw $@ + +find: find.c + $(CCLD) -o $@ $? + install -S 25kw $@ + +finger: finger.c + $(CCLD) -o $@ finger.c + install -S 8kw $@ + +fix: fix.c + $(CCLD) -o $@ fix.c + install -S 32kw $@ + +fold: fold.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +fortune: fortune.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +fsck: fsck.c + $(CCLD) -o $@ $? + install -S 1024k $@ + +fsck1: fsck1.c + $(CCLD) -o $@ $? + install -S 32kw $@ + +getty: getty.c /usr/include/minix/config.h + $(CCLD) -o $@ getty.c + install -S 4kw $@ + +gomoku: gomoku.c + $(CCLD) -o $@ $? -lcurses + install -S 8kw $@ + +grep: grep.c + $(CCLD) -o $@ $? + install -S 32kw $@ + +head: head.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +host: host.c + $(CCLD) -wo -o $@ host.c + install -S 4kw $@ + +hostaddr: hostaddr.c + $(CCLD) -o $@ hostaddr.c + install -S 8kw $@ + +id: id.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +ifconfig: ifconfig.c + $(CCLD) -o $@ ifconfig.c + install -S 4kw $@ + +ifdef: ifdef.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +in.fingerd: in.fingerd.c + $(CCLD) -o $@ in.fingerd.c + install -S 4kw $@ + +in.rshd: in.rshd.c + $(CCLD) -o $@ in.rshd.c + install -S 4kw $@ + +installx: install.c # Note: avoided confict with 'install' rule. + $(CCLD) -o $@ $? + install -S 4kw $@ + +intr: intr.c + $(CCLD) -o $@ intr.c + install -S 4kw $@ + +irdpd: irdpd.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +isoread: isoread.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +join: join.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +kill: kill.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +last: last.c + $(CCLD) -o $@ $? + install -S 5kw $@ + +leave: leave.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +life: life.c + $(CCLD) -o $@ $? -lcurses + install -S 15kw $@ + +login: login.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +look: look.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +lp: lp.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +lpd: lpd.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +ls: ls.c + $(CCLD) -o $@ $? + install -S 20kw $@ + +mail: mail.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +man: man.c + $(CCLD) -o $@ $? + install -S 10kw $@ + +mesg: mesg.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +mkdir: mkdir.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +mkfifo: mkfifo.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +mkfs: mkfs.c + $(CCLD) -o $@ $? + install -S 20kw $@ + +mknod: mknod.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +mkproto: mkproto.c + $(CCLD) -o $@ $? + install -S 20kw $@ + +mkswap: mkswap.c + $(CCLD) -I$(SYS) -o $@ $? + install -S 4kw $@ + +modem: modem.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +mount: mount.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +mt: mt.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +nm: nm.c + $(CCLD) -o $@ $? + install -S 32kw $@ + +nonamed: nonamed.c + $(CCLD) -o $@ $? + install -S 8kw $@ + +od: od.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +passwd: passwd.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +paste: paste.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +ping: ping.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +pr: pr.c + $(CCLD) -o $@ $? + install -S 16kw $@ + +pr_routes: pr_routes.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +prep: prep.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +printf: printf.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +printroot: printroot.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +proto: proto.c + $(CCLD) -o $@ $? + install -S 15kw $@ + +pwd: pwd.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +pwdauth: pwdauth.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +rarpd: rarpd.c + $(CCLD) -o $@ rarpd.c + install -S 4kw $@ + +rcp: rcp.c + $(CCLD) -o $@ rcp.c + install -S 8kw $@ + +rdate: rdate.c + $(CCLD) -o $@ rdate.c + install -S 8kw $@ + +readall: readall.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +readfs: readfs.c + $(CCLD) -o $@ $? + install -S 25kw $@ + +remsync: remsync.c + $(CCLD) -o $@ $? + install -S 256k $@ + +rget: rget.c + $(CCLD) -o $@ $? + install -S 8kw $@ + +rlogin: rlogin.c + $(CCLD) -o $@ $? + install -S 8kw $@ + +rmdir: rmdir.c + $(CCLD) -o $@ $? + install -S 15kw $@ + +rsh: rsh.c + $(CCLD) -o $@ rsh.c + install -S 8kw $@ + +sed: sed.c + $(CCLD) -o $@ $? + install -S 8kw $@ + +shar: shar.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +size: size.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +sleep: sleep.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +slip: slip.c + $(CCLD) -o $@ $? + install -S 20k $@ + +sort: sort.c + $(CCLD) -o $@ $? + install -S 30kw $@ + +split: split.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +stat: stat.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +strings: strings.c + $(CCLD) -o $@ $? + install -S 8kw $@ + +strip: strip.c + $(CCLD) -o $@ $? + install -S 8kw $@ + +stty: stty.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +su: su.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +sum: sum.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +swapfs: swapfs.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +sync: sync.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +synctree: synctree.c + $(CCLD) -o $@ -wo $? + install -S 32kw $@ + +sysenv: sysenv.c + $(CCLD) -o $@ -wo $? + install -S 4kw $@ + +tail: tail.c + $(CCLD) -o $@ $? + install -S 16kw $@ + +tar: tar.c + $(CCLD) -o $@ $? + install -S 30kw $@ + +tcpd: tcpd.c + $(CCLD) -o $@ -DPARANOID=0 tcpd.c + install -S 4kw $@ + +tcpdp: tcpd.c + $(CCLD) -o $@ -DPARANOID=1 tcpd.c + install -S 8kw $@ + +tee: tee.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +term: term.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +termcap: termcap.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +tget: tget.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +time: time.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +touch: touch.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +tr: tr.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +tsort: tsort.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +ttt: ttt.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +tty: tty.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +umount: umount.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +uname: uname.c /usr/include/minix/config.h + $(CCLD) -o $@ uname.c + install -S 4kw $@ + +unexpand: unexpand.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +uniq: uniq.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +update: update.c + $(CCLD) -o $@ $? + install -S 1kw $@ + +uud: uud.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +uue: uue.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +vol: vol.c + $(CCLD) -o $@ $? + install -S 80k $@ # note: '-S' is upper limit to 'vol -m' + +wc: wc.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +which: which.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +who: who.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +whoami: whoami.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +write: write.c + $(CCLD) -o $@ $? + +writeisofs: writeisofs.c + $(CCLD) -o $@ $? + +xargs: xargs.c + $(CCLD) -o $@ $? + install -S 16kw $@ + +yes: yes.c + $(CCLD) -o $@ $? + install -S 4kw $@ + +install: \ + /usr/bin/add_route \ + /usr/bin/del_route \ + /usr/bin/at \ + /usr/bin/backup \ + /usr/bin/restore \ + /usr/bin/badblocks \ + /usr/bin/banner \ + /usr/bin/basename \ + /usr/bin/cal \ + /usr/bin/calendar \ + /usr/bin/cat \ + /usr/bin/cdiff \ + /usr/bin/cgrep \ + /usr/bin/chmem \ + /usr/bin/chmod \ + /usr/bin/chown \ + /usr/bin/chroot \ + /usr/bin/chgrp \ + /usr/bin/ci \ + /usr/bin/cksum \ + /usr/bin/cleantmp \ + /usr/bin/cmp \ + /usr/bin/co \ + /usr/bin/comm \ + /usr/bin/compress \ + /usr/bin/uncompress \ + /usr/bin/zcat \ + /usr/bin/cp \ + /usr/bin/clone \ + /usr/bin/cpdir \ + /usr/bin/ln \ + /usr/bin/mv \ + /usr/bin/rm \ + /usr/bin/crc \ + /usr/bin/cut \ + /usr/bin/date \ + /usr/bin/dd \ + /usr/bin/decomp16 \ + /usr/bin/df \ + /usr/bin/dhrystone \ + /usr/bin/diff \ + /usr/bin/dirname \ + /usr/bin/du \ + /usr/bin/ed \ + /usr/bin/eject \ + /usr/bin/env \ + /usr/bin/expand \ + /usr/bin/factor \ + /usr/bin/fgrep \ + /usr/bin/file \ + /usr/bin/find \ + /usr/bin/finger \ + /usr/bin/fix \ + /usr/bin/fold \ + /usr/bin/fortune \ + /usr/bin/fsck \ + /usr/bin/fsck1 \ + /usr/bin/getty \ + /usr/bin/gomoku \ + /usr/bin/grep \ + /usr/bin/egrep \ + /usr/bin/head \ + /usr/bin/host \ + /usr/bin/hostaddr \ + /usr/bin/id \ + /usr/bin/ifconfig \ + /usr/bin/ifdef \ + /usr/bin/in.fingerd \ + /usr/bin/in.rshd \ + /usr/bin/install \ + /usr/bin/intr \ + /usr/bin/irdpd \ + /usr/bin/isoread \ + /usr/bin/isodir \ + /usr/bin/isoinfo \ + /usr/bin/join \ + /usr/bin/kill \ + /usr/bin/last \ + /usr/bin/uptime \ + /usr/bin/leave \ + /usr/bin/life \ + /usr/bin/login \ + /usr/bin/look \ + /usr/bin/lp \ + /usr/bin/lpd \ + /usr/bin/ls \ + /usr/bin/mail \ + /usr/bin/man \ + /usr/bin/mesg \ + /usr/bin/mkdir \ + /usr/bin/mkfifo \ + /usr/bin/mkfs \ + /usr/bin/mknod \ + /usr/bin/mkproto \ + /usr/bin/mkswap \ + /usr/bin/modem \ + /usr/bin/mount \ + /usr/bin/mt \ + /usr/bin/nm \ + /usr/bin/nonamed \ + /usr/bin/od \ + /usr/bin/passwd \ + /usr/bin/chfn \ + /usr/bin/chsh \ + /usr/bin/paste \ + /usr/bin/ping \ + /usr/bin/pr \ + /usr/bin/pr_routes \ + /usr/bin/prep \ + /usr/bin/printf \ + /usr/bin/printroot \ + /usr/bin/proto \ + /usr/bin/pwd \ + /usr/lib/pwdauth \ + /usr/bin/rarpd \ + /usr/bin/rcp \ + /usr/bin/rdate \ + /usr/bin/readall \ + /usr/bin/readfs \ + /usr/bin/remsync \ + /usr/bin/rget \ + /usr/bin/rput \ + /usr/bin/rlogin \ + /usr/bin/rmdir \ + /usr/bin/rsh \ + /usr/bin/sed \ + /usr/bin/shar \ + /usr/bin/size \ + /usr/bin/sleep \ + /usr/bin/slip \ + /usr/bin/sort \ + /usr/bin/split \ + /usr/bin/stat \ + /usr/bin/fstat \ + /usr/bin/strings \ + /usr/bin/strip \ + /usr/bin/stty \ + /usr/bin/su \ + /usr/bin/sum \ + /usr/bin/swapfs \ + /usr/bin/sync \ + /usr/bin/synctree \ + /usr/bin/sysenv \ + /usr/bin/tail \ + /usr/bin/tar \ + /usr/bin/tcpd \ + /usr/bin/tcpdp \ + /usr/bin/tee \ + /usr/bin/term \ + /usr/bin/termcap \ + /usr/bin/tget \ + /usr/bin/time \ + /usr/bin/touch \ + /usr/bin/tr \ + /usr/bin/tsort \ + /usr/bin/ttt \ + /usr/bin/tty \ + /usr/bin/umount \ + /usr/bin/uname \ + /usr/bin/arch \ + /usr/bin/unexpand \ + /usr/bin/uniq \ + /usr/bin/update \ + /usr/bin/uud \ + /usr/bin/uudecode \ + /usr/bin/uue \ + /usr/bin/uuencode \ + /usr/bin/vol \ + /usr/bin/wc \ + /usr/bin/which \ + /usr/bin/who \ + /usr/bin/whoami \ + /usr/bin/write \ + /usr/bin/writeisofs \ + /usr/bin/xargs \ + /usr/bin/yes \ + /bin/cat \ + /bin/date \ + /bin/fsck \ + /bin/intr \ + /bin/mount \ + /bin/printroot \ + /bin/pwd \ + /bin/sync \ + /bin/umount \ + # + +/usr/bin/add_route: add_route + install -cs -o bin $? $@ + +/usr/bin/del_route: /usr/bin/add_route + install -l $? $@ + +/usr/bin/at: at + install -cs -o root -m 4755 $? $@ + +/usr/bin/backup: backup + install -cs -o bin $? $@ + +/usr/bin/restore: /usr/bin/backup + install -l $? $@ + +/usr/bin/badblocks: badblocks + install -cs -o bin $? $@ + +/usr/bin/banner: banner + install -cs -o bin $? $@ + +/usr/bin/basename: basename + install -cs -o bin $? $@ + +/usr/bin/cal: cal + install -cs -o bin $? $@ + +/usr/bin/calendar: calendar + install -cs -o bin $? $@ + +/usr/bin/cat: cat + install -cs -o bin $? $@ + +/usr/bin/cdiff: cdiff + install -cs -o bin $? $@ + +/usr/bin/cgrep: cgrep + install -cs -o bin $? $@ + +/usr/bin/chmem: chmem + install -cs -o bin $? $@ + +/usr/bin/chmod: chmod + install -cs -o bin $? $@ + +/usr/bin/chown: chown + install -cs -o bin $? $@ + +/usr/bin/chroot: chroot + install -cs -o bin $? $@ + +/usr/bin/chgrp: /usr/bin/chown + install -l $? $@ + +/usr/bin/ci: ci + install -cs -o bin $? $@ + +/usr/bin/cksum: cksum + install -cs -o bin $? $@ + +/usr/bin/cleantmp: cleantmp + install -cs -o bin $? $@ + +/usr/bin/cmp: cmp + install -cs -o bin $? $@ + +/usr/bin/co: co + install -cs -o bin $? $@ + +/usr/bin/comm: comm + install -cs -o bin $? $@ + +/usr/bin/compress: compress + install -cs -o bin $? $@ + +/usr/bin/uncompress /usr/bin/zcat: /usr/bin/compress + install -l $? $@ + +/usr/bin/cp: cp + install -cs -o bin $? $@ + +/usr/bin/clone /usr/bin/cpdir \ +/usr/bin/ln /usr/bin/mv /usr/bin/rm: /usr/bin/cp + install -l $? $@ + +/usr/bin/crc: crc + install -cs -o bin $? $@ + +/usr/bin/cut: cut + install -cs -o bin $? $@ + +/usr/bin/date: date + install -cs -o bin $? $@ + +/usr/bin/dd: dd + install -cs -o bin $? $@ + +/usr/bin/decomp16: decomp16 + install -cs -o bin $? $@ + +/usr/bin/df: df + install -cs -o root -m 4755 $? $@ + +/usr/bin/dhrystone: dhrystone + install -cs -o bin $? $@ + +/usr/bin/diff: diff + install -cs -o bin $? $@ + +/usr/bin/dirname: dirname + install -cs -o bin $? $@ + +/usr/bin/du: du + install -cs -o bin $? $@ + +/usr/bin/ed: ed + install -cs -o bin $? $@ + +/usr/bin/eject: eject + install -cs -o bin $? $@ + +/usr/bin/env: env + install -cs -o bin $? $@ + +/usr/bin/expand: expand + install -cs -o bin $? $@ + +/usr/bin/factor: factor + install -cs -o bin $? $@ + +/usr/bin/fgrep: fgrep + install -cs -o bin $? $@ + +/usr/bin/file: file + install -cs -o bin $? $@ + +/usr/bin/find: find + install -cs -o bin $? $@ + +/usr/bin/finger: finger + install -cs -o bin $? $@ + +/usr/bin/fix: fix + install -cs -o bin $? $@ + +/usr/bin/fold: fold + install -cs -o bin $? $@ + +/usr/bin/fortune: fortune + install -cs -o bin $? $@ + +/usr/bin/fsck: fsck + install -cs -o bin $? $@ + +/usr/bin/fsck1: fsck1 + install -cs -o bin $? $@ + +/usr/bin/getty: getty + install -cs -o bin $? $@ + +/usr/bin/gomoku: gomoku + install -cs -o bin $? $@ + +/usr/bin/grep: grep + install -cs -o bin $? $@ + +/usr/bin/egrep: /usr/bin/grep + install -l $? $@ + +/usr/bin/head: head + install -cs -o bin $? $@ + +/usr/bin/host: host + install -cs -o bin $? $@ + +/usr/bin/hostaddr: hostaddr + install -cs -o root -m 4755 $? $@ + +/usr/bin/id: id + install -cs -o bin $? $@ + +/usr/bin/ifconfig: ifconfig + install -cs -o root -m 4755 $? $@ + +/usr/bin/ifdef: ifdef + install -cs -o bin $? $@ + +/usr/bin/in.fingerd: in.fingerd + install -cs -o bin $? $@ + +/usr/bin/in.rshd: in.rshd + install -cs -o bin $? $@ + +/usr/bin/install: installx + install -cs -o root -m 4755 $? $@ + +/usr/bin/intr: intr + install -cs -o bin $? $@ + +/usr/bin/irdpd: irdpd + install -cs -o bin $? $@ + +/usr/bin/isoread: isoread + install -cs -o bin $? $@ + +/usr/bin/isodir /usr/bin/isoinfo: /usr/bin/isoread + install -l $? $@ + +/usr/bin/join: join + install -cs -o bin $? $@ + +/usr/bin/kill: kill + install -cs -o bin $? $@ + +/usr/bin/last: last + install -cs -o bin $? $@ + +/usr/bin/uptime: /usr/bin/last + install -l $? $@ + +/usr/bin/leave: leave + install -cs -o bin $? $@ + +/usr/bin/life: life + install -cs -o bin $? $@ + +/usr/bin/login: login + install -cs -o bin $? $@ + +/usr/bin/look: look + install -cs -o bin $? $@ + +/usr/bin/lp: lp + install -cs -o bin $? $@ + +/usr/bin/lpd: lpd + install -cs -o daemon -m 4755 $? $@ + +/usr/bin/ls: ls + install -cs -o bin $? $@ + +/usr/bin/mail: mail + install -cs -o root -m 4755 $? $@ + +/usr/bin/man: man + install -cs -o bin $? $@ + +/usr/bin/mesg: mesg + install -cs -o bin $? $@ + +/usr/bin/mkdir: mkdir + install -cs -o bin $? $@ + +/usr/bin/mkfifo: mkfifo + install -cs -o bin $? $@ + +/usr/bin/mkfs: mkfs + install -cs -o bin $? $@ + +/usr/bin/mknod: mknod + install -cs -o bin $? $@ + +/usr/bin/mkproto: mkproto + install -cs -o bin $? $@ + +/usr/bin/mkswap: mkswap + install -cs -o bin $? $@ + +/usr/bin/modem: modem + install -cs -o bin $? $@ + +/usr/bin/mount: mount + install -cs -o root -m 4755 $? $@ + +/usr/bin/mt: mt + install -cs -o bin $? $@ + +/usr/bin/nm: nm + install -cs -o bin $? $@ + +/usr/bin/nonamed: nonamed + install -cs -o bin $? $@ + +/usr/bin/od: od + install -cs -o bin $? $@ + +/usr/bin/passwd: passwd + install -cs -o root -m 4755 $? $@ + +/usr/bin/chfn /usr/bin/chsh: /usr/bin/passwd + install -l $? $@ + +/usr/bin/paste: paste + install -cs -o bin $? $@ + +/usr/bin/ping: ping + install -cs -o root -m 4755 $? $@ + +/usr/bin/pr: pr + install -cs -o bin $? $@ + +/usr/bin/pr_routes: pr_routes + install -cs -o root -m 4755 $? $@ + +/usr/bin/prep: prep + install -cs -o bin $? $@ + +/usr/bin/printf: printf + install -cs -o bin $? $@ + +/usr/bin/printroot: printroot + install -cs -o bin $? $@ + +/usr/bin/proto: proto + install -cs -o bin $? $@ + +/usr/bin/pwd: pwd + install -cs -o bin $? $@ + +/usr/lib/pwdauth: pwdauth + install -cs -o root -m 4755 $? $@ + +/usr/bin/rarpd: rarpd + install -cs -o bin $? $@ + +/usr/bin/rcp: rcp + install -cs -o bin $? $@ + +/usr/bin/rdate: rdate + install -cs -o bin $? $@ + +/usr/bin/readall: readall + install -cs -o bin $? $@ + +/usr/bin/readfs: readfs + install -cs -o bin $? $@ + +/usr/bin/remsync: remsync + install -cs -o bin $? $@ + +/usr/bin/rget: rget + install -cs -o bin $? $@ + +/usr/bin/rput: /usr/bin/rget + install -l $? $@ + +/usr/bin/rlogin: rlogin + install -cs -o bin $? $@ + +/usr/bin/rmdir: rmdir + install -cs -o bin $? $@ + +/usr/bin/rsh: rsh + install -cs -o bin $? $@ + +/usr/bin/sed: sed + install -cs -o bin $? $@ + +/usr/bin/shar: shar + install -cs -o bin $? $@ + +/usr/bin/size: size + install -cs -o bin $? $@ + +/usr/bin/sleep: sleep + install -cs -o bin $? $@ + +/usr/bin/slip: slip + install -cs -o bin $? $@ + +/usr/bin/sort: sort + install -cs -o bin $? $@ + +/usr/bin/split: split + install -cs -o bin $? $@ + +/usr/bin/stat: stat + install -cs -o bin $? $@ + +/usr/bin/fstat: /usr/bin/stat + install -l $? $@ + +/usr/bin/strings: strings + install -cs -o bin $? $@ + +/usr/bin/strip: strip + install -cs -o bin $? $@ + +/usr/bin/stty: stty + install -cs -o bin $? $@ + +/usr/bin/su: su + install -cs -o root -m 4755 $? $@ + +/usr/bin/sum: sum + install -cs -o bin $? $@ + +/usr/bin/swapfs: swapfs + install -cs -o bin $? $@ + +/usr/bin/sync: sync + install -cs -o bin $? $@ + +/usr/bin/synctree: synctree + install -cs -o bin $? $@ + +/usr/bin/sysenv: sysenv + install -cs -o bin $? $@ + +/usr/bin/tail: tail + install -cs -o bin $? $@ + +/usr/bin/tar: tar + install -cs -o bin $? $@ + +/usr/bin/tcpd: tcpd + install -cs -o bin $? $@ + +/usr/bin/tcpdp: tcpdp + install -cs -o bin $? $@ + +/usr/bin/tee: tee + install -cs -o bin $? $@ + +/usr/bin/term: term + install -cs -o bin -g uucp -m 2755 $? $@ + +/usr/bin/termcap: termcap + install -cs -o bin $? $@ + +/usr/bin/tget: tget + install -cs -o bin $? $@ + +/usr/bin/time: time + install -cs -o bin $? $@ + +/usr/bin/touch: touch + install -cs -o bin $? $@ + +/usr/bin/tr: tr + install -cs -o bin $? $@ + +/usr/bin/tsort: tsort + install -cs -o bin $? $@ + +/usr/bin/ttt: ttt + install -cs -o bin $? $@ + +/usr/bin/tty: tty + install -cs -o bin $? $@ + +/usr/bin/umount: umount + install -cs -o root -m 4755 $? $@ + +/usr/bin/uname: uname + install -cs -o bin $? $@ + +/usr/bin/arch: /usr/bin/uname + install -l $? $@ + +/usr/bin/unexpand: unexpand + install -cs -o bin $? $@ + +/usr/bin/uniq: uniq + install -cs -o bin $? $@ + +/usr/bin/update: update + install -cs -o bin $? $@ + +/usr/bin/uud: uud + install -cs -o bin $? $@ + +/usr/bin/uudecode: /usr/bin/uud + install -l $? $@ + +/usr/bin/uue: uue + install -cs -o bin $? $@ + +/usr/bin/uuencode: /usr/bin/uue + install -l $? $@ + +/usr/bin/vol: vol + install -cs -o bin $? $@ + +/usr/bin/wc: wc + install -cs -o bin $? $@ + +/usr/bin/which: which + install -cs -o bin $? $@ + +/usr/bin/who: who + install -cs -o bin $? $@ + +/usr/bin/whoami: whoami + install -cs -o bin $? $@ + +/usr/bin/write: write + install -cs -o bin -g tty -m 2755 $? $@ + +/usr/bin/writeisofs: writeisofs + install -cs -S 2M -o bin $? $@ + +/usr/bin/xargs: xargs + install -cs -o bin $? $@ + +/usr/bin/yes: yes + install -cs -o bin $? $@ + +/bin/cat: /usr/bin/cat + install -lcs $? $@ + +/bin/date: /usr/bin/date + install -lcs $? $@ + +/bin/fsck: /usr/bin/fsck + install -lcs $? $@ + +/bin/intr: /usr/bin/intr + install -lcs $? $@ + +/bin/mount: /usr/bin/mount + install -lcs $? $@ + +/bin/printroot: /usr/bin/printroot + install -lcs $? $@ + +/bin/pwd: /usr/bin/pwd + install -lcs $? $@ + +/bin/sync: /usr/bin/sync + install -lcs $? $@ + +/bin/umount: /usr/bin/umount + install -lcs $? $@ + +clean: + rm -rf $(ALL) a.out core diff --git a/commands/simple/add_route.c b/commands/simple/add_route.c new file mode 100755 index 000000000..cab31631d --- /dev/null +++ b/commands/simple/add_route.c @@ -0,0 +1,347 @@ +/* +add_route.c + +Created August 7, 1991 by Philip Homburg +*/ + +#define _POSIX_C_SOURCE 2 + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <net/hton.h> +#include <net/netlib.h> +#include <net/gen/netdb.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/route.h> +#include <net/gen/socket.h> +#include <net/gen/ip_io.h> + +static char *prog_name; +static enum { ADD, DEL } action; + +static void usage(void); +static int name_to_ip(char *name, ipaddr_t *addr); +static int parse_cidr(char *cidr, ipaddr_t *addr, ipaddr_t *mask); + +void main(int argc, char *argv[]) +{ + struct netent *netent; + ipaddr_t gateway, destination, netmask, defaultmask=0; + u8_t high_byte; + nwio_route_t route; + int ip_fd, itab; + int r; + int metric; + char *check; + char *ip_device; + char *netmask_str, *metric_str, *destination_str, *gateway_str; + int c; + char *d_arg, *g_arg, *m_arg, *n_arg, *I_arg; + int i_flag, o_flag, D_flag, v_flag; + int cidr; + + prog_name= strrchr(argv[0], '/'); + if (prog_name == NULL) prog_name= argv[0]; else prog_name++; + + if (strcmp(prog_name, "add_route") == 0) + action= ADD; + else if (strcmp(prog_name, "del_route") == 0) + action= DEL; + else + { + fprintf(stderr, "Don't know what to do when named '%s'\n", + prog_name); + exit(1); + } + + i_flag= 0; + o_flag= 0; + D_flag= 0; + v_flag= 0; + g_arg= NULL; + d_arg= NULL; + m_arg= NULL; + n_arg= NULL; + I_arg= NULL; + while ((c= getopt(argc, argv, "iovDg:d:m:n:I:?")) != -1) + { + switch(c) + { + case 'i': + if (i_flag) + usage(); + i_flag= 1; + break; + case 'o': + if (o_flag) + usage(); + o_flag= 1; + break; + case 'v': + if (v_flag) + usage(); + v_flag= 1; + break; + case 'D': + if (D_flag) + usage(); + D_flag= 1; + break; + case 'g': + if (g_arg) + usage(); + g_arg= optarg; + break; + case 'd': + if (d_arg) + usage(); + d_arg= optarg; + break; + case 'm': + if (m_arg) + usage(); + m_arg= optarg; + break; + case 'n': + if (n_arg) + usage(); + n_arg= optarg; + break; + case 'I': + if (I_arg) + usage(); + I_arg= optarg; + break; + case '?': + usage(); + default: + fprintf(stderr, "%s: getopt failed\n", prog_name); + exit(1); + } + } + if (optind != argc) + usage(); + if (i_flag && o_flag) + usage(); + itab= i_flag; + + if (i_flag) + { + if (g_arg == NULL || d_arg == NULL || m_arg == NULL) + usage(); + } + else + { + if (g_arg == NULL || (d_arg == NULL && n_arg != NULL)) + { + usage(); + } + } + + gateway_str= g_arg; + destination_str= d_arg; + metric_str= m_arg; + netmask_str= n_arg; + ip_device= I_arg; + + if (!name_to_ip(gateway_str, &gateway)) + { + fprintf(stderr, "%s: unknown host '%s'\n", prog_name, + gateway_str); + exit(1); + } + + destination= 0; + netmask= 0; + cidr= 0; + + if (destination_str) + { + if (parse_cidr(destination_str, &destination, &netmask)) + cidr= 1; + else if (inet_aton(destination_str, &destination)) + ; + else if ((netent= getnetbyname(destination_str)) != NULL) + destination= netent->n_net; + else if (!name_to_ip(destination_str, &destination)) + { + fprintf(stderr, "%s: unknown network/host '%s'\n", + prog_name, destination_str); + exit(1); + } + high_byte= *(u8_t *)&destination; + if (!(high_byte & 0x80)) /* class A or 0 */ + { + if (destination) + defaultmask= HTONL(0xff000000); + } + else if (!(high_byte & 0x40)) /* class B */ + { + defaultmask= HTONL(0xffff0000); + } + else if (!(high_byte & 0x20)) /* class C */ + { + defaultmask= HTONL(0xffffff00); + } + else /* class D is multicast ... */ + { + fprintf(stderr, "%s: Warning: Martian address '%s'\n", + prog_name, inet_ntoa(destination)); + defaultmask= HTONL(0xffffffff); + } + if (destination & ~defaultmask) + { + /* host route */ + defaultmask= HTONL(0xffffffff); + } + if (!cidr) + netmask= defaultmask; + } + + if (netmask_str) + { + if (cidr) + usage(); + if (inet_aton(netmask_str, &netmask) == 0) + { + fprintf(stderr, "%s: illegal netmask'%s'\n", prog_name, + netmask_str); + exit(1); + } + } + + if (metric_str) + { + metric= strtol(metric_str, &check, 0); + if (check[0] != '\0' || metric < 1) + { + fprintf(stderr, "%s: illegal metric %s\n", + prog_name, metric_str); + } + } + else + metric= 1; + + if (!ip_device) + ip_device= getenv("IP_DEVICE"); + if (!ip_device) + ip_device= IP_DEVICE; + + ip_fd= open(ip_device, O_RDWR); + if (ip_fd == -1) + { + fprintf(stderr, "%s: unable to open('%s'): %s\n", + prog_name, ip_device, strerror(errno)); + exit(1); + } + + if (v_flag) + { + printf("%s %s route to %s ", + action == ADD ? "adding" : "deleting", + itab ? "input" : "output", + inet_ntoa(destination)); + printf("with netmask %s ", inet_ntoa(netmask)); + printf("using gateway %s", inet_ntoa(gateway)); + if (itab && action == ADD) + printf(" at distance %d", metric); + printf("\n"); + } + + route.nwr_ent_no= 0; + route.nwr_dest= destination; + route.nwr_netmask= netmask; + route.nwr_gateway= gateway; + route.nwr_dist= action == ADD ? metric : 0; + route.nwr_flags= (action == DEL && D_flag) ? 0 : NWRF_STATIC; + route.nwr_pref= 0; + route.nwr_mtu= 0; + + if (action == ADD) + r= ioctl(ip_fd, itab ? NWIOSIPIROUTE : NWIOSIPOROUTE, &route); + else + r= ioctl(ip_fd, itab ? NWIODIPIROUTE : NWIODIPOROUTE, &route); + if (r == -1) + { + fprintf(stderr, "%s: NWIO%cIP%cROUTE: %s\n", + prog_name, + action == ADD ? 'S' : 'D', + itab ? 'I' : 'O', + strerror(errno)); + exit(1); + } + exit(0); +} + +static void usage(void) +{ + fprintf(stderr, + "Usage: %s\n" + "\t[-o] %s-g gw [-d dst [-n netmask]] %s[-I ipdev] [-v]\n" + "\t-i %s-g gw -d dst [-n netmask] %s[-I ipdev] [-v]\n" + "Note: <dst> may be in CIDR notation\n", + prog_name, + action == DEL ? "[-D] " : "", + action == ADD ? "[-m metric] " : "", + action == DEL ? "[-D] " : "", + action == ADD ? "-m metric " : "" + ); + exit(1); +} + +static int name_to_ip(char *name, ipaddr_t *addr) +{ + /* Translate a name to an IP address. Try first with inet_aton(), then + * with gethostbyname(). (The latter can also recognize an IP address, + * but only decimals with at least one dot).) + */ + struct hostent *hostent; + + if (!inet_aton(name, addr)) { + if ((hostent= gethostbyname(name)) == NULL) return 0; + if (hostent->h_addrtype != AF_INET) return 0; + if (hostent->h_length != sizeof(*addr)) return 0; + memcpy(addr, hostent->h_addr, sizeof(*addr)); + } + return 1; +} + +static int parse_cidr(char *cidr, ipaddr_t *addr, ipaddr_t *mask) +{ + char *slash, *check; + ipaddr_t a; + int ok; + unsigned long len; + + if ((slash= strchr(cidr, '/')) == NULL) + return 0; + + *slash++= 0; + ok= 1; + + if (!inet_aton(cidr, &a)) + ok= 0; + + len= strtoul(slash, &check, 10); + if (check == slash || *check != 0 || len > 32) + ok= 0; + + *--slash= '/'; + if (!ok) + return 0; + *addr= a; + *mask= htonl(len == 0 ? 0 : (0xFFFFFFFF << (32-len)) & 0xFFFFFFFF); + return 1; +} + +/* + * $PchId: add_route.c,v 1.6 2001/04/20 10:45:07 philip Exp $ + */ diff --git a/commands/simple/at.c b/commands/simple/at.c new file mode 100755 index 000000000..d9fb88b21 --- /dev/null +++ b/commands/simple/at.c @@ -0,0 +1,223 @@ +/* at - run a command at a specified time Author: Jan Looyen */ + +#include <sys/types.h> +#include <time.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <stdio.h> +#include <limits.h> +#include <signal.h> +#include <errno.h> + +#define STARTDAY 0 /* see ctime(3) */ +#define LEAPDAY STARTDAY+59 +#define MAXDAYNR STARTDAY+365 +#define NODAY -2 +char CRONPID[] = "/usr/run/cron.pid"; + +_PROTOTYPE(int main, (int argc, char **argv, char **envp)); +_PROTOTYPE(int getltim, (char *t)); +_PROTOTYPE(int getlday, (char *m, char *d)); +_PROTOTYPE(int digitstring, (char *s)); + +int main(argc, argv, envp) +int argc; +char **argv, **envp; +{ + int i, c, mask, ltim, year, lday = NODAY; + char buf[64], job[30], pastjob[35], *dp, *sp; + struct tm *p; + long clk; + FILE *fp; + char pwd[PATH_MAX+1]; + +/*-------------------------------------------------------------------------* + * check arguments & pipe to "pwd" * + *-------------------------------------------------------------------------*/ + if (argc < 2 || argc > 5) { + fprintf(stderr, "Usage: %s time [month day] [file]\n", argv[0]); + exit(1); + } + if ((ltim = getltim(argv[1])) == -1) { + fprintf(stderr, "%s: wrong time specification\n", argv[0]); + exit(1); + } + if ((argc == 4 || argc == 5) && (lday = getlday(argv[2], argv[3])) == -1) { + fprintf(stderr, "%s: wrong date specification\n", argv[0]); + exit(1); + } + if ((argc == 3 || argc == 5) && open(argv[argc - 1], O_RDONLY) == -1) { + fprintf(stderr, "%s: cannot find: %s\n", argv[0], argv[argc - 1]); + exit(1); + } + if (getcwd(pwd, sizeof(pwd)) == NULL) { + fprintf(stderr, "%s: cannot determine current directory: %s\n", + argv[0], strerror(errno)); + exit(1); + } + +/*-------------------------------------------------------------------------* + * determine execution time and create 'at' job file * + *-------------------------------------------------------------------------*/ + time(&clk); + p = localtime(&clk); + year = p->tm_year; + if (lday == NODAY) { /* no [month day] given */ + lday = p->tm_yday; + if (ltim <= (p->tm_hour * 100 + p->tm_min)) { + lday++; + if ((lday == MAXDAYNR && (year % 4)) || lday == MAXDAYNR + 1) { + lday = STARTDAY; + year++; + } + } + } else + switch (year % 4) { + case 0: + if (lday < p->tm_yday || + (lday == p->tm_yday && + ltim <= (p->tm_hour * 100 + p->tm_min))) { + year++; + if (lday > LEAPDAY) lday--; + } + break; + case 1: + case 2: + if (lday > LEAPDAY) lday--; + if (lday < p->tm_yday || + (lday == p->tm_yday && + ltim <= (p->tm_hour * 100 + p->tm_min))) + year++; + break; + case 3: + if (lday < ((lday > LEAPDAY) ? p->tm_yday + 1 : p->tm_yday) || + (lday ==((lday > LEAPDAY) ? p->tm_yday + 1 : p->tm_yday) && + ltim <= (p->tm_hour * 100 + p->tm_min))) + year++; + else if (lday > LEAPDAY) + lday--; + break; + } + sprintf(job, "/usr/spool/at/%02d.%03d.%04d.%02d", + year % 100, lday, ltim, getpid() % 100); + sprintf(pastjob, "/usr/spool/at/past/%02d.%03d.%04d.%02d", + year % 100, lday, ltim, getpid() % 100); + mask= umask(0077); + if ((fp = fopen(pastjob, "w")) == NULL) { + fprintf(stderr, "%s: cannot create %s: %s\n", + argv[0], pastjob, strerror(errno)); + exit(1); + } + +/*-------------------------------------------------------------------------* + * write environment and command(s) to 'at'job file * + *-------------------------------------------------------------------------*/ + i = 0; + while ((sp= envp[i++]) != NULL) { + dp = buf; + while ((c= *sp++) != '\0' && c != '=' && dp < buf+sizeof(buf)-1) + *dp++ = c; + if (c != '=') continue; + *dp = '\0'; + fprintf(fp, "%s='", buf); + while (*sp != 0) { + if (*sp == '\'') + fprintf(fp, "'\\''"); + else + fputc(*sp, fp); + sp++; + } + fprintf(fp, "'; export %s\n", buf); + } + fprintf(fp, "cd '%s'\n", pwd); + fprintf(fp, "umask %o\n", mask); + if (argc == 3 || argc == 5) + fprintf(fp, "%s\n", argv[argc - 1]); + else /* read from stdinput */ + while ((c = getchar()) != EOF) putc(c, fp); + fclose(fp); + + if (chown(pastjob, getuid(), getgid()) == -1) { + fprintf(stderr, "%s: cannot set ownership of %s: %s\n", + argv[0], pastjob, strerror(errno)); + unlink(pastjob); + exit(1); + } + /* "Arm" the job. */ + if (rename(pastjob, job) == -1) { + fprintf(stderr, "%s: cannot move %s to %s: %s\n", + argv[0], pastjob, job, strerror(errno)); + unlink(pastjob); + exit(1); + } + printf("%s: %s created\n", argv[0], job); + + /* Alert cron to the new situation. */ + if ((fp= fopen(CRONPID, "r")) != NULL) { + unsigned long pid; + + pid= 0; + while ((c= fgetc(fp)) != EOF && c != '\n') { + if ((unsigned) (c - '0') >= 10) { pid= 0; break; } + pid= 10*pid + (c - '0'); + if (pid >= 30000) { pid= 0; break; } + } + if (pid > 1) kill((pid_t) pid, SIGHUP); + } + return(0); +} + +/*-------------------------------------------------------------------------* + * getltim() return((time OK) ? daytime : -1) * + *-------------------------------------------------------------------------*/ +int getltim(t) +char *t; +{ + if (t[4] == '\0' && t[3] >= '0' && t[3] <= '9' && + t[2] >= '0' && t[2] <= '5' && t[1] >= '0' && t[1] <= '9' && + (t[0] == '0' || t[0] == '1' || (t[1] <= '3' && t[0] == '2'))) + return(atoi(t)); + else + return(-1); +} + +/*-------------------------------------------------------------------------* + * getlday() return ((date OK) ? yearday : -1) * + *-------------------------------------------------------------------------*/ +int getlday(m, d) +char *m, *d; +{ + int i, day, im; + static int cumday[] = {0, 0, 31, 60, 91, 121, 152, + 182, 213, 244, 274, 305, 335}; + static struct date { + char *mon; + int dcnt; + } *pc, kal[] = { + { "Jan", 31 }, { "Feb", 29 }, { "Mar", 31 }, { "Apr", 30 }, + { "May", 31 }, { "Jun", 30 }, { "Jul", 31 }, { "Aug", 31 }, + { "Sep", 30 }, { "Oct", 31 }, { "Nov", 30 }, { "Dec", 31 }, + }; + + pc = kal; + im = (digitstring(m)) ? atoi(m) : 0; + m[0] &= 0337; + for (i = 1; i < 13 && strcmp(m, pc->mon) && im != i; i++, pc++); + if (i < 13 && (day = (digitstring(d)) ? atoi(d) : 0) && day <= pc->dcnt) { + if (!STARTDAY) day--; + return(day + cumday[i]); + } else + return(-1); +} + + + +int digitstring(s) +char *s; +{ + while (*s >= '0' && *s <= '9') s++; + return((*s == '\0') ? 1 : 0); +} diff --git a/commands/simple/backup.c b/commands/simple/backup.c new file mode 100755 index 000000000..3b397dc1d --- /dev/null +++ b/commands/simple/backup.c @@ -0,0 +1,590 @@ +/* backup - backup a directory Author: Andy Tanenbaum */ + +/* This program recursively backs up a directory. It has two typical uses: + * + * 1. Backing up a directory to 1 or more diskettes + * 2. Backing up RAM disk to a shadow directory on hard disk + * + * The backup directory or medium may be empty, in which case, the entire + * source directory is copied, or it may contain an old backup, in which + * case only those files that are new or out of date are copied. In this + * respect, 'backup' resembles 'make', except that no 'makefile' is needed. + * The backed up copy may optionally be compressed to save space. + * + * The following flags exist: + * + * -d At the top level, only back up directories (not loose files) + * -j Don't copy junk: *.Z, *.bak, *.log, a.out, and core + * -m If ENOSPC encountered, ask for another diskette + * -n No directories, only loose files are backed up + * -o Don't copy *.o files + * -r Restore files (ie. uncompress if necessary) + * -s Don't copy *.s files + * -t Set creation date of target-file equal to cdate of source-file + * -v Verbose (announce what is being done) + * -z Compress on backup/uncompress on restore + * + * Patches: + * 30 Mar 91. Added restore option. cwr. + * 9 Sep 91. Changed user interface. cwr. + * 21 Jan 93. Revised error messages. cwr. + * 29 Mar 95. Added -o, NARROW define. cwr. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <utime.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <stdio.h> + +#undef NARROW /* Width of verbose output */ +#define COPY_SIZE 4096 +#define MAX_ENTRIES 512 +#define DIR_ENT_SIZE 16 +#define NAME_SIZE 14 +#define MAX_PATH 256 +#define NONFATAL 0 +#define FATAL 1 +#define NO_SAVINGS 512 /* compress can return code 2 */ +#define OUT_OF_SPACE 2 + +struct dir_buf { /* list of the src directory */ + unsigned short ino; + char name[NAME_SIZE]; +} dir_buf[MAX_ENTRIES]; + +struct sorted { + int mode; /* file mode */ + char *namep; /* pointer to name in dir_buf */ + long acctime; /* time of last access */ + long modtime; /* time of last modification */ +} sorted[MAX_ENTRIES]; + +char copybuf[COPY_SIZE]; +char *pname; +int dflag, jflag, mflag, nflag, oflag, rflag, sflag, tflag, vflag, zflag; + +extern int errno; +extern char **environ; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void maketarget, (char *dir2)); +_PROTOTYPE(int make_dir, (char *dir)); +_PROTOTYPE(int stat_all, (char *dir1, int n)); +_PROTOTYPE(void sort_dir, (int m)); +_PROTOTYPE(void process, (int m, char *dir1, char *dir2)); +_PROTOTYPE(void swap, (struct sorted *sp1, struct sorted *sp2)); +_PROTOTYPE(int copy, (char *dir1, struct sorted *sp, char *cbuf2)); +_PROTOTYPE(int zcopy, (char *src, char *targ)); +_PROTOTYPE(void copydir, (char *dir1, char *dir2, char *namep)); +_PROTOTYPE(void newdisk, (char *dir)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(void error, (int type, char *s1, char *s2, char *s3)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int ct, n, m, fd; + char *dir1, *dir2, *cp, c; + struct stat s; + + (void) sync(); + + /* Get the flags */ + if ((pname = strrchr(argv[0], '/')) == (char *)NULL) + pname = argv[0]; + else + pname++; + if (argc < 3 || argc > 4) usage(); + if (argc == 4) { + cp = argv[1]; + if (*cp++ != '-') usage(); + while ((c = *cp++) != '\0') { + switch (c) { + case 'd': dflag++; break; + case 'j': jflag++; break; + case 'm': mflag++; break; + case 'n': nflag++; break; + case 'o': oflag++; break; + case 's': sflag++; break; + case 'r': rflag++; break; + case 't': tflag++; break; + case 'v': vflag++; break; + case 'z': zflag++; break; + default: usage(); + } + } + dir1 = argv[2]; + dir2 = argv[3]; + } else { + dir1 = argv[1]; + dir2 = argv[2]; + } + if (!strcmp(pname, "restore") && !rflag) rflag++; + + /* Check for a valid source */ + if (stat(dir1, &s) < 0) error(FATAL, "cannot stat ", dir1, ""); + if ((s.st_mode & S_IFMT) != S_IFDIR) error(FATAL, "non-directory ", dir1, ""); + + /* Read in the source directory */ + fd = open(dir1, O_RDONLY); + if (fd < 0) error(FATAL, "cannot open ", dir1, ""); + ct = read(fd, (char *)&dir_buf[0], MAX_ENTRIES * DIR_ENT_SIZE); + close(fd); + if (ct == MAX_ENTRIES * DIR_ENT_SIZE) + error(FATAL, "directory ", dir1, " is too large"); + + /* Create the target directory. */ + maketarget(dir2); + + /* Stat all the entries. */ + n = ct / DIR_ENT_SIZE; + m = stat_all(dir1, n); + + /* Remove non-entries and sort what's left. */ + sort_dir(m); + + /* Process each of the m entries one at a time. */ + process(m, dir1, dir2); + return(0); +} + + +void maketarget(dir2) +char *dir2; +{ +/* The target directory is created if it does not already exist. */ + + char *p, c, dbuf[MAX_PATH]; + + if (access(dir2, 6) == 0) + return; /* if target exists, we're done */ + if (make_dir(dir2) == 0) return; /* we just made it */ + + /* We have to try creating all the higher level directories. */ + strcpy(dbuf, dir2); + p = dbuf + 1; + while (1) { + while (*p != '/' && *p != '\0') p++; + c = *p; /* either / or \0 */ + *p = 0; + make_dir(dbuf); + if (c == '\0') return; + *p = c; + p++; + } +} + +int make_dir(dir) +char *dir; +{ +/* Create a directory. */ + int pid, status; + + if ((pid = fork()) < 0) + error(FATAL, "cannot fork off mkdir to create ", dir, ""); + if (pid > 0) { + /* Parent process waits for child (mkdir). */ + wait(&status); + return(status); + } else { + /* Child process executes mkdir */ + close(2); /* don't want mkdir's error messages */ + execle("/bin/mkdir", "mkdir", dir, (char *) 0, environ); + execle("/usr/bin/mkdir", "mkdir", dir, (char *) 0, environ); + error(FATAL, "cannot execute mkdir", "", ""); + } + return(0); +} + + +int stat_all(dir1, n) +char *dir1; +int n; +{ +/* Stat all the directory entries. By doing this all at once, the disk + * head can stay in the inode area. + */ + + int i, j; + char cbuf[MAX_PATH]; + struct stat s; + + for (i = 0; i < n; i++) { + /* Mark "." and ".." as null entries, as well as unstatable ones. */ + if (strcmp(dir_buf[i].name, ".") == 0) dir_buf[i].ino = 0; + if (strcmp(dir_buf[i].name, "..") == 0) dir_buf[i].ino = 0; + if (dir_buf[i].ino == 0) continue; + + /* Stat the file. */ + strcpy(cbuf, dir1); + strncat(cbuf, "/", (size_t)1); + strncat(cbuf, dir_buf[i].name, (size_t)NAME_SIZE); + if (stat(cbuf, &s) < 0) { + error(NONFATAL, "cannot stat ", cbuf, ""); + dir_buf[i].ino = 0; /* mark as unusable */ + continue; + } + sorted[i].mode = s.st_mode; + sorted[i].acctime = s.st_atime; + sorted[i].modtime = s.st_mtime; + sorted[i].namep = dir_buf[i].name; + } + + /* Squeeze out all the entries whose ino field is 0. */ + j = 0; + for (i = 0; i < n; i++) { + if (dir_buf[i].ino != 0) { + sorted[j] = sorted[i]; + j++; + } + } + return(j); +} + + +void sort_dir(m) +int m; +{ +/* Sort the directory using bubble sort. */ + + struct sorted *sp1, *sp2; + + for (sp1 = &sorted[0]; sp1 < &sorted[m - 1]; sp1++) { + for (sp2 = sp1 + 1; sp2 < &sorted[m]; sp2++) { + if (strncmp(sp1->namep, sp2->namep, (size_t)NAME_SIZE) > 0) + swap(sp1, sp2); + } + } +} + + +void process(m, dir1, dir2) +int m; +char *dir1, *dir2; +{ +/* Process each entry in sorted[]. If it is a regular file, stat the target + * file. The the source is newer, copy it. If the entry is a directory, + * recursively call the entire program to process the directory. + */ + + int er, fmode, res, namlen; + struct sorted *sp; + struct stat s; + char cbuf[MAX_PATH]; + + for (sp = &sorted[0]; sp < &sorted[m]; sp++) { + fmode = sp->mode & S_IFMT; + if (fmode == S_IFREG) { + /* Regular file. Construct target name and stat it. */ + strcpy(cbuf, dir2); + strncat(cbuf, "/", (size_t)1); + strncat(cbuf, sp->namep, (size_t)NAME_SIZE); + namlen = strlen(sp->namep); + if (namlen > NAME_SIZE) + namlen = NAME_SIZE; /* no terminating null here */ + /* Switch between compressed and uncompressed file names */ + if (zflag && !rflag && strncmp((sp->namep + namlen - 2), ".Z", (size_t)2) + && (namlen <= (NAME_SIZE - 2))) + strncat(cbuf, ".Z", (size_t)2); + if (zflag && rflag && !strncmp((sp->namep + namlen - 2), ".Z", (size_t)2)) + cbuf[strlen(cbuf) - 2] = '\0'; + er = stat(cbuf, &s); + if (er < 0 || sp->modtime > s.st_mtime) { + res = copy(dir1, sp, cbuf); + } else { + res = NONFATAL; + } + + /* Check status of the copy. */ + if (res == OUT_OF_SPACE) { + printf("Out of space while copying to %s\n", cbuf); + /* We ran out of space copying a regular file. */ + if (mflag == 0) + error(FATAL, "Quitting, disk full", "", ""); + + /* If -m, ask for new diskette and continue. */ + newdisk(dir2); + sp--; + continue; + } + } else if (fmode == S_IFDIR) { + /* Directory. Execute this program recursively. */ + copydir(dir1, dir2, sp->namep); + } else if (fmode == S_IFBLK || fmode == S_IFCHR) { + /* Special file. */ + strncpy(cbuf, sp->namep, (size_t)NAME_SIZE); + printf("%s is special file. Not backed up.\n", cbuf); + } + } +} + + + + +void swap(sp1, sp2) +struct sorted *sp1, *sp2; +{ +/* Swap two directory entries. */ + + struct sorted d; + + d = *sp1; + *sp1 = *sp2; + *sp2 = d; +} + + +int copy(dir1, sp, cbuf2) +struct sorted *sp; +char *dir1, *cbuf2; +{ +/* Copy a regular file. */ + + int fd1, fd2, nr, nw, res, n; + char cbuf1[MAX_PATH], *p; +#ifdef NARROW + char *msg = (rflag || strcmp(pname, "backup")) ? "Restored" : "Backing up"; +#endif + + /* If the -j or -o or -s flags were given, suppress certain files. */ + p = sp->namep; + n = strlen(p); + if (n > NAME_SIZE) n = NAME_SIZE; + if (jflag) { + if (strcmp(p, "a.out") == 0) return(0); + if (strcmp(p, "core") == 0) return (0); + if (strcmp(p + n - 2, ".Z") == 0) return (0); + if (strcmp(p + n - 4, ".bak") == 0) return (0); + if (strcmp(p + n - 4, ".log") == 0) return (0); + } + if (oflag) { + if (strcmp(p + n - 2, ".o") == 0) return(0); + } + if (sflag) { + if (strcmp(p + n - 2, ".s") == 0) return(0); + } + res = 0; + if (dflag) return(0); /* backup -d means only directories */ + strcpy(cbuf1, dir1); + strncat(cbuf1, "/", (size_t)1); + strncat(cbuf1, sp->namep, (size_t)NAME_SIZE); /* cbuf1 = source file name */ + + /* At this point, cbuf1 contains the source file name, cbuf2 the target. */ + fd1 = open(cbuf1, O_RDONLY); + if (fd1 < 0) { + error(NONFATAL, "cannot open ", cbuf1, ""); + return(res); + } + fd2 = creat(cbuf2, (sp->mode | S_IWUSR) & 07777); + if (fd2 < 0) { + if (errno == ENFILE) { + close(fd1); + return(OUT_OF_SPACE); + } + error(NONFATAL, "cannot create ", cbuf2, ""); + close(fd1); + return(res); + } + + /* Both files are now open. Do the copying. */ + if (!rflag && strncmp((sp->namep + n - 2), ".Z", (size_t)2) || + rflag && !strncmp((sp->namep + n - 2), ".Z", (size_t)2)) { + if (zflag && (rflag || (n <= (NAME_SIZE - 2)))) { + close(fd1); + close(fd2); + res = zcopy(cbuf1, cbuf2); + if (tflag) utime(cbuf2, (struct utimbuf *) & (sp->acctime)); + if (res != 0) unlink(cbuf2); /* if error, get rid of the corpse */ +#ifdef NARROW + if (vflag && res == 0) printf("%s %s\n", msg, cbuf1); +#else + if (vflag && res == 0) { + printf("%-37.37s -> %-37.37s\n", cbuf1, cbuf2); + if (strlen(cbuf1) > 37 || strlen(cbuf2) > 37) + printf("%37.37s %37.37s\n", + (strlen(cbuf1) > 37) ? (cbuf1 + 37) : "", + (strlen(cbuf2) > 37) ? (cbuf2 + 37) : ""); + } +#endif + return(res); + } + } + while (1) { + nr = read(fd1, copybuf, COPY_SIZE); + if (nr == 0) break; + if (nr < 0) { + error(NONFATAL, "read error on ", cbuf1, ""); + res = EIO; + break; + } + nw = write(fd2, copybuf, nr); + if (nw < 0) { + if (errno == ENOSPC) { + /* Device is full. */ + res = OUT_OF_SPACE; + break; + } + + /* True write error. */ + error(NONFATAL, "write error on ", cbuf2, ""); + res = EIO; + break; + } + } + if (res == 0) { +#ifdef NARROW + if (vflag) printf("%s %s\n", msg, cbuf1); +#else + if (vflag) { + printf("%-37.37s -> %-37.37s\n", cbuf1, cbuf2); + if (strlen(cbuf1) > 37 || strlen(cbuf2) > 37) + printf("%37.37s %37.37s\n", + (strlen(cbuf1) > 37) ? (cbuf1 + 37) : "", + (strlen(cbuf2) > 37) ? (cbuf2 + 37) : ""); + } +#endif + } else { + unlink(cbuf2); + } + close(fd1); + close(fd2); + if (tflag) utime(cbuf2, (struct utimbuf *) & (sp->acctime)); + return(res); +} + + +int zcopy(src, targ) +char *src, *targ; +{ + + int pid, status, res, s; + char fbuf[20]; + + strcpy(fbuf, "-c"); + if (rflag) + strcat(fbuf, "d"); + else + strcat(fbuf, "f"); + + if ((pid = fork()) < 0) error(FATAL, "cannot fork", "", ""); + if (pid > 0) { + wait(&status); + + /* Error codes 0 and 2 are ok, assume others mean disk is full. */ + res = (status == 0 || status == NO_SAVINGS ? 0 : OUT_OF_SPACE); + return(res); + } else { + /* Child must execute compress. */ + close(1); + s = open(targ, O_RDWR); + if (s < 0) error(FATAL, "cannot write on ", "targ", ""); + execle("/bin/compress", "compress", fbuf, src, (char *)0, environ); + execle("/usr/bin/compress", "compress", fbuf, src, (char *)0, environ); + error(FATAL, "cannot exec compress", "", ""); + } + return(0); +} + + +void copydir(dir1, dir2, namep) +char *dir1, *dir2, *namep; +{ +/* Copy a directory. */ + + int pid, status; + char fbuf[20], d1buf[MAX_PATH], d2buf[MAX_PATH]; + + if (nflag) return; /* backup -n means no directories */ + + fbuf[0] = '\0'; + + /* Handle directory copy by forking off 'backup' ! */ + if (jflag || mflag || oflag || rflag || sflag || tflag || vflag || zflag) + strcpy(fbuf, "-"); + if (jflag) strcat(fbuf, "j"); + if (mflag) strcat(fbuf, "m"); + if (oflag) strcat(fbuf, "o"); + if (rflag) strcat(fbuf, "r"); + if (sflag) strcat(fbuf, "s"); + if (tflag) strcat(fbuf, "t"); + if (vflag) strcat(fbuf, "v"); + if (zflag) strcat(fbuf, "z"); + strcpy(d1buf, dir1); + strcat(d1buf, "/"); + strncat(d1buf, namep, (size_t)NAME_SIZE); + strcpy(d2buf, dir2); + strcat(d2buf, "/"); + strncat(d2buf, namep, (size_t)NAME_SIZE); + + if ((pid = fork()) < 0) error(FATAL, "cannot fork", "", ""); + if (pid > 0) { + /* Parent waits for child, then returns. */ + wait(&status); + return; + } + + if (fbuf[0] == '-') { + execle(pname, pname, fbuf, d1buf, d2buf, (char *) 0, environ); + execle("/bin/backup", "backup", fbuf, d1buf, d2buf, (char *)0,environ); + execle("/usr/bin/backup","backup",fbuf,d1buf,d2buf,(char *)0,environ); + error(FATAL, "cannot recursively exec backup", "", ""); + } else { + execle(pname, pname, d1buf, d2buf, (char *) 0, environ); + execle("/bin/backup", "backup", d1buf, d2buf, (char *)0,environ); + execle("/usr/bin/backup","backup", d1buf, d2buf, (char *)0,environ); + error(FATAL, "cannot recursively exec backup", "", ""); + } +} + +void newdisk(dir) +char *dir; +{ +/* Ask for a new diskette. A big problem is that this program does not + * know which device is being used and where it is mounted on. As an + * emergency solution, fork off a shell and ask the user to do the work. + */ + + int pid, status; + + printf("\nDiskette full. Please do the following:\n"); + printf(" 1. Unmount the diskette using /etc/umount\n"); + printf(" 2. Physically replace the diskette by the next one.\n"); + printf(" 3. Mount the new diskette using /etc/mount\n"); + printf(" 4. Type CTRL-D to return to the backup/restore program\n"); + + if ((pid = fork()) < 0) error(FATAL, "cannot fork", "", ""); + if (pid > 0) { + wait(&status); + maketarget(dir); /* make the directory */ + } else { + execle("/bin/sh", "sh", "-i", (char *) 0, environ); + execle("/usr/bin/sh", "sh", "-i", (char *) 0, environ); + error(FATAL, "cannot execute shell to ask for new diskette", "", ""); + } +} + +void usage() +{ + fprintf(stderr, "Usage: %s [-djmnorstvz] dir1 dir2\n", pname); + exit(2); +} + + +void error(type, s1, s2, s3) +int type; +char *s1, *s2, *s3; +{ + fprintf(stderr, "%s: %s%s%s\n", pname, s1, s2, s3); + + if (type == NONFATAL) + return; + else + exit(type); +} diff --git a/commands/simple/badblocks.c b/commands/simple/badblocks.c new file mode 100755 index 000000000..f1cc4c4f7 --- /dev/null +++ b/commands/simple/badblocks.c @@ -0,0 +1,657 @@ +/* badblocks - collect bad blocks in a file Author: Jacob Bunschoten */ + +/* Usage "badblocks block_special [Up_to_7_blocks]" */ + +/* This program is written to handle BADBLOCKS on a hard or floppy disk. + * The program asks for block_numbers. These numbers can be obtained with + * the program readall, written by A. Tanenbaum. It then creates a + * file on the disk containing up to 7 bad blocks. + * + * BUG: + * + * When the zone_size > block_size it can happen that + * the zone is already allocated. This means some + * file is using this zone and may use all the blocks including + * the bad one. This can be cured by inspecting the zone_bitmap + * (is already done) and change the file if this zone is used. + * This means that another zone must be allocated and + * the inode wich claims this zone must be found and changed. + * + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <minix/config.h> +#include <minix/type.h> +#include <fcntl.h> +#include <unistd.h> +#include <dirent.h> +#include <stdlib.h> + +#include "../../servers/fs/const.h" /* must be included before stdio.h */ +#undef printf /* so its define of printf can be undone */ +#include "../../servers/fs/type.h" + +#include <string.h> +#include <stdio.h> + +#define EXTERN extern +#include "../../servers/fs/super.h" + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void rw_super, (int flag)); +_PROTOTYPE(void get_super, (void)); +_PROTOTYPE(void put_super, (void)); +_PROTOTYPE(void rw_inode, (struct stat * stat_ptr, int rw_mode)); +_PROTOTYPE(void get_inode, (struct stat * stat_ptr)); +_PROTOTYPE(void put_inode, (struct stat * stat_ptr)); +_PROTOTYPE(long rd_cmdline, (int argc, char *argv[])); +_PROTOTYPE(void modify, (int nr_blocks)); +_PROTOTYPE(void save_blk, (block_t blk_num)); +_PROTOTYPE(void reset_blks, (void)); +_PROTOTYPE(void show_blks, (void)); +_PROTOTYPE(int blk_is_used, (block_t blk_num)); +_PROTOTYPE(int blk_ok, (block_t num)); +_PROTOTYPE(void set_bit, (zone_t num)); +_PROTOTYPE(long rd_num, (void)); +_PROTOTYPE(int ok, (char *str)); +_PROTOTYPE(void done, (int nr)); + +/* Super block table. + * + * The disk layout is: + * + * Item # block + * boot block 1 + * super block 1 + * inode map s_imap_blocks + * zone map s_zmap_blocks + * inodes (s_ninodes + 1 + inodes_per_block - 1)/inodes_per_block + * unused + * data zones (s_nzones - s_firstdatazone) << s_log_zone_size + * + */ + +#define OK 0 +#define NOT_OK 1 +#define QUIT 2 + +#define READ 0 +#define WRITE 1 + +#define HARMLESS 0 +#define DIR_CREATED 1 +#define DEV_MOUNTED 2 +#define FILE_EXISTS 3 +#define SUCCESS 4 + +#define BYTE 0377 +#define BLOCK_SIZE 1024 +#define SIZE_OF_INT (sizeof (int) ) + +/* Define V_NR_DZONES as the larger of V1_NR_DZONES and V2_NR_DZONES. */ +#if (V1_NR_DZONES > V2_NR_DZONES) +#define V_NR_DZONES V1_NR_DZONES +#define V_SMALLER V2_NR_DZONES +#else +#define V_NR_DZONES V2_NR_DZONES +#define V_SMALLER V1_NR_DZONES +#endif + +#if 0 +struct super_block { + ino_t s_ninodes; /* # usable inodes on the minor device */ + zone1_t s_nzones; /* total device size, including bit maps etc */ + short s_imap_blocks; /* # of blocks used by inode bit map */ + short s_zmap_blocks; /* # of blocks used by zone bit map */ + zone1_t s_firstdatazone; /* number of first data zone */ + short s_log_zone_size; /* log2 of blocks/zone */ + off_t s_max_size; /* maximum file size on this device */ + short s_magic; /* magic number to recognize super-blocks */ + short s_pad; /* try to avoid compiler-dependent padding */ + zone_t s_zones; /* number of zones (replaces s_nzones in V2) */ +} super_block; +#endif + + /* ====== globals ======= */ + +char *dev_name; +char f_name[] = ".Bad_XXXXXX"; +char file_name[50]; +char dir_name[] = "/tmpXXXXXX"; + +block_t block[V_NR_DZONES + 1]; /* last block contains zero */ +int interactive; /* 1 if interactive (argc == 2) */ +int position = 2; /* next block # is argv[position] */ + +FILE *f; +int fd; +int eofseen; /* set if '\n' seen */ +struct stat stat_buf; +struct super_block *sp, sbs; +int inodes_per_block; +size_t inode_size; +int v1fs = 0, v2fs = 0; /* TRUE for V1 file system, FALSE for V2 */ + +d1_inode d1inode; /* declare a V1 disk inode */ +d1_inode *ip1; +d2_inode d2inode; /* declare a V2 disk inode */ +d2_inode *ip2; + + + /* ====== super block routines ======= */ + +void rw_super(flag) +int flag; +{ /* read or write a superblock */ + int rwd; + + lseek(fd, 0L, SEEK_SET); /* rewind */ + lseek(fd, (long) BLOCK_SIZE, SEEK_SET); /* seek */ + + if (flag == READ) + rwd = read(fd, (char *) sp, SUPER_SIZE); + else + rwd = write(fd, (char *) sp, SUPER_SIZE); + if (rwd != SUPER_SIZE) { /* ok ? */ + printf("Bad %s in get_super() (should be %u is %d)\n", + flag == READ ? "read" : "write", + (unsigned) SUPER_SIZE, rwd); + done(DIR_CREATED); + } +} + +void get_super() + /* Get super_block. global pointer sp is used */ +{ + rw_super(READ); + + if (sp->s_magic == SUPER_MAGIC) { + /* This is a V1 file system. */ + v1fs = 1; /* file system is not V2 */ + } else if (sp->s_magic == SUPER_V2) { + /* This is a V2 file system. */ + v2fs = 1; /* this is a V2 file system */ + } else if (sp->s_magic == SUPER_V3) { + v1fs = v2fs = 0; /* this is a V3 file system */ + } else { + /* Neither V1 nor V2 nor V3. */ + printf("Bad magic number in super_block (0x%x)\n", + (unsigned) sp->s_magic); + done(DIR_CREATED); + } +} + + +void put_super() +{ + rw_super(WRITE); +} + + /* ========== inode routines =========== */ + +void rw_inode(stat_ptr, rw_mode) +struct stat *stat_ptr; +int rw_mode; +{ + int rwd; + ino_t i_num; + block_t blk, offset; + + + i_num = stat_ptr->st_ino; + + blk = (block_t) (2 + sp->s_imap_blocks + sp->s_zmap_blocks); + blk += (block_t) ((i_num - 1) / inodes_per_block); + blk *= (block_t) (BLOCK_SIZE);/* this block */ + + offset = (block_t) ((i_num - 1) % inodes_per_block); + offset *= (block_t) (inode_size); /* and this offset */ + + lseek(fd, (off_t) 0, SEEK_SET); /* rewind */ + lseek(fd, (off_t) (blk + offset), SEEK_SET); /* seek */ + + /* Pointer is at the inode */ + if (v1fs) { + /* This is a V1 file system. */ + if (rw_mode == READ) { /* read it */ + rwd = read(fd, (char *) ip1, inode_size); + } else { /* write it */ + rwd = write(fd, (char *) ip1, inode_size); + } + } else { + /* This is a V2 file system. */ + if (rw_mode == READ) { /* read it */ + rwd = read(fd, (char *) ip2, inode_size); + } else { /* write it */ + rwd = write(fd, (char *) ip2, inode_size); + } + } + + if (rwd != inode_size) { /* ok ? */ + printf("Bad %s in get_inode()\n", (rw_mode == READ) ? "read" : + "write"); + done(DIR_CREATED); + } +} + +void get_inode(stat_ptr) +struct stat *stat_ptr; +{ + + int cnt; + + rw_inode(stat_ptr, READ); + + if (v1fs) { + for (cnt = 0; cnt < V1_NR_TZONES; cnt++) + ip1->d1_zone[cnt] = 0; /* Just to be safe */ + } else { + for (cnt = 0; cnt < V2_NR_TZONES; cnt++) + ip2->d2_zone[cnt] = 0; /* Just to be safe */ + } +} + +void put_inode(stat_ptr) +struct stat *stat_ptr; +{ + rw_inode(stat_ptr, WRITE); +} + + + /* ============== main program ================= */ +int main(argc, argv) +int argc; +char *argv[]; +{ + int cnt, finished; + block_t blk_nr; + struct stat dev_stat; + FILE *fp; + int block_size; + + sp = &sbs; + ip1 = &d1inode; + ip2 = &d2inode; + + if (argc < 2 || argc > 9) { + fprintf(stderr, "Usage: %s block_special [up_to_7_blocks]\n", argv[0]); + done(HARMLESS); + } + interactive = (argc == 2 ? 1 : 0); + + /* Do some test. */ + if (geteuid()) { + printf("Sorry, not in superuser mode \n"); + printf("Set_uid bit must be on or you must become super_user\n"); + done(HARMLESS); + } + dev_name = argv[1]; + mktemp(dir_name); + if (mkdir(dir_name, 0777) == -1) { + fprintf(stderr, "%s is already used in system\n", dir_name); + done(HARMLESS); + } + + /* Mount device. This call may fail. */ + mount(dev_name, dir_name, 0); + /* Succes. dev was mounted, try to umount */ + + /* Umount device. Playing with the file system while other processes + * have access to this device is asking for trouble */ + if (umount(dev_name) == -1) { + printf("Could not umount device %s.\n", dev_name); + done(HARMLESS); + } + mktemp(f_name); + /* Create "/tmpXXXXpid/.BadXXpid" */ + strcat(file_name, dir_name); + strcat(file_name, "/"); + strcat(file_name, f_name); + + if (mount(dev_name, dir_name, 0) == -1) { /* this call should work */ + fprintf(stderr, "Could not mount device anymore\n"); + done(HARMLESS); + } + if (stat(file_name, &stat_buf) != -1) { + printf("File %s already exists\n", file_name); + done(DEV_MOUNTED); + } + if ((fp = fopen(file_name, "w")) == NULL) { + printf("Cannot create file %s\n", file_name); + done(DEV_MOUNTED); + } + chmod(file_name, 0); /* "useless" file */ + if (stat(file_name, &stat_buf) == -1) { + printf("What? Second call from stat failed\n"); + done(FILE_EXISTS); + } + + /* Stat buf must be safed. We can now calculate the inode on disk */ + fclose(fp); + + /* ===== the badblock file is created ===== */ + + if (umount(dev_name) == -1) { + printf("Can not umount device anymore??? \n"); + done(DIR_CREATED); + } + if ((fd = open(dev_name, O_RDWR)) == -1) { + printf("Can not open device %s\n", dev_name); + done(DEV_MOUNTED); + } + if (fstat(fd, &dev_stat) == -1) { + printf("fstat on device %s failed\n", dev_name); + done(DEV_MOUNTED); + } + if ((dev_stat.st_mode & S_IFMT) != S_IFBLK) { + printf("Device \"%s\" is not a block_special.\n", dev_name); + done(DEV_MOUNTED); + } + get_super(); + if (sp->s_log_zone_size) { + printf("Block_size != zone_size."); + printf("This program can not handle it\n"); + done(DIR_CREATED); + } + if(v1fs || v2fs) block_size = 1024; + else block_size = sp->s_block_size; + + /* The number of inodes in a block differs in V1 and V2. */ + if (v1fs) { + inodes_per_block = V1_INODES_PER_BLOCK; + inode_size = V1_INODE_SIZE; + } else { + inodes_per_block = V2_INODES_PER_BLOCK(block_size); + inode_size = V2_INODE_SIZE; + } + + get_inode(&stat_buf); + + for (finished = 0; !finished;) { + if (interactive) + printf("Give up to %d bad block numbers separated by spaces\n", + V_SMALLER); + reset_blks(); + cnt = 0; /* cnt keep track of the zone's */ + while (cnt < V_SMALLER) { + int tst; + + if (interactive) + blk_nr = rd_num(); + else + blk_nr = rd_cmdline(argc, argv); + if (blk_nr == -1) break; + tst = blk_ok(blk_nr); + + /* Test if this block is free */ + if (tst == OK) { + cnt++; + save_blk(blk_nr); + } else if (tst == QUIT) + break; + } + if (interactive) show_blks(); + if (!cnt) done(FILE_EXISTS); + if (interactive) { + switch (ok("All these blocks ok <y/n/q> (y:Device will change) ")) { + case OK: finished = 1; break; + case NOT_OK: + break; + case QUIT: done(FILE_EXISTS); + } + } else { + finished = 1; + } + } + + modify(cnt); + close(fd); /* free device */ + done(SUCCESS); + return(0); +} + +long rd_cmdline(argc, argv) +int argc; +char *argv[]; +{ + if (position == argc) return(-1); + return(atol(argv[position++])); +} + + +void modify(nr_blocks) +int nr_blocks; +{ + int i; + + if (nr_blocks == 0) return; + if (v1fs) { + /* This is a V1 file system. */ + for (i = 0; i < nr_blocks; i++) { + set_bit(block[i]); + ip1->d1_zone[i] = block[i]; + } + } else { + /* This is a V2 file system. */ + for (i = 0; i < nr_blocks; i++) { + set_bit(block[i]); + ip2->d2_zone[i] = block[i]; + } + } + if (v1fs) { + ip1->d1_size = (long) (BLOCK_SIZE * nr_blocks); /* give file size */ + ip1->d1_mtime = 0; /* Who wants a file from 1970? */ + } else { + ip2->d2_size = (long) (BLOCK_SIZE * nr_blocks); /* give file size */ + ip2->d2_atime = ip2->d2_mtime = ip2->d2_ctime = 0; + } + + put_inode(&stat_buf); /* save the inode on disk */ + put_super(); /* bit_maps too */ +} + + +static blk_cnt = 0; + +void save_blk(blk_num) +block_t blk_num; +{ + block[blk_cnt++] = blk_num; +} + +void reset_blks() +{ + int i; + + for (i = 0; i <= V_NR_DZONES; i++) + block[i] = 0; /* Note: Last block_number is set to zero */ + blk_cnt = 0; +} + +void show_blks() +{ + int i; + + for (i = 0; i < blk_cnt; i++) + printf("Block[%d] = %lu\n", i, (unsigned long) block[i]); +} + +int blk_is_used(blk_num) +block_t blk_num; +{ /* return TRUE(1) if used */ + int i; + + for (i = 0; block[i] && block[i] != blk_num; i++); + return(block[i] != 0) ? 1 : 0; +} + + + /* ===== bitmap handling ====== */ + +#define BIT_MAP_SHIFT 13 +#define INT_BITS (SIZE_OF_INT << 3) + +int blk_ok(num) /* is this zone free (y/n) */ +block_t num; +{ + block_t blk_offset; + int rd; + int blk, offset, words, bit, tst_word; + zone_t z_num; + + if (blk_is_used(num)) { + printf("Duplicate block (%lu) given\n", (unsigned long) num); + return NOT_OK; + } + + /* Assumption zone_size == block_size */ + + z_num = num - (sp->s_firstdatazone - 1); /* account offset */ + + /* Calculate the word in the bitmap. */ + blk = z_num >> BIT_MAP_SHIFT; /* which block */ + offset = z_num - (blk << BIT_MAP_SHIFT); /* offset */ + words = z_num / INT_BITS; /* which word */ + + blk_offset = (block_t) (2 + sp->s_imap_blocks); /* zone map */ + blk_offset *= (block_t) BLOCK_SIZE; /* of course in block */ + blk_offset += (block_t) (words * SIZE_OF_INT); /* offset */ + + + lseek(fd, (off_t) 0, SEEK_SET); /* rewind */ + lseek(fd, (off_t) blk_offset, SEEK_SET); /* set pointer at word */ + + rd = read(fd, (char *) &tst_word, SIZE_OF_INT); + if (rd != SIZE_OF_INT) { + printf("Read error in bitmap\n"); + done(DIR_CREATED); + } + + /* We have the tst_word, check if bit was off */ + bit = offset % INT_BITS; + + if (((tst_word >> bit) & 01) == 0) /* free */ + return OK; + else { + printf("Bad number %lu. ", (unsigned long) num); + printf("This zone (block) is marked in bitmap\n"); + return NOT_OK; + } +} + +void set_bit(num) /* write in the bitmap */ +zone_t num; +{ + int rwd; + long blk_offset; + int blk, offset, words, tst_word, bit; + unsigned z_num; + + z_num = num - (sp->s_firstdatazone - 1); + + blk = z_num >> BIT_MAP_SHIFT; /* which block */ + offset = z_num - (blk << BIT_MAP_SHIFT); /* offset in block */ + words = z_num / INT_BITS; /* which word */ + + blk_offset = (long) (2 + sp->s_imap_blocks); + blk_offset *= (long) BLOCK_SIZE; + blk_offset += (long) (words * SIZE_OF_INT); + + + lseek(fd, (off_t) 0, SEEK_SET); /* rewind */ + lseek(fd, (off_t) blk_offset, SEEK_SET); + + rwd = read(fd, (char *) &tst_word, SIZE_OF_INT); + if (rwd != SIZE_OF_INT) { + printf("Read error in bitmap\n"); + done(DEV_MOUNTED); + } + bit = offset % INT_BITS; + if (((tst_word >> bit) & 01) == 0) { /* free */ + lseek(fd, 0L, SEEK_SET);/* rewind */ + lseek(fd, (off_t) blk_offset, SEEK_SET); + tst_word |= (1 << bit); /* not free anymore */ + rwd = write(fd, (char *) &tst_word, SIZE_OF_INT); + if (rwd != SIZE_OF_INT) { + printf("Bad write in zone map\n"); + printf("Check file system \n"); + done(DIR_CREATED); + } + return; + } + printf("Bit map indicates that block %lu is in use. Not marked.\n", + (unsigned long) num); +/* done(DIR_CREATED); */ + return; +} + + /* ======= interactive interface ======= */ + +long rd_num() +{ /* read a number from stdin */ + long num; + int c; + + if (eofseen) return(-1); + do { + c = getchar(); + if (c == EOF || c == '\n') return(-1); + } while (c != '-' && (c < '0' || c > '9')); + + if (c == '-') { + printf("Block numbers must be positive\n"); + exit(1); + } + num = 0; + while (c >= '0' && c <= '9') { + num *= 10; + num += c - '0'; + c = getchar(); + if (c == '\n') eofseen = 1; + } + return num; +} + + + +int ok(str) +char *str; +{ + int c; + + for (;;) { + printf("%s", str); + while ((c = getchar()) != EOF && + c != 'y' && c != 'n' && c != 'q') + if (c != '\n') printf(" Bad character %c\n", (char) c); + switch (c) { + case EOF: + return QUIT; + case 'y': + return OK; + case 'n': + return NOT_OK; + case 'q': return QUIT; + } + printf("\n"); + } +} + + +void done(nr) +int nr; +{ + switch (nr) { + case SUCCESS: + case FILE_EXISTS: + unlink(file_name); + case DEV_MOUNTED: + umount(dev_name); + case DIR_CREATED: + rmdir(dir_name); + case HARMLESS:; + } + sync(); + exit(nr == SUCCESS ? 0 : 1); +} diff --git a/commands/simple/banner.c b/commands/simple/banner.c new file mode 100755 index 000000000..a56a98e23 --- /dev/null +++ b/commands/simple/banner.c @@ -0,0 +1,153 @@ +/* banner - print a banner Author: Brian Wallis */ + +/***************************************************************** + * + * SYSVbanner.c + * + * This is a PD version of the SYS V banner program (at least I think + * it is compatible to SYS V) which I wrote to use with the clock + * program written by: + ** DCF, Inc. + ** 14623 North 49th Place + ** Scottsdale, AZ 85254 + * and published in the net comp.sources.misc newsgroup in early July + * since the BSD banner program works quite differently. + * + * There is no copyright or responsibility accepted for the use + * of this software. + * + * Brian Wallis, brw@jim.odr.oz, 4 July 1988 + * + *****************************************************************/ + +#include <string.h> +#include <stdio.h> + +_PROTOTYPE(int main, (int argc, char **argv)); + +char *glyphs[] = { + " @@@ @@ @@ @ @ @@@@@ @@ @@@ ", + " @@@ @@ @@ @ @ @ @ @@@ @ @ @ @@@ ", + " @@@ @ @ @@@@@@@@ @ @@ @ @@ @ ", + " @ @ @ @@@@@ @ @@@ @ ", + " @@@@@@@ @ @ @ @ @ @ ", + " @@@ @ @ @ @ @ @ @@ @ @ ", + " @@@ @ @ @@@@@ @ @@ @@@@ @ ", + + " @@ @@ @", + " @ @ @ @ @ @ ", + " @ @ @ @ @ @ ", + " @ @ @@@@@@@ @@@@@ @@@ @@@@@ @ ", + " @ @ @ @ @ @@@ @ ", + " @ @ @ @ @ @ @@@ @ ", + " @@ @@ @ @@@ @ ", + + " @@@ @ @@@@@ @@@@@ @ @@@@@@@ @@@@@ @@@@@@@", + " @ @ @@ @ @@ @@ @ @ @ @@ @ ", + "@ @ @ @ @ @ @@ @ @ @ @ ", + "@ @ @ @ @@@@@ @@@@@ @@@@@@@ @@@@@ @@@@@@ @ ", + "@ @ @ @ @ @ @ @@ @ @ ", + " @ @ @ @ @ @ @ @ @@ @ @ ", + " @@@ @@@@@ @@@@@@@ @@@@@ @ @@@@@ @@@@@ @ ", + + " @@@@@ @@@@@ @@@ @ @ @@@@@ ", + "@ @@ @ @@@ @@@ @ @ @ @", + "@ @@ @ @@@ @ @@@@@ @ @", + " @@@@@ @@@@@@ @@@ @ @ @@ ", + "@ @ @ @@@ @ @@@@@ @ @ ", + "@ @@ @ @@@ @ @ @ ", + " @@@@@ @@@@@ @@@ @ @ @ @ ", + + " @@@@@ @ @@@@@@ @@@@@ @@@@@@ @@@@@@@@@@@@@@ @@@@@ ", + "@ @ @ @ @ @@ @@ @@ @ @ @", + "@ @@@ @ @ @ @ @@ @ @@ @ @ ", + "@ @ @ @@ @@@@@@@ @ @ @@@@@@ @@@@@ @ @@@@", + "@ @@@@ @@@@@@@@ @@ @ @@ @ @ @", + "@ @@ @@ @@ @@ @@ @ @ @", + " @@@@@ @ @@@@@@@ @@@@@ @@@@@@ @@@@@@@@ @@@@@ ", + + "@ @ @*@ @@ @ @ @ @@ @@@@@@@@", + "@ @ @ @@ @ @ @@ @@@@ @@ @", + "@ @ @ @@ @ @ @ @ @ @@ @ @@ @", + "@@@@@@@ @ @@@@ @ @ @ @@ @ @@ @", + "@ @ @ @ @@ @ @ @ @@ @ @@ @", + "@ @ @ @ @@ @ @ @ @@ @@@ @", + "@ @ @@@ @@@@@ @ @ @@@@@@@@ @@ @@@@@@@@", + + "@@@@@@ @@@@@ @@@@@@ @@@@@ @@@@@@@@ @@ @@ @", + "@ @@ @@ @@ @ @ @ @@ @@ @ @", + "@ @@ @@ @@ @ @ @@ @@ @ @", + "@@@@@@ @ @@@@@@@ @@@@@ @ @ @@ @@ @ @", + "@ @ @ @@ @ @ @ @ @ @ @ @ @ @", + "@ @ @ @ @ @ @ @ @ @ @ @ @ @ @", + "@ @@@@ @@ @ @@@@@ @ @@@@@ @ @@ @@ ", + + "@ @@ @@@@@@@@ @@@@@ @ @@@@@ @ ", + " @ @ @ @ @ @ @ @ @ @ ", + " @ @ @ @ @ @ @ @ @ @ ", + " @ @ @ @ @ @ ", + " @ @ @ @ @ @ @ ", + " @ @ @ @ @ @ @ ", + "@ @ @ @@@@@@@ @@@@@ @ @@@@@ @@@@@@@", + + " @@@ ", + " @@@ @@ @@@@@ @@@@ @@@@@ @@@@@@ @@@@@@ @@@@ ", + " @ @ @ @ @ @ @ @ @ @ @ @ @", + " @ @ @ @@@@@ @ @ @ @@@@@ @@@@@ @ ", + " @@@@@@ @ @ @ @ @ @ @ @ @@@", + " @ @ @ @ @ @ @ @ @ @ @ @", + " @ @ @@@@@ @@@@ @@@@@ @@@@@@ @ @@@@ ", + + " ", + " @ @ @ @ @ @ @ @ @ @ @ @@@@ ", + " @ @ @ @ @ @ @ @@ @@ @@ @ @ @", + " @@@@@@ @ @ @@@@ @ @ @@ @ @ @ @ @ @", + " @ @ @ @ @ @ @ @ @ @ @ @ @ @", + " @ @ @ @ @ @ @ @ @ @ @ @@ @ @", + " @ @ @ @@@@ @ @ @@@@@@ @ @ @ @ @@@@ ", + + " ", + " @@@@@ @@@@ @@@@@ @@@@ @@@@@ @ @ @ @ @ @", + " @ @ @ @ @ @ @ @ @ @ @ @ @ @", + " @ @ @ @ @ @ @@@@ @ @ @ @ @ @ @", + " @@@@@ @ @ @ @@@@@ @ @ @ @ @ @ @ @@ @", + " @ @ @ @ @ @ @ @ @ @ @ @ @@ @@", + " @ @@@ @ @ @ @@@@ @ @@@@ @@ @ @", + + " @@@ @ @@@ @@ @ @ @ @", + " @ @ @ @ @@@@@@ @ @ @ @ @ @ @ @ @ ", + " @ @ @ @ @ @ @ @ @@ @ @ @ @", + " @@ @ @ @@ @@ @ @ @ ", + " @@ @ @ @ @ @ @ @ @ @", + " @ @ @ @ @ @ @ @ @ @ ", + " @ @ @ @@@@@@ @@@ @ @@@ @ @ @ @" +}; + +int main(argc, argv) +int argc; +char *argv[]; +{ + int a, b, c, len, ind; + char line[80]; + + for (argv++; --argc; argv++) { + len = strlen(*argv); + if (len > 10) len = 10; + for (a = 0; a < 7; a++) { + for (b = 0; b < len; b++) { + if ((ind = (*argv)[b] - ' ') < 0) ind = 0; + for (c = 0; c < 7; c++) { + line[b * 8 + c] = glyphs[(ind / 8 * 7) + a][(ind % 8 * 7) + c] == '@' ? ind + ' ' : ' '; + } + line[b * 8 + 7] = ' '; + } + for (b = len * 8 - 1; b >= 0; b--) { + if (line[b] != ' ') break; + line[b] = '\0'; + } + printf("%s\n", line); + } + printf("\n"); + } + return(0); +} diff --git a/commands/simple/basename.c b/commands/simple/basename.c new file mode 100755 index 000000000..f56b3d346 --- /dev/null +++ b/commands/simple/basename.c @@ -0,0 +1,76 @@ +/* basename - print last part of a path Authors: B. Garfolo & P. Nelson */ + +/* Basename - print the last part of a path. + * + * For MINIX -- Conforms to POSIX - P1003.2/D10 + * Exception -- it ignores the LC environment variables. + * + * Original MINIX author: Blaine Garfolo + * POSIX rewrite author: Philip A. Nelson + * + * POSIX version - October 20, 1990 + * Feb 14, 1991: changed rindex to strrchr. (PAN) + * + */ + + +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#define EOS '\0' + +_PROTOTYPE(int main, (int argc, char **argv)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + char *result_string; /* The pointer into argv[1]. */ + char *temp; /* Used to move around in argv[1]. */ + int suffix_len; /* Length of the suffix. */ + int suffix_start; /* Where the suffix should start. */ + + + /* Check for the correct number of arguments. */ + if ((argc < 2) || (argc > 3)) { + fprintf(stderr, "Usage: basename string [suffix] \n"); + exit(1); + } + + /* Check for all /'s */ + for (temp = argv[1]; *temp == '/'; temp++) /* Move to next char. */ + ; + if (*temp == EOS) { + printf("/\n"); + exit(0); + } + + /* Build the basename. */ + result_string = argv[1]; + + /* Find the last /'s */ + temp = strrchr(result_string, '/'); + + if (temp != NULL) { + /* Remove trailing /'s. */ + while ((*(temp + 1) == EOS) && (*temp == '/')) *temp-- = EOS; + + /* Set result_string to last part of path. */ + if (*temp != '/') temp = strrchr(result_string, '/'); + if (temp != NULL && *temp == '/') result_string = temp + 1; + } + + /* Remove the suffix, if any. */ + if (argc > 2) { + suffix_len = strlen(argv[2]); + suffix_start = strlen(result_string) - suffix_len; + if (suffix_start > 0) + if (strcmp(result_string + suffix_start, argv[2]) == EOS) + *(result_string + suffix_start) = EOS; + } + + /* Print the resultant string. */ + printf("%s\n", result_string); + return(0); +} diff --git a/commands/simple/cal.c b/commands/simple/cal.c new file mode 100755 index 000000000..c330eaf16 --- /dev/null +++ b/commands/simple/cal.c @@ -0,0 +1,315 @@ +/* cal - print a calendar Author: Maritn Minow */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#define do3months domonth +#define IO_SUCCESS 0 /* Unix definitions */ +#define IO_ERROR 1 +#define EOS 0 + +#define ENTRY_SIZE 3 /* 3 bytes per value */ +#define DAYS_PER_WEEK 7 /* Sunday, etc. */ +#define WEEKS_PER_MONTH 6 /* Max. weeks in a month */ +#define MONTHS_PER_LINE 3 /* Three months across */ +#define MONTH_SPACE 3 /* Between each month */ + +char *badarg = {"Bad argument\n"}; +char *how = {"Usage: cal [month] year\n"}; + +/* Calendar() stuffs data into layout[], + * output() copies from layout[] to outline[], (then trims blanks). + */ +char layout[MONTHS_PER_LINE][WEEKS_PER_MONTH][DAYS_PER_WEEK][ENTRY_SIZE]; +char outline[(MONTHS_PER_LINE * DAYS_PER_WEEK * ENTRY_SIZE) + + (MONTHS_PER_LINE * MONTH_SPACE) + + 1]; + +char *weekday = " S M Tu W Th F S"; +char *monthname[] = { + "???", /* No month 0 */ + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void doyear, (int year)); +_PROTOTYPE(void domonth, (int year, int month)); +_PROTOTYPE(void output, (int nmonths)); +_PROTOTYPE(void calendar, (int year, int month, int indx)); +_PROTOTYPE(void usage, (char *s)); +_PROTOTYPE(int date, (int year, int month, int week, int wday)); +_PROTOTYPE(void setmonth, (int year, int month)); +_PROTOTYPE(int getdate, (int week, int wday)); +_PROTOTYPE(static int Jan1, (int year)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + register int year; + + register int arg1val; + int arg1len; + int arg2val; + + if (argc <= 1) { + usage(how); + } else { + arg1val = atoi(argv[1]); + arg1len = strlen(argv[1]); + if (argc == 2) { + /* Only one argument, if small, it's a month. If + * large, it's a year. Note: cal 0082 Year + * 0082 cal 82 Year 0082 */ + if (arg1len <= 2 && arg1val <= 12) + do3months(year, arg1val); + else + doyear(arg1val); + } else { + /* Two arguments, allow 1980 12 or 12 1980 */ + arg2val = atoi(argv[2]); + if (arg1len > 2) + do3months(arg1val, arg2val); + else + do3months(arg2val, arg1val); + } + } + return(IO_SUCCESS); +} + +void doyear(year) +int year; +/* Print the calendar for an entire year. */ +{ + register int month; + + if (year < 1 || year > 9999) usage(badarg); + if (year < 100) + printf("\n\n\n 00%2d\n\n", year); + else + printf("\n\n\n%35d\n\n", year); + for (month = 1; month <= 12; month += MONTHS_PER_LINE) { + printf("%12s%23s%23s\n", + monthname[month], + monthname[month + 1], + monthname[month + 2]); + printf("%s %s %s\n", weekday, weekday, weekday); + calendar(year, month + 0, 0); + calendar(year, month + 1, 1); + calendar(year, month + 2, 2); + output(3); +#if MONTHS_PER_LINE != 3 +#error "the above will not work" +#endif + } + printf("\n\n\n"); +} + +void domonth(year, month) +int year; +int month; +/* Do one specific month -- note: no longer used */ +{ + if (year < 1 || year > 9999) usage(badarg); + if (month <= 0 || month > 12) usage(badarg); + printf("%9s%5d\n\n%s\n", monthname[month], year, weekday); + calendar(year, month, 0); + output(1); + printf("\n\n"); +} + +void output(nmonths) +int nmonths; /* Number of months to do */ +/* Clean up and output the text. */ +{ + register int week; + register int month; + register char *outp; + int i; + char tmpbuf[21], *p; + + for (week = 0; week < WEEKS_PER_MONTH; week++) { + outp = outline; + for (month = 0; month < nmonths; month++) { + /* The -1 in the following removes the unwanted + * leading blank from the entry for Sunday. */ + p = &layout[month][week][0][1]; + for (i = 0; i < 20; i++) tmpbuf[i] = *p++; + tmpbuf[20] = 0; + sprintf(outp, "%s ", tmpbuf); + outp += (DAYS_PER_WEEK * ENTRY_SIZE) + MONTH_SPACE - 1; + } + while (outp > outline && outp[-1] == ' ') outp--; + *outp = EOS; + printf("%s\n", outline); + } +} + +void calendar(year, month, indx) +int year; +int month; +int indx; /* Which of the three months */ +/* Actually build the calendar for this month. */ +{ + register char *tp; + int week; + register int wday; + register int today; + + setmonth(year, month); + for (week = 0; week < WEEKS_PER_MONTH; week++) { + for (wday = 0; wday < DAYS_PER_WEEK; wday++) { + tp = &layout[indx][week][wday][0]; + *tp++ = ' '; + today = getdate(week, wday); + if (today <= 0) { + *tp++ = ' '; + *tp++ = ' '; + } else if (today < 10) { + *tp++ = ' '; + *tp = (today + '0'); + } else { + *tp++ = (today / 10) + '0'; + *tp = (today % 10) + '0'; + } + } + } +} + +void usage(s) +char *s; +{ +/* Fatal parameter error. */ + + fprintf(stderr, "%s", s); + exit(IO_ERROR); +} + +/* Calendar routines, intended for eventual porting to TeX + * + * date(year, month, week, wday) + * Returns the date on this week (0 is first, 5 last possible) + * and day of the week (Sunday == 0) + * Note: January is month 1. + * + * setmonth(year, month) + * Parameters are as above, sets getdate() for this month. + * + * int + * getdate(week, wday) + * Parameters are as above, uses the data set by setmonth() + */ + +/* This structure is used to pass data between setmonth() and getdate(). + * It needs considerable expansion if the Julian->Gregorian change is + * to be extended to other countries. + */ + +static struct { + int this_month; /* month number used in 1752 checking */ + int feb; /* Days in February for this month */ + int sept; /* Days in September for this month */ + int days_in_month; /* Number of days in this month */ + int dow_first; /* Day of week of the 1st day in month */ +} info; + +static int day_month[] = { /* 30 days hath September... */ + 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 +}; + +int date(year, month, week, wday) +int year; /* Calendar date being computed */ +int month; /* January == 1 */ +int week; /* Week in the month 0..5 inclusive */ +int wday; /* Weekday, Sunday == 0 */ +/* Return the date of the month that fell on this week and weekday. + * Return zero if it's out of range. + */ +{ + setmonth(year, month); + return(getdate(week, wday)); +} + +void setmonth(year, month) +int year; /* Year to compute */ +int month; /* Month, January is month 1 */ +/* Setup the parameters needed to compute this month + * (stored in the info structure). + */ +{ + register int i; + + if (month < 1 || month > 12) {/* Verify caller's parameters */ + info.days_in_month = 0; /* Garbage flag */ + return; + } + info.this_month = month; /* used in 1752 checking */ + info.dow_first = Jan1(year); /* Day of January 1st for now */ + info.feb = 29; /* Assume leap year */ + info.sept = 30; /* Assume normal year */ + /* Determine whether it's an ordinary year, a leap year or the + * magical calendar switch year of 1752. */ + switch ((Jan1(year + 1) + 7 - info.dow_first) % 7) { + case 1: /* Not a leap year */ + info.feb = 28; + case 2: /* Ordinary leap year */ + break; + + default: /* The magical moment arrives */ + info.sept = 19; /* 19 days hath September */ + break; + } + info.days_in_month = + (month == 2) ? info.feb + : (month == 9) ? info.sept + : day_month[month]; + for (i = 1; i < month; i++) { + switch (i) { /* Special months? */ + case 2: /* February */ + info.dow_first += info.feb; + break; + + case 9: info.dow_first += info.sept; break; + + default: + info.dow_first += day_month[i]; + break; + } + } + info.dow_first %= 7; /* Now it's Sunday to Saturday */ +} + +int getdate(week, wday) +int week; +int wday; +{ + register int today; + + /* Get a first guess at today's date and make sure it's in range. */ + today = (week * 7) + wday - info.dow_first + 1; + if (today <= 0 || today > info.days_in_month) + return(0); + else if (info.sept == 19 && info.this_month == 9 + && today >= 3) /* The magical month? */ + return(today + 11); /* If so, some dates changed */ + else /* Otherwise, */ + return(today); /* Return the date */ +} + +static int Jan1(year) +int year; +/* Return day of the week for Jan 1 of the specified year. */ +{ + register int day; + + day = year + 4 + ((year + 3) / 4); /* Julian Calendar */ + if (year > 1800) { /* If it's recent, do */ + day -= ((year - 1701) / 100); /* Clavian correction */ + day += ((year - 1601) / 400); /* Gregorian correction */ + } + if (year > 1752) /* Adjust for Gregorian */ + day += 3; /* calendar */ + return(day % 7); +} diff --git a/commands/simple/calendar.c b/commands/simple/calendar.c new file mode 100755 index 000000000..136ee6231 --- /dev/null +++ b/commands/simple/calendar.c @@ -0,0 +1,230 @@ +/* calendar - reminder service Authors: S. & K. Hirabayashi */ + +/* Permission is hereby granted for nonprofit use. */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <regexp.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <termcap.h> +#include <unistd.h> +#include <utime.h> +#include <stdio.h> + +/* Change these two lines for your system needs. */ +#define MAIL1 "/usr/bin/mail" +#define MAIL2 "/bin/mail" +#define PASSWD "/etc/passwd" /* system password file */ +#define MAX_EXP 4 /* see date_exp() function */ + +char *mail; /* mail command path ("/bin/mail" etc) */ +regexp *exp[MAX_EXP]; /* date expressions */ +int nexp; /* # of the date expressions */ +char calfile[PATH_MAX]; /* calendar file for the user */ + +int rflg; /* consult aged 'calendar' file and touch */ +int mflg; /* mail (multi user) service */ +char *cmd; /* the name of this command */ +char buf[BUFSIZ]; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void calendar, (void)); +_PROTOTYPE(char *getstr, (char *s, int n)); +_PROTOTYPE(int newaccess, (char *file)); +_PROTOTYPE(void grep, (char *file, char *user)); +_PROTOTYPE(int date_exp, (void)); +_PROTOTYPE(char *date_pat, (time_t t)); +_PROTOTYPE(void regerror, (char *s)); +_PROTOTYPE(void error, (char *s, char *t)); + +int main(argc, argv) +int argc; +char **argv; +{ + char *s; + + cmd = *argv; + while (--argc > 0 && (*++argv)[0] == '-') { + s = argv[0] + 1; + if (*s == '\0') + mflg++; /* mail service */ + else if (strcmp(s, "r") == 0) + rflg++, mflg++; + } + + if (mflg) { /* check mailing agent */ + if (access(MAIL1, X_OK) == 0) + mail = MAIL1; + else if (access(MAIL2, X_OK) == 0) + mail = MAIL2; + else + error("cannot find %s", MAIL1); + } + nexp = date_exp(); + calendar(); + exit(0); +} + +void calendar() +{ + int i; + char *s; + FILE *fp; + + if (!mflg) { + grep("calendar", ""); + return; + } + + /* Mail sevice */ + if ((fp = fopen(PASSWD, "r")) == (FILE *) NULL) + error("cannot open %s", PASSWD); + + while (fgets(buf, BUFSIZ, fp) != (char *) NULL) { + for (i = 0, s = buf; *s && *s != '\n'; s++) + if (*s == ':') i++; + *s = '\0'; + if (i != 6) error("illegal '/etc/passwd' format: %s", buf); + + /* Calendar file = ${HOME}/calendar */ + sprintf(calfile, "%s/%s", getstr(buf, 5), "calendar"); + + if ((access(calfile, R_OK) != 0) || (rflg && !newaccess(calfile))) + continue; + + grep(calfile, getstr(buf, 0)); + } + + fclose(fp); +} + +char *getstr(s, n) +char *s; +int n; +{ +/* Returns the string value of the n-th field in the record (s) */ + int i; + char *t; + static char str[512]; + + for (i = 0; i < n && *s; s++) + if (*s == ':') i++; /* field separator */ + for (i = 0, t = str; *s && *s != ':' && i < 511; i++) *t++ = *s++; + *t = '\0'; + return str; +} + +int newaccess(file) +char *file; /* file name */ +{ +/* Check whether the file has been touched today. */ + + int r = 0; + struct tm *tm; + struct stat stbuf; + time_t clk; + char newdate[8], olddate[8]; + + time(&clk); + tm = localtime(&clk); + sprintf(newdate, "%02d%02d%02d", tm->tm_year, tm->tm_mon + 1, tm->tm_mday); + + if (stat(file, &stbuf) == -1) error("cannot stat %s", file); + tm = localtime(&stbuf.st_mtime); + sprintf(olddate, "%02d%02d%02d", tm->tm_year, tm->tm_mon + 1, tm->tm_mday); + + if (strcmp(newdate, olddate) != 0) { + utime(file, NULL); /* touch */ + r++; + } + return r; +} + +void grep(file, user) +char *file, *user; +{ /* grep 'exp[]' [| mail user] */ + int i; + char command[128]; /* mail command */ + FILE *ifp, *ofp; + + if ((ifp = fopen(file, "r")) == (FILE *) NULL) + error("cannot open %s", file); + if (*user != '\0') { + sprintf(command, "%s %s", mail, user); + ofp = (FILE *) NULL; + } else { + ofp = stdout; + } + + while (fgets(buf, BUFSIZ, ifp) != (char *) NULL) { + for (i = 0; i < nexp; i++) { + if (regexec(exp[i], buf, 1)) { + if ((ofp == (FILE *) NULL) && + (ofp = popen(command, "w")) == (FILE *) NULL) + error("cannot popen %s", mail); + fputs(buf, ofp); + break; + } + } + } + + fclose(ifp); + if (ofp == stdout) + fflush(ofp); + else if (ofp != (FILE *) NULL) + pclose(ofp); +} + +int date_exp() +{ +/* Set compiled regular expressions into the exp[] array. */ + static int n[] = {2, 2, 2, 2, 2, 4, 3}; + int i, r, wday; + time_t clk; + + time(&clk); + wday = localtime(&clk)->tm_wday; + r = n[wday]; + if (r > MAX_EXP) error("too many date expressions", ""); + for (i = 0; i < r; i++) { + exp[i] = regcomp(date_pat(clk)); + clk += 60 * 60 * 24L; /* 24 hours */ + } + return(r); +} + +char *date_pat(t) +time_t t; +{ /* returns date expression for the time (t) */ + static char *month[] = { + "[Jj]an", "[Ff]eb", "[Mm]ar", "[Aa]pr", "[Mm]ay", "[Jj]un", + "[Jj]ul", "[Aa]ug", "[Ss]ep", "[Oo]ct", "[Nn]ov", "[Dd]ec" + }; + static char str[512]; + struct tm *tm; + + tm = localtime(&t); + sprintf(str, + "(^|[ \t(,;])(((%s[^ \t]*[ \t])|0*%d/|\\*/)(0*%d|\\*))([^0123456789]|$)", + month[tm->tm_mon], tm->tm_mon + 1, tm->tm_mday); + + return str; +} + +void regerror(s) +char *s; +{ /* regcomp() needs this */ + error("REGULAR EXPRESSION ERROR (%s)", s); +} + +void error(s, t) +char *s, *t; +{ + fprintf(stderr, "%s: ", cmd); + fprintf(stderr, s, t); + fprintf(stderr, "\n"); + exit(1); +} diff --git a/commands/simple/cat.c b/commands/simple/cat.c new file mode 100755 index 000000000..65824dbfc --- /dev/null +++ b/commands/simple/cat.c @@ -0,0 +1,138 @@ +/* cat - concatenates files Author: Andy Tanenbaum */ + +/* 30 March 1990 - Slightly modified for efficiency by Norbert Schlenker. */ +/* 23 March 2002 - Proper error messages by Kees J. Bot. */ + + +#include <errno.h> +#include <sys/types.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <minix/minlib.h> +#include <stdio.h> + +#define CHUNK_SIZE (2048 * sizeof(char *)) + +static int unbuffered; +static char ibuf[CHUNK_SIZE]; +static char obuf[CHUNK_SIZE]; +static char *op = obuf; + +int main(int argc, char **argv); +static void copyout(char *file, int fd); +static void output(char *buf, size_t count); +static void report(char *label); +static void fatal(char *label); + +static char STDIN[] = "standard input"; +static char STDOUT[] = "standard output"; + +static int excode = 0; + +int main(int argc, char *argv[]) +{ + int i, fd; + + i = 1; + while (i < argc && argv[i][0] == '-') { + char *opt = argv[i] + 1; + + if (opt[0] == 0) break; /* - */ + i++; + if (opt[0] == '-' && opt[1] == 0) break; /* -- */ + + while (*opt != 0) switch (*opt++) { + case 'u': + unbuffered = 1; + break; + default: + std_err("Usage: cat [-u] [file ...]\n"); + exit(1); + } + } + + if (i >= argc) { + copyout(STDIN, STDIN_FILENO); + } else { + while (i < argc) { + char *file = argv[i++]; + + if (file[0] == '-' && file[1] == 0) { + copyout(STDIN, STDIN_FILENO); + } else { + fd = open(file, O_RDONLY); + if (fd < 0) { + report(file); + } else { + copyout(file, fd); + close(fd); + } + } + } + } + output(obuf, (op - obuf)); + return(excode); +} + +static void copyout(char *file, int fd) +{ + int n; + + while (1) { + n = read(fd, ibuf, CHUNK_SIZE); + if (n < 0) fatal(file); + if (n == 0) return; + if (unbuffered || (op == obuf && n == CHUNK_SIZE)) { + output(ibuf, n); + } else { + int bytes_left; + + bytes_left = &obuf[CHUNK_SIZE] - op; + if (n <= bytes_left) { + memcpy(op, ibuf, (size_t)n); + op += n; + } else { + memcpy(op, ibuf, (size_t)bytes_left); + output(obuf, CHUNK_SIZE); + n -= bytes_left; + memcpy(obuf, ibuf + bytes_left, (size_t)n); + op = obuf + n; + } + } + } +} + +static void output(char *buf, size_t count) +{ + ssize_t n; + + while (count > 0) { + n = write(STDOUT_FILENO, buf, count); + if (n <= 0) { + if (n < 0) fatal(STDOUT); + std_err("cat: standard output: EOF\n"); + exit(1); + } + buf += n; + count -= n; + } +} + +static void report(char *label) +{ + int e = errno; + std_err("cat: "); + std_err(label); + std_err(": "); + std_err(strerror(e)); + std_err("\n"); + excode = 1; +} + +static void fatal(char *label) +{ + report(label); + exit(1); +} diff --git a/commands/simple/cdiff.c b/commands/simple/cdiff.c new file mode 100755 index 000000000..e8eaa0ab9 --- /dev/null +++ b/commands/simple/cdiff.c @@ -0,0 +1,365 @@ +/* cdiff - context diff Author: Larry Wall */ + +/* Cdiff - turns a regular diff into a new-style context diff + * + * Usage: cdiff file1 file2 + */ + +#define PATCHLEVEL 2 + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <limits.h> +#include <stdio.h> + +char buff[512]; + +FILE *inputfp, *oldfp, *newfp; + +int oldmin, oldmax, newmin, newmax; +int oldbeg, oldend, newbeg, newend; +int preoldmax, prenewmax; +int preoldbeg, preoldend, prenewbeg, prenewend; +int oldwanted, newwanted; + +char *oldhunk, *newhunk; +char *progname; +size_t oldsize, oldalloc, newsize, newalloc; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void dumphunk, (void)); +_PROTOTYPE(char *getold, (int targ)); +_PROTOTYPE(char *getnew, (int targ)); +_PROTOTYPE(void *xmalloc, (size_t size)); +_PROTOTYPE(void *xrealloc, (void *ptr, size_t size)); + +#define Nullfp (FILE*)0 +#define Nullch (char*)0 +#define ENOUGH (NAME_MAX + PATH_MAX + 1) +#define CRC_END 12 + +int main(argc, argv) +int argc; +char **argv; +{ + FILE *crcfp; + char *old, *new; + int context = 3; + struct stat statbuf; + register char *s; + char op; + char *newmark, *oldmark; + char sysbuf1[ENOUGH], sysbuf2[ENOUGH]; + int len; + char *line; + int i; + int status; + + progname = argv[0]; + oldalloc = 512; + oldhunk = (char *) xmalloc(oldalloc); + newalloc = 512; + newhunk = (char *) xmalloc(newalloc); + + for (argc--, argv++; argc; argc--, argv++) { + if (argv[0][0] != '-') break; + + if (argv[0][1] == 'c') context = atoi(argv[0] + 2); + } + + if (argc != 2) { + fprintf(stderr, "Usage: cdiff old new\n"); + exit(2); + } + old = argv[0]; + new = argv[1]; + + oldfp = fopen(old, "r"); + if (!oldfp) { + fprintf(stderr, "Can't open %s\n", old); + exit(2); + } + newfp = fopen(new, "r"); + if (!newfp) { + fprintf(stderr, "Can't open %s\n", new); + exit(2); + } + + /* Compute crcs by popen()ing crc and reading the output. Do this before + * popen()ing diff to do the work. popen() attempts to support multiple + * clients, but the 1.3-1.6.24b versions don't succeed. + */ + sprintf(sysbuf1, "crc %s", old); + crcfp = popen(sysbuf1, "r"); + if (!crcfp) { + /* The only advantage of cdiff over diff is that it prints crcs, so + * give up easily if crc fails. + */ + fprintf(stderr, "Can't execute crc %s\n", old); + exit(2); + } + fgets(sysbuf1, sizeof(sysbuf1), crcfp); + sysbuf1[CRC_END] = '\0'; + status = pclose(crcfp); + if (status != 0) { + fprintf(stderr, "crc %s returned bad status %d\n", old, status); + exit(2); + } + sprintf(sysbuf2, "crc %s", new); + crcfp = popen(sysbuf2, "r"); + if (!crcfp) { + fprintf(stderr, "Can't execute crc %s\n", new); + exit(2); + } + fgets(sysbuf2, sizeof(sysbuf2), crcfp); + sysbuf2[CRC_END] = '\0'; + status = pclose(crcfp); + if (status != 0) { + fprintf(stderr, "crc %s returned bad status %d\n", new, status); + exit(2); + } + + sprintf(buff, "diff %s %s 2>/dev/null", old, new); + inputfp = popen(buff, "r"); + if (!inputfp) { + fprintf(stderr, "Can't execute diff %s %s\n", old, new); + exit(2); + } + + fstat(fileno(oldfp), &statbuf); + printf("*** %s crc=%s\t%s", old, sysbuf1, ctime(&statbuf.st_mtime)); + fstat(fileno(newfp), &statbuf); + printf("--- %s crc=%s\t%s", new, sysbuf2, ctime(&statbuf.st_mtime)); + + preoldend = -1000; + + while (fgets(buff, sizeof buff, inputfp) != Nullch) { + if (isdigit(*buff)) { + oldmin = atoi(buff); + for (s = buff; isdigit(*s); s++); + if (*s == ',') { + s++; + oldmax = atoi(s); + for (; isdigit(*s); s++); + } else { + oldmax = oldmin; + } + if (*s != 'a' && *s != 'd' && *s != 'c') { + fprintf(stderr, "Unparseable input: %s\n", s); + exit(2); + } + op = *s; + s++; + newmin = atoi(s); + for (; isdigit(*s); s++); + if (*s == ',') { + s++; + newmax = atoi(s); + for (; isdigit(*s); s++); + } else { + newmax = newmin; + } + if (*s != '\n' && *s != ' ') { + fprintf(stderr, "Unparseable input: %s\n", s); + exit(2); + } + newmark = oldmark = "! "; + if (op == 'a') { + oldmin++; + newmark = "+ "; + } + if (op == 'd') { + newmin++; + oldmark = "- "; + } + oldbeg = oldmin - context; + oldend = oldmax + context; + if (oldbeg < 1) oldbeg = 1; + newbeg = newmin - context; + newend = newmax + context; + if (newbeg < 1) newbeg = 1; + + if (preoldend < oldbeg - 1) { + if (preoldend >= 0) { + dumphunk(); + } + preoldbeg = oldbeg; + prenewbeg = newbeg; + oldwanted = newwanted = 0; + oldsize = newsize = 0; + } else { /* we want to append to previous hunk */ + oldbeg = preoldmax + 1; + newbeg = prenewmax + 1; + } + + for (i = oldbeg; i <= oldmax; i++) { + line = getold(i); + if (!line) { + oldend = oldmax = i - 1; + break; + } + len = strlen(line) + 2; + if (oldsize + len + 1 >= oldalloc) { + oldalloc *= 2; + oldhunk = (char *) xrealloc(oldhunk, oldalloc); + } + if (i >= oldmin) { + strcpy(oldhunk + oldsize, oldmark); + oldwanted++; + } else { + strcpy(oldhunk + oldsize, " "); + } + strcpy(oldhunk + oldsize + 2, line); + oldsize += len; + } + preoldmax = oldmax; + preoldend = oldend; + + for (i = newbeg; i <= newmax; i++) { + line = getnew(i); + if (!line) { + newend = newmax = i - 1; + break; + } + len = strlen(line) + 2; + if (newsize + len + 1 >= newalloc) { + newalloc *= 2; + newhunk = (char *) xrealloc(newhunk, newalloc); + } + if (i >= newmin) { + strcpy(newhunk + newsize, newmark); + newwanted++; + } else { + strcpy(newhunk + newsize, " "); + } + strcpy(newhunk + newsize + 2, line); + newsize += len; + } + prenewmax = newmax; + prenewend = newend; + } + } + + if (preoldend >= 0) { + dumphunk(); + } + status = pclose(inputfp); + if (!WIFEXITED(status)) exit(2); + status = WEXITSTATUS(status); + return(status == 0 || status == 1 ? status : 2); +} + +void dumphunk() +{ + int i; + char *line; + int len; + + for (i = preoldmax + 1; i <= preoldend; i++) { + line = getold(i); + if (!line) { + preoldend = i - 1; + break; + } + len = strlen(line) + 2; + if (oldsize + len + 1 >= oldalloc) { + oldalloc *= 2; + oldhunk = (char *) xrealloc(oldhunk, oldalloc); + } + strcpy(oldhunk + oldsize, " "); + strcpy(oldhunk + oldsize + 2, line); + oldsize += len; + } + for (i = prenewmax + 1; i <= prenewend; i++) { + line = getnew(i); + if (!line) { + prenewend = i - 1; + break; + } + len = strlen(line) + 2; + if (newsize + len + 1 >= newalloc) { + newalloc *= 2; + newhunk = (char *) xrealloc(newhunk, newalloc); + } + strcpy(newhunk + newsize, " "); + strcpy(newhunk + newsize + 2, line); + newsize += len; + } + printf("***************\n"); + if (preoldbeg >= preoldend) { + printf("*** %d ****\n", preoldend); + } else { + printf("*** %d,%d ****\n", preoldbeg, preoldend); + } + if (oldwanted) { + printf("%s", oldhunk); + } + oldsize = 0; + *oldhunk = '\0'; + if (prenewbeg >= prenewend) { + printf("--- %d ----\n", prenewend); + } else { + printf("--- %d,%d ----\n", prenewbeg, prenewend); + } + if (newwanted) { + printf("%s", newhunk); + } + newsize = 0; + *newhunk = '\0'; +} + +char *getold(targ) +int targ; +{ + static int oldline = 0; + + while (fgets(buff, sizeof buff, oldfp) != Nullch) { + oldline++; + if (oldline == targ) return buff; + } + return Nullch; +} + +char *getnew(targ) +int targ; +{ + static int newline = 0; + + while (fgets(buff, sizeof buff, newfp) != Nullch) { + newline++; + if (newline == targ) return buff; + } + return Nullch; +} + +void *xmalloc(size) +size_t size; +{ + void *ptr; + + ptr = malloc(size); + if (ptr == NULL) { + fprintf(stderr, "%s: out of memory\n", progname); + exit(2); + } + return(ptr); +} + +void *xrealloc(ptr, size) +void *ptr; +size_t size; +{ + ptr = realloc(ptr, size); + if (ptr == NULL) { + fprintf(stderr, "%s: out of memory\n", progname); + exit(2); + } + return(ptr); +} diff --git a/commands/simple/cgrep.c b/commands/simple/cgrep.c new file mode 100755 index 000000000..d561e060c --- /dev/null +++ b/commands/simple/cgrep.c @@ -0,0 +1,379 @@ +/* cgrep - grep and display context Author: Mark Mallet */ + +/* + Nov 19 1984 Mark Mallett (mem@zinn.MV.COM) + +mem 860224 Modified to do r/e (regular expression) parsing on unix +mem 860324 Added -f, -n; added code to number lines correctly on output. +mem 870325 Added support for regcmp()/regex() style regular expression + library; redid some conditionals to provide better mix'n'match. +mem 870326 Don't try to print the filename if reading from stdin. + Add -w option. Fix a small problem which occasionally allowed + the separator to come out between adjacent lines of the file. +mem 871119 Fix semantics of call to regcmp(): the NULL terminating the + argument list was missing. It worked, but probably only + due to some bizarre coincidence. +dro 890109 Minor mods to compile under Minix + +*/ + +#define OS_UNIX /* Define this for unix systems */ + /* #define REGEX *//* Define this for re_comp/re_exec library */ +#define REGCMP /* Define this to use regcmp/regex */ + /* #define OS_CPM *//* Define this for CP/M-80 */ + + +/* Don't touch these */ +#define NOREGEXP /* Set this for no regular expression */ +#ifdef REGEX +#undef NOREGEXP +#endif /* REGEX */ + +#ifdef REGCMP +#undef NOREGEXP +#endif /* REGCMP */ + + +#ifdef OS_CPM +#include "stdio.h" +#include "ctype.h" +#endif /* OS_CPM */ + +#ifdef OS_UNIX +#include <sys/types.h> +#include <sys/dir.h> /* Either here or in sys directory - dro */ +#include <ctype.h> +#include <limits.h> /* should have this - dro */ +#include <regexp.h> /* should have this - dro */ +#include <stdlib.h> +#include <stdio.h> +#endif /* OS_UNIX */ + + +/* Local definitions */ + +#ifndef NULL +#define NULL ((char *)0) +#endif /* NULL */ + +#ifndef NUL +#define NUL '\000' +#endif + +#ifndef TRUE +#define TRUE 1 +#define FALSE 0 +#endif + + +/* Internal data declared global */ + + +/* Internal routines */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void dosrch, (char *ifnm)); +_PROTOTYPE(void shwlin, (char *fnm, int linnum, char *line)); +_PROTOTYPE(int matlin, (char *line)); +_PROTOTYPE(void regerror, (char *s)); + +/* External data */ + + +/* Local data */ + +static int Debug = {FALSE}; /* Debug enabled flag */ +static int Lcur = {0}; /* Current line (in Lines array) */ +static char **Lines = {NULL}; /* Lines pointer array */ +static int Linlen = {100}; /* Line length */ +static int Lone = {0}; /* Line one (in Lines array) */ +static int Nmr = {0}; /* Number of matched regions */ +static char *Pat = {NULL}; /* Pattern */ +static char Shwfile = {TRUE}; /* Show file name... */ +static char Shwline = {TRUE}; /* Show line number */ +static int Waft = {0}; /* Window after */ +static int Wbef = {0}; /* Window before */ +static int Wsiz = {0}; /* Window size */ + +regexp *Re; /* Result from reg compilation */ + +int main(argc, argv) +int argc; /* Argument count */ +char **argv; /* Argument values */ + +{ + int i; /* Scratch */ + int n; /* Scratch again */ + int c; /* A character */ + char *aptr; /* Argument pointer */ + int nf; /* number of files on command line */ + + nf = 0; /* No files on line */ + + for (i = 1; i < argc; i++) { /* Look at args */ + if (argv[i][0] != '-') {/* If option */ + if (Pat == NULL) { /* If no pattern yet given */ + Pat = argv[i]; /* point here */ +#ifdef REGEX + if ((Re = re_comp(Pat)) != NULL) { + fprintf(stderr, "cgrep: %s\n", re); + exit(1); + } +#endif /* REGEX */ + +#ifdef REGCMP + if ((Re = regcomp(Pat)) == NULL) { + fprintf(stderr, "cgrep: error in regular expression.\n"); + exit(1); + } +#endif /* REGCMP */ + + } else { /* This must be a file to search */ + nf++; /* Count it */ + dosrch(argv[i]); /* Search */ + } + } else { /* Option char */ + c = argv[i][1]; /* Get option char */ + if (isupper(c)) /* Trap idiot definition of tolower */ + c = tolower(c); /* Don't care about case */ + n = i; + aptr = NULL; /* Find arg, if any */ + if (argv[i][2] != NUL) { + aptr = &argv[i][2]; + n = i; /* Where to set i if we use this arg */ + } else if (i < argc - 1) { /* use next.. */ + n = i + 1; + aptr = argv[n]; + } + switch (c) { /* Process the option */ + case 'a': /* Lines after */ + Waft = atoi(aptr); + Lines = NULL; + i = n; + break; + + case 'b': /* Lines before */ + Wbef = atoi(aptr); + Lines = NULL; + i = n; + break; + +/* Disable debug output + case 'd': Debug = TRUE; break; +*/ + + case 'f': /* Suppress filename on output */ + Shwfile = FALSE; + break; + + case 'l': /* Line length */ + Linlen = atoi(aptr); + Lines = NULL; + i = n; + break; + + case 'n': /* Suppress line number on output */ + Shwline = FALSE; + break; + + case 'w': /* Window: lines before and after */ + Waft = Wbef = atoi(aptr); + Lines = NULL; + i = n; + break; + + default: + fprintf(stderr, "Invalid option %s\n", argv[i]); + exit(1); + } + } + } + + if (Pat == NULL) { /* If no pattern given */ + fprintf(stderr, + "Usage: cgrep [-a n] [-b n] [-f] [-l n] [-n] [-w n] pattern [filename... ]\n"); + exit(1); + } + if (nf == 0) /* No files processed ? */ + dosrch((char *)NULL); /* Do standard input */ + return(0); +} + + /* Dosrch (ifnm) Perform the search + * Accepts : + * + * ifn Input file name + * + * + * Returns : + * + * + */ + +void dosrch(ifnm) +char *ifnm; /* Input filelname */ + +{ + FILE *ifp; /* Input fp */ + char *lptr; /* Line pointer */ + int i; /* Scratch */ + int prtaft; /* Print-after count */ + int linnum; /* Line number */ + int nlb; /* Number of lines buffered */ + + if (ifnm != NULL) { /* If file name given */ + ifp = fopen(ifnm, "r"); /* Open it for read access */ + if (ifp == NULL) { + fprintf(stderr, "Can not open file %s\n", ifnm); + return; + } + } else + ifp = stdin; + + if (Lines == NULL) { /* If no line table allocated.. */ + Wsiz = Wbef + 2; /* Determine total window size */ + Lines = (char **) calloc((size_t)Wsiz, sizeof(char *)); + /* Allocate pointer table */ + for (i = 0; i < Wsiz; i++) /* Allocate line buffers */ + Lines[i] = (char *) calloc((size_t)Linlen, sizeof(char)); + } + Lcur = Lone = 0; /* Setup line pointers */ + nlb = 0; /* No lines buffered */ + linnum = 0; /* Line number is zero */ + prtaft = -(Wbef + 1); /* Make sure separator given first time */ + + for (;;) { /* Loop through the file */ + lptr = Lines[Lcur]; /* Get pointer to current line */ + if (++Lcur == Wsiz) /* Bump curr pointer and wrap */ + Lcur = 0; /* if hit end */ + if (Lone == Lcur) /* If wrapped to beginning of window */ + if (++Lone == Wsiz) /* Bump beginning */ + Lone = 0; /* and wrap if hit end */ + + if (fgets(lptr, Linlen, ifp) != lptr) break; /* if end of file */ + + linnum++; /* Count line number */ + if (matlin(lptr)) { /* If matching line */ + if (prtaft < (-Wbef)) /* Check for separator needed */ + if ((Nmr++ > 0) && ((Wbef > 0) || (Waft > 0))) + printf("----------------------------------------------------------------------------\n"); + while (Lone != Lcur) { /* Until we close the window */ + shwlin(ifnm, linnum - nlb, Lines[Lone]); + /* Show the line */ + if (++Lone == Wsiz) Lone = 0; + nlb--; + } + nlb = 0; /* No lines buffered */ + prtaft = Waft; /* Print n lines after */ + } else { /* Didn't match */ + if (prtaft-- > 0) { /* If must print lines after */ + shwlin(ifnm, linnum, lptr); + /* Show the line */ + Lone = Lcur; /* Match pointers */ + } else if (nlb < Wbef) /* Count lines buffered */ + nlb++; + } + } + + if (ifnm != NULL) fclose(ifp); +} + + /* Shwlin (fnm, linnum, line) Show a matching line + * + * Accepts : + * + * fnm File name + * + * linnum Line number + * + * line Line to show + * + * + * Returns : + * + * + */ + +void shwlin(fnm, linnum, line) +char *fnm; /* File name */ +int linnum; /* Line number */ +char *line; /* Line (with newline at end) to print */ + +{ + if (Shwfile && (fnm != NULL)) printf("%s%s", fnm, Shwline ? " " : ":"); + if (Shwline) printf("@%05d:", linnum); + printf("%s", line); +} + + /* Matlin (line) Perform match against pattern and line + * + * Accepts : + * + * line Address of line to match + * + * + * Returns : + * + * <value> TRUE if match FALSE if not + * + * + */ + + +int matlin(line) +char *line; /* Line to match */ + +{ + int rtncode; /* Return value from this routine */ + + +#ifdef NOREGEXP + char *pptr, *lptr, *tlptr; + int c1, c2; +#endif /* NOREGEXP */ + + if (Debug) printf("Matching %s against %s", Pat, line); + +#ifdef REGEX + rtncode = re_exec(line); /* Hand off to r/e evaluator */ +#endif /* REGEX */ + +#ifdef REGCMP + rtncode = (regexec(Re, line, TRUE) != 0); +#endif /* REGCMP */ + +#ifdef NOREGEX /* Have to do menial comparison.. */ + lptr = line; /* Init line pointer */ + + for (rtncode = -1; rtncode < 0;) { + tlptr = lptr++; /* Get temp ptr to line */ + pptr = Pat; /* Get ptr to pattern */ + while (TRUE) { + if ((c1 = *pptr++) == NUL) { + rtncode = 1; /* GOOD return value */ + break; + } + if ((c2 = *tlptr++) == NUL) { + rtncode = 0; /* BAD return value */ + break; + } + if (isupper(c1)) c1 = tolower(c1); + if (isupper(c2)) c2 = tolower(c2); + if (c1 != c2) break; + } + } +#endif /* NOREGEX */ + + + if (Debug) printf("matlin returned %s\n", rtncode ? "TRUE" : "FALSE"); + return(rtncode); +} + + + +void regerror(s) +char *s; +{ + printf("%s\n", s); + exit(1); +} diff --git a/commands/simple/chmem.c b/commands/simple/chmem.c new file mode 100755 index 000000000..07da64c0d --- /dev/null +++ b/commands/simple/chmem.c @@ -0,0 +1,140 @@ +/* chmem - set total memory size for execution Author: Andy Tanenbaum */ + +#include <minix/config.h> +#include <sys/types.h> +#include <a.out.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#define MAX_8086 0x10000L /* maximum allocation size for 8086 */ +#define MAX_386 0x7FFFFFFFL /* etc */ +#define MAX_68K 0x7FFFFFFFL +#define MAX_SPARC 0x20000000L /* No more than 512MB on a SparcStation! */ + +char *progname; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void error, (char *s1, char *s2)); +_PROTOTYPE(void usage, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ +/* The 8088 architecture does not make it possible to catch stacks that grow + * big. The only way to deal with this problem is to let the stack grow down + * towards the data segment and the data segment grow up towards the stack. + * Normally, a total of 64K is allocated for the two of them, but if the + * programmer knows that a smaller amount is sufficient, he can change it + * using chmem. + * + * chmem =4096 prog sets the total space for stack + data growth to 4096 + * chmem +200 prog increments the total space for stack + data growth by 200 + */ + + char *p; + int fd, separate; + size_t s; + long lsize, olddynam, newdynam, newtot, overflow; + struct exec exec; + char cpu; + long max; + + progname = argv[0]; + if (argc < 3) usage(); + p = argv[1]; + if (*p != '=' && *p != '+' && *p != '-') usage(); + lsize = atol(p + 1); + s = sizeof(struct exec); + + if (lsize < 0) { + error(p + 1, "is negative"); + exit(1); + } + argc -= 1; + argv += 1; + + while (--argc) { + ++argv; + fd = open(*argv, O_RDWR); + if (fd < 0) { + error("can't open", *argv); + continue; + } + if (read(fd, (char *) &exec, s) != s) { + error("can't read header in", *argv); + continue; + } + if (BADMAG(exec)) { + error(*argv, "is not executable"); + continue; + } + separate = (exec.a_flags & A_SEP ? 1 : 0); + cpu = exec.a_cpu; + +#if (CHIP == M68000) + if (cpu == A_I8086) cpu = A_M68K; +#endif + + switch (cpu) { + case A_I8086: max = MAX_8086; break; + case A_I80386: max = MAX_386; break; + case A_M68K: max = MAX_68K; break; + case A_SPARC: max = MAX_SPARC; break; + default: + error("bad CPU type in", *argv); + continue; + } + + if (lsize > max) { + error("size is too large for", *argv); + continue; + } + olddynam = exec.a_total - exec.a_data - exec.a_bss; + if (separate == 0) olddynam -= exec.a_text; + + if (*p == '=') + newdynam = lsize; + else if (*p == '+') + newdynam = olddynam + lsize; + else if (*p == '-') + newdynam = olddynam - lsize; + + newtot = exec.a_data + exec.a_bss + newdynam; + if (separate == 0) newtot += exec.a_text; + overflow = (newtot > max ? newtot - max : 0); + newdynam -= overflow; + newtot -= overflow; + exec.a_total = newtot; + lseek(fd, (long) 0, SEEK_SET); + if (write(fd, (char *) &exec, s) != s) { + error("can't modify", *argv); + continue; + } + printf("%s: Stack+malloc area changed from %ld to %ld bytes.\n", + *argv, olddynam, newdynam); + close(fd); + } + return(0); +} + +void error(s1, s2) +char *s1; +char *s2; +{ + fprintf(stderr, "%s: %s ", progname, s1); + if (errno != 0) + perror(s2); + else + fprintf(stderr, "%s\n", s2); + errno = 0; +} + +void usage() +{ + fprintf(stderr, "Usage: %s {=+-} amount file\n", progname); + exit(1); +} diff --git a/commands/simple/chmod.c b/commands/simple/chmod.c new file mode 100755 index 000000000..afc040c96 --- /dev/null +++ b/commands/simple/chmod.c @@ -0,0 +1,256 @@ +/* chmod - Change file modes Author: V. Archer */ + +/* Copyright 1991 by Vincent Archer + * You may freely redistribute this software, in source or binary + * form, provided that you do not alter this copyright mention in any + * way. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <minix/minlib.h> +#include <stdio.h> + +#ifndef S_ISLNK +#define S_ISLNK(mode) 0 +#define lstat stat +#endif + +#define USR_MODES (S_ISUID|S_IRWXU) +#define GRP_MODES (S_ISGID|S_IRWXG) +#define EXE_MODES (S_IXUSR|S_IXGRP|S_IXOTH) +#ifdef S_ISVTX +#define ALL_MODES (USR_MODES|GRP_MODES|S_IRWXO|S_ISVTX) +#else +#define ALL_MODES (USR_MODES|GRP_MODES|S_IRWXO) +#endif + + +/* Common variables */ +char *symbolic; +mode_t new_mode, u_mask; +int rflag, errors; +struct stat st; +char path[PATH_MAX + 1]; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(mode_t parsemode, (char *symbolic, Mode_t oldmode)); +_PROTOTYPE(int do_change, (char *name)); +_PROTOTYPE(void usage, (void)); + +/* Parse a P1003.2 4.7.7-conformant symbolic mode. */ +mode_t parsemode(symbolic, oldmode) +char *symbolic; +mode_t oldmode; +{ + mode_t who, mask, newmode, tmpmask; + char action; + + newmode = oldmode & ALL_MODES; + while (*symbolic) { + who = 0; + for (; *symbolic; symbolic++) { + if (*symbolic == 'a') { + who |= ALL_MODES; + continue; + } + if (*symbolic == 'u') { + who |= USR_MODES; + continue; + } + if (*symbolic == 'g') { + who |= GRP_MODES; + continue; + } + if (*symbolic == 'o') { + who |= S_IRWXO; + continue; + } + break; + } + if (!*symbolic || *symbolic == ',') usage(); + while (*symbolic) { + if (*symbolic == ',') break; + switch (*symbolic) { + default: + usage(); + case '+': + case '-': + case '=': action = *symbolic++; + } + mask = 0; + for (; *symbolic; symbolic++) { + if (*symbolic == 'u') { + tmpmask = newmode & S_IRWXU; + mask |= tmpmask | (tmpmask << 3) | (tmpmask << 6); + symbolic++; + break; + } + if (*symbolic == 'g') { + tmpmask = newmode & S_IRWXG; + mask |= tmpmask | (tmpmask >> 3) | (tmpmask << 3); + symbolic++; + break; + } + if (*symbolic == 'o') { + tmpmask = newmode & S_IRWXO; + mask |= tmpmask | (tmpmask >> 3) | (tmpmask >> 6); + symbolic++; + break; + } + if (*symbolic == 'r') { + mask |= S_IRUSR | S_IRGRP | S_IROTH; + continue; + } + if (*symbolic == 'w') { + mask |= S_IWUSR | S_IWGRP | S_IWOTH; + continue; + } + if (*symbolic == 'x') { + mask |= EXE_MODES; + continue; + } + if (*symbolic == 's') { + mask |= S_ISUID | S_ISGID; + continue; + } + if (*symbolic == 'X') { + if (S_ISDIR(oldmode) || (oldmode & EXE_MODES)) + mask |= EXE_MODES; + continue; + } +#ifdef S_ISVTX + if (*symbolic == 't') { + mask |= S_ISVTX; + who |= S_ISVTX; + continue; + } +#endif + break; + } + switch (action) { + case '=': + if (who) + newmode &= ~who; + else + newmode = 0; + case '+': + if (who) + newmode |= who & mask; + else + newmode |= mask & (~u_mask); + break; + case '-': + if (who) + newmode &= ~(who & mask); + else + newmode &= ~mask | u_mask; + } + } + if (*symbolic) symbolic++; + } + return(newmode); +} + + +/* Main module. The single option possible (-R) does not warrant a call to + * the getopt() stuff. + */ +int main(argc, argv) +int argc; +char *argv[]; +{ + int ex_code = 0; + + argc--; + argv++; + + if (argc && strcmp(*argv, "-R") == 0) { + argc--; + argv++; + rflag = 1; + } else + rflag = 0; + + if (!argc--) usage(); + if (!strcmp(argv[0], "--")) { /* Allow chmod -- -r, as in Draft11 example */ + if (!argc--) usage(); + argv++; + } + symbolic = *argv++; + if (!argc) usage(); + + if (*symbolic >= '0' && *symbolic <= '7') { + new_mode = 0; + while (*symbolic >= '0' && *symbolic <= '7') + new_mode = (new_mode << 3) | (*symbolic++ & 07); + if (*symbolic) usage(); + new_mode &= ALL_MODES; + symbolic = (char *) 0; + } else + u_mask = umask(0); + + while (argc--) + if (do_change(*argv++)) ex_code = 1; + return(ex_code); +} + + +/* Apply a mode change to a given file system element. */ +int do_change(name) +char *name; +{ + mode_t m; + DIR *dirp; + struct dirent *entp; + char *namp; + + if (lstat(name, &st)) { + perror(name); + return(1); + } + if (S_ISLNK(st.st_mode) && rflag) return(0); /* Note: violates POSIX. */ + if (!symbolic) + m = new_mode; + else + m = parsemode(symbolic, st.st_mode); + if (chmod(name, m)) { + perror(name); + errors = 1; + } else + errors = 0; + + if (S_ISDIR(st.st_mode) && rflag) { + if (!(dirp = opendir(name))) { + perror(name); + return(1); + } + if (name != path) strcpy(path, name); + namp = path + strlen(path); + *namp++ = '/'; + while (entp = readdir(dirp)) + if (entp->d_name[0] != '.' || + (entp->d_name[1] && + (entp->d_name[1] != '.' || entp->d_name[2]))) { + strcpy(namp, entp->d_name); + errors |= do_change(path); + } + closedir(dirp); + *--namp = '\0'; + } + return(errors); +} + + +/* Display Posix prototype */ +void usage() +{ + std_err("Usage: chmod [-R] mode file...\n"); + exit(1); +} diff --git a/commands/simple/chown.c b/commands/simple/chown.c new file mode 100755 index 000000000..a6a0f04ff --- /dev/null +++ b/commands/simple/chown.c @@ -0,0 +1,168 @@ +/* chown/chgrp - Change file ownership Author: V. Archer */ + +/* Copyright 1991 by Vincent Archer + * You may freely redistribute this software, in source or binary + * form, provided that you do not alter this copyright mention in any + * way. + */ + +/* Changed 3 Feb 93 by Kees J. Bot: setuid execution nonsense removed. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <ctype.h> +#include <dirent.h> +#include <pwd.h> +#include <grp.h> +#include <string.h> +#include <limits.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <minix/minlib.h> +#include <stdio.h> + +#ifndef S_ISLNK +#define S_ISLNK(mode) 0 +#define lstat stat +#endif + +#define S_IUGID (S_ISUID|S_ISGID) + +/* Global variables, such as flags and path names */ +int gflag, oflag, rflag, error; +char *pgmname, path[PATH_MAX + 1]; +uid_t nuid; +gid_t ngid; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void do_chown, (char *file)); +_PROTOTYPE(void usage, (void)); + +/* Main module. If chown(1) is invoked as chgrp(1), the behaviour is nearly + * identical, except that the default when a single name is given as an + * argument is to take a group id rather than an user id. This allow the + * non-Posix "chgrp user:group file". + * The single option switch used by chown/chgrp (-R) does not warrant a + * call to the getopt stuff. The two others flags (-g, -u) are set from + * the program name and arguments. + */ +int main(argc, argv) +int argc; +char *argv[]; +{ + char *id, *id2; + struct group *grp; + struct passwd *pwp; + + if (pgmname = strrchr(*argv, '/')) + pgmname++; + else + pgmname = *argv; + argc--; + argv++; + gflag = strcmp(pgmname, "chgrp"); + + if (argc && **argv == '-' && argv[0][1] == 'R') { + argc--; + argv++; + rflag = 1; + } + if (argc < 2) usage(); + + id = *argv++; + argc--; + if (id2 = strchr(id, ':')) *id2++ = '\0'; + if (!id2 && !gflag) { + id2 = id; + id = 0; + } + if (id) { + if (isdigit(*id)) + nuid = atoi(id); + else { + if (!(pwp = getpwnam(id))) { + std_err(id); + std_err(": unknown user name\n"); + exit(1); + } + nuid = pwp->pw_uid; + } + oflag = 1; + } else + oflag = 0; + + if (id2) { + if (isdigit(*id2)) + ngid = atoi(id2); + else { + if (!(grp = getgrnam(id2))) { + std_err(id2); + std_err(": unknown group name\n"); + exit(1); + } + ngid = grp->gr_gid; + } + gflag = 1; + } else + gflag = 0; + + error = 0; + while (argc--) do_chown(*argv++); + return(error); +} + +/* Apply the user/group modification here. + */ +void do_chown(file) +char *file; +{ + DIR *dirp; + struct dirent *entp; + char *namp; + struct stat st; + + if (lstat(file, &st)) { + perror(file); + error = 1; + return; + } + + if (S_ISLNK(st.st_mode) && rflag) return; /* Note: violates POSIX. */ + + if (chown(file, oflag ? nuid : st.st_uid, gflag ? ngid : st.st_gid)) { + perror(file); + error = 1; + } + + if (S_ISDIR(st.st_mode) && rflag) { + if (!(dirp = opendir(file))) { + perror(file); + error = 1; + return; + } + if (path != file) strcpy(path, file); + namp = path + strlen(path); + *namp++ = '/'; + while (entp = readdir(dirp)) + if (entp->d_name[0] != '.' || + (entp->d_name[1] && + (entp->d_name[1] != '.' || entp->d_name[2]))) { + strcpy(namp, entp->d_name); + do_chown(path); + } + closedir(dirp); + *--namp = '\0'; + } +} + +/* Posix prototype of the chown/chgrp function */ +void usage() +{ + std_err("Usage: "); + std_err(pgmname); + std_err(gflag ? " owner[:group]" : " [owner:]group"); + std_err(" file...\n"); + exit(1); +} diff --git a/commands/simple/chroot.c b/commands/simple/chroot.c new file mode 100644 index 000000000..3e628a586 --- /dev/null +++ b/commands/simple/chroot.c @@ -0,0 +1,23 @@ + +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> + +int +main(int argc, char *argv[]) +{ + if(argc != 3) { + fprintf(stderr, "usage: %s <root> <command>\n", argv[0]); + return 1; + } + + if(chroot(argv[1]) < 0) { + perror("chroot"); + return 1; + } + + system(argv[2]); + + return 0; +} + diff --git a/commands/simple/ci.c b/commands/simple/ci.c new file mode 100755 index 000000000..cfff38fa1 --- /dev/null +++ b/commands/simple/ci.c @@ -0,0 +1,343 @@ +/* ci - check in Author: Peter S. Housel 12/17/87 */ + +#include <sys/types.h> +#include <string.h> +#include <sys/stat.h> +#include <pwd.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <stdio.h> + +#define SUFFIX ",S" /* svc indicator */ +#define SVCDIR "SVC" /* svc postfix indicator */ + +#define LINELEN 256 /* maximum line length */ + +#ifndef PATCH +#define FIX "fix $1 Fix.$1 > New.$1; mv New.$1 $1\n" +#else +#define FIX "patch -n -s $1 < Fix.$1; rm -f $1.orig\n" +#endif /* !PATCH */ + +#ifdef MAXPATHLEN +#define PATHLEN MAXPATHLEN +#else +#define PATHLEN 128 /* buffer length for filenames */ +#endif + +int unlocked = 0; /* leave unlocked after checkin */ +int relock = 0; /* lock next revision after checkin */ +char file[PATHLEN]; /* file to be checked in */ +char svc[PATHLEN]; /* filename for svc file */ +char newsvc[PATHLEN]; /* new copy of SVC file */ +char line[LINELEN]; /* temporary line buffer */ +char *p; /* scratch character pointer */ + +FILE *svcfp; /* svc file */ +FILE *origfp, *newfp; /* "orig" and "new" temp files */ +FILE *srcfp; /* source file */ +int rev; /* new revision number */ +int status; /* wait() buffer */ +struct stat stb1, stb2; /* stat buffers for size compare */ +char original[] = "/tmp/cioXXXXXX"; /* previous revision */ +char diffout[] = "/tmp/cidXXXXXX"; /* diffs */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void rundiff, (void)); +_PROTOTYPE(void logmsg, (FILE *fp)); +_PROTOTYPE(void fname, (char *src, char *dst)); +_PROTOTYPE(void svcname, (char *src, char *dst)); +_PROTOTYPE(int lockcheck, (FILE *fp, int rev)); +_PROTOTYPE(void onintr, (int dummy)); +_PROTOTYPE(void clean, (void)); +_PROTOTYPE(char *whoami, (void)); + +int main(argc, argv) +int argc; +char **argv; +{ +#ifdef perprintf + char errbuf[BUFSIZ]; + setbuf(stderr, errbuf); + perprintf(stderr); +#endif + + while (++argv, --argc) { + if ('-' == (*argv)[0]) { + if ('u' == (*argv)[1]) + ++unlocked; + else if ('l' == (*argv)[1]) + ++relock; + else { + fprintf(stderr, "ci: illegal option -%c\n", (*argv)[1]); + exit(1); + } + } else + break; + } + + if (1 != argc) { + fprintf(stderr, "ci: bad number of files arguments\n"); + exit(1); + } + fname(*argv, file); + svcname(file, svc); + + fprintf(stderr, "%s -> %s\n", file, svc); + + signal(SIGHUP, onintr); + signal(SIGINT, onintr); + signal(SIGTERM, onintr); + +#ifndef BSD + if (NULL == (p = strrchr(file, '/'))) + p = file; + else + ++p; + + if (strlen(p) > 13) { + fprintf(stderr, "ci: filename %s is too long\n", p); + exit(1); + } +#endif /* !BSD */ + + strcpy(newsvc, svc); + *(strrchr(newsvc, ',')) = ';'; /* temporary file will be "file;S" */ + + if (NULL == (newfp = fopen(newsvc, "w"))) { + perror("ci: can't create SVC temporary"); + exit(1); + } + (void) mktemp(original); + (void) mktemp(diffout); + + if (NULL != (svcfp = fopen(svc, "r"))) { /* does svc-file exist? */ + fgets(line, LINELEN, svcfp); + if (1 != sscanf(line, "# %d", &rev)) { + fprintf(stderr, "ci: %s: illegal SVC file header\n", svc); + exit(1); + } + ++rev; + + if (!lockcheck(svcfp, rev)) { + fprintf(stderr, "Revision %d not locked\n", rev); + clean(); + exit(1); + } + if (NULL == (origfp = fopen(original, "w"))) { + fprintf(stderr, "ci: can't create %s", original); + perror(" "); + } + fgets(line, LINELEN, svcfp); /* skip "cat <<***MAIN-eof***" line */ + + while (NULL != fgets(line, LINELEN, svcfp) + && strcmp(line, "***MAIN-eof***\n")) { + fputs(line, origfp); + if (ferror(origfp)) { + perror("ci: origfile"); + exit(1); + } + } + fclose(origfp); + + rundiff(); + + if (0 != stat(original, &stb1) || 0 != stat(diffout, &stb2)) { + perror("ci: can't stat original or diffout"); + clean(); + exit(1); + } + } else { /* no - create one */ + rev = 1; + } + + fprintf(newfp, "# %d\n", rev); + fprintf(newfp, "cat <<***MAIN-eof*** >$1\n"); + if (NULL == (srcfp = fopen(file, "r"))) { + perror("ci: can't read source file"); + clean(); + exit(1); + } + while (NULL != fgets(line, LINELEN, srcfp)) fputs(line, newfp); + fclose(srcfp); + fputs("***MAIN-eof***\n", newfp); + + if (rev > 1) { + fprintf(newfp, "if test $2 -ge %d ; then rm -f Fix.$1 ; exit 0 ; fi ; cat <<***%d-eof*** >Fix.$1\n", rev, rev); + p = (stb1.st_size <= stb2.st_size) ? original : diffout; + if (NULL == (origfp = fopen(p, "r"))) { + perror("can't open diff output file"); + clean(); + exit(1); + } + while (NULL != fgets(line, LINELEN, origfp)) fputs(line, newfp); + fclose(origfp); + fprintf(newfp, "***%d-eof***\n", rev); + fputs((original == p) ? "mv Fix.$1 $1\n" : FIX, newfp); + logmsg(newfp); + while (NULL != fgets(line, LINELEN, svcfp) && strncmp(line, "#***SVCLOCK***", (size_t)14)) + fputs(line, newfp); + } else { + logmsg(newfp); + fputs("rm -f Fix.$1\n", newfp); + } + + if (relock) { + fprintf(stderr, "(relocking into revision %d)\n", rev + 1); + fprintf(newfp, "#***SVCLOCK*** %s %d\n", whoami(), rev + 1); + } + signal(SIGHUP, SIG_IGN); /* disable during critical section */ + signal(SIGINT, SIG_IGN); + + if (ferror(newfp) || fclose(newfp) || ((rev > 1) && unlink(svc)) + || link(newsvc, svc)) { + fprintf(stderr, "SVC file write/link error - Checkin aborted\n"); + clean(); + exit(1); + } else + fprintf(stderr, "Checkin complete.\n"); + + if (stat(svc, &stb1) < 0 || chmod(svc, stb1.st_mode & 0555) < 0) + perror("ci: can't chmod SVC file"); + + if (unlocked) { + if (stat(file, &stb1) < 0 || chmod(file, stb1.st_mode & 0555) < 0) + perror("ci: can't chmod source file"); + } else if (relock) { + if (stat(file, &stb1) < 0 || chmod(file, stb1.st_mode | 0200) < 0) + perror("ci: can't chmod source file"); + } else + unlink(file); + + clean(); + return(0); +} + +void rundiff() +{ /* do "diff file original > diffout" */ + int fd; /* redirected output file */ + + switch (fork()) { + case -1: + perror("ci: fork"); /* error */ + clean(); + exit(1); + + case 0: /* child */ + if ((fd = creat(diffout, 0600)) < 0 || -1 == dup2(fd, 1)) { + perror("ci: diffout"); + clean(); + exit(1); + } + close(fd); + execlp("diff", "diff", file, original, (char *) 0); + perror("ci: exec diff failed"); + exit(1); + + default: break; /* parent */ +} + wait(&status); + if (0 != status && 1 << 8 != status) { + fprintf(stderr, "ci: bad return status (0x%x) from diff\n", status); + clean(); + exit(1); + } +} + +void logmsg(fp) +FILE *fp; +{ + long now; + + time(&now); + fprintf(stderr, "Enter log message for revision %d (end with ^D or '.'):\n", rev); + fprintf(fp, "#***SVC*** revision %d %s %s", rev, file, ctime(&now)); + while (NULL != gets(line) && strcmp(line, ".")) + fprintf(fp, "#***SVC*** %s\n", line); +} + +void fname(src, dst) +char *src, *dst; +{ + char *p; + strcpy(dst, src); + p = &dst[strlen(src) - strlen(SUFFIX)]; + if (!strcmp(p, SUFFIX)) *p = '\0'; +} + +void svcname(src, dst) +char *src, *dst; +{ + char *p; + + strcpy(dst, src); + strcat(dst, SUFFIX); + + if (0 != access(dst, 4)) { + char dirname[PATHLEN]; + if (NULL != (p = strrchr(src, '/'))) + strncpy(dirname, src, (size_t)(p - src + 1)); + else + dirname[0] = '\0'; + strcat(dirname, SVCDIR); + + if (0 == access(dirname, 1)) { + strcpy(dst, dirname); + if (NULL == p) { + strcat(dst, "/"); + strcat(dst, src); + } else + strcat(dst, p); + strcat(dst, SUFFIX); + } + } +} + +int lockcheck(fp, rev) +FILE *fp; +int rev; +{ + char lock[40], check[40]; + long pos; + int ret; + + sprintf(lock, "#***SVCLOCK*** %s %d\n", whoami(), rev); + + pos = ftell(fp); + fseek(fp, -((long) strlen(lock)), 2); + fgets(check, 40, fp); + ret = (0 == strcmp(lock, check)); + fseek(fp, pos, 0); + + return ret; +} + +void onintr(dummy) +int dummy; /* to keep the compiler happy */ +{ + fprintf(stderr, "Interrupt - Aborting checkin, cleaning up\n"); + clean(); + exit(1); +} + +void clean() +{ + if (strlen(original)) /* if only more programs made this check! */ + unlink(original); + if (strlen(diffout)) unlink(diffout); + if (strlen(newsvc)) unlink(newsvc); +} + +char *whoami() +{ + struct passwd *pw; + + if (NULL != (pw = getpwuid(getuid()))) + return pw->pw_name; + else + return "nobody"; +} diff --git a/commands/simple/cksum.c b/commands/simple/cksum.c new file mode 100755 index 000000000..e6a40376b --- /dev/null +++ b/commands/simple/cksum.c @@ -0,0 +1,155 @@ +/* cksum.c - Display file checksums and block counts Author: V. Archer */ + +/* Copyright 1991 by Vincent Archer + * You may freely redistribute this software, in source or binary + * form, provided that you do not alter this copyright mention in any + * way. + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> + +int error; + +/* Table from P1003.2 (4.9/Fig 4.1). In fact, this table was taken from zmodem + * and rewritten to look like the Draft 11 example. + */ +unsigned long crctab[] = { + 0x7fffffff, + 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, + 0xe963a535, 0x9e6495a3, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, + 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, + 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, + 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, 0x136c9856, 0x646ba8c0, + 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, + 0x8d080df5, 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, + 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, 0x35b5a8fa, + 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, + 0xdcd60dcf, 0xabd13d59, 0x26d930ac, 0x51de003a, 0xc8d75180, + 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, + 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, + 0x58684c11, 0xc1611dab, 0xb6662d3d, 0x76dc4190, 0x01db7106, + 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, + 0xe8b8d433, 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, + 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, 0x6b6b51f4, + 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, + 0x8208f4c1, 0xf50fc457, 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, + 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, + 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, + 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, 0x4369e96a, 0x346ed9fc, + 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, + 0xdd0d7cc9, 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, + 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, 0x5edef90e, + 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, + 0xb7bd5c3b, 0xc0ba6cad, 0xedb88320, 0x9abfb3b6, 0x03b6e20c, + 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, + 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, + 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, 0xf00f9344, 0x8708a3d2, + 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, + 0x6e6b06e7, 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, + 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, 0xd6d6a3e8, + 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, + 0x3fb506dd, 0x48b2364b, 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, + 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, + 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, + 0xbb0b4703, 0x220216b9, 0x5505262f, 0xc5ba3bbe, 0xb2bd0b28, + 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, + 0x5bdeae1d, 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, + 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, 0x95bf4a82, + 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, + 0x7cdcefb7, 0x0bdbdf21, 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, + 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, + 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, + 0xf862ae69, 0x616bffd3, 0x166ccf45, 0xa00ae278, 0xd70dd2ee, + 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, + 0x3e6e77db, 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, + 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, 0xbdbdf21c, + 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, + 0x54de5729, 0x23d967bf, 0xb3667a2e, 0xc4614ab8, 0x5d681b02, + 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void crc, (int fd, char *name)); +_PROTOTYPE(unsigned long strncrc, (unsigned char *b, int n, unsigned long s)); + +static int aux; + +/* Routine straight out of 4.9.10 */ +unsigned long strncrc(b, n, s) +register unsigned char *b; /* byte sequence to checksum */ +register int n; /* length of sequence */ +register unsigned long s; /* initial checksum value */ +{ + register int i; + + while (n-- > 0) { + /* Compute the index to the crc table */ + i = (s >> 24) ^ ((unsigned int) (*b++)); + + if (i == 0) { + /* Replace an intermediate zero with the next value + * from the sequence */ + i = aux++; + if (aux >= sizeof(crctab) / sizeof(crctab[0])) aux = 0; + } + + /* New checksum value */ + s = (s << 8) ^ crctab[i]; + } + return(s); +} + +/* Main module. No options switches allowed, none parsed. */ +int main(argc, argv) +int argc; +char *argv[]; +{ + argc--; + error = 0; + if (!argc) + crc(0, (char *) 0); + else + for (argv++; argc--; argv++) crc(open(*argv, O_RDONLY), *argv); + return(error); +} + +/* Compute crc and size of input file descriptor. */ +void crc(fd, name) +int fd; +char *name; +{ + off_t f_size; + unsigned long crc; + int nb; + unsigned char buffer[1024]; + + if (fd < 0) { + perror(name); + error = 1; + return; + } + crc = 0; + f_size = 0; + aux = 0; + for (;;) { + nb = read(fd, (char *) buffer, sizeof(buffer)); + if (nb < 0) { + close(fd); + perror(name ? name : "stdin"); + error = 1; + return; + } + if (!nb) break; + f_size += nb; + crc = strncrc(buffer, nb, crc); + } + close(fd); + printf("%lu %ld", crc, f_size); + if (name) + printf(" %s\n", name); + else + putchar('\n'); +} diff --git a/commands/simple/cleantmp.c b/commands/simple/cleantmp.c new file mode 100755 index 000000000..b98c547a1 --- /dev/null +++ b/commands/simple/cleantmp.c @@ -0,0 +1,361 @@ +/* cleantmp 1.6 - clean out a tmp dir. Author: Kees J. Bot + * 11 Apr 1991 + */ +#define nil 0 +#include <sys/types.h> +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <string.h> +#include <time.h> +#include <dirent.h> +#include <errno.h> + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +#ifndef S_ISLNK +/* There were no symlinks in medieval times. */ +#define lstat stat +#endif + +#ifndef DEBUG +#define NDEBUG +#endif +#include <assert.h> + +#define SEC_DAY (24 * 3600L) /* A full day in seconds */ +#define DOTDAYS 14 /* Don't remove tmp/.* in at least 14 days. */ + +void report(const char *label) +{ + fprintf(stderr, "cleantmp: %s: %s\n", label, strerror(errno)); +} + +void fatal(const char *label) +{ + report(label); + exit(1); +} + +void *alloc(size_t s) +{ + void *mem; + + if ((mem= (void *) malloc(s)) == nil) fatal(""); + return mem; +} + +int force= 0; /* Force remove all. */ +int debug= 0; /* Debug level. */ + +void days2time(unsigned long days, time_t *retired, time_t *dotretired) +{ + struct tm *tm; + time_t t; + + time(&t); + + tm= localtime(&t); + tm->tm_hour= 0; + tm->tm_min= 0; + tm->tm_sec= 0; /* Step back to midnight of this day. */ + t= mktime(tm); + + if (t < (days - 1) * SEC_DAY) { + *retired= *dotretired= 0; + } else { + *retired= t - (days - 1) * SEC_DAY; + *dotretired= t - (DOTDAYS - 1) * SEC_DAY; + if (*dotretired > *retired) *dotretired= *retired; + } + if (debug >= 2) fprintf(stderr, "Retired: %s", ctime(retired)); + if (debug >= 2) fprintf(stderr, "Dotretired: %s", ctime(dotretired)); +} + +/* Path name construction, addpath adds a component, delpath removes it. + * The string 'path' is used throughout the program as the file under + * examination. + */ + +char *path; /* Path name constructed in path[]. */ +int plen= 0, pidx= 0; /* Lenght/index for path[]. */ + +void addpath(int *didx, char *name) +/* Add a component to path. (name may also be a full path at the first call) + * The index where the current path ends is stored in *pdi. + */ +{ + if (plen == 0) path= (char *) alloc((plen= 32) * sizeof(path[0])); + + *didx= pidx; /* Record point to go back to for delpath. */ + + if (pidx > 0 && path[pidx-1] != '/') path[pidx++]= '/'; + + do { + if (*name != '/' || pidx == 0 || path[pidx-1] != '/') { + if (pidx == plen && + (path= (char *) realloc((void *) path, + (plen*= 2) * sizeof(path[0]))) == nil + ) fatal(""); + path[pidx++]= *name; + } + } while (*name++ != 0); + + --pidx; /* Put pidx back at the null. The path[pidx++]= '/' + * statement will overwrite it at the next call. + */ + assert(pidx < plen); +} + +void delpath(int didx) +{ + assert(0 <= didx); + assert(didx <= pidx); + path[pidx= didx]= 0; +} + +struct file { + struct file *next; + char *name; +}; + +struct file *listdir(void) +{ + DIR *dp; + struct dirent *entry; + struct file *first, **last= &first; + + if ((dp= opendir(path)) == nil) { + report(path); + return nil; + } + + while ((entry= readdir(dp)) != nil) { + struct file *new; + + if (strcmp(entry->d_name, ".") == 0 + || strcmp(entry->d_name, "..") == 0) continue; + + new= (struct file *) alloc(sizeof(*new)); + new->name= (char *) alloc((size_t) strlen(entry->d_name) + 1); + strcpy(new->name, entry->d_name); + + *last= new; + last= &new->next; + } + closedir(dp); + *last= nil; + + return first; +} + +struct file *shorten(struct file *list) +{ + struct file *junk; + + assert(list != nil); + + junk= list; + list= list->next; + + free((void *) junk->name); + free((void *) junk); + + return list; +} + +/* Hash list of files to ignore. */ +struct file *ignore_list[1024]; +size_t n_ignored= 0; + +unsigned ihash(char *name) +/* A simple hashing function on a file name. */ +{ + unsigned h= 0; + + while (*name != 0) h= (h * 0x1111) + *name++; + + return h & (arraysize(ignore_list) - 1); +} + +void do_ignore(int add, char *name) +/* Add or remove a file to/from the list of files to ignore. */ +{ + struct file **ipp, *ip; + + ipp= &ignore_list[ihash(name)]; + while ((ip= *ipp) != nil) { + if (strcmp(name, ip->name) <= 0) break; + ipp= &ip->next; + } + + if (add) { + ip= alloc(sizeof(*ip)); + ip->name= alloc((strlen(name) + 1) * sizeof(ip->name[0])); + strcpy(ip->name, name); + ip->next= *ipp; + *ipp= ip; + n_ignored++; + } else { + assert(ip != nil); + *ipp= ip->next; + free(ip->name); + free(ip); + n_ignored--; + } +} + +int is_ignored(char *name) +/* Is a file in the list of ignored files? */ +{ + struct file *ip; + int r; + + ip= ignore_list[ihash(name)]; + while (ip != nil) { + if ((r = strcmp(name, ip->name)) <= 0) return (r == 0); + ip= ip->next; + } + return 0; +} + +#define is_ignored(name) (n_ignored > 0 && (is_ignored)(name)) + +time_t retired, dotretired; + +enum level { TOP, DOWN }; + +void cleandir(enum level level, time_t retired) +{ + struct file *list; + struct stat st; + time_t ret; + + if (debug >= 2) fprintf(stderr, "Cleaning %s\n", path); + + list= listdir(); + + while (list != nil) { + int didx; + + ret= (level == TOP && list->name[0] == '.') ? + dotretired : retired; + /* don't rm tmp/.* too soon. */ + + addpath(&didx, list->name); + + if (is_ignored(path)) { + if (debug >= 1) fprintf(stderr, "ignoring %s\n", path); + do_ignore(0, path); + } else + if (is_ignored(list->name)) { + if (debug >= 1) fprintf(stderr, "ignoring %s\n", path); + } else + if (lstat(path, &st) < 0) { + report(path); + } else + if (S_ISDIR(st.st_mode)) { + cleandir(DOWN, ret); + if (force || st.st_mtime < ret) { + if (debug < 3 && rmdir(path) < 0) { + if (errno != ENOTEMPTY + && errno != EEXIST) { + report(path); + } + } else { + if (debug >= 1) { + fprintf(stderr, + "rmdir %s\n", path); + } + } + } + } else { + if (force || (st.st_atime < ret + && st.st_mtime < ret + && st.st_ctime < ret) + ) { + if (debug < 3 && unlink(path) < 0) { + if (errno != ENOENT) { + report(path); + } + } else { + if (debug >= 1) { + fprintf(stderr, + "rm %s\n", path); + } + } + } + } + delpath(didx); + list= shorten(list); + } +} + +void usage(void) +{ + fprintf(stderr, + "Usage: cleantmp [-d[level]] [-i file ] ... -days|-f directory ...\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + int i; + unsigned long days; + + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++] + 1; + + if (opt[0] == '-' && opt[1] == 0) break; + + if (opt[0] == 'd') { + debug= 1; + if (opt[1] != 0) debug= atoi(opt + 1); + } else + if (opt[0] == 'i') { + if (*++opt == 0) { + if (i == argc) usage(); + opt= argv[i++]; + } + do_ignore(1, opt); + } else + if (opt[0] == 'f' && opt[1] == 0) { + force= 1; + days= 1; + } else { + char *end; + days= strtoul(opt, &end, 10); + if (*opt == 0 || *end != 0 + || days == 0 + || ((time_t) (days * SEC_DAY)) / SEC_DAY != days + ) { + fprintf(stderr, + "cleantmp: %s is not a valid number of days\n", + opt); + exit(1); + } + } + } + if (days == 0) usage(); + + days2time(days, &retired, &dotretired); + + while (i < argc) { + int didx; + + if (argv[i][0] == 0) { + fprintf(stderr, "cleantmp: empty pathname!\n"); + exit(1); + } + addpath(&didx, argv[i]); + cleandir(TOP, retired); + delpath(didx); + assert(path[0] == 0); + i++; + } + exit(0); +} diff --git a/commands/simple/cmp.c b/commands/simple/cmp.c new file mode 100755 index 000000000..cf98a6d6b --- /dev/null +++ b/commands/simple/cmp.c @@ -0,0 +1,129 @@ +/* cmp - compare two files Author: Kees J. Bot. */ + +#include <sys/types.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +_PROTOTYPE(void fatal, (char *label)); +_PROTOTYPE(int cmp, (int fd1, int fd2)); +_PROTOTYPE(void Usage, (void)); +_PROTOTYPE(int main, (int argc, char **argv)); + +#define BLOCK 4096 + +static int loud = 0, silent = 0; +static char *name1, *name2; + +int main(argc, argv) +int argc; +char **argv; +{ + int fd1, fd2; + + /* Process the '-l' or '-s' option. */ + while (argc > 1 && argv[1][0] == '-' && argv[1][1] != 0) { + if (argv[1][2] != 0) Usage(); + + switch (argv[1][1]) { + case '-': + /* '--': no-op option. */ + break; + case 'l': + loud = 1; + break; + case 's': + silent = 1; + break; + default: + Usage(); + } + argc--; + argv++; + } + if (argc != 3) Usage(); + + /* Open the first file, '-' means standard input. */ + if (argv[1][0] == '-' && argv[1][1] == 0) { + name1 = "stdin"; + fd1 = 0; + } else { + name1 = argv[1]; + if ((fd1 = open(name1, 0)) < 0) fatal(name1); + } + + /* Second file likewise. */ + if (argv[2][0] == '-' && argv[2][1] == 0) { + name2 = "stdin"; + fd2 = 0; + } else { + name2 = argv[2]; + if ((fd2 = open(name2, 0)) < 0) fatal(name2); + } + + exit(cmp(fd1, fd2)); +} + +int cmp(fd1, fd2) +int fd1, fd2; +{ + static char buf1[BLOCK], buf2[BLOCK]; + int n1 = 0, n2 = 0, i1 = 0, i2 = 0, c1, c2; + off_t pos = 0, line = 1; + int eof = 0, differ = 0; + + for (;;) { + if (i1 == n1) { + pos += n1; + + if ((n1 = read(fd1, buf1, sizeof(buf1))) <= 0) { + if (n1 < 0) fatal(name1); + eof |= 1; + } + i1 = 0; + } + if (i2 == n2) { + if ((n2 = read(fd2, buf2, sizeof(buf2))) <= 0) { + if (n2 < 0) fatal(name2); + eof |= 2; + } + i2 = 0; + } + if (eof != 0) break; + + c1 = buf1[i1++]; + c2 = buf2[i2++]; + + if (c1 != c2) { + if (!loud) { + if (!silent) { + printf("%s %s differ: char %ld, line %ld\n", + name1, name2, pos + i1, line); + } + return(1); + } + printf("%10ld %3o %3o\n", pos + i1, c1 & 0xFF, c2 & 0xFF); + differ = 1; + } + if (c1 == '\n') line++; + } + if (eof == (1 | 2)) return(differ); + if (!silent) fprintf(stderr, "cmp: EOF on %s\n", eof == 1 ? name1 : name2); + return(1); +} + +void fatal(label) +char *label; +{ + if (!silent) fprintf(stderr, "cmp: %s: %s\n", label, strerror(errno)); + exit(2); +} + +void Usage() +{ + fprintf(stderr, "Usage: cmp [-l | -s] file1 file2\n"); + exit(2); +} diff --git a/commands/simple/co.c b/commands/simple/co.c new file mode 100755 index 000000000..83c9857c5 --- /dev/null +++ b/commands/simple/co.c @@ -0,0 +1,252 @@ +/* co - check out Author: Peter S. Housel 12/24/87 */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <pwd.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#define SUFFIX ",S" /* svc indicator */ +#define SVCDIR "SVC" /* svc postfix indicator */ + +#define LINELEN 256 /* maximum line length */ + +#ifdef MAXPATHLEN +#define PATHLEN MAXPATHLEN +#else +#define PATHLEN 128 /* buffer length for filenames */ +#endif + +char file[PATHLEN]; /* file to be checked in */ +char svc[PATHLEN]; /* filename for svc file */ +char newsvc[PATHLEN]; /* new copy of SVC file */ +char line[LINELEN]; /* temporary line buffer */ +char *p; /* scratch character pointer */ + +FILE *svcfp; /* svc file */ +int rev; /* old revision number */ +int lastrev, lockrev; /* latest file revision, lock into */ +int status; /* wait() buffer */ +int svclock; /* lock the SVC file */ +struct stat stb; /* stat() buffer */ +char *base; /* basename of file */ + +char difftemp[PATHLEN]; /* extract() fix/patch input */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void fname, (char *src, char *dst)); +_PROTOTYPE(void svcname, (char *src, char *dst)); +_PROTOTYPE(void extract, (char *script, char *out, int rev)); +_PROTOTYPE(char *basename, (char *name)); +_PROTOTYPE(char *whoami, (void)); +_PROTOTYPE(int getyn, (void)); + +int main(argc, argv) +int argc; +char **argv; +{ +#ifdef perprintf + char errbuf[BUFSIZ]; + setbuf(stderr, errbuf); + perprintf(stderr); +#endif + + while (++argv, --argc) { + if ('-' == (*argv)[0]) { + if ('r' == (*argv)[1]) { + --argc; + rev = atoi(*++argv); + if (rev < 1) { + fprintf(stderr, "Illegal revision number\n"); + exit(1); + } + } else if ('l' == (*argv)[1]) + ++svclock; + else { + fprintf(stderr, "co: illegal option -%c\n", (*argv)[1]); + exit(1); + } + } else + break; + } + + if (1 != argc) { + fprintf(stderr, "co: bad number of files arguments\n"); + exit(1); + } + fname(*argv, file); + svcname(file, svc); + + fprintf(stderr, "%s -> %s\n", svc, base = basename(file)); + + if (NULL == (svcfp = fopen(svc, "r"))) { + perror("co: can't read SVC file"); + exit(1); + } + if (1 != fscanf(svcfp, "# %d", &lastrev) || lastrev < 1) { + fprintf(stderr, "co: illegal SVC file format\n"); + exit(1); + } + fclose(svcfp); + + if (stat(base, &stb) >= 0 && (stb.st_mode & 0222)) { + fprintf(stderr, "Writable %s exists - overwrite (n/y)? ", base); + if (!getyn()) { + fprintf(stderr, "Checkout aborted\n"); + exit(1); + } + } + if (strlen(base)) unlink(base); + if (0 == rev) rev = lastrev; + fprintf(stderr, "Checking out revision %d", rev); + extract(svc, base, rev); + + if (svclock) { + lockrev = lastrev + 1; + fprintf(stderr, "; Locking into revision %d\n", lockrev); + if (stat(svc, &stb) < 0 || chmod(svc, stb.st_mode | 0200) < 0) + perror("co: can't chmod SVC file"); + + if (stat(base, &stb) < 0 || chmod(base, stb.st_mode | 0200) < 0) + perror("co: can't chmod source file"); + + if (NULL == (svcfp = fopen(svc, "a")) + || (fprintf(svcfp, "#***SVCLOCK*** %s %d\n", whoami(), lockrev), ferror(svcfp))) { + fprintf(stderr, "co: can't lock %s\n", svc); + exit(1); + } + if (stat(svc, &stb) < 0 || chmod(svc, stb.st_mode & 0555)) + perror("co: can't chmod SVC file"); + } else { + putchar('\n'); + if (stat(base, &stb) < 0 || chmod(base, stb.st_mode & 0555)) + perror("co: can't chmod source file"); + } + + return(0); +} + + +void fname(src, dst) +char *src, *dst; +{ + char *p; + strcpy(dst, src); + p = &dst[strlen(src) - strlen(SUFFIX)]; + if (!strcmp(p, SUFFIX)) *p = '\0'; +} + +void svcname(src, dst) +char *src, *dst; +{ + char *p; + + strcpy(dst, src); + strcat(dst, SUFFIX); + + if (0 != access(dst, 4)) { + char dirname[PATHLEN]; + if (NULL != (p = strrchr(src, '/'))) + strncpy(dirname, src, (size_t)(p - src) + 1); + else + dirname[0] = '\0'; + strcat(dirname, SVCDIR); + + if (0 == access(dirname, 1)) { + strcpy(dst, dirname); + if (NULL == p) { + strcat(dst, "/"); + strcat(dst, src); + } else + strcat(dst, p); + strcat(dst, SUFFIX); + } + } +} + +void extract(script, out, rev) +char *script, *out; +int rev; +{ + FILE *outfp; + int testrev; + char buf[80]; + + sprintf(difftemp, "Fix.%s", out); + + svcfp = fopen(script, "r"); + fgets(line, LINELEN, svcfp); /* skip '# rev' line */ + fgets(line, LINELEN, svcfp); /* skip 'cat <***MAIN-eof***' line */ + + if (NULL == (outfp = fopen(out, "w"))) { + perror("co: can't create output file"); + return; + } + while (NULL != fgets(line, LINELEN, svcfp) && + strcmp(line, "***MAIN-eof***\n")) + fputs(line, outfp); + + fclose(outfp); + + while (NULL != fgets(line, LINELEN, svcfp)) { + if (!strncmp(line, "if ", (size_t)3)) { + sscanf(line, "if test $2 -ge %d", &testrev); + if (rev >= testrev) { + unlink(difftemp); + return; + } + if (NULL == (outfp = fopen(difftemp, "w"))) { + perror("co: can't create output file"); + return; + } + sprintf(buf, "***%d-eof***\n", testrev); + while (NULL != fgets(line, LINELEN, svcfp) && + strcmp(line, buf)) + fputs(line, outfp); + fclose(outfp); + } else if (!strncmp(line, "mv ", (size_t)3)) { + sprintf(buf, "mv Fix.%s %s", out, out); + system(buf); + } else if (!strncmp(line, "fix ", (size_t)4)) { + sprintf(buf, "fix %s Fix.%s > New.%s; mv New.%s %s", out, out, out, out, out); + system(buf); + } else if (!strncmp(line, "patch ", (size_t)6)) { + sprintf(buf, "patch -n -s %s < Fix.%s; rm -f %s.orig", out, out, out); + system(buf); + } else { /* ignore */ + } + } + + unlink(difftemp); + return; +} + +char *basename(name) +char *name; +{ + char *p; + + if (NULL == (p = strrchr(name, '/'))) + return name; + else + return p + 1; +} + +char *whoami() +{ + struct passwd *pw; + + if (NULL != (pw = getpwuid(getuid()))) + return pw->pw_name; + else + return "nobody"; +} + +int getyn() +{ + char ans[10]; + + return(NULL != fgets(ans, 10, stdin)) && ('y' == ans[0] || 'Y' == ans[0]); +} diff --git a/commands/simple/comm.c b/commands/simple/comm.c new file mode 100755 index 000000000..5fecf3322 --- /dev/null +++ b/commands/simple/comm.c @@ -0,0 +1,206 @@ +/* comm - select lines from two sorted files Author: Martin C. Atkins */ + +/* + * This program was written by: + * Martin C. Atkins, + * University of York, + * Heslington, + * York. Y01 5DD + * England + * and is released into the public domain, on the condition + * that this comment is always included without alteration. + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <minix/minlib.h> +#include <stdio.h> + +#define BUFFER_SIZE (512) +#define LINMAX (600) + +struct file { + char *name; /* the file's name */ + int fd; /* the file descripter */ + char buf[BUFFER_SIZE]; /* buffer storage */ + char *next; /* the next character to read */ + char *endp; /* the first invalid character */ + int seeneof; /* an end of file has been seen */ +} files[2]; + +char lines[2][LINMAX]; + +int colflgs[3] = {1, 2, 3}; /* number of tabs + 1: 0 => no column */ + +static char *umsg = "Usage: comm [-[123]] file1 file2\n"; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(void error, (char *s, char *f)); +_PROTOTYPE(void eopen, (char *fn, struct file *file)); +_PROTOTYPE(int getbuf, (struct file *file)); +_PROTOTYPE(int readline, (int fno)); +_PROTOTYPE(void comm, (void)); +_PROTOTYPE(void putcol, (int col, char *buf)); +_PROTOTYPE(void cpycol, (int col)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int cnt; + if (argc > 1 && argv[1][0] == '-' && argv[1][1] != '\0') { + char *ap; + for (ap = &argv[1][1]; *ap; ap++) switch (*ap) { + case '1': + case '2': + case '3': + cnt = *ap - '1'; + if (colflgs[cnt] == 0) break; + colflgs[cnt] = 0; + for (cnt++; cnt < 3; cnt++) colflgs[cnt]--; + break; + default: usage(); + } + argc--; + argv++; + } + if (argc != 3) usage(); + eopen(argv[1], &files[0]); + eopen(argv[2], &files[1]); + comm(); + return(0); +} + +void usage() +{ + + std_err(umsg); + exit(1); +} + +void error(s, f) +char *s, *f; +{ + std_err("comm: "); + std_err(s); + if (f) std_err(f); + std_err("\n"); + exit(1); +} + +void eopen(fn, file) +char *fn; +struct file *file; +{ + file->name = fn; + file->next = file->endp = &file->buf[0]; + file->seeneof = 0; + if (fn[0] == '-' && fn[1] == '\0') + file->fd = 0; + else if ((file->fd = open(fn, O_RDONLY)) < 0) + error("can't open ", fn); +} + + +int getbuf(file) +struct file *file; +{ +/* Get a buffer-full from the file. Return true if no characters + * were obtained because we are at end of file. + */ + int n; + + if (file->seeneof) return(1); + if ((n = read(file->fd, &file->buf[0], BUFFER_SIZE)) < 0) + error("read error on ", file->name); + if (n == 0) { + file->seeneof++; + return 1; + } + file->next = &file->buf[0]; + file->endp = &file->buf[n]; + return(0); +} + + +int readline(fno) +int fno; +{ +/* Read up to the next '\n' character to buf. + * Return a complete line, even if end of file occurs within a line. + * Return false at end of file/ + */ + register struct file *file = &files[fno]; + char *buf = lines[fno]; + + if (file->next == file->endp && getbuf(file)) return(0); + while ((*buf++ = *file->next++) != '\n') + if (file->next == file->endp && getbuf(file)) { + *buf++ = '\n'; + *buf = '\0'; + return(1); + } + *buf = '\0'; + return(1); +} + +void comm() +{ + register int res; + + if (!readline(0)) { + cpycol(1); + return; + } + if (!readline(1)) { + putcol(0, lines[0]); + cpycol(0); + return; + } + for (;;) { + if ((res = strcmp(lines[0], lines[1])) != 0) { + res = res > 0; + putcol(res, lines[res]); + if (!readline(res)) { + putcol(!res, lines[!res]); + cpycol(!res); + return; + } + } else { + putcol(2, lines[0]); /* files[1]lin == f2lin */ + if (!readline(0)) { + cpycol(1); + return; + } + if (!readline(1)) { + putcol(0, lines[0]); + cpycol(0); + return; + } + } + } + + /* NOTREACHED */ +} + +void putcol(col, buf) +int col; +char *buf; +{ + int cnt; + + if (colflgs[col] == 0) return; + for (cnt = 0; cnt < colflgs[col] - 1; cnt++) printf("\t"); + printf("%s", buf); +} + +void cpycol(col) +int col; +{ + if (colflgs[col]) while (readline(col)) + putcol(col, lines[col]); +} diff --git a/commands/simple/compress.c b/commands/simple/compress.c new file mode 100755 index 000000000..ffa032ba0 --- /dev/null +++ b/commands/simple/compress.c @@ -0,0 +1,1616 @@ +/* compress - Reduce file size using Modified Lempel-Ziv encoding */ + +/* + * compress.c - File compression ala IEEE Computer, June 1984. + * + * Authors: Spencer W. Thomas (decvax!harpo!utah-cs!utah-gr!thomas) + * Jim McKie (decvax!mcvax!jim) + * Steve Davies (decvax!vax135!petsd!peora!srd) + * Ken Turkowski (decvax!decwrl!turtlevax!ken) + * James A. Woods (decvax!ihnp4!ames!jaw) + * Joe Orost (decvax!vax135!petsd!joe) + * + * Richard Todd Port to MINIX + * Andy Tanenbaum Cleanup + * + * + * Algorithm from "A Technique for High Performance Data Compression", + * Terry A. Welch, IEEE Computer Vol 17, No 6 (June 1984), pp 8-19. + * + * Usage: compress [-dfvc] [-b bits] [file ...] + * Inputs: + * -d: If given, decompression is done instead. + * + * -c: Write output on stdout. + * + * -b: Parameter limits the max number of bits/code. + * + * -f: Forces output file to be generated, even if one already + * exists, and even if no space is saved by compressing. + * If -f is not used, the user will be prompted if stdin is + * a tty, otherwise, the output file will not be overwritten. + * + * -v: Write compression statistics + * + * file ...: Files to be compressed. If none specified, stdin + * is used. + * Outputs: + * file.Z: Compressed form of file with same mode, owner, and utimes + * or stdout (if stdin used as input) + * + * Assumptions: + * When filenames are given, replaces with the compressed version + * (.Z suffix) only if the file decreases in size. + * Algorithm: + * Modified Lempel-Ziv method (LZW). Basically finds common + * substrings and replaces them with a variable size code. This is + * deterministic, and can be done on the fly. Thus, the decompression + * procedure needs no input table, but tracks the way the table was built. + */ + + +#define AZTEC86 1 + +#define min(a,b) ((a>b) ? b : a) + +/* + * Set USERMEM to the maximum amount of physical user memory available + * in bytes. USERMEM is used to determine the maximum BITS that can be used + * for compression. + * + * SACREDMEM is the amount of physical memory saved for others; compress + * will hog the rest. + */ +#ifndef SACREDMEM +#define SACREDMEM 0 +#endif + +#ifndef USERMEM +# define USERMEM 450000 /* default user memory */ +#endif + +#define REGISTER register +#define DOTZ ".Z" + +#include <limits.h> + +/* The default for Minix is -b13, but we can do -b16 if the machine can. */ +#define DEFAULTBITS 13 +#if INT_MAX == 32767 +# define BITS 13 +#else +# define BITS 16 +#endif + +#ifdef USERMEM +# if USERMEM >= (433484+SACREDMEM) +# define PBITS 16 +# else +# if USERMEM >= (229600+SACREDMEM) +# define PBITS 15 +# else +# if USERMEM >= (127536+SACREDMEM) +# define PBITS 14 +# else +# if USERMEM >= (73464+SACREDMEM) +# define PBITS 13 +# else +# define PBITS 12 +# endif +# endif +# endif +# endif +# undef USERMEM +#endif /* USERMEM */ + +#ifdef PBITS /* Preferred BITS for this memory size */ +# ifndef BITS +# define BITS PBITS +# endif +#endif /* PBITS */ + +#if BITS == 16 +# define HSIZE 69001 /* 95% occupancy */ +#endif +#if BITS == 15 +# define HSIZE 35023 /* 94% occupancy */ +#endif +#if BITS == 14 +# define HSIZE 18013 /* 91% occupancy */ +#endif +#if BITS == 13 +# define HSIZE 9001 /* 91% occupancy */ +#endif +#if BITS <= 12 +# define HSIZE 5003 /* 80% occupancy */ +#endif + + +/* + * a code_int must be able to hold 2**BITS values of type int, and also -1 + */ +#if BITS > 15 +typedef long int code_int; +#else +typedef int code_int; +#endif + +#ifdef SIGNED_COMPARE_SLOW +typedef unsigned long int count_int; +typedef unsigned short int count_short; +#else +typedef long int count_int; +#endif + +#ifdef NO_UCHAR + typedef char char_type; +#else + typedef unsigned char char_type; +#endif /* UCHAR */ +char_type magic_header[] = "\037\235"; /* 1F 9D */ + +/* Defines for third byte of header */ +#define BIT_MASK 0x1f +#define BLOCK_MASK 0x80 +/* Masks 0x40 and 0x20 are free. I think 0x20 should mean that there is + a fourth header byte (for expansion). +*/ +#define INIT_BITS 9 /* initial number of bits/code */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <ctype.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <utime.h> +#include <stdio.h> + +#define ARGVAL() (*++(*argv) || (--argc && *++argv)) + +int n_bits; /* number of bits/code */ +int maxbits = DEFAULTBITS; /* user settable max # bits/code */ +code_int maxcode; /* maximum code, given n_bits */ +code_int maxmaxcode = 1 << BITS; /* should NEVER generate this code */ +#ifdef COMPATIBLE /* But wrong! */ +# define MAXCODE(n_bits) (1 << (n_bits) - 1) +#else +# define MAXCODE(n_bits) ((1 << (n_bits)) - 1) +#endif /* COMPATIBLE */ + +#ifndef AZTEC86 + count_int htab [HSIZE]; + unsigned short codetab [HSIZE]; +#else + count_int *htab; + unsigned short *codetab; +# define HTABSIZE ((size_t)(HSIZE*sizeof(count_int))) +# define CODETABSIZE ((size_t)(HSIZE*sizeof(unsigned short))) + + +#define htabof(i) htab[i] +#define codetabof(i) codetab[i] +#endif /* XENIX_16 */ +code_int hsize = HSIZE; /* for dynamic table sizing */ +count_int fsize; + +/* + * To save much memory, we overlay the table used by compress() with those + * used by decompress(). The tab_prefix table is the same size and type + * as the codetab. The tab_suffix table needs 2**BITS characters. We + * get this from the beginning of htab. The output stack uses the rest + * of htab, and contains characters. There is plenty of room for any + * possible stack (stack used to be 8000 characters). + */ + +#define tab_prefixof(i) codetabof(i) +#ifdef XENIX_16 +# define tab_suffixof(i) ((char_type *)htab[(i)>>15])[(i) & 0x7fff] +# define de_stack ((char_type *)(htab2)) +#else /* Normal machine */ +# define tab_suffixof(i) ((char_type *)(htab))[i] +# define de_stack ((char_type *)&tab_suffixof(1<<BITS)) +#endif /* XENIX_16 */ + +code_int free_ent = 0; /* first unused entry */ +int exit_stat = 0; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void Usage, (void)); +_PROTOTYPE(void compress, (void)); +_PROTOTYPE(void onintr, (int dummy)); +_PROTOTYPE(void oops, (int dummy)); +_PROTOTYPE(void output, (code_int code)); +_PROTOTYPE(int foreground, (void)); +_PROTOTYPE(void decompress, (void)); +_PROTOTYPE(code_int getcode, (void)); +_PROTOTYPE(void writeerr, (void)); +_PROTOTYPE(void copystat, (char *ifname, char *ofname)); +_PROTOTYPE(int foreground, (void)); +_PROTOTYPE(void cl_block , (void)); +_PROTOTYPE(void cl_hash, (count_int hsize)); +_PROTOTYPE(void prratio, (FILE *stream, long int num, long int den)); +_PROTOTYPE(void version, (void)); + +void Usage() { +#ifdef DEBUG +fprintf(stderr,"Usage: compress [-dDVfc] [-b maxbits] [file ...]\n"); +} +int debug = 0; +#else +fprintf(stderr,"Usage: compress [-dfvcV] [-b maxbits] [file ...]\n"); +} +#endif /* DEBUG */ +int nomagic = 0; /* Use a 3-byte magic number header, unless old file */ +int zcat_flg = 0; /* Write output on stdout, suppress messages */ +int quiet = 0; /* don't tell me about compression */ + +/* + * block compression parameters -- after all codes are used up, + * and compression rate changes, start over. + */ +int block_compress = BLOCK_MASK; +int clear_flg = 0; +long int ratio = 0; +#define CHECK_GAP 10000 /* ratio check interval */ +count_int checkpoint = CHECK_GAP; +/* + * the next two codes should not be changed lightly, as they must not + * lie within the contiguous general code space. + */ +#define FIRST 257 /* first free entry */ +#define CLEAR 256 /* table clear output code */ + +int force = 0; +char ofname [100]; +#ifdef DEBUG +int verbose = 0; +#endif /* DEBUG */ + +#ifndef METAWARE +#ifdef AZTEC86 +void +#else +int +#endif +#ifndef __STDC__ +(*bgnd_flag)(); +#else +(*bgnd_flag)(int); +#endif +#endif + +int do_decomp = 0; + + +int main(argc, argv) +int argc; +char **argv; +{ + int overwrite = 0; /* Do not overwrite unless given -f flag */ + char tempname[100]; + char **filelist, **fileptr; + char *cp; + struct stat statbuf; +#ifndef METAWARE + if ( (bgnd_flag = signal ( SIGINT, SIG_IGN )) != SIG_IGN ) { + signal ( SIGINT, onintr ); + signal ( SIGSEGV, oops ); + } +#endif +#ifdef AZTEC86 +#ifdef METAWARE + _setmode(NULL,_ALL_FILES_BINARY); + _setmode(stdin,_BINARY); + _setmode(stdout,_BINARY); + _setmode(stderr,_TEXT); +#endif + if (NULL == (htab = (count_int *)malloc(HTABSIZE))) + { + fprintf(stderr,"Can't allocate htab\n"); + exit(1); + } + if (NULL == (codetab = (unsigned short *)malloc(CODETABSIZE))) + { + fprintf(stderr,"Can't allocate codetab\n"); + exit(1); + } +#endif +#ifdef COMPATIBLE + nomagic = 1; /* Original didn't have a magic number */ +#endif /* COMPATIBLE */ + + filelist = fileptr = (char **)(malloc((size_t)(argc * sizeof(*argv)))); + *filelist = NULL; + + if((cp = strrchr(argv[0], '/')) != 0) { + cp++; + } else { + cp = argv[0]; + } + if(strcmp(cp, "uncompress") == 0) { + do_decomp = 1; + } else if(strcmp(cp, "zcat") == 0) { + do_decomp = 1; + zcat_flg = 1; + } + +#ifdef BSD4_2 + /* 4.2BSD dependent - take it out if not */ + setlinebuf( stderr ); +#endif /* BSD4_2 */ + + /* Argument Processing + * All flags are optional. + * -D => debug + * -V => print Version; debug verbose + * -d => do_decomp + * -v => unquiet + * -f => force overwrite of output file + * -n => no header: useful to uncompress old files + * -b maxbits => maxbits. If -b is specified, then maxbits MUST be + * given also. + * -c => cat all output to stdout + * -C => generate output compatible with compress 2.0. + * if a string is left, must be an input filename. + */ + for (argc--, argv++; argc > 0; argc--, argv++) + { + if (**argv == '-') + { /* A flag argument */ + while (*++(*argv)) + { /* Process all flags in this arg */ + switch (**argv) + { +#ifdef DEBUG + case 'D': + debug = 1; + break; + case 'V': + verbose = 1; + version(); + break; +#else + case 'V': + version(); + break; +#endif /* DEBUG */ + case 'v': + quiet = 0; + break; + case 'd': + do_decomp = 1; + break; + case 'f': + case 'F': + overwrite = 1; + force = 1; + break; + case 'n': + nomagic = 1; + break; + case 'C': + block_compress = 0; + break; + case 'b': + if (!ARGVAL()) + { + fprintf(stderr, "Missing maxbits\n"); + Usage(); + exit(1); + } + maxbits = atoi(*argv); + goto nextarg; + case 'c': + zcat_flg = 1; + break; + case 'q': + quiet = 1; + break; + default: + fprintf(stderr, "Unknown flag: '%c'; ", **argv); + Usage(); + exit(1); + } + } + } + else + { /* Input file name */ + *fileptr++ = *argv; /* Build input file list */ + *fileptr = NULL; + /* process nextarg; */ + } + nextarg: continue; + } + + if(maxbits < INIT_BITS) maxbits = INIT_BITS; + if (maxbits > BITS) maxbits = BITS; + maxmaxcode = 1 << maxbits; + + if (*filelist != NULL) + { + for (fileptr = filelist; *fileptr; fileptr++) + { + exit_stat = 0; + if (do_decomp != 0) + { /* DECOMPRESSION */ + /* Check for .Z suffix */ +#ifndef PCDOS + if (strcmp(*fileptr + strlen(*fileptr) - 2, DOTZ) != 0) +#else + if (strcmp(*fileptr + strlen(*fileptr) - 1, DOTZ) != 0) +#endif + { + /* No .Z: tack one on */ + strcpy(tempname, *fileptr); +#ifndef PCDOS + strcat(tempname, DOTZ); +#else + /* either tack one on or replace last character */ + { + char *dot; + if (NULL == (dot = strchr(tempname,'.'))) + { + strcat(tempname,".Z"); + } + else + /* if there is a dot then either tack a z on + or replace last character */ + { + if (strlen(dot) < 4) + strcat(tempname,DOTZ); + else + dot[3] = 'Z'; + } + } +#endif + *fileptr = tempname; + } + /* Open input file */ + if ((freopen(*fileptr, "r", stdin)) == NULL) + { + perror(*fileptr); continue; + } + /* Check the magic number */ + if (nomagic == 0) + { + unsigned magic1, magic2; + if (((magic1 = getc(stdin)) != (magic_header[0] & 0xFF)) + || ((magic2 = getc(stdin)) != (magic_header[1] & 0xFF))) + { + fprintf(stderr, + "%s: not in compressed format %x %x\n", + *fileptr,magic1,magic2); + continue; + } + maxbits = getc(stdin); /* set -b from file */ + block_compress = maxbits & BLOCK_MASK; + maxbits &= BIT_MASK; + maxmaxcode = 1 << maxbits; + if(maxbits > BITS) + { + fprintf(stderr, + "%s: compressed with %d bits, can only handle %d bits\n", + *fileptr, maxbits, BITS); + continue; + } + } + /* Generate output filename */ + strcpy(ofname, *fileptr); +#ifndef PCDOS + ofname[strlen(*fileptr) - 2] = '\0'; /* Strip off .Z */ +#else + /* kludge to handle various common three character extension */ + { + char *dot; + char fixup = '\0'; + /* first off, map name to upper case */ + for (dot = ofname; *dot; dot++) + *dot = toupper(*dot); + if (NULL == (dot = strchr(ofname,'.'))) + { + fprintf(stderr,"Bad filename %s\n",ofname); + exit(1); + } + if (strlen(dot) == 4) + /* we got three letter extensions */ + { + if (strcmp(dot,".EXZ") == 0) + fixup = 'E'; + else if (strcmp(dot,".COZ") == 0) + fixup = 'M'; + else if (strcmp(dot,".BAZ") == 0) + fixup = 'S'; + else if (strcmp(dot,".OBZ") == 0) + fixup = 'J'; + else if (strcmp(dot,".SYZ") == 0) + fixup = 'S'; + else if (strcmp(dot,".DOZ") == 0) + fixup = 'C'; + + } + /* replace the Z */ + ofname[strlen(*fileptr) - 1] = fixup; + } +#endif + } else + { /* COMPRESSION */ + if (strcmp(*fileptr + strlen(*fileptr) - 2, DOTZ) == 0) + { + fprintf(stderr, "%s: already has .Z suffix -- no change\n", + *fileptr); + continue; + } + /* Open input file */ + if ((freopen(*fileptr, "r", stdin)) == NULL) + { + perror(*fileptr); continue; + } + (void)stat( *fileptr, &statbuf ); + fsize = (long) statbuf.st_size; + /* + * tune hash table size for small files -- ad hoc, + * but the sizes match earlier #defines, which + * serve as upper bounds on the number of output codes. + */ + hsize = HSIZE; /*lint -e506 -e712 */ + if ( fsize < (1 << 12) ) + hsize = min ( 5003, HSIZE ); + else if ( fsize < (1 << 13) ) + hsize = min ( 9001, HSIZE ); + else if ( fsize < (1 << 14) ) + hsize = min ( 18013, HSIZE ); + else if ( fsize < (1 << 15) ) + hsize = min ( 35023, HSIZE ); + else if ( fsize < 47000 ) + hsize = min ( 50021, HSIZE ); /*lint +e506 +e712 */ + + /* Generate output filename */ + strcpy(ofname, *fileptr); +#ifndef BSD4_2 /* Short filenames */ + if ((cp=strrchr(ofname,'/')) != NULL) + cp++; + else + cp = ofname; + if (strlen(cp) > 12) + { + fprintf(stderr,"%s: filename too long to tack on .Z\n",cp); + continue; + } +#ifdef PCDOS + else + { + /* either tack one on or replace last character */ + char *dot; + if (NULL == (dot = strchr(cp,'.'))) + { + strcat(cp,".Z"); + } + else + /* if there is a dot then either tack a z on + or replace last character */ + { + if (strlen(dot) < 4) + strcat(cp,DOTZ); + else + dot[3] = 'Z'; + } + } +#endif +#endif /* BSD4_2 Long filenames allowed */ +#ifndef PCDOS + /* PCDOS takes care of this above */ + strcat(ofname, DOTZ); +#endif + } + /* Check for overwrite of existing file */ + if (overwrite == 0 && zcat_flg == 0) + { + if (stat(ofname, &statbuf) == 0) + { + char response[2]; int fd; + response[0] = 'n'; + fprintf(stderr, "%s already exists;", ofname); + if (foreground()) + { + fd = open("/dev/tty", O_RDONLY); + fprintf(stderr, + " do you wish to overwrite %s (y or n)? ", ofname); + fflush(stderr); + (void)read(fd, response, 2); + while (response[1] != '\n') + { + if (read(fd, response+1, 1) < 0) + { /* Ack! */ + perror("stderr"); + break; + } + } + close(fd); + } + if (response[0] != 'y') + { + fprintf(stderr, "\tnot overwritten\n"); + continue; + } + } + } + if(zcat_flg == 0) + { /* Open output file */ + if (freopen(ofname, "w", stdout) == NULL) + { + perror(ofname); + continue; + } + if(!quiet) + fprintf(stderr, "%s: ", *fileptr); + } + + /* Actually do the compression/decompression */ + if (do_decomp == 0) + compress(); +#ifndef DEBUG + else + decompress(); +#else + else if (debug == 0) + decompress(); + else + printcodes(); + if (verbose) + dump_tab(); +#endif /* DEBUG */ + if(zcat_flg == 0) + { + copystat(*fileptr, ofname); /* Copy stats */ + if((exit_stat == 1) || (!quiet)) + putc('\n', stderr); + } + } + } else + { /* Standard input */ + if (do_decomp == 0) + { + compress(); +#ifdef DEBUG + if(verbose) dump_tab(); +#endif /* DEBUG */ + if(!quiet) + putc('\n', stderr); + } else + { + /* Check the magic number */ + if (nomagic == 0) + { + if ((getc(stdin)!=(magic_header[0] & 0xFF)) + || (getc(stdin)!=(magic_header[1] & 0xFF))) + { + fprintf(stderr, "stdin: not in compressed format\n"); + exit(1); + } + maxbits = getc(stdin); /* set -b from file */ + block_compress = maxbits & BLOCK_MASK; + maxbits &= BIT_MASK; + maxmaxcode = 1 << maxbits; + fsize = 100000; /* assume stdin large for USERMEM */ + if(maxbits > BITS) + { + fprintf(stderr, + "stdin: compressed with %d bits, can only handle %d bits\n", + maxbits, BITS); + exit(1); + } + } +#ifndef DEBUG + decompress(); +#else + if (debug == 0) decompress(); + else printcodes(); + if (verbose) dump_tab(); +#endif /* DEBUG */ + } + } + return(exit_stat); +} + +static int offset; +long int in_count = 1; /* length of input */ +long int bytes_out; /* length of compressed output */ +long int out_count = 0; /* # of codes output (for debugging) */ + +/* + * compress stdin to stdout + * + * Algorithm: use open addressing double hashing (no chaining) on the + * prefix code / next character combination. We do a variant of Knuth's + * algorithm D (vol. 3, sec. 6.4) along with G. Knott's relatively-prime + * secondary probe. Here, the modular division first probe is gives way + * to a faster exclusive-or manipulation. Also do block compression with + * an adaptive reset, whereby the code table is cleared when the compression + * ratio decreases, but after the table fills. The variable-length output + * codes are re-sized at this point, and a special CLEAR code is generated + * for the decompressor. Late addition: construct the table according to + * file size for noticeable speed improvement on small files. Please direct + * questions about this implementation to ames!jaw. + */ + +void compress() +{ + REGISTER long fcode; + REGISTER code_int i = 0; + REGISTER int c; + REGISTER code_int ent; +#ifdef XENIX_16 + REGISTER code_int disp; +#else /* Normal machine */ + REGISTER int disp; +#endif + REGISTER code_int hsize_reg; + REGISTER int hshift; + +#ifndef COMPATIBLE + if (nomagic == 0) + { + putc(magic_header[0],stdout); + putc(magic_header[1],stdout); + putc((char)(maxbits | block_compress),stdout); + if(ferror(stdout)) + writeerr(); + } +#endif /* COMPATIBLE */ + + offset = 0; + bytes_out = 3; /* includes 3-byte header mojo */ + out_count = 0; + clear_flg = 0; + ratio = 0; + in_count = 1; + checkpoint = CHECK_GAP; + maxcode = MAXCODE(n_bits = INIT_BITS); + free_ent = ((block_compress) ? FIRST : 256 ); + + ent = getc(stdin); + + hshift = 0; + for ( fcode = (long) hsize; fcode < 65536L; fcode *= 2L ) + hshift++; + hshift = 8 - hshift; /* set hash code range bound */ + + hsize_reg = hsize; + cl_hash( (count_int) hsize_reg); /* clear hash table */ + +#ifdef SIGNED_COMPARE_SLOW + while ( (c = getc(stdin)) != (unsigned) EOF ) +#else + while ( (c = getc(stdin)) != EOF ) +#endif + { + in_count++; + fcode = (long) (((long) c << maxbits) + ent); + i = ((c << hshift) ^ ent); /* xor hashing */ + + if ( htabof (i) == fcode ) + { + ent = codetabof (i); + continue; + } else if ( (long)htabof (i) < 0 ) /* empty slot */ + goto nomatch; + disp = hsize_reg - i; /* secondary hash (after G. Knott) */ + if ( i == 0 ) + disp = 1; +probe: + if ( (i -= disp) < 0 ) + i += hsize_reg; + + if ( htabof (i) == fcode ) + { + ent = codetabof (i); + continue; + } + if ( (long)htabof (i) > 0 ) + goto probe; +nomatch: + output ( (code_int) ent ); + out_count++; + ent = c; +#ifdef SIGNED_COMPARE_SLOW + if ( (unsigned) free_ent < (unsigned) maxmaxcode) +#else + if ( free_ent < maxmaxcode ) +#endif + { + codetabof (i) = free_ent++; /* code -> hashtable */ + htabof (i) = fcode; + } + else if ( (count_int)in_count >= checkpoint && block_compress ) + cl_block (); + } + /* + * Put out the final code. + */ + output( (code_int)ent ); + out_count++; + output( (code_int)-1 ); + + /* + * Print out stats on stderr + */ + if(zcat_flg == 0 && !quiet) + { +#ifdef DEBUG + fprintf( stderr, + "%ld chars in, %ld codes (%ld bytes) out, compression factor: ", + in_count, out_count, bytes_out ); + prratio( stderr, in_count, bytes_out ); + fprintf( stderr, "\n"); + fprintf( stderr, "\tCompression as in compact: " ); + prratio( stderr, in_count-bytes_out, in_count ); + fprintf( stderr, "\n"); + fprintf( stderr, "\tLargest code (of last block) was %d (%d bits)\n", + free_ent - 1, n_bits ); +#else /* !DEBUG */ + fprintf( stderr, "Compression: " ); + prratio( stderr, in_count-bytes_out, in_count ); +#endif /* DEBUG */ + } + if(bytes_out > in_count) /* exit(2) if no savings */ + exit_stat = 2; + return; +} + +/***************************************************************** + * TAG( output ) + * + * Output the given code. + * Inputs: + * code: A n_bits-bit integer. If == -1, then EOF. This assumes + * that n_bits =< (long)wordsize - 1. + * Outputs: + * Outputs code to the file. + * Assumptions: + * Chars are 8 bits long. + * Algorithm: + * Maintain a BITS character long buffer (so that 8 codes will + * fit in it exactly). Use the VAX insv instruction to insert each + * code in turn. When the buffer fills up empty it and start over. + */ + +static char buf[BITS]; + +#ifndef vax +char_type lmask[9] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00}; +char_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff}; +#endif /* vax */ +void output( code ) +code_int code; +{ +#ifdef DEBUG + static int col = 0; +#endif /* DEBUG */ + + /* + * On the VAX, it is important to have the REGISTER declarations + * in exactly the order given, or the asm will break. + */ + REGISTER int r_off = offset, bits= n_bits; + REGISTER char * bp = buf; +#ifndef BREAKHIGHC +#ifdef METAWARE + int temp; +#endif +#endif +#ifdef DEBUG + if ( verbose ) + fprintf( stderr, "%5d%c", code, + (col+=6) >= 74 ? (col = 0, '\n') : ' ' ); +#endif /* DEBUG */ + if ( code >= 0 ) + { +#ifdef vax + /* VAX DEPENDENT!! Implementation on other machines is below. + * + * Translation: Insert BITS bits from the argument starting at + * offset bits from the beginning of buf. + */ + 0; /* Work around for pcc -O bug with asm and if stmt */ + asm( "insv 4(ap),r11,r10,(r9)" ); +#else /* not a vax */ +/* + * byte/bit numbering on the VAX is simulated by the following code + */ + /* + * Get to the first byte. + */ + bp += (r_off >> 3); + r_off &= 7; + /* + * Since code is always >= 8 bits, only need to mask the first + * hunk on the left. + */ +#ifndef BREAKHIGHC +#ifdef METAWARE + *bp &= rmask[r_off]; + temp = (code << r_off) & lmask[r_off]; + *bp |= temp; +#else + *bp = (*bp & rmask[r_off]) | ((code << r_off) & lmask[r_off]); +#endif +#else + *bp = (*bp & rmask[r_off]) | ((code << r_off) & lmask[r_off]); +#endif + bp++; + bits -= (8 - r_off); + code >>= (8 - r_off); + /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ + if ( bits >= 8 ) + { + *bp++ = code; + code >>= 8; + bits -= 8; + } + /* Last bits. */ + if(bits) + *bp = code; +#endif /* vax */ + offset += n_bits; + if ( offset == (n_bits << 3) ) + { + bp = buf; + bits = n_bits; + bytes_out += bits; + do + putc(*bp++,stdout); + while(--bits); + offset = 0; + } + + /* + * If the next entry is going to be too big for the code size, + * then increase it, if possible. + */ + if ( free_ent > maxcode || (clear_flg > 0)) + { + /* + * Write the whole buffer, because the input side won't + * discover the size increase until after it has read it. + */ + if ( offset > 0 ) + { + if( fwrite( buf, (size_t)1, (size_t)n_bits, stdout ) != n_bits) + writeerr(); + bytes_out += n_bits; + } + offset = 0; + + if ( clear_flg ) + { + maxcode = MAXCODE (n_bits = INIT_BITS); + clear_flg = 0; + } + else + { + n_bits++; + if ( n_bits == maxbits ) + maxcode = maxmaxcode; + else + maxcode = MAXCODE(n_bits); + } +#ifdef DEBUG + if ( debug ) + { + fprintf( stderr, "\nChange to %d bits\n", n_bits ); + col = 0; + } +#endif /* DEBUG */ + } + } else + { + /* + * At EOF, write the rest of the buffer. + */ + if ( offset > 0 ) + fwrite( buf, (size_t)1, (size_t)(offset + 7) / 8, stdout ); + bytes_out += (offset + 7) / 8; + offset = 0; + fflush( stdout ); +#ifdef DEBUG + if ( verbose ) + fprintf( stderr, "\n" ); +#endif /* DEBUG */ + if( ferror( stdout ) ) + writeerr(); + } +} +/* + * Decompress stdin to stdout. This routine adapts to the codes in the + * file building the "string" table on-the-fly; requiring no table to + * be stored in the compressed file. The tables used herein are shared + * with those of the compress() routine. See the definitions above. + */ + +void decompress() { + REGISTER char_type *stackp; + REGISTER int finchar; + REGISTER code_int code, oldcode, incode; + + /* + * As above, initialize the first 256 entries in the table. + */ + maxcode = MAXCODE(n_bits = INIT_BITS); + for ( code = 255; code >= 0; code-- ) { + tab_prefixof(code) = 0; + tab_suffixof(code) = (char_type)code; + } + free_ent = ((block_compress) ? FIRST : 256 ); + + finchar = oldcode = getcode(); + if(oldcode == -1) /* EOF already? */ + return; /* Get out of here */ + putc( (char)finchar,stdout ); /* first code must be 8 bits = char */ + if(ferror(stdout)) /* Crash if can't write */ + writeerr(); + stackp = de_stack; + + while ( (code = getcode()) > -1 ) { + + if ( (code == CLEAR) && block_compress ) { + for ( code = 255; code >= 0; code-- ) + tab_prefixof(code) = 0; + clear_flg = 1; + free_ent = FIRST - 1; + if ( (code = getcode ()) == -1 ) /* O, untimely death! */ + break; + } + incode = code; + /* + * Special case for KwKwK string. + */ + if ( code >= free_ent ) { + *stackp++ = finchar; + code = oldcode; + } + + /* + * Generate output characters in reverse order + */ +#ifdef SIGNED_COMPARE_SLOW + while ( ((unsigned long)code) >= ((unsigned long)256) ) { +#else + while ( code >= 256 ) { +#endif + *stackp++ = tab_suffixof(code); + code = tab_prefixof(code); + } + *stackp++ = finchar = tab_suffixof(code); + + /* + * And put them out in forward order + */ + do + putc ( *--stackp ,stdout); + while ( stackp > de_stack ); + + /* + * Generate the new entry. + */ + if ( (code=free_ent) < maxmaxcode ) + { + tab_prefixof(code) = (unsigned short)oldcode; + tab_suffixof(code) = finchar; + free_ent = code+1; + } + /* + * Remember previous code. + */ + oldcode = incode; + } + fflush( stdout ); + if(ferror(stdout)) + writeerr(); +} + +/***************************************************************** + * TAG( getcode ) + * + * Read one code from the standard input. If EOF, return -1. + * Inputs: + * stdin + * Outputs: + * code or -1 is returned. + */ + +code_int +getcode() +{ + /* + * On the VAX, it is important to have the REGISTER declarations + * in exactly the order given, or the asm will break. + */ + REGISTER code_int code; + static int offset = 0, size = 0; + static char_type buf[BITS]; + REGISTER int r_off, bits; + REGISTER char_type *bp = buf; + + if ( clear_flg > 0 || offset >= size || free_ent > maxcode ) + { + /* + * If the next entry will be too big for the current code + * size, then we must increase the size. This implies reading + * a new buffer full, too. + */ + if ( free_ent > maxcode ) + { + n_bits++; + if ( n_bits == maxbits ) + maxcode = maxmaxcode; /* won't get any bigger now */ + else + maxcode = MAXCODE(n_bits); + } + if ( clear_flg > 0) + { + maxcode = MAXCODE (n_bits = INIT_BITS); + clear_flg = 0; + } + size = fread( buf, (size_t)1, (size_t)n_bits, stdin ); + if ( size <= 0 ) + return -1; /* end of file */ + offset = 0; + /* Round size down to integral number of codes */ + size = (size << 3) - (n_bits - 1); + } + r_off = offset; + bits = n_bits; +#ifdef vax + asm( "extzv r10,r9,(r8),r11" ); +#else /* not a vax */ + /* + * Get to the first byte. + */ + bp += (r_off >> 3); + r_off &= 7; + /* Get first part (low order bits) */ +#ifdef NO_UCHAR + code = ((*bp++ >> r_off) & rmask[8 - r_off]) & 0xff; +#else + code = (*bp++ >> r_off); +#endif /* NO_UCHAR */ + bits -= (8 - r_off); + r_off = 8 - r_off; /* now, offset into code word */ + /* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */ + if ( bits >= 8 ) + { +#ifdef NO_UCHAR + code |= (*bp++ & 0xff) << r_off; +#else + code |= *bp++ << r_off; +#endif /* NO_UCHAR */ + r_off += 8; + bits -= 8; + } + /* high order bits. */ + code |= (*bp & rmask[bits]) << r_off; +#endif /* vax */ + offset += n_bits; + + return code; +} + +#ifndef AZTEC86 +char * +strrchr(s, c) /* For those who don't have it in libc.a */ +REGISTER char *s, c; +{ + char *p; + for (p = NULL; *s; s++) + if (*s == c) + p = s; + return(p); +} +#endif + + +#ifndef METAWARE +#ifdef DEBUG +printcodes() +{ + /* + * Just print out codes from input file. For debugging. + */ + code_int code; + int col = 0, bits; + + bits = n_bits = INIT_BITS; + maxcode = MAXCODE(n_bits); + free_ent = ((block_compress) ? FIRST : 256 ); + while ( ( code = getcode() ) >= 0 ) { + if ( (code == CLEAR) && block_compress ) { + free_ent = FIRST - 1; + clear_flg = 1; + } + else if ( free_ent < maxmaxcode ) + free_ent++; + if ( bits != n_bits ) { + fprintf(stderr, "\nChange to %d bits\n", n_bits ); + bits = n_bits; + col = 0; + } + fprintf(stderr, "%5d%c", code, (col+=6) >= 74 ? (col = 0, '\n') : ' ' ); + } + putc( '\n', stderr ); + exit( 0 ); +} +#ifdef DEBUG2 +code_int sorttab[1<<BITS]; /* sorted pointers into htab */ +#define STACK_SIZE 500 +static char stack[STACK_SIZE]; +/* dumptab doesn't use main stack now -prevents distressing crashes */ +dump_tab() /* dump string table */ +{ + REGISTER int i, first; + REGISTER ent; + int stack_top = STACK_SIZE; + REGISTER c; + + if(do_decomp == 0) { /* compressing */ + REGISTER int flag = 1; + + for(i=0; i<hsize; i++) { /* build sort pointers */ + if((long)htabof(i) >= 0) { + sorttab[codetabof(i)] = i; + } + } + first = block_compress ? FIRST : 256; + for(i = first; i < free_ent; i++) { + fprintf(stderr, "%5d: \"", i); + stack[--stack_top] = '\n'; + stack[--stack_top] = '"'; /* " */ + stack_top = in_stack((int)(htabof(sorttab[i])>>maxbits)&0xff, + stack_top); + for(ent=htabof(sorttab[i]) & ((1<<maxbits)-1); + ent > 256; + ent=htabof(sorttab[ent]) & ((1<<maxbits)-1)) { + stack_top = in_stack((int)(htabof(sorttab[ent]) >> maxbits), + stack_top); + } + stack_top = in_stack(ent, stack_top); + fwrite( &stack[stack_top], (size_t)1, (size_t)(STACK_SIZE-stack_top), stderr); + stack_top = STACK_SIZE; + } + } else if(!debug) { /* decompressing */ + + for ( i = 0; i < free_ent; i++ ) { + ent = i; + c = tab_suffixof(ent); + if ( isascii(c) && isprint(c) ) + fprintf( stderr, "%5d: %5d/'%c' \"", + ent, tab_prefixof(ent), c ); + else + fprintf( stderr, "%5d: %5d/\\%03o \"", + ent, tab_prefixof(ent), c ); + stack[--stack_top] = '\n'; + stack[--stack_top] = '"'; /* " */ + for ( ; ent != NULL; + ent = (ent >= FIRST ? tab_prefixof(ent) : NULL) ) { + stack_top = in_stack(tab_suffixof(ent), stack_top); + } + fwrite( &stack[stack_top], (size_t)1, (size_t)(STACK_SIZE - stack_top), stderr ); + stack_top = STACK_SIZE; + } + } +} + +int +in_stack(c, stack_top) + REGISTER int c, stack_top; +{ + if ( (isascii(c) && isprint(c) && c != '\\') || c == ' ' ) { + stack[--stack_top] = c; + } else { + switch( c ) { + case '\n': stack[--stack_top] = 'n'; break; + case '\t': stack[--stack_top] = 't'; break; + case '\b': stack[--stack_top] = 'b'; break; + case '\f': stack[--stack_top] = 'f'; break; + case '\r': stack[--stack_top] = 'r'; break; + case '\\': stack[--stack_top] = '\\'; break; + default: + stack[--stack_top] = '0' + c % 8; + stack[--stack_top] = '0' + (c / 8) % 8; + stack[--stack_top] = '0' + c / 64; + break; + } + stack[--stack_top] = '\\'; + } + if (stack_top<0) { + fprintf(stderr,"dump_tab stack overflow!!!\n"); + exit(1); + } + return stack_top; +} +#else +dump_tab() {} +#endif /* DEBUG2 */ +#endif /* DEBUG */ +#endif /* METAWARE */ + +void writeerr() +{ + perror ( ofname ); + unlink ( ofname ); + exit ( 1 ); +} + +void copystat(ifname, ofname) +char *ifname, *ofname; +{ + struct stat statbuf; + int mode; +#ifndef AZTEC86 + time_t timep[2]; +#else + unsigned long timep[2]; +#endif + fflush(stdout); + close(fileno(stdout)); + if (stat(ifname, &statbuf)) + { /* Get stat on input file */ + perror(ifname); + return; + } +#ifndef PCDOS + /* meddling with UNIX-style file modes */ + if ((statbuf.st_mode & S_IFMT/*0170000*/) != S_IFREG/*0100000*/) + { + if(quiet) + fprintf(stderr, "%s: ", ifname); + fprintf(stderr, " -- not a regular file: unchanged"); + exit_stat = 1; + } else if (statbuf.st_nlink > 1) + { + if(quiet) + fprintf(stderr, "%s: ", ifname); + fprintf(stderr, " -- has %d other links: unchanged", + statbuf.st_nlink - 1); + exit_stat = 1; + } else +#endif + if (exit_stat == 2 && (!force)) + { /* No compression: remove file.Z */ + if(!quiet) + fprintf(stderr, " -- file unchanged"); + } else + { /* ***** Successful Compression ***** */ + exit_stat = 0; +#ifndef PCDOS + mode = statbuf.st_mode & 07777; +#else + mode = statbuf.st_attr & 07777; +#endif + if (chmod(ofname, mode)) /* Copy modes */ + perror(ofname); +#ifndef PCDOS + chown(ofname, statbuf.st_uid, statbuf.st_gid); /* Copy ownership */ + timep[0] = statbuf.st_atime; + timep[1] = statbuf.st_mtime; +#else + timep[0] = statbuf.st_mtime; + timep[1] = statbuf.st_mtime; +#endif + utime(ofname, (struct utimbuf *)timep); /* Update last accessed and modified times */ +/* + if (unlink(ifname)) + perror(ifname); +*/ + if(!quiet) + if(do_decomp == 0) + fprintf(stderr, " -- compressed to %s", ofname); + else + fprintf(stderr, " -- decompressed to %s", ofname); + return; /* Successful return */ + } + + /* Unsuccessful return -- one of the tests failed */ + if (unlink(ofname)) + perror(ofname); +} +/* + * This routine returns 1 if we are running in the foreground and stderr + * is a tty. + */ +int foreground() +{ +#ifndef METAWARE + if(bgnd_flag) { /* background? */ + return(0); + } else { /* foreground */ +#endif + if(isatty(2)) { /* and stderr is a tty */ + return(1); + } else { + return(0); + } +#ifndef METAWARE + } +#endif +} +#ifndef METAWARE +void onintr (dummy) +int dummy; /* to keep the compiler happy */ +{ + (void)signal(SIGINT,SIG_IGN); + unlink ( ofname ); + exit ( 1 ); +} + +void oops (dummy) /* wild pointer -- assume bad input */ +int dummy; /* to keep the compiler happy */ +{ + (void)signal(SIGSEGV,SIG_IGN); + if ( do_decomp == 1 ) + fprintf ( stderr, "uncompress: corrupt input\n" ); + unlink ( ofname ); + exit ( 1 ); +} +#endif +void cl_block () /* table clear for block compress */ +{ + REGISTER long int rat; + + checkpoint = in_count + CHECK_GAP; +#ifdef DEBUG + if ( debug ) { + fprintf ( stderr, "count: %ld, ratio: ", in_count ); + prratio ( stderr, in_count, bytes_out ); + fprintf ( stderr, "\n"); + } +#endif /* DEBUG */ + + if(in_count > 0x007fffff) { /* shift will overflow */ + rat = bytes_out >> 8; + if(rat == 0) { /* Don't divide by zero */ + rat = 0x7fffffff; + } else { + rat = in_count / rat; + } + } else { + rat = (in_count << 8) / bytes_out; /* 8 fractional bits */ + } + if ( rat > ratio ) { + ratio = rat; + } else { + ratio = 0; +#ifdef DEBUG + if(verbose) + dump_tab(); /* dump string table */ +#endif + cl_hash ( (count_int) hsize ); + free_ent = FIRST; + clear_flg = 1; + output ( (code_int) CLEAR ); +#ifdef DEBUG + if(debug) + fprintf ( stderr, "clear\n" ); +#endif /* DEBUG */ + } +} + +void cl_hash(hsize) /* reset code table */ + REGISTER count_int hsize; +{ +#ifdef AZTEC86 +#ifdef PCDOS + /* This function only in PC-DOS lib, not in MINIX lib */ + memset(htab,-1, hsize * sizeof(count_int)); +#else +/* MINIX and all non-PC machines do it this way */ +#ifndef XENIX_16 /* Normal machine */ + REGISTER count_int *htab_p = htab+hsize; +#else + REGISTER j; + REGISTER long k = hsize; + REGISTER count_int *htab_p; +#endif + REGISTER long i; + REGISTER long m1 = -1; + +#ifdef XENIX_16 + for(j=0; j<=8 && k>=0; j++,k-=8192) + { + i = 8192; + if(k < 8192) + { + i = k; + } + htab_p = &(htab[j][i]); + i -= 16; + if(i > 0) + { +#else + i = hsize - 16; +#endif + do + { /* might use Sys V memset(3) here */ + *(htab_p-16) = m1; + *(htab_p-15) = m1; + *(htab_p-14) = m1; + *(htab_p-13) = m1; + *(htab_p-12) = m1; + *(htab_p-11) = m1; + *(htab_p-10) = m1; + *(htab_p-9) = m1; + *(htab_p-8) = m1; + *(htab_p-7) = m1; + *(htab_p-6) = m1; + *(htab_p-5) = m1; + *(htab_p-4) = m1; + *(htab_p-3) = m1; + *(htab_p-2) = m1; + *(htab_p-1) = m1; + htab_p -= 16; + } while ((i -= 16) >= 0); +#ifdef XENIX_16 + } + } +#endif + for ( i += 16; i > 0; i-- ) + *--htab_p = m1; +#endif +#endif +} + +void prratio(stream, num, den) +FILE *stream; +long int num; +long int den; +{ + REGISTER int q; /* Doesn't need to be long */ + if(num > 214748L) + { /* 2147483647/10000 */ + q = (int)(num / (den / 10000L)); + } else + { + q = (int)(10000L * num / den); /* Long calculations, though */ + } + if (q < 0) + { + putc('-', stream); + q = -q; + } + fprintf(stream, "%d.%02d%c", q / 100, q % 100, '%'); +} + +void version() +{ + fprintf(stderr, "compress 4.1\n"); + fprintf(stderr, "Options: "); +#ifdef vax + fprintf(stderr, "vax, "); +#endif +#ifdef _MINIX + fprintf(stderr, "MINIX, "); +#endif +#ifdef NO_UCHAR + fprintf(stderr, "NO_UCHAR, "); +#endif +#ifdef SIGNED_COMPARE_SLOW + fprintf(stderr, "SIGNED_COMPARE_SLOW, "); +#endif +#ifdef XENIX_16 + fprintf(stderr, "XENIX_16, "); +#endif +#ifdef COMPATIBLE + fprintf(stderr, "COMPATIBLE, "); +#endif +#ifdef DEBUG + fprintf(stderr, "DEBUG, "); +#endif +#ifdef BSD4_2 + fprintf(stderr, "BSD4_2, "); +#endif + fprintf(stderr, "BITS = %d\n", BITS); +} +/* End of text from uok.UUCP:net.sources */ + diff --git a/commands/simple/cp.c b/commands/simple/cp.c new file mode 100755 index 000000000..0b1b9b0c0 --- /dev/null +++ b/commands/simple/cp.c @@ -0,0 +1,1380 @@ +/* cp 1.12 - copy files Author: Kees J. Bot + * mv - move files 20 Jul 1993 + * rm - remove files + * ln - make a link + * cpdir - copy a directory tree (cp -psmr) + * clone - make a link farm (ln -fmr) + */ +#define nil 0 +#include <stdio.h> +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <stddef.h> +#include <unistd.h> +#include <fcntl.h> +#include <time.h> +#include <sys/stat.h> +#include <utime.h> +#include <dirent.h> +#include <errno.h> +#ifndef DEBUG +#define DEBUG 0 +#define NDEBUG 1 +#endif +#include <assert.h> + +#include <sys/dir.h> + +/* Copy files in this size chunks: */ +#if __minix && !__minix_vmd +#define CHUNK (8192 * sizeof(char *)) +#else +#define CHUNK (1024 << (sizeof(int) + sizeof(char *))) +#endif + + +#ifndef CONFORMING +#define CONFORMING 1 /* Precisely POSIX conforming. */ +#endif + + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +char *prog_name; /* Call name of this program. */ +int ex_code= 0; /* Final exit code. */ + +typedef enum identity { CP, MV, RM, LN, CPDIR, CLONE } identity_t; +typedef enum action { COPY, MOVE, REMOVE, LINK } action_t; + +identity_t identity; /* How did the user call me? */ +action_t action; /* Copying, moving, or linking. */ +int pflag= 0; /* -p/-s: Make orginal and copy the same. */ +int iflag= 0; /* -i: Interactive overwriting/deleting. */ +int fflag= 0; /* -f: Force. */ +int sflag= 0; /* -s: Make a symbolic link (ln/clone). */ +int Sflag= 0; /* -S: Make a symlink if across devices. */ +int mflag= 0; /* -m: Merge trees, no target dir trickery. */ +int rflag= 0; /* -r/-R: Recursively copy a tree. */ +int vflag= 0; /* -v: Verbose. */ +int xflag= 0; /* -x: Don't traverse past mount points. */ +int xdev= 0; /* Set when moving or linking cross-device. */ +int expand= 0; /* Expand symlinks, ignore links. */ +int conforming= CONFORMING; /* Sometimes standards are a pain. */ + +int fc_mask; /* File creation mask. */ +int uid, gid; /* Effective uid & gid. */ +int istty; /* Can have terminal input. */ + +#ifndef S_ISLNK +/* There were no symlinks in medieval times. */ +#define S_ISLNK(mode) (0) +#define lstat stat +#define symlink(path1, path2) (errno= ENOSYS, -1) +#define readlink(path, buf, len) (errno= ENOSYS, -1) +#endif + +void report(const char *label) +{ + if (action == REMOVE && fflag) return; + fprintf(stderr, "%s: %s: %s\n", prog_name, label, strerror(errno)); + ex_code= 1; +} + +void fatal(const char *label) +{ + report(label); + exit(1); +} + +void report2(const char *src, const char *dst) +{ + fprintf(stderr, "%s %s %s: %s\n", prog_name, src, dst, strerror(errno)); + ex_code= 1; +} + +#if DEBUG +size_t nchunks= 0; /* Number of allocated cells. */ +#endif + +void *allocate(void *mem, size_t size) +/* Like realloc, but with checking of the return value. */ +{ +#if DEBUG + if (mem == nil) nchunks++; +#endif + if ((mem= mem == nil ? malloc(size) : realloc(mem, size)) == nil) + fatal("malloc()"); + return mem; +} + +void deallocate(void *mem) +/* Release a chunk of memory. */ +{ + if (mem != nil) { +#if DEBUG + nchunks--; +#endif + free(mem); + } +} + +typedef struct pathname { + char *path; /* The actual pathname. */ + size_t idx; /* Index for the terminating null byte. */ + size_t lim; /* Actual length of the path array. */ +} pathname_t; + +void path_init(pathname_t *pp) +/* Initialize a pathname to the null string. */ +{ + pp->path= allocate(nil, pp->lim= DIRSIZ + 2); + pp->path[pp->idx= 0]= 0; +} + +void path_add(pathname_t *pp, const char *name) +/* Add a component to a pathname. */ +{ + size_t lim; + char *p; + + lim= pp->idx + strlen(name) + 2; + + if (lim > pp->lim) { + pp->lim= lim += lim/2; /* add an extra 50% growing space. */ + + pp->path= allocate(pp->path, lim); + } + + p= pp->path + pp->idx; + if (p > pp->path && p[-1] != '/') *p++ = '/'; + + while (*name != 0) { + if (*name != '/' || p == pp->path || p[-1] != '/') *p++ = *name; + name++; + } + *p = 0; + pp->idx= p - pp->path; +} + +void path_trunc(pathname_t *pp, size_t didx) +/* Delete part of a pathname to a remembered length. */ +{ + pp->path[pp->idx= didx]= 0; +} + +#if DEBUG +const char *path_name(const pathname_t *pp) +/* Return the actual name as a C string. */ +{ + return pp->path; +} + +size_t path_length(const pathname_t *pp) +/* The length of the pathname. */ +{ + return pp->idx; +} + +void path_drop(pathname_t *pp) +/* Release the storage occupied by the pathname. */ +{ + deallocate(pp->path); +} + +#else /* !DEBUG */ +#define path_name(pp) ((const char *) (pp)->path) +#define path_length(pp) ((pp)->idx) +#define path_drop(pp) deallocate((void *) (pp)->path) +#endif /* !DEBUG */ + +char *basename(const char *path) +/* Return the last component of a pathname. (Note: declassifies a const + * char * just like strchr. + */ +{ + const char *p= path; + + for (;;) { + while (*p == '/') p++; /* Trailing slashes? */ + + if (*p == 0) break; + + path= p; + while (*p != 0 && *p != '/') p++; /* Skip component. */ + } + return (char *) path; +} + +int affirmative(void) +/* Get a yes/no answer from the suspecting user. */ +{ + int c; + int ok; + + fflush(stdout); + fflush(stderr); + + while ((c= getchar()) == ' ') {} + ok= (c == 'y' || c == 'Y'); + while (c != EOF && c != '\n') c= getchar(); + + return ok; +} + +int writable(const struct stat *stp) +/* True iff the file with the given attributes allows writing. (And we have + * a terminal to ask if ok to overwrite.) + */ +{ + if (!istty || uid == 0) return 1; + if (stp->st_uid == uid) return stp->st_mode & S_IWUSR; + if (stp->st_gid == gid) return stp->st_mode & S_IWGRP; + return stp->st_mode & S_IWOTH; +} + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +static char *link_islink(struct stat *stp, const char *file) +{ + /* Tell if a file, which stat(2) information in '*stp', has been seen + * earlier by this function under a different name. If not return a + * null pointer with errno set to ENOENT, otherwise return the name of + * the link. Return a null pointer with an error code in errno for any + * error, using E2BIG for a too long file name. + * + * Use link_islink(nil, nil) to reset all bookkeeping. + * + * Call for a file twice to delete it from the store. + */ + + typedef struct link { /* In-memory link store. */ + struct link *next; /* Hash chain on inode number. */ + ino_t ino; /* File's inode number. */ + off_t off; /* Offset to more info in temp file. */ + } link_t; + typedef struct dlink { /* On-disk link store. */ + dev_t dev; /* Device number. */ + char file[PATH_MAX]; /* Name of earlier seen link. */ + } dlink_t; + static link_t *links[256]; /* Hash list of known links. */ + static int tfd= -1; /* Temp file for file name storage. */ + static dlink_t dlink; + link_t *lp, **plp; + size_t len; + off_t off; + + if (file == nil) { + /* Reset everything. */ + for (plp= links; plp < arraylimit(links); plp++) { + while ((lp= *plp) != nil) { + *plp= lp->next; + free(lp); + } + } + if (tfd != -1) close(tfd); + tfd= -1; + return nil; + } + + /* The file must be a non-directory with more than one link. */ + if (S_ISDIR(stp->st_mode) || stp->st_nlink <= 1) { + errno= ENOENT; + return nil; + } + + plp= &links[stp->st_ino % arraysize(links)]; + + while ((lp= *plp) != nil) { + if (lp->ino == stp->st_ino) { + /* May have seen this link before. Get it and check. */ + if (lseek(tfd, lp->off, SEEK_SET) == -1) return nil; + if (read(tfd, &dlink, sizeof(dlink)) < 0) return nil; + + /* Only need to check the device number. */ + if (dlink.dev == stp->st_dev) { + if (strcmp(file, dlink.file) == 0) { + /* Called twice. Forget about this link. */ + *plp= lp->next; + free(lp); + errno= ENOENT; + return nil; + } + + /* Return the name of the earlier link. */ + return dlink.file; + } + } + plp= &lp->next; + } + + /* First time I see this link. Add it to the store. */ + if (tfd == -1) { + for (;;) { + char *tmp; + + tmp= tmpnam(nil); + tfd= open(tmp, O_RDWR|O_CREAT|O_EXCL, 0600); + if (tfd < 0) { + if (errno != EEXIST) return nil; + } else { + (void) unlink(tmp); + break; + } + } + } + if ((len= strlen(file)) >= PATH_MAX) { + errno= E2BIG; + return nil; + } + + dlink.dev= stp->st_dev; + strcpy(dlink.file, file); + len += offsetof(dlink_t, file) + 1; + if ((off= lseek(tfd, 0, SEEK_END)) == -1) return nil; + if (write(tfd, &dlink, len) != len) return nil; + + if ((lp= malloc(sizeof(*lp))) == nil) return nil; + lp->next= nil; + lp->ino= stp->st_ino; + lp->off= off; + *plp= lp; + errno= ENOENT; + return nil; +} + +int trylink(const char *src, const char *dst, struct stat *srcst, + struct stat *dstst) +/* Keep the link structure intact if src has been seen before. */ +{ + char *olddst; + int linked; + + if (action == COPY && expand) return 0; + + if ((olddst= link_islink(srcst, dst)) == nil) { + /* if (errno != ENOENT) ... */ + return 0; + } + + /* Try to link the file copied earlier to the new file. */ + if (dstst->st_ino != 0) (void) unlink(dst); + + if ((linked= (link(olddst, dst) == 0)) && vflag) + printf("ln %s %s\n", olddst, dst); + + return linked; +} + +int copy(const char *src, const char *dst, struct stat *srcst, + struct stat *dstst) +/* Copy one file to another and copy (some of) the attributes. */ +{ + char buf[CHUNK]; + int srcfd, dstfd; + ssize_t n; + + assert(srcst->st_ino != 0); + + if (dstst->st_ino == 0) { + /* The file doesn't exist yet. */ + + if (!S_ISREG(srcst->st_mode)) { + /* Making a new mode 666 regular file. */ + srcst->st_mode= (S_IFREG | 0666) & fc_mask; + } else + if (!pflag && conforming) { + /* Making a new file copying mode with umask applied. */ + srcst->st_mode &= fc_mask; + } + } else { + /* File exists, ask if ok to overwrite if '-i'. */ + + if (iflag || (action == MOVE && !fflag && !writable(dstst))) { + fprintf(stderr, "Overwrite %s? (mode = %03o) ", + dst, dstst->st_mode & 07777); + if (!affirmative()) return 0; + } + + if (action == MOVE) { + /* Don't overwrite, remove first. */ + if (unlink(dst) < 0 && errno != ENOENT) { + report(dst); + return 0; + } + } else { + /* Overwrite. */ + if (!pflag) { + /* Keep the existing mode and ownership. */ + srcst->st_mode= dstst->st_mode; + srcst->st_uid= dstst->st_uid; + srcst->st_gid= dstst->st_gid; + } + } + } + + /* Keep the link structure if possible. */ + if (trylink(src, dst, srcst, dstst)) return 1; + + if ((srcfd= open(src, O_RDONLY)) < 0) { + report(src); + return 0; + } + + dstfd= open(dst, O_WRONLY|O_CREAT|O_TRUNC, srcst->st_mode & 0777); + if (dstfd < 0 && fflag && errno == EACCES) { + /* Retry adding a "w" bit. */ + (void) chmod(dst, dstst->st_mode | S_IWUSR); + dstfd= open(dst, O_WRONLY|O_CREAT|O_TRUNC, 0); + } + if (dstfd < 0 && fflag && errno == EACCES) { + /* Retry after trying to delete. */ + (void) unlink(dst); + dstfd= open(dst, O_WRONLY|O_CREAT|O_TRUNC, 0); + } + if (dstfd < 0) { + report(dst); + close(srcfd); + return 0; + } + + /* Get current parameters. */ + if (fstat(dstfd, dstst) < 0) { + report(dst); + close(srcfd); + close(dstfd); + return 0; + } + + /* Copy the little bytes themselves. */ + while ((n= read(srcfd, buf, sizeof(buf))) > 0) { + char *bp = buf; + ssize_t r; + + while (n > 0 && (r= write(dstfd, bp, n)) > 0) { + bp += r; + n -= r; + } + if (r <= 0) { + if (r == 0) { + fprintf(stderr, + "%s: Warning: EOF writing to %s\n", + prog_name, dst); + break; + } + fatal(dst); + } + } + + if (n < 0) { + report(src); + close(srcfd); + close(dstfd); + return 0; + } + + close(srcfd); + close(dstfd); + + /* Copy the ownership. */ + if ((pflag || !conforming) + && S_ISREG(dstst->st_mode) + && (dstst->st_uid != srcst->st_uid + || dstst->st_gid != srcst->st_gid) + ) { + if (chmod(dst, 0) == 0) dstst->st_mode&= ~07777; + if (chown(dst, srcst->st_uid, srcst->st_gid) < 0) { + if (errno != EPERM) { + report(dst); + return 0; + } + } else { + dstst->st_uid= srcst->st_uid; + dstst->st_gid= srcst->st_gid; + } + } + + if (conforming && S_ISREG(dstst->st_mode) + && (dstst->st_uid != srcst->st_uid + || dstst->st_gid != srcst->st_gid) + ) { + /* Suid bits must be cleared in the holy name of + * security (and the assumed user stupidity). + */ + srcst->st_mode&= ~06000; + } + + /* Copy the mode. */ + if (S_ISREG(dstst->st_mode) && dstst->st_mode != srcst->st_mode) { + if (chmod(dst, srcst->st_mode) < 0) { + if (errno != EPERM) { + report(dst); + return 0; + } + fprintf(stderr, "%s: Can't change the mode of %s\n", + prog_name, dst); + } + } + + /* Copy the file modification time. */ + if ((pflag || !conforming) && S_ISREG(dstst->st_mode)) { + struct utimbuf ut; + + ut.actime= action == MOVE ? srcst->st_atime : time(nil); + ut.modtime= srcst->st_mtime; + if (utime(dst, &ut) < 0) { + if (errno != EPERM) { + report(dst); + return 0; + } + if (pflag) { + fprintf(stderr, + "%s: Can't set the time of %s\n", + prog_name, dst); + } + } + } + if (vflag) { + printf(action == COPY ? "cp %s %s\n" : "mv %s %s\n", src, dst); + } + return 1; +} + +void copy1(const char *src, const char *dst, struct stat *srcst, + struct stat *dstst) +/* Inspect the source file and then copy it. Treatment of symlinks and + * special files is a bit complicated. The filetype and link-structure are + * ignored if (expand && !rflag), symlinks and link-structure are ignored + * if (expand && rflag), everything is copied precisely if !expand. + */ +{ + int r, linked; + + assert(srcst->st_ino != 0); + + if (srcst->st_ino == dstst->st_ino && srcst->st_dev == dstst->st_dev) { + fprintf(stderr, "%s: can't copy %s onto itself\n", + prog_name, src); + ex_code= 1; + return; + } + + /* You can forget it if the destination is a directory. */ + if (dstst->st_ino != 0 && S_ISDIR(dstst->st_mode)) { + errno= EISDIR; + report(dst); + return; + } + + if (S_ISREG(srcst->st_mode) || (expand && !rflag)) { + if (!copy(src, dst, srcst, dstst)) return; + + if (action == MOVE && unlink(src) < 0) { + report(src); + return; + } + return; + } + + if (dstst->st_ino != 0) { + if (iflag || (action == MOVE && !fflag && !writable(dstst))) { + fprintf(stderr, "Replace %s? (mode = %03o) ", + dst, dstst->st_mode & 07777); + if (!affirmative()) return; + } + if (unlink(dst) < 0) { + report(dst); + return; + } + dstst->st_ino= 0; + } + + /* Apply the file creation mask if so required. */ + if (!pflag && conforming) srcst->st_mode &= fc_mask; + + linked= 0; + + if (S_ISLNK(srcst->st_mode)) { + char buf[1024+1]; + + if ((r= readlink(src, buf, sizeof(buf)-1)) < 0) { + report(src); + return; + } + buf[r]= 0; + r= symlink(buf, dst); + if (vflag && r == 0) + printf("ln -s %s %s\n", buf, dst); + } else + if (trylink(src, dst, srcst, dstst)) { + linked= 1; + r= 0; + } else + if (S_ISFIFO(srcst->st_mode)) { + r= mkfifo(dst, srcst->st_mode); + if (vflag && r == 0) + printf("mkfifo %s\n", dst); + } else + if (S_ISBLK(srcst->st_mode) || S_ISCHR(srcst->st_mode)) { + r= mknod(dst, srcst->st_mode, srcst->st_rdev); + if (vflag && r == 0) { + printf("mknod %s %c %d %d\n", + dst, + S_ISBLK(srcst->st_mode) ? 'b' : 'c', + (srcst->st_rdev >> 8) & 0xFF, + (srcst->st_rdev >> 0) & 0xFF); + } + } else { + fprintf(stderr, "%s: %s: odd filetype %5o (not copied)\n", + prog_name, src, srcst->st_mode); + ex_code= 1; + return; + } + + if (r < 0 || lstat(dst, dstst) < 0) { + report(dst); + return; + } + + if (action == MOVE && unlink(src) < 0) { + report(src); + (void) unlink(dst); /* Don't want it twice. */ + return; + } + + if (linked) return; + + if (S_ISLNK(srcst->st_mode)) return; + + /* Copy the ownership. */ + if ((pflag || !conforming) + && (dstst->st_uid != srcst->st_uid + || dstst->st_gid != srcst->st_gid) + ) { + if (chown(dst, srcst->st_uid, srcst->st_gid) < 0) { + if (errno != EPERM) { + report(dst); + return; + } + } + } + + /* Copy the file modification time. */ + if (pflag || !conforming) { + struct utimbuf ut; + + ut.actime= action == MOVE ? srcst->st_atime : time(nil); + ut.modtime= srcst->st_mtime; + if (utime(dst, &ut) < 0) { + if (errno != EPERM) { + report(dst); + return; + } + fprintf(stderr, "%s: Can't set the time of %s\n", + prog_name, dst); + } + } +} + +void remove1(const char *src, struct stat *srcst) +{ + if (iflag || (!fflag && !writable(srcst))) { + fprintf(stderr, "Remove %s? (mode = %03o) ", src, + srcst->st_mode & 07777); + if (!affirmative()) return; + } + if (unlink(src) < 0) { + report(src); + } else { + if (vflag) printf("rm %s\n", src); + } +} + +void link1(const char *src, const char *dst, struct stat *srcst, + struct stat *dstst) +{ + pathname_t sym; + const char *p; + + if (dstst->st_ino != 0 && (iflag || fflag)) { + if (srcst->st_ino == dstst->st_ino) { + if (fflag) return; + fprintf(stderr, "%s: Can't link %s onto itself\n", + prog_name, src); + ex_code= 1; + return; + } + if (iflag) { + fprintf(stderr, "Remove %s? ", dst); + if (!affirmative()) return; + } + errno= EISDIR; + if (S_ISDIR(dstst->st_mode) || unlink(dst) < 0) { + report(dst); + return; + } + } + + if (!sflag && !(rflag && S_ISLNK(srcst->st_mode)) && !(Sflag && xdev)) { + /* A normal link. */ + if (link(src, dst) < 0) { + if (!Sflag || errno != EXDEV) { + report2(src, dst); + return; + } + /* Can't do a cross-device link, we have to symlink. */ + xdev= 1; + } else { + if (vflag) printf("ln %s %s\n", src, dst); + return; + } + } + + /* Do a symlink. */ + if (!rflag && !Sflag) { + /* We can get away with a "don't care if it works" symlink. */ + if (symlink(src, dst) < 0) { + report(dst); + return; + } + if (vflag) printf("ln -s %s %s\n", src, dst); + return; + } + + /* If the source is a symlink then it is simply copied. */ + if (S_ISLNK(srcst->st_mode)) { + int r; + char buf[1024+1]; + + if ((r= readlink(src, buf, sizeof(buf)-1)) < 0) { + report(src); + return; + } + buf[r]= 0; + if (symlink(buf, dst) < 0) { + report(dst); + return; + } + if (vflag) printf("ln -s %s %s\n", buf, dst); + return; + } + + /* Make a symlink that has to work, i.e. we must be able to access the + * source now, and the link must work. + */ + if (dst[0] == '/' && src[0] != '/') { + /* ln -[rsS] relative/path /full/path. */ + fprintf(stderr, + "%s: Symlinking %s to %s is too difficult for me to figure out\n", + prog_name, src, dst); + exit(1); + } + + /* Count the number of subdirectories in the destination file and + * add one '..' for each. + */ + path_init(&sym); + if (src[0] != '/') { + p= dst; + while (*p != 0) { + if (p[0] == '.') { + if (p[1] == '/' || p[1] == 0) { + /* A "." component; skip. */ + do p++; while (*p == '/'); + continue; + } else + if (p[1] == '.' && (p[2] == '/' || p[2] == 0)) { + /* A ".." component; oops. */ + switch (path_length(&sym)) { + case 0: + fprintf(stderr, + "%s: Symlinking %s to %s is too difficult for me to figure out\n", + prog_name, src, dst); + exit(1); + case 2: + path_trunc(&sym, 0); + break; + default: + path_trunc(&sym, path_length(&sym) - 3); + } + p++; + do p++; while (*p == '/'); + continue; + } + } + while (*p != 0 && *p != '/') p++; + while (*p == '/') p++; + if (*p == 0) break; + path_add(&sym, ".."); + } + } + path_add(&sym, src); + + if (symlink(path_name(&sym), dst) < 0) { + report(dst); + } else { + if (vflag) printf("ln -s %s %s\n", path_name(&sym), dst); + } + path_drop(&sym); +} + +typedef struct entrylist { + struct entrylist *next; + char *name; +} entrylist_t; + +int eat_dir(const char *dir, entrylist_t **dlist) +/* Make a linked list of all the names in a directory. */ +{ + DIR *dp; + struct dirent *entry; + + if ((dp= opendir(dir)) == nil) return 0; + + while ((entry= readdir(dp)) != nil) { + if (strcmp(entry->d_name, ".") == 0) continue; + if (strcmp(entry->d_name, "..") == 0) continue; + + *dlist= allocate(nil, sizeof(**dlist)); + (*dlist)->name= allocate(nil, strlen(entry->d_name)+1); + strcpy((*dlist)->name, entry->d_name); + dlist= &(*dlist)->next; + } + closedir(dp); + *dlist= nil; + return 1; +} + +void chop_dlist(entrylist_t **dlist) +/* Chop an entry of a name list. */ +{ + entrylist_t *junk= *dlist; + + *dlist= junk->next; + deallocate(junk->name); + deallocate(junk); +} + +void drop_dlist(entrylist_t *dlist) +/* Get rid of a whole list. */ +{ + while (dlist != nil) chop_dlist(&dlist); +} + +void do1(pathname_t *src, pathname_t *dst, int depth) +/* Perform the appropriate action on a source and destination file. */ +{ + size_t slashsrc, slashdst; + struct stat srcst, dstst; + entrylist_t *dlist; + static ino_t topdst_ino; + static dev_t topdst_dev; + static dev_t topsrc_dev; + +#if DEBUG + if (vflag && depth == 0) { + char flags[100], *pf= flags; + + if (pflag) *pf++= 'p'; + if (iflag) *pf++= 'i'; + if (fflag) *pf++= 'f'; + if (sflag) *pf++= 's'; + if (Sflag) *pf++= 'S'; + if (mflag) *pf++= 'm'; + if (rflag) *pf++= 'r'; + if (vflag) *pf++= 'v'; + if (xflag) *pf++= 'x'; + if (expand) *pf++= 'L'; + if (conforming) *pf++= 'C'; + *pf= 0; + printf(": %s -%s %s %s\n", prog_name, flags, + path_name(src), path_name(dst)); + } +#endif + + /* st_ino == 0 if not stat()'ed yet, or nonexistent. */ + srcst.st_ino= 0; + dstst.st_ino= 0; + + if (action != LINK || !sflag || rflag) { + /* Source must exist unless symlinking. */ + if ((expand ? stat : lstat)(path_name(src), &srcst) < 0) { + report(path_name(src)); + return; + } + } + + if (depth == 0) { + /* First call: Not cross-device yet, first dst not seen yet, + * remember top device number. + */ + xdev= 0; + topdst_ino= 0; + topsrc_dev= srcst.st_dev; + } + + /* Inspect the intended destination unless removing. */ + if (action != REMOVE) { + if ((expand ? stat : lstat)(path_name(dst), &dstst) < 0) { + if (errno != ENOENT) { + report(path_name(dst)); + return; + } + } + } + + if (action == MOVE && !xdev) { + if (dstst.st_ino != 0 && srcst.st_dev != dstst.st_dev) { + /* It's a cross-device rename, i.e. copy and remove. */ + xdev= 1; + } else + if (!mflag || dstst.st_ino == 0 || !S_ISDIR(dstst.st_mode)) { + /* Try to simply rename the file (not merging trees). */ + + if (srcst.st_ino == dstst.st_ino) { + fprintf(stderr, + "%s: Can't move %s onto itself\n", + prog_name, path_name(src)); + ex_code= 1; + return; + } + + if (dstst.st_ino != 0) { + if (iflag || (!fflag && !writable(&dstst))) { + fprintf(stderr, + "Replace %s? (mode = %03o) ", + path_name(dst), + dstst.st_mode & 07777); + if (!affirmative()) return; + } + if (!S_ISDIR(dstst.st_mode)) + (void) unlink(path_name(dst)); + } + + if (rename(path_name(src), path_name(dst)) == 0) { + /* Success. */ + if (vflag) { + printf("mv %s %s\n", path_name(src), + path_name(dst)); + } + return; + } + if (errno == EXDEV) { + xdev= 1; + } else { + report2(path_name(src), path_name(dst)); + return; + } + } + } + + if (srcst.st_ino == 0 || !S_ISDIR(srcst.st_mode)) { + /* Copy/move/remove/link a single file. */ + switch (action) { + case COPY: + case MOVE: + copy1(path_name(src), path_name(dst), &srcst, &dstst); + break; + case REMOVE: + remove1(path_name(src), &srcst); + break; + case LINK: + link1(path_name(src), path_name(dst), &srcst, &dstst); + break; + } + return; + } + + /* Recursively copy/move/remove/link a directory if -r or -R. */ + if (!rflag) { + errno= EISDIR; + report(path_name(src)); + return; + } + + /* Ok to remove contents of dir? */ + if (action == REMOVE) { + if (xflag && topsrc_dev != srcst.st_dev) { + /* Don't recurse past a mount point. */ + return; + } + if (iflag) { + fprintf(stderr, "Remove contents of %s? ", path_name(src)); + if (!affirmative()) return; + } + } + + /* Gather the names in the source directory. */ + if (!eat_dir(path_name(src), &dlist)) { + report(path_name(src)); + return; + } + + /* Check/create the target directory. */ + if (action != REMOVE && dstst.st_ino != 0 && !S_ISDIR(dstst.st_mode)) { + if (action != MOVE && !fflag) { + errno= ENOTDIR; + report(path_name(dst)); + return; + } + if (iflag) { + fprintf(stderr, "Replace %s? ", path_name(dst)); + if (!affirmative()) { + drop_dlist(dlist); + return; + } + } + if (unlink(path_name(dst)) < 0) { + report(path_name(dst)); + drop_dlist(dlist); + return; + } + dstst.st_ino= 0; + } + + if (action != REMOVE) { + if (dstst.st_ino == 0) { + /* Create a new target directory. */ + if (!pflag && conforming) srcst.st_mode&= fc_mask; + + if (mkdir(path_name(dst), srcst.st_mode | S_IRWXU) < 0 + || stat(path_name(dst), &dstst) < 0) { + report(path_name(dst)); + drop_dlist(dlist); + return; + } + if (vflag) printf("mkdir %s\n", path_name(dst)); + } else { + /* Target directory already exists. */ + if (action == MOVE && !mflag) { + errno= EEXIST; + report(path_name(dst)); + drop_dlist(dlist); + return; + } + if (!pflag) { + /* Keep the existing attributes. */ + srcst.st_mode= dstst.st_mode; + srcst.st_uid= dstst.st_uid; + srcst.st_gid= dstst.st_gid; + srcst.st_mtime= dstst.st_mtime; + } + } + + if (topdst_ino == 0) { + /* Remember the top destination. */ + topdst_dev= dstst.st_dev; + topdst_ino= dstst.st_ino; + } + + if (srcst.st_ino == topdst_ino && srcst.st_dev == topdst_dev) { + /* E.g. cp -r /shallow /shallow/deep. */ + fprintf(stderr, + "%s%s %s/ %s/: infinite recursion avoided\n", + prog_name, action != MOVE ? " -r" : "", + path_name(src), path_name(dst)); + drop_dlist(dlist); + return; + } + + if (xflag && topsrc_dev != srcst.st_dev) { + /* Don't recurse past a mount point. */ + drop_dlist(dlist); + return; + } + } + + /* Go down. */ + slashsrc= path_length(src); + slashdst= path_length(dst); + + while (dlist != nil) { + path_add(src, dlist->name); + if (action != REMOVE) path_add(dst, dlist->name); + + do1(src, dst, depth+1); + + path_trunc(src, slashsrc); + path_trunc(dst, slashdst); + chop_dlist(&dlist); + } + + if (action == MOVE || action == REMOVE) { + /* The contents of the source directory should have + * been (re)moved above. Get rid of the empty dir. + */ + if (action == REMOVE && iflag) { + fprintf(stderr, "Remove directory %s? ", + path_name(src)); + if (!affirmative()) return; + } + if (rmdir(path_name(src)) < 0) { + if (errno != ENOTEMPTY) report(path_name(src)); + return; + } + if (vflag) printf("rmdir %s\n", path_name(src)); + } + + if (action != REMOVE) { + /* Set the attributes of a new directory. */ + struct utimbuf ut; + + /* Copy the ownership. */ + if ((pflag || !conforming) + && (dstst.st_uid != srcst.st_uid + || dstst.st_gid != srcst.st_gid) + ) { + if (chown(path_name(dst), srcst.st_uid, + srcst.st_gid) < 0) { + if (errno != EPERM) { + report(path_name(dst)); + return; + } + } + } + + /* Copy the mode. */ + if (dstst.st_mode != srcst.st_mode) { + if (chmod(path_name(dst), srcst.st_mode) < 0) { + report(path_name(dst)); + return; + } + } + + /* Copy the file modification time. */ + if (dstst.st_mtime != srcst.st_mtime) { + ut.actime= action == MOVE ? srcst.st_atime : time(nil); + ut.modtime= srcst.st_mtime; + if (utime(path_name(dst), &ut) < 0) { + if (errno != EPERM) { + report(path_name(dst)); + return; + } + fprintf(stderr, + "%s: Can't set the time of %s\n", + prog_name, path_name(dst)); + } + } + } +} + +void usage(void) +{ + char *flags1, *flags2; + + switch (identity) { + case CP: + flags1= "pifsmrRvx"; + flags2= "pifsrRvx"; + break; + case MV: + flags1= "ifsmvx"; + flags2= "ifsvx"; + break; + case RM: + fprintf(stderr, "Usage: rm [-ifrRvx] file ...\n"); + exit(1); + case LN: + flags1= "ifsSmrRvx"; + flags2= "ifsSrRvx"; + break; + case CPDIR: + flags1= "ifvx"; + flags2= nil; + break; + case CLONE: + flags1= "ifsSvx"; + flags2= nil; + break; + } + fprintf(stderr, "Usage: %s [-%s] file1 file2\n", prog_name, flags1); + if (flags2 != nil) + fprintf(stderr, " %s [-%s] file ... dir\n", prog_name, flags2); + exit(1); +} + +void main(int argc, char **argv) +{ + int i; + char *flags; + struct stat st; + pathname_t src, dst; + size_t slash; + +#if DEBUG >= 3 + /* The first argument is the call name while debugging. */ + if (argc < 2) exit(-1); + argv++; + argc--; +#endif +#if DEBUG + vflag= isatty(1); +#endif + + /* Call name of this program. */ + prog_name= basename(argv[0]); + + /* Required action. */ + if (strcmp(prog_name, "cp") == 0) { + identity= CP; + action= COPY; + flags= "pifsmrRvx"; + expand= 1; + } else + if (strcmp(prog_name, "mv") == 0) { + identity= MV; + action= MOVE; + flags= "ifsmvx"; + rflag= pflag= 1; + } else + if (strcmp(prog_name, "rm") == 0) { + identity= RM; + action= REMOVE; + flags= "ifrRvx"; + } else + if (strcmp(prog_name, "ln") == 0) { + identity= LN; + action= LINK; + flags= "ifsSmrRvx"; + } else + if (strcmp(prog_name, "cpdir") == 0) { + identity= CPDIR; + action= COPY; + flags= "pifsmrRvx"; + rflag= mflag= pflag= 1; + conforming= 0; + } else + if (strcmp(prog_name, "clone") == 0) { + identity= CLONE; + action= LINK; + flags= "ifsSmrRvx"; + rflag= mflag= fflag= 1; + } else { + fprintf(stderr, + "%s: Identity crisis, not called cp, mv, rm, ln, cpdir, or clone\n", + prog_name); + exit(1); + } + + /* Who am I?, where am I?, how protective am I? */ + uid= geteuid(); + gid= getegid(); + istty= isatty(0); + fc_mask= ~umask(0); + + /* Gather flags. */ + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++] + 1; + + if (opt[0] == '-' && opt[1] == 0) break; /* -- */ + + while (*opt != 0) { + /* Flag supported? */ + if (strchr(flags, *opt) == nil) usage(); + + switch (*opt++) { + case 'p': + pflag= 1; + break; + case 'i': + iflag= 1; + if (action == MOVE) fflag= 0; + break; + case 'f': + fflag= 1; + if (action == MOVE) iflag= 0; + break; + case 's': + if (action == LINK) { + sflag= 1; + } else { + /* Forget about POSIX, do it right. */ + conforming= 0; + } + break; + case 'S': + Sflag= 1; + break; + case 'm': + mflag= 1; + break; + case 'r': + expand= 0; + /*FALL THROUGH*/ + case 'R': + rflag= 1; + break; + case 'v': + vflag= 1; + break; + case 'x': + xflag= 1; + break; + default: + assert(0); + } + } + } + + switch (action) { + case REMOVE: + if (i == argc) usage(); + break; + case LINK: + /* 'ln dir/file' is to be read as 'ln dir/file .'. */ + if ((argc - i) == 1 && action == LINK) argv[argc++]= "."; + /*FALL THROUGH*/ + default: + if ((argc - i) < 2) usage(); + } + + path_init(&src); + path_init(&dst); + + if (action != REMOVE && !mflag + && stat(argv[argc-1], &st) >= 0 && S_ISDIR(st.st_mode) + ) { + /* The last argument is a directory, this means we have to + * throw the whole lot into this directory. This is the + * Right Thing unless you use -r. + */ + path_add(&dst, argv[argc-1]); + slash= path_length(&dst); + + do { + path_add(&src, argv[i]); + path_add(&dst, basename(argv[i])); + + do1(&src, &dst, 0); + + path_trunc(&src, 0); + path_trunc(&dst, slash); + } while (++i < argc-1); + } else + if (action == REMOVE || (argc - i) == 2) { + /* Just two files (or many files for rm). */ + do { + path_add(&src, argv[i]); + if (action != REMOVE) path_add(&dst, argv[i+1]); + + do1(&src, &dst, 0); + path_trunc(&src, 0); + } while (action == REMOVE && ++i < argc); + } else { + usage(); + } + path_drop(&src); + path_drop(&dst); + +#if DEBUG + if (nchunks != 0) { + fprintf(stderr, "(%ld chunks of memory not freed)\n", + (long) nchunks); + } +#endif + exit(ex_code); +} diff --git a/commands/simple/crc.c b/commands/simple/crc.c new file mode 100755 index 000000000..adf32c157 --- /dev/null +++ b/commands/simple/crc.c @@ -0,0 +1,117 @@ +/* Compute checksum Author: Johan W. Stevenson */ + +/* Copyright 1988 by Johan W. Stevenson */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +int errs; + +#if __STDC__ +int main(int argc, char **argv); +void crc(char *fname); +#else +void crc(); +#endif + +int main(argc, argv) +int argc; +char **argv; +{ + char line[256]; + + if (argc <= 1) + crc((char *) 0); + else if (argc == 2 && strcmp(argv[1], "-") == 0) + while (fgets(line, sizeof line, stdin) != NULL) { + if (line[strlen(line) - 1] == '\n') + line[strlen(line) - 1] = '\0'; + crc(line); + } + else + do { + crc(argv[1]); + argv++; + argc--; + } while (argc > 1); + return(errs != 0); +} + +/* Crctab calculated by Mark G. Mendel, Network Systems Corporation */ +static unsigned short crctab[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +/* Updcrc macro derived from article Copyright (C) 1986 Stephen Satchell. + * NOTE: First argument must be in range 0 to 255. + * Second argument is referenced twice. + * + * Programmers may incorporate any or all code into their programs, + * giving proper credit within the source. Publication of the + * source routines is permitted so long as proper credit is given + * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, + * Omen Technology. + */ + +#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp) + +void crc(fname) +char *fname; +{ + register int c; + register long len = 0; + register unsigned short crc = 0; + register FILE *fp; + + if (fname == NULL) + fp = stdin; + else if ((fp = fopen(fname, "r")) == NULL) { + fprintf(stderr, "crc: Can't open %s: %s\n", fname, strerror(errno)); + errs++; + return; + } + while ((c = getc(fp)) != EOF) { + len++; + crc = updcrc(c, crc); + } + printf("%05u %6ld", crc, len); + if (fname) { + printf(" %s", fname); + fclose(fp); + } + printf("\n"); +} diff --git a/commands/simple/cut.c b/commands/simple/cut.c new file mode 100755 index 000000000..79d57f830 --- /dev/null +++ b/commands/simple/cut.c @@ -0,0 +1,310 @@ +/* cut - extract columns from a file or stdin. Author: Michael J. Holme + * + * Copyright 1989, Michael John Holme, All rights reserved. + * This code may be freely distributed, provided that this notice + * remains intact. + * + * V1.1: 6th September 1989 + * + * Bugs, criticisms, etc, + * c/o Mark Powell + * JANET sq79@uk.ac.liv + * ARPA sq79%liv.ac.uk@nsfnet-relay.ac.uk + * UUCP ...!mcvax!ukc!liv.ac.uk!sq79 + *------------------------------------------------------------------------- + * Changed for POSIX1003.2/Draft10 conformance + * Thomas Brupbacher (tobr@mw.lpc.ethz.ch), September 1990. + * Changes: + * - separation of error messages ( stderr) and output (stdout). + * - support for -b and -n (no effect, -b acts as -c) + * - support for -s + *------------------------------------------------------------------------- + */ + +#include <sys/types.h> +#include <ctype.h> +#include <string.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> + +#define MAX_FIELD 80 /* Pointers to the beginning of each field + * are stored in columns[], if a line holds + * more than MAX_FIELD columns the array + * boundary is exceed. But unlikely at 80 */ + +#define MAX_ARGS 32 /* Maximum number of fields following -f or + * -c switches */ +int args[MAX_ARGS * 2]; +int num_args; + +/* Lots of new defines, should easen maintainance... */ +#define DUMP_STDIN 0 /* define for mode: no options */ +#define OPTIONF 1 /* define for mode: option -f */ +#define OPTIONC 2 /* define for mode: option -c */ +#define OPTIONB 3 /* define for mode: option -b */ +#define NOTSET 0 /* option not selected */ +#define SET 1 /* option selected */ + +/* Defines for the warnings */ +#define DELIMITER_NOT_APPLICABLE 0 +#define OVERRIDING_PREVIOUS_MODE 1 +#define OPTION_NOT_APPLICABLE 2 +#define UNKNOWN_OPTION 3 +#define FILE_NOT_READABLE 4 + +/* Defines for the fatal errors */ +#define SYNTAX_ERROR 101 +#define POSITION_ERROR 102 +#define USAGE 103 +#define LINE_TO_LONG_ERROR 104 +#define RANGE_ERROR 105 +#define MAX_FIELDS_EXEEDED_ERROR 106 +#define MAX_ARGS_EXEEDED_ERROR 107 + + +int mode; /* 0 = dump stdin to stdout, 1=-f, 2=-c */ +int flag_i; /* SET = -i set on command line */ +int flag_s; /* SET = -s set on command line */ +char delim = '\t'; /* default delimiting character */ +FILE *fd; +char *name; +char line[BUFSIZ]; +int exit_status; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void warn, (int warn_number, char *option)); +_PROTOTYPE(void cuterror, (int err)); +_PROTOTYPE(void get_args, (void)); +_PROTOTYPE(void cut, (void)); + +void warn(warn_number, option) +int warn_number; +char *option; +{ + static char *warn_msg[] = { + "%s: Option -d allowed only with -f\n", + "%s: -%s overrides earlier option\n", + "%s: -%s not allowed in current mode\n", + "%s: Cannot open %s\n" + }; + + fprintf(stderr, warn_msg[warn_number], name, option); + exit_status = warn_number + 1; + +} + +void cuterror(err) +int err; +{ + static char *err_mes[] = { + "%s: syntax error\n", + "%s: position must be >0\n", + "%s: usage: cut [-f args [-i] [-d x]]|[-c args] [filename [...]]\n", + "%s: line longer than BUFSIZ\n", + "%s: range must not decrease from left to right\n", + "%s: MAX_FIELD exceeded\n", + "%s: MAX_ARGS exceeded\n" + }; + + fprintf(stderr, err_mes[err - 101], name); + exit(err); +} + + +void get_args() +{ + int i = 0; + int arg_ptr = 0; + int flag; + + num_args = 0; + do { + if (num_args == MAX_ARGS) cuterror(MAX_ARGS_EXEEDED_ERROR); + if (!isdigit(line[i]) && line[i] != '-') cuterror(SYNTAX_ERROR); + + args[arg_ptr] = 1; + args[arg_ptr + 1] = BUFSIZ; + flag = 1; + + while (line[i] != ',' && line[i] != 0) { + if (isdigit(line[i])) { + args[arg_ptr] = 0; + while (isdigit(line[i])) + args[arg_ptr] = 10 * args[arg_ptr] + line[i++] - '0'; + if (!args[arg_ptr]) cuterror(POSITION_ERROR); + arg_ptr++; + } + if (line[i] == '-') { + arg_ptr |= 1; + i++; + flag = 0; + } + } + if (flag && arg_ptr & 1) args[arg_ptr] = args[arg_ptr - 1]; + if (args[num_args * 2] > args[num_args * 2 + 1]) + cuterror(RANGE_ERROR); + num_args++; + arg_ptr = num_args * 2; + } + while (line[i++]); +} + + +void cut() +{ + int i, j, length, maxcol; + char *columns[MAX_FIELD]; + + while (fgets(line, BUFSIZ, fd)) { + length = strlen(line) - 1; + *(line + length) = 0; + switch (mode) { + case DUMP_STDIN: printf("%s", line); break; + case OPTIONF: + maxcol = 0; + columns[maxcol++] = line; + for (i = 0; i < length; i++) { + if (*(line + i) == delim) { + *(line + i) = 0; + if (maxcol == MAX_FIELD) + cuterror(MAX_FIELDS_EXEEDED_ERROR); + columns[maxcol] = line + i + 1; + while (*(line + i + 1) == delim && flag_i) { + columns[maxcol]++; + i++; + } + maxcol++; + } + } + if (maxcol == 1) { + if (flag_s != SET) printf("%s", line); + } else { + for (i = 0; i < num_args; i++) { + for (j = args[i * 2]; j <= args[i * 2 + 1]; j++) + if (j <= maxcol) { + printf("%s", columns[j - 1]); + if (i != num_args - 1 || j != args[i * 2 + 1]) + putchar(delim); + } + } + } + break; + case OPTIONC: + for (i = 0; i < num_args; i++) { + for (j = args[i * 2]; j <= (args[i * 2 + 1] > length ? length : + args[i * 2 + 1]); j++) + putchar(*(line + j - 1)); + } + } + if (maxcol == 1 && flag_s == SET); + else + putchar('\n'); + } +} + + +int main(argc, argv) +int argc; +char *argv[]; +{ + int i = 1; + int numberFilenames = 0; + name = argv[0]; + + if (argc == 1) cuterror(USAGE); + + while (i < argc) { + if (argv[i][0] == '-') { + switch (argv[i++][1]) { + case 'd': + if (mode == OPTIONC || mode == OPTIONB) + warn(DELIMITER_NOT_APPLICABLE, "d"); + delim = argv[i++][0]; + break; + case 'f': + sprintf(line, "%s", argv[i++]); + if (mode == OPTIONC || mode == OPTIONB) + warn(OVERRIDING_PREVIOUS_MODE, "f"); + mode = OPTIONF; + break; + case 'b': + sprintf(line, "%s", argv[i++]); + if (mode == OPTIONF || mode == OPTIONC) + warn(OVERRIDING_PREVIOUS_MODE, "b"); + mode = OPTIONB; + break; + case 'c': + sprintf(line, "%s", argv[i++]); + if (mode == OPTIONF || mode == OPTIONB) + warn(OVERRIDING_PREVIOUS_MODE, "c"); + mode = OPTIONC; + break; + case 'i': flag_i = SET; break; + case 's': flag_s = SET; break; + case '\0': /* - means: read from stdin */ + numberFilenames++; + break; + case 'n': /* needed for Posix, but no effect here */ + if (mode != OPTIONB) + warn(OPTION_NOT_APPLICABLE, "n"); + break; + default: + warn(UNKNOWN_OPTION, &(argv[i - 1][1])); + } + } else { + i++; + numberFilenames++; + } + } + +/* Here follow the checks, if the selected options are reasonable. */ + if (mode == OPTIONB) /* since in Minix char := byte */ + mode = OPTIONC; +/* Flag -s is only allowed with -f, otherwise warn and reset flag_s */ + if (flag_s == SET && (mode == OPTIONB || mode == OPTIONC)) { + warn(OPTION_NOT_APPLICABLE, "s"); + flag_s = NOTSET; + } + +/* Flag -i is only allowed with -f, otherwise warn and reset flag_i */ + if (flag_i == SET && mode == OPTIONF) { + warn(OPTION_NOT_APPLICABLE, "s"); + flag_i = NOTSET; + } + get_args(); + if (numberFilenames != 0) { + i = 1; + while (i < argc) { + if (argv[i][0] == '-') { + switch (argv[i][1]) { + case 'f': + case 'c': + case 'b': + case 'd': i += 2; break; + case 'n': + case 'i': + case 's': i++; break; + case '\0': + fd = stdin; + i++; + cut(); + break; + default: i++; + } + } else { + if ((fd = fopen(argv[i++], "r")) == NULL) { + warn(FILE_NOT_READABLE, argv[i - 1]); + } else { + cut(); + fclose(fd); + } + } + } + } else { + fd = stdin; + cut(); + } + + return(exit_status); +} diff --git a/commands/simple/date.c b/commands/simple/date.c new file mode 100755 index 000000000..f1c235946 --- /dev/null +++ b/commands/simple/date.c @@ -0,0 +1,443 @@ +/* date - Display (or set) the date and time Author: V. Archer */ + +#include <sys/types.h> +#include <ctype.h> +#include <stddef.h> +#include <stdlib.h> +#include <time.h> +#include <string.h> +#include <unistd.h> + +#define MIN 60L /* # seconds in a minute */ +#define HOUR (60 * MIN) /* # seconds in an hour */ +#define DAY (24 * HOUR) /* # seconds in a day */ +#define YEAR (365 * DAY) /* # seconds in a (non-leap) year */ + +int qflag, uflag, sflag, Sflag; + +/* Default output file descriptor. + */ +int outfd = 1; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void putchar, (int c)); +_PROTOTYPE(void pstring, (char *s, int len)); +_PROTOTYPE(void pldecimal, (unsigned long d, int digits)); +_PROTOTYPE(void pdecimal, (int d, int digits)); +_PROTOTYPE(void fmtdate, (char *format, time_t t, struct tm *p)); +_PROTOTYPE(time_t make_time, (char *t)); +_PROTOTYPE(struct tm *september, (time_t *tp)); +_PROTOTYPE(void usage, (void)); + +/* Main module. Handles P1003.2 date and system administrator's date. The + * date entered should be given GMT, regardless of the system's TZ! + */ +int main(argc, argv) +int argc; +char **argv; +{ + time_t t; + struct tm *tm; + char *format; + char time_buf[40]; + int n; + int i; + + time(&t); + + i = 1; + while (i < argc && argv[i][0] == '-') { + char *opt = argv[i++] + 1, *end; + + if (opt[0] == '-' && opt[1] == 0) break; + + while (*opt != 0) switch (*opt++) { + case 'q': + qflag = 1; + break; + case 's': + sflag = 1; + break; + case 'u': + uflag = 1; + break; + case 'S': + Sflag = 1; + break; + case 't': + /* (obsolete, now -r) */ + case 'r': + if (*opt == 0) { + if (i == argc) usage(); + opt = argv[i++]; + } + t = strtoul(opt, &end, 10); + if (*end != 0) usage(); + opt = ""; + break; + default: + usage(); + } + } + + if (!qflag && i < argc && ('0' <= argv[i][0] && argv[i][0] <= '9')) { + t = make_time(argv[i++]); + sflag = 1; + } + + format = "%c"; + if (i < argc && argv[i][0] == '+') format = argv[i++] + 1; + + if (i != argc) usage(); + + if (qflag) { + pstring("\nPlease enter date: MMDDYYhhmmss. Then hit the RETURN key.\n", -1); + n = read(0, time_buf, sizeof(time_buf)); + if (n > 0 && time_buf[n-1] == '\n') n--; + if (n >= 0) time_buf[n] = 0; + t = make_time(time_buf); + sflag = 1; + } + + if (sflag && stime(&t) != 0) { + outfd = 2; + pstring("No permission to set time\n", -1); + return(1); + } + + tm = Sflag ? september(&t) : uflag ? gmtime(&t) : localtime(&t); + + fmtdate(format, t, tm); + putchar('\n'); + return(0); +} + +/* Replacement for stdio putchar(). + */ +void putchar(c) +int c; +{ + static char buf[1024]; + static char *bp = buf; + + if (c != 0) *bp++ = c; + if (c == 0 || c == '\n' || bp == buf + sizeof(buf)) { + write(outfd, buf, bp - buf); + bp = buf; + } +} + +/* Internal function that prints a n-digits number. Replaces stdio in our + * specific case. + */ +void pldecimal(d, digits) +unsigned long d; +int digits; +{ + digits--; + if (d > 9 || digits > 0) pldecimal(d / 10, digits); + putchar('0' + (d % 10)); +} + +void pdecimal(d, digits) +int d, digits; +{ + pldecimal((unsigned long) d, digits); +} + +/* Internal function that prints a fixed-size string. Replaces stdio in our + * specific case. + */ +void pstring(s, len) +char *s; +int len; +{ + while (*s) + if (len--) + putchar(*s++); + else + break; +} + +/* Format the date, using the given locale string. A special case is the + * TZ which might be a sign followed by four digits (New format time zone). + */ +void fmtdate(format, t, p) +char *format; +time_t t; +struct tm *p; +{ + int i; + char *s; + static char *wday[] = {"Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday"}; + static char *month[] = {"January", "February", "March", "April", + "May", "June", "July", "August", + "September", "October", "November", "December"}; + + while (*format) + if (*format == '%') { + switch (*++format) { + case 'A': + pstring(wday[p->tm_wday], -1); + break; + case 'B': + pstring(month[p->tm_mon], -1); + break; + case 'D': + pdecimal(p->tm_mon + 1, 2); + putchar('/'); + pdecimal(p->tm_mday, 2); + putchar('/'); + case 'y': + pdecimal(p->tm_year % 100, 2); + break; + case 'H': + pdecimal(p->tm_hour, 2); + break; + case 'I': + i = p->tm_hour % 12; + pdecimal(i ? i : 12, 2); + break; + case 'M': + pdecimal(p->tm_min, 2); + break; + case 'X': + case 'T': + pdecimal(p->tm_hour, 2); + putchar(':'); + pdecimal(p->tm_min, 2); + putchar(':'); + case 'S': + pdecimal(p->tm_sec, 2); + break; + case 'U': + pdecimal((p->tm_yday - p->tm_wday + 13) / 7, 2); + break; + case 'W': + if (--(p->tm_wday) < 0) p->tm_wday = 6; + pdecimal((p->tm_yday - p->tm_wday + 13) / 7, 2); + if (++(p->tm_wday) > 6) p->tm_wday = 0; + break; + case 'Y': + pdecimal(p->tm_year + 1900, 4); + break; + case 'Z': + if (uflag) { + s = "GMT"; + } else { + s = (p->tm_isdst == 1) ? tzname[1] : tzname[0]; + } + pstring(s, strlen(s)); + break; + case 'a': + pstring(wday[p->tm_wday], 3); + break; + case 'b': + case 'h': + pstring(month[p->tm_mon], 3); + break; + case 'c': + if (!(s = getenv("LC_TIME"))) + s = "%a %b %e %T %Z %Y"; + fmtdate(s, t, p); + break; + case 'd': + pdecimal(p->tm_mday, 2); + break; + case 'e': + if (p->tm_mday < 10) putchar(' '); + pdecimal(p->tm_mday, 1); + break; + case 'j': + pdecimal(p->tm_yday + 1, 3); + break; + case 'm': + pdecimal(p->tm_mon + 1, 2); + break; + case 'n': putchar('\n'); break; + case 'p': + if (p->tm_hour < 12) + putchar('A'); + else + putchar('P'); + putchar('M'); + break; + case 'r': + fmtdate("%I:%M:%S %p", t, p); + break; + case 's': + pldecimal((unsigned long) t, 0); + break; + case 't': putchar('\t'); break; + case 'w': + putchar('0' + p->tm_wday); + break; + case 'x': + fmtdate("%B %e %Y", t, p); + break; + case '%': putchar('%'); break; + case '\0': format--; + } + format++; + } else + putchar(*format++); +} + +/* Convert a local date string into GMT time in seconds. */ +time_t make_time(t) +char *t; +{ + struct tm tm; /* user specified time */ + time_t now; /* current time */ + int leap; /* current year is leap year */ + int i; /* general index */ + int fld; /* number of fields */ + int f[6]; /* time fields */ + static int days_per_month[2][12] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }}; + +/* Get current time just in case */ + now = time((time_t *) 0); + tm = *localtime(&now); + tm.tm_sec = 0; + tm.tm_mon++; + tm.tm_year %= 100; + +/* Parse the time */ +#if '0'+1 != '1' || '1'+1 != '2' || '2'+1 != '3' || '3'+1 != '4' || \ + '4'+1 != '5' || '5'+1 != '6' || '6'+1 != '7' || '7'+1 != '8' || '8'+1 != '9' + << Code unsuitable for character collating sequence >> +#endif + + for (fld = 0; fld < sizeof(f)/sizeof(f[0]); fld++) { + if (*t == 0) break; + f[fld] = 0; + for (i = 0; i < 2; i++, t++) { + if (*t < '0' || *t > '9') usage(); + f[fld] = f[fld] * 10 + *t - '0'; + } + } + + switch (fld) { + case 2: + tm.tm_hour = f[0]; tm.tm_min = f[1]; break; + + case 3: + tm.tm_hour = f[0]; tm.tm_min = f[1]; tm.tm_sec = f[2]; + break; + + case 5: + tm.tm_mon = f[0]; tm.tm_mday = f[1]; tm.tm_year = f[2]; + tm.tm_hour = f[3]; tm.tm_min = f[4]; + break; + + case 6: + tm.tm_mon = f[0]; tm.tm_mday = f[1]; tm.tm_year = f[2]; + tm.tm_hour = f[3]; tm.tm_min = f[4]; tm.tm_sec = f[5]; + break; + + default: + usage(); + } + +/* Convert the time into seconds since 1 January 1970 */ + if (tm.tm_year < 70) + tm.tm_year += 100; + leap = (tm.tm_year % 4 == 0 && tm.tm_year % 400 != 0); + if (tm.tm_mon < 1 || tm.tm_mon > 12 || + tm.tm_mday < 1 || tm.tm_mday > days_per_month[leap][tm.tm_mon-1] || + tm.tm_hour > 23 || tm.tm_min > 59) { + outfd = 2; + pstring("Illegal date format\n", -1); + exit(1); + } + +/* Convert the time into Minix time - zone independent code */ + { + time_t utctime; /* guess at unix time */ + time_t nextbit; /* next bit to try */ + int rv; /* result of try */ + struct tm *tmp; /* local time conversion */ + +#define COMPARE(a,b) ((a) != (b)) ? ((a) - (b)) : + + utctime = 1; + do { + nextbit = utctime; + utctime = nextbit << 1; + } while (utctime >= 1); + + for (utctime = 0; ; nextbit >>= 1) { + + utctime |= nextbit; + tmp = localtime(&utctime); + if (tmp == 0) continue; + + rv = COMPARE(tmp->tm_year, tm.tm_year) + COMPARE(tmp->tm_mon + 1, tm.tm_mon) + COMPARE(tmp->tm_mday, tm.tm_mday) + COMPARE(tmp->tm_hour, tm.tm_hour) + COMPARE(tmp->tm_min, tm.tm_min) + COMPARE(tmp->tm_sec, tm.tm_sec) + 0; + + if (rv > 0) + utctime &= ~nextbit; + else if (rv == 0) + break; + + if (nextbit == 0) { + uflag = 1; + outfd = 2; + pstring("Inexact conversion to UTC from ", -1); + fmtdate("%c\n", utctime, localtime(&utctime) ); + exit(1); + } + } + return utctime; + } +} + +/* Correct the time to the reckoning of Eternal September. */ +struct tm *september(tp) +time_t *tp; +{ + time_t t; + int days; + struct tm *tm; + + tm = localtime(tp); + + t = *tp - (tm->tm_hour - 12) * 3600L; /* No zone troubles around noon. */ + days = 0; + + while (tm->tm_year > 93 || (tm->tm_year == 93 && tm->tm_mon >= 8)) { + /* Step back a year or a month. */ + days += tm->tm_year > 93 ? tm->tm_yday+1 : tm->tm_mday; + t = *tp - days * (24 * 3600L); + + tm = localtime(&t); + } + + if (days > 0) { + tm = localtime(tp); + tm->tm_mday = days; + tm->tm_year = 93; + tm->tm_mon = 8; +#if SANITY + t = mktime(tm); + tm = localtime(&t); +#endif + } + return tm; +} + +/* (Extended) Posix prototype of date. */ +void usage() +{ + outfd = 2; + pstring("Usage: date [-qsuS] [-r seconds] [[MMDDYY]hhmm[ss]] [+format]\n", -1); + exit(1); +} diff --git a/commands/simple/dd.c b/commands/simple/dd.c new file mode 100755 index 000000000..0f7874b86 --- /dev/null +++ b/commands/simple/dd.c @@ -0,0 +1,394 @@ +/* dd - disk dumper */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> + +#define EOS '\0' +#define BOOLEAN int +#define TRUE 1 +#define FALSE 0 + +char *pch, *errorp; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(BOOLEAN is, (char *pc)); +_PROTOTYPE(int num, (void)); +_PROTOTYPE(void puto, (void)); +_PROTOTYPE(void statistics, (void)); +_PROTOTYPE(int ulcase, (int c)); +_PROTOTYPE(void cnull, (int c)); +_PROTOTYPE(void null, (int c)); +_PROTOTYPE(void extra, (void)); +_PROTOTYPE(void over, (int dummy)); + +BOOLEAN is(pc) +char *pc; +{ + register char *ps = pch; + + while (*ps++ == *pc++) + if (*pc == EOS) { + pch = ps; + return(TRUE); + } + return(FALSE); +} + +#define BIGNUM 2147483647 + +int num() +{ + long ans; + register char *pc; + + pc = pch; + ans = 0L; + while ((*pc >= '0') && (*pc <= '9')) + ans = (long) ((*pc++ - '0') + (ans * 10)); + while (TRUE) switch (*pc++) { + case 'w': + ans *= 2L; + continue; + case 'b': + ans *= 512L; + continue; + case 'k': + ans *= 1024L; + continue; + case 'x': + pch = pc; + ans *= (long) num(); + case EOS: + if ((ans >= BIGNUM) || (ans < 0)) { + fprintf(stderr, "dd: argument %s out of range\n", + errorp); + exit(1); + } + return((int) ans); + } +} + +#define SWAB 0x0001 +#define LCASE 0x0002 +#define UCASE 0x0004 +#define NOERROR 0x0008 +#define SYNC 0x0010 +#define SILENT 0x0020 +#define BLANK ' ' +#define DEFAULT 512 + +unsigned cbs, bs, skip, nseek, count; +int seekseen = FALSE; +unsigned ibs = DEFAULT; +unsigned obs = DEFAULT; +unsigned files = 1; +char *ifilename = NULL; +char *ofilename = NULL; + +int convflag = 0; +int flag = 0; +int ifd, ofd, ibc; +char *ibuf, *obuf, *op; +unsigned nifull, nipartial, nofull, nopartial; +int cbc; +unsigned ntr, obc; +int ns; +char mlen[] = {64, 45, 82, 45, 83, 96, 109, 100, 109, 97, 96, 116, 108, 9}; + +void puto() +{ + int n; + + if (obc == 0) return; + if (obc == obs) + nofull++; + else + nopartial++; + if ((n = write(ofd, obuf, obc)) != obc) { + if (n == -1) { + fprintf(stderr, "dd: Write error: %s\n", strerror(errno)); + } else { + fprintf(stderr, "dd: Short write, %d instead of %d\n", n, obc); + } + exit(1); + } + obc = 0; +} + +void statistics() +{ + if (convflag & SILENT) return; + fprintf(stderr, "%u+%u records in\n", nifull, nipartial); + fprintf(stderr, "%u+%u records out\n", nofull, nopartial); + if (ntr) fprintf(stderr, "%d truncated records\n", ntr); +} + + +int main(argc, argv) +int argc; +char *argv[]; +{ +#ifdef __STDC__ + void (*convert) (int); +#else + void (*convert) (); +#endif + char *iptr; + int i, j; + + convert = null; + argc--; + argv++; + while (argc-- > 0) { + pch = *(argv++); + if (is("ibs=")) { + errorp = pch; + ibs = num(); + continue; + } + if (is("obs=")) { + errorp = pch; + obs = num(); + continue; + } + if (is("bs=")) { + errorp = pch; + bs = num(); + continue; + } + if (is("if=")) { + ifilename = pch; + continue; + } + if (is("of=")) { + ofilename = pch; + continue; + } + if (is("skip=")) { + errorp = pch; + skip = num(); + continue; + } + if (is("seek=")) { + errorp = pch; + nseek = num(); + seekseen = TRUE; + continue; + } + if (is("count=")) { + errorp = pch; + count = num(); + continue; + } + if (is("files=")) { + errorp = pch; + files = num(); + continue; + } + if (is("length=")) { + errorp = pch; + for (j = 0; j < 13; j++) mlen[j]++; + write(2, mlen, 14); + continue; + } + if (is("conv=")) { + while (*pch != EOS) { + if (is("lcase")) { + convflag |= LCASE; + continue; + } + if (is("ucase")) { + convflag |= UCASE; + continue; + } + if (is("noerror")) { + convflag |= NOERROR; + continue; + } + if (is("sync")) { + convflag |= SYNC; + continue; + } + if (is("swab")) { + convflag |= SWAB; + continue; + } + if (is("silent")) { + convflag |= SILENT; + continue; + } + if (is(",")) continue; + fprintf(stderr, "dd: bad argument: %s\n", + pch); + exit(1); + } + if (*pch == EOS) continue; + } + fprintf(stderr, "dd: bad argument: %s\n", pch); + exit(1); + } + if ((convert == null) && (convflag & (UCASE | LCASE))) convert = cnull; + if ((ifd = ((ifilename) ? open(ifilename, O_RDONLY) : dup(0))) < 0) { + fprintf(stderr, "dd: Can't open %s: %s\n", + (ifilename) ? ifilename : "stdin", strerror(errno)); + exit(1); + } + if ((ofd = ((ofilename) ? open(ofilename, seekseen ? O_WRONLY | O_CREAT + : O_WRONLY | O_CREAT | O_TRUNC, 0666) + : dup(1))) < 0) { + fprintf(stderr, "dd: Can't open %s: %s\n", + (ofilename) ? ofilename : "stdout", strerror(errno)); + exit(1); + } + if (bs) { + ibs = obs = bs; + if (convert == null) flag++; + } + if (ibs == 0) { + fprintf(stderr, "dd: ibs cannot be zero\n"); + exit(1); + } + if (obs == 0) { + fprintf(stderr, "dd: obs cannot be zero\n"); + exit(1); + } + if ((ibuf = sbrk(ibs)) == (char *) -1) { + fprintf(stderr, "dd: not enough memory\n"); + exit(1); + } + if ((obuf = (flag) ? ibuf : sbrk(obs)) == (char *) -1) { + fprintf(stderr, "dd: not enough memory\n"); + exit(1); + } + ibc = obc = cbc = 0; + op = obuf; + if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, over); + if (skip != 0) { + struct stat st; + if (fstat(ifd,&st) < 0 || !(S_ISREG(st.st_mode) || S_ISBLK(st.st_mode)) + || lseek(ifd, (off_t) ibs * (off_t) skip, SEEK_SET) == (off_t) -1) { + do { + if (read(ifd, ibuf, ibs) == -1) { + fprintf(stderr, + "dd: Error skipping input: %s\n", + strerror(errno)); + exit(1); + } + } while (--skip != 0); + } + } + if (nseek != 0) { + if (lseek(ofd, (off_t) obs * (off_t) nseek, SEEK_SET) == (off_t) -1) { + fprintf(stderr, "dd: Seeking on output failed: %s\n", + strerror(errno)); + exit(1); + } + } + +outputall: + if (ibc-- == 0) { + ibc = 0; + if ((count == 0) || ((nifull + nipartial) != count)) { + if (convflag & (NOERROR | SYNC)) + for (iptr = ibuf + ibs; iptr > ibuf;) *--iptr = 0; + ibc = read(ifd, ibuf, ibs); + } + if (ibc == -1) { + fprintf(stderr, "dd: Read error: %s\n", strerror(errno)); + if ((convflag & NOERROR) == 0) { + puto(); + over(0); + } + ibc = 0; + for (i = 0; i < ibs; i++) + if (ibuf[i] != 0) ibc = i; + statistics(); + } + if ((ibc == 0) && (--files <= 0)) { + puto(); + over(0); + } + if (ibc != ibs) { + nipartial++; + if (convflag & SYNC) ibc = ibs; + } else + nifull++; + iptr = ibuf; + i = ibc >> 1; + if ((convflag & SWAB) && i) do { + int temp; + temp = *iptr++; + iptr[-1] = *iptr; + *iptr++ = temp; + } while (--i); + iptr = ibuf; + if (flag) { + obc = ibc; + puto(); + ibc = 0; + } + goto outputall; + } + i = *iptr++ & 0377; + (*convert) (i); + goto outputall; +} + +int ulcase(c) +int c; +{ + int ans = c; + + if ((convflag & UCASE) && (c >= 'a') && + (c <= 'z')) + ans += 'A' - 'a'; + if ((convflag & LCASE) && (c >= 'A') && + (c <= 'Z')) + ans += 'a' - 'A'; + return(ans); +} + +void cnull(c) +int c; +{ + c = ulcase(c); + null(c); +} + +void null(c) +int c; +{ + *op++ = c; + if (++obc >= obs) { + puto(); + op = obuf; + } +} + +void extra() +{ + if (++cbc >= cbs) { + null('\n'); + cbc = 0; + ns = 0; + } +} + +void over(sig) +int sig; +{ + statistics(); + if (sig != 0) { + signal(sig, SIG_DFL); + raise(sig); + } + exit(0); +} diff --git a/commands/simple/decomp16.c b/commands/simple/decomp16.c new file mode 100755 index 000000000..5cbc4d80d --- /dev/null +++ b/commands/simple/decomp16.c @@ -0,0 +1,444 @@ +/* decomp16: decompress 16bit compressed files on a 16bit Intel processor + * + * Version 1.3 of 25 Mar 92. + * + * This was written by John N. White on 6/30/91 and is Public Domain. + * Patched to run under news by Will Rose, Feb 92. + * J N White's (earlier) patches added by Will Rose, 20 Feb 92. + * Unsigned int increment/wrap bug fixed by Will Rose, 24 Mar 92. + * Argument bug fixed, stdio generalised by Will Rose, 25 Mar 92. + * + * decomp16 can use as as little as 512 bytes of stack; since it forks + * four additional copies, it's probably worth using minimum stack rather + * than the 8192 byte Minix default. To reduce memory still further, + * change BUFSZ below to 256; it is currently set to 1024 for speed. The + * minimal decomp16 needs about 280k to run in pipe mode (56k per copy). + * + * This program acts as a filter: + * decomp16 < compressed_file > decompressed_file + * The arguments -0 to -4 run only the corresponding pass. + * Thus: + * decomp16 -4 < compressed_file > 3; + * decomp16 -3 < 3 > 2; + * decomp16 -2 < 2 > 1; + * decomp16 -1 < 1 > 0; + * decomp16 -0 < 0 > decompressed_file + * will also work, as will connecting the passes by explicit pipes if + * there is enough memory to do so. File name arguments can also be + * given directly on the command line. + * + * Compress uses a modified LZW compression algorithm. A compressed file + * is a set of indices into a dictionary of strings. The number of bits + * used to store each index depends on the number of entries currently + * in the dictionary. If there are between 257 and 512 entries, 9 bits + * are used. With 513 entries, 10 bits are used, etc. The initial dictionary + * consists of 0-255 (which are the corresponding chars) and 256 (which + * is a special CLEAR code). As each index in the compressed file is read, + * a new entry is added to the dictionary consisting of the current string + * with the first char of the next string appended. When the dictionary + * is full, no further entries are added. If a CLEAR code is received, + * the dictionary will be completely reset. The first two bytes of the + * compressed file are a magic number, and the third byte indicates the + * maximum number of bits, and whether the CLEAR code is used (older versions + * of compress didn't have CLEAR). + * + * This program works by forking four more copies of itself. The five + * programs form a pipeline. Copy 0 writes to stdout, and forks copy 1 + * to supply its input, which in turn forks and reads from copy 2, etc. + * This sequence is used so that when the program exits, all writes + * are completed and a program that has exec'd uncompress (such as news) + * can immediately use the uncompressed data when the wait() call returns. + * + * If given a switch -#, where # is a digit from 0 to 4 (example: -2), the + * program will run as that copy, reading from stdin and writing to stdout. + * This allows decompressing with very limited RAM because only one of the + * five passes is in memory at a time. + * + * The compressed data is a series of string indices (and a header at + * the beginning and an occasional CLEAR code). As these indices flow + * through the pipes, each program decodes the ones it can. The result + * of each decoding will be indices that the following programs can handle. + * + * Each of the 65536 strings in the dictionary is an earlier string with + * some character added to the end (except for the the 256 predefined + * single char strings). When new entries are made to the dictionary, + * the string index part will just be the last index to pass through. + * But the char part is the first char of the next string, which isn't + * known yet. So the string can be stored as a pair of indices. When + * this string is specified, it is converted to this pair of indices, + * which are flagged so that the first will be decoded in full while + * the second will be decoded to its first char. The dictionary takes + * 256k to store (64k strings of 2 indices of 2 bytes each). This is + * too big for a 64k data segment, so it is divided into 5 equal parts. + * Copy 4 of the program maintains the high part and copy 0 holds the + * low part. + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> + +#define BUFSZ 1024 /* size of i/o buffers */ +#define BUFSZ_2 (BUFSZ/2) /* # of unsigned shorts in i/o bufs */ +#define DICTSZ (unsigned)13056 /* # of local dictionary entries */ +#define EOF_INDEX (unsigned short)0xFFFF /* EOF flag for pipeline */ +#define FALSE 0 +#define TRUE ~FALSE + +int fdin, fdout, fderr; /* input, output, and error file descriptors */ +int ibufstart, obufind, ibufend;/* i/o buffer indices */ +int ipbufind = BUFSZ_2; /* pipe buffer indices */ +int opbufind = 0; +int pnum = -1; /* ID of this copy */ +unsigned short ipbuf[BUFSZ_2]; /* for buffering input */ +unsigned short opbuf[BUFSZ_2]; /* for buffering output */ +unsigned char *ibuf = (unsigned char *) ipbuf; +unsigned char *obuf = (unsigned char *) opbuf; + +unsigned short dindex[DICTSZ]; /* dictionary: index to substring */ +unsigned short dchar[DICTSZ]; /* dictionary: last char of string */ +unsigned iindex, tindex, tindex2; /* holds index being processed */ +unsigned base; /* where in global dict local dict starts */ +unsigned tbase; +unsigned locend; /* where in global dict local dict ends */ +unsigned curend = 256; /* current end of global dict */ +unsigned maxend; /* max end of global dict */ +int dcharp; /* ptr to dchar that needs next index entry */ +int curbits; /* number of bits for getbits() to read */ +int maxbits; /* limit on number of bits */ +int clearflg; /* if set, allow CLEAR */ +int inmod; /* mod 8 for getbits() */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void ffork, (void)); +_PROTOTYPE(void die, (char *s)); +_PROTOTYPE(void myputc, (unsigned c)); +_PROTOTYPE(unsigned mygetc, (void)); +_PROTOTYPE(void getbits, (void)); +_PROTOTYPE(void getpipe, (void)); +_PROTOTYPE(void putpipe, (unsigned u, int flag)); + +int main(argc, argv) +int argc; +char **argv; +{ + char c, *cp; + int j, k, fdtmp; + unsigned int len; + + /* Find the program name */ + j = 0; + while (argv[0][j] != '\0') j++; + len = (unsigned int) j; + while (j--) + if (argv[0][j] == '/') break; + if (argv[0][j] == '/') j++; + cp = argv[0] + j; + len -= j; + + /* Sort out the flags */ + for (k = 1; k < argc; k++) { + if (argv[k][0] == '-') { + c = argv[k][1]; + switch (c) { + case '0': /* pass numbers */ + case '1': + case '2': + case '3': + case '4': pnum = c - '0'; break; + case 'd': /* used by news */ + break; + default: + (void) write(1, "Usage: ", 7); + (void) write(1, cp, len); + (void) write(1, " [-#] [in] [out]\n", 17); + exit(0); + break; + } + + /* Once it's checked, lose it anyway */ + for (j = k; j < argc; j++) argv[j] = argv[j + 1]; + argc--; + k--; + } + } + + /* Default i/o settings */ + fdin = 0; + fdout = 1; + fderr = 2; + + /* Try to open specific files and connect them to stdin/stdout */ + if (argc > 1) { + if ((fdtmp = open(argv[1], 0)) == -1) die("input open failed"); + (void) close(0); + if ((fdin = dup(fdtmp)) == -1) die("input dup failed\n"); + (void) close(fdtmp); + } + if (argc > 2) { + (void) unlink(argv[2]); + if ((fdtmp = creat(argv[2], 0666)) == -1) die("output creat failed"); + (void) close(1); + if ((fdout = dup(fdtmp)) == -1) die("output dup failed\n"); + (void) close(fdtmp); + } + + /* Sort out type of compression */ + if (pnum == -1 || pnum == 4) {/* if this is pass 4 */ + /* Check header of compressed file */ + if (mygetc() != 0x1F || mygetc() != 0x9D) /* check magic number */ + die("not a compressed file\n"); + iindex = mygetc(); /* get compression style */ + } else + getpipe(); /* get compression style */ + + maxbits = iindex & 0x1F; + clearflg = ((iindex & 0x80) != 0) ? TRUE : FALSE; + if (maxbits < 9 || maxbits > 16) /* check for valid maxbits */ + die("can't decompress\n"); + if (pnum != -1 && pnum != 0) + putpipe(iindex, 0); /* pass style to next copy */ + + /* Fork off an ancestor if necessary - ffork() increments pnum */ + if (pnum == -1) { + pnum = 0; + if (pnum == 0) ffork(); + if (pnum == 1) ffork(); + if (pnum == 2) ffork(); + if (pnum == 3) ffork(); + } + + /* Preliminary inits. Note: end/maxend/curend are highest, not + * highest + 1 */ + base = DICTSZ * pnum + 256; + locend = base + DICTSZ - 1; + maxend = (1 << maxbits) - 1; + if (maxend > locend) maxend = locend; + + while (TRUE) { + curend = 255 + (clearflg ? 1 : 0); /* init dictionary */ + dcharp = DICTSZ; /* flag for none needed */ + curbits = 9; /* init curbits (for copy 0) */ + while (TRUE) { /* for each index in input */ + if (pnum == 4) {/* get index using getbits() */ + if (curbits < maxbits && (1 << curbits) <= curend) { + /* Curbits needs to be increased */ + /* Due to uglyness in compress, these + * indices in the compressed file are + * wasted */ + while (inmod) getbits(); + curbits++; + } + getbits(); + } else + getpipe(); /* get next index */ + + if (iindex == 256 && clearflg) { + if (pnum > 0) putpipe(iindex, 0); + /* Due to uglyness in compress, these indices + * in the compressed file are wasted */ + while (inmod) getbits(); + break; + } + tindex = iindex; + /* Convert the index part, ignoring spawned chars */ + while (tindex >= base) tindex = dindex[tindex - base]; + /* Pass on the index */ + putpipe(tindex, 0); + /* Save the char of the last added entry, if any */ + if (dcharp < DICTSZ) dchar[dcharp++] = tindex; + if (curend < maxend && ++curend > (base - 1)) + dindex[dcharp = (curend - base)] = iindex; + + /* Do spawned chars. They are naturally produced in + * the wrong order. To get them in the right order + * without using memory, a series of passes, + * progressively less deep, are used */ + tbase = base; + while ((tindex = iindex) >= tbase) {/* for each char to spawn*/ + while ((tindex2 = dindex[tindex - base]) >= tbase) + tindex = tindex2; /* scan to desired char */ + putpipe(dchar[tindex-base], 1); /* put it to the pipe*/ + tbase = tindex + 1; + if (tbase == 0) break; /* it's a wrap */ + } + } + } +} + + +/* F f o r k + * + * Fork off the previous pass - the parent reads from the child. + */ +void ffork() +{ + int j, pfd[2]; + + if (pipe(pfd) == -1) die("pipe() error\n"); + if ((j = fork()) == -1) die("fork() error\n"); + if (j == 0) { /* this is the child */ + if (close(1) == -1) die("close(1) error\n"); + if (dup(pfd[1]) != 1) die("dup(1) error\n"); + (void) close(pfd[0]); + pnum++; + } else { /* this is the parent */ + if (close(0) == -1) die("close(0) error\n"); + if (dup(pfd[0]) != 0) die("dup(0) error\n"); + (void) close(pfd[1]); + } +} + + +/* D i e + * + * If s is a message, write it to stderr. Flush buffers if needed. Then exit. + */ +void die(s) +char *s; +{ + /* Flush stdout buffer if needed */ + if (obufind != 0) { + if (write(fdout, (char *) obuf, (unsigned) obufind) != obufind) + s = "bad stdout write\n"; + obufind = 0; + } + + /* Flush pipe if needed */ + do + putpipe(EOF_INDEX, 0); + while (opbufind); + /* Write any error message */ + if (s != (char *) NULL) { + while (*s) (void) write(fderr, s++, 1); + } + exit((s == (char *) NULL) ? 0 : 1); +} + + +/* M p u t c + * + * Put a char to stdout. + */ +void myputc(c) +unsigned c; +{ + obuf[obufind++] = c; + if (obufind >= BUFSZ) { /* if stdout buffer full */ + if (write(fdout, (char *) obuf, BUFSZ) != BUFSZ) /* flush to stdout */ + die("bad stdout write\n"); + obufind = 0; + } +} + + +/* M y g e t c + * + * Get a char from stdin. If EOF, then die() and exit. + */ +unsigned mygetc() +{ + if (ibufstart >= ibufend) { /* if stdin buffer empty */ + if ((ibufend = read(fdin, (char *) ibuf, BUFSZ)) <= 0) + die((char *) NULL); /* if EOF, do normal exit */ + ibufstart = 0; + } + return(ibuf[ibufstart++] & 0xff); +} + + +/* G e t b i t s + * + * Put curbits bits into index from stdin. Note: only copy 4 uses this. + * The bits within a byte are in the correct order. But when the bits + * cross a byte boundry, the lowest bits will be in the higher part of + * the current byte, and the higher bits will be in the lower part of + * the next byte. + */ +void getbits() +{ + int have; + static unsigned curbyte; /* byte having bits extracted from it */ + static int left; /* how many bits are left in curbyte */ + + inmod = (inmod + 1) & 7; /* count input mod 8 */ + iindex = curbyte; + have = left; + if (curbits - have > 8) { + iindex |= mygetc() << have; + have += 8; + } + iindex |= ((curbyte = mygetc()) << have) & ~((unsigned) 0xFFFF << curbits); + curbyte >>= curbits - have; + left = 8 - (curbits - have); +} + + +/* G e t p i p e + * + * Get an index from the pipeline. If flagged firstonly, handle it here. + */ +void getpipe() +{ + static short flags; + static int n = 0; /* number of flags in flags */ + + while (TRUE) { /* while index with firstonly flag set */ + if (n <= 0) { + if (ipbufind >= BUFSZ_2) { /* if pipe input buffer + * empty */ + if (read(fdin, (char *) ipbuf, BUFSZ) != BUFSZ) + die("bad pipe read\n"); + ipbufind = 0; + } + flags = ipbuf[ipbufind++]; + n = 15; + } + iindex = ipbuf[ipbufind++]; + if (iindex > curend) + die((iindex == EOF_INDEX) ? (char *) NULL : "invalid data\n"); + flags <<= 1; + n--; + /* Assume flags < 0 if highest remaining flag is set */ + if (flags < 0) { /* if firstonly flag for index is not set */ + while (iindex >= base) iindex = dindex[iindex - base]; + putpipe(iindex, 1); + } else + return; /* return with valid non-firstonly index */ + } +} + + +/* P u t p i p e + * + * put an index into the pipeline. + */ +void putpipe(u, flag) +unsigned u; +int flag; +{ + static unsigned short flags, *flagp; + static int n = 0; /* number of flags in flags */ + + if (pnum == 0) { /* if we should write to stdout */ + myputc(u); /* index will be the char value */ + return; + } + if (n == 0) { /* if we need to reserve a flag entry */ + flags = 0; + flagp = opbuf + opbufind; + opbufind++; + } + opbuf[opbufind++] = u; /* add index to buffer */ + flags = (flags << 1) | flag; /* add firstonly flag */ + if (++n >= 15) { /* if block of 15 indices */ + n = 0; + *flagp = flags; /* insert flags entry */ + if (opbufind >= BUFSZ_2) { /* if pipe out buffer full */ + opbufind = 0; + if (write(fdout, (char *) opbuf, BUFSZ) != BUFSZ) + die("bad pipe write\n"); + } + } +} diff --git a/commands/simple/df.c b/commands/simple/df.c new file mode 100755 index 000000000..2f1c05e88 --- /dev/null +++ b/commands/simple/df.c @@ -0,0 +1,445 @@ +/* df - disk free block printout Author: Andy Tanenbaum + * + * 91/04/30 Kees J. Bot (kjb@cs.vu.nl) + * Map filename arguments to the devices they live on. + * Changed output to show percentages. + * + * 92/12/12 Kees J. Bot + * Posixized. (Almost, the normal output is in kilobytes, it should + * be 512-byte units. 'df -P' and 'df -kP' are as it should be.) + * + */ + +#include <stdio.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <limits.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <dirent.h> +#if __minix_vmd +#include <sys/mnttab.h> +#else +#include <minix/minlib.h> +#endif + +#include <minix/config.h> +#include <minix/const.h> +#include <minix/type.h> +#include <servers/fs/const.h> +#include <servers/fs/type.h> +#include <servers/fs/super.h> +#undef printf + +#if !__minix_vmd +/* Map Minix-vmd names to Minix names. */ +#define v12_super_block super_block +#define SUPER_V1 SUPER_MAGIC + +/* Only setuid() and setgid(). */ +#define seteuid(uid) setuid(uid) +#define setegid(gid) setgid(gid) +#endif + +#define ISDISK(mode) S_ISBLK(mode) /* || S_ISCHR for raw device??? */ + +extern int errno; +char MTAB[] = "/etc/mtab"; + +struct mtab { /* List of mounted devices from /etc/mtab. */ + struct mtab *next; + dev_t device; + char *devname; + char *mountpoint; +} *mtab= NULL; + +struct mtab *searchtab(char *name); +void readmtab(char *type); +int df(const struct mtab *mt); +bit_t bit_count(unsigned blocks, bit_t bits, int fd, int bs); + +int iflag= 0; /* Focus on inodes instead of blocks. */ +int Pflag= 0; /* Posix standard output. */ +int kflag= 0; /* Output in kilobytes instead of 512 byte units for -P. */ +int istty; /* isatty(1) */ +uid_t ruid, euid; /* To sometimes change identities. */ +gid_t rgid, egid; + +void usage(void) +{ + fprintf(stderr, "Usage: df [-ikP] [-t type] [device]...\n"); + exit(1); +} + +int unitsize; + +int main(int argc, char *argv[]) +{ + int i; + struct mtab *mt; + char *type= "dev"; + int ex= 0; + + while (argc > 1 && argv[1][0] == '-') { + char *opt= argv[1]+1; + + while (*opt != 0) { + switch (*opt++) { + case 'i': iflag= 1; break; + case 'k': kflag= 1; break; + case 'P': Pflag= 1; break; + case 't': + if (argc < 3) usage(); + type= argv[2]; + argv++; + argc--; + break; + default: + usage(); + } + } + argc--; + argv++; + } + + istty= isatty(1); + ruid= getuid(); euid= geteuid(); + rgid= getgid(); egid= getegid(); + + readmtab(type); + + if(!Pflag || (Pflag && kflag)) unitsize = 1024; + else unitsize = 512; + + if (Pflag) { + printf(!iflag ? "\ +Filesystem %4d-blocks Used Available Capacity Mounted on\n" : "\ +Filesystem Inodes IUsed IFree %%IUsed Mounted on\n", + unitsize); + } else { + printf("%s\n", !iflag ? "\ +Filesystem 1k-Blocks free used % FUsed% Mounted on" : "\ +Filesystem Files free used % BUsed% Mounted on" + ); + } + + if (argc == 1) { + for (mt= mtab; mt != NULL; mt= mt->next) ex |= df(mt); + } else { + for (i = 1; i < argc; i++) ex |= df(searchtab(argv[i])); + } + exit(ex); +} + +void readmtab(char *type) +/* Turn the mounted file table into a list. */ +{ + struct mtab **amt= &mtab, *new; + struct stat st; + +#if __minix_vmd + char *devname, *mountpoint; + FILE *mtf; + struct mnttab mte, look; + + if ((mtf= fopen(MTAB, "r")) == NULL) { + fprintf(stderr, "df: can't open %s\n", MTAB); + return; + } + + look.mnt_special= NULL; + look.mnt_mountp= NULL; + look.mnt_fstype= type; + look.mnt_mntopts= NULL; + + while (getmntany(mtf, &mte, &look) >= 0) { + devname= mte.mnt_special; + mountpoint= mte.mnt_mountp; + + /* Skip bad entries, can't complain about everything. */ + if (stat(devname, &st) < 0 || !ISDISK(st.st_mode)) continue; + + /* Make new list cell. */ + if ((new= (struct mtab *) malloc(sizeof(*new))) == NULL + || (new->devname= (char *) malloc(strlen(devname) + 1)) == NULL + || (new->mountpoint= (char *) malloc(strlen(mountpoint) + 1)) == NULL + ) break; + + new->device= st.st_rdev; + strcpy(new->devname, devname); + strcpy(new->mountpoint, mountpoint); + + *amt= new; /* Add the cell to the end. */ + amt= &new->next; + *amt= NULL; + } + fclose(mtf); + +#else /* __minix */ + char devname[128], mountpoint[128], version[10], rw_flag[10]; + + if (load_mtab("df") < 0) exit(1); + + while (get_mtab_entry(devname, mountpoint, version, rw_flag), + devname[0] != 0) { + if (strcmp(type, "dev") == 0) { + if (strcmp(version, "1") != 0 && strcmp(version, "2") != 0 && + strcmp(version, "3")) + continue; + } else { + if (strcmp(type, version) != 0) continue; + } + + /* Skip bad entries, can't complain about everything. */ + if (stat(devname, &st) < 0 || !ISDISK(st.st_mode)) continue; + + /* Make new list cell. */ + if ((new= (struct mtab *) malloc(sizeof(*new))) == NULL + || (new->devname= (char *) malloc(strlen(devname) + 1)) == NULL + || (new->mountpoint= (char *) malloc(strlen(mountpoint) + 1)) == NULL + ) break; + + new->device= st.st_rdev; + strcpy(new->devname, devname); + strcpy(new->mountpoint, mountpoint); + + *amt= new; /* Add the cell to the end. */ + amt= &new->next; + *amt= NULL; + } +#endif +} + +struct mtab *searchtab(char *name) +/* See what we can do with a user supplied name, there are five possibilities: + * 1. It's a device and it is in the mtab: Return mtab entry. + * 2. It's a device and it is not in the mtab: Return device mounted on "". + * 3. It's a file and lives on a device in the mtab: Return mtab entry. + * 4. It's a file and it's not on an mtab device: Search /dev for the device + * and return this device as mounted on "???". + * 5. It's junk: Return something df() will choke on. + */ +{ + static struct mtab unknown; + static char devname[5 + NAME_MAX + 1]= "/dev/"; + struct mtab *mt; + struct stat st; + DIR *dp; + struct dirent *ent; + + unknown.devname= name; + unknown.mountpoint= ""; + + if (stat(name, &st) < 0) return &unknown; /* Case 5. */ + + unknown.device= ISDISK(st.st_mode) ? st.st_rdev : st.st_dev; + + for (mt= mtab; mt != NULL; mt= mt->next) { + if (unknown.device == mt->device) + return mt; /* Case 1 & 3. */ + } + + if (ISDISK(st.st_mode)) { + return &unknown; /* Case 2. */ + } + + if ((dp= opendir("/dev")) == NULL) return &unknown; /* Disaster. */ + + while ((ent= readdir(dp)) != NULL) { + if (ent->d_name[0] == '.') continue; + strcpy(devname + 5, ent->d_name); + if (stat(devname, &st) >= 0 && ISDISK(st.st_mode) + && unknown.device == st.st_rdev + ) { + unknown.devname= devname; + unknown.mountpoint= "???"; + break; + } + } + closedir(dp); + return &unknown; /* Case 4. */ +} + +/* (num / tot) in percentages rounded up. */ +#define percent(num, tot) ((int) ((100L * (num) + ((tot) - 1)) / (tot))) + +/* One must be careful printing all these _t types. */ +#define L(n) ((long) (n)) + +int df(const struct mtab *mt) +{ + int fd; + bit_t i_count, z_count; + block_t totblocks, busyblocks; + int n, block_size; + struct v12_super_block super, *sp; + + /* Don't allow Joe User to df just any device. */ + seteuid(*mt->mountpoint == 0 ? ruid : euid); + setegid(*mt->mountpoint == 0 ? rgid : egid); + + if ((fd = open(mt->devname, O_RDONLY)) < 0) { + fprintf(stderr, "df: %s: %s\n", mt->devname, strerror(errno)); + return(1); + } + lseek(fd, (off_t) SUPER_BLOCK_BYTES, SEEK_SET); /* skip boot block */ + + if (read(fd, (char *) &super, sizeof(super)) != (int) sizeof(super)) { + fprintf(stderr, "df: Can't read super block of %s\n", mt->devname); + close(fd); + return(1); + } + + sp = &super; + if (sp->s_magic != SUPER_V1 && sp->s_magic != SUPER_V2 + && sp->s_magic != SUPER_V3) { + fprintf(stderr, "df: %s: Not a valid file system\n", mt->devname); + close(fd); + return(1); + } + + if(sp->s_magic != SUPER_V3) block_size = STATIC_BLOCK_SIZE; + else block_size = super.s_block_size; + + if(block_size < MIN_BLOCK_SIZE || block_size > MAX_BLOCK_SIZE) { + fprintf(stderr, "df: %s: funny block size (%d)\n", + mt->devname, block_size); + close(fd); + return(1); + } + + if (sp->s_magic == SUPER_V1) sp->s_zones= sp->s_nzones; + + lseek(fd, (off_t) block_size * 2L, SEEK_SET); /* skip rest of super block */ + + i_count = bit_count(sp->s_imap_blocks, (bit_t) (sp->s_ninodes+1), + fd, block_size); + + if (i_count == -1) { + fprintf(stderr, "df: Can't find bit maps of %s\n", mt->devname); + close(fd); + return(1); + } + i_count--; /* There is no inode 0. */ + + /* The first bit in the zone map corresponds with zone s_firstdatazone - 1 + * This means that there are s_zones - (s_firstdatazone - 1) bits in the map + */ + z_count = bit_count(sp->s_zmap_blocks, + (bit_t) (sp->s_zones - (sp->s_firstdatazone - 1)), fd, block_size); + + if (z_count == -1) { + fprintf(stderr, "df: Can't find bit maps of %s\n", mt->devname); + close(fd); + return(1); + } + /* Don't forget those zones before sp->s_firstdatazone - 1 */ + z_count += sp->s_firstdatazone - 1; + +#ifdef __minix_vmd + totblocks = sp->s_zones; + busyblocks = z_count; +#else + totblocks = (block_t) sp->s_zones << sp->s_log_zone_size; + busyblocks = (block_t) z_count << sp->s_log_zone_size; +#endif + + busyblocks = busyblocks * (block_size/512) / (unitsize/512); + totblocks = totblocks * (block_size/512) / (unitsize/512); + + /* Print results. */ + printf("%s", mt->devname); + n= strlen(mt->devname); + if (n > 15 && istty) { putchar('\n'); n= 0; } + while (n < 15) { putchar(' '); n++; } + + if (!Pflag && !iflag) { + printf(" %7ld %7ld %7ld %3d%% %3d%% %s\n", + L(totblocks), /* Blocks */ + L(totblocks - busyblocks), /* free */ + L(busyblocks), /* used */ + percent(busyblocks, totblocks), /* % */ + percent(i_count, sp->s_ninodes), /* FUsed% */ + mt->mountpoint /* Mounted on */ + ); + } + if (!Pflag && iflag) { + printf(" %7ld %7ld %7ld %3d%% %3d%% %s\n", + L(sp->s_ninodes), /* Files */ + L(sp->s_ninodes - i_count), /* free */ + L(i_count), /* used */ + percent(i_count, sp->s_ninodes), /* % */ + percent(busyblocks, totblocks), /* BUsed% */ + mt->mountpoint /* Mounted on */ + ); + } + if (Pflag && !iflag) { + printf(" %7ld %7ld %7ld %4d%% %s\n", + L(totblocks), /* Blocks */ + L(busyblocks), /* Used */ + totblocks - busyblocks, /* Available */ + percent(busyblocks, totblocks), /* Capacity */ + mt->mountpoint /* Mounted on */ + ); + } + if (Pflag && iflag) { + printf(" %7ld %7ld %7ld %4d%% %s\n", + L(sp->s_ninodes), /* Inodes */ + L(i_count), /* IUsed */ + L(sp->s_ninodes - i_count), /* IAvail */ + percent(i_count, sp->s_ninodes), /* Capacity */ + mt->mountpoint /* Mounted on */ + ); + } + close(fd); + return(0); +} + +bit_t bit_count(unsigned blocks, bit_t bits, int fd, int block_size) +{ + char *wptr; + int i, b; + bit_t busy; + char *wlim; + static char buf[MAX_BLOCK_SIZE]; + static char bits_in_char[1 << CHAR_BIT]; + + /* Precalculate bitcount for each char. */ + if (bits_in_char[1] != 1) { + for (b = (1 << 0); b < (1 << CHAR_BIT); b <<= 1) + for (i = 0; i < (1 << CHAR_BIT); i++) + if (i & b) bits_in_char[i]++; + } + + /* Loop on blocks, reading one at a time and counting bits. */ + busy = 0; + for (i = 0; i < blocks && bits != 0; i++) { + if (read(fd, buf, block_size) != block_size) return(-1); + + wptr = &buf[0]; + if (bits >= CHAR_BIT * block_size) { + wlim = &buf[block_size]; + bits -= CHAR_BIT * block_size; + } else { + b = bits / CHAR_BIT; /* whole chars in map */ + wlim = &buf[b]; + bits -= b * CHAR_BIT; /* bits in last char, if any */ + b = *wlim & ((1 << bits) - 1); /* bit pattern from last ch */ + busy += bits_in_char[b]; + bits = 0; + } + + /* Loop on the chars of a block. */ + while (wptr != wlim) + busy += bits_in_char[*wptr++ & ((1 << CHAR_BIT) - 1)]; + } + return(busy); +} + +/* + * $PchId: df.c,v 1.7 1998/07/27 18:42:17 philip Exp $ + */ diff --git a/commands/simple/dhrystone.c b/commands/simple/dhrystone.c new file mode 100755 index 000000000..5eceb8f68 --- /dev/null +++ b/commands/simple/dhrystone.c @@ -0,0 +1,585 @@ +/* dhrystone - benchmark program */ + +#define REGISTER +/* + * + * "DHRYSTONE" Benchmark Program + * + * Version: C/1.1, 12/01/84 + * + * Date: PROGRAM updated 01/06/86, COMMENTS changed 01/31/87 + * + * Author: Reinhold P. Weicker, CACM Vol 27, No 10, 10/84 pg.1013 + * Translated from ADA by Rick Richardson + * Every method to preserve ADA-likeness has been used, + * at the expense of C-ness. + * + * Compile: cc -O dry.c -o drynr : No registers + * cc -O -DREG=register dry.c -o dryr : Registers + * + * Defines: Defines are provided for old C compiler's + * which don't have enums, and can't assign structures. + * The time(2) function is library dependant; Most + * return the time in seconds, but beware of some, like + * Aztec C, which return other units. + * The LOOPS define is initially set for 50000 loops. + * If you have a machine with large integers and is + * very fast, please change this number to 500000 to + * get better accuracy. Please select the way to + * measure the execution time using the TIME define. + * For single user machines, time(2) is adequate. For + * multi-user machines where you cannot get single-user + * access, use the times(2) function. Be careful to + * adjust the HZ parameter below for the units which + * are returned by your times(2) function. You can + * sometimes find this in <sys/param.h>. If you have + * neither time(2) nor times(2), use a stopwatch in + * the dead of the night. + * Use a "printf" at the point marked "start timer" + * to begin your timings. DO NOT use the UNIX "time(1)" + * command, as this will measure the total time to + * run this program, which will (erroneously) include + * the time to malloc(3) storage and to compute the + * time it takes to do nothing. + * + * Run: drynr; dryr + * + * Results: If you get any new machine/OS results, please send to: + * + * ihnp4!castor!pcrat!rick + * + * and thanks to all that do. + * + * Note: I order the list in increasing performance of the + * "with registers" benchmark. If the compiler doesn't + * provide register variables, then the benchmark + * is the same for both REG and NOREG. + * + * PLEASE: Send complete information about the machine type, + * clock speed, OS and C manufacturer/version. If + * the machine is modified, tell me what was done. + * On UNIX, execute uname -a and cc -V to get this info. + * + * 80x8x NOTE: 80x8x benchers: please try to do all memory models + * for a particular compiler. + * + * + * The following program contains statements of a high-level programming + * language (C) in a distribution considered representative: + * + * assignments 53% + * control statements 32% + * procedure, function calls 15% + * + * 100 statements are dynamically executed. The program is balanced with + * respect to the three aspects: + * - statement type + * - operand type (for simple data types) + * - operand access + * operand global, local, parameter, or constant. + * + * The combination of these three aspects is balanced only approximately. + * + * The program does not compute anything meaningfull, but it is + * syntactically and semantically correct. + * + */ + + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <stdio.h> +#include <signal.h> +#include <unistd.h> + +/* Accuracy of timings and human fatigue controlled by next two lines */ +/*#define LOOPS 50000 */ /* Use this for slow or 16 bit machines */ +/*#define LOOPS 500000 */ /* Use this for faster machines */ +/*#define LOOPS (sizeof(int) == 2 ? 50000 : 1000000)*/ + +/* Seconds to run */ +#define SECONDS 15 + +/* Compiler dependent options */ +#define NOENUM /* Define if compiler has no enum's */ +/* #define NOSTRUCTASSIGN */ /* Define if compiler can't assign structures*/ + + +/* Define only one of the next two defines */ +#define TIMES /* Use times(2) time function */ +/*#define TIME */ /* Use time(2) time function */ + + +#ifdef TIME +/* Ganularity of time(2) is of course 1 second */ +#define HZ 1 +#endif + +#ifdef TIMES +/* Define the granularity of your times(2) function */ +/*#define HZ 50 */ /* times(2) returns 1/50 second (europe?) */ +/*#define HZ 60 */ /* times(2) returns 1/60 second (most) */ +/*#define HZ 100 */ /* times(2) returns 1/100 second (WECo) */ +#endif + +/* For compatibility with goofed up version */ +/*#undef GOOF */ /* Define if you want the goofed up version */ + + +#ifdef GOOF +char Version[] = "1.0"; +#else +char Version[] = "1.1"; +#endif + + +#ifdef NOSTRUCTASSIGN +#define structassign(d, s) memcpy(&(d), &(s), sizeof(d)) +#else +#define structassign(d, s) d = s +#endif + + +#ifdef NOENUM +#define Ident1 1 +#define Ident2 2 +#define Ident3 3 +#define Ident4 4 +#define Ident5 5 +typedef int Enumeration; +#else +typedef enum { + Ident1, Ident2, Ident3, Ident4, Ident5 +} Enumeration; +#endif + +typedef int OneToThirty; +typedef int OneToFifty; +typedef char CapitalLetter; +typedef char String30[31]; +typedef int Array1Dim[51]; +typedef int Array2Dim[51][51]; + +struct Record { + struct Record *PtrComp; + Enumeration Discr; + Enumeration EnumComp; + OneToFifty IntComp; + String30 StringComp; +}; + +typedef struct Record RecordType; +typedef RecordType *RecordPtr; +typedef int boolean; + +#ifdef NULL +#undef NULL +#endif + +#define NULL 0 +#define TRUE 1 +#define FALSE 0 + +#ifndef REG +#define REG +#endif + + +#ifdef TIMES +#include <sys/times.h> +#endif + +#ifndef _PROTOTYPE +#define _PROTOTYPE(fun, args) fun args +#endif + +_PROTOTYPE(int main, (void)); +_PROTOTYPE(void prep_timer, (void)); +_PROTOTYPE(void timeout, (int sig)); +_PROTOTYPE(void Proc0, (void)); +_PROTOTYPE(void Proc1, (RecordPtr PtrParIn)); +_PROTOTYPE(void Proc2, (OneToFifty *IntParIO)); +_PROTOTYPE(void Proc3, (RecordPtr *PtrParOut)); +_PROTOTYPE(void Proc4, (void)); +_PROTOTYPE(void Proc5, (void)); +_PROTOTYPE(void Proc6, (Enumeration EnumParIn, Enumeration *EnumParOut)); +_PROTOTYPE(void Proc7, (OneToFifty IntParI1, OneToFifty IntParI2, + OneToFifty *IntParOut)); +_PROTOTYPE(void Proc8, (Array1Dim Array1Par, Array2Dim Array2Par, + OneToFifty IntParI1, OneToFifty IntParI2)); +/*_PROTOTYPE(Enumeration Func1,(CapitalLetter CharPar1, CapitalLetter CharPar2));*/ +_PROTOTYPE(boolean Func2, (String30 StrParI1, String30 StrParI2)); +_PROTOTYPE(boolean Func3, (Enumeration EnumParIn)); + +_PROTOTYPE(Enumeration Func1, (int CharPar1, int CharPar2)); + + +int main() +{ + Proc0(); + return(0); +} + + +#if __STDC__ +volatile int done; +#else +int done; +#endif + +void prep_timer() +{ + signal(SIGALRM, timeout); + done = 0; +} + +void timeout(sig) +int sig; +{ + done = 1; +} + +/* Package 1 */ +int IntGlob; +boolean BoolGlob; +char Char1Glob; +char Char2Glob; +Array1Dim Array1Glob; +Array2Dim Array2Glob; +RecordPtr PtrGlb; +RecordPtr PtrGlbNext; + + +void Proc0() +{ + OneToFifty IntLoc1; + REG OneToFifty IntLoc2; + OneToFifty IntLoc3; + REG char CharIndex; + Enumeration EnumLoc; + String30 String1Loc; + String30 String2Loc; + register unsigned long i; + unsigned long starttime; + unsigned long benchtime; + unsigned long nulltime; + unsigned long nullloops; + unsigned long benchloops; + unsigned long ticks_per_sec; +#ifdef TIMES + struct tms tms; +#endif + +#ifdef HZ +#define ticks_per_sec HZ +#else + ticks_per_sec = sysconf(_SC_CLK_TCK); +#endif + + i = 0; + prep_timer(); + +#ifdef TIME + starttime = time((long *) 0); +#endif + +#ifdef TIMES + times(&tms); + starttime = tms.tms_utime; +#endif + + alarm(1); + while (!done) i++; + +#ifdef TIME + nulltime = time((long *) 0) - starttime; /* Computes o'head of loop */ +#endif + +#ifdef TIMES + times(&tms); + nulltime = tms.tms_utime - starttime; /* Computes overhead of looping */ +#endif + + nullloops = i; + + + PtrGlbNext = (RecordPtr) malloc(sizeof(RecordType)); + PtrGlb = (RecordPtr) malloc(sizeof(RecordType)); + PtrGlb->PtrComp = PtrGlbNext; + PtrGlb->Discr = Ident1; + PtrGlb->EnumComp = Ident3; + PtrGlb->IntComp = 40; + strcpy(PtrGlb->StringComp, "DHRYSTONE PROGRAM, SOME STRING"); +#ifndef GOOF + strcpy(String1Loc, "DHRYSTONE PROGRAM, 1'ST STRING"); /* GOOF */ +#endif + + Array2Glob[8][7] = 10; /* Was missing in published program */ + + +/***************** +-- Start Timer -- +*****************/ + i = 0; + prep_timer(); + +#ifdef TIME + starttime = time((long *) 0); +#endif + +#ifdef TIMES + times(&tms); + starttime = tms.tms_utime; +#endif + + alarm(SECONDS); + while (!done) { + i++; + Proc5(); + Proc4(); + IntLoc1 = 2; + IntLoc2 = 3; + strcpy(String2Loc, "DHRYSTONE PROGRAM, 2'ND STRING"); + EnumLoc = Ident2; + BoolGlob = !Func2(String1Loc, String2Loc); + while (IntLoc1 < IntLoc2) { + IntLoc3 = 5 * IntLoc1 - IntLoc2; + Proc7(IntLoc1, IntLoc2, &IntLoc3); + ++IntLoc1; + } + Proc8(Array1Glob, Array2Glob, IntLoc1, IntLoc3); + Proc1(PtrGlb); + for (CharIndex = 'A'; CharIndex <= Char2Glob; ++CharIndex) + if (EnumLoc == Func1(CharIndex, 'C')) + Proc6(Ident1, &EnumLoc); + IntLoc3 = IntLoc2 * IntLoc1; + IntLoc2 = IntLoc3 / IntLoc1; + IntLoc2 = 7 * (IntLoc3 - IntLoc2) - IntLoc1; + Proc2(&IntLoc1); + } + + +/***************** +-- Stop Timer -- +*****************/ + + +#ifdef TIME + benchtime = time((long *) 0) - starttime; +#endif + +#ifdef TIMES + times(&tms); + benchtime = tms.tms_utime - starttime; +#endif + benchloops = i; + + /* Approximately correct benchtime to the nulltime. */ + benchtime -= nulltime / (nullloops / benchloops); + + printf("Dhrystone(%s) time for %lu passes = %lu.%02lu\n", + Version, + benchloops, benchtime / ticks_per_sec, + benchtime % ticks_per_sec * 100 / ticks_per_sec); + printf("This machine benchmarks at %lu dhrystones/second\n", + benchloops * ticks_per_sec / benchtime); +} + + +void Proc1(PtrParIn) +REG RecordPtr PtrParIn; +{ +#define NextRecord (*(PtrParIn->PtrComp)) + + + structassign(NextRecord, *PtrGlb); + PtrParIn->IntComp = 5; + NextRecord.IntComp = PtrParIn->IntComp; + NextRecord.PtrComp = PtrParIn->PtrComp; + Proc3((RecordPtr *)NextRecord.PtrComp); + if (NextRecord.Discr == Ident1) { + NextRecord.IntComp = 6; + Proc6(PtrParIn->EnumComp, &NextRecord.EnumComp); + NextRecord.PtrComp = PtrGlb->PtrComp; + Proc7(NextRecord.IntComp, 10, &NextRecord.IntComp); + } else + structassign(*PtrParIn, NextRecord); + + +#undef NextRecord +} + + +void Proc2(IntParIO) +OneToFifty *IntParIO; +{ + REG OneToFifty IntLoc; + REG Enumeration EnumLoc; + + + IntLoc = *IntParIO + 10; + for (;;) { + if (Char1Glob == 'A') { + --IntLoc; + *IntParIO = IntLoc - IntGlob; + EnumLoc = Ident1; + } + if (EnumLoc == Ident1) break; + } +} + + +void Proc3(PtrParOut) +RecordPtr *PtrParOut; +{ + if (PtrGlb != NULL) + *PtrParOut = PtrGlb->PtrComp; + else + IntGlob = 100; + Proc7(10, IntGlob, &PtrGlb->IntComp); +} + + +void Proc4() +{ + REG boolean BoolLoc; + + + BoolLoc = Char1Glob == 'A'; + BoolLoc |= BoolGlob; + Char2Glob = 'B'; +} + + +void Proc5() +{ + Char1Glob = 'A'; + BoolGlob = FALSE; +} + + +void Proc6(EnumParIn, EnumParOut) +REG Enumeration EnumParIn; +REG Enumeration *EnumParOut; +{ + *EnumParOut = EnumParIn; + if (!Func3(EnumParIn)) *EnumParOut = Ident4; + switch (EnumParIn) { + case Ident1: *EnumParOut = Ident1; break; + case Ident2: + if (IntGlob > 100) + *EnumParOut = Ident1; + else + *EnumParOut = Ident4; + break; + case Ident3: *EnumParOut = Ident2; break; + case Ident4: + break; + case Ident5: *EnumParOut = Ident3; +} +} + + +void Proc7(IntParI1, IntParI2, IntParOut) +OneToFifty IntParI1; +OneToFifty IntParI2; +OneToFifty *IntParOut; +{ + REG OneToFifty IntLoc; + + + IntLoc = IntParI1 + 2; + *IntParOut = IntParI2 + IntLoc; +} + + +void Proc8(Array1Par, Array2Par, IntParI1, IntParI2) +Array1Dim Array1Par; +Array2Dim Array2Par; +OneToFifty IntParI1; +OneToFifty IntParI2; +{ + REG OneToFifty IntLoc; + REG OneToFifty IntIndex; + + + IntLoc = IntParI1 + 5; + Array1Par[IntLoc] = IntParI2; + Array1Par[IntLoc + 1] = Array1Par[IntLoc]; + Array1Par[IntLoc + 30] = IntLoc; + for (IntIndex = IntLoc; IntIndex <= (IntLoc + 1); ++IntIndex) + Array2Par[IntLoc][IntIndex] = IntLoc; + ++Array2Par[IntLoc][IntLoc - 1]; + Array2Par[IntLoc + 20][IntLoc] = Array1Par[IntLoc]; + IntGlob = 5; +} + + +Enumeration Func1(CharPar1, CharPar2) +CapitalLetter CharPar1; +CapitalLetter CharPar2; +{ + REG CapitalLetter CharLoc1; + REG CapitalLetter CharLoc2; + + + CharLoc1 = CharPar1; + CharLoc2 = CharLoc1; + if (CharLoc2 != CharPar2) + return(Ident1); + else + return(Ident2); +} + + +boolean Func2(StrParI1, StrParI2) +String30 StrParI1; +String30 StrParI2; +{ + REG OneToThirty IntLoc; + REG CapitalLetter CharLoc; + + + IntLoc = 1; + while (IntLoc <= 1) + if (Func1(StrParI1[IntLoc], StrParI2[IntLoc + 1]) == Ident1) { + CharLoc = 'A'; + ++IntLoc; + } + if (CharLoc >= 'W' && CharLoc <= 'Z') IntLoc = 7; + if (CharLoc == 'X') + return(TRUE); + else { + if (strcmp(StrParI1, StrParI2) > 0) { + IntLoc += 7; + return(TRUE); + } else + return(FALSE); + } +} + + +boolean Func3(EnumParIn) +REG Enumeration EnumParIn; +{ + REG Enumeration EnumLoc; + + + EnumLoc = EnumParIn; + if (EnumLoc == Ident3) return(TRUE); + return(FALSE); +} + + +#ifdef NOSTRUCTASSIGN +memcpy(d, s, l) +register char *d; +register char *s; +register int l; +{ + while (l--) *d++ = *s++; +} + +#endif diff --git a/commands/simple/diff.c b/commands/simple/diff.c new file mode 100755 index 000000000..4764ed595 --- /dev/null +++ b/commands/simple/diff.c @@ -0,0 +1,1252 @@ +/* diff - print differences between 2 files Author: Erik Baalbergen */ + +/* Poor man's implementation of diff(1) - no options available +* - may give more output than other diffs, +* due to the straight-forward algorithm +* - runs out of memory if the differing chunks become too large +* - input line length should not exceed LINELEN; longer lines are +* truncated, while only the first LINELEN characters are compared +* +* - Bug fixes by Rick Thomas Sept. 1989 +* +* Please report bugs and suggestions to erikb@cs.vu.nl +*------------------------------------------------------------------------------ +* Changed diff to conform to POSIX 1003.2 ( Draft 11) by Thomas Brupbacher +* ( tobr@mw.lpc.ethz.ch). +* +* To incorporate the context diff option -c in the program, the source code +* for the program cdiff has been copied to the end of this program. Only +* slight modifications for the cdiff code to work within the program diff +* were made( e.g. main() -> context_diff()). +* +* New options: +* -c, -C n where n=0,1,...: +* produces a context diff as the program cdiff. The default is to +* print 3 lines of context, this value can be changed with -C +* ( e.g. -C 5 prints five lines of context.) +* -e : Prints an ed script, so you can convert <file1> to <file2> with +* the command ed <file1> < `diff -e <file1> <file2>`. +* -b : Causes trailing blanks to be ignored and spaces of multiple blanks +* to be reduced to one blank before comparison. +*----------------------------------------------------------------------------- +*/ + +#include <stdlib.h> +#include <limits.h> /* NAME_MAX for maximal filename length */ +#include <string.h> /* string manipulation */ +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <ctype.h> +#include <time.h> +#include <dirent.h> +#include <unistd.h> +#include <stdio.h> + +/* These definitions are needed only to suppress warning messages. */ +#define Nullfp ((FILE*)0) +#define Nullch ((char*)0) +#define NullStructLine ((struct line *)0) + +#define LINELEN 128 /* max line length included in diff */ + + +#define NOT_SET 0 /* Defines to characterise if a flag */ +#define SET 1 /* is set */ + + /* Indexes of the warning-message array */ +#define EXCLUSIVE_OPTIONS 0 +#define CANNOT_OPEN_FILE 1 + + /* Used to define the mode */ +typedef enum { + undefined, context, ed_mode +} MODE; + + /* Global variables for the 'normal' diff part */ +char *progname; /* program name (on command line) */ +int diffs = 0; /* number of differences */ +MODE mode; /* which mode is used */ +int severe_error; /* nonzero after severe, non-fatal error */ + +/* The following global variables are used with the -r option: + * for every pair of files that are different, a "command line" of the + * form "diff <options> <oldfile> <newfile>" is printed before the real + * output starts. */ +int firstoutput = 1; /* flag to print one time */ +char options_string[10]; /* string to hold command line options */ +char oldfile[PATH_MAX]; /* first file */ +char newfile[PATH_MAX]; /* second file */ + + + /* Global variables for the command-line options */ +int trim_blanks = NOT_SET; /* SET if -b specified */ +int recursive_dir = NOT_SET; /* SET if -r specified */ +int context_lines = 3; /* numbers of lines in a context */ +static int offset; /* offset of the actual line number for -e */ + + /* Function prototypes for the functions in this file */ +struct f; +_PROTOTYPE(int main, (int argc, char **argv )); +_PROTOTYPE(void process_command_line, (int argc, char **argv )); +_PROTOTYPE(void analyse_input_files, (char *arg1, char *arg2, char *input1, + char *input2 )); +_PROTOTYPE(void diff, (char *filename1, char *filename2 )); +_PROTOTYPE(FILE *check_file, (char *name )); +_PROTOTYPE(void build_option_string, (void )); +_PROTOTYPE(void fatal_error, (char *fmt, char *s )); +_PROTOTYPE(void warn, (int number, char *string )); +_PROTOTYPE(void trimming_blanks, (char *l_text )); +_PROTOTYPE(char *filename, (char *path_string)); +_PROTOTYPE(struct line *new_line, (int size )); +_PROTOTYPE(void free_line, (struct line *l )); +_PROTOTYPE(int equal_line, (struct line *l1, struct line *l2 )); +_PROTOTYPE(int equal_3, (struct line *l1, struct line *l2 )); +_PROTOTYPE(struct line *read_line, (FILE *fp )); +_PROTOTYPE(void advance, (struct f *f )); +_PROTOTYPE(void aside, (struct f *f, struct line *l )); +_PROTOTYPE(struct line *next, (struct f *f )); +_PROTOTYPE(void init_f, (struct f *f, FILE *fp )); +_PROTOTYPE(void update, (struct f *f, char *s )); +_PROTOTYPE(void __diff, (FILE *fp1, FILE *fp2 )); +_PROTOTYPE(void differ, (struct f *f1, struct f *f2 )); +_PROTOTYPE(int wlen, (struct f *f )); +_PROTOTYPE(void range, (int a, int b )); +_PROTOTYPE(void cdiff, (char *old, char *new, FILE *file1, FILE *file2 )); +_PROTOTYPE(void dumphunk, (void )); +_PROTOTYPE(char *getold, (int targ )); +_PROTOTYPE(char *getnew, (int targ )); +_PROTOTYPE(int isdir, (char *path )); +_PROTOTYPE(void diff_recursive, (char *dir1, char *dir2 )); +_PROTOTYPE(void file_type_error, (char *filename1, char *filename2, + struct stat *statbuf1, struct stat *statbuf2 )); +_PROTOTYPE(void *xmalloc, (size_t size)); +_PROTOTYPE(void *xrealloc, (void *ptr, size_t size)); + +int main(argc, argv) +int argc; +char **argv; +{ + char file1[PATH_MAX], file2[PATH_MAX]; + extern int optind; /* index of the current string in argv */ + + progname = argv[0]; + process_command_line(argc, argv); + + analyse_input_files(argv[optind], argv[optind + 1], file1, file2); + optind++; + + if (recursive_dir == SET) { + build_option_string(); + diff_recursive(file1, file2); + } else { + diff(file1, file2); + } + + return(severe_error ? 2 : diffs > 0 ? 1 : 0); +} + +/* Process the command line and set the flags for the different + * options. the processing of the command line is done with the + * getopt() library function. a minimal error processing is done + * for the number of command line arguments. */ +void process_command_line(argc, argv) +int argc; /* number of arguments on command line */ +char **argv; /* ** to arguments on command line */ +{ + int c; + extern char *optarg; /* points to string with options */ + extern int optind; /* index of the current string in argv */ + + /* Are there enough arguments? */ + if (argc < 3) { + fatal_error("Usage: %s [-c|-e|-C n][-br] file1 file2\n", progname); + } + + /* Process all options using getopt() */ + while ((c = getopt(argc, argv, "ceC:br")) != -1) { + switch (c) { + case 'c': + if (mode != undefined) warn(EXCLUSIVE_OPTIONS, "c"); + mode = context; + context_lines = 3; + break; + case 'e': + if (mode != undefined) warn(EXCLUSIVE_OPTIONS, "e"); + mode = ed_mode; + break; + case 'C': + if (mode != undefined) warn(EXCLUSIVE_OPTIONS, "C"); + mode = context; + context_lines = atoi(optarg); + break; + case 'b': trim_blanks = SET; break; + case 'r': recursive_dir = SET; break; + case '?': + exit(2); + } + } + + /* We should have two arguments left */ + if ((argc - optind) != 2) + fatal_error("Need exactly two input file-names!\n", ""); +} + +/* Analyse_input_files takes the two input files on the command line + * and decides what to do. returns the (corrected) filenames that + * can be used to call diff(). + * if two directories are given, then a recursive diff is done. + * one directory and one filename compares the file with <filename> + * in the directory <directory> with <filename>. + * if two filenames are specified, no special action takes place. + */ +void analyse_input_files(arg1, arg2, input1, input2) +char *arg1, *arg2; /* filenames on the command line */ +char *input1, *input2; /* filenames to be used with diff() */ +{ + int stat1 = 0, stat2 = 0; + + if (strcmp(arg1, "-") != 0) + stat1 = isdir(arg1); /* != 0 <-> arg1 is directory */ + if (strcmp(arg2, "-") != 0) stat2 = isdir(arg2); +#ifdef DEBUG + fprintf(stderr, "%s, stat = %d\n", arg1, stat1); + fprintf(stderr, "%s, stat = %d\n", arg2, stat2); +#endif + if (stat1 && stat2) { /* both arg1 and arg2 are directories */ + recursive_dir = SET; + strcpy(input1, arg1); + strcpy(input2, arg2); + return; + } + if (stat1 != 0) { /* arg1 is a dir, arg2 not */ + if (strcmp(arg2, "-") != 0) { /* arg2 != stdin */ + strcpy(input1, arg1); + strcat(input1, "/"); + strcat(input1, arg2); + strcpy(input2, arg2); + return; + } else { + fatal_error("cannot compare stdin (-) with a directory!", ""); + } + } + if (stat2 != 0) { /* arg2 is a dir, arg1 not */ + if (strcmp(arg1, "-") != 0) { /* arg1 != stdin */ + strcpy(input1, arg1); + strcpy(input2, arg2); + strcat(input2, "/"); + strcat(input2, arg1); + return; + } else { /* arg1 == stdin */ + fatal_error("cannot compare stdin (-) with a directory!", ""); + } + } + + /* Both arg1 and arg2 are normal files */ + strcpy(input1, arg1); + strcpy(input2, arg2); +} + +/* Diff() is the front end for all modes of the program diff, execpt + * the recursive_dir option. + * diff() expects the filenames of the two files to be compared as + * arguments. the mode is determined from the global variable mode. + */ +void diff(filename1, filename2) +char *filename1, *filename2; +{ + FILE *file1 = check_file(filename1); + FILE *file2 = check_file(filename2); + struct stat statbuf1, statbuf2; + + if ((file1 != Nullfp) && (file2 != Nullfp)) { + /* If we do a recursive diff, then we don't compare block + * special, character special or FIFO special files to any + * file. */ + fstat(fileno(file1), &statbuf1); + fstat(fileno(file2), &statbuf2); + if ((((statbuf1.st_mode & S_IFREG) != S_IFREG) || + ((statbuf2.st_mode & S_IFREG) != S_IFREG)) && + (recursive_dir == SET)) { + file_type_error(filename1, filename2, &statbuf1, &statbuf2); + } else { + switch (mode) { + case context: + cdiff(filename1, filename2, file1, file2); + break; + case ed_mode: + case undefined: + __diff(file1, file2); + if (mode == ed_mode) printf("w\n"); + break; + } + } + } else + severe_error = 1; + if (file1 != Nullfp) fclose(file1); + if (file2 != Nullfp) fclose(file2); +} + +/* Check_file() opens the fileptr with name <filename>. if <filename> + * equals "-" stdin is associated with the return value. + */ +FILE *check_file(name) +char *name; +{ + FILE *temp; + + if (strcmp(name, "-") == 0) { + return(stdin); + } else { + temp = fopen(name, "r"); + if (temp == Nullfp) warn(CANNOT_OPEN_FILE, name); + return(temp); + } +} + +/* Build_option_string() is called before recursive_dir() is called + * from the main() function. its purpose is to build the string that + * is used on the command line to get the current operation mode. + * e.g. "-C 6 -b". + */ +void build_option_string() +{ + switch (mode) { + case ed_mode:sprintf(options_string, "-e"); + break; + case context: + if (context_lines == 3) + sprintf(options_string, "-c"); + else + sprintf(options_string, "-C %d", context_lines); + break; + } + +} + + +/* The fatal error handler. + * Expects a format string and a string as arguments. The arguments + * are printed to stderr and the program exits with an error code 2. + */ +void fatal_error(fmt, s) +char *fmt; /* the format sttring to be printed */ +char *s; /* string to be inserted into the format + * string */ +{ + fprintf(stderr, "%s: ", progname); + fprintf(stderr, fmt, s); + fprintf(stderr, "\n"); + exit(2); +} + +/* This function prints non fatal error messages to stderr. + * Expects the index of the message to be printed and a pointer + * to the (optional) string to be printed. + * Returns no value. + */ +void warn(number, string) +int number; /* index of the warning */ +char *string; /* string to be inserted to the warning */ +{ + static char *warning[] = { + "%s: The options -c, -e, -C n are mutually exclusive! Assuming -%c\n", + "%s: cannot open file %s for reading\n", + }; + fprintf(stderr, warning[number], progname, string); +} + +/* Function used with the optione -b, trims the blanks in a input line: + * - blanks between words are reduced to one + * - trailing blanks are eliminated. + */ +void trimming_blanks(l_text) +char *l_text; /* begin of the char array */ +{ + char *line = l_text; + char *copy_to, *copy_from; + + do { + if (*line == ' ') { + copy_from = line; + copy_to = line; + while (*(++copy_from) == ' '); + if (*copy_from != '\n') copy_to++; + while (*copy_from != '\0') *(copy_to++) = *(copy_from++); + *copy_to = '\0'; + } + } while (*(++line) != '\0'); +} + + +/* Filename separates the filename and the relative path in path_string. + * Returns the filename with a leading / + */ +char *filename(path_string) +char *path_string; +{ + char name[NAME_MAX + 2]; /* filename plus / */ + char *ptr; + + name[0] = '/'; + ptr = strrchr(path_string, '/'); + + if (ptr == 0) { /* no / in path_string, only a filename */ + strcat(name, path_string); + } else { + strcat(name, ptr); + } + + return(name); +} + +/* The line module: one member in a linked list of lines. */ +struct line { + struct line *l_next; /* pointer to the next line */ + char l_eof; /* == 0 if last line in file */ + char *l_text; /* array with the text */ +}; + +struct line *freelist = 0; +#define stepup(ll) ( ((ll) && ((ll)->l_eof==0)) ? (ll)->l_next : (ll) ) + +/* Function to allocate space for a new line containing SIZE chars */ +struct line *new_line(size) +int size; +{ + register struct line *l; + + if ((l = freelist) != NullStructLine) + freelist = freelist->l_next; + else { + l = (struct line *) xmalloc(3 * sizeof(void *)); + l->l_text = (char *) xmalloc((size + 2) * sizeof(char)); + if ((l == 0) || (l->l_text == 0)) fatal_error("Out of memory", ""); + } + return l; +} + + +/* Free_line() releases storage allocated for <l>. */ +void free_line(l) +register struct line *l; +{ + l->l_next = freelist; + freelist = l; +} + +/* Equal_line() compares two lines, <l1> and <l2>. + * the returned value is the result of the strcmp() function. + */ +int equal_line(l1, l2) +struct line *l1, *l2; +{ + if (l1 == 0 || l2 == 0) + return(0); + else if (l1->l_eof || l2->l_eof) + return(l1->l_eof == l2->l_eof); + else + return(strcmp(l1->l_text, l2->l_text) == 0); +} + +int equal_3(l1, l2) +struct line *l1, *l2; +{ + register int i, ansr; + + ansr = 1; +#ifdef DEBUG + if (l1 == 0) + fprintf(stderr, "\t(null)\n"); + else if (l1->l_eof) + fprintf(stderr, "\t(eof)\n"); + else + fprintf(stderr, "\t%s", l1->l_text); + if (l2 == 0) + fprintf(stderr, "\t(null)\n"); + else if (l2->l_eof) + fprintf(stderr, "\t(eof)\n"); + else + fprintf(stderr, "\t%s", l2->l_text); +#endif + for (i = 0; i < 3; ++i) { + if (!equal_line(l1, l2)) { + ansr = 0; + break; + } + l1 = stepup(l1); + l2 = stepup(l2); + } +#ifdef DEBUG + fprintf(stderr, "\t%d\n", ansr); +#endif + return(ansr); +} + +struct line * + read_line(fp) +FILE *fp; +{ + register struct line *l = new_line(LINELEN); + register char *p; + register int c; + + (p = &(l->l_text[LINELEN]))[1] = '\377'; + l->l_eof = 0; + if (fgets(l->l_text, LINELEN + 2, fp) == 0) { + l->l_eof = 1; + l->l_text[0] = 0; + } else if ((p[1] & 0377) != 0377 && *p != '\n') { + while ((c = fgetc(fp)) != '\n' && c != EOF) { + } + *p++ = '\n'; + *p = '\0'; + } + l->l_next = 0; + if (trim_blanks == SET) { +#ifdef DEBUG + printf("xxx %s xxx\n", l->l_text); +#endif + trimming_blanks(l->l_text); +#ifdef DEBUG + printf("xxx %s xxx\n", l->l_text); +#endif + } + return l; +} + +/* File window handler */ +struct f { + struct line *f_bwin, *f_ewin; + struct line *f_aside; + int f_linecnt; /* line number in file of last advanced line */ + FILE *f_fp; +}; + +void advance(f) +register struct f *f; +{ + register struct line *l; + + if ((l = f->f_bwin) != NullStructLine) { + if (f->f_ewin == l) + f->f_bwin = f->f_ewin = 0; + else + f->f_bwin = l->l_next; + free_line(l); + (f->f_linecnt)++; + } +} + +void aside(f, l) +struct f *f; +struct line *l; +{ + register struct line *ll; + + if (l == 0) return; + if ((ll = l->l_next) != NullStructLine) { + while (ll->l_next) ll = ll->l_next; + ll->l_next = f->f_aside; + f->f_aside = l->l_next; + l->l_next = 0; + f->f_ewin = l; + } +} + + +struct line *next(f) +register struct f *f; +{ + register struct line *l; + + if ((l = f->f_aside) != NullStructLine) { + f->f_aside = l->l_next; + l->l_next = 0; + } else + l = read_line(f->f_fp); + if (l) { + if (f->f_bwin == 0) + f->f_bwin = f->f_ewin = l; + else { + if (f->f_ewin->l_eof && l->l_eof) { + free_line(l); + return(f->f_ewin); + } + f->f_ewin->l_next = l; + f->f_ewin = l; + } + } + return l; +} + + +/* Init_f() initialises a window structure (struct f). <fp> is the + * file associated with <f>. + */ +void init_f(f, fp) +register struct f *f; +FILE *fp; +{ + f->f_bwin = f->f_ewin = f->f_aside = 0; + f->f_linecnt = 0; + f->f_fp = fp; +} + + +/* Update() prints a window. <f> is a pointer to the window, <s> is the + * string containing the "prefix" to the printout( either "<" or ">"). + * after completion of update(), the window is empty. + */ +void update(f, s) +register struct f *f; +char *s; +{ + char *help; + int only_dot = 0; + + if (firstoutput && (recursive_dir == SET)) { + printf("diff %s %s %s\n", options_string, oldfile, newfile); + firstoutput = 0; + } + while (f->f_bwin && f->f_bwin != f->f_ewin) { + if (mode != ed_mode) { + printf("%s%s", s, f->f_bwin->l_text); + } else { +#ifdef DEBUG + printf("ed_mode: test for only dot"); + printf("%s", f->f_bwin->l_text); +#endif + help = f->f_bwin->l_text; + while ((*help == ' ') || + (*help == '.') || + (*help == '\t')) { + if (*(help++) == '.') only_dot++; + if (only_dot > 1) break; + } + + /* If only_dot is equal 1, there is only one dot on + * the line, so we have to take special actions. + * f the line with only one dot is found, we output + * two dots (".."), terminate the append modus and + * substitute "." for "..". Afterwards we restart + * with the append command. */ + if (*help == '\n' && only_dot == 1) { + help = f->f_bwin->l_text; + while (*help != '\0') { + if (*help == '.') printf("."); + putchar((int) *(help++)); + } + printf(".\n"); + printf(".s/\\.\\././\n"); + printf("a\n"); + } else { + printf("%s%s", s, f->f_bwin->l_text); + } + } + advance(f); + } +} + +/* __Diff(), performs the "core operation" of the program. + * Expects two file-pointers as arguments. This functions does + * *not* check if the file-pointers are valid. + */ + +void __diff(fp1, fp2) +FILE *fp1, *fp2; +{ + struct f f1, f2; + struct line *l1, *s1, *b1, *l2, *s2, *b2; + register struct line *ll; + + init_f(&f1, fp1); + init_f(&f2, fp2); + l1 = next(&f1); + l2 = next(&f2); + while ((l1->l_eof == 0) || (l2->l_eof == 0)) { + if (equal_line(l1, l2)) { + equal: + advance(&f1); + advance(&f2); + l1 = next(&f1); + l2 = next(&f2); + continue; + } + s1 = b1 = l1; + s2 = b2 = l2; + /* Read several more lines */ + next(&f1); + next(&f1); + next(&f2); + next(&f2); + /* Start searching */ +search: + next(&f2); + ll = s1; + do { + if (equal_3(ll, b2)) { + l1 = ll; + l2 = b2; + aside(&f1, ll); + aside(&f2, b2); + differ(&f1, &f2); + goto equal; + } + if (ll->l_eof) break; + ll = stepup(ll); + } while (ll); + b2 = stepup(b2); + + next(&f1); + ll = s2; + do { + if (equal_3(b1, ll)) { + l1 = b1; + l2 = ll; + aside(&f2, ll); + aside(&f1, b1); + differ(&f1, &f2); + goto equal; + } + if (ll->l_eof != 0) break; + ll = stepup(ll); + } while (ll); + b1 = stepup(b1); + + goto search; + } + + /* Both of the files reached EOF */ +} + +/* Differ() prints the differences between files. the arguments <f1> and + * <f2> are pointers to the two windows, where the differences are. + */ +void differ(f1, f2) +register struct f *f1, *f2; +{ + int cnt1 = f1->f_linecnt, len1 = wlen(f1); + int cnt2 = f2->f_linecnt, len2 = wlen(f2); + if ((len1 != 0) || (len2 != 0)) { + if (len1 == 0) { + if (mode == ed_mode) { + cnt1 += offset; + printf("%d a\n", cnt1); + update(f2, ""); + printf(".\n"); + offset += len2; + } else { + printf("%da", cnt1); + range(cnt2 + 1, cnt2 + len2); + } + } else if (len2 == 0) { + if (mode == ed_mode) { + cnt1 += offset; + range(cnt1 + 1, cnt1 + len1); + printf("d\n"); + offset -= len1; + while (f1->f_bwin && f1->f_bwin != f1->f_ewin) + advance(f1); + } else { + range(cnt1 + 1, cnt1 + len1); + printf("d%d", cnt2); + } + } else { + if (mode != ed_mode) { + range(cnt1 + 1, cnt1 + len1); + putchar('c'); + range(cnt2 + 1, cnt2 + len2); + } else { + cnt1 += offset; + if (len1 == len2) { + range(cnt1 + 1, cnt1 + len1); + printf("c\n"); + update(f2, ""); + printf(".\n"); + } else { + range(cnt1 + 1, cnt1 + len1); + printf("d\n"); + printf("%d a\n", cnt1); + update(f2, ""); + printf(".\n"); + offset -= len1 - len2; + } + while (f1->f_bwin && f1->f_bwin != f1->f_ewin) + advance(f1); + } + } + if (mode != ed_mode) { + putchar('\n'); + if (len1 != 0) update(f1, "< "); + if ((len1 != 0) && (len2 != 0)) printf("---\n"); + if (len2 != 0) update(f2, "> "); + } + diffs++; + } +} + + +/* Function wlen() calculates the number of lines in a window. */ +int wlen(f) +struct f *f; +{ + register cnt = 0; + register struct line *l = f->f_bwin, *e = f->f_ewin; + + while (l && l != e) { + cnt++; + l = l->l_next; + } + return cnt; +} + + +/* Range() prints the line numbers of a range. the arguments <a> and <b> + * are the beginning and the ending line number of the range. if + * <a> == <b>, only one line number is printed. otherwise <a> and <b> are + * separated by a ",". + */ +void range(a, b) +int a, b; +{ + printf(((a == b) ? "%d" : "%d,%d"), a, b); +} + +/* Here follows the code for option -c. + * This code is from the cdiff program by Larry Wall. I changed it only + * slightly to reflect the POSIX standard and to call the main routine + * as function context_diff(). + */ + +/* Cdiff - context diff Author: Larry Wall */ + +/* These global variables are still here from the original cdiff program... + * I was to lazy just to sort them out... + */ +char buff[512]; +FILE *oldfp, *newfp; + +int oldmin, oldmax, newmin, newmax; +int oldbeg, oldend, newbeg, newend; +int preoldmax, prenewmax; +int preoldbeg, preoldend, prenewbeg, prenewend; +int oldwanted, newwanted; + +char *oldhunk, *newhunk; +size_t oldsize, oldalloc, newsize, newalloc; + +int oldline, newline; /* Jose */ + +void cdiff(old, new, file1, file2) +char *old, *new; /* The names of the two files to be compared */ +FILE *file1, *file2; /* The corresponding file-pointers */ +{ + FILE *inputfp; + struct stat statbuf; + register char *s; + char op; + char *newmark, *oldmark; + int len; + char *line; + int i, status; + + oldfp = file1; + newfp = file2; + + oldalloc = 512; + oldhunk = (char *) xmalloc(oldalloc); + newalloc = 512; + newhunk = (char *) xmalloc(newalloc); + + +/* The context diff spawns a new process that executes a normal diff + * and parses the output. + */ + if (trim_blanks == SET) + sprintf(buff, "diff -b %s %s", old, new); + else + sprintf(buff, "diff %s %s", old, new); + + inputfp = popen(buff, "r"); + if (!inputfp) { + fprintf(stderr, "Can't execute diff %s %s\n", old, new); + exit(2); + } + preoldend = -1000; + firstoutput = 1; + oldline = newline = 0; + while (fgets(buff, sizeof buff, inputfp) != Nullch) { + if (firstoutput) { + if (recursive_dir == SET) { + printf("diff %s %s %s\n", options_string, + oldfile, newfile); + } + fstat(fileno(oldfp), &statbuf); + printf("*** %s %s", old, ctime(&statbuf.st_mtime)); + fstat(fileno(newfp), &statbuf); + printf("--- %s %s", new, ctime(&statbuf.st_mtime)); + firstoutput = 0; + } + if (isdigit(*buff)) { + oldmin = atoi(buff); + for (s = buff; isdigit(*s); s++); + if (*s == ',') { + s++; + oldmax = atoi(s); + for (; isdigit(*s); s++); + } else { + oldmax = oldmin; + } + if (*s != 'a' && *s != 'd' && *s != 'c') { + fprintf(stderr, "Unparseable input: %s", s); + exit(2); + } + op = *s; + s++; + newmin = atoi(s); + for (; isdigit(*s); s++); + if (*s == ',') { + s++; + newmax = atoi(s); + for (; isdigit(*s); s++); + } else { + newmax = newmin; + } + if (*s != '\n' && *s != ' ') { + fprintf(stderr, "Unparseable input: %s", s); + exit(2); + } + newmark = oldmark = "! "; + if (op == 'a') { + oldmin++; + newmark = "+ "; + } + if (op == 'd') { + newmin++; + oldmark = "- "; + } + oldbeg = oldmin - context_lines; + oldend = oldmax + context_lines; + if (oldbeg < 1) oldbeg = 1; + newbeg = newmin - context_lines; + newend = newmax + context_lines; + if (newbeg < 1) newbeg = 1; + + if (preoldend < oldbeg - 1) { + if (preoldend >= 0) { + dumphunk(); + } + preoldbeg = oldbeg; + prenewbeg = newbeg; + oldwanted = newwanted = 0; + oldsize = newsize = 0; + } else { /* we want to append to previous hunk */ + oldbeg = preoldmax + 1; + newbeg = prenewmax + 1; + } + + for (i = oldbeg; i <= oldmax; i++) { + line = getold(i); + if (!line) { + oldend = oldmax = i - 1; + break; + } + len = strlen(line) + 2; + if (oldsize + len + 1 >= oldalloc) { + oldalloc *= 2; + oldhunk = (char *) xrealloc(oldhunk, oldalloc); + } + if (i >= oldmin) { + strcpy(oldhunk + oldsize, oldmark); + oldwanted++; + } else { + strcpy(oldhunk + oldsize, " "); + } + strcpy(oldhunk + oldsize + 2, line); + oldsize += len; + } + preoldmax = oldmax; + preoldend = oldend; + + for (i = newbeg; i <= newmax; i++) { + line = getnew(i); + if (!line) { + newend = newmax = i - 1; + break; + } + len = strlen(line) + 2; + if (newsize + len + 1 >= newalloc) { + newalloc *= 2; + newhunk = (char *) xrealloc(newhunk, newalloc); + } + if (i >= newmin) { + strcpy(newhunk + newsize, newmark); + newwanted++; + } else { + strcpy(newhunk + newsize, " "); + } + strcpy(newhunk + newsize + 2, line); + newsize += len; + } + prenewmax = newmax; + prenewend = newend; + } + } + status = pclose(inputfp); + if (status != 0) diffs++; + if (!WIFEXITED(status) || WEXITSTATUS(status) > 1) severe_error = 1; + + if (preoldend >= 0) { + dumphunk(); + } +} + +void dumphunk() +{ + int i; + char *line; + int len; + + for (i = preoldmax + 1; i <= preoldend; i++) { + line = getold(i); + if (!line) { + preoldend = i - 1; + break; + } + len = strlen(line) + 2; + if (oldsize + len + 1 >= oldalloc) { + oldalloc *= 2; + oldhunk = (char *) xrealloc(oldhunk, oldalloc); + } + strcpy(oldhunk + oldsize, " "); + strcpy(oldhunk + oldsize + 2, line); + oldsize += len; + } + for (i = prenewmax + 1; i <= prenewend; i++) { + line = getnew(i); + if (!line) { + prenewend = i - 1; + break; + } + len = strlen(line) + 2; + if (newsize + len + 1 >= newalloc) { + newalloc *= 2; + newhunk = (char *) xrealloc(newhunk, newalloc); + } + strcpy(newhunk + newsize, " "); + strcpy(newhunk + newsize + 2, line); + newsize += len; + } + fputs("***************\n", stdout); + if (preoldbeg >= preoldend) { + printf("*** %d ****\n", preoldend); + } else { + printf("*** %d,%d ****\n", preoldbeg, preoldend); + } + if (oldwanted) { + fputs(oldhunk, stdout); + } + oldsize = 0; + *oldhunk = '\0'; + if (prenewbeg >= prenewend) { + printf("--- %d ----\n", prenewend); + } else { + printf("--- %d,%d ----\n", prenewbeg, prenewend); + } + if (newwanted) { + fputs(newhunk, stdout); + } + newsize = 0; + *newhunk = '\0'; +} + +char *getold(targ) +int targ; +{ + while (fgets(buff, sizeof buff, oldfp) != Nullch) { + oldline++; + if (oldline == targ) return buff; + } + return Nullch; +} + +char *getnew(targ) +int targ; +{ + while (fgets(buff, sizeof buff, newfp) != Nullch) { + newline++; + if (newline == targ) return buff; + } + return Nullch; +} + + +/* Isdir() checks, if <path> is the name of a directory. a return value + * is 0, <path> is a normal file. otherwise the <path> is a directory. + */ +int isdir(path) +char *path; +{ + struct stat buf; + stat(path, &buf); + if (buf.st_mode & S_IFDIR) { /* path is a directory */ + return(~0); + } else { + return(0); + } +} + + + +/* This is the "main" function if a diff of two directories has to be + * done. diff_recursive() expects the names of the two directories to + * be compared. */ +void diff_recursive(dir1, dir2) +char *dir1, *dir2; +{ + FILE *ls1, *ls2; + char file1[PATH_MAX], file2[PATH_MAX]; + char jointfile1[PATH_MAX], jointfile2[PATH_MAX]; + char command[PATH_MAX]; + int difference, eof1, eof2; + + sprintf(command, "ls %s", dir1); + ls1 = popen(command, "r"); + sprintf(command, "ls %s", dir2); + ls2 = popen(command, "r"); + + if ((ls1 == NULL) || (ls2 == NULL)) + fatal_error("cannot execute ls!", ""); + + file1[0] = '\0'; + eof1 = fscanf(ls1, "%s\n", file1); + file2[0] = '\0'; + eof2 = fscanf(ls2, "%s\n", file2); + + while ((file1[0] != '\0') && (file2[0] != '\0')) { + difference = strcmp(file1, file2); + while (difference != 0) { + if (difference < 0) { + printf("Only in %s: %s\n", dir1, file1); + file1[0] = '\0'; + eof1 = fscanf(ls1, "%s\n", file1); + if (file1[0] == '\0') break; + } else { + printf("Only in %s: %s\n", dir2, file2); + file2[0] = '\0'; + eof2 = fscanf(ls2, "%s\n", file2); + if (file2[0] == '\0') break; + } + difference = strcmp(file1, file2); + } + if (eof1 != EOF && eof2 != EOF) { + strcpy(jointfile1, dir1); + strcat(jointfile1, "/"); + strcat(jointfile1, file1); + strcpy(jointfile2, dir2); + strcat(jointfile2, "/"); + strcat(jointfile2, file2); + + if ((isdir(jointfile1) != 0) && (isdir(jointfile2) != 0)) { + printf("Common subdirectories: %s and %s\n", + jointfile1, jointfile2); + diff_recursive(jointfile1, jointfile2); + } else { + firstoutput = 1; + strcpy(oldfile, jointfile1); + strcpy(newfile, jointfile2); + diff(jointfile1, jointfile2); + } + file1[0] = '\0'; + eof1 = fscanf(ls1, "%s\n", file1); + file2[0] = '\0'; + eof2 = fscanf(ls2, "%s\n", file2); + } + } + + if (file1[0] != '\0') { /* first arg still has files */ + do { + printf("Only in %s: %s\n", dir1, file1); + eof1 = fscanf(ls1, " %s\n", file1); + } while (eof1 != EOF); + } + if (file2[0] != '\0') { + do { + printf("Only in %s: %s\n", dir2, file2); + eof2 = fscanf(ls2, " %s\n", file2); + } while (eof2 != EOF); + } + if (pclose(ls1) != 0) severe_error = 1; + if (pclose(ls2) != 0) severe_error = 1; +} + + +/* File_type_error is called, if in a recursive diff ( -r) one of the two + * files a block special, a character special or a FIFO special file is. + * The corresponding error message is printed here. */ +void file_type_error(filename1, filename2, statbuf1, statbuf2) +char *filename1, *filename2; +struct stat *statbuf1, *statbuf2; +{ + char type1[25], type2[25]; + + switch (statbuf1->st_mode & S_IFMT) { /* select only file mode */ + case S_IFREG: + sprintf(type1, "regular file "); + break; + case S_IFBLK: + sprintf(type1, "block special file "); + break; + case S_IFDIR: sprintf(type1, "directory "); break; + case S_IFCHR: + sprintf(type1, "character special file "); + break; + case S_IFIFO: + sprintf(type1, "FIFO special file "); + break; + } + + switch (statbuf2->st_mode & S_IFMT) { /* select only file mode */ + case S_IFREG: + sprintf(type2, "regular file "); + break; + case S_IFBLK: + sprintf(type2, "block special file "); + break; + case S_IFDIR: sprintf(type2, "directory "); break; + case S_IFCHR: + sprintf(type2, "character special file "); + break; + case S_IFIFO: + sprintf(type2, "FIFO special file "); + break; + } + printf("File %s is a %s while file %s is a %s\n", + filename1, type1, filename2, type2); +} + +void *xmalloc(size) +size_t size; +{ + void *ptr; + + ptr = malloc(size); + if (ptr == NULL) { + fprintf(stderr, "%s: out of memory\n", progname); + exit(2); + } + return(ptr); +} + +void *xrealloc(ptr, size) +void *ptr; +size_t size; +{ + ptr = realloc(ptr, size); + if (ptr == NULL) { + fprintf(stderr, "%s: out of memory\n", progname); + exit(2); + } + return(ptr); +} diff --git a/commands/simple/dirname.c b/commands/simple/dirname.c new file mode 100755 index 000000000..32d77c8c5 --- /dev/null +++ b/commands/simple/dirname.c @@ -0,0 +1,42 @@ +/* dirname - extract the directory name from a path Author: Peter Holzer */ + +/* Dirname -- extract directory part from a path name + * + * Peter Holzer (hp@vmars.tuwien.ac.at) + * + * $Log$ + * Revision 1.1 2005/04/21 14:55:21 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:30 beng + * Initial import of minix 2.0.4 + * + * Revision 1.1 1994/02/12 16:15:02 hjp + * Initial revision + * + */ + +#include <string.h> +#include <stdio.h> + +int main(int argc, char **argv) +{ + char *p; + char *path; + + if (argc != 2) { + fprintf(stderr, "Usage: %s path\n", argv[0]); + return(1); + } + path = argv[1]; + p = path + strlen(path); + while (p > path && p[-1] == '/') p--; /* trailing slashes */ + while (p > path && p[-1] != '/') p--; /* last component */ + while (p > path && p[-1] == '/') p--; /* trailing slashes */ + if (p == path) { + printf(path[0] == '/' ? "/\n" : ".\n"); + } else { + printf("%.*s\n", (int) (p - path), path); + } + return(0); +} diff --git a/commands/simple/du.c b/commands/simple/du.c new file mode 100755 index 000000000..2803740d5 --- /dev/null +++ b/commands/simple/du.c @@ -0,0 +1,254 @@ +/* du - report on disk usage Author: Alistair G. Crooks */ + +/* + * du.c 1.1 27/5/87 agc Joypace Ltd. + * 1.2 24 Mar 89 nick@nswitgould.oz + * 1.3 31 Mar 89 nick@nswitgould.oz + * 1.4 22 Feb 90 meulenbr@cst.prl.philips.nl + * 1.5 09 Jul 91 hp@vmars.tuwien.ac.at + * 1.6 01 Oct 92 kjb@cs.vu.nl + * 1.7 04 Jan 93 bde + * 1.8 19 Sep 94 kjb + * 1.9 28 Oct 99 kjb + * + * Copyright 1987, Joypace Ltd., London UK. All rights reserved. + * This code may be freely distributed, provided that this notice + * remains attached. + * + * du - a public domain interpretation of du(1). + * + * 1.2: Fixed bug involving 14 character long filenames + * 1.3: Add [-l levels] option to restrict printing. + * 1.4: Added processing of multiple arguments + * 1.5: Fixed processing of multiple arguments. General cleanup. + * 1.6: Use readdir + * 1.7: Merged 1.5 and 1.6. + * Print totals even for non-dirs at top level. + * Count blocks for each dir before printing total for the dir. + * Count blocks for all non-special files. + * Don't clutter link buffer with directories. + * 1.8: Remember all links. + * 1.9: Added -x flag to not cross device boundaries. Type fixes. + */ + + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/statfs.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> +#include <dirent.h> +#include <minix/config.h> +#include <minix/const.h> + +extern char *optarg; +extern int optind; + +#define LINELEN 256 +#define NR_ALREADY 512 + +#ifdef S_IFLNK +#define LSTAT lstat +#else +#define LSTAT stat +#endif + +typedef struct already { + struct already *al_next; + int al_dev; + ino_t al_inum; + nlink_t al_nlink; +} ALREADY; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(int makedname, (char *d, char *f, char *out, int outlen)); +_PROTOTYPE(int done, (Dev_t dev, Ino_t inum, Nlink_t nlink)); +_PROTOTYPE(long dodir, (char *d, int thislev, Dev_t dev)); + +char *prog; /* program name */ +char *optstr = "asxdl:"; /* options */ +int silent = 0; /* silent mode */ +int all = 0; /* all directory entries mode */ +int crosschk = 0; /* do not cross device boundaries mode */ +char *startdir = "."; /* starting from here */ +int levels = 20000; /* # of directory levels to print */ +ALREADY *already[NR_ALREADY]; +int alc; + + +/* + * makedname - make the pathname from the directory name, and the + * directory entry, placing it in out. If this would overflow, + * return 0, otherwise 1. + */ +int makedname(d, f, out, outlen) +char *d; +char *f; +char *out; +int outlen; +{ + char *cp; + int length; + + length = strlen(f); + if (strlen(d) + length + 2 > outlen) return(0); + for (cp = out; *d; *cp++ = *d++); + if (*(cp - 1) != '/') *cp++ = '/'; + while (length--) *cp++ = *f++; + *cp = '\0'; + return(1); +} + +/* + * done - have we encountered (dev, inum) before? Returns 1 for yes, + * 0 for no, and remembers (dev, inum, nlink). + */ +int done(dev, inum, nlink) +dev_t dev; +ino_t inum; +nlink_t nlink; +{ + register ALREADY **pap, *ap; + + pap = &already[(unsigned) inum % NR_ALREADY]; + while ((ap = *pap) != NULL) { + if (ap->al_inum == inum && ap->al_dev == dev) { + if (--ap->al_nlink == 0) { + *pap = ap->al_next; + free(ap); + } + return(1); + } + pap = &ap->al_next; + } + if ((ap = malloc(sizeof(*ap))) == NULL) { + fprintf(stderr, "du: Out of memory\n"); + exit(1); + } + ap->al_next = NULL; + ap->al_inum = inum; + ap->al_dev = dev; + ap->al_nlink = nlink - 1; + *pap = ap; + return(0); +} + +int get_block_size(char *dir, struct stat *st) +{ + struct statfs stfs; + static int fs_block_size = -1, fs_dev = -1; + int d; + + if(st->st_dev == fs_dev) + return fs_block_size; + + if((d = open(dir, O_RDONLY)) < 0) { + perror(dir); + return 0; + } + + if(fstatfs(d, &stfs) < 0) { + perror(dir); + return 0; + } + + fs_block_size = stfs.f_bsize; + fs_dev = st->st_dev; + + return fs_block_size; +} + + +/* + * dodir - process the directory d. Return the long size (in blocks) + * of d and its descendants. + */ +long dodir(d, thislev, dev) +char *d; +int thislev; +dev_t dev; +{ + int maybe_print; + struct stat s; + long total_kb; + char dent[LINELEN]; + DIR *dp; + struct dirent *entry; + int block_size; + + if (LSTAT(d, &s) < 0) { + fprintf(stderr, + "%s: %s: %s\n", prog, d, strerror(errno)); + return 0L; + } + if (s.st_dev != dev && dev != 0 && crosschk) return 0; + block_size = get_block_size(d, &s); + if(block_size < 1) { + fprintf(stderr, + "%s: %s: funny block size found (%d)\n", + prog, d, block_size); + return 0L; + } + total_kb = ((s.st_size + (block_size - 1)) / block_size) * block_size / 1024; + switch (s.st_mode & S_IFMT) { + case S_IFDIR: + /* Directories should not be linked except to "." and "..", so this + * directory should not already have been done. + */ + maybe_print = !silent; + if ((dp = opendir(d)) == NULL) break; + while ((entry = readdir(dp)) != NULL) { + if (strcmp(entry->d_name, ".") == 0 || + strcmp(entry->d_name, "..") == 0) + continue; + if (!makedname(d, entry->d_name, dent, sizeof(dent))) continue; + total_kb += dodir(dent, thislev - 1, s.st_dev); + } + closedir(dp); + break; + case S_IFBLK: + case S_IFCHR: + /* st_size for special files is not related to blocks used. */ + total_kb = 0; + /* Fall through. */ + default: + if (s.st_nlink > 1 && done(s.st_dev, s.st_ino, s.st_nlink)) return 0L; + maybe_print = all; + break; + } + if (thislev >= levels || (maybe_print && thislev >= 0)) { + printf("%ld\t%s\n", total_kb, d); + } + return(total_kb); +} + +int main(argc, argv) +int argc; +char **argv; +{ + int c; + + prog = argv[0]; + while ((c = getopt(argc, argv, optstr)) != EOF) switch (c) { + case 'a': all = 1; break; + case 's': silent = 1; break; + case 'x': + case 'd': crosschk = 1; break; + case 'l': levels = atoi(optarg); break; + default: + fprintf(stderr, + "Usage: %s [-asx] [-l levels] [startdir]\n", prog); + exit(1); + } + do { + if (optind < argc) startdir = argv[optind++]; + alc = 0; + (void) dodir(startdir, levels, 0); + } while (optind < argc); + return(0); +} diff --git a/commands/simple/ed.c b/commands/simple/ed.c new file mode 100755 index 000000000..cd58e614e --- /dev/null +++ b/commands/simple/ed.c @@ -0,0 +1,2198 @@ +/* Copyright 1987 Brian Beattie Rights Reserved. + * + * Permission to copy and/or distribute granted under the + * following conditions: + * + * 1). No charge may be made other than resonable charges + * for reproduction. + * + * 2). This notice must remain intact. + * + * 3). No further restrictions may be added. + * + */ + +/* This program used to be in many little pieces, with this makefile: +.SUFFIXES: .c .s + +CFLAGS = -F + +OBJS = append.s catsub.s ckglob.s deflt.s del.s docmd.s doglob.s\ + doprnt.s doread.s dowrite.s ed.s egets.s find.s getfn.s getlst.s\ + getnum.s getone.s getptr.s getrhs.s gettxt.s ins.s join.s maksub.s\ + move.s optpat.s set.s setbuf.s subst.s getpat.s matchs.s amatch.s\ + unmkpat.s omatch.s makepat.s bitmap.s dodash.s esc.s System.s + +ed: $(OBJS) + cc -T. -i -o ed $(OBJS) +*/ + +#include <sys/types.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/wait.h> +#include <stdio.h> + +/****************************/ + +/* tools.h */ +/* + * #defines for non-printing ASCII characters + */ + +#define NUL 0x00 /* ^@ */ +#define EOS 0x00 /* end of string */ +#define SOH 0x01 /* ^A */ +#define STX 0x02 /* ^B */ +#define ETX 0x03 /* ^C */ +#define EOT 0x04 /* ^D */ +#define ENQ 0x05 /* ^E */ +#define ACK 0x06 /* ^F */ +#define BEL 0x07 /* ^G */ +#define BS 0x08 /* ^H */ +#define HT 0x09 /* ^I */ +#define LF 0x0a /* ^J */ +#define NL '\n' +#define VT 0x0b /* ^K */ +#define FF 0x0c /* ^L */ +#define CR 0x0d /* ^M */ +#define SO 0x0e /* ^N */ +#define SI 0x0f /* ^O */ +#define DLE 0x10 /* ^P */ +#define DC1 0x11 /* ^Q */ +#define DC2 0x12 /* ^R */ +#define DC3 0x13 /* ^S */ +#define DC4 0x14 /* ^T */ +#define NAK 0x15 /* ^U */ +#define SYN 0x16 /* ^V */ +#define ETB 0x17 /* ^W */ +#define CAN 0x18 /* ^X */ +#define EM 0x19 /* ^Y */ +#define SUB 0x1a /* ^Z */ +#define ESC 0x1b /* ^[ */ +#define FS 0x1c /* ^\ */ +#define GS 0x1d /* ^] */ +#define RS 0x1e /* ^^ */ +#define US 0x1f /* ^_ */ +#define SP 0x20 /* space */ +#define DEL 0x7f /* DEL */ + + +#define TRUE 1 +#define FALSE 0 +#define ERR -2 + + +/* Definitions of meta-characters used in pattern matching + * routines. LITCHAR & NCCL are only used as token identifiers; + * all the others are also both token identifier and actual symbol + * used in the regular expression. + */ + + +#define BOL '^' +#define EOL '$' +#define ANY '.' +#define LITCHAR 'L' +#define ESCAPE '\\' +#define CCL '[' /* Character class: [...] */ +#define CCLEND ']' +#define NEGATE '^' +#define NCCL '!' /* Negative character class [^...] */ +#define CLOSURE '*' +#define OR_SYM '|' +#define DITTO '&' +#define OPEN '(' +#define CLOSE ')' + +/* Largest permitted size for an expanded character class. (i.e. the class + * [a-z] will expand into 26 symbols; [a-z0-9] will expand into 36.) + */ +#define CLS_SIZE 128 + +/* + * Tokens are used to hold pattern templates. (see makepat()) + */ +typedef char BITMAP; + +typedef struct token { + char tok; + char lchar; + BITMAP *bitmap; + struct token *next; +} TOKEN; + +#define TOKSIZE sizeof (TOKEN) + +/* + * An absolute maximun for strings. + */ + +#define MAXSTR 132 /* Maximum numbers of characters in a line */ + + +/* Macros */ +#define max(a,b) ((a>b)?a:b) +#define min(a,b) ((a<b)?a:b) +#define toupper(c) (c>='a'&&c<='z'?c-32:c) + +/* ed.h */ +#define FATAL (ERR-1) +struct line { + int l_stat; /* empty, mark */ + struct line *l_prev; + struct line *l_next; + char l_buff[1]; +}; + +typedef struct line LINE; + +#define LINFREE 1 /* entry not in use */ +#define LGLOB 2 /* line marked global */ + + /* max number of chars per line */ +#define MAXLINE (sizeof(int) == 2 ? 256 : 8192) +#define MAXPAT 256 /* max number of chars per replacement + * pattern */ + /* max file name size */ +#define MAXFNAME (sizeof(int) == 2 ? 256 : 1024) + +extern LINE line0; +extern int curln, lastln, line1, line2, nlines; +extern int nflg; /* print line number flag */ +extern int lflg; /* print line in verbose mode */ +extern char *inptr; /* tty input buffer */ +extern char linbuf[], *linptr; /* current line */ +extern int truncflg; /* truncate long line flag */ +extern int eightbit; /* save eighth bit */ +extern int nonascii; /* count of non-ascii chars read */ +extern int nullchar; /* count of null chars read */ +extern int truncated; /* count of lines truncated */ +extern int fchanged; /* file changed */ + +#define nextln(l) ((l)+1 > lastln ? 0 : (l)+1) +#define prevln(l) ((l)-1 < 0 ? lastln : (l)-1) + +/* amatch.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(static char *match, (char *lin, TOKEN *pat, char *boln)); +_PROTOTYPE(char *amatch, (char *lin, TOKEN *pat, char *boln)); +_PROTOTYPE(int append, (int line, int glob)); +_PROTOTYPE(BITMAP *makebitmap, (unsigned size)); +_PROTOTYPE(int setbit, (unsigned c, char *map, unsigned val)); +_PROTOTYPE(int testbit, (unsigned c, char *map)); +_PROTOTYPE(char *catsub, (char *from, char *to, char *sub, char *new, char *newend)); +_PROTOTYPE(int ckglob, (void)); +_PROTOTYPE(int deflt, (int def1, int def2)); +_PROTOTYPE(int del, (int from, int to)); +_PROTOTYPE(int docmd, (int glob)); +_PROTOTYPE(int dolst, (int line1, int line2)); +_PROTOTYPE(char *dodash, (int delim, char *src, char *map)); +_PROTOTYPE(int doglob, (void)); +_PROTOTYPE(int doprnt, (int from, int to)); +_PROTOTYPE(void prntln, (char *str, int vflg, int lin)); +_PROTOTYPE(void putcntl, (int c, FILE *stream)); +_PROTOTYPE(int doread, (int lin, char *fname)); +_PROTOTYPE(int dowrite, (int from, int to, char *fname, int apflg)); +_PROTOTYPE(void intr, (int sig)); +_PROTOTYPE(int egets, (char *str, int size, FILE *stream)); +_PROTOTYPE(int esc, (char **s)); +_PROTOTYPE(int find, (TOKEN *pat, int dir)); +_PROTOTYPE(char *getfn, (void)); +_PROTOTYPE(int getlst, (void)); +_PROTOTYPE(int getnum, (int first)); +_PROTOTYPE(int getone, (void)); +_PROTOTYPE(TOKEN *getpat, (char *arg)); +_PROTOTYPE(LINE *getptr, (int num)); +_PROTOTYPE(int getrhs, (char *sub)); +_PROTOTYPE(char *gettxt, (int num)); +_PROTOTYPE(int ins, (char *str)); +_PROTOTYPE(int System, (char *c)); +_PROTOTYPE(int join, (int first, int last)); +_PROTOTYPE(TOKEN *makepat, (char *arg, int delim)); +_PROTOTYPE(char *maksub, (char *sub, int subsz)); +_PROTOTYPE(char *matchs, (char *line, TOKEN *pat, int ret_endp)); +_PROTOTYPE(int move, (int num)); +_PROTOTYPE(int transfer, (int num)); +_PROTOTYPE(int omatch, (char **linp, TOKEN *pat, char *boln)); +_PROTOTYPE(TOKEN *optpat, (void)); +_PROTOTYPE(int set, (void)); +_PROTOTYPE(int show, (void)); +_PROTOTYPE(void relink, (LINE *a, LINE *x, LINE *y, LINE *b)); +_PROTOTYPE(void clrbuf, (void)); +_PROTOTYPE(void set_buf, (void)); +_PROTOTYPE(int subst, (TOKEN *pat, char *sub, int gflg, int pflag)); +_PROTOTYPE(void unmakepat, (TOKEN *head)); + +/* Scans throught the pattern template looking for a match + * with lin. Each element of lin is compared with the template + * until either a mis-match is found or the end of the template + * is reached. In the former case a 0 is returned; in the latter, + * a pointer into lin (pointing to the character following the + * matched pattern) is returned. + * + * "lin" is a pointer to the line being searched. + * "pat" is a pointer to a template made by makepat(). + * "boln" is a pointer into "lin" which points at the + * character at the beginning of the line. + */ + +char *paropen[9], *parclose[9]; +int between, parnum; + +char *amatch(lin, pat, boln) +char *lin; +TOKEN *pat; +char *boln; +{ + between = 0; + parnum = 0; + + lin = match(lin, pat, boln); + + if (between) return 0; + + while (parnum < 9) { + paropen[parnum] = parclose[parnum] = ""; + parnum++; + } + return lin; +} + +static char *match(lin, pat, boln) +char *lin; +TOKEN *pat; +char *boln; +{ + register char *bocl, *rval, *strstart; + + if (pat == 0) return 0; + + strstart = lin; + + while (pat) { + if (pat->tok == CLOSURE && pat->next) { + /* Process a closure: first skip over the closure + * token to the object to be repeated. This object + * can be a character class. */ + + pat = pat->next; + + /* Now match as many occurrences of the closure + * pattern as possible. */ + bocl = lin; + + while (*lin && omatch(&lin, pat, boln)); + + /* 'Lin' now points to the character that made made + * us fail. Now go on to process the rest of the + * string. A problem here is a character following + * the closure which could have been in the closure. + * For example, in the pattern "[a-z]*t" (which + * matches any lower-case word ending in a t), the + * final 't' will be sucked up in the while loop. + * So, if the match fails, we back up a notch and try + * to match the rest of the string again, repeating + * this process recursively until we get back to the + * beginning of the closure. The recursion goes, at + * most two levels deep. */ + + if (pat = pat->next) { + int savbtwn = between; + int savprnm = parnum; + + while (bocl <= lin) { + if (rval = match(lin, pat, boln)) { + /* Success */ + return(rval); + } else { + --lin; + between = savbtwn; + parnum = savprnm; + } + } + return(0); /* match failed */ + } + } else if (pat->tok == OPEN) { + if (between || parnum >= 9) return 0; + paropen[parnum] = lin; + between = 1; + pat = pat->next; + } else if (pat->tok == CLOSE) { + if (!between) return 0; + parclose[parnum++] = lin; + between = 0; + pat = pat->next; + } else if (omatch(&lin, pat, boln)) { + pat = pat->next; + } else { + return(0); + } + } + + /* Note that omatch() advances lin to point at the next character to + * be matched. Consequently, when we reach the end of the template, + * lin will be pointing at the character following the last character + * matched. The exceptions are templates containing only a BOLN or + * EOLN token. In these cases omatch doesn't advance. + * + * A philosophical point should be mentioned here. Is $ a position or a + * character? (i.e. does $ mean the EOL character itself or does it + * mean the character at the end of the line.) I decided here to + * make it mean the former, in order to make the behavior of match() + * consistent. If you give match the pattern ^$ (match all lines + * consisting only of an end of line) then, since something has to be + * returned, a pointer to the end of line character itself is + * returned. */ + + return((char *) max(strstart, lin)); +} + +/* append.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int append(line, glob) +int line, glob; +{ + int stat; + char lin[MAXLINE]; + + if (glob) return(ERR); + curln = line; + while (1) { + if (nflg) printf("%6d. ", curln + 1); + + if (fgets(lin, MAXLINE, stdin) == NULL) return(EOF); + if (lin[0] == '.' && lin[1] == '\n') return (0); + stat = ins(lin); + if (stat < 0) return(ERR); + + } +} + +/* bitmap.c */ +/* + * BITMAP.C - makebitmap, setbit, testbit + * bit-map manipulation routines. + * + * Copyright (c) Allen I. Holub, all rights reserved. This program may + * for copied for personal, non-profit use only. + * + */ + +#ifdef DEBUG +/* #include <stdio.h> */ +#endif + +/* #include "tools.h" */ + + +BITMAP *makebitmap(size) +unsigned size; +{ + /* Make a bit map with "size" bits. The first entry in the map is an + * "unsigned int" representing the maximum bit. The map itself is + * concatenated to this integer. Return a pointer to a map on + * success, 0 if there's not enough memory. */ + + unsigned *map, numbytes; + + numbytes = (size >> 3) + ((size & 0x07) ? 1 : 0); + +#ifdef DEBUG + printf("Making a %d bit map (%d bytes required)\n", size, numbytes); +#endif + + if (map = (unsigned *) malloc(numbytes + sizeof(unsigned))) { + *map = size; + memset(map + 1, 0, numbytes); + } + + return((BITMAP *) map); +} + +int setbit(c, map, val) +unsigned c, val; +char *map; +{ + /* Set bit c in the map to val. If c > map-size, 0 is returned, else + * 1 is returned. */ + + if (c >= *(unsigned *) map) /* if c >= map size */ + return 0; + + map += sizeof(unsigned); /* skip past size */ + + if (val) + map[c >> 3] |= 1 << (c & 0x07); + else + map[c >> 3] &= ~(1 << (c & 0x07)); + + return 1; +} + +int testbit(c, map) +unsigned c; +char *map; +{ + /* Return 1 if the bit corresponding to c in map is set. 0 if it is not. */ + + if (c >= *(unsigned *) map) return 0; + + map += sizeof(unsigned); + + return(map[c >> 3] & (1 << (c & 0x07))); +} + +/* catsub.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +extern char *paropen[9], *parclose[9]; + +char *catsub(from, to, sub, new, newend) +char *from, *to, *sub, *new, *newend; +{ + char *cp, *cp2; + + for (cp = new; *sub != EOS && cp < newend;) { + if (*sub == DITTO) for (cp2 = from; cp2 < to;) { + *cp++ = *cp2++; + if (cp >= newend) break; + } + else if (*sub == ESCAPE) { + sub++; + if ('1' <= *sub && *sub <= '9') { + char *parcl = parclose[*sub - '1']; + + for (cp2 = paropen[*sub - '1']; cp2 < parcl;) { + *cp++ = *cp2++; + if (cp >= newend) break; + } + } else + *cp++ = *sub; + } else + *cp++ = *sub; + + sub++; + } + + return(cp); +} + +/* ckglob.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int ckglob() +{ + TOKEN *glbpat; + char c, delim; + char lin[MAXLINE]; + int num; + LINE *ptr; + + c = *inptr; + + if (c != 'g' && c != 'v') return(0); + + if (deflt(1, lastln) < 0) return(ERR); + + delim = *++inptr; + if (delim <= ' ') return(ERR); + + glbpat = optpat(); + + if (*inptr == delim) inptr++; + + ptr = getptr(1); + for (num = 1; num <= lastln; num++) { + ptr->l_stat &= ~LGLOB; + if (line1 <= num && num <= line2) { + strcpy(lin, ptr->l_buff); + strcat(lin, "\n"); + if (matchs(lin, glbpat, 0)) { + if (c == 'g') ptr->l_stat |= LGLOB; + } else { + if (c == 'v') ptr->l_stat |= LGLOB; + } + } + ptr = ptr->l_next; + } + return(1); +} + +/* deflt.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int deflt(def1, def2) +int def1, def2; +{ + if (nlines == 0) { + line1 = def1; + line2 = def2; + } + if (line1 > line2 || line1 <= 0) return(ERR); + return(0); +} + +/* del.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int del(from, to) +int from, to; +{ + LINE *first, *last, *next, *tmp; + + if (from < 1) from = 1; + first = getptr(prevln(from)); + last = getptr(nextln(to)); + next = first->l_next; + while (next != last && next != &line0) { + tmp = next->l_next; + free((char *) next); + next = tmp; + } + relink(first, last, first, last); + lastln -= (to - from) + 1; + curln = prevln(from); + return(0); +} + +/* docmd.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +char fname[MAXFNAME]; +int fchanged; +extern int nofname; + +extern int mark[]; + +int docmd(glob) +int glob; +{ + static char rhs[MAXPAT]; + TOKEN *subpat; + int c, err, line3; + int apflg, pflag, gflag; + int nchng; + char *fptr; + + pflag = FALSE; + while (*inptr == SP && *inptr == HT) inptr++; + + c = *inptr++; + + switch (c) { + case NL: + if (nlines == 0) { + if ((line2 = nextln(curln)) == 0) return(ERR); + } + curln = line2; + return(1); + break; + + case '=': printf("%d\n", line2); break; + + case 'a': + if (*inptr != NL || nlines > 1) return(ERR); + + if (append(line1, glob) < 0) return(ERR);; + fchanged = TRUE; + break; + + case 'c': + if (*inptr != NL) return(ERR); + + if (deflt(curln, curln) < 0) return(ERR); + + if (del(line1, line2) < 0) return(ERR); + if (append(curln, glob) < 0) return (ERR); + fchanged = TRUE; + break; + + case 'd': + if (*inptr != NL) return(ERR); + + if (deflt(curln, curln) < 0) return(ERR); + + if (del(line1, line2) < 0) return(ERR); + if (nextln(curln) != 0) curln = nextln(curln); + fchanged = TRUE; + break; + + case 'e': + if (nlines > 0) return(ERR); + if (fchanged) { + fchanged = FALSE; + return(ERR); + } + + /* FALL THROUGH */ + case 'E': + if (nlines > 0) return(ERR); + + if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR); + + if ((fptr = getfn()) == NULL) return(ERR); + + clrbuf(); + if ((err = doread(0, fptr)) < 0) return(err); + + strcpy(fname, fptr); + fchanged = FALSE; + break; + + case 'f': + if (nlines > 0) return(ERR); + + if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR); + + if ((fptr = getfn()) == NULL) return(ERR); + + if (nofname) + printf("%s\n", fname); + else + strcpy(fname, fptr); + break; + + case 'i': + if (*inptr != NL || nlines > 1) return(ERR); + + if (append(prevln(line1), glob) < 0) return(ERR); + fchanged = TRUE; + break; + + case 'j': + if (*inptr != NL || deflt(curln, curln + 1) < 0) return(ERR); + + if (join(line1, line2) < 0) return(ERR); + break; + + case 'k': + while (*inptr == ' ' || *inptr == HT) inptr++; + + if (*inptr < 'a' || *inptr > 'z') return ERR; + c = *inptr++; + + if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR); + + mark[c - 'a'] = line1; + break; + + case 'l': + if (*inptr != NL) return(ERR); + if (deflt(curln, curln) < 0) return (ERR); + if (dolst(line1, line2) < 0) return (ERR); + break; + + case 'm': + if ((line3 = getone()) < 0) return(ERR); + if (deflt(curln, curln) < 0) return (ERR); + if (move(line3) < 0) return (ERR); + fchanged = TRUE; + break; + + case 'P': + case 'p': + if (*inptr != NL) return(ERR); + if (deflt(curln, curln) < 0) return (ERR); + if (doprnt(line1, line2) < 0) return (ERR); + break; + + case 'q': + if (fchanged) { + fchanged = FALSE; + return(ERR); + } + + /* FALL THROUGH */ + case 'Q': + if (*inptr == NL && nlines == 0 && !glob) + return(EOF); + else + return(ERR); + + case 'r': + if (nlines > 1) return(ERR); + + if (nlines == 0) line2 = lastln; + + if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR); + + if ((fptr = getfn()) == NULL) return(ERR); + + if ((err = doread(line2, fptr)) < 0) return(err); + fchanged = TRUE; + break; + + case 's': + if (*inptr == 'e') return(set()); + while (*inptr == SP || *inptr == HT) inptr++; + if ((subpat = optpat()) == NULL) return (ERR); + if ((gflag = getrhs(rhs)) < 0) return (ERR); + if (*inptr == 'p') pflag++; + if (deflt(curln, curln) < 0) return (ERR); + if ((nchng = subst(subpat, rhs, gflag, pflag)) < 0) return (ERR); + if (nchng) fchanged = TRUE; + break; + + case 't': + if ((line3 = getone()) < 0) return(ERR); + if (deflt(curln, curln) < 0) return (ERR); + if (transfer(line3) < 0) return (ERR); + fchanged = TRUE; + break; + + case 'W': + case 'w': + apflg = (c == 'W'); + + if (*inptr != ' ' && *inptr != HT && *inptr != NL) return(ERR); + + if ((fptr = getfn()) == NULL) return(ERR); + + if (deflt(1, lastln) < 0) return(ERR); + if (dowrite(line1, line2, fptr, apflg) < 0) return (ERR); + fchanged = FALSE; + break; + + case 'x': + if (*inptr == NL && nlines == 0 && !glob) { + if ((fptr = getfn()) == NULL) return(ERR); + if (dowrite(1, lastln, fptr, 0) >= 0) return (EOF); + } + return(ERR); + + case 'z': + if (deflt(curln, curln) < 0) return(ERR); + + switch (*inptr) { + case '-': + if (doprnt(line1 - 21, line1) < 0) return(ERR); + break; + + case '.': + if (doprnt(line1 - 11, line1 + 10) < 0) return(ERR); + break; + + case '+': + case '\n': + if (doprnt(line1, line1 + 21) < 0) return(ERR); + break; + } + break; + + default: return(ERR); +} + return(0); +} + +int dolst(line1, line2) +int line1, line2; +{ + int oldlflg = lflg, p; + + lflg = 1; + p = doprnt(line1, line2); + lflg = oldlflg; + + return p; +} + +/* dodash.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ + +/* Expand the set pointed to by *src into dest. + * Stop at delim. Return 0 on error or size of + * character class on success. Update *src to + * point at delim. A set can have one element + * {x} or several elements ( {abcdefghijklmnopqrstuvwxyz} + * and {a-z} are equivalent ). Note that the dash + * notation is expanded as sequential numbers. + * This means (since we are using the ASCII character + * set) that a-Z will contain the entire alphabet + * plus the symbols: [\]^_`. The maximum number of + * characters in a character class is defined by maxccl. + */ +char *dodash(delim, src, map) +int delim; +char *src, *map; +{ + + register int first, last; + char *start; + + start = src; + + while (*src && *src != delim) { + if (*src != '-') setbit(esc(&src), map, 1); + + else if (src == start || *(src + 1) == delim) + setbit('-', map, 1); + else { + src++; + + if (*src < *(src - 2)) { + first = *src; + last = *(src - 2); + } else { + first = *(src - 2); + last = *src; + } + + while (++first <= last) setbit(first, map, 1); + + } + src++; + } + return(src); +} + +/* doglob.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int doglob() +{ + int lin, stat; + char *cmd; + LINE *ptr; + + cmd = inptr; + + while (1) { + ptr = getptr(1); + for (lin = 1; lin <= lastln; lin++) { + if (ptr->l_stat & LGLOB) break; + ptr = ptr->l_next; + } + if (lin > lastln) break; + + ptr->l_stat &= ~LGLOB; + curln = lin; + inptr = cmd; + if ((stat = getlst()) < 0) return(stat); + if ((stat = docmd(1)) < 0) return (stat); + } + return(curln); +} + +/* doprnt.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int doprnt(from, to) +int from, to; +{ + int i; + LINE *lptr; + + from = from < 1 ? 1 : from; + to = to > lastln ? lastln : to; + + if (to != 0) { + lptr = getptr(from); + for (i = from; i <= to; i++) { + prntln(lptr->l_buff, lflg, (nflg ? i : 0)); + lptr = lptr->l_next; + } + curln = to; + } + return(0); +} + +void prntln(str, vflg, lin) +char *str; +int vflg, lin; +{ + if (lin) printf("%7d ", lin); + while (*str && *str != NL) { + if (*str < ' ' || *str >= 0x7f) { + switch (*str) { + case '\t': + if (vflg) + putcntl(*str, stdout); + else + putc(*str, stdout); + break; + + case DEL: + putc('^', stdout); + putc('?', stdout); + break; + + default: + putcntl(*str, stdout); + break; + } + } else + putc(*str, stdout); + str++; + } + if (vflg) putc('$', stdout); + putc('\n', stdout); +} + +void putcntl(c, stream) +char c; +FILE *stream; +{ + putc('^', stream); + putc((c & 31) | '@', stream); +} + +/* doread.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +extern int diag; + +int doread(lin, fname) +int lin; +char *fname; +{ + FILE *fp; + int err; + long bytes; + int lines; + static char str[MAXLINE]; + + err = 0; + nonascii = nullchar = truncated = 0; + + if (diag) printf("\"%s\" ", fname); + if ((fp = fopen(fname, "r")) == NULL) { + printf("file open err\n"); + return(ERR); + } + curln = lin; + for (lines = 0, bytes = 0; (err = egets(str, MAXLINE, fp)) > 0;) { + bytes += strlen(str); + if (ins(str) < 0) { + printf("file insert error\n"); + err++; + break; + } + lines++; + } + fclose(fp); + if (err < 0) return(err); + if (diag) { + printf("%d lines %ld bytes", lines, bytes); + if (nonascii) printf(" [%d non-ascii]", nonascii); + if (nullchar) printf(" [%d nul]", nullchar); + if (truncated) printf(" [%d lines truncated]", truncated); + printf("\n"); + } + return(err); +} + +/* dowrite.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int dowrite(from, to, fname, apflg) +int from, to; +char *fname; +int apflg; +{ + FILE *fp; + int lin, err; + int lines; + long bytes; + char *str; + LINE *lptr; + + err = 0; + + lines = bytes = 0; + if (diag) printf("\"%s\" ", fname); + if ((fp = fopen(fname, (apflg ? "a" : "w"))) == NULL) { + printf("file open error\n"); + return(ERR); + } + lptr = getptr(from); + for (lin = from; lin <= to; lin++) { + str = lptr->l_buff; + lines++; + bytes += strlen(str) + 1; + if (fputs(str, fp) == EOF) { + printf("file write error\n"); + err++; + break; + } + fputc('\n', fp); + lptr = lptr->l_next; + } + if (diag) printf("%d lines %ld bytes\n", lines, bytes); + fclose(fp); + return(err); +} + +/* ed.c */ +/* Copyright 1987 Brian Beattie Rights Reserved. + * + * Permission to copy and/or distribute granted under the + * following conditions: + * + * 1). No charge may be made other than resonable charges + * for reproduction. + * + * 2). This notice must remain intact. + * + * 3). No further restrictions may be added. + * + */ +/* #include <stdio.h> */ +/* #include <signal.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ +#include <setjmp.h> +jmp_buf env; + +LINE line0; +int curln = 0; +int lastln = 0; +char *inptr; +static char inlin[MAXLINE]; +int nflg, lflg; +int line1, line2, nlines; +extern char fname[]; +int version = 1; +int diag = 1; + +void intr(sig) +int sig; +{ + printf("?\n"); + longjmp(env, 1); +} + +int main(argc, argv) +int argc; +char **argv; +{ + int stat, i, doflush; + + set_buf(); + doflush = isatty(1); + + if (argc > 1 && (strcmp(argv[1], "-") == 0 || strcmp(argv[1], "-s") == 0)) { + diag = 0; + argc--; + argv++; + } + if (argc > 1) { + for (i = 1; i < argc; i++) { + if (doread(0, argv[i]) == 0) { + curln = 1; + strcpy(fname, argv[i]); + break; + } + } + } + while (1) { + setjmp(env); + if (signal(SIGINT, SIG_IGN) != SIG_IGN) signal(SIGINT, intr); + + if (doflush) fflush(stdout); + + if (fgets(inlin, sizeof(inlin), stdin) == NULL) { + break; + } + for (;;) { + inptr = strchr(inlin, EOS); + if (inptr >= inlin+2 && inptr[-2] == '\\' && inptr[-1] == NL) { + inptr[-1] = 'n'; + if (fgets(inptr, sizeof(inlin) - (inptr - inlin), + stdin) == NULL) break; + } else { + break; + } + } + if (*inlin == '!') { + if ((inptr = strchr(inlin, NL)) != NULL) *inptr = EOS; + System(inlin + 1); + continue; + } + inptr = inlin; + if (getlst() >= 0) + if ((stat = ckglob()) != 0) { + if (stat >= 0 && (stat = doglob()) >= 0) { + curln = stat; + continue; + } + } else { + if ((stat = docmd(0)) >= 0) { + if (stat == 1) doprnt(curln, curln); + continue; + } + } + if (stat == EOF) { + exit(0); + } + if (stat == FATAL) { + fputs("FATAL ERROR\n", stderr); + exit(1); + } + printf("?\n"); + } + return(0); +} + +/* egets.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int eightbit = 1; /* save eight bit */ +int nonascii, nullchar, truncated; +int egets(str, size, stream) +char *str; +int size; +FILE *stream; +{ + int c, count; + char *cp; + + for (count = 0, cp = str; size > count;) { + c = getc(stream); + if (c == EOF) { + *cp++ = '\n'; + *cp = EOS; + if (count) { + printf("[Incomplete last line]\n"); + } + return(count); + } + if (c == NL) { + *cp++ = c; + *cp = EOS; + return(++count); + } + if (c > 127) { + if (!eightbit) /* if not saving eighth bit */ + c = c & 127; /* strip eigth bit */ + nonascii++; /* count it */ + } + if (c) { + *cp++ = c; /* not null, keep it */ + count++; + } else + nullchar++; /* count nulls */ + } + str[count - 1] = EOS; + if (c != NL) { + printf("truncating line\n"); + truncated++; + while ((c = getc(stream)) != EOF) + if (c == NL) break; + } + return(count); +} + +/* esc.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ + +/* Map escape sequences into their equivalent symbols. Returns the + * correct ASCII character. If no escape prefix is present then s + * is untouched and *s is returned, otherwise **s is advanced to point + * at the escaped character and the translated character is returned. + */ +int esc(s) +char **s; +{ + register int rval; + + + if (**s != ESCAPE) { + rval = **s; + } else { + (*s)++; + + switch (toupper(**s)) { + case '\000': rval = ESCAPE; break; + case 'S': rval = ' '; break; + case 'N': rval = '\n'; break; + case 'T': rval = '\t'; break; + case 'B': rval = '\b'; break; + case 'R': rval = '\r'; break; + default: rval = **s; break; + } + } + + return(rval); +} + +/* find.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int find(pat, dir) +TOKEN *pat; +int dir; +{ + int i, num; + char lin[MAXLINE]; + LINE *ptr; + + num = curln; + ptr = getptr(curln); + num = (dir ? nextln(num) : prevln(num)); + ptr = (dir ? ptr->l_next : ptr->l_prev); + for (i = 0; i < lastln; i++) { + if (num == 0) { + num = (dir ? nextln(num) : prevln(num)); + ptr = (dir ? ptr->l_next : ptr->l_prev); + } + strcpy(lin, ptr->l_buff); + strcat(lin, "\n"); + if (matchs(lin, pat, 0)) { + return(num); + } + num = (dir ? nextln(num) : prevln(num)); + ptr = (dir ? ptr->l_next : ptr->l_prev); + } + return(ERR); +} + +/* getfn.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +extern char fname[MAXFNAME]; +int nofname; + +char *getfn() +{ + static char file[256]; + char *cp; + + if (*inptr == NL) { + nofname = TRUE; + strcpy(file, fname); + } else { + nofname = FALSE; + while (*inptr == SP || *inptr == HT) inptr++; + + cp = file; + while (*inptr && *inptr != NL && *inptr != SP && *inptr != HT) { + *cp++ = *inptr++; + } + *cp = '\0'; + + if (strlen(file) == 0) { + printf("bad file name\n"); + return(NULL); + } + } + + if (strlen(file) == 0) { + printf("no file name\n"); + return(NULL); + } + return(file); +} + +/* getlst.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int getlst() +{ + int num; + + line2 = 0; + for (nlines = 0; (num = getone()) >= 0;) { + line1 = line2; + line2 = num; + nlines++; + if (*inptr != ',' && *inptr != ';') break; + if (*inptr == ';') curln = num; + inptr++; + } + nlines = min(nlines, 2); + if (nlines == 0) line2 = curln; + if (nlines <= 1) line1 = line2; + + if (num == ERR) + return(num); + else + return(nlines); +} + +/* getnum.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int mark['z' - 'a' + 1]; + +int getnum(first) +int first; +{ + TOKEN *srchpat; + int num; + char c; + + while (*inptr == SP || *inptr == HT) inptr++; + + if (*inptr >= '0' && *inptr <= '9') { /* line number */ + for (num = 0; *inptr >= '0' && *inptr <= '9';) { + num = (num * 10) + *inptr - '0'; + inptr++; + } + return num; + } + switch (c = *inptr) { + case '.': + inptr++; + return(curln); + + case '$': + inptr++; + return(lastln); + + case '/': + case '?': + srchpat = optpat(); + if (*inptr == c) inptr++; + return(find(srchpat, c == '/' ? 1 : 0)); + + case '-': + case '+': + return(first ? curln : 1); + + case '\'': + inptr++; + if (*inptr < 'a' || *inptr > 'z') return(EOF); + + return mark[*inptr++ - 'a']; + + default: + return(first ? EOF : 1);/* unknown address */ + } +} + +/* getone.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +#define FIRST 1 +#define NOTFIRST 0 + +int getone() +{ + int c, i, num; + + if ((num = getnum(FIRST)) >= 0) { + while (1) { + while (*inptr == SP || *inptr == HT) inptr++; + + if (*inptr != '+' && *inptr != '-') break; + c = *inptr++; + + if ((i = getnum(NOTFIRST)) < 0) return(i); + + if (c == '+') { + num += i; + } else { + num -= i; + } + } + } + return(num > lastln ? ERR : num); +} + +/* getpat.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ + +/* Translate arg into a TOKEN string */ +TOKEN * + getpat(arg) +char *arg; +{ + + return(makepat(arg, '\000')); +} + +/* getptr.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +LINE * + getptr(num) +int num; +{ + LINE *ptr; + int j; + + if (2 * num > lastln && num <= lastln) { /* high line numbers */ + ptr = line0.l_prev; + for (j = lastln; j > num; j--) ptr = ptr->l_prev; + } else { /* low line numbers */ + ptr = &line0; + for (j = 0; j < num; j++) ptr = ptr->l_next; + } + return(ptr); +} + +/* getrhs.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int getrhs(sub) +char *sub; +{ + if (inptr[0] == NL || inptr[1] == NL) /* check for eol */ + return(ERR); + + if (maksub(sub, MAXPAT) == NULL) return(ERR); + + inptr++; /* skip over delimter */ + while (*inptr == SP || *inptr == HT) inptr++; + if (*inptr == 'g') { + inptr++; + return(1); + } + return(0); +} + +/* gettxt.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +char * + gettxt(num) +int num; +{ + LINE *lin; + static char txtbuf[MAXLINE]; + + lin = getptr(num); + strcpy(txtbuf, lin->l_buff); + strcat(txtbuf, "\n"); + return(txtbuf); +} + +/* ins.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int ins(str) +char *str; +{ + char buf[MAXLINE], *cp; + LINE *new, *cur, *nxt; + + cp = buf; + while (1) { + if ((*cp = *str++) == NL) *cp = EOS; + if (*cp) { + cp++; + continue; + } + if ((new = (LINE *) malloc(sizeof(LINE) + strlen(buf))) == NULL) + return(ERR); /* no memory */ + + new->l_stat = 0; + strcpy(new->l_buff, buf); /* build new line */ + cur = getptr(curln); /* get current line */ + nxt = cur->l_next; /* get next line */ + relink(cur, new, new, nxt); /* add to linked list */ + relink(new, nxt, cur, new); + lastln++; + curln++; + + if (*str == EOS) /* end of line ? */ + return(1); + + cp = buf; + } +} + +/* join.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +extern int fchanged; + +int join(first, last) +int first, last; +{ + char buf[MAXLINE]; + char *cp = buf, *str; + int num; + + if (first <= 0 || first > last || last > lastln) return(ERR); + if (first == last) { + curln = first; + return 0; + } + for (num = first; num <= last; num++) { + str = gettxt(num); + + while (*str != NL && cp < buf + MAXLINE - 1) *cp++ = *str++; + + if (cp == buf + MAXLINE - 1) { + printf("line too long\n"); + return(ERR); + } + } + *cp++ = NL; + *cp = EOS; + del(first, last); + curln = first - 1; + ins(buf); + fchanged = TRUE; + return 0; +} + +/* makepat.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ + +/* Make a pattern template from the strinng pointed to by arg. Stop + * when delim or '\000' or '\n' is found in arg. Return a pointer to + * the pattern template. + * + * The pattern template used here are somewhat different than those + * used in the "Software Tools" book; each token is a structure of + * the form TOKEN (see tools.h). A token consists of an identifier, + * a pointer to a string, a literal character and a pointer to another + * token. This last is 0 if there is no subsequent token. + * + * The one strangeness here is caused (again) by CLOSURE which has + * to be put in front of the previous token. To make this insertion a + * little easier, the 'next' field of the last to point at the chain + * (the one pointed to by 'tail) is made to point at the previous node. + * When we are finished, tail->next is set to 0. + */ +TOKEN * + makepat(arg, delim) +char *arg; +int delim; +{ + TOKEN *head, *tail, *ntok; + int error; + + /* Check for characters that aren't legal at the beginning of a template. */ + + if (*arg == '\0' || *arg == delim || *arg == '\n' || *arg == CLOSURE) + return(0); + + error = 0; + tail = head = NULL; + + while (*arg && *arg != delim && *arg != '\n' && !error) { + ntok = (TOKEN *) malloc(TOKSIZE); + ntok->lchar = '\000'; + ntok->next = 0; + + switch (*arg) { + case ANY: ntok->tok = ANY; break; + + case BOL: + if (head == 0) /* then this is the first symbol */ + ntok->tok = BOL; + else + ntok->tok = LITCHAR; + ntok->lchar = BOL; + break; + + case EOL: + if (*(arg + 1) == delim || *(arg + 1) == '\000' || + *(arg + 1) == '\n') { + ntok->tok = EOL; + } else { + ntok->tok = LITCHAR; + ntok->lchar = EOL; + } + break; + + case CLOSURE: + if (head != 0) { + switch (tail->tok) { + case BOL: + case EOL: + case CLOSURE: + return(0); + + default: + ntok->tok = CLOSURE; + } + } + break; + + case CCL: + + if (*(arg + 1) == NEGATE) { + ntok->tok = NCCL; + arg += 2; + } else { + ntok->tok = CCL; + arg++; + } + + if (ntok->bitmap = makebitmap(CLS_SIZE)) + arg = dodash(CCLEND, arg, ntok->bitmap); + else { + fprintf(stderr, "Not enough memory for pat\n"); + error = 1; + } + break; + + default: + if (*arg == ESCAPE && *(arg + 1) == OPEN) { + ntok->tok = OPEN; + arg++; + } else if (*arg == ESCAPE && *(arg + 1) == CLOSE) { + ntok->tok = CLOSE; + arg++; + } else { + ntok->tok = LITCHAR; + ntok->lchar = esc(&arg); + } + } + + if (error || ntok == 0) { + unmakepat(head); + return(0); + } else if (head == 0) { + /* This is the first node in the chain. */ + + ntok->next = 0; + head = tail = ntok; + } else if (ntok->tok != CLOSURE) { + /* Insert at end of list (after tail) */ + + tail->next = ntok; + ntok->next = tail; + tail = ntok; + } else if (head != tail) { + /* More than one node in the chain. Insert the + * CLOSURE node immediately in front of tail. */ + + (tail->next)->next = ntok; + ntok->next = tail; + } else { + /* Only one node in the chain, Insert the CLOSURE + * node at the head of the linked list. */ + + ntok->next = head; + tail->next = ntok; + head = ntok; + } + arg++; + } + + tail->next = 0; + return(head); +} + +/* maksub.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +char * + maksub(sub, subsz) +char *sub; +int subsz; +{ + int size; + char delim, *cp; + + size = 0; + cp = sub; + + delim = *inptr++; + for (size = 0; *inptr != delim && *inptr != NL && size < subsz; size++) { + if (*inptr == '&') { + *cp++ = DITTO; + inptr++; + } else if ((*cp++ = *inptr++) == ESCAPE) { + if (size >= subsz) return(NULL); + + switch (toupper(*inptr)) { + case NL: *cp++ = ESCAPE; break; + break; + case 'S': + *cp++ = SP; + inptr++; + break; + case 'N': + *cp++ = NL; + inptr++; + break; + case 'T': + *cp++ = HT; + inptr++; + break; + case 'B': + *cp++ = BS; + inptr++; + break; + case 'R': + *cp++ = CR; + inptr++; + break; + case '0':{ + int i = 3; + *cp = 0; + do { + if (*++inptr < '0' || *inptr > '7') + break; + + *cp = (*cp << 3) | (*inptr - '0'); + } while (--i != 0); + cp++; + } break; + default: *cp++ = *inptr++; break; + } + } + } + if (size >= subsz) return(NULL); + + *cp = EOS; + return(sub); +} + +/* matchs.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ + +/* Compares line and pattern. Line is a character string while pat + * is a pattern template made by getpat(). + * Returns: + * 1. A zero if no match was found. + * + * 2. A pointer to the last character satisfing the match + * if ret_endp is non-zero. + * + * 3. A pointer to the beginning of the matched string if + * ret_endp is zero. + * + * e.g.: + * + * matchs ("1234567890", getpat("4[0-9]*7), 0); + * will return a pointer to the '4', while: + * + * matchs ("1234567890", getpat("4[0-9]*7), 1); + * will return a pointer to the '7'. + */ +char * + matchs(line, pat, ret_endp) +char *line; +TOKEN *pat; +int ret_endp; +{ + + char *rval, *bptr; + char *line2; + TOKEN *pat2; + char c; + short ok; + + bptr = line; + + while (*line) { + + if (pat && pat->tok == LITCHAR) { + while (*line) { + pat2 = pat; + line2 = line; + if (*line2 != pat2->lchar) { + c = pat2->lchar; + while (*line2 && *line2 != c) ++line2; + line = line2; + if (*line2 == '\0') break; + } + ok = 1; + ++line2; + pat2 = pat2->next; + while (pat2 && pat2->tok == LITCHAR) { + if (*line2 != pat2->lchar) { + ok = 0; + break; + } + ++line2; + pat2 = pat2->next; + } + if (!pat2) { + if (ret_endp) + return(--line2); + else + return(line); + } else if (ok) + break; + ++line; + } + if (*line == '\0') return(0); + } else { + line2 = line; + pat2 = pat; + } + if ((rval = amatch(line2, pat2, bptr)) == 0) { + if (pat && pat->tok == BOL) break; + line++; + } else { + if (rval > bptr && rval > line) + rval--; /* point to last char matched */ + rval = ret_endp ? rval : line; + break; + } + } + return(rval); +} + +/* move.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int move(num) +int num; +{ + LINE *k0, *k1, *k2, *k3; + + if (line1 <= 0 || line2 < line1 || (line1 <= num && num <= line2)) + return(ERR); + k0 = getptr(prevln(line1)); + k1 = getptr(line1); + k2 = getptr(line2); + k3 = getptr(nextln(line2)); + + relink(k0, k3, k0, k3); + lastln -= line2 - line1 + 1; + + if (num > line1) num -= line2 - line1 + 1; + + curln = num + (line2 - line1 + 1); + + k0 = getptr(num); + k3 = getptr(nextln(num)); + + relink(k0, k1, k2, k3); + relink(k2, k3, k0, k1); + lastln += line2 - line1 + 1; + + return(1); +} + +int transfer(num) +int num; +{ + int mid, lin, ntrans; + + if (line1 <= 0 || line1 > line2) return(ERR); + + mid = num < line2 ? num : line2; + + curln = num; + ntrans = 0; + + for (lin = line1; lin <= mid; lin++) { + ins(gettxt(lin)); + ntrans++; + } + lin += ntrans; + line2 += ntrans; + + for (; lin <= line2; lin += 2) { + ins(gettxt(lin)); + line2++; + } + return(1); +} + +/* omatch.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ + +/* Match one pattern element, pointed at by pat, with the character at + * **linp. Return non-zero on match. Otherwise, return 0. *Linp is + * advanced to skip over the matched character; it is not advanced on + * failure. The amount of advance is 0 for patterns that match null + * strings, 1 otherwise. "boln" should point at the position that will + * match a BOL token. + */ +int omatch(linp, pat, boln) +char **linp; +TOKEN *pat; +char *boln; +{ + + register int advance; + + advance = -1; + + if (**linp) { + switch (pat->tok) { + case LITCHAR: + if (**linp == pat->lchar) advance = 1; + break; + + case BOL: + if (*linp == boln) advance = 0; + break; + + case ANY: + if (**linp != '\n') advance = 1; + break; + + case EOL: + if (**linp == '\n') advance = 0; + break; + + case CCL: + if (testbit(**linp, pat->bitmap)) advance = 1; + break; + + case NCCL: + if (!testbit(**linp, pat->bitmap)) advance = 1; + break; + } + } + if (advance >= 0) *linp += advance; + + return(++advance); +} + +/* optpat.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +TOKEN *oldpat; + +TOKEN * + optpat() +{ + char delim, str[MAXPAT], *cp; + + delim = *inptr++; + cp = str; + while (*inptr != delim && *inptr != NL) { + if (*inptr == ESCAPE && inptr[1] != NL) *cp++ = *inptr++; + *cp++ = *inptr++; + } + + *cp = EOS; + if (*str == EOS) return(oldpat); + if (oldpat) unmakepat(oldpat); + oldpat = getpat(str); + return(oldpat); +} + +/* set.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +struct tbl { + char *t_str; + int *t_ptr; + int t_val; +} *t, tbl[] = { + + "number", &nflg, TRUE, + "nonumber", &nflg, FALSE, + "list", &lflg, TRUE, + "nolist", &lflg, FALSE, + "eightbit", &eightbit, TRUE, + "noeightbit", &eightbit, FALSE, + 0 +}; + +int set() +{ + char word[16]; + int i; + + inptr++; + if (*inptr != 't') { + if (*inptr != SP && *inptr != HT && *inptr != NL) return(ERR); + } else + inptr++; + + if (*inptr == NL) return(show()); + /* Skip white space */ + while (*inptr == SP || *inptr == HT) inptr++; + + for (i = 0; *inptr != SP && *inptr != HT && *inptr != NL;) + word[i++] = *inptr++; + word[i] = EOS; + for (t = tbl; t->t_str; t++) { + if (strcmp(word, t->t_str) == 0) { + *t->t_ptr = t->t_val; + return(0); + } + } + return(0); +} + +int show() +{ + extern int version; + + printf("ed version %d.%d\n", version / 100, version % 100); + printf("number %s, list %s\n", nflg ? "ON" : "OFF", lflg ? "ON" : "OFF"); + return(0); +} + +/* setbuf.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +void relink(a, x, y, b) +LINE *a, *x, *y, *b; +{ + x->l_prev = a; + y->l_next = b; +} + +void clrbuf() +{ + del(1, lastln); +} + +void set_buf() +{ + relink(&line0, &line0, &line0, &line0); + curln = lastln = 0; +} + +/* subst.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ +/* #include "ed.h" */ + +int subst(pat, sub, gflg, pflag) +TOKEN *pat; +char *sub; +int gflg, pflag; +{ + int lin, chngd, nchngd; + char *txtptr, *txt; + char *lastm, *m, *new, buf[MAXLINE]; + + if (line1 <= 0) return(ERR); + nchngd = 0; /* reset count of lines changed */ + for (lin = line1; lin <= line2; lin++) { + txt = txtptr = gettxt(lin); + new = buf; + chngd = 0; + lastm = NULL; + while (*txtptr) { + if (gflg || !chngd) + m = amatch(txtptr, pat, txt); + else + m = NULL; + if (m != NULL && lastm != m) { + chngd++; + new = catsub(txtptr, m, sub, new, + buf + MAXLINE); + lastm = m; + } + if (m == NULL || m == txtptr) { + *new++ = *txtptr++; + } else { + txtptr = m; + } + } + if (chngd) { + if (new >= buf + MAXLINE) return(ERR); + *new++ = EOS; + del(lin, lin); + ins(buf); + nchngd++; + if (pflag) doprnt(curln, curln); + } + } + if (nchngd == 0 && !gflg) { + return(ERR); + } + return(nchngd); +} + +/* System.c */ +#define SHELL "/bin/sh" +#define SHELL2 "/usr/bin/sh" + +int System(c) +char *c; +{ + int pid, status; + + switch (pid = fork()) { + case -1: + return -1; + case 0: + execl(SHELL, "sh", "-c", c, (char *) 0); + execl(SHELL2, "sh", "-c", c, (char *) 0); + exit(-1); + default: while (wait(&status) != pid); +} + return status; +} + +/* unmkpat.c */ +/* #include <stdio.h> */ +/* #include "tools.h" */ + +/* Free up the memory usde for token string */ +void unmakepat(head) +TOKEN *head; +{ + + register TOKEN *old_head; + + while (head) { + switch (head->tok) { + case CCL: + case NCCL: + free(head->bitmap); + /* Fall through to default */ + + default: + old_head = head; + head = head->next; + free((char *) old_head); + break; + } + } +} diff --git a/commands/simple/eject.c b/commands/simple/eject.c new file mode 100755 index 000000000..7bce8cd55 --- /dev/null +++ b/commands/simple/eject.c @@ -0,0 +1,39 @@ +/* eject 1.3 - Eject removable media Author: Kees J. Bot + * 11 Dec 1993 + */ +#define nil 0 +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <fcntl.h> +#include <string.h> +#include <sys/ioctl.h> + +void fatal(char *label) +{ + fprintf(stderr, "eject: %s: %s\n", label, strerror(errno)); + exit(1); +} + +void main(int argc, char **argv) +{ + char *device; + int fd; + + if (argc != 2) { + fprintf(stderr, "Usage: eject <device>\n"); + exit(1); + } + + device= argv[1]; + + /* Try to open it in whatever mode. */ + fd= open(device, O_RDONLY); + if (fd < 0 && errno == EACCES) fd= open(device, O_WRONLY); + if (fd < 0) fatal(device); + + /* Tell it to eject. */ + if (ioctl(fd, DIOCEJECT, nil) < 0) fatal(device); + exit(0); +} diff --git a/commands/simple/env.c b/commands/simple/env.c new file mode 100755 index 000000000..b3f9d50d0 --- /dev/null +++ b/commands/simple/env.c @@ -0,0 +1,83 @@ +/* env 1.1 - Set environment for command Author: Kees J. Bot + * 17 Dec 1997 + */ +#define nil 0 +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +int main(int argc, char **argv) +{ + int i; + int iflag= 0; + int aflag= 0; + extern char **environ; + + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++] + 1; + + if (opt[0] == '-' && opt[1] == 0) break; /* -- */ + + if (opt[0] == 0) iflag= 1; /* - */ + + while (*opt != 0) switch (*opt++) { + case 'i': + iflag= 1; /* Clear environment. */ + break; + case 'a': /* Specify arg 0 separately. */ + aflag= 1; + break; + default: + fprintf(stderr, + "Usage: env [-ia] [name=value] ... [utility [argument ...]]\n"); + exit(1); + } + } + + /* Clear the environment if -i. */ + if (iflag) *environ= nil; + + /* Set the new environment strings. */ + while (i < argc && strchr(argv[i], '=') != nil) { + if (putenv(argv[i]) != 0) { + fprintf(stderr, "env: Setting '%s' failed: %s\n", + argv[i], strerror(errno)); + exit(1); + } + i++; + } + + /* Environment settings and command may be separated with '--'. + * This is for compatibility with other envs, we don't advertise it. + */ + if (i < argc && strcmp(argv[i], "--") == 0) i++; + + if (i >= argc) { + /* No utility given; print environment. */ + char **ep; + + for (ep= environ; *ep != nil; ep++) { + if (puts(*ep) == EOF) { + fprintf(stderr, "env: %s\n", strerror(errno)); + exit(1); + } + } + return 0; + } else { + char *util, **args; + int err; + + util= argv[i]; + args= argv + i; + if (aflag) args++; + (void) execvp(util, args); + err= errno; + fprintf(stderr, "env: Can't execute %s: %s\n", + util, strerror(err)); + return err == ENOENT ? 127 : 126; + } +} diff --git a/commands/simple/expand.c b/commands/simple/expand.c new file mode 100755 index 000000000..aae2aa90d --- /dev/null +++ b/commands/simple/expand.c @@ -0,0 +1,99 @@ +/* expand - expand tabs to spaces Author: Terrence W. Holm */ + +/* Usage: expand [ -tab1,tab2,tab3,... ] [ file ... ] */ + +#include <stddef.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +#define MAX_TABS 32 + +int column = 0; /* Current column, retained between files */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void Expand, (FILE *f, int tab_index, int tabs [])); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int tabs[MAX_TABS]; + int tab_index = 0; /* Default one tab */ + int i; + FILE *f; + + tabs[0] = 8; /* Default tab stop */ + + if (argc > 1 && argv[1][0] == '-') { + char *p = argv[1]; + int last_tab_stop = 0; + + for (tab_index = 0; tab_index < MAX_TABS; ++tab_index) { + if ((tabs[tab_index] = atoi(p + 1)) <= last_tab_stop) { + fprintf(stderr, "Bad tab stop spec\n"); + exit(1); + } + last_tab_stop = tabs[tab_index]; + + if ((p = strchr(p + 1, ',')) == NULL) break; + } + + --argc; + ++argv; + } + if (argc == 1) + Expand(stdin, tab_index, tabs); + else + for (i = 1; i < argc; ++i) { + if ((f = fopen(argv[i], "r")) == NULL) { + perror(argv[i]); + exit(1); + } + Expand(f, tab_index, tabs); + fclose(f); + } + + return(0); +} + + +void Expand(f, tab_index, tabs) +FILE *f; +int tab_index; +int tabs[]; +{ + int next; + int c; + int i; + + while ((c = getc(f)) != EOF) { + if (c == '\t') { + if (tab_index == 0) + next = (column / tabs[0] + 1) * tabs[0]; + else { + for (i = 0; i <= tab_index && tabs[i] <= column; ++i); + + if (i > tab_index) + next = column + 1; + else + next = tabs[i]; + } + + do { + ++column; + putchar(' '); + } while (column < next); + + continue; + } + if (c == '\b') + column = column > 0 ? column - 1 : 0; + else if (c == '\n' || c == '\r') + column = 0; + else + ++column; + + putchar(c); + } +} diff --git a/commands/simple/factor.c b/commands/simple/factor.c new file mode 100755 index 000000000..9a947a5ce --- /dev/null +++ b/commands/simple/factor.c @@ -0,0 +1,53 @@ +/* factor - print the prime factors of a number Author: Andy Tanenbaum */ + +#include <stdlib.h> +#include <stdio.h> + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(long first, (long k)); + +int main(argc, argv) +int argc; +char *argv[]; +{ +/* Factor a number */ + + long i, n, flag = 0; + + if (argc != 2 || (n = atol(argv[1])) < 2) { + printf("Usage: factor n (2 <= n < 2**31)\n"); + exit(1); + } + if (n == 2) { + printf("2 is a prime\n"); + exit(0); + } + while (1) { + i = first(n); + if (i == 0) { + if (flag == 0) + printf("%ld is a prime\n", n); + else + printf("%ld\n", n); + exit(0); + } + printf("%ld ", i); + n = n / i; + flag = 1; + } +} + + +long first(k) +long k; +{ +/* Return the first factor of k. If it is a prime, return 0; */ + + long i; + + if (k == 2) return(0); + if (k % 2 == 0) return (2); + for (i = 3; i <= k / 3; i += 2) + if (k % i == 0) return(i); + return(0); +} diff --git a/commands/simple/fgrep.c b/commands/simple/fgrep.c new file mode 100755 index 000000000..7abb613ed --- /dev/null +++ b/commands/simple/fgrep.c @@ -0,0 +1,355 @@ +/* fgrep - fast grep Author: Bert Gijsbers */ + +/* Copyright (c) 1991 by Bert Gijsbers. All rights reserved. + * Permission to use and redistribute this software is hereby granted provided + * that this copyright notice remains intact and that any modifications are + * clearly marked as such. + * + * syntax: + * fgrep -chlnsv <[-e string] ... [-f file] ... | string> [file] ... + * options: + * -c : print the number of matching lines + * -h : don't print file name headers if more than one file + * -l : print only the file names of the files containing a match + * -n : print line numbers + * -s : don't print, return status only + * -v : reverse, lines not containing one of the strings match + * -e string : search for this string + * -f file : file contains strings to search for + * notes: + * Options are processed by getopt(3). + * Multiple strings per command line are supported, eg. + * fgrep -e str1 -e str2 *.c + * Instead of a filename - is allowed, meaning standard input. + */ + +/* #include <ansi.h> */ +#include <sys/types.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#define MAX_STR_LEN 256 /* maximum length of strings to search for */ +#define BYTE 0xFF /* convert from char to int */ +#define READ_SIZE 4096 /* read() request size */ +#define BUF_SIZE (2*READ_SIZE) /* size of buffer */ + +typedef struct test_str { + struct test_str *next; /* linked list */ + char *str; /* string to be found */ + char *str_end; /* points to last character */ + int len; /* string length */ + char *bufp; /* pointer into input buffer */ + unsigned char table[256]; /* table for Boyer-Moore algorithm */ +} test_str; + +test_str *strings; +char *prog_name; +int cflag, hflag, lflag, nflag, sflag, vflag; +unsigned line_num; /* line number in current file */ + +int fd_in, eof_seen; /* file descriptor for input and eof status */ +char input_buffer[BUF_SIZE + 2];/* buffer + sentinel margin */ +#define buffer (&input_buffer[2]) + +/* Pointers into the input buffer */ +char *input; /* points to current input char */ +char *max_input; /* points to first invalid char */ +char *buf_end; /* points to first char not read */ + +/* Error messages */ +char no_mem[] = "not enough memory"; +char no_arg[] = "argument missing"; + +extern char *optarg; +extern int optind; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(char *search_str, (test_str * ts)); +_PROTOTYPE(int fill_buffer, (void)); +_PROTOTYPE(void failure, (char *mesg)); +_PROTOTYPE(void file_open, (void)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(char *get_line, (void)); +_PROTOTYPE(void string_file, (void)); +_PROTOTYPE(void add_string, (char *str)); +_PROTOTYPE(int getopt, (int argc, char **argv, char *optstring)); + +int main(argc, argv) +int argc; +char **argv; +{ + char *line; + int c; + unsigned count; /* number of matching lines in current file */ + unsigned found_one = 0; /* was there any match in any file at all ? */ + +#ifdef noperprintf + noperprintf(stdout); +#else + static char outbuf[BUFSIZ]; + + setvbuf(stdout, outbuf, _IOFBF, sizeof outbuf); +#endif + + prog_name = argv[0]; + if (argc == 1) usage(); + while ((c = getopt(argc, argv, "ce:f:hlnsv")) != EOF) { + switch (c) { + case 'c': cflag++; break; + case 'e': add_string(optarg); break; + case 'f': string_file(); break; + case 'h': hflag++; break; + case 'l': lflag++; break; + case 'n': nflag++; break; + case 's': sflag++; break; + case 'v': vflag++; break; + default: usage(); break; + } + } + + /* If no -e or -f option is used take a string from the command line. */ + if (strings == (test_str *) NULL) { + if (optind == argc) failure(no_arg); + add_string(argv[optind++]); + } + if (argc - optind < 2) + hflag++; /* don't print filenames if less than two + * files */ + + /* Handle every matching line according to the flags. */ + do { + optarg = argv[optind]; + file_open(); + count = 0; + while ((line = get_line()) != (char *) NULL) { + count++; + if (sflag) return 0; + if (lflag) { + printf("%s\n", optarg); + fflush(stdout); + break; + } + if (cflag) continue; + if (hflag == 0) printf("%s:", optarg); + if (nflag) printf("%u:", line_num); + do { + putchar(*line); + } while (++line < input); + fflush(stdout); + } + found_one |= count; + if (cflag) { + if (hflag == 0) printf("%s: ", optarg); + printf("%u\n", count); + fflush(stdout); + } + close(fd_in); + } while (++optind < argc); + + /* Exit nonzero if no match is found. */ + return found_one ? 0 : 1; +} + +void usage() +{ + fprintf(stderr, + "Usage: %s -chlnsv <[-e string] ... [-f file] ... | string> [file] ...\n", + prog_name); + exit(2); +} + +void failure(mesg) +char *mesg; +{ + fprintf(stderr, "%s: %s\n", prog_name, mesg); + exit(1); +} + +/* Add a string to search for to the global linked list `strings'. */ +void add_string(str) +char *str; +{ + test_str *ts; + int len; + + if (str == (char *) NULL || (len = strlen(str)) == 0) return; + if (len > MAX_STR_LEN) failure("string too long"); + if ((ts = (test_str *) malloc(sizeof(*ts))) == (test_str *) NULL) + failure(no_mem); + + /* Initialize Boyer-Moore table. */ + memset(ts->table, len, sizeof(ts->table)); + ts->len = len; + ts->str = str; + ts->str_end = str + len - 1; + for (; --len >= 0; str++) ts->table[*str & BYTE] = len; + + /* Put it on the list */ + ts->next = strings; + strings = ts; +} + +/* Open a file for reading. Initialize input buffer pointers. */ +void file_open() +{ + /* Use stdin if no file arguments are given on the command line. */ + if (optarg == (char *) NULL || strcmp(optarg, "-") == 0) { + fd_in = 0; + optarg = "stdin"; + } else if ((fd_in = open(optarg, O_RDONLY)) == -1) { + fprintf(stderr, "%s: can't open %s\n", prog_name, optarg); + exit(1); + } + input = max_input = buf_end = buffer; + buffer[-1] = '\n'; /* sentinel */ + eof_seen = 0; + line_num = 0; +} + +/* Move any leftover characters to the head of the buffer. + * Read characters into the rest of the buffer. + * Round off the available input to whole lines. + * Return the number of valid input characters. + */ +int fill_buffer() +{ + char *bufp; + int size; + + if (eof_seen) return 0; + + size = buf_end - max_input; + memmove(buffer, max_input, size); + bufp = &buffer[size]; + + do { + if ((size = read(fd_in, bufp, READ_SIZE)) <= 0) { + if (size != 0) failure("read error"); + eof_seen++; + if (bufp == buffer) /* no input left */ + return 0; + /* Make sure the last char of a file is '\n'. */ + *bufp++ = '\n'; + break; + } + bufp += size; + } while (bufp - buffer < READ_SIZE && bufp[-1] != '\n'); + + buf_end = bufp; + while (*--bufp != '\n'); + if (++bufp == buffer) { + /* Line too long. */ + *buf_end++ = '\n'; + bufp = buf_end; + } + max_input = bufp; + input = buffer; + + return max_input - buffer; +} + +/* Read strings from a file. Give duplicates to add_string(). */ +void string_file() +{ + char *str, *p; + + file_open(); + while (input < max_input || fill_buffer() > 0) { + p = (char *) memchr(input, '\n', BUF_SIZE); + *p++ = '\0'; + if ((str = (char *) malloc(p - input)) == (char *) NULL) + failure(no_mem); + memcpy(str, input, p - input); + add_string(str); + input = p; + } + close(fd_in); +} + +/* Scan the rest of the available input for a string using Boyer-Moore. + * Return a pointer to the match or a pointer beyond end of input if no match. + * Record how far the input is scanned. + */ +char *search_str(ts) +test_str *ts; +{ + char *bufp, *prevbufp, *s; + + bufp = input + ts->len - 1; + while (bufp < max_input) { + prevbufp = bufp; + bufp += ts->table[*bufp & BYTE]; + if (bufp > prevbufp) continue; + s = ts->str_end; + do { + if (s == ts->str) { /* match found */ + ts->bufp = bufp; + return bufp; + } + } while (*--bufp == *--s); + bufp = prevbufp + 1; + } + ts->bufp = bufp; + + return bufp; +} + +/* Return the next line in which one of the strings occurs. + * Or, if the -v option is used, the next line without a match. + * Or NULL on EOF. + */ +char *get_line() +{ + test_str *ts; + char *match, *line; + + /* Loop until a line is found. */ + while (1) { + if (input >= max_input && fill_buffer() == 0) { /* EOF */ + line = (char *) NULL; + break; + } + + /* If match is still equal to max_input after the next loop + * then no match is found. */ + match = max_input; + ts = strings; + do { + if (input == buffer) { + if (search_str(ts) < match) match = ts->bufp; + } else if (ts->bufp < match) { + if (ts->bufp >= input || search_str(ts) < match) + match = ts->bufp; + } + } while ((ts = ts->next) != (test_str *) NULL); + + /* Determine if and in what line a match is found. Only do + * line number counting if it is necessary or very easy. */ + if (vflag) { + line_num++; + line = input; + input = 1 + (char *) memchr(line, '\n', BUF_SIZE); + if (input <= match) break; /* no match in current line */ + } else if (nflag) { + do { + line_num++; + line = input; + input = 1 + (char *) memchr(line, '\n', BUF_SIZE); + } while (input < match || + (input == match && match < max_input)); + if (match < max_input) break; /* match found */ + } else if (match < max_input) { + /* Match found. */ + for (line = match; *--line != '\n';); + line++; + input = 1 + (char *) memchr(match, '\n', BUF_SIZE); + break; + } else + input = max_input; + } + + return line; +} diff --git a/commands/simple/file.c b/commands/simple/file.c new file mode 100755 index 000000000..13df8a76f --- /dev/null +++ b/commands/simple/file.c @@ -0,0 +1,278 @@ +/* file - report on file type. Author: Andy Tanenbaum */ +/* Magic number detection changed to look-up table 08-Jan-91 - ajm */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#define BLOCK_SIZE 1024 + +#define XBITS 00111 /* rwXrwXrwX (x bits in the mode) */ +#define ENGLISH 25 /* cutoff for determining if text is Eng. */ +unsigned char buf[BLOCK_SIZE]; + +struct info { + int execflag; /* 1 == ack executable, 2 == gnu executable, + * 3 == core */ + unsigned char magic[4]; /* First four bytes of the magic number */ + unsigned char mask[4]; /* Mask to apply when matching */ + char *description; /* What it means */ +} table[] = { + 0x00, 0x1f, 0x9d, 0x8d, 0x00, 0xff, 0xff, 0xff, 0x00, + "13-bit compressed file", + 0x00, 0x1f, 0x9d, 0x90, 0x00, 0xff, 0xff, 0xff, 0x00, + "16-bit compressed file", + 0x00, 0x65, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + "MINIX-PC bcc archive", + 0x00, 0x2c, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + "ACK object archive", + 0x00, 0x65, 0xff, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + "MINIX-PC ack archive", + 0x00, 0x47, 0x6e, 0x75, 0x20, 0xff, 0xff, 0xff, 0xff, + "MINIX-68k gnu archive", + 0x00, 0x21, 0x3c, 0x61, 0x72, 0xff, 0xff, 0xff, 0xff, + "MINIX-PC gnu archive", + 0x00, 0x01, 0x02, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + "ACK object file", + 0x00, 0xa3, 0x86, 0x00, 0x00, 0xff, 0xff, 0x00, 0x00, + "MINIX-PC bcc object file", + 0x00, 0x00, 0x00, 0x01, 0x07, 0xff, 0xff, 0xff, 0xff, + "MINIX-68k gnu object file", + 0x00, 0x07, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + "MINIX-PC gnu object file", + 0x01, 0x01, 0x03, 0x00, 0x04, 0xff, 0xff, 0x00, 0xff, + "MINIX-PC 16-bit executable", + 0x01, 0x01, 0x03, 0x00, 0x10, 0xff, 0xff, 0x00, 0xff, + "MINIX-PC 32-bit executable", + 0x01, 0x04, 0x10, 0x03, 0x01, 0xff, 0xff, 0xff, 0xff, + "MINIX-68k old style executable", + 0x01, 0x01, 0x03, 0x10, 0x0b, 0xff, 0xff, 0xff, 0xff, + "MINIX-68k new style executable", + 0x02, 0x0b, 0x01, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, + "MINIX-PC 32-bit gnu executable combined I & D space", + 0x02, 0x00, 0x00, 0x0b, 0x01, 0xff, 0xff, 0xff, 0xff, + "MINIX-68k gnu executable", + 0x03, 0x82, 0x12, 0xC4, 0xC0, 0xff, 0xff, 0xff, 0xff, + "core file", +}; + +int tabsize = sizeof(table) / sizeof(struct info); +int L_flag; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void file, (char *name)); +_PROTOTYPE(void do_strip, (int type)); +_PROTOTYPE(void usage, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ +/* This program uses some heuristics to try to guess about a file type by + * looking at its contents. + */ + int c, i; + + L_flag= 0; + while ((c= getopt(argc, argv, "L?")) != -1) + { + switch(c) + { + case 'L': + L_flag= 1; + break; + case '?': + usage(); + default: + fprintf(stderr, "file: panic getopt failed\n"); + exit(1); + } + } + if (optind >= argc) usage(); + for (i = optind; i < argc; i++) file(argv[i]); + return(0); +} + +void file(name) +char *name; +{ + int i, fd, n, mode, nonascii, special, funnypct, etaoins; + int j; + long engpct; + int c; + struct stat st_buf; + + printf("%s: ", name); + +#ifdef S_IFLNK + if (!L_flag) + n = lstat(name, &st_buf); + else + n = stat(name, &st_buf); +#else + n = stat(name, &st_buf); +#endif + if (n < 0) { + printf("cannot stat\n"); + return; + } + mode = st_buf.st_mode; + + /* Check for directories and special files. */ + if (S_ISDIR(mode)) { + printf("directory\n"); + return; + } + if (S_ISCHR(mode)) { + printf("character special file\n"); + return; + } + if (S_ISBLK(mode)) { + printf("block special file\n"); + return; + } + if (S_ISFIFO(mode)) { + printf("named pipe\n"); + return; + } +#ifdef S_IFLNK + if (S_ISLNK(mode)) { + n= readlink(name, (char *)buf, BLOCK_SIZE); + if (n == -1) + printf("cannot readlink\n"); + else + printf("symbolic link to %.*s\n", n, buf); + return; + } +#endif + if (!S_ISREG(mode)) { + printf("strange file type %5o\n", mode); + return; + } + + /* Open the file, stat it, and read in 1 block. */ + fd = open(name, O_RDONLY); + if (fd < 0) { + printf("cannot open\n"); + return; + } + n = read(fd, (char *)buf, BLOCK_SIZE); + if (n < 0) { + printf("cannot read\n"); + close(fd); + return; + } + if (n == 0) { /* must check this, for loop will fail otherwise !! */ + printf("empty file\n"); + close(fd); + return; + } + + for (i = 0; i < tabsize; i++) { + for (j = 0; j < 4; j++) + if ((buf[j] & table[i].mask[j]) != table[i].magic[j]) + break; + if (j == 4) { + printf("%s", table[i].description); + do_strip(table[i].execflag); + close(fd); + return; + } + } + + + /* Check to see if file is a shell script. */ + if (mode & XBITS) { + /* Not a binary, but executable. Probably a shell script. */ + printf("shell script\n"); + close(fd); + return; + } + + /* Check for ASCII data and certain punctuation. */ + nonascii = 0; + special = 0; + etaoins = 0; + for (i = 0; i < n; i++) { + c = buf[i]; + if (c & 0200) nonascii++; + if (c == ';' || c == '{' || c == '}' || c == '#') special++; + if (c == '*' || c == '<' || c == '>' || c == '/') special++; + if (c >= 'A' && c <= 'Z') c = c - 'A' + 'a'; + if (c == 'e' || c == 't' || c == 'a' || c == 'o') etaoins++; + if (c == 'i' || c == 'n' || c == 's') etaoins++; + } + + if (nonascii == 0) { + /* File only contains ASCII characters. Continue processing. */ + funnypct = 100 * special / n; + engpct = 100L * (long) etaoins / n; + if (funnypct > 1) { + printf("C program\n"); + } else { + if (engpct > (long) ENGLISH) + printf("English text\n"); + else + printf("ASCII text\n"); + } + close(fd); + return; + } + + /* Give up. Call it data. */ + printf("data\n"); + close(fd); + return; +} + +void do_strip(type) +int type; +{ + if (type == 1) { /* Non-GNU executable */ + if (buf[2] & 1) + printf(", UZP"); + if (buf[2] & 2) + printf(", PAL"); + if (buf[2] & 4) + printf(", NSYM"); + if (buf[2] & 0x20) + printf(", sep I&D"); + else + printf(", comm I&D"); + if (( buf[28] | buf[29] | buf[30] | buf[31]) != 0) + printf(" not stripped\n"); + else + printf(" stripped\n"); + return; + } + + if (type == 2) { /* GNU format executable */ + if ((buf[16] | buf[17] | buf[18] | buf[19]) != 0) + printf(" not stripped\n"); + else + printf(" stripped\n"); + return; + } + + if (type == 3) { /* Core file in <sys/core.h> format */ + switch(buf[36] & 0xff) + { + case 1: printf(" of i86 executable"); break; + case 2: printf(" of i386 executable"); break; + default:printf(" of unknown executable"); break; + } + printf(" '%.32s'\n", buf+4); + return; + } + + printf("\n"); /* Not an executable file */ + } + +void usage() +{ + printf("Usage: file [-L] name ...\n"); + exit(1); +} diff --git a/commands/simple/find.c b/commands/simple/find.c new file mode 100755 index 000000000..d7b8cd5f5 --- /dev/null +++ b/commands/simple/find.c @@ -0,0 +1,914 @@ +/* find - look for files satisfying a predicate Author: E. Baalbergen */ + +/* Original author: Erik Baalbergen; POSIX compliant version: Bert Laverman */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <pwd.h> +#include <grp.h> +#include <dirent.h> +#include <limits.h> +#include <stdio.h> + +/*######################## DEFINITIONS ##############################*/ + +#ifdef S_IFLNK +#define LSTAT lstat +#else +#define LSTAT stat +#endif + +#define SHELL "/bin/sh" +#define MAXARG 256 /* maximum length for an argv for -exec */ +#define BSIZE 512 /* POSIX wants 512 byte blocks */ +#define SECS_PER_DAY (24L*60L*60L) /* check your planet */ + +#define OP_NAME 1 /* match name */ +#define OP_PERM 2 /* check file permission bits */ +#define OP_TYPE 3 /* check file type bits */ +#define OP_LINKS 4 /* check link count */ +#define OP_USER 5 /* check owner */ +#define OP_GROUP 6 /* check group ownership */ +#define OP_SIZE 7 /* check size, blocks or bytes */ +#define OP_SIZEC 8 /* this is a fake for -size with 'c' */ +#define OP_INUM 9 /* compare inode number */ +#define OP_ATIME 10 /* check last access time */ +#define OP_CTIME 11 /* check creation time */ +#define OP_MTIME 12 /* check last modification time */ +#define OP_EXEC 13 /* execute command */ +#define OP_OK 14 /* execute with confirmation */ +#define OP_PRINT 15 /* print name */ +#define OP_PRINT0 16 /* print name null terminated */ +#define OP_NEWER 17 /* compare modification times */ +#define OP_AND 18 /* logical and (short circuit) */ +#define OP_OR 19 /* logical or (short circuit) */ +#define OP_XDEV 20 /* do not cross file-system boundaries */ +#define OP_DEPTH 21 /* descend directory before testing */ +#define OP_PRUNE 22 /* don't descend into current directory */ +#define OP_NOUSER 23 /* check validity of user id */ +#define OP_NOGROUP 24 /* check validity of group id */ +#define LPAR 25 /* left parenthesis */ +#define RPAR 26 /* right parenthesis */ +#define NOT 27 /* logical not */ + +/* Some return values: */ +#define EOI -1 /* end of expression */ +#define NONE 0 /* not a valid predicate */ + +/* For -perm with symbolic modes: */ +#define ISWHO(c) ((c == 'u') || (c == 'g') || (c == 'o') || (c == 'a')) +#define ISOPER(c) ((c == '-') || (c == '=') || (c == '+')) +#define ISMODE(c) ((c == 'r') || (c == 'w') || (c == 'x') || \ + (c == 's') || (c == 't')) +#define MUSER 1 +#define MGROUP 2 +#define MOTHERS 4 + + +struct exec { + int e_cnt; + char *e_vec[MAXARG]; +}; + +struct node { + int n_type; /* any OP_ or NOT */ + union { + char *n_str; + struct { + long n_val; + int n_sign; + } n_int; + struct exec *n_exec; + struct { + struct node *n_left, *n_right; + } n_opnd; + } n_info; +}; + +struct oper { + char *op_str; + int op_val; +} ops[] = { + + { + "name", OP_NAME + }, + { + "perm", OP_PERM + }, + { + "type", OP_TYPE + }, + { + "links", OP_LINKS + }, + { + "user", OP_USER + }, + { + "group", OP_GROUP + }, + { + "size", OP_SIZE + }, + { + "inum", OP_INUM + }, + { + "atime", OP_ATIME + }, + { + "ctime", OP_CTIME + }, + { + "mtime", OP_MTIME + }, + { + "exec", OP_EXEC + }, + { + "ok", OP_OK + }, + { + "print", OP_PRINT + }, + { + "print0", OP_PRINT0 + }, + { + "newer", OP_NEWER + }, + { + "a", OP_AND + }, + { + "o", OP_OR + }, + { + "xdev", OP_XDEV + }, + { + "depth", OP_DEPTH + }, + { + "prune", OP_PRUNE + }, + { + "nouser", OP_NOUSER + }, + { + "nogroup", OP_NOGROUP + }, + { + 0, 0 + } +}; + + +char **ipp; /* pointer to next argument during parsing */ +char *prog; /* program name (== argv [0]) */ +char *epath; /* value of PATH environment string */ +long current_time; /* for computing age */ +int tty; /* fd for /dev/tty when using -ok */ +int xdev_flag = 0; /* cross device boundaries? */ +int devnr; /* device nr of first inode */ +int depth_flag = 0; /* descend before check? */ +int prune_here; /* This is Baaaad! Don't ever do this again! */ +int um; /* current umask() */ +int needprint = 1; /* implicit -print needed? */ + + +/* The prototypes: */ +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(char *Malloc, (int n)); +_PROTOTYPE(char *Salloc, (char *s)); +_PROTOTYPE(void find, (char *path, struct node * pred, char *last)); +_PROTOTYPE(int check, (char *path, struct stat * st, struct node * n, char *last)); +_PROTOTYPE(int ichk, (long val, struct node * n)); +_PROTOTYPE(int lex, (char *str)); +_PROTOTYPE(struct node * newnode, (int t)); +_PROTOTYPE(int isnumber, (char *str, int base, int sign)); +_PROTOTYPE(void number, (char *str, int base, long *pl, int *ps)); +_PROTOTYPE(void fmode, (char *str, long *pl, int *ps)); +_PROTOTYPE(struct node * expr, (int t)); +_PROTOTYPE(struct node * primary, (int t)); +_PROTOTYPE(struct node * secondary, (int t)); +_PROTOTYPE(void checkarg, (char *arg)); +_PROTOTYPE(struct node * simple, (int t)); +_PROTOTYPE(void nonfatal, (char *s1, char *s2)); +_PROTOTYPE(void fatal, (char *s1, char *s2)); +_PROTOTYPE(int smatch, (char *s, char *t)); +_PROTOTYPE(char *find_bin, (char *s)); +_PROTOTYPE(int execute, (int op, struct exec * e, char *path)); +_PROTOTYPE(void domode, (int op, int *mode, int bits)); + + +/* Malloc: a certified malloc */ +char *Malloc(n) +int n; +{ + char *m; + + if ((m = (char *) malloc(n)) == (char *) NULL) fatal("out of memory", ""); + return m; +} + +/* Salloc: allocate space for a string */ +char *Salloc(s) +char *s; +{ + return strcpy(Malloc(strlen(s) + 1), s); +} + + +/* Main: the main body */ +int main(argc, argv) +int argc; +char *argv[]; +{ + char **pathlist, *path, *last; + int pathcnt = 0, i; + struct node *pred; + + prog = *argv++; /* set program name (for diagnostics) */ + if ((epath = getenv("PATH")) == (char *) NULL) + fatal("Can't get path from environment", ""); + (void) umask(um = umask(0)); /* non-destructive get-umask :-) */ + time(¤t_time); /* get current time */ + + pathlist= argv; + while (--argc > 0 && lex(*argv) == NONE) { /* find paths */ + pathcnt++; + argv++; + } + if (pathcnt == 0) /* there must be at least one path */ + fatal("Usage: path-list [predicate-list]", ""); + + ipp = argv; /* prepare for parsing */ + if (argc != 0) { /* If there is anything to parse, */ + pred = expr(lex(*ipp)); /* then do so */ + if (lex(*++ipp) != EOI) /* Make sure there's nothing left */ + fatal("syntax error: garbage at end of predicate", ""); + } else /* No predicate list */ + pred = (struct node *) NULL; + + for (i = 0; i < pathcnt; i++) { + if (xdev_flag) xdev_flag = 2; + path = pathlist[i]; + if ((last = strrchr(path, '/')) == NULL) last = path; else last++; + find(path, pred, last); + } + return 0; +} + +void find(path, pred, last) +char *path, *last; +struct node *pred; +{ + char spath[PATH_MAX]; + register char *send = spath, *p; + struct stat st; + DIR *dp; + struct dirent *de; + + if (path[1] == '\0' && *path == '/') { + *send++ = '/'; + *send = '\0'; + } else + while (*send++ = *path++) { + } + + if (LSTAT(spath, &st) == -1) + nonfatal("can't get status of ", spath); + else { + switch (xdev_flag) { + case 0: + break; + case 1: + if (st.st_dev != devnr) return; + break; + case 2: /* set current device number */ + xdev_flag = 1; + devnr = st.st_dev; + break; + } + + prune_here = 0; + if (!depth_flag && check(spath, &st, pred, last) && needprint) + printf("%s\n", spath); + if (!prune_here && (st.st_mode & S_IFMT) == S_IFDIR) { + if ((dp = opendir(spath)) == NULL) { + nonfatal("can't read directory ", spath); + perror( "Error" ); + return; + } + send[-1] = '/'; + while ((de = readdir(dp)) != NULL) { + p = de->d_name; + if ((de->d_name[0] != '.') || ((de->d_name[1]) + && ((de->d_name[1] != '.') + || (de->d_name[2])))) { + strcpy(send, de->d_name); + find(spath, pred, send); + } + } + closedir(dp); + } + if (depth_flag) { + send[-1] = '\0'; + if (check(spath, &st, pred, last) && needprint) + printf("%s\n", spath); + } + } +} + +int check(path, st, n, last) +char *path, *last; +register struct stat *st; +register struct node *n; +{ + if (n == (struct node *) NULL) return 1; + switch (n->n_type) { + case OP_AND: + return check(path, st, n->n_info.n_opnd.n_left, last) && + check(path, st, n->n_info.n_opnd.n_right, last); + case OP_OR: + return check(path, st, n->n_info.n_opnd.n_left, last) || + check(path, st, n->n_info.n_opnd.n_right, last); + case NOT: + return !check(path, st, n->n_info.n_opnd.n_left, last); + case OP_NAME: + return smatch(last, n->n_info.n_str); + case OP_PERM: + if (n->n_info.n_int.n_sign < 0) + return(st->st_mode & (int) n->n_info.n_int.n_val) == + (int) n->n_info.n_int.n_val; + return(st->st_mode & 07777) == (int) n->n_info.n_int.n_val; + case OP_NEWER: + return st->st_mtime > n->n_info.n_int.n_val; + case OP_TYPE: + return(st->st_mode & S_IFMT) == (mode_t) n->n_info.n_int.n_val; + case OP_LINKS: + return ichk((long) (st->st_nlink), n); + case OP_USER: + return st->st_uid == n->n_info.n_int.n_val; + case OP_GROUP: + return st->st_gid == n->n_info.n_int.n_val; + case OP_SIZE: + return ichk((st->st_size == 0) ? 0L : + (long) ((st->st_size - 1) / BSIZE + 1), n); + case OP_SIZEC: + return ichk((long) st->st_size, n); + case OP_INUM: + return ichk((long) (st->st_ino), n); + case OP_ATIME: + return ichk(st->st_atime, n); + case OP_CTIME: + return ichk(st->st_ctime, n); + case OP_MTIME: + return ichk(st->st_mtime, n); + case OP_EXEC: + case OP_OK: + return execute(n->n_type, n->n_info.n_exec, path); + case OP_PRINT: + printf("%s\n", path); + return 1; + case OP_PRINT0: + printf("%s", path); putchar(0); + return 1; + case OP_XDEV: + case OP_DEPTH: + return 1; + case OP_PRUNE: + prune_here = 1; + return 1; + case OP_NOUSER: + return(getpwuid(st->st_uid) == (struct passwd *) NULL); + case OP_NOGROUP: + return(getgrgid(st->st_gid) == (struct group *) NULL); + } + fatal("ILLEGAL NODE", ""); + return 0; /* Never reached */ +} + +int ichk(val, n) +long val; +struct node *n; +{ + switch (n->n_info.n_int.n_sign) { + case 0: + return val == n->n_info.n_int.n_val; + case 1: + return val > n->n_info.n_int.n_val; + case -1: return val < n->n_info.n_int.n_val; +} + fatal("internal: bad n_sign", ""); + return 0; /* Never reached */ +} + +int lex(str) +char *str; +{ + if (str == (char *) NULL) return EOI; + if (*str == '-') { + register struct oper *op; + + str++; + for (op = ops; op->op_str; op++) + if (strcmp(str, op->op_str) == 0) break; + return op->op_val; + } + if (str[1] == 0) { + switch (*str) { + case '(': + return LPAR; + case ')': + return RPAR; + case '!': return NOT; + } + } + return NONE; +} + +struct node * + newnode(t) +int t; +{ + struct node *n = (struct node *) Malloc(sizeof(struct node)); + + n->n_type = t; + return n; +} + +/*########################### PARSER ###################################*/ +/* Grammar: + * expr : primary | primary OR expr; + * primary : secondary | secondary AND primary | secondary primary; + * secondary : NOT secondary | LPAR expr RPAR | simple; + * simple : -OP args... + */ + +/* Isnumber checks correct number syntax. A sign is allowed, but the '+' + * only if the number is to be in decimal. + */ +int isnumber(str, base, sign) +register char *str; +int base; +int sign; +{ + if (sign && ((*str == '-') || ((base == 8) && (*str == '+')))) str++; + while ((*str >= '0') && (*str < ('0' + base))) str++; + return(*str == '\0' ? 1 : 0); +} + +/* Convert a string to an integer, storing sign info in *ps. */ +void number(str, base, pl, ps) +char *str; +int base; +long *pl; +int *ps; +{ + int up = '0' + base - 1; + long val = 0; + + *ps = ((*str == '-' || *str == '+') ? ((*str++ == '-') ? -1 : 1) : 0); + while (*str >= '0' && *str <= up) val = base * val + *str++ - '0'; + if (*str) fatal("syntax error: illegal numeric value", ""); + *pl = val; +} + + +void domode(op, mode, bits) +int op; +int *mode; +int bits; +{ + switch (op) { + case '-': + *mode &= ~bits; + break; /* clear bits */ + case '=': + *mode |= bits; + break; /* set bits */ + case '+': + *mode |= (bits & ~um); /* set, but take umask in account */ + } +} + +void fmode(str, pl, ps) +char *str; +long *pl; +int *ps; +{ + int m = 0, w, op; + char *p = str; + + if (*p == '-') { + *ps = -1; + p++; + } else + *ps = 0; + + while (*p) { + w = 0; + if (ISOPER(*p)) + w = MUSER | MGROUP | MOTHERS; + else if (!ISWHO(*p)) + fatal("u, g, o, or a expected: ", p); + else { + while (ISWHO(*p)) { + switch (*p) { + case 'u': + w |= MUSER; + break; + case 'g': + w |= MGROUP; + break; + case 'o': + w |= MOTHERS; + break; + case 'a': + w = MUSER | MGROUP | MOTHERS; + } + p++; + } + if (!ISOPER(*p)) fatal("-, + or = expected: ", p); + } + op = *p++; + while (ISMODE(*p)) { + switch (*p) { + case 'r': + if (w & MUSER) domode(op, &m, S_IRUSR); + if (w & MGROUP) domode(op, &m, S_IRGRP); + if (w & MOTHERS) domode(op, &m, S_IROTH); + break; + case 'w': + if (w & MUSER) domode(op, &m, S_IWUSR); + if (w & MGROUP) domode(op, &m, S_IWGRP); + if (w & MOTHERS) domode(op, &m, S_IWOTH); + break; + case 'x': + if (w & MUSER) domode(op, &m, S_IXUSR); + if (w & MGROUP) domode(op, &m, S_IXGRP); + if (w & MOTHERS) domode(op, &m, S_IXOTH); + break; + case 's': + if (w & MUSER) domode(op, &m, S_ISUID); + if (w & MGROUP) domode(op, &m, S_ISGID); + break; + case 't': + domode(op, &m, S_ISVTX); + } + p++; + } + if (*p) { + if (*p == ',') + p++; + else + fatal("garbage at end of mode string: ", p); + } + } + *pl = m; +} + +struct node * + expr(t) +int t; +{ + struct node *nd, *p, *nd2; + + nd = primary(t); + if ((t = lex(*++ipp)) == OP_OR) { + nd2 = expr(lex(*++ipp)); + p = newnode(OP_OR); + p->n_info.n_opnd.n_left = nd; + p->n_info.n_opnd.n_right = nd2; + return p; + } + ipp--; + return nd; +} + +struct node * + primary(t) +int t; +{ + struct node *nd, *p, *nd2; + + nd = secondary(t); + if ((t = lex(*++ipp)) != OP_AND) { + ipp--; + if (t == EOI || t == RPAR || t == OP_OR) return nd; + } + nd2 = primary(lex(*++ipp)); + p = newnode(OP_AND); + p->n_info.n_opnd.n_left = nd; + p->n_info.n_opnd.n_right = nd2; + return p; +} + +struct node * + secondary(t) +int t; +{ + struct node *n, *p; + + if (t == LPAR) { + n = expr(lex(*++ipp)); + if (lex(*++ipp) != RPAR) fatal("syntax error, ) expected", ""); + return n; + } + if (t == NOT) { + n = secondary(lex(*++ipp)); + p = newnode(NOT); + p->n_info.n_opnd.n_left = n; + return p; + } + return simple(t); +} + +void checkarg(arg) +char *arg; +{ + if (arg == 0) fatal("syntax error, argument expected", ""); +} + +struct node * + simple(t) +int t; +{ + struct node *p = newnode(t); + struct exec *e; + struct stat est; + struct passwd *pw; + struct group *gr; + long l; + int i; + + switch (t) { + case OP_TYPE: + checkarg(*++ipp); + switch (**ipp) { + case 'b': + p->n_info.n_int.n_val = S_IFBLK; + break; + case 'c': + p->n_info.n_int.n_val = S_IFCHR; + break; + case 'd': + p->n_info.n_int.n_val = S_IFDIR; + break; + case 'f': + p->n_info.n_int.n_val = S_IFREG; + break; + case 'l': +#ifdef S_IFLNK + p->n_info.n_int.n_val = S_IFLNK; +#else + p->n_info.n_int.n_val = ~0; /* Always unequal. */ +#endif + break; + default: + fatal("-type needs b, c, d, f or l", ""); + } + break; + case OP_USER: + checkarg(*++ipp); + if (((pw = getpwnam(*ipp)) == NULL) + && isnumber(*ipp, 10, 0)) + number(*ipp, 10, &(p->n_info.n_int.n_val), + &(p->n_info.n_int.n_sign)); + else { + if (pw == NULL) + fatal("unknown user: ", *ipp); + p->n_info.n_int.n_val = pw->pw_uid; + p->n_info.n_int.n_sign = 0; + } + break; + case OP_GROUP: + checkarg(*++ipp); + if (((gr = getgrnam(*ipp)) == NULL) + && isnumber(*ipp, 10, 0)) + number(*ipp, 10, &(p->n_info.n_int.n_val), + &(p->n_info.n_int.n_sign)); + else { + if (gr == NULL) + fatal("unknown group: ", *ipp); + p->n_info.n_int.n_val = gr->gr_gid; + p->n_info.n_int.n_sign = 0; + } + break; + case OP_SIZE: + checkarg(*++ipp); + i = strlen(*ipp) - 1; + if ((*ipp)[i] == 'c') { + p->n_type = OP_SIZEC; /* Count in bytes i.s.o. blocks */ + (*ipp)[i] = '\0'; + } + number(*ipp, 10, &(p->n_info.n_int.n_val), + &(p->n_info.n_int.n_sign)); + break; + case OP_LINKS: + case OP_INUM: + checkarg(*++ipp); + number(*ipp, 10, &(p->n_info.n_int.n_val), + &(p->n_info.n_int.n_sign)); + break; + case OP_PERM: + checkarg(*++ipp); + if (isnumber(*ipp, 8, 1)) number(*ipp, 8, &(p->n_info.n_int.n_val), + &(p->n_info.n_int.n_sign)); + else + fmode(*ipp, &(p->n_info.n_int.n_val), + &(p->n_info.n_int.n_sign)); + break; + case OP_ATIME: + case OP_CTIME: + case OP_MTIME: + checkarg(*++ipp); + number(*ipp, 10, &l, &(p->n_info.n_int.n_sign)); + p->n_info.n_int.n_val = current_time - l * SECS_PER_DAY; + /* More than n days old means less than the absolute time */ + p->n_info.n_int.n_sign *= -1; + break; + case OP_EXEC: + case OP_OK: + checkarg(*++ipp); + e = (struct exec *) Malloc(sizeof(struct exec)); + e->e_cnt = 2; + e->e_vec[0] = SHELL; + p->n_info.n_exec = e; + while (*ipp) { + if (**ipp == ';' && (*ipp)[1] == '\0') { + e->e_vec[e->e_cnt] = 0; + break; + } + e->e_vec[(e->e_cnt)++] = + (**ipp == '{' && (*ipp)[1] == '}' + && (*ipp)[2] == '\0') ? (char *) (-1) : *ipp; + ipp++; + } + if (*ipp == 0) fatal("-exec/-ok: ; missing", ""); + if ((e->e_vec[1] = find_bin(e->e_vec[2])) == 0) + fatal("can't find program ", e->e_vec[2]); + if (t == OP_OK) + if ((tty = open("/dev/tty", O_RDWR)) < 0) + fatal("can't open /dev/tty", ""); + break; + case OP_NEWER: + checkarg(*++ipp); + if (LSTAT(*ipp, &est) == -1) + fatal("-newer: can't get status of ", *ipp); + p->n_info.n_int.n_val = est.st_mtime; + break; + case OP_NAME: + checkarg(*++ipp); + p->n_info.n_str = *ipp; + break; + case OP_XDEV: xdev_flag = 1; break; + case OP_DEPTH: depth_flag = 1; break; + case OP_PRUNE: + case OP_PRINT: + case OP_PRINT0: + case OP_NOUSER: case OP_NOGROUP: break; + default: + fatal("syntax error, operator expected", ""); + } + if ((t == OP_PRINT) || (t == OP_PRINT0) || (t == OP_EXEC) || (t == OP_OK)) + needprint = 0; + + return p; +} + +/*######################## DIAGNOSTICS ##############################*/ + +void nonfatal(s1, s2) +char *s1, *s2; +{ + fprintf(stderr, "%s: %s%s\n", prog, s1, s2); +} + +void fatal(s1, s2) +char *s1, *s2; +{ + nonfatal(s1, s2); + exit(1); +} + +/*################### SMATCH #########################*/ +/* Don't try to understand the following one... */ +int smatch(s, t) /* shell-like matching */ +char *s, *t; +{ + register n; + + if (*t == '\0') return *s == '\0'; + if (*t == '*') { + ++t; + do + if (smatch(s, t)) return 1; + while (*s++ != '\0'); + return 0; + } + if (*s == '\0') return 0; + if (*t == '\\') return (*s == *++t) ? smatch(++s, ++t) : 0; + if (*t == '?') return smatch(++s, ++t); + if (*t == '[') { + while (*++t != ']') { + if (*t == '\\') ++t; + if (*(t + 1) != '-') + if (*t == *s) { + while (*++t != ']') + if (*t == '\\') ++t; + return smatch(++s, ++t); + } else + continue; + if (*(t + 2) == ']') return(*s == *t || *s == '-'); + n = (*(t + 2) == '\\') ? 3 : 2; + if (*s >= *t && *s <= *(t + n)) { + while (*++t != ']') + if (*t == '\\') ++t; + return smatch(++s, ++t); + } + t += n; + } + return 0; + } + return(*s == *t) ? smatch(++s, ++t) : 0; +} + +/*####################### EXECUTE ###########################*/ +/* Do -exec or -ok */ + +char * + find_bin(s) +char *s; +{ + char *f, *l, buf[PATH_MAX]; + + if (*s == '/') /* absolute path name */ + return(access(s, 1) == 0) ? s : 0; + l = f = epath; + for (;;) { + if (*l == ':' || *l == 0) { + if (l == f) { + if (access(s, 1) == 0) return Salloc(s); + f++; + } else { + register char *p = buf, *q = s; + + while (f != l) *p++ = *f++; + f++; + *p++ = '/'; + while (*p++ = *q++) { + } + if (access(buf, 1) == 0) return Salloc(buf); + } + if (*l == 0) break; + } + l++; + } + return 0; +} + +int execute(op, e, path) +int op; +struct exec *e; +char *path; +{ + int s, pid; + char *argv[MAXARG]; + register char **p, **q; + + for (p = e->e_vec, q = argv; *p;) /* replace the {}s */ + if ((*q++ = *p++) == (char *) -1) q[-1] = path; + *q = '\0'; + if (op == OP_OK) { + char answer[10]; + + for (p = &argv[2]; *p; p++) { + write(tty, *p, strlen(*p)); + write(tty, " ", 1); + } + write(tty, "? ", 2); + if (read(tty, answer, 10) < 2 || *answer != 'y') return 0; + } + if ((pid = fork()) == -1) fatal("can't fork", ""); + if (pid == 0) { + register i = 3; + + while (close(i++) == 0) { + } /* wow!!! */ + execv(argv[1], &argv[2]); /* binary itself? */ + execv(argv[0], &argv[1]); /* shell command? */ + fatal("exec failure: ", argv[1]); /* none of them! */ + exit(127); + } + return wait(&s) == pid && s == 0; +} diff --git a/commands/simple/finger.c b/commands/simple/finger.c new file mode 100755 index 000000000..60c29546f --- /dev/null +++ b/commands/simple/finger.c @@ -0,0 +1,1091 @@ +/* + * Copyright (c) 1980 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#ifndef lint +static char sccsid[] = "@(#)finger.c 1.1 87/12/21 SMI"; /* from 5.8 3/13/86 */ +#endif /* not lint */ + +/* + * This is a finger program. It prints out useful information about users + * by digging it up from various system files. + * + * There are three output formats, all of which give login name, teletype + * line number, and login time. The short output format is reminiscent + * of finger on ITS, and gives one line of information per user containing + * in addition to the minimum basic requirements (MBR), the full name of + * the user, his idle time and location. The + * quick style output is UNIX who-like, giving only name, teletype and + * login time. Finally, the long style output give the same information + * as the short (in more legible format), the home directory and shell + * of the user, and, if it exits, a copy of the file .plan in the users + * home directory. Finger may be called with or without a list of people + * to finger -- if no list is given, all the people currently logged in + * are fingered. + * + * The program is validly called by one of the following: + * + * finger {short form list of users} + * finger -l {long form list of users} + * finger -b {briefer long form list of users} + * finger -q {quick list of users} + * finger -i {quick list of users with idle times} + * finger namelist {long format list of specified users} + * finger -s namelist {short format list of specified users} + * finger -w namelist {narrow short format list of specified users} + * + * where 'namelist' is a list of users login names. + * The other options can all be given after one '-', or each can have its + * own '-'. The -f option disables the printing of headers for short and + * quick outputs. The -b option briefens long format outputs. The -p + * option turns off plans for long format outputs. + */ + +#include <sys/types.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <utmp.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/netdb.h> +#include <net/gen/socket.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_hdr.h> +#include <net/gen/tcp_io.h> +#include <net/hton.h> +#include <net/netlib.h> + +#define NONOTHING 1 /* don't say "No plan", or "No mail" */ + +#define NONET 0 + +#define ASTERISK '*' /* ignore this in real name */ +#define COMMA ',' /* separator in pw_gecos field */ +#define COMMAND '-' /* command line flag char */ +#define SAMENAME '&' /* repeat login name in real name */ +#define TALKABLE 0220 /* tty is writable if this mode */ + +struct utmp user; +#define NMAX sizeof(user.ut_name) +#define LMAX sizeof(user.ut_line) +#define HMAX sizeof(user.ut_host) + +struct person { /* one for each person fingered */ + char *name; /* name */ + char tty[LMAX+1]; /* null terminated tty line */ + char host[HMAX+1]; /* null terminated remote host name */ + long loginat; /* time of (last) login */ + long idletime; /* how long idle (if logged in) */ + char *realname; /* pointer to full name */ + struct passwd *pwd; /* structure of /etc/passwd stuff */ + char loggedin; /* person is logged in */ + char writable; /* tty is writable */ + char original; /* this is not a duplicate entry */ + struct person *link; /* link to next person */ + char *where; /* terminal location */ + char hostt[HMAX+1]; /* login host */ +}; + +char LASTLOG[] = "/usr/adm/lastlog"; /* last login info */ +char USERLOG[] = "/etc/utmp"; /* who is logged in */ +char PLAN[] = "/.plan"; /* what plan file is */ +char PROJ[] = "/.project"; /* what project file */ + +int unbrief = 1; /* -b option default */ +int header = 1; /* -f option default */ +int hack = 1; /* -h option default */ +int idle = 0; /* -i option default */ +int large = 0; /* -l option default */ +int match = 1; /* -m option default */ +int plan = 1; /* -p option default */ +int unquick = 1; /* -q option default */ +int small = 0; /* -s option default */ +int wide = 1; /* -w option default */ + +int unshort; +int lf; /* LASTLOG file descriptor */ +struct person *person1; /* list of people */ +long tloc; /* current time */ + +#if !_MINIX +char *strcpy(); +char *ctime(); +#endif + +char *prog_name; + +int main (int argc, char *argv[]); +static void doall(void); +static void donames(char **args); +static void print(void); +static void fwopen(void); +static void decode(struct person *pers); +static void fwclose(void); +static int netfinger (char *name); +static int matchcmp (char *gname, char *login, char *given); +static void quickprint (struct person *pers); +static void shortprint (struct person *pers); +static void personprint (struct person *pers); +static int AlreadyPrinted(int uid); +static int AnyMail (char *name); +static struct passwd *pwdcopy(struct passwd *pfrom); +static void findidle (struct person *pers); +static int ltimeprint (char *dt, long *before, char *after); +static void stimeprint (long *dt); +static void findwhen (struct person *pers); +static int namecmp (char *name1, char *name2); + +main(argc, argv) + int argc; + register char **argv; +{ + FILE *fp; + register char *s; + + prog_name= argv[0]; + + /* parse command line for (optional) arguments */ + while (*++argv && **argv == COMMAND) + for (s = *argv + 1; *s; s++) + switch (*s) { + case 'b': + unbrief = 0; + break; + case 'f': + header = 0; + break; + case 'h': + hack = 0; + break; + case 'i': + idle = 1; + unquick = 0; + break; + case 'l': + large = 1; + break; + case 'm': + match = 0; + break; + case 'p': + plan = 0; + break; + case 'q': + unquick = 0; + break; + case 's': + small = 1; + break; + case 'w': + wide = 0; + break; + default: + fprintf(stderr, "Usage: finger [-bfhilmpqsw] [login1 [login2 ...] ]\n"); + exit(1); + } + if (unquick || idle) + time(&tloc); + /* + * *argv == 0 means no names given + */ + if (*argv == 0) + doall(); + else + donames(argv); + if (person1) + print(); + exit(0); +} + +static void doall() +{ + register struct person *p; + register struct passwd *pw; + int uf; + char name[NMAX + 1]; + + unshort = large; + if ((uf = open(USERLOG, 0)) < 0) { + fprintf(stderr, "finger: error opening %s\n", USERLOG); + exit(2); + } + if (unquick) { + setpwent(); + fwopen(); + } + while (read(uf, (char *)&user, sizeof user) == sizeof user) { + if (user.ut_name[0] == 0) + continue; + if (person1 == 0) + p = person1 = (struct person *) malloc(sizeof *p); + else { + p->link = (struct person *) malloc(sizeof *p); + p = p->link; + } + bcopy(user.ut_name, name, NMAX); + name[NMAX] = 0; + bcopy(user.ut_line, p->tty, LMAX); + p->tty[LMAX] = 0; + bcopy(user.ut_host, p->host, HMAX); + p->host[HMAX] = 0; + p->loginat = user.ut_time; + p->pwd = 0; + p->loggedin = 1; + p->where = NULL; + if (unquick && (pw = getpwnam(name))) { + p->pwd = pwdcopy(pw); + decode(p); + p->name = p->pwd->pw_name; + } else + p->name = strcpy(malloc(strlen(name) + 1), name); + } + if (unquick) { + fwclose(); + endpwent(); + } + close(uf); + if (person1 == 0) { + printf("No one logged on\n"); + return; + } + p->link = 0; +} + +static void donames(argv) + char **argv; +{ + register struct person *p; + register struct passwd *pw; + int uf; + + /* + * get names from command line and check to see if they're + * logged in + */ + unshort = !small; + for (; *argv != 0; argv++) { + if (netfinger(*argv)) + continue; + if (person1 == 0) + p = person1 = (struct person *) malloc(sizeof *p); + else { + p->link = (struct person *) malloc(sizeof *p); + p = p->link; + } + p->name = *argv; + p->loggedin = 0; + p->original = 1; + p->pwd = 0; + } + if (person1 == 0) + return; + p->link = 0; + /* + * if we are doing it, read /etc/passwd for the useful info + */ + if (unquick) { + setpwent(); + if (!match) { + for (p = person1; p != 0; p = p->link) + if (pw = getpwnam(p->name)) + p->pwd = pwdcopy(pw); + } else while ((pw = getpwent()) != 0) { + for (p = person1; p != 0; p = p->link) { + if (!p->original) + continue; + if (strcmp(p->name, pw->pw_name) != 0 && + !matchcmp(pw->pw_gecos, pw->pw_name, p->name)) + continue; + if (p->pwd == 0) + p->pwd = pwdcopy(pw); + else { + struct person *new; + /* + * handle multiple login names, insert + * new "duplicate" entry behind + */ + new = (struct person *) + malloc(sizeof *new); + new->pwd = pwdcopy(pw); + new->name = p->name; + new->original = 1; + new->loggedin = 0; + new->link = p->link; + p->original = 0; + p->link = new; + p = new; + } + } + } + endpwent(); + } + /* Now get login information */ + if ((uf = open(USERLOG, 0)) < 0) { + fprintf(stderr, "finger: error opening %s\n", USERLOG); + exit(2); + } + while (read(uf, (char *)&user, sizeof user) == sizeof user) { + if (*user.ut_name == 0) + continue; + for (p = person1; p != 0; p = p->link) { + if (p->loggedin == 2) + continue; + if (strncmp(p->pwd ? p->pwd->pw_name : p->name, + user.ut_name, NMAX) != 0) + continue; + if (p->loggedin == 0) { + bcopy(user.ut_line, p->tty, LMAX); + p->tty[LMAX] = 0; + bcopy(user.ut_host, p->host, HMAX); + p->host[HMAX] = 0; + p->loginat = user.ut_time; + p->loggedin = 1; + } else { /* p->loggedin == 1 */ + struct person *new; + new = (struct person *) malloc(sizeof *new); + new->name = p->name; + bcopy(user.ut_line, new->tty, LMAX); + new->tty[LMAX] = 0; + bcopy(user.ut_host, new->host, HMAX); + new->host[HMAX] = 0; + new->loginat = user.ut_time; + new->pwd = p->pwd; + new->loggedin = 1; + new->original = 0; + new->link = p->link; + p->loggedin = 2; + p->link = new; + p = new; + } + } + } + close(uf); + if (unquick) { + fwopen(); + for (p = person1; p != 0; p = p->link) + decode(p); + fwclose(); + } +} + +static void print() +{ + register FILE *fp; + register struct person *p; + register char *s; + register c; + + /* + * print out what we got + */ + if (header) { + if (unquick) { + if (!unshort) + if (wide) + printf("Login Name TTY Idle When Where\n"); + else + printf("Login TTY Idle When Where\n"); + } else { + printf("Login TTY When"); + if (idle) + printf(" Idle"); + putchar('\n'); + } + } + for (p = person1; p != 0; p = p->link) { + if (!unquick) { + quickprint(p); + continue; + } + if (!unshort) { + shortprint(p); + continue; + } + personprint(p); + if (p->pwd != 0 && !AlreadyPrinted(p->pwd->pw_uid)) { + AnyMail(p->pwd->pw_name); + if (hack) { + s = malloc(strlen(p->pwd->pw_dir) + + sizeof PROJ); + strcpy(s, p->pwd->pw_dir); + strcat(s, PROJ); + if ((fp = fopen(s, "r")) != 0) { + printf("Project: "); + while ((c = getc(fp)) != EOF) { + if (c == '\n') + break; + if (isprint(c) || isspace(c)) + putchar(c); + else + putchar(c ^ 100); + } + fclose(fp); + putchar('\n'); + } + free(s); + } + if (plan) { + s = malloc(strlen(p->pwd->pw_dir) + + sizeof PLAN); + strcpy(s, p->pwd->pw_dir); + strcat(s, PLAN); + if ((fp = fopen(s, "r")) == 0) { + if (!NONOTHING) printf("No Plan.\n"); + } else { + printf("Plan:\n"); + while ((c = getc(fp)) != EOF) + if (isprint(c) || isspace(c)) + putchar(c); + else + putchar(c ^ 100); + fclose(fp); + } + free(s); + } + } + if (p->link != 0) + putchar('\n'); + } +} + +/* + * Duplicate a pwd entry. + * Note: Only the useful things (what the program currently uses) are copied. + */ +static struct passwd * +pwdcopy(pfrom) + register struct passwd *pfrom; +{ + register struct passwd *pto; + + pto = (struct passwd *) malloc(sizeof *pto); +#define savestr(s) strcpy(malloc(strlen(s) + 1), s) + pto->pw_name = savestr(pfrom->pw_name); + pto->pw_uid = pfrom->pw_uid; + pto->pw_gecos = savestr(pfrom->pw_gecos); + pto->pw_dir = savestr(pfrom->pw_dir); + pto->pw_shell = savestr(pfrom->pw_shell); +#undef savestr + return pto; +} + +/* + * print out information on quick format giving just name, tty, login time + * and idle time if idle is set. + */ +static void quickprint(pers) + register struct person *pers; +{ + printf("%-*.*s ", NMAX, NMAX, pers->name); + if (pers->loggedin) { + if (idle) { + findidle(pers); + printf("%c%-*s %-16.16s", pers->writable ? ' ' : '*', + LMAX, pers->tty, ctime(&pers->loginat)); + ltimeprint(" ", &pers->idletime, ""); + } else + printf(" %-*s %-16.16s", LMAX, + pers->tty, ctime(&pers->loginat)); + putchar('\n'); + } else + printf(" Not Logged In\n"); +} + +/* + * print out information in short format, giving login name, full name, + * tty, idle time, login time, and host. + */ +static void shortprint(pers) + register struct person *pers; +{ + char *p; + char dialup; + + if (pers->pwd == 0) { + printf("%-15s ???\n", pers->name); + return; + } + printf("%-*s", NMAX, pers->pwd->pw_name); + dialup = 0; + if (wide) { + if (pers->realname) + printf(" %-20.20s", pers->realname); + else + printf(" ??? "); + } + putchar(' '); + if (pers->loggedin && !pers->writable) + putchar('*'); + else + putchar(' '); + if (*pers->tty) { + if (pers->tty[0] == 't' && pers->tty[1] == 't' && + pers->tty[2] == 'y') { + if (pers->tty[3] == 'd' && pers->loggedin) + dialup = 1; + printf("%-2.2s ", pers->tty + 3); + } else + printf("%-2.2s ", pers->tty); + } else + printf(" "); + p = ctime(&pers->loginat); + if (pers->loggedin) { + stimeprint(&pers->idletime); + printf(" %3.3s %-5.5s ", p, p + 11); + } else if (pers->loginat == 0) + printf(" < . . . . >"); + else if (tloc - pers->loginat >= 180L * 24 * 60 * 60) + printf(" <%-6.6s, %-4.4s>", p + 4, p + 20); + else + printf(" <%-12.12s>", p + 4); + if (pers->host[0]) + printf(" %-20.20s", pers->host); + putchar('\n'); +} + + +/* + * print out a person in long format giving all possible information. + * directory and shell are inhibited if unbrief is clear. + */ +static void +personprint(pers) + register struct person *pers; +{ + if (pers->pwd == 0) { + printf("Login name: %-10s\t\t\tIn real life: ???\n", + pers->name); + return; + } + printf("Login name: %-10s", pers->pwd->pw_name); + if (pers->loggedin && !pers->writable) + printf(" (messages off) "); + else + printf(" "); + if (pers->realname) + printf("In real life: %s", pers->realname); + if (unbrief) { + printf("\nDirectory: %-25s", pers->pwd->pw_dir); + if (*pers->pwd->pw_shell) + printf("\tShell: %-s", pers->pwd->pw_shell); + } + if (pers->loggedin) { + register char *ep = ctime(&pers->loginat); + if (*pers->host) { + printf("\nOn since %15.15s on %s from %s", + &ep[4], pers->tty, pers->host); + ltimeprint("\n", &pers->idletime, " Idle Time"); + } else { + printf("\nOn since %15.15s on %-*s", + &ep[4], LMAX, pers->tty); + ltimeprint("\t", &pers->idletime, " Idle Time"); + } + } else if (pers->loginat == 0) { + if (lf >= 0) printf("\nNever logged in."); + } else if (tloc - pers->loginat > 180L * 24 * 60 * 60) { + register char *ep = ctime(&pers->loginat); + printf("\nLast login %10.10s, %4.4s on %s", + ep, ep+20, pers->tty); + if (*pers->host) + printf(" from %s", pers->host); + } else { + register char *ep = ctime(&pers->loginat); + printf("\nLast login %16.16s on %s", ep, pers->tty); + if (*pers->host) + printf(" from %s", pers->host); + } + putchar('\n'); +} + + +/* + * decode the information in the gecos field of /etc/passwd + */ +static void +decode(pers) + register struct person *pers; +{ + char buffer[256]; + register char *bp, *gp, *lp; + int alldigits; + int hasspace; + int len; + + pers->realname = 0; + if (pers->pwd == 0) + return; + gp = pers->pwd->pw_gecos; + bp = buffer; + if (*gp == ASTERISK) + gp++; + while (*gp && *gp != COMMA) /* name */ + if (*gp == SAMENAME) { + lp = pers->pwd->pw_name; + if (islower(*lp)) + *bp++ = toupper(*lp++); + while (*bp++ = *lp++) + ; + bp--; + gp++; + } else + *bp++ = *gp++; + *bp++ = 0; + if ((len = bp - buffer) > 1) + pers->realname = strcpy(malloc(len), buffer); + if (pers->loggedin) + findidle(pers); + else + findwhen(pers); +} + +/* + * find the last log in of a user by checking the LASTLOG file. + * the entry is indexed by the uid, so this can only be done if + * the uid is known (which it isn't in quick mode) + */ + +static void +fwopen() +{ + if ((lf = open(LASTLOG, 0)) < 0) { + if (errno == ENOENT) return; + fprintf(stderr, "finger: %s open error\n", LASTLOG); + } +} + +static void +findwhen(pers) + register struct person *pers; +{ + struct utmp ll; +#define ll_line ut_line +#define ll_host ut_host +#define ll_time ut_time + + int i; + + if (lf >= 0) { + lseek(lf, (long)pers->pwd->pw_uid * sizeof ll, 0); + if ((i = read(lf, (char *)&ll, sizeof ll)) == sizeof ll) { + bcopy(ll.ll_line, pers->tty, LMAX); + pers->tty[LMAX] = 0; + bcopy(ll.ll_host, pers->host, HMAX); + pers->host[HMAX] = 0; + pers->loginat = ll.ll_time; + } else { + if (i != 0) + fprintf(stderr, "finger: %s read error\n", + LASTLOG); + pers->tty[0] = 0; + pers->host[0] = 0; + pers->loginat = 0L; + } + } else { + pers->tty[0] = 0; + pers->host[0] = 0; + pers->loginat = 0L; + } +} + +static void fwclose() +{ + if (lf >= 0) + close(lf); +} + +/* + * find the idle time of a user by doing a stat on /dev/tty??, + * where tty?? has been gotten from USERLOG, supposedly. + */ +static void +findidle(pers) + register struct person *pers; +{ + struct stat ttystatus; + static char buffer[20] = "/dev/"; + long t; +#define TTYLEN 5 + + strcpy(buffer + TTYLEN, pers->tty); + buffer[TTYLEN+LMAX] = 0; + if (stat(buffer, &ttystatus) < 0) { + fprintf(stderr, "finger: Can't stat %s\n", buffer); + exit(4); + } + time(&t); + if (t < ttystatus.st_atime) + pers->idletime = 0L; + else + pers->idletime = t - ttystatus.st_atime; + pers->writable = (ttystatus.st_mode & TALKABLE) == TALKABLE; +} + +/* + * print idle time in short format; this program always prints 4 characters; + * if the idle time is zero, it prints 4 blanks. + */ +static void +stimeprint(dt) + long *dt; +{ + register struct tm *delta; + + delta = gmtime(dt); + if (delta->tm_yday == 0) + if (delta->tm_hour == 0) + if (delta->tm_min == 0) + printf(" "); + else + printf(" %2d", delta->tm_min); + else + if (delta->tm_hour >= 10) + printf("%3d:", delta->tm_hour); + else + printf("%1d:%02d", + delta->tm_hour, delta->tm_min); + else + printf("%3dd", delta->tm_yday); +} + +/* + * print idle time in long format with care being taken not to pluralize + * 1 minutes or 1 hours or 1 days. + * print "prefix" first. + */ +static int +ltimeprint(before, dt, after) + long *dt; + char *before, *after; +{ + register struct tm *delta; + + delta = gmtime(dt); + if (delta->tm_yday == 0 && delta->tm_hour == 0 && delta->tm_min == 0 && + delta->tm_sec <= 10) + return (0); + printf("%s", before); + if (delta->tm_yday >= 10) + printf("%d days", delta->tm_yday); + else if (delta->tm_yday > 0) + printf("%d day%s %d hour%s", + delta->tm_yday, delta->tm_yday == 1 ? "" : "s", + delta->tm_hour, delta->tm_hour == 1 ? "" : "s"); + else + if (delta->tm_hour >= 10) + printf("%d hours", delta->tm_hour); + else if (delta->tm_hour > 0) + printf("%d hour%s %d minute%s", + delta->tm_hour, delta->tm_hour == 1 ? "" : "s", + delta->tm_min, delta->tm_min == 1 ? "" : "s"); + else + if (delta->tm_min >= 10) + printf("%2d minutes", delta->tm_min); + else if (delta->tm_min == 0) + printf("%2d seconds", delta->tm_sec); + else + printf("%d minute%s %d second%s", + delta->tm_min, + delta->tm_min == 1 ? "" : "s", + delta->tm_sec, + delta->tm_sec == 1 ? "" : "s"); + printf("%s", after); +} + +static int +matchcmp(gname, login, given) + register char *gname; + char *login; + char *given; +{ + char buffer[100]; + register char *bp, *lp; + register c; + + if (*gname == ASTERISK) + gname++; + lp = 0; + bp = buffer; + for (;;) + switch (c = *gname++) { + case SAMENAME: + for (lp = login; bp < buffer + sizeof buffer + && (*bp++ = *lp++);) + ; + bp--; + break; + case ' ': + case COMMA: + case '\0': + *bp = 0; + if (namecmp(buffer, given)) + return (1); + if (c == COMMA || c == 0) + return (0); + bp = buffer; + break; + default: + if (bp < buffer + sizeof buffer) + *bp++ = c; + } + /*NOTREACHED*/ +} + +static int +namecmp(name1, name2) + register char *name1, *name2; +{ + register c1, c2; + + for (;;) { + c1 = *name1++; + if (islower(c1)) + c1 = toupper(c1); + c2 = *name2++; + if (islower(c2)) + c2 = toupper(c2); + if (c1 != c2) + break; + if (c1 == 0) + return (1); + } + if (!c1) { + for (name2--; isdigit(*name2); name2++) + ; + if (*name2 == 0) + return (1); + } else if (!c2) { + for (name1--; isdigit(*name1); name1++) + ; + if (*name2 == 0) + return (1); + } + return (0); +} + +#if NONET +static int +netfinger(name) +char *name; +{ + return 0; +} +#else +static int +netfinger(name) + char *name; +{ + char *host; + char fname[100]; + struct hostent *hp; + struct servent *sp; + int s, result; +#if !_MINIX + char *rindex(); +#endif + register FILE *f; + register int c; + register int lastc; + nwio_tcpconf_t tcpconf; + nwio_tcpcl_t tcpconnopt; + char *tcp_device; + + if (name == NULL) + return (0); + host = rindex(name, '@'); + if (host == NULL) + return (0); + *host++ = 0; + hp = gethostbyname(host); + if (hp == NULL) { + static struct hostent def; + static ipaddr_t defaddr; + static char namebuf[128]; + + defaddr = inet_addr(host); + if (defaddr == -1) { + printf("unknown host: %s\n", host); + return (1); + } + strcpy(namebuf, host); + def.h_name = namebuf; + def.h_addr = (char *)&defaddr; + def.h_length = sizeof (ipaddr_t); + def.h_addrtype = AF_INET; + def.h_aliases = 0; + hp = &def; + } + printf("[%s] ", hp->h_name); + fflush(stdout); + + tcp_device= getenv("TCP_DEVICE"); + if (tcp_device == NULL) + tcp_device= TCP_DEVICE; + s= open (tcp_device, O_RDWR); + if (s == -1) + { + fprintf(stderr, "%s: unable to open %s (%s)\n", + prog_name, tcp_device, strerror(errno)); + exit(1); + } + tcpconf.nwtc_flags= NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; + tcpconf.nwtc_remaddr= *(ipaddr_t *)hp->h_addr; + tcpconf.nwtc_remport= htons(TCPPORT_FINGER); + + result= ioctl (s, NWIOSTCPCONF, &tcpconf); + if (result<0) + { + fprintf(stderr, "%s\n", strerror(errno)); + exit(1); + } + + tcpconnopt.nwtcl_flags= 0; + + do + { + result= ioctl (s, NWIOTCPCONN, &tcpconnopt); + if (result<0 && errno== EAGAIN) + { + fprintf(stderr, "got EAGAIN error, sleeping 2s\n"); + sleep(2); + } + } while (result<0 && errno == EAGAIN); + if (result<0) + { + fprintf(stderr, "%s\n", strerror(errno)); + exit(1); + } + printf("\r\n"); + if (large) write(s, "/W ", 3); + write(s, name, strlen(name)); + write(s, "\r\n", 2); + f = fdopen(s, "r"); + while ((c = getc(f)) != EOF) { +/* + switch(c) { + case 0210: + case 0211: + case 0212: + case 0214: + c -= 0200; + break; + case 0215: + c = '\n'; + break; + } +*/ + c &= ~0200; + if (c == '\r') + { + c= getc(f) & ~0200; + if (c == '\012') + { + lastc= c; + putchar('\n'); + continue; + } + else + putchar('\r'); + } + lastc = c; + if (isprint(c) || isspace(c)) + putchar(c); + else + putchar(c ^ 100); + } + if (lastc != '\n') + putchar('\n'); + (void)fclose(f); + return (1); +} +#endif + +/* + * AnyMail - takes a username (string pointer thereto), and + * prints on standard output whether there is any unread mail, + * and if so, how old it is. (JCM@Shasta 15 March 80) + */ +#define preamble "/usr/spool/mail/" /* mailboxes are there */ +static int +AnyMail(name) +char *name; +{ + struct stat buf; /* space for file status buffer */ + char *mbxdir = preamble; /* string with path preamble */ + char *mbxpath; /* space for entire pathname */ + +#if !_MINIX + char *ctime(); /* convert longword time to ascii */ +#endif + char *timestr; + + mbxpath = malloc(strlen(name) + strlen(preamble) + 1); + + strcpy(mbxpath, mbxdir); /* copy preamble into path name */ + strcat(mbxpath, name); /* concatenate user name to path */ + + if (stat(mbxpath, &buf) == -1 || buf.st_size == 0) { + /* Mailbox is empty or nonexistent */ + if (!NONOTHING) printf("No unread mail\n"); + } else { + if (buf.st_mtime == buf.st_atime) { + /* There is something in the mailbox, but we can't really + * be sure whether it is mail held there by the user + * or a (single) new message that was placed in a newly + * recreated mailbox, so we punt and call it "unread mail." + */ + printf("Unread mail since "); + printf(ctime(&buf.st_mtime)); + } else { + /* New mail has definitely arrived since the last time + * mail was read. mtime is the time the most recent + * message arrived; atime is either the time the oldest + * unread message arrived, or the last time the mail + * was read. + */ + printf("New mail received "); + timestr = ctime(&buf.st_mtime); /* time last modified */ + timestr[24] = '\0'; /* suppress newline (ugh) */ + printf(timestr); + printf(";\n unread since "); + printf(ctime(&buf.st_atime)); /* time last accessed */ + } + } + + free(mbxpath); +} + +/* + * return true iff we've already printed project/plan for this uid; + * if not, enter this uid into table (so this function has a side-effect.) + */ +#define PPMAX 200 /* assume no more than 200 logged-in users */ +int PlanPrinted[PPMAX+1]; +int PPIndex = 0; /* index of next unused table entry */ + +static int +AlreadyPrinted(uid) +int uid; +{ + int i = 0; + + while (i++ < PPIndex) { + if (PlanPrinted[i] == uid) + return(1); + } + if (i < PPMAX) { + PlanPrinted[i] = uid; + PPIndex++; + } + return(0); +} diff --git a/commands/simple/fix.c b/commands/simple/fix.c new file mode 100755 index 000000000..46541f746 --- /dev/null +++ b/commands/simple/fix.c @@ -0,0 +1,224 @@ +/* fix file difflist - update file from difflist Author: Erik Baalbergen */ + + +/* Notes: files old and old.patch are equal after the following commands + diff old new > difflist + patch old difflist > old.patch + * the diff output is assumed to be produced by my diff program. + * the difflist has the following form: + difflist ::= chunk* + chunk ::= append | delete | change ; + append ::= n1 'a' n2 [',' n3]? '\n' ['> ' line '\n'](n3 - n2 + 1) + delete ::= n1 [',' n2]? 'd' n3 '\n' ['< ' line '\n'](n2 - n1 + 1) + change ::= n1 [',' n2]? 'c' n3 [',' n4]? '\n' + ['< ' line '\n'](n2 - n1 + 1) + '---\n' + ['> ' line '\n'](n4 - n3 + 1) + where + - n[1234] is an unsigned integer + - "[pat](expr)" means "(expr) occurences of pat" + - "[pat]?" means "either pat or nothing" + * the information in the diff listing is checked against the file to which + it is applied; an error is printed if there is a conflict +*/ + +#include <ctype.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#define IGNORE_WHITE_SPACE /* This makes it white space insensitive */ + +#ifdef IGNORE_WHITE_SPACE +#define strcmp strwcmp +#endif + +#define LINELEN 1024 + +char *prog = 0, *processing = 0; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(char *getline, (FILE *fp, char *b)); +_PROTOTYPE(char *range, (char *s, int *p1, int *p2)); +_PROTOTYPE(int getcommand, (FILE *fp, int *o1, int *o2, char *pcmd, int *n1, int *n2)); +_PROTOTYPE(void fatal, (char *s, ...)); +_PROTOTYPE(int strwcmp, (char *s1, char *s2)); +_PROTOTYPE(int whitespace, (int ch)); + +char * + getline(fp, b) +FILE *fp; +char *b; +{ + if (fgets(b, LINELEN, fp) == NULL) fatal("unexpected eof"); + + return b; +} + +#define copy(str) printf("%s", str) + +int main(argc, argv) +int argc; +char **argv; +{ + char cmd, *fl, *fd, obuf[LINELEN], nbuf[LINELEN]; + int o1, o2, n1, n2, here; + FILE *fpf, *fpd; + + prog = argv[0]; + processing = argv[1]; + if (argc != 3) fatal("use: %s original-file diff-list-file", prog); + if ((fpf = fopen(argv[1], "r")) == NULL) fatal("can't read %s", argv[1]); + if ((fpd = fopen(argv[2], "r")) == NULL) fatal("can't read %s", argv[2]); + here = 0; + while (getcommand(fpd, &o1, &o2, &cmd, &n1, &n2)) { + while (here < o1 - 1) { + here++; + copy(getline(fpf, obuf)); + } + switch (cmd) { + case 'c': + case 'd': + if (cmd == 'd' && n1 != n2) fatal("delete count conflict"); + while (o1 <= o2) { + fl = getline(fpf, obuf); + here++; + fd = getline(fpd, nbuf); + if (strncmp(fd, "<", (size_t)1)) + fatal("illegal delete line"); + if (strcmp(fl, fd + 2)) + fatal("delete line conflict"); + o1++; + } + if (cmd == 'd') break; + if (strcmp(getline(fpd, nbuf), "---\n")) + fatal("illegal separator in chunk"); + /* FALLTHROUGH */ + case 'a': + if (cmd == 'a') { + if (o1 != o2) fatal("append count conflict"); + copy(getline(fpf, obuf)); + here++; + } + while (n1 <= n2) { + if (strncmp(getline(fpd, nbuf), ">", (size_t)1)) + fatal("illegal append line"); + copy(nbuf + 2); + n1++; + } + break; + } + } + while (fgets(obuf, LINELEN, fpf) != NULL) copy(obuf); + return(0); +} + +char * + range(s, p1, p2) +char *s; +int *p1, *p2; +{ + register int v1 = 0, v2; + + while (isdigit(*s)) v1 = 10 * v1 + *s++ - '0'; + v2 = v1; + if (*s == ',') { + s++; + v2 = 0; + while (isdigit(*s)) v2 = 10 * v2 + *s++ - '0'; + } + if (v1 > v2) fatal("illegal range"); + *p1 = v1; + *p2 = v2; + return s; +} + +int getcommand(fp, o1, o2, pcmd, n1, n2) +FILE *fp; +int *o1, *o2, *n1, *n2; +char *pcmd; +{ + char buf[LINELEN]; + register char *s; + char cmd; + + if ((s = fgets(buf, LINELEN, fp)) == NULL) return 0; + s = range(s, o1, o2); + if ((cmd = *s++) != 'a' && cmd != 'c' && cmd != 'd') + fatal("illegal command"); + s = range(s, n1, n2); + if (*s != '\n' && s[1] != '\0') + fatal("extra characters at end of command: %s", s); + *pcmd = cmd; + return 1; +} + +#ifdef __STDC__ +void fatal(char *s, ...) +{ + va_list args; + + va_start (args, s); + fprintf(stderr, "%s: processing: %s fatal: ", prog, processing); + vfprintf(stderr, s, args); + fprintf(stderr, "\n"); + va_end(args); + exit(1); +} +#else +/* the K&R lib does not have vfprintf */ +void fatal(s, a) +char *s, *a; +{ + fprintf(stderr, "%s: processing: %s fatal: ", prog, processing); + fprintf(stderr, s, a); + fprintf(stderr, "\n"); + exit(1); +} +#endif + +#ifdef IGNORE_WHITE_SPACE + +/* This routine is a white space insensitive version of strcmp. + It is needed for testing things which might have undergone + tab conversion or trailing space removal + Bret Mckee June, 1988 */ + +int strwcmp(s1, s2) +char *s1, *s2; +{ + char *x1 = s1, *x2 = s2; + + /* Remove leading white space */ + while (whitespace(*s1)) s1++; + while (whitespace(*s2)) s2++; + do { + while ((*s1 == *s2) && *s1 && *s2) { + s1++; + s2++; + } + ; /* consume identical characters */ + while (whitespace(*s1)) s1++; + while (whitespace(*s2)) s2++; + } while (*s1 && *s2 && (*s1 == *s2)); + if (*s1 - *s2) + fprintf(stderr, "Failing for (%x)[%s]\n (%x)[%s]\n", + (int) *s1, x1, (int) *s2, x2); + return(*s1 - *s2); +} + +int whitespace(ch) +char ch; +{ + switch (ch) { + case ' ': + case '\n': + case 0x0D: + case '\t': + return(1); + default: return(0); +} +} + +#endif diff --git a/commands/simple/fold.c b/commands/simple/fold.c new file mode 100755 index 000000000..9dbe8c1cb --- /dev/null +++ b/commands/simple/fold.c @@ -0,0 +1,70 @@ +/* fold - folds long lines Author: Terrence W. Holm */ + +/* Usage: fold [ -width ] [ file ... ] */ + +#include <stdlib.h> +#include <stdio.h> + +#define TAB 8 +#define DEFAULT_WIDTH 80 + +int column = 0; /* Current column, retained between files */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void Fold, (FILE *f, int width)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int width = DEFAULT_WIDTH; + int i; + FILE *f; + + if (argc > 1 && argv[1][0] == '-') { + if ((width = atoi(&argv[1][1])) <= 0) { + fprintf(stderr, "Bad number for fold\n"); + exit(1); + } + --argc; + ++argv; + } + if (argc == 1) + Fold(stdin, width); + else { + for (i = 1; i < argc; ++i) { + if ((f = fopen(argv[i], "r")) == NULL) { + perror(argv[i]); + exit(1); + } + Fold(f, width); + fclose(f); + } + } + return(0); +} + + +void Fold(f, width) +FILE *f; +int width; +{ + int c; + + while ((c = getc(f)) != EOF) { + if (c == '\t') + column = (column / TAB + 1) * TAB; + else if (c == '\b') + column = column > 0 ? column - 1 : 0; + else if (c == '\n' || c == '\r') + column = 0; + else + ++column; + + if (column > width) { + putchar('\n'); + column = c == '\t' ? TAB : 1; + } + putchar(c); + } +} diff --git a/commands/simple/fortune.c b/commands/simple/fortune.c new file mode 100755 index 000000000..7f3b6f731 --- /dev/null +++ b/commands/simple/fortune.c @@ -0,0 +1,75 @@ +/* fortune - hand out Chinese fortune cookies Author: Bert Reuling */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <time.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#define COOKIEJAR "/usr/lib/fortune.dat" + +static char *Copyright = "\0Copyright (c) 1990 Bert Reuling"; +static unsigned long seed; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(unsigned long magic, (unsigned long range)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int c1, c2, c3; + struct stat cookie_stat; + FILE *cookie; + + if ((cookie = fopen(COOKIEJAR, "r")) == NULL) { + printf("\nSome things better stay closed.\n - %s\n", argv[0]); + exit (-1); + } + + /* Create seed from : date, time, user-id and process-id. we can't get + * the position of the moon, unfortunately. + */ + seed = time( (time_t *) 0) ^ (long) getuid() ^ (long) getpid(); + + if (stat(COOKIEJAR, &cookie_stat) != 0) { + printf("\nIt furthers one to see the super guru.\n - %s\n", argv[0]); + exit (-1); + } + fseek(cookie, magic((unsigned long) cookie_stat.st_size), 0); /* m ove bu magic... */ + + c2 = c3 = '\n'; + while (((c1 = getc(cookie)) != EOF) && ((c1 != '%') || (c2 != '%') || (c3 != '\n'))) { + c3 = c2; + c2 = c1; + } + + if (c1 == EOF) { + printf("\nSomething unexpected has happened.\n - %s", argv[0]); + exit (-1); + } + + c2 = c3 = '\n'; + while (((c1 = getc(cookie)) != '%') || (c2 != '%') || (c3 != '\n')) { + if (c1 == EOF) { + rewind(cookie); + continue; + } + putc(c2, stdout); + c3 = c2; + c2 = c1; + } + putc('\n', stdout); + fclose(cookie); + return (0); +} + +/* magic - please study carefull: there is more than meets the eye */ +unsigned long magic(range) +unsigned long range; +{ + + seed = 9065531L * (seed % 9065533L) - 2 * (seed / 9065531L) + 1L; + return (seed % range); +} diff --git a/commands/simple/fsck.c b/commands/simple/fsck.c new file mode 100755 index 000000000..5337393ef --- /dev/null +++ b/commands/simple/fsck.c @@ -0,0 +1,1555 @@ +/* Hacks for version 1.6 */ + +#define INODES_PER_BLOCK V2_INODES_PER_BLOCK(block_size) +#define INODE_SIZE ((int) V2_INODE_SIZE) +#define WORDS_PER_BLOCK (block_size / (int) sizeof(bitchunk_t)) +#define MAX_ZONES (V2_NR_DZONES+V2_INDIRECTS(block_size)+(long)V2_INDIRECTS(block_size)*V2_INDIRECTS(block_size)) +#define NR_DZONE_NUM V2_NR_DZONES +#define NR_INDIRECTS V2_INDIRECTS(block_size) +#define NR_ZONE_NUMS V2_NR_TZONES +#define ZONE_NUM_SIZE V2_ZONE_NUM_SIZE +#define bit_nr bit_t +#define block_nr block_t +#define d_inode d2_inode +#define d_inum d_ino +#define dir_struct struct direct +#define i_mode d2_mode +#define i_nlinks d2_nlinks +#define i_size d2_size +#define i_zone d2_zone +#define zone_nr zone_t + +/* fsck - file system checker Author: Robbert van Renesse */ + +/* Modified by Norbert Schlenker +* Removed vestiges of standalone/DOS versions: +* - various unused variables and buffers removed +* - now uses library functions rather than private internal routines +* - bytewise structure copies replaced by structure assignment +* - fixed one bug with 14 character file names +* - other small tweaks for speed +* +* Modified by Lars Fredriksen at the request of Andy Tanenbaum, 90-03-10. +* Removed -m option, by which fsck could be told to make a file +* system on a 360K floppy. The code had limited utility, was buggy, +* and failed due to a bug in the ACK C compiler. Use mkfs instead! +*/ + +#include <sys/types.h> +#include <sys/dir.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <minix/config.h> +#include <minix/const.h> +#include <minix/type.h> +#include "../../servers/fs/const.h" +#include "../../servers/fs/inode.h" +#include "../../servers/fs/type.h" +#include <minix/fslib.h> +#include <stdio.h> +#include <sys/stat.h> +#include <a.out.h> +#include <tools.h> +#include <dirent.h> + +#undef N_DATA + +unsigned int fs_version = 2, block_size = 0; + +#define BITSHIFT 4 /* = log2(#bits(int)) */ + +#define MAXPRINT 80 /* max. number of error lines in chkmap */ +#define CINDIR 128 /* number of indirect zno's read at a time */ +#define CDIRECT 1 /* number of dir entries read at a time */ + +/* Macros for handling bitmaps. Now bit_t is long, these are bulky and the + * type demotions produce a lot of lint. The explicit demotion in POWEROFBIT + * is for efficiency and assumes 2's complement ints. Lint should be clever + * enough not to warn about it since BITMASK is small, but isn't. (It would + * be easier to get right if bit_t was was unsigned (long) since then there + * would be no danger from wierd sign representations. Lint doesn't know + * we only use non-negative bit numbers.) There will usually be an implicit + * demotion when WORDOFBIT is used as an array index. This should be safe + * since memory for bitmaps will run out first. + */ +#define BITMASK ((1 << BITSHIFT) - 1) +#define WORDOFBIT(b) ((b) >> BITSHIFT) +#define POWEROFBIT(b) (1 << ((int) (b) & BITMASK)) +#define setbit(w, b) (w[WORDOFBIT(b)] |= POWEROFBIT(b)) +#define clrbit(w, b) (w[WORDOFBIT(b)] &= ~POWEROFBIT(b)) +#define bitset(w, b) (w[WORDOFBIT(b)] & POWEROFBIT(b)) + +#define ZONE_CT 360 /* default zones (when making file system) */ +#define INODE_CT 95 /* default inodes (when making file system) */ + +#include "../../servers/fs/super.h" +static struct super_block sb; + +#define STICKY_BIT 01000 /* not defined anywhere else */ + +/* Ztob gives the block address of a zone + * btoa gives the byte address of a block + */ +#define ztob(z) ((block_nr) (z) << sb.s_log_zone_size) +#define btoa(b) ((long) (b) * block_size) +#define SCALE ((int) ztob(1)) /* # blocks in a zone */ +#define FIRST ((zone_nr) sb.s_firstdatazone) /* as the name says */ + +/* # blocks of each type */ +#define N_IMAP (sb.s_imap_blocks) +#define N_ZMAP (sb.s_zmap_blocks) +#define N_ILIST ((sb.s_ninodes+INODES_PER_BLOCK-1) / INODES_PER_BLOCK) +#define N_DATA (sb.s_zones - FIRST) + +/* Block address of each type */ +#define OFFSET_SUPER_BLOCK SUPER_BLOCK_BYTES +#define BLK_IMAP 2 +#define BLK_ZMAP (BLK_IMAP + N_IMAP) +#define BLK_ILIST (BLK_ZMAP + N_ZMAP) +#define BLK_FIRST ztob(FIRST) +#define ZONE_SIZE ((int) ztob(block_size)) +#define NLEVEL (NR_ZONE_NUMS - NR_DZONE_NUM + 1) + +/* Byte address of a zone/of an inode */ +#define zaddr(z) btoa(ztob(z)) +#define cinoaddr(i) ((long) (i - 1) * INODE_SIZE + (long) btoa(BLK_ILIST)) +#define INDCHUNK ((int) (CINDIR * ZONE_NUM_SIZE)) +#define DIRCHUNK ((int) (CDIRECT * DIR_ENTRY_SIZE)) + +char *prog, *fsck_device; /* program name (fsck), device name */ +int firstcnterr; /* is this the first inode ref cnt error? */ +bitchunk_t *imap, *spec_imap; /* inode bit maps */ +bitchunk_t *zmap, *spec_zmap; /* zone bit maps */ +bitchunk_t *dirmap; /* directory (inode) bit map */ +char *rwbuf; /* one block buffer cache */ +block_nr thisblk; /* block in buffer cache */ +char *nullbuf; /* null buffer */ +nlink_t *count; /* inode count */ +int changed; /* has the diskette been written to? */ +struct stack { + dir_struct *st_dir; + struct stack *st_next; + char st_presence; +} *ftop; + +int dev; /* file descriptor of the device */ + +#define DOT 1 +#define DOTDOT 2 + +/* Counters for each type of inode/zone. */ +int nfreeinode, nregular, ndirectory, nblkspec, ncharspec, nbadinode; +int npipe, nsyml, ztype[NLEVEL]; +long nfreezone; + +int repair, automatic, listing, listsuper; /* flags */ +int firstlist; /* has the listing header been printed? */ +unsigned part_offset; /* sector offset for this partition */ +char answer[] = "Answer questions with y or n. Then hit RETURN"; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void initvars, (void)); +_PROTOTYPE(void fatal, (char *s)); +_PROTOTYPE(int eoln, (int c)); +_PROTOTYPE(int yes, (char *question)); +_PROTOTYPE(int atoo, (char *s)); +_PROTOTYPE(int input, (char *buf, int size)); +_PROTOTYPE(char *alloc, (unsigned nelem, unsigned elsize)); +_PROTOTYPE(void printname, (char *s)); +_PROTOTYPE(void printrec, (struct stack *sp)); +_PROTOTYPE(void printpath, (int mode, int nlcr)); +_PROTOTYPE(void devopen, (void)); +_PROTOTYPE(void devclose, (void)); +_PROTOTYPE(void devio, (block_nr bno, int dir)); +_PROTOTYPE(void devread, (long offset, char *buf, int size)); +_PROTOTYPE(void devwrite, (long offset, char *buf, int size)); +_PROTOTYPE(void pr, (char *fmt, int cnt, char *s, char *p)); +_PROTOTYPE(void lpr, (char *fmt, long cnt, char *s, char *p)); +_PROTOTYPE(bit_nr getnumber, (char *s)); +_PROTOTYPE(char **getlist, (char ***argv, char *type)); +_PROTOTYPE(void lsuper, (void)); +_PROTOTYPE(void getsuper, (void)); +_PROTOTYPE(void chksuper, (void)); +_PROTOTYPE(void lsi, (char **clist)); +_PROTOTYPE(bitchunk_t *allocbitmap, (int nblk)); +_PROTOTYPE(void loadbitmap, (bitchunk_t *bitmap, block_nr bno, int nblk)); +_PROTOTYPE(void dumpbitmap, (bitchunk_t *bitmap, block_nr bno, int nblk)); +_PROTOTYPE(void fillbitmap, (bitchunk_t *bitmap, bit_nr lwb, bit_nr upb, char **list)); +_PROTOTYPE(void freebitmap, (bitchunk_t *p)); +_PROTOTYPE(void getbitmaps, (void)); +_PROTOTYPE(void putbitmaps, (void)); +_PROTOTYPE(void chkword, (unsigned w1, unsigned w2, bit_nr bit, char *type, int *n, int *report, bit_t)); +_PROTOTYPE(void chkmap, (bitchunk_t *cmap, bitchunk_t *dmap, bit_nr bit, block_nr blkno, int nblk, char *type)); +_PROTOTYPE(void chkilist, (void)); +_PROTOTYPE(void getcount, (void)); +_PROTOTYPE(void counterror, (Ino_t ino)); +_PROTOTYPE(void chkcount, (void)); +_PROTOTYPE(void freecount, (void)); +_PROTOTYPE(void printperm, (Mode_t mode, int shift, int special, int overlay)); +_PROTOTYPE(void list, (Ino_t ino, d_inode *ip)); +_PROTOTYPE(int Remove, (dir_struct *dp)); +_PROTOTYPE(void make_printable_name, (char *dst, char *src, int n)); +_PROTOTYPE(int chkdots, (Ino_t ino, off_t pos, dir_struct *dp, Ino_t exp)); +_PROTOTYPE(int chkname, (Ino_t ino, dir_struct *dp)); +_PROTOTYPE(int chkentry, (Ino_t ino, off_t pos, dir_struct *dp)); +_PROTOTYPE(int chkdirzone, (Ino_t ino, d_inode *ip, off_t pos, zone_nr zno)); +_PROTOTYPE(void errzone, (char *mess, zone_nr zno, int level, off_t pos)); +_PROTOTYPE(int markzone, (zone_nr zno, int level, off_t pos)); +_PROTOTYPE(int chkindzone, (Ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int level)); +_PROTOTYPE(off_t jump, (int level)); +_PROTOTYPE(int zonechk, (Ino_t ino, d_inode *ip, off_t *pos, zone_nr zno, int level)); +_PROTOTYPE(int chkzones, (Ino_t ino, d_inode *ip, off_t *pos, zone_nr *zlist, int len, int level)); +_PROTOTYPE(int chkfile, (Ino_t ino, d_inode *ip)); +_PROTOTYPE(int chkdirectory, (Ino_t ino, d_inode *ip)); +_PROTOTYPE(int chklink, (Ino_t ino, d_inode *ip)); +_PROTOTYPE(int chkspecial, (Ino_t ino, d_inode *ip)); +_PROTOTYPE(int chkmode, (Ino_t ino, d_inode *ip)); +_PROTOTYPE(int chkinode, (Ino_t ino, d_inode *ip)); +_PROTOTYPE(int descendtree, (dir_struct *dp)); +_PROTOTYPE(void chktree, (void)); +_PROTOTYPE(void printtotal, (void)); +_PROTOTYPE(void chkdev, (char *f, char **clist, char **ilist, char **zlist)); + +/* Initialize the variables used by this program. */ +void initvars() +{ + register level; + + nregular = ndirectory = nblkspec = ncharspec = nbadinode = npipe = nsyml = 0; + for (level = 0; level < NLEVEL; level++) ztype[level] = 0; + changed = 0; + thisblk = NO_BLOCK; + firstlist = 1; + firstcnterr = 1; +} + +/* Print the string `s' and exit. */ +void fatal(s) +char *s; +{ + printf("%s\nfatal\n", s); + exit(-1); +} + +/* Test for end of line. */ +int eoln(c) +int c; +{ + return(c == EOF || c == '\n' || c == '\r'); +} + +/* Ask a question and get the answer unless automatic is set. */ +int yes(question) +char *question; +{ + register int c, answerchar; + + if (!repair) { + printf("\n"); + return(0); + } + printf("%s? ", question); + if (automatic) { + printf("yes\n"); + return(1); + } + fflush(stdout); + if ((c = answerchar = getchar()) == 'q' || c == 'Q') exit(1); + while (!eoln(c)) c = getchar(); + return !(answerchar == 'n' || answerchar == 'N'); +} + +/* Convert string to integer. Representation is octal. */ +int atoo(s) +register char *s; +{ + register int n = 0; + + while ('0' <= *s && *s < '8') { + n <<= 3; + n += *s++ - '0'; + } + return n; +} + +/* If repairing the file system, print a prompt and get a string from user. */ +int input(buf, size) +char *buf; +int size; +{ + register char *p = buf; + + printf("\n"); + if (repair) { + printf("--> "); + fflush(stdout); + while (--size) { + *p = getchar(); + if (eoln(*p)) { + *p = 0; + return(p > buf); + } + p++; + } + *p = 0; + while (!eoln(getchar())); + return(1); + } + return(0); +} + +/* Allocate some memory and zero it. */ +char *alloc(nelem, elsize) +unsigned nelem, elsize; +{ + char *p; + + if ((p = (char *)malloc((size_t)nelem * elsize)) == 0)fatal("out of memory"); + memset((void *) p, 0, (size_t)nelem * elsize); + return(p); +} + +/* Print the name in a directory entry. */ +void printname(s) +char *s; +{ + register n = NAME_MAX; + int c; + + do { + if ((c = *s) == 0) break; + if (!isprint(c)) c = '?'; + putchar(c); + s++; + } while (--n); +} + +/* Print the pathname given by a linked list pointed to by `sp'. The + * names are in reverse order. + */ +void printrec(sp) +struct stack *sp; +{ + if (sp->st_next != 0) { + printrec(sp->st_next); + putchar('/'); + printname(sp->st_dir->d_name); + } +} + +/* Print the current pathname. */ +void printpath(mode, nlcr) +int mode; +int nlcr; +{ + if (ftop->st_next == 0) + putchar('/'); + else + printrec(ftop); + switch (mode) { + case 1: + printf(" (ino = %u, ", ftop->st_dir->d_inum); + break; + case 2: + printf(" (ino = %u)", ftop->st_dir->d_inum); + break; + } + if (nlcr) printf("\n"); +} + +/* Open the device. */ +void devopen() +{ + if ((dev = open(fsck_device, repair ? O_RDWR : O_RDONLY)) < 0) { + perror(fsck_device); + fatal("couldn't open device to fsck"); + } +} + +/* Close the device. */ +void devclose() +{ + if (close(dev) != 0) { + perror("close"); + fatal(""); + } +} + +/* Read or write a block. */ +void devio(bno, dir) +block_nr bno; +int dir; +{ + if(!block_size) fatal("devio() with unknown block size"); + if (dir == READING && bno == thisblk) return; + thisblk = bno; + +#if 0 +printf("%s at block %5d\n", dir == READING ? "reading " : "writing", bno); +#endif + lseek(dev, (off_t) btoa(bno), SEEK_SET); + if (dir == READING) { + if (read(dev, rwbuf, block_size) == block_size) + return; + } else { + if (write(dev, rwbuf, block_size) == block_size) + return; + } + + printf("%s: can't %s block %ld (error = 0x%x)\n", prog, + dir == READING ? "read" : "write", (long) bno, errno); + if (dir == READING) { + printf("Continuing with a zero-filled block.\n"); + memset(rwbuf, 0, block_size); + return; + } + fatal(""); +} + +/* Read `size' bytes from the disk starting at byte `offset'. */ +void devread(offset, buf, size) +long offset; +char *buf; +int size; +{ + if(!block_size) fatal("devread() with unknown block size"); + devio((block_nr) (offset / block_size), READING); + memmove(buf, &rwbuf[(int) (offset % block_size)], (size_t)size); /* lint but OK */ +} + +/* Write `size' bytes to the disk starting at byte `offset'. */ +void devwrite(offset, buf, size) +long offset; +char *buf; +int size; +{ + if(!block_size) fatal("devwrite() with unknown block size"); + if (!repair) fatal("internal error (devwrite)"); + if (size != block_size) devio((block_nr) (offset / block_size), READING); + memmove(&rwbuf[(int) (offset % block_size)], buf, (size_t)size); /* lint but OK */ + devio((block_nr) (offset / block_size), WRITING); + changed = 1; +} + +/* Print a string with either a singular or a plural pronoun. */ +void pr(fmt, cnt, s, p) +char *fmt, *s, *p; +int cnt; +{ + printf(fmt, cnt, cnt == 1 ? s : p); +} + +/* Same as above, but with a long argument */ +void lpr(fmt, cnt, s, p) +char *fmt, *s, *p; +long cnt; +{ + printf(fmt, cnt, cnt == 1 ? s : p); +} + +/* Convert string to number. */ +bit_nr getnumber(s) +register char *s; +{ + register bit_nr n = 0; + + if (s == NULL) + return NO_BIT; + while (isdigit(*s)) + n = (n << 1) + (n << 3) + *s++ - '0'; + return (*s == '\0') ? n : NO_BIT; +} + +/* See if the list pointed to by `argv' contains numbers. */ +char **getlist(argv, type) +char ***argv, *type; +{ + register char **list = *argv; + register empty = 1; + + while (getnumber(**argv) != NO_BIT) { + (*argv)++; + empty = 0; + } + if (empty) { + printf("warning: no %s numbers given\n", type); + return(NULL); + } + return(list); +} + +/* Make a listing of the super block. If `repair' is set, ask the user + * for changes. + */ +void lsuper() +{ + char buf[80]; + + do { + /* Most of the following atol's enrage lint, for good reason. */ + printf("ninodes = %u", sb.s_ninodes); + if (input(buf, 80)) sb.s_ninodes = atol(buf); + printf("nzones = %ld", sb.s_zones); + if (input(buf, 80)) sb.s_zones = atol(buf); + printf("imap_blocks = %u", sb.s_imap_blocks); + if (input(buf, 80)) sb.s_imap_blocks = atol(buf); + printf("zmap_blocks = %u", sb.s_zmap_blocks); + if (input(buf, 80)) sb.s_zmap_blocks = atol(buf); + printf("firstdatazone = %u", sb.s_firstdatazone); + if (input(buf, 80)) sb.s_firstdatazone = atol(buf); + printf("log_zone_size = %u", sb.s_log_zone_size); + if (input(buf, 80)) sb.s_log_zone_size = atol(buf); + printf("maxsize = %ld", sb.s_max_size); + if (input(buf, 80)) sb.s_max_size = atol(buf); + printf("block size = %ld", sb.s_block_size); + if (input(buf, 80)) sb.s_block_size = atol(buf); + if (yes("ok now")) { + devwrite(OFFSET_SUPER_BLOCK, (char *) &sb, sizeof(sb)); + return; + } + } while (yes("Do you want to try again")); + if (repair) exit(0); +} + +/* Get the super block from either disk or user. Do some initial checks. */ +void getsuper() +{ + if(lseek(dev, OFFSET_SUPER_BLOCK, SEEK_SET) < 0) { + perror("lseek"); + fatal("couldn't seek to super block."); + } + if(read(dev, &sb, sizeof(sb)) != sizeof(sb)) { + fatal("couldn't read super block."); + } + if (listsuper) lsuper(); + if (sb.s_magic == SUPER_MAGIC) fatal("Cannot handle V1 file systems"); + if (sb.s_magic == SUPER_V2) { + fs_version = 2; + block_size = /* STATIC_BLOCK_SIZE */ 8192; + } else if(sb.s_magic == SUPER_V3) { + fs_version = 3; + block_size = sb.s_block_size; + } else { + fatal("bad magic number in super block"); + } + if (sb.s_ninodes <= 0) fatal("no inodes"); + if (sb.s_zones <= 0) fatal("no zones"); + if (sb.s_imap_blocks <= 0) fatal("no imap"); + if (sb.s_zmap_blocks <= 0) fatal("no zmap"); + if (sb.s_firstdatazone <= 4) fatal("first data zone too small"); + if (sb.s_log_zone_size < 0) fatal("zone size < block size"); + if (sb.s_max_size <= 0) fatal("max. file size <= 0"); + +} + +/* Check the super block for reasonable contents. */ +void chksuper() +{ + register n; + register off_t maxsize; + + n = bitmapsize((bit_t) sb.s_ninodes + 1, block_size); + if (sb.s_magic != SUPER_V2 && sb.s_magic != SUPER_V3) + fatal("bad magic number in super block"); + if (sb.s_imap_blocks < n) { + printf("need %d bocks for inode bitmap; only have %d\n", + n, sb.s_imap_blocks); + fatal("too few imap blocks"); + } + if (sb.s_imap_blocks != n) { + pr("warning: expected %d imap_block%s", n, "", "s"); + printf(" instead of %d\n", sb.s_imap_blocks); + } + n = bitmapsize((bit_t) sb.s_zones, block_size); + if (sb.s_zmap_blocks < n) fatal("too few zmap blocks"); + if (sb.s_zmap_blocks != n) { + pr("warning: expected %d zmap_block%s", n, "", "s"); + printf(" instead of %d\n", sb.s_zmap_blocks); + } + if (sb.s_firstdatazone >= sb.s_zones) + fatal("first data zone too large"); + if (sb.s_log_zone_size >= 8 * sizeof(block_nr)) + fatal("log_zone_size too large"); + if (sb.s_log_zone_size > 8) printf("warning: large log_zone_size (%d)\n", + sb.s_log_zone_size); + n = (BLK_ILIST + N_ILIST + SCALE - 1) >> sb.s_log_zone_size; + if (sb.s_firstdatazone < n) fatal("first data zone too small"); + if (sb.s_firstdatazone != n) { + printf("warning: expected first data zone to be %d ", n); + printf("instead of %u\n", sb.s_firstdatazone); + } + maxsize = MAX_FILE_POS; + if (((maxsize - 1) >> sb.s_log_zone_size) / block_size >= MAX_ZONES) + maxsize = ((long) MAX_ZONES * block_size) << sb.s_log_zone_size; + if (sb.s_max_size != maxsize) { + printf("warning: expected max size to be %ld ", maxsize); + printf("instead of %ld\n", sb.s_max_size); + } +} + +int inoaddr(int inn) +{ + int a; + a = cinoaddr(inn); + return a; +} + +/* Make a listing of the inodes given by `clist'. If `repair' is set, ask + * the user for changes. + */ +void lsi(clist) +char **clist; +{ + register bit_nr bit; + register ino_t ino; + d_inode inode, *ip = &inode; + char buf[80]; + + if (clist == 0) return; + while ((bit = getnumber(*clist++)) != NO_BIT) { + setbit(spec_imap, bit); + ino = bit; + do { + devread(inoaddr(ino), (char *) ip, INODE_SIZE); + printf("inode %u:\n", ino); + printf(" mode = %6o", ip->i_mode); + if (input(buf, 80)) ip->i_mode = atoo(buf); + printf(" nlinks = %6u", ip->i_nlinks); + if (input(buf, 80)) ip->i_nlinks = atol(buf); + printf(" size = %6ld", ip->i_size); + if (input(buf, 80)) ip->i_size = atol(buf); + if (yes("Write this back")) { + devwrite(inoaddr(ino), (char *) ip, INODE_SIZE); + break; + } + } while (yes("Do you want to change it again")); + } +} + +/* Allocate `nblk' blocks worth of bitmap. */ +bitchunk_t *allocbitmap(nblk) +int nblk; +{ + register bitchunk_t *bitmap; + + bitmap = (bitchunk_t *) alloc((unsigned) nblk, block_size); + *bitmap |= 1; + return(bitmap); +} + +/* Load the bitmap starting at block `bno' from disk. */ +void loadbitmap(bitmap, bno, nblk) +bitchunk_t *bitmap; +block_nr bno; +int nblk; +{ + register i; + register bitchunk_t *p; + + p = bitmap; + for (i = 0; i < nblk; i++, bno++, p += WORDS_PER_BLOCK) + devread(btoa(bno), (char *) p, block_size); + *bitmap |= 1; +} + +/* Write the bitmap starting at block `bno' to disk. */ +void dumpbitmap(bitmap, bno, nblk) +bitchunk_t *bitmap; +block_nr bno; +int nblk; +{ + register i; + register bitchunk_t *p = bitmap; + + printf("writing bitmap - %d blocks from block %d\n", + nblk, bno); + for (i = 0; i < nblk; i++, bno++, p += WORDS_PER_BLOCK) + devwrite(btoa(bno), (char *) p, block_size); +} + +/* Set the bits given by `list' in the bitmap. */ +void fillbitmap(bitmap, lwb, upb, list) +bitchunk_t *bitmap; +bit_nr lwb, upb; +char **list; +{ + register bit_nr bit; + + if (list == 0) return; + while ((bit = getnumber(*list++)) != NO_BIT) + if (bit < lwb || bit >= upb) { + if (bitmap == spec_imap) + printf("inode number %ld ", bit); + else + printf("zone number %ld ", bit); + printf("out of range (ignored)\n"); + } else + setbit(bitmap, bit - lwb + 1); +} + +/* Deallocate the bitmap `p'. */ +void freebitmap(p) +bitchunk_t *p; +{ + free((char *) p); +} + +/* Get all the bitmaps used by this program. */ +void getbitmaps() +{ + imap = allocbitmap(N_IMAP); + zmap = allocbitmap(N_ZMAP); + spec_imap = allocbitmap(N_IMAP); + spec_zmap = allocbitmap(N_ZMAP); + dirmap = allocbitmap(N_IMAP); +} + +/* Release all the space taken by the bitmaps. */ +void putbitmaps() +{ + freebitmap(imap); + freebitmap(zmap); + freebitmap(spec_imap); + freebitmap(spec_zmap); + freebitmap(dirmap); +} + +/* `w1' and `w2' are differing words from two bitmaps that should be + * identical. Print what's the matter with them. + */ +void chkword(w1, w2, bit, type, n, report, phys) +unsigned w1, w2; +char *type; +bit_nr bit; +int *n, *report; +bit_nr phys; +{ + for (; (w1 | w2); w1 >>= 1, w2 >>= 1, bit++, phys++) + if ((w1 ^ w2) & 1 && ++(*n) % MAXPRINT == 0 && *report && + (!repair || automatic || yes("stop this listing"))) + *report = 0; + else if (*report) + if ((w1 & 1) && !(w2 & 1)) + printf("%s %ld (%ld) is missing\n", type, bit, phys); + else if (!(w1 & 1) && (w2 & 1)) + printf("%s %ld (%ld) is not free\n", type, bit, phys); +} + +/* Check if the given (correct) bitmap is identical with the one that is + * on the disk. If not, ask if the disk should be repaired. + */ +void chkmap(cmap, dmap, bit, blkno, nblk, type) +bitchunk_t *cmap, *dmap; +bit_nr bit; +block_nr blkno; +int nblk; +char *type; +{ + register bitchunk_t *p = dmap, *q = cmap; + int report = 1, nerr = 0; + int w = nblk * WORDS_PER_BLOCK; + bit_nr phys = 0; + + printf("Checking %s map\n", type); + loadbitmap(dmap, blkno, nblk); + do { + if (*p != *q) chkword(*p, *q, bit, type, &nerr, &report, phys); + p++; + q++; + bit += 8 * sizeof(bitchunk_t); + phys += 8 * sizeof(bitchunk_t); + } while (--w > 0); + + if ((!repair || automatic) && !report) printf("etc. "); + if (nerr > MAXPRINT || nerr > 10) printf("%d errors found. ", nerr); + if (nerr != 0 && yes("install a new map")) dumpbitmap(cmap, blkno, nblk); + if (nerr > 0) printf("\n"); +} + +/* See if the inodes that aren't allocated are cleared. */ +void chkilist() +{ + register ino_t ino = 1; + mode_t mode; + + printf("Checking inode list\n"); + do + if (!bitset(imap, (bit_nr) ino)) { + devread(inoaddr(ino), (char *) &mode, sizeof(mode)); + if (mode != I_NOT_ALLOC) { + printf("mode inode %u not cleared", ino); + if (yes(". clear")) devwrite(inoaddr(ino), nullbuf, + INODE_SIZE); + } + } + while (++ino <= sb.s_ninodes && ino != 0); + printf("\n"); +} + +/* Allocate an array to maintain the inode reference counts in. */ +void getcount() +{ + count = (nlink_t *) alloc((unsigned) (sb.s_ninodes + 1), sizeof(nlink_t)); +} + +/* The reference count for inode `ino' is wrong. Ask if it should be adjusted. */ +void counterror(ino) +ino_t ino; +{ + d_inode inode; + + if (firstcnterr) { + printf("INODE NLINK COUNT\n"); + firstcnterr = 0; + } + devread(inoaddr(ino), (char *) &inode, INODE_SIZE); + count[ino] += inode.i_nlinks; /* it was already subtracted; add it back */ + printf("%5u %5u %5u", ino, (unsigned) inode.i_nlinks, count[ino]); + if (yes(" adjust")) { + if ((inode.i_nlinks = count[ino]) == 0) { + fatal("internal error (counterror)"); + inode.i_mode = I_NOT_ALLOC; + clrbit(imap, (bit_nr) ino); + } + devwrite(inoaddr(ino), (char *) &inode, INODE_SIZE); + } +} + +/* Check if the reference count of the inodes are correct. The array `count' + * is maintained as follows: an entry indexed by the inode number is + * incremented each time a link is found; when the inode is read the link + * count in there is substracted from the corresponding entry in `count'. + * Thus, when the whole file system has been traversed, all the entries + * should be zero. + */ +void chkcount() +{ + register ino_t ino; + + for (ino = 1; ino <= sb.s_ninodes && ino != 0; ino++) + if (count[ino] != 0) counterror(ino); + if (!firstcnterr) printf("\n"); +} + +/* Deallocate the `count' array. */ +void freecount() +{ + free((char *) count); +} + +/* Print the inode permission bits given by mode and shift. */ +void printperm(mode, shift, special, overlay) +mode_t mode; +int shift; +int special; +int overlay; +{ + if (mode >> shift & R_BIT) + putchar('r'); + else + putchar('-'); + if (mode >> shift & W_BIT) + putchar('w'); + else + putchar('-'); + if (mode & special) + putchar(overlay); + else + if (mode >> shift & X_BIT) + putchar('x'); + else + putchar('-'); +} + +/* List the given inode. */ +void list(ino, ip) +ino_t ino; +d_inode *ip; +{ + if (firstlist) { + firstlist = 0; + printf(" inode permission link size name\n"); + } + printf("%6u ", ino); + switch (ip->i_mode & I_TYPE) { + case I_REGULAR: putchar('-'); break; + case I_DIRECTORY: putchar('d'); break; + case I_CHAR_SPECIAL: putchar('c'); break; + case I_BLOCK_SPECIAL: putchar('b'); break; + case I_NAMED_PIPE: putchar('p'); break; +#ifdef I_SYMBOLIC_LINK + case I_SYMBOLIC_LINK: putchar('l'); break; +#endif + default: putchar('?'); +} + printperm(ip->i_mode, 6, I_SET_UID_BIT, 's'); + printperm(ip->i_mode, 3, I_SET_GID_BIT, 's'); + printperm(ip->i_mode, 0, STICKY_BIT, 't'); + printf(" %3u ", ip->i_nlinks); + switch (ip->i_mode & I_TYPE) { + case I_CHAR_SPECIAL: + case I_BLOCK_SPECIAL: + printf(" %2x,%2x ", (dev_t) ip->i_zone[0] >> MAJOR & 0xFF, + (dev_t) ip->i_zone[0] >> MINOR & 0xFF); + break; + default: printf("%7ld ", ip->i_size); + } + printpath(0, 1); +} + +/* Remove an entry from a directory if ok with the user. + * Don't name the function remove() - that is owned by ANSI, and chaos results + * when it is a macro. + */ +int Remove(dp) +dir_struct *dp; +{ + setbit(spec_imap, (bit_nr) dp->d_inum); + if (yes(". remove entry")) { + count[dp->d_inum]--; + memset((void *) dp, 0, sizeof(dir_struct)); + return(1); + } + return(0); +} + +/* Convert string so that embedded control characters are printable. */ +void make_printable_name(dst, src, n) +register char *dst; +register char *src; +register int n; +{ + register int c; + + while (--n >= 0 && (c = *src++) != '\0') { + if (isprint(c) && c != '\\') + *dst++ = c; + else { + *dst++ = '\\'; + switch (c) { + case '\\': + *dst++ = '\\'; break; + case '\b': + *dst++ = 'b'; break; + case '\f': + *dst++ = 'f'; break; + case '\n': + *dst++ = 'n'; break; + case '\r': + *dst++ = 'r'; break; + case '\t': + *dst++ = 't'; break; + default: + *dst++ = '0' + ((c >> 6) & 03); + *dst++ = '0' + ((c >> 3) & 07); + *dst++ = '0' + (c & 07); + } + } + } + *dst = '\0'; +} + +/* See if the `.' or `..' entry is as expected. */ +int chkdots(ino, pos, dp, exp) +ino_t ino, exp; +off_t pos; +dir_struct *dp; +{ + char printable_name[4 * NAME_MAX + 1]; + + if (dp->d_inum != exp) { + make_printable_name(printable_name, dp->d_name, sizeof(dp->d_name)); + printf("bad %s in ", printable_name); + printpath(1, 0); + printf("%s is linked to %u ", printable_name, dp->d_inum); + printf("instead of %u)", exp); + setbit(spec_imap, (bit_nr) ino); + setbit(spec_imap, (bit_nr) dp->d_inum); + setbit(spec_imap, (bit_nr) exp); + if (yes(". repair")) { + count[dp->d_inum]--; + dp->d_inum = exp; + count[exp]++; + return(0); + } + } else if (pos != (dp->d_name[1] ? DIR_ENTRY_SIZE : 0)) { + make_printable_name(printable_name, dp->d_name, sizeof(dp->d_name)); + printf("warning: %s has offset %ld in ", printable_name, pos); + printpath(1, 0); + printf("%s is linked to %u)\n", printable_name, dp->d_inum); + setbit(spec_imap, (bit_nr) ino); + setbit(spec_imap, (bit_nr) dp->d_inum); + setbit(spec_imap, (bit_nr) exp); + } + return(1); +} + +/* Check the name in a directory entry. */ +int chkname(ino, dp) +ino_t ino; +dir_struct *dp; +{ + register n = NAME_MAX + 1; + register char *p = dp->d_name; + + if (*p == '\0') { + printf("null name found in "); + printpath(0, 0); + setbit(spec_imap, (bit_nr) ino); + if (Remove(dp)) return(0); + } + while (*p != '\0' && --n != 0) + if (*p++ == '/') { + printf("found a '/' in entry of directory "); + printpath(1, 0); + setbit(spec_imap, (bit_nr) ino); + printf("entry = '"); + printname(dp->d_name); + printf("')"); + if (Remove(dp)) return(0); + break; + } + return(1); +} + +/* Check a directory entry. Here the routine `descendtree' is called + * recursively to check the file or directory pointed to by the entry. + */ +int chkentry(ino, pos, dp) +ino_t ino; +off_t pos; +dir_struct *dp; +{ + if (dp->d_inum < ROOT_INODE || dp->d_inum > sb.s_ninodes) { + printf("bad inode found in directory "); + printpath(1, 0); + printf("ino found = %u, ", dp->d_inum); + printf("name = '"); + printname(dp->d_name); + printf("')"); + if (yes(". remove entry")) { + memset((void *) dp, 0, sizeof(dir_struct)); + return(0); + } + return(1); + } + if ((unsigned) count[dp->d_inum] == SHRT_MAX) { + printf("too many links to ino %u\n", dp->d_inum); + printf("discovered at entry '"); + printname(dp->d_name); + printf("' in directory "); + printpath(0, 1); + if (Remove(dp)) return(0); + } + count[dp->d_inum]++; + if (strcmp(dp->d_name, ".") == 0) { + ftop->st_presence |= DOT; + return(chkdots(ino, pos, dp, ino)); + } + if (strcmp(dp->d_name, "..") == 0) { + ftop->st_presence |= DOTDOT; + return(chkdots(ino, pos, dp, ino == ROOT_INODE ? ino : + ftop->st_next->st_dir->d_inum)); + } + if (!chkname(ino, dp)) return(0); + if (bitset(dirmap, (bit_nr) dp->d_inum)) { + printf("link to directory discovered in "); + printpath(1, 0); + printf("name = '"); + printname(dp->d_name); + printf("', dir ino = %u)", dp->d_inum); + return !Remove(dp); + } + return(descendtree(dp)); +} + +/* Check a zone of a directory by checking all the entries in the zone. + * The zone is split up into chunks to not allocate too much stack. + */ +int chkdirzone(ino, ip, pos, zno) +ino_t ino; +d_inode *ip; +off_t pos; +zone_nr zno; +{ + dir_struct dirblk[CDIRECT]; + register dir_struct *dp; + register n, dirty; + register long offset = zaddr(zno); + register off_t size = 0; + n = SCALE * (NR_DIR_ENTRIES(block_size) / CDIRECT); + + do { + devread(offset, (char *) dirblk, DIRCHUNK); + dirty = 0; + for (dp = dirblk; dp < &dirblk[CDIRECT]; dp++) { + if (dp->d_inum != NO_ENTRY && !chkentry(ino, pos, dp)) + dirty = 1; + pos += DIR_ENTRY_SIZE; + if (dp->d_inum != NO_ENTRY) size = pos; + } + if (dirty) devwrite(offset, (char *) dirblk, DIRCHUNK); + offset += DIRCHUNK; + n--; + } while (n > 0); + + if (size > ip->i_size) { + printf("size not updated of directory "); + printpath(2, 0); + if (yes(". extend")) { + setbit(spec_imap, (bit_nr) ino); + ip->i_size = size; + devwrite(inoaddr(ino), (char *) ip, INODE_SIZE); + } + } + return(1); +} + +/* There is something wrong with the given zone. Print some details. */ +void errzone(mess, zno, level, pos) +char *mess; +zone_nr zno; +int level; +off_t pos; +{ + printf("%s zone in ", mess); + printpath(1, 0); + printf("zno = %ld, type = ", zno); + switch (level) { + case 0: printf("DATA"); break; + case 1: printf("SINGLE INDIRECT"); break; + case 2: printf("DOUBLE INDIRECT"); break; + default: printf("VERY INDIRECT"); + } + printf(", pos = %ld)\n", pos); +} + +/* Found the given zone in the given inode. Check it, and if ok, mark it + * in the zone bitmap. + */ +int markzone(zno, level, pos) +zone_nr zno; +int level; +off_t pos; +{ + register bit_nr bit = (bit_nr) zno - FIRST + 1; + + ztype[level]++; + if (zno < FIRST || zno >= sb.s_zones) { + errzone("out-of-range", zno, level, pos); + return(0); + } + if (bitset(zmap, bit)) { + setbit(spec_zmap, bit); + errzone("duplicate", zno, level, pos); + return(0); + } + nfreezone--; + if (bitset(spec_zmap, bit)) errzone("found", zno, level, pos); + setbit(zmap, bit); + return(1); +} + +/* Check an indirect zone by checking all of its entries. + * The zone is split up into chunks to not allocate too much stack. + */ +int chkindzone(ino, ip, pos, zno, level) +ino_t ino; +d_inode *ip; +off_t *pos; +zone_nr zno; +int level; +{ + zone_nr indirect[CINDIR]; + register n = NR_INDIRECTS / CINDIR; + register long offset = zaddr(zno); + + do { + devread(offset, (char *) indirect, INDCHUNK); + if (!chkzones(ino, ip, pos, indirect, CINDIR, level - 1)) return(0); + offset += INDCHUNK; + } while (--n && *pos < ip->i_size); + return(1); +} + +/* Return the size of a gap in the file, represented by a null zone number + * at some level of indirection. + */ +off_t jump(level) +int level; +{ + off_t power = ZONE_SIZE; + + if (level != 0) do + power *= NR_INDIRECTS; + while (--level); + return(power); +} + +/* Check a zone, which may be either a normal data zone, a directory zone, + * or an indirect zone. + */ +int zonechk(ino, ip, pos, zno, level) +ino_t ino; +d_inode *ip; +off_t *pos; +zone_nr zno; +int level; +{ + if (level == 0) { + if ((ip->i_mode & I_TYPE) == I_DIRECTORY && + !chkdirzone(ino, ip, *pos, zno)) + return(0); + *pos += ZONE_SIZE; + return(1); + } else + return chkindzone(ino, ip, pos, zno, level); +} + +/* Check a list of zones given by `zlist'. */ +int chkzones(ino, ip, pos, zlist, len, level) +ino_t ino; +d_inode *ip; +off_t *pos; +zone_nr *zlist; +int len; +int level; +{ + register ok = 1, i; + + /* The check on the position in the next loop is commented out, since FS + * now requires valid zone numbers in each level that is necessary and FS + * always deleted all the zones in the double indirect block. + */ + for (i = 0; i < len /* && *pos < ip->i_size */ ; i++) + if (zlist[i] == NO_ZONE) + *pos += jump(level); + else if (!markzone(zlist[i], level, *pos)) { + *pos += jump(level); + ok = 0; + } else if (!zonechk(ino, ip, pos, zlist[i], level)) + ok = 0; + return(ok); +} + +/* Check a file or a directory. */ +int chkfile(ino, ip) +ino_t ino; +d_inode *ip; +{ + register ok, i, level; + off_t pos = 0; + + ok = chkzones(ino, ip, &pos, &ip->i_zone[0], NR_DZONE_NUM, 0); + for (i = NR_DZONE_NUM, level = 1; i < NR_ZONE_NUMS; i++, level++) + ok &= chkzones(ino, ip, &pos, &ip->i_zone[i], 1, level); + return(ok); +} + +/* Check a directory by checking the contents. Check if . and .. are present. */ +int chkdirectory(ino, ip) +ino_t ino; +d_inode *ip; +{ + register ok; + + setbit(dirmap, (bit_nr) ino); + ok = chkfile(ino, ip); + if (!(ftop->st_presence & DOT)) { + printf(". missing in "); + printpath(2, 1); + ok = 0; + } + if (!(ftop->st_presence & DOTDOT)) { + printf(".. missing in "); + printpath(2, 1); + ok = 0; + } + return(ok); +} + +#ifdef I_SYMBOLIC_LINK + +/* Check the validity of a symbolic link. */ +int chklink(ino, ip) +ino_t ino; +d_inode *ip; +{ + int ok; + + ok = chkfile(ino, ip); + if (ip->i_size <= 0 || ip->i_size > block_size) { + if (ip->i_size == 0) + printf("empty symbolic link "); + else + printf("symbolic link too large (size %ld) ", ip->i_size); + printpath(2, 1); + ok = 0; + } + return(ok); +} + +#endif + +/* Check the validity of a special file. */ +int chkspecial(ino, ip) +ino_t ino; +d_inode *ip; +{ + int i, ok; + + ok = 1; + if ((dev_t) ip->i_zone[0] == NO_DEV) { + printf("illegal device number %ld for special file ", ip->i_zone[0]); + printpath(2, 1); + ok = 0; + } + + /* FS will not use the remaining "zone numbers" but 1.6.11++ will panic if + * they are nonzero, since this should not happen. + */ + for (i = 1; i < NR_ZONE_NUMS; i++) + if (ip->i_zone[i] != NO_ZONE) { + printf("nonzero zone number %ld for special file ", + ip->i_zone[i]); + printpath(2, 1); + ok = 0; + } + return(ok); +} + +/* Check the mode and contents of an inode. */ +int chkmode(ino, ip) +ino_t ino; +d_inode *ip; +{ + switch (ip->i_mode & I_TYPE) { + case I_REGULAR: + nregular++; + return chkfile(ino, ip); + case I_DIRECTORY: + ndirectory++; + return chkdirectory(ino, ip); + case I_BLOCK_SPECIAL: + nblkspec++; + return chkspecial(ino, ip); + case I_CHAR_SPECIAL: + ncharspec++; + return chkspecial(ino, ip); + case I_NAMED_PIPE: + npipe++; + return chkfile(ino, ip); +#ifdef I_SYMBOLIC_LINK + case I_SYMBOLIC_LINK: + nsyml++; + return chklink(ino, ip); +#endif + default: + nbadinode++; + printf("bad mode of "); + printpath(1, 0); + printf("mode = %o)", ip->i_mode); + return(0); + } +} + +/* Check an inode. */ +int chkinode(ino, ip) +ino_t ino; +d_inode *ip; +{ + if (ino == ROOT_INODE && (ip->i_mode & I_TYPE) != I_DIRECTORY) { + printf("root inode is not a directory "); + printf("(ino = %u, mode = %o)\n", ino, ip->i_mode); + fatal(""); + } + if (ip->i_nlinks == 0) { + printf("link count zero of "); + printpath(2, 0); + return(0); + } + nfreeinode--; + setbit(imap, (bit_nr) ino); + if ((unsigned) ip->i_nlinks > SHRT_MAX) { + printf("link count too big in "); + printpath(1, 0); + printf("cnt = %u)\n", (unsigned) ip->i_nlinks); + count[ino] -= SHRT_MAX; + setbit(spec_imap, (bit_nr) ino); + } else { + count[ino] -= (unsigned) ip->i_nlinks; + } + return chkmode(ino, ip); +} + +/* Check the directory entry pointed to by dp, by checking the inode. */ +int descendtree(dp) +dir_struct *dp; +{ + d_inode inode; + register ino_t ino = dp->d_inum; + register visited; + struct stack stk; + + stk.st_dir = dp; + stk.st_next = ftop; + ftop = &stk; + if (bitset(spec_imap, (bit_nr) ino)) { + printf("found inode %u: ", ino); + printpath(0, 1); + } + visited = bitset(imap, (bit_nr) ino); + if (!visited || listing) { + devread(inoaddr(ino), (char *) &inode, INODE_SIZE); + if (listing) list(ino, &inode); + if (!visited && !chkinode(ino, &inode)) { + setbit(spec_imap, (bit_nr) ino); + if (yes("remove")) { + count[ino] += inode.i_nlinks - 1; + clrbit(imap, (bit_nr) ino); + devwrite(inoaddr(ino), nullbuf, INODE_SIZE); + memset((void *) dp, 0, sizeof(dir_struct)); + ftop = ftop->st_next; + return(0); + } + } + } + ftop = ftop->st_next; + return(1); +} + +/* Check the file system tree. */ +void chktree() +{ + dir_struct dir; + + nfreeinode = sb.s_ninodes; + nfreezone = N_DATA; + dir.d_inum = ROOT_INODE; + dir.d_name[0] = 0; + if (!descendtree(&dir)) fatal("bad root inode"); + putchar('\n'); +} + +/* Print the totals of all the objects found. */ +void printtotal() +{ + printf("blocksize = %5d ", block_size); + printf("zonesize = %5d\n", ZONE_SIZE); + printf("\n"); + pr("%6u Regular file%s\n", nregular, "", "s"); + pr("%6u Director%s\n", ndirectory, "y", "ies"); + pr("%6u Block special file%s\n", nblkspec, "", "s"); + pr("%6u Character special file%s\n", ncharspec, "", "s"); + if (nbadinode != 0) pr("%6u Bad inode%s\n", nbadinode, "", "s"); + pr("%6u Free inode%s\n", nfreeinode, "", "s"); + pr("%6u Named pipe%s\n", npipe, "", "s"); + pr("%6u Symbolic link%s\n", nsyml, "", "s"); +/* Don't print some fields. + printf("\n"); + pr("%6u Data zone%s\n", ztype[0], "", "s"); + pr("%6u Single indirect zone%s\n", ztype[1], "", "s"); + pr("%6u Double indirect zone%s\n", ztype[2], "", "s"); +*/ + lpr("%6ld Free zone%s\n", nfreezone, "", "s"); +} + +/* Check the device which name is given by `f'. The inodes listed by `clist' + * should be listed separately, and the inodes listed by `ilist' and the zones + * listed by `zlist' should be watched for while checking the file system. + */ + +void chkdev(f, clist, ilist, zlist) +char *f, **clist, **ilist, **zlist; +{ + if (automatic) repair = 1; + fsck_device = f; + initvars(); + + devopen(); + + getsuper(); + + if(block_size < MIN_BLOCK_SIZE || block_size > MAX_BLOCK_SIZE) + fatal("funny block size"); + + if(!(rwbuf = malloc(block_size))) fatal("couldn't allocate fs buf (1)"); + if(!(nullbuf = malloc(block_size))) fatal("couldn't allocate fs buf (2)"); + memset(nullbuf, 0, block_size); + + chksuper(); + + lsi(clist); + + getbitmaps(); + + fillbitmap(spec_imap, (bit_nr) 1, (bit_nr) sb.s_ninodes + 1, ilist); + fillbitmap(spec_zmap, (bit_nr) FIRST, (bit_nr) sb.s_zones, zlist); + + getcount(); + chktree(); + chkmap(zmap, spec_zmap, (bit_nr) FIRST - 1, BLK_ZMAP, N_ZMAP, "zone"); + chkcount(); + chkmap(imap, spec_imap, (bit_nr) 0, BLK_IMAP, N_IMAP, "inode"); + chkilist(); + printtotal(); + + putbitmaps(); + freecount(); + devclose(); + + if (changed) printf("----- FILE SYSTEM HAS BEEN MODIFIED -----\n\n"); +} + +int main(argc, argv) +int argc; +char **argv; +{ + register char **clist = 0, **ilist = 0, **zlist = 0; + + register devgiven = 0; + register char *arg; + + if ((1 << BITSHIFT) != 8 * sizeof(bitchunk_t)) { + printf("Fsck was compiled with the wrong BITSHIFT!\n"); + exit(1); + } + + sync(); + prog = *argv++; + while ((arg = *argv++) != 0) + if (arg[0] == '-' && arg[1] != 0 && arg[2] == 0) switch (arg[1]) { + case 'a': automatic ^= 1; break; + case 'c': + clist = getlist(&argv, "inode"); + break; + case 'i': + ilist = getlist(&argv, "inode"); + break; + case 'z': + zlist = getlist(&argv, "zone"); + break; + case 'r': repair ^= 1; break; + case 'l': listing ^= 1; break; + case 's': listsuper ^= 1; break; + default: + printf("%s: unknown flag '%s'\n", prog, arg); + } + else { + chkdev(arg, clist, ilist, zlist); + clist = 0; + ilist = 0; + zlist = 0; + devgiven = 1; + } + if (!devgiven) { + printf("Usage: fsck [-acilrsz] file\n"); + exit(1); + } + return(0); +} diff --git a/commands/simple/fsck1.c b/commands/simple/fsck1.c new file mode 100755 index 000000000..732496084 --- /dev/null +++ b/commands/simple/fsck1.c @@ -0,0 +1,1481 @@ +/* Hacks for version 1.6 */ + +#define INODES_PER_BLOCK V1_INODES_PER_BLOCK +#define INODE_SIZE V1_INODE_SIZE +#define WORDS_PER_BLOCK (BLOCK_SIZE / (int) sizeof(bitchunk_t)) +#define MAX_ZONES (V1_NR_DZONES+V1_INDIRECTS+(long)V1_INDIRECTS*V1_INDIRECTS) +#define NR_DZONE_NUM V1_NR_DZONES +#define NR_INDIRECTS V1_INDIRECTS +#define NR_ZONE_NUMS V1_NR_TZONES +#define ZONE_NUM_SIZE V1_ZONE_NUM_SIZE +#define bit_nr u16_t /* perhaps bit_t should be used, although slower */ +#define Bit_nr U16_t +#define block_nr block_t +#define d_inode d1_inode +#define d_inum d_ino +#define dir_struct struct direct +#define i_mode d1_mode +#define i_nlinks d1_nlinks +#define i_size d1_size +#define i_zone d1_zone +#define zone_nr zone1_t +#define Zone_nr Zone1_t + +/* fsck - file system checker Author: Robbert van Renesse */ + +/* Modified by Norbert Schlenker +* Removed vestiges of standalone/DOS versions: +* - various unused variables and buffers removed +* - now uses library functions rather than private internal routines +* - bytewise structure copies replaced by structure assignment +* - fixed one bug with 14 character file names +* - other small tweaks for speed +* +* Modified by Lars Fredriksen at the request of Andy Tanenbaum, 90-03-10. +* Removed -m option, by which fsck could be told to make a file +* system on a 360K floppy. The code had limited utility, was buggy, +* and failed due to a bug in the ACK C compiler. Use mkfs instead! +*/ + +#include <sys/types.h> +#include <sys/dir.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <minix/config.h> +#include <minix/const.h> +#include <minix/type.h> +#include "../../servers/fs/const.h" +#include "../../servers/fs/inode.h" +#include "../../servers/fs/type.h" +#include <minix/fslib.h> +#include <stdio.h> +#include <dirent.h> + +#define BLOCK_SIZE STATIC_BLOCK_SIZE + +#define BITSHIFT 4 /* = log2(#bits(int)) */ + +#define MAXPRINT 8 /* max. number of error lines in chkmap */ +#define CINDIR 128 /* number of indirect zno's read at a time */ +#define CDIRECT 16 /* number of dir entries read at a time */ +#define BITMASK ((1 << BITSHIFT) - 1) +#define setbit(w, b) (w[(b) >> BITSHIFT] |= 1 << ((b) & BITMASK)) +#define clrbit(w, b) (w[(b) >> BITSHIFT] &= ~(1 << ((b) & BITMASK))) +#define bitset(w, b) (w[(b) >> BITSHIFT] & (1 << ((b) & BITMASK))) + +#define ZONE_CT 360 /* default zones (when making file system) */ +#define INODE_CT 95 /* default inodes (when making file system) */ + +#include "../../servers/fs/super.h" +struct super_block sb; + +#define STICKY_BIT 01000 /* not defined anywhere else */ + +/* Ztob gives the block address of a zone + * btoa gives the byte address of a block + */ +#define ztob(z) ((block_nr) (z) << sb.s_log_zone_size) +#define btoa(b) ((long) (b) * BLOCK_SIZE) +#define SCALE ((int) ztob(1)) /* # blocks in a zone */ +#define FIRST ((zone_nr) sb.s_firstdatazone) /* as the name says */ + +/* # blocks of each type */ +#define N_SUPER 1 +#define N_IMAP (sb.s_imap_blocks) +#define N_ZMAP (sb.s_zmap_blocks) +#define N_ILIST ((sb.s_ninodes+INODES_PER_BLOCK-1) / INODES_PER_BLOCK) +#define N_DATA (sb.s_nzones - FIRST) + +/* Block address of each type */ +#define BLK_SUPER (1) +#define BLK_IMAP (BLK_SUPER + N_SUPER) +#define BLK_ZMAP (BLK_IMAP + N_IMAP) +#define BLK_ILIST (BLK_ZMAP + N_ZMAP) +#define BLK_FIRST ztob(FIRST) +#define ZONE_SIZE ((int) ztob(BLOCK_SIZE)) +#define NLEVEL (NR_ZONE_NUMS - NR_DZONE_NUM + 1) + +/* Byte address of a zone/of an inode */ +#define zaddr(z) btoa(ztob(z)) +#define inoaddr(i) ((long) (i - 1) * INODE_SIZE + btoa(BLK_ILIST)) +#define INDCHUNK (CINDIR * ZONE_NUM_SIZE) +#define DIRCHUNK (CDIRECT * DIR_ENTRY_SIZE) + +char *prog, *device; /* program name (fsck), device name */ +int firstcnterr; /* is this the first inode ref cnt error? */ +bitchunk_t *imap, *spec_imap; /* inode bit maps */ +bitchunk_t *zmap, *spec_zmap; /* zone bit maps */ +bitchunk_t *dirmap; /* directory (inode) bit map */ +char rwbuf[BLOCK_SIZE]; /* one block buffer cache */ +block_nr thisblk; /* block in buffer cache */ +char nullbuf[BLOCK_SIZE]; /* null buffer */ +nlink_t *count; /* inode count */ +int changed; /* has the diskette been written to? */ +struct stack { + dir_struct *st_dir; + struct stack *st_next; + char st_presence; +} *ftop; + +int dev; /* file descriptor of the device */ + +#define DOT 1 +#define DOTDOT 2 + +/* Counters for each type of inode/zone. */ +int nfreeinode, nregular, ndirectory, nblkspec, ncharspec, nbadinode; +int npipe, nsyml, nfreezone, ztype[NLEVEL]; + +int repair, automatic, listing, listsuper; /* flags */ +int firstlist; /* has the listing header been printed? */ +unsigned part_offset; /* sector offset for this partition */ +char answer[] = "Answer questions with y or n. Then hit RETURN"; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void initvars, (void)); +_PROTOTYPE(void fatal, (char *s)); +_PROTOTYPE(int eoln, (int c)); +_PROTOTYPE(int yes, (char *question)); +_PROTOTYPE(int atoo, (char *s)); +_PROTOTYPE(int input, (char *buf, int size)); +_PROTOTYPE(char *alloc, (unsigned nelem, unsigned elsize)); +_PROTOTYPE(void printname, (char *s)); +_PROTOTYPE(void printrec, (struct stack *sp)); +_PROTOTYPE(void printpath, (int mode, int nlcr)); +_PROTOTYPE(void devopen, (void)); +_PROTOTYPE(void devclose, (void)); +_PROTOTYPE(void devio, (block_nr bno, int dir)); +_PROTOTYPE(void devread, (long offset, char *buf, int size)); +_PROTOTYPE(void devwrite, (long offset, char *buf, int size)); +_PROTOTYPE(void pr, (char *fmt, int cnt, char *s, char *p)); +_PROTOTYPE(bit_nr getnumber, (char *s)); +_PROTOTYPE(char **getlist, (char ***argv, char *type)); +_PROTOTYPE(void lsuper, (void)); +_PROTOTYPE(void getsuper, (void)); +_PROTOTYPE(void chksuper, (void)); +_PROTOTYPE(void lsi, (char **clist)); +_PROTOTYPE(bitchunk_t *allocbitmap, (int nblk)); +_PROTOTYPE(void loadbitmap, (bitchunk_t *bitmap, block_nr bno, int nblk)); +_PROTOTYPE(void dumpbitmap, (bitchunk_t *bitmap, block_nr bno, int nblk)); +_PROTOTYPE(void fillbitmap, (bitchunk_t *bitmap, Bit_nr lwb, Bit_nr upb, char **list)); +_PROTOTYPE(void freebitmap, (bitchunk_t *p)); +_PROTOTYPE(void getbitmaps, (void)); +_PROTOTYPE(void putbitmaps, (void)); +_PROTOTYPE(void chkword, (unsigned w1, unsigned w2, Bit_nr bit, char *type, int *n, int *report)); +_PROTOTYPE(void chkmap, (bitchunk_t *cmap, bitchunk_t *dmap, Bit_nr bit, block_nr blkno, int nblk, char *type)); +_PROTOTYPE(void chkilist, (void)); +_PROTOTYPE(void getcount, (void)); +_PROTOTYPE(void counterror, (Ino_t ino)); +_PROTOTYPE(void chkcount, (void)); +_PROTOTYPE(void freecount, (void)); +_PROTOTYPE(void printperm, (Mode_t mode, int shift, int special, int overlay)); +_PROTOTYPE(void list, (Ino_t ino, d_inode *ip)); +_PROTOTYPE(int Remove, (dir_struct *dp)); +_PROTOTYPE(void make_printable_name, (char *dst, char *src, int n)); +_PROTOTYPE(int chkdots, (Ino_t ino, off_t pos, dir_struct *dp, Ino_t exp)); +_PROTOTYPE(int chkname, (Ino_t ino, dir_struct *dp)); +_PROTOTYPE(int chkentry, (Ino_t ino, off_t pos, dir_struct *dp)); +_PROTOTYPE(int chkdirzone, (Ino_t ino, d_inode *ip, off_t pos, Zone_nr zno)); +_PROTOTYPE(void errzone, (char *mess, Zone_nr zno, int level, off_t pos)); +_PROTOTYPE(int markzone, (Ino_t ino, Zone_nr zno, int level, off_t pos)); +_PROTOTYPE(int chkindzone, (Ino_t ino, d_inode *ip, off_t *pos, Zone_nr zno, int level)); +_PROTOTYPE(off_t jump, (int level)); +_PROTOTYPE(int zonechk, (Ino_t ino, d_inode *ip, off_t *pos, Zone_nr zno, int level)); +_PROTOTYPE(int chkzones, (Ino_t ino, d_inode *ip, off_t *pos, zone_nr *zlist, int len, int level)); +_PROTOTYPE(int chkfile, (Ino_t ino, d_inode *ip)); +_PROTOTYPE(int chkdirectory, (Ino_t ino, d_inode *ip)); +_PROTOTYPE(int chklink, (Ino_t ino, d_inode *ip)); +_PROTOTYPE(int chkmode, (Ino_t ino, d_inode *ip)); +_PROTOTYPE(int chkinode, (Ino_t ino, d_inode *ip)); +_PROTOTYPE(int chkspecial, (Ino_t ino, d_inode *ip) ); +_PROTOTYPE(int descendtree, (dir_struct *dp)); +_PROTOTYPE(void chktree, (void)); +_PROTOTYPE(void printtotal, (void)); +_PROTOTYPE(void chkdev, (char *f, char **clist, char **ilist, char **zlist)); + +/* Initialize the variables used by this program. */ +void initvars() +{ + register level; + + nregular = ndirectory = nblkspec = ncharspec = nbadinode = npipe = nsyml = 0; + for (level = 0; level < NLEVEL; level++) ztype[level] = 0; + changed = 0; + thisblk = NO_BLOCK; + firstlist = 1; + firstcnterr = 1; +} + +/* Print the string `s' and exit. */ +void fatal(s) +char *s; +{ + printf("%s\nfatal\n", s); + exit(-1); +} + +/* Test for end of line. */ +int eoln(c) +int c; +{ + return(c == EOF || c == '\n' || c == '\r'); +} + +/* Ask a question and get the answer unless automatic is set. */ +int yes(question) +char *question; +{ + register c, answer; + + if (!repair) { + printf("\n"); + return(0); + } + printf("%s? ", question); + if (automatic) { + printf("yes\n"); + return(1); + } + fflush(stdout); + if ((c = answer = getchar()) == 'q' || c == 'Q') exit(1); + while (!eoln(c)) c = getchar(); + return !(answer == 'n' || answer == 'N'); +} + +/* Convert string to integer. Representation is octal. */ +int atoo(s) +register char *s; +{ + register int n = 0; + + while ('0' <= *s && *s < '8') { + n <<= 3; + n += *s++ - '0'; + } + return n; +} + +/* If repairing the file system, print a prompt and get a string from user. */ +int input(buf, size) +char *buf; +int size; +{ + register char *p = buf; + + printf("\n"); + if (repair) { + printf("--> "); + fflush(stdout); + while (--size) { + *p = getchar(); + if (eoln(*p)) { + *p = 0; + return(p > buf); + } + p++; + } + *p = 0; + while (!eoln(getchar())); + return(1); + } + return(0); +} + +/* Allocate some memory and zero it. */ +char *alloc(nelem, elsize) +unsigned nelem, elsize; +{ + char *p; + + if ((p = (char *) malloc((size_t)nelem * elsize)) == 0) fatal("out of memory"); + memset(p, 0, (size_t)nelem * elsize); + return(p); +} + +/* Print the name in a directory entry. */ +void printname(s) +char *s; +{ + register n = NAME_MAX; + int c; + + do { + if ((c = *s) == 0) break; + if (!isprint(c)) c = '?'; + putchar(c); + s++; + } while (--n); +} + +/* Print the pathname given by a linked list pointed to by `sp'. The + * names are in reverse order. + */ +void printrec(sp) +struct stack *sp; +{ + if (sp->st_next != 0) { + printrec(sp->st_next); + putchar('/'); + printname(sp->st_dir->d_name); + } +} + +/* Print the current pathname. */ +void printpath(mode, nlcr) +int mode; +int nlcr; +{ + if (ftop->st_next == 0) + putchar('/'); + else + printrec(ftop); + switch (mode) { + case 1: + printf(" (ino = %u, ", ftop->st_dir->d_inum); + break; + case 2: + printf(" (ino = %u)", ftop->st_dir->d_inum); + break; + } + if (nlcr) printf("\n"); +} + +/* Open the device. */ +void devopen() +{ + if ((dev = open(device, repair ? O_RDWR : O_RDONLY)) < 0) { + perror(device); + fatal(""); + } +} + +/* Close the device. */ +void devclose() +{ + if (close(dev) != 0) { + perror("close"); + fatal(""); + } +} + +/* Read or write a block. */ +void devio(bno, dir) +block_nr bno; +int dir; +{ + if (dir == READING && bno == thisblk) return; + thisblk = bno; + + lseek(dev, (off_t) btoa(bno), SEEK_SET); + if (dir == READING) { + if (read(dev, rwbuf, BLOCK_SIZE) == BLOCK_SIZE) + return; + } else { + if (write(dev, rwbuf, BLOCK_SIZE) == BLOCK_SIZE) + return; + } + + printf("%s: can't %s block %ld (error = 0x%x)\n", prog, + dir == READING ? "read" : "write", (long) bno, errno); + if (dir == READING) { + printf("Continuing with a zero-filled block.\n"); + memset(rwbuf, 0, BLOCK_SIZE); + return; + } + fatal(""); +} + +/* Read `size' bytes from the disk starting at byte `offset'. */ +void devread(offset, buf, size) +long offset; +char *buf; +int size; +{ + devio((block_nr) (offset / BLOCK_SIZE), READING); + memmove(buf, &rwbuf[(int) (offset % BLOCK_SIZE)], (size_t)size); +} + +/* Write `size' bytes to the disk starting at byte `offset'. */ +void devwrite(offset, buf, size) +long offset; +char *buf; +int size; +{ + if (!repair) fatal("internal error (devwrite)"); + if (size != BLOCK_SIZE) devio((block_nr) (offset / BLOCK_SIZE), READING); + memmove(&rwbuf[(int) (offset % BLOCK_SIZE)], buf, (size_t)size); + devio((block_nr) (offset / BLOCK_SIZE), WRITING); + changed = 1; +} + +/* Print a string with either a singular or a plural pronoun. */ +void pr(fmt, cnt, s, p) +char *fmt, *s, *p; +int cnt; +{ + printf(fmt, cnt, cnt == 1 ? s : p); +} + +/* Convert string to number. */ +bit_nr getnumber(s) +register char *s; +{ + register bit_nr n = 0; + + if (s == NULL) + return NO_BIT; + while (isdigit(*s)) + n = (n << 1) + (n << 3) + *s++ - '0'; + return (*s == '\0') ? n : NO_BIT; +} + +/* See if the list pointed to by `argv' contains numbers. */ +char **getlist(argv, type) +char ***argv, *type; +{ + register char **list = *argv; + register empty = 1; + + while (getnumber(**argv) != NO_BIT) { + (*argv)++; + empty = 0; + } + if (empty) { + printf("warning: no %s numbers given\n", type); + return(NULL); + } + return(list); +} + +/* Make a listing of the super block. If `repair' is set, ask the user + * for changes. + */ +void lsuper() +{ + char buf[80]; + + do { + printf("ninodes = %u", sb.s_ninodes); + if (input(buf, 80)) sb.s_ninodes = atol(buf); + printf("nzones = %u", sb.s_nzones); + if (input(buf, 80)) sb.s_nzones = atol(buf); + printf("imap_blocks = %u", sb.s_imap_blocks); + if (input(buf, 80)) sb.s_imap_blocks = atol(buf); + printf("zmap_blocks = %u", sb.s_zmap_blocks); + if (input(buf, 80)) sb.s_zmap_blocks = atol(buf); + printf("firstdatazone = %u", sb.s_firstdatazone); + if (input(buf, 80)) sb.s_firstdatazone = atol(buf); + printf("log_zone_size = %u", sb.s_log_zone_size); + if (input(buf, 80)) sb.s_log_zone_size = atol(buf); + printf("maxsize = %lu", sb.s_max_size); + if (input(buf, 80)) sb.s_max_size = atol(buf); + if (yes("ok now")) { + devwrite(btoa(BLK_SUPER), (char *) &sb, sizeof(sb)); + return; + } + } while (yes("Do you want to try again")); + if (repair) exit(0); +} + +/* Get the super block from either disk or user. Do some initial checks. */ +void getsuper() +{ + devread(btoa(BLK_SUPER), (char *) &sb, sizeof(sb)); + if (listsuper) lsuper(); + if (sb.s_magic == SUPER_V2) fatal("Cannot handle V2 file systems"); + if (sb.s_magic != SUPER_MAGIC) fatal("bad magic number in super block"); + if ((short) sb.s_ninodes <= 0) fatal("no inodes"); + if (sb.s_nzones <= 2) fatal("no zones"); + if ((short) sb.s_imap_blocks <= 0) fatal("no imap"); + if ((short) sb.s_zmap_blocks <= 0) fatal("no zmap"); + if ((short) sb.s_firstdatazone <= 1) fatal("first data zone too small"); + if ((short) sb.s_log_zone_size < 0) fatal("zone size < block size"); + if (sb.s_max_size <= 0) fatal("max. file size <= 0"); +} + +/* Check the super block for reasonable contents. */ +void chksuper() +{ + register n; + register off_t maxsize; + + n = bitmapsize((bit_t) sb.s_ninodes + 1, BLOCK_SIZE); + if (sb.s_magic != SUPER_MAGIC) fatal("bad magic number in super block"); + if ((short) sb.s_imap_blocks < n) fatal("too few imap blocks"); + if (sb.s_imap_blocks != n) { + pr("warning: expected %d imap_block%s", n, "", "s"); + printf(" instead of %d\n", sb.s_imap_blocks); + } + n = bitmapsize((bit_t) sb.s_nzones, BLOCK_SIZE); + if ((short) sb.s_zmap_blocks < n) fatal("too few zmap blocks"); + if (sb.s_zmap_blocks != n) { + pr("warning: expected %d zmap_block%s", n, "", "s"); + printf(" instead of %d\n", sb.s_zmap_blocks); + } + if (sb.s_firstdatazone >= sb.s_nzones) + fatal("first data zone too large"); + if ((unsigned short) sb.s_log_zone_size >= 8 * sizeof(block_nr)) + fatal("log_zone_size too large"); + if (sb.s_log_zone_size > 8) printf("warning: large log_zone_size (%d)\n", + sb.s_log_zone_size); + n = (BLK_ILIST + N_ILIST + SCALE - 1) >> sb.s_log_zone_size; + if ((short) sb.s_firstdatazone < n) fatal("first data zone too small"); + if (sb.s_firstdatazone != n) { + printf("warning: expected first data zone to be %d ", n); + printf("instead of %u\n", sb.s_firstdatazone); + } + maxsize = MAX_FILE_POS; + if (((maxsize - 1) >> sb.s_log_zone_size) / BLOCK_SIZE >= MAX_ZONES) + maxsize = ((long) MAX_ZONES * BLOCK_SIZE) << sb.s_log_zone_size; + if (sb.s_max_size != maxsize) { + printf("warning: expected max size to be %ld ", maxsize); + printf("instead of %ld\n", sb.s_max_size); + } +} + +/* Make a listing of the inodes given by `clist'. If `repair' is set, ask + * the user for changes. + */ +void lsi(clist) +char **clist; +{ + register bit_nr bit; + register ino_t ino; + d_inode inode, *ip = &inode; + char buf[80]; + + if (clist == 0) return; + while ((bit = getnumber(*clist++)) != NO_BIT) { + setbit(spec_imap, bit); + ino = bit; + do { + devread(inoaddr(ino), (char *) ip, INODE_SIZE); + printf("inode %u:\n", ino); + printf(" mode = %06o", ip->i_mode); + if (input(buf, 80)) ip->i_mode = atoo(buf); + printf(" nlinks = %6u", ip->i_nlinks); + if (input(buf, 80)) ip->i_nlinks = atol(buf); + printf(" size = %6ld", ip->i_size); + if (input(buf, 80)) ip->i_size = atol(buf); + if (yes("Write this back")) { + devwrite(inoaddr(ino), (char *) ip, INODE_SIZE); + break; + } + } while (yes("Do you want to change it again")); + } +} + +/* Allocate `nblk' blocks worth of bitmap. */ +bitchunk_t *allocbitmap(nblk) +int nblk; +{ + register bitchunk_t *bitmap; + + bitmap = (bitchunk_t *) alloc(nblk, BLOCK_SIZE); + *bitmap |= 1; + return(bitmap); +} + +/* Load the bitmap starting at block `bno' from disk. */ +void loadbitmap(bitmap, bno, nblk) +bitchunk_t *bitmap; +block_nr bno; +int nblk; +{ + register i; + register bitchunk_t *p; + + p = bitmap; + for (i = 0; i < nblk; i++, bno++, p += WORDS_PER_BLOCK) + devread(btoa(bno), (char *) p, BLOCK_SIZE); + *bitmap |= 1; +} + +/* Write the bitmap starting at block `bno' to disk. */ +void dumpbitmap(bitmap, bno, nblk) +bitchunk_t *bitmap; +block_nr bno; +int nblk; +{ + register i; + register bitchunk_t *p = bitmap; + + for (i = 0; i < nblk; i++, bno++, p += WORDS_PER_BLOCK) + devwrite(btoa(bno), (char *) p, BLOCK_SIZE); +} + +/* Set the bits given by `list' in the bitmap. */ +void fillbitmap(bitmap, lwb, upb, list) +bitchunk_t *bitmap; +bit_nr lwb, upb; +char **list; +{ + register bit_nr bit; + + if (list == 0) return; + while ((bit = getnumber(*list++)) != NO_BIT) + if (bit < lwb || bit >= upb) { + if (bitmap == spec_imap) + printf("inode number %u ", bit); + else + printf("zone number %u ", bit); + printf("out of range (ignored)\n"); + } else + setbit(bitmap, bit - lwb + 1); +} + +/* Deallocate the bitmap `p'. */ +void freebitmap(p) +bitchunk_t *p; +{ + free((char *) p); +} + +/* Get all the bitmaps used by this program. */ +void getbitmaps() +{ + imap = allocbitmap(N_IMAP); + zmap = allocbitmap(N_ZMAP); + spec_imap = allocbitmap(N_IMAP); + spec_zmap = allocbitmap(N_ZMAP); + dirmap = allocbitmap(N_IMAP); +} + +/* Release all the space taken by the bitmaps. */ +void putbitmaps() +{ + freebitmap(imap); + freebitmap(zmap); + freebitmap(spec_imap); + freebitmap(spec_zmap); + freebitmap(dirmap); +} + +/* `w1' and `w2' are differing words from two bitmaps that should be + * identical. Print what's the matter with them. + */ +void chkword(w1, w2, bit, type, n, report) +unsigned w1, w2; +char *type; +bit_nr bit; +int *n, *report; +{ + for (; (w1 | w2); w1 >>= 1, w2 >>= 1, bit++) + if ((w1 ^ w2) & 1 && ++(*n) % MAXPRINT == 0 && *report && + (!repair || automatic || yes("stop this listing"))) + *report = 0; + else if (*report) + if ((w1 & 1) && !(w2 & 1)) + printf("%s %u is missing\n", type, bit); + else if (!(w1 & 1) && (w2 & 1)) + printf("%s %u is not free\n", type, bit); +} + +/* Check if the given (correct) bitmap is identical with the one that is + * on the disk. If not, ask if the disk should be repaired. + */ +void chkmap(cmap, dmap, bit, blkno, nblk, type) +bitchunk_t *cmap, *dmap; +bit_nr bit; +block_nr blkno; +int nblk; +char *type; +{ + register bitchunk_t *p = dmap, *q = cmap; + int report = 1, nerr = 0; + int w = nblk * WORDS_PER_BLOCK; + + printf("Checking %s map\n", type); + loadbitmap(dmap, blkno, nblk); + do { + if (*p != *q) chkword(*p, *q, bit, type, &nerr, &report); + p++; + q++; + bit += 8 * sizeof(bitchunk_t); + } while (--w > 0); + + if ((!repair || automatic) && !report) printf("etc. "); + if (nerr > MAXPRINT || nerr > 10) printf("%d errors found. ", nerr); + if (nerr != 0 && yes("install a new map")) dumpbitmap(cmap, blkno, nblk); + if (nerr > 0) printf("\n"); +} + +/* See if the inodes that aren't allocated are cleared. */ +void chkilist() +{ + register ino_t ino = 1; + mode_t mode; + + printf("Checking inode list\n"); + do + if (!bitset(imap, (bit_nr) ino)) { + devread(inoaddr(ino), (char *) &mode, sizeof(mode)); + if (mode != I_NOT_ALLOC) { + printf("mode inode %u not cleared", ino); + if (yes(". clear")) devwrite(inoaddr(ino), nullbuf, + INODE_SIZE); + } + } + while (++ino <= sb.s_ninodes); + printf("\n"); +} + +/* Allocate an array to maintain the inode reference counts in. */ +void getcount() +{ + count = (nlink_t *) alloc(sb.s_ninodes + 1, sizeof(nlink_t)); +} + +/* The reference count for inode `ino' is wrong. Ask if it should be adjusted. */ +void counterror(ino) +ino_t ino; +{ + d_inode inode; + + if (firstcnterr) { + printf("INODE NLINK COUNT\n"); + firstcnterr = 0; + } + devread(inoaddr(ino), (char *) &inode, INODE_SIZE); + count[ino] += inode.i_nlinks; + printf("%5u %5u %5u", ino, (unsigned) inode.i_nlinks, count[ino]); + if (yes(" adjust")) { + if ((inode.i_nlinks = count[ino]) == 0) { + fatal("internal error (counterror)"); +/* This would be a patch + inode.i_mode = I_NOT_ALLOC; + clrbit(imap, (bit_nr) ino); +*/ + } + devwrite(inoaddr(ino), (char *) &inode, INODE_SIZE); + } +} + +/* Check if the reference count of the inodes are correct. The array `count' + * is maintained as follows: an entry indexed by the inode number is + * incremented each time a link is found; when the inode is read the link + * count in there is substracted from the corresponding entry in `count'. + * Thus, when the whole file system has been traversed, all the entries + * should be zero. + */ +void chkcount() +{ + register ino_t ino; + + for (ino = 1; ino <= sb.s_ninodes; ino++) + if (count[ino] != 0) counterror(ino); + if (!firstcnterr) printf("\n"); +} + +/* Deallocate the `count' array. */ +void freecount() +{ + free((char *) count); +} + +/* Print the inode permission bits given by mode and shift. */ +void printperm(mode, shift, special, overlay) +mode_t mode; +int shift; +int special; +int overlay; +{ + if (mode >> shift & R_BIT) + putchar('r'); + else + putchar('-'); + if (mode >> shift & W_BIT) + putchar('w'); + else + putchar('-'); + if (mode & special) + putchar(overlay); + else + if (mode >> shift & X_BIT) + putchar('x'); + else + putchar('-'); +} + +/* List the given inode. */ +void list(ino, ip) +ino_t ino; +d_inode *ip; +{ + if (firstlist) { + firstlist = 0; + printf(" inode permission link size name\n"); + } + printf("%6u ", ino); + switch (ip->i_mode & I_TYPE) { + case I_REGULAR: putchar('-'); break; + case I_DIRECTORY: putchar('d'); break; + case I_CHAR_SPECIAL: putchar('c'); break; + case I_BLOCK_SPECIAL: putchar('b'); break; + case I_NAMED_PIPE: putchar('p'); break; +#ifdef I_SYMBOLIC_LINK + case I_SYMBOLIC_LINK: putchar('l'); break; +#endif + default: putchar('?'); +} + printperm(ip->i_mode, 6, I_SET_UID_BIT, 's'); + printperm(ip->i_mode, 3, I_SET_GID_BIT, 's'); + printperm(ip->i_mode, 0, STICKY_BIT, 't'); + printf(" %3u ", ip->i_nlinks); + switch (ip->i_mode & I_TYPE) { + case I_CHAR_SPECIAL: + case I_BLOCK_SPECIAL: + printf(" %2x,%2x ", (dev_t) ip->i_zone[0] >> MAJOR & 0xFF, + (dev_t) ip->i_zone[0] >> MINOR & 0xFF); + break; + default: printf("%7ld ", ip->i_size); + } + printpath(0, 1); +} + +/* Remove an entry from a directory if ok with the user. + * Don't name the function remove() - that is owned by ANSI, and chaos results + * when it is a macro. + */ +int Remove(dp) +dir_struct *dp; +{ + setbit(spec_imap, (bit_nr) dp->d_inum); + if (yes(". remove entry")) { + count[dp->d_inum]--; + memset((void *) dp, 0, sizeof(dir_struct)); + return(1); + } + return(0); +} + +/* Convert string so that embedded control characters are printable. */ +void make_printable_name(dst, src, n) +register char *dst; +register char *src; +register int n; +{ + register int c; + + while (--n >= 0 && (c = *src++) != '\0') { + if (isprint(c) && c != '\\') + *dst++ = c; + else { + *dst++ = '\\'; + switch (c) { + case '\\': + *dst++ = '\\'; break; + case '\b': + *dst++ = 'b'; break; + case '\f': + *dst++ = 'f'; break; + case '\n': + *dst++ = 'n'; break; + case '\r': + *dst++ = 'r'; break; + case '\t': + *dst++ = 't'; break; + default: + *dst++ = '0' + ((c >> 6) & 03); + *dst++ = '0' + ((c >> 3) & 07); + *dst++ = '0' + (c & 07); + } + } + } + *dst = '\0'; +} + +/* See if the `.' or `..' entry is as expected. */ +int chkdots(ino, pos, dp, exp) +ino_t ino, exp; +off_t pos; +dir_struct *dp; +{ + char printable_name[4 * NAME_MAX + 1]; + + if (dp->d_inum != exp) { + make_printable_name(printable_name, dp->d_name, sizeof(dp->d_name)); + printf("bad %s in ", printable_name); + printpath(1, 0); + printf("%s is linked to %u ", printable_name, dp->d_inum); + printf("instead of %u)", exp); + setbit(spec_imap, (bit_nr) ino); + setbit(spec_imap, (bit_nr) dp->d_inum); + setbit(spec_imap, (bit_nr) exp); + if (yes(". repair")) { + count[dp->d_inum]--; + dp->d_inum = exp; + count[exp]++; + return(0); + } + } else if (pos != (dp->d_name[1] ? DIR_ENTRY_SIZE : 0)) { + make_printable_name(printable_name, dp->d_name, sizeof(dp->d_name)); + printf("warning: %s has offset %ld in ", printable_name, pos); + printpath(1, 0); + printf("%s is linked to %u)\n", printable_name, dp->d_inum); + setbit(spec_imap, (bit_nr) ino); + setbit(spec_imap, (bit_nr) dp->d_inum); + setbit(spec_imap, (bit_nr) exp); + } + return(1); +} + +/* Check the name in a directory entry. */ +int chkname(ino, dp) +ino_t ino; +dir_struct *dp; +{ + register n = NAME_MAX + 1; + register char *p = dp->d_name; + + if (*p == '\0') { + printf("null name found in "); + printpath(0, 0); + setbit(spec_imap, (bit_nr) ino); + if (Remove(dp)) return(0); + } + while (*p != '\0' && --n != 0) + if (*p++ == '/') { + printf("found a '/' in entry of directory "); + printpath(1, 0); + setbit(spec_imap, (bit_nr) ino); + printf("entry = '"); + printname(dp->d_name); + printf("')"); + if (Remove(dp)) return(0); + break; + } + return(1); +} + +/* Check a directory entry. Here the routine `descendtree' is called + * recursively to check the file or directory pointed to by the entry. + */ +int chkentry(ino, pos, dp) +ino_t ino; +off_t pos; +dir_struct *dp; +{ + if (dp->d_inum < ROOT_INODE || dp->d_inum > sb.s_ninodes) { + printf("bad inode found in directory "); + printpath(1, 0); + printf("ino found = %u, ", dp->d_inum); + printf("name = '"); + printname(dp->d_name); + printf("')"); + if (yes(". remove entry")) { + memset((void *) dp, 0, sizeof(dir_struct)); + return(0); + } + return(1); + } + if ((unsigned) count[dp->d_inum] == CHAR_MAX) { + printf("too many links to ino %u\n", dp->d_inum); + printf("discovered at entry '"); + printname(dp->d_name); + printf("' in directory "); + printpath(0, 1); + if (Remove(dp)) return(0); + } + count[dp->d_inum]++; + if (strcmp(dp->d_name, ".") == 0) { + ftop->st_presence |= DOT; + return(chkdots(ino, pos, dp, ino)); + } + if (strcmp(dp->d_name, "..") == 0) { + ftop->st_presence |= DOTDOT; + return(chkdots(ino, pos, dp, ino == ROOT_INODE ? ino : + ftop->st_next->st_dir->d_inum)); + } + if (!chkname(ino, dp)) return(0); + if (bitset(dirmap, (bit_nr) dp->d_inum)) { + printf("link to directory discovered in "); + printpath(1, 0); + printf("name = '"); + printname(dp->d_name); + printf("', dir ino = %u)", dp->d_inum); + return !Remove(dp); + } + return(descendtree(dp)); +} + +/* Check a zone of a directory by checking all the entries in the zone. + * The zone is split up into chunks to not allocate too much stack. + */ +int chkdirzone(ino, ip, pos, zno) +ino_t ino; +d_inode *ip; +off_t pos; +zone_nr zno; +{ + dir_struct dirblk[CDIRECT]; + register dir_struct *dp; + register n = SCALE * (NR_DIR_ENTRIES(BLOCK_SIZE) / CDIRECT), dirty; + register long offset = zaddr(zno); + register off_t size = 0; + + do { + devread(offset, (char *) dirblk, DIRCHUNK); + dirty = 0; + for (dp = dirblk; dp < &dirblk[CDIRECT]; dp++) { + if (dp->d_inum != NO_ENTRY && !chkentry(ino, pos, dp)) + dirty = 1; + pos += DIR_ENTRY_SIZE; + if (dp->d_inum != NO_ENTRY) size = pos; + } + if (dirty) devwrite(offset, (char *) dirblk, DIRCHUNK); + offset += DIRCHUNK; + } while (--n); + + if (size > ip->i_size) { + printf("size not updated of directory "); + printpath(2, 0); + if (yes(". extend")) { + setbit(spec_imap, (bit_nr) ino); + ip->i_size = size; + devwrite(inoaddr(ino), (char *) ip, INODE_SIZE); + } + } + return(1); +} + +/* There is something wrong with the given zone. Print some details. */ +void errzone(mess, zno, level, pos) +char *mess; +zone_nr zno; +int level; +off_t pos; +{ + printf("%s zone in ", mess); + printpath(1, 0); + printf("zno = %u, type = ", zno); + switch (level) { + case 0: printf("DATA"); break; + case 1: printf("SINGLE INDIRECT"); break; + case 2: printf("DOUBLE INDIRECT"); break; + default: printf("VERY INDIRECT"); + } + printf(", pos = %ld)\n", pos); +} + +/* Found the given zone in the given inode. Check it, and if ok, mark it + * in the zone bitmap. + */ +int markzone(ino, zno, level, pos) +ino_t ino; +zone_nr zno; +int level; +off_t pos; +{ + register bit_nr bit = (bit_nr) zno - FIRST + 1; + + ztype[level]++; + if (zno < FIRST || zno >= sb.s_nzones) { + errzone("out-of-range", zno, level, pos); + return(0); + } + if (bitset(zmap, bit)) { + setbit(spec_zmap, bit); + errzone("duplicate", zno, level, pos); + return(0); + } + nfreezone--; + if (bitset(spec_zmap, bit)) errzone("found", zno, level, pos); + setbit(zmap, bit); + return(1); +} + +/* Check an indirect zone by checking all of its entries. + * The zone is split up into chunks to not allocate too much stack. + */ +int chkindzone(ino, ip, pos, zno, level) +ino_t ino; +d_inode *ip; +off_t *pos; +zone_nr zno; +int level; +{ + zone_nr indirect[CINDIR]; + register n = NR_INDIRECTS / CINDIR; + register long offset = zaddr(zno); + + do { + devread(offset, (char *) indirect, INDCHUNK); + if (!chkzones(ino, ip, pos, indirect, CINDIR, level - 1)) return(0); + offset += INDCHUNK; + } while (--n && *pos < ip->i_size); + return(1); +} + +/* Return the size of a gap in the file, represented by a null zone number + * at some level of indirection. + */ +off_t jump(level) +int level; +{ + off_t power = ZONE_SIZE; + + if (level != 0) do + power *= NR_INDIRECTS; + while (--level); + return(power); +} + +/* Check a zone, which may be either a normal data zone, a directory zone, + * or an indirect zone. + */ +int zonechk(ino, ip, pos, zno, level) +ino_t ino; +d_inode *ip; +off_t *pos; +zone_nr zno; +int level; +{ + if (level == 0) { + if ((ip->i_mode & I_TYPE) == I_DIRECTORY && + !chkdirzone(ino, ip, *pos, zno)) + return(0); + *pos += ZONE_SIZE; + return(1); + } else + return chkindzone(ino, ip, pos, zno, level); +} + +/* Check a list of zones given by `zlist'. */ +int chkzones(ino, ip, pos, zlist, len, level) +ino_t ino; +d_inode *ip; +off_t *pos; +zone_nr *zlist; +int len; +int level; +{ + register ok = 1, i; + + /* The check on the position in the next loop is commented out, since FS + * now requires valid zone numbers in each level that is necessary and FS + * always deleted all the zones in the double indirect block. + */ + for (i = 0; i < len /* && *pos < ip->i_size */ ; i++) + if (zlist[i] == NO_ZONE) + *pos += jump(level); + else if (!markzone(ino, zlist[i], level, *pos)) { + *pos += jump(level); + ok = 0; + } else if (!zonechk(ino, ip, pos, zlist[i], level)) + ok = 0; + return(ok); +} + +/* Check a file or a directory. */ +int chkfile(ino, ip) +ino_t ino; +d_inode *ip; +{ + register ok, i, level; + off_t pos = 0; + + ok = chkzones(ino, ip, &pos, (zone_nr *)&ip->i_zone[0], NR_DZONE_NUM, 0); + for (i = NR_DZONE_NUM, level = 1; i < NR_ZONE_NUMS; i++, level++) + ok &= chkzones(ino, ip, &pos, (zone_nr *)&ip->i_zone[i], 1, level); + return(ok); +} + +/* Check a directory by checking the contents. Check if . and .. are present. */ +int chkdirectory(ino, ip) +ino_t ino; +d_inode *ip; +{ + register ok; + + setbit(dirmap, (bit_nr) ino); + ok = chkfile(ino, ip); + if (!(ftop->st_presence & DOT)) { + printf(". missing in "); + printpath(2, 1); + ok = 0; + } + if (!(ftop->st_presence & DOTDOT)) { + printf(".. missing in "); + printpath(2, 1); + ok = 0; + } + return(ok); +} + +#ifdef I_SYMBOLIC_LINK + +/* Check the validity of a symbolic link. */ +int chklink(ino, ip) +ino_t ino; +d_inode *ip; +{ + int ok; + + ok = chkfile(ino, ip); + if (ip->i_size <= 0 || ip->i_size > BLOCK_SIZE) { + if (ip->i_size == 0) + printf("empty symbolic link "); + else + printf("symbolic link too large (size %ld) ", ip->i_size); + printpath(2, 1); + ok = 0; + } + return(ok); +} + +#endif + +/* Check the validity of a special file. */ +int chkspecial(ino, ip) +ino_t ino; +d_inode *ip; +{ + int i, ok; + + ok = 1; + if ((dev_t) ip->i_zone[0] == NO_DEV) { + printf("illegal device number %u for special file ", ip->i_zone[0]); + printpath(2, 1); + ok = 0; + } + + /* FS will not use the remaining "zone numbers" but 1.6.11++ will panic if + * they are nonzero, since this should not happen. + */ + for (i = 1; i < NR_ZONE_NUMS; i++) + if (ip->i_zone[i] != NO_ZONE) { + printf("nonzero zone number %u for special file ", + ip->i_zone[i]); + printpath(2, 1); + ok = 0; + } + return(ok); +} + +/* Check the mode and contents of an inode. */ +int chkmode(ino, ip) +ino_t ino; +d_inode *ip; +{ + switch (ip->i_mode & I_TYPE) { + case I_REGULAR: + nregular++; + return chkfile(ino, ip); + case I_DIRECTORY: + ndirectory++; + return chkdirectory(ino, ip); + case I_BLOCK_SPECIAL: + nblkspec++; + return chkspecial(ino, ip); + case I_CHAR_SPECIAL: + ncharspec++; + return chkspecial(ino, ip); + case I_NAMED_PIPE: + npipe++; + return chkfile(ino, ip); +#ifdef I_SYMBOLIC_LINK + case I_SYMBOLIC_LINK: + nsyml++; + return chklink(ino, ip); +#endif + default: + nbadinode++; + printf("bad mode of "); + printpath(1, 0); + printf("mode = %o)", ip->i_mode); + return(0); + } +} + +/* Check an inode. */ +int chkinode(ino, ip) +ino_t ino; +d_inode *ip; +{ + if (ino == ROOT_INODE && (ip->i_mode & I_TYPE) != I_DIRECTORY) { + printf("root inode is not a directory "); + printf("(ino = %u, mode = %o)\n", ino, ip->i_mode); + fatal(""); + } + if (ip->i_nlinks == 0) { + printf("link count zero of "); + printpath(2, 0); + return(0); + } + nfreeinode--; + setbit(imap, (bit_nr) ino); + if ((unsigned) ip->i_nlinks > CHAR_MAX) { + printf("link count too big in "); + printpath(1, 0); + printf("cnt = %u)\n", (unsigned) ip->i_nlinks); + count[ino] -= CHAR_MAX; + setbit(spec_imap, (bit_nr) ino); + } else + count[ino] -= (unsigned) ip->i_nlinks; + return chkmode(ino, ip); +} + +/* Check the directory entry pointed to by dp, by checking the inode. */ +int descendtree(dp) +dir_struct *dp; +{ + d_inode inode; + register ino_t ino = dp->d_inum; + register visited; + struct stack stk; + + stk.st_dir = dp; + stk.st_next = ftop; + ftop = &stk; + if (bitset(spec_imap, (bit_nr) ino)) { + printf("found inode %u: ", ino); + printpath(0, 1); + } + visited = bitset(imap, (bit_nr) ino); + if (!visited || listing) { + devread(inoaddr(ino), (char *) &inode, INODE_SIZE); + if (listing) list(ino, &inode); + if (!visited && !chkinode(ino, &inode)) { + setbit(spec_imap, (bit_nr) ino); + if (yes("remove")) { + count[ino] += inode.i_nlinks - 1; + clrbit(imap, (bit_nr) ino); + devwrite(inoaddr(ino), nullbuf, INODE_SIZE); + memset((void *) dp, 0, sizeof(dir_struct)); + ftop = ftop->st_next; + return(0); + } + } + } + ftop = ftop->st_next; + return(1); +} + +/* Check the file system tree. */ +void chktree() +{ + dir_struct dir; + + nfreeinode = sb.s_ninodes; + nfreezone = N_DATA; + dir.d_inum = ROOT_INODE; + dir.d_name[0] = 0; + if (!descendtree(&dir)) fatal("bad root inode"); + putchar('\n'); +} + +/* Print the totals of all the objects found. */ +void printtotal() +{ + printf("blocksize = %5d ", BLOCK_SIZE); + printf("zonesize = %5d\n", ZONE_SIZE); + printf("\n"); + pr("%6u Regular file%s\n", nregular, "", "s"); + pr("%6u Director%s\n", ndirectory, "y", "ies"); + pr("%6u Block special file%s\n", nblkspec, "", "s"); + pr("%6u Character special file%s\n", ncharspec, "", "s"); + if (nbadinode != 0) pr("%6u Bad inode%s\n", nbadinode, "", "s"); + pr("%6u Free inode%s\n", nfreeinode, "", "s"); + pr("%6u Named pipe%s\n", npipe, "", "s"); + pr("%6u Symbolic link%s\n", nsyml, "", "s"); +/* Don't print some fields. + printf("\n"); + pr("%6u Data zone%s\n", ztype[0], "", "s"); + pr("%6u Single indirect zone%s\n", ztype[1], "", "s"); + pr("%6u Double indirect zone%s\n", ztype[2], "", "s"); +*/ + pr("%6u Free zone%s\n", nfreezone, "", "s"); +} + +/* Check the device which name is given by `f'. The inodes listed by `clist' + * should be listed separately, and the inodes listed by `ilist' and the zones + * listed by `zlist' should be watched for while checking the file system. + */ + +void chkdev(f, clist, ilist, zlist) +char *f, **clist, **ilist, **zlist; +{ + if (automatic) repair = 1; + device = f; + initvars(); + + devopen(); + + getsuper(); + chksuper(); + + lsi(clist); + + getbitmaps(); + + fillbitmap(spec_imap, (bit_nr) 1, (bit_nr) sb.s_ninodes + 1, ilist); + fillbitmap(spec_zmap, (bit_nr) FIRST, (bit_nr) sb.s_nzones, zlist); + + getcount(); + chktree(); + chkmap(zmap, spec_zmap, (bit_nr) FIRST - 1, BLK_ZMAP, N_ZMAP, "zone"); + chkcount(); + chkmap(imap, spec_imap, (bit_nr) 0, BLK_IMAP, N_IMAP, "inode"); + chkilist(); + printtotal(); + + putbitmaps(); + freecount(); + devclose(); + + if (changed) printf("----- FILE SYSTEM HAS BEEN MODIFIED -----\n\n"); +} + +int main(argc, argv) +int argc; +char **argv; +{ + register char **clist = 0, **ilist = 0, **zlist = 0; + + register devgiven = 0; + register char *arg; + + if ((1 << BITSHIFT) != 8 * sizeof(bitchunk_t)) { + printf("Fsck was compiled with the wrong BITSHIFT!\n"); + exit(1); + } + + sync(); + prog = *argv++; + while ((arg = *argv++) != 0) + if (arg[0] == '-' && arg[1] != 0 && arg[2] == 0) switch (arg[1]) { + case 'a': automatic ^= 1; break; + case 'c': + clist = getlist(&argv, "inode"); + break; + case 'i': + ilist = getlist(&argv, "inode"); + break; + case 'z': + zlist = getlist(&argv, "zone"); + break; + case 'r': repair ^= 1; break; + case 'l': listing ^= 1; break; + case 's': listsuper ^= 1; break; + default: + printf("%s: unknown flag '%s'\n", prog, arg); + } + else { + chkdev(arg, clist, ilist, zlist); + clist = 0; + ilist = 0; + zlist = 0; + devgiven = 1; + } + if (!devgiven) { + printf("Usage: fsck1 [-acilrsz] file\n"); + exit(1); + } + return(0); +} diff --git a/commands/simple/getty.c b/commands/simple/getty.c new file mode 100755 index 000000000..ffeb0c938 --- /dev/null +++ b/commands/simple/getty.c @@ -0,0 +1,197 @@ +/* getty - get tty speed Author: Fred van Kempen */ + +/* + * GETTY - Initialize and serve a login-terminal for INIT. + * Also, select the correct speed. The STTY() code + * was taken from stty(1).c; which was written by + * Andrew S. Tanenbaum. + * + * Usage: getty [-c filename] [-h] [-k] [-t] line [speed] + * + * Version: 3.4 02/17/90 + * + * Author: F. van Kempen, MicroWalt Corporation + * + * Modifications: + * All the good stuff removed to get a minimal getty, because + * many modems don't like all that fancy speed detection stuff. + * 03/03/91 Kees J. Bot (kjb@cs.vu.nl) + * + * Uname(), termios. More nonsense removed. (The result has + * only 10% of the original functionality, but a 10x chance of + * working.) + * 12/12/92 Kees J. Bot + * + * Customizable login banner. + * 11/13/95 Kees J. Bot + * + * Suspend/resume signals removed. + * 2001-04-04 Kees J. Bot + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/utsname.h> + +char LOGIN[] = "/usr/bin/login"; +char SHELL[] = "/bin/sh"; + +char *tty_name; /* name of the line */ + +/* Crude indication of a tty being physically secure: */ +#define securetty(dev) ((unsigned) ((dev) - 0x0400) < (unsigned) 8) + +void std_out(char *s) +{ + write(1, s, strlen(s)); +} + +/* Read one character from stdin. + */ +int readch(void) +{ + int st; + char ch1; + + /* read character from TTY */ + st = read(0, &ch1, 1); + if (st == 0) { + std_out("\n"); + exit(0); + } + if (st < 0) { + std_out("getty: "); + std_out(tty_name); + std_out(": read error\n"); + exit(1); + } + return(ch1 & 0xFF); +} + + +/* Handle the process of a GETTY. + */ +void do_getty(char *name, size_t len, char **args) +{ + register char *np, *s, *s0; + int ch; + struct utsname utsname; + char **banner; + static char *def_banner[] = { "%s Release %r Version %v\n\n%n login: ", 0 }; + + /* Default banner? */ + if (args[0] == NULL) args = def_banner; + + /* Display prompt. */ + ch = ' '; + *name = '\0'; + while (ch != '\n') { + /* Get data about this machine. */ + uname(&utsname); + + /* Print the banner. */ + for (banner = args; *banner != NULL; banner++) { + std_out(banner == args ? "\n" : " "); + s0 = *banner; + for (s = *banner; *s != 0; s++) { + if (*s == '\\') { + write(1, s0, s-s0); + s0 = s+2; + switch (*++s) { + case 'n': std_out("\n"); break; + case 's': std_out(" "); break; + case 't': std_out("\t"); break; + case 0: goto leave; + default: s0 = s; + } + } else + if (*s == '%') { + write(1, s0, s-s0); + s0 = s+2; + switch (*++s) { + case 's': std_out(utsname.sysname); break; + case 'n': std_out(utsname.nodename); break; + case 'r': std_out(utsname.release); break; + case 'v': std_out(utsname.version); break; + case 'm': std_out(utsname.machine); break; + case 'p': std_out(utsname.arch); break; +#if __minix_vmd + case 'k': std_out(utsname.kernel); break; + case 'h': std_out(utsname.hostname); break; + case 'b': std_out(utsname.bus); break; +#endif + case 0: goto leave; + default: s0 = s-1; + } + } + } + leave: + write(1, s0, s-s0); + } + + np = name; + while ((ch = readch()) != '\n') { + if (np < name + len) *np++ = ch; + } + *np = '\0'; + if (*name == '\0') ch = ' '; /* blank line typed! */ + } +} + + +/* Execute the login(1) command with the current + * username as its argument. It will reply to the + * calling user by typing "Password: "... + */ +void do_login(char *name) +{ + struct stat st; + + execl(LOGIN, LOGIN, name, (char *) NULL); + /* Failed to exec login. Impossible, but true. Try a shell, but only if + * the terminal is more or less secure, because it will be a root shell. + */ + std_out("getty: can't exec "); + std_out(LOGIN); + std_out("\n"); + if (fstat(0, &st) == 0 && S_ISCHR(st.st_mode) && securetty(st.st_rdev)) { + execl(SHELL, SHELL, (char *) NULL); + } +} + + +int main(int argc, char **argv) +{ + register char *s; + char name[30]; + struct sigaction sa; + + /* Don't let QUIT dump core. */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = exit; + sigaction(SIGQUIT, &sa, NULL); + + tty_name = ttyname(0); + if (tty_name == NULL) { + std_out("getty: tty name unknown\n"); + pause(); + return(1); + } + + chown(tty_name, 0, 0); /* set owner of TTY to root */ + chmod(tty_name, 0600); /* mode to max secure */ + + do_getty(name, sizeof(name), argv+1); /* handle getty() */ + name[29] = '\0'; /* make sure the name fits! */ + + do_login(name); /* and call login(1) if OK */ + + return(1); /* never executed */ +} diff --git a/commands/simple/gomoku.c b/commands/simple/gomoku.c new file mode 100755 index 000000000..3c2996eaf --- /dev/null +++ b/commands/simple/gomoku.c @@ -0,0 +1,739 @@ +/* gomoku - 5 in a row game Author: ? */ + +/* This program plays a very old Japanese game called GO-MOKU, + perhaps better known as 5-in-line. The game is played on + a board with 19 x 19 squares, and the object of the game is + to get 5 stones in a row. +*/ + +#include <sys/types.h> +#include <curses.h> +#include <ctype.h> +#include <stdlib.h> +#include <unistd.h> + +/* Size of the board */ +#define SIZE 19 + +/* Importance of attack (1..16) */ +#define AttackFactor 4 + +/* Value of having 0, 1,2,3,4 or 5 pieces in line */ +int Weight[7] = {0, 0, 4, 20, 100, 500, 0}; + +#define Null 0 +#define Horiz 1 +#define DownLeft 2 +#define DownRight 3 +#define Vert 4 + +/* The two players */ +#define Empty 0 +#define Cross 1 +#define Nought 2 + +char PieceChar[Nought + 1] = {' ', 'X', '0'}; + +int Board[SIZE + 1][SIZE + 1];/* The board */ +int Player; /* The player whose move is next */ +int TotalLines; /* The number of Empty lines left */ +int GameWon; /* Set if one of the players has won */ + +int Line[4][SIZE + 1][SIZE + 1][Nought + 1]; + +/* Value of each square for each player */ +int Value[SIZE + 1][SIZE + 1][Nought + 1]; + +int X, Y; /* Move coordinates */ +char Command; /* Command from keyboard */ +int AutoPlay = FALSE; /* The program plays against itself */ + +_PROTOTYPE(void Initialize, (void)); +_PROTOTYPE(int Abort, (char *s)); +_PROTOTYPE(void WriteLetters, (void)); +_PROTOTYPE(void WriteLine, (int j, int *s)); +_PROTOTYPE(void WriteBoard, (int N, int *Top, int *Middle, int *Bottom)); +_PROTOTYPE(void SetUpScreen, (void)); +_PROTOTYPE(void GotoSquare, (int x, int y)); +_PROTOTYPE(void PrintMove, (int Piece, int X, int Y)); +_PROTOTYPE(void ClearMove, (void)); +_PROTOTYPE(void PrintMsg, (char *Str)); +_PROTOTYPE(void ClearMsg, (void)); +_PROTOTYPE(void WriteCommand, (char *S)); +_PROTOTYPE(void ResetGame, (int FirstGame)); +_PROTOTYPE(int OpponentColor, (int Player)); +_PROTOTYPE(void BlinkRow, (int X, int Y, int Dx, int Dy, int Piece)); +_PROTOTYPE(void BlinkWinner, (int Piece, int X, int Y, int WinningLine)); +_PROTOTYPE(int Random, (int x)); +_PROTOTYPE(void Add, (int *Num)); +_PROTOTYPE(void Update, (int Lin[], int Valu[], int Opponent)); +_PROTOTYPE(void MakeMove, (int X, int Y)); +_PROTOTYPE(int GameOver, (void)); +_PROTOTYPE(void FindMove, (int *X, int *Y)); +_PROTOTYPE(char GetChar, (void)); +_PROTOTYPE(void ReadCommand, (int X, int Y, char *Command)); +_PROTOTYPE(void InterpretCommand, (int Command)); +_PROTOTYPE(void PlayerMove, (void)); +_PROTOTYPE(void ProgramMove, (void)); +_PROTOTYPE(int main, (void)); + +/* Set terminal to raw mode. */ +void Initialize() +{ + srand(getpid() + 13); /* Initialize the random seed with our pid */ + initscr(); + raw(); + noecho(); + clear(); +} + +/* Reset terminal and exit from the program. */ +int Abort(s) +char *s; +{ + move(LINES - 1, 0); + refresh(); + endwin(); + exit(0); +} + +/* Set up the screen ----------------------------------------------- */ + +/* Write the letters */ +void WriteLetters() +{ + int i; + + addch(' '); + addch(' '); + for (i = 1; i <= SIZE; i++) printw(" %c", 'A' + i - 1); + addch('\n'); +} + +/* Write one line of the board */ +void WriteLine(j, s) +int j; +int *s; +{ + int i; + + printw("%2d ", j); + addch(s[0]); + for (i = 2; i <= SIZE - 1; i++) { + addch(s[1]); + addch(s[2]); + } + addch(s[1]); + addch(s[3]); + printw(" %-2d\n", j); +} + +/* Print the Empty board and the border */ +void WriteBoard(N, Top, Middle, Bottom) +int N; +int *Top, *Middle, *Bottom; +{ + int j; + + move(1, 0); + WriteLetters(); + WriteLine(N, Top); + for (j = N - 1; j >= 2; j--) WriteLine(j, Middle); + WriteLine(1, Bottom); + WriteLetters(); +} + +/* Sets up the screen with an Empty board */ +void SetUpScreen() +{ + int top[4], middle[4], bottom[4]; + + top[0] = ACS_ULCORNER; + top[1] = ACS_HLINE; + top[2] = ACS_TTEE; + top[3] = ACS_URCORNER; + + middle[0] = ACS_LTEE; + middle[1] = ACS_HLINE; + middle[2] = ACS_PLUS; + middle[3] = ACS_RTEE; + + bottom[0] = ACS_LLCORNER; + bottom[1] = ACS_HLINE; + bottom[2] = ACS_BTEE; + bottom[3] = ACS_LRCORNER; + + WriteBoard(SIZE, top, middle, bottom); +} + +/* Show moves ----------------------------------------------- */ + +void GotoSquare(x, y) +int x, y; +{ + move(SIZE + 2 - y, 1 + x * 2); +} + +/* Prints a move */ +void PrintMove(Piece, X, Y) +int Piece; +int X, Y; +{ + move(22, 49); + printw("%c %c %d", PieceChar[Piece], 'A' + X - 1, Y); + clrtoeol(); + GotoSquare(X, Y); + addch(PieceChar[Piece]); + GotoSquare(X, Y); + refresh(); +} + +/* Clears the line where a move is displayed */ +void ClearMove() +{ + move(22, 49); + clrtoeol(); +} + +/* Message handling ---------------------------------------------- */ + +/* Prints a message */ +void PrintMsg(Str) +char *Str; +{ + mvprintw(23, 1, "%s", Str); +} + +/* Clears the message about the winner */ +void ClearMsg() +{ + move(23, 1); + clrtoeol(); +} + +/* Highlights the first letter of S */ +void WriteCommand(S) +char *S; +{ + standout(); + addch(*S); + standend(); + printw("%s", S + 1); +} + +/* Display the board ----------------------------------------------- */ + +/* Resets global variables to start a new game */ +void ResetGame(FirstGame) +int FirstGame; +{ + int I, J; + int C, D; + + SetUpScreen(); + if (FirstGame) { + move(1, 49); + addstr("G O M O K U"); + move(3, 49); + WriteCommand("Newgame "); + WriteCommand("Quit "); + move(5, 49); + WriteCommand("Auto"); + move(7, 49); + WriteCommand("Play"); + move(9, 49); + WriteCommand("Hint"); + move(14, 60); + WriteCommand("Left, "); + WriteCommand("Right, "); + move(16, 60); + WriteCommand("Up, "); + WriteCommand("Down"); + move(18, 60); + standout(); + addstr("SPACE"); + move(20, 49); + WriteCommand(" NOTE: Use Num Lock & arrows"); + standend(); + mvaddstr(14, 49, "7 8 9"); + mvaddch(15, 52, ACS_UARROW); + mvaddch(16, 49, '4'); + addch(ACS_LARROW); + mvaddch(16, 54, ACS_RARROW); + addch('6'); + mvaddch(17, 52, ACS_DARROW); + mvaddstr(18, 49, "1 2 3"); + FirstGame = FALSE; + } else { + ClearMsg(); + ClearMove(); + } + + /* Clear tables */ + for (I = 1; I <= SIZE; I++) for (J = 1; J <= SIZE; J++) { + Board[I][J] = Empty; + for (C = Cross; C <= Nought; C++) { + Value[I][J][C] = 0; + for (D = 0; D <= 3; D++) Line[D][I][J][C] = 0; + } + } + + /* Cross starts */ + Player = Cross; + /* Total number of lines */ + TotalLines = 2 * 2 * (SIZE * (SIZE - 4) + (SIZE - 4) * (SIZE - 4)); + GameWon = FALSE; +} + +int OpponentColor(Player) +int Player; +{ + if (Player == Cross) + return Nought; + else + return Cross; +} + +/* Blink the row of 5 stones */ +void BlinkRow(X, Y, Dx, Dy, Piece) +int X, Y, Dx, Dy, Piece; +{ + int I; + + attron(A_BLINK); + for (I = 1; I <= 5; I++) { + GotoSquare(X, Y); + addch(PieceChar[Piece]); + X = X - Dx; + Y = Y - Dy; + } + attroff(A_BLINK); +} + +/* Prints the 5 winning stones in blinking color */ +void BlinkWinner(Piece, X, Y, WinningLine) +int Piece, X, Y, WinningLine; +{ + /* Used to store the position of the winning move */ + int XHold, YHold; + /* Change in X and Y */ + int Dx, Dy; + + /* Display winning move */ + PrintMove(Piece, X, Y); + /* Preserve winning position */ + XHold = X; + YHold = Y; + switch (WinningLine) { + case Horiz: + { + Dx = 1; + Dy = 0; + break; + } + + case DownLeft: + { + Dx = 1; + Dy = 1; + break; + } + + case Vert: + { + Dx = 0; + Dy = 1; + break; + } + + case DownRight: + { + Dx = -1; + Dy = 1; + break; + } + } + + /* Go to topmost, leftmost */ + while (Board[X + Dx][Y + Dy] != Empty && Board[X + Dx][Y + Dy] == Piece) { + X = X + Dx; + Y = Y + Dy; + } + BlinkRow(X, Y, Dx, Dy, Piece); + /* Restore winning position */ + X = XHold; + Y = YHold; + /* Go back to winning square */ + GotoSquare(X, Y); +} + +/* Functions for playing a game -------------------------------- */ + +int Random(x) +int x; +{ + return((rand() / 19) % x); +} + +/* Adds one to the number of pieces in a line */ +void Add(Num) +int *Num; +{ + /* Adds one to the number. */ + *Num = *Num + 1; + /* If it is the first piece in the line, then the opponent cannot use + * it any more. */ + if (*Num == 1) TotalLines = TotalLines - 1; + /* The game is won if there are 5 in line. */ + if (*Num == 5) GameWon = TRUE; +} + +/* Updates the value of a square for each player, taking into + account that player has placed an extra piece in the square. + The value of a square in a usable line is Weight[Lin[Player]+1] + where Lin[Player] is the number of pieces already placed +in the line */ +void Update(Lin, Valu, Opponent) +int Lin[]; +int Valu[]; +int Opponent; +{ + /* If the opponent has no pieces in the line, then simply update the + * value for player */ + if (Lin[Opponent] == 0) + Valu[Player] += Weight[Lin[Player] + 1] - Weight[Lin[Player]]; + else + /* If it is the first piece in the line, then the line is + * spoiled for the opponent */ + if (Lin[Player] == 1) Valu[Opponent] -= Weight[Lin[Opponent] + 1]; +} + +/* Performs the move X,Y for player, and updates the global variables +(Board, Line, Value, Player, GameWon, TotalLines and the screen) */ +void MakeMove(X, Y) +int X, Y; +{ + int Opponent; + int X1, Y1; + int K, L, WinningLine; + + WinningLine = Null; + Opponent = OpponentColor(Player); + GameWon = FALSE; + + /* Each square of the board is part of 20 different lines. The adds + * one to the number of pieces in each of these lines. Then it + * updates the value for each of the 5 squares in each of the 20 + * lines. Finally Board is updated, and the move is printed on the + * screen. */ + + /* Horizontal lines, from left to right */ + for (K = 0; K <= 4; K++) { + X1 = X - K; /* Calculate starting point */ + Y1 = Y; + if ((1 <= X1) && (X1 <= SIZE - 4)) { /* Check starting point */ + Add(&Line[0][X1][Y1][Player]); /* Add one to line */ + if (GameWon && (WinningLine == Null)) /* Save winning line */ + WinningLine = Horiz; + for (L = 0; L <= 4; L++) /* Update value for the + * 5 squares in the line */ + Update(Line[0][X1][Y1], Value[X1 + L][Y1], Opponent); + } + } + + for (K = 0; K <= 4; K++) { /* Diagonal lines, from lower left to + * upper right */ + X1 = X - K; + Y1 = Y - K; + if ((1 <= X1) && (X1 <= SIZE - 4) && + (1 <= Y1) && (Y1 <= SIZE - 4)) { + Add(&Line[1][X1][Y1][Player]); + if (GameWon && (WinningLine == Null)) /* Save winning line */ + WinningLine = DownLeft; + for (L = 0; L <= 4; L++) + Update(Line[1][X1][Y1], Value[X1 + L][Y1 + L], Opponent); + } + } /* for */ + + for (K = 0; K <= 4; K++) { /* Diagonal lines, down right to upper left */ + X1 = X + K; + Y1 = Y - K; + if ((5 <= X1) && (X1 <= SIZE) && + (1 <= Y1) && (Y1 <= SIZE - 4)) { + Add(&Line[3][X1][Y1][Player]); + if (GameWon && (WinningLine == Null)) /* Save winning line */ + WinningLine = DownRight; + for (L = 0; L <= 4; L++) + Update(Line[3][X1][Y1], Value[X1 - L][Y1 + L], Opponent); + } + } /* for */ + + for (K = 0; K <= 4; K++) { /* Vertical lines, from down to up */ + X1 = X; + Y1 = Y - K; + if ((1 <= Y1) && (Y1 <= SIZE - 4)) { + Add(&Line[2][X1][Y1][Player]); + if (GameWon && (WinningLine == Null)) /* Save winning line */ + WinningLine = Vert; + for (L = 0; L <= 4; L++) + Update(Line[2][X1][Y1], Value[X1][Y1 + L], Opponent); + } + } + + Board[X][Y] = Player; /* Place piece in board */ + if (GameWon) + BlinkWinner(Player, X, Y, WinningLine); + else + PrintMove(Player, X, Y);/* Print move on screen */ + Player = Opponent; /* The opponent is next to move */ +} + +int GameOver() +/* A game is over if one of the players have +won, or if there are no more Empty lines */ +{ + return(GameWon || (TotalLines <= 0)); +} + +/* Finds a move X,Y for player, simply by picking the one with the +highest value */ +void FindMove(X, Y) +int *X, *Y; +{ + int Opponent; + int I, J; + int Max, Valu; + + Opponent = OpponentColor(Player); + Max = -10000; + /* If no square has a high value then pick the one in the middle */ + *X = (SIZE + 1) / 2; + *Y = (SIZE + 1) / 2; + if (Board[*X][*Y] == Empty) Max = 4; + /* The evaluation for a square is simply the value of the square for + * the player (attack points) plus the value for the opponent + * (defense points). Attack is more important than defense, since it + * is better to get 5 in line yourself than to prevent the op- ponent + * from getting it. */ + + /* For all Empty squares */ + for (I = 1; I <= SIZE; I++) for (J = 1; J <= SIZE; J++) + if (Board[I][J] == Empty) { + /* Calculate evaluation */ + Valu = Value[I][J][Player] * (16 + AttackFactor) / 16 + Value[I][J][Opponent] + Random(4); + /* Pick move with highest value */ + if (Valu > Max) { + *X = I; + *Y = J; + Max = Valu; + } + } +} + +char GetChar() +/* Get a character from the keyboard */ +{ + int c; + + c = getch(); + if (c < 0) abort(); + if (c == '\033') { /* arrow key */ + if ((c = getch()) == '[') { + c = getch(); + switch (c) { + case 'A': c = 'U'; break; + case 'B': c = 'D'; break; + case 'C': c = 'R'; break; + case 'D': c = 'L'; break; + default: + c = '?'; + break; + } + } + else + c = '?'; + } + if (islower(c)) + return toupper(c); + else + return c; +} + +/* Reads in a valid command character */ +void ReadCommand(X, Y, Command) +int X, Y; +char *Command; +{ + int ValidCommand; + + do { + ValidCommand = TRUE; + GotoSquare(X, Y); /* Goto square */ + refresh(); + *Command = GetChar(); /* Read from keyboard */ + switch (*Command) { + case '\n': /* '\n', '\r' or space means place a */ + case '\r': + case ' ': + *Command = 'E'; + break; /* stone at the cursor position */ + + case 'L': + case 'R': + case 'U': + case 'D': + case '7': + case '9': + case '1': + case '3': + case 'N': + case 'Q': + case 'A': + case 'P': + case 'H': + break; + + case '8': *Command = 'U'; break; + case '2': *Command = 'D'; break; + case '4': *Command = 'L'; break; + case '6': *Command = 'R'; break; + default: + { + if (GameOver()) + *Command = 'P'; + else + ValidCommand = FALSE; + break; + } + } + } while (!ValidCommand); +} + +void InterpretCommand(Command) +char Command; +{ + int Temp; + + switch (Command) { + case 'N':{ /* Start new game */ + ResetGame(FALSE); /* ResetGame but only redraw + * the board */ + X = (SIZE + 1) / 2; + Y = X; + break; + } + case 'H': + FindMove(&X, &Y); + break; /* Give the user a hint */ + case 'L': + X = (X + SIZE - 2) % SIZE + 1; + break; /* Left */ + case 'R': + X = X % SIZE + 1; + break; /* Right */ + case 'D': + Y = (Y + SIZE - 2) % SIZE + 1; + break; /* Down */ + case 'U': + Y = Y % SIZE + 1; + break; /* Up */ + case '7':{ + if ((X == 1) || (Y == SIZE)) { /* Move diagonally *//* t + * owards upper left */ + Temp = X; + X = Y; + Y = Temp; + } else { + X = X - 1; + Y = Y + 1; + } + break; + } + case '9':{ /* Move diagonally */ + if (X == SIZE) {/* toward upper right */ + X = (SIZE - Y) + 1; + Y = 1; + } else if (Y == SIZE) { + Y = (SIZE - X) + 1; + X = 1; + } else { + X = X + 1; + Y = Y + 1; + } + break; + } + case '1':{ /* Move diagonally */ + if (Y == 1) { /* toward lower left */ + Y = (SIZE - X) + 1; + X = SIZE; + } else if (X == 1) { + X = (SIZE - Y) + 1; + Y = SIZE; + } else { + X = X - 1; + Y = Y - 1; + } + break; + } + case '3':{ /* Move diagonally */ + if ((X == SIZE) || (Y == 1)) { /* toward lower right */ + Temp = X; + X = Y; + Y = Temp; + } else { + X = X + 1; + Y = Y - 1; + } + break; + } + case 'A': + AutoPlay = TRUE; + break; /* Auto play mode */ + } /* case */ +} /* InterpretCommand */ + +void PlayerMove() +/* Enter and make a move */ +{ + if (Board[X][Y] == Empty) { + MakeMove(X, Y); + if (GameWon) PrintMsg("Congratulations, You won!"); + Command = 'P'; + } + refresh(); +} /* PlayerMove */ + +void ProgramMove() +/* Find and perform programs move */ +{ + do { + if (GameOver()) { + AutoPlay = FALSE; + if ((Command != 'Q') && (!GameWon)) PrintMsg("Tie game!"); + } else { + FindMove(&X, &Y); + MakeMove(X, Y); + if (GameWon) PrintMsg("I won!"); + } + refresh(); + } while (AutoPlay); +} + +int main() +{ + Initialize(); + ResetGame(TRUE); /* ResetGame and draw the entire screen */ + refresh(); + X = (SIZE + 1) / 2; /* Set starting position to */ + Y = X; /* the middle of the board */ + do { + ReadCommand(X, Y, &Command); + if (GameOver()) + if (Command != 'Q') Command = 'N'; + InterpretCommand(Command); + if (Command == 'E') PlayerMove(); + if (Command == 'P' || Command == 'A') ProgramMove(); + } while (Command != 'Q'); + Abort("Good bye!"); + return(0); +} diff --git a/commands/simple/grep.c b/commands/simple/grep.c new file mode 100755 index 000000000..75bb8dcf1 --- /dev/null +++ b/commands/simple/grep.c @@ -0,0 +1,379 @@ +/* grep - search a file for a pattern Author: Norbert Schlenker */ + +/* Norbert Schlenker (nfs@princeton.edu) 1990-02-08 + * Released into the public domain. + * + * Grep searches files for lines containing a pattern, as specified by + * a regular expression, and prints those lines. It is invoked by: + * grep [flags] [pattern] [file ...] + * + * Flags: + * -e pattern useful when pattern begins with '-' + * -c print a count of lines matched + * -i ignore case + * -l prints just file names, no lines (quietly overrides -n) + * -n printed lines are preceded by relative line numbers + * -s prints errors only (quietly overrides -l and -n) + * -v prints lines which don't contain the pattern + * + * Semantic note: + * If both -l and -v are specified, grep prints the names of those + * files which do not contain the pattern *anywhere*. + * + * Exit: + * Grep sets an exit status which can be tested by the caller. + * Note that these settings are not necessarily compatible with + * any other version of grep, especially when -v is specified. + * Possible status values are: + * 0 if any matches are found + * 1 if no matches are found + * 2 if syntax errors are detected or any file cannot be opened + */ + + +/* External interfaces */ +#include <sys/types.h> +#include <regexp.h> /* Thanks to Henry Spencer */ +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> + +/* Internal constants */ +#define MATCH 0 /* exit code: some match somewhere */ +#define NO_MATCH 1 /* exit code: no match on any line */ +#define FAILURE 2 /* exit code: syntax error or bad file name */ + +/* Macros */ +#define SET_FLAG(c) (flags[(c)-'a'] = 1) +#define FLAG(c) (flags[(c)-'a'] != 0) + +#define uppercase(c) (((unsigned) ((c) - 'A')) <= ('Z' - 'A')) +#define downcase(c) ((c) - 'A' + 'a') + +/* Private storage */ +static char *program; /* program name */ +static char flags[26]; /* invocation flags */ +static regexp *expression; /* compiled search pattern */ +static char *rerr; /* error message */ + +/* External variables. */ +extern int optind; +extern char *optarg; + +/* Internal interfaces */ +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(static int match, (FILE *input, char *label, char *filename)); +_PROTOTYPE(static char *get_line, (FILE *input)); +_PROTOTYPE(static char *map_nocase, (char *line)); +_PROTOTYPE(void regerror , (char *s ) ); +_PROTOTYPE(static void tov8, (char *v8pattern, char *pattern)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int opt; /* option letter from getopt() */ + int egrep=0; /* using extended regexp operators */ + char *pattern; /* search pattern */ + char *v8pattern; /* v8 regexp search pattern */ + int exit_status = NO_MATCH; /* exit status for our caller */ + int file_status; /* status of search in one file */ + FILE *input; /* input file (if not stdin) */ + + program = argv[0]; + if (strlen(program)>=5 && strcmp(program+strlen(program)-5,"egrep")==0) egrep=1; + memset(flags, 0, sizeof(flags)); + pattern = NULL; + +/* Process any command line flags. */ + while ((opt = getopt(argc, argv, "e:cilnsv")) != EOF) { + if (opt == '?') + exit_status = FAILURE; + else + if (opt == 'e') + pattern = optarg; + else + SET_FLAG(opt); + } + +/* Detect a few problems. */ + if ((exit_status == FAILURE) || (optind == argc && pattern == NULL)) { + fprintf(stderr,"Usage: %s [-cilnsv] [-e] expression [file ...]\n",program); + exit(FAILURE); + } + +/* Ensure we have a usable pattern. */ + if (pattern == NULL) + pattern = argv[optind++]; + +/* Map pattern to lowercase if -i given. */ + if (FLAG('i')) { + char *p; + for (p = pattern; *p != '\0'; p++) { + if (uppercase(*p)) + *p = downcase(*p); + } + } + + if (!egrep) { + if ((v8pattern=malloc(2*strlen(pattern)))==(char*)0) { + fprintf(stderr,"%s: out of memory\n"); + exit(FAILURE); + } + tov8(v8pattern,pattern); + } else v8pattern=pattern; + + rerr=(char*)0; + if ((expression = regcomp(v8pattern)) == NULL) { + fprintf(stderr,"%s: bad regular expression",program); + if (rerr) fprintf(stderr," (%s)",rerr); + fprintf(stderr,"\n"); + exit(FAILURE); + } + +/* Process the files appropriately. */ + if (optind == argc) { /* no file names - find pattern in stdin */ + exit_status = match(stdin, (char *) NULL, "<stdin>"); + } + else + if (optind + 1 == argc) { /* one file name - find pattern in it */ + if (strcmp(argv[optind], "-") == 0) { + exit_status = match(stdin, (char *) NULL, "-"); + } else { + if ((input = fopen(argv[optind], "r")) == NULL) { + fprintf(stderr, "%s: couldn't open %s\n", + program, argv[optind]); + exit_status = FAILURE; + } + else { + exit_status = match(input, (char *) NULL, argv[optind]); + } + } + } + else + while (optind < argc) { /* lots of file names - find pattern in all */ + if (strcmp(argv[optind], "-") == 0) { + file_status = match(stdin, "-", "-"); + } else { + if ((input = fopen(argv[optind], "r")) == NULL) { + fprintf(stderr, "%s: couldn't open %s\n", + program, argv[optind]); + exit_status = FAILURE; + } else { + file_status = match(input, argv[optind], argv[optind]); + fclose(input); + } + } + if (exit_status != FAILURE) + exit_status &= file_status; + ++optind; + } + return(exit_status); +} + + +/* match - matches the lines of a file with the regular expression. + * To improve performance when either -s or -l is specified, this + * function handles those cases specially. + */ + +static int match(input, label, filename) +FILE *input; +char *label; +char *filename; +{ + char *line, *testline; /* pointers to input line */ + long int lineno = 0; /* line number */ + long int matchcount = 0; /* lines matched */ + int status = NO_MATCH; /* summary of what was found in this file */ + + if (FLAG('s') || FLAG('l')) { + while ((line = get_line(input)) != NULL) { + testline = FLAG('i') ? map_nocase(line) : line; + if (regexec(expression, testline, 1)) { + status = MATCH; + break; + } + } + if (FLAG('l')) + if ((!FLAG('v') && status == MATCH) || + ( FLAG('v') && status == NO_MATCH)) + puts(filename); + return status; + } + + while ((line = get_line(input)) != NULL) { + ++lineno; + testline = FLAG('i') ? map_nocase(line) : line; + if (regexec(expression, testline, 1)) { + status = MATCH; + if (!FLAG('v')) { + if (label != NULL) + printf("%s:", label); + if (FLAG('n')) + printf("%ld:", lineno); + if (!FLAG('c')) puts(line); + matchcount++; + } + } else { + if (FLAG('v')) { + if (label != NULL) + printf("%s:", label); + if (FLAG('n')) + printf("%ld:", lineno); + if (!FLAG('c')) puts(line); + matchcount++; + } + } + } + if (FLAG('c')) printf("%ld\n", matchcount); + return status; +} + + +/* get_line - fetch a line from the input file + * This function reads a line from the input file into a dynamically + * allocated buffer. If the line is too long for the current buffer, + * attempts will be made to increase its size to accomodate the line. + * The trailing newline is stripped before returning to the caller. + */ + +#define FIRST_BUFFER (size_t)256 /* first buffer size */ + +static char *buf = NULL; /* input buffer */ +static size_t buf_size = 0; /* input buffer size */ + +static char *get_line(input) +FILE *input; +{ + int n; + register char *bp; + register int c; + char *new_buf; + size_t new_size; + + if (buf_size == 0) { + if ((buf = (char *) malloc(FIRST_BUFFER)) == NULL) { + fprintf(stderr,"%s: not enough memory\n",program); + exit(FAILURE); + } + buf_size = FIRST_BUFFER; + } + + bp = buf; + n = buf_size; + while (1) { + while (--n > 0 && (c = getc(input)) != EOF) { + if (c == '\n') { + *bp = '\0'; + return buf; + } + *bp++ = c; + } + if (c == EOF) + return (ferror(input) || bp == buf) ? NULL : buf; + new_size = buf_size << 1; + if ((new_buf = (char *) realloc(buf, new_size)) == NULL) { + fprintf(stderr, "%s: line too long - truncated\n", program); + while ((c = getc(input)) != EOF && c != '\n') ; + *bp = '\0'; + return buf; + } else { + bp = new_buf + (buf_size - 1); + n = buf_size + 1; + buf = new_buf; + buf_size = new_size; + } + } +} + + +/* map_nocase - map a line down to lowercase letters only. + * bad points: assumes line gotten from get_line. + * there is more than A-Z you say? + */ + +static char *map_nocase(line) +char *line; +{ + static char *mapped=(char*)0; + static size_t map_size = 0; + char *mp; + + if (map_size < buf_size) { + if ((mapped=realloc(mapped,map_size=buf_size)) == NULL) { + fprintf(stderr,"%s: not enough memory\n",program); + exit(FAILURE); + } + } + + mp = mapped; + do { + *mp++ = uppercase(*line) ? downcase(*line) : *line; + } while (*line++ != '\0'); + + return mapped; +} + +/* In basic regular expressions, the characters ?, +, |, (, and ) + are taken literally; use the backslashed versions for RE operators. + In v8 regular expressions, things are the other way round, so + we have to swap those characters and their backslashed versions. +*/ +static void tov8(char *v8, char *basic) +{ + while (*basic) switch (*basic) + { + case '?': + case '+': + case '|': + case '(': + case ')': + { + *v8++='\\'; + *v8++=*basic++; + break; + } + case '\\': + { + switch (*(basic+1)) + { + case '?': + case '+': + case '|': + case '(': + case ')': + { + *v8++=*++basic; + ++basic; + break; + } + case '\0': + { + *v8++=*basic++; + break; + } + default: + { + *v8++=*basic++; + *v8++=*basic++; + } + } + break; + } + default: + { + *v8++=*basic++; + } + } + *v8++='\0'; +} + +/* Regular expression code calls this routine to print errors. */ + +void regerror(s) +char *s; +{ + rerr=s; +} diff --git a/commands/simple/head.c b/commands/simple/head.c new file mode 100755 index 000000000..3eb73776a --- /dev/null +++ b/commands/simple/head.c @@ -0,0 +1,78 @@ +/* head - print the first few lines of a file Author: Andy Tanenbaum */ + +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#define DEFAULT 10 + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void do_file, (int n, FILE *f)); +_PROTOTYPE(void usage, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + FILE *f; + int n, k, nfiles; + char *ptr; + + /* Check for flag. Only flag is -n, to say how many lines to print. */ + k = 1; + ptr = argv[1]; + n = DEFAULT; + if (argc > 1 && *ptr++ == '-') { + k++; + n = atoi(ptr); + if (n <= 0) usage(); + } + nfiles = argc - k; + + if (nfiles == 0) { + /* Print standard input only. */ + do_file(n, stdin); + exit(0); + } + + /* One or more files have been listed explicitly. */ + while (k < argc) { + if (nfiles > 1) printf("==> %s <==\n", argv[k]); + if ((f = fopen(argv[k], "r")) == NULL) + fprintf(stderr, "%s: cannot open %s: %s\n", + argv[0], argv[k], strerror(errno)); + else { + do_file(n, f); + fclose(f); + } + k++; + if (k < argc) printf("\n"); + } + return(0); +} + + + +void do_file(n, f) +int n; +FILE *f; +{ + int c; + + /* Print the first 'n' lines of a file. */ + while (n) switch (c = getc(f)) { + case EOF: + return; + case '\n': + --n; + default: putc((char) c, stdout); + } +} + + +void usage() +{ + fprintf(stderr, "Usage: head [-n] [file ...]\n"); + exit(1); +} diff --git a/commands/simple/host.c b/commands/simple/host.c new file mode 100755 index 000000000..99e0a345b --- /dev/null +++ b/commands/simple/host.c @@ -0,0 +1,1413 @@ +/* + * Copyright (c) 1986 Regents of the University of California + * All Rights Reserved + * + * Redistribution and use in source and binary forms are permitted + * provided that this notice is preserved and that due credit is given + * to the University of California at Berkeley. The name of the University + * may not be used to endorse or promote products derived from this + * software without specific prior written permission. This software + * is provided ``as is'' without express or implied warranty. + */ + +/* + * Actually, this program is from Rutgers University, however it is + * based on nslookup and other pieces of named tools, so it needs + * that copyright notice. + */ + +#if _MINIX +#include <sys/types.h> +#include <sys/ioctl.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <net/hton.h> +#include <net/netlib.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/netdb.h> +#include <net/gen/nameser.h> +#include <net/gen/resolv.h> +#include <net/gen/socket.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> + +typedef u8_t u_char; +typedef u16_t u_short; +typedef u32_t u_long; + +#undef ERROR +#else +#include <stdio.h> +#include <sys/types.h> +#include <arpa/nameser.h> +#include <netdb.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <resolv.h> +#include <sys/param.h> +#include <strings.h> +#include <ctype.h> +#endif + +extern int h_errno; + +#define NUMMX 50 + +#define SUCCESS 0 +#define TIME_OUT -1 +#define NO_INFO -2 +#define ERROR -3 +#define NONAUTH -4 + +#define NAME_LEN 256 + +#ifndef T_TXT +#define T_TXT 16 +#endif +#ifndef NO_DATA +#define NO_DATA NO_ADDRESS +#endif +#ifndef C_HS +#define C_HS 4 +#endif + +FILE *filePtr; + +struct state orig; +extern struct state _res; +static u8_t *cname = NULL; +int getclass = C_IN; +int gettype, getdeftype = T_A; +int verbose = 0; +int list = 0; +int server_specified = 0; + +union querybuf; + +int main _ARGS(( int c, char *v[] )); + +static int parsetype _ARGS(( char *s )); +static int parseclass _ARGS(( char *s )); +static void hperror _ARGS(( int errno )); +static void printanswer _ARGS(( struct hostent *hp )); +static int ListHosts _ARGS(( char *namePtr, int queryType )); +static int gethostinfo _ARGS(( char *name )); +static int getdomaininfo _ARGS(( char *name, char *domain )); +static int getinfo _ARGS(( char *name, char *domain, int type )); +static int printinfo _ARGS(( union querybuf *answer, u8_t *eom, int filter, + int isls )); +static char *DecodeError _ARGS(( int result )); +static u8_t *pr_rr _ARGS(( u8_t *cp, u8_t *msg, FILE *file, int filter )); +static u8_t * pr_cdname _ARGS(( u8_t *cp, u8_t *msg, u8_t *name, int namelen )); +static char *pr_class _ARGS(( int class )); +static char *pr_type _ARGS(( int type )); +static int tcpip_writeall _ARGS(( int fd, char *buf, unsigned siz )); + +int +main(c, v) + char **v; +{ + char *domain; + ipaddr_t addr; + register struct hostent *hp; + register char *s, *p; + register inverse = 0; + register waitmode = 0; + u8_t *oldcname; + int ncnames; + int isaddr; + + res_init(); + _res.retrans = 5; + + if (c < 2) { + fprintf(stderr, "Usage: host [-w] [-v] [-r] [-d] [-V] [-t querytype] [-c class] [-a] host [server]\n -w to wait forever until reply\n -v for verbose output\n -r to disable recursive processing\n -d to turn on debugging output\n -t querytype to look for a specific type of information\n -c class to look for non-Internet data\n -a is equivalent to '-v -t *'\n -V to always use a virtual circuit\n"); + exit(1); + } + while (c > 2 && v[1][0] == '-') { + if (strcmp (v[1], "-w") == 0) { + _res.retry = 1; + _res.retrans = 15; + waitmode = 1; + v++; + c--; + } + else if (strcmp (v[1], "-r") == 0) { + _res.options &= ~RES_RECURSE; + v++; + c--; + } + else if (strcmp (v[1], "-d") == 0) { + _res.options |= RES_DEBUG; + v++; + c--; + } + else if (strcmp (v[1], "-v") == 0) { + verbose = 1; + v++; + c--; + } + else if (strcmp (v[1], "-l") == 0) { + list = 1; + v++; + c--; + } + else if (strncmp (v[1], "-t", 2) == 0) { + v++; + c--; + gettype = parsetype(v[1]); + v++; + c--; + } + else if (strncmp (v[1], "-c", 2) == 0) { + v++; + c--; + getclass = parseclass(v[1]); + v++; + c--; + } + else if (strcmp (v[1], "-a") == 0) { + verbose = 1; + gettype = T_ANY; + v++; + c--; + } + else if (strcmp (v[1], "-V") == 0) { + _res.options |= RES_USEVC; + v++; + c--; + } + } + if (c > 2) { + s = v[2]; + server_specified++; + + if ((p = strchr(s, ':')) != NULL) *p++ = 0; + isaddr = inet_aton(s, &addr); + if (p != NULL) p[-1] = ':'; + if (!isaddr) { + hp = gethostbyname(s); + if (hp == NULL) { + fprintf(stderr,"Error in looking up server name:\n"); + hperror(h_errno); + exit(1); + } + _res.nsaddr= *(ipaddr_t *)hp->h_addr; + printf("Using domain server:\n"); + printanswer(hp); + } + else { + _res.nsaddr_list[0]= addr; + _res.nsport_list[0]= htons(NAMESERVER_PORT); + printf("Using domain server %s", + inet_ntoa(_res.nsaddr)); + if (p != NULL) { + printf(" on port %d", atoi(p)); + _res.nsport_list[0]= htons(atoi(p)); + } + printf(":\n"); + } + } + domain = v[1]; + if (strcmp (domain, ".") != 0 && inet_aton(domain, &addr)) { + static char ipname[sizeof("255.255.255.255.in-addr.arpa.")]; + sprintf(ipname, "%d.%d.%d.%d.in-addr.arpa.", + ((unsigned char *) &addr)[3], + ((unsigned char *) &addr)[2], + ((unsigned char *) &addr)[1], + ((unsigned char *) &addr)[0]); + domain = ipname; + getdeftype = T_PTR; + } + + hp = NULL; + h_errno = TRY_AGAIN; +/* + * we handle default domains ourselves, thank you + */ + _res.options &= ~RES_DEFNAMES; + + if (list) + exit(ListHosts(domain, gettype ? gettype : getdeftype)); + oldcname = NULL; + ncnames = 5; + while (hp == NULL && h_errno == TRY_AGAIN) { + cname = NULL; + if (oldcname == NULL) + hp = (struct hostent *)gethostinfo(domain); + else + hp = (struct hostent *)gethostinfo((char *)oldcname); + if (cname) { + if (ncnames-- == 0) { + printf("Too many cnames. Possible loop.\n"); + exit(1); + } + oldcname = cname; + hp = NULL; + h_errno = TRY_AGAIN; + continue; + } + if (!waitmode) + break; + } + + if (hp == NULL) { + hperror(h_errno); + exit(1); + } + + exit(0); + +} + +static int +parsetype(s) + char *s; +{ +if (strcmp(s,"a") == 0) + return(1); +if (strcmp(s,"ns") == 0) + return(2); +if (strcmp(s,"md") == 0) + return(3); +if (strcmp(s,"mf") == 0) + return(4); +if (strcmp(s,"cname") == 0) + return(5); +if (strcmp(s,"soa") == 0) + return(6); +if (strcmp(s,"mb") == 0) + return(7); +if (strcmp(s,"mg") == 0) + return(8); +if (strcmp(s,"mr") == 0) + return(9); +if (strcmp(s,"null") == 0) + return(10); +if (strcmp(s,"wks") == 0) + return(11); +if (strcmp(s,"ptr") == 0) + return(12); +if (strcmp(s,"hinfo") == 0) + return(13); +if (strcmp(s,"minfo") == 0) + return(14); +if (strcmp(s,"mx") == 0) + return(15); +if (strcmp(s,"txt") == 0) /* Roy */ + return(T_TXT); /* Roy */ +if (strcmp(s,"uinfo") == 0) + return(100); +if (strcmp(s,"uid") == 0) + return(101); +if (strcmp(s,"gid") == 0) + return(102); +if (strcmp(s,"unspec") == 0) + return(103); +if (strcmp(s,"any") == 0) + return(255); +if (strcmp(s,"*") == 0) + return(255); +if (atoi(s)) + return(atoi(s)); +fprintf(stderr, "Invalid query type: %s\n", s); +exit(2); +} + +static int +parseclass(s) + char *s; +{ +if (strcmp(s,"in") == 0) + return(C_IN); +if (strcmp(s,"chaos") == 0) + return(C_CHAOS); +if (strcmp(s,"hs") == 0) + return(C_HS); +if (strcmp(s,"any") == 0) + return(C_ANY); +if (atoi(s)) + return(atoi(s)); +fprintf(stderr, "Invalid query class: %s\n", s); +exit(2); +} + +static void +printanswer(hp) + register struct hostent *hp; +{ + register char **cp; + register ipaddr_t **hptr; + + printf("Name: %s\n", hp->h_name); + printf("Address:"); + for (hptr = (ipaddr_t **)hp->h_addr_list; *hptr; hptr++) + printf(" %s", inet_ntoa(*(ipaddr_t *)*hptr)); + printf("\nAliases:"); + for (cp = hp->h_aliases; cp && *cp && **cp; cp++) + printf(" %s", *cp); + printf("\n\n"); +} + +static void +hperror(errno) +int errno; +{ +switch(errno) { + case HOST_NOT_FOUND: + fprintf(stderr,"Host not found.\n"); + break; + case TRY_AGAIN: + fprintf(stderr,"Host not found, try again.\n"); + break; + case NO_RECOVERY: + fprintf(stderr,"No recovery, Host not found.\n"); + break; + case NO_ADDRESS: + fprintf(stderr,"There is an entry for this host, but it doesn't have what you requested.\n"); + break; + } +} + + +typedef union querybuf { + dns_hdr_t qb1; + u8_t qb2[PACKETSZ]; +} querybuf_t; + +static u8_t hostbuf[BUFSIZ+1]; + + +static int +gethostinfo(name) + char *name; +{ + register char *cp, **domain; + int n; + int hp; + int nDomain; + + if (strcmp(name, ".") == 0) + return(getdomaininfo(name, NULL)); + for (cp = name, n = 0; *cp; cp++) + if (*cp == '.') + n++; + if (n && cp[-1] == '.') { + if (cp[-1] == '.') + cp[-1] = 0; + hp = getdomaininfo(name, (char *)NULL); + if (cp[-1] == 0) + cp[-1] = '.'; + return (hp); + } + if (n == 0 && (cp = __hostalias(name))) { + if (verbose) + printf("Aliased to \"%s\"\n", cp); + _res.options |= RES_DEFNAMES; + return (getdomaininfo(cp, (char *)NULL)); + } +#ifdef MAXDS + for (nDomain = 0; + _res.defdname_list[nDomain][0] != 0; + nDomain++) { + for (domain = _res.dnsrch_list[nDomain]; *domain; domain++) { + if (verbose) + printf("Trying domain \"%s\"\n", *domain); + hp = getdomaininfo(name, *domain); + if (hp) + return (hp); + } + } +#else + for (domain = _res.dnsrch; *domain; domain++) { + if (verbose) + printf("Trying domain \"%s\"\n", *domain); + hp = getdomaininfo(name, *domain); + if (hp) + return (hp); + } +#endif + if (h_errno != HOST_NOT_FOUND || + (_res.options & RES_DNSRCH) == 0) + return (0); + if (verbose) + printf("Trying null domain\n"); + return (getdomaininfo(name, (char *)NULL)); +} + +static int +getdomaininfo(name, domain) + char *name, *domain; +{ + return getinfo(name, domain, gettype ? gettype : getdeftype); +} + +static int +getinfo(name, domain, type) + char *name, *domain; +{ + + dns_hdr_t *hp; + u8_t *eom, *bp, *cp; + querybuf_t buf, answer; + int n, n1, i, j, nmx, ancount, nscount, arcount, qdcount, buflen; + u_short pref, class; + char host[2*MAXDNAME+2]; + + if (domain == NULL) + (void)sprintf(host, "%.*s", MAXDNAME, name); + else + (void)sprintf(host, "%.*s.%.*s", MAXDNAME, name, MAXDNAME, domain); + + n = res_mkquery(QUERY, host, getclass, type, (char *)NULL, 0, NULL, + (char *)&buf, sizeof(buf)); + if (n < 0) { + if (_res.options & RES_DEBUG) + printf("res_mkquery failed\n"); + h_errno = NO_RECOVERY; + return(0); + } + n = res_send((char *)&buf, n, (char *)&answer, sizeof(answer)); + if (n < 0) { + if (_res.options & RES_DEBUG) + printf("res_send failed\n"); + h_errno = TRY_AGAIN; + return (0); + } + eom = (u8_t *)&answer + n; + return(printinfo(&answer, eom, T_ANY, 0)); +} + +static int +printinfo(answer, eom, filter, isls) + querybuf_t *answer; + u8_t *eom; + int filter; + int isls; +{ + dns_hdr_t *hp; + u8_t *bp, *cp; + int n, n1, i, j, nmx, ancount, nscount, arcount, qdcount, buflen; + u_short pref, class; + + /* + * find first satisfactory answer + */ + hp = (dns_hdr_t *) answer; + ancount = ntohs(hp->dh_ancount); + qdcount = ntohs(hp->dh_qdcount); + nscount = ntohs(hp->dh_nscount); + arcount = ntohs(hp->dh_arcount); + if (_res.options & RES_DEBUG || (verbose && isls == 0)) + printf("rcode = %d (%s), ancount=%d\n", + hp->dh_flag2 & DHF_RCODE, + DecodeError(hp->dh_flag2 & DHF_RCODE), ancount); + if (hp->dh_flag2 & DHF_RCODE != NOERROR || + (ancount+nscount+arcount) == 0) { + switch (hp->dh_flag2 & DHF_RCODE) { + case NXDOMAIN: + /* Check if it's an authoritive answer */ + if (hp->dh_flag1 & DHF_AA) { + h_errno = HOST_NOT_FOUND; + return(0); + } else { + h_errno = TRY_AGAIN; + return(0); + } + case SERVFAIL: + h_errno = TRY_AGAIN; + return(0); +#ifdef OLDJEEVES + /* + * Jeeves (TOPS-20 server) still does not + * support MX records. For the time being, + * we must accept FORMERRs as the same as + * NOERROR. + */ + case FORMERR: +#endif /* OLDJEEVES */ + case NOERROR: +/* TpB - set a return error for this case. NO_DATA */ + h_errno = NO_DATA; + return(0); /* was 1,but now indicates exception */ +#ifndef OLDJEEVES + case FORMERR: +#endif /* OLDJEEVES */ + case NOTIMP: + case REFUSED: + h_errno = NO_RECOVERY; + return(0); + } + return (0); + } + bp = hostbuf; + nmx = 0; + buflen = sizeof(hostbuf); + cp = (u8_t *)answer + sizeof(dns_hdr_t); + if (qdcount) { + cp += dn_skipname((u8_t *)cp,(u8_t *)eom) + QFIXEDSZ; + while (--qdcount > 0) + cp += dn_skipname((u8_t *)cp,(u8_t *)eom) + QFIXEDSZ; + } + if (ancount) { + if (!(hp->dh_flag1 & DHF_AA)) + if (verbose && isls == 0) + printf("The following answer is not authoritative:\n"); + while (--ancount >= 0 && cp && cp < eom) { + cp = pr_rr(cp, (u8_t *)answer, stdout, filter); +/* + * When we ask for address and there is a CNAME, it seems to return + * both the CNAME and the address. Since we trace down the CNAME + * chain ourselves, we don't really want to print the address at + * this point. + */ + if (cname && ! verbose) + return (1); + } + } + if (! verbose) + return (1); + if (nscount) { + printf("For authoritative answers, see:\n"); + while (--nscount >= 0 && cp && cp < eom) { + cp = pr_rr(cp, (u8_t *)answer, stdout, filter); + } + } + if (arcount) { + printf("Additional information:\n"); + while (--arcount >= 0 && cp && cp < eom) { + cp = pr_rr(cp, (u8_t *)answer, stdout, filter); + } + } + return(1); + } + +static u8_t cnamebuf[MAXDNAME]; + +/* + * Print resource record fields in human readable form. + */ +static u8_t * +pr_rr(cp, msg, file, filter) + u8_t *cp, *msg; + FILE *file; + int filter; +{ + int type, class, dlen, n, c, proto, ttl; + ipaddr_t inaddr; + u8_t *cp1; + struct protoent *protop; + struct servent *servp; + char punc; + int doprint; + u8_t name[MAXDNAME]; + + if ((cp = pr_cdname(cp, msg, name, sizeof(name))) == NULL) + return (NULL); /* compression error */ + + type = _getshort(cp); + cp += sizeof(u_short); + + class = _getshort(cp); + cp += sizeof(u_short); + + ttl = _getlong(cp); + cp += sizeof(u_long); + + if (filter == type || filter == T_ANY || + (filter == T_A && (type == T_PTR || type == T_NS))) + doprint = 1; + else + doprint = 0; + + if (doprint) + if (verbose) + fprintf(file,"%s\t%d%s\t%s", + name, ttl, pr_class(class), pr_type(type)); + else + fprintf(file,"%s%s %s",name, pr_class(class), pr_type(type)); + if (verbose) + punc = '\t'; + else + punc = ' '; + + dlen = _getshort(cp); + cp += sizeof(u_short); + cp1 = cp; + /* + * Print type specific data, if appropriate + */ + switch (type) { + case T_A: + switch (class) { + case C_IN: + bcopy((char *)cp, (char *)&inaddr, sizeof(inaddr)); + if (dlen == 4) { + if (doprint) + fprintf(file,"%c%s", punc, + inet_ntoa(inaddr)); + cp += dlen; + } else if (dlen == 7) { + if (doprint) { + fprintf(file,"%c%s", punc, + inet_ntoa(inaddr)); + fprintf(file,", protocol = %d", cp[4]); + fprintf(file,", port = %d", + (cp[5] << 8) + cp[6]); + } + cp += dlen; + } + break; + } + break; + case T_CNAME: + if (dn_expand(msg, msg + 512, cp, cnamebuf, + sizeof(cnamebuf)-1) >= 0) { + strcat((char *) cnamebuf, "."); + if (gettype != T_CNAME && gettype != T_ANY) + cname = cnamebuf; + } + case T_MB: +#ifdef OLDRR + case T_MD: + case T_MF: +#endif /* OLDRR */ + case T_MG: + case T_MR: + case T_NS: + case T_PTR: + cp = pr_cdname(cp, msg, name, sizeof(name)); + if (doprint) + fprintf(file,"%c%s",punc, name); + break; + + case T_HINFO: + if (n = *cp++) { + if (doprint) + fprintf(file,"%c%.*s", punc, n, cp); + cp += n; + } + if (n = *cp++) { + if (doprint) + fprintf(file,"%c%.*s", punc, n, cp); + cp += n; + } + break; + + case T_SOA: + cp = pr_cdname(cp, msg, name, sizeof(name)); + if (doprint) + fprintf(file,"\t%s", name); + cp = pr_cdname(cp, msg, name, sizeof(name)); + if (doprint) + fprintf(file," %s", name); + if (doprint) + fprintf(file,"(\n\t\t\t%ld\t;serial (version)", _getlong(cp)); + cp += sizeof(u_long); + if (doprint) + fprintf(file,"\n\t\t\t%ld\t;refresh period", _getlong(cp)); + cp += sizeof(u_long); + if (doprint) + fprintf(file,"\n\t\t\t%ld\t;retry refresh this often", _getlong(cp)); + cp += sizeof(u_long); + if (doprint) + fprintf(file,"\n\t\t\t%ld\t;expiration period", _getlong(cp)); + cp += sizeof(u_long); + if (doprint) + fprintf(file,"\n\t\t\t%ld\t;minimum TTL\n\t\t\t)", _getlong(cp)); + cp += sizeof(u_long); + break; + + case T_MX: + if (doprint) + if (verbose) + fprintf(file,"\t%d ",_getshort(cp)); + else + fprintf(file," (pri=%d) by ",_getshort(cp)); + cp += sizeof(u_short); + cp = pr_cdname(cp, msg, name, sizeof(name)); + if (doprint) + fprintf(file, "%s", name); + break; + + case T_MINFO: + cp = pr_cdname(cp, msg, name, sizeof(name)); + if (doprint) + fprintf(file,"%c%s",punc, name); + cp = pr_cdname(cp, msg, name, sizeof(name)); + if (doprint) + fprintf(file," %s", name); + break; + + /* Roy start */ + case T_TXT: + if (n = *cp++) { + if (doprint) + fprintf(file,"%c%.*s", punc, n, cp); + cp += n; + } + break; + /* Roy end */ + + case T_UINFO: + if (doprint) + fprintf(file,"%c%s", punc, cp); + cp += dlen; + break; + + case T_UID: + case T_GID: + if (dlen == 4) { + if (doprint) + fprintf(file,"%c%ld", punc, _getlong(cp)); + cp += sizeof(int); + } + break; + + case T_WKS: + if (dlen < sizeof(u_long) + 1) + break; + bcopy((char *)cp, (char *)&inaddr, sizeof(inaddr)); + cp += sizeof(u_long); + proto = *cp++; + protop = getprotobynumber(proto); + if (doprint) + if (protop) + fprintf(file,"%c%s %s", punc, + inet_ntoa(inaddr), protop->p_name); + else + fprintf(file,"%c%s %d", punc, + inet_ntoa(inaddr), proto); + + n = 0; + while (cp < cp1 + dlen) { + c = *cp++; + do { + if (c & 0200) { + servp = NULL; + if (protop) + servp = getservbyport (htons(n), + protop->p_name); + if (doprint) + if (servp) + fprintf(file, " %s", servp->s_name); + else + fprintf(file, " %d", n); + } + c <<= 1; + } while (++n & 07); + } + break; + + default: + if (doprint) + fprintf(file,"%c???", punc); + cp += dlen; + } + if (cp != cp1 + dlen) + fprintf(file,"packet size error (%#x != %#x)\n", cp, cp1+dlen); + if (doprint) + fprintf(file,"\n"); + return (cp); +} + +static char nbuf[20]; + +/* + * Return a string for the type + */ +static char * +pr_type(type) + int type; +{ + switch (type) { + case T_A: + return(verbose? "A" : "has address"); + case T_NS: /* authoritative server */ + return("NS"); +#ifdef OLDRR + case T_MD: /* mail destination */ + return("MD"); + case T_MF: /* mail forwarder */ + return("MF"); +#endif /* OLDRR */ + case T_CNAME: /* connonical name */ + return(verbose? "CNAME" : "is a nickname for"); + case T_SOA: /* start of authority zone */ + return("SOA"); + case T_MB: /* mailbox domain name */ + return("MB"); + case T_MG: /* mail group member */ + return("MG"); + case T_MX: /* mail routing info */ + return(verbose? "MX" : "mail is handled"); + /* Roy start */ + case T_TXT: /* TXT - descriptive info */ + return(verbose? "TXT" : "descriptive text"); + /* Roy end */ + case T_MR: /* mail rename name */ + return("MR"); + case T_NULL: /* null resource record */ + return("NULL"); + case T_WKS: /* well known service */ + return("WKS"); + case T_PTR: /* domain name pointer */ + return("PTR"); + case T_HINFO: /* host information */ + return("HINFO"); + case T_MINFO: /* mailbox information */ + return("MINFO"); + case T_AXFR: /* zone transfer */ + return("AXFR"); + case T_MAILB: /* mail box */ + return("MAILB"); + case T_MAILA: /* mail address */ + return("MAILA"); + case T_ANY: /* matches any type */ + return("ANY"); + case T_UINFO: + return("UINFO"); + case T_UID: + return("UID"); + case T_GID: + return("GID"); + default: + return (sprintf(nbuf, "%d", type) == EOF ? NULL : nbuf); + } +} + +/* + * Return a mnemonic for class + */ +static char * +pr_class(class) + int class; +{ + + switch (class) { + case C_IN: /* internet class */ + return(verbose? " IN" : ""); + case C_CHAOS: /* chaos class */ + return(verbose? " CHAOS" : ""); + case C_HS: /* Hesiod class */ + return(verbose? " HS" : ""); + case C_ANY: /* matches any class */ + return(" ANY"); + default: + return (sprintf(nbuf," %d", class) == EOF ? NULL : nbuf); + } +} + +static u8_t * +pr_cdname(cp, msg, name, namelen) + u8_t *cp, *msg; + u8_t *name; + int namelen; +{ + int n; + + if ((n = dn_expand(msg, msg + 512, cp, name, namelen - 2)) < 0) + return (NULL); + if (name[0] == '\0') { + name[0] = '.'; + name[1] = '\0'; + } + return (cp + n); +} + +char *resultcodes[] = { + "NOERROR", + "FORMERR", + "SERVFAIL", + "NXDOMAIN", + "NOTIMP", + "REFUSED", + "6", + "7", + "8", + "9", + "10", + "11", + "12", + "13", + "14", + "NOCHANGE", +}; + + + +/* + ****************************************************************************** + * + * ListHosts -- + * + * Requests the name server to do a zone transfer so we + * find out what hosts it knows about. + * + * Results: + * SUCCESS the listing was successful. + * ERROR the server could not be contacted because + * a socket could not be obtained or an error + * occured while receiving, or the output file + * could not be opened. + * + ****************************************************************************** + */ + +static int +ListHosts(namePtr, queryType) + char *namePtr; + int queryType; /* e.g. T_A */ +{ + querybuf_t buf, answer; + dns_hdr_t *headerPtr; + + int msglen; + int amtToRead; + int numRead; + int i; + int numAnswers = 0; + int result; + int soacnt = 0; + u_short len; + int dlen; + int type; + int nscount; + u8_t *cp, *nmp; + u8_t name[NAME_LEN]; + char dname[2][NAME_LEN]; + u8_t domain[NAME_LEN]; +/* names and addresses of name servers to try */ +#define NUMNS 8 + char nsname[NUMNS][NAME_LEN]; + int nshaveaddr[NUMNS]; +#define IPADDRSIZE 4 +#define NUMNSADDR 16 + char nsipaddr[NUMNSADDR][IPADDRSIZE]; + int numns; + int numnsaddr; + int thisns; + struct hostent *hp; + enum { + NO_ERRORS, + ERR_READING_LEN, + ERR_READING_MSG, + ERR_PRINTING + } error = NO_ERRORS; + char *tcp_serv_name; + int tcp_fd; + nwio_tcpconf_t tcpconf; + nwio_tcpcl_t clopt; + int terrno; + +/* + * normalize to not have trailing dot. We do string compares below + * of info from name server, and it won't have trailing dots. + */ + i = strlen(namePtr); + if (namePtr[i-1] == '.') + namePtr[i-1] = 0; + + if (server_specified) { + bcopy((char *)&_res.nsaddr, nsipaddr[0], IPADDRSIZE); + numnsaddr = 1; + } + else { + +/* + * First we have to find out where to look. This needs a NS query, + * possibly followed by looking up addresses for some of the names. + */ + + msglen = res_mkquery(QUERY, namePtr, C_IN, T_NS, + (char *)0, 0, (struct rrec *)0, + (char *)&buf, sizeof(buf)); + + if (msglen < 0) { + printf("res_mkquery failed\n"); + return (ERROR); + } + + msglen = res_send((char *)&buf,msglen,(char *)&answer, sizeof(answer)); + + if (msglen < 0) { + printf("Unable to get to nameserver -- try again later\n"); + return (ERROR); + } + if (_res.options & RES_DEBUG || verbose) + printf("rcode = %d (%s), ancount=%d\n", + answer.qb1.dh_flag2 & DHF_RCODE, + DecodeError(answer.qb1.dh_flag2 & DHF_RCODE), + ntohs(answer.qb1.dh_ancount)); + +/* + * Analyze response to our NS lookup + */ + + nscount = ntohs(answer.qb1.dh_ancount) + ntohs(answer.qb1.dh_nscount) + + ntohs(answer.qb1.dh_arcount); + + if (answer.qb1.dh_flag2 & DHF_RCODE != NOERROR || nscount == 0) { + switch (answer.qb1.dh_flag2 & DHF_RCODE) { + case NXDOMAIN: + /* Check if it's an authoritive answer */ + if (answer.qb1.dh_flag1 & DHF_AA) { + printf("No such domain\n"); + } else { + printf("Unable to get information about domain -- try again later.\n"); + } + break; + case SERVFAIL: + printf("Unable to get information about that domain -- try again later.\n"); + break; + case NOERROR: + printf("That domain exists, but seems to be a leaf node.\n"); + break; + case FORMERR: + case NOTIMP: + case REFUSED: + printf("Unrecoverable error looking up domain name.\n"); + break; + } + return (0); + } + + cp = answer.qb2 + sizeof(dns_hdr_t); + if (ntohs(answer.qb1.dh_qdcount) > 0) + cp += dn_skipname(cp, answer.qb2 + msglen) + QFIXEDSZ; + + numns = 0; + numnsaddr = 0; + +/* + * Look at response from NS lookup for NS and A records. + */ + + for (;nscount; nscount--) { + cp += dn_expand(answer.qb2, answer.qb2 + msglen, cp, + domain, sizeof(domain)); + type = _getshort(cp); + cp += sizeof(u_short) + sizeof(u_short) + sizeof(u_long); + dlen = _getshort(cp); + cp += sizeof(u_short); + if (type == T_NS) { + if (dn_expand(answer.qb2, answer.qb2 + msglen, cp, + name, sizeof(name)) >= 0) { + if (numns < NUMNS && strcasecmp((char *)domain, namePtr) == 0) { + for (i = 0; i < numns; i++) + if (strcasecmp(nsname[i], (char *)name) == 0) + break; /* duplicate */ + if (i >= numns) { + strncpy(nsname[numns], (char *)name, sizeof(name)); + nshaveaddr[numns] = 0; + numns++; + } + } + } + } + else if (type == T_A) { + if (numnsaddr < NUMNSADDR) + for (i = 0; i < numns; i++) { + if (strcasecmp(nsname[i], (char *)domain) == 0) { + nshaveaddr[i]++; + bcopy((char *)cp, nsipaddr[numnsaddr],IPADDRSIZE); + numnsaddr++; + break; + } + } + } + cp += dlen; + } + +/* + * Usually we'll get addresses for all the servers in the additional + * info section. But in case we don't, look up their addresses. + */ + + for (i = 0; i < numns; i++) { + if (! nshaveaddr[i]) { + register long **hptr; + int numaddrs = 0; + + hp = gethostbyname(nsname[i]); + if (hp) { + for (hptr = (long **)hp->h_addr_list; *hptr; hptr++) + if (numnsaddr < NUMNSADDR) { + bcopy((char *)*hptr, nsipaddr[numnsaddr],IPADDRSIZE); + numnsaddr++; + numaddrs++; + } + } + if (_res.options & RES_DEBUG || verbose) + printf("Found %d addresses for %s by extra query\n", + numaddrs, nsname[i]); + } + else + if (_res.options & RES_DEBUG || verbose) + printf("Found %d addresses for %s\n", + nshaveaddr[i], nsname[i]); + } + } +/* + * Now nsipaddr has numnsaddr addresses for name servers that + * serve the requested domain. Now try to find one that will + * accept a zone transfer. + */ + + thisns = 0; + +again: + + numAnswers = 0; + soacnt = 0; + + /* + * Create a query packet for the requested domain name. + * + */ + msglen = res_mkquery(QUERY, namePtr, getclass, T_AXFR, + (char *)0, 0, (struct rrec *)0, + (char *) &buf, sizeof(buf)); + if (msglen < 0) { + if (_res.options & RES_DEBUG) { + fprintf(stderr, "ListHosts: Res_mkquery failed\n"); + } + return (ERROR); + } + + /* + * Set up a virtual circuit to the server. + */ + + tcp_serv_name= getenv("TCP_DEVICE"); + if (!tcp_serv_name) + tcp_serv_name= TCP_DEVICE; + for (;thisns < numnsaddr; thisns++) + { + tcp_fd= open(tcp_serv_name, O_RDWR); + if (tcp_fd == -1) + { + fprintf(stderr, "unable to open '%s': %s\n", tcp_serv_name, + strerror(errno)); + return ERROR; + } + + tcpconf.nwtc_flags= NWTC_EXCL | NWTC_LP_SEL | NWTC_SET_RA | + NWTC_SET_RP; + tcpconf.nwtc_remaddr= *(ipaddr_t *)nsipaddr[thisns]; + tcpconf.nwtc_remport= _res.nsport_list[0]; + result= ioctl(tcp_fd, NWIOSTCPCONF, &tcpconf); + if (result == -1) + { + fprintf(stderr, "tcp_ioc_setconf failed: %s\n", + strerror(errno)); + close(tcp_fd); + return ERROR; + } + if (_res.options & RES_DEBUG || verbose) + printf("Trying %s\n", inet_ntoa(tcpconf.nwtc_remaddr)); + clopt.nwtcl_flags= 0; + result= ioctl(tcp_fd, NWIOTCPCONN, &clopt); + if (result == 0) + break; + terrno= errno; + if (verbose) + fprintf(stderr, + "Connection failed, trying next server: %s\n", + strerror(errno)); + close(tcp_fd); + } + if (thisns >= numnsaddr) { + printf("No server for that domain responded\n"); + if (!verbose) + fprintf(stderr, "Error from the last server was: %s\n", + strerror(terrno)); + return(ERROR); + } + + /* + * Send length & message for zone transfer + */ + + len = htons(msglen); + + result= tcpip_writeall(tcp_fd, (char *)&len, sizeof(len)); + if (result != sizeof(len)) + { + fprintf(stderr, "write failed: %s\n", strerror(errno)); + close(tcp_fd); + return ERROR; + } + result= tcpip_writeall(tcp_fd, (char *)&buf, msglen); + if (result != msglen) + { + fprintf(stderr, "write failed: %s\n", + strerror(errno)); + close(tcp_fd); + return ERROR; + } + filePtr = stdout; + + while (1) { + + /* + * Read the length of the response. + */ + + cp = (u8_t *) &buf; + amtToRead = sizeof(u_short); + while(amtToRead > 0) + { + result = read(tcp_fd, (char *)cp, amtToRead); + if (result <= 0) + break; + cp += result; + amtToRead -= result; + } + if (amtToRead) { + error = ERR_READING_LEN; + break; + } + + if ((len = htons(*(u_short *)&buf)) == 0) { + break; /* nothing left to read */ + } + + /* + * Read the response. + */ + + amtToRead = len; + cp = (u8_t *) &buf; + while(amtToRead > 0) + { + result = read(tcp_fd, (char *)cp, amtToRead); + if (result<= 0) + break; + cp += result; + amtToRead -= result; + } + if (amtToRead) { + error = ERR_READING_MSG; + break; + } + + i = buf.qb1.dh_flag2 & DHF_RCODE; + if (i != NOERROR || ntohs(buf.qb1.dh_ancount) == 0) { + if ((thisns+1) < numnsaddr && + (i == SERVFAIL || i == NOTIMP || i == REFUSED)) { + if (_res.options & RES_DEBUG || verbose) + printf("Server failed, trying next server: %s\n", + i != NOERROR ? + DecodeError(i) : "Premature end of data"); + close(tcp_fd); + thisns++; + goto again; + } + printf("Server failed: %s\n", + i != NOERROR ? DecodeError(i) : "Premature end of data"); + break; + } + + + result = printinfo(&buf, cp, queryType, 1); + if (! result) { + error = ERR_PRINTING; + break; + } + numAnswers++; + cp = buf.qb2 + sizeof(dns_hdr_t); + if (ntohs(buf.qb1.dh_qdcount) > 0) + cp += dn_skipname(cp, buf.qb2 + len) + QFIXEDSZ; + + nmp = cp; + cp += dn_skipname(cp, (u_char *)&buf + len); + if ((_getshort(cp) == T_SOA)) { + dn_expand(buf.qb2, buf.qb2 + len, nmp, (u8_t *)dname[soacnt], + sizeof(dname[0])); + if (soacnt) { + if (strcmp(dname[0], dname[1]) == 0) + break; + } else + soacnt++; + } + } + + close(tcp_fd); + + switch (error) { + case NO_ERRORS: + return (SUCCESS); + + case ERR_READING_LEN: + return(ERROR); + + case ERR_PRINTING: + fprintf(stderr,"*** Error during listing of %s: %s\n", + namePtr, DecodeError(result)); + return(result); + + case ERR_READING_MSG: + headerPtr = (dns_hdr_t *) &buf; + fprintf(stderr,"ListHosts: error receiving zone transfer:\n"); + fprintf(stderr, + " result: %s, answers = %d, authority = %d, additional = %d\n", + resultcodes[headerPtr->dh_flag2 & DHF_RCODE], + ntohs(headerPtr->dh_ancount), + ntohs(headerPtr->dh_nscount), + ntohs(headerPtr->dh_arcount)); + return(ERROR); + default: + return(ERROR); + } +} + +static char * +DecodeError(result) + int result; +{ + switch(result) { + case NOERROR: return("Success"); break; + case FORMERR: return("Format error"); break; + case SERVFAIL: return("Server failed"); break; + case NXDOMAIN: return("Non-existent domain"); break; + case NOTIMP: return("Not implemented"); break; + case REFUSED: return("Query refused"); break; + case NOCHANGE: return("No change"); break; + case NO_INFO: return("No information"); break; + case ERROR: return("Unspecified error"); break; + case TIME_OUT: return("Timed out"); break; + case NONAUTH: return("Non-authoritative answer"); break; + default: break; + } + return("BAD ERROR VALUE"); +} + +static int tcpip_writeall(fd, buf, siz) +int fd; +char *buf; +unsigned siz; +{ + unsigned siz_org; + int nbytes; + + siz_org= siz; + + while (siz) + { + nbytes= write(fd, buf, siz); + if (nbytes == -1) + return nbytes; + assert(siz >= nbytes); + buf += nbytes; + siz -= nbytes; + } + return siz_org; +} diff --git a/commands/simple/hostaddr.c b/commands/simple/hostaddr.c new file mode 100755 index 000000000..7f02b4f2f --- /dev/null +++ b/commands/simple/hostaddr.c @@ -0,0 +1,311 @@ +/* +hostaddr.c + +Fetch an ip and/or ethernet address and print it on one line. + +Created: Jan 27, 1992 by Philip Homburg +*/ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/utsname.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <net/netlib.h> +#include <net/hton.h> +#include <net/gen/ether.h> +#include <net/gen/eth_io.h> +#include <net/gen/if_ether.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/ip_io.h> +#include <net/gen/netdb.h> +#include <net/gen/socket.h> +#include <net/gen/nameser.h> +#include <net/gen/resolv.h> +#include <net/gen/dhcp.h> + +char *prog_name; + +char DHCPCACHE[]="/usr/adm/dhcp.cache"; + +void main _ARGS(( int argc, char *argv[] )); +void usage _ARGS(( void )); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int c; + int first_print; + int a_flag, e_flag, i_flag, h_flag; + char *E_arg, *I_arg; + int do_ether, do_ip, do_asc_ip, do_hostname; + char *eth_device, *ip_device; + int eth_fd, ip_fd; + int result; + nwio_ethstat_t nwio_ethstat; + nwio_ipconf_t nwio_ipconf; + struct hostent *hostent; + char *hostname, *domain; + char nodename[2*256]; + dhcp_t dhcp; + + first_print= 1; + prog_name= argv[0]; + + a_flag= e_flag= i_flag= 0; + E_arg= I_arg= NULL; + + while((c= getopt(argc, argv, "?aheE:iI:")) != -1) + { + switch(c) + { + case '?': + usage(); + case 'a': + if (a_flag) + usage(); + a_flag= 1; + break; + case 'h': + if (h_flag) + usage(); + h_flag= 1; + break; + case 'e': + if (e_flag) + usage(); + e_flag= 1; + break; + case 'E': + if (E_arg) + usage(); + E_arg= optarg; + break; + case 'i': + if (i_flag) + usage(); + i_flag= 1; + break; + case 'I': + if (I_arg) + usage(); + I_arg= optarg; + break; + default: + usage(); + } + } + if(optind != argc) + usage(); + + do_ether= e_flag; + if (E_arg) + eth_device= E_arg; + else + { + eth_device= getenv("ETH_DEVICE"); + if (!eth_device) + eth_device= ETH_DEVICE; + } + + do_ip= i_flag; + do_asc_ip= a_flag; + do_hostname= h_flag; + if (I_arg) + ip_device= I_arg; + else + { + ip_device= getenv("IP_DEVICE"); + if (!ip_device) + ip_device= IP_DEVICE; + } + + if (!do_ether && !do_ip && !do_asc_ip && !do_hostname) + do_ether= do_ip= do_asc_ip= 1; + + if (do_ether) + { + eth_fd= open(eth_device, O_RDWR); + if (eth_fd == -1) + { + fprintf(stderr, "%s: Unable to open '%s': %s\n", + prog_name, eth_device, strerror(errno)); + exit(1); + } + result= ioctl(eth_fd, NWIOGETHSTAT, &nwio_ethstat); + if (result == -1) + { + fprintf(stderr, + "%s: Unable to fetch ethernet address: %s\n", + prog_name, strerror(errno)); + exit(1); + } + printf("%s%s", first_print ? "" : " ", + ether_ntoa(&nwio_ethstat.nwes_addr)); + first_print= 0; + } + if (do_ip || do_asc_ip || do_hostname) + { + ip_fd= open(ip_device, O_RDWR); + if (ip_fd == -1) + { + fprintf(stderr, "%s: Unable to open '%s': %s\n", + prog_name, ip_device, strerror(errno)); + exit(1); + } + result= ioctl(ip_fd, NWIOGIPCONF, &nwio_ipconf); + if (result == -1) + { + fprintf(stderr, + "%s: Unable to fetch IP address: %s\n", + prog_name, + strerror(errno)); + exit(1); + } + } + + setuid(getuid()); + + if (do_ip) + { + printf("%s%s", first_print ? "" : " ", + inet_ntoa(nwio_ipconf.nwic_ipaddr)); + first_print= 0; + } + if (do_asc_ip || do_hostname) + { + int fd; + int r; + dhcp_t d; + u8_t *data; + size_t hlen, dlen; + + hostname= NULL; + domain= NULL; + + /* Use a reverse DNS lookup to get the host name. This is + * the preferred method, but often fails due to lazy admins. + */ + hostent= gethostbyaddr((char *)&nwio_ipconf.nwic_ipaddr, + sizeof(nwio_ipconf.nwic_ipaddr), AF_INET); + if (hostent != NULL) hostname= hostent->h_name; + + if (hostname != NULL) + { + /* Reverse DNS works. */ + } + else if ((fd= open(DHCPCACHE, O_RDONLY)) == -1) + { + if (errno != ENOENT) + { + fprintf(stderr, "%s: %s: %s\n", + prog_name, DHCPCACHE, strerror(errno)); + exit(1); + } + } + else + { + /* Try to get the hostname from the DHCP data. */ + while ((r= read(fd, &d, sizeof(d))) == sizeof(d)) + { + if (d.yiaddr == nwio_ipconf.nwic_ipaddr) break; + } + if (r < 0) + { + fprintf(stderr, "%s: %s: %s\n", + prog_name, DHCPCACHE, strerror(errno)); + exit(1); + } + close(fd); + + if (r == sizeof(d)) + { + if (dhcp_gettag(&d, DHCP_TAG_HOSTNAME, + &data, &hlen)) + hostname= (char *) data; + + if (dhcp_gettag(&d, DHCP_TAG_DOMAIN, + &data, &dlen)) + domain= (char *) data; + + if (hostname != NULL) hostname[hlen] = 0; + if (domain != NULL) domain[dlen] = 0; + } + } + + if (hostname != NULL) + { + if (strchr(hostname, '.') != NULL) + { + domain= strchr(hostname, '.'); + *domain++ = 0; + } + } + else + { + /* No host name anywhere. Use the IP address. */ + hostname= inet_ntoa(nwio_ipconf.nwic_ipaddr); + domain= NULL; + } + + strcpy(nodename, hostname); + if (domain != NULL) + { + strcat(nodename, "."); + strcat(nodename, domain); + } + } + if (do_asc_ip) + { + printf("%s%s", first_print ? "" : " ", nodename); + first_print= 0; + } + if (do_hostname) + { +#if __minix_vmd + if (sysuname(_UTS_SET, _UTS_NODENAME, + nodename, strlen(nodename)+1) == -1) + { + fprintf(stderr, "%s: Unable to set nodename: %s\n", + prog_name, strerror(errno)); + exit(1); + } + + if (sysuname(_UTS_SET, _UTS_HOSTNAME, + hostname, strlen(hostname)+1) == -1) + { + fprintf(stderr, "%s: Unable to set hostname: %s\n", + prog_name, strerror(errno)); + exit(1); + } +#else + FILE *fp; + + if ((fp= fopen("/etc/hostname.file", "w")) == NULL + || fprintf(fp, "%s\n", nodename) == EOF + || fclose(fp) == EOF) + { + fprintf(stderr, "%s: /etc/hostname.file: %s\n", + prog_name, strerror(errno)); + exit(1); + } +#endif + } + if (!first_print) printf("\n"); + exit(0); +} + +void usage() +{ + fprintf(stderr, + "Usage: %s -[eiah] [-E <eth-device>] [-I <ip-device>]\n", + prog_name); + exit(1); +} diff --git a/commands/simple/id.c b/commands/simple/id.c new file mode 100755 index 000000000..4a0843ebd --- /dev/null +++ b/commands/simple/id.c @@ -0,0 +1,107 @@ +/* id - return uid and gid Author: John J. Marco */ + +/*=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-*/ +/* ----- id.c ----- */ +/* Id - get real and effective user id and group id */ +/* Author: John J. Marco */ +/* pa1343@sdcc15.ucsd.edu */ +/*-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=*/ + +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include <unistd.h> +#include <stdio.h> +#include <limits.h> + +_PROTOTYPE(int main, (void)); + +int main() +{ + struct passwd *pwd; + struct group *grp; + uid_t ruid, euid; + gid_t rgid, egid; +#if __minix_vmd + uid_t suid; + gid_t sgid; +#else +# define suid ruid +# define sgid rgid +#endif +#if NGROUPS_MAX > 0 + gid_t groups[NGROUPS_MAX]; + int ngroups; +#else +# define groups (&rgid) +# define ngroups 0 +#endif + int g; + int isug; + +#if __minix_vmd + get6id(&ruid, &euid, &suid, &rgid, &egid, &sgid); + isug = issetugid(); +#else + ruid = getuid(); + euid = geteuid(); + rgid = getgid(); + egid = getegid(); + isug = 0; +#endif +#if NGROUPS_MAX > 0 + ngroups = getgroups(NGROUPS_MAX, groups); +#endif + + if ((pwd = getpwuid(ruid)) == NULL) + printf("uid=%d", ruid); + else + printf("uid=%d(%s)", ruid, pwd->pw_name); + + if ((grp = getgrgid(rgid)) == NULL) + printf(" gid=%d", rgid); + else + printf(" gid=%d(%s)", rgid, grp->gr_name); + + if (euid != ruid) + if ((pwd = getpwuid(euid)) != NULL) + printf(" euid=%d(%s)", euid, pwd->pw_name); + else + printf(" euid=%d", euid); + + if (egid != rgid) + if ((grp = getgrgid(egid)) != NULL) + printf(" egid=%d(%s)", egid, grp->gr_name); + else + printf(" egid=%d", egid); + + if (suid != euid) + if ((pwd = getpwuid(suid)) != NULL) + printf(" suid=%d(%s)", suid, pwd->pw_name); + else + printf(" suid=%d", suid); + + if (sgid != egid) + if ((grp = getgrgid(sgid)) != NULL) + printf(" sgid=%d(%s)", sgid, grp->gr_name); + else + printf(" sgid=%d", sgid); + + if (isug) { + printf(" issetugid"); + } + + if (ngroups > 0) { + printf(" groups="); + for (g = 0; g < ngroups; g++) { + if (g > 0) fputc(',', stdout); + if ((grp = getgrgid(groups[g])) == NULL) + printf("%d", groups[g]); + else + printf("%d(%s)", groups[g], grp->gr_name); + } + } + + printf("\n"); + return(0); +} diff --git a/commands/simple/ifconfig.c b/commands/simple/ifconfig.c new file mode 100755 index 000000000..4568dbfec --- /dev/null +++ b/commands/simple/ifconfig.c @@ -0,0 +1,352 @@ +/* +ifconfig.c +*/ + +#define _POSIX_C_SOURCE 2 + +#include <sys/types.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <net/netlib.h> +#include <net/gen/in.h> +#include <net/gen/ip_io.h> +#include <net/gen/inet.h> + +#if __STDC__ +#define PROTO(x,y) x y +#else +#define PROTO(x,y) x() +#endif + +static PROTO (void usage, (void) ); +static PROTO (void set_hostaddr, (int ip_fd, char *host_s, int ins) ); +static PROTO (void set_netmask, (int ip_fd, char *net_s, int ins) ); +static PROTO (void set_mtu, (int ip_fd, char *mtu_s) ); +static PROTO (int check_ipaddrset, (int ip_fd) ); +static PROTO (int check_netmaskset, (int ip_fd) ); +static PROTO (int get_ipconf, (int ip_fd, + struct nwio_ipconf *ref_ipconf) ); +PROTO (int main, (int argc, char *argv[]) ); + +char *prog_name; + +main(argc, argv) +int argc; +char *argv[]; +{ + char *device_s, *hostaddr_s, *mtu_s, *netmask_s, **arg_s; + int ins; + int c, ip_fd, ifno; + struct nwio_ipconf ipconf; + int i_flag, v_flag, a_flag, modify; + char *d_arg, *h_arg, *m_arg, *n_arg; + + prog_name= argv[0]; + + d_arg= NULL; + h_arg= NULL; + m_arg= NULL; + n_arg= NULL; + i_flag= 0; + v_flag= 0; + a_flag= 0; + while ((c= getopt(argc, argv, "?I:h:m:n:iva")) != -1) + { + switch(c) + { + case '?': + usage(); + case 'I': + if (d_arg) + usage(); + d_arg= optarg; + break; + case 'h': + if (h_arg) + usage(); + h_arg= optarg; + break; + case 'm': + if (m_arg) + usage(); + m_arg= optarg; + break; + case 'n': + if (n_arg) + usage(); + n_arg= optarg; + break; + case 'i': + if (i_flag) + usage(); + i_flag= 1; + break; + case 'v': + if (v_flag) + usage(); + v_flag= 1; + break; + case 'a': + if (a_flag) + usage(); + a_flag= 1; + break; + default: + fprintf(stderr, "%s: getopt failed: '%c'\n", + prog_name, c); + exit(1); + } + } + modify = (h_arg != NULL || n_arg != NULL || m_arg != NULL); + if (a_flag && modify) usage(); + if (!modify) v_flag= 1; + + if (modify) setuid(getuid()); + + if (optind != argc) + usage(); + + hostaddr_s= h_arg; + mtu_s= m_arg; + netmask_s= n_arg; + ins= i_flag; + + ifno= 0; + do + { + if (!a_flag) { + device_s= d_arg; + if (device_s == NULL) + device_s= getenv("IP_DEVICE"); + if (device_s == NULL) + device_s= IP_DEVICE; + } else { + static char device[sizeof("/dev/ip99")]; + + sprintf(device, "/dev/ip%d", ifno); + device_s= device; + } + + ip_fd= open (device_s, O_RDWR); + if (ip_fd<0) + { + if (a_flag && (errno == ENOENT || errno == ENXIO)) + continue; + + fprintf(stderr, "%s: Unable to open '%s': %s\n", + prog_name, device_s, strerror(errno)); + exit(1); + } + + if (hostaddr_s) + set_hostaddr(ip_fd, hostaddr_s, ins); + + if (netmask_s) + set_netmask(ip_fd, netmask_s, ins); + + if (mtu_s) + set_mtu(ip_fd, mtu_s); + + if (v_flag) { + if (!get_ipconf(ip_fd, &ipconf)) + { + if (!a_flag) + { + fprintf(stderr, + "%s: %s: Host address not set\n", + prog_name, device_s); + exit(1); + } + } + else + { + printf("%s: address %s", device_s, + inet_ntoa(ipconf.nwic_ipaddr)); + + if (ipconf.nwic_flags & NWIC_NETMASK_SET) + { + printf(" netmask %s", + inet_ntoa(ipconf.nwic_netmask)); + } +#ifdef NWIC_MTU_SET + if (ipconf.nwic_mtu) + printf(" mtu %u", ipconf.nwic_mtu); +#endif + fputc('\n', stdout); + } + } + close(ip_fd); + } while (a_flag && ++ifno < 32); + exit(0); +} + +static void set_hostaddr (ip_fd, hostaddr_s, ins) +int ip_fd; +char *hostaddr_s; +int ins; +{ + ipaddr_t ipaddr; + struct nwio_ipconf ipconf; + int result; + + ipaddr= inet_addr (hostaddr_s); + if (ipaddr == (ipaddr_t)(-1)) + { + fprintf(stderr, "%s: Invalid host address (%s)\n", + prog_name, hostaddr_s); + exit(1); + } + if (ins && check_ipaddrset(ip_fd)) + return; + + ipconf.nwic_flags= NWIC_IPADDR_SET; + ipconf.nwic_ipaddr= ipaddr; + + result= ioctl(ip_fd, NWIOSIPCONF, &ipconf); + if (result<0) + { + fprintf(stderr, "%s: Unable to set IP configuration: %s\n", + prog_name, strerror(errno)); + exit(1); + } +} + +static int check_ipaddrset (ip_fd) +int ip_fd; +{ + struct nwio_ipconf ipconf; + + if (!get_ipconf(ip_fd, &ipconf)) + return 0; + + assert (ipconf.nwic_flags & NWIC_IPADDR_SET); + + return 1; +} + +static int get_ipconf (ip_fd, ref_ipaddr) +int ip_fd; +struct nwio_ipconf *ref_ipaddr; +{ + int flags; + int error, result; + nwio_ipconf_t ipconf; + + flags= fcntl(ip_fd, F_GETFL); + fcntl(ip_fd, F_SETFL, flags | O_NONBLOCK); + + result= ioctl (ip_fd, NWIOGIPCONF, &ipconf); + error= errno; + + fcntl(ip_fd, F_SETFL, flags); + + if (result <0 && error != EAGAIN) + { + errno= error; + fprintf(stderr, "%s: Unable to get IP configuration: %s\n", + prog_name, strerror(errno)); + exit(1); + } + if (result == 0) + { + *ref_ipaddr = ipconf; + } + return result>=0; +} + +static void usage() +{ + fprintf(stderr, + "Usage: %s [-I ip-device] [-h ipaddr] [-n netmask] [-m mtu] [-iva]\n", + prog_name); + exit(1); +} + +static void set_netmask (ip_fd, netmask_s, ins) +int ip_fd; +char *netmask_s; +int ins; +{ + ipaddr_t netmask; + struct nwio_ipconf ipconf; + int result; + + netmask= inet_addr (netmask_s); + if (netmask == (ipaddr_t)(-1)) + { + fprintf(stderr, "%s: Invalid netmask (%s)\n", + prog_name, netmask_s); + exit(1); + } + if (ins && check_netmaskset(ip_fd)) + return; + + ipconf.nwic_flags= NWIC_NETMASK_SET; + ipconf.nwic_netmask= netmask; + + result= ioctl(ip_fd, NWIOSIPCONF, &ipconf); + if (result<0) + { + fprintf(stderr, "%s: Unable to set IP configuration: %s\n", + prog_name, strerror(errno)); + exit(1); + } +} + +static void set_mtu (ip_fd, mtu_s) +int ip_fd; +char *mtu_s; +{ + ipaddr_t netmask; + int result; + long mtu; + char *check; + struct nwio_ipconf ipconf; + + mtu= strtol (mtu_s, &check, 0); + if (check[0] != '\0') + { + fprintf(stderr, "%s: Invalid mtu (%s)\n", + prog_name, mtu_s); + exit(1); + } + +#ifdef NWIC_MTU_SET + ipconf.nwic_flags= NWIC_MTU_SET; + ipconf.nwic_mtu= mtu; + + result= ioctl(ip_fd, NWIOSIPCONF, &ipconf); + if (result<0) + { + fprintf(stderr, "%s: Unable to set IP configuration: %s\n", + prog_name, strerror(errno)); + exit(1); + } +#endif +} + +static int check_netmaskset (ip_fd) +int ip_fd; +{ + struct nwio_ipconf ipconf; + + if (!get_ipconf(ip_fd, &ipconf)) + { + fprintf(stderr, +"%s: Unable to determine if netmask is set, please set IP address first\n", + prog_name); + exit(1); + } + + return (ipconf.nwic_flags & NWIC_NETMASK_SET); +} + +/* + * $PchId: ifconfig.c,v 1.7 2001/02/21 09:19:52 philip Exp $ + */ diff --git a/commands/simple/ifdef.c b/commands/simple/ifdef.c new file mode 100755 index 000000000..de293b748 --- /dev/null +++ b/commands/simple/ifdef.c @@ -0,0 +1,504 @@ +/* ifdef - remove #ifdefs Author: Warren Toomey */ + +/* Copyright 1989 by Warren Toomey wkt@cs.adfa.oz.au[@uunet.uu.net] + * + * You may freely copy or distribute this code as long as this notice + * remains intact. + * + * You may modify this code, as long as this notice remains intact, and + * you add another notice indicating that the code has been modified. + * + * You may NOT sell this code or in any way profit from this code without + * prior agreement from the author. + */ + +#include <sys/types.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> + +/* Definition of structures and constants used in ifdef.c */ + +/* Types of symbols */ +#define DEF 1 /* Symbol is defined */ +#define UNDEF 2 /* Symbol isn't defined */ +#define IGN 3 /* Ignore this symbol unless defined */ + +/* Redef mode values */ +#define MUTABLE 1 /* Symbol can change defined <-> undefined */ +#define IMMUTABLE 2 /* Symbol can't change as above */ + +/* Processing modes */ +#define NO 0 /* Don't process */ +#define YES 1 /* Process */ + +/* Ignore (IGN), ignore but process */ +struct DEFINE { + char *symbol; /* SLL of defined symbols. The redef */ + char type; /* field indicates if this symbol can */ + char redef; /* change from defined <-> undefined. */ + struct DEFINE *next; /* Type is DEF or UNDEF. */ +}; + +/* Global variables & structures */ +FILE *zin; /* Input file for processing */ +struct DEFINE *defptr; /* Defined symbols SLL */ +struct DEFINE *defend; /* Ptr to last node in defptr */ +struct DEFINE *deftemp; /* Ptr to last found node */ +int line = 1; /* Current line number */ +int table = 0; /* Don't normally want a table */ + +extern int optind; +extern char *optarg; + +/* Prototypes. */ +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(char fgetarg, (FILE *stream, char *cbuf)); +_PROTOTYPE(int find, (char *symd)); +_PROTOTYPE(void defit, (char *sym, int redef, int typed)); +_PROTOTYPE(void stop, (void)); +_PROTOTYPE(void gotoeoln, (void)); +_PROTOTYPE(void prteoln, (void)); +_PROTOTYPE(void printtable, (void)); +_PROTOTYPE(char getendif, (void)); +_PROTOTYPE(void gettable, (void)); +_PROTOTYPE(void parse, (void)); +_PROTOTYPE(void usage, (void)); + +#ifdef __STDC__ +char fgetarg ( FILE *stream , char *cbuf ) +#else +char fgetarg(stream, cbuf) /* Get next arg from file into cbuf, */ +FILE *stream; /* returning the character that */ +char *cbuf; /* terminated it. Cbuf returns 0 */ +#endif +{ /* if no arg. EOF is returned if no */ + int ch; /* args left in file. */ + int i; + + i = 0; + cbuf[i] = 0; + + while (((ch = fgetc(stream)) == ' ') || (ch == '\t') || (ch == '\n')) + if (ch == '\n') return(ch); /* Bypass leading */ + /* Whitespace */ + if (feof(stream)) return(EOF); + + cbuf[i++] = ch; + + while (((ch = fgetc(stream)) != ' ') && (ch != '\t') && (ch != '\n')) + cbuf[i++] = ch; /* Get the argument */ + + cbuf[i] = 0; + return(ch); +} + + +#ifdef __STDC__ +int find ( char *sym ) +#else +int find(sym) +char *sym; +#endif +{ /* Return DEF if defined else UNDEF */ + + deftemp = defptr; + while (deftemp) { /* Search for the symbol */ + if (!strcmp(deftemp->symbol, sym)) + return(deftemp->type); /* Setting up the type */ + deftemp = deftemp->next; + } + return(0); +} + + + +#define Define(x,y) defit(x,y,DEF) +#define Undefine(x,y) defit(x,y,UNDEF) +#define Ignore(x,y) defit(x,y,IGN) + +#ifdef __STDC__ +void defit ( char *sym , int redef , int type ) +#else +void defit(sym, redef, type) /* Add symbol to the define list */ +char *sym; +char redef; /* Mode: MUTABLE etc */ +char type; /* Type: DEF, UNDEF, IGN */ +#endif +{ + struct DEFINE *temp; + char c; + + c = find(sym); /* First try finding the symbol */ + if (type == c) return; /* Return if already declared */ + if (c) { /* We have to move if from DEF <-> UNDEF */ + if (deftemp->redef == IMMUTABLE) + return; + else { + deftemp->type = type; + deftemp->redef = redef; + } + } else { /* We must create a struct & add it */ + /* Malloc room for the struct */ + if ((temp = (struct DEFINE *)malloc(sizeof(struct DEFINE))) == NULL) { + (void)fprintf(stderr, "ifdef: could not malloc\n"); + exit(1); + } + + /* Malloc room for symbol */ + if ((temp->symbol = (char *)malloc(strlen(sym) + 1)) == NULL) { + (void)fprintf(stderr, "ifdef: could not malloc\n"); + exit(1); + } + (void)strcpy(temp->symbol, sym); /* Copy symbol into struct */ + temp->redef = redef; /* and set its redef mode too */ + temp->type = type; /* as well as making it defined */ + + + /* Now add to the SLL */ + if (defptr == NULL) /* If first node set */ + defptr = temp; /* the pointers to it */ + else + defend->next = temp; /* else add it to the */ + defend = temp; /* end of the list. */ + } +} + + + +#ifdef __STDC__ +void stop ( void ) +#else +void stop() +#endif +{ /* Stop: Tidy up at EOF */ + if (table) printtable(); + (void)fclose(zin); + exit(0); +} + +#define Goto { line++; if (ch!='\n') gotoeoln(); } +#define Print { line++; if (ch!='\n') prteoln(); } + +#ifdef __STDC__ +void gotoeoln ( void ) +#else +void gotoeoln() /* Go to the end of the line */ +#endif +{ + int ch; + while ((ch = fgetc(zin)) != '\n') + if (ch == EOF) stop(); +} + + +#ifdef __STDC__ +void prteoln ( void ) +#else +void prteoln() /* Print to the end of the line */ +#endif +{ + int ch; + while ((ch = fgetc(zin)) != '\n') + if (ch == EOF) + stop(); + else + (void)putchar(ch); + (void)putchar('\n'); +} + + +#ifdef __STDC__ +void printtable ( void ) +#else +void printtable() /* Print the defines in the SLL */ +#endif +{ + struct DEFINE *temp; + + (void)printf("Defined\n\n"); + + temp = defptr; + while (temp) { + if (temp->type == DEF) (void)printf("%s\n", temp->symbol); + temp = temp->next; + } + + (void)printf("\n\nUndefined\n\n"); + + temp = defptr; + while (temp) { + if (temp->type == UNDEF) (void)printf("%s\n", temp->symbol); + temp = temp->next; + } +} + +#ifdef __STDC__ +char getendif ( void ) +#else +char getendif() +#endif +{ /* Find matching endif when ignoring */ + char word[80]; /* Buffer for symbols */ + int ch; + int skip; /* Number of skipped #ifdefs */ + + skip = 1; + + while (1) { + /* Scan through the file looking for starting lines */ + if ((ch = fgetc(zin)) == EOF) + stop(); /* Get first char on the line */ + if (ch != '#') { /* If not a # ignore line */ + (void)putchar(ch); + Print; + continue; + } + ch = fgetarg(zin, word); /* Get the word after the # */ + + if (!strcmp(word, "ifdef") || !strcmp(word, "ifndef")) skip++; + /* Keep track of ifdefs & */ + if (!strcmp(word, "endif")) skip--; /* endifs */ + + (void)printf("#%s%c", word, ch); /* Print the line out */ + Print; + if (!skip) return('\n'); /* If matching endif, return */ + } +} + + +#ifdef __STDC__ +void gettable ( void ) +#else +void gettable() /* Get & print a table of defines etc. */ +#endif +{ + + char word[80]; /* Buffer for symbols */ + int ch; + + while (1) { + /* Scan through the file looking for starting lines */ + if ((ch = fgetc(zin)) == EOF) + stop(); /* Get first char on the line */ + if (ch != '#') { /* If not a # ignore line */ + Goto; + continue; + } + ch = fgetarg(zin, word); /* Get the word after the # */ + + if (!strcmp(word, "define")) { /* Define: Define the */ + ch = fgetarg(zin, word); /* symbol, and goto */ + Define(word, MUTABLE); /* the end of line */ + Goto; + continue; + } + if (!strcmp(word, "undef")) { /* Undef: Undefine the */ + ch = fgetarg(zin, word); /* symbol, and goto */ + Undefine(word, MUTABLE); /* the end of line */ + Goto; + continue; + } /* Ifdef: */ + if (!strcmp(word, "ifdef") || !strcmp(word, "ifndef")) { + ch = fgetarg(zin, word); /* Get the symbol */ + if (find(word) != DEF) + Undefine(word, MUTABLE); /* undefine it */ + Goto; + continue; + } + Goto; /* else ignore the line */ + } +} + + + +#ifdef __STDC__ +void parse ( void ) +#else +void parse() +#endif +{ /* Parse & remove ifdefs from C source */ + char word[80]; /* Buffer for symbols */ + int ch; + int proc; /* Should we be processing this bit? */ + int skip; /* Number of skipped #ifdefs */ + + proc = 1; + skip = 0; + + while (1) { + /* Scan through the file looking for starting lines */ + if ((ch = fgetc(zin)) == EOF) + stop(); /* Get first char on the line */ + if (ch != '#') + if (proc) { /* If not # and we're processing */ + (void)putchar(ch); /* then print the line */ + Print; + continue; + } else { + Goto; /* else just skip the line */ + continue; + } + + ch = fgetarg(zin, word); /* Get the word after the # */ + + if (!strcmp(word, "define") && proc) { /* Define: Define the */ + ch = fgetarg(zin, word); /* symbol, and goto */ + Define(word, MUTABLE); /* the end of line */ + (void)printf("#define %s%c", word, ch); + Print; + continue; + } + if (!strcmp(word, "undef") && proc) { /* Undef: Undefine the */ + ch = fgetarg(zin, word); /* symbol, and goto */ + Undefine(word, MUTABLE); /* the end of line */ + (void)printf("#undef %s%c", word, ch); + Print; + continue; + } + if (!strcmp(word, "if")) { /* If: we cannot handle these */ + if (!proc) /* at the moment, so just */ + skip++; /* treat them as an ignored */ + else { /* definition */ + (void)printf("#%s%c",word,ch); + Print; + ch = getendif(); /* Get matching endif */ + continue; + } + } + if (!strcmp(word, "ifdef")) { /* Ifdef: */ + if (!proc) /* If not processing */ + skip++; /* skip it */ + else { + ch = fgetarg(zin, word); /* Get the symbol */ + switch (find(word)) { + case DEF: + break; + case IGN: + (void)printf("#ifdef %s%c", word, ch); + Print; + ch = getendif(); /* Get matching endif */ + break; + /* If symbol undefined */ + default: + Undefine(word, MUTABLE); /* undefine it */ + proc = 0; /* & stop processing */ + } + } + Goto; + continue; + } + if (!strcmp(word, "ifndef")) { + /* Ifndef: */ + if (!proc) /* If not processing */ + skip++; /* skip the line */ + else { + ch = fgetarg(zin, word); /* Get the symbol */ + switch (find(word)) { /* If defined, stop */ + case DEF: + proc = 0; /* processing */ + break; + case IGN: + (void)printf("#ifdef %s%c", word, ch); + Print; + ch = getendif(); /* Get matching endif */ + break; + } + } + Goto; + continue; + } + if (!strcmp(word, "else") && !skip) { /* Else: Flip processing */ + proc = !proc; + Goto; + continue; + } + if (!strcmp(word, "endif")) { /* Endif: If no skipped */ + /* ifdefs turn processing */ + if (!skip) /* on, else decrement the */ + proc = 1; /* number of skips */ + else + skip--; + Goto; + continue; + } + /* The word fails all of the above tests, so if we're */ + /* processing, print the line. */ + if (proc) { + (void)printf("#%s%c", word, ch); + Print; + } else + Goto; + } +} + + +#ifdef __STDC__ +void usage ( void ) +#else +void usage() +#endif +{ + (void)fprintf(stderr, "Usage: ifdef [-t] [-Dsymbol] [-dsymbol] [-Usymbol] [-Isymbol] <file>\n"); + exit(0); +} + + +#ifdef __STDC__ +int main(int argc , char *argv []) +#else +int main(argc, argv) +int argc; +char *argv[]; +#endif +{ + char sym[80]; /* Temp symbol storage */ + int c; + + if (argc == 1) usage(); /* Catch the curious user */ + while ((c = getopt(argc, argv, "tD:d:U:I:")) != EOF) { + switch (c) { + case 't': + table = 1; /* Get the various options */ + break; + + case 'd': + (void)strcpy(sym, optarg); + Define(sym, MUTABLE); + break; + + case 'D': + (void)strcpy(sym, optarg); + Define(sym, IMMUTABLE); + break; + + case 'U': + (void)strcpy(sym, optarg); + Undefine(sym, IMMUTABLE); + break; + + case 'I': + (void)strcpy(sym, optarg); + Ignore(sym, IMMUTABLE); + break; + + default: usage(); + } + } + + zin = stdin; /* If a C file is named */ + /* Open stdin with it */ + if (*argv[argc - 1] != '-') { + (void)fclose(zin); + if ((zin = fopen(argv[argc - 1], "r")) == NULL) { + perror("ifdef"); + exit(1); + } + } + if (table) + gettable(); /* Either generate a table or */ + else + parse(); /* parse & replace with the file */ + return(0); +} diff --git a/commands/simple/in.fingerd.c b/commands/simple/in.fingerd.c new file mode 100755 index 000000000..db86e8ec9 --- /dev/null +++ b/commands/simple/in.fingerd.c @@ -0,0 +1,94 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#ifndef lint +static char sccsid[] = "@(#)in.fingerd.c 1.1 87/12/21 SMI"; /* from UCB 5.1 6/6/85 */ +#endif /* not lint */ + +/* + * Finger server. + */ +#include <sys/types.h> +#include <sys/wait.h> +#include <stdio.h> +#include <ctype.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +int main _ARGS(( int argc, char *argv[] )); +void fatal _ARGS(( char *prog, char *s )); + +int main(argc, argv) + char *argv[]; +{ + register char *sp; + char line[512]; + int i, p[2], pid, status; + FILE *fp; + char *av[4]; + + line[0] = '\0'; + fgets(line, sizeof(line), stdin); + sp = line + strlen(line); + if (sp > line && *--sp == '\n') *sp = '\0'; + sp = line; + av[0] = "finger"; + i = 1; + while (1) { + while (isspace(*sp)) + sp++; + if (!*sp) + break; + if (*sp == '/' && (sp[1] == 'W' || sp[1] == 'w')) { + sp += 2; + av[i++] = "-l"; + } + if (*sp && !isspace(*sp)) { + av[i++] = sp; + while (*sp && !isspace(*sp)) + sp++; + *sp = '\0'; + } + } + av[i] = 0; + if (pipe(p) < 0) + fatal(argv[0], "pipe"); + if ((pid = fork()) == 0) { + close(p[0]); + if (p[1] != 1) { + dup2(p[1], 1); + close(p[1]); + } + execv("/usr/bin/finger", av); + printf("No finger program found\n"); + fflush(stdout); + _exit(1); + } + if (pid == -1) + fatal(argv[0], "fork"); + close(p[1]); + if ((fp = fdopen(p[0], "r")) == NULL) + fatal(argv[0], "fdopen"); + while ((i = getc(fp)) != EOF) { + if (i == '\n') + putchar('\r'); + putchar(i); + } + fclose(fp); + while ((i = wait(&status)) != pid && i != -1) + ; + return(0); +} + +void fatal(prog, s) + char *prog, *s; +{ + + fprintf(stderr, "%s: ", prog); + perror(s); + exit(1); +} diff --git a/commands/simple/in.rshd.c b/commands/simple/in.rshd.c new file mode 100755 index 000000000..ec15f825a --- /dev/null +++ b/commands/simple/in.rshd.c @@ -0,0 +1,445 @@ +/* +in.rshd.c +*/ + +/* + main channel: + + back channel\0 + remuser\0 + locuser\0 + command\0 + data + + back channel: + signal\0 + +*/ + +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <pwd.h> +#include <grp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/netdb.h> +#include <net/gen/socket.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> +#include <net/hton.h> +#include <net/netlib.h> + +#define DEBUG 0 + +#if DEBUG +#define where() fprintf(stderr, "%s, %d: ", __FILE__, __LINE__) +#endif + +char cmdbuf[_POSIX_ARG_MAX+1], locuser[16], remuser[16]; +extern char **environ; +char username[20]="USER="; +char homedir[64]="HOME="; +char shell[64]="SHELL="; +char tz[1024]="TZ="; +char *envinit[]= {homedir, shell, username, tz, "PATH=:/bin:/usr/bin", 0}; +char *prog_name; +char buffer[PIPE_BUF]; + +#if __STDC__ +#define PROTO(func, args) func args +#else +#define PROTO(func, args) func () +#endif + +PROTO (int main, (int argc, char *argv[])); +PROTO (void getstr, (char*buf, int cnt, char *err)); +PROTO (void close_on_exec, (int fd)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int result, result1; + nwio_tcpconf_t tcpconf, err_tcpconf; + nwio_tcpcl_t tcpconnopt; + nwio_tcpatt_t tcpattachopt; + tcpport_t tcpport; + tcpport_t err_port; + int err_fd, pds[2]; + pid_t pid, pid1, new_pg; +#if USEATTACH + int err2_fd; +#endif + struct passwd *pwent; + char *cp, *buff_ptr, *TZ; + char sig; + + prog_name= argv[0]; + if (argc != 1) + { + fprintf(stderr, "%s: wrong number of arguments (%d)\n", + prog_name, argc); + exit(1); + } + + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + +#if DEBUG + { where(); fprintf(stderr, "\n"); } +#endif + result= ioctl (0, NWIOGTCPCONF, &tcpconf); + if (result<0) + { + fprintf(stderr, "%s: ioctl(NWIOGTCPCONF)= %d : %s\n", + prog_name, errno, strerror(errno)); + exit(1); + } +#if DEBUG + { where(); fprintf(stderr, "\n"); } +#endif + + tcpport= ntohs(tcpconf.nwtc_remport); + if (tcpport >= TCPPORT_RESERVED || tcpport < TCPPORT_RESERVED/2) + { + printf("\1%s: unprotected port (%d)\n", prog_name, tcpport); + exit(1); + } + alarm(60); + err_port= 0; + for (;;) + { + char c; + result= read(0, &c, 1); + if (result <0) + { + fprintf(stderr, "%s: read= %d : %s\n", prog_name, + errno, strerror(errno)); + } + if (result<1) + exit(1); + if (c == 0) + break; + err_port= err_port*10 + c - '0'; + } + alarm(0); + if (err_port != 0) + { + int n, pid, lport; + + pid= getpid(); + lport= 1; + do { + lport= (lport << 1) | (pid & 1); + pid >>= 1; + } while (lport < TCPPORT_RESERVED/2); + + n= TCPPORT_RESERVED/2; + do + { + if (--lport < TCPPORT_RESERVED/2) + lport= TCPPORT_RESERVED-1; + err_fd= open ("/dev/tcp", O_RDWR); + if (err_fd<0) + { + fprintf(stderr, "%s: open= %d : %s\n", + prog_name, errno, strerror(errno)); + exit(1); + } + close_on_exec(err_fd); + err_tcpconf.nwtc_flags= NWTC_LP_SET | NWTC_SET_RA | + NWTC_SET_RP | NWTC_EXCL; + err_tcpconf.nwtc_locport= htons(lport); + err_tcpconf.nwtc_remport= htons(err_port); + err_tcpconf.nwtc_remaddr= tcpconf.nwtc_remaddr; + +#if DEBUG + { where(); fprintf(stderr, "\n"); } +#endif + result= ioctl (err_fd, NWIOSTCPCONF, &err_tcpconf); + if (result == 0) break; + if (errno != EADDRINUSE) + { + fprintf(stderr, + "%s: ioctl(NWIOSTCPCONF)= %d : %s\n", + prog_name, errno, strerror(errno)); + exit(1); + } + close(err_fd); + } while (--n > 0); + if (n == 0) + { + printf("\1can't get stderr port\n"); + exit(1); + } + + err_tcpconf.nwtc_flags= NWTC_SHARED; +#if DEBUG +{ where(); fprintf(stderr, "\n"); } +#endif + result= ioctl (err_fd, NWIOSTCPCONF, &err_tcpconf); + if (result<0) + { + fprintf(stderr, + "%s: ioctl(NWIOSTCPCONF)= %d : %s\n", + prog_name, errno, strerror(errno)); + exit(1); + } +#if DEBUG +{ where(); fprintf(stderr, "\n"); } +#endif + tcpconnopt.nwtcl_flags= 0; + + n= 20; + for (;;) + { +#if DEBUG +{ where(); fprintf(stderr, "\n"); } +#endif + result= ioctl (err_fd, NWIOTCPCONN, &tcpconnopt); + if (result == 0) break; + if (errno != EAGAIN && errno != ECONNREFUSED) + { + fprintf(stderr, + "%s: ioctl(NWIOTCPCONN)= %d : %s\n", + prog_name, errno, strerror(errno)); + exit(1); + } + if (--n == 0) break; + sleep(1); +#if DEBUG +{ where(); fprintf(stderr, "\n"); } +#endif + } +#if USEATTACH + err2_fd= open ("/dev/tcp", O_RDWR); + close_on_exec(err2_fd); + if (err2_fd<0) + { + fprintf(stderr, "%s: open= %d : %s\n", errno, + prog_name, strerror(errno)); + exit(1); + } +#if DEBUG + { where(); fprintf(stderr, "\n"); } +#endif + result= ioctl (err2_fd, NWIOSTCPCONF, &err_tcpconf); + if (result<0) + { + fprintf(stderr, "%s: ioctl(NWIOSTCPCONF)= %d : %s\n", + prog_name, errno, strerror(errno)); + exit(1); + } +#if DEBUG + { where(); fprintf(stderr, "\n"); } +#endif + tcpattachopt.nwta_flags= 0; +#if DEBUG + { where(); fprintf(stderr, "\n"); } +#endif + result= ioctl (err2_fd, NWIOTCPATTACH, &tcpattachopt); + if (result<0) + { + fprintf(stderr, "%s: ioctl(NWIOTCPATTACH)= %d : %s\n", + prog_name, errno, strerror(errno)); + exit(1); + } +#if DEBUG + { where(); fprintf(stderr, "\n"); } +#endif +#endif + } + getstr(remuser, sizeof(remuser), "remuser"); + getstr(locuser, sizeof(locuser), "locuser"); + getstr(cmdbuf, sizeof(cmdbuf), "cmdbuf"); + setpwent(); + pwent= getpwnam(locuser); + if (!pwent) + { + printf("\1Login incorrect.\n"); + exit(1); + } + endpwent(); + if (chdir(pwent->pw_dir) < 0) + { + chdir("/"); + } +#if DEBUG + { where(); fprintf(stderr, "calling iruserok(%s, %d, %s, %s)\n", + inet_ntoa(tcpconf.nwtc_remaddr), 0, remuser, locuser); } +#endif + if (iruserok(tcpconf.nwtc_remaddr, 0, remuser, locuser) < 0) + { + printf("\1Permission denied.\n"); + exit(1); + } + if (err_port) + { + /* Let's go to a different process group. */ + new_pg= setsid(); + pid= fork(); + if (pid<0) + { + if (errno != EAGAIN) + { + fprintf(stderr, "%s: fork()= %d : %s\n", + prog_name, errno, strerror(errno)); + } + printf("\1Try again.\n"); + exit(1); + } + if (pid) + { + close(0); /* stdin */ + close(1); /* stdout */ +#if USEATTACH + close(err_fd); /* stderr for shell */ +#endif + dup2(2,0); + dup2(2,1); + for (;;) + { +#if !USEATTACH + if (read(err_fd, &sig, 1) <= 0) +#else + if (read(err2_fd, &sig, 1) <= 0) +#endif + { +#if 0 + printf("read failed: %d\n", errno); +#endif + exit(0); + } + pid= 0; +#if 0 + printf("killing %d with %d\n", -new_pg, sig); +#endif + kill(-new_pg, sig); + } + } +#if USEATTACH + close(err2_fd); /* signal channel for parent */ +#endif + result= pipe(pds); + if (result<0) + { + printf("\1Can't make pipe\n"); + kill(getppid(), SIGTERM); + exit(1); + } + pid1= fork(); + if (pid1<0) + { + if (errno != EAGAIN) + { + fprintf(stderr, "%s: fork()= %d : %s\n", + prog_name, errno, strerror(errno)); + } + printf("\1Try again.\n"); + kill(-new_pg, SIGTERM); + exit(1); + } + if (pid1) + { + close(pds[1]); /* write side of pipe */ + for (;;) + { + result= read(pds[0], buffer, sizeof(buffer)); + if (result<=0) + { + kill(pid, SIGTERM); + exit(0); + } + buff_ptr= buffer; + while (result>0) + { + result1= write (err_fd, buff_ptr, + result); + if (result1 <= 0) + { + fprintf(stderr, + "%s: write()= %d : %s\n", + prog_name, errno, + strerror(errno)); + kill(-new_pg, SIGTERM); + exit(1); + } + result -= result1; + } + } + } + close(err_fd); /* file descriptor for error channel */ + close (pds[0]); /* read side of pipe */ + dup2(pds[1], 2); + close (pds[1]); /* write side of pipe */ + } + if (*pwent->pw_shell == '\0') + pwent->pw_shell= "/bin/sh"; +#if __minix_vmd + initgroups(pwent->pw_name, pwent->pw_gid); +#endif + setgid(pwent->pw_gid); + setuid(pwent->pw_uid); + TZ=getenv("TZ"); + environ= envinit; + strncat(homedir, pwent->pw_dir, sizeof(homedir)-6); + strncat(shell, pwent->pw_shell, sizeof(shell)-7); + strncat(username, pwent->pw_name, sizeof(username)-6); + if (TZ) + strncat(tz, TZ, sizeof(tz)-4); + else + envinit[3]= NULL; + + cp= strrchr(pwent->pw_shell, '/'); + if (cp) + cp++; + else + cp= pwent->pw_shell; + + if (!err_port) + dup2(1, 2); + write(1, "\0", 1); + + execl(pwent->pw_shell, cp, "-c", cmdbuf, 0); + close(2); + open("/dev/tty", O_RDWR); + fprintf(stderr, "%s: execl(%s, %s, .., %s)= %d : %s\n", prog_name, + pwent->pw_shell, cp, cmdbuf, errno, strerror(errno)); + kill(getppid(), SIGTERM); + exit(1); +} + +void getstr(buf, cnt, err) +char *buf; +int cnt; +char *err; +{ + char c; + + do + { + if (read(0, &c, 1) != 1) + exit(1); + *buf++ = c; + if (--cnt == 0) + { + printf("\1%s too long", err); + exit(1); + } + } while (c != 0); +} + +void close_on_exec(fd) +int fd; +{ + (void) fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC); +} diff --git a/commands/simple/install.c b/commands/simple/install.c new file mode 100755 index 000000000..894791659 --- /dev/null +++ b/commands/simple/install.c @@ -0,0 +1,599 @@ +/* install 1.11 - install files. Author: Kees J. Bot + * 21 Feb 1993 + */ +#define nil 0 +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <a.out.h> +#include <limits.h> +#include <pwd.h> +#include <grp.h> +#include <utime.h> +#include <signal.h> + +/* First line used on a self-decompressing executable. */ +char ZCAT[]= "#!/usr/bin/zexec /usr/bin/zcat\n"; +char GZCAT[]= "#!/usr/bin/zexec /usr/bin/gzcat\n"; + +/* Compression filters. */ +char *COMPRESS[]= { "compress", nil }; +char *GZIP[]= { "gzip", "-#", nil }; + +int excode= 0; /* Exit code. */ + +void report(char *label) +{ + if (label == nil || label[0] == 0) + fprintf(stderr, "install: %s\n", strerror(errno)); + else + fprintf(stderr, "install: %s: %s\n", label, strerror(errno)); + excode= 1; +} + +void fatal(char *label) +{ + report(label); + exit(1); +} + +void *allocate(void *mem, size_t size) +/* Safe malloc/realloc. */ +{ + mem= mem == nil ? malloc(size) : realloc(mem, size); + + if (mem == nil) fatal(nil); + return mem; +} + +void deallocate(void *mem) +{ + if (mem != nil) free(mem); +} + +int lflag= 0; /* Make a hard link if possible. */ +int cflag= 0; /* Copy if you can't link, otherwise symlink. */ +int dflag= 0; /* Create a directory. */ +int strip= 0; /* Strip the copy. */ +char **compress= nil; /* Compress utility to make a compressed executable. */ +char *zcat= nil; /* Line one to decompress. */ + +long stack= -1; /* Amount of heap + stack. */ +int wordpow= 1; /* Must be multiplied with wordsize ** wordpow */ + /* So 8kb for an 8086 and 16kb for the rest. */ + +pid_t filter(int fd, char **command) +/* Let a command filter the output to fd. */ +{ + pid_t pid; + int pfd[2]; + + if (pipe(pfd) < 0) { + report("pipe()"); + return -1; + } + + switch ((pid= fork())) { + case -1: + report("fork()"); + return -1; + case 0: + /* Run the filter. */ + dup2(pfd[0], 0); + dup2(fd, 1); + close(pfd[0]); + close(pfd[1]); + close(fd); + signal(SIGPIPE, SIG_DFL); + execvp(command[0], command); + fatal(command[0]); + } + /* Connect fd to the pipe. */ + dup2(pfd[1], fd); + close(pfd[0]); + close(pfd[1]); + return pid; +} + +int mkdirp(char *dir, int mode, int owner, int group) +/* mkdir -p dir */ +{ + int keep; + char *sep, *pref; + + sep= dir; + while (*sep == '/') sep++; + + if (*sep == 0) { + errno= EINVAL; + return -1; + } + + do { + while (*sep != '/' && *sep != 0) sep++; + pref= sep; + while (*sep == '/') sep++; + + keep= *pref; *pref= 0; + + if (strcmp(dir, ".") == 0 || strcmp(dir, "..") == 0) continue; + + if (mkdir(dir, mode) < 0) { + if (errno != EEXIST || *sep == 0) { + /* On purpose not doing: *pref= keep; */ + return -1; + } + } else { + if (chown(dir, owner, group) < 0 && errno != EPERM) + return -1; + } + } while (*pref= keep, *sep != 0); + return 0; +} + +void makedir(char *dir, int mode, int owner, int group) +/* Install a directory, and set it's modes. */ +{ + struct stat st; + + if (stat(dir, &st) < 0) { + if (errno != ENOENT) { report(dir); return; } + + /* The target doesn't exist, make it. */ + if (mode == -1) mode= 0755; + if (owner == -1) owner= getuid(); + if (group == -1) group= getgid(); + + if (mkdirp(dir, mode, owner, group) < 0) { + report(dir); return; + } + } else { + /* The target does exist, change mode and ownership. */ + if (mode == -1) mode= (st.st_mode & 07777) | 0555; + + if ((st.st_mode & 07777) != mode) { + if (chmod(dir, mode) < 0) { report(dir); return; } + } + if (owner == -1) owner= st.st_uid; + if (group == -1) group= st.st_gid; + if (st.st_uid != owner || st.st_gid != group) { + if (chown(dir, owner, group) < 0 && errno != EPERM) { + report(dir); return; + } + /* Set the mode again, chown may have wrecked it. */ + (void) chmod(dir, mode); + } + } +} + +int setstack(struct exec *hdr) +/* Set the stack size in a header. Return true if something changed. */ +{ + long total; + + total= stack; + while (wordpow > 0) { + total *= hdr->a_cpu == A_I8086 ? 2 : 4; + wordpow--; + } + total+= hdr->a_data + hdr->a_bss; + + if (!(hdr->a_flags & A_SEP)) { + total+= hdr->a_text; +#ifdef A_PAL + if (hdr->a_flags & A_PAL) total+= hdr->a_hdrlen; +#endif + } + if (hdr->a_cpu == A_I8086 && total > 0x10000L) + total= 0x10000L; + + if (hdr->a_total != total) { + /* Need to change stack allocation. */ + hdr->a_total= total; + + return 1; + } + return 0; +} + +void copylink(char *source, char *dest, int mode, int owner, int group) +{ + struct stat sst, dst; + int sfd, dfd, n; + int r, same= 0, change= 0, docopy= 1; + char buf[4096]; +# define hdr ((struct exec *) buf) + pid_t pid; + int status; + + /* Source must exist as a plain file, dest may exist as a plain file. */ + + if (stat(source, &sst) < 0) { report(source); return; } + + if (mode == -1) { + mode= sst.st_mode & 07777; + if (!lflag || cflag) { + mode|= 0444; + if (mode & 0111) mode|= 0111; + } + } + if (owner == -1) owner= sst.st_uid; + if (group == -1) group= sst.st_gid; + + if (!S_ISREG(sst.st_mode)) { + fprintf(stderr, "install: %s is not a regular file\n", source); + excode= 1; + return; + } + r= stat(dest, &dst); + if (r < 0) { + if (errno != ENOENT) { report(dest); return; } + } else { + if (!S_ISREG(dst.st_mode)) { + fprintf(stderr, "install: %s is not a regular file\n", + dest); + excode= 1; + return; + } + + /* Are the files the same? */ + if (sst.st_dev == dst.st_dev && sst.st_ino == dst.st_ino) { + if (!lflag && cflag) { + fprintf(stderr, + "install: %s and %s are the same, can't copy\n", + source, dest); + excode= 1; + return; + } + same= 1; + } + } + + if (lflag && !same) { + /* Try to link the files. */ + + if (r >= 0 && unlink(dest) < 0) { + report(dest); return; + } + + if (link(source, dest) >= 0) { + docopy= 0; + } else { + if (!cflag || errno != EXDEV) { + fprintf(stderr, + "install: can't link %s to %s: %s\n", + source, dest, strerror(errno)); + excode= 1; + return; + } + } + } + + if (docopy && !same) { + /* Copy the files, stripping if necessary. */ + long count= LONG_MAX; + int first= 1; + + if ((sfd= open(source, O_RDONLY)) < 0) { + report(source); return; + } + + /* Open for write is less simple, its mode may be 444. */ + dfd= open(dest, O_WRONLY|O_CREAT|O_TRUNC, mode | 0600); + if (dfd < 0 && errno == EACCES) { + (void) chmod(dest, mode | 0600); + dfd= open(dest, O_WRONLY|O_TRUNC); + } + if (dfd < 0) { + report(dest); + close(sfd); + return; + } + + pid= 0; + while (count > 0 && (n= read(sfd, buf, sizeof(buf))) > 0) { + if (first && n >= A_MINHDR && !BADMAG(*hdr)) { + if (strip) { + count= hdr->a_hdrlen + + hdr->a_text + hdr->a_data; +#ifdef A_NSYM + hdr->a_flags &= ~A_NSYM; +#endif + hdr->a_syms= 0; + } + if (stack != -1 && setstack(hdr)) change= 1; + + if (compress != nil) { + /* Write first #! line. */ + (void) write(dfd, zcat, strlen(zcat)); + + /* Put a compressor in between. */ + if ((pid= filter(dfd, compress)) < 0) { + close(sfd); + close(dfd); + return; + } + change= 1; + } + } + if (count < n) n= count; + + if (write(dfd, buf, n) < 0) { + report(dest); + close(sfd); + close(dfd); + if (pid != 0) (void) waitpid(pid, nil, 0); + return; + } + count-= n; + first= 0; + } + if (n < 0) report(source); + close(sfd); + close(dfd); + if (pid != 0 && waitpid(pid, &status, 0) < 0 || status != 0) { + excode= 1; + return; + } + if (n < 0) return; + } else { + if (stack != -1) { + /* The file has been linked into place. Set the + * stack size. + */ + if ((dfd= open(dest, O_RDWR)) < 0) { + report(dest); + return; + } + + if ((n= read(dfd, buf, sizeof(*hdr))) < 0) { + report(dest); return; + } + + if (n >= A_MINHDR && !BADMAG(*hdr) && setstack(hdr)) { + if (lseek(dfd, (off_t) 0, SEEK_SET) == -1 + || write(dfd, buf, n) < 0 + ) { + report(dest); + close(dfd); + return; + } + change= 1; + } + close(dfd); + } + } + + if (stat(dest, &dst) < 0) { report(dest); return; } + + if ((dst.st_mode & 07777) != mode) { + if (chmod(dest, mode) < 0) { report(dest); return; } + } + if (dst.st_uid != owner || dst.st_gid != group) { + if (chown(dest, owner, group) < 0 && errno != EPERM) { + report(dest); return; + } + /* Set the mode again, chown may have wrecked it. */ + (void) chmod(dest, mode); + } + if (!change) { + struct utimbuf ubuf; + + ubuf.actime= dst.st_atime; + ubuf.modtime= sst.st_mtime; + + if (utime(dest, &ubuf) < 0 && errno != EPERM) { + report(dest); return; + } + } +} + +void usage(void) +{ + fprintf(stderr, "\ +Usage:\n\ + install [-lcsz#] [-o owner] [-g group] [-m mode] [-S stack] [file1] file2\n\ + install [-lcsz#] [-o owner] [-g group] [-m mode] [-S stack] file ... dir\n\ + install -d [-o owner] [-g group] [-m mode] directory\n"); + exit(1); +} + +void main(int argc, char **argv) +{ + int i= 1; + int mode= -1; /* Mode of target. */ + int owner= -1; /* Owner. */ + int group= -1; /* Group. */ + int super = 0; +#if NGROUPS_MAX > 0 + gid_t groups[NGROUPS_MAX]; + int ngroups; + int g; +#endif + + /* Only those in group 0 are allowed to set owner and group. */ + if (getgid() == 0) super = 1; +#if NGROUPS_MAX > 0 + ngroups= getgroups(NGROUPS_MAX, groups); + for (g= 0; g < ngroups; g++) if (groups[g] == 0) super= 1; +#endif + if (!super) { + setgid(getgid()); + setuid(getuid()); + } + + /* May use a filter. */ + signal(SIGPIPE, SIG_IGN); + + while (i < argc && argv[i][0] == '-') { + char *p= argv[i++]+1; + char *end; + unsigned long num; + int wp; + struct passwd *pw; + struct group *gr; + + if (strcmp(p, "-") == 0) break; + + while (*p != 0) { + switch (*p++) { + case 'l': lflag= 1; break; + case 'c': cflag= 1; break; + case 's': strip= 1; break; + case 'd': dflag= 1; break; + case 'z': + if (compress == nil) { + compress= COMPRESS; + zcat= ZCAT; + } + break; + case 'o': + if (*p == 0) { + if (i == argc) usage(); + p= argv[i++]; + if (*p == 0) usage(); + } + num= strtoul(p, &end, 10); + if (*end == 0) { + if ((uid_t) num != num) usage(); + owner= num; + } else { + if ((pw= getpwnam(p)) == nil) { + fprintf(stderr, + "install: %s: unknown user\n", + p); + exit(1); + } + owner= pw->pw_uid; + } + p= ""; + break; + case 'g': + if (*p == 0) { + if (i == argc) usage(); + p= argv[i++]; + if (*p == 0) usage(); + } + num= strtoul(p, &end, 10); + if (*end == 0) { + if ((gid_t) num != num) usage(); + group= num; + } else { + if ((gr= getgrnam(p)) == nil) { + fprintf(stderr, + "install: %s: unknown user\n", + p); + exit(1); + } + group= gr->gr_gid; + } + p= ""; + break; + case 'm': + if (*p == 0) { + if (i == argc) usage(); + p= argv[i++]; + if (*p == 0) usage(); + } + num= strtoul(p, &end, 010); + if (*end != 0 || (num & 07777) != num) usage(); + mode= num; + if ((mode & S_ISUID) && super && owner == -1) { + /* Setuid what? Root most likely. */ + owner= 0; + } + if ((mode & S_ISGID) && super && group == -1) { + group= 0; + } + p= ""; + break; + case 'S': + if (*p == 0) { + if (i == argc) usage(); + p= argv[i++]; + if (*p == 0) usage(); + } + stack= strtol(p, &end, 0); + wp= 0; + if (end == p || stack < 0) usage(); + p= end; + while (*p != 0) { + switch (*p++) { + case 'm': + case 'M': num= 1024 * 1024L; break; + case 'k': + case 'K': num= 1024; break; + case 'w': + case 'W': num= 4; wp++; break; + case 'b': + case 'B': num= 1; break; + default: usage(); + } + if (stack > LONG_MAX / num) usage(); + stack*= num; + } + wordpow= 0; + while (wp > 0) { stack /= 4; wordpow++; wp--; } + break; + default: + if ((unsigned) (p[-1] - '1') <= ('9' - '1')) { + compress= GZIP; + GZIP[1][1]= p[-1]; + zcat= GZCAT; + break; + } + usage(); + } + } + } + /* Some options don't mix. */ + if (dflag && (cflag || lflag || strip)) usage(); + + /* Don't let the user umask interfere. */ + umask(000); + + if (dflag) { + /* install directory */ + if ((argc - i) != 1) usage(); + + makedir(argv[i], mode, owner, group); + } else { + struct stat st; + + if ((argc - i) < 1) usage(); + if ((lflag || cflag) && (argc - i) == 1) usage(); + + if (stat(argv[argc-1], &st) >= 0 && S_ISDIR(st.st_mode)) { + /* install file ... dir */ + char *target= nil; + char *base; + + if ((argc - i) == 1) usage(); + + while (i < argc-1) { + if ((base= strrchr(argv[i], '/')) == nil) + base= argv[i]; + else + base++; + target= allocate(target, strlen(argv[argc-1]) + + 1 + strlen(base) + 1); + strcpy(target, argv[argc-1]); + strcat(target, "/"); + strcat(target, base); + + copylink(argv[i++], target, mode, owner, group); + } + } else { + /* install [file1] file2 */ + + copylink(argv[i], argv[argc-1], mode, owner, group); + } + } + exit(excode); +} diff --git a/commands/simple/intr.c b/commands/simple/intr.c new file mode 100755 index 000000000..f858cdb4f --- /dev/null +++ b/commands/simple/intr.c @@ -0,0 +1,148 @@ +/* intr 1.4 - run a command with interrupts enabled + * Author: Kees J. Bot + * 17 Dec 1992 + */ +#define nil 0 +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE 1 +#endif +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#if __minix +static char DEV_LOG[]= "/dev/log"; +#else +static char DEV_LOG[]= "/dev/console"; +#endif + +static void say(const char *s) +{ + write(2, s, strlen(s)); +} + +static void fatal(const char *label) +{ + int err= errno; + + say("intr: "); + say(label); + say(": "); + say(strerror(err)); + say("\n"); + exit(1); +} + +static void usage(void) +{ + say("Usage: intr [-d] [-t seconds] command [arg ...]\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + int fd; + unsigned n= 0; + int daemonize= 0; + int i; + + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++]+1, *end; + unsigned long sec; + + if (opt[0] == '-' && opt[1] == 0) break; + + while (*opt != 0) switch (*opt++) { + case 'd': + /* -d */ + daemonize= 1; + break; + case 't': + /* -t n: alarm in n seconds. */ + if (*opt == 0) { + if (i == argc) usage(); + opt= argv[i++]; + } + sec= strtoul(opt, &end, 10); + if (end == opt || *end != 0 || (n= sec) != sec) + usage(); + opt= ""; + break; + default: + usage(); + } + } + + if ((argc - i) < 1) usage(); + + /* Try to open the controlling tty. */ + if ((fd= open("/dev/tty", O_RDWR)) < 0) { + if (errno != ENXIO) fatal("/dev/tty"); + } + + if (!daemonize) { + /* Bring to the foreground. If we already have a controlling + * tty then use it. Otherwise try to allocate the console as + * controlling tty and begin a process group. + */ + if (fd < 0) { + if (setsid() < 0) fatal("setsid()"); + + fd= open("/dev/console", O_RDWR); + } + + if (fd >= 0) { + if (fd != 0) { + dup2(fd, 0); + close(fd); + } + dup2(0, 1); + dup2(0, 2); + } + + /* Set the usual signals back to the default. */ + signal(SIGHUP, SIG_DFL); + signal(SIGINT, SIG_DFL); + signal(SIGQUIT, SIG_DFL); + signal(SIGTERM, SIG_DFL); + } else { + /* Send to the background. Redirect input to /dev/null, and + * output to the log device. Detach from the process group. + */ + if (fd >= 0) { + close(fd); + + if (setsid() < 0) fatal("setsid()"); + } + if ((fd= open("/dev/null", O_RDWR)) < 0) fatal("/dev/null"); + if (fd != 0) { + dup2(fd, 0); + close(fd); + } + if ((fd= open(DEV_LOG, O_WRONLY)) < 0) fatal(DEV_LOG); + if (fd != 1) { + dup2(fd, 1); + close(fd); + } + dup2(1, 2); + + /* Move to the root directory. */ + (void) chdir("/"); + } + + /* Schedule the alarm. (It is inherited over execve.) */ + if (n != 0) alarm(n); + + /* Call program. */ + execvp(argv[i], argv + i); + + /* Complain. */ + fatal(argv[i]); + return 0; +} diff --git a/commands/simple/irdpd.c b/commands/simple/irdpd.c new file mode 100755 index 000000000..e818cd2dd --- /dev/null +++ b/commands/simple/irdpd.c @@ -0,0 +1,624 @@ +/* irdpd 1.10 - Internet router discovery protocol daemon. + * Author: Kees J. Bot + * 28 May 1994 + * Activily solicitate or passively look for routers. + * Based heavily on its forerunners, the irdp_sol and rip2icmp daemons by + * Philip Homburg. + */ +#define nil 0 +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <limits.h> +#include <unistd.h> +#include <signal.h> +#include <sys/ioctl.h> +#include <sys/asynchio.h> +#include <net/hton.h> +#include <net/netlib.h> +#include <net/gen/in.h> +#include <net/gen/ip_hdr.h> +#include <net/gen/icmp.h> +#include <net/gen/icmp_hdr.h> +#include <net/gen/ip_io.h> +#include <net/gen/inet.h> +#include <net/gen/netdb.h> +#include <net/gen/oneCsum.h> +#include <net/gen/socket.h> +#include <net/gen/udp.h> +#include <net/gen/udp_hdr.h> +#include <net/gen/udp_io.h> + +#define MAX_SOLICITATIONS 3 /* # router solicitations. */ +#define SOLICITATION_INTERVAL 3 /* Secs between solicitate retries. */ +#define DEST_TO (10*60) /* 10 minutes */ +#define NEW_ROUTE (5*60) /* 5 minutes */ +#define DANGER (2*60) /* Nearing a advert timeout? */ +#define DEAD_TO (24L*60*60) /* 24 hours */ +#define DEAD_PREF 0x80000000L /* From RFC 1256 */ +#define MaxAdvertisementInterval (DEST_TO/2) /* Chosen to jive with RIP */ +#define AdvertisementLifetime DEST_TO +#define PRIO_OFF_DEF -1024 +#define RIP_REPLY 2 + +/* It's now or never. */ +#define IMMEDIATELY ((time_t) ((time_t) -1 < 0 ? LONG_MIN : 0)) +#define NEVER ((time_t) ((time_t) -1 < 0 ? LONG_MAX : ULONG_MAX)) + +#if !__minix_vmd +/* Standard Minix needs to choose between router discovery and RIP info. */ +int do_rdisc= 1; +int do_rip= 0; +#else +/* VMD Minix can do both at once. */ +#define do_rdisc 1 +#define do_rip 1 +#endif + +int rip_fd; /* For incoming RIP packet. */ +int irdp_fd; /* Receive or transmit IRDP packets. */ + +char *udp_device; /* UDP device to use. */ +char *ip_device; /* IP device to use. */ + +int priority_offset; /* Offset to make my routes less preferred. */ + +int bcast= 0; /* Broadcast adverts to all. */ +int debug= 0; + +char rip_buf[8192]; /* Incoming RIP packet buffer. */ +char irdp_buf[1024]; /* IRDP buffer. */ + +typedef struct routeinfo +{ + u8_t command; + u8_t version; + u16_t zero1; + struct routedata { + u16_t family; + u16_t zero2; + u32_t ip_addr; + u32_t zero3, zero4; + u32_t metric; + } data[1]; +} routeinfo_t; + +typedef struct table +{ + ipaddr_t tab_gw; + i32_t tab_pref; + time_t tab_time; +} table_t; + +table_t *table; /* Collected table of routing info. */ +size_t table_size; + +int sol_retries= MAX_SOLICITATIONS; +time_t next_sol= IMMEDIATELY; +time_t next_advert= NEVER; +time_t router_advert_valid= IMMEDIATELY; +time_t now; + +void report(const char *label) +/* irdpd: /dev/hd0: Device went up in flames */ +{ + fprintf(stderr, "irdpd: %s: %s\n", label, strerror(errno)); +} + +void fatal(const char *label) +/* irdpd: /dev/house: Taking this with it */ +{ + report(label); + exit(1); +} + +#if DEBUG +char *addr2name(ipaddr_t host) +/* Translate an IP address to a printable name. */ +{ + struct hostent *hostent; + + hostent= gethostbyaddr((char *) &host, sizeof(host), AF_INET); + return hostent == nil ? inet_ntoa(host) : hostent->h_name; +} +#else +#define addr2name(host) inet_ntoa(host) +#endif + +void print_table(void) +/* Show the collected routing table. */ +{ + int i; + table_t *ptab; + struct tm *tm; + + for (i= 0, ptab= table; i < table_size; i++, ptab++) { + if (ptab->tab_time < now - DEAD_TO) continue; + + tm= localtime(&ptab->tab_time); + printf("%-40s %6ld %02d:%02d:%02d\n", + addr2name(ptab->tab_gw), + (long) ptab->tab_pref, + tm->tm_hour, tm->tm_min, tm->tm_sec); + } +} + +void advertize(ipaddr_t host) +/* Send a router advert to a host. */ +{ + char *buf, *data; + ip_hdr_t *ip_hdr; + icmp_hdr_t *icmp_hdr; + int i; + table_t *ptab; + + buf= malloc(sizeof(*ip_hdr) + offsetof(icmp_hdr_t, ih_dun.uhd_data) + + table_size * (sizeof(ipaddr_t) + sizeof(u32_t))); + if (buf == nil) fatal("heap error"); + + ip_hdr= (ip_hdr_t *) buf; + icmp_hdr= (icmp_hdr_t *) (ip_hdr + 1); + + ip_hdr->ih_vers_ihl= 0x45; + ip_hdr->ih_dst= host; + + icmp_hdr->ih_type= ICMP_TYPE_ROUTER_ADVER; + icmp_hdr->ih_code= 0; + icmp_hdr->ih_hun.ihh_ram.iram_na= 0; + icmp_hdr->ih_hun.ihh_ram.iram_aes= 2; + icmp_hdr->ih_hun.ihh_ram.iram_lt= htons(AdvertisementLifetime); + data= (char *) icmp_hdr->ih_dun.uhd_data; + + /* Collect gateway entries from the table. */ + for (i= 0, ptab= table; i < table_size; i++, ptab++) { + if (ptab->tab_time < now - DEAD_TO) continue; + + icmp_hdr->ih_hun.ihh_ram.iram_na++; + if (ptab->tab_time < now - DEST_TO) ptab->tab_pref= DEAD_PREF; + * (ipaddr_t *) data= ptab->tab_gw; + data+= sizeof(ipaddr_t); + * (i32_t *) data= htonl(ptab->tab_pref); + data+= sizeof(i32_t); + } + icmp_hdr->ih_chksum= 0; + icmp_hdr->ih_chksum= ~oneC_sum(0, icmp_hdr, data - (char *) icmp_hdr); + + if (icmp_hdr->ih_hun.ihh_ram.iram_na > 0) { + /* Send routing info. */ + + if (debug) { + printf("Routing table send to %s:\n", addr2name(host)); + print_table(); + } + + if (write(irdp_fd, buf, data - buf) < 0) { + (errno == EIO ? fatal : report)(ip_device); + } + } + free(buf); +} + +void time_functions(void) +/* Perform time dependend functions: router solicitation, router advert. */ +{ + if (now >= next_sol) { + char buf[sizeof(ip_hdr_t) + 8]; + ip_hdr_t *ip_hdr; + icmp_hdr_t *icmp_hdr; + + if (sol_retries == 0) { + /* Stop soliciting. */ + next_sol= NEVER; +#if !__minix_vmd + /* Switch to RIP if no router responded. */ + if (table_size == 0) { + do_rip= 1; + do_rdisc= 0; + } +#endif + return; + } + + /* Broadcast a router solicitation to find a router. */ + ip_hdr= (ip_hdr_t *) buf; + icmp_hdr= (icmp_hdr_t *) (ip_hdr + 1); + + ip_hdr->ih_vers_ihl= 0x45; + ip_hdr->ih_dst= HTONL(0xFFFFFFFFL); + + icmp_hdr->ih_type= ICMP_TYPE_ROUTE_SOL; + icmp_hdr->ih_code= 0; + icmp_hdr->ih_chksum= 0; + icmp_hdr->ih_hun.ihh_unused= 0; + icmp_hdr->ih_chksum= ~oneC_sum(0, icmp_hdr, 8); + + if (debug) printf("Broadcasting router solicitation\n"); + + if (write(irdp_fd, buf, sizeof(buf)) < 0) + fatal("sending router solicitation failed"); + + /* Schedule the next packet. */ + next_sol= now + SOLICITATION_INTERVAL; + + sol_retries--; + } + + if (now >= next_advert) { + /* Advertize routes to the local host (normally), or + * broadcast them (to keep bad hosts up.) + */ + + advertize(bcast ? HTONL(0xFFFFFFFFL) : HTONL(0x7F000001L)); + next_advert= now + MaxAdvertisementInterval; +#if !__minix_vmd + /* Make sure we are listening to RIP now. */ + do_rip= 1; + do_rdisc= 0; +#endif + } +} + +void add_gateway(ipaddr_t host, i32_t pref) +/* Add a router with given address and preference to the routing table. */ +{ + table_t *oldest, *ptab; + int i; + + /* Look for the host, or select the oldest entry. */ + oldest= nil; + for (i= 0, ptab= table; i < table_size; i++, ptab++) { + if (ptab->tab_gw == host) break; + + if (oldest == nil || ptab->tab_time < oldest->tab_time) + oldest= ptab; + } + + /* Don't evict the oldest if it is still valid. */ + if (oldest != nil && oldest->tab_time >= now - DEST_TO) oldest= nil; + + /* Expand the table? */ + if (i == table_size && oldest == nil) { + table_size++; + table= realloc(table, table_size * sizeof(*table)); + if (table == nil) fatal("heap error"); + oldest= &table[table_size - 1]; + } + + if (oldest != nil) { + ptab= oldest; + ptab->tab_gw= host; + ptab->tab_pref= DEAD_PREF; + } + + /* Replace an entry if the new one looks more promising. */ + if (pref >= ptab->tab_pref || ptab->tab_time <= now - NEW_ROUTE) { + ptab->tab_pref= pref; + ptab->tab_time= now; + } +} + +void rip_incoming(ssize_t n) +/* Use a RIP packet to add to the router table. (RIP packets are really for + * between routers, but often it is the only information around.) + */ +{ + udp_io_hdr_t *udp_io_hdr; + u32_t default_dist; + i32_t pref; + routeinfo_t *routeinfo; + struct routedata *data, *end; + + /* We don't care about RIP packets when there are router adverts. */ + if (now + MaxAdvertisementInterval < router_advert_valid) return; + + udp_io_hdr= (udp_io_hdr_t *) rip_buf; + if (udp_io_hdr->uih_data_len != n - sizeof(*udp_io_hdr)) { + if (debug) printf("Bad sized route packet (discarded)\n"); + return; + } + routeinfo= (routeinfo_t *) (rip_buf + sizeof(*udp_io_hdr) + + udp_io_hdr->uih_ip_opt_len); + + if (routeinfo->command != RIP_REPLY) { + if (debug) { + printf("RIP-%d packet command %d ignored\n", + routeinfo->version, routeinfo->command); + } + return; + } + + /* Look for a default route, the route to the gateway. */ + end= (struct routedata *) (rip_buf + n); + default_dist= (u32_t) -1; + for (data= routeinfo->data; data < end; data++) { + if (ntohs(data->family) != AF_INET || data->ip_addr != 0) + continue; + default_dist= ntohl(data->metric); + if (default_dist >= 256) { + if (debug) { + printf("Strange metric %lu\n", + (unsigned long) default_dist); + } + } + } + pref= default_dist >= 256 ? 1 : 512 - default_dist; + pref+= priority_offset; + + /* Add the gateway to the table with the calculated preference. */ + add_gateway(udp_io_hdr->uih_src_addr, pref); + + if (debug) { + printf("Routing table after RIP-%d packet from %s:\n", + routeinfo->version, + addr2name(udp_io_hdr->uih_src_addr)); + print_table(); + } + + /* Start advertizing. */ + if (next_advert == NEVER) next_advert= IMMEDIATELY; +} + +void irdp_incoming(ssize_t n) +/* Look for router solicitations and router advertisements. The solicitations + * are probably from other irdpd daemons, we answer them if we do not expect + * a real router to answer. The advertisements cause this daemon to shut up. + */ +{ + ip_hdr_t *ip_hdr; + icmp_hdr_t *icmp_hdr; + int ip_hdr_len; + char *data; + int i; + int router; + ipaddr_t addr; + i32_t pref; + time_t valid; + + ip_hdr= (ip_hdr_t *) irdp_buf; + ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; + if (n < ip_hdr_len + 8) { + if (debug) printf("Bad sized ICMP (discarded)\n"); + return; + } + + icmp_hdr= (icmp_hdr_t *)(irdp_buf + ip_hdr_len); + + /* Did I send this myself? */ + if (ip_hdr->ih_src == ip_hdr->ih_dst) return; + if ((htonl(ip_hdr->ih_src) & 0xFF000000L) == 0x7F000000L) return; + + if (icmp_hdr->ih_type != ICMP_TYPE_ROUTER_ADVER) return; + + /* Incoming router advertisement, the kind of packet the TCP/IP task + * is very happy with. No need to solicit further. + */ + sol_retries= 0; + + /* Add router info to our table. Also see if the packet really came + * from a router. If so then we can go dormant for the lifetime of + * the ICMP. + */ + router= 0; + data= (char *) icmp_hdr->ih_dun.uhd_data; + for (i= 0; i < icmp_hdr->ih_hun.ihh_ram.iram_na; i++) { + addr= * (ipaddr_t *) data; + data+= sizeof(ipaddr_t); + pref= htonl(* (i32_t *) data); + data+= sizeof(i32_t); + + if (addr == ip_hdr->ih_src) { + /* The sender is in the routing table! */ + router= 1; + } + add_gateway(addr, pref); + } + + valid= now + ntohs(icmp_hdr->ih_hun.ihh_ram.iram_lt); + if (router) router_advert_valid= valid; + + /* Restart advertizing close to the timeout of the advert. (No more + * irdpd adverts if the router stays alive.) + */ + if (router || next_advert > valid - DANGER) + next_advert= valid - DANGER; + + if (debug) { + printf("Routing table after advert received from %s:\n", + addr2name(ip_hdr->ih_src)); + print_table(); + if (router) { + struct tm *tm= localtime(&router_advert_valid); + printf( + "This router advert is valid until %02d:%02d:%02d\n", + tm->tm_hour, tm->tm_min, tm->tm_sec); + } + } +} + +void sig_handler(int sig) +/* A signal changes the debug level. */ +{ + switch (sig) { + case SIGUSR1: debug++; break; + case SIGUSR2: debug= 0; break; + } +} + +void usage(void) +{ + fprintf(stderr, +"Usage: irdpd [-bd] [-U udp-device] [-I ip-device] [-o priority-offset]\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + int i; + struct servent *service; + udpport_t route_port; + nwio_udpopt_t udpopt; + nwio_ipopt_t ipopt; + asynchio_t asyn; + time_t timeout; + struct timeval tv; + struct sigaction sa; + char *offset_arg, *offset_end; + long arg; + + udp_device= ip_device= nil; + offset_arg= nil; + + for (i = 1; i < argc && argv[i][0] == '-'; i++) { + char *p= argv[i] + 1; + + if (p[0] == '-' && p[1] == 0) { i++; break; } + + while (*p != 0) { + switch (*p++) { + case 'U': + if (udp_device != nil) usage(); + if (*p == 0) { + if (++i == argc) usage(); + p= argv[i]; + } + udp_device= p; + p= ""; + break; + case 'I': + if (ip_device != nil) usage(); + if (*p == 0) { + if (++i == argc) usage(); + p= argv[i]; + } + ip_device= p; + p= ""; + break; + case 'o': + if (offset_arg != nil) usage(); + if (*p == 0) { + if (++i == argc) usage(); + p= argv[i]; + } + offset_arg= p; + p= ""; + break; + case 'b': + bcast= 1; + break; + case 's': + /*obsolete*/ + break; + case 'd': + debug= 1; + break; + default: + usage(); + } + } + } + if (i != argc) usage(); + + /* Debug level signals. */ + sa.sa_handler= sig_handler; + sigemptyset(&sa.sa_mask); + sa.sa_flags= 0; + sigaction(SIGUSR1, &sa, nil); + sigaction(SIGUSR2, &sa, nil); + + if (udp_device == nil && (udp_device= getenv("UDP_DEVICE")) == nil) + udp_device= UDP_DEVICE; + + if (ip_device == nil && (ip_device= getenv("IP_DEVICE")) == nil) + ip_device= IP_DEVICE; + + if (offset_arg == nil) { + priority_offset= PRIO_OFF_DEF; + } else { + arg= strtol(offset_arg, &offset_end, 0); + if (*offset_end != 0 || (priority_offset= arg) != arg) usage(); + } + + if ((service= getservbyname("route", "udp")) == nil) { + fprintf(stderr, + "irdpd: unable to look up the port number for the 'route' service\n"); + exit(1); + } + + route_port= (udpport_t) service->s_port; + + if ((rip_fd= open(udp_device, O_RDWR)) < 0) fatal(udp_device); + + udpopt.nwuo_flags= NWUO_COPY | NWUO_LP_SET | NWUO_DI_LOC + | NWUO_EN_BROAD | NWUO_RP_SET | NWUO_RA_ANY | NWUO_RWDATALL + | NWUO_DI_IPOPT; + udpopt.nwuo_locport= route_port; + udpopt.nwuo_remport= route_port; + if (ioctl(rip_fd, NWIOSUDPOPT, &udpopt) < 0) + fatal("setting UDP options failed"); + + if ((irdp_fd= open(ip_device, O_RDWR)) < 0) fatal(ip_device); + + ipopt.nwio_flags= NWIO_COPY | NWIO_EN_LOC | NWIO_EN_BROAD + | NWIO_REMANY | NWIO_PROTOSPEC + | NWIO_HDR_O_SPEC | NWIO_RWDATALL; + ipopt.nwio_tos= 0; + ipopt.nwio_ttl= 1; + ipopt.nwio_df= 0; + ipopt.nwio_hdropt.iho_opt_siz= 0; + ipopt.nwio_rem= HTONL(0xFFFFFFFFL); + ipopt.nwio_proto= IPPROTO_ICMP; + + if (ioctl(irdp_fd, NWIOSIPOPT, &ipopt) < 0) + fatal("can't configure ICMP channel"); + + asyn_init(&asyn); + + while (1) { + ssize_t r; + + if (do_rip) { + /* Try a RIP read. */ + r= asyn_read(&asyn, rip_fd, rip_buf, sizeof(rip_buf)); + if (r < 0) { + if (errno == EIO) fatal(udp_device); + if (errno != EINPROGRESS) report(udp_device); + } else { + now= time(nil); + rip_incoming(r); + } + } + + if (do_rdisc) { + /* Try an IRDP read. */ + r= asyn_read(&asyn, irdp_fd, irdp_buf, + sizeof(irdp_buf)); + if (r < 0) { + if (errno == EIO) fatal(ip_device); + if (errno != EINPROGRESS) report(ip_device); + } else { + now= time(nil); + irdp_incoming(r); + } + } + fflush(stdout); + + /* Compute the next wakeup call. */ + timeout= next_sol < next_advert ? next_sol : next_advert; + + /* Wait for a RIP or IRDP packet or a timeout. */ + tv.tv_sec= timeout; + tv.tv_usec= 0; + if (asyn_wait(&asyn, 0, timeout == NEVER ? nil : &tv) < 0) { + /* Timeout? */ + if (errno != EINTR && errno != EAGAIN) + fatal("asyn_wait()"); + now= time(nil); + time_functions(); + } + } +} diff --git a/commands/simple/isoread.c b/commands/simple/isoread.c new file mode 100755 index 000000000..e9e87a3b7 --- /dev/null +++ b/commands/simple/isoread.c @@ -0,0 +1,860 @@ +/* + * isoread.c + * + * isoread reads a file system in ISO9660 or HIGH SIERRA format from + * a given device. + * + * Apr 5 1995 Michel R. Prevenier + * Nov 16 1996 Kees J. Bot -- bug fix: isoread filename matching + * Dec 7 1997 Albert S. Woodhull -- bug fix: return values + * " " : isodir filename handling + * -- added : isoread -a option + * Mar 21 2000 Michael A. Temari -- bug fix: look_up only searched first + * : block of directory + * : stack overflow in recurse_dir + * : and various other bugs + * Apr 14 2002 Michael A. Temari -- bug fix: fixed recursing directories + * : and printing dates 2000 and + * : later + * May 14 2002 Kees J. Bot -- bug fix: fixed error messages + * Mar 14 2003 Kees J. Bot -- added : iso{dir,read} -B option + * Jul 24 2003 Michael A. Temari -- bug fix: bytes to blocks roundup fix + */ + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <sys/times.h> +#include <unistd.h> + +/* + * definitions used by the ISO9660 and HIGH SIERRA file system + */ + +#define ISO9660_ID "CD001" +#define HIGH_SIERRA_ID "CDROM" +#define BLOCK_SIZE 2048 +#define BLOCK_SHIFT 11 + + +/* Fields in a ISO9660 volume descriptor */ +struct iso9660_descriptor +{ + char type[1]; + char id[5]; + char version[1]; + char reserved1[1]; + char system_id[32]; + char volume_id[32]; + char reserved2[8]; + char volume_size[8]; + char reserved3[32]; + char volume_set_size[4]; + char volume_seq_nr[4]; + char block_size[4]; + char path_table_size[8]; + char type_l_path_table[4]; + char opt_type_l_path_table[4]; + char type_m_path_table[4]; + char opt_type_m_path_table[4]; + char root_dir_entry[34]; + char vol_set_id[128]; + char publ_id[128]; + char prep_id[128]; + char appl_id[128]; + char copyright_file_id[37]; + char abstract_file_id[37]; + char bibl_file_id[37]; + char creation_date[17]; + char mod_date[17]; + char exp_date[17]; + char eff_date[17]; + char file_struc_version[1]; + char reserved4[1]; + char appl_data[512]; + char reserved5[653]; +}; + + +/* Fields in a High Sierra volume descriptor */ +struct high_sierra_descriptor +{ + char reserved1[8]; + char type[1]; + char id[5]; + char version[1]; + char reserved2[1]; + char system_id[32]; + char volume_id[32]; + char reserved3[8]; + char volume_size[8]; + char reserved4[32]; + char vol_set_size[4]; + char volume_seq_nr[4]; + char block_size[4]; + char path_table_size[8]; + char type_l_path_table[4]; + char reserved5[28]; + char root_dir_entry[34]; +}; + + +/* Fields in a directory entry */ +struct dir_entry +{ + char length[1]; + char ext_attr_length[1]; + char first_block[8]; + char size[8]; + char date[7]; + char flags[1]; + char file_unit_size[1]; + char interleave[1]; + char volume_seq_nr[4]; + char name_length[1]; + char name[1]; +}; + + +#define STDOUT stdout +#define STDERR stderr +#define NULL_DIR (struct dir_entry *) 0 +#define MAX_NAME_LENGTH 255 +#define MAX_PATH_LENGTH 1024 + +#define NR_OF_CHARS 13 +#define NR_OF_BLANKS 2 +#define NR_OF_COLS (80 / (NR_OF_CHARS + NR_OF_BLANKS)) + +/* This macro always returns a lower case character */ +#define LOWER_CASE(CHR) (CHR >= 'A' && CHR <= 'Z' ? CHR | 0x20 : CHR) + +/* Macro's for determining . , .. and normal directory entries */ +#define IS_DOT(PTR) (PTR->name_length[0] == 1 && PTR->name[0] == 0 ? 1 : 0) +#define IS_DOT_DOT(PTR) (PTR->name_length[0] == 1 && PTR->name[0] == 1 ? 1 : 0) +#define IS_DIR(PTR) (PTR->flags[-High_Sierra] & 2 ? 1 : 0) + + +_PROTOTYPE (int main, (int argc, char **argv)); +_PROTOTYPE (int iso_cmp, (char *name, struct dir_entry *dir_ptr, int dir_flag)); +_PROTOTYPE (void list_dir, (struct dir_entry *dir_ptr)); +_PROTOTYPE (void list_file, (struct dir_entry *dir_ptr)); +_PROTOTYPE (struct dir_entry *look_up, (char *name)); +_PROTOTYPE (void recurse_dir, (char *path, struct dir_entry *dir_ptr)); +_PROTOTYPE (void read_device, (long offset, int nr_of_bytes, char *buffer)); +_PROTOTYPE (int valid_fs, (void) ); +_PROTOTYPE (void usage, (void) ); +_PROTOTYPE (void print_date, (char *date)); +_PROTOTYPE (void print_dir_date, (char *date)); +_PROTOTYPE (void iso_info, (struct iso9660_descriptor *vol_desc)); +_PROTOTYPE (void hs_info, (struct high_sierra_descriptor *vol_desc)); +_PROTOTYPE (int iso_711, (char *c)); +_PROTOTYPE (int iso_712, (char *c)); +_PROTOTYPE (int iso_721, (char *c)); +_PROTOTYPE (int iso_722, (char *c)); +_PROTOTYPE (int iso_723, (char *c)); +_PROTOTYPE (long iso_731, (char *c)); +_PROTOTYPE (long iso_732, (char *c)); +_PROTOTYPE (long iso_733, (char *c)); + + +char Buffer[BLOCK_SIZE]; /* buffer to hold read data */ +int Device; /* global file descriptor */ +struct iso9660_descriptor *Iso_Vol_Desc; /* iso9660 volume descriptor */ +struct high_sierra_descriptor *Hs_Vol_Desc; /* high sierra volume descriptor */ +int High_Sierra = 0; /* 1 = high sierra format */ +int Iso9660 = 0; /* 1 = iso9660 format */ + +/* This comes in handy when printing the date */ +char months[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; + +/* Flags displaying what to do */ +int Read_File = 0; /* 1 = Read file */ +int Read_Dir = 0; /* 1 = Read directory entry */ +int Read_Info = 0; /* 1 = Read volume descriptor */ +int Recurse = 0; /* 1 = Recursively descend directories */ +int Verbose = 0; /* 1 = Print all info on directories */ +int ByteOffset = 0; /* 1 = Print byte offset and length of files */ +int Aflag = 0; /* 1 = Suppress output of \r */ + +int iso_cmp(name, dir_ptr, dir_flag) +char *name; +struct dir_entry *dir_ptr; +int dir_flag; +{ +/* Compare name with directory entries, looking for match with a dirname. + * An iso9660 filename is terminated by ";n", where n will probably + * be 1. A directory name is not terminated by anything special, it may be + * followed by a \0 if padding is needed to put the following directory + * entry on an even address. + */ + int i; + int len; + + /* First match the filename */ + len = strlen(name); + if (len > iso_711(dir_ptr->name_length)) return 1; + for (i = 0; i < len; i++) + { + if (dir_ptr->name[i] == ';') return 1; /* found end of a filename */ + if (name[i] != LOWER_CASE(dir_ptr->name[i])) return 1; /* match failed */ + } + if (dir_ptr->name[i] != ';' && i != len) return 1; /* incomplete match */ + + /* The filename is ok, now look at the file type */ + if (dir_flag && !IS_DIR(dir_ptr)) return 1; /* File type not correct */ + + return 0; +} + + +void usage() +{ + if (Read_Dir) + fprintf (STDERR, "Usage: isodir [-lrB] inputfile [dir]\n"); + else if (Read_Info) + fprintf (STDERR, "Usage: isoinfo inputfile\n"); + else + fprintf (STDERR, "Usage: isoread [-a] inputfile file\n"); + exit(1); +} + + +int main(argc, argv) +int argc; +char **argv; +{ + struct dir_entry *entry; + char path[MAX_PATH_LENGTH]; + char *input_file; + char *basename; + char *file_name; + int i,j; + + /* Read arguments */ + basename = argv[0]; + while (*argv[0] != '\0') + if (*argv[0]++ == '/') basename = argv[0]; + + if (strcmp(basename,"isodir") == 0) Read_Dir = 1; + else if (strcmp(basename,"isoinfo") == 0) Read_Info = 1; + else Read_File = 1; + + if ((argc > 5 && Read_Dir) || (argc != 2 && Read_Info) || + (argc > 4 && Read_File)) usage(); + + i = 1; + + while (i < argc && argv[i][0] == '-') + { + char *opt = argv[i++] + 1; + + if (opt[0] == '-' && opt[1] == '\0') break; + + while (*opt != '\0') + { + if (Read_Info) usage(); + if (Read_Dir) + switch (*opt++) + { + case 'r': Recurse = 1; break; + case 'l': Verbose = 1; break; + case 'B': ByteOffset = 1; break; + default: usage(); + } + if (Read_File) + switch (*opt++) + { + case 'a': Aflag = 1; break; + case 'B': ByteOffset = 1; break; + default: usage(); + } + } + } + + if (i >= argc) usage(); + input_file = argv[i++]; + + if (Read_File) + { + if (i >= argc) usage(); + file_name = argv[i++]; + } + + if (Read_Dir) + { + file_name = "/"; + if (i < argc) + { + file_name = argv[i++]; + } + } + + if (i < argc) usage(); + + if (Read_File || Read_Dir) + { + for (i=0; file_name[i] != '\0'; i++) + path[i] = LOWER_CASE(file_name[i]); + path[i] = '\0'; + } + + /* Open file system (file or device) */ + if ((Device = open(input_file, O_RDONLY)) < 0) + { + fprintf (STDERR, "cannot open %s: %s\n", input_file, strerror(errno)); + exit(-1); + } + + + if (!valid_fs()) + { + fprintf (STDERR, "File system not in ISO9660 or HIGH SIERRA format \n"); + exit(-1); + } + + + if (Read_Info) + { + if (Iso9660) + iso_info(Iso_Vol_Desc); + else + hs_info(Hs_Vol_Desc); + exit(0); + } + + /* Lookup file */ + if ((entry = look_up(path)) != NULL_DIR) + { + if (Read_Dir) + if (Recurse) recurse_dir(path,entry); + else list_dir(entry); + else + list_file(entry); + } + else + { + if (Read_Dir) + fprintf (STDERR, "Directory"); + else + fprintf (STDERR, "File"); + fprintf (STDERR, " %s not found\n", path); + exit(-1); + } + return 0; +} + + +struct dir_entry *look_up(path) +char *path; +{ + /* Lookup a file name */ + + struct dir_entry *dir_ptr; + long block; + int nr_of_blocks; + int offset; + char name[MAX_NAME_LENGTH + 1]; + int name_index = 0; + int last_in_path = 0; + int found; + int i,j; + + /* Get the right dir entry structure */ + if (Iso9660) + dir_ptr = (struct dir_entry *) Iso_Vol_Desc->root_dir_entry; + else + dir_ptr = (struct dir_entry *) Hs_Vol_Desc->root_dir_entry; + + /* If we look for the root we already have the right entry */ + if (path[0] == '/') + if (strlen(path) == 1) return dir_ptr; + else name_index = 1; /* first name in path */ + + /* Keep searching for the path elements until all are found */ + while (!last_in_path) + { + /* Get next name in path */ + for (i = name_index; i < strlen(path); i++) + { + if (path[i] == '/') break; + name[i - name_index] = path[i]; + } + last_in_path = + (i == strlen(path) || (i == strlen(path) - 1 && path[i] == '/')); + name[i-name_index] = '\0'; + name_index = i + 1; + + /* Get block of next directory */ + block = iso_733(dir_ptr->first_block) + iso_711(dir_ptr->ext_attr_length); + nr_of_blocks = (iso_733(dir_ptr->size) + (BLOCK_SIZE-1)) >> BLOCK_SHIFT; + + /* Search for file in dir entry */ + found = 0; + for (j=0; j < nr_of_blocks && !found; j++) + { + /* Read a directory block */ + read_device(block*BLOCK_SIZE, BLOCK_SIZE, Buffer); + block++; + + dir_ptr = (struct dir_entry *) Buffer; + + offset = 0; + /* Compare with all entries in this block */ + while (iso_711(dir_ptr->length) > 0 && offset < BLOCK_SIZE) + { + if (iso_cmp(name, dir_ptr, + (Read_Dir || (!Read_Dir && !last_in_path))) == 0) + { + found = 1; + break; + } + /* Next entry */ + offset += iso_711(dir_ptr->length); + dir_ptr = (struct dir_entry *) (Buffer + offset); + } + } + if (!found) return NULL_DIR; /* path element not found */ + } + return dir_ptr; +} + + +void recurse_dir(path, dir_ptr) +char *path; +struct dir_entry *dir_ptr; +{ + /* Recursively descend all directories starting with dir_ptr */ + + char tmp_path[MAX_PATH_LENGTH]; + int i,j, path_length; + long block, saveblock, dblock; + int nr_of_blocks; + int offset = 0; + + + /* Save block number and nr of blocks of current dir entry because + * list_dir changes dir_ptr + */ + block = iso_733(dir_ptr->first_block) + iso_711(dir_ptr->ext_attr_length); + nr_of_blocks = (iso_733(dir_ptr->size) + (BLOCK_SIZE-1)) >> BLOCK_SHIFT; + + /* Add a trailing / to path if necessary */ + path_length = strlen(path); + if (path[path_length-1] != '/') + { + path[path_length++] = '/'; + path[path_length] = '\0'; + } + + /* Print current path of directory, and list contents of directory */ + fprintf(STDOUT,"directory %s:\n\n", path); + list_dir(dir_ptr); + fprintf(STDOUT,"\n\n"); + + for (j=0; j < nr_of_blocks; j++) + { + read_device(block*BLOCK_SIZE, BLOCK_SIZE, Buffer); + saveblock = block++; + + /* Save buffer, because the next recursive call destroys + * the global Buffer + */ + dir_ptr = (struct dir_entry *) Buffer; + + /* Search this dir entry for directories */ + offset = 0; + while (iso_711(dir_ptr->length) != 0 && offset < BLOCK_SIZE) + { + /* Is current file a directory and not the . or .. entries */ + if (IS_DIR(dir_ptr) && !IS_DOT(dir_ptr) && !IS_DOT_DOT(dir_ptr)) + { + /* setup path for next recursive call */ + for (i=0; i<path_length; i++) tmp_path[i] = path[i]; + for (i=0;i<iso_711(dir_ptr->name_length) && dir_ptr->name[i] != ';';i++) + tmp_path[i+path_length] = LOWER_CASE(dir_ptr->name[i]); + tmp_path[i+path_length] = '/'; + tmp_path[i+1+path_length] = '\0'; + + /* Read block of directory we found */ + dblock = iso_733(dir_ptr->first_block); + read_device(dblock*BLOCK_SIZE, BLOCK_SIZE, Buffer); + + /* And start all over again with this entry */ + recurse_dir(tmp_path, (struct dir_entry *) Buffer); + + /* get the block we were looking at */ + read_device(saveblock*BLOCK_SIZE, BLOCK_SIZE, Buffer); + } + + /* Go to the next file in this directory */ + offset += iso_711(dir_ptr->length); + dir_ptr = (struct dir_entry *) (Buffer + offset); + } + } +} + + +void list_dir(dir_ptr) +struct dir_entry *dir_ptr; +{ + /* List all entries in a directory */ + + long block; + int nr_of_blocks; + int i,j; + int offset = 0; + char name[NR_OF_CHARS+NR_OF_BLANKS+1]; + int name_len; + int column = 0; + int skip = 0; + + /* Get first block of directory */ + block = iso_733(dir_ptr->first_block) + iso_711(dir_ptr->ext_attr_length); + nr_of_blocks = (iso_733(dir_ptr->size) + (BLOCK_SIZE-1)) >> BLOCK_SHIFT; + + /* Read all directory blocks and display their contents */ + for (j=0; j < nr_of_blocks; j++) + { + read_device(block*BLOCK_SIZE, BLOCK_SIZE, Buffer); + block++; + + dir_ptr = (struct dir_entry *) (Buffer); + offset = 0; + while (iso_711(dir_ptr->length) != 0 && offset < BLOCK_SIZE) + { + name_len = 0; + if (IS_DOT(dir_ptr)) + { + name[name_len++] = '.'; + if (!Verbose) skip = 1; + } + else + { + if (IS_DOT_DOT(dir_ptr)) + { + name[name_len++] = '.'; + name[name_len++] = '.'; + if (!Verbose) skip = 1; + } + else + { + for (i=0; i<iso_711(dir_ptr->name_length) && + i<NR_OF_CHARS; i++) + { + if (dir_ptr->name[i] == ';') break; + name[name_len++] = LOWER_CASE(dir_ptr->name[i]); + } + if (IS_DIR(dir_ptr)) name[name_len++] = '/'; + } + } + if (!skip) + { + if (ByteOffset) + { + fprintf (STDOUT, "%10ld ", + (iso_733(dir_ptr->first_block) + iso_711(dir_ptr->ext_attr_length)) + * BLOCK_SIZE); + } + if (Verbose || ByteOffset) + { + fprintf (STDOUT, "%10ld ", iso_733(dir_ptr->size)); + } + if (Verbose) + { + print_dir_date(dir_ptr->date); + fprintf (STDOUT, " "); + } + for(i=name_len; i<(NR_OF_CHARS+NR_OF_BLANKS); i++) name[i] = ' '; + name[NR_OF_CHARS+NR_OF_BLANKS] = '\0'; + fprintf(STDOUT, "%s", name); + if (!(Verbose || ByteOffset)) + { + column++; + if (column >= NR_OF_COLS) + { + column = 0; + fprintf(STDOUT,"\n"); + } + } + else fprintf(STDOUT,"\n"); + } + skip = 0; + offset += iso_711(dir_ptr->length); + dir_ptr = (struct dir_entry *) (Buffer+offset); + } + } + if (!Verbose && column) fprintf(STDOUT,"\n"); +} + + +void print_dir_date(date) +char *date; +{ + /* Print date in a directory entry */ + + int m; + + m = iso_711(&date[1]) - 1; + if(m < 0 || m > 11) + fprintf(STDOUT, " "); + else + fprintf(STDOUT,"%.3s",&months[m*3]); + + fprintf (STDOUT, " %02d %04d %02d:%02d:%02d", + date[2], + 1900+date[0], + date[3], + date[4], + date[5]); +} + + +void list_file(dir_ptr) +struct dir_entry *dir_ptr; +{ + /* List contents of a file */ + + int i; + long block; + long size; + char c; + + block = iso_733(dir_ptr->first_block); + size = iso_733(dir_ptr->size); + + if (ByteOffset) { + fprintf(STDOUT, "%ld %ld\n", block*BLOCK_SIZE, size); + return; + } + + while (size > 0) + if (Aflag == 1) { + read_device(block*BLOCK_SIZE, BLOCK_SIZE, Buffer); + for (i=0; ((i < size) && (i < BLOCK_SIZE)); i++) + if (Buffer[i] != '\r') fprintf(STDOUT, "%c", Buffer[i]); + size-= BLOCK_SIZE; + block++; + } else { + read_device(block*BLOCK_SIZE, BLOCK_SIZE, Buffer); + for (i=0; ((i < size) && (i < BLOCK_SIZE)); i++) + fprintf(STDOUT, "%c", Buffer[i]); + size-= BLOCK_SIZE; + block++; + } +} + + +void print_date(date) +char *date; +{ + /* Print the date in a volume descriptor */ + + fprintf (STDOUT, "%c%c-%c%c-%c%c%c%c %c%c:%c%c:%c%c", + date[4], + date[5], + date[6], + date[7], + date[0], + date[1], + date[2], + date[3], + date[8], + date[9], + date[10], + date[11], + date[12], + date[13]); +} + +void iso_info(vol_desc) +struct iso9660_descriptor *vol_desc; +{ + int i; + + fprintf (STDOUT, "Format: ISO9660 \n"); + fprintf (STDOUT, "System id: "); + for (i=0; i< sizeof(vol_desc->system_id); i++) + fprintf(STDOUT, "%c", vol_desc->system_id[i]); + fprintf (STDOUT, "\n"); + fprintf (STDOUT, "Volume id: "); + for (i=0; i< sizeof(vol_desc->volume_id); i++) + fprintf(STDOUT, "%c", vol_desc->volume_id[i]); + fprintf (STDOUT, "\n"); + fprintf (STDOUT, "Volume size: %ld Kb\n", iso_733(vol_desc->volume_size)*2); + fprintf (STDOUT, "Block size: %d bytes \n", iso_723(vol_desc->block_size)); + fprintf (STDOUT, "Creation date: "); + print_date(vol_desc->creation_date); + fprintf(STDOUT, "\n"); + fprintf (STDOUT, "Modification date: "); + print_date(vol_desc->mod_date); + fprintf (STDOUT, "\n"); + fprintf (STDOUT, "Expiration date: "); + print_date(vol_desc->exp_date); + fprintf (STDOUT, "\n"); + fprintf (STDOUT, "Effective date: "); + print_date(vol_desc->eff_date); + fprintf (STDOUT, "\n"); +} + + +void hs_info(vol_desc) +struct high_sierra_descriptor *vol_desc; +{ + int i; + + fprintf (STDOUT, "Format: HIGH SIERRA \n"); + fprintf (STDOUT, "System id: "); + for (i=0; i< sizeof(vol_desc->system_id); i++) + fprintf(STDOUT, "%c", vol_desc->system_id[i]); + fprintf (STDOUT, "\n"); + fprintf (STDOUT, "Volume id: "); + for (i=0; i< sizeof(vol_desc->volume_id); i++) + fprintf(STDOUT, "%c", vol_desc->volume_id[i]); + fprintf (STDOUT, "\n"); + fprintf (STDOUT, "Volume size: %ld Kb\n", (iso_733(vol_desc->volume_size)*2)); + fprintf (STDOUT, "Block size: %d bytes \n", iso_723(vol_desc->block_size)); +} + + +int valid_fs() +{ + + int i; + + /* search for a volume descriptor */ + for (i=16; i<100; i++) + { + + read_device((long)(i)*BLOCK_SIZE, BLOCK_SIZE, Buffer); + + Iso_Vol_Desc = (struct iso9660_descriptor *) Buffer; + Hs_Vol_Desc = (struct high_sierra_descriptor *) Buffer; + + if (strncmp(Iso_Vol_Desc->id, ISO9660_ID, sizeof Iso_Vol_Desc->id) == 0) + { + /* iso_info(Iso_Vol_Desc); */ + Iso9660 = 1; + break; + } + + if (strncmp(Hs_Vol_Desc->id, HIGH_SIERRA_ID, sizeof Hs_Vol_Desc->id) == 0) + { + /* hs_info(Hs_Vol_Desc); */ + High_Sierra = 1; + break; + } + } + + if (i >= 100) return 0; + return 1; +} + + +void read_device(offset, nr_of_bytes, buffer) +long offset; +int nr_of_bytes; +char *buffer; +{ + int bytes_read; + + if (lseek(Device, offset, SEEK_SET) == -1) + { + fflush (stdout); + fprintf (STDERR, "seek error: %s\n", strerror(errno)); + exit(1); + } + + bytes_read = read(Device, buffer, nr_of_bytes); + if (bytes_read != nr_of_bytes) + { + fprintf (STDERR, "read error: %s\n", + bytes_read >= 0 ? "Short read" : strerror(errno)); + exit (1); + } +} + + +/* The ISO9660 functions */ + +int iso_711 (c) +char *c; +{ + return (*c & 0xff); +} + + +int iso_712 (c) +char *c; +{ + int n; + + n = *c; + if (n & 0x80) n |= 0xffffff00; + return n; +} + +int iso_721 (c) +char *c; +{ + return ((c[0] & 0xff) | ((c[1] & 0xff) << 8)); +} + +int iso_722 (c) +char *c; +{ + return (((c[0] & 0xff) << 8) | (c[1] & 0xff)); +} + +int iso_723 (c) +char *c; +{ + if (c[0] != c[3] || c[1] != c[2]) + { + fprintf (STDERR, "Invalid ISO 7.2.3 number\n"); + exit (1); + } + return (iso_721 (c)); +} + +long iso_731 (c) +char *c; +{ + return ((long)(c[0] & 0xff) + | ((long)(c[1] & 0xff) << 8) + | ((long)(c[2] & 0xff) << 16) + | ((long)(c[3] & 0xff) << 24)); +} + + +long iso_732 (c) +char *c; +{ + return (((long)(c[0] & 0xff) << 24) + | (((long)c[1] & 0xff) << 16) + | (((long)c[2] & 0xff) << 8) + | ((long)c[3] & 0xff)); +} + +long iso_733 (c) +char *c; +{ +int i; + + for (i = 0; i < 4; i++) + { + if (c[i] != c[7-i]) + { + fprintf (STDERR, "Invalid ISO 7.3.3 number\n"); + exit (1); + } + } + return (iso_731(c)); +} diff --git a/commands/simple/join.c b/commands/simple/join.c new file mode 100755 index 000000000..0efbefc0e --- /dev/null +++ b/commands/simple/join.c @@ -0,0 +1,360 @@ +/* join - relation data base operator Author: Saeko Hirabayashi */ + +/* Written by Saeko Hirabayashi, 1989. + * 1992-01-28 Modified by Kouichi Hirabayashi to add some POSIX1003.2 options. + * + * This a free program. + */ + +#include <string.h> +#include <stdio.h> + +#define MAXFLD 200 /* maximum # of fields to accept */ + +_PROTOTYPE(void main, (int argc, char **argv)); +_PROTOTYPE(void error, (char *s, char *t)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(void match, (void)); +_PROTOTYPE(void f1_only, (void)); +_PROTOTYPE(void f2_only, (void)); +_PROTOTYPE(void output, (int flag)); +_PROTOTYPE(void outfld, (int file)); +_PROTOTYPE(void outputf, (int flag)); +_PROTOTYPE(int compare, (void)); +_PROTOTYPE(int get1, (void)); +_PROTOTYPE(int get2, (int back)); +_PROTOTYPE(int getrec, (int file)); +_PROTOTYPE(int split, (int file)); +_PROTOTYPE(int atoi, (char *str)); +_PROTOTYPE(int exit, (int val)); +_PROTOTYPE(FILE * efopen, (char *file, char *mode)); +_PROTOTYPE(void (*outfun), (int file)); /* output func: output() or outputf()*/ + +#define F1 1 +#define F2 2 +#define SEP (sep ? sep : ' ') + +FILE *fp[2]; /* file pointer for file1 and file2 */ +long head; /* head of the current (same)key group of the + * file2 */ + +char buf[2][BUFSIZ]; /* input buffer for file1 and file2 */ +char *fld[2][MAXFLD]; /* field vector for file1 and file2 */ +int nfld[2]; /* # of fields for file1 and file2 */ + +int kpos[2]; /* key field position for file1 and file2 + * (from 0) */ +char oldkey[BUFSIZ]; /* previous key of the file1 */ + +struct { /* output list by -o option */ + int o_file; /* file #: 0 or 1 */ + int o_field; /* field #: 0, 1, 2, .. */ +} olist[MAXFLD]; +int nout; /* # of output filed */ + +int aflag; /* n for '-an': F1 or F2 or both */ +int vflag; /* n for '-vn': F1 or F2 or both */ +char *es; /* s for '-e s' */ +char sep; /* c for -tc: filed separator */ +char *cmd; /* name of this program */ + +void main(argc, argv) +int argc; +char **argv; +{ + register char *s; + int c, i, j; + + cmd = argv[0]; + outfun = output; /* default output form */ + + while (--argc > 0 && (*++argv)[0] == '-' && (*argv)[1]) { + /* "-" is a file name (stdin) */ + s = argv[0] + 1; + if ((c = *s) == '-' && !s[1]) { + ++argv; + --argc; + break; /* -- */ + } + if (*++s == '\0') { + s = *++argv; + --argc; + } + switch (c) { + case 'a': /* add unpairable line to output */ + vflag = 0; + switch (*s) { + case '1': aflag |= F1; break; + case '2': aflag |= F2; break; + default: aflag |= (F1 | F2); break; + } + break; + + case 'e': /* replace empty field by es */ + es = s; + break; + + case 'j': /* key field (obsolute) */ + c = *s++; + if (*s == '\0') { + s = *++argv; + --argc; + } + + case '1': /* key field of file1 */ + case '2': /* key field of file2 */ + i = atoi(s) - 1; + + switch (c) { + case '1': kpos[0] = i; break; + case '2': kpos[1] = i; break; + default: kpos[0] = kpos[1] = i; + break; + } + break; + + case 'o': /* specify output format */ + do { + i = j = 0; + sscanf(s, "%d.%d", &i, &j); + if (i < 1 || j < 1 || i > 2) usage(); + olist[nout].o_file = i - 1; + olist[nout].o_field = j - 1; + nout++; + if ((s = strchr(s, ',')) != (char *) 0) + s++; + else { + s = *++argv; + --argc; + } + } while (argc > 2 && *s != '-'); + ++argc; + --argv; /* compensation */ + outfun = outputf; + break; + + case 't': /* tab char */ + sep = *s; + break; + + case 'v': /* output unpairable line only */ + aflag = 0; + switch (*s) { + case '1': vflag |= F1; break; + case '2': vflag |= F2; break; + default: vflag |= (F1 | F2); break; + } + break; + + default: usage(); + } + } + if (argc != 2) usage(); + + fp[0] = strcmp(argv[0], "-") ? efopen(argv[0], "r") : stdin; + fp[1] = efopen(argv[1], "r"); + + nfld[0] = get1(); /* read file1 */ + nfld[1] = get2(0); /* read file2 */ + + while (nfld[0] || nfld[1]) { + if ((i = compare()) == 0) + match(); + else if (i < 0) + f1_only(); + else + f2_only(); + } + fflush(stdout); + + exit(0); +} + +void usage() +{ + fprintf(stderr, + "Usage: %s [-an|-vn] [-e str] [-o list] [-tc] [-1 f] [-2 f] file1 file2\n", + cmd); + exit(1); +} + +int compare() +{ /* compare key field */ + register int r; + + if (nfld[1] == 0) /* file2 EOF */ + r = -1; + else if (nfld[0] == 0) /* file1 EOF */ + r = 1; + else { + if (nfld[0] <= kpos[0]) + error("missing key field in file1", (char *) 0); + if (nfld[1] <= kpos[1]) + error("missing key field in file2", (char *) 0); + + r = strcmp(fld[0][kpos[0]], fld[1][kpos[1]]); + } + return r; +} + +void match() +{ + long p; + + if (!vflag) (*outfun) (F1 | F2); + + p = ftell(fp[1]); + nfld[1] = get2(0); /* check key order */ + if (nfld[1] == 0 || strcmp(fld[0][kpos[0]], fld[1][kpos[1]])) { + nfld[0] = get1(); + if (strcmp(fld[0][kpos[0]], oldkey) == 0) { + fseek(fp[1], head, 0); /* re-do from head */ + nfld[1] = get2(1); /* don't check key order */ + } else + head = p; /* mark here */ + } +} + +void f1_only() +{ + if ((aflag & F1) || (vflag & F1)) (*outfun) (F1); + nfld[0] = get1(); +} + +void f2_only() +{ + if ((aflag & F2) || (vflag & F2)) (*outfun) (F2); + head = ftell(fp[1]); /* mark */ + nfld[1] = get2(0); /* check key order */ +} + +void output(f) +{ /* default output form */ + if (f & F1) + fputs(fld[0][kpos[0]], stdout); + else + fputs(fld[1][kpos[1]], stdout); + if (f & F1) outfld(0); + if (f & F2) outfld(1); + fputc('\n', stdout); +} + +void outfld(file) +{ /* output all fields except key_field */ + register int i; + int k, n; + + k = kpos[file]; + n = nfld[file]; + for (i = 0; i < n; i++) + if (i != k) { + fputc(SEP, stdout); + fputs(fld[file][i], stdout); + } +} + +void outputf(f) +{ /* output by '-o list' */ + int i, j, k; + register char *s; + + for (i = k = 0; i < nout; i++) { + j = olist[i].o_file; + if ((f & (j + 1)) && (olist[i].o_field < nfld[j])) + s = fld[j][olist[i].o_field]; + else + s = es; + if (s) { + if (k++) fputc(SEP, stdout); + fputs(s, stdout); + } + } + fputc('\n', stdout); +} + +int get1() +{ /* read file1 */ + int r; + static char oldkey1[BUFSIZ]; + + if (fld[0][kpos[0]]) { + strcpy(oldkey, fld[0][kpos[0]]); /* save previous key for control */ + } + r = getrec(0); + + if (r) { + if (strcmp(oldkey1, fld[0][kpos[0]]) > 0) + error("file1 is not sorted", (char *) 0); + strcpy(oldkey1, fld[0][kpos[0]]); /* save prev key for sort check */ + } + return r; +} + +int get2(back) +{ /* read file2 */ + static char oldkey2[BUFSIZ]; + int r; + + r = getrec(1); + + if (r) { + if (!back && strcmp(oldkey2, fld[1][kpos[1]]) > 0) + error("file2 is not sorted", (char *) 0); + strcpy(oldkey2, fld[1][kpos[1]]); /* save prev key for sort check */ + } + return r; +} + +int getrec(file) +{ /* read one line to split it */ + if (fgets(buf[file], BUFSIZ, fp[file]) == (char *) 0) + *buf[file] = '\0'; + else if (*buf[file] == '\n' || *buf[file] == '\r') + error("null line in file%s", file ? "1" : "0"); + + return split(file); +} + +int split(file) +{ /* setup fields */ + register int n; + register char *s, *t; + + for (n = 0, s = buf[file]; *s && *s != '\n' && *s != '\r';) { + if (sep) { + for (t = s; *s && *s != sep && *s != '\n' && *s != '\r'; s++); + } else { + while (*s == ' ' || *s == '\t') + s++; /* skip leading white space */ + for (t = s; *s && *s != ' ' && *s != '\t' + && *s != '\n' && *s != '\r'; s++); + /* We will treat trailing white space as NULL field */ + } + if (*s) *s++ = '\0'; + fld[file][n++] = t; + if (n == MAXFLD) error("too many filed in file%s", file ? "1" : "0"); + } + fld[file][n] = (char *) 0; + + return n; +} + +FILE *efopen(file, mode) +char *file, *mode; +{ + FILE *fp; + + if ((fp = fopen(file, mode)) == (FILE *) 0) error("can't open %s", file); + + return fp; +} + +void error(s, t) +char *s, *t; +{ + fprintf(stderr, "%s: ", cmd); + fprintf(stderr, s, t); + fprintf(stderr, "\n"); + + exit(1); +} diff --git a/commands/simple/kill.c b/commands/simple/kill.c new file mode 100755 index 000000000..ae6ab1e36 --- /dev/null +++ b/commands/simple/kill.c @@ -0,0 +1,100 @@ +/* kill - send a signal to a process Author: Adri Koppes */ + +#include <sys/types.h> +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void usage, (void)); + +/* Table of signal names. */ +struct signames { + char *name; + int sig; +} signames[] = { + { "HUP", SIGHUP }, + { "INT", SIGINT }, + { "QUIT", SIGQUIT }, + { "ILL", SIGILL }, + { "TRAP", SIGTRAP }, + { "ABRT", SIGABRT }, + { "IOT", SIGIOT }, + { "FPE", SIGFPE }, + { "KILL", SIGKILL }, + { "USR1", SIGUSR1 }, + { "SEGV", SIGSEGV }, + { "USR2", SIGUSR2 }, + { "PIPE", SIGPIPE }, + { "ALRM", SIGALRM }, + { "TERM", SIGTERM }, + { "EMT", SIGEMT }, + { "BUS", SIGBUS }, + { "CHLD", SIGCHLD }, + { "CONT", SIGCONT }, + { "STOP", SIGSTOP }, + { "TSTP", SIGTSTP }, + { "TTIN", SIGTTIN }, + { "TTOU", SIGTTOU }, +#ifdef SIGWINCH + { "WINCH", SIGWINCH }, +#endif + { NULL, 0 } +}; + +int main(argc, argv) +int argc; +char **argv; +{ + pid_t proc; + int ex = 0, sig = SIGTERM; + char *end; + long l; + unsigned long ul; + struct sigaction sa; + int i, doit; + struct signames *snp; + + if (argc > 1 && argv[1][0] == '-') { + sig = -1; + for (snp = signames; snp->name != NULL; snp++) { /* symbolic? */ + if (strcmp(snp->name, argv[1] + 1) == 0) { + sig = snp->sig; + break; + } + } + if (sig < 0) { /* numeric? */ + ul = strtoul(argv[1] + 1, &end, 10); + if (end == argv[1] + 1 || *end != 0 || ul > _NSIG) usage(); + sig = ul; + } + argv++; + argc--; + } + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sa.sa_handler = SIG_IGN; /* try not to kill yourself */ + (void) sigaction(sig, &sa, (struct sigaction *) NULL); + + for (doit = 0; doit <= 1; doit++) { + for (i = 1; i < argc; i++) { + l = strtoul(argv[i], &end, 10); + if (end == argv[i] || *end != 0 || (pid_t) l != l) usage(); + proc = l; + if (doit && kill(proc, sig) < 0) { + fprintf(stderr, "kill: %d: %s\n", + proc, strerror(errno)); + ex = 1; + } + } + } + return(ex); +} + +void usage() +{ + fprintf(stderr, "Usage: kill [-sig] pid\n"); + exit(1); +} diff --git a/commands/simple/last.c b/commands/simple/last.c new file mode 100755 index 000000000..4da5f830b --- /dev/null +++ b/commands/simple/last.c @@ -0,0 +1,470 @@ +/* last - display login history Author: Terrence W. Holm */ + +/* last- Display the user log-in history. + * Last(1) searches backwards through the file of log-in + * records (/usr/adm/wtmp), displaying the length of + * log-in sessions as requested by the options: + * + * Usage: last [-r] [-count] [-f file] [name] [tty] ... + * + * -r Search backwards only until the last reboot + * record. + * + * -count Only print out <count> records. Last(1) stops + * when either -r or -count is satisfied, or at + * the end of the file if neither is given. + * + * -f file Use "file" instead of "/usr/adm/wtmp". + * + * name Print records for the user "name". + * + * tty Print records for the terminal "tty". Actually, + * a list of names may be given and all records + * that match either the user or tty name are + * printed. If no names are given then all records + * are displayed. + * + * A sigquit (^\) causes last(1) to display how far it + * has gone back in the log-in record file, it then + * continues. This is used to check on the progress of + * long running searches. A sigint will stop last(1). + * + * Author: Terrence W. Holm May 1988 + * + * Revision: + * Fred van Kempen, October 1989 + * -Adapted to MSS. + * -Adapted to new utmp database. + * + * Fred van Kempen, December 1989 + * -Adapted to POSIX (MINIX 1.5) + * + * Fred van Kempen, January 1990 + * -Final edit for 1.5 + * + * Philip Homburg, March 1992 + * -Include host in output + * + * Kees J. Bot, July 1997 + * -Approximate system uptime from last reboot record + */ +#include <sys/types.h> +#include <signal.h> +#include <string.h> +#include <utmp.h> +#include <time.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> + + +#define FALSE 0 +#define TRUE 1 +#define RLOGIN 1 + +#define BUFFER_SIZE 4096 /* Room for wtmp records */ +#define MAX_WTMP_COUNT ( BUFFER_SIZE / sizeof(struct utmp) ) + +#define min( a, b ) ( (a < b) ? a : b ) +#define max( a, b ) ( (a > b) ? a : b ) + + +typedef struct logout { /* A logout time record */ + char line[12]; /* The terminal name */ + long time; /* The logout time */ + struct logout *next; /* Next in linked list */ +} logout; + + +static char *Version = "@(#) LAST 1.7 (10/24/92)"; + + +/* command-line option flags */ +char boot_limit = FALSE; /* stop on latest reboot */ +char count_limit = FALSE; /* stop after print_count */ +char tell_uptime = FALSE; /* tell uptime since last reboot */ +int print_count; +char *prog; /* name of this program */ +int arg_count; /* used to select specific */ +char **args; /* users and ttys */ + +/* global variables */ +long boot_time = 0; /* Zero means no reboot yet */ +char *boot_down; /* "crash" or "down " flag */ +logout *first_link = NULL; /* List of logout times */ +int interrupt = FALSE; /* If sigint or sigquit occurs */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void Sigint, (int sig)); +_PROTOTYPE(void Sigquit, (int sig)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(void Process, (struct utmp *wtmp)); +_PROTOTYPE(int Print_Record, (struct utmp *wtmp)); +_PROTOTYPE(void Print_Duration, (long from, long to)); +_PROTOTYPE(void Print_Uptime, (void)); +_PROTOTYPE(void Record_Logout_Time, (struct utmp *wtmp)); + +/* Sigint() and Sigquit() Flag occurrence of an interrupt. */ +void Sigint(sig) +int sig; +{ + interrupt = SIGINT; +} + + +void Sigquit(sig) +int sig; +{ + interrupt = SIGQUIT; +} + + +void usage() +{ + fprintf(stderr, + "Usage: last [-r] [-u] [-count] [-f file] [name] [tty] ...\n"); + exit(-1); +} + + +/* A log-in record format file contains four types of records. + * + * [1] generated on a system reboot: + * + * line="~", name="reboot", host="", time=date() + * + * + * [2] generated after a shutdown: + * + * line="~", name="shutdown", host="", time=date() + * + * + * [3] generated on a successful login(1) + * + * line=ttyname(), name=cuserid(), host=, time=date() + * + * + * [4] generated by init(8) on a logout + * + * line=ttyname(), name="", host="", time=date() + * + * + * Note: This version of last(1) does not recognize the '|' and '}' time + * change records. Last(1) pairs up line login's and logout's to + * generate four types of output lines: + * + * [1] a system reboot or shutdown + * + * reboot ~ Mon May 16 14:16 + * shutdown ~ Mon May 16 14:15 + * + * [2] a login with a matching logout + * + * edwin tty1 Thu May 26 20:05 - 20:32 (00:27) + * + * [3] a login followed by a reboot or shutdown + * + * root tty0 Mon May 16 13:57 - crash (00:19) + * root tty1 Mon May 16 13:45 - down (00:30) + * + * [4] a login not followed by a logout or reboot + * + * terry tty0 Thu May 26 21:19 still logged in + */ +void Process(wtmp) +struct utmp *wtmp; +{ + logout *link; + logout *next_link; + char is_reboot; + + /* suppress the job number on an "ftp" line */ + if (!strncmp(wtmp->ut_line, "ftp", (size_t)3)) strncpy(wtmp->ut_line, "ftp", (size_t)8); + + if (!strcmp(wtmp->ut_line, "~")) { + /* A reboot or shutdown record */ + if (boot_limit) exit(0); + + if (Print_Record(wtmp)) putchar('\n'); + boot_time = wtmp->ut_time; + + is_reboot = !strcmp(wtmp->ut_name, "reboot"); + if (is_reboot) + boot_down = "crash"; + else + boot_down = "down "; + + if (tell_uptime) { + if (!is_reboot) { + fprintf(stderr, + "%s: no reboot record added to wtmp file on system boot!\n", + prog); + exit(1); + } + Print_Uptime(); + exit(0); + } + + /* remove any logout records */ + for (link = first_link; link != NULL; link = next_link) { + next_link = link->next; + free(link); + } + first_link = NULL; + } else if (wtmp->ut_name[0] == '\0') { + /* A logout record */ + Record_Logout_Time(wtmp); + } else { + /* A login record */ + for (link = first_link; link != NULL; link = link->next) + if (!strncmp(link->line, wtmp->ut_line, (size_t)8)) { + /* found corresponding logout record */ + if (Print_Record(wtmp)) { + printf("- %.5s ", ctime(&link->time) + 11); + Print_Duration(wtmp->ut_time, link->time); + } + /* record login time */ + link->time = wtmp->ut_time; + return; + } + /* could not find a logout record for this login tty */ + if (Print_Record(wtmp)) + if (boot_time == 0) /* still on */ + printf(" still logged in\n"); + else { /* system crashed while on */ + printf("- %s ", boot_down); + Print_Duration(wtmp->ut_time, boot_time); + } + Record_Logout_Time(wtmp); /* Needed in case of 2 + * consecutive logins */ + } +} + + +/* Print_Record(wtmp) If the record was requested, then print out + * the user name, terminal, host and time. + */ +int Print_Record(wtmp) +struct utmp *wtmp; +{ + int i; + char print_flag = FALSE; + + /* just interested in the uptime? */ + if (tell_uptime) return(FALSE); + + /* check if we have already printed the requested number of records */ + if (count_limit && print_count == 0) exit(0); + + for (i = 0; i < arg_count; ++i) + if (!strncmp(args[i], wtmp->ut_name, sizeof(wtmp->ut_name)) || + !strncmp(args[i], wtmp->ut_line, sizeof(wtmp->ut_line))) + print_flag = TRUE; + + if (arg_count == 0 || print_flag) { +#ifdef RLOGIN + printf("%-8.8s %-8.8s %-16.16s %.16s ", + wtmp->ut_name, wtmp->ut_line, wtmp->ut_host, + ctime(&wtmp->ut_time)); +#else + printf("%-8.8s %-8.8s %.16s ", + wtmp->ut_name, wtmp->ut_line, ctime(&wtmp->ut_time)); +#endif + --print_count; + return(TRUE); + } + return(FALSE); +} + + +/* Print_Duration(from, to) Calculate and print the days and hh:mm between + * the log-in and the log-out. + */ +void Print_Duration(from, to) +long from; +long to; +{ + long delta, days, hours, minutes; + + delta = max(to - from, 0); + days = delta / (24L * 60L * 60L); + delta = delta % (24L * 60L * 60L); + hours = delta / (60L * 60L); + delta = delta % (60L * 60L); + minutes = delta / 60L; + + if (days > 0) + printf("(%ld+", days); + else + printf(" ("); + + printf("%02ld:%02ld)\n", hours, minutes); +} + + +/* Print_Uptime() Calculate and print the "uptime" between the last recorded + * boot and the current time. + */ +void Print_Uptime() +{ + char *utmp_file = "/etc/utmp"; + unsigned nusers; + struct utmp ut; + FILE *uf; + time_t now; + struct tm *tm; + unsigned long up; + + /* Count the number of active users in the utmp file. */ + if ((uf = fopen(utmp_file, "r")) == NULL) { + fprintf(stderr, "%s: %s: %s\n", prog, utmp_file, strerror(errno)); + exit(1); + } + + nusers = 0; + while (fread(&ut, sizeof(ut), 1, uf) == 1) { +#ifdef USER_PROCESS + if (ut.ut_type == USER_PROCESS) nusers++; +#else + if (ut.ut_name[0] != 0 && ut.ut_line[0] != 0) nusers++; +#endif + } + fclose(uf); + + /* Current time. */ + now = time((time_t *) NULL); + tm = localtime(&now); + + /* Uptime. */ + up = now - boot_time; + + printf(" %d:%02d up", tm->tm_hour, tm->tm_min); + if (up >= 24 * 3600L) { + unsigned long days = up / (24 * 3600L); + printf(" %lu day%s,", days, days == 1 ? "" : "s"); + } + printf(" %lu:%02lu,", (up % (24 * 3600L)) / 3600, (up % 3600) / 60); + printf(" %u user%s\n", nusers, nusers == 1 ? "" : "s"); +} + + +/* Record_Logout_Time(wtmp) A linked list of "last logout time" is kept. + * Each element of the list is for one terminal. + */ +void Record_Logout_Time(wtmp) +struct utmp *wtmp; +{ + logout *link; + + /* see if the terminal is already in the list */ + for (link = first_link; link != NULL; link = link->next) + if (!strncmp(link->line, wtmp->ut_line, (size_t)8)) { + link->time = wtmp->ut_time; + return; + } + /* allocate a new logout record, for a tty not previously encountered */ + link = (logout *) malloc(sizeof(logout)); + if (link == NULL) { + fprintf(stderr, "%s: malloc failure\n", prog); + exit(1); + } + strncpy(link->line, wtmp->ut_line, (size_t)8); + link->time = wtmp->ut_time; + link->next = first_link; + + first_link = link; +} + + +int main(argc, argv) +int argc; +char *argv[]; +{ + char *wtmp_file = "/usr/adm/wtmp"; + FILE *f; + long size; /* Number of wtmp records in the file */ + int wtmp_count; /* How many to read into wtmp_buffer */ + struct utmp wtmp_buffer[MAX_WTMP_COUNT]; + + if ((prog = strrchr(argv[0], '/')) == NULL) prog = argv[0]; else prog++; + + --argc; + ++argv; + + while (argc > 0 && *argv[0] == '-') { + if (!strcmp(argv[0], "-r")) + boot_limit = TRUE; + else + if (!strcmp(argv[0], "-u")) + tell_uptime = TRUE; + else if (argc > 1 && !strcmp(argv[0], "-f")) { + wtmp_file = argv[1]; + --argc; + ++argv; + } else if ((print_count = atoi(argv[0] + 1)) > 0) + count_limit = TRUE; + else + usage(); + + --argc; + ++argv; + } + + arg_count = argc; + args = argv; + + if (!strcmp(prog, "uptime")) tell_uptime = TRUE; + + if ((f = fopen(wtmp_file, "r")) == NULL) { + perror(wtmp_file); + exit(1); + } + if (fseek(f, 0L, 2) != 0 || (size = ftell(f)) % sizeof(struct utmp) != 0) { + fprintf(stderr, "%s: invalid wtmp file\n", prog); + exit(1); + } + if (signal(SIGINT, SIG_IGN) != SIG_IGN) { + signal(SIGINT, Sigint); + signal(SIGQUIT, Sigquit); + } + size /= sizeof(struct utmp); /* Number of records in wtmp */ + + if (size == 0) wtmp_buffer[0].ut_time = time((time_t *)0); + + while (size > 0) { + wtmp_count = (int) min(size, MAX_WTMP_COUNT); + size -= (long) wtmp_count; + + fseek(f, size * sizeof(struct utmp), 0); + + + if (fread(&wtmp_buffer[0], sizeof(struct utmp), (size_t)wtmp_count, f) + != wtmp_count) { + fprintf(stderr, "%s: read error on wtmp file\n", prog); + exit(1); + } + while (--wtmp_count >= 0) { + Process(&wtmp_buffer[wtmp_count]); + if (interrupt) { + printf("\ninterrupted %.16s \n", + ctime(&wtmp_buffer[wtmp_count].ut_time)); + + if (interrupt == SIGINT) exit(2); + + interrupt = FALSE; + signal(SIGQUIT, Sigquit); + } + } + + } /* end while(size > 0) */ + + if (tell_uptime) { + fprintf(stderr, + "%s: no reboot record in wtmp file to compute uptime from\n", + prog); + return(1); + } + + printf("\nwtmp begins %.16s \n", ctime(&wtmp_buffer[0].ut_time)); + return(0); +} diff --git a/commands/simple/leave.c b/commands/simple/leave.c new file mode 100755 index 000000000..bca1213ea --- /dev/null +++ b/commands/simple/leave.c @@ -0,0 +1,210 @@ +/* Usage: leave [ [+] hh[:]mm ] + * + * Author: Terrence W. Holm + * + * Revision: + * Fred van Kempen, MINIX User Group Holland + * -adapted to MSS + * -adapted to new utmp database + * -adapted to POSIX (MINIX 1.5) + * Michael Temari, <temari@ix.netcom.com> + * -use localtime/mktime to fix bug with DST + */ + +#include <sys/types.h> +#include <signal.h> +#include <time.h> +#include <utmp.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + + +#define Min(a,b) ((a<b) ? a : b) + +#define STRING 80 /* lots of room for an argument */ +#define MIN 60L /* seconds per minute */ +#define HOUR (60L*60L) /* seconds per hour */ + +/* Set the following to your personal preferences for the + * time and contents of warnings. + */ +#define INTERVALS 13 /* size of intervals[] */ +#define WARNINGS 4 /* size of warnings[] */ + + +static char *Version = "@(#) LEAVE 1.4 (01/09/90)"; +static int intervals[INTERVALS] = { + -5 * MIN, + -1 * MIN, + 0, + MIN, + 2 * MIN, + 3 * MIN, + 4 * MIN, + 5 * MIN, + 6 * MIN, + 7 * MIN, + 8 * MIN, + 9 * MIN, + 10 * MIN +}; +static char *warnings[WARNINGS] = { + "You have to leave within 5 minutes", + "Just one more minute!", + "Time to leave!", + "You're going to be late!" /* for all subsequent warnings */ +}; + + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void Usage, (void)); +_PROTOTYPE(void Get_Hour_Min, (char *when, int *hour, int *min)); +_PROTOTYPE(int Still_Logged_On, (char *user, char *tty)); + +void Usage() +{ + fprintf(stderr, "Usage: leave [[+]hh[:]mm]\n"); + exit(1); +} + + +void Get_Hour_Min(when, hour, min) +char *when; +int *hour; +int *min; +{ + int hour_min; + int just_min = 0; + + switch (sscanf(when, "%d:%d", &hour_min, &just_min)) { + case 1: + *hour = hour_min / 100; + *min = hour_min % 100; + break; + case 2: + *hour = hour_min; + *min = just_min; + break; + default: + Usage(); + } + + if (hour_min < 0 || just_min < 0 || *min > 59) Usage(); +} + + +int Still_Logged_On(user, tty) +char *user; +char *tty; +{ + FILE *f; + struct utmp login; + + if ((f = fopen(UTMP, "r")) == NULL) + /* no login/logout records kept */ + return(1); + + while (fread(&login, sizeof(struct utmp), (size_t)1, f) == 1) { + if (!strncmp(login.ut_line, tty, (size_t)8)) + if (!strncmp(login.ut_name, user, (size_t)8)) { + fclose(f); + return(1); + } else { + fclose(f); + return(0); + } + } + fclose(f); + return(0); +} + + +int main(argc, argv) +int argc; +char *argv[]; +{ + char when[STRING]; + time_t now = time((time_t *)0); + time_t leave, delta; + struct tm *tm; + int hour, min; + int pid, i; + char *user = cuserid( (char *)NULL); + char *tty = ttyname(0) + 5; + + /* get the argument string "when" either from stdin, or argv */ + if (argc <= 1) { + printf("When do you have to leave? "); + fflush(stdout); + if (fgets(when, STRING, stdin) == NULL || when[0] == '\n') exit(0); + } else { + strcpy(when, argv[1]); + if (argc > 2) strcat(when, argv[2]); + } + + /* determine the leave time from the current time and "when" */ + tm = localtime(&now); + if (when[0] == '+') { + Get_Hour_Min(&when[1], &hour, &min); + tm->tm_hour += hour; + tm->tm_min += min; + leave = mktime(tm); + } else { + /* user entered an absolute time */ + Get_Hour_Min(&when[0], &hour, &min); + tm->tm_hour = hour; + tm->tm_min = min; + leave = mktime(tm); + if (leave < now) { + printf("That time has already passed!\n"); + exit(1); + } + } + + printf("Alarm set for %s", ctime(&leave)); + + if ((pid = fork()) == -1) { + fprintf(stderr, "leave: can not fork\n"); + exit(1); + } + if (pid != 0) exit(0); + + /* only the child continues on */ + if (user == NULL || tty == NULL) { + fprintf(stderr, "leave: Can not determine user and terminal name\n"); + exit(1); + } + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + + for (;;) { + if (!Still_Logged_On(user, tty)) exit(0); + + /* how much longer until the leave time? */ + /* XXX - use difftime all over. */ + delta = leave - time((time_t *)0); + + /* which interval are we currently in? */ + for (i = 0; i < INTERVALS; ++i) + if (delta + intervals[i] > 0) break; + + /* if we are within intervals[0] then print a warning If + * there are more intervals than messages, then use/ + * warnings[WARNINGS-1] for all subsequent messages. */ + if (i > 0) + printf("\007\r%s\r\n", + warnings[i > WARNINGS ? WARNINGS - 1 : i - 1]); + + if (i == INTERVALS) { + printf("That was the last time I'll tell you. Bye.\r\n"); + exit(0); + } + /* Sleep until the next interval. For long periods, wake up + * every hour to check if the user is still on (also required + * because 16 bit ints don't allow long waits). */ + sleep((unsigned) Min(delta + intervals[i], HOUR)); + } +} diff --git a/commands/simple/life.c b/commands/simple/life.c new file mode 100755 index 000000000..31bfbcd92 --- /dev/null +++ b/commands/simple/life.c @@ -0,0 +1,241 @@ +/* life - Conway's game of life Author: Jim King */ + +/* clife.c - curses life simulator. Translated from Pascal to C implementing + * curses Oct 1988 by pulsar@lsrhs, not jek5036@ritvax.isc.rit.edu + * life needs about 18kb stack space on MINIX. + * + * Flags: -d draw your own screen using arrows and space bar + * -p print statistics on the bottom line during the game + */ + +#include <sys/types.h> +#include <signal.h> +#include <time.h> +#include <curses.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#if __minix_vmd /* Use a more random rand(). */ +#define srand(seed) srandom(seed) +#define rand() random() +#endif + +/* A value of -1 will make it go forever */ +/* A value of 0 will make it exit immediately */ +#define REPSTOP -1 /* number of repetitions before stop */ + +int present[23][80]; /* screen 1 cycle ago */ +int past[23][80]; /* screen this cycle */ +int total; /* total # of changes */ +int icnt; /* counter to check for repetition */ +int maxrow = 22; /* some defines to represent the screen */ +int maxcol = 79; +int minrow = 0; +int mincol = 0; +int pri = 0; /* flag for printing stats on bottom line */ +int draw = 0; /* flag for drawing your own screen */ +int i, j, k; /* loop counters */ +int cycle; /* current cycle # */ +int changes; /* # of changes this cycle (live + die) */ +int die; /* number of deaths this cycle */ +int live; /* number of births this cycle */ + +WINDOW *mns; /* Main Screen */ +WINDOW *info; /* Bottom line */ + +_PROTOTYPE(void cleanup, (int s)); +_PROTOTYPE(void initialize, (void)); +_PROTOTYPE(void makscr, (void)); +_PROTOTYPE(void update, (void)); +_PROTOTYPE(void print, (void)); +_PROTOTYPE(int main, (int ac, char *av[])); + +/* Cleanup - cleanup then exit */ +void cleanup(s) +int s; +{ + move(23, 0); /* go to bottom of screen */ + refresh(); /* update cursor */ + + endwin(); /* shutdown curses */ + exit(1); /* exit */ +} + +/* Initialize - init windows, variables, and signals */ + +void initialize() +{ + srand(getpid()); /* init random seed */ + initscr(); /* init curses */ + noecho(); + curs_set(0); + signal(SIGINT, cleanup); /* catch ^C */ + mns = newwin(maxrow, maxcol, 0, 0); /* new window */ + scrollok(mns, FALSE); + info = newwin(1, 80, 23, 0); + scrollok(info, FALSE); + wclear(mns); + wclear(info); + wmove(info, 0, 0); + wrefresh(info); + if (!draw) { /* if no draw, make random pattern */ + for (j = 0; j < maxrow; j++) { + for (k = 0; k < maxcol; k++) { + present[j][k] = rand() % 2; + if (present[j][k] == 1) changes++, live++; + } + } + } +} + +/* Makscr - make your own screen using arrow keys and space bar */ +void makscr() +{ + int curx, cury; /* current point on screen */ + char c; /* input char */ + + wclear(info); + wmove(info, 0, 0); + wprintw(info, "Use arrow keys to move, space to place / erase, ^D to start", NULL); + wrefresh(info); + curx = cury = 1; + wmove(mns, cury - 1, curx - 1); + wrefresh(mns); + noecho(); + for (;;) { + c = wgetch(mns); + if (c == '\004') + break; + else if (c == ' ') { + if (present[cury][curx]) { + --present[cury][curx]; + changes++; + die++; + mvwaddch(mns, cury, curx, ' '); + } else { + ++present[cury][curx]; + changes++; + live++; + mvwaddch(mns, cury, curx, '*'); + } + } else if (c == '\033') { + wgetch(mns); + switch (wgetch(mns)) { + case 'A': --cury; break; + case 'B': ++cury; break; + case 'C': ++curx; break; + case 'D': --curx; break; + default: break; + } + } + if (cury > maxrow) cury = minrow; + if (cury < minrow) cury = maxrow; + if (curx > maxcol) curx = mincol; + if (curx < mincol) curx = maxcol; + wmove(mns, cury, curx); + wrefresh(mns); + } + wclear(info); +} + +/* Update rules: 2 or 3 adjacent alive --- stay alive + * 3 adjacent alive -- dead to live + * all else die or stay dead + */ +void update() +{ /* Does all mathmatical calculations */ + int howmany, w, x, y, z; + changes = die = live = 0; + for (j = 0; j < maxrow; j++) { + for (k = 0; k < maxcol; k++) { + w = j - 1; + x = j + 1; + y = k - 1; + z = k + 1; + + howmany = (past[w][y] + past[w][k] + past[w][z] + + past[j][y] + past[j][z] + past[x][y] + + past[x][k] + past[x][z]); + + switch (howmany) { + case 0: + case 1: + case 4: + case 5: + case 6: + case 7: + case 8: + present[j][k] = 0; + if (past[j][k]) changes++, die++; + break; + case 3: + present[j][k] = 1; + if (!past[j][k]) changes++, live++; + break; + default: break; + } + } + } + if (live == die) + ++icnt; + else + icnt = 0; + + if (icnt == REPSTOP) cleanup(0); +} + +/* Print - updates the screen according to changes from past to present */ +void print() +{ +/* Updates the screen, greatly improved using curses */ + if (pri) { + wmove(info, 0, 0); + total += changes; + cycle++; + wprintw(info, "Cycle %5d | %5d changes: %5d died + %5d born = %5u total changes", (char *) cycle, changes, die, live, total); + wclrtoeol(info); + } + for (j = 1; j < maxrow; j++) { + for (k = 1; k < maxcol; k++) { + if (present[j][k] != past[j][k] && present[j][k] == 1) { + wmove(mns, j, k); + wprintw(mns, "*", NULL); + } else if (present[j][k] != past[j][k] && present[j][k] == 0) { + wmove(mns, j, k); + wprintw(mns, " ", NULL); + } + } + } + if (pri) wrefresh(info); + wrefresh(mns); +} + +/* Main - main procedure */ +int main(ac, av) +int ac; +char *av[]; +{ + if (ac > 1) { + for (j = 1; j < ac; j++) { + switch (av[j][1]) { + case 'd': ++draw; break; + case 'p': ++pri; break; + default: + fprintf(stderr, "%s: usage: %s [-d] [-p]\n", av[0], av[0]); + exit(1); + } + } + } + + initialize(); + if (draw) makscr(); + + for (;;) { + print(); + for (j = 0; j < maxrow; j++) { + for (k = 0; k < maxcol; k++) past[j][k] = present[j][k]; + } + update(); + } +} diff --git a/commands/simple/login.c b/commands/simple/login.c new file mode 100755 index 000000000..802fe1131 --- /dev/null +++ b/commands/simple/login.c @@ -0,0 +1,507 @@ +/* login - log into the system Author: Patrick van Kleef */ + +/* Original version by Patrick van Kleef. History of modifications: + * + * Peter S. Housel Jan. 1988 + * - Set up $USER, $HOME and $TERM. + * - Set signals to SIG_DFL. + * + * Terrence W. Holm June 1988 + * - Allow a username as an optional argument. + * - Time out if a password is not typed within 60 seconds. + * - Perform a dummy delay after a bad username is entered. + * - Don't allow a login if "/etc/nologin" exists. + * - Cause a failure on bad "pw_shell" fields. + * - Record the login in "/usr/adm/wtmp". + * + * Peter S. Housel Dec. 1988 + * - Record the login in "/etc/utmp" also. + * + * F. van Kempen June 1989 + * - various patches for Minix V1.4a. + * + * F. van Kempen September 1989 + * - added login-failure administration (new utmp.h needed!). + * - support arguments in pw_shell field + * - adapted source text to MINIX Style Sheet + * + * F. van Kempen October 1989 + * - adapted to new utmp database. + * F. van Kempen, December 1989 + * - fixed 'slot' assumption in wtmp() + * - fixed all MSS-stuff + * - adapted to POSIX (MINIX 1.5) + * F. van Kempen, January 1990 + * - made all 'bad login accounting' optional by "#ifdef BADLOG". + * F. van Kempen, Februari 1990 + * - fixed 'first argument' bug and added some casts. + * + * Andy Tanenbaum April 1990 + * - if /bin/sh cannot be located, try /usr/bin/sh + * + * Michael A. Temari October 1990 + * - handle more than single digit tty devices + * + * Philip Homburg - Feb 28 1992 + * - use ttyname to get the name of a tty. + * + * Kees J. Bot - Feb 13 1993 + * - putting out garbage. + * - added lastlog. + * + * Kees J. Bot - Feb 13 1993 + * - supplementary groups. + * + * Kees J. Bot - Jan 3 1996 + * - ported back to standard Minix. + */ + +#define _MINIX_SOURCE +#define _POSIX_C_SOURCE 2 + +#include <sys/types.h> +#include <ttyent.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <pwd.h> +#include <grp.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <utmp.h> +#include <time.h> +#include <sys/utsname.h> +#include <minix/minlib.h> + +char PATH_UTMP[] = "/etc/utmp"; /* current logins */ +char PATH_WTMP[] = "/usr/adm/wtmp"; /* login/logout history */ +char PATH_LASTLOG[] = "/usr/adm/lastlog"; /* last login history */ +char PATH_MOTD[] = "/etc/motd"; /* message of the day */ + +#define TTY_GID 4 /* group ID of ttys */ + +#define EXTRA_ENV 6 + +/* Crude indication of a tty being physically secure: */ +#define securetty(dev) ((unsigned) ((dev) - 0x0400) < (unsigned) 8) + +int time_out; +char *hostname; +char user[32]; +char logname[35]; +char home[128]; +char shell[128]; +char term[128]; +char **env; +extern char **environ; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void wtmp, (char *user, int uid)); +_PROTOTYPE(void show_file, (char *nam)); +_PROTOTYPE(void Time_out, (int dummy)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(void add2env, (char **env, char *entry, int replace)); + +void wtmp(user, uid) +char *user; /* user name */ +int uid; /* user id */ +{ + /* Make entries in /usr/adm/wtmp and /etc/utmp. */ + struct utmp entry; + register int fd= -1; + int lineno; + int err = 0; + char *what; + + /* First, read the current UTMP entry. we need some of its + * parameters! (like PID, ID etc...). + */ + what= "ttyslot()"; + lineno= ttyslot(); + if (lineno == 0) err= errno; /* ttyslot failed */ + + if (err == 0 && (fd = open(what = PATH_UTMP, O_RDONLY)) < 0) { + if (errno == ENOENT) return; + err= errno; + } + if (err == 0 && lseek(fd, (off_t) lineno * sizeof(entry), SEEK_SET) < 0) + err= errno; + if (err == 0 && read(fd, (char *) &entry, sizeof(entry)) != sizeof(entry)) + err= errno; + if (fd >= 0) close(fd); + + /* Enter new fields. */ + strncpy(entry.ut_user, user, sizeof(entry.ut_user)); + if (hostname) strncpy(entry.ut_host, hostname, sizeof(entry.ut_host)); + + if (entry.ut_pid == 0) entry.ut_pid = getpid(); + + entry.ut_type = USER_PROCESS; /* we are past login... */ + time(&entry.ut_time); + + /* Write a WTMP record. */ + if (err == 0) { + if ((fd = open(what = PATH_WTMP, O_WRONLY|O_APPEND)) < 0) { + if (errno != ENOENT) err= errno; + } else { + if (write(fd, (char *) &entry, sizeof(entry)) < 0) err= errno; + close(fd); + } + } + + /* Rewrite the UTMP entry. */ + if (err == 0 && (fd = open(what = PATH_UTMP, O_WRONLY)) < 0) + err= errno; + if (err == 0 && lseek(fd, (off_t) lineno * sizeof(entry), SEEK_SET) < 0) + err= errno; + if (err == 0 && write(fd, (char *) &entry, sizeof(entry)) < 0) + err= errno; + if (fd >= 0) close(fd); + + /* Write the LASTLOG entry. */ + if (err == 0 && (fd = open(what = PATH_LASTLOG, O_WRONLY)) < 0) { + if (errno == ENOENT) return; + err= errno; + } + if (err == 0 && lseek(fd, (off_t) uid * sizeof(entry), SEEK_SET) < 0) + err= errno; + if (err == 0 && write(fd, (char *) &entry, sizeof(entry)) < 0) + err= errno; + if (fd >= 0) close(fd); + + if (err != 0) { + fprintf(stderr, "login: %s: %s\n", what, strerror(err)); + return; + } +} + + +void show_file(nam) +char *nam; +{ +/* Read a textfile and show it on the desired terminal. */ + register int fd, len; + char buf[80]; + + if ((fd = open(nam, O_RDONLY)) > 0) { + len = 1; + while (len > 0) { + len = read(fd, buf, 80); + write(1, buf, len); + } + close(fd); + } +} + + +int main(argc, argv) +int argc; +char *argv[]; +{ + char name[30]; + char *password, *cryptedpwd; + char *tty_name, *p; + int n, ap, check_pw, bad, secure, i, envsiz, do_banner; + struct passwd *pwd; + char *bp, *argx[8], **ep; /* pw_shell arguments */ + char argx0[64]; /* argv[0] of the shell */ + char *sh = "/bin/sh"; /* sh/pw_shell field value */ + char *initialname; + int c, b_flag, f_flag, p_flag; + char *h_arg; + int authorized, preserv_env; + struct ttyent *ttyp; + struct stat ttystat; + struct sigaction sa; + struct utsname uts; + + /* Don't let QUIT dump core. */ + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = exit; + sigaction(SIGQUIT, &sa, NULL); + + /* Parse options. */ + b_flag= 0; + f_flag= 0; + p_flag= 0; + h_arg= NULL; + while ((c= getopt(argc, argv, "?bfh:p")) != -1) + { + switch(c) + { + case 'b': b_flag= 1; break; + case 'f': f_flag= 1; break; + case 'h': + if (h_arg) + usage(); + if (getuid() == 0) + h_arg= optarg; + break; + case 'p': p_flag= 1; break; + case '?': + usage(); + default: + fprintf(stderr, "login: getopt failed: '%c'\n", c); + exit(1); + } + } + if (optind < argc) + initialname= argv[optind++]; + else + initialname= NULL; + if (optind != argc) + usage(); + + authorized= f_flag; + hostname= h_arg; + preserv_env= p_flag; + do_banner= b_flag; + + /* Look up /dev/tty number. */ + tty_name= ttyname(0); + if (tty_name == NULL) + { + write(1, "Unable to lookup tty name\n", 26); + exit(1); + } + + if (do_banner) + { + uname(&uts); + write(1, "\n", 1); + write(1, uts.sysname, strlen(uts.sysname)); + write(1, "/", 1); + write(1, uts.machine, strlen(uts.machine)); + write(1, " Release ", 9); + write(1, uts.release, strlen(uts.release)); + write(1, " Version ", 9); + write(1, uts.version, strlen(uts.version)); + write(1, " (", 2); + p= strrchr(tty_name, '/'); + if (!p) + p= tty_name; + else + p++; + write(1, p, strlen(p)); + write(1, ")\n\n", 3); + write(1, uts.nodename, strlen(uts.nodename)); + write(1, " ", 1); + } + + /* Get login name and passwd. */ + for (;;initialname= NULL) { + if (initialname) + strcpy(name, initialname); + else { + do { + write(1, "login: ", 7); + n = read(0, name, 30); + if (n == 0) exit(1); + if (n < 0) + { + if (errno != EINTR) + fprintf(stderr, + "login: read failed: %s\n", + strerror(errno)); + exit(1); + } + } while (n < 2); + name[n - 1] = 0; + } + + /* Start timer running. */ + time_out = 0; + sa.sa_handler = Time_out; + sigaction(SIGALRM, &sa, NULL); + alarm(60); + + + /* Look up login/passwd. */ + pwd = getpwnam(name); + + check_pw = 1; /* default is check password. */ + + /* For now, only console is secure. */ + secure = fstat(0, &ttystat) == 0 && securetty(ttystat.st_rdev); + + if (pwd && authorized && initialname + && (pwd->pw_uid == getuid() || getuid() == 0)) { + check_pw= 0; /* Don't ask a password for + * pre-authorized users. + */ + } else + if (pwd && secure && strcmp(crypt("", pwd->pw_passwd), + pwd->pw_passwd) == 0) { + check_pw= 0; /* empty password */ + } + + if (check_pw) { + password = getpass("Password:"); + + if (time_out) exit(1); + + bad = 0; + if (!pwd) bad = 1; + if (!password) { password = ""; bad = 1; } + if (!secure && pwd && strcmp(crypt("", pwd->pw_passwd), + pwd->pw_passwd) == 0) bad = 1; + + cryptedpwd = bad ? "*" : pwd->pw_passwd; + + if (strcmp(crypt(password, cryptedpwd), cryptedpwd) != 0) { + write(1, "Login incorrect\n", 16); + continue; + } + } + /* Check if the system is going down */ + if (access("/etc/nologin", 0) == 0 && strcmp(name, "root") != 0) { + write(1, "System going down\n\n", 19); + continue; + } + + /* Stop timer. */ + alarm(0); + + /* Write login record to /usr/adm/wtmp and /etc/utmp */ + wtmp(name, pwd->pw_uid); + + /* Create the argv[] array from the pw_shell field. */ + ap = 0; + argx[ap++] = argx0; /* "-sh" most likely */ + if (pwd->pw_shell[0]) { + sh = pwd->pw_shell; + bp = sh; + while (*bp) { + while (*bp && *bp != ' ' && *bp != '\t') bp++; + if (*bp == ' ' || *bp == '\t') { + *bp++ = '\0'; /* mark end of string */ + argx[ap++] = bp; + } + } + } else + argx[ap] = NULL; + strcpy(argx0, "-"); /* most shells need it for their .profile */ + if ((bp= strrchr(sh, '/')) == NULL) bp = sh; else bp++; + strncat(argx0, bp, sizeof(argx0) - 2); + + /* Set the environment */ + if (p_flag) + { + for (ep= environ; *ep; ep++) + ; + } + else + ep= environ; + + envsiz= ep-environ; + env= calloc(envsiz + EXTRA_ENV, sizeof(*env)); + if (env == NULL) + { + fprintf(stderr, "login: out of memory\n"); + exit(1); + } + for (i= 0; i<envsiz; i++) + env[i]= environ[i]; + + strcpy(user, "USER="); + strcat(user, name); + add2env(env, user, 1); + strcpy(logname, "LOGNAME="); + strcat(logname, name); + add2env(env, logname, 1); + strcpy(home, "HOME="); + strcat(home, pwd->pw_dir); + add2env(env, home, 1); + strcpy(shell, "SHELL="); + strcat(shell, sh); + add2env(env, shell, 1); + if ((ttyp = getttynam(tty_name + 5)) != NULL) { + strcpy(term, "TERM="); + strcat(term, ttyp->ty_type); + add2env(env, term, 0); + } + + /* Show the message-of-the-day. */ + show_file(PATH_MOTD); + + /* Assign the terminal to this user. */ + chown(tty_name, pwd->pw_uid, TTY_GID); + chmod(tty_name, 0620); + + /* Change id. */ +#if __minix_vmd + initgroups(pwd->pw_name, pwd->pw_gid); +#endif + setgid(pwd->pw_gid); + setuid(pwd->pw_uid); + + /* cd $HOME */ + chdir(pwd->pw_dir); + + /* Reset signals to default values. */ + sa.sa_handler = SIG_DFL; + for (n = 1; n <= _NSIG; ++n) sigaction(n, &sa, NULL); + + /* Execute the user's shell. */ + execve(sh, argx, env); + + if (pwd->pw_gid == 0) { + /* Privileged user gets /bin/sh in times of crisis. */ + sh= "/bin/sh"; + argx[0]= "-sh"; + strcpy(shell, "SHELL="); + strcat(shell, sh); + execve(sh, argx, env); + } + fprintf(stderr, "login: can't execute %s: %s\n", sh, strerror(errno)); + exit(1); + } + return(0); +} + + +void Time_out(dummy) +int dummy; /* to keep the compiler happy */ +{ + write(2, "\r\nLogin timed out after 60 seconds\r\n", 36); + time_out = 1; +} + +void usage() +{ + fprintf(stderr, + "Usage: login [-h hostname] [-b] [-f] [-p] [username]\n"); + exit(1); +} + +void add2env(env, entry, replace) +char **env; +char *entry; +int replace; +{ +/* Replace an environment variable with entry or add entry if the environment + * variable doesn't exit yet. + */ + char *cp; + int keylen; + + cp= strchr(entry, '='); + keylen= cp-entry+1; + + for(; *env; env++) + { + if (strncmp(*env, entry, keylen) == 0) { + if (!replace) return; /* Don't replace */ + break; + } + } + *env= entry; +} + +/* + * $PchId: login.c,v 1.6 2001/07/31 14:23:28 philip Exp $ + */ diff --git a/commands/simple/look.c b/commands/simple/look.c new file mode 100755 index 000000000..746b247cf --- /dev/null +++ b/commands/simple/look.c @@ -0,0 +1,158 @@ +/* look 1.3 - Find lines in a sorted list. Author: Kees J. Bot + */ +#define nil 0 +#include <sys/types.h> +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> + +char DEFAULT[] = "/usr/lib/dict/words"; + +char *string, *wordlist= DEFAULT; + +#define MAXLEN 1024 /* Maximum word length. */ + +int dflg= 0, fflg= 0; + +void nonascii(char *what) +{ + fprintf(stderr, "look: %s contains non-ASCII characters.\n", what); + exit(1); +} + +int compare(char *prefix, char *word) +{ + char *p= prefix, *w= word; + int cp, cw; + + do { + do { + if ((cp= *p++) == 0) return 0; + if (!isascii(cp)) nonascii("prefix string"); + } while (dflg && !isspace(cp) && !isalnum(cp)); + + if (dflg) { + if (isspace(cp)) { + while (isspace(*p)) p++; + cp= ' '; + } + } + if (fflg && isupper(cp)) cp= tolower(cp); + + do { + if ((cw= *w++) == 0) return 1; + if (!isascii(cw)) nonascii(wordlist); + } while (dflg && !isspace(cw) && !isalnum(cw)); + + if (dflg) { + if (isspace(cw)) { + while (isspace(*w)) w++; + cw= ' '; + } + } + if (fflg && isupper(cw)) cw= tolower(cw); + } while (cp == cw); + + return cp - cw; +} + +char *readword(FILE *f) +{ + static char word[MAXLEN + 2]; + int n; + + if (fgets(word, sizeof(word), f) == nil) { + if (ferror(f)) { + fprintf(stderr, "look: read error on %s", + wordlist); + exit(1); + } + return nil; + } + + n= strlen(word); + + if (word[n-1] != '\n') { + fprintf(stderr, "look: word from %s is too long\n", wordlist); + exit(1); + } + word[n-1] = 0; + + return word; +} + +void look(void) +{ + off_t low, mid, high; + FILE *f; + char *word; + int c; + + if ((f= fopen(wordlist, "r")) == nil) { + fprintf(stderr, "look: Can't open %s\n", wordlist); + exit(1); + } + + low= 0; + + fseek(f, (off_t) 0, 2); + + high= ftell(f); + + while (low <= high) { + mid= (low + high) / 2; + + fseek(f, mid, 0); + + if (mid != 0) readword(f); + + if ((word= readword(f)) == nil) + c= -1; + else + c= compare(string, word); + + if (c <= 0) high= mid - 1; else low= mid + 1; + } + fseek(f, low, 0); + if (low != 0) readword(f); + + c=0; + while (c >= 0 && (word= readword(f)) != nil) { + c= compare(string, word); + + if (c == 0) puts(word); + } +} + +int main(int argc, char **argv) +{ + if (argc == 2) dflg= fflg= 1; + + while (argc > 1 && argv[1][0] == '-') { + char *p= argv[1] + 1; + + while (*p != 0) { + switch (*p++) { + case 'd': dflg= 1; break; + case 'f': fflg= 1; break; + default: + fprintf(stderr, "look: Bad flag: %c\n", p[-1]); + exit(1); + } + } + argc--; + argv++; + } + if (argc == 3) + wordlist= argv[2]; + else + if (argc != 2) { + fprintf(stderr, "Usage: look [-df] string [file]\n"); + exit(1); + } + string= argv[1]; + look(); + exit(0); +} +/* Kees J. Bot 24-5-89. */ diff --git a/commands/simple/lp.c b/commands/simple/lp.c new file mode 100755 index 000000000..8ea373aab --- /dev/null +++ b/commands/simple/lp.c @@ -0,0 +1,91 @@ +/* lp 1.4 - Send file to the lineprinter Author: Kees J. Bot + * 3 Dec 1989 + */ +#define nil 0 +#include <sys/types.h> +#include <stdio.h> +#include <limits.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <sys/wait.h> + +char LPD1[] = "/usr/sbin/lpd"; /* Proper place of lpd */ +char LPD2[] = "/usr/bin/lpd"; /* Minix has no sbin directories. */ + +void report(char *mess) +{ + fprintf(stderr, "lp: %s: %s\n", mess, strerror(errno)); +} + +void fatal(char *mess) +{ + report(mess); + exit(1); +} + +void lp(char *file) +/* Start the lpd daemon giving it the file to spool and print. */ +{ + int pid, status; + + if (file[0] != '/' || (pid= fork()) == 0) { + execl(LPD1, LPD1, file, (char *) nil); + if (errno != ENOENT) fatal(LPD1); + execl(LPD2, LPD2, file, (char *) nil); + fatal(LPD2); + } + + if (pid < 0) fatal("can't fork"); + + if (waitpid(pid, &status, 0) < 0) fatal("wait"); + + if (status != 0) exit(1); +} + +char path[PATH_MAX+1]; +int cwdsize; + +int main(int argc, char **argp) +{ + int e=0; + char *file; + + if (argc <= 1) lp("stdin"); + + /* Lpd requires full path names, so find out where we are. */ + if (getcwd(path, sizeof(path)) == nil) + fatal("Can't determine current directory"); + + cwdsize= strlen(path); + + /* Hand each file to lpd. */ + while ((file= *++argp) != nil) { + + close(0); + + if (open(file, O_RDONLY) != 0) { + report(file); + e=1; + continue; + } + if (file[0] == '/') { + lp(file); + continue; + } + if (cwdsize + 1 + strlen(file) + 1 > sizeof(path)) { + fprintf(stderr, + "lp: full pathname of %s is too long\n", + file); + e=1; + continue; + } + path[cwdsize] = '/'; + strcpy(path + cwdsize + 1, file); + + lp(path); + } + exit(e); +} diff --git a/commands/simple/lpd.c b/commands/simple/lpd.c new file mode 100755 index 000000000..750d79776 --- /dev/null +++ b/commands/simple/lpd.c @@ -0,0 +1,368 @@ +/* lpd 1.6 - Printer daemon Author: Kees J. Bot + * 3 Dec 1989 + */ +#define nil 0 +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> +#include <termcap.h> + +char PRINTER[] = "/dev/lp"; +char SPOOL[] = "/usr/spool/lpd"; +char LOG[] = "/dev/log"; + +void report(char *mess) +{ + fprintf(stderr, "lpd: %s: %s\n", mess, strerror(errno)); +} + +void fatal(char *mess) +{ + report(mess); + exit(1); +} + +char jobX[] = "jobXXXXXX"; +char tmpX[] = "tmpXXXXXX"; + +void spoolerr(char *file) +{ + int e= errno; + + unlink(jobX); + unlink(tmpX); + fatal(file); +} + +void spool(char *path) +/* Place a file into the spool directory, either by copying it, or by leaving + * a reference. + */ +{ + char *file; + int j, u; + + mktemp(jobX); + file= mktemp(tmpX); + + if (path[0] == '/') { + int f; + + if ((f= open(path, O_RDONLY)) >= 0) { + close(f); + file= path; + } + } + if (file != path) { + int c; + FILE *t; + + if ((t= fopen(tmpX, "w")) == nil) spoolerr(tmpX); + + while ((c= getchar()) != EOF && putc(c, t) != EOF) {} + + if (ferror(stdin)) spoolerr(path); + + if (ferror(t) || fclose(t) == EOF) spoolerr(tmpX); + + fclose(stdin); + } + + if ((j= open(jobX, O_WRONLY|O_CREAT|O_EXCL, 0000)) < 0) spoolerr(jobX); + + u= getuid(); + if (write(j, file, strlen(file)+1) < 0 + || write(j, &u, sizeof(u)) < 0 + || write(j, path, strlen(path)+1) < 0 + || close(j) < 0 + || chmod(jobX, 0600) < 0 + ) spoolerr(jobX); +} + +struct job { + struct job *next; + time_t age; + char name[sizeof(jobX)]; +} *jobs = nil; + +int job(void) +/* Look for print jobs in the spool directory. Make a list of them sorted + * by age. Return true iff the list is nonempty. + */ +{ + DIR *spool; + struct dirent *entry; + struct job *newjob, **ajob; + struct stat st; + + if (jobs != nil) return 1; + + if ((spool= opendir(".")) == nil) fatal(SPOOL); + + while ((entry= readdir(spool)) != nil) { + if (strncmp(entry->d_name, "job", 3) != 0) continue; + + if (stat(entry->d_name, &st) < 0 + || (st.st_mode & 0777) == 0000) continue; + + if ((newjob= malloc(sizeof(*newjob))) == nil) fatal("malloc()"); + newjob->age = st.st_mtime; + strcpy(newjob->name, entry->d_name); + + ajob= &jobs; + while (*ajob != nil && (*ajob)->age < newjob->age) + ajob= &(*ajob)->next; + + newjob->next= *ajob; + *ajob= newjob; + } + closedir(spool); + + return jobs != nil; +} + +/* What to do with control-X: + * 0 ignore, + * 1 give up on controlling the printer, assume user knows how printer works, + * 2 print. + */ +char control[] = { + 0, 1, 1, 1, 1, 1, 1, 0, /* \0, \a don't show. */ + 2, 2, 2, 1, 2, 2, 1, 1, /* \b, \t, \n, \f, \r */ + 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1 +}; + +int lp; +char buf[BUFSIZ]; +int count, column, line, ncols = 80, nlines = 66; + +int flush(void) +/* Copy the characters in the output buffer to the printer, with retries if + * out of paper. + */ +{ + char *bp= buf; + + while (count > 0) { + int retry = 0, complain = 0; + int r; + + while ((r= write(lp, bp, count)) < 0) { + if (errno != EAGAIN) fatal(PRINTER); + if (retry == complain) { + fprintf(stderr, + "lpd: %s: Printer out of paper\n", + PRINTER); + complain= retry + 60; + } + sleep(1); + retry++; + } + bp+= r; + count-= r; + } + count = 0; +} + +int put(int c) +/* Send characters to the output buffer to be printed and do so if the buffer + * is full. Track the position of the write-head in `column' and `line'. + */ +{ + buf[count++] = c; + + switch (c) { + case '\f': + column = 0; + line = 0; + break; + case '\r': + column = 0; + break; + case '\n': + line++; + break; + case '\b': + column--; + break; + default: + if (++column > ncols) { line++; column= 1; } + } + if (line == nlines) line= 0; + + if (count == BUFSIZ) flush(); +} + +void print(FILE *f) +/* Send the contents of an open file to the printer. Expand tabs and change + * linefeed to a carriage-return linefeed sequence. Print a formfeed at the + * end if needed to reach the top of the next page. If a control character + * is printed that we do not know about, then the user is assumed to know + * what they are doing, so output processing is disabled. + */ +{ + int c; + + count= column= line= 0; + + while ((c= getc(f)) != EOF) { + if (c < ' ') { + switch (control[c]) { + case 0: continue; /* Ignore this one. */ + case 1: + /* Can't handle this junk, assume smart user. */ + do { + buf[count++] = c; + if (count == BUFSIZ) flush(); + } while ((c= getc(f)) != EOF); + + flush(); + return; + case 2: /* fine */; + } + } + + switch (c) { + case '\n': + put('\r'); + put('\n'); + break; + case '\t': + do { + put(' '); + } while (column & 07); + break; + case '\b': + if (column > 0) put(c); + break; + default: + put(c); + } + } + if (column > 0) { put('\r'); put('\n'); } + if (line > 0) put('\f'); + flush(); + return; +} + +void joberr(char *job) +{ + fprintf(stderr, "lpd: something is wrong with %s\n", job); + + if (unlink(job) < 0) fatal("can't remove it"); +} + +void work(void) +/* Print all the jobs in the job list. */ +{ + FILE *j, *f; + char file[PATH_MAX+1], *pf=file; + int c; + struct job *job; + + job= jobs; + jobs= jobs->next; + + if ((j= fopen(job->name, "r")) == nil) { + joberr(job->name); + return; + } + + do { + if (pf == file + sizeof(file) || (c= getc(j)) == EOF) { + fclose(j); + joberr(job->name); + return; + } + *pf++ = c; + } while (c != 0); + + fclose(j); + + if ((f= fopen(file, "r")) == nil) + fprintf(stderr, "lpd: can't read %s\n", file); + else { + print(f); + fclose(f); + } + if (file[0] != '/' && unlink(file) < 0) report(file); + + if (unlink(job->name) < 0) fatal(job->name); + free(job); +} + +void getcap(void) +/* Find the line printer dimensions in the termcap database under "lp". */ +{ + char printcap[1024]; + int n; + + if (tgetent(printcap, "lp") == 1) { + if ((n= tgetnum("co")) > 0) ncols= n; + if ((n= tgetnum("li")) > 0) nlines= n; + } +} + +void haunt(void) +/* Become a daemon, print jobs while there are any, exit. */ +{ + int fd; + + if ((fd= open("/dev/tty", O_RDONLY)) != -1) { + /* We have a controlling tty! Disconnect. */ + close(fd); + + switch(fork()) { + case -1: fatal("can't fork"); + case 0: break; + default: exit(0); + } + + if ((fd= open("/dev/null", O_RDONLY)) < 0) fatal("/dev/null"); + dup2(fd, 0); + close(fd); + if ((fd= open(LOG, O_WRONLY)) < 0) fatal(LOG); + dup2(fd, 1); + dup2(fd, 2); + close(fd); + setsid(); + } + + getcap(); + + do { + if ((lp= open(PRINTER, O_WRONLY)) < 0) { + /* Another lpd? */ + if (errno == EBUSY) exit(0); + fatal(PRINTER); + } + + while (job()) work(); + + close(lp); + } while (job()); +} + +int main(int argc, char **argv) +{ + if (argc > 2) { + fprintf(stderr, "Usage: %s [path | stdin < path]\n", argv[0]); + exit(1); + } + + umask(0077); + + if (chdir(SPOOL) < 0) fatal(SPOOL); + + if (argc == 2) spool(argv[1]); + + haunt(); +} diff --git a/commands/simple/ls.c b/commands/simple/ls.c new file mode 100755 index 000000000..4c45c3188 --- /dev/null +++ b/commands/simple/ls.c @@ -0,0 +1,1150 @@ +/* ls 5.4 - List files. Author: Kees J. Bot + * 25 Apr 1989 + * + * About the amount of bytes for heap + stack under Minix: + * Ls needs a average amount of 42 bytes per unserviced directory entry, so + * scanning 10 directory levels deep in an ls -R with 100 entries per directory + * takes 42000 bytes of heap. So giving ls 10000 bytes is tight, 20000 is + * usually enough, 40000 is pessimistic. + */ + +/* The array l_ifmt[] is used in an 'ls -l' to map the type of a file to a + * letter. This is done so that ls can list any future file or device type + * other than symlinks, without recompilation. (Yes it's dirty.) + */ +char l_ifmt[] = "0pcCd?bB-?l?s???"; + +#define ifmt(mode) l_ifmt[((mode) >> 12) & 0xF] + +#define nil 0 +#include <stdio.h> +#include <string.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> +#include <dirent.h> +#include <time.h> +#include <pwd.h> +#include <grp.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <termios.h> +#include <sys/ioctl.h> + +#ifndef major +#define major(dev) ((int) (((dev) >> 8) & 0xFF)) +#define minor(dev) ((int) (((dev) >> 0) & 0xFF)) +#endif + +#if !__minix +#define SUPER_ID uid /* Let -A flag be default for SUPER_ID == 0. */ +#else +#define SUPER_ID gid +#endif + +#ifdef S_IFLNK +int (*status)(const char *file, struct stat *stp); +#else +#define status stat +#endif + +/* Basic disk block size is 512 except for one niche O.S. */ +#if __minix +#define BLOCK 1024 +#else +#define BLOCK 512 +#endif + +/* Assume other systems have st_blocks. */ +#if !__minix +#define ST_BLOCKS 1 +#endif + +/* Some terminals ignore more than 80 characters on a line. Dumb ones wrap + * when the cursor hits the side. Nice terminals don't wrap until they have + * to print the 81st character. Whether we like it or not, no column 80. + */ +int ncols= 79; + +#define NSEP 3 /* # spaces between columns. */ + +#define MAXCOLS 128 /* Max # of files per line. */ + +char *arg0; /* Last component of argv[0]. */ +int uid, gid; /* callers id. */ +int ex= 0; /* Exit status to be. */ +int istty; /* Output is on a terminal. */ + +/* Safer versions of malloc and realloc: */ + +void heaperr(void) +{ + fprintf(stderr, "%s: Out of memory\n", arg0); + exit(-1); +} + +void *allocate(size_t n) +/* Deliver or die. */ +{ + void *a; + + if ((a= malloc(n)) == nil) heaperr(); + return a; +} + +void *reallocate(void *a, size_t n) +{ + if ((a= realloc(a, n)) == nil) heaperr(); + return a; +} + +char allowed[] = "acdfghilnpqrstu1ACDFLMRTX"; +char flags[sizeof(allowed)]; + +char arg0flag[] = "cdfmrtx"; /* These in argv[0] go to upper case. */ + +void setflags(char *flgs) +{ + int c; + + while ((c= *flgs++) != 0) { + if (strchr(allowed, c) == nil) { + fprintf(stderr, "Usage: %s [-%s] [file ...]\n", + arg0, allowed); + exit(1); + } else + if (strchr(flags, c) == nil) { + flags[strlen(flags)] = c; + } + } +} + +int present(int f) +{ + return f == 0 || strchr(flags, f) != nil; +} + +void report(char *f) +/* Like perror(3), but in the style: "ls: junk: No such file or directory. */ +{ + fprintf(stderr, "%s: %s: %s\n", arg0, f, strerror(errno)); + ex= 1; +} + +/* Two functions, uidname and gidname, translate id's to readable names. + * All names are remembered to avoid searching the password file. + */ +#define NNAMES (1 << (sizeof(int) + sizeof(char *))) +enum whatmap { PASSWD, GROUP }; + +struct idname { /* Hash list of names. */ + struct idname *next; + char *name; + uid_t id; +} *uids[NNAMES], *gids[NNAMES]; + +char *idname(unsigned id, enum whatmap map) +/* Return name for a given user/group id. */ +{ + struct idname *i; + struct idname **ids= &(map == PASSWD ? uids : gids)[id % NNAMES]; + + while ((i= *ids) != nil && id < i->id) ids= &i->next; + + if (i == nil || id != i->id) { + /* Not found, go look in the password or group map. */ + char *name= nil; + char noname[3 * sizeof(uid_t)]; + + if (!present('n')) { + if (map == PASSWD) { + struct passwd *pw= getpwuid(id); + + if (pw != nil) name= pw->pw_name; + } else { + struct group *gr= getgrgid(id); + + if (gr != nil) name= gr->gr_name; + } + } + if (name == nil) { + /* Can't find it, weird. Use numerical "name." */ + sprintf(noname, "%u", id); + name= noname; + } + + /* Add a new id-to-name cell. */ + i= allocate(sizeof(*i)); + i->id= id; + i->name= allocate(strlen(name) + 1); + strcpy(i->name, name); + i->next= *ids; + *ids= i; + } + return i->name; +} + +#define uidname(uid) idname((uid), PASSWD) +#define gidname(gid) idname((gid), GROUP) + +/* Path name construction, addpath adds a component, delpath removes it. + * The string path is used throughout the program as the file under examination. + */ + +char *path; /* Path name constructed in path[]. */ +int plen= 0, pidx= 0; /* Lenght/index for path[]. */ + +void addpath(int *didx, char *name) +/* Add a component to path. (name may also be a full path at the first call) + * The index where the current path ends is stored in *pdi. + */ +{ + if (plen == 0) path= (char *) allocate((plen= 32) * sizeof(path[0])); + + if (pidx == 1 && path[0] == '.') pidx= 0; /* Remove "." */ + + *didx= pidx; /* Record point to go back to for delpath. */ + + if (pidx > 0 && path[pidx-1] != '/') path[pidx++]= '/'; + + do { + if (*name != '/' || pidx == 0 || path[pidx-1] != '/') { + if (pidx == plen) { + path= (char *) reallocate((void *) path, + (plen*= 2) * sizeof(path[0])); + } + path[pidx++]= *name; + } + } while (*name++ != 0); + + --pidx; /* Put pidx back at the null. The path[pidx++]= '/' + * statement will overwrite it at the next call. + */ +} + +#define delpath(didx) (path[pidx= didx]= 0) /* Remove component. */ + +int field = 0; /* (used to be) Fields that must be printed. */ + /* (now) Effects triggered by certain flags. */ + +#define L_INODE 0x0001 /* -i */ +#define L_BLOCKS 0x0002 /* -s */ +#define L_EXTRA 0x0004 /* -X */ +#define L_MODE 0x0008 /* -lMX */ +#define L_LONG 0x0010 /* -l */ +#define L_GROUP 0x0020 /* -g */ +#define L_BYTIME 0x0040 /* -tuc */ +#define L_ATIME 0x0080 /* -u */ +#define L_CTIME 0x0100 /* -c */ +#define L_MARK 0x0200 /* -F */ +#define L_MARKDIR 0x0400 /* -p */ +#define L_TYPE 0x0800 /* -D */ +#define L_LONGTIME 0x1000 /* -T */ +#define L_DIR 0x2000 /* -d */ +#define L_KMG 0x4000 /* -h */ + +struct file { /* A file plus stat(2) information. */ + struct file *next; /* Lists are made of them. */ + char *name; /* Null terminated name. */ + ino_t ino; + mode_t mode; + uid_t uid; + gid_t gid; + nlink_t nlink; + dev_t rdev; + off_t size; + time_t mtime; + time_t atime; + time_t ctime; +#if ST_BLOCKS + long blocks; +#endif +}; + +void setstat(struct file *f, struct stat *stp) +{ + f->ino= stp->st_ino; + f->mode= stp->st_mode; + f->nlink= stp->st_nlink; + f->uid= stp->st_uid; + f->gid= stp->st_gid; + f->rdev= stp->st_rdev; + f->size= stp->st_size; + f->mtime= stp->st_mtime; + f->atime= stp->st_atime; + f->ctime= stp->st_ctime; +#if ST_BLOCKS + f->blocks= stp->st_blocks; +#endif +} + +#define PAST (26*7*24*3600L) /* Half a year ago. */ +/* Between PAST and FUTURE from now a time is printed, otherwise a year. */ +#define FUTURE ( 1*7*24*3600L) /* One week. */ + +static char *timestamp(struct file *f) +/* Transform the right time field into something readable. */ +{ + struct tm *tm; + time_t t; + static time_t now; + static int drift= 0; + static char date[] = "Jan 19 03:14:07 2038"; + static char month[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; + + t= f->mtime; + if (field & L_ATIME) t= f->atime; + if (field & L_CTIME) t= f->ctime; + + tm= localtime(&t); + if (--drift < 0) { time(&now); drift= 50; } /* limit time() calls */ + + if (field & L_LONGTIME) { + sprintf(date, "%.3s %2d %02d:%02d:%02d %d", + month + 3*tm->tm_mon, + tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + 1900 + tm->tm_year); + } else + if (t < now - PAST || t > now + FUTURE) { + sprintf(date, "%.3s %2d %d", + month + 3*tm->tm_mon, + tm->tm_mday, + 1900 + tm->tm_year); + } else { + sprintf(date, "%.3s %2d %02d:%02d", + month + 3*tm->tm_mon, + tm->tm_mday, + tm->tm_hour, tm->tm_min); + } + return date; +} + +char *permissions(struct file *f) +/* Compute long or short rwx bits. */ +{ + static char rwx[] = "drwxr-x--x"; + + rwx[0] = ifmt(f->mode); + /* Note that rwx[0] is a guess for the more alien file types. It is + * correct for BSD4.3 and derived systems. I just don't know how + * "standardized" these numbers are. + */ + + if (field & L_EXTRA) { /* Short style */ + int mode = f->mode, ucase= 0; + + if (uid == f->uid) { /* What group of bits to use. */ + /* mode<<= 0, */ + ucase= (mode<<3) | (mode<<6); + /* Remember if group or others have permissions. */ + } else + if (gid == f->gid) { + mode<<= 3; + } else { + mode<<= 6; + } + rwx[1]= mode&S_IRUSR ? (ucase&S_IRUSR ? 'R' : 'r') : '-'; + rwx[2]= mode&S_IWUSR ? (ucase&S_IWUSR ? 'W' : 'w') : '-'; + + if (mode&S_IXUSR) { + static char sbit[]= { 'x', 'g', 'u', 's' }; + + rwx[3]= sbit[(f->mode&(S_ISUID|S_ISGID))>>10]; + if (ucase&S_IXUSR) rwx[3] += 'A'-'a'; + } else { + rwx[3]= f->mode&(S_ISUID|S_ISGID) ? '=' : '-'; + } + rwx[4]= 0; + } else { /* Long form. */ + char *p= rwx+1; + int mode= f->mode; + + do { + p[0] = (mode & S_IRUSR) ? 'r' : '-'; + p[1] = (mode & S_IWUSR) ? 'w' : '-'; + p[2] = (mode & S_IXUSR) ? 'x' : '-'; + mode<<= 3; + } while ((p+=3) <= rwx+7); + + if (f->mode&S_ISUID) rwx[3]= f->mode&(S_IXUSR>>0) ? 's' : '='; + if (f->mode&S_ISGID) rwx[6]= f->mode&(S_IXUSR>>3) ? 's' : '='; + if (f->mode&S_ISVTX) rwx[9]= f->mode&(S_IXUSR>>6) ? 't' : '='; + } + return rwx; +} + +void numeral(int i, char **pp) +{ + char itoa[3*sizeof(int)], *a=itoa; + + do *a++ = i%10 + '0'; while ((i/=10) > 0); + + do *(*pp)++ = *--a; while (a>itoa); +} + +#define K 1024L /* A kilobyte counts in multiples of K */ +#define T 1000L /* A megabyte in T*K, a gigabyte in T*T*K */ + +char *cxsize(struct file *f) +/* Try and fail to turn a 32 bit size into 4 readable characters. */ +{ + static char siz[] = "1.2m"; + char *p= siz; + off_t z; + + siz[1]= siz[2]= siz[3]= 0; + + if (f->size <= 5*K) { /* <= 5K prints as is. */ + numeral((int) f->size, &p); + return siz; + } + z= (f->size + K-1) / K; + + if (z <= 999) { /* Print as 123k. */ + numeral((int) z, &p); + *p = 'k'; /* Can't use 'K', looks bad */ + } else + if (z*10 <= 99*T) { /* 1.2m (Try ls -X /dev/at0) */ + z= (z*10 + T-1) / T; /* Force roundup */ + numeral((int) z / 10, &p); + *p++ = '.'; + numeral((int) z % 10, &p); + *p = 'm'; + } else + if (z <= 999*T) { /* 123m */ + numeral((int) ((z + T-1) / T), &p); + *p = 'm'; + } else { /* 1.2g */ + z= (z*10 + T*T-1) / (T*T); + numeral((int) z / 10, &p); + *p++ = '.'; + numeral((int) z % 10, &p); + *p = 'g'; + } + return siz; +} + +/* Transform size of file to number of blocks. This was once a function that + * guessed the number of indirect blocks, but that nonsense has been removed. + */ +#if ST_BLOCKS +#define nblocks(f) ((f)->blocks) +#else +#define nblocks(f) (((f)->size + BLOCK-1) / BLOCK) +#endif + +/* From number of blocks to kilobytes. */ +#if BLOCK < 1024 +#define nblk2k(nb) (((nb) + (1024 / BLOCK - 1)) / (1024 / BLOCK)) +#else +#define nblk2k(nb) ((nb) * (BLOCK / 1024)) +#endif + +static int (*CMP)(struct file *f1, struct file *f2); +static int (*rCMP)(struct file *f1, struct file *f2); + +static void mergesort(struct file **al) +/* This is either a stable mergesort, or thermal noise, I'm no longer sure. + * It must be called like this: if (L != nil && L->next != nil) mergesort(&L); + */ +{ + /* static */ struct file *l1, **mid; /* Need not be local */ + struct file *l2; + + l1= *(mid= &(*al)->next); + do { + if ((l1= l1->next) == nil) break; + mid= &(*mid)->next; + } while ((l1= l1->next) != nil); + + l2= *mid; + *mid= nil; + + if ((*al)->next != nil) mergesort(al); + if (l2->next != nil) mergesort(&l2); + + l1= *al; + for (;;) { + if ((*CMP)(l1, l2) <= 0) { + if ((l1= *(al= &l1->next)) == nil) { + *al= l2; + break; + } + } else { + *al= l2; + l2= *(al= &l2->next); + *al= l1; + if (l2 == nil) break; + } + } +} + +int namecmp(struct file *f1, struct file *f2) +{ + return strcmp(f1->name, f2->name); +} + +int mtimecmp(struct file *f1, struct file *f2) +{ + return f1->mtime == f2->mtime ? 0 : f1->mtime > f2->mtime ? -1 : 1; +} + +int atimecmp(struct file *f1, struct file *f2) +{ + return f1->atime == f2->atime ? 0 : f1->atime > f2->atime ? -1 : 1; +} + +int ctimecmp(struct file *f1, struct file *f2) +{ + return f1->ctime == f2->ctime ? 0 : f1->ctime > f2->ctime ? -1 : 1; +} + +int typecmp(struct file *f1, struct file *f2) +{ + return ifmt(f1->mode) - ifmt(f2->mode); +} + +int revcmp(struct file *f1, struct file *f2) { return (*rCMP)(f2, f1); } + +static void sort(struct file **al) +/* Sort the files according to the flags. */ +{ + if (!present('f') && *al != nil && (*al)->next != nil) { + CMP= namecmp; + + if (!(field & L_BYTIME)) { + /* Sort on name */ + + if (present('r')) { rCMP= CMP; CMP= revcmp; } + mergesort(al); + } else { + /* Sort on name first, then sort on time. */ + + mergesort(al); + if (field & L_CTIME) { + CMP= ctimecmp; + } else + if (field & L_ATIME) { + CMP= atimecmp; + } else { + CMP= mtimecmp; + } + + if (present('r')) { rCMP= CMP; CMP= revcmp; } + mergesort(al); + } + /* Separate by file type if so desired. */ + + if (field & L_TYPE) { + CMP= typecmp; + mergesort(al); + } + } +} + +struct file *newfile(char *name) +/* Create file structure for given name. */ +{ + struct file *new; + + new= (struct file *) allocate(sizeof(*new)); + new->name= strcpy((char *) allocate(strlen(name)+1), name); + return new; +} + +void pushfile(struct file **flist, struct file *new) +/* Add file to the head of a list. */ +{ + new->next= *flist; + *flist= new; +} + +void delfile(struct file *old) +/* Release old file structure. */ +{ + free((void *) old->name); + free((void *) old); +} + +struct file *popfile(struct file **flist) +/* Pop file off top of file list. */ +{ + struct file *f; + + f= *flist; + *flist= f->next; + return f; +} + +int dotflag(char *name) +/* Return flag that would make ls list this name: -a or -A. */ +{ + if (*name++ != '.') return 0; + + switch (*name++) { + case 0: return 'a'; /* "." */ + case '.': if (*name == 0) return 'a'; /* ".." */ + default: return 'A'; /* ".*" */ + } +} + +int adddir(struct file **aflist, char *name) +/* Add directory entries of directory name to a file list. */ +{ + DIR *d; + struct dirent *e; + + if (access(name, 0) < 0) { + report(name); + return 0; + } + + if ((d= opendir(name)) == nil) { + report(name); + return 0; + } + while ((e= readdir(d)) != nil) { + if (e->d_ino != 0 && present(dotflag(e->d_name))) { + pushfile(aflist, newfile(e->d_name)); + aflist= &(*aflist)->next; + } + } + closedir(d); + return 1; +} + +off_t countblocks(struct file *flist) +/* Compute total block count for a list of files. */ +{ + off_t cb = 0; + + while (flist != nil) { + switch (flist->mode & S_IFMT) { + case S_IFDIR: + case S_IFREG: +#ifdef S_IFLNK + case S_IFLNK: +#endif + cb += nblocks(flist); + } + flist= flist->next; + } + return cb; +} + +void printname(char *name) +/* Print a name with control characters as '?' (unless -q). The terminal is + * assumed to be eight bit clean. + */ +{ + int c, q= present('q'); + + while ((c= (unsigned char) *name++) != 0) { + if (q && (c < ' ' || c == 0177)) c= '?'; + putchar(c); + } +} + +int mark(struct file *f, int doit) +{ + int c; + + c= 0; + + if (field & L_MARK) { + switch (f->mode & S_IFMT) { + case S_IFDIR: c= '/'; break; +#ifdef S_IFIFO + case S_IFIFO: c= '|'; break; +#endif +#ifdef S_IFLNK + case S_IFLNK: c= '@'; break; +#endif +#ifdef S_IFSOCK + case S_IFSOCK: c= '='; break; +#endif + case S_IFREG: + if (f->mode & (S_IXUSR | S_IXGRP | S_IXOTH)) c= '*'; + break; + } + } else + if (field & L_MARKDIR) { + if (S_ISDIR(f->mode)) c= '/'; + } + + if (doit && c != 0) putchar(c); + return c; +} + +/* Width of entire column, and of several fields. */ +enum { W_COL, W_INO, W_BLK, W_NLINK, W_UID, W_GID, W_SIZE, W_NAME, MAXFLDS }; + +unsigned char fieldwidth[MAXCOLS][MAXFLDS]; + +void maxise(unsigned char *aw, int w) +/* Set *aw to the larger of it and w. */ +{ + if (w > *aw) { + if (w > UCHAR_MAX) w= UCHAR_MAX; + *aw= w; + } +} + +int numwidth(unsigned long n) +/* Compute width of 'n' when printed. */ +{ + int width= 0; + + do { width++; } while ((n /= 10) > 0); + return width; +} + +#if !__minix +int numxwidth(unsigned long n) +/* Compute width of 'n' when printed in hex. */ +{ + int width= 0; + + do { width++; } while ((n /= 16) > 0); + return width; +} +#endif + +static int nsp= 0; /* This many spaces have not been printed yet. */ +#define spaces(n) (nsp= (n)) +#define terpri() (nsp= 0, putchar('\n')) /* No trailing spaces */ + +void print1(struct file *f, int col, int doit) +/* Either compute the number of spaces needed to print file f (doit == 0) or + * really print it (doit == 1). + */ +{ + int width= 0, n; + char *p; + unsigned char *f1width = fieldwidth[col]; + + while (nsp>0) { putchar(' '); nsp--; }/* Fill gap between two columns */ + + if (field & L_INODE) { + if (doit) { + printf("%*d ", f1width[W_INO], f->ino); + } else { + maxise(&f1width[W_INO], numwidth(f->ino)); + width++; + } + } + if (field & L_BLOCKS) { + unsigned long nb= nblk2k(nblocks(f)); + if (doit) { + printf("%*lu ", f1width[W_BLK], nb); + } else { + maxise(&f1width[W_BLK], numwidth(nb)); + width++; + } + } + if (field & L_MODE) { + if (doit) { + printf("%s ", permissions(f)); + } else { + width+= (field & L_EXTRA) ? 5 : 11; + } + } + if (field & L_EXTRA) { + p= cxsize(f); + n= strlen(p)+1; + + if (doit) { + n= f1width[W_SIZE] - n; + while (n > 0) { putchar(' '); --n; } + printf("%s ", p); + } else { + maxise(&f1width[W_SIZE], n); + } + } + if (field & L_LONG) { + if (doit) { + printf("%*u ", f1width[W_NLINK], (unsigned) f->nlink); + } else { + maxise(&f1width[W_NLINK], numwidth(f->nlink)); + width++; + } + if (!(field & L_GROUP)) { + if (doit) { + printf("%-*s ", f1width[W_UID], + uidname(f->uid)); + } else { + maxise(&f1width[W_UID], + strlen(uidname(f->uid))); + width+= 2; + } + } + if (doit) { + printf("%-*s ", f1width[W_GID], gidname(f->gid)); + } else { + maxise(&f1width[W_GID], strlen(gidname(f->gid))); + width+= 2; + } + + switch (f->mode & S_IFMT) { + case S_IFBLK: + case S_IFCHR: +#ifdef S_IFMPB + case S_IFMPB: +#endif +#ifdef S_IFMPC + case S_IFMPC: +#endif +#if __minix + if (doit) { + printf("%*d, %3d ", f1width[W_SIZE] - 5, + major(f->rdev), minor(f->rdev)); + } else { + maxise(&f1width[W_SIZE], + numwidth(major(f->rdev)) + 5); + width++; + } +#else /* !__minix */ + if (doit) { + printf("%*lX ", f1width[W_SIZE], + (unsigned long) f->rdev); + } else { + maxise(&f1width[W_SIZE], numwidth(f->rdev)); + width++; + } +#endif /* !__minix */ + break; + default: + if (field & L_KMG) { + p= cxsize(f); + n= strlen(p)+1; + + if (doit) { + n= f1width[W_SIZE] - n; + while (n > 0) { putchar(' '); --n; } + printf("%s ", p); + } else { + maxise(&f1width[W_SIZE], n); + } + } else { + if (doit) { + printf("%*lu ", f1width[W_SIZE], + (unsigned long) f->size); + } else { + maxise(&f1width[W_SIZE], + numwidth(f->size)); + width++; + } + } + } + + if (doit) { + printf("%s ", timestamp(f)); + } else { + width+= (field & L_LONGTIME) ? 21 : 13; + } + } + + n= strlen(f->name); + if (doit) { + printname(f->name); + if (mark(f, 1) != 0) n++; +#ifdef S_IFLNK + if ((field & L_LONG) && (f->mode & S_IFMT) == S_IFLNK) { + char *buf; + int r, didx; + + buf= (char *) allocate(((size_t) f->size + 1) + * sizeof(buf[0])); + addpath(&didx, f->name); + r= readlink(path, buf, (int) f->size); + delpath(didx); + if (r > 0) buf[r] = 0; else r=1, strcpy(buf, "?"); + printf(" -> "); + printname(buf); + free((void *) buf); + n+= 4 + r; + } +#endif + spaces(f1width[W_NAME] - n); + } else { + if (mark(f, 0) != 0) n++; +#ifdef S_IFLNK + if ((field & L_LONG) && (f->mode & S_IFMT) == S_IFLNK) { + n+= 4 + (int) f->size; + } +#endif + maxise(&f1width[W_NAME], n + NSEP); + + for (n= 1; n < MAXFLDS; n++) width+= f1width[n]; + maxise(&f1width[W_COL], width); + } +} + +int countfiles(struct file *flist) +/* Return number of files in the list. */ +{ + int n= 0; + + while (flist != nil) { n++; flist= flist->next; } + + return n; +} + +struct file *filecol[MAXCOLS]; /* filecol[i] is list of files for column i. */ +int nfiles, nlines; /* # files to print, # of lines needed. */ + +void columnise(struct file *flist, int nplin) +/* Chop list of files up in columns. Note that 3 columns are used for 5 files + * even though nplin may be 4, filecol[3] will simply be nil. + */ +{ + int i, j; + + nlines= (nfiles + nplin - 1) / nplin; /* nlines needed for nfiles */ + + filecol[0]= flist; + + for (i=1; i<nplin; i++) { /* Give nlines files to each column. */ + for (j=0; j<nlines && flist != nil; j++) flist= flist->next; + + filecol[i]= flist; + } +} + +int print(struct file *flist, int nplin, int doit) +/* Try (doit == 0), or really print the list of files over nplin columns. + * Return true if it can be done in nplin columns or if nplin == 1. + */ +{ + register struct file *f; + register int col, fld, totlen; + + columnise(flist, nplin); + + if (!doit) { + for (col= 0; col < nplin; col++) { + for (fld= 0; fld < MAXFLDS; fld++) { + fieldwidth[col][fld]= 0; + } + } + } + + while (--nlines >= 0) { + totlen= 0; + + for (col= 0; col < nplin; col++) { + if ((f= filecol[col]) != nil) { + filecol[col]= f->next; + print1(f, col, doit); + } + if (!doit && nplin > 1) { + /* See if this line is not too long. */ + if (fieldwidth[col][W_COL] == UCHAR_MAX) { + return 0; + } + totlen+= fieldwidth[col][W_COL]; + if (totlen > ncols+NSEP) return 0; + } + } + if (doit) terpri(); + } + return 1; +} + +enum depth { SURFACE, SURFACE1, SUBMERGED }; +enum state { BOTTOM, SINKING, FLOATING }; + +void listfiles(struct file *flist, enum depth depth, enum state state) +/* Main workhorse of ls, it sorts and prints the list of files. Flags: + * depth: working with the command line / just one file / listing dir. + * state: How "recursive" do we have to be. + */ +{ + struct file *dlist= nil, **afl= &flist, **adl= &dlist; + int nplin; + static int white = 1; /* Nothing printed yet. */ + + /* Flush everything previously printed, so new error output will + * not intermix with files listed earlier. + */ + fflush(stdout); + + if (field != 0 || state != BOTTOM) { /* Need stat(2) info. */ + while (*afl != nil) { + static struct stat st; + int r, didx; + + addpath(&didx, (*afl)->name); + + if ((r= status(path, &st)) < 0 +#ifdef S_IFLNK + && (status == lstat || lstat(path, &st) < 0) +#endif + ) { + if (depth != SUBMERGED || errno != ENOENT) + report((*afl)->name); + delfile(popfile(afl)); + } else { + setstat(*afl, &st); + afl= &(*afl)->next; + } + delpath(didx); + } + } + sort(&flist); + + if (depth == SUBMERGED && (field & (L_BLOCKS | L_LONG))) { + printf("total %ld\n", nblk2k(countblocks(flist))); + } + + if (state == SINKING || depth == SURFACE1) { + /* Don't list directories themselves, list their contents later. */ + afl= &flist; + while (*afl != nil) { + if (((*afl)->mode & S_IFMT) == S_IFDIR) { + pushfile(adl, popfile(afl)); + adl= &(*adl)->next; + } else { + afl= &(*afl)->next; + } + } + } + + if ((nfiles= countfiles(flist)) > 0) { + /* Print files in how many columns? */ + nplin= !present('C') ? 1 : nfiles < MAXCOLS ? nfiles : MAXCOLS; + + while (!print(flist, nplin, 0)) nplin--; /* Try first */ + + print(flist, nplin, 1); /* Then do it! */ + white = 0; + } + + while (flist != nil) { /* Destroy file list */ + if (state == FLOATING && (flist->mode & S_IFMT) == S_IFDIR) { + /* But keep these directories for ls -R. */ + pushfile(adl, popfile(&flist)); + adl= &(*adl)->next; + } else { + delfile(popfile(&flist)); + } + } + + while (dlist != nil) { /* List directories */ + if (dotflag(dlist->name) != 'a' || depth != SUBMERGED) { + int didx; + + addpath(&didx, dlist->name); + + flist= nil; + if (adddir(&flist, path)) { + if (depth != SURFACE1) { + if (!white) putchar('\n'); + printf("%s:\n", path); + white = 0; + } + listfiles(flist, SUBMERGED, + state == FLOATING ? FLOATING : BOTTOM); + } + delpath(didx); + } + delfile(popfile(&dlist)); + } +} + +int main(int argc, char **argv) +{ + struct file *flist= nil, **aflist= &flist; + enum depth depth; + char *lsflags; + struct winsize ws; + + uid= geteuid(); + gid= getegid(); + + if ((arg0= strrchr(argv[0], '/')) == nil) arg0= argv[0]; else arg0++; + argv++; + + if (strcmp(arg0, "ls") != 0) { + char *p= arg0+1; + + while (*p != 0) { + if (strchr(arg0flag, *p) != nil) *p += 'A' - 'a'; + p++; + } + setflags(arg0+1); + } + while (*argv != nil && (*argv)[0] == '-') { + if ((*argv)[1] == '-' && (*argv)[2] == 0) { + argv++; + break; + } + setflags(*argv++ + 1); + } + + istty= isatty(1); + + if (istty && (lsflags= getenv("LSOPTS")) != nil) { + if (*lsflags == '-') lsflags++; + setflags(lsflags); + } + + if (!present('1') && !present('C') && !present('l') + && (istty || present('M') || present('X') || present('F')) + ) setflags("C"); + + if (istty) setflags("q"); + + if (SUPER_ID == 0 || present('a')) setflags("A"); + + if (present('i')) field|= L_INODE; + if (present('s')) field|= L_BLOCKS; + if (present('M')) field|= L_MODE; + if (present('X')) field|= L_EXTRA | L_MODE; + if (present('t')) field|= L_BYTIME; + if (present('u')) field|= L_ATIME; + if (present('c')) field|= L_CTIME; + if (present('l')) field|= L_MODE | L_LONG; + if (present('g')) field|= L_MODE | L_LONG | L_GROUP; + if (present('F')) field|= L_MARK; + if (present('p')) field|= L_MARKDIR; + if (present('D')) field|= L_TYPE; + if (present('T')) field|= L_MODE | L_LONG | L_LONGTIME; + if (present('d')) field|= L_DIR; + if (present('h')) field|= L_KMG; + if (field & L_LONG) field&= ~L_EXTRA; + +#ifdef S_IFLNK + status= present('L') ? stat : lstat; +#endif + + if (present('C')) { + int t= istty ? 1 : open("/dev/tty", O_WRONLY); + + if (t >= 0 && ioctl(t, TIOCGWINSZ, &ws) == 0 && ws.ws_col > 0) + ncols= ws.ws_col - 1; + + if (t != 1 && t != -1) close(t); + } + + depth= SURFACE; + + if (*argv == nil) { + if (!(field & L_DIR)) depth= SURFACE1; + pushfile(aflist, newfile(".")); + } else { + if (argv[1] == nil && !(field & L_DIR)) depth= SURFACE1; + + do { + pushfile(aflist, newfile(*argv++)); + aflist= &(*aflist)->next; + } while (*argv!=nil); + } + listfiles(flist, depth, + (field & L_DIR) ? BOTTOM : present('R') ? FLOATING : SINKING); + return ex; +} diff --git a/commands/simple/mail.c b/commands/simple/mail.c new file mode 100755 index 000000000..4c84c21b8 --- /dev/null +++ b/commands/simple/mail.c @@ -0,0 +1,776 @@ +/* mail - send/receive mail Author: Peter S. Housel */ +/* Version 0.2 of September 1990: added -e, -t, * options - cwr */ + +/* 2003-07-18: added -s option - ASW */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#undef EOF /* temporary hack */ +#include <signal.h> +#include <pwd.h> +#include <time.h> +#include <setjmp.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <unistd.h> +#include <sys/wait.h> +#include <stdio.h> + +#ifdef DEBUG +#define D(Q) (Q) +#else +#define D(Q) +#endif + +#define SHELL "/bin/sh" + +#define DROPNAME "/usr/spool/mail/%s" +#define LOCKNAME "/usr/spool/mail/%s.lock" +#define LOCKWAIT 5 /* seconds to wait after collision */ +#define LOCKTRIES 4 /* maximum number of collisions */ + +#define MBOX "mbox" + +#define HELPFILE "/usr/lib/mail.help" +#define PROMPT "? " +#define PATHLEN 80 +#define MAXRCPT 100 /* maximum number of recipients */ +#define LINELEN 512 + +/* #define MAILER "/usr/bin/smail" */ /* smart mailer */ +#define MAILERARGS /* (unused) */ + +#define UNREAD 1 /* 'not read yet' status */ +#define DELETED 2 /* 'deleted' status */ +#define READ 3 /* 'has been read' status */ + +struct letter { + struct letter *prev, *next; /* linked letter list */ + int status; /* letter status */ + off_t location; /* location within mailbox file */ +}; + +struct letter *firstlet, *lastlet; + +int usemailer = 1; /* use MAILER to deliver (if any) */ +int printmode = 0; /* print-and-exit mode */ +int quitmode = 0; /* take interrupts */ +int reversemode = 0; /* print mailbox in reverse order */ +int usedrop = 1; /* read the maildrop (no -f given) */ +int verbose = 0; /* pass "-v" flag on to mailer */ +int needupdate = 0; /* need to update mailbox */ +int msgstatus = 0; /* return the mail status */ +int distlist = 0; /* include distribution list */ +char mailbox[PATHLEN]; /* user's mailbox/maildrop */ +char tempname[PATHLEN] = "/tmp/mailXXXXXX"; /* temporary file */ +char *subject = NULL; +FILE *boxfp = NULL; /* mailbox file */ +jmp_buf printjump; /* for quitting out of letters */ +unsigned oldmask; /* saved umask() */ + +extern int optind; +extern char *optarg; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(int deliver, (int count, char *vec [])); +_PROTOTYPE(FILE *makerewindable, (void)); +_PROTOTYPE(int copy, (FILE *fromfp, FILE *tofp)); +_PROTOTYPE(void readbox, (void)); +_PROTOTYPE(void printall, (void)); +_PROTOTYPE(void interact, (void)); +_PROTOTYPE(void onint, (int dummy)); +_PROTOTYPE(void savelet, (struct letter *let, char *savefile)); +_PROTOTYPE(void updatebox, (void)); +_PROTOTYPE(void printlet, (struct letter *let, FILE *tofp)); +_PROTOTYPE(void doshell, (char *command)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(char *basename, (char *name)); +_PROTOTYPE(char *whoami, (void)); +_PROTOTYPE(void dohelp, (void)); +_PROTOTYPE(int filesize, (char *name)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int c; + + if ('l' == (basename(argv[0]))[0]) /* 'lmail' link? */ + usemailer = 0; /* yes, let's deliver it */ + + (void) mktemp(tempname); /* name the temp file */ + + oldmask = umask(022); /* change umask for security */ + + while (EOF != (c = getopt(argc, argv, "epqrf:tdvs:"))) switch (c) { + case 'e': ++msgstatus; break; + + case 't': ++distlist; break; + + case 'p': ++printmode; break; + + case 'q': ++quitmode; break; + + case 'r': ++reversemode; break; + + case 'f': + setuid(getuid()); /* won't need to lock */ + usedrop = 0; + strncpy(mailbox, optarg, (size_t)(PATHLEN - 1)); + break; + + case 'd': usemailer = 0; break; + + case 'v': ++verbose; break; + + case 's': subject = optarg; break; + + default: + usage(); + exit(1); + } + + if (optind < argc) { + if (deliver(argc - optind, argv + optind) < 0) + exit(1); + else + exit(0); + } + if (usedrop) sprintf(mailbox, DROPNAME, whoami()); + + D(printf("mailbox=%s\n", mailbox)); + + if (msgstatus) { + if (filesize(mailbox)) + exit(0); + else + exit(1); + } + + readbox(); + + if (printmode) + printall(); + else + interact(); + + if (needupdate) updatebox(); + + return(0); +} + +int deliver(count, vec) +int count; +char *vec[]; +{ + int i, j; + int errs = 0; /* count of errors */ + int dropfd; /* file descriptor for user's drop */ + int created = 0; /* true if we created the maildrop */ + FILE *mailfp; /* fp for mail */ + struct stat stb; /* for checking drop modes, owners */ +#ifdef __STDC__ + void (*sigint)(int), (*sighup)(int), (*sigquit)(int);/* saving signal state */ +#else + void (*sigint) (), (*sighup) (), (*sigquit) (); /* saving signal state */ +#endif + time_t now; /* for datestamping the postmark */ + char sender[32]; /* sender's login name */ + char lockname[PATHLEN]; /* maildrop lock */ + int locktries; /* tries when box is locked */ + struct passwd *pw; /* sender and recipent */ + int to_console; /* deliver to console if everything fails */ + + if (count > MAXRCPT) { + fprintf(stderr, "mail: too many recipients\n"); + return -1; + } +#ifdef MAILER + if (usemailer) { + char *argvec[MAXRCPT + 3]; + char **argp; + + setuid(getuid()); + + argp = argvec; + *argp++ = "send-mail"; + if (verbose) *argp++ = "-v"; + + for (i = 0; i < count; ++i) *argp++ = vec[i]; + + *argp = NULL; + execv(MAILER, argvec); + fprintf(stderr, "mail: couldn't exec %s\n", MAILER); + return -1; + } +#endif /* MAILER */ + + if (NULL == (pw = getpwuid(getuid()))) { + fprintf(stderr, "mail: unknown sender\n"); + return -1; + } + strcpy(sender, pw->pw_name); + + /* If we need to rewind stdin and it isn't rewindable, make a copy */ + if (isatty(0) || (count > 1 && lseek(0, 0L, 0) == (off_t) -1)) { + mailfp = makerewindable(); + } else + mailfp = stdin; + + /* Shut off signals during the delivery */ + sigint = signal(SIGINT, SIG_IGN); + sighup = signal(SIGHUP, SIG_IGN); + sigquit = signal(SIGQUIT, SIG_IGN); + + for (i = 0; i < count; ++i) { + if (count > 1) rewind(mailfp); + + D(printf("deliver to %s\n", vec[i])); + + if (NULL == (pw = getpwnam(vec[i]))) { + fprintf(stderr, "mail: user %s not known\n", vec[i]); + ++errs; + continue; + } + sprintf(mailbox, DROPNAME, pw->pw_name); + sprintf(lockname, LOCKNAME, pw->pw_name); + + D(printf("maildrop='%s', lock='%s'\n", mailbox, lockname)); + + /* Lock the maildrop while we're messing with it. Races are + * possible (though not very likely) when we have to create + * the maildrop, but not otherwise. If the box is already + * locked, wait awhile and try again. */ + locktries = created = to_console = 0; +trylock: + if (link(mailbox, lockname) != 0) { + if (ENOENT == errno) { /* user doesn't have a drop yet */ + dropfd = creat(mailbox, 0600); + if (dropfd < 0 && errno == ENOENT) { + /* Probably missing spool dir; to console. */ + boxfp = fopen("/dev/console", "w"); + if (boxfp != NULL) { + to_console = 1; + goto nobox; + } + } + if (dropfd < 0) { + fprintf(stderr, "mail: couln't create a maildrop for user %s\n", + vec[i]); + ++errs; + continue; + } + ++created; + goto trylock; + } else { /* somebody else has it locked, it seems - + * wait */ + if (++locktries >= LOCKTRIES) { + fprintf(stderr, "mail: couldn't lock maildrop for user %s\n", + vec[i]); + ++errs; + continue; + } + sleep(LOCKWAIT); + goto trylock; + } + } + if (created) { + (void) chown(mailbox, pw->pw_uid, pw->pw_gid); + boxfp = fdopen(dropfd, "a"); + } else + boxfp = fopen(mailbox, "a"); + + if (NULL == boxfp || stat(mailbox, &stb) < 0) { + fprintf(stderr, "mail: serious maildrop problems for %s\n", vec[i]); + unlink(lockname); + ++errs; + continue; + } + if (stb.st_uid != pw->pw_uid || (stb.st_mode & S_IFMT) != S_IFREG) { + fprintf(stderr, "mail: mailbox for user %s is illegal\n", vec[i]); + unlink(lockname); + ++errs; + continue; + } +nobox: + if (to_console) { + fprintf(boxfp, + "-------------\n| Mail from %s to %s\n-------------\n", + sender, vec[i]); + } else { + (void) time(&now); + fprintf(boxfp, "From %s %24.24s\n", sender, ctime(&now)); + } + + /* Add the To: header line */ + fprintf(boxfp, "To: %s\n", vec[i]); + + if (distlist) { + fprintf(boxfp, "Dist: "); + for (j = 0; j < count; ++j) + if (getpwnam(vec[j]) != NULL && j != i) + fprintf(boxfp, "%s ", vec[j]) ; + fprintf(boxfp, "\n"); + } + + /* Add the Subject: header line */ + if (subject != NULL) fprintf(boxfp, "Subject: %s\n", subject); + + fprintf(boxfp, "\n"); + + if ((copy(mailfp, boxfp) < 0) || (fclose(boxfp) != 0)) { + fprintf(stderr, "mail: error delivering to user %s", vec[i]); + perror(" "); + ++errs; + } + unlink(lockname); + } + + fclose(mailfp); + + /* Put signals back the way they were */ + signal(SIGINT, sigint); + signal(SIGHUP, sighup); + signal(SIGQUIT, sigquit); + + return(0 == errs) ? 0 : -1; +} + +/* 'stdin' isn't rewindable. Make a temp file that is. + * Note that if one wanted to catch SIGINT and write a '~/dead.letter' + * for interactive mails, this might be the place to do it (though the + * case where a MAILER is being used would also need to be handled). + */ +FILE *makerewindable() +{ + FILE *tempfp; /* temp file used for copy */ + int c; /* character being copied */ + int state; /* ".\n" detection state */ + + if (NULL == (tempfp = fopen(tempname, "w"))) { + fprintf(stderr, "mail: can't create temporary file\n"); + return NULL; + } + + /* Here we copy until we reach the end of the letter (end of file or + * a line containing only a '.'), painstakingly avoiding setting a + * line length limit. */ + state = '\n'; + while (EOF != (c = getc(stdin))) switch (state) { + case '\n': + if ('.' == c) + state = '.'; + else { + if ('\n' != c) state = '\0'; + putc(c, tempfp); + } + break; + case '.': + if ('\n' == c) goto done; + state = '\0'; + putc('.', tempfp); + putc(c, tempfp); + break; + default: + state = ('\n' == c) ? '\n' : '\0'; + putc(c, tempfp); + } +done: + if (ferror(tempfp) || fclose(tempfp)) { + fprintf(stderr, "mail: couldn't copy letter to temporary file\n"); + return NULL; + } + tempfp = freopen(tempname, "r", stdin); + unlink(tempname); /* unlink name; file lingers on in limbo */ + return tempfp; +} + +int copy(fromfp, tofp) +FILE *fromfp, *tofp; +{ + int c; /* character being copied */ + int state; /* ".\n" and postmark detection state */ + int blankline = 0; /* was most recent line completely blank? */ + static char postmark[] = "From "; + char *p, *q; + + /* Here we copy until we reach the end of the letter (end of file or + * a line containing only a '.'). Postmarks (lines beginning with + * "From ") are copied with a ">" prepended. Here we also complicate + * things by not setting a line limit. */ + state = '\n'; + p = postmark; + while (EOF != (c = getc(fromfp))) { + switch (state) { + case '\n': + if ('.' == c) /* '.' at BOL */ + state = '.'; + else if (*p == c) { /* start of postmark */ + ++p; + state = 'P'; + } else { /* anything else */ + if ('\n' == c) + blankline = 1; + else { + state = '\0'; + blankline = 0; + } + putc(c, tofp); + } + break; + case '.': + if ('\n' == c) goto done; + state = '\0'; + putc('.', tofp); + putc(c, tofp); + break; + case 'P': + if (*p == c) { + if (*++p == '\0') { /* successfully reached end */ + p = postmark; + putc('>', tofp); + fputs(postmark, tofp); + state = '\0'; + break; + } + break; /* not there yet */ + } + state = ('\n' == c) ? '\n' : '\0'; + for (q = postmark; q < p; ++q) putc(*q, tofp); + putc(c, tofp); + blankline = 0; + p = postmark; + break; + default: + state = ('\n' == c) ? '\n' : '\0'; + putc(c, tofp); + } + } + if ('\n' != state) putc('\n', tofp); +done: + if (!blankline) putc('\n', tofp); + if (ferror(tofp)) return -1; + return 0; +} + +void readbox() +{ + char linebuf[512]; + struct letter *let; + off_t current; + + firstlet = lastlet = NULL; + + if (access(mailbox, 4) < 0 || NULL == (boxfp = fopen(mailbox, "r"))) { + if (usedrop && ENOENT == errno) return; + fprintf(stderr, "can't access mailbox "); + perror(mailbox); + exit(1); + } + current = 0L; + while (1) { + if (NULL == fgets(linebuf, sizeof linebuf, boxfp)) break; + + if (!strncmp(linebuf, "From ", (size_t)5)) { + if (NULL == (let = (struct letter *) malloc(sizeof(struct letter)))) { + fprintf(stderr, "Out of memory.\n"); + exit(1); + } + if (NULL == lastlet) { + firstlet = let; + let->prev = NULL; + } else { + let->prev = lastlet; + lastlet->next = let; + } + lastlet = let; + let->next = NULL; + + let->status = UNREAD; + let->location = current; + D(printf("letter at %ld\n", current)); + } + current += strlen(linebuf); + } +} + +void printall() +{ + struct letter *let; + + let = reversemode ? firstlet : lastlet; + + if (NULL == let) { + printf("No mail.\n"); + return; + } + while (NULL != let) { + printlet(let, stdout); + let = reversemode ? let->next : let->prev; + } +} + +void interact() +{ + char linebuf[512]; /* user input line */ + struct letter *let, *next; /* current and next letter */ + int interrupted = 0; /* SIGINT hit during letter print */ + int needprint = 1; /* need to print this letter */ + char *savefile; /* filename to save into */ + + if (NULL == firstlet) { + printf("No mail.\n"); + return; + } + let = reversemode ? firstlet : lastlet; + + while (1) { + next = reversemode ? let->next : let->prev; + if (NULL == next) next = let; + + if (!quitmode) { + interrupted = setjmp(printjump); + signal(SIGINT, onint); + } + if (!interrupted && needprint) { + if (DELETED != let->status) let->status = READ; + printlet(let, stdout); + } + if (interrupted) putchar('\n'); + needprint = 0; + fputs(PROMPT, stdout); + fflush(stdout); + + if (fgets(linebuf, sizeof linebuf, stdin) == NULL) break; + + if (!quitmode) signal(SIGINT, SIG_IGN); + + switch (linebuf[0]) { + case '\n': + let = next; + needprint = 1; + continue; + case 'd': + let->status = DELETED; + if (next != let)/* look into this */ + needprint = 1; + needupdate = 1; + let = next; + continue; + case 'p': + needprint = 1; + continue; + case '-': + next = reversemode ? let->prev : let->next; + if (NULL == next) next = let; + let = next; + needprint = 1; + continue; + case 's': + for (savefile = strtok(linebuf + 1, " \t\n"); + savefile != NULL; + savefile = strtok((char *) NULL, " \t\n")) { + savelet(let, savefile); + } + continue; + case '!': + doshell(linebuf + 1); + continue; + case '*': + dohelp(); + continue; + case 'q': + return; + case 'x': + exit(0); + default: + fprintf(stderr, "Illegal command\n"); + continue; + } + } +} + +void onint(dummy) +int dummy; /* to satisfy ANSI compilers */ +{ + longjmp(printjump, 1); +} + +void savelet(let, savefile) +struct letter *let; +char *savefile; +{ + int waitstat, pid; + FILE *savefp; + + if ((pid = fork()) < 0) { + perror("mail: couldn't fork"); + return; + } else if (pid != 0) { /* parent */ + wait(&waitstat); + return; + } + + /* Child */ + setgid(getgid()); + setuid(getuid()); + if ((savefp = fopen(savefile, "a")) == NULL) { + perror(savefile); + exit(0); + } + printlet(let, savefp); + if ((ferror(savefp) != 0) | (fclose(savefp) != 0)) { + fprintf(stderr, "savefile write error:"); + perror(savefile); + } + exit(0); +} + +void updatebox() +{ + FILE *tempfp; /* fp for tempfile */ + char lockname[PATHLEN]; /* maildrop lock */ + int locktries = 0; /* tries when box is locked */ + struct letter *let; /* current letter */ + int c; + + sprintf(lockname, LOCKNAME, whoami()); + + if (NULL == (tempfp = fopen(tempname, "w"))) { + perror("mail: can't create temporary file"); + return; + } + for (let = firstlet; let != NULL; let = let->next) { + if (let->status != DELETED) { + printlet(let, tempfp); + D(printf("printed letter at %ld\n", let->location)); + } + } + + if (ferror(tempfp) || NULL == (tempfp = freopen(tempname, "r", tempfp))) { + perror("mail: temporary file write error"); + unlink(tempname); + return; + } + + /* Shut off signals during the update */ + signal(SIGINT, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + + if (usedrop) while (link(mailbox, lockname) != 0) { + if (++locktries >= LOCKTRIES) { + fprintf(stderr, "mail: couldn't lock maildrop for update\n"); + return; + } + sleep(LOCKWAIT); + } + + if (NULL == (boxfp = freopen(mailbox, "w", boxfp))) { + perror("mail: couldn't reopen maildrop"); + fprintf(stderr, "mail may have been lost; look in %s\n", tempname); + if (usedrop) unlink(lockname); + return; + } + unlink(tempname); + + while ((c = getc(tempfp)) != EOF) putc(c, boxfp); + + fclose(boxfp); + + if (usedrop) unlink(lockname); +} + +void printlet(let, tofp) +struct letter *let; +FILE *tofp; +{ + off_t current, limit; + int c; + + fseek(boxfp, (current = let->location), 0); + limit = (NULL != let->next) ? let->next->location : -1; + + while (current != limit && (c = getc(boxfp)) != EOF) { + putc(c, tofp); + ++current; + } +} + +void doshell(command) +char *command; +{ + int waitstat, pid; + char *shell; + + if (NULL == (shell = getenv("SHELL"))) shell = SHELL; + + if ((pid = fork()) < 0) { + perror("mail: couldn't fork"); + return; + } else if (pid != 0) { /* parent */ + wait(&waitstat); + return; + } + + /* Child */ + setgid(getgid()); + setuid(getuid()); + umask(oldmask); + + execl(shell, shell, "-c", command, (char *) NULL); + fprintf(stderr, "can't exec shell\n"); + exit(127); +} + +void usage() +{ + fprintf(stderr, "usage: mail [-epqr] [-f file]\n"); + fprintf(stderr, " mail [-dtv] [-s subject] user [...]\n"); +} + +char *basename(name) +char *name; +{ + char *p; + + if (NULL == (p = rindex(name, '/'))) + return name; + else + return p + 1; +} + +char *whoami() +{ + struct passwd *pw; + + if (NULL != (pw = getpwuid(getuid()))) + return pw->pw_name; + else + return "nobody"; +} + +void dohelp() +{ + FILE *fp; + char buffer[80]; + + if ( (fp = fopen(HELPFILE, "r")) == NULL) + fprintf(stdout, "can't open helpfile %s\n", HELPFILE); + else + while (fgets(buffer, 80, fp)) + fputs(buffer, stdout); +} + +int filesize(name) +char *name ; +{ + struct stat buf; + + if (stat(name, &buf) == -1) + buf.st_size = 0L; + + return (buf.st_size ? 1 : 0); +} diff --git a/commands/simple/man.c b/commands/simple/man.c new file mode 100755 index 000000000..2fe4e6393 --- /dev/null +++ b/commands/simple/man.c @@ -0,0 +1,695 @@ +/* man 2.4 - display online manual pages Author: Kees J. Bot + * 17 Mar 1993 + */ +#define nil NULL +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <dirent.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <stdarg.h> +#include <fcntl.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/wait.h> + +/* Defaults: */ +char MANPATH[]= "/usr/local/man:/usr/man"; +char PAGER[]= "more"; + +/* Comment at the start to let tbl(1) be run before n/troff. */ +char TBL_MAGIC[] = ".\\\"t\n"; + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) +#define between(a, c, z) ((unsigned) ((c) - (a)) <= (unsigned) ((z) - (a))) + +/* Section 9 uses special macros under Minix. */ +#if __minix +#define SEC9SPECIAL 1 +#else +#define SEC9SPECIAL 0 +#endif + +int searchwhatis(FILE *wf, char *title, char **ppage, char **psection) +/* Search a whatis file for the next occurence of "title". Return the basename + * of the page to read and the section it is in. Return 0 on failure, 1 on + * success, -1 on EOF or error. + */ +{ + static char page[256], section[32]; + char alias[256]; + int found= 0; + int c; + + /* Each whatis line should have the format: + * page, title, title (section) - descriptive text + */ + + /* Search the file for a line with the title. */ + do { + int first= 1; + char *pc= section; + + c= fgetc(wf); + + /* Search the line for the title. */ + do { + char *pa= alias; + + while (c == ' ' || c == '\t' || c == ',') c= fgetc(wf); + + while (c != ' ' && c != '\t' && c != ',' + && c != '(' && c != '\n' && c != EOF + ) { + if (pa < arraylimit(alias)-1) *pa++= c; + c= getc(wf); + } + *pa= 0; + if (first) { strcpy(page, alias); first= 0; } + + if (strcmp(alias, title) == 0) found= 1; + } while (c != '(' && c != '\n' && c != EOF); + + if (c != '(') { + found= 0; + } else { + while ((c= fgetc(wf)) != ')' && c != '\n' && c != EOF) { + if ('A' <= c && c <= 'Z') c= c - 'A' + 'a'; + if (pc < arraylimit(section)-1) *pc++= c; + } + *pc= 0; + if (c != ')' || pc == section) found= 0; + } + while (c != EOF && c != '\n') c= getc(wf); + } while (!found && c != EOF); + + if (found) { + *ppage= page; + *psection= section; + } + return c == EOF ? -1 : found; +} + +int searchwindex(FILE *wf, char *title, char **ppage, char **psection) +/* Search a windex file for the next occurence of "title". Return the basename + * of the page to read and the section it is in. Return 0 on failure, 1 on + * success, -1 on EOF or error. + */ +{ + static char page[256], section[32]; + static long low, high; + long mid0, mid1; + int c; + unsigned char *pt; + char *pc; + + /* Each windex line should have the format: + * title page (section) - descriptive text + * The file is sorted. + */ + + if (ftell(wf) == 0) { + /* First read of this file, initialize. */ + low= 0; + fseek(wf, (off_t) 0, SEEK_END); + high= ftell(wf); + } + + /* Binary search for the title. */ + while (low <= high) { + pt= (unsigned char *) title; + + mid0= mid1= (low + high) >> 1; + if (mid0 == 0) { + if (fseek(wf, (off_t) 0, SEEK_SET) != 0) + return -1; + } else { + if (fseek(wf, (off_t) mid0 - 1, SEEK_SET) != 0) + return -1; + + /* Find the start of a line. */ + while ((c= getc(wf)) != EOF && c != '\n') + mid1++; + if (ferror(wf)) return -1; + } + + /* See if the line has the title we seek. */ + for (;;) { + if ((c= getc(wf)) == ' ' || c == '\t') c= 0; + if (c == 0 || c != *pt) break; + pt++; + } + + /* Halve the search range. */ + if (c == EOF || *pt <= c) { + high= mid0 - 1; + } else { + low= mid1 + 1; + } + } + + /* Look for the title from 'low' onwards. */ + if (fseek(wf, (off_t) low, SEEK_SET) != 0) + return -1; + + do { + if (low != 0) { + /* Find the start of a line. */ + while ((c= getc(wf)) != EOF && c != '\n') + low++; + if (ferror(wf)) return -1; + } + + /* See if the line has the title we seek. */ + pt= (unsigned char *) title; + + for (;;) { + if ((c= getc(wf)) == EOF) return 0; + low++; + if (c == ' ' || c == '\t') c= 0; + if (c == 0 || c != *pt) break; + pt++; + } + } while (c < *pt); + + if (*pt != c) return 0; /* Not found. */ + + /* Get page and section. */ + while ((c= fgetc(wf)) == ' ' || c == '\t') {} + + pc= page; + while (c != ' ' && c != '\t' && c != '(' && c != '\n' && c != EOF) { + if (pc < arraylimit(page)-1) *pc++= c; + c= getc(wf); + } + if (pc == page) return 0; + *pc= 0; + + while (c == ' ' || c == '\t') c= fgetc(wf); + + if (c != '(') return 0; + + pc= section; + while ((c= fgetc(wf)) != ')' && c != '\n' && c != EOF) { + if ('A' <= c && c <= 'Z') c= c - 'A' + 'a'; + if (pc < arraylimit(section)-1) *pc++= c; + } + *pc= 0; + if (c != ')' || pc == section) return 0; + + while (c != EOF && c != '\n') c= getc(wf); + if (c != '\n') return 0; + + *ppage= page; + *psection= section; + return 1; +} + +char ALL[]= ""; /* Magic sequence of all sections. */ + +int all= 0; /* Show all pages with a given title. */ +int whatis= 0; /* man -f word == whatis word. */ +int apropos= 0; /* man -k word == apropos word. */ +int quiet= 0; /* man -q == quietly check. */ +enum ROFF { NROFF, TROFF } rofftype= NROFF; +char *roff[] = { "nroff", "troff" }; + +int shown; /* True if something has been shown. */ +int tty; /* True if displaying on a terminal. */ +char *manpath; /* The manual directory path. */ +char *pager; /* The pager to use. */ + +char *pipeline[8][8]; /* An 8 command pipeline of 7 arguments each. */ +char *(*plast)[8] = pipeline; + +void putinline(char *arg1, ...) +/* Add a command to the pipeline. */ +{ + va_list ap; + char **argv; + + argv= *plast++; + *argv++= arg1; + + va_start(ap, arg1); + while ((*argv++= va_arg(ap, char *)) != nil) {} + va_end(ap); +} + +void execute(int set_mp, char *file) +/* Execute the pipeline build with putinline(). (This is a lot of work to + * avoid a call to system(), but it so much fun to do it right!) + */ +{ + char *(*plp)[8], **argv; + char *mp; + int fd0, pfd[2], err[2]; + pid_t pid; + int r, status; + int last; + void (*isav)(int sig), (*qsav)(int sig), (*tsav)(int sig); + + if (tty) { + /* Must run this through a pager. */ + putinline(pager, (char *) nil); + } + if (plast == pipeline) { + /* No commands at all? */ + putinline("cat", (char *) nil); + } + + /* Add the file as argument to the first command. */ + argv= pipeline[0]; + while (*argv != nil) argv++; + *argv++= file; + *argv= nil; + + /* Start the commands. */ + fd0= 0; + for (plp= pipeline; plp < plast; plp++) { + argv= *plp; + last= (plp+1 == plast); + + /* Create an error pipe and pipe between this command and the next. */ + if (pipe(err) < 0 || (!last && pipe(pfd) < 0)) { + fprintf(stderr, "man: can't create a pipe: %s\n", strerror(errno)); + exit(1); + } + + (void) fcntl(err[1], F_SETFD, fcntl(err[1], F_GETFD) | FD_CLOEXEC); + + if ((pid = fork()) < 0) { + fprintf(stderr, "man: cannot fork: %s\n", strerror(errno)); + exit(1); + } + if (pid == 0) { + /* Child. */ + if (set_mp) { + mp= malloc((8 + strlen(manpath) + 1) * sizeof(*mp)); + if (mp != nil) { + strcpy(mp, "MANPATH="); + strcat(mp, manpath); + (void) putenv(mp); + } + } + + if (fd0 != 0) { + dup2(fd0, 0); + close(fd0); + } + if (!last) { + close(pfd[0]); + if (pfd[1] != 1) { + dup2(pfd[1], 1); + close(pfd[1]); + } + } + close(err[0]); + execvp(argv[0], argv); + (void) write(err[1], &errno, sizeof(errno)); + _exit(1); + } + + close(err[1]); + if (read(err[0], &errno, sizeof(errno)) != 0) { + fprintf(stderr, "man: %s: %s\n", argv[0], + strerror(errno)); + exit(1); + } + close(err[0]); + + if (!last) { + close(pfd[1]); + fd0= pfd[0]; + } + set_mp= 0; + } + + /* Wait for the last command to finish. */ + isav= signal(SIGINT, SIG_IGN); + qsav= signal(SIGQUIT, SIG_IGN); + tsav= signal(SIGTERM, SIG_IGN); + while ((r= wait(&status)) != pid) { + if (r < 0) { + fprintf(stderr, "man: wait(): %s\n", strerror(errno)); + exit(1); + } + } + (void) signal(SIGINT, isav); + (void) signal(SIGQUIT, qsav); + (void) signal(SIGTERM, tsav); + if (status != 0) exit(1); + plast= pipeline; +} + +void keyword(char *keyword) +/* Make an apropos(1) or whatis(1) call. */ +{ + putinline(apropos ? "apropos" : "whatis", + all ? "-a" : (char *) nil, + (char *) nil); + + if (tty) { + printf("Looking for keyword '%s'\n", keyword); + fflush(stdout); + } + + execute(1, keyword); +} + +enum pagetype { CAT, CATZ, MAN, MANZ, SMAN, SMANZ }; + +int showpage(char *page, enum pagetype ptype, char *macros) +/* Show a manual page if it exists using the proper decompression and + * formatting tools. + */ +{ + struct stat st; + + /* We want a normal file without X bits if not a full path. */ + if (stat(page, &st) < 0) return 0; + + if (!S_ISREG(st.st_mode)) return 0; + if ((st.st_mode & 0111) && page[0] != '/') return 0; + + /* Do we only care if it exists? */ + if (quiet) { shown= 1; return 1; } + + if (ptype == CATZ || ptype == MANZ || ptype == SMANZ) { + putinline("zcat", (char *) nil); + } + + if (ptype == SMAN || ptype == SMANZ) { + /* Change SGML into regular *roff. */ + putinline("/usr/lib/sgml/sgml2roff", (char *) nil); + putinline("tbl", (char *) nil); + putinline("eqn", (char *) nil); + } + + if (ptype == MAN) { + /* Do we need tbl? */ + FILE *fp; + int c; + char *tp = TBL_MAGIC; + + if ((fp = fopen(page, "r")) == nil) { + fprintf(stderr, "man: %s: %s\n", page, strerror(errno)); + exit(1); + } + c= fgetc(fp); + for (;;) { + if (c == *tp || (c == '\'' && *tp == '.')) { + if (*++tp == 0) { + /* A match, add tbl. */ + putinline("tbl", (char *) nil); + break; + } + } else { + /* No match. */ + break; + } + while ((c = fgetc(fp)) == ' ' || c == '\t') {} + } + fclose(fp); + } + + if (ptype == MAN || ptype == MANZ || ptype == SMAN || ptype == SMANZ) { + putinline(roff[rofftype], macros, (char *) nil); + } + + if (tty) { + printf("%s %s\n", + ptype == CAT || ptype == CATZ ? "Showing" : "Formatting", page); + fflush(stdout); + } + execute(0, page); + + shown= 1; + return 1; +} + +int member(char *word, char *list) +/* True if word is a member of a comma separated list. */ +{ + size_t len= strlen(word); + + if (list == ALL) return 1; + + while (*list != 0) { + if (strncmp(word, list, len) == 0 + && (list[len] == 0 || list[len] == ',')) + return 1; + while (*list != 0 && *list != ',') list++; + if (*list == ',') list++; + } + return 0; +} + +int trymandir(char *mandir, char *title, char *section) +/* Search the whatis file of the manual directory for a page of the given + * section and display it. + */ +{ + FILE *wf; + char whatis[1024], pagename[1024], *wpage, *wsection; + int rsw, rsp; + int ntries; + int (*searchidx)(FILE *, char *, char **, char **); + struct searchnames { + enum pagetype ptype; + char *pathfmt; + } *sp; + static struct searchnames searchN[] = { + { CAT, "%s/cat%s/%s.%s" }, /* SysV */ + { CATZ, "%s/cat%s/%s.%s.Z" }, + { MAN, "%s/man%s/%s.%s" }, + { MANZ, "%s/man%s/%s.%s.Z" }, + { SMAN, "%s/sman%s/%s.%s" }, /* Solaris */ + { SMANZ,"%s/sman%s/%s.%s.Z" }, + { CAT, "%s/cat%.1s/%s.%s" }, /* BSD */ + { CATZ, "%s/cat%.1s/%s.%s.Z" }, + { MAN, "%s/man%.1s/%s.%s" }, + { MANZ, "%s/man%.1s/%s.%s.Z" }, + }; + + if (strlen(mandir) + 1 + 6 + 1 > arraysize(whatis)) return 0; + + /* Prefer a fast windex database if available. */ + sprintf(whatis, "%s/windex", mandir); + + if ((wf= fopen(whatis, "r")) != nil) { + searchidx= searchwindex; + } else { + /* Use a classic whatis database. */ + sprintf(whatis, "%s/whatis", mandir); + + if ((wf= fopen(whatis, "r")) == nil) return 0; + searchidx= searchwhatis; + } + + rsp= 0; + while (!rsp && (rsw= (*searchidx)(wf, title, &wpage, &wsection)) == 1) { + if (!member(wsection, section)) continue; + + /* When looking for getc(1S) we try: + * cat1s/getc.1s + * cat1s/getc.1s.Z + * man1s/getc.1s + * man1s/getc.1s.Z + * sman1s/getc.1s + * sman1s/getc.1s.Z + * cat1/getc.1s + * cat1/getc.1s.Z + * man1/getc.1s + * man1/getc.1s.Z + */ + + if (strlen(mandir) + 2 * strlen(wsection) + strlen(wpage) + + 10 > arraysize(pagename)) + continue; + + sp= searchN; + ntries= arraysize(searchN); + do { + if (sp->ptype <= CATZ && rofftype != NROFF) + continue; + + sprintf(pagename, sp->pathfmt, + mandir, wsection, wpage, wsection); + + rsp= showpage(pagename, sp->ptype, + (SEC9SPECIAL && strcmp(wsection, "9") == 0) ? "-mnx" : "-man"); + } while (sp++, !rsp && --ntries != 0); + + if (all) rsp= 0; + } + if (rsw < 0 && ferror(wf)) { + fprintf(stderr, "man: %s: %s\n", whatis, strerror(errno)); + exit(1); + } + fclose(wf); + return rsp; +} + +int trysubmandir(char *mandir, char *title, char *section) +/* Search the subdirectories of this manual directory for whatis files, they + * may have manual pages that override the ones in the major directory. + */ +{ + char submandir[1024]; + DIR *md; + struct dirent *entry; + + if ((md= opendir(mandir)) == nil) return 0; + + while ((entry= readdir(md)) != nil) { + if (strcmp(entry->d_name, ".") == 0 + || strcmp(entry->d_name, "..") == 0) continue; + if ((strncmp(entry->d_name, "man", 3) == 0 + || strncmp(entry->d_name, "cat", 3) == 0) + && between('0', entry->d_name[3], '9')) continue; + + if (strlen(mandir) + 1 + strlen(entry->d_name) + 1 + > arraysize(submandir)) continue; + + sprintf(submandir, "%s/%s", mandir, entry->d_name); + + if (trymandir(submandir, title, section) && !all) { + closedir(md); + return 1; + } + } + closedir(md); + + return 0; +} + +void searchmanpath(char *title, char *section) +/* Search the manual path for a manual page describing "title." */ +{ + char mandir[1024]; + char *pp= manpath, *pd; + + for (;;) { + while (*pp != 0 && *pp == ':') pp++; + + if (*pp == 0) break; + + pd= mandir; + while (*pp != 0 && *pp != ':') { + if (pd < arraylimit(mandir)) *pd++= *pp; + pp++; + } + if (pd == arraylimit(mandir)) continue; /* forget it */ + + *pd= 0; + if (trysubmandir(mandir, title, section) && !all) break; + if (trymandir(mandir, title, section) && !all) break; + } +} + +void usage(void) +{ + fprintf(stderr, "Usage: man -[antfkq] [-M path] [-s section] title ...\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + char *title, *section= ALL; + int i; + int nomoreopt= 0; + char *opt; + + if ((pager= getenv("PAGER")) == nil) pager= PAGER; + if ((manpath= getenv("MANPATH")) == nil) manpath= MANPATH; + tty= isatty(1); + + i= 1; + do { + while (i < argc && argv[i][0] == '-' && !nomoreopt) { + opt= argv[i++]+1; + if (opt[0] == '-' && opt[1] == 0) { + nomoreopt= 1; + break; + } + while (*opt != 0) { + switch (*opt++) { + case 'a': + all= 1; + break; + case 'f': + whatis= 1; + break; + case 'k': + apropos= 1; + break; + case 'q': + quiet= 1; + break; + case 'n': + rofftype= NROFF; + apropos= whatis= 0; + break; + case 't': + rofftype= TROFF; + apropos= whatis= 0; + break; + case 's': + if (*opt == 0) { + if (i == argc) usage(); + section= argv[i++]; + } else { + section= opt; + opt= ""; + } + break; + case 'M': + if (*opt == 0) { + if (i == argc) usage(); + manpath= argv[i++]; + } else { + manpath= opt; + opt= ""; + } + break; + default: + usage(); + } + } + } + + if (i >= argc) usage(); + + if (between('0', argv[i][0], '9') && argv[i][1] == 0) { + /* Allow single digit section designations. */ + section= argv[i++]; + } + if (i == argc) usage(); + + title= argv[i++]; + + if (whatis || apropos) { + keyword(title); + } else { + shown= 0; + searchmanpath(title, section); + + if (!shown) (void) showpage(title, MAN, "-man"); + + if (!shown) { + if (!quiet) { + fprintf(stderr, + "man: no manual on %s\n", + title); + } + exit(1); + } + } + } while (i < argc); + + return 0; +} diff --git a/commands/simple/mesg.c b/commands/simple/mesg.c new file mode 100755 index 000000000..2acfee969 --- /dev/null +++ b/commands/simple/mesg.c @@ -0,0 +1,48 @@ +/* mesg - enable or disable communications. Author: John J. Ribera */ + +/* + * mesg - enable or disable communications. + * + * Usage: mesg [ y | n ] + * + * 'mesg n' will turn off group and world permissions of the + * user's terminal. + * 'mesg y' will enable group and world to write to the user's + * tty. + * mesg with no parameters will put the writeable status + * onto stdout. + * + * Author: John J. Ribera, Jr. 09/09/90 + */ +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +_PROTOTYPE( int main, (int argc, char *argv []) ); + +int main(argc, argv) +int argc; +char *argv[]; +{ + struct stat statb; + char *tty_name; + + if ((tty_name = ttyname(0)) == NULL) exit(2); + if (stat(tty_name, &statb) == -1) exit(2); + if (--argc) { + if (*argv[1] == 'n') statb.st_mode = 0600; + else if (*argv[1] == 'y') statb.st_mode = 0620; + else { + fprintf(stderr, "mesg: usage: mesg [n|y]\n"); + exit(2); + } + if (chmod(tty_name, statb.st_mode) == -1) exit(2); + } else printf((statb.st_mode & 020) ? "is y\n" : "is n\n"); + + if (statb.st_mode & 020) exit(0); + + exit(1); +} + diff --git a/commands/simple/mkdir.c b/commands/simple/mkdir.c new file mode 100755 index 000000000..38f366053 --- /dev/null +++ b/commands/simple/mkdir.c @@ -0,0 +1,265 @@ +/* mkdir - Make directories Author: V. Archer */ + +/* Copyright 1991 by Vincent Archer + * You may freely redistribute this software, in source or binary + * form, provided that you do not alter this copyright mention in any + * way. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <minix/minlib.h> +#include <limits.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +extern int optind, opterr; +extern char *optarg; + +#define USR_MODES (S_ISUID|S_IRWXU) +#define GRP_MODES (S_ISGID|S_IRWXG) +#define EXE_MODES (S_IXUSR|S_IXGRP|S_IXOTH) +#ifdef S_ISVTX +#define ALL_MODES (USR_MODES|GRP_MODES|S_IRWXO|S_ISVTX) +#else +#define ALL_MODES (USR_MODES|GRP_MODES|S_IRWXO) +#endif +#define DEFAULT_MODE (S_IRWXU|S_IRWXG|S_IRWXO) +#define USER_WX (S_IWUSR|S_IXUSR) + + +/* Global variables */ +int pflag; +char *symbolic; +mode_t u_mask; +struct stat st; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(mode_t parsemode, (char *symbolic, Mode_t oldmode)); +_PROTOTYPE(int makepath, (char *fordir)); +_PROTOTYPE(int makedir, (char *dirname)); +_PROTOTYPE(void usage, (void)); + +/* Parse a P1003.2 4.7.7-conformant symbolic mode. */ +mode_t parsemode(symbolic, oldmode) +char *symbolic; +mode_t oldmode; +{ + mode_t who, mask, newmode, tmpmask; + char action; + char *end; + unsigned long octalmode; + + octalmode = strtoul(symbolic, &end, 010); + if (octalmode < ALL_MODES && *end == 0 && end != symbolic) return octalmode; + + newmode = oldmode & ALL_MODES; + while (*symbolic) { + who = 0; + for (; *symbolic; symbolic++) { + if (*symbolic == 'a') { + who |= ALL_MODES; + continue; + } + if (*symbolic == 'u') { + who |= USR_MODES; + continue; + } + if (*symbolic == 'g') { + who |= GRP_MODES; + continue; + } + if (*symbolic == 'o') { + who |= S_IRWXO; + continue; + } + break; + } + if (!*symbolic || *symbolic == ',') usage(); + while (*symbolic) { + if (*symbolic == ',') break; + switch (*symbolic) { + default: + usage(); + case '+': + case '-': + case '=': action = *symbolic++; + } + mask = 0; + for (; *symbolic; symbolic++) { + if (*symbolic == 'u') { + tmpmask = newmode & S_IRWXU; + mask |= tmpmask | (tmpmask << 3) | (tmpmask << 6); + symbolic++; + break; + } + if (*symbolic == 'g') { + tmpmask = newmode & S_IRWXG; + mask |= tmpmask | (tmpmask >> 3) | (tmpmask << 3); + symbolic++; + break; + } + if (*symbolic == 'o') { + tmpmask = newmode & S_IRWXO; + mask |= tmpmask | (tmpmask >> 3) | (tmpmask >> 6); + symbolic++; + break; + } + if (*symbolic == 'r') { + mask |= S_IRUSR | S_IRGRP | S_IROTH; + continue; + } + if (*symbolic == 'w') { + mask |= S_IWUSR | S_IWGRP | S_IWOTH; + continue; + } + if (*symbolic == 'x') { + mask |= EXE_MODES; + continue; + } + if (*symbolic == 's') { + mask |= S_ISUID | S_ISGID; + continue; + } + if (*symbolic == 'X') { + if (S_ISDIR(oldmode) || (oldmode & EXE_MODES)) + mask |= EXE_MODES; + continue; + } +#ifdef S_ISVTX + if (*symbolic == 't') { + mask |= S_ISVTX; + who |= S_ISVTX; + continue; + } +#endif + break; + } + switch (action) { + case '=': + if (who) + newmode &= ~who; + else + newmode = 0; + case '+': + if (who) + newmode |= who & mask; + else + newmode |= mask & (~u_mask); + break; + case '-': + if (who) + newmode &= ~(who & mask); + else + newmode &= ~mask | u_mask; + } + } + if (*symbolic) symbolic++; + } + return(newmode); +} + + +/* Main module. */ +int main(argc, argv) +int argc; +char **argv; +{ + int error, c; + + opterr = 0; + pflag = 0; + symbolic = (char *) 0; + u_mask = umask(0); + umask(u_mask); + while ((c = getopt(argc, argv, "m:p")) != EOF) switch (c) { + case 'm': symbolic = optarg; break; + case 'p': pflag = 1; break; + default: usage(); + } + if (optind >= argc) usage(); + + error = 0; + while (optind < argc) error |= makedir(argv[optind++]); + return(error); +} + + +/* P1003.2 requires that missing intermediate pathname components should be + * created if the -p option is specified (4.40.3). + */ +int makepath(fordir) +char *fordir; +{ + char parent[PATH_MAX + 1], *end; + + strcpy(parent, fordir); + if (!(end = strrchr(parent, '/'))) return(0); + *end = '\0'; + if (!parent[0]) return(0); + + if (!stat(parent, &st)) { + if (S_ISDIR(st.st_mode)) return(0); + errno = ENOTDIR; + perror(parent); + return(1); + } + if (mkdir(parent, DEFAULT_MODE)) { + if (makepath(parent)) return(1); + if (mkdir(parent, DEFAULT_MODE)) { + perror(parent); + return(1); + } + } + +/* P1003.2 states that, regardless of umask() value, intermediate paths + * should have at least write and search (x) permissions (4.40.10). + */ + if ((u_mask & USER_WX) && + chmod(parent, ((~u_mask) | USER_WX)) & DEFAULT_MODE) { + perror(parent); + return(1); + } + return(0); +} + + +/* Actual directory creation, using a mkdir() system call. */ +int makedir(dirname) +char *dirname; +{ + if (mkdir(dirname, DEFAULT_MODE)) { + if (!pflag) { + perror(dirname); + return(1); + } + if (!stat(dirname, &st)) { + if (S_ISDIR(st.st_mode)) return(0); + errno = ENOTDIR; + perror(dirname); + return(1); + } + if (makepath(dirname)) return(1); + if (mkdir(dirname, DEFAULT_MODE)) { + perror(dirname); + return(1); + } + } + if (symbolic && (stat(dirname, &st) || + chmod(dirname, parsemode(symbolic, st.st_mode)))) { + perror(dirname); + return(1); + } + return(0); +} + + +/* Posix command prototype. */ +void usage() +{ + std_err("Usage: mkdir [-p] [-m mode] dir...\n"); + exit(1); +} diff --git a/commands/simple/mkfifo.c b/commands/simple/mkfifo.c new file mode 100755 index 000000000..8900dc52c --- /dev/null +++ b/commands/simple/mkfifo.c @@ -0,0 +1,196 @@ +/* mkfifo - Make FIFO special files Author: V. Archer */ + +/* Copyright 1991 by Vincent Archer + * You may freely redistribute this software, in source or binary + * form, provided that you do not alter this copyright mention in any + * way. + */ + +#include <sys/types.h> +#include <stdlib.h> +#include <sys/stat.h> +#include <string.h> +#include <unistd.h> +#include <minix/minlib.h> +#include <stdio.h> + +#define USR_MODES (S_ISUID|S_IRWXU) +#define GRP_MODES (S_ISGID|S_IRWXG) +#define EXE_MODES (S_IXUSR|S_IXGRP|S_IXOTH) +#ifdef S_ISVTX +#define ALL_MODES (USR_MODES|GRP_MODES|S_IRWXO|S_ISVTX) +#else +#define ALL_MODES (USR_MODES|GRP_MODES|S_IRWXO) +#endif +#define DEFAULT_MODE (S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH|S_IWOTH) + + +/* Global u_mask needed in changemode.h */ +mode_t u_mask; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(mode_t parsemode, (char *symbolic, Mode_t oldmode)); +_PROTOTYPE(void usage, (void)); + +/* Parse a P1003.2 4.7.7-conformant symbolic mode. */ +mode_t parsemode(symbolic, oldmode) +char *symbolic; +mode_t oldmode; +{ + mode_t who, mask, newmode, tmpmask; + char action; + + newmode = oldmode & ALL_MODES; + while (*symbolic) { + who = 0; + for (; *symbolic; symbolic++) { + if (*symbolic == 'a') { + who |= ALL_MODES; + continue; + } + if (*symbolic == 'u') { + who |= USR_MODES; + continue; + } + if (*symbolic == 'g') { + who |= GRP_MODES; + continue; + } + if (*symbolic == 'o') { + who |= S_IRWXO; + continue; + } + break; + } + if (!*symbolic || *symbolic == ',') usage(); + while (*symbolic) { + if (*symbolic == ',') break; + switch (*symbolic) { + default: + usage(); + case '+': + case '-': + case '=': action = *symbolic++; + } + mask = 0; + for (; *symbolic; symbolic++) { + if (*symbolic == 'u') { + tmpmask = newmode & S_IRWXU; + mask |= tmpmask | (tmpmask << 3) | (tmpmask << 6); + symbolic++; + break; + } + if (*symbolic == 'g') { + tmpmask = newmode & S_IRWXG; + mask |= tmpmask | (tmpmask >> 3) | (tmpmask << 3); + symbolic++; + break; + } + if (*symbolic == 'o') { + tmpmask = newmode & S_IRWXO; + mask |= tmpmask | (tmpmask >> 3) | (tmpmask >> 6); + symbolic++; + break; + } + if (*symbolic == 'r') { + mask |= S_IRUSR | S_IRGRP | S_IROTH; + continue; + } + if (*symbolic == 'w') { + mask |= S_IWUSR | S_IWGRP | S_IWOTH; + continue; + } + if (*symbolic == 'x') { + mask |= EXE_MODES; + continue; + } + if (*symbolic == 's') { + mask |= S_ISUID | S_ISGID; + continue; + } + if (*symbolic == 'X') { + if (S_ISDIR(oldmode) || (oldmode & EXE_MODES)) + mask |= EXE_MODES; + continue; + } +#ifdef S_ISVTX + if (*symbolic == 't') { + mask |= S_ISVTX; + who |= S_ISVTX; + continue; + } +#endif + break; + } + switch (action) { + case '=': + if (who) + newmode &= ~who; + else + newmode = 0; + case '+': + if (who) + newmode |= who & mask; + else + newmode |= mask & (~u_mask); + break; + case '-': + if (who) + newmode &= ~(who & mask); + else + newmode &= ~mask | u_mask; + } + } + if (*symbolic) symbolic++; + } + return(newmode); +} + + +/* Main module. Since only one option (-m mode) is allowed, there's no need + * to include the whole getopt() stuff. + */ +int main(argc, argv) +int argc; +char *argv[]; +{ + int errors = 0; + char *symbolic; + + if (argc > 2 && *argv[1] == '-' && strcmp(argv[1], "-m") != 0) usage(); + argc--; + argv++; + if (argc && strncmp(*argv, "-m", (size_t) 2) == 0) { + argc--; + if ((argv[0])[2]) + symbolic = (*argv++) + 2; + else { + if (!argc--) usage(); + argv++; + symbolic = *argv++; + } + u_mask = umask(0); + umask(u_mask); + } else + symbolic = (char *) 0; + + if (!argc) usage(); + for (; argc--; argv++) + if (mkfifo(*argv, DEFAULT_MODE)) { + perror(*argv); + errors = 1; + } else if (symbolic && chmod(*argv, parsemode(symbolic, DEFAULT_MODE))) { + unlink(*argv); + perror(*argv); + errors = 1; + } + return(errors); +} + + +/* Posix command prototype. */ +void usage() +{ + std_err("Usage: mkfifo [-m mode] file...\n"); + exit(1); +} diff --git a/commands/simple/mkfs-old b/commands/simple/mkfs-old new file mode 100755 index 000000000..f044a8b49 Binary files /dev/null and b/commands/simple/mkfs-old differ diff --git a/commands/simple/mkfs.c b/commands/simple/mkfs.c new file mode 100755 index 000000000..0e1637b54 --- /dev/null +++ b/commands/simple/mkfs.c @@ -0,0 +1,1534 @@ +/* mkfs - make the MINIX filesystem Authors: Tanenbaum et al. */ + +/* Authors: Andy Tanenbaum, Paul Ogilvie, Frans Meulenbroeks, Bruce Evans + * + * This program can make both version 1 and version 2 file systems, as follows: + * mkfs /dev/fd0 1200 # Version 2 (default) + * mkfs -1 /dev/fd0 360 # Version 1 + * + */ + +#include <sys/types.h> +#include <sys/dir.h> +#include <sys/stat.h> +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <minix/config.h> +#include <minix/const.h> +#include <minix/type.h> +#include <minix/minlib.h> +#include "../../servers/fs/const.h" +#if (MACHINE == IBM_PC) +#include <minix/partition.h> +#include <minix/u64.h> +#include <sys/ioctl.h> +#endif +#include <a.out.h> +#include <tools.h> +#include <dirent.h> + +#undef EXTERN +#define EXTERN /* get rid of EXTERN by making it null */ +#include "../../servers/fs/type.h" +#include "../../servers/fs/super.h" +#include <minix/fslib.h> + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#ifndef DOS +#ifndef UNIX +#define UNIX +#endif +#endif + +#define INODE_MAP 2 +#define MAX_TOKENS 10 +#define LINE_LEN 200 +#define BIN 2 +#define BINGRP 2 +#define BIT_MAP_SHIFT 13 +#define N_BLOCKS (1024L * 1024) +#define N_BLOCKS16 (128L * 1024) +#define INODE_MAX ((unsigned) 65535) + +/* You can make a really large file system on a 16-bit system, but the array + * of bits that get_block()/putblock() needs gets a bit big, so we can only + * prefill MAX_INIT blocks. (16-bit fsck can't check a file system larger + * than N_BLOCKS16 anyway.) + */ +#define MAX_INIT (sizeof(char *) == 2 ? N_BLOCKS16 : N_BLOCKS) + + +#ifdef DOS +maybedefine O_RDONLY 4 /* O_RDONLY | BINARY_BIT */ + maybedefine BWRITE 5 /* O_WRONLY | BINARY_BIT */ +#endif + +#if (MACHINE == ATARI) +int isdev; +#endif + +extern char *optarg; +extern int optind; + +int next_zone, next_inode, zone_size, zone_shift = 0, zoff; +block_t nrblocks; +int inode_offset, lct = 0, disk, fd, print = 0, file = 0; +unsigned int nrinodes; +int override = 0, simple = 0, dflag; +int donttest; /* skip test if it fits on medium */ +char *progname; + +long current_time, bin_time; +char *zero, *lastp; +char umap[MAX_INIT / 8]; /* bit map tells if block read yet */ +block_t zone_map; /* where is zone map? (depends on # inodes) */ +int inodes_per_block; +int fs_version; +unsigned int block_size; +block_t max_nrblocks; + +FILE *proto; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(block_t sizeup, (char *device)); +_PROTOTYPE(void super, (zone_t zones, Ino_t inodes)); +_PROTOTYPE(void rootdir, (Ino_t inode)); +_PROTOTYPE(void eat_dir, (Ino_t parent)); +_PROTOTYPE(void eat_file, (Ino_t inode, int f)); +_PROTOTYPE(void enter_dir, (Ino_t parent, char *name, Ino_t child)); +_PROTOTYPE(void incr_size, (Ino_t n, long count)); +_PROTOTYPE(PRIVATE ino_t alloc_inode, (int mode, int usrid, int grpid)); +_PROTOTYPE(PRIVATE zone_t alloc_zone, (void)); +_PROTOTYPE(void add_zone, (Ino_t n, zone_t z, long bytes, long cur_time)); +_PROTOTYPE(void add_z_1, (Ino_t n, zone_t z, long bytes, long cur_time)); +_PROTOTYPE(void add_z_2, (Ino_t n, zone_t z, long bytes, long cur_time)); +_PROTOTYPE(void incr_link, (Ino_t n)); +_PROTOTYPE(void insert_bit, (block_t block, int bit)); +_PROTOTYPE(int mode_con, (char *p)); +_PROTOTYPE(void getline, (char line[LINE_LEN], char *parse[MAX_TOKENS])); +_PROTOTYPE(void check_mtab, (char *devname)); +_PROTOTYPE(long file_time, (int f)); +_PROTOTYPE(void pexit, (char *s)); +_PROTOTYPE(void copy, (char *from, char *to, int count)); +_PROTOTYPE(void print_fs, (void)); +_PROTOTYPE(int read_and_set, (block_t n)); +_PROTOTYPE(void special, (char *string)); +_PROTOTYPE(void get_block, (block_t n, char *buf)); +_PROTOTYPE(void get_super_block, (char *buf)); +_PROTOTYPE(void put_block, (block_t n, char *buf)); +_PROTOTYPE(void cache_init, (void)); +_PROTOTYPE(void flush, (void)); +_PROTOTYPE(void mx_read, (int blocknr, char *buf)); +_PROTOTYPE(void mx_write, (int blocknr, char *buf)); +_PROTOTYPE(void dexit, (char *s, int sectnum, int err)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(char *alloc_block, (void)); + +/*================================================================ + * mkfs - make filesystem + *===============================================================*/ +int main(argc, argv) +int argc; +char *argv[]; +{ + int nread, mode, usrid, grpid, ch; + block_t blocks; + block_t i; + ino_t root_inum; + ino_t inodes; + zone_t zones; + char *token[MAX_TOKENS], line[LINE_LEN]; + struct stat statbuf; + + /* Get two times, the current time and the mod time of the binary of + * mkfs itself. When the -d flag is used, the later time is put into + * the i_mtimes of all the files. This feature is useful when + * producing a set of file systems, and one wants all the times to be + * identical. First you set the time of the mkfs binary to what you + * want, then go. + */ + current_time = time((time_t *) 0); /* time mkfs is being run */ + stat(argv[0], &statbuf); + bin_time = statbuf.st_mtime; /* time when mkfs binary was last modified */ + + /* Process switches. */ + progname = argv[0]; + blocks = 0; + i = 0; + fs_version = 3; + inodes_per_block = 0; + max_nrblocks = N_BLOCKS; + block_size = 0; + while ((ch = getopt(argc, argv, "12b:di:lotB:")) != EOF) + switch (ch) { + case '1': + fs_version = 1; + inodes_per_block = V1_INODES_PER_BLOCK; + max_nrblocks = 0xFFFF; + break; + case '2': + fs_version = 2; + break; + case 'b': + blocks = strtoul(optarg, (char **) NULL, 0); + break; + case 'd': + dflag = 1; + current_time = bin_time; + break; + case 'i': + i = strtoul(optarg, (char **) NULL, 0); + break; + case 'l': print = 1; break; + case 'o': override = 1; break; + case 't': donttest = 1; break; + case 'B': block_size = atoi(optarg); break; + default: usage(); + } + + if(fs_version == 3) { + if(!block_size) block_size = 8192; /* V3 default block size */ + if(block_size%SECTOR_SIZE || block_size < MIN_BLOCK_SIZE) { + fprintf(stderr, "block size must be multiple of sector (%d) " + "and at least %d bytes\n", + SECTOR_SIZE, MIN_BLOCK_SIZE); + pexit("specified block size illegal"); + } + if(block_size%V2_INODE_SIZE) { + fprintf(stderr, "block size must be a multiple of inode size (%d bytes)\n", + V2_INODE_SIZE); + pexit("specified block size illegal"); + } + } else { + if(block_size) { + pexit("Can't specify a block size if FS version is <3"); + } + block_size = STATIC_BLOCK_SIZE; /* V1/V2 block size */ + } + + if(!inodes_per_block) + inodes_per_block = V2_INODES_PER_BLOCK(block_size); + + /* now that the block size is known, do buffer allocations where + * possible. + */ + zero = alloc_block(); + bzero(zero, block_size); + + /* Determine the size of the device if not specified as -b or proto. */ + if (argc - optind == 1 && blocks == 0) blocks = sizeup(argv[optind]); + + /* The remaining args must be 'special proto', or just 'special' if the + * no. of blocks has already been specified. + */ + if (argc - optind != 2 && (argc - optind != 1 || blocks == 0)) usage(); + + /* Check special. */ + check_mtab(argv[optind]); + + /* Check and start processing proto. */ + optarg = argv[++optind]; + if (optind < argc && (proto = fopen(optarg, "r")) != NULL) { + /* Prototype file is readable. */ + lct = 1; + getline(line, token); /* skip boot block info */ + + /* Read the line with the block and inode counts. */ + getline(line, token); + blocks = atol(token[0]); + if (blocks > max_nrblocks) pexit("Block count too large"); + if (sizeof(char *) == 2 && blocks > N_BLOCKS16) { + fprintf(stderr, + "%s: warning: FS is larger than the %dM that fsck can check!\n", + progname, (int) (N_BLOCKS16 / (1024L * 1024))); + } + inodes = atoi(token[1]); + + /* Process mode line for root directory. */ + getline(line, token); + mode = mode_con(token[0]); + usrid = atoi(token[1]); + grpid = atoi(token[2]); + } else { + lct = 0; + if (optind < argc) { + /* Maybe the prototype file is just a size. Check. */ + blocks = strtoul(optarg, (char **) NULL, 0); + if (blocks == 0) pexit("Can't open prototype file"); + } + if (i == 0) { + int kb; + kb = blocks * (min(block_size,1024) / 1024); + /* The default for inodes is 3 blocks per inode, rounded up + * to fill an inode block. Above 20M, the average files are + * sure to be larger because it is hard to fill up 20M with + * tiny files, so reduce the default number of inodes. This + * default can always be overridden by using the '-i option. + */ + i = kb / 3; + if (kb >= 20000) i = kb / 4; + if (kb >= 40000) i = kb / 5; + if (kb >= 60000) i = kb / 6; + if (kb >= 80000) i = kb / 7; + if (kb >= 100000) i = kb / 8; + i += inodes_per_block - 1; + i = i / inodes_per_block * inodes_per_block; + if (i > INODE_MAX) i = INODE_MAX; + } + if (blocks < 5) pexit("Block count too small"); + if (blocks > max_nrblocks) pexit("Block count too large"); + if (i < 1) pexit("Inode count too small"); + if (i > INODE_MAX) pexit("Inode count too large"); + inodes = (ino_t) i; + + /* Make simple file system of the given size, using defaults. */ + mode = 040777; + usrid = BIN; + grpid = BINGRP; + simple = 1; + } + nrblocks = blocks; + nrinodes = inodes; + + /* Open special. */ + special(argv[--optind]); + +#ifdef UNIX + if (!donttest) { + short *testb; + ssize_t w; + + testb = (short *) alloc_block(); + + /* Try writing the last block of partition or diskette. */ + if(lseek(fd, (off_t) (blocks - 1) * block_size, SEEK_SET) < 0) { + pexit("couldn't seek to last block to test size (1)"); + } + testb[0] = 0x3245; + testb[1] = 0x11FF; + testb[block_size-1] = 0x1F2F; + if ((w=write(fd, (char *) testb, block_size)) != block_size) { + if(w < 0) perror("write"); + printf("%d/%d\n", w, block_size); + pexit("File system is too big for minor device (write)"); + } + sync(); /* flush write, so if error next read fails */ + if(lseek(fd, (off_t) (blocks - 1) * block_size, SEEK_SET) < 0) { + pexit("couldn't seek to last block to test size (2)"); + } + testb[0] = 0; + testb[1] = 0; + nread = read(fd, (char *) testb, block_size); + if (nread != block_size || testb[0] != 0x3245 || testb[1] != 0x11FF || + testb[block_size-1] != 0x1F2F) { + if(nread < 0) perror("read"); + pexit("File system is too big for minor device (read)"); + } + lseek(fd, (off_t) (blocks - 1) * block_size, SEEK_SET); + testb[0] = 0; + testb[1] = 0; + if (write(fd, (char *) testb, block_size) != block_size) + pexit("File system is too big for minor device (write2)"); + lseek(fd, 0L, SEEK_SET); + free(testb); + } +#endif + + /* Make the file-system */ + + cache_init(); + +#if (MACHINE == ATARI) + if (isdev) { + char block0[BLOCK_SIZE]; + get_block((block_t) 0, block0); + /* Need to read twice; first time gets an empty block */ + get_block((block_t) 0, block0); + /* Zero parts of the boot block so the disk won't be + * recognized as a tos disk any more. */ + block0[0] = block0[1] = 0; /* branch code to boot code */ + strncpy(&block0[2], "MINIX ", (size_t) 6); + block0[16] = 0; /* number of FATS */ + block0[17] = block0[18] = 0; /* number of dir entries */ + block0[22] = block0[23] = 0; /* sectors/FAT */ + bzero(&block0[30], 480);/* boot code */ + put_block((block_t) 0, block0); + } else +#endif + + put_block((block_t) 0, zero); /* Write a null boot block. */ + + zone_shift = 0; /* for future use */ + zones = nrblocks >> zone_shift; + + super(zones, inodes); + + root_inum = alloc_inode(mode, usrid, grpid); + rootdir(root_inum); + if (simple == 0) eat_dir(root_inum); + + if (print) print_fs(); + flush(); + return(0); + + /* NOTREACHED */ +} /* end main */ + + +/*================================================================ + * sizeup - determine device size + *===============================================================*/ +block_t sizeup(device) +char *device; +{ + int fd; + struct partition entry; + block_t d; + + if ((fd = open(device, O_RDONLY)) == -1) { + perror("sizeup open"); + return 0; + } + if (ioctl(fd, DIOCGETP, &entry) == -1) { + perror("sizeup ioctl"); + entry.size = cvu64(0); + } + close(fd); + d = div64u(entry.size, block_size); + return d; +} + + +/*================================================================ + * super - construct a superblock + *===============================================================*/ + +void super(zones, inodes) +zone_t zones; +ino_t inodes; +{ + unsigned int i; + int inodeblks; + int initblks; + + zone_t initzones, nrzones, v1sq, v2sq; + zone_t zo; + struct super_block *sup; + char *buf, *cp; + + buf = alloc_block(); + + for (cp = buf; cp < &buf[block_size]; cp++) *cp = 0; + sup = (struct super_block *) buf; /* lint - might use a union */ + + sup->s_ninodes = inodes; + if (fs_version == 1) { + sup->s_nzones = zones; + } else { + sup->s_nzones = 0; /* not used in V2 - 0 forces errors early */ + sup->s_zones = zones; + } + sup->s_imap_blocks = bitmapsize((bit_t) (1 + inodes), block_size); + sup->s_zmap_blocks = bitmapsize((bit_t) zones, block_size); + inode_offset = sup->s_imap_blocks + sup->s_zmap_blocks + 2; + inodeblks = (inodes + inodes_per_block - 1) / inodes_per_block; + initblks = inode_offset + inodeblks; + initzones = (initblks + (1 << zone_shift) - 1) >> zone_shift; + nrzones = nrblocks >> zone_shift; + sup->s_firstdatazone = (initblks + (1 << zone_shift) - 1) >> zone_shift; + zoff = sup->s_firstdatazone - 1; + sup->s_log_zone_size = zone_shift; + if (fs_version == 1) { + sup->s_magic = SUPER_MAGIC; /* identify super blocks */ + v1sq = (zone_t) V1_INDIRECTS * V1_INDIRECTS; + zo = V1_NR_DZONES + (long) V1_INDIRECTS + v1sq; + sup->s_max_size = zo * block_size; + } else { + v2sq = (zone_t) V2_INDIRECTS(block_size) * V2_INDIRECTS(block_size); + zo = V2_NR_DZONES + (zone_t) V2_INDIRECTS(block_size) + v2sq; + if(fs_version == 2) { + sup->s_magic = SUPER_V2;/* identify super blocks */ + sup->s_max_size = zo * block_size; + } else { + sup->s_magic = SUPER_V3; + sup->s_block_size = block_size; + sup->s_disk_version = 0; +#define MAX_MAX_SIZE ((unsigned long) 0xffffffff) + if(MAX_MAX_SIZE/block_size < zo) { + sup->s_max_size = MAX_MAX_SIZE; + } + else { + sup->s_max_size = zo * block_size; + } + } + } + + zone_size = 1 << zone_shift; /* nr of blocks per zone */ + + if (lseek(fd, (off_t) STATIC_BLOCK_SIZE, SEEK_SET) == (off_t) -1) { + pexit("super() couldn't seek"); + } + if (write(fd, buf, STATIC_BLOCK_SIZE) != STATIC_BLOCK_SIZE) { + pexit("super() couldn't write"); + } + + /* Clear maps and inodes. */ + for (i = 2; i < initblks; i++) put_block((block_t) i, zero); + + next_zone = sup->s_firstdatazone; + next_inode = 1; + + zone_map = INODE_MAP + sup->s_imap_blocks; + + insert_bit(zone_map, 0); /* bit zero must always be allocated */ + insert_bit((block_t) INODE_MAP, 0); /* inode zero not used but + * must be allocated */ + + free(buf); +} + + +/*================================================================ + * rootdir - install the root directory + *===============================================================*/ +void rootdir(inode) +ino_t inode; +{ + zone_t z; + + z = alloc_zone(); + add_zone(inode, z, 2 * sizeof(struct direct), current_time); + enter_dir(inode, ".", inode); + enter_dir(inode, "..", inode); + incr_link(inode); + incr_link(inode); +} + + +/*================================================================ + * eat_dir - recursively install directory + *===============================================================*/ +void eat_dir(parent) +ino_t parent; +{ + /* Read prototype lines and set up directory. Recurse if need be. */ + char *token[MAX_TOKENS], *p; + char line[LINE_LEN]; + int mode, usrid, grpid, maj, min, f; + ino_t n; + zone_t z; + long size; + + while (1) { + getline(line, token); + p = token[0]; + if (*p == '$') return; + p = token[1]; + mode = mode_con(p); + usrid = atoi(token[2]); + grpid = atoi(token[3]); + if (grpid & 0200) fprintf(stderr, "A.S.Tanenbaum\n"); + n = alloc_inode(mode, usrid, grpid); + + /* Enter name in directory and update directory's size. */ + enter_dir(parent, token[0], n); + incr_size(parent, sizeof(struct direct)); + + /* Check to see if file is directory or special. */ + incr_link(n); + if (*p == 'd') { + /* This is a directory. */ + z = alloc_zone(); /* zone for new directory */ + add_zone(n, z, 2 * sizeof(struct direct), current_time); + enter_dir(n, ".", n); + enter_dir(n, "..", parent); + incr_link(parent); + incr_link(n); + eat_dir(n); + } else if (*p == 'b' || *p == 'c') { + /* Special file. */ + maj = atoi(token[4]); + min = atoi(token[5]); + size = 0; + if (token[6]) size = atoi(token[6]); + size = block_size * size; + add_zone(n, (zone_t) ((maj << 8) | min), size, current_time); + } else { + /* Regular file. Go read it. */ + if ((f = open(token[4], O_RDONLY)) < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + progname, token[4], strerror(errno)); + } else + eat_file(n, f); + } + } + +} + +/*================================================================ + * eat_file - copy file to MINIX + *===============================================================*/ +/* Zonesize >= blocksize */ +void eat_file(inode, f) +ino_t inode; +int f; +{ + int ct, i, j, k; + zone_t z; + char *buf; + long timeval; + + buf = alloc_block(); + + do { + for (i = 0, j = 0; i < zone_size; i++, j += ct) { + for (k = 0; k < block_size; k++) buf[k] = 0; + if ((ct = read(f, buf, block_size)) > 0) { + if (i == 0) z = alloc_zone(); + put_block((z << zone_shift) + i, buf); + } + } + timeval = (dflag ? current_time : file_time(f)); + if (ct) add_zone(inode, z, (long) j, timeval); + } while (ct == block_size); + close(f); +} + + + +/*================================================================ + * directory & inode management assist group + *===============================================================*/ +void enter_dir(parent, name, child) +ino_t parent, child; +char *name; +{ + /* Enter child in parent directory */ + /* Works for dir > 1 block and zone > block */ + int i, j, k, l, off; + block_t b; + zone_t z; + char *p1, *p2; + struct direct *dir_entry; + d1_inode ino1[V1_INODES_PER_BLOCK]; + d2_inode *ino2; + int nr_dzones; + + b = ((parent - 1) / inodes_per_block) + inode_offset; + off = (parent - 1) % inodes_per_block; + + if(!(dir_entry = malloc(NR_DIR_ENTRIES(block_size) * sizeof(*dir_entry)))) + pexit("couldn't allocate directory entry"); + + if(!(ino2 = malloc(V2_INODES_PER_BLOCK(block_size) * sizeof(*ino2)))) + pexit("couldn't allocate block of inodes entry"); + + if (fs_version == 1) { + get_block(b, (char *) ino1); + nr_dzones = V1_NR_DZONES; + } else { + get_block(b, (char *) ino2); + nr_dzones = V2_NR_DZONES; + } + for (k = 0; k < nr_dzones; k++) { + if (fs_version == 1) { + z = ino1[off].d1_zone[k]; + if (z == 0) { + z = alloc_zone(); + ino1[off].d1_zone[k] = z; + } + } else { + z = ino2[off].d2_zone[k]; + if (z == 0) { + z = alloc_zone(); + ino2[off].d2_zone[k] = z; + } + } + for (l = 0; l < zone_size; l++) { + get_block((z << zone_shift) + l, (char *) dir_entry); + for (i = 0; i < NR_DIR_ENTRIES(block_size); i++) { + if (dir_entry[i].d_ino == 0) { + dir_entry[i].d_ino = child; + p1 = name; + p2 = dir_entry[i].d_name; + j = sizeof(dir_entry[i].d_name); + while (j--) { + *p2++ = *p1; + if (*p1 != 0) p1++; + } + put_block((z << zone_shift) + l, (char *) dir_entry); + if (fs_version == 1) { + put_block(b, (char *) ino1); + } else { + put_block(b, (char *) ino2); + } + free(dir_entry); + free(ino2); + return; + } + } + } + } + + printf("Directory-inode %d beyond direct blocks. Could not enter %s\n", + parent, name); + pexit("Halt"); +} + + +void add_zone(n, z, bytes, cur_time) +ino_t n; +zone_t z; +long bytes, cur_time; +{ + if (fs_version == 1) { + add_z_1(n, z, bytes, cur_time); + } else { + add_z_2(n, z, bytes, cur_time); + } +} + +void add_z_1(n, z, bytes, cur_time) +ino_t n; +zone_t z; +long bytes, cur_time; +{ + /* Add zone z to inode n. The file has grown by 'bytes' bytes. */ + + int off, i; + block_t b; + zone_t indir; + zone1_t blk[V1_INDIRECTS]; + d1_inode *p; + d1_inode inode[V1_INODES_PER_BLOCK]; + + b = ((n - 1) / V1_INODES_PER_BLOCK) + inode_offset; + off = (n - 1) % V1_INODES_PER_BLOCK; + get_block(b, (char *) inode); + p = &inode[off]; + p->d1_size += bytes; + p->d1_mtime = cur_time; + for (i = 0; i < V1_NR_DZONES; i++) + if (p->d1_zone[i] == 0) { + p->d1_zone[i] = (zone1_t) z; + put_block(b, (char *) inode); + return; + } + put_block(b, (char *) inode); + + /* File has grown beyond a small file. */ + if (p->d1_zone[V1_NR_DZONES] == 0) + p->d1_zone[V1_NR_DZONES] = (zone1_t) alloc_zone(); + indir = p->d1_zone[V1_NR_DZONES]; + put_block(b, (char *) inode); + b = indir << zone_shift; + get_block(b, (char *) blk); + for (i = 0; i < V1_INDIRECTS; i++) + if (blk[i] == 0) { + blk[i] = (zone1_t) z; + put_block(b, (char *) blk); + return; + } + pexit("File has grown beyond single indirect"); +} + +void add_z_2(n, z, bytes, cur_time) +ino_t n; +zone_t z; +long bytes, cur_time; +{ + /* Add zone z to inode n. The file has grown by 'bytes' bytes. */ + + int off, i; + block_t b; + zone_t indir; + zone_t *blk; + d2_inode *p; + d2_inode *inode; + + if(!(blk = malloc(V2_INDIRECTS(block_size)*sizeof(*blk)))) + pexit("Couldn't allocate indirect block"); + + if(!(inode = malloc(V2_INODES_PER_BLOCK(block_size)*sizeof(*inode)))) + pexit("Couldn't allocate block of inodes"); + + b = ((n - 1) / V2_INODES_PER_BLOCK(block_size)) + inode_offset; + off = (n - 1) % V2_INODES_PER_BLOCK(block_size); + get_block(b, (char *) inode); + p = &inode[off]; + p->d2_size += bytes; + p->d2_mtime = cur_time; + for (i = 0; i < V2_NR_DZONES; i++) + if (p->d2_zone[i] == 0) { + p->d2_zone[i] = z; + put_block(b, (char *) inode); + free(blk); + free(inode); + return; + } + put_block(b, (char *) inode); + + /* File has grown beyond a small file. */ + if (p->d2_zone[V2_NR_DZONES] == 0) p->d2_zone[V2_NR_DZONES] = alloc_zone(); + indir = p->d2_zone[V2_NR_DZONES]; + put_block(b, (char *) inode); + b = indir << zone_shift; + get_block(b, (char *) blk); + for (i = 0; i < V2_INDIRECTS(block_size); i++) + if (blk[i] == 0) { + blk[i] = z; + put_block(b, (char *) blk); + free(blk); + free(inode); + return; + } + pexit("File has grown beyond single indirect"); +} + + +void incr_link(n) +ino_t n; +{ + /* Increment the link count to inode n */ + int off; + block_t b; + + b = ((n - 1) / inodes_per_block) + inode_offset; + off = (n - 1) % inodes_per_block; + if (fs_version == 1) { + d1_inode inode1[V1_INODES_PER_BLOCK]; + + get_block(b, (char *) inode1); + inode1[off].d1_nlinks++; + put_block(b, (char *) inode1); + } else { + d2_inode *inode2; + + if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size)))) + pexit("couldn't allocate a block of inodes"); + + get_block(b, (char *) inode2); + inode2[off].d2_nlinks++; + put_block(b, (char *) inode2); + + free(inode2); + } +} + + +void incr_size(n, count) +ino_t n; +long count; +{ + /* Increment the file-size in inode n */ + block_t b; + int off; + + b = ((n - 1) / inodes_per_block) + inode_offset; + off = (n - 1) % inodes_per_block; + if (fs_version == 1) { + d1_inode inode1[V1_INODES_PER_BLOCK]; + + get_block(b, (char *) inode1); + inode1[off].d1_size += count; + put_block(b, (char *) inode1); + } else { + d2_inode *inode2; + if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size)))) + pexit("couldn't allocate a block of inodes"); + + get_block(b, (char *) inode2); + inode2[off].d2_size += count; + put_block(b, (char *) inode2); + free(inode2); + } +} + + +/*================================================================ + * allocation assist group + *===============================================================*/ +PRIVATE ino_t alloc_inode(mode, usrid, grpid) +int mode, usrid, grpid; +{ + ino_t num; + int off; + block_t b; + + num = next_inode++; + if (num > nrinodes) pexit("File system does not have enough inodes"); + b = ((num - 1) / inodes_per_block) + inode_offset; + off = (num - 1) % inodes_per_block; + if (fs_version == 1) { + d1_inode inode1[V1_INODES_PER_BLOCK]; + + get_block(b, (char *) inode1); + inode1[off].d1_mode = mode; + inode1[off].d1_uid = usrid; + inode1[off].d1_gid = grpid; + put_block(b, (char *) inode1); + } else { + d2_inode *inode2; + + if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size)))) + pexit("couldn't allocate a block of inodes"); + + get_block(b, (char *) inode2); + inode2[off].d2_mode = mode; + inode2[off].d2_uid = usrid; + inode2[off].d2_gid = grpid; + put_block(b, (char *) inode2); + + free(inode2); + } + + /* Set the bit in the bit map. */ + /* DEBUG FIXME. This assumes the bit is in the first inode map block. */ + insert_bit((block_t) INODE_MAP, (int) num); + return(num); +} + + +PRIVATE zone_t alloc_zone() +{ + /* Allocate a new zone */ + /* Works for zone > block */ + block_t b; + int i; + zone_t z; + + z = next_zone++; + b = z << zone_shift; + if ((b + zone_size) > nrblocks) + pexit("File system not big enough for all the files"); + for (i = 0; i < zone_size; i++) + put_block(b + i, zero); /* give an empty zone */ + /* DEBUG FIXME. This assumes the bit is in the first zone map block. */ + insert_bit(zone_map, (int) (z - zoff)); /* lint, NOT OK because + * z hasn't been broken + * up into block + + * offset yet. */ + return(z); +} + + +void insert_bit(block, bit) +block_t block; +int bit; +{ + /* Insert 'count' bits in the bitmap */ + int w, s; + short *buf; + + buf = (short *) alloc_block(); + + if (block < 0) pexit("insert_bit called with negative argument"); + get_block(block, (char *) buf); + w = bit / (8 * sizeof(short)); + s = bit % (8 * sizeof(short)); + buf[w] |= (1 << s); + put_block(block, (char *) buf); + + free(buf); +} + + +/*================================================================ + * proto-file processing assist group + *===============================================================*/ +int mode_con(p) +char *p; +{ + /* Convert string to mode */ + int o1, o2, o3, mode; + char c1, c2, c3; + + c1 = *p++; + c2 = *p++; + c3 = *p++; + o1 = *p++ - '0'; + o2 = *p++ - '0'; + o3 = *p++ - '0'; + mode = (o1 << 6) | (o2 << 3) | o3; + if (c1 == 'd') mode += I_DIRECTORY; + if (c1 == 'b') mode += I_BLOCK_SPECIAL; + if (c1 == 'c') mode += I_CHAR_SPECIAL; + if (c1 == '-') mode += I_REGULAR; + if (c2 == 'u') mode += I_SET_UID_BIT; + if (c3 == 'g') mode += I_SET_GID_BIT; + return(mode); +} + +void getline(line, parse) +char *parse[MAX_TOKENS]; +char line[LINE_LEN]; +{ + /* Read a line and break it up in tokens */ + int k; + char c, *p; + int d; + + for (k = 0; k < MAX_TOKENS; k++) parse[k] = 0; + for (k = 0; k < LINE_LEN; k++) line[k] = 0; + k = 0; + parse[0] = 0; + p = line; + while (1) { + if (++k > LINE_LEN) pexit("Line too long"); + d = fgetc(proto); + if (d == EOF) pexit("Unexpected end-of-file"); + *p = d; + if (*p == '\n') lct++; + if (*p == ' ' || *p == '\t') *p = 0; + if (*p == '\n') { + *p++ = 0; + *p = '\n'; + break; + } + p++; + } + + k = 0; + p = line; + lastp = line; + while (1) { + c = *p++; + if (c == '\n') return; + if (c == 0) continue; + parse[k++] = p - 1; + do { + c = *p++; + } while (c != 0 && c != '\n'); + } +} + + +/*================================================================ + * other stuff + *===============================================================*/ +void check_mtab(devname) +char *devname; /* /dev/hd1 or whatever */ +{ +/* Check to see if the special file named in s is mounted. */ + + int n; + char special[PATH_MAX + 1], mounted_on[PATH_MAX + 1], version[10], rw_flag[10]; + + if (load_mtab("mkfs") < 0) return; + while (1) { + n = get_mtab_entry(special, mounted_on, version, rw_flag); + if (n < 0) return; + if (strcmp(devname, special) == 0) { + /* Can't mkfs on top of a mounted file system. */ + fprintf(stderr, "%s: %s is mounted on %s\n", + progname, devname, mounted_on); + exit(1); + } + } +} + + +long file_time(f) +int f; +{ +#ifdef UNIX + struct stat statbuf; + fstat(f, &statbuf); + return(statbuf.st_mtime); +#else /* fstat not supported by DOS */ + return(0L); +#endif +} + + +void pexit(s) +char *s; +{ + fprintf(stderr, "%s: %s\n", progname, s); + if (lct != 0) + fprintf(stderr, "Line %d being processed when error detected.\n", lct); + flush(); + exit(2); +} + + +void copy(from, to, count) +char *from, *to; +int count; +{ + while (count--) *to++ = *from++; +} + +char *alloc_block() +{ + char *buf; + + if(!(buf = malloc(block_size))) { + pexit("couldn't allocate filesystem buffer"); + } + bzero(buf, block_size); + + return buf; +} + +void print_fs() +{ + int i, j; + ino_t k; + d1_inode inode1[V1_INODES_PER_BLOCK]; + d2_inode *inode2; + unsigned short *usbuf; + block_t b, inode_limit; + struct direct *dir; + + if(!(inode2 = malloc(V2_INODES_PER_BLOCK(block_size)))) + pexit("couldn't allocate a block of inodes"); + + if(!(dir = malloc(NR_DIR_ENTRIES(block_size)*sizeof(*dir)))) + pexit("malloc of directory entry failed"); + + usbuf = (unsigned short *) alloc_block(); + + get_super_block((char *) usbuf); + printf("\nSuperblock: "); + for (i = 0; i < 8; i++) printf("%06o ", usbuf[i]); + get_block((block_t) 2, (char *) usbuf); + printf("...\nInode map: "); + for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]); + get_block((block_t) 3, (char *) usbuf); + printf("...\nZone map: "); + for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]); + printf("...\n"); + + free(usbuf); + usbuf = NULL; + + k = 0; + for (b = inode_offset; k < nrinodes; b++) { + if (fs_version == 1) { + get_block(b, (char *) inode1); + } else { + get_block(b, (char *) inode2); + } + for (i = 0; i < inodes_per_block; i++) { + k = inodes_per_block * (int) (b - inode_offset) + i + 1; + /* Lint but OK */ + if (k > nrinodes) break; + if (fs_version == 1) { + if (inode1[i].d1_mode != 0) { + printf("Inode %2d: mode=", k); + printf("%06o", inode1[i].d1_mode); + printf(" uid=%2d gid=%2d size=", + inode1[i].d1_uid, inode1[i].d1_gid); + printf("%6ld", inode1[i].d1_size); + printf(" zone[0]=%d\n", inode1[i].d1_zone[0]); + } + if ((inode1[i].d1_mode & I_TYPE) == I_DIRECTORY) { + /* This is a directory */ + get_block(inode1[i].d1_zone[0], (char *) dir); + for (j = 0; j < NR_DIR_ENTRIES(block_size); j++) + if (dir[j].d_ino) + printf("\tInode %2d: %s\n", dir[j].d_ino, dir[j].d_name); + } + } else { + if (inode2[i].d2_mode != 0) { + printf("Inode %2d: mode=", k); + printf("%06o", inode2[i].d2_mode); + printf(" uid=%2d gid=%2d size=", + inode2[i].d2_uid, inode2[i].d2_gid); + printf("%6ld", inode2[i].d2_size); + printf(" zone[0]=%ld\n", inode2[i].d2_zone[0]); + } + if ((inode2[i].d2_mode & I_TYPE) == I_DIRECTORY) { + /* This is a directory */ + get_block(inode2[i].d2_zone[0], (char *) dir); + for (j = 0; j < NR_DIR_ENTRIES(block_size); j++) + if (dir[j].d_ino) + printf("\tInode %2d: %s\n", dir[j].d_ino, dir[j].d_name); + } + } + } + } + + printf("%d inodes used. %d zones used.\n", next_inode - 1, next_zone); + free(dir); + free(inode2); +} + + +int read_and_set(n) +block_t n; +{ +/* The first time a block is read, it returns all 0s, unless there has + * been a write. This routine checks to see if a block has been accessed. + */ + + int w, s, mask, r; + + if (sizeof(char *) == 2 && n >= MAX_INIT) pexit("can't initialize past 128M"); + w = n / 8; + s = n % 8; + mask = 1 << s; + r = (umap[w] & mask ? 1 : 0); + umap[w] |= mask; + return(r); +} + +void usage() +{ + fprintf(stderr, + "Usage: %s [-12dlot] [-b blocks] [-i inodes] [-B blocksize] special [proto]\n", + progname); + exit(1); +} + +/*================================================================ + * get_block & put_block for MS-DOS + *===============================================================*/ +#ifdef DOS + +/* + * These are the get_block and put_block routines + * when compiling & running mkfs.c under MS-DOS. + * + * It requires the (asembler) routines absread & abswrite + * from the file diskio.asm. Since these routines just do + * as they are told (read & write the sector specified), + * a local cache is used to minimize the i/o-overhead for + * frequently used blocks. + * + * The global variable "file" determines whether the output + * is to a disk-device or to a binary file. + */ + + +#define PH_SECTSIZE 512 /* size of a physical disk-sector */ + + +char *derrtab[14] = { + "no error", + "disk is read-only", + "unknown unit", + "device not ready", + "bad command", + "data error", + "internal error: bad request structure length", + "seek error", + "unknown media type", + "sector not found", + "printer out of paper (?)", + "write fault", + "read error", + "general error" +}; + +#define CACHE_SIZE 20 /* 20 block-buffers */ + + +struct cache { + char blockbuf[BLOCK_SIZE]; + block_t blocknum; + int dirty; + int usecnt; +} cache[CACHE_SIZE]; + + +void special(string) +char *string; +{ + + if (string[1] == ':' && string[2] == 0) { + /* Format: d: or d:fname */ + disk = (string[0] & ~32) - 'A'; + if (disk > 1 && !override) /* safety precaution */ + pexit("Bad drive specifier for special"); + } else { + file = 1; + if ((fd = creat(string, BWRITE)) == 0) + pexit("Can't open special file"); + } +} + +void get_block(n, buf) +block_t n; +char *buf; +{ + /* Get a block to the user */ + struct cache *bp, *fp; + + /* First access returns a zero block */ + if (read_and_set(n) == 0) { + copy(zero, buf, block_size); + return; + } + + /* Look for block in cache */ + fp = 0; + for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) { + if (bp->blocknum == n) { + copy(bp, buf, block_size); + bp->usecnt++; + return; + } + + /* Remember clean block */ + if (bp->dirty == 0) + if (fp) { + if (fp->usecnt > bp->usecnt) fp = bp; + } else + fp = bp; + } + + /* Block not in cache, get it */ + if (!fp) { + /* No clean buf, flush one */ + for (bp = cache, fp = cache; bp < &cache[CACHE_SIZE]; bp++) + if (fp->usecnt > bp->usecnt) fp = bp; + mx_write(fp->blocknum, fp); + } + mx_read(n, fp); + fp->dirty = 0; + fp->usecnt = 0; + fp->blocknum = n; + copy(fp, buf, block_size); +} + +void put_block(n, buf) +block_t n; +char *buf; +{ + /* Accept block from user */ + struct cache *fp, *bp; + + (void) read_and_set(n); + + /* Look for block in cache */ + fp = 0; + for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) { + if (bp->blocknum == n) { + copy(buf, bp, block_size); + bp->dirty = 1; + return; + } + + /* Remember clean block */ + if (bp->dirty == 0) + if (fp) { + if (fp->usecnt > bp->usecnt) fp = bp; + } else + fp = bp; + } + + /* Block not in cache */ + if (!fp) { + /* No clean buf, flush one */ + for (bp = cache, fp = cache; bp < &cache[CACHE_SIZE]; bp++) + if (fp->usecnt > bp->usecnt) fp = bp; + mx_write(fp->blocknum, fp); + } + fp->dirty = 1; + fp->usecnt = 1; + fp->blocknum = n; + copy(buf, fp, block_size); +} + +void cache_init() +{ + struct cache *bp; + for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) bp->blocknum = -1; +} + +void flush() +{ + /* Flush all dirty blocks to disk */ + struct cache *bp; + + for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) + if (bp->dirty) { + mx_write(bp->blocknum, bp); + bp->dirty = 0; + } +} + +/*================================================================== + * hard read & write etc. + *=================================================================*/ +#define MAX_RETRIES 5 + + +void mx_read(blocknr, buf) +int blocknr; +char *buf; +{ + + /* Read the requested MINIX-block in core */ + char (*bp)[PH_SECTSIZE]; + int sectnum, retries, err; + + if (file) { + lseek(fd, (off_t) blocknr * block_size, 0); + if (read(fd, buf, block_size) != block_size) + pexit("mx_read: error reading file"); + } else { + sectnum = blocknr * (block_size / PH_SECTSIZE); + for (bp = buf; bp < &buf[block_size]; bp++) { + retries = MAX_RETRIES; + do + err = absread(disk, sectnum, bp); + while (err && --retries); + + if (retries) { + sectnum++; + } else { + dexit("mx_read", sectnum, err); + } + } + } +} + +void mx_write(blocknr, buf) +int blocknr; +char *buf; +{ + /* Write the MINIX-block to disk */ + char (*bp)[PH_SECTSIZE]; + int retries, sectnum, err; + + if (file) { + lseek(fd, blocknr * block_size, 0); + if (write(fd, buf, block_size) != block_size) { + pexit("mx_write: error writing file"); + } + } else { + sectnum = blocknr * (block_size / PH_SECTSIZE); + for (bp = buf; bp < &buf[block_size]; bp++) { + retries = MAX_RETRIES; + do { + err = abswrite(disk, sectnum, bp); + } while (err && --retries); + + if (retries) { + sectnum++; + } else { + dexit("mx_write", sectnum, err); + } + } + } +} + + +void dexit(s, sectnum, err) +int sectnum, err; +char *s; +{ + printf("Error: %s, sector: %d, code: %d, meaning: %s\n", + s, sectnum, err, derrtab[err]); + exit(2); +} + +#endif + +/*================================================================ + * get_block & put_block for UNIX + *===============================================================*/ +#ifdef UNIX + +void special(string) +char *string; +{ + fd = creat(string, 0777); + close(fd); + fd = open(string, O_RDWR); + if (fd < 0) pexit("Can't open special file"); +#if (MACHINE == ATARI) + { + struct stat statbuf; + + if (fstat(fd, &statbuf) < 0) return; + isdev = (statbuf.st_mode & S_IFMT) == S_IFCHR + || + (statbuf.st_mode & S_IFMT) == S_IFBLK + ; + } +#endif +} + + + +void get_block(n, buf) +block_t n; +char *buf; +{ +/* Read a block. */ + + int k; + + /* First access returns a zero block */ + if (read_and_set(n) == 0) { + copy(zero, buf, block_size); + return; + } + lseek(fd, (off_t) n * block_size, SEEK_SET); + k = read(fd, buf, block_size); + if (k != block_size) { + pexit("get_block couldn't read"); + } +} + +void get_super_block(buf) +char *buf; +{ +/* Read a block. */ + + int k; + + if(lseek(fd, (off_t) SUPER_BLOCK_BYTES, SEEK_SET) < 0) { + perror("lseek"); + pexit("seek failed"); + } + k = read(fd, buf, STATIC_BLOCK_SIZE); + if (k != STATIC_BLOCK_SIZE) { + pexit("get_super_block couldn't read"); + } +} + +void put_block(n, buf) +block_t n; +char *buf; +{ +/* Write a block. */ + + (void) read_and_set(n); + + /* XXX - check other lseeks too. */ + if (lseek(fd, (off_t) n * block_size, SEEK_SET) == (off_t) -1) { + pexit("put_block couldn't seek"); + } + if (write(fd, buf, block_size) != block_size) { + pexit("put_block couldn't write"); + } +} + + +/* Dummy routines to keep source file clean from #ifdefs */ + +void flush() +{ + return; +} + +void cache_init() +{ + return; +} + +#endif diff --git a/commands/simple/mknod.c b/commands/simple/mknod.c new file mode 100755 index 000000000..223e2c1e6 --- /dev/null +++ b/commands/simple/mknod.c @@ -0,0 +1,70 @@ +/* mknod - build a special file Author: Andy Tanenbaum */ + +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <minix/minlib.h> +#include <errno.h> +#include <stdio.h> + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void badcomm, (void)); +_PROTOTYPE(void badfifo, (void)); +_PROTOTYPE(void badchar, (void)); +_PROTOTYPE(void badblock, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ +/* Mknod name b/c major minor makes a node. */ + + int mode, major, minor, dev; + + if (argc < 3) badcomm(); + if (*argv[2] != 'b' && *argv[2] != 'c' && *argv[2] != 'p') badcomm(); + if (*argv[2] == 'p' && argc != 3) badfifo(); + if (*argv[2] == 'c' && argc != 5) badchar(); + if (*argv[2] == 'b' && argc != 5) badblock(); + if (*argv[2] == 'p') { + mode = 010666; + dev = 0; + } else { + mode = (*argv[2] == 'b' ? 060666 : 020666); + major = atoi(argv[3]); + minor = atoi(argv[4]); + if (major - 1 > 0xFE || minor > 0xFF) badcomm(); + dev = (major << 8) | minor; + } + if (mknod(argv[1], mode, dev) < 0) { + int err = errno; + std_err("mknod: "); + errno = err; + perror(argv[1]); + } + return(0); +} + +void badcomm() +{ + std_err("Usage: mknod name b/c/p [major minor]\n"); + exit(1); +} + +void badfifo() +{ + std_err("Usage: mknod name p\n"); + exit(1); +} + +void badchar() +{ + std_err("Usage: mknod name c major minor\n"); + exit(1); +} + +void badblock() +{ + std_err("Usage: mknod name b major minor\n"); + exit(1); +} diff --git a/commands/simple/mkproto.c b/commands/simple/mkproto.c new file mode 100755 index 000000000..09ce30030 --- /dev/null +++ b/commands/simple/mkproto.c @@ -0,0 +1,248 @@ +/* mkproto - make an mkfs prototype Author: Andrew Cagney */ + +/* Submitted by: cagney@chook.ua.oz (Andrew Cagney - aka Noid) */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <limits.h> +#include <dirent.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> + +/* The default values for the prototype file */ +#define DEF_UID 2 /* bin */ +#define DEF_GID 1 /* daemon group */ +#define DEF_PROT 0555 /* a=re */ +#define DEF_BLOCKS 360 +#define DEF_INODES 63 +#define DEF_INDENTSTR "\t" + +#define major(x) ( (x>>8) & 0377) +#define minor(x) (x & 0377) + +/* Globals. */ +int count, origlen, tabs; +int gid, uid, prot, same_uid, same_gid, same_prot, blocks, inodes; +int block_given, inode_given, dprot; +char *indentstr; +char *proto_file, *top; +FILE *outfile; + +extern int optind; +extern char *optarg; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void descend, (char *dirname)); +_PROTOTYPE(void display_attrib, (char *name, struct stat *st)); +_PROTOTYPE(void usage, (char *binname)); +_PROTOTYPE(void open_outfile, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + char *dir; + struct stat st; + int op; + + gid = DEF_GID; + uid = DEF_UID; + prot = DEF_PROT; + blocks = DEF_BLOCKS; + inodes = DEF_INODES; + indentstr = DEF_INDENTSTR; + inode_given = 0; + block_given = 0; + top = 0; + same_uid = 0; + same_gid = 0; + same_prot = 0; + while ((op = getopt(argc, argv, "b:g:i:p:t:u:d:s")) != EOF) { + switch (op) { + case 'b': + blocks = atoi(optarg); + block_given = 1; + break; + case 'g': + gid = atoi(optarg); + if (gid == 0) usage(argv[0]); + same_gid = 0; + break; + case 'i': + inodes = atoi(optarg); + inode_given = 1; + break; + case 'p': + sscanf(optarg, "%o", &prot); + if (prot == 0) usage(argv[0]); + same_prot = 0; + break; + case 's': + same_prot = 1; + same_uid = 1; + same_gid = 1; + break; + case 't': top = optarg; break; + case 'u': + uid = atoi(optarg); + if (uid == 0) usage(argv[0]); + same_uid = 0; + break; + case 'd': indentstr = optarg; break; + default: /* Illegal options */ + usage(argv[0]); + } + } + + if (optind >= argc) { + usage(argv[0]); + } else { + dir = argv[optind]; + optind++; + proto_file = argv[optind]; + } + if (!top) top = dir; + open_outfile(); + if (block_given && !inode_given) inodes = (blocks / 3) + 8; + if (!block_given && inode_given) usage(argv[0]); + count = 1; + tabs = 0; + origlen = strlen(dir); + + /* Check that it really is a directory */ + stat(dir, &st); + if ((st.st_mode & S_IFMT) != S_IFDIR) { + fprintf(stderr, "mkproto: %s must be a directory\n", dir); + usage(argv[0]); + } + fprintf(outfile, "boot\n%d %d\n", blocks, inodes); + display_attrib("", &st); + fprintf(outfile, "\n"); + descend(dir); + fprintf(outfile, "$\n"); + return(0); +} + +/* Output the prototype spec for this directory. */ +void descend(dirname) +char *dirname; +{ + struct dirent *dp; + DIR *dirp; + char *name, *temp, *tempend; + int i; + struct stat st; + mode_t mode; + + dirp = opendir(dirname); + if (dirp == NULL) { + fprintf(stderr, "unable to open directory %s\n", dirname); + return; + } + tabs++; + temp = (char *) malloc(sizeof(char) * strlen(dirname) +1 + PATH_MAX); + strcpy(temp, dirname); + strcat(temp, "/"); + tempend = &temp[strlen(temp)]; + + for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { + name = dp->d_name; + + count++; + strcpy(tempend, name); + + if (stat(temp, &st) == -1) { + fprintf(stderr, "cant get status of '%s' \n", temp); + continue; + } + if (name[0] == '.' && (name[1] == 0 || + (name[1] == '.' && name[2] == 0))) + continue; + + display_attrib(name, &st); + + mode = st.st_mode & S_IFMT; + if (mode == S_IFDIR) { + fprintf(outfile, "\n"); + descend(temp); + for (i = 0; i < tabs; i++) { + fprintf(outfile, indentstr); + } + fprintf(outfile, "$\n"); + continue; + } + if (mode == S_IFBLK || mode == S_IFCHR) { + fprintf(outfile, " %d %d\n", major(st.st_rdev), minor(st.st_rdev)); + continue; + } + if (mode == S_IFREG) { + i = origlen; + fprintf(outfile, "%s%s", indentstr, top); + while (temp[i] != '\0') { + fputc(temp[i], outfile); + i++; + } + fprintf(outfile, "\n"); + continue; + } + fprintf(outfile, " /dev/null"); + fprintf(stderr,"File\n\t%s\n has an invalid mode, made empty.\n",temp); + } + closedir(dirp); + free(temp); + tabs--; +} + + +void display_attrib(name, st) +char *name; +struct stat *st; +{ +/* Output the specification for a single file */ + + int i; + + if (same_uid) uid = st->st_uid; + if (same_gid) gid = st->st_gid; + if (same_prot) + prot = st->st_mode & 0777; /***** This one is a bit shady *****/ + for (i = 0; i < tabs; i++) fprintf(outfile, indentstr); + fprintf(outfile, "%s%s%c%c%c%3o %d %d", + name, + *name == '\0' ? "" : indentstr, /* stop the tab for a null name */ + (st->st_mode & S_IFMT) == S_IFDIR ? 'd' : + (st->st_mode & S_IFMT) == S_IFCHR ? 'c' : + (st->st_mode & S_IFMT) == S_IFBLK ? 'b' : + '-', /* file type */ + (st->st_mode & S_ISUID) ? 'u' : '-', /* set uid */ + (st->st_mode & S_ISGID) ? 'g' : '-', /* set gid */ + prot, + uid, + gid); +} + +void usage(binname) +char *binname; +{ + fprintf(stderr, "Usage: %s [options] source_directory [prototype_file]\n", binname); + fprintf(stderr, "options:\n"); + fprintf(stderr, " -b n\t\t file system size is n blocks (default %d)\n", DEF_BLOCKS); + fprintf(stderr, " -d STRING\t define the indentation characters (default %s)\n", "(none)"); + fprintf(stderr, " -g n\t\t use n as the gid on all files (default %d)\n", DEF_GID); + fprintf(stderr, " -i n\t\t file system gets n i-nodes (default %d)\n", DEF_INODES); + fprintf(stderr, " -p nnn\t use nnn (octal) as mode on all files (default %o)\n", DEF_PROT); + fprintf(stderr, " -s \t\t use the same uid, gid and mode as originals have\n"); + fprintf(stderr, " -t ROOT\t inital path prefix for each entry\n"); + fprintf(stderr, " -u n\t\t use nnn as the uid on all files (default %d)\n", DEF_UID); + exit(1); +} + +void open_outfile() +{ + if (proto_file == NULL) + outfile = stdout; + else if ((outfile = fopen(proto_file, "w")) == NULL) + fprintf(stderr, "Cannot create %s\n ", proto_file); +} diff --git a/commands/simple/mkswap.c b/commands/simple/mkswap.c new file mode 100755 index 000000000..19a46e473 --- /dev/null +++ b/commands/simple/mkswap.c @@ -0,0 +1,197 @@ +/* mkswap 1.0 - Initialize a swap partition or file + * Author: Kees J. Bot + * 6 Jan 2001 + */ +#define nil ((void*)0) +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <unistd.h> +#include <dirent.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <minix/config.h> +#include <minix/const.h> +#include <minix/type.h> +#include <minix/u64.h> +#include <minix/partition.h> +#include <minix/swap.h> +#include <servers/fs/const.h> +#include <servers/fs/type.h> +#include <servers/fs/super.h> + +static void usage(void) +{ + fprintf(stderr, "Usage: mkswap [-f] device-or-file [size[km]]\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + int first; + int i; + char *file; + unsigned long offset, size, devsize; + int fd; + struct stat st; + ssize_t r; + struct super_block super; + swap_hdr_t swap_hdr; + static u8_t block[MAX_BLOCK_SIZE]; + + first= 0; + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++]+1; + + if (opt[0] == '-' && opt[1] == 0) break; /* -- */ + + while (*opt != 0) switch (*opt++) { + case 'f': first= 1; break; + default: usage(); + } + } + if (i == argc) usage(); + file= argv[i++]; + + size= 0; + if (i < argc) { + char *end; + unsigned long m; + + size= strtoul(argv[i], &end, 10); + if (end == argv[i]) usage(); + m= 1024; + if (*end != 0) { + switch (*end) { + case 'm': case 'M': m *= 1024; /*FALL THROUGH*/ + case 'k': case 'K': end++; break; + } + } + if (*end != 0 || size == -1 + || (size * m) / m != size || (size *= m) <= SWAP_OFFSET + ) { + fprintf(stderr, "mkswap: %s: Bad size\n", argv[i]); + exit(1); + } + i++; + } + if (i != argc) usage(); + + /* Open the device or file. */ + if ((fd= open(file, O_RDWR | O_CREAT | O_TRUNC, 0600)) < 0) { + fprintf(stderr, "mkswap: Can't open %s: %s\n", file, strerror(errno)); + exit(1); + } + + /* File or device? */ + (void) fstat(fd, &st); + if (S_ISBLK(st.st_mode) || S_ISCHR(st.st_mode)) { + struct partition part; + + /* How big is the partition? */ + if (ioctl(fd, DIOCGETP, &part) < 0) { + fprintf(stderr, "mkswap: Can't determine the size of %s: %s\n", + file, strerror(errno)); + exit(1); + } + devsize= cv64ul(part.size); + offset= 0; + + if (!first) { + /* Is there a file system? */ + r= -1; + if (lseek(fd, SUPER_BLOCK_BYTES, SEEK_SET) == -1 + || (r= read(fd, block, STATIC_BLOCK_SIZE)) < STATIC_BLOCK_SIZE + ) { + fprintf(stderr, "mkswap: %s: %s\n", + file, r >= 0 ? "End of file" : strerror(errno)); + exit(1); + } + memcpy(&super, block, sizeof(super)); + if (super.s_magic == SUPER_MAGIC) { + offset= (unsigned long) super.s_nzones * STATIC_BLOCK_SIZE; + } else + if (super.s_magic == SUPER_V2) { + offset= (unsigned long) super.s_zones * STATIC_BLOCK_SIZE; + } else if (super.s_magic == SUPER_V3) { + offset= (unsigned long) super.s_zones * super.s_block_size; + } else { + first= 1; + } + } + if (size == 0) size= devsize - offset; + if (size == 0 || offset + size > devsize) { + fprintf(stderr, "mkswap: There is no room on %s for ", file); + if (size > 0) fprintf(stderr, "%lu kilobytes of ", size/1024); + fprintf(stderr, "swapspace\n"); + if (offset > 0) { + fprintf(stderr, "(Use the -f flag to wipe the file system)\n"); + } + exit(1); + } + } else + if (S_ISREG(st.st_mode)) { + /* Write to the swap file to guarantee space for MM. */ + unsigned long n; + + if (size == 0) { + fprintf(stderr, "mkswap: No size specified for %s\n", file); + usage(); + } + + memset(block, 0, sizeof(block)); + for (n= 0; n < size; n += r) { + r= size > sizeof(block) ? sizeof(block) : size; + if ((r= write(fd, block, r)) <= 0) { + fprintf(stderr, "mkswap: %s: %s\n", + file, r == 0 ? "End of file" : strerror(errno)); + exit(1); + } + } + first= 1; + } else { + fprintf(stderr, "mkswap: %s is not a device or a file\n", file); + exit(1); + } + + if (offset < SWAP_OFFSET) { + offset += SWAP_OFFSET; + if (size < SWAP_OFFSET) size= 0; else size -= SWAP_OFFSET; + } + swap_hdr.sh_magic[0]= SWAP_MAGIC0; + swap_hdr.sh_magic[1]= SWAP_MAGIC1; + swap_hdr.sh_magic[2]= SWAP_MAGIC2; + swap_hdr.sh_magic[3]= SWAP_MAGIC3; + swap_hdr.sh_version= SH_VERSION; + swap_hdr.sh_priority= 0; + swap_hdr.sh_offset= offset; + swap_hdr.sh_swapsize= size; + + r= -1; + if (lseek(fd, SWAP_BOOTOFF, SEEK_SET) == -1 + || (r= read(fd, block, sizeof(block))) < sizeof(block) + ) { + fprintf(stderr, "mkswap: %s: %s\n", file, + file, r >= 0 ? "End of file" : strerror(errno)); + exit(1); + } + + r= (first ? SWAP_BOOTOFF : OPTSWAP_BOOTOFF) - SWAP_BOOTOFF; + memcpy(block + r, &swap_hdr, sizeof(swap_hdr)); + + r= -1; + if (lseek(fd, SWAP_BOOTOFF, SEEK_SET) == -1 + || (r= write(fd, block, sizeof(block))) < sizeof(block) + ) { + fprintf(stderr, "mkswap: %s: %s\n", file, + file, r >= 0 ? "End of file" : strerror(errno)); + exit(1); + } + printf("%s: swapspace at offset %lu, size %lu kilobytes\n", + file, offset / 1024, size / 1024); + return 0; +} diff --git a/commands/simple/modem.c b/commands/simple/modem.c new file mode 100755 index 000000000..f7ed1b083 --- /dev/null +++ b/commands/simple/modem.c @@ -0,0 +1,251 @@ +/* modem - Put modem into DIALIN or DIALOUT mode. Author: F. van Kempen */ + +/* Exit: 0 OK, suspended/restarted GETTY + * 1 UNIX error + * 2 Process busy + * Version: 1.3 12/30/89 + * + * Author: F. van Kempen, MicroWalt Corporation + * + * All fancy stuff removed, see getty.c. Kees J. Bot. + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include <utmp.h> +#include <errno.h> + +char PATH_UTMP[] = "/etc/utmp"; /* current logins */ + +_PROTOTYPE(void usage , (void)); +_PROTOTYPE(int main , (int argc , char *argv [])); +_PROTOTYPE(void sendcodes , (char *tty, char *codes)); + + +void usage() +{ + fprintf(stderr, +"Usage: modem [-sio] [-I in-codes] [-O out-codes] line [command args ...]\n"); + exit(1); +} + + +main(argc, argv) +int argc; +char *argv[]; +{ + struct utmp entry; + char devtty[1024], *tty; + char **command; + int ex_code = 0; + int fd, i, slot, getty; + struct stat st; + enum { TOGGLE, DIALIN, DIALOUT } mode= TOGGLE; + int silent = 0; + _PROTOTYPE(void (*hsig), (int)); + _PROTOTYPE(void (*isig), (int)); + _PROTOTYPE(void (*qsig), (int)); + _PROTOTYPE(void (*tsig), (int)); + pid_t pid; + int r, status; + uid_t uid = getuid(); + gid_t gid = getgid(); + char *in_codes, *out_codes; + + i = 1; + while (i < argc && argv[i][0] == '-') { + char *opt = argv[i++] + 1; + + if (opt[0] == '-' && opt[1] == 0) break; + + while (*opt != 0) { + switch (*opt++) { + case 's': /* silent mode */ + silent = 1; + break; + case 'i': /* DIAL-IN mode: suspend GETTY */ + mode = DIALIN; + break; + case 'o': /* DIAL-OUT mode: restart GETTY */ + mode = DIALOUT; + break; + case 'I': /* code to switch modem to dial-in */ + if (*opt == 0) { + if (i == argc) usage(); + opt = argv[i++]; + } + in_codes = opt; + opt = ""; + break; + case 'O': /* code to switch modem to dial-out */ + if (*opt == 0) { + if (i == argc) usage(); + opt = argv[i++]; + } + out_codes = opt; + opt = ""; + break; + default: + usage(); + } + } + } + + if (i == argc) usage(); + tty = argv[i++]; /* Modem line */ + + if (mode != TOGGLE && i != argc) usage(); + command = argv + i; /* Command to execute (if any). */ + + if (strchr(tty, '/') == NULL) { + strcpy(devtty, "/dev/"); + strncat(devtty, tty, 1024 - 6); + tty = devtty; + } + + if (stat(tty, &st) < 0) { + fprintf(stderr, "modem: %s: %s\n", tty, strerror(errno)); + exit(1); + } + + if (!S_ISCHR(st.st_mode)) { + fprintf(stderr, "%s is not a tty\n", tty); + exit(1); + } + + /* Find the utmp slot number for the line. */ + if ((fd= open(tty, O_RDONLY)) < 0 || (slot= fttyslot(fd)) == 0) { + fprintf(stderr, "modem: %s: %s\n", tty, strerror(errno)); + exit(1); + } + close(fd); + + /* Read the UTMP file to find out the PID and STATUS of the GETTY. */ + entry.ut_type= 0; + if ((fd = open(PATH_UTMP, O_RDONLY)) < 0 + || lseek(fd, (off_t) slot * sizeof(entry), SEEK_SET) < 0 + || read(fd, &entry, sizeof(entry)) < 0 + ) { + fprintf(stderr, "modem: cannot read UTMP !\n"); + exit(1); + } + close(fd); + + hsig= signal(SIGHUP, SIG_IGN); + isig= signal(SIGINT, SIG_IGN); + qsig= signal(SIGQUIT, SIG_IGN); + tsig= signal(SIGTERM, SIG_IGN); + + /* Process the terminal entry if we got one. */ + switch (entry.ut_type) { + case LOGIN_PROCESS: /* getty waiting for a call */ + getty = 1; + break; + case USER_PROCESS: /* login or user-shell */ + if (!silent) fprintf(stderr, "modem: line is busy.\n"); + exit(2); + break; + default: + getty = 0; + } + + for (i = (mode == TOGGLE) ? 0 : 1; i < 2; i++) { + /* Now perform the desired action (DIALIN or DIALOUT). */ + switch (mode) { + case DIALOUT: + case TOGGLE: + if (getty) kill(entry.ut_pid, SIGUSR1); /* suspend getty */ + chown(tty, uid, st.st_gid); /* give line to user */ + chmod(tty, 0600); + if (out_codes != NULL) sendcodes(tty, out_codes); + if (!silent) printf("modem on %s set for dialout.\n", tty); + break; + case DIALIN: + if (in_codes != NULL) sendcodes(tty, in_codes); + chown(tty, 0, st.st_gid); /* revoke access */ + chmod(tty, 0600); + if (getty) kill(entry.ut_pid, SIGUSR2); /* restart getty */ + if (!silent) printf("modem on %s set for dialin.\n", tty); + } + if (mode == TOGGLE) { + /* Start the command to run */ + pid_t pid; + int status; + + switch ((pid = fork())) { + case -1: + fprintf(stderr, "modem: fork(): %s\n", strerror(errno)); + ex_code= 1; + break; + case 0: + setgid(gid); + setuid(uid); + (void) signal(SIGHUP, hsig); + (void) signal(SIGINT, isig); + (void) signal(SIGQUIT, qsig); + (void) signal(SIGTERM, tsig); + execvp(command[0], command); + fprintf(stderr, "modem: %s: %s\n", + command[0], strerror(errno)); + _exit(127); + default: + while ((r= wait(&status)) != pid) { + if (r == -1 && errno != EINTR) break; + } + if (r == -1 || status != 0) ex_code = 1; + } + mode = DIALIN; + } + } + exit(ex_code); +} + +void sendcodes(tty, codes) +char *tty, *codes; +{ + int fd; + int c; + char buf[1024], *bp = buf; + + if ((fd = open(tty, O_RDWR|O_NONBLOCK)) < 0) { + fprintf(stderr, "modem: can't send codes to %s: %s\n", + tty, strerror(errno)); + return; + } + while ((c = *codes++) != 0) { +fprintf(stderr, "%d\n", __LINE__); + if (c == '\\') { + if ((c = *codes++) == 0) break; + if (c == 'r') c= '\r'; + if (c == 'n') c= '\n'; + } + *bp++ = c; + if (bp == buf + sizeof(buf) || c == '\r' || c == '\n') { +fprintf(stderr, "%d\n", __LINE__); + write(fd, buf, bp - buf); +fprintf(stderr, "%d\n", __LINE__); + do {sleep(1); +fprintf(stderr, "%d\n", __LINE__); + fprintf(stderr, "%d\n", read(fd, buf, sizeof(buf))); + }while (read(fd, buf, sizeof(buf)) > 0); +fprintf(stderr, "%d\n", __LINE__); + bp = buf; + } + } + if (bp > buf) { +fprintf(stderr, "%d\n", __LINE__); + write(fd, buf, bp - buf); +fprintf(stderr, "%d\n", __LINE__); + do sleep(1); while (read(fd, buf, sizeof(buf)) > 0); +fprintf(stderr, "%d\n", __LINE__); + } + close(fd); +} diff --git a/commands/simple/mount.c b/commands/simple/mount.c new file mode 100755 index 000000000..8fe83842a --- /dev/null +++ b/commands/simple/mount.c @@ -0,0 +1,206 @@ +/* mount - mount a file system Author: Andy Tanenbaum */ + +#include <errno.h> +#include <sys/types.h> +#include <limits.h> +#include <stdlib.h> +#include <dirent.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <minix/config.h> +#include <minix/const.h> +#include <minix/minlib.h> +#include <minix/swap.h> +#include <sys/svrctl.h> +#include <stdio.h> +#include "../../servers/fs/const.h" + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void list, (void)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(void tell, (char *this)); +_PROTOTYPE(void swapon, (char *file)); + +static u8_t MAGIC[] = { SWAP_MAGIC0, SWAP_MAGIC1, SWAP_MAGIC2, SWAP_MAGIC3 }; + +int main(argc, argv) +int argc; +char *argv[]; +{ + int i, ro, swap, n, v; + char **ap, *vs, *opt, *err; + char special[PATH_MAX+1], mounted_on[PATH_MAX+1], version[10], rw_flag[10]; + + if (argc == 1) list(); /* just list /etc/mtab */ + ro = 0; + swap = 0; + ap = argv+1; + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + opt = argv[i]+1; + while (*opt != 0) switch (*opt++) { + case 'r': ro = 1; break; + case 's': swap = 1; break; + default: usage(); + } + } else { + *ap++ = argv[i]; + } + } + *ap = NULL; + argc = (ap - argv); + + if (ro && swap) usage(); + + if (swap) { + if (argc != 2) usage(); + swapon(argv[1]); + tell(argv[1]); + tell(" is swapspace\n"); + } else { + if (argc != 3) usage(); + if (mount(argv[1], argv[2], ro) < 0) { + err = strerror(errno); + std_err("mount: Can't mount "); + std_err(argv[1]); + std_err(" on "); + std_err(argv[2]); + std_err(": "); + std_err(err); + std_err("\n"); + exit(1); + } + /* The mount has completed successfully. Tell the user. */ + tell(argv[1]); + tell(" is read-"); + tell(ro ? "only" : "write"); + tell(" mounted on "); + tell(argv[2]); + tell("\n"); + } + + /* Update /etc/mtab. */ + n = load_mtab("mount"); + if (n < 0) exit(1); /* something is wrong. */ + + /* Loop on all the /etc/mtab entries, copying each one to the output buf. */ + while (1) { + n = get_mtab_entry(special, mounted_on, version, rw_flag); + if (n < 0) break; + n = put_mtab_entry(special, mounted_on, version, rw_flag); + if (n < 0) { + std_err("mount: /etc/mtab has grown too large\n"); + exit(1); + } + } + if (swap) { + vs = "swap"; + } else { + v = fsversion(argv[1], "mount"); + if (v == 1) + vs = "1"; + else if (v == 2) + vs = "2"; + else if (v == 3) + vs = "3"; + else + vs = "0"; + } + n = put_mtab_entry(argv[1], swap ? "swap" : argv[2], vs, (ro ? "ro" : "rw") ); + if (n < 0) { + std_err("mount: /etc/mtab has grown too large\n"); + exit(1); + } + + n = rewrite_mtab("mount"); + return(0); +} + + +void list() +{ + int n; + char special[PATH_MAX+1], mounted_on[PATH_MAX+1], version[10], rw_flag[10]; + + /* Read and print /etc/mtab. */ + n = load_mtab("mount"); + if (n < 0) exit(1); + + while (1) { + n = get_mtab_entry(special, mounted_on, version, rw_flag); + if (n < 0) break; + write(1, special, strlen(special)); + if (strcmp(version, "swap") == 0) { + tell(" is swapspace\n"); + } else { + tell(" is read-"); + tell(strcmp(rw_flag, "rw") == 0 ? "write" : "only"); + tell(" mounted on "); + tell(mounted_on); + tell("\n"); + } + } + exit(0); +} + + +void usage() +{ + std_err("Usage: mount [-r] special name\n mount -s special\n"); + exit(1); +} + + +void tell(this) +char *this; +{ + write(1, this, strlen(this)); +} + +void swapon(file) +char *file; +{ + u32_t super[2][MAX_BLOCK_SIZE / 2 / sizeof(u32_t)]; + swap_hdr_t *sp; + struct mmswapon mmswapon; + int fd, r; + char *err; + + if ((fd = open(file, O_RDWR)) < 0 + || lseek(fd, SUPER_BLOCK_BYTES, SEEK_SET) == -1 + || (r = read(fd, super, STATIC_BLOCK_SIZE)) < 0 + ) { + err = strerror(errno); + std_err("mount: "); + std_err(file); + std_err(": "); + std_err(err); + std_err("\n"); + exit(1); + } + sp = (swap_hdr_t *) &super[0]; + if (memcmp(sp->sh_magic, MAGIC, sizeof(MAGIC)) != 0) + sp = (swap_hdr_t *) &super[1]; + if (r == STATIC_BLOCK_SIZE && memcmp(sp->sh_magic, MAGIC, sizeof(MAGIC)) != 0 + || sp->sh_version > SH_VERSION) { + std_err("mount: "); + std_err(file); + std_err(" is not swapspace\n"); + exit(1); + } + close(fd); + mmswapon.offset = sp->sh_offset; + mmswapon.size = sp->sh_swapsize; + strncpy(mmswapon.file, file, sizeof(mmswapon.file)); + mmswapon.file[sizeof(mmswapon.file)-1] = 0; + if (svrctl(MMSWAPON, &mmswapon) < 0) { + err = strerror(errno); + std_err("mount: "); + std_err(file); + std_err(": "); + std_err(err); + std_err("\n"); + exit(1); + } +} diff --git a/commands/simple/mt.c b/commands/simple/mt.c new file mode 100755 index 000000000..9ed73b73e --- /dev/null +++ b/commands/simple/mt.c @@ -0,0 +1,216 @@ +/* mt 1.3 - magnetic tape control Author: Kees J. Bot + * 4 Apr 1993 + */ +#define nil NULL +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE 1 +#endif +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/mtio.h> + +/* Device status. */ +#define DS_OK 0 +#define DS_ERR 1 +#define DS_EOF 2 + +/* SCSI Sense key bits. */ +#define SENSE_KEY 0x0F /* The key part. */ +#define SENSE_ILI 0x20 /* Illegal block size. */ +#define SENSE_EOM 0x40 /* End-of-media. */ +#define SENSE_EOF 0x80 /* Filemark reached. */ + +/* Supported operations: */ + +typedef struct tape_operation { + int op; /* Opcode for MTIOCTOP ioctl (if any). */ + char *cmd; /* Command name. */ + int lim; /* Limits on count. */ +} tape_operation_t; + +#define SELF -1 /* Not a simple command, have to interpret. */ +#define IGN -1 /* Ignore count field (or accept anything.) */ +#define NNG 0 /* Nonnegative count field. */ +#define POS 1 /* Positive count field. */ + +tape_operation_t tapeops[] = { + { MTWEOF, "eof", POS }, /* Write EOF mark */ + { MTWEOF, "weof", POS }, /* Same */ + { MTFSF, "fsf", POS }, /* Forward Space File */ + { MTFSR, "fsr", POS }, /* Forward Space Record */ + { MTBSF, "bsf", NNG }, /* Backward Space File */ + { MTBSR, "bsr", POS }, /* Backward Space Record */ + { MTEOM, "eom", IGN }, /* To End-Of-Media */ + { MTREW, "rewind", IGN }, /* Rewind */ + { MTOFFL, "offline", IGN }, /* Rewind and take offline */ + { MTOFFL, "rewoffl", IGN }, /* Same */ + { SELF, "status", IGN }, /* Tape Status */ + { MTRETEN, "retension",IGN }, /* Retension the tape */ + { MTERASE, "erase", IGN }, /* Erase the tape */ + { MTMODE, "density", NNG }, /* Select density */ + { MTBLKZ, "blksize", NNG }, /* Select block size */ + { MTBLKZ, "blocksize",NNG }, /* Same */ +}; + +#define arraysize(a) (sizeof(a)/sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +/* From aha_scsi.c: */ +char *dev_state[] = { + "OK", "ERR", "EOF" +}; + +char *scsi_sense[] = { + "NO SENSE INFO", "RECOVERED ERROR", "NOT READY", "MEDIUM ERROR", + "HARDWARE ERROR", "ILLEGAL REQUEST", "UNIT ATTENTION", "DATA PROTECT", + "BLANK CHECK", "VENDOR UNIQUE ERROR", "COPY ABORTED", "ABORTED COMMAND", + "EQUAL", "VOLUME OVERFLOW", "MISCOMPARE", "SENSE RESERVED" +}; + +void usage(void) +{ + fprintf(stderr, "Usage: mt [-f device] command [count]\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + char *tape; + char *cmd; + int count= 1; + int fd, r; + tape_operation_t *op, *found; + struct mtop mtop; + struct mtget mtget; + + tape= getenv("TAPE"); + + /* -f tape? */ + if (argc > 1 && argv[1][0] == '-' && argv[1][1] == 'f') { + tape= argv[1] + 2; + + if (*tape == 0) { + if (--argc < 2) usage(); + argv++; + tape= argv[1]; + } + argc--; + argv++; + } + + if (argc != 2 && argc != 3) usage(); + + if (argc == 3) { + /* Check and convert the 'count' argument. */ + char *end; + + errno= 0; + count= strtol(argv[2], &end, 0); + if (*end != 0) usage(); + if (errno == ERANGE || (mtop.mt_count= count) != count) { + fprintf(stderr, "mt: %s: count too large, overflow\n", + argv[2]); + exit(1); + } + } + + if (tape == nil) { + fprintf(stderr, + "mt: tape device not specified by -f or $TAPE\n"); + exit(1); + } + + cmd= argv[1]; + if (strcmp(cmd, "rew") == 0) cmd= "rewind"; /* aha! */ + found= nil; + + /* Search for an operation that is unambiguously named. */ + for (op= tapeops; op < arraylimit(tapeops); op++) { + if (strncmp(op->cmd, cmd, strlen(cmd)) == 0) { + if (found != nil) { + fprintf(stderr, "mt: %s: ambiguous\n", cmd); + exit(1); + } + found= op; + } + } + + if ((op= found) == nil) { + fprintf(stderr, "mt: unknown command '%s'\n", cmd); + exit(1); + } + + /* Check count. */ + switch (op->lim) { + case NNG: + if (count < 0) { + fprintf(stderr, "mt %s: count may not be negative\n", + op->cmd); + exit(1); + } + break; + case POS: + if (count <= 0) { + fprintf(stderr, + "mt %s: count must be greater than zero\n", + op->cmd); + exit(1); + } + break; + } + + if (strcmp(tape, "-") == 0) { + fd= 0; + } else + if ((fd= open(tape, O_RDONLY)) < 0) { + fprintf(stderr, "mt: %s: %s\n", tape, strerror(errno)); + exit(1); + } + + if (op->op != SELF) { + /* A simple tape operation. */ + + mtop.mt_op= op->op; + mtop.mt_count= count; + r= ioctl(fd, MTIOCTOP, &mtop); + } else + if (strcmp(op->cmd, "status") == 0) { + /* Get status information. */ + + if ((r= ioctl(fd, MTIOCGET, &mtget)) == 0) { + printf("\ +SCSI tape drive %s:\n\ + drive status = 0x%02x (%s), sense key = 0x%02x (%s%s%s%s)\n\ + file no = %ld, block no = %ld, residual = %ld, block size = ", + tape, mtget.mt_dsreg, + mtget.mt_dsreg > 2 ? "?" : + dev_state[mtget.mt_dsreg], + mtget.mt_erreg, + mtget.mt_erreg & SENSE_EOF ? "EOF + " : "", + mtget.mt_erreg & SENSE_EOM ? "EOM + " : "", + mtget.mt_erreg & SENSE_ILI ? "ILI + " : "", + scsi_sense[mtget.mt_erreg & SENSE_KEY], + (long) mtget.mt_fileno, + (long) mtget.mt_blkno, + (long) mtget.mt_resid); + printf(mtget.mt_blksize == 0 ? "variable\n" : "%ld\n", + mtget.mt_blksize); + } + } + if (r < 0) { + if (errno == ENOTTY) { + fprintf(stderr, "mt: %s: command '%s' not supported\n", + tape, op->cmd); + exit(2); + } + fprintf(stderr, "mt: %s: %s\n", tape, strerror(errno)); + exit(1); + } + exit(0); +} diff --git a/commands/simple/nm.c b/commands/simple/nm.c new file mode 100755 index 000000000..52f37d4b9 --- /dev/null +++ b/commands/simple/nm.c @@ -0,0 +1,226 @@ +/* nm - print name list. Author: Dick van Veen */ + +/* Dick van Veen: veench@cs.vu.nl */ + +#include <sys/types.h> +#include <a.out.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +/* Read the name list in memory, sort it, and print it. */ + +/* Nm [-gnopru] [file] ... + * + * flags: + * -d address in decimal + * -g print only external symbols. + * -n sort numerically rather than alphabetically. + * -o prepend file name to each line rather than only once. + * -p don't sort, pint n symbol-table order. + * -r sort in reverse order. + * -u print only undefined symbols. + * + * - when no file name is present, a.out is assumed. + * + * NOTE: no archives are supported because assembly files don't + * have symbol tables. + * + */ + +#define A_OUT "a.out" + +int d_flag; +int g_flag; +int n_flag; +int o_flag; +int p_flag; +int r_flag; +int u_flag; + +char io_buf[BUFSIZ]; /* io buffer */ +struct exec header; /* header of a.out file */ +int stbl_elems; /* #elements in symbol table */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(int nm_sort, (const void *tmp_stbl1, const void *tmp_stbl2)); +_PROTOTYPE(void nm, (char *file)); +_PROTOTYPE(int read_header, (int fd)); +_PROTOTYPE(void nm_print, (char *file, struct nlist *stbl)); + +int main(argc, argv) +int argc; +char **argv; +{ + argv++; + while (*argv != 0 && **argv == '-') { + *argv += 1; + while (**argv != '\0') { + switch (**argv) { + case 'd': d_flag = 1; break; + case 'g': g_flag = 1; break; + case 'n': n_flag = 1; break; + case 'o': o_flag = 1; break; + case 'p': p_flag = 1; break; + case 'r': r_flag = 1; break; + case 'u': u_flag = 1; break; + default: + fprintf(stderr, "illegal flag: -%c\n", **argv); + exit(-1); + } + *argv += 1; + } + argv++; + } + setbuf(stdin, io_buf); + if (*argv == 0) + nm(A_OUT); + else + while (*argv != 0) { + nm(*argv); + argv++; + } + return(0); +} + +int nm_sort(tmp_stbl1, tmp_stbl2) +_CONST void *tmp_stbl1, *tmp_stbl2; +{ + + struct nlist *stbl1 = (struct nlist *)tmp_stbl1; + struct nlist *stbl2 = (struct nlist *)tmp_stbl2; + int cmp; + + if (n_flag) { /* sort numerically */ + if ((stbl1->n_sclass & N_SECT) < + (stbl2->n_sclass & N_SECT)) + cmp = -1; + else if ((stbl1->n_sclass & N_SECT) > + (stbl2->n_sclass & N_SECT)) + cmp = 1; + else if (stbl1->n_value < stbl2->n_value) + cmp = -1; + else if (stbl1->n_value > stbl2->n_value) + cmp = 1; + else + cmp = strncmp(stbl1->n_name, stbl2->n_name, (size_t)8); + } else { + cmp = strncmp(stbl1->n_name, stbl2->n_name, (size_t)8); + if (cmp == 0) { + if (stbl1->n_value < stbl2->n_value) + cmp = -1; + else if (stbl1->n_value > stbl2->n_value) + cmp = 1; + } + } + + if (r_flag) cmp = -cmp; /* reverse sort */ + return(cmp); +} + +void nm(file) +char *file; +{ + struct nlist *stbl; + int fd; + + fd = open(file, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "can't open %s\n", file); + return; + } + if (read_header(fd)) { + fprintf(stderr, "%s: no executable file\n", file); + close(fd); + return; + } + if (header.a_syms == 0) { + close(fd); + return; + } + if ((size_t) header.a_syms != header.a_syms) { + fprintf(stderr, "%s: symbol table too large to allocate\n", file); + close(fd); + return; + } + if ((int) header.a_syms != header.a_syms) { + /* This is necessary because we are too lazy to iterate the read. */ + fprintf(stderr, "%s: symbol table too large to read\n", file); + close(fd); + return; + } + stbl = (struct nlist *) malloc((size_t) header.a_syms); + if (stbl == NULL) { + fprintf(stderr, "%s: can't allocate symbol table\n", file); + close(fd); + return; + } + if (read(fd, (char *) stbl, (unsigned) header.a_syms) != header.a_syms) { + fprintf(stderr, "%s: can't read symbol table\n", file); + free(stbl); + close(fd); + return; + } + stbl_elems = (int) header.a_syms / sizeof(struct nlist); + if (!p_flag) qsort(stbl, (size_t)stbl_elems, sizeof(struct nlist), nm_sort); + nm_print(file, stbl); + free(stbl); + close(fd); +} + +int read_header(fd) +int fd; +{ + if (read(fd, (char *) &header, sizeof(struct exec)) != sizeof(struct exec)) + return(1); + if (BADMAG(header)) return(1); + lseek(fd, A_SYMPOS(header), SEEK_SET); + + return(0); +} + +void nm_print(file, stbl) +char *file; +register struct nlist *stbl; +{ + struct nlist *last; + char name[9]; + int n_sclass; + char type; + + name[8] = '\0'; + if (!o_flag) printf("%s:\n", file); + for (last = &stbl[stbl_elems]; stbl != last; stbl++) { + if (g_flag && (stbl->n_sclass & N_CLASS) != C_EXT) continue; + if (u_flag && (stbl->n_sclass & N_SECT) != N_UNDF) continue; + + n_sclass = stbl->n_sclass & N_SECT; + if (n_sclass == N_ABS) + type = 'a'; + else if (n_sclass == N_TEXT) + type = 't'; + else if (n_sclass == N_DATA) + type = 'd'; + else if (n_sclass == N_BSS) + type = 'b'; + else + type = 'u'; + if ((stbl->n_sclass & N_CLASS) == C_EXT) type += 'A' - 'a'; + strncpy(name, stbl->n_name, (size_t)8); + if (d_flag) { + /* Offsets in decimal. */ + if (o_flag) + printf("%s:%08ld %c %s\n",file,stbl->n_value,type,name); + else + printf("%08ld %c %s\n", stbl->n_value, type, name); + } else { + /* Offsets in hex. */ + if (o_flag) + printf("%s:%08lx %c %s\n",file,stbl->n_value,type,name); + else + printf("%08lx %c %s\n", stbl->n_value, type, name); + } + } +} diff --git a/commands/simple/nonamed.c b/commands/simple/nonamed.c new file mode 100755 index 000000000..c1b823aba --- /dev/null +++ b/commands/simple/nonamed.c @@ -0,0 +1,2153 @@ +/* nonamed - Not a name daemon, but plays one on TV. + * Author: Kees J. Bot + * 29 Nov 1994 + */ +static const char version[] = "2.7"; + +/* Use the file reading gethostent() family of functions. */ +#define sethostent _sethostent +#define gethostent _gethostent +#define endhostent _endhostent + +#define nil ((void*)0) +#include <sys/types.h> +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <limits.h> +#include <signal.h> +#include <assert.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/asynchio.h> +#include <net/hton.h> +#include <net/netlib.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/nameser.h> +#include <net/gen/resolv.h> +#include <net/gen/netdb.h> +#include <net/gen/socket.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> +#include <net/gen/udp.h> +#include <net/gen/udp_hdr.h> +#include <net/gen/udp_io.h> +#include <net/gen/dhcp.h> + +#define HTTL 3600L /* Default time to live for /etc/hosts data. */ +#define SHORT_TIMEOUT 2 /* If you expect an answer soon. */ +#define MEDIUM_TIMEOUT 4 /* Soon, but not that soon. */ +#define LONG_TIMEOUT 300 /* For stream connections to a real named. */ +#define N_IDS 256 /* Keep track of this many queries. */ +#define N_DATAMAX (4096*sizeof(char *)) /* Default response cache size. */ +#define N_NAMEDS 8 /* Max # name daemons we can keep track of. */ +#define NO_FD (-1) /* No name daemon channel here. */ +#define T_NXD ((u16_t) -1) /* A "type" signalling a nonexistent domain. */ + +/* Can't do async I/O under standard Minix, so forget about TCP. */ +#define DO_TCP (__minix_vmd || !__minix) + +/* Host data, file to store our process id in, our cache, DHCP's cache. */ +static char HOSTS[]= "/etc/hosts"; +static char PIDFILE[]= "/usr/run/nonamed.pid"; +static char NNCACHE[]= "/usr/adm/nonamed.cache"; +static char DHCPCACHE[]="/usr/adm/dhcp.cache"; + +/* Magic string to head the cache file. */ +static char MAGIC[4]= "NND\2"; + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) +#define between(a, c, z) ((unsigned) ((c) - (a)) <= (unsigned) ((z) - (a))) + +/* The start of time and the far future. */ +#define IMMEDIATE ((time_t) 0) +#define NEVER ((time_t) ((time_t) -1 < 0 ? LONG_MAX : ULONG_MAX)) + +static unsigned debug; /* Debug level. */ +static time_t now; /* Current time. */ +static u32_t stale; /* Extension time for stale data. */ +static u32_t httl; /* TTL for /etc/hosts data. */ +static int reinit, done; /* Reinit config / program is done. */ +static int single; /* Run single on a nondefault interface. */ + +static void report(const char *label) +{ + fprintf(stderr, "nonamed: %s: %s\n", label, strerror(errno)); +} + +static void fatal(const char *label) +{ + report(label); + if (debug >= 3) { fflush(nil); abort(); } + exit(1); +} + +static void *allocate(void *mem, size_t size) +{ + if ((mem= realloc(mem, size)) == nil) fatal("malloc()"); + return mem; +} + +static void deallocate(void *mem) +{ + free(mem); +} + +static char *timegmt(time_t t) +/* Simple "time in seconds to GMT time today" converter. */ +{ + unsigned h, m, s; + static char asctime[sizeof("00:00:00")]; + + s= t % 60; + t /= 60; + m= t % 60; + t /= 60; + h= t % 24; + sprintf(asctime, "%02u:%02u:%02u", h, m, s); + return asctime; +} + +static char *nowgmt(void) +{ + return timegmt(now); +} + +#define PC(n) ((void) sizeof(char [sizeof(*(n)) == 1]), (char *) (n)) +#define namecpy(n1, n2) strcpy(PC(n1), PC(n2)) +#define namecat(n1, n2) strcat(PC(n1), PC(n2)) +#define namechr(n, c) ((u8_t *) strchr(PC(n), (c))) +#define namecmp(n1, n2) strcasecmp(PC(n1), PC(n2)) +#define namencmp(n1, n2, len) strncasecmp(PC(n1), PC(n2), len) + +typedef struct dns { /* A DNS packet. */ + dns_hdr_t hdr; /* DNS header. */ + u8_t data[PACKETSZ - sizeof(dns_hdr_t)]; /* DNS data. */ +} dns_t; + +/* Addres of DNS packet to octet address, or vv. */ +#define dns2oct(dp) ((u8_t *) (dp)) +#define oct2dns(dp) ((dns_t *) (dp)) + +typedef struct query { /* One cached answer to a query. */ + struct query *less; /* Less recently used. */ + struct query *more; /* More recently used. */ + time_t age; /* Time it was added. */ + time_t stale; /* Time it goes stale by TTL. */ + u16_t usage; /* Counts of queries answered. */ + u8_t flags; /* QF_REFRESH. */ + size_t size; /* Size of DNS packet. */ + dns_t dns; /* Answer to query as a DNS packet. */ +} query_t; + +#define QF_REFRESH 0x01 /* This stale data must be refreshed. */ +#define QU_SHIFT 1 /* To shift usage by when evicting. */ + +/* Size of new query_t or existing query_t. */ +#define query_allocsize(dnssize) (offsetof(query_t, dns) + (dnssize)) +#define query_size(qp) query_allocsize((qp)->size) + +static query_t *mru, *lru; /* Most and least recently used answers. */ +static int q_refresh; /* Set when an entry needs refreshing. */ + +static void pack16(u8_t *buf, u16_t s) +/* Pack a 16 bit value into a byte array. */ +{ + buf[0]= ((u8_t *) &s)[0]; + buf[1]= ((u8_t *) &s)[1]; +} + +static void pack32(u8_t *buf, u32_t l) +/* Pack a 32 bit value into a byte array. */ +{ + buf[0]= ((u8_t *) &l)[0]; + buf[1]= ((u8_t *) &l)[1]; + buf[2]= ((u8_t *) &l)[2]; + buf[3]= ((u8_t *) &l)[3]; +} + +static u16_t upack16(u8_t *buf) +/* Unpack a 16 bit value from a byte array. */ +{ + u16_t s; + + ((u8_t *) &s)[0]= buf[0]; + ((u8_t *) &s)[1]= buf[1]; + return s; +} + +static u32_t upack32(u8_t *buf) +/* Unpack a 32 bit value from a byte array. */ +{ + u32_t l; + + ((u8_t *) &l)[0]= buf[0]; + ((u8_t *) &l)[1]= buf[1]; + ((u8_t *) &l)[2]= buf[2]; + ((u8_t *) &l)[3]= buf[3]; + return l; +} + +/* Encoding of RRs: i(paddr), d(omain), l(ong), c(har), s(tring), (s)h(ort). */ +static char *encoding[] = { + "c*", /* anything unknown is c* */ + "i", /* A */ + "d", /* NS */ + "d", /* MD */ + "d", /* MF */ + "d", /* CNAME */ + "ddlllll", /* SOA */ + "d", /* MB */ + "d", /* MG */ + "d", /* MR */ + "c*", /* NULL */ + "icc*", /* WKS */ + "d", /* PTR */ + "ss", /* HINFO */ + "dd", /* MINFO */ + "hd", /* MX */ + "s*", /* TXT */ +}; + +static char *itoa(char *fmt, u32_t i) +{ + static char output[32 + 3 * sizeof(i)]; + + sprintf(output, fmt, (unsigned long) i); + return output; +} + +static char *classname(unsigned class) +/* Class name of a resource record, for debug purposes. */ +{ + static char *classes[] = { "IN", "CS", "CHAOS", "HS" }; + + if ((class - C_IN) < arraysize(classes)) return classes[class - C_IN]; + return itoa("C_%u", class); +} + +static char *typename(unsigned type) +/* Type name of a resource record, for debug purposes. */ +{ + static char type_A[][6] = { + "A", "NS", "MD", "MF", "CNAME", "SOA", "MB", "MG", "MR", "NULL", + "WKS", "PTR", "HINFO", "MINFO", "MX", "TXT", + }; + static char type_AXFR[][6] = { + "AXFR", "MAILB", "MAILA", "ANY", + }; + if ((type - T_A) < arraysize(type_A)) return type_A[type - T_A]; + if ((type - T_AXFR) < arraysize(type_AXFR)) return type_AXFR[type - T_AXFR]; + return itoa("T_%u", type); +} + +static int print_qrr(dns_t *dp, size_t size, u8_t *cp0, int q) +/* Print a query (q) or resource record (!q) from 'cp0' in a DNS packet for + * debug purposes. Return number of bytes skipped or -1 on error. + */ +{ + u8_t name[MAXDNAME+1]; + u8_t *cp; + char *ep; + u8_t *dlim, *rlim; + u16_t type, class, rdlength; + u32_t ttl; + int r; + + cp= cp0; + dlim= dns2oct(dp) + size; + r= dn_expand(dns2oct(dp), dlim, cp, name, MAXDNAME); + if (r == -1) return -1; + cp += r; + if (cp + 2 * sizeof(u16_t) > dlim) return -1; + type= ntohs(upack16(cp)); + cp += sizeof(u16_t); + class= ntohs(upack16(cp)); + cp += sizeof(u16_t); + printf("%-25s", (char *) name); + if (q) { + /* We're just printing a query segment, stop right here. */ + printf(" %8s", classname(class)); + printf(" %-5s", typename(type)); + return cp - cp0; + } + if (cp + sizeof(u32_t) + sizeof(u16_t) > dlim) return -1; + ttl= ntohl(upack32(cp)); + cp += sizeof(u32_t); + rdlength= ntohs(upack16(cp)); + cp += sizeof(u16_t); + if (cp + rdlength > dlim) return -1; + rlim = cp + rdlength; + printf(" %5lu", (unsigned long) ttl); + printf(" %s", classname(class)); + printf(" %-5s", typename(type)); + ep= type < arraysize(encoding) ? encoding[type] : encoding[0]; + while (*ep != 0) { + switch (*ep++) { + case 'i': + if (cp + sizeof(u32_t) > rlim) return -1; + printf(" %s", inet_ntoa(upack32(cp))); + cp += sizeof(u32_t); + break; + case 'l': + if (cp + sizeof(u32_t) > rlim) return -1; + printf(" %ld", (long)(i32_t) ntohl(upack32(cp))); + cp += sizeof(u32_t); + break; + case 'd': + r= dn_expand(dns2oct(dp), dlim, cp, name, MAXDNAME); + if (r == -1) return -1; + printf(" %s", (char *) name); + cp += r; + break; + case 'c': + if (cp >= rlim) return -1; + printf(" %02X", *cp++); + break; + case 's': + r= *cp + 1; + if (cp + r > rlim) return -1; + printf(" \"%.*s\"", *cp, (char *) (cp + 1)); + cp += r; + break; + case 'h': + if (cp + sizeof(u16_t) > rlim) return -1; + printf(" %u", ntohs(upack16(cp))); + cp += sizeof(u16_t); + break; + } + if (*ep == '*') ep= cp < rlim ? ep-1 : ep+1; + } + return cp - cp0; +} + +static void dns_tell(int indent, dns_t *dp, size_t size) +/* Explain a DNS packet, for debug purposes. */ +{ + u8_t *cp; + int r, i; + unsigned count[4]; + static char label[4][4]= { "QD:", "AN:", "NS:", "AR:" }; + static char rcodes[][9] = { + "NOERROR", "FORMERR", "SERVFAIL", "NXDOMAIN", "NOTIMP", "REFUSED" + }; + + if (size < sizeof(dns_hdr_t)) return; + + printf("%*s", indent, ""); + printf("DNS %s:", (dp->hdr.dh_flag1 & DHF_QR) ? "reply" : "query"); + r= dp->hdr.dh_flag2 & DHF_RCODE; + printf(" %s", r < arraysize(rcodes) ? rcodes[r] : itoa("ERR_%lu", r)); + if (dp->hdr.dh_flag1 & DHF_AA) printf(" AA"); + if (dp->hdr.dh_flag1 & DHF_TC) printf(" TC"); + if (dp->hdr.dh_flag1 & DHF_RD) printf(" RD"); + if (dp->hdr.dh_flag2 & DHF_RA) printf(" RA"); +#ifdef DHF_AD + if (dp->hdr.dh_flag2 & DHF_AD) printf(" AD"); + if (dp->hdr.dh_flag2 & DHF_CD) printf(" CD"); +#endif + fputc('\n', stdout); + + count[0]= ntohs(dp->hdr.dh_qdcount); + count[1]= ntohs(dp->hdr.dh_ancount); + count[2]= ntohs(dp->hdr.dh_nscount); + count[3]= ntohs(dp->hdr.dh_arcount); + cp = dp->data; + for (i= 0; i < 4; i++) { + while (count[i] > 0) { + printf("%*s", indent, ""); + printf(" %s ", label[i]); + r= print_qrr(dp, size, cp, (i == 0)); + fputc('\n', stdout); + if (r == -1) return; + cp += r; + count[i]--; + } + } +} + +static u32_t dns_ttl(dns_t *dp, size_t size, u32_t delta) +/* Compute the minimum TTL of all RRs in a DNS packet and subtract delta from + * all TTLs. (We are actually only interested in the minimum (delta = 0) or + * the subtraction (delta > 0). It was easier to roll this into one routine.) + */ +{ + u8_t *cp, *rdp, *dlim; + int r, i, hasttl, hassoa; + unsigned type, count[4]; + u32_t ttl, minimum, minttl; + unsigned rcode; + u8_t name[MAXDNAME+1]; + + hasttl= hassoa= 0; + minttl= 365*24*3600L; + dlim= dns2oct(dp) + size; + if (size < sizeof(dns_hdr_t)) return 0; + + rcode= dp->hdr.dh_flag2 & DHF_RCODE; + count[0]= ntohs(dp->hdr.dh_qdcount); + count[1]= ntohs(dp->hdr.dh_ancount); + count[2]= ntohs(dp->hdr.dh_nscount); + count[3]= ntohs(dp->hdr.dh_arcount); + cp = dp->data; + for (i= 0; i < 4 && cp < dlim; i++) { + while (count[i] > 0) { + r= dn_expand(dns2oct(dp), dlim, cp, name, MAXDNAME); + if (r == -1) break; + cp += r + 2 * sizeof(u16_t); + if (i != 0) { + if (cp + sizeof(u32_t) + sizeof(u16_t) > dlim) break; + type= upack16(cp - 2 * sizeof(u16_t)); + ttl= ntohl(upack32(cp)); + ttl= ttl < delta ? 0 : ttl - delta; + if (rcode == NXDOMAIN && i == 2 && type == HTONS(T_SOA)) { + rdp= cp + sizeof(u32_t) + sizeof(u16_t); + r= dn_expand(dns2oct(dp), dlim, rdp, name, MAXDNAME); + if (r == -1) break; + rdp += r; + r= dn_expand(dns2oct(dp), dlim, rdp, name, MAXDNAME); + if (r == -1) break; + rdp += r + 4 * sizeof(u32_t); + if (rdp + sizeof(u32_t) > dlim) break; + minimum= ntohl(upack32(rdp)); + if (ttl > minimum) ttl= minimum; + hassoa= 1; + } + if (delta != 0) pack32(cp, htonl(ttl)); + if (ttl < minttl) minttl= ttl; + hasttl= 1; + cp += sizeof(u32_t); + cp += sizeof(u16_t) + ntohs(upack16(cp)); + } + count[i]--; + } + } + return ((rcode == NOERROR && hasttl) || (rcode == NXDOMAIN && hassoa)) + ? minttl : 0; +} + +/* Total cached query data. */ +static size_t n_datamax= N_DATAMAX; +static size_t n_data; + +static query_t *extract_query(query_t *qp) +/* Take a query out of the query cache. */ +{ + assert(qp != nil); + *(qp->less != nil ? &qp->less->more : &lru) = qp->more; + *(qp->more != nil ? &qp->more->less : &mru) = qp->less; + n_data -= query_size(qp); + return qp; +} + +static query_t *get_query(u8_t *name, unsigned type) +/* Find a query and if so remove it from the cache and return it. */ +{ + query_t *qp, *less; + u8_t qname[MAXDNAME+1]; + int r; + + for (qp= mru; qp != nil; qp= less) { + less= qp->less; + if (qp->stale <= now - stale) { + /* This answer has expired. */ + deallocate(extract_query(qp)); + } else { + r= dn_expand(dns2oct(&qp->dns), dns2oct(&qp->dns) + qp->size, + qp->dns.data, qname, MAXDNAME); + if (r == -1) continue; + if (namecmp(qname, name) == 0 && upack16(qp->dns.data+r) == type) { + /* Found an answer to the query. */ + return extract_query(qp); + } + } + } + return nil; +} + +static void insert_query(query_t *qp) +/* (Re)insert a query into the cache. */ +{ + *(qp->less != nil ? &qp->less->more : &lru) = qp; + *(qp->more != nil ? &qp->more->less : &mru) = qp; + n_data += query_size(qp); + + /* Try to delete the LRU while there is too much memory in use. If + * its usage count is too high then it gets a second chance. + */ + while (n_data > n_datamax && lru != nil) { + if ((lru->usage >>= QU_SHIFT) == 0 || lru->stale <= now - stale) { + deallocate(extract_query(lru)); + } else { + lru->less= mru; /* Make list circular. */ + mru->more= lru; + mru= lru; /* Move one over, making LRU the MRU. */ + lru= lru->more; + lru->less= nil; /* Break the circle. */ + mru->more= nil; + } + } + + if (debug >= 2) { + unsigned n= 0; + for (qp= mru; qp != nil; qp= qp->less) n++; + printf("%u cached repl%s, %u bytes, sbrk(0) = %u\n", + n, n == 1 ? "y" : "ies", + (unsigned) n_data, + (unsigned) sbrk(0)); + } +} + +static void put_query(query_t *qp) +/* Add a new query to the cache as the MRU. */ +{ + qp->less= mru; + qp->more= nil; + insert_query(qp); +} + +static void cache2file(void) +/* Store the cached data into the cache file. */ +{ + FILE *fp; + query_t *qp; + u8_t data[4+1+2+2]; + u16_t usage; + char newcache[sizeof(NNCACHE) + sizeof(".new")]; + + if (single) return; + + strcpy(newcache, NNCACHE); + strcat(newcache, ".new"); + + if ((fp= fopen(newcache, "w")) == nil) { + if ((errno != ENOENT && errno != EROFS) || debug >= 2) report(newcache); + return; + } + if (debug >= 2) printf("Writing %s:\n", newcache); + + /* Magic number: */ + fwrite(MAGIC, 1, sizeof(MAGIC), fp); + + for (qp= lru; qp != nil; qp= qp->more) { + if (qp->stale <= now - stale) continue; + if (debug >= 2) { + printf("Usage = %u, Age = %ld, Flags = %02X:\n", + qp->usage, (long) (now - qp->age), qp->flags); + dns_tell(2, &qp->dns, qp->size); + } + pack32(data+0, htonl(qp->age)); + data[4]= qp->flags; + pack16(data+5, htons(qp->size)); + pack16(data+7, htons(qp->usage)); + fwrite(data, 1, sizeof(data), fp); + fwrite(&qp->dns, 1, qp->size, fp); + if (ferror(fp)) break; + } + + if (ferror(fp) || fclose(fp) == EOF) { + report(newcache); + (void) unlink(newcache); + return; + } + + if (debug >= 2) printf("mv %s %s\n", newcache, NNCACHE); + if (rename(newcache, NNCACHE) < 0) { + fprintf(stderr, "nonamed: mv %s %s: %s\n", + newcache, NNCACHE, strerror(errno)); + (void) unlink(newcache); + } +} + +static void file2cache(void) +/* Read cached data from the cache file. */ +{ + query_t *qp; + FILE *fp; + u8_t data[4+1+2+2]; + size_t dlen; + + if (single) return; + + if ((fp= fopen(NNCACHE, "r")) == nil) { + if (errno != ENOENT || debug >= 2) report(NNCACHE); + return; + } + if (debug >= 2) printf("Reading %s:\n", NNCACHE); + + /* Magic number? */ + fread(data, 1, sizeof(MAGIC), fp); + if (ferror(fp) || memcmp(MAGIC, data, sizeof(MAGIC)) != 0) goto err; + + for (;;) { + fread(data, 1, sizeof(data), fp); + if (feof(fp) || ferror(fp)) break; + dlen= ntohs(upack16(data+5)); + qp= allocate(nil, query_allocsize(dlen)); + qp->age= htonl(upack32(data+0)); + qp->flags= data[4]; + if (qp->flags & QF_REFRESH) q_refresh= 1; + qp->size= dlen; + qp->usage= htons(upack16(data+7)); + fread(&qp->dns, 1, qp->size, fp); + if (feof(fp) || ferror(fp)) { + deallocate(qp); + goto err; + } + qp->stale= qp->age + dns_ttl(&qp->dns, dlen, 0); + if (debug >= 2) { + printf("Usage = %u, Age = %ld, Flags = %02X:\n", + qp->usage, (long) (now - qp->age), qp->flags); + dns_tell(2, &qp->dns, dlen); + } + put_query(qp); + } + if (ferror(fp)) { + err: + /* The cache file did not end at EOF or is otherwise a mess. */ + fprintf(stderr, "nonamed: %s: %s\n", NNCACHE, + ferror(fp) ? strerror(errno) : "Corrupt"); + while (lru != nil) deallocate(extract_query(lru)); + } + fclose(fp); +} + +typedef int handler_t(void *data, int expired); + +/* All actions are in the form of "jobs". */ +typedef struct job { + struct job *next, **prev; /* To make a job queue. */ + handler_t *handler; /* Function to handle this job. */ + time_t timeout; /* Moment it times out. */ + void *data; /* Data associated with the job. */ +} job_t; + +static job_t *queue; /* Main job queue. */ + +static void newjob(handler_t *handler, time_t timeout, void *data) +/* Create a new job with the given handler, timeout time and data. */ +{ + job_t *job, **prev; + + job= allocate(nil, sizeof(*job)); + job->handler= handler; + job->timeout= timeout; + job->data= data; + + for (prev= &queue; *prev != nil; prev= &(*prev)->next) { + if (job->timeout < (*prev)->timeout) break; + } + job->next= *prev; + job->prev= prev; + *prev= job; + if (job->next != nil) job->next->prev= &job->next; +} + +static int execjob(job_t *job, int expired) +/* Execute a job by calling the handler. Remove the job if it returns true, + * indicating that it is done. Expired is set if the job timed out. It is + * otherwise called to check for I/O. + */ +{ + if ((*job->handler)(job->data, expired)) { + *job->prev= job->next; + if (job->next != nil) job->next->prev= job->prev; + deallocate(job); + return 1; + } + return 0; +} + +static void force_expire(handler_t *handler) +/* Force jobs to expire immediately, the named searcher for instance. */ +{ + job_t *job, **prev= &queue; + + while ((job= *prev) != nil) { + if (job->handler == handler && job->timeout != IMMEDIATE) { + *prev= job->next; + if (job->next != nil) job->next->prev= prev; + newjob(job->handler, IMMEDIATE, job->data); + deallocate(job); + } else { + prev= &job->next; + } + } +} + +static int nxdomain(u8_t *name) +/* True iff the two top level components in a name are repeated in the name, + * or if in-addr.arpa is found within a name. Such things happen often in a + * search for an already fully qualified local name. For instance: + * flotsam.cs.vu.nl.cs.vu.nl. (We don't want this at boot time.) + */ +{ + u8_t *end, *top, *p; + size_t n; + + end= namechr(name, 0); + top= end; + while (top > name && *--top != '.') {} + while (top > name && *--top != '.') {} + n= end - top; + p= top; + for (;;) { + if (p == name) return 0; + if (*--p == '.') { + if (namencmp(p, top, n) == 0 && p[n] == '.') return 1; + if (namencmp(p, ".in-addr.arpa.", 14) == 0) return 1; + } + } +} + +typedef struct id2id { + u16_t id; /* ID of old query. */ + u16_t port; /* Reply port. */ + ipaddr_t ip; /* Reply address. */ +} id2id_t; + +static id2id_t id2id[N_IDS]; +static u16_t id_counter; + +static u16_t new_id(u16_t in_id, u16_t in_port, ipaddr_t in_ip) +/* An incoming UDP query must be relabeled with a new ID before it can be + * send on to a real name daemon. + */ +{ + id2id_t *idp; + u16_t id; + + id= id_counter++; + idp= &id2id[id % N_IDS]; + idp->id= in_id; + idp->port= in_port; + idp->ip= in_ip; + return htons(id); +} + +static int old_id(u16_t id, u16_t *out_id, u16_t *out_port, ipaddr_t *out_ip) +/* Translate a reply id back to the id, port, and address used in the query. + * Return true if the translation is possible. + */ +{ + id= ntohs(id); + if ((u16_t) (id_counter - id) > N_IDS) { + /* Too old. */ + return 0; + } else { + /* We know this one. */ + id2id_t *idp= &id2id[id % N_IDS]; + + if (idp->port == 0) return 0; /* Named is trying to fool us? */ + *out_id= idp->id; + *out_port= idp->port; + *out_ip= idp->ip; + idp->port= 0; + return 1; + } +} + +/* IDs used to mark my own queries to name servers, must be new_id translated + * to make them unique "on the wire". + */ +#define ID_IPSELF HTONL(0) /* "I did it myself" address. */ +#define ID_PROBE HTONS(0) /* Name server probe. */ +#define ID_REFRESH HTONS(1) /* Query to refresh a cache entry. */ + +static char *tcp_device, *udp_device; /* TCP and UDP device names. */ +static int udp_fd; /* To send or receive UDP packets. */ +static asynchio_t asyn; /* For I/O in progress. */ +static ipaddr_t my_ip; /* My IP address. */ +static u16_t my_port, named_port; /* Port numbers, normally "domain". */ + +static ipaddr_t named[N_NAMEDS]; /* Addresses of all name servers. */ +static unsigned n_nameds; /* Number of configured name daemons. */ +static unsigned i_named; /* Index to current name server. */ +static int expect; /* Set when we expect an answer. */ +static int search_ct= -1; /* Named search count and state. */ +static int dirty; /* True when new entry put in cache. */ + +#define current_named() (+named[i_named]) +#define searching() (search_ct > 0) +#define start_searching() ((void) (search_ct= -1)) +#define stop_searching() ((void) (search_ct= 0)) +#define expecting() (+expect) +#define start_expecting() ((void) (expect= 1)) +#define stop_expecting() ((void) (expect= 0)) + +static time_t filetime(const char *file) +/* Get the modified time of a file. */ +{ + struct stat st; + + return stat(file, &st) == 0 ? st.st_mtime : 0; +} + +static void init_config(ipaddr_t ifip) +/* Read name daemon list and other special stuff from the hosts file. */ +{ + struct hostent *he; + u32_t nip, hip; + static time_t hosts_time, dhcp_time; + time_t ht, dt; + + /* See if anything really changed. */ + if (((ifip ^ HTONL(0x7F000000)) & HTONL(0xFF000000)) == 0) ifip= my_ip; + ht= filetime(HOSTS); + dt= filetime(DHCPCACHE); + if (ifip == my_ip && ht == hosts_time && dt == dhcp_time) return; + my_ip= ifip; + hosts_time= ht; + dhcp_time= dt; + + if (debug >= 2) { + printf("%s: I am nonamed %s at %s:%u\n", + nowgmt(), version, inet_ntoa(my_ip), ntohs(my_port)); + } + + httl= HTONL(HTTL); + stale= 0; + n_nameds= 0; + + if (!single) { + sethostent(0); + while ((he= gethostent()) != nil) { + memcpy(&nip, he->h_addr, sizeof(u32_t)); + hip= ntohl(nip); + if (namecmp(he->h_name, "%ttl") == 0) httl= nip; + if (namecmp(he->h_name, "%stale") == 0) stale= hip; + if (namecmp(he->h_name, "%memory") == 0) n_datamax= hip; + if (namecmp(he->h_name, "%nameserver") == 0) { + if (nip != my_ip || named_port != my_port) { + if (n_nameds < N_NAMEDS) named[n_nameds++]= nip; + } + } + } + endhostent(); + } + + if (n_nameds == 0) { + /* No name daemons found in the host file. What about DHCP? */ + int fd; + dhcp_t d; + ssize_t r; + u8_t *data; + size_t len; + + if ((fd= open(DHCPCACHE, O_RDONLY)) < 0) { + if (errno != ENOENT) fatal(DHCPCACHE); + } else { + while ((r= read(fd, &d, sizeof(d))) == sizeof(d)) { + if (d.yiaddr == my_ip) break; + } + if (r < 0) fatal(DHCPCACHE); + close(fd); + + if (r == sizeof(d) && dhcp_gettag(&d, DHCP_TAG_DNS, &data, &len)) { + while (len >= sizeof(nip)) { + memcpy(&nip, data, sizeof(nip)); + data += sizeof(nip); + len -= sizeof(nip); + if (nip != my_ip || named_port != my_port) { + if (n_nameds < N_NAMEDS) named[n_nameds++]= nip; + } + } + } + } + } + i_named= 0; +} + +static handler_t job_save_cache, job_read_udp, job_find_named, job_expect_named; +#if DO_TCP +static handler_t job_setup_listen, job_listen, job_setup_connect, job_connect; +static handler_t job_read_query, job_write_query; +static handler_t job_read_reply, job_write_reply; +#endif + +static int query_hosts(u8_t *qname, unsigned type, dns_t *dp, size_t *pdlen) +/* Read the /etc/hosts file to try and answer an A or PTR query. Return + * true iff an answer can be found, with the answer copied to *dp. + */ +{ + struct hostent *he; + int i, r; + dns_t dns; + u8_t *domain; + u8_t *cp; + u8_t name[MAXDNAME+1]; + u8_t *dnvec[40]; + unsigned ancount; + struct hostent localhost; + static char *noaliases[]= { nil }; + static ipaddr_t localaddr= HTONL(0x7F000001L); + static char *localaddrlist[]= { (char *) &localaddr, nil }; + + if (single) return 0; + + /* Assume we can answer. */ + dns.hdr.dh_flag1= DHF_QR | DHF_AA; + dns.hdr.dh_flag2= DHF_RA; + dns.hdr.dh_qdcount= HTONS(1); + ancount= 0; + dns.hdr.dh_nscount= HTONS(0); + dns.hdr.dh_arcount= HTONS(0); + + dnvec[0]= dns2oct(&dns); + dnvec[1]= nil; + cp= dns.data; + r= dn_comp(qname, cp, arraysize(dns.data), dnvec, arraylimit(dnvec)); + if (r == -1) return 0; + cp += r; + pack16(cp, type); + cp += sizeof(u16_t); + pack16(cp, HTONS(C_IN)); + cp += sizeof(u16_t); + + /* Localhost is fixed to 127.0.0.1. */ + localhost.h_name= + namencmp(qname, "localhost.", 10) == 0 ? (char *) qname : "localhost"; + localhost.h_aliases= noaliases; + localhost.h_addr_list= localaddrlist; + he= &localhost; + + sethostent(0); + do { + switch (type) { + case HTONS(T_A): + if (namecmp(qname, he->h_name) == 0) { + addA: + r= dn_comp((u8_t *) he->h_name, cp, arraylimit(dns.data) - cp, + dnvec, arraylimit(dnvec)); + if (r == -1) return 0; + cp += r; + if (cp + 3 * sizeof(u16_t) + 2 * sizeof(u32_t) + > arraylimit(dns.data)) { r= -1; break; } + pack16(cp, HTONS(T_A)); + cp += sizeof(u16_t); + pack16(cp, HTONS(C_IN)); + cp += sizeof(u16_t); + pack32(cp, httl); + cp += sizeof(u32_t); + pack16(cp, HTONS(sizeof(u32_t))); + cp += sizeof(u16_t); + memcpy(cp, he->h_addr, sizeof(u32_t)); + cp += sizeof(u32_t); + ancount++; + break; + } + /*FALL THROUGH*/ + case HTONS(T_CNAME): + domain= namechr(he->h_name, '.'); + for (i= 0; he->h_aliases[i] != nil; i++) { + namecpy(name, he->h_aliases[i]); + if (domain != nil && namechr(name, '.') == nil) { + namecat(name, domain); + } + if (namecmp(qname, name) == 0) { + r= dn_comp(name, cp, arraylimit(dns.data) - cp, + dnvec, arraylimit(dnvec)); + if (r == -1) break; + cp += r; + if (cp + 3 * sizeof(u16_t) + + 1 * sizeof(u32_t) > arraylimit(dns.data)) return 0; + pack16(cp, HTONS(T_CNAME)); + cp += sizeof(u16_t); + pack16(cp, HTONS(C_IN)); + cp += sizeof(u16_t); + pack32(cp, httl); + cp += sizeof(u32_t); + /* pack16(cp, htonl(RDLENGTH)) */ + cp += sizeof(u16_t); + r= dn_comp((u8_t *) he->h_name, cp, + arraylimit(dns.data) - cp, + dnvec, arraylimit(dnvec)); + if (r == -1) break; + pack16(cp - sizeof(u16_t), htons(r)); + cp += r; + ancount++; + if (type == HTONS(T_A)) goto addA; /* really wants A */ + break; + } + } + break; + case HTONS(T_PTR): + if (ancount > 0) break; + if (he->h_name[0] == '%') break; + sprintf((char *) name, "%d.%d.%d.%d.in-addr.arpa", + ((u8_t *) he->h_addr)[3], + ((u8_t *) he->h_addr)[2], + ((u8_t *) he->h_addr)[1], + ((u8_t *) he->h_addr)[0]); + if (namecmp(qname, name) == 0) { + r= dn_comp(name, cp, arraylimit(dns.data) - cp, + dnvec, arraylimit(dnvec)); + if (r == -1) break; + cp += r; + if (cp + 3 * sizeof(u16_t) + 1 * sizeof(u32_t) + > arraylimit(dns.data)) { r= -1; break; } + pack16(cp, HTONS(T_PTR)); + cp += sizeof(u16_t); + pack16(cp, HTONS(C_IN)); + cp += sizeof(u16_t); + pack32(cp, httl); + cp += sizeof(u32_t); + /* pack16(cp, htonl(RDLENGTH)) */ + cp += sizeof(u16_t); + r= dn_comp((u8_t *) he->h_name, cp, + arraylimit(dns.data) - cp, dnvec, arraylimit(dnvec)); + if (r == -1) return 0; + pack16(cp - sizeof(u16_t), htons(r)); + cp += r; + ancount++; + } + break; + } + } while (r != -1 && (he= gethostent()) != nil); + endhostent(); + + if (r == -1 || ancount == 0) return 0; + + dns.hdr.dh_ancount= htons(ancount); + memcpy(dp, &dns, *pdlen= cp - dns2oct(&dns)); + return 1; +} + +static int query_chaos(u8_t *qname, unsigned type, dns_t *dp, size_t *pdlen) +/* Report my version. Can't let BIND take all the credit. :-) */ +{ + int i, n, r; + dns_t dns; + u8_t *cp; + u8_t *dnvec[40]; + + if (type != HTONS(T_TXT) || namecmp(qname, "version.bind") != 0) return 0; + + dns.hdr.dh_flag1= DHF_QR | DHF_AA; + dns.hdr.dh_flag2= DHF_RA; + dns.hdr.dh_qdcount= HTONS(1); + dns.hdr.dh_ancount= HTONS(1); + dns.hdr.dh_nscount= HTONS(0); + dns.hdr.dh_arcount= htons(n_nameds); + + dnvec[0]= dns2oct(&dns); + dnvec[1]= nil; + cp= dns.data; + r= dn_comp(qname, cp, arraysize(dns.data), dnvec, arraylimit(dnvec)); + if (r == -1) return 0; + cp += r; + pack16(cp, type); + cp += sizeof(u16_t); + pack16(cp, HTONS(C_CHAOS)); + cp += sizeof(u16_t); + + r= dn_comp(qname, cp, arraylimit(dns.data) - cp, dnvec, arraylimit(dnvec)); + if (r == -1) return 0; + cp += r; + pack16(cp, HTONS(T_TXT)); + cp += sizeof(u16_t); + pack16(cp, HTONS(C_CHAOS)); + cp += sizeof(u16_t); + pack32(cp, HTONL(0)); + cp += sizeof(u32_t); + /* pack16(cp, htonl(RDLENGTH)) */ + cp += sizeof(u16_t); + sprintf((char *) cp + 1, "nonamed %s at %s:%u", + version, inet_ntoa(my_ip), ntohs(my_port)); + r= strlen((char *) cp + 1) + 1; + pack16(cp - sizeof(u16_t), htons(r)); + *cp= r-1; + cp += r; + for (n= 0, i= i_named; n < n_nameds; n++, i= (i+1) % n_nameds) { + r= dn_comp((u8_t *) "%nameserver", cp, arraylimit(dns.data) - cp, + dnvec, arraylimit(dnvec)); + if (r == -1) return 0; + cp += r; + if (cp + 3 * sizeof(u16_t) + + 2 * sizeof(u32_t) > arraylimit(dns.data)) return 0; + pack16(cp, HTONS(T_A)); + cp += sizeof(u16_t); + pack16(cp, HTONS(C_IN)); + cp += sizeof(u16_t); + pack32(cp, HTONL(0)); + cp += sizeof(u32_t); + pack16(cp, HTONS(sizeof(u32_t))); + cp += sizeof(u16_t); + memcpy(cp, &named[i], sizeof(u32_t)); + cp += sizeof(u32_t); + } + + memcpy(dp, &dns, *pdlen= cp - dns2oct(&dns)); + return 1; +} + +static void cache_reply(dns_t *dp, size_t dlen) +/* Store a DNS packet in the cache. */ +{ + int r; + query_t *qp, *less, *more; + unsigned usage; + u16_t type; + u8_t *cp; + u8_t name[MAXDNAME]; + u32_t minttl; + + if ((dp->hdr.dh_flag1 & (DHF_RD | DHF_TC)) != DHF_RD) return; + if (dp->hdr.dh_qdcount != HTONS(1)) return; + cp= dp->data; + r= dn_expand(dns2oct(dp), dns2oct(dp) + dlen, cp, name, MAXDNAME); + if (r == -1) return; + cp += r; + type= upack16(cp); + cp += sizeof(u16_t); + if (upack16(cp) != HTONS(C_IN)) return; + + /* Delete old cached data, if any. Note where it is in the LRU. */ + if ((qp= get_query(name, type)) != nil) { + less= qp->less; + more= qp->more; + usage= qp->usage; + deallocate(qp); + } else { + /* Not yet in the cache. */ + less= mru; + more= nil; + usage= 1; + } + + /* Determine minimum TTL. Discard if zero, never cache zero TTLs. */ + if ((minttl= dns_ttl(dp, dlen, 0)) == 0) return; + + /* Enter new reply in cache. */ + qp= allocate(nil, query_allocsize(dlen)); + qp->less= less; + qp->more= more; + qp->age= now; + qp->flags= 0; + qp->usage= usage; + qp->size= dlen; + memcpy(&qp->dns, dp, dlen); + qp->stale= qp->age + minttl; + insert_query(qp); + if (debug >= 1) printf("Answer cached\n"); + + /* Save the cache soon. */ + if (!dirty) { + dirty= 1; + newjob(job_save_cache, now + LONG_TIMEOUT, nil); + } +} + +static int job_save_cache(void *data, int expired) +/* Some time after the cache is changed it is written back to disk. */ +{ + if (!expired) return 0; + cache2file(); + dirty= 0; +} + +static int compose_reply(dns_t *dp, size_t *pdlen) +/* Try to compose a reply to a request in *dp using the hosts file or + * cached data. Return answer in *dp with its size in *pdlen. Return true + * iff an answer is given. + */ +{ + size_t dlen= *pdlen; + int r, rd; + query_t *qp; + unsigned id, type, class; + u8_t *cp; + u8_t name[MAXDNAME]; + + cp= dp->data; + r= dn_expand(dns2oct(dp), dns2oct(dp) + dlen, cp, name, MAXDNAME); + if (r != -1) { + cp += r; + if (cp + 2 * sizeof(u16_t) > dns2oct(dp) + dlen) { + r= -1; + } else { + type= upack16(cp); + cp += sizeof(u16_t); + class= upack16(cp); + cp += sizeof(u16_t); + } + } + + /* Remember ID and RD. */ + id= dp->hdr.dh_id; + rd= dp->hdr.dh_flag1 & DHF_RD; + + if (r == -1) { + /* Malformed query, reply "FORMERR". */ + dp->hdr.dh_flag1 &= ~(DHF_TC); + dp->hdr.dh_flag1 |= DHF_QR | DHF_AA; + dp->hdr.dh_flag2 &= ~(DHF_UNUSED | DHF_RCODE); + dp->hdr.dh_flag2 |= DHF_RA | FORMERR; + } else + if (class == HTONS(C_IN) && query_hosts(name, type, dp, pdlen)) { + /* Answer to this query is in the hosts file. */ + dlen= *pdlen; + } else + if (class == HTONS(C_IN) && (qp= get_query(name, type)) != nil) { + /* Answer to this query is present in the cache. */ + memcpy(dp, &qp->dns, dlen= qp->size); + dp->hdr.dh_flag1 &= ~DHF_AA; + (void) dns_ttl(dp, dlen, now - qp->age); + if (rd) { + if (qp->stale <= now) { + qp->flags |= QF_REFRESH; + q_refresh= 1; + } + qp->usage++; + } + put_query(qp); + } else + if (class == HTONS(C_CHAOS) && query_chaos(name, type, dp, pdlen)) { + /* Return our version numbers. */ + dlen= *pdlen; + } else + if (n_nameds == 0 || nxdomain(name)) { + /* No real name daemon present, or this name has a repeated top level + * domain sequence. Reply "no such domain". + */ + dp->hdr.dh_flag1 &= ~(DHF_TC); + dp->hdr.dh_flag1 |= DHF_QR | DHF_AA; + dp->hdr.dh_flag2 &= ~(DHF_UNUSED | DHF_RCODE); + dp->hdr.dh_flag2 |= DHF_RA | NXDOMAIN; + } else + if (!rd) { + /* "Recursion Desired" is off, so don't bother to relay. */ + dp->hdr.dh_flag1 &= ~(DHF_TC); + dp->hdr.dh_flag1 |= DHF_QR; + dp->hdr.dh_flag2 &= ~(DHF_UNUSED | DHF_RCODE); + dp->hdr.dh_flag2 |= DHF_RA | NOERROR; + } else { + /* Caller needs to consult with a real name daemon. */ + return 0; + } + + /* Copy ID and RD back to answer. */ + dp->hdr.dh_id= id; + dp->hdr.dh_flag1 &= ~DHF_RD; + dp->hdr.dh_flag1 |= rd; + *pdlen= dlen; + return 1; +} + +typedef struct udp_dns { /* One DNS packet over UDP. */ + udp_io_hdr_t hdr; /* UDP header (source/destination). */ + dns_t dns; /* DNS packet. */ +} udp_dns_t; + +static void refresh_cache(void) +/* Find a stale entry in the cache that was used to answer a query, and send + * a request to a name server that should refresh this entry. + */ +{ + query_t *qp; + unsigned type; + int r; + u8_t *cp; + size_t dlen, ulen; + u8_t qname[MAXDNAME+1]; + u8_t *dnvec[40]; + udp_dns_t udp; + + if (!q_refresh) return; + for (qp= lru; qp != nil; qp= qp->more) { + if ((qp->flags & QF_REFRESH) && qp->stale > now - stale) break; + } + if (qp == nil) { + q_refresh= 0; + return; + } + + /* Found one to refresh. */ + qp->flags &= ~QF_REFRESH; + r= dn_expand(dns2oct(&qp->dns), dns2oct(&qp->dns) + qp->size, + qp->dns.data, qname, MAXDNAME); + if (r == -1) return; + type= upack16(qp->dns.data+r); + + dnvec[0]= dns2oct(&udp.dns); + dnvec[1]= nil; + cp= udp.dns.data; + r= dn_comp(qname, cp, arraysize(udp.dns.data), dnvec, arraylimit(dnvec)); + if (r == -1) return; + cp += r; + pack16(cp, type); + cp += sizeof(u16_t); + pack16(cp, HTONS(C_IN)); + cp += sizeof(u16_t); + dlen= cp - dns2oct(&udp.dns); + + udp.dns.hdr.dh_id= new_id(ID_REFRESH, my_port, ID_IPSELF); + udp.dns.hdr.dh_flag1= DHF_RD; + udp.dns.hdr.dh_flag2= 0; + udp.dns.hdr.dh_qdcount= HTONS(1); + udp.dns.hdr.dh_ancount= HTONS(0); + udp.dns.hdr.dh_nscount= HTONS(0); + udp.dns.hdr.dh_arcount= HTONS(0); + + udp.hdr.uih_dst_addr= current_named(); + udp.hdr.uih_dst_port= named_port; + udp.hdr.uih_ip_opt_len= 0; + udp.hdr.uih_data_len= dlen; + + if (debug >= 1) { + printf("Refresh to %s:%u:\n", + inet_ntoa(current_named()), ntohs(named_port)); + dns_tell(0, &udp.dns, dlen); + } + ulen= offsetof(udp_dns_t, dns) + dlen; + if (write(udp_fd, &udp, ulen) < 0) fatal(udp_device); +} + +static int job_read_udp(void *data, int expired) +/* Read UDP queries and replies. */ +{ + size_t ulen, dlen; + static udp_dns_t udp; + u16_t id, port; + ipaddr_t ip; + time_t dtime; + + assert(!expired); + + /* Try to read a packet. */ + ulen= asyn_read(&asyn, udp_fd, &udp, sizeof(udp)); + dlen= ulen - offsetof(udp_dns_t, dns); + + if (ulen == -1) { + if (errno == EINPROGRESS && !expired) return 0; + if (errno == EIO) fatal(udp_device); + + if (debug >= 2) { + printf("%s: UDP read: %s\n", nowgmt(), strerror(errno)); + } + } else { + if (debug >= 2) { + printf("%s: UDP read, %d bytes\n", nowgmt(), (int) ulen); + } + } + + /* Restart this job no matter what. */ + newjob(job_read_udp, NEVER, nil); + + if (ulen < (ssize_t) (sizeof(udp_io_hdr_t) + sizeof(dns_hdr_t))) return 1; + + if (debug >= 1) { + printf("%s:%u UDP ", inet_ntoa(udp.hdr.uih_src_addr), + ntohs(udp.hdr.uih_src_port)); + dns_tell(0, &udp.dns, dlen); + } + + /* Check, and if necessary reinitialize my configuration. */ + init_config(udp.hdr.uih_dst_addr); + + if (udp.dns.hdr.dh_flag1 & DHF_QR) { + /* This is a remote named reply, not a query. */ + + /* Response to a query previously relayed? */ + if (!old_id(udp.dns.hdr.dh_id, &id, &port, &ip)) return 1; + + if (ip == ID_IPSELF && id == ID_PROBE) { + if (searching()) { + /* We have found a name server! */ + int i; + + /* In my list? */ + for (i= 0; i < n_nameds; i++) { + if (named[i] == udp.hdr.uih_src_addr) { + i_named= i; + if (debug >= 1) { + printf("Current named = %s\n", + inet_ntoa(current_named())); + } + stop_searching(); + force_expire(job_find_named); + } + } + } + } + + /* We got an answer, so stop worrying. */ + if (expecting()) { + stop_expecting(); + force_expire(job_expect_named); + } + + /* Put the information in the cache. */ + cache_reply(&udp.dns, dlen); + + /* Refresh a cached entry that was used when stale. */ + refresh_cache(); + + /* Discard reply to myself. */ + if (ip == ID_IPSELF) return 1; + + /* Send the reply to the process that asked for it. */ + udp.dns.hdr.dh_id= id; + udp.hdr.uih_dst_addr= ip; + udp.hdr.uih_dst_port= port; + if (debug >= 1) printf("To client %s:%u\n", inet_ntoa(ip), ntohs(port)); + } else { + /* A query. */ + if (udp.dns.hdr.dh_qdcount != HTONS(1)) return 1; + + /* Try to compose a reply from local data. */ + if (compose_reply(&udp.dns, &dlen)) { + udp.hdr.uih_dst_addr= udp.hdr.uih_src_addr; + udp.hdr.uih_dst_port= udp.hdr.uih_src_port; + udp.hdr.uih_ip_opt_len= 0; + udp.hdr.uih_data_len= dlen; + ulen= offsetof(udp_dns_t, dns) + dlen; + + /* Send an UDP DNS reply. */ + if (debug >= 1) { + printf("%s:%u UDP ", inet_ntoa(udp.hdr.uih_dst_addr), + ntohs(udp.hdr.uih_dst_port)); + dns_tell(0, &udp.dns, dlen); + } + } else { + /* Let a real name daemon handle the query. */ + udp.dns.hdr.dh_id= new_id(udp.dns.hdr.dh_id, + udp.hdr.uih_src_port, udp.hdr.uih_src_addr); + udp.hdr.uih_dst_addr= current_named(); + udp.hdr.uih_dst_port= named_port; + if (!expecting()) { + start_expecting(); + newjob(job_expect_named, now + MEDIUM_TIMEOUT, nil); + } + if (debug >= 1) { + printf("To named %s:%u\n", + inet_ntoa(current_named()), ntohs(named_port)); + } + } + } + if (write(udp_fd, &udp, ulen) < 0) fatal(udp_device); + return 1; +} + +#if DO_TCP + +typedef struct data_cl { /* Data for connect or listen jobs. */ + int fd; /* Open TCP channel. */ + int dn_fd; /* TCP channel to the name daemon. */ + int retry; /* Retrying a connect? */ + nwio_tcpcl_t tcpcl; /* Flags. */ +} data_cl_t; + +typedef struct data_rw { /* Data for TCP read or write jobs. */ + int r_fd; /* Read from this TCP channel. */ + int w_fd; /* And write to this TCP channel. */ + struct data_rw *rev; /* Optional reverse TCP channel. */ + u8_t *buf; /* Buffer for bytes to transfer. */ + ssize_t offset; /* Offset in buf to r/w at. */ + size_t size; /* Size of buf. */ +} data_rw_t; + +static int job_setup_listen(void *data, int expired) +/* Set up a listening channel for TCP DNS queries. */ +{ + data_cl_t *data_cl= data; + nwio_tcpconf_t tcpconf; + nwio_tcpopt_t tcpopt; + int fd; + + if (!expired) return 0; + if (debug >= 2) printf("%s: Setup listen\n", nowgmt()); + + if (data_cl == nil) { + if ((fd= open(tcp_device, O_RDWR)) < 0) { + if (errno != EMFILE) report(tcp_device); + newjob(job_setup_listen, now + SHORT_TIMEOUT, nil); + return 1; + } + + tcpconf.nwtc_flags= NWTC_SHARED | NWTC_LP_SET | NWTC_UNSET_RA + | NWTC_UNSET_RP; + tcpconf.nwtc_locport= my_port; + if (ioctl(fd, NWIOSTCPCONF, &tcpconf) == -1) fatal(tcp_device); + + tcpopt.nwto_flags= NWTO_DEL_RST; + if (ioctl(fd, NWIOSTCPOPT, &tcpopt) == -1) fatal(tcp_device); + + data_cl= allocate(nil, sizeof(*data_cl)); + data_cl->fd= fd; + data_cl->tcpcl.nwtcl_flags= 0; + } + /* And listen. */ + newjob(job_listen, NEVER, data_cl); + return 1; +} + +static int job_listen(void *data, int expired) +/* A connection on the TCP DNS query channel. */ +{ + data_cl_t *data_cl= data; + + /* Wait for a client. */ + if (asyn_ioctl(&asyn, data_cl->fd, NWIOTCPLISTEN, &data_cl->tcpcl) < 0) { + if (errno == EINPROGRESS) return 0; + report(tcp_device); + + /* Try again after a short time. */ + newjob(job_setup_listen, now + SHORT_TIMEOUT, data_cl); + return 1; + } + if (debug >= 2) printf("%s: Listen\n", nowgmt()); + + /* Immediately resume listening. */ + newjob(job_setup_listen, IMMEDIATE, nil); + + /* Set up a connect to the real name daemon. */ + data_cl->retry= 0; + newjob(job_setup_connect, IMMEDIATE, data_cl); + return 1; +} + +static void start_relay(int fd, int dn_fd) +/* Start one or two read jobs after job_setup_connect() or job_connect(). */ +{ + data_rw_t *query; /* Client to DNS daemon relay. */ + data_rw_t *reply; /* DNS daemon to client relay. */ + + query= allocate(nil, sizeof(*query)); + query->r_fd= fd; + query->buf= allocate(nil, sizeof(u16_t)); + query->offset= 0; + query->size= sizeof(u16_t); + if (dn_fd == NO_FD) { + /* Answer mode. */ + query->w_fd= fd; + query->rev= nil; + } else { + /* Relay mode. */ + reply= allocate(nil, sizeof(*reply)); + reply->r_fd= dn_fd; + reply->w_fd= fd; + reply->buf= allocate(nil, sizeof(u16_t)); + reply->offset= 0; + reply->size= sizeof(u16_t); + reply->rev= query; + query->w_fd= dn_fd; + query->rev= reply; + newjob(job_read_reply, now + LONG_TIMEOUT, reply); + } + newjob(job_read_query, now + LONG_TIMEOUT, query); +} + +static void close_relay(data_rw_t *data_rw) +/* Close a relay channel. */ +{ + if (data_rw->rev != nil) { + /* Other end still active, signal EOF. */ + (void) ioctl(data_rw->w_fd, NWIOTCPSHUTDOWN, nil); + data_rw->rev->rev= nil; + } else { + /* Close both ends down. */ + asyn_close(&asyn, data_rw->r_fd); + close(data_rw->r_fd); + if (data_rw->w_fd != data_rw->r_fd) { + asyn_close(&asyn, data_rw->w_fd); + close(data_rw->w_fd); + } + } + deallocate(data_rw->buf); + deallocate(data_rw); +} + +static int job_setup_connect(void *data, int expired) +/* Set up a connect for a TCP channel to the real name daemon. */ +{ + nwio_tcpconf_t tcpconf; + int dn_fd; + data_cl_t *data_cl= data; + + if (!expired) return 0; + if (debug >= 2) printf("%s: Setup connect\n", nowgmt()); + + if (n_nameds == 0) { + /* No name daemons to relay to, answer myself. */ + start_relay(data_cl->fd, NO_FD); + deallocate(data_cl); + return 1; + } + + if ((dn_fd= open(tcp_device, O_RDWR)) < 0) { + if (errno != EMFILE) report(tcp_device); + if (++data_cl->retry < 5) { + /* Retry. */ + newjob(job_setup_connect, now + SHORT_TIMEOUT, data_cl); + } else { + /* Reply myself (bound to fail). */ + start_relay(data_cl->fd, NO_FD); + deallocate(data_cl); + } + return 1; + } + + tcpconf.nwtc_flags= NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; + tcpconf.nwtc_remaddr= current_named(); + tcpconf.nwtc_remport= named_port; + if (ioctl(dn_fd, NWIOSTCPCONF, &tcpconf) == -1) fatal(tcp_device); + + /* And connect. */ + data_cl->dn_fd= dn_fd; + data_cl->tcpcl.nwtcl_flags= 0; + newjob(job_connect, NEVER, data_cl); + return 1; +} + +static int job_connect(void *data, int expired) +/* Connect to a TCP DNS query channel. */ +{ + data_cl_t *data_cl= data; + + /* Try to connect. */ + if (asyn_ioctl(&asyn, data_cl->dn_fd, NWIOTCPCONN, &data_cl->tcpcl) < 0) { + if (errno == EINPROGRESS) return 0; + if (errno == EIO) fatal(tcp_device); + + /* Connection refused. */ + if (debug >= 2) printf("%s: Connect: %s\n", nowgmt(), strerror(errno)); + asyn_close(&asyn, data_cl->dn_fd); + close(data_cl->dn_fd); + data_cl->dn_fd= NO_FD; + if (++data_cl->retry < 5) { + /* Search a new name daemon. */ + if (!searching()) { + start_searching(); + force_expire(job_find_named); + } + newjob(job_setup_connect, NEVER, data_cl); + return 1; + } + /* Reply with a failure eventually. */ + } + if (debug >= 2) printf("%s: Connect\n", nowgmt()); + + /* Read the query from the user, send on to the name daemon, etc. */ + start_relay(data_cl->fd, data_cl->dn_fd); + deallocate(data_cl); + return 1; +} + +static void tcp_dns_tell(int fd, u8_t *buf) +/* Tell about a DNS packet on a TCP channel. */ +{ + nwio_tcpconf_t tcpconf; + + if (ioctl(fd, NWIOGTCPCONF, &tcpconf) < 0) { + printf("??\?:?? TCP "); + } else { + printf("%s:%u TCP ", inet_ntoa(tcpconf.nwtc_remaddr), + ntohs(tcpconf.nwtc_remport)); + } + dns_tell(0, oct2dns(buf + sizeof(u16_t)), ntohs(upack16(buf))); +} + +static int job_read_query(void *data, int expired) +/* Read TCP queries from the client. */ +{ + data_rw_t *data_rw= data; + ssize_t count; + + /* Try to read count bytes. */ + count= asyn_read(&asyn, data_rw->r_fd, + data_rw->buf + data_rw->offset, + data_rw->size - data_rw->offset); + + if (count < 0) { + if (errno == EINPROGRESS && !expired) return 0; + if (errno == EIO) fatal(tcp_device); + + /* Remote end is late, or an error occurred. */ + if (debug >= 2) { + printf("%s: TCP read query: %s\n", nowgmt(), strerror(errno)); + } + close_relay(data_rw); + return 1; + } + + if (debug >= 2) { + printf("%s: TCP read query, %d/%u bytes\n", + nowgmt(), data_rw->offset + count, data_rw->size); + } + if (count == 0) { + /* EOF. */ + close_relay(data_rw); + return 1; + } + data_rw->offset += count; + if (data_rw->offset == data_rw->size) { + data_rw->size= sizeof(u16_t) + ntohs(upack16(data_rw->buf)); + if (data_rw->size < sizeof(u16_t)) { + /* Malformed. */ + close_relay(data_rw); + return 1; + } + if (data_rw->offset < data_rw->size) { + /* Query not complete, read more. */ + data_rw->buf= allocate(data_rw->buf, data_rw->size); + newjob(job_read_query, now + LONG_TIMEOUT, data_rw); + return 1; + } + } + + if (data_rw->size < sizeof(u16_t) + sizeof(dns_hdr_t)) { + close_relay(data_rw); + return 1; + } + if (debug >= 1) tcp_dns_tell(data_rw->r_fd, data_rw->buf); + + /* Relay or reply. */ + if (data_rw->w_fd != data_rw->r_fd) { + /* We have a real name daemon to do the work. */ + data_rw->offset= 0; + newjob(job_write_query, now + LONG_TIMEOUT, data_rw); + } else { + /* No real name daemons or none reachable, so use the hosts file. */ + dns_t *dp; + size_t dlen; + + if (data_rw->size < sizeof(u16_t) + PACKETSZ) { + data_rw->buf= allocate(data_rw->buf, sizeof(u16_t) + PACKETSZ); + } + + /* Build a reply packet. */ + dp= oct2dns(data_rw->buf + sizeof(u16_t)); + dlen= data_rw->size - sizeof(u16_t); + if (!compose_reply(dp, &dlen)) { + /* We're told to ask a name daemon, but that won't work. */ + close_relay(data_rw); + return 1; + } + + /* Start a reply write. */ + pack16(data_rw->buf, htons(dlen)); + data_rw->size= sizeof(u16_t) + dlen; + data_rw->buf= allocate(data_rw->buf, data_rw->size); + data_rw->offset= 0; + newjob(job_write_reply, now + LONG_TIMEOUT, data_rw); + } + return 1; +} + +static int job_write_query(void *data, int expired) +/* Relay a TCP query to the name daemon. */ +{ + data_rw_t *data_rw= data; + ssize_t count; + + /* Try to write count bytes to the name daemon. */ + count= asyn_write(&asyn, data_rw->w_fd, + data_rw->buf + data_rw->offset, + data_rw->size - data_rw->offset); + + if (count <= 0) { + if (errno == EINPROGRESS && !expired) return 0; + if (errno == EIO) fatal(tcp_device); + + /* A write expired or failed (usually a broken connection.) */ + if (debug >= 2) { + printf("%s: TCP write query: %s\n", nowgmt(), strerror(errno)); + } + close_relay(data_rw); + return 1; + } + + if (debug >= 2) { + printf("%s: TCP write query, %d/%u bytes\n", + nowgmt(), data_rw->offset + count, data_rw->size); + } + data_rw->offset += count; + if (data_rw->offset < data_rw->size) { + /* Partial write, continue. */ + newjob(job_write_query, now + LONG_TIMEOUT, data_rw); + return 1; + } + if (debug >= 1) tcp_dns_tell(data_rw->w_fd, data_rw->buf); + + /* Query fully send on, go read more queries. */ + data_rw->offset= 0; + data_rw->size= sizeof(u16_t); + newjob(job_read_query, now + LONG_TIMEOUT, data_rw); + return 1; +} + +static int job_read_reply(void *data, int expired) +/* Read a TCP reply from the real name daemon. */ +{ + data_rw_t *data_rw= data; + ssize_t count; + + /* Try to read count bytes. */ + count= asyn_read(&asyn, data_rw->r_fd, + data_rw->buf + data_rw->offset, + data_rw->size - data_rw->offset); + + if (count < 0) { + if (errno == EINPROGRESS && !expired) return 0; + if (errno == EIO) fatal(tcp_device); + + /* Remote end is late, or an error occurred. */ + if (debug >= 2) { + printf("%s: TCP read reply: %s\n", nowgmt(), strerror(errno)); + } + close_relay(data_rw); + return 1; + } + + if (debug >= 2) { + printf("%s: TCP read reply, %d/%u bytes\n", + nowgmt(), data_rw->offset + count, data_rw->size); + } + if (count == 0) { + /* EOF. */ + close_relay(data_rw); + return 1; + } + data_rw->offset += count; + if (data_rw->offset == data_rw->size) { + data_rw->size= sizeof(u16_t) + ntohs(upack16(data_rw->buf)); + if (data_rw->size < sizeof(u16_t)) { + /* Malformed. */ + close_relay(data_rw); + return 1; + } + if (data_rw->offset < data_rw->size) { + /* Reply not complete, read more. */ + data_rw->buf= allocate(data_rw->buf, data_rw->size); + newjob(job_read_reply, now + LONG_TIMEOUT, data_rw); + return 1; + } + } + if (debug >= 1) tcp_dns_tell(data_rw->r_fd, data_rw->buf); + + /* Reply fully read, send it on. */ + data_rw->offset= 0; + newjob(job_write_reply, now + LONG_TIMEOUT, data_rw); + return 1; +} + +static int job_write_reply(void *data, int expired) +/* Send a TCP reply to the client. */ +{ + data_rw_t *data_rw= data; + ssize_t count; + + /* Try to write count bytes to the client. */ + count= asyn_write(&asyn, data_rw->w_fd, + data_rw->buf + data_rw->offset, + data_rw->size - data_rw->offset); + + if (count <= 0) { + if (errno == EINPROGRESS && !expired) return 0; + if (errno == EIO) fatal(tcp_device); + + /* A write expired or failed (usually a broken connection.) */ + if (debug >= 2) { + printf("%s: TCP write reply: %s\n", nowgmt(), strerror(errno)); + } + close_relay(data_rw); + return 1; + } + + if (debug >= 2) { + printf("%s: TCP write reply, %d/%u bytes\n", + nowgmt(), data_rw->offset + count, data_rw->size); + } + data_rw->offset += count; + if (data_rw->offset < data_rw->size) { + /* Partial write, continue. */ + newjob(job_write_reply, now + LONG_TIMEOUT, data_rw); + return 1; + } + if (debug >= 1) tcp_dns_tell(data_rw->w_fd, data_rw->buf); + + /* Reply fully send on, go read more replies (or queries). */ + data_rw->offset= 0; + data_rw->size= sizeof(u16_t); + newjob(data_rw->w_fd != data_rw->r_fd ? job_read_reply : job_read_query, + now + LONG_TIMEOUT, data_rw); + return 1; +} +#else /* !DO_TCP */ + +static int job_dummy(void *data, int expired) +{ + return 1; +} +#define job_setup_listen job_dummy +#define job_setup_connect job_dummy +#endif /* !DO_TCP */ + +static void named_probe(ipaddr_t ip) +/* Send a probe to a name daemon, like 'host -r -t ns . <ip>'. */ +{ + udp_dns_t udp; +# define dlen (offsetof(dns_t, data) + 5) +# define ulen (offsetof(udp_dns_t, dns) + dlen) + + /* Send a simple DNS query that all name servers can answer easily: + * "What are the name servers for the root domain?" + */ + udp.dns.hdr.dh_id= new_id(ID_PROBE, my_port, ID_IPSELF); + udp.dns.hdr.dh_flag1= 0; + udp.dns.hdr.dh_flag2= 0; + udp.dns.hdr.dh_qdcount= HTONS(1); + udp.dns.hdr.dh_ancount= HTONS(0); + udp.dns.hdr.dh_nscount= HTONS(0); + udp.dns.hdr.dh_arcount= HTONS(0); + + udp.dns.data[0] = 0; /* Null name. */ + pack16(udp.dns.data+1, HTONS(T_NS)); + pack16(udp.dns.data+3, HTONS(C_IN)); + if (debug >= 1) { + printf("PROBE %s ", inet_ntoa(ip)); + dns_tell(0, &udp.dns, dlen); + } + + udp.hdr.uih_dst_addr= ip; + udp.hdr.uih_dst_port= named_port; + udp.hdr.uih_ip_opt_len= 0; + udp.hdr.uih_data_len= dlen; + + if (write(udp_fd, &udp, ulen) < 0) fatal(udp_device); +#undef dlen +#undef ulen +} + +static int job_find_named(void *data, int expired) +/* Look for a real name daemon to answer real DNS queries. */ +{ + if (!expired) return 0; + if (debug >= 2) printf("%s: Find named\n", nowgmt()); + + /* New search? */ + if (search_ct < 0) { + search_ct= n_nameds; + i_named= -1; + } + + if (--search_ct < 0) { + /* Forced end of search (named response!), or end of search with + * nothing found. Search again after a long time. + */ + newjob(job_find_named, + (stale > 0 || i_named > 0) ? now + LONG_TIMEOUT : NEVER, nil); + force_expire(job_setup_connect); + return 1; + } + + /* Send a named probe. */ + i_named= (i_named+1) % n_nameds; + named_probe(current_named()); + + /* Schedule the next call. */ + newjob(job_find_named, now + SHORT_TIMEOUT, nil); + return 1; +} + +static int job_expect_named(void *data, int expired) +/* The real name server is expected to answer by now. */ +{ + if (!expired) return 0; + if (debug >= 2) printf("%s: Expect named\n", nowgmt()); + + if (expecting() && !searching()) { + /* No answer yet, start searching. */ + start_searching(); + force_expire(job_find_named); + } + return 1; +} + +static void sig_handler(int sig) +/* A signal forces a search for a real name daemon, etc. */ +{ + switch (sig) { + case SIGINT: + case SIGTERM: done= 1; break; + case SIGHUP: reinit= 1; break; + case SIGUSR1: debug++; break; + case SIGUSR2: debug= 0; break; + } +} + +static void usage(void) +{ + fprintf(stderr, "Usage: nonamed [-qs] [-d[level]] [-p port]\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + job_t *job; + nwio_udpopt_t udpopt; + int i; + struct servent *servent; + struct sigaction sa; + FILE *fp; + int quit= 0; + + /* Debug output must be line buffered. */ + setvbuf(stdout, nil, _IOLBF, 0); + + /* DNS service port number? */ + if ((servent= getservbyname("domain", nil)) == nil) { + fprintf(stderr, "nonamed: \"domain\": unknown service\n"); + exit(1); + } + my_port= servent->s_port; + named_port= servent->s_port; + + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++] + 1, *end; + + if (opt[0] == '-' && opt[1] == 0) break; + + switch (*opt++) { + case 'd': /* Debug level. */ + debug= 1; + if (between('0', *opt, '9')) debug= strtoul(opt, &opt, 10); + break; + case 'p': /* Port to listen to (for testing.) */ + if (*opt == 0) { + if (i == argc) usage(); + opt= argv[i++]; + } + my_port= htons(strtoul(opt, &end, 0)); + if (opt == end || *end != 0) usage(); + opt= end; + break; + case 's': + single= 1; + break; + case 'q': /* Quit after printing cache contents. */ + quit= 1; + break; + default: + usage(); + } + } + if (i != argc) usage(); + + if (quit) { + /* Oops, just having a look at the cache. */ + debug= 2; + now= time(nil); + n_datamax= -1; + file2cache(); + return 0; + } + + /* Don't die on broken pipes, reinitialize on hangup, etc. */ + sa.sa_handler= SIG_IGN; + sigemptyset(&sa.sa_mask); + sa.sa_flags= 0; + sigaction(SIGPIPE, &sa, nil); + sa.sa_handler= sig_handler; + sigaction(SIGINT, &sa, nil); + sigaction(SIGHUP, &sa, nil); + sigaction(SIGUSR1, &sa, nil); + sigaction(SIGUSR2, &sa, nil); + sigaction(SIGTERM, &sa, nil); + + /* TCP and UDP device names. */ + if ((tcp_device= getenv("TCP_DEVICE")) == nil) tcp_device= TCP_DEVICE; + if ((udp_device= getenv("UDP_DEVICE")) == nil) udp_device= UDP_DEVICE; + + /* Open an UDP channel for incoming DNS queries. */ + if ((udp_fd= open(udp_device, O_RDWR)) < 0) fatal(udp_device); + + udpopt.nwuo_flags= NWUO_EXCL | NWUO_LP_SET | NWUO_EN_LOC | NWUO_DI_BROAD + | NWUO_RP_ANY | NWUO_RA_ANY | NWUO_RWDATALL | NWUO_DI_IPOPT; + udpopt.nwuo_locport= my_port; + if (ioctl(udp_fd, NWIOSUDPOPT, &udpopt) == -1 + || ioctl(udp_fd, NWIOGUDPOPT, &udpopt) == -1 + ) { + fatal(udp_device); + } + + /* The current time is... */ + now= time(nil); + + /* Read configuration and data cached by the previous nonamed. */ + init_config(udpopt.nwuo_locaddr); + file2cache(); + + if (!single) { + /* Save process id. */ + if ((fp= fopen(PIDFILE, "w")) != nil) { + fprintf(fp, "%u\n", (unsigned) getpid()); + fclose(fp); + } + } + + /* Jobs that start the ball rolling. */ + newjob(job_read_udp, NEVER, nil); + newjob(job_setup_listen, IMMEDIATE, nil); + newjob(job_find_named, IMMEDIATE, nil); + + while (!done) { + /* There is always something in the queue. */ + assert(queue != nil); + + /* Any expired jobs? */ + while (queue->timeout <= now) { + (void) execjob(queue, 1); + assert(queue != nil); + } + + /* Check I/O jobs. */ + for (job= queue; job != nil; job= job->next) { + if (execjob(job, 0)) break; + } + + if (queue->timeout != IMMEDIATE) { + struct timeval tv, *tvp; + + if (debug >= 2) printf("%s: I/O wait", nowgmt()); + + if (queue->timeout != NEVER) { + tv.tv_sec= queue->timeout; + tv.tv_usec= 0; + tvp= &tv; + if (debug >= 2) printf(" (expires %s)\n", timegmt(tv.tv_sec)); + } else { + tvp= nil; + if (debug >= 2) fputc('\n', stdout); + } + fflush(stdout); + + if (asyn_wait(&asyn, 0, tvp) < 0) { + if (errno != EINTR && errno != EAGAIN) fatal("fwait()"); + } + now= time(nil); + } + + if (reinit) { + /* A hangup makes us go back to square one. */ + reinit= 0; + if (ioctl(udp_fd, NWIOGUDPOPT, &udpopt) == -1) fatal(udp_device); + init_config(udpopt.nwuo_locaddr); + start_searching(); + force_expire(job_find_named); + } + } + cache2file(); + (void) unlink(PIDFILE); + if (debug >= 2) printf("sbrk(0) = %u\n", (unsigned) sbrk(0)); + return 0; +} diff --git a/commands/simple/od.c b/commands/simple/od.c new file mode 100755 index 000000000..4bacb44ea --- /dev/null +++ b/commands/simple/od.c @@ -0,0 +1,314 @@ +/* od - octal dump Author: Andy Tanenbaum */ + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + + +int bflag, cflag, dflag, oflag, xflag, hflag, vflag; +int linenr, width, state, ever; +int prevwds[8]; +long off; +char buf[512], buffer[BUFSIZ]; +int next; +int bytespresent; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(long offset, (int argc, char *argv [], int k)); +_PROTOTYPE(void dumpfile, (void)); +_PROTOTYPE(void wdump, (short *words, int k, int radix)); +_PROTOTYPE(void bdump, (char bytes [16 ], int k, int c)); +_PROTOTYPE(void byte, (int val, int c)); +_PROTOTYPE(int getwords, (short **words)); +_PROTOTYPE(int same, (short *w1, int *w2)); +_PROTOTYPE(void outword, (int val, int radix)); +_PROTOTYPE(void outnum, (int num, int radix)); +_PROTOTYPE(void addrout, (long l)); +_PROTOTYPE(char hexit, (int k)); +_PROTOTYPE(void usage, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int k, flags; + char *p; + + /* Process flags */ + setbuf(stdout, buffer); + flags = 0; + p = argv[1]; + if (argc > 1 && *p == '-') { + /* Flags present. */ + flags++; + p++; + while (*p) { + switch (*p) { + case 'b': bflag++; break; + case 'c': cflag++; break; + case 'd': dflag++; break; + case 'h': hflag++; break; + case 'o': oflag++; break; + case 'v': vflag++; break; + case 'x': xflag++; break; + default: usage(); + } + p++; + } + } else { + oflag = 1; + } + if ((bflag | cflag | dflag | oflag | xflag) == 0) oflag = 1; + k = (flags ? 2 : 1); + if (bflag | cflag) { + width = 8; + } else if (oflag) { + width = 7; + } else if (dflag) { + width = 6; + } else { + width = 5; + } + + /* Process file name, if any. */ + p = argv[k]; + if (k < argc && *p != '+') { + /* Explicit file name given. */ + close(0); + if (open(argv[k], O_RDONLY) != 0) { + fprintf(stderr, "od: cannot open %s\n", argv[k]); + exit(1); + } + k++; + } + + /* Process offset, if any. */ + if (k < argc) { + /* Offset present. */ + off = offset(argc, argv, k); + off = (off / 16L) * 16L; + lseek(0, off, SEEK_SET); + } + dumpfile(); + addrout(off); + printf("\n"); + return(0); +} + + +long offset(argc, argv, k) +int argc; +char *argv[]; +int k; +{ + int dot, radix; + char *p, c; + long val; + + /* See if the offset is decimal. */ + dot = 0; + p = argv[k]; + while (*p) + if (*p++ == '.') dot = 1; + + /* Convert offset to binary. */ + radix = (dot ? 10 : 8); + val = 0; + p = argv[k]; + if (*p == '+') p++; + while (*p != 0 && *p != '.') { + c = *p++; + if (c < '0' || c > '9') { + printf("Bad character in offset: %c\n", c); + exit(1); + } + val = radix * val + c - '0'; + } + + p = argv[k + 1]; + if (k + 1 == argc - 1 && *p == 'b') val = 512L * val; + return(val); +} + + +void dumpfile() +{ + int k; + short *words; + + while ((k = getwords(&words))) { /* 'k' is # bytes read */ + if (!vflag) { /* ensure 'lazy' evaluation */ + if (k == 16 && ever == 1 && same(words, prevwds)) { + if (state == 0) { + printf("*\n"); + state = 1; + off += 16; + continue; + } else if (state == 1) { + off += 16; + continue; + } + } + } + addrout(off); + off += k; + state = 0; + ever = 1; + linenr = 1; + if (oflag) wdump(words, k, 8); + if (dflag) wdump(words, k, 10); + if (xflag) wdump(words, k, 16); + if (cflag) bdump((char *)words, k, (int)'c'); + if (bflag) bdump((char *)words, k, (int)'b'); + for (k = 0; k < 8; k++) prevwds[k] = words[k]; + for (k = 0; k < 8; k++) words[k] = 0; + } +} + + +void wdump(words, k, radix) +short *words; +int k, radix; +{ + int i; + + if (linenr++ != 1) printf(" "); + for (i = 0; i < (k + 1) / 2; i++) outword(words[i] & 0xFFFF, radix); + printf("\n"); +} + + +void bdump(bytes, k, c) +char bytes[16]; +int k; +char c; +{ + int i; + + if (linenr++ != 1) printf(" "); + for (i = 0; i < k; i++) byte(bytes[i] & 0377, c); + printf("\n"); +} + +void byte(val, c) +int val; +char c; +{ + if (c == 'b') { + printf(" "); + outnum(val, 7); + return; + } + if (val == 0) + printf(" \\0"); + else if (val == '\b') + printf(" \\b"); + else if (val == '\f') + printf(" \\f"); + else if (val == '\n') + printf(" \\n"); + else if (val == '\r') + printf(" \\r"); + else if (val == '\t') + printf(" \\t"); + else if (val >= ' ' && val < 0177) + printf(" %c", val); + else { + printf(" "); + outnum(val, 7); + } +} + + +int getwords(words) +short **words; +{ + int count; + + if (next >= bytespresent) { + bytespresent = read(0, buf, 512); + next = 0; + } + if (next >= bytespresent) return(0); + *words = (short *) &buf[next]; + if (next + 16 <= bytespresent) + count = 16; + else + count = bytespresent - next; + + next += count; + return(count); +} + +int same(w1, w2) +short *w1; +int *w2; +{ + int i; + i = 8; + while (i--) + if (*w1++ != *w2++) return(0); + return(1); +} + +void outword(val, radix) +int val, radix; +{ +/* Output 'val' in 'radix' in a field of total size 'width'. */ + + int i; + + if (radix == 16) i = width - 4; + if (radix == 10) i = width - 5; + if (radix == 8) i = width - 6; + if (i == 1) + printf(" "); + else if (i == 2) + printf(" "); + else if (i == 3) + printf(" "); + else if (i == 4) + printf(" "); + outnum(val, radix); +} + + +void outnum(num, radix) +int num, radix; +{ +/* Output a number with all leading 0s present. Octal is 6 places, + * decimal is 5 places, hex is 4 places. + */ + unsigned val; + + val = (unsigned) num; + if (radix == 8) + printf ("%06o", val); + else if (radix == 10) + printf ("%05u", val); + else if (radix == 16) + printf ("%04x", val); + else if (radix == 7) { + /* special case */ + printf ("%03o", val); + } +} + + +void addrout(l) +long l; +{ + if (hflag == 0) { + printf("%07lo", l); + } else { + printf("%07lx", l); + } +} + + +void usage() +{ + fprintf(stderr, "Usage: od [-bcdhovx] [file] [ [+] offset [.] [b] ]\n"); +} diff --git a/commands/simple/origmkfs.c b/commands/simple/origmkfs.c new file mode 100644 index 000000000..c437b6f50 --- /dev/null +++ b/commands/simple/origmkfs.c @@ -0,0 +1,1374 @@ +/* mkfs - make the MINIX filesystem Authors: Tanenbaum et al. */ + +/* Authors: Andy Tanenbaum, Paul Ogilvie, Frans Meulenbroeks, Bruce Evans + * + * This program can make both version 1 and version 2 file systems, as follows: + * mkfs /dev/fd0 1200 # Version 2 (default) + * mkfs -1 /dev/fd0 360 # Version 1 + * + */ + +#include <sys/types.h> +#include <sys/dir.h> +#include <sys/stat.h> +#include <stdio.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <dirent.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <minix/config.h> +#include <minix/const.h> +#include <minix/type.h> +#include <minix/minlib.h> +#include "../../servers/fs/const.h" +#if (MACHINE == IBM_PC) +#include <minix/partition.h> +#include <minix/u64.h> +#include <sys/ioctl.h> +#endif + +#undef EXTERN +#define EXTERN /* get rid of EXTERN by making it null */ +#include "../../servers/fs/type.h" +#include "../../servers/fs/super.h" +#include <minix/fslib.h> + +#ifndef DOS +#ifndef UNIX +#define UNIX +#endif +#endif + +#undef BLOCK_SIZE +#define BLOCK_SIZE 1024 + +#define INODE_MAP 2 +#define MAX_TOKENS 10 +#define LINE_LEN 200 +#define BIN 2 +#define BINGRP 2 +#define BIT_MAP_SHIFT 13 +#define N_BLOCKS (1024L * 1024) +#define N_BLOCKS16 (128L * 1024) +#define INODE_MAX ((unsigned) 65535) + +/* You can make a really large file system on a 16-bit system, but the array + * of bits that get_block()/putblock() needs gets a bit big, so we can only + * prefill MAX_INIT blocks. (16-bit fsck can't check a file system larger + * than N_BLOCKS16 anyway.) + */ +#define MAX_INIT (sizeof(char *) == 2 ? N_BLOCKS16 : N_BLOCKS) + + +#ifdef DOS +maybedefine O_RDONLY 4 /* O_RDONLY | BINARY_BIT */ + maybedefine BWRITE 5 /* O_WRONLY | BINARY_BIT */ +#endif + +#if (MACHINE == ATARI) +int isdev; +#endif + +extern char *optarg; +extern int optind; + +int next_zone, next_inode, zone_size, zone_shift = 0, zoff; +block_t nrblocks; +int inode_offset, lct = 0, disk, fd, print = 0, file = 0; +unsigned int nrinodes; +int override = 0, simple = 0, dflag; +int donttest; /* skip test if it fits on medium */ +char *progname; + +long current_time, bin_time; +char zero[BLOCK_SIZE], *lastp; +char umap[MAX_INIT / 8]; /* bit map tells if block read yet */ +block_t zone_map; /* where is zone map? (depends on # inodes) */ +int inodes_per_block; +int fs_version; +block_t max_nrblocks; + +FILE *proto; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(block_t sizeup, (char *device)); +_PROTOTYPE(void super, (zone_t zones, Ino_t inodes)); +_PROTOTYPE(void rootdir, (Ino_t inode)); +_PROTOTYPE(void eat_dir, (Ino_t parent)); +_PROTOTYPE(void eat_file, (Ino_t inode, int f)); +_PROTOTYPE(void enter_dir, (Ino_t parent, char *name, Ino_t child)); +_PROTOTYPE(void incr_size, (Ino_t n, long count)); +_PROTOTYPE(PRIVATE ino_t alloc_inode, (int mode, int usrid, int grpid)); +_PROTOTYPE(PRIVATE zone_t alloc_zone, (void)); +_PROTOTYPE(void add_zone, (Ino_t n, zone_t z, long bytes, long cur_time)); +_PROTOTYPE(void add_z_1, (Ino_t n, zone_t z, long bytes, long cur_time)); +_PROTOTYPE(void add_z_2, (Ino_t n, zone_t z, long bytes, long cur_time)); +_PROTOTYPE(void incr_link, (Ino_t n)); +_PROTOTYPE(void insert_bit, (block_t block, int bit)); +_PROTOTYPE(int mode_con, (char *p)); +_PROTOTYPE(void getline, (char line[LINE_LEN], char *parse[MAX_TOKENS])); +_PROTOTYPE(void check_mtab, (char *devname)); +_PROTOTYPE(long file_time, (int f)); +_PROTOTYPE(void pexit, (char *s)); +_PROTOTYPE(void copy, (char *from, char *to, int count)); +_PROTOTYPE(void print_fs, (void)); +_PROTOTYPE(int read_and_set, (block_t n)); +_PROTOTYPE(void special, (char *string)); +_PROTOTYPE(void get_block, (block_t n, char buf[BLOCK_SIZE])); +_PROTOTYPE(void put_block, (block_t n, char buf[BLOCK_SIZE])); +_PROTOTYPE(void cache_init, (void)); +_PROTOTYPE(void flush, (void)); +_PROTOTYPE(void mx_read, (int blocknr, char buf[BLOCK_SIZE])); +_PROTOTYPE(void mx_write, (int blocknr, char buf[BLOCK_SIZE])); +_PROTOTYPE(void dexit, (char *s, int sectnum, int err)); +_PROTOTYPE(void usage, (void)); + +/*================================================================ + * mkfs - make filesystem + *===============================================================*/ +int main(argc, argv) +int argc; +char *argv[]; +{ + int nread, mode, usrid, grpid, ch; + block_t blocks; + block_t i; + ino_t root_inum; + ino_t inodes; + zone_t zones; + char *token[MAX_TOKENS], line[LINE_LEN]; + struct stat statbuf; + + /* Get two times, the current time and the mod time of the binary of + * mkfs itself. When the -d flag is used, the later time is put into + * the i_mtimes of all the files. This feature is useful when + * producing a set of file systems, and one wants all the times to be + * identical. First you set the time of the mkfs binary to what you + * want, then go. + */ + current_time = time((time_t *) 0); /* time mkfs is being run */ + stat(argv[0], &statbuf); + bin_time = statbuf.st_mtime; /* time when mkfs binary was last modified */ + + /* Process switches. */ + progname = argv[0]; + blocks = 0; + i = 0; + fs_version = 2; + inodes_per_block = V2_INODES_PER_BLOCK(BLOCK_SIZE); + max_nrblocks = N_BLOCKS; + while ((ch = getopt(argc, argv, "1b:di:lot")) != EOF) + switch (ch) { + case '1': + fs_version = 1; + inodes_per_block = V1_INODES_PER_BLOCK; + max_nrblocks = 0xFFFF; + break; + case 'b': + blocks = strtoul(optarg, (char **) NULL, 0); + break; + case 'd': + dflag = 1; + current_time = bin_time; + break; + case 'i': + i = strtoul(optarg, (char **) NULL, 0); + break; + case 'l': print = 1; break; + case 'o': override = 1; break; + case 't': donttest = 1; break; + default: usage(); + } + + /* Determine the size of the device if not specified as -b or proto. */ + if (argc - optind == 1 && blocks == 0) blocks = sizeup(argv[optind]); + printf("%lu blocks\n", blocks); + + /* The remaining args must be 'special proto', or just 'special' if the + * block size has already been specified. + */ + if (argc - optind != 2 && (argc - optind != 1 || blocks == 0)) usage(); + + /* Check special. */ + check_mtab(argv[optind]); + + /* Check and start processing proto. */ + optarg = argv[++optind]; + if (optind < argc && (proto = fopen(optarg, "r")) != NULL) { + /* Prototype file is readable. */ + lct = 1; + getline(line, token); /* skip boot block info */ + + /* Read the line with the block and inode counts. */ + getline(line, token); + blocks = atol(token[0]); + if (blocks > max_nrblocks) { + printf("%d > %d\n", blocks, max_nrblocks); + pexit("Block count too large"); + } + if (sizeof(char *) == 2 && blocks > N_BLOCKS16) { + fprintf(stderr, + "%s: warning: FS is larger than the %dM that fsck can check!\n", + progname, (int) (N_BLOCKS16 / (1024L * 1024))); + } + inodes = atoi(token[1]); + + /* Process mode line for root directory. */ + getline(line, token); + mode = mode_con(token[0]); + usrid = atoi(token[1]); + grpid = atoi(token[2]); + } else { + lct = 0; + if (optind < argc) { + /* Maybe the prototype file is just a size. Check. */ + blocks = strtoul(optarg, (char **) NULL, 0); + if (blocks == 0) pexit("Can't open prototype file"); + } + if (i == 0) { + /* The default for inodes is 3 blocks per inode, rounded up + * to fill an inode block. Above 20M, the average files are + * sure to be larger because it is hard to fill up 20M with + * tiny files, so reduce the default number of inodes. This + * default can always be overridden by using the -i option. + */ + i = blocks / 3; + if (blocks >= 20000) i = blocks / 4; + if (blocks >= 40000) i = blocks / 5; + if (blocks >= 60000) i = blocks / 6; + if (blocks >= 80000) i = blocks / 7; + if (blocks >= 100000) i = blocks / 8; + i += inodes_per_block - 1; + i = i / inodes_per_block * inodes_per_block; + if (i > INODE_MAX) i = INODE_MAX; + } + if (blocks < 5) pexit("Block count too small"); + if (blocks > max_nrblocks) { + printf("%d > %d\n", blocks, max_nrblocks); + pexit("Block count too large"); + } + if (i < 1) pexit("Inode count too small"); + if (i > INODE_MAX) pexit("Inode count too large"); + inodes = (ino_t) i; + + /* Make simple file system of the given size, using defaults. */ + mode = 040777; + usrid = BIN; + grpid = BINGRP; + simple = 1; + } + nrblocks = blocks; + nrinodes = inodes; + + /* Open special. */ + special(argv[--optind]); + +#ifdef UNIX + if (!donttest) { + static short testb[BLOCK_SIZE / sizeof(short)]; + + /* Try writing the last block of partition or diskette. */ + lseek(fd, (off_t) (blocks - 1) * BLOCK_SIZE, SEEK_SET); + testb[0] = 0x3245; + testb[1] = 0x11FF; + if (write(fd, (char *) testb, BLOCK_SIZE) != BLOCK_SIZE) + pexit("File system is too big for minor device"); + sync(); /* flush write, so if error next read fails */ + lseek(fd, (off_t) (blocks - 1) * BLOCK_SIZE, SEEK_SET); + testb[0] = 0; + testb[1] = 0; + nread = read(fd, (char *) testb, BLOCK_SIZE); + if (nread != BLOCK_SIZE || testb[0] != 0x3245 || testb[1] != 0x11FF) + pexit("File system is too big for minor device"); + lseek(fd, (off_t) (blocks - 1) * BLOCK_SIZE, SEEK_SET); + testb[0] = 0; + testb[1] = 0; + if (write(fd, (char *) testb, BLOCK_SIZE) != BLOCK_SIZE) + pexit("File system is too big for minor device"); + lseek(fd, 0L, SEEK_SET); + } +#endif + + /* Make the file-system */ + + cache_init(); + +#if (MACHINE == ATARI) + if (isdev) { + char block0[BLOCK_SIZE]; + get_block((block_t) 0, block0); + /* Need to read twice; first time gets an empty block */ + get_block((block_t) 0, block0); + /* Zero parts of the boot block so the disk won't be + * recognized as a tos disk any more. */ + block0[0] = block0[1] = 0; /* branch code to boot code */ + strncpy(&block0[2], "MINIX ", (size_t) 6); + block0[16] = 0; /* number of FATS */ + block0[17] = block0[18] = 0; /* number of dir entries */ + block0[22] = block0[23] = 0; /* sectors/FAT */ + bzero(&block0[30], 480);/* boot code */ + put_block((block_t) 0, block0); + } else +#endif + + put_block((block_t) 0, zero); /* Write a null boot block. */ + + zone_shift = 0; /* for future use */ + zones = nrblocks >> zone_shift; + + super(zones, inodes); + + root_inum = alloc_inode(mode, usrid, grpid); + rootdir(root_inum); + if (simple == 0) eat_dir(root_inum); + + if (print) print_fs(); + flush(); + return(0); + + /* NOTREACHED */ +} /* end main */ + + +/*================================================================ + * sizeup - determine device size + *===============================================================*/ +block_t sizeup(device) +char *device; +{ + int fd; + struct partition entry; + + if ((fd = open(device, O_RDONLY)) == -1) return 0; + if (ioctl(fd, DIOCGETP, &entry) == -1) entry.size = cvu64(0); + close(fd); + return div64u(entry.size, BLOCK_SIZE); +} + + +/*================================================================ + * super - construct a superblock + *===============================================================*/ + +void super(zones, inodes) +zone_t zones; +ino_t inodes; +{ + unsigned int i; + int inodeblks; + int initblks; + + zone_t initzones, nrzones, v1sq, v2sq; + zone_t zo; + struct super_block *sup; + char buf[BLOCK_SIZE], *cp; + + for (cp = buf; cp < &buf[BLOCK_SIZE]; cp++) *cp = 0; + sup = (struct super_block *) buf; /* lint - might use a union */ + + sup->s_ninodes = inodes; + if (fs_version == 1) { + sup->s_nzones = zones; + } else { + sup->s_nzones = 0; /* not used in V2 - 0 forces errors early */ + sup->s_zones = zones; + } + sup->s_imap_blocks = bitmapsize((bit_t) (1 + inodes), BLOCK_SIZE); + sup->s_zmap_blocks = bitmapsize((bit_t) zones, BLOCK_SIZE); + inode_offset = sup->s_imap_blocks + sup->s_zmap_blocks + 2; + inodeblks = (inodes + inodes_per_block - 1) / inodes_per_block; + initblks = inode_offset + inodeblks; + initzones = (initblks + (1 << zone_shift) - 1) >> zone_shift; + nrzones = nrblocks >> zone_shift; + sup->s_firstdatazone = (initblks + (1 << zone_shift) - 1) >> zone_shift; + zoff = sup->s_firstdatazone - 1; + sup->s_log_zone_size = zone_shift; + if (fs_version == 1) { + sup->s_magic = SUPER_MAGIC; /* identify super blocks */ + v1sq = (zone_t) V1_INDIRECTS * V1_INDIRECTS; + zo = V1_NR_DZONES + (long) V1_INDIRECTS + v1sq; + } else { + sup->s_magic = SUPER_V2;/* identify super blocks */ + v2sq = (zone_t) V2_INDIRECTS(BLOCK_SIZE) * V2_INDIRECTS(BLOCK_SIZE); + zo = V2_NR_DZONES + (zone_t) V2_INDIRECTS(BLOCK_SIZE) + v2sq; + } + sup->s_max_size = zo * BLOCK_SIZE; + zone_size = 1 << zone_shift; /* nr of blocks per zone */ + + put_block((block_t) 1, buf); + + /* Clear maps and inodes. */ + for (i = 2; i < initblks; i++) put_block((block_t) i, zero); + + next_zone = sup->s_firstdatazone; + next_inode = 1; + + zone_map = INODE_MAP + sup->s_imap_blocks; + + insert_bit(zone_map, 0); /* bit zero must always be allocated */ + insert_bit((block_t) INODE_MAP, 0); /* inode zero not used but + * must be allocated */ +} + + +/*================================================================ + * rootdir - install the root directory + *===============================================================*/ +void rootdir(inode) +ino_t inode; +{ + zone_t z; + + z = alloc_zone(); + add_zone(inode, z, 32L, current_time); + enter_dir(inode, ".", inode); + enter_dir(inode, "..", inode); + incr_link(inode); + incr_link(inode); +} + + +/*================================================================ + * eat_dir - recursively install directory + *===============================================================*/ +void eat_dir(parent) +ino_t parent; +{ + /* Read prototype lines and set up directory. Recurse if need be. */ + char *token[MAX_TOKENS], *p; + char line[LINE_LEN]; + int mode, usrid, grpid, maj, min, f; + ino_t n; + zone_t z; + long size; + + while (1) { + getline(line, token); + p = token[0]; + if (*p == '$') return; + p = token[1]; + mode = mode_con(p); + usrid = atoi(token[2]); + grpid = atoi(token[3]); + if (grpid & 0200) fprintf(stderr, "A.S.Tanenbaum\n"); + n = alloc_inode(mode, usrid, grpid); + + /* Enter name in directory and update directory's size. */ + enter_dir(parent, token[0], n); + incr_size(parent, 16L); + + /* Check to see if file is directory or special. */ + incr_link(n); + if (*p == 'd') { + /* This is a directory. */ + z = alloc_zone(); /* zone for new directory */ + add_zone(n, z, 32L, current_time); + enter_dir(n, ".", n); + enter_dir(n, "..", parent); + incr_link(parent); + incr_link(n); + eat_dir(n); + } else if (*p == 'b' || *p == 'c') { + /* Special file. */ + maj = atoi(token[4]); + min = atoi(token[5]); + size = 0; + if (token[6]) size = atoi(token[6]); + size = BLOCK_SIZE * size; + add_zone(n, (zone_t) ((maj << 8) | min), size, current_time); + } else { + /* Regular file. Go read it. */ + if ((f = open(token[4], O_RDONLY)) < 0) { + fprintf(stderr, "%s: Can't open %s: %s\n", + progname, token[4], strerror(errno)); + } else + eat_file(n, f); + } + } + +} + +/*================================================================ + * eat_file - copy file to MINIX + *===============================================================*/ +/* Zonesize >= blocksize */ +void eat_file(inode, f) +ino_t inode; +int f; +{ + int ct, i, j, k; + zone_t z; + char buf[BLOCK_SIZE]; + long timeval; + + do { + for (i = 0, j = 0; i < zone_size; i++, j += ct) { + for (k = 0; k < BLOCK_SIZE; k++) buf[k] = 0; + if ((ct = read(f, buf, BLOCK_SIZE)) > 0) { + if (i == 0) z = alloc_zone(); + put_block((z << zone_shift) + i, buf); + } + } + timeval = (dflag ? current_time : file_time(f)); + if (ct) add_zone(inode, z, (long) j, timeval); + } while (ct == BLOCK_SIZE); + close(f); +} + + + +/*================================================================ + * directory & inode management assist group + *===============================================================*/ +void enter_dir(parent, name, child) +ino_t parent, child; +char *name; +{ + /* Enter child in parent directory */ + /* Works for dir > 1 block and zone > block */ + int i, j, k, l, off; + block_t b; + zone_t z; + char *p1, *p2; + struct direct dir_entry[NR_DIR_ENTRIES(BLOCK_SIZE)]; + d1_inode ino1[V1_INODES_PER_BLOCK]; + d2_inode ino2[V2_INODES_PER_BLOCK(BLOCK_SIZE)]; + int nr_dzones; + + b = ((parent - 1) / inodes_per_block) + inode_offset; + off = (parent - 1) % inodes_per_block; + + if (fs_version == 1) { + get_block(b, (char *) ino1); + nr_dzones = V1_NR_DZONES; + } else { + get_block(b, (char *) ino2); + nr_dzones = V2_NR_DZONES; + } + for (k = 0; k < nr_dzones; k++) { + if (fs_version == 1) { + z = ino1[off].d1_zone[k]; + if (z == 0) { + z = alloc_zone(); + ino1[off].d1_zone[k] = z; + } + } else { + z = ino2[off].d2_zone[k]; + if (z == 0) { + z = alloc_zone(); + ino2[off].d2_zone[k] = z; + } + } + for (l = 0; l < zone_size; l++) { + get_block((z << zone_shift) + l, (char *) dir_entry); + for (i = 0; i < NR_DIR_ENTRIES(BLOCK_SIZE); i++) { + if (dir_entry[i].d_ino == 0) { + dir_entry[i].d_ino = child; + p1 = name; + p2 = dir_entry[i].d_name; + j = 14; + while (j--) { + *p2++ = *p1; + if (*p1 != 0) p1++; + } + put_block((z << zone_shift) + l, (char *) dir_entry); + if (fs_version == 1) { + put_block(b, (char *) ino1); + } else { + put_block(b, (char *) ino2); + } + return; + } + } + } + } + + printf("Directory-inode %d beyond direct blocks. Could not enter %s\n", + parent, name); + pexit("Halt"); +} + + +void add_zone(n, z, bytes, cur_time) +ino_t n; +zone_t z; +long bytes, cur_time; +{ + if (fs_version == 1) { + add_z_1(n, z, bytes, cur_time); + } else { + add_z_2(n, z, bytes, cur_time); + } +} + +void add_z_1(n, z, bytes, cur_time) +ino_t n; +zone_t z; +long bytes, cur_time; +{ + /* Add zone z to inode n. The file has grown by 'bytes' bytes. */ + + int off, i; + block_t b; + zone_t indir; + zone1_t blk[V1_INDIRECTS]; + d1_inode *p; + d1_inode inode[V1_INODES_PER_BLOCK]; + + b = ((n - 1) / V1_INODES_PER_BLOCK) + inode_offset; + off = (n - 1) % V1_INODES_PER_BLOCK; + get_block(b, (char *) inode); + p = &inode[off]; + p->d1_size += bytes; + p->d1_mtime = cur_time; + for (i = 0; i < V1_NR_DZONES; i++) + if (p->d1_zone[i] == 0) { + p->d1_zone[i] = (zone1_t) z; + put_block(b, (char *) inode); + return; + } + put_block(b, (char *) inode); + + /* File has grown beyond a small file. */ + if (p->d1_zone[V1_NR_DZONES] == 0) + p->d1_zone[V1_NR_DZONES] = (zone1_t) alloc_zone(); + indir = p->d1_zone[V1_NR_DZONES]; + put_block(b, (char *) inode); + b = indir << zone_shift; + get_block(b, (char *) blk); + for (i = 0; i < V1_INDIRECTS; i++) + if (blk[i] == 0) { + blk[i] = (zone1_t) z; + put_block(b, (char *) blk); + return; + } + pexit("File has grown beyond single indirect"); +} + +void add_z_2(n, z, bytes, cur_time) +ino_t n; +zone_t z; +long bytes, cur_time; +{ + /* Add zone z to inode n. The file has grown by 'bytes' bytes. */ + + int off, i; + block_t b; + zone_t indir; + zone_t blk[V2_INDIRECTS(BLOCK_SIZE)]; + d2_inode *p; + d2_inode inode[V2_INODES_PER_BLOCK(BLOCK_SIZE)]; + + b = ((n - 1) / V2_INODES_PER_BLOCK(BLOCK_SIZE)) + inode_offset; + off = (n - 1) % V2_INODES_PER_BLOCK(BLOCK_SIZE); + get_block(b, (char *) inode); + p = &inode[off]; + p->d2_size += bytes; + p->d2_mtime = cur_time; + for (i = 0; i < V2_NR_DZONES; i++) + if (p->d2_zone[i] == 0) { + p->d2_zone[i] = z; + put_block(b, (char *) inode); + return; + } + put_block(b, (char *) inode); + + /* File has grown beyond a small file. */ + if (p->d2_zone[V2_NR_DZONES] == 0) p->d2_zone[V2_NR_DZONES] = alloc_zone(); + indir = p->d2_zone[V2_NR_DZONES]; + put_block(b, (char *) inode); + b = indir << zone_shift; + get_block(b, (char *) blk); + for (i = 0; i < V2_INDIRECTS(BLOCK_SIZE); i++) + if (blk[i] == 0) { + blk[i] = z; + put_block(b, (char *) blk); + return; + } + pexit("File has grown beyond single indirect"); +} + + +void incr_link(n) +ino_t n; +{ + /* Increment the link count to inode n */ + int off; + block_t b; + + b = ((n - 1) / inodes_per_block) + inode_offset; + off = (n - 1) % inodes_per_block; + if (fs_version == 1) { + d1_inode inode1[V1_INODES_PER_BLOCK]; + + get_block(b, (char *) inode1); + inode1[off].d1_nlinks++; + put_block(b, (char *) inode1); + } else { + d2_inode inode2[V2_INODES_PER_BLOCK(BLOCK_SIZE)]; + + get_block(b, (char *) inode2); + inode2[off].d2_nlinks++; + put_block(b, (char *) inode2); + } +} + + +void incr_size(n, count) +ino_t n; +long count; +{ + /* Increment the file-size in inode n */ + block_t b; + int off; + + b = ((n - 1) / inodes_per_block) + inode_offset; + off = (n - 1) % inodes_per_block; + if (fs_version == 1) { + d1_inode inode1[V1_INODES_PER_BLOCK]; + + get_block(b, (char *) inode1); + inode1[off].d1_size += count; + put_block(b, (char *) inode1); + } else { + d2_inode inode2[V2_INODES_PER_BLOCK(BLOCK_SIZE)]; + + get_block(b, (char *) inode2); + inode2[off].d2_size += count; + put_block(b, (char *) inode2); + } +} + + +/*================================================================ + * allocation assist group + *===============================================================*/ +PRIVATE ino_t alloc_inode(mode, usrid, grpid) +int mode, usrid, grpid; +{ + ino_t num; + int off; + block_t b; + + num = next_inode++; + if (num > nrinodes) pexit("File system does not have enough inodes"); + b = ((num - 1) / inodes_per_block) + inode_offset; + off = (num - 1) % inodes_per_block; + if (fs_version == 1) { + d1_inode inode1[V1_INODES_PER_BLOCK]; + + get_block(b, (char *) inode1); + inode1[off].d1_mode = mode; + inode1[off].d1_uid = usrid; + inode1[off].d1_gid = grpid; + put_block(b, (char *) inode1); + } else { + d2_inode inode2[V2_INODES_PER_BLOCK(BLOCK_SIZE)]; + + get_block(b, (char *) inode2); + inode2[off].d2_mode = mode; + inode2[off].d2_uid = usrid; + inode2[off].d2_gid = grpid; + put_block(b, (char *) inode2); + } + + /* Set the bit in the bit map. */ + /* DEBUG FIXME. This assumes the bit is in the first inode map block. */ + insert_bit((block_t) INODE_MAP, (int) num); + return(num); +} + + +PRIVATE zone_t alloc_zone() +{ + /* Allocate a new zone */ + /* Works for zone > block */ + block_t b; + int i; + zone_t z; + + z = next_zone++; + b = z << zone_shift; + if ((b + zone_size) > nrblocks) + pexit("File system not big enough for all the files"); + for (i = 0; i < zone_size; i++) + put_block(b + i, zero); /* give an empty zone */ + /* DEBUG FIXME. This assumes the bit is in the first zone map block. */ + insert_bit(zone_map, (int) (z - zoff)); /* lint, NOT OK because + * z hasn't been broken + * up into block + + * offset yet. */ + return(z); +} + + +void insert_bit(block, bit) +block_t block; +int bit; +{ + /* Insert 'count' bits in the bitmap */ + int w, s; + short buf[BLOCK_SIZE / sizeof(short)]; + + if (block < 0) pexit("insert_bit called with negative argument"); + get_block(block, (char *) buf); + w = bit / (8 * sizeof(short)); + s = bit % (8 * sizeof(short)); + buf[w] |= (1 << s); + put_block(block, (char *) buf); +} + + +/*================================================================ + * proto-file processing assist group + *===============================================================*/ +int mode_con(p) +char *p; +{ + /* Convert string to mode */ + int o1, o2, o3, mode; + char c1, c2, c3; + + c1 = *p++; + c2 = *p++; + c3 = *p++; + o1 = *p++ - '0'; + o2 = *p++ - '0'; + o3 = *p++ - '0'; + mode = (o1 << 6) | (o2 << 3) | o3; + if (c1 == 'd') mode += I_DIRECTORY; + if (c1 == 'b') mode += I_BLOCK_SPECIAL; + if (c1 == 'c') mode += I_CHAR_SPECIAL; + if (c1 == '-') mode += I_REGULAR; + if (c2 == 'u') mode += I_SET_UID_BIT; + if (c3 == 'g') mode += I_SET_GID_BIT; + return(mode); +} + +void getline(line, parse) +char *parse[MAX_TOKENS]; +char line[LINE_LEN]; +{ + /* Read a line and break it up in tokens */ + int k; + char c, *p; + int d; + + for (k = 0; k < MAX_TOKENS; k++) parse[k] = 0; + for (k = 0; k < LINE_LEN; k++) line[k] = 0; + k = 0; + parse[0] = 0; + p = line; + while (1) { + if (++k > LINE_LEN) pexit("Line too long"); + d = fgetc(proto); + if (d == EOF) pexit("Unexpected end-of-file"); + *p = d; + if (*p == '\n') lct++; + if (*p == ' ' || *p == '\t') *p = 0; + if (*p == '\n') { + *p++ = 0; + *p = '\n'; + break; + } + p++; + } + + k = 0; + p = line; + lastp = line; + while (1) { + c = *p++; + if (c == '\n') return; + if (c == 0) continue; + parse[k++] = p - 1; + do { + c = *p++; + } while (c != 0 && c != '\n'); + } +} + + +/*================================================================ + * other stuff + *===============================================================*/ +void check_mtab(devname) +char *devname; /* /dev/hd1 or whatever */ +{ +/* Check to see if the special file named in s is mounted. */ + + int n; + char special[PATH_MAX + 1], mounted_on[PATH_MAX + 1], version[10], rw_flag[10]; + + if (load_mtab("mkfs") < 0) return; + while (1) { + n = get_mtab_entry(special, mounted_on, version, rw_flag); + if (n < 0) return; + if (strcmp(devname, special) == 0) { + /* Can't mkfs on top of a mounted file system. */ + fprintf(stderr, "%s: %s is mounted on %s\n", + progname, devname, mounted_on); + exit(1); + } + } +} + + +long file_time(f) +int f; +{ +#ifdef UNIX + struct stat statbuf; + fstat(f, &statbuf); + return(statbuf.st_mtime); +#else /* fstat not supported by DOS */ + return(0L); +#endif +} + + +void pexit(s) +char *s; +{ + fprintf(stderr, "%s: %s\n", progname, s); + if (lct != 0) + fprintf(stderr, "Line %d being processed when error detected.\n", lct); + flush(); + exit(2); +} + + +void copy(from, to, count) +char *from, *to; +int count; +{ + while (count--) *to++ = *from++; +} + + +void print_fs() +{ + int i, j; + ino_t k; + d1_inode inode1[V1_INODES_PER_BLOCK]; + d2_inode inode2[V2_INODES_PER_BLOCK(BLOCK_SIZE)]; + unsigned short usbuf[BLOCK_SIZE / sizeof(unsigned short)]; + block_t b, inode_limit; + struct direct dir[NR_DIR_ENTRIES(BLOCK_SIZE)]; + + get_block((block_t) 1, (char *) usbuf); + printf("\nSuperblock: "); + for (i = 0; i < 8; i++) printf("%06o ", usbuf[i]); + get_block((block_t) 2, (char *) usbuf); + printf("...\nInode map: "); + for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]); + get_block((block_t) 3, (char *) usbuf); + printf("...\nZone map: "); + for (i = 0; i < 9; i++) printf("%06o ", usbuf[i]); + printf("...\n"); + + k = 0; + for (b = inode_offset; k < nrinodes; b++) { + if (fs_version == 1) { + get_block(b, (char *) inode1); + } else { + get_block(b, (char *) inode2); + } + for (i = 0; i < inodes_per_block; i++) { + k = inodes_per_block * (int) (b - inode_offset) + i + 1; + /* Lint but OK */ + if (k > nrinodes) break; + if (fs_version == 1) { + if (inode1[i].d1_mode != 0) { + printf("Inode %2d: mode=", k); + printf("%06o", inode1[i].d1_mode); + printf(" uid=%2d gid=%2d size=", + inode1[i].d1_uid, inode1[i].d1_gid); + printf("%6ld", inode1[i].d1_size); + printf(" zone[0]=%d\n", inode1[i].d1_zone[0]); + } + if ((inode1[i].d1_mode & I_TYPE) == I_DIRECTORY) { + /* This is a directory */ + get_block(inode1[i].d1_zone[0], (char *) dir); + for (j = 0; j < NR_DIR_ENTRIES(BLOCK_SIZE); j++) + if (dir[j].d_ino) + printf("\tInode %2d: %s\n", dir[j].d_ino, dir[j].d_name); + } + } else { + if (inode2[i].d2_mode != 0) { + printf("Inode %2d: mode=", k); + printf("%06o", inode2[i].d2_mode); + printf(" uid=%2d gid=%2d size=", + inode2[i].d2_uid, inode2[i].d2_gid); + printf("%6ld", inode2[i].d2_size); + printf(" zone[0]=%ld\n", inode2[i].d2_zone[0]); + } + if ((inode2[i].d2_mode & I_TYPE) == I_DIRECTORY) { + /* This is a directory */ + get_block(inode2[i].d2_zone[0], (char *) dir); + for (j = 0; j < NR_DIR_ENTRIES(BLOCK_SIZE); j++) + if (dir[j].d_ino) + printf("\tInode %2d: %s\n", dir[j].d_ino, dir[j].d_name); + } + } + } + } + + printf("%d inodes used. %d zones used.\n", next_inode - 1, next_zone); +} + + +int read_and_set(n) +block_t n; +{ +/* The first time a block is read, it returns all 0s, unless there has + * been a write. This routine checks to see if a block has been accessed. + */ + + int w, s, mask, r; + + if (sizeof(char *) == 2 && n >= MAX_INIT) pexit("can't initialize past 128M"); + w = n / 8; + s = n % 8; + mask = 1 << s; + r = (umap[w] & mask ? 1 : 0); + umap[w] |= mask; + return(r); +} + +void usage() +{ + fprintf(stderr, + "Usage: %s [-1dlot] [-b blocks] [-i inodes] special [proto]\n", + progname); + exit(1); +} + +/*================================================================ + * get_block & put_block for MS-DOS + *===============================================================*/ +#ifdef DOS + +/* + * These are the get_block and put_block routines + * when compiling & running mkfs.c under MS-DOS. + * + * It requires the (asembler) routines absread & abswrite + * from the file diskio.asm. Since these routines just do + * as they are told (read & write the sector specified), + * a local cache is used to minimize the i/o-overhead for + * frequently used blocks. + * + * The global variable "file" determines whether the output + * is to a disk-device or to a binary file. + */ + + +#define PH_SECTSIZE 512 /* size of a physical disk-sector */ + + +char *derrtab[14] = { + "no error", + "disk is read-only", + "unknown unit", + "device not ready", + "bad command", + "data error", + "internal error: bad request structure length", + "seek error", + "unknown media type", + "sector not found", + "printer out of paper (?)", + "write fault", + "read error", + "general error" +}; + +#define CACHE_SIZE 20 /* 20 block-buffers */ + + +struct cache { + char blockbuf[BLOCK_SIZE]; + block_t blocknum; + int dirty; + int usecnt; +} cache[CACHE_SIZE]; + + +void special(string) +char *string; +{ + + if (string[1] == ':' && string[2] == 0) { + /* Format: d: or d:fname */ + disk = (string[0] & ~32) - 'A'; + if (disk > 1 && !override) /* safety precaution */ + pexit("Bad drive specifier for special"); + } else { + file = 1; + if ((fd = creat(string, BWRITE)) == 0) + pexit("Can't open special file"); + } +} + +void get_block(n, buf) +block_t n; +char buf[BLOCK_SIZE]; +{ + /* Get a block to the user */ + struct cache *bp, *fp; + + /* First access returns a zero block */ + if (read_and_set(n) == 0) { + copy(zero, buf, BLOCK_SIZE); + return; + } + + /* Look for block in cache */ + fp = 0; + for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) { + if (bp->blocknum == n) { + copy(bp, buf, BLOCK_SIZE); + bp->usecnt++; + return; + } + + /* Remember clean block */ + if (bp->dirty == 0) + if (fp) { + if (fp->usecnt > bp->usecnt) fp = bp; + } else + fp = bp; + } + + /* Block not in cache, get it */ + if (!fp) { + /* No clean buf, flush one */ + for (bp = cache, fp = cache; bp < &cache[CACHE_SIZE]; bp++) + if (fp->usecnt > bp->usecnt) fp = bp; + mx_write(fp->blocknum, fp); + } + mx_read(n, fp); + fp->dirty = 0; + fp->usecnt = 0; + fp->blocknum = n; + copy(fp, buf, BLOCK_SIZE); +} + +void put_block(n, buf) +block_t n; +char buf[BLOCK_SIZE]; +{ + /* Accept block from user */ + struct cache *fp, *bp; + + (void) read_and_set(n); + + /* Look for block in cache */ + fp = 0; + for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) { + if (bp->blocknum == n) { + copy(buf, bp, BLOCK_SIZE); + bp->dirty = 1; + return; + } + + /* Remember clean block */ + if (bp->dirty == 0) + if (fp) { + if (fp->usecnt > bp->usecnt) fp = bp; + } else + fp = bp; + } + + /* Block not in cache */ + if (!fp) { + /* No clean buf, flush one */ + for (bp = cache, fp = cache; bp < &cache[CACHE_SIZE]; bp++) + if (fp->usecnt > bp->usecnt) fp = bp; + mx_write(fp->blocknum, fp); + } + fp->dirty = 1; + fp->usecnt = 1; + fp->blocknum = n; + copy(buf, fp, BLOCK_SIZE); +} + +void cache_init() +{ + struct cache *bp; + for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) bp->blocknum = -1; +} + +void flush() +{ + /* Flush all dirty blocks to disk */ + struct cache *bp; + + for (bp = cache; bp < &cache[CACHE_SIZE]; bp++) + if (bp->dirty) { + mx_write(bp->blocknum, bp); + bp->dirty = 0; + } +} + +/*================================================================== + * hard read & write etc. + *=================================================================*/ +#define MAX_RETRIES 5 + + +void mx_read(blocknr, buf) +int blocknr; +char buf[BLOCK_SIZE]; +{ + + /* Read the requested MINIX-block in core */ + char (*bp)[PH_SECTSIZE]; + int sectnum, retries, err; + + if (file) { + lseek(fd, (off_t) blocknr * BLOCK_SIZE, 0); + if (read(fd, buf, BLOCK_SIZE) != BLOCK_SIZE) + pexit("mx_read: error reading file"); + } else { + sectnum = blocknr * (BLOCK_SIZE / PH_SECTSIZE); + for (bp = buf; bp < &buf[BLOCK_SIZE]; bp++) { + retries = MAX_RETRIES; + do + err = absread(disk, sectnum, bp); + while (err && --retries); + + if (retries) { + sectnum++; + } else { + dexit("mx_read", sectnum, err); + } + } + } +} + +void mx_write(blocknr, buf) +int blocknr; +char buf[BLOCK_SIZE]; +{ + /* Write the MINIX-block to disk */ + char (*bp)[PH_SECTSIZE]; + int retries, sectnum, err; + + if (file) { + lseek(fd, blocknr * BLOCK_SIZE, 0); + if (write(fd, buf, BLOCK_SIZE) != BLOCK_SIZE) { + pexit("mx_write: error writing file"); + } + } else { + sectnum = blocknr * (BLOCK_SIZE / PH_SECTSIZE); + for (bp = buf; bp < &buf[BLOCK_SIZE]; bp++) { + retries = MAX_RETRIES; + do { + err = abswrite(disk, sectnum, bp); + } while (err && --retries); + + if (retries) { + sectnum++; + } else { + dexit("mx_write", sectnum, err); + } + } + } +} + + +void dexit(s, sectnum, err) +int sectnum, err; +char *s; +{ + printf("Error: %s, sector: %d, code: %d, meaning: %s\n", + s, sectnum, err, derrtab[err]); + exit(2); +} + +#endif + +/*================================================================ + * get_block & put_block for UNIX + *===============================================================*/ +#ifdef UNIX + +void special(string) +char *string; +{ + fd = creat(string, 0777); + close(fd); + fd = open(string, O_RDWR); + if (fd < 0) pexit("Can't open special file"); +#if (MACHINE == ATARI) + { + struct stat statbuf; + + if (fstat(fd, &statbuf) < 0) return; + isdev = (statbuf.st_mode & S_IFMT) == S_IFCHR + || + (statbuf.st_mode & S_IFMT) == S_IFBLK + ; + } +#endif +} + + + +void get_block(n, buf) +block_t n; +char buf[BLOCK_SIZE]; +{ +/* Read a block. */ + + int k; + + /* First access returns a zero block */ + if (read_and_set(n) == 0) { + copy(zero, buf, BLOCK_SIZE); + return; + } + lseek(fd, (off_t) n * BLOCK_SIZE, SEEK_SET); + k = read(fd, buf, BLOCK_SIZE); + if (k != BLOCK_SIZE) { + pexit("get_block couldn't read"); + } +} + +void put_block(n, buf) +block_t n; +char buf[BLOCK_SIZE]; +{ +/* Write a block. */ + + (void) read_and_set(n); + + /* XXX - check other lseeks too. */ + if (lseek(fd, (off_t) n * BLOCK_SIZE, SEEK_SET) == (off_t) -1) { + pexit("put_block couldn't seek"); + } + if (write(fd, buf, BLOCK_SIZE) != BLOCK_SIZE) { + pexit("put_block couldn't write"); + } +} + + +/* Dummy routines to keep source file clean from #ifdefs */ + +void flush() +{ + return; +} + +void cache_init() +{ + return; +} + +#endif diff --git a/commands/simple/passwd.c b/commands/simple/passwd.c new file mode 100755 index 000000000..322647518 --- /dev/null +++ b/commands/simple/passwd.c @@ -0,0 +1,260 @@ +/* passwd - change a passwd Author: Adri Koppes */ + +/* chfn, chsh - change full name, shell Added by: Kees J. Bot */ + +#include <sys/types.h> +#include <fcntl.h> +#include <string.h> +#include <signal.h> +#include <pwd.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <sys/stat.h> +#include <minix/minlib.h> +#include <stdio.h> + +_PROTOTYPE(void report, (char *label)); +_PROTOTYPE(void quit, (int ex_stat)); +_PROTOTYPE(void fatal, (char *label)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(int goodchars, (char *s)); +_PROTOTYPE(int main, (int argc, char **argv)); + +char pw_file[] = "/etc/passwd"; +char sh_file[] = "/etc/shadow"; +char pw_tmp[] = "/etc/ptmp"; +char bad[] = "Permission denied\n"; +char buf[1024]; + +enum action { + PASSWD, CHFN, CHSH +} action = PASSWD; + +char *arg0; + +void report(label) +char *label; +{ + int e = errno; + fprintf(stderr, "%s: ", arg0); + fflush(stderr); + errno = e; + perror(label); +} + +void quit(ex_stat) +int ex_stat; +{ + if (unlink(pw_tmp) < 0 && errno != ENOENT) { + report(pw_tmp); + ex_stat = 1; + } + exit(ex_stat); +} + +void fatal(label) +char *label; +{ + report(label); + quit(1); +} + +void usage() +{ + static char *usages[] = { + "passwd [user]\n", + "chfn [user] fullname\n", + "chsh [user] shell\n" + }; + std_err(usages[(int) action]); + exit(1); +} + +int goodchars(s) +char *s; +{ + int c; + + while ((c = *s++) != 0) { + if (c == ':' || c < ' ' || c >= 127) return(0); + } + return(1); +} + +int main(argc, argv) +int argc; +char *argv[]; +{ + int uid, cn, n; + int fd_pwd, fd_tmp; + FILE *fp_tmp; + time_t salt; + struct passwd *pwd; + char *name, pwname[9], oldpwd[9], newpwd[9], newcrypted[14], sl[2]; + char *argn; + int shadow = 0; + + if ((arg0 = strrchr(argv[0], '/')) != 0) + arg0++; + else + arg0 = argv[0]; + + if (strcmp(arg0, "chfn") == 0) + action = CHFN; + else if (strcmp(arg0, "chsh") == 0) + action = CHSH; + + uid = getuid(); + + n = action == PASSWD ? 1 : 2; + + if (argc != n && argc != n + 1) usage(); + + if (argc == n) { + pwd = getpwuid(uid); + strcpy(pwname, pwd->pw_name); + name = pwname; + } else { + name = argv[1]; + pwd = getpwnam(name); + } + if (pwd == NULL || ((uid != pwd->pw_uid) && uid != 0)) { + std_err(bad); + exit(1); + } + + switch (action) { + case PASSWD: + if (pwd->pw_passwd[0] == '#' && pwd->pw_passwd[1] == '#') { + /* The password is found in the shadow password file. */ + shadow = 1; + strncpy(pwname, pwd->pw_passwd + 2, 8); + pwname[8] = 0; + name = pwname; + setpwfile(sh_file); + if ((pwd= getpwnam(name)) == NULL) { + std_err(bad); + exit(1); + } + printf("Changing the shadow password of %s\n", name); + } else { + printf("Changing the password of %s\n", name); + } + + oldpwd[0] = 0; + if (pwd->pw_passwd[0] != '\0' && uid != 0) { + strcpy(oldpwd, getpass("Old password:")); + if (strcmp(pwd->pw_passwd, crypt(oldpwd, pwd->pw_passwd)) != 0) + { + std_err(bad); + exit(1); + } + } + for (;;) { + strcpy(newpwd, getpass("New password:")); + + if (newpwd[0] == '\0') + std_err("Password cannot be null"); + else if (strcmp(newpwd, getpass("Retype password:")) != 0) + std_err("Passwords don't match"); + else + break; + + std_err(", try again\n"); + } + time(&salt); + sl[0] = (salt & 077) + '.'; + sl[1] = ((salt >> 6) & 077) + '.'; + for (cn = 0; cn < 2; cn++) { + if (sl[cn] > '9') sl[cn] += 7; + if (sl[cn] > 'Z') sl[cn] += 6; + } + strcpy(newcrypted, crypt(newpwd, sl)); + break; + + case CHFN: + case CHSH: + argn = argv[argc - 1]; + + if (strlen(argn) > (action == CHFN ? 80 : 60) || !goodchars(argn)) { + std_err(bad); + exit(1); + } + } + + signal(SIGHUP, SIG_IGN); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGTERM, SIG_IGN); + + umask(0); + n = 10; + while ((fd_tmp = open(pw_tmp, O_RDWR | O_CREAT | O_EXCL, 0400)) < 0) { + if (errno != EEXIST) fatal("Can't create temporary file"); + + if (n-- > 0) { + sleep(2); + } else { + fprintf(stderr, "Password file busy, try again later.\n"); + exit(1); + } + } + + if ((fp_tmp = fdopen(fd_tmp, "w+")) == NULL) fatal(pw_tmp); + + setpwent(); + while ((pwd = getpwent()) != 0) { + if (strcmp(name, pwd->pw_name) == 0) { + switch (action) { + case PASSWD: + pwd->pw_passwd = newcrypted; + break; + case CHFN: + pwd->pw_gecos = argn; + break; + case CHSH: + pwd->pw_shell = argn; + break; + } + } + if (strcmp(pwd->pw_shell, "/bin/sh") == 0 + || strcmp(pwd->pw_shell, "/usr/bin/sh") == 0 + ) + pwd->pw_shell = ""; + + fprintf(fp_tmp, "%s:%s:%s:", + pwd->pw_name, + pwd->pw_passwd, + itoa(pwd->pw_uid) + ); + if (ferror(fp_tmp)) fatal(pw_tmp); + + fprintf(fp_tmp, "%s:%s:%s:%s\n", + itoa(pwd->pw_gid), + pwd->pw_gecos, + pwd->pw_dir, + pwd->pw_shell + ); + if (ferror(fp_tmp)) fatal(pw_tmp); + } + endpwent(); + if (fflush(fp_tmp) == EOF) fatal(pw_tmp); + + if (lseek(fd_tmp, (off_t) 0, SEEK_SET) != 0) + fatal("Can't reread temp file"); + + if ((fd_pwd = open(shadow ? sh_file : pw_file, O_WRONLY | O_TRUNC)) < 0) + fatal("Can't recreate password file"); + + while ((n = read(fd_tmp, buf, sizeof(buf))) != 0) { + if (n < 0 || write(fd_pwd, buf, n) != n) { + report("Error rewriting password file, tell root!"); + exit(1); + } + } + close(fd_tmp); + close(fd_pwd); + quit(0); +} diff --git a/commands/simple/paste.c b/commands/simple/paste.c new file mode 100755 index 000000000..418048117 --- /dev/null +++ b/commands/simple/paste.c @@ -0,0 +1,456 @@ +/* paste - laminate files Author: David Ihnat */ + +/* Paste - a recreation of the Unix(Tm) paste(1) command. + * + * syntax: paste file1 file2 ... paste -dLIST file1 file2 ... paste -s [-dLIST] + * file1 file2 ... + * + * Copyright (C) 1984 by David M. Ihnat + * + * This program is a total rewrite of the Bell Laboratories Unix(Tm) command of + * the same name, as of System V. It contains no proprietary code, and + * therefore may be used without violation of any proprietary agreements + * whatsoever. However, you will notice that the program is copyrighted by + * me. This is to assure the program does *not* fall into the public domain. + * Thus, I may specify just what I am now: This program may be freely copied + * and distributed, provided this notice remains; it may not be sold for + * profit without express written consent of the author. Please note that I + * recreated the behavior of the Unix(Tm) 'paste' command as faithfully as + * possible, with minor exceptions (noted below); however, I haven't run a + * full set of regression * tests. Thus, the user of this program accepts + * full responsibility for any effects or loss; in particular, the author is + * not responsible for any losses, explicit or incidental, that may be + * incurred through use of this program. + * + * The changes to the program, with one exception, are transparent to a user + * familiar with the Unix command of the same name. These changes are: + * + * 1) The '-s' option had a bug in the Unix version when used with multiple + * files. (It would repeat each file in a list, i.e., for + * + * paste -s file1 file2 file3 + * + * it would list + * + * <file1\n><file1\n><file2\n><file1\n><file2\n><file3\n> + * + * I fixed this, and reported the bug to the providers of the command in Unix. + * + * 2) The list of valid escape sequences has been expanded to include \b,\f, + * and \r. (Just because *I* can't imagine why you'd want to use them + * doesn't mean I should keep them from you.) + * + * 3) There is no longer any restriction on line length. + * + * I ask that any bugs (and, if possible, fixes) be reported to me when + * possible. -David Ihnat (312) 784-4544 ihuxx!ignatz + */ + +/* Modified to run under MINIX 1.1 by David O. Tinker (416) 978-3636 + * (utgpu!dtinker) Sept. 19, 1987 + */ + +/* Modified to conform to POSIX 1003.2/Draft10 standard 23rd Sept. 1990 + * Changes: + * - the arguments can be in any order + * - removed the ToUpper function + * by Thomas Brupbacher (tobr@mw.lpc.ethz.ch) + */ + +#include <errno.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +/* I'd love to use enums, but not everyone has them. Portability, y'know. */ +#define NODELIM 1 +#define USAGE 2 +#define BADFILE 3 +#define TOOMANY 4 + +#define TAB '\t' +#define NL '\n' +#define BS '\b' +#define FF '\f' +#define CR '\r' +#define DEL '\177' +#define SPACE ' ' +#define BACKSLASH '\\' + +#define _MAXSZ 512 +#define _MAXFILES 12 +#define CLOSED ((FILE *)-1) +#define ENDLIST ((FILE *)-2) + +char *cmdnam; + +short int sflag; +static char default_delims[] = {TAB}; /* default delimiter string */ +char *delims; /* the pointer to the delimiters */ +int number_of_delims = 1; /* number of delimiters to use */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void docol, (int nfiles, char **fnamptr)); +_PROTOTYPE(void doserial, (int nfiles, char **fnamptr)); +_PROTOTYPE(void delimbuild, (char *strptr)); +_PROTOTYPE(void prerr, (int etype, char *estring)); + +int main(argc, argv) +int argc; +char **argv; +{ + char **arg_ptr; /* used to save argv, needed for docol() etc */ + int num_files = 0; /* Number of filenames specified on cmd line */ + sflag = 0; + delims = default_delims; /* use default delimiters */ + + cmdnam = *argv; + + if (argc >= 2) { + + /* Skip invocation name */ + argv++; + argc--; + + /* Save argv */ + arg_ptr = argv; + /* First, parse input options */ + + while (argc-- > 0) { + if (argv[0][0] == '-' && argv[0][1] != '\0') { + switch (argv[0][1]) { + case 'd': + /* Delimiter character(s) */ + if (*(++argv) == '\0') + prerr(NODELIM, ""); + else + delimbuild(*(argv)); + argc--; + break; + + case 's': sflag++; break; + + default: prerr(USAGE, ""); + } + argv++; + } else { + num_files++; + argv++; + } + } + + /* If there are more than MAX_FILES files on the command + * line, exit with error message. */ + if (num_files > _MAXFILES) prerr(TOOMANY, ""); + + /* If no files specified, simply exit. Otherwise, if not the + * old '-s' option, process all files. If '-s', then process + * files one-at-a-time. */ + + if (!sflag) + docol(num_files, arg_ptr); /* Column paste */ + else + doserial(num_files, arg_ptr); /* Serial paste */ + + exit(0); + } else + prerr(USAGE, ""); + return(0); +} + +void docol(nfiles, fnamptr) +int nfiles; +char **fnamptr; +{ + char iobuff[_MAXSZ]; /* i/o buffer for the fgets */ + short int somedone; /* flag for blank field handling */ + + /* There is a strange case where all files are just ready to be + * closed, or will on this round. In that case, the string of + * delimiters must be preserved. delbuf[1] ->delbuf[MAXFILES+1] + * provides intermediate storage for closed files, if needed; + * delbuf[0] is the current index. + */ + char delbuf[_MAXFILES + 2]; + + FILE *fileptr[_MAXFILES + 1]; + + int filecnt; /* Set to number of files to process */ + register char *delimptr; /* Cycling delimiter pointer */ + int index; /* Working variable */ + int strend; /* End of string in buffer */ + + /* Perform column paste. First, attempt to open all files. (This + * could be expanded to an infinite number of files, but at the + * (considerable) expense of remembering the file and its current + * offset, then opening/reading/closing. The commands' utility + * doesn't warrant the effort; at least, to me...) + */ + + for (filecnt = 0; (nfiles > 0); fnamptr++) { + if ((fnamptr[0][0] == '-') && (fnamptr[0][1] != '\0')) { + if (fnamptr[0][1] == 'd') fnamptr++; + } else { + nfiles--; + if (fnamptr[0][0] == '-') { + fileptr[filecnt++] = stdin; + } else { + fileptr[filecnt] = fopen(fnamptr[0], "r"); + if (fileptr[filecnt++] == NULL) + prerr(BADFILE, *fnamptr); + } + } + } + + fileptr[filecnt] = ENDLIST; /* End of list. */ + + /* Have all files. Now, read a line from each file, and output to + * stdout. Notice that the old 511 character limitation on the line + * length no longer applies, since this program doesn't do the + * buffering. Do this until you go through the loop and don't + * successfully read from any of the files. + */ + for (; filecnt;) { + somedone = 0; /* Blank field handling flag */ + delimptr = delims; /* Start at beginning of delim list */ + delbuf[0] = 0; /* No squirreled delims */ + + for (index = 0; (fileptr[index] != ENDLIST) && filecnt; index++) { + /* Read a line and immediately output. If it's too + * big for the buffer, then dump what was read and go + * back for more. + * + * Otherwise, if it is from the last file, then leave + * the carriage return in place; if not, replace with + * a delimiter (if any) + */ + + strend = 0; /* Set so can easily detect EOF */ + + if (fileptr[index] != CLOSED) + while (fgets(iobuff, (_MAXSZ - 1), + fileptr[index]) != NULL) { + strend = strlen(iobuff);/* Did the buf fill? */ + + if (strend == (_MAXSZ - 1)) { + /* Gosh, what a long line. */ + fputs(iobuff, stdout); + strend = 0; + continue; + } + + /* Ok got whole line in buffer. */ + break; /* Out of loop for this file */ + } + + /* Ended either on an EOF (well, actually NULL + * return-- it *could* be some sort of file error, + * but but if the file was opened successfully, this + * is unlikely. Besides, error checking on streams + * doesn't allow us to decide exactly what went + * wrong, so I'm going to be very Unix-like and + * ignore it!), or a closed file, or a received line. + * If an EOF, close the file and mark it in the list. + * In any case, output the delimiter of choice. + */ + + if (!strend) { + if (fileptr[index] != CLOSED) { + fclose(fileptr[index]); + fileptr[index] = CLOSED; + filecnt--; + } + + /* Is this the end of the whole thing? */ + if ((fileptr[index + 1] == ENDLIST) && !somedone) + continue; /* EXITS */ + + /* Ok, some files not closed this line. Last file? */ + if (fileptr[index + 1] == ENDLIST) { + if (delbuf[0]) { + fputs(&delbuf[1], stdout); + delbuf[0] = 0; + } + putc((int) NL, stdout); + continue; /* Next read of files */ + } else { + /* Closed file; setup delim */ + if (*delimptr != DEL) { + delbuf[0]++; + delbuf[delbuf[0]] = *delimptr++; + delbuf[delbuf[0] + 1] = '\0'; + } else + delimptr++; + } + + /* Reset end of delimiter string if necessary */ + if (*delimptr == '\0') delimptr = delims; + } else { + /* Some data read. */ + somedone++; + + /* Any saved delims? */ + if (delbuf[0]) { + fputs(&delbuf[1], stdout); + delbuf[0] = 0; + } + + /* If last file, last char will be NL. */ + if (fileptr[index + 1] != ENDLIST) { + if (*delimptr == DEL) { + delimptr++; + iobuff[strend - 1] = '\0';/* No delim*/ + } else + iobuff[strend - 1] = *delimptr++; + } + if (*delimptr == '\0') delimptr = delims; + + /* Now dump the buffer */ + fputs(iobuff, stdout); + fflush(stdout); + } + } + } +} + +void doserial(nfiles, fnamptr) +int nfiles; +char **fnamptr; +{ + /* Do serial paste. Simply scarf characters, performing + * one-character buffering to facilitate delim processing. + */ + + register int charnew, charold; + register char *delimptr; + + register FILE *fileptr; + + for (; nfiles != 0; fnamptr++) { + if ((fnamptr[0][0] == '-') && (fnamptr[0][1] != '\0')) { + if (fnamptr[0][1] == 'd') fnamptr++; + } else { + if (fnamptr[0][0] == '-') { + fileptr = stdin; + } else { + fileptr = fopen(*fnamptr, "r"); + + if (fileptr == NULL) prerr(BADFILE, *fnamptr); + } + + /* The file is open; just keep taking characters, + * stashing them in charnew; output charold, + * converting to the appropriate delimiter character + * if needful. After the EOF, simply output + * 'charold' if it's a newline; otherwise, output it + * and then a newline. + */ + + delimptr = delims; /* Set up for delimiter string */ + + if ((charold = getc(fileptr)) == EOF) { + /* Empty file! */ + putc(NL, stdout); + fflush(stdout); + continue; /* Go on to the next file */ + } + + /* Ok, 'charold' is set up. Hit it! */ + + while ((charnew = getc(fileptr)) != EOF) { + /* Ok, process the old character */ + if (charold == NL) { + if (*delimptr != DEL) + putc((int) *delimptr++, stdout); + + /* Reset pointer at end of delimiter string */ + if (*delimptr == '\0') delimptr = delims; + } else + putc(charold, stdout); + + charold = charnew; + } + + /* Ok, hit EOF. Process that last character */ + + putc(charold, stdout); + if ((char) charold != NL) putc(NL, stdout); + fflush(stdout); + nfiles--; + } + } +} + +void delimbuild(strptr) +char *strptr; +{ + /* Process the delimiter string into something that can be used by + * the routines. This involves, primarily, collapsing the backslash + * representations of special characters into their actual values, + * and terminating the string in a manner that the routines can + * recognize. The set of possible backslash characters has been + * expanded beyond that recognized by the vanilla Unix(Tm) version. + */ + + register char *strout; + + delims = strptr; /* delims now points to argv[...] */ + strout = strptr; /* Start at the same place, anyway */ + + while (*strptr) { + if (*strptr != '\\') /* Is it an escape character? */ + *strout++ = *strptr++; /* No, just transfer it */ + else { + strptr++; /* Get past escape character */ + + switch (*strptr) { + case '0': *strout++ = DEL; break; + + case 't': *strout++ = TAB; break; + + case 'n': *strout++ = NL; break; + + case 'b': *strout++ = BS; break; + + case 'f': *strout++ = FF; break; + + case 'r': *strout++ = CR; break; + + case '\\': + *strout++ = BACKSLASH; + break; + + default: *strout++ = *strptr; + } + + strptr++; + } + + } + *strout = '\0'; /* Heaven forfend that we forget this! */ +} + +void prerr(etype, estring) +int etype; +char *estring; +{ + switch (etype) { + case USAGE: + fprintf(stderr, "%s : Usage: %s [-s] [-d <delimiters>] file1 file2 ...\n", cmdnam, cmdnam); + break; + + case NODELIM: + fprintf(stderr, "%s : no delimiters\n", cmdnam); + break; + + case BADFILE: + fprintf(stderr, "%s : %s : cannot open\n", cmdnam, estring); + break; + + case TOOMANY: + fprintf(stderr, "%s : too many files\n", cmdnam); + break; + } + exit(1); +} diff --git a/commands/simple/ping.c b/commands/simple/ping.c new file mode 100755 index 000000000..65e2be4e7 --- /dev/null +++ b/commands/simple/ping.c @@ -0,0 +1,151 @@ +/* +ping.c +*/ + +#define DEBUG 1 + +#include <sys/types.h> +#include <errno.h> +#include <signal.h> +#include <net/gen/netdb.h> +#include <sys/ioctl.h> +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <net/gen/oneCsum.h> +#include <fcntl.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/ip_hdr.h> +#include <net/gen/icmp_hdr.h> +#include <net/gen/ip_io.h> + +#define WRITE_SIZE 30 +char buffer[16*1024]; + +int main(int argc, char *argv[]); + +#if DEBUG +#define where() fprintf(stderr, "%s %d:", __FILE__, __LINE__); +#endif + +#if __STDC__ +#define PROTO(x,y) x y +#else +#define PROTO(x,y) X () +#endif + +PROTO (int main, (int argc, char *argv[]) ); +static PROTO (void sig_hand, (int signal) ); + +main(argc, argv) +int argc; +char *argv[]; +{ + int fd, i; + int result, result1; + nwio_ipopt_t ipopt; + ip_hdr_t *ip_hdr; + int ihl; + icmp_hdr_t *icmp_hdr; + ipaddr_t dst_addr; + struct hostent *hostent; + int length; + + if (argc<2 || argc>3) + { + fprintf(stderr, "Usage: %s hostname [-l length] [-t timeout]\n", + argv[0]); + exit(1); + } + hostent= gethostbyname(argv[1]); + if (!hostent) + { + dst_addr= inet_addr(argv[1]); + if (dst_addr == -1) + { + fprintf(stderr, "%s: unknown host (%s)\n", + argv[0], argv[1]); + exit(1); + } + } + else + dst_addr= *(ipaddr_t *)(hostent->h_addr); + if (argc == 3) + { + length= strtol (argv[2], (char **)0, 0); + if (length< sizeof(icmp_hdr_t) + IP_MIN_HDR_SIZE) + { + fprintf(stderr, "%s: length too small (%s)\n", + argv[0], argv[2]); + exit(1); + } + } + else + length= WRITE_SIZE; + + fd= open ("/dev/ip", O_RDWR); + if (fd<0) + perror("open"), exit(1); + + ipopt.nwio_flags= NWIO_COPY | NWIO_PROTOSPEC; + ipopt.nwio_proto= 1; + + result= ioctl (fd, NWIOSIPOPT, &ipopt); + if (result<0) + perror("ioctl (NWIOSIPOPT)"), exit(1); + + result= ioctl (fd, NWIOGIPOPT, &ipopt); + if (result<0) + perror("ioctl (NWIOGIPOPT)"), exit(1); + + for (i= 0; i< 20; i++) + { + ip_hdr= (ip_hdr_t *)buffer; + ip_hdr->ih_dst= dst_addr; + + icmp_hdr= (icmp_hdr_t *)(buffer+20); + icmp_hdr->ih_type= 8; + icmp_hdr->ih_code= 0; + icmp_hdr->ih_chksum= 0; + icmp_hdr->ih_chksum= ~oneC_sum(0, (u16_t *)icmp_hdr, + WRITE_SIZE-20); + result= write(fd, buffer, length); + if (result<0) + { + perror("write"); + exit(1); + } + if (result != length) + { + where(); + fprintf(stderr, "result= %d\n", result); + exit(1); + } + + alarm(0); + signal (SIGALRM, sig_hand); + alarm(1); + + result= read(fd, buffer, sizeof(buffer)); + if (result>= 0 || errno != EINTR) + break; + } + if (i >= 20) + { + printf("no answer from %s\n", argv[1]); + exit(1); + } + if (result<0) + { + perror ("read"); + exit(1); + } + printf("%s is alive\n", argv[1]); + exit(0); +} + +static void sig_hand(signal) +int signal; +{ +} diff --git a/commands/simple/pr.c b/commands/simple/pr.c new file mode 100755 index 000000000..8f16a5424 --- /dev/null +++ b/commands/simple/pr.c @@ -0,0 +1,504 @@ +/* pr - print files Author: Michiel Huisjes */ + +/* Pr - print files + * + * Author: Michiel Huisjes. + * Modified: Jacob P. Bunschoten. (30 nov 87) + * When "columns" is not given and numbering is on: + * line numbers are correlated with input lines. + * (try pr [-1] -n file ) + * tabs are accounted for. + * When numbering is turned on, width know this. + * automatic line-folding. -f to get the original program. + * backspaces are accounted for. -b to disable this. + * multi-column mode changed. + * header can be given and used. + * format changed may occur between printing of several files: + * pr -l30 file1 -w75 file2 + * + * Modified: Rick Thomas. (Sept 12, 1988) + * added "-M" option to cover functionality of old "-n" option, + * and made "-n" option behavior compatible with system V. + * + * Usage: pr [+page] [-columns] [-h header] [-wwidth] [-llength] [-ntm] [files] + * -t : Do not print the 5 line header and trailer at the page. + * -n : Turn on line numbering. + * -M : Use "Minix" style line numbering -- Each page begins at + * a line number that is an even multiple of the page length. + * Like the listings in Appendix E of the book. + * +page : Start printing at page n. + * -columns : Print files in n-columns. + * -l length: Take the length of the page to be n instead of 66 + * -h header: Take next argument as page header. + * -w width : Take the width of the page to be n instead of default 79 + * -f : do not fold lines. + * + * Modified: Lars Fredriksen (Jan 19, 1990) + * fixed the program so that + * pr -n *.c + * would work. The clobal variable 'width' was decremented + * by NUM_WIDTH, for each file, resulting in width finally + * being so small that nothing was printed. Used the local + * variable 'w' for the width adjustment (in print()) + * + * Modified: Kenneth J. Hendrickson (10 April 1991) + * date in header should be last modification date for files, + * and the current time for stdin. + * + * Modified: Kees J. Bot (5 October 1992) + * Use localtime(3) to get the date, it knows TZ. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <string.h> +#include <time.h> +#include <stdlib.h> +#include <stdio.h> + + +#define DEF_LENGTH 66 +#define DEF_WIDTH 79 +#define NUM_WIDTH 8 +#define TAB_WIDTH 8 /* fixed tab_width */ + +/* Used to compute next (fixed) tabstop */ +#define TO_TAB(x) (( (x) + TAB_WIDTH ) & ~07 ) + +typedef char BOOL; + +#define FALSE 0 +#define TRUE 1 + +/* EAT: eat rest of input line */ +#define EAT(fp) while((c=getc(fp))!='\n' && c!=EOF) + +/* L_BUF: calculate address of pointer to char (string) used in format */ +#define L_BUF(i,j) * (char **) (line_buf + (i + j*length)*sizeof(char *)) + +char *header; +BOOL no_header; +BOOL number = FALSE; +BOOL minix_number = FALSE; +BOOL ext_header_set = FALSE; /* external header given */ +BOOL back_space = TRUE; /* back space correction in line width */ +BOOL dont_fold = FALSE; /* original. If the line does not fit eat it. */ +short columns; +short cwidth; +short start_page = 1; +short width = DEF_WIDTH; +short length = DEF_LENGTH; +short linenr; +char *line_buf; /* used in format for multi-column output */ + +char output[1024]; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(static char *myalloc, (size_t size)); +_PROTOTYPE(char skip_page, (int lines, int width, FILE * filep)); +_PROTOTYPE(void format, (FILE * filep)); +_PROTOTYPE(void print_page, (int pagenr, int maxcol)); +_PROTOTYPE(void print, (FILE * filep)); +_PROTOTYPE(void out_header, (int page)); +_PROTOTYPE(void print_time, (time_t t)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + FILE *file; + char *ptr; + int index = 1; /* index is one ahead of argc */ + int line, col; + + setbuf(stdout, output); + do { + if (argc == index) /* No arguments (left) */ + goto pr_files; + + ptr = argv[index++]; + if (*ptr == '+') { + start_page = atoi(++ptr); + continue; + } + if (*ptr != '-') { /* no flags */ + index--; + goto pr_files; + } + if (*++ptr >= '0' && *ptr <= '9') { + columns = atoi(ptr); + if (columns <= 0) columns = 1; + continue; /* Fetch next flag */ + } + while (*ptr) switch (*ptr++) { + case 't': no_header = TRUE; break; + case 'n': + number = TRUE; + minix_number = FALSE; + break; + case 'M': + number = TRUE; + minix_number = TRUE; + break; + case 'h': + header = argv[index++]; + ext_header_set = TRUE; + break; + case 'w': + if ((width = atoi(ptr)) <= 0) width = DEF_WIDTH; + *ptr = '\0'; + break; + case 'l': + if ((length = atoi(ptr)) <= 0) length = DEF_LENGTH; + *ptr = '\0'; + break; + case 'b': /* back_space correction off */ + back_space = FALSE; + break; + case 'f': /* do not fold lines */ + dont_fold = TRUE; + break; + default: + fprintf(stderr, "Usage: %s [+page] [-columns] [-h header] [-w<width>] [-l<length>] [-nMt] [files]\n", argv[0]); + exit(1); + } + continue; /* Scan for next flags */ + + + /* ============== flags are read. Print the file(s) ========= */ + +pr_files: + + if (!no_header) length -= 10; + + if (columns) { + cwidth = width / columns + 1; + if (columns > width) { + fprintf(stderr, "Too many columns for page width.\n"); + exit(1); + } + + /* Allocate piece of mem to hold some pointers */ + line_buf = myalloc(length * columns * sizeof(char *)); + } + for (line = 0; line < length; line++) + for (col = 0; col < columns; col++) + L_BUF(line, col) = NULL; + + if (length <= 0) { + fprintf(stderr, "Minimal length should be %d\n", no_header ? + 1 : 11); + exit(1); + } + while (index <= argc) { /* print all files, including stdin */ + if (index < argc && (*argv[index] == '-' || *argv[index] == '+')) + break; /* Format change */ + + if (argc == index) { /* no file specified, so stdin */ + if (!ext_header_set) header = ""; + file = stdin; + } else { + if ((file = fopen(argv[index], "r")) == (FILE *) 0) { + fprintf(stderr, "Cannot open %s\n", argv[index++]); + continue; + } + if (!ext_header_set) header = argv[index]; + } + if (columns) + format(file); + else + print(file); + fclose(file); + if (++index >= argc) + break; /* all files (including stdin) done */ + } + if (index >= argc) break; + /* When control comes here. format changes are to be done. + * reinitialize some variables */ + if (!no_header) length += 10; + + start_page = 1; + ext_header_set = FALSE; + if (columns) free(line_buf); + } while (index <= argc); /* "pr -l60" should work too */ + + (void) fflush(stdout); + return(0); +} + +char skip_page(lines, width, filep) +int lines, width; +FILE *filep; +{ + short c; + int char_cnt; + int w; + + do { + w = width; + if (number) /* first lines are shorter */ + if (!columns || /* called from print(file) */ + !(lines % columns)) /* called from format(file) */ + w -= NUM_WIDTH; + + char_cnt = 0; + while ((c = getc(filep)) != '\n' && c != EOF && char_cnt < w) { + /* Calculate if this line is longer than "width (w)" + * characters */ + if (c == '\b' && back_space) { + if (--char_cnt < 0) char_cnt = 0; + } else if (c == '\t') + char_cnt = TO_TAB(char_cnt); + else + char_cnt++; + } + if (dont_fold && c != '\n' && c != EOF) EAT(filep); + lines--; + if (c == '\n') linenr++; + } while (lines > 0 && c != EOF); + + return c; /* last char read */ +} + +void format(filep) +FILE *filep; +{ + char buf[512]; + short c = '\0'; + short index, lines, i; + short page_number = 0; + short maxcol = columns; + short wdth; + short line, col; + + do { + /* Check printing of page */ + page_number++; + + if (page_number < start_page && c != EOF) { + c = (char) skip_page(columns * length, cwidth, filep); + continue; + } + if (c == EOF) return; + + lines = columns * length; + for (line = 0; line < length; line++) + for (col = 0; col < columns; col++) { + if (L_BUF(line, col) != NULL) + free(L_BUF(line, col)); + L_BUF(line, col) = (char *) NULL; + } + line = 0; + col = 0; + do { + index = 0; + wdth = cwidth - 1; + if (number && !col) /* need room for numbers */ + wdth -= NUM_WIDTH; + + /* Intermidiate colums are shortened by 1 char */ + /* Last column not */ + if (col + 1 == columns) wdth++; + for (i = 0; i < wdth - 1; i++) { + c = getc(filep); + if (c == '\n' || c == EOF) break; + + if (c == '\b' && back_space) { + buf[index++] = '\b'; + if (--i < 0) { /* just in case ... */ + i = 0; + index = 0; + } + } else if (c == '\t') { + int cnt, max; + + max = TO_TAB(i); + for (cnt = i; cnt < max; cnt++) + buf[index++] = ' '; + i = max - 1; + } else + buf[index++] = (char) c; + } + buf[index++] = '\0'; + /* Collected enough chars (or eoln, or EOF) */ + + /* First char is EOF */ + if (i == 0 && lines == columns * length && c == EOF) return; + + /* Alloc mem to hold this (sub) string */ + L_BUF(line, col) = myalloc(index * sizeof(char)); + strcpy(L_BUF(line, col), buf); + + line++; + line %= length; + if (line == 0) { + col++; + col %= columns; + } + if (dont_fold && c != '\n' && c != EOF) EAT(filep); + lines--; /* line ready for output */ + if (c == EOF) { + maxcol = columns - lines / length; + } + } while (c != EOF && lines); + print_page(page_number, maxcol); + } while (c != EOF); +} + +void print_page(pagenr, maxcol) +short pagenr, maxcol; +{ + short pad, i, j; + short width; + char *p; + + if (minix_number) + linenr = (pagenr - 1) * length + 1; + else + linenr = 1; + + if (!no_header) out_header(pagenr); + + for (i = 0; i < length; i++) { + for (j = 0; j < maxcol; j++) { + width = cwidth; + if (number && j == 0) { /* first columns */ + printf("%7.7d ", linenr++); /* 7 == NUM_WIDTH-1 */ + width -= NUM_WIDTH; + } + pad = 0; + if (p = (char *) L_BUF(i, j)) + for (; pad < width - 1 && *p; pad++) putchar(*p++); + if (j < maxcol - 1) while (pad++ < width - 1) + putchar(' '); + } + putchar('\n'); + } + if (!no_header) printf("\n\n\n\n\n"); +} + +void print(filep) +FILE *filep; +{ + short c = '\0'; + short page_number = 0; + short lines; + short cnt; + short w = width; + BOOL pr_number = TRUE; /* only real lines are numbered, not folded + * parts */ + + linenr = 1; + if (number) w -= NUM_WIDTH; + + do { + /* Check printing of page */ + page_number++; + + if (page_number < start_page && c != EOF) { + pr_number = FALSE; + c = skip_page(length, w, filep); + if (c == '\n') pr_number = TRUE; + continue; + } + if (c == EOF) return; + + if (minix_number) linenr = (page_number - 1) * length + 1; + + if (page_number == start_page) c = getc(filep); + + /* Print the page */ + lines = length; + while (lines && c != EOF) { + if (lines == length && !no_header) out_header(page_number); + if (number) + if (pr_number) + printf("%7.7d ", linenr++); /* 7 == NUM_WIDTH-1 */ + else + printf("%7c ", ' '); /* 7 == NUM_WIDTH-1 */ + pr_number = FALSE; + cnt = 0; + while (c != '\n' && c != EOF && cnt < w) { + if (c == '\t') { + int i, max; + max = TO_TAB(cnt); + for (i = cnt; i < max; i++) putchar(' '); + cnt = max - 1; + } else if (c == '\b' && back_space) { + putchar('\b'); + cnt--; + } else + putchar(c); + c = getc(filep); + cnt++; + } + putchar('\n'); + if (dont_fold && c != '\n' && c != EOF) EAT(filep); + lines--; + if (c == '\n') { + c = getc(filep); + pr_number = TRUE; + } + } + if (lines == length) /* We never printed anything on this + * page -- */ + return; /* even the header, so dont try to fill it up */ + if (!no_header) /* print the trailer -- 5 blank lines */ + printf("\n\n\n\n\n"); + } while (c != EOF); + + /* Fill last page */ + if (page_number >= start_page) { + while (lines--) putchar('\n'); + } +} + +static char *myalloc(size) +size_t size; /* How many bytes */ +{ + void *ptr; + + ptr = malloc(size); + if (ptr == NULL) { + fprintf(stderr, "malloc returned NULL\n"); + exit(1); + } + return(char *) ptr; +} + +void out_header(page) +short page; +{ + time_t t; + struct stat buf; + + if (strlen(header)) { + stat(header, &buf); /* use last modify time for file */ + t = buf.st_mtime; + } else + (void) time(&t); /* use current time for stdin */ + print_time(t); + printf(" %s Page %d\n\n\n", header, page); +} + +char *moname[] = { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" +}; + +/* Print the date. */ +void print_time(t) +time_t t; +{ + struct tm *tm; + + tm = localtime(&t); + + printf("\n\n%s %2d %2d:%02d %d", + moname[tm->tm_mon], + tm->tm_mday, + tm->tm_hour, + tm->tm_min, + 1900 + tm->tm_year + ); +} diff --git a/commands/simple/pr_routes.c b/commands/simple/pr_routes.c new file mode 100755 index 000000000..1e24e3fd7 --- /dev/null +++ b/commands/simple/pr_routes.c @@ -0,0 +1,307 @@ +/* +vmd/cmd/simple/pr_routes.c +*/ + +#define _POSIX_C_SOURCE 2 + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <net/netlib.h> +#include <net/hton.h> +#include <net/gen/in.h> +#include <net/gen/ip_io.h> +#include <net/gen/route.h> +#include <net/gen/netdb.h> +#include <net/gen/inet.h> + +#define N_IF 64 /* More than enough? */ + +char *prog_name; +int all_devices; +char *ifname; +ipaddr_t iftab[N_IF]; + +static void print_header(void); +static void print_route(nwio_route_t *route); +static void fill_iftab(void); +static char *get_ifname(ipaddr_t addr); +static void fatal(char *fmt, ...); +static void usage(void); + +int main(int argc, char *argv[]) +{ + int nr_routes, i; + nwio_route_t route; + nwio_ipconf_t ip_conf; + unsigned long ioctl_cmd; + int ip_fd; + int result; + int c; + char *ip_device, *cp; + int a_flag, i_flag, o_flag; + char *I_arg; + + prog_name= argv[0]; + + a_flag= 0; + i_flag= 0; + o_flag= 0; + I_arg= NULL; + while ((c =getopt(argc, argv, "?aI:io")) != -1) + { + switch(c) + { + case '?': + usage(); + case 'a': + if (a_flag) + usage(); + a_flag= 1; + break; + case 'I': + if (I_arg) + usage(); + I_arg= optarg; + break; + case 'i': + if (i_flag || o_flag) + usage(); + i_flag= 1; + break; + case 'o': + if (i_flag || o_flag) + usage(); + o_flag= 1; + break; + default: + fprintf(stderr, "%s: getopt failed: '%c'\n", + prog_name, c); + exit(1); + } + } + if (optind != argc) + usage(); + + ip_device= I_arg; + all_devices= a_flag; + + if (i_flag) + ioctl_cmd= NWIOGIPIROUTE; + else + ioctl_cmd= NWIOGIPOROUTE; + + if (ip_device == NULL) + ip_device= getenv("IP_DEVICE"); + ifname= ip_device; + if (ip_device == NULL) + ip_device= IP_DEVICE; + + ip_fd= open(ip_device, O_RDONLY); + if (ip_fd == -1) + { + fprintf(stderr, "%s: unable to open %s: %s\n", prog_name, + ip_device, strerror(errno)); + exit(1); + } + + if (!all_devices && ifname) + { + cp= strrchr(ip_device, '/'); + if (cp) + ifname= cp+1; + } + else + { + ifname= NULL; + fill_iftab(); + } + + result= ioctl(ip_fd, NWIOGIPCONF, &ip_conf); + if (result == -1) + { + fprintf(stderr, "%s: unable to NWIOIPGCONF: %s\n", + prog_name, strerror(errno)); + exit(1); + } + + route.nwr_ent_no= 0; + result= ioctl(ip_fd, ioctl_cmd, &route); + if (result == -1) + { + fprintf(stderr, "%s: unable to NWIOGIPxROUTE: %s\n", + prog_name, strerror(errno)); + exit(1); + } + print_header(); + nr_routes= route.nwr_ent_count; + for (i= 0; i<nr_routes; i++) + { + route.nwr_ent_no= i; + result= ioctl(ip_fd, ioctl_cmd, &route); + if (result == -1) + { + fprintf(stderr, "%s: unable to NWIOGIPxROUTE: %s\n", + prog_name, strerror(errno)); + exit(1); + } + if (all_devices || route.nwr_ifaddr == ip_conf.nwic_ipaddr) + print_route(&route); + } + exit(0); +} + +int ent_width= 5; +int if_width= 4; +int dest_width= 18; +int gateway_width= 15; +int dist_width= 4; +int pref_width= 5; +int mtu_width= 4; + +static void print_header(void) +{ + printf("%*s ", ent_width, "ent #"); + printf("%*s ", if_width, "if"); + printf("%*s ", dest_width, "dest"); + printf("%*s ", gateway_width, "gateway"); + printf("%*s ", dist_width, "dist"); + printf("%*s ", pref_width, "pref"); + printf("%*s ", mtu_width, "mtu"); + printf("%s", "flags"); + printf("\n"); +} + +static char *cidr2a(ipaddr_t addr, ipaddr_t mask) +{ + ipaddr_t testmask= 0xFFFFFFFF; + int n; + static char result[sizeof("255.255.255.255/255.255.255.255")]; + + for (n= 32; n >= 0; n--) + { + if (mask == htonl(testmask)) + break; + testmask= (testmask << 1) & 0xFFFFFFFF; + } + + sprintf(result, "%s/%-2d", inet_ntoa(addr), n); + if (n == -1) + strcpy(strchr(result, '/')+1, inet_ntoa(mask)); + return result; +} + +static void print_route(nwio_route_t *route) +{ + if (!(route->nwr_flags & NWRF_INUSE)) + return; + + printf("%*lu ", ent_width, (unsigned long) route->nwr_ent_no); + printf("%*s ", if_width, + ifname ? ifname : get_ifname(route->nwr_ifaddr)); + printf("%*s ", dest_width, cidr2a(route->nwr_dest, route->nwr_netmask)); + printf("%*s ", gateway_width, inet_ntoa(route->nwr_gateway)); + printf("%*lu ", dist_width, (unsigned long) route->nwr_dist); + printf("%*ld ", pref_width, (long) route->nwr_pref); + printf("%*lu", mtu_width, (long) route->nwr_mtu); + if (route->nwr_flags & NWRF_STATIC) + printf(" static"); + if (route->nwr_flags & NWRF_UNREACHABLE) + printf(" dead"); + printf("\n"); +} + +static void fill_iftab(void) +{ + int i, j, r, fd; + nwio_ipconf_t ip_conf; + char dev_name[12]; /* /dev/ipXXXX */ + + for (i= 0; i<N_IF; i++) + { + iftab[i]= 0; + + sprintf(dev_name, "/dev/ip%d", i); + fd= open(dev_name, O_RDWR); + if (fd == -1) + { + if (errno == EACCES || errno == ENOENT || errno == ENXIO) + continue; + fatal("unable to open '%s': %s", + dev_name, strerror(errno)); + } + fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK); + r= ioctl(fd, NWIOGIPCONF, &ip_conf); + if (r == -1 && errno == EAGAIN) + { + /* interface is down */ + close(fd); + continue; + } + if (r == -1) + { + fatal("NWIOGIPCONF failed on %s: %s", + dev_name, strerror(errno)); + } + + iftab[i]= ip_conf.nwic_ipaddr; + close(fd); + + for (j= 0; j<i; j++) + { + if (iftab[j] == iftab[i]) + { + fatal("duplicate address in ip%d and ip%d: %s", + i, j, inet_ntoa(iftab[i])); + } + } + + } +} + +static char *get_ifname(ipaddr_t addr) +{ + static char name[7]; /* ipXXXX */ + + int i; + + for (i= 0; i<N_IF; i++) + { + if (iftab[i] != addr) + continue; + sprintf(name, "ip%d", i); + return name; + } + + return inet_ntoa(addr); +} + +static void fatal(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "%s: ", prog_name); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + exit(1); +} + +static void usage(void) +{ + fprintf(stderr, "Usage: %s [-i|-o] [ -a ] [ -I <ip-device> ]\n", + prog_name); + exit(1); +} + +/* + * $PchId: pr_routes.c,v 1.8 2002/04/11 10:58:58 philip Exp $ + */ diff --git a/commands/simple/prep.c b/commands/simple/prep.c new file mode 100755 index 000000000..b596be092 --- /dev/null +++ b/commands/simple/prep.c @@ -0,0 +1,136 @@ +/* prep - prepare file for statistics Author: Andy Tanenbaum */ + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> + +#define TROFF_CHAR '.' /* troff commands begin with this char */ +#define EOL '\n' /* end of line char */ +#define APOSTROPHE 047 /* single quote */ +#define BACKSLASH '\\' /* troff code */ + +int lfread; /* set when last char read was lf */ +int lfwritten = 1; /* set when last char written was lf */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void skipline, (void)); +_PROTOTYPE(int backslash, (void)); +_PROTOTYPE(void usage, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + + int c; + + if (argc > 2) usage(); + if (argc == 2) { + if (freopen(argv[1], "r", stdin) == NULL) { + printf("prep: cannot open %s\n", argv[1]); + exit(1); + } + } + while ((c = getchar()) != EOF) { + /* Lines beginning with "." are troff commands -- skip them. */ + if (lfread && c == TROFF_CHAR) { + skipline(); + continue; + } + while (c == BACKSLASH) c = backslash(); /* eat troff stuff */ + + if (isupper(c)) { + putchar(tolower(c)); + lfwritten = 0; + lfread = 0; + continue; + } + if (islower(c)) { + putchar(c); + lfwritten = 0; + lfread = 0; + continue; + } + if (c == APOSTROPHE) { + putchar(c); + lfwritten = 0; + lfread = 0; + continue; + } + lfread = (c == EOL ? 1 : 0); + if (lfwritten) continue; + putchar(EOL); + lfwritten = 1; + } + return(0); +} + + +void skipline() +{ + char c; + + while ((c = getchar()) != EOL); +} + + +int backslash() +{ +/* A backslash has been seen. Eat troff stuff. */ + + int c, c1, c2; + + c = getchar(); + switch (c) { + case 'f': + c = getchar(); + c = getchar(); + return(c); + + case 's': /* \s7 or \s14 */ + c = getchar(); + c = getchar(); + if (isdigit(c)) c = getchar(); + return(c); + + case 'n': /* \na or \n(xx */ + c = getchar(); + if (c == '(') { + c = getchar(); + c = getchar(); + } + c = getchar(); + return(c); + + case '*': /* / * (XX */ + c = getchar(); + if (c == '(') { + c = getchar(); + c = getchar(); + c = getchar(); + return(c); + } + + case '(': /* troff 4-character escape sequence */ + c1 = getchar(); + c2 = getchar(); + if (c1 == 'e' && c2 == 'm') return(' '); + if (c1 == 'e' && c2 == 'n') return(' '); + c = getchar(); + return(c); + + case '%': /* soft hyphen: \% */ + c = getchar(); + return(c); + + default: + return(c); + + } +} + +void usage() +{ + printf("Usage: prep [file]\n"); + exit(1); +} diff --git a/commands/simple/printf.c b/commands/simple/printf.c new file mode 100755 index 000000000..4e797b6ef --- /dev/null +++ b/commands/simple/printf.c @@ -0,0 +1,471 @@ +#if ever +static char sccsid[] = "@(#)printf.c (U of Maryland) FLB 6-Jan-1987"; +static char RCSid[] = "@(#)$Header$"; +#endif + +/* + * Printf - Duplicate the C library routine of the same name, but from + * the shell command level. + * + * Fred Blonder <fred@Mimsy.umd.edu> + * + * To Compile: + % cc -s -O printf.c -o printf + * + * $Log$ + * Revision 1.1 2005/04/21 14:55:31 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:30 beng + * Initial import of minix 2.0.4 + * + * Revision 1.4 87/01/29 20:52:30 fred + * Re-installed backslash-notation conversion for string & char arguments. + * + * Revision 1.3 87/01/29 20:44:23 fred + * Converted to portable algorithm. + * Added Roman format for integers. + * 29-Jan-87 FLB + * + * Revision 1.2 87/01/09 19:10:57 fred + * Fixed bug in argument-count error-checking. + * Changed backslash escapes within strings to correspond to ANSII C + * draft standard. (9-Jan-87 FLB) + * + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#define EX_OK 0 +#define EX_USAGE 1 + +int ctrl(char *s); + +#define atoi(a) strtoul((a), NULL, 0) + +/****************************************************************************/ + +int main(int argc, char *argv[]) +{ +register char *cp, *conv_spec, **argp, **ep; +char *ctor(int x); + +if (argc < 2) { + fprintf(stderr, + "printf: Usage: printf <format-string> [ arg1 . . . ]\n"); + exit(EX_USAGE); + } + +argp = &argv[2]; /* Point at first arg (if any) beyond format string. */ +ep = &argv[argc]; /* Point beyond last arg. */ + +ctrl(argv[1]); /* Change backslash notation to control chars in fmt string. */ + +/* Scan format string for conversion specifications, and do appropriate + conversion on the corresponding argument. */ +for (cp = argv[1]; *cp; cp++) { +register int dynamic_count; + + /* Look for next conversion spec. */ + while (*cp && *cp != '%') { + putchar(*cp++); + } + + if (!*cp) /* End of format string */ + break; + + dynamic_count = 0; /* Begin counting dynamic field width specs. */ + conv_spec = cp++; /* Remember where this conversion begins. */ + + for (;*cp; cp++) { /* Scan until conversion character. */ + char conv_buf[BUFSIZ]; /* Save conversion string here. */ + register int conv_len; /* Length of ``conv_buf''. */ + + switch (*cp) { /* Field-width spec.: Keep scanning. */ + case '.': case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': case '8': + case '9': + continue; + + case '*': /* Dynamic field-width spec */ + dynamic_count++; + continue; + + case 's': /* String */ + if (&argp[dynamic_count] >= ep) { + fprintf(stderr, + "printf: Not enough args for format.\n" + ); + exit(EX_USAGE); + } + + (void) strncpy(conv_buf, conv_spec, + conv_len = cp - conv_spec + 1); + conv_buf[conv_len] = '\0'; + + switch (dynamic_count) { + case 0: + ctrl(*argp); + printf(conv_buf, *argp++); + break; + + case 1: + { + register int a1; + + a1 = atoi(*argp++); + ctrl(*argp); + printf(conv_buf, a1, *argp++); + } + break; + + case 2: + { + register int a1, a2; + + a1 = atoi(*argp++); + a2 = atoi(*argp++); + ctrl(*argp); + printf(conv_buf, a1, a2, *argp++); + } + break; + + } + goto out; + + case 'c': /* Char */ + if (&argp[dynamic_count] >= ep) { + fprintf(stderr, + "printf: Not enough args for format.\n" + ); + exit(EX_USAGE); + } + + (void) strncpy(conv_buf, conv_spec, + conv_len = cp - conv_spec + 1); + conv_buf[conv_len] = '\0'; + + switch (dynamic_count) { + case 0: + ctrl(*argp); + printf(conv_buf, **argp++); + break; + + case 1: + { + register int a1; + + a1 = atoi(*argp++); + ctrl(*argp); + printf(conv_buf, a1, **argp++); + } + break; + + case 2: + { + register int a1, a2; + + a1 = atoi(*argp++); + a2 = atoi(*argp++); + ctrl(*argp); + printf(conv_buf, a1, a2, **argp++); + } + break; + } + goto out; + + case 'd': /* Integer */ + case 'o': + case 'x': + case 'X': + case 'u': + if (&argp[dynamic_count] >= ep) { + fprintf(stderr, + "printf: Not enough args for format.\n" + ); + exit(EX_USAGE); + } + + (void) strncpy(conv_buf, conv_spec, + conv_len = cp - conv_spec + 1); + conv_buf[conv_len] = '\0'; + + switch (dynamic_count) { + case 0: + printf(conv_buf, atoi(*argp++)); + break; + + case 1: + { + register int a1; + + a1 = atoi(*argp++); + printf(conv_buf, a1, atoi(*argp++)); + } + break; + + case 2: + { + register int a1, a2; + + a1 = atoi(*argp++); + a2 = atoi(*argp++); + printf(conv_buf, a1, a2, atoi(*argp++)); + } + break; + + } + goto out; + + case 'f': /* Real */ + case 'e': + case 'g': + if (&argp[dynamic_count] >= ep) { + fprintf(stderr, + "printf: Not enough args for format.\n" + ); + exit(EX_USAGE); + } + + (void) strncpy(conv_buf, conv_spec, + conv_len = cp - conv_spec + 1); + conv_buf[conv_len] = '\0'; + + switch (dynamic_count) { + case 0: + printf(conv_buf, atof(*argp++)); + break; + + case 1: + { + register int a1; + + a1 = atoi(*argp++); + printf(conv_buf, a1, atof(*argp++)); + } + break; + + case 2: + { + register int a1, a2; + + a1 = atoi(*argp++); + a2 = atoi(*argp++); + printf(conv_buf, a1, a2, atof(*argp++)); + } + break; + + } + goto out; + + case 'r': /* Roman (Well, why not?) */ + if (&argp[dynamic_count] >= ep) { + fprintf(stderr, + "printf: Not enough args for format.\n" + ); + exit(EX_USAGE); + } + + (void) strncpy(conv_buf, conv_spec, + conv_len = cp - conv_spec + 1); + conv_buf[conv_len] = '\0'; + conv_buf[conv_len - 1] = 's'; + + switch (dynamic_count) { + case 0: + printf(conv_buf, + ctor(atoi(*argp++))); + break; + + case 1: + { + register int a1; + + a1 = atoi(*argp++); + printf(conv_buf, a1, + ctor(atoi(*argp++))); + } + break; + + case 2: + { + register int a1, a2; + + a1 = atoi(*argp++); + a2 = atoi(*argp++); + printf(conv_buf, a1, a2, + ctor(atoi(*argp++))); + } + break; + + } + goto out; + + case '%': /* Boring */ + putchar('%'); + break; + + default: /* Probably an error, but let user + have his way. */ + continue; + } + } + out: ; + } + +exit(EX_OK); +} + +/****************************************************************************/ + +/* Convert backslash notation to control characters, in place. */ + +int ctrl(char *s) +{ +register char *op; +static int val; + +for (op = s; *s; s++) + if (*s == '\\') + switch (*++s) { + case '\0': /* End-of-string: user goofed */ + goto out; + + case '\\': /* Backslash */ + *op++ = '\\'; + break; + + case 'n': /* newline */ + *op++ = '\n'; + break; + + case 't': /* horizontal tab */ + *op++ = '\t'; + break; + + case 'r': /* carriage-return */ + *op++ = '\r'; + break; + + case 'f': /* form-feed */ + *op++ = '\f'; + break; + + case 'b': /* backspace */ + *op++ = '\b'; + break; + + case 'v': /* vertical tab */ + *op++ = '\13'; + break; + + case 'a': /* WARNING! DANGER! DANGER! DANGER! */ + *op++ = '\7'; + break; + + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + { /* octal constant */ + register int digits; + + val = 0; + (void) sscanf(s, "%3o", &val); + *op++ = val; + for (digits = 3; s[1] && + strchr("01234567", s[1]) + && --digits > 0; + s++); + } + break; + + case 'x': /* hex constant */ + case 'X': + s++; + { + register int digits; + + val = 0; + (void) sscanf(s, "%3x", &val); + *op++ = val; + for (digits = 3; *s && s[1] && + strchr("0123456789abcdefABCDEF", + s[1]) + && --digits > 0; + s++); + } + break; + + } + else + *op++ = *s; + +out: + +*op = '\0'; +} + +/****************************************************************************/ + +/* Convert integer to Roman Numerals. (Have have you survived without it?) */ + +struct roman { + unsigned r_mag; + char r_units, r_fives; + } roman[] = { + { 1000, 'M', '\0', }, + { 100, 'C', 'D', }, + { 10, 'X', 'L', }, + { 1, 'I', 'V', }, + }; + +char *ctor(int x) +{ +register struct roman *mp; +static char buf[BUFSIZ]; +register char *cp = buf; + +/* I've never actually seen a roman numeral with a minus-sign. + Probably ought to print out some appropriate latin phrase instead. */ +if (x < 0) { + *cp++ = '-'; + x = -x; + } + +for (mp = roman; x; mp++) { + register unsigned units; + + units = x / mp->r_mag; + x = x % mp->r_mag; + + if (cp > &buf[BUFSIZ-2]) + return "???"; + + if (units == 9 && mp > roman) { /* Do inverse notation: Eg: ``IX''. */ + *cp++ = mp->r_units; + *cp++ = mp[-1].r_units; + } + else if (units == 4 && mp->r_fives) { + /* Inverse notation for half-decades: Eg: ``IV'' */ + *cp++ = mp->r_units; + *cp++ = mp->r_fives; + } + else { /* Additive notation */ + if (units >= 5 && mp->r_fives) { + *cp++ = mp->r_fives; + units -= 5; + } + while (units--) { + *cp++ = mp->r_units; + if (cp > &buf[BUFSIZ-5]) + return "???"; + } + } + } + +*cp = '\0'; + +return buf; +} + +/****************************************************************************/ diff --git a/commands/simple/printroot.c b/commands/simple/printroot.c new file mode 100755 index 000000000..d9ad00c8f --- /dev/null +++ b/commands/simple/printroot.c @@ -0,0 +1,85 @@ +/* printroot - print root device on stdout Author: Bruce Evans */ + +/* This program figures out what the root device is by doing a stat on it, and + * then searching /dev until it finds an entry with the same device number. + * A typical use (probably the only use) is in /etc/rc for initializing + * /etc/mtab, as follows: + * + * /usr/bin/printroot >/etc/mtab + * + * 9 Dec 1989 - clean up for 1.5 - full prototypes (BDE) + * 15 Oct 1989 - avoid ACK cc bugs (BDE): + * - sizeof "foo" is 2 (from wrong type char *) instead of 4 + * - char foo[10] = "bar"; allocates 4 bytes instead of 10 + * 1 Oct 1989 - Minor changes by Andy Tanenbaum + * 5 Oct 1992 - Use readdir (kjb) + * 26 Nov 1994 - Flag -r: print just the root device (kjb) + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <minix/minlib.h> +#include <dirent.h> + +static char DEV_PATH[] = "/dev/"; /* #define would step on sizeof bug */ +static char MESSAGE[] = " / "; /* ditto */ +#define UNKNOWN_DEV "/dev/unknown" +#define ROOT "/" +int rflag; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void done, (char *name, int status)); + +int main(argc, argv) +int argc; +char **argv; +{ + DIR *dp; + struct dirent *entry; + struct stat filestat, rootstat; + static char namebuf[sizeof DEV_PATH + NAME_MAX]; + + rflag = (argc > 1 && strcmp(argv[1], "-r") == 0); + + if (stat(ROOT, &rootstat) == 0 && (dp = opendir(DEV_PATH)) != (DIR *) NULL) { + while ((entry = readdir(dp)) != (struct dirent *) NULL) { + strcpy(namebuf, DEV_PATH); + strcat(namebuf, entry->d_name); + if (stat(namebuf, &filestat) != 0) continue; + if ((filestat.st_mode & S_IFMT) != S_IFBLK) continue; + if (filestat.st_rdev != rootstat.st_dev) continue; + done(namebuf, 0); + } + } + done(UNKNOWN_DEV, 1); + return(0); /* not reached */ +} + +void done(name, status) +char *name; +int status; +{ + int v; + + write(1, name, strlen(name)); + if (rflag) { + write(1, "\n", 1); + exit(status); + } + write(1, MESSAGE, sizeof MESSAGE - 1); + v = fsversion(name, "printroot"); /* determine file system version */ + if (v == 1) + write(1, "1 rw\n", 5); + else if (v == 2) + write(1, "2 rw\n", 5); + else if (v == 3) + write(1, "3 rw\n", 5); + else + write(1, "0 rw\n", 5); + exit(status); +} diff --git a/commands/simple/proto.c b/commands/simple/proto.c new file mode 100755 index 000000000..41a364092 --- /dev/null +++ b/commands/simple/proto.c @@ -0,0 +1,688 @@ +/* proto - Generate ANSI C prototypes. Author: Eric R. Smith */ + +/* Program to extract function declarations from C source code + * Written by Eric R. Smith and placed in the public domain + * Thanks are due to Jwahar R. Bammi for fixing several bugs + * And providing the Unix makefiles. + */ +#define EXIT_SUCCESS 0 +#define EXIT_FAILURE 1 + +#include <string.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> + +#define ISCSYM(x) ((x) > 0 && (isalnum(x) || (x) == '_' )) +#define ABORTED ( (Word *) -1 ) +#define MAXPARAM 20 /* max. number of parameters to a function */ + +typedef struct word { + struct word *next; + char string[1]; +} Word; + +int inquote = 0; /* in a quote? */ +int newline_seen = 1; /* are we at the start of a line */ +long linenum = 1L; /* line number in current file */ +long endline = 0L; /* the last line before the { of a f'n */ +long symline = 0L; /* Line that symbol was on, set by getsym() */ +int dostatic = 0; /* do static functions? */ +int donum = 0; /* print line numbers? */ +int dohead = 1; /* do file headers? */ +int docond = 1; /* conditionalize for non-ANSI compilers? */ +int dodiff = 0; /* Output a diff file to prototype original */ +int doold = 0; /* do old style: P() */ +int glastc = ' '; /* last char. seen by getsym() */ +Word *endlist; /* Parentheses after the parameters */ +char *progname; /* name of program (for error messages) */ + + +_PROTOTYPE(Word * word_alloc, (char *s)); +_PROTOTYPE(void word_free, (Word * w)); +_PROTOTYPE(int List_len, (Word * w)); +_PROTOTYPE(Word * word_append, (Word * w1, Word * w2)); +_PROTOTYPE(int foundin, (Word * w1, Word * w2)); +_PROTOTYPE(void addword, (Word * w, char *s)); +_PROTOTYPE(void printlist, (Word * p)); +_PROTOTYPE(Word * typelist, (Word * p)); +_PROTOTYPE(void typefixhack, (Word * w)); +_PROTOTYPE(int ngetc, (FILE * f)); +_PROTOTYPE(int fnextch, (FILE * f)); +_PROTOTYPE(int nextch, (FILE * f)); +_PROTOTYPE(int getsym, (char *buf, FILE * f)); +_PROTOTYPE(int skipit, (char *buf, FILE * f)); +_PROTOTYPE(Word * getparamlist, (FILE * f)); +_PROTOTYPE(void emit, (Word * wlist, Word * plist, long startline)); +_PROTOTYPE(void getdecl, (FILE * f)); +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void Usage, (void)); + +/* Routines for manipulating lists of words. */ + +Word *word_alloc(s) +char *s; +{ + Word *w; + + w = (Word *) malloc(sizeof(Word) + strlen(s) + 1); + if (w == NULL) { + fprintf(stderr, "%s: out of memory\n", progname); + exit(1); + } + (void) strcpy(w->string, s); + w->next = NULL; + return w; +} + +void word_free(w) +Word *w; +{ + Word *oldw; + while (w) { + oldw = w; + w = w->next; + free((char *) oldw); + } +} + +/* Return the length of a list; empty words are not counted */ +int List_len(w) +Word *w; +{ + int count = 0; + + while (w) { + if (*w->string) count++; + w = w->next; + } + return count; +} + +/* Append two lists, and return the result */ +Word *word_append(w1, w2) +Word *w1, *w2; +{ + Word *r, *w; + + r = w = word_alloc(""); + + while (w1) { + w->next = word_alloc(w1->string); + w = w->next; + w1 = w1->next; + } + while (w2) { + w->next = word_alloc(w2->string); + w = w->next; + w2 = w2->next; + } + + return r; +} + +/* See if the last entry in w2 is in w1 */ +int foundin(w1, w2) +Word *w1, *w2; +{ + while (w2->next) w2 = w2->next; + + while (w1) { + if (!strcmp(w1->string, w2->string)) return 1; + w1 = w1->next; + } + return 0; +} + +/* Add the string s to the given list of words */ +void addword(w, s) +Word *w; +char *s; +{ + while (w->next) w = w->next; + w->next = word_alloc(s); +} + +/* Printlist: print out a list */ +void printlist(p) +Word *p; +{ + Word *w; + int i = 0; + + for (w = p; w; w = w->next) { + printf("%s", w->string); + if (ISCSYM(w->string[0]) && i > 0 + && w->next && w->next->string[0] != ',') printf(" "); + i++; + } +} + +/* Given a list representing a type and a variable name, extract just + * the base type, e.g. "struct word *x" would yield "struct word". + * Similarly, "unsigned char x[]" would yield "unsigned char". + */ +Word *typelist(p) +Word *p; +{ + Word *w, *r, *last; + + last = r = w = word_alloc(""); + while (p && p->next) { + if (p->string[0] == '[') { + word_free(w); + last->next = NULL; + break; + } + if (p->string[0] && !ISCSYM(p->string[0])) break; + w->next = word_alloc(p->string); + last = w; + w = w->next; + p = p->next; + } + return r; +} + +/* Typefixhack: promote formal parameters of type "char", "unsigned char", + * "short", or "unsigned short" to "int". + */ +void typefixhack(w) +Word *w; +{ + Word *oldw = 0; + + while (w) { + if (*w->string) { + if ((!strcmp(w->string, "char") || + !strcmp(w->string, "short")) + && (List_len(w->next) < 2)) { + if (oldw && !strcmp(oldw->string, "unsigned")) { + oldw->next = w->next; + free((char *) w); + w = oldw; + } + (void) strcpy(w->string, "int"); + } + } + w = w->next; + } +} + +/* Read a character: if it's a newline, increment the line count */ +int ngetc(f) +FILE *f; +{ + int c; + + c = getc(f); + if (c == '\n') linenum++; + + return c; +} + +/* Read the next character from the file. If the character is '\' then + * read and skip the next character. Any comment sequence is converted + * to a blank. + */ +int fnextch(f) +FILE *f; +{ + int c, lastc, incomment; + + c = ngetc(f); + while (c == '\\') { + c = ngetc(f); /* skip a character */ + c = ngetc(f); + } + if (c == '/' && !inquote) { + c = ngetc(f); + if (c == '*') { + incomment = 1; + c = ' '; + while (incomment) { + lastc = c; + c = ngetc(f); + if (lastc == '*' && c == '/') + incomment = 0; + else if (c < 0) + return c; + } + return fnextch(f); + } else { + if (c == '\n') linenum--; + (void) ungetc(c, f); + return '/'; + } + } + return c; +} + + +/* Get the next "interesting" character. Comments are skipped, and strings + * are converted to "0". Also, if a line starts with "#" it is skipped. + */ +int nextch(f) +FILE *f; +{ + int c; + + c = fnextch(f); + if (newline_seen && c == '#') { + do { + c = fnextch(f); + } while (c >= 0 && c != '\n'); + if (c < 0) return c; + } + newline_seen = (c == '\n'); + + if (c == '\'' || c == '\"') { + inquote = c; + while ((c = fnextch(f)) >= 0) { + if (c == inquote) { + inquote = 0; + return '0'; + } + } + } + return c; +} + +/* Get the next symbol from the file, skipping blanks. + * Return 0 if OK, -1 for EOF. + * Also collapses everything between { and } + */ +int getsym(buf, f) +char *buf; +FILE *f; +{ + register int c; + int inbrack = 0; + + c = glastc; + while ((c > 0) && isspace(c)) c = nextch(f); + if (c < 0) return -1; + if (c == '{') { + inbrack = 1; + endline = linenum; + while (inbrack) { + c = nextch(f); + if (c < 0) { + glastc = c; + return c; + } + if (c == '{') + inbrack++; + else if (c == '}') + inbrack--; + } + (void) strcpy(buf, "{}"); + glastc = nextch(f); + return 0; + } + if (!ISCSYM(c)) { + *buf++ = c; + glastc = nextch(f); + if (c == '(' && glastc == '*') { /* Look for a 'f'n pointer */ + *buf++ = glastc; + glastc = nextch(f); + } + *buf = 0; + return 0; + } + symline = linenum; + while (ISCSYM(c)) { + *buf++ = c; + c = nextch(f); + } + *buf = 0; + glastc = c; + return 0; +} + + +/* Skipit: skip until a ";" or the end of a function declaration is seen */ +int skipit(buf, f) +char *buf; +FILE *f; +{ + int i; + + do { + i = getsym(buf, f); + if (i < 0) return i; + } while (*buf != ';' && *buf != '{'); + + return 0; +} + +/* Get a parameter list; when this is called the next symbol in line + * should be the first thing in the list. + */ +Word *getparamlist(f) +FILE *f; +{ + static Word *pname[MAXPARAM]; /* parameter names */ + Word *tlist, /* type name */ + *plist; /* temporary */ + int np = 0; /* number of parameters */ + int typed[MAXPARAM]; /* parameter has been given a type */ + int tlistdone; /* finished finding the type name */ + int sawsomething; + int i; + int inparen = 0; + char buf[80]; + + for (i = 0; i < MAXPARAM; i++) typed[i] = 0; + + plist = word_alloc(""); + endlist = word_alloc(""); + + /* First, get the stuff inside brackets (if anything) */ + + sawsomething = 0; /* gets set nonzero when we see an arg */ + for (;;) { + if (getsym(buf, f) < 0) return(NULL); + if (*buf == ')' && (--inparen < 0)) { + if (sawsomething) { /* if we've seen an arg */ + pname[np] = plist; + plist = word_alloc(""); + np++; + } + break; + } + if (*buf == ';') { /* something weird */ + return ABORTED; + } + sawsomething = 1; /* there's something in the arg. list */ + if (*buf == ',' && inparen == 0) { + pname[np] = plist; + plist = word_alloc(""); + np++; + } else { + addword(plist, buf); + if (*buf == '(') inparen++; + } + } + + /* Next, get the declarations after the function header */ + inparen = 0; + tlist = word_alloc(""); + plist = word_alloc(""); + tlistdone = 0; + sawsomething = 0; + for (;;) { + if (getsym(buf, f) < 0) return(NULL); + + /* Handle parentheses, which should indicate func pointer rtn values */ + if (*buf == '(') { + addword(endlist, buf); + addword(endlist, " void "); + inparen++; + } else if (*buf == ')') { + if (symline == linenum) { + addword(endlist, buf); + addword(endlist, buf); + } + inparen--; + } else if (*buf == ',' && !inparen) { + /* Handle a list like "int x,y,z" */ + if (!sawsomething) return(NULL); + for (i = 0; i < np; i++) { + if (!typed[i] && foundin(plist, pname[i])) { + typed[i] = 1; + word_free(pname[i]); + pname[i] = word_append(tlist, plist); + /* Promote types */ + typefixhack(pname[i]); + break; + } + } + if (!tlistdone) { + tlist = typelist(plist); + tlistdone = 1; + } + word_free(plist); + plist = word_alloc(""); + } else if (*buf == ';') { + /* Handle the end of a list */ + if (!sawsomething) return ABORTED; + for (i = 0; i < np; i++) { + if (!typed[i] && foundin(plist, pname[i])) { + typed[i] = 1; + word_free(pname[i]); + pname[i] = word_append(tlist, plist); + typefixhack(pname[i]); + break; + } + } + tlistdone = 0; + word_free(tlist); + word_free(plist); + tlist = word_alloc(""); + plist = word_alloc(""); + } else if (!strcmp(buf, "{}")) + break; /* Handle the beginning of the function */ + /* Otherwise, throw word into list (except for "register") */ + else if (strcmp(buf, "register")) { + sawsomething = 1; + addword(plist, buf); + if (*buf == '(') inparen++; + if (*buf == ')') inparen--; + } + } + + /* Now take the info we have and build a prototype list */ + + /* Empty parameter list means "void" */ + if (np == 0) return word_alloc("void"); + + plist = tlist = word_alloc(""); + for (i = 0; i < np; i++) { + + /* If no type provided, make it an "int" */ + if (!(pname[i]->next) || + (!(pname[i]->next->next)&&strcmp(pname[i]->next->string,"void"))) { + addword(tlist, "int"); + } + while (tlist->next) tlist = tlist->next; + tlist->next = pname[i]; + if (i < np - 1) addword(tlist, ", "); + } + return plist; +} + +/* Emit a function declaration. The attributes and name of the function + * are in wlist; the parameters are in plist. + */ +void emit(wlist, plist, startline) +Word *wlist, *plist; +long startline; +{ + Word *w; + int count = 0; + + if (doold == 0) printf("_PROTOTYPE( "); + if (dodiff) { + printf("%lda%ld,%ld\n", startline - 1, startline, startline +2); + printf("> #ifdef __STDC__\n> "); + } + if (donum) printf("/*%8ld */ ", startline); + for (w = wlist; w; w = w->next) { + if (w->string[0]) count++; + } + if (count < 2) printf("int "); + printlist(wlist); + if (docond) { + if (doold) + printf(" P(("); + else + printf(", ("); + } else { + printf("("); + } + + printlist(plist); + printlist(endlist); + + if (docond) { + if (doold) + printf("))"); + else + printf(") )"); + } else { + printf(")"); + } + + if (!dodiff) + printf(";\n"); + else + printf("\n"); + + if (dodiff) { + printf("> #else\n"); + printf("%lda%ld\n", endline - 1, endline); + printf("> #endif\n"); + } +} + +/* Get all the function declarations */ +void getdecl(f) +FILE *f; +{ + Word *plist, *wlist = NULL; + char buf[80]; + int sawsomething; + long startline = 0L; /* line where declaration started */ + int oktoprint; + +again: /* SHAME SHAME */ + word_free(wlist); + wlist = word_alloc(""); + sawsomething = 0; + oktoprint = 1; + + for (;;) { + if (getsym(buf, f) < 0) return; + + /* Guess when a declaration is not an external function definition */ + if (!strcmp(buf, ",") || !strcmp(buf, "{}") || + !strcmp(buf, "=") || !strcmp(buf, "typedef") || + !strcmp(buf, "extern")) { + (void) skipit(buf, f); + goto again; + } + if (!dostatic && !strcmp(buf, "static")) oktoprint = 0; + + /* For the benefit of compilers that allow "inline" declarations */ + if (!strcmp(buf, "inline") && !sawsomething) continue; + if (!strcmp(buf, ";")) goto again; + + /* A left parenthesis *might* indicate a function definition */ + if (!strcmp(buf, "(")) { + if (!sawsomething || !(plist = getparamlist(f))) { + (void) skipit(buf, f); + goto again; + } + if (plist == ABORTED) goto again; + + /* It seems to have been what we wanted */ + if (oktoprint) emit(wlist, plist, startline); + word_free(plist); + goto again; + } + addword(wlist, buf); + if (!sawsomething) startline = symline; + sawsomething = 1; + } +} + +int main(argc, argv) +int argc; +char **argv; +{ + FILE *f, *g; + char *t; + char newname[40]; + + progname = argv[0]; + argv++; + argc--; + g = stdout; + + while (*argv && **argv == '-') { + t = *argv++; + --argc; + t++; + while (*t) { + if (*t == 's') + dostatic = 1; + else if (*t == 'n') + donum = 1; + else if (*t == 'p') + docond = 0; + else if (*t == 'P') + doold =1; + else if (*t == 'd') { + dodiff = 1; + doold = 1; + docond = 0; + donum = 0; + dostatic = 1; + } else + Usage(); + t++; + } + } + + if (docond && doold) { + printf("#ifdef __STDC__\n"); + printf("# define P(args)\targs\n"); + printf("#else\n"); + printf("# define P(args)\t()\n"); + printf("#endif\n\n"); + } + if (argc == 0) + getdecl(stdin); + else + while (argc > 0 && *argv) { + if (!(f = fopen(*argv, "r"))) { + perror(*argv); + exit(EXIT_FAILURE); + } +#if 0 + if (dodiff) { + (void) sprintf(newname, "%sdif", *argv); + (void) fclose(g); + if (!(g = fopen(newname, "w"))) { + perror(newname); + exit(EXIT_FAILURE); + } + } +#endif + if (doold && dohead && !dodiff) printf("\n/* %s */\n", *argv); + linenum = 1; + newline_seen = 1; + glastc = ' '; + getdecl(f); + argc--; + argv++; + (void) fclose(f); + } + if (docond && doold) printf("\n#undef P\n"); /* clean up namespace */ + (void) fclose(g); + return(EXIT_SUCCESS); +} + + +void Usage() +{ + fputs("Usage: ", stderr); + fputs(progname, stderr); + fputs(" [-d][-n][-p][-s] [files ...]\n", stderr); + fputs(" -P: use P() style instead of _PROTOTYPE\n", stderr); + fputs(" -d: produce a diff file to prototype original source\n", stderr); + fputs(" -n: put line numbers of declarations as comments\n", stderr); + fputs(" -p: don't make header files readable by K&R compilers\n", stderr); + fputs(" -s: include declarations for static functions\n", stderr); + exit(EXIT_FAILURE); +} diff --git a/commands/simple/pwd.c b/commands/simple/pwd.c new file mode 100755 index 000000000..279cc0b4e --- /dev/null +++ b/commands/simple/pwd.c @@ -0,0 +1,49 @@ +/* pwd - print working directory Author: Norbert Schlenker */ + +/* + * pwd - print working directory + * Syntax: pwd + * Flags: None. + * Author: Norbert Schlenker + * Copyright: None. Released to the public domain. + * Reference: IEEE P1003.2 Section 4.50 (draft 10) + * Bugs: No internationalization support; all messages are in English. + */ + +/* Force visible Posix names */ +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE 1 +#endif + +/* External interfaces */ +#include <sys/types.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +/* Magic numbers suggested or required by Posix specification */ +#define SUCCESS 0 /* exit code in case of success */ +#define FAILURE 1 /* or failure */ + +_PROTOTYPE(int main, (void)); + +static char dir[PATH_MAX + 1]; +static char *errmsg = "pwd: cannot search some directory on the path\n"; + +int main() +{ + char *p; + size_t n; + + p = getcwd(dir, PATH_MAX); + if (p == NULL) { + write(STDERR_FILENO, errmsg, strlen(errmsg)); + exit(FAILURE); + } + n = strlen(p); + p[n] = '\n'; + if (write(STDOUT_FILENO, p, n + 1) != n + 1) exit(FAILURE); + return(SUCCESS); +} diff --git a/commands/simple/pwdauth.c b/commands/simple/pwdauth.c new file mode 100755 index 000000000..65044b7f7 --- /dev/null +++ b/commands/simple/pwdauth.c @@ -0,0 +1,357 @@ +/* pwdauth 2.0 - check a shadow password Author: Kees J. Bot + * 7 Feb 1994 + * + * This program gets as input the key and salt arguments of the crypt(3) + * function as two null terminated strings. The crypt result is output as + * one null terminated string. Input and output must be <= 1024 characters. + * The exit code will be 1 on any error. + * + * If the key has the form '##name' then the key will be encrypted and the + * result checked to be equal to the encrypted password in the shadow password + * file. If equal than '##name' will be returned, otherwise exit code 2. + * + * Otherwise the key will be encrypted normally and the result returned. + * + * As a special case, anything matches a null encrypted password to allow + * a no-password login. + */ +#define nil 0 +#define crypt CRYPT /* The true crypt is included here. */ +#include <sys/types.h> +#include <pwd.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +#define LEN 1024 +char SHADOW[] = "/etc/shadow"; + +int main(int argc, char **argv) +{ + char key[LEN]; + char *salt; + struct passwd *pw; + int n; + + /* Read input data. Check if there are exactly two null terminated + * strings. + */ + n= read(0, key, LEN); + if (n < 0) return 1; + salt = key + n; + n = 0; + while (salt > key) if (*--salt == 0) n++; + if (n != 2) return 1; + salt = key + strlen(key) + 1; + + if (salt[0] == '#' && salt[1] == '#') { + /* Get the encrypted password from the shadow password file, + * encrypt key and compare. + */ + setpwfile(SHADOW); + + if ((pw= getpwnam(salt + 2)) == nil) return 2; + + /* A null encrypted password matches a null key, otherwise + * do the normal crypt(3) authentication check. + */ + if (*pw->pw_passwd == 0 && *key == 0) { + /* fine */ + } else + if (strcmp(crypt(key, pw->pw_passwd), pw->pw_passwd) != 0) { + return 2; + } + } else { + /* Normal encryption. */ + if (*salt == 0 && *key == 0) { + /* fine */ + } else { + salt= crypt(key, salt); + } + } + + /* Return the (possibly new) salt to the caller. */ + if (write(1, salt, strlen(salt) + 1) < 0) return 1; + return 0; +} + +/* The one and only crypt(3) function. */ + +/* From Andy Tanenbaum's book "Computer Networks", + rewritten in C +*/ + +struct block { + unsigned char b_data[64]; +}; + +struct ordering { + unsigned char o_data[64]; +}; + +static struct block key; + +static struct ordering InitialTr = { + 58,50,42,34,26,18,10, 2,60,52,44,36,28,20,12, 4, + 62,54,46,38,30,22,14, 6,64,56,48,40,32,24,16, 8, + 57,49,41,33,25,17, 9, 1,59,51,43,35,27,19,11, 3, + 61,53,45,37,29,21,13, 5,63,55,47,39,31,23,15, 7, +}; + +static struct ordering FinalTr = { + 40, 8,48,16,56,24,64,32,39, 7,47,15,55,23,63,31, + 38, 6,46,14,54,22,62,30,37, 5,45,13,53,21,61,29, + 36, 4,44,12,52,20,60,28,35, 3,43,11,51,19,59,27, + 34, 2,42,10,50,18,58,26,33, 1,41, 9,49,17,57,25, +}; + +static struct ordering swap = { + 33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48, + 49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64, + 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15,16, + 17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32, +}; + +static struct ordering KeyTr1 = { + 57,49,41,33,25,17, 9, 1,58,50,42,34,26,18, + 10, 2,59,51,43,35,27,19,11, 3,60,52,44,36, + 63,55,47,39,31,23,15, 7,62,54,46,38,30,22, + 14, 6,61,53,45,37,29,21,13, 5,28,20,12, 4, +}; + +static struct ordering KeyTr2 = { + 14,17,11,24, 1, 5, 3,28,15, 6,21,10, + 23,19,12, 4,26, 8,16, 7,27,20,13, 2, + 41,52,31,37,47,55,30,40,51,45,33,48, + 44,49,39,56,34,53,46,42,50,36,29,32, +}; + +static struct ordering etr = { + 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, + 8, 9,10,11,12,13,12,13,14,15,16,17, + 16,17,18,19,20,21,20,21,22,23,24,25, + 24,25,26,27,28,29,28,29,30,31,32, 1, +}; + +static struct ordering ptr = { + 16, 7,20,21,29,12,28,17, 1,15,23,26, 5,18,31,10, + 2, 8,24,14,32,27, 3, 9,19,13,30, 6,22,11, 4,25, +}; + +static unsigned char s_boxes[8][64] = { +{ 14, 4,13, 1, 2,15,11, 8, 3,10, 6,12, 5, 9, 0, 7, + 0,15, 7, 4,14, 2,13, 1,10, 6,12,11, 9, 5, 3, 8, + 4, 1,14, 8,13, 6, 2,11,15,12, 9, 7, 3,10, 5, 0, + 15,12, 8, 2, 4, 9, 1, 7, 5,11, 3,14,10, 0, 6,13, +}, + +{ 15, 1, 8,14, 6,11, 3, 4, 9, 7, 2,13,12, 0, 5,10, + 3,13, 4, 7,15, 2, 8,14,12, 0, 1,10, 6, 9,11, 5, + 0,14, 7,11,10, 4,13, 1, 5, 8,12, 6, 9, 3, 2,15, + 13, 8,10, 1, 3,15, 4, 2,11, 6, 7,12, 0, 5,14, 9, +}, + +{ 10, 0, 9,14, 6, 3,15, 5, 1,13,12, 7,11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6,10, 2, 8, 5,14,12,11,15, 1, + 13, 6, 4, 9, 8,15, 3, 0,11, 1, 2,12, 5,10,14, 7, + 1,10,13, 0, 6, 9, 8, 7, 4,15,14, 3,11, 5, 2,12, +}, + +{ 7,13,14, 3, 0, 6, 9,10, 1, 2, 8, 5,11,12, 4,15, + 13, 8,11, 5, 6,15, 0, 3, 4, 7, 2,12, 1,10,14, 9, + 10, 6, 9, 0,12,11, 7,13,15, 1, 3,14, 5, 2, 8, 4, + 3,15, 0, 6,10, 1,13, 8, 9, 4, 5,11,12, 7, 2,14, +}, + +{ 2,12, 4, 1, 7,10,11, 6, 8, 5, 3,15,13, 0,14, 9, + 14,11, 2,12, 4, 7,13, 1, 5, 0,15,10, 3, 9, 8, 6, + 4, 2, 1,11,10,13, 7, 8,15, 9,12, 5, 6, 3, 0,14, + 11, 8,12, 7, 1,14, 2,13, 6,15, 0, 9,10, 4, 5, 3, +}, + +{ 12, 1,10,15, 9, 2, 6, 8, 0,13, 3, 4,14, 7, 5,11, + 10,15, 4, 2, 7,12, 9, 5, 6, 1,13,14, 0,11, 3, 8, + 9,14,15, 5, 2, 8,12, 3, 7, 0, 4,10, 1,13,11, 6, + 4, 3, 2,12, 9, 5,15,10,11,14, 1, 7, 6, 0, 8,13, +}, + +{ 4,11, 2,14,15, 0, 8,13, 3,12, 9, 7, 5,10, 6, 1, + 13, 0,11, 7, 4, 9, 1,10,14, 3, 5,12, 2,15, 8, 6, + 1, 4,11,13,12, 3, 7,14,10,15, 6, 8, 0, 5, 9, 2, + 6,11,13, 8, 1, 4,10, 7, 9, 5, 0,15,14, 2, 3,12, +}, + +{ 13, 2, 8, 4, 6,15,11, 1,10, 9, 3,14, 5, 0,12, 7, + 1,15,13, 8,10, 3, 7, 4,12, 5, 6,11, 0,14, 9, 2, + 7,11, 4, 1, 9,12,14, 2, 0, 6,10,13,15, 3, 5, 8, + 2, 1,14, 7, 4,10, 8,13,15,12, 9, 0, 3, 5, 6,11, +}, +}; + +static int rots[] = { + 1,1,2,2,2,2,2,2,1,2,2,2,2,2,2,1, +}; + +static void transpose(struct block *data, struct ordering *t, int n) +{ + struct block x; + + x = *data; + + while (n-- > 0) { + data->b_data[n] = x.b_data[t->o_data[n] - 1]; + } +} + +static void rotate(struct block *key) +{ + unsigned char *p = key->b_data; + unsigned char *ep = &(key->b_data[55]); + int data0 = key->b_data[0], data28 = key->b_data[28]; + + while (p++ < ep) *(p-1) = *p; + key->b_data[27] = data0; + key->b_data[55] = data28; +} + +static struct ordering *EP = &etr; + +static void f(int i, struct block *key, struct block *a, struct block *x) +{ + struct block e, ikey, y; + int k; + unsigned char *p, *q, *r; + + e = *a; + transpose(&e, EP, 48); + for (k = rots[i]; k; k--) rotate(key); + ikey = *key; + transpose(&ikey, &KeyTr2, 48); + p = &(y.b_data[48]); + q = &(e.b_data[48]); + r = &(ikey.b_data[48]); + while (p > y.b_data) { + *--p = *--q ^ *--r; + } + q = x->b_data; + for (k = 0; k < 8; k++) { + int xb, r; + + r = *p++ << 5; + r += *p++ << 3; + r += *p++ << 2; + r += *p++ << 1; + r += *p++; + r += *p++ << 4; + + xb = s_boxes[k][r]; + + *q++ = (xb >> 3) & 1; + *q++ = (xb>>2) & 1; + *q++ = (xb>>1) & 1; + *q++ = (xb & 1); + } + transpose(x, &ptr, 32); +} + +static void setkey(char *k) +{ + + key = *((struct block *) k); + transpose(&key, &KeyTr1, 56); +} + +static void encrypt(char *blck, int edflag) +{ + struct block *p = (struct block *) blck; + int i; + + transpose(p, &InitialTr, 64); + for (i = 15; i>= 0; i--) { + int j = edflag ? i : 15 - i; + int k; + struct block b, x; + + b = *p; + for (k = 31; k >= 0; k--) { + p->b_data[k] = b.b_data[k + 32]; + } + f(j, &key, p, &x); + for (k = 31; k >= 0; k--) { + p->b_data[k+32] = b.b_data[k] ^ x.b_data[k]; + } + } + transpose(p, &swap, 64); + transpose(p, &FinalTr, 64); +} + +char *crypt(const char *pw, const char *salt) +{ + char pwb[66]; + char *cp; + static char result[16]; + char *p = pwb; + struct ordering new_etr; + int i; + + while (*pw && p < &pwb[64]) { + int j = 7; + + while (j--) { + *p++ = (*pw >> j) & 01; + } + pw++; + *p++ = 0; + } + while (p < &pwb[64]) *p++ = 0; + + setkey(p = pwb); + + while (p < &pwb[66]) *p++ = 0; + + new_etr = etr; + EP = &new_etr; + if (salt[0] == 0 || salt[1] == 0) salt = "**"; + for (i = 0; i < 2; i++) { + char c = *salt++; + int j; + + result[i] = c; + if ( c > 'Z') c -= 6 + 7 + '.'; /* c was a lower case letter */ + else if ( c > '9') c -= 7 + '.';/* c was upper case letter */ + else c -= '.'; /* c was digit, '.' or '/'. */ + /* now, 0 <= c <= 63 */ + for (j = 0; j < 6; j++) { + if ((c >> j) & 01) { + int t = 6*i + j; + int temp = new_etr.o_data[t]; + new_etr.o_data[t] = new_etr.o_data[t+24]; + new_etr.o_data[t+24] = temp; + } + } + } + + if (result[1] == 0) result[1] = result[0]; + + for (i = 0; i < 25; i++) encrypt(pwb,0); + EP = &etr; + + p = pwb; + cp = result+2; + while (p < &pwb[66]) { + int c = 0; + int j = 6; + + while (j--) { + c <<= 1; + c |= *p++; + } + c += '.'; /* becomes >= '.' */ + if (c > '9') c += 7; /* not in [./0-9], becomes upper */ + if (c > 'Z') c += 6; /* not in [A-Z], becomes lower */ + *cp++ = c; + } + *cp = 0; + return result; +} diff --git a/commands/simple/rarpd.c b/commands/simple/rarpd.c new file mode 100755 index 000000000..abb1872ea --- /dev/null +++ b/commands/simple/rarpd.c @@ -0,0 +1,365 @@ +/* +rarpd.c + +Created: Nov 12, 1992 by Philip Homburg + +Changed: May 13, 1995 by Kees J. Bot + Rewrite to handle multiple ethernets. + +Changed: Jul 18, 1995 by Kees J. Bot + Do RARP requests (formerly inet's job) + +Changed: Dec 14, 1996 by Kees J. Bot + Query the netmask + +Changed: Dec 11, 2000 by Kees J. Bot + Dressed down to be only a RARP server, giving the floor to DHCP +*/ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include <sys/asynchio.h> +#include <net/hton.h> +#include <net/gen/socket.h> +#include <net/gen/netdb.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/ether.h> +#include <net/gen/eth_io.h> +#include <net/gen/if_ether.h> +#include <net/gen/ip_io.h> +#include <net/gen/nameser.h> + +#define MAX_RARP_RETRIES 5 +#define RARP_TIMEOUT 5 + +typedef struct rarp46 +{ + ether_addr_t a46_dstaddr; + ether_addr_t a46_srcaddr; + ether_type_t a46_ethtype; + u16_t a46_hdr, a46_pro; + u8_t a46_hln, a46_pln; + u16_t a46_op; + ether_addr_t a46_sha; + u8_t a46_spa[4]; + ether_addr_t a46_tha; + u8_t a46_tpa[4]; + char a46_padding[ETH_MIN_PACK_SIZE - (4*6 + 2*4 + 4*2 + 2*1)]; +} rarp46_t; + +#define ETH_RARP_PROTO 0x8035 + +#define RARP_ETHERNET 1 + +#define RARP_REQUEST 3 +#define RARP_REPLY 4 + +static char *program; +static unsigned debug; + +#define between(a, c, z) ((unsigned) (c) - (a) <= (unsigned) (z) - (a)) + +static void report(const char *label) +{ + fprintf(stderr, "%s: %s: %s\n", program, label, strerror(errno)); +} + +static void fatal(const char *label) +{ + report(label); + exit(1); +} + +static void *allocate(size_t size) +{ + void *mem; + + if ((mem= malloc(size)) == NULL) fatal("Can't allocate memory"); + return mem; +} + +static char *ethdev(int n) +{ + static char an_ethdev[]= "/dev/ethNNN"; + + sprintf(an_ethdev + sizeof(an_ethdev)-4, "%d", n); + return an_ethdev; +} + +static char *ipdev(int n) +{ + static char an_ipdev[]= "/dev/ipNNN"; + + sprintf(an_ipdev + sizeof(an_ipdev)-4, "%d", n); + return an_ipdev; +} + +typedef struct ethernet { + int n; /* Network number. */ + int eth_fd; /* Open low level ethernet device. */ + ether_addr_t eth_addr; /* Ethernet address of this net. */ + char packet[ETH_MAX_PACK_SIZE]; /* Incoming packet. */ + ipaddr_t ip_addr; /* IP address of this net. */ + ipaddr_t ip_mask; /* Associated netmask. */ +} ethernet_t; + +static ethernet_t *ethernets; + +static void onsig(int sig) +{ + switch (sig) { + case SIGUSR1: debug++; break; + case SIGUSR2: debug= 0; break; + } +} + +static void rarp_reply(ethernet_t *ep, char *hostname, ipaddr_t ip_addr, + ether_addr_t eth_addr) +{ + rarp46_t rarp46; + + /* Construct a RARP reply packet and send it. */ + rarp46.a46_dstaddr= eth_addr; + rarp46.a46_hdr= HTONS(RARP_ETHERNET); + rarp46.a46_pro= HTONS(ETH_IP_PROTO); + rarp46.a46_hln= 6; + rarp46.a46_pln= 4; + rarp46.a46_op= HTONS(RARP_REPLY); + rarp46.a46_sha= ep->eth_addr; + memcpy(rarp46.a46_spa, &ep->ip_addr, sizeof(ipaddr_t)); + rarp46.a46_tha= eth_addr; + memcpy(rarp46.a46_tpa, &ip_addr, sizeof(ipaddr_t)); + + if (debug >= 1) { + printf("%s: Replying %s (%s) to %s\n", + ethdev(ep->n), inet_ntoa(ip_addr), hostname, ether_ntoa(ð_addr)); + } + (void) write(ep->eth_fd, &rarp46, sizeof(rarp46)); +} + +static int addhostname(char *addname, char *hostname, int n) +{ + /* Create an additional hostname for a given hostname by adding "-n" to + * the first part. E.g. given "wombat.cs.vu.nl" and n=2 return + * "wombat-2.cs.vu.nl". This is useful for VU practical work where + * people get a few extra ethernet addresses on a machine and are asked + * to build a TCP/IP stack on it. + */ + char *dot; + + if (strlen(hostname) + 4 >= 1024) return 0; + if ((dot= strchr(hostname, '.')) == NULL) dot= strchr(hostname, 0); + sprintf(addname, "%.*s-%d%s", (dot - hostname), hostname, n, dot); + return 1; +} + +static void usage(void) +{ + fprintf(stderr, "Usage: %s [-d[level]] network-name ...\n", program); + exit(1); +} + +static int ifname2n(const char *name) +{ + /* Translate an interface name, ip0, ip1, etc, to a number. */ + const char *np; + char *end; + unsigned long n; + + np= name; + if (*np++ != 'i' || *np++ != 'p') usage(); + n= strtoul(np, &end, 10); + if (end == np || *end != 0) usage(); + if (n >= 1000) { + fprintf(stderr, "%s: Network number of \"%s\" is a bit large\n", + program, name); + exit(1); + } + return n; +} + +int main(int argc, char **argv) +{ + int i; + ethernet_t *ep; + nwio_ethopt_t ethopt; + nwio_ethstat_t ethstat; + char hostname[1024]; + struct hostent *hostent; + struct sigaction sa; + nwio_ipconf_t ipconf; + asynchio_t asyn; + ssize_t n; + ipaddr_t ip_addr; + rarp46_t rarp46; + int fd; + int n_eths; + + program= argv[0]; + asyn_init(&asyn); + + debug= 0; + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++]+1; + + if (opt[0] == '-' && opt[1] == 0) break; /* -- */ + + while (*opt != 0) switch (*opt++) { + case 'd': + debug= 1; + if (between('0', *opt, '9')) debug= strtoul(opt, &opt, 10); + break; + default: + usage(); + } + } + + if ((n_eths= (argc - i)) == 0) usage(); + +#if __minix_vmd + /* Minix-vmd can handle all nets at once using async I/O. */ + ethernets= allocate(n_eths * sizeof(ethernets[0])); + for (i= 0; i < n_eths; i++) { + ethernets[i].n= ifname2n(argv[argc - n_eths + i]); + } +#else + /* Minix forks n-1 times to handle each net in a process each. */ + for (i= 0; i < n_eths; i++) { + if (i+1 < n_eths) { + switch (fork()) { + case -1: fatal("fork()"); + case 0: break; + default: continue; + } + } + ethernets= allocate(1 * sizeof(ethernets[0])); + ethernets[0].n= ifname2n(argv[argc - n_eths + i]); + } + n_eths= 1; +#endif + + sa.sa_handler= onsig; + sigemptyset(&sa.sa_mask); + sa.sa_flags= 0; + sigaction(SIGUSR1, &sa, NULL); + sigaction(SIGUSR2, &sa, NULL); + + for (i= 0; i < n_eths; i++) { + ep= ðernets[i]; + if ((ep->eth_fd= open(ethdev(ep->n), O_RDWR)) < 0) fatal(ethdev(ep->n)); + + if (ioctl(ep->eth_fd, NWIOGETHSTAT, ðstat) < 0) { + fprintf(stderr, "%s: %s: Unable to get eth statistics: %s\n", + program, ethdev(ep->n), strerror(errno)); + exit(1); + } + ep->eth_addr= ethstat.nwes_addr; + if (debug >= 1) { + printf("%s: Ethernet address is %s\n", + ethdev(ep->n), ether_ntoa(&ep->eth_addr)); + } + + ethopt.nweo_flags= NWEO_COPY | NWEO_EN_LOC | NWEO_EN_BROAD | + NWEO_TYPESPEC; + ethopt.nweo_type= HTONS(ETH_RARP_PROTO); + + if (ioctl(ep->eth_fd, NWIOSETHOPT, ðopt) < 0) { + fprintf(stderr, "%s: %s: Unable to set eth options: %s\n", + program, ethdev(ep->n), strerror(errno)); + exit(1); + } + + /* What are my address and netmask? */ + if ((fd= open(ipdev(ep->n), O_RDWR)) < 0) fatal(ipdev(ep->n)); + if (ioctl(fd, NWIOGIPCONF, &ipconf) < 0) fatal(ipdev(ep->n)); + + ep->ip_addr= ipconf.nwic_ipaddr; + ep->ip_mask= ipconf.nwic_netmask; + close(fd); + if (debug >= 1) { + printf("%s: IP address is %s / ", + ipdev(ep->n), inet_ntoa(ep->ip_addr)); + printf("%s\n", inet_ntoa(ep->ip_mask)); + } + } + + /* Wait for RARP requests, reply, repeat. */ + for(;;) { + fflush(NULL); + + /* Wait for a RARP request. */ + for (i= 0; i < n_eths; i++) { + ep= ðernets[i]; + + n= asyn_read(&asyn, ep->eth_fd, ep->packet, sizeof(ep->packet)); + if (n != -1) break; + if (errno != EINPROGRESS) { + report(ethdev(ep->n)); + sleep(10); + } + } + + /* RARP request? */ + if (i < n_eths + && n >= sizeof(rarp46) + && (memcpy(&rarp46, ep->packet, sizeof(rarp46)), 1) + && rarp46.a46_hdr == HTONS(RARP_ETHERNET) + && rarp46.a46_pro == HTONS(ETH_IP_PROTO) + && rarp46.a46_hln == 6 + && rarp46.a46_pln == 4 + && rarp46.a46_op == HTONS(RARP_REQUEST) + ) { + if ((ether_ntohost(hostname, &rarp46.a46_tha) == 0 + || (rarp46.a46_tha.ea_addr[0] == 'v' + && (memcpy(&ip_addr, rarp46.a46_tha.ea_addr+2, 4), 1) + && (hostent= gethostbyaddr((char*) &ip_addr, + 4, AF_INET)) != NULL + && addhostname(hostname, hostent->h_name, + rarp46.a46_tha.ea_addr[1]))) + && (hostent= gethostbyname(hostname)) != NULL + && hostent->h_addrtype == AF_INET + ) { + /* Host is found in the ethers file and the DNS, or the + * ethernet address denotes a special additional address + * used for implementing a TCP/IP stack in user space. + */ + for (i= 0; hostent->h_addr_list[i] != NULL; i++) { + memcpy(&ip_addr, hostent->h_addr_list[i], sizeof(ipaddr_t)); + + /* Check if the address is on this network. */ + if (((ip_addr ^ ep->ip_addr) & ep->ip_mask) == 0) break; + } + + if (hostent->h_addr_list[i] != NULL) { + rarp_reply(ep, hostname, ip_addr, rarp46.a46_tha); + } else { + if (debug >= 2) { + printf("%s: Host '%s' (%s) is on the wrong net\n", + ethdev(ep->n), + hostname, ether_ntoa(&rarp46.a46_tha)); + } + } + } else { + if (debug >= 2) { + printf("%s: RARP request from unknown host '%s'\n", + ethdev(ep->n), ether_ntoa(&rarp46.a46_tha)); + } + } + } + + /* Wait for another request. */ + if (asyn_wait(&asyn, 0, NULL) < 0) { + report("asyn_wait()"); + sleep(10); + } + } +} diff --git a/commands/simple/rcp.c b/commands/simple/rcp.c new file mode 100755 index 000000000..933b2b719 --- /dev/null +++ b/commands/simple/rcp.c @@ -0,0 +1,848 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. The Berkeley software License Agreement + * specifies the terms and conditions for redistribution. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1983 Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rcp.c 1.1 87/12/21 SMI"; /* from UCB 5.3 6/8/85"*/ +#endif /* not lint */ + +/* + * rcp + */ + +#undef _MINIX +#define NAMESERVER + +#include <ctype.h> +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <utime.h> + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> + +#include <dirent.h> +#include <fcntl.h> +#include <pwd.h> +#include <signal.h> +#include <unistd.h> + +#include <net/gen/netdb.h> +#include <net/netlib.h> + +#if __STDC__ +#define PROTO(func, args) func args +#else +#define PROTO(func, args) func () +#endif /* __STDC__ */ + +PROTO (int main, (int argc, char *argv[])); +PROTO (void lostconn, (int sig)); +PROTO (void error, (char *fmt, ...) ); +PROTO (int response, (void) ); +PROTO (void source, (int argc, char *argv[]) ); +PROTO (void sink, (int argc, char *argv[]) ); +PROTO (void usage, (void) ); +PROTO (char *colon, (char *cp) ); +PROTO (int okname, (char *cp0) ); +PROTO (int susystem, (char *s) ); +PROTO (void verifydir, (char *cp) ); +PROTO (void rsource, (char *name, struct stat *statp) ); +PROTO (struct buffer *allocbuf, (struct buffer *bp, int fd, int blksize) ); + +#define vfork fork + +int rem; +int errs; +int errno; +int iamremote, targetshouldbedirectory; +int iamrecursive; +int myuid; /* uid of invoker */ +int pflag; +struct passwd *pwd; +int userid; +int port; + +struct buffer { + int cnt; + char *buf; +}; + + +#define ga() (void) write(rem, "", 1) + +main(argc, argv) + int argc; + char **argv; +{ + char *targ, *host, *src; +#ifndef NAMESERVER + char *suser, *tuser; +#else /* NAMESERVER */ + char *suser, *tuser, *thost; +#endif /* NAMESERVER */ + int i; + char buf[BUFSIZ], cmd[16]; + struct servent *sp; + + sp = getservbyname("shell", "tcp"); + if (sp == NULL) { + fprintf(stderr, "rcp: shell/tcp: unknown service\n"); + exit(1); + } + port = sp->s_port; + pwd = getpwuid(userid = getuid()); + if (pwd == 0) { + fprintf(stderr, "who are you?\n"); + exit(1); + } + +#ifdef NOT_DEF + /* + * This is a kludge to allow seteuid to user before touching + * files and seteuid root before doing rcmd so we can open + * the socket. + */ + myuid = getuid(); + if (setruid(0) < 0) { + perror("setruid root"); + exit(1); + } + seteuid(myuid); +#endif + + for (argc--, argv++; argc > 0 && **argv == '-'; argc--, argv++) { + (*argv)++; + while (**argv) switch (*(*argv)++) { + + case 'r': + iamrecursive++; + break; + + case 'p': /* preserve mtimes and atimes */ + pflag++; + break; + + /* The rest of these are not for users. */ + case 'd': + targetshouldbedirectory = 1; + break; + + case 'f': /* "from" */ + iamremote = 1; + (void) response(); + source(--argc, ++argv); + exit(errs); + + case 't': /* "to" */ + iamremote = 1; + sink(--argc, ++argv); + exit(errs); + + default: + usage(); + exit(1); + } + } +if (iamremote) +{ + close(2); + open("/dev/tty", 2); +} + + if (argc < 2) { + usage(); + exit(1); + } + rem = -1; + if (argc > 2) + targetshouldbedirectory = 1; + (void) sprintf(cmd, "rcp%s%s%s", + iamrecursive ? " -r" : "", pflag ? " -p" : "", + targetshouldbedirectory ? " -d" : ""); + (void) signal(SIGPIPE, lostconn); + targ = colon(argv[argc - 1]); + if (targ) { /* ... to remote */ + *targ++ = 0; + if (*targ == 0) + targ = "."; +#ifndef NAMESERVER + tuser = strrchr(argv[argc - 1], '.'); + if (tuser) { + *tuser++ = 0; + if (!okname(tuser)) + exit(1); + } else + tuser = pwd->pw_name; +#else /* NAMESERVER */ + thost = strchr(argv[argc - 1], '@'); + if (thost) { + *thost++ = 0; + tuser = argv[argc - 1]; + if (*tuser == '\0') + tuser = pwd->pw_name; + else if (!okname(tuser)) + exit(1); + } else { + thost = argv[argc - 1]; + tuser = pwd->pw_name; + } +#endif /* NAMESERVER */ + for (i = 0; i < argc - 1; i++) { + src = colon(argv[i]); + if (src) { /* remote to remote */ + *src++ = 0; + if (*src == 0) + src = "."; +#ifndef NAMESERVER + suser = strrchr(argv[i], '.'); + if (suser) { + *suser++ = 0; + if (!okname(suser)) +#else /* NAMESERVER */ + host = strchr(argv[i], '@'); + if (host) { + *host++ = 0; + suser = argv[i]; + if (*suser == '\0') + suser = pwd->pw_name; + else if (!okname(suser)) +#endif /* NAMESERVER */ + continue; +#ifndef NAMESERVER + (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s.%s:%s'", + argv[i], suser, cmd, src, + argv[argc - 1], tuser, targ); + } else + (void) sprintf(buf, "rsh %s -n %s %s '%s.%s:%s'", + argv[i], cmd, src, + argv[argc - 1], tuser, targ); +#else /* NAMESERVER */ + (void) sprintf(buf, "rsh %s -l %s -n %s %s '%s@%s:%s'", + host, suser, cmd, src, + tuser, thost, targ); + } else + (void) sprintf(buf, "rsh %s -n %s %s '%s@%s:%s'", + argv[i], cmd, src, + tuser, thost, targ); +#endif /* NAMESERVER */ + (void) susystem(buf); + } else { /* local to remote */ + if (rem == -1) { + (void) sprintf(buf, "%s -t %s", + cmd, targ); +#ifndef NAMESERVER + host = argv[argc - 1]; +#else /* NAMESERVER */ + host = thost; +#endif /* NAMESERVER */ +#ifdef NOT_DEF + if (seteuid(0) < 0) { + perror("seteuid root"); + exit(1); + } +#endif + rem = rcmd(&host, port, pwd->pw_name, + tuser, buf, 0); +#ifdef NO_DEF + seteuid(myuid); +#endif + if (rem < 0) + exit(1); + if (response() < 0) + exit(1); + } + source(1, argv+i); + } + } + } else { /* ... to local */ + if (targetshouldbedirectory) + verifydir(argv[argc - 1]); + for (i = 0; i < argc - 1; i++) { + src = colon(argv[i]); + if (src == 0) { /* local to local */ + (void) sprintf(buf, "cp%s%s %s %s", + iamrecursive ? " -r" : "", + pflag ? " -p" : "", + argv[i], argv[argc - 1]); + (void) susystem(buf); + } else { /* remote to local */ + *src++ = 0; + if (*src == 0) + src = "."; +#ifndef NAMESERVER + suser = strrchr(argv[i], '.'); + if (suser) { + *suser++ = 0; + if (!okname(suser)) +#else /* NAMESERVER */ + host = strchr(argv[i], '@'); + if (host) { + *host++ = 0; + suser = argv[i]; + if (*suser == '\0') + suser = pwd->pw_name; + else if (!okname(suser)) +#endif /* NAMESERVER */ + continue; +#ifndef NAMESERVER + } else +#else /* NAMESERVER */ + } else { + host = argv[i]; +#endif /* NAMESERVER */ + suser = pwd->pw_name; +#ifdef NAMESERVER + } +#endif /* NAMESERVER */ + (void) sprintf(buf, "%s -f %s", cmd, src); +#ifndef NAMESERVER + host = argv[i]; +#endif /* NAMESERVER */ +#ifdef NOT_DEF + if (seteuid(0) < 0) { + perror("seteuid root"); + exit(1); + } +#endif + rem = rcmd(&host, port, pwd->pw_name, suser, + buf, 0); +#ifdef NOT_DEF + seteuid(myuid); +#endif + if (rem < 0) { + errs++; + continue; + } + sink(1, argv+argc-1); + (void) close(rem); + rem = -1; + } + } + } + exit(errs); +} + +void +verifydir(cp) + char *cp; +{ + struct stat stb; + + if (stat(cp, &stb) >= 0) { + if ((stb.st_mode & S_IFMT) == S_IFDIR) + return; + errno = ENOTDIR; + } + error("rcp: %s: %s.\n", cp, strerror(errno)); + exit(1); +} + +char * +colon(cp) + char *cp; +{ + + while (*cp) { + if (*cp == ':') + return (cp); + if (*cp == '/') + return (0); + cp++; + } + return (0); +} + + +int +okname(cp0) + char *cp0; +{ + register char *cp = cp0; + register int c; + + do { + c = *cp; + if (c & 0200) + goto bad; + if (!isalpha(c) && !isdigit(c) && c != '_' && c != '-') + goto bad; + cp++; + } while (*cp); + return (1); +bad: + fprintf(stderr, "rcp: invalid user name %s\n", cp0); + return (0); +} + +int +susystem(s) + char *s; +{ + int status, pid, w; + register void PROTO ((*istat), (int) ), PROTO ((*qstat), (int) ); + + if ((pid = vfork()) == 0) { +#ifdef NOT_DEF + (void) setruid(myuid); +#endif + execl("/bin/sh", "sh", "-c", s, (char *)0); + _exit(127); + } + istat = signal(SIGINT, SIG_IGN); + qstat = signal(SIGQUIT, SIG_IGN); + while ((w = wait(&status)) != pid && w != -1) + ; + if (w == -1) + status = -1; + (void) signal(SIGINT, istat); + (void) signal(SIGQUIT, qstat); + return (status); +} + +void +source(argc, argv) + int argc; + char **argv; +{ + char *last, *name; + struct stat stb; + static struct buffer buffer; + struct buffer *bp; + int x, sizerr, f, amt; + off_t i; + char buf[BUFSIZ]; + + for (x = 0; x < argc; x++) { + name = argv[x]; + if ((f = open(name, 0)) < 0) { + error("rcp: %s: %s\n", name, strerror(errno)); + continue; + } + if (fstat(f, &stb) < 0) + goto notreg; + switch (stb.st_mode&S_IFMT) { + + case S_IFREG: + break; + + case S_IFDIR: + if (iamrecursive) { + (void) close(f); + rsource(name, &stb); + continue; + } + /* fall into ... */ + default: +notreg: + (void) close(f); + error("rcp: %s: not a plain file\n", name); + continue; + } + last = strrchr(name, '/'); + if (last == 0) + last = name; + else + last++; + if (pflag) { + /* + * Make it compatible with possible future + * versions expecting microseconds. + */ + (void) sprintf(buf, "T%ld 0 %ld 0\n", + stb.st_mtime, stb.st_atime); + (void) write(rem, buf, strlen(buf)); + if (response() < 0) { + (void) close(f); + continue; + } + } + (void) sprintf(buf, "C%04o %ld %s\n", + stb.st_mode&07777, stb.st_size, last); + (void) write(rem, buf, strlen(buf)); + if (response() < 0) { + (void) close(f); + continue; + } + if ((bp = allocbuf(&buffer, f, BUFSIZ)) == 0) { + (void) close(f); + continue; + } + sizerr = 0; + for (i = 0; i < stb.st_size; i += bp->cnt) { + amt = bp->cnt; + if (i + amt > stb.st_size) + amt = stb.st_size - i; + if (sizerr == 0 && read(f, bp->buf, amt) != amt) + sizerr = 1; + (void) write(rem, bp->buf, amt); + } + (void) close(f); + if (sizerr == 0) + ga(); + else + error("rcp: %s: file changed size\n", name); + (void) response(); + } +} + + +void +rsource(name, statp) + char *name; + struct stat *statp; +{ + DIR *d = opendir(name); + char *last; + struct dirent *dp; + char buf[BUFSIZ]; + char *bufv[1]; + + if (d == 0) { + error("rcp: %s: %s\n", name, strerror(errno)); + return; + } + last = strrchr(name, '/'); + if (last == 0) + last = name; + else + last++; + if (pflag) { + (void) sprintf(buf, "T%ld 0 %ld 0\n", + statp->st_mtime, statp->st_atime); + (void) write(rem, buf, strlen(buf)); + if (response() < 0) { + closedir(d); + return; + } + } + (void) sprintf(buf, "D%04o %d %s\n", statp->st_mode&07777, 0, last); + (void) write(rem, buf, strlen(buf)); + if (response() < 0) { + closedir(d); + return; + } + while (dp = readdir(d)) { + if (dp->d_ino == 0) + continue; + if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, "..")) + continue; + if (strlen(name) + 1 + strlen(dp->d_name) >= BUFSIZ - 1) { + error("%s/%s: Name too long.\n", name, dp->d_name); + continue; + } + (void) sprintf(buf, "%s/%s", name, dp->d_name); + bufv[0] = buf; + source(1, bufv); + } + closedir(d); + (void) write(rem, "E\n", 2); + (void) response(); +} + +int +response() +{ + char resp, c, rbuf[BUFSIZ], *cp = rbuf; + + if (read(rem, &resp, 1) != 1) + lostconn(0); + switch (resp) { + + case 0: /* ok */ + return (0); + + default: + *cp++ = resp; + /* fall into... */ + case 1: /* error, followed by err msg */ + case 2: /* fatal error, "" */ + do { + if (read(rem, &c, 1) != 1) + lostconn(0); + *cp++ = c; + } while (cp < &rbuf[BUFSIZ] && c != '\n'); + if (iamremote == 0) + (void) write(2, rbuf, cp - rbuf); + errs++; + if (resp == 1) + return (-1); + exit(1); + } + /*NOTREACHED*/ +} + +void +lostconn(sig) +int sig; +{ + + if (iamremote == 0) + fprintf(stderr, "rcp: lost connection\n"); + exit(1); +} + +void +sink(argc, argv) + int argc; + char **argv; +{ + off_t i, j, size; + char *targ, *whopp, *cp; + int of, mode, wrerr, exists, first, count, amt; + struct buffer *bp; + static struct buffer buffer; + struct stat stb; + int targisdir = 0; + int mask = umask(0); + char *myargv[1]; + char cmdbuf[BUFSIZ], nambuf[BUFSIZ]; + int setimes = 0; + struct utimbuf utimbuf; +#define atime utimbuf.actime +#define mtime utimbuf.modtime + time_t dummy; +#define SCREWUP(str) { whopp = str; goto screwup; } + +#ifdef NOT_DEF + seteuid(pwd->pw_uid); +#endif + if (!pflag) + (void) umask(mask); + if (argc != 1) { + error("rcp: ambiguous target\n"); + exit(1); + } + targ = *argv; + if (targetshouldbedirectory) + verifydir(targ); + ga(); + if (stat(targ, &stb) == 0 && (stb.st_mode & S_IFMT) == S_IFDIR) + targisdir = 1; + for (first = 1; ; first = 0) { + cp = cmdbuf; + if (read(rem, cp, 1) <= 0) + return; + if (*cp++ == '\n') + SCREWUP("unexpected '\\n'"); + do { + if (read(rem, cp, 1) != 1) + SCREWUP("lost connection"); + } while (*cp++ != '\n'); + *cp = 0; + if (cmdbuf[0] == '\01' || cmdbuf[0] == '\02') { + if (iamremote == 0) + (void) write(2, cmdbuf+1, strlen(cmdbuf+1)); + if (cmdbuf[0] == '\02') + exit(1); + errs++; + continue; + } + *--cp = 0; + cp = cmdbuf; + if (*cp == 'E') { + ga(); + return; + } + +#define getnum(t) (t) = 0; while (isdigit(*cp)) (t) = (t) * 10 + (*cp++ - '0'); + if (*cp == 'T') { + setimes++; + cp++; + getnum(mtime); + if (*cp++ != ' ') + SCREWUP("mtime.sec not delimited"); + getnum(dummy); + if (*cp++ != ' ') + SCREWUP("mtime.usec not delimited"); + getnum(atime); + if (*cp++ != ' ') + SCREWUP("atime.sec not delimited"); + getnum(dummy); + if (*cp++ != '\0') + SCREWUP("atime.usec not delimited"); + ga(); + continue; + } + if (*cp != 'C' && *cp != 'D') { + /* + * Check for the case "rcp remote:foo\* local:bar". + * In this case, the line "No match." can be returned + * by the shell before the rcp command on the remote is + * executed so the ^Aerror_message convention isn't + * followed. + */ + if (first) { + error("%s\n", cp); + exit(1); + } + SCREWUP("expected control record"); + } + cp++; + mode = 0; + for (; cp < cmdbuf+5; cp++) { + if (*cp < '0' || *cp > '7') + SCREWUP("bad mode"); + mode = (mode << 3) | (*cp - '0'); + } + if (*cp++ != ' ') + SCREWUP("mode not delimited"); + size = 0; + while (isdigit(*cp)) + size = size * 10 + (*cp++ - '0'); + if (*cp++ != ' ') + SCREWUP("size not delimited"); + if (targisdir) + (void) sprintf(nambuf, "%s%s%s", targ, + *targ ? "/" : "", cp); + else + (void) strcpy(nambuf, targ); + exists = stat(nambuf, &stb) == 0; + if (cmdbuf[0] == 'D') { + if (exists) { + if ((stb.st_mode&S_IFMT) != S_IFDIR) { + errno = ENOTDIR; + goto bad; + } + if (pflag) + (void) chmod(nambuf, mode); + } else if (mkdir(nambuf, mode) < 0) + goto bad; + myargv[0] = nambuf; + sink(1, myargv); + if (setimes) { + setimes = 0; + if (utime(nambuf, &utimbuf) < 0) + error("rcp: can't set times on %s: %s\n", + nambuf, strerror(errno)); + } + continue; + } + if ((of = creat(nambuf, mode)) < 0) { + bad: + error("rcp: %s: %s\n", nambuf, strerror(errno)); + continue; + } + if (exists && pflag) + (void) chmod(nambuf, mode); + ga(); + if ((bp = allocbuf(&buffer, of, BUFSIZ)) == 0) { + (void) close(of); + continue; + } + cp = bp->buf; + count = 0; + wrerr = 0; + for (i = 0; i < size; i += BUFSIZ) { + amt = BUFSIZ; + if (i + amt > size) + amt = size - i; + count += amt; + do { + j = read(rem, cp, amt); + if (j <= 0) + exit(1); + amt -= j; + cp += j; + } while (amt > 0); + if (count == bp->cnt) { + if (wrerr == 0 && + write(of, bp->buf, count) != count) + wrerr++; + count = 0; + cp = bp->buf; + } + } + if (count != 0 && wrerr == 0 && + write(of, bp->buf, count) != count) + wrerr++; + (void) close(of); + (void) response(); + if (setimes) { + setimes = 0; + if (utime(nambuf, &utimbuf) < 0) + error("rcp: can't set times on %s: %s\n", + nambuf, strerror(errno)); + } + if (wrerr) + error("rcp: %s: %s\n", nambuf, strerror(errno)); + else + ga(); + } +screwup: + error("rcp: protocol screwup: %s\n", whopp); + exit(1); +} + +struct buffer * +allocbuf(bp, fd, blksize) + struct buffer *bp; + int fd, blksize; +{ + struct stat stb; + int size; + + if (fstat(fd, &stb) < 0) { + error("rcp: fstat: %s\n", strerror(errno)); + return ((struct buffer *)0); + } + size= 0; +#if NOT_DEF + size = roundup(stb.st_blksize, blksize); +#endif + if (size == 0) + size = blksize; + if (bp->cnt < size) { + if (bp->buf != 0) + free(bp->buf); + bp->buf = (char *)malloc((unsigned) size); + if (bp->buf == 0) { + error("rcp: malloc: out of memory\n"); + return ((struct buffer *)0); + } + } + bp->cnt = size; + return (bp); +} + +/*VARARGS1*/ +#if __STDC__ +void +error (char *fmt, ...) +#else +error(fmt) +char *fmt; +#endif +{ + char buf[BUFSIZ], *cp = buf; + va_list ap; + + va_start(ap, fmt); + + errs++; + *cp++ = 1; + (void) vsprintf(cp, fmt, ap); + va_end(ap); + (void) write(rem, buf, strlen(buf)); + if (iamremote == 0) + (void) write(2, buf+1, strlen(buf+1)); +} + +void +usage() +{ + fprintf(stderr, "Usage: rcp [-p] f1 f2; or: rcp [-rp] f1 ... fn d2\n"); +} diff --git a/commands/simple/rdate.c b/commands/simple/rdate.c new file mode 100755 index 000000000..edb4b39f0 --- /dev/null +++ b/commands/simple/rdate.c @@ -0,0 +1,109 @@ +/* rdate 1.0 - Set time&date from remote host Author: Kees J. Bot + * 12 Oct 1995 + */ +#define nil 0 +#include <sys/types.h> +#include <stdio.h> +#include <time.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/ioctl.h> +#include <net/hton.h> +#include <net/netlib.h> +#include <net/gen/in.h> +#include <net/gen/netdb.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> + +void report(const char *label) +{ + fprintf(stderr, "rdate: %s: %s\n", label, strerror(errno)); +} + +void fatal(const char *label) +{ + report(label); + exit(1); +} + +int main(int argc, char **argv) +{ + char *tcp_device; + int fd; + int i; + struct servent *servent; + struct hostent *hostent; + u16_t time_port; + nwio_tcpconf_t tcpconf; + nwio_tcpcl_t tcpcl; + u32_t net_time; + time_t unix_time; + + if (argc <= 1) { + fprintf(stderr, "Usage: rdate host ...\n"); + exit(1); + } + + /* Look up the port number of the TCP service "time". */ + if ((servent= getservbyname("time", "tcp")) == nil) { + fprintf(stderr, "rdate: \"time\": unknown service\n"); + exit(1); + } + time_port= servent->s_port; + + if ((tcp_device= getenv("TCP_DEVICE")) == nil) tcp_device= TCP_DEVICE; + + if ((fd= open(tcp_device, O_RDWR)) < 0) fatal(tcp_device); + + /* Try each host on the command line. */ + for (i= 1; i < argc; i++) { + if ((hostent= gethostbyname(argv[i])) == nil) { + fprintf(stderr, "rdate: %s: unknown host\n", argv[i]); + continue; + } + + /* Configure a TCP channel and connect to the remote host. */ + + tcpconf.nwtc_flags= NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; + memcpy(&tcpconf.nwtc_remaddr, hostent->h_addr, 4); + tcpconf.nwtc_remport= time_port; + if (ioctl(fd, NWIOSTCPCONF, &tcpconf) == -1) fatal(tcp_device); + + tcpcl.nwtcl_flags= 0; + if (ioctl(fd, NWIOTCPCONN, &tcpcl) < 0) { + report(argv[i]); + continue; + } + + /* Read four bytes to obtain the time. */ + switch (read(fd, &net_time, sizeof(net_time))) { + case -1: + report(argv[i]); + continue; + default: + fprintf(stderr, "rdate: %s: short read\n", argv[i]); + continue; + case sizeof(net_time): + break; + } + break; + } + if (i == argc) exit(1); + + /* Internet time is in seconds since 1900, UNIX time is in seconds + * since 1970. + */ + unix_time= ntohl(net_time) - 2208988800; + + /* Try to set the time and tell us about it. */ + if (stime(&unix_time) < 0) { + printf("time on "); + } else { + printf("time set to "); + } + printf("%s: %s", argv[i], ctime(&unix_time)); + exit(0); +} diff --git a/commands/simple/readall.c b/commands/simple/readall.c new file mode 100755 index 000000000..9e56e3497 --- /dev/null +++ b/commands/simple/readall.c @@ -0,0 +1,139 @@ +/* readall - read a whole device fast Author: Andy Tanenbaum */ + +/* Readall reads all the blocks on a device as fast as it can. If it hits + * an error, it stops reading in large units and reads one block at a time. + * It reports on all errors it finds. + * + * If the -b flag is given, the output is a shell script that can be run + * to mark all the bad blocks. + * + * If the -t flag is given, only the total numbers of blocks is reported. + * + * Examples of usage: + * readall /dev/hd1 # read /dev/hd1 + * readall -b /dev/hd2 # prepare bad block list on stdout + * readall -t /dev/ram # report size of ram disk + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#define CHUNK 25 /* max number of blocks read at once */ +#define BLOCK_SIZE 1024 /* size of a block */ +#define RESUME 200 /* # good reads before going back to CHUNK */ +#define DIVISOR 50 /* how often to print statistics */ +#define STORE 4096 /* save this many bad blocks for summary */ + +int chunk = CHUNK; /* current number of blocks being read */ +long goodies; /* incremented on good reads */ +long errors; /* number of errors so far */ +int normal = 1; /* set unless -b flag is given */ +int total = 0; /* unset unless -t flag is given */ +char *name; /* name of special file being read */ + +char a[CHUNK * BLOCK_SIZE]; /* read buffer */ +long rotten[STORE]; /* list of bad blocks */ + +_PROTOTYPE(int main, (int argc, char **argv)); +static _PROTOTYPE(void output, (long blocks_read)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int fd, s, i, badprinted; + long b = 0; + char *p; + + if (argc != 2 && argc != 3) { + fprintf(stderr, "Usage: readall [-b | -t] file\n"); + exit(1); + } + i = 1; + + p = argv[1]; + if (*p == '-' && *(p + 1) == 'b' && *(p + 2) == '\0') { + normal = 0; + i++; + name = argv[i]; + } + if (*p == '-' && *(p + 1) == 't' && *(p + 2) == '\0') { + normal = 0; + total = 1; + i++; + name = argv[i]; + } + fd = open(argv[i], O_RDONLY); + if (fd < 0) { + fprintf(stderr, "%s is not readable\n", argv[i]); + exit(1); + } + + /* Read the entire file. Try it in large chunks, but if an error + * occurs, go to single reads for a while. */ + while (1) { + lseek(fd, BLOCK_SIZE * b, SEEK_SET); + s = read(fd, a, BLOCK_SIZE * chunk); + if (s == BLOCK_SIZE * chunk) { + /* Normal read, no errors. */ + b += chunk; + goodies++; + if (chunk == 1) { + if (goodies >= RESUME && b % DIVISOR == 0) + chunk = CHUNK; + } + } else if (s < 0) { + /* I/O error. */ + if (chunk != 1) { + chunk = 1; /* regress to single block mode */ + continue; + } + if (errors == STORE) { + fprintf(stderr, + "\n%ld Bad blocks is too many. Exiting\n", + errors); + exit(1); + } + rotten[(int) errors] = b; /* log the error */ + b += chunk; + errors++; + } else { + /* End of file. */ + b += s / BLOCK_SIZE; + if (normal) { + output(b); + fprintf(stderr, "\n"); + } + if (total) printf("%8ld\n", b); + if ((errors == 0) || total) exit(0); + badprinted = 0; + if (normal) printf("Summary of bad blocks\n"); + + /* Print summary of bad blocks, possibly as shell script. */ + for (i = 0; i < errors; i++) { + if (normal == 0 && badprinted == 0) { + printf("badblocks %s ", name); + badprinted = 1; + } + printf("%6ld ", rotten[i]); + if ((i + 1) % 7 == 0) { + printf("\n"); + badprinted = 0; + } + } + printf("\n"); + exit(0); + } + if (normal && b % DIVISOR == 0) output(b); + } +} + +static void output(blocks_read) +long blocks_read; +{ + fprintf(stderr, "%8ld blocks read, %5ld errors\r", blocks_read, errors); + fflush(stderr); +} diff --git a/commands/simple/readfs.c b/commands/simple/readfs.c new file mode 100755 index 000000000..2389d1d9d --- /dev/null +++ b/commands/simple/readfs.c @@ -0,0 +1,601 @@ +/* readfs - read a MINIX file system Author: Paul Polderman */ + +/* Command: readfs - read and extract a MINIX filesystem. + * + * Syntax: readfs [-li] block-special [directory] + * + * Flags: -l: Extract files and dirs and produce a mkfs-listing on stdout + * -i: Information only: give the listing, but do not extract files. + * -d: Don't extract regular files, just the skeleton please. + * + * Examples: readfs /dev/fd1 # extract all files from /dev/fd1. + * readfs -i /dev/hd2 # see what's on /dev/hd2. + * readfs -l /dev/at0 rootfs # extract and list the filesystem + * # of /dev/at0 and put the tree + * # in the directory `rootfs'. + * + * Readfs reads a MINIX filesystem and extracts recursively all directories + * and files, and (optionally) produces a mkfs-listing of them on stdout. + * The root directory contents are placed in the current directory, unless + * a directory is given as argument, in which case the contents are put there. + * Readfs tries to restore the attributes (mode/uid/gid/time) of the files + * extracted to those of the original files. + * Special files are created as ordinary files, but the mkfs-listing + * enables mkfs to restore them to original. + */ + +#include <sys/types.h> +#include <sys/dir.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <limits.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <utime.h> +#include <dirent.h> + +#define BLOCK_SIZE STATIC_BLOCK_SIZE + +#include <minix/config.h> +#include <minix/const.h> +#include <minix/type.h> +#include "../../servers/fs/const.h" +#include "../../servers/fs/type.h" +#include "../../servers/fs/buf.h" +#include "../../servers/fs/super.h" + +#undef printf /* Definition used only in the kernel */ +#include <stdio.h> + +/* Compile with -I/user0/ast/minix + * (i.e. the directory containing the MINIX system sources) + * + * Author: Paul Polderman (polder@cs.vu.nl) April 1987 + */ + +char verbose = 0; /* give a mkfs-listing of the filesystem */ + /* And extracts its contents. */ +char noaction = 0; /* just give a mkfs-listing, do not extract + * files. */ +char nofiles = 0; /* only extract the skeleton FS structure */ + +struct super_block sb; +char pathname[1024]; +int inodes_per_block; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void get_flags, (char *flags)); +_PROTOTYPE(void readfs, (char *special_file, char *directory)); +_PROTOTYPE(int get_inode, (int fd, Ino_t inum, d1_inode * ip)); +_PROTOTYPE(void dump_dir, (int special, d1_inode * ip, char *directory)); +_PROTOTYPE(int dump_file, (int special, d1_inode * ip, char *filename)); +_PROTOTYPE(int get_fileblock, (int special, d1_inode * ip, block_t b, struct buf * bp)); +_PROTOTYPE(int get_block, (int fd, block_t block, struct buf * bp, int type)); +_PROTOTYPE(int get_rawblock, (int special, block_t blockno, char *bufp)); +_PROTOTYPE(void restore, (char *name, d1_inode * ip)); +_PROTOTYPE(void show_info, (char *name, d1_inode * ip, char *path)); +_PROTOTYPE(void do_indent, (int i)); +_PROTOTYPE(int Mkdir, (char *directory)); + +int main(argc, argv) +int argc; +char **argv; +{ + switch (argc) { + case 2: + pathname[0] = '\0'; + readfs(argv[1], pathname); + break; + case 3: + if (argv[1][0] == '-') { + get_flags(&argv[1][1]); + pathname[0] = '\0'; + readfs(argv[2], pathname); + } else { + strcpy(pathname, argv[2]); + readfs(argv[1], pathname); + } + break; + case 4: + if (argv[1][0] == '-') { + get_flags(&argv[1][1]); + strcpy(pathname, argv[3]); + readfs(argv[2], pathname); + break; + } /* else fall through .. */ + default: + fprintf(stderr, "Usage: %s [-li] <special> [dirname]\n", argv[0]); + exit(1); + } + return(0); +} + +void get_flags(flags) +register char *flags; +{ + while (*flags) { + switch (*flags) { + case 'L': + case 'l': verbose = 1; break; + case 'I': + case 'i': + noaction = 1; + verbose = 1; + break; + case 'D': + case 'd': nofiles = 1; break; + default: + fprintf(stderr, "Bad flag: %c\n", *flags); + break; + } + flags++; + } +} + +#define zone_shift (sb.s_log_zone_size) /* zone to block ratio */ + +void readfs(special_file, directory) +char *special_file, *directory; +/* Readfs: opens the given special file (with MINIX filesystem), + * and extracts its contents into the given directory. + */ +{ + d1_inode root_inode; + int special, magic; + off_t super_b; + + umask(0); + + /* Open the special file */ + if ((special = open(special_file, O_RDONLY)) < 0) { + fprintf(stderr, "cannot open %s\n", special_file); + return; + } + + /* Read the superblock */ + super_b = (off_t) 1 *(off_t) BLOCK_SIZE; + if (lseek(special, super_b, SEEK_SET) != super_b) { + fprintf(stderr, "cannot seek to superblock\n"); + return; + } + if (read(special, (char *) &sb, sizeof(struct super_block)) + != sizeof(struct super_block)) { + fprintf(stderr, "cannot read superblock\n"); + return; + } + + /* The number of inodes in a block differs in V1 and V2. */ + magic = sb.s_magic; + if (magic == SUPER_MAGIC || magic == SUPER_REV) { + inodes_per_block = V1_INODES_PER_BLOCK; + } else { + inodes_per_block = V2_INODES_PER_BLOCK(BLOCK_SIZE); + } + + /* Is it really a MINIX filesystem ? */ + if (magic != SUPER_MAGIC && magic != SUPER_V2) { + fprintf(stderr, "%s is not a valid MINIX filesystem\n", special_file); + return; + } + + /* Fetch the inode of the root directory */ + if (get_inode(special, (ino_t) ROOT_INODE, &root_inode) < 0) { + fprintf(stderr, "cannot get inode of root directory\n"); + return; + } + + /* Print number of blocks and inodes */ + if (verbose) printf("boot\n%ld %d\n", + (block_t) sb.s_nzones << zone_shift, sb.s_ninodes); + + /* Extract (recursively) the root directory */ + dump_dir(special, &root_inode, directory); +} + +/* Different type of blocks: (used in routine get_block for caching) */ + +#define B_INODE 0 /* Cache #0 is the inode cache */ +#define B_INDIRECT 1 /* Cache #1 is the (dbl) indirect block cache */ +#define B_DATA 2 /* No cache for data blocks (only read once) */ + +int get_inode(fd, inum, ip) +int fd; +ino_t inum; +d1_inode *ip; + +/* Get inode `inum' from the MINIX filesystem. (Uses the inode-cache) */ +{ + struct buf bp; + block_t block; + block_t ino_block; + unsigned short ino_offset; + + /* Calculate start of i-list */ + block = 1 + 1 + sb.s_imap_blocks + sb.s_zmap_blocks; + + /* Calculate block with inode inum */ + ino_block = ((inum - 1) / inodes_per_block); + ino_offset = ((inum - 1) % inodes_per_block); + block += ino_block; + + /* Fetch the block */ + if (get_block(fd, block, &bp, B_INODE) == 0) { + memcpy((void *) ip, (void *) &bp.b_v1_ino[ino_offset], sizeof(d1_inode)); + return(0); + } + + /* Oeps, foutje .. */ + fprintf(stderr, "cannot find inode %d\n", inum); + return(-1); +} + +static int indent = 0; /* current indent (used for mkfs-listing) */ + +void dump_dir(special, ip, directory) +int special; +d1_inode *ip; +char *directory; +/* Make the given directory (if non-NULL), + * and recursively extract its contents. + */ +{ + register struct direct *dp; + register int n_entries; + register char *name; + block_t b = 0; + d1_inode dip; + struct buf bp; + + if (verbose) { + show_info(directory, ip, ""); + indent++; + } + if (!noaction && *directory) { + /* Try to make the directory if not already there */ + if (Mkdir(directory) != 0 || chdir(directory) < 0) { + fprintf(stderr, "Mkdir %s failed\n", directory); + return; + } + } + for (name = directory; *name; name++) /* Find end of pathname */ + ; + *name++ = '/'; /* Add trailing slash */ + + n_entries = (int) (ip->d1_size / (off_t) sizeof(struct direct)); + while (n_entries > 0) { + + /* Read next block of the directory */ + if (get_fileblock(special, ip, b, &bp) < 0) return; + dp = &bp.b_dir[0]; + if (b++ == (block_t) 0) { + dp += 2; /* Skip "." and ".." */ + n_entries -= 2; + } + + /* Extract the files/directories listed in the block */ + while (n_entries-- > 0 && dp < &bp.b_dir[NR_DIR_ENTRIES(BLOCK_SIZE)]) { + if (dp->d_ino != (ino_t) 0) { + if (get_inode(special, dp->d_ino, &dip) < 0) { + /* Bad luck */ + dp++; + continue; + } + + /* Add new pathname-component to `pathname'. */ + strncpy(name, dp->d_name, (size_t) NAME_MAX); + name[NAME_MAX] = '\0'; + + /* Call the right routine */ + if ((dip.d1_mode & I_TYPE) == I_DIRECTORY) + dump_dir(special, &dip, name); + else + dump_file(special, &dip, name); + } + dp++; /* Next entry, please. */ + } + } + *--name = '\0'; /* Restore `pathname' to what it was. */ + if (!noaction && *directory) { + chdir(".."); /* Go back up. */ + restore(directory, ip); /* Restore mode/owner/accesstime */ + } + if (verbose) { + do_indent(--indent); /* Let mkfs know we are done */ + printf("$\n"); /* with this directory. */ + } +} + +int dump_file(special, ip, filename) +int special; +d1_inode *ip; +char *filename; +/* Extract given filename from the MINIX-filesystem, + * and store it on the local filesystem. + */ +{ + int file; + block_t b = 0; + struct buf bp; + off_t size; + + if (nofiles && (ip->d1_mode & I_TYPE) == I_REGULAR) return(0); + + if (verbose) show_info(filename, ip, pathname); + + if (noaction) return(0); + + if (access(filename, 0) == 0) { + /* Should not happen, but just in case .. */ + fprintf(stderr, "Will not create %s: file exists\n", filename); + return(-1); + } + if ((file = creat(filename, (ip->d1_mode & ALL_MODES))) < 0) { + fprintf(stderr, "cannot create %s\n", filename); + return(-1); + } + + /* Don't try to extract /dev/hd0 */ + if ((ip->d1_mode & I_TYPE) == I_REGULAR) { + size = ip->d1_size; + while (size > (off_t) 0) { + /* Get next block of file */ + if (get_fileblock(special, ip, b++, &bp) < 0) { + close(file); + return(-1); + } + + /* Write it to the file */ + if (size > (off_t) BLOCK_SIZE) + write(file, bp.b_data, BLOCK_SIZE); + else + write(file, bp.b_data, (int) size); + + size -= (off_t) BLOCK_SIZE; + } + } + close(file); + restore(filename, ip); /* Restore mode/owner/filetimes */ + return(0); +} + +int get_fileblock(special, ip, b, bp) +int special; +d1_inode *ip; +block_t b; +struct buf *bp; +/* Read the `b'-th block from the file whose inode is `ip'. */ +{ + zone_t zone, ind_zone; + block_t z, zone_index; + int r; + + /* Calculate zone in which the datablock number is contained */ + zone = (zone_t) (b >> zone_shift); + + /* Calculate index of the block number in the zone */ + zone_index = b - ((block_t) zone << zone_shift); + + /* Go get the zone */ + if (zone < (zone_t) V1_NR_DZONES) { /* direct block */ + zone = ip->d1_zone[(int) zone]; + z = ((block_t) zone << zone_shift) + zone_index; + r = get_block(special, z, bp, B_DATA); + return(r); + } + + /* The zone is not a direct one */ + zone -= (zone_t) V1_NR_DZONES; + + /* Is it single indirect ? */ + if (zone < (zone_t) V1_INDIRECTS) { /* single indirect block */ + ind_zone = ip->d1_zone[V1_NR_DZONES]; + } else { /* double indirect block */ + /* Fetch the double indirect block */ + ind_zone = ip->d1_zone[V1_NR_DZONES + 1]; + z = (block_t) ind_zone << zone_shift; + r = get_block(special, z, bp, B_INDIRECT); + if (r < 0) return(r); + + /* Extract the indirect zone number from it */ + zone -= (zone_t) V1_INDIRECTS; + + /* The next line assumes a V1 file system only! */ + ind_zone = bp->b_v1_ind[(int) (zone / V1_INDIRECTS)]; + zone %= (zone_t) V1_INDIRECTS; + } + + /* Extract the datablock number from the indirect zone */ + z = (block_t) ind_zone << zone_shift; + r = get_block(special, z, bp, B_INDIRECT); + if (r < 0) return(r); + + /* The next line assumes a V1 file system only! */ + zone = bp->b_v1_ind[(int) zone]; + + /* Calculate datablock number to be fetched */ + z = ((block_t) zone << zone_shift) + zone_index; + r = get_block(special, z, bp, B_DATA); + return(r); +} + +/* The following routines simulate a LRU block cache. + * + * Definition of a cache block: + */ + +struct cache_block { + block_t b_block; /* block number of block */ + long b_access; /* counter value of last access */ + char b_buf[BLOCK_SIZE]; /* buffer for block */ +}; + +#define NR_CACHES 2 /* total number of caches */ +#define NR_CBLOCKS 5 /* number of blocks in a cache */ + +static struct cache_block cache[NR_CACHES][NR_CBLOCKS]; +static long counter = 0L; /* Counter used as a sense of time. */ + /* Incremented after each cache operation. */ + +int get_block(fd, block, bp, type) +int fd; +block_t block; +struct buf *bp; +int type; +/* Get the requested block from the device with filedescriptor fd. + * If it is in the cache, no (floppy-) disk access is needed, + * if not, allocate a cache block and read the block into it. + */ +{ + register int i; + register struct cache_block *cache_p, *cp; + + if (block == (block_t) NO_ZONE) { + /* Should never happen in a good filesystem. */ + fprintf(stderr, "get_block: NO_ZONE requested !\n"); + return(-1); + } + if (type < 0 || type >= NR_CACHES) /* No cache for this type */ + return(get_rawblock(fd, block, (char *) bp)); + + cache_p = cache[type]; + cp = (struct cache_block *) 0; + + /* First find out if block requested is in the cache */ + for (i = 0; i < NR_CBLOCKS; i++) { + if (cache_p[i].b_block == block) { /* found right block */ + cp = &cache_p[i]; + break; + } + } + + if (cp == (struct cache_block *) 0) { /* block is not in cache */ + cp = cache_p; /* go find oldest buffer */ + for (i = 0; i < NR_CBLOCKS; i++) { + if (cache_p[i].b_access < cp->b_access) cp = &cache_p[i]; + } + + /* Fill the buffer with the right block */ + if (get_rawblock(fd, block, cp->b_buf) < 0) return(-1); + } + + /* Update/store last access counter */ + cp->b_access = ++counter; + cp->b_block = block; + memcpy((void *) bp, (void *) cp->b_buf, BLOCK_SIZE); + return(0); +} + +int get_rawblock(special, blockno, bufp) +int special; +block_t blockno; +char *bufp; +/* Read a block from the disk. */ +{ + off_t pos; + + /* Calculate the position of the block on the disk */ + pos = (off_t) blockno *(off_t) BLOCK_SIZE; + + /* Read the block from the disk */ + if (lseek(special, pos, SEEK_SET) == pos + && read(special, bufp, BLOCK_SIZE) == BLOCK_SIZE) + return(0); + + /* Should never get here .. */ + fprintf(stderr, "read block %d failed\n", blockno); + return(-1); +} + +void restore(name, ip) +char *name; +d1_inode *ip; +/* Restores given file's attributes. + * `ip' contains the attributes of the file on the MINIX filesystem, + * `name' is the filename of the extracted file on the local filesystem. + */ +{ + long ttime[2]; + + chown(name, ip->d1_uid, ip->d1_gid); /* Fails if not superuser */ + chmod(name, (ip->d1_mode & ALL_MODES)); + ttime[0] = ttime[1] = ip->d1_mtime; + utime(name, (struct utimbuf *) ttime); +} + +/* Characters to use as prefix to `mkfs' mode field */ + +static char special_chars[] = { + '-', /* I_REGULAR */ + 'c', /* I_CHAR_SPECIAL */ + 'd', /* I_DIRECTORY */ + 'b' /* I_BLOCK_SPECIAL */ +}; + +void show_info(name, ip, path) +char *name; +d1_inode *ip; +char *path; +/* Show information about the given file/dir in `mkfs'-format */ +{ + char c1, c2, c3; + + c1 = special_chars[(ip->d1_mode >> 13) & 03]; + c2 = ((ip->d1_mode & ALL_MODES & ~RWX_MODES) == I_SET_UID_BIT) ? 'u' : '-'; + c3 = ((ip->d1_mode & ALL_MODES & ~RWX_MODES) == I_SET_GID_BIT) ? 'g' : '-'; + + if (*name) { + do_indent(indent); + printf("%-14s ", name); + } + printf("%c%c%c%03o %d %d", c1, c2, c3, + (ip->d1_mode & RWX_MODES), ip->d1_uid, ip->d1_gid); + + switch (ip->d1_mode & I_TYPE) { + case I_DIRECTORY: + break; + case I_CHAR_SPECIAL: /* Print major and minor dev numbers */ + printf(" %d %d", (ip->d1_zone[0] >> MAJOR) & 0377, + (ip->d1_zone[0] >> MINOR) & 0377); + break; + case I_BLOCK_SPECIAL: /* Print major and minor dev numbers */ + printf(" %d %d", (ip->d1_zone[0] >> MAJOR) & 0377, + (ip->d1_zone[0] >> MINOR) & 0377); + /* Also print the number of blocks on the device */ + printf(" %ld", (ip->d1_size / (off_t) BLOCK_SIZE)); + break; + default: /* Just print the pathname */ + printf(" %s", path); + break; + } + putchar('\n'); +} + +#define INDENT_SIZE 4 + +void do_indent(i) +int i; +{ + i *= INDENT_SIZE; + while (i-- > 0) putchar(' '); +} + +int Mkdir(directory) +char *directory; +/* Make a directory, return exit status. + * This routine is not necessary on systems that + * have a system call to make directories. + */ +{ + int pid, status; + + if ((pid = fork()) == 0) { + execl("/bin/Mkdir", "Mkdir", directory, (char *) 0); + execl("/usr/bin/Mkdir", "Mkdir", directory, (char *) 0); + exit(1); + } else if (pid < 0) + return(-1); + while (wait(&status) != pid); + return(status); +} diff --git a/commands/simple/remsync.c b/commands/simple/remsync.c new file mode 100755 index 000000000..5dabada9a --- /dev/null +++ b/commands/simple/remsync.c @@ -0,0 +1,1551 @@ +/* remsync 1.5 - remotely synchronize file trees Author: Kees J. Bot + * 10 Jun 1994 + */ +#define nil 0 +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <dirent.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <time.h> +#include <utime.h> + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +#ifndef major +#define major(dev) ((int) ((dev) >> 8)) +#define minor(dev) ((int) ((dev) & 0xFF)) +#endif + +#ifndef S_ISLNK +/* There were no symlinks in medieval times. */ +#define S_ISLNK(mode) (0) +#define lstat stat +#define symlink(path1, path2) (errno= ENOSYS, -1) +#define readlink(path, buf, len) (errno= ENOSYS, -1) +#endif + +int sflag; /* Make state file. */ +int dflag; /* Make list of differences. */ +int uflag; /* Only update files with newer versions. */ +int xflag; /* Do not cross device boundaries. */ +int Dflag; /* Debug: Readable differences, no file contents. */ +int vflag; /* Verbose. */ + +#define NO_DEVICE (-1) +dev_t xdev= NO_DEVICE; /* The device that you should stay within. */ + +int excode= 0; /* Exit(excode); */ + +#define BASE_INDENT 2 /* State file basic indent. */ + +void report(const char *label) +{ + fprintf(stderr, "remsync: %s: %s\n", label, strerror(errno)); + excode= 1; +} + +void fatal(const char *label) +{ + report(label); + exit(1); +} + +void *allocate(void *mem, size_t size) +{ + if ((mem= mem == nil ? malloc(size) : realloc(mem, size)) == nil) { + fprintf(stderr, "remsync: Out of memory: %s\n", + strerror(errno)); + exit(1); + } + return mem; +} + +void deallocate(void *mem) +{ + if (mem != nil) free(mem); +} + +/* One needs to slowly forget two sets of objects: for the code that reads + * the state file, and for the code that traverses trees. + */ +int keep; +#define KEEP_STATE 0 +#define KEEP_TRAVERSE 1 + +void forget(void *mem) +/* Some objects must be deleted in time, but not just yet. */ +{ + static void *death_row[2][50]; + static void **dp[2]= { death_row[0], death_row[1] }; + + deallocate(*dp[keep]); + *dp[keep]++= mem; + if (dp[keep] == arraylimit(death_row[keep])) dp[keep]= death_row[keep]; +} + +char *copystr(const char *s) +{ + char *c= allocate(nil, (strlen(s) + 1) * sizeof(c[0])); + strcpy(c, s); + return c; +} + +typedef struct pathname { + char *path; /* The actual pathname. */ + size_t idx; /* Index for the terminating null byte. */ + size_t lim; /* Actual length of the path array. */ +} pathname_t; + +void path_init(pathname_t *pp) +/* Initialize a pathname to the null string. */ +{ + pp->path= allocate(nil, (pp->lim= 16) * sizeof(pp->path[0])); + pp->path[pp->idx= 0]= 0; +} + +void path_add(pathname_t *pp, const char *name) +/* Add a component to a pathname. */ +{ + size_t lim; + char *p; + int slash; + + lim= pp->idx + strlen(name) + 2; + + if (lim > pp->lim) { + pp->lim= lim + lim/2; /* add an extra 50% growing space. */ + pp->path= allocate(pp->path, pp->lim * sizeof(pp->path[0])); + } + + p= pp->path + pp->idx; + slash= (pp->idx > 0); + if (pp->idx == 1 && p[-1] == '/') p--; + + while (*name != 0) { + if (*name == '/') { + slash= 1; + } else { + if (slash) { *p++ = '/'; slash= 0; } + *p++= *name; + } + name++; + } + if (slash && p == pp->path) *p++= '/'; + *p = 0; + pp->idx= p - pp->path; +} + +void path_trunc(pathname_t *pp, size_t didx) +/* Delete part of a pathname to a remembered length. */ +{ + pp->path[pp->idx= didx]= 0; +} + +#if kept_for_comments_only + +const char *path_name(const pathname_t *pp) +/* Return the actual name as a char array. */ +{ + return pp->path; +} + +size_t path_length(const pathname_t *pp) +/* The length of the pathname. */ +{ + return pp->idx; +} + +void path_drop(pathname_t *pp) +/* Release the storage occupied by the pathname. */ +{ + free(pp->path); +} +#endif + +#define path_name(pp) ((const char *) (pp)->path) +#define path_length(pp) ((pp)->idx) +#define path_drop(pp) free((void *) (pp)->path) + +typedef struct namelist { /* Obviously a list of names. */ + struct namelist *next; + char *name; +} namelist_t; + +char *rdlink(const char *link, off_t size) +/* Look where "link" points. */ +{ + static char *path= nil; + static size_t len= 0; + size_t n; + + if (len <= size) { + path= allocate(path, (len= size * 2) * sizeof(path[0])); + } + if ((n= readlink(link, path, len)) == -1) return nil; + path[n]= 0; + return path; +} + +void sort(namelist_t **anl) +/* A stable mergesort disguised as line noise. Must be called like this: + * if (L!=nil && L->next!=nil) sort(&L); + */ +{ + /* static */ namelist_t *nl1, **mid; /* Need not be local */ + namelist_t *nl2; + + nl1= *(mid= &(*anl)->next); + do { + if ((nl1= nl1->next) == nil) break; + mid= &(*mid)->next; + } while ((nl1= nl1->next) != nil); + + nl2= *mid; + *mid= nil; + + if ((*anl)->next != nil) sort(anl); + if (nl2->next != nil) sort(&nl2); + + nl1= *anl; + for (;;) { + if (strcmp(nl1->name, nl2->name)<=0) { + if ((nl1= *(anl= &nl1->next)) == nil) { + *anl= nl2; + break; + } + } else { + *anl= nl2; + nl2= *(anl= &nl2->next); + *anl= nl1; + if (nl2 == nil) break; + } + } +} + +namelist_t *collect(const char *dir) +/* Return a sorted list of directory entries. Returns null with errno != 0 + * on error. + */ +{ + namelist_t *names, **pn= &names; + DIR *dp; + struct dirent *entry; + + if ((dp= opendir(dir)) == nil) return nil; + + while ((entry= readdir(dp)) != nil) { + if (entry->d_name[0] == '.' + && (entry->d_name[1] == 0 + || (entry->d_name[1] == '.' + && entry->d_name[2] == 0))) { + continue; + } + *pn= allocate(nil, sizeof(**pn)); + (*pn)->name= copystr(entry->d_name); + pn= &(*pn)->next; + } + closedir(dp); + *pn= nil; + errno= 0; + if (names != nil && names->next != nil) sort(&names); + return names; +} + +char *pop_name(namelist_t **names) +/* Return one name of a name list. */ +{ + char *name; + namelist_t *junk; + + junk= *names; + *names= junk->next; + name= junk->name; + deallocate(junk); + forget(name); + return name; +} + +typedef enum filetype { /* The files we know about. */ + F_DIR, + F_FILE, + F_BLK, + F_CHR, + F_PIPE, + F_LINK +} filetype_t; + +typedef struct entry { /* One file. */ + int depth; /* Depth in directory tree. */ + const char *name; /* Name of entry. */ + const char *path; /* Path name. */ + int ignore; /* Ignore this entry (errno number.) */ + unsigned long fake_ino; /* Fake inode number for hard links. */ + int linked; /* Is the file hard linked? */ + int lastlink; /* Is it the last link? */ + char *link; /* Where a (sym)link points to. */ + filetype_t type; + mode_t mode; /* Not unlike those in struct stat. */ + uid_t uid; + gid_t gid; + off_t size; + time_t mtime; + dev_t rdev; +} entry_t; + +void linked(entry_t *entry, struct stat *stp) +/* Return an "inode number" if a file could have links (link count > 1). + * Also return a path to the first link if you see the file again. + */ +{ + static unsigned long new_fake_ino= 0; + static struct links { + struct links *next; + char *path; + ino_t ino; + dev_t dev; + nlink_t nlink; + unsigned long fake_ino; + } *links[1024]; + struct links **plp, *lp; + + entry->linked= entry->lastlink= 0; + entry->fake_ino= 0; + entry->link= nil; + + if (S_ISDIR(stp->st_mode) || stp->st_nlink < 2) return; + + plp= &links[stp->st_ino % arraysize(links)]; + while ((lp= *plp) != nil && (lp->ino != stp->st_ino + || lp->dev != stp->st_dev)) plp= &lp->next; + + if (lp == nil) { + /* New file, store it with a new fake inode number. */ + *plp= lp= allocate(nil, sizeof(*lp)); + lp->next= nil; + lp->path= copystr(entry->path); + lp->ino= stp->st_ino; + lp->dev= stp->st_dev; + lp->nlink= stp->st_nlink; + lp->fake_ino= ++new_fake_ino; + } else { + entry->link= lp->path; + entry->linked= 1; + } + entry->fake_ino= lp->fake_ino; + + if (--lp->nlink == 0) { + /* No need to remember this one, no more links coming. */ + *plp= lp->next; + forget(lp->path); + deallocate(lp); + entry->lastlink= 1; + } +} + +char *tree; /* Tree to work on. */ +FILE *statefp; /* State file. */ +char *state_file; +FILE *difffp; /* File of differences. */ +char *diff_file; + +entry_t *traverse(void) +/* Get one name from the directory tree. */ +{ + static int depth; + static pathname_t path; + static entry_t entry; + static namelist_t **entries; + static size_t *trunc; + static size_t deep; + static namelist_t *newentries; + struct stat st; + +recurse: + keep= KEEP_TRAVERSE; + + if (deep == 0) { + /* Initialize for the root of the tree. */ + path_init(&path); + path_add(&path, tree); + entries= allocate(nil, 1 * sizeof(entries[0])); + entries[0]= allocate(nil, sizeof(*entries[0])); + entries[0]->next= nil; + entries[0]->name= copystr("/"); + trunc= allocate(nil, 1 * sizeof(trunc[0])); + trunc[0]= path_length(&path); + deep= 1; + } else + if (newentries != nil) { + /* Last entry was a directory, need to go down. */ + if (entry.ignore) { + /* Ouch, it is to be ignored! */ + while (newentries != nil) (void) pop_name(&newentries); + goto recurse; + } + if (++depth == deep) { + deep++; + entries= allocate(entries, deep * sizeof(entries[0])); + trunc= allocate(trunc, deep * sizeof(trunc[0])); + } + entries[depth]= newentries; + newentries= nil; + trunc[depth]= path_length(&path); + } else { + /* Pop up out of emptied directories. */ + while (entries[depth] == nil) { + if (depth == 0) return nil; /* Back at the root. */ + + /* Go up one level. */ + depth--; + } + } + entry.name= pop_name(&entries[depth]); + path_trunc(&path, trunc[depth]); + path_add(&path, entry.name); + if (depth == 0) { + entry.path= "/"; + } else { + entry.path= path_name(&path) + trunc[0]; + if (entry.path[0] == '/') entry.path++; + } + entry.depth= depth; + entry.ignore= 0; + + if (lstat(path_name(&path), &st) < 0) { + if (depth == 0 || errno != ENOENT) { + /* Something wrong with this entry, complain about + * it and ignore it further. + */ + entry.ignore= errno; + report(path_name(&path)); + return &entry; + } else { + /* Entry strangely nonexistent; simply continue. */ + goto recurse; + } + } + + /* Don't cross mountpoints if -x is set. */ + if (xflag) { + if (xdev == NO_DEVICE) xdev= st.st_dev; + if (st.st_dev != xdev) { + /* Ignore the mountpoint. */ + entry.ignore= EXDEV; + return &entry; + } + } + + entry.mode= st.st_mode & 07777; + entry.uid= st.st_uid; + entry.gid= st.st_gid; + entry.size= st.st_size; + entry.mtime= st.st_mtime; + entry.rdev= st.st_rdev; + + linked(&entry, &st); + + if (S_ISDIR(st.st_mode)) { + /* A directory. */ + entry.type= F_DIR; + + /* Gather directory entries for the next traverse. */ + if ((newentries= collect(path_name(&path))) == nil + && errno != 0) { + entry.ignore= errno; + report(path_name(&path)); + } + } else + if (S_ISREG(st.st_mode)) { + /* A plain file. */ + entry.type= F_FILE; + } else + if (S_ISBLK(st.st_mode)) { + /* A block special file. */ + entry.type= F_BLK; + } else + if (S_ISCHR(st.st_mode)) { + /* A character special file. */ + entry.type= F_CHR; + } else + if (S_ISFIFO(st.st_mode)) { + /* A named pipe. */ + entry.type= F_PIPE; + } else + if (S_ISLNK(st.st_mode)) { + /* A symbolic link. */ + entry.type= F_LINK; + if ((entry.link= rdlink(path_name(&path), st.st_size)) == nil) { + entry.ignore= errno; + report(path_name(&path)); + } + } else { + /* Unknown type of file. */ + entry.ignore= EINVAL; + } + return &entry; +} + +void checkstate(void) +{ + if (ferror(statefp)) fatal(state_file); +} + +void indent(int depth) +/* Provide indentation to show directory depth. */ +{ + int n= BASE_INDENT * (depth - 1); + + while (n >= 8) { + if (putc('\t', statefp) == EOF) checkstate(); + n-= 8; + } + while (n > 0) { + if (putc(' ', statefp) == EOF) checkstate(); + n--; + } +} + +int print_name(FILE *fp, const char *name) +/* Encode a name. */ +{ + const char *p; + int c; + + for (p= name; (c= (unsigned char) *p) != 0; p++) { + if (c <= ' ' || c == '\\') { + fprintf(fp, "\\%03o", c); + if (ferror(fp)) return 0; + } else { + if (putc(c, fp) == EOF) return 0; + } + } + return 1; +} + +void mkstatefile(void) +/* Make a state file out of the directory tree. */ +{ + entry_t *entry; + + while ((entry= traverse()) != nil) { + indent(entry->depth); + if (!print_name(statefp, entry->name)) checkstate(); + + if (entry->ignore) { + fprintf(statefp, "\tignore (%s)\n", + strerror(entry->ignore)); + checkstate(); + continue; + } + + switch (entry->type) { + case F_DIR: + fprintf(statefp, "\td%03o %u %u", + (unsigned) entry->mode, + (unsigned) entry->uid, (unsigned) entry->gid); + break; + case F_FILE: + fprintf(statefp, "\t%03o %u %u %lu %lu", + (unsigned) entry->mode, + (unsigned) entry->uid, (unsigned) entry->gid, + (unsigned long) entry->size, + (unsigned long) entry->mtime); + break; + case F_BLK: + case F_CHR: + fprintf(statefp, "\t%c%03o %u %u %x", + entry->type == F_BLK ? 'b' : 'c', + (unsigned) entry->mode, + (unsigned) entry->uid, (unsigned) entry->gid, + (unsigned) entry->rdev); + break; + case F_PIPE: + fprintf(statefp, "\tp%03o %u %u", + (unsigned) entry->mode, + (unsigned) entry->uid, (unsigned) entry->gid); + break; + case F_LINK: + fprintf(statefp, "\t-> "); + checkstate(); + (void) print_name(statefp, entry->link); + break; + } + checkstate(); + if (entry->fake_ino != 0) + fprintf(statefp, " %lu", entry->fake_ino); + if (entry->lastlink) + fprintf(statefp, " last"); + if (fputc('\n', statefp) == EOF) checkstate(); + } + fflush(statefp); + checkstate(); +} + +char *read1line(FILE *fp) +/* Read one line from a file. Return null on EOF or error. */ +{ + static char *line; + static size_t len; + size_t idx; + int c; + + if (len == 0) line= allocate(nil, (len= 16) * sizeof(line[0])); + + idx= 0; + while ((c= getc(fp)) != EOF && c != '\n') { + if (c < '\t') { + /* Control characters are not possible. */ + fprintf(stderr, + "remsync: control character in data file!\n"); + exit(1); + } + line[idx++]= c; + if (idx == len) { + line= allocate(line, (len*= 2) * sizeof(line[0])); + } + } + if (c == EOF) { + if (ferror(fp)) return nil; + if (idx == 0) return nil; + } + line[idx]= 0; + return line; +} + +void getword(char **pline, char **parg, size_t *plen) +/* Get one word from a line, interpret octal escapes. */ +{ + char *line= *pline; + char *arg= *parg; + size_t len= *plen; + int i; + int c; + size_t idx; + + idx= 0; + while ((c= *line) != 0 && c != ' ' && c != '\t') { + line++; + if (c == '\\') { + c= 0; + for (i= 0; i < 3; i++) { + if ((unsigned) (*line - '0') >= 010) break; + c= (c << 3) | (*line - '0'); + line++; + } + } + arg[idx++]= c; + if (idx == len) arg= allocate(arg, (len*= 2) * sizeof(arg[0])); + } + arg[idx]= 0; + *pline= line; + *parg= arg; + *plen= len; +} + +void splitline(char *line, char ***pargv, size_t *pargc) +/* Split a line into an array of words. */ +{ + static char **argv; + static size_t *lenv; + static size_t len; + size_t idx; + + idx= 0; + for (;;) { + while (*line == ' ' || *line == '\t') line++; + + if (*line == 0) break; + + if (idx == len) { + len++; + argv= allocate(argv, len * sizeof(argv[0])); + lenv= allocate(lenv, len * sizeof(lenv[0])); + argv[idx]= allocate(nil, 16 * sizeof(argv[idx][0])); + lenv[idx]= 16; + } + getword(&line, &argv[idx], &lenv[idx]); + idx++; + } + *pargv= argv; + *pargc= idx; +} + +int getattributes(entry_t *entry, int argc, char **argv) +/* Convert state or difference file info into file attributes. */ +{ + int i; + int attr; +#define A_MODE1 0x01 /* Some of these attributes follow the name */ +#define A_MODE 0x02 +#define A_OWNER 0x04 +#define A_SIZETIME 0x08 +#define A_DEV 0x10 +#define A_LINK 0x20 + + switch (argv[0][0]) { + case 'd': + /* Directory. */ + entry->type= F_DIR; + attr= A_MODE1 | A_OWNER; + break; + case 'b': + /* Block device. */ + entry->type= F_BLK; + attr= A_MODE1 | A_OWNER | A_DEV; + break; + case 'c': + /* Character device. */ + entry->type= F_CHR; + attr= A_MODE1 | A_OWNER | A_DEV; + break; + case 'p': + /* Named pipe. */ + entry->type= F_PIPE; + attr= A_MODE1 | A_OWNER; + break; + case '-': + /* Symlink. */ + entry->type= F_LINK; + attr= A_LINK; + break; + default: + /* Normal file. */ + entry->type= F_FILE; + attr= A_MODE | A_OWNER | A_SIZETIME; + } + + if (attr & (A_MODE | A_MODE1)) { + entry->mode= strtoul(argv[0] + (attr & A_MODE1), nil, 010); + } + i= 1; + if (attr & A_OWNER) { + if (i + 2 > argc) return 0; + entry->uid= strtoul(argv[i++], nil, 10); + entry->gid= strtoul(argv[i++], nil, 10); + } + if (attr & A_SIZETIME) { + if (i + 2 > argc) return 0; + entry->size= strtoul(argv[i++], nil, 10); + entry->mtime= strtoul(argv[i++], nil, 10); + } + if (attr & A_DEV) { + if (i + 1 > argc) return 0; + entry->rdev= strtoul(argv[i++], nil, 0x10); + } + if (attr & A_LINK) { + if (i + 1 > argc) return 0; + entry->link= argv[i++]; + } + entry->linked= entry->lastlink= 0; + if (i < argc) { + /* It has a fake inode number, so it is a hard link. */ + static struct links { /* List of hard links. */ + struct links *next; + unsigned long fake_ino; + char *path; + } *links[1024]; + struct links **plp, *lp; + unsigned long fake_ino; + + fake_ino= strtoul(argv[i++], nil, 10); + + plp= &links[fake_ino % arraysize(links)]; + while ((lp= *plp) != nil && lp->fake_ino != fake_ino) + plp= &lp->next; + + if (lp == nil) { + /* New link. */ + *plp= lp= allocate(nil, sizeof(*lp)); + lp->next= nil; + lp->fake_ino= fake_ino; + lp->path= copystr(entry->path); + } else { + /* Linked to. */ + entry->link= lp->path; + entry->linked= 1; + } + + if (i < argc) { + if (strcmp(argv[i++], "last") != 0) return 0; + + /* Last hard link of a file. */ + forget(lp->path); + *plp= lp->next; + deallocate(lp); + entry->lastlink= 1; + } + } + if (i != argc) return 0; + return 1; +} + +void state_syntax(off_t line) +{ + fprintf(stderr, "remsync: %s: syntax error on line %lu\n", + state_file, (unsigned long) line); + exit(1); +} + +entry_t *readstate(void) +/* Read one entry from the state file. */ +{ + static entry_t entry; + static pathname_t path; + static size_t *trunc; + static size_t trunc_len; + static base_indent; + char *line; + char **argv; + size_t argc; + static off_t lineno; + int indent, depth; + +recurse: + keep= KEEP_STATE; + + if (feof(statefp) || (line= read1line(statefp)) == nil) { + checkstate(); + return nil; + } + lineno++; + + /* How far is this entry indented? */ + indent= 0; + while (*line != 0) { + if (*line == ' ') indent++; + else + if (*line == '\t') indent= (indent + 8) & ~7; + else + break; + line++; + } + if (indent > 0 && base_indent == 0) base_indent= indent; + depth= (base_indent == 0 ? 0 : indent / base_indent) + 1; + + if (entry.ignore && depth > entry.depth) { + /* If the old directory is ignored, then so are its entries. */ + goto recurse; + } + entry.depth= depth; + + splitline(line, &argv, &argc); + if (argc < 2) state_syntax(lineno); + + if (trunc == nil) { + /* The root of the tree, initialize path. */ + if (argv[0][0] != '/') state_syntax(lineno); + path_init(&path); + path_add(&path, "/"); + trunc= allocate(nil, (trunc_len= 16) * sizeof(trunc[0])); + + /* The root has depth 0. */ + entry.depth= 0; + trunc[0]= 0; + } else { + if (entry.depth > trunc_len) { + trunc= allocate(trunc, + (trunc_len*= 2) * sizeof(trunc[0])); + } + path_trunc(&path, trunc[entry.depth - 1]); + path_add(&path, argv[0]); + trunc[entry.depth]= path_length(&path); + } + + entry.path= path_name(&path); + entry.name= argv[0]; + entry.link= nil; + if ((entry.ignore= strcmp(argv[1], "ignore") == 0)) { + return &entry; + } + if (!getattributes(&entry, argc - 1, argv + 1)) state_syntax(lineno); + return &entry; +} + +void checkdiff(void) +{ + if (ferror(difffp)) fatal(diff_file); +} + +enum { DELETE, REPLACE, COPY, SIMILAR, EQUAL, ADD } +compare(entry_t *remote, entry_t *local) +/* Compare the local and remote entries and tell what need to be done. */ +{ + int cmp; + + /* Surplus entries? */ + if (local == nil) return DELETE; + if (remote == nil) return ADD; + + /* Extra directory entries? */ + if (remote->depth > local->depth) return DELETE; + if (local->depth > remote->depth) return ADD; + + /* Compare names. */ + cmp= strcmp(remote->name, local->name); + if (cmp < 0) return DELETE; + if (cmp > 0) return ADD; + + /* The files have the same name. Ignore one, ignore the other. */ + if (remote->ignore || local->ignore) { + remote->ignore= local->ignore= 1; + return EQUAL; + } + + /* Reasons for replacement? */ + if (remote->type != local->type) return REPLACE; + + /* Should be hard linked to the same file. */ + if (remote->linked || local->linked) { + if (!remote->linked || !local->linked) return REPLACE; + if (strcmp(remote->link, local->link) != 0) return REPLACE; + } + + switch (remote->type) { + case F_FILE: + if (uflag) { + if (remote->mtime < local->mtime) return COPY; + } else { + if (remote->size != local->size + || remote->mtime != local->mtime) + return COPY; + } + goto check_modes; + case F_BLK: + case F_CHR: + if (remote->rdev != local->rdev) return REPLACE; + goto check_modes; + case F_DIR: + case F_PIPE: + check_modes: + if (remote->mode != local->mode + || remote->uid != local->uid + || remote->gid != local->gid) return SIMILAR; + break; + case F_LINK: + if (strcmp(remote->link, local->link) != 0) return REPLACE; + break; + } + return EQUAL; +} + +void delete(entry_t *old) +/* Emit an instruction to remove an entry. */ +{ + if (old->ignore) return; + if (uflag) return; + + fprintf(difffp, "rm "); + checkdiff(); + if (!print_name(difffp, old->path)) checkdiff(); + if (putc('\n', difffp) == EOF) checkdiff(); + if (vflag) fprintf(stderr, "rm %s\n", old->path); +} + +void change_modes(entry_t *old, entry_t *new) +/* Emit an instruction to change the attributes of an entry. */ +{ + if (new->ignore) return; + + fprintf(difffp, "chmod "); + checkdiff(); + if (!print_name(difffp, new->path)) checkdiff(); + fprintf(difffp, " %03o %u %u\n", + (unsigned) new->mode, + (unsigned) new->uid, (unsigned) new->gid); + checkdiff(); + if (vflag && old->mode != new->mode) { + fprintf(stderr, "chmod %s %03o %u %u\n", + new->path, + (unsigned) new->mode, + (unsigned) new->uid, (unsigned) new->gid); + } +} + +int cat(int f, off_t size) +/* Include the contents of a file in the differences file. */ +{ + ssize_t n; + unsigned char buf[1024 << sizeof(int)]; + unsigned char *p; + int c; + + if (Dflag) return 1; /* Debug: Don't need the file contents. */ + + while ((n= read(f, buf, sizeof(buf))) > 0) { + p= buf; + do { + if (size == 0) { + /* File suddenly larger. */ + errno= EINVAL; + return 0; + } + c= *p++; + if (putc(c, difffp) == EOF) checkdiff(); + size--; + } while (--n != 0); + } + if (size > 0) { + int err= errno; + + /* File somehow shrunk, pad it out. */ + do { + if (putc(0, difffp) == EOF) checkdiff(); + } while (--size != 0); + errno= n == 0 ? EINVAL : err; + n= -1; + } + return n == 0; +} + +void add(entry_t *old, entry_t *new) +/* Emit an instruction to add an entry. */ +{ + pathname_t file; + int f; + + if (new->ignore) return; + + if (new->linked) { + /* This file is to be a hard link to an existing file. */ + fprintf(difffp, "ln "); + checkdiff(); + if (!print_name(difffp, new->link)) checkdiff(); + if (fputc(' ', difffp) == EOF) checkdiff(); + if (!print_name(difffp, new->path)) checkdiff(); + if (fputc('\n', difffp) == EOF) checkdiff(); + if (vflag) { + fprintf(stderr, "ln %s %s\n", new->link, new->path); + } + return; + } + + /* Add some other type of file. */ + fprintf(difffp, "add "); + checkdiff(); + if (!print_name(difffp, new->path)) checkdiff(); + + switch (new->type) { + case F_DIR: + fprintf(difffp, " d%03o %u %u\n", + (unsigned) new->mode, + (unsigned) new->uid, (unsigned) new->gid); + if (vflag) fprintf(stderr, "mkdir %s\n", new->path); + break; + case F_FILE: + path_init(&file); + path_add(&file, tree); + path_add(&file, new->path); + if ((f= open(path_name(&file), O_RDONLY)) < 0) { + report(path_name(&file)); + path_drop(&file); + fprintf(difffp, " ignore\n"); + break; + } + fprintf(difffp, " %03o %u %u %lu %lu\n", + (unsigned) new->mode, + (unsigned) new->uid, (unsigned) new->gid, + (unsigned long) new->size, + (unsigned long) new->mtime); + checkdiff(); + if (!cat(f, new->size)) { + int err= errno; + report(path_name(&file)); + fprintf(difffp, "old "); + checkdiff(); + print_name(difffp, err == EINVAL + ? "File changed when copied" : strerror(err)); + fputc('\n', difffp); + checkdiff(); + } else { + if (vflag) { + fprintf(stderr, "%s %s\n", + old == nil ? "add" : + old->mtime > new->mtime ? + "restore" : "update", + new->path); + } + } + close(f); + path_drop(&file); + break; + case F_BLK: + case F_CHR: + fprintf(difffp, " %c%03o %u %u %lx\n", + new->type == F_BLK ? 'b' : 'c', + (unsigned) new->mode, + (unsigned) new->uid, (unsigned) new->gid, + (unsigned long) new->rdev); + if (vflag) fprintf(stderr, "mknod %s\n", new->path); + break; + case F_PIPE: + fprintf(difffp, " p%03o %u %u\n", + (unsigned) new->mode, + (unsigned) new->uid, (unsigned) new->gid); + if (vflag) fprintf(stderr, "mkfifo %s\n", new->path); + break; + case F_LINK: + fprintf(difffp, " -> "); + checkdiff(); + (void) print_name(difffp, new->link); + checkdiff(); + fputc('\n', difffp); + if (vflag) { + fprintf(stderr, "ln -s %s %s\n", new->link, new->path); + } + break; + } + checkdiff(); +} + +void mkdifferences(void) +{ + entry_t *remote; + entry_t *local; + + remote= readstate(); + local= traverse(); + + while (remote != nil || local != nil) { + switch (compare(remote, local)) { + case DELETE: + /* Remove the remote file. */ + delete(remote); + remote->ignore= 1; + remote= readstate(); + break; + case REPLACE: + /* Replace the remote file with the local one. */ + if (remote->type == F_FILE && local->type == F_FILE + && !local->linked) { + /* Don't overwrite, remove first. */ + delete(remote); + } + /*FALL THROUGH*/ + case COPY: + /* Overwrite the remote file with the local one. */ + add(remote, local); + remote->ignore= 1; + goto skip2; + case SIMILAR: + /* About the same, but the attributes need changing. */ + change_modes(remote, local); + goto skip2; + case EQUAL: + skip2: + /* Skip two files. */ + remote= readstate(); + local= traverse(); + break; + case ADD: + /* Add the local file. */ + add(nil, local); + local= traverse(); + break; + } + } + fprintf(difffp, "end\n"); + fflush(difffp); + checkdiff(); +} + +void apply_remove(pathname_t *pp) +/* Remove an obsolete file. */ +{ + struct stat st; + + if (lstat(path_name(pp), &st) < 0) { + if (errno != ENOENT) report(path_name(pp)); + return; + } + + if (S_ISDIR(st.st_mode)) { + /* Recursively delete directories. */ + size_t len; + namelist_t *entries; + + if ((entries= collect(path_name(pp))) == nil && errno != 0) { + report(path_name(pp)); + return; + } + len= path_length(pp); + + while (entries != nil) { + path_add(pp, pop_name(&entries)); + apply_remove(pp); + path_trunc(pp, len); + } + if (rmdir(path_name(pp)) < 0) { + report(path_name(pp)); + return; + } + if (vflag) fprintf(stderr, "rmdir %s\n", path_name(pp)); + } else { + /* Some other type of file. */ + if (unlink(path_name(pp)) < 0) { + report(path_name(pp)); + return; + } + if (vflag) fprintf(stderr, "rm %s\n", path_name(pp)); + } +} + +void apply_mkold(const char *file, const char *err) +/* Make a file very old. (An error occurred when it was added.) */ +{ + struct utimbuf utb; + + utb.actime= utb.modtime= 0; + if (utime(file, &utb) < 0) { + report(file); + return; + } + fprintf(stderr, "made %s look old", file); + fprintf(stderr, err == nil ? "\n" : " due to a remote problem: %s\n", + err); +} + +void apply_chmod(const char *file, mode_t mode, uid_t uid, gid_t gid, int talk) +/* Change mode and ownership. */ +{ + struct stat st; + + if (lstat(file, &st) < 0) { + report(file); + return; + } + if ((st.st_mode & 07777) != mode) { + if (chmod(file, mode) < 0) { + report(file); + return; + } + if (vflag && talk) { + fprintf(stderr, "chmod %03o %s\n", + (unsigned) mode, file); + } + } + if (st.st_uid != uid || st.st_gid != gid) { + if (chown(file, uid, gid) < 0) { + if (errno != EPERM) report(file); + return; + } + if (vflag && talk) { + fprintf(stderr, "chown %u:%u %s\n", + (unsigned) uid, (unsigned) gid, file); + } + } +} + +void apply_add(pathname_t *pp, entry_t *entry) +/* Add or replace a file. */ +{ + const char *file; + off_t size; + int f; + unsigned char buf[1024 << sizeof(int)]; + unsigned char *p; + int c; + int dirty; + struct stat st; + struct utimbuf utb; + + if (entry->ignore) return; + + if (lstat(path_name(pp), &st) >= 0 && (entry->type != F_FILE + || !S_ISREG(st.st_mode))) { + apply_remove(pp); + } + + file= path_name(pp); + + switch (entry->type) { + case F_DIR: + if (mkdir(file, entry->mode) < 0) { + report(file); + return; + } + if (vflag) fprintf(stderr, "mkdir %s\n", file); + break; + case F_FILE: + size= entry->size; + + f= -1; + st.st_mode= 0; + if (lstat(file, &st) < 0 || S_ISREG(st.st_mode)) { + f= open(file, O_WRONLY | O_CREAT | O_TRUNC, + entry->mode); + if (f < 0) { + (void) chmod(file, entry->mode | 0200); + f= open(file, O_WRONLY | O_CREAT | O_TRUNC, + entry->mode); + } + if (f < 0) { + (void) unlink(file); + f= open(file, O_WRONLY | O_CREAT | O_TRUNC, + entry->mode); + } + if (f < 0) report(file); + } + dirty= (f >= 0); + p= buf; + while (size > 0 && (c= getc(difffp)) != EOF) { + size--; + *p++= c; + if (p == arraylimit(buf) || size == 0) { + if (f >= 0 && write(f, buf, p - buf) < 0) { + report(file); + close(f); + f= -1; + } + p= buf; + } + } + if (size > 0) { + if (ferror(difffp)) report(diff_file); + if (feof(difffp)) { + fprintf(stderr, "remspec: %s: premature EOF\n", + diff_file); + } + if (dirty) apply_mkold(file, nil); + exit(1); + } + if (f < 0) { + if (dirty) apply_mkold(file, nil); + return; + } + close(f); + if (vflag) { + fprintf(stderr, st.st_mode == 0 ? "add %s\n" + : entry->mtime >= st.st_mtime + ? "update %s\n" : "restore %s\n", file); + } + utb.actime= time(nil); + utb.modtime= entry->mtime; + if (utime(file, &utb) < 0) report(file); + break; + case F_BLK: + if (mknod(file, S_IFBLK | entry->mode, entry->rdev) < 0) { + report(file); + return; + } + if (vflag) { + fprintf(stderr, "mknod %s b %d %d\n", file, + major(entry->rdev), minor(entry->rdev)); + } + break; + case F_CHR: + if (mknod(file, S_IFCHR | entry->mode, entry->rdev) < 0) { + report(file); + return; + } + if (vflag) { + fprintf(stderr, "mknod %s c %d %d\n", file, + major(entry->rdev), minor(entry->rdev)); + } + break; + case F_PIPE: + if (mknod(file, S_IFIFO | entry->mode, 0) < 0) { + report(file); + return; + } + if (vflag) fprintf(stderr, "mknod %s p\n", file); + break; + case F_LINK: + if (symlink(entry->link, file) < 0) { + report(file); + return; + } + if (vflag) fprintf(stderr, "ln -s %s %s\n", entry->link, file); + return; + } + apply_chmod(file, entry->mode, entry->uid, entry->gid, 0); +} + +void apply_link(const char *file, pathname_t *pp) +/* Hard link *pp to file. */ +{ + struct stat st1, st2; + + if (lstat(file, &st1) < 0) { + report(file); + return; + } + if (lstat(path_name(pp), &st2) >= 0) { + if (st1.st_ino == st2.st_ino && st1.st_dev == st2.st_dev) + return; + apply_remove(pp); + if (lstat(path_name(pp), &st2) >= 0) return; + } + if (link(file, path_name(pp)) < 0) { + fprintf(stderr, "remsync: ln %s %s: %s\n", file, path_name(pp), + strerror(errno)); + excode= 1; + return; + } + if (vflag) fprintf(stderr, "ln %s %s\n", file, path_name(pp)); +} + +void diff_syntax(const char *line) +{ + fprintf(stderr, "remsync: %s: syntax error on this line: %s\n", + diff_file, line); + exit(1); +} + +void apply_differences(void) +/* Update a tree to a list of differences derived from a remote tree. */ +{ + char *line; + char **argv; + size_t argc; + pathname_t path, link; + size_t trunc; + + path_init(&path); + path_init(&link); + path_add(&path, tree); + path_add(&link, tree); + trunc= path_length(&path); + + while (!feof(difffp) && (line= read1line(difffp)) != nil) { + splitline(line, &argv, &argc); + if (argc == 0) diff_syntax(line); + + path_trunc(&path, trunc); + + if (strcmp(argv[0], "add") == 0) { + entry_t entry; + + if (argc < 3) diff_syntax(line); + path_add(&path, argv[1]); + entry.ignore= (strcmp(argv[2], "ignore") == 0); + if (!entry.ignore && !getattributes(&entry, + argc - 2, argv + 2)) + diff_syntax(line); + apply_add(&path, &entry); + } else + if (strcmp(argv[0], "rm") == 0) { + if (argc != 2) diff_syntax(line); + path_add(&path, argv[1]); + apply_remove(&path); + } else + if (strcmp(argv[0], "ln") == 0) { + if (argc != 3) diff_syntax(line); + path_trunc(&link, trunc); + path_add(&link, argv[1]); + path_add(&path, argv[2]); + apply_link(path_name(&link), &path); + } else + if (strcmp(argv[0], "chmod") == 0) { + if (argc != 5) diff_syntax(line); + path_add(&path, argv[1]); + apply_chmod(path_name(&path), + strtoul(argv[2], nil, 010), + strtoul(argv[3], nil, 10), + strtoul(argv[4], nil, 10), + 1); + } else + if (strcmp(argv[0], "old") == 0) { + if (argc != 3) diff_syntax(line); + path_add(&path, argv[1]); + apply_mkold(path_name(&path), argv[2]); + } else + if (strcmp(argv[0], "end") == 0) { + if (argc != 1) diff_syntax(line); + break; + } else { + diff_syntax(line); + } + } + checkdiff(); +} + +void usage(void) +{ + fprintf(stderr, "Usage: remsync -sxv tree [state-file]\n"); + fprintf(stderr, " remsync -duxvD tree [state-file [diff-file]]\n"); + fprintf(stderr, " remsync [-xv] tree [diff-file]\n"); + exit(1); +} + +int main(int argc, char **argv) +{ + int i; + + for (i= 1; i < argc && argv[i][0] == '-'; i++) { + char *p= argv[i] + 1; + + if (p[0] == '-' && p[1] == 0) { i++; break; } + + while (*p != 0) { + switch (*p++) { + case 's': sflag= 1; break; + case 'd': dflag= 1; break; + case 'u': uflag= 1; break; + case 'x': xflag= 1; break; + case 'D': Dflag= 1; break; + case 'v': vflag= 1; break; + default: usage(); + } + } + } + if (sflag && dflag) usage(); + if (sflag && uflag) usage(); + if (!sflag && !dflag && uflag) usage(); + if (!dflag && Dflag) usage(); + + if (i == argc) usage(); + tree= argv[i++]; + + if (sflag) { + /* Make a state file. */ + state_file= i < argc ? argv[i++] : "-"; + if (i != argc) usage(); + + statefp= stdout; + if (strcmp(state_file, "-") != 0) { + if ((statefp= fopen(state_file, "w")) == nil) + fatal(state_file); + } + mkstatefile(); + } else + if (dflag) { + /* Make a file of differences. */ + state_file= i < argc ? argv[i++] : "-"; + + diff_file= i < argc ? argv[i++] : "-"; + if (i != argc) usage(); + + statefp= stdin; + if (strcmp(state_file, "-") != 0) { + if ((statefp= fopen(state_file, "r")) == nil) + fatal(state_file); + } + + difffp= stdout; + if (strcmp(diff_file, "-") != 0) { + if ((difffp= fopen(diff_file, "w")) == nil) + fatal(diff_file); + } + mkdifferences(); + } else { + /* Apply a file of differences. */ + diff_file= i < argc ? argv[i++] : "-"; + if (i != argc) usage(); + + difffp= stdin; + if (strcmp(diff_file, "-") != 0) { + if ((difffp= fopen(diff_file, "r")) == nil) + fatal(diff_file); + } + apply_differences(); + } + exit(excode); +} diff --git a/commands/simple/rget.c b/commands/simple/rget.c new file mode 100755 index 000000000..48777bc12 --- /dev/null +++ b/commands/simple/rget.c @@ -0,0 +1,290 @@ +/* rget 2.6 - remote pipe Author: Kees J. Bot + * 20 Mar 1989 + * + * here$ ... | rput key there$ rget -h here key | ... + * here$ rput key command ... there$ rget -h here key command ... + * + * (Once my first try at network programming, completely reworked by now.) + */ +#define nil ((void*)0) +#include <sys/types.h> +#include <sys/ioctl.h> +#include <stdio.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <errno.h> +#if __minix +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/netdb.h> +#include <net/gen/socket.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_hdr.h> +#include <net/gen/tcp_io.h> +#include <net/hton.h> +#include <net/netlib.h> +#else +#include <sys/socket.h> +#include <netinet/in.h> +#include <netdb.h> +#endif + +static char *name; +static int iflag, oflag, hflag, lflag, cflag; /* -iolch? */ +static char *host; /* Argument to -h. */ +static struct hostent *hent; /* gethostbyname(host) */ +static char *key; /* key (port) */ +static char **cmdv; /* command [arg ...] */ + +static void fatal(const char *label) +{ + int err= errno; + + fprintf(stderr, "%s: %s: %s\n", name, label, strerror(err)); + exit(1); +} + +static unsigned name2port(char *n) +{ + char *end; + unsigned port; + + port= strtoul(n, &end, 0); + if (end == n || *end != 0) { + port= 1; + while (*n != 0) port *= (*n++ & 0xFF); + port |= 0x8000; + } + return htons(port & 0xFFFF); +} + +static void usage(void) +{ + fprintf(stderr, + "Usage: %s [-lcio] [-h host] key [command [arg ...]]\n" + "\t-l: Open TCP socket and listen (default for rput)\n" + "\t-c: Connect to a remote TCP socket (default for rget)\n" + "\t-i: Tie standard input to the TCP stream (default for rget)\n" + "\t-o: Tie standard output to the TCP stream (default for rput)\n" + "\t-io: Bidirectional!\n" + "\tkey: A word to hash into a port number, or simply a port number\n", + name); + exit(1); +} + +int main(int argc, char **argv) +{ + int i, s; + + if ((name= strrchr(argv[0], '/')) == nil) name= argv[0]; else name++; + + if (strcmp(name, "rget") != 0 && strcmp(name, "rput") != 0) { + fprintf(stderr, "Don't know what to do if you call me '%s'\n", name); + exit(1); + } + + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++]+1; + + if (opt[0] == '-' && opt[1] == 0) break; /* -- */ + + while (*opt != 0) switch (*opt++) { + case 'l': lflag= 1; break; + case 'c': cflag= 1; break; + case 'i': iflag= 1; break; + case 'o': oflag= 1; break; + case 'h': + hflag= 1; + if (*opt == 0) { + if (i == argc) usage(); + opt= argv[i++]; + } + host= opt; + opt= ""; + break; + default: usage(); break; + } + } + + if (i == argc) usage(); + key= argv[i++]; + cmdv= argv + i; + + /* Defaults. */ + if (!lflag && !cflag) { + if (name[1] == 'p') lflag= 1; + if (name[1] == 'g') cflag= 1; + } + if (!iflag && !oflag) { + if (name[1] == 'g') iflag= 1; + if (name[1] == 'p') oflag= 1; + } + + /* Constraints. */ + if (lflag && cflag) { + fprintf(stderr, "%s: -c and -l don't mix\n", name); + usage(); + } + if (cflag && !hflag) { + fprintf(stderr, "%s: -c requires a host name given with -h\n", name); + usage(); + } + if (lflag && hflag) { + fprintf(stderr, "%s: -l does not require a host name given with -h\n", + name); + usage(); + } + if (iflag && oflag && cmdv[0] == nil) { + fprintf(stderr, "%s: -io requires that a command is given\n", name); + usage(); + } + + if (hflag) { + if ((hent= gethostbyname(host)) == nil) { + fprintf(stderr, "%s: %s: Name lookup failed\n", name, host); + exit(1); + } + } + + s= -1; + if (lflag) { + /* We need to listen and wait. (We're "rput", most likely.) */ +#if __minix + char *tcp_device; + struct nwio_tcpconf tcpconf; + struct nwio_tcpcl tcplistenopt; + + if ((tcp_device= getenv("TCP_DEVICE")) == nil) tcp_device= "/dev/tcp"; + if ((s= open(tcp_device, O_RDWR)) < 0) fatal(tcp_device); + + tcpconf.nwtc_flags= + NWTC_EXCL | NWTC_LP_SET | NWTC_UNSET_RA | NWTC_UNSET_RP; + tcpconf.nwtc_locport= name2port(key); + if (ioctl(s, NWIOSTCPCONF, &tcpconf) < 0) fatal("NWIOSTCPCONF"); + + tcplistenopt.nwtcl_flags= 0; + if (ioctl(s, NWIOTCPLISTEN, &tcplistenopt) < 0) fatal("NWIOTCPLISTEN"); +#else + int sa; + struct sockaddr_in channel; + static int on= 1; + + if ((s= socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))<0) fatal("socket()"); + + (void) setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *) &on, + sizeof(on)); + memset(&channel, 0, sizeof(channel)); + channel.sin_family= AF_INET; + channel.sin_addr.s_addr= htonl(INADDR_ANY); + channel.sin_port= name2port(key); + if (bind(s, (struct sockaddr *) &channel, sizeof(channel)) < 0) + fatal("bind()"); + + if (listen(s, 0) < 0) fatal("listen()"); + + if ((sa= accept(s, nil, nil)) < 0) fatal("accept()"); + close(s); + s= sa; +#endif + } + + if (cflag) { + /* Connect to the remote end. (We're "rget", most likely.) */ +#if __minix + int n; + char *tcp_device; + nwio_tcpconf_t tcpconf; + nwio_tcpcl_t tcpconnopt; + + if ((tcp_device= getenv("TCP_DEVICE")) == nil) tcp_device= "/dev/tcp"; + + n=60; + for (;;) { + if ((s= open(tcp_device, O_RDWR)) < 0) fatal(tcp_device); + + tcpconf.nwtc_flags= NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; + memcpy(&tcpconf.nwtc_remaddr, hent->h_addr, + sizeof(tcpconf.nwtc_remaddr)); + tcpconf.nwtc_remport= name2port(key); + if (ioctl(s, NWIOSTCPCONF, &tcpconf) < 0) fatal("NWIOSTCPCONF"); + + tcpconnopt.nwtcl_flags= 0; + if (ioctl(s, NWIOTCPCONN, &tcpconnopt) == 0) break; + + if (--n > 0) sleep(2); else fatal("NWIOTCPCONN"); + close(s); + } +#else + int n; + struct sockaddr_in channel; + + n=60; + for (;;) { + if ((s= socket(PF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) + fatal("socket()"); + + memset(&channel, 0, sizeof(channel)); + channel.sin_family= AF_INET; + memcpy(&channel.sin_addr.s_addr, hent->h_addr, + sizeof(channel.sin_addr.s_addr)); + channel.sin_port= name2port(key); + if (connect(s, (struct sockaddr *) &channel, + sizeof(channel)) >= 0) break; + + if (--n > 0) sleep(2); else fatal("connect()"); + close(s); + } +#endif + } + + if (cmdv[0] != nil) { + /* A command is given, so execute it with standard input (rget), + * standard output (rput) or both (-io) tied to the TCP stream. + */ + if (iflag) dup2(s, 0); + if (oflag) dup2(s, 1); + close(s); + + execvp(cmdv[0], cmdv); + fatal(cmdv[0]); + } else { + /* Without a command we have to copy bytes ourselves, probably to or + * from a command that is connected to us with a pipe. (The original + * function of rput/rget, a remote pipe.) + */ + int fi, fo; + int n; + char buf[8192]; + + if (iflag) { + fi= s; + fo= 1; + } else { + fi= 0; + fo= s; + } + + while ((n= read(fi, buf, sizeof(buf))) > 0) { + char *bp= buf; + + while (n > 0) { + int r; + + if ((r= write(fo, bp, n)) <= 0) { + if (r == 0) { + fprintf(stderr, "%s: write(): Unexpected EOF\n", name); + exit(1); + } + fatal("write()"); + } + bp+= r; + n-= r; + } + } + if (n < 0) fatal("read()"); + } + return 0; +} diff --git a/commands/simple/rlogin.c b/commands/simple/rlogin.c new file mode 100755 index 000000000..c5c2d0719 --- /dev/null +++ b/commands/simple/rlogin.c @@ -0,0 +1,1777 @@ +/* + * Copyright (c) 1983, 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#ifdef ID +static char sccsid[] = "@(#)rlogin.c 5.33 (Berkeley) 3/1/91"; +#endif +#endif /* not lint */ + +/* + * $Source$ + * $Header: mit/rlogin/RCS/rlogin.c,v 5.2 89/07/26 12:11:21 kfall + * Exp Locker: kfall $ + */ + +/* + * rlogin - remote login + */ +#include <sys/ioctl.h> +#include <sys/types.h> +#include <sys/wait.h> + +#include <net/netlib.h> +#include <net/hton.h> +#include <net/gen/in.h> +#include <net/gen/netdb.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> + +#include <termios.h> +#include <setjmp.h> +#include <errno.h> +#include <pwd.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <assert.h> +#include <ctype.h> +#include <fcntl.h> +#include <signal.h> +#include <stdlib.h> + +#if __minix +typedef unsigned char u_char; +#endif + +#ifdef KERBEROS +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> + +CREDENTIALS cred; +Key_schedule schedule; +int use_kerberos = 1, doencrypt; +char dst_realm_buf[REALM_SZ], *dest_realm = NULL; +extern char *krb_realmofhost(); +#endif + +#ifndef TIOCPKT_WINDOW +#define TIOCPKT_WINDOW 0x80 +#endif + +/* concession to Sun */ +#ifndef SIGUSR1 +#define SIGUSR1 30 +#endif + +extern int errno; +int eight, litout, rem; + +int noescape; +u_char escapechar = '~'; + +struct speed +{ + speed_t speed; + char *name; +} speeds[] = { + { B0, "0" }, { B50, "50" }, { B75, "75" }, { B110, "110" }, + { B134, "134" }, { B150, "150" }, { B200, "200" }, { B300, "300" }, + { B600, "600" }, { B1200, "1200" }, { B1800, "1800" }, + { B2400, "2400" }, { B4800, "4800" }, { B9600, "9600" }, + { B19200, "19200" }, { B38400, "38400" }, { B57600, "57600" }, + { B115200, "115200" }, + { -1, NULL }, +}; + +#if __minix_vmd +/* flow control variables */ +int more2read_0; +int inprogress_0; +int more2write_1; +int inprogress_1; +int more2read_rem; +int inprogress_rd_rem; +int more2write_rem; +int inprogress_wr_rem; + +/* write to remote */ +size_t wr_rem_size; +size_t wr_rem_offset; +size_t extra_wr_rem_size; +size_t extra_wr_rem_offset; +char *extra_wr_rem; +size_t extra_wr_rem_new_size; +char *extra_wr_rem_new; + +#endif /* __minix_vmd */ + +struct winsize winsize; + +#define get_window_size(fd, wp) ioctl(fd, TIOCGWINSZ, wp) + +extern int main _ARGS(( int argc, char **argv )); +static void usage _ARGS(( void )); +static u_char getescape _ARGS(( char *p )); +static char *speeds2str _ARGS(( speed_t speed )); +static void lostpeer _ARGS(( int sig )); +static void doit _ARGS(( void )); +static void setsignal _ARGS(( int sig, void (*act)(int sig) )); +static void msg _ARGS(( char *str )); +static void done _ARGS(( int status )); +#if !__minix_vmd +static int reader _ARGS(( void )); +#endif +static void mode _ARGS(( int f )); +#if __minix_vmd +static void mark_async _ARGS(( int fd )); +static void init_0 _ARGS(( void )); +static void init_1 _ARGS(( void )); +static void init_rd_rem _ARGS(( void )); +static void init_wr_rem _ARGS(( void )); +static void restart_0 _ARGS(( void )); +static void restart_1 _ARGS(( void )); +static void restart_rd_rem _ARGS(( void )); +static void restart_wr_rem _ARGS(( void )); +static void completed_0 _ARGS(( int result, int error )); +static void completed_1 _ARGS(( int result, int error )); +static void completed_rd_rem _ARGS(( int result, int error )); +static void completed_wr_rem _ARGS(( int result, int error )); +static void do_urg _ARGS(( int urg_byte )); +#endif +#if !__minix_vmd +static void catch_child _ARGS(( int sig )); +static void writer _ARGS(( void )); +#endif +static void echo _ARGS(( int c )); +#if __minix_vmd +static void finish _ARGS(( void )); +static void sendwindow _ARGS(( void )); +static void sigwinch _ARGS(( int sig )); +static void subshell _ARGS(( void )); +#endif + +int main(argc, argv) + int argc; + char **argv; +{ + extern char *optarg; + extern int optind; + struct passwd *pw; + struct servent *sp; + struct termios ttyb; + nwio_tcpopt_t tcpopt; + int error; + int argoff, ch, dflag, one, uid; + char *host, *p, *user, term[1024]; + + argoff = dflag = 0; + one = 1; + host = user = NULL; + + if (p = rindex(argv[0], '/')) + ++p; + else + p = argv[0]; + + if (strcmp(p, "rlogin")) + host = p; + + /* handle "rlogin host flags" */ + if (!host && argc > 2 && argv[1][0] != '-') { + host = argv[1]; + argoff = 1; + } + +#ifdef KERBEROS +#define OPTIONS "8EKLde:k:l:x" +#else +#define OPTIONS "8EKLde:l:" +#endif + while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF) + switch(ch) { + case '8': + eight = 1; + break; + case 'E': + noescape = 1; + break; + case 'K': +#ifdef KERBEROS + use_kerberos = 0; +#endif + break; + case 'L': + litout = 1; + break; + case 'd': + dflag = 1; + break; + case 'e': + escapechar = getescape(optarg); + break; +#ifdef KERBEROS + case 'k': + dest_realm = dst_realm_buf; + (void)strncpy(dest_realm, optarg, REALM_SZ); + break; +#endif + case 'l': + user = optarg; + break; +#ifdef CRYPT +#ifdef KERBEROS + case 'x': + doencrypt = 1; + des_set_key(cred.session, schedule); + break; +#endif +#endif + case '?': + default: + usage(); + } + optind += argoff; + argc -= optind; + argv += optind; + + /* if haven't gotten a host yet, do so */ + if (!host && !(host = *argv++)) + usage(); + + if (*argv) + usage(); + + if (!(pw = getpwuid(uid = getuid()))) { + (void)fprintf(stderr, "rlogin: unknown user id.\n"); + exit(1); + } + if (!user) + user = pw->pw_name; + + sp = NULL; +#ifdef KERBEROS + if (use_kerberos) { + sp = getservbyname((doencrypt ? "eklogin" : "klogin"), "tcp"); + if (sp == NULL) { + use_kerberos = 0; + warning("can't get entry for %s/tcp service", + doencrypt ? "eklogin" : "klogin"); + } + } +#endif + if (sp == NULL) + sp = getservbyname("login", "tcp"); + if (sp == NULL) { + (void)fprintf(stderr, "rlogin: login/tcp: unknown service.\n"); + exit(1); + } + + (void)strncpy(term, (p = getenv("TERM")) ? p : "network", sizeof(term)); + term[sizeof(term)-1]= 0; + + if (tcgetattr(0, &ttyb) == 0) { + (void)strcat(term, "/"); + (void)strcat(term, speeds2str(cfgetospeed(&ttyb))); + } + + (void)get_window_size(0, &winsize); + + (void)signal(SIGPIPE, lostpeer); + +#ifdef KERBEROS +try_connect: + if (use_kerberos) { + rem = KSUCCESS; + errno = 0; + if (dest_realm == NULL) + dest_realm = krb_realmofhost(host); + +#ifdef CRYPT + if (doencrypt) + rem = krcmd_mutual(&host, sp->s_port, user, term, 0, + dest_realm, &cred, schedule); + else +#endif /* CRYPT */ + rem = krcmd(&host, sp->s_port, user, term, 0, + dest_realm); + if (rem < 0) { + use_kerberos = 0; + sp = getservbyname("login", "tcp"); + if (sp == NULL) { + (void)fprintf(stderr, + "rlogin: unknown service login/tcp.\n"); + exit(1); + } + if (errno == ECONNREFUSED) + warning("remote host doesn't support Kerberos"); + if (errno == ENOENT) + warning("can't provide Kerberos auth data"); + goto try_connect; + } + } else { +#ifdef CRYPT + if (doencrypt) { + (void)fprintf(stderr, + "rlogin: the -x flag requires Kerberos authentication.\n"); + exit(1); + } +#endif /* CRYPT */ + rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0); + } +#else + rem = rcmd(&host, sp->s_port, pw->pw_name, user, term, 0); +#endif /* KERBEROS */ + + if (rem < 0) + exit(1); + + /* Enable BSD compatibility for urgent data. */ + tcpopt.nwto_flags= NWTO_BSD_URG; + error= ioctl(rem, NWIOSTCPOPT, &tcpopt); + if (error == -1) + { + fprintf(stderr, "rlogin: NWIOSTCPOPT failed: %s\n", + strerror(errno)); + } + + (void)setuid(uid); + doit(); + /*NOTREACHED*/ +} + +struct termios defattr, rawattr; +#if __minix_vmd +int mustsendwindow; +#else +int child; +#endif + +static void +doit() +{ + struct termios sb; +#if !__minix_vmd + int r; +#else + asio_fd_set_t fd_set; + struct fwait fw; + int result; +#endif + + (void)tcgetattr(0, &sb); + defattr = sb; + rawattr = sb; + + rawattr.c_iflag &= ~(ICRNL | IGNCR | INLCR | ISTRIP | IXOFF | IXON | + PARMRK | IXANY); + rawattr.c_oflag &= ~(OPOST); + rawattr.c_lflag &= ~(ECHONL | ECHO | ICANON | IEXTEN | ISIG); + + (void)signal(SIGINT, SIG_IGN); + setsignal(SIGHUP, exit); + setsignal(SIGQUIT, exit); + +#if !__minix_vmd + child = fork(); + if (child == -1) { + (void)fprintf(stderr, "rlogin: fork: %s.\n", strerror(errno)); + done(1); + } + if (child == 0) { + mode(1); + r = reader(); + if (r == 0) { + msg("connection closed."); + exit(0); + } + sleep(1); + msg("\007connection closed."); + exit(1); + } + + (void)signal(SIGCHLD, catch_child); + writer(); + +#else /* __minix_vmd */ + + mode(1); + /* mark the file descriptors 0, 1, and rem as asynchronous. */ + mark_async(0); + mark_async(1); + mark_async(rem); + init_0(); + init_1(); + init_rd_rem(); + init_wr_rem(); + + for (;;) + { + ASIO_FD_ZERO(&fd_set); + fw.fw_flags= 0; + fw.fw_bits= fd_set.afds_bits; + fw.fw_maxfd= ASIO_FD_SETSIZE; + + if (more2read_0 && !inprogress_0) + { + restart_0(); + fw.fw_flags |= FWF_NONBLOCK; + } + + if (more2write_1 && !inprogress_1) + { + restart_1(); + fw.fw_flags |= FWF_NONBLOCK; + } + + if (more2read_rem && !inprogress_rd_rem) + { + restart_rd_rem(); + fw.fw_flags |= FWF_NONBLOCK; + } + + if (more2write_rem && !inprogress_wr_rem) + { + restart_wr_rem(); + fw.fw_flags |= FWF_NONBLOCK; + } + + if (more2read_0 && inprogress_0) + ASIO_FD_SET(0, ASIO_READ, &fd_set); + if (more2write_1 && inprogress_1) + ASIO_FD_SET(1, ASIO_WRITE, &fd_set); + if (more2read_rem && inprogress_rd_rem) + ASIO_FD_SET(rem, ASIO_READ, &fd_set); + if (more2write_rem && inprogress_wr_rem) + ASIO_FD_SET(rem, ASIO_WRITE, &fd_set); + + for (;;) + { + result= fwait(&fw); + if (result == -1 && (errno == EAGAIN || + errno == EINTR)) + { + break; + } + if (result == -1) + { + fprintf(stderr, "fwait failed (%s)\n", + strerror(errno)); + exit(1); + } + assert(result == 0); +#if 0 +printf("fwait: fw_fw= %d, fw_operation= %d, fw_result= %d, fw.fw_errno= %d\n", + fw.fw_fd, fw.fw_operation, fw.fw_result, fw.fw_errno); +#endif + if (fw.fw_fd == 0 && fw.fw_operation == ASIO_READ) + { + completed_0(fw.fw_result, fw.fw_errno); + } + else if (fw.fw_fd == 1 && + fw.fw_operation == ASIO_WRITE) + { + completed_1(fw.fw_result, fw.fw_errno); + } + else if (fw.fw_fd == rem && + fw.fw_operation == ASIO_READ) + { + completed_rd_rem(fw.fw_result, fw.fw_errno); + } + else if (fw.fw_fd == rem && + fw.fw_operation == ASIO_WRITE) + { + completed_wr_rem(fw.fw_result, fw.fw_errno); + } + else + { + fprintf(stderr, + "strange result from fwait: fd= %d, operation= %d\n", + fw.fw_fd, fw.fw_operation); + exit(1); + } + if (!(fw.fw_flags & FWF_MORE)) + break; + } + if (mustsendwindow) + { + mustsendwindow= 0; + sendwindow(); + } + } +#endif /* __minix_vmd */ + msg("connection closed."); + done(0); +} + +/* trap a signal, unless it is being ignored. */ +static void +setsignal(sig, act) + int sig; + void (*act) _ARGS(( int sig )); +{ + if (signal(sig, act) == SIG_IGN) + (void)signal(sig, SIG_IGN); +} + +static void +done(status) + int status; +{ + int w, wstatus; + + mode(0); +#if !__minix_vmd + if (child > 0) { + /* make sure catch_child does not snap it up */ + (void)signal(SIGCHLD, SIG_DFL); + if (kill(child, SIGKILL) >= 0) + while ((w = wait(&wstatus)) > 0 && w != child); + } +#endif + exit(status); +} + +int dosigwinch; +#if !__minix +void sigwinch(); +#endif + +#if !__minix_vmd +static void +catch_child(sig) + int sig; +{ + int status; + int pid; + + for (;;) { + pid = waitpid(-1, &status, WNOHANG|WUNTRACED); + if (pid == 0) + return; + /* if the child (reader) dies, just quit */ + if (pid < 0 || pid == child && !WIFSTOPPED(status)) + done(WTERMSIG(status) | WEXITSTATUS(status)); + } + /* NOTREACHED */ +} +#endif + +#if !__minix_vmd +/* + * writer: write to remote: 0 -> line. + * ~. terminate + * ~^Z suspend rlogin process. + * ~<delayed-suspend char> suspend rlogin process, but leave reader alone. + */ +static void +writer() +{ + register int bol, local, n; + u_char ch; + int c; + + bol = 1; /* beginning of line */ + local = 0; + for (;;) { + n = read(STDIN_FILENO, &ch, 1); + if (n <= 0) { + if (n < 0 && errno == EINTR) + continue; + break; + } + c = ch; + /* + * If we're at the beginning of the line and recognize a + * command character, then we echo locally. Otherwise, + * characters are echo'd remotely. If the command character + * is doubled, this acts as a force and local echo is + * suppressed. + */ + if (bol) { + bol = 0; + if (!noescape && c == escapechar) { + local = 1; + continue; + } + } else if (local) { + local = 0; + if (c == '.' || c == defattr.c_cc[VEOF]) { + echo(c); + break; + } +#if !__minix + if (c == defattr.c_cc[VSUSP]) { + bol = 1; + echo(c); + stop(c); + continue; + } +#endif + if (c != escapechar) +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + (void)des_write(rem, &escapechar, 1); + else +#endif +#endif + (void)write(rem, &escapechar, 1); + } + + ch = c; +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) { + if (des_write(rem, &ch, 1) == 0) { + msg("line gone"); + break; + } + } else +#endif +#endif + if (write(rem, &ch, 1) == 0) { + msg("line gone"); + break; + } + bol = c == defattr.c_cc[VKILL] || + c == defattr.c_cc[VEOF] || + c == defattr.c_cc[VINTR] || + c == defattr.c_cc[VSUSP] || + c == '\r' || c == '\n'; + } +} +#endif + +#if !__minix_vmd +static void +echo(c) +int c; +{ + register char *p; + char buf[8]; + + p = buf; + c &= 0177; + *p++ = escapechar; + if (c < ' ') { + *p++ = '^'; + *p++ = c + '@'; + } else if (c == 0177) { + *p++ = '^'; + *p++ = '?'; + } else + *p++ = c; + *p++ = '\r'; + *p++ = '\n'; + (void)write(STDOUT_FILENO, buf, p - buf); +} +#endif + +#if !__minix +stop(cmdc) + char cmdc; +{ + mode(0); + (void)signal(SIGCHLD, SIG_IGN); + (void)kill(cmdc == defltc.t_suspc ? 0 : getpid(), SIGTSTP); + (void)signal(SIGCHLD, catch_child); + mode(1); + sigwinch(); /* check for size changes */ +} +#endif + +#ifdef SIGWINCH +static void +sigwinch(sig) + int sig; +{ + struct winsize ws; + +#if __minix + signal(SIGWINCH, sigwinch); +#endif + + if (dosigwinch && get_window_size(0, &ws) == 0 && + memcmp(&ws, &winsize, sizeof(ws))) { + winsize = ws; + mustsendwindow= 1; + } +} + +/* + * Send the window size to the server via the magic escape + */ +static void +sendwindow() +{ + struct winsize *wp; + char *obuf, *new_buf; + + new_buf= realloc(extra_wr_rem_new, + extra_wr_rem_new_size+4+sizeof(*wp)); + if (new_buf == 0) + return; + extra_wr_rem_new= new_buf; + obuf= new_buf+extra_wr_rem_new_size; + extra_wr_rem_new_size += 4+sizeof(*wp); + + more2read_0= 0; + more2write_rem= 1; + + wp = (struct winsize *)(obuf+4); + obuf[0] = 0377; + obuf[1] = 0377; + obuf[2] = 's'; + obuf[3] = 's'; + wp->ws_row = htons(winsize.ws_row); + wp->ws_col = htons(winsize.ws_col); + wp->ws_xpixel = htons(winsize.ws_xpixel); + wp->ws_ypixel = htons(winsize.ws_ypixel); +} +#endif /* SIGWINCH */ + +#if !__minix_vmd +/* + * reader: read from remote: line -> 1 + */ +#define READING 1 +#define WRITING 2 + +int rcvcnt, rcvstate; +char rcvbuf[8 * 1024]; + +static int +reader() +{ + int pid = -getpid(); + int n, remaining; + char *bufp = rcvbuf; + + for (;;) { + while ((remaining = rcvcnt - (bufp - rcvbuf)) > 0) { + rcvstate = WRITING; + n = write(STDOUT_FILENO, bufp, remaining); + if (n < 0) { + if (errno != EINTR) + return(-1); + continue; + } + bufp += n; + } + bufp = rcvbuf; + rcvcnt = 0; + rcvstate = READING; + +#ifdef CRYPT +#ifdef KERBEROS + if (doencrypt) + rcvcnt = des_read(rem, rcvbuf, sizeof(rcvbuf)); + else +#endif +#endif + rcvcnt = read(rem, rcvbuf, sizeof (rcvbuf)); + if (rcvcnt == 0) + return (0); + if (rcvcnt < 0) { + if (errno == EINTR) + continue; + if (errno == EURG) { + nwio_tcpopt_t tcpopt; +#if DEBUG +fprintf(stderr, "\n\rEURG\n\r"); +#endif + tcpopt.nwto_flags= NWTO_RCV_URG; + if (ioctl(rem, NWIOSTCPOPT, &tcpopt) == -1) { + fprintf(stderr, + "rlogin: trouble with urgent data: %s\n", + strerror(errno)); + return(-1); + } + continue; + } + if (errno == ENOURG) { + nwio_tcpopt_t tcpopt; +#if DEBUG +fprintf(stderr, "\n\rENOURG\n\r"); +#endif + tcpopt.nwto_flags= NWTO_RCV_NOTURG; + if (ioctl(rem, NWIOSTCPOPT, &tcpopt) == -1) { + fprintf(stderr, + "rlogin: trouble with not-urgent data: %s\n", + strerror(errno)); + return(-1); + } + continue; + } + (void)fprintf(stderr, "rlogin: read: %s\n", + strerror(errno)); + return(-1); + } + } +} +#endif /* !__minix_vmd */ + +static void +mode(f) + int f; +{ + struct termios *sb; + + switch(f) { + case 0: + sb= &defattr; + break; + case 1: + sb= &rawattr; + break; + default: + return; + } + (void)tcsetattr(0, TCSAFLUSH, sb); +} + +static void +lostpeer(sig) +int sig; +{ + (void)signal(SIGPIPE, SIG_IGN); + msg("\007connection closed."); + done(1); +} + +static void +msg(str) + char *str; +{ + (void)fprintf(stderr, "rlogin: %s\r\n", str); +} + +#ifdef KERBEROS +/* VARARGS */ +warning(va_alist) +va_dcl +{ + va_list ap; + char *fmt; + + (void)fprintf(stderr, "rlogin: warning, using standard rlogin: "); + va_start(ap); + fmt = va_arg(ap, char *); + vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, ".\n"); +} +#endif + +static void +usage() +{ + (void)fprintf(stderr, + "Usage: rlogin [-%s]%s[-e char] [-l username] host\n", +#ifdef KERBEROS +#ifdef CRYPT + "8ELx", " [-k realm] "); +#else + "8EL", " [-k realm] "); +#endif +#else + "8EL", " "); +#endif + exit(1); +} + +/* + * The following routine provides compatibility (such as it is) between 4.2BSD + * Suns and others. Suns have only a `ttysize', so we convert it to a winsize. + */ +#ifdef sun +get_window_size(fd, wp) + int fd; + struct winsize *wp; +{ + struct ttysize ts; + int error; + + if ((error = ioctl(0, TIOCGSIZE, &ts)) != 0) + return(error); + wp->ws_row = ts.ts_lines; + wp->ws_col = ts.ts_cols; + wp->ws_xpixel = 0; + wp->ws_ypixel = 0; + return(0); +} +#endif + +static u_char +getescape(p) + register char *p; +{ + long val; + int len; + + if ((len = strlen(p)) == 1) /* use any single char, including '\' */ + return((u_char)*p); + /* otherwise, \nnn */ + if (*p == '\\' && len >= 2 && len <= 4) { + val = strtol(++p, (char **)NULL, 8); + for (;;) { + if (!*++p) + return((u_char)val); + if (*p < '0' || *p > '8') + break; + } + } + msg("illegal option value -- e"); + usage(); + /* NOTREACHED */ +} + +static char * +speeds2str(speed) + speed_t speed; +{ + int i; + for (i= 0; speeds[i].name != NULL && speeds[i].speed != speed; i++) { + if (speeds[i].speed == speed) return speeds[i].name; + } + return "unknown"; +} + +#if __minix_vmd +static void +mark_async(fd) + int fd; +{ + int result; + int v; + + result= fcntl(fd, F_GETFD); + if (result == -1) + { + fprintf(stderr, + "rlogin: mark_async: fcntl(%d, GETFD) failed (%s)\n", + fd, strerror(errno)); + exit(1); + } + v= result | FD_ASYNCHIO; + result= fcntl(fd, F_SETFD, v); + if (result == -1) + { + fprintf(stderr, + "rlogin: mark_async: fcntl(%d, SETFD, %d) failed (%s)\n", + fd, v, strerror(errno)); + exit(1); + } +} + +#define RD_0_BUFSIZE 256 +char rd_0_buf[RD_0_BUFSIZE]; +size_t rd_0_offset; + +static void +init_0() +{ + more2read_0= 1; + inprogress_0= 0; + rd_0_offset= 0; +} + +size_t wr_1_size; +size_t wr_1_offset; +char *urg_1; +size_t urg_1_size; +char *extra_1; +size_t extra_1_size; +size_t extra_1_offset; +char *extra_1_new; +size_t extra_1_new_size; +#define MAX_EXTRA_1_NEW_SIZE (16*1024) + +static void +init_1() +{ + more2write_1= 0; + inprogress_1= 0; + wr_1_size= 0; + wr_1_offset= 0; + urg_1= NULL; + urg_1_size= 0; + extra_1= NULL; + extra_1_size= 0; + extra_1_offset= 0; + extra_1_new= NULL; + extra_1_new_size= 0; +} + +#define RD_REM_BUFSIZE (8*1024) +char rd_rem_buf[RD_REM_BUFSIZE]; +size_t rd_rem_offset; +int rd_rem_urg; + +static void +init_rd_rem() +{ + more2read_rem= 1; + inprogress_rd_rem= 0; + rd_rem_offset= 0; + rd_rem_urg= 0; +} + +static void +init_wr_rem() +{ + more2write_rem= 0; + inprogress_wr_rem= 0; + wr_rem_size= 0; + wr_rem_offset= 0; + extra_wr_rem_size= 0; + extra_wr_rem_offset= 0; + extra_wr_rem= NULL; + extra_wr_rem_new_size= 0; + extra_wr_rem_new= NULL; +} + +static void +restart_0() +{ + size_t offset; + int result, error; + + assert(!inprogress_0); + rd_0_offset= 1; + offset= 0; + while (offset < RD_0_BUFSIZE) + { + result= read(0, rd_0_buf+rd_0_offset+offset, + RD_0_BUFSIZE-rd_0_offset-offset); + if (result > 0) + { + offset += result; + assert(rd_0_offset+offset <= RD_0_BUFSIZE); + continue; + } + error= errno; + + if (offset != 0) + completed_0(offset, 0); + rd_0_offset += offset; + if (result == -1 && error == EINPROGRESS) + { + inprogress_0= 1; + return; + } + completed_0(result, error); + return; + } + completed_0(offset, 0); +} + +static void +restart_1() +{ + size_t offset; + int result, error; + + assert(!inprogress_1); + + while (extra_1 != NULL || extra_1_new != NULL) + { + if (extra_1 == NULL) + { + extra_1= extra_1_new; + extra_1_new= NULL; + extra_1_size= extra_1_new_size; + extra_1_new_size= 0; + extra_1_offset= 0; + } + offset= 0; +#if DEBUG + if (extra_1_size == 0) + fprintf(stderr, "restart_1: extra_1_size= 0\n"); +#endif + while (offset < extra_1_size) + { + result= write(1, extra_1+extra_1_offset+offset, + extra_1_size-offset); + if (result > 0) + { + assert (result <= extra_1_size-offset); + offset += result; + continue; + } + error= errno; + if (offset != 0) + completed_1(offset, 0); + + if (result == -1 && errno == EINPROGRESS) + { + inprogress_1= 1; + return; + } + completed_1(result, errno); + return; + } + completed_1(offset, 0); + } + + offset= 0; + + if (wr_1_size == 0) + { + more2write_1= 0; + more2read_rem= 1; + return; + } + + while (offset < wr_1_size) + { + result= write(1, rd_rem_buf+wr_1_offset+offset, + wr_1_size-offset); + if (result > 0) + { + assert (result <= wr_1_size-offset); + offset += result; + continue; + } + error= errno; + if (offset != 0) + completed_1(offset, 0); + + if (result == -1 && errno == EINPROGRESS) + { + inprogress_1= 1; + return; + } + completed_1(result, errno); + return; + } + completed_1(offset, 0); +} + +static void +restart_rd_rem() +{ + size_t offset; + int result, error; + + assert(!inprogress_rd_rem); + rd_rem_offset= 0; + offset= 0; + while (offset < RD_REM_BUFSIZE) + { + result= read(rem, rd_rem_buf+offset, RD_REM_BUFSIZE-offset); + if (result > 0) + { + offset += result; + assert(offset <= RD_REM_BUFSIZE); + continue; + } + error= errno; + + if (offset != 0) + completed_rd_rem(offset, 0); + rd_rem_offset= offset; + if (result == -1 && error == EINPROGRESS) + { + inprogress_rd_rem= 1; + return; + } + completed_rd_rem(result, error); + return; + } + completed_rd_rem(offset, 0); +} + +static void +restart_wr_rem() +{ + size_t offset; + int result, error; + + assert(!inprogress_wr_rem); + + if (extra_wr_rem_new != NULL && extra_wr_rem == NULL) + { + extra_wr_rem= extra_wr_rem_new; + extra_wr_rem_size= extra_wr_rem_new_size; + extra_wr_rem_offset= 0; + extra_wr_rem_new= NULL; + extra_wr_rem_new_size= 0; + } + if (extra_wr_rem != NULL) + { + offset= 0; + while (offset < extra_wr_rem_size) + { + result= write(rem, + extra_wr_rem+extra_wr_rem_offset+offset, + extra_wr_rem_size-offset); + if (result > 0) + { + assert (result <= extra_wr_rem_size-offset); + offset += result; + continue; + } + error= errno; + if (offset != 0) + completed_wr_rem(offset, 0); + + if (result == -1 && errno == EINPROGRESS) + { + inprogress_wr_rem= 1; + return; + } + completed_wr_rem(result, errno); + return; + } + completed_wr_rem(offset, 0); + } + if (wr_rem_size == 0) + return; + + offset= 0; + while (offset < wr_rem_size) + { + result= write(rem, rd_0_buf+wr_rem_offset+offset, + wr_rem_size-offset); + if (result > 0) + { + assert (result <= wr_rem_size-offset); + offset += result; + continue; + } + error= errno; + if (offset != 0) + completed_wr_rem(offset, 0); + + if (result == -1 && errno == EINPROGRESS) + { + inprogress_wr_rem= 1; + return; + } + completed_wr_rem(result, errno); + return; + } + completed_wr_rem(offset, 0); +} + +static void +completed_0(result, error) + int result; + int error; +{ + static int bol= 0, local= 0; + + char *iptr, *optr; + int i; + u_char c; + + inprogress_0= 0; + + if (result > 0) + { + assert(rd_0_offset > 0); + wr_rem_offset= 1; + + iptr= rd_0_buf+rd_0_offset; + optr= rd_0_buf+wr_rem_offset; + for (i= 0; i<result; iptr++, i++) + { + c= *iptr; + if (bol) + { + bol= 0; + if (!noescape && c == escapechar) + { + local= 1; + continue; + } + } + else if (local) + { + local= 0; + if (c == '.' || (c != _POSIX_VDISABLE && + c == defattr.c_cc[VEOF])) + { + echo(c); + finish(); + /* NOTREACHED */ + } + if (c == '!') + { + subshell(); + continue; + } + if (c != escapechar) + { + if (optr < iptr) + { + *(optr++)= escapechar; + } + else + { + assert(optr == iptr); + assert(iptr == rd_0_buf+ + rd_0_offset); + assert(rd_0_offset > 0); + wr_rem_offset--; + optr[-1]= escapechar; + } + } + } + *(optr++)= c; + bol= (c != _POSIX_VDISABLE) && + (c == defattr.c_cc[VKILL] || + c == defattr.c_cc[VEOF] || + c == defattr.c_cc[VINTR] || + c == defattr.c_cc[VSUSP] || + c == '\r' || c == '\n'); + } + wr_rem_size += optr-rd_0_buf-wr_rem_offset; + if (wr_rem_size != 0) + { + more2read_0= 0; + more2write_rem= 1; + } + return; + } else + if (result < 0) { + fprintf(stderr, "rlogin: %s\n", strerror(error)); + } + done(1); +} + +static void +completed_1(result, error) + int result; + int error; +{ + inprogress_1= 0; + + if (result > 0) + { + if (extra_1 != NULL) + { + assert (result <= extra_1_size); + extra_1_size -= result; + extra_1_offset += result; + if (extra_1_size == 0) + { + more2write_1= 0; + more2read_rem= 1; + free(extra_1); + extra_1= NULL; + } + return; + } + assert (result <= wr_1_size); + wr_1_size -= result; + wr_1_offset += result; + if (wr_1_size == 0) + { + more2write_1= 0; + more2read_rem= 1; + } + return; + } else + if (result < 0) { + fprintf(stderr, "rlogin: %s\n", strerror(error)); + } + done(1); +} + +static void +completed_rd_rem(result, error) + int result; + int error; +{ + nwio_tcpopt_t tcpopt; + char *new_buf; + size_t keep_size; + u_char urg_byte; + int i; + + inprogress_rd_rem= 0; + + if (result > 0) + { + if (rd_rem_urg) + { +#if DEBUG +fprintf(stderr, "\n\r%d urg bytes\n\r", result); +#endif + if (urg_1_size > MAX_EXTRA_1_NEW_SIZE) + { + keep_size= MAX_EXTRA_1_NEW_SIZE/2; + memmove(urg_1, urg_1+urg_1_size-keep_size, + keep_size); + urg_1_size= keep_size; + } + new_buf= realloc(urg_1, urg_1_size+result); + if (new_buf == NULL) + { + fprintf(stderr, + "rlogin: warning realloc %d failed\n", + urg_1_size+result); + return; + } + memcpy(new_buf+urg_1_size, + rd_rem_buf+rd_rem_offset, result); + urg_1= new_buf; + urg_1_size += result; + return; + } + more2read_rem= 0; + more2write_1= 1; + wr_1_size= result; + wr_1_offset= rd_rem_offset; + return; + } + if (result == -1 && error == EURG) + { +#if DEBUG +fprintf(stderr, "\n\rEURG\n\r"); +#endif + rd_rem_urg= 1; + tcpopt.nwto_flags= NWTO_RCV_URG; + result= ioctl(rem, NWIOSTCPOPT, &tcpopt); + if (result == -1) + { + fprintf(stderr, + "rlogin: NWIOSTCPOPT on %d failed (%s)\n", + rem, strerror(errno)); + exit(1); + } + return; + } + if (result == -1 && error == ENOURG) + { +#if DEBUG +fprintf(stderr, "\n\rENOURG\n\r"); +#endif + rd_rem_urg= 0; + tcpopt.nwto_flags= NWTO_RCV_NOTURG; + result= ioctl(rem, NWIOSTCPOPT, &tcpopt); + if (result == -1) + { + fprintf(stderr, + "rlogin: NWIOSTCPOPT on %d failed (%s)\n", + rem, strerror(errno)); + exit(1); + } + if (urg_1_size != 0) + { + urg_byte= urg_1[urg_1_size-1]; + urg_1_size--; + do_urg(urg_byte); + if (urg_1_size == 0) + return; + if (extra_1_new_size + urg_1_size > MAX_EXTRA_1_NEW_SIZE) + { + extra_1_new_size= 0; + free(extra_1_new); + extra_1_new= NULL; + } + if (extra_1_new_size != 0) + { + new_buf= realloc(extra_1_new, + extra_1_new_size+urg_1_size); + if (new_buf == 0) + { + extra_1_new_size= 0; + free(extra_1_new); + extra_1_new= NULL; + } + else + { + extra_1_new= new_buf; + memcpy(extra_1_new+extra_1_new_size, + urg_1, urg_1_size); + extra_1_new_size += urg_1_size; + urg_1_size= 0; + free(urg_1); + urg_1= NULL; + } + } + if (extra_1_new_size == 0) + { + extra_1_new_size= urg_1_size; + extra_1_new= urg_1; + urg_1_size= 0; + urg_1= NULL; + } + more2read_rem= 0; + more2write_1= 1; + } + return; + } + if (result == -1 && error == EINTR) + { + /* Never mind. */ + return; + } + if (result == 0) + { + msg("connection closed."); + done(0); + } + if (result < 0) { + fprintf(stderr, "rlogin: %s\n", strerror(error)); + } + done(1); +} + +static void +completed_wr_rem(result, error) + int result; + int error; +{ + inprogress_wr_rem= 0; + + if (result > 0) + { + if (extra_wr_rem != NULL) + { + assert (result <= extra_wr_rem_size); + extra_wr_rem_size -= result; + extra_wr_rem_offset += result; + if (extra_wr_rem_size == 0) + { + free(extra_wr_rem); + extra_wr_rem= NULL; + if (wr_rem_size == 0) + { + more2write_rem= 0; + more2read_0= 1; + } + } + return; + } + + assert (result <= wr_rem_size); + wr_rem_size -= result; + wr_rem_offset += result; + if (wr_rem_size == 0) + { + more2write_rem= 0; + more2read_0= 1; + } + return; + } + if (result < 0) { + fprintf(stderr, "rlogin: %s\n", strerror(error)); + } + done(1); +} + +static void +do_urg(urg_byte) + int urg_byte; +{ +#if DEBUG + fprintf(stderr, "rlogin: warning got urg_byte 0x%x\r\n", urg_byte); +#endif + if (urg_byte & TIOCPKT_WINDOW) + { + if (dosigwinch == 0) + { + sendwindow(); + signal(SIGWINCH, sigwinch); + } + dosigwinch= 1; + } +} + +static void +echo(c) + int c; +{ + u_char c1; + char *new_buf; + + new_buf= realloc(extra_1_new, extra_1_new_size+6); + if (new_buf == NULL) + return; + extra_1_new= new_buf; + new_buf= extra_1_new+extra_1_new_size; + + c1= escapechar; + if (c1 < ' ') + { + *new_buf++= '^'; + *new_buf++= c1 + '@'; + } + else if (c1 == 0x7f) + { + *new_buf++= '^'; + *new_buf++= '?'; + } + else + *new_buf++= c1; + + if (c < ' ') + { + *new_buf++= '^'; + *new_buf++= c + '@'; + } + else if (c == 0x7f) + { + *new_buf++= '^'; + *new_buf++= '?'; + } + else + *new_buf++= c; + + *new_buf++= '\r'; + *new_buf++= '\n'; + extra_1_new_size= new_buf-extra_1_new; + more2write_1= 1; +} + +static void +finish() +{ + done(0); +} + +static char cmdbuf[256]; + +static void +subshell() +{ + /* Start a subshell. Based on the first character of the command, + * the tcp connection will be present at fd 3 ('+'), or at + * fd 0 and fd 1 ('=') + */ + int r, pid, stat, len; + char *shell, *cmd; + + /* cancel the reads and writes that are in progress. */ + if (inprogress_0) + { + r= fcancel(0, ASIO_READ); + if (r != 0) abort(); + } + if (inprogress_1) + { + r= fcancel(1, ASIO_WRITE); + if (r != 0) abort(); + } + if (inprogress_rd_rem) + { + r= fcancel(rem, ASIO_READ); + if (r != 0) abort(); + } + if (inprogress_wr_rem) + { + r= fcancel(rem, ASIO_WRITE); + if (r != 0) abort(); + } + + mode(0); + + pid= fork(); + if (pid == -1) abort(); + if (pid != 0) + { + r= waitpid(pid, &stat, 0); + if (r != pid) abort(); + +#if DEBUG + fprintf(stderr, "stat: 0x%x\n", stat); +#endif + mode(1); + return; + } + + (void)signal(SIGINT, SIG_DFL); + + shell= getenv("SHELL"); + if (shell == NULL) + shell= "/bin/sh"; + printf("~!\ncommand [%s]: ", shell); + cmd= fgets(cmdbuf, sizeof(cmdbuf), stdin); + if (cmd == NULL) + exit(0); +#if DEBUG + printf("got command '%s'\n", cmd); +#endif + + /* Strip the trailing newline */ + len= strlen(cmd); + if (len > 0 && cmd[len-1] == '\n') + cmd[len-1]= '\0'; + else + printf("\n"); + + /* Skip leading white space */ + while (*cmd != '\0' && isspace(*cmd)) + cmd++; + + if (*cmd == '+') + { + if (rem != 3) + { + dup2(rem, 3); + close(rem); + } + cmd++; + } + else if (*cmd == '=') + { + dup2(rem, 0); + dup2(rem, 1); + close(rem); + cmd++; + } + else + close(rem); + if (*cmd == '\0') + { + r= execl(shell, shell, NULL); + } + else + { + r= execl("/bin/sh", "sh", "-c", cmd, NULL); + } + printf("exec failed: %d, %d\n", r, errno); + exit(0); +} +#endif /* __minix_vmd */ diff --git a/commands/simple/rmdir.c b/commands/simple/rmdir.c new file mode 100755 index 000000000..6d4ba833d --- /dev/null +++ b/commands/simple/rmdir.c @@ -0,0 +1,46 @@ +/* rmdir - remove directory. Author: Kees J. Bot + */ +#define nil 0 +#include <sys/types.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> + +void tell(char *what) +{ + write(2, what, strlen(what)); +} + +void report(char *label) +{ + char *err= strerror(errno); + + tell("rmdir: "); + tell(label); + tell(": "); + tell(err); + tell("\n"); +} + +int main(int argc, char **argv) +{ + int i, ex= 0; + + if (argc < 2) { + tell("Usage: rmdir directory ...\n"); + exit(1); + } + + i=1; + do { + if (rmdir(argv[i]) < 0) { + report(argv[i]); + ex= 1; + } + } while (++i < argc); + + exit(ex); +} +/* Kees J. Bot 27-12-90. */ diff --git a/commands/simple/rsh.c b/commands/simple/rsh.c new file mode 100755 index 000000000..365f1a937 --- /dev/null +++ b/commands/simple/rsh.c @@ -0,0 +1,568 @@ +/*- + * Copyright (c) 1983, 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1983, 1990 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)rsh.c 5.24 (Berkeley) 7/1/91"; +#endif /* not lint */ + +/* + * $Source$ + * $Header$ + */ + +#if _MINIX +#include <sys/types.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <net/netlib.h> +#include <net/gen/netdb.h> +void main _ARGS(( int argc, char *argv[] )); +void usage _ARGS(( void )); +char *copyargs _ARGS(( char **argv )); +void sendsig _ARGS(( int signo )); +void talk _ARGS(( int nflag, long omask, int pid, int rem )); + +#define _PATH_RLOGIN1 "/bin/rlogin" +#define _PATH_RLOGIN2 "/usr/bin/rlogin" + +typedef unsigned u_int; +#else +#include <sys/types.h> +#include <sys/signal.h> +#include <sys/socket.h> +#include <sys/ioctl.h> +#include <sys/file.h> + +#include <netinet/in.h> +#include <netdb.h> + +#include <pwd.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <varargs.h> +#include "pathnames.h" +#endif + +#ifdef KERBEROS +#include <kerberosIV/des.h> +#include <kerberosIV/krb.h> + +CREDENTIALS cred; +Key_schedule schedule; +int use_kerberos = 1, doencrypt; +char dst_realm_buf[REALM_SZ], *dest_realm; +extern char *krb_realmofhost(); +#endif + +/* + * rsh - remote shell + */ +extern int errno; +int rfd2; + +void +main(argc, argv) + int argc; + char **argv; +{ + extern char *optarg; + extern int optind; + struct passwd *pw; + struct servent *sp; + long omask; + int argoff, asrsh, ch, dflag, nflag, one, pid, rem, uid; + register char *p; + char *args, *host, *user; +#if !_MINIX + char *copyargs(); + void sendsig(); +#endif + + argoff = asrsh = dflag = nflag = 0; + one = 1; + host = user = NULL; + + /* if called as something other than "rsh", use it as the host name */ + if (p = rindex(argv[0], '/')) + ++p; + else + p = argv[0]; + if (strcmp(p, "rsh")) + host = p; + else + asrsh = 1; + + /* handle "rsh host flags" */ + if (!host && argc > 2 && argv[1][0] != '-') { + host = argv[1]; + argoff = 1; + } + +#ifdef KERBEROS +#ifdef CRYPT +#define OPTIONS "8KLdek:l:nwx" +#else +#define OPTIONS "8KLdek:l:nw" +#endif +#else +#define OPTIONS "8KLdel:nw" +#endif + while ((ch = getopt(argc - argoff, argv + argoff, OPTIONS)) != EOF) + switch(ch) { + case 'K': +#ifdef KERBEROS + use_kerberos = 0; +#endif + break; + case 'L': /* -8Lew are ignored to allow rlogin aliases */ + case 'e': + case 'w': + case '8': + break; + case 'd': + dflag = 1; + break; + case 'l': + user = optarg; + break; +#ifdef KERBEROS + case 'k': + dest_realm = dst_realm_buf; + strncpy(dest_realm, optarg, REALM_SZ); + break; +#endif + case 'n': + nflag = 1; + break; +#ifdef KERBEROS +#ifdef CRYPT + case 'x': + doencrypt = 1; + des_set_key(cred.session, schedule); + break; +#endif +#endif + case '?': + default: + usage(); + } + optind += argoff; + + /* if haven't gotten a host yet, do so */ + if (!host && !(host = argv[optind++])) + usage(); + + /* if no further arguments, must have been called as rlogin. */ + if (!argv[optind]) { + if (asrsh) + *argv = "rlogin"; + execv(_PATH_RLOGIN1, argv); + execv(_PATH_RLOGIN2, argv); + (void)fprintf(stderr, "rsh: can't exec rlogin\n"); + exit(1); + } + + argc -= optind; + argv += optind; + + if (!(pw = getpwuid(uid = getuid()))) { + (void)fprintf(stderr, "rsh: unknown user id.\n"); + exit(1); + } + if (!user) + user = pw->pw_name; + +#ifdef KERBEROS +#ifdef CRYPT + /* -x turns off -n */ + if (doencrypt) + nflag = 0; +#endif +#endif + + args = copyargs(argv); + + sp = NULL; +#ifdef KERBEROS + if (use_kerberos) { + sp = getservbyname((doencrypt ? "ekshell" : "kshell"), "tcp"); + if (sp == NULL) { + use_kerberos = 0; + warning("can't get entry for %s/tcp service", + doencrypt ? "ekshell" : "kshell"); + } + } +#endif + if (sp == NULL) + sp = getservbyname("shell", "tcp"); + if (sp == NULL) { + (void)fprintf(stderr, "rsh: shell/tcp: unknown service.\n"); + exit(1); + } + +#ifdef KERBEROS +try_connect: + if (use_kerberos) { + rem = KSUCCESS; + errno = 0; + if (dest_realm == NULL) + dest_realm = krb_realmofhost(host); + +#ifdef CRYPT + if (doencrypt) + rem = krcmd_mutual(&host, sp->s_port, user, args, + &rfd2, dest_realm, &cred, schedule); + else +#endif + rem = krcmd(&host, sp->s_port, user, args, &rfd2, + dest_realm); + if (rem < 0) { + use_kerberos = 0; + sp = getservbyname("shell", "tcp"); + if (sp == NULL) { + (void)fprintf(stderr, + "rsh: unknown service shell/tcp.\n"); + exit(1); + } + if (errno == ECONNREFUSED) + warning("remote host doesn't support Kerberos"); + if (errno == ENOENT) + warning("can't provide Kerberos auth data"); + goto try_connect; + } + } else { + if (doencrypt) { + (void)fprintf(stderr, + "rsh: the -x flag requires Kerberos authentication.\n"); + exit(1); + } + rem = rcmd(&host, sp->s_port, pw->pw_name, user, args, &rfd2); + } +#else + rem = rcmd(&host, sp->s_port, pw->pw_name, user, args, &rfd2); +#endif + + if (rem < 0) + exit(1); + + if (rfd2 < 0) { + (void)fprintf(stderr, "rsh: can't establish stderr.\n"); + exit(1); + } +#if !_MINIX + if (dflag) { + if (setsockopt(rem, SOL_SOCKET, SO_DEBUG, &one, + sizeof(one)) < 0) + (void)fprintf(stderr, "rsh: setsockopt: %s.\n", + strerror(errno)); + if (setsockopt(rfd2, SOL_SOCKET, SO_DEBUG, &one, + sizeof(one)) < 0) + (void)fprintf(stderr, "rsh: setsockopt: %s.\n", + strerror(errno)); + } +#endif + + (void)setuid(uid); +#if !_MINIX + omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGTERM)); +#endif + if (signal(SIGINT, SIG_IGN) != SIG_IGN) + (void)signal(SIGINT, sendsig); + if (signal(SIGQUIT, SIG_IGN) != SIG_IGN) + (void)signal(SIGQUIT, sendsig); + if (signal(SIGTERM, SIG_IGN) != SIG_IGN) + (void)signal(SIGTERM, sendsig); + + if (!nflag) { + pid = fork(); + if (pid < 0) { + (void)fprintf(stderr, + "rsh: fork: %s.\n", strerror(errno)); + exit(1); + } + } + +#ifdef KERBEROS +#ifdef CRYPT + if (!doencrypt) +#endif +#endif + { +#if _MINIX + ; +#else + (void)ioctl(rfd2, FIONBIO, &one); + (void)ioctl(rem, FIONBIO, &one); +#endif + } + + talk(nflag, omask, pid, rem); + + if (!nflag && pid) + { +#if DEBUG + printf("killing %d with %d\n", pid, SIGKILL); +#endif + (void)kill(pid, SIGKILL); + } + exit(0); +} + +void +talk(nflag, omask, pid, rem) + int nflag, pid; + long omask; + register int rem; +{ + register int cc, wc; + register char *bp; + int readfrom, ready, rembits; + char buf[BUFSIZ]; +#if _MINIX + int pid1; +#endif + + if (!nflag && pid == 0) { + (void)close(rfd2); + +reread: errno = 0; + if ((cc = read(0, buf, sizeof buf)) <= 0) + goto done; + bp = buf; + +rewrite: +#if !_MINIX + rembits = 1 << rem; + if (select(16, 0, &rembits, 0, 0) < 0) { + if (errno != EINTR) { + (void)fprintf(stderr, + "rsh: select: %s.\n", strerror(errno)); + exit(1); + } + goto rewrite; + } + if ((rembits & (1 << rem)) == 0) + goto rewrite; +#endif +#ifdef KERBEROS +#ifdef CRYPT + if (doencrypt) + wc = des_write(rem, bp, cc); + else +#endif +#endif + wc = write(rem, bp, cc); + if (wc < 0) { +#if !_MINIX + if (errno == EWOULDBLOCK) + goto rewrite; +#endif + goto done; + } + bp += wc; + cc -= wc; + if (cc == 0) + goto reread; + goto rewrite; +done: +#if _MINIX + ioctl(rem, NWIOTCPSHUTDOWN, NULL); +#else + (void)shutdown(rem, 1); +#endif + exit(0); + } + +#if _MINIX + pid1= fork(); + if (pid1 == -1) + { + (void)fprintf(stderr, "rsh: fork: %s.\n", strerror(errno)); + exit(1); + } + close (pid1 ? rfd2 : rem); + for(;;) + { + errno = 0; + cc = read(pid1 ? rem : rfd2, buf, sizeof buf); + if (cc <= 0) + { + if (pid1) + { +#if DEBUG + printf("killing %d with %d\n", pid1, SIGKILL); +#endif + kill(pid1, SIGKILL); + return; + } + exit(0); + } + (void)write(pid1 ? 1 : 2, buf, cc); + } +#else + (void)sigsetmask(omask); + readfrom = (1 << rfd2) | (1 << rem); + do { + ready = readfrom; + if (select(16, &ready, 0, 0, 0) < 0) { + if (errno != EINTR) { + (void)fprintf(stderr, + "rsh: select: %s.\n", strerror(errno)); + exit(1); + } + continue; + } + if (ready & (1 << rfd2)) { + errno = 0; +#ifdef KERBEROS +#ifdef CRYPT + if (doencrypt) + cc = des_read(rfd2, buf, sizeof buf); + else +#endif +#endif + cc = read(rfd2, buf, sizeof buf); + if (cc <= 0) { + if (errno != EWOULDBLOCK) + readfrom &= ~(1 << rfd2); + } else + (void)write(2, buf, cc); + } + if (ready & (1 << rem)) { + errno = 0; +#ifdef KERBEROS +#ifdef CRYPT + if (doencrypt) + cc = des_read(rem, buf, sizeof buf); + else +#endif +#endif + cc = read(rem, buf, sizeof buf); + if (cc <= 0) { + if (errno != EWOULDBLOCK) + readfrom &= ~(1 << rem); + } else + (void)write(1, buf, cc); + } + } while (readfrom); +#endif +} + +void +sendsig(signo) + char signo; +{ +#ifdef KERBEROS +#ifdef CRYPT + if (doencrypt) + (void)des_write(rfd2, &signo, 1); + else +#endif +#endif + (void)write(rfd2, &signo, 1); +} + +#ifdef KERBEROS +/* VARARGS */ +warning(va_alist) +va_dcl +{ + va_list ap; + char *fmt; + + (void)fprintf(stderr, "rsh: warning, using standard rsh: "); + va_start(ap); + fmt = va_arg(ap, char *); + vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, ".\n"); +} +#endif + +char * +copyargs(argv) + char **argv; +{ + register int cc; + register char **ap, *p; + char *args; +#if !_MINIX + char *malloc(); +#endif + + cc = 0; + for (ap = argv; *ap; ++ap) + cc += strlen(*ap) + 1; + if (!(args = malloc((u_int)cc))) { + (void)fprintf(stderr, "rsh: %s.\n", strerror(ENOMEM)); + exit(1); + } + for (p = args, ap = argv; *ap; ++ap) { + (void)strcpy(p, *ap); + for (p = strcpy(p, *ap); *p; ++p); + if (ap[1]) + *p++ = ' '; + } + return(args); +} + +void +usage() +{ + (void)fprintf(stderr, + "usage: rsh [-nd%s]%s[-l login] host [command]\n", +#ifdef KERBEROS +#ifdef CRYPT + "x", " [-k realm] "); +#else + "", " [-k realm] "); +#endif +#else + "", " "); +#endif + exit(1); +} diff --git a/commands/simple/sed.c b/commands/simple/sed.c new file mode 100755 index 000000000..4f11bfa64 --- /dev/null +++ b/commands/simple/sed.c @@ -0,0 +1,1613 @@ +/* sed - stream editor Author: Eric S. Raymond */ + +/* This used to be three different files with the following makefile: + * (Note the chmem). + +CFLAGS= -F -T. + +OBJS= sedcomp.s sedexec.s + +sed: $(OBJS) + cc -T. -o sed $(OBJS) + @chmem =13312 sed + +$(OBJS): sed.h + + * If you want longer lines: increase MAXBUF. + * If you want scripts with more text: increase POOLSIZE. + * If you want more commands per script: increase MAXCMDS. + */ + +#include <ctype.h> +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +/*+++++++++++++++*/ + +/* Sed.h -- types and constants for the stream editor */ + +/* Data area sizes used by both modules */ +#define MAXBUF 4000 /* current line buffer size */ +#define MAXAPPENDS 20 /* maximum number of appends */ +#define MAXTAGS 9 /* tagged patterns are \1 to \9 */ + +/* Constants for compiled-command representation */ +#define EQCMD 0x01 /* = -- print current line number */ +#define ACMD 0x02 /* a -- append text after current line */ +#define BCMD 0x03 /* b -- branch to label */ +#define CCMD 0x04 /* c -- change current line */ +#define DCMD 0x05 /* d -- delete all of pattern space */ +#define CDCMD 0x06 /* D -- delete first line of pattern space */ +#define GCMD 0x07 /* g -- copy hold space to pattern space */ +#define CGCMD 0x08 /* G -- append hold space to pattern space */ +#define HCMD 0x09 /* h -- copy pattern space to hold space */ +#define CHCMD 0x0A /* H -- append pattern space to hold space */ +#define ICMD 0x0B /* i -- insert text before current line */ +#define LCMD 0x0C /* l -- print pattern space in escaped form */ +#define NCMD 0x0D /* n -- get next line into pattern space */ +#define CNCMD 0x0E /* N -- append next line to pattern space */ +#define PCMD 0x0F /* p -- print pattern space to output */ +#define CPCMD 0x10 /* P -- print first line of pattern space */ +#define QCMD 0x11 /* q -- exit the stream editor */ +#define RCMD 0x12 /* r -- read in a file after current line */ +#define SCMD 0x13 /* s -- regular-expression substitute */ +#define TCMD 0x14 /* t -- branch on any substitute successful */ +#define CTCMD 0x15 /* T -- branch on any substitute failed */ +#define WCMD 0x16 /* w -- write pattern space to file */ +#define CWCMD 0x17 /* W -- write first line of pattern space */ +#define XCMD 0x18 /* x -- exhange pattern and hold spaces */ +#define YCMD 0x19 /* y -- transliterate text */ + +struct cmd_t { /* compiled-command representation */ + char *addr1; /* first address for command */ + char *addr2; /* second address for command */ + union { + char *lhs; /* s command lhs */ + struct cmd_t *link; /* label link */ + } u; + char command; /* command code */ + char *rhs; /* s command replacement string */ + FILE *fout; /* associated output file descriptor */ + struct { + char allbut; /* was negation specified? */ + char global; /* was g postfix specified? */ + char print; /* was p postfix specified? */ + char inrange; /* in an address range? */ + } flags; +}; +typedef struct cmd_t sedcmd; /* use this name for declarations */ + +#define BAD ((char *) -1) /* guaranteed not a string ptr */ + + + +/* Address and regular expression compiled-form markers */ +#define STAR 1 /* marker for Kleene star */ +#define CCHR 2 /* non-newline character to be matched + * follows */ +#define CDOT 4 /* dot wild-card marker */ +#define CCL 6 /* character class follows */ +#define CNL 8 /* match line start */ +#define CDOL 10 /* match line end */ +#define CBRA 12 /* tagged pattern start marker */ +#define CKET 14 /* tagged pattern end marker */ +#define CBACK 16 /* backslash-digit pair marker */ +#define CLNUM 18 /* numeric-address index follows */ +#define CEND 20 /* symbol for end-of-source */ +#define CEOF 22 /* end-of-field mark */ + +/* Sed.h ends here */ + +#ifndef CMASK +#define CMASK 0xFF /* some char type should have been unsigned + * char? */ +#endif + +/*+++++++++++++++*/ + +/* Sed - stream editor Author: Eric S. Raymond */ + +/* + The stream editor compiles its command input (from files or -e options) + into an internal form using compile() then executes the compiled form using + execute(). Main() just initializes data structures, interprets command line + options, and calls compile() and execute() in appropriate sequence. + + The data structure produced by compile() is an array of compiled-command + structures (type sedcmd). These contain several pointers into pool[], the + regular-expression and text-data pool, plus a command code and g & p flags. + In the special case that the command is a label the struct will hold a ptr + into the labels array labels[] during most of the compile, until resolve() + resolves references at the end. + + The operation of execute() is described in its source module. +*/ + +/* #include <stdio.h> */ +/* #include "sed.h" */ + +/* Imported functions */ + +/***** public stuff ******/ + +#define MAXCMDS 500 /* maximum number of compiled commands */ +#define MAXLINES 256 /* max # numeric addresses to compile */ + +/* Main data areas */ +char linebuf[MAXBUF + 1]; /* current-line buffer */ +sedcmd cmds[MAXCMDS + 1]; /* hold compiled commands */ +long linenum[MAXLINES]; /* numeric-addresses table */ + +/* Miscellaneous shared variables */ +int nflag; /* -n option flag */ +int eargc; /* scratch copy of argument count */ +char **eargv; /* scratch copy of argument list */ +char bits[] = {1, 2, 4, 8, 16, 32, 64, 128}; + +/***** module common stuff *****/ + +#define POOLSIZE 20000 /* size of string-pool space */ +#define WFILES 10 /* max # w output files that can be compiled */ +#define RELIMIT 256 /* max chars in compiled RE */ +#define MAXDEPTH 20 /* maximum {}-nesting level */ +#define MAXLABS 50 /* max # of labels that can be handled */ + +#define SKIPWS(pc) while ((*pc==' ') || (*pc=='\t')) pc++ +#define ABORT(msg) (fprintf(stderr, msg, linebuf), quit(2)) +#define IFEQ(x, v) if (*x == v) x++ , /* do expression */ + +/* Error messages */ +static char AGMSG[] = "sed: garbled address %s\n"; +static char CGMSG[] = "sed: garbled command %s\n"; +static char TMTXT[] = "sed: too much text: %s\n"; +static char AD1NG[] = "sed: no addresses allowed for %s\n"; +static char AD2NG[] = "sed: only one address allowed for %s\n"; +static char TMCDS[] = "sed: too many commands, last was %s\n"; +static char COCFI[] = "sed: cannot open command-file %s\n"; +static char UFLAG[] = "sed: unknown flag %c\n"; +static char CCOFI[] = "sed: cannot create %s\n"; +static char ULABL[] = "sed: undefined label %s\n"; +static char TMLBR[] = "sed: too many {'s\n"; +static char FRENL[] = "sed: first RE must be non-null\n"; +static char NSCAX[] = "sed: no such command as %s\n"; +static char TMRBR[] = "sed: too many }'s\n"; +static char DLABL[] = "sed: duplicate label %s\n"; +static char TMLAB[] = "sed: too many labels: %s\n"; +static char TMWFI[] = "sed: too many w files\n"; +static char REITL[] = "sed: RE too long: %s\n"; +static char TMLNR[] = "sed: too many line numbers\n"; +static char TRAIL[] = "sed: command \"%s\" has trailing garbage\n"; + +typedef struct { /* represent a command label */ + char *name; /* the label name */ + sedcmd *last; /* it's on the label search list */ + sedcmd *address; /* pointer to the cmd it labels */ +} + + label; + +/* Label handling */ +static label labels[MAXLABS]; /* here's the label table */ +static label *lab = labels + 1; /* pointer to current label */ +static label *lablst = labels; /* header for search list */ + +/* String pool for regular expressions, append text, etc. etc. */ +static char pool[POOLSIZE]; /* the pool */ +static char *fp = pool; /* current pool pointer */ +static char *poolend = pool + POOLSIZE; /* pointer past pool end */ + +/* Compilation state */ +static FILE *cmdf = NULL; /* current command source */ +static char *cp = linebuf; /* compile pointer */ +static sedcmd *cmdp = cmds; /* current compiled-cmd ptr */ +static char *lastre = NULL; /* old RE pointer */ +static int bdepth = 0; /* current {}-nesting level */ +static int bcount = 0; /* # tagged patterns in current RE */ + +/* Compilation flags */ +static int eflag; /* -e option flag */ +static int gflag; /* -g option flag */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(static void compile, (void)); +_PROTOTYPE(static int cmdcomp, (int cchar)); +_PROTOTYPE(static char *rhscomp, (char *rhsp, int delim)); +_PROTOTYPE(static char *recomp, (char *expbuf, int redelim)); +_PROTOTYPE(static int cmdline, (char *cbuf)); +_PROTOTYPE(static char *address, (char *expbuf)); +_PROTOTYPE(static char *gettext, (char *txp)); +_PROTOTYPE(static label *search, (label *ptr)); +_PROTOTYPE(static void resolve, (void)); +_PROTOTYPE(static char *ycomp, (char *ep, int delim)); +_PROTOTYPE(void quit, (int n)); +_PROTOTYPE(void execute, (void)); +_PROTOTYPE(static int selected, (sedcmd *ipc)); +_PROTOTYPE(static int match, (char *expbuf, int gf)); +_PROTOTYPE(static int advance, (char *lp, char *ep)); +_PROTOTYPE(static int substitute, (sedcmd *ipc)); +_PROTOTYPE(static void dosub, (char *rhsbuf)); +_PROTOTYPE(static char *place, (char *asp, char *al1, char *al2)); +_PROTOTYPE(static void listto, (char *p1, FILE *fp)); +_PROTOTYPE(static void truncated, (int h)); +_PROTOTYPE(static void command, (sedcmd *ipc)); +_PROTOTYPE(static void openfile, (char *file)); +_PROTOTYPE(static void get, (void)); +_PROTOTYPE(static void initget, (void)); +_PROTOTYPE(static char *getline, (char *buf)); +_PROTOTYPE(static int Memcmp, (char *a, char *b, int count)); +_PROTOTYPE(static void readout, (void)); + +int main(argc, argv) +/* Main sequence of the stream editor */ +int argc; +char *argv[]; +{ + eargc = argc; /* set local copy of argument count */ + eargv = argv; /* set local copy of argument list */ + cmdp->addr1 = pool; /* 1st addr expand will be at pool start */ + if (eargc == 1) quit(0); /* exit immediately if no arguments */ + /* Scan through the arguments, interpreting each one */ + while ((--eargc > 0) && (**++eargv == '-')) switch (eargv[0][1]) { + case 'e': + eflag++; + compile(); /* compile with e flag on */ + eflag = 0; + continue; /* get another argument */ + case 'f': + if (eargc-- <= 0) /* barf if no -f file */ + quit(2); + if ((cmdf = fopen(*++eargv, "r")) == NULL) { + fprintf(stderr, COCFI, *eargv); + quit(2); + } + compile(); /* file is O.K., compile it */ + fclose(cmdf); + continue; /* go back for another argument */ + case 'g': + gflag++; /* set global flag on all s cmds */ + continue; + case 'n': + nflag++; /* no print except on p flag or w */ + continue; + default: + fprintf(stdout, UFLAG, eargv[0][1]); + continue; + } + + + if (cmdp == cmds) { /* no commands have been compiled */ + eargv--; + eargc++; + eflag++; + compile(); + eflag = 0; + eargv++; + eargc--; + } + if (bdepth) /* we have unbalanced squigglies */ + ABORT(TMLBR); + + lablst->address = cmdp; /* set up header of label linked list */ + resolve(); /* resolve label table indirections */ + execute(); /* execute commands */ + quit(0); /* everything was O.K. if we got here */ + return(0); +} + + +#define H 0x80 /* 128 bit, on if there's really code for + * command */ +#define LOWCMD 56 /* = '8', lowest char indexed in cmdmask */ + +/* Indirect through this to get command internal code, if it exists */ +static char cmdmask[] = +{ + 0, 0, H, 0, 0, H + EQCMD, 0, 0, + 0, 0, 0, 0, H + CDCMD, 0, 0, CGCMD, + CHCMD, 0, 0, 0, 0, 0, CNCMD, 0, + CPCMD, 0, 0, 0, H + CTCMD, 0, 0, H + CWCMD, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, H + ACMD, H + BCMD, H + CCMD, DCMD, 0, 0, GCMD, + HCMD, H + ICMD, 0, 0, H + LCMD, 0, NCMD, 0, + PCMD, H + QCMD, H + RCMD, H + SCMD, H + TCMD, 0, 0, H + WCMD, + XCMD, H + YCMD, 0, H + BCMD, 0, H, 0, 0, +}; + +static void compile() +/* Precompile sed commands out of a file */ +{ + char ccode; + + + for (;;) { /* main compilation loop */ + if (*cp == '\0') { /* get a new command line */ + *linebuf = '\0'; /* K.H */ + if (cmdline(cp = linebuf) < 0) break; + } + SKIPWS(cp); + if (*cp == '\0') /* empty */ + continue; + if (*cp == '#') { /* comment */ + while (*cp) ++cp; + continue; + } + if (*cp == ';') { /* ; separates cmds */ + cp++; + continue; + } + + /* Compile first address */ + if (fp > poolend) + ABORT(TMTXT); + else if ((fp = address(cmdp->addr1 = fp)) == BAD) + ABORT(AGMSG); + + if (fp == cmdp->addr1) {/* if empty RE was found */ + if (lastre) /* if there was previous RE */ + cmdp->addr1 = lastre; /* use it */ + else + ABORT(FRENL); + } else if (fp == NULL) {/* if fp was NULL */ + fp = cmdp->addr1; /* use current pool location */ + cmdp->addr1 = NULL; + } else { + lastre = cmdp->addr1; + if (*cp == ',' || *cp == ';') { /* there's 2nd addr */ + cp++; + if (fp > poolend) ABORT(TMTXT); + fp = address(cmdp->addr2 = fp); + if (fp == BAD || fp == NULL) ABORT(AGMSG); + if (fp == cmdp->addr2) + cmdp->addr2 = lastre; + else + lastre = cmdp->addr2; + } else + cmdp->addr2 = NULL; /* no 2nd address */ + } + if (fp > poolend) ABORT(TMTXT); + + SKIPWS(cp); /* discard whitespace after address */ + IFEQ(cp, '!') cmdp->flags.allbut = 1; + + SKIPWS(cp); /* get cmd char, range-check it */ + if ((*cp < LOWCMD) || (*cp > '~') + || ((ccode = cmdmask[*cp - LOWCMD]) == 0)) + ABORT(NSCAX); + + cmdp->command = ccode & ~H; /* fill in command value */ + if ((ccode & H) == 0) /* if no compile-time code */ + cp++; /* discard command char */ + else if (cmdcomp(*cp++))/* execute it; if ret = 1 */ + continue; /* skip next line read */ + + if (++cmdp >= cmds + MAXCMDS) ABORT(TMCDS); + + SKIPWS(cp); /* look for trailing stuff */ + if (*cp != '\0' && *cp != ';' && *cp != '#') ABORT(TRAIL); + } +} + +static int cmdcomp(cchar) +/* Compile a single command */ +register char cchar; /* character name of command */ +{ + static sedcmd **cmpstk[MAXDEPTH]; /* current cmd stack for {} */ + static char *fname[WFILES]; /* w file name pointers */ + static FILE *fout[WFILES]; /* w file file ptrs */ + static int nwfiles = 1; /* count of open w files */ + int i; /* indexing dummy used in w */ + sedcmd *sp1, *sp2; /* temps for label searches */ + label *lpt; + char redelim; /* current RE delimiter */ + + fout[0] = stdout; + switch (cchar) { + case '{': /* start command group */ + cmdp->flags.allbut = !cmdp->flags.allbut; + cmpstk[bdepth++] = &(cmdp->u.link); + if (++cmdp >= cmds + MAXCMDS) ABORT(TMCDS); + return(1); + + case '}': /* end command group */ + if (cmdp->addr1) ABORT(AD1NG); /* no addresses allowed */ + if (--bdepth < 0) ABORT(TMRBR); /* too many right braces */ + *cmpstk[bdepth] = cmdp; /* set the jump address */ + return(1); + + case '=': /* print current source line number */ + case 'q': /* exit the stream editor */ + if (cmdp->addr2) ABORT(AD2NG); + break; + + case ':': /* label declaration */ + if (cmdp->addr1) ABORT(AD1NG); /* no addresses allowed */ + fp = gettext(lab->name = fp); /* get the label name */ + if (lpt = search(lab)) {/* does it have a double? */ + if (lpt->address) ABORT(DLABL); /* yes, abort */ + } else { /* check that it doesn't overflow label table */ + lab->last = NULL; + lpt = lab; + if (++lab >= labels + MAXLABS) ABORT(TMLAB); + } + lpt->address = cmdp; + return(1); + + case 'b': /* branch command */ + case 't': /* branch-on-succeed command */ + case 'T': /* branch-on-fail command */ + SKIPWS(cp); + if (*cp == '\0') { /* if branch is to start of cmds... */ + /* Add current command to end of label last */ + if (sp1 = lablst->last) { + while (sp2 = sp1->u.link) sp1 = sp2; + sp1->u.link = cmdp; + } else /* lablst->last == NULL */ + lablst->last = cmdp; + break; + } + fp = gettext(lab->name = fp); /* else get label into pool */ + if (lpt = search(lab)) {/* enter branch to it */ + if (lpt->address) + cmdp->u.link = lpt->address; + else { + sp1 = lpt->last; + while (sp2 = sp1->u.link) sp1 = sp2; + sp1->u.link = cmdp; + } + } else { /* matching named label not found */ + lab->last = cmdp; /* add the new label */ + lab->address = NULL; /* it's forward of here */ + if (++lab >= labels + MAXLABS) /* overflow if last */ + ABORT(TMLAB); + } + break; + + case 'a': /* append text */ + case 'i': /* insert text */ + case 'r': /* read file into stream */ + if (cmdp->addr2) ABORT(AD2NG); + case 'c': /* change text */ + if ((*cp == '\\') && (*++cp == '\n')) cp++; + fp = gettext(cmdp->u.lhs = fp); + break; + + case 'D': /* delete current line in hold space */ + cmdp->u.link = cmds; + break; + + case 's': /* substitute regular expression */ + redelim = *cp++; /* get delimiter from 1st ch */ + if ((fp = recomp(cmdp->u.lhs = fp, redelim)) == BAD) ABORT(CGMSG); + if (fp == cmdp->u.lhs) /* if compiled RE zero len */ + cmdp->u.lhs = lastre; /* use the previous one */ + else /* otherwise */ + lastre = cmdp->u.lhs; /* save the one just found */ + if ((cmdp->rhs = fp) > poolend) ABORT(TMTXT); + if ((fp = rhscomp(cmdp->rhs, redelim)) == BAD) ABORT(CGMSG); + if (gflag) cmdp->flags.global ++; + while (*cp == 'g' || *cp == 'p' || *cp == 'P') { + IFEQ(cp, 'g') cmdp->flags.global ++; + IFEQ(cp, 'p') cmdp->flags.print = 1; + IFEQ(cp, 'P') cmdp->flags.print = 2; + } + + case 'l': /* list pattern space */ + if (*cp == 'w') + cp++; /* and execute a w command! */ + else + break; /* s or l is done */ + + case 'w': /* write-pattern-space command */ + case 'W': /* write-first-line command */ + if (nwfiles >= WFILES) ABORT(TMWFI); + fp = gettext(fname[nwfiles] = fp); /* filename will be in pool */ + for (i = nwfiles - 1; i >= 0; i--) /* match it in table */ + if ((fname[i] != NULL) && + (strcmp(fname[nwfiles], fname[i]) == 0)) { + cmdp->fout = fout[i]; + return(0); + } + + /* If didn't find one, open new out file */ + if ((cmdp->fout = fopen(fname[nwfiles], "w")) == NULL) { + fprintf(stderr, CCOFI, fname[nwfiles]); + quit(2); + } + fout[nwfiles++] = cmdp->fout; + break; + + case 'y': /* transliterate text */ + fp = ycomp(cmdp->u.lhs = fp, *cp++); /* compile translit */ + if (fp == BAD) ABORT(CGMSG); /* fail on bad form */ + if (fp > poolend) ABORT(TMTXT); /* fail on overflow */ + break; + } + return(0); /* succeeded in interpreting one command */ +} + +static char *rhscomp(rhsp, delim) /* uses bcount */ + /* Generate replacement string for substitute command right hand side */ +register char *rhsp; /* place to compile expression to */ +register char delim; /* regular-expression end-mark to look for */ +{ + register char *p = cp; /* strictly for speed */ + + for (;;) + if ((*rhsp = *p++) == '\\') { /* copy; if it's a \, */ + *rhsp = *p++; /* copy escaped char */ + /* Check validity of pattern tag */ + if (*rhsp > bcount + '0' && *rhsp <= '9') return(BAD); + *rhsp++ |= 0x80;/* mark the good ones */ + continue; + } else if (*rhsp == delim) { /* found RE end, hooray... */ + *rhsp++ = '\0'; /* cap the expression string */ + cp = p; + return(rhsp); /* pt at 1 past the RE */ + } else if (*rhsp++ == '\0') /* last ch not RE end, help! */ + return(BAD); +} + +static char *recomp(expbuf, redelim) /* uses cp, bcount */ + /* Compile a regular expression to internal form */ +char *expbuf; /* place to compile it to */ +char redelim; /* RE end-marker to look for */ +{ + register char *ep = expbuf; /* current-compiled-char pointer */ + register char *sp = cp; /* source-character ptr */ + register int c; /* current-character pointer */ + char negclass; /* all-but flag */ + char *lastep; /* ptr to last expr compiled */ + char *svclass; /* start of current char class */ + char brnest[MAXTAGS]; /* bracket-nesting array */ + char *brnestp; /* ptr to current bracket-nest */ + int classct; /* class element count */ + int tags; /* # of closed tags */ + + if (*cp == redelim) /* if first char is RE endmarker */ + return(cp++, expbuf); /* leave existing RE unchanged */ + + lastep = NULL; /* there's no previous RE */ + brnestp = brnest; /* initialize ptr to brnest array */ + tags = bcount = 0; /* initialize counters */ + + if (*ep++ = (*sp == '^')) /* check for start-of-line syntax */ + sp++; + + for (;;) { + if (ep >= expbuf + RELIMIT) /* match is too large */ + return(cp = sp, BAD); + if ((c = *sp++) == redelim) { /* found the end of the RE */ + cp = sp; + if (brnestp != brnest) /* \(, \) unbalanced */ + return(BAD); + *ep++ = CEOF; /* write end-of-pattern mark */ + return(ep); /* return ptr to compiled RE */ + } + if (c != '*') /* if we're a postfix op */ + lastep = ep; /* get ready to match last */ + + switch (c) { + case '\\': + if ((c = *sp++) == '(') { /* start tagged section */ + if (bcount >= MAXTAGS) return(cp = sp, BAD); + *brnestp++ = bcount; /* update tag stack */ + *ep++ = CBRA; /* enter tag-start */ + *ep++ = bcount++; /* bump tag count */ + continue; + } else if (c == ')') { /* end tagged section */ + if (brnestp <= brnest) /* extra \) */ + return(cp = sp, BAD); + *ep++ = CKET; /* enter end-of-tag */ + *ep++ = *--brnestp; /* pop tag stack */ + tags++; /* count closed tags */ + continue; + } else if (c >= '1' && c <= '9') { /* tag use */ + if ((c -= '1') >= tags) /* too few */ + return(BAD); + *ep++ = CBACK; /* enter tag mark */ + *ep++ = c; /* and the number */ + continue; + } else if (c == '\n') /* escaped newline no good */ + return(cp = sp, BAD); + else if (c == 'n') /* match a newline */ + c = '\n'; + else if (c == 't') /* match a tab */ + c = '\t'; + else if (c == 'r') /* match a return */ + c = '\r'; + goto defchar; + + case '\0': /* ignore nuls */ + continue; + + case '\n': /* trailing pattern delimiter is missing */ + return(cp = sp, BAD); + + case '.': /* match any char except newline */ + *ep++ = CDOT; + continue; + case '*': /* 0..n repeats of previous pattern */ + if (lastep == NULL) /* if * isn't first on line */ + goto defchar; /* match a literal * */ + if (*lastep == CKET) /* can't iterate a tag */ + return(cp = sp, BAD); + *lastep |= STAR;/* flag previous pattern */ + continue; + + case '$': /* match only end-of-line */ + if (*sp != redelim) /* if we're not at end of RE */ + goto defchar; /* match a literal $ */ + *ep++ = CDOL; /* insert end-symbol mark */ + continue; + + case '[': /* begin character set pattern */ + if (ep + 17 >= expbuf + RELIMIT) ABORT(REITL); + *ep++ = CCL; /* insert class mark */ + if (negclass = ((c = *sp++) == '^')) c = *sp++; + svclass = sp; /* save ptr to class start */ + do { + if (c == '\0') ABORT(CGMSG); + + /* Handle character ranges */ + if (c == '-' && sp > svclass && *sp != ']') + for (c = sp[-2]; c < *sp; c++) + ep[c >> 3] |= bits[c & 7]; + + /* Handle escape sequences in sets */ + if (c == '\\') + if ((c = *sp++) == 'n') + c = '\n'; + else if (c == 't') + c = '\t'; + else if (c == 'r') + c = '\r'; + + /* Enter (possibly translated) char in set */ + ep[c >> 3] |= bits[c & 7]; + } while + ((c = *sp++) != ']'); + + /* Invert the bitmask if all-but was specified */ + if (negclass) for (classct = 0; classct < 16; classct++) + ep[classct] ^= 0xFF; + ep[0] &= 0xFE; /* never match ASCII 0 */ + ep += 16; /* advance ep past set mask */ + continue; + + defchar: /* match literal character */ + default: /* which is what we'd do by default */ + *ep++ = CCHR; /* insert character mark */ + *ep++ = c; + } + } +} + +static int cmdline(cbuf) /* uses eflag, eargc, cmdf */ + /* Read next command from -e argument or command file */ +register char *cbuf; +{ + register int inc; /* not char because must hold EOF */ + + *cbuf-- = 0; /* so pre-increment points us at cbuf */ + + /* E command flag is on */ + if (eflag) { + register char *p; /* ptr to current -e argument */ + static char *savep; /* saves previous value of p */ + + if (eflag > 0) { /* there are pending -e arguments */ + eflag = -1; + if (eargc-- <= 0) quit(2); /* if no arguments, barf */ + + /* Else transcribe next e argument into cbuf */ + p = *++eargv; + while (*++cbuf = *p++) + if (*cbuf == '\\') { + if ((*++cbuf = *p++) == '\0') + return(savep = NULL, -1); + else + continue; + } else if (*cbuf == '\n') { /* end of 1 cmd line */ + *cbuf = '\0'; + return(savep = p, 1); + /* We'll be back for the rest... */ + } + + /* Found end-of-string; can advance to next argument */ + return(savep = NULL, 1); + } + if ((p = savep) == NULL) return(-1); + + while (*++cbuf = *p++) + if (*cbuf == '\\') { + if ((*++cbuf = *p++) == '0') + return(savep = NULL, -1); + else + continue; + } else if (*cbuf == '\n') { + *cbuf = '\0'; + return(savep = p, 1); + } + return(savep = NULL, 1); + } + + /* If no -e flag read from command file descriptor */ + while ((inc = getc(cmdf)) != EOF) /* get next char */ + if ((*++cbuf = inc) == '\\') /* if it's escape */ + *++cbuf = inc = getc(cmdf); /* get next char */ + else if (*cbuf == '\n') /* end on newline */ + return(*cbuf = '\0', 1); /* cap the string */ + + return(*++cbuf = '\0', -1); /* end-of-file, no more chars */ +} + +static char *address(expbuf) /* uses cp, linenum */ + /* Expand an address at *cp... into expbuf, return ptr at following char */ +register char *expbuf; +{ + static int numl = 0; /* current ind in addr-number table */ + register char *rcp; /* temp compile ptr for forwd look */ + long lno; /* computed value of numeric address */ + + if (*cp == '$') { /* end-of-source address */ + *expbuf++ = CEND; /* write symbolic end address */ + *expbuf++ = CEOF; /* and the end-of-address mark (!) */ + cp++; /* go to next source character */ + return(expbuf); /* we're done */ + } + if (*cp == '/' || *cp == '\\') { /* start of regular-expression match */ + if (*cp == '\\') cp++; + return(recomp(expbuf, *cp++)); /* compile the RE */ + } + + rcp = cp; + lno = 0; /* now handle a numeric address */ + while (*rcp >= '0' && *rcp <= '9') /* collect digits */ + lno = lno * 10 + *rcp++ - '0'; /* compute their value */ + + if (rcp > cp) { /* if we caught a number... */ + *expbuf++ = CLNUM; /* put a numeric-address marker */ + *expbuf++ = numl; /* and the address table index */ + linenum[numl++] = lno; /* and set the table entry */ + if (numl >= MAXLINES) /* oh-oh, address table overflow */ + ABORT(TMLNR); /* abort with error message */ + *expbuf++ = CEOF; /* write the end-of-address marker */ + cp = rcp; /* point compile past the address */ + return(expbuf); /* we're done */ + } + return(NULL); /* no legal address was found */ +} + +static char *gettext(txp) /* uses global cp */ + /* Accept multiline input from *cp..., discarding leading whitespace */ +register char *txp; /* where to put the text */ +{ + register char *p = cp; /* this is for speed */ + + SKIPWS(p); /* discard whitespace */ + do { + if ((*txp = *p++) == '\\') /* handle escapes */ + *txp = *p++; + if (*txp == '\0') /* we're at end of input */ + return(cp = --p, ++txp); + else if (*txp == '\n') /* also SKIPWS after newline */ + SKIPWS(p); + } while + (txp++); /* keep going till we find that nul */ + return(txp); +} + +static label *search(ptr) /* uses global lablst */ + /* Find the label matching *ptr, return NULL if none */ +register label *ptr; +{ + register label *rp; + for (rp = lablst; rp < ptr; rp++) + if ((rp->name != NULL) && (strcmp(rp->name, ptr->name) == 0)) + return(rp); + return(NULL); +} + +static void resolve() +{ /* uses global lablst */ + /* Write label links into the compiled-command space */ + register label *lptr; + register sedcmd *rptr, *trptr; + + /* Loop through the label table */ + for (lptr = lablst; lptr < lab; lptr++) + if (lptr->address == NULL) { /* barf if not defined */ + fprintf(stderr, ULABL, lptr->name); + quit(2); + } else if (lptr->last) {/* if last is non-null */ + rptr = lptr->last; /* chase it */ + while (trptr = rptr->u.link) { /* resolve refs */ + rptr->u.link = lptr->address; + rptr = trptr; + } + rptr->u.link = lptr->address; + } +} + +static char *ycomp(ep, delim) +/* Compile a y (transliterate) command */ +register char *ep; /* where to compile to */ +char delim; /* end delimiter to look for */ +{ + register char *tp, *sp; + register int c; + + /* Scan the 'from' section for invalid chars */ + for (sp = tp = cp; *tp != delim; tp++) { + if (*tp == '\\') tp++; + if ((*tp == '\n') || (*tp == '\0')) return (BAD); + } + tp++; /* tp now points at first char of 'to' + * section */ + + /* Now rescan the 'from' section */ + while ((c = *sp++ & 0x7F) != delim) { + if (c == '\\' && *sp == 'n') { + sp++; + c = '\n'; + } + if ((ep[c] = *tp++) == '\\' && *tp == 'n') { + ep[c] = '\n'; + tp++; + } + if ((ep[c] == delim) || (ep[c] == '\0')) return(BAD); + } + + if (*tp != delim) /* 'to', 'from' parts have unequal lengths */ + return(BAD); + + cp = ++tp; /* point compile ptr past translit */ + + for (c = 0; c < 128; c++) /* fill in self-map entries in table */ + if (ep[c] == 0) ep[c] = c; + + return(ep + 0x80); /* return first free location past table end */ +} + +void quit(n) +int n; +{ +/* Flush buffers and exit. Now a historical relic. Rely on exit to flush + * the buffers. + */ + exit(n); +} + +/*+++++++++++++++*/ + +/* + sedexec.c -- execute compiled form of stream editor commands + + The single entry point of this module is the function execute(). It + may take a string argument (the name of a file to be used as text) or + the argument NULL which tells it to filter standard input. It executes + the compiled commands in cmds[] on each line in turn. + + The function command() does most of the work. Match() and advance() + are used for matching text against precompiled regular expressions and + dosub() does right-hand-side substitution. Getline() does text input; + readout() and Memcmp() are output and string-comparison utilities. +*/ + +/* #include <stdio.h> */ +/* #include <ctype.h> */ +/* #include "sed.h" */ + +/***** shared variables imported from the main ******/ + +/* Main data areas */ +extern char linebuf[]; /* current-line buffer */ +extern sedcmd cmds[]; /* hold compiled commands */ +extern long linenum[]; /* numeric-addresses table */ + +/* Miscellaneous shared variables */ +extern int nflag; /* -n option flag */ +extern int eargc; /* scratch copy of argument count */ +extern char **eargv; /* scratch copy of argument list */ +extern char bits[]; /* the bits table */ + +/***** end of imported stuff *****/ + +#define MAXHOLD MAXBUF /* size of the hold space */ +#define GENSIZ MAXBUF /* maximum genbuf size */ + +#define TRUE 1 +#define FALSE 0 + +static char LTLMSG[] = "sed: line too long\n"; + +static char *spend; /* current end-of-line-buffer pointer */ +static long lnum = 0L; /* current source line number */ + +/* Append buffer maintenance */ +static sedcmd *appends[MAXAPPENDS]; /* array of ptrs to a,i,c commands */ +static sedcmd **aptr = appends; /* ptr to current append */ + +/* Genbuf and its pointers */ +static char genbuf[GENSIZ]; +static char *loc1; +static char *loc2; +static char *locs; + +/* Command-logic flags */ +static int lastline; /* do-line flag */ +static int jump; /* jump to cmd's link address if set */ +static int delete; /* delete command flag */ + +/* Tagged-pattern tracking */ +static char *bracend[MAXTAGS]; /* tagged pattern start pointers */ +static char *brastart[MAXTAGS]; /* tagged pattern end pointers */ + +static int anysub; /* true if any s on current line succeeded */ + + +void execute() +/* Execute the compiled commands in cmds[] */ +{ + register char *p1; /* dummy copy ptrs */ + register sedcmd *ipc; /* ptr to current command */ + char *execp; /* ptr to source */ + + + initget(); + + /* Here's the main command-execution loop */ + for (;;) { + + /* Get next line to filter */ + if ((execp = getline(linebuf)) == BAD) return; + spend = execp; + anysub = FALSE; + + /* Loop through compiled commands, executing them */ + for (ipc = cmds; ipc->command;) { + if (!selected(ipc)) { + ipc++; + continue; + } + command(ipc); /* execute the command pointed at */ + + if (delete) /* if delete flag is set */ + break; /* don't exec rest of compiled cmds */ + + if (jump) { /* if jump set, follow cmd's link */ + jump = FALSE; + if ((ipc = ipc->u.link) == 0) { + ipc = cmds; + break; + } + } else /* normal goto next command */ + ipc++; + } + + /* We've now done all modification commands on the line */ + + /* Here's where the transformed line is output */ + if (!nflag && !delete) { + for (p1 = linebuf; p1 < spend; p1++) putc(*p1, stdout); + putc('\n', stdout); + } + + /* If we've been set up for append, emit the text from it */ + if (aptr > appends) readout(); + + delete = FALSE; /* clear delete flag; about to get next cmd */ + } +} + +static int selected(ipc) +/* Is current command selected */ +sedcmd *ipc; +{ + register char *p1 = ipc->addr1; /* point p1 at first address */ + register char *p2 = ipc->addr2; /* and p2 at second */ + int c; + int sel = TRUE; /* select by default */ + + if (!p1) /* No addresses: always selected */ + ; + else if (ipc->flags.inrange) { + if (*p2 == CEND); + else if (*p2 == CLNUM) { + c = p2[1] & CMASK; + if (lnum >= linenum[c]) { + ipc->flags.inrange = FALSE; + if (lnum > linenum[c]) sel = FALSE; + } + } else if (match(p2, 0)) + ipc->flags.inrange = FALSE; + } else if (*p1 == CEND) { + if (!lastline) sel = FALSE; + } else if (*p1 == CLNUM) { + c = p1[1] & CMASK; + if (lnum != linenum[c]) + sel = FALSE; + else if (p2) + ipc->flags.inrange = TRUE; + } else if (match(p1, 0)) { + if (p2) ipc->flags.inrange = TRUE; + } else + sel = FALSE; + + return ipc->flags.allbut ? !sel : sel; +} + +static int match(expbuf, gf) /* uses genbuf */ + /* Match RE at expbuf against linebuf; if gf set, copy linebuf from genbuf */ +char *expbuf; +int gf; +{ + register char *p1, *p2, c; + + if (gf) { + if (*expbuf) return(FALSE); + p1 = linebuf; + p2 = genbuf; + while (*p1++ = *p2++); + locs = p1 = loc2; + } else { + p1 = linebuf; + locs = FALSE; + } + + p2 = expbuf; + if (*p2++) { + loc1 = p1; + if (*p2 == CCHR && p2[1] != *p1) /* 1st char is wrong */ + return(FALSE); /* so fail */ + return(advance(p1, p2));/* else try to match rest */ + } + + /* Quick check for 1st character if it's literal */ + if (*p2 == CCHR) { + c = p2[1]; /* pull out character to search for */ + do { + if (*p1 != c) continue; /* scan the source string */ + if (advance(p1, p2)) /* found it, match the rest */ + return(loc1 = p1, 1); + } while + (*p1++); + return(FALSE); /* didn't find that first char */ + } + + /* Else try for unanchored match of the pattern */ + do { + if (advance(p1, p2)) return(loc1 = p1, 1); + } while + (*p1++); + + /* If got here, didn't match either way */ + return(FALSE); +} + +static int advance(lp, ep) +/* Attempt to advance match pointer by one pattern element */ +register char *lp; /* source (linebuf) ptr */ +register char *ep; /* regular expression element ptr */ +{ + register char *curlp; /* save ptr for closures */ + char c; /* scratch character holder */ + char *bbeg; + int ct; + + for (;;) switch (*ep++) { + case CCHR: /* literal character */ + if (*ep++ == *lp++) /* if chars are equal */ + continue; /* matched */ + return(FALSE); /* else return false */ + + case CDOT: /* anything but newline */ + if (*lp++) /* first NUL is at EOL */ + continue; /* keep going if didn't find */ + return(FALSE); /* else return false */ + + case CNL: /* start-of-line */ + case CDOL: /* end-of-line */ + if (*lp == 0) /* found that first NUL? */ + continue; /* yes, keep going */ + return(FALSE); /* else return false */ + + case CEOF: /* end-of-address mark */ + loc2 = lp; /* set second loc */ + return(TRUE); /* return true */ + + case CCL: /* a closure */ + c = *lp++ & 0177; + if (ep[c >> 3] & bits[c & 07]) { /* is char in set? */ + ep += 16; /* then skip rest of bitmask */ + continue; /* and keep going */ + } + return(FALSE); /* else return false */ + + case CBRA: /* start of tagged pattern */ + brastart[*ep++] = lp; /* mark it */ + continue; /* and go */ + + case CKET: /* end of tagged pattern */ + bracend[*ep++] = lp; /* mark it */ + continue; /* and go */ + + case CBACK: + bbeg = brastart[*ep]; + ct = bracend[*ep++] - bbeg; + + if (Memcmp(bbeg, lp, ct)) { + lp += ct; + continue; + } + return(FALSE); + + case CBACK | STAR: + bbeg = brastart[*ep]; + ct = bracend[*ep++] - bbeg; + curlp = lp; + while (Memcmp(bbeg, lp, ct)) lp += ct; + + while (lp >= curlp) { + if (advance(lp, ep)) return(TRUE); + lp -= ct; + } + return(FALSE); + + + case CDOT | STAR: /* match .* */ + curlp = lp; /* save closure start loc */ + while (*lp++); /* match anything */ + goto star; /* now look for followers */ + + case CCHR | STAR: /* match <literal char>* */ + curlp = lp; /* save closure start loc */ + while (*lp++ == *ep); /* match many of that char */ + ep++; /* to start of next element */ + goto star; /* match it and followers */ + + case CCL | STAR: /* match [...]* */ + curlp = lp; /* save closure start loc */ + do { + c = *lp++ & 0x7F; /* match any in set */ + } while + (ep[c >> 3] & bits[c & 07]); + ep += 16; /* skip past the set */ + goto star; /* match followers */ + + star: /* the recursion part of a * or + match */ + if (--lp == curlp) /* 0 matches */ + continue; + + if (*ep == CCHR) { + c = ep[1]; + do { + if (*lp != c) continue; + if (advance(lp, ep)) return (TRUE); + } while + (lp-- > curlp); + return(FALSE); + } + if (*ep == CBACK) { + c = *(brastart[ep[1]]); + do { + if (*lp != c) continue; + if (advance(lp, ep)) return (TRUE); + } while + (lp-- > curlp); + return(FALSE); + } + do { + if (lp == locs) break; + if (advance(lp, ep)) return (TRUE); + } while + (lp-- > curlp); + return(FALSE); + + default: + fprintf(stderr, "sed: RE error, %o\n", *--ep); + quit(2); + } +} + +static int substitute(ipc) +/* Perform s command */ +sedcmd *ipc; /* ptr to s command struct */ +{ + int nullmatch; + + if (match(ipc->u.lhs, 0)) { /* if no match */ + nullmatch = (loc1 == loc2); + dosub(ipc->rhs); /* perform it once */ + } else + return(FALSE); /* command fails */ + + if (ipc->flags.global) /* if global flag enabled */ + while (*loc2) { /* cycle through possibles */ + if (nullmatch) loc2++; + if (match(ipc->u.lhs, 1)) { /* found another */ + nullmatch = (loc1 == loc2); + dosub(ipc->rhs); /* so substitute */ + } else /* otherwise, */ + break; /* we're done */ + } + return(TRUE); /* we succeeded */ +} + +static void dosub(rhsbuf) /* uses linebuf, genbuf, spend */ + /* Generate substituted right-hand side (of s command) */ +char *rhsbuf; /* where to put the result */ +{ + register char *lp, *sp, *rp; + int c; + + /* Copy linebuf to genbuf up to location 1 */ + lp = linebuf; + sp = genbuf; + while (lp < loc1) *sp++ = *lp++; + + for (rp = rhsbuf; c = *rp++;) { + if (c == '&') { + sp = place(sp, loc1, loc2); + continue; + } else if (c & 0200 && (c &= 0177) >= '1' && c < MAXTAGS + '1') { + sp = place(sp, brastart[c - '1'], bracend[c - '1']); + continue; + } + *sp++ = c & 0177; + if (sp >= genbuf + MAXBUF) fprintf(stderr, LTLMSG); + } + lp = loc2; + loc2 = sp - genbuf + linebuf; + while (*sp++ = *lp++) + if (sp >= genbuf + MAXBUF) fprintf(stderr, LTLMSG); + lp = linebuf; + sp = genbuf; + while (*lp++ = *sp++); + spend = lp - 1; +} + +static char *place(asp, al1, al2) /* uses genbuf */ + /* Place chars at *al1...*(al1 - 1) at asp... in genbuf[] */ +register char *asp, *al1, *al2; +{ + while (al1 < al2) { + *asp++ = *al1++; + if (asp >= genbuf + MAXBUF) fprintf(stderr, LTLMSG); + } + return(asp); +} + +static void listto(p1, fp) +/* Write a hex dump expansion of *p1... to fp */ +register char *p1; /* the source */ +FILE *fp; /* output stream to write to */ +{ + p1--; + while (*p1++) + if (isprint(*p1)) + putc(*p1, fp); /* pass it through */ + else { + putc('\\', fp); /* emit a backslash */ + switch (*p1) { + case '\b': + putc('b', fp); + break; /* BS */ + case '\t': + putc('t', fp); + break; /* TAB */ + case '\n': + putc('n', fp); + break; /* NL */ + case '\r': + putc('r', fp); + break; /* CR */ + case '\33': + putc('e', fp); + break; /* ESC */ + default: + fprintf(fp, "%02x", *p1 & 0xFF); + } + } + putc('\n', fp); +} + +static void truncated(h) +int h; +{ + static long last = 0L; + + if (lnum == last) return; + last = lnum; + + fprintf(stderr, "sed: "); + fprintf(stderr, h ? "hold space" : "line %ld", lnum); + fprintf(stderr, " truncated to %d characters\n", MAXBUF); +} + +static void command(ipc) +/* Execute compiled command pointed at by ipc */ +sedcmd *ipc; +{ + static char holdsp[MAXHOLD + 1]; /* the hold space */ + static char *hspend = holdsp; /* hold space end pointer */ + register char *p1, *p2; + char *execp; + int didsub; /* true if last s succeeded */ + + switch (ipc->command) { + case ACMD: /* append */ + *aptr++ = ipc; + if (aptr >= appends + MAXAPPENDS) fprintf(stderr, + "sed: too many appends after line %ld\n", + lnum); + *aptr = 0; + break; + + case CCMD: /* change pattern space */ + delete = TRUE; + if (!ipc->flags.inrange || lastline) printf("%s\n", ipc->u.lhs); + break; + + case DCMD: /* delete pattern space */ + delete++; + break; + + case CDCMD: /* delete a line in hold space */ + p1 = p2 = linebuf; + while (*p1 != '\n') + if (delete = (*p1++ == 0)) return; + p1++; + while (*p2++ = *p1++) continue; + spend = p2 - 1; + jump++; + break; + + case EQCMD: /* show current line number */ + fprintf(stdout, "%ld\n", lnum); + break; + + case GCMD: /* copy hold space to pattern space */ + p1 = linebuf; + p2 = holdsp; + while (*p1++ = *p2++); + spend = p1 - 1; + break; + + case CGCMD: /* append hold space to pattern space */ + *spend++ = '\n'; + p1 = spend; + p2 = holdsp; + do + if (p1 > linebuf + MAXBUF) { + truncated(0); + p1[-1] = 0; + break; + } + while (*p1++ = *p2++); + + spend = p1 - 1; + break; + + case HCMD: /* copy pattern space to hold space */ + p1 = holdsp; + p2 = linebuf; + while (*p1++ = *p2++); + hspend = p1 - 1; + break; + + case CHCMD: /* append pattern space to hold space */ + *hspend++ = '\n'; + p1 = hspend; + p2 = linebuf; + do + if (p1 > holdsp + MAXBUF) { + truncated(1); + p1[-1] = 0; + break; + } + while (*p1++ = *p2++); + + hspend = p1 - 1; + break; + + case ICMD: /* insert text */ + printf("%s\n", ipc->u.lhs); + break; + + case BCMD: /* branch to label */ + jump = TRUE; + break; + + case LCMD: /* list text */ + listto(linebuf, (ipc->fout != NULL) ? ipc->fout : stdout); + break; + + case NCMD: /* read next line into pattern space */ + if (!nflag) puts(linebuf); /* flush out the current line */ + if (aptr > appends) readout(); /* do pending a, r commands */ + if ((execp = getline(linebuf)) == BAD) { + delete = TRUE; + break; + } + spend = execp; + anysub = FALSE; + break; + + case CNCMD: /* append next line to pattern space */ + if (aptr > appends) readout(); + *spend++ = '\n'; + if ((execp = getline(spend)) == BAD) { + *--spend = 0; + break; + } + spend = execp; + anysub = FALSE; + break; + + case PCMD: /* print pattern space */ + puts(linebuf); + break; + + case CPCMD: /* print one line from pattern space */ +cpcom: /* so s command can jump here */ + for (p1 = linebuf; *p1 != '\n' && *p1 != '\0';) putc(*p1++, stdout); + putc('\n', stdout); + break; + + case QCMD: /* quit the stream editor */ + if (!nflag) puts(linebuf); /* flush out the current line */ + if (aptr > appends) + readout(); /* do any pending a and r commands */ + quit(0); + + case RCMD: /* read a file into the stream */ + *aptr++ = ipc; + if (aptr >= appends + MAXAPPENDS) fprintf(stderr, + "sed: too many reads after line %ld\n", + lnum); + *aptr = 0; + break; + + case SCMD: /* substitute RE */ + didsub = substitute(ipc); + if (didsub) anysub = TRUE; + if (ipc->flags.print && didsub) + if (ipc->flags.print == TRUE) + puts(linebuf); + else + goto cpcom; + if (didsub && ipc->fout) fprintf(ipc->fout, "%s\n", linebuf); + break; + + case TCMD: /* branch on any s successful */ + case CTCMD: /* branch on any s failed */ + if (anysub == (ipc->command == CTCMD)) + break; /* no branch if any s failed, else */ + anysub = FALSE; + jump = TRUE; /* set up to jump to assoc'd label */ + break; + + case CWCMD: /* write one line from pattern space */ + for (p1 = linebuf; *p1 != '\n' && *p1 != '\0';) + putc(*p1++, ipc->fout); + putc('\n', ipc->fout); + break; + + case WCMD: /* write pattern space to file */ + fprintf(ipc->fout, "%s\n", linebuf); + break; + + case XCMD: /* exchange pattern and hold spaces */ + p1 = linebuf; + p2 = genbuf; + while (*p2++ = *p1++) continue; + p1 = holdsp; + p2 = linebuf; + while (*p2++ = *p1++) continue; + spend = p2 - 1; + p1 = genbuf; + p2 = holdsp; + while (*p2++ = *p1++) continue; + hspend = p2 - 1; + break; + + case YCMD: + p1 = linebuf; + p2 = ipc->u.lhs; + while (*p1 = p2[*p1]) p1++; + break; + } +} + +static void openfile(file) +char *file; +/* Replace stdin by given file */ +{ + if (freopen(file, "r", stdin) == NULL) { + fprintf(stderr, "sed: can't open %s\n", file); + quit(1); + } +} + +static int c; /* Will be the next char to read, a kind of + * lookahead */ + +static void get() +/* Read next character into c treating all argument files as run through cat */ +{ + while ((c = getchar()) == EOF && --eargc >= 0) openfile(*eargv++); +} + +static void initget() +/* Initialise character input */ +{ + if (--eargc >= 0) openfile(*eargv++); /* else input == stdin */ + get(); +} + +static char *getline(buf) +/* Get next line of text to be edited, return pointer to end */ +register char *buf; /* where to send the input */ +{ + if (c == EOF) return BAD; + + lnum++; /* we can read a new line */ + + do { + if (c == '\n') { + get(); + break; + } + if (buf <= linebuf + MAXBUF) *buf++ = c; + get(); + } while (c != EOF); + + if (c == EOF) lastline = TRUE; + + if (buf > linebuf + MAXBUF) { + truncated(0); + --buf; + } + *buf = 0; + return buf; +} + +static int Memcmp(a, b, count) +/* Return TRUE if *a... == *b... for count chars, FALSE otherwise */ +register char *a, *b; +int count; +{ + while (count--) /* look at count characters */ + if (*a++ != *b++) /* if any are nonequal */ + return(FALSE); /* return FALSE for false */ + return(TRUE); /* compare succeeded */ +} + +static void readout() +/* Write file indicated by r command to output */ +{ + register int t; /* hold input char or EOF */ + FILE *fi; /* ptr to file to be read */ + + aptr = appends - 1; /* arrange for pre-increment to work right */ + while (*++aptr) + if ((*aptr)->command == ACMD) /* process "a" cmd */ + printf("%s\n", (*aptr)->u.lhs); + else { /* process "r" cmd */ + if ((fi = fopen((*aptr)->u.lhs, "r")) == NULL) { + fprintf(stderr, "sed: can't open %s\n", + (*aptr)->u.lhs); + continue; + } + while ((t = getc(fi)) != EOF) putc((char) t, stdout); + fclose(fi); + } + aptr = appends; /* reset the append ptr */ + *aptr = 0; +} + +/* Sedexec.c ends here */ diff --git a/commands/simple/shar.c b/commands/simple/shar.c new file mode 100755 index 000000000..c5096bc3d --- /dev/null +++ b/commands/simple/shar.c @@ -0,0 +1,71 @@ +/* shar - make a shell archive Author: Michiel Husijes */ + +#include <stdlib.h> /* for the nonstd :-( _PROTOTYPE */ +#include <stdio.h> + +static _PROTOTYPE( void error, (char *progname, char *operation, + char *filename) ); +_PROTOTYPE( int main, (int argc, char **argv) ); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int argn; + register int ch; + register FILE *fp; + int exitstatus; + char *filename; + + exitstatus = 0; + for (argn = 1; argn < argc; argn++) { + filename = argv[argn]; + if ((fp = fopen(filename, "r")) == NULL) { + error(argv[0], "opening ", filename); + exitstatus = 1; + } else { + fputs("echo x - ", stdout); + fputs(filename, stdout); + fputs("\nsed '/^X/s///' > ", stdout); + fputs(filename, stdout); + fputs(" << '/'\n", stdout); + while ((ch = getc(fp)) != EOF) { + putchar('X'); + putchar(ch); + while (ch != '\n') { + ch = getc(fp); + if (ch == EOF) break; + putchar(ch); + } + if (ch == EOF) break; + } + fputs("/\n", stdout); + if (ferror(fp)) { + error(argv[0], "reading ", filename); + exitstatus = 1; + } + if (fclose(fp) != 0) { + error(argv[0], "closing ", filename); + exitstatus = 1; + } + if (ferror(stdout)) break; /* lost already */ + } + } + fflush(stdout); + if (ferror(stdout)) { + error(argv[0], "writing ", "stdout"); + exitstatus = 1; + } + return(exitstatus); +} + +static void error(progname, operation, filename) +char *progname; +char *operation; +char *filename; +{ + fputs(progname, stderr); + fputs(": error ", stderr); + fputs(operation, stderr); + perror(filename); +} diff --git a/commands/simple/size.c b/commands/simple/size.c new file mode 100755 index 000000000..1a9fe7942 --- /dev/null +++ b/commands/simple/size.c @@ -0,0 +1,64 @@ +/* size - tell size of an object file Author: Andy Tanenbaum */ + +#include <sys/types.h> +#include <fcntl.h> +#include <a.out.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +int heading; /* set when heading printed */ +int error; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void size, (char *name)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int i; + + if (argc == 1) { + size("a.out"); + exit(error); + } + for (i = 1; i < argc; i++) size(argv[i]); + return(error); +} + + + +void size(name) +char *name; +{ + int fd, separate; + long dynam, allmem; + struct exec exec; + + if ((fd = open(name, O_RDONLY)) < 0) { + fprintf(stderr, "size: can't open %s\n", name); + error = 1; + return; + } + if (read(fd, (char *)&exec, sizeof(struct exec)) != sizeof(struct exec)) { + fprintf(stderr, "size: %s: header too short\n", name); + error = 1; + close(fd); + return; + } + if (BADMAG(exec)) { + fprintf(stderr, "size: %s not an object file\n", name); + error = 1; + close(fd); + return; + } + separate = (exec.a_flags & A_SEP ? 1 : 0); + dynam = exec.a_total - exec.a_text - exec.a_data - exec.a_bss; + if (separate) dynam += exec.a_text; + allmem = (separate ? exec.a_total + exec.a_text : exec.a_total); + if (heading++ == 0) printf(" text data bss stack memory\n"); + printf("%7ld %7ld %7ld %8ld %8ld %s\n", + exec.a_text, exec.a_data, exec.a_bss, dynam, allmem, name); + close(fd); +} diff --git a/commands/simple/sleep.c b/commands/simple/sleep.c new file mode 100755 index 000000000..c1e0d121f --- /dev/null +++ b/commands/simple/sleep.c @@ -0,0 +1,34 @@ +/* sleep - suspend a process for x sec Author: Andy Tanenbaum */ + +#include <sys/types.h> +#include <stdlib.h> +#include <unistd.h> +#include <minix/minlib.h> + +_PROTOTYPE(int main, (int argc, char **argv)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + register seconds; + register char c; + + seconds = 0; + + if (argc != 2) { + std_err("Usage: sleep time\n"); + exit(1); + } + while (c = *(argv[1])++) { + if (c < '0' || c > '9') { + std_err("sleep: bad arg\n"); + exit(1); + } + seconds = 10 * seconds + (c - '0'); + } + + /* Now sleep. */ + sleep(seconds); + return(0); +} diff --git a/commands/simple/slip.c b/commands/simple/slip.c new file mode 100755 index 000000000..efc44bc91 --- /dev/null +++ b/commands/simple/slip.c @@ -0,0 +1,323 @@ +/* slip 1.1 - Serial line IP Author: Kees J. Bot + * 19 Jul 1997 + */ +#define nil 0 +#include <sys/types.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <sys/asynchio.h> + +#if __minix && !__minix_vmd +#define HAS_ASYN 0 /* Standard Minix doesn't have async I/O. */ +#else +#define HAS_ASYN 1 /* Everyone else does in some way. */ +#endif + +#if !HAS_ASYN +#include <signal.h> +#endif + +#define END 0300 /* End of packet. */ +#define ESC 0333 /* Byte stuffing escape. */ +#define ESC_END 0334 /* END -> ESC ESC_END -> END. */ +#define ESC_ESC 0335 /* ESC -> ESC ESC_ESC -> ESC. */ + +#define PACKLEN 2048 /* Max datagram size. */ +#define SLIPLEN (1 + 2*PACKLEN + 1) /* Max serial size when all escaped. */ + +/* Pathetic fprintf() clone to avoid dragging in the stdio library. */ +static int fprintf(int fd, const char *format, ...); +#define stderr 2 + +int main(int argc, char **argv) +{ + char *ps_device; + int ps_fd; + int doing[2], discard; + ssize_t r; +#if !HAS_ASYN + pid_t other_pid; +#endif + size_t ps_len[2], sl_len[2]; + unsigned char *sl_end; + unsigned char ps_buf[2][PACKLEN]; + unsigned char sl_buf[2][SLIPLEN]; + asynchio_t asyn; + + if (argc != 2) { + fprintf(stderr, "Usage: slip psip-device\n"); + exit(1); + } + ps_device= argv[1]; + + if ((ps_fd= open(ps_device, O_RDWR)) < 0) { + fprintf(stderr, "slip: can't open %s: %s\n", + ps_device, strerror(errno)); + exit(1); + } + + doing[0]= 1; /* We're doing serial -> psip. */ + discard= 0; /* No input error. */ + sl_len[0]= 0; /* Nothing read from serial line yet. */ + sl_end= nil; /* No END marker seen. */ + ps_len[0]= 0; /* Nothing to write to pseudo IP device. */ + + doing[1]= 1; /* We're doing psip -> serial. */ + sl_len[1]= 0; /* Nothing read from pseudo IP device yet. */ + ps_len[1]= 0; /* Nothing to write to serial line. */ + +#if !HAS_ASYN + /* Oops, standard Minix can't do asynchronous I/O. Fork and let the parent + * do serial -> psip, and the child do psip -> serial. (Note that we have + * to make sure that we're not reading and writing at the same time even + * for standard Minix. For Minix-vmd we do fill an input buffer while an + * output buffer is waiting to be drained to improve performance a bit.) + */ + switch ((other_pid= fork())) { + case -1: + fprintf(stderr, "slip: can't fork: %s\n", strerror(errno)); + exit(1); + case 0: + /* Child. */ + doing[0]= 0; /* *Not* doing serial -> psip. */ + other_pid= getppid(); + break; + default: + /* Parent. */ + doing[1]= 0; /* *Not* doing psip -> serial. */ + } +#endif + + asyn_init(&asyn); + + for (;;) { + if (doing[0]) { + /* If there is an END marker in the serial input then create + * an IP packet to be send to the TCP/IP task. + */ + while (sl_end != nil && ps_len[0] == 0) { + unsigned char *sp= sl_buf[0]; + unsigned char *pp= ps_buf[0]; + + while (sp < sl_end) { + int c= *sp++; + + if (c == ESC) { + switch (*sp++) { + case ESC_ESC: /* ESC ESC_ESC -> ESC. */ + c= ESC; + break; + case ESC_END: /* ESC ESC_END -> END. */ + c= END; + break; + default: + /* Protocol error. */ + discard= 1; + } + } + if (pp < ps_buf[0] + PACKLEN) { + *pp++ = c; + } else { + /* Packet too big, discard. */ + discard= 1; + } + } + if (discard) { + discard= 0; + } else { + /* A new packet can be send to the TCP/IP server. */ + ps_len[0]= (pp - ps_buf[0]); + } + /* Move what's beyond END to the front. */ + sl_end++; + sl_len[0] -= (sl_end - sl_buf[0]); + memmove(sl_buf[0], sl_end, sl_len[0]); + sl_end= memchr(sl_buf[0], END, sl_len[0]); + } + + /* Reading from serial input. */ + if (sl_end == nil && (HAS_ASYN || ps_len[0] == 0)) { + r= asyn_read(&asyn, 0, sl_buf[0] + sl_len[0], + SLIPLEN - sl_len[0]); + if (r > 0) { + sl_end= memchr(sl_buf[0] + sl_len[0], END, r); + sl_len[0]+= r; + if (sl_end == nil && sl_len[0] == SLIPLEN) { + /* Packet is idiotically big and no END in sight. */ + sl_len[0]= 0; + discard= 1; + } + } else + if (r == 0) { + fprintf(stderr, "slip: EOF on serial input\n"); + break; + } else + if (errno != ASYN_INPROGRESS) { + fprintf(stderr, "slip: serial input error: %s\n", + strerror(errno)); + break; + } + } + + /* Writing to the psip device. */ + if (ps_len[0] > 0) { + r= asyn_write(&asyn, ps_fd, ps_buf[0], ps_len[0]); + if (r == ps_len[0]) { + /* Packet written. */ + ps_len[0]= 0; + } else + if (r >= 0) { + fprintf(stderr, + "slip: odd write to %s, tried %u, wrote %d\n", + ps_device, (unsigned) ps_len[0], (int) r); + break; + } else + if (errno != ASYN_INPROGRESS) { + fprintf(stderr, "slip: error writing %s: %s\n", + ps_device, strerror(errno)); + break; + } + } + } + + if (doing[1]) { + /* Transform an IP packet to a "byte stuffed" serial packet. */ + if (ps_len[1] > 0 && sl_len[1] == 0) { + unsigned char *pp= ps_buf[1]; + unsigned char *sp= sl_buf[1]; + + *sp++ = END; + while (ps_len[1] > 0) { + int c= *pp++; + ps_len[1]--; + switch (c) { + case ESC: /* ESC -> ESC ESC_ESC. */ + *sp++ = ESC; + c= ESC_ESC; + break; + case END: /* END -> ESC ESC_END. */ + *sp++ = ESC; + c= ESC_END; + break; + } + *sp++ = c; + } + *sp++ = END; + sl_len[1]= (sp - sl_buf[1]); + } + + /* Reading from the psip device. */ + if (ps_len[1] == 0 && (HAS_ASYN || sl_len[1] == 0)) { + r= asyn_read(&asyn, ps_fd, ps_buf[1], PACKLEN); + if (r > 0) { + /* One packet read. */ + ps_len[1]= r; + } else + if (r == 0) { + fprintf(stderr, "slip: EOF on %s\n", ps_device); + break; + } else + if (errno != ASYN_INPROGRESS) { + fprintf(stderr, "slip: error reading %s: %s\n", + ps_device, strerror(errno)); + break; + } + } + + /* Writing to serial output. */ + if (sl_len[1] > 0) { + r= asyn_write(&asyn, 1, sl_buf[1], sl_len[1]); + if (r > 0) { + if ((sl_len[1]-= r) > 0) { + memmove(sl_buf[1], sl_buf[1] + r, sl_len[1]); + } + } else + if (r == 0) { + fprintf(stderr, "slip: EOF on serial output\n"); + break; + } else + if (errno != ASYN_INPROGRESS) { + fprintf(stderr, "slip: serial output error: %s\n", + strerror(errno)); + break; + } + } + } + + /* Wait for something to happen. */ + if (asyn_wait(&asyn, 0, nil) < 0) { + fprintf(stderr, + "slip: error while waiting for I/O to happen: %s\n", + strerror(errno)); + break; + } + } +#if !HAS_ASYN + /* Tell my alter ego that the game is over. */ + kill(other_pid, SIGKILL); +#endif + return 1; +} + +static int fprintf(int fd, const char *format, ...) +/* Simple fprintf() to save a few bytes by not using the stdio library. */ +{ + int len; + ssize_t r; + const char *fp0, *fp; + va_list ap; + + len= 0; + fp= fp0= format; + va_start(ap, format); + + while (*fp != 0) { + if (*fp == '%' && memchr("sdu", fp[1], 3) != nil) { + if (fp > fp0) { + if ((r= write(fd, fp0, (fp - fp0))) < 0) return -1; + len+= r; + } + fp++; + fp0= fp+1; + + if (*fp == 's') { + char *s= va_arg(ap, char *); + + if ((r= write(fd, s, strlen(s))) < 0) return -1; + len+= r; + } else { + int d; + unsigned u; + char a[3 * sizeof(u) + 2]; + char *p; + + if (*fp == 'd') { + u= d= va_arg(ap, int); + if (d < 0) u= -u; + } else { + u= va_arg(ap, unsigned); + d= 0; + } + + p= a + sizeof(a); + *--p= 0; + do *--p= '0' + (u % 10); while ((u /= 10) > 0); + + if (d < 0) *--p= '-'; + if ((r= write(fd, p, (a + sizeof(a)) - p)) < 0) return -1; + len+= r; + } + } + fp++; + } + if (fp > fp0) { + if ((r= write(fd, fp0, (fp - fp0))) < 0) return -1; + len+= r; + } + va_end(ap); + return len; +} diff --git a/commands/simple/sort.c b/commands/simple/sort.c new file mode 100755 index 000000000..09a7d348c --- /dev/null +++ b/commands/simple/sort.c @@ -0,0 +1,1193 @@ +/* sort - sort a file of lines Author: Michiel Huisjes */ + +/* SYNOPSIS: + * sort [-funbirdcmt'x'] [+beg_pos[opts] [-end_pos]] [-o outfile] [file].. + * + * [opts] can be any of + * -f : Fold upper case to lower. + * -n : Sort to numeric value (optional decimal point) implies -b + * -b : Skip leading blanks + * -i : Ignore chars outside ASCII range (040 - 0176) + * -r : Reverse the sense of comparisons. + * -d : Sort to dictionary order. Only letters, digits, comma's and points + * are compared. + * If any of these flags are used in [opts], then they override all global + * ordering for this field. + * + * I/O control flags are: + * -u : Print uniq lines only once. + * -c : Check if files are sorted in order. + * -m : Merge already sorted files. + * -o outfile : Name of output file. (Can be one of the input files). + * Default is stdout. + * - : Take stdin as input. + * + * Fields: + * -t'x' : Field separating character is 'x' + * +a.b : Start comparing at field 'a' with offset 'b'. A missing 'b' is + * taken to be 0. + * -a.b : Stop comparing at field 'a' with offset 'b'. A missing 'b' is + * taken to be 0. + * A missing -a.b means the rest of the line. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <limits.h> + +#define OPEN_FILES (OPEN_MAX-4) /* Nr of open files per process */ +#if __minix_vmd +#define MEMORY_SIZE (1024 * 1024) +#else +#define MEMORY_SIZE ((10 * sizeof(int)) * 1024) +#endif + /* Total mem_size */ +#define LINE_SIZE (1024 >> 1) /* Max length of a line */ +#define IO_SIZE (2 * 1024) /* Size of buffered output */ +#define STD_OUT 1 /* Fd of terminal */ + +/* Return status of functions */ +#define OK 0 +#define ERROR -1 +#define NIL_PTR ((char *) 0) + +/* Compare return values */ +#define LOWER -1 +#define SAME 0 +#define HIGHER 1 + +/* Table definitions. */ +#define DICT 0x001 /* Alpha, numeric, letters and . */ +#define ASCII 0x002 /* All between ' ' and '~' */ +#define BLANK 0x004 /* ' ' and '\t' */ +#define DIGIT 0x008 /* 0-9 */ +#define UPPER 0x010 /* A-Z */ + +typedef int BOOL; + +#define FALSE 0 +#define TRUE 1 + +typedef struct { + int fd; /* Fd of file */ + char *buffer; /* Buffer for reads */ + int read_chars; /* Nr of chars actually read in buffer */ + int cnt; /* Nr of chars taken out of buffer */ + char *line; /* Contains line currently used */ +} MERGE; + +#define NIL_MERGE ((MERGE *) 0) +MERGE merge_f[OPEN_FILES]; /* Merge structs */ +int buf_size; /* Size of core available for each struct */ + +#define FIELDS_LIMIT 10 /* 1 global + 9 user */ +#define GLOBAL 0 + +typedef struct { + int beg_field, beg_pos; /* Begin field + offset */ + int end_field, end_pos; /* End field + offset. ERROR == EOLN */ + BOOL reverse; /* TRUE if rev. flag set on this field */ + BOOL blanks; + BOOL dictionary; + BOOL fold_case; + BOOL ascii; + BOOL numeric; +} FIELD; + +/* Field declarations. A total of FILEDS_LIMIT is allowed */ +FIELD fields[FIELDS_LIMIT]; +int field_cnt; /* Nr of field actually assigned */ + +/* Various output control flags */ +BOOL check = FALSE; +BOOL only_merge = FALSE; +BOOL uniq = FALSE; + +char *mem_top; /* Mem_top points to lowest pos of memory. */ +char *cur_pos; /* First free position in mem */ +char **line_table; /* Pointer to the internal line table */ +BOOL in_core = TRUE; /* Set if input cannot all be sorted in core */ + + /* Place where temp_files should be made */ +char temp_files[] = "/tmp/sort.XXXXX.XX"; +char *output_file; /* Name of output file */ +int out_fd; /* Fd to output file (could be STD_OUT) */ +char out_buffer[IO_SIZE]; /* For buffered output */ + +char **argptr; /* Pointer to argv structure */ +int args_offset; /* Nr of args spilled on options */ +int args_limit; /* Nr of args given */ + +char separator; /* Char that separates fields */ +int nr_of_files = 0; /* Nr_of_files to be merged */ +int disabled; /* Nr of files done */ + +char USAGE[] = "Usage: sort [-funbirdcmt'x'] [+beg_pos [-end_pos]] [-o outfile] [file] .."; + +/* Forward declarations */ +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void get_opts, (char *ptr, FIELD * field)); +_PROTOTYPE(void new_field, (FIELD * field, int *offset, BOOL beg_fl)); +_PROTOTYPE(void adjust_options, (FIELD * field)); +_PROTOTYPE(void error, (BOOL quit, char *message, char *arg)); +_PROTOTYPE(void open_outfile, (void)); +_PROTOTYPE(void get_file, (int fd, off_t size)); +_PROTOTYPE(int last_line, (void)); +_PROTOTYPE(void print_table, (int fd)); +_PROTOTYPE(char *file_name, (int nr)); +_PROTOTYPE(void mread, (int fd, char *address, int bytes)); +_PROTOTYPE(void mwrite, (int fd, char *address, int bytes)); +_PROTOTYPE(void sort, (void)); +_PROTOTYPE(void sort_table, (int nel)); +_PROTOTYPE(void incr, (int si, int ei)); +_PROTOTYPE(int cmp_fields, (char *el1, char *el2)); +_PROTOTYPE(void build_field, (char *dest, FIELD * field, char *src)); +_PROTOTYPE(char *skip_fields, (char *str, int nf)); +_PROTOTYPE(int compare, (char *el1, char *el2)); +_PROTOTYPE(int cmp, (unsigned char *el1, unsigned char *el2, FIELD * field)); +_PROTOTYPE(int digits, (char *str1, char *str2, BOOL check_sign)); +_PROTOTYPE(void files_merge, (int file_cnt)); +_PROTOTYPE(void merge, (int start_file, int limit_file)); +_PROTOTYPE(void put_line, (char *line)); +_PROTOTYPE(MERGE * print, (MERGE * merg, int file_cnt)); +_PROTOTYPE(int read_line, (MERGE * merg)); +_PROTOTYPE(MERGE * skip_lines, (MERGE * smallest, int file_cnt)); +_PROTOTYPE(void uniq_lines, (MERGE * merg)); +_PROTOTYPE(void check_file, (int fd, char *file)); +_PROTOTYPE(int length, (char *line)); +_PROTOTYPE(void copy, (char *dest, char *src)); +_PROTOTYPE(char *msbrk, (int size)); +_PROTOTYPE(void mbrk, (char *address)); +_PROTOTYPE(void catch, (int dummy)); + +/* Table of all chars. 0 means no special meaning. */ +char table[256] = { +/* '^@' to space */ + 0, 0, 0, 0, 0, 0, 0, 0, + 0, BLANK | DICT, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + +/* Space to '0' */ + BLANK | DICT | ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, + ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, + ASCII, ASCII, + +/* '0' until '9' */ + DIGIT | DICT | ASCII, DIGIT | DICT | ASCII, DIGIT | DICT | ASCII, + DIGIT | DICT | ASCII, DIGIT | DICT | ASCII, DIGIT | DICT | ASCII, + DIGIT | DICT | ASCII, DIGIT | DICT | ASCII, DIGIT | DICT | ASCII, + DIGIT | DICT | ASCII, + +/* ASCII from ':' to '@' */ + ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, + +/* Upper case letters 'A' to 'Z' */ + UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII, + UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII, + UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII, + UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII, + UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII, + UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII, + UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII, + UPPER | DICT | ASCII, UPPER | DICT | ASCII, UPPER | DICT | ASCII, + UPPER | DICT | ASCII, UPPER | DICT | ASCII, + +/* ASCII from '[' to '`' */ + ASCII, ASCII, ASCII, ASCII, ASCII, ASCII, + +/* Lower case letters from 'a' to 'z' */ + DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII, + DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII, + DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII, + DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII, + DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII, + DICT | ASCII, DICT | ASCII, DICT | ASCII, DICT | ASCII, + DICT | ASCII, DICT | ASCII, + +/* ASCII from '{' to '~' */ + ASCII, ASCII, ASCII, ASCII, + +/* Stuff from -1 to -177 */ + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 +}; + + +/* + * Get_opts () assigns the options into the field structure as described in ptr. + * This field structure could be the GLOBAL one. + */ +void get_opts(ptr, field) +register char *ptr; +register FIELD *field; +{ + switch (*ptr) { + case 'b': /* Skip leading blanks */ + field->blanks = TRUE; + break; + case 'd': /* Dictionary order */ + field->dictionary = TRUE; + break; + case 'f': /* Fold upper case to lower */ + field->fold_case = TRUE; + break; + case 'i': /* Skip chars outside ' ' '~' */ + field->ascii = TRUE; + break; + case 'n': /* Sort on numeric */ + field->numeric = TRUE; + field->blanks = TRUE; + break; + case 'r': /* Reverse comparisons */ + field->reverse = TRUE; + break; + default: /* Illegal options */ + error(TRUE, USAGE, NIL_PTR); + } +} + +/* New_field () assigns a new field as described by the arguments. + * A field description is of the form: +a.b[opts] -c.d, where b and d, as well + * as -c.d and [opts] are optional. Nr before digit is field nr. Nr after digit + * is offset from field. + */ +void new_field(field, offset, beg_fl) +register FIELD *field; /* Field to assign */ +int *offset; /* Offset in argv structure */ +BOOL beg_fl; /* Assign beg or end of field */ +{ + register char *ptr; + + ptr = argptr[*offset]; + *offset += 1; /* Incr offset to next arg */ + ptr++; + + if (beg_fl) + field->beg_field = atoi(ptr); /* Assign int of first field */ + else + field->end_field = atoi(ptr); + + while (table[*ptr] & DIGIT) /* Skip all digits */ + ptr++; + + if (*ptr == '.') { /* Check for offset */ + ptr++; + if (beg_fl) + field->beg_pos = atoi(ptr); + else + field->end_pos = atoi(ptr); + while (table[*ptr] & DIGIT) /* Skip digits */ + ptr++; + } + if (beg_fl) { + while (*ptr != '\0') /* Check options after field */ + get_opts(ptr++, field); + } + if (beg_fl) { /* Check for end pos */ + ptr = argptr[*offset]; + if (ptr && *ptr == '-' && ((table[*(ptr + 1)] & DIGIT) || *(ptr + 1) == '.')) { + new_field(field, offset, FALSE); + if (field->beg_field > field->end_field) + error(TRUE, "End field is before start field!", NIL_PTR); + } else /* No end pos. */ + field->end_field = ERROR; + } +} + +int main(argc, argv) +int argc; +char *argv[]; +{ + int arg_count = 1; /* Offset in argv */ + struct stat st; + register char *ptr; /* Ptr to *argv in use */ + register int fd; + int pid, pow; + + argptr = argv; + cur_pos = mem_top = msbrk(MEMORY_SIZE); /* Find lowest mem. location */ + + while (arg_count < argc && ((ptr = argv[arg_count])[0] == '-' || *ptr == '+')) { + if (*ptr == '-' && *(ptr + 1) == '\0') /* "-" means stdin */ + break; + if (*ptr == '+') { /* Assign field. */ + if (++field_cnt == FIELDS_LIMIT) + error(TRUE, "Too many fields", NIL_PTR); + new_field(&fields[field_cnt], &arg_count, TRUE); + } else { /* Get output options */ + while (*++ptr) { + switch (*ptr) { + case 'c': /* Only check file */ + check = TRUE; + break; + case 'm': /* Merge (sorted) files */ + only_merge = TRUE; + break; + case 'u': /* Only give uniq lines */ + uniq = TRUE; + break; + case 'o': /* Name of output file */ + output_file = argv[++arg_count]; + break; + case 't': /* Field separator */ + ptr++; + separator = *ptr; + break; + default: /* Sort options */ + get_opts(ptr, &fields[GLOBAL]); + } + } + arg_count++; + } + } + + for (fd = 1; fd <= field_cnt; fd++) adjust_options(&fields[fd]); + +/* Create name of tem_files 'sort.pid.aa' */ + ptr = &temp_files[10]; + pid = getpid(); + pow = 10000; + while (pow != 0) { + *ptr++ = pid / pow + '0'; + pid %= pow; + pow /= 10; + } + + signal(SIGINT, catch); + +/* Only merge files. Set up */ + if (only_merge) { + args_limit = args_offset = arg_count; + while (argv[args_limit] != NIL_PTR) + args_limit++; /* Find nr of args */ + files_merge(args_limit - arg_count); + exit(0); + } + if (arg_count == argc) { /* No args left. Use stdin */ + if (check) + check_file(0, NIL_PTR); + else + get_file(0, (off_t) 0); + } else + while (arg_count < argc) { /* Sort or check args */ + if (strcmp(argv[arg_count], "-") == 0) + fd = 0; + else if (stat(argv[arg_count], &st) < 0) { + error(FALSE, "Cannot find ", argv[arg_count++]); + continue; + } + + /* Open files */ + else if ((fd = open(argv[arg_count], O_RDONLY)) < 0) { + error(FALSE, "Cannot open ", argv[arg_count++]); + continue; + } + if (check) + check_file(fd, argv[arg_count]); + else /* Get_file reads whole file */ + get_file(fd, st.st_size); + arg_count++; + } + + if (check) exit(0); + + sort(); /* Sort whatever is left */ + + if (nr_of_files == 1) /* Only one file sorted -> don't merge */ + exit(0); + + files_merge(nr_of_files); + return(0); +} + +/* Adjust_options() assigns all global variables set also in the fields + * assigned. + */ +void adjust_options(field) +register FIELD *field; +{ + register FIELD *gfield = &fields[GLOBAL]; + + if (gfield->reverse) field->reverse = TRUE; + if (gfield->blanks) field->blanks = TRUE; + if (gfield->dictionary) field->dictionary = TRUE; + if (gfield->fold_case) field->fold_case = TRUE; + if (gfield->ascii) field->ascii = TRUE; + if (gfield->numeric) field->numeric = TRUE; +} + +/* Error () prints the error message on stderr and exits if quit == TRUE. */ +void error(quit, message, arg) +register BOOL quit; +register char *message, *arg; +{ + write(2, message, strlen(message)); + if (arg != NIL_PTR) write(2, arg, strlen(arg)); + perror(" "); + if (quit) exit(1); +} + +/* Open_outfile () assigns to out_fd the fd where the output must go when all + * the sorting is done. + */ +void open_outfile() +{ + if (output_file == NIL_PTR) + out_fd = STD_OUT; + else if ((out_fd = creat(output_file, 0644)) < 0) + error(TRUE, "Cannot creat ", output_file); +} + +/* Get_file reads the whole file of filedescriptor fd. If the file is too big + * to keep in core, a partial sort is done, and the output is stashed somewhere. + */ +void get_file(fd, size) +int fd; /* Fd of file to read */ +register off_t size; /* Size of file */ +{ + register int i; + int rest; /* Rest in memory */ + char save_ch; /* Used in stdin readings */ + + rest = MEMORY_SIZE - (cur_pos - mem_top); + if (fd == 0) { /* We're reding stdin */ + while ((i = read(0, cur_pos, rest)) > 0) { + if ((cur_pos - mem_top) + i == MEMORY_SIZE) { + in_core = FALSE; + i = last_line(); /* End of last line */ + save_ch = mem_top[i]; + mem_top[i] = '\0'; + sort(); /* Sort core */ + mem_top[i] = save_ch; /* Restore erased char */ + /* Restore last (half read) line */ + for (rest = 0; i + rest != MEMORY_SIZE; rest++) + mem_top[rest] = mem_top[i + rest]; + /* Assign current pos. in memory */ + cur_pos = &mem_top[rest]; + } else { /* Fits, just assign position in mem. */ + cur_pos = cur_pos + i; + *cur_pos = '\0'; + } + + /* Calculate rest of mem */ + rest = MEMORY_SIZE - (cur_pos - mem_top); + } + } + + /* Reading file. Check size */ + else if (size > rest) { /* Won't fit */ + mread(fd, cur_pos, rest); + in_core = FALSE; + i = last_line(); /* Get pos. of last line */ + mem_top[i] = '\0'; /* Truncate */ + (void) lseek(fd, (off_t) (i - MEMORY_SIZE), SEEK_CUR); /* Do this next time */ + size = size - rest - i + MEMORY_SIZE; /* Calculate rest */ + cur_pos = mem_top; /* Reset mem */ + sort(); /* Sort core */ + get_file(fd, size); /* Get rest of file */ + } else { /* Fits. Just read in */ + rest = size; + mread(fd, cur_pos, rest); + cur_pos = cur_pos + rest; /* Reassign cur_pos */ + *cur_pos = '\0'; + (void) close(fd); /* File completed */ + } +} + +/* Last_line () find the last line in core and retuns the offset from the top + * of the memory. + */ +int last_line() +{ + register int i; + + for (i = MEMORY_SIZE - 2; i > 0; i--) + if (mem_top[i] == '\n') break; + return i + 1; +} + +/* Print_table prints the line table in the given file_descriptor. If the fd + * equals ERROR, it opens a temp_file itself. + */ +void print_table(fd) +int fd; +{ + register char **line_ptr; /* Ptr in line_table */ + register char *ptr; /* Ptr to line */ + int index = 0; /* Index in output buffer */ + + if (fd == ERROR) { + if ((fd = creat(file_name(nr_of_files), 0644)) < 0) + error(TRUE, "Cannot creat ", file_name(nr_of_files)); + } + for (line_ptr = line_table; *line_ptr != NIL_PTR; line_ptr++) { + ptr = *line_ptr; + /* Skip all same lines if uniq is set */ + if (uniq && *(line_ptr + 1) != NIL_PTR) { + if (compare(ptr, *(line_ptr + 1)) == SAME) continue; + } + do { /* Print line in a buffered way */ + out_buffer[index++] = *ptr; + if (index == IO_SIZE) { + mwrite(fd, out_buffer, IO_SIZE); + index = 0; + } + } while (*ptr++ != '\n'); + } + mwrite(fd, out_buffer, index);/* Flush buffer */ + (void) close(fd); /* Close file */ + nr_of_files++; /* Increment nr_of_files to merge */ +} + +/* File_name () returns the nr argument from the argument list, or a uniq + * filename if the nr is too high, or the arguments were not merge files. + */ +char *file_name(nr) +register int nr; +{ + if (only_merge) { + if (args_offset + nr < args_limit) return argptr[args_offset + nr]; + } + temp_files[16] = nr / 26 + 'a'; + temp_files[17] = nr % 26 + 'a'; + + return temp_files; +} + +/* Mread () performs a normal read (), but checks the return value. */ +void mread(fd, address, bytes) +int fd; +char *address; +register int bytes; +{ + if (read(fd, address, bytes) < 0 && bytes != 0) + error(TRUE, "Read error", NIL_PTR); +} + +/* Mwrite () performs a normal write (), but checks the return value. */ +void mwrite(fd, address, bytes) +int fd; +char *address; +register int bytes; +{ + if (write(fd, address, bytes) != bytes && bytes != 0) + error(TRUE, "Write error", NIL_PTR); +} + +/* Sort () sorts the input in memory starting at mem_top. */ +void sort() +{ + register char *ptr = mem_top; + register int count = 0; + +/* Count number of lines in memory */ + while (*ptr) { + if (*ptr++ == '\n') count++; + } + +/* Set up the line table */ + line_table = (char **) msbrk(count * sizeof(char *) + sizeof(char *)); + + count = 1; + ptr = line_table[0] = mem_top; + while (*ptr) { + if (*ptr++ == '\n') line_table[count++] = ptr; + } + + line_table[count - 1] = NIL_PTR; + +/* Sort the line table */ + sort_table(count - 1); + +/* Stash output somewhere */ + if (in_core) { + open_outfile(); + print_table(out_fd); + } else + print_table(ERROR); + +/* Free line table */ + mbrk((char *) line_table); +} + +/* Sort_table () sorts the line table consisting of nel elements. */ +void sort_table(nel) +register int nel; +{ + char *tmp; + register int i; + + /* Make heap */ + for (i = (nel >> 1); i >= 1; i--) incr(i, nel); + + /* Sort from heap */ + for (i = nel; i > 1; i--) { + tmp = line_table[0]; + line_table[0] = line_table[i - 1]; + line_table[i - 1] = tmp; + incr(1, i - 1); + } +} + +/* Incr () increments the heap. */ +void incr(si, ei) +register int si, ei; +{ + char *tmp; + + while (si <= (ei >> 1)) { + si <<= 1; + if (si + 1 <= ei && compare(line_table[si - 1], line_table[si]) <= 0) + si++; + if (compare(line_table[(si >> 1) - 1], line_table[si - 1]) >= 0) + return; + tmp = line_table[(si >> 1) - 1]; + line_table[(si >> 1) - 1] = line_table[si - 1]; + line_table[si - 1] = tmp; + } +} + +/* Cmp_fields builds new lines out of the lines pointed to by el1 and el2 and + * puts it into the line1 and line2 arrays. It then calls the cmp () routine + * with the field describing the arguments. + */ +int cmp_fields(el1, el2) +register char *el1, *el2; +{ + int i, ret; + char line1[LINE_SIZE], line2[LINE_SIZE]; + + for (i = 0; i < field_cnt; i++) { /* Setup line parts */ + build_field(line1, &fields[i + 1], el1); + build_field(line2, &fields[i + 1], el2); + if ((ret = cmp((unsigned char *) line1, (unsigned char *) line2, + &fields[i + 1])) != SAME) + break; /* If equal, try next field */ + } + +/* Check for reverse flag */ + if (i != field_cnt && fields[i + 1].reverse) return -ret; + +/* Else return the last return value of cmp () */ + return ret; +} + +/* Build_field builds a new line from the src as described by the field. + * The result is put in dest. + */ +void build_field(dest, field, src) +char *dest; /* Holds result */ +register FIELD *field; /* Field description */ +register char *src; /* Source line */ +{ + char *begin = src; /* Remember start location */ + char *last; /* Pointer to end location */ + int i; + +/* Skip begin fields */ + src = skip_fields(src, field->beg_field); + +/* Skip begin positions */ + for (i = 0; i < field->beg_pos && *src != '\n'; i++) src++; + +/* Copy whatever is left */ + copy(dest, src); + +/* If end field is assigned truncate (perhaps) the part copied */ + if (field->end_field != ERROR) { /* Find last field */ + last = skip_fields(begin, field->end_field); +/* Skip positions as given by end fields description */ + for (i = 0; i < field->end_pos && *last != '\n'; i++) last++; + dest[last - src] = '\n';/* Truncate line */ + } +} + +/* Skip_fields () skips nf fields of the line pointed to by str. */ +char *skip_fields(str, nf) +register char *str; +int nf; +{ + while (nf-- > 0) { + if (separator == '\0') {/* Means ' ' or '\t' */ + while (*str != ' ' && *str != '\t' && *str != '\n') str++; + while (table[*str] & BLANK) str++; + } else { + while (*str != separator && *str != '\n') str++; + if (*str == separator) str++; + } + } + return str; /* Return pointer to indicated field */ +} + +/* Compare is called by all sorting routines. It checks if fields assignments + * has been made. if so, it calls cmp_fields (). If not, it calls cmp () and + * reversed the return value if the (global) reverse flag is set. + */ +int compare(el1, el2) +register char *el1, *el2; +{ + int ret; + + if (field_cnt > GLOBAL) return cmp_fields(el1, el2); + + ret = cmp((unsigned char *) el1, (unsigned char *) el2, &fields[GLOBAL]); + return(fields[GLOBAL].reverse) ? -ret : ret; +} + +/* Cmp () is the actual compare routine. It compares according to the + * description given in the field pointer. + */ +int cmp(el1, el2, field) +register unsigned char *el1, *el2; +FIELD *field; +{ + int c1, c2; + + if (field->blanks) { /* Skip leading blanks */ + while (table[*el1] & BLANK) el1++; + while (table[*el2] & BLANK) el2++; + } + if (field->numeric) /* Compare numeric */ + return digits((char *) el1, (char *) el2, TRUE); + + for (;;) { + while (*el1 == *el2) { + if (*el1++ == '\n') /* EOLN on both strings */ + return SAME; + el2++; + } + if (*el1 == '\n') /* EOLN on string one */ + return LOWER; + if (*el2 == '\n') return HIGHER; + if (field->ascii) { /* Skip chars outside 040 - 0177 */ + if ((table[*el1] & ASCII) == 0) { + do { + el1++; + } while ((table[*el1] & ASCII) == 0); + continue; + } + if ((table[*el2] & ASCII) == 0) { + do { + el2++; + } while ((table[*el2] & ASCII) == 0); + continue; + } + } + if (field->dictionary) {/* Skip non-dict chars */ + if ((table[*el1] & DICT) == 0) { + do { + el1++; + } while ((table[*el1] & DICT) == 0); + continue; + } + if ((table[*el2] & DICT) == 0) { + do { + el2++; + } while ((table[*el2] & DICT) == 0); + continue; + } + } + if (field->fold_case) { /* Fold upper case to lower */ + if (table[c1 = *el1++] & UPPER) c1 += 'a' - 'A'; + if (table[c2 = *el2++] & UPPER) c2 += 'a' - 'A'; + if (c1 == c2) continue; + return c1 - c2; + } + return *el1 - *el2; + } + + /* NOTREACHED */ +} + +/* + * Digits compares () the two strings that point to a number of digits followed + * by an optional decimal point. + */ +int digits(str1, str2, check_sign) +register char *str1, *str2; +BOOL check_sign; /* True if sign must be checked */ +{ + BOOL negative = FALSE; /* True if negative numbers */ + int diff, pow, ret; + +/* Check for optional minus or plus sign */ + if (check_sign) { + if (*str1 == '-') { + negative = TRUE; + str1++; + } else if (*str1 == '+') + str1++; + + if (*str2 == '-') { + if (negative == FALSE) return HIGHER; + str2++; + } else if (negative) + return LOWER; + else if (*str2 == '+') + str2++; + } + +/* Keep incrementing as long as digits are available and equal */ + while ((table[*str1] & DIGIT) && table[*str2] & DIGIT) { + if (*str1 != *str2) break; + str1++; + str2++; + } + +/* First check for the decimal point. */ + if (*str1 == '.' || *str2 == '.') { + if (*str1 == '.') { + if (*str2 == '.') /* Both. Check decimal part */ + ret = digits(str1 + 1, str2 + 1, FALSE); + else + ret = (table[*str2] & DIGIT) ? LOWER : HIGHER; + } else + ret = (table[*str1] & DIGIT) ? HIGHER : LOWER; + } + +/* Now either two digits differ, or unknown char is seen (e.g. end of string) */ + else if ((table[*str1] & DIGIT) && (table[*str2] & DIGIT)) { + diff = *str1 - *str2; /* Basic difference */ + pow = 0; /* Check power of numbers */ + while (table[*str1++] & DIGIT) pow++; + while (table[*str2++] & DIGIT) pow--; + ret = (pow == 0) ? diff : pow; + } + +/* Unknown char. Check on which string it occurred */ + else { + if ((table[*str1] & DIGIT) == 0) + ret = (table[*str2] & DIGIT) ? LOWER : SAME; + else + ret = HIGHER; + } + +/* Reverse sense of comparisons if negative is true. (-1000 < -1) */ + return(negative) ? -ret : ret; +} + +/* Files_merge () merges all files as indicated by nr_of_files. Merging goes + * in numbers of files that can be opened at the same time. (OPEN_FILES) + */ +void files_merge(file_cnt) +register int file_cnt; /* Nr_of_files to merge */ +{ + register int i; + int limit; + + for (i = 0; i < file_cnt; i += OPEN_FILES) { + /* Merge last files and store in output file */ + if ((limit = i + OPEN_FILES) >= file_cnt) { + open_outfile(); + limit = file_cnt; + } else { /* Merge OPEN_FILES files and store in temp + * file */ + temp_files[16] = file_cnt / 26 + 'a'; + temp_files[17] = file_cnt % 26 + 'a'; + if ((out_fd = creat(temp_files, 0644)) < 0) + error(TRUE, "Cannot creat ", temp_files); + file_cnt++; + } + merge(i, limit); + } + +/* Cleanup mess */ + i = (only_merge) ? args_limit - args_offset : 0; + while (i < file_cnt) (void) unlink(file_name(i++)); +} + +/* Merge () merges the files between start_file and limit_file. */ +void merge(start_file, limit_file) +int start_file, limit_file; +{ + register MERGE *smallest; /* Keeps track of smallest line */ + register int i; + int file_cnt = limit_file - start_file; /* Nr of files to merge */ + +/* Calculate size in core available for file_cnt merge structs */ + buf_size = MEMORY_SIZE / file_cnt - LINE_SIZE; + + mbrk(mem_top); /* First reset mem to lowest loc. */ + disabled = 0; /* All files not done yet */ + +/* Set up merge structures. */ + for (i = start_file; i < limit_file; i++) { + smallest = &merge_f[i - start_file]; + if (!strcmp(file_name(i), "-")) /* File is stdin */ + smallest->fd = 0; + else if ((smallest->fd = open(file_name(i), O_RDONLY)) < 0) { + smallest->fd = ERROR; + error(FALSE, "Cannot open ", file_name(i)); + disabled++; /* Done this file */ + continue; + } + smallest->buffer = msbrk(buf_size); + smallest->line = msbrk(LINE_SIZE); + smallest->cnt = smallest->read_chars = 0; + (void) read_line(smallest); /* Read first line */ + } + + if (disabled == file_cnt) { /* Couldn't open files */ + (void) close(out_fd); + return; + } + +/* Find a merg struct to assign smallest. */ + for (i = 0; i < file_cnt; i++) { + if (merge_f[i].fd != ERROR) { + smallest = &merge_f[i]; + break; + } + } + +/* Loop until all files minus one are done */ + while (disabled < file_cnt - 1) { + if (uniq) /* Skip all same lines */ + smallest = skip_lines(smallest, file_cnt); + else { /* Find smallest line */ + for (i = 0; i < file_cnt; i++) { + if (merge_f[i].fd == ERROR) + continue; /* We've had this one */ + if (compare(merge_f[i].line, smallest->line) < 0) + smallest = &merge_f[i]; + } + } /* Print line and read next */ + smallest = print(smallest, file_cnt); + } + + if (only_merge && uniq) + uniq_lines(smallest); /* Print only uniq lines */ + else /* Print rest of file */ + while (print(smallest, file_cnt) != NIL_MERGE); + + put_line(NIL_PTR); /* Flush output buffer */ +} + +/* Put_line () prints the line into the out_fd filedescriptor. If line equals + * NIL_PTR, the out_fd is flushed and closed. + */ +void put_line(line) +register char *line; +{ + static int index = 0; /* Index in out_buffer */ + + if (line == NIL_PTR) { /* Flush and close */ + mwrite(out_fd, out_buffer, index); + index = 0; + (void) close(out_fd); + return; + } + do { /* Fill out_buffer with line */ + out_buffer[index++] = *line; + if (index == IO_SIZE) { + mwrite(out_fd, out_buffer, IO_SIZE); + index = 0; + } + } while (*line++ != '\n'); +} + +/* + * Print () prints the line of the merg structure and tries to read another one. + * If this fails, it returns the next merg structure which file_descriptor is + * still open. If none could be found, a NIL structure is returned. + */ +MERGE *print(merg, file_cnt) +register MERGE *merg; +int file_cnt; /* Nr of files that are being merged */ +{ + register int i; + + put_line(merg->line); /* Print the line */ + + if (read_line(merg) == ERROR) { /* Read next line */ + for (i = 0; i < file_cnt; i++) { + if (merge_f[i].fd != ERROR) { + merg = &merge_f[i]; + break; + } + } + if (i == file_cnt) /* No more files left */ + return NIL_MERGE; + } + return merg; +} + +/* Read_line () reads a line from the fd from the merg struct. If the read + * failed, disabled is incremented and the file is closed. Readings are + * done in buf_size bytes. + * Lines longer than LINE_SIZE are silently truncated. + */ +int read_line(merg) +register MERGE *merg; +{ + register char *ptr = merg->line - 1; /* Ptr buf that will hold line */ + + do { + ptr++; + if (merg->cnt == merg->read_chars) { /* Read new buffer */ + if ((merg->read_chars = + read(merg->fd, merg->buffer, buf_size)) <= 0) { + (void) close(merg->fd); /* OOPS */ + merg->fd = ERROR; + disabled++; + return ERROR; + } + merg->cnt = 0; + } + *ptr = merg->buffer[merg->cnt++]; /* Assign next char of line */ + if (ptr - merg->line == LINE_SIZE - 1) + *ptr = '\n'; /* Truncate very long lines */ + } while (*ptr != '\n' && *ptr != '\0'); + + if (*ptr == '\0') /* Add '\n' to last line */ + *ptr = '\n'; + *++ptr = '\0'; /* Add '\0' */ + return OK; +} + +/* Skip_lines () skips all same lines in all the files currently being merged. + * It returns a pointer to the merge struct containing the smallest line. + */ +MERGE *skip_lines(smallest, file_cnt) +register MERGE *smallest; +int file_cnt; +{ + register int i; + int ret; + + if (disabled == file_cnt - 1) /* We've had all */ + return smallest; + + for (i = 0; i < file_cnt; i++) { + if (merge_f[i].fd == ERROR || smallest == &merge_f[i]) + continue; /* Don't check same file */ + while ((ret = compare(merge_f[i].line, smallest->line)) == 0) { + if (read_line(&merge_f[i]) == ERROR) break; /* EOF */ + } + if (ret < 0) /* Line wasn't smallest. Try again */ + return skip_lines(&merge_f[i], file_cnt); + } + return smallest; +} + +/* Uniq_lines () prints only the uniq lines out of the fd of the merg struct. */ +void uniq_lines(merg) +register MERGE *merg; +{ + char lastline[LINE_SIZE]; /* Buffer to hold last line */ + + for (;;) { + put_line(merg->line); /* Print this line */ + copy(lastline, merg->line); /* and save it */ + if (read_line(merg) == ERROR) /* Read the next */ + return; + /* Keep reading until lines duffer */ + while (compare(lastline, merg->line) == SAME) + if (read_line(merg) == ERROR) return; + } + + /* NOTREACHED */ +} + +/* + * Check_file () checks if a file is sorted in order according to the arguments + * given in main (). + */ +void check_file(fd, file) +int fd; +char *file; +{ + register MERGE *merg; /* 1 file only */ + char lastline[LINE_SIZE]; /* Save last line */ + register int ret; /* ret status of compare */ + + if (fd == 0) file = "stdin"; + merg = (MERGE *) mem_top; /* Assign MERGE structure */ + merg->buffer = mem_top + sizeof(MERGE); + merg->line = msbrk(LINE_SIZE); + merg->cnt = merg->read_chars = 0; + merg->fd = fd; + buf_size = MEMORY_SIZE - sizeof(MERGE); + + if (read_line(merg) == ERROR) /* Read first line */ + return; + copy(lastline, merg->line); /* and save it */ + + for (;;) { + if (read_line(merg) == ERROR) /* EOF reached */ + break; + if ((ret = compare(lastline, merg->line)) > 0) { + error(FALSE, "Disorder in file ", file); + write(2, merg->line, length(merg->line)); + break; + } else if (ret < 0) /* Copy if lines not equal */ + copy(lastline, merg->line); + else if (uniq) { + error(FALSE, "Non uniq line in file ", file); + write(2, merg->line, length(merg->line)); + break; + } + } + + mbrk(mem_top); /* Reset mem */ +} + +/* Length () returns the length of the argument line including the linefeed. */ +int length(line) +register char *line; +{ + register int i = 1; /* Add linefeed */ + + while (*line++ != '\n') i++; + return i; +} + +/* Copy () copies the src line into the dest line including linefeed. */ +void copy(dest, src) +register char *dest, *src; +{ + while ((*dest++ = *src++) != '\n'); +} + +/* Msbrk() does a sbrk() and checks the return value. */ +char *msbrk(size) +register int size; +{ + register char *address; + + if ((address = sbrk(size)) == (char *) -1) + error(TRUE, "Not enough memory. Use chmem to allocate more", NIL_PTR); + return address; +} + +/* Mbrk() does a brk() and checks the return value. */ +void mbrk(address) +char *address; +{ + if (brk(address) == -1) error(TRUE, "Cannot reset memory", NIL_PTR); +} + +void catch(dummy) +int dummy; /* to satisfy the prototype */ +{ + register int i; + + signal(SIGINT, SIG_IGN); + only_merge = FALSE; + for (i = 0; i < 26; i++) (void) unlink(file_name(i)); + exit(2); +} diff --git a/commands/simple/split.c b/commands/simple/split.c new file mode 100755 index 000000000..290f486bf --- /dev/null +++ b/commands/simple/split.c @@ -0,0 +1,117 @@ +/* split - split a file Author: Michiel Huisjes */ + +#include <sys/types.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <minix/minlib.h> + +#define CHUNK_SIZE 1024 + +int cut_line = 1000; +int infile; +char out_file[100]; +char *suffix; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void split, (void)); +_PROTOTYPE(int newfile, (void)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(void quit, (void)); + +int main(argc, argv) +int argc; +char **argv; +{ + unsigned short i; + + out_file[0] = 'x'; + infile = -1; + + if (argc > 4) usage(); + for (i = 1; i < argc; i++) { + if (argv[i][0] == '-') { + if (argv[i][1] >= '0' && argv[i][1] <= '9' + && cut_line == 1000) + cut_line = atoi(argv[i]); + else if (argv[i][1] == '\0' && infile == -1) + infile = 0; + else + usage(); + } else if (infile == -1) { + if ((infile = open(argv[i], O_RDONLY)) < 0) { + std_err("Cannot open input file.\n"); + exit(1); + } + } else + strcpy(out_file, argv[i]); + } + if (infile == -1) infile = 0; + strcat(out_file, "aa"); + for (suffix = out_file; *suffix; suffix++); + suffix--; + +/* Appendix now points to last `a' of "aa". We have to decrement it by one */ + *suffix = 'a' - 1; + split(); + return(0); +} + +void split() +{ + char buf[CHUNK_SIZE]; + register char *index, *base; + register int n; + int fd; + long lines = 0L; + + fd = -1; + while ((n = read(infile, buf, CHUNK_SIZE)) > 0) { + base = index = buf; + while (--n >= 0) { + if (*index++ == '\n') + if (++lines % cut_line == 0) { + if (fd == -1) fd = newfile(); + if (write(fd, base, (int) (index - base)) != (int) (index - base)) + quit(); + base = index; + close(fd); + fd = -1; + } + } + if (index == base) continue; + if (fd == -1) fd = newfile(); + if (write(fd, base, (int) (index - base)) != (int) (index - base)) + quit(); + } +} + +int newfile() +{ + int fd; + + if (++*suffix > 'z') { /* Increment letter */ + *suffix = 'a'; /* Reset last letter */ + ++*(suffix - 1); /* Previous letter must be incremented */ + /* E.g. was `filename.az' */ + /* Now `filename.ba' */ + } + if ((fd = creat(out_file, 0644)) < 0) { + std_err("Cannot create new file.\n"); + exit(2); + } + return fd; +} + +void usage() +{ + std_err("Usage: split [-n] [file [name]].\n"); + exit(1); +} + +void quit() +{ + std_err("split: write error\n"); + exit(1); +} diff --git a/commands/simple/stat.c b/commands/simple/stat.c new file mode 100755 index 000000000..c23e0677e --- /dev/null +++ b/commands/simple/stat.c @@ -0,0 +1,369 @@ +/* stat.c Feb 1987 - main, printit, statit + * + * stat - a program to perform what the stat(2) call does. + * + * usage: stat [-] [-all] -<field> [-<field> ...] [file1 file2 file3 ...] + * + * where <field> is one of the struct stat fields without the leading "st_". + * The three times can be printed out as human times by requesting + * -Ctime instead of -ctime (upper case 1st letter). + * - means take the file names from stdin. + * -0.. means fd0.. + * no files means all fds. + * + * output: if only one field is specified, that fields' contents are printed. + * if more than one field is specified, the output is + * file filed1: f1val, field2: f2val, etc + * + * written: Larry McVoy, (mcvoy@rsch.wisc.edu) + */ + +# define ALLDEF /* Make -all default. (kjb) */ + +# include <sys/types.h> +# include <errno.h> +# include <limits.h> +# include <stdio.h> +# include <stdlib.h> +# include <string.h> +# include <time.h> +# include <sys/stat.h> +# define addr(x) ((void*) &sbuf.x) +# define size(x) sizeof(sbuf.x) +# define equal(s, t) (strcmp(s, t) == 0) +# ifndef PATH_MAX +# define PATH_MAX 1024 +# endif +# undef LS_ADDS_SPACE /* AT&T Unix PC, ls prints "file[* /]" */ + /* This makes stat fail. */ + +# ifndef _MINIX /* All but Minix have u_* and st_blocks */ +# define BSD +# endif + +# ifndef BSD +typedef unsigned char u_char; +typedef unsigned short u_short; +typedef unsigned int u_int; +typedef unsigned long u_long; +# endif + +# ifndef S_IREAD +# define S_IREAD S_IRUSR +# define S_IWRITE S_IWUSR +# define S_IEXEC S_IXUSR +# endif + +char * arg0; +struct stat sbuf; +extern int errno; +int first_file= 1; +#ifndef S_IFLNK +#define lstat stat +#endif + +struct field { + char* f_name; /* field name in stat */ + u_char* f_addr; /* address of the field in sbuf */ + u_short f_size; /* size of the object, needed for pointer arith */ + u_short f_print; /* show this field? */ +} fields[] = { + { "dev", addr(st_dev), size(st_dev), 0 }, + { "ino", addr(st_ino), size(st_ino), 0 }, + { "mode", addr(st_mode), size(st_mode), 0 }, + { "nlink", addr(st_nlink), size(st_nlink), 0 }, + { "uid", addr(st_uid), size(st_uid), 0 }, + { "gid", addr(st_gid), size(st_gid), 0 }, + { "rdev", addr(st_rdev), size(st_rdev), 0 }, + { "size", addr(st_size), size(st_size), 0 }, + { "Atime", addr(st_atime), size(st_atime), 0 }, + { "atime", addr(st_atime), size(st_atime), 0 }, + { "Mtime", addr(st_mtime), size(st_mtime), 0 }, + { "mtime", addr(st_mtime), size(st_mtime), 0 }, + { "Ctime", addr(st_ctime), size(st_ctime), 0 }, + { "ctime", addr(st_ctime), size(st_ctime), 0 }, +# ifdef BSD + { "blksize", addr(st_blksize), size(st_blksize), 0 }, + { "blocks", addr(st_blocks), size(st_blocks), 0 }, +# endif + { NULL, 0, 0, 0 }, +}; + +void printstat(struct stat *sbuf, int nprint); +void printit(struct stat* sb, struct field* f, int n); +void rwx(mode_t mode, char *bit); +void usage(void); + +int main(int ac, char** av) +{ + int i, j, nprint = 0, files = 0; + char buf[PATH_MAX], *check; + int sym=0, ret=0, from_stdin = 0; + int err; + u_long fd; + + if ((arg0 = strrchr(av[0], '/')) == NULL) arg0 = av[0]; else arg0++; +#ifdef S_IFLNK + if (equal(arg0, "lstat")) sym = 1; +#endif + + if (ac > 1 && equal(av[i = 1], "-")) + i++, from_stdin++; + + for (i= 1; i<ac; i++) { + if (av[i][0] == '-') { + if (equal(av[i], "-")) { + from_stdin= 1; + files++; + continue; + } + if (equal("-all", av[i])) { + for (j=0; fields[j].f_name; j++) + nprint++, fields[j].f_print++; + continue; + } + if (equal("-s", av[i])) { +#ifdef S_IFLNK + sym=1; +#endif + continue; + } + fd = strtoul(av[i]+1, &check, 0); + if (check != av[i]+1 && *check == '\0') + { + files++; + continue; + } + for (j=0; fields[j].f_name; j++) + if (equal(fields[j].f_name, &av[i][1])) { + nprint++, fields[j].f_print++; + break; + } + if (!fields[j].f_name) { + if (!equal("-?", av[i])) { + fprintf(stderr, "stat: %s: bad field\n", av[i]); + } + usage(); + } + } + else + files++; + } + if (!nprint) { +# ifndef ALLDEF + usage(); +# else + for (j=0; fields[j].f_name; j++) + nprint++, fields[j].f_print++; +# endif + } + + if (from_stdin) + files++; /* We don't know how many files come from stdin. */ + + if (files == 0) { /* Stat all file descriptors. */ + for (i= 0; i<OPEN_MAX; i++) { + err= fstat(i, &sbuf); + if (err == -1 && errno == EBADF) + continue; + if (err == 0) { + if (!first_file) fputc('\n', stdout); + printf("fd %d:\n", i); + printstat(&sbuf, nprint); + } + else { + fprintf(stderr, "%s: fd %d: %s\n", arg0, i, strerror(errno)); + ret= 1; + } + } + exit(ret); + } + + for (i=1; i<ac; i++) { + if (equal(av[i], "-")) { + while (fgets(buf, sizeof(buf), stdin)) { + char *p= strchr(buf, '\n'); + if (p) *p= 0; + if (!sym) err= stat(av[i], &sbuf); + if (sym || (err != 0 && errno == ENOENT)) { + err= lstat(av[i], &sbuf); + } + if (err == -1) { + fprintf(stderr, "%s: %s: %s\n", + arg0, av[i], strerror(errno)); + ret= 1; + } + else { + if (!first_file) fputc('\n', stdout); + printf("%s:\n", buf); + printstat(&sbuf, nprint); + } + } + continue; + } + if (av[i][0] == '-') { + fd= strtoul(av[i]+1, &check, 10); + if (check == av[i]+1 || *check != '\0') continue; + if (fd >= INT_MAX) { + err= -1; + errno= EBADF; + } + else { + err= fstat((int) fd, &sbuf); + } + if (err != -1) { + if (!first_file) fputc('\n', stdout); + if (files != 1) printf("fd %lu:\n", fd); + printstat(&sbuf, nprint); + } + else { + fprintf(stderr, "fd %lu: %s\n", fd, strerror(errno)); + ret= 1; + } + continue; + } + if (!sym) err= stat(av[i], &sbuf); + if (sym || (err != 0 && errno == ENOENT)) err= lstat(av[i], &sbuf); + if (err != -1) { + if (!first_file) fputc('\n', stdout); + if (files != 1) printf("%s:\n", av[i]); + printstat(&sbuf, nprint); + } + else { + fprintf(stderr, "%s: %s: %s\n", arg0, av[i], strerror(errno)); + ret= 1; + } + } + exit(ret); +} + +/*------------------------------------------------------------------30/Jan/87-* + * printstat(file, nprint) - do the work + *----------------------------------------------------------------larry mcvoy-*/ +void printstat(struct stat *sbuf, int nprint) +{ + int j; + int first_field= 1; + + for (j=0; fields[j].f_name; j++) { + if (fields[j].f_print) { + if (!first_field) fputc('\n', stdout); + printit(sbuf, &fields[j], nprint); + first_field= 0; + } + } + fputc('\n', stdout); + first_file= 0; +} + +/*------------------------------------------------------------------30/Jan/87-* + * printit(sb, f, n) - print the field + * + * Inputs -> (struct stat*), (struct field*), (int) + * + * Results -> Displays the field, with special handling of weird fields like + * mode and mtime. The mode field is dumped in octal, followed + * by one or more of the S_IF<X> and/or S_I<X> values. + *----------------------------------------------------------------larry mcvoy-*/ +void printit(struct stat* sb, struct field* f, int n) +{ + if (n > 1) + printf("%s: ", f->f_name); + if (equal(f->f_name, "mode")) { + /* This lot changed to my personal liking. (kjb) */ + char bit[11]; + + printf("%07lo, ", (u_long) sb->st_mode); + + strcpy(bit, "----------"); + + switch (sb->st_mode&S_IFMT) { + case S_IFDIR: bit[0]='d'; break; +# ifdef S_IFFIFO + case S_IFFIFO: bit[0]='p'; break; +# endif + case S_IFCHR: bit[0]='c'; break; + case S_IFBLK: bit[0]='b'; break; +# ifdef S_IFSOCK + case S_IFSOCK: bit[0]='S'; break; +# endif +# ifdef S_IFMPC + case S_IFMPC: bit[0]='C'; break; +# endif +# ifdef S_IFMPB + case S_IFMPB: bit[0]='B'; break; +# endif +# ifdef S_IFLNK + case S_IFLNK: bit[0]='l'; break; +# endif + } + rwx(sb->st_mode, bit+1); + rwx(sb->st_mode<<3, bit+4); + rwx(sb->st_mode<<6, bit+7); + if (sb->st_mode&S_ISUID) bit[3]='s'; + if (sb->st_mode&S_ISGID) bit[6]='s'; + if (sb->st_mode&S_ISVTX) bit[9]='t'; + printf("\"%s\"", bit); + } + /* times in human form, uppercase first letter */ + else if (equal("Ctime", f->f_name)) { + printf("%.24s (%lu)", ctime(&sb->st_ctime), (u_long) sb->st_ctime); + f[1].f_print= 0; + } + else if (equal("Mtime", f->f_name)) { + printf("%.24s (%lu)", ctime(&sb->st_mtime), (u_long) sb->st_mtime); + f[1].f_print= 0; + } + else if (equal("Atime", f->f_name)) { + printf("%.24s (%lu)", ctime(&sb->st_atime), (u_long) sb->st_atime); + f[1].f_print= 0; + } + else if (equal("ctime", f->f_name)) { + printf("%lu", (u_long) sb->st_ctime); + } + else if (equal("mtime", f->f_name)) { + printf("%lu", (u_long) sb->st_mtime); + } + else if (equal("atime", f->f_name)) { + printf("%lu", (u_long) sb->st_atime); + } + else { + switch (f->f_size) { + case sizeof(char): + printf("%d", * (u_char *) f->f_addr); + break; + case sizeof(short): + printf("%u", (u_int) * (u_short *) f->f_addr); + break; +#if INT_MAX != SHRT_MAX + case sizeof(int): + printf("%u", * (u_int *) f->f_addr); + break; +#endif +#if LONG_MAX != INT_MAX && LONG_MAX != SHRT_MAX + case sizeof(long): + printf("%lu", * (u_long *) f->f_addr); + break; +#endif + default: + fprintf(stderr, "\nProgram error: bad '%s' field size %d\n", + f->f_name, f->f_size); + break; + } + } +} + +void rwx(mode_t mode, char *bit) +{ + if (mode&S_IREAD) bit[0]='r'; + if (mode&S_IWRITE) bit[1]='w'; + if (mode&S_IEXEC) bit[2]='x'; +} + +void usage(void) +{ + fprintf(stderr, + "Usage: %s [-] [-fd] [-all] [-s] [-field ...] [file1 ...]\n", + arg0); + exit(1); +} diff --git a/commands/simple/strings.c b/commands/simple/strings.c new file mode 100755 index 000000000..cf8a60af5 --- /dev/null +++ b/commands/simple/strings.c @@ -0,0 +1,139 @@ +/* strings - print ASCII strings in a file Author: Peter S. Housel */ + +/* + This is a version of the BSD "strings" program for Minix. It is used + to search a file for printable strings. To install, + + cc -o strings strings.c + chmem =8000 strings + +Command: strings - search file for printable strings +Syntax: strings [-] [-o] [-len] file ... +Flags: - Search the entire file. If this option is not given, only + the initialized data segment of files that appear to be + "a.out" format is searched. + -o Print the offset (in octal) with each string. + -len Use "len" as the minimum string length. The default is 4. + +Examples: strings core + strings -o a.out > str + +Strings searches the specified file(s) for printable ASCII strings (four +or more printable characters followed by a newline or a null) and writes +them to the standard output. This can be used to find out, for example, to +find out what program a "core" file came from, what kinds of error messages +are in an executable, or to see ASCII data hidden in a "binary" data file. + +P.S. The program doesn't use the "a.out.h" file posted last week by +Dick van Veen, both because it was written before then, and because +not everybody has a.out.h yet. Future revisions probably ought to, though. + +*/ + + + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +/* Minix (8086 version) dependant definitions */ +#define SMALLMAGIC 0x04100301L /* small model a.out magic number */ +#define SEPARATEMAGIC 0x04200301L /* separate instruction/data a.out */ + +#define HDR_MAGIC 0 /* 0'th long magic number */ +#define HDR_HSIZE 1 /* 1'st long size of header */ +#define HDR_TSIZE 2 /* 2'nd long size of text */ +#define HDR_DSIZE 3 /* 3'rd long size of init'ed data */ +#define HDR_BSIZE 4 /* 4'th long size of bss */ +#define HDR_TOTMEM 6 /* 6'th long total memory */ + +#define HDR_LEN 8 /* total length of header */ + +/* Miscellaneous definitions */ +#define STRLEN 4 /* default minimum string length */ +#define STRBUF 512 /* buffer length for strings */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void strings, (char *filename)); +_PROTOTYPE(void usage, (void)); + +int strmin = STRLEN; /* minimum string length */ +int printoff = 0; /* print octal offset of each str */ +int objall = 0; /* search entire a.out file, not */ + +/* Just initialized data segment */ + +int main(argc, argv) +int argc; +char *argv[]; +{ + while ((++argv, --argc) && '-' == (*argv)[0]) { + if (!strcmp(*argv, "-")) + ++objall; + else if (!strcmp(*argv, "-o")) + ++printoff; + else if (isdigit((*argv)[1])) + strmin = atoi(&(*argv)[1]); + else + usage(); + } + + if (0 == argc) usage(); + while (argc--) strings(*argv++); + return(0); +} + +void strings(filename) +char *filename; +{ + char buf[STRBUF]; /* the strings buffer */ + char *bufptr; /* pointer into the strings buffer */ + FILE *input; /* input file */ + long header[HDR_LEN]; /* buffer for reading the header */ + long offset; /* file offset */ + long limit; /* limit, if doing data segment only */ + int c; /* input character */ + + if (NULL == (input = fopen(filename, "r"))) { + fprintf(stderr, "strings: "); + perror(filename); + exit(1); + } + if (HDR_LEN == fread(header, sizeof(long), (size_t)HDR_LEN, input) + && (SMALLMAGIC == header[HDR_MAGIC] + ||SEPARATEMAGIC == header[HDR_MAGIC]) && !objall) { + offset = header[HDR_HSIZE] + header[HDR_TSIZE]; /* object file */ + limit = offset + header[HDR_DSIZE]; + } else { + offset = 0L; + limit = 0L; + } + + fseek(input, offset, 0); + bufptr = buf; + + while (!limit || offset < limit) { + if (EOF == (c = getc(input))) break; + if ((('\0' == c || '\n' == c) && bufptr - buf >= strmin) + || (bufptr - buf == STRBUF - 1)) { + *bufptr = '\0'; + if (printoff) printf("%lo:", offset - (bufptr - buf)); + puts(buf); + bufptr = buf; + } else if ((' ' <= c && c < 0177) || '\t' == c) + *bufptr++ = c; + else + bufptr = buf; + + ++offset; + } + + fclose(input); +} + +void usage() +{ + fprintf(stderr, "usage: strings [-] [-o] [-num] file ...\n"); + exit(1); +} diff --git a/commands/simple/strip.c b/commands/simple/strip.c new file mode 100755 index 000000000..972f6090c --- /dev/null +++ b/commands/simple/strip.c @@ -0,0 +1,169 @@ +/* strip - remove symbols. Author: Dick van Veen */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <a.out.h> +#include <unistd.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +/* Strip [file] ... + * + * - when no file is present, a.out is assumed. + * + */ + +#define A_OUT "a.out" +#define NAME_LENGTH 128 /* max file path name */ + +char buffer[BUFSIZ]; /* used to copy executable */ +char new_file[NAME_LENGTH]; /* contains name of temporary */ +struct exec header; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void strip, (char *file)); +_PROTOTYPE(int read_header, (int fd)); +_PROTOTYPE(int write_header, (int fd)); +_PROTOTYPE(int make_tmp, (char *new_name, char *name)); +_PROTOTYPE(int copy_file, (int fd1, int fd2, long size)); + +int main(argc, argv) +int argc; +char **argv; +{ + argv++; + if (*argv == NULL) + strip(A_OUT); + else + while (*argv != NULL) { + strip(*argv); + argv++; + } + return(0); +} + +void strip(file) +char *file; +{ + int fd, new_fd; + struct stat buf; + long symb_size, relo_size; + + fd = open(file, O_RDONLY); + if (fd == -1) { + fprintf(stderr, "can't open %s\n", file); + close(fd); + return; + } + if (read_header(fd)) { + fprintf(stderr, "%s: not an executable file\n", file); + close(fd); + return; + } + if (header.a_syms == 0L) { + close(fd); /* no symbol table present */ + return; + } + symb_size = header.a_syms; + header.a_syms = 0L; /* remove table size */ + fstat(fd, &buf); + relo_size = buf.st_size - (A_MINHDR + header.a_text + header.a_data + symb_size); + new_fd = make_tmp(new_file, file); + if (new_fd == -1) { + fprintf(stderr, "can't create temporary file\n"); + close(fd); + return; + } + if (write_header(new_fd)) { + fprintf(stderr, "can't write temporary file\n"); + unlink(new_file); + close(fd); + close(new_fd); + return; + } + if (copy_file(fd, new_fd, header.a_text + header.a_data)) { + fprintf(stderr, "can't copy %s\n", file); + unlink(new_file); + close(fd); + close(new_fd); + return; + } + if (relo_size != 0) { + lseek(fd, symb_size, 1); + if (copy_file(fd, new_fd, relo_size)) { + fprintf(stderr, "can't copy %s\n", file); + unlink(new_file); + close(fd); + close(new_fd); + return; + } + } + close(fd); + close(new_fd); + if (unlink(file) == -1) { + fprintf(stderr, "can't unlink %s\n", file); + unlink(new_file); + return; + } + link(new_file, file); + unlink(new_file); + chmod(file, buf.st_mode); +} + +int read_header(fd) +int fd; +{ + if (read(fd, (char *) &header, A_MINHDR) != A_MINHDR) return(1); + if (BADMAG(header)) return (1); + if (header.a_hdrlen > sizeof(struct exec)) return (1); + lseek(fd, 0L, SEEK_SET); /* variable size header */ + if (read(fd, (char *)&header, (int)header.a_hdrlen) != (int) header.a_hdrlen) + return(1); + return(0); +} + +int write_header(fd) +int fd; +{ + lseek(fd, 0L, SEEK_SET); + if (write(fd, (char *)&header, (int)header.a_hdrlen) != (int)header.a_hdrlen) + return(1); + return(0); +} + +int make_tmp(new_name, name) +char *new_name, *name; +{ + int len; + char *nameptr; + + len = strlen(name); + if (len + 1 > NAME_LENGTH) return(-1); + strcpy(new_name, name); + nameptr = strrchr(new_name, '/'); + if (nameptr == NULL) nameptr = new_name - 1; + if (nameptr - new_name + 6 + 1 > NAME_LENGTH) return (-1); + strcpy(nameptr + 1, "XXXXXX"); + mktemp(new_name); + return(creat(new_name, 0777)); +} + +int copy_file(fd1, fd2, size) +int fd1, fd2; +long size; +{ + int length; + + while (size > 0) { + if (size < sizeof(buffer)) + length = size; + else + length = sizeof(buffer); + if (read(fd1, buffer, length) != length) return(1); + if (write(fd2, buffer, length) != length) return (1); + size -= length; + } + return(0); +} diff --git a/commands/simple/stty.c b/commands/simple/stty.c new file mode 100755 index 000000000..c8dda55d3 --- /dev/null +++ b/commands/simple/stty.c @@ -0,0 +1,1277 @@ +/* stty - set terminal mode Author: Andy Tanenbaum */ + +/* + Adapted to POSIX 1003.2 by Philip Homburg. + */ + +#ifdef __minix_vmd +#define _MINIX_SOURCE +#endif + +#include <assert.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#ifdef __minix +#include <sys/types.h> +#include <sys/ioctl.h> +#endif + +/* Default settings, the Minix ones are defined in <termios.h> */ + +#ifndef TCTRL_DEF +#define TCTRL_DEF (PARENB | CREAD | CS7) +#endif + +#ifndef TSPEED_DEF +#define TSPEED_DEF B1200 +#endif + +#ifndef TINPUT_DEF +#define TINPUT_DEF (BRKINT | IGNPAR | ISTRIP | ICRNL) +#endif + +#ifndef TOUTPUT_DEF +#define TOUTPUT_DEF OPOST +#endif + +#ifndef TLOCAL_DEF +#define TLOCAL_DEF (ISIG | IEXTEN | ICANON | ECHO | ECHOE) +#endif + +#ifndef TEOF_DEF +#define TEOF_DEF '\4' /* ^D */ +#endif +#ifndef TEOL_DEF +#ifdef _POSIX_VDISABLE +#define TEOL_DEF _POSIX_VDISABLE +#else +#define TEOL_DEF '\n' +#endif +#endif +#ifndef TERASE_DEF +#define TERASE_DEF '\10' /* ^H */ +#endif +#ifndef TINTR_DEF +#define TINTR_DEF '\177' /* ^? */ +#endif +#ifndef TKILL_DEF +#define TKILL_DEF '\25' /* ^U */ +#endif +#ifndef TQUIT_DEF +#define TQUIT_DEF '\34' /* ^\ */ +#endif +#ifndef TSUSP_DEF +#define TSUSP_DEF '\32' /* ^Z */ +#endif +#ifndef TSTART_DEF +#define TSTART_DEF '\21' /* ^Q */ +#endif +#ifndef TSTOP_DEF +#define TSTOP_DEF '\23' /* ^S */ +#endif +#ifndef TMIN_DEF +#define TMIN_DEF 1 +#endif +#ifndef TTIME_DEF +#define TTIME_DEF 0 +#endif + + + +char *prog_name; +struct termios termios; +int column= 0, max_column=80; /* Assume 80 character terminals. */ +#ifdef __minix +struct winsize winsize; +#endif + +#if __minix +#define PROTO(a) _ARGS(a) +#else +#define PROTO(a) () +#endif + +void main PROTO(( int argc, char **argv )); +void report PROTO(( int flags )); +int option PROTO(( char *opt, char *next )); +int match PROTO(( char *s1, char *s2 )); +void prctl PROTO(( int c )); +speed_t long2speed PROTO(( long num )); +long speed2long PROTO(( unsigned long speed )); +void print_flags PROTO(( unsigned long flags, unsigned long flag, + unsigned long def, char *string, int all )); +void output PROTO(( char *s )); +void do_print_char PROTO(( unsigned chr, unsigned def, char *name, int all )); +void do_print_num PROTO(( unsigned num, unsigned def, char *name, int all )); +void set_saved_settings PROTO(( char *opt )); +void set_control PROTO(( int option, char *value )); +void set_min_tim PROTO(( int option, char *value )); + +#define print_char(c,d,n,a) (do_print_char((unsigned)(c),(unsigned)(d),(n),(a))) +#define print_num(m,d,n,a) (do_print_num((unsigned)(m),(unsigned)(d),(n),(a))) + +void main(argc, argv) +int argc; +char *argv[]; +{ + int flags, k; + + prog_name= argv[0]; + flags= 0; + + /* Stty with no arguments just reports on current status. */ + if (tcgetattr(0, &termios) == -1) + { + fprintf(stderr, "%s: can't read ioctl parameters from stdin: %s\n", + prog_name, strerror(errno)); + exit(1); + } +#ifdef __minix + if (ioctl(0, TIOCGWINSZ, &winsize) == -1) + { + fprintf(stderr, "%s: can't get screen size from stdin: %s\n", + prog_name, strerror(errno)); + exit(1); + } + if (winsize.ws_col != 0) + max_column= winsize.ws_col; +#endif + + if (argc == 2) + { + if (!strcmp(argv[1], "-a")) + flags |= 1; + else if (!strcmp(argv[1], "-g")) + flags |= 2; + } + if (argc == 1 || flags) { + report(flags); + exit(0); + } + + /* Process the options specified. */ + for (k= 1; k < argc; k++) + k += option(argv[k], k+1 < argc ? argv[k+1] : ""); + +#ifdef __minix + if (ioctl(0, TIOCSWINSZ, &winsize) == -1) + { + fprintf(stderr, "%s: can't set screen size to stdin: %s\n", + prog_name, strerror(errno)); + exit(1); + } +#endif + if (tcsetattr(0, TCSANOW, &termios) == -1) + { + fprintf(stderr, "%s: can't set terminal parameters to stdin: %s\n", + prog_name, strerror(errno)); + exit(1); + } + exit(0); +} + + + +void report(flags) +int flags; +{ + int i, all; + tcflag_t c_cflag, c_iflag, c_oflag, c_lflag; + char line[80]; + speed_t ispeed, ospeed; + + if (flags & 2) + { /* We have to write the termios structure in a encoded form + * to stdout. + */ + printf(":%x:%x:%x:%x:%x:%x", termios.c_iflag, termios.c_oflag, + termios.c_cflag, termios.c_lflag, cfgetispeed(&termios), + cfgetospeed(&termios)); + for (i= 0; i<NCCS; i++) + printf(":%x", termios.c_cc[i]); + printf(":\n"); + return; + } + + all= !!flags; + + /* Start with the baud rate. */ + ispeed= cfgetispeed(&termios); + ospeed= cfgetospeed(&termios); + if (ispeed != ospeed) + { + sprintf(line, "ispeed %lu baud; ospeed %lu baud;", + speed2long(ispeed), speed2long(ospeed)); + output(line); + } + else if (all || ospeed != TSPEED_DEF) + { + sprintf(line, "speed %lu baud;", speed2long(ospeed)); + output(line); + } + + /* The control modes. */ + + c_cflag= termios.c_cflag; + if (all || (c_cflag & CSIZE) != (TCTRL_DEF & CSIZE)) + { + switch (c_cflag & CSIZE) + { + case CS5: output("cs5"); break; + case CS6: output("cs6"); break; + case CS7: output("cs7"); break; + case CS8: output("cs8"); break; + default: output("cs??"); break; + } + } + print_flags(c_cflag, PARENB, TCTRL_DEF, "-parenb", all); + print_flags(c_cflag, PARODD, TCTRL_DEF, "-parodd", all); + print_flags(c_cflag, HUPCL, TCTRL_DEF, "-hupcl", all); + print_flags(c_cflag, CSTOPB, TCTRL_DEF, "-cstopb", all); + print_flags(c_cflag, CREAD, TCTRL_DEF, "-cread", all); + print_flags(c_cflag, CLOCAL, TCTRL_DEF, "-clocal", all); + + if (all) + { + printf("\n"); + column= 0; + } + + /* The input flags. */ + + c_iflag= termios.c_iflag; + + print_flags(c_iflag, IGNBRK, TINPUT_DEF, "-ignbrk", all); + print_flags(c_iflag, BRKINT, TINPUT_DEF, "-brkint", all); + print_flags(c_iflag, IGNPAR, TINPUT_DEF, "-ignpar", all); + print_flags(c_iflag, PARMRK, TINPUT_DEF, "-parmrk", all); + print_flags(c_iflag, INPCK, TINPUT_DEF, "-inpck", all); + print_flags(c_iflag, ISTRIP, TINPUT_DEF, "-istrip", all); + print_flags(c_iflag, INLCR, TINPUT_DEF, "-inlcr", all); + print_flags(c_iflag, IGNCR, TINPUT_DEF, "-igncr", all); + print_flags(c_iflag, ICRNL, TINPUT_DEF, "-icrnl", all); + print_flags(c_iflag, IXON, TINPUT_DEF, "-ixon", all); + print_flags(c_iflag, IXOFF, TINPUT_DEF, "-ixoff", all); + print_flags(c_iflag, IXANY, TINPUT_DEF, "-ixany", all); + + if (all) + { + printf("\n"); + column= 0; + } + + /* The output flags. */ + + c_oflag= termios.c_oflag; + + print_flags(c_oflag, OPOST, TOUTPUT_DEF, "-opost", all); +#ifdef __minix + print_flags(c_oflag, ONLCR, TOUTPUT_DEF, "-onlcr", all); + print_flags(c_oflag, XTABS, TOUTPUT_DEF, "-xtabs", all); + print_flags(c_oflag, ONOEOT, TOUTPUT_DEF, "-onoeot", all); +#endif + if (all) + { + printf("\n"); + column= 0; + } + + /* The local flags. */ + + c_lflag= termios.c_lflag; + + print_flags(c_lflag, ISIG, TLOCAL_DEF, "-isig", all); + print_flags(c_lflag, ICANON, TLOCAL_DEF, "-icanon", all); + print_flags(c_lflag, IEXTEN, TLOCAL_DEF, "-iexten", all); + print_flags(c_lflag, ECHO, TLOCAL_DEF, "-echo", all); + print_flags(c_lflag, ECHOE, TLOCAL_DEF, "-echoe", all); + print_flags(c_lflag, ECHOK, TLOCAL_DEF, "-echok", all); + print_flags(c_lflag, ECHONL, TLOCAL_DEF, "-echonl", all); + print_flags(c_lflag, NOFLSH, TLOCAL_DEF, "-noflsh", all); +#ifdef TOSTOP + print_flags(c_lflag, TOSTOP, TLOCAL_DEF, "-tostop", all); +#endif +#ifdef __minix + print_flags(c_lflag, LFLUSHO, TLOCAL_DEF, "-lflusho", all); +#endif + + if (all) + { + printf("\n"); + column= 0; + } + + /* The special control characters. */ + + print_char(termios.c_cc[VEOF], TEOF_DEF, "eof", all); + print_char(termios.c_cc[VEOL], TEOL_DEF, "eol", all); + print_char(termios.c_cc[VERASE], TERASE_DEF, "erase", all); + print_char(termios.c_cc[VINTR], TINTR_DEF, "intr", all); + print_char(termios.c_cc[VKILL], TKILL_DEF, "kill", all); + print_char(termios.c_cc[VQUIT], TQUIT_DEF, "quit", all); + print_char(termios.c_cc[VSUSP], TSUSP_DEF, "susp", all); + print_char(termios.c_cc[VSTART], TSTART_DEF, "start", all); + print_char(termios.c_cc[VSTOP], TSTOP_DEF, "stop", all); +#ifdef __minix + print_char(termios.c_cc[VREPRINT], TREPRINT_DEF, "rprnt", all); + print_char(termios.c_cc[VLNEXT], TLNEXT_DEF, "lnext", all); + print_char(termios.c_cc[VDISCARD], TDISCARD_DEF, "flush", all); +#endif + print_num(termios.c_cc[VMIN], TMIN_DEF, "min", all); + print_num(termios.c_cc[VTIME], TTIME_DEF, "time", all); + if (all) + { + printf("\n"); + column= 0; + } + +#ifdef __minix + /* Screen size */ + if (all || winsize.ws_row != 0 || winsize.ws_col != 0) + { + sprintf(line, "%d rows %d columns", winsize.ws_row, + winsize.ws_col); + output(line); + } + + if (all || winsize.ws_ypixel != 0 || winsize.ws_xpixel != 0) + { + sprintf(line, "%d ypixels %d xpixels", winsize.ws_ypixel, + winsize.ws_xpixel); + output(line); + } + + if (all) + { + printf("\n"); + column= 0; + } +#endif + + if (column != 0) + { + printf("\n"); + column= 0; + } +} + +int option(opt, next) +char *opt, *next; +{ + char *check; + speed_t speed; + long num; + + /* The control options. */ + + if (match(opt, "clocal")) { + termios.c_cflag |= CLOCAL; + return 0; + } + if (match(opt, "-clocal")) { + termios.c_cflag &= ~CLOCAL; + return 0; + } + + if (match(opt, "cread")) { + termios.c_cflag |= CREAD; + return 0; + } + if (match(opt, "-cread")) { + termios.c_cflag &= ~CREAD; + return 0; + } + + if (match(opt, "cs5")) { + termios.c_cflag &= ~CSIZE; + termios.c_cflag |= CS5; + return 0; + } + if (match(opt, "cs6")) { + termios.c_cflag &= ~CSIZE; + termios.c_cflag |= CS6; + return 0; + } + if (match(opt, "cs7")) { + termios.c_cflag &= ~CSIZE; + termios.c_cflag |= CS7; + return 0; + } + if (match(opt, "cs8")) { + termios.c_cflag &= ~CSIZE; + termios.c_cflag |= CS8; + return 0; + } + + if (match(opt, "cstopb")) { + termios.c_cflag |= CSTOPB; + return 0; + } + if (match(opt, "-cstopb")) { + termios.c_cflag &= ~CSTOPB; + return 0; + } + + if (match(opt, "hupcl") || match(opt, "hup")) { + termios.c_cflag |= HUPCL; + return 0; + } + if (match(opt, "-hupcl") || match(opt, "-hup")) { + termios.c_cflag &= ~HUPCL; + return 0; + } + + if (match(opt, "parenb")) { + termios.c_cflag |= PARENB; + return 0; + } + if (match(opt, "-parenb")) { + termios.c_cflag &= ~PARENB; + return 0; + } + + if (match(opt, "parodd")) { + termios.c_cflag |= PARODD; + return 0; + } + if (match(opt, "-parodd")) { + termios.c_cflag &= ~PARODD; + return 0; + } + + num= strtol(opt, &check, 10); + if (check[0] == '\0') + { + speed= long2speed(num); + if (speed == (speed_t)-1) + { + fprintf(stderr, "%s: illegal speed: '%s'\n", prog_name, opt); + return 0; + } + /* Speed OK */ + cfsetispeed(&termios, speed); + cfsetospeed(&termios, speed); + return 0; + } + + if (match(opt, "ispeed")) { + num= strtol(next, &check, 10); + if (check != '\0') + { + speed= long2speed(num); + if (speed == (speed_t)-1) + { + fprintf(stderr, "%s: illegal speed: '%s'\n", prog_name, + opt); + return 1; + } + cfsetispeed(&termios, speed); + return 1; + } + else + { + fprintf(stderr, "%s: invalid argument to ispeed: '%s'\n", + prog_name, next); + return 1; + } + } + + if (match(opt, "ospeed")) { + num= strtol(next, &check, 10); + if (check != '\0') + { + speed= long2speed(num); + if (speed == (speed_t)-1) + { + fprintf(stderr, "%s: illegal speed: '%s'\n", prog_name, + opt); + return 1; + } + cfsetospeed(&termios, speed); + return 1; + } + else + { + fprintf(stderr, "%s: invalid argument to ospeed: %s\n", + prog_name, next); + return 1; + } + } + + /* Input modes. */ + + if (match(opt, "brkint")) { + termios.c_iflag |= BRKINT; + return 0; + } + if (match(opt, "-brkint")) { + termios.c_iflag &= ~BRKINT; + return 0; + } + + if (match(opt, "icrnl")) { + termios.c_iflag |= ICRNL; + return 0; + } + if (match(opt, "-icrnl")) { + termios.c_iflag &= ~ICRNL; + return 0; + } + + if (match(opt, "ignbrk")) { + termios.c_iflag |= IGNBRK; + return 0; + } + if (match(opt, "-ignbrk")) { + termios.c_iflag &= ~IGNBRK; + return 0; + } + + if (match(opt, "igncr")) { + termios.c_iflag |= IGNCR; + return 0; + } + if (match(opt, "-igncr")) { + termios.c_iflag &= ~IGNCR; + return 0; + } + + if (match(opt, "ignpar")) { + termios.c_iflag |= IGNPAR; + return 0; + } + if (match(opt, "-ignpar")) { + termios.c_iflag &= ~IGNPAR; + return 0; + } + + if (match(opt, "inlcr")) { + termios.c_iflag |= INLCR; + return 0; + } + if (match(opt, "-inlcr")) { + termios.c_iflag &= ~INLCR; + return 0; + } + + if (match(opt, "inpck")) { + termios.c_iflag |= INPCK; + return 0; + } + if (match(opt, "-inpck")) { + termios.c_iflag &= ~INPCK; + return 0; + } + + if (match(opt, "istrip")) { + termios.c_iflag |= ISTRIP; + return 0; + } + if (match(opt, "-istrip")) { + termios.c_iflag &= ~ISTRIP; + return 0; + } + + if (match(opt, "ixoff")) { + termios.c_iflag |= IXOFF; + return 0; + } + if (match(opt, "-ixoff")) { + termios.c_iflag &= ~IXOFF; + return 0; + } + + if (match(opt, "ixon")) { + termios.c_iflag |= IXON; + return 0; + } + if (match(opt, "-ixon")) { + termios.c_iflag &= ~IXON; + return 0; + } + + if (match(opt, "parmrk")) { + termios.c_iflag |= PARMRK; + return 0; + } + if (match(opt, "-parmrk")) { + termios.c_iflag &= ~PARMRK; + return 0; + } + + if (match(opt, "ixany")) { + termios.c_iflag |= IXANY; + return 0; + } + if (match(opt, "-ixany")) { + termios.c_iflag &= ~IXANY; + return 0; + } + + /* Output modes. */ + + if (match(opt, "opost")) { + termios.c_oflag |= OPOST; + return 0; + } + if (match(opt, "-opost")) { + termios.c_oflag &= ~OPOST; + return 0; + } +#ifdef __minix + if (match(opt, "onlcr")) { + termios.c_oflag |= ONLCR; + return 0; + } + if (match(opt, "-onlcr")) { + termios.c_oflag &= ~ONLCR; + return 0; + } + + if (match(opt, "xtabs")) { + termios.c_oflag |= XTABS; + return 0; + } + if (match(opt, "-xtabs")) { + termios.c_oflag &= ~XTABS; + return 0; + } + + if (match(opt, "onoeot")) { + termios.c_oflag |= ONOEOT; + return 0; + } + if (match(opt, "-onoeot")) { + termios.c_oflag &= ~ONOEOT; + return 0; + } +#endif + + /* Local modes. */ + + if (match(opt, "echo")) { + termios.c_lflag |= ECHO; + return 0; + } + if (match(opt, "-echo")) { + termios.c_lflag &= ~ECHO; + return 0; + } + + if (match(opt, "echoe")) { + termios.c_lflag |= ECHOE; + return 0; + } + if (match(opt, "-echoe")) { + termios.c_lflag &= ~ECHOE; + return 0; + } + + if (match(opt, "echok")) { + termios.c_lflag |= ECHOK; + return 0; + } + if (match(opt, "-echok")) { + termios.c_lflag &= ~ECHOK; + return 0; + } + + if (match(opt, "echonl")) { + termios.c_lflag |= ECHONL; + return 0; + } + if (match(opt, "-echonl")) { + termios.c_lflag &= ~ECHONL; + return 0; + } + + if (match(opt, "icanon")) { + termios.c_lflag |= ICANON; + return 0; + } + if (match(opt, "-icanon")) { + termios.c_lflag &= ~ICANON; + return 0; + } + + if (match(opt, "iexten")) { + termios.c_lflag |= IEXTEN; + return 0; + } + if (match(opt, "-iexten")) { + termios.c_lflag &= ~IEXTEN; + return 0; + } + + if (match(opt, "isig")) { + termios.c_lflag |= ISIG; + return 0; + } + if (match(opt, "-isig")) { + termios.c_lflag &= ~ISIG; + return 0; + } + + if (match(opt, "noflsh")) { + termios.c_lflag |= NOFLSH; + return 0; + } + if (match(opt, "-noflsh")) { + termios.c_lflag &= ~NOFLSH; + return 0; + } + + if (match(opt, "tostop")) { + termios.c_lflag |= TOSTOP; + return 0; + } + if (match(opt, "-tostop")) { + termios.c_lflag &= ~TOSTOP; + return 0; + } + +#ifdef __minix + if (match(opt, "lflusho")) { + termios.c_lflag |= LFLUSHO; + return 0; + } + if (match(opt, "-lflusho")) { + termios.c_lflag &= ~LFLUSHO; + return 0; + } +#endif + + /* The special control characters. */ + if (match(opt, "eof")) { + set_control(VEOF, next); + return 1; + } + + if (match(opt, "eol")) { + set_control(VEOL, next); + return 1; + } + + if (match(opt, "erase")) { + set_control(VERASE, next); + return 1; + } + + if (match(opt, "intr")) { + set_control(VINTR, next); + return 1; + } + + if (match(opt, "kill")) { + set_control(VKILL, next); + return 1; + } + + if (match(opt, "quit")) { + set_control(VQUIT, next); + return 1; + } + + if (match(opt, "susp")) { + set_control(VSUSP, next); + return 1; + } + + if (match(opt, "start")) { + set_control(VSTART, next); + return 1; + } + + if (match(opt, "stop")) { + set_control(VSTOP, next); + return 1; + } +#ifdef __minix + if (match(opt, "rprnt")) { + set_control(VREPRINT, next); + return 1; + } + + if (match(opt, "lnext")) { + set_control(VLNEXT, next); + return 1; + } + + if (match(opt, "flush")) { + set_control(VDISCARD, next); + return 1; + } +#endif + + if (match(opt, "min")) { + set_min_tim(VMIN, next); + return 1; + } + + if (match(opt, "time")) { + set_min_tim(VTIME, next); + return 1; + } + + /* Special modes. */ + if (opt[0] == ':') + { + set_saved_settings(opt); + return 0; + } + + if (match(opt, "cooked") || match(opt, "raw")) { + int x = opt[0] == 'c' ? 1 : 0; + + option(x + "-icrnl", ""); /* off in raw mode, on in cooked mode */ + option(x + "-ixon", ""); + option(x + "-opost", ""); + option(x + "-onlcr", ""); + option(x + "-isig", ""); + option(x + "-icanon", ""); + option(x + "-iexten", ""); + option(x + "-echo", ""); + return 0; + } + + if (match(opt, "evenp") || match(opt, "parity")) { + option("parenb", ""); + option("cs7", ""); + option("-parodd", ""); + return 0; + } + + if (match(opt, "oddp")) { + option("parenb", ""); + option("cs7", ""); + option("parodd", ""); + return 0; + } + + if (match(opt, "-parity") || match(opt, "-evenp") || match(opt, "-oddp")) { + option("-parenb", ""); + option("cs8", ""); + return 0; + } + + if (match(opt, "nl")) { + option("icrnl", ""); + return 0; + } + + if (match(opt, "-nl")) { + option("-icrnl", ""); + option("-inlcr", ""); + option("-igncr", ""); + return 0; + } + + if (match(opt, "ek")) { + termios.c_cc[VERASE]= TERASE_DEF;; + termios.c_cc[VKILL]= TKILL_DEF;; + return 0; + } + + if (match(opt, "sane")) + { + /* Reset all terminal attributes to a sane state, except things like + * line speed and parity, because it can't be known what their sane + * values are. + */ + termios.c_iflag= (TINPUT_DEF & ~(IGNPAR|ISTRIP|INPCK)) + | (termios.c_iflag & (IGNPAR|ISTRIP|INPCK)); + termios.c_oflag= (TOUTPUT_DEF & ~(XTABS)) + | (termios.c_oflag & (XTABS)); + termios.c_cflag= (TCTRL_DEF & ~(CLOCAL|CSIZE|CSTOPB|PARENB|PARODD)) + | (termios.c_cflag & (CLOCAL|CSIZE|CSTOPB|PARENB|PARODD)); + termios.c_lflag= (TLOCAL_DEF & ~(ECHOE|ECHOK)) + | (termios.c_lflag & (ECHOE|ECHOK)); + if (termios.c_lflag & ICANON) { + termios.c_cc[VMIN]= TMIN_DEF; + termios.c_cc[VTIME]= TTIME_DEF; + } + termios.c_cc[VEOF]= TEOF_DEF; + termios.c_cc[VEOL]= TEOL_DEF; + termios.c_cc[VERASE]= TERASE_DEF; + termios.c_cc[VINTR]= TINTR_DEF; + termios.c_cc[VKILL]= TKILL_DEF; + termios.c_cc[VQUIT]= TQUIT_DEF; + termios.c_cc[VSUSP]= TSUSP_DEF; +#ifdef __minix + termios.c_cc[VREPRINT]= TREPRINT_DEF; + termios.c_cc[VLNEXT]= TLNEXT_DEF; + termios.c_cc[VDISCARD]= TDISCARD_DEF; +#endif + termios.c_cc[VSTART]= TSTART_DEF; + termios.c_cc[VSTOP]= TSTOP_DEF; + if (!(termios.c_lflag & ICANON)) { + termios.c_cc[VMIN]= TMIN_DEF; + termios.c_cc[VTIME]= TTIME_DEF; + } + return 0; + } + +#ifdef __minix + if (match(opt, "cols")) + { + num= strtol(next, &check, 0); + if (check[0] != '\0') + { + fprintf(stderr, "%s: illegal parameter to cols: '%s'\n", + prog_name, next); + return 1; + } + winsize.ws_col= num; + return 1; + } + + if (match(opt, "rows")) + { + num= strtol(next, &check, 0); + if (check[0] != '\0') + { + fprintf(stderr, "%s: illegal parameter to rows: '%s'\n", + prog_name, next); + return 1; + } + winsize.ws_row= num; + return 1; + } + + if (match(opt, "xpixels")) + { + num= strtol(next, &check, 0); + if (check[0] != '\0') + { + fprintf(stderr, "%s: illegal parameter to xpixels: '%s'\n", + prog_name, next); + return 1; + } + winsize.ws_xpixel= num; + return 1; + } + + if (match(opt, "ypixels")) + { + num= strtol(next, &check, 0); + if (check[0] != '\0') + { + fprintf(stderr, "%s: illegal parameter to ypixels: '%s'\n", + prog_name, next); + return 1; + } + winsize.ws_ypixel= num; + return 1; + } +#endif /* __minix */ + + fprintf(stderr, "%s: unknown mode: %s\n", prog_name, opt); + return 0; +} + +int match(s1, s2) +char *s1, *s2; +{ + + while (1) { + if (*s1 == 0 && *s2 == 0) return(1); + if (*s1 == 0 || *s2 == 0) return (0); + if (*s1 != *s2) return (0); + s1++; + s2++; + } +} + +void prctl(c) +char c; +{ + if (c < ' ') + printf("^%c", 'A' + c - 1); + else if (c == 0177) + printf("^?"); + else + printf("%c", c); +} + +struct s2s { + speed_t ts; + long ns; +} s2s[] = { + { B0, 0 }, + { B50, 50 }, + { B75, 75 }, + { B110, 110 }, + { B134, 134 }, + { B150, 150 }, + { B200, 200 }, + { B300, 300 }, + { B600, 600 }, + { B1200, 1200 }, + { B1800, 1800 }, + { B2400, 2400 }, + { B4800, 4800 }, + { B9600, 9600 }, + { B19200, 19200 }, + { B38400, 38400 }, +#ifdef __minix + { B57600, 57600 }, + { B115200, 115200 }, +#ifdef B230400 + { B230400, 230400 }, +#endif +#ifdef B460800 + { B460800, 460800 }, +#endif +#ifdef B921600 + { B921600, 921600 }, +#endif +#endif +}; + +speed_t long2speed(num) +long num; +{ + struct s2s *sp; + + for (sp = s2s; sp < s2s + (sizeof(s2s) / sizeof(s2s[0])); sp++) { + if (sp->ns == num) return sp->ts; + } + return -1; +} + +long speed2long(speed) +unsigned long speed; +{ + struct s2s *sp; + + for (sp = s2s; sp < s2s + (sizeof(s2s) / sizeof(s2s[0])); sp++) { + if (sp->ts == speed) return sp->ns; + } + return -1; +} + +void print_flags(flags, flag, def, string, all) +unsigned long flags; +unsigned long flag; +unsigned long def; +char *string; +int all; +{ + if (!(flags & flag)) + { + if (all || (def & flag)) + output(string); + return; + } + string++; + if (all || !(def & flag)) + output(string); +} + +void output(s) +char *s; +{ + int len; + + len= strlen(s); + if (column + len + 3 >= max_column) + { + printf("\n"); + column= 0; + } + if (column) + { + putchar(' '); + column++; + } + fputs(s, stdout); + column += len; +} + +void do_print_char(chr, def, name, all) +unsigned chr; +unsigned def; +char *name; +int all; +{ + char line[20]; + + if (!all && chr == def) + return; + +#ifdef _POSIX_VDISABLE + if (chr == _POSIX_VDISABLE) + sprintf(line, "%s = <undef>", name); + else +#endif + if (chr < ' ') + sprintf(line, "%s = ^%c", name, chr + '@'); + else if (chr == 127) + sprintf(line, "%s = ^?", name); + else + sprintf(line, "%s = %c", name, chr); + output(line); +} + +void do_print_num(num, def, name, all) +unsigned num; +unsigned def; +char *name; +int all; +{ + char line[20]; + + if (!all && num == def) + return; + sprintf(line, "%s = %u", name, num); + output(line); +} + +void set_saved_settings(opt) +char *opt; +{ + long num; + char *check; + tcflag_t c_oflag, c_cflag, c_lflag, c_iflag; + cc_t c_cc[NCCS]; + speed_t ispeed, ospeed; + int i; + + check= opt; + num= strtol(check+1, &check, 16); + if (check[0] != ':') + { + fprintf(stderr, "error in saved settings '%s'\n", opt); + return; + } + c_iflag= num; + + num= strtol(check+1, &check, 16); + if (check[0] != ':') + { + fprintf(stderr, "error in saved settings '%s'\n", opt); + return; + } + c_oflag= num; + + num= strtol(check+1, &check, 16); + if (check[0] != ':') + { + fprintf(stderr, "error in saved settings '%s'\n", opt); + return; + } + c_cflag= num; + + num= strtol(check+1, &check, 16); + if (check[0] != ':') + { + fprintf(stderr, "error in saved settings '%s'\n", opt); + return; + } + c_lflag= num; + + num= strtol(check+1, &check, 16); + if (check[0] != ':') + { + fprintf(stderr, "error in saved settings '%s'\n", opt); + return; + } + ispeed= num; + + num= strtol(check+1, &check, 16); + if (check[0] != ':') + { + fprintf(stderr, "error in saved settings '%s'\n", opt); + return; + } + ospeed= num; + + for(i=0; i<NCCS; i++) + { + num= strtol(check+1, &check, 16); + if (check[0] != ':') + { + fprintf(stderr, "error in saved settings '%s'\n", opt); + return; + } + c_cc[i]= num; + } + if (check[1] != '\0') + { + fprintf(stderr, "error in saved settings '%s'\n", opt); + return; + } + termios.c_iflag= c_iflag; + termios.c_oflag= c_oflag; + termios.c_cflag= c_cflag; + termios.c_lflag= c_lflag; + + cfsetispeed(&termios, ispeed); + cfsetospeed(&termios, ospeed); + + for(i=0; i<NCCS; i++) + termios.c_cc[i]= c_cc[i]; +} + +void set_control(option, value) +int option; +char *value; +{ + int chr; + + if (match(value, "undef") || match(value, "^-")) { +#ifdef _POSIX_VDISABLE + chr= _POSIX_VDISABLE; +#else + fprintf(stderr, + "stty: unable to set option to _POSIX_VDISABLE\n"); + return; +#endif + } else if (match(value, "^?")) + chr= '\177'; + else if (strlen(value) == 2 && value[0] == '^') { + chr= toupper(value[1]) - '@'; + if (chr < 0 || chr >= 32) { + fprintf(stderr, "stty: illegal option value: '%s'\n", + value); + return; + } + } else if (strlen(value) == 1) + chr= value[0]; + else { + fprintf(stderr, "stty: illegal option value: '%s'\n", value); + return; + } + + assert(option >= 0 && option < NCCS); + termios.c_cc[option]= chr; +} + +void set_min_tim(option, value) +int option; +char *value; +{ + long num; + char *check; + + num= strtol(value, &check, 0); + if (check[0] != '\0') { + fprintf(stderr, "stty: illegal option value: '%s'\n", value); + return; + } + + if ((cc_t)num != num) { + fprintf(stderr, "stty: illegal option value: '%s'\n", value); + return; + } + assert(option >= 0 && option < NCCS); + termios.c_cc[option]= num; +} + +/* + * $PchId: stty.c,v 1.7 2001/05/02 15:04:42 philip Exp $ + */ diff --git a/commands/simple/su.c b/commands/simple/su.c new file mode 100755 index 000000000..a58ba71bf --- /dev/null +++ b/commands/simple/su.c @@ -0,0 +1,153 @@ +/* su - become super-user Author: Patrick van Kleef */ + +#include <sys/types.h> +#include <pwd.h> +#include <grp.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <fcntl.h> +#include <time.h> +#if __minix_vmd +#include <sys/syslog.h> +#endif +#include <minix/minlib.h> + +_PROTOTYPE(int main, (int argc, char **argv)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + register char *name, *password; + char *shell, sh0[100]; + char from_user[8+1], from_shell[100]; + register struct passwd *pwd; + char USER[20], LOGNAME[25], HOME[100], SHELL[100]; + char *envv[20], **envp; + int smallenv; + char *p; + int super; + int loginshell; +#if __minix_vmd + gid_t groups[NGROUPS_MAX]; + int ngroups; + int g; +#endif + + smallenv = 0; + loginshell = 0; + if (argc > 1 && (strcmp(argv[1], "-") == 0 || strcmp(argv[1], "-e") == 0)) { + if (argv[1][1] == 0) + loginshell= 1; /* 'su -' reads .profile */ + argv[1] = argv[0]; + argv++; + argc--; + smallenv = 1; /* Use small environment. */ + } + if (argc > 1) { + if (argv[1][0] == '-') { + fprintf(stderr, + "Usage: su [-[e]] [user [shell-arguments ...]]\n"); + exit(1); + } + name = argv[1]; + argv[1] = argv[0]; + argv++; + } else { + name = "root"; + } + + if ((pwd = getpwuid(getuid())) == 0) { + fprintf(stderr, "You do not exist\n"); + exit(1); + } + strncpy(from_user, pwd->pw_name, 8); + from_user[8]= 0; + strncpy(from_shell, pwd->pw_shell[0] == '\0' ? "/bin/sh" : pwd->pw_shell, + sizeof(from_shell)-1); + from_shell[sizeof(from_shell)-1]= 0; + + if ((pwd = getpwnam(name)) == 0) { + fprintf(stderr, "Unknown id: %s\n", name); + exit(1); + } + super = 0; + if (getgid() == 0) super = 1; +#if __minix_vmd + ngroups = getgroups(NGROUPS_MAX, groups); + for (g = 0; g < ngroups; g++) if (groups[g] == 0) super = 1; +#endif + + if (!super && strcmp(pwd->pw_passwd, crypt("", pwd->pw_passwd)) != 0) { +#if __minix_vmd + openlog("su", 0, LOG_AUTH); +#endif + password = getpass("Password:"); + if (password == 0 + || strcmp(pwd->pw_passwd, crypt(password, pwd->pw_passwd)) != 0) { + if (password != 0 && *password != 0) { +#if __minix_vmd + syslog(LOG_WARNING, "su %s failed for %s", + name, from_user); +#endif + } + fprintf(stderr, "Sorry\n"); + exit(2); + } +#if __minix_vmd + syslog(LOG_NOTICE, "su %s succeeded for %s", name, from_user); + closelog(); +#endif + } + +#if __minix_vmd + initgroups(pwd->pw_name, pwd->pw_gid); +#endif + setgid(pwd->pw_gid); + setuid(pwd->pw_uid); + if (loginshell) { + shell = pwd->pw_shell[0] == '\0' ? "/bin/sh" : pwd->pw_shell; + } else { + if ((shell = getenv("SHELL")) == NULL) shell = from_shell; + } + if ((p= strrchr(shell, '/')) == 0) p= shell; else p++; + sh0[0]= '-'; + strcpy(loginshell ? sh0+1 : sh0, p); + argv[0]= sh0; + + if (smallenv) { + envp = envv; + *envp++ = "PATH=:/bin:/usr/bin", + strcpy(USER, "USER="); + strcpy(USER + 5, name); + *envp++ = USER; + strcpy(LOGNAME, "LOGNAME="); + strcpy(LOGNAME + 8, name); + *envp++ = LOGNAME; + strcpy(SHELL, "SHELL="); + strcpy(SHELL + 6, shell); + *envp++ = SHELL; + strcpy(HOME, "HOME="); + strcpy(HOME + 5, pwd->pw_dir); + *envp++ = HOME; + if ((p = getenv("TERM")) != NULL) { + *envp++ = p - 5; + } + if ((p = getenv("TERMCAP")) != NULL) { + *envp++ = p - 8; + } + if ((p = getenv("TZ")) != NULL) { + *envp++ = p - 3; + } + *envp = NULL; + (void) chdir(pwd->pw_dir); + execve(shell, argv, envv); + } else { + execv(shell, argv); + } + fprintf(stderr, "No shell\n"); + return(3); +} diff --git a/commands/simple/sum.c b/commands/simple/sum.c new file mode 100755 index 000000000..7b0d6699d --- /dev/null +++ b/commands/simple/sum.c @@ -0,0 +1,121 @@ +/* sum - checksum a file Author: Martin C. Atkins */ + +/* + * This program was written by: + * Martin C. Atkins, + * University of York, + * Heslington, + * York. Y01 5DD + * England + * and is released into the public domain, on the condition + * that this comment is always included without alteration. + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <minix/minlib.h> +#include <stdio.h> + +#define BUFFER_SIZE (512) + +int rc = 0; + +char *defargv[] = {"-", 0}; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void error, (char *s, char *f)); +_PROTOTYPE(void sum, (int fd, char *fname)); +_PROTOTYPE(void putd, (int number, int fw, int zeros)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + register int fd; + + if (*++argv == 0) argv = defargv; + for (; *argv; argv++) { + if (argv[0][0] == '-' && argv[0][1] == '\0') + fd = 0; + else + fd = open(*argv, O_RDONLY); + + if (fd == -1) { + error("can't open ", *argv); + rc = 1; + continue; + } + sum(fd, (argc > 2) ? *argv : (char *) 0); + if (fd != 0) close(fd); + } + return(rc); +} + +void error(s, f) +char *s, *f; +{ + + std_err("sum: "); + std_err(s); + + if (f) std_err(f); + std_err("\n"); +} + +void sum(fd, fname) +int fd; +char *fname; +{ + char buf[BUFFER_SIZE]; + register int i, n; + long size = 0; + unsigned crc = 0; + unsigned tmp, blks; + + while ((n = read(fd, buf, BUFFER_SIZE)) > 0) { + for (i = 0; i < n; i++) { + crc = (crc >> 1) + ((crc & 1) ? 0x8000 : 0); + tmp = buf[i] & 0377; + crc += tmp; + crc &= 0xffff; + size++; + } + } + + if (n < 0) { + if (fname) + error("read error on ", fname); + else + error("read error", (char *) 0); + rc = 1; + return; + } + putd(crc, 5, 1); + blks = (size + (long) BUFFER_SIZE - 1L) / (long) BUFFER_SIZE; + putd(blks, 6, 0); + if (fname) printf(" %s", fname); + printf("\n"); +} + +void putd(number, fw, zeros) +int number, fw, zeros; +{ +/* Put a decimal number, in a field width, to stdout. */ + + char buf[10]; + int n; + unsigned num; + + num = (unsigned) number; + for (n = 0; n < fw; n++) { + if (num || n == 0) { + buf[fw - n - 1] = '0' + num % 10; + num /= 10; + } else + buf[fw - n - 1] = zeros ? '0' : ' '; + } + buf[fw] = 0; + printf("%s", buf); +} diff --git a/commands/simple/swapfs.c b/commands/simple/swapfs.c new file mode 100755 index 000000000..10bd86c23 --- /dev/null +++ b/commands/simple/swapfs.c @@ -0,0 +1,1048 @@ +/* swapfs - swap a Minix file system Author: Niels C. Willems */ + + +/* $Id$ */ + +/* Swapfs, a program to convert V1 or V2 Minix file systems from big endian + byte order to little endian and vv. + + Some examples: + swapfs -v disk.01 ! only show verbose information. + swapfs /dev/fd0 | compress > fd0r.Z ! convert and compress filesystem. + swapfs -v fileA fileA ! read, convert and write the same filesystem. + + This program uses one byte of heap memory for each data block (1Kbytes) + in the file system, so with Minix-PC 16-bit you can't swap file systems + bigger than about 32 Mbytes + + Be careful with 'swapfs fileA fileA'. If the program aborts e.g. by + user interrupt, power failure or an inconsistent file system, you + better have a backup of fileA + + This program only converts directories and indirect blocks of files + that are in use. Converting indirect blocks or directories of deleted + files is hard and not yet done. + + If you have a (1.6.xx, xx < 18) version of Minix that supports the + mounting of reversed file systems always mount them read-only and + avoid any attemp to modify them (mkdir, open, creat) too! + These problems have been fixed in Minix 1.6.18. + + In this version you can get some more information about the + file system with the -d (debug) flag. + + Please send your bug reports or ideas to ncwille@cs.vu.nl + */ + + +#define _POSIX_SOURCE 1 + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +#include <assert.h> + +#if __STDC__ == 1 +#define _PROTOTYPE(function, params) function params +#else +#define _PROTOTYPE(function, params) function() +#endif + +#define BLOCK_SIZE 1024 + +#define BOOT_BLOCK_OFF (blockn_t) 0 +#define SUPER_BLOCK_OFF (blockn_t) 1 + +#define V1_MAGIC 0x137F +#define V2_MAGIC 0x2468 +#define NINODES_OFFSET 0 +#define V1_ZONES_OFFSET 2 +#define IMAP_BLOCKS_OFFSET 4 +#define ZMAP_BLOCKS_OFFSET 6 +#define FIRSTDATAZONE_OFFSET 8 +#define LOG_ZONE_SIZE_OFFSET 10 +#define MAGIC_OFFSET 16 +#define V2_ZONES_OFFSET 20 + + +#define NR_DIRECT_ZONES 7 +#define V1_NR_TZONES 9 +#define V2_NR_TZONES 10 +#define V1_INODE_SIZE 32 +#define V2_INODE_SIZE 64 + +#define INODE1_MODE_OFF 0 +#define INODE1_SIZE_OFF 4 +#define INODE1_DIRECT_OFF 14 +#define INODE1_IND1_OFF 28 +#define INODE1_IND2_OFF 30 + +#define INODE2_MODE_OFF 0 +#define INODE2_SIZE_OFF 8 +#define INODE2_DIRECT_OFF 24 +#define INODE2_IND1_OFF 52 +#define INODE2_IND2_OFF 56 +#define INODE2_IND3_OFF 60 + +#define INODE_MODE_MASK 0xf000 /* file type mask */ +#define INODE_DIR_MODE 0x4000 /* directory */ +#define INODE_BLK_SPECIAL_MODE 0x6000 /* block special */ +#define INODE_CHR_SPECIAL_MODE 0x2000 /* character special */ + +#define T_MASK 0x1c +#define T_UNKNOWN 0x00 +#define T_MAYBE_OLD_DIR 0x04 +#define T_OLD_NON_DIR 0x08 +#define T_DIR 0x0c +#define T_NON_DIR 0x10 + +#define INDIRECT_MASK 0x03 + +#define IND_PROCESSED_BIT 0x20 /* set when all blocks in ind block are + * marked */ +#define IND_CONFLICT_BIT 0x40 +#define TYPE_CONFLICT_BIT 0x80 + +#define DIR_ENTRY_SIZE 16 + +typedef enum { + Unused_zone, Old_zone, In_use_zone +} class_t; + +typedef unsigned long blockn_t; +typedef unsigned int inodesn_t; + +typedef struct { + inodesn_t ninodes; /* # usable inodes on the minor device */ + blockn_t imap_blocks; /* # of blocks used by inode bit map */ + blockn_t zmap_blocks; /* # of blocks used by zone bit map */ + blockn_t firstdatazone; /* number of first data zone */ + int log_zone_size; /* log2 of blocks/zone */ + blockn_t zones; /* number of zones */ + + int version; /* file system version */ + inodesn_t inodes_per_block; + blockn_t first_imap_block; + blockn_t first_zmap_block; + blockn_t first_inode_block; /* number of first block with inodes */ + size_t dzmap_size; /* # of data zone blocks */ +} super_t; + + +typedef struct { /* summary of inode */ + long size; /* current file size in bytes */ + blockn_t direct[NR_DIRECT_ZONES]; /* block numbers for direct, + * ind, ... */ + blockn_t ind1; /* single indirect block number */ + blockn_t ind2; /* double indirect block number */ + blockn_t ind3; /* triple indirect block number */ + int ztype; /* type of zones that belong to this inode */ +} inode_t; + +static char rcsid[] = "$Id$"; + +static int super_format[] = {2, 2, 2, 2, 2, 2, 4, 2, 2, 4, 0}; +static int inode1_format[] = {2, 2, 4, 4, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0}; +static int inode2_format[] = {2, 2, 2, 2, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, + 4, 4, 0}; + +static char *ind_str[4] = {"direct", "single indirect", + "double indirect", "triple indirect"}; + +static int big_endian_fs; /* set in init_super(), 1 iff file system has + * big endian byte order */ +static int verbose_flag; +static int debug_flag; +static int test_flag; + +typedef unsigned char *dzmap_t; + + +int _PROTOTYPE(main, (int argc, char *argv[])); +static void _PROTOTYPE(parse_args_init_io, (int argc, char *argv[])); +static void _PROTOTYPE(rw_boot, (void)); +static void _PROTOTYPE(rw_init_super, (super_t * sp)); +static void _PROTOTYPE(init_dzmap, (dzmap_t * dzmap_ptr, size_t dzmap_size)); +static void _PROTOTYPE(rw_ibmap, (super_t super)); +static void _PROTOTYPE(rw_zbmap, (super_t super)); +static void _PROTOTYPE(print_stat, (dzmap_t dzmap, super_t super)); +static void _PROTOTYPE(p1_rw_inodes, (dzmap_t dzmap, super_t super)); +static void _PROTOTYPE(rd_indirects, (dzmap_t dzmap, super_t super, int ind, + class_t required_class)); +static void _PROTOTYPE(rw_data_zones, (dzmap_t dzmap, super_t super)); + +static int _PROTOTYPE(read_block, (char *buf, blockn_t offset)); +static void _PROTOTYPE(write_block, (char *buf)); +static int _PROTOTYPE(convcpy, (char *dst, char *src, int *format)); +static void _PROTOTYPE(conv2_blkcpy, (char *dst, char *src)); +static void _PROTOTYPE(conv4_blkcpy, (char *dst, char *src)); +static void _PROTOTYPE(conv2cpy, (char *dst, char *src)); +static int _PROTOTYPE(inode_size, (int version)); + +static void _PROTOTYPE(init_super, (super_t * sp, char *buf)); +static void _PROTOTYPE(get_inode, (inode_t * ip, char *buf, int version)); +static int _PROTOTYPE(check_inode, (inode_t inode, super_t super)); +static int _PROTOTYPE(was_blk_special, (inode_t inode)); +static int _PROTOTYPE(check_blk_number, (blockn_t num, super_t super)); +static void _PROTOTYPE(cw_inode_block, (char *buf, inodesn_t ninodes, + int version)); +static void _PROTOTYPE(proc_ind, (dzmap_t dzmap, size_t curr_ind, + char *buf, super_t super)); +static void _PROTOTYPE(cw_dir_block, (char *buf)); +static void _PROTOTYPE(dzmap_add_inode, (dzmap_t dzmap, inode_t inode, + super_t super)); +static void _PROTOTYPE(dz_update, (dzmap_t dzmap, blockn_t blknum, + int new_indnum, int new_ztype, super_t super)); +static class_t _PROTOTYPE(ztype_class, (int ztype)); + +static unsigned int _PROTOTYPE(two_bytes, (char buf[2])); +static long _PROTOTYPE(four_bytes, (char buf[4])); + +static void _PROTOTYPE(fail, (char *string)); +static void _PROTOTYPE(usage, (char *arg0)); + + +int main(argc, argv) +int argc; +char *argv[]; +{ + super_t super; + dzmap_t dzmap; + + parse_args_init_io(argc, argv); + rw_boot(); + rw_init_super(&super); + init_dzmap(&dzmap, super.dzmap_size); + rw_ibmap(super); + rw_zbmap(super); + p1_rw_inodes(dzmap, super); + + rd_indirects(dzmap, super, 3, In_use_zone); + rd_indirects(dzmap, super, 2, In_use_zone); + rd_indirects(dzmap, super, 1, In_use_zone); + if (verbose_flag) putc('\n', stderr); + + print_stat(dzmap, super); + rw_data_zones(dzmap, super); + return 0; +} + + +static void parse_args_init_io(argc, argv) +int argc; +char *argv[]; +{ + char *str; + struct stat buf; + ino_t src_ino; + int i; + + debug_flag = 0; + verbose_flag = 0; + test_flag = 0; + + for (i = 1; i < argc; i++) { + str = argv[i]; + if (*str != '-') break; + switch (*++str) { + case 'v': verbose_flag = 1; break; + case 'd': + debug_flag = 1; + verbose_flag = 1; + break; + case 't': test_flag = 1; break; + default: usage(argv[0]); + } + } + if ((argc - i == 0 && isatty(0)) || (argc - i) > 2) usage(argv[0]); + + if (argc - i > 0) { + (void) close(0); + if (open(argv[i], O_RDONLY) != 0) { + fprintf(stderr, "Can't open input file %s", argv[i]); + fail(""); + } + } + if (isatty(1) || argc - i == 2) { + if (argc - i < 2) + test_flag = 1; + else { + i++; + (void) close(1); + (void) fstat(0, &buf); + src_ino = buf.st_ino; + if (stat(argv[i], &buf) == 0 && src_ino == buf.st_ino) { + /* Src and dest are the same */ + if (open(argv[i], O_WRONLY) != 1) { + fprintf(stderr, "Can't open output file %s", argv[i]); + fail(""); + } + } else if (creat(argv[i], 0644) != 1) { + fprintf(stderr, "Can't creat output file %s", argv[i]); + fail(""); + } + } + } +} + + +static void rw_boot() +{ + char buf[BLOCK_SIZE]; + + if (read_block(buf, BOOT_BLOCK_OFF) != BLOCK_SIZE) + fail("Can't read bootblock"); + write_block(buf); +} + + +static void rw_init_super(sp) +super_t *sp; +{ + char ibuf[BLOCK_SIZE], obuf[BLOCK_SIZE]; + + if (read_block(ibuf, SUPER_BLOCK_OFF) != BLOCK_SIZE) + fail("Can't read superblock"); + + init_super(sp, ibuf); + + memcpy(obuf, ibuf, (size_t) BLOCK_SIZE); /* preserve 'unused' data */ + (void) convcpy(obuf, ibuf, super_format); + + write_block(obuf); +} + + +static void init_dzmap(dzmap_ptr, dzmap_size) +dzmap_t *dzmap_ptr; +size_t dzmap_size; +{ + if ((*dzmap_ptr = (dzmap_t) malloc(dzmap_size)) == (dzmap_t) NULL) + fail("Not enough space for data zone map"); + memset(*dzmap_ptr, '\0', (size_t) dzmap_size); +} + + +static void rw_ibmap(super) +super_t super; +{ + char ibuf[BLOCK_SIZE], obuf[BLOCK_SIZE]; + blockn_t i; + + for (i = 0; i < super.imap_blocks; i++) { + if (read_block(ibuf, super.first_imap_block + i) != BLOCK_SIZE) + fail("Can't read inode bit map"); + conv2_blkcpy(obuf, ibuf); + write_block(obuf); + } +} + + +static void rw_zbmap(super) +super_t super; +{ + char ibuf[BLOCK_SIZE], obuf[BLOCK_SIZE]; + blockn_t i; + + for (i = 0; i < super.zmap_blocks; i++) { + if (read_block(ibuf, super.first_zmap_block + i) != BLOCK_SIZE) + fail("Can't read zone bit map"); + conv2_blkcpy(obuf, ibuf); + write_block(obuf); + } +} + + +static void p1_rw_inodes(dzmap, super) +dzmap_t dzmap; +super_t super; +{ + char buf[BLOCK_SIZE], *buf_ptr; + inodesn_t i, num_inodes; + blockn_t next_block; + inode_t inode; + + + next_block = super.first_inode_block; + + for (i = 1; i <= super.ninodes; i++) { + if ((i - 1) % super.inodes_per_block == 0) { + if (read_block(buf, next_block) != BLOCK_SIZE) + fail("read failed in inode block"); + buf_ptr = buf; + next_block++; + num_inodes = super.ninodes + 1 - i; + if (num_inodes > super.inodes_per_block) + num_inodes = super.inodes_per_block; + cw_inode_block(buf, num_inodes, super.version); + } + get_inode(&inode, buf_ptr, super.version); + dzmap_add_inode(dzmap, inode, super); + buf_ptr += inode_size(super.version); + } +} + + +static void print_stat(dzmap, super) +dzmap_t dzmap; +super_t super; +{ + size_t i; + register unsigned char dz; + int both_conflict = 0, ind_conflict = 0, type_conflict = 0, unreferenced = 0; + int not_in_use = 0; + + if (!verbose_flag) return; + + for (i = 0; i < super.dzmap_size; i++) { + dz = dzmap[i]; + if (dz & IND_CONFLICT_BIT && dz & TYPE_CONFLICT_BIT) + both_conflict++; + else if (dz & IND_CONFLICT_BIT) + ind_conflict++; + else if (dz & TYPE_CONFLICT_BIT) + type_conflict++; + + if (dz == 0) unreferenced++; + if (ztype_class(dz & T_MASK) < In_use_zone) not_in_use++; + + } + if (debug_flag) { + fprintf(stderr, "%5d zone blocks with conflicting indir.\n", + ind_conflict); + fprintf(stderr, "%5d zone blocks with conflicting types.\n", + type_conflict); + fprintf(stderr, "%5d zone blocks with conflicting types and indir.\n", + both_conflict); + fprintf(stderr, "%5d zone blocks never referenced.\n", unreferenced); + } + fprintf(stderr, "%5d zone blocks not in use.\n", not_in_use); + putc('\n', stderr); +} + + +static void rd_indirects(dzmap, super, ind, required_class) +dzmap_t dzmap; +super_t super; +int ind; +class_t required_class; +{ + size_t i; + int ind_cnt; + off_t dz_offset; + char buf[BLOCK_SIZE]; + + dz_offset = super.firstdatazone; + ind_cnt = 0; + for (i = 0; i < super.dzmap_size; i++) { + if (ztype_class(dzmap[i] & T_MASK) != required_class || + (dzmap[i] & INDIRECT_MASK) != ind || + (dzmap[i] & IND_PROCESSED_BIT)) + continue; + + ind_cnt++; + if (read_block(buf, dz_offset + i) != BLOCK_SIZE) { + fprintf(stderr, "Can't read %s block", ind_str[ind]); + fail(""); + } + proc_ind(dzmap, i, buf, super); + } + if ((verbose_flag && ind_cnt > 0) || debug_flag) + fprintf(stderr, "%5d %s zone blocks.\n", ind_cnt, ind_str[ind]); +} + + +static void rw_data_zones(dzmap, super) +dzmap_t dzmap; +super_t super; +{ + char ibuf[BLOCK_SIZE], obuf[BLOCK_SIZE]; + size_t i; + int ztype, ind, last_read; + off_t dz_offset; + + dz_offset = super.firstdatazone; + for (i = 0; i < super.dzmap_size; i++) { + last_read = read_block(ibuf, dz_offset + i); + if (last_read != BLOCK_SIZE) break; + + ind = dzmap[i] & INDIRECT_MASK; + if (ind == 0) { + ztype = dzmap[i] & T_MASK; + if (ztype == T_DIR) + cw_dir_block(ibuf); + else + write_block(ibuf); + } else { + if (super.version == 1) + conv2_blkcpy(obuf, ibuf); + else + conv4_blkcpy(obuf, ibuf); + write_block(obuf); + } + if (verbose_flag && i && i % 1024 == 0) { + fprintf(stderr, "."); + fflush(stderr); + } + } + if (verbose_flag && i > 1024) putc('\n', stderr); + + if (last_read != BLOCK_SIZE) for (; i < super.dzmap_size; i++) + if (ztype_class(dzmap[i] & T_MASK) == In_use_zone) + fail("Can't read data zone"); +} + + +static int read_block(buf, offset) +char *buf; +blockn_t offset; +{ + static blockn_t curr_offset = 0; + int bytes; + + if (offset != curr_offset) { + if (lseek(0, (off_t) offset * BLOCK_SIZE, 0) == -1) + fail("lseek failed on input file"); + curr_offset = offset; + } + bytes = read(0, buf, BLOCK_SIZE); + if (bytes < 0) fail("read failed on input file"); + + curr_offset += bytes; + + return bytes; +} + + +static void write_block(buf) +char *buf; +{ + if (test_flag) return; + + if (write(1, buf, BLOCK_SIZE) != BLOCK_SIZE) + fail("write failed on output file"); +} + + +static int convcpy(dst, src, format) +char *dst; +char *src; +int *format; +{ + char *old_src = src; + register char tmp; + int i; + + for (i = 0; format[i] > 0; i++) { + switch (format[i]) { + case 1: *dst++ = *src++; break; + case 2: + tmp = *src++; + *dst++ = *src++; + *dst++ = tmp; + break; + case 4: + tmp = src[0]; + dst[0] = src[3]; + dst[3] = tmp; + tmp = src[1]; + dst[1] = src[2]; + dst[2] = tmp; + src += 4; + dst += 4; + break; + default: + fail("wrong format array for convcpy"); + } + } + return(src - old_src); +} + + +static void conv2_blkcpy(dst, src) +char *dst; +char *src; +{ + int i; + register char tmp; + + for (i = 0; i < BLOCK_SIZE; i += 2) { + tmp = *src++; + *dst++ = *src++; + *dst++ = tmp; + } +} + + +static void conv4_blkcpy(dst, src) +char *dst; +char *src; +{ + int i; + register char tmp; + + for (i = 0; i < BLOCK_SIZE; i += 4) { + tmp = src[0]; + dst[0] = src[3]; + dst[3] = tmp; + + tmp = src[1]; + dst[1] = src[2]; + dst[2] = tmp; + + src += 4; + dst += 4; + } +} + + +static void conv2cpy(dst, src) +char *dst; +char *src; +{ + register char tmp; + tmp = *src++; + *dst++ = *src++; + *dst++ = tmp; +} + + +static int inode_size(version) +int version; +{ + return(version == 1) ? V1_INODE_SIZE : V2_INODE_SIZE; +} + + +static void init_super(sp, buf) +super_t *sp; +char *buf; +{ + int magic; + long imapblks, zmapblks; + + big_endian_fs = 0; /* guess the file system is little endian */ + magic = two_bytes(buf + MAGIC_OFFSET); + + if (magic != V1_MAGIC && magic != V2_MAGIC) { + big_endian_fs = 1; + magic = two_bytes(buf + MAGIC_OFFSET); + } + switch (magic) { + case V1_MAGIC: sp->version = 1; break; + case V2_MAGIC: sp->version = 2; break; + default: fail("Not a Minix file system"); +} + + if (verbose_flag) fprintf(stderr, "\nVersion = V%d, %s endian.\n", + sp->version, big_endian_fs ? "big" : "little"); + + sp->ninodes = two_bytes(buf + NINODES_OFFSET); + imapblks = two_bytes(buf + IMAP_BLOCKS_OFFSET); + sp->imap_blocks = imapblks; + zmapblks = two_bytes(buf + ZMAP_BLOCKS_OFFSET); + sp->zmap_blocks = zmapblks; + sp->firstdatazone = two_bytes(buf + FIRSTDATAZONE_OFFSET); + sp->log_zone_size = two_bytes(buf + LOG_ZONE_SIZE_OFFSET); + + if (sp->version == 1) + sp->zones = two_bytes(buf + V1_ZONES_OFFSET); + else + sp->zones = four_bytes(buf + V2_ZONES_OFFSET); + + sp->inodes_per_block = BLOCK_SIZE / inode_size(sp->version); + + if (imapblks < 0 || zmapblks < 0 || sp->ninodes < 1 || sp->zones < 1) + fail("Bad superblock"); + + + if (sp->log_zone_size != 0) + fail("Can't swap file systems with different zone and block sizes"); + + sp->first_imap_block = SUPER_BLOCK_OFF + 1; + sp->first_zmap_block = sp->first_imap_block + sp->imap_blocks; + sp->first_inode_block = sp->first_zmap_block + sp->zmap_blocks; + + sp->dzmap_size = sp->zones - sp->firstdatazone; + if (verbose_flag) { + fprintf(stderr, "nzones = %ld, ", sp->zones); + fprintf(stderr, "ninodes = %u, ", sp->ninodes); + fprintf(stderr, "first data zone = %ld.\n\n", sp->firstdatazone); + } +} + + +static void get_inode(ip, buf, version) +inode_t *ip; +char *buf; +int version; +{ + int i; + int mode; + + if (version == 1) { + mode = two_bytes(buf + INODE1_MODE_OFF); + ip->size = four_bytes(buf + INODE1_SIZE_OFF); + ip->ind1 = two_bytes(buf + INODE1_IND1_OFF); + ip->ind2 = two_bytes(buf + INODE1_IND2_OFF); + ip->ind3 = 0; + for (i = 0; i < NR_DIRECT_ZONES; i++) + ip->direct[i] = two_bytes(buf + INODE1_DIRECT_OFF + 2 * i); + } else { + mode = two_bytes(buf + INODE2_MODE_OFF); + ip->size = four_bytes(buf + INODE2_SIZE_OFF); + ip->ind1 = four_bytes(buf + INODE2_IND1_OFF); + ip->ind2 = four_bytes(buf + INODE2_IND2_OFF); + ip->ind3 = four_bytes(buf + INODE2_IND3_OFF); + for (i = 0; i < NR_DIRECT_ZONES; i++) + ip->direct[i] = four_bytes(buf + INODE2_DIRECT_OFF + 4 * i); + } + + if (mode == 0) { + if (ip->size % DIR_ENTRY_SIZE == 0) + ip->ztype = T_MAYBE_OLD_DIR; + else + ip->ztype = T_OLD_NON_DIR; + if (was_blk_special(*ip)) ip->size = 0; + } else { + mode = mode & INODE_MODE_MASK; + if (mode == INODE_BLK_SPECIAL_MODE || mode == INODE_CHR_SPECIAL_MODE) + ip->size = 0; /* prevent the use of the block numbers. */ + ip->ztype = (mode == INODE_DIR_MODE) ? T_DIR : T_NON_DIR; + } +} + + +static int check_inode(inode, super) +inode_t inode; +super_t super; +{ + int i; + + for (i = 0; i < NR_DIRECT_ZONES; i++) + if (!check_blk_number(inode.direct[i], super)) return 0; + + return(check_blk_number(inode.ind1, super) && + check_blk_number(inode.ind2, super) && + check_blk_number(inode.ind3, super)); +} + + +static int check_blk_number(num, super) +blockn_t num; +super_t super; +{ + if (num == 0 || (num >= super.firstdatazone && num < super.zones)) + return 1; + + fprintf(stderr, "warning bad block number %ld in inode.\n", num); + return 0; +} + + +static int was_blk_special(inode) +inode_t inode; +{ + int i, result; + blockn_t block_size; + + if (inode.size % BLOCK_SIZE || inode.ind1) return 0; + block_size = inode.size / BLOCK_SIZE; + + for (i = NR_DIRECT_ZONES - 1; i >= 0; i--) + if (inode.direct[i] != 0) break; + + result = (i < 1 && block_size > i + 1); + + if (debug_flag && result) { + fprintf(stderr, "old block special file detected (slot = %d).\n", i); + } + return result; +} + + +static void cw_inode_block(buf, ninodes, version) +char *buf; +inodesn_t ninodes; +int version; +{ + char output_buf[BLOCK_SIZE]; + char *src, *dst; + inodesn_t i; + int cnt, free_bytes; + int *format; + + src = buf; + dst = output_buf; + + format = (version == 1) ? inode1_format : inode2_format; + for (i = 0; i < ninodes; i++) { + cnt = convcpy(dst, src, format); + src += cnt; + dst += cnt; + } + + assert(cnt == inode_size(version)); + + free_bytes = BLOCK_SIZE - (src - buf); + assert(free_bytes >= 0); + if (verbose_flag && free_bytes > 0) { + /* There is a small change that the last free inode has no + * matching bit in the last inode bit map block: e.g. if + * sp->ninodes == 8191. */ + fprintf(stderr, "%5d bytes (%d inodes) free in last inode block.\n", + free_bytes, free_bytes / inode_size(version)); + memcpy(dst, src, (size_t) free_bytes); + } + write_block(output_buf); +} + + +static void proc_ind(dzmap, curr_ind, buf, super) +dzmap_t dzmap; +size_t curr_ind; +char *buf; +super_t super; +{ + int indnum, i, ztype; + int word_size; /* size of zone block number in ind. block in + * bytes */ + unsigned char dz, tmp_dz; + blockn_t blk, ind_blk; + int bad_range = 0, hidden_zero = 0, zero_flag = 0, expired = 0; + size_t blk_index; + + dz = dzmap[curr_ind]; + indnum = dz & INDIRECT_MASK; + ztype = dz & T_MASK; + ind_blk = curr_ind + super.firstdatazone; + + word_size = (super.version == 1) ? 2 : 4; + assert(indnum > 0); + + for (i = 0; i < BLOCK_SIZE; i += word_size) { + if (word_size == 2) + blk = two_bytes(buf + i); + else + blk = four_bytes(buf + i); + + if (blk == 0) + zero_flag = 1; + else if (blk < super.firstdatazone || blk >= super.zones) + bad_range = 1; + else { + if (zero_flag) hidden_zero = 1; + blk_index = blk - super.firstdatazone; + tmp_dz = dzmap[blk_index]; + if (ztype_class(tmp_dz & T_MASK) == In_use_zone) expired = 1; + } + + } + + if (ztype_class(ztype) == In_use_zone) { + if (bad_range) { + fprintf(stderr, "%s zone block contains ", ind_str[indnum]); + fail("illegal value"); + } + if ((ztype == T_DIR || indnum > 1) && hidden_zero) { + fprintf(stderr, "WARNING: %s zone block %ld contains ", + ind_str[indnum], ind_blk); + fprintf(stderr, "unexpected zero block numbers\n"); + } + } else { + if (expired) { + dzmap[curr_ind] &= ~(INDIRECT_MASK & IND_CONFLICT_BIT); + return; + } + + /* Not yet implemented. :-( if (bad_range || (indnum > 1 && + * hidden_zero) || equal_values(buf, super.version ) { } */ + } + + for (i = 0; i < BLOCK_SIZE; i += word_size) { + if (word_size == 2) + blk = two_bytes(buf + i); + else + blk = four_bytes(buf + i); + + if (blk == 0) continue; + blk_index = blk - super.firstdatazone; + tmp_dz = dzmap[blk_index]; + if (ztype_class(tmp_dz & T_MASK) == In_use_zone) { /* trouble */ + if ((tmp_dz & INDIRECT_MASK) == indnum - 1 && + (tmp_dz & T_MASK) == ztype) + fprintf(stderr, "WARNING: %s zone block %ld used more \ +than once\n", ind_str[indnum - 1], blk); + else { + fprintf(stderr, "Block %ld used more than ", blk); + fail("once with different types"); + } + } + dzmap[blk_index] = (dz & ~INDIRECT_MASK) | (indnum - 1); + } + dzmap[curr_ind] |= IND_PROCESSED_BIT; +} + + +static void cw_dir_block(buf) +char *buf; +{ + char output_buf[BLOCK_SIZE]; + int ino, i, old_ino_offset; + + memcpy(output_buf, buf, BLOCK_SIZE); + + for (i = 0; i < BLOCK_SIZE; i += DIR_ENTRY_SIZE) { + ino = two_bytes(buf + i); + if (ino == 0) { + old_ino_offset = i + DIR_ENTRY_SIZE - 2; + conv2cpy(output_buf + old_ino_offset, buf + old_ino_offset); + } else + conv2cpy(output_buf + i, buf + i); + } + write_block(output_buf); +} + + +static void dzmap_add_inode(dzmap, inode, super) +dzmap_t dzmap; +inode_t inode; +super_t super; +{ + int i; + + if (inode.size == 0 || !check_inode(inode, super)) return; + + for (i = 0; i < NR_DIRECT_ZONES; i++) + dz_update(dzmap, inode.direct[i], 0, inode.ztype, super); + + dz_update(dzmap, inode.ind1, 1, inode.ztype, super); + dz_update(dzmap, inode.ind2, 2, inode.ztype, super); + dz_update(dzmap, inode.ind3, 3, inode.ztype, super); +} + + +static void dz_update(dzmap, blknum, new_indnum, new_ztype, super) +dzmap_t dzmap; +blockn_t blknum; +int new_indnum; +int new_ztype; +super_t super; +{ + size_t dznum; + int old_indnum; + int old_ztype; + unsigned char *dz; + char new_dz; + + + if (blknum == 0) return; + + dznum = (size_t) (blknum - super.firstdatazone); + + dz = &dzmap[dznum]; + old_indnum = *dz & INDIRECT_MASK; + old_ztype = *dz & T_MASK; + + new_dz = new_ztype | new_indnum; + + if (ztype_class(new_ztype) > ztype_class(old_ztype)) { + *dz = new_dz; + return; + } else if (ztype_class(new_ztype) < ztype_class(old_ztype)) + return; + + /* Collision: old and new have the same class */ + + if (ztype_class(old_ztype) == In_use_zone) { /* trouble */ + if (new_indnum == old_indnum && new_ztype == old_ztype) { + fprintf(stderr, "WARNING: file system corrupt, zone block %ld \ +is used more than once.\n", blknum); + return; + } + fprintf(stderr, "ERROR: file system corrupt, zone block %ld is used \ +more than once.\n", blknum); + fail("Can't determine its type"); + } + assert(ztype_class(old_ztype) == Old_zone); + + + if (new_indnum != old_indnum) { + *dz |= IND_CONFLICT_BIT; + if (new_indnum > old_indnum) { + *dz &= ~INDIRECT_MASK; + *dz |= new_indnum; + } + } + if (new_ztype == T_MAYBE_OLD_DIR || old_ztype == T_MAYBE_OLD_DIR) { + *dz |= TYPE_CONFLICT_BIT; + *dz &= ~T_MASK; + *dz |= T_MAYBE_OLD_DIR; + } +} + + +static class_t ztype_class(ztype) +int ztype; +{ + class_t class; + + if (ztype == T_MAYBE_OLD_DIR || ztype == T_OLD_NON_DIR) + class = Old_zone; + else if (ztype == T_DIR || ztype == T_NON_DIR) + class = In_use_zone; + else + class = Unused_zone; + + return class; +} + + +static void fail(str) +char *str; +{ + fprintf(stderr, "%s\n", str); + exit(1); +} + + +static unsigned int two_bytes(buf) +char buf[2]; +{ + unsigned char *ubuf = (unsigned char *) buf; + + if (big_endian_fs) + return(ubuf[0] << 8) | ubuf[1]; + else + return(ubuf[1] << 8) | ubuf[0]; +} + + +static long four_bytes(buf) +char buf[4]; +{ + unsigned char *ubuf = (unsigned char *) buf; + register int r1, r2; + + if (big_endian_fs) { + r1 = (ubuf[0] << 8) | ubuf[1]; + r2 = (ubuf[2] << 8) | ubuf[3]; + } else { + r2 = (ubuf[1] << 8) | ubuf[0]; + r1 = (ubuf[3] << 8) | ubuf[2]; + } + return((long) r1 << 16) | r2; +} + + +static void usage(arg0) +char *arg0; +{ + fprintf(stderr, "usage: %s [-v] srcfs [destfs]\n", arg0); + exit(2); +} diff --git a/commands/simple/sync.c b/commands/simple/sync.c new file mode 100755 index 000000000..adb7318a9 --- /dev/null +++ b/commands/simple/sync.c @@ -0,0 +1,14 @@ +/* sync - flush the file system buffers. Author: Andy Tanenbaum */ + +#include <sys/types.h> +#include <unistd.h> + +_PROTOTYPE(int main, (void)); + +int main() +{ +/* First prize in shortest useful program contest. */ +/* Highest comment/code ratio */ + sync(); + return(0); +} diff --git a/commands/simple/synctree.c b/commands/simple/synctree.c new file mode 100755 index 000000000..69db0a1a4 --- /dev/null +++ b/commands/simple/synctree.c @@ -0,0 +1,1481 @@ +/* synctree 4.16 - Synchronise file tree. Author: Kees J. Bot + * 5 Apr 1989 + * SYNOPSYS + * synctree [-iuf] [[user1@machine1:]dir1 [[user2@]machine2:]dir2 + * + * Dir2 will then be synchronized to dir1 with respect to the flags. + * The flags mean: + * -i Be interactive on files other than directories too. + * -u Only install files that are newer, i.e. that need an update. + * -f Force. Don't ask for confirmation, all answers are 'yes'. + * + * Hitting return lets synctree use its proposed answer. Hitting CTRL-D is + * like typing return to all questions that follow. + * + * If either of the directories to be synchronized contains the file ".backup" + * then it is a backup directory. The file ".backup" in this directory is + * an array of mode information indexed on inode number. + * + * 89/04/05, Kees J. Bot - Birth of tree synchronizing program. + * 92/02/02 - General overhaul, rcp(1) like syntax. + */ + +#define nil 0 +#include <sys/types.h> +#include <stddef.h> +#include <stdio.h> +#include <sys/stat.h> +#include <utime.h> +#include <string.h> +#include <signal.h> +#include <dirent.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <sys/wait.h> + +#if _MINIX +#include "limits.h" +#include "minix/config.h" + +/*#define BLOCK_SIZE 1024*/ +#define LITTLE_ENDIAN (CHIP == INTEL) +#define USE_SHADOWING (CHIP == M68000) +#else +#define LITTLE_ENDIAN 0 +#define USE_SHADOWING 0 +#endif + +#ifndef PATH_MAX +#define PATH_MAX 1024 +#endif + +#ifndef S_ISLNK +/* There were no symlinks in medieval times. */ +#define S_ISLNK(mode) (0) +#define lstat stat +#define symlink(path1, path2) (errno= ENOSYS, -1) +#define readlink(path, buf, len) (errno= ENOSYS, -1) +#endif + +#define NUMBYTES 4 /* Any number fits in this many bytes. */ +#define CHUNK 4096 /* Transfer files in this size chunks. */ + +static int install= 0; /* Install files, do not delete, update if newer. */ +static int interact= 0; /* Ask permission to install too. */ +static int force= 0; /* Force trees to be completely equal. */ +static int backup= 0; /* This tree is for backup. */ + +static char SYNCNAME[] = "synctree"; +static char SLAVENAME[] = "==SLAVE=="; +static char MASTERNAME[]= "==MASTER=="; + + +static char BACKUP[] = ".backup"; /* Backup filemodes. */ +static int filemodes; /* Filemodes fildes. */ + +static int chan[2]= { 0, 1 }; /* In and output channel to opposite side. */ + +#define BUCKSIZE (1+NUMBYTES+CHUNK) +static char bucket[BUCKSIZE]; /* Put a lot of things here before sending. */ +static char *buckp= bucket; /* Fill pointer. */ +static int buckn= 0; /* # bytes in bucket. */ + +enum orders { /* What back breaking labour should the slave perform? */ + ENTER= 128, /* Make ready to process contents of directory. */ + ADVANCE, /* Determine next pathname and report it back. */ + CAT, /* Send contents of file. */ + MORE, /* Send more file contents. */ + CANCEL, /* Current pathname is not installed, remove as link. */ + DIE, /* Die with exit(0); */ + DIE_BAD, /* exit(1); */ + POSITIVE, /* Ask a yes/no question expecting yes. */ + NEGATIVE, /* Same expecting no. */ + PASS_YES, /* Pass this to the master will you. */ + PASS_NO /* Same here. */ +}; + +#ifdef DEBUG +char *ORDERS[]= { + "ENTER", "ADVANCE", "CAT", "MORE", "CANCEL", "DIE", "DIE_BAD", + "POSITIVE", "NEGATIVE", "PASS_YES", "PASS_NO" +}; +#endif + +enum answers { + PATH= 128, /* Report pathname, and stat(2) info. */ + LINK, /* Report linkname, pathname, and stat(2) info. */ + DATA, /* Contents of file follow. */ + NODATA, /* Can't read file. */ + DONE, /* Nothing more to advance to. */ + SYMLINK, /* Report symlinkname, pathname, and stat(2) info. */ + YES, NO /* Result of an ASK. */ +}; + +#ifdef DEBUG +char *ANSWERS[]= { + "PATH", "LINK", "DATA", "NODATA", "DONE", "SYMLINK", "YES", "NO" +}; + +#define DPRINTF(format, arg) fprintf(stderr, format, arg0, arg) +#else +#define DPRINTF(format, arg) +#endif + +struct mode { + unsigned short md_mode; + unsigned short md_uid; + unsigned short md_gid; + unsigned short md_rdev; + unsigned short md_devsiz; +}; + +static char *arg0; /* basename(argv[0]) */ +static int ex= 0; /* exit status. */ + +static void because() +{ + fprintf(stderr, ": %s\n", strerror(errno)); + ex= 1; +} + +static void perr(label) char *label; +{ + fprintf(stderr, "%s: %s: %s\n", arg0, label, strerror(errno)); + ex= 1; +} + +static void perrx(label) char *label; +{ + perr(label); + exit(1); +} + +#if S_HIDDEN +/* Support for per achitecture hidden files. */ +static int transparent= 0; + +static void isvisible(name) char *name; +{ + char *p= name + strlen(name); + + while (p > name && *--p == '/') {} + + if (p > name && *p == '@' && p[-1] != '/') transparent= 1; +} +#else +#define transparent 0 +#define isvisible(name) ((void) 0) +#endif + +static void isbackup(slave) int slave; +{ + if ((filemodes= open(BACKUP, slave ? O_RDONLY : O_RDWR)) >= 0) + backup= 1; + else { + if (errno != ENOENT) perrx(BACKUP); + } +} + +static char path[PATH_MAX+1]; /* Holds pathname of file being worked on. */ +static char lnkpth[PATH_MAX+1]; /* (Sym)link to path. */ +static char *linkpath; /* What path is, or should be linked to. */ +static struct stat st; /* Corresponding stat(2) info. */ +static char Spath[PATH_MAX+1]; /* Slave is looking at this. */ +static char Slnkpth[PATH_MAX+1];/* (Sym)link to Spath. */ +static char *Slinkpath=nil; /* Either nil or Slnkpth. */ +static struct stat Sst; /* Slave's stat(2). */ + +static char *addpath(p, n) char *p, *n; +/* Add a name to the path, return pointer to end. */ +{ + if (p - path + 1 + strlen(n) > PATH_MAX) { + *p= 0; + fprintf(stderr, "%s: %s/%s is too long.\n", arg0, path, n); + fprintf(stderr, "%s: Unable to continue.\n", arg0); + exit(1); + } + if (p == path+1 && path[0] == '.') p= path; + + if (p > path) *p++ = '/'; + + while (*n != 0) *p++ = *n++; + *p= 0; + return p; +} + +struct entry { /* A directory entry. */ + struct entry *next; /* Next entry in same directory */ + struct entry *dir; /* It is part of this directory */ + struct entry *con; /* If a dir, its contents */ + char *name; /* Name of this dir entry */ +}; + +static struct entry *E= nil; /* File being processed. */ + +static void setpath(e) struct entry *e; +/* Set path leading to e. */ +{ + static char *pend; + + if (e == nil) + pend= path; + else { + setpath(e->dir); + pend= addpath(pend, e->name); + } +} + +static void sort(ae) struct entry **ae; +/* This is either a stable mergesort, or thermal noise, I'm no longer sure. + * It must be called like this: if (L!=nil && L->next!=nil) sort(&L); + */ +{ + /* static */ struct entry *e1, **mid; /* Need not be local */ + struct entry *e2; + + e1= *(mid= &(*ae)->next); + do { + if ((e1= e1->next) == nil) break; + mid= &(*mid)->next; + } while ((e1= e1->next) != nil); + + e2= *mid; + *mid= nil; + + if ((*ae)->next != nil) sort(ae); + if (e2->next != nil) sort(&e2); + + e1= *ae; + for (;;) { + if (strcmp(e1->name, e2->name)<=0) { + if ((e1= *(ae= &e1->next)) == nil) { + *ae= e2; + break; + } + } else { + *ae= e2; + e2= *(ae= &e2->next); + *ae= e1; + if (e2 == nil) break; + } + } +} + +static void enter() +/* Collect directory entries of E. */ +{ + struct entry **last= &E->con, *new; + struct dirent *e; + DIR *d; + + if ((d= opendir(path)) == nil) { + fprintf(stderr, "%s: Can't read dir %s\n", arg0, path); + return; + } + + while ((e= readdir(d)) != nil) { + if (e->d_name[0] == '.' && (e->d_name[1] == 0 + || (e->d_name[1] == '.' && e->d_name[2] == 0) + )) continue; + + new= (struct entry *) malloc(sizeof(*new)); + + new->next= nil; + new->dir= E; + new->con= nil; + new->name= (char *) malloc(strlen(e->d_name) + 1); + strcpy(new->name, e->d_name); + *last= new; + last= &new->next; + } + closedir(d); + if (E->con != nil && E->con->next != nil) sort(&E->con); +} + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +static char *link_islink(struct stat *stp, const char *file) +{ + /* Tell if a file, which stat(2) information in '*stp', has been seen + * earlier by this function under a different name. If not return a + * null pointer with errno set to ENOENT, otherwise return the name of + * the link. Return a null pointer with an error code in errno for any + * error, using E2BIG for a too long file name. + * + * Use link_islink(nil, nil) to reset all bookkeeping. + * + * Call for a file twice to delete it from the store. + */ + + typedef struct link { /* In-memory link store. */ + struct link *next; /* Hash chain on inode number. */ + ino_t ino; /* File's inode number. */ + off_t off; /* Offset to more info in temp file. */ + } link_t; + typedef struct dlink { /* On-disk link store. */ + dev_t dev; /* Device number. */ + char file[PATH_MAX]; /* Name of earlier seen link. */ + } dlink_t; + static link_t *links[256]; /* Hash list of known links. */ + static int tfd= -1; /* Temp file for file name storage. */ + static dlink_t dlink; + link_t *lp, **plp; + size_t len; + off_t off; + + if (file == nil) { + /* Reset everything. */ + for (plp= links; plp < arraylimit(links); plp++) { + while ((lp= *plp) != nil) { + *plp= lp->next; + free(lp); + } + } + if (tfd != -1) close(tfd); + tfd= -1; + return nil; + } + + /* The file must be a non-directory with more than one link. */ + if (S_ISDIR(stp->st_mode) || stp->st_nlink <= 1) { + errno= ENOENT; + return nil; + } + + plp= &links[stp->st_ino % arraysize(links)]; + + while ((lp= *plp) != nil) { + if (lp->ino == stp->st_ino) { + /* May have seen this link before. Get it and check. */ + if (lseek(tfd, lp->off, SEEK_SET) == -1) return nil; + if (read(tfd, &dlink, sizeof(dlink)) < 0) return nil; + + /* Only need to check the device number. */ + if (dlink.dev == stp->st_dev) { + if (strcmp(file, dlink.file) == 0) { + /* Called twice. Forget about this link. */ + *plp= lp->next; + free(lp); + errno= ENOENT; + return nil; + } + + /* Return the name of the earlier link. */ + return dlink.file; + } + } + plp= &lp->next; + } + + /* First time I see this link. Add it to the store. */ + if (tfd == -1) { + for (;;) { + char *tmp; + + tmp= tmpnam(nil); + tfd= open(tmp, O_RDWR|O_CREAT|O_EXCL, 0600); + if (tfd < 0) { + if (errno != EEXIST) return nil; + } else { + (void) unlink(tmp); + break; + } + } + } + if ((len= strlen(file)) >= PATH_MAX) { + errno= E2BIG; + return nil; + } + + dlink.dev= stp->st_dev; + strcpy(dlink.file, file); + len += offsetof(dlink_t, file) + 1; + if ((off= lseek(tfd, 0, SEEK_END)) == -1) return nil; + if (write(tfd, &dlink, len) != len) return nil; + + if ((lp= malloc(sizeof(*lp))) == nil) return nil; + lp->next= nil; + lp->ino= stp->st_ino; + lp->off= off; + *plp= lp; + errno= ENOENT; + return nil; +} + +#define cancellink() ((void) islink()) + +static char *islink() +/* Returns the name of the file path is linked to. If no such link can be + * found, then path is added to the list and nil is returned. If all the + * links of a file have been seen, then it is removed from the list. + * Directories are not seen as linkable. + */ +{ + char *name; + + name= link_islink(&st, path); + if (name == nil && errno != ENOENT) perrx(path); + return name; +} + +static void setstat(ino, stp) ino_t ino; struct stat *stp; +/* Set backup status info, we know that backup is true. */ +{ + struct mode md; + + md.md_mode = stp->st_mode; + md.md_uid = stp->st_uid; + md.md_gid = stp->st_gid; + md.md_rdev = stp->st_rdev; + md.md_devsiz = stp->st_size / 1024; + + if (lseek(filemodes, (off_t) sizeof(md) * (off_t) ino, 0) == -1 + || write(filemodes, (char *) &md, sizeof(md)) != sizeof(md) + ) perrx(BACKUP); +} + +static int getstat(name, stp) char *name; struct stat *stp; +/* Get status information of file name, skipping some files. Backup info + * is inserted as needed. + */ +{ + errno= 0; + + if (strcmp(name, BACKUP) == 0) return -1; + + if (lstat(name, stp) < 0) return -1; + + if (stp->st_mode == S_IFREG && stp->st_mtime == 0) return -1; + + if (backup) { + struct mode md; + + if (lseek(filemodes, + (off_t) sizeof(md) * (off_t) stp->st_ino, 0) == -1 + || read(filemodes, (char *) &md, sizeof(md)) != sizeof(md) + || md.md_mode == 0 + ) { + errno= 0; + setstat(stp->st_ino, stp); + } else { + stp->st_mode = md.md_mode; + stp->st_uid = md.md_uid; + stp->st_gid = md.md_gid; + stp->st_rdev = md.md_rdev; + if (S_ISBLK(stp->st_mode)) + stp->st_size= (off_t) md.md_devsiz * 1024; + } + } + return 0; +} + +static int advance() +/* Determine next pathname, return true on success. */ +{ + for (;;) { + if (E==nil) { /* First call, enter root dir. */ + E= (struct entry *) malloc(sizeof(*E)); + E->dir= nil; + E->con= nil; + E->next= nil; + E->name= (char *) malloc(3); + strcpy(E->name, transparent ? ".@" : "."); + } else + if (E->con != nil) /* Dir's files must be processed. */ + E= E->con; + else { + for (;;) { + /* Remove E from it's parents list, then + * try next entry, if none, go to parent dir. + */ + struct entry *junk= E, *parent= E->dir; + + if (parent != nil) parent->con= E->next; + E= E->next; + free(junk->name); + free(junk); + + if (E != nil) break; + + if ((E= parent) == nil) return 0; + } + } + setpath(E); + if (getstat(path, &st) == 0) { + if (S_ISLNK(st.st_mode)) { + int n; + + if ((n= readlink(path, lnkpth, PATH_MAX)) >= 0) + { + lnkpth[n]= 0; + break; + } + } else { + break; + } + } + if (errno != 0 && errno != ENOENT) perr(path); + } + + linkpath= islink(); + DPRINTF("%s: path = %s\n", path); + return 1; +} + +static enum orders request() +/* Slave reads command sent by master. */ +{ + static char buf[64], *bp; + static int n= 0; + int req; + + for (;;) { + if (n == 0) { + if ((n= read(chan[0], buf, (int) sizeof(buf))) <= 0) { + if (n < 0) perrx("request()"); + /* Master died, try to report it then follow. */ + fprintf(stderr, + "%s: Master died prematurely.\n", arg0); + exit(1); + } + bp= buf; + } + req= *bp++ & 0xFF; + n--; + if (req >= (int) ENTER) break; + + /* Master using slave to print to stdout: */ + putchar(req); + } + DPRINTF("%s: request() == %s\n", ORDERS[req - (int) ENTER]); + + return (enum orders) req; +} + +static void report() +{ + int r; + + DPRINTF("%s: reporting now!\n", 0); + + buckp= bucket; + + while (buckn > 0) { + r = buckn; + if (r > (512 << sizeof(char*))) r= (512 << sizeof(char*)); + + if ((r= write(chan[1], buckp, r)) < 0) perrx("report()"); + + buckp += r; + buckn -= r; + } + buckp= bucket; + buckn= 0; +} + +static void inform(a) enum answers a; +/* Slave replies to master. */ +{ + DPRINTF("%s: inform(%s)\n", ANSWERS[(int) a - (int) PATH]); + + *buckp++ = (int) a; + buckn++; +} + +#define wwrite(buf, n) (memcpy(buckp, (buf), (n)), buckp+= (n), buckn+= (n)) + +static void sendnum(n) long n; +/* Send number from least to most significant byte. */ +{ +#if LITTLE_ENDIAN + wwrite((char *) &n, sizeof(n)); +#else + char buf[NUMBYTES]; + + buf[0]= (char) (n >> 0); + buf[1]= (char) (n >> 8); + buf[2]= (char) (n >> 16); + buf[3]= (char) (n >> 24); + wwrite(buf, sizeof(buf)); +#endif +} + +static void send(buf, n) char *buf; int n; +/* Slave sends size and contents of buf. */ +{ + sendnum((long) n); + if (n > 0) wwrite(buf, (size_t) n); +} + +static void sendstat(stp) struct stat *stp; +{ + sendnum((long) stp->st_mode); + sendnum((long) stp->st_uid); + sendnum((long) stp->st_gid); + sendnum((long) stp->st_rdev); + sendnum((long) stp->st_size); + sendnum((long) stp->st_mtime); +} + +static int ask(); + +static void slave() +/* Carry out orders from the master, such as transmitting path names. + * Note that the slave uses path, not Spath, the master uses Spath. + */ +{ + int f, n; + char buf[CHUNK]; + enum { run, done, die } state= run; + + do { + switch (request()) { + case ENTER: + enter(); + break; + case ADVANCE: + if (!advance() || state == done) { + inform(DONE); + state= done; + } else { + if (linkpath!=nil) { + inform(LINK); + send(linkpath, strlen(linkpath) + 1); + } else + if (S_ISLNK(st.st_mode)) { + inform(SYMLINK); + send(lnkpth, strlen(lnkpth) + 1); + } else { + inform(PATH); + } + send(path, strlen(path) + 1); + sendstat(&st); + } + break; + case CAT: + if ((f= open(path, O_RDONLY))<0) { + fprintf(stderr, "%s: Can't open %s", + arg0, path); + because(); + inform(NODATA); + break; + } + inform(DATA); + do { + n= read(f, buf, sizeof(buf)); + if (n < 0) perr(path); + send(buf, n); + if (n > 0) report(); + } while (n > 0); + close(f); + break; + case CANCEL: + cancellink(); + break; + case DIE_BAD: + ex= 1; + /*FALL THROUGH*/ + case DIE: + state= die; + break; + case POSITIVE: + inform(ask('y') ? YES : NO); + break; + case NEGATIVE: + inform(ask('n') ? YES : NO); + break; + case PASS_YES: + inform(YES); + break; + case PASS_NO: + inform(NO); + break; + default: + fprintf(stderr, "%s: strange request\n", arg0); + exit(1); + } + report(); + } while (state != die); +} + +static int execute(argv) char **argv; +/* Execute a command and return its success or failure. */ +{ + int pid, r, status; + + if ((pid= fork())<0) { + perr("fork()"); + return 0; + } + if (pid == 0) { + execvp(argv[0], argv); + perrx(argv[0]); + } + while ((r= wait(&status)) != pid) { + if (r < 0) { + perr(argv[0]); + return 0; + } + } + return status == 0; +} + +static int removedir(dir) char *dir; +/* Remove a directory and its contents. */ +{ + static char *argv[] = { "rm", "-r", nil, nil }; + + printf("(rm -r %s)\n", dir); + + argv[2]= dir; + return execute(argv); +} + +static void order(o) enum orders o; +/* Master tells slave what to do. */ +{ + char c= (char) o; + + DPRINTF("%s: order(%s)\n", ORDERS[o - (int) ENTER]); + + if (write(chan[1], &c, 1) != 1) perrx("order()"); +} + +static void rread(buf, n) char *buf; int n; +/* Master gets buf of size n from slave, doing multiple reads if needed. */ +{ + int r; + + while (n > 0) { + if (buckn == 0) { + switch (buckn= read(chan[0], bucket, BUCKSIZE)) { + case -1: + perrx("reply channel from slave"); + case 0: + fprintf(stderr, + "%s: slave died prematurely.\n", + arg0); + exit(1); + } + buckp= bucket; + } + r= n < buckn ? n : buckn; + memcpy(buf, buckp, r); + buckp+= r; + buckn-= r; + buf+= r; + n-= r; + } +} + +static enum answers answer() +/* Master reads slave's reply. */ +{ + char c; + int a; + + rread(&c, 1); + a= c & 0xFF; + + DPRINTF("%s: answer() == %s\n", ANSWERS[a - (int) PATH]); + + return (enum answers) a; +} + +static long recnum() +/* Read number as pack of bytes from least to most significant. The data + * is on the wire in little-endian format. (Mostly run on PC's). + */ +{ +#if LITTLE_ENDIAN + long n; + + rread((char *) &n, (int) sizeof(n)); + return n; +#else + unsigned char buf[NUMBYTES]; + + rread(buf, sizeof(buf)); + return buf[0] + | ((unsigned) buf[1] << 8) + | ((unsigned long) buf[2] << 16) + | ((unsigned long) buf[3] << 24); +#endif +} + +static int receive(buf, max) char *buf; int max; +/* Master get's data from slave, by first reading size, then data. */ +{ + int n; + + n= recnum(); + if (n > max) { + fprintf(stderr, "%s: panic: Can't read %d bytes\n", arg0, n); + exit(1); + } + if (n > 0) rread(buf, n); + return n; +} + +static void recstat(stp) struct stat *stp; +{ + stp->st_mode= recnum(); + stp->st_uid= recnum(); + stp->st_gid= recnum(); + stp->st_rdev= recnum(); + stp->st_size= recnum(); + stp->st_mtime= recnum(); +} + +static int key() +{ + int c; + static int tty= -1; + + if (tty < 0) tty= isatty(0); + + if (feof(stdin) || (c= getchar()) == EOF) { + c= '\n'; + if (tty) putchar('\n'); + } + + if (!tty) putchar(c); + + return c; +} + +static int ask(def) int def; +/* Ask for a yes or no, anything else means choose def. */ +{ + int y, c; + + if (chan[0] == 0) { + /* I'm running remote, tell the slave to ask. */ + fflush(stdout); + order(def == 'y' ? POSITIVE : NEGATIVE); + return answer() == YES; + } + + printf("? (%c) ", def); + fflush(stdout); + + do c= key(); while (c == ' ' || c == '\t'); + + y= c; + + while (c != '\n') c= key(); + + if (y != 'y' && y != 'Y' && y != 'n' && y != 'N') y= def; + + return y == 'y' || y == 'Y'; +} + +static void setmodes(silent) int silent; +{ + struct stat st; + int change= 0; + struct utimbuf tms; + + errno= 0; + getstat(Spath, &st); + if (backup && silent) { + setstat(st.st_ino, &Sst); + getstat(Spath, &st); + } + + if (S_ISLNK(st.st_mode)) return; + + if (errno == 0 && st.st_mode != Sst.st_mode) { + if (!backup) chmod(Spath, Sst.st_mode & 07777); + change= 1; + } + if (errno == 0 + && (st.st_uid != Sst.st_uid || st.st_gid != Sst.st_gid) + && (backup || geteuid() == 0) + ) { + errno= 0; + if (!backup) chown(Spath, Sst.st_uid, Sst.st_gid); + change= 1; + } + + if (backup && !silent) setstat(st.st_ino, &Sst); + + if (errno == 0 && S_ISREG(Sst.st_mode) && st.st_mtime != Sst.st_mtime) { + time(&tms.actime); + tms.modtime= Sst.st_mtime; + errno= 0; + utime(Spath, &tms); + change= 1; + } + if (errno != 0) { + fprintf(stderr, "%s: Can't set modes of %s", arg0, Spath); + because(); + } else + if (change && !silent) { + printf("Mode changed of %s\n", Spath); + } +} + +static void makeold() +{ + static struct utimbuf tms= { 0, 0 }; + + if (utime(Spath, &tms) < 0) { + if (errno != ENOENT) { + fprintf(stderr, + "%s: can't make %s look old", arg0, Spath); + because(); + } + } else { + fprintf(stderr, "%s: made %s look old.\n", arg0, Spath); + } +} + +static int busy= 0; + +static void bail_out(sig) int sig; +{ + signal(sig, SIG_IGN); + + fprintf(stderr, "%s: Exiting after signal %d\n", arg0, sig); + + if (busy) { + fprintf(stderr, "%s: was installing %s\n", arg0, Spath); + makeold(); + } + order(DIE_BAD); + + exit(sig); +} + +static int makenode(name, mode, addr, size) + char *name; int mode; dev_t addr; off_t size; +{ + int r; + + if (!backup) { + r= mknod(name, mode, addr); + } else { + if ((r= creat(name, 0644)) >= 0) close(r); + } + return r; +} + +static void add(update) int update; +/* Add Spath to the filesystem. */ +{ + int f, n; + char buf[CHUNK]; + int forced_update= force && update; + + if (Slinkpath != nil && !S_ISLNK(Sst.st_mode)) { + if (interact && !update) { + printf("Link %s to %s", Spath, Slinkpath); + if (!ask('n')) return; + } + if (link(Slinkpath, Spath) >= 0) { + printf("Linked %s to %s\n", Spath, Slinkpath); + return; + } else { + fprintf(stderr, + "%s: Can't link %s to %s", + arg0, Slinkpath, Spath); + because(); + /* Try to install instead. */ + } + } + switch (Sst.st_mode & S_IFMT) { + case S_IFDIR: + if (!force) { + printf("Add dir %s", Spath); + if (!ask('n')) return; + } + if (mkdir(Spath, backup ? 0755 : Sst.st_mode) < 0) { + perr(Spath); + return; + } + printf("Directory %s created.\n", Spath); + order(ENTER); + break; + case S_IFBLK: + case S_IFCHR: + case S_IFIFO: + if (interact && !update) { + printf("Create special file %s", Spath); + if (!ask('n')) { order(CANCEL); return; } + } + if (makenode(Spath, Sst.st_mode, Sst.st_rdev, Sst.st_size)<0) { + fprintf(stderr, + "%s: Can't create special file %s", + arg0, Spath); + because(); + return; + } + printf("Special file %s created\n", Spath); + break; +#ifdef S_IFLNK + case S_IFLNK: + if (interact && !update) { + printf("Install %s -> %s", Spath, Slnkpth); + if (!ask('n')) { order(CANCEL); return; } + } + if (symlink(Slnkpth, Spath) < 0) { + fprintf(stderr, "%s: Can't create symlink %s", + arg0, Spath); + because(); + return; + } + printf("%s %s -> %s\n", + forced_update ? "Updated: " : "Installed:", + Spath, Slnkpth); + break; +#endif + case S_IFREG: + if (interact && !update) { + printf("Install %s", Spath); + if (!ask('n')) { order(CANCEL); return; } + } + order(CAT); + if (answer() != DATA) return; + + busy= 1; + if ((f= creat(Spath, backup ? 0644 : Sst.st_mode&07777)) < 0) { + busy= 0; + fprintf(stderr, "%s: Can't create %s", arg0, Spath); + because(); + } + + while ((n= receive(buf, sizeof(buf)))>0) { + if (f >= 0 && write(f, buf, n) != n) { + fprintf(stderr, "%s: Write error on %s", + arg0, Spath); + because(); + close(f); f= -1; + } + } + if (n < 0) { + fprintf(stderr, "%s: Slave read err on %s\n", + arg0, Spath); + } + if (f >= 0) close(f); + if (n < 0 || f < 0) { makeold(); busy= 0; return; } + busy= 0; + printf("%s %s\n", + forced_update ? + Sst.st_mtime < st.st_mtime ? "Restored: " : + "Updated: " : + "Installed:", + Spath + ); + break; + default: + fprintf(stderr, + "%s: Won't add file with strange mode %05o: %s\n", + arg0, Sst.st_mode, Spath); + order(CANCEL); + return; + } + setmodes(1); +} + +static int delete(update) int update; +/* Delete path. */ +{ + int forced_update= force && update; + + if (S_ISDIR(st.st_mode)) { + if (install) return 0; + if (!force) { + printf("Delete dir %s", path); + if (!ask('n')) return 0; + } + if (!removedir(path)) { ex= 1; return 0; } + if (!forced_update) printf("Directory %s deleted.\n", path); + return 1; + } + + if (install && !update) return 0; + + if (!force) { + printf("Delete %s", path); + if (!ask((interact && !update) ? 'n' : 'y')) return 0; + } + + if (unlink(path)<0) { + fprintf(stderr, "Can't delete %s", path); + because(); + return 0; + } + cancellink(); + if (!forced_update) printf("Deleted: %s\n", path); + return 1; +} + +static int different() +/* Return true iff path and Spath are different. */ +{ + if (! ( (linkpath == nil && Slinkpath == nil) + || (linkpath != nil && Slinkpath != nil + && strcmp(linkpath, Slinkpath) == 0) + )) { + linkpath= Slinkpath; + return 1; + } + + if ((st.st_mode & S_IFMT) != (Sst.st_mode & S_IFMT)) return 1; + + switch (st.st_mode & S_IFMT) { + case S_IFDIR: + return 0; + case S_IFBLK: + case S_IFCHR: + return st.st_rdev != Sst.st_rdev; + case S_IFREG: + if (install) return Sst.st_mtime > st.st_mtime; + return st.st_size != Sst.st_size + || st.st_mtime != Sst.st_mtime; + case S_IFIFO: return 0; +#ifdef S_IFLNK + case S_IFLNK: return strcmp(lnkpth, Slnkpth) != 0; +#endif + default: return 1; + } +} + +static void compare() +/* See if path and Spath are same. */ +{ + if (different()) { + if (!force) { + printf("%sing %s (delete + add)\n", + Sst.st_mtime < st.st_mtime ? "Restor" : "Updat", + path); + } + if (delete(1)) add(1); + } else { + if (!install) setmodes(0); + + if (S_ISDIR(st.st_mode)) { + order(ENTER); + enter(); + } + } +} + +static int done= 0, Sdone= 0; + +static enum action { ADD, COMPARE, DELETE } action() +/* Look at path's of master and slave, compare them alphabetically to see + * who is ahead of who, then tell what is to be done. + */ +{ + int c; + char *Sp, *p; + + if (done) return ADD; /* Slave still has names. */ + if (Sdone) return DELETE; /* Master has too many names. */ + + /* Compare paths. Let "a/a" come before "a.a". */ + Sp= Spath; + p= path; + while (*Sp == *p && *Sp != 0) { Sp++; p++; } + if (*Sp == '/') return ADD; + if (*p == '/') return DELETE; + return (c= strcmp(Sp, p)) == 0 ? COMPARE : c < 0 ? ADD : DELETE; +} + +static void master() +/* Synchronise file tree to that of its slave. */ +{ + enum action a= COMPARE; /* Trick first advances. */ + + umask(backup ? 0022 : 0000); + + signal(SIGPIPE, SIG_IGN); + signal(SIGHUP, bail_out); + signal(SIGINT, bail_out); + signal(SIGTERM, bail_out); + + while (!done || !Sdone) { + if (!Sdone && (a == ADD || a == COMPARE)) { + /* Slave advances. */ + order(ADVANCE); + switch (answer()) { + case PATH: + Slinkpath= nil; + receive(Spath, sizeof(Spath)); + recstat(&Sst); + break; + case LINK: + receive(Slnkpth, sizeof(Slnkpth)); + Slinkpath= Slnkpth; + receive(Spath, sizeof(Spath)); + recstat(&Sst); + break; + case SYMLINK: + Slinkpath= nil; + receive(Slnkpth, sizeof(Slnkpth)); + receive(Spath, sizeof(Spath)); + recstat(&Sst); + break; + case DONE: + Sdone= 1; + break; + default: + fprintf(stderr, + "%s: Strange answer from slave.\n", + arg0); + exit(1); + } + } + if (!done && (a == COMPARE || a == DELETE)) { + /* Master advances. */ + if (!advance()) done= 1; + } + + if (done && Sdone) break; + + switch (a= action()) { + case ADD: /* Spath exists, path doesn't, add? */ + add(0); + break; + case COMPARE: /* They both exist, are they the same? */ + compare(); + break; + case DELETE: /* path exists, Spath doesn't, delete? */ + delete(0); + } + fflush(stdout); /* Don't keep user in suspense. */ + } + order(ex == 0 ? DIE : DIE_BAD); +} + +static void mediator() +/* Sits at the local machine and passes orders from master to slave, both + * on remote machines. Only diagnostics and questions are handled. + */ +{ + enum orders req; + + for (;;) { + switch (req= request()) { + case DIE_BAD: + ex= 1; + /*FALL THROUGH*/ + case DIE: + order(DIE); + return; + case POSITIVE: + order(ask('y') ? PASS_YES : PASS_NO); + break; + case NEGATIVE: + order(ask('n') ? PASS_YES : PASS_NO); + break; + default: + order(req); + } + } +} + +#define P_EXIT 1 /* Make sure process doesn't return. */ +#define P_SHADOW 2 /* Always use exec on 68000. */ + +static void startprocess(proc, machine, path, p_flags) + void (*proc)(); char *machine, *path; int p_flags; +{ + char *argv[10], **argp= argv; + char flags[10], *pfl= flags; + + if (machine != nil) { + char *u= machine, *m; + + *argp++ = "rsh"; + if ((m= strchr(machine, '@')) != nil) { + *m++ = 0; + *argp++ = "-l"; + *argp++ = u; + machine= m; + } + *argp++ = machine; + } else + /* Without this check it would run like a pig on an non MMU 68000: */ + if (!(USE_SHADOWING && p_flags & P_SHADOW)) { + if (chdir(path) < 0) { + if (proc != master || errno != ENOENT + || mkdir(path, 0700) < 0) + perrx(path); + if (chdir(path) < 0) perrx(path); + printf("Destination directory %s created\n", path); + } + isvisible(path); + isbackup(proc == slave); + (*proc)(); + if (p_flags & P_EXIT) exit(ex); + return; + } + *argp++ = SYNCNAME; + *pfl++ = '-'; + if (interact) *pfl++ = 'i'; + if (install) *pfl++ = 'u'; + if (force) *pfl++ = 'f'; + *pfl= 0; + *argp++ = flags; + *argp++ = proc == slave ? SLAVENAME : MASTERNAME; + *argp++ = path; + *argp++ = nil; +#ifdef DEBUG + fprintf(stderr, "execlp("); + for (argp= argv; *argp != nil; argp++) fprintf(stderr, "%s, ", *argp); + fprintf(stderr, "nil);\n"); +#endif + execvp(argv[0], argv); + perrx(argv[0]); +} + +void splitcolon(path, amach, adir) char *path, **amach, **adir; +{ + char *dir= path; + + for (;;) { + if (*dir == ':') { + *dir++ = 0; + *amach= path; + *adir= dir; + break; + } + if (*dir == 0 || *dir == '/') { + *amach= nil; + *adir= path; + break; + } + dir++; + } +} + +static void Usage() +{ + fprintf(stderr, + "Usage: %s [-iuf] [[user@]machine:]dir1 [[user@]machine:]dir2\n", + arg0); + exit(1); +} + +main(argc, argv) int argc; char **argv; +{ + char *s_mach, *s_dir; + char *m_mach, *m_dir; + int m2s[2], s2m[2], m2m[2]; + int s_pid= 0, m_pid= 0; + int r; + + if ((arg0= strrchr(argv[0], '/')) == nil) arg0= argv[0]; else arg0++; + + while (argc>1 && argv[1][0] == '-') { + char *f= argv[1]+1; + + while (*f != 0) { + switch (*f++) { + case 'i': interact= 1; break; + case 'u': install= 1; break; + case 'f': force= 1; break; + default: Usage(); + } + } + argc--; + argv++; + } + + if (argc != 3) Usage(); + + if (strcmp(argv[1], SLAVENAME) == 0) { + arg0= "Slave"; + splitcolon(argv[2], &s_mach, &s_dir); + startprocess(slave, s_mach, s_dir, P_EXIT); + } else + if (strcmp(argv[1], MASTERNAME) == 0) { + arg0= "Master"; + splitcolon(argv[2], &m_mach, &m_dir); + startprocess(master, m_mach, m_dir, P_EXIT); + } + + splitcolon(argv[1], &s_mach, &s_dir); + splitcolon(argv[2], &m_mach, &m_dir); + + /* How difficult can plumbing be? */ + if (pipe(m2s) < 0 || pipe(s2m) < 0) perrx("pipe()"); + + if (m_mach == nil) { + /* synctree [machine:]dir1 dir2 */ + switch (s_pid= fork()) { + case -1: + perrx("fork()"); + case 0: + dup2(m2s[0], 0); close(m2s[0]); close(m2s[1]); + dup2(s2m[1], 1); close(s2m[0]); close(s2m[1]); + arg0= "Slave"; + startprocess(slave, s_mach, s_dir, P_EXIT|P_SHADOW); + } + chan[0]= s2m[0]; close(s2m[1]); + chan[1]= m2s[1]; close(m2s[0]); + startprocess(master, m_mach, m_dir, 0); + } else + if (s_mach == nil) { + /* synctree dir1 machine:dir2 */ + switch (m_pid= fork()) { + case -1: + perrx("fork()"); + case 0: + dup2(s2m[0], 0); close(s2m[0]); close(s2m[1]); + dup2(m2s[1], 1); close(m2s[0]); close(m2s[1]); + arg0= "Master"; + startprocess(master, m_mach, m_dir, P_EXIT|P_SHADOW); + } + chan[0]= m2s[0]; close(m2s[1]); + chan[1]= s2m[1]; close(s2m[0]); + startprocess(slave, s_mach, s_dir, 0); + } else { + /* synctree machine1:dir1 machine2:dir2 */ + if (pipe(m2m) < 0) perrx(pipe); + + switch (s_pid= fork()) { + case -1: + perrx("fork()"); + case 0: + dup2(m2s[0], 0); close(m2s[0]); close(m2s[1]); + dup2(s2m[1], 1); close(s2m[0]); close(s2m[1]); + close(m2m[0]); close(m2m[1]); + arg0= "Slave"; + startprocess(slave, s_mach, s_dir, P_EXIT|P_SHADOW); + } + + switch (m_pid= fork()) { + case -1: + perrx("fork()"); + case 0: + dup2(s2m[0], 0); close(s2m[0]); close(s2m[1]); + close(m2s[0]); close(m2s[1]); + dup2(m2m[1], 1); close(m2m[0]); close(m2m[1]); + arg0= "Master"; + startprocess(master, m_mach, m_dir, P_EXIT|P_SHADOW); + } + close(s2m[0]); close(s2m[1]); + chan[0]= m2m[0]; close(m2m[1]); + chan[1]= m2s[1]; close(m2s[0]); + mediator(); + } + close(chan[0]); + close(chan[1]); + + alarm(15); /* Don't wait(2) forever. */ + + while (s_pid != 0 || m_pid != 0) { + if ((r= wait((int *) nil)) < 0) perrx("wait()"); + if (r == s_pid) s_pid= 0; + if (r == m_pid) m_pid= 0; + } + exit(ex); +} diff --git a/commands/simple/sysenv.c b/commands/simple/sysenv.c new file mode 100755 index 000000000..b1c693eec --- /dev/null +++ b/commands/simple/sysenv.c @@ -0,0 +1,80 @@ +/* sysenv 1.0 - request system boot parameter Author: Kees J. Bot + * 23 Dec 2000 + */ +#define nil ((void*)0) +#include <sys/types.h> +#include <sys/svrctl.h> +#include <stdarg.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> +#include <string.h> + +#define NIL ((char*)0) + +static void tell(int fd, ...) +{ + va_list ap; + char *s; + + va_start(ap, fd); + while ((s= va_arg(ap, char *)) != NIL) { + (void) write(fd, s, strlen(s)); + } + va_end(ap); +} + +int main(int argc, char **argv) +{ + struct sysgetenv sysgetenv; + int i; + int ex= 0; + char *e; + char val[1024]; + + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++]+1; + + if (opt[0] == '-' && opt[1] == 0) break; /* -- */ + + if (*opt != 0) { + tell(2, "Usage: sysenv [name ...]\n", NIL); + exit(1); + } + } + + do { + if (i < argc) { + sysgetenv.key= argv[i]; + sysgetenv.keylen= strlen(sysgetenv.key) + 1; + } else { + sysgetenv.key= nil; + sysgetenv.keylen= 0; + } + sysgetenv.val= val; + sysgetenv.vallen= sizeof(val); + + if (svrctl(SYSGETENV, &sysgetenv) == -1) { + if (errno == ESRCH) { + ex |= 2; + } else { + ex |= 1; + tell(2, "sysenv: ", strerror(errno), "\n", NIL); + } + continue; + } + + e= sysgetenv.val; + do { + e += strlen(e); + *e++ = '\n'; + } while (i == argc && *e != 0); + + if (write(1, sysgetenv.val, e - sysgetenv.val) < 0) { + ex |= 1; + tell(2, "sysenv: ", strerror(errno), "\n", NIL); + } + } while (++i < argc); + return ex; +} diff --git a/commands/simple/tail.c b/commands/simple/tail.c new file mode 100755 index 000000000..ae00d7905 --- /dev/null +++ b/commands/simple/tail.c @@ -0,0 +1,359 @@ +/* tail - copy the end of a file Author: Norbert Schlenker */ + +/* Syntax: tail [-f] [-c number | -n number] [file] + * tail -[number][c|l][f] [file] (obsolescent) + * tail +[number][c|l][f] [file] (obsolescent) + * Flags: + * -c number Measure starting point in bytes. If number begins + * with '+', the starting point is relative to the + * the file's beginning. If number begins with '-' + * or has no sign, the starting point is relative to + * the end of the file. + * -f Keep trying to read after EOF on files and FIFOs. + * -n number Measure starting point in lines. The number + * following the flag has significance similar to + * that described for the -c flag. + * + * If neither -c nor -n are specified, the default is tail -n 10. + * + * In the obsolescent syntax, an argument with a 'c' following the + * (optional) number is equivalent to "-c number" in the standard + * syntax, with number including the leading sign ('+' or '-') of the + * argument. An argument with 'l' following the number is equivalent + * to "-n number" in the standard syntax. If the number is not + * specified, 10 is used as the default. If neither 'c' nor 'l' are + * specified, 'l' is assumed. The character 'f' may be suffixed to + * the argument and is equivalent to specifying "-f" in the standard + * syntax. Look for lines marked "OBSOLESCENT". + * + * If no file is specified, standard input is assumed. + * + * P1003.2 does not specify tail's behavior when a count of 0 is given. + * It also does not specify clearly whether the first byte (line) of a + * file should be numbered 0 or 1. Historical behavior is that the + * first byte is actually number 1 (contrary to all Unix standards). + * Historically, a count of 0 (or -0) results in no output whatsoever, + * while a count of +0 results in the entire file being copied (just like + * +1). The implementor does not agree with these behaviors, but has + * copied them slavishly. Look for lines marked "HISTORICAL". + * + * Author: Norbert Schlenker + * Copyright: None. Released to the public domain. + * Reference: P1003.2 section 4.59 (draft 10) + * Notes: Under Minix, this program requires chmem =30000. + * Bugs: No internationalization support; all messages are in English. + */ + +/* Force visible Posix names */ +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE 1 +#endif + +/* External interfaces */ +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> + +/* External interfaces that should have been standardized into <getopt.h> */ +extern char *optarg; +extern int optind; + +/* We expect this constant to be defined in <limits.h> in a Posix program, + * but we'll specify it here just in case it's been left out. + */ +#ifndef LINE_MAX +#define LINE_MAX 2048 /* minimum acceptable lower bound */ +#endif + +/* Magic numbers suggested or required by Posix specification */ +#define SUCCESS 0 /* exit code in case of success */ +#define FAILURE 1 /* or failure */ +#define DEFAULT_COUNT 10 /* default number of lines or bytes */ +#define MIN_BUFSIZE (LINE_MAX * DEFAULT_COUNT) +#define SLEEP_INTERVAL 1 /* sleep for one second intervals with -f */ + +#define FALSE 0 +#define TRUE 1 + +/* Internal functions - prototyped under Minix */ +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(int tail, (int count, int bytes, int read_until_killed)); +_PROTOTYPE(int keep_reading, (void)); +_PROTOTYPE(void usage, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int cflag = FALSE; + int nflag = FALSE; + int fflag = FALSE; + int number = -DEFAULT_COUNT; + char *suffix; + int opt; + struct stat stat_buf; + +/* Determining whether this invocation is via the standard syntax or + * via an obsolescent one is a nasty kludge. Here it is, but there is + * no pretense at elegance. + */ + if (argc == 1) { /* simple: default read of a pipe */ + exit(tail(-DEFAULT_COUNT, 0, fflag)); + } + if ((argv[1][0] == '+') || /* OBSOLESCENT */ + (argv[1][0] == '-' && ((isdigit(argv[1][1])) || + (argv[1][1] == 'l') || + (argv[1][1] == 'c' && argv[1][2] == 'f')))) { + --argc; ++argv; + if (isdigit(argv[0][1])) { + number = (int)strtol(argv[0], &suffix, 10); + if (number == 0) { /* HISTORICAL */ + if (argv[0][0] == '+') + number = 1; + else + exit(SUCCESS); + } + } else { + number = (argv[0][0] == '+') ? DEFAULT_COUNT : -DEFAULT_COUNT; + suffix = &(argv[0][1]); + } + if (*suffix != '\0') { + if (*suffix == 'c') { + cflag = TRUE; + ++suffix; + } + else + if (*suffix == 'l') { + nflag = TRUE; + ++suffix; + } + } + if (*suffix != '\0') { + if (*suffix == 'f') { + fflag = TRUE; + ++suffix; + } + } + if (*suffix != '\0') { /* bad form: assume to be a file name */ + number = -DEFAULT_COUNT; + cflag = nflag = FALSE; + fflag = FALSE; + } else { + --argc; ++argv; + } + } else { /* new standard syntax */ + while ((opt = getopt(argc, argv, "c:fn:")) != EOF) { + switch (opt) { + case 'c': + cflag = TRUE; + if (*optarg == '+' || *optarg == '-') + number = atoi(optarg); + else + if (isdigit(*optarg)) + number = -atoi(optarg); + else + usage(); + if (number == 0) { /* HISTORICAL */ + if (*optarg == '+') + number = 1; + else + exit(SUCCESS); + } + break; + case 'f': + fflag = TRUE; + break; + case 'n': + nflag = TRUE; + if (*optarg == '+' || *optarg == '-') + number = atoi(optarg); + else + if (isdigit(*optarg)) + number = -atoi(optarg); + else + usage(); + if (number == 0) { /* HISTORICAL */ + if (*optarg == '+') + number = 1; + else + exit(SUCCESS); + } + break; + default: + usage(); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + } + + if (argc > 1 || /* too many arguments */ + (cflag && nflag)) { /* both bytes and lines specified */ + usage(); + } + + if (argc > 0) { /* an actual file */ + if (freopen(argv[0], "r", stdin) != stdin) { + fputs("tail: could not open ", stderr); + fputs(argv[0], stderr); + fputs("\n", stderr); + exit(FAILURE); + } + /* There is an optimization possibility here. If a file is being + * read, we need not look at the front of it. If we seek backwards + * from the end, we can (potentially) avoid looking at most of the + * file. Some systems fail when asked to seek backwards to a point + * before the start of the file, so we avoid that possibility. + */ + if (number < 0 && fstat(fileno(stdin), &stat_buf) == 0) { + long offset = cflag ? (long)number : (long)number * LINE_MAX; + + if (-offset < stat_buf.st_size) + fseek(stdin, offset, SEEK_END); + } + } else { + fflag = FALSE; /* force -f off when reading a pipe */ + } + exit(tail(number, cflag, fflag)); + /* NOTREACHED */ +} + +int tail(count, bytes, read_until_killed) +int count; /* lines or bytes desired */ +int bytes; /* TRUE if we want bytes */ +int read_until_killed; /* keep reading at EOF */ +{ + int c; + char *buf; /* pointer to input buffer */ + char *buf_end; /* and one past its end */ + char *start; /* pointer to first desired character in buf */ + char *finish; /* pointer past last desired character */ + int wrapped_once = FALSE; /* TRUE after buf has been filled once */ + +/* This is magic. If count is positive, it means start at the count'th + * line or byte, with the first line or byte considered number 1. Thus, + * we want to SKIP one less line or byte than the number specified. In + * the negative case, we look backward from the end of the file for the + * (count + 1)'th newline or byte, so we really want the count to be one + * LARGER than was specified (in absolute value). In either case, the + * right thing to do is: + */ + --count; + +/* Count is positive: skip the desired lines or bytes and then copy. */ + if (count >= 0) { + while (count > 0 && (c = getchar()) != EOF) { + if (bytes || c == '\n') + --count; + } + while ((c = getchar()) != EOF) { + if (putchar(c) == EOF) + return FAILURE; + } + if (read_until_killed) + return keep_reading(); + return ferror(stdin) ? FAILURE : SUCCESS; + } + +/* Count is negative: allocate a reasonably large buffer. */ + if ((buf = (char *)malloc(MIN_BUFSIZE + 1)) == (char *)NULL) { + fputs("tail: out of memory\n", stderr); + return FAILURE; + } + buf_end = buf + (MIN_BUFSIZE + 1); + +/* Read the entire file into the buffer. */ + finish = buf; + while ((c = getchar()) != EOF) { + *finish++ = c; + if (finish == buf_end) { + finish = buf; + wrapped_once = TRUE; + } + } + if (ferror(stdin)) + return FAILURE; + +/* Back up inside the buffer. The count has already been adjusted to + * back up exactly one character too far, so we will bump the buffer + * pointer once after we're done. + * + * BUG: For large line counts, the buffer may not be large enough to + * hold all the lines. The specification allows the program to + * fail in such a case - this program will simply dump the entire + * buffer's contents as its best attempt at the desired behavior. + */ + if (finish != buf || wrapped_once) { /* file was not empty */ + start = (finish == buf) ? buf_end - 1 : finish - 1; + while (start != finish) { + if ((bytes || *start == '\n') && ++count == 0) + break; + if (start == buf) { + start = buf_end - 1; + if (!wrapped_once) /* never wrapped: stop now */ + break; + } else { + --start; + } + } + if (++start == buf_end) { /* bump after going too far */ + start = buf; + } + if (finish > start) { + fwrite(start, 1, finish - start, stdout); + } else { + fwrite(start, 1, buf_end - start, stdout); + fwrite(buf, 1, finish - buf, stdout); + } + } + if (read_until_killed) + return keep_reading(); + return ferror(stdout) ? FAILURE : SUCCESS; +} + +/* Wake at intervals to reread standard input. Copy anything read to + * standard output and then go to sleep again. + */ +int keep_reading() +{ + char buf[1024]; + int n; + int i; + off_t pos; + struct stat st; + + pos = lseek(0, (off_t) 0, SEEK_CUR); + for (;;) { + for (i = 0; i < 60; i++) { + while ((n = read(0, buf, sizeof(buf))) > 0) { + if (write(1, buf, n) < 0) return FAILURE; + } + if (n < 0) return FAILURE; + + sleep(SLEEP_INTERVAL); + } + + /* Rewind if suddenly truncated. */ + if (pos != -1) { + if (fstat(0, &st) == -1) { + pos = -1; + } else + if (st.st_size < pos) { + pos = lseek(0, (off_t) 0, SEEK_SET); + } else { + pos = st.st_size; + } + } + } +} + +/* Tell the user the standard syntax. */ +void usage() +{ + fputs("Usage: tail [-f] [-c number | -n number] [file]\n", stderr); + exit(FAILURE); +} diff --git a/commands/simple/tar.c b/commands/simple/tar.c new file mode 100755 index 000000000..21894012d --- /dev/null +++ b/commands/simple/tar.c @@ -0,0 +1,1106 @@ +/* tar - tape archiver Author: Michiel Huisjes */ + +/* Usage: tar [cxt][vo][F][f] tapefile [files] + * + * attempt to make tar to conform to POSIX 1003.1 + * disclaimer: based on an old (1986) POSIX draft. + * Klamer Schutte, 20/9/89 + * + * Changes: + * Changed to handle the original minix-tar format. KS 22/9/89 + * Changed to handle BSD4.3 tar format. KS 22/9/89 + * Conform to current umask if not super-user. KS 22/9/89 + * Update usage message to show f option KS 22/9/89 + * + * + * 1) tar will back itself up, should check archive inode num(&dev) and + then check the target inode number. In verbose mode, issue + warning, in all cases ignore target. + marks@mgse Mon Sep 25 10:38:58 CDT 1989 + added global varaibles, made changes to main() and add_file(); + maks@mgse Mon Sep 25 12:09:20 CDT 1989 + + 2) tar will not notice that a file has changed size while it was being + backed up. should issue warning. + marks@mgse Mon Sep 25 10:38:58 CDT 1989 + + 3) the 'f' option was not documented in usage[]. + marks@mgse Mon Sep 25 12:03:20 CDT 1989 + changed both usage[] defines. Why are there two (one is commented out)? + ( deleted by me (was done twice) -- KS, 2/10/89 ) + * + * changed stat on tar_fd to an fstat KS 2/10/89 + * deleted mkfifo() code -- belongs in libc.a KS 2/10/89 + * made ar_dev default to -1 : an illegal device KS 2/10/89 + * made impossible to chown if normal user KS 2/10/89 + * if names in owner fields not known use numirical values KS 2/10/89 + * creat with mask 666 -- use umask if to liberal KS 2/10/89 + * allow to make directories as ../directory KS 2/10/89 + * allow tmagic field to end with a space (instead of \0) KS 2/10/89 + * correct usage of tmagic field KS 3/10/89 + * made mkdir() to return a value if directory == "." KS 3/10/89 + * made lint complains less (On a BSD 4.3 system) KS 3/10/89 + * use of directory(3) routines KS 3/10/89 + * deleted use of d_namlen selector of struct dirent KS 18/10/89 + * support mknod4(2) EC 7/7/90 + * forget inodes when link count expires EC 6/4/91 + * don't remember directories *twice*! + * added 'p' flag to ignore umask for normal user KJB 6/10/92 + * mknod4(2) out KJB 30/10/94 + * added 'D' flag to not recurse into directories KJB 19/12/94 + * status output to stdout unless 'tar cvf -' KJB 3/5/97 + * + * Bugs: + * verbose mode is not reporting consistent + * code needs cleanup + * prefix field is not used + * timestamp of a directory will not be correct if there are files to be + * unpacked in the directory + * (add you favorite bug here (or two (or three (or ...)))) +*/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <pwd.h> +#include <grp.h> +#include <tar.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <utime.h> +#include <sys/wait.h> +#include <stdio.h> /* need NULL */ +#include <errno.h> + +#define POSIX_COMP /* POSIX compatible */ +#define DIRECT_3 /* use directory(3) routines */ + +#ifdef DIRECT_3 +#ifndef BSD +/* To all minix users: i am sorry, developed this piece of code on a + * BSD system. KS 18/10/89 */ +#include <dirent.h> +#define direct dirent /* stupid BSD non-POSIX compatible name! */ +#else /* BSD */ +#include <sys/dir.h> +#include <dir.h> +#endif /* BSD */ +#endif /* DIRECT_3 */ + +#ifdef S_IFIFO +#define HAVE_FIFO /* have incorporated Simon Pooles' changes */ +#endif +#ifdef S_IFLNK +#define HAVE_SYMLINK +#endif + +typedef char BOOL; +#define TRUE 1 +#define FALSE 0 + +#define STRING_SIZE 256 /* string buffer size */ +#define HEADER_SIZE TBLOCK +#define NAME_SIZE NAMSIZ +/* #define BLOCK_BOUNDARY 20 -- not in POSIX ! */ + +typedef union hblock HEADER; + +/* Make the MINIX member names overlap to the POSIX names */ +#define m_name name +#define m_mode mode +#define m_uid uid +#define m_gid gid +#define m_size size +#define m_time mtime +#define m_checksum chksum +#define m_linked typeflag +#define m_link linkname +#define hdr_block dummy +#define m header +#define member dbuf + +#if 0 /* original structure -- see tar.h for new + * structure */ +typedef union { + char hdr_block[HEADER_SIZE]; + struct m { + char m_name[NAME_SIZE]; + char m_mode[8]; + char m_uid[8]; + char m_gid[8]; + char m_size[12]; + char m_time[12]; + char m_checksum[8]; + char m_linked; + char m_link[NAME_SIZE]; + } member; +} HEADER; + +#endif + +/* Structure used to note links */ +struct link { + ino_t ino; + dev_t dev; + nlink_t nlink; + struct link *next; + char name[1]; +} *link_top = NULL; + +HEADER header; + +#define INT_TYPE (sizeof(header.member.m_uid)) +#define LONG_TYPE (sizeof(header.member.m_size)) + +#define NIL_HEADER ((HEADER *) 0) +#define NIL_PTR ((char *) 0) +#define TBLOCK_SIZE TBLOCK + +#define flush() print(NIL_PTR) + +BOOL show_fl, creat_fl, ext_fl; + +int tar_fd; +/* Char usage[] = "Usage: tar [cxt] tarfile [files]."; */ +char usage[] = "Usage: tar [cxt][vo][F][f] tarfile [files]."; +char io_buffer[TBLOCK_SIZE]; +char path[NAME_SIZE]; +char pathname[NAME_SIZE]; +int force_flag = 0; +#ifdef ORIGINAL_DEFAULTS +int chown_flag = 1; +int verbose_flag = 1; +#else +int chown_flag = 0; +int verbose_flag = 0; +#endif +int norec_flag = 0; + +/* Make sure we don't tar ourselves. marks@mgse Mon Sep 25 12:06:28 CDT 1989 */ +ino_t ar_inode; /* archive inode number */ +dev_t ar_dev; /* archive device number */ + +int total_blocks; +int u_mask; /* one's complement of current umask */ + +#define block_size() (int) ((convert(header.member.m_size, LONG_TYPE) \ + + (long) TBLOCK_SIZE - 1) / (long) TBLOCK_SIZE) + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void error, (char *s1, char *s2)); +_PROTOTYPE(BOOL get_header, (void)); +_PROTOTYPE(void tarfile, (void)); +_PROTOTYPE(void skip_entry, (void)); +_PROTOTYPE(void extract, (char *file)); +_PROTOTYPE(void delete, (char *file)); +_PROTOTYPE(void do_chown, (char *file)); +_PROTOTYPE(void timestamp, (char *file)); +_PROTOTYPE(void copy, (char *file, int from, int to, long bytes)); +_PROTOTYPE(long convert, (char str[], int type)); +_PROTOTYPE(int checksum, (void)); +_PROTOTYPE(int is_dir, (char *file)); +_PROTOTYPE(char *path_name, (char *file)); +_PROTOTYPE(void add_path, (char *name)); +_PROTOTYPE(void add_file, (char *file)); +_PROTOTYPE(void verb_print, (char *s1, char *s2)); +_PROTOTYPE(void add_close, (int fd)); +_PROTOTYPE(int add_open, (char *file, struct stat * st)); +_PROTOTYPE(void make_header, (char *file, struct stat * st)); +_PROTOTYPE(void is_added, (struct stat * st, char *file)); +_PROTOTYPE(void is_deleted, (struct stat * st)); +_PROTOTYPE(char *is_linked, (struct stat * st)); +_PROTOTYPE(void clear_header, (void)); +_PROTOTYPE(void adjust_boundary, (void)); +_PROTOTYPE(void mread, (int fd, char *address, int bytes)); +_PROTOTYPE(void mwrite, (int fd, char *address, int bytes)); +_PROTOTYPE(int bread, (int fd, char *address, int bytes)); +_PROTOTYPE(int bwrite, (int fd, char *address, int bytes)); +_PROTOTYPE(void print, (char *str)); +_PROTOTYPE(char *num_out, (long number)); +_PROTOTYPE(void string_print, (char *buffer, char *fmt,...)); + +void error(s1, s2) +char *s1, *s2; +{ + string_print(NIL_PTR, "%s %s\n", s1, s2 ? s2 : ""); + flush(); + exit(1); +} + +int main(argc, argv) +int argc; +register char *argv[]; +{ + register char *mem_name; + register char *ptr; + struct stat st; + int i; + + if (argc < 3) error(usage, NIL_PTR); + + for (ptr = argv[1]; *ptr; ptr++) { + switch (*ptr) { + case 'c': creat_fl = TRUE; break; + case 'x': ext_fl = TRUE; break; + case 't': show_fl = TRUE; break; + case 'v': /* verbose output -Dal */ + verbose_flag = !verbose_flag; + break; + case 'o': /* chown/chgrp files -Dal */ + chown_flag = TRUE; + break; + case 'F': /* IGNORE ERRORS -Dal */ + force_flag = TRUE; + break; + case 'f': /* standard U*IX usage -KS */ + break; + case 'p': /* restore file modes right, ignore umask. */ + (void) umask(0); + break; + case 'D': /* do not recursively add directories. */ + norec_flag = TRUE; + break; + default: error(usage, NIL_PTR); + } + } + + if (creat_fl + ext_fl + show_fl != 1) error(usage, NIL_PTR); + + if (strcmp(argv[2], "-") == 0)/* only - means stdin/stdout - KS */ + tar_fd = creat_fl ? 1 : 0; /* '-' means used + * stdin/stdout -Dal */ + else + tar_fd = creat_fl ? creat(argv[2], 0666) : open(argv[2], O_RDONLY); + + if (tar_fd < 0) error("Cannot open ", argv[2]); + + if (geteuid()) { /* check if super-user */ + int save_umask; + save_umask = umask(0); + u_mask = ~save_umask; + umask(save_umask); + chown_flag = TRUE; /* normal user can't chown */ + } else + u_mask = ~0; /* don't restrict if 'privileged utility' */ + + ar_dev = -1; /* impossible device nr */ + if (creat_fl) { + if (tar_fd > 1 && fstat(tar_fd, &st) < 0) + error("Can't stat ", argv[2]); /* will never be here, + * right? */ + else { /* get archive inode & device */ + ar_inode = st.st_ino; /* save files inode */ + ar_dev = st.st_dev; /* save files device */ + } /* marks@mgse Mon Sep 25 11:30:45 CDT 1989 */ + + for (i = 3; i < argc; i++) { + add_file(argv[i]); + path[0] = '\0'; + } + adjust_boundary(); + } else if (ext_fl) { + /* Extraction code moved here from tarfile() MSP */ + while (get_header()) { + mem_name = header.member.m_name; + if (is_dir(mem_name)) { + for (ptr = mem_name; *ptr; ptr++); + *(ptr - 1) = '\0'; + header.dbuf.typeflag = '5'; + } + for (i = 3; i < argc; i++) + if (!strncmp(argv[i], mem_name, strlen(argv[i]))) + break; + if (argc == 3 || (i < argc)) { + extract(mem_name); + } else if (header.dbuf.typeflag == '0' || + header.dbuf.typeflag == 0 || + header.dbuf.typeflag == ' ') + skip_entry(); + flush(); + } + } else + tarfile(); /* tarfile() justs prints info. now MSP */ + + flush(); + return(0); +} + +BOOL get_header() +{ + register int check; + + mread(tar_fd, (char *) &header, sizeof(header)); + if (header.member.m_name[0] == '\0') return FALSE; + + if (force_flag) /* skip checksum verification -Dal */ + return TRUE; + + check = (int) convert(header.member.m_checksum, INT_TYPE); + + if (check != checksum()) error("Tar: header checksum error.", NIL_PTR); + + return TRUE; +} + +/* Tarfile() just lists info about archive now; as of the t flag. */ +/* Extraction has been moved into main() as that needs access to argv[] */ + +void tarfile() +{ + register char *mem_name; + + while (get_header()) { + mem_name = header.member.m_name; + string_print(NIL_PTR, "%s%s", mem_name, + (verbose_flag ? " " : "\n")); + switch (header.dbuf.typeflag) { + case '1': + verb_print("linked to", header.dbuf.linkname); + break; + case '2': + verb_print("symbolic link to", header.dbuf.linkname); + break; + case '6': verb_print("", "fifo"); break; + case '3': + case '4': + if (verbose_flag) { + char sizebuf[TSIZLEN + 1]; + + strncpy(sizebuf, header.dbuf.size, (size_t) TSIZLEN); + sizebuf[TSIZLEN] = 0; + string_print(NIL_PTR, + "%s special file major %s minor %s\n", + (header.dbuf.typeflag == '3' ? + "character" : "block"), + header.dbuf.devmajor, + header.dbuf.devminor, + sizebuf); + } + break; + case '0': /* official POSIX */ + case 0: /* also mentioned in POSIX */ + case ' ': /* ofetn used */ + if (!is_dir(mem_name)) { + if (verbose_flag) + string_print(NIL_PTR, "%d tape blocks\n", + block_size()); + skip_entry(); + break; + } else /* FALL TROUGH */ + case '5': + verb_print("", "directory"); + break; + default: + string_print(NIL_PTR, "not recogised item %d\n", + header.dbuf.typeflag); + } + flush(); + } +} + +void skip_entry() +{ + register int blocks = block_size(); + + while (blocks--) (void) bread(tar_fd, io_buffer, TBLOCK_SIZE); +} + +void extract(file) +register char *file; +{ + register int fd, r; + char *pd1, *pd2; /* walk thru failed directory path */ + + switch (header.dbuf.typeflag) { + case '1': /* Link */ + delete(file); + if (link(header.member.m_link, file) < 0) + string_print(NIL_PTR, "Cannot link %s to %s: %s\n", + header.member.m_link, file, strerror(errno)); + else if (verbose_flag) + string_print(NIL_PTR, "Linked %s to %s\n", + header.member.m_link, file); + return; + case '5': /* directory */ + if (!(file[0] == '.' && file[1] == '\0')) delete(file); + if ((file[0] == '.' && file[1] == '\0') || mkdir(file, 0700) == 0) { + do_chown(file); + verb_print("created directory", file); + } else { + string_print(NIL_PTR, "Can't make directory %s: %s\n", + file, strerror(errno)); + } + return; + case '3': /* character special */ + case '4': /* block special */ + { + int dmajor, dminor, mode; + + dmajor = (int) convert(header.dbuf.devmajor, INT_TYPE); + dminor = (int) convert(header.dbuf.devminor, INT_TYPE); + mode = (header.dbuf.typeflag == '3' ? S_IFCHR : S_IFBLK); + delete(file); + if (mknod(file, mode, (dmajor << 8 | dminor)) == 0) { + if (verbose_flag) string_print(NIL_PTR, + "made %s special file major %s minor %s\n", + (header.dbuf.typeflag == '3' ? + "character" : "block"), + header.dbuf.devmajor, + header.dbuf.devminor); + do_chown(file); + } + else + { + string_print(NIL_PTR, + "cannot make %s special file major %s minor %s: %s\n", + (header.dbuf.typeflag == '3' ? + "character" : "block"), + header.dbuf.devmajor, + header.dbuf.devminor, + strerror(errno)); + } + return; + } + case '2': /* symbolic link */ +#ifdef HAVE_SYMLINK + delete(file); + if (symlink(header.member.m_link, file) < 0) + string_print(NIL_PTR, "Cannot make symbolic link %s to %s: %s\n", + header.member.m_link, file, strerror(errno)); + else if (verbose_flag) + string_print(NIL_PTR, "Symbolic link %s to %s\n", + header.member.m_link, file); + return; +#endif + case '7': /* contiguous file -- what is this (KS) */ + print("Not implemented file type\n"); + return; /* not implemented, but break out */ +#ifdef HAVE_FIFO + case '6': /* fifo */ + delete(file); + if (mkfifo(file, 0) == 0) { /* is chmod'ed in do_chown */ + do_chown(file); + verb_print("made fifo", file); + } else + string_print(NIL_PTR, "Can't make fifo %s: %s\n", + file, strerror(errno)); + return; +#endif + } + + /* Create regular file. If failure, try to make missing directories. */ + if ((fd = creat(file, 0600)) < 0) { + pd1 = file; + while ((pd2 = index(pd1, '/')) > (char *) 0) { + *pd2 = '\0'; + if (access(file, 1) < 0) + if (mkdir(file, 0777) < 0) { + string_print(NIL_PTR, "Cannot mkdir %s: %s\n", + file, strerror(errno)); + return; + } else + string_print(NIL_PTR, "Made directory %s\n", file); + *pd2 = '/'; + pd1 = ++pd2; + } + if ((fd = creat(file, 0600)) < 0) { + string_print(NIL_PTR, "Cannot create %s: %s\n", + file, strerror(errno)); + return; + } + } + copy(file, tar_fd, fd, convert(header.member.m_size, LONG_TYPE)); + (void) close(fd); + + do_chown(file); +} + +void delete(file) +char *file; +{ + /* remove a file or an empty directory */ + struct stat stbuf; + + if (stat(file, &stbuf) < 0) return; + + if (S_ISDIR(stbuf.st_mode)) (void) rmdir(file); else (void) unlink(file); + /* leave error reporting to the create following soon. */ +} + +void do_chown(file) +char *file; +{ + int uid = -1, gid = -1; /* these are illegal ??? -- KS */ + + if (!chown_flag) { /* set correct owner and group -Dal */ + if (header.dbuf.magic[TMAGLEN] == ' ') + header.dbuf.magic[TMAGLEN] = '\0'; /* some tars out there + * ... */ + if (strncmp(TMAGIC, header.dbuf.magic, (size_t) TMAGLEN)) { + struct passwd *pwd; + struct group *grp; + + pwd = getpwnam(header.dbuf.uname); + if (pwd != NULL) uid = pwd->pw_uid; + grp = getgrnam(header.dbuf.gname); + if (grp != NULL) gid = grp->gr_gid; + } + if (uid == -1) uid = (int) convert(header.member.m_uid, INT_TYPE); + if (gid == -1) gid = (int) convert(header.member.m_gid, INT_TYPE); + chown(file, uid, gid); + } + chmod(file, u_mask & (int) convert(header.member.m_mode, INT_TYPE)); + + /* Should there be a timestamp if the chown failes? -- KS */ + timestamp(file); + +} + +void timestamp(file) +char *file; +{ + struct utimbuf buf; + + buf.modtime = buf.actime = convert(header.dbuf.mtime, LONG_TYPE); + utime(file, &buf); +} + +void copy(file, from, to, bytes) +char *file; +int from, to; +register long bytes; +{ + register int rest; + int blocks = (int) ((bytes + (long) TBLOCK_SIZE - 1) / (long) TBLOCK_SIZE); + + if (verbose_flag) + string_print(NIL_PTR, "%s, %d tape blocks\n", file, blocks); + + while (blocks--) { + (void) bread(from, io_buffer, TBLOCK_SIZE); + rest = (bytes > (long) TBLOCK_SIZE) ? TBLOCK_SIZE : (int) bytes; + mwrite(to, io_buffer, (to == tar_fd) ? TBLOCK_SIZE : rest); + bytes -= (long) rest; + } +} + +long convert(str, type) +char str[]; +int type; +{ + register long ac = 0L; + register int i; + + for (i = 0; i < type; i++) { + if (str[i] >= '0' && str[i] <= '7') { + ac <<= 3; + ac += (long) (str[i] - '0'); + } + } + + return ac; +} + +int checksum() +{ + register char *ptr = header.member.m_checksum; + register int ac = 0; + + while (ptr < &header.member.m_checksum[INT_TYPE]) *ptr++ = ' '; + + ptr = header.hdr_block; + while (ptr < &header.hdr_block[TBLOCK_SIZE]) ac += *ptr++; + + return ac; +} + +int is_dir(file) +register char *file; +{ + while (*file++ != '\0'); + + return(*(file - 2) == '/'); +} + + +char *path_name(file) +register char *file; +{ + + string_print(pathname, "%s%s", path, file); + return pathname; +} + +void add_path(name) +register char *name; +{ + register char *path_ptr = path; + + while (*path_ptr) path_ptr++; + + if (name == NIL_PTR) { + while (*path_ptr-- != '/'); + while (*path_ptr != '/' && path_ptr != path) path_ptr--; + if (*path_ptr == '/') path_ptr++; + *path_ptr = '\0'; + } else { + while (*name) { + if (path_ptr == &path[NAME_SIZE]) + error("Pathname too long", NIL_PTR); + *path_ptr++ = *name++; + } + *path_ptr++ = '/'; + *path_ptr = '\0'; + } +} + +/* + * add a file to the archive +*/ +void add_file(file) +register char *file; +{ + struct stat st; + char *linkname; + register int fd = -1; + char namebuf[16]; /* -Dal */ + char cwd[129]; /* -KS */ + +#ifdef HAVE_SYMLINK + if (lstat(file, &st) < 0) { +#else + if (stat(file, &st) < 0) { +#endif + string_print(NIL_PTR, "%s: %s\n", file, strerror(errno)); + return; + } + if (st.st_dev == ar_dev && st.st_ino == ar_inode) { + string_print(NIL_PTR, "Cannot tar current archive file (%s)\n", file); + return; + } /* marks@mgse Mon Sep 25 12:06:28 CDT 1989 */ + if ((fd = add_open(file, &st)) < 0) { + string_print(NIL_PTR, "Cannot open %s\n", file); + return; + } + make_header(path_name(file), &st); + if ((linkname = is_linked(&st)) != NULL) { + strncpy(header.dbuf.linkname, linkname, (size_t) NAMSIZ); + header.dbuf.typeflag = '1'; + if (verbose_flag) string_print(NIL_PTR, "linked %s to %s\n", + header.dbuf.linkname, file); + string_print(header.member.m_checksum, "%I ", checksum()); + mwrite(tar_fd, (char *) &header, sizeof(header)); + } else { + is_added(&st, file); + switch (st.st_mode & S_IFMT) { + case S_IFREG: + header.dbuf.typeflag = '0'; + string_print(header.member.m_checksum, "%I ", checksum()); + mwrite(tar_fd, (char *) &header, sizeof(header)); + copy(path_name(file), fd, tar_fd, (long) st.st_size); + break; + case S_IFDIR: + header.dbuf.typeflag = '5'; + string_print(header.member.m_checksum, "%I ", checksum()); + mwrite(tar_fd, (char *) &header, sizeof(header)); + verb_print("read directory", file); + if (norec_flag) break; + if (NULL == getcwd(cwd, (int) sizeof cwd)) + string_print(NIL_PTR, "Error: cannot getcwd()\n"); + else if (chdir(file) < 0) + string_print(NIL_PTR, "Cannot chdir to %s: %s\n", + file, strerror(errno)); + else { + add_path(file); +#ifdef DIRECT_3 + { + DIR *dirp; + struct direct *dp; + struct stat dst; + + add_close(fd); + fd= 0; + dirp = opendir("."); + while (NULL != (dp = readdir(dirp))) + if (strcmp(dp->d_name, ".") == 0) + is_linked(&st); + else if (strcmp(dp->d_name, "..") == 0) { + if (stat("..", &dst) == 0) + is_linked(&dst); + } else { + strcpy(namebuf, dp->d_name); + add_file(namebuf); + } + closedir(dirp); + } +#else + { + int i; + struct direct dir; + struct stat dst; + + for (i = 0; i < 2; i++) { /* . and .. */ + mread(fd, &dir, sizeof(dir)); + if (strcmp(dir.d_name, ".") == 0) + is_linked(&st); + else if (strcmp(dir.d_name, "..") == 0) { + if (stat("..", &dst) == 0) + is_linked(&dst); + } else + break; + } + while (bread(fd, &dir, sizeof(dir)) == sizeof(dir)) + if (dir.d_ino) { + strncpy(namebuf, dir.d_name, + (size_t) DIRSIZ); + namebuf[DIRSIZ] = '\0'; + add_file(namebuf); + } + } +#endif + chdir(cwd); + add_path(NIL_PTR); + *file = 0; + } + break; +#ifdef HAVE_SYMLINK + case S_IFLNK: + { + int i; + + header.dbuf.typeflag = '2'; + verb_print("read symlink", file); + i = readlink(file, + header.dbuf.linkname, + sizeof(header.dbuf.linkname) - 1); + if (i < 0) { + string_print(NIL_PTR, + "Cannot read symbolic link %s: %s\n", + file, strerror(errno)); + return; + } + header.dbuf.linkname[i] = 0; + string_print(header.member.m_checksum, "%I ", checksum()); + mwrite(tar_fd, (char *) &header, sizeof(header)); + break; + } +#endif +#ifdef HAVE_FIFO + case S_IFIFO: + header.dbuf.typeflag = '6'; + verb_print("read fifo", file); + string_print(header.member.m_checksum, "%I ", checksum()); + mwrite(tar_fd, (char *) &header, sizeof(header)); + break; +#endif + case S_IFBLK: + header.dbuf.typeflag = '4'; + if (verbose_flag) { + char sizebuf[TSIZLEN + 1]; + + strncpy(sizebuf, header.dbuf.size, (size_t) TSIZLEN); + sizebuf[TSIZLEN] = 0; + string_print(NIL_PTR, + "read block device %s major %s minor %s\n", + file, header.dbuf.devmajor, header.dbuf.devminor, sizebuf); + } + string_print(header.member.m_checksum, "%I ", checksum()); + mwrite(tar_fd, (char *) &header, sizeof(header)); + break; + case S_IFCHR: + header.dbuf.typeflag = '3'; + if (verbose_flag) string_print(NIL_PTR, + "read character device %s major %s minor %s\n", + file, header.dbuf.devmajor, header.dbuf.devminor); + string_print(header.member.m_checksum, "%I ", checksum()); + mwrite(tar_fd, (char *) &header, sizeof(header)); + break; + default: + is_deleted(&st); + string_print(NIL_PTR, "Tar: %s unknown file type. Not added.\n", file); + *file = 0; + } + } + + flush(); + add_close(fd); +} + +void verb_print(s1, s2) +char *s1, *s2; +{ + if (verbose_flag) string_print(NIL_PTR, "%s: %s\n", s1, s2); +} + +void add_close(fd) +int fd; +{ + if (fd != 0) close(fd); +} + +/* + * open file 'file' to be added to archive, return file descriptor +*/ +int add_open(file, st) +char *file; +struct stat *st; +{ + int fd; + if (((st->st_mode & S_IFMT) != S_IFREG) && + ((st->st_mode & S_IFMT) != S_IFDIR)) + return 0; + fd = open(file, O_RDONLY); + if (fd == -1) + fprintf(stderr, "open failed: %s\n", strerror(errno)); + return fd; +} + +void make_header(file, st) +char *file; +register struct stat *st; +{ + register char *ptr = header.member.m_name; + struct passwd *pwd; + struct group *grp; + + clear_header(); + + while (*ptr++ = *file++); + + if ((st->st_mode & S_IFMT) == S_IFDIR) { /* fixed test -Dal */ + *(ptr - 1) = '/'; + } + string_print(header.member.m_mode, "%I ", st->st_mode & 07777); + string_print(header.member.m_uid, "%I ", st->st_uid); + string_print(header.member.m_gid, "%I ", st->st_gid); + if ((st->st_mode & S_IFMT) == S_IFREG) + string_print(header.member.m_size, "%L ", st->st_size); + else + strncpy(header.dbuf.size, "0", (size_t) TSIZLEN); + string_print(header.member.m_time, "%L ", st->st_mtime); + strncpy(header.dbuf.magic, TMAGIC, (size_t) TMAGLEN); + header.dbuf.version[0] = 0; + header.dbuf.version[1] = 0; + pwd = getpwuid(st->st_uid); + strncpy(header.dbuf.uname, + (pwd != NULL ? pwd->pw_name : "nobody"), TUNMLEN); + grp = getgrgid(st->st_gid); + strncpy(header.dbuf.gname, + (grp != NULL ? grp->gr_name : "nobody"), TGNMLEN); + if (st->st_mode & (S_IFBLK | S_IFCHR)) { + string_print(header.dbuf.devmajor, "%I ", (st->st_rdev >> 8)); + string_print(header.dbuf.devminor, "%I ", (st->st_rdev & 0xFF)); + } + header.dbuf.prefix[0] = 0; +} + +void is_added(st, file) +struct stat *st; +char *file; +{ + struct link *new; + char *name; + + if ((*file == 0) || (st->st_nlink == 1)) return; + name = path_name(file); + new = (struct link *) malloc(sizeof(struct link) + strlen(name)); + if (new == NULL) { + print("Out of memory\n"); + return; + } + new->next = link_top; + new->dev = st->st_dev; + new->ino = st->st_ino; + new->nlink = st->st_nlink - 1; + strcpy(new->name, name); + link_top = new; +} + +void is_deleted(st) +struct stat *st; +{ + struct link *old; + + if ((old = link_top) != NULL) { + link_top = old->next; + free(old); + } +} + +char *is_linked(st) +struct stat *st; +{ + struct link *cur = link_top; + struct link **pre = &link_top; + static char name[NAMSIZ]; + + while (cur != NULL) + if ((cur->dev != st->st_dev) || (cur->ino != st->st_ino)) { + pre = &cur->next; + cur = cur->next; + } else { + if (--cur->nlink == 0) { + *pre = cur->next; + strncpy(name, cur->name, NAMSIZ); + return name; + } + return cur->name; + } + return NULL; +} + +void clear_header() +{ + register char *ptr = header.hdr_block; + + while (ptr < &header.hdr_block[TBLOCK_SIZE]) *ptr++ = '\0'; +} + +void adjust_boundary() +{ + clear_header(); + mwrite(tar_fd, (char *) &header, sizeof(header)); +#ifndef POSIX_COMP + while (total_blocks++ < BLOCK_BOUNDARY) + mwrite(tar_fd, (char *) &header, sizeof(header)); +#else + mwrite(tar_fd, (char *) &header, sizeof(header)); +#endif + (void) close(tar_fd); +} + +void mread(fd, address, bytes) +int fd, bytes; +char *address; +{ + if (bread(fd, address, bytes) != bytes) error("Tar: read error.", NIL_PTR); +} + +void mwrite(fd, address, bytes) +int fd, bytes; +char *address; +{ + if (bwrite(fd, address, bytes) != bytes) error("Tar: write error.", NIL_PTR); + + total_blocks++; +} + +int bread(fd, address, bytes) +int fd, bytes; +char *address; +{ + int n = 0, r; + + while (n < bytes) { + if ((r = read(fd, address + n, bytes - n)) <= 0) { + if (r < 0) return r; + break; + } + n += r; + } + return n; +} + +int bwrite(fd, address, bytes) +int fd, bytes; +char *address; +{ + int n = 0, r; + + while (n < bytes) { + if ((r = write(fd, address + n, bytes - n)) <= 0) { + if (r < 0) return r; + break; + } + n += r; + } + return n; +} + +char output[TBLOCK_SIZE]; +void print(str) +register char *str; +{ + int fd = (tar_fd == 1 ? 2 : 1); + static int indx = 0; + + if (str == NIL_PTR) { + write(fd, output, indx); + indx = 0; + return; + } + while (*str) { + output[indx++] = *str++; + if (indx == TBLOCK_SIZE) { + write(fd, output, TBLOCK_SIZE); + indx = 0; + } + } +} + +char *num_out(number) +register long number; +{ + static char num_buf[12]; + register int i; + + for (i = 11; i--;) { + num_buf[i] = (number & 07) + '0'; + number >>= 3; + } + + return num_buf; +} + +/*VARARGS2*/ +#if __STDC__ +void string_print(char *buffer, char *fmt,...) +#else +void string_print(buffer, fmt) +char *buffer; +char *fmt; +#endif +{ + va_list args; + register char *buf_ptr; + char *scan_ptr; + char buf[STRING_SIZE]; + BOOL pr_fl, i; + + if (pr_fl = (buffer == NIL_PTR)) buffer = buf; + + va_start(args, fmt); + buf_ptr = buffer; + while (*fmt) { + if (*fmt == '%') { + fmt++; + switch (*fmt++) { + case 's': + scan_ptr = (char *) (va_arg(args, char *)); + break; + case 'I': + scan_ptr = num_out((long) (va_arg(args, int))); + for (i = 0; i < 5; i++) scan_ptr++; + break; + case 'L': + scan_ptr = num_out((long) va_arg(args, long)); + break; + case 'd': + scan_ptr = num_out((long) va_arg(args, int)); + while (*scan_ptr == '0') scan_ptr++; + scan_ptr--; + break; + default: scan_ptr = ""; + } + while (*buf_ptr++ = *scan_ptr++); + buf_ptr--; + } else + *buf_ptr++ = *fmt++; + } + *buf_ptr = '\0'; + + if (pr_fl) print(buffer); + va_end(args); +} diff --git a/commands/simple/tcpd.c b/commands/simple/tcpd.c new file mode 100755 index 000000000..f66964eae --- /dev/null +++ b/commands/simple/tcpd.c @@ -0,0 +1,303 @@ +/* +tcpd.c +*/ + +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <signal.h> +#include <minix/config.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <net/hton.h> +#include <net/netlib.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/netdb.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> + +/* This program can be compiled to be paranoid, i.e. check incoming connection + * according to an access file, or to trust anyone. The much smaller "trust + * 'em" binary will call the paranoid version if the access file exists. + */ + +static char *arg0, *service; +static unsigned nchildren; + +static void report(const char *label) +{ + int err= errno; + + fprintf(stderr, "%s %s: %s: %s\n", arg0, service, label, strerror(err)); + errno= err; +} + +static void sigchld(int sig) +{ + while (waitpid(0, NULL, WNOHANG) > 0) { + if (nchildren > 0) nchildren--; + } +} + +static void release(int *fd) +{ + if (*fd != -1) { + close(*fd); + *fd= -1; + } +} + +static void usage(void) +{ + fprintf(stderr, + "Usage: %s [-d] [-m maxclients] service program [arg ...]\n", + arg0); + exit(1); +} + +int main(int argc, char **argv) +{ + tcpport_t port; + struct nwio_tcpcl tcplistenopt; + struct nwio_tcpconf tcpconf; + struct nwio_tcpopt tcpopt; + char *tcp_device; + struct servent *servent; + int tcp_fd, client_fd, count, r; + int pfd[2]; + unsigned stall= 0; + struct sigaction sa; + sigset_t chldmask, chldunmask, oldmask; + char **progv; + +#if !PARANOID +# define debug 0 +# define max_children ((unsigned) -1) + arg0= argv[0]; + + /* Switch to the paranoid version of me if there are flags, or if + * there is an access file. + */ + if (argv[1][0] == '-' || access(_PATH_SERVACCES, F_OK) == 0) { + execv("/usr/bin/tcpdp", argv); + report("tcpdp"); + exit(1); + } + if (argc < 3) usage(); + service= argv[1]; + progv= argv+2; + +#else /* PARANOID */ + int debug, i; + unsigned max_children; + + arg0= argv[0]; + debug= 0; + max_children= -1; + i= 1; + while (i < argc && argv[i][0] == '-') { + char *opt= argv[i++] + 1; + unsigned long m; + char *end; + + if (*opt == '-' && opt[1] == 0) break; /* -- */ + + while (*opt != 0) switch (*opt++) { + case 'd': + debug= 1; + break; + case 'm': + if (*opt == 0) { + if (i == argc) usage(); + opt= argv[i++]; + } + m= strtoul(opt, &end, 10); + if (m <= 0 || m > UINT_MAX || *end != 0) usage(); + max_children= m; + opt= ""; + break; + default: + usage(); + } + } + service= argv[i++]; + progv= argv+i; + if (i >= argc) usage(); +#endif + + /* The interface to start the service on. */ + if ((tcp_device= getenv("TCP_DEVICE")) == NULL) tcp_device= TCP_DEVICE; + + /* Let SIGCHLD interrupt whatever I'm doing. */ + sigemptyset(&chldmask); + sigaddset(&chldmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &chldmask, &oldmask); + chldunmask= oldmask; + sigdelset(&chldunmask, SIGCHLD); + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = sigchld; + sigaction(SIGCHLD, &sa, NULL); + + /* Open a socket to the service I'm to serve. */ + if ((servent= getservbyname(service, "tcp")) == NULL) { + unsigned long p; + char *end; + + p= strtoul(service, &end, 0); + if (p <= 0 || p > 0xFFFF || *end != 0) { + fprintf(stderr, "%s: %s: Unknown service\n", + arg0, service); + exit(1); + } + port= htons((tcpport_t) p); + } else { + port= servent->s_port; + + if (debug) + { + fprintf(stderr, "%s %s: listening to port %u\n", + arg0, service, ntohs(port)); + } + } + + /* No client yet. */ + client_fd= -1; + + while (1) { + if ((tcp_fd= open(tcp_device, O_RDWR)) < 0) { + report(tcp_device); + if (errno == ENOENT || errno == ENODEV + || errno == ENXIO) { + exit(1); + } + goto bad; + } + + tcpconf.nwtc_flags= NWTC_LP_SET | NWTC_UNSET_RA | NWTC_UNSET_RP; + tcpconf.nwtc_locport= port; + + if (ioctl(tcp_fd, NWIOSTCPCONF, &tcpconf) < 0) { + report("Can't configure TCP channel"); + exit(1); + } + + tcpopt.nwto_flags= NWTO_DEL_RST; + + if (ioctl(tcp_fd, NWIOSTCPOPT, &tcpopt) < 0) { + report("Can't set TCP options"); + exit(1); + } + + if (client_fd != -1) { + /* We have a client, so start a server for it. */ + + tcpopt.nwto_flags= 0; + (void) ioctl(client_fd, NWIOSTCPOPT, &tcpopt); + + fflush(NULL); + + /* Create a pipe to serve as an error indicator. */ + if (pipe(pfd) < 0) { + report("pipe"); + goto bad; + } + (void) fcntl(pfd[1], F_SETFD, + fcntl(pfd[1], F_GETFD) | FD_CLOEXEC); + + /* Fork and exec. */ + switch (fork()) { + case -1: + report("fork"); + close(pfd[0]); + close(pfd[1]); + goto bad; + case 0: + close(tcp_fd); + close(pfd[0]); +#if PARANOID + /* Check if access to this service allowed. */ + if (ioctl(client_fd, NWIOGTCPCONF, &tcpconf) == 0 + && tcpconf.nwtc_remaddr != tcpconf.nwtc_locaddr + && !servxcheck(tcpconf.nwtc_remaddr, argv[1], NULL) + ) { + exit(1); + } +#endif + sigprocmask(SIG_SETMASK, &oldmask, NULL); + dup2(client_fd, 0); + dup2(client_fd, 1); + close(client_fd); + execvp(progv[0], progv); + report(progv[0]); + write(pfd[1], &errno, sizeof(errno)); + exit(1); + default: + nchildren++; + release(&client_fd); + close(pfd[1]); + r= read(pfd[0], &errno, sizeof(errno)); + close(pfd[0]); + if (r != 0) goto bad; + break; + } + } + + while (nchildren >= max_children) { + /* Too many clients, wait for one to die off. */ + sigsuspend(&chldunmask); + } + + /* Wait for a new connection. */ + sigprocmask(SIG_UNBLOCK, &chldmask, NULL); + + tcplistenopt.nwtcl_flags= 0; + while (ioctl(tcp_fd, NWIOTCPLISTEN, &tcplistenopt) < 0) { + if (errno != EINTR) { + if (errno != EAGAIN || debug) { + report("Unable to listen"); + } + goto bad; + } + } + sigprocmask(SIG_BLOCK, &chldmask, NULL); + + /* We got a connection. */ + client_fd= tcp_fd; + tcp_fd= -1; + + if (debug && ioctl(client_fd, NWIOGTCPCONF, &tcpconf) == 0) { + fprintf(stderr, "%s %s: Connection from %s:%u\n", + arg0, service, + inet_ntoa(tcpconf.nwtc_remaddr), + ntohs(tcpconf.nwtc_remport)); + } + /* All is well, no need to stall. */ + stall= 0; + continue; + + bad: + /* All is not well, release resources. */ + release(&tcp_fd); + release(&client_fd); + + /* Wait a bit if this happens more than once. */ + if (stall != 0) { + if (debug) { + fprintf(stderr, "%s %s: stalling %u second%s\n", + arg0, service, + stall, stall == 1 ? "" : "s"); + } + sleep(stall); + stall <<= 1; + } else { + stall= 1; + } + } +} diff --git a/commands/simple/tee.c b/commands/simple/tee.c new file mode 100755 index 000000000..4a2222dc9 --- /dev/null +++ b/commands/simple/tee.c @@ -0,0 +1,66 @@ +/* tee - pipe fitting Author: Paul Polderman */ + +#include <sys/types.h> +#include <fcntl.h> +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> +#include <minix/minlib.h> + +#define MAXFD 18 +#define CHUNK_SIZE 4096 + +int fd[MAXFD]; + +_PROTOTYPE(int main, (int argc, char **argv)); + +int main(argc, argv) +int argc; +char **argv; +{ + char iflag = 0, aflag = 0; + char buf[CHUNK_SIZE]; + int i, s, n; + + argv++; + --argc; + while (argc > 0 && argv[0][0] == '-') { + switch (argv[0][1]) { + case 'i': /* Interrupt turned off. */ + iflag++; + break; + case 'a': /* Append to outputfile(s), instead of + * overwriting them. */ + aflag++; + break; + default: + std_err("Usage: tee [-i] [-a] [files].\n"); + exit(1); + } + argv++; + --argc; + } + fd[0] = 1; /* Always output to stdout. */ + for (s = 1; s < MAXFD && argc > 0; --argc, argv++, s++) { + if (aflag && (fd[s] = open(*argv, O_RDWR)) >= 0) { + lseek(fd[s], 0L, SEEK_END); + continue; + } else { + if ((fd[s] = creat(*argv, 0666)) >= 0) continue; + } + std_err("Cannot open output file: "); + std_err(*argv); + std_err("\n"); + exit(2); + } + + if (iflag) signal(SIGINT, SIG_IGN); + + while ((n = read(0, buf, CHUNK_SIZE)) > 0) { + for (i = 0; i < s; i++) write(fd[i], buf, n); + } + + for (i = 0; i < s; i++) /* Close all fd's */ + close(fd[i]); + return(0); +} diff --git a/commands/simple/term.c b/commands/simple/term.c new file mode 100755 index 000000000..c03352d36 --- /dev/null +++ b/commands/simple/term.c @@ -0,0 +1,539 @@ +/* term - terminal simulator Author: Andy Tanenbaum */ + +/* This program allows the user to turn a MINIX system into a dumb + * terminal to communicate with a remote computer through one of the ttys. + * It forks into two processes. The parent sits in a tight loop copying + * from stdin to the tty. The child sits in a tight loop copying from + * the tty to stdout. + * + * 2 Sept 88 BDE (Bruce D. Evans): Massive changes to make current settings the + * default, allow any file as the "tty", support fancy baud rates and remove + * references to and dependencies on modems and keyboards, so (e.g.) + * a local login on /dev/tty1 can do an external login on /dev/tty2. + * + * 3 Sept 88 BDE: Split parent again to main process copies from stdin to a + * pipe which is copied to the tty. This stops a blocked write to the + * tty from hanging the program. + * + * 11 Oct 88 BDE: Cleaned up baud rates and parity stripping. + * + * 09 Oct 90 MAT (Michael A. Temari): Fixed bug where terminal isn't reset + * if an error occurs. + * + * Nov 90 BDE: Don't broadcast kill(0, SIGINT) since two or more of these + * in a row will kill the parent shell. + * + * 19 Oct 89 RW (Ralf Wenk): Adapted to MINIX ST 1.1 + RS232 driver. Split + * error into error_n and error. Added resetting of the terminal settings + * in error. + * + * 24 Nov 90 RW: Adapted to MINIX ST 1.5.10.2. Forked processes are now + * doing an exec to get a better performance. This idea is stolen from + * a terminal program written by Felix Croes. + * + * 01 May 91 RW: Merged the MINIX ST patches with Andys current version. + * Most of the 19 Oct 89 patches are deleted because they are already there. + * + * 10 Mar 96 KJB: Termios adaption, cleanup, command key interface. + * + * 27 Nov 96 KJB: Add -c flag that binds commands to keys. + * + * Example usage: + * term : baud, bits/char, parity from /dev/tty1 + * term 9600 7 even : 9600 baud, 7 bits/char, even parity + * term odd 300 7 : 300 baud, 7 bits/char, odd parity + * term /dev/tty2 : use /dev/tty2 rather than /dev/tty1 + * : Any argument starting with "/" is + * : taken as the communication device. + * term 8 57600 /dev/tty2 -atdt4441234 : if an argument begins with + * : - , the rest of that arg is + * : sent to the modem as a + * : dial string + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <termios.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdarg.h> +#include <errno.h> +#include <sys/wait.h> +#include <sys/stat.h> + +#define CHUNK 1024 /* how much to read at once */ + +char TERM_LINE[] = "/dev/modem";/* default serial port to use */ + + /* device lock file */ +char lockfile[] = "/usr/spool/locks/LK.iii.jjj.kkk"; + +char *commdev; /* communications device a.k.a. "modem". */ +int commfd; /* open file no. for comm device */ +struct termios tccomm; /* terminal parameters for commfd */ +struct termios tcstdin; /* terminal parameters for stdin */ +struct termios tcsavestdin; /* saved terminal parameters for stdin */ + +/* Special key to get term's attention. */ +#define HOTKEY '\035' /* CTRL-] */ + +struct param_s { + char *pattern; + unsigned value; + enum { BAD, BITS, PARITY, SPEED } type; +} params[] = { + { "5", CS5, BITS }, + { "6", CS6, BITS }, + { "7", CS7, BITS }, + { "8", CS8, BITS }, + + { "even", PARENB, PARITY }, + { "odd", PARENB|PARODD, PARITY }, + + { "50", B50, SPEED }, + { "75", B75, SPEED }, + { "110", B110, SPEED }, + { "134", B134, SPEED }, + { "200", B200, SPEED }, + { "300", B300, SPEED }, + { "600", B600, SPEED }, + { "1200", B1200, SPEED }, + { "1800", B1800, SPEED }, + { "2400", B2400, SPEED }, + { "4800", B4800, SPEED }, + { "9600", B9600, SPEED }, + { "19200", B19200, SPEED }, + { "38400", B38400, SPEED }, + { "57600", B57600, SPEED }, + { "115200", B115200, SPEED }, + { "", 0, BAD }, /* BAD type to end list */ +}; + +#define NIL ((char *) NULL) /* tell(fd, ..., NIL) */ + +_PROTOTYPE(int main, (int argc, char *argv[])); +_PROTOTYPE(int isdialstr, (char *arg)); +_PROTOTYPE(void tell, (int fd, ...)); +_PROTOTYPE(void reader, (int on)); +_PROTOTYPE(void shell, (char *cmd)); +_PROTOTYPE(void lock_device, (char *device)); +_PROTOTYPE(void fatal, (char *label)); +_PROTOTYPE(void setnum, (char *s, int n)); +_PROTOTYPE(void set_uart, (int argc, char *argv[], struct termios *tcp)); +_PROTOTYPE(void set_raw, (struct termios *tcp)); +_PROTOTYPE(void quit, (int code)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int i; + unsigned char key; + int candial; + + for (i = 1; i < argc; ++i) { + if (argv[i][0] == '/') { + if (commdev != NULL) { + tell(2, "term: too many communication devices\n", NIL); + exit(1); + } + commdev = argv[i]; + } + } + if (commdev == NULL) commdev = TERM_LINE; + + /* Save tty attributes of the terminal. */ + if (tcgetattr(0, &tcsavestdin) < 0) { + tell(2, "term: standard input is not a terminal\n", NIL); + exit(1); + } + + lock_device(commdev); + + commfd = open(commdev, O_RDWR); + if (commfd < 0) { + tell(2, "term: can't open ", commdev, ": ", strerror(errno), "\n", NIL); + quit(1); + } + + /* Compute RAW modes of terminal and modem. */ + if (tcgetattr(commfd, &tccomm) < 0) { + tell(2, "term: ", commdev, " is not a terminal\n", NIL); + quit(1); + } + signal(SIGINT, quit); + signal(SIGTERM, quit); + tcstdin = tcsavestdin; + set_raw(&tcstdin); + set_raw(&tccomm); + set_uart(argc, argv, &tccomm); + tcsetattr(0, TCSANOW, &tcstdin); + tcsetattr(commfd, TCSANOW, &tccomm); + + /* Start a reader process to copy modem output to the screen. */ + reader(1); + + /* Welcome message. */ + tell(1, "Connected to ", commdev, + ", command key is CTRL-], type ^]? for help\r\n", NIL); + + /* Dial. */ + candial = 0; + for (i = 1; i < argc; ++i) { + if (!isdialstr(argv[i])) continue; + tell(commfd, argv[i] + 1, "\r", NIL); + candial = 1; + } + + /* Main loop of the terminal simulator. */ + while (read(0, &key, 1) == 1) { + if (key == HOTKEY) { + /* Command key typed. */ + if (read(0, &key, 1) != 1) continue; + + switch (key) { + default: + /* Added command? */ + for (i = 1; i < argc; ++i) { + char *arg = argv[i]; + + if (arg[0] == '-' && arg[1] == 'c' + && arg[2] == key) { + reader(0); + tcsetattr(0, TCSANOW, &tcsavestdin); + shell(arg+3); + tcsetattr(0, TCSANOW, &tcstdin); + reader(1); + break; + } + } + if (i < argc) break; + + /* Unrecognized command, print list. */ + tell(1, "\r\nTerm commands:\r\n", + " ? - this help\r\n", + candial ? " d - redial\r\n" : "", + " s - subshell (e.g. for file transfer)\r\n", + " h - hangup (+++ ATH)\r\n", + " b - send a break\r\n", + " q - exit term\r\n", + NIL); + for (i = 1; i < argc; ++i) { + char *arg = argv[i]; + static char cmd[] = " x - "; + + if (arg[0] == '-' && arg[1] == 'c' + && arg[2] != 0) { + cmd[1] = arg[2]; + tell(1, cmd, arg+3, "\r\n", NIL); + } + } + tell(1, "^] - send a CTRL-]\r\n\n", + NIL); + break; + case 'd': + /* Redial by sending the dial commands again. */ + for (i = 1; i < argc; ++i) { + if (!isdialstr(argv[i])) continue; + tell(commfd, argv[i] + 1, "\r", NIL); + } + break; + case 's': + /* Subshell. */ + reader(0); + tcsetattr(0, TCSANOW, &tcsavestdin); + shell(NULL); + tcsetattr(0, TCSANOW, &tcstdin); + reader(1); + break; + case 'h': + /* Hangup by using the +++ escape and ATH command. */ + sleep(2); + tell(commfd, "+++", NIL); + sleep(2); + tell(commfd, "ATH\r", NIL); + break; + case 'b': + /* Send a break. */ + tcsendbreak(commfd, 0); + break; + case 'q': + /* Exit term. */ + quit(0); + case HOTKEY: + (void) write(commfd, &key, 1); + break; + } + } else { + /* Send keyboard input down the serial line. */ + if (write(commfd, &key, 1) != 1) break; + } + } + tell(2, "term: nothing to copy from input to ", commdev, "?\r\n", NIL); + quit(1); +} + + +int isdialstr(char *arg) +{ +/* True iff arg is the start of a dial string, i.e. "-at...". */ + + return (arg[0] == '-' + && (arg[1] == 'a' || arg[1] == 'A') + && (arg[2] == 't' || arg[2] == 'T')); +} + + +void tell(int fd, ...) +{ +/* Write strings to file descriptor 'fd'. */ + va_list ap; + char *s; + + va_start(ap, fd); + while ((s = va_arg(ap, char *)) != NIL) write(fd, s, strlen(s)); + va_end(ap); +} + + +void reader(on) +int on; +{ +/* Start or end a process that copies from the modem to the screen. */ + + static pid_t pid; + char buf[CHUNK]; + ssize_t n, m, r; + + if (!on) { + /* End the reader process (if any). */ + if (pid == 0) return; + kill(pid, SIGKILL); + (void) waitpid(pid, (int *) NULL, 0); + pid = 0; + return; + } + + /* Start a reader */ + pid = fork(); + if (pid < 0) { + tell(2, "term: fork() failed: ", strerror(errno), "\r\n", NIL); + quit(1); + } + if (pid == 0) { + /* Child: Copy from the modem to the screen. */ + + while ((n = read(commfd, buf, sizeof(buf))) > 0) { + m = 0; + while (m < n && (r = write(1, buf + m, n - m)) > 0) m += r; + } + tell(2, "term: nothing to copy from ", commdev, " to output?\r\n", NIL); + kill(getppid(), SIGTERM); + _exit(1); + } + /* One reader on the loose. */ +} + + +void shell(char *cmd) +{ +/* Invoke a subshell to allow one to run zmodem for instance. Run sh -c 'cmd' + * instead if 'cmd' non-null. + */ + + pid_t pid; + char *shell, *sh0; + _PROTOTYPE(void (*isav), (int)); + _PROTOTYPE(void (*qsav), (int)); + _PROTOTYPE(void (*tsav), (int)); + + if (cmd == NULL) { + tell(1, "\nExit the shell to return to term, ", + commdev, " is open on file descriptor 9.\n", NIL); + } + + if (cmd != NULL || (shell = getenv("SHELL")) == NULL) shell = "/bin/sh"; + if ((sh0 = strrchr(shell, '/')) == NULL) sh0 = shell; else sh0++; + + /* Start a shell */ + pid = fork(); + if (pid < 0) { + tell(2, "term: fork() failed: ", strerror(errno), "\n", NIL); + return; + } + if (pid == 0) { + /* Child: Exec the shell. */ + setgid(getgid()); + setuid(getuid()); + + if (commfd != 9) { dup2(commfd, 9); close(commfd); } + + if (cmd == NULL) { + execl(shell, sh0, (char *) NULL); + } else { + execl(shell, sh0, "-c", cmd, (char *) NULL); + } + tell(2, "term: can't execute ", shell, ": ", strerror(errno), "\n",NIL); + _exit(1); + } + /* Wait for the shell to exit. */ + isav = signal(SIGINT, SIG_IGN); + qsav = signal(SIGQUIT, SIG_IGN); + tsav = signal(SIGTERM, SIG_IGN); + (void) waitpid(pid, (int *) 0, 0); + (void) signal(SIGINT, isav); + (void) signal(SIGQUIT, qsav); + (void) signal(SIGTERM, tsav); + tell(1, "\n[back to term]\n", NIL); +} + + +void lock_device(device) +char *device; +{ +/* Lock a device by creating a lock file using SYSV style locking. */ + + struct stat stbuf; + unsigned int pid; + int fd; + int n; + int u; + + if (stat(device, &stbuf) < 0) fatal(device); + + if (!S_ISCHR(stbuf.st_mode)) { + tell(2, "term: ", device, " is not a character device\n", NIL); + exit(1); + } + + /* Compute the lock file name. */ + setnum(lockfile + 23, (stbuf.st_dev >> 8) & 0xFF); /* FS major (why?) */ + setnum(lockfile + 27, (stbuf.st_rdev >> 8) & 0xFF); /* device major */ + setnum(lockfile + 31, (stbuf.st_rdev >> 0) & 0xFF); /* device minor */ + + /* Try to make a lock file and put my pid in it. */ + u = umask(0); + for (;;) { + if ((fd = open(lockfile, O_RDONLY)) < 0) { + /* No lock file, try to lock it myself. */ + if (errno != ENOENT) fatal(device); + if ((fd = open(lockfile, O_WRONLY|O_CREAT|O_EXCL, 0444)) < 0) { + if (errno == EEXIST) continue; + fatal(lockfile); + } + pid = getpid(); + n = write(fd, &pid, sizeof(pid)); + if (n < 0) { + n = errno; + (void) unlink(lockfile); + errno = n; + fatal(lockfile); + } + close(fd); + break; + } else { + /* Already there, but who owns it? */ + n = read(fd, &pid, sizeof(pid)); + if (n < 0) fatal(device); + close(fd); + if (n == sizeof(pid) && !(kill(pid, 0) < 0 && errno == ESRCH)) { + /* It is locked by a running process. */ + tell(2, "term: ", device, + " is in use by another program\n", NIL); + if (getpgrp() == getpid()) sleep(3); + exit(1); + } + /* Stale lock. */ + tell(1, "Removing stale lock ", lockfile, "\n", NIL); + if (unlink(lockfile) < 0 && errno != ENOENT) fatal(lockfile); + } + } + /* Lock achieved, but what if two terms encounters a stale lock at the same + * time? + */ + umask(u); +} + + +void fatal(char *label) +{ + tell(2, "term: ", label, ": ", strerror(errno), "\n", NIL); + exit(1); +} + + +void setnum(char *s, int n) +{ +/* Poke 'n' into string 's' backwards as three decimal digits. */ + int i; + + for (i = 0; i < 3; i++) { *--s = '0' + (n % 10); n /= 10; } +} + + +void set_uart(argc, argv, tcp) +int argc; +char *argv[]; +struct termios *tcp; +{ +/* Set up the UART parameters. */ + + int i; + char *arg; + struct param_s *param; + + /* Examine all the parameters and check for validity. */ + for (i = 1; i < argc; ++i) { + arg = argv[i]; + if (arg[0] == '/' || arg[0] == '-') continue; + + /* Check parameter for legality. */ + for (param = ¶ms[0]; + param->type != BAD && strcmp(arg, param->pattern) != 0; + ++param); + switch (param->type) { + case BAD: + tell(2, "Invalid parameter: ", arg, "\n", NIL); + quit(1); + break; + case BITS: + tcp->c_cflag &= ~CSIZE; + tcp->c_cflag |= param->value; + break; + case PARITY: + tcp->c_cflag &= PARENB | PARODD; + tcp->c_cflag |= param->value; + break; + case SPEED: + cfsetispeed(tcp, (speed_t) param->value); + cfsetospeed(tcp, (speed_t) param->value); + break; + } + } +} + + +void set_raw(tcp) +struct termios *tcp; +{ + /* Set termios attributes for RAW mode. */ + + tcp->c_iflag &= ~(ICRNL|IGNCR|INLCR|IXON|IXOFF); + tcp->c_lflag &= ~(ICANON|IEXTEN|ISIG|ECHO|ECHONL); + tcp->c_oflag &= ~(OPOST); + tcp->c_cc[VMIN] = 1; + tcp->c_cc[VTIME] = 0; +} + + +void quit(code) +int code; +{ +/* Stop the reader process, reset the terminal, and exit. */ + reader(0); + tcsetattr(0, TCSANOW, &tcsavestdin); + (void) unlink(lockfile); + exit(code); +} diff --git a/commands/simple/termcap.c b/commands/simple/termcap.c new file mode 100755 index 000000000..95b43ca4d --- /dev/null +++ b/commands/simple/termcap.c @@ -0,0 +1,163 @@ +/* termcap - print termcap settings Author: Terrence Holm */ + +#include <stdlib.h> +#include <termcap.h> +#include <stdio.h> + +#define TC_BUFFER 1024 /* Size of termcap(3) buffer */ + +/****************************************************************/ +/* */ +/* termcap [ type ] */ +/* */ +/* Prints out all of the termcap capabilities as described */ +/* in termcap(4). If "type" is not supplied then $TERM is */ +/* used. */ +/* */ +/****************************************************************/ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void Print, (char *comment, char *name)); +_PROTOTYPE(void Error, (char *message, char *arg)); + +int main(argc, argv) +int argc; +char *argv[]; + + { + char *term; + char buffer[ TC_BUFFER ]; + + + /* Check for an argument */ + + if ( argc > 2 ) + Error( "Usage: %s [ type ]\n", argv[0] ); + + if ( argc == 2 ) + term = argv[1]; + else + term = getenv( "TERM" ); + + if ( term == NULL ) + Error( "termcap: $TERM is not defined\n", "" ); + + + /* Read in the termcap entry */ + + if ( tgetent( buffer, term ) != 1 ) + Error( "termcap: No termcap entry for %s\n", term ); + + + /* Print out the entry's contents */ + + printf( "TERM = %s\n\n", term ); + + if ( tgetflag( "am" ) == 1 ) + printf( "End of line wraps to next line (am)\n" ); + + if ( tgetflag( "bs" ) == 1 ) + printf( "Ctrl/H performs a backspace (bs)\n" ); + + printf( "Number of columns (co) = %d\n", tgetnum( "co" ) ); + printf( "Number of lines (li) = %d\n", tgetnum( "li" ) ); + + Print( "Clear to end of line", "ce" ); + Print( "Clear to end of screen", "cd" ); + Print( "Clear the whole screen", "cl" ); + + Print( "Start \"stand out\" mode", "so" ); + Print( "End \"stand out\" mode", "se" ); + Print( "Start underscore mode", "us" ); + Print( "End underscore mode", "ue" ); + Print( "Start blinking mode", "mb" ); + Print( "Start bold mode", "md" ); + Print( "Start reverse mode", "mr" ); + Print( "Return to normal mode", "me" ); + + Print( "Scroll backwards", "sr" ); + Print( "Cursor motion", "cm" ); + + Print( "Up one line", "up" ); + Print( "Down one line", "do" ); + Print( "Left one space", "le" ); + Print( "Right one space", "nd" ); + Print( "Move to top left corner", "ho" ); + + Print( "Generated by \"UP\"", "ku" ); + Print( "Generated by \"DOWN\"", "kd" ); + Print( "Generated by \"LEFT\"", "kl" ); + Print( "Generated by \"RIGHT\"", "kr" ); + Print( "Generated by \"HOME\"", "kh" ); + Print( "Generated by \"END\"", "k0" ); + Print( "Generated by \"PGUP\"", "k1" ); + Print( "Generated by \"PGDN\"", "k2" ); + Print( "Generated by numeric \"+\"", "k3" ); + Print( "Generated by numeric \"-\"", "k4" ); + Print( "Generated by numeric \"5\"", "k5" ); + + return( 0 ); + } + + + + + + +/****************************************************************/ +/* */ +/* Print( comment, name ) */ +/* */ +/* If a termcap entry exists for "name", then */ +/* print out "comment" and the entry. Control */ +/* characters are printed as ^x. */ +/* */ +/****************************************************************/ + + +void Print( comment, name ) + char *comment; + char *name; + + { + char entry[ 50 ]; + char *p = entry; + + if ( tgetstr( name, &p ) == NULL ) + return; + + printf( "%-32s (%s) = ", comment, name ); + + for ( p = entry; *p != '\0'; ++p ) + if ( *p < ' ' ) + printf( "^%c", *p + '@' ); + else if ( *p == '\177' ) + printf( "^?" ); + else + putchar( *p ); + + putchar( '\n' ); + } + + + + + + +/****************************************************************/ +/* */ +/* Error( message, arg ) */ +/* */ +/* Printf the "message" and abort. */ +/* */ +/****************************************************************/ + + +void Error( message, arg ) + char *message; + char *arg; + + { + fprintf( stderr, message, arg ); + exit( 1 ); + } diff --git a/commands/simple/tget.c b/commands/simple/tget.c new file mode 100755 index 000000000..b98fc28a8 --- /dev/null +++ b/commands/simple/tget.c @@ -0,0 +1,98 @@ +/* tget 1.0 - get termcap values Author: Kees J. Bot + * 6 Mar 1994 + */ +#define nil 0 +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termcap.h> + +void fputchar(int c) +{ + putchar(c); +} + +void usage(void) +{ + fprintf(stderr, +"Usage: tget [-flag id] [-num id] [-str id] [-goto col line] [[-echo] string]\n" + ); + exit(-1); +} + +void main(int argc, char **argv) +{ + char termbuf[1024]; + char string[256], *pstr; + char *term; + int i; + int excode= 0; + + if ((term= getenv("TERM")) == nil) { + fprintf(stderr, "tget: $TERM is not set\n"); + exit(-1); + } + + if (tgetent(termbuf, term) != 1) { + fprintf(stderr, "tget: no termcap entry for '%s'\n", term); + exit(-1); + } + + for (i= 1; i < argc; i++) { + char *option= argv[i]; + char *id; + + if (option[0] != '-') { + fputs(option, stdout); + continue; + } + + if (++i == argc) usage(); + id= argv[i]; + + if (strcmp(option, "-flag") == 0) { + excode= tgetflag(id) ? 0 : 1; + } else + if (strcmp(option, "-num") == 0) { + int num; + + if ((num= tgetnum(id)) == -1) { + excode= 1; + } else { + excode= 0; + printf("%d", num); + } + } else + if (strcmp(option, "-str") == 0) { + char *str; + + if ((str= tgetstr(id, (pstr= string, &pstr))) == nil) { + excode= 1; + } else { + excode= 0; + tputs(str, 0, fputchar); + } + } else + if (strcmp(option, "-goto") == 0) { + char *cm; + int col, line; + + col= atoi(id); + if (++i == argc) usage(); + line= atoi(argv[i]); + + if ((cm= tgetstr("cm", (pstr= string, &pstr))) == nil) { + excode= 1; + } else { + excode= 0; + tputs(tgoto(cm, col, line), 0, fputchar); + } + } else + if (strcmp(option, "-echo") == 0) { + fputs(id, stdout); + } else { + usage(); + } + } + exit(excode); +} diff --git a/commands/simple/time.c b/commands/simple/time.c new file mode 100755 index 000000000..d01ce0383 --- /dev/null +++ b/commands/simple/time.c @@ -0,0 +1,160 @@ +/* time - time a command Authors: Andy Tanenbaum & Michiel Huisjes */ + +#define NEW 1 + +#include <sys/types.h> +#include <sys/times.h> +#include <limits.h> +#include <time.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> +#include <minix/minlib.h> +#include <stdio.h> + +/* -DNEW prints time to 0.01 sec. */ +#ifdef NEW +#define HUNDREDTHS 1 +#endif + +char **args; +char *name; + +int digit_seen; +char a[] = " . \0"; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void print_time, (clock_t t)); +_PROTOTYPE(void twin, (int n, char *p)); +_PROTOTYPE(void execute, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + + struct tms pre_buf, post_buf; + int status, pid; +#if _VMD_EXT + struct timeval start_time, end_time; +#else + time_t start_time, end_time; +#endif + clock_t real_time; + + if (argc == 1) exit(0); + + args = &argv[1]; + name = argv[1]; + + /* Get real time at start of run. */ +#if _VMD_EXT + (void) sysutime(UTIME_TIMEOFDAY, &start_time); +#else + (void) time(&start_time); +#endif + + /* Fork off child. */ + if ((pid = fork()) < 0) { + std_err("Cannot fork\n"); + exit(1); + } + if (pid == 0) execute(); + + /* Parent is the time program. Disable interrupts and wait. */ + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + + do { + times(&pre_buf); + } while (wait(&status) != pid); +#if _VMD_EXT + (void) sysutime(UTIME_TIMEOFDAY, &end_time); + real_time = (end_time.tv_sec - start_time.tv_sec) * CLOCKS_PER_SEC + + (end_time.tv_usec - start_time.tv_usec) * CLOCKS_PER_SEC / 1000000; +#else + (void) time(&end_time); + real_time = (end_time - start_time) * CLOCKS_PER_SEC; +#endif + + if ((status & 0377) != 0) std_err("Command terminated abnormally.\n"); + times(&post_buf); + + /* Print results. -DNEW enables time on one line to 0.01 sec */ +#ifndef NEW + std_err("real "); + print_time(real_time); + std_err("\nuser "); + print_time(post_buf.tms_cutime - pre_buf.tms_cutime); + std_err("\nsys "); + print_time(post_buf.tms_cstime - pre_buf.tms_cstime); + std_err("\n"); +#else + print_time(real_time); + std_err(" real"); + print_time(post_buf.tms_cutime - pre_buf.tms_cutime); + std_err(" user"); + print_time(post_buf.tms_cstime - pre_buf.tms_cstime); + std_err(" sys\n"); +#endif + return((status & 0377) ? -1 : (status >> 8)); +} + +void print_time(t) +register clock_t t; +{ +/* Print the time 't' in hours: minutes: seconds. 't' is in ticks. */ + + int hours, minutes, seconds, hundredths, i; + + digit_seen = 0; + for (i = 0; i < 8; i++) a[i] = ' '; + hours = (int) (t / ((clock_t) 3600 * CLOCKS_PER_SEC)); + t -= (clock_t) hours * 3600 * CLOCKS_PER_SEC; + minutes = (int) (t / ((clock_t) 60 * CLOCKS_PER_SEC)); + t -= (clock_t) minutes * 60 * CLOCKS_PER_SEC; + seconds = (int) (t / CLOCKS_PER_SEC); + t -= (clock_t) seconds * CLOCKS_PER_SEC; + hundredths = (int) (t * 100 / CLOCKS_PER_SEC); + + if (hours) { + twin(hours, &a[0]); + a[2] = ':'; + } + if (minutes || digit_seen) { + twin(minutes, &a[3]); + a[5] = ':'; + } + if (seconds || digit_seen) + twin(seconds, &a[6]); + else + a[7] = '0'; + a[9] = hundredths / 10 + '0'; +#ifdef HUNDREDTHS /* tenths used to be enough */ + a[10] = hundredths % 10 + '0'; +#endif + std_err(a); +} + +void twin(n, p) +int n; +char *p; +{ + char c1, c2; + c1 = (n / 10) + '0'; + c2 = (n % 10) + '0'; + if (digit_seen == 0 && c1 == '0') c1 = ' '; + *p++ = c1; + *p++ = c2; + if (n > 0) digit_seen = 1; +} + +void execute() +{ + execvp(name, args); + std_err("Cannot execute "); + std_err(name); + std_err("\n"); + exit(-1); +} diff --git a/commands/simple/touch.c b/commands/simple/touch.c new file mode 100755 index 000000000..167f8c560 --- /dev/null +++ b/commands/simple/touch.c @@ -0,0 +1,248 @@ +/* Touch - change file access and modification times. + * + * Usage: see end of file + * + * Conforms to P1003.2 draft 10, sec. 4.62, except that time values + * are not checked for validity, but passed on to mktime, so that + * 9301990000 will refer to Apr. 9th 1993. As a side effect, leap + * seconds are not handled correctly. + * + * Authors: Original author unknown. Rewritten for POSIX by + * Peter Holzer (hp@vmars.tuwien.ac.at). + * + * $Id$ + * $Log$ + * Revision 1.1 2005/04/21 14:55:35 beng + * Initial revision + * + * Revision 1.1.1.1 2005/04/20 13:33:47 beng + * Initial import of minix 2.0.4 + * + * Revision 1.8 1994/03/17 21:39:19 hjp + * fixed bug with 4-digit years + * + * Revision 1.7 1994/03/15 00:43:27 hjp + * Changes from kjb (vmd 1.6.25.1): + * fixed exit code + * nonstandard flag 0 to make file very old + * + * Revision 1.6 1994/02/12 17:26:33 hjp + * fixed -a and -m flags + * + * Revision 1.5 1994/02/12 16:04:13 hjp + * fixed bug when -t argument was not given + * removed debugging code + * run through pretty to get Minix layout + * + * Revision 1.4 1994/02/07 21:23:11 hjp + * POSIXified. + * + */ + +#define _POSIX_C_SOURCE 2 /* getopt */ +#include <assert.h> +#include <ctype.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <fcntl.h> +#include <unistd.h> +#include <utime.h> + +#define val2(string) ((string)[0] * 10 + (string)[1] - '0' * 11) +#define val4(string) (val2(string) * 100 + val2(string + 2)) + +typedef enum { + OLD, NEW +} formatT; + +char *cmnd; +int no_creat = 0; +unsigned int to_change = 0; +# define ATIME 1 +# define MTIME 2 + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(int doit, (char *name, struct utimbuf tvp)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(time_t parsetime, (const char *string, formatT format)); + +time_t parsetime(string, format) +const char *string; +formatT format; +{ + struct tm tm; + time_t touchtime; + size_t l; + + l = strspn(string, "0123456789"); + if (l % 2 == 1) return -1; + if (string[l] != '\0' && (string[l] != '.' || format == OLD)) { + return -1; + } + if (format == OLD) { + if (l == 10) { + /* Last two digits are year */ + tm.tm_year = val2(string + 8); + if (tm.tm_year <= 68) tm.tm_year += 100; + } else if (l == 8) { + time(&touchtime); + tm = *localtime(&touchtime); + } else { + return -1; + } + } else { + if (l == 12) { + /* First four digits are year */ + tm.tm_year = val4(string) - 1900; + string += 4; + } else if (l == 10) { + /* First two digits are year */ + tm.tm_year = val2(string); + if (tm.tm_year <= 68) tm.tm_year += 100; + string += 2; + } else if (l == 8) { + time(&touchtime); + tm = *localtime(&touchtime); + } else { + return -1; + } + } + tm.tm_mon = val2(string) - 1; + string += 2; + tm.tm_mday = val2(string); + string += 2; + tm.tm_hour = val2(string); + string += 2; + tm.tm_min = val2(string); + string += 2; + if (format == NEW && string[0] == '.') { + if (isdigit(string[1]) && isdigit(string[2]) && + string[3] == '\0') { + tm.tm_sec = val2(string + 1); + } else { + return -1; + } + } else { + tm.tm_sec = 0; + } + tm.tm_isdst = -1; + touchtime = mktime(&tm); + return touchtime; +} + + +int main(argc, argv) +int argc; +char **argv; +{ + time_t auxtime; + struct stat sb; + int c; + struct utimbuf touchtimes; + int fail = 0; + + cmnd = argv[0]; + auxtime = time((time_t *) NULL); + touchtimes.modtime = auxtime; + touchtimes.actime = auxtime; + + while ((c = getopt(argc, argv, "r:t:acm0")) != EOF) { + switch (c) { + case 'r': + if (stat(optarg, &sb) == -1) { + fprintf(stderr, "%s: cannot stat %s: %s\n", + cmnd, optarg, strerror(errno)); + exit(1); + } + touchtimes.modtime = sb.st_mtime; + touchtimes.actime = sb.st_atime; + break; + case 't': + auxtime = parsetime(optarg, NEW); + if (auxtime == (time_t) - 1) usage(); + touchtimes.modtime = auxtime; + touchtimes.actime = auxtime; + break; + case 'a': to_change |= ATIME; break; + case 'm': to_change |= MTIME; break; + case 'c': no_creat = 1; break; + case '0': + touchtimes.modtime = touchtimes.actime = 0; + break; + case '?': usage(); break; + default: assert(0); + } + } + if (to_change == 0) { + to_change = ATIME | MTIME; + } + if (optind == argc) usage(); + + /* Now check for old style time argument */ + if (strcmp(argv[optind - 1], "--") != 0 && + (auxtime = parsetime(argv[optind], OLD)) != (time_t) - 1) { + touchtimes.modtime = auxtime; + touchtimes.actime = auxtime; + optind++; + if (optind == argc) usage(); + } + while (optind < argc) { + if (doit(argv[optind], touchtimes) > 0) { + fprintf(stderr, "%s: cannot touch %s: %s\n", + cmnd, argv[optind], strerror(errno)); + fail = 1; + } + optind++; + } + return fail ? 1 : 0; +} + + +int doit(name, tvp) +char *name; +struct utimbuf tvp; +{ + int fd; + struct stat sb; + + if (to_change != (ATIME | MTIME)) { + + if (stat(name, &sb) != -1) { + if (!(to_change & ATIME)) { + tvp.actime = sb.st_atime; + } else { + tvp.modtime = sb.st_mtime; + } + } + } + if (utime(name, &tvp) == 0) return 0; + if (errno != ENOENT) return 1; + if (no_creat == 1) return 0; + if ((fd = creat(name, 0666)) >= 0) { + if (fstat(fd, &sb) != -1) { + if (!(to_change & ATIME)) { + tvp.actime = sb.st_atime; + } else { + tvp.modtime = sb.st_mtime; + } + } else { + assert(0); + } + close(fd); + if (utime(name, &tvp) == 0) return 0; + } + return 1; +} + + +void usage() +{ + fprintf(stderr, "Usage: %s [-c] [-a] [-m] [-r file] [-t [CC[YY]]MMDDhhmm[.ss]] " + "[MMDDhhmm[YY]] file...\n", cmnd); + exit(1); +} diff --git a/commands/simple/tr.c b/commands/simple/tr.c new file mode 100755 index 000000000..d3c64a8c8 --- /dev/null +++ b/commands/simple/tr.c @@ -0,0 +1,169 @@ +/* tr - translate characters Author: Michiel Huisjes */ +/* Usage: tr [-cds] [string1 [string2]] + * c: take complement of string1 + * d: delete input characters coded string1 + * s: squeeze multiple output characters of string2 into one character + */ + +#define BUFFER_SIZE 1024 +#define ASCII 0377 + +typedef char BOOL; +#define TRUE 1 +#define FALSE 0 + +#define NIL_PTR ((char *) 0) + +BOOL com_fl, del_fl, sq_fl; + +unsigned char output[BUFFER_SIZE], input[BUFFER_SIZE]; +unsigned char vector[ASCII + 1]; +BOOL invec[ASCII + 1], outvec[ASCII + 1]; + +short in_index, out_index; + +#include <sys/types.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void convert, (void)); +_PROTOTYPE(void map, (unsigned char *string1, unsigned char *string2)); +_PROTOTYPE(void expand, (char *arg, unsigned char *buffer)); +_PROTOTYPE(void complement, (unsigned char *buffer)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + register unsigned char *ptr; + int index = 1; + short i; + + if (argc > 1 && argv[index][0] == '-') { + for (ptr = (unsigned char *) &argv[index][1]; *ptr; ptr++) { + switch (*ptr) { + case 'c': com_fl = TRUE; break; + case 'd': del_fl = TRUE; break; + case 's': sq_fl = TRUE; break; + default: + write(2,"Usage: tr [-cds] [string1 [string2]].\n", 38); + exit(1); + } + } + index++; + } + for (i = 0; i <= ASCII; i++) { + vector[i] = i; + invec[i] = outvec[i] = FALSE; + } + + if (argv[index] != NIL_PTR) { + expand(argv[index++], input); + if (com_fl) complement(input); + if (argv[index] != NIL_PTR) expand(argv[index], output); + if (argv[index] != NIL_PTR) map(input, output); + for (ptr = input; *ptr; ptr++) invec[*ptr] = TRUE; + for (ptr = output; *ptr; ptr++) outvec[*ptr] = TRUE; + } + convert(); + return(0); +} + +void convert() +{ + short read_chars = 0; + short c, coded; + short last = -1; + + for (;;) { + if (in_index == read_chars) { + if ((read_chars = read(0, (char *)input, BUFFER_SIZE)) <= 0) { + if (write(1, (char *)output, out_index) != out_index) + write(2, "Bad write\n", 10); + exit(0); + } + in_index = 0; + } + c = input[in_index++]; + coded = vector[c]; + if (del_fl && invec[c]) continue; + if (sq_fl && last == coded && outvec[coded]) continue; + output[out_index++] = last = coded; + if (out_index == BUFFER_SIZE) { + if (write(1, (char *)output, out_index) != out_index) { + write(2, "Bad write\n", 10); + exit(1); + } + out_index = 0; + } + } + + /* NOTREACHED */ +} + +void map(string1, string2) +register unsigned char *string1, *string2; +{ + unsigned char last; + + while (*string1) { + if (*string2 == '\0') + vector[*string1] = last; + else + vector[*string1] = last = *string2++; + string1++; + } +} + +void expand(arg, buffer) +register char *arg; +register unsigned char *buffer; +{ + int i, ac; + + while (*arg) { + if (*arg == '\\') { + arg++; + i = ac = 0; + if (*arg >= '0' && *arg <= '7') { + do { + ac = (ac << 3) + *arg++ - '0'; + i++; + } while (i < 4 && *arg >= '0' && *arg <= '7'); + *buffer++ = ac; + } else if (*arg != '\0') + *buffer++ = *arg++; + } else if (*arg == '[') { + arg++; + i = *arg++; + if (*arg++ != '-') { + *buffer++ = '['; + arg -= 2; + continue; + } + ac = *arg++; + while (i <= ac) *buffer++ = i++; + arg++; /* Skip ']' */ + } else + *buffer++ = *arg++; + } +} + +void complement(buffer) +unsigned char *buffer; +{ + register unsigned char *ptr; + register short i, index; + unsigned char conv[ASCII + 2]; + + index = 0; + for (i = 1; i <= ASCII; i++) { + for (ptr = buffer; *ptr; ptr++) + if (*ptr == i) break; + if (*ptr == '\0') conv[index++] = i & ASCII; + } + conv[index] = '\0'; + strcpy((char *)buffer, (char *)conv); +} diff --git a/commands/simple/tsort.c b/commands/simple/tsort.c new file mode 100755 index 000000000..866956b3b --- /dev/null +++ b/commands/simple/tsort.c @@ -0,0 +1,356 @@ +/* topo - topological sort Author: Kent Williams */ + +/* +** topo - perform a topological sort of the output of lorder. +** +** Usage : topo [infile] [outfile] +** +** Author: Kent Williams (williams@umaxc.weeg.uiowa.edu) +*/ + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +typedef struct __v { + struct __v *next; /* link list node */ + int indegree, /* number of edges into this vertex */ + visited, /* depth-first search visited flag */ + on_the_path, /* used to find cycles */ + has_a_cycle; /* true if a cycle at this vertex */ + struct __e *out; /* outgoing edges from this vertex */ + char key[1]; /* name of this vertex */ +} vertex; + +typedef struct __e { + struct __e *next; /* link list node */ + vertex *v; /* vertex to which this edge goes */ +} edge; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void *xmalloc, (size_t siz)); +_PROTOTYPE(edge *new_edge, (vertex *v)); +_PROTOTYPE(char *copyupto, (char *name, char *buf, int stop)); +_PROTOTYPE(int child_of, (vertex *parent, vertex *child)); +_PROTOTYPE(vertex *add_v, (char *s)); +_PROTOTYPE(void readin, (void)); +_PROTOTYPE(void pushname, (char *s)); +_PROTOTYPE(char *popname, (void)); +_PROTOTYPE(void topo, (void)); +_PROTOTYPE(void print_cycle, (vertex *parent, vertex *child)); +_PROTOTYPE(void dfs, (vertex *v)); +_PROTOTYPE(void check_cycles, (void)); + +/* +** xmalloc -- standard do or die malloc front end. +*/ +void * +xmalloc(siz) +size_t siz; +{ + void *rval = (void *)malloc(siz); + if(rval == NULL) { + fputs("Out of memory.\n",stderr); + exit(1); + } + return rval; +} + +/* +** edge allocater. +*/ +edge * +new_edge(v) +vertex *v; +{ + edge *rval; + rval = (edge *)xmalloc(sizeof(edge)); + rval->v = v; return rval; +} + +/* +** copyupto - copy until you see the stop character. +*/ +char * +copyupto(name,buf,stop) +char *name,*buf,stop; +{ + while(*buf != '\0' && *buf != stop) + *name++ = *buf++; + *name = '\0'; + while(*buf != '\0' && isspace(*buf)) + buf++; + return buf; +} + +/* +** find out if the vertex child is a child of the vertex parent. +*/ +int +child_of(parent,child) +vertex *parent,*child; +{ + edge *e; + for(e = parent->out; e != NULL && e->v != child; e = e->next) + ; + return e == NULL ? 0 : 1; +} + +/* +** the vertex set. +** +** add_v adds a vertex to the set if it's not already there. +*/ +vertex *vset = NULL; + +vertex * +add_v(s) +char *s; +{ + vertex *v,*last; + /* + ** go looking for this key in the vertex set. + */ + for(last = v = vset; v != NULL && strcmp(v->key,s) != 0; + last = v, v = v->next) + ; + if(v != NULL) { + /* + ** use the move-to-front heuristic to keep this from being + ** an O(N^2) algorithm. + */ + if(last != vset) { + last->next = v->next; + v->next = vset; + vset = v; + } + return v; + } + + v = (vertex *)xmalloc(sizeof(vertex) + strlen(s)); + + v->out = NULL; + strcpy(v->key,s); + v->indegree = + v->on_the_path = + v->has_a_cycle = + v->visited = 0; + v->next = vset; + vset = v; + return v; +} + +/* +** readin -- read in the dependency pairs. +*/ +void +readin() +{ + static char buf[128]; + static char name[64]; + char *bp; + vertex *child,*parent; + edge *e; + while(fgets(buf,sizeof(buf),stdin) != NULL) { + bp = buf + strlen(buf); + if (bp > buf && bp[-1] == '\n') *--bp = 0; + bp = copyupto(name,buf,' '); + child = add_v(name); + parent = add_v(bp); + if(child != parent && !child_of(parent,child)) { + e = new_edge(child); + e->next = parent->out; + parent->out = e; + child->indegree++; + } + } +} + +/* +** the topological sort produces names of modules in reverse of +** the order we want them in, so use a stack to hold the names +** until we get them all, then pop them off to print them. +*/ +struct name { struct name *next; char *s; } +*namelist = NULL; + +void +pushname(s) +char *s; +{ + struct name *x = (struct name *)xmalloc(sizeof(struct name)); + x->s = s; + x->next = namelist; + namelist = x; +} + +char * +popname() { + char *rval; + struct name *tmp; + if(namelist == NULL) + return NULL; + tmp = namelist; + rval = namelist->s; + namelist = namelist->next; + free(tmp); + return rval; +} + +/* +** topo - do a topological sort of the dependency graph. +*/ +void topo() { + vertex *x = vset,*n; + edge *e; + vertex *outq = NULL,*tmp; +#define insq(x) ((x->next = outq),(outq = x)) +#define deq() ((tmp = outq),(outq = outq->next),tmp) + + /* + ** find all vertices that don't depend on any other vertices + ** Since it breaks the "next" links to insert x into the queue, + ** x->next is saved before insq, to resume the list traversal. + */ + while (x != NULL) { + n = x->next; + if(x->indegree == 0) { + insq(x); + pushname(x->key); + } + x = n; + } + + /* + ** for each vertex V with indegree of zero, + ** for each edge E from vertex V + ** subtract one from the indegree of the vertex V' + ** pointed to by E. If V' now has an indegree of zero, + ** add it to the set of vertices with indegree zero, and + ** push its name on the output stack. + */ + while(outq != NULL) { + x = deq(); + e = x->out; + while(e != NULL) { + if(--(e->v->indegree) == 0) { + insq(e->v); + pushname(e->v->key); + } + e = e->next; + } + } + + /* + ** print the vertex names in opposite of the order they were + ** encountered. + */ + while(namelist != NULL) + puts(popname()); +} + +/* +** print_cycle -- +** A cycle has been detected between parent and child. +** Start with the child, and look at each of its edges for +** the parent. +** +** We know a vertex is on the path from the child to the parent +** because the depth-first search sets on_the_path true for each +** vertex it visits. +*/ +void +print_cycle(parent,child) +vertex *parent, *child; +{ + char *s; + vertex *x; + edge *e; + for(x = child; x != parent; ) { + pushname(x->key); + for(e = x->out; e != NULL; e = e->next) { + /* + ** stop looking for the path at the first node found + ** that's on the path. Watch out for cycles already + ** detected, because if you follow an edge into a cycle, + ** you're stuck in an infinite loop! + */ + if(e->v->on_the_path && !e->v->has_a_cycle) { + x = e->v; + break; + } + } + } + /* + ** print the name of the parent, and then names of each of the + ** vertices on the path from the child to the parent. + */ + fprintf(stderr,"%s\n",x->key); + while((s = popname()) != NULL) + fprintf(stderr,"%s\n",s); +} + +/* +** depth first search for cycles in the dependency graph. +** See "Introduction to Algorithms" by Udi Manber Addison-Wesley 1989 +*/ +void +dfs(v) +vertex *v; +{ + edge *e; + + if(v->visited) /* If you've been here before, don't go again! */ + return; + v->visited++; + v->on_the_path++; /* this node is on the path from the root. */ + + /* + ** depth-first search all outgoing edges. + */ + for(e = v->out; e != NULL; e = e->next) { + if(!e->v->visited) + dfs(e->v); + if(e->v->on_the_path) { + fprintf(stderr,"cycle found between %s and %s\n", + v->key,e->v->key); + print_cycle(v,e->v); + v->has_a_cycle++; + } + } + v->on_the_path = 0; +} + +/* +** check cycles starts the recursive depth-first search from +** each vertex in vset. +*/ +void +check_cycles() +{ + vertex *v; + for(v = vset; v != NULL; v = v->next) + dfs(v); +} + +/* +** main program. +*/ +int main(argc,argv) +int argc; +char **argv; +{ + if(argc > 1 && freopen(argv[1],"r",stdin) == NULL) { + perror(argv[1]); + exit(0); + } + if(argc > 2 && freopen(argv[2],"w",stdout) == NULL) { + perror(argv[2]); + exit(0); + } + readin(); + check_cycles(); + topo(); + return(0); +} diff --git a/commands/simple/ttt.c b/commands/simple/ttt.c new file mode 100755 index 000000000..079718cf4 --- /dev/null +++ b/commands/simple/ttt.c @@ -0,0 +1,299 @@ +/* tic tac toe (noughts and crosses) Author: Warren Toomey */ + +/* Copyright 1988 by Warren Toomey wkt@cs.adfa.oz.au[@uunet.uu.net] + * + * You may freely copy or distribute this code as long as this notice + * remains intact. + * + * You may modify this code, as long as this notice remains intact, and + * you add another notice indicating that the code has been modified. + * + * You may NOT sell this code or in any way profit from this code without + * prior agreement from the author. + */ + +/* Compile with cc -o tic tic.c -lcurses -ltermcap */ + +#include <stdlib.h> +#include <time.h> + +#ifdef CURSES +#include <curses.h> +#endif + +#include <stdio.h> + +#ifndef CURSES +#define printw printf +#endif + + +typedef struct { + int value; /* The move returned by the */ + int path; /* alphabeta consists of a value */ +} MOVE; /* and an actual move (path) */ + +_PROTOTYPE(int main, (void)); +_PROTOTYPE(int stateval, (int board [], int whosemove)); +_PROTOTYPE(MOVE alphabeta, (int board [], int whosemove, int alpha, int beta)); +_PROTOTYPE(void draw, (int board [])); +_PROTOTYPE(void getmove, (int board [])); +_PROTOTYPE(int endofgame, (int board [])); +_PROTOTYPE(int randommove, (void)); + + /* Static evaluator. Returns 100 if we have 3 in a row -100 if they have 3 + * in a row + * + * Board is array of 9 ints, where 0=empty square 1=our move 4= their move + * + * and board is indices 0 1 2 3 4 5 6 7 8 */ + + +int stateval(board, whosemove) +int board[]; +int whosemove; +{ + static int row[8][3] = {{0, 1, 2}, {3, 4, 5}, {6, 7, 8}, /* Indices of 3in-a-rows */ + {0, 3, 6}, {1, 4, 7}, {2, 5, 8}, + {0, 4, 8}, {2, 4, 6}}; + + int temp; /* Temp row results */ + int i, j; /* Loop counters */ + int side; /* Depth multiplier */ + int win, lose; + + if (whosemove == 1) { + win = 100; + lose = -100; + side = 1; + } else { + /* Multiply by -1 if */ + win = -100; + lose = 100; + side = -1; + } /* not out move */ + for (i = 0; i < 8; i++) { /* For every 3-in-a-row */ + temp = 0; + for (j = 0; j < 3; j++) /* Add up the board values */ + temp += board[row[i][j]]; + + if (temp == 3) return(win); /* We've got 3 in a row */ + if (temp == 12) return (lose); /* They've got 3 in a row */ + } + return(0); /* Finally return sum */ +} + + +MOVE alphabeta(board, whosemove, alpha, beta) /* Alphabeta: takes a board, */ +int board[]; /* whose move, alpha & beta cutoffs, */ +int whosemove; /* and returns a move to make and */ +int alpha; /* the value that the move has */ +int beta; +{ + MOVE result, successor; + int best_score, i, best_path, mademove; + + result.value = stateval(board, whosemove); /* Work out the board's */ + /* Static value */ + if ((result.value == 100) || /* If a win or loss already */ + (result.value == -100)) + return(result); /* return the result */ + + best_score = beta; /* Ok, set worst score */ + mademove = 0; /* to the beta cutoff */ + for (i = 0; i < 9; i++) { + if (board[i] == 0) { /* For all valid moves */ + mademove = 1; + board[i] = whosemove; /* make the move on board */ + successor = alphabeta(board, 5 - whosemove, -best_score - 1, -alpha - 1); + /* Get value of the move */ + board[i] = 0; /* Take move back */ + if (-successor.value > best_score) { /* If a better score */ + best_score = -successor.value; /* update our score */ + best_path = i; /* and move */ + if (best_score > alpha) + break; /* If we've beaten alpha */ + } /* return immediately */ + } + } + if (mademove) { + result.value = best_score; /* Finally return best score */ + result.path = best_path;/* and best move */ + } + return(result); /* If no move, return static result */ +} + + +void draw(board) /* Draw the board */ +int board[]; +{ + int i, j, row; + static char out[] = " X O"; /* Lookup table for character */ + + row = 6; +#ifdef CURSES + move(row, 0); +#endif + for (j = 0; j < 9; j += 3) { + printw(" %d | %d | %d ", j, j + 1, j + 2); + for (i = 0; i < 3; i++) { + printw("%c ", out[board[j + i]]); + if (i < 2) printw("| "); + } + if (j < 4) { +#ifdef CURSES + move(++row, 0); +#else + printw("\n"); +#endif + printw("---+---+--- ---+---+---"); + } +#ifdef CURSES + move(++row, 0); +#else + printw("\n"); +#endif + } +#ifdef CURSES + refresh(); +#else + printw("\n"); +#endif +} + + +void getmove(board) /* Get a player's move */ +int board[]; +{ + int Move; + int ItemsRead; + char dumc; + + do { + do { +#ifdef CURSES + move(9, 40); + printw("Your move: "); /* Prompt for move */ + refresh(); +#else + printw("Your move: "); /* Prompt for move */ +#endif + ItemsRead = scanf("%d", &Move); /* Input the move */ + if (ItemsRead == 0) scanf("%c", &dumc); /* Remove the offending character */ + } + while (ItemsRead != 1); + } + while (board[Move]); + board[Move] = 4; /* If legal, add to board */ + draw(board); /* Draw the board */ +} + + +int endofgame(board) /* Determine end of the game */ +int board[]; +{ + int eval; + int count; + + eval = stateval(board, 1); +#ifdef CURSES + move(20, 25); +#endif + if (eval == 100) { + printw("I have beaten you.\n"); + return(1); + } + if (eval == -100) { + printw("Bus error (core dumped)\n"); + return(1); + } + count = 0; + for (eval = 0; eval < 9; eval++) + if (board[eval] != 0) count++; + if (count == 9) { + printw("A draw!\n"); + return(1); + } +#ifdef CURSES + refresh(); +#endif + return(0); +} + + +int randommove() +{ /* Make an initial random move */ + int i; + + i = abs((int) time((long *) 0)); + return(i % 9); +} + + +int main() +{ /* The actual game */ + int i, board[9]; + char ch; + MOVE ourmove; + + for (i = 0; i < 9; i++) board[i] = 0; /* Initialise the board */ +#ifdef CURSES + initscr(); + clear(); + refresh(); +#endif + printw(" TIC TAC TOE \n\n"); + printw(" Your moves are 'O'\n"); + printw(" My moves are 'X'\n\n"); +#ifdef CURSES + move(5, 0); + printw("Do you wish to move first: "); + refresh(); + while (scanf("%c", &ch) != 1); + move(5, 0); + printw(" ......."); /* Kludge to get rid */ + refresh(); + move(5, 0); + printw(" "); /* of input letter */ + refresh(); +#else + do + printw("Do you wish to move first: "); + while (scanf("%c", &ch) != 1); +#endif + if ((ch != 'y') && (ch != 'Y')) { + i = randommove(); /* If we move first */ + board[i] = 1; /* make it random */ +#ifdef CURSES + move(7, 42); + printw("My move: %d\n", i); + refresh(); +#else + printw("My move: %d\n", i); +#endif + } + draw(board); + getmove(board); + + while (1) { + ourmove = alphabeta(board, 1, 99, -99); /* Get a move for us; + * return wins */ + /* Immediately & ignore losses */ + board[ourmove.path] = 1;/* and make it */ +#ifdef CURSES + move(7, 42); + printw("My move: %d\n", ourmove.path); + refresh(); +#else + printw("My move: %d\n", ourmove.path); +#endif + draw(board); + if (endofgame(board)) break; /* If end of game, exit */ + getmove(board); /* Get opponent's move */ + if (endofgame(board)) break; /* If end of game, exit */ + } +#ifdef CURSES + endwin(); +#endif + return(0); +} diff --git a/commands/simple/tty.c b/commands/simple/tty.c new file mode 100755 index 000000000..21abf3751 --- /dev/null +++ b/commands/simple/tty.c @@ -0,0 +1,30 @@ +/* tty.c - Return tty name Author: Freeman P. Pascal IV */ + +/* Minor changes to make tty conform to POSIX1003.2 Draft10 + Thomas Brupbacher (tobr@mw.lpc.ethz.ch) */ + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +_PROTOTYPE(int main, (int argc, char **argv)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + char *tty_name; + + tty_name = ttyname(STDIN_FILENO); + if ((argc == 2) && (!strcmp(argv[1], "-s"))) + /* Do nothing - shhh! we're in silent mode */ ; + else + puts((tty_name != NULL) ? tty_name : "not a tty"); + + if (isatty(STDIN_FILENO) == 0) + return(1); + else + return(0); +} diff --git a/commands/simple/umount.c b/commands/simple/umount.c new file mode 100755 index 000000000..95eee6cf1 --- /dev/null +++ b/commands/simple/umount.c @@ -0,0 +1,92 @@ +/* umount - unmount a file system Author: Andy Tanenbaum */ + +#define _MINIX 1 /* for proto of the non-POSIX umount() */ +#define _POSIX_SOURCE 1 /* for PATH_MAX from limits.h */ + +#include <sys/types.h> +#include <sys/svrctl.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <minix/minlib.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void update_mtab, (char *devname)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(void tell, (char *this)); + +static char mountpoint[PATH_MAX+1]; + +int main(argc, argv) +int argc; +char *argv[]; +{ + int sflag = 0; + + while (argc > 1 && argv[1][0] == '-') { + char *opt = argv[1]+1; + while (*opt) if (*opt++ == 's') sflag = 1; else usage(); + argc--; + argv++; + } + if (argc != 2) usage(); + if ((sflag ? svrctl(MMSWAPOFF, NULL) : umount(argv[1])) < 0) { + if (errno == EINVAL) + std_err("Device not mounted\n"); + else + perror("umount"); + exit(1); + } + update_mtab(argv[1]); + tell(argv[1]); + tell(" unmounted"); + if (*mountpoint != '\0') { + tell(" from "); + tell(mountpoint); + } + tell("\n"); + return(0); +} + +void update_mtab(devname) +char *devname; +{ +/* Remove an entry from /etc/mtab. */ + int n; + char special[PATH_MAX+1], mounted_on[PATH_MAX+1], version[10], rw_flag[10]; + + if (load_mtab("umount") < 0) { + std_err("/etc/mtab not updated.\n"); + exit(1); + } + while (1) { + n = get_mtab_entry(special, mounted_on, version, rw_flag); + if (n < 0) break; + if (strcmp(devname, special) == 0) { + strcpy(mountpoint, mounted_on); + continue; + } + (void) put_mtab_entry(special, mounted_on, version, rw_flag); + } + n = rewrite_mtab("umount"); + if (n < 0) { + std_err("/etc/mtab not updated.\n"); + exit(1); + } +} + +void usage() +{ + std_err("Usage: umount [-s] special\n"); + exit(1); +} + +void tell(this) +char *this; +{ + write(1, this, strlen(this)); +} diff --git a/commands/simple/uname.c b/commands/simple/uname.c new file mode 100755 index 000000000..d6ab16c8e --- /dev/null +++ b/commands/simple/uname.c @@ -0,0 +1,128 @@ +/* uname - print system name Author: Earl Chew */ + +/* Print the following system information as returned by the uname() + * function: + * + * system name Minix + * node name waddles + * release name 1.5 + * version 10 + * machine name i86 + * arch i86 (Minix specific) + */ + +#include <sys/types.h> +#include <sys/utsname.h> +#include <stdarg.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +/* Define the uname components. */ +#define ALL ((unsigned) 0x1F) +#define SYSNAME ((unsigned) 0x01) +#define NODENAME ((unsigned) 0x02) +#define RELEASE ((unsigned) 0x04) +#define VERSION ((unsigned) 0x08) +#define MACHINE ((unsigned) 0x10) +#define ARCH ((unsigned) 0x20) + +_PROTOTYPE(int main, (int argc, char **argv )); +_PROTOTYPE(void print, (int fd, ... )); +_PROTOTYPE(void usage, (void )); + +#ifdef __STDC__ +void print(int fd, ...) +#else +void print(fd) +int fd; +#endif +{ +/* Print a sequence of strings onto the named channel. */ + va_list argp; + char *p; + + va_start(argp, fd); + while (1) { + p = va_arg(argp, char *); + if (p == (char *) NULL) break; + write(fd, p, strlen(p)); + } + va_end(argp); +} + +char *name; + +void usage() +{ + print(STDERR_FILENO, "Usage: ", name, " -snrvmpa\n", (char *) NULL); + exit(EXIT_FAILURE); +} + +int main(argc, argv) +int argc; +char **argv; +{ + int info; + char *p; + struct utsname un; + + name = strrchr(argv[0], '/'); + if (name == NULL) name = argv[0]; else name++; + + for (info = 0; argc > 1; argc--, argv++) { + if (argv[1][0] == '-') { + for (p = &argv[1][1]; *p; p++) { + switch (*p) { + case 'a': info |= ALL; break; + case 'm': info |= MACHINE; break; + case 'n': info |= NODENAME; break; + case 'r': info |= RELEASE; break; + case 's': info |= SYSNAME; break; + case 'v': info |= VERSION; break; + case 'p': info |= ARCH; break; + default: usage(); + } + } + } else { + usage(); + } + } + + if (info == 0) info = strcmp(name, "arch") == 0 ? ARCH : SYSNAME; + + if (uname(&un) != 0) { + print(STDERR_FILENO, "unable to determine uname values\n", (char *) NULL); + exit(EXIT_FAILURE); + } + + if ((info & SYSNAME) != 0) + print(STDOUT_FILENO, un.sysname, (char *) NULL); + if ((info & NODENAME) != 0) { + if ((info & (SYSNAME)) != 0) + print(STDOUT_FILENO, " ", (char *) NULL); + print(STDOUT_FILENO, un.nodename, (char *) NULL); + } + if ((info & RELEASE) != 0) { + if ((info & (SYSNAME|NODENAME)) != 0) + print(STDOUT_FILENO, " ", (char *) NULL); + print(STDOUT_FILENO, un.release, (char *) NULL); + } + if ((info & VERSION) != 0) { + if ((info & (SYSNAME|NODENAME|RELEASE)) != 0) + print(STDOUT_FILENO, " ", (char *) NULL); + print(STDOUT_FILENO, un.version, (char *) NULL); + } + if ((info & MACHINE) != 0) { + if ((info & (SYSNAME|NODENAME|RELEASE|VERSION)) != 0) + print(STDOUT_FILENO, " ", (char *) NULL); + print(STDOUT_FILENO, un.machine, (char *) NULL); + } + if ((info & ARCH) != 0) { + if ((info & (SYSNAME|NODENAME|RELEASE|VERSION|MACHINE)) != 0) + print(STDOUT_FILENO, " ", (char *) NULL); + print(STDOUT_FILENO, un.arch, (char *) NULL); + } + print(STDOUT_FILENO, "\n", (char *) NULL); + return EXIT_SUCCESS; +} diff --git a/commands/simple/unexpand.c b/commands/simple/unexpand.c new file mode 100755 index 000000000..0a3365a9f --- /dev/null +++ b/commands/simple/unexpand.c @@ -0,0 +1,121 @@ +/* unexpand - convert spaces to tabs Author: Terrence W. Holm */ + +/* Usage: unexpand [ -a ] [ file ... ] */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#define TAB 8 + + +int column = 0; /* Current column, retained between files */ +int spaces = 0; /* Spaces since last tab stop */ +int leading_blank = 1; /* Only unexpand leading blanks, */ +/* Overruled by -a option */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void Unexpand, (FILE *f, int all)); + +int main(argc, argv) +int argc; +char *argv[]; + +{ + int all = 0; /* -a flag means unexpand all spaces */ + int i; + FILE *f; + + if (argc > 1 && argv[1][0] == '-') { + if (strcmp(argv[1], "-a") == 0) + all = 1; + else { + fprintf(stderr, "Usage: unexpand [ -a ] [ file ... ]\n"); + exit(1); + } + + --argc; + ++argv; + } + if (argc == 1) + Unexpand(stdin, all); + else + for (i = 1; i < argc; ++i) { + if ((f = fopen(argv[i], "r")) == NULL) { + perror(argv[i]); + exit(1); + } + Unexpand(f, all); + fclose(f); + } + + + /* If there are pending spaces print them. */ + + while (spaces > 0) { + putchar(' '); + --spaces; + } + + return(0); +} + +void Unexpand(f, all) +FILE *f; +int all; + +{ + int c; + + while ((c = getc(f)) != EOF) { + if (c == ' ' && (all || leading_blank)) { + ++column; + ++spaces; + + /* If we have white space up to a tab stop, then output */ + /* A tab. If only one space is required, use a ' '. */ + + if (column % TAB == 0) { + if (spaces == 1) + putchar(' '); + else + putchar('\t'); + + spaces = 0; + } + continue; + } + + /* If a tab character is encountered in the input then */ + /* Simply echo it. Any accumulated spaces can only be */ + /* Since the last tab stop, so ignore them. */ + if (c == '\t') { + column = (column / TAB + 1) * TAB; + spaces = 0; + putchar('\t'); + continue; + } + + /* A non-space character is to be printed. If there */ + /* Are pending spaces, then print them. There will be */ + /* At most TAB-1 spaces to print. */ + while (spaces > 0) { + putchar(' '); + --spaces; + } + + if (c == '\n' || c == '\r') { + column = 0; + leading_blank = 1; + putchar(c); + continue; + } + if (c == '\b') + column = column > 0 ? column - 1 : 0; + else + ++column; + + leading_blank = 0; + putchar(c); + } +} diff --git a/commands/simple/uniq.c b/commands/simple/uniq.c new file mode 100755 index 000000000..e12ce4d16 --- /dev/null +++ b/commands/simple/uniq.c @@ -0,0 +1,195 @@ +/* uniq - compact repeated lines Author: John Woods */ +/* Uniq [-udc] [-n] [+n] [infile [outfile]] + * + * Written 02/08/86 by John Woods, placed into public domain. Enjoy. + * + */ + +/* If the symbol WRITE_ERROR is defined, uniq will exit(1) if it gets a + * write error on the output. This is not (of course) how V7 uniq does it, + * so undefine the symbol if you want to lose your output to a full disk + */ + +#define WRITE_ERROR 1 +#include <ctype.h> +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +char buffer[BUFSIZ]; +int uflag = 1; /* default is union of -d and -u outputs */ +int dflag = 1; /* flags are mutually exclusive */ +int cflag = 0; +int fields = 0; +int chars = 0; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(FILE *xfopen, (char *fn, char *mode)); +_PROTOTYPE(char *skip, (char *s)); +_PROTOTYPE(int equal, (char *s1, char *s2)); +_PROTOTYPE(void show, (char *line, int count)); +_PROTOTYPE(int uniq, (void)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(int getline, (char *buf, int count)); + +FILE *xfopen(fn, mode) +char *fn, *mode; +{ + FILE *p; + + if ((p = fopen(fn, mode)) == NULL) { + perror("uniq"); + fflush(stdout); + exit(1); + } + return(p); +} + +int main(argc, argv) +int argc; +char *argv[]; +{ + char *p; + int inf = -1, outf; + + setbuf(stdout, buffer); + for (--argc, ++argv; argc > 0 && (**argv == '-' || **argv == '+'); + --argc, ++argv) { + if (**argv == '+') + chars = atoi(*argv + 1); + else if (isdigit(argv[0][1])) + fields = atoi(*argv + 1); + else if (argv[0][1] == '\0') + inf = 0; /* - is stdin */ + else + for (p = *argv + 1; *p; p++) { + switch (*p) { + case 'd': + dflag = 1; + uflag = 0; + break; + case 'u': + uflag = 1; + dflag = 0; + break; + case 'c': cflag = 1; break; + default: usage(); + } + } + } + + /* Input file */ + if (argc == 0) + inf = 0; + else if (inf == -1) { /* if - was not given */ + fclose(stdin); + xfopen(*argv++, "r"); + argc--; + } + if (argc == 0) + outf = 1; + else { + fclose(stdout); + xfopen(*argv++, "w"); + argc--; + } + + uniq(); + fflush(stdout); + return(0); +} + +char *skip(s) +char *s; +{ + int n; + + /* Skip fields */ + for (n = fields; n > 0; --n) { + /* Skip blanks */ + while (*s && (*s == ' ' || *s == '\t')) s++; + if (!*s) return s; + while (*s && (*s != ' ' && *s != '\t')) s++; + if (!*s) return s; + } + + /* Skip characters */ + for (n = chars; n > 0; --n) { + if (!*s) return s; + s++; + } + return s; +} + +int equal(s1, s2) +char *s1, *s2; +{ + return !strcmp(skip(s1), skip(s2)); +} + +void show(line, count) +char *line; +int count; +{ + if (cflag) + printf("%4d %s", count, line); + else { + if ((uflag && count == 1) || (dflag && count != 1)) + printf("%s", line); + } +} + +/* The meat of the whole affair */ +char *nowline, *prevline, buf1[1024], buf2[1024]; + +int uniq() +{ + char *p; + int seen; + + /* Setup */ + prevline = buf1; + if (getline(prevline, 1024) < 0) return(0); + seen = 1; + nowline = buf2; + + /* Get nowline and compare if not equal, dump prevline and swap + * pointers else continue, bumping seen count */ + while (getline(nowline, 1024) > 0) { + if (!equal(prevline, nowline)) { + show(prevline, seen); + seen = 1; + p = nowline; + nowline = prevline; + prevline = p; + } else + seen += 1; + } + show(prevline, seen); + return 0; +} + +void usage() +{ + fprintf(stderr, "Usage: uniq [-udc] [+n] [-n] [input [output]]\n"); +} + +int getline(buf, count) +char *buf; +int count; +{ + int c; + int ct = 0; + + while (ct++ < count) { + c = getc(stdin); + if (c < 0) return(-1); + *buf++ = c; + if (c == '\n') { + *buf++ = 0; + return(ct); + } + } + return(ct); +} diff --git a/commands/simple/update.c b/commands/simple/update.c new file mode 100755 index 000000000..28603978d --- /dev/null +++ b/commands/simple/update.c @@ -0,0 +1,24 @@ +/* update - do sync periodically Author: Andy Tanenbaum */ + +#include <sys/types.h> +#include <signal.h> +#include <unistd.h> + +_PROTOTYPE(int main, (void)); + +int main() +{ + /* Release all (?) open file descriptors. */ + close(0); + close(1); + close(2); + + /* Release current directory to avoid locking current device. */ + chdir("/"); + + /* Flush the cache every 30 seconds. */ + while (1) { + sync(); + sleep(30); + } +} diff --git a/commands/simple/uud.c b/commands/simple/uud.c new file mode 100755 index 000000000..a0fe427f5 --- /dev/null +++ b/commands/simple/uud.c @@ -0,0 +1,562 @@ +/* uud - bulletproof version of uudecode */ + +/* + * Uud -- decode a uuencoded file back to binary form. + * + * From the Berkeley original, modified by MSD, RDR, JPHD & WLS. + * The Atari GEMDOS version compiled with MWC 2.x. + * The MSDOS version with TurboC. + * The Unix version with cc. + * this version is made: 25 Nov 1988. + * Jan 2 1990: Change system definition and change MSDOS to open the output + * file for write binary do cr/lf replacement. + */ + +#define UNIX 1 /* define one of: UNIX (Minix too!), MSDOS, or GEMDOS */ + +#ifdef GEMDOS +#define SYSNAME "gemdos" +#define SMALL 1 +#endif +#ifdef MSDOS +#define SYSNAME "msdos" +#define SMALL 1 +#endif +#ifdef UNIX +#define SYSNAME "unix" +#endif + +#include <sys/types.h> +#include <stdarg.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <stdio.h> + +#ifdef GEMDOS +#include <osbind.h> +#define Error(n) { Bconin(2); exit(n); } +#else +#define Error(n) exit(n) +#endif +#ifdef UNIX +#define WRITE "w" +#else +#define WRITE "wb" /* for both MSDOS and GEMDOS! */ +#endif + +#define loop while (1) + +#define NCHARS 256 +#define LINELEN 256 +#define FILELEN 64 +#define NORMLEN 60 /* allows for 80 encoded chars per line */ + +#define SEQMAX 'z' +#define SEQMIN 'a' +char seqc; +int first, secnd, check, numl; + +FILE *in, *out; +char *pos; +char ifname[FILELEN], ofname[FILELEN]; +char *source = NULL, *target = NULL; +char blank, part = '\0'; +int partn, lens; +int debug = 0, nochk = 0, onedone = 0; +int chtbl[NCHARS], cdlen[NORMLEN + 3]; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(char *getnword, (char *str, int n)); +_PROTOTYPE(void gettable, (void)); +_PROTOTYPE(void decode, (void)); +_PROTOTYPE(void getfile, (char *buf)); +_PROTOTYPE(void format, (char *fp, ...)); +_PROTOTYPE(void doprnt, (char *fp, char *ap)); +_PROTOTYPE(void puti, (unsigned int i, unsigned int r)); +_PROTOTYPE(void outc, (int c)); + +int main(argc, argv) int argc; char *argv[]; +{ + int mode; + register int i, j; + char *curarg; + char dest[FILELEN], buf[LINELEN]; + + while ((curarg = argv[1]) != NULL && curarg[0] == '-') { + if (((curarg[1] == 'd') || (curarg[1] == 'D')) && + (curarg[2] == '\0')) { + debug = 1; + } else if (((curarg[1] == 'n') || (curarg[1] == 'N')) && + (curarg[2] == '\0')) { + nochk = 1; + } else if (((curarg[1] == 't') || (curarg[1] == 'T')) && + (curarg[2] == '\0')) { + argv++; + argc--; + if (argc < 2) { + format("uud: Missing target directory.\n"); + Error(15); + } + target = argv[1]; + if (debug) + format("Target dir = %s\n",target); + } else if (((curarg[1] == 's') || (curarg[1] == 'S')) && + (curarg[2] == '\0')) { + argv++; + argc--; + if (argc < 2) { + format("uud: Missing source directory.\n"); + Error(15); + } + source = argv[1]; + if (debug) + format("Source dir = %s\n",source); + } else if (curarg[1] != '\0') { + format("Usage: uud [-n] [-d] [-s dir] [-t dir] [input-file]\n"); + Error(1); + } else + break; + argv++; + argc--; + } + + if (curarg == NULL || ((curarg[0] == '-') && (curarg[1] == '\0'))) { + in = stdin; + strcpy(ifname, "<stdin>"); + } else { + if (source != NULL) { + strcpy(ifname, source); + strcat(ifname, curarg); + } else + strcpy(ifname, curarg); + if ((in = fopen(ifname, "r")) == NULL) { + format("uud: Can't open %s\n", ifname); + Error(2); + } + numl = 0; + } + +/* + * Set up the default translation table. + */ + for (i = 0; i < ' '; i++) chtbl[i] = -1; + for (i = ' ', j = 0; i < ' ' + 64; i++, j++) chtbl[i] = j; + for (i = ' ' + 64; i < NCHARS; i++) chtbl[i] = -1; + chtbl['`'] = chtbl[' ']; /* common mutation */ + chtbl['~'] = chtbl['^']; /* an other common mutation */ + blank = ' '; +/* + * set up the line length table, to avoid computing lotsa * and / ... + */ + cdlen[0] = 1; + for (i = 1, j = 5; i <= NORMLEN; i += 3, j += 4) + cdlen[i] = (cdlen[i + 1] = (cdlen[i + 2] = j)); +/* + * search for header or translation table line. + */ + loop { /* master loop for multiple decodes in one file */ + partn = 'a'; + loop { + if (fgets(buf, sizeof buf, in) == NULL) { + if (onedone) { + if (debug) format("End of file.\n"); + exit(0); + } else { + format("uud: No begin line.\n"); + Error(3); + } + } + numl++; + if (strncmp(buf, "table", (size_t)5) == 0) { + gettable(); + continue; + } + if (strncmp(buf, "begin", (size_t)5) == 0) { + break; + } + } + lens = strlen(buf); + if (lens) buf[--lens] = '\0'; +#ifdef SMALL + if ((pos = getnword(buf, 3))) { + strcpy(dest, pos); + } else +#else + if(sscanf(buf,"begin%o%s", &mode, dest) != 2) +#endif + { + format("uud: Missing filename in begin line.\n"); + Error(10); + } + + if (target != NULL) { + strcpy(ofname, target); + strcat(ofname, dest); + } else + strcpy(ofname, dest); + + if((out = fopen(ofname, WRITE)) == NULL) { + format("uud: Cannot open output file: %s\n", ofname); + Error(4); + } + if (debug) format("Begin uudecoding: %s\n", ofname); + seqc = SEQMAX; + check = nochk ? 0 : 1; + first = 1; + secnd = 0; + decode(); + fclose(out); +#ifdef UNIX + chmod(ofname, mode); +#endif + onedone = 1; + if (debug) format("End uudecoding: %s\n", ofname); + } /* master loop for multiple decodes in one file */ +} + +/* + * Bring back a pointer to the start of the nth word. + */ +char *getnword(str, n) register char *str; register int n; +{ + while((*str == '\t') || (*str == ' ')) str++; + if (! *str) return NULL; + while(--n) { + while ((*str != '\t') && (*str != ' ') && (*str)) str++; + if (! *str) return NULL; + while((*str == '\t') || (*str == ' ')) str++; + if (! *str) return NULL; + } + return str; +} + +/* + * Install the table in memory for later use. + */ +void gettable() +{ + char buf[LINELEN]; + register int c, n = 0; + register char *cpt; + + for (c = 0; c < NCHARS; c++) chtbl[c] = -1; + +again: if (fgets(buf, sizeof buf, in) == NULL) { + format("uud: EOF while in translation table.\n"); + Error(5); + } + numl++; + if (strncmp(buf, "begin", (size_t)5) == 0) { + format("uud: Incomplete translation table.\n"); + Error(6); + } + cpt = buf + strlen(buf) - 1; + *cpt = ' '; + while (*(cpt) == ' ') { + *cpt = 0; + cpt--; + } + cpt = buf; + while (c = *cpt) { + if (chtbl[c] != -1) { + format("uud: Duplicate char in translation table.\n"); + Error(7); + } + if (n == 0) blank = c; + chtbl[c] = n++; + if (n >= 64) return; + cpt++; + } + goto again; +} + +/* + * copy from in to out, decoding as you go along. + */ + +void decode() +{ + char buf[LINELEN], outl[LINELEN]; + register char *bp, *ut; + register int *trtbl = chtbl; + register int n, c, rlen; + register unsigned int len; + + loop { + if (fgets(buf, sizeof buf, in) == NULL) { + format("uud: EOF before end.\n"); + fclose(out); + Error(8); + } + numl++; + len = strlen(buf); + if (len) buf[--len] = '\0'; +/* + * Is it an unprotected empty line before the end line ? + */ + if (len == 0) continue; +/* + * Get the binary line length. + */ + n = trtbl[*buf]; + if (n >= 0) goto decod; +/* + * end of uuencoded file ? + */ + if (strncmp(buf, "end", (size_t)3) == 0) return; +/* + * end of current file ? : get next one. + */ + if (strncmp(buf, "include", (size_t)7) == 0) { + getfile(buf); + continue; + } + format("uud: Bad prefix line %d in file: %s\n",numl, ifname); + if (debug) format("Bad line =%s\n",buf); + Error(11); +/* + * Sequence checking ? + */ +decod: rlen = cdlen[n]; +/* + * Is it the empty line before the end line ? + */ + if (n == 0) continue; +/* + * Pad with blanks. + */ + for (bp = &buf[c = len]; + c < rlen; c++, bp++) *bp = blank; +/* + * Verify if asked for. + */ + if (debug) { + for (len = 0, bp = buf; len < rlen; len++) { + if (trtbl[*bp] < 0) { + format( + "Non uuencoded char <%c>, line %d in file: %s\n", *bp, numl, ifname); + format("Bad line =%s\n",buf); + Error(16); + } + bp++; + } + } +/* + * All this just to check for uuencodes that append a 'z' to each line.... + */ + if (secnd && check) { + secnd = 0; + if (buf[rlen] == SEQMAX) { + check = 0; + if (debug) format("Sequence check turned off (2).\n"); + } else + if (debug) format("Sequence check on (2).\n"); + } else if (first && check) { + first = 0; + secnd = 1; + if (buf[rlen] != SEQMAX) { + check = 0; + if (debug) format("No sequence check (1).\n"); + } else + if (debug) format("Sequence check on (1).\n"); + } +/* + * There we check. + */ + if (check) { + if (buf[rlen] != seqc) { + format("uud: Wrong sequence line %d in %s\n", + numl, ifname); + if (debug) + format( + "Sequence char is <%c> instead of <%c>.\n", buf[rlen], seqc); + Error(18); + } + seqc--; + if (seqc < SEQMIN) seqc = SEQMAX; + } +/* + * output a group of 3 bytes (4 input characters). + * the input chars are pointed to by p, they are to + * be output to file f.n is used to tell us not to + * output all of them at the end of the file. + */ + ut = outl; + len = n; + bp = &buf[1]; + while (n > 0) { + *(ut++) = trtbl[*bp] << 2 | trtbl[bp[1]] >> 4; + n--; + if (n) { + *(ut++) = (trtbl[bp[1]] << 4) | + (trtbl[bp[2]] >> 2); + n--; + } + if (n) { + *(ut++) = trtbl[bp[2]] << 6 | trtbl[bp[3]]; + n--; + } + bp += 4; + } + if ((n = fwrite(outl, (size_t)1, (size_t)len, out)) <= 0) { + format("uud: Error on writing decoded file.\n"); + Error(18); + } + } +} + +/* + * Find the next needed file, if existing, otherwise try further + * on next file. + */ +void getfile(buf) register char *buf; +{ + if ((pos = getnword(buf, 2)) == NULL) { + format("uud: Missing include file name.\n"); + Error(17); + } else + if (source != NULL) { + strcpy(ifname, source); + strcat(ifname, pos); + } else + strcpy(ifname, pos); +#ifdef GEMDOS + if (Fattrib(ifname, 0, 0) < 0) +#else + if (access(ifname, 04)) +#endif + { + if (debug) { + format("Cant find: %s\n", ifname); + format("Continuing to read same file.\n"); + } + } + else { + if (freopen(ifname, "r", in) == in) { + numl = 0; + if (debug) + format("Reading next section from: %s\n", ifname); + } else { + format("uud: Freopen abort: %s\n", ifname); + Error(9); + } + } + loop { + if (fgets(buf, LINELEN, in) == NULL) { + format("uud: No begin line after include: %s\n", ifname); + Error(12); + } + numl++; + if (strncmp(buf, "table", (size_t)5) == 0) { + gettable(); + continue; + } + if (strncmp(buf, "begin", (size_t)5) == 0) break; + } + lens = strlen(buf); + if (lens) buf[--lens] = '\0'; +/* + * Check the part suffix. + */ + if ((pos = getnword(buf, 3)) == NULL ) { + format("uud: Missing part name, in included file: %s\n", ifname); + Error(13); + } else { + part = *pos; + partn++; + if (partn > 'z') partn = 'a'; + if (part != partn) { + format("uud: Part suffix mismatch: <%c> instead of <%c>.\n", + part, partn); + Error(14); + } + if (debug) format("Reading part %c\n", *pos); + } +} + +/* + * Printf style formatting. (Borrowed from MicroEmacs by Dave Conroy.) + * A lot smaller than the full fledged printf. + */ +#ifdef __STDC__ +void format(char *fp, ...) +{ + va_list args; + + va_start (args, fp); + doprnt(fp, (char *)&args); + va_end(args); +} +#else +/* VARARGS1 */ +void format(fp, args) char *fp; +{ + doprnt(fp, (char *)&args); +} +#endif + +void doprnt(fp, ap) +register char *fp; +register char *ap; +{ + register int c, k; + register char *s; + + while ((c = *fp++) != '\0') { + if (c != '%') + outc(c); + else { + c = *fp++; + switch (c) { + case 'd': + puti(*(int *)ap, 10); + ap += sizeof(int); + break; + + case 's': + s = *(char **)ap; + while ((k = *s++) != '\0') + outc(k); + ap += sizeof(char *); + break; + + case 'c': + outc(*(int *)ap); + ap += sizeof(int); + break; + + default: + outc(c); + } + } + } +} + +/* + * Put integer, in radix "r". + */ +void puti(i, r) +register unsigned int i; +register unsigned int r; +{ + register unsigned int q, s; + + if ((q = i / r) != 0) + puti(q, r); + s = i % r; + if (s <= 9) + outc(s + '0'); + else + outc(s - 10 + 'A'); +} +void outc(c) register char c; +{ +#ifdef GEMDOS + if (c == '\n') Bconout(2, '\r'); + Bconout(2, c); +#else + putchar(c); +#endif +} diff --git a/commands/simple/uue.c b/commands/simple/uue.c new file mode 100755 index 000000000..6a07add33 --- /dev/null +++ b/commands/simple/uue.c @@ -0,0 +1,217 @@ +/* uue - bulletproof version of uuencode */ + +/* Uue -- encode a file so that it's printable ascii, short lines + * + * Slightly modified from a version posted to net.sources a while back, + * and suitable for compilation on the IBM PC + * + * modified for Lattice C on the ST - 11.05.85 by MSD + * modified for ALCYON on the ST - 10-24-86 by RDR + * modified a little more for MWC... 02/09/87 by JPHD + * (An optional first argument of the form: -nnumber (e.g. -500), will + * produce a serie of files that long, linked by the include statement, + * such files are automatically uudecoded by the companion program.) + * More mods, - ... 05/06/87 by jphd + * Mods for TOPS 20, and more. 08/06/87 by jphd + * (remove freopen and rindex...change filename generation...) + * (A lot more to do about I/O speed, avoiding completely the stdio.h...) + * May be called as uuencode. Oct 2 1993 by Kees J. Bot + * + */ + + +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#define USAGE +#define FILE_NAME 10 /* affects how long names are truncated */ + +/* ENC is the basic 1 character encoding function to make a char printing */ +#define ENC(c) (((c) & 077) + ' ') + +FILE *fp, *outp; +char ofname[80]; +int lenofname; +int stdo = 0; + +#ifdef ST +#define READ "rb" +#else +#define READ "r" +#endif + +int part = 'a', chap = 'a'; +#define SEQMAX 'z' +#define SEQMIN 'a' +char seqc = SEQMAX; + +int split = 0; +int fileln = 32000; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void maketable, (void)); +_PROTOTYPE(void makename, (void)); +_PROTOTYPE(void encode, (void)); +_PROTOTYPE(void outdec, (char *p)); +_PROTOTYPE(int fr, (char *buf, int cnt)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + char *prog_name; + char *fname; + int filter; + + prog_name = argv[0] + strlen(argv[0]); + while (prog_name > argv[0] && prog_name[-1] != '/') prog_name--; + filter = strcmp(prog_name, "uuencode") == 0; + + if (argc < 2) { + fprintf(stderr, "Usage: %s [-n] inputfile [-]\n", prog_name); + exit(2); + } + if (argv[1][0] == '-') { + fileln = -atoi(argv[1]); + if (fileln <= 0) { + fprintf(stderr, "Wrong file length arg.\n"); + exit(3); + } + split = 1; + argv++; + argc--; + } + if (filter) { /* old uuencode reads from standard input */ + fp = stdin; + } else { + if ((fp = fopen(argv[1], READ)) == NULL) { /* binary input !!! */ + fprintf(stderr, "Cannot open %s\n", argv[1]); + exit(1); + } + } + fname = argv[1] + strlen(argv[1]); + while (fname > argv[1] && fname[-1] != '/') fname--; + strcpy(ofname, fname); + fname = ofname; + do { + if (*fname == '.') *fname = '\0'; + } while (*fname++); + /* 10 char prefix + .uue -> 14 chars MAX */ + lenofname = strlen(ofname); + if (lenofname > FILE_NAME) ofname[FILE_NAME] = '\0'; + strcat(ofname, ".uue"); + lenofname = strlen(ofname); + if (!split && (filter || (argc > 2) && (argv[2][0] == '-'))) { + stdo = 1; + outp = stdout; + } else { + makename(); + if ((outp = fopen(ofname, "w")) == NULL) { + fprintf(stderr, "Cannot open %s\n", ofname); + exit(1); + } + } + maketable(); + fprintf(outp, "begin %o %s\n", 0644, argv[1]); + encode(); + fprintf(outp, "end\n"); + fclose(outp); + return(0); +} + +/* Create ASCII table so a mailer can screw it up and the decode + * program can restore the error. + */ +void maketable() +{ + register int i, j; + + fputs("table\n", outp); + for (i = ' ', j = 0; i < '`'; j++) { + if (j == 32) putc('\n', outp); + fputc(i++, outp); + } + putc('\n', outp); +} + +/* Generate the names needed for single and multiple part encoding. */ +void makename() +{ + if (split) { + ofname[lenofname - 1] = part; + ofname[lenofname - 2] = chap; + } +} + +/* Copy from in to out, encoding as you go along. */ +void encode() +{ + char buf[80]; + register int i, n; + register int lines; + lines = 6; + + for (;;) { + n = fr(buf, 45); + putc(ENC(n), outp); + for (i = 0; i < n; i += 3) outdec(&buf[i]); + putc(seqc, outp); + seqc--; + if (seqc < SEQMIN) seqc = SEQMAX; + putc('\n', outp); + ++lines; + if (split && (lines > fileln)) { + part++; + if (part > 'z') { + part = 'a'; + if (chap == 'z') + chap = 'a'; /* loop ... */ + else + chap++; + } + makename(); + fprintf(outp, "include %s\n", ofname); + fclose(outp); + if ((outp = fopen(ofname, "w")) == NULL) { + fprintf(stderr, "Cannot open %s\n", ofname); + exit(1); + } + maketable(); + fprintf(outp, "begin part %c %s\n", part, ofname); + lines = 6; + } + if (n <= 0) break; + } +} + +/* Output one group of 3 bytes, pointed at by p, on file f. */ +void outdec(p) +register char *p; +{ + register int c1, c2, c3, c4; + + c1 = *p >> 2; + c2 = ((*p << 4) & 060) | ((p[1] >> 4) & 017); + c3 = ((p[1] << 2) & 074) | ((p[2] >> 6) & 03); + c4 = p[2] & 077; + putc(ENC(c1), outp); + putc(ENC(c2), outp); + putc(ENC(c3), outp); + putc(ENC(c4), outp); +} + +/* Fr: like read but stdio */ +int fr(buf, cnt) +register char *buf; +register int cnt; +{ + register int c, i; + for (i = 0; i < cnt; i++) { + c = fgetc(fp); + if (feof(fp)) return(i); + buf[i] = c; + } + return(cnt); +} diff --git a/commands/simple/vol.c b/commands/simple/vol.c new file mode 100755 index 000000000..0e66add7d --- /dev/null +++ b/commands/simple/vol.c @@ -0,0 +1,371 @@ +/* vol - break stdin into volumes Author: Andy Tanenbaum */ + +/* This program reads standard input and writes it onto diskettes, pausing + * at the start of each one. It's main use is for saving files that are + * larger than a single diskette. Vol just writes its standard input onto + * a diskette, and prompts for a new one when it is full. This mechanism + * is transparent to the process producing vol's standard input. For example, + * tar cf - . | vol -w 360 /dev/fd0 + * puts the tar output as as many diskettes as needed. To read them back in, + * use + * vol -r 360 /dev/fd0 | tar xf - + * + * Changed 17 Nov 1993 by Kees J. Bot to handle buffering to slow devices. + * Changed 27 Jul 1994 by Kees J. Bot to auto discover data direction + -rw. + * Changed 19 Sep 1995 by Kees J. Bot to do better buffering to tapes. + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <limits.h> +#include <string.h> +#include <sys/ioctl.h> +#include <sys/mtio.h> +#include <minix/partition.h> +#include <minix/u64.h> + +/* Preferred block size to variable block length tapes, block devices or files. + */ +#define VAR_BLKSIZ 8192 + +/* Required block size multiple of fixed block size tapes (usually updated by + * 'mt status' data) and character devices. + */ +#define FIX_BLKSIZ 512 + +/* Maximum multiple block size. */ +#if __minix_vmd +#define MULT_MAX 1048576 +#else +#define MULT_MAX ((ssize_t) (SSIZE_MAX < 65536L ? SSIZE_MAX : 65536L)) +#endif + +char *buffer = NULL; +size_t block_size = 0, mult_max = 0; +size_t buffer_size; +long volume_size; +char *str_vol_size; +int rflag = 0, wflag = 0, oneflag = 0, variable = 0; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(long str2size, (char *name, char *str, long min, long max, + int assume_kb)); +_PROTOTYPE(void tape_inquire, (char *name, int fd)); +_PROTOTYPE(void allocate_buffer, (void)); +_PROTOTYPE(void diskio, (int fd1, int fd2, char *file1, char *file2)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int volume = 1, fd, tty, i, init, autovolsize; + char *p, *name; + struct stat stb; + struct partition part; + char key; + + /* Fetch and verify the arguments. */ + i = 1; + while (i < argc && argv[i][0] == '-') { + p = argv[i++] + 1; + if (p[0] == '-' && p[1] == 0) { + /* -- */ + i++; + break; + } + while (*p != '\0') { + switch (*p++) { + case 'r': + case 'u': + rflag = 1; + break; + case 'w': + wflag = 1; + break; + case '1': + oneflag = 1; + break; + case 'b': + if (*p == 0) { + if (i == argc) usage(); + p = argv[i++]; + } + block_size = str2size("block", p, + 1L, (long) SSIZE_MAX, 0); + p= ""; + break; + case 'm': + if (*p == 0) { + if (i == argc) usage(); + p = argv[i++]; + } + mult_max = str2size("maximum", p, + 1L, (long) SSIZE_MAX, 0); + p= ""; + break; + default: + usage(); + } + } + } + if (i < argc - 1) { + str_vol_size = argv[i++]; + volume_size = str2size("volume", str_vol_size, 1L, LONG_MAX, 1); + autovolsize = 0; + } else { + volume_size = 0; /* unlimited (long tape) or use DIOCGETP */ + autovolsize = 1; + } + + if (i >= argc) usage(); + name = argv[i]; + + if (!rflag && !wflag) { + /* Auto direction. If there is a terminal at one side then data is + * to go out at the other side. + */ + if (isatty(0)) rflag = 1; + if (isatty(1)) wflag = 1; + } + + if (rflag == wflag) { + fprintf(stderr, "vol: should %s be read or written?\n", name); + usage(); + } + + if (stat(name, &stb) < 0) { + fprintf(stderr, "vol: %s: %s\n", name, strerror(errno)); + exit(1); + } + if (!S_ISBLK(stb.st_mode) && !S_ISCHR(stb.st_mode)) { + fprintf(stderr, "vol: %s is not a device\n", name); + exit(1); + } + variable = !S_ISCHR(stb.st_mode); + + if (!oneflag) { + tty = open("/dev/tty", O_RDONLY); + if (tty < 0) { + fprintf(stderr, "vol: cannot open /dev/tty\n"); + exit(1); + } + } + + /* Buffer initializations are yet to be done. */ + init = 0; + + while (1) { + sleep(1); + if (oneflag) { + if (volume != 1) { + if (rflag) exit(0); + fprintf(stderr, + "vol: can't continue, volume is full\n"); + exit(1); + } + } else { + fprintf(stderr, + "\007Please insert %sput volume %d and hit return\n", + rflag ? "in" : "out", volume); + while (read(tty, &key, sizeof(key)) == 1 && key != '\n') {} + } + + /* Open the special file. */ + fd = open(name, rflag ? O_RDONLY : O_WRONLY); + if (fd < 0) { + fprintf(stderr, "vol: %s: %s\n", name, strerror(errno)); + exit(1); + } + + if (!init) { + /* Ask for the tape block size and allocate a buffer. */ + if (S_ISCHR(stb.st_mode)) tape_inquire(name, fd); + allocate_buffer(); + init = 1; + } + + if (autovolsize) { + /* Ask the driver how big the volume is. */ + if (ioctl(fd, DIOCGETP, &part) < 0) { + autovolsize = 0; + } else { + volume_size = cv64ul(part.size); + } + } + + /* Read or write the requisite number of blocks. */ + if (rflag) { + diskio(fd, 1, name, "stdout"); /* vol -r | tar xf - */ + } else { + diskio(0, fd, "stdin", name); /* tar cf - | vol -w */ + } + close(fd); + volume++; + } +} + +void usage() +{ + fprintf(stderr, + "Usage: vol [-rw1] [-b blocksize] [-m max] [size] block-special\n"); + exit(1); +} + +long str2size(name, str, min, max, assume_kb) +char *name; +char *str; +long min, max; +int assume_kb; +{ + /* Convert a string to a size. The number may be followed by 'm', 'k', 'b' + * or 'w' to multiply the size as shown below. If 'assume_kb' is set then + * kilobytes is the default. + */ + long size, factor; + char *ptr; + int bad; + + errno = 0; + size = strtol(str, &ptr, 10); + bad = (errno != 0 || ptr == str || size < min || size > max); + if (*ptr == 0 && assume_kb) ptr = "k"; + while (!bad && *ptr != 0) { + switch (*ptr++) { + case 'm': + case 'M': + factor = 1024*1024L; break; + case 'k': + case 'K': + factor = 1024; break; + case 'b': + case 'B': + factor = 512; break; + case 'w': + case 'W': + factor = 2; break; + default: + factor = 1; bad = 1; + } + if (size <= max / factor) size *= factor; else bad = 1; + } + if (bad) { + fprintf(stderr, "vol: bad %s size '%s'\n", name, str); + exit(1); + } + return size; +} + +void tape_inquire(name, fd) +char *name; +int fd; +{ + /* If the device happens to be a tape, then what is its block size? */ + struct mtget mtget; + + if (ioctl(fd, MTIOCGET, &mtget) < 0) { + if (errno != ENOTTY) { + fprintf(stderr, "vol: %s: %s\n", name, + strerror(errno)); + exit(1); + } + } else { + if (mtget.mt_blksize > SSIZE_MAX) { + fprintf(stderr, + "vol: %s: tape block size (%lu) is too large to handle\n", + name, (unsigned long) mtget.mt_blksize); + exit(1); + } + if (mtget.mt_blksize == 0) { + variable = 1; + } else { + /* fixed */ + block_size = mtget.mt_blksize; + } + } +} + +void allocate_buffer() +{ + /* Set block size and maximum multiple. */ + if (block_size == 0) block_size = variable ? 1 : FIX_BLKSIZ; + if (mult_max == 0) mult_max = variable ? VAR_BLKSIZ : MULT_MAX; + + /* Stretch the buffer size to the max. */ + buffer_size = mult_max / block_size * block_size; + if (buffer_size == 0) buffer_size = block_size; + + if (volume_size % block_size != 0) { + fprintf(stderr, + "vol: volume size (%s) is not a multiple of the block size (%lu)\n", + str_vol_size, (unsigned long) block_size); + exit(1); + } + + buffer = (char *) malloc(buffer_size); + if (buffer == NULL) { + fprintf(stderr, "vol: cannot allocate a %luk buffer\n", + (unsigned long) buffer_size / 1024); + exit(1); + } +} + +void diskio(fd1, fd2, file1, file2) +int fd1, fd2; +char *file1, *file2; +{ +/* Read 'volume_size' bytes from 'fd1' and write them on 'fd2'. Watch out for + * the fact that reads on pipes can return less than the desired data. + */ + + ssize_t n, in_needed, in_count, out_count; + long needed = volume_size; + int eof = 0; + + for (;;) { + if (volume_size == 0) needed = buffer_size; + + if (needed == 0) break; + + in_count = 0; + in_needed = needed > buffer_size ? buffer_size : needed; + while (in_count < in_needed) { + n = in_needed - in_count; + n = eof ? 0 : read(fd1, buffer + in_count, n); + if (n == 0) { + eof = 1; + if ((n = in_count % block_size) > 0) { + n = block_size - n; + memset(buffer + in_count, '\0', n); + if ((in_count += n) > in_needed) + in_count = in_needed; + } + break; + } + if (n < 0) { + fprintf(stderr, "vol: %s: %s\n", + file1, strerror(errno)); + exit(1); + } + in_count += n; + } + if (in_count == 0) exit(0); /* EOF */ + out_count = 0; + while (out_count < in_count) { + n = in_count - out_count; + n = write(fd2, buffer + out_count, n); + if (n < 0) { + fprintf(stderr, "vol: %s: %s\n", + file2, strerror(errno)); + exit(1); + } + out_count += n; + } + needed -= in_count; + } +} diff --git a/commands/simple/wc.c b/commands/simple/wc.c new file mode 100755 index 000000000..2ce90ea7c --- /dev/null +++ b/commands/simple/wc.c @@ -0,0 +1,151 @@ +/* wc - count lines, words and characters Author: David Messer */ + +#include <ctype.h> +#include <stdlib.h> +#include <stdio.h> + +/* + * + * Usage: wc [-lwc] [names] + * + * Flags: + * l - count lines. + * w - count words. + * c - count characters. + * + * Flags l, w, and c are default. + * Words are delimited by any non-alphabetic character. + * + * Released into the PUBLIC-DOMAIN 02/10/86 + * + * If you find this program to be of use to you, a donation of + * whatever you think it is worth will be cheerfully accepted. + * + * Written by: David L. Messer + * P.O. Box 19130, Mpls, MN, 55119 + * Program (heavily) modified by Andy Tanenbaum + */ + + +int lflag; /* Count lines */ +int wflag; /* Count words */ +int cflag; /* Count characters */ + +long lcount; /* Count of lines */ +long wcount; /* Count of words */ +long ccount; /* Count of characters */ + +long ltotal; /* Total count of lines */ +long wtotal; /* Total count of words */ +long ctotal; /* Total count of characters */ + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(void count, (FILE *f)); +_PROTOTYPE(void usage, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int k; + char *cp; + int tflag, files; + + /* Get flags. */ + files = argc - 1; + k = 1; + cp = argv[1]; + if (argc > 1 && *cp++ == '-') { + files--; + k++; /* points to first file */ + while (*cp != 0) { + switch (*cp) { + case 'l': lflag++; break; + case 'w': wflag++; break; + case 'c': cflag++; break; + default: usage(); + } + cp++; + } + } + + /* If no flags are set, treat as wc -lwc. */ + if (!lflag && !wflag && !cflag) { + lflag = 1; + wflag = 1; + cflag = 1; + } + + /* Process files. */ + tflag = files >= 2; /* set if # files > 1 */ + + /* Check to see if input comes from std input. */ + if (k >= argc) { + count(stdin); + if (lflag) printf(" %6ld", lcount); + if (wflag) printf(" %6ld", wcount); + if (cflag) printf(" %6ld", ccount); + printf(" \n"); + fflush(stdout); + exit(0); + } + + /* There is an explicit list of files. Loop on files. */ + while (k < argc) { + FILE *f; + + if ((f = fopen(argv[k], "r")) == NULL) { + fprintf(stderr, "wc: cannot open %s\n", argv[k]); + } else { + count(f); + if (lflag) printf(" %6ld", lcount); + if (wflag) printf(" %6ld", wcount); + if (cflag) printf(" %6ld", ccount); + printf(" %s\n", argv[k]); + fclose(f); + } + k++; + } + + if (tflag) { + if (lflag) printf(" %6ld", ltotal); + if (wflag) printf(" %6ld", wtotal); + if (cflag) printf(" %6ld", ctotal); + printf(" total\n"); + } + fflush(stdout); + return(0); +} + +void count(f) +FILE *f; +{ + register int c; + register int word = 0; + + lcount = 0; + wcount = 0; + ccount = 0L; + + while ((c = getc(f)) != EOF) { + ccount++; + + if (isspace(c)) { + if (word) wcount++; + word = 0; + } else { + word = 1; + } + + if (c == '\n' || c == '\f') lcount++; + } + ltotal += lcount; + wtotal += wcount; + ctotal += ccount; +} + +void usage() +{ + fprintf(stderr, "Usage: wc [-lwc] [name ...]\n"); + exit(1); +} diff --git a/commands/simple/which.c b/commands/simple/which.c new file mode 100755 index 000000000..aedb50d44 --- /dev/null +++ b/commands/simple/which.c @@ -0,0 +1,86 @@ +/* which - search paths for executable */ + +#define DELIMITER ':' + +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +_PROTOTYPE(int main, (int argc, char **argv)); + +int main(ac, av) +int ac; +char **av; +{ + char *path, *cp; + char buf[400]; + char prog[400]; + char patbuf[512]; + int quit, none; + int excode = 0; + + if (ac < 2) { + fprintf(stderr, "Usage: %s cmd [cmd, ..]\n", *av); + exit(1); + } + av[ac] = 0; + for (av++; *av; av++) { + + quit = 0; + none = 1; + if ((path = getenv("PATH")) == NULL) { + fprintf(stderr, "Null path.\n"); + exit(0); + } + strcpy(patbuf, path); + path = patbuf; + cp = path; + + while (1) { + cp = strchr(path, DELIMITER); + if (cp == NULL) + quit++; + else + *cp = '\0'; + + if (strcmp(path, "") == 0 && quit == 0) { + sprintf(buf, "%s./%s", path, *av); + } else + sprintf(buf, "%s/%s", path, *av); + + /* Fprintf(stderr,"Trying %s, path %s\n",buf,path); */ + + path = ++cp; + + if (access(buf, 1) == 0) { + printf("%s\n", buf); + none = 0; + } + sprintf(prog, "%s.%s", buf, "prg"); + if (access(prog, 1) == 0) { + printf("%s\n", prog); + none = 0; + } + sprintf(prog, "%s.%s", buf, "ttp"); + if (access(prog, 1) == 0) { + printf("%s\n", prog); + none = 0; + } + sprintf(prog, "%s.%s", buf, "tos"); + if (access(prog, 1) == 0) { + printf("%s\n", prog); + none = 0; + } + if (quit) { + if (none) { + fprintf(stderr, "No %s in %s\n", *av, getenv("PATH")); + excode = 1; + } + break; + } + } + } + return(excode); +} diff --git a/commands/simple/who.c b/commands/simple/who.c new file mode 100755 index 000000000..ac25dc0a6 --- /dev/null +++ b/commands/simple/who.c @@ -0,0 +1,71 @@ +/* who 1.5 - tell who is currently logged in Author: Kees J. Bot + * 9 Jul 1989 + */ +#define nil 0 +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/stat.h> +#include <utmp.h> +#include <time.h> +#include <string.h> + +char PATH_UTMP[] = "/etc/utmp"; + +char day[] = "SunMonTueWedThuFriSat"; +char month[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; + +int main(int argc, char **argv) +{ + char *tmp= PATH_UTMP; + FILE *f; + struct utmp ut; + struct tm *tm; + int slot, wtmp= 0, once= 0; + + if (argc > 3) { + fprintf(stderr, "Usage: who <account-file> | who am i\n"); + exit(1); + } + if (argc == 2) { + tmp= argv[1]; + wtmp= 1; + } + + if ((f= fopen(tmp, "r")) == nil) { + fprintf(stderr, "who: can't open %s\n", tmp); + exit(1); + } + if (argc == 3) { + if ((slot= ttyslot()) < 0) { + fprintf(stderr, "who: no access to terminal.\n"); + exit(1); + } + fseek(f, (off_t) sizeof(ut) * slot, 0); + once= 1; + } + + while (fread((char *) &ut, sizeof(ut), 1, f) == 1) { + if (!wtmp && ut.ut_name[0] == 0) continue; + + tm= localtime(&ut.ut_time); + + printf("%-9.8s %-9.8s %.3s %.3s %2d %02d:%02d", + ut.ut_name, + ut.ut_line, + day + (3 * tm->tm_wday), + month + (3 * tm->tm_mon), + tm->tm_mday, + tm->tm_hour, + tm->tm_min + ); + + if (ut.ut_host[0] != 0) printf(" (%.*s)", + (int) sizeof(ut.ut_host), ut.ut_host); + + printf("\n"); + if (once) break; + } + exit(0); +} diff --git a/commands/simple/whoami.c b/commands/simple/whoami.c new file mode 100755 index 000000000..99ec440cb --- /dev/null +++ b/commands/simple/whoami.c @@ -0,0 +1,19 @@ +/* whoami - print the current user name Author: Terrence W. Holm */ + +#include <sys/types.h> +#include <pwd.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +_PROTOTYPE(int main, (void)); + +int main() +{ + struct passwd *pw_entry; + + pw_entry = getpwuid(geteuid()); + if (pw_entry == NULL) exit(1); + puts(pw_entry->pw_name); + return(0); +} diff --git a/commands/simple/write.c b/commands/simple/write.c new file mode 100755 index 000000000..14d0c9390 --- /dev/null +++ b/commands/simple/write.c @@ -0,0 +1,268 @@ +/* write - write to a logged in user Authors: N. Andrew and F. van Kempen */ + +/* + * Usage: write [-c] [-v] user [tty] + * -c Read & write one character at a time (cbreak mode) + * -v Verbose + * + * Version: 1.6 10/24/92 + * + * NOTES: Write requires 1.4a (or higher) libraries, + * for getopt(), strchr(). + * + * Authors: Nick Andrew (nick@nswitgould.oz) - Public Domain + * Fred van Kempen (minixug!waltje@kyber.uucp) + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <pwd.h> +#include <termios.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <utmp.h> +#include <time.h> +#include <stdio.h> + +static char *Version = "@(#) WRITE 1.6 (10/24/92)"; + +int otty; /* file desc of callee's terminal */ +short int cbreak = 0; /* are we in CBREAK (-c) mode? */ +short int verbose = 0; /* are we in VERBOSE (-v) mode? */ +short int writing = 0; /* is there a connection? */ +char *user = NULL; /* callee's user name */ +char *tty = NULL; /* callee's terminal if given */ +char *ourtty = NULL; /* our terminal name */ +struct termios ttyold, ttynew; /* our tty controlling structs */ + +extern int optind; + +_PROTOTYPE(int main, (int argc, char **argv)); +_PROTOTYPE(char *finduser, (void)); +_PROTOTYPE(void settty, (char *utty)); +_PROTOTYPE(void sayhello, (void)); +_PROTOTYPE(void escape, (char *cmd)); +_PROTOTYPE(void writetty, (void)); +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(void intr, (int dummy)); + +char *finduser() +{ +/* Search the UTMP database for the user we want. */ + + static char utmptty[16]; + struct utmp utmp; + struct passwd *userptr; + int utmpfd; + + ourtty = ttyname(0); + if (ourtty == NULL) ourtty = "/dev/console"; + + if (user == NULL) exit(-1); + if ((userptr = getpwnam(user)) == NULL) { + fprintf(stderr, "No such user: %s\n", user); + return(NULL); + } + if (verbose) fprintf(stderr, "Trying to write to %s\n", + userptr->pw_gecos); + + if ((utmpfd = open("/etc/utmp", O_RDONLY)) < 0) { + fprintf(stderr, "Cannot open utmp file\n"); + return(NULL); + } + utmptty[0] = '\0'; + + /* We want to find if 'user' is logged on, and return in utmptty[] + * 'user' `s terminal, and if 'user' is logged onto the tty the + * caller specified, return that tty name. */ + while (read(utmpfd, (char *) &utmp, sizeof(utmp)) == sizeof(utmp)) { + /* is this the user we are looking for? */ + if (strncmp(utmp.ut_name, user, sizeof(utmp.ut_name))) continue; + + strcpy(utmptty, utmp.ut_line); + /* is he on the terminal we want to write to? */ + if (tty == NULL || !strcmp(utmptty, tty)) { + break; + } + } + + if (utmptty[0] == '\0') { + fprintf(stderr, "%s is not logged on\n", user); + return( NULL); + } + if (tty != NULL && strcmp(utmptty, tty)) { + fprintf(stderr, "%s is logged onto %s, not %s\n", user, utmptty, tty); + return( NULL); + } + close(utmpfd); + + if (verbose) fprintf(stderr, "Writing to %s on %s\n", user, utmptty); + return(utmptty); +} + + +void settty(utty) +char *utty; /* name of terminal found in utmp */ +{ +/* Open other person's terminal and setup our own terminal. */ + + char buff[48]; + + sprintf(buff, "/dev/%s", utty); + if ((otty = open(buff, O_WRONLY)) < 0) { + fprintf(stderr, "Cannot open %s to write to %s\n", utty, user); + fprintf(stderr, "It may have write permission turned off\n"); + exit(-1); + } + tcgetattr(0, &ttyold); + tcgetattr(0, &ttynew); + ttynew.c_lflag &= ~(ICANON|ECHO); + signal(SIGINT, intr); + if (cbreak) tcsetattr(0, TCSANOW, &ttynew); +} + + +void sayhello() +{ + struct passwd *pw; + char buff[128]; + long now; + char *sp; + + time(&now); + + pw = getpwuid(getuid()); + if (pw == NULL) { + fprintf(stderr, "unknown user\n"); + exit(-1); + } + if ((sp = strrchr(ourtty, '/')) != NULL) + ++sp; + else + sp = ourtty; + + sprintf(buff, "\nMessage from %s (%s) %-24.24s...\n", + pw->pw_name, sp, ctime(&now)); + + write(otty, buff, strlen(buff)); + printf("\007\007"); + fflush(stdout); +} + + +void escape(cmd) +char *cmd; +{ +/* Shell escape. */ + + register char *x; + + write(1, "!\n", 2); + for (x = cmd; *x; ++x) + if (*x == '\n') *x = '\0'; + + system(cmd); + write(1, "!\n", 2); +} + + +void writetty() +{ +/* The write loop. */ + + char line[80]; + int n, cb_esc; + + writing = 1; + cb_esc = 0; + + while ((n = read(0, line, 79)) > 0) { + if (line[0] == '\004') break; /* EOT */ + + if (cbreak && line[0] == '\n') cb_esc = 1; + + if (cbreak) write(1, line, n); + + if (line[0] == '!') { + if (cbreak && cb_esc) { + cb_esc = 0; + tcsetattr(0, TCSANOW, &ttyold); + read(0, line, 79); + escape(line); + tcsetattr(0, TCSANOW, &ttynew); + } else if (cbreak) + write(otty, line, n); + else + escape(&line[1]); + continue; + } + write(otty, line, n); + } + write(1, "\nEOT\n", 5); + write(otty, "\nEOT\n", 5); +} + + +void usage() +{ + fprintf(stderr, "usage: write [-c] [-v] user [tty]\n"); + fprintf(stderr, "\t-c : cbreak mode\n\t-v : verbose\n"); + exit(-1); +} + + +int main(argc, argv) +int argc; +char *argv[]; +{ + register int c; + char *sp; + + setbuf(stdout, (char *) NULL); + + /* Parse options. */ + while ((c = getopt(argc, argv, "cv")) != EOF) switch (c) { + case 'c': cbreak = 1; break; + case 'v': verbose = 1; break; + default: + usage(); + } + + /* Parse user and tty arguments */ + if (optind < argc) { + user = argv[optind++]; + + /* WTMP usernames are 1-8 chars */ + if (strlen(user) > 8) *(user + 8) = '\0'; + + if (optind < argc) { + tty = argv[optind++]; + if (optind < argc) usage(); + } + } else + usage(); + + sp = finduser(); /* find which tty to write onto */ + if (sp != NULL) { /* did we find one? */ + settty(sp); /* setup our terminal */ + sayhello(); /* print the initial message */ + writetty(); /* the write loop */ + tcsetattr(0, TCSANOW, &ttyold); + exit(0); + } + return(-1); +} + +void intr(dummy) +int dummy; /* to satisfy the prototype */ +{ +/* The interrupt key has been hit. exit cleanly. */ + + signal(SIGINT, SIG_IGN); + fprintf(stderr, "\nInterrupt. Exiting write\n"); + tcsetattr(0, TCSANOW, &ttyold); + if (writing) write(otty, "\nEOT\n", 5); + exit(0); +} diff --git a/commands/simple/writeisofs.c b/commands/simple/writeisofs.c new file mode 100644 index 000000000..dca640cdc --- /dev/null +++ b/commands/simple/writeisofs.c @@ -0,0 +1,1048 @@ + +/* writeisofs - simple ISO9660-format-image writing utility */ + +#include <stdio.h> +#include <time.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <dirent.h> +#include <ctype.h> + +#include <sys/stat.h> + +#define Writefield(fd, f) Write(fd, &(f), sizeof(f)) + +extern char *optarg; +extern int optind; + +typedef unsigned char u_int8_t; +typedef unsigned short int u_int16_t; +typedef unsigned long int u_int32_t; + +#ifndef min +#define min(a,b) ((a) < (b) ? (a) : (b)) +#endif + +#define FLAG_DIR 2 + +#include <sys/types.h> +#include <sys/stat.h> + +#define NAMELEN (DIRSIZ+5) +#define ISONAMELEN 11 +#define PLATFORM_80X86 0 + +#define ISO_SECTOR 2048 +#define VIRTUAL_SECTOR 512 + +#define CURRENTDIR "." +#define PARENTDIR ".." + +/* *** CD (disk) data structures ********************* */ + +/* primary volume descriptor */ + +struct pvd { + u_int8_t one; + char set[6]; + u_int8_t zero; + char system[32]; + char volume[32]; + u_int8_t zeroes1[8]; + u_int32_t sectors[2]; + u_int8_t zeroes2[32]; + u_int16_t setsize[2]; + u_int16_t seq[2]; + u_int16_t sectorsize[2]; + u_int32_t pathtable[2]; + u_int32_t first_little_pathtable_start; + u_int32_t second_little_pathtable_start; + u_int32_t first_big_pathtable_start; + u_int32_t second_big_pathtable_start; + u_int8_t rootrecord[34]; + u_int8_t volumeset[128]; + u_int8_t publisher[128]; + u_int8_t preparer[128]; + u_int8_t application[128]; + u_int8_t copyrightfile[37]; + u_int8_t abstractfile[37]; + u_int8_t bibliofile[37]; + u_int8_t create[17]; + u_int8_t modified[17]; + char expiry[17]; + u_int8_t effective[17]; + u_int8_t one2; + u_int8_t zero2; + u_int8_t zeroes3[512]; + u_int8_t zeroes4[653]; +}; + +/* boot record volume descriptor */ + +struct bootrecord { + u_int8_t indicator; /* 0 */ + char set[5]; /* "CD001" */ + u_int8_t version; /* 1 */ + char ident[32]; /* "EL TORITO SPECIFICATION" */ + u_int8_t zero[32]; /* unused, must be 0 */ + u_int32_t bootcatalog; /* starting sector of boot catalog */ + u_int8_t zero2[1973]; /* unused, must be 0 */ +}; + +/* boot catalog validation entry */ + +struct bc_validation { + u_int8_t headerid; /* 1 */ + u_int8_t platform; /* 0: 80x86; 1: powerpc; 2: mac */ + u_int8_t zero[2]; /* unused, must be 0 */ + char idstring[24]; /* id string */ + u_int16_t checksum; + u_int8_t keys[2]; /* 0x55AA */ +}; + +/* boot catalog initial/default entry */ + +#define INDICATE_BOOTABLE 0x88 + +#define BOOTMEDIA_NONE 0 +#define BOOTMEDIA_120M 1 +#define BOOTMEDIA_144M 2 +#define BOOTMEDIA_288M 3 +#define BOOTMEDIA_HARDDISK 4 + +struct bc_initial { + u_int8_t indicator; /* INDICATE_BOOTABLE */ + u_int8_t media; /* BOOTMEDIA_* */ + u_int16_t seg; /* load segment or 0 for default */ + u_int8_t type; /* system type (from part. table) */ + u_int8_t zero; + u_int16_t sectors; + u_int32_t startsector; + u_int8_t zero2[20]; +}; + +/* directory entry */ + +struct dir { + u_int8_t recordsize; + u_int8_t extended; + u_int32_t datasector[2]; + u_int32_t filesize[2]; + u_int8_t year; + u_int8_t month; + u_int8_t day; + u_int8_t hour; + u_int8_t minute; + u_int8_t second; + u_int8_t offset; + u_int8_t flags; + u_int8_t interleaved; + u_int8_t interleavegap; + u_int16_t sequence[2]; + u_int8_t namelen; + char name[NAMELEN]; +}; + +/* *** program (memory) data structures ********************* */ + +struct node { + char name[NAMELEN]; + int isdir; + int pathtablerecord; + struct node *firstchild, *nextchild; + + /* filled out at i/o time */ + u_int32_t startsector, bytesize; +}; + +int n_reserved_pathtableentries = 0, n_used_pathtableentries = 0; + +ssize_t +Write(int fd, void *buf, ssize_t len) +{ + ssize_t r; + if((r=write(fd, buf, len)) != len) { + if(r < 0) { perror("write"); } + fprintf(stderr, "failed or short write - aborting.\n"); + exit(1); + } + return len; +} + +off_t +Lseek(int fd, off_t pos, int rel) +{ + off_t r; + + if((r=lseek(fd, pos, rel)) < 0) { + perror("lseek"); + fprintf(stderr, "lseek failed - aborting.\n"); + exit(1); + } + + return r; +} + +void +writesector(int fd, char *block, int *currentsector) +{ + Write(fd, block, ISO_SECTOR); + (*currentsector)++; + return; +} + +void +seeksector(int fd, int sector, int *currentsector) +{ + Lseek(fd, sector*ISO_SECTOR, SEEK_SET); + *currentsector = sector; +} + +void +seekwritesector(int fd, int sector, char *block, int *currentsector) +{ + seeksector(fd, sector, currentsector); + writesector(fd, block, currentsector); +} + +ssize_t +Read(int fd, void *buf, ssize_t len) +{ + ssize_t r; + if((r=read(fd, buf, len)) != len) { + if(r < 0) { perror("read"); } + fprintf(stderr, "failed or short read.\n"); + exit(1); + } + + return len; +} + +void both16(unsigned char *both, unsigned short i16) +{ + unsigned char *little, *big; + + little = both; + big = both + 2; + + little[0] = big[1] = i16 & 0xFF; + little[1] = big[0] = (i16 >> 8) & 0xFF; +} + +void both32(unsigned char *both, unsigned long i32) +{ + unsigned char *little, *big; + + little = both; + big = both + 4; + + little[0] = big[3] = i32 & 0xFF; + little[1] = big[2] = (i32 >> 8) & 0xFF; + little[2] = big[1] = (i32 >> 16) & 0xFF; + little[3] = big[0] = (i32 >> 24) & 0xFF; +} + +#define MINDIRLEN 1 +#define MAXDIRLEN 31 + +#define MAXLEVEL 8 + +static int cmpf(const void *v1, const void *v2) +{ + struct node *n1, *n2; + int i; + char f1[NAMELEN], f2[NAMELEN]; + + n1 = (struct node *) v1; + n2 = (struct node *) v2; + strcpy(f1, n1->name); + strcpy(f2, n2->name); + for(i = 0; i < strlen(f1); i++) f1[i] = toupper(f1[i]); + for(i = 0; i < strlen(f2); i++) f2[i] = toupper(f2[i]); + + + return -strcmp(f1, f2); +} + +void +maketree(struct node *thisdir, char *name, int level) +{ + DIR *dir; + struct dirent *e; + struct node *dirnodes = NULL; + int reserved_dirnodes = 0, used_dirnodes = 0; + struct node *child; + + thisdir->firstchild = NULL; + thisdir->isdir = 1; + thisdir->startsector = 0xdeadbeef; + + if(level >= MAXLEVEL) { + fprintf(stderr, "ignoring entries in %s (too deep for iso9660)\n", + name); + return; + } + + if(!(dir = opendir(CURRENTDIR))) { + perror("opendir"); + return; + } + + /* how many entries do we need to allocate? */ + while(readdir(dir)) reserved_dirnodes++; + if(!reserved_dirnodes) { + closedir(dir); + return; + } + + if(!(dirnodes = malloc(sizeof(*dirnodes)*reserved_dirnodes))) { + fprintf(stderr, "couldn't allocate dirnodes (%d bytes)\n", + sizeof(*dirnodes)*reserved_dirnodes); + exit(1); + } + + + /* remember all entries in this dir */ + rewinddir(dir); + + child = dirnodes; + while((e=readdir(dir))) { + struct stat st; + mode_t type; + if(!strcmp(e->d_name, CURRENTDIR) || !strcmp(e->d_name, PARENTDIR)) + continue; + if(stat(e->d_name, &st) < 0) { + perror(e->d_name); + fprintf(stderr, "failed to stat file/dir\n"); + exit(1); + } + + type = st.st_mode & S_IFMT; + +/* + printf("%s type: %x dir: %x file: %x\n", + e->d_name, type, S_IFDIR, S_IFREG); + */ + if(type != S_IFDIR && type != S_IFREG) + continue; + + used_dirnodes++; + if(used_dirnodes > reserved_dirnodes) { + fprintf(stderr, "huh, directory entries appeared " + "(not enough pre-allocated nodes; this can't happen) ?\n"); + exit(1); + } + + if(type == S_IFDIR) { + child->isdir = 1; + } else { + child->isdir = 0; + child->firstchild = NULL; + } + strncpy(child->name, e->d_name, sizeof(child->name)); + + child++; + } + + closedir(dir); + + if(!used_dirnodes) + return; + + if(!(dirnodes=realloc(dirnodes, used_dirnodes*sizeof(*dirnodes)))) { + fprintf(stderr, "realloc() of dirnodes failed - aborting\n"); + exit(1); + } + + qsort(dirnodes, used_dirnodes, sizeof(*dirnodes), cmpf); + + child = dirnodes; + + while(used_dirnodes--) { + child->nextchild = thisdir->firstchild; + thisdir->firstchild = child; + if(child->isdir) { + if(chdir(child->name) < 0) { + perror(child->name); + } else { + maketree(child, child->name, level+1); + if(chdir(PARENTDIR) < 0) { + perror("chdir() failed"); + fprintf(stderr, "couldn't chdir() to parent, aborting\n"); + exit(1); + } + } + } + + child++; + } + +} + +void +little32(unsigned char *dest, u_int32_t src) +{ + dest[0] = ((src >> 0) & 0xFF); + dest[1] = ((src >> 8) & 0xFF); + dest[2] = ((src >> 16) & 0xFF); + dest[3] = ((src >> 24) & 0xFF); + + return; +} + +void +little16(unsigned char *dest, u_int16_t src) +{ + dest[0] = ((src >> 0) & 0xFF); + dest[1] = ((src >> 8) & 0xFF); + + return; +} + +void +big32(unsigned char *dest, u_int32_t src) +{ + dest[3] = ((src >> 0) & 0xFF); + dest[2] = ((src >> 8) & 0xFF); + dest[1] = ((src >> 16) & 0xFF); + dest[0] = ((src >> 24) & 0xFF); + return; +} + +void +big16(unsigned char *dest, u_int16_t src) +{ + dest[1] = ((src >> 0) & 0xFF); + dest[0] = ((src >> 8) & 0xFF); + return; +} + + +void +traversetree(struct node *root, int level, int littleendian, + int maxlevel, int *bytes, int fd, int parentrecord, int *recordno) +{ + struct node *child; + struct pte { + u_int8_t len; + u_int8_t zero; + u_int32_t startsector; + u_int16_t parent; + } pte; + + if(level == maxlevel) { + int i; + char newname[NAMELEN]; + if(!root->isdir) + return; + pte.zero = 0; + if(level == 1) { + /* root */ + pte.len = 1; + pte.parent = 1; + root->name[0] = root->name[1] = '\0'; + } else { + pte.len = strlen(root->name); + pte.parent = parentrecord; + } + pte.startsector = root->startsector; + root->pathtablerecord = (*recordno)++; + + if(littleendian) { + little32((unsigned char *) &pte.startsector, pte.startsector); + little16((unsigned char *) &pte.parent, pte.parent); + } else { + big32((unsigned char *) &pte.startsector, pte.startsector); + big16((unsigned char *) &pte.parent, pte.parent); + } + + *bytes += Write(fd, &pte.len, sizeof(pte.len)); + *bytes += Write(fd, &pte.zero, sizeof(pte.zero)); + *bytes += Write(fd, &pte.startsector, sizeof(pte.startsector)); + *bytes += Write(fd, &pte.parent, sizeof(pte.parent)); + if(!(pte.len%2)) + root->name[pte.len++] = '\0'; + for(i = 0; i < pte.len; i++) + newname[i] = toupper(root->name[i]); + *bytes += Write(fd, newname, pte.len); + return; + } + + for(child = root->firstchild; child; child = child->nextchild) + if(child->isdir) + traversetree(child, level+1, littleendian, + maxlevel, bytes, fd, root->pathtablerecord, + recordno); + + return; +} + +int +makepathtables(struct node *root, int littleendian, int *bytes, int fd) +{ + int level; + static char block[ISO_SECTOR]; + int recordno; + + recordno = 1; + + *bytes = 0; + + for(level = 1; level <= MAXLEVEL; level++) + traversetree(root, 1, littleendian, level, bytes, fd, 1, &recordno); + + if(*bytes % ISO_SECTOR) { + ssize_t x; + x = ISO_SECTOR-(*bytes % ISO_SECTOR); + write(fd, block, x); + *bytes += x; + } + + return *bytes/ISO_SECTOR; +} + +ssize_t +write_direntry(char *origname, u_int32_t sector, u_int32_t size, int isdir, + int fd) +{ + int namelen, total = 0; + struct dir entry; + char copyname[NAMELEN]; + + memset(&entry, 0, sizeof(entry)); + + if(!strcmp(origname, CURRENTDIR)) { + namelen = 1; + } else if(!strcmp(origname, PARENTDIR)) { + entry.name[0] = '\001'; + namelen = 1; + } else { + int i; + strcpy(copyname, origname); + namelen = strlen(copyname); + + if(namelen >= ISONAMELEN) { + fprintf(stderr, "%s: truncated, too long for iso9660\n", copyname); + namelen = ISONAMELEN; + copyname[namelen] = '\0'; + } + + strcpy(entry.name, copyname); + for(i = 0; i < namelen; i++) + entry.name[i] = toupper(entry.name[i]); + + /* padding byte + system field */ + entry.name[namelen] = '\0'; + entry.name[namelen+1] = '\0'; + entry.name[namelen+2] = '\0'; + } + entry.namelen = namelen; /* original length */ + if(!(namelen%2)) namelen++; /* length with padding byte */ + + + /* XXX 2 extra bytes for 'system use'.. */ + entry.recordsize = 33 + namelen; + both32((unsigned char *) entry.datasector, sector); + both32((unsigned char *) entry.filesize, size); + + if(isdir) entry.flags = FLAG_DIR; + + /* XXX node date */ + + both16((unsigned char *) entry.sequence, 1); + + total = Write(fd, &entry.recordsize, sizeof(entry.recordsize)); + total += Write(fd, &entry.extended, sizeof(entry.extended)); + total += Write(fd, entry.datasector, sizeof(entry.datasector)); + total += Write(fd, entry.filesize, sizeof(entry.filesize)); + total += Write(fd, &entry.year, sizeof(entry.year)); + total += Write(fd, &entry.month, sizeof(entry.month)); + total += Write(fd, &entry.day, sizeof(entry.day)); + total += Write(fd, &entry.hour, sizeof(entry.hour)); + total += Write(fd, &entry.minute, sizeof(entry.minute)); + total += Write(fd, &entry.second, sizeof(entry.second)); + total += Write(fd, &entry.offset, sizeof(entry.offset)); + total += Write(fd, &entry.flags, sizeof(entry.flags)); + total += Write(fd, &entry.interleaved, sizeof(entry.interleaved)); + total += Write(fd, &entry.interleavegap, sizeof(entry.interleavegap)); + total += Write(fd, entry.sequence, sizeof(entry.sequence)); + total += Write(fd, &entry.namelen, sizeof(entry.namelen)); + total += Write(fd, entry.name, namelen); + + if(total != entry.recordsize || (total % 2) != 0) { + printf("%2d, %2d! ", total, entry.recordsize); + printf("%3d = %3d - %2d + %2d\n", + entry.recordsize, sizeof(entry), sizeof(entry.name), namelen); + } + + return entry.recordsize; +} + +void +writedata(struct node *parent, struct node *root, + int fd, int *currentsector, int dirs, struct dir *rootentry, + int rootsize, int remove_after) +{ + static char buf[1024*1024]; + struct node *c; + ssize_t written = 0, rest; + + for(c = root->firstchild; c; c = c->nextchild) { + if(c->isdir && chdir(c->name) < 0) { + perror(c->name); + fprintf(stderr, "couldn't chdir to %s - aborting\n", + c->name); + exit(1); + } + writedata(root, c, fd, currentsector, dirs, rootentry, rootsize, remove_after); + if(c->isdir && chdir(PARENTDIR) < 0) { + perror("chdir to .."); + fprintf(stderr, "couldn't chdir to parent - " + "aborting\n"); + exit(1); + } + } + + /* write nodes depth-first, down-top */ + + if(root->isdir && dirs) { + /* dir */ + written = 0; + root->startsector = *currentsector; + written += write_direntry(CURRENTDIR, root->startsector, + root->bytesize, root->isdir, fd); + if(parent) { + written += write_direntry(PARENTDIR, parent->startsector, + root->bytesize, root->isdir, fd); + } else { + written += write_direntry(PARENTDIR, root->startsector, + root->bytesize, root->isdir, fd); + } + for(c = root->firstchild; c; c = c->nextchild) { + off_t cur1, cur2; + ssize_t written_before; + cur1 = Lseek(fd, 0, SEEK_CUR); + written_before = written; + written += write_direntry(c->name, + c->startsector, c->bytesize, c->isdir, fd); + cur2 = Lseek(fd, 0, SEEK_CUR); + if(cur1/ISO_SECTOR != (cur2-1)/ISO_SECTOR) { + /* passed a sector boundary, argh! */ + Lseek(fd, cur1, SEEK_SET); + written = written_before; + rest=(ISO_SECTOR-(written % ISO_SECTOR)); + memset(buf, 0, rest); + Write(fd, buf, rest); + written += rest; + written += write_direntry(c->name, + c->startsector, c->bytesize, c->isdir, fd); + } + } + root->bytesize = written; + } else if(!root->isdir && !dirs) { + /* file */ + struct stat st; + ssize_t rem; + int filefd; + + if(stat(root->name, &st) < 0) { + perror(root->name); + fprintf(stderr, "couldn't stat %s - aborting\n", root->name); + exit(1); + } + + if((filefd = open(root->name, O_RDONLY)) < 0) { + perror(root->name); + fprintf(stderr, "couldn't open %s - aborting\n", root->name); + exit(1); + } + + rem = st.st_size; + + root->startsector = *currentsector; + + while(rem > 0) { + ssize_t chunk; + chunk = min(sizeof(buf), rem); + Read(filefd, buf, chunk); + Write(fd, buf, chunk); + rem -= chunk; + } + + close(filefd); + + root->bytesize = written = st.st_size; + if(remove_after && unlink(root->name) < 0) { + perror("unlink"); + fprintf(stderr, "couldn't remove %s\n", root->name); + } + } else { + /* nothing to be done */ + return; + } + + /* fill out sector with zero bytes */ + + if((rest=(ISO_SECTOR-(written % ISO_SECTOR)))) { + memset(buf, 0, rest); + Write(fd, buf, rest); + written += rest; + } + + /* update dir size with padded size */ + + if(root->isdir) { root->bytesize = written; } + + *currentsector += written/ISO_SECTOR; +} + +void +writebootcatalog(int fd, int *currentsector, int imagesector, int imagesectors) +{ + static char buf[ISO_SECTOR]; + struct bc_validation validate; + struct bc_initial initial; + + ssize_t written, rest; + u_int16_t *v, sum = 0; + int i; + + /* write validation entry */ + + memset(&validate, 0, sizeof(validate)); + validate.headerid = 1; + validate.platform = PLATFORM_80X86; + strcpy(validate.idstring, ""); + validate.keys[0] = 0x55; + validate.keys[1] = 0xaa; + + v = (u_int16_t *) &validate; + for(i = 0; i < sizeof(validate)/2; i++) + sum += v[i]; + validate.checksum = 65535 - sum + 1; /* sum must be 0 */ + + written = Write(fd, &validate, sizeof(validate)); + + /* write initial/default entry */ + + memset(&initial, 0, sizeof(initial)); + + initial.indicator = INDICATE_BOOTABLE; + initial.media = BOOTMEDIA_144M; + /* initial.sectors = imagesectors; */ + initial.sectors = 1; + initial.startsector = imagesector; + + written += Write(fd, &initial, sizeof(initial)); + + /* fill out the rest of the sector with 0's */ + + if((rest = ISO_SECTOR - (written % 2048))) { + memset(buf, 0, sizeof(buf)); + written += Write(fd, buf, rest); + } + + (*currentsector) += written / ISO_SECTOR; + + return; +} + +int +writebootimage(char *bootimage, int bootfd, int fd, int *currentsector) +{ + static char buf[1024*64]; + ssize_t chunk, written = 0, rest; + int virtuals; + + while((chunk=read(bootfd, buf, sizeof(buf))) > 0) + written += Write(fd, buf, chunk); + + if(chunk < 0) { + perror("read boot image"); + exit(1); + } + + virtuals = written / VIRTUAL_SECTOR; + + if((rest = ISO_SECTOR - (written % 2048))) { + memset(buf, 0, sizeof(buf)); + written += Write(fd, buf, rest); + } + + (*currentsector) += written/ISO_SECTOR; + + return virtuals; +} + +void +writebootrecord(int fd, int *currentsector, int bootcatalogsector) +{ + int i; + static struct bootrecord bootrecord; + ssize_t w = 0; + /* boot record volume descriptor */ + + memset(&bootrecord, 0, sizeof(bootrecord)); + bootrecord.set[0] = 'C'; + bootrecord.set[1] = 'D'; + bootrecord.set[2] = '0'; + bootrecord.set[3] = '0'; + bootrecord.set[4] = '1'; + bootrecord.version = 1; + bootrecord.bootcatalog = bootcatalogsector; + strcpy(bootrecord.ident, "EL TORITO SPECIFICATION"); + for(i = strlen(bootrecord.ident); + i < sizeof(bootrecord.ident); i++) + bootrecord.ident[i] = '\0'; + + w = Writefield(fd, bootrecord.indicator); + w += Writefield(fd, bootrecord.set); + w += Writefield(fd, bootrecord.version); + w += Writefield(fd, bootrecord.ident); + w += Writefield(fd, bootrecord.zero); + w += Writefield(fd, bootrecord.bootcatalog); + w += Writefield(fd, bootrecord.zero2); + + if(w != ISO_SECTOR) { + fprintf(stderr, "WARNING: something went wrong - boot record (%d) isn't a sector size (%d)\n", + w, ISO_SECTOR); + } + + (*currentsector)++; +} + +int +main(int argc, char *argv[]) +{ + int currentsector = 0; + int imagesector, imagesectors; + int bootfd, fd, i, ch, nsectors; + int remove_after = 0; + static char block[ISO_SECTOR]; + static struct pvd pvd; + char *label = "ISO9660"; + struct tm *now; + time_t nowtime; + char timestr[20], *prog; + char *bootimage = NULL; + struct node root; + int pvdsector; + int bigpath, littlepath, pathbytes = 0, dirsector, filesector, enddir; + int bootvolumesector, bootcatalogsector; + + prog = argv[0]; + + /* This check is to prevent compiler padding screwing up + * our format. + */ + + if(sizeof(struct pvd) != ISO_SECTOR) { + fprintf(stderr, "Something confusing happened at\n" + "compile-time; pvd should be a sector size. %d != %d\n", + sizeof(struct pvd), ISO_SECTOR); + return 1; + } + + while ((ch = getopt(argc, argv, "Rb:l:")) != -1) { + switch(ch) { + case 'l': + label = optarg; + break; + case 'r': + remove_after = 1; + break; + case 'b': + bootimage = optarg; + if((bootfd = open(bootimage, O_RDONLY)) < 0) { + perror(bootimage); + return 1; + } + break; + } + } + + argc -= optind; + argv += optind; + + if(argc != 2) { + fprintf(stderr, "usage: %s [-l <label>] [-b <bootfloppyimage>] <dir> <isofile>\n", + prog); + return 1; + } + + /* create .iso file */ + + if((fd=open(argv[1], O_WRONLY | O_TRUNC | O_CREAT, 0600)) < 0) { + perror(argv[1]); + return 1; + } + + /* go to where the iso has to be made from */ + + if(chdir(argv[0]) < 0) { + perror(argv[0]); + return 1; + } + + /* collect dirs and files */ + + fprintf(stderr, " * traversing input tree\n"); + + maketree(&root, "", 1); + + fprintf(stderr, " * writing initial zeroes and pvd\n"); + + /* first sixteen sectors are zero */ + + memset(block, 0, sizeof(block)); + + for(i = 0; i < 16; i++) + writesector(fd, block, ¤tsector); + + /* Primary Volume Descriptor */ + memset(&pvd, 0, sizeof(pvd)); + pvd.one = 1; + pvd.set[0] = 67; + pvd.set[1] = 68; + pvd.set[2] = 48; + pvd.set[3] = 48; + pvd.set[4] = 49; + pvd.set[5] = 1; + pvd.set[5] = 1; + + strncpy(pvd.volume, label, sizeof(pvd.volume)-1); + for(i = strlen(pvd.volume); i < sizeof(pvd.volume); i++) + pvd.volume[i] = ' '; + for(i = 0; i < sizeof(pvd.system); i++) + pvd.system[i] = ' '; + + both16((unsigned char *) pvd.setsize, 1); + both16((unsigned char *) pvd.seq, 1); + both16((unsigned char *) pvd.sectorsize, ISO_SECTOR); + + /* fill time fields */ + time(&nowtime); + now = gmtime(&nowtime); + strftime(timestr, sizeof(timestr), "%Y%m%d%H%M%S000", now); + memcpy(pvd.create, timestr, strlen(timestr)); + memcpy(pvd.modified, timestr, strlen(timestr)); + memcpy(pvd.effective, timestr, strlen(timestr)); + strcpy(pvd.expiry, "0000000000000000"); /* not specified */ + pvdsector = currentsector; + + writesector(fd, (char *) &pvd, ¤tsector); + + if(bootimage) { + fprintf(stderr, " * writing boot record volume descriptor\n"); + bootvolumesector = currentsector; + writebootrecord(fd, ¤tsector, 0); + } + + /* volume descriptor set terminator */ + memset(block, 0, sizeof(block)); + block[0] = 255; + block[1] = 67; + block[2] = 68; + block[3] = 48; + block[4] = 48; + block[5] = 49; + block[6] = 1; + + writesector(fd, block, ¤tsector); + + if(bootimage) { + /* write the boot catalog */ + fprintf(stderr, " * writing the boot catalog\n"); + bootcatalogsector = currentsector; + writebootcatalog(fd, ¤tsector, imagesector, imagesectors); + + /* write boot image */ + fprintf(stderr, " * writing the boot image\n"); + imagesector = currentsector; + imagesectors = writebootimage(bootimage, bootfd, + fd, ¤tsector); + fprintf(stderr, " * image: %d virtual sectors @ sector 0x%x\n", + imagesectors, imagesector); + + close(bootfd); + } + + /* write out all the file data */ + + filesector = currentsector; + fprintf(stderr, " * writing file data\n"); + writedata(NULL, &root, fd, ¤tsector, 0, + (struct dir *) &pvd.rootrecord, sizeof(pvd.rootrecord), + remove_after); + + /* write out all the dir data */ + + dirsector = currentsector; + fprintf(stderr, " * writing dir data\n"); + writedata(NULL, &root, fd, ¤tsector, 1, + (struct dir *) &pvd.rootrecord, sizeof(pvd.rootrecord), + remove_after); + enddir = currentsector; + seeksector(fd, dirsector, ¤tsector); + fprintf(stderr, " * rewriting dir data\n"); + fflush(NULL); + writedata(NULL, &root, fd, ¤tsector, 1, + (struct dir *) &pvd.rootrecord, sizeof(pvd.rootrecord), + remove_after); + if(currentsector != enddir) { + fprintf(stderr, "warning: inconsistent directories - " + "I have a bug! iso may be broken.\n"); + } + + /* now write the path table in both formats */ + + fprintf(stderr, " * writing big-endian path table\n"); + bigpath = currentsector; + currentsector += makepathtables(&root, 0, &pathbytes, fd); + + fprintf(stderr, " * writing little-endian path table\n"); + littlepath = currentsector; + currentsector += makepathtables(&root, 1, &pathbytes, fd); + + /* this is the size of the iso filesystem for use in the pvd later */ + + nsectors = currentsector; + both32((unsigned char *) pvd.sectors, nsectors); + + /* *********** Filesystem writing done ************************* */ + + /* finish and rewrite the pvd. */ + fprintf(stderr, " * rewriting pvd\n"); + seekwritesector(fd, pvdsector, (char *) &pvd, ¤tsector); + + both32((unsigned char *) pvd.pathtable, pathbytes); + little32((unsigned char *) &pvd.first_little_pathtable_start, littlepath); + little32((unsigned char *) &pvd.first_big_pathtable_start, bigpath); + + /* write root dir entry in pvd */ + seeksector(fd, pvdsector, ¤tsector); + Lseek(fd, (int)((char *) &pvd.rootrecord - (char *) &pvd), SEEK_CUR); + if(write_direntry(CURRENTDIR, root.startsector, root.bytesize, + root.isdir, fd) > sizeof(pvd.rootrecord)) { + fprintf(stderr, "warning: unexpectedly large root record\n"); + } + + if(bootimage) { + fprintf(stderr, " * rewriting boot catalog\n"); + seeksector(fd, bootcatalogsector, ¤tsector); + writebootcatalog(fd, ¤tsector, imagesector, imagesectors); + + /* finish and rewrite the boot record volume descriptor */ + fprintf(stderr, " * rewriting the boot rvd\n"); + seeksector(fd, bootvolumesector, ¤tsector); + writebootrecord(fd, ¤tsector, bootcatalogsector); + } + + fprintf(stderr, " * all ok\n"); + + return 0; +} + + diff --git a/commands/simple/xargs.c b/commands/simple/xargs.c new file mode 100755 index 000000000..4931df9a3 --- /dev/null +++ b/commands/simple/xargs.c @@ -0,0 +1,425 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * John B. Roll Jr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +char copyright[] = +"@(#) Copyright (c) 1990 The Regents of the University of California.\n\ + All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)xargs.c 5.11 (Berkeley) 6/19/91"; +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> +#include <fcntl.h> +#include <stdarg.h> +#if __minix +#define _PATH_ECHO "/bin/echo" +typedef unsigned u_int; +#else +#include "pathnames.h" +#endif + +#ifndef ARG_MAX +#define ARG_MAX (sizeof(int) == 2 ? 4096 : 128 * 1024) +#endif + +int exit_status = 0; +int tflag; +void err(const char *, ...); +void run(char **argv); +void usage(void); + +int main(int argc, char **argv) +{ + extern int optind; + extern char *optarg; + register int ch; + register char *p, *bbp, *ebp, **bxp, **exp, **xp; + int cnt, indouble, insingle, nargs, nflag, nline, xflag, zflag; + char **av, *argp; + + /* + * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that + * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given + * that the smallest argument is 2 bytes in length, this means that + * the number of arguments is limited to: + * + * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2. + * + * We arbitrarily limit the number of arguments to 5000. This is + * allowed by POSIX.2 as long as the resulting minimum exec line is + * at least LINE_MAX. Realloc'ing as necessary is possible, but + * probably not worthwhile. + */ +#if !__minix || __minix_vmd + nargs = 5000; + nline = ARG_MAX - 4 * 1024; +#else + /* Things are more cramped under standard Minix. */ + nargs = 100 * sizeof(int); + nline = ARG_MAX - 512 * sizeof(int); +#endif + nflag = xflag = zflag = 0; + while ((ch = getopt(argc, argv, "n:s:tx0")) != EOF) + switch(ch) { + case 'n': + nflag = 1; + if ((nargs = atoi(optarg)) <= 0) + err("illegal argument count"); + break; + case 's': + nline = atoi(optarg); + break; + case 't': + tflag = 1; + break; + case 'x': + xflag = 1; + break; + case '0': + zflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (xflag && !nflag) + usage(); + + /* + * Allocate pointers for the utility name, the utility arguments, + * the maximum arguments to be read from stdin and the trailing + * NULL. + */ + if (!(av = bxp = + malloc((u_int)(1 + argc + nargs + 1) * sizeof(char **)))) + err("%s", strerror(errno)); + + /* + * Use the user's name for the utility as argv[0], just like the + * shell. Echo is the default. Set up pointers for the user's + * arguments. + */ + if (!*argv) + cnt = strlen(*bxp++ = _PATH_ECHO); + else { + cnt = 0; + do { + cnt += strlen(*bxp++ = *argv) + 1; + } while (*++argv); + } + + /* + * Set up begin/end/traversing pointers into the array. The -n + * count doesn't include the trailing NULL pointer, so the malloc + * added in an extra slot. + */ + exp = (xp = bxp) + nargs; + + /* + * Allocate buffer space for the arguments read from stdin and the + * trailing NULL. Buffer space is defined as the default or specified + * space, minus the length of the utility name and arguments. Set up + * begin/end/traversing pointers into the array. The -s count does + * include the trailing NULL, so the malloc didn't add in an extra + * slot. + */ + nline -= cnt; + if (nline <= 0) + err("insufficient space for command"); + + if (!(bbp = malloc((u_int)nline + 1))) + err("%s", strerror(errno)); + ebp = (argp = p = bbp) + nline - 1; + + if (zflag) { + /* Read pathnames terminated by null bytes as produced by + * find ... -print0. No comments in this code, see further + * below. + */ + for (;;) + switch(ch = getchar()) { + case EOF: + if (p == bbp) + exit(exit_status); + + if (argp == p) { + *xp = NULL; + run(av); + exit(exit_status); + } + /*FALL THROUGH*/ + case '\0': + if (argp == p) + continue; + + *p = '\0'; + *xp++ = argp; + + if (xp == exp || p == ebp || ch == EOF) { + if (xflag && xp != exp && p == ebp) + err( + "insufficient space for arguments"); + *xp = NULL; + run(av); + if (ch == EOF) + exit(exit_status); + p = bbp; + xp = bxp; + } else + ++p; + argp = p; + break; + default: + if (p < ebp) { + *p++ = ch; + break; + } + + if (bxp == xp) + err("insufficient space for argument"); + if (xflag) + err("insufficient space for arguments"); + + *xp = NULL; + run(av); + xp = bxp; + cnt = ebp - argp; + bcopy(argp, bbp, cnt); + p = (argp = bbp) + cnt; + *p++ = ch; + break; + } + /* NOTREACHED */ + } + + for (insingle = indouble = 0;;) + switch(ch = getchar()) { + case EOF: + /* No arguments since last exec. */ + if (p == bbp) + exit(exit_status); + + /* Nothing since end of last argument. */ + if (argp == p) { + *xp = NULL; + run(av); + exit(exit_status); + } + goto arg1; + case ' ': + case '\t': + /* Quotes escape tabs and spaces. */ + if (insingle || indouble) + goto addch; + goto arg2; + case '\n': + /* Empty lines are skipped. */ + if (argp == p) + continue; + + /* Quotes do not escape newlines. */ +arg1: if (insingle || indouble) + err("unterminated quote"); + +arg2: *p = '\0'; + *xp++ = argp; + + /* + * If max'd out on args or buffer, or reached EOF, + * run the command. If xflag and max'd out on buffer + * but not on args, object. + */ + if (xp == exp || p == ebp || ch == EOF) { + if (xflag && xp != exp && p == ebp) + err("insufficient space for arguments"); + *xp = NULL; + run(av); + if (ch == EOF) + exit(exit_status); + p = bbp; + xp = bxp; + } else + ++p; + argp = p; + break; + case '\'': + if (indouble) + goto addch; + insingle = !insingle; + break; + case '"': + if (insingle) + goto addch; + indouble = !indouble; + break; + case '\\': + /* Backslash escapes anything, is escaped by quotes. */ + if (!insingle && !indouble && (ch = getchar()) == EOF) + err("backslash at EOF"); + /* FALLTHROUGH */ + default: +addch: if (p < ebp) { + *p++ = ch; + break; + } + + /* If only one argument, not enough buffer space. */ + if (bxp == xp) + err("insufficient space for argument"); + /* Didn't hit argument limit, so if xflag object. */ + if (xflag) + err("insufficient space for arguments"); + + *xp = NULL; + run(av); + xp = bxp; + cnt = ebp - argp; + bcopy(argp, bbp, cnt); + p = (argp = bbp) + cnt; + *p++ = ch; + break; + } + /* NOTREACHED */ +} + +void run(char **argv) +{ + register char **p; + pid_t pid; + int noinvoke; + int status; + int pfd[2]; + + if (tflag) { + (void)fprintf(stderr, "%s", *argv); + for (p = argv + 1; *p; ++p) + (void)fprintf(stderr, " %s", *p); + (void)fprintf(stderr, "\n"); + (void)fflush(stderr); + } + if (pipe(pfd) < 0) err("pipe: %s", strerror(errno)); + + switch(pid = fork()) { + case -1: + err("fork: %s", strerror(errno)); + case 0: + close(pfd[0]); + fcntl(pfd[1], F_SETFD, fcntl(pfd[1], F_GETFD) | FD_CLOEXEC); + + execvp(argv[0], argv); + noinvoke = (errno == ENOENT) ? 127 : 126; + (void)fprintf(stderr, + "xargs: %s: %s.\n", argv[0], strerror(errno)); + + /* Modern way of returning noinvoke instead of a dirty vfork() + * trick: (kjb) + */ + write(pfd[1], &noinvoke, sizeof(noinvoke)); + _exit(-1); + } + close(pfd[1]); + if (read(pfd[0], &noinvoke, sizeof(noinvoke)) < sizeof(noinvoke)) + noinvoke = 0; + close(pfd[0]); + + pid = waitpid(pid, &status, 0); + if (pid == -1) + err("waitpid: %s", strerror(errno)); + + /* + * If we couldn't invoke the utility or the utility didn't exit + * properly, quit with 127 or 126 respectively. + */ + if (noinvoke) + exit(noinvoke); + + /* + * According to POSIX, we have to exit if the utility exits with + * a 255 status, or is interrupted by a signal. xargs is allowed + * to return any exit status between 1 and 125 in these cases, but + * we'll use 124 and 125, the same values used by GNU xargs. + */ + if (WIFEXITED(status)) { + if (WEXITSTATUS (status) == 255) { + fprintf (stderr, "xargs: %s exited with status 255\n", + argv[0]); + exit(124); + } else if (WEXITSTATUS (status) != 0) { + exit_status = 123; + } + } else if (WIFSTOPPED (status)) { + fprintf (stderr, "xargs: %s terminated by signal %d\n", + argv[0], WSTOPSIG (status)); + exit(125); + } else if (WIFSIGNALED (status)) { + fprintf (stderr, "xargs: %s terminated by signal %d\n", + argv[0], WTERMSIG (status)); + exit(125); + } +} + +void usage(void) +{ + (void)fprintf(stderr, +"usage: xargs [-t0] [[-x] -n number] [-s size] [utility [argument ...]]\n"); + exit(1); +} + +void err(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + (void)fprintf(stderr, "xargs: "); + (void)vfprintf(stderr, fmt, ap); + va_end(ap); + (void)fprintf(stderr, "\n"); + exit(1); + /* NOTREACHED */ +} diff --git a/commands/simple/yes.c b/commands/simple/yes.c new file mode 100755 index 000000000..b05ad14af --- /dev/null +++ b/commands/simple/yes.c @@ -0,0 +1,23 @@ +/* yes 1.4 - print 'y' or argv[1] continuously. Author: Kees J. Bot + * 15 Apr 1989 + */ +#include <sys/types.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int main(int argc, char **argv) +{ + char *yes; + static char y[] = "y"; + int n; + + yes= argc == 1 ? y : argv[1]; + + n= strlen(yes); + + yes[n++]= '\n'; + + while (write(1, yes, n) != -1) {} + exit(1); +} diff --git a/commands/talk/Makefile b/commands/talk/Makefile new file mode 100755 index 000000000..269794dc8 --- /dev/null +++ b/commands/talk/Makefile @@ -0,0 +1,30 @@ +# Makefile for talk +# +# 08/01/96 Michael Temari, <temari@ix.netcom.com> +# + +CFLAGS= -O -D_MINIX -D_POSIX_SOURCE +LDFLAGS=-i +BINDIR= /usr/bin +PROG= talk + +OBJS= talk.o screen.o net.o proto.o + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) -lcurses + install -S 16kw $@ + +clean: + rm -f $(PROG) $(OBJS) + +install: $(BINDIR)/$(PROG) + +$(BINDIR)/$(PROG): $(PROG) + install -cs -o bin $? $@ + +talk.o: talk.c talk.h proto.h net.h screen.h +screen.o: screen.c screen.h +net.o: net.c talk.h net.h +proto.o: proto.c talk.h proto.h net.h screen.h diff --git a/commands/talk/net.c b/commands/talk/net.c new file mode 100755 index 000000000..6dd6beb24 --- /dev/null +++ b/commands/talk/net.c @@ -0,0 +1,266 @@ +/* net.c Copyright Michael Temari 08/01/1996 All Rights Reserved */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <signal.h> +#include <net/netlib.h> +#include <net/hton.h> +#include <net/gen/netdb.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> +#include <net/gen/udp.h> +#include <net/gen/udp_io.h> +#include <net/gen/udp_hdr.h> + +#include "talk.h" +#include "net.h" + +_PROTOTYPE(void TimeOut, (int sig)); + +static unsigned char buffer[8192]; + +static int udp_ctl; +int tcp_fd; + +static udpport_t ntalk_port; + +char luser[USER_SIZE+1], ruser[USER_SIZE+1]; +char lhost[HOST_SIZE+1], rhost[HOST_SIZE+1]; +char ltty[TTY_SIZE+1], rtty[TTY_SIZE+1]; +udpport_t ctlport; +tcpport_t dataport; +ipaddr_t laddr, raddr; + +int NetInit() +{ +int s; +struct servent *servent; +char *udp_device; +char *tcp_device; +nwio_udpopt_t udpopt; +nwio_tcpconf_t tcpconf; + + if((udp_device = getenv("UDP_DEVICE")) == (char *)NULL) + udp_device = UDP_DEVICE; + + if((udp_ctl = open(udp_device, O_RDWR)) < 0) { + fprintf(stderr, "talk: Could not open %s: %s\n", + udp_device, strerror(errno)); + return(-1); + } + + if((servent = getservbyname("ntalk", "udp")) == (struct servent *)NULL) { + fprintf(stderr, "talk: Could not find ntalk udp service\n"); + close(udp_ctl); + return(-1); + } + + ntalk_port = (udpport_t)servent->s_port; + + udpopt.nwuo_flags = NWUO_NOFLAGS; + udpopt.nwuo_flags |= NWUO_COPY | NWUO_LP_SEL | NWUO_EN_LOC; + udpopt.nwuo_flags |= NWUO_DI_BROAD | NWUO_RP_SET | NWUO_RA_SET; + udpopt.nwuo_flags |= NWUO_RWDATONLY | NWUO_DI_IPOPT; + udpopt.nwuo_remaddr = raddr; + udpopt.nwuo_remport = ntalk_port; + + s = ioctl(udp_ctl, NWIOSUDPOPT, &udpopt); + if(s < 0) { + perror("talk: ioctl NWIOSUDPOPT"); + close(udp_ctl); + return(-1); + } + + s = ioctl(udp_ctl, NWIOGUDPOPT, &udpopt); + if(s < 0) { + perror("talk: ioctl NWIOGUDPOPT"); + close(udp_ctl); + return(-1); + } + laddr = udpopt.nwuo_locaddr; + ctlport = udpopt.nwuo_locport; + + if((tcp_device = getenv("TCP_DEVICE")) == (char *)NULL) + tcp_device = TCP_DEVICE; + + if((tcp_fd = open(tcp_device, O_RDWR)) < 0) { + fprintf(stderr, "talk: Could not open %s: %s\n", + tcp_device, strerror(errno)); + close(udp_ctl); + return(-1); + } + + tcpconf.nwtc_flags = NWTC_NOFLAGS; + tcpconf.nwtc_flags |= NWTC_LP_SEL | NWTC_SET_RA | NWTC_UNSET_RP; + tcpconf.nwtc_remaddr = raddr; + + s = ioctl(tcp_fd, NWIOSTCPCONF, &tcpconf); + if(s < 0) { + perror("talk: ioctl NWIOSTCPCONF"); + close(udp_ctl); + close(tcp_fd); + return(-1); + } + + s = ioctl(tcp_fd, NWIOGTCPCONF, &tcpconf); + if(s < 0) { + perror("talk: ioctl NWIOGTCPCONF"); + close(udp_ctl); + close(tcp_fd); + return(-1); + } + + dataport = tcpconf.nwtc_locport; + + return(0); +} + +int getreply(reply, timeout) +struct talk_reply *reply; +int timeout; +{ +int s; +int terrno; +udp_io_hdr_t *udp_io_hdr; + + signal(SIGALRM, TimeOut); + alarm(timeout); + s = read(udp_ctl, buffer, sizeof(buffer)); + terrno = errno; + alarm(0); + errno = terrno; + if(s < 0 && errno == EINTR) + return(1); + if(s < 0) { + perror("talk: Read error in getreply"); + return(-1); + } + + if(s == sizeof(struct talk_reply)) + memcpy((char *)reply, buffer, s); + + return(0); +} + +int sendrequest(request, here) +struct talk_request *request; +int here; +{ +int s; +nwio_udpopt_t udpopt; +udp_io_hdr_t *udp_io_hdr; + + udpopt.nwuo_flags = NWUO_NOFLAGS; + udpopt.nwuo_flags |= NWUO_COPY | NWUO_LP_SET | NWUO_EN_LOC; + udpopt.nwuo_flags |= NWUO_DI_BROAD | NWUO_RP_SET | NWUO_RA_SET; + udpopt.nwuo_flags |= NWUO_RWDATONLY | NWUO_DI_IPOPT; + udpopt.nwuo_locport = ctlport; + if(here) + udpopt.nwuo_remaddr = laddr; + else + udpopt.nwuo_remaddr = raddr; + udpopt.nwuo_remport = ntalk_port; + + s = ioctl(udp_ctl, NWIOSUDPOPT, &udpopt); + if(s < 0) { + perror("talk: ioctl NWIOSUDPOPT"); + return(-1); + } + + s = ioctl(udp_ctl, NWIOGUDPOPT, &udpopt); + if(s < 0) { + perror("talk: ioctl NWIOGUDPOPT"); + return(-1); + } + + s = write(udp_ctl, request, sizeof(struct talk_request)); + if(s < 0) { + perror("talk: write error in sendrequest"); + return(-1); + } + + if(s != sizeof(struct talk_request)) { + fprintf(stderr, "talk: sendrequest size mismatch %d %d\n", s, sizeof(struct talk_request)); + return(-1); + } + + return(0); +} + +void TimeOut(sig) +int sig; +{ +} + +int NetConnect(port) +u16_t port; +{ +int s; +nwio_tcpconf_t tcpconf; +nwio_tcpcl_t tcpcopt; + + tcpconf.nwtc_flags = NWTC_NOFLAGS; + tcpconf.nwtc_flags |= NWTC_LP_SET | NWTC_SET_RA | NWTC_SET_RP; + tcpconf.nwtc_locport = dataport; + tcpconf.nwtc_remaddr = raddr; + tcpconf.nwtc_remport = port; + + s = ioctl(tcp_fd, NWIOSTCPCONF, &tcpconf); + if(s < 0) { + perror("talk: ioctl NWIOSTCPCONF"); + return(-1); + } + + s = ioctl(tcp_fd, NWIOGTCPCONF, &tcpconf); + if(s < 0) { + perror("talk: ioctl NWIOGTCPCONF"); + return(-1); + } + + tcpcopt.nwtcl_flags = 0; + + s = ioctl(tcp_fd, NWIOTCPCONN, &tcpcopt); + if(s < 0 && errno == ECONNREFUSED) + return(1); + if(s < 0) { + perror("talk: ioctl NWIOTCPCONN"); + return(-1); + } + + return(0); +} + +int NetListen(timeout) +int timeout; +{ +int s; +nwio_tcpcl_t tcplopt; +int terrno; + + tcplopt.nwtcl_flags = 0; + + signal(SIGALRM, TimeOut); + alarm(timeout); + s = ioctl(tcp_fd, NWIOTCPLISTEN, &tcplopt); + terrno = errno; + alarm(0); + errno = terrno; + + if(s < 0 && errno == EINTR) + return(1); + + if(s < 0) { + perror("talk: ioctl NWIOTCPLISTEN"); + return(-1); + } + + return(0); +} diff --git a/commands/talk/net.h b/commands/talk/net.h new file mode 100755 index 000000000..56358763f --- /dev/null +++ b/commands/talk/net.h @@ -0,0 +1,15 @@ +/* net.h Copyright Michael Temari 08/01/1996 All Rights Reserved */ + +extern char luser[], ruser[]; +extern char lhost[], rhost[]; +extern char ltty[], rtty[]; +extern udpport_t ctlport; +extern tcpport_t dataport; +extern ipaddr_t laddr, raddr; +extern int tcp_fd; + +_PROTOTYPE(int NetInit, (void)); +_PROTOTYPE(int getreply, (struct talk_reply *reply, int timeout)); +_PROTOTYPE(int sendrequest, (struct talk_request *request, int here)); +_PROTOTYPE(int NetConnect, (U16_t port)); +_PROTOTYPE(int NetListen, (int timeout)); diff --git a/commands/talk/proto.c b/commands/talk/proto.c new file mode 100755 index 000000000..0e79a1647 --- /dev/null +++ b/commands/talk/proto.c @@ -0,0 +1,142 @@ +/* proto.c Copyright Michael Temari 08/01/1996 All Rights Reserved */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <net/hton.h> +#include <net/gen/socket.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/tcp.h> +#include <net/gen/udp.h> + +#include "talk.h" +#include "proto.h" +#include "net.h" +#include "screen.h" + +_PROTOTYPE(static int TalkChk, (int gotreply, struct talk_reply *reply, char *msg)); +_PROTOTYPE(static int TalkTrans, (int type, long id, struct talk_reply *reply, int here)); + +static char *AnswerMsgs[] = { + "Success", + "User Not Logged In", + "Failure", + "Remote Does Not Know who we are", + "User is not accepting calls", + "Are request was not know", + "Incorrect Version", + "Bad Address", + "Bad Control Address" +}; + +static int TalkChk(gotreply, reply, msg) +int gotreply; +struct talk_reply *reply; +char *msg; +{ + if(!gotreply) { + ScreenMsg(msg); + return(-1); + } + if(reply->answer == SUCCESS) return(0); + if(reply->answer < (sizeof(AnswerMsgs) / sizeof(AnswerMsgs[0]))) + ScreenMsg(AnswerMsgs[reply->answer]); + else + ScreenMsg("Bad Answer"); + + return(-1); +} + +static int TalkTrans(type, id, reply, here) +int type; +long id; +struct talk_reply *reply; +int here; +{ +struct talk_request request; +int tries; +int gotreply; + + memset(&request, 0, sizeof(request)); + + request.version = TALK_VERSION; + request.type = type; + request.id = id; + request.addr.sa_family = htons(AF_INET); + request.addr.sin_port = dataport; + request.addr.sin_addr = laddr; + request.ctl_addr.sa_family = htons(AF_INET); + request.ctl_addr.sin_port = ctlport; + request.ctl_addr.sin_addr = laddr; + request.pid = getpid(); + strncpy(request.luser, luser, USER_SIZE); + strncpy(request.ruser, ruser, USER_SIZE); + strncpy(request.rtty, rtty, TTY_SIZE); + + tries = 0; + gotreply = 0; + while(!ScreenDone && tries++ < 3 && !gotreply) { + if(!sendrequest(&request, here)) + if(!getreply(reply, 5)) + gotreply = 1; + if(!gotreply) continue; + if(reply->version != request.version || + reply->type != request.type) + gotreply = 0; + } + return(gotreply); +} + +int TalkInit() +{ +struct talk_reply reply; +long id = 0; +long rid; +int s; +int ring; +char buff[32]; + + /* Check if someone was calling us */ + ScreenMsg("Initiating Talk Protocol"); + + /* Check is someone was calling us */ + s = TalkTrans(LOOK_UP, ++id, &reply, 0); + + /* Someone was calling us */ + if(s && reply.answer == SUCCESS) { + s = NetConnect(reply.addr.sin_port); + if(s == 1) { + ScreenMsg("Your party has hung up"); + TalkTrans(DELETE, reply.id, &reply, 0); + } + return(s == 0 ? 0 : -1); + } + + ScreenMsg("Ringing User"); + + ring = 0; + while(!ScreenDone && ring++ < 5) { + if(TalkChk(TalkTrans(ANNOUNCE, -1, &reply, 0), + &reply, "No response to are ring")) + return(-1); + rid = reply.id; + sprintf(buff, "Ring #%d", ring); + ScreenMsg(buff); + if(ring == 1) { + if(TalkChk(TalkTrans(LEAVE_INVITE, ++id, &reply, 1), + &reply, "Could not leave are invitaion locally")) + return(-1); + } + s = NetListen(RING_WAIT); + if(s <= 0) { + TalkTrans(DELETE, reply.id, &reply, 1); + TalkTrans(DELETE, rid, &reply, 0); + return(s); + } + } + + return(-1); +} diff --git a/commands/talk/proto.h b/commands/talk/proto.h new file mode 100755 index 000000000..27442bc33 --- /dev/null +++ b/commands/talk/proto.h @@ -0,0 +1,3 @@ +/* proto.h Copyright Michael Temari 08/01/1996 All Rights Reserved */ + +_PROTOTYPE(int TalkInit, (void)); diff --git a/commands/talk/screen.c b/commands/talk/screen.c new file mode 100755 index 000000000..1b84ba471 --- /dev/null +++ b/commands/talk/screen.c @@ -0,0 +1,208 @@ +/* screen.c Copyright Michael Temari 08/01/1996 All Rights Reserved */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <signal.h> +#include <ctype.h> +#include <string.h> +#include <unistd.h> +#include <curses.h> + +#include "screen.h" + +_PROTOTYPE(void gotsig, (int sig)); +_PROTOTYPE(static char *delword, (WINDOW *w)); + +struct { + WINDOW *win; + char erase; + char kill; + char werase; +} window[2]; + +static char line[80+1]; + +int ScreenDone = 0; + +static WINDOW *dwin; + +void gotsig(sig) +int sig; +{ + ScreenDone = 1; + signal(sig, gotsig); +} + +int ScreenInit() +{ +int i; + + if(initscr() == (WINDOW *)NULL) { + fprintf(stderr, "talk: Could not initscr\n"); + return(-1); + } + signal(SIGINT, gotsig); + signal(SIGQUIT, gotsig); + signal(SIGPIPE, gotsig); + signal(SIGHUP, gotsig); + clear(); + refresh(); + noecho(); + cbreak(); + + /* local window */ + window[LOCALWIN].win = newwin(LINES / 2, COLS, 0, 0); + scrollok(window[LOCALWIN].win, TRUE); + wclear(window[LOCALWIN].win); + + /* divider between windows */ + dwin = newwin(1, COLS, LINES / 2, 0); + i = COLS; + while(i-- > 0) + waddch(dwin, '-'); + wrefresh(dwin); + + /* remote window */ + window[REMOTEWIN].win = newwin(LINES - (LINES / 2) - 1, COLS, LINES / 2 + 1, 0); + scrollok(window[REMOTEWIN].win, TRUE); + wclear(window[REMOTEWIN].win); + + return(0); +} + +void ScreenMsg(msg) +char *msg; +{ +WINDOW *w; + + w =window[LOCALWIN].win; + + wmove(w, 0, 0); + + if(*msg != '\0') { + wprintw(w, "[%s]", msg); + wclrtoeol(w); + } else + werase(w); + + wrefresh(w); +} + +void ScreenWho(user, host) +char *user; +char *host; +{ + if(*host != '\0') { + wmove(dwin, 0, (COLS - (1 + strlen(user) + 1 + strlen(host) + 1)) / 2); + wprintw(dwin, " %s@%s ", user, host); + } else { + wmove(dwin, 0, (COLS - (1 + strlen(user) + 1)) / 2); + wprintw(dwin, " %s ", user); + } + wrefresh(dwin); +} + +void ScreenEdit(lcc, rcc) +char lcc[]; +char rcc[]; +{ + window[LOCALWIN].erase = lcc[0]; + window[LOCALWIN].kill = lcc[1]; + window[LOCALWIN].werase = lcc[2]; + window[REMOTEWIN].erase = rcc[0]; + window[REMOTEWIN].kill = rcc[1]; + window[REMOTEWIN].werase = rcc[2]; +} + +void ScreenPut(data, len, win) +char *data; +int len; +int win; +{ +WINDOW *w; +unsigned char ch; +int r, c; + + w = window[win].win; + + while(len-- > 0) { + ch = *data++; + /* new line CR, NL */ + if(ch == '\r' || ch == '\n') { + waddch(w, '\n'); + } else + /* erase a character, BS, DEL */ + if(ch == 0x08 || ch == 0x7f || ch == window[win].erase) { + getyx(w, r, c); + if(c > 0) + c--; + wmove(w, r, c); + waddch(w, ' '); + wmove(w, r, c); + } else + /* erase line CTL-U */ + if(ch == 0x15 || ch == window[win].kill) { + getyx(w, r, c); + wmove(w, r, 0); + wclrtoeol(w); + } else + /* refresh CTL-L */ + if(ch == 0x0c) { + if(win == LOCALWIN) { + touchwin(w); + wrefresh(w); + touchwin(window[REMOTEWIN].win); + wrefresh(window[REMOTEWIN].win); + } + } else + /* bell CTL-G */ + if(ch == 0x07) { + putchar(ch); + } + else + /* erase last word CTL-W */ + if(ch == 0x17 || ch == window[win].werase) { + (void) delword(w); + } else { + getyx(w, r, c); + if(1 || isprint(ch)) { + if(ch != ' ' && c == (COLS - 1)) + wprintw(w, "\n%s", delword(w)); + waddch(w, ch); + } + } + } + wrefresh(w); +} + +static char *delword(w) +WINDOW *w; +{ +int r, c; +int i = 0; +char ch; +char *p = &line[80]; + + *p-- = '\0'; + getyx(w, r, c); + if(c == 0) return; + while(c >= 0) { + c--; + ch = mvwinch(w, r, c); + if(ch == ' ') break; + *p-- = ch; + i = 1; + waddch(w, ' '); + } + c += i; + wmove(w, r, c); + return(++p); +} + +void ScreenEnd() +{ + move(LINES - 1, 0); + refresh(); + endwin(); +} diff --git a/commands/talk/screen.h b/commands/talk/screen.h new file mode 100755 index 000000000..a01eb418f --- /dev/null +++ b/commands/talk/screen.h @@ -0,0 +1,13 @@ +/* screen.h Copyright Michael Temari 08/01/1996 All Rights Reserved */ + +_PROTOTYPE(int ScreenInit, (void)); +_PROTOTYPE(void ScreenMsg, (char *msg)); +_PROTOTYPE(void ScreenWho, (char *user, char *host)); +_PROTOTYPE(void ScreenEdit, (char lcc[], char rcc[])); +_PROTOTYPE(void ScreenPut, (char *data, int len, int mywin)); +_PROTOTYPE(void ScreenEnd, (void)); + +extern int ScreenDone; + +#define LOCALWIN 0 +#define REMOTEWIN 1 diff --git a/commands/talk/talk.c b/commands/talk/talk.c new file mode 100755 index 000000000..6450eedbf --- /dev/null +++ b/commands/talk/talk.c @@ -0,0 +1,237 @@ +/* talk.c Copyright Michael Temari 08/01/1996 All Rights Reserved */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <signal.h> +#include <utmp.h> +#include <termios.h> +#include <net/gen/netdb.h> +#include <net/hton.h> +#include <net/gen/socket.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/tcp.h> +#include <net/gen/udp.h> + +#include "talk.h" +#include "proto.h" +#include "net.h" +#include "screen.h" + +_PROTOTYPE(int main, (int argc, char *argv[])); +_PROTOTYPE(void DoTalk, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ +char *p; +struct hostent *hp; +struct stat st; +struct utmp utmp; +int slot; +FILE *fp; + + if(argc < 2 || argc > 3) { + fprintf(stderr, "Usage: talk user[@host] [tty]\n"); + return(-1); + } + + /* get local host name */ + if(gethostname(lhost, HOST_SIZE) < 0) { + fprintf(stderr, "talk: Error getting local host name\n"); + return(-1); + } + + /* get local user name and tty */ + if((slot = ttyslot()) < 0) { + fprintf(stderr, "talk: You are not on a terminal\n"); + return(-1); + } + if((fp = fopen(UTMP, "r")) == (FILE *)NULL) { + fprintf(stderr, "talk: Could not open %s\n", UTMP); + return(-1); + } + if(fseek(fp, (off_t) sizeof(utmp) * slot, SEEK_SET)) { + fprintf(stderr, "talk: Could not seek %s\n", UTMP); + fclose(fp); + return(-1); + } + if(fread((char *)&utmp, sizeof(utmp), 1 , fp) != 1) { + fprintf(stderr, "talk: Could not read %s\n", UTMP); + fclose(fp); + return(-1); + } + fclose(fp); + strncpy(luser, utmp.ut_user, USER_SIZE < sizeof(utmp.ut_user) ? + USER_SIZE : sizeof(utmp.ut_user)); + luser[USER_SIZE] = '\0'; + + /* get local tty */ + if((p = ttyname(0)) == (char *)NULL) { + fprintf(stderr, "talk: You are not on a terminal\n"); + return(-1); + } + strncpy(ltty, p+5, TTY_SIZE); + ltty[TTY_SIZE] = '\0'; + + /* check if local tty is going to be writable */ + if(stat(p, &st) < 0) { + perror("talk: Could not stat local tty"); + return(-1); + } + if((st.st_mode & S_IWGRP) == 0) { + fprintf(stderr, "talk: Your terminal is not writable. Use: mesg y\n"); + return(-1); + } + + /* get remote user and host name */ + if((p = strchr(argv[1], '@')) != (char *)NULL) + *p++ = '\0'; + else + p = lhost; + strncpy(ruser, argv[1], USER_SIZE); + ruser[USER_SIZE] = '\0'; + strncpy(rhost, p, HOST_SIZE); + rhost[HOST_SIZE] = '\0'; + + /* get remote tty */ + if(argc > 2) + strncpy(rtty, argv[2], TTY_SIZE); + else + rtty[0] = '\0'; + rtty[TTY_SIZE] = '\0'; + + if((hp = gethostbyname(rhost)) == (struct hostent *)NULL) { + fprintf(stderr, "talk: Could not determine address of %s\n", rhost); + return(-1); + } + memcpy((char *)&raddr, (char *)hp->h_addr, hp->h_length); + + if(NetInit()) { + fprintf(stderr, "talk: Error in NetInit\n"); + return(-1); + } + + if(ScreenInit()) + return(-1); + + if(!TalkInit()) + DoTalk(); + + ScreenEnd(); + + return(0); +} + +struct pdata { + int win; + int len; + char buffer[64]; +} pdata; + +void DoTalk() +{ +int s; +int s2; +int kid; +int pfd[2]; +int win; +int len; +struct termios termios; +char lcc[3]; +char rcc[3]; + + ScreenMsg(""); + ScreenWho(ruser, rhost); + + /* Get and send edit characters */ + s = tcgetattr(0, &termios); + if(s < 0) { + perror("talk: tcgetattr"); + return; + } + lcc[0] = termios.c_cc[VERASE]; + lcc[1] = termios.c_cc[VKILL]; + lcc[2] = 0x17; /* Control - W */ + s = write(tcp_fd, lcc, sizeof(lcc)); + if(s != sizeof(lcc)) { + ScreenMsg("Connection Closing due to error"); + return; + } + s = read(tcp_fd, rcc, sizeof(rcc)); + if(s != sizeof(rcc)) { + ScreenMsg("Connection Closing due to error"); + return; + } + ScreenEdit(lcc, rcc); + + s = pipe(pfd); + if(s < 0) { + ScreenMsg("Could not create pipes"); + return; + } + + if((kid = fork()) < 0) { + ScreenMsg("Could not fork"); + close(pfd[0]); + close(pfd[1]); + return; + } + + if(kid == 0) { + close(tcp_fd); + close(pfd[1]); + while(1) { + s = read(pfd[0], &pdata, sizeof(pdata)); + if(s != sizeof(pdata)) { + close(pfd[0]); + exit(-1); + } + ScreenPut(pdata.buffer, pdata.len, pdata.win); + } + } + + close(pfd[0]); + + if((kid = fork()) < 0) { + ScreenMsg("Could not fork"); + close(pfd[1]); + return; + } + + if(kid == 0) { + pdata.win = REMOTEWIN; + while(!ScreenDone) { + s = read(tcp_fd, pdata.buffer, sizeof(pdata.buffer)); + if(s <= 0) + break; + pdata.len = s; + write(pfd[1], &pdata, sizeof(pdata)); + } + close(pfd[1]); + close(tcp_fd); + kill(getppid(), SIGINT); + exit(-1); + } + + pdata.win = LOCALWIN; + while(!ScreenDone) { + s = read(0, pdata.buffer, sizeof(pdata.buffer)); + if(s <= 0) + break; + pdata.len = s; + write(pfd[1], &pdata, sizeof(pdata)); + s2 = write(tcp_fd, pdata.buffer, s); + if(s2 != s) + break; + } + kill(kid, SIGINT); + close(pfd[1]); + close(tcp_fd); + return; +} diff --git a/commands/talk/talk.h b/commands/talk/talk.h new file mode 100755 index 000000000..e7ecbab11 --- /dev/null +++ b/commands/talk/talk.h @@ -0,0 +1,57 @@ +/* talk.h Copyright Michael Temari 07/22/1996 All Rights Reserved */ + +#define USER_SIZE 12 +#define TTY_SIZE 16 +#define HOST_SIZE 255 + +struct osockaddr { + u16_t sa_family; + u16_t sin_port; + ipaddr_t sin_addr; + char junk[8]; +}; + +struct talk_request { + u8_t version; + u8_t type; + u8_t answer; + u8_t junk; + u32_t id; + struct osockaddr addr; + struct osockaddr ctl_addr; + long pid; + char luser[USER_SIZE]; + char ruser[USER_SIZE]; + char rtty[TTY_SIZE]; +}; + +struct talk_reply { + u8_t version; + u8_t type; + u8_t answer; + u8_t junk; + u32_t id; + struct osockaddr addr; +}; + +#define TALK_VERSION 1 + +/* message type values */ +#define LEAVE_INVITE 0 /* leave invitation with server */ +#define LOOK_UP 1 /* check for invitation by callee */ +#define DELETE 2 /* delete invitation by caller */ +#define ANNOUNCE 3 /* announce invitation by caller */ + +/* answer values */ +#define SUCCESS 0 /* operation completed properly */ +#define NOT_HERE 1 /* callee not logged in */ +#define FAILED 2 /* operation failed for unexplained reason */ +#define MACHINE_UNKNOWN 3 /* caller's machine name unknown */ +#define PERMISSION_DENIED 4 /* callee's tty doesn't permit announce */ +#define UNKNOWN_REQUEST 5 /* request has invalid type value */ +#define BADVERSION 6 /* request has invalid protocol version */ +#define BADADDR 7 /* request has invalid addr value */ +#define BADCTLADDR 8 /* request has invalid ctl_addr value */ + +#define MAX_LIFE 60 /* max time daemon saves invitations */ +#define RING_WAIT 30 /* time to wait before resending invitation */ diff --git a/commands/talkd/Makefile b/commands/talkd/Makefile new file mode 100755 index 000000000..6a8173069 --- /dev/null +++ b/commands/talkd/Makefile @@ -0,0 +1,30 @@ +# Makefile for talkd +# +# 07/22/96 Michael Temari, <temari@ix.netcom.com> +# + +CFLAGS= -O -D_MINIX -D_POSIX_SOURCE +LDFLAGS=-i +BINDIR= /usr/bin +PROG= talkd + +OBJS= talkd.o net.o process.o finduser.o + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) + install -S 8kw $@ + +clean: + rm -f $(PROG) $(OBJS) + +install: $(BINDIR)/$(PROG) + +$(BINDIR)/$(PROG): $(PROG) + install -cs -o bin $? $@ + +talkd.o: talkd.c talk.h talkd.h process.h net.h +net.o: net.c talk.h talkd.h net.h +process.o: process.c talk.h talkd.h finduser.h process.h +finduser.o: finduser.c talk.h finduser.h diff --git a/commands/talkd/finduser.c b/commands/talkd/finduser.c new file mode 100755 index 000000000..82049cd86 --- /dev/null +++ b/commands/talkd/finduser.c @@ -0,0 +1,44 @@ +/* finduser.c Copyright Michael Temari 07/22/1996 All Rights Reserved */ + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <time.h> +#include <utmp.h> +#include <net/gen/in.h> + +#include "talk.h" +#include "finduser.h" + +int find_user(name, tty) +char *name; +char *tty; +{ +int fd; +int ret; +struct utmp utmp; + + /* Now find out if the requested user is logged in. */ + if((fd = open(UTMP, O_RDONLY)) < 0) { + perror("talkd: opening UTMP file"); + return(FAILED); + } + + ret = NOT_HERE; + + while(read(fd, &utmp, sizeof(struct utmp)) == sizeof(struct utmp)) { + if(utmp.ut_type != USER_PROCESS) continue; + if(strncmp(utmp.ut_user, name, sizeof(utmp.ut_user))) continue; + if(*tty && strncmp(utmp.ut_line, tty, sizeof(utmp.ut_line))) continue; + strcpy(tty, utmp.ut_line); + ret = SUCCESS; + break; + } + + close(fd); + + return(ret); +} diff --git a/commands/talkd/finduser.h b/commands/talkd/finduser.h new file mode 100755 index 000000000..7fd68bd5f --- /dev/null +++ b/commands/talkd/finduser.h @@ -0,0 +1,3 @@ +/* finduser.h Copyright Michael Temari 07/22/1996 All Rights Reserved */ + +_PROTOTYPE(int find_user, (char *name, char *tty)); diff --git a/commands/talkd/net.c b/commands/talkd/net.c new file mode 100755 index 000000000..4eb136cce --- /dev/null +++ b/commands/talkd/net.c @@ -0,0 +1,188 @@ +/* net.c Copyright Michael Temari 07/22/1996 All Rights Reserved */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <stdio.h> +#include <stdlib.h> +#include <fcntl.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <net/netlib.h> +#include <net/hton.h> +#include <net/gen/netdb.h> +#include <net/gen/in.h> +#include <net/gen/udp.h> +#include <net/gen/udp_io.h> +#include <net/gen/udp_hdr.h> + +#include "talk.h" +#include "talkd.h" +#include "net.h" + +static unsigned char buffer[8192]; + +static int udp_in; +static int udp_out; + +static udpport_t ntalk_port; + +int NetInit() +{ +int s; +struct servent *servent; +char *udp_device; +nwio_udpopt_t udpopt; + + if((udp_device = getenv("UDP_DEVICE")) == (char *)NULL) + udp_device = UDP_DEVICE; + + if((udp_in = open(udp_device, O_RDWR)) < 0) { + fprintf(stderr, "talkd: Could not open %s: %s\n", + udp_device, strerror(errno)); + return(-1); + } + + if((udp_out = open(udp_device, O_RDWR)) < 0) { + fprintf(stderr, "talkd: Could not open %s: %s\n", + udp_device, strerror(errno)); + close(udp_in); + return(-1); + } + + if((servent = getservbyname("ntalk", "udp")) == (struct servent *)NULL) { + fprintf(stderr, "talkd: Could not find ntalk udp service\n"); + close(udp_in); + close(udp_out); + return(-1); + } + + ntalk_port = (udpport_t)servent->s_port; + + udpopt.nwuo_flags = NWUO_NOFLAGS; + udpopt.nwuo_flags |= NWUO_COPY | NWUO_LP_SET | NWUO_EN_LOC; + udpopt.nwuo_flags |= NWUO_DI_BROAD | NWUO_RP_ANY | NWUO_RA_ANY; + udpopt.nwuo_flags |= NWUO_RWDATALL | NWUO_DI_IPOPT; + udpopt.nwuo_locport = ntalk_port; + + s = ioctl(udp_in, NWIOSUDPOPT, &udpopt); + if(s < 0) { + perror("talkd: ioctl NWIOSUDPOPT"); + close(udp_in); + close(udp_out); + return(-1); + } + + s = ioctl(udp_in, NWIOGUDPOPT, &udpopt); + if(s < 0) { + perror("talkd: ioctl NWIOGUDPOPT"); + close(udp_in); + close(udp_out); + return(-1); + } + + return(0); +} + +int getrequest(request) +struct talk_request *request; +{ +int s; +udp_io_hdr_t *udp_io_hdr; + + s = read(udp_in, buffer, sizeof(buffer)); + if(s < 0) { + perror("talkd: Read error in getrequest"); + return(-1); + } + if(s < sizeof(udp_io_hdr_t)) { + fprintf(stderr, "talkd: Packet size read %d is smaller the udp_io_hdr\n", s); + return(-1); + } + udp_io_hdr = (udp_io_hdr_t *)buffer; + s = s - sizeof(udp_io_hdr_t); + + /* why is uih_data_len already in host order??? */ + + if(udp_io_hdr->uih_data_len != s) { + fprintf(stderr, "talkd: Size mismatch Packet %d Udp Data %d\n", + s, udp_io_hdr->uih_data_len); + return(-1); + } + + if(s != sizeof(struct talk_request)) { + fprintf(stderr, "talkd: Size mismatch in request %d %d\n", + s, sizeof(struct talk_request)); + return(-1); + } + + memcpy((char *)request, buffer + sizeof(udp_io_hdr_t), s); + + if(opt_d) { + fprintf(stderr, "Request: "); + fprintf(stderr, "%02x %02x %02x %02x ", + request->version, request->type, request->answer, request->junk); + fprintf(stderr, "%08lx ", request->id); + fprintf(stderr, "%04x %08lx:%04x\n", + request->addr.sa_family, request->addr.sin_addr, request->addr.sin_port); + fprintf(stderr, " %08lx ", request->pid); + fprintf(stderr, "%04x %08lx:%04x\n", + request->ctl_addr.sa_family, request->ctl_addr.sin_addr, request->ctl_addr.sin_port); + fprintf(stderr, " %-12.12s %-12.12s %-16.16s\n", + request->luser, request->ruser, request->rtty); + } + + return(0); +} + +int sendreply(request, reply) +struct talk_request *request; +struct talk_reply *reply; +{ +int s; +nwio_udpopt_t udpopt; +udp_io_hdr_t *udp_io_hdr; + + udpopt.nwuo_flags = NWUO_NOFLAGS; + udpopt.nwuo_flags |= NWUO_COPY | NWUO_LP_SET | NWUO_EN_LOC; + udpopt.nwuo_flags |= NWUO_DI_BROAD | NWUO_RP_SET | NWUO_RA_SET; + udpopt.nwuo_flags |= NWUO_RWDATONLY | NWUO_DI_IPOPT; + udpopt.nwuo_locport = ntalk_port; + udpopt.nwuo_remaddr = request->ctl_addr.sin_addr; + udpopt.nwuo_remport = request->ctl_addr.sin_port; + + s = ioctl(udp_out, NWIOSUDPOPT, &udpopt); + if(s < 0) { + perror("talkd: ioctl NWIOSUDPOPT"); + return(-1); + } + + s = ioctl(udp_out, NWIOGUDPOPT, &udpopt); + if(s < 0) { + perror("talkd: ioctl NWIOGUDPOPT"); + return(-1); + } + + if(opt_d) { + fprintf(stderr, "Reply: "); + fprintf(stderr, "%02x %02x %02x %02x ", + reply->version, reply->type, reply->answer, reply->junk); + fprintf(stderr, "%08lx ", reply->id); + fprintf(stderr, "%04x %08lx:%04x", + reply->addr.sa_family, reply->addr.sin_addr, reply->addr.sin_port); + fprintf(stderr, "\n"); + } + + s = write(udp_out, reply, sizeof(struct talk_reply)); + if(s < 0) { + perror("talkd: write"); + return(-1); + } + if(s != sizeof(struct talk_reply)) { + fprintf(stderr, "talkd: write size mismatch %d %d\n", + s, sizeof(struct talk_reply)); + return(-1); + } + + return(0); +} diff --git a/commands/talkd/net.h b/commands/talkd/net.h new file mode 100755 index 000000000..9254d3ca8 --- /dev/null +++ b/commands/talkd/net.h @@ -0,0 +1,5 @@ +/* net.h Copyright Michael Temari 07/22/1996 All Rights Reserved */ + +_PROTOTYPE(int NetInit, (void)); +_PROTOTYPE(int getrequest, (struct talk_request *request)); +_PROTOTYPE(int sendreply, (struct talk_request *request, struct talk_reply *reply)); diff --git a/commands/talkd/process.c b/commands/talkd/process.c new file mode 100755 index 000000000..02715c507 --- /dev/null +++ b/commands/talkd/process.c @@ -0,0 +1,269 @@ +/* process.c Copyright Michael Temari 07/22/1996 All Rights Reserved */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <ctype.h> +#include <time.h> +#include <net/hton.h> +#include <net/gen/socket.h> +#include <net/gen/in.h> +#include <net/gen/netdb.h> + +#include "talk.h" +#include "talkd.h" +#include "process.h" +#include "finduser.h" + +struct entry { + struct entry *prev; + struct talk_request rq; + time_t expire; + struct entry *next; +}; + +_PROTOTYPE(static int announce, (struct talk_request *request, char *rhost)); +_PROTOTYPE(static struct talk_request *lookup, (struct talk_request *request, int type)); +_PROTOTYPE(static int addreq, (struct talk_request *request)); +_PROTOTYPE(static delete_invite, (long id)); +_PROTOTYPE(static long nextid, (void)); +_PROTOTYPE(static void delete, (struct entry *e)); + +static struct entry *entry = (struct entry *)NULL; + +int processrequest(request, reply) +struct talk_request *request; +struct talk_reply *reply; +{ +char *p; +struct talk_request *rq; +struct hostent *hp; + + reply->version = TALK_VERSION; + reply->type = request->type; + reply->answer = 0; + reply->junk = 0; + reply->id = htonl(0); + + + /* check version */ + if(request->version != TALK_VERSION) { + reply->answer = BADVERSION; + return(0); + } + + /* check address family */ + if(ntohs(request->addr.sa_family) != AF_INET) { + reply->answer = BADADDR; + return(0); + } + + /* check control address family */ + if(ntohs(request->ctl_addr.sa_family) != AF_INET) { + reply->answer = BADCTLADDR; + return(0); + } + + /* check local name */ + p = request->luser; + while(*p) + if(!isprint(*p)) { + reply->answer = FAILED; + return(0); + } else + p++; + + switch(request->type) { + case ANNOUNCE: + reply->answer = find_user(request->ruser, request->rtty); + if(reply->answer != SUCCESS) break; + hp = gethostbyaddr((char *)&request->ctl_addr.sin_addr, sizeof(ipaddr_t), AF_INET); + if(hp == (struct hostent *)NULL) { + reply->answer = MACHINE_UNKNOWN; + break; + } + if((rq = lookup(request, 1)) == (struct talk_request *)NULL) { + reply->id = addreq(request); + reply->answer = announce(request, hp->h_name); + break; + } + if(ntohl(request->id) > ntohl(rq->id)) { + rq->id = nextid(); + reply->id = rq->id; + reply->answer = announce(request, hp->h_name); + } else { + reply->id = rq->id; + reply->answer = SUCCESS; + } + break; + case LEAVE_INVITE: + rq = lookup(request, 1); + if(rq == (struct talk_request *)NULL) + reply->id = addreq(request); + else { + reply->id = rq->id; + reply->answer = SUCCESS; + } + break; + case LOOK_UP: + if((rq = lookup(request, 0)) == (struct talk_request *)NULL) + reply->answer = NOT_HERE; + else { + reply->id = rq->id; + memcpy((char *)&reply->addr, (char *)&rq->addr, sizeof(reply->addr)); + reply->answer = SUCCESS; + } + break; + case DELETE: + reply->answer = delete_invite(request->id); + break; + default: + reply->answer = UNKNOWN_REQUEST; + } + + return(0); +} + +static int announce(request, rhost) +struct talk_request *request; +char *rhost; +{ +char tty[5+TTY_SIZE+1]; +struct stat st; +FILE *fp; +time_t now; +struct tm *tm; + + sprintf(tty, "/dev/%s", request->rtty); + + if(stat(tty, &st) < 0) + return(PERMISSION_DENIED); + + if(!(st.st_mode & S_IWGRP)) + return(PERMISSION_DENIED); + + if((fp = fopen(tty, "w")) == (FILE *)NULL) + return(PERMISSION_DENIED); + + (void) time(&now); + + tm = localtime(&now); + + fprintf(fp, "\007\007\007\rtalkd: Message from talkd@%s at %d:%02d:%02d\r\n", + myhostname, tm->tm_hour, tm->tm_min, tm->tm_sec); + fprintf(fp, "talkd: %s@%s would like to talk to you\r\n", + request->luser, rhost); + fprintf(fp, "talkd: to answer type: talk %s@%s\r\n", + request->luser, rhost); + + fclose(fp); + + return(SUCCESS); +} + +static struct talk_request *lookup(request, type) +struct talk_request *request; +int type; +{ +time_t now; +struct entry *e; + + (void) time(&now); + + for(e = entry; e != (struct entry *)NULL; e = e->next) { + if(now > e->expire) { + delete(e); + continue; + } + if(type == 0) { + if(!strncmp(request->luser, e->rq.ruser, USER_SIZE) && + !strncmp(request->ruser, e->rq.luser, USER_SIZE) && + e->rq.type == LEAVE_INVITE) + return(&e->rq); + } else { + if(request->type == e->rq.type && + request->pid == e->rq.pid && + !strncmp(request->luser, e->rq.luser, USER_SIZE) && + !strncmp(request->ruser, e->rq.ruser, USER_SIZE)) { + e->expire = now + MAX_LIFE; + return(&e->rq); + } + } + } + return((struct talk_request *)NULL); +} + +static int addreq(request) +struct talk_request *request; +{ +time_t now; +struct entry *e; + + (void) time(&now); + request->id = nextid(); + e = (struct entry *) malloc(sizeof(struct entry)); + if(e == (struct entry *)NULL) { + fprintf(stderr, "talkd: out of memory in insert table\n"); + exit(1); + } + e->expire = now + MAX_LIFE; + memcpy((char *)&e->rq, (char *)request, sizeof(struct talk_request)); + e->next = entry; + if(e->next != (struct entry *)NULL) + e->next->prev = e; + e->prev = (struct entry *)NULL; + entry = e; + return(request->id); +} + +static int delete_invite(id) +long id; +{ +time_t now; +struct entry *e; + + (void) time(&now); + + for(e = entry; e != (struct entry *)NULL; e = e->next) { + if(now > e->expire) { + delete(e); + continue; + } + if(e->rq.id == id) { + delete(e); + return(SUCCESS); + } + } + return(NOT_HERE); +} + +static void delete(e) +struct entry *e; +{ + if(e == (struct entry *)NULL) return; + + if(entry == e) + entry = e->next; + else + if(e->prev != (struct entry *)NULL) + e->prev->next = e->next; + + if(e->next != (struct entry *)NULL) + e->next->prev = e->prev; + + free((char *)e); + + return; +} + +static long nextid() +{ +static long id = 0; + + id++; + if(id <= 0) id = 1; + return(htonl(id)); +} diff --git a/commands/talkd/process.h b/commands/talkd/process.h new file mode 100755 index 000000000..79e02d28e --- /dev/null +++ b/commands/talkd/process.h @@ -0,0 +1,3 @@ +/* process.h Copyright Michael Temari 07/22/1996 All Rights Reserved */ + +_PROTOTYPE(int processrequest, (struct talk_request *request, struct talk_reply *reply)); diff --git a/commands/talkd/talk.h b/commands/talkd/talk.h new file mode 100755 index 000000000..e7ecbab11 --- /dev/null +++ b/commands/talkd/talk.h @@ -0,0 +1,57 @@ +/* talk.h Copyright Michael Temari 07/22/1996 All Rights Reserved */ + +#define USER_SIZE 12 +#define TTY_SIZE 16 +#define HOST_SIZE 255 + +struct osockaddr { + u16_t sa_family; + u16_t sin_port; + ipaddr_t sin_addr; + char junk[8]; +}; + +struct talk_request { + u8_t version; + u8_t type; + u8_t answer; + u8_t junk; + u32_t id; + struct osockaddr addr; + struct osockaddr ctl_addr; + long pid; + char luser[USER_SIZE]; + char ruser[USER_SIZE]; + char rtty[TTY_SIZE]; +}; + +struct talk_reply { + u8_t version; + u8_t type; + u8_t answer; + u8_t junk; + u32_t id; + struct osockaddr addr; +}; + +#define TALK_VERSION 1 + +/* message type values */ +#define LEAVE_INVITE 0 /* leave invitation with server */ +#define LOOK_UP 1 /* check for invitation by callee */ +#define DELETE 2 /* delete invitation by caller */ +#define ANNOUNCE 3 /* announce invitation by caller */ + +/* answer values */ +#define SUCCESS 0 /* operation completed properly */ +#define NOT_HERE 1 /* callee not logged in */ +#define FAILED 2 /* operation failed for unexplained reason */ +#define MACHINE_UNKNOWN 3 /* caller's machine name unknown */ +#define PERMISSION_DENIED 4 /* callee's tty doesn't permit announce */ +#define UNKNOWN_REQUEST 5 /* request has invalid type value */ +#define BADVERSION 6 /* request has invalid protocol version */ +#define BADADDR 7 /* request has invalid addr value */ +#define BADCTLADDR 8 /* request has invalid ctl_addr value */ + +#define MAX_LIFE 60 /* max time daemon saves invitations */ +#define RING_WAIT 30 /* time to wait before resending invitation */ diff --git a/commands/talkd/talkd.c b/commands/talkd/talkd.c new file mode 100755 index 000000000..b7afb88f5 --- /dev/null +++ b/commands/talkd/talkd.c @@ -0,0 +1,54 @@ +/* talkd.c Copyright Michael Temari 07/22/1996 All Rights Reserved */ + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> +#include <net/gen/in.h> + +#include "talk.h" +#include "talkd.h" +#include "net.h" +#include "process.h" + +_PROTOTYPE(int main, (int argc, char *argv[])); + +int opt_d = 0; +char myhostname[HOST_SIZE+1]; + +int main(argc, argv) +int argc; +char *argv[]; +{ +struct talk_request request; +struct talk_reply reply; + + if(argc > 1) + if(strcmp(argv[1], "-d") || argc > 2) { + fprintf(stderr, "Usage: talkd [-d]\n"); + return(-1); + } else + opt_d = 1; + + if(getuid() != 0) { + fprintf(stderr, "talkd: Must be run as super user\n"); + return(-1); + } + + if(gethostname(myhostname, HOST_SIZE) < 0) { + fprintf(stderr, "talkd: Error getting hostname\n"); + return(-1); + } + + if(NetInit()) { + fprintf(stderr, "talkd: Error in NetInit\n"); + return(-1); + } + + while(getrequest(&request) == 0) { + if(processrequest(&request, &reply)) break; + if(sendreply(&request, &reply)) break; + } + + return(-1); +} diff --git a/commands/talkd/talkd.h b/commands/talkd/talkd.h new file mode 100755 index 000000000..075d9736a --- /dev/null +++ b/commands/talkd/talkd.h @@ -0,0 +1,4 @@ +/* talkd.h Copyright Michael Temari 07/22/1996 All Rights Reserved */ + +extern int opt_d; /* debug option */ +extern char myhostname[]; diff --git a/commands/telnet/Makefile b/commands/telnet/Makefile new file mode 100755 index 000000000..cce1ccbb2 --- /dev/null +++ b/commands/telnet/Makefile @@ -0,0 +1,20 @@ +# Makefile for telnet + +CFLAGS= -D_MINIX -D_POSIX_SOURCE +LDFLAGS=-i + +SRC= ttn.c ttn_conf.c + +all: ttn + +ttn: $(SRC) + $(CC) $(CFLAGS) $(LDFLAGS) -o ttn $(SRC) + install -S 4kw $@ + +clean: + rm -f ttn + +install: /usr/bin/telnet + +/usr/bin/telnet: ttn + install -cs -o bin ttn $@ diff --git a/commands/telnet/ttn.c b/commands/telnet/ttn.c new file mode 100755 index 000000000..007a340c2 --- /dev/null +++ b/commands/telnet/ttn.c @@ -0,0 +1,689 @@ +/* +ttn.c +*/ + +#ifndef _POSIX_SOURCE +#define _POSIX_SOURCE 1 +#endif + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <termios.h> +#include <signal.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <net/hton.h> +#include <net/netlib.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/netdb.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> +#include "ttn.h" + +#if __STDC__ +#define PROTOTYPE(func,args) func args +#else +#define PROTOTYPE(func,args) func() +#endif + +static int do_read(int fd, char *buf, unsigned len); +static void screen(void); +static void keyboard(void); +static void send_brk(void); +static int process_opt (char *bp, int count); +static void do_option (int optsrt); +static void dont_option (int optsrt); +static void will_option (int optsrt); +static void wont_option (int optsrt); +static int writeall (int fd, char *buffer, int buf_size); +static int sb_termtype (char *sb, int count); +static void fatal(char *fmt, ...); +static void usage(void); + +#if DEBUG +#define where() (fprintf(stderr, "%s %d:", __FILE__, __LINE__)) +#endif + +static char *prog_name; +static tcp_fd; +static char *term_env; +static int esc_char= '~'; +static enum { LS_NORM, LS_BOL, LS_ESC } line_state= LS_BOL; + +int main(int argc, char *argv[]) +{ + struct hostent *hostent; + struct servent *servent; + ipaddr_t host; + tcpport_t port; + int pid, ppid; + nwio_tcpconf_t tcpconf; + int c, r; + nwio_tcpcl_t tcpconnopt; + struct termios termios; + char *tcp_device, *remote_name, *port_name; + char *e_arg; + + (prog_name=strrchr(argv[0],'/')) ? prog_name++ : (prog_name=argv[0]); + + e_arg= NULL; + while (c= getopt(argc, argv, "?e:"), c != -1) + { + switch(c) + { + case '?': usage(); + case 'e': e_arg= optarg; break; + default: + fatal("Optind failed: '%c'", c); + } + } + + if (optind >= argc) + usage(); + remote_name= argv[optind++]; + if (optind < argc) + port_name= argv[optind++]; + else + port_name= NULL; + if (optind != argc) + usage(); + + if (e_arg) + { + switch(strlen(e_arg)) + { + case 0: esc_char= -1; break; + case 1: esc_char= e_arg[0]; break; + default: fatal("Invalid escape character '%s'", e_arg); + } + } + + hostent= gethostbyname(remote_name); + if (!hostent) + fatal("Unknown host %s", remote_name); + host= *(ipaddr_t *)(hostent->h_addr); + + if (!port_name) + port= htons(TCPPORT_TELNET); + else + { + servent= getservbyname (port_name, "tcp"); + if (!servent) + { + port= htons(strtol(port_name, (char **)0, 0)); + if (!port) + fatal("Unknown port %s", port_name); + } + else + port= (tcpport_t)(servent->s_port); + } + + fprintf(stderr, "Connecting to %s:%u...\n", + inet_ntoa(host), ntohs(port)); + + tcp_device= getenv("TCP_DEVICE"); + if (tcp_device == NULL) + tcp_device= TCP_DEVICE; + tcp_fd= open (tcp_device, O_RDWR); + if (tcp_fd == -1) + fatal("Unable to open %s: %s", tcp_device, strerror(errno)); + + tcpconf.nwtc_flags= NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; + tcpconf.nwtc_remaddr= host; + tcpconf.nwtc_remport= port; + + r= ioctl (tcp_fd, NWIOSTCPCONF, &tcpconf); + if (r == -1) + fatal("NWIOSTCPCONF failed: %s", strerror(errno)); + + tcpconnopt.nwtcl_flags= 0; + do + { + r= ioctl (tcp_fd, NWIOTCPCONN, &tcpconnopt); + if (r == -1 && errno == EAGAIN) + { + fprintf(stderr, "%s: Got EAGAIN, sleeping(1s)\n", + prog_name); + sleep(1); + } + } while (r == -1 && errno == EAGAIN); + if (r == -1) + fatal("Unable to connect: %s", strerror(errno)); + printf("Connected\n"); + ppid= getpid(); + pid= fork(); + switch(pid) + { + case 0: + keyboard(); +#if DEBUG +fprintf(stderr, "killing %d with %d\r\n", ppid, SIGKILL); +#endif + kill(ppid, SIGKILL); + break; + case -1: + fprintf(stderr, "%s: fork failed: %s\r\n", argv[0], + strerror(errno)); + exit(1); + break; + default: + tcgetattr(0, &termios); + screen(); +#if DEBUG +fprintf(stderr, "killing %d with %d\r\n", pid, SIGKILL); +#endif + kill(pid, SIGKILL); + tcsetattr(0, TCSANOW, &termios); + break; + } + exit(0); +} + +static int do_read(fd, buf, len) +int fd; +char *buf; +unsigned len; +{ + nwio_tcpopt_t tcpopt; + int count; + + for (;;) + { + count= read (fd, buf, len); + if (count <0) + { + if (errno == EURG || errno == ENOURG) + { + /* Toggle urgent mode. */ + tcpopt.nwto_flags= errno == EURG ? + NWTO_RCV_URG : NWTO_RCV_NOTURG; + if (ioctl(tcp_fd, NWIOSTCPOPT, &tcpopt) == -1) + { + return -1; + } + continue; + } + return -1; + } + return count; + } +} + +static void screen() +{ + char buffer[1024], *bp, *iacptr; + int count, optsize; + + for (;;) + { + count= do_read (tcp_fd, buffer, sizeof(buffer)); +#if DEBUG && 0 + { where(); fprintf(stderr, "read %d bytes\r\n", count); } +#endif + if (count <0) + { + perror ("read"); + return; + } + if (!count) + return; + bp= buffer; + do + { + iacptr= memchr (bp, IAC, count); + if (!iacptr) + { + write(1, bp, count); + count= 0; + } + if (iacptr && iacptr>bp) + { +#if DEBUG + { where(); fprintf(stderr, "iacptr-bp= %d\r\n", iacptr-bp); } +#endif + write(1, bp, iacptr-bp); + count -= (iacptr-bp); + bp= iacptr; + continue; + } + if (iacptr) + { +assert (iacptr == bp); + optsize= process_opt(bp, count); +#if DEBUG && 0 + { where(); fprintf(stderr, "process_opt(...)= %d\r\n", optsize); } +#endif + if (optsize<0) + return; +assert (optsize); + bp += optsize; + count -= optsize; + } + } while (count); + } +} + +static void keyboard() +{ + char c, buffer[1024]; + int count; + + for (;;) + { + count= read (0, buffer, 1 /* sizeof(buffer) */); + if (count == -1) + fatal("Read: %s\r\n", strerror(errno)); + if (!count) + return; + + if (line_state != LS_NORM) + { + c= buffer[0]; + if (line_state == LS_BOL) + { + if (c == esc_char) + { + line_state= LS_ESC; + continue; + } + line_state= LS_NORM; + } + else if (line_state == LS_ESC) + { + line_state= LS_NORM; + if (c == '.') + return; + if (c == '#') + { + send_brk(); + continue; + } + + /* Not a valid command or a repeat of the + * escape char + */ + if (c != esc_char) + { + c= esc_char; + write(tcp_fd, &c, 1); + } + } + } + if (buffer[0] == '\n') + write(tcp_fd, "\r", 1); + count= write(tcp_fd, buffer, count); + if (buffer[0] == '\r') + { + line_state= LS_BOL; + write(tcp_fd, "\0", 1); + } + if (count<0) + { + perror("write"); + fprintf(stderr, "errno= %d\r\n", errno); + return; + } + if (!count) + return; + } +} + +static void send_brk(void) +{ + int r; + unsigned char buffer[2]; + + buffer[0]= IAC; + buffer[1]= IAC_BRK; + + r= writeall(tcp_fd, (char *)buffer, 2); + if (r == -1) + fatal("Error writing to TCP connection: %s", strerror(errno)); +} + +#define next_char(var) \ + if (offset<count) { (var) = bp[offset++]; } \ + else if (do_read(tcp_fd, (char *)&(var), 1) <= 0) \ + { perror ("read"); return -1; } + +static int process_opt (char *bp, int count) +{ + unsigned char iac, command, optsrt, sb_command; + int offset, result; ; +#if DEBUG && 0 + { where(); fprintf(stderr, "process_opt(bp= 0x%x, count= %d)\r\n", + bp, count); } +#endif + + offset= 0; +assert (count); + next_char(iac); +assert (iac == IAC); + next_char(command); + switch(command) + { + case IAC_NOP: + break; + case IAC_DataMark: + /* Ought to flush input queue or something. */ + break; + case IAC_BRK: +fprintf(stderr, "got a BRK\r\n"); + break; + case IAC_IP: +fprintf(stderr, "got a IP\r\n"); + break; + case IAC_AO: +fprintf(stderr, "got a AO\r\n"); + break; + case IAC_AYT: +fprintf(stderr, "got a AYT\r\n"); + break; + case IAC_EC: +fprintf(stderr, "got a EC\r\n"); + break; + case IAC_EL: +fprintf(stderr, "got a EL\r\n"); + break; + case IAC_GA: +fprintf(stderr, "got a GA\r\n"); + break; + case IAC_SB: + next_char(sb_command); + switch (sb_command) + { + case OPT_TERMTYPE: +#if DEBUG && 0 +fprintf(stderr, "got SB TERMINAL-TYPE\r\n"); +#endif + result= sb_termtype(bp+offset, count-offset); + if (result<0) + return result; + else + return result+offset; + default: +fprintf(stderr, "got an unknown SB (skiping)\r\n"); + for (;;) + { + next_char(iac); + if (iac != IAC) + continue; + next_char(optsrt); + if (optsrt == IAC) + continue; +if (optsrt != IAC_SE) + fprintf(stderr, "got IAC %d\r\n", optsrt); + break; + } + } + break; + case IAC_WILL: + next_char(optsrt); + will_option(optsrt); + break; + case IAC_WONT: + next_char(optsrt); + wont_option(optsrt); + break; + case IAC_DO: + next_char(optsrt); + do_option(optsrt); + break; + case IAC_DONT: + next_char(optsrt); + dont_option(optsrt); + break; + case IAC: +fprintf(stderr, "got a IAC\r\n"); + break; + default: +fprintf(stderr, "got unknown command (%d)\r\n", command); + } + return offset; +} + +static void do_option (int optsrt) +{ + unsigned char reply[3]; + int result; + + switch (optsrt) + { + case OPT_TERMTYPE: + if (WILL_terminal_type) + return; + if (!WILL_terminal_type_allowed) + { + reply[0]= IAC; + reply[1]= IAC_WONT; + reply[2]= optsrt; + } + else + { + WILL_terminal_type= TRUE; + term_env= getenv("TERM"); + if (!term_env) + term_env= "unknown"; + reply[0]= IAC; + reply[1]= IAC_WILL; + reply[2]= optsrt; + } + break; + default: +#if DEBUG + fprintf(stderr, "got a DO (%d)\r\n", optsrt); + fprintf(stderr, "WONT (%d)\r\n", optsrt); +#endif + reply[0]= IAC; + reply[1]= IAC_WONT; + reply[2]= optsrt; + break; + } + result= writeall(tcp_fd, (char *)reply, 3); + if (result<0) + perror("write"); +} + +static void will_option (int optsrt) +{ + unsigned char reply[3]; + int result; + + switch (optsrt) + { + case OPT_ECHO: + if (DO_echo) + break; + if (!DO_echo_allowed) + { + reply[0]= IAC; + reply[1]= IAC_DONT; + reply[2]= optsrt; + } + else + { + struct termios termios; + + tcgetattr(0, &termios); + termios.c_iflag &= ~(ICRNL|IGNCR|INLCR|IXON|IXOFF); + termios.c_oflag &= ~(OPOST); + termios.c_lflag &= ~(ECHO|ECHONL|ICANON|IEXTEN|ISIG); + tcsetattr(0, TCSANOW, &termios); + + DO_echo= TRUE; + reply[0]= IAC; + reply[1]= IAC_DO; + reply[2]= optsrt; + } + result= writeall(tcp_fd, (char *)reply, 3); + if (result<0) + perror("write"); + break; + case OPT_SUPP_GA: + if (DO_suppress_go_ahead) + break; + if (!DO_suppress_go_ahead_allowed) + { + reply[0]= IAC; + reply[1]= IAC_DONT; + reply[2]= optsrt; + } + else + { + DO_suppress_go_ahead= TRUE; + reply[0]= IAC; + reply[1]= IAC_DO; + reply[2]= optsrt; + } + result= writeall(tcp_fd, (char *)reply, 3); + if (result<0) + perror("write"); + break; + default: +#if DEBUG + fprintf(stderr, "got a WILL (%d)\r\n", optsrt); + fprintf(stderr, "DONT (%d)\r\n", optsrt); +#endif + reply[0]= IAC; + reply[1]= IAC_DONT; + reply[2]= optsrt; + result= writeall(tcp_fd, (char *)reply, 3); + if (result<0) + perror("write"); + break; + } +} + +static int writeall (fd, buffer, buf_size) +int fd; +char *buffer; +int buf_size; +{ + int result; + + while (buf_size) + { + result= write (fd, buffer, buf_size); + if (result <= 0) + return -1; +assert (result <= buf_size); + buffer += result; + buf_size -= result; + } + return 0; +} + +static void dont_option (int optsrt) +{ + switch (optsrt) + { + default: +#if DEBUG + fprintf(stderr, "got a DONT (%d)\r\n", optsrt); +#endif + break; + } +} + +static void wont_option (int optsrt) +{ + switch (optsrt) + { + default: +#if DEBUG + fprintf(stderr, "got a WONT (%d)\r\n", optsrt); +#endif + break; + } +} + +static int sb_termtype (char *bp, int count) +{ + unsigned char command, iac, optsrt; + unsigned char buffer[4]; + int offset, result; + + offset= 0; + next_char(command); + if (command == TERMTYPE_SEND) + { + buffer[0]= IAC; + buffer[1]= IAC_SB; + buffer[2]= OPT_TERMTYPE; + buffer[3]= TERMTYPE_IS; + result= writeall(tcp_fd, (char *)buffer,4); + if (result<0) + return result; + count= strlen(term_env); + if (!count) + { + term_env= "unknown"; + count= strlen(term_env); + } + result= writeall(tcp_fd, term_env, count); + if (result<0) + return result; + buffer[0]= IAC; + buffer[1]= IAC_SE; + result= writeall(tcp_fd, (char *)buffer,2); + if (result<0) + return result; + + } + else + { +#if DEBUG + where(); +#endif + fprintf(stderr, "got an unknown command (skipping)\r\n"); + } + for (;;) + { + next_char(iac); + if (iac != IAC) + continue; + next_char(optsrt); + if (optsrt == IAC) + continue; + if (optsrt != IAC_SE) + { +#if DEBUG + where(); +#endif + fprintf(stderr, "got IAC %d\r\n", optsrt); + } + break; + } + return offset; +} + +static void fatal(char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + fprintf(stderr, "%s: ", prog_name); + vfprintf(stderr, fmt, ap); + fprintf(stderr, "\n"); + va_end(ap); + + exit(1); +} + +static void usage(void) +{ + fprintf(stderr, "Usage: %s [-e esc-char] host [port]\r\n", + prog_name); + exit(1); +} + +/* + * $PchId: ttn.c,v 1.5 2002/05/07 12:06:41 philip Exp $ + */ diff --git a/commands/telnet/ttn.h b/commands/telnet/ttn.h new file mode 100755 index 000000000..ce9a1e312 --- /dev/null +++ b/commands/telnet/ttn.h @@ -0,0 +1,44 @@ +/* +ttn.h +*/ + +#ifndef TTN_H +#define TTN_H + +#define IAC 255 +#define IAC_SE 240 +#define IAC_NOP 241 +#define IAC_DataMark 242 +#define IAC_BRK 243 +#define IAC_IP 244 +#define IAC_AO 245 +#define IAC_AYT 246 +#define IAC_EC 247 +#define IAC_EL 248 +#define IAC_GA 249 +#define IAC_SB 250 +#define IAC_WILL 251 +#define IAC_WONT 252 +#define IAC_DO 253 +#define IAC_DONT 254 + +#define OPT_ECHO 1 +#define OPT_SUPP_GA 3 +#define OPT_TERMTYPE 24 + +#define TERMTYPE_SEND 1 +#define TERMTYPE_IS 0 + +#define FALSE 0 +#define TRUE (!(FALSE)) + +extern int DO_echo; +extern int DO_echo_allowed; +extern int WILL_terminal_type; +extern int WILL_terminal_type_allowed; +extern int DO_suppress_go_ahead; +extern int DO_suppress_go_ahead_allowed; +extern int WILL_suppress_go_ahead; +extern int WILL_suppress_go_ahead_allowed; + +#endif /* TTN_H */ diff --git a/commands/telnet/ttn_conf.c b/commands/telnet/ttn_conf.c new file mode 100755 index 000000000..1fa35e0ff --- /dev/null +++ b/commands/telnet/ttn_conf.c @@ -0,0 +1,12 @@ +/* +ttn_conf.c +*/ + +#include "ttn.h" + +int DO_echo= FALSE; +int DO_echo_allowed= TRUE; +int WILL_terminal_type= FALSE; +int WILL_terminal_type_allowed= TRUE; +int DO_suppress_go_ahead= FALSE; +int DO_suppress_go_ahead_allowed= TRUE; diff --git a/commands/telnetd/Makefile b/commands/telnetd/Makefile new file mode 100755 index 000000000..df0e88e4e --- /dev/null +++ b/commands/telnetd/Makefile @@ -0,0 +1,31 @@ +# Makefile for telnetd +# +# 01/30/96 Initial Release Michael Temari, <temari@ix.netcom.com> +# + +CFLAGS= -O -D_MINIX -D_POSIX_SOURCE +LDFLAGS=-i +BINDIR= /usr/bin +PROG= in.telnetd + +OBJS= main.o telnet.o term.o pty.o wtmp.o + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) + install -S 8kw $@ + +clean: + rm -f $(PROG) $(OBJS) + +install: $(BINDIR)/$(PROG) + +$(BINDIR)/$(PROG): $(PROG) + install -cs -o bin $? $@ + +main.o: main.c telnetd.h +telnet.o: telnet.c telnetd.h telnet.h +term.o: term.c telnetd.h telnet.h +pty.o: pty.c telnetd.h +wtmp.o: telnetd.h diff --git a/commands/telnetd/main.c b/commands/telnetd/main.c new file mode 100755 index 000000000..18e1640ae --- /dev/null +++ b/commands/telnetd/main.c @@ -0,0 +1,158 @@ +/* + * TNET A server program for MINIX which implements the TCP/IP + * suite of networking protocols. It is based on the + * TCP/IP code written by Phil Karn et al, as found in + * his NET package for Packet Radio communications. + * + * This file contains an implementation of the "server" + * for the TELNET protocol. This protocol can be used to + * remote-login on other systems, just like a normal TTY + * session. + * + * Usage: telnetd [-dv] + * + * Version: @(#)telnetd.c 1.00 07/26/92 + * + * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + * Michael Temari, <temari@temari.ae.ge.com> + */ +#include <sys/types.h> +#include <fcntl.h> +#include <sys/wait.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <time.h> +#include <stdio.h> +#include <ttyent.h> +#include <utmp.h> +#include <net/gen/in.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> +#include <net/gen/socket.h> +#include <net/gen/netdb.h> +#include <net/gen/inet.h> +#include "telnetd.h" + +static char *Version = "@(#) telnetd 1.00 (07/26/92)"; + +int opt_d = 0; /* debugging output flag */ + +_PROTOTYPE(void usage, (void)); +_PROTOTYPE(int main, (int argc, char *argv[])); +_PROTOTYPE(void wtmp, (int type, int linenr, char *line, pid_t pid, + char *host)); + +void usage() +{ + fprintf(stderr, "Usage: telnetd [-dv]\n"); + + exit(-1); +} + +int main(argc, argv) +int argc; +char *argv[]; +{ +char buff[128]; +register int c; +int pty_fd; +int tty_fd; +pid_t pid; +int lineno; +char *tty_name; +struct ttyent *ttyp; +nwio_tcpconf_t tcpconf; +struct hostent *hostent; +char *hostname; + + opterr = 0; + while ((c = getopt(argc, argv, "dv")) != EOF) switch(c) { + case 'd': + case 'v': + opt_d = 1; + break; + default: + usage(); + } + + /* No more arguments allowed. */ + if (optind != argc) usage(); + + /* Obtain the name of the remote host. */ + if (ioctl(0, NWIOGTCPCONF, &tcpconf) < 0) { + sprintf(buff, "Unable to obtain your IP address\r\n"); + (void) write(1, buff, strlen(buff)); + return(-1); + } + if ((hostent = gethostbyaddr((char *) &tcpconf.nwtc_remaddr, + sizeof(tcpconf.nwtc_remaddr), AF_INET)) != NULL) { + hostname = hostent->h_name; + } else { + hostname = inet_ntoa(tcpconf.nwtc_remaddr); + } + + /* Try allocating a PTY. */ + if (get_pty(&pty_fd, &tty_name) < 0) { + sprintf(buff, "I am sorry, but there is no free PTY left!\r\n"); + (void) write(1, buff, strlen(buff)); + return(-1); + } + + /* Find the tty in the tty table. */ + lineno = 0; + for (;;) { + if ((ttyp = getttyent()) == NULL) { + sprintf(buff, "Can't find %s in the tty table\r\n"); + (void) write(1, buff, strlen(buff)); + } + if (strcmp(ttyp->ty_name, tty_name+5) == 0) break; + lineno++; + } + endttyent(); + + /* Initialize the connection to an 8 bit clean channel. */ + term_init(); + + /* Fork off a child process and have it execute a getty(8). */ + if ((pid = fork()) == 0) { + /* Set up a new session. */ + setsid(); + if ((tty_fd = open(tty_name, O_RDWR)) < 0) { + sprintf(buff, "Can't open %s\r\n", tty_name); + (void) write(1, buff, strlen(buff)); + return(-1); + } + + close(pty_fd); + dup2(tty_fd, 0); + dup2(tty_fd, 1); + dup2(tty_fd, 2); + close(tty_fd); + (void) execl("/usr/sbin/getty", "getty", (char *)NULL); + (void) execl("/usr/bin/getty", "getty", (char *)NULL); + (void) execl("/usr/bin/login", "login", (char *)NULL); + (void) write(1, "EXEC failed!\r\n", 14); + } else if (pid < 0) { + sprintf(buff, "I am sorry, but the fork(2) call failed!\r\n"); + (void) write(1, buff, strlen(buff)); + (void) close(pty_fd); + return(-1); + } + + wtmp(LOGIN_PROCESS, lineno, tty_name+5, pid, hostname); + + term_inout(pty_fd); + + (void) close(pty_fd); + + wtmp(DEAD_PROCESS, lineno, tty_name+5, pid, hostname); + + chown(tty_name, 0, 0); + chmod(tty_name, 0666); + + return(0); +} diff --git a/commands/telnetd/pty.c b/commands/telnetd/pty.c new file mode 100755 index 000000000..0900a2cd0 --- /dev/null +++ b/commands/telnetd/pty.c @@ -0,0 +1,78 @@ +/* + * TNET A server program for MINIX which implements the TCP/IP + * suite of networking protocols. It is based on the + * TCP/IP code written by Phil Karn et al, as found in + * his NET package for Packet Radio communications. + * + * Handle the allocation of a PTY. + * + * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + */ +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include "telnetd.h" + + +#define DEV_DIR "/dev" + +/* + * Allocate a PTY, by trying to open one repeatedly, + * until all PTY channels are done. If at that point + * no PTY is found, go into panic mode :-( + */ +int get_pty(pty_fdp, tty_namep) +int *pty_fdp; +char **tty_namep; +{ + char buff[128], temp[128]; + register int i, j; + int pty_fd; + static char tty_name[128]; + + for(i = 'p'; i < 'w'; i++) { + j = 0; + do { + sprintf(buff, "%s/pty%c%c", + DEV_DIR, i, (j < 10) ? j + '0' : j + 'a' - 10); + + if (opt_d == 1) { + (void) write(2, "Testing: ", 9); + (void) write(2, buff, strlen(buff)); + (void) write(2, "...: ", 5); + } + + pty_fd = open(buff, O_RDWR); + if (opt_d == 1) { + if (pty_fd < 0) sprintf(temp, "error %d\r\n", errno); + else sprintf(temp, "OK\r\n"); + (void) write(2, temp, strlen(temp)); + } + + if (pty_fd >= 0) break; + + j++; + if (j == 16) break; + } while(1); + + /* Did we find one? */ + if (j < 16) break; + } + if (pty_fd < 0) return(-1); + + if (opt_d == 1) { + sprintf(temp, "File %s, desc %d\n", buff, pty_fd); + (void) write(1, temp, strlen(temp)); + } + + sprintf(tty_name, "%s/tty%c%c", DEV_DIR, + i, (j < 10) ? j + '0' : j + 'a' - 10); + + *pty_fdp = pty_fd; + *tty_namep = tty_name; + return(0); +} diff --git a/commands/telnetd/telnet.c b/commands/telnetd/telnet.c new file mode 100755 index 000000000..90794c3f1 --- /dev/null +++ b/commands/telnetd/telnet.c @@ -0,0 +1,263 @@ +/* + * TNET A server program for MINIX which implements the TCP/IP + * suite of networking protocols. It is based on the + * TCP/IP code written by Phil Karn et al, as found in + * his NET package for Packet Radio communications. + * + * This module handles telnet option processing. + * + * Author: Michael Temari, <temari@temari.ae.ge.com> 01/13/93 + * + */ +#include <sys/types.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include "telnetd.h" +#include "telnet.h" +#include <stdio.h> + +#define IN_DATA 0 +#define IN_CR 1 +#define IN_IAC 2 +#define IN_IAC2 3 + +_PROTOTYPE(static void dowill, (int c)); +_PROTOTYPE(static void dowont, (int c)); +_PROTOTYPE(static void dodo, (int c)); +_PROTOTYPE(static void dodont, (int c)); +_PROTOTYPE(static void respond, (int ack, int option)); + +#define LASTTELOPT TELOPT_SGA + +static int TelROpts[LASTTELOPT+1]; +static int TelLOpts[LASTTELOPT+1]; + +static int telfdout; + +void tel_init() +{ +int i; + + for(i = 0; i <= LASTTELOPT; i++) { + TelROpts[i] = 0; + TelLOpts[i] = 0; + } +} + +void telopt(fdout, what, option) +int fdout; +int what; +int option; +{ +char buf[3]; +int len; + + buf[0] = IAC; + buf[1] = what; + buf[2] = option; + len = 0; + + switch(what) { + case DO: + if(option <= LASTTELOPT) { + TelROpts[option] = 1; + len = 3; + } + break; + case DONT: + if(option <= LASTTELOPT) { + TelROpts[option] = 1; + len = 3; + } + break; + case WILL: + if(option <= LASTTELOPT) { + TelLOpts[option] = 1; + len = 3; + } + break; + case WONT: + if(option <= LASTTELOPT) { + TelLOpts[option] = 1; + len = 3; + } + break; + } + if(len > 0) + (void) write(fdout, buf, len); +} + +int tel_in(fdout, telout, buffer, len) +int fdout; +int telout; +char *buffer; +int len; +{ +static int InState = IN_DATA; +static int ThisOpt = 0; +char *p; +char *p2; +int size; +int c; + + telfdout = telout; + p = p2 = buffer; + size = 0; + + while(len > 0) { + c = (unsigned char)*p++; len--; + switch(InState) { + case IN_CR: + InState = IN_DATA; + if(c == 0 || c == '\n') + break; + /* fall through */ + case IN_DATA: + if(c == IAC) { + InState = IN_IAC; + break; + } + *p2++ = c; size++; + if(c == '\r') InState = IN_CR; + break; + case IN_IAC: + switch(c) { + case IAC: + *p2++ = c; size++; + InState = IN_DATA; + break; + case WILL: + case WONT: + case DO: + case DONT: + InState = IN_IAC2; + ThisOpt = c; + break; + case EOR: + case SE: + case NOP: + case BREAK: + case IP: + case AO: + case AYT: + case EC: + case EL: + case GA: + case SB: + break; + default: + break; + } + break; + case IN_IAC2: + if(size > 0) { + write(fdout, buffer, size); + p2 = buffer; + size = 0; + } + InState = IN_DATA; + switch(ThisOpt) { + case WILL: dowill(c); break; + case WONT: dowont(c); break; + case DO: dodo(c); break; + case DONT: dodont(c); break; + } + break; + } + } + + if(size > 0) + write(fdout, buffer, size); +} + +int tel_out(fdout, buf, size) +int fdout; +char *buf; +int size; +{ +char *p; +int got_iac, len; + + p = buf; + while(size > 0) { + buf = p; + got_iac = 0; + if((p = (char *)memchr(buf, IAC, size)) != (char *)NULL) { + got_iac = 1; + p++; + } else + p = buf + size; + len = p - buf; + if(len > 0) + (void) write(fdout, buf, len); + if(got_iac) + (void) write(fdout, p - 1, 1); + size = size - len; + } +} + +static void dowill(c) +int c; +{ +int ack; + + switch(c) { + case TELOPT_BINARY: + case TELOPT_ECHO: + case TELOPT_SGA: + if(TelROpts[c] == 1) + return; + TelROpts[c] = 1; + ack = DO; + break; + default: + ack = DONT; + } + respond(ack, c); +} + +static void dowont(c) +int c; +{ + if(c <= LASTTELOPT) { + if(TelROpts[c] == 0) + return; + TelROpts[c] = 0; + } + respond(DONT, c); +} + +static void dodo(c) +int c; +{ +int ack; + + switch(c) { + default: + ack = WONT; + } + respond(ack, c); +} + +static void dodont(c) +int c; +{ + if(c <= LASTTELOPT) { + if(TelLOpts[c] == 0) + return; + TelLOpts[c] = 0; + } + respond(WONT, c); +} + +static void respond(ack, option) +int ack, option; +{ +unsigned char c[3]; + + c[0] = IAC; + c[1] = ack; + c[2] = option; + /* write(telfdout, c, 3); */ +} diff --git a/commands/telnetd/telnet.h b/commands/telnetd/telnet.h new file mode 100755 index 000000000..668b282aa --- /dev/null +++ b/commands/telnetd/telnet.h @@ -0,0 +1,70 @@ +/* + * TNET A server program for MINIX which implements the TCP/IP + * suite of networking protocols. It is based on the + * TCP/IP code written by Phil Karn et al, as found in + * his NET package for Packet Radio communications. + * + * Definitions for the TELNET protocol (see RFC XXX). + * + * Version: @(#)arpa/telnet.h 1.00 07/02/92 + * + * Authors: Original taken from BSD 4.3/TAHOE. + * Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + */ +#ifndef _ARPA_TELNET_H +#define _ARPA_TELNET_H + +#define IAC 255 /* interpret as command: */ +#define DONT 254 /* you are not to use option */ +#define DO 253 /* please, you use option */ +#define WONT 252 /* I won't use option */ +#define WILL 251 /* I will use option */ +#define SB 250 /* interpret as subnegotiation */ +#define GA 249 /* you may reverse the line */ +#define EL 248 /* erase the current line */ +#define EC 247 /* erase the current character */ +#define AYT 246 /* are you there */ +#define AO 245 /* abort output--but let prog finish */ +#define IP 244 /* interrupt process--permanently */ +#define BREAK 243 /* break */ +#define DM 242 /* data mark--for connect. cleaning */ +#define NOP 241 /* nop */ +#define SE 240 /* end sub negotiation */ +#define EOR 239 /* end of record (transparent mode) */ + +#define SYNCH 242 /* for telfunc calls */ + +/* Telnet options. */ +#define TELOPT_BINARY 0 /* 8-bit data path */ +#define TELOPT_ECHO 1 /* echo */ +#define TELOPT_RCP 2 /* prepare to reconnect */ +#define TELOPT_SGA 3 /* suppress go ahead */ +#define TELOPT_NAMS 4 /* approximate message size */ +#define TELOPT_STATUS 5 /* give status */ +#define TELOPT_TM 6 /* timing mark */ +#define TELOPT_RCTE 7 /* remote controlled transmission and echo */ +#define TELOPT_NAOL 8 /* negotiate about output line width */ +#define TELOPT_NAOP 9 /* negotiate about output page size */ +#define TELOPT_NAOCRD 10 /* negotiate about CR disposition */ +#define TELOPT_NAOHTS 11 /* negotiate about horizontal tabstops */ +#define TELOPT_NAOHTD 12 /* negotiate about horizontal tab disposition */ +#define TELOPT_NAOFFD 13 /* negotiate about formfeed disposition */ +#define TELOPT_NAOVTS 14 /* negotiate about vertical tab stops */ +#define TELOPT_NAOVTD 15 /* negotiate about vertical tab disposition */ +#define TELOPT_NAOLFD 16 /* negotiate about output LF disposition */ +#define TELOPT_XASCII 17 /* extended ascic character set */ +#define TELOPT_LOGOUT 18 /* force logout */ +#define TELOPT_BM 19 /* byte macro */ +#define TELOPT_DET 20 /* data entry terminal */ +#define TELOPT_SUPDUP 21 /* supdup protocol */ +#define TELOPT_SUPDUPOUTPUT 22 /* supdup output */ +#define TELOPT_SNDLOC 23 /* send location */ +#define TELOPT_TTYPE 24 /* terminal type */ +#define TELOPT_EOR 25 /* end or record */ +#define TELOPT_EXOPL 255 /* extended-options-list */ + +/* Sub-option qualifiers. */ +#define TELQUAL_IS 0 /* option is... */ +#define TELQUAL_SEND 1 /* send option */ + +#endif /* _ARPA_TELNET_H */ diff --git a/commands/telnetd/telnetd.h b/commands/telnetd/telnetd.h new file mode 100755 index 000000000..04e19ec52 --- /dev/null +++ b/commands/telnetd/telnetd.h @@ -0,0 +1,20 @@ +/* + * TNET A server program for MINIX which implements the TCP/IP + * suite of networking protocols. It is based on the + * TCP/IP code written by Phil Karn et al, as found in + * his NET package for Packet Radio communications. + * + * Definitions for the TELNET server. + * + * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + */ + +extern int opt_d; /* debugging flag */ + +_PROTOTYPE(int get_pty, (int *, char **)); +_PROTOTYPE(void term_init, (void)); +_PROTOTYPE(void term_inout, (int pty_fd)); +_PROTOTYPE(void tel_init, (void)); +_PROTOTYPE(void telopt, (int fdout, int what, int option)); +_PROTOTYPE(int tel_in, (int fdout, int telout, char *buffer, int len)); +_PROTOTYPE(int tel_out, (int fdout, char *buf, int size)); diff --git a/commands/telnetd/term.c b/commands/telnetd/term.c new file mode 100755 index 000000000..446b6ee84 --- /dev/null +++ b/commands/telnetd/term.c @@ -0,0 +1,85 @@ +/* + * TNET A server program for MINIX which implements the TCP/IP + * suite of networking protocols. It is based on the + * TCP/IP code written by Phil Karn et al, as found in + * his NET package for Packet Radio communications. + * + * Handle the TERMINAL module. + * + * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + * Michael Temari, <temari@temari.ae.ge.com> + * + * 07/29/92 MT Telnet options hack which seems to work okay + * 01/12/93 MT Better telnet options processing instead of hack + */ +#include <sys/types.h> +#include <errno.h> +#if 0 +#include <fcntl.h> +#endif +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <signal.h> +#include "telnet.h" +#include "telnetd.h" + +_PROTOTYPE(void sig_done, (int sig)); + +static char buff[4096]; + +void term_init() +{ + tel_init(); + + telopt(1, WILL, TELOPT_SGA); + telopt(1, DO, TELOPT_SGA); + telopt(1, WILL, TELOPT_BINARY); + telopt(1, DO, TELOPT_BINARY); + telopt(1, WILL, TELOPT_ECHO); +} + +static int io_done = 0; + +void term_inout(pty_fd) +int pty_fd; +{ +register int i; +pid_t pid; +struct sigaction sa; + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sa.sa_handler = sig_done; + sigaction(SIGALRM, &sa, (struct sigaction *) NULL); + + if ((pid = fork()) == -1) { + sprintf(buff, "telnetd: fork() failed: %s\r\n", strerror(errno)); + (void) write(1, buff, strlen(buff)); + } + + if (pid != 0) { + /* network -> login process */ + while (!io_done && (i = read(0, buff, sizeof(buff))) > 0) { + tel_in(pty_fd, 1, buff, i); + } + /* EOF, kill opposite number and exit. */ + (void) kill(pid, SIGKILL); + } else { + /* login process -> network */ + while ((i = read(pty_fd, buff, sizeof(buff))) > 0) { + tel_out(1, buff, i); + } + /* EOF, alert opposite number and exit. */ + (void) kill(getppid(), SIGALRM); + } + /* EOF. */ +} + +void sig_done(sig) +int sig; +{ + io_done = 1; + alarm(1); /* there is always a chance... */ +} diff --git a/commands/telnetd/wtmp.c b/commands/telnetd/wtmp.c new file mode 100755 index 000000000..47769ce18 --- /dev/null +++ b/commands/telnetd/wtmp.c @@ -0,0 +1,113 @@ +/* + * TNET A server program for MINIX which implements the TCP/IP + * suite of networking protocols. It is based on the + * TCP/IP code written by Phil Karn et al, as found in + * his NET package for Packet Radio communications. + * + * This file contains an implementation of the "server" + * for the TELNET protocol. This protocol can be used to + * remote-login on other systems, just like a normal TTY + * session. + * + * Usage: telnetd [-dv] + * + * Version: @(#)telnetd.c 1.00 07/26/92 + * + * Author: Fred N. van Kempen, <waltje@uwalt.nl.mugnet.org> + * Michael Temari, <temari@temari.ae.ge.com> + */ +#include <sys/types.h> +#include <fcntl.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <utmp.h> +#include <time.h> +#include <stdio.h> +#include "telnetd.h" + +static char PATH_UTMP[] = "/etc/utmp"; +static char PATH_WTMP[] = "/usr/adm/wtmp"; + +_PROTOTYPE(void wtmp, (int type, int linenr, char *line, pid_t pid, + char *host)); +_PROTOTYPE(void report, (char *label)); + +void wtmp(type, linenr, line, pid, host) +int type; /* type of entry */ +int linenr; /* line number in ttytab */ +char *line; /* tty name (only good on login) */ +pid_t pid; /* pid of process */ +char *host; /* name of the remote host */ +{ +/* Log an event into the UTMP and WTMP files. */ + + struct utmp utmp; /* UTMP/WTMP User Accounting */ + int fd; + + /* Clear the utmp record. */ + memset((void *) &utmp, 0, sizeof(utmp)); + + /* Fill in utmp. */ + switch (type) { + case LOGIN_PROCESS: + /* A new login, fill in line and host name. */ + strncpy(utmp.ut_line, line, sizeof(utmp.ut_line)); + strncpy(utmp.ut_host, host, sizeof(utmp.ut_host)); + break; + + case DEAD_PROCESS: + /* A logout. Use the current utmp entry, but make sure it is a + * user process exiting, and not getty or login giving up. + */ + if ((fd = open(PATH_UTMP, O_RDONLY)) < 0) { + if (errno != ENOENT) report(PATH_UTMP); + return; + } + if (lseek(fd, (off_t) (linenr+1) * sizeof(utmp), SEEK_SET) == -1 + || read(fd, &utmp, sizeof(utmp)) == -1 + ) { + report(PATH_UTMP); + close(fd); + return; + } + close(fd); + if (utmp.ut_type != USER_PROCESS) return; + strncpy(utmp.ut_name, "", sizeof(utmp.ut_name)); + break; + } + + /* Finish new utmp entry. */ + utmp.ut_pid = pid; + utmp.ut_type = type; + utmp.ut_time = time((time_t *) 0); + + /* Write new entry to utmp. */ + if ((fd = open(PATH_UTMP, O_WRONLY)) < 0 + || lseek(fd, (off_t) (linenr+1) * sizeof(utmp), SEEK_SET) == -1 + || write(fd, &utmp, sizeof(utmp)) == -1 + ) { + if (errno != ENOENT) report(PATH_UTMP); + } + if (fd != -1) close(fd); + + if (type == DEAD_PROCESS) { + /* Add new wtmp entry. */ + if ((fd = open(PATH_WTMP, O_WRONLY | O_APPEND)) < 0 + || write(fd, &utmp, sizeof(utmp)) == -1 + ) { + if (errno != ENOENT) report(PATH_WTMP); + } + if (fd != -1) close(fd); + } +} + +void report(label) +char *label; +{ + char message[128]; + + sprintf(message, "telnetd: %s: %s\r\n", strerror(errno)); + (void) write(1, message, strlen(message)); +} diff --git a/commands/urlget/Makefile b/commands/urlget/Makefile new file mode 100755 index 000000000..bf7d23636 --- /dev/null +++ b/commands/urlget/Makefile @@ -0,0 +1,35 @@ +# Makefile for urlget +# +# 07/02/96 Initial Release Michael Temari, <temari@ix.netcom.com> +# + +CFLAGS= -D_MINIX $(OPT) +LDFLAGS=-fnone +BINDIR= /usr/bin +PROG= urlget +LINK1= httpget +LINK2= ftpget + +OBJS= urlget.o net.o + +all: $(PROG) + +$(PROG): $(OBJS) + $(CC) $(LDFLAGS) -o $@ $(OBJS) + install -S 4kw $@ + +clean: + rm -f $(PROG) $(OBJS) + +tar: + tar cvf urlget.tar Makefile urlget.c net.c net.h + +install: $(BINDIR)/$(PROG) $(BINDIR)/$(LINK1) $(BINDIR)/$(LINK2) + +$(BINDIR)/$(PROG): $(PROG) + install -c $? $@ + +$(BINDIR)/$(LINK1) $(BINDIR)/$(LINK2): $(BINDIR)/$(PROG) + install -l $? $@ + +$(OBJS): net.h diff --git a/commands/urlget/net.c b/commands/urlget/net.c new file mode 100755 index 000000000..c831f759a --- /dev/null +++ b/commands/urlget/net.c @@ -0,0 +1,98 @@ +/* net.c Copyright 2000 by Michael Temari All Rights Reserved */ +/* 04/05/2000 Michael Temari <Michael@TemWare.Com> */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <unistd.h> +#include <time.h> +#include <net/netlib.h> +#include <net/hton.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> +#include <net/gen/socket.h> +#include <net/gen/netdb.h> + +#include "net.h" + +int connect(host, port) +char *host; +int port; +{ +nwio_tcpconf_t tcpconf; +nwio_tcpcl_t tcpcopt; +char *tcp_device; +int netfd; +ipaddr_t nethost; +tcpport_t netport; +struct hostent *hp; +struct servent *sp; +char *p; +int s; +int tries; + + if((hp = gethostbyname(host)) == (struct hostent *)NULL) { + fprintf(stderr, "Unknown host %s!\n", host); + return(-1); + } else + memcpy((char *) &nethost, (char *) hp->h_addr, hp->h_length); + + netport = htons(port); + + /* Connect to the host */ + if((tcp_device = getenv("TCP_DEVICE")) == NULL) + tcp_device = TCP_DEVICE; + + if((netfd = open(tcp_device, O_RDWR)) < 0) { + perror("httpget: opening tcp"); + return(-1); + } + + tcpconf.nwtc_flags = NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; + tcpconf.nwtc_remaddr = nethost; + tcpconf.nwtc_remport = netport; + + s = ioctl(netfd, NWIOSTCPCONF, &tcpconf); + if(s < 0) { + perror("httpget: NWIOSTCPCONF"); + close(netfd); + return(-1); + } + + s = ioctl(netfd, NWIOGTCPCONF, &tcpconf); + if(s < 0) { + perror("httpget: NWIOGTCPCONF"); + close(netfd); + return(-1); + } + + tcpcopt.nwtcl_flags = 0; + + tries = 0; + do { + s = ioctl(netfd, NWIOTCPCONN, &tcpcopt); + if(s == -1 && errno == EAGAIN) { + if(tries++ >= 10) + break; + sleep(10); + } else + break; + } while(1); + + if(s < 0) { + perror("httpget: NWIOTCPCONN"); + close(netfd); + return(-1); + } + + return(netfd); +} diff --git a/commands/urlget/net.h b/commands/urlget/net.h new file mode 100755 index 000000000..08e1ac6fd --- /dev/null +++ b/commands/urlget/net.h @@ -0,0 +1,4 @@ +/* net.h Copyright 2000 by Michael Temari All Rights Reserved */ +/* 04/05/2000 Michael Temari <Michael@TemWare.Com> */ + +_PROTOTYPE(int connect, (char *host, int port)); diff --git a/commands/urlget/urlget.c b/commands/urlget/urlget.c new file mode 100755 index 000000000..44be5e22a --- /dev/null +++ b/commands/urlget/urlget.c @@ -0,0 +1,609 @@ +/* urlget.c Copyright 2000 by Michael Temari All Rights Reserved */ +/* 04/05/2000 Michael Temari <Michael@TemWare.Com> */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/wait.h> +#include <stdio.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <unistd.h> +#include <time.h> + +#include "net.h" + +_PROTOTYPE(char *unesc, (char *s)); +_PROTOTYPE(void encode64, (char **pp, char *s)); +_PROTOTYPE(char *auth, (char *user, char *pass)); +_PROTOTYPE(int skipit, (char *buf, int len, int *skip)); +_PROTOTYPE(int httpget, (char *host, int port, char *user, char *pass, char *path, int headers, int discard, int post)); +_PROTOTYPE(void ftppasv, (char *reply)); +_PROTOTYPE(int ftpreply, (FILE *fpr)); +_PROTOTYPE(int ftpcmd, (FILE *fpw, FILE *fpr, char *cmd, char *arg)); +_PROTOTYPE(int ftpget, (char *host, int port, char *user, char *pass, char *path, int type)); +_PROTOTYPE(int tcpget, (char *host, int port, char *user, char *pass, char *path)); +_PROTOTYPE(int main, (int argc, char *argv[])); + +char ftpphost[15+1]; +unsigned int ftppport; + +#define SCHEME_HTTP 1 +#define SCHEME_FTP 2 +#define SCHEME_TCP 3 +#define SCHEME_NNTP 4 + +char buffer[16000]; + +#if 0 +_PROTOTYPE(int strncasecmp, (const char *s1, const char *s2, size_t len)); +int +strncasecmp(s1, s2, len) +const char *s1, *s2; +size_t len; +{ + int c1, c2; + do { + if (len == 0) + return 0; + len--; + } while (c1= toupper(*s1++), c2= toupper(*s2++), c1 == c2 && (c1 & c2)) + ; + if (c1 & c2) + return c1 < c2 ? -1 : 1; + return c1 ? 1 : (c2 ? -1 : 0); +} +#endif + +char *unesc(s) +char *s; +{ +char *p; +char *p2; +unsigned char c; + + p = s; + p2 = s; + while(*p) { + if(*p != '%') { + *p2++ = *p++; + continue; + } + p++; + if(*p == '%') { + *p2++ = *p++; + continue; + } + if(*p >= '0' && *p <= '9') c = *p++ - '0'; else + if(*p >= 'a' && *p <= 'f') c = *p++ - 'a' + 10; else + if(*p >= 'A' && *p <= 'F') c = *p++ - 'A' + 10; else + break; + if(*p >= '0' && *p <= '9') c = c << 4 | (*p++ - '0'); else + if(*p >= 'a' && *p <= 'f') c = c << 4 | (*p++ - 'a') + 10; else + if(*p >= 'A' && *p <= 'F') c = c << 4 | (*p++ - 'A') + 10; else + break; + *p2++ = c; + } + *p2 = '\0'; + return(s); +} + +void encode64(pp, s) +char **pp; +char *s; +{ +char *p; +char c[3]; +int i; +int len; +static char e64[64] = { + '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', + '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', + '0','1','2','3','4','5','6','7','8','9','+','/' }; + + p = *pp; + len = strlen(s); + for(i=0; i < len; i += 3) { + c[0] = *s++; + c[1] = *s++; + c[2] = *s++; + *p++ = e64[ c[0] >> 2]; + *p++ = e64[((c[0] << 4) & 0x30) | ((c[1] >> 4) & 0x0f)]; + *p++ = e64[((c[1] << 2) & 0x3c) | ((c[2] >> 6) & 0x03)]; + *p++ = e64[ c[2] & 0x3f]; + } + if(i == len+1) + p[-1] = '='; + else + if(i == len+2) { + p[-1] = '='; + p[-2] = '='; + } + *p = '\0'; + *pp = p; + return; +} + +char *auth(user, pass) +char *user; +char *pass; +{ +static char a[128]; +char up[128]; +char *p; + + strcpy(a, "BASIC "); + p = a + 6; + sprintf(up, "%s:%s", user, pass); + encode64(&p, up); + + return(a); +} + +int skipit(buf, len, skip) +char *buf; +int len; +int *skip; +{ +static int lf = 0; +static int crlf = 0; +char *p; + + p = buf; + + while(--len >= 0) { + if((crlf == 0 || crlf == 2) && *p == '\r') + crlf++; + else + if((crlf == 1 || crlf == 3) && *p == '\n') + crlf++; + else + crlf = 0; + if(*p == '\n') + lf++; + else + lf = 0; + if(crlf == 4 || lf == 2) { + *skip = 0; + return(len); + } + p++; + } + + return(0); +} + +int httpget(host, port, user, pass, path, headers, discard, post) +char *host; +int port; +char *user; +char *pass; +char *path; +int headers; +int discard; +int post; +{ +int fd; +int skip; +int s; +int s2; +char *a; +char *qs; +int len; + + if(port == 0) + port = 80; + + fd = connect(host, port); + if(fd < 0) { + fprintf(stderr, "httpget: Could not connect to %s:%d\n", host, port); + return(-1); + } + + if(post) { + qs = strrchr(path, '?'); + if(qs != (char *)NULL) { + *qs++ = '\0'; + len = strlen(qs); + } else + len = 0; + } + + if(post && len > 0) + write(fd, "POST ", 5); + else + write(fd, "GET ", 4); + write(fd, path, strlen(path)); + write(fd, " HTTP/1.0\r\n", 11); + write(fd, "User-Agent: urlget\r\n", 20); + write(fd, "Connection: Close\r\n", 19); + if(*user) { + write(fd, "Authorization: ", 15); + a = auth(user, pass); + write(fd, a, strlen(a)); + write(fd, "\r\n", 2); + } + if(post && len > 0) { + sprintf(buffer, "Content-Length: %u\r\n", len); + write(fd, buffer, strlen(buffer)); + } + write(fd, "Host: ", 6); + write(fd, host, strlen(host)); + write(fd, "\r\n", 2); + write(fd, "\r\n", 2); + if(post && len > 0) + write(fd, qs, len); + + skip = 1; + while((s = read(fd, buffer, sizeof(buffer))) > 0) { + if(skip) { + s2 = skipit(buffer, s, &skip); + if(headers) + write(1, buffer, s - s2); + } else + s2 = s; + if(s2 && !discard) + if(write(1, &buffer[s - s2], s2) != s2) { + perror("write"); + return(-1); + } + } + if(s < 0) { + fprintf(stderr, "httpget: Read error\n"); + return(-1); + } + + close(fd); + + return(0); +} + +void ftppasv(reply) +char *reply; +{ +char *p; +unsigned char n[6]; +int i; + + ftppport = 0; + + p = reply; + while(*p && *p != '(') p++; + if(!*p) return; + p++; + i = 0; + while(1) { + n[i++] = atoi(p); + if(i == 6) break; + p = strchr(p, ','); + if(p == (char *)NULL) return; + p++; + } + sprintf(ftpphost, "%d.%d.%d.%d", n[0], n[1], n[2], n[3]); + ftppport = n[4] * 256 + n[5]; + return; +} + +int ftpreply(fpr) +FILE *fpr; +{ +static char reply[256]; +int s; +char code[4]; +int ft; + + do { + ft = 1; + do { + if(fgets(reply, sizeof(reply), fpr) == (char *)NULL) + return(-1); + if(ft) { + ft = 0; + strncpy(code, reply, 3); + code[3] = '\0'; + } + } while(strncmp(reply, code, 3) || reply[3] == '-'); + s = atoi(code); + } while(s < 200 && s != 125 && s != 150); + if(s == 227) ftppasv(reply); + return(s); +} + +int ftpcmd(fpw, fpr, cmd, arg) +FILE *fpw; +FILE *fpr; +char *cmd; +char *arg; +{ + fprintf(fpw, "%s%s%s\r\n", cmd, *arg ? " " : "", arg); + fflush(fpw); + return(ftpreply(fpr)); +} + +int ftpget(host, port, user, pass, path, type) +char *host; +int port; +char *user; +char *pass; +char *path; +int type; +{ +int fd; +int fd2; +FILE *fpr; +FILE *fpw; +int s; +int s2; +char *p; +char *p2; +char typec[2]; + + if(port == 0) + port = 21; + + if(type == '\0') + type = 'i'; + + fd = connect(host, port); + if(fd < 0) { + fprintf(stderr, "ftpget: Could not connect to %s:%d\n", host, port); + return(-1); + } + fpr = fdopen(fd, "r"); + fpw = fdopen(fd, "w"); + + s = ftpreply(fpr); + if(s / 100 != 2) goto error; + s = ftpcmd(fpw, fpr, "USER", *user ? user : "ftp"); + if(s / 100 == 3) + s = ftpcmd(fpw, fpr, "PASS", *pass ? pass : "urlget@"); + + if(s / 100 != 2) goto error; + + p = path; + if(*p == '/') p++; + while((p2 = strchr(p, '/')) != (char *)NULL) { + *p2++ = '\0'; + s = ftpcmd(fpw, fpr, "CWD", unesc(p)); + p = p2; + } + sprintf(typec, "%c", type == 'd' ? 'A' : type); + s = ftpcmd(fpw, fpr, "TYPE", typec); + if(s / 100 != 2) goto error; + s = ftpcmd(fpw, fpr, "PASV", ""); + if(s != 227) goto error; + fd2 = connect(ftpphost, ftppport); + if(fd2 < 0) goto error; + s = ftpcmd(fpw, fpr, type == 'd' ? "NLST" : "RETR", unesc(p)); + if(s / 100 != 1) goto error; + while((s = read(fd2, buffer, sizeof(buffer))) > 0) { + s2 = write(1, buffer, s); + if(s2 != s) break; + } + if(s2 != s && s != 0) s = -1; + close(fd2); + + s = ftpreply(fpr); + if(s / 100 == 2) s = 0; + +error: + (void) ftpcmd(fpw, fpr, "QUIT", ""); + + fclose(fpr); + fclose(fpw); + close(fd); + + return(s == 0 ? 0 : -1); +} + +int tcpget(host, port, user, pass, path) +char *host; +int port; +char *user; +char *pass; +char *path; +{ +int fd; +int s; +int s2; + + if(port == 0) { + fprintf(stderr, "tcpget: No port specified\n"); + return(-1); + } + + fd = connect(host, port); + if(fd < 0) { + fprintf(stderr, "httpget: Could not connect to %s:%d\n", host, port); + return(-1); + } + if(*path == '\/') + path++; + + write(fd, path, strlen(path)); + write(fd, "\n", 1); + while((s = read(fd, buffer, sizeof(buffer))) > 0) { + s2 = write(1, buffer, s); + if(s2 != s) break; + } + close(fd); + return(0); +} + +int main(argc, argv) +int argc; +char *argv[]; +{ +char *prog; +char *url; +char scheme; +char user[64]; +char pass[64]; +char host[64]; +int port; +char *path; +int type; +char *ps; +char *p; +char *at; +int s, c; +int opt_h = 0; +int opt_d = 0; +int opt_p = 0; + + prog = strrchr(*argv, '/'); + if(prog == (char *)NULL) + prog = *argv; + argv++; + argc--; + + while(argc && argv[0][0] == '-') { + char *opt = *argv++ + 1; + argc--; + + if (opt[0] == '-' && opt[1] == 0) break; + + while (*opt) switch (*opt++) { + case 'h': opt_h = -1; break; + case 'd': opt_d = -1; break; + case 'p': opt_p = -1; break; + default: argc = 0; break; + } + } + + if(strcmp(prog, "ftpget") == 0) { + if(argc < 2 || argc > 4) { + fprintf(stderr, "Usage: %s host path [user [pass]]\n", prog); + return(-1); + } + strncpy(host, *argv++, sizeof(host)); + port = 21; + path = *argv++; + if(argc) { + strncpy(user, *argv++, sizeof(user)); + argc++; + } else + *user = '\0'; + if(argc) { + strncpy(pass, *argv++, sizeof(pass)); + argc++; + } else + *pass = '\0'; + s = ftpget(host, port, user, path, path, 'i'); + return(s); + } + if(strcmp(prog, "httpget") == 0) { + if(argc != 2) { + fprintf(stderr, "Usage: %s [-h] [-d] [-p] host path\n", prog); + return(-1); + } + strncpy(host, *argv++, sizeof(host)); + port = 80; + path = *argv++; + s = httpget(host, port, user, path, path, opt_h, opt_d, opt_p); + return(s); + } + + if(argc != 1) { + usage: + fprintf(stderr, "Usage: %s [-h] [-p] url\n", prog); + return(-1); + } + + url = *argv++; + argc--; + + if(strncasecmp(url, "http://", 7) == 0) { + scheme = SCHEME_HTTP; + ps = url + 7; + } else + if(strncasecmp(url, "ftp://", 6) == 0) { + scheme = SCHEME_FTP; + ps = url + 6; + } else + if(strncasecmp(url, "tcp://", 6) == 0) { + scheme = SCHEME_TCP; + ps = url + 6; + } else { + fprintf(stderr, "%s: I do not handle this scheme\n", prog); + return(-1); + } + + user[0] = '\0'; + pass[0] = '\0'; + host[0] = '\0'; + port = 0; + + p = ps; + while(*p && *p != '/') p++; + path = p; + c = *path; + *path = '\0'; + + at = strchr(ps, '@'); + if(at != (char *)NULL) { + *at = '\0'; + p = ps; + while(*p && *p != ':') p++; + if(*p) + *p++ = '\0'; + strcpy(user, ps); + strcpy(pass, p); + ps = at + 1; + } + + *path = c; + p = ps; + while(*p && *p != '/' && *p != ':') p++; + strncpy(host, ps, p - ps); + host[p - ps] = '\0'; + if(*p == ':') { + p++; + ps = p; + while(*p && *p != '/') + port = port * 10 + (*p++ - '0'); + } + if(*p == '/') + path = p; + else + path = "/"; + if(scheme == SCHEME_FTP) { + p = path; + while(*p && *p != ';') p++; + if(*p) { + *p++ = '\0'; + if(strncasecmp(p, "type=", 5) == 0) { + p += 5; + type = tolower(*p); + } + } + } + +#if 0 + fprintf(stderr, "Host: %s\n", host); + fprintf(stderr, "Port: %d\n", port); + fprintf(stderr, "User: %s\n", user); + fprintf(stderr, "Pass: %s\n", pass); + fprintf(stderr, "Path: %s\n", path); + fprintf(stderr, "Type: %c\n", type); +#endif + + switch(scheme) { + case SCHEME_HTTP: + s = httpget(host, port, user, pass, path, opt_h, opt_d, opt_p); + break; + case SCHEME_FTP: + s = ftpget(host, port, user, pass, path, type); + break; + case SCHEME_TCP: + s = tcpget(host, port, user, pass, path); + break; + } + + return(s); +} diff --git a/commands/yap/Makefile b/commands/yap/Makefile new file mode 100755 index 000000000..2268b03dd --- /dev/null +++ b/commands/yap/Makefile @@ -0,0 +1,209 @@ +# $Header$ + +# CFLAGS : options for the C-compiler +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -wa + +# LDFLAGS : flags for the loader +# -i for a PDP-11 with separate I/D (not necessary) +LDFLAGS = -i + +# BINDIR : where the binary will go when you type "make install" +BINDIR = /usr/bin + +# LIBRARIES : -ltermcap or -ltermlib, +# -lPW on USG systems (System III, System V), +# -ljobs on Berkeley Unix system (4.1, 2.8?, 2.9) +LIBRARIES = + +HFILES= assert.h\ + commands.h\ + display.h\ + getcomm.h\ + getline.h\ + help.h\ + in_all.h\ + keys.h\ + machine.h\ + main.h\ + options.h\ + output.h\ + pattern.h\ + process.h\ + prompt.h\ + term.h + +CFILES= assert.c\ + commands.c\ + display.c\ + getcomm.c\ + getline.c\ + help.c\ + keys.c\ + machine.c\ + main.c\ + options.c\ + output.c\ + pattern.c\ + process.c\ + prompt.c\ + term.c + +OFILES= assert.o\ + commands.o\ + display.o\ + getcomm.o\ + getline.o\ + help.o\ + keys.o\ + machine.o\ + main.o\ + options.o\ + output.o\ + pattern.o\ + process.o\ + prompt.o\ + term.o + +all: yap + +yap: $(OFILES) + $(CC) $(LDFLAGS) -o yap $(OFILES) $(LIBRARIES) + install -S 16kw yap + +install: $(BINDIR)/yap $(BINDIR)/more + +$(BINDIR)/yap: yap + install -cs -o bin yap $@ + +$(BINDIR)/more: $(BINDIR)/yap + install -l $? $@ + +clean: + rm -f yap $(OFILES) a.out core *.bak + +pr: + pr Makefile $(HFILES) $(CFILES) + +lint: + lint $(DEFINES) $(CFILES) + +#AUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTOAUTO +assert.o: assert.h +assert.o: in_all.h +assert.o: output.h +assert.o: term.h +commands.o: assert.h +commands.o: commands.h +commands.o: display.h +commands.o: getcomm.h +commands.o: getline.h +commands.o: help.h +commands.o: in_all.h +commands.o: keys.h +commands.o: machine.h +commands.o: main.h +commands.o: options.h +commands.o: output.h +commands.o: pattern.h +commands.o: process.h +commands.o: prompt.h +commands.o: term.h +display.o: assert.h +display.o: display.h +display.o: getline.h +display.o: in_all.h +display.o: machine.h +display.o: main.h +display.o: options.h +display.o: output.h +display.o: process.h +display.o: term.h +getcomm.o: assert.h +getcomm.o: commands.h +getcomm.o: display.h +getcomm.o: getcomm.h +getcomm.o: getline.h +getcomm.o: in_all.h +getcomm.o: keys.h +getcomm.o: machine.h +getcomm.o: main.h +getcomm.o: output.h +getcomm.o: process.h +getcomm.o: prompt.h +getcomm.o: term.h +getline.o: assert.h +getline.o: display.h +getline.o: getline.h +getline.o: in_all.h +getline.o: main.h +getline.o: options.h +getline.o: output.h +getline.o: process.h +getline.o: term.h +help.o: commands.h +help.o: display.h +help.o: help.h +help.o: in_all.h +help.o: keys.h +help.o: machine.h +help.o: main.h +help.o: options.h +help.o: output.h +help.o: prompt.h +help.o: term.h +keys.o: assert.h +keys.o: commands.h +keys.o: in_all.h +keys.o: keys.h +keys.o: machine.h +keys.o: prompt.h +machine.o: assert.h +machine.o: getline.h +machine.o: in_all.h +machine.o: machine.h +main.o: commands.h +main.o: display.h +main.o: in_all.h +main.o: main.h +main.o: options.h +main.o: output.h +main.o: process.h +main.o: prompt.h +main.o: term.h +options.o: display.h +options.o: in_all.h +options.o: options.h +options.o: output.h +output.o: in_all.h +output.o: main.h +output.o: output.h +pattern.o: in_all.h +pattern.o: pattern.h +process.o: commands.h +process.o: display.h +process.o: getline.h +process.o: in_all.h +process.o: main.h +process.o: options.h +process.o: output.h +process.o: process.h +process.o: prompt.h +prompt.o: display.h +prompt.o: getcomm.h +prompt.o: getline.h +prompt.o: in_all.h +prompt.o: main.h +prompt.o: options.h +prompt.o: output.h +prompt.o: process.h +prompt.o: prompt.h +prompt.o: term.h +term.o: display.h +term.o: getline.h +term.o: in_all.h +term.o: keys.h +term.o: machine.h +term.o: main.h +term.o: options.h +term.o: output.h +term.o: term.h diff --git a/commands/yap/NOTICE b/commands/yap/NOTICE new file mode 100755 index 000000000..8c4064cef --- /dev/null +++ b/commands/yap/NOTICE @@ -0,0 +1,21 @@ +$Header$ + +The software and documentation contained within this +directory is copyright (c) Ceriel J.H. Jacobs, 1988. + +Permission is granted to reproduce and distribute +this software as long as no fee is charged and +this notice is included. + +Other rights are reserved except as explicitly +granted by written permission from the author. + + Ceriel J.H. Jacobs + Dept. of Maths and Computer Science + Vrije Universiteit + De Boelelaan 1081 + 1081 HV Amsterdam + The Netherlands + email : + ceriel@cs.vu.nl + diff --git a/commands/yap/READ_ME b/commands/yap/READ_ME new file mode 100755 index 000000000..b820f4432 --- /dev/null +++ b/commands/yap/READ_ME @@ -0,0 +1,41 @@ +$Header$ + +This directory contains the sources of YAP, Yet Another Pager. +It can do most of the things more(1) can, and much much more. + +Yap has been tested on the following systems: + +- DEC PDP 11/44 running V7 +- DEC PDP 11/60 running V7 +- DEC VAX 11/750 running 4.1BSD +- IBM PC/XT running PC/IX +- NCR Minitower running a System V +- Several 68k systems +- SUN-3 and SUN-4 + +On other systems, you might have some problems getting yap to run, but then +again, you might not. You can always ask me for help, and maybe even get it. +If you make any changes, please + - tell me about them + - mark them clearly, preferably through conditional compilation. + +What you need to run yap: + +- you need termlib/termcap. + It probably is'nt too hard to adapt yap to terminfo. + It might even work unchanged, but I don't know about that. We don't have + terminfo or the system V curses. + If you adapt yap to terminfo, I would sure like to get the changes you made, + so please send them to me (email address at the bottom of this note); +- you also need regex(III) (Either Berkeley style or USG will do). + If you do not have it, you should define NOREGEX, which gives you + simpleminded searches without meta-characters. + +How to install yap: + +- edit Makefile (easy) +- edit in_all.h (easy) +- type "make install" + + Ceriel Jacobs, Vrije Universiteit Amsterdam + ceriel@cs.vu.nl diff --git a/commands/yap/assert.c b/commands/yap/assert.c new file mode 100755 index 000000000..d89cb008f --- /dev/null +++ b/commands/yap/assert.c @@ -0,0 +1,34 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif + +# define _ASSERT_ + +# include "in_all.h" +# include "assert.h" +# if DO_ASSERT +# include "output.h" +# include "term.h" + +/* + * Assertion fails. Tell me about it. + */ + +VOID +badassertion(ass,f,l) char *ass, *f; { + + clrbline(); + putline("Assertion \""); + putline(ass); + putline("\" failed "); + putline(f); + putline(", line "); + prnum((long) l); + putline(".\r\n"); + flush(); + resettty(); + abort(); +} +# endif diff --git a/commands/yap/assert.h b/commands/yap/assert.h new file mode 100755 index 000000000..600bd7dfe --- /dev/null +++ b/commands/yap/assert.h @@ -0,0 +1,28 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +/* Assertion macro */ + +# ifndef _ASSERT_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +#if DO_ASSERT +#define assert(x) if(!(x)) badassertion("x",__FILE__,__LINE__) +VOID badassertion(); +/* + * void badassertion(ass,fn,lineno) + * char *ass, The assertion in string form, + * *fn; The filename in which the assertion failed, + * int lineno; The line number of the assertion. + * + * Reports the assertion on standard output and then aborts with a core dump. + */ +#else +#define assert(x) /* nothing */ +#endif + +# undef PUBLIC diff --git a/commands/yap/commands.c b/commands/yap/commands.c new file mode 100755 index 000000000..804044c34 --- /dev/null +++ b/commands/yap/commands.c @@ -0,0 +1,706 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif + +# define _COMMANDS_ + +# include "in_all.h" +# include "commands.h" +# include "output.h" +# include "process.h" +# include "help.h" +# include "term.h" +# include "prompt.h" +# include "getline.h" +# include "getcomm.h" +# include "pattern.h" +# include "display.h" +# include "options.h" +# include "machine.h" +# include "keys.h" +# include "main.h" +# include "assert.h" +# if USG_OPEN +# include <fcntl.h> +# include <errno.h> +extern int errno; +# endif +# if BSD4_2_OPEN +# include <sys/file.h> +# include <errno.h> +extern int errno; +# endif +# if POSIX_OPEN +# include <sys/types.h> +# include <fcntl.h> +# include <errno.h> +# endif + +char *strcpy(), *strcat(); + +static long lastcount; /* Save last count for '.' command */ +static int lastcomm; /* Save last command for '.' command */ + +/*ARGSUSED*/ +STATIC int +do_nocomm(cnt) long cnt; { /* Do nothing */ +} + +/*ARGSUSED*/ +int +do_chkm(cnt) long cnt; { /* Change key map */ + register struct keymap *p; + + if (!(p = othermap)) { + error("No other keymap"); + return; + } + othermap = currmap; + currmap = p; +} + +static int searchdir; /* Direction of last search */ + +/* + * Perform searches + */ + +STATIC VOID +do_search(str,cnt,dir) char *str; long cnt; int dir; { + register char *p; + register long lineno; + + if (str) { + /* + * We have to get a pattern, which we have to prompt for + * with the string "str". + */ + if ((p = readline(str)) == 0) { + /* + * User cancelled command + */ + return; + } + if ((p = re_comp(p))) { + /* + * There was an error in the pattern + */ + error(p); + return; + } + searchdir = dir; + } + if (dir < 0) lineno = scr_info.firstline; + else lineno = scr_info.lastline; + for (;;) { + p = 0; + if ((lineno += dir) > 0) p = getline(lineno, 0); + if (interrupt) return; + if (!p) { /* End of file reached */ + error("pattern not found"); + return; + } + if (re_exec(p) && --cnt <= 0) { + /* + * We found the pattern, and we found it often enough. + * Pity that we still don't know where the match is. + * We only know the linenumber. So, we just hope the + * following will at least bring it on the screen ... + */ + (VOID) display(lineno,0,pagesize,0); + (VOID) scrollb(2,0); + redraw(0); + return; + } + } + /* NOTREACHED */ +} + +STATIC int +do_fsearch(cnt) long cnt; { /* Forward search */ + + do_search("/", cnt, 1); +} + +STATIC int +do_bsearch(cnt) long cnt; { /* Backward search */ + + do_search("?", cnt, -1); +} + +/* + * Repeat last search in direction "dir" + */ + +STATIC int +n_or_rn_search(cnt,dir) long cnt; int dir; { + register char *p; + + if (dir == 1) { + p = "/\r"; + } + else if (dir == -1) { + p = "?\r"; + } + else { + error("No previous pattern"); + return; + } + if (!stupid) clrbline(); + putline(p); + flush(); + do_search((char *) 0, cnt, dir); +} + +STATIC int +do_nsearch(cnt) long cnt; { /* Repeat search in same direction */ + + n_or_rn_search(cnt,searchdir); +} + +STATIC int +do_rnsearch(cnt) long cnt; { /* Repeat search in opposite direction */ + + n_or_rn_search(cnt, -searchdir); +} + +STATIC int shell(esc_ch, cnt) long cnt; +{ + register char *p; + static char buf[2]; + + buf[0] = esc_ch; + if (p = readline(buf)) { + shellescape(p, esc_ch); + if (cnt >= 0 && !hardcopy) { + p = startcomm; + startcomm = 0; + ret_to_continue(); + putline(TI); + if (!p) { + /* + * Avoid double redraw. + * After a "startcomm", a redraw will + * take place anyway. + */ + redraw(1); + } + } + } +} + +STATIC int +do_shell(cnt) long cnt; { /* Execute a shell escape */ + shell('!', cnt); +} + +STATIC int +do_pipe(cnt) long cnt; { /* Execute a shell escape */ + shell('|', cnt); +} + +/*ARGSUSED*/ +STATIC int +do_writefile(cnt) long cnt; { /* Write input to a file */ + register char *p; + int fd; + + if ((p = readline("Filename: ")) == 0 || !*p) { + /* + * No file name given + */ + return; + } +# if USG_OPEN || BSD4_2_OPEN || POSIX_OPEN + if ((fd = open(p,O_CREAT|O_EXCL|O_WRONLY,0644)) < 0) { + if (errno == EEXIST) { + error("File exists"); + return; + } + error("Could not open file"); + return; + } +# else + if (!access(p,0)) { + error("File exists"); + return; + } + if ((fd = creat(p,0644)) < 0) { + error("Could not open file"); + return; + } +# endif + wrt_fd(fd); + (VOID) close(fd); +} + +VOID +wrt_fd(fd) +{ + register long l = 1; + register char *p = getline(l,0), *pbuf; + char buf[1024]; + + while (p) { + pbuf = buf; + while (p && pbuf < &buf[1024]) { + if (!*p) { + *pbuf++ = '\n'; + p = getline(++l,0); + } + else *pbuf++ = *p++ & 0177; + } + if (write(fd, buf, pbuf - buf) < 0) { + error("Write failed"); + break; + } + } +} + +STATIC int +do_absolute(cnt) long cnt; { /* Go to linenumber "cnt" */ + + if (!getline(cnt,0)) { /* Not there or interrupt */ + if (!interrupt) { + /* + * User did'nt give an interrupt, so the line number + * was too high. Go to the last line. + */ + do_lline(cnt); + } + return; + } + (VOID) display(cnt,0,pagesize,1); +} + +/*ARGSUSED*/ +STATIC int +do_visit(cnt) long cnt; { /* Visit a file */ + register char *p; + static char fn[128]; /* Keep file name */ + + if ((p = readline("Filename: ")) == 0) { + return; + } + if (*p) { + (VOID) strcpy(fn,p); + visitfile(fn); + } + else { + /* + * User typed a return. Visit the current file + */ + if (!(p = filenames[filecount])) { + error("No current file"); + return; + } + visitfile(p); + } + (VOID) display(1L, 0, pagesize, 1); +} + +/*ARGSUSED*/ +STATIC int +do_error(cnt) long cnt; { /* Called when user types wrong key sequence */ + + error(currmap->k_help); +} + +/* + * Interface routine for displaying previous screen, + * depending on cflag. + */ + +STATIC int +prev_screen(sz,really) int sz, really; { + register int retval; + + retval = scrollb(sz - 1, really && cflag); + if (really && !cflag) { + /* + * The previous call did not display anything, but at least we + * know where to start + */ + return display(scr_info.firstline, scr_info.nf, sz, 1); + } + return retval; +} + +/* + * Interface routine for displaying the next screen, + * dependent on cflag. + */ + +STATIC int +next_screen(sz,really) int sz, really; { + + register int t; + register struct scr_info *p = &scr_info; + + if (cflag) { + return scrollf(sz-1,really); + } + t = p->tail->cnt - 1; + if (p->lastline == p->firstline) { + t += p->nf; + } + return display(p->lastline, t, sz, really); +} + +/*ARGSUSED*/ +STATIC int +do_redraw(cnt) long cnt; { + + redraw(1); +} + +STATIC int +page_size(cnt) unsigned cnt; { + + if (cnt) { + if (cnt > maxpagesize) return maxpagesize; + if (cnt < MINPAGESIZE) return MINPAGESIZE; + return (int) cnt; + } + return pagesize; +} + +STATIC int +do_forward(cnt) long cnt; { /* Display next page */ + register int i; + + i = page_size((unsigned) cnt); + if (status & EOFILE) { + /* + * May seem strange, but actually a visit to the next file + * has already been done here + */ + (VOID) display(1L,0,i,1); + return; + } + (VOID) next_screen(i,1); +} + +STATIC int +do_backward(cnt) long cnt; { + register int i, temp; + + i = page_size((unsigned) cnt); + if (!(status & START)) { + (VOID) prev_screen(i,1); + return; + } + if (stdf < 0) { + (VOID) display(1L,0,i,1); + return; + } + /* + * The next part is a bit clumsy. + * We want to display the last page of the previous file (for which + * a visit has already been done), but the pagesize may temporarily + * be different because the command had a count + */ + temp = pagesize; + pagesize = i; + do_lline(cnt); + pagesize = temp; +} + +/*ARGSUSED*/ +STATIC int +do_firstline(cnt) long cnt; { /* Go to start of input */ + + do_absolute(1L); +} + +STATIC int +do_lline(cnt) long cnt; { /* Go to end of input */ + register int i = 0; + register int j = pagesize - 1; + + if ((cnt = to_lastline()) < 0) { + /* + * Interrupted by the user + */ + return; + } + /* + * Display the page such that only the last line of the page is + * a "~", independant of the pagesize + */ + while (!(display(cnt,i,j,0) & EOFILE)) { + /* + * The last line could of course be very long ... + */ + i+= j; + } + (VOID) scrollb(j - scr_info.tail->cnt, 0); + redraw(0); +} + +STATIC int +do_lf(cnt) long cnt; { /* Display next line, or go to line */ + + if (cnt) { /* Go to line */ + do_absolute(cnt); + return; + } + (VOID) scrollf(1,1); +} + +STATIC int +do_upline(cnt) long cnt; { /* Display previous line, or go to line */ + + if (cnt) { /* Go to line */ + do_absolute(cnt); + return; + } + (VOID) scrollb(1,1); +} + +STATIC int +do_skiplines(cnt) long cnt; { /* Skip lines forwards */ + + /* Should be interruptable ... */ + (VOID) scrollf((int) (cnt + maxpagesize - 1), 0); + redraw(0); +} + +STATIC int +do_bskiplines(cnt) long cnt; { /* Skip lines backwards */ + + /* Should be interruptable ... */ + (VOID) scrollb((int) (cnt + pagesize - 1), 0); + redraw(0); +} + +STATIC int +do_fscreens(cnt) long cnt; { /* Skip screens forwards */ + + do { + if ((next_screen(pagesize,0) & EOFILE) || interrupt) break; + } while (--cnt >= 0); + redraw(0); +} + +STATIC int +do_bscreens(cnt) long cnt; { /* Skip screens backwards */ + + do { + if ((prev_screen(pagesize,0) & START) || interrupt) break; + } while (--cnt >= 0); + redraw(0); +} + +STATIC int +scro_size(cnt) unsigned cnt; { + + if (cnt >= maxpagesize) return maxpagesize; + if (cnt) return (int) cnt; + return scrollsize; +} + +STATIC int +do_f_scroll(cnt) long cnt; { /* Scroll forwards */ + + (VOID) scrollf(scro_size((unsigned) cnt),1); +} + +STATIC int +do_b_scroll(cnt) long cnt; { /* Scroll backwards */ + + (VOID) scrollb(scro_size((unsigned) cnt),1); +} + +STATIC int +do_previousfile(cnt) long cnt; {/* Visit previous file */ + + if (nextfile(- (int) cnt)) { + error("No (Nth) previous file"); + return; + } + redraw(0); +} + +STATIC int +do_nextfile(cnt) long cnt; { /* Visit next file */ + + if (nextfile((int) cnt)) { + error("No (Nth) next file"); + return; + } + redraw(0); +} + +STATIC int do_lcomm(); + +/* + * The next array is initialized, sorted on the first element of the structs, + * so that we can perform binary search + */ +struct commands commands[] = { +{"", 0, do_error, ""}, +{"", 0, do_nocomm, ""}, +{"bf", STICKY|NEEDS_COUNT, + do_previousfile,"Visit previous file"}, +{"bl", NEEDS_SCREEN|STICKY, + do_upline, "Scroll one line up, or go to line"}, +{"bot", STICKY, + do_lline, "Go to last line of the input"}, +{"bp", BACK|NEEDS_SCREEN|TOPREVFILE|STICKY, + do_backward, "display previous page"}, +{"bps", SCREENSIZE_ADAPT|BACK|NEEDS_SCREEN|TOPREVFILE|STICKY, + do_backward, "Display previous page, set pagesize"}, +{"bs", BACK|NEEDS_SCREEN|STICKY, + do_b_scroll, "Scroll backwards"}, +{"bse", 0, do_bsearch, "Search backwards for pattern"}, +{"bsl", BACK|NEEDS_SCREEN|STICKY|NEEDS_COUNT, + do_bskiplines, "Skip lines backwards"}, +{"bsp", BACK|NEEDS_SCREEN|STICKY|NEEDS_COUNT, + do_bscreens, "Skip screens backwards"}, +{"bss", SCROLLSIZE_ADAPT|BACK|NEEDS_SCREEN|STICKY, + do_b_scroll, "Scroll backwards, set scrollsize"}, +{"chm", 0, do_chkm, "Switch to other keymap"}, +{"exg", STICKY, exgmark, "Exchange current page with mark"}, +{"ff", STICKY|NEEDS_COUNT, + do_nextfile, "Visit next file"}, +{"fl", NEEDS_SCREEN|STICKY, + do_lf, "Scroll one line down, or go to line"}, +{"fp", TONEXTFILE|AHEAD|STICKY, + do_forward, "Display next page"}, +{"fps", SCREENSIZE_ADAPT|TONEXTFILE|AHEAD|STICKY, + do_forward, "Display next page, set pagesize"}, +{"fs", AHEAD|NEEDS_SCREEN|STICKY, + do_f_scroll, "Scroll forwards"}, +{"fse", 0, do_fsearch, "Search forwards for pattern"}, +{"fsl", AHEAD|NEEDS_SCREEN|STICKY|NEEDS_COUNT, + do_skiplines, "Skip lines forwards"}, +{"fsp", AHEAD|NEEDS_SCREEN|STICKY|NEEDS_COUNT, + do_fscreens, "Skip screens forwards"}, +{"fss", SCROLLSIZE_ADAPT|AHEAD|NEEDS_SCREEN|STICKY, + do_f_scroll, "Scroll forwards, set scrollsize"}, +{"hlp", 0, do_help, "Give description of all commands"}, +{"mar", 0, setmark, "Set a mark on the current page"}, +{"nse", STICKY, do_nsearch, "Repeat the last search"}, +{"nsr", STICKY, do_rnsearch, "Repeat last search in other direction"}, +{"pip", ESC, do_pipe, "pipe input into shell command"}, +{"qui", 0, quit, "Exit from yap"}, +{"red", 0, do_redraw, "Redraw screen"}, +{"rep", 0, do_lcomm, "Repeat last command"}, +{"shl", ESC, do_shell, "Execute a shell escape"}, +{"tom", 0, tomark, "Go to mark"}, +{"top", STICKY, do_firstline, "Go to the first line of the input"}, +{"vis", 0, do_visit, "Visit a file"}, +{"wrf", 0, do_writefile, "Write input to a file"}, +}; + +/* + * Lookup string "s" in the commands array, and return index. + * return 0 if not found. + */ + +int +lookup(s) char *s; { + register struct commands *l, *u, *m; + + l = &commands[2]; + u = &commands[sizeof(commands) / sizeof(*u) - 1]; + do { + /* + * Perform binary search + */ + m = l + (u - l) / 2; + if (strcmp(s, m->c_cmd) > 0) l = m + 1; + else u = m; + } while (l < u); + if (!strcmp(s, u->c_cmd)) return u - commands; + return 0; +} + +/*ARGSUSED*/ +STATIC int +do_lcomm(cnt) long cnt; { /* Repeat last command */ + + if (!lastcomm) { + error("No previous command"); + return; + } + do_comm(lastcomm, lastcount); +} + +/* + * Execute a command, with optional count "count". + */ + +VOID +do_comm(comm, count) register int comm; register long count; { + + register struct commands *pcomm; + register int temp; + register int flags; + + pcomm = &commands[comm]; + flags = pcomm->c_flags; + + /* + * Check the command. + * If the last line of the file is displayed and the command goes + * forwards and does'nt have the ability to go to the next file, it + * is an error. + * If the first line of the file is displayed and the command goes + * backwards and does'nt have the ability to go to the previous file, + * it is an error. + * Also check wether we need the next or previous file. If so, get it. + */ + if ((status & EOFILE) && (flags & AHEAD)) { + if (qflag || !(flags & TONEXTFILE)) return; + if (nextfile(1)) quit(); + } + if ((status & START) && (flags & BACK)) { + if (qflag || !(flags & TOPREVFILE)) return; + if (nextfile(-1)) quit(); + } + /* + * Does the command stick around for LASTCOMM? + */ + if (flags & STICKY) { + lastcomm = comm; + lastcount = count; + } + if (!count) { + if (flags & NEEDS_COUNT) count = 1; + } + else { + /* + * Does the command adapt the screensize? + */ + if (flags & SCREENSIZE_ADAPT) { + temp = maxpagesize; + if ((unsigned) count < temp) { + temp = count; + } + if (temp < MINPAGESIZE) { + temp = MINPAGESIZE; + } + count = 0; + pagesize = temp; + } + /* + * Does the command adapt the scrollsize? + */ + if (flags & SCROLLSIZE_ADAPT) { + temp = maxpagesize - 1; + if ((unsigned) count < temp) { + temp = (int) count; + } + scrollsize = temp; + count = 0; + } + } + /* + * Now execute the command. + */ + (*(pcomm->c_func))(count); +} diff --git a/commands/yap/commands.h b/commands/yap/commands.h new file mode 100755 index 000000000..73df1ca1e --- /dev/null +++ b/commands/yap/commands.h @@ -0,0 +1,64 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +# ifndef _COMMANDS_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +/* Flags, describing properties of commands */ + +# define SCREENSIZE_ADAPT 01 /* Can change screen size */ +# define SCROLLSIZE_ADAPT 02 /* Can change scrollsize */ +# define TONEXTFILE 04 /* to next file */ +# define AHEAD 010 /* goes ahead in the file */ +# define BACK 020 /* goes back in the file */ +# define NEEDS_SCREEN 040 /* needs screen info */ +# define TOPREVFILE 0100 /* to previous file */ +# define STICKY 0200 /* remember for lastcomm */ +# define NEEDS_COUNT 0400 /* command needs a count */ +# define ESC 01000 /* shell or pipe escape */ + +extern struct commands { + char *c_cmd; /* Short mnemonic for a command */ + int c_flags; /* describes command properties */ + int (*c_func)(); /* Function executing the command */ + char *c_descr; /* Short command description */ +} commands[]; + +int do_chkm(); +/* + * int do_chkm(cnt) + * long cnt; Ignored + * + * Switch to other keymap + */ + +VOID do_comm(); +/* + * void do_comm(command, count) + * int command; Index in the commands array + * long count; Some commands possibly take a count + * + * Checks and executes commands. + */ + +int lookup(); +/* + * int lookup(str) + * char *str; + * + * Searches the string "str" in the command list. + * It returns its index if it exists, otherwise it returns 0. + */ + +VOID wrt_fd(); +/* + * void wrt_fd(fd) + * int fd; File descriptor + * + * Write input to file descriptor fd + */ +# undef PUBLIC diff --git a/commands/yap/display.c b/commands/yap/display.c new file mode 100755 index 000000000..47bf289a8 --- /dev/null +++ b/commands/yap/display.c @@ -0,0 +1,547 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif + +# define _DISPLAY_ + +# include "in_all.h" +# include "display.h" +# include "assert.h" +# include "machine.h" +# include "term.h" +# include "output.h" +# include "options.h" +# include "process.h" +# include "getline.h" +# include "main.h" + +STATIC char * do_line(); + +/* + * Fill n lines of the screen, each with "str". + */ + +STATIC VOID +fillscr(n,str) char *str; int n; { + + while (n-- > 0) { + putline(str); + } +} + +/* + * Skip "n" screenlines of line "p", and return what's left of it. + */ + +STATIC char * +skiplines(p,n) char *p; int n; { + + while (n-- > 0) { + p = do_line(p,0); + scr_info.currentpos--; + } + return p; +} + +/* + * Redraw screen. + * "n" = 1 if it is a real redraw, 0 if one page must be displayed. + * It is also called when yap receives a stop signal. + */ + +VOID +redraw(n) int n; { + register struct scr_info *p = &scr_info; + register int i; + + i = pagesize; + if (n && p->currentpos) { + i = p->currentpos; + } + (VOID) display(p->firstline,p->nf,i,1); +} + +/* + * Compute return value for the routines "display" and "scrollf". + * This return value indicates wether we are at the end of file + * or at the start, or both. + * "s" contains that part of the last line that was not displayed. + */ + +STATIC int +compretval(s) char *s; { + register int i; + register struct scr_info *p = &scr_info; + + i = 0; + if (!s || (!*s && !getline(p->lastline+1, 1))) { + i = EOFILE; + } + if (p->firstline == 1 && !p->nf) { + i |= START; + } + status = i; + return i; +} + +/* + * Display nlines, starting at line n, not displaying the first + * nd screenlines of n. + * If reallydispl = 0, the actual displaying is not performed, + * only the computing associated with it is done. + */ + +int +display(n,nd,nlines,reallydispl) + long n; int nd; register int nlines; int reallydispl; { + + register struct scr_info *s = &scr_info; + register char *p; /* pointer to line to be displayed */ + + if (startcomm) { /* No displaying on a command from the + * yap command line. In this case, displaying + * will be done after executing the command, + * by a redraw. + */ + reallydispl = 0; + } + if (!n) { + n = 1L; + nd = 0; + } + if (reallydispl) { /* move cursor to starting point */ + if (stupid) { + putline(currentfile); + putline(", line "); + prnum(n); + nlines--; + } + if (cflag) { + putline("\r\n"); + } + else { + home(); + clrscreen(); + } + } + /* + * Now, do computations and display + */ + s->currentpos = 0; + s->nf = nd; + s->head = s->tail; + s->tail->cnt = 0; + s->tail->line = n; + p = skiplines(getline(n,1),nd); + while (nlines && p) { + /* + * While there is room, + * and there is something left to display ... + */ + (s->tail->cnt)++; + nlines--; + if (*(p = do_line(p,reallydispl)) == '\0') { + /* + * File-line finished, get next one ... + */ + p = getline(++n,1); + if (nlines && p) { + s->tail = s->tail->next; + s->tail->cnt = 0; + s->tail->line = n; + } + } + } + if (!stupid) { + s->currentpos += nlines; + if (reallydispl) { + fillscr(nlines, "~\r\n"); + fillscr(maxpagesize - s->currentpos, "\r\n"); + } + } + return compretval(p); +} + +/* + * Scroll forwards n lines. + */ + +int +scrollf(n,reallydispl) int n; int reallydispl; { + + register struct scr_info *s = &scr_info; + register char *p; + register long ll; + register int i; + + /* + * First, find out how many screenlines of the last line were already + * on the screen, and possibly above it. + */ + + if (n <= 0 || (status & EOFILE)) return status; + if (startcomm) reallydispl = 0; + /* + * Find out where to begin displaying + */ + i = s->tail->cnt; + if ((ll = s->lastline) == s->firstline) i += s->nf; + p = skiplines(getline(ll, 1), i); + /* + * Now, place the cursor at the first free line + */ + if (reallydispl && !stupid) { + clrbline(); + mgoto(s->currentpos); + } + /* + * Now display lines, keeping track of which lines are on the screen. + */ + while (n-- > 0) { /* There are still rows to be displayed */ + if (!*p) { /* End of line, get next one */ + if (!(p = getline(++ll, 1))) { + /* + * No lines left. At end of file + */ + break; + } + s->tail = s->tail->next; + s->tail->cnt = 0; + s->tail->line = ll; + } + if (s->currentpos >= maxpagesize) { + /* + * No room, delete first screen-line + */ + s->currentpos--; + s->nf++; + if (--(s->head->cnt) == 0) { + /* + * The first file-line on the screen is wiped + * out completely; update administration + * accordingly. + */ + s->nf = 0; + s->head = s->head->next; + assert(s->head->cnt > 0); + } + } + s->tail->cnt++; + p = do_line(p, reallydispl); + } + return compretval(p); +} + +/* + * Scroll back n lines + */ + +int +scrollb(n, reallydispl) int n, reallydispl; { + + register struct scr_info *s = &scr_info; + register char *p; /* Holds string to be displayed */ + register int i; + register int count; + register long ln; /* a line number */ + register int nodispl; + int cannotscroll; /* stupid or no insert-line */ + + /* + * First, find out where to start + */ + if ((count = n) <= 0 || (status & START)) return status; + if (startcomm) reallydispl = 0; + cannotscroll = stupid || (!*AL && !*SR); + ln = s->firstline; + nodispl = s->nf; + while (count) { /* While scrolling back ... */ + if (i = nodispl) { + /* + * There were screen-lines of s->firstline that were not + * displayed. + * We can use them now, but only "count" of them. + */ + if (i > count) i = count; + s->currentpos += i; + nodispl -= i; + count -= i; + } + else { /* Get previous line */ + if (ln == 1) break; /* isn't there ... */ + p = getline(--ln, 1); + /* + * Make it the first line of the screen and compute + * how many screenlines it takes. These lines are not + * displayed, but nodispl is set to this count, so + * that it will be nonzero next time around + */ + nodispl = 0; + do { /* Find out how many screenlines */ + nodispl++; + p = skiplines(p, 1); + } while (*p); + } + } + n -= count; + if ((i = s->currentpos) > maxpagesize) i = maxpagesize; + if (reallydispl && hardcopy) i = n; + /* + * Now that we know where to start, we can use "display" to do the + * rest of the computing for us, and maybe even the displaying ... + */ + i = display(ln, + nodispl, + i, + reallydispl && cannotscroll); + if (cannotscroll || !reallydispl) { + /* + * Yes, "display" did the displaying, or we did'nt have to + * display at all. + * I like it, but the user obviously does not. + * Let him buy another (smarter) terminal ... + */ + return i; + } + /* + * Now, all we have to do is the displaying. And we are dealing with + * a smart terminal (it can insert lines or scroll back). + */ + home(); + /* + * Insert lines all at once + */ + for (i = n; i; i--) { + if (DB && *CE) { + /* + * Grumble..., terminal retains lines below, so we have + * to clear the lines that we push off the screen + */ + clrbline(); + home(); + } + if (*SR) { + scrollreverse(); + } + else { +# ifdef VT100_PATCH + insert_line(0); +# else + insert_line(); +# endif + } + } + p = skiplines(getline(ln = s->firstline, 1), s->nf); + for (i = 0; i < n; i++) { + p = do_line(p,1); + s->currentpos--; + if (!*p) { + p = getline(++ln, 1); + } + } + return count; +} + +/* + * Process a line. + * If reallydispl > 0 then display it. + */ + +STATIC char * +do_line(str, reallydispl) register char *str; int reallydispl; { + + char buf[1024]; + register char *p = buf; + register int pos = COLS; + register int c; + register int c1; + register int do_ul = 0, do_hl = 0; + int lastmode = 0, lasthlmode = 0; + int c2; + + while (*str && pos > 0) { + if (*str < ' ' && (c1 = match(str,&c2,sppat)) > 0) { + /* + * We found a string that matches, and thus must be + * echoed literally + */ + if ((pos - c2) <= 0) { + /* + * It did not fit + */ + break; + } + pos -= c2; + str += c1; + if (reallydispl) { + c = *str; + *p = *str = 0; + cputline(p = buf); + putline(str - c1); + *str = c; + } + continue; + } + c = *str++; + do_hl = 0; + if (*str == '\b' && *(str+1) != 0 + && (c != '_' || *(str+2) == '\b')) { + while (*str == '\b' && *(str+1) != 0) { + str++; + c = *str++; + do_hl = 1; + } + } + do_ul = 1; + /* + * Find underline sequences ... + */ + if (c == '_' && *str == '\b') { + str++; + c = *str++; + } + else { + if (*str == '\b' && *(str+1) == '_') { + str += 2; + } + else do_ul = 0; + } + if (reallydispl && do_hl != lasthlmode) { + *p = 0; + cputline(p = buf); + if (do_hl) bold(); + else end_bold(); + } + lasthlmode = do_hl; + if (reallydispl && do_ul != lastmode) { + *p = 0; + cputline(p = buf); + if (do_ul) underline(); + else end_underline(); + } + lastmode = do_ul; + *p++ = c; + if (c >= ' ' && c < 0177) { + pos--; + if (reallydispl && do_ul && *UC && pos > 0) { + /* + * Underlining apparently is done one + * character at a time. + */ + *p = 0; + cputline(p = buf); + backspace(); + underchar(); + } + continue; + } + if (c == '\t') { + p--; + c1 = 8 - ((COLS - pos) & 07); + /* + * Actually, if COLS is a multiple of 8, this can be + * simplified to + * c1 = pos & 07; + * But of course, we don't know that for sure. + */ + if (pos - c1 < 0) break; + pos -= c1; + if (reallydispl) { + if (expandtabs) { + /* + * Expand tabs. We cannot let the + * kernel take care of this + * for two reasons: + * 1. There can be tabs in cursor + * addressing strings, + * 2. We probably do it better. + */ + while (c1-- > 0) { + *p++ = ' '; + } + } + else { + *p = 0; + cputline(p = buf); + givetab(); + } + } + continue; + } + /* + * Now we have a control character, which takes two positions + */ + if (pos <= 1) { + p--; + break; + } + pos -= 2; + } + if (reallydispl) { + *p = 0; + cputline(buf); + if (pos > 0 || (pos <= 0 && (!AM || XN))) { + putline("\r\n"); + } + /* + * The next should be here! I.e. it may not be before printing + * the newline. This has to do with XN. We don't know exactly + * WHEN the terminal will stop ignoring the newline. + * I have for example a terminal (Ampex a230) that will + * continue to ignore the newline after a clear to end of line + * sequence, but not after an end_underline sequence. + */ + if (do_ul) { + end_underline(); + } + if (do_hl) { + standend(); + } + } + scr_info.currentpos++; + return str; +} + +/* ARGSUSED */ +int +setmark(cnt) long cnt; { /* Set a mark on the current page */ + register struct scr_info *p = &scr_info; + + p->savfirst = p->firstline; + p->savnf = p->nf; +} + +/* ARGSUSED */ +int +tomark(cnt) long cnt; { /* Go to the mark */ + register struct scr_info *p = &scr_info; + + (VOID) display(p->savfirst,p->savnf,pagesize,1); +} + +/* ARGSUSED */ +int +exgmark(cnt) long cnt; { /* Exchange mark and current page */ + register struct scr_info *p = &scr_info; + register long svfirst; + register int svnf; + + svfirst = p->firstline; + svnf = p->nf; + tomark(0L); + p->savfirst = svfirst; + p->savnf = svnf; +} + +VOID +d_clean() { /* Clean up */ + register struct scr_info *p = &scr_info; + + p->savnf = 0; + p->savfirst = 0; + p->head = p->tail; + p->head->line = 0; + p->currentpos = 0; +} diff --git a/commands/yap/display.h b/commands/yap/display.h new file mode 100755 index 000000000..d917074df --- /dev/null +++ b/commands/yap/display.h @@ -0,0 +1,124 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +# ifndef _DISPLAY_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +# define MINPAGESIZE 5 + +PUBLIC int pagesize; /* How many lines on a page */ +PUBLIC int maxpagesize; /* Maximum # of lines on a page */ +PUBLIC int scrollsize; /* number of lines in a scroll */ +struct scr_info { + struct linelist { + int cnt; /* # of screenlines for this line */ + long line; /* lineno of this line */ +# define firstline head->line +# define lastline tail->line + struct linelist *next; + struct linelist *prev; + } *tail, *head; /* Of all lines of the input file that are + * on the screen, remember how many + * screenlines they occupy. Keep this + * info in a doubly linked list. + */ + int nf; /* How many screenlines of the first line + * on the screen are not on the screen? + */ + int currentpos; /* Y coordinate of first free line */ + struct linelist ssaavv; /* Mark */ +# define savfirst ssaavv.line +# define savnf ssaavv.cnt +}; + +PUBLIC struct scr_info scr_info; +PUBLIC int status; /* EOFILE on end of file + * START on start of file + * logical "or" if both + */ +/* Flags for status field */ + +# define EOFILE 01 +# define START 02 + +VOID redraw(); +/* + * void redraw(flag) + * int flag; Either 0 or 1 + * + * Redraws the screen. If flag = 1, the screen is redrawn as precisely + * as possible, otherwise one page is displayed (which possibly does not + * take a whole screen. + */ + +int display(); +/* + * int display(firstline, nodispl, nlines, really) + * long firstline; Line with which to start + * int nodispl; Do not display nodispl lines of it + * int nlines; Number of screen lines that must be displayed + * int really; Either 0 or 1 + * + * Displays nlines as a page. if "really" = 0, the actual displaying is not + * performed. Only the computing associated with it is done. + */ + +int scrollf(); +/* + * int scrollf(nlines,really) + * int nlines; Number of lines to scroll + * int really; Either 0 or 1, see explanation above + * + * Scroll forwards "nlines" (screen)lines. + */ + +int scrollb(); +/* + * int scrollb(nlines,really) + * int nlines; Number of lines to scroll + * int really; Either 0 or 1, see explanation above + * + * Scroll backwards "nlines" (screen)lines. + */ + +int tomark(); +/* + * int tomark(cnt) + * long cnt; (Argument ignored) + * + * Display a page starting at the mark. If there was no + * mark, display the first page of the file. + * (There is always assumed to be a mark, the initial one is on the first page + * of the file). + */ + +int setmark(); +/* + * int setmark(cnt) + * long cnt; (Argument ignored) + * + * Sets a mark on the current page. + * It returns nothing (but the address is taken ...) + */ + +int exgmark(); +/* + * int exgmark(cnt) + * long cnt; (Argumewnt ignored) + * + * Sets the mark on the current page and displays the + * previously marked page. + */ + +VOID d_clean(); +/* + * void d_clean() + * + * Clean up and initialize. To be called before displaying a new file + */ + +# undef PUBLIC diff --git a/commands/yap/getcomm.c b/commands/yap/getcomm.c new file mode 100755 index 000000000..e6879d427 --- /dev/null +++ b/commands/yap/getcomm.c @@ -0,0 +1,313 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* + * Command reader, also executes shell escapes + */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif + +# define _GETCOMM_ + +# include <ctype.h> +# include "in_all.h" +# include "term.h" +# include "process.h" +# include "getcomm.h" +# include "commands.h" +# include "prompt.h" +# include "main.h" +# include "output.h" +# include "getline.h" +# include "machine.h" +# include "keys.h" +# include "display.h" +# include "assert.h" + +#if USG_OPEN +#include <fcntl.h> +#endif +#if POSIX_OPEN +#include <sys/types.h> +#include <fcntl.h> +#endif + +char *strcpy(), + *getenv(); + +STATIC int killchar(); + +/* + * Read a line from the terminal, doing line editing. + * The parameter s contains the prompt for the line. + */ + +char * +readline(s) char *s; { + + static char buf[80]; + register char *p = buf; + register int ch; + register int pos; + + clrbline(); + putline(s); + pos = strlen(s); + while ((ch = getch()) != '\n' && ch != '\r') { + if (ch == -1) { + /* + * Can only occur because of an interrupted read. + */ + ch = erasech; + interrupt = 0; + } + if (ch == erasech) { + /* + * Erase last char + */ + if (p == buf) { + /* + * There was none, so return + */ + return (char *) 0; + } + pos -= killchar(*--p); + if (*p != '\\') continue; + } + if (ch == killch) { + /* + * Erase the whole line + */ + if (!(p > buf && *(p-1) == '\\')) { + while (p > buf) { + pos -= killchar(*--p); + } + continue; + } + pos -= killchar(*--p); + } + if (p > &buf[78] || pos >= COLS - 2) { + /* + * Line does not fit. + * Simply refuse to make it any longer + */ + pos -= killchar(*--p); + } + *p++ = ch; + if (ch < ' ' || ch >= 0177) { + fputch('^'); + pos++; + ch ^= 0100; + } + fputch(ch); + pos++; + } + fputch('\r'); + *p++ = '\0'; + flush(); + return buf; +} + +/* + * Erase a character from the command line. + */ + +STATIC int +killchar(c) { + + backspace(); + putch(' '); + backspace(); + if (c < ' ' || c >= 0177) { + (VOID) killchar(' '); + return 2; + } + return 1; +} + +/* + * Do a shell escape, after expanding '%' and '!'. + */ + +VOID +shellescape(p, esc_char) register char *p; { + + register char *p2; /* walks through command */ + register int id; /* procid of child */ + register int cnt; /* prevent array bound errors */ + register int lastc = 0; /* will contain the previous char */ +# ifdef SIGTSTP + VOID (*savetstp)(); +# endif + static char previous[256]; /* previous command */ + char comm[256]; /* space for command */ + int piped[2]; + + p2 = comm; + *p2++ = esc_char; + cnt = 253; + while (*p) { + /* + * expand command + */ + switch(*p++) { + case '!': + /* + * An unescaped ! expands to the previous + * command, but disappears if there is none + */ + if (lastc != '\\') { + if (*previous) { + id = strlen(previous); + if ((cnt -= id) <= 0) break; + (VOID) strcpy(p2,previous); + p2 += id; + } + } + else { + *(p2-1) = '!'; + } + continue; + case '%': + /* + * An unescaped % will expand to the current + * filename, but disappears is there is none + */ + if (lastc != '\\') { + if (nopipe) { + id = strlen(currentfile); + if ((cnt -= id) <= 0) break; + (VOID) strcpy(p2,currentfile); + p2 += id; + } + } + else { + *(p2-1) = '%'; + } + continue; + default: + lastc = *(p-1); + if (cnt-- <= 0) break; + *p2++ = lastc; + continue; + } + break; + } + clrbline(); + *p2 = '\0'; + if (!stupid) { + /* + * Display expanded command + */ + cputline(comm); + putline("\r\n"); + } + flush(); + (VOID) strcpy(previous,comm + 1); + resettty(); + if (esc_char == '|' && pipe(piped) < 0) { + error("Cannot create pipe"); + return; + } + if ((id = fork()) < 0) { + error("Cannot fork"); + return; + } + if (id == 0) { + /* + * Close files, as child might need the file descriptors + */ + cls_files(); + if (esc_char == '|') { + close(piped[1]); +#if USG_OPEN || POSIX_OPEN + close(0); + fcntl(piped[0], F_DUPFD, 0); +#else + dup2(piped[0], 0); +#endif + close(piped[0]); + } + execl("/bin/sh", "sh", "-c", comm + 1, (char *) 0); + exit(1); + } + (VOID) signal(SIGINT,SIG_IGN); + (VOID) signal(SIGQUIT,SIG_IGN); +# ifdef SIGTSTP + if ((savetstp = signal(SIGTSTP,SIG_IGN)) != SIG_IGN) { + (VOID) signal(SIGTSTP,SIG_DFL); + } +# endif + if (esc_char == '|') { + (VOID) close(piped[0]); + (VOID) signal(SIGPIPE, SIG_IGN); + wrt_fd(piped[1]); + (VOID) close(piped[1]); + } + while ((lastc = wait((int *) 0)) != id && lastc >= 0) { + /* + * Wait for child, making sure it is the one we expected ... + */ + } + (VOID) signal(SIGINT,catchdel); + (VOID) signal(SIGQUIT,quit); +# ifdef SIGTSTP + (VOID) signal(SIGTSTP, savetstp); +# endif + inittty(); +} + +/* + * Get all those commands ... + */ + +int +getcomm (plong) long *plong; { + int c; + long count; + char *p; + int i; + int j; + char buf[10]; + + for (;;) { + count = 0; + give_prompt(); + while (isdigit((c = getch()))) { + count = count * 10 + (c - '0'); + } + *plong = count; + p = buf; + for (;;) { + if (c == -1) { + /* + * This should never happen, but it does, + * when the user gives a TSTP signal (^Z) or + * an interrupt while the program is trying + * to read a character from the terminal. + * In this case, the read is interrupted, so + * we end up here. + * Right, we will have to read again. + */ + if (interrupt) return 1; + break; + } + *p++ = c; + *p = 0; + if ((i = match(buf, &j, currmap->k_mach)) > 0) { + /* + * The key sequence matched. We have a command + */ + return j; + } + if (i == 0) return 0; + /* + * We have a prefix of a command. + */ + assert(i == FSM_ISPREFIX); + c = getch(); + } + } + /* NOTREACHED */ +} diff --git a/commands/yap/getcomm.h b/commands/yap/getcomm.h new file mode 100755 index 000000000..3db0b57f8 --- /dev/null +++ b/commands/yap/getcomm.h @@ -0,0 +1,36 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +# ifndef _GETCOMM_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +int getcomm(); +/* + * int getcomm() + * + * Reads commands given by the user. The command is returned. + */ + +VOID shellescape(); +/* + * void shellescape(command) + * char *command; The shell command to be executed + * + * Expands '%' and '!' in the command "command" to the current filename + * and the previous command respectively, and then executes "command". + */ + +char * readline(); +/* + * char * readline(prompt) + * char *prompt; Prompt given to the user + * + * Gives a prompt "prompt" and reads a line to be typed in by the user. + * A pointer to this line is returned. Space for this line is static. + */ + +# undef PUBLIC diff --git a/commands/yap/getline.c b/commands/yap/getline.c new file mode 100755 index 000000000..8e121cbb3 --- /dev/null +++ b/commands/yap/getline.c @@ -0,0 +1,722 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif + +# define _GETLINE_ + +# include <errno.h> +# include "in_all.h" +# include "getline.h" +# include "options.h" +# include "process.h" +# include "term.h" +# include "main.h" +# include "display.h" +# include "output.h" +# include "assert.h" + +extern int errno; + +# define BLOCKSIZE 2048 /* size of blocks */ +# define CHUNK 50 /* # of blockheaders allocated at a time */ + +/* + * The blockheaders of the blocks that are in core are kept in a linked list. + * The last added block is indicated by b_head, + * the tail of the list is indicated by b_tail. + * The links go from b_tail to b_head. + * The blockheaders are all in an array, in the order of the line numbers. + * Also, the blockheaders must always be in core, so they have to be rather + * small. On systems with a small address space, yap can run out of core, + * and panic. However, this should only happen with very large files (>> 1M). + */ + +struct block { + int b_flags; /* Contains the following flags: */ +# define DUMPED 01 /* block dumped on temporary file */ +# define PARTLY 02 /* block not filled completely (eof) */ + int b_next; /* ptr in linked list */ + long b_end; /* line number of last line in block */ + char * b_info; /* the block */ + int * b_offs; /* line offsets within the block */ + long b_foff; /* offset of block in file */ +}; + +static struct block * blocklist, /* beginning of the list of blocks */ + * maxblocklist, /* first free entry in the list */ + * topblocklist; /* end of allocated core for the list */ +static int b_head, + b_tail; +static int tfdes, ifdes; /* File descriptors for temporary's */ +static long lastreadline; /* lineno of last line read */ +static int ENDseen; + +STATIC VOID readblock(); +STATIC VOID nextblock(); +STATIC char *re_alloc(); + +STATIC struct block * +new_block() +{ + register struct block *pblock = maxblocklist - 1; + + if (!maxblocklist || !(pblock->b_flags & PARTLY)) { + /* + * There is no last block, or it was filled completely, + * so allocate a new blockheader. + */ + register int siz; + + pblock = blocklist; + if (maxblocklist == topblocklist) { + /* + * No blockheaders left. Allocate new ones + */ + siz = topblocklist - pblock; + blocklist = pblock = (struct block *) + re_alloc((char *) pblock, + (unsigned) (siz * sizeof(*pblock)), + (unsigned) ((siz + CHUNK) * sizeof(*pblock))); + pblock += siz; + topblocklist = pblock + CHUNK; + maxblocklist = pblock; + for (; pblock < topblocklist; pblock++) { + pblock->b_end = 0; + pblock->b_info = 0; + pblock->b_flags = 0; + } + if (!siz) { + /* + * Create dummy header cell. + */ + maxblocklist++; + } + } + pblock = maxblocklist++; + } + nextblock(pblock); + return pblock; +} + +/* + * Return the block in which line 'n' of the current file can be found. + * If "disable_interrupt" = 0, the call may be interrupted, in which + * case it returns 0. + */ + +STATIC struct block * +getblock(n, disable_interrupt) register long n; { + register struct block * pblock; + + if (stdf < 0) { + /* + * Not file descriptor, so return end of file + */ + return 0; + } + pblock = maxblocklist - 1; + if (n < lastreadline || + (n == lastreadline && !(pblock->b_flags & PARTLY))) { + /* + * The line asked for has been read already. + * Perform binary search in the blocklist to find the block + * where it's in. + */ + register struct block *min, *mid; + + min = blocklist + 1; + do { + mid = min + (pblock - min) / 2; + if (n > mid->b_end) { + min = mid + 1; + } + else pblock = mid; + } while (min < pblock); + /* Found, pblock is now a reference to the block wanted */ + if (!pblock->b_info) readblock(pblock); + return pblock; + } + + /* + * The line was'nt read yet, so read blocks until found + */ + for (;;) { + if (interrupt && !disable_interrupt) return 0; + pblock = new_block(); + if (pblock->b_end >= n) { + return pblock; + } + if (pblock->b_flags & PARTLY) { + /* + * We did not find it, and the last block could not be + * read completely, so return 0; + */ + return 0; + } + } + /* NOTREACHED */ +} + +char * +getline(n, disable_interrupt) long n; { + register struct block *pblock; + + if (!(pblock = getblock(n, disable_interrupt))) { + return (char *) 0; + } + return pblock->b_info + pblock->b_offs[n - ((pblock-1)->b_end + 1)]; +} + +/* + * Find the last line of the input, and return its number + */ + +long +to_lastline() { + + for (;;) { + if (!getline(lastreadline + 1, 0)) { + /* + * "lastreadline" always contains the linenumber of + * the last line read. So, if the call to getline + * succeeds, "lastreadline" is affected + */ + if (interrupt) return -1L; + return lastreadline; + } + } + /* NOTREACHED */ +} + +#if MAXNBLOCKS +int nblocks; /* Count number of large blocks */ +#endif + +/* + * Allocate some memory. If unavailable, free some and try again. + * If all fails, panic. + */ + +char * +alloc(size, isblock) unsigned size; { + + register char *pmem; + register struct block *pblock, *bllist; + char *malloc(); + long lseek(); + register long i; + + bllist = blocklist; + while ( +#if MAXNBLOCKS + (isblock && nblocks >= MAXNBLOCKS) || +#endif + !(pmem = malloc(size)) /* No space */ + ) { + if (b_tail == 0) { + /* + * Also, no blocks in core. Pity + */ + panic("No core"); + } +#if MAXNBLOCKS + nblocks--; +#endif + pblock = bllist + b_tail; + b_tail = pblock->b_next; + if (!nopipe && !(pblock->b_flags & DUMPED)) { + /* + * Dump the block on a temporary file + */ + if (!tfdes) { + /* + * create and open temporary files + */ + tfdes = opentemp(0); + ifdes = opentemp(1); + } + pblock->b_flags |= DUMPED; + /* + * Find out where to dump the block, and dump it + */ + i = (pblock-1)->b_end * sizeof(int); + (VOID) lseek(tfdes, + ((long) BLOCKSIZE * (pblock - bllist)), 0); + if (write(tfdes, pblock->b_info, BLOCKSIZE) + != BLOCKSIZE) { + panic("write failed"); + } + /* + * Also dump the offsets of the lines in the block + */ + (VOID) lseek(ifdes, i, 0); + i = pblock->b_end * sizeof(int) - i; + if (write(ifdes, (char *) pblock->b_offs, (int) i) + != (int) i) { + panic("Write failed"); + } + } + /* + * Now that the block is dumped, the space taken by it can + * be freed + */ + free((char *) pblock->b_offs); + free(pblock->b_info); + pblock->b_info = (char *) 0; + } +#if MAXNBLOCKS + if (isblock) nblocks++; +#endif + return pmem; +} + +/* + * Re-allocate the memorychunk pointed to by ptr, to let it + * grow or shrink. + * realloc of the standard C library is useless, as it is destructive + * if the malloc fails. + */ + +STATIC char * +re_alloc(ptr,oldsize, newsize) +char *ptr; unsigned oldsize; unsigned newsize; { + register char *pmem; + register char *c1, *c2; + + /* + * We could be smarter here, by checking if newsize < oldsize, and in + * that case using realloc, but this depends on realloc using the + * same block if the block shrinks. The question is, wether all + * reallocs in the world do this. + */ + pmem = alloc(newsize, 0); + if (oldsize) { + /* + * This test makes re_alloc also work if there was no old block + */ + c1 = pmem; + c2 = ptr; + if (newsize > oldsize) { + newsize = oldsize; + } + while (newsize--) { + *c1++ = *c2++; + } + free(ptr); + } + return pmem; +} + +/* + * Append a block to the linked list of blockheaders of blocks that are + * in core. + */ + +STATIC VOID +addtolist(pblock) register struct block *pblock; { + register struct block *bllist = blocklist; + + pblock->b_next = 0; + (bllist + b_head)->b_next = pblock - bllist; + b_head = pblock - bllist; + if (!b_tail) { + /* + * The list was empty, initialize + */ + b_tail = b_head; + } +} + +static char *saved; +static long filldegree; + +/* + * Try to read the block indicated by pblock + */ + +STATIC VOID +nextblock(pblock) register struct block *pblock; { + register char *c, /* Run through pblock->b_info */ + *c1; /* indicate end of pblock->b_info */ + register int *poff; /* pointer in line-offset list */ + register int cnt; /* # of characters read */ + register unsigned siz; /* Size of allocated line-offset list */ + static unsigned savedsiz; /* saved "siz" */ + static int *savedpoff; /* saved "poff" */ + static char *savedc1; /* saved "c1" */ + + if (pblock->b_flags & PARTLY) { + /* + * The block was already partly filled. Initialize locals + * accordingly + */ + poff = savedpoff; + siz = savedsiz; + pblock->b_flags = 0; + c1 = savedc1; + if (c1 == pblock->b_info || *(c1 - 1)) { + /* + * We had incremented "lastreadline" temporarily, + * because the last line could not be completely read + * last time we tried. Undo this increment + */ + poff--; + --lastreadline; + } + } + else { + if (nopipe) pblock->b_foff = lseek(stdf, 0L, 1); + if (saved) { + /* + * There were leftovers from the previous block + */ + pblock->b_info = saved; + if (nopipe) pblock->b_foff -= savedc1 - saved; + c1 = savedc1; + saved = 0; + } + else { /* Allocate new block */ + pblock->b_info = c1 = alloc(BLOCKSIZE + 1, 1); + } + /* + * Allocate some space for line-offsets + */ + pblock->b_offs = poff = (int *) + alloc((unsigned) (100 * sizeof(int)), 0); + siz = 99; + *poff++ = 0; + } + c = c1; + for (;;) { + /* + * Read loop + */ + cnt = read(stdf, c1, BLOCKSIZE - (c1 - pblock->b_info)); + if (cnt < 0) { + /* + * Interrupted read + */ + if (errno == EINTR) continue; + error("Could not read input file"); + cnt = 0; + } + c1 += cnt; + if (c1 != pblock->b_info + BLOCKSIZE) { + ENDseen = 1; + pblock->b_flags |= PARTLY; + } + break; + } + assert(c <= c1); + while (c < c1) { + /* + * Now process the block + */ + *c &= 0177; /* Most significant bit ignored */ + if (*c == '\n') { + /* + * Newlines are replaced by '\0', so that "getline" + * can deliver one line at a time + */ + *c = 0; + lastreadline++; + /* + * Remember the line-offset + */ + if (poff == pblock->b_offs + siz) { + /* + * No space for it, allocate some more + */ + pblock->b_offs = (int *) + re_alloc((char *) pblock->b_offs, + (siz+1) * sizeof(int), + (siz + 51) * sizeof(int)); + poff = pblock->b_offs + siz; + siz += 50; + } + *poff++ = c - pblock->b_info + 1; + } + else if (*c == '\0') { + /* + * 0-bytes are replaced by 0200, because newlines are + * replaced by 0, and 0200 & 0177 gives again 0 ... + */ + *c = 0200; + } + c++; + } + assert(c==c1); + *c = 0; + if (c != pblock->b_info && *(c-1) != 0) { + /* + * The last line read does not end with a newline, so add one + */ + lastreadline++; + *poff++ = c - pblock->b_info + 1; + if (!(pblock->b_flags & PARTLY) && *(poff - 2) != 0) { + /* + * Save the started line; it will be in the next block. + * Remove the newline we added just now. + */ + saved = c1 = alloc(BLOCKSIZE + 1, 1); + c = pblock->b_info + *(--poff - 1); + while (*c) *c1++ = *c++; + c = pblock->b_info + *(poff - 1); + savedc1 = c1; + --lastreadline; + } + } + pblock->b_end = lastreadline; + if (pblock->b_flags & PARTLY) { + /* + * Take care, that we can call "nextblock" again, to fill in + * the rest of this block + */ + savedsiz = siz; + savedpoff = poff; + savedc1 = c; + if (c == pblock->b_info) { + lastreadline++; + pblock->b_end = 0; + } + } + else { + /* + * Not completely read blocks are not in the linked list, + * so can never be "swapped out". + */ + addtolist(pblock); + cnt = pblock - blocklist; + filldegree = ((c-pblock->b_info) + (cnt-1) * filldegree) / cnt; + } + assert(pblock->b_end - (pblock-1)->b_end <= poff - pblock->b_offs); +} + +/* + * Allocate core for the block, and read it back from + * the temporary file. + */ + +STATIC VOID +readblock(pblock) register struct block *pblock; { + + register int size; + register long i; + + /* + * Find out where the block is, and read it + */ + pblock->b_info = alloc(BLOCKSIZE + 1, 1); + i = (pblock - 1)->b_end * sizeof(int); + size = (int) (pblock->b_end * sizeof(int) - i); + pblock->b_offs = (int *) alloc((unsigned) size, 0); + if (nopipe) { + register char *c; + register int line_index; + int cnt; + long l = lseek(stdf, 0L, 1); + + (VOID) lseek(stdf, pblock->b_foff, 0); + cnt = read(stdf, pblock->b_info, BLOCKSIZE); + (VOID) lseek(stdf, l, 0); + c = pblock->b_info; + pblock->b_offs[0] = 0; + line_index = 1; + size /= sizeof(int); + while (c < pblock->b_info + cnt) { + *c &= 0177; + if (*c == '\n') { + *c = '\0'; + if (line_index < size) + pblock->b_offs[line_index++] = + (c - pblock->b_info) + 1; + } + else if (*c == '\0') *c = 0200; + c++; + } + *c = '\0'; + } + else { + (VOID) lseek(tfdes, (long) ((long) BLOCKSIZE * (pblock - blocklist)),0); + if (read(tfdes, pblock->b_info,BLOCKSIZE) != BLOCKSIZE) { + panic("read error"); + } + /* + * Find out where the line-offset list is, and read it + */ + (VOID) lseek(ifdes, i, 0); + if (read(ifdes, (char *) pblock->b_offs, size) != size) { + panic("read error"); + } + pblock->b_info[BLOCKSIZE] = '\0'; + } + /* + * Add this block to the list of incore blocks + */ + addtolist(pblock); +} + +/* + * Called after processing a file. + * Free all core. + */ + +VOID +do_clean() { + + register struct block *pblock; + register char *p; + + for (pblock = blocklist; pblock < maxblocklist; pblock++) { + if (p = pblock->b_info) { + free(p); + free((char *) pblock->b_offs); + } + } + if (p = (char *) blocklist) { + free(p); + } + blocklist = 0; + maxblocklist = 0; + topblocklist = 0; + lastreadline = 0; + filldegree = 0; + ENDseen = 0; + if (p = saved) free(p); + saved = 0; + b_head = 0; + b_tail = 0; +# if MAXNBLOCKS + nblocks = 0; +# endif +} + +/* + * Close a file with file-descriptor "file", if it indeed is one + */ + +STATIC VOID +cls(file) { + if (file) (VOID) close(file); +} + +/* + * Close all files + */ + +VOID +cls_files() { + + cls(tfdes); + cls(ifdes); + cls(stdf); +} + +/* + * Get a character. If possible, do some workahead. + */ + +int +getch() { +# if USG_OPEN +# include <fcntl.h> +# include <sys/stat.h> + + register int i,j; + struct stat buf; +# else +# ifdef FIONREAD +# include <sys/stat.h> + + struct stat buf; + long i; +# endif +# endif + + char c; + int retval; + + flush(); + if (startcomm) { + /* + * Command line option command + */ + if (*startcomm) return *startcomm++; + return '\n'; + } +# if USG_OPEN + if (stdf >= 0) { + /* + * Make reads from the terminal non-blocking, so that + * we can see if the user typed something + */ + i = fcntl(0,F_GETFL,0); + if (i != -1 && fcntl(0, F_SETFL, i|O_NDELAY) != -1) { + j = 0; + while (! ENDseen && + ((j = read(0,&c,1)) == 0 +#ifdef EWOULDBLOCK + || (j < 0 && errno == EWOULDBLOCK) +#endif + ) + && + (nopipe || + (fstat(stdf,&buf) >= 0 && buf.st_size > 0))) { + /* + * Do some read ahead, after making sure there + * is input and the user did not type a command + */ + new_block(); + } + (VOID) fcntl(0,F_SETFL,i); + if (j < 0) { + /* + * Could this have happened? + * I'm not sure, because the read is + * nonblocking. Can it be interrupted then? + */ + return -1; + } + if (j > 0) return c; + } + } +# else +# ifdef FIONREAD + if (stdf >= 0) { + /* + * See if there are any characters waiting in the terminal input + * queue. If there are not, read ahead. + */ + while (! ENDseen && + ( ioctl(0, FIONREAD, (char *) &i) >= 0 && i == 0) && + ( nopipe || fstat(stdf,&buf) >= 0 && buf.st_size > 0)) { + /* + * While the user does'nt type anything, and there is + * input to be processed, work ahead + */ + if (interrupt) return -1; + new_block(); + } + } +# endif +# endif + if (read(0,&c,1) <= 0) retval = -1; else retval = c & 0177; + return retval; +} + +/* + * Get the position of line "ln" in the file. + */ + +long +getpos(ln) long ln; { + register struct block *pblock; + register long i; + + pblock = getblock(ln,1); + assert(pblock != 0); + i = filldegree * (pblock - blocklist); + return i - (filldegree - pblock->b_offs[ln - (pblock-1)->b_end]); +} diff --git a/commands/yap/getline.h b/commands/yap/getline.h new file mode 100755 index 000000000..d4aa41603 --- /dev/null +++ b/commands/yap/getline.h @@ -0,0 +1,71 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +# ifndef _GETLINE_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +char * getline(); +/* + * char * getline(ln,disable_interrupt) + * long ln; The line number of the line to be returned + * int disable_interrupt; 1 if interrupts must be ignored, 0 otherwise + * + * Returns a pointer to the line with linenumber "ln". + * It returns 0 if + * - there was an interrupt, and interrupts were not disabled, or + * - there is no line with linenumber "ln". + */ + +char * alloc(); +/* + * char * alloc(size, isblock) + * unsigned size; The size in bytes + * int isblock; Flag indicating whether this is a file-text + * block + * + * Return a pointer to a block of "size" bytes. + * Panics if no core can be found. + */ + +VOID do_clean(); +/* + * void do_clean() + * + * Cleans up and initializes. + */ + +VOID cls_files(); +/* + * void cls_files() + * + * Closes files. Useful for shell escapes. + */ + +int getch(); +/* + * int getch() + * + * Get a character from input or command option line (only at start up). + * Some systems allow us to do some workahead while the user is + * thinking/reading. Use this to get parts of the input file in core. + */ + +long to_lastline(); +/* + * long to_lastline() + * + * Finds the last line of the file, and returns its number. + * This command can be interrupted, in which case it returns 0. + */ + +long getpos(); +/* + * long getpos(line); + * + * get offset of line "line" in the input + */ +# undef PUBLIC diff --git a/commands/yap/help.c b/commands/yap/help.c new file mode 100755 index 000000000..2c550253c --- /dev/null +++ b/commands/yap/help.c @@ -0,0 +1,107 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +#ifndef lint +static char rcsid[] = "$Header$"; +#endif + +#define _HELP_ +#include "in_all.h" +#include "help.h" +#include "machine.h" +#include "commands.h" +#include "keys.h" +#include "output.h" +#include "prompt.h" +#include "main.h" +#include "display.h" +#include "term.h" +#include "options.h" + +static int h_cnt; /* Count # of lines */ +static struct state *origin; /* Keep track of startstate */ + +/* + * Print a key sequence. + * We arrived at an endstate. The s_next link in the state structure now + * leads us from "origin" to the current state, so that we can print the key + * sequence easily. + */ + +STATIC VOID +pr_comm() { + register struct state *p = origin; + register char *pb; + register int c; + char buf[30]; + register int i = 0; /* How many characters printed? */ + + pb = buf; + for (;;) { + c = p->s_char & 0177; + if (c < ' ' || c == 0177) { + /* + * Will take an extra position + */ + i++; + } + *pb++ = c; + i++; + if (!p->s_match) break; + p = p->s_next; + } + do { + *pb++ = ' '; + } while (++i < 12); + *pb = 0; + cputline(buf); +} + +/* + * Print out a description of the keymap. This is done, by temporarily using + * the s_next field in the state structure indicate the state matching the + * next character, so that we can walk from "origin" to an endstate. + */ + +STATIC VOID +pr_mach(currstate, back) register struct state *currstate, *back; { + struct state *save; + + while (currstate) { + if (interrupt) break; + if (back) { + save = back->s_next; /* Save original link */ + back->s_next = currstate; + } + if (!currstate->s_match) { + /* + * End state, print command + */ + pr_comm(); + putline(commands[currstate->s_cnt].c_descr); + putline("\r\n"); + if (++h_cnt >= maxpagesize) { + ret_to_continue(); + h_cnt = 0; + } + } + else pr_mach(currstate->s_match, currstate); + currstate = currstate->s_next; + if (back) back->s_next = save; /* restore */ + else origin = currstate; + } +} + +/*ARGSUSED*/ +int +do_help(i) long i; { /* The help command */ + + startcomm = 0; + h_cnt = 2; + putline("\r\nSummary of yap commands:\r\n"); + origin = currmap->k_mach; + pr_mach(currmap->k_mach, (struct state *) 0); + if (h_cnt) { + ret_to_continue(); + } + if (!hardcopy && scr_info.currentpos) redraw(1); +} diff --git a/commands/yap/help.h b/commands/yap/help.h new file mode 100755 index 000000000..c05c50744 --- /dev/null +++ b/commands/yap/help.h @@ -0,0 +1,20 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +# ifndef _HELP_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +int do_help(); +/* + * int do_help(cnt); + * long cnt; This is ignored, but a count is given + * to any command + * + * Give a summary of commands + */ + +# undef PUBLIC diff --git a/commands/yap/in_all.h b/commands/yap/in_all.h new file mode 100755 index 000000000..b5fdb6318 --- /dev/null +++ b/commands/yap/in_all.h @@ -0,0 +1,89 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +#define DO_ASSERT 0 /* define when debugging */ +# ifdef DO_ASSERT +# define STATIC +# else +# define STATIC static +# endif + +#define VOID void /* preferably void, but int if your compiler does + not recognize void + */ + +#if _POSIX_SOURCE +#define POSIX_OPEN 1 /* POSIX "open" system call */ +#else +#define USG_OPEN 0 /* USG "open" system call (include <fcntl.h>) */ +#define BSD4_2_OPEN 0 /* BSD 4.2 "open" system call (include <sys/file.h>)*/ +#endif + +/* Sanity check 1 */ +# if (!!USG_OPEN) + (!!BSD4_2_OPEN) + (!!POSIX_OPEN) > 1 +Oops, now why did you do that? +O, never mind, just try it again with +USG_OPEN = 1 or for System III, System V etc. +BSD4_2_OPEN = 1 or for Berkeley 4.2, Ultrix etc. +POSIX_OPEN = 1 or for POSIX compliant systems. +USG_OPEN = 0 and BSD4_2_OPEN = 0 and POSIX_OOPEN + for Berkeley 4.1, v7 and whatever else +# endif + +#define BSD_REGEX 0 /* Berkeley style re_comp/re_exec */ +#define V8_REGEX 1 /* V8 style regexec/regcomp */ +#define USG_REGEX 0 /* USG style regex/regcmp */ + +/* Sanity check 2 */ +# if USG_REGEX + BSD_REGEX + V8_REGEX > 1 +Select one style for the regular expressions please! +# endif + +#define USG_TTY 0 /* define if you have an USG tty driver (termio) */ + /* If you do not define this, you get either the + * V7 tty driver or the BSD one. + */ +#if _POSIX_SOURCE +#define POSIX_TTY 1 +#endif + +#if __minix && !__minix_vmd +#define MAXNBLOCKS 10 /* Limit the number of blocks that yap will use to keep + * the input in core. + * This was needed to let yap run on an IBM XT + * running PC/IX. The problem is that malloc can + * allocate almost all available space, leaving no + * space for the stack, which causes a memory fault. + * Internal yap blocks are 2K, but there is a lot of + * additional information that yap keeps around. You + * can also use it if you want to limit yap's maximum + * size. If defined, it should be at least 3. + * 10 is probably a reasonable number. + */ +#endif +/* Sanity check 3 */ +# ifdef MAXNBLOCKS +# if MAXNBLOCKS < 3 +Read the above comment! +# endif +# endif + +#define VT100_PATCH /* This involves a patch that will insert lines + * correctly on a VT100 terminal. The termcap entry + * for it contains an "al" with %-escapes. According + * to the termcap-documentation this is not allowed, + * but ... + * If VT100_PATCH is defined, the "al" capability will + * be offered to "tgoto", before "tputs"-ing it. + * I don't know if there are any terminals out there + * that have a % in their "al" capability. If there + * are, yap will not work properly when compiled with + * VT100_PATCH defined. + * Also, escape sequences for standout and underline + * will be tputs-ed if VT100_PATCH is defined. + */ + +#if _MINIX +#define LCASE 0 /* Minix doesn;t have LCASE */ +#endif diff --git a/commands/yap/keys.c b/commands/yap/keys.c new file mode 100755 index 000000000..306e2af6d --- /dev/null +++ b/commands/yap/keys.c @@ -0,0 +1,188 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif + +# define _KEYS_ + +# include <ctype.h> +# include "in_all.h" +# include "machine.h" +# include "keys.h" +# include "commands.h" +# include "prompt.h" +# include "assert.h" + +char defaultmap[] = "\ +bf=P:bl=k:bl=^K:bl=^[[A:bot=l:bot=$:bot=^[[Y:bp=-:bp=^[[V:bs=^B:bse=?:bsl=S:\ +bsp=F:chm=X:exg=x:ff=N:fl=^J:fl=^M:fl=j:fl=^[[B:fp= :fp=^[[U:fs=^D:fse=/:\ +fsl=s:fsp=f:hlp=h:nse=n:nsr=r:red=^L:rep=.:bps=Z:bss=b:fps=z:fss=d:shl=!:\ +tom=':top=\\^:top=^[[H:vis=e:wrf=w:qui=q:qui=Q:mar=m:pip=|"; + +char *strcpy(); +char *strcat(); +char *getenv(); + +/* + * Construct an error message and return it + */ + +STATIC char * +kerror(key, emess) char *key, *emess; { + static char ebuf[80]; /* Room for the error message */ + + (VOID) strcpy(ebuf, key); + (VOID) strcat(ebuf, emess); + return ebuf; +} + +/* + * Compile a keymap into commtable. Returns an error message if there + * is one + */ + +STATIC char * +compile(map, commtable) + register char *map; register struct keymap *commtable; { + register char *mark; /* Indicates start of mnemonic */ + register char *c; /* Runs through buf */ + register int temp; + char *escapes = commtable->k_esc; + char buf[10]; /* Will hold key sequence */ + + (VOID) strcpy(commtable->k_help,"Illegal command"); + while (*map) { + c = buf; + mark = map; /* Start of mnemonic */ + while (*map && *map != '=') { + map++; + } + if (!*map) { + /* + * Mnemonic should end with '=' + */ + return kerror(mark, ": Syntax error"); + } + *map++ = 0; + while (*map) { + /* + * Get key sequence + */ + if (*map == ':') { + /* + * end of key sequence + */ + map++; + break; + } + *c = *map++ & 0177; + if (*c == '^' || *c == '\\') { + if (!(temp = *map++)) { + /* + * Escape not followed by a character + */ + return kerror(mark, ": Syntax error"); + } + if (*c == '^') { + if (temp == '?') *c = 0177; + else *c = temp & 037; + } + else *c = temp & 0177; + } + setused(*c); + c++; + if (c >= &buf[9]) { + return kerror(mark,": Key sequence too long"); + } + } + *c = 0; + if (!(temp = lookup(mark))) { + return kerror(mark,": Nonexistent function"); + } + if (c == &buf[1] && (commands[temp].c_flags & ESC) && + escapes < &(commtable->k_esc[sizeof(commtable->k_esc)-1])) { + *escapes++ = buf[0] & 0177; + } + temp = addstring(buf, temp, &(commtable->k_mach)); + if (temp == FSM_ISPREFIX) { + return kerror(mark,": Prefix of other key sequence"); + } + if (temp == FSM_HASPREFIX) { + return kerror(mark,": Other key sequence is prefix"); + } + assert(temp == FSM_OKE); + if (!strcmp(mark, "hlp")) { + /* + * Create an error message to be given when the user + * types an illegal command + */ + (VOID) strcpy(commtable->k_help, "Type "); + (VOID) strcat(commtable->k_help, buf); + (VOID) strcat(commtable->k_help, " for help"); + } + } + *escapes = 0; + return (char *) 0; +} + +/* + * Initialize the keymaps + */ + +VOID +initkeys() { + register char *p; + static struct keymap xx[2]; + + currmap = &xx[0]; + othermap = &xx[1]; + p = compile(defaultmap, currmap); /* Compile default map */ + assert(p == (char *) 0); + p = getenv("YAPKEYS"); + if (p) { + if (!(p = compile(p, othermap))) { + /* + * No errors in user defined keymap. So, use it + */ + do_chkm(0L); + return; + } + error(p); + } + othermap = 0; /* No other keymap */ +} + +int +is_escape(c) +{ + register char *p = currmap->k_esc; + + while (*p) { + if (c == *p++) return 1; + } + return 0; +} + +static char keyset[16]; /* bitset indicating which keys are + * used + */ +/* + * Mark key "key" as used + */ + +VOID +setused(key) int key; { + + keyset[(key & 0177) >> 3] |= (1 << (key & 07)); +} + +/* + * return non-zero if key "key" is used in a keymap + */ + +int +isused(key) int key; { + + return keyset[(key & 0177) >> 3] & (1 << (key & 07)); +} diff --git a/commands/yap/keys.h b/commands/yap/keys.h new file mode 100755 index 000000000..7ec81cab2 --- /dev/null +++ b/commands/yap/keys.h @@ -0,0 +1,50 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +# ifndef _KEYS_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +PUBLIC struct keymap { + char k_help[80]; /* To be printed on illegal command */ + struct state *k_mach; /* Finite state machine */ + char k_esc[10]; /* escape chars */ +} *currmap, /* pointer to current key map */ + *othermap; /* pointer to other keymap */ + +VOID initkeys(); +/* + * void initkeys(); + * + * Initializes the keymap(s). + */ + +VOID setused(); +/* + * void setused(key); + * int key; + * + * Marks the key "key" as used. + */ + +int isused(); +/* + * int isused(key); + * int key; + * + * returns 0 if the key "key" is not used. + * Otherwise it returns non-zero. + */ + +int is_escape(); +/* + * int is_escape(c); + * int c; + * + * Returns 1 if "c" is an escape char (shell or pipe) in the current + * keymap. + */ +# undef PUBLIC diff --git a/commands/yap/machine.c b/commands/yap/machine.c new file mode 100755 index 000000000..c6984e1eb --- /dev/null +++ b/commands/yap/machine.c @@ -0,0 +1,137 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif + +# define _MACHINE_ + +# include <ctype.h> +# include "in_all.h" +# include "machine.h" +# include "getline.h" +# include "assert.h" + +/* + * Add part of finite state machine to recognize the string s. + */ + +STATIC int +addtomach(s, cnt, list) char *s; struct state **list; { + + register struct state *l; + register int i = FSM_OKE; /* Return value */ + register int j; + + for (;;) { + l = *list; + if (!l) { + /* + * Create new list element + */ + *list = l = (struct state *) alloc(sizeof(*l), 0); + l->s_char = *s; + l->s_endstate = 0; + l->s_match = 0; + l->s_next = 0; + } + if (l->s_char == *s) { + /* + * Continue with next character + */ + if (!*++s) { + /* + * No next character + */ + j = l->s_endstate; + l->s_endstate = 1; + if (l->s_match || j) { + /* + * If the state already was an endstate, + * or has a successor, the currently + * added string is a prefix of an + * already recognized string + */ + return FSM_ISPREFIX; + } + l->s_cnt = cnt; + return i; + } + if (l->s_endstate) { + /* + * In this case, the currently added string has + * a prefix that is an already recognized + * string. + */ + i = FSM_HASPREFIX; + } + list = &(l->s_match); + continue; + } + list = &(l->s_next); + } + /* NOTREACHED */ +} + +/* + * Add a string to the FSM. + */ + +int +addstring(s,cnt,machine) register char *s; struct state **machine; { + + if (!s || !*s) { + return FSM_ISPREFIX; + } + return addtomach(s,cnt,machine); +} + +/* + * Match string s with the finite state machine. + * If it matches, the number of characters actually matched is returned, + * and the count is put in the word pointed to by i. + * If the string is a prefix of a string that could be matched, + * FSM_ISPREFIX is returned. Otherwise, 0 is returned. + */ + +int +match(s,i,mach) char *s; int *i; register struct state *mach; { + + register char *s1 = s; /* Walk through string */ + register struct state *mach1 = 0; + /* Keep track of previous state */ + + while (mach && *s1) { + if (mach->s_char == *s1) { + /* + * Current character matches. Carry on with next + * character and next state + */ + mach1 = mach; + mach = mach->s_match; + s1++; + continue; + } + mach = mach->s_next; + } + if (!mach1) { + /* + * No characters matched + */ + return 0; + } + if (mach1->s_endstate) { + /* + * The string matched + */ + *i = mach1->s_cnt; + return s1 - s; + } + if (!*s1) { + /* + * The string matched a prefix + */ + return FSM_ISPREFIX; + } + return 0; +} diff --git a/commands/yap/machine.h b/commands/yap/machine.h new file mode 100755 index 000000000..31c19443f --- /dev/null +++ b/commands/yap/machine.h @@ -0,0 +1,60 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +# ifndef _MACHINE_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +/* + * Simple minded finite state machine implementation to recognize + * strings. + */ + +struct state { + char s_char; /* character to match with */ + char s_endstate; /* flag, 1 if this state is an endstate */ + struct state *s_match; /* new state if matched */ + struct state *s_next; /* other characters to match with */ + short s_cnt; /* if an endstate, this field is filled with + * some info, dependant on the machine. + */ +}; + +# define FSM_OKE 0 +# define FSM_ISPREFIX -1 /* Must be < 0 */ +# define FSM_HASPREFIX 1 + +int addstring(); +/* + * int addstring(str,cnt,mach) + * char *str; The string to be recognized + * int cnt; Attribute of the string. + * struct state **mach; The finite state machine + * + * This routine adds a string to a finite state automaton. + * It returns FSM_ISPREFIX if the added string is a prefix of a string already + * in the automaton, FSM_HASPREFIX if a string, already recognized by the + * automaton, is a prefix of the added string. + * Otherwise it returns FSM_OKE. + */ + +int match(); +/* + * int match(str,p_int,mach) + * char *str; pointer to string + * int *p_int; Pointer to an integer + * struct state *mach; The finite state machine + * + * A match of the string indicated by "str" is tried. If a head of "str" + * is recognized by the finite state automaton, a machine dependant number + * is put in the integer pointed to by "p_int". + * The number of characters that match is returned, so a return value of 0 + * means no match. + * A return value of FSM_PREFIX means that the string "str" was a prefix of a + * matched string. + */ + +# undef PUBLIC diff --git a/commands/yap/main.c b/commands/yap/main.c new file mode 100755 index 000000000..52d4e7cff --- /dev/null +++ b/commands/yap/main.c @@ -0,0 +1,220 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif + +# define _MAIN_ + +# include "in_all.h" +# if USG_OPEN +# include <fcntl.h> +# endif +# if BSD4_2_OPEN +# include <sys/file.h> +# endif +# if POSIX_OPEN +# include <sys/types.h> +# include <fcntl.h> +# endif +# include "main.h" +# include "term.h" +# include "options.h" +# include "output.h" +# include "process.h" +# include "commands.h" +# include "display.h" +# include "prompt.h" + +char *strcpy(); + +STATIC int initialize(); +# ifdef SIGTSTP +STATIC int suspsig(); +# endif + +int +main(argc,argv) register char ** argv; { + + register char ** av; + + if (! isatty(1)) { + no_tty = 1; + } + argv[argc] = 0; + progname = argv[0]; + if ((av = readoptions(argv)) == (char **) 0 || + initialize(*av ? 1 : 0)) { + if (no_tty) { + close(1); + (VOID) dup(2); + } + putline("Usage: "); + putline(argv[0]); + putline( +" [-c] [-u] [-n] [-q] [-number] [+command] [file ... ]\n"); + flush(); + exit(1); + } + if (no_tty) { + *--av = "cat"; + execve("/bin/cat", av, (char *) 0); + } + else processfiles(argc-(av-argv), av); + (VOID) quit(); + /* NOTREACHED */ +} + +char *mktemp(); + +/* + * Open temporary file for reading and writing. + * Panic if it fails + */ + +static char indexfile[30], tempfile[30]; + +int +opentemp(i) { + + register fildes; + register char *f; + + f = i ? mktemp(indexfile) : mktemp(tempfile); +# if BSD4_2_OPEN || USG_OPEN || POSIX_OPEN + if ((fildes = open(f,O_RDWR|O_TRUNC|O_CREAT,0600)) < 0) { +# else + if ((fildes = creat(f,0600)) <= 0 || close(fildes) < 0 || + (fildes = open(f,2)) < 0) { +# endif + panic("Couldn't open temporary file"); + } + (VOID) unlink(f); + return fildes; +} + +/* + * Collect initializing stuff here. + */ + +STATIC int +initialize(x) { + + if (!(nopipe = x)) { + /* + * Reading from pipe + */ + if (isatty(0)) { + return 1; + } + stdf = dup(0); /* Duplicate file descriptor of input */ + if (no_tty) return 0; + /* + * Make sure standard input is from the terminal. + */ + (VOID) close(0); +# if BSD4_2_OPEN || USG_OPEN || POSIX_OPEN + if (open("/dev/tty",O_RDONLY,0) != 0) { +# else + if (open("/dev/tty",0) != 0) { +# endif + putline("Couldn't open terminal\n"); + flush(); + exit(1); + } + } + if (no_tty) return 0; + (VOID) strcpy(tempfile,"/usr/tmp/yap_XXXXXX"); + (VOID) strcpy(indexfile,"/usr/tmp/yap-XXXXXX"); + /* + * Handle signals. + * Catch QUIT, DELETE and ^Z + */ + (VOID) signal(SIGQUIT,SIG_IGN); + (VOID) signal(SIGINT, catchdel); + ini_terminal(); +# ifdef SIGTSTP + if (signal(SIGTSTP,SIG_IGN) == SIG_DFL) { + (VOID) signal(SIGTSTP,suspsig); + } +# endif + (VOID) signal(SIGQUIT,quit); + return 0; +} + +int +catchdel() { + (VOID) signal(SIGINT, catchdel); + interrupt = 1; +} + +# ifdef SIGTSTP + +/* + * We had a SIGTSTP signal. + * Suspend, by a call to this routine. + */ + +VOID +suspend() { + + nflush(); + resettty(); + (VOID) signal(SIGTSTP,SIG_DFL); +#if BSD4_2_OPEN + sigsetmask(sigblock(0)&~(1 << (SIGTSTP - 1))); +#endif + (VOID) kill(0, SIGTSTP); + /* + * We are not here anymore ... + * + + * + * But we arive here ... + */ + inittty(); + putline(TI); + flush(); + (VOID) signal(SIGTSTP,suspsig); +} + +/* + * SIGTSTP signal handler. + * Just indicate that we had one, ignore further ones and return. + */ + +STATIC int +suspsig() { + + suspend(); + if (DoneSetJmp) longjmp(SetJmpBuf, 1); +} +# endif + +/* + * quit : called on exit. + * I bet you guessed that much. + */ + +int +quit() { + + clrbline(); + resettty(); + flush(); + exit(0); +} + +/* + * Exit, but nonvoluntarily. + * At least tell the user why. + */ + +VOID +panic(s) char *s; { + + putline("\007\007\007\r\n"); + putline(s); + putline("\r\n"); + quit(); +} diff --git a/commands/yap/main.h b/commands/yap/main.h new file mode 100755 index 000000000..4a381a220 --- /dev/null +++ b/commands/yap/main.h @@ -0,0 +1,68 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +# ifndef _MAIN_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +PUBLIC int nopipe; /* Not reading from pipe? */ +PUBLIC char * progname; /* Name of this program */ +PUBLIC int interrupt; /* Interrupt given? */ +PUBLIC int no_tty; /* output not to a terminal, behave like cat */ + +int main(); +/* + * int main(argc,argv) + * int argc; Argument count + * char *argv[]; The arguments + * + * Main program. + */ + +int opentemp(); +/* + * int opentemp(i) + * int i; Either 0 or 1, indicates which temporary to open + * + * Returns a file descriptor for the temporary file, or panics if + * it couldn't open one. + */ + +int catchdel(); +/* + * int catchdel(); + * + * interrupt handler. Does not return a value, but PCC has some + * difficulty with the type pointer to function returning void. + * This routine only sets a flag indicating that there was an interrupt. + */ + +int quit(); +/* + * int quit(); + * + * Quit signal handler. Also used for normal exits. + * It resets the terminal and exits + */ + +VOID panic(); +/* + * void panic(str) + * char *str; Reason for panic + * + * Panic, but at least tell the user why. + */ + +# ifdef SIGTSTP +VOID suspend(); +/* + * void suspend() + * + * Suspends this process + */ +# endif + +# undef PUBLIC diff --git a/commands/yap/options.c b/commands/yap/options.c new file mode 100755 index 000000000..357b2ebcc --- /dev/null +++ b/commands/yap/options.c @@ -0,0 +1,88 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif + +# define _OPTIONS_ + +# include "in_all.h" +# include "options.h" +# include "output.h" +# include "display.h" +# include <ctype.h> + +STATIC int parsopt(); +char *getenv(); + +/* + * Read the options. Return the argv pointer following them if there were + * no errors, otherwise return 0. + */ + +char ** +readoptions(argv) char ** argv; { + + register char ** av = argv+1; + register char *p; + + if (p = getenv("YAP")) { + (VOID) parsopt(p); + } + while (*av && **av == '-') { + if (parsopt(*av)) { + /* + * Error in option + */ + putline(*av); + putline(": illegal option\n"); + return (char **) 0; + } + av++; + } + if (*av && **av == '+') { + /* + * Command in command line + */ + startcomm = *av + 1; + av++; + } + return av; +} + +STATIC int +parsopt(s) register char *s; { + register i; + + if (*s == '-') s++; + if (isdigit(*s)) { + /* + * pagesize option + */ + i = 0; + do { + i = i * 10 + *s++ - '0'; + } while (isdigit(*s)); + if (i < MINPAGESIZE) i = MINPAGESIZE; + pagesize = i; + } + while (*s) { + switch(*s++) { + case 'c' : + cflag++; + break; + case 'n' : + nflag++; + break; + case 'u' : + uflag++; + break; + case 'q' : + qflag++; + break; + default : + return 1; + } + } + return 0; +} diff --git a/commands/yap/options.h b/commands/yap/options.h new file mode 100755 index 000000000..f3ea75da5 --- /dev/null +++ b/commands/yap/options.h @@ -0,0 +1,26 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +# ifndef _OPTIONS_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +PUBLIC int cflag; /* no home before each page */ +PUBLIC int uflag; /* no underlining */ +PUBLIC int nflag; /* no pattern matching on input */ +PUBLIC int qflag; /* no exit on the next page command */ +PUBLIC char * startcomm; /* There was a command option */ + +char ** readoptions(); +/* + * char ** readoptions(argv) + * char **argv; Arguments given to yap. + * + * process the options from the arguments. Return 0 if there was an error, + * otherwise return a pointer to where the filenames start. + */ + +# undef PUBLIC diff --git a/commands/yap/output.c b/commands/yap/output.c new file mode 100755 index 000000000..53c535b00 --- /dev/null +++ b/commands/yap/output.c @@ -0,0 +1,100 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* + * Handle output to screen + */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif + +# define _OUTPUT_ + +# include "in_all.h" +# include "output.h" +# include "main.h" + +# define OBUFSIZ 64*128 + +static char _outbuf[OBUFSIZ]; + +VOID +flush() { /* Flush output buffer, by writing it */ + register char *p = _outbuf; + + _ocnt = OBUFSIZ; + if (_optr) (VOID) write(1, p, _optr - p); + _optr = p; +} + +VOID +nflush() { /* Flush output buffer, ignoring it */ + + _ocnt = OBUFSIZ; + _optr = _outbuf; +} + +int +fputch(ch) char ch; { /* print a character */ + putch(ch); +} + +VOID +putline(s) register char *s; { /* Print string s */ + + if (!s) return; + while (*s) { + putch(*s++); + } +} + +/* + * A safe version of putline. All control characters are echoed as ^X + */ + +VOID +cputline(s) char *s; { + register c; + + while (c = *s++) { + if ((unsigned) c > 0177) c &= 0177; + if (c < ' ' || c == 0177) { + putch('^'); + c ^= 0100; + } + putch(c); + } +} + +/* + * Simple minded routine to print a number + */ + +VOID +prnum(n) long n; { + + putline(getnum(n)); +} + +static char * +fillnum(n, p) + long n; + char *p; +{ + if (n >= 10) { + p = fillnum(n / 10, p); + } + *p++ = (int) (n % 10) + '0'; + *p = '\0'; + return p; +} + +char * +getnum(n) + long n; +{ + static char buf[20]; + + fillnum(n, buf); + return buf; +} diff --git a/commands/yap/output.h b/commands/yap/output.h new file mode 100755 index 000000000..5f04df156 --- /dev/null +++ b/commands/yap/output.h @@ -0,0 +1,69 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +# ifndef _OUTPUT_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +PUBLIC int _ocnt; +PUBLIC char *_optr; + +#define putch(ch) if (1) {if (--_ocnt <= 0) flush(); *_optr++ = (ch);} else + +VOID flush(); +/* + * void flush() + * + * Write the output buffer to the screen + */ + +VOID nflush(); +/* + * void nflush() + * + * Clear output buffer, but do not write it + */ + +int fputch(); +/* + * int fputch(c) + * int c; The character to be printed + * + * Put character "c" in output buffer and flush if necessary. + */ + +VOID putline(); +/* + * void putline(s) + * char *s; The string to be printed + * + * Put string "s" in output buffer etc... + */ + +VOID cputline(); +/* + * void cputline(s) + * char *s; The string to be handled + * + * Put string "s" in the output buffer, expanding control characters + */ + +VOID prnum(); +/* + * void prnum(n) + * long n; The number to be printed + * + * print the number "n", using putch. + */ + +char *getnum(); +/* + * char *getnum(n) + * long n; The number to be converted to a string + * + * Convert a number to a string and return a pointer to it. + */ +# undef PUBLIC diff --git a/commands/yap/pattern.c b/commands/yap/pattern.c new file mode 100755 index 000000000..8bf192695 --- /dev/null +++ b/commands/yap/pattern.c @@ -0,0 +1,160 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif not lint + +# define _PATTERN_ + +# include "in_all.h" +# include "pattern.h" +# include "getline.h" + +# if V8_REGEX +# include <regexp.h> +# endif V8_REGEX + +/* + * Interface to regular expression routines. + * Also: simple minded patterns without meta-characters. + */ + +# if USG_REGEX +static char *pattern; /* Pointer to compiled pattern */ +char *regcmp(), *regex(); +# endif USG_REGEX +# if V8_REGEX +static struct regexp *pattern; +static char *rc_error; +struct regexp *regcomp(); +# endif V8_REGEX + +# if USG_REGEX || V8_REGEX +/* + * Compile a new pattern, but first free previous result. + */ + +char * +re_comp(s) char *s; { + + if (!*s) { + /* + * user wants previous pattern + */ + return (char *) 0; + } + if (pattern) { + /* + * there was a compiled pattern + */ + free(pattern); + pattern = 0; + } +# if USG_REGEX + return (pattern = regcmp(s, (char *) 0)) ? + (char *) 0 : + "Error in pattern"; +# endif USG_REGEX +# if V8_REGEX + pattern = regcomp(s); + if (pattern) return (char *) 0; + if (rc_error) return rc_error; + return "Error in pattern"; +# endif V8_REGEX +} + +# if V8_REGEX +VOID +regerror(str) char *str; { + rc_error = str; +} +# endif V8_REGEX + +/* + * Search for compiled pattern in string "s". Return 0 if not found. + */ + +re_exec(s) char *s; { + +# if USG_REGEX + return !(regex(pattern,s) == 0); +# endif USG_REGEX +# if V8_REGEX +# if _MINIX + return regexec(pattern,s,1); +# else + return regexec(pattern,s); +# endif +# endif V8_REGEX +} +# else +# ifndef BSD_REGEX +/* + * In this case, simple minded pattern search without meta-characters + */ + +char *strcpy(); + +static char *pattern; + +/* + * re_comp : Just remember pattern. + */ + +char * +re_comp(s) char *s; { + + if (!*s) { + /* + * User wants previous pattern + */ + if (!pattern) { + return "No previous regular expression"; + } + return (char *) 0; + } + if (pattern) { + /* + * Free old pattern + */ + free(pattern); + } + pattern = alloc((unsigned) (strlen(s) + 1), 0); + (VOID) strcpy(pattern,s); + return (char *) 0; +} + +/* + * re-exec : Simple minded pattern matcher + */ + +re_exec(s) register char *s; { + + register char *ppat, *pstr; + + for (; *s; s++) { + /* + * As long as there are characters ... + */ + ppat = pattern; /* Try the pattern again */ + pstr = s; + while (*ppat == *pstr) { + if (*++ppat == '\0') { + /* + * The pattern matched! Report success + */ + return 1; + } + if (*++pstr == '\0') { + /* + * Not enough characters left in the string. + * Report failure + */ + return 0; + } + } + } + return 0; /* Failure */ +} +# endif not BSD_REGEX +# endif diff --git a/commands/yap/pattern.h b/commands/yap/pattern.h new file mode 100755 index 000000000..a44498b1c --- /dev/null +++ b/commands/yap/pattern.h @@ -0,0 +1,14 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +# ifndef _PATTERN_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +char * re_comp(); +int re_exec(); + +# undef PUBLIC diff --git a/commands/yap/process.c b/commands/yap/process.c new file mode 100755 index 000000000..2429127b3 --- /dev/null +++ b/commands/yap/process.c @@ -0,0 +1,127 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif + +# define _PROCESS_ + +# include "in_all.h" +# if USG_OPEN +# include <fcntl.h> +# endif +# if BSD4_2_OPEN +# include <sys/file.h> +# endif +# if POSIX_OPEN +# include <sys/types.h> +# include <fcntl.h> +# endif +# include <sys/types.h> +# include <sys/stat.h> +# include "process.h" +# include "commands.h" +# include "display.h" +# include "prompt.h" +# include "getline.h" +# include "main.h" +# include "options.h" +# include "output.h" + +static int nfiles; /* Number of filenames on command line */ + +/* + * Visit a file, file name is "fn". + */ + +VOID +visitfile(fn) char *fn; { + struct stat statbuf; + + if (stdf > 0) { + /* + * Close old input file + */ + (VOID) close(stdf); + } + currentfile = fn; +# if USG_OPEN || BSD4_2_OPEN || POSIX_OPEN + if ((stdf = open(fn,O_RDONLY,0)) < 0) { +# else + if ((stdf = open(fn,0)) < 0) { +# endif + error(": could not open"); + maxpos = 0; + } + else { /* Get size for percentage in prompt */ + (VOID) fstat(stdf, &statbuf); + maxpos = statbuf.st_size; + } + do_clean(); + d_clean(); +} + +/* + * process the input files, one by one. + * If there is none, input is from a pipe. + */ + +VOID +processfiles(n,argv) char ** argv; { + + static char *dummies[3]; + long arg; + + if (!(nfiles = n)) { + /* + * Input from pipe + */ + currentfile = "standard-input"; + /* + * Take care that *(filenames - 1) and *(filenames + 1) are 0 + */ + filenames = &dummies[1]; + d_clean(); + do_clean(); + } + else { + filenames = argv; + (VOID) nextfile(0); + } + *--argv = 0; + if (startcomm) { + n = getcomm(&arg); + if (commands[n].c_flags & NEEDS_SCREEN) { + redraw(0); + } + do_comm(n,arg); + startcomm = 0; + } + redraw(1); + if (setjmp(SetJmpBuf)) { + nflush(); + redraw(1); + } + DoneSetJmp = 1; + for (;;) { + interrupt = 0; + n = getcomm(&arg); + do_comm(n, arg); + } +} + +/* + * Get the next file the user asks for. + */ + +int +nextfile(n) { + register i; + + if ((i = filecount + n) >= nfiles || i < 0) { + return 1; + } + filecount = i; + visitfile(filenames[i]); + return 0; +} diff --git a/commands/yap/process.h b/commands/yap/process.h new file mode 100755 index 000000000..f94517ccc --- /dev/null +++ b/commands/yap/process.h @@ -0,0 +1,48 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +# ifndef _PROCESS_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +# include <setjmp.h> + +PUBLIC jmp_buf SetJmpBuf; +PUBLIC int DoneSetJmp; + +PUBLIC int stdf; /* input file descriptor */ +PUBLIC int filecount; /* index in filename table */ +PUBLIC char ** filenames; /* the filenametable */ +PUBLIC char * currentfile; /* Name of current file */ +PUBLIC long maxpos; /* Size of file */ + +VOID visitfile(); +/* + * void visitfile(fn) + * char *fn; name of file to be visited + * + * Opens the file "fn" and gives an error message if this fails. + */ + +VOID processfiles(); +/* + * void processfiles(n,argv) + * int n; number of files to be handled + * char ** argv; names of the files + * + * Does all the work according to the divide and conquer method + */ + +int nextfile(); +/* + * int nextfile(n) + * int n; + * + * Visits n'th next file. If not there in argument list, return 1. + * Otherwise return 0. + */ + +# undef PUBLIC diff --git a/commands/yap/prompt.c b/commands/yap/prompt.c new file mode 100755 index 000000000..c31c809b5 --- /dev/null +++ b/commands/yap/prompt.c @@ -0,0 +1,193 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif + +# define _PROMPT_ + +# include "in_all.h" +# include "prompt.h" +# include "term.h" +# include "output.h" +# include "options.h" +# include "display.h" +# include "process.h" +# include "getline.h" +# include "main.h" +# include "getcomm.h" +# include "keys.h" +# include "assert.h" +# include "commands.h" + +#define basename(x) x + +#ifndef basename +STATIC char * basename(); +#endif + +static char *errorgiven; /* Set to error message, if there is one */ + +char * +copy(p, ep, s) + register char *p, *s; + char *ep; +{ + while (p < ep && *s) { + *p++ = *s++; + } + return p; +} + +/* + * display the prompt and refresh the screen. + */ + +VOID +give_prompt() { + + register char **name; + register struct scr_info *p = &scr_info; + char buf[256]; + register char *pb = buf; + + if (startcomm) return; + flush(); + if (window()) { + redraw(0); + flush(); + } + if (!stupid) { + /* + * fancy prompt + */ + clrbline(); + standout(); + pb = copy(pb, &buf[255], basename(currentfile)); + if (stdf >= 0) { + pb = copy(pb, &buf[255], ", "); + pb = copy(pb, &buf[255], getnum(p->firstline)); + pb = copy(pb, &buf[255], "-"); + pb = copy(pb, &buf[255], getnum(p->lastline)); + } + } + else { + *pb++ = '\007'; /* Stupid terminal, stupid prompt */ + } + if (errorgiven) { + /* + * display error message + */ + pb = copy(pb, &buf[255], " "); + pb = copy(pb, &buf[255], errorgiven); + if (stupid) { + pb = copy(pb, &buf[255], "\r\n"); + } + errorgiven = 0; + } + else if (!stupid && (status || maxpos)) { + pb = copy(pb, &buf[255], " ("); + name = &filenames[filecount]; + if (status) { + /* + * indicate top and/or bottom + */ + if (status & START) { + if (!*(name - 1)) { + pb = copy(pb, &buf[255], "Top"); + } + else { + pb = copy(pb, &buf[255], "Previous: "); + pb = copy(pb, &buf[255], basename(*(name - 1))); + } + if (status & EOFILE) { + pb = copy(pb, &buf[255], ", "); + } + } + if (status & EOFILE) { + if (!*(name+1)) { + pb = copy(pb, &buf[255], "Bottom"); + } + else { + pb = copy(pb, &buf[255], "Next: "); + pb = copy(pb, &buf[255], basename(*(name + 1))); + } + } + } + else { /* display percentage */ + pb = copy(pb, &buf[255], getnum((100 * getpos(p->lastline))/maxpos)); + pb = copy(pb, &buf[255], "%"); + } + pb = copy(pb, &buf[255], ")"); + } + *pb = '\0'; + if (!stupid) { + buf[COLS-1] = 0; + putline(buf); + standend(); + } + else putline(buf); +} + +/* + * Remember error message + */ + +VOID +error(str) char *str; { + + errorgiven = str; +} + +#ifndef basename +STATIC char * +basename(fn) char *fn; { /* Return name without path */ + + register char *s; + + s = fn; + while (*s++) ; /* Search end of name */ + for (;;) { + if (*--s == '/') { + /* + * Backwards to first '/' + */ + if (*(s+1)) { + /* + * There is a name after the '/' + */ + return s + 1; + } + *s = 0; /* No name after the '/' */ + } + if (s == fn) return s; + } + /* NOTREACHED */ +} +#endif + +VOID +ret_to_continue() { /* Obvious */ + int c; + static char buf[2]; + + for (;;) { + clrbline(); + standout(); + if (errorgiven) { + putline(errorgiven); + putline(" "); + errorgiven = 0; + } + putline("[Type anything to continue]"); + standend(); + if (is_escape(c = getch())) { + buf[0] = c; + (VOID) match(buf, &c, currmap->k_mach); + assert(c > 0); + do_comm(c, -1L); + } + else break; + } + clrbline(); +} diff --git a/commands/yap/prompt.h b/commands/yap/prompt.h new file mode 100755 index 000000000..2164ba037 --- /dev/null +++ b/commands/yap/prompt.h @@ -0,0 +1,33 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +# ifndef _PROMPT_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +VOID give_prompt(); +/* + * void give_prompt() + * + * Displays a prompt, with possibly an error message + */ + +VOID error(); +/* + * void error(s) + * char *s; The error + * + * Takes care that there will be an error message in the next prompt. + */ + +VOID ret_to_continue(); +/* + * void ret_to_continue(); + * + * Asks the user to type something before continuing. + */ + +# undef PUBLIC diff --git a/commands/yap/term.c b/commands/yap/term.c new file mode 100755 index 000000000..35b8e020a --- /dev/null +++ b/commands/yap/term.c @@ -0,0 +1,487 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* + * Terminal handling routines, mostly initializing. + */ + +# ifndef lint +static char rcsid[] = "$Header$"; +# endif + +# define _TERM_ + +#include "in_all.h" +#include "term.h" +#include "machine.h" +#include "output.h" +#include "display.h" +#include "options.h" +#include "getline.h" +#include "keys.h" +#include "main.h" + +#ifdef TIOCGWINSZ +static struct winsize w; +#endif + +char *strcpy(), + *strcat(), + *tgoto(), + *tgetstr(), + *getenv(); + +static char tcbuf1[1024]; /* Holds terminal capability strings */ +static char * ptc; /* Pointer in it */ +static char tcbuf[1024]; /* Another termcap buffer */ +short ospeed; /* Needed for tputs() */ +char PC; /* Needed for tputs() */ +char * UP; /* Needed for tgoto() */ +static char *ll; + +struct linelist _X[100]; /* 100 is enough ? */ + +# if USG_TTY +static struct termio _tty,_svtty; +# elif POSIX_TTY +static struct termios _tty, _svtty; +# else +# ifdef TIOCSPGRP +static int proc_id, saved_pgrpid; +# endif +static struct sgttyb _tty,_svtty; +# ifdef TIOCGETC +static struct tchars _ttyc, _svttyc; +# endif +# ifdef TIOCGLTC +static int line_discipline; +static struct ltchars _lttyc, _svlttyc; +# endif +# endif + +static VOID +handle(c) char *c; { /* if character *c is used, set it to undefined */ + + if (isused(*c)) *c = 0377; +} + +/* + * Set terminal in cbreak mode. + * Also check if tabs need expanding. + */ + +VOID +inittty() { +# if USG_TTY + register struct termio *p = &_tty; + + ioctl(0,TCGETA,(char *) p); + _svtty = *p; + if (p->c_oflag & TAB3) { + /* + * We do tab expansion ourselves + */ + expandtabs = 1; + } + p->c_oflag &= ~(TAB3|OCRNL|ONLRET|ONLCR); + p->c_oflag |= (/*ONOCR|*/OPOST); /* ONOCR does not seem to work + very well in combination with + ~ONLCR + */ + p->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON); + if (isused('S'&037) || isused('Q'&037)) p->c_iflag &= ~IXON; + handle(&(p->c_cc[0])); /* INTR and QUIT (mnemonics not defined ??) */ + handle(&(p->c_cc[1])); + erasech = p->c_cc[VERASE]; + killch = p->c_cc[VKILL]; + p->c_cc[VMIN] = 1; /* Just wait for one character */ + p->c_cc[VTIME] = 0; + ospeed = p->c_cflag & CBAUD; + ioctl(0,TCSETAW,(char *) p); +#elif POSIX_TTY + register struct termios *p = &_tty; + + tcgetattr(0, p); + _svtty = *p; +#ifdef _MINIX /* Should be XTABS */ + if (p->c_oflag & XTABS) { + /* + * We do tab expansion ourselves + */ + expandtabs = 1; + } + p->c_oflag &= (OPOST|XTABS); +#else + p->c_oflag &= ~OPOST; +#endif + p->c_lflag &= ~(ECHO|ECHOE|ECHOK|ECHONL|ICANON); + if (isused('S'&037) || isused('Q'&037)) p->c_iflag &= ~IXON; + handle(&(p->c_cc[VINTR])); + handle(&(p->c_cc[VQUIT])); + erasech = p->c_cc[VERASE]; + killch = p->c_cc[VKILL]; + p->c_cc[VMIN] = 1; /* Just wait for one character */ + p->c_cc[VTIME] = 0; + ospeed = cfgetospeed(p); + tcsetattr(0, TCSANOW, p); +# else + register struct sgttyb *p = &_tty; + +# ifdef TIOCSPGRP + /* + * If we can, we put yap in another process group, and the terminal + * with it. This is done, so that interrupts given by the user + * will only affect yap and not it's children (processes writing + * on a pipe to yap) + */ + if (ioctl(0, TIOCSPGRP, (char *) &proc_id) != -1) { + setpgrp(0, proc_id); + } +# endif + ioctl(0,TIOCGETP,(char *) p); + _svtty = *p; + erasech = p->sg_erase; + killch = p->sg_kill; + ospeed = p->sg_ospeed; + if (p->sg_flags & XTABS) { + /* + * We do tab expansion ourselves + */ + expandtabs = 1; + } + p->sg_flags |= (CBREAK); + p->sg_flags &= ~(ECHO|XTABS|RAW|LCASE|CRMOD); +#ifdef TIOCSETN + ioctl(0, TIOCSETN, (char *) p); +#else + ioctl(0,TIOCSETP,(char *) p); +#endif +/* Bloody Sun ... */ +#undef t_startc +#undef t_stopc +#undef t_intrc +#undef t_quitc +#undef t_suspc +#undef t_dsuspc +#undef t_flushc +#undef t_lnextc +# ifdef TIOCGETC + { register struct tchars *q = &_ttyc; + + ioctl(0,TIOCGETC,(char *) q); + _svttyc = *q; + handle(&(q->t_intrc)); + handle(&(q->t_quitc)); + if (isused(q->t_startc) || isused(q->t_stopc)) { + q->t_startc = q->t_stopc = 0377; + } + ioctl(0,TIOCSETC, (char *) q); + } +# endif +# ifdef TIOCGLTC + { register struct ltchars *q = &_lttyc; + + ioctl(0,TIOCGETD,(char *) &line_discipline); + if (line_discipline == NTTYDISC) { + ioctl(0, TIOCGLTC,(char *) q); + _svlttyc = *q; + handle(&(q->t_suspc)); + handle(&(q->t_dsuspc)); + q->t_flushc = q->t_lnextc = 0377; + ioctl(0,TIOCSLTC, (char *) q); + } + } +# endif +# endif +} + +/* + * Reset the terminal to its original state + */ + +VOID +resettty() { + +# if USG_TTY + ioctl(0,TCSETAW,(char *) &_svtty); +# elif POSIX_TTY + tcsetattr(0, TCSANOW, &_svtty); +# else +# ifdef TIOCSPGRP + ioctl(0, TIOCSPGRP, (char *) &saved_pgrpid); + setpgrp(0, saved_pgrpid); +# endif + ioctl(0,TIOCSETP,(char *) &_svtty); +# ifdef TIOCGETC + ioctl(0,TIOCSETC, (char *) &_svttyc); +# endif +# ifdef TIOCGLTC + if (line_discipline == NTTYDISC) ioctl(0,TIOCSLTC, (char *) &_svlttyc); +# endif +# endif + putline(TE); + flush(); +} + +/* + * Get terminal capability "cap". + * If not present, return an empty string. + */ + +STATIC char * +getcap(cap) char *cap; { + register char *s; + + s = tgetstr(cap, &ptc); + if (!s) return ""; + return s; +} + +/* + * Initialize some terminal-dependent stuff. + */ + +VOID +ini_terminal() { + + register char * s; + register struct linelist *lp, *lp1; + register i; + register UG, SG; + char tempbuf[20]; + char *mb, *mh, *mr; /* attributes */ + + initkeys(); +#if !_MINIX +# ifdef TIOCSPGRP + proc_id = getpid(); + ioctl(0,TIOCGPGRP, (char *) &saved_pgrpid); +# endif +#endif + inittty(); + stupid = 1; + ptc = tcbuf1; + BC = "\b"; + TA = "\t"; + if (!(s = getenv("TERM"))) s = "dumb"; + if (tgetent(tcbuf, s) <= 0) { + panic("No termcap entry"); + } + stupid = 0; + hardcopy = tgetflag("hc"); /* Hard copy terminal?*/ + PC = *(getcap("pc")); + if (*(s = getcap("bc"))) { + /* + * Backspace if not ^H + */ + BC = s; + } + UP = getcap("up"); /* move up a line */ + CE = getcap("ce"); /* clear to end of line */ + CL = getcap("cl"); /* clear screen */ + if (!*CL) cflag = 1; + TI = getcap("ti"); /* Initialization for CM */ + TE = getcap("te"); /* end for CM */ + CM = getcap("cm"); /* cursor addressing */ + SR = getcap("sr"); /* scroll reverse */ + AL = getcap("al"); /* Insert line */ + SO = getcap("so"); /* standout */ + SE = getcap("se"); /* standend */ + SG = tgetnum("sg"); /* blanks left by SO, SE */ + if (SG < 0) SG = 0; + US = getcap("us"); /* underline */ + UE = getcap("ue"); /* end underline */ + UG = tgetnum("ug"); /* blanks left by US, UE */ + if (UG < 0) UG = 0; + UC = getcap("uc"); /* underline a character */ + mb = getcap("mb"); /* blinking attribute */ + MD = getcap("md"); /* bold attribute */ + ME = getcap("me"); /* turn off attributes */ + mh = getcap("mh"); /* half bright attribute */ + mr = getcap("mr"); /* reversed video attribute */ + if (!nflag) { + /* + * Recognize special strings + */ + (VOID) addstring(SO,SG,&sppat); + (VOID) addstring(SE,SG,&sppat); + (VOID) addstring(US,UG,&sppat); + (VOID) addstring(UE,UG,&sppat); + (VOID) addstring(mb,0,&sppat); + (VOID) addstring(MD,0,&sppat); + (VOID) addstring(ME,0,&sppat); + (VOID) addstring(mh,0,&sppat); + (VOID) addstring(mr,0,&sppat); + if (*UC) { + (VOID) strcpy(tempbuf,BC); + (VOID) strcat(tempbuf,UC); + (VOID) addstring(tempbuf,0,&sppat); + } + } + if (UG > 0 || uflag) { + US = ""; + UE = ""; + } + if (*US || uflag) UC = ""; + COLS = tgetnum("co"); /* columns on page */ + i = tgetnum("li"); /* Lines on page */ + AM = tgetflag("am"); /* terminal wraps automatically? */ + XN = tgetflag("xn"); /* and then ignores next newline? */ + DB = tgetflag("db"); /* terminal retains lines below */ + if (!*(s = getcap("ho")) && *CM) { + s = tgoto(CM,0,0); /* Another way of getting home */ + } + if ((!*CE && !*AL) || !*s || hardcopy) { + cflag = stupid = 1; + } + (VOID) strcpy(HO,s); + if (*(s = getcap("ta"))) { + /* + * Tab (other than ^I or padding) + */ + TA = s; + } + if (!*(ll = getcap("ll")) && *CM && i > 0) { + /* + * Lower left hand corner + */ + (VOID) strcpy(BO, tgoto(CM,0,i-1)); + } + else (VOID) strcpy(BO, ll); + if (COLS <= 0 || COLS > 256) { + if ((unsigned) COLS >= 65409) { + /* SUN bug */ + COLS &= 0xffff; + COLS -= (65409 - 128); + } + if (COLS <= 0 || COLS > 256) COLS = 80; + } + if (i <= 0) { + i = 24; + cflag = stupid = 1; + } + LINES = i; + maxpagesize = i - 1; + scrollsize = maxpagesize / 2; + if (scrollsize <= 0) scrollsize = 1; + if (!pagesize || pagesize >= i) { + pagesize = maxpagesize; + } + + /* + * The next part does not really belong here, but there it is ... + * Initialize a circular list for the screenlines. + */ + + scr_info.tail = lp = _X; + lp1 = lp + (100 - 1); + for (; lp <= lp1; lp++) { + /* + * Circular doubly linked list + */ + lp->next = lp + 1; + lp->prev = lp - 1; + } + lp1->next = scr_info.tail; + lp1->next->prev = lp1; + if (stupid) { + (VOID) strcpy(BO,"\r\n"); + } + putline(TI); + window(); +} + +/* + * Place cursor at start of line n. + */ + +VOID +mgoto(n) register n; { + + if (n == 0) home(); + else if (n == maxpagesize && *BO) bottom(); + else if (*CM) { + /* + * Cursor addressing + */ + tputs(tgoto(CM,0,n),1,fputch); + } + else if (*BO && *UP && n >= (maxpagesize >> 1)) { + /* + * Bottom and then up + */ + bottom(); + while (n++ < maxpagesize) putline(UP); + } + else { /* Home, and then down */ + home(); + while (n--) putline("\r\n"); + } +} + +/* + * Clear bottom line + */ + +VOID +clrbline() { + + if (stupid) { + putline("\r\n"); + return; + } + bottom(); + if (*CE) { + /* + * We can clear to end of line + */ + clrtoeol(); + return; + } +# ifdef VT100_PATCH + insert_line(maxpagesize); +# else + insert_line(); +# endif +} + +# ifdef VT100_PATCH +ins_line(l) { + tputs(tgoto(AL, l, 0), maxpagesize - l, fputch); +} +# endif + +VOID +home() { + + tputs(HO,1,fputch); +} + +VOID +bottom() { + + tputs(BO,1,fputch); + if (!*BO) mgoto(maxpagesize); +} + +int +window() +{ +#ifdef TIOCGWINSZ + if (ioctl(1, TIOCGWINSZ, &w) < 0) return 0; + + if (w.ws_col == 0) w.ws_col = COLS; + if (w.ws_row == 0) w.ws_row = LINES; + if (w.ws_col != COLS || w.ws_row != LINES) { + COLS = w.ws_col; + LINES = w.ws_row; + maxpagesize = LINES - 1; + pagesize = maxpagesize; + if (! *ll) (VOID) strcpy(BO, tgoto(CM,0,maxpagesize)); + scr_info.currentpos = 0; + scrollsize = maxpagesize / 2; + if (scrollsize <= 0) scrollsize = 1; + return 1; + } +#endif + return 0; +} diff --git a/commands/yap/term.h b/commands/yap/term.h new file mode 100755 index 000000000..fb696a293 --- /dev/null +++ b/commands/yap/term.h @@ -0,0 +1,137 @@ +/* Copyright (c) 1985 Ceriel J.H. Jacobs */ + +/* $Header$ */ + +/* All terminal and terminal dependent stuff */ + +# ifndef _TERM_ +# define PUBLIC extern +# else +# define PUBLIC +# endif + +# if USG_TTY +# include <termio.h> +# elif POSIX_TTY +# include <termios.h> +# else +# include <sgtty.h> +# endif + +#include <sys/types.h> +#include <signal.h> +#include <sys/ioctl.h> + +/* Terminal setting */ + +PUBLIC int expandtabs; /* Tabs need expanding? */ +PUBLIC int stupid; /* Stupid terminal */ +PUBLIC int hardcopy; /* Hardcopy terminal */ + +/* termcap stuff */ +PUBLIC +char *CE, /* clear to end of line */ + *CL, /* clear screen */ + *SO, /* stand out */ + *SE, /* stand end */ + *US, /* underline start */ + *UE, /* underline end */ + *UC, /* underline character */ + *MD, /* bold start */ + *ME, /* attributes (like bold) off */ + *TI, /* initialize for CM */ + *TE, /* End of CM */ + *CM, /* Cursor addressing */ + *TA, /* Tab */ + *SR, /* Scroll reverse */ + *AL; /* insert line */ +PUBLIC +int LINES, /* # of lines on screen */ + COLS, /* # of colums */ + AM, /* Automatic margins */ + XN, /* newline ignored after wrap */ + DB; /* terminal retains lines below */ +PUBLIC +char HO[20], /* Sequence to get to home position */ + BO[20]; /* sequence to get to lower left hand corner */ +PUBLIC +int erasech, /* users erase character */ + killch; /* users kill character */ +PUBLIC struct state *sppat; /* Special patterns to be recognized */ +PUBLIC char + *BC; /* Back space */ + +#define backspace() putline(BC) +#define clrscreen() tputs(CL,LINES,fputch) +#define clrtoeol() tputs(CE,1,fputch) +#define scrollreverse() tputs(SR,LINES,fputch) +#ifdef VT100_PATCH +#define insert_line(l) ins_line(l) +#define standout() tputs(SO,1,fputch) +#define standend() tputs(SE,1,fputch) +#define underline() tputs(US,1,fputch) +#define end_underline() tputs(UE,1,fputch) +#define bold() tputs(MD,1,fputch) +#define end_bold() tputs(ME,1,fputch) +#define underchar() tputs(UC,1,fputch) +# else +#define insert_line() tputs(AL,LINES,fputch) +#define standout() putline(SO) +#define standend() putline(SE) +#define underline() putline(US) +#define end_underline() putline(UE) +#define bold() putline(MD) +#define end_bold() putline(ME) +#define underchar() putline(UC) +# endif +#define givetab() tputs(TA,1,fputch) + +VOID inittty(); +/* + * void inittty() + * + * Initialises the terminal (sets it in cbreak mode, etc) + */ + +VOID resettty(); +/* + * void resettty() + * + * resets the terminal to the mode in which it was before yap was invoked + */ + +VOID ini_terminal(); +/* + * void ini_terminal() + * + * Handles the termcap entry for your terminal. In some cases, the terminal + * will be considered stupid. + */ + +VOID mgoto(); +/* + * void mgoto(n) + * int n; Line to go to + * + * Put the cursor at the start of the n'th screen line. + * This can be done in several ways (of course). + */ + +VOID clrbline(); +/* + * void clrbline() + * + * clears the bottom line, by either clearing it to end of line, + * or pushing it of the screen by inserting a line before it. + */ + +VOID home(); +VOID bottom(); +/* + * Obvious + */ + +#ifdef WINDOW +int window(); +#endif +# undef PUBLIC diff --git a/commands/zmodem/Makefile b/commands/zmodem/Makefile new file mode 100755 index 000000000..221e40e2d --- /dev/null +++ b/commands/zmodem/Makefile @@ -0,0 +1,24 @@ +# Makefile for zmodem + +all: rz sz + +CFLAGS= -DPOSIX -D_MINIX -D_POSIX_SOURCE -O -wo + +rz: rz.c rbsb.c zm.c zmodem.h + cc $(CFLAGS) -i -o rz rz.c + install -S 5kw $@ + +sz: sz.c rbsb.c zm.c zmodem.h + cc $(CFLAGS) -i -o sz sz.c + install -S 5kw $@ + +install: /usr/bin/rz /usr/bin/sz + +/usr/bin/rz: rz + install -cs -o bin rz $@ + +/usr/bin/sz: sz + install -cs -o bin sz $@ + +clean: + rm -f *.bak *.o core rz sz diff --git a/commands/zmodem/crctab.c b/commands/zmodem/crctab.c new file mode 100755 index 000000000..6811b3daf --- /dev/null +++ b/commands/zmodem/crctab.c @@ -0,0 +1,140 @@ +/* + * Crc calculation stuff + */ + +/* crctab calculated by Mark G. Mendel, Network Systems Corporation */ +static unsigned short crctab[256] = { + 0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50a5, 0x60c6, 0x70e7, + 0x8108, 0x9129, 0xa14a, 0xb16b, 0xc18c, 0xd1ad, 0xe1ce, 0xf1ef, + 0x1231, 0x0210, 0x3273, 0x2252, 0x52b5, 0x4294, 0x72f7, 0x62d6, + 0x9339, 0x8318, 0xb37b, 0xa35a, 0xd3bd, 0xc39c, 0xf3ff, 0xe3de, + 0x2462, 0x3443, 0x0420, 0x1401, 0x64e6, 0x74c7, 0x44a4, 0x5485, + 0xa56a, 0xb54b, 0x8528, 0x9509, 0xe5ee, 0xf5cf, 0xc5ac, 0xd58d, + 0x3653, 0x2672, 0x1611, 0x0630, 0x76d7, 0x66f6, 0x5695, 0x46b4, + 0xb75b, 0xa77a, 0x9719, 0x8738, 0xf7df, 0xe7fe, 0xd79d, 0xc7bc, + 0x48c4, 0x58e5, 0x6886, 0x78a7, 0x0840, 0x1861, 0x2802, 0x3823, + 0xc9cc, 0xd9ed, 0xe98e, 0xf9af, 0x8948, 0x9969, 0xa90a, 0xb92b, + 0x5af5, 0x4ad4, 0x7ab7, 0x6a96, 0x1a71, 0x0a50, 0x3a33, 0x2a12, + 0xdbfd, 0xcbdc, 0xfbbf, 0xeb9e, 0x9b79, 0x8b58, 0xbb3b, 0xab1a, + 0x6ca6, 0x7c87, 0x4ce4, 0x5cc5, 0x2c22, 0x3c03, 0x0c60, 0x1c41, + 0xedae, 0xfd8f, 0xcdec, 0xddcd, 0xad2a, 0xbd0b, 0x8d68, 0x9d49, + 0x7e97, 0x6eb6, 0x5ed5, 0x4ef4, 0x3e13, 0x2e32, 0x1e51, 0x0e70, + 0xff9f, 0xefbe, 0xdfdd, 0xcffc, 0xbf1b, 0xaf3a, 0x9f59, 0x8f78, + 0x9188, 0x81a9, 0xb1ca, 0xa1eb, 0xd10c, 0xc12d, 0xf14e, 0xe16f, + 0x1080, 0x00a1, 0x30c2, 0x20e3, 0x5004, 0x4025, 0x7046, 0x6067, + 0x83b9, 0x9398, 0xa3fb, 0xb3da, 0xc33d, 0xd31c, 0xe37f, 0xf35e, + 0x02b1, 0x1290, 0x22f3, 0x32d2, 0x4235, 0x5214, 0x6277, 0x7256, + 0xb5ea, 0xa5cb, 0x95a8, 0x8589, 0xf56e, 0xe54f, 0xd52c, 0xc50d, + 0x34e2, 0x24c3, 0x14a0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405, + 0xa7db, 0xb7fa, 0x8799, 0x97b8, 0xe75f, 0xf77e, 0xc71d, 0xd73c, + 0x26d3, 0x36f2, 0x0691, 0x16b0, 0x6657, 0x7676, 0x4615, 0x5634, + 0xd94c, 0xc96d, 0xf90e, 0xe92f, 0x99c8, 0x89e9, 0xb98a, 0xa9ab, + 0x5844, 0x4865, 0x7806, 0x6827, 0x18c0, 0x08e1, 0x3882, 0x28a3, + 0xcb7d, 0xdb5c, 0xeb3f, 0xfb1e, 0x8bf9, 0x9bd8, 0xabbb, 0xbb9a, + 0x4a75, 0x5a54, 0x6a37, 0x7a16, 0x0af1, 0x1ad0, 0x2ab3, 0x3a92, + 0xfd2e, 0xed0f, 0xdd6c, 0xcd4d, 0xbdaa, 0xad8b, 0x9de8, 0x8dc9, + 0x7c26, 0x6c07, 0x5c64, 0x4c45, 0x3ca2, 0x2c83, 0x1ce0, 0x0cc1, + 0xef1f, 0xff3e, 0xcf5d, 0xdf7c, 0xaf9b, 0xbfba, 0x8fd9, 0x9ff8, + 0x6e17, 0x7e36, 0x4e55, 0x5e74, 0x2e93, 0x3eb2, 0x0ed1, 0x1ef0 +}; + +/* + * updcrc macro derived from article Copyright (C) 1986 Stephen Satchell. + * NOTE: First srgument must be in range 0 to 255. + * Second argument is referenced twice. + * + * Programmers may incorporate any or all code into their programs, + * giving proper credit within the source. Publication of the + * source routines is permitted so long as proper credit is given + * to Stephen Satchell, Satchell Evaluations and Chuck Forsberg, + * Omen Technology. + */ + +#define updcrc(cp, crc) ( crctab[((crc >> 8) & 255)] ^ (crc << 8) ^ cp) + +/* + * Copyright (C) 1986 Gary S. Brown. You may use this program, or + * code or tables extracted from it, as desired without restriction. + */ + +/* First, the polynomial itself and its table of feedback terms. The */ +/* polynomial is */ +/* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ +/* Note that we take it "backwards" and put the highest-order term in */ +/* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ +/* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ +/* the MSB being 1. */ + +/* Note that the usual hardware shift register implementation, which */ +/* is what we're using (we're merely optimizing it by doing eight-bit */ +/* chunks at a time) shifts bits into the lowest-order term. In our */ +/* implementation, that means shifting towards the right. Why do we */ +/* do it this way? Because the calculated CRC must be transmitted in */ +/* order from highest-order term to lowest-order term. UARTs transmit */ +/* characters in order from LSB to MSB. By storing the CRC this way, */ +/* we hand it to the UART in the order low-byte to high-byte; the UART */ +/* sends each low-bit to hight-bit; and the result is transmission bit */ +/* by bit from highest- to lowest-order term without requiring any bit */ +/* shuffling on our part. Reception works similarly. */ + +/* The feedback terms table consists of 256, 32-bit entries. Notes: */ +/* */ +/* The table can be generated at runtime if desired; code to do so */ +/* is shown later. It might not be obvious, but the feedback */ +/* terms simply represent the results of eight shift/xor opera- */ +/* tions for all combinations of data and CRC register values. */ +/* */ +/* The values must be right-shifted by eight bits by the "updcrc" */ +/* logic; the shift must be unsigned (bring in zeroes). On some */ +/* hardware you could probably optimize the shift in assembler by */ +/* using byte-swap instructions. */ + +static long cr3tab[] = { /* CRC polynomial 0xedb88320 */ +0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, +0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91, +0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de, 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7, +0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec, 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5, +0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172, 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b, +0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940, 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59, +0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116, 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f, +0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924, 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d, +0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a, 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433, +0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818, 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01, +0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e, 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457, +0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c, 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65, +0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2, 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb, +0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0, 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9, +0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086, 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f, +0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4, 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad, +0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a, 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683, +0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8, 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1, +0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe, 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7, +0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc, 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5, +0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252, 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b, +0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60, 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79, +0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236, 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f, +0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04, 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d, +0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a, 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713, +0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38, 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21, +0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e, 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777, +0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c, 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45, +0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2, 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db, +0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0, 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9, +0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6, 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf, +0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94, 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d +}; + +#ifdef NFGM +long +UPDC32(b, c) +long c; +{ + return (cr3tab[((int)c ^ b) & 0xff] ^ ((c >> 8) & 0x00FFFFFF)); +} + +#else + +#define UPDC32(b, c) (cr3tab[((int)c ^ b) & 0xff] ^ ((c >> 8) & 0x00FFFFFF)) +#endif + +/* End of crctab.c */ diff --git a/commands/zmodem/rbsb.c b/commands/zmodem/rbsb.c new file mode 100755 index 000000000..e139bfefc --- /dev/null +++ b/commands/zmodem/rbsb.c @@ -0,0 +1,421 @@ +/* + * + * Rev 05-05-1988 + * This file contains Unix specific code for setting terminal modes, + * very little is specific to ZMODEM or YMODEM per se (that code is in + * sz.c and rz.c). The CRC-16 routines used by XMODEM, YMODEM, and ZMODEM + * are also in this file, a fast table driven macro version + * + * V7/BSD HACKERS: SEE NOTES UNDER mode(2) !!! + * + * This file is #included so the main file can set parameters such as HOWMANY. + * See the main files (rz.c/sz.c) for compile instructions. + */ + +#ifdef V7 +#include <sys/types.h> +#include <sys/stat.h> +#include <sgtty.h> +#define OS "V7/BSD" +#ifdef LLITOUT +long Locmode; /* Saved "local mode" for 4.x BSD "new driver" */ +long Locbit = LLITOUT; /* Bit SUPPOSED to disable output translations */ +#include <strings.h> +#endif +#endif + +#ifdef POSIX +#include <sys/types.h> +#include <sys/stat.h> +#include <termios.h> +#define OS "POSIX" +#endif + +#ifndef OS +#ifndef USG +#define USG +#endif +#endif + +#ifdef USG +#include <sys/types.h> +#include <sys/stat.h> +#include <termio.h> +#include <sys/ioctl.h> +#define OS "SYS III/V" +#define MODE2OK +#include <string.h> +#endif + +#include "zmodem.h" + +_PROTOTYPE(static unsigned getspeed , (int code )); + +#if HOWMANY > 255 +Howmany must be 255 or less +#endif + +/* + * return 1 iff stdout and stderr are different devices + * indicating this program operating with a modem on a + * different line + */ +int Fromcu; /* Were called from cu or yam */ + +void from_cu() +{ + struct stat a, b; + + fstat(1, &a); fstat(2, &b); + Fromcu = a.st_rdev != b.st_rdev; + return; +} + +void cucheck() +{ + if (Fromcu) + fprintf(stderr,"Please read the manual page BUGS chapter!\r\n"); +} + + +struct { + unsigned baudr; + int speedcode; +} speeds[] = { + 110, B110, + 300, B300, +#ifdef B600 + 600, B600, +#endif + 1200, B1200, + 2400, B2400, + 4800, B4800, + 9600, B9600, +#ifdef EXTA + 19200, EXTA, + 38400, EXTB, +#endif + 0, +}; + +int Twostop; /* Use two stop bits */ + + +#ifndef READCHECK +#ifdef FIONREAD +#define READCHECK +/* + * Return non 0 iff something to read from io descriptor f + */ +int rdchk(f) +{ + static long lf; + + ioctl(f, FIONREAD, &lf); + return ((int) lf); +} +#endif +#ifdef SV +#define READCHECK +#include <fcntl.h> + +char checked = '\0' ; +/* + * Nonblocking I/O is a bit different in System V, Release 2 + */ +int rdchk(f) +{ + int lf, savestat; + + savestat = fcntl(f, F_GETFL) ; + fcntl(f, F_SETFL, savestat | O_NDELAY) ; + lf = read(f, &checked, 1) ; + fcntl(f, F_SETFL, savestat) ; + return(lf) ; +} +#endif +#endif + + +static unsigned +getspeed(code) +int code; +{ + register n; + + for (n=0; speeds[n].baudr; ++n) + if (speeds[n].speedcode == code) + return speeds[n].baudr; + return 38400; /* Assume fifo if ioctl failed */ +} + + + +#ifdef POSIX +struct termios oldtty, tty; +#else +#ifdef ICANON +struct termio oldtty, tty; +#else +struct sgttyb oldtty, tty; +struct tchars oldtch, tch; +#endif +#endif + +int iofd = 0; /* File descriptor for ioctls & reads */ + +/* + * mode(n) + * 3: save old tty stat, set raw mode with flow control + * 2: set XON/XOFF for sb/sz with ZMODEM or YMODEM-g + * 1: save old tty stat, set raw mode + * 0: restore original tty mode + */ +int mode(n) +int n; +{ + static did0 = FALSE; + + vfile("mode:%d", n); + switch(n) { +#ifdef POSIX + case 2: /* Un-raw mode used by sz, sb when -g detected */ + if(!did0) + (void) tcgetattr(iofd, &oldtty); + tty = oldtty; + + tty.c_iflag = BRKINT|IXON; + + tty.c_oflag = 0; /* Transparent output */ + + tty.c_cflag &= ~PARENB; /* Disable parity */ + tty.c_cflag |= CS8; /* Set character size = 8 */ + if (Twostop) + tty.c_cflag |= CSTOPB; /* Set two stop bits */ + + + tty.c_lflag = ISIG; + tty.c_cc[VINTR] = Zmodem ? 03:030; /* Interrupt char */ + tty.c_cc[VQUIT] = -1; /* Quit char */ + tty.c_cc[VMIN] = 3; /* This many chars satisfies reads */ + tty.c_cc[VTIME] = 1; /* or in this many tenths of seconds */ + + (void) tcsetattr(iofd, TCSANOW, &tty); + did0 = TRUE; + return OK; + case 1: + case 3: + if(!did0) + (void) tcgetattr(iofd, &oldtty); + tty = oldtty; + + tty.c_iflag = n==3 ? (IGNBRK|IXOFF) : IGNBRK; + + /* No echo, crlf mapping, INTR, QUIT, delays, no erase/kill */ + tty.c_lflag &= ~(ECHO | ECHONL | ICANON | ISIG | IEXTEN); + + tty.c_oflag = 0; /* Transparent output */ + + tty.c_cflag &= ~PARENB; /* Same baud rate, disable parity */ + tty.c_cflag |= CS8; /* Set character size = 8 */ + if (Twostop) + tty.c_cflag |= CSTOPB; /* Set two stop bits */ + tty.c_cc[VMIN] = HOWMANY; /* This many chars satisfies reads */ + tty.c_cc[VTIME] = 1; /* or in this many tenths of seconds */ + (void) tcsetattr(iofd, TCSANOW, &tty); + did0 = TRUE; + Baudrate = cfgetospeed(&tty); + return OK; +#endif +#ifdef USG + case 2: /* Un-raw mode used by sz, sb when -g detected */ + if(!did0) + (void) ioctl(iofd, TCGETA, &oldtty); + tty = oldtty; + + tty.c_iflag = BRKINT|IXON; + + tty.c_oflag = 0; /* Transparent output */ + + tty.c_cflag &= ~PARENB; /* Disable parity */ + tty.c_cflag |= CS8; /* Set character size = 8 */ + if (Twostop) + tty.c_cflag |= CSTOPB; /* Set two stop bits */ + + +#ifdef READCHECK + tty.c_lflag = Zmodem ? 0 : ISIG; + tty.c_cc[VINTR] = Zmodem ? -1:030; /* Interrupt char */ +#else + tty.c_lflag = ISIG; + tty.c_cc[VINTR] = Zmodem ? 03:030; /* Interrupt char */ +#endif + tty.c_cc[VQUIT] = -1; /* Quit char */ +#ifdef NFGVMIN + tty.c_cc[VMIN] = 1; +#else + tty.c_cc[VMIN] = 3; /* This many chars satisfies reads */ +#endif + tty.c_cc[VTIME] = 1; /* or in this many tenths of seconds */ + + (void) ioctl(iofd, TCSETAW, &tty); + did0 = TRUE; + return OK; + case 1: + case 3: + if(!did0) + (void) ioctl(iofd, TCGETA, &oldtty); + tty = oldtty; + + tty.c_iflag = n==3 ? (IGNBRK|IXOFF) : IGNBRK; + + /* No echo, crlf mapping, INTR, QUIT, delays, no erase/kill */ + tty.c_lflag &= ~(ECHO | ICANON | ISIG); + + tty.c_oflag = 0; /* Transparent output */ + + tty.c_cflag &= ~PARENB; /* Same baud rate, disable parity */ + tty.c_cflag |= CS8; /* Set character size = 8 */ + if (Twostop) + tty.c_cflag |= CSTOPB; /* Set two stop bits */ +#ifdef NFGVMIN + tty.c_cc[VMIN] = 1; /* This many chars satisfies reads */ +#else + tty.c_cc[VMIN] = HOWMANY; /* This many chars satisfies reads */ +#endif + tty.c_cc[VTIME] = 1; /* or in this many tenths of seconds */ + (void) ioctl(iofd, TCSETAW, &tty); + did0 = TRUE; + Baudrate = getspeed(tty.c_cflag & CBAUD); + return OK; +#endif +#ifdef V7 + /* + * NOTE: this should transmit all 8 bits and at the same time + * respond to XOFF/XON flow control. If no FIONREAD or other + * READCHECK alternative, also must respond to INTRRUPT char + * This doesn't work with V7. It should work with LLITOUT, + * but LLITOUT was broken on the machine I tried it on. + */ + case 2: /* Un-raw mode used by sz, sb when -g detected */ + if(!did0) { +#ifdef TIOCEXCL + ioctl(iofd, TIOCEXCL, 0); +#endif + ioctl(iofd, TIOCGETP, &oldtty); + ioctl(iofd, TIOCGETC, (struct sgttyb *) &oldtch); +#ifdef LLITOUT + ioctl(iofd, TIOCLGET, &Locmode); +#endif + } + tty = oldtty; + tch = oldtch; +#ifdef READCHECK + tch.t_intrc = Zmodem ? -1:030; /* Interrupt char */ +#else + tch.t_intrc = Zmodem ? 03:030; /* Interrupt char */ +#endif +#ifdef ODDP + tty.sg_flags |= ODDP; +#endif +#ifdef EVENP + tty.sg_flags |= EVENP; +#endif +#ifdef CBREAK + tty.sg_flags |= CBREAK; +#endif +#ifdef ALLDELAY + tty.sg_flags &= ~ALLDELAY; +#endif +#ifdef CRMOD + tty.sg_flags &= ~CRMOD; +#endif +#ifdef ECHO + tty.sg_flags &= ~ECHO; +#endif +#ifdef LCASE + tty.sg_flags &= ~LCASE; +#endif + + ioctl(iofd, TIOCSETP, &tty); + ioctl(iofd, TIOCSETC, (struct sgttyb *) &tch); +#ifdef LLITOUT + ioctl(iofd, TIOCLBIS, &Locbit); +#endif + bibi(99); /* un-raw doesn't work w/o lit out */ + did0 = TRUE; + return OK; + case 1: + case 3: + if(!did0) { +#ifdef TIOCEXCL + ioctl(iofd, TIOCEXCL, 0); +#endif + ioctl(iofd, TIOCGETP, &oldtty); + ioctl(iofd, TIOCGETC, (struct sgttyb *) &oldtch); +#ifdef LLITOUT + ioctl(iofd, TIOCLGET, &Locmode); +#endif + } + tty = oldtty; + tty.sg_flags |= RAW; + tty.sg_flags &= ~ECHO; + ioctl(iofd, TIOCSETP, &tty); + did0 = TRUE; + Baudrate = getspeed(tty.sg_ospeed); + return OK; +#endif + case 0: + if(!did0) + return ERROR; +#ifdef POSIX + /* Wait for output to drain, flush input queue, restore + * modes and restart output. + */ + (void) tcsetattr(iofd, TCSAFLUSH, &oldtty); + (void) tcflow(iofd, TCOON); +#endif +#ifdef USG + (void) ioctl(iofd, TCSBRK, 1); /* Wait for output to drain */ + (void) ioctl(iofd, TCFLSH, 1); /* Flush input queue */ + (void) ioctl(iofd, TCSETAW, &oldtty); /* Restore modes */ + (void) ioctl(iofd, TCXONC,1); /* Restart output */ +#endif +#ifdef V7 + ioctl(iofd, TIOCSETP, &oldtty); + ioctl(iofd, TIOCSETC, (struct sgttyb *) &oldtch); +#ifdef TIOCNXCL + ioctl(iofd, TIOCNXCL, 0); +#endif +#ifdef LLITOUT + ioctl(iofd, TIOCLSET, &Locmode); +#endif +#endif + + return OK; + default: + return ERROR; + } +} + +void sendbrk() +{ +#ifdef POSIX + tcsendbreak(iofd, 1); +#endif +#ifdef V7 +#ifdef TIOCSBRK +#define CANBREAK + sleep(1); + ioctl(iofd, TIOCSBRK, 0); + sleep(1); + ioctl(iofd, TIOCCBRK, 0); +#endif +#endif +#ifdef USG +#define CANBREAK + ioctl(iofd, TCSBRK, 0); +#endif +} + +/* End of rbsb.c */ diff --git a/commands/zmodem/rz.c b/commands/zmodem/rz.c new file mode 100755 index 000000000..2661eadb5 --- /dev/null +++ b/commands/zmodem/rz.c @@ -0,0 +1,1549 @@ +#define VERSION "2.03 05-17-88" +#define PUBDIR "/usr/spool/uucppublic" + +/*% cc -compat -M2 -Ox -K -i -DMD -DOMEN % -o rz; size rz; +<-xtx-*> cc386 -Ox -DMD -DOMEN -DSEGMENTS=8 rz.c -o $B/rz; size $B/rz + * + * rz.c By Chuck Forsberg + * + * cc -O rz.c -o rz USG (3.0) Unix + * cc -O -DV7 rz.c -o rz Unix V7, BSD 2.8 - 4.3 + * + * ln rz rb; ln rz rx For either system + * + * ln rz /usr/bin/rzrmail For remote mail. Make this the + * login shell. rzrmail then calls + * rmail(1) to deliver mail. + * + * To compile on VMS: + * + * define LNK$LIBRARY SYS$LIBRARY:VAXCRTL.OLB + * cc rz.c + * cc vvmodem.c + * link rz,vvmodem + * rz :== $disk:[username.subdir]rz.exe + * + * + * Unix is a trademark of Western Electric Company + * + * A program for Unix to receive files and commands from computers running + * Professional-YAM, PowerCom, YAM, IMP, or programs supporting XMODEM. + * rz uses Unix buffered input to reduce wasted CPU time. + * + * Iff the program is invoked by rzCOMMAND, output is piped to + * "COMMAND filename" (Unix only) + * + * Some systems (Venix, Coherent, Regulus) may not support tty raw mode + * read(2) the same way as Unix. ONEREAD must be defined to force one + * character reads for these systems. Added 7-01-84 CAF + * + * Alarm signal handling changed to work with 4.2 BSD 7-15-84 CAF + * + * BIX added 6-30-87 to support BIX(TM) upload protocol used by the + * Byte Information Exchange. + * + * NFGVMIN Updated 2-18-87 CAF for Xenix systems where c_cc[VMIN] + * doesn't work properly (even though it compiles without error!), + * + * SEGMENTS=n added 2-21-88 as a model for CP/M programs + * for CP/M-80 systems that cannot overlap modem and disk I/O. + * + * VMS flavor hacks begin with rz version 2.00 + * + * -DMD may be added to compiler command line to compile in + * Directory-creating routines from Public Domain TAR by John Gilmore + * + * HOWMANY may be tuned for best performance + * + * USG UNIX (3.0) ioctl conventions courtesy Jeff Martin + */ + +#include <sys/types.h> + +#ifdef vax11c +#include <types.h> +#include <stat.h> +#define LOGFILE "rzlog.tmp" +#define OS "VMS" +#define BUFREAD +extern int errno; +#define SS_NORMAL SS$_NORMAL +#else +/* Not vax11c */ +#define SS_NORMAL 0 +#define LOGFILE "/tmp/rzlog" +#endif + +#include <time.h> +#include <ctype.h> +#include <errno.h> +#include <signal.h> +#include <setjmp.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <utime.h> +#include <stdio.h> + +#define OK 0 +#define FALSE 0 +#define TRUE 1 +#undef ERROR +#define ERROR (-1) + + +_PROTOTYPE(long getfree , (void)); +_PROTOTYPE(void alrm , (int sig )); +_PROTOTYPE(int main , (int argc , char *argv [])); +_PROTOTYPE(int usage , (void)); +_PROTOTYPE(int wcreceive , (int argc , char **argp )); +_PROTOTYPE(int wcrxpn , (char *rpn )); +_PROTOTYPE(int wcrx , (void)); +_PROTOTYPE(int wcgetsec , (char *rxbuf , int maxtime )); +_PROTOTYPE(int readline , (int timeout )); +_PROTOTYPE(void purgeline , (void)); +_PROTOTYPE(int procheader , (char *name )); +_PROTOTYPE(int make_dirs , (char *pathname )); +_PROTOTYPE(int makedir , (char *dpath , int dmode )); +_PROTOTYPE(int putsec , (char *buf , int n )); +_PROTOTYPE(void sendline , (int c )); +_PROTOTYPE(void flushmo , (void)); +_PROTOTYPE(void uncaps , (char *s )); +_PROTOTYPE(int IsAnyLower , (char *s )); +_PROTOTYPE(char *substr , (char *s , char *t )); +void zperr(); +_PROTOTYPE(void canit , (void)); +_PROTOTYPE(void report , (int sct )); +_PROTOTYPE(void chkinvok , (char *s )); +_PROTOTYPE(void checkpath , (char *name )); +_PROTOTYPE(int tryz , (void)); +_PROTOTYPE(int rzfiles , (void)); +_PROTOTYPE(int rzfile , (void)); +_PROTOTYPE(void zmputs , (char *s )); +_PROTOTYPE(int closeit , (void)); +_PROTOTYPE(void ackbibi , (void)); +_PROTOTYPE(void bttyout , (int c )); +_PROTOTYPE(int sys2 , (char *s )); +_PROTOTYPE(void exec2 , (char *s )); + +/* + * Max value for HOWMANY is 255. + * A larger value reduces system overhead but may evoke kernel bugs. + * 133 corresponds to an XMODEM/CRC sector + */ +#ifndef HOWMANY +#define HOWMANY 133 +#endif + +/* Ward Christensen / CP/M parameters - Don't change these! */ +#define ENQ 005 +#define CAN ('X'&037) +#define XOFF ('s'&037) +#define XON ('q'&037) +#define SOH 1 +#define STX 2 +#define EOT 4 +#define ACK 6 +#define NAK 025 +#define CPMEOF 032 +#define WANTCRC 0103 /* send C not NAK to get crc not checksum */ +#define TIMEOUT (-2) +#define RCDO (-3) +#define ERRORMAX 5 +#define RETRYMAX 5 +#define WCEOT (-10) +#define PATHLEN 257 /* ready for 4.2 bsd ? */ +#define UNIXFILE 0xF000 /* The S_IFMT file mask bit for stat */ + +int Zmodem=0; /* ZMODEM protocol requested */ +int Nozmodem = 0; /* If invoked as "rb" */ +unsigned Baudrate = 2400; + +#ifdef vax11c +#include "vrzsz.c" /* most of the system dependent stuff here */ +#else +#include "rbsb.c" /* most of the system dependent stuff here */ +#endif + +#include "crctab.c" + +FILE *fout; + +/* + * Routine to calculate the free bytes on the current file system + * ~0 means many free bytes (unknown) + */ +long getfree() +{ + return(~0L); /* many free bytes ... */ +} + +int Lastrx; +int Crcflg; +int Firstsec; +int Eofseen; /* indicates cpm eof (^Z) has been received */ +int errors; +int Restricted=0; /* restricted; no /.. or ../ in filenames */ +#ifdef ONEREAD +/* Sorry, Regulus and some others don't work right in raw mode! */ +int Readnum = 1; /* Number of bytes to ask for in read() from modem */ +#else +int Readnum = HOWMANY; /* Number of bytes to ask for in read() from modem */ +#endif + +#define DEFBYTL 2000000000L /* default rx file size */ +long Bytesleft; /* number of bytes of incoming file left */ +long Modtime; /* Unix style mod time for incoming file */ +int Filemode; /* Unix style mode for incoming file */ +char Pathname[PATHLEN]; +char *Progname; /* the name by which we were called */ + +int Batch=0; +int Topipe=0; +int MakeLCPathname=TRUE; /* make received pathname lower case */ +int Verbose=0; +int Quiet=0; /* overrides logic that would otherwise set verbose */ +int Nflag = 0; /* Don't really transfer files */ +int Rxclob=FALSE; /* Clobber existing file */ +int Rxbinary=FALSE; /* receive all files in bin mode */ +int Rxascii=FALSE; /* receive files in ascii (translate) mode */ +int Thisbinary; /* current file is to be received in bin mode */ +int Blklen; /* record length of received packets */ + +#ifdef SEGMENTS +int chinseg = 0; /* Number of characters received in this data seg */ +char secbuf[1+(SEGMENTS+1)*1024]; +#else +char secbuf[1025]; +#endif + + +char linbuf[HOWMANY]; +int Lleft=0; /* number of characters in linbuf */ +time_t timep[2]; +char Lzmanag; /* Local file management request */ +char zconv; /* ZMODEM file conversion request */ +char zmanag; /* ZMODEM file management request */ +char ztrans; /* ZMODEM file transport request */ +int Zctlesc; /* Encode control characters */ +int Zrwindow = 1400; /* RX window size (controls garbage count) */ + +jmp_buf tohere; /* For the interrupt on RX timeout */ + +#define xsendline(c) sendline(c) +#include "zm.c" + +int tryzhdrtype=ZRINIT; /* Header type to send corresponding to Last rx close */ + +void alrm(sig) +int sig; +{ + longjmp(tohere, -1); +} + +/* called by signal interrupt or terminate to clean things up */ +void bibi(n) +int n; +{ + if (Zmodem) + zmputs(Attn); + canit(); mode(0); + fprintf(stderr, "rz: caught signal %d; exiting\n", n); + cucheck(); + exit(128+n); +} + +int main(argc, argv) +int argc; +char *argv[]; +{ + register char *cp; + register npats; + char *virgin, **patts; + int exitcode = 0; + + Rxtimeout = 100; + setbuf(stderr, (char *)NULL); + if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh"))) + Restricted=TRUE; + + from_cu(); +#ifdef vax11c + Progname = virgin = "rz"; +#else + chkinvok(virgin=argv[0]); /* if called as [-]rzCOMMAND set flag */ +#endif + npats = 0; + while (--argc) { + cp = *++argv; + if (*cp == '-') { + while( *++cp) { + switch(*cp) { + case '\\': + cp[1] = toupper(cp[1]); continue; + case '+': + Lzmanag = ZMAPND; break; + case 'a': + Rxascii=TRUE; break; + case 'b': + Rxbinary=TRUE; break; + case 'c': + Crcflg=TRUE; break; +#ifndef vax11c + case 'D': + Nflag = TRUE; break; +#endif + case 'e': + Zctlesc = 1; break; + case 'p': + Lzmanag = ZMPROT; break; + case 'q': + Quiet=TRUE; Verbose=0; break; + case 't': + if (--argc < 1) { + usage(); + } + Rxtimeout = atoi(*++argv); + if (Rxtimeout<10 || Rxtimeout>1000) + usage(); + break; + case 'w': + if (--argc < 1) { + usage(); + } + Zrwindow = atoi(*++argv); + break; + case 'u': + MakeLCPathname=FALSE; break; + case 'v': + ++Verbose; break; + case 'y': + Rxclob=TRUE; break; + default: + usage(); + } + } + } + else if ( !npats && argc>0) { + if (argv[0][0]) { + npats=argc; + patts=argv; + } + } + } + if (npats > 1) + usage(); + if (Batch && npats) + usage(); + if (Verbose) { + if (freopen(LOGFILE, "a", stderr)==NULL) { + printf("Can't open log file %s\n",LOGFILE); + exit(0200); + } + setbuf(stderr, (char *)NULL); + fprintf(stderr, "argv[0]=%s Progname=%s\n", virgin, Progname); + } + if (Fromcu && !Quiet) { + if (Verbose == 0) + Verbose = 2; + } + vfile("%s %s for %s\n", Progname, VERSION, OS); + mode(1); + if (signal(SIGINT, bibi) == SIG_IGN) { + signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN); + } + else { + signal(SIGINT, bibi); signal(SIGKILL, bibi); + } + signal(SIGTERM, bibi); + if (wcreceive(npats, patts)==ERROR) { + exitcode=0200; + canit(); + } + mode(0); + vfile("exitcode = %d\n",exitcode); + if (exitcode && !Zmodem) /* bellow again with all thy might. */ + canit(); + if (exitcode) + cucheck(); + if (Verbose) putc('\n', stderr); + exit(exitcode ? exitcode:SS_NORMAL); +} + + +int usage() +{ + cucheck(); +#ifdef vax11c + fprintf(stderr,"Usage: rz [-abeuvy]\n"); +#else + fprintf(stderr,"Usage: rz [-abeuvy] (ZMODEM)\n"); + fprintf(stderr,"or rb [-abuvy] (YMODEM)\n"); + fprintf(stderr,"or rx [-abcv] file (XMODEM or XMODEM-1k)\n"); +#endif + fprintf(stderr," -a ASCII transfer (strip CR)\n"); + fprintf(stderr," -b Binary transfer for all files\n"); +#ifndef vax11c + fprintf(stderr," -c Use 16 bit CRC (XMODEM)\n"); +#endif + fprintf(stderr," -e Escape control characters (ZMODEM)\n"); + fprintf(stderr," -v Verbose more v's give more info\n"); + fprintf(stderr," -y Yes, clobber existing file if any\n"); + fprintf(stderr,"%s %s for %s by Chuck Forsberg, Omen Technology INC\n", + Progname, VERSION, OS); + fprintf(stderr, "\t\t\042The High Reliability Software\042\n"); + exit(SS_NORMAL); +} +/* + * Debugging information output interface routine + */ +/* VARARGS1 */ +void vfile(f, a, b, c) +register char *f,*a,*b,*c; + +{ + if (Verbose > 2) { + fprintf(stderr, f, a, b, c); + fprintf(stderr, "\n"); + } +} + +/* + * Let's receive something already. + */ + +char *rbmsg = +"%s ready. To begin transfer, type \"%s file ...\" to your modem program\r\n\n"; + +int wcreceive(argc, argp) +int argc; +char **argp; +{ + register c; + + if (Batch || argc==0) { + Crcflg=1; + if ( !Quiet) + fprintf(stderr, rbmsg, Progname, Nozmodem?"sb":"sz"); + if (c=tryz()) { + if (c == ZCOMPL) + return OK; + if (c == ERROR) + goto fubar; + c = rzfiles(); + if (c) + goto fubar; + } else { + for (;;) { + if (wcrxpn(secbuf)== ERROR) + goto fubar; + if (secbuf[0]==0) + return OK; + if (procheader(secbuf) == ERROR) + goto fubar; + if (wcrx()==ERROR) + goto fubar; + } + } + } else { + Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L; + + procheader(""); strcpy(Pathname, *argp); checkpath(Pathname); + fprintf(stderr, "\nrz: ready to receive %s\r\n", Pathname); + if ((fout=fopen(Pathname, "w")) == NULL) + return ERROR; + if (wcrx()==ERROR) + goto fubar; + } + return OK; +fubar: + canit(); +#ifndef vax11c + if (Topipe && fout) { + pclose(fout); return ERROR; + } +#endif + if (fout) + fclose(fout); +#ifndef vax11c + if (Restricted) { + unlink(Pathname); + fprintf(stderr, "\r\nrz: %s removed.\r\n", Pathname); + } +#endif + return ERROR; +} + + +/* + * Fetch a pathname from the other end as a C ctyle ASCIZ string. + * Length is indeterminate as long as less than Blklen + * A null string represents no more files (YMODEM) + */ +int wcrxpn(rpn) +char *rpn; /* receive a pathname */ +{ + register c; + +#ifdef NFGVMIN + readline(1); +#else + purgeline(); +#endif + +et_tu: + Firstsec=TRUE; Eofseen=FALSE; + sendline(Crcflg?WANTCRC:NAK); + Lleft=0; /* Do read next time ... */ + while ((c = wcgetsec(rpn, 100)) != 0) { + if (c == WCEOT) { + zperr( "Pathname fetch returned %d", c); + sendline(ACK); + Lleft=0; /* Do read next time ... */ + readline(1); + goto et_tu; + } + return ERROR; + } + sendline(ACK); + return OK; +} + +/* + * Adapted from CMODEM13.C, written by + * Jack M. Wierda and Roderick W. Hart + */ + +int wcrx() +{ + register int sectnum, sectcurr; + register char sendchar; + int cblklen; /* bytes to dump this block */ + + Firstsec=TRUE;sectnum=0; Eofseen=FALSE; + sendchar=Crcflg?WANTCRC:NAK; + + for (;;) { + sendline(sendchar); /* send it now, we're ready! */ + Lleft=0; /* Do read next time ... */ + sectcurr=wcgetsec(secbuf, (sectnum&0177)?50:130); + report(sectcurr); + if (sectcurr==((sectnum+1) &0377)) { + sectnum++; + cblklen = Bytesleft>Blklen ? Blklen:Bytesleft; + if (putsec(secbuf, cblklen)==ERROR) + return ERROR; + if ((Bytesleft-=cblklen) < 0) + Bytesleft = 0; + sendchar=ACK; + } + else if (sectcurr==(sectnum&0377)) { + zperr( "Received dup Sector"); + sendchar=ACK; + } + else if (sectcurr==WCEOT) { + if (closeit()) + return ERROR; + sendline(ACK); + Lleft=0; /* Do read next time ... */ + return OK; + } + else if (sectcurr==ERROR) + return ERROR; + else { + zperr( "Sync Error"); + return ERROR; + } + } +} + +/* + * Wcgetsec fetches a Ward Christensen type sector. + * Returns sector number encountered or ERROR if valid sector not received, + * or CAN CAN received + * or WCEOT if eot sector + * time is timeout for first char, set to 4 seconds thereafter + ***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK ************** + * (Caller must do that when he is good and ready to get next sector) + */ + +int wcgetsec(rxbuf, maxtime) +char *rxbuf; +int maxtime; +{ + register checksum, wcj, firstch; + register unsigned short oldcrc; + register char *p; + int sectcurr; + + for (Lastrx=errors=0; errors<RETRYMAX; errors++) { + + if ((firstch=readline(maxtime))==STX) { + Blklen=1024; goto get2; + } + if (firstch==SOH) { + Blklen=128; +get2: + sectcurr=readline(1); + if ((sectcurr+(oldcrc=readline(1)))==0377) { + oldcrc=checksum=0; + for (p=rxbuf,wcj=Blklen; --wcj>=0; ) { + if ((firstch=readline(1)) < 0) + goto bilge; + oldcrc=updcrc(firstch, oldcrc); + checksum += (*p++ = firstch); + } + if ((firstch=readline(1)) < 0) + goto bilge; + if (Crcflg) { + oldcrc=updcrc(firstch, oldcrc); + if ((firstch=readline(1)) < 0) + goto bilge; + oldcrc=updcrc(firstch, oldcrc); + if (oldcrc & 0xFFFF) + zperr( "CRC"); + else { + Firstsec=FALSE; + return sectcurr; + } + } + else if (((checksum-firstch)&0377)==0) { + Firstsec=FALSE; + return sectcurr; + } + else + zperr( "Checksum"); + } + else + zperr("Sector number garbled"); + } + /* make sure eot really is eot and not just mixmash */ +#ifdef NFGVMIN + else if (firstch==EOT && readline(1)==TIMEOUT) + return WCEOT; +#else + else if (firstch==EOT && Lleft==0) + return WCEOT; +#endif + else if (firstch==CAN) { + if (Lastrx==CAN) { + zperr( "Sender CANcelled"); + return ERROR; + } else { + Lastrx=CAN; + continue; + } + } + else if (firstch==TIMEOUT) { + if (Firstsec) + goto humbug; +bilge: + zperr( "TIMEOUT"); + } + else + zperr( "Got 0%o sector header", firstch); + +humbug: + Lastrx=0; + while(readline(1)!=TIMEOUT) + ; + if (Firstsec) { + sendline(Crcflg?WANTCRC:NAK); + Lleft=0; /* Do read next time ... */ + } else { + maxtime=40; sendline(NAK); + Lleft=0; /* Do read next time ... */ + } + } + /* try to stop the bubble machine. */ + canit(); + return ERROR; +} + +#ifndef vax11c +/* + * This version of readline is reasoably well suited for + * reading many characters. + * (except, currently, for the Regulus version!) + * + * timeout is in tenths of seconds + */ +int readline(timeout) +int timeout; +{ + register n; + static char *cdq; /* pointer for removing chars from linbuf */ + + if (--Lleft >= 0) { + if (Verbose > 8) { + fprintf(stderr, "%02x ", *cdq&0377); + } + return (*cdq++ & 0377); + } + n = timeout/10; + if (n < 2) + n = 3; + if (Verbose > 5) + fprintf(stderr, "Calling read: alarm=%d Readnum=%d ", + n, Readnum); + if (setjmp(tohere)) { +#ifdef TIOCFLUSH +/* ioctl(iofd, TIOCFLUSH, 0); */ +#endif + Lleft = 0; + if (Verbose>1) + fprintf(stderr, "Readline:TIMEOUT\n"); + return TIMEOUT; + } + signal(SIGALRM, alrm); alarm(n); + Lleft=read(iofd, cdq=linbuf, Readnum); + alarm(0); + if (Verbose > 5) { + fprintf(stderr, "Read returned %d bytes\n", Lleft); + } + if (Lleft < 1) + return TIMEOUT; + --Lleft; + if (Verbose > 8) { + fprintf(stderr, "%02x ", *cdq&0377); + } + return (*cdq++ & 0377); +} + + + +/* + * Purge the modem input queue of all characters + */ +void purgeline() +{ + Lleft = 0; +#ifdef USG + ioctl(iofd, TCFLSH, 0); +#else + lseek(iofd, 0L, 2); +#endif +} +#endif + + +/* + * Process incoming file information header + */ +int procheader(name) +char *name; +{ + register char *openmode, *p; + + /* set default parameters and overrides */ + openmode = "w"; + Thisbinary = (!Rxascii) || Rxbinary; + if (Lzmanag) + zmanag = Lzmanag; + + /* + * Process ZMODEM remote file management requests + */ + if (!Rxbinary && zconv == ZCNL) /* Remote ASCII override */ + Thisbinary = 0; + if (zconv == ZCBIN) /* Remote Binary override */ + Thisbinary = TRUE; + else if (zmanag == ZMAPND) + openmode = "a"; + +#ifndef BIX + /* Check for existing file */ + if (!Rxclob && (zmanag&ZMMASK) != ZMCLOB && (fout=fopen(name, "r"))) { + fclose(fout); return ERROR; + } +#endif + + Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L; + + p = name + 1 + strlen(name); + if (*p) { /* file coming from Unix or DOS system */ + sscanf(p, "%ld%lo%o", &Bytesleft, &Modtime, &Filemode); +#ifndef vax11c + if (Filemode & UNIXFILE) + ++Thisbinary; +#endif + if (Verbose) { + fprintf(stderr, "\nIncoming: %s %ld %lo %o\n", + name, Bytesleft, Modtime, Filemode); + } + } + +#ifdef BIX + if ((fout=fopen("scratchpad", openmode)) == NULL) + return ERROR; + return OK; +#else + + else { /* File coming from CP/M system */ + for (p=name; *p; ++p) /* change / to _ */ + if ( *p == '/') + *p = '_'; + + if ( *--p == '.') /* zap trailing period */ + *p = 0; + } + +#ifndef vax11c + if (!Zmodem && MakeLCPathname && !IsAnyLower(name) + && !(Filemode&UNIXFILE)) + uncaps(name); +#endif + if (Topipe > 0) { + sprintf(Pathname, "%s %s", Progname+2, name); + if (Verbose) + fprintf(stderr, "Topipe: %s %s\n", + Pathname, Thisbinary?"BIN":"ASCII"); +#ifndef vax11c + if ((fout=popen(Pathname, "w")) == NULL) + return ERROR; +#endif + } else { + strcpy(Pathname, name); + if (Verbose) { + fprintf(stderr, "Receiving %s %s %s\n", + name, Thisbinary?"BIN":"ASCII", openmode); + } + checkpath(name); + if (Nflag) + name = "/dev/null"; +#ifndef vax11c +#ifdef OMEN + if (name[0] == '!' || name[0] == '|') { + if ( !(fout = popen(name+1, "w"))) { + return ERROR; + } + Topipe = -1; return(OK); + } +#endif +#endif +#ifdef MD + fout = fopen(name, openmode); + if ( !fout) + if (make_dirs(name)) + fout = fopen(name, openmode); +#else + fout = fopen(name, openmode); +#endif + if ( !fout) + return ERROR; + } + return OK; +#endif /* BIX */ +} + +#ifdef MD +/* + * Directory-creating routines from Public Domain TAR by John Gilmore + */ + +/* + * After a file/link/symlink/dir creation has failed, see if + * it's because some required directory was not present, and if + * so, create all required dirs. + */ +int make_dirs(pathname) +register char *pathname; +{ + register char *p; /* Points into path */ + int madeone = 0; /* Did we do anything yet? */ + int save_errno = errno; /* Remember caller's errno */ + char *strchr(); + + if (errno != ENOENT) + return 0; /* Not our problem */ + + for (p = strchr(pathname, '/'); p != NULL; p = strchr(p+1, '/')) { + /* Avoid mkdir of empty string, if leading or double '/' */ + if (p == pathname || p[-1] == '/') + continue; + /* Avoid mkdir where last part of path is '.' */ + if (p[-1] == '.' && (p == pathname+1 || p[-2] == '/')) + continue; + *p = 0; /* Truncate the path there */ + if ( !makedir(pathname, 0777)) { /* Try to create it as a dir */ + vfile("Made directory %s\n", pathname); + madeone++; /* Remember if we made one */ + *p = '/'; + continue; + } + *p = '/'; + if (errno == EEXIST) /* Directory already exists */ + continue; + /* + * Some other error in the makedir. We return to the caller. + */ + break; + } + errno = save_errno; /* Restore caller's errno */ + return madeone; /* Tell them to retry if we made one */ +} + +#if (MD != 2) +#define TERM_SIGNAL(status) ((status) & 0x7F) +#define TERM_COREDUMP(status) (((status) & 0x80) != 0) +#define TERM_VALUE(status) ((status) >> 8) +/* + * Make a directory. Compatible with the mkdir() system call on 4.2BSD. + */ +int makedir(dpath, dmode) +char *dpath; +int dmode; +{ + int cpid, status; + struct stat statbuf; + + if (stat(dpath,&statbuf) == 0) { + errno = EEXIST; /* Stat worked, so it already exists */ + return -1; + } + + /* If stat fails for a reason other than non-existence, return error */ + if (errno != ENOENT) return -1; + + switch (cpid = fork()) { + + case -1: /* Error in fork() */ + return(-1); /* Errno is set already */ + + case 0: /* Child process */ + /* + * Cheap hack to set mode of new directory. Since this + * child process is going away anyway, we zap its umask. + * FIXME, this won't suffice to set SUID, SGID, etc. on this + * directory. Does anybody care? + */ + status = umask(0); /* Get current umask */ + status = umask(status | (0777 & ~dmode)); /* Set for mkdir */ + execl("/bin/mkdir", "mkdir", dpath, (char *)0); + _exit(-1); /* Can't exec /bin/mkdir */ + + default: /* Parent process */ + while (cpid != wait(&status)) ; /* Wait for kid to finish */ + } + + if (TERM_SIGNAL(status) != 0 || TERM_VALUE(status) != 0) { + errno = EIO; /* We don't know why, but */ + return -1; /* /bin/mkdir failed */ + } + + return 0; +} +#endif /* MD != 2 */ +#endif /* MD */ + +/* + * Putsec writes the n characters of buf to receive file fout. + * If not in binary mode, carriage returns, and all characters + * starting with CPMEOF are discarded. + */ +int putsec(buf, n) +char *buf; +register int n; +{ + register char *p; + + if (n == 0) + return OK; + if (Thisbinary) { + for (p=buf; --n>=0; ) + putc( *p++, fout); + } + else { + if (Eofseen) + return OK; + for (p=buf; --n>=0; ++p ) { + if ( *p == '\r') + continue; + if (*p == CPMEOF) { + Eofseen=TRUE; return OK; + } + putc(*p ,fout); + } + } + return OK; +} + +#ifndef vax11c +/* + * Send a character to modem. Small is beautiful. + */ +void sendline(c) +int c; +{ + char d; + + d = c; + if (Verbose>6) + fprintf(stderr, "Sendline: %x\n", c); + write(1, &d, 1); +} + +void flushmo() {} +#endif + + + + + +/* make string s lower case */ +void uncaps(s) +register char *s; +{ + for ( ; *s; ++s) + if (isupper(*s)) + *s = tolower(*s); +} +/* + * IsAnyLower returns TRUE if string s has lower case letters. + */ +int IsAnyLower(s) +register char *s; +{ + for ( ; *s; ++s) + if (islower(*s)) + return TRUE; + return FALSE; +} + +/* + * substr(string, token) searches for token in string s + * returns pointer to token within string if found, NULL otherwise + */ +char * +substr(s, t) +register char *s,*t; +{ + register char *ss,*tt; + /* search for first char of token */ + for (ss=s; *s; s++) + if (*s == *t) + /* compare token with substring */ + for (ss=s,tt=t; ;) { + if (*tt == 0) + return s; + if (*ss++ != *tt++) + break; + } + return (char *)NULL; +} + +/* + * Log an error + */ +/*VARARGS1*/ +void zperr(s,p,u) +char *s, *p, *u; +{ + if (Verbose <= 0) + return; + fprintf(stderr, "Retry %d: ", errors); + fprintf(stderr, s, p, u); + fprintf(stderr, "\n"); +} + +/* send cancel string to get the other end to shut up */ +void canit() +{ + static char canistr[] = { + 24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0 + }; + +#ifdef vax11c + raw_wbuf(strlen(canistr), canistr); + purgeline(); +#else + printf(canistr); + Lleft=0; /* Do read next time ... */ + fflush(stdout); +#endif +} + + +void report(sct) +int sct; +{ + if (Verbose>1) + fprintf(stderr,"%03d%c",sct,sct%10? ' ' : '\r'); +} + +#ifndef vax11c +/* + * If called as [-][dir/../]vrzCOMMAND set Verbose to 1 + * If called as [-][dir/../]rzCOMMAND set the pipe flag + * If called as rb use YMODEM protocol + */ +void chkinvok(s) +char *s; +{ + register char *p; + + p = s; + while (*p == '-') + s = ++p; + while (*p) + if (*p++ == '/') + s = p; + if (*s == 'v') { + Verbose=1; ++s; + } + Progname = s; + if (s[0]=='r' && s[1]=='z') + Batch = TRUE; + if (s[0]=='r' && s[1]=='b') + Batch = Nozmodem = TRUE; + if (s[2] && s[0]=='r' && s[1]=='b') + Topipe = 1; + if (s[2] && s[0]=='r' && s[1]=='z') + Topipe = 1; +} +#endif + +/* + * Totalitarian Communist pathname processing + */ +void checkpath(name) +char *name; +{ + if (Restricted) { + if (fopen(name, "r") != NULL) { + canit(); + fprintf(stderr, "\r\nrz: %s exists\n", name); + bibi(-1); + } + /* restrict pathnames to current tree or uucppublic */ + if ( substr(name, "../") + || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) { + canit(); + fprintf(stderr,"\r\nrz:\tSecurity Violation\r\n"); + bibi(-1); + } + } +} + +/* + * Initialize for Zmodem receive attempt, try to activate Zmodem sender + * Handles ZSINIT frame + * Return ZFILE if Zmodem filename received, -1 on error, + * ZCOMPL if transaction finished, else 0 + */ +int tryz() +{ + register c, n; + register cmdzack1flg; + + if (Nozmodem) /* Check for "rb" program name */ + return 0; + + + for (n=Zmodem?15:5; --n>=0; ) { + /* Set buffer length (0) and capability flags */ +#ifdef SEGMENTS + stohdr(SEGMENTS*1024L); +#else + stohdr(0L); +#endif +#ifdef CANBREAK + Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO|CANBRK; +#else + Txhdr[ZF0] = CANFC32|CANFDX|CANOVIO; +#endif + if (Zctlesc) + Txhdr[ZF0] |= TESCCTL; + zshhdr(tryzhdrtype, Txhdr); + if (tryzhdrtype == ZSKIP) /* Don't skip too far */ + tryzhdrtype = ZRINIT; /* CAF 8-21-87 */ +again: + switch (zgethdr(Rxhdr, 0)) { + case ZRQINIT: + continue; + case ZEOF: + continue; + case TIMEOUT: + continue; + case ZFILE: + zconv = Rxhdr[ZF0]; + zmanag = Rxhdr[ZF1]; + ztrans = Rxhdr[ZF2]; + tryzhdrtype = ZRINIT; + c = zrdata(secbuf, 1024); + mode(3); + if (c == GOTCRCW) + return ZFILE; + zshhdr(ZNAK, Txhdr); + goto again; + case ZSINIT: + Zctlesc = TESCCTL & Rxhdr[ZF0]; + if (zrdata(Attn, ZATTNLEN) == GOTCRCW) { + stohdr(1L); + zshhdr(ZACK, Txhdr); + goto again; + } + zshhdr(ZNAK, Txhdr); + goto again; + case ZFREECNT: + stohdr(getfree()); + zshhdr(ZACK, Txhdr); + goto again; + case ZCOMMAND: +#ifdef vax11c + return ERROR; +#else + cmdzack1flg = Rxhdr[ZF0]; + if (zrdata(secbuf, 1024) == GOTCRCW) { + if (cmdzack1flg & ZCACK1) + stohdr(0L); + else + stohdr((long)sys2(secbuf)); + purgeline(); /* dump impatient questions */ + do { + zshhdr(ZCOMPL, Txhdr); + } + while (++errors<20 && zgethdr(Rxhdr,1) != ZFIN); + ackbibi(); + if (cmdzack1flg & ZCACK1) + exec2(secbuf); + return ZCOMPL; + } + zshhdr(ZNAK, Txhdr); goto again; +#endif + case ZCOMPL: + goto again; + default: + continue; + case ZFIN: + ackbibi(); return ZCOMPL; + case ZCAN: + return ERROR; + } + } + return 0; +} + +/* + * Receive 1 or more files with ZMODEM protocol + */ +int rzfiles() +{ + register c; + + for (;;) { + switch (c = rzfile()) { + case ZEOF: + case ZSKIP: + switch (tryz()) { + case ZCOMPL: + return OK; + default: + return ERROR; + case ZFILE: + break; + } + continue; + default: + return c; + case ERROR: + return ERROR; + } + } +} + +/* + * Receive a file with ZMODEM protocol + * Assumes file name frame is in secbuf + */ +int rzfile() +{ + register c, n; + long rxbytes; + + Eofseen=FALSE; + if (procheader(secbuf) == ERROR) { + return (tryzhdrtype = ZSKIP); + } + + n = 20; rxbytes = 0l; + + for (;;) { +#ifdef SEGMENTS + chinseg = 0; +#endif + stohdr(rxbytes); + zshhdr(ZRPOS, Txhdr); +nxthdr: + switch (c = zgethdr(Rxhdr, 0)) { + default: + vfile("rzfile: zgethdr returned %d", c); + return ERROR; + case ZNAK: + case TIMEOUT: +#ifdef SEGMENTS + putsec(secbuf, chinseg); + chinseg = 0; +#endif + if ( --n < 0) { + vfile("rzfile: zgethdr returned %d", c); + return ERROR; + } + case ZFILE: + zrdata(secbuf, 1024); + continue; + case ZEOF: +#ifdef SEGMENTS + putsec(secbuf, chinseg); + chinseg = 0; +#endif + if (rclhdr(Rxhdr) != rxbytes) { + /* + * Ignore eof if it's at wrong place - force + * a timeout because the eof might have gone + * out before we sent our zrpos. + */ + errors = 0; goto nxthdr; + } + if (closeit()) { + tryzhdrtype = ZFERR; + vfile("rzfile: closeit returned <> 0"); + return ERROR; + } + vfile("rzfile: normal EOF"); + return c; + case ERROR: /* Too much garbage in header search error */ +#ifdef SEGMENTS + putsec(secbuf, chinseg); + chinseg = 0; +#endif + if ( --n < 0) { + vfile("rzfile: zgethdr returned %d", c); + return ERROR; + } + zmputs(Attn); + continue; + case ZSKIP: +#ifdef SEGMENTS + putsec(secbuf, chinseg); + chinseg = 0; +#endif + closeit(); + vfile("rzfile: Sender SKIPPED file"); + return c; + case ZDATA: + if (rclhdr(Rxhdr) != rxbytes) { + if ( --n < 0) { + return ERROR; + } +#ifdef SEGMENTS + putsec(secbuf, chinseg); + chinseg = 0; +#endif + zmputs(Attn); continue; + } +moredata: + if (Verbose>1) + fprintf(stderr, "\r%7ld ZMODEM%s ", + rxbytes, Crc32?" CRC-32":""); +#ifdef SEGMENTS + if (chinseg >= (1024 * SEGMENTS)) { + putsec(secbuf, chinseg); + chinseg = 0; + } + switch (c = zrdata(secbuf+chinseg, 1024)) +#else + switch (c = zrdata(secbuf, 1024)) +#endif + { + case ZCAN: +#ifdef SEGMENTS + putsec(secbuf, chinseg); + chinseg = 0; +#endif + vfile("rzfile: zgethdr returned %d", c); + return ERROR; + case ERROR: /* CRC error */ +#ifdef SEGMENTS + putsec(secbuf, chinseg); + chinseg = 0; +#endif + if ( --n < 0) { + vfile("rzfile: zgethdr returned %d", c); + return ERROR; + } + zmputs(Attn); + continue; + case TIMEOUT: +#ifdef SEGMENTS + putsec(secbuf, chinseg); + chinseg = 0; +#endif + if ( --n < 0) { + vfile("rzfile: zgethdr returned %d", c); + return ERROR; + } + continue; + case GOTCRCW: + n = 20; +#ifdef SEGMENTS + chinseg += Rxcount; + putsec(secbuf, chinseg); + chinseg = 0; +#else + putsec(secbuf, Rxcount); +#endif + rxbytes += Rxcount; + stohdr(rxbytes); + zshhdr(ZACK, Txhdr); + sendline(XON); + goto nxthdr; + case GOTCRCQ: + n = 20; +#ifdef SEGMENTS + chinseg += Rxcount; +#else + putsec(secbuf, Rxcount); +#endif + rxbytes += Rxcount; + stohdr(rxbytes); + zshhdr(ZACK, Txhdr); + goto moredata; + case GOTCRCG: + n = 20; +#ifdef SEGMENTS + chinseg += Rxcount; +#else + putsec(secbuf, Rxcount); +#endif + rxbytes += Rxcount; + goto moredata; + case GOTCRCE: + n = 20; +#ifdef SEGMENTS + chinseg += Rxcount; +#else + putsec(secbuf, Rxcount); +#endif + rxbytes += Rxcount; + goto nxthdr; + } + } + } +} + +/* + * Send a string to the modem, processing for \336 (sleep 1 sec) + * and \335 (break signal) + */ +void zmputs(s) +char *s; +{ + register c; + + while (*s) { + switch (c = *s++) { + case '\336': + sleep(1); continue; + case '\335': + sendbrk(); continue; + default: + sendline(c); + } + } +} + +/* + * Close the receive dataset, return OK or ERROR + */ +int closeit() +{ + time_t q; + +#ifndef vax11c + if (Topipe) { + if (pclose(fout)) { + return ERROR; + } + return OK; + } +#endif + if (fclose(fout)==ERROR) { + fprintf(stderr, "file close ERROR\n"); + return ERROR; + } +#ifndef vax11c + if (Modtime) { + timep[0] = time(&q); + timep[1] = Modtime; + utime(Pathname, (struct utimbuf *) timep); + } +#endif + if ((Filemode&S_IFMT) == S_IFREG) + chmod(Pathname, (07777 & Filemode)); + return OK; +} + +/* + * Ack a ZFIN packet, let byegones be byegones + */ +void ackbibi() +{ + register n; + + vfile("ackbibi:"); + Readnum = 1; + stohdr(0L); + for (n=3; --n>=0; ) { + purgeline(); + zshhdr(ZFIN, Txhdr); + switch (readline(100)) { + case 'O': + readline(1); /* Discard 2nd 'O' */ + vfile("ackbibi complete"); + return; + case RCDO: + return; + case TIMEOUT: + default: + break; + } + } +} + + + +/* + * Local console output simulation + */ +void bttyout(c) +int c; +{ + if (Verbose || Fromcu) + putc(c, stderr); +} + +#ifndef vax11c +/* + * Strip leading ! if present, do shell escape. + */ +int sys2(s) +register char *s; +{ + if (*s == '!') + ++s; + return system(s); +} +/* + * Strip leading ! if present, do exec. + */ +void exec2(s) +register char *s; +{ + if (*s == '!') + ++s; + mode(0); + execl("/bin/sh", "sh", "-c", s); +} +#endif +/* End of rz.c */ diff --git a/commands/zmodem/sz.c b/commands/zmodem/sz.c new file mode 100755 index 000000000..3dc99fb54 --- /dev/null +++ b/commands/zmodem/sz.c @@ -0,0 +1,1755 @@ +#define VERSION "sz 2.12 05-29-88" +#define PUBDIR "/usr/spool/uucppublic" + +/*% cc -compat -M2 -Ox -K -i -DTXBSIZE=16384 -DNFGVMIN -DREADCHECK sz.c -lx -o sz; size sz + + Following is used for testing, might not be reasonable for production +<-xtx-*> cc -Osal -DTXBSIZE=32768 -DSV sz.c -lx -o $B/sz; size $B/sz + + **************************************************************************** + * + * sz.c By Chuck Forsberg, Omen Technology INC + * + **************************************************************************** + * + * Typical Unix/Xenix/Clone compiles: + * + * cc -O sz.c -o sz USG (SYS III/V) Unix + * cc -O -DSV sz.c -o sz Sys V Release 2 with non-blocking input + * Define to allow reverse channel checking + * cc -O -DV7 sz.c -o sz Unix Version 7, 2.8 - 4.3 BSD + * + * cc -O -K -i -DNFGVMIN -DREADCHECK sz.c -lx -o sz Classic Xenix + * + * ln sz sb **** All versions **** + * ln sz sx **** All versions **** + * + **************************************************************************** + * + * Typical VMS compile and install sequence: + * + * define LNK$LIBRARY SYS$LIBRARY:VAXCRTL.OLB + * cc sz.c + * cc vvmodem.c + * link sz,vvmodem + * sz :== $disk$user2:[username.subdir]sz.exe + * + * If you feel adventureous, remove the #define BADSYNC line + * immediately following the #ifdef vax11c line! Some VMS + * systems know how to fseek, some don't. + * + **************************************************************************** + * + * + * A program for Unix to send files and commands to computers running + * Professional-YAM, PowerCom, YAM, IMP, or programs supporting Y/XMODEM. + * + * Sz uses buffered I/O to greatly reduce CPU time compared to UMODEM. + * + * USG UNIX (3.0) ioctl conventions courtesy Jeff Martin + * + * 2.1x hacks to avoid VMS fseek() bogosity, allow input from pipe + * -DBADSEEK -DTXBSIZE=32768 + * 2.x has mods for VMS flavor + * + * 1.34 implements tx backchannel garbage count and ZCRCW after ZRPOS + * in accordance with the 7-31-87 ZMODEM Protocol Description + */ + + +#include <sys/types.h> + +#ifdef vax11c +#define BADSEEK +#define TXBSIZE 32768 /* Must be power of two, < MAXINT */ +#include <types.h> +#include <stat.h> +#define LOGFILE "szlog.tmp" +#define OS "VMS" +#define READCHECK +#define BUFWRITE +#define iofd +extern int errno; +#define SS_NORMAL SS$_NORMAL +#define xsendline(c) sendline(c) + + +#else /* vax11c */ + + +#define SS_NORMAL 0 +#define LOGFILE "/tmp/szlog" + +#define sendline(c) putchar((c) & 0377) +#define xsendline(c) putchar(c) + +#endif + +#include <signal.h> +#include <setjmp.h> +#include <ctype.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <utime.h> +#include <stdio.h> + +#define PATHLEN 256 +#define OK 0 +#define FALSE 0 +#define TRUE 1 +#undef ERROR +#define ERROR (-1) +/* Ward Christensen / CP/M parameters - Don't change these! */ +#define ENQ 005 +#define CAN ('X'&037) +#define XOFF ('s'&037) +#define XON ('q'&037) +#define SOH 1 +#define STX 2 +#define EOT 4 +#define ACK 6 +#define NAK 025 +#define CPMEOF 032 +#define WANTCRC 0103 /* send C not NAK to get crc not checksum */ +#define WANTG 0107 /* Send G not NAK to get nonstop batch xmsn */ +#define TIMEOUT (-2) +#define RCDO (-3) +#define RETRYMAX 10 + + +#define HOWMANY 2 +int Zmodem=0; /* ZMODEM protocol requested by receiver */ +unsigned Baudrate=2400; /* Default, should be set by first mode() call */ +unsigned Txwindow; /* Control the size of the transmitted window */ +unsigned Txwspac; /* Spacing between zcrcq requests */ +unsigned Txwcnt; /* Counter used to space ack requests */ +long Lrxpos; /* Receiver's last reported offset */ +int errors; + +#ifdef vax11c +#include "vrzsz.c" /* most of the system dependent stuff here */ +#else +#include "rbsb.c" /* most of the system dependent stuff here */ +#endif +#include "crctab.c" + +int Filesleft; +long Totalleft; + +/* + * Attention string to be executed by receiver to interrupt streaming data + * when an error is detected. A pause (0336) may be needed before the + * ^C (03) or after it. + */ +#ifdef READCHECK +char Myattn[] = { 0 }; +#else +#ifdef USG +char Myattn[] = { 03, 0336, 0 }; +#else +char Myattn[] = { 0 }; +#endif +#endif + +FILE *in; + +#ifdef BADSEEK +int Canseek = 0; /* 1: Can seek 0: only rewind -1: neither (pipe) */ +#ifndef TXBSIZE +#define TXBSIZE 16384 /* Must be power of two, < MAXINT */ +#endif +#else +int Canseek = 1; /* 1: Can seek 0: only rewind -1: neither (pipe) */ +#endif + +#ifdef TXBSIZE +#define TXBMASK (TXBSIZE-1) +char Txb[TXBSIZE]; /* Circular buffer for file reads */ +char *txbuf = Txb; /* Pointer to current file segment */ +#else +char txbuf[1024]; +#endif +long vpos = 0; /* Number of bytes read from file */ + +char Lastrx; +char Crcflg; +int Verbose=0; +int Modem2=0; /* XMODEM Protocol - don't send pathnames */ +int Restricted=0; /* restricted; no /.. or ../ in filenames */ +int Quiet=0; /* overrides logic that would otherwise set verbose */ +int Ascii=0; /* Add CR's for brain damaged programs */ +int Fullname=0; /* transmit full pathname */ +int Unlinkafter=0; /* Unlink file after it is sent */ +int Dottoslash=0; /* Change foo.bar.baz to foo/bar/baz */ +int firstsec; +int errcnt=0; /* number of files unreadable */ +int blklen=128; /* length of transmitted records */ +int Optiong; /* Let it rip no wait for sector ACK's */ +int Eofseen; /* EOF seen on input set by zfilbuf */ +int BEofseen; /* EOF seen on input set by fooseek */ +int Totsecs; /* total number of sectors this file */ +int Filcnt=0; /* count of number of files opened */ +int Lfseen=0; +unsigned Rxbuflen = 16384; /* Receiver's max buffer length */ +int Tframlen = 0; /* Override for tx frame length */ +int blkopt=0; /* Override value for zmodem blklen */ +int Rxflags = 0; +long bytcnt; +int Wantfcs32 = TRUE; /* want to send 32 bit FCS */ +char Lzconv; /* Local ZMODEM file conversion request */ +char Lzmanag; /* Local ZMODEM file management request */ +int Lskipnocor; +char Lztrans; +char zconv; /* ZMODEM file conversion request */ +char zmanag; /* ZMODEM file management request */ +char ztrans; /* ZMODEM file transport request */ +int Command; /* Send a command, then exit. */ +char *Cmdstr; /* Pointer to the command string */ +int Cmdtries = 11; +int Cmdack1; /* Rx ACKs command, then do it */ +int Exitcode = 0; +int Test; /* 1= Force receiver to send Attn, etc with qbf. */ + /* 2= Character transparency test */ +char *qbf="The quick brown fox jumped over the lazy dog's back 1234567890\r\n"; +long Lastsync; /* Last offset to which we got a ZRPOS */ +int Beenhereb4; /* How many times we've been ZRPOS'd same place */ + +jmp_buf tohere; /* For the interrupt on RX timeout */ +jmp_buf intrjmp; /* For the interrupt on RX CAN */ + +_PROTOTYPE(void onintr , (int sig )); +_PROTOTYPE(int main , (int argc , char *argv [])); +_PROTOTYPE(int wcsend , (int argc , char *argp [])); +_PROTOTYPE(int wcs , (char *oname )); +_PROTOTYPE(int wctxpn , (char *name )); +_PROTOTYPE(int getnak , (void)); +_PROTOTYPE(int wctx , (long flen )); +_PROTOTYPE(int wcputsec , (char *buf , int sectnum , int cseclen )); +_PROTOTYPE(int filbuf , (char *buf , int count )); +_PROTOTYPE(int zfilbuf , (void)); +_PROTOTYPE(int fooseek , (FILE *fptr , long pos , int whence )); +_PROTOTYPE(void alrm , (int sig )); +_PROTOTYPE(int readline , (int timeout )); +_PROTOTYPE(void flushmo , (void)); +_PROTOTYPE(void purgeline , (void)); +_PROTOTYPE(void canit , (void)); +void zperr(); +_PROTOTYPE(char *substr , (char *s , char *t )); +_PROTOTYPE(int usage , (void)); +_PROTOTYPE(int getzrxinit , (void)); +_PROTOTYPE(int sendzsinit , (void)); +_PROTOTYPE(int zsendfile , (char *buf , int blen )); +_PROTOTYPE(int zsendfdata , (void)); +_PROTOTYPE(int getinsync , (int flag )); +_PROTOTYPE(void saybibi , (void)); +_PROTOTYPE(void bttyout , (int c )); +_PROTOTYPE(int zsendcmd , (char *buf , int blen )); +_PROTOTYPE(void chkinvok , (char *s )); +_PROTOTYPE(void countem , (int argc , char **argv )); +_PROTOTYPE(void chartest , (int m )); + +/* called by signal interrupt or terminate to clean things up */ +void bibi(n) +int n; +{ + canit(); fflush(stdout); mode(0); + fprintf(stderr, "sz: caught signal %d; exiting\n", n); + if (n == SIGQUIT) + abort(); + if (n == 99) + fprintf(stderr, "mode(2) in rbsb.c not implemented!!\n"); + cucheck(); + exit(128+n); +} +/* Called when ZMODEM gets an interrupt (^X) */ +void onintr(sig) +int sig; +{ + signal(SIGINT, SIG_IGN); + longjmp(intrjmp, -1); +} + +int Zctlesc; /* Encode control characters */ +int Nozmodem = 0; /* If invoked as "sb" */ +char *Progname = "sz"; +int Zrwindow = 1400; /* RX window size (controls garbage count) */ +#include "zm.c" + + +int main(argc, argv) +int argc; +char *argv[]; +{ + register char *cp; + register npats; + int dm; + char **patts; + static char xXbuf[BUFSIZ]; + + if ((cp = getenv("ZNULLS")) && *cp) + Znulls = atoi(cp); + if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh"))) + Restricted=TRUE; + from_cu(); + chkinvok(argv[0]); + + Rxtimeout = 600; + npats=0; + if (argc<2) + usage(); + setbuf(stdout, xXbuf); + while (--argc) { + cp = *++argv; + if (*cp++ == '-' && *cp) { + while ( *cp) { + switch(*cp++) { + case '\\': + *cp = toupper(*cp); continue; + case '+': + Lzmanag = ZMAPND; break; +#ifdef CSTOPB + case '2': + Twostop = TRUE; break; +#endif + case 'a': + Lzconv = ZCNL; + Ascii = TRUE; break; + case 'b': + Lzconv = ZCBIN; break; + case 'C': + if (--argc < 1) { + usage(); + } + Cmdtries = atoi(*++argv); + break; + case 'i': + Cmdack1 = ZCACK1; + /* **** FALL THROUGH TO **** */ + case 'c': + if (--argc != 1) { + usage(); + } + Command = TRUE; + Cmdstr = *++argv; + break; + case 'd': + ++Dottoslash; + /* **** FALL THROUGH TO **** */ + case 'f': + Fullname=TRUE; break; + case 'e': + Zctlesc = 1; break; + case 'k': + blklen=1024; break; + case 'L': + if (--argc < 1) { + usage(); + } + blkopt = atoi(*++argv); + if (blkopt<24 || blkopt>1024) + usage(); + break; + case 'l': + if (--argc < 1) { + usage(); + } + Tframlen = atoi(*++argv); + if (Tframlen<32 || Tframlen>1024) + usage(); + break; + case 'N': + Lzmanag = ZMNEWL; break; + case 'n': + Lzmanag = ZMNEW; break; + case 'o': + Wantfcs32 = FALSE; break; + case 'p': + Lzmanag = ZMPROT; break; + case 'r': + Lzconv = ZCRESUM; + case 'q': + Quiet=TRUE; Verbose=0; break; + case 't': + if (--argc < 1) { + usage(); + } + Rxtimeout = atoi(*++argv); + if (Rxtimeout<10 || Rxtimeout>1000) + usage(); + break; + case 'T': + if (++Test > 1) { + chartest(1); chartest(2); + mode(0); exit(0); + } + break; +#ifndef vax11c + case 'u': + ++Unlinkafter; break; +#endif + case 'v': + ++Verbose; break; + case 'w': + if (--argc < 1) { + usage(); + } + Txwindow = atoi(*++argv); + if (Txwindow < 256) + Txwindow = 256; + Txwindow = (Txwindow/64) * 64; + Txwspac = Txwindow/4; + if (blkopt > Txwspac + || (!blkopt && Txwspac < 1024)) + blkopt = Txwspac; + break; + case 'X': + ++Modem2; break; + case 'Y': + Lskipnocor = TRUE; + /* **** FALLL THROUGH TO **** */ + case 'y': + Lzmanag = ZMCLOB; break; + default: + usage(); + } + } + } + else if ( !npats && argc>0) { + if (argv[0][0]) { + npats=argc; + patts=argv; +#ifndef vax11c + if ( !strcmp(*patts, "-")) + iofd = 1; +#endif + } + } + } + if (npats < 1 && !Command && !Test) + usage(); + if (Verbose) { + if (freopen(LOGFILE, "a", stderr)==NULL) { + printf("Can't open log file %s\n",LOGFILE); + exit(0200); + } + setbuf(stderr, (char *)NULL); + } + if (Fromcu && !Quiet) { + if (Verbose == 0) + Verbose = 2; + } + vfile("%s %s for %s\n", Progname, VERSION, OS); + + mode(1); + + if (signal(SIGINT, bibi) == SIG_IGN) { + signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN); + } else { + signal(SIGINT, bibi); signal(SIGKILL, bibi); + } + if ( !Fromcu) + signal(SIGQUIT, SIG_IGN); + signal(SIGTERM, bibi); + + if ( !Modem2) { + if (!Nozmodem) { + printf("rz\r"); fflush(stdout); + } + countem(npats, patts); + if (!Nozmodem) { + stohdr(0L); + if (Command) + Txhdr[ZF0] = ZCOMMAND; + zshhdr(ZRQINIT, Txhdr); + } + } + fflush(stdout); + + if (Command) { + if (getzrxinit()) { + Exitcode=0200; canit(); + } + else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) { + Exitcode=0200; canit(); + } + } else if (wcsend(npats, patts)==ERROR) { + Exitcode=0200; + canit(); + } + fflush(stdout); + mode(0); + dm = ((errcnt != 0) | Exitcode); + if (dm) { + cucheck(); exit(dm); + } + putc('\n',stderr); + exit(SS_NORMAL); + /*NOTREACHED*/ +} + +int wcsend(argc, argp) +int argc; +char *argp[]; +{ + register int n; + + Crcflg=FALSE; + firstsec=TRUE; + bytcnt = -1; + for (n=0; n<argc; ++n) { + Totsecs = 0; + if (wcs(argp[n])==ERROR) + return ERROR; + } + Totsecs = 0; + if (Filcnt==0) { /* bitch if we couldn't open ANY files */ + if ( !Modem2) { + Command = TRUE; + Cmdstr = "echo \"sz: Can't open any requested files\""; + if (getnak()) { + Exitcode=0200; canit(); + } + if (!Zmodem) + canit(); + else if (zsendcmd(Cmdstr, 1+strlen(Cmdstr))) { + Exitcode=0200; canit(); + } + Exitcode = 1; return OK; + } + canit(); + fprintf(stderr,"\r\nCan't open any requested files.\r\n"); + return ERROR; + } + if (Zmodem) + saybibi(); + else if ( !Modem2) + wctxpn(""); + return OK; +} + +int wcs(oname) +char *oname; +{ + register c; + register char *p; + struct stat f; + char name[PATHLEN]; + + strcpy(name, oname); + + if (Restricted) { + /* restrict pathnames to current tree or uucppublic */ + if ( substr(name, "../") + || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) { + canit(); + fprintf(stderr,"\r\nsz:\tSecurity Violation\r\n"); + return ERROR; + } + } + + if ( !strcmp(oname, "-")) { + if ((p = getenv("ONAME")) && *p) + strcpy(name, p); + else + sprintf(name, "s%d.sz", getpid()); + in = stdin; + } + else if ((in=fopen(oname, "r"))==NULL) { + ++errcnt; + return OK; /* pass over it, there may be others */ + } + BEofseen = Eofseen = 0; vpos = 0; + /* Check for directory or block special files */ + fstat(fileno(in), &f); + c = f.st_mode & S_IFMT; + if (c == S_IFDIR || c == S_IFBLK) { + fclose(in); + return OK; + } + + ++Filcnt; + switch (wctxpn(name)) { + case ERROR: + return ERROR; + case ZSKIP: + return OK; + } + if (!Zmodem && wctx(f.st_size)==ERROR) + return ERROR; +#ifndef vax11c + if (Unlinkafter) + unlink(oname); +#endif + return 0; +} + +/* + * generate and transmit pathname block consisting of + * pathname (null terminated), + * file length, mode time and file mode in octal + * as provided by the Unix fstat call. + * N.B.: modifies the passed name, may extend it! + */ +int wctxpn(name) +char *name; +{ + register char *p, *q; + char name2[PATHLEN]; + struct stat f; + + if (Modem2) { + if ((in!=stdin) && *name && fstat(fileno(in), &f)!= -1) { + fprintf(stderr, "Sending %s, %ld blocks: ", + name, f.st_size>>7); + } + fprintf(stderr, "Give your local XMODEM receive command now.\r\n"); + return OK; + } + zperr("Awaiting pathname nak for %s", *name?name:"<END>"); + if ( !Zmodem) + if (getnak()) + return ERROR; + + q = (char *) 0; + if (Dottoslash) { /* change . to . */ + for (p=name; *p; ++p) { + if (*p == '/') + q = p; + else if (*p == '.') + *(q=p) = '/'; + } + if (q && strlen(++q) > 8) { /* If name>8 chars */ + q += 8; /* make it .ext */ + strcpy(name2, q); /* save excess of name */ + *q = '.'; + strcpy(++q, name2); /* add it back */ + } + } + + for (p=name, q=txbuf ; *p; ) + if ((*q++ = *p++) == '/' && !Fullname) + q = txbuf; + *q++ = 0; + p=q; + while (q < (txbuf + 1024)) + *q++ = 0; + if (!Ascii && (in!=stdin) && *name && fstat(fileno(in), &f)!= -1) + sprintf(p, "%lu %lo %o 0 %d %ld", f.st_size, f.st_mtime, + f.st_mode, Filesleft, Totalleft); + Totalleft -= f.st_size; + if (--Filesleft <= 0) + Totalleft = 0; + if (Totalleft < 0) + Totalleft = 0; + + /* force 1k blocks if name won't fit in 128 byte block */ + if (txbuf[125]) + blklen=1024; + else { /* A little goodie for IMP/KMD */ + txbuf[127] = (f.st_size + 127) >>7; + txbuf[126] = (f.st_size + 127) >>15; + } + if (Zmodem) + return zsendfile(txbuf, 1+strlen(p)+(p-txbuf)); + if (wcputsec(txbuf, 0, 128)==ERROR) + return ERROR; + return OK; +} + +int getnak() +{ + register firstch; + + Lastrx = 0; + for (;;) { + switch (firstch = readline(800)) { + case ZPAD: + if (getzrxinit()) + return ERROR; + Ascii = 0; /* Receiver does the conversion */ + return FALSE; + case TIMEOUT: + zperr("Timeout on pathname"); + return TRUE; + case WANTG: +#ifdef MODE2OK + mode(2); /* Set cbreak, XON/XOFF, etc. */ +#endif + Optiong = TRUE; + blklen=1024; + case WANTCRC: + Crcflg = TRUE; + case NAK: + return FALSE; + case CAN: + if ((firstch = readline(20)) == CAN && Lastrx == CAN) + return TRUE; + default: + break; + } + Lastrx = firstch; + } +} + + +int wctx(flen) +long flen; +{ + register int thisblklen; + register int sectnum, attempts, firstch; + long charssent; + + charssent = 0; firstsec=TRUE; thisblklen = blklen; + vfile("wctx:file length=%ld", flen); + + while ((firstch=readline(Rxtimeout))!=NAK && firstch != WANTCRC + && firstch != WANTG && firstch!=TIMEOUT && firstch!=CAN) + ; + if (firstch==CAN) { + zperr("Receiver CANcelled"); + return ERROR; + } + if (firstch==WANTCRC) + Crcflg=TRUE; + if (firstch==WANTG) + Crcflg=TRUE; + sectnum=0; + for (;;) { + if (flen <= (charssent + 896L)) + thisblklen = 128; + if ( !filbuf(txbuf, thisblklen)) + break; + if (wcputsec(txbuf, ++sectnum, thisblklen)==ERROR) + return ERROR; + charssent += thisblklen; + } + fclose(in); + attempts=0; + do { + purgeline(); + sendline(EOT); + fflush(stdout); + ++attempts; + } + while ((firstch=(readline(Rxtimeout)) != ACK) && attempts < RETRYMAX); + if (attempts == RETRYMAX) { + zperr("No ACK on EOT"); + return ERROR; + } + else + return OK; +} + +int wcputsec(buf, sectnum, cseclen) +char *buf; +int sectnum; +int cseclen; /* data length of this sector to send */ +{ + register checksum, wcj; + register char *cp; + unsigned oldcrc; + int firstch; + int attempts; + + firstch=0; /* part of logic to detect CAN CAN */ + + if (Verbose>2) + fprintf(stderr, "Sector %3d %2dk\n", Totsecs, Totsecs/8 ); + else if (Verbose>1) + fprintf(stderr, "\rSector %3d %2dk ", Totsecs, Totsecs/8 ); + for (attempts=0; attempts <= RETRYMAX; attempts++) { + Lastrx= firstch; + sendline(cseclen==1024?STX:SOH); + sendline(sectnum); + sendline(-sectnum -1); + oldcrc=checksum=0; + for (wcj=cseclen,cp=buf; --wcj>=0; ) { + sendline(*cp); + oldcrc=updcrc((0377& *cp), oldcrc); + checksum += *cp++; + } + if (Crcflg) { + oldcrc=updcrc(0,updcrc(0,oldcrc)); + sendline((int)oldcrc>>8); + sendline((int)oldcrc); + } + else + sendline(checksum); + + if (Optiong) { + firstsec = FALSE; return OK; + } + firstch = readline(Rxtimeout); +gotnak: + switch (firstch) { + case CAN: + if(Lastrx == CAN) { +cancan: + zperr("Cancelled"); return ERROR; + } + break; + case TIMEOUT: + zperr("Timeout on sector ACK"); continue; + case WANTCRC: + if (firstsec) + Crcflg = TRUE; + case NAK: + zperr("NAK on sector"); continue; + case ACK: + firstsec=FALSE; + Totsecs += (cseclen>>7); + return OK; + case ERROR: + zperr("Got burst for sector ACK"); break; + default: + zperr("Got %02x for sector ACK", firstch); break; + } + for (;;) { + Lastrx = firstch; + if ((firstch = readline(Rxtimeout)) == TIMEOUT) + break; + if (firstch == NAK || firstch == WANTCRC) + goto gotnak; + if (firstch == CAN && Lastrx == CAN) + goto cancan; + } + } + zperr("Retry Count Exceeded"); + return ERROR; +} + +/* fill buf with count chars padding with ^Z for CPM */ +int filbuf(buf, count) +register char *buf; +int count; +{ + register c, m; + + if ( !Ascii) { + m = read(fileno(in), buf, count); + if (m <= 0) + return 0; + while (m < count) + buf[m++] = 032; + return count; + } + m=count; + if (Lfseen) { + *buf++ = 012; --m; Lfseen = 0; + } + while ((c=getc(in))!=EOF) { + if (c == 012) { + *buf++ = 015; + if (--m == 0) { + Lfseen = TRUE; break; + } + } + *buf++ =c; + if (--m == 0) + break; + } + if (m==count) + return 0; + else + while (--m>=0) + *buf++ = CPMEOF; + return count; +} + +/* Fill buffer with blklen chars */ +int zfilbuf() +{ + int n; + +#ifdef TXBSIZE + /* We assume request is within buffer, or just beyond */ + txbuf = Txb + (bytcnt & TXBMASK); + if (vpos <= bytcnt) { + n = fread(txbuf, 1, blklen, in); + vpos += n; + if (n < blklen) + Eofseen = 1; + return n; + } + if (vpos >= (bytcnt+blklen)) + return blklen; + /* May be a short block if crash recovery etc. */ + Eofseen = BEofseen; + return (vpos - bytcnt); +#else + n = fread(txbuf, 1, blklen, in); + if (n < blklen) + Eofseen = 1; + return n; +#endif +} + +#ifdef TXBSIZE +int fooseek(fptr, pos, whence) +FILE *fptr; +long pos; +{ + int m, n; + + vfile("fooseek: pos =%lu vpos=%lu Canseek=%d", pos, vpos, Canseek); + /* Seek offset < current buffer */ + if (pos < (vpos -TXBSIZE +1024)) { + BEofseen = 0; + if (Canseek > 0) { + vpos = pos & ~TXBMASK; + if (vpos >= pos) + vpos -= TXBSIZE; + if (fseek(fptr, vpos, 0)) + return 1; + } + else if (Canseek == 0) + if (fseek(fptr, vpos = 0L, 0)) + return 1; + else + return 1; + while (vpos <= pos) { + n = fread(Txb, 1, TXBSIZE, fptr); + vpos += n; + vfile("n=%d vpos=%ld", n, vpos); + if (n < TXBSIZE) { + BEofseen = 1; + break; + } + } + vfile("vpos=%ld", vpos); + return 0; + } + /* Seek offset > current buffer (crash recovery, etc.) */ + if (pos > vpos) { + if (Canseek) + if (fseek(fptr, vpos = (pos & ~TXBMASK), 0)) + return 1; + while (vpos <= pos) { + txbuf = Txb + (vpos & TXBMASK); + m = TXBSIZE - (vpos & TXBMASK); + n = fread(txbuf, 1, m, fptr); + vpos += n; + vfile("bo=%d n=%d vpos=%ld", txbuf-Txb, n, vpos); + if (m < n) { + BEofseen = 1; + break; + } + } + return 0; + } + /* Seek offset is within current buffer */ + vfile("vpos=%ld", vpos); + return 0; +} +#define fseek fooseek +#endif + + +/* VARARGS1 */ +void vfile(f, a, b, c) +register char *f,*a,*b,*c; +{ + if (Verbose > 2) { + fprintf(stderr, f, a, b, c); + fprintf(stderr, "\n"); + } +} + + +void alrm(sig) +int sig; +{ + longjmp(tohere, -1); +} + + +#ifndef vax11c +/* + * readline(timeout) reads character(s) from file descriptor 0 + * timeout is in tenths of seconds + */ +int readline(timeout) +int timeout; +{ + register int c; + static char byt[1]; + + fflush(stdout); + if (setjmp(tohere)) { + zperr("TIMEOUT"); + return TIMEOUT; + } + c = timeout/10; + if (c<2) + c=2; + if (Verbose>5) { + fprintf(stderr, "Timeout=%d Calling alarm(%d) ", timeout, c); + } + signal(SIGALRM, alrm); alarm(c); + c=read(iofd, byt, 1); + alarm(0); + if (Verbose>5) + fprintf(stderr, "ret %x\n", byt[0]); + if (c<1) + return TIMEOUT; + return (byt[0]&0377); +} + +void flushmo() +{ + fflush(stdout); +} + + +void purgeline() +{ +#ifdef USG + ioctl(iofd, TCFLSH, 0); +#else + lseek(iofd, 0L, 2); +#endif +} +#endif + +/* send cancel string to get the other end to shut up */ +void canit() +{ + static char canistr[] = { + 24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0 + }; + +#ifdef vax11c + raw_wbuf(strlen(canistr), canistr); + purgeline(); +#else + printf(canistr); + fflush(stdout); +#endif +} + + +/* + * Log an error + */ +/*VARARGS1*/ +void zperr(s,p,u) +char *s, *p, *u; +{ + if (Verbose <= 0) + return; + fprintf(stderr, "\nRetry %d: ", errors); + fprintf(stderr, s, p, u); + fprintf(stderr, "\n"); +} + +/* + * substr(string, token) searches for token in string s + * returns pointer to token within string if found, NULL otherwise + */ +char * +substr(s, t) +register char *s,*t; +{ + register char *ss,*tt; + /* search for first char of token */ + for (ss=s; *s; s++) + if (*s == *t) + /* compare token with substring */ + for (ss=s,tt=t; ;) { + if (*tt == 0) + return s; + if (*ss++ != *tt++) + break; + } + return (char *)NULL; +} + +char *babble[] = { +#ifdef vax11c + " Send file(s) with ZMODEM Protocol", + "Usage: sz [-2+abdefkLlNnquvwYy] [-] file ...", + " sz [-2Ceqv] -c COMMAND", + " \\ Force next option letter to upper case", +#else + "Send file(s) with ZMODEM/YMODEM/XMODEM Protocol", + " (Y) = Option applies to YMODEM only", + " (Z) = Option applies to ZMODEM only", + "Usage: sz [-2+abdefkLlNnquvwYy] [-] file ...", + " sz [-2Ceqv] -c COMMAND", + " sb [-2adfkquv] [-] file ...", + " sx [-2akquv] [-] file", +#endif +#ifdef CSTOPB + " 2 Use 2 stop bits", +#endif + " + Append to existing destination file (Z)", + " a (ASCII) change NL to CR/LF", + " b Binary file transfer override", + " c send COMMAND (Z)", +#ifndef vax11c + " d Change '.' to '/' in pathnames (Y/Z)", +#endif + " e Escape all control characters (Z)", + " f send Full pathname (Y/Z)", + " i send COMMAND, ack Immediately (Z)", + " k Send 1024 byte packets (Y)", + " L N Limit subpacket length to N bytes (Z)", + " l N Limit frame length to N bytes (l>=L) (Z)", + " n send file if source newer (Z)", + " N send file if source newer or longer (Z)", + " o Use 16 bit CRC instead of 32 bit CRC (Z)", + " p Protect existing destination file (Z)", + " r Resume/Recover interrupted file transfer (Z)", + " q Quiet (no progress reports)", +#ifndef vax11c + " u Unlink file after transmission", +#endif + " v Verbose - provide debugging information", + " w N Window is N bytes (Z)", + " Y Yes, overwrite existing file, skip if not present at rx (Z)", + " y Yes, overwrite existing file (Z)", + "- as pathname sends standard input as sPID.sz or environment ONAME", + "" +}; + +int usage() +{ + char **pp; + + for (pp=babble; **pp; ++pp) + fprintf(stderr, "%s\n", *pp); + fprintf(stderr, "%s for %s by Chuck Forsberg, Omen Technology INC\n", + VERSION, OS); + fprintf(stderr, "\t\t\042The High Reliability Software\042\n"); + cucheck(); + exit(SS_NORMAL); +} + +/* + * Get the receiver's init parameters + */ +int getzrxinit() +{ + register n; + struct stat f; + + for (n=10; --n>=0; ) { + + switch (zgethdr(Rxhdr, 1)) { + case ZCHALLENGE: /* Echo receiver's challenge numbr */ + stohdr(Rxpos); + zshhdr(ZACK, Txhdr); + continue; + case ZCOMMAND: /* They didn't see out ZRQINIT */ + stohdr(0L); + zshhdr(ZRQINIT, Txhdr); + continue; + case ZRINIT: + Rxflags = 0377 & Rxhdr[ZF0]; + Txfcs32 = (Wantfcs32 && (Rxflags & CANFC32)); + Zctlesc |= Rxflags & TESCCTL; + Rxbuflen = (0377 & Rxhdr[ZP0])+((0377 & Rxhdr[ZP1])<<8); + if ( !(Rxflags & CANFDX)) + Txwindow = 0; + vfile("Rxbuflen=%d Tframlen=%d", Rxbuflen, Tframlen); + if ( !Fromcu) + signal(SIGINT, SIG_IGN); +#ifdef MODE2OK + mode(2); /* Set cbreak, XON/XOFF, etc. */ +#endif +#ifndef READCHECK +#ifndef USG + /* Use 1024 byte frames if no sample/interrupt */ + if (Rxbuflen < 32 || Rxbuflen > 1024) { + Rxbuflen = 1024; + vfile("Rxbuflen=%d", Rxbuflen); + } +#endif +#endif + /* Override to force shorter frame length */ + if (Rxbuflen && (Rxbuflen>Tframlen) && (Tframlen>=32)) + Rxbuflen = Tframlen; + if ( !Rxbuflen && (Tframlen>=32) && (Tframlen<=1024)) + Rxbuflen = Tframlen; + vfile("Rxbuflen=%d", Rxbuflen); + +#ifndef vax11c + /* If using a pipe for testing set lower buf len */ + fstat(iofd, &f); + if ((f.st_mode & S_IFMT) != S_IFCHR) { + Rxbuflen = 1024; + } +#endif +#ifdef BADSEEK + Canseek = 0; + Txwindow = TXBSIZE - 1024; + Txwspac = TXBSIZE/4; +#endif + /* + * If input is not a regular file, force ACK's to + * prevent running beyond the buffer limits + */ + if ( !Command) { + fstat(fileno(in), &f); + if ((f.st_mode & S_IFMT) != S_IFREG) { + Canseek = -1; +#ifdef TXBSIZE + Txwindow = TXBSIZE - 1024; + Txwspac = TXBSIZE/4; +#else + return ERROR; +#endif + } + } + /* Set initial subpacket length */ + if (blklen < 1024) { /* Command line override? */ + if (Baudrate > 300) + blklen = 256; + if (Baudrate > 1200) + blklen = 512; + if (Baudrate > 2400) + blklen = 1024; + } + if (Rxbuflen && blklen>Rxbuflen) + blklen = Rxbuflen; + if (blkopt && blklen > blkopt) + blklen = blkopt; + vfile("Rxbuflen=%d blklen=%d", Rxbuflen, blklen); + vfile("Txwindow = %u Txwspac = %d", Txwindow, Txwspac); + + return (sendzsinit()); + case ZCAN: + case TIMEOUT: + return ERROR; + case ZRQINIT: + if (Rxhdr[ZF0] == ZCOMMAND) + continue; + default: + zshhdr(ZNAK, Txhdr); + continue; + } + } + return ERROR; +} + +/* Send send-init information */ +int sendzsinit() +{ + register c; + + if (Myattn[0] == '\0' && (!Zctlesc || (Rxflags & TESCCTL))) + return OK; + errors = 0; + for (;;) { + stohdr(0L); + if (Zctlesc) { + Txhdr[ZF0] |= TESCCTL; zshhdr(ZSINIT, Txhdr); + } + else + zsbhdr(ZSINIT, Txhdr); + zsdata(Myattn, 1+strlen(Myattn), ZCRCW); + c = zgethdr(Rxhdr, 1); + switch (c) { + case ZCAN: + return ERROR; + case ZACK: + return OK; + default: + if (++errors > 19) + return ERROR; + continue; + } + } +} + +/* Send file name and related info */ +int zsendfile(buf, blen) +char *buf; +int blen; +{ + register c; + register UNSL long crc; + + for (;;) { + Txhdr[ZF0] = Lzconv; /* file conversion request */ + Txhdr[ZF1] = Lzmanag; /* file management request */ + if (Lskipnocor) + Txhdr[ZF1] |= ZMSKNOLOC; + Txhdr[ZF2] = Lztrans; /* file transport request */ + Txhdr[ZF3] = 0; + zsbhdr(ZFILE, Txhdr); + zsdata(buf, blen, ZCRCW); +again: + c = zgethdr(Rxhdr, 1); + switch (c) { + case ZRINIT: + while ((c = readline(50)) > 0) + if (c == ZPAD) { + goto again; + } + /* **** FALL THRU TO **** */ + default: + continue; + case ZCAN: + case TIMEOUT: + case ZABORT: + case ZFIN: + return ERROR; + case ZCRC: + crc = 0xFFFFFFFFL; + if (Canseek >= 0) { + while (((c = getc(in)) != EOF) && --Rxpos) + crc = UPDC32(c, crc); + crc = ~crc; + clearerr(in); /* Clear EOF */ + fseek(in, 0L, 0); + } + stohdr(crc); + zsbhdr(ZCRC, Txhdr); + goto again; + case ZSKIP: + fclose(in); return c; + case ZRPOS: + /* + * Suppress zcrcw request otherwise triggered by + * lastyunc==bytcnt + */ + if (Rxpos && fseek(in, Rxpos, 0)) + return ERROR; + Lastsync = (bytcnt = Txpos = Rxpos) -1; + return zsendfdata(); + } + } +} + +/* Send the data in the file */ +int zsendfdata() +{ + register c, e, n; + register int newcnt; + register long tcount = 0; + int junkcount; /* Counts garbage chars received by TX */ + static int tleft = 6; /* Counter for test mode */ + + Lrxpos = 0; + junkcount = 0; + Beenhereb4 = FALSE; +somemore: + if (setjmp(intrjmp)) { +waitack: + junkcount = 0; + c = getinsync(0); +gotack: + switch (c) { + default: + case ZCAN: + fclose(in); + return ERROR; + case ZSKIP: + fclose(in); + return c; + case ZACK: + case ZRPOS: + break; + case ZRINIT: + return OK; + } +#ifdef READCHECK + /* + * If the reverse channel can be tested for data, + * this logic may be used to detect error packets + * sent by the receiver, in place of setjmp/longjmp + * rdchk(fdes) returns non 0 if a character is available + */ + while (rdchk(iofd)) { +#ifdef SV + switch (checked) +#else + switch (readline(1)) +#endif + { + case CAN: + case ZPAD: + c = getinsync(1); + goto gotack; + case XOFF: /* Wait a while for an XON */ + case XOFF|0200: + readline(100); + } + } +#endif + } + + if ( !Fromcu) + signal(SIGINT, onintr); + newcnt = Rxbuflen; + Txwcnt = 0; + stohdr(Txpos); + zsbhdr(ZDATA, Txhdr); + + /* + * Special testing mode. This should force receiver to Attn,ZRPOS + * many times. Each time the signal should be caught, causing the + * file to be started over from the beginning. + */ + if (Test) { + if ( --tleft) + while (tcount < 20000) { + printf(qbf); fflush(stdout); + tcount += strlen(qbf); +#ifdef READCHECK + while (rdchk(iofd)) { +#ifdef SV + switch (checked) +#else + switch (readline(1)) +#endif + { + case CAN: + case ZPAD: +#ifdef TCFLSH + ioctl(iofd, TCFLSH, 1); +#endif + goto waitack; + case XOFF: /* Wait for XON */ + case XOFF|0200: + readline(100); + } + } +#endif + } + signal(SIGINT, SIG_IGN); canit(); + sleep(3); purgeline(); mode(0); + printf("\nsz: Tcount = %ld\n", tcount); + if (tleft) { + printf("ERROR: Interrupts Not Caught\n"); + exit(1); + } + exit(SS_NORMAL); + } + + do { + n = zfilbuf(); + if (Eofseen) + e = ZCRCE; + else if (junkcount > 3) + e = ZCRCW; + else if (bytcnt == Lastsync) + e = ZCRCW; + else if (Rxbuflen && (newcnt -= n) <= 0) + e = ZCRCW; + else if (Txwindow && (Txwcnt += n) >= Txwspac) { + Txwcnt = 0; e = ZCRCQ; + } + else + e = ZCRCG; + if (Verbose>1) + fprintf(stderr, "\r%7ld ZMODEM%s ", + Txpos, Crc32t?" CRC-32":""); + zsdata(txbuf, n, e); + bytcnt = Txpos += n; + if (e == ZCRCW) + goto waitack; +#ifdef READCHECK + /* + * If the reverse channel can be tested for data, + * this logic may be used to detect error packets + * sent by the receiver, in place of setjmp/longjmp + * rdchk(fdes) returns non 0 if a character is available + */ + fflush(stdout); + while (rdchk(iofd)) { +#ifdef SV + switch (checked) +#else + switch (readline(1)) +#endif + { + case CAN: + case ZPAD: + c = getinsync(1); + if (c == ZACK) + break; +#ifdef TCFLSH + ioctl(iofd, TCFLSH, 1); +#endif + /* zcrce - dinna wanna starta ping-pong game */ + zsdata(txbuf, 0, ZCRCE); + goto gotack; + case XOFF: /* Wait a while for an XON */ + case XOFF|0200: + readline(100); + default: + ++junkcount; + } + } +#endif /* READCHECK */ + if (Txwindow) { + while ((tcount = Txpos - Lrxpos) >= Txwindow) { + vfile("%ld window >= %u", tcount, Txwindow); + if (e != ZCRCQ) + zsdata(txbuf, 0, e = ZCRCQ); + c = getinsync(1); + if (c != ZACK) { +#ifdef TCFLSH + ioctl(iofd, TCFLSH, 1); +#endif + zsdata(txbuf, 0, ZCRCE); + goto gotack; + } + } + vfile("window = %ld", tcount); + } + } while (!Eofseen); + if ( !Fromcu) + signal(SIGINT, SIG_IGN); + + for (;;) { + stohdr(Txpos); + zsbhdr(ZEOF, Txhdr); + switch (getinsync(0)) { + case ZACK: + continue; + case ZRPOS: + goto somemore; + case ZRINIT: + return OK; + case ZSKIP: + fclose(in); + return c; + default: + fclose(in); + return ERROR; + } + } +} + +/* + * Respond to receiver's complaint, get back in sync with receiver + */ +int getinsync(flag) +int flag; +{ + register int c; + + for (;;) { + if (Test) { + printf("\r\n\n\n***** Signal Caught *****\r\n"); + Rxpos = 0; c = ZRPOS; + } else + c = zgethdr(Rxhdr, 0); + switch (c) { + case ZCAN: + case ZABORT: + case ZFIN: + case TIMEOUT: + return ERROR; + case ZRPOS: + /* ************************************* */ + /* If sending to a buffered modem, you */ + /* might send a break at this point to */ + /* dump the modem's buffer. */ + clearerr(in); /* In case file EOF seen */ + if (fseek(in, Rxpos, 0)) + return ERROR; + Eofseen = 0; + bytcnt = Lrxpos = Txpos = Rxpos; + if (Lastsync == Rxpos) { + if (++Beenhereb4 > 4) + if (blklen > 32) + blklen /= 2; + } + Lastsync = Rxpos; + return c; + case ZACK: + Lrxpos = Rxpos; + if (flag || Txpos == Rxpos) + return ZACK; + continue; + case ZRINIT: + case ZSKIP: + fclose(in); + return c; + case ERROR: + default: + zsbhdr(ZNAK, Txhdr); + continue; + } + } +} + + +/* Say "bibi" to the receiver, try to do it cleanly */ +void saybibi() +{ + for (;;) { + stohdr(0L); /* CAF Was zsbhdr - minor change */ + zshhdr(ZFIN, Txhdr); /* to make debugging easier */ + switch (zgethdr(Rxhdr, 0)) { + case ZFIN: + sendline('O'); sendline('O'); flushmo(); + case ZCAN: + case TIMEOUT: + return; + } + } +} + +/* Local screen character display function */ +void bttyout(c) +int c; +{ + if (Verbose) + putc(c, stderr); +} + +/* Send command and related info */ +int zsendcmd(buf, blen) +char *buf; +int blen; +{ + register c; + long cmdnum; + + cmdnum = getpid(); + errors = 0; + for (;;) { + stohdr(cmdnum); + Txhdr[ZF0] = Cmdack1; + zsbhdr(ZCOMMAND, Txhdr); + zsdata(buf, blen, ZCRCW); +listen: + Rxtimeout = 100; /* Ten second wait for resp. */ + c = zgethdr(Rxhdr, 1); + + switch (c) { + case ZRINIT: + goto listen; /* CAF 8-21-87 */ + case ERROR: + case TIMEOUT: + if (++errors > Cmdtries) + return ERROR; + continue; + case ZCAN: + case ZABORT: + case ZFIN: + case ZSKIP: + case ZRPOS: + return ERROR; + default: + if (++errors > 20) + return ERROR; + continue; + case ZCOMPL: + Exitcode = Rxpos; + saybibi(); + return OK; + case ZRQINIT: +#ifdef vax11c /* YAMP :== Yet Another Missing Primitive */ + return ERROR; +#else + vfile("******** RZ *******"); + system("rz"); + vfile("******** SZ *******"); + goto listen; +#endif + } + } +} + +/* + * If called as sb use YMODEM protocol + */ +void chkinvok(s) +char *s; +{ +#ifdef vax11c + Progname = "sz"; +#else + register char *p; + + p = s; + while (*p == '-') + s = ++p; + while (*p) + if (*p++ == '/') + s = p; + if (*s == 'v') { + Verbose=1; ++s; + } + Progname = s; + if (s[0]=='s' && s[1]=='b') { + Nozmodem = TRUE; blklen=1024; + } + if (s[0]=='s' && s[1]=='x') { + Modem2 = TRUE; + } +#endif +} + +void countem(argc, argv) +int argc; +register char **argv; +{ + register c; + struct stat f; + + for (Totalleft = 0, Filesleft = 0; --argc >=0; ++argv) { + f.st_size = -1; + if (Verbose>2) { + fprintf(stderr, "\nCountem: %03d %s ", argc, *argv); + fflush(stderr); + } + if (access(*argv, 04) >= 0 && stat(*argv, &f) >= 0) { + c = f.st_mode & S_IFMT; + if (c != S_IFDIR && c != S_IFBLK) { + ++Filesleft; Totalleft += f.st_size; + } + } + if (Verbose>2) + fprintf(stderr, " %ld", f.st_size); + } + if (Verbose>2) + fprintf(stderr, "\ncountem: Total %d %ld\n", + Filesleft, Totalleft); +} + +void chartest(m) +int m; +{ + register int n; + + mode(m); + printf("\r\n\nCharacter Transparency Test Mode %d\r\n", m); + printf("If Pro-YAM/ZCOMM is not displaying ^M hit ALT-V NOW.\r\n"); + printf("Hit Enter.\021"); fflush(stdout); + readline(500); + + for (n = 0; n < 256; ++n) { + if (!(n%8)) + printf("\r\n"); + printf("%02x ", n); fflush(stdout); + sendline(n); flushmo(); + printf(" "); fflush(stdout); + if (n == 127) { + printf("Hit Enter.\021"); fflush(stdout); + readline(500); + printf("\r\n"); fflush(stdout); + } + } + printf("\021\r\nEnter Characters, echo is in hex.\r\n"); + printf("Hit SPACE or pause 40 seconds for exit.\r\n"); + + while (n != TIMEOUT && n != ' ') { + n = readline(400); + printf("%02x\r\n", n); + fflush(stdout); + } + printf("\r\nMode %d character transparency test ends.\r\n", m); + fflush(stdout); +} + +/* End of sz.c */ diff --git a/commands/zmodem/zm.c b/commands/zmodem/zm.c new file mode 100755 index 000000000..9e97b08e9 --- /dev/null +++ b/commands/zmodem/zm.c @@ -0,0 +1,789 @@ +/* + * Z M . C + * ZMODEM protocol primitives + * 05-09-88 Chuck Forsberg Omen Technology Inc + * + * Entry point Functions: + * zsbhdr(type, hdr) send binary header + * zshhdr(type, hdr) send hex header + * zgethdr(hdr, eflag) receive header - binary or hex + * zsdata(buf, len, frameend) send data + * zrdata(buf, len) receive data + * stohdr(pos) store position data in Txhdr + * long rclhdr(hdr) recover position offset from header + */ + +#ifndef CANFDX +#include "zmodem.h" +#endif +int Rxtimeout = 100; /* Tenths of seconds to wait for something */ + +#ifndef UNSL +#define UNSL +#endif + + +/* Globals used by ZMODEM functions */ +int Rxframeind; /* ZBIN ZBIN32, or ZHEX type of frame received */ +int Rxtype; /* Type of header received */ +int Rxcount; /* Count of data bytes received */ +char Rxhdr[4]; /* Received header */ +char Txhdr[4]; /* Transmitted header */ +long Rxpos; /* Received file position */ +long Txpos; /* Transmitted file position */ +int Txfcs32; /* TURE means send binary frames with 32 bit FCS */ +int Crc32t; /* Display flag indicating 32 bit CRC being sent */ +int Crc32; /* Display flag indicating 32 bit CRC being received */ +int Znulls; /* Number of nulls to send at beginning of ZDATA hdr */ +char Attn[ZATTNLEN+1]; /* Attention string rx sends to tx on err */ + +static lastsent; /* Last char we sent */ +static Not8bit; /* Seven bits seen on header */ + +static char *frametypes[] = { + "Carrier Lost", /* -3 */ + "TIMEOUT", /* -2 */ + "ERROR", /* -1 */ +#define FTOFFSET 3 + "ZRQINIT", + "ZRINIT", + "ZSINIT", + "ZACK", + "ZFILE", + "ZSKIP", + "ZNAK", + "ZABORT", + "ZFIN", + "ZRPOS", + "ZDATA", + "ZEOF", + "ZFERR", + "ZCRC", + "ZCHALLENGE", + "ZCOMPL", + "ZCAN", + "ZFREECNT", + "ZCOMMAND", + "ZSTDERR", + "xxxxx" +#define FRTYPES 22 /* Total number of frame types in this array */ + /* not including psuedo negative entries */ +}; + +static char badcrc[] = "Bad CRC"; + +/* Send ZMODEM binary header hdr of type type */ +void zsbhdr(type, hdr) +int type; +register char *hdr; +{ + register int n; + register unsigned short crc; + + vfile("zsbhdr: %s %lx", frametypes[type+FTOFFSET], rclhdr(hdr)); + if (type == ZDATA) + for (n = Znulls; --n >=0; ) + xsendline(0); + + xsendline(ZPAD); xsendline(ZDLE); + + if (Crc32t=Txfcs32) + zsbh32(hdr, type); + else { + xsendline(ZBIN); zsendline(type); crc = updcrc(type, 0); + + for (n=4; --n >= 0; ++hdr) { + zsendline(*hdr); + crc = updcrc((0377& *hdr), crc); + } + crc = updcrc(0,updcrc(0,crc)); + zsendline(crc>>8); + zsendline(crc); + } + if (type != ZDATA) + flushmo(); +} + + +/* Send ZMODEM binary header hdr of type type */ +void zsbh32(hdr, type) +register char *hdr; +int type; +{ + register int n; + register UNSL long crc; + + xsendline(ZBIN32); zsendline(type); + crc = 0xFFFFFFFFL; crc = UPDC32(type, crc); + + for (n=4; --n >= 0; ++hdr) { + crc = UPDC32((0377 & *hdr), crc); + zsendline(*hdr); + } + crc = ~crc; + for (n=4; --n >= 0;) { + zsendline((int)crc); + crc >>= 8; + } +} + +/* Send ZMODEM HEX header hdr of type type */ +void zshhdr(type, hdr) +int type; +register char *hdr; +{ + register int n; + register unsigned short crc; + + vfile("zshhdr: %s %lx", frametypes[type+FTOFFSET], rclhdr(hdr)); + sendline(ZPAD); sendline(ZPAD); sendline(ZDLE); sendline(ZHEX); + zputhex(type); + Crc32t = 0; + + crc = updcrc(type, 0); + for (n=4; --n >= 0; ++hdr) { + zputhex(*hdr); crc = updcrc((0377 & *hdr), crc); + } + crc = updcrc(0,updcrc(0,crc)); + zputhex(crc>>8); zputhex(crc); + + /* Make it printable on remote machine */ + sendline(015); sendline(0212); + /* + * Uncork the remote in case a fake XOFF has stopped data flow + */ + if (type != ZFIN && type != ZACK) + sendline(021); + flushmo(); +} + +/* + * Send binary array buf of length length, with ending ZDLE sequence frameend + */ +static char *Zendnames[] = { "ZCRCE", "ZCRCG", "ZCRCQ", "ZCRCW"}; + +void zsdata(buf, length, frameend) +register char *buf; +int length; +int frameend; +{ + register unsigned short crc; + + vfile("zsdata: %d %s", length, Zendnames[(frameend-ZCRCE)&3]); + if (Crc32t) + zsda32(buf, length, frameend); + else { + crc = 0; + for (;--length >= 0; ++buf) { + zsendline(*buf); crc = updcrc((0377 & *buf), crc); + } + xsendline(ZDLE); xsendline(frameend); + crc = updcrc(frameend, crc); + + crc = updcrc(0,updcrc(0,crc)); + zsendline(crc>>8); zsendline(crc); + } + if (frameend == ZCRCW) { + xsendline(XON); flushmo(); + } +} + +void zsda32(buf, length, frameend) +register char *buf; +int length; +int frameend; +{ + register int c; + register UNSL long crc; + + crc = 0xFFFFFFFFL; + for (;--length >= 0; ++buf) { + c = *buf & 0377; + if (c & 0140) + xsendline(lastsent = c); + else + zsendline(c); + crc = UPDC32(c, crc); + } + xsendline(ZDLE); xsendline(frameend); + crc = UPDC32(frameend, crc); + + crc = ~crc; + for (length=4; --length >= 0;) { + zsendline((int)crc); crc >>= 8; + } +} + +/* + * Receive array buf of max length with ending ZDLE sequence + * and CRC. Returns the ending character or error code. + * NB: On errors may store length+1 bytes! + */ +int zrdata(buf, length) +register char *buf; +int length; +{ + register int c; + register unsigned short crc; + register char *end; + register int d; + + if (Rxframeind == ZBIN32) + return zrdat32(buf, length); + + crc = Rxcount = 0; end = buf + length; + while (buf <= end) { + if ((c = zdlread()) & ~0377) { +crcfoo: + switch (c) { + case GOTCRCE: + case GOTCRCG: + case GOTCRCQ: + case GOTCRCW: + crc = updcrc((d=c)&0377, crc); + if ((c = zdlread()) & ~0377) + goto crcfoo; + crc = updcrc(c, crc); + if ((c = zdlread()) & ~0377) + goto crcfoo; + crc = updcrc(c, crc); + if (crc & 0xFFFF) { + zperr(badcrc); + return ERROR; + } + Rxcount = length - (end - buf); + vfile("zrdata: %d %s", Rxcount, + Zendnames[(d-GOTCRCE)&3]); + return d; + case GOTCAN: + zperr("Sender Canceled"); + return ZCAN; + case TIMEOUT: + zperr("TIMEOUT"); + return c; + default: + zperr("Bad data subpacket"); + return c; + } + } + *buf++ = c; + crc = updcrc(c, crc); + } + zperr("Data subpacket too long"); + return ERROR; +} + +int zrdat32(buf, length) +register char *buf; +int length; +{ + register int c; + register UNSL long crc; + register char *end; + register int d; + + crc = 0xFFFFFFFFL; Rxcount = 0; end = buf + length; + while (buf <= end) { + if ((c = zdlread()) & ~0377) { +crcfoo: + switch (c) { + case GOTCRCE: + case GOTCRCG: + case GOTCRCQ: + case GOTCRCW: + d = c; c &= 0377; + crc = UPDC32(c, crc); + if ((c = zdlread()) & ~0377) + goto crcfoo; + crc = UPDC32(c, crc); + if ((c = zdlread()) & ~0377) + goto crcfoo; + crc = UPDC32(c, crc); + if ((c = zdlread()) & ~0377) + goto crcfoo; + crc = UPDC32(c, crc); + if ((c = zdlread()) & ~0377) + goto crcfoo; + crc = UPDC32(c, crc); + if (crc != 0xDEBB20E3) { + zperr(badcrc); + return ERROR; + } + Rxcount = length - (end - buf); + vfile("zrdat32: %d %s", Rxcount, + Zendnames[(d-GOTCRCE)&3]); + return d; + case GOTCAN: + zperr("Sender Canceled"); + return ZCAN; + case TIMEOUT: + zperr("TIMEOUT"); + return c; + default: + zperr("Bad data subpacket"); + return c; + } + } + *buf++ = c; + crc = UPDC32(c, crc); + } + zperr("Data subpacket too long"); + return ERROR; +} + + +/* + * Read a ZMODEM header to hdr, either binary or hex. + * eflag controls local display of non zmodem characters: + * 0: no display + * 1: display printing characters only + * 2: display all non ZMODEM characters + * On success, set Zmodem to 1, set Rxpos and return type of header. + * Otherwise return negative on error. + * Return ERROR instantly if ZCRCW sequence, for fast error recovery. + */ +int zgethdr(hdr, eflag) +char *hdr; +int eflag; +{ + register int c, n, cancount; + + n = Zrwindow + Baudrate; /* Max bytes before start of frame */ + Rxframeind = Rxtype = 0; + +startover: + cancount = 5; +again: + /* Return immediate ERROR if ZCRCW sequence seen */ + switch (c = readline(Rxtimeout)) { + case RCDO: + case TIMEOUT: + goto fifi; + case CAN: +gotcan: + if (--cancount <= 0) { + c = ZCAN; goto fifi; + } + switch (c = readline(1)) { + case TIMEOUT: + goto again; + case ZCRCW: + c = ERROR; + /* **** FALL THRU TO **** */ + case RCDO: + goto fifi; + default: + break; + case CAN: + if (--cancount <= 0) { + c = ZCAN; goto fifi; + } + goto again; + } + /* **** FALL THRU TO **** */ + default: +agn2: + if ( --n == 0) { + zperr("Garbage count exceeded"); + return(ERROR); + } + if (eflag && ((c &= 0177) & 0140)) + bttyout(c); + else if (eflag > 1) + bttyout(c); +#ifdef UNIX + fflush(stderr); +#endif + goto startover; + case ZPAD|0200: /* This is what we want. */ + Not8bit = c; + case ZPAD: /* This is what we want. */ + break; + } + cancount = 5; +splat: + switch (c = noxrd7()) { + case ZPAD: + goto splat; + case RCDO: + case TIMEOUT: + goto fifi; + default: + goto agn2; + case ZDLE: /* This is what we want. */ + break; + } + + switch (c = noxrd7()) { + case RCDO: + case TIMEOUT: + goto fifi; + case ZBIN: + Rxframeind = ZBIN; Crc32 = FALSE; + c = zrbhdr(hdr); + break; + case ZBIN32: + Crc32 = Rxframeind = ZBIN32; + c = zrbhdr32(hdr); + break; + case ZHEX: + Rxframeind = ZHEX; Crc32 = FALSE; + c = zrhhdr(hdr); + break; + case CAN: + goto gotcan; + default: + goto agn2; + } + Rxpos = hdr[ZP3] & 0377; + Rxpos = (Rxpos<<8) + (hdr[ZP2] & 0377); + Rxpos = (Rxpos<<8) + (hdr[ZP1] & 0377); + Rxpos = (Rxpos<<8) + (hdr[ZP0] & 0377); +fifi: + switch (c) { + case GOTCAN: + c = ZCAN; + /* **** FALL THRU TO **** */ + case ZNAK: + case ZCAN: + case ERROR: + case TIMEOUT: + case RCDO: + zperr("Got %s", frametypes[c+FTOFFSET]); + /* **** FALL THRU TO **** */ + default: + if (c >= -3 && c <= FRTYPES) + vfile("zgethdr: %s %lx", frametypes[c+FTOFFSET], Rxpos); + else + vfile("zgethdr: %d %lx", c, Rxpos); + } + return c; +} + +/* Receive a binary style header (type and position) */ +int zrbhdr(hdr) +register char *hdr; +{ + register int c, n; + register unsigned short crc; + + if ((c = zdlread()) & ~0377) + return c; + Rxtype = c; + crc = updcrc(c, 0); + + for (n=4; --n >= 0; ++hdr) { + if ((c = zdlread()) & ~0377) + return c; + crc = updcrc(c, crc); + *hdr = c; + } + if ((c = zdlread()) & ~0377) + return c; + crc = updcrc(c, crc); + if ((c = zdlread()) & ~0377) + return c; + crc = updcrc(c, crc); + if (crc & 0xFFFF) { + zperr(badcrc); + return ERROR; + } +#ifdef ZMODEM + Protocol = ZMODEM; +#endif + Zmodem = 1; + return Rxtype; +} + +/* Receive a binary style header (type and position) with 32 bit FCS */ +int zrbhdr32(hdr) +register char *hdr; +{ + register int c, n; + register UNSL long crc; + + if ((c = zdlread()) & ~0377) + return c; + Rxtype = c; + crc = 0xFFFFFFFFL; crc = UPDC32(c, crc); +#ifdef DEBUGZ + vfile("zrbhdr32 c=%X crc=%lX", c, crc); +#endif + + for (n=4; --n >= 0; ++hdr) { + if ((c = zdlread()) & ~0377) + return c; + crc = UPDC32(c, crc); + *hdr = c; +#ifdef DEBUGZ + vfile("zrbhdr32 c=%X crc=%lX", c, crc); +#endif + } + for (n=4; --n >= 0;) { + if ((c = zdlread()) & ~0377) + return c; + crc = UPDC32(c, crc); +#ifdef DEBUGZ + vfile("zrbhdr32 c=%X crc=%lX", c, crc); +#endif + } + if (crc != 0xDEBB20E3) { + zperr(badcrc); + return ERROR; + } +#ifdef ZMODEM + Protocol = ZMODEM; +#endif + Zmodem = 1; + return Rxtype; +} + + +/* Receive a hex style header (type and position) */ +int zrhhdr(hdr) +char *hdr; +{ + register int c; + register unsigned short crc; + register int n; + + if ((c = zgethex()) < 0) + return c; + Rxtype = c; + crc = updcrc(c, 0); + + for (n=4; --n >= 0; ++hdr) { + if ((c = zgethex()) < 0) + return c; + crc = updcrc(c, crc); + *hdr = c; + } + if ((c = zgethex()) < 0) + return c; + crc = updcrc(c, crc); + if ((c = zgethex()) < 0) + return c; + crc = updcrc(c, crc); + if (crc & 0xFFFF) { + zperr(badcrc); return ERROR; + } + switch ( c = readline(1)) { + case 0215: + Not8bit = c; + /* **** FALL THRU TO **** */ + case 015: + /* Throw away possible cr/lf */ + switch (c = readline(1)) { + case 012: + Not8bit |= c; + } + } +#ifdef ZMODEM + Protocol = ZMODEM; +#endif + Zmodem = 1; return Rxtype; +} + +/* Send a byte as two hex digits */ +void zputhex(c) +register int c; +{ + static char digits[] = "0123456789abcdef"; + + if (Verbose>8) + vfile("zputhex: %02X", c); + sendline(digits[(c&0xF0)>>4]); + sendline(digits[(c)&0xF]); +} + +/* + * Send character c with ZMODEM escape sequence encoding. + * Escape XON, XOFF. Escape CR following @ (Telenet net escape) + */ +void zsendline(c) +int c; +{ + + /* Quick check for non control characters */ + if (c & 0140) + xsendline(lastsent = c); + else { + switch (c &= 0377) { + case ZDLE: + xsendline(ZDLE); + xsendline (lastsent = (c ^= 0100)); + break; + case 015: + case 0215: + if (!Zctlesc && (lastsent & 0177) != '@') + goto sendit; + /* **** FALL THRU TO **** */ + case 020: + case 021: + case 023: + case 0220: + case 0221: + case 0223: + xsendline(ZDLE); + c ^= 0100; + sendit: + xsendline(lastsent = c); + break; + default: + if (Zctlesc && ! (c & 0140)) { + xsendline(ZDLE); + c ^= 0100; + } + xsendline(lastsent = c); + } + } +} + +/* Decode two lower case hex digits into an 8 bit byte value */ +int zgethex() +{ + register int c; + + c = zgeth1(); + if (Verbose>8) + vfile("zgethex: %02X", c); + return c; +} +int zgeth1() +{ + register int c, n; + + if ((c = noxrd7()) < 0) + return c; + n = c - '0'; + if (n > 9) + n -= ('a' - ':'); + if (n & ~0xF) + return ERROR; + if ((c = noxrd7()) < 0) + return c; + c -= '0'; + if (c > 9) + c -= ('a' - ':'); + if (c & ~0xF) + return ERROR; + c += (n<<4); + return c; +} + +/* + * Read a byte, checking for ZMODEM escape encoding + * including CAN*5 which represents a quick abort + */ +int zdlread() +{ + register int c; + +again: + /* Quick check for non control characters */ + if ((c = readline(Rxtimeout)) & 0140) + return c; + switch (c) { + case ZDLE: + break; + case 023: + case 0223: + case 021: + case 0221: + goto again; + default: + if (Zctlesc && !(c & 0140)) { + goto again; + } + return c; + } +again2: + if ((c = readline(Rxtimeout)) < 0) + return c; + if (c == CAN && (c = readline(Rxtimeout)) < 0) + return c; + if (c == CAN && (c = readline(Rxtimeout)) < 0) + return c; + if (c == CAN && (c = readline(Rxtimeout)) < 0) + return c; + switch (c) { + case CAN: + return GOTCAN; + case ZCRCE: + case ZCRCG: + case ZCRCQ: + case ZCRCW: + return (c | GOTOR); + case ZRUB0: + return 0177; + case ZRUB1: + return 0377; + case 023: + case 0223: + case 021: + case 0221: + goto again2; + default: + if (Zctlesc && ! (c & 0140)) { + goto again2; + } + if ((c & 0140) == 0100) + return (c ^ 0100); + break; + } + if (Verbose>1) + zperr("Bad escape sequence %x", c); + return ERROR; +} + +/* + * Read a character from the modem line with timeout. + * Eat parity, XON and XOFF characters. + */ +int noxrd7() +{ + register int c; + + for (;;) { + if ((c = readline(Rxtimeout)) < 0) + return c; + switch (c &= 0177) { + case XON: + case XOFF: + continue; + default: + if (Zctlesc && !(c & 0140)) + continue; + case '\r': + case '\n': + case ZDLE: + return c; + } + } +} + +/* Store long integer pos in Txhdr */ +void stohdr(pos) +long pos; +{ + Txhdr[ZP0] = pos; + Txhdr[ZP1] = pos>>8; + Txhdr[ZP2] = pos>>16; + Txhdr[ZP3] = pos>>24; +} + +/* Recover a long integer from a header */ +long +rclhdr(hdr) +register char *hdr; +{ + register long l; + + l = (hdr[ZP3] & 0377); + l = (l << 8) | (hdr[ZP2] & 0377); + l = (l << 8) | (hdr[ZP1] & 0377); + l = (l << 8) | (hdr[ZP0] & 0377); + return l; +} + +/* End of zm.c */ diff --git a/commands/zmodem/zmodem.h b/commands/zmodem/zmodem.h new file mode 100755 index 000000000..7b3f203ea --- /dev/null +++ b/commands/zmodem/zmodem.h @@ -0,0 +1,161 @@ +/* + * Z M O D E M . H Manifest constants for ZMODEM + * application to application file transfer protocol + * 05-23-87 Chuck Forsberg Omen Technology Inc + */ +#define ZPAD '*' /* 052 Padding character begins frames */ +#define ZDLE 030 /* Ctrl-X Zmodem escape - `ala BISYNC DLE */ +#define ZDLEE (ZDLE^0100) /* Escaped ZDLE as transmitted */ +#define ZBIN 'A' /* Binary frame indicator */ +#define ZHEX 'B' /* HEX frame indicator */ +#define ZBIN32 'C' /* Binary frame with 32 bit FCS */ + +/* Frame types (see array "frametypes" in zm.c) */ +#define ZRQINIT 0 /* Request receive init */ +#define ZRINIT 1 /* Receive init */ +#define ZSINIT 2 /* Send init sequence (optional) */ +#define ZACK 3 /* ACK to above */ +#define ZFILE 4 /* File name from sender */ +#define ZSKIP 5 /* To sender: skip this file */ +#define ZNAK 6 /* Last packet was garbled */ +#define ZABORT 7 /* Abort batch transfers */ +#define ZFIN 8 /* Finish session */ +#define ZRPOS 9 /* Resume data trans at this position */ +#define ZDATA 10 /* Data packet(s) follow */ +#define ZEOF 11 /* End of file */ +#define ZFERR 12 /* Fatal Read or Write error Detected */ +#define ZCRC 13 /* Request for file CRC and response */ +#define ZCHALLENGE 14 /* Receiver's Challenge */ +#define ZCOMPL 15 /* Request is complete */ +#define ZCAN 16 /* Other end canned session with CAN*5 */ +#define ZFREECNT 17 /* Request for free bytes on filesystem */ +#define ZCOMMAND 18 /* Command from sending program */ +#define ZSTDERR 19 /* Output to standard error, data follows */ + +/* ZDLE sequences */ +#define ZCRCE 'h' /* CRC next, frame ends, header packet follows */ +#define ZCRCG 'i' /* CRC next, frame continues nonstop */ +#define ZCRCQ 'j' /* CRC next, frame continues, ZACK expected */ +#define ZCRCW 'k' /* CRC next, ZACK expected, end of frame */ +#define ZRUB0 'l' /* Translate to rubout 0177 */ +#define ZRUB1 'm' /* Translate to rubout 0377 */ + +/* zdlread return values (internal) */ +/* -1 is general error, -2 is timeout */ +#define GOTOR 0400 +#define GOTCRCE (ZCRCE|GOTOR) /* ZDLE-ZCRCE received */ +#define GOTCRCG (ZCRCG|GOTOR) /* ZDLE-ZCRCG received */ +#define GOTCRCQ (ZCRCQ|GOTOR) /* ZDLE-ZCRCQ received */ +#define GOTCRCW (ZCRCW|GOTOR) /* ZDLE-ZCRCW received */ +#define GOTCAN (GOTOR|030) /* CAN*5 seen */ + +/* Byte positions within header array */ +#define ZF0 3 /* First flags byte */ +#define ZF1 2 +#define ZF2 1 +#define ZF3 0 +#define ZP0 0 /* Low order 8 bits of position */ +#define ZP1 1 +#define ZP2 2 +#define ZP3 3 /* High order 8 bits of file position */ + +/* Bit Masks for ZRINIT flags byte ZF0 */ +#define CANFDX 01 /* Rx can send and receive true FDX */ +#define CANOVIO 02 /* Rx can receive data during disk I/O */ +#define CANBRK 04 /* Rx can send a break signal */ +#define CANCRY 010 /* Receiver can decrypt */ +#define CANLZW 020 /* Receiver can uncompress */ +#define CANFC32 040 /* Receiver can use 32 bit Frame Check */ +#define ESCCTL 0100 /* Receiver expects ctl chars to be escaped */ +#define ESC8 0200 /* Receiver expects 8th bit to be escaped */ + +/* Parameters for ZSINIT frame */ +#define ZATTNLEN 32 /* Max length of attention string */ +/* Bit Masks for ZSINIT flags byte ZF0 */ +#define TESCCTL 0100 /* Transmitter expects ctl chars to be escaped */ +#define TESC8 0200 /* Transmitter expects 8th bit to be escaped */ + +/* Parameters for ZFILE frame */ +/* Conversion options one of these in ZF0 */ +#define ZCBIN 1 /* Binary transfer - inhibit conversion */ +#define ZCNL 2 /* Convert NL to local end of line convention */ +#define ZCRESUM 3 /* Resume interrupted file transfer */ +/* Management include options, one of these ored in ZF1 */ +#define ZMSKNOLOC 0200 /* Skip file if not present at rx */ +/* Management options, one of these ored in ZF1 */ +#define ZMMASK 037 /* Mask for the choices below */ +#define ZMNEWL 1 /* Transfer if source newer or longer */ +#define ZMCRC 2 /* Transfer if different file CRC or length */ +#define ZMAPND 3 /* Append contents to existing file (if any) */ +#define ZMCLOB 4 /* Replace existing file */ +#define ZMNEW 5 /* Transfer if source newer */ + /* Number 5 is alive ... */ +#define ZMDIFF 6 /* Transfer if dates or lengths different */ +#define ZMPROT 7 /* Protect destination file */ +/* Transport options, one of these in ZF2 */ +#define ZTLZW 1 /* Lempel-Ziv compression */ +#define ZTCRYPT 2 /* Encryption */ +#define ZTRLE 3 /* Run Length encoding */ +/* Extended options for ZF3, bit encoded */ +#define ZXSPARS 64 /* Encoding for sparse file operations */ + +/* Parameters for ZCOMMAND frame ZF0 (otherwise 0) */ +#define ZCACK1 1 /* Acknowledge, then do command */ + +/* Globals used by ZMODEM functions */ +extern Rxframeind; /* ZBIN ZBIN32, or ZHEX type of frame received */ +extern Rxtype; /* Type of header received */ +extern Rxcount; /* Count of data bytes received */ +extern Zrwindow; /* RX window size (controls garbage count) */ +extern Rxtimeout; /* Tenths of seconds to wait for something */ +extern char Rxhdr[4]; /* Received header */ +extern char Txhdr[4]; /* Transmitted header */ +extern long Rxpos; /* Received file position */ +extern long Txpos; /* Transmitted file position */ +extern Txfcs32; /* TURE means send binary frames with 32 bit FCS */ +extern Crc32t; /* Display flag indicating 32 bit CRC being sent */ +extern Crc32; /* Display flag indicating 32 bit CRC being received */ +extern Znulls; /* Number of nulls to send at beginning of ZDATA hdr */ +extern char Attn[ZATTNLEN+1]; /* Attention string rx sends to tx on err */ + +/* crctab.c */ + +_PROTOTYPE(long UPDC32 , (int b , long c )); + +/* rbsb.c */ + +_PROTOTYPE(void from_cu , (void)); +_PROTOTYPE(void cucheck , (void)); +_PROTOTYPE(int rdchk , (int f )); +_PROTOTYPE(int rdchk , (int f )); +_PROTOTYPE(int mode , (int n )); +_PROTOTYPE(void sendbrk , (void)); + +/* zm.c */ + +_PROTOTYPE(void zsbhdr , (int type , char *hdr )); +_PROTOTYPE(void zsbh32 , (char *hdr , int type )); +_PROTOTYPE(void zshhdr , (int type , char *hdr )); +_PROTOTYPE(void zsdata , (char *buf , int length , int frameend )); +_PROTOTYPE(void zsda32 , (char *buf , int length , int frameend )); +_PROTOTYPE(int zrdata , (char *buf , int length )); +_PROTOTYPE(int zrdat32 , (char *buf , int length )); +_PROTOTYPE(int zgethdr , (char *hdr , int eflag )); +_PROTOTYPE(int zrbhdr , (char *hdr )); +_PROTOTYPE(int zrbhdr32 , (char *hdr )); +_PROTOTYPE(int zrhhdr , (char *hdr )); +_PROTOTYPE(void zputhex , (int c )); +_PROTOTYPE(void zsendline , (int c )); +_PROTOTYPE(int zgethex , (void)); +_PROTOTYPE(int zgeth1 , (void)); +_PROTOTYPE(int zdlread , (void)); +_PROTOTYPE(int noxrd7 , (void)); +_PROTOTYPE(void stohdr , (long pos )); +_PROTOTYPE(long rclhdr , (char *hdr )); + +/* rz.c sz.c */ + +void vfile(); +_PROTOTYPE(void bibi , (int n )); + +/* End of ZMODEM.H */ diff --git a/conflicts b/conflicts new file mode 100644 index 000000000..12911369f --- /dev/null +++ b/conflicts @@ -0,0 +1,8 @@ +./Makefile +./commands/ash/test/malloc.c +./servers/fs/const.h +./servers/fs/main.c +./servers/fs/mount.c +./servers/fs/protect.c +./servers/fs/read.c +./servers/mm/misc.c diff --git a/drivers/Makefile b/drivers/Makefile new file mode 100644 index 000000000..19ad2df70 --- /dev/null +++ b/drivers/Makefile @@ -0,0 +1,22 @@ +# Makefile for all device drivers. +# +MAKE = exec make -$(MAKEFLAGS) + +usage: + @echo "" >&2 + @echo "Makefile for all device drivers." >&2 + @echo "Usage:" >&2 + @echo " make build # Compile all device drivers locally" >&2 + @echo " make clean # Remove local compiler results" >&2 + @echo " make install # Install drivers to /etc/drivers/" >&2 + @echo " (requires root privileges)" >&2 + @echo "" >&2 + +build: all +all install clean: + cd ./tty && $(MAKE) $@ + cd ./memory && $(MAKE) $@ + cd ./at_wini && $(MAKE) $@ + cd ./floppy && $(MAKE) $@ + cd ./printer && $(MAKE) $@ + diff --git a/drivers/at_wini/Makefile b/drivers/at_wini/Makefile new file mode 100644 index 000000000..19054d4f9 --- /dev/null +++ b/drivers/at_wini/Makefile @@ -0,0 +1,55 @@ +# Makefile for the AT disk driver (AT_WINI) +DRIVER = at_wini + +# directories +u = /usr +i = $u/include +s = $i/sys +m = $i/minix +b = $i/ibm +d = $u/src/drivers + +# programs, flags, etc. +MAKE = exec make +CC = exec cc +CFLAGS = -I$i +LDFLAGS = -i +LIBS = -lsys -lutils -ltimers + +OBJ = at_wini.o +LIBDRIVER = $d/libdriver/driver.o $d/libdriver/drvlib.o + + +# build local binary +all build: $(DRIVER) +$(DRIVER): $(OBJ) $(LIBDRIVER) + $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBDRIVER) $(LIBS) + install -S 256w $(DRIVER) + +$(LIBDRIVER): + cd $d/libdriver && $(MAKE) + +# install with other drivers +install: /usr/sbin/drivers/$(DRIVER) +/usr/sbin/drivers/$(DRIVER): $(DRIVER) + install -o root -cs $? $@ + +# clean up local files +clean: + rm -f $(DRIVER) *.o *.bak + + +# dependencies +a = $d/drivers.h $b/interrupt.h $b/bios.h \ + $i/ansi.h $i/string.h $i/limits.h $i/stddef.h $i/errno.h \ + $m/config.h $m/type.h $m/com.h $m/callnr.h $m/const.h $s/types.h \ + $m/syslib.h $s/types.h \ + $m/utils.h $m/serverassert.h $m/devio.h +l = $d/libdriver/driver.h $d/libdriver/driver.c $m/partition.h $m/u64.h + + +at_wini.o: $a $l + +$(LIBDRIVER): $a $l +$(LIBDRIVER): $s/ioc_disk.h + diff --git a/drivers/at_wini/at_wini.c b/drivers/at_wini/at_wini.c new file mode 100644 index 000000000..8b65f7275 --- /dev/null +++ b/drivers/at_wini/at_wini.c @@ -0,0 +1,1204 @@ +/* This file contains the device dependent part of a driver for the IBM-AT + * winchester controller. Written by Adri Koppes. + * + * The file contains one entry point: + * + * at_winchester_task: main entry when system is brought up + * + * Changes: + * Nov 18, 2004 moved AT disk driver to user-space (Jorrit N. Herder) + * Aug 20, 2004 watchdogs replaced by sync alarms (Jorrit N. Herder) + * May 02, 2004 sys_flagalrm() replaces micro_elapsed() (Jorrit N. Herder) + * Mar 23, 2000 added ATAPI CDROM support (Michael Temari) + * May 14, 2000 d-d/i rewrite (Kees J. Bot) + * Apr 13, 1992 device dependent/independent split (Kees J. Bot) + */ + +#include "at_wini.h" +#include <minix/utils.h> +#include <minix/keymap.h> + +#if ENABLE_AT_WINI + +#define ATAPI_DEBUG 0 /* To debug ATAPI code. */ + +/* I/O Ports used by winchester disk controllers. */ + +/* Read and write registers */ +#define REG_BASE0 0x1F0 /* base register of controller 0 */ +#define REG_BASE1 0x170 /* base register of controller 1 */ +#define REG_DATA 0 /* data register (offset from the base reg.) */ +#define REG_PRECOMP 1 /* start of write precompensation */ +#define REG_COUNT 2 /* sectors to transfer */ +#define REG_SECTOR 3 /* sector number */ +#define REG_CYL_LO 4 /* low byte of cylinder number */ +#define REG_CYL_HI 5 /* high byte of cylinder number */ +#define REG_LDH 6 /* lba, drive and head */ +#define LDH_DEFAULT 0xA0 /* ECC enable, 512 bytes per sector */ +#define LDH_LBA 0x40 /* Use LBA addressing */ +#define ldh_init(drive) (LDH_DEFAULT | ((drive) << 4)) + +/* Read only registers */ +#define REG_STATUS 7 /* status */ +#define STATUS_BSY 0x80 /* controller busy */ +#define STATUS_RDY 0x40 /* drive ready */ +#define STATUS_WF 0x20 /* write fault */ +#define STATUS_SC 0x10 /* seek complete (obsolete) */ +#define STATUS_DRQ 0x08 /* data transfer request */ +#define STATUS_CRD 0x04 /* corrected data */ +#define STATUS_IDX 0x02 /* index pulse */ +#define STATUS_ERR 0x01 /* error */ +#define STATUS_ADMBSY 0x100 /* administratively busy (software) */ +#define REG_ERROR 1 /* error code */ +#define ERROR_BB 0x80 /* bad block */ +#define ERROR_ECC 0x40 /* bad ecc bytes */ +#define ERROR_ID 0x10 /* id not found */ +#define ERROR_AC 0x04 /* aborted command */ +#define ERROR_TK 0x02 /* track zero error */ +#define ERROR_DM 0x01 /* no data address mark */ + +/* Write only registers */ +#define REG_COMMAND 7 /* command */ +#define CMD_IDLE 0x00 /* for w_command: drive idle */ +#define CMD_RECALIBRATE 0x10 /* recalibrate drive */ +#define CMD_READ 0x20 /* read data */ +#define CMD_WRITE 0x30 /* write data */ +#define CMD_READVERIFY 0x40 /* read verify */ +#define CMD_FORMAT 0x50 /* format track */ +#define CMD_SEEK 0x70 /* seek cylinder */ +#define CMD_DIAG 0x90 /* execute device diagnostics */ +#define CMD_SPECIFY 0x91 /* specify parameters */ +#define ATA_IDENTIFY 0xEC /* identify drive */ +#define REG_CTL 0x206 /* control register */ +#define CTL_NORETRY 0x80 /* disable access retry */ +#define CTL_NOECC 0x40 /* disable ecc retry */ +#define CTL_EIGHTHEADS 0x08 /* more than eight heads */ +#define CTL_RESET 0x04 /* reset controller */ +#define CTL_INTDISABLE 0x02 /* disable interrupts */ + +#if ENABLE_ATAPI +#define ERROR_SENSE 0xF0 /* sense key mask */ +#define SENSE_NONE 0x00 /* no sense key */ +#define SENSE_RECERR 0x10 /* recovered error */ +#define SENSE_NOTRDY 0x20 /* not ready */ +#define SENSE_MEDERR 0x30 /* medium error */ +#define SENSE_HRDERR 0x40 /* hardware error */ +#define SENSE_ILRQST 0x50 /* illegal request */ +#define SENSE_UATTN 0x60 /* unit attention */ +#define SENSE_DPROT 0x70 /* data protect */ +#define SENSE_ABRT 0xb0 /* aborted command */ +#define SENSE_MISCOM 0xe0 /* miscompare */ +#define ERROR_MCR 0x08 /* media change requested */ +#define ERROR_ABRT 0x04 /* aborted command */ +#define ERROR_EOM 0x02 /* end of media detected */ +#define ERROR_ILI 0x01 /* illegal length indication */ +#define REG_FEAT 1 /* features */ +#define FEAT_OVERLAP 0x02 /* overlap */ +#define FEAT_DMA 0x01 /* dma */ +#define REG_IRR 2 /* interrupt reason register */ +#define IRR_REL 0x04 /* release */ +#define IRR_IO 0x02 /* direction for xfer */ +#define IRR_COD 0x01 /* command or data */ +#define REG_SAMTAG 3 +#define REG_CNT_LO 4 /* low byte of cylinder number */ +#define REG_CNT_HI 5 /* high byte of cylinder number */ +#define REG_DRIVE 6 /* drive select */ +#define REG_STATUS 7 /* status */ +#define STATUS_BSY 0x80 /* controller busy */ +#define STATUS_DRDY 0x40 /* drive ready */ +#define STATUS_DMADF 0x20 /* dma ready/drive fault */ +#define STATUS_SRVCDSC 0x10 /* service or dsc */ +#define STATUS_DRQ 0x08 /* data transfer request */ +#define STATUS_CORR 0x04 /* correctable error occurred */ +#define STATUS_CHECK 0x01 /* check error */ + +#define ATAPI_PACKETCMD 0xA0 /* packet command */ +#define ATAPI_IDENTIFY 0xA1 /* identify drive */ +#define SCSI_READ10 0x28 /* read from disk */ + +#define CD_SECTOR_SIZE 2048 /* sector size of a CD-ROM */ +#endif /* ATAPI */ + +/* Interrupt request lines. */ +#define NO_IRQ 0 /* no IRQ set yet */ + +/* Common command block */ +struct command { + u8_t precomp; /* REG_PRECOMP, etc. */ + u8_t count; + u8_t sector; + u8_t cyl_lo; + u8_t cyl_hi; + u8_t ldh; + u8_t command; +}; + + +/* Error codes */ +#define ERR (-1) /* general error */ +#define ERR_BAD_SECTOR (-2) /* block marked bad detected */ + +/* Some controllers don't interrupt, the clock will wake us up. */ +#define WAKEUP (32*HZ) /* drive may be out for 31 seconds max */ + +/* Miscellaneous. */ +#define MAX_DRIVES 4 /* this driver supports 4 drives (d0 - d3) */ +#if _WORD_SIZE > 2 +#define MAX_SECS 256 /* controller can transfer this many sectors */ +#else +#define MAX_SECS 127 /* but not to a 16 bit process */ +#endif +#define MAX_ERRORS 4 /* how often to try rd/wt before quitting */ +#define NR_DEVICES (MAX_DRIVES * DEV_PER_DRIVE) +#define SUB_PER_DRIVE (NR_PARTITIONS * NR_PARTITIONS) +#define NR_SUBDEVS (MAX_DRIVES * SUB_PER_DRIVE) +#define DELAY_USECS 1000 /* controller timeout in microseconds */ +#define DELAY_TICKS 1 /* controller timeout in ticks */ +#define TIMEOUT_USECS 5000000 /* controller timeout in microseconds */ +#define TIMEOUT_TICKS 300 /* controller timeout in ticks */ +#define RECOVERY_USECS 500000 /* controller recovery time in microseconds */ +#define RECOVERY_TICKS 30 /* controller recovery time in ticks */ +#define INITIALIZED 0x01 /* drive is initialized */ +#define DEAF 0x02 /* controller must be reset */ +#define SMART 0x04 /* drive supports ATA commands */ +#if ENABLE_ATAPI +#define ATAPI 0x08 /* it is an ATAPI device */ +#else +#define ATAPI 0 /* don't bother with ATAPI; optimise out */ +#endif + + +/* Variables. */ +PRIVATE struct wini { /* main drive struct, one entry per drive */ + unsigned state; /* drive state: deaf, initialized, dead */ + unsigned base; /* base register of the register file */ + unsigned irq; /* interrupt request line */ + unsigned lcylinders; /* logical number of cylinders (BIOS) */ + unsigned lheads; /* logical number of heads */ + unsigned lsectors; /* logical number of sectors per track */ + unsigned pcylinders; /* physical number of cylinders (translated) */ + unsigned pheads; /* physical number of heads */ + unsigned psectors; /* physical number of sectors per track */ + unsigned ldhpref; /* top four bytes of the LDH (head) register */ + unsigned precomp; /* write precompensation cylinder / 4 */ + unsigned max_count; /* max request for this drive */ + unsigned open_ct; /* in-use count */ + struct device part[DEV_PER_DRIVE]; /* disks and partitions */ + struct device subpart[SUB_PER_DRIVE]; /* subpartitions */ +} wini[MAX_DRIVES], *w_wn; + +PRIVATE int win_tasknr; /* my task number */ +PRIVATE int w_command; /* current command in execution */ +PRIVATE u8_t w_byteval; /* used for SYS_IRQCTL */ +PRIVATE int w_status; /* status after interrupt */ +PRIVATE int w_drive; /* selected drive */ +PRIVATE struct device *w_dv; /* device's base and size */ + +FORWARD _PROTOTYPE( void init_params, (void) ); +FORWARD _PROTOTYPE( int w_do_open, (struct driver *dp, message *m_ptr) ); +FORWARD _PROTOTYPE( struct device *w_prepare, (int device) ); +FORWARD _PROTOTYPE( int w_identify, (void) ); +FORWARD _PROTOTYPE( char *w_name, (void) ); +FORWARD _PROTOTYPE( int w_specify, (void) ); +FORWARD _PROTOTYPE( int w_transfer, (int proc_nr, int opcode, off_t position, + iovec_t *iov, unsigned nr_req) ); +FORWARD _PROTOTYPE( int com_out, (struct command *cmd) ); +FORWARD _PROTOTYPE( void w_need_reset, (void) ); +FORWARD _PROTOTYPE( int w_do_close, (struct driver *dp, message *m_ptr) ); +FORWARD _PROTOTYPE( int com_simple, (struct command *cmd) ); +FORWARD _PROTOTYPE( void w_timeout, (void) ); +FORWARD _PROTOTYPE( int w_reset, (void) ); +FORWARD _PROTOTYPE( void w_intr_wait, (void) ); +FORWARD _PROTOTYPE( int at_intr_wait, (void) ); +FORWARD _PROTOTYPE( int w_waitfor, (int mask, int value) ); +FORWARD _PROTOTYPE( void w_geometry, (struct partition *entry) ); +#if ENABLE_ATAPI +FORWARD _PROTOTYPE( int atapi_sendpacket, (u8_t *packet, unsigned cnt) ); +FORWARD _PROTOTYPE( int atapi_intr_wait, (void) ); +FORWARD _PROTOTYPE( int atapi_open, (void) ); +FORWARD _PROTOTYPE( void atapi_close, (void) ); +FORWARD _PROTOTYPE( int atapi_transfer, (int proc_nr, int opcode, + off_t position, iovec_t *iov, unsigned nr_req) ); +#endif + + +/* Entry points to this driver. */ +PRIVATE struct driver w_dtab = { + w_name, /* current device's name */ + w_do_open, /* open or mount request, initialize device */ + w_do_close, /* release device */ + do_diocntl, /* get or set a partition's geometry */ + w_prepare, /* prepare for I/O on a given minor device */ + w_transfer, /* do the I/O */ + nop_cleanup, /* nothing to clean up */ + w_geometry, /* tell the geometry of the disk */ + nop_stop, /* no cleanup needed on shutdown */ + nop_alarm, /* ignore leftover alarms */ + nop_stats, /* drivers statistics */ +}; + + +/*===========================================================================* + * at_winchester_task * + *===========================================================================*/ +PUBLIC void main() +{ + int s; +/* Register function key for debugging dumps. */ + fkey_enable(SF8); + +/* Set special disk parameters then call the generic main loop. */ + if ((s=get_own_proc_nr(&win_tasknr)) != OK) + server_panic(w_name(),"Couldn't get own process number",s); + init_params(); + driver_task(&w_dtab); +} + + +/*============================================================================* + * init_params * + *============================================================================*/ +PRIVATE void init_params() +{ +/* This routine is called at startup to initialize the drive parameters. */ + + u16_t parv[2]; + unsigned int vector, size; + int drive, nr_drives; + struct wini *wn; + u8_t params[16]; + int s; + + /* Get the number of drives from the BIOS data area */ + if ((s=sys_vircopy(SELF, BIOS_SEG, ADR_WINI_PARAMS, + SELF, D, (vir_bytes) params, LEN_WINI_PARAMS)) != OK) + server_panic(w_name(), "Couldn't read BIOS", s); + if ((nr_drives = params[0]) > 2) nr_drives = 2; + + for (drive = 0, wn = wini; drive < MAX_DRIVES; drive++, wn++) { + if (drive < nr_drives) { + /* Copy the BIOS parameter vector */ + vector = drive == 0 ? ADR_WINI_0_PARM_VEC:ADR_WINI_1_PARM_VEC; + size = drive == 0 ? LEN_WINI_0_PARM_VEC:LEN_WINI_1_PARM_VEC; + if ((s=sys_vircopy(SELF, BIOS_SEG, vector, + SELF, D, (vir_bytes) parv, size)) != OK) + server_panic(w_name(), "Couldn't read BIOS", s); + + /* Calculate the address of the parameters and copy them */ + if ((s=sys_vircopy( + SELF, BIOS_SEG, hclick_to_physb(parv[1]) + parv[0], + SELF, D, (phys_bytes) params, 16L))!=OK) + server_panic(w_name(),"Couldn't copy parameters", s); + + /* Copy the parameters to the structures of the drive */ + wn->lcylinders = bp_cylinders(params); + wn->lheads = bp_heads(params); + wn->lsectors = bp_sectors(params); + wn->precomp = bp_precomp(params) >> 2; + } + wn->ldhpref = ldh_init(drive); + wn->max_count = MAX_SECS << SECTOR_SHIFT; + + /* Base I/O register to address controller. */ + wn->base = drive < 2 ? REG_BASE0 : REG_BASE1; + } +} + + +/*============================================================================* + * w_do_open * + *============================================================================*/ +PRIVATE int w_do_open(dp, m_ptr) +struct driver *dp; +message *m_ptr; +{ +/* Device open: Initialize the controller and read the partition table. */ + + struct wini *wn; + + if (w_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO); + wn = w_wn; + + if (wn->state == 0) { + /* Try to identify the device. */ + if (w_identify() != OK) { + printf("%s: probe failed\n", w_name()); + if (wn->state & DEAF) w_reset(); + wn->state = 0; + return(ENXIO); + } + } + if (wn->open_ct == 0) { +#if ENABLE_ATAPI + if (wn->state & ATAPI) { + int r; + + if (m_ptr->COUNT & W_BIT) return(EACCES); + if ((r = atapi_open()) != OK) return(r); + } +#endif + /* Partition the disk. */ + partition(&w_dtab, w_drive * DEV_PER_DRIVE, P_PRIMARY); + wn->open_ct++; + } + return(OK); +} + + +/*===========================================================================* + * w_prepare * + *===========================================================================*/ +PRIVATE struct device *w_prepare(device) +int device; +{ +/* Prepare for I/O on a device. */ + + if (device < NR_DEVICES) { /* d0, d0p[0-3], d1, ... */ + w_drive = device / DEV_PER_DRIVE; /* save drive number */ + w_wn = &wini[w_drive]; + w_dv = &w_wn->part[device % DEV_PER_DRIVE]; + } else + if ((unsigned) (device -= MINOR_d0p0s0) < NR_SUBDEVS) {/*d[0-7]p[0-3]s[0-3]*/ + w_drive = device / SUB_PER_DRIVE; + w_wn = &wini[w_drive]; + w_dv = &w_wn->subpart[device % SUB_PER_DRIVE]; + } else { + return(NIL_DEV); + } + return(w_dv); +} + + +/*===========================================================================* + * w_identify * + *===========================================================================*/ +PRIVATE int w_identify() +{ +/* Find out if a device exists, if it is an old AT disk, or a newer ATA + * drive, a removable media device, etc. + */ + + struct wini *wn = w_wn; + struct command cmd; + char id_string[40]; + int i, r, s; + unsigned long size; +#define id_byte(n) (&tmp_buf[2 * (n)]) +#define id_word(n) (((u16_t) id_byte(n)[0] << 0) \ + |((u16_t) id_byte(n)[1] << 8)) +#define id_longword(n) (((u32_t) id_byte(n)[0] << 0) \ + |((u32_t) id_byte(n)[1] << 8) \ + |((u32_t) id_byte(n)[2] << 16) \ + |((u32_t) id_byte(n)[3] << 24)) + + /* Try to identify the device. */ + cmd.ldh = wn->ldhpref; + cmd.command = ATA_IDENTIFY; + if (com_simple(&cmd) == OK) { + /* This is an ATA device. */ + wn->state |= SMART; + + /* Device information. */ + if ((s=sys_insw(wn->base + REG_DATA, SELF, tmp_buf, SECTOR_SIZE)) != OK) + server_panic(w_name(),"Call to sys_insw() failed", s); + + /* Why are the strings byte swapped??? */ + for (i = 0; i < 40; i++) id_string[i] = id_byte(27)[i^1]; + + /* Preferred CHS translation mode. */ + wn->pcylinders = id_word(1); + wn->pheads = id_word(3); + wn->psectors = id_word(6); + size = (u32_t) wn->pcylinders * wn->pheads * wn->psectors; + + if ((id_byte(49)[1] & 0x02) && size > 512L*1024*2) { + /* Drive is LBA capable and is big enough to trust it to + * not make a mess of it. + */ + wn->ldhpref |= LDH_LBA; + size = id_longword(60); + } + + if (wn->lcylinders == 0) { + /* No BIOS parameters? Then make some up. */ + wn->lcylinders = wn->pcylinders; + wn->lheads = wn->pheads; + wn->lsectors = wn->psectors; + while (wn->lcylinders > 1024) { + wn->lheads *= 2; + wn->lcylinders /= 2; + } + } +#if ENABLE_ATAPI + } else + if (cmd.command = ATAPI_IDENTIFY, com_simple(&cmd) == OK) { + /* An ATAPI device. */ + wn->state |= ATAPI; + + /* Device information. */ + if ((s=sys_insw(wn->base + REG_DATA, SELF, tmp_buf, 512)) != OK) + server_panic(w_name(),"Call to sys_insw() failed", s); + + /* Why are the strings byte swapped??? */ + for (i = 0; i < 40; i++) id_string[i] = id_byte(27)[i^1]; + + size = 0; /* Size set later. */ +#endif + } else { + /* Not an ATA device; no translations, no special features. Don't + * touch it unless the BIOS knows about it. + */ + if (wn->lcylinders == 0) return(ERR); /* no BIOS parameters */ + wn->pcylinders = wn->lcylinders; + wn->pheads = wn->lheads; + wn->psectors = wn->lsectors; + size = (u32_t) wn->pcylinders * wn->pheads * wn->psectors; + } + + /* Size of the whole drive */ + wn->part[0].dv_size = mul64u(size, SECTOR_SIZE); + + if (w_specify() != OK && w_specify() != OK) return(ERR); + + printf("%s: user-level AT Winchester driver detected ", w_name()); + if (wn->state & (SMART|ATAPI)) { + printf("%.40s\n", id_string); + } else { + printf("%ux%ux%u\n", wn->pcylinders, wn->pheads, wn->psectors); + } + + /* Everything looks OK; register IRQ so we can stop polling. */ + wn->irq = w_drive < 2 ? AT_WINI_0_IRQ : AT_WINI_1_IRQ; + sys_irqsetpolicy(w_wn->irq, (IRQ_READ_PORT | IRQ_BYTE | IRQ_REENABLE), SELF, + (w_wn->base + REG_STATUS), &w_byteval, 0); + sys_irqenable(wn->irq); + return(OK); +} + + +/*===========================================================================* + * w_name * + *===========================================================================*/ +PRIVATE char *w_name() +{ +/* Return a name for the current device. */ + static char name[] = "AT-D0"; + + name[4] = '0' + w_drive; + return name; +} + + +/*===========================================================================* + * w_specify * + *===========================================================================*/ +PRIVATE int w_specify() +{ +/* Routine to initialize the drive after boot or when a reset is needed. */ + + struct wini *wn = w_wn; + struct command cmd; + + if ((wn->state & DEAF) && w_reset() != OK) return(ERR); + + if (!(wn->state & ATAPI)) { + /* Specify parameters: precompensation, number of heads and sectors. */ + cmd.precomp = wn->precomp; + cmd.count = wn->psectors; + cmd.ldh = w_wn->ldhpref | (wn->pheads - 1); + cmd.command = CMD_SPECIFY; /* Specify some parameters */ + + /* Output command block and see if controller accepts the parameters. */ + if (com_simple(&cmd) != OK) return(ERR); + + if (!(wn->state & SMART)) { + /* Calibrate an old disk. */ + cmd.sector = 0; + cmd.cyl_lo = 0; + cmd.cyl_hi = 0; + cmd.ldh = w_wn->ldhpref; + cmd.command = CMD_RECALIBRATE; + + if (com_simple(&cmd) != OK) return(ERR); + } + } + wn->state |= INITIALIZED; + return(OK); +} + + +/*===========================================================================* + * w_transfer * + *===========================================================================*/ +PRIVATE int w_transfer(proc_nr, opcode, position, iov, nr_req) +int proc_nr; /* process doing the request */ +int opcode; /* DEV_GATHER or DEV_SCATTER */ +off_t position; /* offset on device to read or write */ +iovec_t *iov; /* pointer to read or write request vector */ +unsigned nr_req; /* length of request vector */ +{ + struct wini *wn = w_wn; + iovec_t *iop, *iov_end = iov + nr_req; + int r, s, errors; + unsigned long block; + unsigned long dv_size = cv64ul(w_dv->dv_size); + struct command cmd; + unsigned cylinder, head, sector, nbytes, count, chunk; + unsigned secspcyl = wn->pheads * wn->psectors; + +#if ENABLE_ATAPI + if (w_wn->state & ATAPI) { + return atapi_transfer(proc_nr, opcode, position, iov, nr_req); + } +#endif + + /* Check disk address. */ + if ((position & SECTOR_MASK) != 0) return(EINVAL); + + errors = 0; + + while (nr_req > 0) { + /* How many bytes to transfer? */ + nbytes = 0; + for (iop = iov; iop < iov_end; iop++) nbytes += iop->iov_size; + if ((nbytes & SECTOR_MASK) != 0) return(EINVAL); + + /* Which block on disk and how close to EOF? */ + if (position >= dv_size) return(OK); /* At EOF */ + if (position + nbytes > dv_size) nbytes = dv_size - position; + block = div64u(add64ul(w_dv->dv_base, position), SECTOR_SIZE); + + if (nbytes >= wn->max_count) { + /* The drive can't do more then max_count at once. */ + nbytes = wn->max_count; + } + + /* First check to see if a reinitialization is needed. */ + if (!(wn->state & INITIALIZED) && w_specify() != OK) return(EIO); + + /* Tell the controller to transfer nbytes bytes. */ + cmd.precomp = wn->precomp; + cmd.count = (nbytes >> SECTOR_SHIFT) & BYTE; + if (wn->ldhpref & LDH_LBA) { + cmd.sector = (block >> 0) & 0xFF; + cmd.cyl_lo = (block >> 8) & 0xFF; + cmd.cyl_hi = (block >> 16) & 0xFF; + cmd.ldh = wn->ldhpref | ((block >> 24) & 0xF); + } else { + cylinder = block / secspcyl; + head = (block % secspcyl) / wn->psectors; + sector = block % wn->psectors; + cmd.sector = sector + 1; + cmd.cyl_lo = cylinder & BYTE; + cmd.cyl_hi = (cylinder >> 8) & BYTE; + cmd.ldh = wn->ldhpref | head; + } + cmd.command = opcode == DEV_SCATTER ? CMD_WRITE : CMD_READ; + + r = com_out(&cmd); + + while (r == OK && nbytes > 0) { + /* For each sector, wait for an interrupt and fetch the data + * (read), or supply data to the controller and wait for an + * interrupt (write). + */ + + if (opcode == DEV_GATHER) { + /* First an interrupt, then data. */ + if ((r = at_intr_wait()) != OK) { + /* An error, send data to the bit bucket. */ + if (w_status & STATUS_DRQ) { + if ((s=sys_insw(wn->base + REG_DATA, SELF, tmp_buf, SECTOR_SIZE)) != OK) + server_panic(w_name(),"Call to sys_insw() failed", s); + } + break; + } + } + + /* Wait for data transfer requested. */ + if (!w_waitfor(STATUS_DRQ, STATUS_DRQ)) { r = ERR; break; } + + /* Copy bytes to or from the device's buffer. */ + if (opcode == DEV_GATHER) { + if ((s=sys_insw(wn->base + REG_DATA, proc_nr, (void *) iov->iov_addr, SECTOR_SIZE)) != OK) + server_panic(w_name(),"Call to sys_insw() failed", s); + } else { + if ((s=sys_outsw(wn->base + REG_DATA, proc_nr, (void *) iov->iov_addr, SECTOR_SIZE)) != OK) + server_panic(w_name(),"Call to sys_insw() failed", s); + + /* Data sent, wait for an interrupt. */ + if ((r = at_intr_wait()) != OK) break; + } + + /* Book the bytes successfully transferred. */ + nbytes -= SECTOR_SIZE; + position += SECTOR_SIZE; + iov->iov_addr += SECTOR_SIZE; + if ((iov->iov_size -= SECTOR_SIZE) == 0) { iov++; nr_req--; } + } + + /* Any errors? */ + if (r != OK) { + /* Don't retry if sector marked bad or too many errors. */ + if (r == ERR_BAD_SECTOR || ++errors == MAX_ERRORS) { + w_command = CMD_IDLE; + return(EIO); + } + } + } + + w_command = CMD_IDLE; + return(OK); +} + + +/*============================================================================* + * com_out * + *============================================================================*/ +PRIVATE int com_out(cmd) +struct command *cmd; /* Command block */ +{ +/* Output the command block to the winchester controller and return status */ + + struct wini *wn = w_wn; + unsigned base = wn->base; + pvb_pair_t outbyte[7]; /* vector for sys_voutb() */ + int s; /* status for sys_(v)outb() */ + + if (!w_waitfor(STATUS_BSY, 0)) { + printf("%s: controller not ready\n", w_name()); + return(ERR); + } + + /* Select drive. */ + if ((s=sys_outb(base + REG_LDH, cmd->ldh)) != OK) + server_panic(w_name(),"Couldn't write register to select drive",s); + + if (!w_waitfor(STATUS_BSY, 0)) { + printf("%s: drive not ready\n", w_name()); + return(ERR); + } + + /* Schedule a wakeup call, some controllers are flaky. This is done with + * a synchronous alarm. If a timeout occurs a SYN_ALARM message is sent + * from HARDWARE, so that w_intr_wait() can call w_timeout() in case the + * controller was not able to execute the command. Leftover timeouts are + * simply ignored by the main loop. + */ + sys_syncalrm(SELF, WAKEUP, 0); + + w_status = STATUS_ADMBSY; + w_command = cmd->command; + pv_set(outbyte[0], base + REG_CTL, wn->pheads >= 8 ? CTL_EIGHTHEADS : 0); + pv_set(outbyte[1], base + REG_PRECOMP, cmd->precomp); + pv_set(outbyte[2], base + REG_COUNT, cmd->count); + pv_set(outbyte[3], base + REG_SECTOR, cmd->sector); + pv_set(outbyte[4], base + REG_CYL_LO, cmd->cyl_lo); + pv_set(outbyte[5], base + REG_CYL_HI, cmd->cyl_hi); + pv_set(outbyte[6], base + REG_COMMAND, cmd->command); + if ((s=sys_voutb(outbyte,7)) != OK) + server_panic("AT_WINI","Couldn't write registers with sys_voutb()",s); + return(OK); +} + + +/*===========================================================================* + * w_need_reset * + *===========================================================================*/ +PRIVATE void w_need_reset() +{ +/* The controller needs to be reset. */ + struct wini *wn; + + for (wn = wini; wn < &wini[MAX_DRIVES]; wn++) { + wn->state |= DEAF; + wn->state &= ~INITIALIZED; + } +} + + +/*============================================================================* + * w_do_close * + *============================================================================*/ +PRIVATE int w_do_close(dp, m_ptr) +struct driver *dp; +message *m_ptr; +{ +/* Device close: Release a device. */ + + if (w_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO); + w_wn->open_ct--; +#if ENABLE_ATAPI + if (w_wn->open_ct == 0 && (w_wn->state & ATAPI)) atapi_close(); +#endif + return(OK); +} + + +/*============================================================================* + * com_simple * + *============================================================================*/ +PRIVATE int com_simple(cmd) +struct command *cmd; /* Command block */ +{ +/* A simple controller command, only one interrupt and no data-out phase. */ + int r; + + if ((r = com_out(cmd)) == OK) r = at_intr_wait(); + w_command = CMD_IDLE; + return(r); +} + + +/*===========================================================================* + * w_timeout * + *===========================================================================*/ +PRIVATE void w_timeout(void) +{ + struct wini *wn = w_wn; + + switch (w_command) { + case CMD_IDLE: + break; /* fine */ + case CMD_READ: + case CMD_WRITE: + /* Impossible, but not on PC's: The controller does not respond. */ + + /* Limiting multisector I/O seems to help. */ + if (wn->max_count > 8 * SECTOR_SIZE) { + wn->max_count = 8 * SECTOR_SIZE; + } else { + wn->max_count = SECTOR_SIZE; + } + /*FALL THROUGH*/ + default: + /* Some other command. */ + printf("%s: timeout on command %02x\n", w_name(), w_command); + w_need_reset(); + w_status = 0; + } +} + + +/*===========================================================================* + * w_reset * + *===========================================================================*/ +PRIVATE int w_reset() +{ +/* Issue a reset to the controller. This is done after any catastrophe, + * like the controller refusing to respond. + */ + int s; + struct wini *wn; + + /* Wait for any internal drive recovery. */ + tick_delay(RECOVERY_TICKS); + + + /* Strobe reset bit */ + if ((s=sys_outb(wn->base + REG_CTL, CTL_RESET)) != OK) + server_panic("AT_WINI","Couldn't strobe reset bit",s); + tick_delay(DELAY_TICKS); + if ((s=sys_outb(wn->base + REG_CTL, 0)) != OK) + server_panic("AT_WINI","Couldn't strobe reset bit",s); + tick_delay(DELAY_TICKS); + + /* Wait for controller ready */ + if (!w_waitfor(STATUS_BSY, 0)) { + printf("%s: reset failed, drive busy\n", w_name()); + return(ERR); + } + + /* The error register should be checked now, but some drives mess it up. */ + + for (wn = wini; wn < &wini[MAX_DRIVES]; wn++) { + if (wn->base == w_wn->base) wn->state &= ~DEAF; + } + return(OK); +} + + +/*============================================================================* + * w_intr_wait * + *============================================================================*/ +PRIVATE void w_intr_wait() +{ +/* Wait for a task completion interrupt. */ + + message m; + + if (w_wn->irq != NO_IRQ) { + /* Wait for an interrupt that sets w_status to "not busy". */ + while (w_status & (STATUS_ADMBSY|STATUS_BSY)) { + receive(HARDWARE, &m); /* expect HARD_INT message */ + if (m.m_type == SYN_ALARM) { /* but check for timeout */ + w_timeout(); /* a.o. set w_status */ + } else if (m.m_type == HARD_INT) { + w_status = w_byteval; /* read by generic handler */ + } + } + } else { + /* Interrupt not yet allocated; use polling. */ + (void) w_waitfor(STATUS_BSY, 0); + } +} + + +/*============================================================================* + * at_intr_wait * + *============================================================================*/ +PRIVATE int at_intr_wait() +{ +/* Wait for an interrupt, study the status bits and return error/success. */ + int r; + int s,inbval; /* read value with sys_inb */ + + w_intr_wait(); + if ((w_status & (STATUS_BSY | STATUS_WF | STATUS_ERR)) == 0) { + r = OK; + } else { + if ((s=sys_inb(w_wn->base + REG_ERROR, &inbval)) != OK) + server_panic(w_name(),"Couldn't read register",s); + if ((w_status & STATUS_ERR) && (inbval & ERROR_BB)) { + r = ERR_BAD_SECTOR; /* sector marked bad, retries won't help */ + } else { + r = ERR; /* any other error */ + } + } + w_status |= STATUS_ADMBSY; /* assume still busy with I/O */ + return(r); +} + + +/*==========================================================================* + * w_waitfor * + *==========================================================================*/ +PRIVATE int w_waitfor(mask, value) +int mask; /* status mask */ +int value; /* required status */ +{ +/* Wait until controller is in the required state. Return zero on timeout. + * An alarm that set a timeout flag is used. TIMEOUT is in micros, we need + * ticks. Disabling the alarm is not needed, because a static flag is used + * and a leftover timeout cannot do any harm. + */ + static int timeout_flag = 0; /* must be static, not cancelled */ + int s; + sys_flagalrm(TIMEOUT_TICKS, &timeout_flag); + do { + if ((s=sys_inb(w_wn->base + REG_STATUS, &w_status)) != OK) + server_panic(w_name(),"Couldn't read register",s); + if ((w_status & mask) == value) { + return 1; + } + } while (! timeout_flag); + w_need_reset(); /* controller gone deaf */ + return(0); +} + + +/*============================================================================* + * w_geometry * + *============================================================================*/ +PRIVATE void w_geometry(entry) +struct partition *entry; +{ + struct wini *wn = w_wn; + + if (wn->state & ATAPI) { /* Make up some numbers. */ + entry->cylinders = div64u(wn->part[0].dv_size, SECTOR_SIZE) / (64*32); + entry->heads = 64; + entry->sectors = 32; + } else { /* Return logical geometry. */ + entry->cylinders = wn->lcylinders; + entry->heads = wn->lheads; + entry->sectors = wn->lsectors; + } +} + + +#if ENABLE_ATAPI +/*===========================================================================* + * atapi_open * + *===========================================================================*/ +PRIVATE int atapi_open() +{ +/* Should load and lock the device and obtain its size. For now just set the + * size of the device to something big. What is really needed is a generic + * SCSI layer that does all this stuff for ATAPI and SCSI devices (kjb). (XXX) + */ + w_wn->part[0].dv_size = mul64u(800L*1024, 1024); + return(OK); +} + +/*===========================================================================* + * atapi_close * + *===========================================================================*/ +PRIVATE void atapi_close() +{ +/* Should unlock the device. For now do nothing. (XXX) */ +} + +/*===========================================================================* + * atapi_transfer * + *===========================================================================*/ +PRIVATE int atapi_transfer(proc_nr, opcode, position, iov, nr_req) +int proc_nr; /* process doing the request */ +int opcode; /* DEV_GATHER or DEV_SCATTER */ +off_t position; /* offset on device to read or write */ +iovec_t *iov; /* pointer to read or write request vector */ +unsigned nr_req; /* length of request vector */ +{ + struct wini *wn = w_wn; + iovec_t *iop, *iov_end = iov + nr_req; + int r, s, errors, fresh; + u64_t pos; + unsigned long block; + unsigned long dv_size = cv64ul(w_dv->dv_size); + unsigned nbytes, nblocks, count, before, chunk; + u8_t packet[12]; + + errors = fresh = 0; + + while (nr_req > 0 && !fresh) { + /* The Minix block size is smaller than the CD block size, so we + * may have to read extra before or after the good data. + */ + pos = add64ul(w_dv->dv_base, position); + block = div64u(pos, CD_SECTOR_SIZE); + before = rem64u(pos, CD_SECTOR_SIZE); + + /* How many bytes to transfer? */ + nbytes = count = 0; + for (iop = iov; iop < iov_end; iop++) { + nbytes += iop->iov_size; + if ((before + nbytes) % CD_SECTOR_SIZE == 0) count = nbytes; + } + + /* Does one of the memory chunks end nicely on a CD sector multiple? */ + if (count != 0) nbytes = count; + + /* Data comes in as words, so we have to enforce even byte counts. */ + if ((before | nbytes) & 1) return(EINVAL); + + /* Which block on disk and how close to EOF? */ + if (position >= dv_size) return(OK); /* At EOF */ + if (position + nbytes > dv_size) nbytes = dv_size - position; + + nblocks = (before + nbytes + CD_SECTOR_SIZE - 1) / CD_SECTOR_SIZE; + if (ATAPI_DEBUG) { + printf("block=%lu, before=%u, nbytes=%u, nblocks=%u\n", + block, before, nbytes, nblocks); + } + + /* First check to see if a reinitialization is needed. */ + if (!(wn->state & INITIALIZED) && w_specify() != OK) return(EIO); + + /* Build an ATAPI command packet. */ + packet[0] = SCSI_READ10; + packet[1] = 0; + packet[2] = (block >> 24) & 0xFF; + packet[3] = (block >> 16) & 0xFF; + packet[4] = (block >> 8) & 0xFF; + packet[5] = (block >> 0) & 0xFF; + packet[7] = (nblocks >> 8) & 0xFF; + packet[8] = (nblocks >> 0) & 0xFF; + packet[9] = 0; + packet[10] = 0; + packet[11] = 0; + + /* Tell the controller to execute the packet command. */ + r = atapi_sendpacket(packet, nblocks * CD_SECTOR_SIZE); + if (r != OK) goto err; + + /* Read chunks of data. */ + while ((r = atapi_intr_wait()) > 0) { + count = r; + + if (ATAPI_DEBUG) { + printf("before=%u, nbytes=%u, count=%u\n", + before, nbytes, count); + } + + while (before > 0 && count > 0) { /* Discard before. */ + chunk = before; + if (chunk > count) chunk = count; + if (chunk > DMA_BUF_SIZE) chunk = DMA_BUF_SIZE; + if ((s=sys_insw(wn->base + REG_DATA, SELF, tmp_buf, chunk)) != OK) + server_panic(w_name(),"Call to sys_insw() failed", s); + before -= chunk; + count -= chunk; + } + + while (nbytes > 0 && count > 0) { /* Requested data. */ + chunk = nbytes; + if (chunk > count) chunk = count; + if (chunk > iov->iov_size) chunk = iov->iov_size; + if ((s=sys_insw(wn->base + REG_DATA, proc_nr, (void *) iov->iov_addr, chunk)) != OK) + server_panic(w_name(),"Call to sys_insw() failed", s); + position += chunk; + nbytes -= chunk; + count -= chunk; + iov->iov_addr += chunk; + fresh = 0; + if ((iov->iov_size -= chunk) == 0) { + iov++; + nr_req--; + fresh = 1; /* new element is optional */ + } + } + + while (count > 0) { /* Excess data. */ + chunk = count; + if (chunk > DMA_BUF_SIZE) chunk = DMA_BUF_SIZE; + if ((s=sys_insw(wn->base + REG_DATA, SELF, tmp_buf, chunk)) != OK) + server_panic(w_name(),"Call to sys_insw() failed", s); + count -= chunk; + } + } + + if (r < 0) { + err: /* Don't retry if too many errors. */ + if (++errors == MAX_ERRORS) { + w_command = CMD_IDLE; + return(EIO); + } + } + } + + w_command = CMD_IDLE; + return(OK); +} + +/*===========================================================================* + * atapi_sendpacket * + *===========================================================================*/ +PRIVATE int atapi_sendpacket(packet, cnt) +u8_t *packet; +unsigned cnt; +{ +/* Send an Atapi Packet Command */ + struct wini *wn = w_wn; + message mess; + pvb_pair_t outbyte[6]; /* vector for sys_voutb() */ + int s; + + /* Select Master/Slave drive */ + if ((s=sys_outb(wn->base + REG_DRIVE, wn->ldhpref)) != OK) + server_panic("AT_WINI","Couldn't select master/ slave drive",s); + + if (!w_waitfor(STATUS_BSY | STATUS_DRQ, 0)) { + printf("%s: drive not ready\n", w_name()); + return(ERR); + } + + /* Schedule a wakeup call, some controllers are flaky. This is done with + * a synchronous alarm. If a timeout occurs a SYN_ALARM message is sent + * from HARDWARE, so that w_intr_wait() can call w_timeout() in case the + * controller was not able to execute the command. Leftover timeouts are + * simply ignored by the main loop. + */ + sys_syncalrm(SELF, WAKEUP, 0); + +#if _WORD_SIZE > 2 + if (cnt > 0xFFFE) cnt = 0xFFFE; /* Max data per interrupt. */ +#endif + + w_command = ATAPI_PACKETCMD; + pv_set(outbyte[0], wn->base + REG_FEAT, 0); + pv_set(outbyte[1], wn->base + REG_IRR, 0); + pv_set(outbyte[2], wn->base + REG_SAMTAG, 0); + pv_set(outbyte[3], wn->base + REG_CNT_LO, (cnt >> 0) & 0xFF); + pv_set(outbyte[4], wn->base + REG_CNT_HI, (cnt >> 8) & 0xFF); + pv_set(outbyte[5], wn->base + REG_COMMAND, w_command); + if ((s=sys_voutb(outbyte,6)) != OK) + server_panic("AT_WINI","Couldn't write registers with sys_voutb()",s); + + if (!w_waitfor(STATUS_BSY | STATUS_DRQ, STATUS_DRQ)) { + printf("%s: timeout (BSY|DRQ -> DRQ)\n"); + return(ERR); + } + w_status |= STATUS_ADMBSY; /* Command not at all done yet. */ + + /* Send the command packet to the device. */ + if ((s=sys_outsw(wn->base + REG_DATA, SELF, packet, 12)) != OK) + server_panic(w_name(),"sys_outsw() failed", s); + return(OK); +} + +/*============================================================================* + * atapi_intr_wait * + *============================================================================*/ +PRIVATE int atapi_intr_wait() +{ +/* Wait for an interrupt and study the results. Returns a number of bytes + * that need to be transferred, or an error code. + */ + struct wini *wn = w_wn; + pvb_pair_t inbyte[4]; /* vector for sys_vinb() */ + int s; /* status for sys_vinb() */ + int e; + int len; + int irr; + int r; + int phase; + + w_intr_wait(); + + /* Request series of device I/O. */ + inbyte[0].port = wn->base + REG_ERROR; + inbyte[1].port = wn->base + REG_CNT_LO; + inbyte[2].port = wn->base + REG_CNT_HI; + inbyte[3].port = wn->base + REG_IRR; + if ((s=sys_vinb(inbyte, 4)) != OK) + server_panic(w_name(),"ATAPI failed sys_vinb()", s); + e = inbyte[0].value; + len = inbyte[1].value; + len |= inbyte[2].value << 8; + irr = inbyte[3].value; + + if (ATAPI_DEBUG) { + printf("S=%02x E=%02x L=%04x I=%02x\n", w_status, e, len, irr); + } + if (w_status & (STATUS_BSY | STATUS_CHECK)) return ERR; + + phase = (w_status & STATUS_DRQ) | (irr & (IRR_COD | IRR_IO)); + + switch (phase) { + case IRR_COD | IRR_IO: + if (ATAPI_DEBUG) printf("ACD: Phase Command Complete\n"); + r = OK; + break; + case 0: + if (ATAPI_DEBUG) printf("ACD: Phase Command Aborted\n"); + r = ERR; + break; + case STATUS_DRQ | IRR_COD: + if (ATAPI_DEBUG) printf("ACD: Phase Command Out\n"); + r = ERR; + break; + case STATUS_DRQ: + if (ATAPI_DEBUG) printf("ACD: Phase Data Out %d\n", len); + r = len; + break; + case STATUS_DRQ | IRR_IO: + if (ATAPI_DEBUG) printf("ACD: Phase Data In %d\n", len); + r = len; + break; + default: + if (ATAPI_DEBUG) printf("ACD: Phase Unknown\n"); + r = ERR; + break; + } + +#if 0 + /* retry if the media changed */ + XXX while (phase == (IRR_IO | IRR_COD) && (w_status & STATUS_CHECK) + && (e & ERROR_SENSE) == SENSE_UATTN && --try > 0); +#endif + + w_status |= STATUS_ADMBSY; /* Assume not done yet. */ + return(r); +} +#endif /* ENABLE_ATAPI */ +#endif /* ENABLE_AT_WINI */ diff --git a/drivers/at_wini/at_wini.h b/drivers/at_wini/at_wini.h new file mode 100644 index 000000000..b1c64ccf1 --- /dev/null +++ b/drivers/at_wini/at_wini.h @@ -0,0 +1,6 @@ +#include "../drivers.h" +#include "../libdriver/driver.h" +#include "../libdriver/drvlib.h" + +_PROTOTYPE(void main, (void)); + diff --git a/drivers/drivers.h b/drivers/drivers.h new file mode 100644 index 000000000..2155841c7 --- /dev/null +++ b/drivers/drivers.h @@ -0,0 +1,28 @@ +/* This is the master header for all device drivers. It includes some other + * files and defines the principal constants. + */ +#define _POSIX_SOURCE 1 /* tell headers to include POSIX stuff */ +#define _MINIX 1 /* tell headers to include MINIX stuff */ +#define _SYSTEM 1 /* get negative error number in <errno.h> */ + +/* The following are so basic, all the *.c files get them automatically. */ +#include <minix/config.h> /* MUST be first */ +#include <ansi.h> /* MUST be second */ +#include <minix/type.h> +#include <minix/com.h> +#include <minix/callnr.h> +#include <sys/types.h> +#include <minix/const.h> +#include <minix/devio.h> +#include <minix/syslib.h> +#include <minix/utils.h> + +#include <ibm/interrupt.h> /* IRQ vectors and miscellaneous ports */ +#include <ibm/bios.h> /* BIOS index numbers */ +#include <ibm/ports.h> /* Well-known ports */ + +#include <string.h> +#include <limits.h> +#include <stddef.h> +#include <errno.h> + diff --git a/drivers/floppy/Makefile b/drivers/floppy/Makefile new file mode 100644 index 000000000..930719305 --- /dev/null +++ b/drivers/floppy/Makefile @@ -0,0 +1,54 @@ +# Makefile for the floppy disk driver (FLOPPY) +DRIVER = floppy + +# directories +u = /usr +i = $u/include +s = $i/sys +m = $i/minix +b = $i/ibm +d = $u/src/drivers + +# programs, flags, etc. +MAKE = exec make +CC = exec cc +CFLAGS = -I$i +LDFLAGS = -i +LIBS = -lsys -lutils -ltimers + +OBJ = floppy.o +LIBDRIVER = $d/libdriver/driver.o $d/libdriver/drvlib.o + +# build local binary +all build: $(DRIVER) +$(DRIVER): $(OBJ) $(LIBDRIVER) + $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBDRIVER) $(LIBS) + install -S 256w $(DRIVER) + +$(LIBDRIVER): + cd $d/libdriver && $(MAKE) + +# install with other drivers +install: /usr/sbin/drivers/$(DRIVER) +/usr/sbin/drivers/$(DRIVER): $(DRIVER) + install -o root -cs $? $@ + +# clean up local files +clean: + rm -f $(DRIVER) *.o *.bak + + +# dependencies +a = $d/drivers.h $b/interrupt.h $b/bios.h \ + $i/ansi.h $i/string.h $i/limits.h $i/stddef.h $i/errno.h \ + $m/config.h $m/type.h $m/com.h $m/callnr.h $m/const.h $s/types.h \ + $m/syslib.h $s/types.h \ + $m/utils.h $m/serverassert.h $m/devio.h +l = $d/libdriver/driver.h $d/libdriver/driver.c $m/partition.h $m/u64.h + + +floppy.o: $a $l + +$(LIBDRIVER): $a $l +$(LIBDRIVER): $s/ioc_disk.h + diff --git a/drivers/floppy/floppy.c b/drivers/floppy/floppy.c new file mode 100644 index 000000000..53430714e --- /dev/null +++ b/drivers/floppy/floppy.c @@ -0,0 +1,1308 @@ +/* This file contains the device dependent part of the driver for the Floppy + * Disk Controller (FDC) using the NEC PD765 chip. + * + * The file contains two entry points: + * + * floppy_task: main entry when system is brought up + * + * Changes: + * Dec 01, 2004 floppy driver moved to user-space (Jorrit N. Herder) + * Sep 15, 2004 sync alarms/ local timer management (Jorrit N. Herder) + * May 02, 2004 sys_flagalrm() replaces micro_elapsed() (Jorrit N. Herder) + * Aug 12, 2003 null seek no interrupt fix (Mike Haertel) + * May 14, 2000 d-d/i rewrite (Kees J. Bot) + * Apr 04, 1992 device dependent/independent split (Kees J. Bot) + * Mar 27, 1992 last details on density checking (Kees J. Bot) + * Feb 14, 1992 check drive density on opens only (Andy Tanenbaum) + * 1991 len[] / motors / reset / step rate / ... (Bruce Evans) + * May 13, 1991 renovated the errors loop (Don Chapman) + * 1989 I/O vector to keep up with 1-1 interleave (Bruce Evans) + * Jan 06, 1988 allow 1.44 MB diskettes (Al Crew) + * Nov 28, 1986 better resetting for 386 (Peter Kay) + * Oct 27, 1986 fdc_results fixed for 8 MHz (Jakob Schripsema) + */ + +#include "floppy.h" +#include <timers.h> +#include <ibm/diskparm.h> +#include <minix/utils.h> +#include <minix/syslib.h> + +/* I/O Ports used by floppy disk task. */ +#define DOR 0x3F2 /* motor drive control bits */ +#define FDC_STATUS 0x3F4 /* floppy disk controller status register */ +#define FDC_DATA 0x3F5 /* floppy disk controller data register */ +#define FDC_RATE 0x3F7 /* transfer rate register */ +#define DMA_ADDR 0x004 /* port for low 16 bits of DMA address */ +#define DMA_TOP 0x081 /* port for top 4 bits of 20-bit DMA addr */ +#define DMA_COUNT 0x005 /* port for DMA count (count = bytes - 1) */ +#define DMA_FLIPFLOP 0x00C /* DMA byte pointer flip-flop */ +#define DMA_MODE 0x00B /* DMA mode port */ +#define DMA_INIT 0x00A /* DMA init port */ +#define DMA_RESET_VAL 0x06 + +/* Status registers returned as result of operation. */ +#define ST0 0x00 /* status register 0 */ +#define ST1 0x01 /* status register 1 */ +#define ST2 0x02 /* status register 2 */ +#define ST3 0x00 /* status register 3 (return by DRIVE_SENSE) */ +#define ST_CYL 0x03 /* slot where controller reports cylinder */ +#define ST_HEAD 0x04 /* slot where controller reports head */ +#define ST_SEC 0x05 /* slot where controller reports sector */ +#define ST_PCN 0x01 /* slot where controller reports present cyl */ + +/* Fields within the I/O ports. */ +/* Main status register. */ +#define CTL_BUSY 0x10 /* bit is set when read or write in progress */ +#define DIRECTION 0x40 /* bit is set when reading data reg is valid */ +#define MASTER 0x80 /* bit is set when data reg can be accessed */ + +/* Digital output port (DOR). */ +#define MOTOR_SHIFT 4 /* high 4 bits control the motors in DOR */ +#define ENABLE_INT 0x0C /* used for setting DOR port */ + +/* ST0. */ +#define ST0_BITS 0xF8 /* check top 5 bits of seek status */ +#define TRANS_ST0 0x00 /* top 5 bits of ST0 for READ/WRITE */ +#define SEEK_ST0 0x20 /* top 5 bits of ST0 for SEEK */ + +/* ST1. */ +#define BAD_SECTOR 0x05 /* if these bits are set in ST1, recalibrate */ +#define WRITE_PROTECT 0x02 /* bit is set if diskette is write protected */ + +/* ST2. */ +#define BAD_CYL 0x1F /* if any of these bits are set, recalibrate */ + +/* ST3 (not used). */ +#define ST3_FAULT 0x80 /* if this bit is set, drive is sick */ +#define ST3_WR_PROTECT 0x40 /* set when diskette is write protected */ +#define ST3_READY 0x20 /* set when drive is ready */ + +/* Floppy disk controller command bytes. */ +#define FDC_SEEK 0x0F /* command the drive to seek */ +#define FDC_READ 0xE6 /* command the drive to read */ +#define FDC_WRITE 0xC5 /* command the drive to write */ +#define FDC_SENSE 0x08 /* command the controller to tell its status */ +#define FDC_RECALIBRATE 0x07 /* command the drive to go to cyl 0 */ +#define FDC_SPECIFY 0x03 /* command the drive to accept params */ +#define FDC_READ_ID 0x4A /* command the drive to read sector identity */ +#define FDC_FORMAT 0x4D /* command the drive to format a track */ + +/* DMA channel commands. */ +#define DMA_READ 0x46 /* DMA read opcode */ +#define DMA_WRITE 0x4A /* DMA write opcode */ + +/* Parameters for the disk drive. */ +#define HC_SIZE 2880 /* # sectors on largest legal disk (1.44MB) */ +#define NR_HEADS 0x02 /* two heads (i.e., two tracks/cylinder) */ +#define MAX_SECTORS 18 /* largest # sectors per track */ +#define DTL 0xFF /* determines data length (sector size) */ +#define SPEC2 0x02 /* second parameter to SPECIFY */ +#define MOTOR_OFF (3*HZ) /* how long to wait before stopping motor */ +#define WAKEUP (2*HZ) /* timeout on I/O, FDC won't quit. */ + +/* Error codes */ +#define ERR_SEEK (-1) /* bad seek */ +#define ERR_TRANSFER (-2) /* bad transfer */ +#define ERR_STATUS (-3) /* something wrong when getting status */ +#define ERR_READ_ID (-4) /* bad read id */ +#define ERR_RECALIBRATE (-5) /* recalibrate didn't work properly */ +#define ERR_DRIVE (-6) /* something wrong with a drive */ +#define ERR_WR_PROTECT (-7) /* diskette is write protected */ +#define ERR_TIMEOUT (-8) /* interrupt timeout */ + +/* No retries on some errors. */ +#define err_no_retry(err) ((err) <= ERR_WR_PROTECT) + +/* Encoding of drive type in minor device number. */ +#define DEV_TYPE_BITS 0x7C /* drive type + 1, if nonzero */ +#define DEV_TYPE_SHIFT 2 /* right shift to normalize type bits */ +#define FORMAT_DEV_BIT 0x80 /* bit in minor to turn write into format */ + +/* Miscellaneous. */ +#define MAX_ERRORS 6 /* how often to try rd/wt before quitting */ +#define MAX_RESULTS 7 /* max number of bytes controller returns */ +#define NR_DRIVES 2 /* maximum number of drives */ +#define DIVISOR 128 /* used for sector size encoding */ +#define SECTOR_SIZE_CODE 2 /* code to say "512" to the controller */ +#define TIMEOUT_MICROS 500000L /* microseconds waiting for FDC */ +#define TIMEOUT_TICKS 30 /* ticks waiting for FDC */ +#define NT 7 /* number of diskette/drive combinations */ +#define UNCALIBRATED 0 /* drive needs to be calibrated at next use */ +#define CALIBRATED 1 /* no calibration needed */ +#define BASE_SECTOR 1 /* sectors are numbered starting at 1 */ +#define NO_SECTOR (-1) /* current sector unknown */ +#define NO_CYL (-1) /* current cylinder unknown, must seek */ +#define NO_DENS 100 /* current media unknown */ +#define BSY_IDLE 0 /* busy doing nothing */ +#define BSY_IO 1 /* busy doing I/O */ +#define BSY_WAKEN 2 /* got a wakeup call */ + +/* Seven combinations of diskette/drive are supported. + * + * # Diskette Drive Sectors Tracks Rotation Data-rate Comment + * 0 360K 360K 9 40 300 RPM 250 kbps Standard PC DSDD + * 1 1.2M 1.2M 15 80 360 RPM 500 kbps AT disk in AT drive + * 2 360K 720K 9 40 300 RPM 250 kbps Quad density PC + * 3 720K 720K 9 80 300 RPM 250 kbps Toshiba, et al. + * 4 360K 1.2M 9 40 360 RPM 300 kbps PC disk in AT drive + * 5 720K 1.2M 9 80 360 RPM 300 kbps Toshiba in AT drive + * 6 1.44M 1.44M 18 80 300 RPM 500 kbps PS/2, et al. + * + * In addition, 720K diskettes can be read in 1.44MB drives, but that does + * not need a different set of parameters. This combination uses + * + * 3 720K 1.44M 9 80 300 RPM 250 kbps PS/2, et al. + */ +PRIVATE struct density { + u8_t secpt; /* sectors per track */ + u8_t cyls; /* tracks per side */ + u8_t steps; /* steps per cylinder (2 = double step) */ + u8_t test; /* sector to try for density test */ + u8_t rate; /* data rate (2=250, 1=300, 0=500 kbps) */ + u8_t start; /* motor start (clock ticks) */ + u8_t gap; /* gap size */ + u8_t spec1; /* first specify byte (SRT/HUT) */ +} fdensity[NT] = { + { 9, 40, 1, 4*9, 2, 4*HZ/8, 0x2A, 0xDF }, /* 360K / 360K */ + { 15, 80, 1, 14, 0, 4*HZ/8, 0x1B, 0xDF }, /* 1.2M / 1.2M */ + { 9, 40, 2, 2*9, 2, 4*HZ/8, 0x2A, 0xDF }, /* 360K / 720K */ + { 9, 80, 1, 4*9, 2, 6*HZ/8, 0x2A, 0xDF }, /* 720K / 720K */ + { 9, 40, 2, 2*9, 1, 4*HZ/8, 0x23, 0xDF }, /* 360K / 1.2M */ + { 9, 80, 1, 4*9, 1, 4*HZ/8, 0x23, 0xDF }, /* 720K / 1.2M */ + { 18, 80, 1, 17, 0, 6*HZ/8, 0x1B, 0xCF }, /* 1.44M / 1.44M */ +}; + +/* The following table is used with the test_sector array to recognize a + * drive/floppy combination. The sector to test has been determined by + * looking at the differences in gap size, sectors/track, and double stepping. + * This means that types 0 and 3 can't be told apart, only the motor start + * time differs. If a read test succeeds then the drive is limited to the + * set of densities it can support to avoid unnecessary tests in the future. + */ + +#define b(d) (1 << (d)) /* bit for density d. */ + +PRIVATE struct test_order { + u8_t t_density; /* floppy/drive type */ + u8_t t_class; /* limit drive to this class of densities */ +} test_order[NT-1] = { + { 6, b(3) | b(6) }, /* 1.44M {720K, 1.44M} */ + { 1, b(1) | b(4) | b(5) }, /* 1.2M {1.2M, 360K, 720K} */ + { 3, b(2) | b(3) | b(6) }, /* 720K {360K, 720K, 1.44M} */ + { 4, b(1) | b(4) | b(5) }, /* 360K {1.2M, 360K, 720K} */ + { 5, b(1) | b(4) | b(5) }, /* 720K {1.2M, 360K, 720K} */ + { 2, b(2) | b(3) }, /* 360K {360K, 720K} */ + /* Note that type 0 is missing, type 3 can read/write it too, which is + * why the type 3 parameters have been pessimized to be like type 0. + */ +}; + +/* Variables. */ +PRIVATE struct floppy { /* main drive struct, one entry per drive */ + unsigned fl_curcyl; /* current cylinder */ + unsigned fl_hardcyl; /* hardware cylinder, as opposed to: */ + unsigned fl_cylinder; /* cylinder number addressed */ + unsigned fl_sector; /* sector addressed */ + unsigned fl_head; /* head number addressed */ + char fl_calibration; /* CALIBRATED or UNCALIBRATED */ + u8_t fl_density; /* NO_DENS = ?, 0 = 360K; 1 = 360K/1.2M; etc.*/ + u8_t fl_class; /* bitmap for possible densities */ + timer_t fl_tmr_stop; /* timer to stop motor */ + struct device fl_geom; /* Geometry of the drive */ + struct device fl_part[NR_PARTITIONS]; /* partition's base & size */ +} floppy[NR_DRIVES]; + +PRIVATE int motor_status; /* bitmap of current motor status */ +PRIVATE int need_reset; /* set to 1 when controller must be reset */ +PRIVATE unsigned f_drive; /* selected drive */ +PRIVATE unsigned f_device; /* selected minor device */ +PRIVATE struct floppy *f_fp; /* current drive */ +PRIVATE struct density *f_dp; /* current density parameters */ +PRIVATE struct density *prev_dp;/* previous density parameters */ +PRIVATE unsigned f_sectors; /* equal to f_dp->secpt (needed a lot) */ +PRIVATE u16_t f_busy; /* BSY_IDLE, BSY_IO, BSY_WAKEN */ +#if DEAD_CODE +PRIVATE irq_hook_t f_hook; /* interrupt hook */ +#endif +PRIVATE struct device *f_dv; /* device's base and size */ +PRIVATE struct disk_parameter_s fmt_param; /* parameters for format */ +PRIVATE u8_t f_results[MAX_RESULTS];/* the controller can give lots of output */ + +/* The floppy uses various timers. These are managed by the floppy driver + * itself, because only a single synchronous alarm is available per process. + * Besides the 'f_tmr_timeout' timer below, the floppy structure for each + * floppy disk drive contains a 'fl_tmr_stop' timer. + */ +PRIVATE timer_t f_tmr_timeout; /* timer for various timeouts */ +PRIVATE timer_t *f_timers; /* queue of floppy timers */ +PRIVATE clock_t f_next_timeout; /* the next timeout time */ +FORWARD _PROTOTYPE( void f_expire_tmrs, (struct driver *dp) ); +FORWARD _PROTOTYPE( void f_set_timer, (timer_t *tp, clock_t delta, + tmr_func_t watchdog) ); +FORWARD _PROTOTYPE( void stop_motor, (timer_t *tp) ); +FORWARD _PROTOTYPE( void f_timeout, (timer_t *tp) ); + +FORWARD _PROTOTYPE( struct device *f_prepare, (int device) ); +FORWARD _PROTOTYPE( char *f_name, (void) ); +FORWARD _PROTOTYPE( void f_cleanup, (void) ); +FORWARD _PROTOTYPE( int f_transfer, (int proc_nr, int opcode, off_t position, + iovec_t *iov, unsigned nr_req) ); +FORWARD _PROTOTYPE( void dma_setup, (int opcode) ); +FORWARD _PROTOTYPE( void start_motor, (void) ); +FORWARD _PROTOTYPE( int seek, (void) ); +FORWARD _PROTOTYPE( int fdc_transfer, (int opcode) ); +FORWARD _PROTOTYPE( int fdc_results, (void) ); +#if DEAD_CODE +FORWARD _PROTOTYPE( int f_handler, (irq_hook_t *hook) ); +#endif +FORWARD _PROTOTYPE( int fdc_command, (u8_t *cmd, int len) ); +FORWARD _PROTOTYPE( void fdc_out, (int val) ); +FORWARD _PROTOTYPE( int recalibrate, (void) ); +FORWARD _PROTOTYPE( void f_reset, (void) ); +FORWARD _PROTOTYPE( int f_intr_wait, (void) ); +FORWARD _PROTOTYPE( int read_id, (void) ); +FORWARD _PROTOTYPE( int f_do_open, (struct driver *dp, message *m_ptr) ); +FORWARD _PROTOTYPE( void floppy_stop, (struct driver *dp) ); +FORWARD _PROTOTYPE( int test_read, (int density) ); +FORWARD _PROTOTYPE( void f_geometry, (struct partition *entry)); + + +/* Entry points to this driver. */ +PRIVATE struct driver f_dtab = { + f_name, /* current device's name */ + f_do_open, /* open or mount request, sense type of diskette */ + do_nop, /* nothing on a close */ + do_diocntl, /* get or set a partitions geometry */ + f_prepare, /* prepare for I/O on a given minor device */ + f_transfer, /* do the I/O */ + f_cleanup, /* cleanup before sending reply to user process */ + f_geometry, /* tell the geometry of the diskette */ + floppy_stop, /* floppy cleanup on shutdown */ + f_expire_tmrs,/* expire all alarm timers */ +}; + + +/*===========================================================================* + * floppy_task * + *===========================================================================*/ +PUBLIC void main() +{ +/* Initialize the floppy structure and the timers. */ + + struct floppy *fp; + int irqs; + + f_next_timeout = TMR_NEVER; + tmr_inittimer(&f_tmr_timeout); + + for (fp = &floppy[0]; fp < &floppy[NR_DRIVES]; fp++) { + fp->fl_curcyl = NO_CYL; + fp->fl_density = NO_DENS; + fp->fl_class = ~0; + tmr_inittimer(&fp->fl_tmr_stop); + } + + /* Set IRQ policy, only request notifications. */ + if ((irqs=sys_irqsetpolicy(FLOPPY_IRQ, 0, SELF, 0, 0, 0 )) != OK) + server_panic("FLOPPY", "Couldn't set IRQ policy", irqs); + if ((irqs=sys_irqenable(FLOPPY_IRQ)) != OK) + server_panic("FLOPPY", "Couldn't enable IRQs", irqs); + + printf("FLOPPY: user-level floppy disk driver initialized\n"); + driver_task(&f_dtab); +} + + +/*===========================================================================* + * f_expire_tmrs * + *===========================================================================*/ +PRIVATE void f_expire_tmrs(struct driver *dp) +{ +/* A synchronous alarm message was received. Check if there are any expired + * timers. Possibly reschedule the next alarm. + */ + clock_t now; /* current time */ + timer_t *tp; + int s; + + /* Get the current time to compare the timers against. */ + if ((s=sys_getuptime(&now)) != OK) + server_panic("FLOPPY","Couldn't get uptime from clock.", s); + + /* Scan the timers queue for expired timers. Dispatch the watchdog function + * for each expired timers. FLOPPY watchdog functions are f_tmr_timeout() + * and stop_motor(). Possibly a new alarm call must be scheduled. + */ + tmrs_exptimers(&f_timers, now); + if (f_timers == NULL) { + f_next_timeout = TMR_NEVER; + } else { /* set new sync alarm */ + f_next_timeout = f_timers->tmr_exp_time; + if ((s=sys_syncalrm(SELF, f_next_timeout, 1)) != OK) + server_panic("FLOPPY","Couldn't set synchronous alarm.", s); + } +} + +/*===========================================================================* + * f_set_timer * + *===========================================================================*/ +PRIVATE void f_set_timer(tp, delta, watchdog) +timer_t *tp; /* timer to be set */ +clock_t delta; /* in how many ticks */ +tmr_func_t watchdog; /* watchdog function to be called */ +{ + clock_t now; /* current time */ + int s; + + /* Get the current time. */ + if ((s=sys_getuptime(&now)) != OK) + server_panic("FLOPPY","Couldn't get uptime from clock.", s); + + /* Add the timer to the local timer queue. */ + tmrs_settimer(&f_timers, tp, now + delta, watchdog); + + /* Possibly reschedule an alarm call. This happens when the front of the + * timers queue was reinserted at another position, i.e., when a timer was + * reset, or when a new timer was added in front. + */ + if (f_timers->tmr_exp_time != f_next_timeout) { + f_next_timeout = f_timers->tmr_exp_time; + if ((s=sys_syncalrm(SELF, f_next_timeout, 1)) != OK) + server_panic("FLOPPY","Couldn't set synchronous alarm.", s); + } +} + +/*===========================================================================* + * f_prepare * + *===========================================================================*/ +PRIVATE struct device *f_prepare(device) +int device; +{ +/* Prepare for I/O on a device. */ + + f_device = device; + f_drive = device & ~(DEV_TYPE_BITS | FORMAT_DEV_BIT); + if (f_drive < 0 || f_drive >= NR_DRIVES) return(NIL_DEV); + + f_fp = &floppy[f_drive]; + f_dv = &f_fp->fl_geom; + if (f_fp->fl_density < NT) { + f_dp = &fdensity[f_fp->fl_density]; + f_sectors = f_dp->secpt; + f_fp->fl_geom.dv_size = mul64u((long) (NR_HEADS * f_sectors + * f_dp->cyls), SECTOR_SIZE); + } + + /* A partition? */ + if ((device &= DEV_TYPE_BITS) >= MINOR_fd0p0) + f_dv = &f_fp->fl_part[(device - MINOR_fd0p0) >> DEV_TYPE_SHIFT]; + + return f_dv; +} + + +/*===========================================================================* + * f_name * + *===========================================================================*/ +PRIVATE char *f_name() +{ +/* Return a name for the current device. */ + static char name[] = "fd0"; + + name[2] = '0' + f_drive; + return name; +} + + +/*===========================================================================* + * f_cleanup * + *===========================================================================*/ +PRIVATE void f_cleanup() +{ + /* Start a timer to turn the motor off in a few seconds. */ + tmr_arg(&f_fp->fl_tmr_stop)->ta_int = f_drive; + f_set_timer(&f_fp->fl_tmr_stop, MOTOR_OFF, stop_motor); + + /* Exiting the floppy driver, so forget where we are. */ + f_fp->fl_sector = NO_SECTOR; +} + + +/*===========================================================================* + * f_transfer * + *===========================================================================*/ +PRIVATE int f_transfer(proc_nr, opcode, position, iov, nr_req) +int proc_nr; /* process doing the request */ +int opcode; /* DEV_GATHER or DEV_SCATTER */ +off_t position; /* offset on device to read or write */ +iovec_t *iov; /* pointer to read or write request vector */ +unsigned nr_req; /* length of request vector */ +{ + struct floppy *fp = f_fp; + iovec_t *iop, *iov_end = iov + nr_req; + int s, r, errors; + unsigned block; /* Seen any 32M floppies lately? */ + unsigned nbytes, count, chunk, sector; + unsigned long dv_size = cv64ul(f_dv->dv_size); + vir_bytes user_addr; + vir_bytes uaddrs[MAX_SECTORS], *up; + u8_t cmd[3]; + + /* Check disk address. */ + if ((position & SECTOR_MASK) != 0) return(EINVAL); + + errors = 0; + while (nr_req > 0) { + /* How many bytes to transfer? */ + nbytes = 0; + for (iop = iov; iop < iov_end; iop++) nbytes += iop->iov_size; + + /* Which block on disk and how close to EOF? */ + if (position >= dv_size) return(OK); /* At EOF */ + if (position + nbytes > dv_size) nbytes = dv_size - position; + block = div64u(add64ul(f_dv->dv_base, position), SECTOR_SIZE); + + if ((nbytes & SECTOR_MASK) != 0) return(EINVAL); + + /* Using a formatting device? */ + if (f_device & FORMAT_DEV_BIT) { + if (opcode != DEV_SCATTER) return(EIO); + if (iov->iov_size < SECTOR_SIZE + sizeof(fmt_param)) + return(EINVAL); + + if ((s=sys_datacopy(proc_nr, iov->iov_addr + SECTOR_SIZE, + SELF, (vir_bytes) &fmt_param, + (phys_bytes) sizeof(fmt_param))) != OK) + server_panic("FLOPPY", "Sys_vircopy failed", s); + + /* Check that the number of sectors in the data is reasonable, + * to avoid division by 0. Leave checking of other data to + * the FDC. + */ + if (fmt_param.sectors_per_cylinder == 0) return(EIO); + + /* Only the first sector of the parameters now needed. */ + iov->iov_size = nbytes = SECTOR_SIZE; + } + + /* Only try one sector if there were errors. */ + if (errors > 0) nbytes = SECTOR_SIZE; + + /* Compute cylinder and head of the track to access. */ + fp->fl_cylinder = block / (NR_HEADS * f_sectors); + fp->fl_hardcyl = fp->fl_cylinder * f_dp->steps; + fp->fl_head = (block % (NR_HEADS * f_sectors)) / f_sectors; + + /* For each sector on this track compute the user address it is to + * go or to come from. + */ + for (up = uaddrs; up < uaddrs + MAX_SECTORS; up++) *up = 0; + count = 0; + iop = iov; + sector = block % f_sectors; + for (;;) { + user_addr = iop->iov_addr; + chunk = iop->iov_size; + if ((chunk & SECTOR_MASK) != 0) return(EINVAL); + + while (chunk > 0) { + uaddrs[sector++] = user_addr; + chunk -= SECTOR_SIZE; + user_addr += SECTOR_SIZE; + count += SECTOR_SIZE; + if (sector == f_sectors || count == nbytes) + goto track_set_up; + } + iop++; + } + track_set_up: + + /* First check to see if a reset is needed. */ + if (need_reset) f_reset(); + + /* See if motor is running; if not, turn it on and wait. */ + start_motor(); + + /* Set the stepping rate and data rate */ + if (f_dp != prev_dp) { + cmd[0] = FDC_SPECIFY; + cmd[1] = f_dp->spec1; + cmd[2] = SPEC2; + (void) fdc_command(cmd, 3); + if ((s=sys_outb(FDC_RATE, f_dp->rate)) != OK) + server_panic("FLOPPY","Sys_outb failed", s); + prev_dp = f_dp; + } + + /* If we are going to a new cylinder, perform a seek. */ + r = seek(); + + /* Avoid read_id() if we don't plan to read much. */ + if (fp->fl_sector == NO_SECTOR && count < (6 * SECTOR_SIZE)) + fp->fl_sector = 0; + + for (nbytes = 0; nbytes < count; nbytes += SECTOR_SIZE) { + if (fp->fl_sector == NO_SECTOR) { + /* Find out what the current sector is. This often + * fails right after a seek, so try it twice. + */ + if (r == OK && read_id() != OK) r = read_id(); + } + + /* Look for the next job in uaddrs[] */ + if (r == OK) { + for (;;) { + if (fp->fl_sector >= f_sectors) + fp->fl_sector = 0; + + up = &uaddrs[fp->fl_sector]; + if (*up != 0) break; + fp->fl_sector++; + } + } + + if (r == OK && opcode == DEV_SCATTER) { + /* Copy the user bytes to the DMA buffer. */ + if((s=sys_datacopy(proc_nr, *up, SELF, + (vir_bytes) tmp_buf, + (phys_bytes) SECTOR_SIZE)) != OK) + server_panic("FLOPPY", "Sys_vircopy failed", s); + } + + /* Set up the DMA chip and perform the transfer. */ + if (r == OK) { + dma_setup(opcode); + r = fdc_transfer(opcode); + } + + if (r == OK && opcode == DEV_GATHER) { + /* Copy the DMA buffer to user space. */ + if((s=sys_datacopy(SELF, (vir_bytes) tmp_buf, + proc_nr, *up, + (phys_bytes) SECTOR_SIZE)) != OK) + server_panic("FLOPPY", "Sys_vircopy failed", s); + } + + if (r != OK) { + /* Don't retry if write protected or too many errors. */ + if (err_no_retry(r) || ++errors == MAX_ERRORS) { + return(EIO); + } + + /* Recalibrate if halfway. */ + if (errors == MAX_ERRORS / 2) + fp->fl_calibration = UNCALIBRATED; + + nbytes = 0; + break; /* retry */ + } + } + + /* Book the bytes successfully transferred. */ + position += nbytes; + for (;;) { + if (nbytes < iov->iov_size) { + /* Not done with this one yet. */ + iov->iov_addr += nbytes; + iov->iov_size -= nbytes; + break; + } + nbytes -= iov->iov_size; + iov->iov_addr += iov->iov_size; + iov->iov_size = 0; + if (nbytes == 0) { + /* The rest is optional, so we return to give FS a + * chance to think it over. + */ + return(OK); + } + iov++; + nr_req--; + } + } + return(OK); +} + + +/*===========================================================================* + * dma_setup * + *===========================================================================*/ +PRIVATE void dma_setup(opcode) +int opcode; /* DEV_GATHER or DEV_SCATTER */ +{ +/* The IBM PC can perform DMA operations by using the DMA chip. To use it, + * the DMA (Direct Memory Access) chip is loaded with the 20-bit memory address + * to be read from or written to, the byte count minus 1, and a read or write + * opcode. This routine sets up the DMA chip. Note that the chip is not + * capable of doing a DMA across a 64K boundary (e.g., you can't read a + * 512-byte block starting at physical address 65520). + */ + pvb_pair_t byte_out[9]; + int s; + + /* Set up the DMA registers. (The comment on the reset is a bit strong, + * it probably only resets the floppy channel.) + */ + pv_set(byte_out[0], DMA_INIT, DMA_RESET_VAL); /* reset the dma controller */ + pv_set(byte_out[1], DMA_FLIPFLOP, 0); /* write anything to reset it */ + pv_set(byte_out[2], DMA_MODE, opcode == DEV_SCATTER ? DMA_WRITE : DMA_READ); + pv_set(byte_out[3], DMA_ADDR, (unsigned) tmp_phys >> 0); + pv_set(byte_out[4], DMA_ADDR, (unsigned) tmp_phys >> 8); + pv_set(byte_out[5], DMA_TOP, (unsigned) (tmp_phys >> 16)); + pv_set(byte_out[6], DMA_COUNT, (SECTOR_SIZE - 1) >> 0); + pv_set(byte_out[7], DMA_COUNT, (SECTOR_SIZE - 1) >> 8); + pv_set(byte_out[8], DMA_INIT, 2); /* some sort of enable */ + + if ((s=sys_voutb(byte_out, 9)) != OK) + server_panic("FLOPPY","Sys_voutb in dma_setup() failed", s); +} + + +/*===========================================================================* + * start_motor * + *===========================================================================*/ +PRIVATE void start_motor() +{ +/* Control of the floppy disk motors is a big pain. If a motor is off, you + * have to turn it on first, which takes 1/2 second. You can't leave it on + * all the time, since that would wear out the diskette. However, if you turn + * the motor off after each operation, the system performance will be awful. + * The compromise used here is to leave it on for a few seconds after each + * operation. If a new operation is started in that interval, it need not be + * turned on again. If no new operation is started, a timer goes off and the + * motor is turned off. I/O port DOR has bits to control each of 4 drives. + */ + + int s, motor_bit, running; + message mess; + + motor_bit = 1 << f_drive; /* bit mask for this drive */ + running = motor_status & motor_bit; /* nonzero if this motor is running */ + motor_status |= motor_bit; /* want this drive running too */ + + if ((s=sys_outb(DOR, + (motor_status << MOTOR_SHIFT) | ENABLE_INT | f_drive)) != OK) + server_panic("FLOPPY","Sys_outb in start_motor() failed", s); + + /* If the motor was already running, we don't have to wait for it. */ + if (running) return; /* motor was already running */ + + /* Set an alarm timer to force a timeout if the hardware does not interrupt + * in time. Expect HARD_INT message, but check for SYN_ALARM timeout. + */ + f_set_timer(&f_tmr_timeout, f_dp->start, f_timeout); + f_busy = BSY_IO; + do { + receive(HARDWARE, &mess); + if (mess.m_type == SYN_ALARM) { + f_expire_tmrs(NULL); + } else { + f_busy = BSY_IDLE; + } + } while (f_busy == BSY_IO); + f_fp->fl_sector = NO_SECTOR; +} + + +/*===========================================================================* + * stop_motor * + *===========================================================================*/ +PRIVATE void stop_motor(tp) +timer_t *tp; +{ +/* This routine is called from an alarm timer after several seconds have + * elapsed with no floppy disk activity. It turns the drive motor off. + */ + int s; + motor_status &= ~(1 << tmr_arg(tp)->ta_int); + if ((s=sys_outb(DOR, (motor_status << MOTOR_SHIFT) | ENABLE_INT)) != OK) + server_panic("FLOPPY","Sys_outb in stop_motor() failed", s); +} + + +/*===========================================================================* + * floppy_stop * + *===========================================================================*/ +PRIVATE void floppy_stop(struct driver *dp) +{ +/* Stop all activity and cleanly exit with the system. */ + int s; + if ((s=sys_outb(DOR, ENABLE_INT)) != OK) + server_panic("FLOPPY","Sys_outb in floppy_stop() failed", s); + sys_exit(0); +} + + +/*===========================================================================* + * seek * + *===========================================================================*/ +PRIVATE int seek() +{ +/* Issue a SEEK command on the indicated drive unless the arm is already + * positioned on the correct cylinder. + */ + + struct floppy *fp = f_fp; + int r; + message mess; + u8_t cmd[3]; + + /* Are we already on the correct cylinder? */ + if (fp->fl_calibration == UNCALIBRATED) + if (recalibrate() != OK) return(ERR_SEEK); + if (fp->fl_curcyl == fp->fl_hardcyl) return(OK); + + /* No. Wrong cylinder. Issue a SEEK and wait for interrupt. */ + cmd[0] = FDC_SEEK; + cmd[1] = (fp->fl_head << 2) | f_drive; + cmd[2] = fp->fl_hardcyl; + if (fdc_command(cmd, 3) != OK) return(ERR_SEEK); + if (f_intr_wait() != OK) return(ERR_TIMEOUT); + + /* Interrupt has been received. Check drive status. */ + fdc_out(FDC_SENSE); /* probe FDC to make it return status */ + r = fdc_results(); /* get controller status bytes */ + if (r != OK || (f_results[ST0] & ST0_BITS) != SEEK_ST0 + || f_results[ST1] != fp->fl_hardcyl) { + /* seek failed, may need a recalibrate */ + return(ERR_SEEK); + } + /* Give head time to settle on a format, no retrying here! */ + if (f_device & FORMAT_DEV_BIT) { + /* Set a synchronous alarm to force a timeout if the hardware does + * not interrupt. Expect HARD_INT, but check for SYN_ALARM timeout. + */ + f_set_timer(&f_tmr_timeout, HZ/30, f_timeout); + f_busy = BSY_IO; + do { + /* printf("Wait for message from HARDWARE 0\n"); */ + receive(HARDWARE, &mess); + /* printf("Floppy: got message, type %d (HARD_INT %d)\n", + mess.m_type, (mess.m_type == HARD_INT)); */ + if (mess.m_type == SYN_ALARM) { + f_expire_tmrs(NULL); + } else { + f_busy = BSY_IDLE; + } + } while (f_busy == BSY_IO); + } + fp->fl_curcyl = fp->fl_hardcyl; + fp->fl_sector = NO_SECTOR; + return(OK); +} + + +/*===========================================================================* + * fdc_transfer * + *===========================================================================*/ +PRIVATE int fdc_transfer(opcode) +int opcode; /* DEV_GATHER or DEV_SCATTER */ +{ +/* The drive is now on the proper cylinder. Read, write or format 1 block. */ + + struct floppy *fp = f_fp; + int r, s; + u8_t cmd[9]; + + /* Never attempt a transfer if the drive is uncalibrated or motor is off. */ + if (fp->fl_calibration == UNCALIBRATED) return(ERR_TRANSFER); + if ((motor_status & (1 << f_drive)) == 0) return(ERR_TRANSFER); + + /* The command is issued by outputting several bytes to the controller chip. + */ + if (f_device & FORMAT_DEV_BIT) { + cmd[0] = FDC_FORMAT; + cmd[1] = (fp->fl_head << 2) | f_drive; + cmd[2] = fmt_param.sector_size_code; + cmd[3] = fmt_param.sectors_per_cylinder; + cmd[4] = fmt_param.gap_length_for_format; + cmd[5] = fmt_param.fill_byte_for_format; + if (fdc_command(cmd, 6) != OK) return(ERR_TRANSFER); + } else { + cmd[0] = opcode == DEV_SCATTER ? FDC_WRITE : FDC_READ; + cmd[1] = (fp->fl_head << 2) | f_drive; + cmd[2] = fp->fl_cylinder; + cmd[3] = fp->fl_head; + cmd[4] = BASE_SECTOR + fp->fl_sector; + cmd[5] = SECTOR_SIZE_CODE; + cmd[6] = f_sectors; + cmd[7] = f_dp->gap; /* sector gap */ + cmd[8] = DTL; /* data length */ + if (fdc_command(cmd, 9) != OK) return(ERR_TRANSFER); + } + + /* Block, waiting for disk interrupt. */ + if (f_intr_wait() != OK) { + printf("%s: disk interrupt timed out.\n", f_name()); + return(ERR_TIMEOUT); + } + + /* Get controller status and check for errors. */ + r = fdc_results(); + if (r != OK) return(r); + + if (f_results[ST1] & WRITE_PROTECT) { + printf("%s: diskette is write protected.\n", f_name()); + return(ERR_WR_PROTECT); + } + + if ((f_results[ST0] & ST0_BITS) != TRANS_ST0) return(ERR_TRANSFER); + if (f_results[ST1] | f_results[ST2]) return(ERR_TRANSFER); + + if (f_device & FORMAT_DEV_BIT) return(OK); + + /* Compare actual numbers of sectors transferred with expected number. */ + s = (f_results[ST_CYL] - fp->fl_cylinder) * NR_HEADS * f_sectors; + s += (f_results[ST_HEAD] - fp->fl_head) * f_sectors; + s += (f_results[ST_SEC] - BASE_SECTOR - fp->fl_sector); + if (s != 1) return(ERR_TRANSFER); + + /* This sector is next for I/O: */ + fp->fl_sector = f_results[ST_SEC] - BASE_SECTOR; +#if 0 + if (processor < 386) fp->fl_sector++; /* Old CPU can't keep up. */ +#endif + return(OK); +} + + +/*==========================================================================* + * fdc_results * + *==========================================================================*/ +PRIVATE int fdc_results() +{ +/* Extract results from the controller after an operation, then allow floppy + * interrupts again. + */ + + int s, result_nr, status; + static int timeout; /* must be static if not cancelled */ + int irqs; + + /* Extract bytes from FDC until it says it has no more. The loop is + * really an outer loop on result_nr and an inner loop on status. + * A timeout flag alarm is set. + */ + result_nr = 0; + timeout = 0; + sys_flagalrm(TIMEOUT_TICKS, &timeout); + do { + /* Reading one byte is almost a mirror of fdc_out() - the DIRECTION + * bit must be set instead of clear, but the CTL_BUSY bit destroys + * the perfection of the mirror. + */ + if ((s=sys_inb(FDC_STATUS, &status)) != OK) + server_panic("FLOPPY","Sys_inb in fdc_results() failed", s); + status &= (MASTER | DIRECTION | CTL_BUSY); + if (status == (MASTER | DIRECTION | CTL_BUSY)) { + if (result_nr >= MAX_RESULTS) break; /* too many results */ + if ((s=sys_inb(FDC_DATA, &f_results[result_nr])) != OK) + server_panic("FLOPPY","Sys_inb in fdc_results() failed", s); + result_nr ++; + continue; + } + if (status == MASTER) { /* all read */ + if ((irqs=sys_irqenable(FLOPPY_IRQ)) != OK) + server_panic("FLOPPY", "Couldn't enable IRQs", irqs); + + /* Disabling the alarm is not needed, because a static flag + * is used and a leftover timeout cannot do any harm. It is + * done for correctness. This can safely be removed, though. + */ + sys_flagalrm(0, &timeout); /* for correctness */ + return(OK); /* only good exit */ + } + } while (! timeout); + need_reset = TRUE; /* controller chip must be reset */ + + if ((irqs=sys_irqenable(FLOPPY_IRQ)) != OK) + server_panic("FLOPPY", "Couldn't enable IRQs", irqs); + return(ERR_STATUS); +} + + +#if DEAD_CODE +/*==========================================================================* + * f_handler * + *==========================================================================*/ +PRIVATE int f_handler(hook) +irq_hook_t *hook; +{ +/* FDC interrupt, send message to floppy task. */ +#if DEAD_CODE + f_busy = BSY_IDLE; +#endif + notify(FLOPPY, HARD_INT); + return 0; +} +#endif + + +/*===========================================================================* + * fdc_command * + *===========================================================================*/ +PRIVATE int fdc_command(cmd, len) +u8_t *cmd; /* command bytes */ +int len; /* command length */ +{ +/* Output a command to the controller. */ + + /* Set a synchronous alarm to force a timeout if the hardware does + * not interrupt. Expect HARD_INT, but check for SYN_ALARM timeout. + * Note that the actual check is done by the code that issued the + * fdc_command() call. + */ + f_set_timer(&f_tmr_timeout, WAKEUP, f_timeout); + + f_busy = BSY_IO; + while (len > 0) { + fdc_out(*cmd++); + len--; + } + return(need_reset ? ERR_DRIVE : OK); +} + + +/*===========================================================================* + * fdc_out * + *===========================================================================*/ +PRIVATE void fdc_out(val) +int val; /* write this byte to floppy disk controller */ +{ +/* Output a byte to the controller. This is not entirely trivial, since you + * can only write to it when it is listening, and it decides when to listen. + * If the controller refuses to listen, the FDC chip is given a hard reset. + */ + + static int timeout; /* must be static if not cancelled */ + int s, status; + + if (need_reset) return; /* if controller is not listening, return */ + + /* It may take several tries to get the FDC to accept a command. + * A timeout flag alarm is set. + */ + timeout = 0; + sys_flagalrm(TIMEOUT_TICKS, &timeout); + do { + if (timeout) { /* controller is not listening */ + need_reset = TRUE; /* hit it over the head */ + return; + } + if ((s=sys_inb(FDC_STATUS, &status)) != OK) + server_panic("FLOPPY","Sys_inb in fdc_out() failed", s); + } + while ((status & (MASTER | DIRECTION)) != (MASTER | 0)); + + /* Disabling the alarm is not needed, because a static flag is used and a + * leftover timeout cannot do any harm. It is done for correctness. This + * can safely be removed, though. + */ + sys_flagalrm(0, &timeout); /* for correctness */ + if ((s=sys_outb(FDC_DATA, val)) != OK) + server_panic("FLOPPY","Sys_outb in fdc_out() failed", s); +} + + +/*===========================================================================* + * recalibrate * + *===========================================================================*/ +PRIVATE int recalibrate() +{ +/* The floppy disk controller has no way of determining its absolute arm + * position (cylinder). Instead, it steps the arm a cylinder at a time and + * keeps track of where it thinks it is (in software). However, after a + * SEEK, the hardware reads information from the diskette telling where the + * arm actually is. If the arm is in the wrong place, a recalibration is done, + * which forces the arm to cylinder 0. This way the controller can get back + * into sync with reality. + */ + + struct floppy *fp = f_fp; + int r; + u8_t cmd[2]; + + /* Issue the RECALIBRATE command and wait for the interrupt. */ + cmd[0] = FDC_RECALIBRATE; /* tell drive to recalibrate itself */ + cmd[1] = f_drive; /* specify drive */ + if (fdc_command(cmd, 2) != OK) return(ERR_SEEK); + if (f_intr_wait() != OK) return(ERR_TIMEOUT); + + /* Determine if the recalibration succeeded. */ + fdc_out(FDC_SENSE); /* issue SENSE command to request results */ + r = fdc_results(); /* get results of the FDC_RECALIBRATE command*/ + fp->fl_curcyl = NO_CYL; /* force a SEEK next time */ + fp->fl_sector = NO_SECTOR; + if (r != OK || /* controller would not respond */ + (f_results[ST0] & ST0_BITS) != SEEK_ST0 || f_results[ST_PCN] != 0) { + /* Recalibration failed. FDC must be reset. */ + need_reset = TRUE; + return(ERR_RECALIBRATE); + } else { + /* Recalibration succeeded. */ + fp->fl_calibration = CALIBRATED; + fp->fl_curcyl = f_results[ST_PCN]; + return(OK); + } +} + + +/*===========================================================================* + * f_reset * + *===========================================================================*/ +PRIVATE void f_reset() +{ +/* Issue a reset to the controller. This is done after any catastrophe, + * like the controller refusing to respond. + */ + pvb_pair_t byte_out[2]; + int s,i; + message mess; + + /* Disable interrupts and strobe reset bit low. */ + need_reset = FALSE; + + /* It is not clear why the next lock is needed. Writing 0 to DOR causes + * interrupt, while the PC documentation says turning bit 8 off disables + * interrupts. Without the lock: + * 1) the interrupt handler sets the floppy mask bit in the 8259. + * 2) writing ENABLE_INT to DOR causes the FDC to assert the interrupt + * line again, but the mask stops the cpu being interrupted. + * 3) the sense interrupt clears the interrupt (not clear which one). + * and for some reason the reset does not work. + */ + (void) fdc_command((u8_t *) 0, 0); /* need only the timer */ + motor_status = 0; + pv_set(byte_out[0], DOR, 0); /* strobe reset bit low */ + pv_set(byte_out[1], DOR, ENABLE_INT); /* strobe it high again */ + if ((s=sys_voutb(byte_out, 2)) != OK) + server_panic("FLOPPY", "Sys_voutb in f_reset() failed", s); + + /* A synchronous alarm timer was set in fdc_command. Expect a HARD_INT + * message to collect the reset interrupt, but be prepared to handle the + * SYN_ALARM message on a timeout. + */ + do { + receive(HARDWARE, &mess); + if (mess.m_type == SYN_ALARM) { + f_expire_tmrs(NULL); + } else { /* expect HARD_INT */ + f_busy = BSY_IDLE; + } + } while (f_busy == BSY_IO); + + /* The controller supports 4 drives and returns a result for each of them. + * Collect all the results now. The old version only collected the first + * result. This happens to work for 2 drives, but it doesn't work for 3 + * or more drives, at least with only drives 0 and 2 actually connected + * (the controller generates an extra interrupt for the middle drive when + * drive 2 is accessed and the driver panics). + * + * It would be better to keep collecting results until there are no more. + * For this, fdc_results needs to return the number of results (instead + * of OK) when it succeeds. + */ + for (i = 0; i < 4; i++) { + fdc_out(FDC_SENSE); /* probe FDC to make it return status */ + (void) fdc_results(); /* flush controller */ + } + for (i = 0; i < NR_DRIVES; i++) /* clear each drive */ + floppy[i].fl_calibration = UNCALIBRATED; + + /* The current timing parameters must be specified again. */ + prev_dp = NULL; +} + + +/*===========================================================================* + * f_intr_wait * + *===========================================================================*/ +PRIVATE int f_intr_wait() +{ +/* Wait for an interrupt, but not forever. The FDC may have all the time of + * the world, but we humans do not. + */ + message mess; + + /* We expect a HARD_INT message from the interrupt handler, but if there is + * a timeout, a SYN_ALARM notification is received instead. If a timeout + * occurs, report an error. + */ + do { + /* printf("Wait for message from HARDWARE 2\n"); */ + receive(HARDWARE, &mess); + /* printf("Floppy: got message, type %d (HARD_INT %d) ", + mess.m_type, (mess.m_type == HARD_INT)); */ + if (mess.m_type == SYN_ALARM) { + f_expire_tmrs(NULL); + } else { + f_busy = BSY_IDLE; + } + } while (f_busy == BSY_IO); + + if (f_busy == BSY_WAKEN) { + + /* No interrupt from the FDC, this means that there is probably no + * floppy in the drive. Get the FDC down to earth and return error. + */ + need_reset = TRUE; + return(ERR_TIMEOUT); + } + return(OK); +} + +/*===========================================================================* + * f_timeout * + *===========================================================================*/ +PRIVATE void f_timeout(tp) +timer_t *tp; +{ +/* This routine is called when a timer expires. Usually to tell that a + * motor has spun up, but also to forge an interrupt when it takes too long + * for the FDC to interrupt (no floppy in the drive). It sets a flag to tell + * what has happened. + */ + if (f_busy == BSY_IO) { + f_busy = BSY_WAKEN; + } +} + + +/*==========================================================================* + * read_id * + *==========================================================================*/ +PRIVATE int read_id() +{ +/* Determine current cylinder and sector. */ + + struct floppy *fp = f_fp; + int result; + u8_t cmd[2]; + + /* Never attempt a read id if the drive is uncalibrated or motor is off. */ + if (fp->fl_calibration == UNCALIBRATED) return(ERR_READ_ID); + if ((motor_status & (1 << f_drive)) == 0) return(ERR_READ_ID); + + /* The command is issued by outputting 2 bytes to the controller chip. */ + cmd[0] = FDC_READ_ID; /* issue the read id command */ + cmd[1] = (fp->fl_head << 2) | f_drive; + if (fdc_command(cmd, 2) != OK) return(ERR_READ_ID); + if (f_intr_wait() != OK) return(ERR_TIMEOUT); + + /* Get controller status and check for errors. */ + result = fdc_results(); + if (result != OK) return(result); + + if ((f_results[ST0] & ST0_BITS) != TRANS_ST0) return(ERR_READ_ID); + if (f_results[ST1] | f_results[ST2]) return(ERR_READ_ID); + + /* The next sector is next for I/O: */ + fp->fl_sector = f_results[ST_SEC] - BASE_SECTOR + 1; + return(OK); +} + + +/*==========================================================================* + * f_do_open * + *==========================================================================*/ +PRIVATE int f_do_open(dp, m_ptr) +struct driver *dp; +message *m_ptr; /* pointer to open message */ +{ +/* Handle an open on a floppy. Determine diskette type if need be. */ + + int dtype; + struct test_order *top; + + /* Decode the message parameters. */ + if (f_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO); + + dtype = f_device & DEV_TYPE_BITS; /* get density from minor dev */ + if (dtype >= MINOR_fd0p0) dtype = 0; + + if (dtype != 0) { + /* All types except 0 indicate a specific drive/medium combination.*/ + dtype = (dtype >> DEV_TYPE_SHIFT) - 1; + if (dtype >= NT) return(ENXIO); + f_fp->fl_density = dtype; + (void) f_prepare(f_device); /* Recompute parameters. */ + return(OK); + } + if (f_device & FORMAT_DEV_BIT) return(EIO); /* Can't format /dev/fdN */ + + /* The device opened is /dev/fdN. Experimentally determine drive/medium. + * First check fl_density. If it is not NO_DENS, the drive has been used + * before and the value of fl_density tells what was found last time. Try + * that first. If the motor is still running then assume nothing changed. + */ + if (f_fp->fl_density != NO_DENS) { + if (motor_status & (1 << f_drive)) return(OK); + if (test_read(f_fp->fl_density) == OK) return(OK); + } + + /* Either drive type is unknown or a different diskette is now present. + * Use test_order to try them one by one. + */ + for (top = &test_order[0]; top < &test_order[NT-1]; top++) { + dtype = top->t_density; + + /* Skip densities that have been proven to be impossible */ + if (!(f_fp->fl_class & (1 << dtype))) continue; + + if (test_read(dtype) == OK) { + /* The test succeeded, use this knowledge to limit the + * drive class to match the density just read. + */ + f_fp->fl_class &= top->t_class; + return(OK); + } + /* Test failed, wrong density or did it time out? */ + if (f_busy == BSY_WAKEN) break; + } + f_fp->fl_density = NO_DENS; + return(EIO); /* nothing worked */ +} + + +/*==========================================================================* + * test_read * + *==========================================================================*/ +PRIVATE int test_read(density) +int density; +{ +/* Try to read the highest numbered sector on cylinder 2. Not all floppy + * types have as many sectors per track, and trying cylinder 2 finds the + * ones that need double stepping. + */ + int device; + off_t position; + iovec_t iovec1; + int result; + + f_fp->fl_density = density; + device = ((density + 1) << DEV_TYPE_SHIFT) + f_drive; + + (void) f_prepare(device); + position = (off_t) f_dp->test << SECTOR_SHIFT; + iovec1.iov_addr = (vir_bytes) tmp_buf; + iovec1.iov_size = SECTOR_SIZE; + result = f_transfer(SELF, DEV_GATHER, position, &iovec1, 1); + + if (iovec1.iov_size != 0) return(EIO); + + partition(&f_dtab, f_drive, P_FLOPPY); + return(OK); +} + + +/*============================================================================* + * f_geometry * + *============================================================================*/ +PRIVATE void f_geometry(entry) +struct partition *entry; +{ + entry->cylinders = f_dp->cyls; + entry->heads = NR_HEADS; + entry->sectors = f_sectors; +} diff --git a/drivers/floppy/floppy.h b/drivers/floppy/floppy.h new file mode 100644 index 000000000..b1c64ccf1 --- /dev/null +++ b/drivers/floppy/floppy.h @@ -0,0 +1,6 @@ +#include "../drivers.h" +#include "../libdriver/driver.h" +#include "../libdriver/drvlib.h" + +_PROTOTYPE(void main, (void)); + diff --git a/drivers/libdriver/Makefile b/drivers/libdriver/Makefile new file mode 100644 index 000000000..178cfac31 --- /dev/null +++ b/drivers/libdriver/Makefile @@ -0,0 +1,36 @@ +# Makefile for driver library + +# Directories +u = /usr +i = $u/include +s = $i/sys +b = $i/ibm +m = $i/minix + +# Programs, flags, etc. +CC = exec cc +CFLAGS = -I$i +LDFLAGS = -i +LIBS = -lsys -lutils + +OBJECTS = driver.o drvlib.o + +all build: $(OBJECTS) + +# $(CC) -c $@ $(LDFLAGS) $(OBJ) $(LIBS) + +clean: + rm -f *.o *.bak + +# Dependencies +a = $m/config.h $i/ansi.h $m/type.h $m/com.h $m/callnr.h $s/types.h \ + $m/const.h $m/syslib.h $m/utils.h \ + $i/string.h $i/limits.h $i/stddef.h $i/errno.h \ + $m/partition.h $m/u64.h + +driver.o: driver.h $a +driver.o: $s/ioc_disk.h + +drvlib.o: driver.h $a +drvlib.o: drvlib.h $b/partition.h + diff --git a/drivers/libdriver/driver.c b/drivers/libdriver/driver.c new file mode 100644 index 000000000..b4010dec1 --- /dev/null +++ b/drivers/libdriver/driver.c @@ -0,0 +1,420 @@ +/* This file contains device independent device driver interface. + * Author: Kees J. Bot. + * Changes: + * Sep 15, 2004 added SYN_ALARM type for timeouts (Jorrit N. Herder) + * Aug 18, 2004 added HARD_STOP type for shutdown (Jorrit N. Herder) + * Jul 23, 2004 removed kernel dependencies (Jorrit N. Herder) + * + * + * The drivers support the following operations (using message format m2): + * + * m_type DEVICE PROC_NR COUNT POSITION ADRRESS + * ---------------------------------------------------------------- + * | DEV_OPEN | device | proc nr | | | | + * |------------+---------+---------+---------+---------+---------| + * | DEV_CLOSE | device | proc nr | | | | + * |------------+---------+---------+---------+---------+---------| + * | DEV_READ | device | proc nr | bytes | offset | buf ptr | + * |------------+---------+---------+---------+---------+---------| + * | DEV_WRITE | device | proc nr | bytes | offset | buf ptr | + * |------------+---------+---------+---------+---------+---------| + * | DEV_GATHER | device | proc nr | iov len | offset | iov ptr | + * |------------+---------+---------+---------+---------+---------| + * | DEV_SCATTER| device | proc nr | iov len | offset | iov ptr | + * |------------+---------+---------+---------+---------+---------| + * | DEV_IOCTL | device | proc nr |func code| | buf ptr | + * |------------+---------+---------+---------+---------+---------| + * | HARD_STOP | | | | | | + * ---------------------------------------------------------------- + * + * The file contains one entry point: + * + * driver_task: called by the device dependent task entry + * + * + * Constructed 92/04/02 by Kees J. Bot from the old AT wini and floppy driver. + */ + +#include "../drivers.h" +#include <sys/ioc_disk.h> +#include "driver.h" + + +#if (CHIP == INTEL) +#if ENABLE_AHA1540_SCSI && DMA_BUF_SIZE < 2048 +/* A bit extra scratch for the Adaptec driver. */ +#define BUF_EXTRA (2048 - DMA_BUF_SIZE) +#else +#define BUF_EXTRA 0 +#endif + +/* Claim space for variables. */ +PRIVATE u8_t buffer[(unsigned) 2 * DMA_BUF_SIZE + BUF_EXTRA]; +u8_t *tmp_buf; /* the DMA buffer eventually */ +phys_bytes tmp_phys; /* phys address of DMA buffer */ + +#else /* CHIP != INTEL */ + +/* Claim space for variables. */ +u8_t tmp_buf[DMA_BUF_SIZE]; /* the DMA buffer */ +phys_bytes tmp_phys; /* phys address of DMA buffer */ + +#endif /* CHIP != INTEL */ + +FORWARD _PROTOTYPE( void init_buffer, (void) ); +FORWARD _PROTOTYPE( int do_rdwt, (struct driver *dr, message *mp) ); +FORWARD _PROTOTYPE( int do_vrdwt, (struct driver *dr, message *mp) ); + +PRIVATE unsigned long dev_read_count; +PRIVATE unsigned long dev_write_count; +PRIVATE unsigned long dev_gather_count; +PRIVATE unsigned long dev_scatter_count; + +/*===========================================================================* + * driver_task * + *===========================================================================*/ +PUBLIC void driver_task(dp) +struct driver *dp; /* Device dependent entry points. */ +{ +/* Main program of any device driver task. */ + + int r, caller, proc_nr; + message mess; + int s; + + /* Get a DMA buffer. */ + init_buffer(); + + /* Here is the main loop of the disk task. It waits for a message, carries + * it out, and sends a reply. + */ + while (TRUE) { + + /* Wait for a request to read or write a disk block. */ + receive(ANY, &mess); + + caller = mess.m_source; + proc_nr = mess.PROC_NR; + +#if DEAD_CODE /* drivers, like TTY can have any number */ + /* Check if legitimate caller: FS or a task. */ + if (caller != FS_PROC_NR && caller >= 0) { + printf("%s: got message from %d\n", (*dp->dr_name)(), caller); + continue; + } +#endif + + /* Now carry out the work. */ + switch(mess.m_type) { + case DEV_OPEN: r = (*dp->dr_open)(dp, &mess); break; + case DEV_CLOSE: r = (*dp->dr_close)(dp, &mess); break; + case DEV_IOCTL: r = (*dp->dr_ioctl)(dp, &mess); break; + + case DEV_READ: dev_read_count ++; + case DEV_WRITE: dev_write_count ++; + r = do_rdwt(dp, &mess); break; + case DEV_GATHER: dev_gather_count ++; + case DEV_SCATTER: dev_scatter_count ++; + r = do_vrdwt(dp, &mess); break; + + case HARD_INT: /* leftover interrupt or expired timer. */ + /* printf("Driver.c: Left-over interrupt!\n"); */ + continue; + case HARD_STOP: (*dp->dr_stop)(dp); + continue; /* don't reply */ + case SYN_ALARM: (*dp->dr_alarm)(dp); + continue; /* don't reply */ + case FKEY_PRESSED: (*dp->dr_fkey)(dp, &mess); + continue; /* don't reply */ + default: r = EINVAL; break; + } + + /* Clean up leftover state. */ + (*dp->dr_cleanup)(); + + /* Finally, prepare and send the reply message. */ + mess.m_type = TASK_REPLY; + mess.REP_PROC_NR = proc_nr; + + mess.REP_STATUS = r; /* # of bytes transferred or error code */ + send(caller, &mess); /* send reply to caller */ + } +} + + +/*===========================================================================* + * init_buffer * + *===========================================================================*/ +PRIVATE void init_buffer() +{ +/* Select a buffer that can safely be used for DMA transfers. It may also + * be used to read partition tables and such. Its absolute address is + * 'tmp_phys', the normal address is 'tmp_buf'. + */ + +#if (CHIP == INTEL) + unsigned left; + + tmp_buf = buffer; + sys_umap(SELF, D, (vir_bytes)buffer, (phys_bytes)sizeof(buffer), &tmp_phys); + + if ((left = dma_bytes_left(tmp_phys)) < DMA_BUF_SIZE) { + /* First half of buffer crosses a 64K boundary, can't DMA into that */ + tmp_buf += left; + tmp_phys += left; + } +#else /* CHIP != INTEL */ + tmp_phys = vir2phys(tmp_buf); +#endif /* CHIP != INTEL */ +} + + +/*===========================================================================* + * do_rdwt * + *===========================================================================*/ +PRIVATE int do_rdwt(dp, mp) +struct driver *dp; /* device dependent entry points */ +message *mp; /* pointer to read or write message */ +{ +/* Carry out a single read or write request. */ + iovec_t iovec1; + int r, opcode; + phys_bytes phys_addr; + + /* Disk address? Address and length of the user buffer? */ + if (mp->COUNT < 0) return(EINVAL); + + /* Check the user buffer. */ + sys_umap(mp->PROC_NR, D, (vir_bytes) mp->ADDRESS, mp->COUNT, &phys_addr); + if (phys_addr == 0) return(EFAULT); + + /* Prepare for I/O. */ + if ((*dp->dr_prepare)(mp->DEVICE) == NIL_DEV) return(ENXIO); + + /* Create a one element scatter/gather vector for the buffer. */ + opcode = mp->m_type == DEV_READ ? DEV_GATHER : DEV_SCATTER; + iovec1.iov_addr = (vir_bytes) mp->ADDRESS; + iovec1.iov_size = mp->COUNT; + + /* Transfer bytes from/to the device. */ + r = (*dp->dr_transfer)(mp->PROC_NR, opcode, mp->POSITION, &iovec1, 1); + + /* Return the number of bytes transferred or an error code. */ + return(r == OK ? (mp->COUNT - iovec1.iov_size) : r); +} + + +/*==========================================================================* + * do_vrdwt * + *==========================================================================*/ +PRIVATE int do_vrdwt(dp, mp) +struct driver *dp; /* device dependent entry points */ +message *mp; /* pointer to read or write message */ +{ +/* Carry out an device read or write to/from a vector of user addresses. + * The "user addresses" are assumed to be safe, i.e. FS transferring to/from + * its own buffers, so they are not checked. + */ + static iovec_t iovec[NR_IOREQS]; + iovec_t *iov; + phys_bytes iovec_phys, user_iovec_phys; + phys_bytes iovec_size; + unsigned nr_req; + int r; + + nr_req = mp->COUNT; /* Length of I/O vector */ + + if (mp->m_source < 0) { + /* Called by a task, no need to copy vector. */ + iov = (iovec_t *) mp->ADDRESS; + } else { + /* Copy the vector from the caller to kernel space. */ + if (nr_req > NR_IOREQS) nr_req = NR_IOREQS; + iovec_size = (phys_bytes) (nr_req * sizeof(iovec[0])); + + if (OK != sys_datacopy(mp->m_source, (vir_bytes) mp->ADDRESS, + SELF, (vir_bytes) iovec, iovec_size)) + server_panic((*dp->dr_name)(),"bad I/O vector by", mp->m_source); +#if DEAD_CODE + sys_umap(mp->m_source, (vir_bytes) mp->ADDRESS, + iovec_size, &user_iovec_phys); + if (user_iovec_phys == 0) + server_panic((*dp->dr_name)(),"bad I/O vector by", mp->m_source); + iovec_phys = vir2phys(iovec); + phys_copy(user_iovec_phys, iovec_phys, (phys_bytes) iovec_size); +#endif + iov = iovec; + } + + /* Prepare for I/O. */ + if ((*dp->dr_prepare)(mp->DEVICE) == NIL_DEV) return(ENXIO); + + /* Transfer bytes from/to the device. */ + r = (*dp->dr_transfer)(mp->PROC_NR, mp->m_type, mp->POSITION, iov, nr_req); + + /* Copy the I/O vector back to the caller. */ + if (mp->m_source >= 0) { + sys_datacopy(SELF, (vir_bytes) iovec, + mp->m_source, (vir_bytes) mp->ADDRESS, iovec_size); +#if DEAD_CODE + phys_copy(iovec_phys, user_iovec_phys, (phys_bytes) iovec_size); +#endif + } + return(r); +} + + +/*===========================================================================* + * no_name * + *===========================================================================*/ +PUBLIC char *no_name() +{ +/* Use this default name if there is no specific name for the device. This was + * originally done by fetching the name from the task table for this process: + * "return(tasktab[proc_number(proc_ptr) + NR_TASKS].name);", but currently a + * real "noname" is returned. Perhaps, some system information service can be + * queried for a name at a later time. + */ + static char name[] = "noname"; + return name; +} + + +/*============================================================================* + * do_nop * + *============================================================================*/ +PUBLIC int do_nop(dp, mp) +struct driver *dp; +message *mp; +{ +/* Nothing there, or nothing to do. */ + + switch (mp->m_type) { + case DEV_OPEN: return(ENODEV); + case DEV_CLOSE: return(OK); + case DEV_IOCTL: return(ENOTTY); + default: return(EIO); + } +} + +/*============================================================================* + * nop_stop * + *============================================================================*/ +PUBLIC void nop_stop(dp) +struct driver *dp; +{ +/* No cleanup needed on shutdown. */ + sys_exit(0); +} + +/*============================================================================* + * nop_alarm * + *============================================================================*/ +PUBLIC void nop_alarm(dp) +struct driver *dp; +{ +/* Ignore the leftover alarm. */ +} + +/*===========================================================================* + * nop_prepare * + *===========================================================================*/ +PUBLIC struct device *nop_prepare(device) +{ +/* Nothing to prepare for. */ + return(NIL_DEV); +} + +/*===========================================================================* + * nop_cleanup * + *===========================================================================*/ +PUBLIC void nop_cleanup() +{ +/* Nothing to clean up. */ +} + +/*===========================================================================* + * nop_stats * + *===========================================================================*/ +PUBLIC void nop_stats(dp, m_ptr) +struct driver *dp; +message *m_ptr; +{ +/* Dump message counts. */ + printf("Driver shared code statistics for %s\n", (*dp->dr_name)()); + printf("DEV_READ %10u\n", dev_read_count); + printf("DEV_WRITE %10u\n", dev_write_count); + printf("DEV_GATHER %10u\n", dev_gather_count); + printf("DEV_SCATTER %10u\n", dev_scatter_count); +} + +/*===========================================================================* + * nop_task * + *===========================================================================*/ +PUBLIC void nop_task() +{ +/* Unused controllers are "serviced" by this task. */ + struct driver nop_tab = { + no_name, + do_nop, + do_nop, + do_nop, + nop_prepare, + NULL, + nop_cleanup, + NULL, + nop_stop, + nop_alarm, + }; + driver_task(&nop_tab); +} + + +/*============================================================================* + * do_diocntl * + *============================================================================*/ +PUBLIC int do_diocntl(dp, mp) +struct driver *dp; +message *mp; /* pointer to ioctl request */ +{ +/* Carry out a partition setting/getting request. */ + struct device *dv; + phys_bytes user_phys, entry_phys; + struct partition entry; + int s; + + if (mp->REQUEST != DIOCSETP && mp->REQUEST != DIOCGETP) return(ENOTTY); + + /* Decode the message parameters. */ + if ((dv = (*dp->dr_prepare)(mp->DEVICE)) == NIL_DEV) return(ENXIO); + +#if DEAD_CODE + sys_umap(mp->PROC_NR, D, (vir_bytes) mp->ADDRESS, sizeof(entry), &user_phys); + if (user_phys == 0) return(EFAULT); + entry_phys = vir2phys(&entry); +#endif + + if (mp->REQUEST == DIOCSETP) { + /* Copy just this one partition table entry. */ +#if DEAD_CODE + phys_copy(user_phys, entry_phys, (phys_bytes) sizeof(entry)); +#endif + if (OK != (s=sys_datacopy(mp->PROC_NR, (vir_bytes) mp->ADDRESS, + SELF, (vir_bytes) &entry, sizeof(entry)))) + return s; + dv->dv_base = entry.base; + dv->dv_size = entry.size; + } else { + /* Return a partition table entry and the geometry of the drive. */ + entry.base = dv->dv_base; + entry.size = dv->dv_size; + (*dp->dr_geometry)(&entry); +#if DEAD_CODE + phys_copy(entry_phys, user_phys, (phys_bytes) sizeof(entry)); +#endif + if (OK != (s=sys_datacopy(SELF, (vir_bytes) &entry, + mp->PROC_NR, (vir_bytes) mp->ADDRESS, sizeof(entry)))) + return s; + } + return(OK); +} diff --git a/drivers/libdriver/driver.h b/drivers/libdriver/driver.h new file mode 100644 index 000000000..df34347ed --- /dev/null +++ b/drivers/libdriver/driver.h @@ -0,0 +1,85 @@ +/* Types and constants shared between the generic and device dependent + * device driver code. + */ + +#define _POSIX_SOURCE 1 /* tell headers to include POSIX stuff */ +#define _MINIX 1 /* tell headers to include MINIX stuff */ +#define _SYSTEM 1 /* get negative error number in <errno.h> */ + +/* The following are so basic, all the *.c files get them automatically. */ +#include <minix/config.h> /* MUST be first */ +#include <ansi.h> /* MUST be second */ +#include <minix/type.h> +#include <minix/com.h> +#include <minix/callnr.h> +#include <sys/types.h> +#include <minix/const.h> +#include <minix/syslib.h> +#include <minix/utils.h> + +#include <string.h> +#include <limits.h> +#include <stddef.h> +#include <errno.h> + +#include <minix/partition.h> +#include <minix/u64.h> + +/* Info about and entry points into the device dependent code. */ +struct driver { + _PROTOTYPE( char *(*dr_name), (void) ); + _PROTOTYPE( int (*dr_open), (struct driver *dp, message *m_ptr) ); + _PROTOTYPE( int (*dr_close), (struct driver *dp, message *m_ptr) ); + _PROTOTYPE( int (*dr_ioctl), (struct driver *dp, message *m_ptr) ); + _PROTOTYPE( struct device *(*dr_prepare), (int device) ); + _PROTOTYPE( int (*dr_transfer), (int proc_nr, int opcode, off_t position, + iovec_t *iov, unsigned nr_req) ); + _PROTOTYPE( void (*dr_cleanup), (void) ); + _PROTOTYPE( void (*dr_geometry), (struct partition *entry) ); + _PROTOTYPE( void (*dr_stop), (struct driver *dp) ); + _PROTOTYPE( void (*dr_alarm), (struct driver *dp) ); + _PROTOTYPE( void (*dr_fkey), (struct driver *dp, message *m_ptr) ); +}; + +#if (CHIP == INTEL) + +/* Number of bytes you can DMA before hitting a 64K boundary: */ +#define dma_bytes_left(phys) \ + ((unsigned) (sizeof(int) == 2 ? 0 : 0x10000) - (unsigned) ((phys) & 0xFFFF)) + +#endif /* CHIP == INTEL */ + +/* Base and size of a partition in bytes. */ +struct device { + u64_t dv_base; + u64_t dv_size; +}; + +#define NIL_DEV ((struct device *) 0) + +/* Functions defined by driver.c: */ +_PROTOTYPE( void driver_task, (struct driver *dr) ); +_PROTOTYPE( char *no_name, (void) ); +_PROTOTYPE( int do_nop, (struct driver *dp, message *m_ptr) ); +_PROTOTYPE( struct device *nop_prepare, (int device) ); +_PROTOTYPE( void nop_cleanup, (void) ); +_PROTOTYPE( void nop_task, (void) ); +_PROTOTYPE( void nop_stop, (struct driver *dp) ); +_PROTOTYPE( void nop_alarm, (struct driver *dp) ); +_PROTOTYPE( void nop_stats, (struct driver *dp, message *m_ptr) ); +_PROTOTYPE( int do_diocntl, (struct driver *dp, message *m_ptr) ); + +/* Parameters for the disk drive. */ +#define SECTOR_SIZE 512 /* physical sector size in bytes */ +#define SECTOR_SHIFT 9 /* for division */ +#define SECTOR_MASK 511 /* and remainder */ + +/* Size of the DMA buffer buffer in bytes. */ +#define DMA_BUF_SIZE (DMA_SECTORS * SECTOR_SIZE) + +#if (CHIP == INTEL) +extern u8_t *tmp_buf; /* the DMA buffer */ +#else +extern u8_t tmp_buf[]; /* the DMA buffer */ +#endif +extern phys_bytes tmp_phys; /* phys address of DMA buffer */ diff --git a/drivers/libdriver/drvlib.c b/drivers/libdriver/drvlib.c new file mode 100644 index 000000000..0caa5104b --- /dev/null +++ b/drivers/libdriver/drvlib.c @@ -0,0 +1,268 @@ +/* IBM device driver utility functions. Author: Kees J. Bot + * 7 Dec 1995 + * Entry point: + * partition: partition a disk to the partition table(s) on it. + */ + +#include "driver.h" +#include "drvlib.h" + +/* Extended partition? */ +#define ext_part(s) ((s) == 0x05 || (s) == 0x0F) + +FORWARD _PROTOTYPE( void extpartition, (struct driver *dp, int extdev, + unsigned long extbase) ); +FORWARD _PROTOTYPE( int get_part_table, (struct driver *dp, int device, + unsigned long offset, struct part_entry *table) ); +FORWARD _PROTOTYPE( int get_iso_fake_part_table, (struct driver *dp, int device, + unsigned long offset, struct part_entry *table) ); +FORWARD _PROTOTYPE( void sort, (struct part_entry *table) ); + + +/*============================================================================* + * partition * + *============================================================================*/ +PUBLIC void partition(dp, device, style) +struct driver *dp; /* device dependent entry points */ +int device; /* device to partition */ +int style; /* partitioning style: floppy, primary, sub. */ +{ +/* This routine is called on first open to initialize the partition tables + * of a device. It makes sure that each partition falls safely within the + * device's limits. Depending on the partition style we are either making + * floppy partitions, primary partitions or subpartitions. Only primary + * partitions are sorted, because they are shared with other operating + * systems that expect this. + */ + struct part_entry table[NR_PARTITIONS], *pe; + int disk, par; + struct device *dv; + unsigned long base, limit, part_limit; + + /* Get the geometry of the device to partition */ + if ((dv = (*dp->dr_prepare)(device)) == NIL_DEV + || cmp64u(dv->dv_size, 0) == 0) return; + base = div64u(dv->dv_base, SECTOR_SIZE); + limit = base + div64u(dv->dv_size, SECTOR_SIZE); + + /* Read the partition table for the device. */ + if (!get_part_table(dp, device, 0L, table)) + if(!get_iso_fake_part_table(dp, device, 0L, table)) + return; + + /* Compute the device number of the first partition. */ + switch (style) { + case P_FLOPPY: + device += MINOR_fd0p0; + break; + case P_PRIMARY: + sort(table); /* sort a primary partition table */ + device += 1; + break; + case P_SUB: + disk = device / DEV_PER_DRIVE; + par = device % DEV_PER_DRIVE - 1; + device = MINOR_d0p0s0 + (disk * NR_PARTITIONS + par) * NR_PARTITIONS; + } + + /* Find an array of devices. */ + if ((dv = (*dp->dr_prepare)(device)) == NIL_DEV) return; + + /* Set the geometry of the partitions from the partition table. */ + for (par = 0; par < NR_PARTITIONS; par++, dv++) { + /* Shrink the partition to fit within the device. */ + pe = &table[par]; + part_limit = pe->lowsec + pe->size; + if (part_limit < pe->lowsec) part_limit = limit; + if (part_limit > limit) part_limit = limit; + if (pe->lowsec < base) pe->lowsec = base; + if (part_limit < pe->lowsec) part_limit = pe->lowsec; + + dv->dv_base = mul64u(pe->lowsec, SECTOR_SIZE); + dv->dv_size = mul64u(part_limit - pe->lowsec, SECTOR_SIZE); + + if (style == P_PRIMARY) { + /* Each Minix primary partition can be subpartitioned. */ + if (pe->sysind == MINIX_PART) + partition(dp, device + par, P_SUB); + + /* An extended partition has logical partitions. */ + if (ext_part(pe->sysind)) + extpartition(dp, device + par, pe->lowsec); + } + } +} + +/*============================================================================* + * extpartition * + *============================================================================*/ +PRIVATE void extpartition(dp, extdev, extbase) +struct driver *dp; /* device dependent entry points */ +int extdev; /* extended partition to scan */ +unsigned long extbase; /* sector offset of the base extended partition */ +{ +/* Extended partitions cannot be ignored alas, because people like to move + * files to and from DOS partitions. Avoid reading this code, it's no fun. + */ + struct part_entry table[NR_PARTITIONS], *pe; + int subdev, disk, par; + struct device *dv; + unsigned long offset, nextoffset; + + disk = extdev / DEV_PER_DRIVE; + par = extdev % DEV_PER_DRIVE - 1; + subdev = MINOR_d0p0s0 + (disk * NR_PARTITIONS + par) * NR_PARTITIONS; + + offset = 0; + do { + if (!get_part_table(dp, extdev, offset, table)) return; + sort(table); + + /* The table should contain one logical partition and optionally + * another extended partition. (It's a linked list.) + */ + nextoffset = 0; + for (par = 0; par < NR_PARTITIONS; par++) { + pe = &table[par]; + if (ext_part(pe->sysind)) { + nextoffset = pe->lowsec; + } else + if (pe->sysind != NO_PART) { + if ((dv = (*dp->dr_prepare)(subdev)) == NIL_DEV) return; + + dv->dv_base = mul64u(extbase + offset + pe->lowsec, + SECTOR_SIZE); + dv->dv_size = mul64u(pe->size, SECTOR_SIZE); + + /* Out of devices? */ + if (++subdev % NR_PARTITIONS == 0) return; + } + } + } while ((offset = nextoffset) != 0); +} + + +/*============================================================================* + * get_part_table * + *============================================================================*/ +PRIVATE int get_part_table(dp, device, offset, table) +struct driver *dp; +int device; +unsigned long offset; /* sector offset to the table */ +struct part_entry *table; /* four entries */ +{ +/* Read the partition table for the device, return true iff there were no + * errors. + */ + iovec_t iovec1; + off_t position; + static int proc_nr = NONE; + int s; + + /* Read the partition table at 'offset'. */ + if (proc_nr == NONE) { + if ((s=get_proc_nr(&proc_nr,NULL)) != OK) { + printf("%s: can't get own proc nr: %d\n", (*dp->dr_name)(), s); + return 0; + } + } + position = offset << SECTOR_SHIFT; + iovec1.iov_addr = (vir_bytes) tmp_buf; + iovec1.iov_size = SECTOR_SIZE; + if ((*dp->dr_prepare)(device) != NIL_DEV) { + (void) (*dp->dr_transfer)(proc_nr, DEV_GATHER, position, &iovec1, 1); + } + if (iovec1.iov_size != 0) { + printf("%s: can't read partition table\n", (*dp->dr_name)()); + return 0; + } + if (tmp_buf[510] != 0x55 || tmp_buf[511] != 0xAA) { + /* Invalid partition table. */ + return 0; + } + memcpy(table, (tmp_buf + PART_TABLE_OFF), NR_PARTITIONS * sizeof(table[0])); + return 1; +} + +/*============================================================================* + * get_iso_fake_part_table * + *============================================================================*/ +PRIVATE int get_iso_fake_part_table(dp, device, offset, table) +struct driver *dp; +int device; +unsigned long offset; /* sector offset to the table */ +struct part_entry *table; /* four entries */ +{ + iovec_t iovec1; + off_t position; + off_t isosize; + static int proc_nr = NONE; + int s; +#ifndef CD_SECTOR_SIZE +#define CD_SECTOR_SIZE 2048 +#endif + static unsigned char pvd[CD_SECTOR_SIZE]; + + /* Read the partition table at 'offset'. */ + if (proc_nr == NONE) { + if ((s=sys_getprocnr(&proc_nr,0,0)) != OK) { + printf("%s: can't get own proc nr: %d\n", (*dp->dr_name)(), s); + return 0; + } + } + + position = 16*CD_SECTOR_SIZE; + iovec1.iov_addr = (vir_bytes) pvd; + iovec1.iov_size = CD_SECTOR_SIZE; + if ((*dp->dr_prepare)(device) != NIL_DEV) { + (void) (*dp->dr_transfer)(proc_nr, DEV_GATHER, position, &iovec1, 1); + } + if (iovec1.iov_size != 0) { + return 0; + } + if (pvd[0] != 1 || pvd[1] != 'C' || pvd[2] != 'D' || pvd[3] != '0' || + pvd[4] != '0' || pvd[5] != '1' || pvd[6] != 1) { + /* Invalid primary volume descriptor. */ + return 0; + } + memcpy(&isosize, pvd + 80, sizeof(isosize)); + isosize *= CD_SECTOR_SIZE; + +#define ROOT_IMAGE_SECTORS (1440*1024/SECTOR_SIZE) + table[0].lowsec = 0; + table[0].size = isosize / SECTOR_SIZE; + + table[1].lowsec = table[0].size; + table[1].size = ROOT_IMAGE_SECTORS; + + /* XXX figure out real size */ + table[2].lowsec = table[1].lowsec + table[1].size; + table[2].size = 500*1024*1024/SECTOR_SIZE; + + table[0].sysind = table[1].sysind = table[2].sysind = MINIX_PART; + table[3].sysind = NO_PART; + + return 1; +} + + +/*===========================================================================* + * sort * + *===========================================================================*/ +PRIVATE void sort(table) +struct part_entry *table; +{ +/* Sort a partition table. */ + struct part_entry *pe, tmp; + int n = NR_PARTITIONS; + + do { + for (pe = table; pe < table + NR_PARTITIONS-1; pe++) { + if (pe[0].sysind == NO_PART + || (pe[0].lowsec > pe[1].lowsec + && pe[1].sysind != NO_PART)) { + tmp = pe[0]; pe[0] = pe[1]; pe[1] = tmp; + } + } + } while (--n > 0); +} diff --git a/drivers/libdriver/drvlib.h b/drivers/libdriver/drvlib.h new file mode 100644 index 000000000..cd234a975 --- /dev/null +++ b/drivers/libdriver/drvlib.h @@ -0,0 +1,27 @@ +/* IBM device driver definitions Author: Kees J. Bot + * 7 Dec 1995 + */ + +#include <ibm/partition.h> + +_PROTOTYPE( void partition, (struct driver *dr, int device, int style) ); + +/* BIOS parameter table layout. */ +#define bp_cylinders(t) (* (u16_t *) (&(t)[0])) +#define bp_heads(t) (* (u8_t *) (&(t)[2])) +#define bp_reduced_wr(t) (* (u16_t *) (&(t)[3])) +#define bp_precomp(t) (* (u16_t *) (&(t)[5])) +#define bp_max_ecc(t) (* (u8_t *) (&(t)[7])) +#define bp_ctlbyte(t) (* (u8_t *) (&(t)[8])) +#define bp_landingzone(t) (* (u16_t *) (&(t)[12])) +#define bp_sectors(t) (* (u8_t *) (&(t)[14])) + +/* Miscellaneous. */ +#define DEV_PER_DRIVE (1 + NR_PARTITIONS) +#define MINOR_t0 64 +#define MINOR_r0 120 +#define MINOR_d0p0s0 128 +#define MINOR_fd0p0 (28<<2) +#define P_FLOPPY 0 +#define P_PRIMARY 1 +#define P_SUB 2 diff --git a/drivers/libpci/pci.c b/drivers/libpci/pci.c new file mode 100644 index 000000000..31c1a3ea3 --- /dev/null +++ b/drivers/libpci/pci.c @@ -0,0 +1,1223 @@ +/* +pci.c + +Configure devices on the PCI bus + +Created: Jan 2000 by Philip Homburg <philip@cs.vu.nl> +*/ + +#include "../../kernel/kernel.h" + +#include "../../kernel/assert.h" +#include "pci.h" +#include "pci_amd.h" +#include "pci_intel.h" +#include "pci_sis.h" +#include "pci_via.h" +#if __minix_vmd +#include "../../kernel/config.h" +#endif + +#if ENABLE_PCI + +#if !__minix_vmd +#define debug 0 +#define irq_mode_pci(irq) ((void)0) +#endif + +INIT_ASSERT + +#define NR_PCIBUS 2 +#define NR_PCIDEV 20 + +#define PBT_INTEL 1 +#define PBT_PCIBRIDGE 2 + +PRIVATE struct pcibus +{ + int pb_type; + int pb_isabridge_dev; + int pb_isabridge_type; + + int pb_devind; + int pb_bus; + u8_t (*pb_rreg8)(int busind, int devind, int port); + u16_t (*pb_rreg16)(int busind, int devind, int port); + u32_t (*pb_rreg32)(int busind, int devind, int port); + void (*pb_wreg16)(int busind, int devind, int port, U16_t value); + void (*pb_wreg32)(int busind, int devind, int port, u32_t value); + u16_t (*pb_rsts)(int busind); + void (*pb_wsts)(int busind, U16_t value); +} pcibus[NR_PCIBUS]; +PRIVATE int nr_pcibus= 0; + +PRIVATE struct pcidev +{ + u8_t pd_busind; + u8_t pd_dev; + u8_t pd_func; + u8_t pd_baseclass; + u8_t pd_subclass; + u8_t pd_infclass; + u16_t pd_vid; + u16_t pd_did; + u8_t pd_inuse; +} pcidev[NR_PCIDEV]; +PRIVATE int nr_pcidev= 0; + +FORWARD _PROTOTYPE( void pci_intel_init, (void) ); +FORWARD _PROTOTYPE( void probe_bus, (int busind) ); +FORWARD _PROTOTYPE( int do_isabridge, (int busind) ); +FORWARD _PROTOTYPE( void do_pcibridge, (int busind) ); +FORWARD _PROTOTYPE( int do_piix, (int devind) ); +FORWARD _PROTOTYPE( int do_amd_isabr, (int devind) ); +FORWARD _PROTOTYPE( int do_sis_isabr, (int devind) ); +FORWARD _PROTOTYPE( int do_via_isabr, (int devind) ); +FORWARD _PROTOTYPE( char *pci_vid_name, (U16_t vid) ); +FORWARD _PROTOTYPE( char *pci_baseclass_name, (U8_t baseclass) ); +FORWARD _PROTOTYPE( char *pci_subclass_name, (U8_t baseclass, + U8_t subclass, U8_t infclass) ); +FORWARD _PROTOTYPE( void ntostr, (unsigned n, char **str, char *end) ); +FORWARD _PROTOTYPE( u16_t pci_attr_rsts, (int devind) ); +FORWARD _PROTOTYPE( void pci_attr_wsts, (int devind, U16_t value) ); +FORWARD _PROTOTYPE( u16_t pcibr_intel_rsts, (int busind) ); +FORWARD _PROTOTYPE( void pcibr_intel_wsts, (int busind, U16_t value) ); +FORWARD _PROTOTYPE( u16_t pcibr_via_rsts, (int busind) ); +FORWARD _PROTOTYPE( void pcibr_via_wsts, (int busind, U16_t value) ); +FORWARD _PROTOTYPE( u8_t pcii_rreg8, (int busind, int devind, int port) ); +FORWARD _PROTOTYPE( u16_t pcii_rreg16, (int busind, int devind, + int port) ); +FORWARD _PROTOTYPE( u32_t pcii_rreg32, (int busind, int devind, + int port) ); +FORWARD _PROTOTYPE( void pcii_wreg16, (int busind, int devind, int port, + U16_t value) ); +FORWARD _PROTOTYPE( void pcii_wreg32, (int busind, int devind, int port, + u32_t value) ); +FORWARD _PROTOTYPE( u16_t pcii_rsts, (int busind) ); +FORWARD _PROTOTYPE( void pcii_wsts, (int busind, U16_t value) ); + +/*===========================================================================* + * pci_init * + *===========================================================================*/ +PUBLIC void pci_init() +{ + static int first_time= 1; + + if (!first_time) + return; + + /* We don't expect to interrupted */ + assert(first_time == 1); + first_time= -1; + + /* Only Intel (compatible) PCI controllers are supported at the + * moment. + */ + pci_intel_init(); + + first_time= 0; +} + +/*===========================================================================* + * pci_find_dev * + *===========================================================================*/ +PUBLIC int pci_find_dev(bus, dev, func, devindp) +u8_t bus; +u8_t dev; +u8_t func; +int *devindp; +{ + int devind; + + for (devind= 0; devind < nr_pcidev; devind++) + { + if (pcidev[devind].pd_busind == bus && + pcidev[devind].pd_dev == dev && + pcidev[devind].pd_func == func) + { + break; + } + } + if (devind >= nr_pcidev) + return 0; + if (pcidev[devind].pd_inuse) + return 0; + *devindp= devind; + return 1; +} + + +/*===========================================================================* + * pci_first_dev * + *===========================================================================*/ +PUBLIC int pci_first_dev(devindp, vidp, didp) +int *devindp; +u16_t *vidp; +u16_t *didp; +{ + int devind; + + for (devind= 0; devind < nr_pcidev; devind++) + { + if (!pcidev[devind].pd_inuse) + break; + } + if (devind >= nr_pcidev) + return 0; + *devindp= devind; + *vidp= pcidev[devind].pd_vid; + *didp= pcidev[devind].pd_did; + return 1; +} + + +/*===========================================================================* + * pci_next_dev * + *===========================================================================*/ +PUBLIC int pci_next_dev(devindp, vidp, didp) +int *devindp; +u16_t *vidp; +u16_t *didp; +{ + int devind; + + for (devind= *devindp+1; devind < nr_pcidev; devind++) + { + if (!pcidev[devind].pd_inuse) + break; + } + if (devind >= nr_pcidev) + return 0; + *devindp= devind; + *vidp= pcidev[devind].pd_vid; + *didp= pcidev[devind].pd_did; + return 1; +} + + +/*===========================================================================* + * pci_reserve * + *===========================================================================*/ +PUBLIC void pci_reserve(devind) +int devind; +{ + assert(devind <= nr_pcidev); + assert(!pcidev[devind].pd_inuse); + pcidev[devind].pd_inuse= 1; +} + + +/*===========================================================================* + * pci_ids * + *===========================================================================*/ +PUBLIC void pci_ids(devind, vidp, didp) +int devind; +u16_t *vidp; +u16_t *didp; +{ + assert(devind <= nr_pcidev); + *vidp= pcidev[devind].pd_vid; + *didp= pcidev[devind].pd_did; +} + + +/*===========================================================================* + * pci_slot_name * + *===========================================================================*/ +PUBLIC char *pci_slot_name(devind) +int devind; +{ + static char label[]= "ddd.ddd.ddd"; + char *end; + char *p; + + p= label; + end= label+sizeof(label); + + ntostr(pcidev[devind].pd_busind, &p, end); + *p++= '.'; + + ntostr(pcidev[devind].pd_dev, &p, end); + *p++= '.'; + + ntostr(pcidev[devind].pd_func, &p, end); + + return label; +} + +/*===========================================================================* + * pci_dev_name * + *===========================================================================*/ +PUBLIC char *pci_dev_name(vid, did) +u16_t vid; +u16_t did; +{ + int i; + + for (i= 0; pci_device_table[i].name; i++) + { + if (pci_device_table[i].vid == vid && + pci_device_table[i].did == did) + { + return pci_device_table[i].name; + } + } + return NULL; +} + + +/*===========================================================================* + * pci_attr_r8 * + *===========================================================================*/ +PUBLIC u8_t pci_attr_r8(devind, port) +int devind; +int port; +{ + int busind; + + busind= pcidev[devind].pd_busind; + return pcibus[busind].pb_rreg8(busind, devind, port); +} + + +/*===========================================================================* + * pci_attr_r16 * + *===========================================================================*/ +PUBLIC u16_t pci_attr_r16(devind, port) +int devind; +int port; +{ + int busind; + + busind= pcidev[devind].pd_busind; + return pcibus[busind].pb_rreg16(busind, devind, port); +} + + +/*===========================================================================* + * pci_attr_r32 * + *===========================================================================*/ +PUBLIC u32_t pci_attr_r32(devind, port) +int devind; +int port; +{ + int busind; + + busind= pcidev[devind].pd_busind; + return pcibus[busind].pb_rreg32(busind, devind, port); +} + + +/*===========================================================================* + * pci_attr_w16 * + *===========================================================================*/ +PUBLIC void pci_attr_w16(devind, port, value) +int devind; +int port; +u16_t value; +{ + int busind; + + busind= pcidev[devind].pd_busind; + pcibus[busind].pb_wreg16(busind, devind, port, value); +} + + +/*===========================================================================* + * pci_attr_w32 * + *===========================================================================*/ +PUBLIC void pci_attr_w32(devind, port, value) +int devind; +int port; +u32_t value; +{ + int busind; + + busind= pcidev[devind].pd_busind; + pcibus[busind].pb_wreg32(busind, devind, port, value); +} + + +/*===========================================================================* + * pci_intel_init * + *===========================================================================*/ +PRIVATE void pci_intel_init() +{ + /* Try to detect a know PCI controller. Read the Vendor ID and + * the Device ID for function 0 of device 0. + * Two times the value 0xffff suggests a system without a (compatible) + * PCI controller. Only controllers with values listed in the table + * pci_intel_ctrl are actually used. + */ + u32_t bus, dev, func; + u16_t vid, did; + int i, r, busind; + char *dstr; + + bus= 0; + dev= 0; + func= 0; + + vid= PCII_RREG16_(bus, dev, func, PCI_VID); + did= PCII_RREG16_(bus, dev, func, PCI_DID); + outl(PCII_CONFADD, PCII_UNSEL); + + if (vid == 0xffff && did == 0xffff) + return; /* Nothing here */ + + for (i= 0; pci_intel_ctrl[i].vid; i++) + { + if (pci_intel_ctrl[i].vid == vid && + pci_intel_ctrl[i].did == did) + { + break; + } + } + + if (!pci_intel_ctrl[i].vid) + { + printf("pci_intel_init: unknown PCI-controller:\n" + "\tvendor %04X (%s), device %04X\n", + vid, pci_vid_name(vid), did); + return; + } + + if (nr_pcibus >= NR_PCIBUS) + panic("too many PCI busses", nr_pcibus); + busind= nr_pcibus; + nr_pcibus++; + pcibus[busind].pb_type= PBT_INTEL; + pcibus[busind].pb_isabridge_dev= -1; + pcibus[busind].pb_isabridge_type= 0; + pcibus[busind].pb_devind= -1; + pcibus[busind].pb_bus= 0; + pcibus[busind].pb_rreg8= pcii_rreg8; + pcibus[busind].pb_rreg16= pcii_rreg16; + pcibus[busind].pb_rreg32= pcii_rreg32; + pcibus[busind].pb_wreg16= pcii_wreg16; + pcibus[busind].pb_wreg32= pcii_wreg32; + pcibus[busind].pb_rsts= pcii_rsts; + pcibus[busind].pb_wsts= pcii_wsts; + + dstr= pci_dev_name(vid, did); + if (!dstr) + dstr= "unknown device"; + if (debug) + { + printf("pci_intel_init: %s (%04X/%04X)\n", + dstr, vid, did); + } + + probe_bus(busind); + + r= do_isabridge(busind); + if (r != OK) + { + /* Disable all devices for this bus */ + for (i= 0; i<nr_pcidev; i++) + { + if (pcidev[i].pd_busind != busind) + continue; + pcidev[i].pd_inuse= 1; + } + return; + } + + /* Look for PCI bridges (for AGP) */ + do_pcibridge(busind); +} + + +/*===========================================================================* + * probe_bus * + *===========================================================================*/ +PRIVATE void probe_bus(busind) +int busind; +{ + u32_t dev, func; + u16_t vid, did, sts; + u8_t headt; + u8_t baseclass, subclass, infclass; + int devind; + char *s, *dstr; + +#if DEBUG +printf("probe_bus(%d)\n", busind); +#endif + if (nr_pcidev >= NR_PCIDEV) + panic("too many PCI devices", nr_pcidev); + devind= nr_pcidev; + + for (dev= 0; dev<32; dev++) + { + + for (func= 0; func < 8; func++) + { + pcidev[devind].pd_busind= busind; + pcidev[devind].pd_dev= dev; + pcidev[devind].pd_func= func; + + pci_attr_wsts(devind, + PSR_SSE|PSR_RMAS|PSR_RTAS); + vid= pci_attr_r16(devind, PCI_VID); + did= pci_attr_r16(devind, PCI_DID); + headt= pci_attr_r8(devind, PCI_HEADT); + sts= pci_attr_rsts(devind); + if (sts & (PSR_SSE|PSR_RMAS|PSR_RTAS)) + break; + if (vid == NO_VID) + { + /* Some bridge implementations do support + * pci_attr_rsts. + */ + break; + } + + dstr= pci_dev_name(vid, did); + if (debug) + { + if (dstr) + { + printf("%d.%lu.%lu: %s (%04X/%04X)\n", + busind, (unsigned long)dev, + (unsigned long)func, dstr, + vid, did); + } + else + { + printf( + "%d.%lu.%lu: Unknown device, vendor %04X (%s), device %04X\n", + busind, (unsigned long)dev, + (unsigned long)func, vid, + pci_vid_name(vid), did); + } + } + + baseclass= pci_attr_r8(devind, PCI_BCR); + subclass= pci_attr_r8(devind, PCI_SCR); + infclass= pci_attr_r8(devind, PCI_PIFR); + s= pci_subclass_name(baseclass, subclass, infclass); + if (!s) + s= pci_baseclass_name(baseclass); + { + if (!s) + s= "(unknown class)"; + } + if (debug) + { + printf("\tclass %s (%X/%X/%X)\n", s, + baseclass, subclass, infclass); + } + + devind= nr_pcidev; + nr_pcidev++; + pcidev[devind].pd_baseclass= baseclass; + pcidev[devind].pd_subclass= subclass; + pcidev[devind].pd_infclass= infclass; + pcidev[devind].pd_vid= vid; + pcidev[devind].pd_did= did; + pcidev[devind].pd_inuse= 0; + + if (nr_pcidev >= NR_PCIDEV) + panic("too many PCI devices", nr_pcidev); + devind= nr_pcidev; + + if (func == 0 && !(headt & PHT_MULTIFUNC)) + break; + } + } +} + + +/*===========================================================================* + * do_isabridge * + *===========================================================================*/ +PRIVATE int do_isabridge(busind) +int busind; +{ + int unknown_bridge= -1; + int bridge_dev= -1; + int i, j, r, type; + u16_t vid, did; + char *dstr; + + j= 0; /* lint */ + vid= did= 0; /* lint */ + for (i= 0; i< nr_pcidev; i++) + { + if (pcidev[i].pd_busind != busind) + continue; + if (pcidev[i].pd_baseclass == 0x06 && + pcidev[i].pd_subclass == 0x01 && + pcidev[i].pd_infclass == 0x00) + { + /* ISA bridge. Report if no supported bridge is + * found. + */ + unknown_bridge= i; + } + + vid= pcidev[i].pd_vid; + did= pcidev[i].pd_did; + for (j= 0; pci_isabridge[j].vid != 0; j++) + { + if (pci_isabridge[j].vid != vid) + continue; + if (pci_isabridge[j].did != did) + continue; + if (pci_isabridge[j].checkclass && + unknown_bridge != i) + { + /* This part of multifunction device is + * not the bridge. + */ + continue; + } + break; + } + if (pci_isabridge[j].vid) + { + bridge_dev= i; + break; + } + } + + if (bridge_dev != -1) + { + dstr= pci_dev_name(vid, did); + if (!dstr) + dstr= "unknown device"; + if (debug) + { + printf("found ISA bridge (%04X/%04X) %s\n", + vid, did, dstr); + } + pcibus[busind].pb_isabridge_dev= bridge_dev; + type= pci_isabridge[j].type; + pcibus[busind].pb_isabridge_type= type; + switch(type) + { + case PCI_IB_PIIX: + r= do_piix(bridge_dev); + break; + case PCI_IB_VIA: + r= do_via_isabr(bridge_dev); + break; + case PCI_IB_AMD: + r= do_amd_isabr(bridge_dev); + break; + case PCI_IB_SIS: + r= do_sis_isabr(bridge_dev); + break; + default: + panic("unknown ISA bridge type", type); + } + return r; + } + + if (unknown_bridge == -1) + { + printf("do_isabridge: no ISA bridge found for bus %d", busind); + return -1; + } + printf("Unsupported ISA bridge %04X/%04X for bus %d\n", + pcidev[unknown_bridge].pd_vid, + pcidev[unknown_bridge].pd_did, + busind); + return -1; +} + + +/*===========================================================================* + * do_pcibridge * + *===========================================================================*/ +PRIVATE void do_pcibridge(busind) +int busind; +{ + int devind, i; + int ind, type; + u16_t vid, did; + u8_t sbusn; + + vid= did= 0; /* lint */ + for (devind= 0; devind< nr_pcidev; devind++) + { + if (pcidev[devind].pd_busind != busind) + continue; + + vid= pcidev[devind].pd_vid; + did= pcidev[devind].pd_did; + for (i= 0; pci_pcibridge[i].vid != 0; i++) + { + if (pci_pcibridge[i].vid != vid) + continue; + if (pci_pcibridge[i].did != did) + continue; + break; + } + if (pci_pcibridge[i].vid == 0) + continue; + type= pci_pcibridge[i].type; + + if (debug) + printf("PCI-to-PCI bridge: %04X/%04X\n", vid, did); + + /* Assume that the BIOS initialized the secondary bus + * number. + */ + sbusn= pci_attr_r8(devind, PPB_SBUSN); +#if DEBUG + printf("sbusn = %d\n", sbusn); +#endif + + if (nr_pcibus >= NR_PCIBUS) + panic("too many PCI busses", nr_pcibus); + ind= nr_pcibus; + nr_pcibus++; + pcibus[ind].pb_type= PBT_PCIBRIDGE; + pcibus[ind].pb_isabridge_dev= -1; + pcibus[ind].pb_isabridge_type= 0; + pcibus[ind].pb_devind= devind; + pcibus[ind].pb_bus= sbusn; + pcibus[ind].pb_rreg8= pcibus[busind].pb_rreg8; + pcibus[ind].pb_rreg16= pcibus[busind].pb_rreg16; + pcibus[ind].pb_rreg32= pcibus[busind].pb_rreg32; + pcibus[ind].pb_wreg16= pcibus[busind].pb_wreg16; + pcibus[ind].pb_wreg32= pcibus[busind].pb_wreg32; + switch(type) + { + case PCI_AGPB_INTEL: + pcibus[ind].pb_rsts= pcibr_intel_rsts; + pcibus[ind].pb_wsts= pcibr_intel_wsts; + break; + case PCI_AGPB_VIA: + pcibus[ind].pb_rsts= pcibr_via_rsts; + pcibus[ind].pb_wsts= pcibr_via_wsts; + break; + default: + panic("unknown PCI-PCI bridge type", type); + } + + probe_bus(ind); + } +} + + +/*===========================================================================* + * do_piix * + *===========================================================================*/ +PRIVATE int do_piix(devind) +int devind; +{ + int i, dev, func, irqrc, irq; + u16_t elcr1, elcr2, elcr; + +#if DEBUG + printf("in piix\n"); +#endif + dev= pcidev[devind].pd_dev; + func= pcidev[devind].pd_func; + elcr1= inb(PIIX_ELCR1); + elcr2= inb(PIIX_ELCR2); + elcr= elcr1 | (elcr2 << 8); + for (i= 0; i<4; i++) + { + irqrc= pci_attr_r8(devind, PIIX_PIRQRCA+i); + if (irqrc & PIIX_IRQ_DI) + { + if (debug) + printf("INT%c: disabled\n", 'A'+i); + } + else + { + irq= irqrc & PIIX_IRQ_MASK; + if (debug) + printf("INT%c: %d\n", 'A'+i, irq); + if (!(elcr & (1 << irq))) + { + printf("IRQ %d is not level triggered\n", + irq); + panic(NULL, NO_NUM); + } + irq_mode_pci(irq); + } + } + return 0; +} + +/*===========================================================================* + * do_amd_isabr * + *===========================================================================*/ +PRIVATE int do_amd_isabr(devind) +int devind; +{ + int i, bus, dev, func, xdevind, irq, edge; + u8_t levmask; + u16_t pciirq; + + /* Find required function */ + func= AMD_ISABR_FUNC; + bus= pcidev[devind].pd_busind; + dev= pcidev[devind].pd_dev; + + /* Fake a device with the required function */ + if (nr_pcidev >= NR_PCIDEV) + panic("too many PCI devices", nr_pcidev); + xdevind= nr_pcidev; + pcidev[xdevind].pd_busind= bus; + pcidev[xdevind].pd_dev= dev; + pcidev[xdevind].pd_func= func; + pcidev[xdevind].pd_inuse= 1; + nr_pcidev++; + + levmask= pci_attr_r8(xdevind, AMD_ISABR_PCIIRQ_LEV); + pciirq= pci_attr_r16(xdevind, AMD_ISABR_PCIIRQ_ROUTE); + for (i= 0; i<4; i++) + { + edge= (levmask >> i) & 1; + irq= (pciirq >> (4*i)) & 0xf; + if (!irq) + { + if (debug) + printf("INT%c: disabled\n", 'A'+i); + } + else + { + if (debug) + printf("INT%c: %d\n", 'A'+i, irq); + if (edge) + { + printf("IRQ %d is not level triggered\n", + irq); + panic(NULL, NO_NUM); + } + irq_mode_pci(irq); + } + } + nr_pcidev--; + return 0; +} + + +/*===========================================================================* + * do_sis_isabr * + *===========================================================================*/ +PRIVATE int do_sis_isabr(devind) +int devind; +{ + int i, dev, func, irq; + + dev= pcidev[devind].pd_dev; + func= pcidev[devind].pd_func; + irq= 0; /* lint */ + for (i= 0; i<4; i++) + { + irq= pci_attr_r8(devind, SIS_ISABR_IRQ_A+i); + if (irq & SIS_IRQ_DISABLED) + { + if (debug) + printf("INT%c: disabled\n", 'A'+i); + } + else + { + irq &= SIS_IRQ_MASK; + if (debug) + printf("INT%c: %d\n", 'A'+i, irq); + irq_mode_pci(irq); + } + } + return 0; +} + + +/*===========================================================================* + * do_via_isabr * + *===========================================================================*/ +PRIVATE int do_via_isabr(devind) +int devind; +{ + int i, dev, func, irq, edge; + u8_t levmask; + + dev= pcidev[devind].pd_dev; + func= pcidev[devind].pd_func; + levmask= pci_attr_r8(devind, VIA_ISABR_EL); + irq= 0; /* lint */ + edge= 0; /* lint */ + for (i= 0; i<4; i++) + { + switch(i) + { + case 0: + edge= (levmask & VIA_ISABR_EL_INTA); + irq= pci_attr_r8(devind, VIA_ISABR_IRQ_R2) >> 4; + break; + case 1: + edge= (levmask & VIA_ISABR_EL_INTB); + irq= pci_attr_r8(devind, VIA_ISABR_IRQ_R2); + break; + case 2: + edge= (levmask & VIA_ISABR_EL_INTC); + irq= pci_attr_r8(devind, VIA_ISABR_IRQ_R3) >> 4; + break; + case 3: + edge= (levmask & VIA_ISABR_EL_INTD); + irq= pci_attr_r8(devind, VIA_ISABR_IRQ_R1) >> 4; + break; + default: + assert(0); + } + irq &= 0xf; + if (!irq) + { + if (debug) + printf("INT%c: disabled\n", 'A'+i); + } + else + { + if (debug) + printf("INT%c: %d\n", 'A'+i, irq); + if (edge) + { + printf("IRQ %d is not level triggered\n", + irq); + panic(NULL, NO_NUM); + } + irq_mode_pci(irq); + } + } + return 0; +} + + +/*===========================================================================* + * pci_vid_name * + *===========================================================================*/ +PRIVATE char *pci_vid_name(vid) +u16_t vid; +{ + int i; + + for (i= 0; pci_vendor_table[i].name; i++) + { + if (pci_vendor_table[i].vid == vid) + return pci_vendor_table[i].name; + } + return "unknown"; +} + +/*===========================================================================* + * pci_baseclass_name * + *===========================================================================*/ +PRIVATE char *pci_baseclass_name(baseclass) +u8_t baseclass; +{ + int i; + + for (i= 0; pci_baseclass_table[i].name; i++) + { + if (pci_baseclass_table[i].baseclass == baseclass) + return pci_baseclass_table[i].name; + } + return NULL; +} + +/*===========================================================================* + * pci_subclass_name * + *===========================================================================*/ +PRIVATE char *pci_subclass_name(baseclass, subclass, infclass) +u8_t baseclass; +u8_t subclass; +u8_t infclass; +{ + int i; + + for (i= 0; pci_subclass_table[i].name; i++) + { + if (pci_subclass_table[i].baseclass != baseclass) + continue; + if (pci_subclass_table[i].subclass != subclass) + continue; + if (pci_subclass_table[i].infclass != infclass && + pci_subclass_table[i].infclass != (u16_t)-1) + { + continue; + } + return pci_subclass_table[i].name; + } + return NULL; +} + +/*===========================================================================* + * ntostr * + *===========================================================================*/ +PRIVATE void ntostr(n, str, end) +unsigned n; +char **str; +char *end; +{ + char tmpstr[20]; + int i; + + if (n == 0) + { + tmpstr[0]= '0'; + i= 1; + } + else + { + for (i= 0; n; i++) + { + tmpstr[i]= '0' + (n%10); + n /= 10; + } + } + for (; i>0; i--) + { + if (*str == end) + { + break; + } + **str= tmpstr[i-1]; + (*str)++; + } + if (*str == end) + end[-1]= '\0'; + else + **str= '\0'; +} + + +/*===========================================================================* + * pci_attr_rsts * + *===========================================================================*/ +PRIVATE u16_t pci_attr_rsts(devind) +int devind; +{ + int busind; + + busind= pcidev[devind].pd_busind; + return pcibus[busind].pb_rsts(busind); +} + + +/*===========================================================================* + * pcibr_intel_rsts * + *===========================================================================*/ +PRIVATE u16_t pcibr_intel_rsts(busind) +int busind; +{ + int devind; + devind= pcibus[busind].pb_devind; + + return pci_attr_r16(devind, PPB_SSTS); +} + + +/*===========================================================================* + * pcibr_intel_wsts * + *===========================================================================*/ +PRIVATE void pcibr_intel_wsts(busind, value) +int busind; +u16_t value; +{ + int devind; + devind= pcibus[busind].pb_devind; + +#if 0 + printf("pcibr_intel_wsts(%d, 0x%X), devind= %d\n", + busind, value, devind); +#endif + pci_attr_w16(devind, PPB_SSTS, value); +} + + +/*===========================================================================* + * pcibr_via_rsts * + *===========================================================================*/ +PRIVATE u16_t pcibr_via_rsts(busind) +int busind; +{ + int devind; + devind= pcibus[busind].pb_devind; + + return 0; +} + + +/*===========================================================================* + * pcibr_via_wsts * + *===========================================================================*/ +PRIVATE void pcibr_via_wsts(busind, value) +int busind; +u16_t value; +{ + int devind; + devind= pcibus[busind].pb_devind; + +#if 0 + printf("pcibr_via_wsts(%d, 0x%X), devind= %d (not implemented)\n", + busind, value, devind); +#endif +} + + +/*===========================================================================* + * pci_attr_wsts * + *===========================================================================*/ +PRIVATE void pci_attr_wsts(devind, value) +int devind; +u16_t value; +{ + int busind; + + busind= pcidev[devind].pd_busind; + pcibus[busind].pb_wsts(busind, value); +} + + +/*===========================================================================* + * pcii_rreg8 * + *===========================================================================*/ +PRIVATE u8_t pcii_rreg8(busind, devind, port) +int busind; +int devind; +int port; +{ + u8_t v; + + + v= PCII_RREG8_(pcibus[busind].pb_bus, + pcidev[devind].pd_dev, pcidev[devind].pd_func, + port); + outl(PCII_CONFADD, PCII_UNSEL); +#if 0 + printf("pcii_rreg8(%d, %d, 0x%X): %d.%d.%d= 0x%X\n", + busind, devind, port, + pcibus[busind].pb_bus, pcidev[devind].pd_dev, + pcidev[devind].pd_func, v); +#endif + return v; +} + + +/*===========================================================================* + * pcii_rreg16 * + *===========================================================================*/ +PRIVATE u16_t pcii_rreg16(busind, devind, port) +int busind; +int devind; +int port; +{ + u16_t v; + + v= PCII_RREG16_(pcibus[busind].pb_bus, + pcidev[devind].pd_dev, pcidev[devind].pd_func, + port); + outl(PCII_CONFADD, PCII_UNSEL); +#if 0 + printf("pcii_rreg16(%d, %d, 0x%X): %d.%d.%d= 0x%X\n", + busind, devind, port, + pcibus[busind].pb_bus, pcidev[devind].pd_dev, + pcidev[devind].pd_func, v); +#endif + return v; +} + + +/*===========================================================================* + * pcii_rreg32 * + *===========================================================================*/ +PRIVATE u32_t pcii_rreg32(busind, devind, port) +int busind; +int devind; +int port; +{ + u32_t v; + + v= PCII_RREG32_(pcibus[busind].pb_bus, + pcidev[devind].pd_dev, pcidev[devind].pd_func, + port); + outl(PCII_CONFADD, PCII_UNSEL); +#if 0 + printf("pcii_rreg32(%d, %d, 0x%X): %d.%d.%d= 0x%X\n", + busind, devind, port, + pcibus[busind].pb_bus, pcidev[devind].pd_dev, + pcidev[devind].pd_func, v); +#endif + return v; +} + + +/*===========================================================================* + * pcii_wreg16 * + *===========================================================================*/ +PRIVATE void pcii_wreg16(busind, devind, port, value) +int busind; +int devind; +int port; +u16_t value; +{ +#if 0 + printf("pcii_wreg16(%d, %d, 0x%X, 0x%X): %d.%d.%d\n", + busind, devind, port, value, + pcibus[busind].pb_bus, pcidev[devind].pd_dev, + pcidev[devind].pd_func); +#endif + PCII_WREG16_(pcibus[busind].pb_bus, + pcidev[devind].pd_dev, pcidev[devind].pd_func, + port, value); + outl(PCII_CONFADD, PCII_UNSEL); +} + + +/*===========================================================================* + * pcii_wreg32 * + *===========================================================================*/ +PRIVATE void pcii_wreg32(busind, devind, port, value) +int busind; +int devind; +int port; +u32_t value; +{ +#if 0 + printf("pcii_wreg32(%d, %d, 0x%X, 0x%X): %d.%d.%d\n", + busind, devind, port, value, + pcibus[busind].pb_bus, pcidev[devind].pd_dev, + pcidev[devind].pd_func); +#endif + PCII_WREG32_(pcibus[busind].pb_bus, + pcidev[devind].pd_dev, pcidev[devind].pd_func, + port, value); + outl(PCII_CONFADD, PCII_UNSEL); +} + + +/*===========================================================================* + * pcii_rsts * + *===========================================================================*/ +PRIVATE u16_t pcii_rsts(busind) +int busind; +{ + u16_t v; + v= PCII_RREG16_(pcibus[busind].pb_bus, 0, 0, PCI_PCISTS); + outl(PCII_CONFADD, PCII_UNSEL); + return v; +} + + +/*===========================================================================* + * pcii_wsts * + *===========================================================================*/ +PRIVATE void pcii_wsts(busind, value) +int busind; +u16_t value; +{ + PCII_WREG16_(pcibus[busind].pb_bus, 0, 0, PCI_PCISTS, value); + outl(PCII_CONFADD, PCII_UNSEL); +} +#endif /* ENABLE_PCI */ + +/* + * $PchId: pci.c,v 1.7 2003/08/07 09:06:51 philip Exp $ + */ diff --git a/drivers/libpci/pci.h b/drivers/libpci/pci.h new file mode 100644 index 000000000..16865d949 --- /dev/null +++ b/drivers/libpci/pci.h @@ -0,0 +1,97 @@ +/* +pci.h + +Created: Jan 2000 by Philip Homburg <philip@cs.vu.nl> +*/ + +#define PCI_VID 0x00 /* Vendor ID, 16-bit */ +#define PCI_DID 0x02 /* Device ID, 16-bit */ +#define PCI_CR 0x04 /* Command Register, 16-bit */ +#define PCI_PCISTS 0x06 /* PCI status, 16-bit */ +#define PSR_SSE 0x4000 /* Signaled System Error */ +#define PSR_RMAS 0x2000 /* Received Master Abort Status */ +#define PSR_RTAS 0x1000 /* Received Target Abort Status */ +#define PCI_PIFR 0x09 /* Prog. Interface Register */ +#define PCI_SCR 0x0A /* Sub-Class Register */ +#define PCI_BCR 0x0B /* Base-Class Register */ +#define PCI_HEADT 0x0E /* Header type, 8-bit */ +#define PHT_MULTIFUNC 0x80 /* Multiple functions */ +#define PCI_BAR 0x10 /* Base Address Register */ +#define PCI_ILR 0x3C /* Interrupt Line Register */ +#define PCI_IPR 0x3D /* Interrupt Pin Register */ + +/* PCI bridge devices (AGP) */ +#define PPB_SBUSN 0x19 /* Secondary Bus Number */ + +/* Intel compatible PCI bridge devices (AGP) */ +#define PPB_SSTS 0x1E /* Secondary PCI-to-PCI Status Register */ + +#define NO_VID 0xffff /* No PCI card present */ + +struct pci_vendor +{ + u16_t vid; + char *name; +}; + +struct pci_device +{ + u16_t vid; + u16_t did; + char *name; +}; + +struct pci_baseclass +{ + u8_t baseclass; + char *name; +}; + +struct pci_subclass +{ + u8_t baseclass; + u8_t subclass; + u16_t infclass; + char *name; +}; + +struct pci_intel_ctrl +{ + u16_t vid; + u16_t did; +}; + +struct pci_isabridge +{ + u16_t vid; + u16_t did; + int checkclass; + int type; +}; + +struct pci_pcibridge +{ + u16_t vid; + u16_t did; + int type; +}; + +#define PCI_IB_PIIX 1 /* Intel PIIX compatible ISA bridge */ +#define PCI_IB_VIA 2 /* VIA compatible ISA bridge */ +#define PCI_IB_AMD 3 /* AMD compatible ISA bridge */ +#define PCI_IB_SIS 4 /* SIS compatible ISA bridge */ + +#define PCI_AGPB_INTEL 1 /* Intel compatible AGP bridge */ +#define PCI_AGPB_VIA 2 /* VIA compatible AGP bridge */ + +extern struct pci_vendor pci_vendor_table[]; +extern struct pci_device pci_device_table[]; +extern struct pci_baseclass pci_baseclass_table[]; +extern struct pci_subclass pci_subclass_table[]; +extern struct pci_intel_ctrl pci_intel_ctrl[]; +extern struct pci_isabridge pci_isabridge[]; +extern struct pci_pcibridge pci_pcibridge[]; + +/* + * $PchId: pci.h,v 1.4 2001/12/06 20:21:22 philip Exp $ + */ diff --git a/drivers/libpci/pci_amd.h b/drivers/libpci/pci_amd.h new file mode 100644 index 000000000..2d709d2f3 --- /dev/null +++ b/drivers/libpci/pci_amd.h @@ -0,0 +1,21 @@ +/* +pci_amd.h + +Created: Nov 2001 by Philip Homburg <philip@cs.vu.nl> +*/ + +#define AMD_ISABR_FUNC 3 /* Registers are in function 3 */ +#define AMD_ISABR_PCIIRQ_LEV 0x54 +#define AMD_PCILEV_INTA 0x1 +#define AMD_PCILEV_INTB 0x2 +#define AMD_PCILEV_INTC 4x2 +#define AMD_PCILEV_INTD 4x8 +#define AMD_ISABR_PCIIRQ_ROUTE 0x56 +#define AMD_PCIIRQ_INTA_MASK 0x000F +#define AMD_PCIIRQ_INTB_MASK 0x00F0 +#define AMD_PCIIRQ_INTC_MASK 0x0F00 +#define AMD_PCIIRQ_INTD_MASK 0xF000 + +/* + * $PchId: pci_amd.h,v 1.1 2001/11/09 19:57:37 philip Exp $ + */ diff --git a/drivers/libpci/pci_intel.h b/drivers/libpci/pci_intel.h new file mode 100644 index 000000000..50c59f0ee --- /dev/null +++ b/drivers/libpci/pci_intel.h @@ -0,0 +1,58 @@ +/* +pci_intel.h + +Created: Jan 2000 by Philip Homburg <philip@cs.vu.nl> +*/ + +#define PCII_CONFADD 0xCF8 +#define PCIIC_CODE 0x80000000 +#define PCIIC_BUSNUM_MASK 0xff0000 +#define PCIIC_BUSNUM_SHIFT 16 +#define PCIIC_DEVNUM_MASK 0xf800 +#define PCIIC_DEVNUM_SHIFT 11 +#define PCIIC_FUNCNUM_MASK 0x700 +#define PCIIC_FUNCNUM_SHIFT 8 +#define PCIIC_REGNUM_MASK 0xfc +#define PCIIC_REGNUM_SHIFT 2 +#define PCII_CONFDATA 0xCFC + +#define PCII_SELREG_(bus, dev, func, reg) \ + (PCIIC_CODE | \ + (((bus) << PCIIC_BUSNUM_SHIFT) & PCIIC_BUSNUM_MASK) | \ + (((dev) << PCIIC_DEVNUM_SHIFT) & PCIIC_DEVNUM_MASK) | \ + (((func) << PCIIC_FUNCNUM_SHIFT) & PCIIC_FUNCNUM_MASK) | \ + ((((reg)/4) << PCIIC_REGNUM_SHIFT) & PCIIC_REGNUM_MASK)) +#define PCII_UNSEL (0) + +#define PCII_RREG8_(bus, dev, func, reg) \ + (outl(PCII_CONFADD, PCII_SELREG_(bus, dev, func, reg)), \ + inb(PCII_CONFDATA+((reg)&3))) +#define PCII_RREG16_(bus, dev, func, reg) \ + (PCII_RREG8_(bus, dev, func, reg) | \ + (PCII_RREG8_(bus, dev, func, reg+1) << 8)) +#define PCII_RREG32_(bus, dev, func, reg) \ + (PCII_RREG16_(bus, dev, func, reg) | \ + (PCII_RREG16_(bus, dev, func, reg+2) << 16)) + +#define PCII_WREG8_(bus, dev, func, reg, val) \ + (outl(PCII_CONFADD, PCII_SELREG_(bus, dev, func, reg)), \ + outb(PCII_CONFDATA+((reg)&3), (val))) +#define PCII_WREG16_(bus, dev, func, reg, val) \ + (PCII_WREG8_(bus, dev, func, reg, (val)), \ + (PCII_WREG8_(bus, dev, func, reg+1, (val) >> 8))) +#define PCII_WREG32_(bus, dev, func, reg, val) \ + (PCII_WREG16_(bus, dev, func, reg, (val)), \ + (PCII_WREG16_(bus, dev, func, reg+2, (val) >> 16))) + +/* PIIX configuration registers */ +#define PIIX_PIRQRCA 0x60 +#define PIIX_IRQ_DI 0x80 +#define PIIX_IRQ_MASK 0x0F + +/* PIIX extensions to the PIC */ +#define PIIX_ELCR1 0x4D0 +#define PIIX_ELCR2 0x4D1 + +/* + * $PchId: pci_intel.h,v 1.1 2000/08/12 11:20:17 philip Exp $ + */ diff --git a/drivers/libpci/pci_sis.h b/drivers/libpci/pci_sis.h new file mode 100644 index 000000000..252300952 --- /dev/null +++ b/drivers/libpci/pci_sis.h @@ -0,0 +1,17 @@ +/* +pci_sis.h + +Created: Nov 2001 by Philip Homburg <philip@cs.vu.nl> +*/ + +/* Constants are taken from pci-irq.c in the Linux kernel source */ +#define SIS_ISABR_IRQ_A 0x41 /* IRQA routing */ +#define SIS_ISABR_IRQ_B 0x42 /* IRQB routing */ +#define SIS_ISABR_IRQ_C 0x43 /* IRQC routing */ +#define SIS_ISABR_IRQ_D 0x44 /* IRQD routing */ +#define SIS_IRQ_DISABLED 0x80 +#define SIS_IRQ_MASK 0x0F + +/* + * $PchId: pci_sis.h,v 1.1 2001/12/06 20:22:52 philip Exp $ + */ diff --git a/drivers/libpci/pci_table.c b/drivers/libpci/pci_table.c new file mode 100644 index 000000000..fc4af3011 --- /dev/null +++ b/drivers/libpci/pci_table.c @@ -0,0 +1,240 @@ +/* +pci_table.c + +Tables with PCI vendor and device identifiers + +Created: Jan 2000 by Philip Homburg <philip@cs.vu.nl> + +See the Linux PCI ID Repository <http://pciids.sourceforge.net/>. +*/ + +#include "../../kernel/kernel.h" +#include "pci.h" +#if __minix_vmd +#include "../../kernel/config.h" +#endif + +#if ENABLE_PCI + +struct pci_vendor pci_vendor_table[]= +{ + { 0x1000, "NCR" }, + { 0x1002, "ATI Technologies" }, + { 0x100B, "National Semiconductor Corporation" }, + { 0x1013, "Cirrus Logic" }, + { 0x1022, "Advanced Micro Devices" }, + { 0x102B, "Matrox Graphics, Inc." }, + { 0x1039, "Silicon Integrated Systems (SiS)" }, + { 0x104C, "Texas Instruments" }, + { 0x105A, "Promise Technology" }, + { 0x10B7, "3Com Corporation" }, + { 0x10B9, "AcerLabs (ALI)" }, + { 0x10DE, "nVidia Corporation" }, + { 0x10EC, "Realtek" }, + { 0x1106, "VIA" }, + { 0x110A, "Siemens Nixdorf AG" }, + { 0x125D, "ESS Technology" }, + { 0x1274, "Ensoniq" }, + { 0x5333, "S3" }, + { 0x8086, "Intel" }, + { 0x9004, "Adaptec" }, + { 0x9005, "Adaptec" }, + { 0x0000, NULL } +}; + +struct pci_device pci_device_table[]= +{ + { 0x1000, 0x0001, "NCR 53C810" }, + { 0x1000, 0x000F, "NCR 53C875" }, + { 0x1002, 0x4752, "ATI Rage XL PCI" }, + { 0x100B, 0xD001, "Nat. Semi. 87410" }, + { 0x1013, 0x00B8, "Cirrus Logic GD 5446" }, + { 0x1013, 0x6003, "Cirrus Logic CS4614/22/24 CrystalClear" }, + { 0x1022, 0x700C, "AMD-762 CPU to PCI Bridge (SMP chipset)" }, + { 0x1022, 0x700D, "AMD-762 CPU to PCI Bridge (AGP 4x)" }, + { 0x1022, 0x7410, "AMD-766 PCI to ISA/LPC Bridge" }, + { 0x1022, 0x7411, "AMD-766 EIDE Controller" }, + { 0x102B, 0x051B, "Matrox MGA 2164W [Millennium II]" }, + { 0x102B, 0x0525, "Matrox MGA G400 AGP" }, + { 0x1039, 0x0008, "SiS 85C503/5513" }, + { 0x1039, 0x0200, "SiS 5597/5598 VGA" }, + { 0x1039, 0x0406, "SiS 85C501/2" }, + { 0x1039, 0x5597, "SiS 5582" }, + { 0x104C, 0xAC1C, "TI PCI1225" }, + { 0x105A, 0x0D30, "Promise Technology 20265" }, + { 0x10B7, 0x9058, "3Com 3c905B-Combo" }, + { 0x10B7, 0x9805, "3Com 3c980-TX Python-T" }, + { 0x10B9, 0x1533, "ALI M1533 ISA-bridge [Aladdin IV]" }, + { 0x10B9, 0x1541, "ALI M1541" }, + { 0x10B9, 0x5229, "ALI M5229 (IDE)" }, + { 0x10B9, 0x5243, "ALI M5243" }, + { 0x10B9, 0x7101, "ALI M7101 PMU" }, + { 0x10DE, 0x0020, "nVidia Riva TnT [NV04]" }, + { 0x10DE, 0x0110, "nVidia GeForce2 MX [NV11]" }, + { 0x10EC, 0x8029, "Realtek RTL8029" }, + { 0x10EC, 0x8139, "Realtek RTL8139" }, + { 0x1106, 0x0305, "VIA VT8363/8365 [KT133/KM133]" }, + { 0x1106, 0x0571, "VIA IDE controller" }, + { 0x1106, 0x0686, "VIA VT82C686 (Apollo South Bridge)" }, + { 0x1106, 0x3038, "VT83C572 PCI USB Controller" }, + { 0x1106, 0x3057, "VT82C686A ACPI Power Management Controller" }, + { 0x1106, 0x3058, "VIA AC97 Audio Controller" }, + { 0x1106, 0x3059, "VIA AC97 Audio Controller" }, + { 0x1106, 0x3074, "VIA VT8233" }, + { 0x1106, 0x3099, "VIA VT8367 [KT266]" }, + { 0x1106, 0x8305, "VIA VT8365 [KM133 AGP]" }, + { 0x1106, 0xB099, "VIA VT8367 [KT266 AGP]" }, + { 0x110A, 0x0005, "Siemens Nixdorf Tulip Cntlr., Power Management" }, + { 0x1186, 0x1300, "D-Link RTL8139" }, + { 0x125D, 0x1969, "ESS ES1969 Solo-1 Audiodrive" }, + { 0x1274, 0x1371, "Ensoniq ES1371 [AudioPCI-97]" }, + { 0x1274, 0x5000, "Ensoniq ES1370" }, + { 0x1274, 0x5880, "Ensoniq CT5880 [AudioPCI]" }, + { 0x5333, 0x8811, "S3 86c764/765 [Trio32/64/64V+]" }, + { 0x5333, 0x883d, "S3 Virge/VX" }, + { 0x5333, 0x88d0, "S3 Vision 964 vers 0" }, + { 0x5333, 0x8a01, "S3 Virge/DX or /GX" }, + { 0x8086, 0x1004, "Intel 82543GC Gigabit Ethernet Controller" }, + { 0x8086, 0x1229, "Intel 82557" }, + { 0x8086, 0x122D, "Intel 82437FX" }, + { 0x8086, 0x122E, "Intel 82371FB (PIIX)" }, + { 0x8086, 0x1230, "Intel 82371FB (IDE)" }, + { 0x8086, 0x1237, "Intel 82441FX (440FX)" }, + { 0x8086, 0x1250, "Intel 82439HX" }, + { 0x8086, 0x7000, "Intel 82371SB" }, + { 0x8086, 0x7010, "Intel 82371SB (IDE)" }, + { 0x8086, 0x7020, "Intel 82371SB (USB)" }, + { 0x8086, 0x7110, "Intel 82371AB (PIIX4)" }, + { 0x8086, 0x7111, "Intel 82371AB (IDE)" }, + { 0x8086, 0x7112, "Intel 82371AB (USB)" }, + { 0x8086, 0x7113, "Intel 82371AB (Power)" }, + { 0x8086, 0x7190, "Intel 82443BX" }, + { 0x8086, 0x7191, "Intel 82443BX (AGP bridge)" }, + { 0x9004, 0x8178, "Adaptec AHA-2940U/2940UW Ultra/Ultra-Wide SCSI Ctrlr" }, + { 0x9005, 0x0080, "Adaptec AIC-7892A Ultra160/m PCI SCSI Controller" }, + { 0x0000, 0x0000, NULL } +}; + +struct pci_baseclass pci_baseclass_table[]= +{ + { 0x00, "No device class" }, + { 0x01, "Mass storage controller" }, + { 0x02, "Network controller" }, + { 0x03, "Display controller" }, + { 0x04, "Multimedia device" }, + { 0x05, "Memory controller" }, + { 0x06, "Bridge device" }, + { 0x07, "Simple comm. controller" }, + { 0x08, "Base system peripheral" }, + { 0x09, "Input device" }, + { 0x0A, "Docking station" }, + { 0x0B, "Processor" }, + { 0x0C, "Serial bus controller" }, + { 0x0d, "Wireless controller" }, + { 0x0e, "Intelligent I/O controller" }, + { 0x0f, "Satellite comm. controller" }, + { 0x10, "Encryption/decryption controller" }, + { 0x11, "Data acquisition controller" }, + { 0xff, "Misc. device" }, + + { 0x00, NULL } +}; + +/* -1 in the infclass field is a wildcard for infclass */ +struct pci_subclass pci_subclass_table[]= +{ + { 0x00, 0x01, 0x00, "VGA-compatible device" }, + + { 0x01, 0x00, 0x00, "SCSI bus controller" }, + { 0x01, 0x01, -1, "IDE controller" }, + { 0x01, 0x02, 0x00, "Floppy disk controller" }, + { 0x01, 0x03, 0x00, "IPI controller" }, + { 0x01, 0x04, 0x00, "RAID controller" }, + { 0x01, 0x80, 0x00, "Other mass storage controller" }, + + { 0x02, 0x00, 0x00, "Ethernet controller" }, + { 0x02, 0x01, 0x00, "Token Ring controller" }, + { 0x02, 0x02, 0x00, "FDDI controller" }, + { 0x02, 0x03, 0x00, "ATM controller" }, + { 0x02, 0x04, 0x00, "ISDN controller" }, + { 0x02, 0x80, 0x00, "Other network controller" }, + + { 0x03, 0x00, 0x00, "VGA-compatible controller" }, + { 0x03, 0x00, 0x01, "8514-compatible controller" }, + { 0x03, 0x01, 0x00, "XGA controller" }, + { 0x03, 0x02, 0x00, "3D controller" }, + { 0x03, 0x80, 0x00, "Other display controller" }, + + { 0x04, 0x00, 0x00, "Video device" }, + { 0x04, 0x01, 0x00, "Audio device" }, + { 0x04, 0x02, 0x00, "Computer telephony device" }, + { 0x04, 0x80, 0x00, "Other multimedia device" }, + + { 0x06, 0x00, 0x00, "Host bridge" }, + { 0x06, 0x01, 0x00, "ISA bridge" }, + { 0x06, 0x02, 0x00, "EISA bridge" }, + { 0x06, 0x03, 0x00, "MCA bridge" }, + { 0x06, 0x04, 0x00, "PCI-to-PCI bridge" }, + { 0x06, 0x04, 0x01, "Subtractive decode PCI-to-PCI bridge" }, + { 0x06, 0x05, 0x00, "PCMCIA bridge" }, + { 0x06, 0x06, 0x00, "NuBus bridge" }, + { 0x06, 0x07, 0x00, "CardBus bridge" }, + { 0x06, 0x08, -1, "RACEway bridge" }, + { 0x06, 0x09, -1, "Semi-transparent PCI-to-PCI bridge" }, + { 0x06, 0x80, 0x00, "Other bridge device" }, + + { 0x0C, 0x00, 0x00, "IEEE 1394 (FireWire)" }, + { 0x0C, 0x00, 0x10, "IEEE 1394 (OpenHCI)" }, + { 0x0C, 0x01, 0x00, "ACCESS bus" }, + { 0x0C, 0x02, 0x00, "SSA" }, + { 0x0C, 0x03, 0x00, "USB (with UHC)" }, + { 0x0C, 0x03, 0x10, "USB (with OHC)" }, + { 0x0C, 0x03, 0x80, "USB (other host inf.)" }, + { 0x0C, 0x03, 0xFE, "USB device" }, + { 0x0C, 0x04, 0x00, "Fibre Channel" }, + { 0x0C, 0x05, 0x00, "SMBus" }, + + { 0x00, 0x00, 0x00, NULL } +}; + +struct pci_intel_ctrl pci_intel_ctrl[]= +{ + { 0x1022, 0x700C, }, /* AMD-762 */ + { 0x1039, 0x0406, }, /* SiS 85C501/2 */ + { 0x1039, 0x5597, }, /* SiS 5582 */ + { 0x10B9, 0x1541, }, /* ALI M1541 */ + { 0x1106, 0x0305, }, /* VIA VT8363/8365 */ + { 0x1106, 0x3099, }, /* VIA VT8367 [KT266] */ + { 0x8086, 0x122D, }, /* Intel 82437FX */ + { 0x8086, 0x1237, }, /* Intel 82441FX */ + { 0x8086, 0x1250, }, /* Intel 82439HX */ + { 0x8086, 0x7190, }, /* Intel 82443BX */ + { 0x0000, 0x0000, }, +}; + +struct pci_isabridge pci_isabridge[]= +{ + { 0x1022, 0x7410, 1, PCI_IB_AMD, }, /* AMD-766 */ + { 0x1039, 0x0008, 1, PCI_IB_SIS, }, /* SiS 85C503/5513 */ + { 0x10B9, 0x1533, 1, PCI_IB_PIIX, }, /* ALI M1533 */ + { 0x1106, 0x0686, 1, PCI_IB_VIA, }, /* VIA VT82C686 */ + { 0x1106, 0x3074, 1, PCI_IB_VIA, }, /* VIA VT8233 */ + { 0x8086, 0x122E, 1, PCI_IB_PIIX, }, /* Intel 82371FB */ + { 0x8086, 0x7000, 1, PCI_IB_PIIX, }, /* Intel 82371SB */ + { 0x8086, 0x7110, 1, PCI_IB_PIIX, }, /* Intel PIIX4 */ + { 0x0000, 0x0000, 0, 0, }, +}; + +struct pci_pcibridge pci_pcibridge[]= +{ + { 0x8086, 0x7191, PCI_AGPB_INTEL, }, /* Intel 82443BX (AGP bridge) */ + { 0x1022, 0x700D, PCI_AGPB_INTEL, }, /* AMD-762 (AGP 4x) */ + { 0x10B9, 0x5243, PCI_AGPB_INTEL, }, /* ALI M5243 */ + { 0x1106, 0x8305, PCI_AGPB_VIA, }, /* VIA VT8365 [KM133 AGP] */ + { 0x0000, 0x0000, 0, }, +}; +#endif /* ENABLE_PCI */ + +/* + * $PchId: pci_table.c,v 1.7 2003/09/05 10:53:22 philip Exp $ + */ diff --git a/drivers/libpci/pci_via.h b/drivers/libpci/pci_via.h new file mode 100644 index 000000000..3d2fee378 --- /dev/null +++ b/drivers/libpci/pci_via.h @@ -0,0 +1,27 @@ +/* +pci_via.h + +Created: Jun 2001 by Philip Homburg <philip@cs.vu.nl> +*/ + +#define VIA_ISABR_EL 0x54 /* Edge or level triggered */ +#define VIA_ISABR_EL_INTA 0x08 /* Edge (1) or level (0) */ +#define VIA_ISABR_EL_INTB 0x04 +#define VIA_ISABR_EL_INTC 0x02 +#define VIA_ISABR_EL_INTD 0x01 + +#define VIA_ISABR_IRQ_R1 0x55 /* IRQ routing 1 */ +#define VIA_ISABR_IRQ_INTD 0xf0 /* routing for INTD */ +#define VIA_ISABR_IRQ_INT0 0x0f /* routing for INT0 */ +#define VIA_ISABR_IRQ_R2 0x56 /* IRQ routing 2 */ +#define VIA_ISABR_IRQ_INTA 0xf0 /* routing for INTA */ +#define VIA_ISABR_IRQ_INTB 0x0f /* routing for INTB */ +#define VIA_ISABR_IRQ_R3 0x57 /* IRQ routing 3 */ +#define VIA_ISABR_IRQ_INTC 0xf0 /* routing for INTC */ +#define VIA_ISABR_IRQ_INT1 0x0f /* routing for INT1 */ +#define VIA_ISABR_IRQ_R4 0x58 /* IRQ routing 4 */ +#define VIA_ISABR_IRQ_INT2 0x0f /* routing for INT2 */ + +/* + * $PchId: pci_via.h,v 1.1 2001/06/20 15:50:25 philip Exp $ + */ diff --git a/drivers/memory/Makefile b/drivers/memory/Makefile new file mode 100644 index 000000000..ee65d03aa --- /dev/null +++ b/drivers/memory/Makefile @@ -0,0 +1,55 @@ +# Makefile for memory driver (MEMORY) +DRIVER = memory + +# directories +u = /usr +i = $u/include +s = $i/sys +m = $i/minix +b = $i/ibm +d = $u/src/drivers + +# programs, flags, etc. +MAKE = exec make +CC = exec cc +CFLAGS = -I$i +LDFLAGS = -i +LIBS = -lsys -lutils + +OBJ = memory.o +LIBDRIVER = $d/libdriver/driver.o + + +# build local binary +all build: $(DRIVER) +$(DRIVER): $(OBJ) $(LIBDRIVER) + $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBDRIVER) $(LIBS) + install -S 64w $(DRIVER) + +$(LIBDRIVER): + cd $d/libdriver && $(MAKE) + +# install with other drivers +install: /usr/sbin/drivers/$(DRIVER) +/usr/sbin/drivers/$(DRIVER): $(DRIVER) + install -o root -cs $? $@ + +# clean up local files +clean: + rm -f $(DRIVER) *.o *.bak + + +# dependencies +a = $d/drivers.h $b/interrupt.h $b/bios.h \ + $i/ansi.h $i/string.h $i/limits.h $i/stddef.h $i/errno.h \ + $m/config.h $m/type.h $m/com.h $m/callnr.h $m/const.h $s/types.h \ + $m/syslib.h $s/types.h \ + $m/utils.h $m/serverassert.h $m/devio.h +l = $d/libdriver/driver.h $d/libdriver/driver.c $m/partition.h $m/u64.h + + +memory.o: $a $l + +$(LIBDRIVER): $a $l +$(LIBDRIVER): $s/ioc_disk.h + diff --git a/drivers/memory/memory.c b/drivers/memory/memory.c new file mode 100644 index 000000000..d8e3674b5 --- /dev/null +++ b/drivers/memory/memory.c @@ -0,0 +1,286 @@ +/* This file contains the device dependent part of the drivers for the + * following special files: + * /dev/ram - RAM disk + * /dev/mem - absolute memory + * /dev/kmem - kernel virtual memory + * /dev/null - null device (data sink) + * /dev/boot - boot FS loaded from boot image + * + * Changes: + * Apr 09, 2005 added support for boot FS (Jorrit N. Herder) + * Sep 03, 2004 secured code with ENABLE_USERPRIV (Jorrit N. Herder) + * Jul 26, 2004 moved RAM driver to user-space (Jorrit N. Herder) + * Apr 20, 1992 device dependent/independent split (Kees J. Bot) + */ + +#include "../drivers.h" +#include "../libdriver/driver.h" +#include <sys/ioc_memory.h> +#if (CHIP == INTEL) && ENABLE_USERBIOS +#include <ibm/int86.h> +#endif + +#define NR_DEVS 5 /* number of RAM-type devices */ + +PRIVATE struct device m_geom[NR_DEVS]; /* base and size of each RAM disk */ +PRIVATE int m_device; /* current device */ +PRIVATE struct kenviron kenv; /* need protected_mode */ +PRIVATE struct psinfo psinfo = { NR_TASKS, NR_PROCS, 0, 0, 0 }; + +FORWARD _PROTOTYPE( struct device *m_prepare, (int device) ); +FORWARD _PROTOTYPE( int m_transfer, (int proc_nr, int opcode, off_t position, + iovec_t *iov, unsigned nr_req) ); +FORWARD _PROTOTYPE( int m_do_open, (struct driver *dp, message *m_ptr) ); +FORWARD _PROTOTYPE( void m_init, (void) ); +FORWARD _PROTOTYPE( int m_ioctl, (struct driver *dp, message *m_ptr) ); +FORWARD _PROTOTYPE( void m_geometry, (struct partition *entry) ); +FORWARD _PROTOTYPE( char *m_name, (void) ); + + +/* Entry points to this driver. */ +PRIVATE struct driver m_dtab = { + m_name, /* current device's name */ + m_do_open, /* open or mount */ + do_nop, /* nothing on a close */ + m_ioctl, /* specify ram disk geometry */ + m_prepare, /* prepare for I/O on a given minor device */ + m_transfer, /* do the I/O */ + nop_cleanup, /* no need to clean up */ + m_geometry, /* memory device "geometry" */ + nop_stop, /* no need to clean up on shutdown */ + nop_alarm, /* ignore leftover alarms */ +}; + + +/*===========================================================================* + * main * + *===========================================================================*/ +PUBLIC void main(void) +{ + m_init(); + driver_task(&m_dtab); +} + + +/*===========================================================================* + * m_name * + *===========================================================================*/ +PRIVATE char *m_name() +{ +/* Return a name for the current device. */ + static char name[] = "memory"; + return name; +} + + +/*===========================================================================* + * m_prepare * + *===========================================================================*/ +PRIVATE struct device *m_prepare(device) +int device; +{ +/* Prepare for I/O on a device: check if the minor device number is ok. */ + + if (device < 0 || device >= NR_DEVS) return(NIL_DEV); + m_device = device; + + return(&m_geom[device]); +} + + +/*===========================================================================* + * m_transfer * + *===========================================================================*/ +PRIVATE int m_transfer(proc_nr, opcode, position, iov, nr_req) +int proc_nr; /* process doing the request */ +int opcode; /* DEV_GATHER or DEV_SCATTER */ +off_t position; /* offset on device to read or write */ +iovec_t *iov; /* pointer to read or write request vector */ +unsigned nr_req; /* length of request vector */ +{ +/* Read or write /dev/null, /dev/mem, /dev/kmem, /dev/ram, or /dev/boot. */ + + int device; + phys_bytes mem_phys, user_phys; + unsigned count; + vir_bytes user_vir; + struct device *dv; + unsigned long dv_size; + + /* Get minor device number and check for /dev/null. */ + device = m_device; + dv = &m_geom[device]; + dv_size = cv64ul(dv->dv_size); + + + while (nr_req > 0) { + count = iov->iov_size; + user_vir = iov->iov_addr; + + switch (device) { + case NULL_DEV: + if (opcode == DEV_GATHER) return(OK); /* always at EOF */ + break; + + default: + /* /dev/mem, /dev/kmem, /dev/ram, /dev/boot: check for EOF */ + if (position >= dv_size) return(OK); + if (position + count > dv_size) count = dv_size - position; + mem_phys = cv64ul(dv->dv_base) + position; + + /* Copy the data. */ + if (opcode == DEV_GATHER) { + sys_copy(ABS, D, mem_phys, proc_nr, D, user_vir, count); + } else { + sys_copy(proc_nr, D, user_vir, ABS, D, mem_phys, count); + } + } + + /* Book the number of bytes transferred. */ + position += count; + iov->iov_addr += count; + if ((iov->iov_size -= count) == 0) { iov++; nr_req--; } + } + return(OK); +} + + +/*============================================================================* + * m_do_open * + *============================================================================*/ +PRIVATE int m_do_open(dp, m_ptr) +struct driver *dp; +message *m_ptr; +{ +/* Check device number on open. Give I/O privileges to a process opening + * /dev/mem or /dev/kmem. This is needed for systems with memory mapped I/O. + */ + if (m_prepare(m_ptr->DEVICE) == NIL_DEV) return(ENXIO); + +#if (CHIP == INTEL) && ENABLE_USERPRIV && ENABLE_USERIOPL + if (m_device == MEM_DEV || m_device == KMEM_DEV) { + sys_enable_iop(m_ptr->PROC_NR); + printf("MEMORY: sys_enable_iop for proc nr %d.\n", m_ptr->PROC_NR); + } +#endif + + return(OK); +} + + +/*===========================================================================* + * m_init * + *===========================================================================*/ +PRIVATE void m_init() +{ + /* Initialize this task. */ + extern int end; + int s; + + /* Print welcome message. */ + printf("MEMORY: user-level memory (RAM) driver is alive"); + + /* Get kernel environment (protected_mode and addresses). */ + if (OK != (s=sys_getkenviron(&kenv))) { + server_panic("MEM","Couldn't get kernel environment.",s); + } + m_geom[KMEM_DEV].dv_base = cvul64(kenv.kmem_base); + m_geom[KMEM_DEV].dv_size = cvul64(kenv.kmem_size); + m_geom[BOOT_DEV].dv_base = cvul64(kenv.bootfs_base); + m_geom[BOOT_DEV].dv_size = cvul64(kenv.bootfs_size); + psinfo.proc = kenv.proc_addr; + +#if (CHIP == INTEL) + if (!kenv.protected) { + m_geom[MEM_DEV].dv_size = cvul64(0x100000); /* 1M for 8086 systems */ + } else { +#if _WORD_SIZE == 2 + m_geom[MEM_DEV].dv_size = cvul64(0x1000000); /* 16M for 286 systems */ +#else + m_geom[MEM_DEV].dv_size = cvul64(0xFFFFFFFF); /* 4G-1 for 386 systems */ +#endif + } +#else /* !(CHIP == INTEL) */ +#if (CHIP == M68000) + m_geom[MEM_DEV].dv_size = cvul64(MEM_BYTES); +#else /* !(CHIP == M68000) */ +#error /* memory limit not set up */ +#endif /* !(CHIP == M68000) */ +#endif /* !(CHIP == INTEL) */ +} + + +/*===========================================================================* + * m_ioctl * + *===========================================================================*/ +PRIVATE int m_ioctl(dp, m_ptr) +struct driver *dp; +message *m_ptr; /* pointer to read or write message */ +{ +/* Set parameters for the RAM disk. */ + + struct device *dv; + + if ((dv = m_prepare(m_ptr->DEVICE)) == NIL_DEV) return(ENXIO); + + switch (m_ptr->REQUEST) { + case MIOCRAMSIZE: { + /* FS wants to create a new RAM disk with the given size. */ + unsigned long bytesize; + phys_bytes base; + int s; + + if (m_ptr->PROC_NR != FS_PROC_NR) return(EPERM); + + /* Try to allocate a piece of kernel memory for the RAM disk. */ + bytesize = m_ptr->POSITION; + if (OK != (s = sys_kmalloc(bytesize, &base))) + server_panic("MEM","Couldn't allocate kernel memory", s); + dv->dv_base = cvul64(base); + dv->dv_size = cvul64(bytesize); + break; + } + /* Perhaps it is cleaner to move all code relating to psinfo to the info + * server??? (Note that psinfo is global; psinfo.proc is set in m_init.) + * This requires changes to ioctl as well. + */ + case MIOCSPSINFO: { + /* MM or FS set the address of their process table. */ + phys_bytes psinfo_phys; + + if (m_ptr->PROC_NR == MM_PROC_NR) { + psinfo.mproc = (vir_bytes) m_ptr->ADDRESS; + } else + if (m_ptr->PROC_NR == FS_PROC_NR) { + psinfo.fproc = (vir_bytes) m_ptr->ADDRESS; + } else { + return(EPERM); + } + break; + } + case MIOCGPSINFO: { + /* The ps program wants the process table addresses. */ + if (sys_datacopy(SELF, (vir_bytes) &psinfo, + m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS, + sizeof(psinfo)) != OK) return(EFAULT); + break; + } + + default: + return(do_diocntl(&m_dtab, m_ptr)); + } + return(OK); +} + + +/*============================================================================* + * m_geometry * + *============================================================================*/ +PRIVATE void m_geometry(entry) +struct partition *entry; +{ + /* Memory devices don't have a geometry, but the outside world insists. */ + entry->cylinders = div64u(m_geom[m_device].dv_size, SECTOR_SIZE) / (64 * 32); + entry->heads = 64; + entry->sectors = 32; +} diff --git a/drivers/printer/Makefile b/drivers/printer/Makefile new file mode 100644 index 000000000..1a3552582 --- /dev/null +++ b/drivers/printer/Makefile @@ -0,0 +1,43 @@ +# Makefile for Centronics printer driver (PRINTER) +DRIVER = printer + +# directories +u = /usr +i = $u/include +s = $i/sys +m = $i/minix +b = $i/ibm +d = $u/src/drivers + +# programs, flags, etc. +CC = exec cc +CFLAGS = -I$i +LDFLAGS = -i +LIBS = -lsys -lutils + +OBJ = printer.o + +# build local binary +all build: $(DRIVER) +$(DRIVER): $(OBJ) + $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS) + install -S 64w $(DRIVER) + +# install with other drivers +install: /usr/sbin/drivers/$(DRIVER) +/usr/sbin/drivers/$(DRIVER): $(DRIVER) + install -o root -cs $? $@ + +# clean up local files +clean: + rm -f *.o *.bak $(DRIVER) + +# Dependencies +a = $d/drivers.h $b/interrupt.h $b/bios.h \ + $i/ansi.h $i/string.h $i/limits.h $i/stddef.h $i/errno.h \ + $m/config.h $m/type.h $m/com.h $m/callnr.h $m/const.h $s/types.h \ + $m/syslib.h $s/types.h \ + $m/utils.h $m/serverassert.h $m/devio.h + +printer.o: $a + diff --git a/drivers/printer/printer.c b/drivers/printer/printer.c new file mode 100644 index 000000000..7d5ae494a --- /dev/null +++ b/drivers/printer/printer.c @@ -0,0 +1,383 @@ +/* This file contains the printer driver. It is a fairly simple driver, + * supporting only one printer. Characters that are written to the driver + * are written to the printer without any changes at all. + * + * Changes: + * May 07, 2004 wait until printer is ready (Jorrit N. Herder) + * May 06, 2004 printer driver moved to user-space (Jorrit N. Herder) + * + * The valid messages and their parameters are: + * + * DEV_OPEN: initializes the printer + * DEV_CLOSE: does nothing + * HARD_INT: interrupt handler has finished current chunk of output + * DEV_WRITE: a process wants to write on a terminal + * CANCEL: terminate a previous incomplete system call immediately + * + * m_type TTY_LINE PROC_NR COUNT ADDRESS + * |-------------+---------+---------+---------+---------| + * | DEV_OPEN | | | | | + * |-------------+---------+---------+---------+---------| + * | DEV_CLOSE | | proc nr | | | + * ------------------------------------------------------- + * | HARD_INT | | | | | + * |-------------+---------+---------+---------+---------| + * | HARD_STOP | | | | | + * |-------------+---------+---------+---------+---------| + * | DEV_WRITE |minor dev| proc nr | count | buf ptr | + * |-------------+---------+---------+---------+---------| + * | CANCEL |minor dev| proc nr | | | + * ------------------------------------------------------- + * + * Note: since only 1 printer is supported, minor dev is not used at present. + */ + +#include "../drivers.h" + +#if ENABLE_PRINTER + +/* Control bits (in port_base + 2). "+" means positive logic and "-" means + * negative logic. Most of the signals are negative logic on the pins but + * many are converted to positive logic in the ports. Some manuals are + * misleading because they only document the pin logic. + * + * +0x01 Pin 1 -Strobe + * +0x02 Pin 14 -Auto Feed + * -0x04 Pin 16 -Initialize Printer + * +0x08 Pin 17 -Select Printer + * +0x10 IRQ7 Enable + * + * Auto Feed and Select Printer are always enabled. Strobe is enabled briefly + * when characters are output. Initialize Printer is enabled briefly when + * the task is started. IRQ7 is enabled when the first character is output + * and left enabled until output is completed (or later after certain + * abnormal completions). + */ +#define ASSERT_STROBE 0x1D /* strobe a character to the interface */ +#define NEGATE_STROBE 0x1C /* enable interrupt on interface */ +#define SELECT 0x0C /* select printer bit */ +#define INIT_PRINTER 0x08 /* init printer bits */ + +/* Status bits (in port_base + 2). + * + * -0x08 Pin 15 -Error + * +0x10 Pin 13 +Select Status + * +0x20 Pin 12 +Out of Paper + * -0x40 Pin 10 -Acknowledge + * -0x80 Pin 11 +Busy + */ +#define BUSY_STATUS 0x10 /* printer gives this status when busy */ +#define NO_PAPER 0x20 /* status bit saying that paper is out */ +#define NORMAL_STATUS 0x90 /* printer gives this status when idle */ +#define ON_LINE 0x10 /* status bit saying that printer is online */ +#define STATUS_MASK 0xB0 /* mask to filter out status bits */ + +#define MAX_ONLINE_RETRIES 120 /* about 60s: waits 0.5s after each retry */ + +/* Centronics interface timing that must be met by software (in microsec). + * + * Strobe length: 0.5u to 100u (not sure about the upper limit). + * Data set up: 0.5u before strobe. + * Data hold: 0.5u after strobe. + * Init pulse length: over 200u (not sure). + * + * The strobe length is about 50u with the code here and function calls for + * sys_outb() - not much to spare. The 0.5u minimums will not be violated + * with the sys_outb() messages exchanged. + */ + +PRIVATE int caller; /* process to tell when printing done (FS) */ +PRIVATE int done_status; /* status of last output completion */ +PRIVATE int oleft; /* bytes of output left in obuf */ +PRIVATE char obuf[128]; /* output buffer */ +PRIVATE char *optr; /* ptr to next char in obuf to print */ +PRIVATE int orig_count; /* original byte count */ +PRIVATE int port_base; /* I/O port for printer */ +PRIVATE int proc_nr; /* user requesting the printing */ +PRIVATE int user_left; /* bytes of output left in user buf */ +PRIVATE vir_bytes user_vir; /* address of remainder of user buf */ +PRIVATE int writing; /* nonzero while write is in progress */ + +FORWARD _PROTOTYPE( void do_cancel, (message *m_ptr) ); +FORWARD _PROTOTYPE( void output_done, (void) ); +FORWARD _PROTOTYPE( void do_write, (message *m_ptr) ); +FORWARD _PROTOTYPE( void prepare_output, (void) ); +FORWARD _PROTOTYPE( void do_initialize, (void) ); +FORWARD _PROTOTYPE( void reply, (int code,int replyee,int proc,int status)); +FORWARD _PROTOTYPE( void do_printer_output, (void) ); + +/*===========================================================================* + * printer_task * + *===========================================================================*/ +PUBLIC void main(void) +{ +/* Main routine of the printer task. */ + + message pr_mess; /* buffer for all incoming messages */ + + printf("PRN: user-level printer driver is up and running\n"); + + while (TRUE) { + receive(ANY, &pr_mess); + switch(pr_mess.m_type) { + case DEV_OPEN: + do_initialize(); /* initialize */ + /* fall through */ + case DEV_CLOSE: + reply(TASK_REPLY, pr_mess.m_source, pr_mess.PROC_NR, OK); + break; + case DEV_WRITE: do_write(&pr_mess); break; + case CANCEL : do_cancel(&pr_mess); break; + case HARD_INT : do_printer_output(); break; + case HARD_STOP: sys_exit(0); + /* never reached */ + break; + default: + reply(TASK_REPLY, pr_mess.m_source, pr_mess.PROC_NR, EINVAL); + } + } +} + + +/*===========================================================================* + * do_write * + *===========================================================================*/ +PRIVATE void do_write(m_ptr) +register message *m_ptr; /* pointer to the newly arrived message */ +{ +/* The printer is used by sending DEV_WRITE messages to it. Process one. */ + + register int r = SUSPEND; + int retries; + int status; + + /* Reject command if last write is not yet finished, the count is not + * positive, or the user address is bad. + */ + if (writing) r = EIO; + else if (m_ptr->COUNT <= 0) r = EINVAL; + + /* Reply to FS, no matter what happened, possible SUSPEND caller. */ + reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r); + + /* If no errors occurred, continue printing with SUSPENDED caller. + * First wait until the printer is online to prevent stupid errors. + */ + if (SUSPEND == r) { + caller = m_ptr->m_source; + proc_nr = m_ptr->PROC_NR; + user_left = m_ptr->COUNT; + orig_count = m_ptr->COUNT; + user_vir = (vir_bytes) m_ptr->ADDRESS; + writing = TRUE; + + retries = MAX_ONLINE_RETRIES + 1; + while (--retries > 0) { + sys_inb(port_base + 1, &status); + if ((status & ON_LINE)) { /* printer online! */ + prepare_output(); + do_printer_output(); + return; + } + tick_delay(30); /* wait before retry */ + } + /* If we reach this point, the printer was not online in time. */ + done_status = status; + output_done(); + } +} + + +/*===========================================================================* + * output_done * + *===========================================================================*/ +PRIVATE void output_done() +{ +/* Previous chunk of printing is finished. Continue if OK and more. + * Otherwise, reply to caller (FS). + */ + register int status; + + if (!writing) return; /* probably leftover interrupt */ + if (done_status != OK) { /* printer error occurred */ + status = EIO; + if ((done_status & ON_LINE) == 0) { + printf("Printer is not on line\n"); + } else if ((done_status & NO_PAPER)) { + printf("Printer is out of paper\n"); + status = EAGAIN; + } else { + printf("Printer error, status is 0x%02X\n", done_status); + } + /* Some characters have been printed, tell how many. */ + if (status == EAGAIN && user_left < orig_count) { + status = orig_count - user_left; + } + oleft = 0; /* cancel further output */ + } + else if (user_left != 0) { /* not yet done, continue! */ + prepare_output(); + return; + } + else { /* done! report back to FS */ + status = orig_count; + } + reply(REVIVE, caller, proc_nr, status); + writing = FALSE; +} + + +/*===========================================================================* + * do_cancel * + *===========================================================================*/ +PRIVATE void do_cancel(m_ptr) +register message *m_ptr; /* pointer to the newly arrived message */ +{ +/* Cancel a print request that has already started. Usually this means that + * the process doing the printing has been killed by a signal. It is not + * clear if there are race conditions. Try not to cancel the wrong process, + * but rely on FS to handle the EINTR reply and de-suspension properly. + */ + + if (writing && m_ptr->PROC_NR == proc_nr) { + oleft = 0; /* cancel output by interrupt handler */ + writing = FALSE; + } + reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, EINTR); +} + + +/*===========================================================================* + * reply * + *===========================================================================*/ +PRIVATE void reply(code, replyee, process, status) +int code; /* TASK_REPLY or REVIVE */ +int replyee; /* destination for message (normally FS) */ +int process; /* which user requested the printing */ +int status; /* number of chars printed or error code */ +{ +/* Send a reply telling FS that printing has started or stopped. */ + + message pr_mess; + + pr_mess.m_type = code; /* TASK_REPLY or REVIVE */ + pr_mess.REP_STATUS = status; /* count or EIO */ + pr_mess.REP_PROC_NR = process; /* which user does this pertain to */ + send(replyee, &pr_mess); /* send the message */ +} + + +/*===========================================================================* + * do_initialize * + *===========================================================================*/ +PRIVATE void do_initialize() +{ +/* Set global variables and initialize the printer. */ + + static int initialized = FALSE; + if (initialized) return; + initialized = TRUE; + + /* Get the base port for first printer. This used to be done from the + * BIOS with phys_copy(0x408L, vir2phys(&port_base), 2L); but currently + * a magic number is put in place. + */ + port_base = 0x378; + sys_outb(port_base + 2, INIT_PRINTER); + tick_delay(1); /* easily satisfies Centronics minimum */ + /* was 2 millisecs; now is ~17 millisecs */ + sys_outb(port_base + 2, SELECT); + sys_irqsetpolicy(PRINTER_IRQ, SELF, 0, 0, 0, 0); + sys_irqenable(PRINTER_IRQ); + +} + + +/*==========================================================================* + * prepare_output * + *==========================================================================*/ +PRIVATE void prepare_output() +{ +/* Start next chunk of printer output. Fetch the data from user space. */ + + register int chunk; + + if ( (chunk = user_left) > sizeof obuf) chunk = sizeof obuf; + if (OK!=sys_datacopy(proc_nr, user_vir, SELF, (vir_bytes) obuf, chunk)) { + done_status = EFAULT; + output_done(); + return; + } + optr = obuf; + oleft = chunk; +} + + +/*===========================================================================* + * do_printer_output * + *===========================================================================*/ +PRIVATE void do_printer_output() +{ +/* This function does the actual output to the printer. This is called on + * a HARD_INT message sent from the generic interrupt handler that 'forwards' + * interrupts to this driver. The generic handler did not reenable the + * printer IRQ yet! + */ + + int status; + pvb_pair_t char_out[3]; + + if (oleft == 0) { + /* Nothing more to print. Turn off printer interrupts in case they + * are level-sensitive as on the PS/2. This should be safe even + * when the printer is busy with a previous character, because the + * interrupt status does not affect the printer. + */ + sys_outb(port_base + 2, SELECT); + sys_irqenable(PRINTER_IRQ); + return; + } + + do { + /* Loop to handle fast (buffered) printers. It is important that + * processor interrupts are not disabled here, just printer interrupts. + */ + (void) sys_inb(port_base + 1, &status); + if ((status & STATUS_MASK) == BUSY_STATUS) { + /* Still busy with last output. This normally happens + * immediately after doing output to an unbuffered or slow + * printer. It may happen after a call from prepare_output or + * pr_restart, since they are not synchronized with printer + * interrupts. It may happen after a spurious interrupt. + */ + sys_irqenable(PRINTER_IRQ); + return; + } + if ((status & STATUS_MASK) == NORMAL_STATUS) { + /* Everything is all right. Output another character. */ + pv_set(char_out[0], port_base, *optr++); + pv_set(char_out[1], port_base+2, ASSERT_STROBE); + pv_set(char_out[2], port_base+2, NEGATE_STROBE); + sys_voutb(char_out, 3); /* request series of port outb */ + + user_vir++; + user_left--; + } else { + /* Error. This would be better ignored (treat as busy). */ + done_status = status; + output_done(); + sys_irqenable(PRINTER_IRQ); + return; + } + } + while (--oleft != 0); + + /* Finished printing chunk OK. */ + done_status = OK; + output_done(); + sys_irqenable(PRINTER_IRQ); +} + + +#endif /* ENABLE_PRINTER */ + diff --git a/drivers/tty/Makefile b/drivers/tty/Makefile new file mode 100644 index 000000000..8e72718fd --- /dev/null +++ b/drivers/tty/Makefile @@ -0,0 +1,56 @@ +# Makefile for terminal driver (TTY) +DRIVER = tty + +# directories +u = /usr +i = $u/include +s = $i/sys +m = $i/minix +b = $i/ibm +d = $u/src/drivers + +# programs, flags, etc. +MAKE = exec make +CC = exec cc +CFLAGS = -I$i +LDFLAGS = -i +LIBS = -lsys -lutils -ltimers + +OBJ = tty.o console.o vidcopy.o keyboard.o +# rs232.o + +# build local binary +all build: $(DRIVER) + cd keymaps && $(MAKE) -$(MAKEFLAGS) $@ +$(DRIVER): $(OBJ) + $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS) + install -S 256w $(DRIVER) + +# install with other drivers +install: /usr/sbin/drivers/$(DRIVER) + cd keymaps && $(MAKE) -$(MAKEFLAGS) $@ +/usr/sbin/drivers/$(DRIVER): $(DRIVER) + install -o root -cs $? $@ + +# clean up local files +clean: + cd keymaps && $(MAKE) -$(MAKEFLAGS) $@ + rm -f $(DRIVER) *.o *.bak + + + +# dependencies +a = $d/drivers.h $b/interrupt.h $b/bios.h \ + $i/ansi.h $i/string.h $i/limits.h $i/stddef.h $i/errno.h \ + $m/config.h $m/type.h $m/com.h $m/callnr.h $m/const.h $s/types.h \ + $m/syslib.h $s/types.h \ + $m/utils.h $m/serverassert.h $m/devio.h + +tty.o: $a +console.o: $a +vidcopy.o: # nothing +keyboard.o: $a +rs232.o: $a $i/termios.h $i/signal.h + + + diff --git a/drivers/tty/console.c b/drivers/tty/console.c new file mode 100644 index 000000000..44713690c --- /dev/null +++ b/drivers/tty/console.c @@ -0,0 +1,1083 @@ +/* Code and data for the IBM console driver. + * + * The 6845 video controller used by the IBM PC shares its video memory with + * the CPU somewhere in the 0xB0000 memory bank. To the 6845 this memory + * consists of 16-bit words. Each word has a character code in the low byte + * and a so-called attribute byte in the high byte. The CPU directly modifies + * video memory to display characters, and sets two registers on the 6845 that + * specify the video origin and the cursor position. The video origin is the + * place in video memory where the first character (upper left corner) can + * be found. Moving the origin is a fast way to scroll the screen. Some + * video adapters wrap around the top of video memory, so the origin can + * move without bounds. For other adapters screen memory must sometimes be + * moved to reset the origin. All computations on video memory use character + * (word) addresses for simplicity and assume there is no wrapping. The + * assembly support functions translate the word addresses to byte addresses + * and the scrolling function worries about wrapping. + */ + +#include "../drivers.h" +#include <termios.h> +#include <minix/callnr.h> +#include <minix/com.h> +#include "tty.h" + +/* Definitions used by the console driver. */ +#define MONO_BASE 0xB0000L /* base of mono video memory */ +#define COLOR_BASE 0xB8000L /* base of color video memory */ +#define MONO_SIZE 0x1000 /* 4K mono video memory */ +#define COLOR_SIZE 0x4000 /* 16K color video memory */ +#define EGA_SIZE 0x8000 /* EGA & VGA have at least 32K */ +#define BLANK_COLOR 0x0700 /* determines cursor color on blank screen */ +#define SCROLL_UP 0 /* scroll forward */ +#define SCROLL_DOWN 1 /* scroll backward */ +#define BLANK_MEM ((u16_t *) 0) /* tells mem_vid_copy() to blank the screen */ +#define CONS_RAM_WORDS 80 /* video ram buffer size */ +#define MAX_ESC_PARMS 4 /* number of escape sequence params allowed */ + +/* Constants relating to the controller chips. */ +#define M_6845 0x3B4 /* port for 6845 mono */ +#define C_6845 0x3D4 /* port for 6845 color */ +#define INDEX 0 /* 6845's index register */ +#define DATA 1 /* 6845's data register */ +#define VID_ORG 12 /* 6845's origin register */ +#define CURSOR 14 /* 6845's cursor register */ + +/* Beeper. */ +#define BEEP_FREQ 0x0533 /* value to put into timer to set beep freq */ +#define B_TIME 3 /* length of CTRL-G beep is ticks */ + +/* definitions used for font management */ +#define GA_SEQUENCER_INDEX 0x3C4 +#define GA_SEQUENCER_DATA 0x3C5 +#define GA_GRAPHICS_INDEX 0x3CE +#define GA_GRAPHICS_DATA 0x3CF +#define GA_VIDEO_ADDRESS 0xA0000L +#define GA_FONT_SIZE 8192 + +/* Global variables used by the console driver and assembly support. */ +PUBLIC u16_t vid_seg; +PUBLIC vir_bytes vid_off; /* video ram is found at vid_seg:vid_off */ +PUBLIC unsigned vid_size; /* 0x2000 for color or 0x0800 for mono */ +PUBLIC unsigned vid_mask; /* 0x1FFF for color or 0x07FF for mono */ +PUBLIC unsigned blank_color = BLANK_COLOR; /* display code for blank */ + +/* Private variables used by the console driver. */ +PRIVATE int vid_port; /* I/O port for accessing 6845 */ +PRIVATE int wrap; /* hardware can wrap? */ +PRIVATE int softscroll; /* 1 = software scrolling, 0 = hardware */ +PRIVATE int beeping; /* speaker is beeping? */ +PRIVATE unsigned font_lines; /* font lines per character */ +PRIVATE unsigned scr_width; /* # characters on a line */ +PRIVATE unsigned scr_lines; /* # lines on the screen */ +PRIVATE unsigned scr_size; /* # characters on the screen */ + +/* Per console data. */ +typedef struct console { + tty_t *c_tty; /* associated TTY struct */ + int c_column; /* current column number (0-origin) */ + int c_row; /* current row (0 at top of screen) */ + int c_rwords; /* number of WORDS (not bytes) in outqueue */ + unsigned c_start; /* start of video memory of this console */ + unsigned c_limit; /* limit of this console's video memory */ + unsigned c_org; /* location in RAM where 6845 base points */ + unsigned c_cur; /* current position of cursor in video RAM */ + unsigned c_attr; /* character attribute */ + unsigned c_blank; /* blank attribute */ + char c_reverse; /* reverse video */ + char c_esc_state; /* 0=normal, 1=ESC, 2=ESC[ */ + char c_esc_intro; /* Distinguishing character following ESC */ + int *c_esc_parmp; /* pointer to current escape parameter */ + int c_esc_parmv[MAX_ESC_PARMS]; /* list of escape parameters */ + u16_t c_ramqueue[CONS_RAM_WORDS]; /* buffer for video RAM */ +} console_t; + +PRIVATE int nr_cons= 1; /* actual number of consoles */ +PRIVATE console_t cons_table[NR_CONS]; +PRIVATE console_t *curcons; /* currently visible */ + +/* Color if using a color controller. */ +#define color (vid_port == C_6845) + +/* Map from ANSI colors to the attributes used by the PC */ +PRIVATE int ansi_colors[8] = {0, 4, 2, 6, 1, 5, 3, 7}; + +/* Structure used for font management */ +struct sequence { + unsigned short index; + unsigned char port; + unsigned char value; +}; + +FORWARD _PROTOTYPE( void cons_write, (struct tty *tp) ); +FORWARD _PROTOTYPE( void cons_echo, (tty_t *tp, int c) ); +FORWARD _PROTOTYPE( void out_char, (console_t *cons, int c) ); +FORWARD _PROTOTYPE( void putk, (int c) ); +FORWARD _PROTOTYPE( void beep, (void) ); +FORWARD _PROTOTYPE( void do_escape, (console_t *cons, int c) ); +FORWARD _PROTOTYPE( void flush, (console_t *cons) ); +FORWARD _PROTOTYPE( void parse_escape, (console_t *cons, int c) ); +FORWARD _PROTOTYPE( void scroll_screen, (console_t *cons, int dir) ); +FORWARD _PROTOTYPE( void set_6845, (int reg, unsigned val) ); +FORWARD _PROTOTYPE( void stop_beep, (timer_t *tmrp) ); +FORWARD _PROTOTYPE( void cons_org0, (void) ); +FORWARD _PROTOTYPE( int ga_program, (struct sequence *seq) ); +FORWARD _PROTOTYPE( void cons_ioctl, (tty_t *tp) ); + + +/*===========================================================================* + * cons_write * + *===========================================================================*/ +PRIVATE void cons_write(tp) +register struct tty *tp; /* tells which terminal is to be used */ +{ +/* Copy as much data as possible to the output queue, then start I/O. On + * memory-mapped terminals, such as the IBM console, the I/O will also be + * finished, and the counts updated. Keep repeating until all I/O done. + */ + + int count; + int result; + register char *tbuf; + char buf[64]; + console_t *cons = tp->tty_priv; + + /* Check quickly for nothing to do, so this can be called often without + * unmodular tests elsewhere. + */ + if ((count = tp->tty_outleft) == 0 || tp->tty_inhibited) return; + + /* Copy the user bytes to buf[] for decent addressing. Loop over the + * copies, since the user buffer may be much larger than buf[]. + */ + do { + if (count > sizeof(buf)) count = sizeof(buf); + if ((result = sys_vircopy(tp->tty_outproc, D, tp->tty_out_vir, + SELF, D, (vir_bytes) buf, (vir_bytes) count)) != OK) + break; + tbuf = buf; + + /* Update terminal data structure. */ + tp->tty_out_vir += count; + tp->tty_outcum += count; + tp->tty_outleft -= count; + + /* Output each byte of the copy to the screen. Avoid calling + * out_char() for the "easy" characters, put them into the buffer + * directly. + */ + do { + if ((unsigned) *tbuf < ' ' || cons->c_esc_state > 0 + || cons->c_column >= scr_width + || cons->c_rwords >= buflen(cons->c_ramqueue)) + { + out_char(cons, *tbuf++); + } else { + cons->c_ramqueue[cons->c_rwords++] = + cons->c_attr | (*tbuf++ & BYTE); + cons->c_column++; + } + } while (--count != 0); + } while ((count = tp->tty_outleft) != 0 && !tp->tty_inhibited); + + flush(cons); /* transfer anything buffered to the screen */ + + /* Reply to the writer if all output is finished or if an error occured. */ + if (tp->tty_outleft == 0 || result != OK) { + tty_reply(tp->tty_outrepcode, tp->tty_outcaller, tp->tty_outproc, + tp->tty_outcum); + tp->tty_outcum = 0; + } +} + + +/*===========================================================================* + * cons_echo * + *===========================================================================*/ +PRIVATE void cons_echo(tp, c) +register tty_t *tp; /* pointer to tty struct */ +int c; /* character to be echoed */ +{ +/* Echo keyboard input (print & flush). */ + console_t *cons = tp->tty_priv; + + out_char(cons, c); + flush(cons); +} + + +/*===========================================================================* + * out_char * + *===========================================================================*/ +PRIVATE void out_char(cons, c) +register console_t *cons; /* pointer to console struct */ +int c; /* character to be output */ +{ +/* Output a character on the console. Check for escape sequences first. */ + if (cons->c_esc_state > 0) { + parse_escape(cons, c); + return; + } + + switch(c) { + case 000: /* null is typically used for padding */ + return; /* better not do anything */ + + case 007: /* ring the bell */ + flush(cons); /* print any chars queued for output */ + beep(); + return; + + case '\b': /* backspace */ + if (--cons->c_column < 0) { + if (--cons->c_row >= 0) cons->c_column += scr_width; + } + flush(cons); + return; + + case '\n': /* line feed */ + if ((cons->c_tty->tty_termios.c_oflag & (OPOST|ONLCR)) + == (OPOST|ONLCR)) { + cons->c_column = 0; + } + /*FALL THROUGH*/ + case 013: /* CTRL-K */ + case 014: /* CTRL-L */ + if (cons->c_row == scr_lines-1) { + scroll_screen(cons, SCROLL_UP); + } else { + cons->c_row++; + } + flush(cons); + return; + + case '\r': /* carriage return */ + cons->c_column = 0; + flush(cons); + return; + + case '\t': /* tab */ + cons->c_column = (cons->c_column + TAB_SIZE) & ~TAB_MASK; + if (cons->c_column > scr_width) { + cons->c_column -= scr_width; + if (cons->c_row == scr_lines-1) { + scroll_screen(cons, SCROLL_UP); + } else { + cons->c_row++; + } + } + flush(cons); + return; + + case 033: /* ESC - start of an escape sequence */ + flush(cons); /* print any chars queued for output */ + cons->c_esc_state = 1; /* mark ESC as seen */ + return; + + default: /* printable chars are stored in ramqueue */ + if (cons->c_column >= scr_width) { + if (!LINEWRAP) return; + if (cons->c_row == scr_lines-1) { + scroll_screen(cons, SCROLL_UP); + } else { + cons->c_row++; + } + cons->c_column = 0; + flush(cons); + } + if (cons->c_rwords == buflen(cons->c_ramqueue)) flush(cons); + cons->c_ramqueue[cons->c_rwords++] = cons->c_attr | (c & BYTE); + cons->c_column++; /* next column */ + return; + } +} + + +/*===========================================================================* + * scroll_screen * + *===========================================================================*/ +PRIVATE void scroll_screen(cons, dir) +register console_t *cons; /* pointer to console struct */ +int dir; /* SCROLL_UP or SCROLL_DOWN */ +{ + unsigned new_line, new_org, chars; + + flush(cons); + chars = scr_size - scr_width; /* one screen minus one line */ + + /* Scrolling the screen is a real nuisance due to the various incompatible + * video cards. This driver supports software scrolling (Hercules?), + * hardware scrolling (mono and CGA cards) and hardware scrolling without + * wrapping (EGA cards). In the latter case we must make sure that + * c_start <= c_org && c_org + scr_size <= c_limit + * holds, because EGA doesn't wrap around the end of video memory. + */ + if (dir == SCROLL_UP) { + /* Scroll one line up in 3 ways: soft, avoid wrap, use origin. */ + if (softscroll) { + vid_vid_copy(cons->c_start + scr_width, cons->c_start, chars); + } else + if (!wrap && cons->c_org + scr_size + scr_width >= cons->c_limit) { + vid_vid_copy(cons->c_org + scr_width, cons->c_start, chars); + cons->c_org = cons->c_start; + } else { + cons->c_org = (cons->c_org + scr_width) & vid_mask; + } + new_line = (cons->c_org + chars) & vid_mask; + } else { + /* Scroll one line down in 3 ways: soft, avoid wrap, use origin. */ + if (softscroll) { + vid_vid_copy(cons->c_start, cons->c_start + scr_width, chars); + } else + if (!wrap && cons->c_org < cons->c_start + scr_width) { + new_org = cons->c_limit - scr_size; + vid_vid_copy(cons->c_org, new_org + scr_width, chars); + cons->c_org = new_org; + } else { + cons->c_org = (cons->c_org - scr_width) & vid_mask; + } + new_line = cons->c_org; + } + /* Blank the new line at top or bottom. */ + blank_color = cons->c_blank; + mem_vid_copy(BLANK_MEM, new_line, scr_width); + + /* Set the new video origin. */ + if (cons == curcons) set_6845(VID_ORG, cons->c_org); + flush(cons); +} + + +/*===========================================================================* + * flush * + *===========================================================================*/ +PRIVATE void flush(cons) +register console_t *cons; /* pointer to console struct */ +{ +/* Send characters buffered in 'ramqueue' to screen memory, check the new + * cursor position, compute the new hardware cursor position and set it. + */ + unsigned cur; + tty_t *tp = cons->c_tty; + + /* Have the characters in 'ramqueue' transferred to the screen. */ + if (cons->c_rwords > 0) { + mem_vid_copy(cons->c_ramqueue, cons->c_cur, cons->c_rwords); + cons->c_rwords = 0; + + /* TTY likes to know the current column and if echoing messed up. */ + tp->tty_position = cons->c_column; + tp->tty_reprint = TRUE; + } + + /* Check and update the cursor position. */ + if (cons->c_column < 0) cons->c_column = 0; + if (cons->c_column > scr_width) cons->c_column = scr_width; + if (cons->c_row < 0) cons->c_row = 0; + if (cons->c_row >= scr_lines) cons->c_row = scr_lines - 1; + cur = cons->c_org + cons->c_row * scr_width + cons->c_column; + if (cur != cons->c_cur) { + if (cons == curcons) set_6845(CURSOR, cur); + cons->c_cur = cur; + } +} + + +/*===========================================================================* + * parse_escape * + *===========================================================================*/ +PRIVATE void parse_escape(cons, c) +register console_t *cons; /* pointer to console struct */ +char c; /* next character in escape sequence */ +{ +/* The following ANSI escape sequences are currently supported. + * If n and/or m are omitted, they default to 1. + * ESC [nA moves up n lines + * ESC [nB moves down n lines + * ESC [nC moves right n spaces + * ESC [nD moves left n spaces + * ESC [m;nH" moves cursor to (m,n) + * ESC [J clears screen from cursor + * ESC [K clears line from cursor + * ESC [nL inserts n lines ar cursor + * ESC [nM deletes n lines at cursor + * ESC [nP deletes n chars at cursor + * ESC [n@ inserts n chars at cursor + * ESC [nm enables rendition n (0=normal, 4=bold, 5=blinking, 7=reverse) + * ESC M scrolls the screen backwards if the cursor is on the top line + */ + + switch (cons->c_esc_state) { + case 1: /* ESC seen */ + cons->c_esc_intro = '\0'; + cons->c_esc_parmp = bufend(cons->c_esc_parmv); + do { + *--cons->c_esc_parmp = 0; + } while (cons->c_esc_parmp > cons->c_esc_parmv); + switch (c) { + case '[': /* Control Sequence Introducer */ + cons->c_esc_intro = c; + cons->c_esc_state = 2; + break; + case 'M': /* Reverse Index */ + do_escape(cons, c); + break; + default: + cons->c_esc_state = 0; + } + break; + + case 2: /* ESC [ seen */ + if (c >= '0' && c <= '9') { + if (cons->c_esc_parmp < bufend(cons->c_esc_parmv)) + *cons->c_esc_parmp = *cons->c_esc_parmp * 10 + (c-'0'); + } else + if (c == ';') { + if (cons->c_esc_parmp < bufend(cons->c_esc_parmv)) + cons->c_esc_parmp++; + } else { + do_escape(cons, c); + } + break; + } +} + + +/*===========================================================================* + * do_escape * + *===========================================================================*/ +PRIVATE void do_escape(cons, c) +register console_t *cons; /* pointer to console struct */ +char c; /* next character in escape sequence */ +{ + int value, n; + unsigned src, dst, count; + int *parmp; + + /* Some of these things hack on screen RAM, so it had better be up to date */ + flush(cons); + + if (cons->c_esc_intro == '\0') { + /* Handle a sequence beginning with just ESC */ + switch (c) { + case 'M': /* Reverse Index */ + if (cons->c_row == 0) { + scroll_screen(cons, SCROLL_DOWN); + } else { + cons->c_row--; + } + flush(cons); + break; + + default: break; + } + } else + if (cons->c_esc_intro == '[') { + /* Handle a sequence beginning with ESC [ and parameters */ + value = cons->c_esc_parmv[0]; + switch (c) { + case 'A': /* ESC [nA moves up n lines */ + n = (value == 0 ? 1 : value); + cons->c_row -= n; + flush(cons); + break; + + case 'B': /* ESC [nB moves down n lines */ + n = (value == 0 ? 1 : value); + cons->c_row += n; + flush(cons); + break; + + case 'C': /* ESC [nC moves right n spaces */ + n = (value == 0 ? 1 : value); + cons->c_column += n; + flush(cons); + break; + + case 'D': /* ESC [nD moves left n spaces */ + n = (value == 0 ? 1 : value); + cons->c_column -= n; + flush(cons); + break; + + case 'H': /* ESC [m;nH" moves cursor to (m,n) */ + cons->c_row = cons->c_esc_parmv[0] - 1; + cons->c_column = cons->c_esc_parmv[1] - 1; + flush(cons); + break; + + case 'J': /* ESC [sJ clears in display */ + switch (value) { + case 0: /* Clear from cursor to end of screen */ + count = scr_size - (cons->c_cur - cons->c_org); + dst = cons->c_cur; + break; + case 1: /* Clear from start of screen to cursor */ + count = cons->c_cur - cons->c_org; + dst = cons->c_org; + break; + case 2: /* Clear entire screen */ + count = scr_size; + dst = cons->c_org; + break; + default: /* Do nothing */ + count = 0; + dst = cons->c_org; + } + blank_color = cons->c_blank; + mem_vid_copy(BLANK_MEM, dst, count); + break; + + case 'K': /* ESC [sK clears line from cursor */ + switch (value) { + case 0: /* Clear from cursor to end of line */ + count = scr_width - cons->c_column; + dst = cons->c_cur; + break; + case 1: /* Clear from beginning of line to cursor */ + count = cons->c_column; + dst = cons->c_cur - cons->c_column; + break; + case 2: /* Clear entire line */ + count = scr_width; + dst = cons->c_cur - cons->c_column; + break; + default: /* Do nothing */ + count = 0; + dst = cons->c_cur; + } + blank_color = cons->c_blank; + mem_vid_copy(BLANK_MEM, dst, count); + break; + + case 'L': /* ESC [nL inserts n lines at cursor */ + n = value; + if (n < 1) n = 1; + if (n > (scr_lines - cons->c_row)) + n = scr_lines - cons->c_row; + + src = cons->c_org + cons->c_row * scr_width; + dst = src + n * scr_width; + count = (scr_lines - cons->c_row - n) * scr_width; + vid_vid_copy(src, dst, count); + blank_color = cons->c_blank; + mem_vid_copy(BLANK_MEM, src, n * scr_width); + break; + + case 'M': /* ESC [nM deletes n lines at cursor */ + n = value; + if (n < 1) n = 1; + if (n > (scr_lines - cons->c_row)) + n = scr_lines - cons->c_row; + + dst = cons->c_org + cons->c_row * scr_width; + src = dst + n * scr_width; + count = (scr_lines - cons->c_row - n) * scr_width; + vid_vid_copy(src, dst, count); + blank_color = cons->c_blank; + mem_vid_copy(BLANK_MEM, dst + count, n * scr_width); + break; + + case '@': /* ESC [n@ inserts n chars at cursor */ + n = value; + if (n < 1) n = 1; + if (n > (scr_width - cons->c_column)) + n = scr_width - cons->c_column; + + src = cons->c_cur; + dst = src + n; + count = scr_width - cons->c_column - n; + vid_vid_copy(src, dst, count); + blank_color = cons->c_blank; + mem_vid_copy(BLANK_MEM, src, n); + break; + + case 'P': /* ESC [nP deletes n chars at cursor */ + n = value; + if (n < 1) n = 1; + if (n > (scr_width - cons->c_column)) + n = scr_width - cons->c_column; + + dst = cons->c_cur; + src = dst + n; + count = scr_width - cons->c_column - n; + vid_vid_copy(src, dst, count); + blank_color = cons->c_blank; + mem_vid_copy(BLANK_MEM, dst + count, n); + break; + + case 'm': /* ESC [nm enables rendition n */ + for (parmp = cons->c_esc_parmv; parmp <= cons->c_esc_parmp + && parmp < bufend(cons->c_esc_parmv); parmp++) { + if (cons->c_reverse) { + /* Unswap fg and bg colors */ + cons->c_attr = ((cons->c_attr & 0x7000) >> 4) | + ((cons->c_attr & 0x0700) << 4) | + ((cons->c_attr & 0x8800)); + } + switch (n = *parmp) { + case 0: /* NORMAL */ + cons->c_attr = cons->c_blank = BLANK_COLOR; + cons->c_reverse = FALSE; + break; + + case 1: /* BOLD */ + /* Set intensity bit */ + cons->c_attr |= 0x0800; + break; + + case 4: /* UNDERLINE */ + if (color) { + /* Change white to cyan, i.e. lose red + */ + cons->c_attr = (cons->c_attr & 0xBBFF); + } else { + /* Set underline attribute */ + cons->c_attr = (cons->c_attr & 0x99FF); + } + break; + + case 5: /* BLINKING */ + /* Set the blink bit */ + cons->c_attr |= 0x8000; + break; + + case 7: /* REVERSE */ + cons->c_reverse = TRUE; + break; + + default: /* COLOR */ + if (n == 39) n = 37; /* set default color */ + if (n == 49) n = 40; + + if (!color) { + /* Don't mess up a monochrome screen */ + } else + if (30 <= n && n <= 37) { + /* Foreground color */ + cons->c_attr = + (cons->c_attr & 0xF8FF) | + (ansi_colors[(n - 30)] << 8); + cons->c_blank = + (cons->c_blank & 0xF8FF) | + (ansi_colors[(n - 30)] << 8); + } else + if (40 <= n && n <= 47) { + /* Background color */ + cons->c_attr = + (cons->c_attr & 0x8FFF) | + (ansi_colors[(n - 40)] << 12); + cons->c_blank = + (cons->c_blank & 0x8FFF) | + (ansi_colors[(n - 40)] << 12); + } + } + if (cons->c_reverse) { + /* Swap fg and bg colors */ + cons->c_attr = ((cons->c_attr & 0x7000) >> 4) | + ((cons->c_attr & 0x0700) << 4) | + ((cons->c_attr & 0x8800)); + } + } + break; + } + } + cons->c_esc_state = 0; +} + + +/*===========================================================================* + * set_6845 * + *===========================================================================*/ +PRIVATE void set_6845(reg, val) +int reg; /* which register pair to set */ +unsigned val; /* 16-bit value to set it to */ +{ +/* Set a register pair inside the 6845. + * Registers 12-13 tell the 6845 where in video ram to start + * Registers 14-15 tell the 6845 where to put the cursor + */ + pvb_pair_t char_out[4]; + pv_set(char_out[0], vid_port + INDEX, reg); /* set index register */ + pv_set(char_out[1], vid_port + DATA, (val>>8) & BYTE); /* high byte */ + pv_set(char_out[2], vid_port + INDEX, reg + 1); /* again */ + pv_set(char_out[3], vid_port + DATA, val&BYTE); /* low byte */ + sys_voutb(char_out, 4); /* do actual output */ +} + + +/*===========================================================================* + * beep * + *===========================================================================*/ +PRIVATE void beep() +{ +/* Making a beeping sound on the speaker (output for CRTL-G). + * This routine works by turning on the bits 0 and 1 in port B of the 8255 + * chip that drive the speaker. + */ + static timer_t tmr_stop_beep; + pvb_pair_t char_out[3]; + clock_t now; + int port_b_val, s; + + /* Fetch current time in advance to prevent beeping delay. */ + if ((s=sys_getuptime(&now)) != OK) + server_panic("TTY","Console couldn't get clock's uptime.", s); + if (!beeping) { + /* Set timer channel 2, square wave, with given frequency. */ + pv_set(char_out[0], TIMER_MODE, 0xB6); + pv_set(char_out[1], TIMER2, (BEEP_FREQ >> 0) & BYTE); + pv_set(char_out[2], TIMER2, (BEEP_FREQ >> 8) & BYTE); + if (sys_voutb(char_out, 3)==OK) { + if (sys_inb(PORT_B, &port_b_val)==OK && + sys_outb(PORT_B, (port_b_val|3))==OK) + beeping = TRUE; + } + } + /* Add a timer to the timers list. Possibly reschedule the alarm. */ + tmrs_settimer(&tty_timers, &tmr_stop_beep, now+B_TIME, stop_beep); + if (tty_timers->tmr_exp_time != tty_next_timeout) { + tty_next_timeout = tty_timers->tmr_exp_time; + if ((s=sys_syncalrm(SELF, tty_next_timeout, 1)) != OK) + server_panic("TTY","Console couldn't set syn alarm.", s); + } +} + + +/*===========================================================================* + * stop_beep * + *===========================================================================*/ +PRIVATE void stop_beep(tmrp) +timer_t *tmrp; +{ +/* Turn off the beeper by turning off bits 0 and 1 in PORT_B. */ + int port_b_val; + if (sys_inb(PORT_B, &port_b_val)==OK && + sys_outb(PORT_B, (port_b_val & ~3))==OK) + beeping = FALSE; +} + + +/*===========================================================================* + * scr_init * + *===========================================================================*/ +PUBLIC void scr_init(tp) +tty_t *tp; +{ +/* Initialize the screen driver. */ + console_t *cons; + phys_bytes vid_base; + u16_t bios_columns, bios_crtbase, bios_fontlines; + u8_t bios_rows; + int line; + unsigned page_size; + int s; + static int vdu_initialized = 0; + + /* Associate console and TTY. */ + line = tp - &tty_table[0]; + if (line >= nr_cons) return; + cons = &cons_table[line]; + cons->c_tty = tp; + tp->tty_priv = cons; + + /* Initialize the keyboard driver. */ + kb_init(tp); + + /* Fill in TTY function hooks. */ + tp->tty_devwrite = cons_write; + tp->tty_echo = cons_echo; + tp->tty_ioctl = cons_ioctl; + + /* Get the BIOS parameters that describe the VDU. */ + if (! vdu_initialized++) { + + /* How about error checking? What to do on failure??? */ + s=sys_vircopy(SELF, BIOS_SEG, (vir_bytes) 0x44AL, + SELF, D, (vir_bytes) &bios_columns, 2L); + s=sys_vircopy(SELF, BIOS_SEG, (vir_bytes) 0x463L, + SELF, D, (vir_bytes) &bios_crtbase, 2L); + s=sys_vircopy(SELF, BIOS_SEG, (vir_bytes) 0x484L, + SELF, D, (vir_bytes) &bios_rows, 1L); + s=sys_vircopy(SELF, BIOS_SEG, (vir_bytes) 0x485L, + SELF, D, (vir_bytes) &bios_fontlines, 2L); + + vid_port = bios_crtbase; + scr_width = bios_columns; + font_lines = bios_fontlines; + scr_lines = kenv.ega ? bios_rows+1 : 25; + + if (color) { + vid_base = COLOR_BASE; + vid_size = COLOR_SIZE; + } else { + vid_base = MONO_BASE; + vid_size = MONO_SIZE; + } + if (kenv.ega) vid_size = EGA_SIZE; + wrap = !kenv.ega; + + s = sys_phys2seg(&vid_seg, &vid_off, vid_base, vid_size); + + vid_size >>= 1; /* word count */ + vid_mask = vid_size - 1; + + /* Size of the screen (number of displayed characters.) */ + scr_size = scr_lines * scr_width; + + /* There can be as many consoles as video memory allows. */ + nr_cons = vid_size / scr_size; + if (nr_cons > NR_CONS) nr_cons = NR_CONS; + if (nr_cons > 1) wrap = 0; + page_size = vid_size / nr_cons; + } + + cons->c_start = line * page_size; + cons->c_limit = cons->c_start + page_size; + cons->c_cur = cons->c_org = cons->c_start; + cons->c_attr = cons->c_blank = BLANK_COLOR; + + /* Clear the screen. */ + blank_color = BLANK_COLOR; + mem_vid_copy(BLANK_MEM, cons->c_start, scr_size); + select_console(0); + cons_ioctl(tp); +} + +/*==========================================================================* + * kputc * + *==========================================================================*/ +PUBLIC void kputc(c) +int c; +{ + putk(c); +} + + +/*==========================================================================* + * do_new_kmess * + *==========================================================================*/ +PUBLIC void do_new_kmess(m) +message *m; +{ +/* Notification for a new kernel message. */ + struct kmessages kmess; /* kmessages structure */ + static int prev_next = 0; /* previous next seen */ + int size, next; + int bytes; + int r; + + /* Try to get a fresh copy of the buffer with kernel messages. */ + if ((r=sys_getkmessages(&kmess)) != OK) { + printf("TTY: couldn't get copy of kmessages: %d\n", r); + return; + } + + /* Print only the new part. Determine how many new bytes there are with + * help of the current and previous 'next' index. Note that the kernel + * buffer is circular. This works fine if less then KMESS_BUF_SIZE bytes + * is new data; else we miss % KMESS_BUF_SIZE here. + * Check for size being positive, the buffer might as well be emptied! + */ + if (kmess.km_size > 0) { + bytes = ((kmess.km_next + KMESS_BUF_SIZE) - prev_next) % KMESS_BUF_SIZE; + r=prev_next; /* start at previous old */ + while (bytes > 0) { + putk( kmess.km_buf[(r%KMESS_BUF_SIZE)] ); + bytes --; + r ++; + } + putk(0); /* terminate to flush output */ + } + + /* Almost done, store 'next' so that we can determine what part of the + * kernel messages buffer to print next time a notification arrives. + */ + prev_next = kmess.km_next; +} + +/*==========================================================================* + * do_diagnostics * + *==========================================================================*/ +PUBLIC void do_diagnostics(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* Print a string for a server. */ + char c; + vir_bytes src; + int count; + int result = OK; + int proc_nr = m_ptr->DIAG_PROC_NR; + if (proc_nr == SELF) proc_nr = m_ptr->m_source; + + src = (vir_bytes) m_ptr->DIAG_PRINT_BUF; + for (count = m_ptr->DIAG_BUF_COUNT; count > 0; count--) { + if (sys_vircopy(proc_nr, D, src++, SELF, D, (vir_bytes) &c, 1) != OK) { + result = EFAULT; + break; + } + putk(c); + } + putk(0); /* always terminate, even with EFAULT */ + m_ptr->m_type = result; + send(m_ptr->m_source, m_ptr); +} + + +/*===========================================================================* + * putk * + *===========================================================================*/ +PRIVATE void putk(c) +int c; /* character to print */ +{ +/* This procedure is used by the version of printf() that is linked with + * the TTY driver. The one in the library sends a message to FS, which is + * not what is needed for printing within the TTY. This version just queues + * the character and starts the output. + */ + if (c != 0) { + if (c == '\n') putk('\r'); + out_char(&cons_table[0], (int) c); + } else { + flush(&cons_table[0]); + } +} + + +/*===========================================================================* + * toggle_scroll * + *===========================================================================*/ +PUBLIC void toggle_scroll() +{ +/* Toggle between hardware and software scroll. */ + + cons_org0(); + softscroll = !softscroll; + printf("%sware scrolling enabled.\n", softscroll ? "Soft" : "Hard"); +} + + +/*===========================================================================* + * cons_stop * + *===========================================================================*/ +PUBLIC void cons_stop() +{ +/* Prepare for halt or reboot. */ + cons_org0(); + softscroll = 1; + select_console(0); + cons_table[0].c_attr = cons_table[0].c_blank = BLANK_COLOR; +} + + +/*===========================================================================* + * cons_org0 * + *===========================================================================*/ +PRIVATE void cons_org0() +{ +/* Scroll video memory back to put the origin at 0. */ + int cons_line; + console_t *cons; + unsigned n; + + for (cons_line = 0; cons_line < nr_cons; cons_line++) { + cons = &cons_table[cons_line]; + while (cons->c_org > cons->c_start) { + n = vid_size - scr_size; /* amount of unused memory */ + if (n > cons->c_org - cons->c_start) + n = cons->c_org - cons->c_start; + vid_vid_copy(cons->c_org, cons->c_org - n, scr_size); + cons->c_org -= n; + } + flush(cons); + } + select_console(ccurrent); +} + + +/*===========================================================================* + * select_console * + *===========================================================================*/ +PUBLIC void select_console(int cons_line) +{ +/* Set the current console to console number 'cons_line'. */ + + if (cons_line < 0 || cons_line >= nr_cons) return; + ccurrent = cons_line; + curcons = &cons_table[cons_line]; + set_6845(VID_ORG, curcons->c_org); + set_6845(CURSOR, curcons->c_cur); +} + + +/*===========================================================================* + * con_loadfont * + *===========================================================================*/ +PUBLIC int con_loadfont(m) +message *m; +{ +/* Load a font into the EGA or VGA adapter. */ + int result; + static struct sequence seq1[7] = { + { GA_SEQUENCER_INDEX, 0x00, 0x01 }, + { GA_SEQUENCER_INDEX, 0x02, 0x04 }, + { GA_SEQUENCER_INDEX, 0x04, 0x07 }, + { GA_SEQUENCER_INDEX, 0x00, 0x03 }, + { GA_GRAPHICS_INDEX, 0x04, 0x02 }, + { GA_GRAPHICS_INDEX, 0x05, 0x00 }, + { GA_GRAPHICS_INDEX, 0x06, 0x00 }, + }; + static struct sequence seq2[7] = { + { GA_SEQUENCER_INDEX, 0x00, 0x01 }, + { GA_SEQUENCER_INDEX, 0x02, 0x03 }, + { GA_SEQUENCER_INDEX, 0x04, 0x03 }, + { GA_SEQUENCER_INDEX, 0x00, 0x03 }, + { GA_GRAPHICS_INDEX, 0x04, 0x00 }, + { GA_GRAPHICS_INDEX, 0x05, 0x10 }, + { GA_GRAPHICS_INDEX, 0x06, 0 }, + }; + + seq2[6].value= color ? 0x0E : 0x0A; + + if (!kenv.ega) return(ENOTTY); + result = ga_program(seq1); /* bring font memory into view */ + + result = sys_copy(m->PROC_NR, D, (vir_bytes) m->ADDRESS, + ABS, 0, (phys_bytes) GA_VIDEO_ADDRESS, (phys_bytes)GA_FONT_SIZE); + + result = ga_program(seq2); /* restore */ + + return(result); +} + + +/*===========================================================================* + * ga_program * + *===========================================================================*/ +PRIVATE int ga_program(seq) +struct sequence *seq; +{ + pvb_pair_t char_out[14]; + int i; + for (i=0; i<7; i++) { + pv_set(char_out[2*i], seq->index, seq->port); + pv_set(char_out[2*i+1], seq->index+1, seq->value); + seq++; + } + return sys_voutb(char_out, 14); +} + + +/*===========================================================================* + * cons_ioctl * + *===========================================================================*/ +PRIVATE void cons_ioctl(tp) +tty_t *tp; +{ +/* Set the screen dimensions. */ + + tp->tty_winsize.ws_row= scr_lines; + tp->tty_winsize.ws_col= scr_width; + tp->tty_winsize.ws_xpixel= scr_width * 8; + tp->tty_winsize.ws_ypixel= scr_lines * font_lines; +} diff --git a/drivers/tty/keyboard.c b/drivers/tty/keyboard.c new file mode 100644 index 000000000..06bfde611 --- /dev/null +++ b/drivers/tty/keyboard.c @@ -0,0 +1,600 @@ +/* Keyboard driver for PC's and AT's. + * + * Changes: + * Jul 13, 2004 processes can observe function keys (Jorrit N. Herder) + * Jun 15, 2004 removed wreboot(), except panic dumps (Jorrit N. Herder) + * Feb 04, 1994 loadable keymaps (Marcus Hampel) + */ + +#include "../drivers.h" +#include <termios.h> +#include <signal.h> +#include <unistd.h> +#include <minix/callnr.h> +#include <minix/com.h> +#include <minix/keymap.h> +#include "tty.h" +#include "keymaps/us-std.src" +#include "../../kernel/kernel.h" +#include "../../kernel/proc.h" + +/* Standard and AT keyboard. (PS/2 MCA implies AT throughout.) */ +#define KEYBD 0x60 /* I/O port for keyboard data */ + +/* AT keyboard. */ +#define KB_COMMAND 0x64 /* I/O port for commands on AT */ +#define KB_STATUS 0x64 /* I/O port for status on AT */ +#define KB_ACK 0xFA /* keyboard ack response */ +#define KB_OUT_FULL 0x01 /* status bit set when keypress char pending */ +#define KB_IN_FULL 0x02 /* status bit set when not ready to receive */ +#define LED_CODE 0xED /* command to keyboard to set LEDs */ +#define MAX_KB_ACK_RETRIES 0x1000 /* max #times to wait for kb ack */ +#define MAX_KB_BUSY_RETRIES 0x1000 /* max #times to loop while kb busy */ +#define KBIT 0x80 /* bit used to ack characters to keyboard */ + +/* Miscellaneous. */ +#define ESC_SCAN 0x01 /* reboot key when panicking */ +#define SLASH_SCAN 0x35 /* to recognize numeric slash */ +#define RSHIFT_SCAN 0x36 /* to distinguish left and right shift */ +#define HOME_SCAN 0x47 /* first key on the numeric keypad */ +#define INS_SCAN 0x52 /* INS for use in CTRL-ALT-INS reboot */ +#define DEL_SCAN 0x53 /* DEL for use in CTRL-ALT-DEL reboot */ + +#define CONSOLE 0 /* line number for console */ +#define KB_IN_BYTES 32 /* size of keyboard input buffer */ +PRIVATE char ibuf[KB_IN_BYTES]; /* input buffer */ +PRIVATE char *ihead = ibuf; /* next free spot in input buffer */ +PRIVATE char *itail = ibuf; /* scan code to return to TTY */ +PRIVATE int icount; /* # codes in buffer */ + +PRIVATE int esc; /* escape scan code detected? */ +PRIVATE int alt_l; /* left alt key state */ +PRIVATE int alt_r; /* right alt key state */ +PRIVATE int alt; /* either alt key */ +PRIVATE int ctrl_l; /* left control key state */ +PRIVATE int ctrl_r; /* right control key state */ +PRIVATE int ctrl; /* either control key */ +PRIVATE int shift_l; /* left shift key state */ +PRIVATE int shift_r; /* right shift key state */ +PRIVATE int shift; /* either shift key */ +PRIVATE int num_down; /* num lock key depressed */ +PRIVATE int caps_down; /* caps lock key depressed */ +PRIVATE int scroll_down; /* scroll lock key depressed */ +PRIVATE int locks[NR_CONS]; /* per console lock keys state */ + +/* Lock key active bits. Chosen to be equal to the keyboard LED bits. */ +#define SCROLL_LOCK 0x01 +#define NUM_LOCK 0x02 +#define CAPS_LOCK 0x04 + +PRIVATE char numpad_map[] = + {'H', 'Y', 'A', 'B', 'D', 'C', 'V', 'U', 'G', 'S', 'T', '@'}; + +/* Variables and definition for observed function keys. */ +PRIVATE int fkey_obs[12]; /* observers for F1-F12 */ +PRIVATE int sfkey_obs[12]; /* observers for SHIFT F1-F12 */ + +FORWARD _PROTOTYPE( int kb_ack, (void) ); +FORWARD _PROTOTYPE( int kb_wait, (void) ); +FORWARD _PROTOTYPE( int func_key, (int scode) ); +FORWARD _PROTOTYPE( int scan_keyboard, (void) ); +FORWARD _PROTOTYPE( unsigned make_break, (int scode) ); +FORWARD _PROTOTYPE( void set_leds, (void) ); +FORWARD _PROTOTYPE( void kb_read, (struct tty *tp) ); +FORWARD _PROTOTYPE( unsigned map_key, (int scode) ); + + +/*===========================================================================* + * map_key0 * + *===========================================================================*/ +/* Map a scan code to an ASCII code ignoring modifiers. */ +#define map_key0(scode) \ + ((unsigned) keymap[(scode) * MAP_COLS]) + + +/*===========================================================================* + * map_key * + *===========================================================================*/ +PRIVATE unsigned map_key(scode) +int scode; +{ +/* Map a scan code to an ASCII code. */ + + int caps, column, lk; + u16_t *keyrow; + + if (scode == SLASH_SCAN && esc) return '/'; /* don't map numeric slash */ + + keyrow = &keymap[scode * MAP_COLS]; + + caps = shift; + lk = locks[ccurrent]; + if ((lk & NUM_LOCK) && HOME_SCAN <= scode && scode <= DEL_SCAN) caps = !caps; + if ((lk & CAPS_LOCK) && (keyrow[0] & HASCAPS)) caps = !caps; + + if (alt) { + column = 2; + if (ctrl || alt_r) column = 3; /* Ctrl + Alt == AltGr */ + if (caps) column = 4; + } else { + column = 0; + if (caps) column = 1; + if (ctrl) column = 5; + } + return keyrow[column] & ~HASCAPS; +} + +/*===========================================================================* + * kbd_hw_int * + *===========================================================================*/ +PUBLIC void do_interrupt(m_ptr) +message *m_ptr; +{ +/* A keyboard interrupt has occurred. Process it. */ + int scode; + static timer_t timer; /* timer must be static! */ + + /* Fetch the character from the keyboard hardware and acknowledge it. */ + scode = scan_keyboard(); + + /* Store the scancode in memory so the task can get at it later. */ + if (icount < KB_IN_BYTES) { + *ihead++ = scode; + if (ihead == ibuf + KB_IN_BYTES) ihead = ibuf; + icount++; + tty_table[ccurrent].tty_events = 1; + } +} + + +/*==========================================================================* + * kb_read * + *==========================================================================*/ +PRIVATE void kb_read(tp) +tty_t *tp; +{ +/* Process characters from the circular keyboard buffer. */ + + char buf[3]; + int scode; + unsigned ch; + + tp = &tty_table[ccurrent]; /* always use the current console */ + + while (icount > 0) { + scode = *itail++; /* take one key scan code */ + if (itail == ibuf + KB_IN_BYTES) itail = ibuf; + icount--; + + /* Function keys are being used for debug dumps. */ + if (func_key(scode)) continue; + + /* Perform make/break processing. */ + ch = make_break(scode); + + if (ch <= 0xFF) { + /* A normal character. */ + buf[0] = ch; + (void) in_process(tp, buf, 1); + } else + if (HOME <= ch && ch <= INSRT) { + /* An ASCII escape sequence generated by the numeric pad. */ + buf[0] = ESC; + buf[1] = '['; + buf[2] = numpad_map[ch - HOME]; + (void) in_process(tp, buf, 3); + } else + if (ch == ALEFT) { + /* Choose lower numbered console as current console. */ + select_console(ccurrent - 1); + set_leds(); + } else + if (ch == ARIGHT) { + /* Choose higher numbered console as current console. */ + select_console(ccurrent + 1); + set_leds(); + } else + if (AF1 <= ch && ch <= AF12) { + /* Alt-F1 is console, Alt-F2 is ttyc1, etc. */ + select_console(ch - AF1); + set_leds(); + } else + if (CF1 <= ch && ch <= CF12) { + switch(ch) { + case CF3: toggle_scroll(); break; /* hardware <-> software */ + case CF7: sigchar(&tty_table[CONSOLE], SIGQUIT); break; + case CF8: sigchar(&tty_table[CONSOLE], SIGINT); break; + case CF9: sigchar(&tty_table[CONSOLE], SIGKILL); break; + } + } + } +} + + +/*===========================================================================* + * make_break * + *===========================================================================*/ +PRIVATE unsigned make_break(scode) +int scode; /* scan code of key just struck or released */ +{ +/* This routine can handle keyboards that interrupt only on key depression, + * as well as keyboards that interrupt on key depression and key release. + * For efficiency, the interrupt routine filters out most key releases. + */ + int ch, make, escape; + static int CAD_count = 0; + + /* Check for CTRL-ALT-DEL, and if found, halt the computer. This would + * be better done in keyboard() in case TTY is hung, except control and + * alt are set in the high level code. + */ + if (ctrl && alt && (scode == DEL_SCAN || scode == INS_SCAN)) + { + if (++CAD_count == 3) sys_abort(RBT_HALT); + sys_kill(INIT_PROC_NR, SIGABRT); + return -1; + } + + /* High-order bit set on key release. */ + make = (scode & KEY_RELEASE) == 0; /* true if pressed */ + + ch = map_key(scode &= ASCII_MASK); /* map to ASCII */ + + escape = esc; /* Key is escaped? (true if added since the XT) */ + esc = 0; + + switch (ch) { + case CTRL: /* Left or right control key */ + *(escape ? &ctrl_r : &ctrl_l) = make; + ctrl = ctrl_l | ctrl_r; + break; + case SHIFT: /* Left or right shift key */ + *(scode == RSHIFT_SCAN ? &shift_r : &shift_l) = make; + shift = shift_l | shift_r; + break; + case ALT: /* Left or right alt key */ + *(escape ? &alt_r : &alt_l) = make; + alt = alt_l | alt_r; + break; + case CALOCK: /* Caps lock - toggle on 0 -> 1 transition */ + if (caps_down < make) { + locks[ccurrent] ^= CAPS_LOCK; + set_leds(); + } + caps_down = make; + break; + case NLOCK: /* Num lock */ + if (num_down < make) { + locks[ccurrent] ^= NUM_LOCK; + set_leds(); + } + num_down = make; + break; + case SLOCK: /* Scroll lock */ + if (scroll_down < make) { + locks[ccurrent] ^= SCROLL_LOCK; + set_leds(); + } + scroll_down = make; + break; + case EXTKEY: /* Escape keycode */ + esc = 1; /* Next key is escaped */ + return(-1); + default: /* A normal key */ + if (make) return(ch); + } + + /* Key release, or a shift type key. */ + return(-1); +} + + +/*===========================================================================* + * set_leds * + *===========================================================================*/ +PRIVATE void set_leds() +{ +/* Set the LEDs on the caps, num, and scroll lock keys */ + int s; + if (!kenv.pc_at) return; /* PC/XT doesn't have LEDs */ + + kb_wait(); /* wait for buffer empty */ + if ((s=sys_outb(KEYBD, LED_CODE)) != OK) + printf("Warning, sys_outb couldn't prepare for LED values: %d\n", s); + /* prepare keyboard to accept LED values */ + kb_ack(); /* wait for ack response */ + + kb_wait(); /* wait for buffer empty */ + if ((s=sys_outb(KEYBD, locks[ccurrent])) != OK) + printf("Warning, sys_outb couldn't give LED values: %d\n", s); + /* give keyboard LED values */ + kb_ack(); /* wait for ack response */ +} + + +/*==========================================================================* + * kb_wait * + *==========================================================================*/ +PRIVATE int kb_wait() +{ +/* Wait until the controller is ready; return zero if this times out. */ + + int retries, status, temp; + int s; + + retries = MAX_KB_BUSY_RETRIES + 1; /* wait until not busy */ + do { + s = sys_inb(KB_STATUS, &status); + if (status & KB_OUT_FULL) { + s = sys_inb(KEYBD, &temp); /* discard value */ + } + if (! (status & (KB_IN_FULL|KB_OUT_FULL)) ) + break; /* wait until ready */ + } while (--retries != 0); /* continue unless timeout */ + return(retries); /* zero on timeout, positive if ready */ +} + + +/*==========================================================================* + * kb_ack * + *==========================================================================*/ +PRIVATE int kb_ack() +{ +/* Wait until kbd acknowledges last command; return zero if this times out. */ + + int retries, s; + u8_t u8val; + + retries = MAX_KB_ACK_RETRIES + 1; + do { + s = sys_inb(KEYBD, &u8val); + if (u8val == KB_ACK) + break; /* wait for ack */ + } while(--retries != 0); /* continue unless timeout */ + + return(retries); /* nonzero if ack received */ +} + +/*===========================================================================* + * kb_init * + *===========================================================================*/ +PUBLIC void kb_init(tp) +tty_t *tp; +{ +/* Initialize the keyboard driver. */ + static int count = 0; + int i; + + tp->tty_devread = kb_read; /* input function */ + set_leds(); /* turn off numlock led */ + scan_keyboard(); /* discard leftover keystroke */ + + /* The following initialization should only run once. */ + if (! count ++) { + + /* Clear the function key observers array. Also see func_key(). */ + for (i=0; i<12; i++) { + fkey_obs[i] = NONE; /* F1-F12 observers */ + sfkey_obs[i] = NONE; /* Shift F1-F12 observers */ + } + + /* Set interrupt handler and enable keyboard IRQ. */ + if ((i=sys_irqsetpolicy(KEYBOARD_IRQ, IRQ_REENABLE, SELF, 0, 0, 0)) != OK) + server_panic("TTY", "Couldn't set keyboard IRQ policy", i); + if ((i=sys_irqenable(KEYBOARD_IRQ)) != OK) + server_panic("TTY", "Couldn't enable keyboard IRQs", i); + } +} + + +/*===========================================================================* + * kbd_loadmap * + *===========================================================================*/ +PUBLIC int kbd_loadmap(m) +message *m; +{ +/* Load a new keymap. */ + int result; + result = sys_vircopy(m->PROC_NR, D, (vir_bytes) m->ADDRESS, + SELF, D, (vir_bytes) keymap, + (vir_bytes) sizeof(keymap)); + return(result); +} + + +/*===========================================================================* + * do_fkey_ctl * + *===========================================================================*/ +PUBLIC void do_fkey_ctl(m_ptr) +message *m_ptr; /* pointer to the request message */ +{ +/* This procedure allows processes to register a function key to receive + * notifications if it is pressed. At most one binding per key can exist. + */ + int fkey = m_ptr->FKEY_CODE; /* get function key code */ + unsigned code; + int *observers = NULL; + int index = -1; + int result; + + /* See if this key can be observed; get the observers array and index. */ + if (SF1 == fkey) result = EINVAL; /* Shift-F1 is TTY reserved */ + if (F1 <= fkey && fkey <= F12) { /* F1-F12 */ + observers = fkey_obs; + index = fkey - F1; + } else if (SF2 <= fkey && fkey <= SF12) { /* Shift F1-F12 */ + observers = sfkey_obs; + index = fkey - SF1; + } + + /* Handle the request if an observers array was set above. */ + if (observers) { + if (m_ptr->FKEY_ENABLE) { /* try to register an observer */ + if (observers[index] == NONE) { + observers[index] = m_ptr->m_source; + result = OK; /* done, new observer registered */ + } else { + result = EBUSY; /* function key already bound */ + } + } else { /* unregister an observer */ + if (observers[index] == m_ptr->m_source) { + observers[index] = NONE; + result = OK; /* done, observer unregistered */ + } else { + result = EPERM; /* can only remove own binding */ + } + } + } else { + result = EINVAL; /* key cannot be observed */ + } + + /* Almost done, return result to caller. */ + m_ptr->m_type = result; + send(m_ptr->m_source, m_ptr); +} + +/*===========================================================================* + * func_key * + *===========================================================================*/ +PRIVATE int func_key(scode) +int scode; /* scan code for a function key */ +{ +/* This procedure traps function keys for debugging purposes. Observers of + * function keys are kept in a global array. If a subject (a key) is pressed + * the observer is notified of the event. Initialization of the arrays is done + * in kb_init, where NONE is set to indicate there is no interest in the key. + * Returns FALSE on a key release or if the key is not observable. + */ + message m; + int *observers = NULL; + unsigned fkey; + int index = -1; + int i,s; + struct proc proc; + + /* Ignore key releases. If this is a key press, get full key code. */ + if (scode & KEY_RELEASE) return(FALSE); /* key release */ + fkey = map_key(scode); /* include modifiers */ + + /* Key pressed, now see if there is an observer for the pressed key. + * F1-F12 observers are in fkey_obs array. + * SHIFT F1-F12 observers are in sfkey_req array. + * CTRL F1-F12 reserved (see kb_read) + * ALT F1-F12 reserved (see kb_read) + * Other combinations are not in use. Note that Alt+Shift+F1-F12 is yet + * defined in <minix/keymap.h>, but other modifier combinations are not. + */ + if (SF1 == fkey) { + printf("\n"); + printf("System information. Known function key mappings to request debug dumps:\n"); + printf("-------------------------------------------------------------------------\n"); + for (i=0; i<12; i++) { + + printf(" %sF%d: ", i+1<10? " ":"", i+1); + if (fkey_obs[i] != NONE) { + if ((s=sys_getproc(&proc, fkey_obs[i]))!=OK) + printf("sys_getproc: %d\n", s); + printf("%-14.14s", proc.p_name); + } else { + printf("%-14.14s", "<none>"); + } + + printf(" %sShift-F%d: ", i+1<10? " ":"", i+1); + if (sfkey_obs[i] != NONE) { + if ((s=sys_getproc(&proc, sfkey_obs[i]))!=OK) + printf("sys_getproc: %d\n", s); + printf("%-14.14s", proc.p_name); + } else { + printf("%-14.14s", "<none>"); + } + printf("\n"); + } + printf("\n"); + printf("Press one of the registered function keys to trigger a debug dump.\n"); + printf("\n"); + } + else if (F1 <= fkey && fkey <= F12) { /* F1-F12 */ + observers = &fkey_obs[0]; + index = fkey - F1; + } else if (SF2 <= fkey && fkey <= SF12) { /* Shift F2-F12 */ + observers = &sfkey_obs[0]; + index = fkey - SF1; + } + if (! observers) return(FALSE); /* not observable */ + + /* See if an observer is registered and send it a message. */ + if (observers[index] != NONE) { + m.m_type = FKEY_PRESSED; + m.FKEY_NUM = index+1; + m.FKEY_CODE = fkey; + if (OK != (s=nb_send(observers[index], &m))) { + printf("WARNING: F%d key notification to process %d failed: %d.\n", + index+1, observers[index], s); + } + } + return(TRUE); +} + + +/*==========================================================================* + * scan_keyboard * + *==========================================================================*/ +PRIVATE int scan_keyboard() +{ +/* Fetch the character from the keyboard hardware and acknowledge it. */ + pvb_pair_t byte_in[2], byte_out[2]; + + byte_in[0].port = KEYBD; /* get the scan code for the key struck */ + byte_in[1].port = PORT_B; /* strobe the keyboard to ack the char */ + sys_vinb(byte_in, 2); /* request actual input */ + + pv_set(byte_out[0], PORT_B, byte_in[1].value | KBIT); /* strobe bit high */ + pv_set(byte_out[1], PORT_B, byte_in[1].value); /* then strobe low */ + sys_voutb(byte_out, 2); /* request actual output */ + + return(byte_in[0].value); /* return scan code */ +} + + +/*==========================================================================* + * do_panic_dumps * + *==========================================================================*/ +PUBLIC void do_panic_dumps(m) +message *m; /* request message to TTY */ +{ +/* Wait for keystrokes for printing debugging info and reboot. */ + + int quiet, code; + + /* A panic! Allow debug dumps until user wants to shutdown. */ + printf("\nHit ESC to reboot, DEL to shutdown, F-keys for debug dumps\n"); + + (void) scan_keyboard(); /* ack any old input */ + quiet = scan_keyboard();/* quiescent value (0 on PC, last code on AT)*/ + for (;;) { + tick_delay(10); + /* See if there are pending request for output, but don't block. + * Diagnostics can span multiple printf()s, so do it in a loop. + */ + while (nb_receive(ANY, m) == OK) { + switch(m->m_type) { + case NEW_KMESS: do_new_kmess(m); break; + case DIAGNOSTICS: do_diagnostics(m); break; + default: ; /* do nothing */ + } + tick_delay(1); /* allow more */ + } + code = scan_keyboard(); + if (code != quiet) { + /* A key has been pressed. */ + switch (code) { /* possibly abort MINIX */ + case ESC_SCAN: sys_abort(RBT_REBOOT); return; + case DEL_SCAN: sys_abort(RBT_HALT); return; + } + (void) func_key(code); /* check for function key */ + quiet = scan_keyboard(); + } + } +} + + diff --git a/drivers/tty/keymaps/Makefile b/drivers/tty/keymaps/Makefile new file mode 100644 index 000000000..93954681b --- /dev/null +++ b/drivers/tty/keymaps/Makefile @@ -0,0 +1,81 @@ +# Generate binary keymaps. + +LK = /usr/lib/keymaps + +.SUFFIXES: .src .map + +.src.map: + $(CC) -DKEYSRC=\"$<\" genmap.c + ./a.out > $@ + @rm -f a.out + +all: \ + french.map \ + german.map \ + italian.map \ + japanese.map \ + latin-am.map \ + olivetti.map \ + polish.map \ + scandinavn.map \ + spanish.map \ + uk.map \ + us-std.map \ + us-swap.map \ + +install: \ + $(LK) \ + $(LK)/french.map \ + $(LK)/german.map \ + $(LK)/italian.map \ + $(LK)/japanese.map \ + $(LK)/latin-am.map \ + $(LK)/olivetti.map \ + $(LK)/polish.map \ + $(LK)/scandinavn.map \ + $(LK)/spanish.map \ + $(LK)/uk.map \ + $(LK)/us-std.map \ + $(LK)/us-swap.map \ + +clean: + rm -f a.out *.map + +$(LK): + install -d $@ + +$(LK)/french.map: french.map + install -c $? $@ + +$(LK)/german.map: german.map + install -c $? $@ + +$(LK)/italian.map: italian.map + install -c $? $@ + +$(LK)/japanese.map: japanese.map + install -c $? $@ + +$(LK)/latin-am.map: latin-am.map + install -c $? $@ + +$(LK)/olivetti.map: olivetti.map + install -c $? $@ + +$(LK)/polish.map: polish.map + install -c $? $@ + +$(LK)/scandinavn.map: scandinavn.map + install -c $? $@ + +$(LK)/spanish.map: spanish.map + install -c $? $@ + +$(LK)/uk.map: uk.map + install -c $? $@ + +$(LK)/us-std.map: us-std.map + install -c $? $@ + +$(LK)/us-swap.map: us-swap.map + install -c $? $@ diff --git a/drivers/tty/keymaps/french.src b/drivers/tty/keymaps/french.src new file mode 100644 index 000000000..5326c4d4d --- /dev/null +++ b/drivers/tty/keymaps/french.src @@ -0,0 +1,135 @@ +/* Keymap for the French keyboard. */ + +u16_t keymap[NR_SCAN_CODES * MAP_COLS] = { + +/* scan-code !Shift Shift Alt AltGr Alt+Sh Ctrl */ +/* ==================================================================== */ +/* 00 - none */ 0, 0, 0, 0, 0, 0, +/* 01 - ESC */ C('['), C('['), CA('['),C('['), C('['), C('['), +/* 02 - '1' */ '&', '1', A('1'), '&', '1', C('A'), +/* 03 - '2' */ 0202, '2', A('2'), '~', '2', C('B'), +/* 04 - '3' */ '"', '3', A('3'), '#', '3', C('C'), +/* 05 - '4' */ '\'', '4', A('4'), '{', '4', C('D'), +/* 06 - '5' */ '(', '5', A('5'), '[', '5', C('E'), +/* 07 - '6' */ '-', '6', A('6'), '|', '6', C('F'), +/* 08 - '7' */ 0212, '7', A('7'), '`', '7', C('G'), +/* 09 - '8' */ '_', '8', A('8'), '\\', '8', C('H'), +/* 10 - '9' */ 0207, '9', A('9'), '^', '9', C('I'), +/* 11 - '0' */ 0205, '0', A('0'), '@', '0', C('J'), +/* 12 - '-' */ ')', 0370, A(')'), ']', '-', C('K'), +/* 13 - '=' */ '=', '+', A('='), '}', '=', C('L'), +/* 14 - BS */ C('H'), C('H'), CA('H'),C('H'), C('H'), 0177, +/* 15 - TAB */ C('I'), C('I'), CA('I'),C('I'), C('I'), C('I'), +/* 16 - 'q' */ L('a'), 'A', A('a'), 'a', 'Q', C('A'), +/* 17 - 'w' */ L('z'), 'Z', A('z'), 'z', 'W', C('Z'), +/* 18 - 'e' */ L('e'), 'E', A('e'), 'e', 'E', C('E'), +/* 19 - 'r' */ L('r'), 'R', A('r'), 'r', 'R', C('R'), +/* 20 - 't' */ L('t'), 'T', A('t'), 't', 'T', C('T'), +/* 21 - 'y' */ L('y'), 'Y', A('y'), 'y', 'Y', C('Y'), +/* 22 - 'u' */ L('u'), 'U', A('u'), 'u', 'U', C('U'), +/* 23 - 'i' */ L('i'), 'I', A('i'), 'i', 'I', C('I'), +/* 24 - 'o' */ L('o'), 'O', A('o'), 'o', 'O', C('O'), +/* 25 - 'p' */ L('p'), 'P', A('p'), 'p', 'P', C('P'), +/* 26 - '[' */ '^', '"', A('^'), '^', '[', C('^'), +/* 27 - ']' */ '$', 0234, A('$'), '$', ']', C('$'), +/* 28 - CR/LF */ C('M'), C('M'), CA('M'),C('M'), C('M'), C('J'), +/* 29 - Ctrl */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, +/* 30 - 'a' */ L('q'), 'Q', A('q'), 'q', 'A', C('Q'), +/* 31 - 's' */ L('s'), 'S', A('s'), 's', 'S', C('S'), +/* 32 - 'd' */ L('d'), 'D', A('d'), 'd', 'D', C('D'), +/* 33 - 'f' */ L('f'), 'F', A('f'), 'f', 'F', C('F'), +/* 34 - 'g' */ L('g'), 'G', A('g'), 'g', 'G', C('G'), +/* 35 - 'h' */ L('h'), 'H', A('h'), 'h', 'H', C('H'), +/* 36 - 'j' */ L('j'), 'J', A('j'), 'j', 'J', C('J'), +/* 37 - 'k' */ L('k'), 'K', A('k'), 'k', 'K', C('K'), +/* 38 - 'l' */ L('l'), 'L', A('l'), 'l', 'L', C('L'), +/* 39 - ';' */ L('m'), 'M', A('m'), 'm', 'M', C('M'), +/* 40 - '\'' */ 0227, '%', A('%'), 0227, '\\', C('G'), +/* 41 - '`' */ 0375, 0375, 0375, 0375, '`', C('['), +/* 42 - l. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 43 - '`' */ '*', 0346, A('*'), '*', '`', C('*'), +/* 44 - 'z' */ L('w'), 'W', A('w'), 'w', 'Z', C('W'), +/* 45 - 'x' */ L('x'), 'X', A('x'), 'x', 'X', C('X'), +/* 46 - 'c' */ L('c'), 'C', A('c'), 'c', 'C', C('C'), +/* 47 - 'v' */ L('v'), 'V', A('v'), 'v', 'V', C('V'), +/* 48 - 'b' */ L('b'), 'B', A('b'), 'b', 'B', C('B'), +/* 49 - 'n' */ L('n'), 'N', A('n'), 'n', 'N', C('N'), +/* 50 - 'm' */ ',', '?', A(','), ',', 'm', C('@'), +/* 51 - ',' */ ';', '.', A(';'), ';', ',', C('@'), +/* 52 - '.' */ ':', '/', A(':'), ':', '.', C('@'), +/* 53 - '/' */ '!', '$'/*025*/,A('!'), '!', '/', C('@'), +/* 54 - r. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 55 - '*' */ '*', '*', A('*'), '*', '*', C('@'), +/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, ALT, +/* 57 - ' ' */ ' ', ' ', A(' '), ' ', ' ', C('@'), +/* 58 - CapsLck */ CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, +/* 59 - F1 */ F1, SF1, AF1, AF1, ASF1, CF1, +/* 60 - F2 */ F2, SF2, AF2, AF2, ASF2, CF2, +/* 61 - F3 */ F3, SF3, AF3, AF3, ASF3, CF3, +/* 62 - F4 */ F4, SF4, AF4, AF4, ASF4, CF4, +/* 63 - F5 */ F5, SF5, AF5, AF5, ASF5, CF5, +/* 64 - F6 */ F6, SF6, AF6, AF6, ASF6, CF6, +/* 65 - F7 */ F7, SF7, AF7, AF7, ASF7, CF7, +/* 66 - F8 */ F8, SF8, AF8, AF8, ASF8, CF8, +/* 67 - F9 */ F9, SF9, AF9, AF9, ASF9, CF9, +/* 68 - F10 */ F10, SF10, AF10, AF10, ASF10, CF10, +/* 69 - NumLock */ NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, +/* 70 - ScrLock */ SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, +/* 71 - Home */ HOME, '7', AHOME, AHOME, '7', CHOME, +/* 72 - CurUp */ UP, '8', AUP, AUP, '8', CUP, +/* 73 - PgUp */ PGUP, '9', APGUP, APGUP, '9', CPGUP, +/* 74 - '-' */ NMIN, '-', ANMIN, ANMIN, '-', CNMIN, +/* 75 - Left */ LEFT, '4', ALEFT, ALEFT, '4', CLEFT, +/* 76 - MID */ MID, '5', AMID, AMID, '5', CMID, +/* 77 - Right */ RIGHT, '6', ARIGHT, ARIGHT, '6', CRIGHT, +/* 78 - '+' */ PLUS, '+', APLUS, APLUS, '+', CPLUS, +/* 79 - End */ END, '1', AEND, AEND, '1', CEND, +/* 80 - Down */ DOWN, '2', ADOWN, ADOWN, '2', CDOWN, +/* 81 - PgDown */ PGDN, '3', APGDN, APGDN, '3', CPGDN, +/* 82 - Insert */ INSRT, '0', AINSRT, AINSRT, '0', CINSRT, +/* 83 - Delete */ 0177, '.', A(0177),0177, '.', 0177, +/* 84 - Enter */ C('M'), C('M'), CA('M'),C('M'), C('M'), C('J'), +/* 85 - ??? */ 0, 0, 0, 0, 0, 0, +/* 86 - ??? */ '<', '>', A('<'), '<', '>', C('@'), +/* 87 - F11 */ F11, SF11, AF11, AF11, ASF11, CF11, +/* 88 - F12 */ F12, SF12, AF12, AF12, ASF12, CF12, +/* 89 - ??? */ 0, 0, 0, 0, 0, 0, +/* 90 - ??? */ 0, 0, 0, 0, 0, 0, +/* 91 - ??? */ 0, 0, 0, 0, 0, 0, +/* 92 - ??? */ 0, 0, 0, 0, 0, 0, +/* 93 - ??? */ 0, 0, 0, 0, 0, 0, +/* 94 - ??? */ 0, 0, 0, 0, 0, 0, +/* 95 - ??? */ 0, 0, 0, 0, 0, 0, +/* 96 - EXT_KEY */ EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, +/* 97 - ??? */ 0, 0, 0, 0, 0, 0, +/* 98 - ??? */ 0, 0, 0, 0, 0, 0, +/* 99 - ??? */ 0, 0, 0, 0, 0, 0, +/*100 - ??? */ 0, 0, 0, 0, 0, 0, +/*101 - ??? */ 0, 0, 0, 0, 0, 0, +/*102 - ??? */ 0, 0, 0, 0, 0, 0, +/*103 - ??? */ 0, 0, 0, 0, 0, 0, +/*104 - ??? */ 0, 0, 0, 0, 0, 0, +/*105 - ??? */ 0, 0, 0, 0, 0, 0, +/*106 - ??? */ 0, 0, 0, 0, 0, 0, +/*107 - ??? */ 0, 0, 0, 0, 0, 0, +/*108 - ??? */ 0, 0, 0, 0, 0, 0, +/*109 - ??? */ 0, 0, 0, 0, 0, 0, +/*110 - ??? */ 0, 0, 0, 0, 0, 0, +/*111 - ??? */ 0, 0, 0, 0, 0, 0, +/*112 - ??? */ 0, 0, 0, 0, 0, 0, +/*113 - ??? */ 0, 0, 0, 0, 0, 0, +/*114 - ??? */ 0, 0, 0, 0, 0, 0, +/*115 - ??? */ 0, 0, 0, 0, 0, 0, +/*116 - ??? */ 0, 0, 0, 0, 0, 0, +/*117 - ??? */ 0, 0, 0, 0, 0, 0, +/*118 - ??? */ 0, 0, 0, 0, 0, 0, +/*119 - ??? */ 0, 0, 0, 0, 0, 0, +/*120 - ??? */ 0, 0, 0, 0, 0, 0, +/*121 - ??? */ 0, 0, 0, 0, 0, 0, +/*122 - ??? */ 0, 0, 0, 0, 0, 0, +/*123 - ??? */ 0, 0, 0, 0, 0, 0, +/*124 - ??? */ 0, 0, 0, 0, 0, 0, +/*125 - ??? */ 0, 0, 0, 0, 0, 0, +/*126 - ??? */ 0, 0, 0, 0, 0, 0, +/*127 - ??? */ 0, 0, 0, 0, 0, 0 +}; diff --git a/drivers/tty/keymaps/genmap.c b/drivers/tty/keymaps/genmap.c new file mode 100644 index 000000000..1d5c809a7 --- /dev/null +++ b/drivers/tty/keymaps/genmap.c @@ -0,0 +1,58 @@ +/* genmap - output binary keymap Author: Marcus Hampel + */ +#include <sys/types.h> +#include <minix/keymap.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> + +#include KEYSRC + +u8_t comprmap[4 + NR_SCAN_CODES * MAP_COLS * 9/8 * 2 + 1]; + +void tell(const char *s) +{ + write(2, s, strlen(s)); +} + +int main(void) +{ + u8_t *cm, *fb; + u16_t *km; + int n; + + /* Compress the keymap. */ + memcpy(comprmap, KEY_MAGIC, 4); + cm = comprmap + 4; + n = 8; + for (km = keymap; km < keymap + NR_SCAN_CODES * MAP_COLS; km++) { + if (n == 8) { + /* Allocate a new flag byte. */ + fb = cm; + *cm++ = 0; + n= 0; + } + *cm++ = (*km & 0x00FF); /* Low byte. */ + if (*km & 0xFF00) { + *cm++ = (*km >> 8); /* High byte only when set. */ + *fb |= (1 << n); /* Set a flag if so. */ + } + n++; + } + + /* Don't store trailing zeros. */ + while (cm > comprmap && cm[-1] == 0) cm--; + + /* Emit the compressed keymap. */ + if (write(1, comprmap, cm - comprmap) < 0) { + int err = errno; + + tell("genmap: "); + tell(strerror(err)); + tell("\n"); + exit(1); + } + exit(0); +} diff --git a/drivers/tty/keymaps/german.src b/drivers/tty/keymaps/german.src new file mode 100644 index 000000000..264436c2e --- /dev/null +++ b/drivers/tty/keymaps/german.src @@ -0,0 +1,135 @@ +/* Keymap for German MF-2 keyboard. */ + +u16_t keymap[NR_SCAN_CODES * MAP_COLS] = { + +/* scan-code unsh Shift Alt AltGr Alt+Sh Strg */ +/* ==================================================================== */ +/* 00 - none */ 0, 0, 0, 0, 0, 0, +/* 01 - ESC */ C('['), C('['), CA('['),C('['), C('['), C('['), +/* 02 - '1' */ '1', '!', A('1'), '1', '!', C('A'), +/* 03 - '2' */ '2', '"', A('2'), 0375, '@', C('@'), +/* 04 - '3' */ '3', 025, A('3'), 0374, '#', C('C'), +/* 05 - '4' */ '4', '$', A('4'), '4', '$', C('D'), +/* 06 - '5' */ '5', '%', A('5'), '5', '%', C('E'), +/* 07 - '6' */ '6', '&', A('6'), '6', '^', C('^'), +/* 08 - '7' */ '7', '/', A('7'), '{', '&', C('G'), +/* 09 - '8' */ '8', '(', A('8'), '[', '*', C('H'), +/* 10 - '9' */ '9', ')', A('9'), ']', '(', C('I'), +/* 11 - '0' */ '0', '=', A('0'), '}', ')', C('@'), +/* 12 - '-' */ 0341, '?', 0341, '\\', '_', C('_'), +/* 13 - '=' */ '\'', '`', A('\''),'=', '+', C('@'), +/* 14 - BS */ C('H'), C('H'), CA('H'),C('H'), C('H'), 0177, +/* 15 - TAB */ C('I'), C('I'), CA('I'),C('I'), C('I'), C('I'), +/* 16 - 'q' */ L('q'), 'Q', A('q'), '@', 'Q', C('Q'), +/* 17 - 'w' */ L('w'), 'W', A('w'), 'w', 'W', C('W'), +/* 18 - 'e' */ L('e'), 'E', A('e'), 'e', 'E', C('E'), +/* 19 - 'r' */ L('r'), 'R', A('r'), 'r', 'R', C('R'), +/* 20 - 't' */ L('t'), 'T', A('t'), 't', 'T', C('T'), +/* 21 - 'y' */ L('z'), 'Z', A('z'), 'z', 'Z', C('Z'), +/* 22 - 'u' */ L('u'), 'U', A('u'), 'u', 'U', C('U'), +/* 23 - 'i' */ L('i'), 'I', A('i'), 'i', 'I', C('I'), +/* 24 - 'o' */ L('o'), 'O', A('o'), 'o', 'O', C('O'), +/* 25 - 'p' */ L('p'), 'P', A('p'), 'p', 'P', C('P'), +/* 26 - '[' */ L(0201),0232, 0201, '[', '{', C('['), +/* 27 - ']' */ '+', '*', A('+'), '~', ']', C(']'), +/* 28 - CR/LF */ C('M'), C('M'), CA('M'),C('M'), C('M'), C('J'), +/* 29 - Strg;-) */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, +/* 30 - 'a' */ L('a'), 'A', A('a'), 'a', 'A', C('A'), +/* 31 - 's' */ L('s'), 'S', A('s'), 's', 'S', C('S'), +/* 32 - 'd' */ L('d'), 'D', A('d'), 'd', 'D', C('D'), +/* 33 - 'f' */ L('f'), 'F', A('f'), 'f', 'F', C('F'), +/* 34 - 'g' */ L('g'), 'G', A('g'), 'g', 'G', C('G'), +/* 35 - 'h' */ L('h'), 'H', A('h'), 'h', 'H', C('H'), +/* 36 - 'j' */ L('j'), 'J', A('j'), 'j', 'J', C('J'), +/* 37 - 'k' */ L('k'), 'K', A('k'), 'k', 'K', C('K'), +/* 38 - 'l' */ L('l'), 'L', A('l'), 'l', 'L', C('L'), +/* 39 - ';' */ L(0224),0231, 0224, ';', ':', C('@'), +/* 40 - '\'' */ L(0204),0216, 0204, '\'', '"', C('@'), +/* 41 - '`' */ '^', 0370, A('^'), '`', '~', C('^'), +/* 42 - SHIFT */ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 43 - '\\' */ '#', '\'', A('#'), '\\', '|', C('\\'), +/* 44 - 'z' */ L('y'), 'Y', A('y'), 'y', 'Y', C('Y'), +/* 45 - 'x' */ L('x'), 'X', A('x'), 'x', 'X', C('X'), +/* 46 - 'c' */ L('c'), 'C', A('c'), 'c', 'C', C('C'), +/* 47 - 'v' */ L('v'), 'V', A('v'), 'v', 'V', C('V'), +/* 48 - 'b' */ L('b'), 'B', A('b'), 'b', 'B', C('B'), +/* 49 - 'n' */ L('n'), 'N', A('n'), 'n', 'N', C('N'), +/* 50 - 'm' */ L('m'), 'M', A('m'), 0346, 'M', C('M'), +/* 51 - ',' */ ',', ';', A(','), ',', '<', C('@'), +/* 52 - '.' */ '.', ':', A('.'), '.', '>', C('@'), +/* 53 - '/' */ '-', '_', A('-'), '/', '?', C('_'), +/* 54 - SHIFT */ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 55 - '*' */ '*', '*', A('*'), '*', '*', C('@'), +/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, ALT, +/* 57 - ' ' */ ' ', ' ', A(' '), ' ', ' ', C('@'), +/* 58 - CapsLck */ CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, +/* 59 - F1 */ F1, SF1, AF1, AF1, ASF1, CF1, +/* 60 - F2 */ F2, SF2, AF2, AF2, ASF2, CF2, +/* 61 - F3 */ F3, SF3, AF3, AF3, ASF3, CF3, +/* 62 - F4 */ F4, SF4, AF4, AF4, ASF4, CF4, +/* 63 - F5 */ F5, SF5, AF5, AF5, ASF5, CF5, +/* 64 - F6 */ F6, SF6, AF6, AF6, ASF6, CF6, +/* 65 - F7 */ F7, SF7, AF7, AF7, ASF7, CF7, +/* 66 - F8 */ F8, SF8, AF8, AF8, ASF8, CF8, +/* 67 - F9 */ F9, SF9, AF9, AF9, ASF9, CF9, +/* 68 - F10 */ F10, SF10, AF10, AF10, ASF10, CF10, +/* 69 - NumLock */ NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, +/* 70 - ScrLock */ SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, +/* 71 - Home */ HOME, '7', AHOME, AHOME, '7', CHOME, +/* 72 - CurUp */ UP, '8', AUP, AUP, '8', CUP, +/* 73 - PgUp */ PGUP, '9', APGUP, APGUP, '9', CPGUP, +/* 74 - '-' */ NMIN, '-', ANMIN, ANMIN, '-', CNMIN, +/* 75 - Left */ LEFT, '4', ALEFT, ALEFT, '4', CLEFT, +/* 76 - MID */ MID, '5', AMID, AMID, '5', CMID, +/* 77 - Right */ RIGHT, '6', ARIGHT, ARIGHT, '6', CRIGHT, +/* 78 - '+' */ PLUS, '+', APLUS, APLUS, '+', CPLUS, +/* 79 - End */ END, '1', AEND, AEND, '1', CEND, +/* 80 - Down */ DOWN, '2', ADOWN, ADOWN, '2', CDOWN, +/* 81 - PgDown */ PGDN, '3', APGDN, APGDN, '3', CPGDN, +/* 82 - Insert */ INSRT, '0', AINSRT, AINSRT, '0', CINSRT, +/* 83 - Delete */ 0177, '.', A(0177),0177, '.', 0177, +/* 84 - Enter */ C('M'), C('M'), CA('M'),C('M'), C('M'), C('J'), +/* 85 - ??? */ 0, 0, 0, 0, 0, 0, +/* 86 - ??? */ '<', '>', A('<'), '|', '>', C('@'), +/* 87 - F11 */ F11, SF11, AF11, AF11, ASF11, CF11, +/* 88 - F12 */ F12, SF12, AF12, AF12, ASF12, CF12, +/* 89 - ??? */ 0, 0, 0, 0, 0, 0, +/* 90 - ??? */ 0, 0, 0, 0, 0, 0, +/* 91 - ??? */ 0, 0, 0, 0, 0, 0, +/* 92 - ??? */ 0, 0, 0, 0, 0, 0, +/* 93 - ??? */ 0, 0, 0, 0, 0, 0, +/* 94 - ??? */ 0, 0, 0, 0, 0, 0, +/* 95 - ??? */ 0, 0, 0, 0, 0, 0, +/* 96 - EXT_KEY */ EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, +/* 97 - ??? */ 0, 0, 0, 0, 0, 0, +/* 98 - ??? */ 0, 0, 0, 0, 0, 0, +/* 99 - ??? */ 0, 0, 0, 0, 0, 0, +/*100 - ??? */ 0, 0, 0, 0, 0, 0, +/*101 - ??? */ 0, 0, 0, 0, 0, 0, +/*102 - ??? */ 0, 0, 0, 0, 0, 0, +/*103 - ??? */ 0, 0, 0, 0, 0, 0, +/*104 - ??? */ 0, 0, 0, 0, 0, 0, +/*105 - ??? */ 0, 0, 0, 0, 0, 0, +/*106 - ??? */ 0, 0, 0, 0, 0, 0, +/*107 - ??? */ 0, 0, 0, 0, 0, 0, +/*108 - ??? */ 0, 0, 0, 0, 0, 0, +/*109 - ??? */ 0, 0, 0, 0, 0, 0, +/*110 - ??? */ 0, 0, 0, 0, 0, 0, +/*111 - ??? */ 0, 0, 0, 0, 0, 0, +/*112 - ??? */ 0, 0, 0, 0, 0, 0, +/*113 - ??? */ 0, 0, 0, 0, 0, 0, +/*114 - ??? */ 0, 0, 0, 0, 0, 0, +/*115 - ??? */ 0, 0, 0, 0, 0, 0, +/*116 - ??? */ 0, 0, 0, 0, 0, 0, +/*117 - ??? */ 0, 0, 0, 0, 0, 0, +/*118 - ??? */ 0, 0, 0, 0, 0, 0, +/*119 - ??? */ 0, 0, 0, 0, 0, 0, +/*120 - ??? */ 0, 0, 0, 0, 0, 0, +/*121 - ??? */ 0, 0, 0, 0, 0, 0, +/*122 - ??? */ 0, 0, 0, 0, 0, 0, +/*123 - ??? */ 0, 0, 0, 0, 0, 0, +/*124 - ??? */ 0, 0, 0, 0, 0, 0, +/*125 - ??? */ 0, 0, 0, 0, 0, 0, +/*126 - ??? */ 0, 0, 0, 0, 0, 0, +/*127 - ??? */ 0, 0, 0, 0, 0, 0 +}; diff --git a/drivers/tty/keymaps/italian.src b/drivers/tty/keymaps/italian.src new file mode 100644 index 000000000..56a4ea064 --- /dev/null +++ b/drivers/tty/keymaps/italian.src @@ -0,0 +1,137 @@ +/* Keymap for Italian keyboard. */ +/* Modified by Ernesto Del Prete in October 1997 */ +/* ernesto@cclix1.polito.it or s84508@cclix1.polito.it */ + +u16_t keymap[NR_SCAN_CODES * MAP_COLS] = { + +/* scan-code !Shift Shift Alt AltGr Alt+Sh Ctrl */ +/* ==================================================================== */ +/* 00 - none */ 0, 0, 0, 0, 0, 0, +/* 01 - ESC */ C('['), C('['), CA('['),C('['), C('['), C('['), +/* 02 - '1' */ '1', '!', A('1'), '1', '!', C('A'), +/* 03 - '2' */ '2', '"', A('2'), '2', '@', C('@'), +/* 04 - '3' */ '3', 0234, A('3'), '3', 0234, C('C'), +/* 05 - '4' */ '4', '$', A('4'), '4', '$', C('D'), +/* 06 - '5' */ '5', '%', A('5'), '5', '%', C('E'), +/* 07 - '6' */ '6', '&', A('6'), '6', '&', C('F'), +/* 08 - '7' */ '7', '/', A('7'), '7', '/', C('G'), +/* 09 - '8' */ '8', '(', A('8'), '8', '(', C('H'), +/* 10 - '9' */ '9', ')', A('9'), '8', ')', C('I'), +/* 11 - '0' */ '0', '=', A('0'), '0', '=', C('@'), +/* 12 - '-' */ '\'', '?', A('\''),'`', '?', C('@'), +/* 13 - '=' */ 0215, '^', A('|'), 0215, '^', C('^'), +/* 14 - BS */ C('H'), C('H'), CA('H'),C('H'), C('H'), 0177, +/* 15 - TAB */ C('I'), C('I'), CA('I'),C('I'), C('I'), C('I'), +/* 16 - 'q' */ L('q'), 'Q', A('q'), 'q', 'Q', C('Q'), +/* 17 - 'w' */ L('w'), 'W', A('w'), 'w', 'W', C('W'), +/* 18 - 'e' */ L('e'), 'E', A('e'), 'e', 'E', C('E'), +/* 19 - 'r' */ L('r'), 'R', A('r'), 'r', 'R', C('R'), +/* 20 - 't' */ L('t'), 'T', A('t'), 't', 'T', C('T'), +/* 21 - 'y' */ L('y'), 'Y', A('y'), 'y', 'Y', C('Y'), +/* 22 - 'u' */ L('u'), 'U', A('u'), 'u', 'U', C('U'), +/* 23 - 'i' */ L('i'), 'I', A('i'), 'i', 'I', C('I'), +/* 24 - 'o' */ L('o'), 'O', A('o'), 'o', 'O', C('O'), +/* 25 - 'p' */ L('p'), 'P', A('p'), 'p', 'P', C('P'), +/* 26 - '[' */ 0212, 0202, 0212, '[', '{', C('['), +/* 27 - ']' */ '+', '*', A('+'), ']', '}', C(']'), +/* 28 - CR/LF */ C('M'), C('M'), CA('M'),C('M'), C('M'), C('J'), +/* 29 - Ctrl */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, +/* 30 - 'a' */ L('a'), 'A', A('a'), 'a', 'A', C('A'), +/* 31 - 's' */ L('s'), 'S', A('s'), 's', 'S', C('S'), +/* 32 - 'd' */ L('d'), 'D', A('d'), 'd', 'D', C('D'), +/* 33 - 'f' */ L('f'), 'F', A('f'), 'f', 'F', C('F'), +/* 34 - 'g' */ L('g'), 'G', A('g'), 'g', 'G', C('G'), +/* 35 - 'h' */ L('h'), 'H', A('h'), 'h', 'H', C('H'), +/* 36 - 'j' */ L('j'), 'J', A('j'), 'j', 'J', C('J'), +/* 37 - 'k' */ L('k'), 'K', A('k'), 'k', 'K', C('K'), +/* 38 - 'l' */ L('l'), 'L', A('l'), 'l', 'L', C('L'), +/* 39 - ';' */ 0225, 0207, 0225, '@', '@', C('@'), +/* 40 - '\'' */ 0205, 0370, 0205, '#', 0276, C('@'), +/* 41 - '`' */ '\\', '|', A('\\'),0176, '|', C('\\'), +/* 42 - l. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 43 - '\\' */ 0227, 025, 0227, 0227, 025, C('@'), +/* 44 - 'z' */ L('z'), 'Z', A('z'), 'z', 'Z', C('Z'), +/* 45 - 'x' */ L('x'), 'X', A('x'), 'x', 'X', C('X'), +/* 46 - 'c' */ L('c'), 'C', A('c'), 'c', 'C', C('C'), +/* 47 - 'v' */ L('v'), 'V', A('v'), 'v', 'V', C('V'), +/* 48 - 'b' */ L('b'), 'B', A('b'), 'b', 'B', C('B'), +/* 49 - 'n' */ L('n'), 'N', A('n'), 'n', 'N', C('N'), +/* 50 - 'm' */ L('m'), 'M', A('m'), 'm', 'M', C('M'), +/* 51 - ',' */ ',', ';', A(','), ',', ';', C('@'), +/* 52 - '.' */ '.', ':', A('.'), '.', ':', C('@'), +/* 53 - '/' */ '-', '_', A('-'), '-', '_', C('_'), +/* 54 - r. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 55 - '*' */ '*', '*', A('*'), '*', '*', C('M'), +/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, ALT, +/* 57 - ' ' */ ' ', ' ', A(' '), ' ', ' ', C('@'), +/* 58 - CapsLck */ CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, +/* 59 - F1 */ F1, SF1, AF1, AF1, ASF1, CF1, +/* 60 - F2 */ F2, SF2, AF2, AF2, ASF2, CF2, +/* 61 - F3 */ F3, SF3, AF3, AF3, ASF3, CF3, +/* 62 - F4 */ F4, SF4, AF4, AF4, ASF4, CF4, +/* 63 - F5 */ F5, SF5, AF5, AF5, ASF5, CF5, +/* 64 - F6 */ F6, SF6, AF6, AF6, ASF6, CF6, +/* 65 - F7 */ F7, SF7, AF7, AF7, ASF7, CF7, +/* 66 - F8 */ F8, SF8, AF8, AF8, ASF8, CF8, +/* 67 - F9 */ F9, SF9, AF9, AF9, ASF9, CF9, +/* 68 - F10 */ F10, SF10, AF10, AF10, ASF10, CF10, +/* 69 - NumLock */ NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, +/* 70 - ScrLock */ SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, +/* 71 - Home */ HOME, '7', AHOME, AHOME, '7', CHOME, +/* 72 - CurUp */ UP, '8', AUP, AUP, '8', CUP, +/* 73 - PgUp */ PGUP, '9', APGUP, APGUP, '9', CPGUP, +/* 74 - '-' */ NMIN, '-', ANMIN, ANMIN, '-', CNMIN, +/* 75 - Left */ LEFT, '4', ALEFT, ALEFT, '4', CLEFT, +/* 76 - MID */ MID, '5', AMID, AMID, '5', CMID, +/* 77 - Right */ RIGHT, '6', ARIGHT, ARIGHT, '6', CRIGHT, +/* 78 - '+' */ PLUS, '+', APLUS, APLUS, '+', CPLUS, +/* 79 - End */ END, '1', AEND, AEND, '1', CEND, +/* 80 - Down */ DOWN, '2', ADOWN, ADOWN, '2', CDOWN, +/* 81 - PgDown */ PGDN, '3', APGDN, APGDN, '3', CPGDN, +/* 82 - Insert */ INSRT, '0', AINSRT, AINSRT, '0', CINSRT, +/* 83 - Delete */ 0177, '.', A(0177),0177, '.', 0177, +/* 84 - Enter */ C('M'), C('M'), CA('M'),C('M'), C('M'), C('J'), +/* 85 - ??? */ 0, 0, 0, 0, 0, 0, +/* 86 - ??? */ '<', '>', A('<'), '|', '>', C('@'), +/* 87 - F11 */ F11, SF11, AF11, AF11, ASF11, CF11, +/* 88 - F12 */ F12, SF12, AF12, AF12, ASF12, CF12, +/* 89 - ??? */ 0, 0, 0, 0, 0, 0, +/* 90 - ??? */ 0, 0, 0, 0, 0, 0, +/* 91 - ??? */ 0, 0, 0, 0, 0, 0, +/* 92 - ??? */ 0, 0, 0, 0, 0, 0, +/* 93 - ??? */ 0, 0, 0, 0, 0, 0, +/* 94 - ??? */ 0, 0, 0, 0, 0, 0, +/* 95 - ??? */ 0, 0, 0, 0, 0, 0, +/* 96 - EXT_KEY */ EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, +/* 97 - ??? */ 0, 0, 0, 0, 0, 0, +/* 98 - ??? */ 0, 0, 0, 0, 0, 0, +/* 99 - ??? */ 0, 0, 0, 0, 0, 0, +/*100 - ??? */ 0, 0, 0, 0, 0, 0, +/*101 - ??? */ 0, 0, 0, 0, 0, 0, +/*102 - ??? */ 0, 0, 0, 0, 0, 0, +/*103 - ??? */ 0, 0, 0, 0, 0, 0, +/*104 - ??? */ 0, 0, 0, 0, 0, 0, +/*105 - ??? */ 0, 0, 0, 0, 0, 0, +/*106 - ??? */ 0, 0, 0, 0, 0, 0, +/*107 - ??? */ 0, 0, 0, 0, 0, 0, +/*108 - ??? */ 0, 0, 0, 0, 0, 0, +/*109 - ??? */ 0, 0, 0, 0, 0, 0, +/*110 - ??? */ 0, 0, 0, 0, 0, 0, +/*111 - ??? */ 0, 0, 0, 0, 0, 0, +/*112 - ??? */ 0, 0, 0, 0, 0, 0, +/*113 - ??? */ 0, 0, 0, 0, 0, 0, +/*114 - ??? */ 0, 0, 0, 0, 0, 0, +/*115 - ??? */ 0, 0, 0, 0, 0, 0, +/*116 - ??? */ 0, 0, 0, 0, 0, 0, +/*117 - ??? */ 0, 0, 0, 0, 0, 0, +/*118 - ??? */ 0, 0, 0, 0, 0, 0, +/*119 - ??? */ 0, 0, 0, 0, 0, 0, +/*120 - ??? */ 0, 0, 0, 0, 0, 0, +/*121 - ??? */ 0, 0, 0, 0, 0, 0, +/*122 - ??? */ 0, 0, 0, 0, 0, 0, +/*123 - ??? */ 0, 0, 0, 0, 0, 0, +/*124 - ??? */ 0, 0, 0, 0, 0, 0, +/*125 - ??? */ 0, 0, 0, 0, 0, 0, +/*126 - ??? */ 0, 0, 0, 0, 0, 0, +/*127 - ??? */ 0, 0, 0, 0, 0, 0 +}; diff --git a/drivers/tty/keymaps/japanese.src b/drivers/tty/keymaps/japanese.src new file mode 100644 index 000000000..d9b9391da --- /dev/null +++ b/drivers/tty/keymaps/japanese.src @@ -0,0 +1,157 @@ +/* + * Keymap for Japanese 106 keyboard. + * Dec. 31 1995 + * Toshiya Ogawa <ogw@shizuokanet.or.jp> + * <GCD02425@niftyserve.or.jp> + */ + +/* + * Japanese 106 keyboard has following additional 5 scan codes + * compared to US standard 101 keyboard. + * + * scan-code keytop effect in this keymap + * ------------------------------------------------------- + * 112(0x70) KANA (ignored) + * 115(0x73) BackSlash mapped to '\\' and '_' + * 121(0x79) HENKAN (ignored) + * 123(0x7B) MU-HENKAN (ignored) + * 125(0x7D) YEN mapped to '\\' and '|' + */ + +#if (NR_SCAN_CODES != 0x80) +#error NR_SCAN_CODES mis-match +#endif + +u16_t keymap[NR_SCAN_CODES * MAP_COLS] = { + +/* scan-code !Shift Shift Alt1 Alt2 Alt+Sh Ctrl */ +/* ==================================================================== */ +/* 00 - none */ 0, 0, 0, 0, 0, 0, +/* 01 - ESC */ C('['), C('['), CA('['),CA('['),CA('['),C('['), +/* 02 - '1' */ '1', '!', A('1'), A('1'), A('!'), C('A'), +/* 03 - '2' */ '2', '"', A('2'), A('2'), A('"'), C('B'), +/* 04 - '3' */ '3', '#', A('3'), A('3'), A('#'), C('C'), +/* 05 - '4' */ '4', '$', A('4'), A('4'), A('$'), C('D'), +/* 06 - '5' */ '5', '%', A('5'), A('5'), A('%'), C('E'), +/* 07 - '6' */ '6', '&', A('6'), A('6'), A('&'), C('F'), +/* 08 - '7' */ '7', '\'', A('7'), A('7'), A('\''),C('G'), +/* 09 - '8' */ '8', '(', A('8'), A('8'), A('('), C('H'), +/* 10 - '9' */ '9', ')', A('9'), A('9'), A(')'), C('I'), +/* 11 - '0' */ '0', '~', A('0'), A('0'), A('~'), C('@'), +/* 12 - '-' */ '-', '=', A('-'), A('-'), A('='), C('@'), +/* 13 - '^' */ '^', '~', A('^'), A('^'), A('~'), C('^'), +/* 14 - BS */ C('H'), C('H'), CA('H'),CA('H'),CA('H'),0177, +/* 15 - TAB */ C('I'), C('I'), CA('I'),CA('I'),CA('I'),C('I'), +/* 16 - 'q' */ L('q'), 'Q', A('q'), A('q'), A('Q'), C('Q'), +/* 17 - 'w' */ L('w'), 'W', A('w'), A('w'), A('W'), C('W'), +/* 18 - 'e' */ L('e'), 'E', A('e'), A('e'), A('E'), C('E'), +/* 19 - 'r' */ L('r'), 'R', A('r'), A('r'), A('R'), C('R'), +/* 20 - 't' */ L('t'), 'T', A('t'), A('t'), A('T'), C('T'), +/* 21 - 'y' */ L('y'), 'Y', A('y'), A('y'), A('Y'), C('Y'), +/* 22 - 'u' */ L('u'), 'U', A('u'), A('u'), A('U'), C('U'), +/* 23 - 'i' */ L('i'), 'I', A('i'), A('i'), A('I'), C('I'), +/* 24 - 'o' */ L('o'), 'O', A('o'), A('o'), A('O'), C('O'), +/* 25 - 'p' */ L('p'), 'P', A('p'), A('p'), A('P'), C('P'), +/* 26 - '@' */ '@', '`', A('@'), A('@'), A('`'), C('@'), +/* 27 - '[' */ '[', '{', A('['), A('['), A('{'), C('['), +/* 28 - Enter */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 29 - Ctrl */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, +/* 30 - 'a' */ L('a'), 'A', A('a'), A('a'), A('A'), C('A'), +/* 31 - 's' */ L('s'), 'S', A('s'), A('s'), A('S'), C('S'), +/* 32 - 'd' */ L('d'), 'D', A('d'), A('d'), A('D'), C('D'), +/* 33 - 'f' */ L('f'), 'F', A('f'), A('f'), A('F'), C('F'), +/* 34 - 'g' */ L('g'), 'G', A('g'), A('g'), A('G'), C('G'), +/* 35 - 'h' */ L('h'), 'H', A('h'), A('h'), A('H'), C('H'), +/* 36 - 'j' */ L('j'), 'J', A('j'), A('j'), A('J'), C('J'), +/* 37 - 'k' */ L('k'), 'K', A('k'), A('k'), A('K'), C('K'), +/* 38 - 'l' */ L('l'), 'L', A('l'), A('l'), A('L'), C('L'), +/* 39 - ';' */ ';', '+', A(';'), A(';'), A('+'), C('@'), +/* 40 - ':' */ ':', '*', A(':'), A(':'), A('*'), C('@'), +/* 41 - KANJI */ 0, 0, 0, 0, 0, 0, +/* 42 - l. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 43 - ']' */ ']', '}', A(']'), A(']'), A('}'), C(']'), +/* 44 - 'z' */ L('z'), 'Z', A('z'), A('z'), A('Z'), C('Z'), +/* 45 - 'x' */ L('x'), 'X', A('x'), A('x'), A('X'), C('X'), +/* 46 - 'c' */ L('c'), 'C', A('c'), A('c'), A('C'), C('C'), +/* 47 - 'v' */ L('v'), 'V', A('v'), A('v'), A('V'), C('V'), +/* 48 - 'b' */ L('b'), 'B', A('b'), A('b'), A('B'), C('B'), +/* 49 - 'n' */ L('n'), 'N', A('n'), A('n'), A('N'), C('N'), +/* 50 - 'm' */ L('m'), 'M', A('m'), A('m'), A('M'), C('M'), +/* 51 - ',' */ ',', '<', A(','), A(','), A('<'), C('@'), +/* 52 - '.' */ '.', '>', A('.'), A('.'), A('>'), C('@'), +/* 53 - '/' */ '/', '?', A('/'), A('/'), A('?'), C('@'), +/* 54 - r. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 55 - '*' */ '*', '*', A('*'), A('*'), A('*'), C('@'), +/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, ALT, +/* 57 - ' ' */ ' ', ' ', A(' '), A(' '), A(' '), C('@'), +/* 58 - CapsLck */ CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, +/* 59 - F1 */ F1, SF1, AF1, AF1, ASF1, CF1, +/* 60 - F2 */ F2, SF2, AF2, AF2, ASF2, CF2, +/* 61 - F3 */ F3, SF3, AF3, AF3, ASF3, CF3, +/* 62 - F4 */ F4, SF4, AF4, AF4, ASF4, CF4, +/* 63 - F5 */ F5, SF5, AF5, AF5, ASF5, CF5, +/* 64 - F6 */ F6, SF6, AF6, AF6, ASF6, CF6, +/* 65 - F7 */ F7, SF7, AF7, AF7, ASF7, CF7, +/* 66 - F8 */ F8, SF8, AF8, AF8, ASF8, CF8, +/* 67 - F9 */ F9, SF9, AF9, AF9, ASF9, CF9, +/* 68 - F10 */ F10, SF10, AF10, AF10, ASF10, CF10, +/* 69 - NumLock */ NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, +/* 70 - ScrLock */ SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, +/* 71 - Home */ HOME, '7', AHOME, AHOME, A('7'), CHOME, +/* 72 - CurUp */ UP, '8', AUP, AUP, A('8'), CUP, +/* 73 - PgUp */ PGUP, '9', APGUP, APGUP, A('9'), CPGUP, +/* 74 - '-' */ NMIN, '-', ANMIN, ANMIN, A('-'), CNMIN, +/* 75 - Left */ LEFT, '4', ALEFT, ALEFT, A('4'), CLEFT, +/* 76 - MID */ MID, '5', AMID, AMID, A('5'), CMID, +/* 77 - Right */ RIGHT, '6', ARIGHT, ARIGHT, A('6'), CRIGHT, +/* 78 - '+' */ PLUS, '+', APLUS, APLUS, A('+'), CPLUS, +/* 79 - End */ END, '1', AEND, AEND, A('1'), CEND, +/* 80 - Down */ DOWN, '2', ADOWN, ADOWN, A('2'), CDOWN, +/* 81 - PgDown */ PGDN, '3', APGDN, APGDN, A('3'), CPGDN, +/* 82 - Insert */ INSRT, '0', AINSRT, AINSRT, A('0'), CINSRT, +/* 83 - Delete */ 0177, '.', A(0177),A(0177),A('.'), 0177, +/* 84 - Enter */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 85 - ??? */ 0, 0, 0, 0, 0, 0, +/* 86 - ??? */ 0, 0, 0, 0, 0, 0, +/* 87 - F11 */ F11, SF11, AF11, AF11, ASF11, CF11, +/* 88 - F12 */ F12, SF12, AF12, AF12, ASF12, CF12, +/* 89 - ??? */ 0, 0, 0, 0, 0, 0, +/* 90 - ??? */ 0, 0, 0, 0, 0, 0, +/* 91 - ??? */ 0, 0, 0, 0, 0, 0, +/* 92 - ??? */ 0, 0, 0, 0, 0, 0, +/* 93 - ??? */ 0, 0, 0, 0, 0, 0, +/* 94 - ??? */ 0, 0, 0, 0, 0, 0, +/* 95 - ??? */ 0, 0, 0, 0, 0, 0, +/* 96 - EXT_KEY */ EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, +/* 97 - ??? */ 0, 0, 0, 0, 0, 0, +/* 98 - ??? */ 0, 0, 0, 0, 0, 0, +/* 99 - ??? */ 0, 0, 0, 0, 0, 0, +/*100 - ??? */ 0, 0, 0, 0, 0, 0, +/*101 - ??? */ 0, 0, 0, 0, 0, 0, +/*102 - ??? */ 0, 0, 0, 0, 0, 0, +/*103 - ??? */ 0, 0, 0, 0, 0, 0, +/*104 - ??? */ 0, 0, 0, 0, 0, 0, +/*105 - ??? */ 0, 0, 0, 0, 0, 0, +/*106 - ??? */ 0, 0, 0, 0, 0, 0, +/*107 - ??? */ 0, 0, 0, 0, 0, 0, +/*108 - ??? */ 0, 0, 0, 0, 0, 0, +/*109 - ??? */ 0, 0, 0, 0, 0, 0, +/*110 - ??? */ 0, 0, 0, 0, 0, 0, +/*111 - ??? */ 0, 0, 0, 0, 0, 0, +/*112 - KANA */ 0, 0, 0, 0, 0, 0, +/*113 - ??? */ 0, 0, 0, 0, 0, 0, +/*114 - ??? */ 0, 0, 0, 0, 0, 0, +/*115 - '\\' */ '\\', '_', A('\\'),A('\\'),A('_'), C('_'), +/*116 - ??? */ 0, 0, 0, 0, 0, 0, +/*117 - ??? */ 0, 0, 0, 0, 0, 0, +/*118 - ??? */ 0, 0, 0, 0, 0, 0, +/*119 - ??? */ 0, 0, 0, 0, 0, 0, +/*120 - ??? */ 0, 0, 0, 0, 0, 0, +/*121 - HENKAN */ 0, 0, 0, 0, 0, 0, +/*122 - ??? */ 0, 0, 0, 0, 0, 0, +/*123 - MU-HENKAN*/ 0, 0, 0, 0, 0, 0, +/*124 - ??? */ 0, 0, 0, 0, 0, 0, +/*125 - YEN */ '\\', '|', A('\\'),A('\\'),A('|'), C('\\'), +/*126 - ??? */ 0, 0, 0, 0, 0, 0, +/*127 - ??? */ 0, 0, 0, 0, 0, 0 +}; diff --git a/drivers/tty/keymaps/latin-am.src b/drivers/tty/keymaps/latin-am.src new file mode 100644 index 000000000..4dc40ecfc --- /dev/null +++ b/drivers/tty/keymaps/latin-am.src @@ -0,0 +1,153 @@ +/*** + Keymap for Latin American keyboard. v1.02 + Victor A. Rodriguez - El bit Fantasma - Bit-Man@Tasa.Com.AR + + The Latin American keyboard makes differences between the left and + right ALT keys (the right one is so called ALT GR), and uses accent. + + Release History + =============== + v1.00 Initial version + v1.01 Extended ASCII characters replaced by hex. equivalents + v1.02 NR_SCAN_CODES has grown to 0x80, required by Toshiya Ogawa + (ogw@shizuokanet.or.jp) and added by Kees J.Bot (kjb@cs.vu.nl) + in MINIX 1.7.2 +***/ + +#if (NR_SCAN_CODES != 0x80) +#error NR_SCAN_CODES mis-match +#endif + +u16_t keymap[NR_SCAN_CODES * MAP_COLS] = { + +/* scan-code !Shift Shift Alt1 Alt2 Alt+Sh Ctrl */ +/* ==================================================================== */ +/* 00 - none */ 0, 0, 0, 0, 0, 0, +/* 01 - ESC */ C('['), C('['), CA('['),CA('['),CA('['),C('['), +/* 02 - '1' */ '1', '!', A('1'), A('1'), A('!'), C('A'), +/* 03 - '2' */ '2', '"', A('2'), A('2'), A('"'), C('@'), +/* 04 - '3' */ '3', '#', A('3'), A('3'), A('#'), C('C'), +/* 05 - '4' */ '4', '$', A('4'), A('4'), A('$'), C('D'), +/* 06 - '5' */ '5', '%', A('5'), A('5'), A('%'), C('E'), +/* 07 - '6' */ '6', '&', A('6'), A('6'), A('$'), C('^'), +/* 08 - '7' */ '7', '/', A('7'), A('7'), A('/'), C('G'), +/* 09 - '8' */ '8', '(', A('8'), A('8'), A('('), C('H'), +/* 10 - '9' */ '9', ')', A('9'), A('9'), A(')'), C('I'), +/* 11 - '0' */ '0', '=', A('0'), A('0'), A('='), C('@'), +/* 12 - '-' */ '\'', '?', A('\''), 0x5c, A('?'), C('?'), +/* 13 - '¨' */ 0xa8, 0xad, A(0xa8),A(0xa8),A(0xad), C('@'), +/* 14 - BS */ C('H'), C('H'), CA('H'),CA('H'),CA('H'),0177, +/* 15 - TAB */ C('I'), C('I'), CA('I'),CA('I'),CA('I'),C('I'), +/* 16 - 'q' */ L('q'), 'Q', A('q'), 0x40, A('Q'), C('Q'), +/* 17 - 'w' */ L('w'), 'W', A('w'), A('w'), A('W'), C('W'), +/* 18 - 'e' */ L('e'), 'E', A('e'), A('e'), A('E'), C('E'), +/* 19 - 'r' */ L('r'), 'R', A('r'), A('r'), A('R'), C('R'), +/* 20 - 't' */ L('t'), 'T', A('t'), A('t'), A('T'), C('T'), +/* 21 - 'y' */ L('y'), 'Y', A('y'), A('y'), A('Y'), C('Y'), +/* 22 - 'u' */ L('u'), 'U', A('u'), A('u'), A('U'), C('U'), +/* 23 - 'i' */ L('i'), 'I', A('i'), A('i'), A('I'), C('I'), +/* 24 - 'o' */ L('o'), 'O', A('o'), A('o'), A('O'), C('O'), +/* 25 - 'p' */ L('p'), 'P', A('p'), A('p'), A('P'), C('P'), +/* 26 - 'ï' */ 0xef, 0xf9, A(0xef),A(0xef),A(0xf9),C(0xef), +/* 27 - '+' */ '+', '*', A('+'), 0x7e, A('*'), C('+'), +/* 28 - CR/LF */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 29 - Ctrl */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, +/* 30 - 'a' */ L('a'), 'A', A('a'), A('a'), A('A'), C('A'), +/* 31 - 's' */ L('s'), 'S', A('s'), A('s'), A('S'), C('S'), +/* 32 - 'd' */ L('d'), 'D', A('d'), A('d'), A('D'), C('D'), +/* 33 - 'f' */ L('f'), 'F', A('f'), A('f'), A('F'), C('F'), +/* 34 - 'g' */ L('g'), 'G', A('g'), A('g'), A('G'), C('G'), +/* 35 - 'h' */ L('h'), 'H', A('h'), A('h'), A('H'), C('H'), +/* 36 - 'j' */ L('j'), 'J', A('j'), A('j'), A('J'), C('J'), +/* 37 - 'k' */ L('k'), 'K', A('k'), A('k'), A('K'), C('K'), +/* 38 - 'l' */ L('l'), 'L', A('l'), A('l'), A('L'), C('L'), +/* 39 - '¤' */ L(0xa4),0xa5, A(0xa4),A(0xa4),A(0xa5),C('@'), +/* 40 - '{' */ '{', '[', A('{'), 0x5e, A('['), C('@'), +/* 41 - '|' */ '|', 0xf8, A('|'), 0xaa, A('\''),C('@'), +/* 42 - l. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 43 - '}' */ 0x7d, 0x5d, A('<'), 0x60, A('>'), C('<'), +/* 44 - 'z' */ L('z'), 'Z', A('z'), A('z'), A('Z'), C('Z'), +/* 45 - 'x' */ L('x'), 'X', A('x'), A('x'), A('X'), C('X'), +/* 46 - 'c' */ L('c'), 'C', A('c'), A('c'), A('C'), C('C'), +/* 47 - 'v' */ L('v'), 'V', A('v'), A('v'), A('V'), C('V'), +/* 48 - 'b' */ L('b'), 'B', A('b'), A('b'), A('B'), C('B'), +/* 49 - 'n' */ L('n'), 'N', A('n'), A('n'), A('N'), C('N'), +/* 50 - 'm' */ L('m'), 'M', A('m'), A('m'), A('M'), C('M'), +/* 51 - ',' */ ',', ';', A(','), A(','), A(';'), C('@'), +/* 52 - '.' */ '.', ':', A('.'), A('.'), A(':'), C('@'), +/* 53 - '/' */ '-', '_', A('-'), A('-'), A('_'), C('@'), +/* 54 - r. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 55 - '*' */ '*', '*', A('*'), A('*'), A('*'), C('@'), +/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, ALT, +/* 57 - ' ' */ ' ', ' ', A(' '), A(' '), A(' '), C('@'), +/* 58 - CapsLck */ CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, +/* 59 - F1 */ F1, SF1, AF1, AF1, ASF1, CF1, +/* 60 - F2 */ F2, SF2, AF2, AF2, ASF2, CF2, +/* 61 - F3 */ F3, SF3, AF3, AF3, ASF3, CF3, +/* 62 - F4 */ F4, SF4, AF4, AF4, ASF4, CF4, +/* 63 - F5 */ F5, SF5, AF5, AF5, ASF5, CF5, +/* 64 - F6 */ F6, SF6, AF6, AF6, ASF6, CF6, +/* 65 - F7 */ F7, SF7, AF7, AF7, ASF7, CF7, +/* 66 - F8 */ F8, SF8, AF8, AF8, ASF8, CF8, +/* 67 - F9 */ F9, SF9, AF9, AF9, ASF9, CF9, +/* 68 - F10 */ F10, SF10, AF10, AF10, ASF10, CF10, +/* 69 - NumLock */ NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, +/* 70 - ScrLock */ SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, +/* 71 - Home */ HOME, '7', AHOME, AHOME, A('7'), CHOME, +/* 72 - CurUp */ UP, '8', AUP, AUP, A('8'), CUP, +/* 73 - PgUp */ PGUP, '9', APGUP, APGUP, A('9'), CPGUP, +/* 74 - '-' */ '-', '-', '-', '-', '-', '-', +/* 75 - Left */ LEFT, '4', ALEFT, ALEFT, A('4'), CLEFT, +/* 76 - MID */ MID, '5', AMID, AMID, A('5'), CMID, +/* 77 - Right */ RIGHT, '6', ARIGHT, ARIGHT, A('6'), CRIGHT, +/* 78 - '+' */ '+', '+', '+', '+', '+', '+', +/* 79 - End */ END, '1', AEND, AEND, A('1'), CEND, +/* 80 - Down */ DOWN, '2', ADOWN, ADOWN, A('2'), CDOWN, +/* 81 - PgDown */ PGDN, '3', APGDN, APGDN, A('3'), CPGDN, +/* 82 - Insert */ INSRT, '0', AINSRT, AINSRT, A('0'), CINSRT, +/* 83 - Delete */ 0177, '.', A(0177),A(0177),A('.'), 0177, +/* 84 - Enter */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 85 - ??? */ 0, 0, 0, 0, 0, 0, +/* 86 - ??? */ '<', '>', A('<'), A('>'), A('>'), C('@'), +/* 87 - F11 */ F11, SF11, AF11, AF11, ASF11, CF11, +/* 88 - F12 */ F12, SF12, AF12, AF12, ASF12, CF12, +/* 89 - ??? */ 0, 0, 0, 0, 0, 0, +/* 90 - ??? */ 0, 0, 0, 0, 0, 0, +/* 91 - ??? */ 0, 0, 0, 0, 0, 0, +/* 92 - ??? */ 0, 0, 0, 0, 0, 0, +/* 93 - ??? */ 0, 0, 0, 0, 0, 0, +/* 94 - ??? */ 0, 0, 0, 0, 0, 0, +/* 95 - ??? */ 0, 0, 0, 0, 0, 0, +/* 96 - EXT_KEY */ EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, +/* 97 - ??? */ 0, 0, 0, 0, 0, 0, +/* 98 - ??? */ 0, 0, 0, 0, 0, 0, +/* 99 - ??? */ 0, 0, 0, 0, 0, 0, +/*100 - ??? */ 0, 0, 0, 0, 0, 0, +/*101 - ??? */ 0, 0, 0, 0, 0, 0, +/*102 - ??? */ 0, 0, 0, 0, 0, 0, +/*103 - ??? */ 0, 0, 0, 0, 0, 0, +/*104 - ??? */ 0, 0, 0, 0, 0, 0, +/*105 - ??? */ 0, 0, 0, 0, 0, 0, +/*106 - ??? */ 0, 0, 0, 0, 0, 0, +/*107 - ??? */ 0, 0, 0, 0, 0, 0, +/*108 - ??? */ 0, 0, 0, 0, 0, 0, +/*109 - ??? */ 0, 0, 0, 0, 0, 0, +/*110 - ??? */ 0, 0, 0, 0, 0, 0, +/*111 - ??? */ 0, 0, 0, 0, 0, 0, +/*112 - ??? */ 0, 0, 0, 0, 0, 0, +/*113 - ??? */ 0, 0, 0, 0, 0, 0, +/*114 - ??? */ 0, 0, 0, 0, 0, 0, +/*115 - ??? */ 0, 0, 0, 0, 0, 0, +/*116 - ??? */ 0, 0, 0, 0, 0, 0, +/*117 - ??? */ 0, 0, 0, 0, 0, 0, +/*118 - ??? */ 0, 0, 0, 0, 0, 0, +/*119 - ??? */ 0, 0, 0, 0, 0, 0, +/*120 - ??? */ 0, 0, 0, 0, 0, 0, +/*121 - ??? */ 0, 0, 0, 0, 0, 0, +/*122 - ??? */ 0, 0, 0, 0, 0, 0, +/*123 - ??? */ 0, 0, 0, 0, 0, 0, +/*124 - ??? */ 0, 0, 0, 0, 0, 0, +/*125 - ??? */ 0, 0, 0, 0, 0, 0, +/*126 - ??? */ 0, 0, 0, 0, 0, 0, +/*127 - ??? */ 0, 0, 0, 0, 0, 0 +}; diff --git a/drivers/tty/keymaps/olivetti.src b/drivers/tty/keymaps/olivetti.src new file mode 100644 index 000000000..a18d4fd31 --- /dev/null +++ b/drivers/tty/keymaps/olivetti.src @@ -0,0 +1,135 @@ +/* Keymap for the Olivetti M24. */ + +u16_t keymap[NR_SCAN_CODES * MAP_COLS] = { + +/* scan-code !Shift Shift Alt1 Alt2 Alt+Sh Ctrl */ +/* ==================================================================== */ +/* 00 - none */ 0, 0, 0, 0, 0, 0, +/* 01 - ESC */ C('['), C('['), CA('['),CA('['),CA('['),C('['), +/* 02 - '1' */ '1', '!', A('1'), A('1'), A('!'), C('A'), +/* 03 - '2' */ '2', '"', A('2'), A('2'), A('"'), C('B'), +/* 04 - '3' */ '3', '#', A('3'), A('3'), A('#'), C('C'), +/* 05 - '4' */ '4', '$', A('4'), A('4'), A('$'), C('D'), +/* 06 - '5' */ '5', '%', A('5'), A('5'), A('%'), C('E'), +/* 07 - '6' */ '6', '&', A('6'), A('6'), A('&'), C('F'), +/* 08 - '7' */ '7', '\'', A('7'), A('7'), A('\''),C('G'), +/* 09 - '8' */ '8', '(', A('8'), A('8'), A('('), C('H'), +/* 10 - '9' */ '9', ')', A('9'), A('9'), A(')'), C('I'), +/* 11 - '0' */ '0', '_', A('0'), A('0'), A('_'), C('@'), +/* 12 - '-' */ '-', '=', A('-'), A('-'), A('='), C('@'), +/* 13 - '=' */ '^', '~', A('^'), A('^'), A('~'), C('^'), +/* 14 - BS */ C('H'), C('H'), CA('H'),CA('H'),CA('H'),0177, +/* 15 - TAB */ C('I'), C('I'), CA('I'),CA('I'),CA('I'),C('I'), +/* 16 - 'q' */ L('q'), 'Q', A('q'), A('q'), A('Q'), C('Q'), +/* 17 - 'w' */ L('w'), 'W', A('w'), A('w'), A('W'), C('W'), +/* 18 - 'e' */ L('e'), 'E', A('e'), A('e'), A('E'), C('E'), +/* 19 - 'r' */ L('r'), 'R', A('r'), A('r'), A('R'), C('R'), +/* 20 - 't' */ L('t'), 'T', A('t'), A('t'), A('T'), C('T'), +/* 21 - 'y' */ L('y'), 'Y', A('y'), A('y'), A('Y'), C('Y'), +/* 22 - 'u' */ L('u'), 'U', A('u'), A('u'), A('U'), C('U'), +/* 23 - 'i' */ L('i'), 'I', A('i'), A('i'), A('I'), C('I'), +/* 24 - 'o' */ L('o'), 'O', A('o'), A('o'), A('O'), C('O'), +/* 25 - 'p' */ L('p'), 'P', A('p'), A('p'), A('P'), C('P'), +/* 26 - '[' */ '@', '`', A('@'), A('@'), A('`'), C('@'), +/* 27 - ']' */ '[', '{', A('['), A('['), A('{'), C('['), +/* 28 - CR/LF */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 29 - Ctrl */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, +/* 30 - 'a' */ L('a'), 'A', A('a'), A('a'), A('A'), C('A'), +/* 31 - 's' */ L('s'), 'S', A('s'), A('s'), A('S'), C('S'), +/* 32 - 'd' */ L('d'), 'D', A('d'), A('d'), A('D'), C('D'), +/* 33 - 'f' */ L('f'), 'F', A('f'), A('f'), A('F'), C('F'), +/* 34 - 'g' */ L('g'), 'G', A('g'), A('g'), A('G'), C('G'), +/* 35 - 'h' */ L('h'), 'H', A('h'), A('h'), A('H'), C('H'), +/* 36 - 'j' */ L('j'), 'J', A('j'), A('j'), A('J'), C('J'), +/* 37 - 'k' */ L('k'), 'K', A('k'), A('k'), A('K'), C('K'), +/* 38 - 'l' */ L('l'), 'L', A('l'), A('l'), A('L'), C('L'), +/* 39 - ';' */ ';', '+', A(';'), A(';'), A('+'), C('@'), +/* 40 - '\'' */ ':', '*', A(':'), A(':'), A('*'), C('@'), +/* 41 - '`' */ ']', '}', A(']'), A(']'), A('}'), C(']'), +/* 42 - l. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 43 - '\\' */ '\\', '|', A('\\'),A('\\'),A('|'), C('\\'), +/* 44 - 'z' */ L('z'), 'Z', A('z'), A('z'), A('Z'), C('Z'), +/* 45 - 'x' */ L('x'), 'X', A('x'), A('x'), A('X'), C('X'), +/* 46 - 'c' */ L('c'), 'C', A('c'), A('c'), A('C'), C('C'), +/* 47 - 'v' */ L('v'), 'V', A('v'), A('v'), A('V'), C('V'), +/* 48 - 'b' */ L('b'), 'B', A('b'), A('b'), A('B'), C('B'), +/* 49 - 'n' */ L('n'), 'N', A('n'), A('n'), A('N'), C('N'), +/* 50 - 'm' */ L('m'), 'M', A('m'), A('m'), A('M'), C('M'), +/* 51 - ',' */ ',', '<', A(','), A(','), A('<'), C('@'), +/* 52 - '.' */ '.', '>', A('.'), A('.'), A('>'), C('@'), +/* 53 - '/' */ '/', '?', A('/'), A('/'), A('?'), C('@'), +/* 54 - r. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 55 - '*' */ '*', '*', A('*'), A('*'), A('*'), C('@'), +/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, ALT, +/* 57 - ' ' */ ' ', ' ', A(' '), A(' '), A(' '), C('@'), +/* 58 - CapsLck */ CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, +/* 59 - F1 */ F1, SF1, AF1, AF1, ASF1, CF1, +/* 60 - F2 */ F2, SF2, AF2, AF2, ASF2, CF2, +/* 61 - F3 */ F3, SF3, AF3, AF3, ASF3, CF3, +/* 62 - F4 */ F4, SF4, AF4, AF4, ASF4, CF4, +/* 63 - F5 */ F5, SF5, AF5, AF5, ASF5, CF5, +/* 64 - F6 */ F6, SF6, AF6, AF6, ASF6, CF6, +/* 65 - F7 */ F7, SF7, AF7, AF7, ASF7, CF7, +/* 66 - F8 */ F8, SF8, AF8, AF8, ASF8, CF8, +/* 67 - F9 */ F9, SF9, AF9, AF9, ASF9, CF9, +/* 68 - F10 */ F10, SF10, AF10, AF10, ASF10, CF10, +/* 69 - NumLock */ C('S'), C('S'), C('S'), C('S'), C('S'), C('S'), +/* 70 - ScrLock */ SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, +/* 71 - Home */ HOME, '7', AHOME, AHOME, A('7'), CHOME, +/* 72 - CurUp */ UP, '8', AUP, AUP, A('8'), CUP, +/* 73 - PgUp */ PGUP, '9', APGUP, APGUP, A('9'), CPGUP, +/* 74 - '-' */ NMIN, '-', ANMIN, ANMIN, A('-'), CNMIN, +/* 75 - Left */ LEFT, '4', ALEFT, ALEFT, A('4'), CLEFT, +/* 76 - MID */ MID, '5', AMID, AMID, A('5'), CMID, +/* 77 - Right */ RIGHT, '6', ARIGHT, ARIGHT, A('6'), CRIGHT, +/* 78 - '+' */ PLUS, '+', APLUS, APLUS, A('+'), CPLUS, +/* 79 - End */ END, '1', AEND, AEND, A('1'), CEND, +/* 80 - Down */ DOWN, '2', ADOWN, ADOWN, A('2'), CDOWN, +/* 81 - PgDown */ PGDN, '3', APGDN, APGDN, A('3'), CPGDN, +/* 82 - Insert */ INSRT, '0', AINSRT, AINSRT, A('0'), CINSRT, +/* 83 - Delete */ 0177, '.', A(0177),A(0177),A('.'), 0177, +/* 84 - Enter */ ' ', ' ', A(' '), A(' '), A(' '), C('@'), +/* 85 - ??? */ LEFT, 014, A(014), A(014), A(014), 014, +/* 86 - ??? */ 0212, 0212, 0212, 0212, 0212, 0212, +/* 87 - F11 */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 88 - F12 */ F12, SF12, AF12, AF12, ASF12, CF12, +/* 89 - ??? */ DOWN, DOWN, DOWN, DOWN, DOWN, DOWN, +/* 90 - ??? */ RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, +/* 91 - ??? */ UP, UP, UP, UP, UP, UP, +/* 92 - ??? */ LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, +/* 93 - ??? */ 0, 0, 0, 0, 0, 0, +/* 94 - ??? */ 0, 0, 0, 0, 0, 0, +/* 95 - ??? */ 0, 0, 0, 0, 0, 0, +/* 96 - EXT_KEY */ EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, +/* 97 - ??? */ 0, 0, 0, 0, 0, 0, +/* 98 - ??? */ 0, 0, 0, 0, 0, 0, +/* 99 - ??? */ 0, 0, 0, 0, 0, 0, +/*100 - ??? */ 0, 0, 0, 0, 0, 0, +/*101 - ??? */ 0, 0, 0, 0, 0, 0, +/*102 - ??? */ 0, 0, 0, 0, 0, 0, +/*103 - ??? */ 0, 0, 0, 0, 0, 0, +/*104 - ??? */ 0, 0, 0, 0, 0, 0, +/*105 - ??? */ 0, 0, 0, 0, 0, 0, +/*106 - ??? */ 0, 0, 0, 0, 0, 0, +/*107 - ??? */ 0, 0, 0, 0, 0, 0, +/*108 - ??? */ 0, 0, 0, 0, 0, 0, +/*109 - ??? */ 0, 0, 0, 0, 0, 0, +/*110 - ??? */ 0, 0, 0, 0, 0, 0, +/*111 - ??? */ 0, 0, 0, 0, 0, 0, +/*112 - ??? */ 0, 0, 0, 0, 0, 0, +/*113 - ??? */ 0, 0, 0, 0, 0, 0, +/*114 - ??? */ 0, 0, 0, 0, 0, 0, +/*115 - ??? */ 0, 0, 0, 0, 0, 0, +/*116 - ??? */ 0, 0, 0, 0, 0, 0, +/*117 - ??? */ 0, 0, 0, 0, 0, 0, +/*118 - ??? */ 0, 0, 0, 0, 0, 0, +/*119 - ??? */ 0, 0, 0, 0, 0, 0, +/*120 - ??? */ 0, 0, 0, 0, 0, 0, +/*121 - ??? */ 0, 0, 0, 0, 0, 0, +/*122 - ??? */ 0, 0, 0, 0, 0, 0, +/*123 - ??? */ 0, 0, 0, 0, 0, 0, +/*124 - ??? */ 0, 0, 0, 0, 0, 0, +/*125 - ??? */ 0, 0, 0, 0, 0, 0, +/*126 - ??? */ 0, 0, 0, 0, 0, 0, +/*127 - ??? */ 0, 0, 0, 0, 0, 0 +}; diff --git a/drivers/tty/keymaps/polish.src b/drivers/tty/keymaps/polish.src new file mode 100644 index 000000000..21ec112a7 --- /dev/null +++ b/drivers/tty/keymaps/polish.src @@ -0,0 +1,140 @@ +/* + * Keymap for US MF-2 keyboard. + Polish national letters. + * Modified by Antek Sawicki <tenox@tenox.tc> + * Charset: ISO-8859-2 - [Polska Norma PN-93 T-42118] + */ + + +u16_t keymap[NR_SCAN_CODES * MAP_COLS] = { + +/* scan-code !Shift Shift Alt1 Alt2 Alt+Sh Ctrl */ +/* ==================================================================== */ +/* 00 - none */ 0, 0, 0, 0, 0, 0, +/* 01 - ESC */ C('['), C('['), CA('['),CA('['),CA('['),C('['), +/* 02 - '1' */ '1', '!', A('1'), A('1'), A('!'), C('A'), +/* 03 - '2' */ '2', '@', A('2'), A('2'), A('@'), C('@'), +/* 04 - '3' */ '3', '#', A('3'), A('3'), A('#'), C('C'), +/* 05 - '4' */ '4', '$', A('4'), A('4'), A('$'), C('D'), +/* 06 - '5' */ '5', '%', A('5'), A('5'), A('%'), C('E'), +/* 07 - '6' */ '6', '^', A('6'), A('6'), A('^'), C('^'), +/* 08 - '7' */ '7', '&', A('7'), A('7'), A('&'), C('G'), +/* 09 - '8' */ '8', '*', A('8'), A('8'), A('*'), C('H'), +/* 10 - '9' */ '9', '(', A('9'), A('9'), A('('), C('I'), +/* 11 - '0' */ '0', ')', A('0'), A('0'), A(')'), C('@'), +/* 12 - '-' */ '-', '_', A('-'), A('-'), A('_'), C('_'), +/* 13 - '=' */ '=', '+', A('='), A('='), A('+'), C('@'), +/* 14 - BS */ C('H'), C('H'), CA('H'),CA('H'),CA('H'),0177, +/* 15 - TAB */ C('I'), C('I'), CA('I'),CA('I'),CA('I'),C('I'), +/* 16 - 'q' */ L('q'), 'Q', A('q'), A('q'), A('Q'), C('Q'), +/* 17 - 'w' */ L('w'), 'W', A('w'), A('w'), A('W'), C('W'), +/* 18 - 'e' */ L('e'), 'E', A(0xea),A(0xea),A(0xca),C('E'), +/* 19 - 'r' */ L('r'), 'R', A('r'), A('r'), A('R'), C('R'), +/* 20 - 't' */ L('t'), 'T', A('t'), A('t'), A('T'), C('T'), +/* 21 - 'y' */ L('y'), 'Y', A('y'), A('y'), A('Y'), C('Y'), +/* 22 - 'u' */ L('u'), 'U', A('u'), A('u'), A('U'), C('U'), +/* 23 - 'i' */ L('i'), 'I', A('i'), A('i'), A('I'), C('I'), +/* 24 - 'o' */ L('o'), 'O', A(0xf3),A(0xf3),A(0xd3),C('O'), +/* 25 - 'p' */ L('p'), 'P', A('p'), A('p'), A('P'), C('P'), +/* 26 - '[' */ '[', '{', A('['), A('['), A('{'), C('['), +/* 27 - ']' */ ']', '}', A(']'), A(']'), A('}'), C(']'), +/* 28 - CR/LF */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 29 - Ctrl */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, +/* 30 - 'a' */ L('a'), 'A', A(0xb1),A(0xb1),A(0xa1),C('A'), +/* 31 - 's' */ L('s'), 'S', A(0xb6),A(0xb6),A(0xa6),C('S'), +/* 32 - 'd' */ L('d'), 'D', A('d'), A('d'), A('D'), C('D'), +/* 33 - 'f' */ L('f'), 'F', A('f'), A('f'), A('F'), C('F'), +/* 34 - 'g' */ L('g'), 'G', A('g'), A('g'), A('G'), C('G'), +/* 35 - 'h' */ L('h'), 'H', A('h'), A('h'), A('H'), C('H'), +/* 36 - 'j' */ L('j'), 'J', A('j'), A('j'), A('J'), C('J'), +/* 37 - 'k' */ L('k'), 'K', A('k'), A('k'), A('K'), C('K'), +/* 38 - 'l' */ L('l'), 'L', A(0xb3),A(0xb3),A(0xa3),C('L'), +/* 39 - ';' */ ';', ':', A(';'), A(';'), A(':'), C('@'), +/* 40 - '\'' */ '\'', '"', A('\''),A('\''),A('"'), C('@'), +/* 41 - '`' */ '`', '~', A('`'), A('`'), A('~'), C('@'), +/* 42 - l. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 43 - '\\' */ '\\', '|', A('\\'),A('\\'),A('|'), C('\\'), +/* 44 - 'z' */ L('z'), 'Z', A(0xbf),A(0xbf),A(0xaf),C('Z'), +/* 45 - 'x' */ L('x'), 'X', A(0xbc),A(0xbc),A(0xac),C('X'), +/* 46 - 'c' */ L('c'), 'C', A(0xe6),A(0xe6),A(0xc6),C('C'), +/* 47 - 'v' */ L('v'), 'V', A('v'), A('v'), A('V'), C('V'), +/* 48 - 'b' */ L('b'), 'B', A('b'), A('b'), A('B'), C('B'), +/* 49 - 'n' */ L('n'), 'N', A(0xf1),A(0xf1),A(0xd1),C('N'), +/* 50 - 'm' */ L('m'), 'M', A('m'), A('m'), A('M'), C('M'), +/* 51 - ',' */ ',', '<', A(','), A(','), A('<'), C('@'), +/* 52 - '.' */ '.', '>', A('.'), A('.'), A('>'), C('@'), +/* 53 - '/' */ '/', '?', A('/'), A('/'), A('?'), C('@'), +/* 54 - r. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 55 - '*' */ '*', '*', A('*'), A('*'), A('*'), C('@'), +/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, ALT, +/* 57 - ' ' */ ' ', ' ', A(' '), A(' '), A(' '), C('@'), +/* 58 - CapsLck */ CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, +/* 59 - F1 */ F1, SF1, AF1, AF1, ASF1, CF1, +/* 60 - F2 */ F2, SF2, AF2, AF2, ASF2, CF2, +/* 61 - F3 */ F3, SF3, AF3, AF3, ASF3, CF3, +/* 62 - F4 */ F4, SF4, AF4, AF4, ASF4, CF4, +/* 63 - F5 */ F5, SF5, AF5, AF5, ASF5, CF5, +/* 64 - F6 */ F6, SF6, AF6, AF6, ASF6, CF6, +/* 65 - F7 */ F7, SF7, AF7, AF7, ASF7, CF7, +/* 66 - F8 */ F8, SF8, AF8, AF8, ASF8, CF8, +/* 67 - F9 */ F9, SF9, AF9, AF9, ASF9, CF9, +/* 68 - F10 */ F10, SF10, AF10, AF10, ASF10, CF10, +/* 69 - NumLock */ NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, +/* 70 - ScrLock */ SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, +/* 71 - Home */ HOME, '7', AHOME, AHOME, A('7'), CHOME, +/* 72 - CurUp */ UP, '8', AUP, AUP, A('8'), CUP, +/* 73 - PgUp */ PGUP, '9', APGUP, APGUP, A('9'), CPGUP, +/* 74 - '-' */ NMIN, '-', ANMIN, ANMIN, A('-'), CNMIN, +/* 75 - Left */ LEFT, '4', ALEFT, ALEFT, A('4'), CLEFT, +/* 76 - MID */ MID, '5', AMID, AMID, A('5'), CMID, +/* 77 - Right */ RIGHT, '6', ARIGHT, ARIGHT, A('6'), CRIGHT, +/* 78 - '+' */ PLUS, '+', APLUS, APLUS, A('+'), CPLUS, +/* 79 - End */ END, '1', AEND, AEND, A('1'), CEND, +/* 80 - Down */ DOWN, '2', ADOWN, ADOWN, A('2'), CDOWN, +/* 81 - PgDown */ PGDN, '3', APGDN, APGDN, A('3'), CPGDN, +/* 82 - Insert */ INSRT, '0', AINSRT, AINSRT, A('0'), CINSRT, +/* 83 - Delete */ 0177, '.', A(0177),A(0177),A('.'), 0177, +/* 84 - Enter */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 85 - ??? */ 0, 0, 0, 0, 0, 0, +/* 86 - ??? */ '<', '>', A('<'), A('|'), A('>'), C('@'), +/* 87 - F11 */ F11, SF11, AF11, AF11, ASF11, CF11, +/* 88 - F12 */ F12, SF12, AF12, AF12, ASF12, CF12, +/* 89 - ??? */ 0, 0, 0, 0, 0, 0, +/* 90 - ??? */ 0, 0, 0, 0, 0, 0, +/* 91 - ??? */ 0, 0, 0, 0, 0, 0, +/* 92 - ??? */ 0, 0, 0, 0, 0, 0, +/* 93 - ??? */ 0, 0, 0, 0, 0, 0, +/* 94 - ??? */ 0, 0, 0, 0, 0, 0, +/* 95 - ??? */ 0, 0, 0, 0, 0, 0, +/* 96 - EXT_KEY */ EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, +/* 97 - ??? */ 0, 0, 0, 0, 0, 0, +/* 98 - ??? */ 0, 0, 0, 0, 0, 0, +/* 99 - ??? */ 0, 0, 0, 0, 0, 0, +/*100 - ??? */ 0, 0, 0, 0, 0, 0, +/*101 - ??? */ 0, 0, 0, 0, 0, 0, +/*102 - ??? */ 0, 0, 0, 0, 0, 0, +/*103 - ??? */ 0, 0, 0, 0, 0, 0, +/*104 - ??? */ 0, 0, 0, 0, 0, 0, +/*105 - ??? */ 0, 0, 0, 0, 0, 0, +/*106 - ??? */ 0, 0, 0, 0, 0, 0, +/*107 - ??? */ 0, 0, 0, 0, 0, 0, +/*108 - ??? */ 0, 0, 0, 0, 0, 0, +/*109 - ??? */ 0, 0, 0, 0, 0, 0, +/*110 - ??? */ 0, 0, 0, 0, 0, 0, +/*111 - ??? */ 0, 0, 0, 0, 0, 0, +/*112 - ??? */ 0, 0, 0, 0, 0, 0, +/*113 - ??? */ 0, 0, 0, 0, 0, 0, +/*114 - ??? */ 0, 0, 0, 0, 0, 0, +/*115 - ??? */ 0, 0, 0, 0, 0, 0, +/*116 - ??? */ 0, 0, 0, 0, 0, 0, +/*117 - ??? */ 0, 0, 0, 0, 0, 0, +/*118 - ??? */ 0, 0, 0, 0, 0, 0, +/*119 - ??? */ 0, 0, 0, 0, 0, 0, +/*120 - ??? */ 0, 0, 0, 0, 0, 0, +/*121 - ??? */ 0, 0, 0, 0, 0, 0, +/*122 - ??? */ 0, 0, 0, 0, 0, 0, +/*123 - ??? */ 0, 0, 0, 0, 0, 0, +/*124 - ??? */ 0, 0, 0, 0, 0, 0, +/*125 - ??? */ 0, 0, 0, 0, 0, 0, +/*126 - ??? */ 0, 0, 0, 0, 0, 0, +/*127 - ??? */ 0, 0, 0, 0, 0, 0 +}; diff --git a/drivers/tty/keymaps/scandinavn.src b/drivers/tty/keymaps/scandinavn.src new file mode 100644 index 000000000..a4afba929 --- /dev/null +++ b/drivers/tty/keymaps/scandinavn.src @@ -0,0 +1,138 @@ +/* Keymap for Scandinavian keyboard. + * by Oliver Reitalu, nolx@nolx.tartu.ee + * preliminary version, 8 Sept 1996 + */ + +u16_t keymap[NR_SCAN_CODES * MAP_COLS] = { + +/* scan-code unsh Shift Alt AltGr Alt+Sh Ctrl */ +/* ==================================================================== */ +/* 00 - none */ 0, 0, 0, 0, 0, 0, +/* 01 - ESC */ C('['), C('['), CA('['),C('['), C('['), C('['), +/* 02 - '1' */ '1', '!', A('1'), '1', '!', C('A'), +/* 03 - '2' */ '2', '"', A('2'), '@', '@', C('@'), +/* 04 - '3' */ '3', '#', A('3'), 156, '#', C('C'), +/* 05 - '4' */ '4', ' ', A('4'), '$', '$', C('D'), +/* 06 - '5' */ '5', '%', A('5'), '5', '%', C('E'), +/* 07 - '6' */ '6', '&', A('6'), '6', '^', C('^'), +/* 08 - '7' */ '7', '/', A('7'), '{', '&', C('G'), +/* 09 - '8' */ '8', '(', A('8'), '[', '*', C('H'), +/* 10 - '9' */ '9', ')', A('9'), ']', '(', C('I'), +/* 11 - '0' */ '0', '=', A('0'), '}', ')', C('@'), +/* 12 - '_' */ '+', '?', 0341, '\\', '_', C('_'), +/* 13 - '=' */ '\'', '`', A('\''),'=', '+', C('@'), +/* 14 - BS */ C('H'), C('H'), CA('H'),C('H'), C('H'), 0177, +/* 15 - TAB */ C('I'), C('I'), CA('I'),C('I'), C('I'), C('I'), +/* 16 - 'q' */ L('q'), 'Q', A('q'), '@', 'Q', C('Q'), +/* 17 - 'w' */ L('w'), 'W', A('w'), 'w', 'W', C('W'), +/* 18 - 'e' */ L('e'), 'E', A('e'), 'e', 'E', C('E'), +/* 19 - 'r' */ L('r'), 'R', A('r'), 'r', 'R', C('R'), +/* 20 - 't' */ L('t'), 'T', A('t'), 't', 'T', C('T'), +/* 21 - 'y' */ L('y'), 'Y', A('y'), 'y', 'Y', C('Y'), +/* 22 - 'u' */ L('u'), 'U', A('u'), 'u', 'U', C('U'), +/* 23 - 'i' */ L('i'), 'I', A('i'), 'i', 'I', C('I'), +/* 24 - 'o' */ L('o'), 'O', A('o'), 'o', 'O', C('O'), +/* 25 - 'p' */ L('p'), 'P', A('p'), 'p', 'P', C('P'), +/* 26 - '[' */ L(134), 143, 0201, '[', '{', C('['), +/* 27 - ']' */ '\"', '^', A('+'), '~', ']', C(']'), +/* 28 - CR/LF */ C('M'), C('M'), CA('M'),C('M'), C('M'), C('J'), +/* 29 - Ctrl */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, +/* 30 - 'a' */ L('a'), 'A', A('a'), 'a', 'A', C('A'), +/* 31 - 's' */ L('s'), 'S', A('s'), 's', 'S', C('S'), +/* 32 - 'd' */ L('d'), 'D', A('d'), 'd', 'D', C('D'), +/* 33 - 'f' */ L('f'), 'F', A('f'), 'f', 'F', C('F'), +/* 34 - 'g' */ L('g'), 'G', A('g'), 'g', 'G', C('G'), +/* 35 - 'h' */ L('h'), 'H', A('h'), 'h', 'H', C('H'), +/* 36 - 'j' */ L('j'), 'J', A('j'), 'j', 'J', C('J'), +/* 37 - 'k' */ L('k'), 'K', A('k'), 'k', 'K', C('K'), +/* 38 - 'l' */ L('l'), 'L', A('l'), 'l', 'L', C('L'), +/* 39 - ';' */ L(0224),0231, 0224, ';', ':', C('@'), +/* 40 - '\'' */ L(0204),0216, 0204, '\'', '"', C('@'), +/* 41 - '`' */ L(21), 171, A('^'), '`', '~', C('^'), +/* 42 - SHIFT */ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 43 - '\\' */ 39, '*', A('#'), '\\', '|', C('\\'), +/* 44 - 'z' */ L('z'), 'Z', A('z'), 'z', 'Z', C('Z'), +/* 45 - 'x' */ L('x'), 'X', A('x'), 'x', 'X', C('X'), +/* 46 - 'c' */ L('c'), 'C', A('c'), 'c', 'C', C('C'), +/* 47 - 'v' */ L('v'), 'V', A('v'), 'v', 'V', C('V'), +/* 48 - 'b' */ L('b'), 'B', A('b'), 'b', 'B', C('B'), +/* 49 - 'n' */ L('n'), 'N', A('n'), 'n', 'N', C('N'), +/* 50 - 'm' */ L('m'), 'M', A('m'), 0346, 'M', C('M'), +/* 51 - ',' */ ',', ';', A(','), ',', '<', C('@'), +/* 52 - '.' */ '.', ':', A('.'), '.', '>', C('@'), +/* 53 - '/' */ '-', '_', A('-'), '/', '?', C('_'), +/* 54 - SHIFT */ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 55 - '*' */ '*', '*', A('*'), '*', '*', C('@'), +/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, ALT, +/* 57 - ' ' */ ' ', ' ', A(' '), ' ', ' ', C('@'), +/* 58 - CapsLck */ CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, +/* 59 - F1 */ F1, SF1, AF1, AF1, ASF1, CF1, +/* 60 - F2 */ F2, SF2, AF2, AF2, ASF2, CF2, +/* 61 - F3 */ F3, SF3, AF3, AF3, ASF3, CF3, +/* 62 - F4 */ F4, SF4, AF4, AF4, ASF4, CF4, +/* 63 - F5 */ F5, SF5, AF5, AF5, ASF5, CF5, +/* 64 - F6 */ F6, SF6, AF6, AF6, ASF6, CF6, +/* 65 - F7 */ F7, SF7, AF7, AF7, ASF7, CF7, +/* 66 - F8 */ F8, SF8, AF8, AF8, ASF8, CF8, +/* 67 - F9 */ F9, SF9, AF9, AF9, ASF9, CF9, +/* 68 - F10 */ F10, SF10, AF10, AF10, ASF10, CF10, +/* 69 - NumLock */ NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, +/* 70 - ScrLock */ SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, +/* 71 - Home */ HOME, '7', AHOME, AHOME, '7', CHOME, +/* 72 - CurUp */ UP, '8', AUP, AUP, '8', CUP, +/* 73 - PgUp */ PGUP, '9', APGUP, APGUP, '9', CPGUP, +/* 74 - '-' */ NMIN, '-', ANMIN, ANMIN, '-', CNMIN, +/* 75 - Left */ LEFT, '4', ALEFT, ALEFT, '4', CLEFT, +/* 76 - MID */ MID, '5', AMID, AMID, '5', CMID, +/* 77 - Right */ RIGHT, '6', ARIGHT, ARIGHT, '6', CRIGHT, +/* 78 - '+' */ PLUS, '+', APLUS, APLUS, '+', CPLUS, +/* 79 - End */ END, '1', AEND, AEND, '1', CEND, +/* 80 - Down */ DOWN, '2', ADOWN, ADOWN, '2', CDOWN, +/* 81 - PgDown */ PGDN, '3', APGDN, APGDN, '3', CPGDN, +/* 82 - Insert */ INSRT, '0', AINSRT, AINSRT, '0', CINSRT, +/* 83 - Delete */ 0177, '.', A(0177),0177, '.', 0177, +/* 84 - Enter */ C('M'), C('M'), CA('M'),C('M'), C('M'), C('J'), +/* 85 - ??? */ 0, 0, 0, 0, 0, 0, +/* 86 - ??? */ '<', '>', A('<'), '|', '>', C('@'), +/* 87 - F11 */ F11, SF11, AF11, AF11, ASF11, CF11, +/* 88 - F12 */ F12, SF12, AF12, AF12, ASF12, CF12, +/* 89 - ??? */ 0, 0, 0, 0, 0, 0, +/* 90 - ??? */ 0, 0, 0, 0, 0, 0, +/* 91 - ??? */ 0, 0, 0, 0, 0, 0, +/* 92 - ??? */ 0, 0, 0, 0, 0, 0, +/* 93 - ??? */ 0, 0, 0, 0, 0, 0, +/* 94 - ??? */ 0, 0, 0, 0, 0, 0, +/* 95 - ??? */ 0, 0, 0, 0, 0, 0, +/* 96 - EXT_KEY */ EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, +/* 97 - ??? */ 0, 0, 0, 0, 0, 0, +/* 98 - ??? */ 0, 0, 0, 0, 0, 0, +/* 99 - ??? */ 0, 0, 0, 0, 0, 0, +/*100 - ??? */ 0, 0, 0, 0, 0, 0, +/*101 - ??? */ 0, 0, 0, 0, 0, 0, +/*102 - ??? */ 0, 0, 0, 0, 0, 0, +/*103 - ??? */ 0, 0, 0, 0, 0, 0, +/*104 - ??? */ 0, 0, 0, 0, 0, 0, +/*105 - ??? */ 0, 0, 0, 0, 0, 0, +/*106 - ??? */ 0, 0, 0, 0, 0, 0, +/*107 - ??? */ 0, 0, 0, 0, 0, 0, +/*108 - ??? */ 0, 0, 0, 0, 0, 0, +/*109 - ??? */ 0, 0, 0, 0, 0, 0, +/*110 - ??? */ 0, 0, 0, 0, 0, 0, +/*111 - ??? */ 0, 0, 0, 0, 0, 0, +/*112 - ??? */ 0, 0, 0, 0, 0, 0, +/*113 - ??? */ 0, 0, 0, 0, 0, 0, +/*114 - ??? */ 0, 0, 0, 0, 0, 0, +/*115 - ??? */ 0, 0, 0, 0, 0, 0, +/*116 - ??? */ 0, 0, 0, 0, 0, 0, +/*117 - ??? */ 0, 0, 0, 0, 0, 0, +/*118 - ??? */ 0, 0, 0, 0, 0, 0, +/*119 - ??? */ 0, 0, 0, 0, 0, 0, +/*120 - ??? */ 0, 0, 0, 0, 0, 0, +/*121 - ??? */ 0, 0, 0, 0, 0, 0, +/*122 - ??? */ 0, 0, 0, 0, 0, 0, +/*123 - ??? */ 0, 0, 0, 0, 0, 0, +/*124 - ??? */ 0, 0, 0, 0, 0, 0, +/*125 - ??? */ 0, 0, 0, 0, 0, 0, +/*126 - ??? */ 0, 0, 0, 0, 0, 0, +/*127 - ??? */ 0, 0, 0, 0, 0, 0 +}; diff --git a/drivers/tty/keymaps/spanish.src b/drivers/tty/keymaps/spanish.src new file mode 100644 index 000000000..096ef3738 --- /dev/null +++ b/drivers/tty/keymaps/spanish.src @@ -0,0 +1,138 @@ +/* Keymap for Spanish MF-2 keyboard. */ +/* Modified by Javier Garcia Martin jawa@inf.deusto.es */ + +u16_t keymap[NR_SCAN_CODES * MAP_COLS] = { + +/* scan-code !Shift Shift Alt AltGr Alt+Sh Ctrl */ +/* +==================================================================== +*/ +/* 00 - none */ 0, 0, 0, 0, 0, 0, +/* 01 - ESC */ C('['), C('['), CA('['),C('['), C('['), C('['), +/* 02 - '1' */ '1', '!', A('1'), '|', '!', C('A'), +/* 03 - '2' */ '2', '"', A('2'), '@', '"', C('@'), +/* 04 - '3' */ '3', 0372, A('3'), '#', 0372, C('C'), +/* 05 - '4' */ '4', '$', A('4'), '4', '$', C('D'), +/* 06 - '5' */ '5', '%', A('5'), '5', '%', C('E'), +/* 07 - '6' */ '6', '&', A('6'), 0252, '&', C('F'), +/* 08 - '7' */ '7', '/', A('7'), '{', '/', C('G'), +/* 09 - '8' */ '8', '(', A('8'), '(', '(', C('H'), +/* 10 - '9' */ '9', ')', A('9'), ')', ')', C('I'), +/* 11 - '0' */ '0', '=', A('0'), '=', '=', C('@'), +/* 12 - '-' */ '\'', '?', A('\''),'?', '?', C('_'), +/* 13 - '=' */ 0255, 0250, A(0255),0250, 0250, C('@'), +/* 14 - BS */ C('H'), C('H'), CA('H'),C('H'), C('H'), 0177, +/* 15 - TAB */ C('I'), C('I'), CA('I'),C('I'), C('I'), C('I'), +/* 16 - 'q' */ L('q'), 'Q', A('q'), 'q', 'Q', C('Q'), +/* 17 - 'w' */ L('w'), 'W', A('w'), 'w', 'W', C('W'), +/* 18 - 'e' */ L('e'), 'E', A('e'), 'e', 'E', C('E'), +/* 19 - 'r' */ L('r'), 'R', A('r'), 'r', 'R', C('R'), +/* 20 - 't' */ L('t'), 'T', A('t'), 't', 'T', C('T'), +/* 21 - 'y' */ L('y'), 'Y', A('y'), 'y', 'Y', C('Y'), +/* 22 - 'u' */ L('u'), 'U', A('u'), 'u', 'U', C('U'), +/* 23 - 'i' */ L('i'), 'I', A('i'), 'i', 'I', C('I'), +/* 24 - 'o' */ L('o'), 'O', A('o'), 'o', 'O', C('O'), +/* 25 - 'p' */ L('p'), 'P', A('p'), 'p', 'P', C('P'), +/* 26 - '[' */ '`', '^', A('`'),'[', '^', C('['), +/* 27 - ']' */ '+', '*', A('+'), ']', '*', C(']'), +/* 28 - CR/LF */ C('M'), C('M'), CA('M'),C('M'), C('M'), C('J'), +/* 29 - Ctrl */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, +/* 30 - 'a' */ L('a'), 'A', A('a'), 'a', 'A', C('A'), +/* 31 - 's' */ L('s'), 'S', A('s'), 's', 'S', C('S'), +/* 32 - 'd' */ L('d'), 'D', A('d'), 'd', 'D', C('D'), +/* 33 - 'f' */ L('f'), 'F', A('f'), 'f', 'F', C('F'), +/* 34 - 'g' */ L('g'), 'G', A('g'), 'g', 'G', C('G'), +/* 35 - 'h' */ L('h'), 'H', A('h'), 'h', 'H', C('H'), +/* 36 - 'j' */ L('j'), 'J', A('j'), 'j', 'J', C('J'), +/* 37 - 'k' */ L('k'), 'K', A('k'), 'k', 'K', C('K'), +/* 38 - 'l' */ L('l'), 'L', A('l'), 'l', 'L', C('L'), +/* 39 - ';' */ L(0244),0245, A(0244),0244, 0245, C('@'), +/* 40 - '\'' */ '\'', '"', A('\''),'{', '"', C('@'), +/* 41 - '`' */ 0247, 0246, A(0247),'\\', 0246, C('@'), +/* 42 - l. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 43 - '\\' */ L(0207),0200, A(0207),'}', 0200, C('@'), +/* 44 - 'z' */ L('z'), 'Z', A('z'), 'z', 'Z', C('Z'), +/* 45 - 'x' */ L('x'), 'X', A('x'), 'x', 'X', C('X'), +/* 46 - 'c' */ L('c'), 'C', A('c'), 'c', 'C', C('C'), +/* 47 - 'v' */ L('v'), 'V', A('v'), 'v', 'V', C('V'), +/* 48 - 'b' */ L('b'), 'B', A('b'), 'b', 'B', C('B'), +/* 49 - 'n' */ L('n'), 'N', A('n'), 'n', 'N', C('N'), +/* 50 - 'm' */ L('m'), 'M', A('m'), 'm', 'M', C('M'), +/* 51 - ',' */ ',', ';', A(','), ',', ';', C('@'), +/* 52 - '.' */ '.', ':', A('.'), '.', ':', C('@'), +/* 53 - '/' */ '-', '_', A('-'), '-', '_', C('@'), +/* 54 - r. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 55 - '*' */ '*', '*', A('*'), '*', '*', C('M'), +/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, ALT, +/* 57 - ' ' */ ' ', ' ', A(' '), ' ', ' ', C('@'), +/* 58 - CapsLck */ CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, +/* 59 - F1 */ F1, SF1, AF1, AF1, ASF1, CF1, +/* 60 - F2 */ F2, SF2, AF2, AF2, ASF2, CF2, +/* 61 - F3 */ F3, SF3, AF3, AF3, ASF3, CF3, +/* 62 - F4 */ F4, SF4, AF4, AF4, ASF4, CF4, +/* 63 - F5 */ F5, SF5, AF5, AF5, ASF5, CF5, +/* 64 - F6 */ F6, SF6, AF6, AF6, ASF6, CF6, +/* 65 - F7 */ F7, SF7, AF7, AF7, ASF7, CF7, +/* 66 - F8 */ F8, SF8, AF8, AF8, ASF8, CF8, +/* 67 - F9 */ F9, SF9, AF9, AF9, ASF9, CF9, +/* 68 - F10 */ F10, SF10, AF10, AF10, ASF10, CF10, +/* 69 - NumLock */ NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, +/* 70 - ScrLock */ SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, +/* 71 - Home */ HOME, '7', AHOME, AHOME, '7', CHOME, +/* 72 - CurUp */ UP, '8', AUP, AUP, '8', CUP, +/* 73 - PgUp */ PGUP, '9', APGUP, APGUP, '9', CPGUP, +/* 74 - '-' */ NMIN, '-', ANMIN, ANMIN, '-', CNMIN, +/* 75 - Left */ LEFT, '4', ALEFT, ALEFT, '4', CLEFT, +/* 76 - MID */ MID, '5', AMID, AMID, '5', CMID, +/* 77 - Right */ RIGHT, '6', ARIGHT, ARIGHT, '6', CRIGHT, +/* 78 - '+' */ PLUS, '+', APLUS, APLUS, '+', CPLUS, +/* 79 - End */ END, '1', AEND, AEND, '1', CEND, +/* 80 - Down */ DOWN, '2', ADOWN, ADOWN, '2', CDOWN, +/* 81 - PgDown */ PGDN, '3', APGDN, APGDN, '3', CPGDN, +/* 82 - Insert */ INSRT, '0', AINSRT, AINSRT, '0', CINSRT, +/* 83 - Delete */ 0177, '.', A(0177),0177, '.', 0177, +/* 84 - Enter */ C('M'), C('M'), CA('M'),C('M'), C('M'), C('J'), +/* 85 - ??? */ 0, 0, 0, 0, 0, 0, +/* 86 - ??? */ '<', '>', A('<'), '<', '>', C('@'), +/* 87 - F11 */ F11, SF11, AF11, AF11, ASF11, CF11, +/* 88 - F12 */ F12, SF12, AF12, AF12, ASF12, CF12, +/* 89 - ??? */ 0, 0, 0, 0, 0, 0, +/* 90 - ??? */ 0, 0, 0, 0, 0, 0, +/* 91 - ??? */ 0, 0, 0, 0, 0, 0, +/* 92 - ??? */ 0, 0, 0, 0, 0, 0, +/* 93 - ??? */ 0, 0, 0, 0, 0, 0, +/* 94 - ??? */ 0, 0, 0, 0, 0, 0, +/* 95 - ??? */ 0, 0, 0, 0, 0, 0, +/* 96 - EXT_KEY */ EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, +/* 97 - ??? */ 0, 0, 0, 0, 0, 0, +/* 98 - ??? */ 0, 0, 0, 0, 0, 0, +/* 99 - ??? */ 0, 0, 0, 0, 0, 0, +/*100 - ??? */ 0, 0, 0, 0, 0, 0, +/*101 - ??? */ 0, 0, 0, 0, 0, 0, +/*102 - ??? */ 0, 0, 0, 0, 0, 0, +/*103 - ??? */ 0, 0, 0, 0, 0, 0, +/*104 - ??? */ 0, 0, 0, 0, 0, 0, +/*105 - ??? */ 0, 0, 0, 0, 0, 0, +/*106 - ??? */ 0, 0, 0, 0, 0, 0, +/*107 - ??? */ 0, 0, 0, 0, 0, 0, +/*108 - ??? */ 0, 0, 0, 0, 0, 0, +/*109 - ??? */ 0, 0, 0, 0, 0, 0, +/*110 - ??? */ 0, 0, 0, 0, 0, 0, +/*111 - ??? */ 0, 0, 0, 0, 0, 0, +/*112 - ??? */ 0, 0, 0, 0, 0, 0, +/*113 - ??? */ 0, 0, 0, 0, 0, 0, +/*114 - ??? */ 0, 0, 0, 0, 0, 0, +/*115 - ??? */ 0, 0, 0, 0, 0, 0, +/*116 - ??? */ 0, 0, 0, 0, 0, 0, +/*117 - ??? */ 0, 0, 0, 0, 0, 0, +/*118 - ??? */ 0, 0, 0, 0, 0, 0, +/*119 - ??? */ 0, 0, 0, 0, 0, 0, +/*120 - ??? */ 0, 0, 0, 0, 0, 0, +/*121 - ??? */ 0, 0, 0, 0, 0, 0, +/*122 - ??? */ 0, 0, 0, 0, 0, 0, +/*123 - ??? */ 0, 0, 0, 0, 0, 0, +/*124 - ??? */ 0, 0, 0, 0, 0, 0, +/*125 - ??? */ 0, 0, 0, 0, 0, 0, +/*126 - ??? */ 0, 0, 0, 0, 0, 0, +/*127 - ??? */ 0, 0, 0, 0, 0, 0 +}; diff --git a/drivers/tty/keymaps/uk.src b/drivers/tty/keymaps/uk.src new file mode 100644 index 000000000..e7cc9fbd8 --- /dev/null +++ b/drivers/tty/keymaps/uk.src @@ -0,0 +1,135 @@ +/* Keymap for standard UK keyboard. Author: Darren Mason */ + +u16_t keymap[NR_SCAN_CODES * MAP_COLS] = { + +/* scan-code !Shift Shift Alt1 Alt2 Alt+Sh Ctrl */ +/* ==================================================================== */ +/* 00 - none */ 0, 0, 0, 0, 0, 0, +/* 01 - ESC */ C('['), C('['), CA('['),CA('['),CA('['),C('['), +/* 02 - '1' */ '1', '!', A('1'), A('1'), A('!'), C('A'), +/* 03 - '2' */ '2', '"', A('2'), A('2'), A('@'), C('@'), +/* 04 - '3' */ '3', 156, A('3'), A('3'), A(156), C('C'), +/* 05 - '4' */ '4', '$', A('4'), A('4'), A('$'), C('D'), +/* 06 - '5' */ '5', '%', A('5'), A('5'), A('%'), C('E'), +/* 07 - '6' */ '6', '^', A('6'), A('6'), A('^'), C('^'), +/* 08 - '7' */ '7', '&', A('7'), A('7'), A('&'), C('G'), +/* 09 - '8' */ '8', '*', A('8'), A('8'), A('*'), C('H'), +/* 10 - '9' */ '9', '(', A('9'), A('9'), A('('), C('I'), +/* 11 - '0' */ '0', ')', A('0'), A('0'), A(')'), C('@'), +/* 12 - '-' */ '-', '_', A('-'), A('-'), A('_'), C('_'), +/* 13 - '=' */ '=', '+', A('='), A('='), A('+'), C('@'), +/* 14 - BS */ C('H'), C('H'), CA('H'),CA('H'),CA('H'),0177, +/* 15 - TAB */ C('I'), C('I'), CA('I'),CA('I'),CA('I'),C('I'), +/* 16 - 'q' */ L('q'), 'Q', A('q'), A('q'), A('Q'), C('Q'), +/* 17 - 'w' */ L('w'), 'W', A('w'), A('w'), A('W'), C('W'), +/* 18 - 'e' */ L('e'), 'E', A('e'), A('e'), A('E'), C('E'), +/* 19 - 'r' */ L('r'), 'R', A('r'), A('r'), A('R'), C('R'), +/* 20 - 't' */ L('t'), 'T', A('t'), A('t'), A('T'), C('T'), +/* 21 - 'y' */ L('y'), 'Y', A('y'), A('y'), A('Y'), C('Y'), +/* 22 - 'u' */ L('u'), 'U', A('u'), A('u'), A('U'), C('U'), +/* 23 - 'i' */ L('i'), 'I', A('i'), A('i'), A('I'), C('I'), +/* 24 - 'o' */ L('o'), 'O', A('o'), A('o'), A('O'), C('O'), +/* 25 - 'p' */ L('p'), 'P', A('p'), A('p'), A('P'), C('P'), +/* 26 - '[' */ '[', '{', A('['), A('['), A('{'), C('['), +/* 27 - ']' */ ']', '}', A(']'), A(']'), A('}'), C(']'), +/* 28 - CR/LF */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 29 - Ctrl */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, +/* 30 - 'a' */ L('a'), 'A', A('a'), A('a'), A('A'), C('A'), +/* 31 - 's' */ L('s'), 'S', A('s'), A('s'), A('S'), C('S'), +/* 32 - 'd' */ L('d'), 'D', A('d'), A('d'), A('D'), C('D'), +/* 33 - 'f' */ L('f'), 'F', A('f'), A('f'), A('F'), C('F'), +/* 34 - 'g' */ L('g'), 'G', A('g'), A('g'), A('G'), C('G'), +/* 35 - 'h' */ L('h'), 'H', A('h'), A('h'), A('H'), C('H'), +/* 36 - 'j' */ L('j'), 'J', A('j'), A('j'), A('J'), C('J'), +/* 37 - 'k' */ L('k'), 'K', A('k'), A('k'), A('K'), C('K'), +/* 38 - 'l' */ L('l'), 'L', A('l'), A('l'), A('L'), C('L'), +/* 39 - ';' */ ';', ':', A(';'), A(';'), A(':'), C('@'), +/* 40 - '\'' */ '\'', '@', A('\''),A('\''),A('"'), C('@'), +/* 41 - '`' */ '`', '~', A('`'), A('`'), A('~'), C('@'), +/* 42 - l. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 43 - '#' */ '#', '~', A('#'), A('#'), A('~'), C('#'), +/* 44 - 'z' */ L('z'), 'Z', A('z'), A('z'), A('Z'), C('Z'), +/* 45 - 'x' */ L('x'), 'X', A('x'), A('x'), A('X'), C('X'), +/* 46 - 'c' */ L('c'), 'C', A('c'), A('c'), A('C'), C('C'), +/* 47 - 'v' */ L('v'), 'V', A('v'), A('v'), A('V'), C('V'), +/* 48 - 'b' */ L('b'), 'B', A('b'), A('b'), A('B'), C('B'), +/* 49 - 'n' */ L('n'), 'N', A('n'), A('n'), A('N'), C('N'), +/* 50 - 'm' */ L('m'), 'M', A('m'), A('m'), A('M'), C('M'), +/* 51 - ',' */ ',', '<', A(','), A(','), A('<'), C('@'), +/* 52 - '.' */ '.', '>', A('.'), A('.'), A('>'), C('@'), +/* 53 - '/' */ '/', '?', A('/'), A('/'), A('?'), C('@'), +/* 54 - r. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 55 - '*' */ '*', '*', A('*'), A('*'), A('*'), C('@'), +/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, ALT, +/* 57 - ' ' */ ' ', ' ', A(' '), A(' '), A(' '), C('@'), +/* 58 - CapsLck */ CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, +/* 59 - F1 */ F1, SF1, AF1, AF1, ASF1, CF1, +/* 60 - F2 */ F2, SF2, AF2, AF2, ASF2, CF2, +/* 61 - F3 */ F3, SF3, AF3, AF3, ASF3, CF3, +/* 62 - F4 */ F4, SF4, AF4, AF4, ASF4, CF4, +/* 63 - F5 */ F5, SF5, AF5, AF5, ASF5, CF5, +/* 64 - F6 */ F6, SF6, AF6, AF6, ASF6, CF6, +/* 65 - F7 */ F7, SF7, AF7, AF7, ASF7, CF7, +/* 66 - F8 */ F8, SF8, AF8, AF8, ASF8, CF8, +/* 67 - F9 */ F9, SF9, AF9, AF9, ASF9, CF9, +/* 68 - F10 */ F10, SF10, AF10, AF10, ASF10, CF10, +/* 69 - NumLock */ NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, +/* 70 - ScrLock */ SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, +/* 71 - Home */ HOME, '7', AHOME, AHOME, A('7'), CHOME, +/* 72 - CurUp */ UP, '8', AUP, AUP, A('8'), CUP, +/* 73 - PgUp */ PGUP, '9', APGUP, APGUP, A('9'), CPGUP, +/* 74 - '-' */ NMIN, '-', ANMIN, ANMIN, A('-'), CNMIN, +/* 75 - Left */ LEFT, '4', ALEFT, ALEFT, A('4'), CLEFT, +/* 76 - MID */ MID, '5', AMID, AMID, A('5'), CMID, +/* 77 - Right */ RIGHT, '6', ARIGHT, ARIGHT, A('6'), CRIGHT, +/* 78 - '+' */ PLUS, '+', APLUS, APLUS, A('+'), CPLUS, +/* 79 - End */ END, '1', AEND, AEND, A('1'), CEND, +/* 80 - Down */ DOWN, '2', ADOWN, ADOWN, A('2'), CDOWN, +/* 81 - PgDown */ PGDN, '3', APGDN, APGDN, A('3'), CPGDN, +/* 82 - Insert */ INSRT, '0', AINSRT, AINSRT, A('0'), CINSRT, +/* 83 - Delete */ 0177, '.', A(0177),A(0177),A('.'), 0177, +/* 84 - Enter */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 85 - ??? */ 0, 0, 0, 0, 0, 0, +/* 86 - ??? */ '\\', '|', A('\\'),A('|'), A('|'), C('@'), +/* 87 - F11 */ F11, SF11, AF11, AF11, ASF11, CF11, +/* 88 - F12 */ F12, SF12, AF12, AF12, ASF12, CF12, +/* 89 - ??? */ 0, 0, 0, 0, 0, 0, +/* 90 - ??? */ 0, 0, 0, 0, 0, 0, +/* 91 - ??? */ 0, 0, 0, 0, 0, 0, +/* 92 - ??? */ 0, 0, 0, 0, 0, 0, +/* 93 - ??? */ 0, 0, 0, 0, 0, 0, +/* 94 - ??? */ 0, 0, 0, 0, 0, 0, +/* 95 - ??? */ 0, 0, 0, 0, 0, 0, +/* 96 - EXT_KEY */ EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, +/* 97 - ??? */ 0, 0, 0, 0, 0, 0, +/* 98 - ??? */ 0, 0, 0, 0, 0, 0, +/* 99 - ??? */ 0, 0, 0, 0, 0, 0, +/*100 - ??? */ 0, 0, 0, 0, 0, 0, +/*101 - ??? */ 0, 0, 0, 0, 0, 0, +/*102 - ??? */ 0, 0, 0, 0, 0, 0, +/*103 - ??? */ 0, 0, 0, 0, 0, 0, +/*104 - ??? */ 0, 0, 0, 0, 0, 0, +/*105 - ??? */ 0, 0, 0, 0, 0, 0, +/*106 - ??? */ 0, 0, 0, 0, 0, 0, +/*107 - ??? */ 0, 0, 0, 0, 0, 0, +/*108 - ??? */ 0, 0, 0, 0, 0, 0, +/*109 - ??? */ 0, 0, 0, 0, 0, 0, +/*110 - ??? */ 0, 0, 0, 0, 0, 0, +/*111 - ??? */ 0, 0, 0, 0, 0, 0, +/*112 - ??? */ 0, 0, 0, 0, 0, 0, +/*113 - ??? */ 0, 0, 0, 0, 0, 0, +/*114 - ??? */ 0, 0, 0, 0, 0, 0, +/*115 - ??? */ 0, 0, 0, 0, 0, 0, +/*116 - ??? */ 0, 0, 0, 0, 0, 0, +/*117 - ??? */ 0, 0, 0, 0, 0, 0, +/*118 - ??? */ 0, 0, 0, 0, 0, 0, +/*119 - ??? */ 0, 0, 0, 0, 0, 0, +/*120 - ??? */ 0, 0, 0, 0, 0, 0, +/*121 - ??? */ 0, 0, 0, 0, 0, 0, +/*122 - ??? */ 0, 0, 0, 0, 0, 0, +/*123 - ??? */ 0, 0, 0, 0, 0, 0, +/*124 - ??? */ 0, 0, 0, 0, 0, 0, +/*125 - ??? */ 0, 0, 0, 0, 0, 0, +/*126 - ??? */ 0, 0, 0, 0, 0, 0, +/*127 - ??? */ 0, 0, 0, 0, 0, 0 +}; diff --git a/drivers/tty/keymaps/us-std.src b/drivers/tty/keymaps/us-std.src new file mode 100644 index 000000000..ea4e33432 --- /dev/null +++ b/drivers/tty/keymaps/us-std.src @@ -0,0 +1,135 @@ +/* Keymap for US MF-2 keyboard. */ + +u16_t keymap[NR_SCAN_CODES * MAP_COLS] = { + +/* scan-code !Shift Shift Alt1 Alt2 Alt+Sh Ctrl */ +/* ==================================================================== */ +/* 00 - none */ 0, 0, 0, 0, 0, 0, +/* 01 - ESC */ C('['), C('['), CA('['),CA('['),CA('['),C('['), +/* 02 - '1' */ '1', '!', A('1'), A('1'), A('!'), C('A'), +/* 03 - '2' */ '2', '@', A('2'), A('2'), A('@'), C('@'), +/* 04 - '3' */ '3', '#', A('3'), A('3'), A('#'), C('C'), +/* 05 - '4' */ '4', '$', A('4'), A('4'), A('$'), C('D'), +/* 06 - '5' */ '5', '%', A('5'), A('5'), A('%'), C('E'), +/* 07 - '6' */ '6', '^', A('6'), A('6'), A('^'), C('^'), +/* 08 - '7' */ '7', '&', A('7'), A('7'), A('&'), C('G'), +/* 09 - '8' */ '8', '*', A('8'), A('8'), A('*'), C('H'), +/* 10 - '9' */ '9', '(', A('9'), A('9'), A('('), C('I'), +/* 11 - '0' */ '0', ')', A('0'), A('0'), A(')'), C('@'), +/* 12 - '-' */ '-', '_', A('-'), A('-'), A('_'), C('_'), +/* 13 - '=' */ '=', '+', A('='), A('='), A('+'), C('@'), +/* 14 - BS */ C('H'), C('H'), CA('H'),CA('H'),CA('H'),0177, +/* 15 - TAB */ C('I'), C('I'), CA('I'),CA('I'),CA('I'),C('I'), +/* 16 - 'q' */ L('q'), 'Q', A('q'), A('q'), A('Q'), C('Q'), +/* 17 - 'w' */ L('w'), 'W', A('w'), A('w'), A('W'), C('W'), +/* 18 - 'e' */ L('e'), 'E', A('e'), A('e'), A('E'), C('E'), +/* 19 - 'r' */ L('r'), 'R', A('r'), A('r'), A('R'), C('R'), +/* 20 - 't' */ L('t'), 'T', A('t'), A('t'), A('T'), C('T'), +/* 21 - 'y' */ L('y'), 'Y', A('y'), A('y'), A('Y'), C('Y'), +/* 22 - 'u' */ L('u'), 'U', A('u'), A('u'), A('U'), C('U'), +/* 23 - 'i' */ L('i'), 'I', A('i'), A('i'), A('I'), C('I'), +/* 24 - 'o' */ L('o'), 'O', A('o'), A('o'), A('O'), C('O'), +/* 25 - 'p' */ L('p'), 'P', A('p'), A('p'), A('P'), C('P'), +/* 26 - '[' */ '[', '{', A('['), A('['), A('{'), C('['), +/* 27 - ']' */ ']', '}', A(']'), A(']'), A('}'), C(']'), +/* 28 - CR/LF */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 29 - Ctrl */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, +/* 30 - 'a' */ L('a'), 'A', A('a'), A('a'), A('A'), C('A'), +/* 31 - 's' */ L('s'), 'S', A('s'), A('s'), A('S'), C('S'), +/* 32 - 'd' */ L('d'), 'D', A('d'), A('d'), A('D'), C('D'), +/* 33 - 'f' */ L('f'), 'F', A('f'), A('f'), A('F'), C('F'), +/* 34 - 'g' */ L('g'), 'G', A('g'), A('g'), A('G'), C('G'), +/* 35 - 'h' */ L('h'), 'H', A('h'), A('h'), A('H'), C('H'), +/* 36 - 'j' */ L('j'), 'J', A('j'), A('j'), A('J'), C('J'), +/* 37 - 'k' */ L('k'), 'K', A('k'), A('k'), A('K'), C('K'), +/* 38 - 'l' */ L('l'), 'L', A('l'), A('l'), A('L'), C('L'), +/* 39 - ';' */ ';', ':', A(';'), A(';'), A(':'), C('@'), +/* 40 - '\'' */ '\'', '"', A('\''),A('\''),A('"'), C('@'), +/* 41 - '`' */ '`', '~', A('`'), A('`'), A('~'), C('@'), +/* 42 - l. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 43 - '\\' */ '\\', '|', A('\\'),A('\\'),A('|'), C('\\'), +/* 44 - 'z' */ L('z'), 'Z', A('z'), A('z'), A('Z'), C('Z'), +/* 45 - 'x' */ L('x'), 'X', A('x'), A('x'), A('X'), C('X'), +/* 46 - 'c' */ L('c'), 'C', A('c'), A('c'), A('C'), C('C'), +/* 47 - 'v' */ L('v'), 'V', A('v'), A('v'), A('V'), C('V'), +/* 48 - 'b' */ L('b'), 'B', A('b'), A('b'), A('B'), C('B'), +/* 49 - 'n' */ L('n'), 'N', A('n'), A('n'), A('N'), C('N'), +/* 50 - 'm' */ L('m'), 'M', A('m'), A('m'), A('M'), C('M'), +/* 51 - ',' */ ',', '<', A(','), A(','), A('<'), C('@'), +/* 52 - '.' */ '.', '>', A('.'), A('.'), A('>'), C('@'), +/* 53 - '/' */ '/', '?', A('/'), A('/'), A('?'), C('@'), +/* 54 - r. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 55 - '*' */ '*', '*', A('*'), A('*'), A('*'), C('@'), +/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, ALT, +/* 57 - ' ' */ ' ', ' ', A(' '), A(' '), A(' '), C('@'), +/* 58 - CapsLck */ CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, +/* 59 - F1 */ F1, SF1, AF1, AF1, ASF1, CF1, +/* 60 - F2 */ F2, SF2, AF2, AF2, ASF2, CF2, +/* 61 - F3 */ F3, SF3, AF3, AF3, ASF3, CF3, +/* 62 - F4 */ F4, SF4, AF4, AF4, ASF4, CF4, +/* 63 - F5 */ F5, SF5, AF5, AF5, ASF5, CF5, +/* 64 - F6 */ F6, SF6, AF6, AF6, ASF6, CF6, +/* 65 - F7 */ F7, SF7, AF7, AF7, ASF7, CF7, +/* 66 - F8 */ F8, SF8, AF8, AF8, ASF8, CF8, +/* 67 - F9 */ F9, SF9, AF9, AF9, ASF9, CF9, +/* 68 - F10 */ F10, SF10, AF10, AF10, ASF10, CF10, +/* 69 - NumLock */ NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, +/* 70 - ScrLock */ SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, +/* 71 - Home */ HOME, '7', AHOME, AHOME, A('7'), CHOME, +/* 72 - CurUp */ UP, '8', AUP, AUP, A('8'), CUP, +/* 73 - PgUp */ PGUP, '9', APGUP, APGUP, A('9'), CPGUP, +/* 74 - '-' */ NMIN, '-', ANMIN, ANMIN, A('-'), CNMIN, +/* 75 - Left */ LEFT, '4', ALEFT, ALEFT, A('4'), CLEFT, +/* 76 - MID */ MID, '5', AMID, AMID, A('5'), CMID, +/* 77 - Right */ RIGHT, '6', ARIGHT, ARIGHT, A('6'), CRIGHT, +/* 78 - '+' */ PLUS, '+', APLUS, APLUS, A('+'), CPLUS, +/* 79 - End */ END, '1', AEND, AEND, A('1'), CEND, +/* 80 - Down */ DOWN, '2', ADOWN, ADOWN, A('2'), CDOWN, +/* 81 - PgDown */ PGDN, '3', APGDN, APGDN, A('3'), CPGDN, +/* 82 - Insert */ INSRT, '0', AINSRT, AINSRT, A('0'), CINSRT, +/* 83 - Delete */ 0177, '.', A(0177),A(0177),A('.'), 0177, +/* 84 - Enter */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 85 - ??? */ 0, 0, 0, 0, 0, 0, +/* 86 - ??? */ '<', '>', A('<'), A('|'), A('>'), C('@'), +/* 87 - F11 */ F11, SF11, AF11, AF11, ASF11, CF11, +/* 88 - F12 */ F12, SF12, AF12, AF12, ASF12, CF12, +/* 89 - ??? */ 0, 0, 0, 0, 0, 0, +/* 90 - ??? */ 0, 0, 0, 0, 0, 0, +/* 91 - ??? */ 0, 0, 0, 0, 0, 0, +/* 92 - ??? */ 0, 0, 0, 0, 0, 0, +/* 93 - ??? */ 0, 0, 0, 0, 0, 0, +/* 94 - ??? */ 0, 0, 0, 0, 0, 0, +/* 95 - ??? */ 0, 0, 0, 0, 0, 0, +/* 96 - EXT_KEY */ EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, +/* 97 - ??? */ 0, 0, 0, 0, 0, 0, +/* 98 - ??? */ 0, 0, 0, 0, 0, 0, +/* 99 - ??? */ 0, 0, 0, 0, 0, 0, +/*100 - ??? */ 0, 0, 0, 0, 0, 0, +/*101 - ??? */ 0, 0, 0, 0, 0, 0, +/*102 - ??? */ 0, 0, 0, 0, 0, 0, +/*103 - ??? */ 0, 0, 0, 0, 0, 0, +/*104 - ??? */ 0, 0, 0, 0, 0, 0, +/*105 - ??? */ 0, 0, 0, 0, 0, 0, +/*106 - ??? */ 0, 0, 0, 0, 0, 0, +/*107 - ??? */ 0, 0, 0, 0, 0, 0, +/*108 - ??? */ 0, 0, 0, 0, 0, 0, +/*109 - ??? */ 0, 0, 0, 0, 0, 0, +/*110 - ??? */ 0, 0, 0, 0, 0, 0, +/*111 - ??? */ 0, 0, 0, 0, 0, 0, +/*112 - ??? */ 0, 0, 0, 0, 0, 0, +/*113 - ??? */ 0, 0, 0, 0, 0, 0, +/*114 - ??? */ 0, 0, 0, 0, 0, 0, +/*115 - ??? */ 0, 0, 0, 0, 0, 0, +/*116 - ??? */ 0, 0, 0, 0, 0, 0, +/*117 - ??? */ 0, 0, 0, 0, 0, 0, +/*118 - ??? */ 0, 0, 0, 0, 0, 0, +/*119 - ??? */ 0, 0, 0, 0, 0, 0, +/*120 - ??? */ 0, 0, 0, 0, 0, 0, +/*121 - ??? */ 0, 0, 0, 0, 0, 0, +/*122 - ??? */ 0, 0, 0, 0, 0, 0, +/*123 - ??? */ 0, 0, 0, 0, 0, 0, +/*124 - ??? */ 0, 0, 0, 0, 0, 0, +/*125 - ??? */ 0, 0, 0, 0, 0, 0, +/*126 - ??? */ 0, 0, 0, 0, 0, 0, +/*127 - ??? */ 0, 0, 0, 0, 0, 0 +}; diff --git a/drivers/tty/keymaps/us-swap.src b/drivers/tty/keymaps/us-swap.src new file mode 100644 index 000000000..16d84a012 --- /dev/null +++ b/drivers/tty/keymaps/us-swap.src @@ -0,0 +1,135 @@ +/* Keymap for US MF-2 keyboard with the Caps Lock and Control key swapped. */ + +u16_t keymap[NR_SCAN_CODES * MAP_COLS] = { + +/* scan-code !Shift Shift Alt1 Alt2 Alt+Sh Ctrl */ +/* ==================================================================== */ +/* 00 - none */ 0, 0, 0, 0, 0, 0, +/* 01 - ESC */ C('['), C('['), CA('['),CA('['),CA('['),C('['), +/* 02 - '1' */ '1', '!', A('1'), A('1'), A('!'), C('A'), +/* 03 - '2' */ '2', '@', A('2'), A('2'), A('@'), C('@'), +/* 04 - '3' */ '3', '#', A('3'), A('3'), A('#'), C('C'), +/* 05 - '4' */ '4', '$', A('4'), A('4'), A('$'), C('D'), +/* 06 - '5' */ '5', '%', A('5'), A('5'), A('%'), C('E'), +/* 07 - '6' */ '6', '^', A('6'), A('6'), A('^'), C('^'), +/* 08 - '7' */ '7', '&', A('7'), A('7'), A('&'), C('G'), +/* 09 - '8' */ '8', '*', A('8'), A('8'), A('*'), C('H'), +/* 10 - '9' */ '9', '(', A('9'), A('9'), A('('), C('I'), +/* 11 - '0' */ '0', ')', A('0'), A('0'), A(')'), C('@'), +/* 12 - '-' */ '-', '_', A('-'), A('-'), A('_'), C('_'), +/* 13 - '=' */ '=', '+', A('='), A('='), A('+'), C('@'), +/* 14 - BS */ C('H'), C('H'), CA('H'),CA('H'),CA('H'),0177, +/* 15 - TAB */ C('I'), C('I'), CA('I'),CA('I'),CA('I'),C('I'), +/* 16 - 'q' */ L('q'), 'Q', A('q'), A('q'), A('Q'), C('Q'), +/* 17 - 'w' */ L('w'), 'W', A('w'), A('w'), A('W'), C('W'), +/* 18 - 'e' */ L('e'), 'E', A('e'), A('e'), A('E'), C('E'), +/* 19 - 'r' */ L('r'), 'R', A('r'), A('r'), A('R'), C('R'), +/* 20 - 't' */ L('t'), 'T', A('t'), A('t'), A('T'), C('T'), +/* 21 - 'y' */ L('y'), 'Y', A('y'), A('y'), A('Y'), C('Y'), +/* 22 - 'u' */ L('u'), 'U', A('u'), A('u'), A('U'), C('U'), +/* 23 - 'i' */ L('i'), 'I', A('i'), A('i'), A('I'), C('I'), +/* 24 - 'o' */ L('o'), 'O', A('o'), A('o'), A('O'), C('O'), +/* 25 - 'p' */ L('p'), 'P', A('p'), A('p'), A('P'), C('P'), +/* 26 - '[' */ '[', '{', A('['), A('['), A('{'), C('['), +/* 27 - ']' */ ']', '}', A(']'), A(']'), A('}'), C(']'), +/* 28 - CR/LF */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 29 - Ctrl */ CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, CALOCK, +/* 30 - 'a' */ L('a'), 'A', A('a'), A('a'), A('A'), C('A'), +/* 31 - 's' */ L('s'), 'S', A('s'), A('s'), A('S'), C('S'), +/* 32 - 'd' */ L('d'), 'D', A('d'), A('d'), A('D'), C('D'), +/* 33 - 'f' */ L('f'), 'F', A('f'), A('f'), A('F'), C('F'), +/* 34 - 'g' */ L('g'), 'G', A('g'), A('g'), A('G'), C('G'), +/* 35 - 'h' */ L('h'), 'H', A('h'), A('h'), A('H'), C('H'), +/* 36 - 'j' */ L('j'), 'J', A('j'), A('j'), A('J'), C('J'), +/* 37 - 'k' */ L('k'), 'K', A('k'), A('k'), A('K'), C('K'), +/* 38 - 'l' */ L('l'), 'L', A('l'), A('l'), A('L'), C('L'), +/* 39 - ';' */ ';', ':', A(';'), A(';'), A(':'), C('@'), +/* 40 - '\'' */ '\'', '"', A('\''),A('\''),A('"'), C('@'), +/* 41 - '`' */ '`', '~', A('`'), A('`'), A('~'), C('@'), +/* 42 - l. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 43 - '\\' */ '\\', '|', A('\\'),A('\\'),A('|'), C('\\'), +/* 44 - 'z' */ L('z'), 'Z', A('z'), A('z'), A('Z'), C('Z'), +/* 45 - 'x' */ L('x'), 'X', A('x'), A('x'), A('X'), C('X'), +/* 46 - 'c' */ L('c'), 'C', A('c'), A('c'), A('C'), C('C'), +/* 47 - 'v' */ L('v'), 'V', A('v'), A('v'), A('V'), C('V'), +/* 48 - 'b' */ L('b'), 'B', A('b'), A('b'), A('B'), C('B'), +/* 49 - 'n' */ L('n'), 'N', A('n'), A('n'), A('N'), C('N'), +/* 50 - 'm' */ L('m'), 'M', A('m'), A('m'), A('M'), C('M'), +/* 51 - ',' */ ',', '<', A(','), A(','), A('<'), C('@'), +/* 52 - '.' */ '.', '>', A('.'), A('.'), A('>'), C('@'), +/* 53 - '/' */ '/', '?', A('/'), A('/'), A('?'), C('@'), +/* 54 - r. SHIFT*/ SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, SHIFT, +/* 55 - '*' */ '*', '*', A('*'), A('*'), A('*'), C('@'), +/* 56 - ALT */ ALT, ALT, ALT, ALT, ALT, ALT, +/* 57 - ' ' */ ' ', ' ', A(' '), A(' '), A(' '), C('@'), +/* 58 - CapsLck */ CTRL, CTRL, CTRL, CTRL, CTRL, CTRL, +/* 59 - F1 */ F1, SF1, AF1, AF1, ASF1, CF1, +/* 60 - F2 */ F2, SF2, AF2, AF2, ASF2, CF2, +/* 61 - F3 */ F3, SF3, AF3, AF3, ASF3, CF3, +/* 62 - F4 */ F4, SF4, AF4, AF4, ASF4, CF4, +/* 63 - F5 */ F5, SF5, AF5, AF5, ASF5, CF5, +/* 64 - F6 */ F6, SF6, AF6, AF6, ASF6, CF6, +/* 65 - F7 */ F7, SF7, AF7, AF7, ASF7, CF7, +/* 66 - F8 */ F8, SF8, AF8, AF8, ASF8, CF8, +/* 67 - F9 */ F9, SF9, AF9, AF9, ASF9, CF9, +/* 68 - F10 */ F10, SF10, AF10, AF10, ASF10, CF10, +/* 69 - NumLock */ NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, NLOCK, +/* 70 - ScrLock */ SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, SLOCK, +/* 71 - Home */ HOME, '7', AHOME, AHOME, A('7'), CHOME, +/* 72 - CurUp */ UP, '8', AUP, AUP, A('8'), CUP, +/* 73 - PgUp */ PGUP, '9', APGUP, APGUP, A('9'), CPGUP, +/* 74 - '-' */ NMIN, '-', ANMIN, ANMIN, A('-'), CNMIN, +/* 75 - Left */ LEFT, '4', ALEFT, ALEFT, A('4'), CLEFT, +/* 76 - MID */ MID, '5', AMID, AMID, A('5'), CMID, +/* 77 - Right */ RIGHT, '6', ARIGHT, ARIGHT, A('6'), CRIGHT, +/* 78 - '+' */ PLUS, '+', APLUS, APLUS, A('+'), CPLUS, +/* 79 - End */ END, '1', AEND, AEND, A('1'), CEND, +/* 80 - Down */ DOWN, '2', ADOWN, ADOWN, A('2'), CDOWN, +/* 81 - PgDown */ PGDN, '3', APGDN, APGDN, A('3'), CPGDN, +/* 82 - Insert */ INSRT, '0', AINSRT, AINSRT, A('0'), CINSRT, +/* 83 - Delete */ 0177, '.', A(0177),A(0177),A('.'), 0177, +/* 84 - Enter */ C('M'), C('M'), CA('M'),CA('M'),CA('M'),C('J'), +/* 85 - ??? */ 0, 0, 0, 0, 0, 0, +/* 86 - ??? */ '<', '>', A('<'), A('|'), A('>'), C('@'), +/* 87 - F11 */ F11, SF11, AF11, AF11, ASF11, CF11, +/* 88 - F12 */ F12, SF12, AF12, AF12, ASF12, CF12, +/* 89 - ??? */ 0, 0, 0, 0, 0, 0, +/* 90 - ??? */ 0, 0, 0, 0, 0, 0, +/* 91 - ??? */ 0, 0, 0, 0, 0, 0, +/* 92 - ??? */ 0, 0, 0, 0, 0, 0, +/* 93 - ??? */ 0, 0, 0, 0, 0, 0, +/* 94 - ??? */ 0, 0, 0, 0, 0, 0, +/* 95 - ??? */ 0, 0, 0, 0, 0, 0, +/* 96 - EXT_KEY */ EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, EXTKEY, +/* 97 - ??? */ 0, 0, 0, 0, 0, 0, +/* 98 - ??? */ 0, 0, 0, 0, 0, 0, +/* 99 - ??? */ 0, 0, 0, 0, 0, 0, +/*100 - ??? */ 0, 0, 0, 0, 0, 0, +/*101 - ??? */ 0, 0, 0, 0, 0, 0, +/*102 - ??? */ 0, 0, 0, 0, 0, 0, +/*103 - ??? */ 0, 0, 0, 0, 0, 0, +/*104 - ??? */ 0, 0, 0, 0, 0, 0, +/*105 - ??? */ 0, 0, 0, 0, 0, 0, +/*106 - ??? */ 0, 0, 0, 0, 0, 0, +/*107 - ??? */ 0, 0, 0, 0, 0, 0, +/*108 - ??? */ 0, 0, 0, 0, 0, 0, +/*109 - ??? */ 0, 0, 0, 0, 0, 0, +/*110 - ??? */ 0, 0, 0, 0, 0, 0, +/*111 - ??? */ 0, 0, 0, 0, 0, 0, +/*112 - ??? */ 0, 0, 0, 0, 0, 0, +/*113 - ??? */ 0, 0, 0, 0, 0, 0, +/*114 - ??? */ 0, 0, 0, 0, 0, 0, +/*115 - ??? */ 0, 0, 0, 0, 0, 0, +/*116 - ??? */ 0, 0, 0, 0, 0, 0, +/*117 - ??? */ 0, 0, 0, 0, 0, 0, +/*118 - ??? */ 0, 0, 0, 0, 0, 0, +/*119 - ??? */ 0, 0, 0, 0, 0, 0, +/*120 - ??? */ 0, 0, 0, 0, 0, 0, +/*121 - ??? */ 0, 0, 0, 0, 0, 0, +/*122 - ??? */ 0, 0, 0, 0, 0, 0, +/*123 - ??? */ 0, 0, 0, 0, 0, 0, +/*124 - ??? */ 0, 0, 0, 0, 0, 0, +/*125 - ??? */ 0, 0, 0, 0, 0, 0, +/*126 - ??? */ 0, 0, 0, 0, 0, 0, +/*127 - ??? */ 0, 0, 0, 0, 0, 0 +}; diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c new file mode 100644 index 000000000..115fedd04 --- /dev/null +++ b/drivers/tty/pty.c @@ -0,0 +1,448 @@ +/* pty.c - pseudo terminal driver Author: Kees J. Bot + * 30 Dec 1995 + * PTYs can be seen as a bidirectional pipe with TTY + * input and output processing. For example a simple rlogin session: + * + * keyboard -> rlogin -> in.rld -> /dev/ptypX -> /dev/ttypX -> shell + * shell -> /dev/ttypX -> /dev/ptypX -> in.rld -> rlogin -> screen + * + * This file takes care of copying data between the tty/pty device pairs and + * the open/read/write/close calls on the pty devices. The TTY task takes + * care of the input and output processing (interrupt, backspace, raw I/O, + * etc.) using the pty_read() and pty_write() functions as the "keyboard" and + * "screen" functions of the ttypX devices. + * Be careful when reading this code, the terms "reading" and "writing" are + * used both for the tty and the pty end of the pseudo tty. Writes to one + * end are to be read at the other end and vice-versa. + */ + +#include "../drivers.h" +#include <termios.h> +#include <signal.h> +#include <minix/com.h> +#include <minix/callnr.h> +#include "tty.h" +#include "proc.h" + +#if NR_PTYS > 0 + +/* PTY bookkeeping structure, one per pty/tty pair. */ +typedef struct pty { + tty_t *tty; /* associated TTY structure */ + char state; /* flags: busy, closed, ... */ + + /* Read call on /dev/ptypX. */ + char rdrepcode; /* reply code, TASK_REPLY or REVIVE */ + char rdcaller; /* process making the call (usually FS) */ + char rdproc; /* process that wants to read from the pty */ + vir_bytes rdvir; /* virtual address in readers address space */ + int rdleft; /* # bytes yet to be read */ + int rdcum; /* # bytes written so far */ + + /* Write call to /dev/ptypX. */ + char wrrepcode; /* reply code, TASK_REPLY or REVIVE */ + char wrcaller; /* process making the call (usually FS) */ + char wrproc; /* process that wants to write to the pty */ + vir_bytes wrvir; /* virtual address in writers address space */ + int wrleft; /* # bytes yet to be written */ + int wrcum; /* # bytes written so far */ + + /* Output buffer. */ + int ocount; /* # characters in the buffer */ + char *ohead, *otail; /* head and tail of the circular buffer */ + char obuf[128]; /* buffer for bytes going to the pty reader */ +} pty_t; + +#define PTY_ACTIVE 0x01 /* pty is open/active */ +#define TTY_CLOSED 0x02 /* tty side has closed down */ +#define PTY_CLOSED 0x04 /* pty side has closed down */ + +PRIVATE pty_t pty_table[NR_PTYS]; /* PTY bookkeeping */ + + +FORWARD _PROTOTYPE( void pty_write, (tty_t *tp) ); +FORWARD _PROTOTYPE( void pty_echo, (tty_t *tp, int c) ); +FORWARD _PROTOTYPE( void pty_start, (pty_t *pp) ); +FORWARD _PROTOTYPE( void pty_finish, (pty_t *pp) ); +FORWARD _PROTOTYPE( void pty_read, (tty_t *tp) ); +FORWARD _PROTOTYPE( void pty_close, (tty_t *tp) ); +FORWARD _PROTOTYPE( void pty_icancel, (tty_t *tp) ); +FORWARD _PROTOTYPE( void pty_ocancel, (tty_t *tp) ); + + +/*==========================================================================* + * do_pty * + *==========================================================================*/ +PUBLIC void do_pty(tp, m_ptr) +tty_t *tp; +message *m_ptr; +{ +/* Perform an open/close/read/write call on a /dev/ptypX device. */ + pty_t *pp = tp->tty_priv; + int r; + + switch (m_ptr->m_type) { + case DEV_READ: + /* Check, store information on the reader, do I/O. */ + if (pp->state & TTY_CLOSED) { + r = 0; + break; + } + if (pp->rdleft != 0) { + r = EIO; + break; + } + if (m_ptr->COUNT <= 0) { + r = EINVAL; + break; + } + if (numap_local(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS, + m_ptr->COUNT) == 0) { + r = EFAULT; + break; + } + pp->rdrepcode = TASK_REPLY; + pp->rdcaller = m_ptr->m_source; + pp->rdproc = m_ptr->PROC_NR; + pp->rdvir = (vir_bytes) m_ptr->ADDRESS; + pp->rdleft = m_ptr->COUNT; + pty_start(pp); + handle_events(tp); + if (pp->rdleft == 0) return; /* already done */ + + if (m_ptr->TTY_FLAGS & O_NONBLOCK) { + r = EAGAIN; /* don't suspend */ + pp->rdleft = pp->rdcum = 0; + } else { + r = SUSPEND; /* do suspend */ + pp->rdrepcode = REVIVE; + } + break; + + case DEV_WRITE: + /* Check, store information on the writer, do I/O. */ + if (pp->state & TTY_CLOSED) { + r = EIO; + break; + } + if (pp->wrleft != 0) { + r = EIO; + break; + } + if (m_ptr->COUNT <= 0) { + r = EINVAL; + break; + } + if (numap_local(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS, + m_ptr->COUNT) == 0) { + r = EFAULT; + break; + } + pp->wrrepcode = TASK_REPLY; + pp->wrcaller = m_ptr->m_source; + pp->wrproc = m_ptr->PROC_NR; + pp->wrvir = (vir_bytes) m_ptr->ADDRESS; + pp->wrleft = m_ptr->COUNT; + handle_events(tp); + if (pp->wrleft == 0) return; /* already done */ + + if (m_ptr->TTY_FLAGS & O_NONBLOCK) { /* don't suspend */ + r = pp->wrcum > 0 ? pp->wrcum : EAGAIN; + pp->wrleft = pp->wrcum = 0; + } else { + pp->wrrepcode = REVIVE; /* do suspend */ + r = SUSPEND; + } + break; + + case DEV_OPEN: + r = pp->state != 0 ? EIO : OK; + pp->state |= PTY_ACTIVE; + break; + + case DEV_CLOSE: + r = OK; + if (pp->state & TTY_CLOSED) { + pp->state = 0; + } else { + pp->state |= PTY_CLOSED; + sigchar(tp, SIGHUP); + } + break; + + case CANCEL: + if (m_ptr->PROC_NR == pp->rdproc) { + /* Cancel a read from a PTY. */ + pp->rdleft = pp->rdcum = 0; + } + if (m_ptr->PROC_NR == pp->wrproc) { + /* Cancel a write to a PTY. */ + pp->wrleft = pp->wrcum = 0; + } + r = EINTR; + break; + + default: + r = EINVAL; + } + tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r); +} + + +/*==========================================================================* + * pty_write * + *==========================================================================*/ +PRIVATE void pty_write(tp) +tty_t *tp; +{ +/* (*dev_write)() routine for PTYs. Transfer bytes from the writer on + * /dev/ttypX to the output buffer. + */ + pty_t *pp = tp->tty_priv; + int count, ocount; + phys_bytes user_phys; + + /* PTY closed down? */ + if (pp->state & PTY_CLOSED) { + if (tp->tty_outleft > 0) { + tty_reply(tp->tty_outrepcode, tp->tty_outcaller, + tp->tty_outproc, EIO); + tp->tty_outleft = tp->tty_outcum = 0; + } + return; + } + + /* While there is something to do. */ + for (;;) { + ocount = buflen(pp->obuf) - pp->ocount; + count = bufend(pp->obuf) - pp->ohead; + if (count > ocount) count = ocount; + if (count > tp->tty_outleft) count = tp->tty_outleft; + if (count == 0 || tp->tty_inhibited) break; + + /* Copy from user space to the PTY output buffer. */ + user_phys = proc_vir2phys(proc_addr(tp->tty_outproc), tp->tty_out_vir); + phys_copy(user_phys, vir2phys(pp->ohead), (phys_bytes) count); + + /* Perform output processing on the output buffer. */ + out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount); + if (count == 0) break; + + /* Assume echoing messed up by output. */ + tp->tty_reprint = TRUE; + + /* Bookkeeping. */ + pp->ocount += ocount; + if ((pp->ohead += ocount) >= bufend(pp->obuf)) + pp->ohead -= buflen(pp->obuf); + pty_start(pp); + tp->tty_out_vir += count; + tp->tty_outcum += count; + if ((tp->tty_outleft -= count) == 0) { + /* Output is finished, reply to the writer. */ + tty_reply(tp->tty_outrepcode, tp->tty_outcaller, + tp->tty_outproc, tp->tty_outcum); + tp->tty_outcum = 0; + } + } + pty_finish(pp); +} + + +/*==========================================================================* + * pty_echo * + *==========================================================================*/ +PRIVATE void pty_echo(tp, c) +tty_t *tp; +int c; +{ +/* Echo one character. (Like pty_write, but only one character, optionally.) */ + + pty_t *pp = tp->tty_priv; + int count, ocount; + + ocount = buflen(pp->obuf) - pp->ocount; + if (ocount == 0) return; /* output buffer full */ + count = 1; + *pp->ohead = c; /* add one character */ + + out_process(tp, pp->obuf, pp->ohead, bufend(pp->obuf), &count, &ocount); + if (count == 0) return; + + pp->ocount += ocount; + if ((pp->ohead += ocount) >= bufend(pp->obuf)) pp->ohead -= buflen(pp->obuf); + pty_start(pp); +} + + +/*==========================================================================* + * pty_start * + *==========================================================================*/ +PRIVATE void pty_start(pp) +pty_t *pp; +{ +/* Transfer bytes written to the output buffer to the PTY reader. */ + int count; + phys_bytes user_phys; + + /* While there are things to do. */ + for (;;) { + count = bufend(pp->obuf) - pp->otail; + if (count > pp->ocount) count = pp->ocount; + if (count > pp->rdleft) count = pp->rdleft; + if (count == 0) break; + + /* Copy from the output buffer to the readers address space. */ + user_phys = proc_vir2phys(proc_addr(pp->rdproc), pp->rdvir); + phys_copy(vir2phys(pp->otail), user_phys, (phys_bytes) count); + + /* Bookkeeping. */ + pp->ocount -= count; + if ((pp->otail += count) == bufend(pp->obuf)) pp->otail = pp->obuf; + pp->rdvir += count; + pp->rdcum += count; + pp->rdleft -= count; + } +} + + +/*==========================================================================* + * pty_finish * + *==========================================================================*/ +PRIVATE void pty_finish(pp) +pty_t *pp; +{ +/* Finish the read request of a PTY reader if there is at least one byte + * transferred. + */ + + if (pp->rdcum > 0) { + tty_reply(pp->rdrepcode, pp->rdcaller, pp->rdproc, pp->rdcum); + pp->rdleft = pp->rdcum = 0; + } +} + + +/*==========================================================================* + * pty_read * + *==========================================================================*/ +PRIVATE void pty_read(tp) +tty_t *tp; +{ +/* Offer bytes from the PTY writer for input on the TTY. (Do it one byte at + * a time, 99% of the writes will be for one byte, so no sense in being smart.) + */ + pty_t *pp = tp->tty_priv; + phys_bytes user_phys; + char c; + + if (pp->state & PTY_CLOSED) { + if (tp->tty_inleft > 0) { + tty_reply(tp->tty_inrepcode, tp->tty_incaller, tp->tty_inproc, + tp->tty_incum); + tp->tty_inleft = tp->tty_incum = 0; + } + return; + } + + while (pp->wrleft > 0) { + /* Transfer one character to 'c'. */ + user_phys = proc_vir2phys(proc_addr(pp->wrproc), pp->wrvir); + phys_copy(user_phys, vir2phys(&c), 1L); + + /* Input processing. */ + if (in_process(tp, &c, 1) == 0) break; + + /* PTY writer bookkeeping. */ + pp->wrvir++; + pp->wrcum++; + if (--pp->wrleft == 0) { + tty_reply(pp->wrrepcode, pp->wrcaller, pp->wrproc, pp->wrcum); + pp->wrcum = 0; + } + } +} + + +/*==========================================================================* + * pty_close * + *==========================================================================*/ +PRIVATE void pty_close(tp) +tty_t *tp; +{ +/* The tty side has closed, so shut down the pty side. */ + pty_t *pp = tp->tty_priv; + + if (!(pp->state & PTY_ACTIVE)) return; + + if (pp->rdleft > 0) { + tty_reply(pp->rdrepcode, pp->rdcaller, pp->rdproc, 0); + pp->rdleft = pp->rdcum = 0; + } + + if (pp->wrleft > 0) { + tty_reply(pp->wrrepcode, pp->wrcaller, pp->wrproc, EIO); + pp->wrleft = pp->wrcum = 0; + } + + if (pp->state & PTY_CLOSED) pp->state = 0; else pp->state |= TTY_CLOSED; +} + + +/*==========================================================================* + * pty_icancel * + *==========================================================================*/ +PRIVATE void pty_icancel(tp) +tty_t *tp; +{ +/* Discard waiting input. */ + pty_t *pp = tp->tty_priv; + + if (pp->wrleft > 0) { + tty_reply(pp->wrrepcode, pp->wrcaller, pp->wrproc, + pp->wrcum + pp->wrleft); + pp->wrleft = pp->wrcum = 0; + } +} + + +/*==========================================================================* + * pty_ocancel * + *==========================================================================*/ +PRIVATE void pty_ocancel(tp) +tty_t *tp; +{ +/* Drain the output buffer. */ + pty_t *pp = tp->tty_priv; + + pp->ocount = 0; + pp->otail = pp->ohead; +} + + +/*==========================================================================* + * pty_init * + *==========================================================================*/ +PUBLIC void pty_init(tp) +tty_t *tp; +{ + pty_t *pp; + int line; + + /* Associate PTY and TTY structures. */ + line = tp - &tty_table[NR_CONS + NR_RS_LINES]; + pp = tp->tty_priv = &pty_table[line]; + pp->tty = tp; + + /* Set up output queue. */ + pp->ohead = pp->otail = pp->obuf; + + /* Fill in TTY function hooks. */ + tp->tty_devread = pty_read; + tp->tty_devwrite = pty_write; + tp->tty_echo = pty_echo; + tp->tty_icancel = pty_icancel; + tp->tty_ocancel = pty_ocancel; + tp->tty_close = pty_close; +} +#endif /* NR_PTYS > 0 */ diff --git a/drivers/tty/rs232.c b/drivers/tty/rs232.c new file mode 100644 index 000000000..b9d558a19 --- /dev/null +++ b/drivers/tty/rs232.c @@ -0,0 +1,930 @@ +#define USER_SPACE 1 + +#include <minix/config.h> +/*==========================================================================* + * rs232.c - serial driver for 8250 and 16450 UARTs * + * Added support for Atari ST M68901 and YM-2149 --kub * + *==========================================================================*/ + +#include "../drivers.h" +#include <termios.h> +#include <signal.h> +#include "tty.h" + +#if USER_SPACE +typedef u16_t port_t; +#endif + +#if NR_RS_LINES > 0 + +#if (MACHINE != IBM_PC) && (MACHINE != ATARI) +#error /* rs232.c only supports PC and Atari ST */ +#endif + +#if (MACHINE == ATARI) +#include "staddr.h" +#include "stsound.h" +#include "stmfp.h" +#if (NR_RS_LINES > 1) +#error /* Only one physical RS232 line available */ +#endif +#endif + +#if (MACHINE == IBM_PC) /* PC/AT 8250/16450 chip combination */ + +/* 8250 constants. */ +#define UART_FREQ 115200L /* timer frequency */ + +/* Interrupt enable bits. */ +#define IE_RECEIVER_READY 1 +#define IE_TRANSMITTER_READY 2 +#define IE_LINE_STATUS_CHANGE 4 +#define IE_MODEM_STATUS_CHANGE 8 + +/* Interrupt status bits. */ +#define IS_MODEM_STATUS_CHANGE 0 +#define IS_TRANSMITTER_READY 2 +#define IS_RECEIVER_READY 4 +#define IS_LINE_STATUS_CHANGE 6 + +/* Line control bits. */ +#define LC_2STOP_BITS 0x04 +#define LC_PARITY 0x08 +#define LC_PAREVEN 0x10 +#define LC_BREAK 0x40 +#define LC_ADDRESS_DIVISOR 0x80 + +/* Line status bits. */ +#define LS_OVERRUN_ERR 2 +#define LS_PARITY_ERR 4 +#define LS_FRAMING_ERR 8 +#define LS_BREAK_INTERRUPT 0x10 +#define LS_TRANSMITTER_READY 0x20 + +/* Modem control bits. */ +#define MC_DTR 1 +#define MC_RTS 2 +#define MC_OUT2 8 /* required for PC & AT interrupts */ + +/* Modem status bits. */ +#define MS_CTS 0x10 +#define MS_RLSD 0x80 /* Received Line Signal Detect */ +#define MS_DRLSD 0x08 /* RLSD Delta */ + +#else /* MACHINE == ATARI */ /* Atari ST 68901 USART */ + +/* Most of the USART constants are already defined in stmfp.h . The local + * definitions made here are for keeping C code changes smaller. --kub + */ + +#define UART_FREQ 19200L /* timer frequency */ + +/* Line status bits. */ +#define LS_OVERRUN_ERR R_OE +#define LS_PARITY_ERR R_PE +#define LS_FRAMING_ERR R_FE +#define LS_BREAK_INTERRUPT R_BREAK + +/* Modem status bits. */ +#define MS_CTS IO_SCTS /* 0x04 */ + +#endif /* MACHINE == ATARI */ + +#define DATA_BITS_SHIFT 8 /* amount data bits shifted in mode */ +#define DEF_BAUD 1200 /* default baud rate */ + +#define RS_IBUFSIZE 1024 /* RS232 input buffer size */ +#define RS_OBUFSIZE 1024 /* RS232 output buffer size */ + +/* Input buffer watermarks. + * The external device is asked to stop sending when the buffer + * exactly reaches high water, or when TTY requests it. Sending restarts + * when the input buffer empties below the low watermark. + */ +#define RS_ILOWWATER (1 * RS_IBUFSIZE / 4) +#define RS_IHIGHWATER (3 * RS_IBUFSIZE / 4) + +/* Output buffer low watermark. + * TTY is notified when the output buffer empties below the low watermark, so + * it may continue filling the buffer if doing a large write. + */ +#define RS_OLOWWATER (1 * RS_OBUFSIZE / 4) + +#if (MACHINE == IBM_PC) + +/* Macros to handle flow control. + * Interrupts must be off when they are used. + * Time is critical - already the function call for outb() is annoying. + * If outb() can be done in-line, tests to avoid it can be dropped. + * istart() tells external device we are ready by raising RTS. + * istop() tells external device we are not ready by dropping RTS. + * DTR is kept high all the time (it probably should be raised by open and + * dropped by close of the device). + * OUT2 is also kept high all the time. + */ +#define istart(rs) \ + (outb((rs)->modem_ctl_port, MC_OUT2 | MC_RTS | MC_DTR), \ + (rs)->idevready = TRUE) +#define istop(rs) \ + (outb((rs)->modem_ctl_port, MC_OUT2 | MC_DTR), \ + (rs)->idevready = FALSE) + +/* Macro to tell if device is ready. The rs->cts field is set to MS_CTS if + * CLOCAL is in effect for a line without a CTS wire. + */ +#define devready(rs) ((inb(rs->modem_status_port) | rs->cts) & MS_CTS) + +/* Macro to tell if transmitter is ready. */ +#define txready(rs) (inb(rs->line_status_port) & LS_TRANSMITTER_READY) + +/* Macro to tell if carrier has dropped. + * The RS232 Carrier Detect (CD) line is usually connected to the 8250 + * Received Line Signal Detect pin, reflected by bit MS_RLSD in the Modem + * Status Register. The MS_DRLSD bit tells if MS_RLSD has just changed state. + * So if MS_DRLSD is set and MS_RLSD cleared, we know that carrier has just + * dropped. + */ +#define devhup(rs) \ + ((inb(rs->modem_status_port) & (MS_RLSD|MS_DRLSD)) == MS_DRLSD) + +#else /* MACHINE == ATARI */ + +/* Macros to handle flow control. + * Time is critical - already the function call for lock()/restore() is + * annoying. + * istart() tells external device we are ready by raising RTS. + * istop() tells external device we are not ready by dropping RTS. + * DTR is kept high all the time (it probably should be raised by open and + * dropped by close of the device). NOTE: The modem lines are active low. + */ +#define set_porta(msk,val) { register int s = lock(); \ + SOUND->sd_selr = YM_IOA; \ + SOUND->sd_wdat = \ + SOUND->sd_rdat & (msk) | (val); \ + restore(s); } +#define istart(rs) { set_porta( ~(PA_SRTS|PA_SDTR),0 ); \ + (rs)->idevready = TRUE; } +#define istop(rs) { set_porta( ~PA_SDTR, PA_SRTS ); \ + (rs)->idevready = FALSE; } + +/* Macro to tell if device is ready. The rs->cts field is set to MS_CTS if + * CLOCAL is in effect for a line without a CTS wire. + */ +#define devready(rs) ((~MFP->mf_gpip | rs->cts) & MS_CTS) + +/* Transmitter ready test */ +#define txready(rs) (MFP->mf_tsr & (T_EMPTY | T_UE)) + +#endif /* MACHINE == ATARI */ + +/* Types. */ +typedef unsigned char bool_t; /* boolean */ + +/* RS232 device structure, one per device. */ +typedef struct rs232 { + tty_t *tty; /* associated TTY structure */ + + int icount; /* number of bytes in the input buffer */ + char *ihead; /* next free spot in input buffer */ + char *itail; /* first byte to give to TTY */ + bool_t idevready; /* nonzero if we are ready to receive (RTS) */ + char cts; /* normally 0, but MS_CTS if CLOCAL is set */ + + unsigned char ostate; /* combination of flags: */ +#define ODONE 1 /* output completed (< output enable bits) */ +#define ORAW 2 /* raw mode for xoff disable (< enab. bits) */ +#define OWAKEUP 4 /* tty_wakeup() pending (asm code only) */ +#define ODEVREADY MS_CTS /* external device hardware ready (CTS) */ +#define OQUEUED 0x20 /* output buffer not empty */ +#define OSWREADY 0x40 /* external device software ready (no xoff) */ +#define ODEVHUP MS_RLSD /* external device has dropped carrier */ +#define OSOFTBITS (ODONE | ORAW | OWAKEUP | OQUEUED | OSWREADY) + /* user-defined bits */ +#if (OSOFTBITS | ODEVREADY | ODEVHUP) == OSOFTBITS + /* a weak sanity check */ +#error /* bits are not unique */ +#endif + unsigned char oxoff; /* char to stop output */ + bool_t inhibited; /* output inhibited? (follows tty_inhibited) */ + bool_t drain; /* if set drain output and reconfigure line */ + int ocount; /* number of bytes in the output buffer */ + char *ohead; /* next free spot in output buffer */ + char *otail; /* next char to output */ + +#if (MACHINE == IBM_PC) + port_t xmit_port; /* i/o ports */ + port_t recv_port; + port_t div_low_port; + port_t div_hi_port; + port_t int_enab_port; + port_t int_id_port; + port_t line_ctl_port; + port_t modem_ctl_port; + port_t line_status_port; + port_t modem_status_port; +#endif + + unsigned char lstatus; /* last line status */ + unsigned char pad; /* ensure alignment for 16-bit ints */ + unsigned framing_errors; /* error counts (no reporting yet) */ + unsigned overrun_errors; + unsigned parity_errors; + unsigned break_interrupts; + + irq_hook_t hook; /* interrupt hook */ + + char ibuf[RS_IBUFSIZE]; /* input buffer */ + char obuf[RS_OBUFSIZE]; /* output buffer */ +} rs232_t; + +PUBLIC rs232_t rs_lines[NR_RS_LINES]; + +/* Table and macro to translate an RS232 line number to its rs_lines entry. */ +PRIVATE rs232_t *p_rs_addr[NR_RS_LINES]; + +#define rs_addr(line) (p_rs_addr[line]) + +#if (MACHINE == IBM_PC) +/* 8250 base addresses. */ +PRIVATE port_t addr_8250[] = { + 0x3F8, /* COM1 */ + 0x2F8, /* COM2 */ + 0x3E8, /* COM3 */ + 0x2E8, /* COM4 */ +}; +#endif + +FORWARD _PROTOTYPE( int rs232_handler, (irq_hook_t *hook) ); +FORWARD _PROTOTYPE( void in_int, (rs232_t *rs) ); +FORWARD _PROTOTYPE( void line_int, (rs232_t *rs) ); +FORWARD _PROTOTYPE( void modem_int, (rs232_t *rs) ); +FORWARD _PROTOTYPE( void rs_write, (tty_t *tp) ); +FORWARD _PROTOTYPE( void rs_echo, (tty_t *tp, int c) ); +FORWARD _PROTOTYPE( void rs_ioctl, (tty_t *tp) ); +FORWARD _PROTOTYPE( void rs_config, (rs232_t *rs) ); +FORWARD _PROTOTYPE( void rs_read, (tty_t *tp) ); +FORWARD _PROTOTYPE( void rs_icancel, (tty_t *tp) ); +FORWARD _PROTOTYPE( void rs_ocancel, (tty_t *tp) ); +FORWARD _PROTOTYPE( void rs_ostart, (rs232_t *rs) ); +FORWARD _PROTOTYPE( void rs_break, (tty_t *tp) ); +FORWARD _PROTOTYPE( void rs_close, (tty_t *tp) ); +FORWARD _PROTOTYPE( void out_int, (rs232_t *rs) ); + + +/*==========================================================================* + * rs_write * + *==========================================================================*/ +PRIVATE void rs_write(tp) +register tty_t *tp; +{ +/* (*devwrite)() routine for RS232. */ + + rs232_t *rs = tp->tty_priv; + int count, ocount; + phys_bytes user_phys; + + if (rs->inhibited != tp->tty_inhibited) { + /* Inhibition state has changed. */ + lock(); + rs->ostate |= OSWREADY; + if (tp->tty_inhibited) rs->ostate &= ~OSWREADY; + unlock(); + rs->inhibited = tp->tty_inhibited; + } + + if (rs->drain) { + /* Wait for the line to drain then reconfigure and continue output. */ + if (rs->ocount > 0) return; + rs->drain = FALSE; + rs_config(rs); + } + + /* While there is something to do. */ + for (;;) { + ocount = buflen(rs->obuf) - rs->ocount; + count = bufend(rs->obuf) - rs->ohead; + if (count > ocount) count = ocount; + if (count > tp->tty_outleft) count = tp->tty_outleft; + if (count == 0 || tp->tty_inhibited) break; + + /* Copy from user space to the RS232 output buffer. */ + user_phys = proc_vir2phys(proc_addr(tp->tty_outproc), tp->tty_out_vir); + phys_copy(user_phys, vir2phys(rs->ohead), (phys_bytes) count); + + /* Perform output processing on the output buffer. */ + out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount); + if (count == 0) break; + + /* Assume echoing messed up by output. */ + tp->tty_reprint = TRUE; + + /* Bookkeeping. */ + lock(); /* protect interrupt sensitive rs->ocount */ + rs->ocount += ocount; + rs_ostart(rs); + unlock(); + if ((rs->ohead += ocount) >= bufend(rs->obuf)) + rs->ohead -= buflen(rs->obuf); + tp->tty_out_vir += count; + tp->tty_outcum += count; + if ((tp->tty_outleft -= count) == 0) { + /* Output is finished, reply to the writer. */ + tty_reply(tp->tty_outrepcode, tp->tty_outcaller, + tp->tty_outproc, tp->tty_outcum); + tp->tty_outcum = 0; + } + } + if (tp->tty_outleft > 0 && tp->tty_termios.c_ospeed == B0) { + /* Oops, the line has hung up. */ + tty_reply(tp->tty_outrepcode, tp->tty_outcaller, tp->tty_outproc, EIO); + tp->tty_outleft = tp->tty_outcum = 0; + } +} + + +/*==========================================================================* + * rs_echo * + *==========================================================================*/ +PRIVATE void rs_echo(tp, c) +tty_t *tp; /* which TTY */ +int c; /* character to echo */ +{ +/* Echo one character. (Like rs_write, but only one character, optionally.) */ + + rs232_t *rs = tp->tty_priv; + int count, ocount; + + ocount = buflen(rs->obuf) - rs->ocount; + if (ocount == 0) return; /* output buffer full */ + count = 1; + *rs->ohead = c; /* add one character */ + + out_process(tp, rs->obuf, rs->ohead, bufend(rs->obuf), &count, &ocount); + if (count == 0) return; + + lock(); + rs->ocount += ocount; + rs_ostart(rs); + unlock(); + if ((rs->ohead += ocount) >= bufend(rs->obuf)) rs->ohead -= buflen(rs->obuf); +} + + +/*==========================================================================* + * rs_ioctl * + *==========================================================================*/ +PRIVATE void rs_ioctl(tp) +tty_t *tp; /* which TTY */ +{ +/* Reconfigure the line as soon as the output has drained. */ + rs232_t *rs = tp->tty_priv; + + rs->drain = TRUE; +} + + +/*==========================================================================* + * rs_config * + *==========================================================================*/ +PRIVATE void rs_config(rs) +rs232_t *rs; /* which line */ +{ +/* Set various line control parameters for RS232 I/O. + * If DataBits == 5 and StopBits == 2, 8250 will generate 1.5 stop bits. + * The 8250 can't handle split speed, so we use the input speed. + */ + + tty_t *tp = rs->tty; + int divisor; + int line_controls; + static struct speed2divisor { + speed_t speed; + int divisor; + } s2d[] = { +#if (MACHINE == IBM_PC) + { B50, UART_FREQ / 50 }, +#endif + { B75, UART_FREQ / 75 }, + { B110, UART_FREQ / 110 }, + { B134, UART_FREQ * 10 / 1345 }, + { B150, UART_FREQ / 150 }, + { B200, UART_FREQ / 200 }, + { B300, UART_FREQ / 300 }, + { B600, UART_FREQ / 600 }, + { B1200, UART_FREQ / 1200 }, +#if (MACHINE == IBM_PC) + { B1800, UART_FREQ / 1800 }, +#endif + { B2400, UART_FREQ / 2400 }, + { B4800, UART_FREQ / 4800 }, + { B9600, UART_FREQ / 9600 }, + { B19200, UART_FREQ / 19200 }, +#if (MACHINE == IBM_PC) + { B38400, UART_FREQ / 38400 }, + { B57600, UART_FREQ / 57600 }, + { B115200, UART_FREQ / 115200L }, +#endif + }; + struct speed2divisor *s2dp; + + /* RS232 needs to know the xoff character, and if CTS works. */ + rs->oxoff = tp->tty_termios.c_cc[VSTOP]; + rs->cts = (tp->tty_termios.c_cflag & CLOCAL) ? MS_CTS : 0; + + /* Look up the 8250 rate divisor from the output speed. */ + divisor = 0; + for (s2dp = s2d; s2dp < s2d + sizeof(s2d)/sizeof(s2d[0]); s2dp++) { + if (s2dp->speed == tp->tty_termios.c_ospeed) divisor = s2dp->divisor; + } + if (divisor == 0) return; /* B0? */ + +#if (MACHINE == IBM_PC) + /* Compute line control flag bits. */ + line_controls = 0; + if (tp->tty_termios.c_cflag & PARENB) { + line_controls |= LC_PARITY; + if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= LC_PAREVEN; + } + if (divisor >= (UART_FREQ / 110)) line_controls |= LC_2STOP_BITS; + line_controls |= (tp->tty_termios.c_cflag & CSIZE) >> 2; + + /* Lock out interrupts while setting the speed. The receiver register is + * going to be hidden by the div_low register, but the input interrupt + * handler relies on reading it to clear the interrupt and avoid looping + * forever. + */ + lock(); + + /* Select the baud rate divisor registers and change the rate. */ + outb(rs->line_ctl_port, LC_ADDRESS_DIVISOR); + outb(rs->div_low_port, divisor); + outb(rs->div_hi_port, divisor >> 8); + + /* Change the line controls and reselect the usual registers. */ + outb(rs->line_ctl_port, line_controls); + + rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */ + if ((tp->tty_termios.c_lflag & IXON) && rs->oxoff != _POSIX_VDISABLE) + rs->ostate &= ~ORAW; + + unlock(); + +#else /* MACHINE == ATARI */ + + line_controls = U_Q16; + if (tp->tty_termios.c_cflag & PARENB) { + line_controls |= U_PAR; + if (!(tp->tty_termios.c_cflag & PARODD)) line_controls |= U_EVEN; + } + line_controls |= (divisor >= (UART_FREQ / 110)) ? U_ST2 : U_ST1; + + switch (tp->tty_termios.c_cflag & CSIZE) { /* XXX - are U_Dn like CSn? */ + case CS5: line_controls |= U_D5; break; + case CS5: line_controls |= U_D6; break; + case CS5: line_controls |= U_D7; break; + case CS5: line_controls |= U_D8; break; + } + lock(); + MFP->mf_ucr = line_controls; + MFP->mf_tddr = divisor; + unlock(); +#endif /* MACHINE == ATARI */ +} + + +/*==========================================================================* + * rs_init * + *==========================================================================*/ +PUBLIC void rs_init(tp) +tty_t *tp; /* which TTY */ +{ +/* Initialize RS232 for one line. */ + + register rs232_t *rs; + int line; +#if (MACHINE == IBM_PC) + port_t this_8250; + int irq; + long v; +#endif + + /* Associate RS232 and TTY structures. */ + line = tp - &tty_table[NR_CONS]; + rs = tp->tty_priv = &rs_lines[line]; + rs->tty = tp; + + /* Set up input queue. */ + rs->ihead = rs->itail = rs->ibuf; + +#if (MACHINE == IBM_PC) + /* Precalculate port numbers for speed. Magic numbers in the code (once). */ + this_8250 = addr_8250[line]; + rs->xmit_port = this_8250 + 0; + rs->recv_port = this_8250 + 0; + rs->div_low_port = this_8250 + 0; + rs->div_hi_port = this_8250 + 1; + rs->int_enab_port = this_8250 + 1; + rs->int_id_port = this_8250 + 2; + rs->line_ctl_port = this_8250 + 3; + rs->modem_ctl_port = this_8250 + 4; + rs->line_status_port = this_8250 + 5; + rs->modem_status_port = this_8250 + 6; +#endif + + /* Set up the hardware to a base state, in particular + * o turn off DTR (MC_DTR) to try to stop the external device. + * o be careful about the divisor latch. Some BIOS's leave it enabled + * here and that caused trouble (no interrupts) in version 1.5 by + * hiding the interrupt enable port in the next step, and worse trouble + * (continual interrupts) in an old version by hiding the receiver + * port in the first interrupt. Call rs_ioctl() early to avoid this. + * o disable interrupts at the chip level, to force an edge transition + * on the 8259 line when interrupts are next enabled and active. + * RS232 interrupts are guaranteed to be disabled now by the 8259 + * mask, but there used to be trouble if the mask was set without + * handling a previous interrupt. + */ + istop(rs); /* sets modem_ctl_port */ + rs_config(rs); +#if (MACHINE == IBM_PC) + outb(rs->int_enab_port, 0); +#endif + + /* Clear any harmful leftover interrupts. An output interrupt is harmless + * and will occur when interrupts are enabled anyway. Set up the output + * queue using the status from clearing the modem status interrupt. + */ +#if (MACHINE == IBM_PC) + inb(rs->line_status_port); + inb(rs->recv_port); +#endif + rs->ostate = devready(rs) | ORAW | OSWREADY; /* reads modem_ctl_port */ + rs->ohead = rs->otail = rs->obuf; + +#if (MACHINE == IBM_PC) + /* Enable interrupts for both interrupt controller and device. */ + irq = (line & 1) == 0 ? RS232_IRQ : SECONDARY_IRQ; + + put_irq_handler(&rs->hook, irq, rs232_handler); + enable_irq(&rs->hook); + outb(rs->int_enab_port, IE_LINE_STATUS_CHANGE | IE_MODEM_STATUS_CHANGE + | IE_RECEIVER_READY | IE_TRANSMITTER_READY); +#else /* MACHINE == ATARI */ + /* Initialize the 68901 chip, then enable interrupts. */ + MFP->mf_scr = 0x00; + MFP->mf_tcdcr |= T_Q004; + MFP->mf_rsr = R_ENA; + MFP->mf_tsr = T_ENA; + MFP->mf_aer = (MFP->mf_aer | (IO_SCTS|IO_SDCD)) ^ + (MFP->mf_gpip & (IO_SCTS|IO_SDCD)); + MFP->mf_ddr = (MFP->mf_ddr & ~ (IO_SCTS|IO_SDCD)); + MFP->mf_iera |= (IA_RRDY|IA_RERR|IA_TRDY|IA_TERR); + MFP->mf_imra |= (IA_RRDY|IA_RERR|IA_TRDY|IA_TERR); + MFP->mf_ierb |= (IB_SCTS|IB_SDCD); + MFP->mf_imrb |= (IB_SCTS|IB_SDCD); +#endif /* MACHINE == ATARI */ + + /* Fill in TTY function hooks. */ + tp->tty_devread = rs_read; + tp->tty_devwrite = rs_write; + tp->tty_echo = rs_echo; + tp->tty_icancel = rs_icancel; + tp->tty_ocancel = rs_ocancel; + tp->tty_ioctl = rs_ioctl; + tp->tty_break = rs_break; + tp->tty_close = rs_close; + + /* Tell external device we are ready. */ + istart(rs); +} + + +/*==========================================================================* + * rs_icancel * + *==========================================================================*/ +PRIVATE void rs_icancel(tp) +tty_t *tp; /* which TTY */ +{ +/* Cancel waiting input. */ + rs232_t *rs = tp->tty_priv; + + lock(); + rs->icount = 0; + rs->itail = rs->ihead; + istart(rs); + unlock(); +} + + +/*==========================================================================* + * rs_ocancel * + *==========================================================================*/ +PRIVATE void rs_ocancel(tp) +tty_t *tp; /* which TTY */ +{ +/* Cancel pending output. */ + rs232_t *rs = tp->tty_priv; + + lock(); + rs->ostate &= ~(ODONE | OQUEUED); + rs->ocount = 0; + rs->otail = rs->ohead; + unlock(); +} + + +/*==========================================================================* + * rs_read * + *==========================================================================*/ +PRIVATE void rs_read(tp) +tty_t *tp; /* which tty */ +{ +/* Process characters from the circular input buffer. */ + + rs232_t *rs = tp->tty_priv; + int icount, count, ostate; + + if (!(tp->tty_termios.c_cflag & CLOCAL)) { + /* Send a SIGHUP if hangup detected. */ + lock(); + ostate = rs->ostate; + rs->ostate &= ~ODEVHUP; /* save ostate, clear DEVHUP */ + unlock(); + if (ostate & ODEVHUP) { + sigchar(tp, SIGHUP); + tp->tty_termios.c_ospeed = B0; /* Disable further I/O. */ + return; + } + } + + while ((count = rs->icount) > 0) { + icount = bufend(rs->ibuf) - rs->itail; + if (count > icount) count = icount; + + /* Perform input processing on (part of) the input buffer. */ + if ((count = in_process(tp, rs->itail, count)) == 0) break; + + lock(); /* protect interrupt sensitive variables */ + rs->icount -= count; + if (!rs->idevready && rs->icount < RS_ILOWWATER) istart(rs); + unlock(); + if ((rs->itail += count) == bufend(rs->ibuf)) rs->itail = rs->ibuf; + } +} + + +/*==========================================================================* + * rs_ostart * + *==========================================================================*/ +PRIVATE void rs_ostart(rs) +rs232_t *rs; /* which rs line */ +{ +/* Tell RS232 there is something waiting in the output buffer. */ + + rs->ostate |= OQUEUED; + if (txready(rs)) out_int(rs); +} + + +/*==========================================================================* + * rs_break * + *==========================================================================*/ +PRIVATE void rs_break(tp) +tty_t *tp; /* which tty */ +{ +/* Generate a break condition by setting the BREAK bit for 0.4 sec. */ + rs232_t *rs = tp->tty_priv; + int line_controls; + + line_controls = inb(rs->line_ctl_port); + outb(rs->line_ctl_port, line_controls | LC_BREAK); + milli_delay(400); /* ouch */ + outb(rs->line_ctl_port, line_controls); +} + + +/*==========================================================================* + * rs_close * + *==========================================================================*/ +PRIVATE void rs_close(tp) +tty_t *tp; /* which tty */ +{ +/* The line is closed; optionally hang up. */ + rs232_t *rs = tp->tty_priv; + + if (tp->tty_termios.c_cflag & HUPCL) { + outb(rs->modem_ctl_port, MC_OUT2 | MC_RTS); + } +} + + +/* Low level (interrupt) routines. */ + +#if (MACHINE == IBM_PC) +/*==========================================================================* + * rs232_handler * + *==========================================================================*/ +PRIVATE int rs232_handler(hook) +irq_hook_t *hook; +{ +/* Interrupt hander for RS232. */ + + register rs232_t *rs = structof(rs232_t, hook, hook); + + while (TRUE) { + /* Loop to pick up ALL pending interrupts for device. + * This usually just wastes time unless the hardware has a buffer + * (and then we have to worry about being stuck in the loop too long). + * Unfortunately, some serial cards lock up without this. + */ + switch (inb(rs->int_id_port)) { + case IS_RECEIVER_READY: + in_int(rs); + continue; + case IS_TRANSMITTER_READY: + out_int(rs); + continue; + case IS_MODEM_STATUS_CHANGE: + modem_int(rs); + continue; + case IS_LINE_STATUS_CHANGE: + line_int(rs); + continue; + } + return(1); /* reenable serial interrupt */ + } +} +#endif /* MACHINE == IBM_PC */ + +#if (MACHINE == ATARI) +/*==========================================================================* + * siaint * + *==========================================================================*/ +PRIVATE void siaint(type) +int type; /* interrupt type */ +{ +/* siaint is the rs232 interrupt procedure for Atari ST's. For ST there are + * as much as 5 interrupt lines used for rs232. The trap type byte left on the + * stack by the assembler interrupt handler identifies the interrupt cause. + */ + + register unsigned char code; + register rs232_t *rs = &rs_lines[0]; + int s = lock(); + + switch (type & 0x00FF) + { + case 0x00: /* receive buffer full */ + in_int(rs); + break; + case 0x01: /* receive error */ + line_int(rs); + break; + case 0x02: /* transmit buffer empty */ + out_int(rs); + break; + case 0x03: /* transmit error */ + code = MFP->mf_tsr; + if (code & ~(T_ENA | T_UE | T_EMPTY)) + { + printf("sia: transmit error: status=%x\r\n", code); + /* MFP->mf_udr = lastchar; */ /* retry */ + } + break; + case 0x04: /* modem lines change */ + modem_int(rs); + break; + } + restore(s); +} +#endif /* MACHINE == ATARI */ + + +/*==========================================================================* + * in_int * + *==========================================================================*/ +PRIVATE void in_int(rs) +register rs232_t *rs; /* line with input interrupt */ +{ +/* Read the data which just arrived. + * If it is the oxoff char, clear OSWREADY, else if OSWREADY was clear, set + * it and restart output (any char does this, not just xon). + * Put data in the buffer if room, otherwise discard it. + * Set a flag for the clock interrupt handler to eventually notify TTY. + */ + + int c; + +#if (MACHINE == IBM_PC) + c = inb(rs->recv_port); +#else /* MACHINE == ATARI */ + c = MFP->mf_udr; +#endif + + if (!(rs->ostate & ORAW)) { + if (c == rs->oxoff) { + rs->ostate &= ~OSWREADY; + } else + if (!(rs->ostate & OSWREADY)) { + rs->ostate |= OSWREADY; + if (txready(rs)) out_int(rs); + } + } + + if (rs->icount == buflen(rs->ibuf)) return; /* input buffer full, discard */ + + if (++rs->icount == RS_IHIGHWATER && rs->idevready) istop(rs); + *rs->ihead = c; + if (++rs->ihead == bufend(rs->ibuf)) rs->ihead = rs->ibuf; + if (rs->icount == 1) { + rs->tty->tty_events = 1; + force_timeout(); + } +} + + +/*==========================================================================* + * line_int * + *==========================================================================*/ +PRIVATE void line_int(rs) +register rs232_t *rs; /* line with line status interrupt */ +{ +/* Check for and record errors. */ + +#if (MACHINE == IBM_PC) + rs->lstatus = inb(rs->line_status_port); +#else /* MACHINE == ATARI */ + rs->lstatus = MFP->mf_rsr; + MFP->mf_rsr &= R_ENA; + rs->pad = MFP->mf_udr; /* discard char in case of LS_OVERRUN_ERR */ +#endif /* MACHINE == ATARI */ + if (rs->lstatus & LS_FRAMING_ERR) ++rs->framing_errors; + if (rs->lstatus & LS_OVERRUN_ERR) ++rs->overrun_errors; + if (rs->lstatus & LS_PARITY_ERR) ++rs->parity_errors; + if (rs->lstatus & LS_BREAK_INTERRUPT) ++rs->break_interrupts; +} + + +/*==========================================================================* + * modem_int * + *==========================================================================*/ +PRIVATE void modem_int(rs) +register rs232_t *rs; /* line with modem interrupt */ +{ +/* Get possibly new device-ready status, and clear ODEVREADY if necessary. + * If the device just became ready, restart output. + */ + +#if (MACHINE == ATARI) + /* Set active edge interrupt so that next change causes a new interrupt */ + MFP->mf_aer = (MFP->mf_aer | (IO_SCTS|IO_SDCD)) ^ + (MFP->mf_gpip & (IO_SCTS|IO_SDCD)); +#endif + + if (devhup(rs)) { + rs->ostate |= ODEVHUP; + rs->tty->tty_events = 1; + force_timeout(); + } + + if (!devready(rs)) + rs->ostate &= ~ODEVREADY; + else if (!(rs->ostate & ODEVREADY)) { + rs->ostate |= ODEVREADY; + if (txready(rs)) out_int(rs); + } +} + + +/*==========================================================================* + * out_int * + *==========================================================================*/ +PRIVATE void out_int(rs) +register rs232_t *rs; /* line with output interrupt */ +{ +/* If there is output to do and everything is ready, do it (local device is + * known ready). + * Notify TTY when the buffer goes empty. + */ + + if (rs->ostate >= (ODEVREADY | OQUEUED | OSWREADY)) { + /* Bit test allows ORAW and requires the others. */ +#if (MACHINE == IBM_PC) + outb(rs->xmit_port, *rs->otail); +#else /* MACHINE == ATARI */ + MFP->mf_udr = *rs->otail; +#endif + if (++rs->otail == bufend(rs->obuf)) rs->otail = rs->obuf; + if (--rs->ocount == 0) { + rs->ostate ^= (ODONE | OQUEUED); /* ODONE on, OQUEUED off */ + rs->tty->tty_events = 1; + force_timeout(); + } else + if (rs->ocount == RS_OLOWWATER) { /* running low? */ + rs->tty->tty_events = 1; + force_timeout(); + } + } +} +#endif /* NR_RS_LINES > 0 */ + diff --git a/drivers/tty/tty.c b/drivers/tty/tty.c new file mode 100644 index 000000000..e10b638be --- /dev/null +++ b/drivers/tty/tty.c @@ -0,0 +1,1812 @@ +/* This file contains the tesminal driver, both for the IBM console and regular + * ASCII terminals. It handles only the device-independent part of a TTY, the + * device dependent parts are in console.c, rs232.c, etc. This file contains + * two main entry points, tty_task() and tty_wakeup(), and several minor entry + * points for use by the device-dependent code. + * + * The device-independent part accepts "keyboard" input from the device- + * dependent part, performs input processing (special key interpretation), + * and sends the input to a process reading from the TTY. Output to a TTY + * is sent to the device-dependent code for output processing and "screen" + * display. Input processing is done by the device by calling 'in_process' + * on the input characters, output processing may be done by the device itself + * or by calling 'out_process'. The TTY takes care of input queuing, the + * device does the output queuing. If a device receives an external signal, + * like an interrupt, then it causes tty_wakeup() to be run by the CLOCK task + * to, you guessed it, wake up the TTY to check if input or output can + * continue. + * + * The valid messages and their parameters are: + * + * HARD_INT: output has been completed or input has arrived + * HARD_STOP: MINIX wants to shutdown; run code to cleanly stop + * DEV_READ: a process wants to read from a terminal + * DEV_WRITE: a process wants to write on a terminal + * DEV_IOCTL: a process wants to change a terminal's parameters + * DEV_OPEN: a tty line has been opened + * DEV_CLOSE: a tty line has been closed + * CANCEL: terminate a previous incomplete system call immediately + * + * m_type TTY_LINE PROC_NR COUNT TTY_SPEK TTY_FLAGS ADDRESS + * --------------------------------------------------------------------------- + * | HARD_INT | | | | | | | + * |-------------+---------+---------+---------+---------+---------+---------| + * | HARD_STOP | | | | | | | + * |-------------+---------+---------+---------+---------+---------+---------| + * | DEV_READ |minor dev| proc nr | count | O_NONBLOCK| buf ptr | + * |-------------+---------+---------+---------+---------+---------+---------| + * | DEV_WRITE |minor dev| proc nr | count | | | buf ptr | + * |-------------+---------+---------+---------+---------+---------+---------| + * | DEV_IOCTL |minor dev| proc nr |func code|erase etc| flags | | + * |-------------+---------+---------+---------+---------+---------+---------| + * | DEV_OPEN |minor dev| proc nr | O_NOCTTY| | | | + * |-------------+---------+---------+---------+---------+---------+---------| + * | DEV_CLOSE |minor dev| proc nr | | | | | + * |-------------+---------+---------+---------+---------+---------+---------| + * | CANCEL |minor dev| proc nr | | | | | + * --------------------------------------------------------------------------- + * + * Changes: + * Jan 20, 2004 moved TTY driver to user-space (Jorrit N. Herder) + * Sep 20, 2004 local timer management/ sync alarms (Jorrit N. Herder) + * Jul 13, 2004 support for function key observers (Jorrit N. Herder) + */ + +#include "../drivers.h" +#include <termios.h> +#if ENABLE_SRCCOMPAT || ENABLE_BINCOMPAT +#include <sgtty.h> +#endif +#include <sys/ioc_tty.h> +#include <signal.h> +#include <minix/callnr.h> +#include <minix/com.h> +#if (CHIP == INTEL) +#include <minix/keymap.h> +#endif +#include "tty.h" + +/* Address of a tty structure. */ +#define tty_addr(line) (&tty_table[line]) + +/* First minor numbers for the various classes of TTY devices. */ +#define CONS_MINOR 0 +#define LOG_MINOR 15 +#define RS232_MINOR 16 +#define TTYPX_MINOR 128 +#define PTYPX_MINOR 192 + +/* Macros for magic tty types. */ +#define isconsole(tp) ((tp) < tty_addr(NR_CONS)) + +/* Macros for magic tty structure pointers. */ +#define FIRST_TTY tty_addr(0) +#define END_TTY tty_addr(sizeof(tty_table) / sizeof(tty_table[0])) + +/* A device exists if at least its 'devread' function is defined. */ +#define tty_active(tp) ((tp)->tty_devread != NULL) + +/* RS232 lines or pseudo terminals can be completely configured out. */ +#if NR_RS_LINES == 0 +#define rs_init(tp) ((void) 0) +#endif +#if NR_PTYS == 0 +#define pty_init(tp) ((void) 0) +#define do_pty(tp, mp) ((void) 0) +#endif + +FORWARD _PROTOTYPE( void tty_timed_out, (timer_t *tp) ); +FORWARD _PROTOTYPE( void expire_timers, (void) ); +FORWARD _PROTOTYPE( void settimer, (tty_t *tty_ptr, int enable) ); +FORWARD _PROTOTYPE( void do_cancel, (tty_t *tp, message *m_ptr) ); +FORWARD _PROTOTYPE( void do_ioctl, (tty_t *tp, message *m_ptr) ); +FORWARD _PROTOTYPE( void do_open, (tty_t *tp, message *m_ptr) ); +FORWARD _PROTOTYPE( void do_close, (tty_t *tp, message *m_ptr) ); +FORWARD _PROTOTYPE( void do_read, (tty_t *tp, message *m_ptr) ); +FORWARD _PROTOTYPE( void do_write, (tty_t *tp, message *m_ptr) ); +FORWARD _PROTOTYPE( void in_transfer, (tty_t *tp) ); +FORWARD _PROTOTYPE( int echo, (tty_t *tp, int ch) ); +FORWARD _PROTOTYPE( void rawecho, (tty_t *tp, int ch) ); +FORWARD _PROTOTYPE( int back_over, (tty_t *tp) ); +FORWARD _PROTOTYPE( void reprint, (tty_t *tp) ); +FORWARD _PROTOTYPE( void dev_ioctl, (tty_t *tp) ); +FORWARD _PROTOTYPE( void setattr, (tty_t *tp) ); +FORWARD _PROTOTYPE( void tty_icancel, (tty_t *tp) ); +FORWARD _PROTOTYPE( void tty_init, (tty_t *tp) ); +#if ENABLE_SRCCOMPAT || ENABLE_BINCOMPAT +FORWARD _PROTOTYPE( int compat_getp, (tty_t *tp, struct sgttyb *sg) ); +FORWARD _PROTOTYPE( int compat_getc, (tty_t *tp, struct tchars *sg) ); +FORWARD _PROTOTYPE( int compat_setp, (tty_t *tp, struct sgttyb *sg) ); +FORWARD _PROTOTYPE( int compat_setc, (tty_t *tp, struct tchars *sg) ); +FORWARD _PROTOTYPE( int tspd2sgspd, (speed_t tspd) ); +FORWARD _PROTOTYPE( speed_t sgspd2tspd, (int sgspd) ); +#if ENABLE_BINCOMPAT +FORWARD _PROTOTYPE( void do_ioctl_compat, (tty_t *tp, message *m_ptr) ); +#endif +#endif + +/* Default attributes. */ +PRIVATE struct termios termios_defaults = { + TINPUT_DEF, TOUTPUT_DEF, TCTRL_DEF, TLOCAL_DEF, TSPEED_DEF, TSPEED_DEF, + { + TEOF_DEF, TEOL_DEF, TERASE_DEF, TINTR_DEF, TKILL_DEF, TMIN_DEF, + TQUIT_DEF, TTIME_DEF, TSUSP_DEF, TSTART_DEF, TSTOP_DEF, + TREPRINT_DEF, TLNEXT_DEF, TDISCARD_DEF, + }, +}; +PRIVATE struct winsize winsize_defaults; /* = all zeroes */ + +/* Global variables for the TTY task (declared extern in tty.h). */ +PUBLIC tty_t tty_table[NR_CONS+NR_RS_LINES+NR_PTYS]; +PUBLIC int ccurrent; /* currently active console */ +PUBLIC timer_t *tty_timers; /* queue of TTY timers */ +PUBLIC clock_t tty_next_timeout; /* time that the next alarm is due */ +PUBLIC struct kenviron kenv; /* kernel environment variables */ + + +/*===========================================================================* + * tty_task * + *===========================================================================*/ +PUBLIC void main(void) +{ +/* Main routine of the terminal task. */ + + message tty_mess; /* buffer for all incoming messages */ + register tty_t *tp; + unsigned line; + int s; + char *types[] = {"task","driver","server", "user"}; + register struct proc *rp; + + /* Initialize the terminal lines. */ + for (tp = FIRST_TTY,s=0; tp < END_TTY; tp++,s++) { + tp->tty_index = s; + tty_init(tp); + } + + /* Get kernel environment (protected_mode, pc_at and ega are needed). */ + if (OK != (s=sys_getkenviron(&kenv))) { + server_panic("TTY","Couldn't obtain kernel environment.", s); + } + + printf("User-level TTY driver alive!\n"); + + while (TRUE) { + + /* Check for and handle any events on any of the ttys. */ + for (tp = FIRST_TTY; tp < END_TTY; tp++) { + if (tp->tty_events) handle_events(tp); + } + + /* Get a request message. */ + receive(ANY, &tty_mess); + + /* First handle all kernel notification types that the TTY supports. + * - An alarm went off, expire all timers and handle the events. + * - A hardware interrupt also is an invitation to check for events. + * - A new kernel message is available for printing. + * - Reset the console on system shutdown. + * Then see if this message is different from a normal device driver + * request and should be handled separately. These extra functions + * do not operate on a device, in constrast to the driver requests. + */ + switch (tty_mess.m_type) { + case SYN_ALARM: /* fall through */ + case HARD_INT: /* hardware interrupt notification */ + do_interrupt(&tty_mess);/* fetch chars from keyboard */ + expire_timers(); /* run watchdogs of expired timers */ + continue; /* contine to check for events */ + case NEW_KMESS: /* new kernel message is available */ + do_new_kmess(&tty_mess); + continue; + case HARD_STOP: { /* MINIX is going down */ + static int stop = 0; /* expect two HARD_STOP messages */ + if (! stop++) { + cons_stop(); /* first switch to primary console */ + } else { + printf("[DONE]\n"); + printf("MINIX will now be shutdown.\n"); + sys_exit(0); /* then exit TTY */ + } + continue; + } + case PANIC_DUMPS: /* allow panic dumps */ + cons_stop(); /* switch to primary console */ + do_panic_dumps(&tty_mess); + continue; + case DIAGNOSTICS: /* a server wants to print some */ + do_diagnostics(&tty_mess); + continue; + case FKEY_CONTROL: /* (un)register a fkey observer */ + do_fkey_ctl(&tty_mess); + continue; + default: /* should be a driver request */ + ; /* do nothing; end switch */ + } + + /* Only device requests should get to this point. + * Check the minor device number. + */ + line = tty_mess.TTY_LINE; + if ((line - CONS_MINOR) < NR_CONS) { + tp = tty_addr(line - CONS_MINOR); + } else if (line == LOG_MINOR) { + tp = tty_addr(0); + } else if ((line - RS232_MINOR) < NR_RS_LINES) { + tp = tty_addr(line - RS232_MINOR + NR_CONS); + } else if ((line - TTYPX_MINOR) < NR_PTYS) { + tp = tty_addr(line - TTYPX_MINOR + NR_CONS + NR_RS_LINES); + } else if ((line - PTYPX_MINOR) < NR_PTYS) { + tp = tty_addr(line - PTYPX_MINOR + NR_CONS + NR_RS_LINES); + if (tty_mess.m_type != DEV_IOCTL) { + do_pty(tp, &tty_mess); + continue; + } + } else { + tp = NULL; + } + + /* If the device doesn't exist or is not configured return ENXIO. */ + if (tp == NULL || ! tty_active(tp)) { + tty_reply(TASK_REPLY, tty_mess.m_source, + tty_mess.PROC_NR, ENXIO); + continue; + } + + /* Execute the requested device driver function. */ + switch (tty_mess.m_type) { + case DEV_READ: do_read(tp, &tty_mess); break; + case DEV_WRITE: do_write(tp, &tty_mess); break; + case DEV_IOCTL: do_ioctl(tp, &tty_mess); break; + case DEV_OPEN: do_open(tp, &tty_mess); break; + case DEV_CLOSE: do_close(tp, &tty_mess); break; + case CANCEL: do_cancel(tp, &tty_mess); break; + default: tty_reply(TASK_REPLY, tty_mess.m_source, + tty_mess.PROC_NR, EINVAL); + } + } +} + + +/*===========================================================================* + * do_read * + *===========================================================================*/ +PRIVATE void do_read(tp, m_ptr) +register tty_t *tp; /* pointer to tty struct */ +register message *m_ptr; /* pointer to message sent to the task */ +{ +/* A process wants to read from a terminal. */ + int r, status; + + /* Check if there is already a process hanging in a read, check if the + * parameters are correct, do I/O. + */ + if (tp->tty_inleft > 0) { + r = EIO; + } else + if (m_ptr->COUNT <= 0) { + r = EINVAL; +#if DEAD_CODE /* to be replaced by check on tp->tty_instatus !!! */ + } else + if (numap_local(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS, m_ptr->COUNT) == 0) { + r = EFAULT; +#endif + } else { + /* Copy information from the message to the tty struct. */ + tp->tty_inrepcode = TASK_REPLY; + tp->tty_incaller = m_ptr->m_source; + tp->tty_inproc = m_ptr->PROC_NR; + tp->tty_in_vir = (vir_bytes) m_ptr->ADDRESS; + tp->tty_inleft = m_ptr->COUNT; + + if (!(tp->tty_termios.c_lflag & ICANON) + && tp->tty_termios.c_cc[VTIME] > 0) { + if (tp->tty_termios.c_cc[VMIN] == 0) { + /* MIN & TIME specify a read timer that finishes the + * read in TIME/10 seconds if no bytes are available. + */ + settimer(tp, TRUE); + tp->tty_min = 1; + } else { + /* MIN & TIME specify an inter-byte timer that may + * have to be cancelled if there are no bytes yet. + */ + if (tp->tty_eotct == 0) { + settimer(tp, FALSE); + tp->tty_min = tp->tty_termios.c_cc[VMIN]; + } + } + } + + /* Anything waiting in the input buffer? Clear it out... */ + tp->tty_instatus = OK; /* start with OK, check later */ + in_transfer(tp); + /* ...then go back for more. */ + if (tp->tty_instatus == OK) + handle_events(tp); + if (tp->tty_inleft == 0) + return; /* already done */ + + /* There were no bytes in the input queue available, so either suspend + * the caller or break off the read if nonblocking. + */ + if (tp->tty_instatus != OK) { /* error occurred */ + r = tp->tty_instatus; + tp->tty_inleft = tp->tty_incum = 0; /* cancel the read */ + } + if (m_ptr->TTY_FLAGS & O_NONBLOCK) { + r = EAGAIN; /* cancel the read */ + tp->tty_inleft = tp->tty_incum = 0; + } else { + r = SUSPEND; /* suspend the caller */ + tp->tty_inrepcode = REVIVE; + } + } + tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r); +} + + +/*===========================================================================* + * do_write * + *===========================================================================*/ +PRIVATE void do_write(tp, m_ptr) +register tty_t *tp; +register message *m_ptr; /* pointer to message sent to the task */ +{ +/* A process wants to write on a terminal. */ + int r; + + /* Check if there is already a process hanging in a write, check if the + * parameters are correct, do I/O. + */ + if (tp->tty_outleft > 0) { + r = EIO; + } else + if (m_ptr->COUNT <= 0) { + r = EINVAL; +#if DEAD_CODE /* to be replaced by check on tp->tty_outstatus (!!!) */ + } else + if (numap_local(m_ptr->PROC_NR, (vir_bytes) m_ptr->ADDRESS, m_ptr->COUNT) == 0) { + r = EFAULT; +#endif + } else { + /* Copy message parameters to the tty structure. */ + tp->tty_outrepcode = TASK_REPLY; + tp->tty_outcaller = m_ptr->m_source; + tp->tty_outproc = m_ptr->PROC_NR; + tp->tty_out_vir = (vir_bytes) m_ptr->ADDRESS; + tp->tty_outleft = m_ptr->COUNT; + + /* Try to write. */ + tp->tty_outstatus = OK; /* start with OK, check later */ + handle_events(tp); + if (tp->tty_outleft == 0) return; /* already done */ + + /* None or not all the bytes could be written, so either suspend the + * caller or break off the write if nonblocking. + */ + if (tp->tty_outstatus != OK) { /* error occurred */ + r = tp->tty_outstatus; + tp->tty_outleft = tp->tty_outcum = 0; /* cancel the write */ + } + else if (m_ptr->TTY_FLAGS & O_NONBLOCK) { /* cancel the write */ + r = tp->tty_outcum > 0 ? tp->tty_outcum : EAGAIN; + tp->tty_outleft = tp->tty_outcum = 0; + } else { + r = SUSPEND; /* suspend the caller */ + tp->tty_outrepcode = REVIVE; + } + } + tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r); +} + + +/*===========================================================================* + * do_ioctl * + *===========================================================================*/ +PRIVATE void do_ioctl(tp, m_ptr) +register tty_t *tp; +message *m_ptr; /* pointer to message sent to task */ +{ +/* Perform an IOCTL on this terminal. Posix termios calls are handled + * by the IOCTL system call + */ + + int r; + union { + int i; +#if ENABLE_SRCCOMPAT + struct sgttyb sg; + struct tchars tc; +#endif + } param; + size_t size; + + /* Size of the ioctl parameter. */ + switch (m_ptr->TTY_REQUEST) { + case TCGETS: /* Posix tcgetattr function */ + case TCSETS: /* Posix tcsetattr function, TCSANOW option */ + case TCSETSW: /* Posix tcsetattr function, TCSADRAIN option */ + case TCSETSF: /* Posix tcsetattr function, TCSAFLUSH option */ + size = sizeof(struct termios); + break; + + case TCSBRK: /* Posix tcsendbreak function */ + case TCFLOW: /* Posix tcflow function */ + case TCFLSH: /* Posix tcflush function */ + case TIOCGPGRP: /* Posix tcgetpgrp function */ + case TIOCSPGRP: /* Posix tcsetpgrp function */ + size = sizeof(int); + break; + + case TIOCGWINSZ: /* get window size (not Posix) */ + case TIOCSWINSZ: /* set window size (not Posix) */ + size = sizeof(struct winsize); + break; + +#if ENABLE_SRCCOMPAT + case TIOCGETP: /* BSD-style get terminal properties */ + case TIOCSETP: /* BSD-style set terminal properties */ + size = sizeof(struct sgttyb); + break; + + case TIOCGETC: /* BSD-style get terminal special characters */ + case TIOCSETC: /* BSD-style get terminal special characters */ + size = sizeof(struct tchars); + break; +#endif +#if (MACHINE == IBM_PC) + case KIOCSMAP: /* load keymap (Minix extension) */ + size = sizeof(keymap_t); + break; + + case TIOCSFON: /* load font (Minix extension) */ + size = sizeof(u8_t [8192]); + break; + +#endif + case TCDRAIN: /* Posix tcdrain function -- no parameter */ + default: size = 0; + } + + r = OK; + switch (m_ptr->TTY_REQUEST) { + case TCGETS: + /* Get the termios attributes. */ + r = sys_vircopy(SELF, D, (vir_bytes) &tp->tty_termios, + m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS, + (vir_bytes) size); + break; + + case TCSETSW: + case TCSETSF: + case TCDRAIN: + if (tp->tty_outleft > 0) { + /* Wait for all ongoing output processing to finish. */ + tp->tty_iocaller = m_ptr->m_source; + tp->tty_ioproc = m_ptr->PROC_NR; + tp->tty_ioreq = m_ptr->REQUEST; + tp->tty_iovir = (vir_bytes) m_ptr->ADDRESS; + r = SUSPEND; + break; + } + if (m_ptr->TTY_REQUEST == TCDRAIN) break; + if (m_ptr->TTY_REQUEST == TCSETSF) tty_icancel(tp); + /*FALL THROUGH*/ + case TCSETS: + /* Set the termios attributes. */ + r = sys_vircopy( m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS, + SELF, D, (vir_bytes) &tp->tty_termios, (vir_bytes) size); + if (r != OK) break; + setattr(tp); + break; + + case TCFLSH: + r = sys_vircopy( m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS, + SELF, D, (vir_bytes) ¶m.i, (vir_bytes) size); + if (r != OK) break; + switch (param.i) { + case TCIFLUSH: tty_icancel(tp); break; + case TCOFLUSH: (*tp->tty_ocancel)(tp); break; + case TCIOFLUSH: tty_icancel(tp); (*tp->tty_ocancel)(tp);break; + default: r = EINVAL; + } + break; + + case TCFLOW: + r = sys_vircopy( m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS, + SELF, D, (vir_bytes) ¶m.i, (vir_bytes) size); + if (r != OK) break; + switch (param.i) { + case TCOOFF: + case TCOON: + tp->tty_inhibited = (param.i == TCOOFF); + tp->tty_events = 1; + break; + case TCIOFF: + (*tp->tty_echo)(tp, tp->tty_termios.c_cc[VSTOP]); + break; + case TCION: + (*tp->tty_echo)(tp, tp->tty_termios.c_cc[VSTART]); + break; + default: + r = EINVAL; + } + break; + + case TCSBRK: + if (tp->tty_break != NULL) (*tp->tty_break)(tp); + break; + + case TIOCGWINSZ: + r = sys_vircopy(SELF, D, (vir_bytes) &tp->tty_winsize, + m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS, + (vir_bytes) size); + break; + + case TIOCSWINSZ: + r = sys_vircopy( m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS, + SELF, D, (vir_bytes) &tp->tty_winsize, (vir_bytes) size); + /* SIGWINCH... */ + break; + +#if ENABLE_SRCCOMPAT + case TIOCGETP: + compat_getp(tp, ¶m.sg); + r = sys_vircopy(SELF, D, (vir_bytes) ¶m.sg, + m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS, + (vir_bytes) size); + break; + + case TIOCSETP: + r = sys_vircopy( m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS, + SELF, D, (vir_bytes) ¶m.sg, (vir_bytes) size); + if (r != OK) break; + compat_setp(tp, ¶m.sg); + break; + + case TIOCGETC: + compat_getc(tp, ¶m.tc); + r = sys_vircopy(SELF, D, (vir_bytes) ¶m.tc, + m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS, + (vir_bytes) size); + break; + + case TIOCSETC: + r = sys_vircopy( m_ptr->PROC_NR, D, (vir_bytes) m_ptr->ADDRESS, + SELF, D, (vir_bytes) ¶m.tc, (vir_bytes) size); + if (r != OK) break; + compat_setc(tp, ¶m.tc); + break; +#endif + +#if (MACHINE == IBM_PC) + case KIOCSMAP: + /* Load a new keymap (only /dev/console). */ + if (isconsole(tp)) r = kbd_loadmap(m_ptr); + break; + + case TIOCSFON: + /* Load a font into an EGA or VGA card (hs@hck.hr) */ + if (isconsole(tp)) r = con_loadfont(m_ptr); + break; +#endif + +#if (MACHINE == ATARI) + case VDU_LOADFONT: + r = vdu_loadfont(m_ptr); + break; +#endif + +/* These Posix functions are allowed to fail if _POSIX_JOB_CONTROL is + * not defined. + */ + case TIOCGPGRP: + case TIOCSPGRP: + default: +#if ENABLE_BINCOMPAT + do_ioctl_compat(tp, m_ptr); + return; +#else + r = ENOTTY; +#endif + } + + /* Send the reply. */ + tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r); +} + + +/*===========================================================================* + * do_open * + *===========================================================================*/ +PRIVATE void do_open(tp, m_ptr) +register tty_t *tp; +message *m_ptr; /* pointer to message sent to task */ +{ +/* A tty line has been opened. Make it the callers controlling tty if + * O_NOCTTY is *not* set and it is not the log device. 1 is returned if + * the tty is made the controlling tty, otherwise OK or an error code. + */ + int r = OK; + + if (m_ptr->TTY_LINE == LOG_MINOR) { + /* The log device is a write-only diagnostics device. */ + if (m_ptr->COUNT & R_BIT) r = EACCES; + } else { + if (!(m_ptr->COUNT & O_NOCTTY)) { + tp->tty_pgrp = m_ptr->PROC_NR; + r = 1; + } + tp->tty_openct++; + } + tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, r); +} + + +/*===========================================================================* + * do_close * + *===========================================================================*/ +PRIVATE void do_close(tp, m_ptr) +register tty_t *tp; +message *m_ptr; /* pointer to message sent to task */ +{ +/* A tty line has been closed. Clean up the line if it is the last close. */ + + if (m_ptr->TTY_LINE != LOG_MINOR && --tp->tty_openct == 0) { + tp->tty_pgrp = 0; + tty_icancel(tp); + (*tp->tty_ocancel)(tp); + (*tp->tty_close)(tp); + tp->tty_termios = termios_defaults; + tp->tty_winsize = winsize_defaults; + setattr(tp); + } + tty_reply(TASK_REPLY, m_ptr->m_source, m_ptr->PROC_NR, OK); +} + + +/*===========================================================================* + * do_cancel * + *===========================================================================*/ +PRIVATE void do_cancel(tp, m_ptr) +register tty_t *tp; +message *m_ptr; /* pointer to message sent to task */ +{ +/* A signal has been sent to a process that is hanging trying to read or write. + * The pending read or write must be finished off immediately. + */ + + int proc_nr; + int mode; + + /* Check the parameters carefully, to avoid cancelling twice. */ + proc_nr = m_ptr->PROC_NR; + mode = m_ptr->COUNT; + if ((mode & R_BIT) && tp->tty_inleft != 0 && proc_nr == tp->tty_inproc) { + /* Process was reading when killed. Clean up input. */ + tty_icancel(tp); + tp->tty_inleft = tp->tty_incum = 0; + } + if ((mode & W_BIT) && tp->tty_outleft != 0 && proc_nr == tp->tty_outproc) { + /* Process was writing when killed. Clean up output. */ + (*tp->tty_ocancel)(tp); + tp->tty_outleft = tp->tty_outcum = 0; + } + if (tp->tty_ioreq != 0 && proc_nr == tp->tty_ioproc) { + /* Process was waiting for output to drain. */ + tp->tty_ioreq = 0; + } + tp->tty_events = 1; + tty_reply(TASK_REPLY, m_ptr->m_source, proc_nr, EINTR); +} + + +/*===========================================================================* + * handle_events * + *===========================================================================*/ +PUBLIC void handle_events(tp) +tty_t *tp; /* TTY to check for events. */ +{ +/* Handle any events pending on a TTY. These events are usually device + * interrupts. + * + * Two kinds of events are prominent: + * - a character has been received from the console or an RS232 line. + * - an RS232 line has completed a write request (on behalf of a user). + * The interrupt handler may delay the interrupt message at its discretion + * to avoid swamping the TTY task. Messages may be overwritten when the + * lines are fast or when there are races between different lines, input + * and output, because MINIX only provides single buffering for interrupt + * messages (in proc.c). This is handled by explicitly checking each line + * for fresh input and completed output on each interrupt. + */ + char *buf; + unsigned count; + int status; + + do { + tp->tty_events = 0; + + /* Read input and perform input processing. */ + (*tp->tty_devread)(tp); + + /* Perform output processing and write output. */ + (*tp->tty_devwrite)(tp); + + /* Ioctl waiting for some event? */ + if (tp->tty_ioreq != 0) dev_ioctl(tp); + } while (tp->tty_events); + + /* Transfer characters from the input queue to a waiting process. */ + in_transfer(tp); + + /* Reply if enough bytes are available. */ + if (tp->tty_incum >= tp->tty_min && tp->tty_inleft > 0) { + tty_reply(tp->tty_inrepcode, tp->tty_incaller, tp->tty_inproc, + tp->tty_incum); + tp->tty_inleft = tp->tty_incum = 0; + } +} + + +/*===========================================================================* + * in_transfer * + *===========================================================================*/ +PRIVATE void in_transfer(tp) +register tty_t *tp; /* pointer to terminal to read from */ +{ +/* Transfer bytes from the input queue to a process reading from a terminal. */ + + int ch; + int count; + char buf[64], *bp; + + /* Force read to succeed if the line is hung up, looks like EOF to reader. */ + if (tp->tty_termios.c_ospeed == B0) tp->tty_min = 0; + + /* Anything to do? */ + if (tp->tty_inleft == 0 || tp->tty_eotct < tp->tty_min) return; + + bp = buf; + while (tp->tty_inleft > 0 && tp->tty_eotct > 0) { + ch = *tp->tty_intail; + + if (!(ch & IN_EOF)) { + /* One character to be delivered to the user. */ + *bp = ch & IN_CHAR; + tp->tty_inleft--; + if (++bp == bufend(buf)) { + /* Temp buffer full, copy to user space. */ + sys_vircopy(SELF, D, (vir_bytes) buf, + tp->tty_inproc, D, tp->tty_in_vir, + (vir_bytes) buflen(buf)); + tp->tty_in_vir += buflen(buf); + tp->tty_incum += buflen(buf); + bp = buf; + } + } + + /* Remove the character from the input queue. */ + if (++tp->tty_intail == bufend(tp->tty_inbuf)) + tp->tty_intail = tp->tty_inbuf; + tp->tty_incount--; + if (ch & IN_EOT) { + tp->tty_eotct--; + /* Don't read past a line break in canonical mode. */ + if (tp->tty_termios.c_lflag & ICANON) tp->tty_inleft = 0; + } + } + + if (bp > buf) { + /* Leftover characters in the buffer. */ + count = bp - buf; + sys_vircopy(SELF, D, (vir_bytes) buf, + tp->tty_inproc, D, tp->tty_in_vir, (vir_bytes) count); + tp->tty_in_vir += count; + tp->tty_incum += count; + } + + /* Usually reply to the reader, possibly even if incum == 0 (EOF). */ + if (tp->tty_inleft == 0) { + tty_reply(tp->tty_inrepcode, tp->tty_incaller, tp->tty_inproc, + tp->tty_incum); + tp->tty_inleft = tp->tty_incum = 0; + } +} + + +/*===========================================================================* + * in_process * + *===========================================================================*/ +PUBLIC int in_process(tp, buf, count) +register tty_t *tp; /* terminal on which character has arrived */ +char *buf; /* buffer with input characters */ +int count; /* number of input characters */ +{ +/* Characters have just been typed in. Process, save, and echo them. Return + * the number of characters processed. + */ + + int ch, sig, ct; + int timeset = FALSE; + static unsigned char csize_mask[] = { 0x1F, 0x3F, 0x7F, 0xFF }; + + for (ct = 0; ct < count; ct++) { + /* Take one character. */ + ch = *buf++ & BYTE; + + /* Strip to seven bits? */ + if (tp->tty_termios.c_iflag & ISTRIP) ch &= 0x7F; + + /* Input extensions? */ + if (tp->tty_termios.c_lflag & IEXTEN) { + + /* Previous character was a character escape? */ + if (tp->tty_escaped) { + tp->tty_escaped = NOT_ESCAPED; + ch |= IN_ESC; /* protect character */ + } + + /* LNEXT (^V) to escape the next character? */ + if (ch == tp->tty_termios.c_cc[VLNEXT]) { + tp->tty_escaped = ESCAPED; + rawecho(tp, '^'); + rawecho(tp, '\b'); + continue; /* do not store the escape */ + } + + /* REPRINT (^R) to reprint echoed characters? */ + if (ch == tp->tty_termios.c_cc[VREPRINT]) { + reprint(tp); + continue; + } + } + + /* _POSIX_VDISABLE is a normal character value, so better escape it. */ + if (ch == _POSIX_VDISABLE) ch |= IN_ESC; + + /* Map CR to LF, ignore CR, or map LF to CR. */ + if (ch == '\r') { + if (tp->tty_termios.c_iflag & IGNCR) continue; + if (tp->tty_termios.c_iflag & ICRNL) ch = '\n'; + } else + if (ch == '\n') { + if (tp->tty_termios.c_iflag & INLCR) ch = '\r'; + } + + /* Canonical mode? */ + if (tp->tty_termios.c_lflag & ICANON) { + + /* Erase processing (rub out of last character). */ + if (ch == tp->tty_termios.c_cc[VERASE]) { + (void) back_over(tp); + if (!(tp->tty_termios.c_lflag & ECHOE)) { + (void) echo(tp, ch); + } + continue; + } + + /* Kill processing (remove current line). */ + if (ch == tp->tty_termios.c_cc[VKILL]) { + while (back_over(tp)) {} + if (!(tp->tty_termios.c_lflag & ECHOE)) { + (void) echo(tp, ch); + if (tp->tty_termios.c_lflag & ECHOK) + rawecho(tp, '\n'); + } + continue; + } + + /* EOF (^D) means end-of-file, an invisible "line break". */ + if (ch == tp->tty_termios.c_cc[VEOF]) ch |= IN_EOT | IN_EOF; + + /* The line may be returned to the user after an LF. */ + if (ch == '\n') ch |= IN_EOT; + + /* Same thing with EOL, whatever it may be. */ + if (ch == tp->tty_termios.c_cc[VEOL]) ch |= IN_EOT; + } + + /* Start/stop input control? */ + if (tp->tty_termios.c_iflag & IXON) { + + /* Output stops on STOP (^S). */ + if (ch == tp->tty_termios.c_cc[VSTOP]) { + tp->tty_inhibited = STOPPED; + tp->tty_events = 1; + continue; + } + + /* Output restarts on START (^Q) or any character if IXANY. */ + if (tp->tty_inhibited) { + if (ch == tp->tty_termios.c_cc[VSTART] + || (tp->tty_termios.c_iflag & IXANY)) { + tp->tty_inhibited = RUNNING; + tp->tty_events = 1; + if (ch == tp->tty_termios.c_cc[VSTART]) + continue; + } + } + } + + if (tp->tty_termios.c_lflag & ISIG) { + /* Check for INTR (^?) and QUIT (^\) characters. */ + if (ch == tp->tty_termios.c_cc[VINTR] + || ch == tp->tty_termios.c_cc[VQUIT]) { + sig = SIGINT; + if (ch == tp->tty_termios.c_cc[VQUIT]) sig = SIGQUIT; + sigchar(tp, sig); + (void) echo(tp, ch); + continue; + } + } + + /* Is there space in the input buffer? */ + if (tp->tty_incount == buflen(tp->tty_inbuf)) { + /* No space; discard in canonical mode, keep in raw mode. */ + if (tp->tty_termios.c_lflag & ICANON) continue; + break; + } + + if (!(tp->tty_termios.c_lflag & ICANON)) { + /* In raw mode all characters are "line breaks". */ + ch |= IN_EOT; + + /* Start an inter-byte timer? */ + if (!timeset && tp->tty_termios.c_cc[VMIN] > 0 + && tp->tty_termios.c_cc[VTIME] > 0) { + settimer(tp, TRUE); + timeset = TRUE; + } + } + + /* Perform the intricate function of echoing. */ + if (tp->tty_termios.c_lflag & (ECHO|ECHONL)) ch = echo(tp, ch); + + /* Save the character in the input queue. */ + *tp->tty_inhead++ = ch; + if (tp->tty_inhead == bufend(tp->tty_inbuf)) + tp->tty_inhead = tp->tty_inbuf; + tp->tty_incount++; + if (ch & IN_EOT) tp->tty_eotct++; + + /* Try to finish input if the queue threatens to overflow. */ + if (tp->tty_incount == buflen(tp->tty_inbuf)) in_transfer(tp); + } + return ct; +} + + +/*===========================================================================* + * echo * + *===========================================================================*/ +PRIVATE int echo(tp, ch) +register tty_t *tp; /* terminal on which to echo */ +register int ch; /* pointer to character to echo */ +{ +/* Echo the character if echoing is on. Some control characters are echoed + * with their normal effect, other control characters are echoed as "^X", + * normal characters are echoed normally. EOF (^D) is echoed, but immediately + * backspaced over. Return the character with the echoed length added to its + * attributes. + */ + int len, rp; + + ch &= ~IN_LEN; + if (!(tp->tty_termios.c_lflag & ECHO)) { + if (ch == ('\n' | IN_EOT) && (tp->tty_termios.c_lflag + & (ICANON|ECHONL)) == (ICANON|ECHONL)) + (*tp->tty_echo)(tp, '\n'); + return(ch); + } + + /* "Reprint" tells if the echo output has been messed up by other output. */ + rp = tp->tty_incount == 0 ? FALSE : tp->tty_reprint; + + if ((ch & IN_CHAR) < ' ') { + switch (ch & (IN_ESC|IN_EOF|IN_EOT|IN_CHAR)) { + case '\t': + len = 0; + do { + (*tp->tty_echo)(tp, ' '); + len++; + } while (len < TAB_SIZE && (tp->tty_position & TAB_MASK) != 0); + break; + case '\r' | IN_EOT: + case '\n' | IN_EOT: + (*tp->tty_echo)(tp, ch & IN_CHAR); + len = 0; + break; + default: + (*tp->tty_echo)(tp, '^'); + (*tp->tty_echo)(tp, '@' + (ch & IN_CHAR)); + len = 2; + } + } else + if ((ch & IN_CHAR) == '\177') { + /* A DEL prints as "^?". */ + (*tp->tty_echo)(tp, '^'); + (*tp->tty_echo)(tp, '?'); + len = 2; + } else { + (*tp->tty_echo)(tp, ch & IN_CHAR); + len = 1; + } + if (ch & IN_EOF) while (len > 0) { (*tp->tty_echo)(tp, '\b'); len--; } + + tp->tty_reprint = rp; + return(ch | (len << IN_LSHIFT)); +} + + +/*==========================================================================* + * rawecho * + *==========================================================================*/ +PRIVATE void rawecho(tp, ch) +register tty_t *tp; +int ch; +{ +/* Echo without interpretation if ECHO is set. */ + int rp = tp->tty_reprint; + if (tp->tty_termios.c_lflag & ECHO) (*tp->tty_echo)(tp, ch); + tp->tty_reprint = rp; +} + + +/*==========================================================================* + * back_over * + *==========================================================================*/ +PRIVATE int back_over(tp) +register tty_t *tp; +{ +/* Backspace to previous character on screen and erase it. */ + u16_t *head; + int len; + + if (tp->tty_incount == 0) return(0); /* queue empty */ + head = tp->tty_inhead; + if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf); + if (*--head & IN_EOT) return(0); /* can't erase "line breaks" */ + if (tp->tty_reprint) reprint(tp); /* reprint if messed up */ + tp->tty_inhead = head; + tp->tty_incount--; + if (tp->tty_termios.c_lflag & ECHOE) { + len = (*head & IN_LEN) >> IN_LSHIFT; + while (len > 0) { + rawecho(tp, '\b'); + rawecho(tp, ' '); + rawecho(tp, '\b'); + len--; + } + } + return(1); /* one character erased */ +} + + +/*==========================================================================* + * reprint * + *==========================================================================*/ +PRIVATE void reprint(tp) +register tty_t *tp; /* pointer to tty struct */ +{ +/* Restore what has been echoed to screen before if the user input has been + * messed up by output, or if REPRINT (^R) is typed. + */ + int count; + u16_t *head; + + tp->tty_reprint = FALSE; + + /* Find the last line break in the input. */ + head = tp->tty_inhead; + count = tp->tty_incount; + while (count > 0) { + if (head == tp->tty_inbuf) head = bufend(tp->tty_inbuf); + if (head[-1] & IN_EOT) break; + head--; + count--; + } + if (count == tp->tty_incount) return; /* no reason to reprint */ + + /* Show REPRINT (^R) and move to a new line. */ + (void) echo(tp, tp->tty_termios.c_cc[VREPRINT] | IN_ESC); + rawecho(tp, '\r'); + rawecho(tp, '\n'); + + /* Reprint from the last break onwards. */ + do { + if (head == bufend(tp->tty_inbuf)) head = tp->tty_inbuf; + *head = echo(tp, *head); + head++; + count++; + } while (count < tp->tty_incount); +} + + +/*==========================================================================* + * out_process * + *==========================================================================*/ +PUBLIC void out_process(tp, bstart, bpos, bend, icount, ocount) +tty_t *tp; +char *bstart, *bpos, *bend; /* start/pos/end of circular buffer */ +int *icount; /* # input chars / input chars used */ +int *ocount; /* max output chars / output chars used */ +{ +/* Perform output processing on a circular buffer. *icount is the number of + * bytes to process, and the number of bytes actually processed on return. + * *ocount is the space available on input and the space used on output. + * (Naturally *icount < *ocount.) The column position is updated modulo + * the TAB size, because we really only need it for tabs. + */ + + int tablen; + int ict = *icount; + int oct = *ocount; + int pos = tp->tty_position; + + while (ict > 0) { + switch (*bpos) { + case '\7': + break; + case '\b': + pos--; + break; + case '\r': + pos = 0; + break; + case '\n': + if ((tp->tty_termios.c_oflag & (OPOST|ONLCR)) + == (OPOST|ONLCR)) { + /* Map LF to CR+LF if there is space. Note that the + * next character in the buffer is overwritten, so + * we stop at this point. + */ + if (oct >= 2) { + *bpos = '\r'; + if (++bpos == bend) bpos = bstart; + *bpos = '\n'; + pos = 0; + ict--; + oct -= 2; + } + goto out_done; /* no space or buffer got changed */ + } + break; + case '\t': + /* Best guess for the tab length. */ + tablen = TAB_SIZE - (pos & TAB_MASK); + + if ((tp->tty_termios.c_oflag & (OPOST|XTABS)) + == (OPOST|XTABS)) { + /* Tabs must be expanded. */ + if (oct >= tablen) { + pos += tablen; + ict--; + oct -= tablen; + do { + *bpos = ' '; + if (++bpos == bend) bpos = bstart; + } while (--tablen != 0); + } + goto out_done; + } + /* Tabs are output directly. */ + pos += tablen; + break; + default: + /* Assume any other character prints as one character. */ + pos++; + } + if (++bpos == bend) bpos = bstart; + ict--; + oct--; + } +out_done: + tp->tty_position = pos & TAB_MASK; + + *icount -= ict; /* [io]ct are the number of chars not used */ + *ocount -= oct; /* *[io]count are the number of chars that are used */ +} + + +/*===========================================================================* + * dev_ioctl * + *===========================================================================*/ +PRIVATE void dev_ioctl(tp) +tty_t *tp; +{ +/* The ioctl's TCSETSW, TCSETSF and TCDRAIN wait for output to finish to make + * sure that an attribute change doesn't affect the processing of current + * output. Once output finishes the ioctl is executed as in do_ioctl(). + */ + int result; + + if (tp->tty_outleft > 0) return; /* output not finished */ + + if (tp->tty_ioreq != TCDRAIN) { + if (tp->tty_ioreq == TCSETSF) tty_icancel(tp); + result = sys_vircopy(tp->tty_ioproc, D, tp->tty_iovir, + SELF, D, (vir_bytes) &tp->tty_termios, + (vir_bytes) sizeof(tp->tty_termios)); + setattr(tp); + } + tp->tty_ioreq = 0; + tty_reply(REVIVE, tp->tty_iocaller, tp->tty_ioproc, result); +} + + +/*===========================================================================* + * setattr * + *===========================================================================*/ +PRIVATE void setattr(tp) +tty_t *tp; +{ +/* Apply the new line attributes (raw/canonical, line speed, etc.) */ + u16_t *inp; + int count; + + if (!(tp->tty_termios.c_lflag & ICANON)) { + /* Raw mode; put a "line break" on all characters in the input queue. + * It is undefined what happens to the input queue when ICANON is + * switched off, a process should use TCSAFLUSH to flush the queue. + * Keeping the queue to preserve typeahead is the Right Thing, however + * when a process does use TCSANOW to switch to raw mode. + */ + count = tp->tty_eotct = tp->tty_incount; + inp = tp->tty_intail; + while (count > 0) { + *inp |= IN_EOT; + if (++inp == bufend(tp->tty_inbuf)) inp = tp->tty_inbuf; + --count; + } + } + + /* Inspect MIN and TIME. */ + settimer(tp, FALSE); + if (tp->tty_termios.c_lflag & ICANON) { + /* No MIN & TIME in canonical mode. */ + tp->tty_min = 1; + } else { + /* In raw mode MIN is the number of chars wanted, and TIME how long + * to wait for them. With interesting exceptions if either is zero. + */ + tp->tty_min = tp->tty_termios.c_cc[VMIN]; + if (tp->tty_min == 0 && tp->tty_termios.c_cc[VTIME] > 0) + tp->tty_min = 1; + } + + if (!(tp->tty_termios.c_iflag & IXON)) { + /* No start/stop output control, so don't leave output inhibited. */ + tp->tty_inhibited = RUNNING; + tp->tty_events = 1; + } + + /* Setting the output speed to zero hangs up the phone. */ + if (tp->tty_termios.c_ospeed == B0) sigchar(tp, SIGHUP); + + /* Set new line speed, character size, etc at the device level. */ + (*tp->tty_ioctl)(tp); +} + + +/*===========================================================================* + * tty_reply * + *===========================================================================*/ +PUBLIC void tty_reply(code, replyee, proc_nr, status) +int code; /* TASK_REPLY or REVIVE */ +int replyee; /* destination address for the reply */ +int proc_nr; /* to whom should the reply go? */ +int status; /* reply code */ +{ +/* Send a reply to a process that wanted to read or write data. */ + + message tty_mess; + + tty_mess.m_type = code; + tty_mess.REP_PROC_NR = proc_nr; + tty_mess.REP_STATUS = status; + if ((status = send(replyee, &tty_mess)) != OK) + server_panic("TTY","tty_reply failed, status\n", status); +} + + +/*===========================================================================* + * sigchar * + *===========================================================================*/ +PUBLIC void sigchar(tp, sig) +register tty_t *tp; +int sig; /* SIGINT, SIGQUIT, SIGKILL or SIGHUP */ +{ +/* Process a SIGINT, SIGQUIT or SIGKILL char from the keyboard or SIGHUP from + * a tty close, "stty 0", or a real RS-232 hangup. MM will send the signal to + * the process group (INT, QUIT), all processes (KILL), or the session leader + * (HUP). + */ + int status; + + if (tp->tty_pgrp != 0) + if (OK != (status = sys_kill(tp->tty_pgrp, sig))) + server_panic("TTY","Error, call to sys_kill failed", status); + + if (!(tp->tty_termios.c_lflag & NOFLSH)) { + tp->tty_incount = tp->tty_eotct = 0; /* kill earlier input */ + tp->tty_intail = tp->tty_inhead; + (*tp->tty_ocancel)(tp); /* kill all output */ + tp->tty_inhibited = RUNNING; + tp->tty_events = 1; + } +} + + +/*==========================================================================* + * tty_icancel * + *==========================================================================*/ +PRIVATE void tty_icancel(tp) +register tty_t *tp; +{ +/* Discard all pending input, tty buffer or device. */ + + tp->tty_incount = tp->tty_eotct = 0; + tp->tty_intail = tp->tty_inhead; + (*tp->tty_icancel)(tp); +} + + +/*==========================================================================* + * tty_init * + *==========================================================================*/ +PRIVATE void tty_init(tp) +tty_t *tp; /* TTY line to initialize. */ +{ +/* Initialize tty structure and call device initialization routines. */ + + tmr_inittimer(&tp->tty_tmr); + + tp->tty_intail = tp->tty_inhead = tp->tty_inbuf; + tp->tty_min = 1; + tp->tty_termios = termios_defaults; + tp->tty_icancel = tp->tty_ocancel = tp->tty_ioctl = tp->tty_close = + tty_devnop; + if (tp < tty_addr(NR_CONS)) { + scr_init(tp); + } else + if (tp < tty_addr(NR_CONS+NR_RS_LINES)) { + rs_init(tp); + } else { + pty_init(tp); + } +} + +/*==========================================================================* + * tty_timed_out * + *==========================================================================*/ +PRIVATE void tty_timed_out(timer_t *tp) +{ +/* This timer has expired. Set the events flag, to force processing. */ + tty_t *tty_ptr; + tty_ptr = &tty_table[tmr_arg(tp)->ta_int]; + tty_ptr->tty_min = 0; /* force read to succeed */ + tty_ptr->tty_events = 1; +} + +/*==========================================================================* + * expire_timers * + *==========================================================================*/ +PRIVATE void expire_timers(void) +{ +/* A synchronous alarm message was received. Check if there are any expired + * timers. Possibly set the event flag and reschedule another alarm. + */ + clock_t now; /* current time */ + int s; + + /* Get the current time to compare the timers against. */ + if ((s=sys_getuptime(&now)) != OK) + server_panic("TTY","Couldn't get uptime from clock.", s); + + /* Scan the queue of timers for expired timers. This dispatch the watchdog + * functions of expired timers. Possibly a new alarm call must be scheduled. + */ + tmrs_exptimers(&tty_timers, now); + if (tty_timers == NULL) tty_next_timeout = TMR_NEVER; + else { /* set new sync alarm */ + tty_next_timeout = tty_timers->tmr_exp_time; + if ((s=sys_syncalrm(SELF, tty_next_timeout, 1)) != OK) + server_panic("TTY","Couldn't set synchronous alarm.", s); + } +} + +/*===========================================================================* + * settimer * + *===========================================================================*/ +PRIVATE void settimer(tty_ptr, enable) +tty_t *tty_ptr; /* line to set or unset a timer on */ +int enable; /* set timer if true, otherwise unset */ +{ + clock_t now; /* current time */ + clock_t exp_time; + int s; + + /* Get the current time to calculate the timeout time. */ + if ((s=sys_getuptime(&now)) != OK) + server_panic("TTY","Couldn't get uptime from clock.", s); + if (enable) { + exp_time = now + tty_ptr->tty_termios.c_cc[VTIME] * (HZ/10); + /* Set a new timer for enabling the TTY events flags. */ + tmrs_settimer(&tty_timers, &tty_ptr->tty_tmr, + exp_time, tty_timed_out); + } else { + /* Remove the timer from the active and expired lists. */ + tmrs_clrtimer(&tty_timers, &tty_ptr->tty_tmr); + } + + /* Now check if a new alarm must be scheduled. This happens when the front + * of the timers queue was disabled or reinserted at another position, or + * when a new timer was added to the front. + */ + if (tty_timers == NULL) tty_next_timeout = TMR_NEVER; + else if (tty_timers->tmr_exp_time != tty_next_timeout) { + tty_next_timeout = tty_timers->tmr_exp_time; + if ((s=sys_syncalrm(SELF, tty_next_timeout, 1)) != OK) + server_panic("TTY","Couldn't set synchronous alarm.", s); + } +} + + +/*==========================================================================* + * tty_devnop * + *==========================================================================*/ +PUBLIC void tty_devnop(tp) +tty_t *tp; +{ + /* Some functions need not be implemented at the device level. */ +} + + +#if ENABLE_SRCCOMPAT || ENABLE_BINCOMPAT +/*===========================================================================* + * compat_getp * + *===========================================================================*/ +PRIVATE int compat_getp(tp, sg) +tty_t *tp; +struct sgttyb *sg; +{ +/* Translate an old TIOCGETP to the termios equivalent. */ + int flgs; + + sg->sg_erase = tp->tty_termios.c_cc[VERASE]; + sg->sg_kill = tp->tty_termios.c_cc[VKILL]; + sg->sg_ospeed = tspd2sgspd(cfgetospeed(&tp->tty_termios)); + sg->sg_ispeed = tspd2sgspd(cfgetispeed(&tp->tty_termios)); + + flgs = 0; + + /* XTABS - if OPOST and XTABS */ + if ((tp->tty_termios.c_oflag & (OPOST|XTABS)) == (OPOST|XTABS)) + flgs |= 0006000; + + /* BITS5..BITS8 - map directly to CS5..CS8 */ + flgs |= (tp->tty_termios.c_cflag & CSIZE) << (8-2); + + /* EVENP - if PARENB and not PARODD */ + if ((tp->tty_termios.c_cflag & (PARENB|PARODD)) == PARENB) + flgs |= 0000200; + + /* ODDP - if PARENB and PARODD */ + if ((tp->tty_termios.c_cflag & (PARENB|PARODD)) == (PARENB|PARODD)) + flgs |= 0000100; + + /* RAW - if not ICANON and not ISIG */ + if (!(tp->tty_termios.c_lflag & (ICANON|ISIG))) + flgs |= 0000040; + + /* CRMOD - if ICRNL */ + if (tp->tty_termios.c_iflag & ICRNL) + flgs |= 0000020; + + /* ECHO - if ECHO */ + if (tp->tty_termios.c_lflag & ECHO) + flgs |= 0000010; + + /* CBREAK - if not ICANON and ISIG */ + if ((tp->tty_termios.c_lflag & (ICANON|ISIG)) == ISIG) + flgs |= 0000002; + + sg->sg_flags = flgs; + return(OK); +} + + +/*===========================================================================* + * compat_getc * + *===========================================================================*/ +PRIVATE int compat_getc(tp, tc) +tty_t *tp; +struct tchars *tc; +{ +/* Translate an old TIOCGETC to the termios equivalent. */ + + tc->t_intrc = tp->tty_termios.c_cc[VINTR]; + tc->t_quitc = tp->tty_termios.c_cc[VQUIT]; + tc->t_startc = tp->tty_termios.c_cc[VSTART]; + tc->t_stopc = tp->tty_termios.c_cc[VSTOP]; + tc->t_brkc = tp->tty_termios.c_cc[VEOL]; + tc->t_eofc = tp->tty_termios.c_cc[VEOF]; + return(OK); +} + + +/*===========================================================================* + * compat_setp * + *===========================================================================*/ +PRIVATE int compat_setp(tp, sg) +tty_t *tp; +struct sgttyb *sg; +{ +/* Translate an old TIOCSETP to the termios equivalent. */ + struct termios termios; + int flags; + + termios = tp->tty_termios; + + termios.c_cc[VERASE] = sg->sg_erase; + termios.c_cc[VKILL] = sg->sg_kill; + cfsetispeed(&termios, sgspd2tspd(sg->sg_ispeed & BYTE)); + cfsetospeed(&termios, sgspd2tspd(sg->sg_ospeed & BYTE)); + flags = sg->sg_flags; + + /* Input flags */ + + /* BRKINT - not changed */ + /* ICRNL - set if CRMOD is set and not RAW */ + /* (CRMOD also controls output) */ + termios.c_iflag &= ~ICRNL; + if ((flags & 0000020) && !(flags & 0000040)) + termios.c_iflag |= ICRNL; + + /* IGNBRK - not changed */ + /* IGNCR - forced off (ignoring cr's is not supported) */ + termios.c_iflag &= ~IGNCR; + + /* IGNPAR - not changed */ + /* INLCR - forced off (mapping nl's to cr's is not supported) */ + termios.c_iflag &= ~INLCR; + + /* INPCK - not changed */ + /* ISTRIP - not changed */ + /* IXOFF - not changed */ + /* IXON - forced on if not RAW */ + termios.c_iflag &= ~IXON; + if (!(flags & 0000040)) + termios.c_iflag |= IXON; + + /* PARMRK - not changed */ + + /* Output flags */ + + /* OPOST - forced on if not RAW */ + termios.c_oflag &= ~OPOST; + if (!(flags & 0000040)) + termios.c_oflag |= OPOST; + + /* ONLCR - forced on if CRMOD */ + termios.c_oflag &= ~ONLCR; + if (flags & 0000020) + termios.c_oflag |= ONLCR; + + /* XTABS - forced on if XTABS */ + termios.c_oflag &= ~XTABS; + if (flags & 0006000) + termios.c_oflag |= XTABS; + + /* CLOCAL - not changed */ + /* CREAD - forced on (receiver is always enabled) */ + termios.c_cflag |= CREAD; + + /* CSIZE - CS5-CS8 correspond directly to BITS5-BITS8 */ + termios.c_cflag = (termios.c_cflag & ~CSIZE) | ((flags & 0001400) >> (8-2)); + + /* CSTOPB - not changed */ + /* HUPCL - not changed */ + /* PARENB - set if EVENP or ODDP is set */ + termios.c_cflag &= ~PARENB; + if (flags & (0000200|0000100)) + termios.c_cflag |= PARENB; + + /* PARODD - set if ODDP is set */ + termios.c_cflag &= ~PARODD; + if (flags & 0000100) + termios.c_cflag |= PARODD; + + /* Local flags */ + + /* ECHO - set if ECHO is set */ + termios.c_lflag &= ~ECHO; + if (flags & 0000010) + termios.c_lflag |= ECHO; + + /* ECHOE - not changed */ + /* ECHOK - not changed */ + /* ECHONL - not changed */ + /* ICANON - set if neither CBREAK nor RAW */ + termios.c_lflag &= ~ICANON; + if (!(flags & (0000002|0000040))) + termios.c_lflag |= ICANON; + + /* IEXTEN - set if not RAW */ + /* ISIG - set if not RAW */ + termios.c_lflag &= ~(IEXTEN|ISIG); + if (!(flags & 0000040)) + termios.c_lflag |= (IEXTEN|ISIG); + + /* NOFLSH - not changed */ + /* TOSTOP - not changed */ + + tp->tty_termios = termios; + setattr(tp); + return(OK); +} + + +/*===========================================================================* + * compat_setc * + *===========================================================================*/ +PRIVATE int compat_setc(tp, tc) +tty_t *tp; +struct tchars *tc; +{ +/* Translate an old TIOCSETC to the termios equivalent. */ + struct termios termios; + + termios = tp->tty_termios; + + termios.c_cc[VINTR] = tc->t_intrc; + termios.c_cc[VQUIT] = tc->t_quitc; + termios.c_cc[VSTART] = tc->t_startc; + termios.c_cc[VSTOP] = tc->t_stopc; + termios.c_cc[VEOL] = tc->t_brkc; + termios.c_cc[VEOF] = tc->t_eofc; + + tp->tty_termios = termios; + setattr(tp); + return(OK); +} + + +/* Table of termios line speed to sgtty line speed translations. All termios + * speeds are present even if sgtty didn't know about them. (Now it does.) + */ +PRIVATE struct s2s { + speed_t tspd; + u8_t sgspd; +} ts2sgs[] = { + { B0, 0 }, + { B50, 50 }, + { B75, 75 }, + { B110, 1 }, + { B134, 134 }, + { B200, 2 }, + { B300, 3 }, + { B600, 6 }, + { B1200, 12 }, + { B1800, 18 }, + { B2400, 24 }, + { B4800, 48 }, + { B9600, 96 }, + { B19200, 192 }, + { B38400, 195 }, + { B57600, 194 }, + { B115200, 193 }, +}; + +/*===========================================================================* + * tspd2sgspd * + *===========================================================================*/ +PRIVATE int tspd2sgspd(tspd) +speed_t tspd; +{ +/* Translate a termios speed to sgtty speed. */ + struct s2s *s; + + for (s = ts2sgs; s < ts2sgs + sizeof(ts2sgs)/sizeof(ts2sgs[0]); s++) { + if (s->tspd == tspd) return(s->sgspd); + } + return 96; +} + + +/*===========================================================================* + * sgspd2tspd * + *===========================================================================*/ +PRIVATE speed_t sgspd2tspd(sgspd) +int sgspd; +{ +/* Translate a sgtty speed to termios speed. */ + struct s2s *s; + + for (s = ts2sgs; s < ts2sgs + sizeof(ts2sgs)/sizeof(ts2sgs[0]); s++) { + if (s->sgspd == sgspd) return(s->tspd); + } + return B9600; +} + + +#if ENABLE_BINCOMPAT +/*===========================================================================* + * do_ioctl_compat * + *===========================================================================*/ +PRIVATE void do_ioctl_compat(tp, m_ptr) +tty_t *tp; +message *m_ptr; +{ +/* Handle the old sgtty ioctl's that packed the sgtty or tchars struct into + * the Minix message. Efficient then, troublesome now. + */ + int minor, proc, func, result, r; + long flags, erki, spek; + u8_t erase, kill, intr, quit, xon, xoff, brk, eof, ispeed, ospeed; + struct sgttyb sg; + struct tchars tc; + message reply_mess; + + minor = m_ptr->TTY_LINE; + proc = m_ptr->PROC_NR; + func = m_ptr->REQUEST; + spek = m_ptr->m2_l1; + flags = m_ptr->m2_l2; + + switch(func) + { + case (('t'<<8) | 8): /* TIOCGETP */ + r = compat_getp(tp, &sg); + erase = sg.sg_erase; + kill = sg.sg_kill; + ispeed = sg.sg_ispeed; + ospeed = sg.sg_ospeed; + flags = sg.sg_flags; + erki = ((long)ospeed<<24) | ((long)ispeed<<16) | ((long)erase<<8) |kill; + break; + case (('t'<<8) | 18): /* TIOCGETC */ + r = compat_getc(tp, &tc); + intr = tc.t_intrc; + quit = tc.t_quitc; + xon = tc.t_startc; + xoff = tc.t_stopc; + brk = tc.t_brkc; + eof = tc.t_eofc; + erki = ((long)intr<<24) | ((long)quit<<16) | ((long)xon<<8) | xoff; + flags = (eof << 8) | brk; + break; + case (('t'<<8) | 17): /* TIOCSETC */ + tc.t_stopc = (spek >> 0) & 0xFF; + tc.t_startc = (spek >> 8) & 0xFF; + tc.t_quitc = (spek >> 16) & 0xFF; + tc.t_intrc = (spek >> 24) & 0xFF; + tc.t_brkc = (flags >> 0) & 0xFF; + tc.t_eofc = (flags >> 8) & 0xFF; + r = compat_setc(tp, &tc); + break; + case (('t'<<8) | 9): /* TIOCSETP */ + sg.sg_erase = (spek >> 8) & 0xFF; + sg.sg_kill = (spek >> 0) & 0xFF; + sg.sg_ispeed = (spek >> 16) & 0xFF; + sg.sg_ospeed = (spek >> 24) & 0xFF; + sg.sg_flags = flags; + r = compat_setp(tp, &sg); + break; + default: + r = ENOTTY; + } + reply_mess.m_type = TASK_REPLY; + reply_mess.REP_PROC_NR = m_ptr->PROC_NR; + reply_mess.REP_STATUS = r; + reply_mess.m2_l1 = erki; + reply_mess.m2_l2 = flags; + send(m_ptr->m_source, &reply_mess); +} +#endif /* ENABLE_BINCOMPAT */ +#endif /* ENABLE_SRCCOMPAT || ENABLE_BINCOMPAT */ + + + diff --git a/drivers/tty/tty.h b/drivers/tty/tty.h new file mode 100644 index 000000000..d0431adf7 --- /dev/null +++ b/drivers/tty/tty.h @@ -0,0 +1,154 @@ +/* tty.h - Terminals */ + +#include <timers.h> + + +#define TTY_IN_BYTES 256 /* tty input queue size */ +#define TAB_SIZE 8 /* distance between tab stops */ +#define TAB_MASK 7 /* mask to compute a tab stop position */ + +#define ESC '\33' /* escape */ + +#define O_NOCTTY 00400 /* from <fcntl.h>, or cc will choke */ +#define O_NONBLOCK 04000 + +struct tty; +typedef _PROTOTYPE( void (*devfun_t), (struct tty *tp) ); +typedef _PROTOTYPE( void (*devfunarg_t), (struct tty *tp, int c) ); + +typedef struct tty { + int tty_events; /* set when TTY should inspect this line */ + int tty_index; /* index into TTY table */ + + /* Input queue. Typed characters are stored here until read by a program. */ + u16_t *tty_inhead; /* pointer to place where next char goes */ + u16_t *tty_intail; /* pointer to next char to be given to prog */ + int tty_incount; /* # chars in the input queue */ + int tty_eotct; /* number of "line breaks" in input queue */ + devfun_t tty_devread; /* routine to read from low level buffers */ + devfun_t tty_icancel; /* cancel any device input */ + int tty_min; /* minimum requested #chars in input queue */ + timer_t tty_tmr; /* the timer for this tty */ + + /* Output section. */ + devfun_t tty_devwrite; /* routine to start actual device output */ + devfunarg_t tty_echo; /* routine to echo characters input */ + devfun_t tty_ocancel; /* cancel any ongoing device output */ + devfun_t tty_break; /* let the device send a break */ + + /* Terminal parameters and status. */ + int tty_position; /* current position on the screen for echoing */ + char tty_reprint; /* 1 when echoed input messed up, else 0 */ + char tty_escaped; /* 1 when LNEXT (^V) just seen, else 0 */ + char tty_inhibited; /* 1 when STOP (^S) just seen (stops output) */ + char tty_pgrp; /* slot number of controlling process */ + char tty_openct; /* count of number of opens of this tty */ + + /* Information about incomplete I/O requests is stored here. */ + char tty_inrepcode; /* reply code, TASK_REPLY or REVIVE */ + char tty_incaller; /* process that made the call (usually FS) */ + char tty_inproc; /* process that wants to read from tty */ + vir_bytes tty_in_vir; /* virtual address where data is to go */ + int tty_inleft; /* how many chars are still needed */ + int tty_incum; /* # chars input so far */ + int tty_instatus; /* status of last sys_vircopy() action */ + char tty_outrepcode; /* reply code, TASK_REPLY or REVIVE */ + char tty_outcaller; /* process that made the call (usually FS) */ + char tty_outproc; /* process that wants to write to tty */ + vir_bytes tty_out_vir; /* virtual address where data comes from */ + int tty_outleft; /* # chars yet to be output */ + int tty_outcum; /* # chars output so far */ + int tty_outstatus; /* status of last sys_vircopy() action */ + char tty_iocaller; /* process that made the call (usually FS) */ + char tty_ioproc; /* process that wants to do an ioctl */ + int tty_ioreq; /* ioctl request code */ + vir_bytes tty_iovir; /* virtual address of ioctl buffer */ + + /* Miscellaneous. */ + devfun_t tty_ioctl; /* set line speed, etc. at the device level */ + devfun_t tty_close; /* tell the device that the tty is closed */ + void *tty_priv; /* pointer to per device private data */ + struct termios tty_termios; /* terminal attributes */ + struct winsize tty_winsize; /* window size (#lines and #columns) */ + + u16_t tty_inbuf[TTY_IN_BYTES];/* tty input buffer */ +} tty_t; + +/* Memory allocated in tty.c, so extern here. */ +extern tty_t tty_table[NR_CONS+NR_RS_LINES+NR_PTYS]; +extern int ccurrent; /* currently visible console */ + +/* Values for the fields. */ +#define NOT_ESCAPED 0 /* previous character is not LNEXT (^V) */ +#define ESCAPED 1 /* previous character was LNEXT (^V) */ +#define RUNNING 0 /* no STOP (^S) has been typed to stop output */ +#define STOPPED 1 /* STOP (^S) has been typed to stop output */ + +/* Fields and flags on characters in the input queue. */ +#define IN_CHAR 0x00FF /* low 8 bits are the character itself */ +#define IN_LEN 0x0F00 /* length of char if it has been echoed */ +#define IN_LSHIFT 8 /* length = (c & IN_LEN) >> IN_LSHIFT */ +#define IN_EOT 0x1000 /* char is a line break (^D, LF) */ +#define IN_EOF 0x2000 /* char is EOF (^D), do not return to user */ +#define IN_ESC 0x4000 /* escaped by LNEXT (^V), no interpretation */ + +/* Times and timeouts. */ +#define force_timeout() ((void) (0)) + +/* Memory allocated in tty.c, so extern here. */ +extern timer_t *tty_timers; /* queue of TTY timers */ +extern clock_t tty_next_timeout; /* next TTY timeout */ + +/* Number of elements and limit of a buffer. */ +#define buflen(buf) (sizeof(buf) / sizeof((buf)[0])) +#define bufend(buf) ((buf) + buflen(buf)) + +/* Memory allocated in tty.c, so extern here. */ +extern struct kenviron kenv; /* kernel environment (a.o.: pc_at, ega) */ + + +/* Function prototypes for TTY driver. */ +/* tty.c */ +_PROTOTYPE( void handle_events, (struct tty *tp) ); +_PROTOTYPE( void sigchar, (struct tty *tp, int sig) ); +_PROTOTYPE( void tty_task, (void) ); +_PROTOTYPE( int in_process, (struct tty *tp, char *buf, int count) ); +_PROTOTYPE( void out_process, (struct tty *tp, char *bstart, char *bpos, + char *bend, int *icount, int *ocount) ); +_PROTOTYPE( void tty_wakeup, (clock_t now) ); +_PROTOTYPE( void tty_reply, (int code, int replyee, int proc_nr, + int status) ); +_PROTOTYPE( void tty_devnop, (struct tty *tp) ); + +/* rs232.c */ +_PROTOTYPE( void rs_init, (struct tty *tp) ); + +#if (CHIP == INTEL) +/* console.c */ +_PROTOTYPE( void kputc, (int c) ); +_PROTOTYPE( void cons_stop, (void) ); +_PROTOTYPE( void do_new_kmess, (message *m) ); +_PROTOTYPE( void do_diagnostics, (message *m) ); +_PROTOTYPE( void scr_init, (struct tty *tp) ); +_PROTOTYPE( void toggle_scroll, (void) ); +_PROTOTYPE( int con_loadfont, (message *m) ); +_PROTOTYPE( void select_console, (int cons_line) ); + +/* keyboard.c */ +_PROTOTYPE( void kb_init, (struct tty *tp) ); +_PROTOTYPE( int kbd_loadmap, (message *m) ); +_PROTOTYPE( void do_panic_dumps, (message *m) ); +_PROTOTYPE( void do_fkey_ctl, (message *m) ); +_PROTOTYPE( void do_interrupt, (message *m) ); + +/* pty.c */ +_PROTOTYPE( void do_pty, (struct tty *tp, message *m_ptr) ); +_PROTOTYPE( void pty_init, (struct tty *tp) ); + +/* vidcopy.s */ +_PROTOTYPE( void vid_vid_copy, (unsigned src, unsigned dst, unsigned count)); +_PROTOTYPE( void mem_vid_copy, (u16_t *src, unsigned dst, unsigned count)); + +#endif /* (CHIP == INTEL) */ + + diff --git a/drivers/tty/vidcopy.s b/drivers/tty/vidcopy.s new file mode 100644 index 000000000..7f970954c --- /dev/null +++ b/drivers/tty/vidcopy.s @@ -0,0 +1,160 @@ +# +! This file contains two specialized assembly code routines to update the +! video memory. The routines can copy from user to video memory, or from +! video to video memory. + +! sections + +.sect .text; .sect .rom; .sect .data; .sect .bss + +! exported functions + +.define _mem_vid_copy ! copy data to video ram +.define _vid_vid_copy ! move data in video ram + +! The routines only guarantee to preserve the registers the C compiler +! expects to be preserved (ebx, esi, edi, ebp, esp, segment registers, and +! direction bit in the flags). + +.sect .text +!*===========================================================================* +!* mem_vid_copy * +!*===========================================================================* +! PUBLIC void mem_vid_copy(u16 *src, unsigned dst, unsigned count); +! +! Copy count characters from kernel memory to video memory. Src is an ordinary +! pointer to a word, but dst and count are character (word) based video offset +! and count. If src is null then screen memory is blanked by filling it with +! blank_color. + +_mem_vid_copy: + push ebp + mov ebp, esp + push esi + push edi + push es + mov esi, 8(ebp) ! source + mov edi, 12(ebp) ! destination + mov edx, 16(ebp) ! count + mov es, (_vid_seg) ! segment containing video memory + cld ! make sure direction is up +mvc_loop: + and edi, (_vid_mask) ! wrap address + mov ecx, edx ! one chunk to copy + mov eax, (_vid_size) + sub eax, edi + cmp ecx, eax + jbe 0f + mov ecx, eax ! ecx = min(ecx, vid_size - edi) +0: sub edx, ecx ! count -= ecx + shl edi, 1 ! byte address + add edi, (_vid_off) ! in video memory + test esi, esi ! source == 0 means blank the screen + jz mvc_blank +mvc_copy: + rep ! copy words to video memory + o16 movs + jmp mvc_test +mvc_blank: + mov eax, (_blank_color) ! ax = blanking character + rep + o16 stos ! copy blanks to video memory + !jmp mvc_test +mvc_test: + sub edi, (_vid_off) + shr edi, 1 ! back to a word address + test edx, edx + jnz mvc_loop +mvc_done: + pop es + pop edi + pop esi + pop ebp + ret + + +!*===========================================================================* +!* vid_vid_copy * +!*===========================================================================* +! PUBLIC void vid_vid_copy(unsigned src, unsigned dst, unsigned count); +! +! Copy count characters from video memory to video memory. Handle overlap. +! Used for scrolling, line or character insertion and deletion. Src, dst +! and count are character (word) based video offsets and count. + +_vid_vid_copy: + push ebp + mov ebp, esp + push esi + push edi + push es + mov esi, 8(ebp) ! source + mov edi, 12(ebp) ! destination + mov edx, 16(ebp) ! count + mov es, (_vid_seg) ! segment containing video memory + cmp esi, edi ! copy up or down? + jb vvc_down +vvc_up: + cld ! direction is up +vvc_uploop: + and esi, (_vid_mask) ! wrap addresses + and edi, (_vid_mask) + mov ecx, edx ! one chunk to copy + mov eax, (_vid_size) + sub eax, esi + cmp ecx, eax + jbe 0f + mov ecx, eax ! ecx = min(ecx, vid_size - esi) +0: mov eax, (_vid_size) + sub eax, edi + cmp ecx, eax + jbe 0f + mov ecx, eax ! ecx = min(ecx, vid_size - edi) +0: sub edx, ecx ! count -= ecx + call vvc_copy ! copy video words + test edx, edx + jnz vvc_uploop ! again? + jmp vvc_done +vvc_down: + std ! direction is down + lea esi, -1(esi)(edx*1) ! start copying at the top + lea edi, -1(edi)(edx*1) +vvc_downloop: + and esi, (_vid_mask) ! wrap addresses + and edi, (_vid_mask) + mov ecx, edx ! one chunk to copy + lea eax, 1(esi) + cmp ecx, eax + jbe 0f + mov ecx, eax ! ecx = min(ecx, esi + 1) +0: lea eax, 1(edi) + cmp ecx, eax + jbe 0f + mov ecx, eax ! ecx = min(ecx, edi + 1) +0: sub edx, ecx ! count -= ecx + call vvc_copy + test edx, edx + jnz vvc_downloop ! again? + cld ! C compiler expects up + !jmp vvc_done +vvc_done: + pop es + pop edi + pop esi + pop ebp + ret + +! Copy video words. (Inner code of both the up and downcopying loop.) +vvc_copy: + shl esi, 1 + shl edi, 1 ! byte addresses + add esi, (_vid_off) + add edi, (_vid_off) ! in video memory + rep +eseg o16 movs ! copy video words + sub esi, (_vid_off) + sub edi, (_vid_off) + shr esi, 1 + shr edi, 1 ! back to word addresses + ret + diff --git a/etc/Makefile b/etc/Makefile new file mode 100644 index 000000000..c36a3a078 --- /dev/null +++ b/etc/Makefile @@ -0,0 +1,18 @@ + +ETC=/etc +FILES1=fstab group hostname.file inet.conf motd mtab passwd profile protocols rc services termcap ttytab utmp +FILES2=shadow + +all:: + +clean:: + +install:: + @echo "Installing /etc.." + mkdir -p $(ETC) + for f in $(FILES1); do if [ -f $(ETC)/$$f ]; then echo $$f exists; else cp $$f $(ETC); fi; done + for f in $(FILES2); do if [ -f $(ETC)/$$f ]; then echo $$f exists; else cp $$f $(ETC); chmod 600 $$f; fi; done + @echo "Making hierarchy.." + ./mtree.sh mtree/minix.tree + cd /dev && /usr/src/commands/scripts/MAKEDEV.sh std + (cd ast && tar cf - . ) | (cd /usr/ast && tar xf - ) diff --git a/etc/fstab b/etc/fstab new file mode 100755 index 000000000..8df5be80c --- /dev/null +++ b/etc/fstab @@ -0,0 +1,4 @@ +# Poor man's File System Table. + +root=/dev/ROOT +usr=/dev/USR diff --git a/etc/group b/etc/group new file mode 100755 index 000000000..c00afcfeb --- /dev/null +++ b/etc/group @@ -0,0 +1,11 @@ +operator:*:0: +daemon:*:1: +bin:*:2: +other:*:3: +tty:*:4: +uucp:*:5: +news:*:6: +ftp:*:7: +kmem:*:8: +www:*:9: +nogroup:*:99: diff --git a/etc/hostname.file b/etc/hostname.file new file mode 100755 index 000000000..eaa95e7d0 --- /dev/null +++ b/etc/hostname.file @@ -0,0 +1 @@ +noname diff --git a/etc/inet.conf b/etc/inet.conf new file mode 100755 index 000000000..3a30ce6bf --- /dev/null +++ b/etc/inet.conf @@ -0,0 +1 @@ +psip0 { default; }; diff --git a/etc/motd b/etc/motd new file mode 100755 index 000000000..e69de29bb diff --git a/etc/mtab b/etc/mtab new file mode 100755 index 000000000..e69de29bb diff --git a/etc/mtree.sh b/etc/mtree.sh new file mode 100755 index 000000000..786b2b503 --- /dev/null +++ b/etc/mtree.sh @@ -0,0 +1,4 @@ +#!/bin/sh +cat $1 | while read line +do awk '{ print "mkdir -p "$4" || exit 1; chmod "$1" "$4" || exit 1; chown "$2" "$4" || exit 1; chgrp "$3" "$4" || exit 1" }' | sh || exit 1 +done diff --git a/etc/mtree/minix.tree b/etc/mtree/minix.tree new file mode 100644 index 000000000..17671fc80 --- /dev/null +++ b/etc/mtree/minix.tree @@ -0,0 +1,62 @@ +755 root operator / +755 bin operator /bin +755 root operator /dev +755 root operator /etc +555 root operator /fd0 +755 root operator /lib +755 root operator /lib/i386 +755 bin operator /boot +555 root operator /mnt +555 root operator /root +1777 root operator /tmp +755 root operator /usr +755 root operator /usr/adm +755 root operator /usr/adm/old +755 ast other /usr/ast +755 bin operator /usr/bin +755 root operator /usr/etc +755 root operator /usr/include +755 root operator /usr/lib +755 root operator /usr/lib/advent +755 root operator /usr/lib/cawf +755 root operator /usr/lib/dict +755 root operator /usr/lib/ego +755 root operator /usr/lib/flex +755 root operator /usr/lib/fonts +755 root operator /usr/lib/i86 +755 root operator /usr/lib/keymaps +755 root operator /usr/lib/m2 +755 root operator /usr/sbin +755 root operator /usr/sbin/drivers +755 root operator /usr/sbin/servers +775 bin operator /usr/local +775 bin operator /usr/local/bin +775 bin operator /usr/local/include +755 root operator /usr/local/info +775 bin operator /usr/local/lib +775 bin operator /usr/local/man +755 root operator /usr/local/man/man1 +755 root operator /usr/local/man/man5 +755 root operator /usr/local/man/man8 +775 bin operator /usr/local/src +755 bin operator /usr/man +755 bin operator /usr/man/man1 +755 bin operator /usr/man/man2 +755 bin operator /usr/man/man3 +755 bin operator /usr/man/man4 +755 bin operator /usr/man/man5 +755 bin operator /usr/man/man6 +755 bin operator /usr/man/man7 +755 bin operator /usr/man/man8 +755 bin operator /usr/man/man9 +755 bin operator /usr/mdec +700 root operator /usr/preserve +755 root operator /usr/run +755 root operator /usr/spool +711 root operator /usr/spool/at +711 root operator /usr/spool/at/past +700 root operator /usr/spool/crontabs +775 root uucp /usr/spool/locks +700 daemon daemon /usr/spool/lpd +755 bin operator /usr/src +1777 root operator /usr/tmp diff --git a/etc/passwd b/etc/passwd new file mode 100755 index 000000000..2245ee63c --- /dev/null +++ b/etc/passwd @@ -0,0 +1,9 @@ +root:##root:0:0:Big Brother:/: +daemon:*:1:1:The Deuce:/etc: +bin:##root:2:0:Binaries:/usr/src: +uucp:*:5:5:UNIX to UNIX copy:/usr/spool/uucp:/usr/bin/uucico +news:*:6:6:Usenet news:/usr/spool/news: +ftp:*:7:7:Anonymous FTP:/usr/ftp: +ast:*:8:3:Andrew S. Tanenbaum:/usr/ast: +www:*:9:9:World Wide Web:/usr/www: +nobody:*:9999:99::/tmp: diff --git a/etc/profile b/etc/profile new file mode 100755 index 000000000..d9c074116 --- /dev/null +++ b/etc/profile @@ -0,0 +1,3 @@ +# Set timezone. + +TZ='GMT0' export TZ diff --git a/etc/protocols b/etc/protocols new file mode 100755 index 000000000..1726ef545 --- /dev/null +++ b/etc/protocols @@ -0,0 +1,19 @@ +# +# Internet (IP) protocols +# +# @(#)protocols 8.1 (Berkeley) 6/9/93 +# +ip 0 IP # internet protocol, pseudo protocol number +icmp 1 ICMP # internet control message protocol +igmp 2 IGMP # internet group management protocol +ggp 3 GGP # gateway-gateway protocol +tcp 6 TCP # transmission control protocol +egp 8 EGP # exterior gateway protocol +pup 12 PUP # PARC universal packet protocol +udp 17 UDP # user datagram protocol +hmp 20 HMP # host monitoring protocol +xns-idp 22 XNS-IDP # Xerox NS IDP +rdp 27 RDP # reliable data protocol +iso-tp4 29 ISO-TP4 # ISO Transport Protocol Class 4 +iso-ip 80 ISO-IP # ISO Internet Protocol +encap 98 ENCAP # RFC1241 encapsulation diff --git a/etc/rc b/etc/rc new file mode 100755 index 000000000..4c4f8c927 --- /dev/null +++ b/etc/rc @@ -0,0 +1,115 @@ +# /etc/rc - System startup script run by init before going multiuser. + +umask 022 +TERM="${TERM-minix}" +PATH=/usr/local/bin:/bin:/usr/bin +export TERM PATH + +usage() +{ + echo >&2 "Usage: $0 [-saf] start|stop|down" + exec intr sh +} + +while getopts 'saf' opt +do + case $opt in + s) sflag=t ;; # Single user + a) aflag=t ;; # Ask for /usr + f) fflag=t ;; # Force a full file system check + *) usage + esac +done +shift `expr $OPTIND - 1` + +case "$#:$1" in +1:start|1:stop|1:down) + action=$1 + ;; +*) usage +esac + +case $action in +start) + # National keyboard? + test -f /etc/keymap && loadkeys /etc/keymap + + # Set timezone. + . /etc/profile + + # Try to read the hardware real-time clock, otherwise do it manually. + readclock || intr date -q + + # Initialize files. + printroot >/etc/mtab # /etc/mtab keeps track of mounts + >/etc/utmp # /etc/utmp keeps track of logins + + # /etc/fstab lists the root, tmp and usr devices. + . /etc/fstab + + # Any swapspace on a device? + test "$swap" : '/dev/' && mount -s $swap + + # Mount the /usr partition unless this is a single floppy Minix. + if [ ! -d /usr/bin ] + then + if [ "$aflag" -o "$usr" = unknown ] + then + # We need to ask what the /usr du jour is. + intr sh -c ' + echo -n "Finish the name of device to mount as /usr: /dev/" + read usr + echo "usr=/dev/$usr" >/tmp/usr' + . /tmp/usr + fi + mount $usr /usr || { + echo "\ +Please try to mount something else as /usr, then hit CTRL-D to continue startup. +Mount $usr /usr failed -- Single user." + intr sh + } + rm -f /tmp/usr + fi + + # Check if the system crashed. + if shutdown -C + then + echo + echo "The system was not properly shut down. Checking file systems." + fflag=t + fi + + if [ "$fflag" ] + then + umount $usr + intr fsck -r $root + intr fsck -r $usr + mount $usr /usr + fi + + if [ "$sflag" ] + then + echo "Single user." + intr sh + fi + + # Any swapspace on a file? + test -n "$swap" -a ! "$swap" : '/dev/' && mount -s $swap + + case "`printroot -r`" in + /dev/ram) + # Remove boot-only things to make space. + rm -rf /boot /minix + esac + + # Things should be alright now. + echo;echo "Multiuser startup in progress." +esac + +# Further initialization. +test -f /usr/etc/rc && sh /usr/etc/rc $action +test -f /usr/local/etc/rc && sh /usr/local/etc/rc $action + +# Any messages? +test "$action" = start -a -f /etc/issue && cat /etc/issue +exit 0 diff --git a/etc/services b/etc/services new file mode 100755 index 000000000..ac14a0d86 --- /dev/null +++ b/etc/services @@ -0,0 +1,88 @@ +# +# Network services, Internet style +# +# @(#)services 8.1 (Berkeley) 6/9/93 +# +tcpmux 1/tcp # TCP port multiplexer (RFC1078) +echo 7/tcp +echo 7/udp +discard 9/tcp sink null +discard 9/udp sink null +systat 11/tcp users +daytime 13/tcp +daytime 13/udp +netstat 15/tcp +qotd 17/tcp quote +chargen 19/tcp ttytst source +chargen 19/udp ttytst source +ftp 21/tcp +telnet 23/tcp +smtp 25/tcp mail +time 37/tcp timserver +time 37/udp timserver +rlp 39/udp resource # resource location +nameserver 42/tcp name # IEN 116 +whois 43/tcp nicname +domain 53/tcp nameserver # name-domain server +domain 53/udp nameserver +mtp 57/tcp # deprecated +bootps 67/udp # Bootstrap Protocol Server +bootpc 68/udp # Bootstrap Protocol Client +tftp 69/udp +rje 77/tcp netrjs +finger 79/tcp +http 80/tcp # World Wide Web +link 87/tcp ttylink +supdup 95/tcp +hostnames 101/tcp hostname # usually from sri-nic +tsap 102/tcp # part of ISODE. +pop 110/tcp postoffice +sunrpc 111/tcp +sunrpc 111/udp +auth 113/tcp authentication +sftp 115/tcp +uucp-path 117/tcp +nntp 119/tcp readnews untp # USENET News Transfer Protocol +ntp 123/udp +netbios-ns 137/tcp # NETBIOS Name Service +netbios-ns 137/udp # NETBIOS Name Service +netbios-dgm 138/tcp # NETBIOS Datagram Service +netbios-dgm 138/udp # NETBIOS Datagram Service +netbios-ssn 139/tcp # NETBIOS Session Service +netbios-ssn 139/udp # NETBIOS Session Service +imap 143/tcp +snmp 161/udp +snmp-trap 162/udp +# +# UNIX specific services +# +exec 512/tcp +biff 512/udp comsat +login 513/tcp +who 513/udp whod +shell 514/tcp cmd # no passwords used +syslog 514/udp +printer 515/tcp spooler # line printer spooler +talk 517/udp +ntalk 518/udp +route 520/udp router routed +timed 525/udp timeserver +tempo 526/tcp newdate +courier 530/tcp rpc +conference 531/tcp chat +netnews 532/tcp readnews +netwall 533/udp # -for emergency broadcasts +uucp 540/tcp uucpd # uucp daemon +rdist 541/tcp rdistd # rdist daemon +remotefs 556/tcp rfs_server rfs # Brunhoff remote filesystem +ingreslock 1524/tcp +# +# Kerberos (Project Athena/MIT) services +# +kerberos 750/udp kdc # Kerberos (server) udp +kerberos 750/tcp kdc # Kerberos (server) tcp +krbupdate 760/tcp kreg # Kerberos registration +kpasswd 761/tcp kpwd # Kerberos "passwd" +klogin 543/tcp # Kerberos rlogin +eklogin 2105/tcp # Kerberos encrypted rlogin +kshell 544/tcp krcmd # Kerberos remote shell diff --git a/etc/shadow b/etc/shadow new file mode 100755 index 000000000..3895e9ec4 --- /dev/null +++ b/etc/shadow @@ -0,0 +1 @@ +root::0:0::: diff --git a/etc/termcap b/etc/termcap new file mode 100755 index 000000000..d2b25885e --- /dev/null +++ b/etc/termcap @@ -0,0 +1,60 @@ +mx|minix|minix console:\ + :am:xn:bs:\ + :co#80:li#25:\ + :is=\E[0m:\ + :cd=\E[0J:cl=\E[H\E[0J:\ + :so=\E[7m:se=\E[0m:\ + :us=\E[4m:ue=\E[0m:\ + :mb=\E[5m:md=\E[1m:\ + :mr=\E[7m:me=\E[0m:\ + :sr=\EM:\ + :cm=\E[%i%d;%dH:\ + :ho=\E[H:\ + :al=\E[L:AL=\E[%dL:\ + :ce=\E[K:\ + :DC=\E[%dP:dc=\E[P:\ + :DL=\E[%dM:dl=\E[M:\ + :DO=\E[%dB:do=\E[B:\ + :IC=\E[%d@:ic=\E[@:\ + :it#8:\ + :le=^H:LE=\E[%dD:\ + :nd=\E[C:RI=\E[%dC:\ + :up=\E[A:UP=\E[%dA:\ + :ku=\E[A:kd=\E[B:\ + :kl=\E[D:kr=\E[C:\ + :kh=\E[H:kb=^H:\ + :kD=\177:kI=\E[@:\ + :kN=\E[U:kP=\E[V: +du|dialup|Dialup line:\ + :bs:co#80:li#24: +db|dumb|Really dumb terminal:\ + :bs:co#80:li#24: +lp|lp|Line Printer:\ + :co#80:li#66: +li|ansi|Ansi standard crt:\ + :am:bs:cd=\E[J:ce=\E[K:cl=\E[2J\E[H:cm=\E[%i%d;%dH:co#80:\ + :dn=\E[B:me=\E[0m:mb=\E[5m:mr=\E[7m:md=\E[1m:ho=\E[H:li#24:\ + :nd=\E[C:ms:pt:so=\E[7m:se=\E[0m:us=\E[4m:ue=\E[0m:up=\E[A:\ + :kb=^h:ku=\E[A:kd=\E[B:kl=\E[D:kr=\E[C: +vs|xterm|xterms|vs100|xterm terminal emulator (X window system):\ + :am:cr=^M:do=^J:nl=^J:bl=^G:le=^H:ho=\E[H:\ + :co#80:li#24:cl=\E[H\E[2J:bs:cm=\E[%i%d;%dH:nd=\E[C:up=\E[A:\ + :ce=\E[K:cd=\E[J:so=\E[7m:se=\E[m:us=\E[4m:ue=\E[m:\ + :md=\E[1m:mr=\E[7m:me=\E[m:\ + :ku=\E[A:kd=\E[B:kr=\E[C:kl=\E[D:kb=^H:kD=\177:\ + :kI=\E[@:kN=\E[U:kP=\E[V:ta=^I:pt:sf=\n:sr=\EM:\ + :al=\E[L:dl=\E[M:ic=\E[@:dc=\E[P:\ + :MT:ks=\E[?1h\E=:ke=\E[?1l\E>:\ + :is=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l:\ + :rs=\E[r\E<\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l:xn:\ + :AL=\E[%dL:DL=\E[%dM:IC=\E[%d@:DC=\E[%dP:\ + :hs:ts=\E]2;:fs=^G:ds=\E]2;^G: +d0|vt100|vt100-am|vt100am|dec-vt100|dec vt100:\ + :do=^J:co#80:li#24:cl=\E[;H\E[2J:sf=\ED:\ + :le=^H:bs:am:cm=\E[%i%d;%dH:nd=\E[C:up=\E[A:\ + :ce=\E[K:cd=\E[J:so=\E[7m:se=\E[m:us=\E[4m:ue=\E[m:\ + :md=\E[1m:mr=\E[7m:mb=\E[5m:me=\E[m:is=\E[1;24r\E[24;1H:\ + :rs=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h:ks=\E[?1h\E=:ke=\E[?1l\E>:\ + :ku=\EOA:kd=\EOB:kr=\EOC:kl=\EOD:kb=^H:\ + :ho=\E[H:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:pt:sr=\EM:vt#3:xn:\ + :sc=\E7:rc=\E8:cs=\E[%i%d;%dr: diff --git a/etc/ttytab b/etc/ttytab new file mode 100755 index 000000000..d7e67d984 --- /dev/null +++ b/etc/ttytab @@ -0,0 +1,19 @@ +# ttytab - terminals +# +# Device Type Program Init +console minix getty +ttyc1 minix getty +ttyc2 minix getty +ttyc3 minix getty +tty00 unknown +tty01 unknown +ttyp0 network +ttyp1 network +ttyp2 network +ttyp3 network +ttyp4 network +ttyp5 network +ttyp6 network +ttyp7 network +ttyp8 network +ttyp9 network diff --git a/etc/utmp b/etc/utmp new file mode 100755 index 000000000..e69de29bb diff --git a/include/Makefile b/include/Makefile new file mode 100644 index 000000000..efb8c088c --- /dev/null +++ b/include/Makefile @@ -0,0 +1,11 @@ + +INC=/usr/include + +all:: + +clean:: + +install:: + -rm -rf $(INC) + mkdir $(INC) + tar cf - `find . -name '*.h'` | (cd $(INC) && tar xf -) diff --git a/include/a.out.h b/include/a.out.h new file mode 100755 index 000000000..0499a1f10 --- /dev/null +++ b/include/a.out.h @@ -0,0 +1,118 @@ +/* The <a.out> header file describes the format of executable files. */ + +#ifndef _AOUT_H +#define _AOUT_H + +struct exec { /* a.out header */ + unsigned char a_magic[2]; /* magic number */ + unsigned char a_flags; /* flags, see below */ + unsigned char a_cpu; /* cpu id */ + unsigned char a_hdrlen; /* length of header */ + unsigned char a_unused; /* reserved for future use */ + unsigned short a_version; /* version stamp (not used at present) */ + long a_text; /* size of text segement in bytes */ + long a_data; /* size of data segment in bytes */ + long a_bss; /* size of bss segment in bytes */ + long a_entry; /* entry point */ + long a_total; /* total memory allocated */ + long a_syms; /* size of symbol table */ + + /* SHORT FORM ENDS HERE */ + long a_trsize; /* text relocation size */ + long a_drsize; /* data relocation size */ + long a_tbase; /* text relocation base */ + long a_dbase; /* data relocation base */ +}; + +#define A_MAGIC0 (unsigned char) 0x01 +#define A_MAGIC1 (unsigned char) 0x03 +#define BADMAG(X) ((X).a_magic[0] != A_MAGIC0 ||(X).a_magic[1] != A_MAGIC1) + +/* CPU Id of TARGET machine (byte order coded in low order two bits) */ +#define A_NONE 0x00 /* unknown */ +#define A_I8086 0x04 /* intel i8086/8088 */ +#define A_M68K 0x0B /* motorola m68000 */ +#define A_NS16K 0x0C /* national semiconductor 16032 */ +#define A_I80386 0x10 /* intel i80386 */ +#define A_SPARC 0x17 /* Sun SPARC */ + +#define A_BLR(cputype) ((cputype&0x01)!=0) /* TRUE if bytes left-to-right */ +#define A_WLR(cputype) ((cputype&0x02)!=0) /* TRUE if words left-to-right */ + +/* Flags. */ +#define A_UZP 0x01 /* unmapped zero page (pages) */ +#define A_PAL 0x02 /* page aligned executable */ +#define A_NSYM 0x04 /* new style symbol table */ +#define A_IMG 0x08 /* image instead of executable (e.g. root FS) */ +#define A_EXEC 0x10 /* executable */ +#define A_SEP 0x20 /* separate I/D */ +#define A_PURE 0x40 /* pure text */ /* not used */ +#define A_TOVLY 0x80 /* text overlay */ /* not used */ + +/* Offsets of various things. */ +#define A_MINHDR 32 +#define A_TEXTPOS(X) ((long)(X).a_hdrlen) +#define A_DATAPOS(X) (A_TEXTPOS(X) + (X).a_text) +#define A_HASRELS(X) ((X).a_hdrlen > (unsigned char) A_MINHDR) +#define A_HASEXT(X) ((X).a_hdrlen > (unsigned char) (A_MINHDR + 8)) +#define A_HASLNS(X) ((X).a_hdrlen > (unsigned char) (A_MINHDR + 16)) +#define A_HASTOFF(X) ((X).a_hdrlen > (unsigned char) (A_MINHDR + 24)) +#define A_TRELPOS(X) (A_DATAPOS(X) + (X).a_data) +#define A_DRELPOS(X) (A_TRELPOS(X) + (X).a_trsize) +#define A_SYMPOS(X) (A_TRELPOS(X) + (A_HASRELS(X) ? \ + ((X).a_trsize + (X).a_drsize) : 0)) + +struct reloc { + long r_vaddr; /* virtual address of reference */ + unsigned short r_symndx; /* internal segnum or extern symbol num */ + unsigned short r_type; /* relocation type */ +}; + +/* r_tyep values: */ +#define R_ABBS 0 +#define R_RELLBYTE 2 +#define R_PCRBYTE 3 +#define R_RELWORD 4 +#define R_PCRWORD 5 +#define R_RELLONG 6 +#define R_PCRLONG 7 +#define R_REL3BYTE 8 +#define R_KBRANCHE 9 + +/* r_symndx for internal segments */ +#define S_ABS ((unsigned short)-1) +#define S_TEXT ((unsigned short)-2) +#define S_DATA ((unsigned short)-3) +#define S_BSS ((unsigned short)-4) + +struct nlist { /* symbol table entry */ + char n_name[8]; /* symbol name */ + long n_value; /* value */ + unsigned char n_sclass; /* storage class */ + unsigned char n_numaux; /* number of auxiliary entries (not used) */ + unsigned short n_type; /* language base and derived type (not used) */ +}; + +/* Low bits of storage class (section). */ +#define N_SECT 07 /* section mask */ +#define N_UNDF 00 /* undefined */ +#define N_ABS 01 /* absolute */ +#define N_TEXT 02 /* text */ +#define N_DATA 03 /* data */ +#define N_BSS 04 /* bss */ +#define N_COMM 05 /* (common) */ + +/* High bits of storage class. */ +#define N_CLASS 0370 /* storage class mask */ +#define C_NULL +#define C_EXT 0020 /* external symbol */ +#define C_STAT 0030 /* static */ + +/* Function prototypes. */ +#ifndef _ANSI_H +#include <ansi.h> +#endif + +_PROTOTYPE( int nlist, (char *_file, struct nlist *_nl) ); + +#endif /* _AOUT_H */ diff --git a/include/alloca.h b/include/alloca.h new file mode 100755 index 000000000..f90b6e6c8 --- /dev/null +++ b/include/alloca.h @@ -0,0 +1,24 @@ +/* alloca.h - The dreaded alloca() function. + */ + +#ifndef _ALLOCA_H +#define _ALLOCA_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +#if __GNUC__ + +/* The compiler recognizes this special keyword, and inlines the code. */ +#define alloca(size) __builtin_alloca(size) + +#endif /* __GCC__ */ + +#if __ACK__ || __CCC__ + +_PROTOTYPE(void *alloca, (size_t _size) ); + +#endif /* __ACK__ || __CCC__ */ + +#endif /* _ALLOCA_H */ diff --git a/include/ansi.h b/include/ansi.h new file mode 100755 index 000000000..1e4eac3e1 --- /dev/null +++ b/include/ansi.h @@ -0,0 +1,66 @@ +/* The <ansi.h> header attempts to decide whether the compiler has enough + * conformance to Standard C for Minix to take advantage of. If so, the + * symbol _ANSI is defined (as 31415). Otherwise _ANSI is not defined + * here, but it may be defined by applications that want to bend the rules. + * The magic number in the definition is to inhibit unnecessary bending + * of the rules. (For consistency with the new '#ifdef _ANSI" tests in + * the headers, _ANSI should really be defined as nothing, but that would + * break many library routines that use "#if _ANSI".) + + * If _ANSI ends up being defined, a macro + * + * _PROTOTYPE(function, params) + * + * is defined. This macro expands in different ways, generating either + * ANSI Standard C prototypes or old-style K&R (Kernighan & Ritchie) + * prototypes, as needed. Finally, some programs use _CONST, _VOIDSTAR etc + * in such a way that they are portable over both ANSI and K&R compilers. + * The appropriate macros are defined here. + */ + +#ifndef _ANSI_H +#define _ANSI_H + +#if __STDC__ == 1 +#define _ANSI 31459 /* compiler claims full ANSI conformance */ +#endif + +#ifdef __GNUC__ +#define _ANSI 31459 /* gcc conforms enough even in non-ANSI mode */ +#endif + +#ifdef _ANSI + +/* Keep everything for ANSI prototypes. */ +#define _PROTOTYPE(function, params) function params +#define _ARGS(params) params + +#define _VOIDSTAR void * +#define _VOID void +#define _CONST const +#define _VOLATILE volatile +#define _SIZET size_t + +#else + +/* Throw away the parameters for K&R prototypes. */ +#define _PROTOTYPE(function, params) function() +#define _ARGS(params) () + +#define _VOIDSTAR void * +#define _VOID void +#define _CONST +#define _VOLATILE +#define _SIZET int + +#endif /* _ANSI */ + +/* Setting any of _MINIX, _POSIX_C_SOURCE or _POSIX2_SOURCE implies + * _POSIX_SOURCE. (Seems wrong to put this here in ANSI space.) + */ +#if defined(_MINIX) || _POSIX_C_SOURCE > 0 || defined(_POSIX2_SOURCE) +#undef _POSIX_SOURCE +#define _POSIX_SOURCE 1 +#endif + +#endif /* ANSI_H */ diff --git a/include/assert.h b/include/assert.h new file mode 100755 index 000000000..eed8fbda4 --- /dev/null +++ b/include/assert.h @@ -0,0 +1,40 @@ +/* The <assert.h> header contains a macro called "assert" that allows + * programmers to put assertions in the code. These assertions can be verified + * at run time. If an assertion fails, an error message is printed and the + * program aborts. + * Assertion checking can be disabled by adding the statement + * + * #define NDEBUG + * + * to the program before the + * + * #include <assert.h> + * + * statement. + */ + +#undef assert + +#ifndef _ANSI_H +#include <ansi.h> +#endif + + +#ifdef NDEBUG +/* Debugging disabled -- do not evaluate assertions. */ +#define assert(expr) ((void) 0) +#else +/* Debugging enabled -- verify assertions at run time. */ +#ifdef _ANSI +#define __str(x) # x +#define __xstr(x) __str(x) + +_PROTOTYPE( void __bad_assertion, (const char *_mess) ); +#define assert(expr) ((expr)? (void)0 : \ + __bad_assertion("Assertion \"" #expr \ + "\" failed, file " __xstr(__FILE__) \ + ", line " __xstr(__LINE__) "\n")) +#else +#define assert(expr) ((void) ((expr) ? 0 : __assert( __FILE__, __LINE__))) +#endif /* _ANSI */ +#endif diff --git a/include/configfile.h b/include/configfile.h new file mode 100755 index 000000000..f3801807b --- /dev/null +++ b/include/configfile.h @@ -0,0 +1,44 @@ +/* configfile.h - Generic configuration file format. + * Author: Kees J. Bot + * 5 Jun 1999 + */ +#ifndef _CONFIGFILE_H +#define _CONFIGFILE_H + +/* Data can only be modified inside the library. */ +#ifndef _c +#define _c const +#endif + +typedef _c struct config { /* Contents of a generic configuration file. */ +_c struct config *next; /* Next configuration file thing. */ +_c struct config *list; /* For a { sublist }. */ + const char *file; /* File and line where this is found. */ + unsigned line; + int flags; /* Special flags. */ + char word[1]; /* Payload. */ +} config_t; + +#define CFG_CLONG 0x0001 /* strtol(word, &end, 0) is valid. */ +#define CFG_OLONG 0x0002 /* strtol(word, &end, 010). */ +#define CFG_DLONG 0x0004 /* strtol(word, &end, 10). */ +#define CFG_XLONG 0x0008 /* strtol(word, &end, 0x10). */ +#define CFG_CULONG 0x0010 /* strtoul(word, &end, 0). */ +#define CFG_OULONG 0x0020 /* strtoul(word, &end, 010). */ +#define CFG_DULONG 0x0040 /* strtoul(word, &end, 10). */ +#define CFG_XULONG 0x0080 /* strtoul(word, &end, 0x10). */ +#define CFG_STRING 0x0100 /* The word is enclosed in quotes. */ +#define CFG_SUBLIST 0x0200 /* This is a sublist, so no word. */ +#define CFG_ESCAPED 0x0400 /* Escapes are still marked with \. */ + +config_t *config_read(const char *_file, int flags, config_t *_cfg); +void config_delete(config_t *_cfg); +int config_renewed(config_t *_cfg); +size_t config_length(config_t *_cfg); +#define config_issub(cfg) (!!((cfg)->flags & CFG_SUBLIST)) +#define config_isatom(cfg) (!config_issub(cfg)) +#define config_isstring(cfg) (!!((cfg)->flags & CFG_STRING)) + +#undef _c + +#endif /* _CONFIGFILE_H */ diff --git a/include/ctype.h b/include/ctype.h new file mode 100755 index 000000000..2467557ab --- /dev/null +++ b/include/ctype.h @@ -0,0 +1,54 @@ +/* The <ctype.h> header file defines some macros used to identify characters. + * It works by using a table stored in chartab.c. When a character is presented + * to one of these macros, the character is used as an index into the table + * (__ctype) to retrieve a byte. The relevant bit is then extracted. + */ + +#ifndef _CTYPE_H +#define _CTYPE_H + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +extern char __ctype[]; /* property array defined in chartab.c */ + +#define _U 0x01 /* this bit is for upper-case letters [A-Z] */ +#define _L 0x02 /* this bit is for lower-case letters [a-z] */ +#define _N 0x04 /* this bit is for numbers [0-9] */ +#define _S 0x08 /* this bit is for white space \t \n \f etc */ +#define _P 0x10 /* this bit is for punctuation characters */ +#define _C 0x20 /* this bit is for control characters */ +#define _X 0x40 /* this bit is for hex digits [a-f] and [A-F]*/ + +/* Function Prototypes (have to go before the macros). */ +_PROTOTYPE( int isalnum, (int _c) ); /* alphanumeric [a-z], [A-Z], [0-9] */ +_PROTOTYPE( int isalpha, (int _c) ); /* alphabetic */ +_PROTOTYPE( int iscntrl, (int _c) ); /* control characters */ +_PROTOTYPE( int isdigit, (int _c) ); /* digit [0-9] */ +_PROTOTYPE( int isgraph, (int _c) ); /* graphic character */ +_PROTOTYPE( int islower, (int _c) ); /* lower-case letter [a-z] */ +_PROTOTYPE( int isprint, (int _c) ); /* printable character */ +_PROTOTYPE( int ispunct, (int _c) ); /* punctuation mark */ +_PROTOTYPE( int isspace, (int _c) ); /* white space sp, \f, \n, \r, \t, \v*/ +_PROTOTYPE( int isupper, (int _c) ); /* upper-case letter [A-Z] */ +_PROTOTYPE( int isxdigit,(int _c) ); /* hex digit [0-9], [a-f], [A-F] */ +_PROTOTYPE( int tolower, (int _c) ); /* convert to lower-case */ +_PROTOTYPE( int toupper, (int _c) ); /* convert to upper-case */ + +/* Macros for identifying character classes. */ +#define isalnum(c) ((__ctype+1)[c]&(_U|_L|_N)) +#define isalpha(c) ((__ctype+1)[c]&(_U|_L)) +#define iscntrl(c) ((__ctype+1)[c]&_C) +#define isgraph(c) ((__ctype+1)[c]&(_P|_U|_L|_N)) +#define ispunct(c) ((__ctype+1)[c]&_P) +#define isspace(c) ((__ctype+1)[c]&_S) +#define isxdigit(c) ((__ctype+1)[c]&(_N|_X)) + +#define isdigit(c) ((unsigned) ((c)-'0') < 10) +#define islower(c) ((unsigned) ((c)-'a') < 26) +#define isupper(c) ((unsigned) ((c)-'A') < 26) +#define isprint(c) ((unsigned) ((c)-' ') < 95) +#define isascii(c) ((unsigned) (c) < 128) + +#endif /* _CTYPE_H */ diff --git a/include/curses.h b/include/curses.h new file mode 100755 index 000000000..af1b0b329 --- /dev/null +++ b/include/curses.h @@ -0,0 +1,226 @@ +/* curses.h - defines macros and prototypes for curses */ + +#ifndef _CURSES_H +#define _CURSES_H + +#include <termios.h> +#include <stdarg.h> +#include <stdio.h> + +typedef int bool; + +#ifndef TRUE +#define TRUE 1 +#endif +#ifndef FALSE +#define FALSE 0 +#endif +#ifndef ERR +#define ERR (-1) /* general error flag */ +#endif +#ifndef OK +#define OK 0 /* general OK flag */ +#endif + +/* Macros. */ +#define box(win,vc,hc) wbox(win,0,0,0,0,vc,hc) +#define addch(ch) waddch(stdscr,ch) +#define mvaddch(y,x,ch) (wmove(stdscr,y,x)==ERR?ERR:waddch(stdscr,ch)) +#define mvwaddch(win,y,x,ch) (wmove(win,y,x)==ERR?ERR:waddch(win,ch)) +#define getch() wgetch(stdscr) +#define mvgetch(y,x) (wmove(stdscr,y,x)==ERR?ERR:wgetch(stdscr)) +#define mvwgetch(win,y,x) (wmove(win,y,x)==ERR?ERR:wgetch(win)) +#define addstr(str) waddstr(stdscr,str) +#define mvaddstr(y,x,str) (wmove(stdscr,y,x)==ERR?ERR:waddstr(stdscr,str)) +#define mvwaddstr(win,y,x,str) (wmove(win,y,x)==ERR?ERR:waddstr(win,str)) +#define getstr(str) wgetstr(stdscr,str) +#define mvgetstr(y,x,str) (wmove(stdscr,y,x)==ERR?ERR:wgetstr(stdscr,str)) +#define mvwgetstr(win,y,x,str) (wmove(win,y,x)==ERR?ERR:wgetstr(win,str)) +#define move(y,x) wmove(stdscr,y,x) +#define clear() wclear(stdscr) +#define erase() werase(stdscr) +#define clrtobot() wclrtobot(stdscr) +#define mvclrtobot(y,x) (wmove(stdscr,y,x)==ERR?ERR:wclrtobot(stdscr)) +#define mvwclrtobot(win,y,x) (wmove(win,y,x)==ERR?ERR:wclrtobot(win)) +#define clrtoeol() wclrtoeol(stdscr) +#define mvclrtoeol(y,x) (wmove(stdscr,y,x)==ERR?ERR:wclrtoeol(stdscr)) +#define mvwclrtoeol(win,y,x) (wmove(win,y,x)==ERR?ERR:wclrtoeol(win)) +#define insertln() winsertln(stdscr) +#define mvinsertln(y,x) (wmove(stdscr,y,x)==ERR?ERR:winsertln(stdscr)) +#define mvwinsertln(win,y,x) (wmove(win,y,x)==ERR?ERR:winsertln(win)) +#define deleteln() wdeleteln(stdscr) +#define mvdeleteln(y,x) (wmove(stdscr,y,x)==ERR?ERR:wdeleteln(stdscr)) +#define mvwdeleteln(win,y,x) (wmove(win,y,x)==ERR?ERR:wdeleteln(win)) +#define refresh() wrefresh(stdscr) +#define inch() winch(stdscr) +#define insch(ch) winsch(stdscr,ch) +#define mvinsch(y,x,ch) (wmove(stdscr,y,x)==ERR?ERR:winsch(stdscr,ch)) +#define mvwinsch(win,y,x,ch) (wmove(win,y,x)==ERR?ERR:winsch(win,ch)) +#define delch() wdelch(stdscr) +#define mvdelch(y,x) (wmove(stdscr,y,x)==ERR?ERR:wdelch(stdscr)) +#define mvwdelch(win,y,x) (wmove(win,y,x)==ERR?ERR:wdelch(win)) +#define standout() wstandout(stdscr) +#define wstandout(win) ((win)->_attrs |= A_STANDOUT) +#define standend() wstandend(stdscr) +#define wstandend(win) ((win)->_attrs &= ~A_STANDOUT) +#define attrset(attrs) wattrset(stdscr, attrs) +#define wattrset(win, attrs) ((win)->_attrs = (attrs)) +#define attron(attrs) wattron(stdscr, attrs) +#define wattron(win, attrs) ((win)->_attrs |= (attrs)) +#define attroff(attrs) wattroff(stdscr,attrs) +#define wattroff(win, attrs) ((win)->_attrs &= ~(attrs)) +#define resetty() tcsetattr(1, TCSANOW, &_orig_tty) +#define getyx(win,y,x) (y = (win)->_cury, x = (win)->_curx) + +/* Video attribute definitions. */ +#define A_BLINK 0x0100 +#define A_BLANK 0 +#define A_BOLD 0x0200 +#define A_DIM 0 +#define A_PROTECT 0 +#define A_REVERSE 0x0400 +#define A_STANDOUT 0x0800 +#define A_UNDERLINE 0x1000 +#define A_ALTCHARSET 0x2000 + +/* Type declarations. */ +typedef struct { + int _cury; /* current pseudo-cursor */ + int _curx; + int _maxy; /* max coordinates */ + int _maxx; + int _begy; /* origin on screen */ + int _begx; + int _flags; /* window properties */ + int _attrs; /* attributes of written characters */ + int _tabsize; /* tab character size */ + bool _clear; /* causes clear at next refresh */ + bool _leave; /* leaves cursor as it happens */ + bool _scroll; /* allows window scrolling */ + bool _nodelay; /* input character wait flag */ + bool _keypad; /* flags keypad key mode active */ + int **_line; /* pointer to line pointer array */ + int *_minchng; /* First changed character in line */ + int *_maxchng; /* Last changed character in line */ + int _regtop; /* Top/bottom of scrolling region */ + int _regbottom; +} WINDOW; + +/* External variables */ +extern int LINES; /* terminal height */ +extern int COLS; /* terminal width */ +extern bool NONL; /* \n causes CR too ? */ +extern WINDOW *curscr; /* the current screen image */ +extern WINDOW *stdscr; /* the default screen window */ +extern struct termios _orig_tty, _tty; + +extern unsigned int ACS_ULCORNER; /* terminal dependent block grafic */ +extern unsigned int ACS_LLCORNER; /* charcters. Forget IBM, we are */ +extern unsigned int ACS_URCORNER; /* independent of their charset. :-) */ +extern unsigned int ACS_LRCORNER; +extern unsigned int ACS_RTEE; +extern unsigned int ACS_LTEE; +extern unsigned int ACS_BTEE; +extern unsigned int ACS_TTEE; +extern unsigned int ACS_HLINE; +extern unsigned int ACS_VLINE; +extern unsigned int ACS_PLUS; +extern unsigned int ACS_S1; +extern unsigned int ACS_S9; +extern unsigned int ACS_DIAMOND; +extern unsigned int ACS_CKBOARD; +extern unsigned int ACS_DEGREE; +extern unsigned int ACS_PLMINUS; +extern unsigned int ACS_BULLET; +extern unsigned int ACS_LARROW; +extern unsigned int ACS_RARROW; +extern unsigned int ACS_DARROW; +extern unsigned int ACS_UARROW; +extern unsigned int ACS_BOARD; +extern unsigned int ACS_LANTERN; +extern unsigned int ACS_BLOCK; + +_PROTOTYPE( char *unctrl, (int _c) ); +_PROTOTYPE( int baudrate, (void)); +_PROTOTYPE( void beep, (void)); +_PROTOTYPE( void cbreak, (void)); +_PROTOTYPE( void clearok, (WINDOW *_win, bool _flag) ); +_PROTOTYPE( void clrscr, (void)); +_PROTOTYPE( void curs_set, (int _visibility) ); +_PROTOTYPE( void delwin, (WINDOW *_win) ); +_PROTOTYPE( void doupdate, (void)); +_PROTOTYPE( void echo, (void)); +_PROTOTYPE( int endwin, (void)); +_PROTOTYPE( int erasechar, (void)); +_PROTOTYPE( void fatal, (char *_s) ); +_PROTOTYPE( int fixterm, (void)); +_PROTOTYPE( void flash, (void)); +_PROTOTYPE( void gettmode, (void)); +_PROTOTYPE( void idlok, (WINDOW *_win, bool _flag) ); +_PROTOTYPE( WINDOW *initscr, (void)); +_PROTOTYPE( void keypad, (WINDOW *_win, bool _flag) ); +_PROTOTYPE( int killchar, (void)); +_PROTOTYPE( void leaveok, (WINDOW *_win, bool _flag) ); +_PROTOTYPE( char *longname, (void)); +_PROTOTYPE( void meta, (WINDOW *_win, bool _flag) ); +_PROTOTYPE( int mvcur, (int _oldy, int _oldx, int _newy, int _newx) ); +_PROTOTYPE( int mvinch, (int _y, int _x) ); +_PROTOTYPE( int mvprintw, (int _y, int _x, const char *_fmt, ...) ); +_PROTOTYPE( int mvscanw, (int _y, int _x, const char *_fmt, ...) ); +_PROTOTYPE( int mvwin, (WINDOW *_win, int _begy, int _begx) ); +_PROTOTYPE( int mvwinch, (WINDOW *_win, int _y, int _x) ); +_PROTOTYPE( int mvwprintw, (WINDOW *_win, int _y, int _x, const char *_fmt, + ...) ); +_PROTOTYPE( int mvwscanw, (WINDOW *_win, int _y, int _x, const char *_fmt, + ...) ); +_PROTOTYPE( WINDOW *newwin, (int _num_lines, int _num_cols, int _y, int _x)); +_PROTOTYPE( void nl, (void)); +_PROTOTYPE( void nocbreak, (void)); +_PROTOTYPE( void nodelay, (WINDOW *_win, bool _flag) ); +_PROTOTYPE( void noecho, (void)); +_PROTOTYPE( void nonl, (void)); +_PROTOTYPE( void noraw, (void)); +_PROTOTYPE( void outc, (int _c) ); +_PROTOTYPE( void overlay, (WINDOW *_win1, WINDOW *_win2) ); +_PROTOTYPE( void overwrite, (WINDOW *_win1, WINDOW *_win2) ); +_PROTOTYPE( void poscur, (int _r, int _c) ); +_PROTOTYPE( int printw, (const char *_fmt, ...) ); +_PROTOTYPE( void raw, (void)); +_PROTOTYPE( int resetterm, (void)); +_PROTOTYPE( int saveoldterm, (void)); +_PROTOTYPE( int saveterm, (void)); +_PROTOTYPE( int savetty, (void)); +_PROTOTYPE( int scanw, (const char *_fmt, ...) ); +_PROTOTYPE( void scroll, (WINDOW *_win) ); +_PROTOTYPE( void scrollok, (WINDOW *_win, bool _flag) ); +_PROTOTYPE( int setscrreg, (int _top, int _bottom) ); +_PROTOTYPE( int setterm, (char *_type) ); +_PROTOTYPE( int setupterm, (void)); +_PROTOTYPE( WINDOW *subwin, (WINDOW *_orig, int _nlines, int _ncols, int _y, + int _x)); +_PROTOTYPE( int tabsize, (int _ts) ); +_PROTOTYPE( void touchwin, (WINDOW *_win) ); +_PROTOTYPE( int waddch, (WINDOW *_win, int _c) ); +_PROTOTYPE( int waddstr, (WINDOW *_win, char *_str) ); +_PROTOTYPE( int wbox, (WINDOW *_win, int _ymin, int _xmin, int _ymax, + int _xmax, unsigned int _v, unsigned int _h) ); +_PROTOTYPE( void wclear, (WINDOW *_win) ); +_PROTOTYPE( int wclrtobot, (WINDOW *_win) ); +_PROTOTYPE( int wclrtoeol, (WINDOW *_win) ); +_PROTOTYPE( int wdelch, (WINDOW *_win) ); +_PROTOTYPE( int wdeleteln, (WINDOW *_win) ); +_PROTOTYPE( void werase, (WINDOW *_win) ); +_PROTOTYPE( int wgetch, (WINDOW *_win) ); +_PROTOTYPE( int wgetstr, (WINDOW *_win, char *_str) ); +_PROTOTYPE( int winch, (WINDOW *_win) ); +_PROTOTYPE( int winsch, (WINDOW *_win, int _c) ); +_PROTOTYPE( int winsertln, (WINDOW *_win) ); +_PROTOTYPE( int wmove, (WINDOW *_win, int _y, int _x) ); +_PROTOTYPE( void wnoutrefresh, (WINDOW *_win) ); +_PROTOTYPE( int wprintw, (WINDOW *_win, const char *_fmt, ...)); +_PROTOTYPE( void wrefresh, (WINDOW *_win) ); +_PROTOTYPE( int wscanw, (WINDOW *_win, const char *_fmt, ...)); +_PROTOTYPE( int wsetscrreg, (WINDOW *_win, int _top, int _bottom) ); +_PROTOTYPE( int wtabsize, (WINDOW *_win, int _ts) ); + +#endif /* _CURSES_H */ diff --git a/include/dirent.h b/include/dirent.h new file mode 100755 index 000000000..304eed8dc --- /dev/null +++ b/include/dirent.h @@ -0,0 +1,81 @@ +/* dirent.h - Declarations for directory reading routines. + * Author: Kees J. Bot + * 24 Apr 1989 + * + * Note: The V7 format directory entries used under Minix must be transformed + * into a struct dirent with a d_name of at least 15 characters. Given that + * we have to transform V7 entries anyhow it is little trouble to let the + * routines understand the so-called "flex" directory format too. + */ + +#ifndef _DIRENT_H +#define _DIRENT_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +#include <sys/dir.h> + +/* _fl_direct is a flexible directory entry. Actually it's a union of 8 + * characters and the 3 fields defined below. + */ + +/* Flexible directory entry: */ +struct _fl_direct { /* First slot in an entry */ + ino_t d_ino; + unsigned char d_extent; + char d_name[3]; /* two characters for the shortest name */ +}; + + /* Name of length len needs _EXTENT(len) extra slots. */ +#define _EXTENT(len) (((len) + 5) >> 3) + +/* Version 7 directory entry: */ +struct _v7_direct { + ino_t d_ino; + char d_name[DIRSIZ]; +}; + +/* The block size must be at least 1024 bytes, because otherwise + * the superblock (at 1024 bytes) overlaps with other filesystem data. + */ +#define MIN_BLOCK_SIZE 1024 +#define MAX_BLOCK_SIZE 8192 + +/* This is the block size for the fixed versions of the filesystem (V1/V2) */ +#define STATIC_BLOCK_SIZE 1024 + +#define STATIC_FLEX_PER_BLOCK (STATIC_BLOCK_SIZE/sizeof(struct _fl_direct)) +#define FLEX_PER_V7 (_EXTENT(DIRSIZ) + 1) +#define FLEX_PER_BLOCK (STATIC_BLOCK_SIZE/sizeof(struct _fl_direct)) + +/* Definitions for the directory(3) routines: */ +typedef struct { + char _fd; /* Filedescriptor of open directory */ + char _v7; /* Directory is Version 7 */ + short _count; /* This many objects in buf */ + off_t _pos; /* Position in directory file */ + struct _fl_direct *_ptr; /* Next slot in buf */ + struct _fl_direct _buf[FLEX_PER_BLOCK]; /* One block of a directory file */ + struct _fl_direct _v7f[FLEX_PER_V7]; /* V7 entry transformed to flex */ +} DIR; + +struct dirent { /* Largest entry (8 slots) */ + ino_t d_ino; /* I-node number */ + unsigned char d_extent; /* Extended with this many slots */ + char d_name[61]; /* Null terminated name */ +}; + +/* Function Prototypes. */ +_PROTOTYPE( int closedir, (DIR *_dirp) ); +_PROTOTYPE( DIR *opendir, (const char *_dirname) ); +_PROTOTYPE( struct dirent *readdir, (DIR *_dirp) ); +_PROTOTYPE( void rewinddir, (DIR *_dirp) ); + +#ifdef _MINIX +_PROTOTYPE( int seekdir, (DIR *_dirp, off_t _loc) ); +_PROTOTYPE( off_t telldir, (DIR *_dirp) ); +#endif + +#endif /* _DIRENT_H */ diff --git a/include/env.h b/include/env.h new file mode 100644 index 000000000..d25e6a244 --- /dev/null +++ b/include/env.h @@ -0,0 +1,4 @@ +_PROTOTYPE( int env_parse, (char *env, char *fmt, int field, + long *param, long min, long max) ); +_PROTOTYPE( void env_panic, (char *env) ); +_PROTOTYPE( int env_prefix, (char *env, char *prefix) ); diff --git a/include/errno.h b/include/errno.h new file mode 100755 index 000000000..0d11fa96b --- /dev/null +++ b/include/errno.h @@ -0,0 +1,129 @@ +/* The <errno.h> header defines the numbers of the various errors that can + * occur during program execution. They are visible to user programs and + * should be small positive integers. However, they are also used within + * MINIX, where they must be negative. For example, the READ system call is + * executed internally by calling do_read(). This function returns either a + * (negative) error number or a (positive) number of bytes actually read. + * + * To solve the problem of having the error numbers be negative inside the + * the system and positive outside, the following mechanism is used. All the + * definitions are are the form: + * + * #define EPERM (_SIGN 1) + * + * If the macro _SYSTEM is defined, then _SIGN is set to "-", otherwise it is + * set to "". Thus when compiling the operating system, the macro _SYSTEM + * will be defined, setting EPERM to (- 1), whereas when when this + * file is included in an ordinary user program, EPERM has the value ( 1). + */ + +#ifndef _ERRNO_H /* check if <errno.h> is already included */ +#define _ERRNO_H /* it is not included; note that fact */ + +/* Now define _SIGN as "" or "-" depending on _SYSTEM. */ +#ifdef _SYSTEM +# define _SIGN - +# define OK 0 +#else +# define _SIGN +#endif + +extern int errno; /* place where the error numbers go */ + +/* Here are the numerical values of the error numbers. */ +#define _NERROR 70 /* number of errors */ + +#define EGENERIC (_SIGN 99) /* generic error */ +#define EPERM (_SIGN 1) /* operation not permitted */ +#define ENOENT (_SIGN 2) /* no such file or directory */ +#define ESRCH (_SIGN 3) /* no such process */ +#define EINTR (_SIGN 4) /* interrupted function call */ +#define EIO (_SIGN 5) /* input/output error */ +#define ENXIO (_SIGN 6) /* no such device or address */ +#define E2BIG (_SIGN 7) /* arg list too long */ +#define ENOEXEC (_SIGN 8) /* exec format error */ +#define EBADF (_SIGN 9) /* bad file descriptor */ +#define ECHILD (_SIGN 10) /* no child process */ +#define EAGAIN (_SIGN 11) /* resource temporarily unavailable */ +#define ENOMEM (_SIGN 12) /* not enough space */ +#define EACCES (_SIGN 13) /* permission denied */ +#define EFAULT (_SIGN 14) /* bad address */ +#define ENOTBLK (_SIGN 15) /* Extension: not a block special file */ +#define EBUSY (_SIGN 16) /* resource busy */ +#define EEXIST (_SIGN 17) /* file exists */ +#define EXDEV (_SIGN 18) /* improper link */ +#define ENODEV (_SIGN 19) /* no such device */ +#define ENOTDIR (_SIGN 20) /* not a directory */ +#define EISDIR (_SIGN 21) /* is a directory */ +#define EINVAL (_SIGN 22) /* invalid argument */ +#define ENFILE (_SIGN 23) /* too many open files in system */ +#define EMFILE (_SIGN 24) /* too many open files */ +#define ENOTTY (_SIGN 25) /* inappropriate I/O control operation */ +#define ETXTBSY (_SIGN 26) /* no longer used */ +#define EFBIG (_SIGN 27) /* file too large */ +#define ENOSPC (_SIGN 28) /* no space left on device */ +#define ESPIPE (_SIGN 29) /* invalid seek */ +#define EROFS (_SIGN 30) /* read-only file system */ +#define EMLINK (_SIGN 31) /* too many links */ +#define EPIPE (_SIGN 32) /* broken pipe */ +#define EDOM (_SIGN 33) /* domain error (from ANSI C std) */ +#define ERANGE (_SIGN 34) /* result too large (from ANSI C std) */ +#define EDEADLK (_SIGN 35) /* resource deadlock avoided */ +#define ENAMETOOLONG (_SIGN 36) /* file name too long */ +#define ENOLCK (_SIGN 37) /* no locks available */ +#define ENOSYS (_SIGN 38) /* function not implemented */ +#define ENOTEMPTY (_SIGN 39) /* directory not empty */ + +/* The following errors relate to networking. */ +#define EPACKSIZE (_SIGN 50) /* invalid packet size for some protocol */ +#define EOUTOFBUFS (_SIGN 51) /* not enough buffers left */ +#define EBADIOCTL (_SIGN 52) /* illegal ioctl for device */ +#define EBADMODE (_SIGN 53) /* badmode in ioctl */ +#define EWOULDBLOCK (_SIGN 54) +#define EBADDEST (_SIGN 55) /* not a valid destination address */ +#define EDSTNOTRCH (_SIGN 56) /* destination not reachable */ +#define EISCONN (_SIGN 57) /* all ready connected */ +#define EADDRINUSE (_SIGN 58) /* address in use */ +#define ECONNREFUSED (_SIGN 59) /* connection refused */ +#define ECONNRESET (_SIGN 60) /* connection reset */ +#define ETIMEDOUT (_SIGN 61) /* connection timed out */ +#define EURG (_SIGN 62) /* urgent data present */ +#define ENOURG (_SIGN 63) /* no urgent data present */ +#define ENOTCONN (_SIGN 64) /* no connection (yet or anymore) */ +#define ESHUTDOWN (_SIGN 65) /* a write call to a shutdown connection */ +#define ENOCONN (_SIGN 66) /* no such connection */ +#define EAFNOSUPPORT (_SIGN 67) /* address family not supported */ +#define EPROTONOSUPPORT (_SIGN 68) /* protocol not supported by AF */ + +/* The following are not POSIX errors, but they can still happen. + * All of these are generated by the kernel and relate to message passing. + */ +#define ELOCKED (_SIGN 101) /* can't send message due to deadlock */ +#define EBADCALL (_SIGN 102) /* illegal system call number */ +#define EBADSRCDST (_SIGN 103) /* bad source or destination process */ +#define ECALLDENIED (_SIGN 104) /* no permission for system call */ +#define EDEADDST (_SIGN 105) /* send destination is not alive */ +#define ENOTREADY (_SIGN 106) /* source or destination is not ready */ +#define EBADREQUEST (_SIGN 107) /* destination cannot handle request */ +#define EDONTREPLY (_SIGN 201) /* pseudo-code: don't send a reply */ + +/* The following error codes are generated by the kernel itself. */ +#if DEAD_CODE /* replaced by above codes */ +#ifdef _SYSTEM +#define E_TRY_AGAIN -1003 /* can't send -- tables full */ +#define E_TASK -1006 /* can't send to task */ +#define E_OVERRUN -1004 /* interrupt for task that is not waiting */ +#define E_NO_PERM -1008 /* ordinary users can't send to tasks */ +#define E_BAD_DEST -1001 /* destination address illegal */ +#define E_BAD_BUF -1005 /* message buf outside caller's addr space */ +#define E_BAD_FCN -1009 /* unknown (illegal) request type */ +#define E_BAD_ADDR -1010 /* bad address given to utility routine */ +#define E_BAD_PROC -1011 /* bad proc number given to utility */ + +#define E_BAD_REQUEST -1009 /* unknown (illegal) request type */ +#define E_DONT_REPLY -2000 /* pseudo-code: do not send a reply message */ +#endif /* DEAD_CODE */ + +#endif /* _SYSTEM */ + +#endif /* _ERRNO_H */ diff --git a/include/fcntl.h b/include/fcntl.h new file mode 100755 index 000000000..06f6f89c6 --- /dev/null +++ b/include/fcntl.h @@ -0,0 +1,68 @@ +/* The <fcntl.h> header is needed by the open() and fcntl() system calls, + * which have a variety of parameters and flags. They are described here. + * The formats of the calls to each of these are: + * + * open(path, oflag [,mode]) open a file + * fcntl(fd, cmd [,arg]) get or set file attributes + * + */ + +#ifndef _FCNTL_H +#define _FCNTL_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +/* These values are used for cmd in fcntl(). POSIX Table 6-1. */ +#define F_DUPFD 0 /* duplicate file descriptor */ +#define F_GETFD 1 /* get file descriptor flags */ +#define F_SETFD 2 /* set file descriptor flags */ +#define F_GETFL 3 /* get file status flags */ +#define F_SETFL 4 /* set file status flags */ +#define F_GETLK 5 /* get record locking information */ +#define F_SETLK 6 /* set record locking information */ +#define F_SETLKW 7 /* set record locking info; wait if blocked */ + +/* File descriptor flags used for fcntl(). POSIX Table 6-2. */ +#define FD_CLOEXEC 1 /* close on exec flag for third arg of fcntl */ + +/* L_type values for record locking with fcntl(). POSIX Table 6-3. */ +#define F_RDLCK 1 /* shared or read lock */ +#define F_WRLCK 2 /* exclusive or write lock */ +#define F_UNLCK 3 /* unlock */ + +/* Oflag values for open(). POSIX Table 6-4. */ +#define O_CREAT 00100 /* creat file if it doesn't exist */ +#define O_EXCL 00200 /* exclusive use flag */ +#define O_NOCTTY 00400 /* do not assign a controlling terminal */ +#define O_TRUNC 01000 /* truncate flag */ + +/* File status flags for open() and fcntl(). POSIX Table 6-5. */ +#define O_APPEND 02000 /* set append mode */ +#define O_NONBLOCK 04000 /* no delay */ + +/* File access modes for open() and fcntl(). POSIX Table 6-6. */ +#define O_RDONLY 0 /* open(name, O_RDONLY) opens read only */ +#define O_WRONLY 1 /* open(name, O_WRONLY) opens write only */ +#define O_RDWR 2 /* open(name, O_RDWR) opens read/write */ + +/* Mask for use with file access modes. POSIX Table 6-7. */ +#define O_ACCMODE 03 /* mask for file access modes */ + +/* Struct used for locking. POSIX Table 6-8. */ +struct flock { + short l_type; /* type: F_RDLCK, F_WRLCK, or F_UNLCK */ + short l_whence; /* flag for starting offset */ + off_t l_start; /* relative offset in bytes */ + off_t l_len; /* size; if 0, then until EOF */ + pid_t l_pid; /* process id of the locks' owner */ +}; + + +/* Function Prototypes. */ +_PROTOTYPE( int creat, (const char *_path, Mode_t _mode) ); +_PROTOTYPE( int fcntl, (int _filedes, int _cmd, ...) ); +_PROTOTYPE( int open, (const char *_path, int _oflag, ...) ); + +#endif /* _FCNTL_H */ diff --git a/include/float.h b/include/float.h new file mode 100755 index 000000000..a9aa92aa2 --- /dev/null +++ b/include/float.h @@ -0,0 +1,42 @@ +/* The <float.h> header defines some implementation limits for (IEEE) floating + * point. Application programs can use it to find out how big and small + * floating-point numbers can be, what epsilon to use for iteration, etc. + */ + +#ifndef _FLOAT_H +#define _FLOAT_H + +#define FLT_DIG 6 +#define FLT_EPSILON 1.19209290e-07F +#define FLT_MANT_DIG 24 +#define FLT_MAX 3.40282347e+38F +#define FLT_MAX_10_EXP 38 +#define FLT_MAX_EXP 128 +#define FLT_MIN 1.17549435e-38F +#define FLT_MIN_10_EXP -37 +#define FLT_MIN_EXP -125 + +#define DBL_DIG 15 +#define DBL_EPSILON 2.2204460492503131e-16 +#define DBL_MANT_DIG 53 +#define DBL_MAX 1.7976931348623157e+308 +#define DBL_MAX_10_EXP 308 +#define DBL_MAX_EXP 1024 +#define DBL_MIN 2.2250738585072014e-308 +#define DBL_MIN_10_EXP -307 +#define DBL_MIN_EXP -1021 + +#define LDBL_DIG 15 +#define LDBL_EPSILON 2.2204460492503131e-16L +#define LDBL_MANT_DIG 53 +#define LDBL_MAX 1.7976931348623157e+308L +#define LDBL_MAX_10_EXP 308 +#define LDBL_MAX_EXP 1024 +#define LDBL_MIN 2.2250738585072014e-308L +#define LDBL_MIN_10_EXP -307 +#define LDBL_MIN_EXP -1021 + +#define FLT_ROUNDS 1 +#define FLT_RADIX 2 + +#endif /* _FLOAT_H */ diff --git a/include/grp.h b/include/grp.h new file mode 100755 index 000000000..cd307ddce --- /dev/null +++ b/include/grp.h @@ -0,0 +1,28 @@ +/* The <grp.h> header is used for the getgrid() and getgrnam() calls. */ + +#ifndef _GRP_H +#define _GRP_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +struct group { + char *gr_name; /* the name of the group */ + char *gr_passwd; /* the group passwd */ + gid_t gr_gid; /* the numerical group ID */ + char **gr_mem; /* a vector of pointers to the members */ +}; + +/* Function Prototypes. */ +_PROTOTYPE( struct group *getgrgid, (Gid_t _gid) ); +_PROTOTYPE( struct group *getgrnam, (const char *_name) ); + +#ifdef _MINIX +_PROTOTYPE( void endgrent, (void) ); +_PROTOTYPE( struct group *getgrent, (void) ); +_PROTOTYPE( int setgrent, (void) ); +_PROTOTYPE( void setgrfile, (const char *_file) ); +#endif + +#endif /* _GRP_H */ diff --git a/include/ibm/bios.h b/include/ibm/bios.h new file mode 100644 index 000000000..6bf014a6d --- /dev/null +++ b/include/ibm/bios.h @@ -0,0 +1,55 @@ +/* BIOS definitions. Each BIOS entry has an index that is to be used with the + * sys_bioscopy() system call. The raw addresses, sizes, and magic numbers + * are defined here as well. The values that are defined here were collected + * from various kernel files in MINIX 2.0.4. + * + * Author: Jorrit N. Herder + */ + +#ifndef _BIOS_H +#define _BIOS_H + +/* Memory check (is stopped on reboot). */ +#define BIOS_MEM_CHECK 0 /* address to stop memory check */ +#define ADR_MEM_CHECK 0x472L +#define LEN_MEM_CHECK 1L +#define STOP_MEM_CHECK 0x1234 /* magic number to stop memory check */ + +/* Centronics printers. */ +#define BIOS_PRN_PORTBASE 1 /* base of printer ports */ +#define ADR_PRN_PORTBASE 0x408L +#define LEN_PRN_PORTBASE 2L + +/* Hard disk parameter vectors. */ +#define BIOS_WINI_PARAMS 2 /* number of hard disk drives */ +#define ADR_WINI_PARAMS 0x475L +#define LEN_WINI_PARAMS 1L +#define BIOS_WINI_0_PARM_VEC 3 /* disk 0 parameters */ +#define ADR_WINI_0_PARM_VEC 0x41*4L +#define LEN_WINI_0_PARM_VEC 4L +#define BIOS_WINI_1_PARM_VEC 4 /* disk 1 parameters */ +#define ADR_WINI_1_PARM_VEC 0x46*4L +#define LEN_WINI_1_PARM_VEC 4L + +/* Video controller (VDU). */ +#define BIOS_VDU_COLUMNS 5 +#define ADR_VDU_COLUMNS 0x44AL +#define LEN_VDU_COLUMNS 2L +#define BIOS_VDU_CRTBASE 6 +#define ADR_VDU_CRTBASE 0x463L +#define LEN_VDU_CRTBASE 2L +#define BIOS_VDU_ROWS 7 +#define ADR_VDU_ROWS 0x484L +#define LEN_VDU_ROWS 1L +#define BIOS_VDU_FONTLINES 8 +#define ADR_VDU_FONTLINES 0x485L +#define LEN_VDU_FONTLINES 2L + +/* Machine ID. */ +#define BIOS_MACHINE_ID 9 +#define ADR_MACHINE_ID 0xFFFFEL +#define LEN_MACHINE_ID 1L +#define PS_386_MACHINE 0xF8 /* Machine ID byte for PS/2 model 80 */ +#define PC_AT_MACHINE 0xFC /* PC/AT, PC/XT286, PS/2 models 50/60 */ + +#endif /* _BIOS_H */ diff --git a/include/ibm/cmos.h b/include/ibm/cmos.h new file mode 100755 index 000000000..ff5e99a44 --- /dev/null +++ b/include/ibm/cmos.h @@ -0,0 +1,86 @@ +/* +ibm/cmos.h + +Created: Dec 1998 by Philip Homburg <philip@cs.vu.nl> + +Definitions for the CMOS/realtime clock. Based on the datasheet for the +Dallas DS12887, compatible with the Motorola MC146818 +*/ + +#define RTC_INDEX 0x70 /* Bit 7 = NMI enable (1) / disable (0) + * bits 0..6 index + */ +#define RTC_IO 0x71 /* Data register, + * Note: the operation following a write to + * RTC_INDEX should an access (read or write) + * to RTC_IO + */ + +#define RTC_SEC 0x0 /* Seconds register */ +#define RTC_SEC_ALRM 0x1 /* Seconds register for alarm */ +#define RTC_MIN 0x2 /* Minutes register */ +#define RTC_MIN_ALRM 0x3 /* Minutes register for alarm */ +#define RTC_HOUR 0x4 /* Hours register */ +#define RTC_HOUR_ALRM 0x5 /* Hours register for alarm */ +#define RTC_WDAY 0x6 /* Day of the week, 1..7, Sunday = 1 */ +#define RTC_MDAY 0x7 /* Day of the month, 1..31 */ +#define RTC_MONTH 0x8 /* Month, 1..12 */ +#define RTC_YEAR 0x9 /* Year, 0..99 */ +#define RTC_REG_A 0xA +#define RTC_A_UIP 0x80 /* Update in progress. When clear, + * no update will occur for 244 + * micro seconds. + */ +#define RTC_A_DV 0x70 /* Divider bits, valid values are: */ +#define RTC_A_DV_OK 0x20 /* Normal */ +#define RTC_A_DV_STOP 0x70 /* Stop, a re-start starts + * halfway through a cycle, + * i.e. the update occurs after + * 500ms. + */ +#define RTC_A_RS 0x0F /* Int. freq */ + /* 0 None + * 1 256 Hz + * 2 128 Hz + * 3 8192 Hz + * 4 4096 Hz + * 5 2048 Hz + * 6 1024 Hz + * 7 512 Hz + * 8 256 Hz + * 9 128 Hz + * 10 64 Hz + * 11 32 Hz + * 12 16 Hz + * 13 8 Hz + * 14 4 Hz + * 15 2 Hz + */ +#define RTC_A_RS_DEF 6 /* Default freq. */ +#define RTC_REG_B 0xB +#define RTC_B_SET 0x80 /* Inhibit updates */ +#define RTC_B_PIE 0x40 /* Enable periodic interrupts */ +#define RTC_B_AIE 0x20 /* Enable alarm interrupts */ +#define RTC_B_UIE 0x10 /* Enable update ended interrupts */ +#define RTC_B_SQWE 0x08 /* Enable square wave output */ +#define RTC_B_DM_BCD 0x04 /* Data is in BCD (otherwise binary) */ +#define RTC_B_24 0x02 /* Count hours in 24-hour mode */ +#define RTC_B_DSE 0x01 /* Automatic (wrong) daylight savings + * updates + */ +#define RTC_REG_C 0xC + + +/* Contents of the general purpose CMOS RAM (source IBM reference manual) */ +#define CMOS_STATUS 0xE +#define CS_LOST_POWER 0x80 /* Chip lost power */ +#define CS_BAD_CHKSUM 0x40 /* Checksum is incorrect */ +#define CS_BAD_CONFIG 0x20 /* Bad configuration info */ +#define CS_BAD_MEMSIZE 0x10 /* Wrong memory size of CMOS */ +#define CS_BAD_HD 0x08 /* Harddisk failed */ +#define CS_BAD_TIME 0x04 /* CMOS time is invalid */ + /* bits 0 and 1 are reserved */ + +/* + * $PchId: cmos.h,v 1.1 1998/12/16 09:14:21 philip Exp $ + */ diff --git a/include/ibm/diskparm.h b/include/ibm/diskparm.h new file mode 100755 index 000000000..fdf514916 --- /dev/null +++ b/include/ibm/diskparm.h @@ -0,0 +1,20 @@ +/* PC (and AT) BIOS structure to hold disk parameters. Under Minix, it is + * used mainly for formatting. + */ + +#ifndef _DISKPARM_H +#define _DISKPARM_H +struct disk_parameter_s { + char spec1; + char spec2; + char motor_turnoff_sec; + char sector_size_code; + char sectors_per_cylinder; + char gap_length; + char dtl; + char gap_length_for_format; + char fill_byte_for_format; + char head_settle_msec; + char motor_start_eigth_sec; +}; +#endif /* _DISKPARM_H */ diff --git a/include/ibm/int86.h b/include/ibm/int86.h new file mode 100755 index 000000000..7d42b0043 --- /dev/null +++ b/include/ibm/int86.h @@ -0,0 +1,63 @@ +/* int86.h - 8086 interrupt types Author: Kees J. Bot + * 3 May 2000 + */ + +/* Registers used in an PC real mode call for BIOS or DOS services. A + * driver is called through the vector if the interrupt number is zero. + */ +union reg86 { + struct l { + u32_t ef; /* 32 bit flags (output only) */ + u32_t vec; /* Driver vector (input only) */ + u32_t _ds_es[1]; + u32_t eax; /* 32 bit general registers */ + u32_t ebx; + u32_t ecx; + u32_t edx; + u32_t esi; + u32_t edi; + u32_t ebp; + } l; + struct w { + u16_t f, _ef[1]; /* 16 bit flags (output only) */ + u16_t off, seg; /* Driver vector (input only) */ + u16_t ds, es; /* DS and ES real mode segment regs */ + u16_t ax, _eax[1]; /* 16 bit general registers */ + u16_t bx, _ebx[1]; + u16_t cx, _ecx[1]; + u16_t dx, _edx[1]; + u16_t si, _esi[1]; + u16_t di, _edi[1]; + u16_t bp, _ebp[1]; + } w; + struct b { + u8_t intno, _intno[3]; /* Interrupt number (input only) */ + u8_t _vec[4]; + u8_t _ds_es[4]; + u8_t al, ah, _eax[2]; /* 8 bit general registers */ + u8_t bl, bh, _ebx[2]; + u8_t cl, ch, _ecx[2]; + u8_t dl, dh, _edx[2]; + u8_t _esi[4]; + u8_t _edi[4]; + u8_t _ebp[4]; + } b; +}; + +#ifdef _SYSTEM /* Kernel: Registers used in an 8086 interrupt */ +EXTERN union reg86 reg86; +#endif + +/* Parameters passed on ioctls to the memory task. */ + +struct mio_int86 { /* MIOCINT86 */ + union reg86 reg86; /* x86 registers as above */ + u16_t off, seg; /* Address of kernel buffer */ + void *buf; /* User data buffer */ + size_t len; /* Size of user buffer */ +}; + +struct mio_ldt86 { /* MIOCGLDT86, MIOCSLDT86 */ + size_t idx; /* Index in process' LDT */ + u16_t entry[4]; /* One LDT entry to get or set. */ +}; diff --git a/include/ibm/interrupt.h b/include/ibm/interrupt.h new file mode 100644 index 000000000..deb9c46cf --- /dev/null +++ b/include/ibm/interrupt.h @@ -0,0 +1,62 @@ +/* Interrupt numbers and hardware vectors. */ + +#ifndef _INTERRUPT_H +#define _INTERRUPT_H + +#if (CHIP == INTEL) + +/* 8259A interrupt controller ports. */ +#define INT_CTL 0x20 /* I/O port for interrupt controller */ +#define INT_CTLMASK 0x21 /* setting bits in this port disables ints */ +#define INT2_CTL 0xA0 /* I/O port for second interrupt controller */ +#define INT2_CTLMASK 0xA1 /* setting bits in this port disables ints */ + +/* Magic numbers for interrupt controller. */ +#define ENABLE 0x20 /* code used to re-enable after an interrupt */ + + +/* Interrupt vectors defined/reserved by processor. */ +#define DIVIDE_VECTOR 0 /* divide error */ +#define DEBUG_VECTOR 1 /* single step (trace) */ +#define NMI_VECTOR 2 /* non-maskable interrupt */ +#define BREAKPOINT_VECTOR 3 /* software breakpoint */ +#define OVERFLOW_VECTOR 4 /* from INTO */ + +/* Fixed system call vector. */ +#define SYS_VECTOR 32 /* system calls are made with int SYSVEC */ +#define SYS386_VECTOR 33 /* except 386 system calls use this */ +#define LEVEL0_VECTOR 34 /* for execution of a function at level 0 */ + +/* Suitable irq bases for hardware interrupts. Reprogram the 8259(s) from + * the PC BIOS defaults since the BIOS doesn't respect all the processor's + * reserved vectors (0 to 31). + */ +#define BIOS_IRQ0_VEC 0x08 /* base of IRQ0-7 vectors used by BIOS */ +#define BIOS_IRQ8_VEC 0x70 /* base of IRQ8-15 vectors used by BIOS */ +#define IRQ0_VECTOR 0x50 /* nice vectors to relocate IRQ0-7 to */ +#define IRQ8_VECTOR 0x70 /* no need to move IRQ8-15 */ + +/* Hardware interrupt numbers. */ +#define NR_IRQ_VECTORS 16 +#define CLOCK_IRQ 0 +#define KEYBOARD_IRQ 1 +#define CASCADE_IRQ 2 /* cascade enable for 2nd AT controller */ +#define ETHER_IRQ 3 /* default ethernet interrupt vector */ +#define SECONDARY_IRQ 3 /* RS232 interrupt vector for port 2 */ +#define RS232_IRQ 4 /* RS232 interrupt vector for port 1 */ +#define XT_WINI_IRQ 5 /* xt winchester */ +#define FLOPPY_IRQ 6 /* floppy disk */ +#define PRINTER_IRQ 7 +#define AT_WINI_0_IRQ 14 /* at winchester controller 0 */ +#define AT_WINI_1_IRQ 15 /* at winchester controller 1 */ + + +/* Interrupt number to hardware vector. */ +#define BIOS_VECTOR(irq) \ + (((irq) < 8 ? BIOS_IRQ0_VEC : BIOS_IRQ8_VEC) + ((irq) & 0x07)) +#define VECTOR(irq) \ + (((irq) < 8 ? IRQ0_VECTOR : IRQ8_VECTOR) + ((irq) & 0x07)) + +#endif /* (CHIP == INTEL) */ + +#endif /* _INTERRUPT_H */ diff --git a/include/ibm/partition.h b/include/ibm/partition.h new file mode 100755 index 000000000..2e270d004 --- /dev/null +++ b/include/ibm/partition.h @@ -0,0 +1,26 @@ +/* Description of entry in partition table. */ +#ifndef _PARTITION_H +#define _PARTITION_H + +struct part_entry { + unsigned char bootind; /* boot indicator 0/ACTIVE_FLAG */ + unsigned char start_head; /* head value for first sector */ + unsigned char start_sec; /* sector value + cyl bits for first sector */ + unsigned char start_cyl; /* track value for first sector */ + unsigned char sysind; /* system indicator */ + unsigned char last_head; /* head value for last sector */ + unsigned char last_sec; /* sector value + cyl bits for last sector */ + unsigned char last_cyl; /* track value for last sector */ + unsigned long lowsec; /* logical first sector */ + unsigned long size; /* size of partition in sectors */ +}; + +#define ACTIVE_FLAG 0x80 /* value for active in bootind field (hd0) */ +#define NR_PARTITIONS 4 /* number of entries in partition table */ +#define PART_TABLE_OFF 0x1BE /* offset of partition table in boot sector */ + +/* Partition types. */ +#define NO_PART 0x00 /* unused entry */ +#define MINIX_PART 0x81 /* Minix partition type */ + +#endif /* _PARTITION_H */ diff --git a/include/ibm/portio.h b/include/ibm/portio.h new file mode 100755 index 000000000..07060bbda --- /dev/null +++ b/include/ibm/portio.h @@ -0,0 +1,29 @@ +/* +ibm/portio.h + +Created: Jan 15, 1992 by Philip Homburg +*/ + +#ifndef _PORTIO_H_ +#define _PORTIO_H_ + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +unsigned inb(U16_t _port); +unsigned inw(U16_t _port); +unsigned inl(U32_t _port); +void outb(U16_t _port, U8_t _value); +void outw(U16_t _port, U16_t _value); +void outl(U16_t _port, U32_t _value); +void insb(U16_t _port, void *_buf, size_t _count); +void insw(U16_t _port, void *_buf, size_t _count); +void insl(U16_t _port, void *_buf, size_t _count); +void outsb(U16_t _port, void *_buf, size_t _count); +void outsw(U16_t _port, void *_buf, size_t _count); +void outsl(U16_t _port, void *_buf, size_t _count); +void intr_disable(void); +void intr_enable(void); + +#endif /* _PORTIO_H_ */ diff --git a/include/ibm/ports.h b/include/ibm/ports.h new file mode 100644 index 000000000..3c04395bd --- /dev/null +++ b/include/ibm/ports.h @@ -0,0 +1,17 @@ +/* Addresses and magic numbers for miscellaneous ports. */ + +#ifndef _PORTS_H +#define _PORTS_H + +#if (CHIP == INTEL) + +/* Miscellaneous ports. */ +#define PCR 0x65 /* Planar Control Register */ +#define PORT_B 0x61 /* I/O port for 8255 port B (kbd, beeper...) */ +#define TIMER0 0x40 /* I/O port for timer channel 0 */ +#define TIMER2 0x42 /* I/O port for timer channel 2 */ +#define TIMER_MODE 0x43 /* I/O port for timer mode control */ + +#endif /* (CHIP == INTEL) */ + +#endif /* _PORTS_H */ diff --git a/include/lib.h b/include/lib.h new file mode 100755 index 000000000..948739a47 --- /dev/null +++ b/include/lib.h @@ -0,0 +1,39 @@ +/* The <lib.h> header is the master header used by the library. + * All the C files in the lib subdirectories include it. + */ + +#ifndef _LIB_H +#define _LIB_H + +/* First come the defines. */ +#define _POSIX_SOURCE 1 /* tell headers to include POSIX stuff */ +#define _MINIX 1 /* tell headers to include MINIX stuff */ + +/* The following are so basic, all the lib files get them automatically. */ +#include <minix/config.h> /* must be first */ +#include <sys/types.h> +#include <limits.h> +#include <errno.h> +#include <ansi.h> + +#include <minix/const.h> +#include <minix/type.h> +#include <minix/callnr.h> + +#include <minix/ipc.h> + +#define MM 0 +#define FS 1 + +_PROTOTYPE( int __execve, (const char *_path, char *const _argv[], + char *const _envp[], int _nargs, int _nenvps) ); +_PROTOTYPE( int _syscall, (int _who, int _syscallnr, message *_msgptr) ); +_PROTOTYPE( void _loadname, (const char *_name, message *_msgptr) ); +_PROTOTYPE( int _len, (const char *_s) ); +_PROTOTYPE( void panic, (const char *_message, int _errnum) ); +#if 0 +_PROTOTYPE( int _sendrec, (int _src_dest, message *_m_ptr) ); +#endif +_PROTOTYPE( void _begsig, (int _dummy) ); + +#endif /* _LIB_H */ diff --git a/include/limits.h b/include/limits.h new file mode 100755 index 000000000..0c7490857 --- /dev/null +++ b/include/limits.h @@ -0,0 +1,87 @@ +/* The <limits.h> header defines some basic sizes, both of the language types + * (e.g., the number of bits in an integer), and of the operating system (e.g. + * the number of characters in a file name. + */ + +#ifndef _LIMITS_H +#define _LIMITS_H + +/* Definitions about chars (8 bits in MINIX, and signed). */ +#define CHAR_BIT 8 /* # bits in a char */ +#define CHAR_MIN -128 /* minimum value of a char */ +#define CHAR_MAX 127 /* maximum value of a char */ +#define SCHAR_MIN -128 /* minimum value of a signed char */ +#define SCHAR_MAX 127 /* maximum value of a signed char */ +#define UCHAR_MAX 255 /* maximum value of an unsigned char */ +#define MB_LEN_MAX 1 /* maximum length of a multibyte char */ + +/* Definitions about shorts (16 bits in MINIX). */ +#define SHRT_MIN (-32767-1) /* minimum value of a short */ +#define SHRT_MAX 32767 /* maximum value of a short */ +#define USHRT_MAX 0xFFFF /* maximum value of unsigned short */ + +/* _EM_WSIZE is a compiler-generated symbol giving the word size in bytes. */ +#if _EM_WSIZE == 2 +#define INT_MIN (-32767-1) /* minimum value of a 16-bit int */ +#define INT_MAX 32767 /* maximum value of a 16-bit int */ +#define UINT_MAX 0xFFFF /* maximum value of an unsigned 16-bit int */ +#endif + +#if _EM_WSIZE == 4 +#define INT_MIN (-2147483647-1) /* minimum value of a 32-bit int */ +#define INT_MAX 2147483647 /* maximum value of a 32-bit int */ +#define UINT_MAX 0xFFFFFFFF /* maximum value of an unsigned 32-bit int */ +#endif + +/*Definitions about longs (32 bits in MINIX). */ +#define LONG_MIN (-2147483647L-1)/* minimum value of a long */ +#define LONG_MAX 2147483647L /* maximum value of a long */ +#define ULONG_MAX 0xFFFFFFFFL /* maximum value of an unsigned long */ + +#include <sys/dir.h> + +/* Minimum sizes required by the POSIX P1003.1 standard (Table 2-3). */ +#ifdef _POSIX_SOURCE /* these are only visible for POSIX */ +#define _POSIX_ARG_MAX 4096 /* exec() may have 4K worth of args */ +#define _POSIX_CHILD_MAX 6 /* a process may have 6 children */ +#define _POSIX_LINK_MAX 8 /* a file may have 8 links */ +#define _POSIX_MAX_CANON 255 /* size of the canonical input queue */ +#define _POSIX_MAX_INPUT 255 /* you can type 255 chars ahead */ +#define _POSIX_NAME_MAX DIRSIZ /* a file name may have 14 chars */ +#define _POSIX_NGROUPS_MAX 0 /* supplementary group IDs are optional */ +#define _POSIX_OPEN_MAX 16 /* a process may have 16 files open */ +#define _POSIX_PATH_MAX 255 /* a pathname may contain 255 chars */ +#define _POSIX_PIPE_BUF 512 /* pipes writes of 512 bytes must be atomic */ +#define _POSIX_STREAM_MAX 8 /* at least 8 FILEs can be open at once */ +#define _POSIX_TZNAME_MAX 3 /* time zone names can be at least 3 chars */ +#define _POSIX_SSIZE_MAX 32767 /* read() must support 32767 byte reads */ + +/* Values actually implemented by MINIX (Tables 2-4, 2-5, 2-6, and 2-7). */ +/* Some of these old names had better be defined when not POSIX. */ +#define _NO_LIMIT 100 /* arbitrary number; limit not enforced */ + +#define NGROUPS_MAX 0 /* supplemental group IDs not available */ +#if _EM_WSIZE > 2 +#define ARG_MAX 16384 /* # bytes of args + environ for exec() */ +#else +#define ARG_MAX 4096 /* args + environ on small machines */ +#endif +#define CHILD_MAX _NO_LIMIT /* MINIX does not limit children */ +#define OPEN_MAX 20 /* # open files a process may have */ +#if 0 /* V1 file system */ +#define LINK_MAX CHAR_MAX /* # links a file may have */ +#else /* V2 or better file system */ +#define LINK_MAX SHRT_MAX /* # links a file may have */ +#endif +#define MAX_CANON 255 /* size of the canonical input queue */ +#define MAX_INPUT 255 /* size of the type-ahead buffer */ +#define NAME_MAX DIRSIZ /* # chars in a file name */ +#define PATH_MAX 255 /* # chars in a path name */ +#define PIPE_BUF 7168 /* # bytes in atomic write to a pipe */ +#define STREAM_MAX 20 /* must be the same as FOPEN_MAX in stdio.h */ +#define TZNAME_MAX 3 /* maximum bytes in a time zone name is 3 */ +#define SSIZE_MAX 32767 /* max defined byte count for read() */ + +#endif /* _POSIX_SOURCE */ + +#endif /* _LIMITS_H */ diff --git a/include/locale.h b/include/locale.h new file mode 100755 index 000000000..e48add267 --- /dev/null +++ b/include/locale.h @@ -0,0 +1,47 @@ +/* The <locale.h> header is used to custom tailor currency symbols, decimal + * points, and other items to the local style. It is ANSI's attempt at + * avoiding cultural imperialism. The locale given below is for C. + */ + +#ifndef _LOCALE_H +#define _LOCALE_H + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +struct lconv { + char *decimal_point; /* "." */ + char *thousands_sep; /* "" */ + char *grouping; /* "" */ + char *int_curr_symbol; /* "" */ + char *currency_symbol; /* "" */ + char *mon_decimal_point; /* "" */ + char *mon_thousands_sep; /* "" */ + char *mon_grouping; /* "" */ + char *positive_sign; /* "" */ + char *negative_sign; /* "" */ + char int_frac_digits; /* CHAR_MAX */ + char frac_digits; /* CHAR_MAX */ + char p_cs_precedes; /* CHAR_MAX */ + char p_sep_by_space; /* CHAR_MAX */ + char n_cs_precedes; /* CHAR_MAX */ + char n_sep_by_space; /* CHAR_MAX */ + char p_sign_posn; /* CHAR_MAX */ + char n_sign_posn; /* CHAR_MAX */ +}; + +#define NULL ((void *)0) + +#define LC_ALL 1 +#define LC_COLLATE 2 +#define LC_CTYPE 3 +#define LC_MONETARY 4 +#define LC_NUMERIC 5 +#define LC_TIME 6 + +/* Function Prototypes. */ +_PROTOTYPE( char *setlocale, (int _category, const char *_locale) ); +_PROTOTYPE( struct lconv *localeconv, (void) ); + +#endif /* _LOCALE_H */ diff --git a/include/math.h b/include/math.h new file mode 100755 index 000000000..48d16aa1f --- /dev/null +++ b/include/math.h @@ -0,0 +1,39 @@ +/* The <math.h> header contains prototypes for mathematical functions. */ + +#ifndef _MATH_H +#define _MATH_H + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +#define HUGE_VAL (__huge_val()) /* may be infinity */ + +/* Function Prototypes. */ +_PROTOTYPE( double __huge_val, (void) ); +_PROTOTYPE( int __IsNan, (double _x) ); + +_PROTOTYPE( double acos, (double _x) ); +_PROTOTYPE( double asin, (double _x) ); +_PROTOTYPE( double atan, (double _x) ); +_PROTOTYPE( double atan2, (double _y, double _x) ); +_PROTOTYPE( double ceil, (double _x) ); +_PROTOTYPE( double cos, (double _x) ); +_PROTOTYPE( double cosh, (double _x) ); +_PROTOTYPE( double exp, (double _x) ); +_PROTOTYPE( double fabs, (double _x) ); +_PROTOTYPE( double floor, (double _x) ); +_PROTOTYPE( double fmod, (double _x, double _y) ); +_PROTOTYPE( double frexp, (double _x, int *_exp) ); +_PROTOTYPE( double ldexp, (double _x, int _exp) ); +_PROTOTYPE( double log, (double _x) ); +_PROTOTYPE( double log10, (double _x) ); +_PROTOTYPE( double modf, (double _x, double *_iptr) ); +_PROTOTYPE( double pow, (double _x, double _y) ); +_PROTOTYPE( double sin, (double _x) ); +_PROTOTYPE( double sinh, (double _x) ); +_PROTOTYPE( double sqrt, (double _x) ); +_PROTOTYPE( double tan, (double _x) ); +_PROTOTYPE( double tanh, (double _x) ); + +#endif /* _MATH_H */ diff --git a/include/mathconst.h b/include/mathconst.h new file mode 100755 index 000000000..ee11bcc30 --- /dev/null +++ b/include/mathconst.h @@ -0,0 +1,29 @@ +/* + * mathconst.h - mathematic constants + */ +/* $Header$ */ + +#ifndef _MATHCONST_H +#define _MATHCONST_H + +/* Some constants (Hart & Cheney) */ +#define M_PI 3.14159265358979323846264338327950288 +#define M_2PI 6.28318530717958647692528676655900576 +#define M_3PI_4 2.35619449019234492884698253745962716 +#define M_PI_2 1.57079632679489661923132169163975144 +#define M_3PI_8 1.17809724509617246442349126872981358 +#define M_PI_4 0.78539816339744830961566084581987572 +#define M_PI_8 0.39269908169872415480783042290993786 +#define M_1_PI 0.31830988618379067153776752674502872 +#define M_2_PI 0.63661977236758134307553505349005744 +#define M_4_PI 1.27323954473516268615107010698011488 +#define M_E 2.71828182845904523536028747135266250 +#define M_LOG2E 1.44269504088896340735992468100189213 +#define M_LOG10E 0.43429448190325182765112891891660508 +#define M_LN2 0.69314718055994530941723212145817657 +#define M_LN10 2.30258509299404568401799145468436421 +#define M_SQRT2 1.41421356237309504880168872420969808 +#define M_1_SQRT2 0.70710678118654752440084436210484904 +#define M_EULER 0.57721566490153286060651209008240243 + +#endif /* _MATHCONST_H */ diff --git a/include/minix/callnr.h b/include/minix/callnr.h new file mode 100755 index 000000000..9d3ef3f6f --- /dev/null +++ b/include/minix/callnr.h @@ -0,0 +1,77 @@ +#define NCALLS 83 /* number of system calls allowed */ + +#define EXIT 1 +#define FORK 2 +#define READ 3 +#define WRITE 4 +#define OPEN 5 +#define CLOSE 6 +#define WAIT 7 +#define CREAT 8 +#define LINK 9 +#define UNLINK 10 +#define WAITPID 11 +#define CHDIR 12 +#define TIME 13 +#define MKNOD 14 +#define CHMOD 15 +#define CHOWN 16 +#define BRK 17 +#define STAT 18 +#define LSEEK 19 +#define GETPID 20 +#define MOUNT 21 +#define UMOUNT 22 +#define SETUID 23 +#define GETUID 24 +#define STIME 25 +#define PTRACE 26 +#define ALARM 27 +#define FSTAT 28 +#define PAUSE 29 +#define UTIME 30 +#define ACCESS 33 +#define SYNC 36 +#define KILL 37 +#define RENAME 38 +#define MKDIR 39 +#define RMDIR 40 +#define DUP 41 +#define PIPE 42 +#define TIMES 43 +#define SETGID 46 +#define GETGID 47 +#define SIGNAL 48 +#define IOCTL 54 +#define FCNTL 55 +#define EXEC 59 +#define UMASK 60 +#define CHROOT 61 +#define SETSID 62 +#define GETPGRP 63 + +/* The following are not system calls, but are processed like them. */ +#define UNPAUSE 65 /* to MM or FS: check for EINTR */ +#define REVIVE 67 /* to FS: revive a sleeping process */ +#define TASK_REPLY 68 /* to FS: reply code from tty task */ + +/* Posix signal handling. */ +#define SIGACTION 71 +#define SIGSUSPEND 72 +#define SIGPENDING 73 +#define SIGPROCMASK 74 +#define SIGRETURN 75 + +#define REBOOT 76 + +/* MINIX specific calls to support system services. */ +#define SVRCTL 77 +#define CMOSTIME 78 +#define GETSYSINFO 79 /* to MM or FS */ + +#if ENABLE_MESSAGE_STATS +#define MSTATS 80 +#endif + +#define SETCACHE 81 +#define FSTATFS 82 diff --git a/include/minix/cdrom.h b/include/minix/cdrom.h new file mode 100755 index 000000000..2cbe5b26c --- /dev/null +++ b/include/minix/cdrom.h @@ -0,0 +1,42 @@ +/* This file contains some structures used by the Mitsumi cdrom driver. + * + * Feb 13 1995 Author: Michel R. Prevenier + */ + +/* Index into the mss arrays */ +#define MINUTES 0 +#define SECONDS 1 +#define SECTOR 2 + +struct cd_play_mss +{ + u8_t begin_mss[3]; + u8_t end_mss[3]; +}; + + +struct cd_play_track +{ + u8_t begin_track; + u8_t end_track; +}; + + +struct cd_disk_info +{ + u8_t first_track; + u8_t last_track; + u8_t disk_length_mss[3]; + u8_t first_track_mss[3]; +}; + + +struct cd_toc_entry +{ + u8_t control_address; + u8_t track_nr; + u8_t index_nr; + u8_t track_time_mss[3]; + u8_t reserved; + u8_t position_mss[3]; +}; diff --git a/include/minix/com.h b/include/minix/com.h new file mode 100755 index 000000000..9058db81b --- /dev/null +++ b/include/minix/com.h @@ -0,0 +1,400 @@ +/*===========================================================================* + * System calls and magic process numbers * + *===========================================================================*/ + +/* Masks and flags for system calls. */ +#define SYSCALL_FUNC 0x0F /* mask for system call function */ +#define SYSCALL_FLAGS 0xF0 /* mask for system call flags */ +#define NON_BLOCKING 0x10 /* don't block, but return */ + +/* System calls (numbers passed when trapping to the kernel) */ +#define SEND 1 /* function code for sending messages */ +#define RECEIVE 2 /* function code for receiving messages */ +#define BOTH 3 /* function code for SEND + RECEIVE */ +#define NB_SEND (SEND | NON_BLOCKING) /* non-blocking SEND */ +#define NB_RECEIVE (RECEIVE | NON_BLOCKING) /* non-blocking RECEIVE */ + +/* Magic, invalid process numbers. */ +#define ANY 0x7ace /* used to indicate 'any process' */ +#define NONE 0x6ace /* used to indicate 'no process at all' */ +#define SELF 0x8ace /* used to indicate 'own process' */ + + +/*===========================================================================* + * Process numbers of processes in the system image * + *===========================================================================*/ + +/* The values of several task numbers depend on whether they or other tasks + * are enabled. They are defined as (PREVIOUS_TASK - ENABLE_TASK) in general. + * ENABLE_TASK is either 0 or 1, so a task either gets a new number, or gets + * the same number as the previous task and is further unused. Note that the + * order should correspond to the order in the task table defined in table.c. + */ + +/* Kernel tasks. These all run in the same address space. */ +#define RTL8139 IDLE - ENABLE_RTL8139 /* networking */ +#define IDLE -4 /* runs when no one else can run */ +#define CLOCK -3 /* alarms and other clock functions */ +#define SYSTASK -2 /* request system functionality */ +#define HARDWARE -1 /* used as source on notify() messages */ + +/* Number of tasks. Note that NR_PROCS is defined in <minix/config.h>. */ +#define NR_TASKS (4 + ENABLE_RTL8139) + +/* Magic numbers for controllers. Device driver mapping is dynamic. */ +#define CTRLR(n) (NONE + (n)) + +/* User-level processes, that is, device drivers, servers, and INIT. */ +#define MM_PROC_NR 0 /* memory manager */ +#define FS_PROC_NR 1 /* file system */ +#define IS_PROC_NR 5 /* information server */ +#define TTY 6 /* terminal (TTY) driver */ +#define MEMORY 8 /* memory driver (RAM disk, null, etc.) */ +#define AT_WINI (MEMORY + ENABLE_AT_WINI) /* AT Winchester */ +#define FLOPPY (AT_WINI + ENABLE_FLOPPY) /* floppy disk */ +#define PRINTER (FLOPPY + ENABLE_PRINTER) /* Centronics */ +#define INIT_PROC_NR (PRINTER + 1) /* init -- goes multiuser */ + +/* Number of first user process not part of the operating system. */ +#define LOW_USER INIT_PROC_NR + +/* The number of processes that are contained in the system image. */ +#define IMAGE_SIZE (NR_TASKS + \ + 5 + ENABLE_AT_WINI + ENABLE_FLOPPY + \ + ENABLE_PRINTER + 1 ) + + +/*===========================================================================* + * Kernel notification types * + *===========================================================================*/ + +/* Kernel notification types. In principle, these can be sent to any process, + * so make sure that these types do not interfere with other message types. + * Notifications are prioritized because of the way they are unhold() and + * blocking notifications are delivered. The lowest numbers go first. The + * offset are used for the per-process notification bit maps. + */ +#define NR_NOTIFICATIONS 5 /* max. is # bits in notify_mask_t */ +# define NOTIFICATION 333 /* offset for notification types */ +# define HARD_INT NOTIFICATION + 0 /* hardware interrupt */ +# define SYN_ALARM NOTIFICATION + 1 /* synchronous alarm */ +# define KSIG_PENDING NOTIFICATION + 2 /* signal(s) pending */ +# define NEW_KMESS NOTIFICATION + 3 /* new kernel message */ +# define HARD_STOP NOTIFICATION + 4 /* system shutdown */ + + +/*===========================================================================* + * Messages for BLOCK and CHARACTER device drivers * + *===========================================================================*/ + +#define CANCEL 0 /* general req to force a task to cancel */ +#define DEV_READ 3 /* fcn code for reading from tty */ +#define DEV_WRITE 4 /* fcn code for writing to tty */ +#define DEV_IOCTL 5 /* fcn code for ioctl */ +#define DEV_OPEN 6 /* fcn code for opening tty */ +#define DEV_CLOSE 7 /* fcn code for closing tty */ +#define DEV_SCATTER 8 /* fcn code for writing from a vector */ +#define DEV_GATHER 9 /* fcn code for reading into a vector */ +#define TTY_SETPGRP 10 /* fcn code for setpgroup */ +#define TTY_EXIT 11 /* a process group leader has exited */ +#define SUSPEND -998 /* used in interrupts when tty has no data */ + +/* Field names for messages to block and character device drivers. */ +#define DEVICE m2_i1 /* major-minor device */ +#define PROC_NR m2_i2 /* which (proc) wants I/O? */ +#define COUNT m2_i3 /* how many bytes to transfer */ +#define REQUEST m2_i3 /* ioctl request code */ +#define POSITION m2_l1 /* file offset */ +#define ADDRESS m2_p1 /* core buffer address */ + +/* Field names used in reply messages from tasks. */ +#define REP_PROC_NR m2_i1 /* # of proc on whose behalf I/O was done */ +#define REP_STATUS m2_i2 /* bytes transferred or error number */ + +/* Field names for messages to TTY driver. */ +#define TTY_LINE DEVICE /* message parameter: terminal line */ +#define TTY_REQUEST COUNT /* message parameter: ioctl request code */ +#define TTY_SPEK POSITION/* message parameter: ioctl speed, erasing */ +#define TTY_FLAGS m2_l2 /* message parameter: ioctl tty mode */ +#define TTY_PGRP m2_i3 /* message parameter: process group */ + +/* Field names for the QIC 02 status reply from tape driver */ +#define TAPE_STAT0 m2_l1 +#define TAPE_STAT1 m2_l2 + +/* Major and minor device numbers for MEMORY driver. */ +# define MEMORY_MAJOR 1 /* major device for memory devices */ +# define RAM_DEV 0 /* minor device for /dev/ram */ +# define MEM_DEV 1 /* minor device for /dev/mem */ +# define KMEM_DEV 2 /* minor device for /dev/kmem */ +# define NULL_DEV 3 /* minor device for /dev/null */ +# define BOOT_DEV 4 /* minor device for /dev/boot */ + +# define DEV_RAM 0x0100 /* device number of /dev/ram */ +# define DEV_BOOT 0x0104 /* device number of /dev/boot */ + + +/*===========================================================================* + * Messages for networking layer * + *===========================================================================*/ + +/* Message types for network layer requests. */ +#define NW_OPEN DEV_OPEN +#define NW_CLOSE DEV_CLOSE +#define NW_READ DEV_READ +#define NW_WRITE DEV_WRITE +#define NW_IOCTL DEV_IOCTL +#define NW_CANCEL CANCEL + +/* Message types for data link layer requests. */ +#define DL_WRITE 3 +#define DL_WRITEV 4 +#define DL_READ 5 +#define DL_READV 6 +#define DL_INIT 7 +#define DL_STOP 8 +#define DL_GETSTAT 9 + +/* Message type for data link layer replies. */ +#define DL_INIT_REPLY 20 +#define DL_TASK_REPLY 21 + +/* Field names for data link layer messages. */ +#define DL_PORT m2_i1 +#define DL_PROC m2_i2 +#define DL_COUNT m2_i3 +#define DL_MODE m2_l1 +#define DL_CLCK m2_l2 +#define DL_ADDR m2_p1 +#define DL_STAT m2_l1 + +/* Bits in 'DL_STAT' field of DL replies. */ +# define DL_PACK_SEND 0x01 +# define DL_PACK_RECV 0x02 +# define DL_READ_IP 0x04 + +/* Bits in 'DL_MODE' field of DL requests. */ +# define DL_NOMODE 0x0 +# define DL_PROMISC_REQ 0x2 +# define DL_MULTI_REQ 0x4 +# define DL_BROAD_REQ 0x8 + + +/*===========================================================================* + * CLOCK request types and field names * + *===========================================================================*/ + +/* Clock library calls are dispatched via a call vector, so be careful when + * modifying the clock call numbers. The numbers here determine which call + * is made from the call vector. + */ +# define CLK_SIGNALRM 1 /* clk_signalrm(proc_nr, ticks) */ +# define CLK_SYNCALRM 6 /* clk_syncalrm(proc_nr,exp_time,abs_time) */ +# define CLK_FLAGALRM 7 /* clk_flagalrm(ticks, flag_ptr) */ + +/*===========================================================================* + * SYSTASK request types and field names * + *===========================================================================*/ + +/* System library calls are dispatched via a call vector, so be careful when + * modifying the system call numbers. The numbers here determine which call + * is made from the call vector. + */ +#define NR_SYS_CALLS 32 /* number of system calls */ +# define SYS_TIMES 0 /* sys_times(proc_nr, bufptr) */ +# define SYS_XIT 1 /* sys_xit(parent, proc) */ + +# define SYS_SIGCTL 3 /* sys_sigctl(req,pnr,sig,ctxt,flag,pnr,map) */ +# define SYS_FORK 4 /* sys_fork(parent, child, pid) */ +# define SYS_NEWMAP 5 /* sys_newmap(proc_nr, map_ptr) */ +# define SYS_COPY 6 /* sys_copy(ptr) */ +# define SYS_EXEC 7 /* sys_exec(proc_nr, new_sp) */ + +# define SYS_ABORT 9 /* sys_abort() */ +# define SYS_KILL 10 /* sys_kill(proc_nr, sig) */ +# define SYS_UMAP 11 /* sys_umap(proc_nr, etc) */ + +# define SYS_TRACE 13 /* sys_trace(req,pid,addr,data) */ +# define SYS_VCOPY 14 /* sys_vcopy(src_p, dst_p, vcpy_s, vcpy_ptr) */ +# define SYS_SIGNALRM 15 /* sys_signalrm(proc_nr, ticks) */ +# define SYS_SYNCALRM 16 /* sys_syncalrm(proc_nr,exp_time,abs_time) */ +# define SYS_FLAGALRM 17 /* sys_flagalrm(ticks, flag_ptr) */ + +# define SYS_SVRCTL 19 /* sys_svrctl(proc_nr, req, argp) */ +# define SYS_SDEVIO 20 /* sys_sdevio(port, proc_nr, buf, count) */ + +# define SYS_GETINFO 22 /* sys_getinfo(what, whereto) */ +# define SYS_DEVIO 23 /* sys_devio(port, value) */ +# define SYS_VDEVIO 24 /* sys_vdevio(buf_ptr, nr_ports) */ +# define SYS_IRQCTL 25 /* sys_irqctl() */ +# define SYS_KMALLOC 26 /* sys_kmalloc(size, phys_base) */ +# define SYS_IOPENABLE 27 /* sys_enable_iop() */ +# define SYS_PHYS2SEG 28 /* sys_phys2seg(*seg, *off, phys) */ +# define SYS_EXIT 29 /* sys_exit(status) */ +# define SYS_VIRCOPY 30 /* sys_vircopy(src,seg,addr,dst,seg,addr,cnt) */ +# define SYS_PHYSCOPY 31 /* sys_physcopy(src_addr,dst_addr,count) */ +# define SYS_MSTATS 32 + +/* Field names for SYS_MEM, SYS_KMALLOC. */ +#define MEM_CHUNK_BASE m4_l1 /* physical base address */ +#define MEM_CHUNK_SIZE m4_l2 /* size of mem chunk */ +#define MEM_TOT_SIZE m4_l3 /* total memory size */ +#define MEM_CHUNK_TAG m4_l4 /* tag to identify chunk of mem */ + +/* Field names for SYS_DEVIO, SYS_VDEVIO, SYS_SDEVIO. */ +#define DIO_REQUEST m2_i3 /* device in or output */ +# define DIO_INPUT 0 /* input */ +# define DIO_OUTPUT 1 /* output */ +#define DIO_TYPE m2_i1 /* flag indicating byte, word, or long */ +# define DIO_BYTE 'b' /* byte type values */ +# define DIO_WORD 'w' /* word type values */ +# define DIO_LONG 'l' /* long type values */ +#define DIO_PORT m2_l1 /* single port address */ +#define DIO_VALUE m2_l2 /* single I/O value */ +#define DIO_VEC_ADDR m2_p1 /* address of buffer or (p,v)-pairs */ +#define DIO_VEC_SIZE m2_l2 /* number of elements in vector */ +#define DIO_VEC_PROC m2_i2 /* number of process where vector is */ + +/* Field names for SYS_SIGNARLM, SYS_FLAGARLM, SYS_SYNCALRM. */ +#define ALRM_EXP_TIME m2_l1 /* expire time for the alarm call */ +#define ALRM_ABS_TIME m2_i2 /* set to 1 to use absolute alarm time */ +#define ALRM_TIME_LEFT m2_l1 /* how many ticks were remaining */ +#define ALRM_PROC_NR m2_i1 /* which process wants the alarm? */ +#define ALRM_FLAG_PTR m2_p1 /* virtual address of timeout flag */ + +/* Field names for SYS_IRQCTL. */ +#define IRQ_REQUEST m5_c1 /* what to do? */ +# define IRQ_SETPOLICY 1 /* manage a slot of the IRQ table */ +# define IRQ_ENABLE 2 /* enable interrupts */ +# define IRQ_DISABLE 3 /* disable interrupts */ +#define IRQ_VECTOR m5_c2 /* irq vector */ +#define IRQ_POLICY m5_i1 /* options for IRQCTL request */ +# define IRQ_READ_PORT 0x001 /* read port and return value */ +# define IRQ_WRITE_PORT 0x002 /* write given value to port */ +# define IRQ_STROBE 0x010 /* write masked value back to port */ +# define IRQ_ECHO_VAL 0x020 /* write value read back to port */ +# define IRQ_REENABLE 0x040 /* reenable IRQ line after interrupt */ +# define IRQ_BYTE 0x100 /* byte values */ +# define IRQ_WORD 0x200 /* word values */ +# define IRQ_LONG 0x400 /* long values */ +#define IRQ_PROC_NR m5_i2 /* process number, SELF, NONE */ +#define IRQ_PORT m5_l1 /* port to read or write */ +#define IRQ_VIR_ADDR m5_l2 /* address to store value read */ +#define IRQ_MASK_VAL m5_l3 /* value or strobe mask */ + +/* Names of message field and paramaters for SYS_EXIT request. */ +#define EXIT_STATUS m2_i1 /* zero for normal exit, non-zero else */ + +/* Field names for SYS_PHYS2SEG. */ +#define SEG_SELECT m4_l1 /* segment selector returned */ +#define SEG_OFFSET m4_l2 /* offset in segment returned */ +#define SEG_PHYS m4_l3 /* physical address of segment */ +#define SEG_SIZE m4_l4 /* segment size */ + +/* Field names for SYS_VIDCOPY. */ +#define VID_REQUEST m4_l1 /* what to do? */ +# define VID_VID_COPY 1 /* request vid_vid_copy() */ +# define MEM_VID_COPY 2 /* request mem_vid_copy() */ +#define VID_SRC_ADDR m4_l2 /* virtual address in memory */ +#define VID_SRC_OFFSET m4_l3 /* offset in video memory */ +#define VID_DST_OFFSET m4_l4 /* offset in video memory */ +#define VID_CP_COUNT m4_l5 /* number of words to be copied */ + +/* Field names for SYS_ABORT. */ +#define ABRT_HOW m1_i1 /* RBT_REBOOT, RBT_HALT, etc. */ +#define ABRT_MON_PROC m1_i2 /* process where monitor params are */ +#define ABRT_MON_LEN m1_i3 /* length of monitor params */ +#define ABRT_MON_ADDR m1_p1 /* virtual address of monitor params */ + +/* Field names for SYS_COPY, _UMAP, _VIRCOPY, _PHYSCOPY. */ +#define CP_SRC_SPACE m5_c1 /* T or D space (stack is also D) */ +#define CP_SRC_PROC_NR m5_i1 /* process to copy from */ +#define CP_SRC_ADDR m5_l1 /* address where data come from */ +#define CP_DST_SPACE m5_c2 /* T or D space (stack is also D) */ +#define CP_DST_PROC_NR m5_i2 /* process to copy to */ +#define CP_DST_ADDR m5_l2 /* address where data go to */ +#define CP_NR_BYTES m5_l3 /* number of bytes to copy */ + +/* Field names for SYS_VCOPY and SYS_VVIRCOPY. */ +#define VCP_SRC_PROC m1_i1 /* process to copy from */ +#define VCP_DST_PROC m1_i2 /* process to copy to */ +#define VCP_VEC_SIZE m1_i3 /* size of copy vector */ +#define VCP_VEC_ADDR m1_p1 /* pointer to copy vector */ + +/* Field names for SYS_GETINFO. */ +#define I_REQUEST m7_i3 /* what info to get */ +# define GET_KENVIRON 0 /* get kernel environment variables */ +# define GET_IMAGE 1 /* get system image table */ +# define GET_PROCTAB 2 /* get (kernel) process table */ +# define GET_PROCNR 3 /* find nr of process with name */ +# define GET_MONPARAMS 4 /* get monitor parameters */ +# define GET_KENV 5 /* get kernel environment string */ +# define GET_IRQTAB 6 /* get the IRQ table */ +# define GET_KMESSAGES 7 /* get kernel messages */ +# define GET_MEMCHUNKS 8 /* get base+size of mem chunks */ +# define GET_KADDRESSES 9 /* get various kernel addresses */ +# define GET_SCHEDINFO 10 /* get scheduling queues */ +# define GET_PROC 11 /* get process slot if given process */ +#define I_PROC_NR m7_i4 /* calling process */ +#define I_VAL_PTR m7_p1 /* virtual address at caller */ +#define I_VAL_LEN m7_i1 /* max length of value */ +#define I_KEY_PTR m7_p2 /* virtual address of key to lookup */ +#define I_KEY_LEN m7_i2 /* length of key to lookup */ + +/* Field names for SYS_TIMES. */ +#define T_PROC_NR m4_l1 /* process to request time info for */ +#define T_USER_TIME m4_l1 /* user time consumed by process */ +#define T_SYSTEM_TIME m4_l2 /* system time consumed by process */ +#define T_CHILD_UTIME m4_l3 /* user time consumed by process' children */ +#define T_CHILD_STIME m4_l4 /* sys time consumed by process' children */ +#define T_BOOT_TICKS m4_l5 /* number of clock ticks since boot time */ + +/* Field names for SYS_TRACE, SYS_SVRCTL. */ +#define CTL_PROC_NR m2_i1 /* process number of the caller */ +#define CTL_REQUEST m2_i2 /* server control request */ +#define CTL_MM_PRIV m2_i3 /* privilege as seen by MM */ +#define CTL_ARG_PTR m2_p1 /* pointer to argument */ +#define CTL_ADDRESS m2_l1 /* address at traced process' space */ +#define CTL_DATA m2_l2 /* data field for tracing */ + +/* Field names for SYS_KILL, SYS_SIGCTL */ +#define SIG_REQUEST m2_l2 /* MM signal control request */ +#define S_GETSIG 0 /* get pending kernel signal */ +#define S_ENDSIG 1 /* finish a kernel signal */ +#define S_SENDSIG 2 /* POSIX style signal handling */ +#define S_SIGRETURN 3 /* return from POSIX handling */ +#define S_KILL 4 /* servers kills process with signal */ +#define SIG_PROC m2_i1 /* process number for inform */ +#define SIG_NUMBER m2_i2 /* signal number to send */ +#define SIG_FLAGS m2_i3 /* signal flags field */ +#define SIG_MAP m2_l1 /* used by kernel to pass signal bit map */ +#define SIG_CTXT_PTR m2_p1 /* pointer to info to restore signal context */ + +/* Field names for SYS_FORK, _EXEC, _XIT, _GETSP, _GETMAP, _NEWMAP */ +#define PR_PROC_NR m1_i1 /* indicates a (child) process */ +#define PR_PPROC_NR m1_i2 /* indicates a (parent) process */ +#define PR_STACK_PTR m1_p1 /* used for stack ptr in sys_exec, sys_getsp */ +#define PR_TRACING m1_i3 /* flag to indicate tracing is on/ off */ +#define PR_NAME_PTR m1_p2 /* tells where program name is for dmp */ +#define PR_IP_PTR m1_p3 /* initial value for ip after exec */ +#define PR_PID m1_i3 /* process id passed from MM to kernel */ +#define PR_MEM_PTR m1_p1 /* tells where memory map is for sys_newmap */ + + +/*===========================================================================* + * Miscellaneous messages, mainly used by IS * + *===========================================================================*/ + +/* Miscellaneous request types and field names, e.g. used by IS server. */ +#define PANIC_DUMPS 97 /* debug dumps at the TTY on RBT_PANIC */ +#define FKEY_CONTROL 98 /* control a function key at the TTY */ +#define FKEY_PRESSED 99 /* notify process of function key event */ +# define FKEY_NUM m2_l1 /* fkey number excluding modifiers */ +# define FKEY_CODE m2_l2 /* fkey code including modifiers */ +# define FKEY_ENABLE m2_i1 /* enable or disable mapping */ +#define DIAGNOSTICS 100 /* output a string without FS in between */ +# define DIAG_PRINT_BUF m1_p1 +# define DIAG_BUF_COUNT m1_i1 +# define DIAG_PROC_NR m1_i2 + + diff --git a/include/minix/config.h b/include/minix/config.h new file mode 100755 index 000000000..8a4151e55 --- /dev/null +++ b/include/minix/config.h @@ -0,0 +1,178 @@ +#ifndef _CONFIG_H +#define _CONFIG_H + +/* Minix release and version numbers. */ +#define OS_RELEASE "3" +#define OS_VERSION "0.1" + +/* This file sets configuration parameters for the MINIX kernel, FS, and MM. + * It is divided up into two main sections. The first section contains + * user-settable parameters. In the second section, various internal system + * parameters are set based on the user-settable parameters. + */ + +/*===========================================================================* + * This section contains user-settable parameters * + *===========================================================================*/ +#define MACHINE IBM_PC /* Must be one of the names listed below */ + +#define IBM_PC 1 /* any 8088 or 80x86-based system */ +#define SUN_4 40 /* any Sun SPARC-based system */ +#define SUN_4_60 40 /* Sun-4/60 (aka SparcStation 1 or Campus) */ +#define ATARI 60 /* ATARI ST/STe/TT (68000/68030) */ +#define AMIGA 61 /* Commodore Amiga (68000) */ +#define MACINTOSH 62 /* Apple Macintosh (68000) */ + +/* Word size in bytes (a constant equal to sizeof(int)). */ +#if __ACK__ +#define _WORD_SIZE _EM_WSIZE +#endif + +/* Number of slots in the process table for non-kernel processes. */ +#define NR_PROCS 64 + +/* The buffer cache should be made as large as you can afford. */ +#if (MACHINE == IBM_PC && _WORD_SIZE == 2) +#define NR_BUFS 40 /* # blocks in the buffer cache */ +#define NR_BUF_HASH 64 /* size of buf hash table; MUST BE POWER OF 2*/ +#endif + +#if (MACHINE == IBM_PC && _WORD_SIZE == 4) +#define NR_BUFS 128 /* # blocks in the buffer cache */ +#define NR_BUF_HASH 1024 /* size of buf hash table; MUST BE POWER OF 2*/ +#endif + +#if (MACHINE == SUN_4_60) +#define NR_BUFS 512 /* # blocks in the buffer cache (<=1536) */ +#define NR_BUF_HASH 512 /* size of buf hash table; MUST BE POWER OF 2*/ +#endif + +/* Defines for kernel configuration. */ +#define AUTO_BIOS 0 /* xt_wini.c - use Western's autoconfig BIOS */ +#define LINEWRAP 1 /* console.c - wrap lines at column 80 */ +#define ALLOW_GAP_MESSAGES 1 /* proc.c - allow messages in the gap between + * the end of bss and lowest stack address */ +#define KMESS_BUF_SIZE 512 /* size in B for kernel messages */ + +/* Number of controller tasks (/dev/cN device classes). */ +#define NR_CTRLRS 2 + +/* Enable or disable the second level file system cache on the RAM disk. */ +#define ENABLE_CACHE2 0 + +/* Enable or disable swapping processes to disk. */ +#define ENABLE_SWAP 1 + +/* Enable or disable kernel calls (allows for minimal kernel). */ +#define ENABLE_K_TRACING 1 /* process tracing can be disabled */ +#define ENABLE_K_DEBUGGING 1 /* kernel debugging calls */ + +/* Include or exclude device drivers. Set to 1 to include, 0 to exclude. */ +#define ENABLE_BIOS_WINI 0 /* enable BIOS winchester driver */ +#define ENABLE_ESDI_WINI 0 /* enable ESDI winchester driver */ +#define ENABLE_XT_WINI 0 /* enable XT winchester driver */ +#define ENABLE_AHA1540 0 /* enable Adaptec 1540 SCSI driver */ +#define ENABLE_FATFILE 0 /* enable FAT file virtual disk driver */ +#define ENABLE_DOSFILE 0 /* enable DOS file virtual disk driver */ +#define ENABLE_SB16 0 /* enable Soundblaster audio driver */ +#define ENABLE_PCI 1 /* enable PCI device recognition */ + +/* Include or exclude user-level device drivers (and supporting servers). */ +#define ENABLE_PRINTER 1 /* user-level Centronics printer driver */ +#define ENABLE_FLOPPY 1 /* enable floppy disk driver */ +#define ENABLE_AT_WINI 1 /* enable AT winchester driver */ +#define ENABLE_ATAPI 1 /* add ATAPI support to AT driver */ + +/* DMA_SECTORS may be increased to speed up DMA based drivers. */ +#define DMA_SECTORS 1 /* DMA buffer size (must be >= 1) */ + +/* Enable or disable networking drivers. */ +#define ENABLE_DP8390 0 /* enable DP8390 ethernet driver */ +#define ENABLE_WDETH 0 /* add Western Digital WD80x3 */ +#define ENABLE_NE2000 0 /* add Novell NE1000/NE2000 */ +#define ENABLE_3C503 0 /* add 3Com Etherlink II (3c503) */ +#define ENABLE_RTL8139 1 /* enable Realtek 8139 (rtl8139) */ + +/* Include or exclude backwards compatibility code. */ +#define ENABLE_BINCOMPAT 0 /* for binaries using obsolete calls */ +#define ENABLE_SRCCOMPAT 0 /* for sources using obsolete calls */ + +/* Include or exclude security sensitive code, i.e., enable or disable certain + * code sections that would allow special priviliges to user-level processes. + */ +#define ENABLE_USERPRIV 1 /* allow special user mode privileges */ + +/* User mode privileges. Be careful to set these security related features. + * USERBIOS allows user processes to perform INT86, GLDT86, and SLDT86 MIOC + * calls; USERIOPL set the CPU's I/O Protection Level bits so that user + * processes can access I/O on opening /dev/mem/ or /dev/kmem/. In normal + * operation, only the kernel should be trusted to do all this. Note that + * ENABLE_USERPRIV must be set to 1 to allow the features anyway. + */ +#define ENABLE_USERBIOS 0 /* enable user mode BIOS calls */ +#define ENABLE_LOOSELDT 0 /* allow imprecise, page based LDT entries */ +#define ENABLE_USERIOPL 1 /* enable CPU's IOPL bits for /dev/(k)mem */ + +/* NR_CONS, NR_RS_LINES, and NR_PTYS determine the number of terminals the + * system can handle. + */ +#define NR_CONS 4 /* # system consoles (1 to 8) */ +#define NR_RS_LINES 0 /* # rs232 terminals (0 to 4) */ +#define NR_PTYS 0 /* # pseudo terminals (0 to 64) */ + +#define ENABLE_MESSAGE_STATS 0 + +/*===========================================================================* + * There are no user-settable parameters after this line * + *===========================================================================*/ +/* Set the CHIP type based on the machine selected. The symbol CHIP is actually + * indicative of more than just the CPU. For example, machines for which + * CHIP == INTEL are expected to have 8259A interrrupt controllers and the + * other properties of IBM PC/XT/AT/386 types machines in general. */ +#define INTEL 1 /* CHIP type for PC, XT, AT, 386 and clones */ +#define M68000 2 /* CHIP type for Atari, Amiga, Macintosh */ +#define SPARC 3 /* CHIP type for SUN-4 (e.g. SPARCstation) */ + +/* Set the FP_FORMAT type based on the machine selected, either hw or sw */ +#define FP_NONE 0 /* no floating point support */ +#define FP_IEEE 1 /* conform IEEE floating point standard */ + +#if (MACHINE == IBM_PC) +#define CHIP INTEL +#endif + +#if (MACHINE == ATARI) || (MACHINE == AMIGA) || (MACHINE == MACINTOSH) +#define CHIP M68000 +#endif + +#if (MACHINE == SUN_4) || (MACHINE == SUN_4_60) +#define CHIP SPARC +#define FP_FORMAT FP_IEEE +#endif + +#if (MACHINE == ATARI) || (MACHINE == SUN_4) +#define ASKDEV 1 /* ask for boot device */ +#define FASTLOAD 1 /* use multiple block transfers to init ram */ +#endif + +#if (ATARI_TYPE == TT) /* and all other 68030's */ +#define FPP +#endif + +#ifndef FP_FORMAT +#define FP_FORMAT FP_NONE +#endif + +#ifndef MACHINE +error "In <minix/config.h> please define MACHINE" +#endif + +#ifndef CHIP +error "In <minix/config.h> please define MACHINE to have a legal value" +#endif + +#if (MACHINE == 0) +error "MACHINE has incorrect value (0)" +#endif + +#endif /* _CONFIG_H */ diff --git a/include/minix/const.h b/include/minix/const.h new file mode 100755 index 000000000..d9c796f9a --- /dev/null +++ b/include/minix/const.h @@ -0,0 +1,115 @@ +/* Copyright (C) 2001 by Prentice-Hall, Inc. See the copyright notice in + * the file /usr/src/LICENSE. + */ + +#ifndef CHIP +#error CHIP is not defined +#endif + +#define EXTERN extern /* used in *.h files */ +#define PRIVATE static /* PRIVATE x limits the scope of x */ +#define PUBLIC /* PUBLIC is the opposite of PRIVATE */ +#define FORWARD static /* some compilers require this to be 'static'*/ + +#define TRUE 1 /* used for turning integers into Booleans */ +#define FALSE 0 /* used for turning integers into Booleans */ + +#define HZ 60 /* clock freq (software settable on IBM-PC) */ + +#define SUPER_USER (uid_t) 0 /* uid_t of superuser */ + +#define MAJOR 8 /* major device = (dev>>MAJOR) & 0377 */ +#define MINOR 0 /* minor device = (dev>>MINOR) & 0377 */ + +#define NULL ((void *)0) /* null pointer */ +#define CPVEC_NR 16 /* max # of entries in a SYS_VCOPY request */ +#define CPVVEC_NR 64 /* max # of entries in a SYS_VCOPY request */ +#define NR_IOREQS MIN(NR_BUFS, 64) + /* maximum number of entries in an iorequest */ + +#define SEGMENT_TYPE 0xFF00 /* bit mask to get segment type */ +#define SEGMENT_INDEX 0x00FF /* bit mask to get segment index */ + +#define LOCAL_SEG 0x0000 /* flags indicating local memory segment */ +#define NR_LOCAL_SEGS 3 /* # local segments per process (fixed) */ +#define T 0 /* proc[i].mem_map[T] is for text */ +#define D 1 /* proc[i].mem_map[D] is for data */ +#define S 2 /* proc[i].mem_map[S] is for stack */ + +#define REMOTE_SEG 0x0100 /* flags indicating remote memory segment */ +#define NR_REMOTE_SEGS 3 /* # remote memory regions (variable) */ + +#define BIOS_SEG 0x0200 /* flags indicating BIOS memory segment */ +#define NR_BIOS_SEGS 3 /* # BIOS memory regions (variable) */ + +/* Labels used to disable code sections for different reasons. */ +#define DEAD_CODE 0 /* unused code in normal configuration */ +#define FUTURE_CODE 0 /* new code to be activated + tested later */ +#define TEMP_CODE 1 /* active code to be removed later */ + +/* Process name length in the process table, including '\0'. */ +#define PROC_NAME_LEN 16 + +/* Miscellaneous */ +#define BYTE 0377 /* mask for 8 bits */ +#define READING 0 /* copy data to user */ +#define WRITING 1 /* copy data from user */ +#define NO_NUM 0x8000 /* used as numerical argument to panic() */ +#define NIL_PTR (char *) 0 /* generally useful expression */ +#define HAVE_SCATTERED_IO 1 /* scattered I/O is now standard */ + +/* Macros. */ +#define MAX(a, b) ((a) > (b) ? (a) : (b)) +#define MIN(a, b) ((a) < (b) ? (a) : (b)) + +/* Memory is allocated in clicks. */ +#if (CHIP == INTEL) +#define CLICK_SIZE 1024 /* unit in which memory is allocated */ +#define CLICK_SHIFT 10 /* log2 of CLICK_SIZE */ +#endif + +#if (CHIP == SPARC) || (CHIP == M68000) +#define CLICK_SIZE 4096 /* unit in which memory is allocated */ +#define CLICK_SHIFT 12 /* log2 of CLICK_SIZE */ +#endif + +/* Click to byte conversions (and vice versa). */ +#define HCLICK_SHIFT 4 /* log2 of HCLICK_SIZE */ +#define HCLICK_SIZE 16 /* hardware segment conversion magic */ +#if CLICK_SIZE >= HCLICK_SIZE +#define click_to_hclick(n) ((n) << (CLICK_SHIFT - HCLICK_SHIFT)) +#else +#define click_to_hclick(n) ((n) >> (HCLICK_SHIFT - CLICK_SHIFT)) +#endif +#define hclick_to_physb(n) ((phys_bytes) (n) << HCLICK_SHIFT) +#define physb_to_hclick(n) ((n) >> HCLICK_SHIFT) + + +#define ABS -999 /* this process means absolute memory */ + +/* Flag bits for i_mode in the inode. */ +#define I_TYPE 0170000 /* this field gives inode type */ +#define I_REGULAR 0100000 /* regular file, not dir or special */ +#define I_BLOCK_SPECIAL 0060000 /* block special file */ +#define I_DIRECTORY 0040000 /* file is a directory */ +#define I_CHAR_SPECIAL 0020000 /* character special file */ +#define I_NAMED_PIPE 0010000 /* named pipe (FIFO) */ +#define I_SET_UID_BIT 0004000 /* set effective uid_t on exec */ +#define I_SET_GID_BIT 0002000 /* set effective gid_t on exec */ +#define ALL_MODES 0006777 /* all bits for user, group and others */ +#define RWX_MODES 0000777 /* mode bits for RWX only */ +#define R_BIT 0000004 /* Rwx protection bit */ +#define W_BIT 0000002 /* rWx protection bit */ +#define X_BIT 0000001 /* rwX protection bit */ +#define I_NOT_ALLOC 0000000 /* this inode is free */ + +/* Some limits. */ +#define MAX_BLOCK_NR ((block_t) 077777777) /* largest block number */ +#define HIGHEST_ZONE ((zone_t) 077777777) /* largest zone number */ +#define MAX_INODE_NR ((ino_t) 037777777777) /* largest inode number */ +#define MAX_FILE_POS ((off_t) 037777777777) /* largest legal file offset */ + +#define NO_BLOCK ((block_t) 0) /* absence of a block number */ +#define NO_ENTRY ((ino_t) 0) /* absence of a dir entry */ +#define NO_ZONE ((zone_t) 0) /* absence of a zone number */ +#define NO_DEV ((dev_t) 0) /* absence of a device numb */ diff --git a/include/minix/devio.h b/include/minix/devio.h new file mode 100644 index 000000000..04e2acc84 --- /dev/null +++ b/include/minix/devio.h @@ -0,0 +1,57 @@ +/* This file provides basic types and some constants for the + * SYS_DEVIO and SYS_VDEVIO system calls, which allow user-level + * processes to perform device I/O. + * + * Created: + * Apr 08, 2004 by Jorrit N. Herder + */ + +#ifndef _DEVIO_H +#define _DEVIO_H + +#include <minix/config.h> /* needed to include <minix/type.h> */ +#include <sys/types.h> /* u8_t, u16_t, u32_t needed */ + +/* We have different granularities of port I/O: 8, 16, 32 bits. + * Also see <ibm/portio.h>, which has functions for bytes, words, + * and longs. Hence, we need different (port,value)-pair types. + */ +typedef struct { u16_t port; u8_t value; } pvb_pair_t; +typedef struct { u16_t port; u16_t value; } pvw_pair_t; +typedef struct { u16_t port; u32_t value; } pvl_pair_t; + +/* Macro shorthand to set (port,value)-pair. */ +#define pv_set(pv, p, v) ((pv).port = (p), (pv).value = (v)) +#define pv_ptr_set(pv_ptr, p, v) ((pv_ptr)->port = (p), (pv_ptr)->value = (v)) + +#if 0 /* no longer in use !!! */ +/* Define a number of flags to indicate granularity we are using. */ +#define MASK_GRANULARITY 0x000F /* not in use! does not match flags */ +#define PVB_FLAG 'b' +#define PVW_FLAG 'w' +#define PVL_FLAG 'l' + +/* Flags indicating whether request wants to do input or output. */ +#define MASK_IN_OR_OUT 0x00F0 +#define DEVIO_INPUT 0x0010 +#define DEVIO_OUTPUT 0x0020 +#endif /* 0 */ + +#if 0 /* no longer used !!! */ +/* Define how large the (port,value)-pair buffer in the kernel is. + * This buffer is used to copy the (port,value)-pairs in kernel space. + */ +#define PV_BUF_SIZE 64 /* creates char pv_buf[PV_BUF_SIZE] */ + +/* Note that SYS_VDEVIO sends a pointer to a vector of (port,value)-pairs, + * whereas SYS_DEVIO includes a single (port,value)-pair in the messages. + * Calculate maximum number of (port,value)-pairs that can be handled + * in a single SYS_VDEVIO system call with above struct definitions. + */ +#define MAX_PVB_PAIRS ((PV_BUF_SIZE * sizeof(char)) / sizeof(pvb_pair_t)) +#define MAX_PVW_PAIRS ((PV_BUF_SIZE * sizeof(char)) / sizeof(pvw_pair_t)) +#define MAX_PVL_PAIRS ((PV_BUF_SIZE * sizeof(char)) / sizeof(pvl_pair_t)) +#endif /* 0 */ + + +#endif /* _DEVIO_H */ diff --git a/include/minix/dl_eth.h b/include/minix/dl_eth.h new file mode 100755 index 000000000..91a174b3e --- /dev/null +++ b/include/minix/dl_eth.h @@ -0,0 +1,26 @@ +/* The eth_stat struct is used in a DL_GETSTAT request the the ehw_task. */ + +#ifndef _ETH_HW_H +#define _ETH_HW_H + +typedef struct eth_stat +{ + unsigned long ets_recvErr, /* # receive errors */ + ets_sendErr, /* # send error */ + ets_OVW, /* # buffer overwrite warnings */ + ets_CRCerr, /* # crc errors of read */ + ets_frameAll, /* # frames not alligned (# bits % 8 != 0) */ + ets_missedP, /* # packets missed due to slow processing */ + ets_packetR, /* # packets received */ + ets_packetT, /* # packets transmitted */ + ets_transDef, /* # transmission defered (Tx was busy) */ + ets_collision, /* # collissions */ + ets_transAb, /* # Tx aborted due to excess collisions */ + ets_carrSense, /* # carrier sense lost */ + ets_fifoUnder, /* # FIFO underruns (processor too busy) */ + ets_fifoOver, /* # FIFO overruns (processor too busy) */ + ets_CDheartbeat, /* # times unable to transmit collision sig*/ + ets_OWC; /* # times out of window collision */ +} eth_stat_t; + +#endif /* _ETH_HW_H */ diff --git a/include/minix/fslib.h b/include/minix/fslib.h new file mode 100755 index 000000000..d57b73533 --- /dev/null +++ b/include/minix/fslib.h @@ -0,0 +1,11 @@ +/* V1 and V2 file system disk to/from memory support functions. */ + +_PROTOTYPE( int bitmapsize, (bit_t _nr_bits, int block_size) ); +_PROTOTYPE( unsigned conv2, (int _norm, int _w) ); +_PROTOTYPE( long conv4, (int _norm, long _x) ); +_PROTOTYPE( void conv_inode, (struct inode *_rip, d1_inode *_dip, + d2_inode *_dip2, int _rw_flag, int _magic) ); +_PROTOTYPE( void old_icopy, (struct inode *_rip, d1_inode *_dip, + int _direction, int _norm)); +_PROTOTYPE( void new_icopy, (struct inode *_rip, d2_inode *_dip, + int _direction, int _norm)); diff --git a/include/minix/ioctl.h b/include/minix/ioctl.h new file mode 100755 index 000000000..13ea29de4 --- /dev/null +++ b/include/minix/ioctl.h @@ -0,0 +1,44 @@ +/* minix/ioctl.h - Ioctl helper definitions. Author: Kees J. Bot + * 23 Nov 2002 + * + * This file is included by every header file that defines ioctl codes. + */ + +#ifndef _M_IOCTL_H +#define _M_IOCTL_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +#if _EM_WSIZE >= 4 +/* Ioctls have the command encoded in the low-order word, and the size + * of the parameter in the high-order word. The 3 high bits of the high- + * order word are used to encode the in/out/void status of the parameter. + */ +#define _IOCPARM_MASK 0x1FFF +#define _IOC_VOID 0x20000000 +#define _IOCTYPE_MASK 0xFFFF +#define _IOC_IN 0x40000000 +#define _IOC_OUT 0x80000000 +#define _IOC_INOUT (_IOC_IN | _IOC_OUT) + +#define _IO(x,y) ((x << 8) | y | _IOC_VOID) +#define _IOR(x,y,t) ((x << 8) | y | ((sizeof(t) & _IOCPARM_MASK) << 16) |\ + _IOC_OUT) +#define _IOW(x,y,t) ((x << 8) | y | ((sizeof(t) & _IOCPARM_MASK) << 16) |\ + _IOC_IN) +#define _IORW(x,y,t) ((x << 8) | y | ((sizeof(t) & _IOCPARM_MASK) << 16) |\ + _IOC_INOUT) +#else +/* No fancy encoding on a 16-bit machine. */ + +#define _IO(x,y) ((x << 8) | y) +#define _IOR(x,y,t) _IO(x,y) +#define _IOW(x,y,t) _IO(x,y) +#define _IORW(x,y,t) _IO(x,y) +#endif + +int ioctl(int _fd, int _request, void *_data); + +#endif /* _M_IOCTL_H */ diff --git a/include/minix/ipc.h b/include/minix/ipc.h new file mode 100644 index 000000000..d1646b90a --- /dev/null +++ b/include/minix/ipc.h @@ -0,0 +1,107 @@ +#ifndef _IPC_H +#define _IPC_H + +/*==========================================================================* + * Types relating to messages. * + *==========================================================================*/ + +#define M1 1 +#define M3 3 +#define M4 4 +#define M3_STRING 14 + +typedef struct {int m1i1, m1i2, m1i3; char *m1p1, *m1p2, *m1p3;} mess_1; +typedef struct {int m2i1, m2i2, m2i3; long m2l1, m2l2; char *m2p1;} mess_2; +typedef struct {int m3i1, m3i2; char *m3p1; char m3ca1[M3_STRING];} mess_3; +typedef struct {long m4l1, m4l2, m4l3, m4l4, m4l5;} mess_4; +typedef struct {short m5c1, m5c2; int m5i1, m5i2; long m5l1, m5l2, m5l3;}mess_5; +#if 0 +typedef struct {int m6i1, m6i2, m6i3; long m6l1; char *m6c1;} mess_6; +#endif +typedef struct {int m7i1, m7i2, m7i3, m7i4; char *m7p1, *m7p2;} mess_7; + +typedef struct { + int m_source; /* who sent the message */ + int m_type; /* what kind of message is it */ + union { + mess_1 m_m1; + mess_2 m_m2; + mess_3 m_m3; + mess_4 m_m4; + mess_5 m_m5; +#if 0 + mess_6 m_m6; +#endif + mess_7 m_m7; + } m_u; +} message; + +/* The following defines provide names for useful members. */ +#define m1_i1 m_u.m_m1.m1i1 +#define m1_i2 m_u.m_m1.m1i2 +#define m1_i3 m_u.m_m1.m1i3 +#define m1_p1 m_u.m_m1.m1p1 +#define m1_p2 m_u.m_m1.m1p2 +#define m1_p3 m_u.m_m1.m1p3 + +#define m2_i1 m_u.m_m2.m2i1 +#define m2_i2 m_u.m_m2.m2i2 +#define m2_i3 m_u.m_m2.m2i3 +#define m2_l1 m_u.m_m2.m2l1 +#define m2_l2 m_u.m_m2.m2l2 +#define m2_p1 m_u.m_m2.m2p1 + +#define m3_i1 m_u.m_m3.m3i1 +#define m3_i2 m_u.m_m3.m3i2 +#define m3_p1 m_u.m_m3.m3p1 +#define m3_ca1 m_u.m_m3.m3ca1 + +#define m4_l1 m_u.m_m4.m4l1 +#define m4_l2 m_u.m_m4.m4l2 +#define m4_l3 m_u.m_m4.m4l3 +#define m4_l4 m_u.m_m4.m4l4 +#define m4_l5 m_u.m_m4.m4l5 + +#define m5_c1 m_u.m_m5.m5c1 +#define m5_c2 m_u.m_m5.m5c2 +#define m5_i1 m_u.m_m5.m5i1 +#define m5_i2 m_u.m_m5.m5i2 +#define m5_l1 m_u.m_m5.m5l1 +#define m5_l2 m_u.m_m5.m5l2 +#define m5_l3 m_u.m_m5.m5l3 + +#if 0 +#define m6_i1 m_u.m_m6.m6i1 +#define m6_i2 m_u.m_m6.m6i2 +#define m6_i3 m_u.m_m6.m6i3 +#define m6_l1 m_u.m_m6.m6l1 +#define m6_c1 m_u.m_m6.m6c1 +#endif + +#define m7_i1 m_u.m_m7.m7i1 +#define m7_i2 m_u.m_m7.m7i2 +#define m7_i3 m_u.m_m7.m7i3 +#define m7_i4 m_u.m_m7.m7i4 +#define m7_p1 m_u.m_m7.m7p1 +#define m7_p2 m_u.m_m7.m7p2 + + +/*==========================================================================* + * Minix run-time system (IPC). * + *==========================================================================*/ + +/* Hide names to avoid name space pollution. */ +#define sendrec _sendrec +#define receive _receive +#define send _send +#define nb_receive _nb_receive +#define nb_send _nb_send + +_PROTOTYPE( int sendrec, (int src_dest, message *m_ptr) ); +_PROTOTYPE( int receive, (int src, message *m_ptr) ); +_PROTOTYPE( int send, (int dest, message *m_ptr) ); +_PROTOTYPE( int nb_receive, (int src, message *m_ptr) ); +_PROTOTYPE( int nb_send, (int dest, message *m_ptr) ); + + +#endif /* _IPC_H */ diff --git a/include/minix/jmp_buf.h b/include/minix/jmp_buf.h new file mode 100755 index 000000000..8a85c4eea --- /dev/null +++ b/include/minix/jmp_buf.h @@ -0,0 +1,87 @@ +/* This file is intended for use by assembly language programs that + * need to manipulate a jmp_buf. It may only be used by those systems + * for which a jmp_buf is identical to a struct sigcontext. + */ + +#ifndef _JMP_BUF_H +#define _JMP_BUF_H + +#if !defined(CHIP) +#include "error, configuration is not known" +#endif + +#if (CHIP == INTEL) +#if _WORD_SIZE == 4 +#define JB_FLAGS 0 +#define JB_MASK 4 +#define JB_GS 8 +#define JB_FS 10 +#define JB_ES 12 +#define JB_DS 14 +#define JB_DI 16 +#define JB_SI 20 +#define JB_BP 24 +#define JB_ST 28 +#define JB_BX 32 +#define JB_DX 36 +#define JB_CX 40 +#define JB_AX 44 +#define JB_RETADR 48 +#define JB_IP 52 +#define JB_CS 56 +#define JB_PSW 60 +#define JB_SP 64 +#define JB_SS 68 +#else /* _WORD_SIZE == 2 */ +#define JB_FLAGS 0 +#define JB_MASK 2 +#define JB_ES 6 +#define JB_DS 8 +#define JB_DI 10 +#define JB_SI 12 +#define JB_BP 14 +#define JB_ST 16 +#define JB_BX 18 +#define JB_DX 20 +#define JB_CX 22 +#define JB_AX 24 +#define JB_RETADR 26 +#define JB_IP 28 +#define JB_CS 30 +#define JB_PSW 32 +#define JB_SP 34 +#define JB_SS 36 +#endif /* _WORD_SIZE == 2 */ +#else /* !(CHIP == INTEL) */ +#if (CHIP == M68000) +#define JB_FLAGS 0 +#define JB_MASK 2 +#define JB_RETREG 6 +#define JB_D1 10 +#define JB_D2 14 +#define JB_D3 18 +#define JB_D4 22 +#define JB_D5 26 +#define JB_D6 20 +#define JB_D7 34 +#define JB_A0 38 +#define JB_A1 42 +#define JB_A2 46 +#define JB_A3 50 +#define JB_A4 54 +#define JB_A5 58 +#define JB_A6 62 +#define JB_SP 66 +#define JB_PC 70 +#define JB_PSW 74 +#else /* !(CHIP == INTEL) && !(CHIP == M68000) */ +#include "error, CHIP is not supported" +#endif /* (CHIP == INTEL) */ + +/* Defines from C headers needed in assembly code. The headers have too + * much C stuff to used directly. + */ +#define SIG_BLOCK 0 /* must agree with <signal.h> */ +#define SC_SIGCONTEXT 2 /* must agree with <sys/sigcontext.h> */ +#define SC_NOREGLOCALS 4 /* must agree with <sys/sigcontext.h> */ +#endif /* _JMP_BUF_H */ diff --git a/include/minix/keymap.h b/include/minix/keymap.h new file mode 100755 index 000000000..51cce8cbc --- /dev/null +++ b/include/minix/keymap.h @@ -0,0 +1,146 @@ +/* keymap.h - defines for keymapping Author: Marcus Hampel + */ +#ifndef _SYS__KEYMAP_H +#define _SYS__KEYMAP_H + +#define C(c) ((c) & 0x1F) /* Map to control code */ +#define A(c) ((c) | 0x80) /* Set eight bit (ALT) */ +#define CA(c) A(C(c)) /* Control-Alt */ +#define L(c) ((c) | HASCAPS) /* Add "Caps Lock has effect" attribute */ + +#define EXT 0x0100 /* Normal function keys */ +#define CTRL 0x0200 /* Control key */ +#define SHIFT 0x0400 /* Shift key */ +#define ALT 0x0800 /* Alternate key */ +#define EXTKEY 0x1000 /* extended keycode */ +#define HASCAPS 0x8000 /* Caps Lock has effect */ + +/* Scan code conversion. */ +#define KEY_RELEASE 0200 +#define ASCII_MASK 0177 + +/* Numeric keypad */ +#define HOME (0x01 + EXT) +#define END (0x02 + EXT) +#define UP (0x03 + EXT) +#define DOWN (0x04 + EXT) +#define LEFT (0x05 + EXT) +#define RIGHT (0x06 + EXT) +#define PGUP (0x07 + EXT) +#define PGDN (0x08 + EXT) +#define MID (0x09 + EXT) +#define NMIN (0x0A + EXT) +#define PLUS (0x0B + EXT) +#define INSRT (0x0C + EXT) + +/* Alt + Numeric keypad */ +#define AHOME (0x01 + ALT) +#define AEND (0x02 + ALT) +#define AUP (0x03 + ALT) +#define ADOWN (0x04 + ALT) +#define ALEFT (0x05 + ALT) +#define ARIGHT (0x06 + ALT) +#define APGUP (0x07 + ALT) +#define APGDN (0x08 + ALT) +#define AMID (0x09 + ALT) +#define ANMIN (0x0A + ALT) +#define APLUS (0x0B + ALT) +#define AINSRT (0x0C + ALT) + +/* Ctrl + Numeric keypad */ +#define CHOME (0x01 + CTRL) +#define CEND (0x02 + CTRL) +#define CUP (0x03 + CTRL) +#define CDOWN (0x04 + CTRL) +#define CLEFT (0x05 + CTRL) +#define CRIGHT (0x06 + CTRL) +#define CPGUP (0x07 + CTRL) +#define CPGDN (0x08 + CTRL) +#define CMID (0x09 + CTRL) +#define CNMIN (0x0A + CTRL) +#define CPLUS (0x0B + CTRL) +#define CINSRT (0x0C + CTRL) + +/* Lock keys */ +#define CALOCK (0x0D + EXT) /* caps lock */ +#define NLOCK (0x0E + EXT) /* number lock */ +#define SLOCK (0x0F + EXT) /* scroll lock */ + +/* Function keys */ +#define F1 (0x10 + EXT) +#define F2 (0x11 + EXT) +#define F3 (0x12 + EXT) +#define F4 (0x13 + EXT) +#define F5 (0x14 + EXT) +#define F6 (0x15 + EXT) +#define F7 (0x16 + EXT) +#define F8 (0x17 + EXT) +#define F9 (0x18 + EXT) +#define F10 (0x19 + EXT) +#define F11 (0x1A + EXT) +#define F12 (0x1B + EXT) + +/* Alt+Fn */ +#define AF1 (0x10 + ALT) +#define AF2 (0x11 + ALT) +#define AF3 (0x12 + ALT) +#define AF4 (0x13 + ALT) +#define AF5 (0x14 + ALT) +#define AF6 (0x15 + ALT) +#define AF7 (0x16 + ALT) +#define AF8 (0x17 + ALT) +#define AF9 (0x18 + ALT) +#define AF10 (0x19 + ALT) +#define AF11 (0x1A + ALT) +#define AF12 (0x1B + ALT) + +/* Ctrl+Fn */ +#define CF1 (0x10 + CTRL) +#define CF2 (0x11 + CTRL) +#define CF3 (0x12 + CTRL) +#define CF4 (0x13 + CTRL) +#define CF5 (0x14 + CTRL) +#define CF6 (0x15 + CTRL) +#define CF7 (0x16 + CTRL) +#define CF8 (0x17 + CTRL) +#define CF9 (0x18 + CTRL) +#define CF10 (0x19 + CTRL) +#define CF11 (0x1A + CTRL) +#define CF12 (0x1B + CTRL) + +/* Shift+Fn */ +#define SF1 (0x10 + SHIFT) +#define SF2 (0x11 + SHIFT) +#define SF3 (0x12 + SHIFT) +#define SF4 (0x13 + SHIFT) +#define SF5 (0x14 + SHIFT) +#define SF6 (0x15 + SHIFT) +#define SF7 (0x16 + SHIFT) +#define SF8 (0x17 + SHIFT) +#define SF9 (0x18 + SHIFT) +#define SF10 (0x19 + SHIFT) +#define SF11 (0x1A + SHIFT) +#define SF12 (0x1B + SHIFT) + +/* Alt+Shift+Fn */ +#define ASF1 (0x10 + ALT + SHIFT) +#define ASF2 (0x11 + ALT + SHIFT) +#define ASF3 (0x12 + ALT + SHIFT) +#define ASF4 (0x13 + ALT + SHIFT) +#define ASF5 (0x14 + ALT + SHIFT) +#define ASF6 (0x15 + ALT + SHIFT) +#define ASF7 (0x16 + ALT + SHIFT) +#define ASF8 (0x17 + ALT + SHIFT) +#define ASF9 (0x18 + ALT + SHIFT) +#define ASF10 (0x19 + ALT + SHIFT) +#define ASF11 (0x1A + ALT + SHIFT) +#define ASF12 (0x1B + ALT + SHIFT) + +#define MAP_COLS 6 /* Number of columns in keymap */ +#define NR_SCAN_CODES 0x80 /* Number of scan codes (rows in keymap) */ + +typedef unsigned short keymap_t[NR_SCAN_CODES * MAP_COLS]; + +#define KEY_MAGIC "KMAZ" /* Magic number of keymap file */ + +#endif /* _SYS__KEYMAP_H */ diff --git a/include/minix/minlib.h b/include/minix/minlib.h new file mode 100755 index 000000000..52379a808 --- /dev/null +++ b/include/minix/minlib.h @@ -0,0 +1,23 @@ +#ifndef _MINLIB +#define _MINLIB + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +/* Miscellaneous BSD. */ +_PROTOTYPE(void swab, (char *_from, char *_to, int _count)); +_PROTOTYPE(char *itoa, (int _n)); +_PROTOTYPE(char *getpass, (const char *_prompt)); + +/* Miscellaneous MINIX. */ +_PROTOTYPE(void std_err, (char *_s)); +_PROTOTYPE(void prints, (const char *_s, ...)); +_PROTOTYPE(int fsversion, (char *_dev, char *_prog)); +_PROTOTYPE(int getprocessor, (void)); +_PROTOTYPE(int load_mtab, (char *_prog_name)); +_PROTOTYPE(int rewrite_mtab, (char *_prog_name)); +_PROTOTYPE(int get_mtab_entry, (char *_s1, char *_s2, char *_s3, char *_s4)); +_PROTOTYPE(int put_mtab_entry, (char *_s1, char *_s2, char *_s3, char *_s4)); + +#endif diff --git a/include/minix/partition.h b/include/minix/partition.h new file mode 100755 index 000000000..7932c97e5 --- /dev/null +++ b/include/minix/partition.h @@ -0,0 +1,21 @@ +/* minix/partition.h Author: Kees J. Bot + * 7 Dec 1995 + * Place of a partition on disk and the disk geometry, + * for use with the DIOCGETP and DIOCSETP ioctl's. + */ +#ifndef _MINIX__PARTITION_H +#define _MINIX__PARTITION_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +struct partition { + u64_t base; /* byte offset to the partition start */ + u64_t size; /* number of bytes in the partition */ + unsigned cylinders; /* disk geometry */ + unsigned heads; + unsigned sectors; +}; + +#endif /* _MINIX__PARTITION_H */ diff --git a/include/minix/serverassert.h b/include/minix/serverassert.h new file mode 100644 index 000000000..ffdace313 --- /dev/null +++ b/include/minix/serverassert.h @@ -0,0 +1,36 @@ +#ifndef __SERVERASSERT_H +#define __SERVERASSERT_H + +/* This file contains functions and macros used for debugging within + * system servers. Also see <assert.h> which is used in regular programs. + */ + +#ifndef NDEBUG /* 8086 must do without training wheels. */ +#define NDEBUG (_WORD_SIZE == 2) +#endif + +#if !NDEBUG + +#define INIT_SERVER_ASSERT static char *server_assert_file= __FILE__; + +void server_assert_failed(char *file, int line, char *what); +void server_compare_failed(char *file, int line, int lhs, char *what, int rhs); + +#define server_assert(x) (!(x) ? server_assert_failed( \ + server_assert_file, __LINE__, #x) : (void) 0) +#define server_compare(a,t,b) (!((a) t (b)) ? server_compare_failed( \ + server_assert_file, __LINE__, (a), #a " " #t " " #b, (b)) : (void) 0) + + +#else /* NDEBUG */ + +#define INIT_SERVER_ASSERT /* nothing */ + +#define server_assert(x) (void) 0 +#define server_compare(a,t,b) (void) 0 + +#endif /* NDEBUG */ + + +#endif /* __SERVERASSERT_H */ + diff --git a/include/minix/sound.h b/include/minix/sound.h new file mode 100755 index 000000000..3f5835e98 --- /dev/null +++ b/include/minix/sound.h @@ -0,0 +1,46 @@ +/* Definitions used by /dev/audio and /dev/mixer. + * + * Feb 13 1995 Author: Michel R. Prevenier + */ + +#ifndef SOUND_H +#define SOUND_H + + +/* ------- Mixer stuff ------- */ + +/* Available devices */ +enum Device +{ + Master, /* Master volume */ + Dac, /* DSP, digitized sound */ + Fm, /* Fm synthesized sound */ + Cd, /* Compact */ + Line, /* Line in */ + Mic, /* Microphone */ + Speaker, /* Pc speaker */ + Treble, /* Treble */ + Bass /* Bass */ +}; + +enum InputState +{ + ON, OFF +}; + +/* Volume levels range from 0 to 31, bass & treble range from 0 to 15 */ +struct volume_level +{ + enum Device device; + int left; + int right; +}; + +struct inout_ctrl +{ + enum Device device; + enum InputState left; + enum InputState right; +}; + +#endif /* SOUND_H */ diff --git a/include/minix/swap.h b/include/minix/swap.h new file mode 100755 index 000000000..806e9dc56 --- /dev/null +++ b/include/minix/swap.h @@ -0,0 +1,48 @@ +/* +minix/swap.h + +Defines the super block of swap partitions and some useful constants. + +Created: Aug 2, 1992 by Philip Homburg +*/ + +#ifndef _MINIX__SWAP_H +#define _MINIX__SWAP_H + +/* Two possible layouts for a partition with swapspace: + * + * Sector Swap partition FS+swap partition + * + * 0 - 1 bootblock bootblock + * 2 swap header FS header + * 3 blank swap header + * 4 - m swapspace file system + * m+1 - n - swapspace + */ + +#define SWAP_MAGIC0 0x9D +#define SWAP_MAGIC1 0xC3 +#define SWAP_MAGIC2 0x01 +#define SWAP_MAGIC3 0x82 + +typedef struct swap_hdr +{ + u8_t sh_magic[4]; + u8_t sh_version; + u8_t sh_dummy[3]; + u32_t sh_offset; + u32_t sh_swapsize; + i32_t sh_priority; +} swap_hdr_t; + +#define SWAP_BOOTOFF 1024 +#define SWAP_OFFSET 2048 +#define OPTSWAP_BOOTOFF (1024+512) +#define SH_VERSION 1 +#define SH_PRIORITY 0 + +#endif /* _MINIX__SWAP_H */ + +/* + * $PchId: swap.h,v 1.6 1996/04/10 20:25:48 philip Exp $ + */ diff --git a/include/minix/syslib.h b/include/minix/syslib.h new file mode 100755 index 000000000..98264fffc --- /dev/null +++ b/include/minix/syslib.h @@ -0,0 +1,169 @@ +/* Prototypes for system library functions. + * + * Changes: + * Nov 15, 2004 unified sys_sigctl calls (Jorrit N. Herder) + * Oct 28, 2004 added nb_send, nb_receive (Jorrit N. Herder) + * Oct 26, 2004 added sys_sdevio (Jorrit N. Herder) + * Oct 18, 2004 added sys_irqctl (Jorrit N. Herder) + * Oct 10, 2004 removed sys_findproc (Jorrit N. Herder) + * Sep 23, 2004 added sys_getsig (Jorrit N. Herder) + * Sep 09, 2004 added sys_physcopy, sys_vircopy (Jorrit N. Herder) + * Sep 02, 2004 added sys_exit (Jorrit N. Herder) + * Aug 15, 2004 added sys_getinfo (Jorrit N. Herder) + * Jul 23, 2004 added sys_umap (Jorrit N. Herder) + * Jul 13, 2004 added sys_enable_iop, sys_phys2seg (Jorrit N. Herder) + * Mar 20, 2004 added sys_devio, sys_vdevio (Jorrit N. Herder) + */ + +#ifndef _SYSLIB_H +#define _SYSLIB_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +#ifndef _IPC_H +#include <minix/ipc.h> +#endif + +#ifndef _DEVIO_H +#include <minix/devio.h> +#endif + + +/*==========================================================================* + * Minix system library. * + *==========================================================================*/ +_PROTOTYPE( int printf, (const char *fmt, ...) ); +_PROTOTYPE( void kputc, (int c) ); +_PROTOTYPE( int _taskcall, (int who, int syscallnr, message *msgptr) ); + +_PROTOTYPE( int sys_abort, (int how, ...) ); +_PROTOTYPE( int sys_adjmap, (int proc, struct mem_map *ptr, + vir_clicks data_clicks, vir_clicks sp) ); +_PROTOTYPE( int sys_copy, (int src_proc, int src_seg, phys_bytes src_vir, + int dst_proc, int dst_seg, phys_bytes dst_vir, phys_bytes bytes)); +_PROTOTYPE( int sys_exec, (int proc, char *ptr, int traced, + char *aout, vir_bytes initpc) ); +_PROTOTYPE( int sys_execmap, (int proc, struct mem_map *ptr) ); +_PROTOTYPE( int sys_fork, (int parent, int child, int pid) ); +_PROTOTYPE( int sys_getsp, (int proc, vir_bytes *newsp) ); +_PROTOTYPE( int sys_newmap, (int proc, struct mem_map *ptr) ); +_PROTOTYPE( int sys_getmap, (int proc, struct mem_map *ptr) ); +_PROTOTYPE( int sys_times, (int proc_nr, clock_t *ptr) ); +_PROTOTYPE( int sys_getuptime, (clock_t *ticks) ); +_PROTOTYPE( int sys_trace, (int req, int proc, long addr, long *data_p) ); +_PROTOTYPE( int sys_xit, (int parent, int proc) ); +_PROTOTYPE( int sys_kill, (int proc, int sig) ); +_PROTOTYPE( int sys_svrctl, (int proc, int req, int priv,vir_bytes argp)); + + +/*==========================================================================* + * New prototypes since MINIX release 2 (Jorrit N. Herder) * + *==========================================================================*/ + +/* Shorthands for sys_sdevio() system call. */ +#define sys_insb(port, proc_nr, buffer, count) \ + sys_sdevio(DIO_INPUT, port, DIO_BYTE, proc_nr, buffer, count) +#define sys_insw(port, proc_nr, buffer, count) \ + sys_sdevio(DIO_INPUT, port, DIO_WORD, proc_nr, buffer, count) +#define sys_outsb(port, proc_nr, buffer, count) \ + sys_sdevio(DIO_OUTPUT, port, DIO_BYTE, proc_nr, buffer, count) +#define sys_outsw(port, proc_nr, buffer, count) \ + sys_sdevio(DIO_OUTPUT, port, DIO_WORD, proc_nr, buffer, count) +_PROTOTYPE( int sys_sdevio, (int req, long port, int type, int proc_nr, + void *buffer, int count) ); + +/* Clock functionality: (un)schedule an alarm call. */ +_PROTOTYPE(int sys_flagalrm, (clock_t ticks, int *flag_ptr) ); +_PROTOTYPE(int sys_signalrm, (int proc_nr, clock_t *ticks) ); +_PROTOTYPE(int sys_syncalrm, (int proc_nr, clock_t exp_time, int abs_time) ); + +/* Shorthands for sys_irqctl() system call. */ +#define sys_irqdisable(irq_vec) \ + sys_irqctl(IRQ_DISABLE, irq_vec, 0, 0, 0, 0, 0) +#define sys_irqenable(irq_vec) \ + sys_irqctl(IRQ_ENABLE, irq_vec, 0, 0, 0, 0, 0) +#define sys_irqsetpolicy(irq_vec, policy, proc_nr, port, val_ptr, mask_val) \ + sys_irqctl(IRQ_SETPOLICY, irq_vec, policy, proc_nr, port, val_ptr, mask_val) +_PROTOTYPE ( int sys_irqctl, (int request, int irq_vec, int policy, + int proc_nr, long port, void *val_ptr, long mask_val) ); + +/* Shorthands for sys_vircopy() system call. */ +#define sys_biosin(bios_vir, dst_vir, bytes) \ + sys_vircopy(SELF, BIOS_SEG, bios_vir, SELF, D, dst_vir, bytes) +#define sys_biosout(src_vir, bios_vir, bytes) \ + sys_vircopy(SELF, D, src_vir, SELF, BIOS_SEG, bios_vir, bytes) +#define sys_datacopy(src_proc, src_vir, dst_proc, dst_vir, bytes) \ + sys_vircopy(src_proc, D, src_vir, dst_proc, D, dst_vir, bytes) +#define sys_textcopy(src_proc, src_vir, dst_proc, dst_vir, bytes) \ + sys_vircopy(src_proc, T, src_vir, dst_proc, T, dst_vir, bytes) +#define sys_stackcopy(src_proc, src_vir, dst_proc, dst_vir, bytes) \ + sys_vircopy(src_proc, S, src_vir, dst_proc, S, dst_vir, bytes) +_PROTOTYPE(int sys_vircopy, (int src_proc, int src_seg, vir_bytes src_vir, + int dst_proc, int dst_seg, vir_bytes dst_vir, phys_bytes bytes) ); + +_PROTOTYPE(int sys_physcopy, (phys_bytes src_phys, phys_bytes dst_phys, + phys_bytes bytes) ); +_PROTOTYPE(int sys_umap, (int proc_nr, int seg, vir_bytes vir_addr, + vir_bytes bytes, phys_bytes *phys_addr) ); +_PROTOTYPE(int sys_phys2seg, (u16_t *seg,vir_bytes *off,phys_bytes phys, vir_bytes size)); +_PROTOTYPE(int sys_enable_iop, (int proc_nr) ); +_PROTOTYPE(int sys_kmalloc, (size_t size, phys_bytes *phys_base) ); + +/* Shorthands for sys_getinfo() system call. */ +#define sys_getkmessages(dst) sys_getinfo(GET_KMESSAGES, dst, 0,0,0) +#define sys_getkenviron(dst) sys_getinfo(GET_KENVIRON, dst, 0,0,0) +#define sys_getproctab(dst) sys_getinfo(GET_PROCTAB, dst, 0,0,0) +#define sys_getproc(dst,nr) sys_getinfo(GET_PROC, dst, 0,0, nr) +#define sys_getprocnr(dst,k,kl) sys_getinfo(GET_PROCNR, dst, 0,k,kl) +#define sys_getimage(dst) sys_getinfo(GET_IMAGE, dst, 0,0,0) +#define sys_getirqtab(dst) sys_getinfo(GET_IRQTAB, dst, 0,0,0) +#define sys_getmemchunks(dst) sys_getinfo(GET_MEMCHUNKS, dst, 0,0,0) +#define sys_getmonparams(v,vl) sys_getinfo(GET_MONPARAMS, v,vl, 0,0) +#define sys_getkenv(k,kl,v,vl) sys_getinfo(GET_KENV, v,vl, k,kl) +#define sys_getschedinfo(v1,v2) sys_getinfo(GET_SCHEDINFO, v1,0, v2,0) +#define sys_getkaddr(dst) sys_getinfo(GET_KADDRESSES, dst, 0,0,0) +_PROTOTYPE(int sys_getinfo, (int request, void *val_ptr, int val_len, + void *key_ptr, int key_len) ); +_PROTOTYPE(int sys_exit, (int status) ); + + +/* Shorthands for sys_sigctl() system call. */ +#define sys_getsig(proc_nr, sig_map) \ + sys_sigctl(S_GETSIG, 0,0,0, proc_nr, sig_map) +#define sys_endsig(proc_nr) \ + sys_sigctl(S_ENDSIG, proc_nr, 0,0,0,0) +#define sys_sendsig(proc_nr, sig_ctxt) \ + sys_sigctl(S_SENDSIG, proc_nr, sig_ctxt, 0,0,0) +#define sys_sigreturn(proc_nr, sig_ctxt, flags) \ + sys_sigctl(S_SIGRETURN, proc_nr, sig_ctxt, flags, 0,0) +_PROTOTYPE(int sys_sigctl, (int request, int proc_nr, struct sigmsg *sig_ctxt, + int flags, int *k_proc_nr, sigset_t *k_sig_map) ); + +/* NOTE: two different approaches were used to distinguish the device I/O + * types 'byte', 'word', 'long': the latter uses #define and results in a + * smaller implementation, but looses the static type checking. + */ +_PROTOTYPE(int sys_voutb, (pvb_pair_t *pvb_pairs, int nr_ports) ); +_PROTOTYPE(int sys_voutw, (pvw_pair_t *pvw_pairs, int nr_ports) ); +_PROTOTYPE(int sys_voutl, (pvl_pair_t *pvl_pairs, int nr_ports) ); +_PROTOTYPE(int sys_vinb, (pvb_pair_t *pvb_pairs, int nr_ports) ); +_PROTOTYPE(int sys_vinw, (pvw_pair_t *pvw_pairs, int nr_ports) ); +_PROTOTYPE(int sys_vinl, (pvl_pair_t *pvl_pairs, int nr_ports) ); + +/* Shorthands for sys_out() system call. */ +#define sys_outb(p,v) sys_out((p), (unsigned long) (v), DIO_BYTE) +#define sys_outw(p,v) sys_out((p), (unsigned long) (v), DIO_WORD) +#define sys_outl(p,v) sys_out((p), (unsigned long) (v), DIO_LONG) +_PROTOTYPE(int sys_out, (int port, unsigned long value, int type) ); + +/* Shorthands for sys_in() system call. */ +#define sys_inb(p,v) sys_in((p), (unsigned long*) (v), DIO_BYTE) +#define sys_inw(p,v) sys_in((p), (unsigned long*) (v), DIO_WORD) +#define sys_inl(p,v) sys_in((p), (unsigned long*) (v), DIO_LONG) +_PROTOTYPE(int sys_in, (int port, unsigned long *value, int type) ); + + +#endif /* _SYSLIB_H */ + diff --git a/include/minix/type.h b/include/minix/type.h new file mode 100755 index 000000000..2dd6236c4 --- /dev/null +++ b/include/minix/type.h @@ -0,0 +1,103 @@ +#ifndef _TYPE_H +#define _TYPE_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +/* Type definitions. */ +typedef unsigned int vir_clicks; /* virtual addresses and lengths in clicks */ +typedef unsigned long phys_bytes;/* physical addresses and lengths in bytes */ +typedef unsigned int phys_clicks;/* physical addresses and lengths in clicks */ + +#if (CHIP == INTEL) +typedef unsigned int vir_bytes; /* virtual addresses and lengths in bytes */ +#endif + +#if (CHIP == M68000) +typedef unsigned long vir_bytes;/* virtual addresses and lengths in bytes */ +#endif + +#if (CHIP == SPARC) +typedef unsigned long vir_bytes;/* virtual addresses and lengths in bytes */ +#endif + +struct mem_map { + vir_clicks mem_vir; /* virtual address */ + phys_clicks mem_phys; /* physical address */ + vir_clicks mem_len; /* length */ +}; + +struct far_mem { + phys_clicks mem_phys; /* physical address */ + vir_clicks mem_len; /* length */ +}; + +typedef struct { + vir_bytes iov_addr; /* address of an I/O buffer */ + vir_bytes iov_size; /* sizeof an I/O buffer */ +} iovec_t; + +typedef struct { + vir_bytes cpv_src; /* src address of data */ + vir_bytes cpv_dst; /* dst address of data */ + vir_bytes cpv_size; /* size of data */ +} cpvec_t; + +/* virtual copy vector */ +typedef struct { + int cpvv_src_seg, cpvv_dst_seg; /* src & dst segments */ + vir_bytes cpvv_src; /* src address of data */ + vir_bytes cpvv_dst; /* dst address of data */ + vir_bytes cpvv_size; /* size of data */ +} cpvvec_t; + +/* MM passes the address of a structure of this type to KERNEL when + * do_sendsig() is invoked as part of the signal catching mechanism. + * The structure contain all the information that KERNEL needs to build + * the signal stack. + */ +struct sigmsg { + int sm_signo; /* signal number being caught */ + unsigned long sm_mask; /* mask to restore when handler returns */ + vir_bytes sm_sighandler; /* address of handler */ + vir_bytes sm_sigreturn; /* address of _sigreturn in C library */ + vir_bytes sm_stkptr; /* user stack pointer */ +}; + +#define MESS_SIZE (sizeof(message)) /* might need usizeof from fs here */ +#define NIL_MESS ((message *) 0) + +struct psinfo { /* information for the ps(1) program */ + u16_t nr_tasks, nr_procs; /* NR_TASKS and NR_PROCS constants. */ + vir_bytes proc, mproc, fproc; /* addresses of the main process tables. */ +}; + +/* This is used to obtain system information through SYS_GETINFO. */ +struct kenviron { + int pc_at; + int ps_mca; + int processor; + int protected; + int ega; + int vga; + vir_bytes proc_addr; /* virtual address of process table */ + phys_bytes kmem_base; /* kernel memory layout (/dev/kmem) */ + phys_bytes kmem_size; + phys_bytes bootfs_base; /* FS image from boot image (/dev/boot) */ + phys_bytes bootfs_size; + phys_bytes params_base; /* parameters passed by boot monitor */ + phys_bytes params_size; +}; + +/* The kernel outputs messages in a local buffer, which can be requested and + * printed by the TTY driver. The buffer structure is defined here. + */ +struct kmessages { + int km_next; /* next index to write */ + int km_size; /* current size in buffer */ + char km_buf[KMESS_BUF_SIZE]; /* buffer for messages */ +}; + + +#endif /* _TYPE_H */ diff --git a/include/minix/u64.h b/include/minix/u64.h new file mode 100755 index 000000000..82a515a9c --- /dev/null +++ b/include/minix/u64.h @@ -0,0 +1,33 @@ +/* minix/u64.h Author: Kees J. Bot + * 7 Dec 1995 + * Functions to manipulate 64 bit disk addresses. + */ +#ifndef _MINIX__U64_H +#define _MINIX__U64_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +u64_t add64(u64_t i, u64_t j); +u64_t add64u(u64_t i, unsigned j); +u64_t add64ul(u64_t i, unsigned long j); +u64_t sub64(u64_t i, u64_t j); +u64_t sub64u(u64_t i, unsigned j); +u64_t sub64ul(u64_t i, unsigned long j); +unsigned diff64(u64_t i, u64_t j); +u64_t cvu64(unsigned i); +u64_t cvul64(unsigned long i); +unsigned cv64u(u64_t i); +unsigned long cv64ul(u64_t i); +unsigned long div64u(u64_t i, unsigned j); +unsigned rem64u(u64_t i, unsigned j); +u64_t mul64u(unsigned long i, unsigned j); +int cmp64(u64_t i, u64_t j); +int cmp64u(u64_t i, unsigned j); +int cmp64ul(u64_t i, unsigned long j); +unsigned long ex64lo(u64_t i); +unsigned long ex64hi(u64_t i); +u64_t make64(unsigned long lo, unsigned long hi); + +#endif /* _MINIX__U64_H */ diff --git a/include/minix/utils.h b/include/minix/utils.h new file mode 100644 index 000000000..1cfe1e35d --- /dev/null +++ b/include/minix/utils.h @@ -0,0 +1,52 @@ +#ifndef _EXTRALIB_H +#define _EXTRALIB_H + +/* Extra system library definitions to support device drivers and servers. + * + * Created: + * Mar 15, 2004 by Jorrit N. Herder + * + * Changes: + * Mar 18, 2005: added tick_delay + * Mar 15, 2005: added get_proc_nr + * Oct 01, 2004: added env_parse, env_prefix, env_panic + * Jul 13, 2004: added fkey_ctl + * Apr 28, 2004: added server_report, server_panic, server_assert + * Mar 31, 2004: setup like other libraries, such as syslib + */ + + +/*==========================================================================* + * Miscellaneous helper functions. + *==========================================================================*/ + +#include <minix/serverassert.h> + +/* Environment parsing return values. */ +#define EP_BUF_SIZE 128 /* local buffer for env value */ +#define EP_UNSET 0 /* variable not set */ +#define EP_OFF 1 /* var = off */ +#define EP_ON 2 /* var = on (or field left blank) */ +#define EP_SET 3 /* var = 1:2:3 (nonblank field) */ +#define EP_EGETKENV 4 /* sys_getkenv() failed ... */ + +_PROTOTYPE(int get_mon_param, (char *key, char *value, int max_size) ); +_PROTOTYPE(int env_prefix, (char *env, char *prefix) ); +_PROTOTYPE(void env_panic, (char *key) ); +_PROTOTYPE(int env_parse, (char *env, char *fmt, int field, long *param, + long min, long max) ); + +#define fkey_enable(fkey) fkey_ctl(fkey, 1) +#define fkey_disable(fkey) fkey_ctl(fkey, 0) +_PROTOTYPE(int fkey_ctl, (int fkey_code, int enable_disable) ); + +_PROTOTYPE(void server_report, (char *who, char *mess, int num) ); +_PROTOTYPE(void server_panic, (char *who, char *mess, int num) ); + +#define get_own_proc_nr(where) get_proc_nr((where), NULL) +_PROTOTYPE(int get_proc_nr, (int *proc_nr, char *proc_name) ); + +_PROTOTYPE(int tick_delay, (clock_t ticks)); + +#endif /* _EXTRALIB_H */ + diff --git a/include/net/gen/dhcp.h b/include/net/gen/dhcp.h new file mode 100755 index 000000000..d782e341c --- /dev/null +++ b/include/net/gen/dhcp.h @@ -0,0 +1,73 @@ +/* net/gen/dhcp.h - DHCP packet format Author: Kees J. Bot + * 1 Dec 2000 + */ + +#ifndef __NET__GEN__DHCP_H__ +#define __NET__GEN__DHCP_H__ + +typedef struct dhcp { + u8_t op; /* Message opcode/type. */ + u8_t htype; /* Hardware address type. */ + u8_t hlen; /* Hardware address length. */ + u8_t hops; /* Hop count when relaying. */ + u32_t xid; /* Transaction ID. */ + u16_t secs; /* Seconds past since client began. */ + u16_t flags; /* Flags. */ + ipaddr_t ciaddr; /* Client IP address. */ + ipaddr_t yiaddr; /* "Your" IP address. */ + ipaddr_t siaddr; /* Boot server IP address. */ + ipaddr_t giaddr; /* Relay agent (gateway) IP address. */ + u8_t chaddr[16]; /* Client hardware address. */ + u8_t sname[64]; /* Server host name. */ + u8_t file[128]; /* Boot file. */ + u32_t magic; /* Magic number. */ + u8_t options[308]; /* Optional parameters. */ +} dhcp_t; + +/* DHCP operations and stuff: */ +#define DHCP_BOOTREQUEST 1 /* Boot request message. */ +#define DHCP_BOOTREPLY 2 /* Boot reply message. */ +#define DHCP_HTYPE_ETH 1 /* Ethernet hardware type. */ +#define DHCP_HLEN_ETH 6 /* Ethernet hardware address length. */ +#define DHCP_FLAGS_BCAST 0x8000U /* Reply must be broadcast to client. */ + + /* "Magic" first four option bytes. */ +#define DHCP_MAGIC HTONL(0x63825363UL) + +/* DHCP common tags: */ +#define DHCP_TAG_NETMASK 1 /* Netmask. */ +#define DHCP_TAG_GATEWAY 3 /* Gateway list. */ +#define DHCP_TAG_DNS 6 /* DNS Nameserver list. */ +#define DHCP_TAG_HOSTNAME 12 /* Host name. */ +#define DHCP_TAG_DOMAIN 15 /* Domain. */ +#define DHCP_TAG_IPMTU 26 /* Interface MTU. */ + +/* DHCP protocol tags: */ +#define DHCP_TAG_REQIP 50 /* Request this IP. */ +#define DHCP_TAG_LEASE 51 /* Lease time requested/offered. */ +#define DHCP_TAG_OVERLOAD 52 /* Options continued in file/sname. */ +#define DHCP_TAG_TYPE 53 /* DHCP message (values below). */ +#define DHCP_TAG_SERVERID 54 /* Server identifier. */ +#define DHCP_TAG_REQPAR 55 /* Parameters requested. */ +#define DHCP_TAG_MESSAGE 56 /* Error message. */ +#define DHCP_TAG_MAXDHCP 57 /* Max DHCP packet size. */ +#define DHCP_TAG_RENEWAL 58 /* Time to go into renewal state. */ +#define DHCP_TAG_REBINDING 59 /* Time to go into rebinding state. */ +#define DHCP_TAG_CLASSID 60 /* Class identifier. */ +#define DHCP_TAG_CLIENTID 61 /* Client identifier. */ + +/* DHCP messages: */ +#define DHCP_DISCOVER 1 /* Locate available servers. */ +#define DHCP_OFFER 2 /* Parameters offered to client. */ +#define DHCP_REQUEST 3 /* (Re)request offered parameters. */ +#define DHCP_DECLINE 4 /* Client declines offer. */ +#define DHCP_ACK 5 /* Server acknowlegdes request. */ +#define DHCP_NAK 6 /* Server denies request. */ +#define DHCP_RELEASE 7 /* Client relinguishes address. */ +#define DHCP_INFORM 8 /* Client requests just local config. */ + +void dhcp_init(dhcp_t *_dp); +int dhcp_settag(dhcp_t *_dp, int _tag, void *_data, size_t _len); +int dhcp_gettag(dhcp_t *_dp, int _searchtag, u8_t **_pdata, size_t *_plen); + +#endif /* __NET__GEN__DHCP_H__ */ diff --git a/include/net/gen/emu.h b/include/net/gen/emu.h new file mode 100644 index 000000000..a020290d8 --- /dev/null +++ b/include/net/gen/emu.h @@ -0,0 +1,59 @@ +/* +emu.h - bsd sockets lookalike interface +*/ + +#ifndef __EMU_H__ +#define __EMU_H__ + +typedef int socklen_t; +typedef int sa_family_t; +typedef int in_port_t; +typedef unsigned long sin_addr_t; +typedef unsigned long in_addr_t; + +/* generic socket address */ +struct sockaddr { + unsigned char sa_len; /* total address size */ + sa_family_t sa_family; /* address type (family) */ + char sa_data[14]; /* family-dependent addr (may be longer) */ +}; + +struct in_addr { + in_addr_t s_addr; +}; + +/* internet-domain (AF_INET) socket address */ +struct sockaddr_in { + unsigned char sin_len; + sa_family_t sin_family; + in_port_t sin_port; + struct in_addr sin_addr; + char sin_zero[8]; +}; + +/* type argument to socket() */ +#define SOCK_STREAM 2 +#define SOCK_DGRAM 3 + +/* protocol argument to socket() */ +#define IPPROTO_ICMP 1 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 + +/* 2nd args to shutdown() */ +#define SHUT_RD 0 +#define SHUT_WR 1 +#define SHUT_RDWR 2 + +/* bsd-lookalike functions */ +int socket(int, int, int); +int bind(int, struct sockaddr *, socklen_t); +int listen(int, int); +int shutdown(int, int); +int connect(int, struct sockaddr *, socklen_t); + +int recv(int s, void *buf, size_t len, int flags); +int send(int s, void *buf, size_t len, int flags); + +#endif /* __EMU_H__ */ + diff --git a/include/net/gen/eth_hdr.h b/include/net/gen/eth_hdr.h new file mode 100755 index 000000000..7b7bb9cbb --- /dev/null +++ b/include/net/gen/eth_hdr.h @@ -0,0 +1,15 @@ +/* +server/ip/gen/eth_hdr.h +*/ + +#ifndef __SERVER__IP__GEN__ETH_HDR_H__ +#define __SERVER__IP__GEN__ETH_HDR_H__ + +typedef struct eth_hdr +{ + ether_addr_t eh_dst; + ether_addr_t eh_src; + ether_type_t eh_proto; +} eth_hdr_t; + +#endif /* __SERVER__IP__GEN__ETH_HDR_H__ */ diff --git a/include/net/gen/eth_io.h b/include/net/gen/eth_io.h new file mode 100755 index 000000000..9358f71c3 --- /dev/null +++ b/include/net/gen/eth_io.h @@ -0,0 +1,79 @@ +/* +server/gen/ip/eth_io.h +*/ + +#ifndef __SERVER__IP__GEN__ETH_IO_H__ +#define __SERVER__IP__GEN__ETH_IO_H__ + +typedef struct nwio_ethopt +{ + u32_t nweo_flags; + ether_addr_t nweo_multi, nweo_rem; + ether_type_t nweo_type; +} nwio_ethopt_t; + +#define NWEO_NOFLAGS 0x0000L +#define NWEO_ACC_MASK 0x0003L +# define NWEO_EXCL 0x00000001L +# define NWEO_SHARED 0x00000002L +# define NWEO_COPY 0x00000003L +#define NWEO_LOC_MASK 0x0010L +# define NWEO_EN_LOC 0x00000010L +# define NWEO_DI_LOC 0x00100000L +#define NWEO_BROAD_MASK 0x0020L +# define NWEO_EN_BROAD 0x00000020L +# define NWEO_DI_BROAD 0x00200000L +#define NWEO_MULTI_MASK 0x0040L +# define NWEO_EN_MULTI 0x00000040L +# define NWEO_DI_MULTI 0x00400000L +#define NWEO_PROMISC_MASK 0x0080L +# define NWEO_EN_PROMISC 0x00000080L +# define NWEO_DI_PROMISC 0x00800000L +#define NWEO_REM_MASK 0x0100L +# define NWEO_REMSPEC 0x00000100L +# define NWEO_REMANY 0x01000000L +#define NWEO_TYPE_MASK 0x0200L +# define NWEO_TYPESPEC 0x00000200L +# define NWEO_TYPEANY 0x02000000L +#define NWEO_RW_MASK 0x1000L +# define NWEO_RWDATONLY 0x00001000L +# define NWEO_RWDATALL 0x10000000L + +typedef struct eth_stat +{ + unsigned long ets_recvErr, /* # receive errors */ + ets_sendErr, /* # send error */ + ets_OVW, /* # buffer overwrite warnings, + (packets arrive faster than + can be processed) */ + ets_CRCerr, /* # crc errors of read */ + ets_frameAll, /* # frames not alligned (# bits + not a mutiple of 8) */ + ets_missedP, /* # packets missed due to too + slow packet processing */ + ets_packetR, /* # packets received */ + ets_packetT, /* # packets transmitted */ + ets_transDef, /* # transmission defered (there + was a transmission of an + other station in progress */ + ets_collision, /* # collissions */ + ets_transAb, /* # transmissions aborted due + to accesive collisions */ + ets_carrSense, /* # carrier sense lost */ + ets_fifoUnder, /* # fifo underruns (processor + is too busy) */ + ets_fifoOver, /* # fifo overruns (processor is + too busy) */ + ets_CDheartbeat, /* # times unable to transmit + collision signal */ + ets_OWC; /* # times out of window + collision */ +} eth_stat_t; + +typedef struct nwio_ethstat +{ + ether_addr_t nwes_addr; + eth_stat_t nwes_stat; +} nwio_ethstat_t; + +#endif /* __SERVER__IP__GEN__ETH_IO_H__ */ diff --git a/include/net/gen/ether.h b/include/net/gen/ether.h new file mode 100755 index 000000000..d2ccc3566 --- /dev/null +++ b/include/net/gen/ether.h @@ -0,0 +1,25 @@ +/* +server/ip/gen/ether.h +*/ + +#ifndef __SERVER__IP__GEN__ETHER_H__ +#define __SERVER__IP__GEN__ETHER_H__ + +#define ETH_MIN_PACK_SIZE 60 +#define ETH_MAX_PACK_SIZE 1514 +#define ETH_MAX_PACK_SIZE_TAGGED 1518 +#define ETH_HDR_SIZE 14 +#define ETH_CRC_SIZE 4 + +typedef struct ether_addr +{ + u8_t ea_addr[6]; +} ether_addr_t; + +typedef u16_t ether_type_t; +typedef U16_t Ether_type_t; + +#define ETH_ARP_PROTO 0x806 +#define ETH_IP_PROTO 0x800 + +#endif /* __SERVER__IP__GEN__ETHER_H__ */ diff --git a/include/net/gen/icmp.h b/include/net/gen/icmp.h new file mode 100755 index 000000000..cb3ae37b4 --- /dev/null +++ b/include/net/gen/icmp.h @@ -0,0 +1,40 @@ +/* +server/ip/gen/icmp.h +*/ + +#ifndef __SERVER__IP__GEN__ICMP_H__ +#define __SERVER__IP__GEN__ICMP_H__ + +#define ICMP_MIN_HDR_LEN 4 + +#define ICMP_TYPE_ECHO_REPL 0 +#define ICMP_TYPE_DST_UNRCH 3 +# define ICMP_NET_UNRCH 0 +# define ICMP_HOST_UNRCH 1 +# define ICMP_PROTOCOL_UNRCH 2 +# define ICMP_PORT_UNRCH 3 +# define ICMP_FRAGM_AND_DF 4 +# define ICMP_SOURCE_ROUTE_FAILED 5 +#define ICMP_TYPE_SRC_QUENCH 4 +#define ICMP_TYPE_REDIRECT 5 +# define ICMP_REDIRECT_NET 0 +# define ICMP_REDIRECT_HOST 1 +# define ICMP_REDIRECT_TOS_AND_NET 2 +# define ICMP_REDIRECT_TOS_AND_HOST 3 +#define ICMP_TYPE_ECHO_REQ 8 +#define ICMP_TYPE_ROUTER_ADVER 9 +#define ICMP_TYPE_ROUTE_SOL 10 +#define ICMP_TYPE_TIME_EXCEEDED 11 +# define ICMP_TTL_EXC 0 +# define ICMP_FRAG_REASSEM 1 +#define ICMP_TYPE_PARAM_PROBLEM 12 +#define ICMP_TYPE_TS_REQ 13 +#define ICMP_TYPE_TS_REPL 14 +#define ICMP_TYPE_INFO_REQ 15 +#define ICMP_TYPE_INFO_REPL 16 + +#endif /* __SERVER__IP__GEN__ICMP_H__ */ + +/* + * $PchId: icmp.h,v 1.5 1995/11/17 22:38:46 philip Exp $ + */ diff --git a/include/net/gen/icmp_hdr.h b/include/net/gen/icmp_hdr.h new file mode 100755 index 000000000..dbd5dd0f0 --- /dev/null +++ b/include/net/gen/icmp_hdr.h @@ -0,0 +1,55 @@ +/* +server/ip/gen/icmp_hdr.h +*/ + +#ifndef __SERVER__IP__GEN__ICMP_HDR_H__ +#define __SERVER__IP__GEN__ICMP_HDR_H__ + +typedef struct icmp_id_seq +{ + u16_t iis_id, iis_seq; +} icmp_id_seq_t; + +typedef struct icmp_ip_id +{ + ip_hdr_t iii_hdr; + /* ip_hdr_options and 64 bytes of data */ +} icmp_ip_id_t; + +typedef struct icmp_ram /* RFC 1256 */ +{ + u8_t iram_na; + u8_t iram_aes; + u16_t iram_lt; +} icmp_ram_t; + +typedef struct icmp_pp +{ + u8_t ipp_ptr; + u8_t ipp_unused[3]; +} icmp_pp_t; + +typedef struct icmp_hdr +{ + u8_t ih_type, ih_code; + u16_t ih_chksum; + union + { + u32_t ihh_unused; + icmp_id_seq_t ihh_idseq; + ipaddr_t ihh_gateway; + icmp_ram_t ihh_ram; + icmp_pp_t ihh_pp; + } ih_hun; + union + { + icmp_ip_id_t ihd_ipid; + u8_t uhd_data[1]; + } ih_dun; +} icmp_hdr_t; + +#endif /* __SERVER__IP__GEN__ICMP_HDR_H__ */ + +/* + * $PchId: icmp_hdr.h,v 1.4 1995/11/17 22:28:58 philip Exp $ + */ diff --git a/include/net/gen/if_ether.h b/include/net/gen/if_ether.h new file mode 100755 index 000000000..f99e348e7 --- /dev/null +++ b/include/net/gen/if_ether.h @@ -0,0 +1,18 @@ +/* +server/ip/gen/if_ether.h +*/ + +#ifndef __SERVER__IP__GEN__IF_ETHER_H__ +#define __SERVER__IP__GEN__IF_ETHER_H__ + +struct ether_addr; + +#define _PATH_ETHERS "/etc/ethers" + +char *ether_ntoa _ARGS(( struct ether_addr *e )); +struct ether_addr *ether_aton _ARGS(( const char *s )); +int ether_ntohost _ARGS(( char *hostname, struct ether_addr *e )); +int ether_hostton _ARGS(( char *hostname, struct ether_addr *e )); +int ether_line _ARGS(( char *l, struct ether_addr *e, char *hostname )); + +#endif /* __SERVER__IP__GEN__IF_ETHER_H__ */ diff --git a/include/net/gen/in.h b/include/net/gen/in.h new file mode 100755 index 000000000..d86ee4e60 --- /dev/null +++ b/include/net/gen/in.h @@ -0,0 +1,33 @@ +/* +server/ip/gen/in.h +*/ + +#ifndef __SERVER__IP__GEN__IN_H__ +#define __SERVER__IP__GEN__IN_H__ + +#define IP_MIN_HDR_SIZE 20 +#define IP_MAX_HDR_SIZE 60 /* 15 * 4 */ +#define IP_VERSION 4 +#define IP_MAX_TTL 255 +#define IP_DEF_MSS 576 +#define IP_MAX_PACKSIZE 40000 /* 8192 */ + /* Note: this restriction is not part of the IP-protocol but + introduced by this implementation. */ + +#define IPPROTO_ICMP 1 +#define IPPROTO_TCP 6 +#define IPPROTO_UDP 17 + +typedef u32_t ipaddr_t; +typedef u8_t ipproto_t; +typedef struct ip_hdropt +{ + u8_t iho_opt_siz; + u8_t iho_data[IP_MAX_HDR_SIZE-IP_MIN_HDR_SIZE]; +} ip_hdropt_t; + +#endif /* __SERVER__IP__GEN__IN_H__ */ + +/* + * $PchId: in.h,v 1.3 1995/11/17 22:27:50 philip Exp $ + */ diff --git a/include/net/gen/inet.h b/include/net/gen/inet.h new file mode 100755 index 000000000..a35ececa7 --- /dev/null +++ b/include/net/gen/inet.h @@ -0,0 +1,13 @@ +/* +server/ip/gen/inet.h +*/ + +#ifndef __SERVER__IP__GEN__INET_H__ +#define __SERVER__IP__GEN__INET_H__ + +ipaddr_t inet_addr _ARGS(( const char *addr )); +ipaddr_t inet_network _ARGS(( const char *addr )); +char *inet_ntoa _ARGS(( ipaddr_t addr )); +int inet_aton _ARGS(( const char *cp, ipaddr_t *pin )); + +#endif /* __SERVER__IP__GEN__INET_H__ */ diff --git a/include/net/gen/ip_hdr.h b/include/net/gen/ip_hdr.h new file mode 100755 index 000000000..ba1c5336c --- /dev/null +++ b/include/net/gen/ip_hdr.h @@ -0,0 +1,42 @@ +/* +server/ip/gen/ip_hdr.h +*/ + +#ifndef __SERVER__IP__GEN__HDR_H__ +#define __SERVER__IP__GEN__HDR_H__ + +typedef struct ip_hdr +{ + u8_t ih_vers_ihl, + ih_tos; + u16_t ih_length, + ih_id, + ih_flags_fragoff; + u8_t ih_ttl, + ih_proto; + u16_t ih_hdr_chk; + ipaddr_t ih_src, + ih_dst; +} ip_hdr_t; + +#define IH_IHL_MASK 0xf +#define IH_VERSION_MASK 0xf +#define IH_FRAGOFF_MASK 0x1fff +#define IH_MORE_FRAGS 0x2000 +#define IH_DONT_FRAG 0x4000 +#define IH_FLAGS_UNUSED 0x8000 + +#define IP_OPT_COPIED 0x80 +#define IP_OPT_NUMBER 0x1f + +#define IP_OPT_EOL 0x00 +#define IP_OPT_NOP 0x01 +#define IP_OPT_LSRR 0x83 +#define IP_OPT_RR 0x07 +#define IP_OPT_RR_MIN 4 + +#endif /* __SERVER__IP__GEN__HDR_H__ */ + +/* + * $PchId: ip_hdr.h,v 1.4 1995/11/17 22:26:00 philip Exp $ + */ diff --git a/include/net/gen/ip_io.h b/include/net/gen/ip_io.h new file mode 100755 index 000000000..97fac609a --- /dev/null +++ b/include/net/gen/ip_io.h @@ -0,0 +1,55 @@ +/* +server/ip/gen/ip_io.h +*/ + +#ifndef __SERVER__IP__GEN__IP_IO_H__ +#define __SERVER__IP__GEN__IP_IO_H__ + +typedef struct nwio_ipconf +{ + u32_t nwic_flags; + ipaddr_t nwic_ipaddr; + ipaddr_t nwic_netmask; +} nwio_ipconf_t; + +#define NWIC_NOFLAGS 0x0 +#define NWIC_FLAGS 0x3 +# define NWIC_IPADDR_SET 0x1 +# define NWIC_NETMASK_SET 0x2 + +typedef struct nwio_ipopt +{ + u32_t nwio_flags; + ipaddr_t nwio_rem; + ip_hdropt_t nwio_hdropt; + u8_t nwio_tos; + u8_t nwio_ttl; + u8_t nwio_df; + ipproto_t nwio_proto; +} nwio_ipopt_t; + +#define NWIO_NOFLAGS 0x0000l +#define NWIO_ACC_MASK 0x0003l +# define NWIO_EXCL 0x00000001l +# define NWIO_SHARED 0x00000002l +# define NWIO_COPY 0x00000003l +#define NWIO_LOC_MASK 0x0010l +# define NWIO_EN_LOC 0x00000010l +# define NWIO_DI_LOC 0x00100000l +#define NWIO_BROAD_MASK 0x0020l +# define NWIO_EN_BROAD 0x00000020l +# define NWIO_DI_BROAD 0x00200000l +#define NWIO_REM_MASK 0x0100l +# define NWIO_REMSPEC 0x00000100l +# define NWIO_REMANY 0x01000000l +#define NWIO_PROTO_MASK 0x0200l +# define NWIO_PROTOSPEC 0x00000200l +# define NWIO_PROTOANY 0x02000000l +#define NWIO_HDR_O_MASK 0x0400l +# define NWIO_HDR_O_SPEC 0x00000400l +# define NWIO_HDR_O_ANY 0x04000000l +#define NWIO_RW_MASK 0x1000l +# define NWIO_RWDATONLY 0x00001000l +# define NWIO_RWDATALL 0x10000000l + +#endif /* __SERVER__IP__GEN__IP_IO_H__ */ diff --git a/include/net/gen/nameser.h b/include/net/gen/nameser.h new file mode 100755 index 000000000..d7b61b37b --- /dev/null +++ b/include/net/gen/nameser.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1983, 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#)nameser.h 5.24 (Berkeley) 6/1/90 + */ + +/* +server/ip/gen/nameser.h + +Created Sept 18, 1991 by Philip Homburg +*/ + +#ifndef __SERVER__IP__GEN__NAEMSER_H__ +#define __SERVER__IP__GEN__NAEMSER_H__ + +typedef struct dns_hdr +{ + u16_t dh_id; + u8_t dh_flag1; + u8_t dh_flag2; + u16_t dh_qdcount; + u16_t dh_ancount; + u16_t dh_nscount; + u16_t dh_arcount; +} dns_hdr_t; + +#define DHF_QR 0x80 +#define DHF_OPCODE 0x78 +#define DHF_AA 0x04 +#define DHF_TC 0x02 +#define DHF_RD 0x01 + +#define DHF_RA 0x80 +#define DHF_PR 0x40 +#define DHF_UNUSED 0x30 +#define DHF_RCODE 0x0F + + +/* +Define constants based on rfc883 +*/ +#define PACKETSZ 512 /* maximum packet size */ +#define MAXDNAME 256 /* maximum domain name */ +#define MAXCDNAME 255 /* maximum compressed domain name */ +#define MAXLABEL 63 /* maximum length of domain label */ + /* Number of bytes of fixed size data in query structure */ +#define QFIXEDSZ 4 + /* number of bytes of fixed size data in resource record */ +#define RRFIXEDSZ 10 +#define INDIR_MASK 0xc0 + /* Defines for handling compressed domain names */ + +/* +Opcodes for DNS +*/ + +#define QUERY 0x0 /* standard query */ +#define IQUERY 0x1 /* inverse query */ + +/* +Error codes +*/ +#define NOERROR 0 /* no error */ +#define FORMERR 1 /* format error */ +#define SERVFAIL 2 /* server failure */ +#define NXDOMAIN 3 /* non existent domain */ +#define NOTIMP 4 /* not implemented */ +#define REFUSED 5 /* query refused */ + /* non standard */ +#define NOCHANGE 0xf /* update failed to change db */ + +/* Valid types */ + +#define T_A 1 /* host address */ +#define T_NS 2 /* authoritative server */ +#define T_MD 3 /* mail destination */ +#define T_MF 4 /* mail forwarder */ +#define T_CNAME 5 /* connonical name */ +#define T_SOA 6 /* start of authority zone */ +#define T_MB 7 /* mailbox domain name */ +#define T_MG 8 /* mail group member */ +#define T_MR 9 /* mail rename name */ +#define T_NULL 10 /* null resource record */ +#define T_WKS 11 /* well known service */ +#define T_PTR 12 /* domain name pointer */ +#define T_HINFO 13 /* host information */ +#define T_MINFO 14 /* mailbox information */ +#define T_MX 15 /* mail routing information */ +#define T_TXT 16 /* text strings */ + /* non standard */ +#define T_UINFO 100 /* user (finger) information */ +#define T_UID 101 /* user ID */ +#define T_GID 102 /* group ID */ +#define T_UNSPEC 103 /* Unspecified format (binary data) */ + /* Query type values which do not appear in resource records */ +#define T_AXFR 252 /* transfer zone of authority */ +#define T_MAILB 253 /* transfer mailbox records */ +#define T_MAILA 254 /* transfer mail agent records */ +#define T_ANY 255 /* wildcard match */ + +/* Valid classes */ + +#define C_IN 1 /* the internet */ +#define C_CHAOS 3 /* for chaos net (MIT) */ +#define C_HS 4 /* for Hesiod name server at MIT */ + +#define C_ANY 255 /* wildcard */ + +#endif /* __SERVER__IP__GEN__NAEMSER_H__ */ diff --git a/include/net/gen/netdb.h b/include/net/gen/netdb.h new file mode 100755 index 000000000..55585e528 --- /dev/null +++ b/include/net/gen/netdb.h @@ -0,0 +1,128 @@ +/*- + * Copyright (c) 1980, 1983, 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)netdb.h 5.15 (Berkeley) 4/3/91 + */ + +#ifndef _NETDB_H_ +#define _NETDB_H_ + +#define _PATH_HEQUIV "/etc/hosts.equiv" +#define _PATH_HOSTS "/etc/hosts" +#define _PATH_NETWORKS "/etc/networks" +#define _PATH_PROTOCOLS "/etc/protocols" +#define _PATH_SERVICES "/etc/services" +#define _PATH_SERVACCES "/etc/serv.access" + +/* + * Structures returned by network data base library. All addresses are + * supplied in host order, and returned in network order (suitable for + * use in system calls). + */ +struct hostent { + char *h_name; /* official name of host */ + char **h_aliases; /* alias list */ + int h_addrtype; /* host address type */ + int h_length; /* length of address */ + char **h_addr_list; /* list of addresses from name server */ +#define h_addr h_addr_list[0] /* address, for backward compatiblity */ +}; + +/* + * Assumption here is that a network number + * fits in 32 bits -- probably a poor one. + */ +struct netent { + char *n_name; /* official name of net */ + char **n_aliases; /* alias list */ + int n_addrtype; /* net address type */ + unsigned long n_net; /* network # */ +}; + +struct servent { + char *s_name; /* official service name */ + char **s_aliases; /* alias list */ + int s_port; /* port # */ + char *s_proto; /* protocol to use */ +}; + +struct protoent { + char *p_name; /* official protocol name */ + char **p_aliases; /* alias list */ + int p_proto; /* protocol # */ +}; + +/* + * Error return codes from gethostbyname() and gethostbyaddr() + * (left in extern int h_errno). + */ +extern int h_errno; + +#define HOST_NOT_FOUND 1 /* Authoritative Answer Host not found */ +#define TRY_AGAIN 2 /* Non-Authoritive Host not found, or SERVERFAIL */ +#define NO_RECOVERY 3 /* Non recoverable errors, FORMERR, REFUSED, NOTIMP */ +#define NO_DATA 4 /* Valid name, no data record of requested type */ +#define NO_ADDRESS NO_DATA /* no address, look for MX record */ + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +void endhostent _ARGS((void)); +void endnetent _ARGS((void)); +void endprotoent _ARGS((void)); +void endservent _ARGS((void)); +struct hostent *gethostbyaddr _ARGS((const char *, int, int)); +struct hostent *gethostbyname _ARGS((const char *)); +struct hostent *gethostent _ARGS((void)); +struct netent *getnetbyaddr _ARGS((long, int)); /* u_long? */ +struct netent *getnetbyname _ARGS((const char *)); +struct netent *getnetent _ARGS((void)); +struct protoent *getprotobyname _ARGS((const char *)); +struct protoent *getprotobynumber _ARGS((int)); +struct protoent *getprotoent _ARGS((void)); +struct servent *getservbyname _ARGS((const char *, const char *)); +struct servent *getservbyport _ARGS((int, const char *)); +struct servent *getservent _ARGS((void)); +void herror _ARGS((const char *)); +void sethostent _ARGS((int)); +/* void sethostfile _ARGS((const char *)); */ +void setnetent _ARGS((int)); +void setprotoent _ARGS((int)); +void setservent _ARGS((int)); +#ifdef _MINIX +int servxcheck _ARGS((unsigned long _peer, const char *_service, + void (*_logf) _ARGS((int _pass, const char *_name)))); +char *servxfile _ARGS((const char *_file)); +#endif + +#endif /* !_NETDB_H_ */ diff --git a/include/net/gen/oneCsum.h b/include/net/gen/oneCsum.h new file mode 100755 index 000000000..b9de8fa1d --- /dev/null +++ b/include/net/gen/oneCsum.h @@ -0,0 +1,10 @@ +/* +server/ip/gen/oneCsum.h +*/ + +#ifndef __SERVER__IP__GEN__ONECSUM_H__ +#define __SERVER__IP__GEN__ONECSUM_H__ + +u16_t oneC_sum _ARGS(( U16_t prev, void *data, size_t data_len )); + +#endif /* __SERVER__IP__GEN__ONECSUM_H__ */ diff --git a/include/net/gen/psip_hdr.h b/include/net/gen/psip_hdr.h new file mode 100755 index 000000000..5f85406a2 --- /dev/null +++ b/include/net/gen/psip_hdr.h @@ -0,0 +1,22 @@ +/* +server/ip/gen/psip_hdr.h +*/ + +#ifndef __SERVER__IP__GEN__PSIP_HDR_H__ +#define __SERVER__IP__GEN__PSIP_HDR_H__ + +typedef struct psip_io_hdr +{ + u8_t pih_flags; + u8_t pih_dummy[3]; +} psip_io_hdr_t; + +#define PF_LOC_REM_MASK 1 +#define PF_LOC2REM 0 +#define PF_REM2LOC 1 + +#endif /* __SERVER__IP__GEN__PSIP_HDR_H__ */ + +/* + * $PchId: psip_hdr.h,v 1.2 1995/11/17 22:22:35 philip Exp $ + */ diff --git a/include/net/gen/psip_io.h b/include/net/gen/psip_io.h new file mode 100755 index 000000000..335798342 --- /dev/null +++ b/include/net/gen/psip_io.h @@ -0,0 +1,21 @@ +/* +server/ip/gen/psip_io.h +*/ + +#ifndef __SERVER__IP__GEN__PSIP_IO_H__ +#define __SERVER__IP__GEN__PSIP_IO_H__ + +typedef struct nwio_psipopt +{ + unsigned long nwpo_flags; +} nwio_psipopt_t; + +#define NWPO_PROMISC_MASK 0x0001L +#define NWPO_EN_PROMISC 0x00000001L +#define NWUO_DI_PROMISC 0x00010000L + +#endif /* __SERVER__IP__GEN__PSIP_IO_H__ */ + +/* + * $PchId: psip_io.h,v 1.2 1995/11/17 22:22:16 philip Exp $ + */ diff --git a/include/net/gen/resolv.h b/include/net/gen/resolv.h new file mode 100755 index 000000000..bd9060346 --- /dev/null +++ b/include/net/gen/resolv.h @@ -0,0 +1,107 @@ +/* + * Copyright (c) 1983, 1987, 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + * + * @(#)resolv.h 5.10 (Berkeley) 6/1/90 + */ +#ifndef _NET__GEN__RESOLV_H +#define _NET__GEN__RESOLV_H + +/* + * Resolver configuration file. + * Normally not present, but may contain the address of the + * inital name server(s) to query and the domain search list. + */ + +#ifndef _PATH_RESCONF +#define _PATH_RESCONF "/etc/resolv.conf" +#endif + +/* + * Global defines and variables for resolver stub. + */ +#define MAXNS 3 /* max # name servers we'll track */ +#define MAXDFLSRCH 3 /* # default domain levels to try */ +#define MAXDNSRCH 6 /* max # domains in search path */ +#define LOCALDOMAINPARTS 2 /* min levels in name that is "local" */ + +#define RES_TIMEOUT 5 /* min. seconds between retries */ + +#define NAMESERVER_PORT 53 + +struct state { + int retrans; /* retransmition time interval */ + int retry; /* number of times to retransmit */ + long options; /* option flags - see below. */ + int nscount; /* number of name servers */ + ipaddr_t nsaddr_list[MAXNS]; /* address of name server */ +#define nsaddr nsaddr_list[0] /* for backward compatibility */ + u16_t nsport_list[MAXNS]; /* port of name server */ + u16_t id; /* current packet id */ + char defdname[MAXDNAME]; /* default domain */ + char *dnsrch[MAXDNSRCH+1]; /* components of domain to search */ +}; + +/* + * Resolver options + */ +#define RES_INIT 0x0001 /* address initialized */ +#define RES_DEBUG 0x0002 /* print debug messages */ +#define RES_AAONLY 0x0004 /* authoritative answers only */ +#define RES_USEVC 0x0008 /* use virtual circuit */ +#define RES_PRIMARY 0x0010 /* query primary server only */ +#define RES_IGNTC 0x0020 /* ignore trucation errors */ +#define RES_RECURSE 0x0040 /* recursion desired */ +#define RES_DEFNAMES 0x0080 /* use default domain name */ +#define RES_STAYOPEN 0x0100 /* Keep TCP socket open */ +#define RES_DNSRCH 0x0200 /* search up local domain tree */ + +#define RES_DEFAULT (RES_RECURSE | RES_DEFNAMES | RES_DNSRCH ) + +extern struct state _res; + +struct rrec; + +int res_init _ARGS(( void )); +int res_mkquery _ARGS(( int op, const char *dname, int class, int type, + const char *data, int datalen, const struct rrec *newrr, + char *buf, int buflen )); +int res_query _ARGS(( char *name, int class, int type, u8_t *answer, + int anslen )); +int res_querydomain _ARGS(( char *name, char *domain, int class, int type, + u8_t *answer, int anslen )); +int res_search _ARGS(( char *name, int class, int type, u8_t *answer, + int anslen )); +int res_send _ARGS(( const char *buf, int buflen, char *answer, int anslen )); +void _res_close _ARGS(( void )); + +int dn_comp _ARGS(( const u8_t *exp_dn, u8_t *comp_dn, int length, + u8_t **dnptrs, u8_t **lastdnptr )); +int dn_expand _ARGS(( const u8_t *msg, const u8_t *eomorig, + const u8_t *comp_dn, u8_t *exp_dn, int length )); +int dn_skipname _ARGS(( const u8_t *comp_dn, const u8_t *eom )); + +char *__hostalias _ARGS(( const char *name )); + +u16_t _getshort _ARGS(( const u8_t *msgp )); +u32_t _getlong _ARGS(( const u8_t *msgp )); +void __putshort _ARGS(( U16_t s, u8_t *msgp )); +void __putlong _ARGS(( u32_t l, u8_t *msgp )); + +void p_query _ARGS(( char *msg )); + +#endif /* _NET__GEN__RESOLV_H */ diff --git a/include/net/gen/rip.h b/include/net/gen/rip.h new file mode 100755 index 000000000..7bb56c069 --- /dev/null +++ b/include/net/gen/rip.h @@ -0,0 +1,77 @@ +/* +net/gen/rip.h + +Definitions for the Routing Information Protocol (RFC-1058). + +Created: Aug 16, 1993 by Philip Homburg <philip@cs.vu.nl> +*/ + +#ifndef NET__GEN__RIP_H +#define NET__GEN__RIP_H + +typedef struct rip_hdr +{ + u8_t rh_command; + u8_t rh_version; + u16_t rh_zero; +} rip_hdr_t; + +#define RHC_REQUEST 1 +#define RHC_RESPONSE 2 + +#define RIP_ENTRY_MAX 25 + +typedef struct rip_entry +{ + union + { + struct rip_entry_v1 + { + u16_t re_family; + u16_t re_zero0; + u32_t re_address; + u32_t re_zero1; + u32_t re_zero2; + u32_t re_metric; + } v1; + struct rip_entry_v2 + { + u16_t re_family; + u16_t re_tag; + u32_t re_address; + u32_t re_mask; + u32_t re_nexthop; + u32_t re_metric; + } v2; + } u; +} rip_entry_t; + +#define RIP_FAMILY_IP 2 +#define RIP_INFINITY 16 + +#define RIP_UDP_PORT 520 +#define RIP_PERIOD 30 /* A responce is sent once every + * RIP_PERIOD seconds + */ +#define RIP_FUZZ 10 /* The actual value used is RIP_FREQUENCE - + * a random number of at most RIP_FUZZ. + */ +#define RIP_TIMEOUT 180 /* A route is dead after RIP_TIMEOUT seconds */ +#define RIP_DELETE_TO 120 /* A dead route is removed after RIP_DELETE_TO + * seconds + */ + +#ifdef __RIP_DEBUG +#undef RIP_PERIOD +#define RIP_PERIOD 15 +#undef RIP_TIMEOUT +#define RIP_TIMEOUT 10 +#undef RIP_DELETE_TO +#define RIP_DELETE_TO 10 +#endif /* __RIP_DEBUG */ + +#endif /* NET__GEN__RIP_H */ + +/* + * $PchId: rip.h,v 1.3 1995/11/17 22:21:16 philip Exp $ + */ diff --git a/include/net/gen/route.h b/include/net/gen/route.h new file mode 100755 index 000000000..a2a8c28c5 --- /dev/null +++ b/include/net/gen/route.h @@ -0,0 +1,31 @@ +/* +server/ip/gen/route.h +*/ + +#ifndef __SERVER__IP__GEN__ROUTE_H__ +#define __SERVER__IP__GEN__ROUTE_H__ + +typedef struct nwio_route +{ + u32_t nwr_ent_no; + u32_t nwr_ent_count; + ipaddr_t nwr_dest; + ipaddr_t nwr_netmask; + ipaddr_t nwr_gateway; + u32_t nwr_dist; + u32_t nwr_flags; + u32_t nwr_pref; + u32_t nwr_mtu; /* Ignored, compatibility with VMD */ + ipaddr_t nwr_ifaddr; +} nwio_route_t; + +#define NWRF_EMPTY 0 +#define NWRF_INUSE 1 +#define NWRF_STATIC 2 +#define NWRF_UNREACHABLE 4 + +#endif /* __SERVER__IP__GEN__ROUTE_H__ */ + +/* + * $PchId: route.h,v 1.3 1995/11/17 22:19:50 philip Exp $ + */ diff --git a/include/net/gen/socket.h b/include/net/gen/socket.h new file mode 100755 index 000000000..f4de57c94 --- /dev/null +++ b/include/net/gen/socket.h @@ -0,0 +1,41 @@ +/* +server/ip/gen/socket.h +*/ + +#ifndef __SERVER__IP__GEN__SOCKET_H__ +#define __SERVER__IP__GEN__SOCKET_H__ + +/* From SunOS: /usr/include/sys/socketh */ + +/* + * Address families. + */ +#define AF_UNSPEC 0 /* unspecified */ +#define AF_UNIX 1 /* local to host (pipes, portals) */ +#define AF_INET 2 /* internetwork: UDP, TCP, etc. */ +#define AF_IMPLINK 3 /* arpanet imp addresses */ +#define AF_PUP 4 /* pup protocols: e.g. BSP */ +#define AF_CHAOS 5 /* mit CHAOS protocols */ +#define AF_NS 6 /* XEROX NS protocols */ +#define AF_NBS 7 /* nbs protocols */ +#define AF_ECMA 8 /* european computer manufacturers */ +#define AF_DATAKIT 9 /* datakit protocols */ +#define AF_CCITT 10 /* CCITT protocols, X.25 etc */ +#define AF_SNA 11 /* IBM SNA */ +#define AF_DECnet 12 /* DECnet */ +#define AF_DLI 13 /* Direct data link interface */ +#define AF_LAT 14 /* LAT */ +#define AF_HYLINK 15 /* NSC Hyperchannel */ +#define AF_APPLETALK 16 /* Apple Talk */ + +#define AF_NIT 17 /* Network Interface Tap */ +#define AF_802 18 /* IEEE 802.2, also ISO 8802 */ +#define AF_OSI 19 /* umbrella for all families used + * by OSI (e.g. protosw lookup) */ +#define AF_X25 20 /* CCITT X.25 in particular */ +#define AF_OSINET 21 /* AFI = 47, IDI = 4 */ +#define AF_GOSIP 22 /* U.S. Government OSI */ + +#define AF_MAX 21 + +#endif /* __SERVER__IP__GEN__SOCKET_H__ */ diff --git a/include/net/gen/tcp.h b/include/net/gen/tcp.h new file mode 100755 index 000000000..11022e330 --- /dev/null +++ b/include/net/gen/tcp.h @@ -0,0 +1,19 @@ +/* +server/ip/gen/tcp.h +*/ + +#ifndef __SERVER__IP__GEN__TCP_H__ +#define __SERVER__IP__GEN__TCP_H__ + +#define TCP_MIN_HDR_SIZE 20 +#define TCP_MAX_HDR_SIZE 60 + +#define TCPPORT_TELNET 23 +#define TCPPORT_FINGER 79 + +#define TCPPORT_RESERVED 1024 + +typedef u16_t tcpport_t; +typedef U16_t Tcpport_t; /* for use in prototypes */ + +#endif /* __SERVER__IP__GEN__TCP_H__ */ diff --git a/include/net/gen/tcp_hdr.h b/include/net/gen/tcp_hdr.h new file mode 100755 index 000000000..b0a42b2a8 --- /dev/null +++ b/include/net/gen/tcp_hdr.h @@ -0,0 +1,45 @@ +/* +server/ip/gen/tcp_hdr.h +*/ + +#ifndef __SERVER__IP__GEN__TCP_HDR_H__ +#define __SERVER__IP__GEN__TCP_HDR_H__ + +typedef struct tcp_hdr +{ + tcpport_t th_srcport; + tcpport_t th_dstport; + u32_t th_seq_nr; + u32_t th_ack_nr; + u8_t th_data_off; + u8_t th_flags; + u16_t th_window; + u16_t th_chksum; + u16_t th_urgptr; +} tcp_hdr_t; + +#define TH_DO_MASK 0xf0 + +#define TH_FLAGS_MASK 0x3f +#define THF_FIN 0x1 +#define THF_SYN 0x2 +#define THF_RST 0x4 +#define THF_PSH 0x8 +#define THF_ACK 0x10 +#define THF_URG 0x20 + +typedef struct tcp_hdropt +{ + int tho_opt_siz; + u8_t tho_data[TCP_MAX_HDR_SIZE-TCP_MIN_HDR_SIZE]; +} tcp_hdropt_t; + +#define TCP_OPT_EOL 0 +#define TCP_OPT_NOP 1 +#define TCP_OPT_MSS 2 + +#endif /* __SERVER__IP__GEN__TCP_HDR_H__ */ + +/* + * $PchId: tcp_hdr.h,v 1.3 1995/11/17 22:18:13 philip Exp $ + */ diff --git a/include/net/gen/tcp_io.h b/include/net/gen/tcp_io.h new file mode 100755 index 000000000..b41eb64ac --- /dev/null +++ b/include/net/gen/tcp_io.h @@ -0,0 +1,66 @@ +/* +server/ip/gen/tcp_io.h +*/ + +#ifndef __SERVER__IP__GEN__TCP_IO_H__ +#define __SERVER__IP__GEN__TCP_IO_H__ + +typedef struct nwio_tcpconf +{ + u32_t nwtc_flags; + ipaddr_t nwtc_locaddr; + ipaddr_t nwtc_remaddr; + tcpport_t nwtc_locport; + tcpport_t nwtc_remport; +} nwio_tcpconf_t; + +#define NWTC_NOFLAGS 0x0000L +#define NWTC_ACC_MASK 0x0003L +# define NWTC_EXCL 0x00000001L +# define NWTC_SHARED 0x00000002L +# define NWTC_COPY 0x00000003L +#define NWTC_LOCPORT_MASK 0x0030L +# define NWTC_LP_UNSET 0x00000010L +# define NWTC_LP_SET 0x00000020L +# define NWTC_LP_SEL 0x00000030L +#define NWTC_REMADDR_MASK 0x0100L +# define NWTC_SET_RA 0x00000100L +# define NWTC_UNSET_RA 0x01000000L +#define NWTC_REMPORT_MASK 0x0200L +# define NWTC_SET_RP 0x00000200L +# define NWTC_UNSET_RP 0x02000000L + +typedef struct nwio_tcpcl +{ + long nwtcl_flags; + long nwtcl_ttl; +} nwio_tcpcl_t; + +typedef struct nwio_tcpatt +{ + long nwta_flags; +} nwio_tcpatt_t; + +typedef struct nwio_tcpopt +{ + u32_t nwto_flags; +} nwio_tcpopt_t; + +#define NWTO_NOFLAG 0x0000L +#define NWTO_SND_URG_MASK 0x0001L +# define NWTO_SND_URG 0x00000001L +# define NWTO_SND_NOTURG 0x00010000L +#define NWTO_RCV_URG_MASK 0x0002L +# define NWTO_RCV_URG 0x00000002L +# define NWTO_RCV_NOTURG 0x00020000L +#define NWTO_BSD_URG_MASK 0x0004L +# define NWTO_BSD_URG 0x00000004L +# define NWTO_NOTBSD_URG 0x00040000L +#define NWTO_DEL_RST_MASK 0x0008L +# define NWTO_DEL_RST 0x00000008L + +#endif /* __SERVER__IP__GEN__TCP_IO_H__ */ + +/* + * $PchId: tcp_io.h,v 1.4 1995/11/17 22:17:47 philip Exp $ + */ diff --git a/include/net/gen/udp.h b/include/net/gen/udp.h new file mode 100755 index 000000000..a78dceb41 --- /dev/null +++ b/include/net/gen/udp.h @@ -0,0 +1,14 @@ +/* +server/ip/gen/udp.h +*/ + +#ifndef __SERVER__IP__GEN__UDP_H__ +#define __SERVER__IP__GEN__UDP_H__ + +typedef u16_t udpport_t; +typedef U16_t Udpport_t; + +#define UDP_HDR_SIZE 8 +#define UDP_IO_HDR_SIZE 16 + +#endif /* __SERVER__IP__GEN__UDP_H__ */ diff --git a/include/net/gen/udp_hdr.h b/include/net/gen/udp_hdr.h new file mode 100755 index 000000000..1c7caf5d9 --- /dev/null +++ b/include/net/gen/udp_hdr.h @@ -0,0 +1,26 @@ +/* +server/ip/gen/udp_hdr.h +*/ + +#ifndef __SERVER__IP__GEN__UDP_HDR_H__ +#define __SERVER__IP__GEN__UDP_HDR_H__ + +typedef struct udp_hdr +{ + udpport_t uh_src_port; + udpport_t uh_dst_port; + u16_t uh_length; + u16_t uh_chksum; +} udp_hdr_t; + +typedef struct udp_io_hdr +{ + ipaddr_t uih_src_addr; + ipaddr_t uih_dst_addr; + udpport_t uih_src_port; + udpport_t uih_dst_port; + u16_t uih_ip_opt_len; + u16_t uih_data_len; +} udp_io_hdr_t; + +#endif /* __SERVER__IP__GEN__UDP_HDR_H__ */ diff --git a/include/net/gen/udp_io.h b/include/net/gen/udp_io.h new file mode 100755 index 000000000..6c7ad63a0 --- /dev/null +++ b/include/net/gen/udp_io.h @@ -0,0 +1,45 @@ +/* +server/ip/gen/udp_io.h +*/ + +#ifndef __SERVER__IP__GEN__UDP_IO_H__ +#define __SERVER__IP__GEN__UDP_IO_H__ + +typedef struct nwio_udpopt +{ + unsigned long nwuo_flags; + udpport_t nwuo_locport; + udpport_t nwuo_remport; + ipaddr_t nwuo_locaddr; + ipaddr_t nwuo_remaddr; +} nwio_udpopt_t; + +#define NWUO_NOFLAGS 0x0000L +#define NWUO_ACC_MASK 0x0003L +#define NWUO_EXCL 0x00000001L +#define NWUO_SHARED 0x00000002L +#define NWUO_COPY 0x00000003L +#define NWUO_LOCPORT_MASK 0x000CL +#define NWUO_LP_SEL 0x00000004L +#define NWUO_LP_SET 0x00000008L +#define NWUO_LP_ANY 0x0000000CL +#define NWUO_LOCADDR_MASK 0x0010L +#define NWUO_EN_LOC 0x00000010L +#define NWUO_DI_LOC 0x00100000L +#define NWUO_BROAD_MASK 0x0020L +#define NWUO_EN_BROAD 0x00000020L +#define NWUO_DI_BROAD 0x00200000L +#define NWUO_REMPORT_MASK 0x0100L +#define NWUO_RP_SET 0x00000100L +#define NWUO_RP_ANY 0x01000000L +#define NWUO_REMADDR_MASK 0x0200L +#define NWUO_RA_SET 0x00000200L +#define NWUO_RA_ANY 0x02000000L +#define NWUO_RW_MASK 0x1000L +#define NWUO_RWDATONLY 0x00001000L +#define NWUO_RWDATALL 0x10000000L +#define NWUO_IPOPT_MASK 0x2000L +#define NWUO_EN_IPOPT 0x00002000L +#define NWUO_DI_IPOPT 0x20000000L + +#endif /* __SERVER__IP__GEN__UDP_IO_H__ */ diff --git a/include/net/gen/vjhc.h b/include/net/gen/vjhc.h new file mode 100755 index 000000000..dbe0b986b --- /dev/null +++ b/include/net/gen/vjhc.h @@ -0,0 +1,85 @@ +/* +net/gen/vjhc.h + +Defines for Van Jacobson TCP/IP Header Compression as defined in RFC-1144 + +Created: Nov 15, 1993 by Philip Homburg <philip@cs.vu.nl> +*/ + +#ifndef __NET__GEN__VJHC_H__ +#define __NET__GEN__VJHC_H__ + +#define VJHC_FLAG_U 0x01 +#define VJHC_FLAG_W 0x02 +#define VJHC_FLAG_A 0x04 +#define VJHC_FLAG_S 0x08 +#define VJHC_FLAG_P 0x10 +#define VJHC_FLAG_I 0x20 +#define VJHC_FLAG_C 0x40 + +#define VJHC_SPEC_I (VJHC_FLAG_S | VJHC_FLAG_W | VJHC_FLAG_U) +#define VJHC_SPEC_D (VJHC_FLAG_S | VJHC_FLAG_A | VJHC_FLAG_W | VJHC_FLAG_U) +#define VJHC_SPEC_MASK (VJHC_FLAG_S | VJHC_FLAG_A | VJHC_FLAG_W | VJHC_FLAG_U) + +#define VJHC_ENCODE(cp, n) \ +{ \ + if ((u16_t)(n) >= 256) \ + { \ + *(cp)++= 0; \ + *(cp)++= (n >> 8); \ + *(cp)++= (n); \ + } \ + else \ + *(cp)++= (n); \ +} + +#define VJHC_ENCODEZ(cp, n) \ +{ \ + if ((u16_t)(n) == 0 || (u16_t)(n) >= 256) \ + { \ + *(cp)++= 0; \ + *(cp)++= (n >> 8); \ + *(cp)++= (n); \ + } \ + else \ + *(cp)++= (n); \ +} + +#define VJHC_DECODEL(cp, l) \ +{ \ + if (*(cp) == 0) \ + { \ + (l)= htonl(ntohl((l)) + (((cp)[1] << 8) | (cp)[2])); \ + (cp) += 3; \ + } \ + else \ + (l)= htonl(ntohl((l)) + (u32_t)*(cp)++); \ +} + +#define VJHC_DECODES(cp, s) \ +{ \ + if (*(cp) == 0) \ + { \ + (s)= htons(ntohs((s)) + (((cp)[1] << 8) | (cp)[2])); \ + (cp) += 3; \ + } \ + else \ + (s)= htons(ntohs((s)) + (u16_t)*(cp)++); \ +} + +#define VJHC_DECODEU(cp, s) \ +{ \ + if (*(cp) == 0) \ + { \ + (s)= htons(((cp)[1] << 8) | (cp)[2]); \ + (cp) += 3; \ + } \ + else \ + (s)= htons((u16_t)*(cp)++); \ +} + +#endif /* __NET__GEN__VJHC_H__ */ + +/* + * $PchId: vjhc.h,v 1.2 1995/11/17 22:14:46 philip Exp $ + */ diff --git a/include/net/hton.h b/include/net/hton.h new file mode 100755 index 000000000..0ae1bcb1c --- /dev/null +++ b/include/net/hton.h @@ -0,0 +1,83 @@ +/* +The following macro definitions convert to and from the network standard byte +order. The macros with their name in lower case guarantee to evaluate their +argument exactly once. The function of the macros is encoded in their names; +htons means convert a (unsigned) short in host byte order to network byte order. +*/ + +#ifndef _NET__HTON_H +#define _NET__HTON_H + +#include <minix/config.h> + +extern u16_t _tmp; +extern u32_t _tmp_l; + +/* Find out about the byte order. */ + +/* assume <minix/config.h> is included, let's check */ +#if (CHIP == 0) +#include "CHIP macro not set, include <minix/config.h>" +#endif + +#if (CHIP == INTEL) +#define LITTLE_ENDIAN 1 +#endif + +#if (CHIP == M68000 || CHIP == SPARC) +#define BIG_ENDIAN 1 +#endif + + +#if (LITTLE_ENDIAN) && (BIG_ENDIAN) +#include "both LITTLE_ENDIAN and BIG_ENDIAN are defined" + /* LITTLE_ENDIAN and BIG_ENDIAN are both defined */ +#endif + +#if !(LITTLE_ENDIAN) && !(BIG_ENDIAN) +#include "neither LITTLE_ENDIAN nor BIG_ENDIAN is defined" + /* LITTLE_ENDIAN and BIG_ENDIAN are both NOT defined */ +#endif + +#if LITTLE_ENDIAN +#define HTONS(x) ( ( (((unsigned short)(x)) >>8) & 0xff) | \ + ((((unsigned short)(x)) & 0xff)<<8) ) +#define NTOHS(x) ( ( (((unsigned short)(x)) >>8) & 0xff) | \ + ((((unsigned short)(x)) & 0xff)<<8) ) +#define HTONL(x) ((((x)>>24) & 0xffL) | (((x)>>8) & 0xff00L) | \ + (((x)<<8) & 0xff0000L) | (((x)<<24) & 0xff000000L)) +#define NTOHL(x) ((((x)>>24) & 0xffL) | (((x)>>8) & 0xff00L) | \ + (((x)<<8) & 0xff0000L) | (((x)<<24) & 0xff000000L)) + +#if _WORD_SIZE > 2 +#define htons(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00)) +#define ntohs(x) (_tmp=(x), ((_tmp>>8) & 0xff) | ((_tmp<<8) & 0xff00)) +#define htonl(x) (_tmp_l=(x), ((_tmp_l>>24) & 0xffL) | \ + ((_tmp_l>>8) & 0xff00L) | \ + ((_tmp_l<<8) & 0xff0000L) | ((_tmp_l<<24) & 0xff000000L)) +#define ntohl(x) (_tmp_l=(x), ((_tmp_l>>24) & 0xffL) \ + | ((_tmp_l>>8) & 0xff00L) | \ + ((_tmp_l<<8) & 0xff0000L) | ((_tmp_l<<24) & 0xff000000L)) + +#else /* _WORD_SIZE == 2 */ +/* The above macros are too unwieldy for a 16-bit machine. */ +u16_t htons(u16_t x); +u16_t ntohs(u16_t x); +u32_t htonl(u32_t x); +u32_t ntohl(u32_t x); +#endif /* _WORD_SIZE == 2 */ + +#endif /* LITTLE_ENDIAN */ + +#if BIG_ENDIAN +#define htons(x) (x) +#define HTONS(x) (x) +#define ntohs(x) (x) +#define NTOHS(x) (x) +#define htonl(x) (x) +#define HTONL(x) (x) +#define ntohl(x) (x) +#define NTOHL(x) (x) +#endif /* BIG_ENDIAN */ + +#endif /* _NET__HTON_H */ diff --git a/include/net/ioctl.h b/include/net/ioctl.h new file mode 100755 index 000000000..e412809bc --- /dev/null +++ b/include/net/ioctl.h @@ -0,0 +1,44 @@ +/* net/ioctl.h - Network ioctl() command codes. Author: Kees J. Bot + * 23 Nov 2002 + * + */ + +#ifndef _NET__IOCTL_H +#define _NET__IOCTL_H + +#include <minix/ioctl.h> + +/* Network ioctls. */ +#define NWIOSETHOPT _IOW('n', 16, struct nwio_ethopt) +#define NWIOGETHOPT _IOR('n', 17, struct nwio_ethopt) +#define NWIOGETHSTAT _IOR('n', 18, struct nwio_ethstat) + +#define NWIOSIPCONF _IOW('n', 32, struct nwio_ipconf) +#define NWIOGIPCONF _IOR('n', 33, struct nwio_ipconf) +#define NWIOSIPOPT _IOW('n', 34, struct nwio_ipopt) +#define NWIOGIPOPT _IOR('n', 35, struct nwio_ipopt) + +#define NWIOGIPOROUTE _IORW('n', 40, struct nwio_route) +#define NWIOSIPOROUTE _IOW ('n', 41, struct nwio_route) +#define NWIODIPOROUTE _IOW ('n', 42, struct nwio_route) + +#define NWIOGIPIROUTE _IORW('n', 43, struct nwio_route) +#define NWIOSIPIROUTE _IOW ('n', 44, struct nwio_route) +#define NWIODIPIROUTE _IOW ('n', 45, struct nwio_route) + +#define NWIOSTCPCONF _IOW('n', 48, struct nwio_tcpconf) +#define NWIOGTCPCONF _IOR('n', 49, struct nwio_tcpconf) +#define NWIOTCPCONN _IOW('n', 50, struct nwio_tcpcl) +#define NWIOTCPLISTEN _IOW('n', 51, struct nwio_tcpcl) +#define NWIOTCPATTACH _IOW('n', 52, struct nwio_tcpatt) +#define NWIOTCPSHUTDOWN _IO ('n', 53) +#define NWIOSTCPOPT _IOW('n', 54, struct nwio_tcpopt) +#define NWIOGTCPOPT _IOR('n', 55, struct nwio_tcpopt) + +#define NWIOSUDPOPT _IOW('n', 64, struct nwio_udpopt) +#define NWIOGUDPOPT _IOR('n', 65, struct nwio_udpopt) + +#define NWIOSPSIPOPT _IOW('n', 80, struct nwio_psipopt) +#define NWIOGPSIPOPT _IOR('n', 81, struct nwio_psipopt) + +#endif /* _NET__IOCTL_H */ diff --git a/include/net/netlib.h b/include/net/netlib.h new file mode 100755 index 000000000..970b81b89 --- /dev/null +++ b/include/net/netlib.h @@ -0,0 +1,22 @@ +/* +net/netlib.h +*/ + +#ifndef _NET__NETLIB_H_ +#define _NET__NETLIB_H_ + +#ifndef _ANSI +#include <ansi.h> +#endif + +_PROTOTYPE (int iruserok, (unsigned long raddr, int superuser, + const char *ruser, const char *luser) ); +_PROTOTYPE (int rcmd, (char **ahost, int rport, const char *locuser, + const char *remuser, const char *cmd, int *fd2p) ); + +#define ETH_DEVICE "/dev/eth" +#define IP_DEVICE "/dev/ip" +#define TCP_DEVICE "/dev/tcp" +#define UDP_DEVICE "/dev/udp" + +#endif /* _NET__NETLIB_H_ */ diff --git a/include/net/socket.h b/include/net/socket.h new file mode 100644 index 000000000..7e17a470a --- /dev/null +++ b/include/net/socket.h @@ -0,0 +1,13 @@ + +#ifndef __NET_SOCKET_H__ +#define __NET_SOCKET_H__ 1 + +#include <sys/types.h> +#include <net/hton.h> +#include <net/gen/in.h> +#include <net/gen/socket.h> +#include <net/gen/emu.h> +#include <net/gen/netdb.h> + +#endif + diff --git a/include/pwd.h b/include/pwd.h new file mode 100755 index 000000000..48eb04f3d --- /dev/null +++ b/include/pwd.h @@ -0,0 +1,34 @@ +/* The <pwd.h> header defines the items in the password file. */ + +#ifndef _PWD_H +#define _PWD_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +struct passwd { + char *pw_name; /* login name */ + uid_t pw_uid; /* uid corresponding to the name */ + gid_t pw_gid; /* gid corresponding to the name */ + char *pw_dir; /* user's home directory */ + char *pw_shell; /* name of the user's shell */ + + /* The following members are not defined by POSIX. */ + char *pw_passwd; /* password information */ + char *pw_gecos; /* just in case you have a GE 645 around */ +}; + + +/* Function Prototypes. */ +_PROTOTYPE( struct passwd *getpwnam, (const char *_name) ); +_PROTOTYPE( struct passwd *getpwuid, (Uid_t _uid) ); + +#ifdef _MINIX +_PROTOTYPE( void endpwent, (void) ); +_PROTOTYPE( struct passwd *getpwent, (void) ); +_PROTOTYPE( int setpwent, (void) ); +_PROTOTYPE( void setpwfile, (const char *_file) ); +#endif + +#endif /* _PWD_H */ diff --git a/include/regex.h b/include/regex.h new file mode 100755 index 000000000..698e92d5d --- /dev/null +++ b/include/regex.h @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 1992 Henry Spencer. + * Copyright (c) 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer of the University of Toronto. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regex.h 8.2 (Berkeley) 1/3/94 + */ + +#ifndef _REGEX_H_ +#define _REGEX_H_ + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +/* types */ +typedef off_t regoff_t; + +typedef struct { + int re_magic; + size_t re_nsub; /* number of parenthesized subexpressions */ + const char *re_endp; /* end pointer for REG_PEND */ + struct re_guts *re_g; /* none of your business :-) */ +} regex_t; + +typedef struct { + regoff_t rm_so; /* start of match */ + regoff_t rm_eo; /* end of match */ +} regmatch_t; + +/* regcomp() flags */ +#define REG_BASIC 0000 +#define REG_EXTENDED 0001 +#define REG_ICASE 0002 +#define REG_NOSUB 0004 +#define REG_NEWLINE 0010 +#define REG_NOSPEC 0020 +#define REG_PEND 0040 +#define REG_DUMP 0200 + +/* regerror() flags */ +#define REG_NOMATCH 1 +#define REG_BADPAT 2 +#define REG_ECOLLATE 3 +#define REG_ECTYPE 4 +#define REG_EESCAPE 5 +#define REG_ESUBREG 6 +#define REG_EBRACK 7 +#define REG_EPAREN 8 +#define REG_EBRACE 9 +#define REG_BADBR 10 +#define REG_ERANGE 11 +#define REG_ESPACE 12 +#define REG_BADRPT 13 +#define REG_EMPTY 14 +#define REG_ASSERT 15 +#define REG_INVARG 16 +#define REG_ATOI 255 /* convert name to number (!) */ +#define REG_ITOA 0400 /* convert number to name (!) */ + +/* regexec() flags */ +#define REG_NOTBOL 00001 +#define REG_NOTEOL 00002 +#define REG_STARTEND 00004 +#define REG_TRACE 00400 /* tracing of execution */ +#define REG_LARGE 01000 /* force large representation */ +#define REG_BACKR 02000 /* force use of backref code */ + +int regcomp(regex_t *, const char *, int); +size_t regerror(int, const regex_t *, char *, size_t); +int regexec(const regex_t *, const char *, size_t, regmatch_t [], int); +void regfree(regex_t *); + +#endif /* !_REGEX_H_ */ diff --git a/include/regexp.h b/include/regexp.h new file mode 100755 index 000000000..31d925329 --- /dev/null +++ b/include/regexp.h @@ -0,0 +1,39 @@ +/* The <regexp.h> header is used by the (V8-compatible) regexp(3) routines. */ +/* NOTE: Obsoleted by the POSIX regex(3) library. */ + +#ifndef _REGEXP_H +#define _REGEXP_H + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +#define CHARBITS 0377 +#define NSUBEXP 10 +typedef struct regexp { + const char *startp[NSUBEXP]; + const char *endp[NSUBEXP]; + char regstart; /* Internal use only. */ + char reganch; /* Internal use only. */ + char *regmust; /* Internal use only. */ + int regmlen; /* Internal use only. */ + char program[1]; /* Unwarranted chumminess with compiler. */ +} regexp; + +/* Keep these functions away from the POSIX versions. */ +#define regcomp _v8_regcomp +#define regexec _v8_regexec +#define regsub _v8_regsub +#define regerror _v8_regerror + +/* Function Prototypes. */ +regexp *regcomp(const char *_exp); +int regexec(regexp *_prog, const char *_string, int _bolflag); +void regsub(regexp *_prog, char *_source, char *_dest); +void regerror(const char *_message) ; + +#endif /* _REGEXP_H */ + +/* + * $PchId: regexp.h,v 1.4 1996/04/10 21:43:17 philip Exp $ + */ diff --git a/include/setjmp.h b/include/setjmp.h new file mode 100755 index 000000000..970d1c4af --- /dev/null +++ b/include/setjmp.h @@ -0,0 +1,148 @@ +/* The <setjmp.h> header relates to the C phenomenon known as setjmp/longjmp. + * It is used to escape out of the current situation into a previous one. + * A typical example is in an editor, where hitting DEL breaks off the current + * command and puts the editor back in the main loop, though care has to be + * taken when the DEL occurs while executing a library function, since + * some of them are not reentrant. + * + * POSIX does not require the process signal mask to be saved and restored + * during setjmp/longjmp. However, the current implementation does this + * in order to agree with OSF/1 and other BSD derived systems. + * + * The pair of functions _setjmp/_longjmp may be used when the signal + * mask is not to be saved/restored. These functions are traditional + * in BSD systems. + * + * There are different ways of implementing setjmp/longjmp. Probably + * the best way is to unify it with signal handling. This is true for the + * following reasons: Both setjmp/longjmp and signal delivery must save + * a context so that it may be restored later. The jmp_buf necessarily + * contains signal information, namely the signal mask to restore. Both + * longjmp and the return of a signal handler must trap to the operating + * system to restore the previous signal mask. Finally, the jmp_buf + * and the sigcontext structure contain the registers to restore. + * + * Some compilers, namely ACK, will not enregister any variables inside a + * function containing a call to setjmp, even if those variables are + * explicitly declared as register variables. Thus for ACK, the + * identification of the jmp_buf with a sigcontext structure would cause + * unnecessary overhead: the jmp_buf has room for all the registers, but + * the only registers that need to be saved are the stack pointer, + * frame pointer, and program counter. + * + * So, for ACK a jmp_buf is much smaller than a sigcontext structure, and + * longjmp does not directly call sigreturn. Instead, longjmp calls a + * front-end function which initializes the appropriate fields of a + * sigcontext structure, marks this structure as containing no valid + * general purpose registers, and then calls sigreturn. + * + * The POSIX sigjmp_buf is identical to the jmp_buf in all cases. + * + * Different compilers have different symbols that they recognize as + * setjmp symbols. ACK recognizes __setjmp, the GNU C compiler + * recognizes setjmp and _setjmp, and BCC recognizes all three. + * When these symbols occur within a function, the compiler may keep + * all local variables on the stack, avoid certain optimizations, or + * pass hidden arguments to the setjmp function. + * + * Thus, setjmp implementations vary in two independent ways which may + * be identified through the following preprocessor tokens: + * + * _SETJMP_SYMBOL -- If 0, this means the compiler treats setjmp and _setjmp + * specially. If 1, this means the compiler treats __setjmp specially. + * + * _SETJMP_SAVES_REGS -- If 1, this means setjmp/longjmp must explicitly + * save and restore all registers. This also implies that a jmp_buf is + * different than a sigcontext structure. If 0, this means that the compiler + * will not use register variables within a function that calls one of + * its SETJMP_SYMBOLs. + * + * When _SETJMP_SYMBOL = 1, the implementation has a few dozen bytes of + * unnecessary overhead. This happens in the following manner: a program uses + * _setjmp/_longjmp because it is not interested in saving and restoring the + * signal mask. Nevertheless, because _setjmp expands to the general purpose + * function __setjmp, code for sigprocmask(2) is linked into the program. + */ + +#ifndef _SETJMP_H +#define _SETJMP_H + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +#if !defined(__ACK__) && !defined(__BCC__) && !defined(__GNUC__) +#define __ACK__ +#endif + +#ifdef __ACK__ +#define _SETJMP_SYMBOL 1 +#define _SETJMP_SAVES_REGS 0 +#endif +#ifdef __BCC__ +#define _SETJMP_SYMBOL 0 +#define _SETJMP_SAVES_REGS 1 +#endif +#ifdef __GNUC__ +#define _SETJMP_SYMBOL 0 +#define _SETJMP_SAVES_REGS 1 +#endif + +/* The jmp_buf data type. Do not change the order of these fields -- some + * C library code refers to these fields by name. When _SETJMP_SAVES_REGS + * is 1, the file <sys/jmp_buf.h> gives the usage of the sixteen registers. + */ +typedef struct { + int __flags; /* XXX - long might give better alignment */ + long __mask; /* must have size >= sizeof(sigset_t) */ +#if (_SETJMP_SAVES_REGS == 0) + _PROTOTYPE(void (*__pc),(void)); /* program counter */ + void *__sp; /* stack pointer */ + void *__lb; /* local base (ACKspeak for frame pointer) */ +#else + void *__regs[16]; /* size is machine dependent */ +#endif +} jmp_buf[1]; + +#if (_SETJMP_SYMBOL == 1) + +_PROTOTYPE( int __setjmp, (jmp_buf _env, int _savemask) ); +_PROTOTYPE( void longjmp, (jmp_buf _env, int _val) ); +_PROTOTYPE(int sigjmp, (jmp_buf _jb, int _retval) ); + +#define setjmp(env) __setjmp((env), 1) + +#ifdef _MINIX +#define _setjmp(env) __setjmp((env), 0) +_PROTOTYPE(void _longjmp, (jmp_buf _env, int _val) ); +#endif + +#ifdef _POSIX_SOURCE +typedef jmp_buf sigjmp_buf; +_PROTOTYPE( void siglongjmp, (sigjmp_buf _env, int _val) ); + +#define sigsetjmp(env, savemask) __setjmp((env), (savemask)) +#endif /* _POSIX_SOURCE */ + +#endif /* _SETJMP_SYMBOL == 1 */ + +#if (_SETJMP_SYMBOL == 0) + +_PROTOTYPE( int setjmp, (jmp_buf _env) ); +_PROTOTYPE( void longjmp, (jmp_buf _env, int _val) ); + +#ifdef _MINIX +_PROTOTYPE( int _setjmp, (jmp_buf _env) ); +_PROTOTYPE( void _longjmp, (jmp_buf _env, int _val) ); +#endif + +#ifdef _POSIX_SOURCE +#define sigjmp_buf jmp_buf +_PROTOTYPE( void siglongjmp, (sigjmp_buf _env, int _val) ); +/* XXX - the name _setjmp is no good - that's why ACK used __setjmp. */ +#define sigsetjmp(env, savemask) ((savemask) ? setjmp(env) : _setjmp(env)) +#endif /* _POSIX_SOURCE */ + +#endif /* _SETJMP_SYMBOL == 0 */ + +#endif /* _SETJMP_H */ diff --git a/include/sgtty.h b/include/sgtty.h new file mode 100755 index 000000000..0e9ac1e26 --- /dev/null +++ b/include/sgtty.h @@ -0,0 +1,92 @@ +/* The <sgtty.h> header contains data structures for ioctl(). */ + +#ifndef _SGTTY_H +#define _SGTTY_H + +/* Should not be used, nor extended. Termios.h is the replacement for + * sgtty.h for tty functions, and ioctl replaced code should be moved to + * sys/ioctl.h and specific header files in the sys, or minix directory. + */ +#include <sys/ioctl.h> /* Ouch. */ + +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 */ +}; + +struct tchars { + char t_intrc; /* SIGINT char */ + char t_quitc; /* SIGQUIT char */ + char t_startc; /* start output (initially CTRL-Q) */ + char t_stopc; /* stop output (initially CTRL-S) */ + char t_eofc; /* EOF (initially CTRL-D) */ + char t_brkc; /* input delimiter (like nl) */ +}; + +#if !_SYSTEM /* the kernel doesn't want to see the rest */ + +/* Field names */ +#define XTABS 0006000 /* do tab expansion */ +#define BITS8 0001400 /* 8 bits/char */ +#define BITS7 0001000 /* 7 bits/char */ +#define BITS6 0000400 /* 6 bits/char */ +#define BITS5 0000000 /* 5 bits/char */ +#define EVENP 0000200 /* even parity */ +#define ODDP 0000100 /* odd parity */ +#define RAW 0000040 /* enable raw mode */ +#define CRMOD 0000020 /* map lf to cr + lf */ +#define ECHO 0000010 /* echo input */ +#define CBREAK 0000002 /* enable cbreak mode */ +#define COOKED 0000000 /* neither CBREAK nor RAW */ + +#define DCD 0100000 /* Data Carrier Detect */ + +/* Line speeds */ +#define B0 0 /* code for line-hangup */ +#define B110 1 +#define B300 3 +#define B1200 12 +#define B2400 24 +#define B4800 48 +#define B9600 96 +#define B19200 192 +#define B38400 195 +#define B57600 194 +#define B115200 193 + +/* Things Minix supports but not properly */ +/* the divide-by-100 encoding ain't too hot */ +#define ANYP 0000300 +#define B50 0 +#define B75 0 +#define B134 0 +#define B150 0 +#define B200 2 +#define B600 6 +#define B1800 18 +#define B3600 36 +#define B7200 72 +#define EXTA 192 +#define EXTB 0 + +/* Things Minix doesn't support but are fairly harmless if used */ +#define NLDELAY 0001400 +#define TBDELAY 0006000 +#define CRDELAY 0030000 +#define VTDELAY 0040000 +#define BSDELAY 0100000 +#define ALLDELAY 0177400 + +/* Copied from termios.h: */ +struct winsize +{ + unsigned short ws_row; /* rows, in characters */ + unsigned short ws_col; /* columns, in characters */ + unsigned short ws_xpixel; /* horizontal size, pixels */ + unsigned short ws_ypixel; /* vertical size, pixels */ +}; +#endif /* !_SYSTEM */ +#endif /* _SGTTY_H */ diff --git a/include/signal.h b/include/signal.h new file mode 100755 index 000000000..d5588956b --- /dev/null +++ b/include/signal.h @@ -0,0 +1,112 @@ +/* The <signal.h> header defines all the ANSI and POSIX signals. + * MINIX supports all the signals required by POSIX. They are defined below. + * Some additional signals are also supported. + */ + +#ifndef _SIGNAL_H +#define _SIGNAL_H + +#ifndef _ANSI_H +#include <ansi.h> +#endif +#ifdef _POSIX_SOURCE +#ifndef _TYPES_H +#include <sys/types.h> +#endif +#endif + +/* Here are types that are closely associated with signal handling. */ +typedef int sig_atomic_t; + +#ifdef _POSIX_SOURCE +#ifndef _SIGSET_T +#define _SIGSET_T +typedef unsigned long sigset_t; +#endif +#endif + +#define _NSIG 17 /* number of signals used */ + +#define SIGHUP 1 /* hangup */ +#define SIGINT 2 /* interrupt (DEL) */ +#define SIGQUIT 3 /* quit (ASCII FS) */ +#define SIGILL 4 /* illegal instruction */ +#define SIGTRAP 5 /* trace trap (not reset when caught) */ +#define SIGABRT 6 /* IOT instruction */ +#define SIGIOT 6 /* SIGABRT for people who speak PDP-11 */ +#define SIGUNUSED 7 /* spare code */ +#define SIGFPE 8 /* floating point exception */ +#define SIGKILL 9 /* kill (cannot be caught or ignored) */ +#define SIGUSR1 10 /* user defined signal # 1 */ +#define SIGSEGV 11 /* segmentation violation */ +#define SIGUSR2 12 /* user defined signal # 2 */ +#define SIGPIPE 13 /* write on a pipe with no one to read it */ +#define SIGALRM 14 /* alarm clock */ +#define SIGTERM 15 /* software termination signal from kill */ +#define SIGCHLD 17 /* child process terminated or stopped */ + +#define SIGEMT 7 /* obsolete */ +#define SIGBUS 10 /* obsolete */ + +/* POSIX requires the following signals to be defined, even if they are + * not supported. Here are the definitions, but they are not supported. + */ +#define SIGCONT 18 /* continue if stopped */ +#define SIGSTOP 19 /* stop signal */ +#define SIGTSTP 20 /* interactive stop signal */ +#define SIGTTIN 21 /* background process wants to read */ +#define SIGTTOU 22 /* background process wants to write */ + +/* The sighandler_t type is not allowed unless _POSIX_SOURCE is defined. */ +typedef void _PROTOTYPE( (*__sighandler_t), (int) ); + +/* Macros used as function pointers. */ +#define SIG_ERR ((__sighandler_t) -1) /* error return */ +#define SIG_DFL ((__sighandler_t) 0) /* default signal handling */ +#define SIG_IGN ((__sighandler_t) 1) /* ignore signal */ +#define SIG_HOLD ((__sighandler_t) 2) /* block signal */ +#define SIG_CATCH ((__sighandler_t) 3) /* catch signal */ + +#ifdef _POSIX_SOURCE +struct sigaction { + __sighandler_t sa_handler; /* SIG_DFL, SIG_IGN, or pointer to function */ + sigset_t sa_mask; /* signals to be blocked during handler */ + int sa_flags; /* special flags */ +}; + +/* Fields for sa_flags. */ +#define SA_ONSTACK 0x0001 /* deliver signal on alternate stack */ +#define SA_RESETHAND 0x0002 /* reset signal handler when signal caught */ +#define SA_NODEFER 0x0004 /* don't block signal while catching it */ +#define SA_RESTART 0x0008 /* automatic system call restart */ +#define SA_SIGINFO 0x0010 /* extended signal handling */ +#define SA_NOCLDWAIT 0x0020 /* don't create zombies */ +#define SA_NOCLDSTOP 0x0040 /* don't receive SIGCHLD when child stops */ + +/* POSIX requires these values for use with sigprocmask(2). */ +#define SIG_BLOCK 0 /* for blocking signals */ +#define SIG_UNBLOCK 1 /* for unblocking signals */ +#define SIG_SETMASK 2 /* for setting the signal mask */ +#define SIG_INQUIRE 4 /* for internal use only */ +#endif /* _POSIX_SOURCE */ + +/* POSIX and ANSI function prototypes. */ +_PROTOTYPE( int raise, (int _sig) ); +_PROTOTYPE( __sighandler_t signal, (int _sig, __sighandler_t _func) ); + +#ifdef _POSIX_SOURCE +_PROTOTYPE( int kill, (pid_t _pid, int _sig) ); +_PROTOTYPE( int sigaction, + (int _sig, const struct sigaction *_act, struct sigaction *_oact) ); +_PROTOTYPE( int sigaddset, (sigset_t *_set, int _sig) ); +_PROTOTYPE( int sigdelset, (sigset_t *_set, int _sig) ); +_PROTOTYPE( int sigemptyset, (sigset_t *_set) ); +_PROTOTYPE( int sigfillset, (sigset_t *_set) ); +_PROTOTYPE( int sigismember, (const sigset_t *_set, int _sig) ); +_PROTOTYPE( int sigpending, (sigset_t *_set) ); +_PROTOTYPE( int sigprocmask, + (int _how, const sigset_t *_set, sigset_t *_oset) ); +_PROTOTYPE( int sigsuspend, (const sigset_t *_sigmask) ); +#endif + +#endif /* _SIGNAL_H */ diff --git a/include/stdarg.h b/include/stdarg.h new file mode 100755 index 000000000..b37f57b1e --- /dev/null +++ b/include/stdarg.h @@ -0,0 +1,90 @@ +/* The <stdarg.h> header is ANSI's way to handle variable numbers of params. + * Some programming languages require a function that is declared with n + * parameters to be called with n parameters. C does not. A function may + * called with more parameters than it is declared with. The well-known + * printf function, for example, may have arbitrarily many parameters. + * The question arises how one can access all the parameters in a portable + * way. The C standard defines three macros that programs can use to + * advance through the parameter list. The definition of these macros for + * MINIX are given in this file. The three macros are: + * + * va_start(ap, parmN) prepare to access parameters + * va_arg(ap, type) get next parameter value and type + * va_end(ap) access is finished + * + * Ken Thompson's famous line from V6 UNIX is equally applicable to this file: + * + * "You are not expected to understand this" + * + */ + +#ifndef _STDARG_H +#define _STDARG_H + + +#ifdef __GNUC__ +/* The GNU C-compiler uses its own, but similar varargs mechanism. */ + +typedef char *va_list; + +/* Amount of space required in an argument list for an arg of type TYPE. + * TYPE may alternatively be an expression whose type is used. + */ + +#define __va_rounded_size(TYPE) \ + (((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int)) + +#if __GNUC__ < 2 + +#ifndef __sparc__ +#define va_start(AP, LASTARG) \ + (AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG))) +#else +#define va_start(AP, LASTARG) \ + (__builtin_saveregs (), \ + AP = ((char *) &(LASTARG) + __va_rounded_size (LASTARG))) +#endif + +void va_end (va_list); /* Defined in gnulib */ +#define va_end(AP) + +#define va_arg(AP, TYPE) \ + (AP += __va_rounded_size (TYPE), \ + *((TYPE *) (AP - __va_rounded_size (TYPE)))) + +#else /* __GNUC__ >= 2 */ + +#ifndef __sparc__ +#define va_start(AP, LASTARG) \ + (AP = ((char *) __builtin_next_arg ())) +#else +#define va_start(AP, LASTARG) \ + (__builtin_saveregs (), AP = ((char *) __builtin_next_arg ())) +#endif + +void va_end (va_list); /* Defined in libgcc.a */ +#define va_end(AP) + +#define va_arg(AP, TYPE) \ + (AP = ((char *) (AP)) += __va_rounded_size (TYPE), \ + *((TYPE *) ((char *) (AP) - __va_rounded_size (TYPE)))) + +#endif /* __GNUC__ >= 2 */ + +#else /* not __GNUC__ */ + + +typedef char *va_list; + +#define __vasz(x) ((sizeof(x)+sizeof(int)-1) & ~(sizeof(int) -1)) + +#define va_start(ap, parmN) ((ap) = (va_list)&parmN + __vasz(parmN)) +#define va_arg(ap, type) \ + (*((type *)((va_list)((ap) = (void *)((va_list)(ap) + __vasz(type))) \ + - __vasz(type)))) +#define va_end(ap) + + +#endif /* __GNUC__ */ + +#endif /* _STDARG_H */ diff --git a/include/stddef.h b/include/stddef.h new file mode 100755 index 000000000..b6ab471d1 --- /dev/null +++ b/include/stddef.h @@ -0,0 +1,27 @@ +/* The <stddef.h> header defines certain commonly used macros. */ + +#ifndef _STDDEF_H +#define _STDDEF_H + +#define NULL ((void *)0) + +/* The following is not portable, but the compiler accepts it. */ +#define offsetof(type, ident) ((size_t) (unsigned long) &((type *)0)->ident) + +#if _EM_PSIZE == _EM_WSIZE +typedef int ptrdiff_t; /* result of subtracting two pointers */ +#else /* _EM_PSIZE == _EM_LSIZE */ +typedef long ptrdiff_t; +#endif + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; /* type returned by sizeof */ +#endif + +#ifndef _WCHAR_T +#define _WCHAR_T +typedef char wchar_t; /* type expanded character set */ +#endif + +#endif /* _STDDEF_H */ diff --git a/include/stdio.h b/include/stdio.h new file mode 100755 index 000000000..2a8dd676c --- /dev/null +++ b/include/stdio.h @@ -0,0 +1,155 @@ +/* + * stdio.h - input/output definitions + * + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#ifndef _STDIO_H +#define _STDIO_H + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +/* + * Focus point of all stdio activity. + */ +typedef struct __iobuf { + int _count; + int _fd; + int _flags; + int _bufsiz; + unsigned char *_buf; + unsigned char *_ptr; +} FILE; + +#define _IOFBF 0x000 +#define _IOREAD 0x001 +#define _IOWRITE 0x002 +#define _IONBF 0x004 +#define _IOMYBUF 0x008 +#define _IOEOF 0x010 +#define _IOERR 0x020 +#define _IOLBF 0x040 +#define _IOREADING 0x080 +#define _IOWRITING 0x100 +#define _IOAPPEND 0x200 + +/* The following definitions are also in <unistd.h>. They should not + * conflict. + */ +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 + +#define stdin (&__stdin) +#define stdout (&__stdout) +#define stderr (&__stderr) + +#define BUFSIZ 1024 +#define NULL ((void *)0) +#define EOF (-1) + +#define FOPEN_MAX 20 + +#include <sys/dir.h> +#define FILENAME_MAX DIRSIZ + +#define TMP_MAX 999 +#define L_tmpnam (sizeof("/tmp/") + FILENAME_MAX) +#define __STDIO_VA_LIST__ void * + +typedef long int fpos_t; + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; /* type returned by sizeof */ +#endif /* _SIZE_T */ + +extern FILE *__iotab[FOPEN_MAX]; +extern FILE __stdin, __stdout, __stderr; + +_PROTOTYPE( int remove, (const char *_filename) ); +_PROTOTYPE( int rename, (const char *_old, const char *_new) ); +_PROTOTYPE( FILE *tmpfile, (void) ); +_PROTOTYPE( char *tmpnam, (char *_s) ); +_PROTOTYPE( int fclose, (FILE *_stream) ); +_PROTOTYPE( int fflush, (FILE *_stream) ); +_PROTOTYPE( FILE *fopen, (const char *_filename, const char *_mode) ); +_PROTOTYPE( FILE *freopen, + (const char *_filename, const char *_mode, FILE *_stream) ); +_PROTOTYPE( void setbuf, (FILE *_stream, char *_buf) ); +_PROTOTYPE( int setvbuf, + (FILE *_stream, char *_buf, int _mode, size_t _size) ); +_PROTOTYPE( int fprintf, (FILE *_stream, const char *_format, ...) ); +_PROTOTYPE( int printf, (const char *_format, ...) ); +_PROTOTYPE( int sprintf, (char *_s, const char *_format, ...) ); +_PROTOTYPE( int vfprintf, + (FILE *_stream, const char *_format, char *_arg) ); +_PROTOTYPE( int vprintf, (const char *_format, char *_arg) ); +_PROTOTYPE( int vsprintf, (char *_s, const char *_format, char *_arg) ); +_PROTOTYPE( int fscanf, (FILE *_stream, const char *_format, ...) ); +_PROTOTYPE( int scanf, (const char *_format, ...) ); +_PROTOTYPE( int sscanf, (const char *_s, const char *_format, ...) ); +#define vfscanf _doscan +_PROTOTYPE( int vfscanf, (FILE *_stream, const char *_format, char *_arg)); +_PROTOTYPE( int vscanf, (const char *_format, char *_arg) ); +_PROTOTYPE( int vsscanf, (const char *_s, const char *_format, char *_arg)); +_PROTOTYPE( int fgetc, (FILE *_stream) ); +_PROTOTYPE( char *fgets, (char *_s, int _n, FILE *_stream) ); +_PROTOTYPE( int fputc, (int _c, FILE *_stream) ); +_PROTOTYPE( int fputs, (const char *_s, FILE *_stream) ); +_PROTOTYPE( int getc, (FILE *_stream) ); +_PROTOTYPE( int getchar, (void) ); +_PROTOTYPE( char *gets, (char *_s) ); +_PROTOTYPE( int putc, (int _c, FILE *_stream) ); +_PROTOTYPE( int putchar, (int _c) ); +_PROTOTYPE( int puts, (const char *_s) ); +_PROTOTYPE( int ungetc, (int _c, FILE *_stream) ); +_PROTOTYPE( size_t fread, + (void *_ptr, size_t _size, size_t _nmemb, FILE *_stream) ); +_PROTOTYPE( size_t fwrite, + (const void *_ptr, size_t _size, size_t _nmemb, FILE *_stream) ); +_PROTOTYPE( int fgetpos, (FILE *_stream, fpos_t *_pos) ); +_PROTOTYPE( int fseek, (FILE *_stream, long _offset, int _whence) ); +_PROTOTYPE( int fsetpos, (FILE *_stream, fpos_t *_pos) ); +_PROTOTYPE( long ftell, (FILE *_stream) ); +_PROTOTYPE( void rewind, (FILE *_stream) ); +_PROTOTYPE( void clearerr, (FILE *_stream) ); +_PROTOTYPE( int feof, (FILE *_stream) ); +_PROTOTYPE( int ferror, (FILE *_stream) ); +_PROTOTYPE( void perror, (const char *_s) ); +_PROTOTYPE( int __fillbuf, (FILE *_stream) ); +_PROTOTYPE( int __flushbuf, (int _c, FILE *_stream) ); + +#define getchar() getc(stdin) +#define putchar(c) putc(c,stdout) +#define getc(p) (--(p)->_count >= 0 ? (int) (*(p)->_ptr++) : \ + __fillbuf(p)) +#define putc(c, p) (--(p)->_count >= 0 ? \ + (int) (*(p)->_ptr++ = (c)) : \ + __flushbuf((c),(p))) + +#define feof(p) (((p)->_flags & _IOEOF) != 0) +#define ferror(p) (((p)->_flags & _IOERR) != 0) +#define clearerr(p) ((p)->_flags &= ~(_IOERR|_IOEOF)) + +#ifdef _POSIX_SOURCE +_PROTOTYPE( int fileno, (FILE *_stream) ); +_PROTOTYPE (FILE *fdopen, (int _fildes, const char *_types) ); +#define fileno(stream) ((stream)->_fd) +#define L_ctermid 255 /* required by POSIX */ +#define L_cuserid 255 /* required by POSIX */ +#endif + +#ifdef _MINIX +_PROTOTYPE(FILE *popen, (const char *_command, const char *_type)); +_PROTOTYPE(int pclose, (FILE *_stream)); +_PROTOTYPE(int snprintf, (char *_s, size_t _n, const char *_format, ...)); +_PROTOTYPE(int vsnprintf, (char *_s, size_t _n, const char *_format, + char *_arg) ); +#endif + +#endif /* _STDIO_H */ diff --git a/include/stdlib.h b/include/stdlib.h new file mode 100755 index 000000000..8f803c2e9 --- /dev/null +++ b/include/stdlib.h @@ -0,0 +1,73 @@ +/* The <stdlib.h> header defines certain common macros, types, and functions.*/ + +#ifndef _STDLIB_H +#define _STDLIB_H + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +/* The macros are NULL, EXIT_FAILURE, EXIT_SUCCESS, RAND_MAX, and MB_CUR_MAX.*/ +#define NULL ((void *)0) + +#define EXIT_FAILURE 1 /* standard error return using exit() */ +#define EXIT_SUCCESS 0 /* successful return using exit() */ +#define RAND_MAX 32767 /* largest value generated by rand() */ +#define MB_CUR_MAX 1 /* max value of multibyte character in MINIX */ + +typedef struct { int quot, rem; } div_t; +typedef struct { long quot, rem; } ldiv_t; + +/* The types are size_t, wchar_t, div_t, and ldiv_t. */ +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; /* type returned by sizeof */ +#endif + +#ifndef _WCHAR_T +#define _WCHAR_T +typedef char wchar_t; /* type expanded character set */ +#endif + +/* Function Prototypes. */ +_PROTOTYPE( void abort, (void) ); +_PROTOTYPE( int abs, (int _j) ); +_PROTOTYPE( int atexit, (void (*_func)(void)) ); +_PROTOTYPE( double atof, (const char *_nptr) ); +_PROTOTYPE( int atoi, (const char *_nptr) ); +_PROTOTYPE( long atol, (const char *_nptr) ); +_PROTOTYPE( void *calloc, (size_t _nmemb, size_t _size) ); +_PROTOTYPE( div_t div, (int _numer, int _denom) ); +_PROTOTYPE( void exit, (int _status) ); +_PROTOTYPE( void free, (void *_ptr) ); +_PROTOTYPE( char *getenv, (const char *_name) ); +_PROTOTYPE( long labs, (long _j) ); +_PROTOTYPE( ldiv_t ldiv, (long _numer, long _denom) ); +_PROTOTYPE( void *malloc, (size_t _size) ); +_PROTOTYPE( int mblen, (const char *_s, size_t _n) ); +_PROTOTYPE( size_t mbstowcs, (wchar_t *_pwcs, const char *_s, size_t _n)); +_PROTOTYPE( int mbtowc, (wchar_t *_pwc, const char *_s, size_t _n) ); +_PROTOTYPE( int rand, (void) ); +_PROTOTYPE( void *realloc, (void *_ptr, size_t _size) ); +_PROTOTYPE( void srand, (unsigned int _seed) ); +_PROTOTYPE( double strtod, (const char *_nptr, char **_endptr) ); +_PROTOTYPE( long strtol, (const char *_nptr, char **_endptr, int _base) ); +_PROTOTYPE( int system, (const char *_string) ); +_PROTOTYPE( size_t wcstombs, (char *_s, const wchar_t *_pwcs, size_t _n)); +_PROTOTYPE( int wctomb, (char *_s, wchar_t _wchar) ); +_PROTOTYPE( void *bsearch, (const void *_key, const void *_base, + size_t _nmemb, size_t _size, + int (*compar) (const void *, const void *)) ); +_PROTOTYPE( void qsort, (void *_base, size_t _nmemb, size_t _size, + int (*compar) (const void *, const void *)) ); +_PROTOTYPE( unsigned long int strtoul, + (const char *_nptr, char **_endptr, int _base) ); + +#ifdef _MINIX +_PROTOTYPE( int putenv, (const char *_name) ); +_PROTOTYPE(int getopt, (int _argc, char **_argv, char *_opts)); +extern char *optarg; +extern int optind, opterr, optopt; +#endif /* _MINIX */ + +#endif /* STDLIB_H */ diff --git a/include/string.h b/include/string.h new file mode 100755 index 000000000..bd93957d2 --- /dev/null +++ b/include/string.h @@ -0,0 +1,59 @@ +/* The <string.h> header contains prototypes for the string handling + * functions. + */ + +#ifndef _STRING_H +#define _STRING_H + +#define NULL ((void *)0) + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; /* type returned by sizeof */ +#endif /*_SIZE_T */ + +/* Function Prototypes. */ +#ifndef _ANSI_H +#include <ansi.h> +#endif + +_PROTOTYPE( void *memchr, (const void *_s, int _c, size_t _n) ); +_PROTOTYPE( int memcmp, (const void *_s1, const void *_s2, size_t _n) ); +_PROTOTYPE( void *memcpy, (void *_s1, const void *_s2, size_t _n) ); +_PROTOTYPE( void *memmove, (void *_s1, const void *_s2, size_t _n) ); +_PROTOTYPE( void *memset, (void *_s, int _c, size_t _n) ); +_PROTOTYPE( char *strcat, (char *_s1, const char *_s2) ); +_PROTOTYPE( char *strchr, (const char *_s, int _c) ); +_PROTOTYPE( int strncmp, (const char *_s1, const char *_s2, size_t _n) ); +_PROTOTYPE( int strcmp, (const char *_s1, const char *_s2) ); +_PROTOTYPE( int strcoll, (const char *_s1, const char *_s2) ); +_PROTOTYPE( char *strcpy, (char *_s1, const char *_s2) ); +_PROTOTYPE( size_t strcspn, (const char *_s1, const char *_s2) ); +_PROTOTYPE( char *strerror, (int _errnum) ); +_PROTOTYPE( size_t strlen, (const char *_s) ); +_PROTOTYPE( char *strncat, (char *_s1, const char *_s2, size_t _n) ); +_PROTOTYPE( char *strncpy, (char *_s1, const char *_s2, size_t _n) ); +_PROTOTYPE( char *strpbrk, (const char *_s1, const char *_s2) ); +_PROTOTYPE( char *strrchr, (const char *_s, int _c) ); +_PROTOTYPE( size_t strspn, (const char *_s1, const char *_s2) ); +_PROTOTYPE( char *strstr, (const char *_s1, const char *_s2) ); +_PROTOTYPE( char *strtok, (char *_s1, const char *_s2) ); +_PROTOTYPE( size_t strxfrm, (char *_s1, const char *_s2, size_t _n) ); + +#ifdef _MINIX +/* For backward compatibility. */ +_PROTOTYPE( char *index, (const char *_s, int _charwanted) ); +_PROTOTYPE( char *rindex, (const char *_s, int _charwanted) ); +_PROTOTYPE( void bcopy, (const void *_src, void *_dst, size_t _length) ); +_PROTOTYPE( int bcmp, (const void *_s1, const void *_s2, size_t _length)); +_PROTOTYPE( void bzero, (void *_dst, size_t _length) ); +_PROTOTYPE( void *memccpy, (char *_dst, const char *_src, int _ucharstop, + size_t _size) ); +/* Misc. extra functions */ +_PROTOTYPE( int strcasecmp, (const char *_s1, const char *_s2) ); +_PROTOTYPE( int strncasecmp, (const char *_s1, const char *_s2, + size_t _len) ); +_PROTOTYPE( size_t strnlen, (const char *_s, size_t _n) ); +#endif + +#endif /* _STRING_H */ diff --git a/include/sys/asynchio.h b/include/sys/asynchio.h new file mode 100755 index 000000000..3bde7128d --- /dev/null +++ b/include/sys/asynchio.h @@ -0,0 +1,40 @@ +/* asynchio.h - Asynchronous I/O Author: Kees J. Bot + * 26 Jan 1995 + * This is just a fake async I/O library to be used for programs + * written for Minix-vmd that must also run under standard Minix. + * This limits the number of ugly #ifdefs somewhat. The programs must + * be restricted to performing just one service, of course. + */ +#ifndef _SYS__ASYNCHIO_H +#define _SYS__ASYNCHIO_H + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +typedef struct { + char state; + char op; + char fd; + char req; + void *data; + ssize_t count; + int errno; +} asynchio_t; + +#define ASYN_NONBLOCK 0x01 + +struct timeval { long tv_sec, tv_usec; }; + +#define EINPROGRESS EINTR +#define ASYN_INPROGRESS EINPROGRESS + +void asyn_init(asynchio_t *_asyn); +ssize_t asyn_read(asynchio_t *_asyn, int _fd, void *_buf, size_t _len); +ssize_t asyn_write(asynchio_t *_asyn, int _fd, const void *_buf, size_t _len); +int asyn_ioctl(asynchio_t *_asyn, int _fd, unsigned long _request, void *_data); +int asyn_wait(asynchio_t *_asyn, int _flags, struct timeval *to); +int asyn_synch(asynchio_t *_asyn, int _fd); +int asyn_close(asynchio_t *_asyn, int _fd); + +#endif /* _SYS__ASYNCHIO_H */ diff --git a/include/sys/dir.h b/include/sys/dir.h new file mode 100755 index 000000000..bb2373de1 --- /dev/null +++ b/include/sys/dir.h @@ -0,0 +1,19 @@ +/* The <dir.h> header gives the layout of a directory. */ + +#ifndef _DIR_H +#define _DIR_H + +#include <sys/types.h> + +#define DIRBLKSIZ 512 /* size of directory block */ + +#ifndef DIRSIZ +#define DIRSIZ 60 +#endif + +struct direct { + ino_t d_ino; + char d_name[DIRSIZ]; +}; + +#endif /* _DIR_H */ diff --git a/include/sys/ioc_disk.h b/include/sys/ioc_disk.h new file mode 100755 index 000000000..d5faae3d9 --- /dev/null +++ b/include/sys/ioc_disk.h @@ -0,0 +1,15 @@ +/* sys/ioc_disk.h - Disk ioctl() command codes. Author: Kees J. Bot + * 23 Nov 2002 + * + */ + +#ifndef _S_I_DISK_H +#define _S_I_DISK_H + +#include <minix/ioctl.h> + +#define DIOCSETP _IOW('d', 3, struct partition) +#define DIOCGETP _IOR('d', 4, struct partition) +#define DIOCEJECT _IO ('d', 5) + +#endif /* _S_I_DISK_H */ diff --git a/include/sys/ioc_memory.h b/include/sys/ioc_memory.h new file mode 100755 index 000000000..5a9ff7511 --- /dev/null +++ b/include/sys/ioc_memory.h @@ -0,0 +1,19 @@ +/* sys/ioc_memory.h - Memory ioctl() command codes. + * Author: Kees J. Bot + * 23 Nov 2002 + * + */ + +#ifndef _S_I_MEMORY_H +#define _S_I_MEMORY_H + +#include <minix/ioctl.h> + +#define MIOCRAMSIZE _IOW('m', 3, u32_t) +#define MIOCSPSINFO _IOW('m', 4, void *) +#define MIOCGPSINFO _IOR('m', 5, struct psinfo) +#define MIOCINT86 _IORW('m', 6, struct mio_int86) +#define MIOCGLDT86 _IORW('m', 7, struct mio_ldt86) +#define MIOCSLDT86 _IOW('m', 8, struct mio_ldt86) + +#endif /* _S_I_MEMORY_H */ diff --git a/include/sys/ioc_scsi.h b/include/sys/ioc_scsi.h new file mode 100755 index 000000000..23c267a93 --- /dev/null +++ b/include/sys/ioc_scsi.h @@ -0,0 +1,13 @@ +/* sys/ioc_scsi.h - SCSI ioctl() command codes. Author: Kees J. Bot + * 23 Nov 2002 + * + */ + +#ifndef _S_I_SCSI_H +#define _S_I_SCSI_H + +#include <minix/ioctl.h> + +#define SCIOCCMD _IOW('S', 1, struct scsicmd) + +#endif /* _S_I_SCSI_H */ diff --git a/include/sys/ioc_sound.h b/include/sys/ioc_sound.h new file mode 100755 index 000000000..eb0338488 --- /dev/null +++ b/include/sys/ioc_sound.h @@ -0,0 +1,30 @@ +/* sys/ioc_sound.h - Sound ioctl() command codes. Author: Kees J. Bot + * 23 Nov 2002 + * + */ + +#ifndef _S_I_SOUND_H +#define _S_I_SOUND_H + +#include <minix/ioctl.h> + +/* Soundcard DSP ioctls. */ +#define DSPIORATE _IOR('s', 1, unsigned int) +#define DSPIOSTEREO _IOR('s', 2, unsigned int) +#define DSPIOSIZE _IOR('s', 3, unsigned int) +#define DSPIOBITS _IOR('s', 4, unsigned int) +#define DSPIOSIGN _IOR('s', 5, unsigned int) +#define DSPIOMAX _IOW('s', 6, unsigned int) +#define DSPIORESET _IO ('s', 7) + +/* Soundcard mixer ioctls. */ +#define MIXIOGETVOLUME _IORW('s', 10, struct volume_level) +#define MIXIOGETINPUTLEFT _IORW('s', 11, struct inout_ctrl) +#define MIXIOGETINPUTRIGHT _IORW('s', 12, struct inout_ctrl) +#define MIXIOGETOUTPUT _IORW('s', 13, struct inout_ctrl) +#define MIXIOSETVOLUME _IORW('s', 20, struct volume_level) +#define MIXIOSETINPUTLEFT _IORW('s', 21, struct inout_ctrl) +#define MIXIOSETINPUTRIGHT _IORW('s', 22, struct inout_ctrl) +#define MIXIOSETOUTPUT _IORW('s', 23, struct inout_ctrl) + +#endif /* _S_I_SOUND_H */ diff --git a/include/sys/ioc_tape.h b/include/sys/ioc_tape.h new file mode 100755 index 000000000..f3cda0d83 --- /dev/null +++ b/include/sys/ioc_tape.h @@ -0,0 +1,15 @@ +/* sys/ioc_tape.h - Magnetic Tape ioctl() command codes. + * Author: Kees J. Bot + * 23 Nov 2002 + * + */ + +#ifndef _S_I_TAPE_H +#define _S_I_TAPE_H + +#include <minix/ioctl.h> + +#define MTIOCTOP _IOW('M', 1, struct mtop) +#define MTIOCGET _IOR('M', 2, struct mtget) + +#endif /* _S_I_TAPE_H */ diff --git a/include/sys/ioc_tty.h b/include/sys/ioc_tty.h new file mode 100755 index 000000000..4105eccf8 --- /dev/null +++ b/include/sys/ioc_tty.h @@ -0,0 +1,36 @@ +/* sys/ioc_tty.h - Terminal ioctl() command codes. + * Author: Kees J. Bot + * 23 Nov 2002 + * + */ + +#ifndef _S_I_TTY_H +#define _S_I_TTY_H + +#include <minix/ioctl.h> + +/* Terminal ioctls. */ +#define TCGETS _IOR('T', 8, struct termios) /* tcgetattr */ +#define TCSETS _IOW('T', 9, struct termios) /* tcsetattr, TCSANOW */ +#define TCSETSW _IOW('T', 10, struct termios) /* tcsetattr, TCSADRAIN */ +#define TCSETSF _IOW('T', 11, struct termios) /* tcsetattr, TCSAFLUSH */ +#define TCSBRK _IOW('T', 12, int) /* tcsendbreak */ +#define TCDRAIN _IO ('T', 13) /* tcdrain */ +#define TCFLOW _IOW('T', 14, int) /* tcflow */ +#define TCFLSH _IOW('T', 15, int) /* tcflush */ +#define TIOCGWINSZ _IOR('T', 16, struct winsize) +#define TIOCSWINSZ _IOW('T', 17, struct winsize) +#define TIOCGPGRP _IOW('T', 18, int) +#define TIOCSPGRP _IOW('T', 19, int) +#define TIOCSFON _IOW('T', 20, u8_t [8192]) + +/* Legacy <sgtty.h> */ +#define TIOCGETP _IOR('t', 1, struct sgttyb) +#define TIOCSETP _IOW('t', 2, struct sgttyb) +#define TIOCGETC _IOR('t', 3, struct tchars) +#define TIOCSETC _IOW('t', 4, struct tchars) + +/* Keyboard ioctls. */ +#define KIOCSMAP _IOW('k', 3, keymap_t) + +#endif /* _S_I_TTY_H */ diff --git a/include/sys/ioctl.h b/include/sys/ioctl.h new file mode 100755 index 000000000..f49b88236 --- /dev/null +++ b/include/sys/ioctl.h @@ -0,0 +1,24 @@ +/* sys/ioctl.h - All ioctl() command codes. Author: Kees J. Bot + * 23 Nov 2002 + * + * This header file includes all other ioctl command code headers. + */ + +#ifndef _S_IOCTL_H +#define _S_IOCTL_H + +/* A driver that uses ioctls claims a character for its series of commands. + * For instance: #define TCGETS _IOR('T', 8, struct termios) + * This is a terminal ioctl that uses the character 'T'. The character(s) + * used in each header file are shown in the comment following. + */ + +#include <sys/ioc_tty.h> /* 'T' 't' 'k' */ +#include <net/ioctl.h> /* 'n' */ +#include <sys/ioc_disk.h> /* 'd' */ +#include <sys/ioc_memory.h> /* 'm' */ +#include <sys/ioc_tape.h> /* 'M' */ +#include <sys/ioc_scsi.h> /* 'S' */ +#include <sys/ioc_sound.h> /* 's' */ + +#endif /* _S_IOCTL_H */ diff --git a/include/sys/mtio.h b/include/sys/mtio.h new file mode 100755 index 000000000..a5e16e5ef --- /dev/null +++ b/include/sys/mtio.h @@ -0,0 +1,45 @@ +/* <sys/mtio.h> magnetic tape commands Author: Kees J. Bot + */ + +#ifndef _SYS__MTIO_H +#define _SYS__MTIO_H + +/* Tape operations: ioctl(fd, MTIOCTOP, &struct mtop) */ + +struct mtop { + short mt_op; /* Operation (MTWEOF, etc.) */ + int mt_count; /* Repeat count. */ +}; + +#define MTWEOF 0 /* Write End-Of-File Marker */ +#define MTFSF 1 /* Forward Space File mark */ +#define MTBSF 2 /* Backward Space File mark */ +#define MTFSR 3 /* Forward Space Record */ +#define MTBSR 4 /* Backward Space Record */ +#define MTREW 5 /* Rewind tape */ +#define MTOFFL 6 /* Rewind and take Offline */ +#define MTNOP 7 /* No-Operation, set status only */ +#define MTRETEN 8 /* Retension (completely wind and rewind) */ +#define MTERASE 9 /* Erase the tape and rewind */ +#define MTEOM 10 /* Position at End-Of-Media */ +#define MTMODE 11 /* Select tape density */ +#define MTBLKZ 12 /* Select tape block size */ + +/* Tape status: ioctl(fd, MTIOCGET, &struct mtget) */ + +struct mtget { + short mt_type; /* Type of tape device. */ + + /* Device dependent "registers". */ + short mt_dsreg; /* Drive status register. */ + short mt_erreg; /* Error register. */ + short dummy; /* (alignment) */ + + /* Misc info. */ + off_t mt_resid; /* Residual count. */ + off_t mt_fileno; /* Current File Number. */ + off_t mt_blkno; /* Current Block Number within file. */ + off_t mt_blksize; /* Current block size. */ +}; + +#endif /* _SYS__MTIO_H */ diff --git a/include/sys/ptrace.h b/include/sys/ptrace.h new file mode 100755 index 000000000..0b02a7270 --- /dev/null +++ b/include/sys/ptrace.h @@ -0,0 +1,27 @@ +/* <sys/ptrace.h> + * definitions for ptrace(2) + */ + +#ifndef _PTRACE_H +#define _PTRACE_H + +#define T_STOP -1 /* stop the process */ +#define T_OK 0 /* enable tracing by parent for this process */ +#define T_GETINS 1 /* return value from instruction space */ +#define T_GETDATA 2 /* return value from data space */ +#define T_GETUSER 3 /* return value from user process table */ +#define T_SETINS 4 /* set value from instruction space */ +#define T_SETDATA 5 /* set value from data space */ +#define T_SETUSER 6 /* set value in user process table */ +#define T_RESUME 7 /* resume execution */ +#define T_EXIT 8 /* exit */ +#define T_STEP 9 /* set trace bit */ + +/* Function Prototypes. */ +#ifndef _ANSI_H +#include <ansi.h> +#endif + +_PROTOTYPE( long ptrace, (int _req, pid_t _pid, long _addr, long _data) ); + +#endif /* _PTRACE_H */ diff --git a/include/sys/sigcontext.h b/include/sys/sigcontext.h new file mode 100755 index 000000000..e6b238700 --- /dev/null +++ b/include/sys/sigcontext.h @@ -0,0 +1,146 @@ +#ifndef _SIGCONTEXT_H +#define _SIGCONTEXT_H + +/* The sigcontext structure is used by the sigreturn(2) system call. + * sigreturn() is seldom called by user programs, but it is used internally + * by the signal catching mechanism. + */ + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +#ifndef _CONFIG_H +#include <minix/config.h> +#endif + +#if !defined(CHIP) +#include "error, configuration is not known" +#endif + +/* The following structure should match the stackframe_s structure used + * by the kernel's context switching code. Floating point registers should + * be added in a different struct. + */ +#if (CHIP == INTEL) +struct sigregs { +#if _WORD_SIZE == 4 + short sr_gs; + short sr_fs; +#endif /* _WORD_SIZE == 4 */ + short sr_es; + short sr_ds; + int sr_di; + int sr_si; + int sr_bp; + int sr_st; /* stack top -- used in kernel */ + int sr_bx; + int sr_dx; + int sr_cx; + int sr_retreg; + int sr_retadr; /* return address to caller of save -- used + * in kernel */ + int sr_pc; + int sr_cs; + int sr_psw; + int sr_sp; + int sr_ss; +}; + +struct sigframe { /* stack frame created for signalled process */ + _PROTOTYPE( void (*sf_retadr), (void) ); + int sf_signo; + int sf_code; + struct sigcontext *sf_scp; + int sf_fp; + _PROTOTYPE( void (*sf_retadr2), (void) ); + struct sigcontext *sf_scpcopy; +}; + +#else +#if (CHIP == M68000) +struct sigregs { + long sr_retreg; /* d0 */ + long sr_d1; + long sr_d2; + long sr_d3; + long sr_d4; + long sr_d5; + long sr_d6; + long sr_d7; + long sr_a0; + long sr_a1; + long sr_a2; + long sr_a3; + long sr_a4; + long sr_a5; + long sr_a6; + long sr_sp; /* also known as a7 */ + long sr_pc; + short sr_psw; + short sr_dummy; /* make size multiple of 4 for system.c */ +}; +#else +#include "error, CHIP is not supported" +#endif +#endif /* CHIP == INTEL */ + +struct sigcontext { + int sc_flags; /* sigstack state to restore */ + long sc_mask; /* signal mask to restore */ + struct sigregs sc_regs; /* register set to restore */ +}; + +#if (CHIP == INTEL) +#if _WORD_SIZE == 4 +#define sc_gs sc_regs.sr_gs +#define sc_fs sc_regs.sr_fs +#endif /* _WORD_SIZE == 4 */ +#define sc_es sc_regs.sr_es +#define sc_ds sc_regs.sr_ds +#define sc_di sc_regs.sr_di +#define sc_si sc_regs.sr_si +#define sc_fp sc_regs.sr_bp +#define sc_st sc_regs.sr_st /* stack top -- used in kernel */ +#define sc_bx sc_regs.sr_bx +#define sc_dx sc_regs.sr_dx +#define sc_cx sc_regs.sr_cx +#define sc_retreg sc_regs.sr_retreg +#define sc_retadr sc_regs.sr_retadr /* return address to caller of + save -- used in kernel */ +#define sc_pc sc_regs.sr_pc +#define sc_cs sc_regs.sr_cs +#define sc_psw sc_regs.sr_psw +#define sc_sp sc_regs.sr_sp +#define sc_ss sc_regs.sr_ss +#endif /* CHIP == INTEL */ + +#if (CHIP == M68000) +#define sc_retreg sc_regs.sr_retreg +#define sc_d1 sc_regs.sr_d1 +#define sc_d2 sc_regs.sr_d2 +#define sc_d3 sc_regs.sr_d3 +#define sc_d4 sc_regs.sr_d4 +#define sc_d5 sc_regs.sr_d5 +#define sc_d6 sc_regs.sr_d6 +#define sc_d7 sc_regs.sr_d7 +#define sc_a0 sc_regs.sr_a0 +#define sc_a1 sc_regs.sr_a1 +#define sc_a2 sc_regs.sr_a2 +#define sc_a3 sc_regs.sr_a3 +#define sc_a4 sc_regs.sr_a4 +#define sc_a5 sc_regs.sr_a5 +#define sc_fp sc_regs.sr_a6 +#define sc_sp sc_regs.sr_sp +#define sc_pc sc_regs.sr_pc +#define sc_psw sc_regs.sr_psw +#endif /* CHIP == M68000 */ + +/* Values for sc_flags. Must agree with <minix/jmp_buf.h>. */ +#define SC_SIGCONTEXT 2 /* nonzero when signal context is included */ +#define SC_NOREGLOCALS 4 /* nonzero when registers are not to be + saved and restored */ + +_PROTOTYPE( int sigreturn, (struct sigcontext *_scp) ); + +#endif /* _SIGCONTEXT_H */ diff --git a/include/sys/stat.h b/include/sys/stat.h new file mode 100755 index 000000000..f1be28525 --- /dev/null +++ b/include/sys/stat.h @@ -0,0 +1,74 @@ +/* The <sys/stat.h> header defines a struct that is used in the stat() and + * fstat functions. The information in this struct comes from the i-node of + * some file. These calls are the only approved way to inspect i-nodes. + */ + +#ifndef _STAT_H +#define _STAT_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +struct stat { + dev_t st_dev; /* major/minor device number */ + ino_t st_ino; /* i-node number */ + mode_t st_mode; /* file mode, protection bits, etc. */ + short int st_nlink; /* # links; TEMPORARY HACK: should be nlink_t*/ + uid_t st_uid; /* uid of the file's owner */ + short int st_gid; /* gid; TEMPORARY HACK: should be gid_t */ + dev_t st_rdev; + off_t st_size; /* file size */ + time_t st_atime; /* time of last access */ + time_t st_mtime; /* time of last data modification */ + time_t st_ctime; /* time of last file status change */ +}; + +/* Traditional mask definitions for st_mode. */ +/* The ugly casts on only some of the definitions are to avoid suprising sign + * extensions such as S_IFREG != (mode_t) S_IFREG when ints are 32 bits. + */ +#define S_IFMT ((mode_t) 0170000) /* type of file */ +#define S_IFREG ((mode_t) 0100000) /* regular */ +#define S_IFBLK 0060000 /* block special */ +#define S_IFDIR 0040000 /* directory */ +#define S_IFCHR 0020000 /* character special */ +#define S_IFIFO 0010000 /* this is a FIFO */ +#define S_ISUID 0004000 /* set user id on execution */ +#define S_ISGID 0002000 /* set group id on execution */ + /* next is reserved for future use */ +#define S_ISVTX 01000 /* save swapped text even after use */ + +/* POSIX masks for st_mode. */ +#define S_IRWXU 00700 /* owner: rwx------ */ +#define S_IRUSR 00400 /* owner: r-------- */ +#define S_IWUSR 00200 /* owner: -w------- */ +#define S_IXUSR 00100 /* owner: --x------ */ + +#define S_IRWXG 00070 /* group: ---rwx--- */ +#define S_IRGRP 00040 /* group: ---r----- */ +#define S_IWGRP 00020 /* group: ----w---- */ +#define S_IXGRP 00010 /* group: -----x--- */ + +#define S_IRWXO 00007 /* others: ------rwx */ +#define S_IROTH 00004 /* others: ------r-- */ +#define S_IWOTH 00002 /* others: -------w- */ +#define S_IXOTH 00001 /* others: --------x */ + +/* The following macros test st_mode (from POSIX Sec. 5.6.1.1). */ +#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) /* is a reg file */ +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) /* is a directory */ +#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) /* is a char spec */ +#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) /* is a block spec */ +#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) /* is a pipe/FIFO */ + + +/* Function Prototypes. */ +_PROTOTYPE( int chmod, (const char *_path, Mode_t _mode) ); +_PROTOTYPE( int fstat, (int _fildes, struct stat *_buf) ); +_PROTOTYPE( int mkdir, (const char *_path, Mode_t _mode) ); +_PROTOTYPE( int mkfifo, (const char *_path, Mode_t _mode) ); +_PROTOTYPE( int stat, (const char *_path, struct stat *_buf) ); +_PROTOTYPE( mode_t umask, (Mode_t _cmask) ); + +#endif /* _STAT_H */ diff --git a/include/sys/statfs.h b/include/sys/statfs.h new file mode 100644 index 000000000..b1f2371c6 --- /dev/null +++ b/include/sys/statfs.h @@ -0,0 +1,16 @@ +/* Data for fstatfs() call. */ + +#ifndef _STATFS_H +#define _STATFS_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +struct statfs { + off_t f_bsize; /* file system block size */ +}; + +_PROTOTYPE( int fstatfs, (int fd, struct statfs *st) ); + +#endif /* _STATFS_H */ diff --git a/include/sys/svrctl.h b/include/sys/svrctl.h new file mode 100755 index 000000000..94d1cc04b --- /dev/null +++ b/include/sys/svrctl.h @@ -0,0 +1,58 @@ +/* +sys/svrctl.h + +Created: Feb 15, 1994 by Philip Homburg <philip@cs.vu.nl> +*/ + +#ifndef _SYS__SVRCTL_H +#define _SYS__SVRCTL_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +/* Server control commands have the same encoding as the commands for ioctls. */ +#include <minix/ioctl.h> + +/* MM controls. */ +#define MMSIGNON _IO ('M', 4) +#define MMSWAPON _IOW('M', 5, struct mmswapon) +#define MMSWAPOFF _IO ('M', 6) +#define MMGETPARAM _IOW('M', 5, struct sysgetenv) + +/* FS controls. */ +#define FSSIGNON _IOW('F', 2, struct fssignon) +#define FSDEVMAP _IORW('F', 5, struct fsdevmap) + +/* Kernel controls. */ +#define SYSSENDMASK _IO ('S', 4) +#define SYSSIGNON _IOR('S', 2, struct systaskinfo) +#define SYSGETENV _IOW('S', 5, struct sysgetenv) + +struct mmswapon { + u32_t offset; /* Starting offset within file. */ + u32_t size; /* Size of swap area. */ + char file[128]; /* Name of swap file/device. */ +}; + +/* TEMP!!! A proper system call must be created later. */ +#include "/usr/src/servers/fs/dmap.h" +struct fssignon { + dev_t dev; /* Device to manage. */ + enum dev_style style; /* Management style. */ +}; + +struct systaskinfo { + int proc_nr; /* Process number of caller. */ +}; + +struct sysgetenv { + char *key; /* Name requested. */ + size_t keylen; /* Length of name including \0. */ + char *val; /* Buffer for returned data. */ + size_t vallen; /* Size of return data buffer. */ +}; + +_PROTOTYPE( int svrctl, (int _request, void *_data) ); + +#endif /* _SYS__SVRCTL_H */ diff --git a/include/sys/times.h b/include/sys/times.h new file mode 100755 index 000000000..76862a1bd --- /dev/null +++ b/include/sys/times.h @@ -0,0 +1,25 @@ +/* The <times.h> header is for time times() system call. */ + +#ifndef _TIMES_H +#define _TIMES_H + +#ifndef _CLOCK_T +#define _CLOCK_T +typedef long clock_t; /* unit for system accounting */ +#endif + +struct tms { + clock_t tms_utime; + clock_t tms_stime; + clock_t tms_cutime; + clock_t tms_cstime; +}; + +/* Function Prototypes. */ +#ifndef _ANSI_H +#include <ansi.h> +#endif + +_PROTOTYPE( clock_t times, (struct tms *_buffer) ); + +#endif /* _TIMES_H */ diff --git a/include/sys/types.h b/include/sys/types.h new file mode 100755 index 000000000..c4a847ce6 --- /dev/null +++ b/include/sys/types.h @@ -0,0 +1,119 @@ +/* The <sys/types.h> header contains important data type definitions. + * It is considered good programming practice to use these definitions, + * instead of the underlying base type. By convention, all type names end + * with _t. + */ + +#ifndef _TYPES_H +#define _TYPES_H + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +/* The type size_t holds all results of the sizeof operator. At first glance, + * it seems obvious that it should be an unsigned int, but this is not always + * the case. For example, MINIX-ST (68000) has 32-bit pointers and 16-bit + * integers. When one asks for the size of a 70K struct or array, the result + * requires 17 bits to express, so size_t must be a long type. The type + * ssize_t is the signed version of size_t. + */ +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif + +#ifndef _SSIZE_T +#define _SSIZE_T +typedef int ssize_t; +#endif + +#ifndef _TIME_T +#define _TIME_T +typedef long time_t; /* time in sec since 1 Jan 1970 0000 GMT */ +#endif + +#ifndef _CLOCK_T +#define _CLOCK_T +typedef long clock_t; /* unit for system accounting */ +#endif + +#ifndef _SIGSET_T +#define _SIGSET_T +typedef unsigned long sigset_t; +#endif + +/* Types used in disk, inode, etc. data structures. */ +typedef short dev_t; /* holds (major|minor) device pair */ +typedef char gid_t; /* group id */ +typedef unsigned long ino_t; /* i-node number (V3 filesystem) */ +typedef unsigned short mode_t; /* file type and permissions bits */ +typedef short nlink_t; /* number of links to a file */ +typedef unsigned long off_t; /* offset within a file */ +typedef int pid_t; /* process id (must be signed) */ +typedef short uid_t; /* user id */ +typedef unsigned long zone_t; /* zone number */ +typedef unsigned long block_t; /* block number */ +typedef unsigned long bit_t; /* bit number in a bit map */ +typedef unsigned short zone1_t; /* zone number for V1 file systems */ +typedef unsigned short bitchunk_t; /* collection of bits in a bitmap */ + +typedef unsigned char u8_t; /* 8 bit type */ +typedef unsigned short u16_t; /* 16 bit type */ +typedef unsigned long u32_t; /* 32 bit type */ + +typedef char i8_t; /* 8 bit signed type */ +typedef short i16_t; /* 16 bit signed type */ +typedef long i32_t; /* 32 bit signed type */ + +typedef struct { u32_t _[2]; } u64_t; + +/* The following types are needed because MINIX uses K&R style function + * definitions (for maximum portability). When a short, such as dev_t, is + * passed to a function with a K&R definition, the compiler automatically + * promotes it to an int. The prototype must contain an int as the parameter, + * not a short, because an int is what an old-style function definition + * expects. Thus using dev_t in a prototype would be incorrect. It would be + * sufficient to just use int instead of dev_t in the prototypes, but Dev_t + * is clearer. + */ +typedef int Dev_t; +typedef int Gid_t; +typedef int Nlink_t; +typedef int Uid_t; +typedef int U8_t; +typedef unsigned long U32_t; +typedef int I8_t; +typedef int I16_t; +typedef long I32_t; + +/* ANSI C makes writing down the promotion of unsigned types very messy. When + * sizeof(short) == sizeof(int), there is no promotion, so the type stays + * unsigned. When the compiler is not ANSI, there is usually no loss of + * unsignedness, and there are usually no prototypes so the promoted type + * doesn't matter. The use of types like Ino_t is an attempt to use ints + * (which are not promoted) while providing information to the reader. + */ + +typedef unsigned long Ino_t; + +#if _EM_WSIZE == 2 +/*typedef unsigned int Ino_t; Ino_t is now 32 bits */ +typedef unsigned int Zone1_t; +typedef unsigned int Bitchunk_t; +typedef unsigned int U16_t; +typedef unsigned int Mode_t; + +#else /* _EM_WSIZE == 4, or _EM_WSIZE undefined */ +/*typedef int Ino_t; Ino_t is now 32 bits */ +typedef int Zone1_t; +typedef int Bitchunk_t; +typedef int U16_t; +typedef int Mode_t; + +#endif /* _EM_WSIZE == 2, etc */ + +/* Signal handler type, e.g. SIG_IGN */ +typedef void _PROTOTYPE( (*sighandler_t), (int) ); + +#endif /* _TYPES_H */ diff --git a/include/sys/utsname.h b/include/sys/utsname.h new file mode 100755 index 000000000..0066689b0 --- /dev/null +++ b/include/sys/utsname.h @@ -0,0 +1,22 @@ +/* The <sys/utsname.h> header gives the system name. */ + +#ifndef _UTSNAME_H +#define _UTSNAME_H + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +struct utsname { + char sysname[15+1]; + char nodename[255+1]; + char release[11+1]; + char version[7+1]; + char machine[11+1]; + char arch[11+1]; +}; + +/* Function Prototypes. */ +_PROTOTYPE( int uname, (struct utsname *_name) ); + +#endif /* _UTSNAME_H */ diff --git a/include/sys/wait.h b/include/sys/wait.h new file mode 100755 index 000000000..73d6f5595 --- /dev/null +++ b/include/sys/wait.h @@ -0,0 +1,40 @@ +/* The <sys/wait.h> header contains macros related to wait(). The value + * returned by wait() and waitpid() depends on whether the process + * terminated by an exit() call, was killed by a signal, or was stopped + * due to job control, as follows: + * + * High byte Low byte + * +---------------------+ + * exit(status) | status | 0 | + * +---------------------+ + * killed by signal | 0 | signal | + * +---------------------+ + * stopped (job control) | signal | 0177 | + * +---------------------+ + */ + +#ifndef _WAIT_H +#define _WAIT_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +#define _LOW(v) ( (v) & 0377) +#define _HIGH(v) ( ((v) >> 8) & 0377) + +#define WNOHANG 1 /* do not wait for child to exit */ +#define WUNTRACED 2 /* for job control; not implemented */ + +#define WIFEXITED(s) (_LOW(s) == 0) /* normal exit */ +#define WEXITSTATUS(s) (_HIGH(s)) /* exit status */ +#define WTERMSIG(s) (_LOW(s) & 0177) /* sig value */ +#define WIFSIGNALED(s) (((unsigned int)(s)-1 & 0xFFFF) < 0xFF) /* signaled */ +#define WIFSTOPPED(s) (_LOW(s) == 0177) /* stopped */ +#define WSTOPSIG(s) (_HIGH(s) & 0377) /* stop signal */ + +/* Function Prototypes. */ +_PROTOTYPE( pid_t wait, (int *_stat_loc) ); +_PROTOTYPE( pid_t waitpid, (pid_t _pid, int *_stat_loc, int _options) ); + +#endif /* _WAIT_H */ diff --git a/include/tar.h b/include/tar.h new file mode 100755 index 000000000..d176a4ef7 --- /dev/null +++ b/include/tar.h @@ -0,0 +1,71 @@ +/* The <tar.h> header is used with the tape archiver, tar. */ + +#ifndef _TAR_H +#define _TAR_H + +#define TBLOCK 512 +#define NAMSIZ 100 +#define PFXSIZ 155 + +#define TMODLEN 8 +#define TUIDLEN 8 +#define TGIDLEN 8 +#define TSIZLEN 12 +#define TMTMLEN 12 +#define TCKSLEN 8 + +#define TMAGIC "ustar" +#define TMAGLEN 6 +#define TVERSION "00" +#define TVERSLEN 2 +#define TUNMLEN 32 +#define TGNMLEN 32 +#define TDEVLEN 8 + +#define REGTYPE '0' +#define AREGTYPE '\0' +#define LNKTYPE '1' +#define SYMTYPE '2' +#define CHRTYPE '3' +#define BLKTYPE '4' +#define DIRTYPE '5' +#define FIFOTYPE '6' +#define CONTTYPE '7' + +#define TSUID 04000 +#define TSGID 02000 +#define TSVTX 01000 + +#define TUREAD 00400 +#define TUWRITE 00200 +#define TUEXEC 00100 +#define TGREAD 00040 +#define TGWRITE 00020 +#define TGEXEC 00010 +#define TOREAD 00004 +#define TOWRITE 00002 +#define TOEXEC 00001 + +union hblock { + char dummy[TBLOCK]; + struct header { + char name[NAMSIZ]; + char mode[TMODLEN]; + char uid[TUIDLEN]; + char gid[TGIDLEN]; + char size[TSIZLEN]; + char mtime[TMTMLEN]; + char chksum[TCKSLEN]; + char typeflag; + char linkname[NAMSIZ]; + char magic[TMAGLEN]; + char version[TVERSLEN]; + char uname[TUNMLEN]; + char gname[TGNMLEN]; + char devmajor[TDEVLEN]; + char devminor[TDEVLEN]; + char prefix[PFXSIZ]; + } dbuf; +}; + +#endif /* _TAR_H */ diff --git a/include/termcap.h b/include/termcap.h new file mode 100755 index 000000000..9f9495ce4 --- /dev/null +++ b/include/termcap.h @@ -0,0 +1,13 @@ +#ifndef _TERMCAP_H +#define _TERMCAP_H + +#include <ansi.h> + +_PROTOTYPE( int tgetent, (char *_bp, char *_name) ); +_PROTOTYPE( int tgetflag, (char *_id) ); +_PROTOTYPE( int tgetnum, (char *_id) ); +_PROTOTYPE( char *tgetstr, (char *_id, char **_area) ); +_PROTOTYPE( char *tgoto, (char *_cm, int _destcol, int _destline) ); +_PROTOTYPE( int tputs, (char *_cp, int _affcnt, void (*_outc)(int)) ); + +#endif /* _TERMCAP_H */ diff --git a/include/termios.h b/include/termios.h new file mode 100755 index 000000000..f2e01743e --- /dev/null +++ b/include/termios.h @@ -0,0 +1,207 @@ +/* The <termios.h> header is used for controlling tty modes. */ + +#ifndef _TERMIOS_H +#define _TERMIOS_H + +typedef unsigned short tcflag_t; +typedef unsigned char cc_t; +typedef unsigned int speed_t; + +#define NCCS 20 /* size of cc_c array, some extra space + * for extensions. */ + +/* Primary terminal control structure. POSIX Table 7-1. */ +struct termios { + tcflag_t c_iflag; /* input modes */ + tcflag_t c_oflag; /* output modes */ + tcflag_t c_cflag; /* control modes */ + tcflag_t c_lflag; /* local modes */ + speed_t c_ispeed; /* input speed */ + speed_t c_ospeed; /* output speed */ + cc_t c_cc[NCCS]; /* control characters */ +}; + +/* Values for termios c_iflag bit map. POSIX Table 7-2. */ +#define BRKINT 0x0001 /* signal interrupt on break */ +#define ICRNL 0x0002 /* map CR to NL on input */ +#define IGNBRK 0x0004 /* ignore break */ +#define IGNCR 0x0008 /* ignore CR */ +#define IGNPAR 0x0010 /* ignore characters with parity errors */ +#define INLCR 0x0020 /* map NL to CR on input */ +#define INPCK 0x0040 /* enable input parity check */ +#define ISTRIP 0x0080 /* mask off 8th bit */ +#define IXOFF 0x0100 /* enable start/stop input control */ +#define IXON 0x0200 /* enable start/stop output control */ +#define PARMRK 0x0400 /* mark parity errors in the input queue */ + +/* Values for termios c_oflag bit map. POSIX Sec. 7.1.2.3. */ +#define OPOST 0x0001 /* perform output processing */ + +/* Values for termios c_cflag bit map. POSIX Table 7-3. */ +#define CLOCAL 0x0001 /* ignore modem status lines */ +#define CREAD 0x0002 /* enable receiver */ +#define CSIZE 0x000C /* number of bits per character */ +#define CS5 0x0000 /* if CSIZE is CS5, characters are 5 bits */ +#define CS6 0x0004 /* if CSIZE is CS6, characters are 6 bits */ +#define CS7 0x0008 /* if CSIZE is CS7, characters are 7 bits */ +#define CS8 0x000C /* if CSIZE is CS8, characters are 8 bits */ +#define CSTOPB 0x0010 /* send 2 stop bits if set, else 1 */ +#define HUPCL 0x0020 /* hang up on last close */ +#define PARENB 0x0040 /* enable parity on output */ +#define PARODD 0x0080 /* use odd parity if set, else even */ + +/* Values for termios c_lflag bit map. POSIX Table 7-4. */ +#define ECHO 0x0001 /* enable echoing of input characters */ +#define ECHOE 0x0002 /* echo ERASE as backspace */ +#define ECHOK 0x0004 /* echo KILL */ +#define ECHONL 0x0008 /* echo NL */ +#define ICANON 0x0010 /* canonical input (erase and kill enabled) */ +#define IEXTEN 0x0020 /* enable extended functions */ +#define ISIG 0x0040 /* enable signals */ +#define NOFLSH 0x0080 /* disable flush after interrupt or quit */ +#define TOSTOP 0x0100 /* send SIGTTOU (job control, not implemented*/ + +/* Indices into c_cc array. Default values in parentheses. POSIX Table 7-5. */ +#define VEOF 0 /* cc_c[VEOF] = EOF char (^D) */ +#define VEOL 1 /* cc_c[VEOL] = EOL char (undef) */ +#define VERASE 2 /* cc_c[VERASE] = ERASE char (^H) */ +#define VINTR 3 /* cc_c[VINTR] = INTR char (DEL) */ +#define VKILL 4 /* cc_c[VKILL] = KILL char (^U) */ +#define VMIN 5 /* cc_c[VMIN] = MIN value for timer */ +#define VQUIT 6 /* cc_c[VQUIT] = QUIT char (^\) */ +#define VTIME 7 /* cc_c[VTIME] = TIME value for timer */ +#define VSUSP 8 /* cc_c[VSUSP] = SUSP (^Z, ignored) */ +#define VSTART 9 /* cc_c[VSTART] = START char (^S) */ +#define VSTOP 10 /* cc_c[VSTOP] = STOP char (^Q) */ + +#define _POSIX_VDISABLE (cc_t)0xFF /* You can't even generate this + * character with 'normal' keyboards. + * But some language specific keyboards + * can generate 0xFF. It seems that all + * 256 are used, so cc_t should be a + * short... + */ + +/* Values for the baud rate settings. POSIX Table 7-6. */ +#define B0 0x0000 /* hang up the line */ +#define B50 0x1000 /* 50 baud */ +#define B75 0x2000 /* 75 baud */ +#define B110 0x3000 /* 110 baud */ +#define B134 0x4000 /* 134.5 baud */ +#define B150 0x5000 /* 150 baud */ +#define B200 0x6000 /* 200 baud */ +#define B300 0x7000 /* 300 baud */ +#define B600 0x8000 /* 600 baud */ +#define B1200 0x9000 /* 1200 baud */ +#define B1800 0xA000 /* 1800 baud */ +#define B2400 0xB000 /* 2400 baud */ +#define B4800 0xC000 /* 4800 baud */ +#define B9600 0xD000 /* 9600 baud */ +#define B19200 0xE000 /* 19200 baud */ +#define B38400 0xF000 /* 38400 baud */ + +/* Optional actions for tcsetattr(). POSIX Sec. 7.2.1.2. */ +#define TCSANOW 1 /* changes take effect immediately */ +#define TCSADRAIN 2 /* changes take effect after output is done */ +#define TCSAFLUSH 3 /* wait for output to finish and flush input */ + +/* Queue_selector values for tcflush(). POSIX Sec. 7.2.2.2. */ +#define TCIFLUSH 1 /* flush accumulated input data */ +#define TCOFLUSH 2 /* flush accumulated output data */ +#define TCIOFLUSH 3 /* flush accumulated input and output data */ + +/* Action values for tcflow(). POSIX Sec. 7.2.2.2. */ +#define TCOOFF 1 /* suspend output */ +#define TCOON 2 /* restart suspended output */ +#define TCIOFF 3 /* transmit a STOP character on the line */ +#define TCION 4 /* transmit a START character on the line */ + + +/* Function Prototypes. */ +#ifndef _ANSI_H +#include <ansi.h> +#endif + +_PROTOTYPE( int tcsendbreak, (int _fildes, int _duration) ); +_PROTOTYPE( int tcdrain, (int _filedes) ); +_PROTOTYPE( int tcflush, (int _filedes, int _queue_selector) ); +_PROTOTYPE( int tcflow, (int _filedes, int _action) ); +_PROTOTYPE( speed_t cfgetispeed, (const struct termios *_termios_p) ); +_PROTOTYPE( speed_t cfgetospeed, (const struct termios *_termios_p) ); +_PROTOTYPE( int cfsetispeed, (struct termios *_termios_p, speed_t _speed) ); +_PROTOTYPE( int cfsetospeed, (struct termios *_termios_p, speed_t _speed) ); +_PROTOTYPE( int tcgetattr, (int _filedes, struct termios *_termios_p) ); +_PROTOTYPE( int tcsetattr, \ + (int _filedes, int _opt_actions, const struct termios *_termios_p) ); + +#define cfgetispeed(termios_p) ((termios_p)->c_ispeed) +#define cfgetospeed(termios_p) ((termios_p)->c_ospeed) +#define cfsetispeed(termios_p, speed) ((termios_p)->c_ispeed = (speed), 0) +#define cfsetospeed(termios_p, speed) ((termios_p)->c_ospeed = (speed), 0) + +#ifdef _MINIX +/* Here are the local extensions to the POSIX standard for Minix. Posix + * conforming programs are not able to access these, and therefore they are + * only defined when a Minix program is compiled. + */ + +/* Extensions to the termios c_iflag bit map. */ +#define IXANY 0x0800 /* allow any key to continue ouptut */ + +/* Extensions to the termios c_oflag bit map. They are only active iff + * OPOST is enabled. */ +#define ONLCR 0x0002 /* Map NL to CR-NL on output */ +#define XTABS 0x0004 /* Expand tabs to spaces */ +#define ONOEOT 0x0008 /* discard EOT's (^D) on output) */ + +/* Extensions to the termios c_lflag bit map. */ +#define LFLUSHO 0x0200 /* Flush output. */ + +/* Extensions to the c_cc array. */ +#define VREPRINT 11 /* cc_c[VREPRINT] (^R) */ +#define VLNEXT 12 /* cc_c[VLNEXT] (^V) */ +#define VDISCARD 13 /* cc_c[VDISCARD] (^O) */ + +/* Extensions to baud rate settings. */ +#define B57600 0x0100 /* 57600 baud */ +#define B115200 0x0200 /* 115200 baud */ + +/* These are the default settings used by the kernel and by 'stty sane' */ + +#define TCTRL_DEF (CREAD | CS8 | HUPCL) +#define TINPUT_DEF (BRKINT | ICRNL | IXON | IXANY) +#define TOUTPUT_DEF (OPOST | ONLCR) +#define TLOCAL_DEF (ISIG | IEXTEN | ICANON | ECHO | ECHOE) +#define TSPEED_DEF B9600 + +#define TEOF_DEF '\4' /* ^D */ +#define TEOL_DEF _POSIX_VDISABLE +#define TERASE_DEF '\10' /* ^H */ +#define TINTR_DEF '\177' /* ^? */ +#define TKILL_DEF '\25' /* ^U */ +#define TMIN_DEF 1 +#define TQUIT_DEF '\34' /* ^\ */ +#define TSTART_DEF '\21' /* ^Q */ +#define TSTOP_DEF '\23' /* ^S */ +#define TSUSP_DEF '\32' /* ^Z */ +#define TTIME_DEF 0 +#define TREPRINT_DEF '\22' /* ^R */ +#define TLNEXT_DEF '\26' /* ^V */ +#define TDISCARD_DEF '\17' /* ^O */ + +/* Window size. This information is stored in the TTY driver but not used. + * This can be used for screen based applications in a window environment. + * The ioctls TIOCGWINSZ and TIOCSWINSZ can be used to get and set this + * information. + */ + +struct winsize +{ + unsigned short ws_row; /* rows, in characters */ + unsigned short ws_col; /* columns, in characters */ + unsigned short ws_xpixel; /* horizontal size, pixels */ + unsigned short ws_ypixel; /* vertical size, pixels */ +}; +#endif /* _MINIX */ + +#endif /* _TERMIOS_H */ diff --git a/include/time.h b/include/time.h new file mode 100755 index 000000000..7e5a4b054 --- /dev/null +++ b/include/time.h @@ -0,0 +1,73 @@ +/* The <time.h> header is used by the procedures that deal with time. + * Handling time is surprisingly complicated, what with GMT, local time + * and other factors. Although the Bishop of Ussher (1581-1656) once + * calculated that based on the Bible, the world began on 12 Oct. 4004 BC + * at 9 o'clock in the morning, in the UNIX world time begins at midnight, + * 1 Jan. 1970 GMT. Before that, all was NULL and (void). + */ + +#ifndef _TIME_H +#define _TIME_H + +#define CLOCKS_PER_SEC 60 /* MINIX always uses 60 Hz, even in Europe */ + +#ifdef _POSIX_SOURCE +#define CLK_TCK CLOCKS_PER_SEC /* obsolescent mame for CLOCKS_PER_SEC */ +#endif + +#define NULL ((void *)0) + +#ifndef _SIZE_T +#define _SIZE_T +typedef unsigned int size_t; +#endif + +#ifndef _TIME_T +#define _TIME_T +typedef long time_t; /* time in sec since 1 Jan 1970 0000 GMT */ +#endif + +#ifndef _CLOCK_T +#define _CLOCK_T +typedef long clock_t; /* time in ticks since process started */ +#endif + +struct tm { + int tm_sec; /* seconds after the minute [0, 59] */ + int tm_min; /* minutes after the hour [0, 59] */ + int tm_hour; /* hours since midnight [0, 23] */ + int tm_mday; /* day of the month [1, 31] */ + int tm_mon; /* months since January [0, 11] */ + int tm_year; /* years since 1900 */ + int tm_wday; /* days since Sunday [0, 6] */ + int tm_yday; /* days since January 1 [0, 365] */ + int tm_isdst; /* Daylight Saving Time flag */ +}; + +extern char *tzname[]; + +/* Function Prototypes. */ +#ifndef _ANSI_H +#include <ansi.h> +#endif + +_PROTOTYPE( clock_t clock, (void) ); +_PROTOTYPE( double difftime, (time_t _time1, time_t _time0) ); +_PROTOTYPE( time_t mktime, (struct tm *_timeptr) ); +_PROTOTYPE( time_t time, (time_t *_timeptr) ); +_PROTOTYPE( char *asctime, (const struct tm *_timeptr) ); +_PROTOTYPE( char *ctime, (const time_t *_timer) ); +_PROTOTYPE( struct tm *gmtime, (const time_t *_timer) ); +_PROTOTYPE( struct tm *localtime, (const time_t *_timer) ); +_PROTOTYPE( size_t strftime, (char *_s, size_t _max, const char *_fmt, + const struct tm *_timep) ); + +#ifdef _POSIX_SOURCE +_PROTOTYPE( void tzset, (void) ); +#endif + +#ifdef _MINIX +_PROTOTYPE( int stime, (time_t *_top) ); +#endif + +#endif /* _TIME_H */ diff --git a/include/timers.h b/include/timers.h new file mode 100644 index 000000000..62d134ee5 --- /dev/null +++ b/include/timers.h @@ -0,0 +1,62 @@ +/* This library provides generic watchdog timer management functionality. + * The functions operate on a timer queue provided by the caller. Note that + * the timers must use absolute time to allow sorting. The library provides: + * + * tmrs_settimer: (re)set a new watchdog timer in the timers queue + * tmrs_clrtimer: remove a timer from both the timers queue + * tmrs_exptimers: check for expired timers and run watchdog functions + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + * Adapted from tmr_settimer and tmr_clrtimer in src/kernel/clock.c. + * Last modified: September 30, 2004. + */ + +#ifndef _TIMERS_H +#define _TIMERS_H + + +#include <limits.h> +#include <sys/types.h> + +struct timer; +typedef void (*tmr_func_t)(struct timer *tp); +typedef union { int ta_int; long ta_long; void *ta_ptr; } tmr_arg_t; + +/* A timer_t variable must be declare for each distinct timer to be used. + * The timers watchdog function and expiration time are automatically set + * by the library function tmrs_settimer, but its argument is not. + */ +typedef struct timer +{ + struct timer *tmr_next; /* next in a timer chain */ + clock_t tmr_exp_time; /* expiration time */ + tmr_func_t tmr_func; /* function to call when expired */ + tmr_arg_t tmr_arg; /* random argument */ +} timer_t; + +/* Used when the timer is not active. */ +#define TMR_NEVER ((clock_t) -1 < 0) ? ((clock_t) LONG_MAX) : ((clock_t) -1) +#undef TMR_NEVER +#define TMR_NEVER ((clock_t) LONG_MAX) + + +/* These definitions can be used to initialize a timer variable and to set + * the timer's argument before passing it to tmrs_settimer. + */ +#define tmr_inittimer(tp) (void)((tp)->tmr_exp_time = TMR_NEVER) +#define tmr_arg(tp) (&(tp)->tmr_arg) + + +/* The following generic timer management functions are available. They + * can be used to operate on the lists of timers. + */ +_PROTOTYPE( void tmrs_clrtimer, (timer_t **tmrs, timer_t *tp) ); +_PROTOTYPE( void tmrs_exptimers, (timer_t **tmrs, clock_t now) ); +_PROTOTYPE( void tmrs_settimer, (timer_t **tmrs, timer_t *tp, + clock_t exp_time, tmr_func_t watchdog) ); + + +#endif /* _TIMERS_H */ + + diff --git a/include/tools.h b/include/tools.h new file mode 100755 index 000000000..497f5bd40 --- /dev/null +++ b/include/tools.h @@ -0,0 +1,124 @@ +/* Constants describing the disk */ +#define SECTOR_SIZE 512 +#define SECTOR_SHIFT 9 +#define RATIO(b) ((b)/SECTOR_SIZE) +#define ISO_SECTOR_SIZE 2048 +#define ISO_PVD_OFFSET 16 +#define HRATIO (SECTOR_SIZE / HCLICK_SIZE) +#define PARAMSEC 1 /* sector containing boot parameters */ +#define DSKBASE 0x1E /* floppy disk parameter vector */ +#define DSKPARSIZE 11 /* there are this many bytes of parameters */ +#define ESC '\33' /* escape key */ +#define HEADERSEG 0x0060 /* place for an array of struct exec's */ +#define MINIXSEG 0x0080 /* MINIX loaded here (rounded up to a click) */ +#define BOOTSEG 0x07C0 /* bootstraps are loaded here */ +#define SIGNATURE 0xAA55 /* proper bootstraps have this signature */ +#define SIGNATPOS 510 /* offset within bootblock */ +#define FREESEG 0x0800 /* Memory from FREESEG to cseg is free */ +#define MSEC_PER_TICK 55 /* 18.2 ticks per second */ + +/* Scan codes for four different keyboards (from kernel/keyboard.c) */ +#define DUTCH_EXT_SCAN 32 /* 'd' */ +#define OLIVETTI_SCAN 12 /* '=' key on olivetti */ +#define STANDARD_SCAN 13 /* '=' key on IBM */ +#define US_EXT_SCAN 22 /* 'u' */ + +/* Other */ +#define ROOT_INO ((ino_t) 1) /* Inode nr of root dir. */ +#define IM_NAME_MAX 63 + +/* Variables */ +#ifndef EXTERN +#define EXTERN extern +#endif + +typedef struct vector { + u16_t offset; + u16_t segment; +} vector; + +struct image_header { + char name[IM_NAME_MAX + 1]; /* Null terminated. */ + struct exec process; +}; + +EXTERN vector rem_part; /* boot partition table entry */ +EXTERN u16_t cseg, dseg; /* code and data segment of the boot program */ +EXTERN u32_t runsize; /* size of this program */ +EXTERN u16_t device; /* drive being booted from */ +EXTERN u16_t heads, sectors; /* the drive's number of heads and sectors */ +extern u16_t eqscancode; /* Set by peek/getch() if they see a '=' */ + +/* Sticky attributes */ +#define E_SPECIAL 0x01 /* These are known to the program */ +#define E_DEV 0x02 /* The value is a device name */ +#define E_RESERVED 0x04 /* May not be set by user, e.g. scancode */ +#define E_STICKY 0x07 /* Don't go once set */ + +/* Volatile attributes */ +#define E_VAR 0x08 /* Variable */ +#define E_FUNCTION 0x10 /* Function definition */ + +typedef struct environment { + struct environment *next; + char flags; + char *name; /* name = value */ + char *arg; /* name(arg) {value} */ + char *value; + char *defval; /* Safehouse for default values */ +} environment; + +/* External variables */ +EXTERN environment *env; /* Lists the environment */ +EXTERN int fsok; /* True if the boot device contains an FS */ +EXTERN u32_t lowsec; /* Offset to the file system on the boot dev */ + +/* Prototypes */ +_PROTOTYPE( off_t r_super, (void)); +_PROTOTYPE( void r_stat, (Ino_t _inum, struct stat *_stp )); +_PROTOTYPE( ino_t r_readdir, (char *_name )); +_PROTOTYPE( off_t r_vir2abs, (off_t _virblk )); +_PROTOTYPE( ino_t r_lookup, (Ino_t _cwd, char *_path )); + +#ifdef _MONHEAD +_PROTOTYPE( void readerr, (off_t _sec, int _err )); +_PROTOTYPE( int numprefix, (char *_s, char **_ps )); +_PROTOTYPE( int numeric, (char *_s )); +_PROTOTYPE( dev_t name2dev, (char *_name )); +_PROTOTYPE( int delay, (char *_msec )); +_PROTOTYPE( char *unix_err, (int _err )); +_PROTOTYPE( void init_cache, (void)); +_PROTOTYPE( void invalidate_cache, (void)); +_PROTOTYPE( char *b_value, (char *_name )); +_PROTOTYPE( void raw_copy, (int _doff, int _dseg, int _soff, int _sseg, + int _count)); +_PROTOTYPE( void raw_clear, (int _off, int _seg, int _count)); +_PROTOTYPE( void bootstrap, (int _device, int _partoff, int _partseg)); + +_PROTOTYPE( long a2l, (char *_a )); +_PROTOTYPE( char *ul2a, (u32_t _n )); +_PROTOTYPE( char *u2a, (int _n1 )); + +/* Functions defined in monhead.s and usable by other files. */ +_PROTOTYPE( void reset_video, (int color)); +_PROTOTYPE( int dev_geometry, (void)); +_PROTOTYPE( u16_t get_ext_memsize, (void)); +_PROTOTYPE( u16_t get_low_memsize, (void)); +_PROTOTYPE( u16_t get_processor, (void)); +_PROTOTYPE( u32_t get_tick, (void)); +_PROTOTYPE( u16_t get_video, (void)); +_PROTOTYPE( u16_t get_word, (int _off, int _seg)); +_PROTOTYPE( int getchar, (void)); +_PROTOTYPE( void minix, (void)); +_PROTOTYPE( void minix86, (int _kcs, int _kds, char *_bpar, int _psize)); +_PROTOTYPE( void minix386, (int _kcs, int _kds, char *_bpar, int _psize)); +_PROTOTYPE( int peekchar, (void)); +_PROTOTYPE( void put_word, (int _off, int _seg, int _word)); +_PROTOTYPE( int putchar, (char _c)); +_PROTOTYPE( int readsectors, (int _off, int _seg, off_t _adr, int _ct)); +_PROTOTYPE( void reboot, (void)); +_PROTOTYPE( void relocate, (void)); +_PROTOTYPE( int writesectors, (int _off, int _seg, off_t _adr, int _ct)); +#endif + + diff --git a/include/ttyent.h b/include/ttyent.h new file mode 100755 index 000000000..9f739ba5d --- /dev/null +++ b/include/ttyent.h @@ -0,0 +1,23 @@ +/* <ttyent.h> is used by getttyent(3). Author: Kees J. Bot + * 28 Oct 1995 + */ +#ifndef _TTYENT_H +#define _TTYENT_H + +#ifndef _ANSI_H +#include <ansi.h> +#endif + +struct ttyent { + char *ty_name; /* Name of the terminal device. */ + char *ty_type; /* Terminal type name (termcap(3)). */ + char **ty_getty; /* Program to run, normally getty. */ + char **ty_init; /* Initialization command, normally stty. */ +}; + +_PROTOTYPE( struct ttyent *getttyent, (void) ); +_PROTOTYPE( struct ttyent *getttynam, (const char *_name) ); +_PROTOTYPE( int setttyent, (void) ); +_PROTOTYPE( void endttyent, (void) ); + +#endif /* _TTYENT_H */ diff --git a/include/unistd.h b/include/unistd.h new file mode 100755 index 000000000..22c0f9bee --- /dev/null +++ b/include/unistd.h @@ -0,0 +1,153 @@ +/* The <unistd.h> header contains a few miscellaneous manifest constants. */ + +#ifndef _UNISTD_H +#define _UNISTD_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +/* Values used by access(). POSIX Table 2-8. */ +#define F_OK 0 /* test if file exists */ +#define X_OK 1 /* test if file is executable */ +#define W_OK 2 /* test if file is writable */ +#define R_OK 4 /* test if file is readable */ + +/* Values used for whence in lseek(fd, offset, whence). POSIX Table 2-9. */ +#define SEEK_SET 0 /* offset is absolute */ +#define SEEK_CUR 1 /* offset is relative to current position */ +#define SEEK_END 2 /* offset is relative to end of file */ + +/* This value is required by POSIX Table 2-10. */ +#define _POSIX_VERSION 199009L /* which standard is being conformed to */ + +/* These three definitions are required by POSIX Sec. 8.2.1.2. */ +#define STDIN_FILENO 0 /* file descriptor for stdin */ +#define STDOUT_FILENO 1 /* file descriptor for stdout */ +#define STDERR_FILENO 2 /* file descriptor for stderr */ + +#ifdef _MINIX +/* How to exit the system or stop a server process. */ +#define RBT_HALT 0 +#define RBT_REBOOT 1 +#define RBT_PANIC 2 /* a server panics */ +#define RBT_MONITOR 3 /* let the monitor do this */ +#define RBT_RESET 4 /* hard reset the system */ +#endif + +/* NULL must be defined in <unistd.h> according to POSIX Sec. 2.7.1. */ +#define NULL ((void *)0) + +/* The following relate to configurable system variables. POSIX Table 4-2. */ +#define _SC_ARG_MAX 1 +#define _SC_CHILD_MAX 2 +#define _SC_CLOCKS_PER_SEC 3 +#define _SC_CLK_TCK 3 +#define _SC_NGROUPS_MAX 4 +#define _SC_OPEN_MAX 5 +#define _SC_JOB_CONTROL 6 +#define _SC_SAVED_IDS 7 +#define _SC_VERSION 8 +#define _SC_STREAM_MAX 9 +#define _SC_TZNAME_MAX 10 + +/* The following relate to configurable pathname variables. POSIX Table 5-2. */ +#define _PC_LINK_MAX 1 /* link count */ +#define _PC_MAX_CANON 2 /* size of the canonical input queue */ +#define _PC_MAX_INPUT 3 /* type-ahead buffer size */ +#define _PC_NAME_MAX 4 /* file name size */ +#define _PC_PATH_MAX 5 /* pathname size */ +#define _PC_PIPE_BUF 6 /* pipe size */ +#define _PC_NO_TRUNC 7 /* treatment of long name components */ +#define _PC_VDISABLE 8 /* tty disable */ +#define _PC_CHOWN_RESTRICTED 9 /* chown restricted or not */ + +/* POSIX defines several options that may be implemented or not, at the + * implementer's whim. This implementer has made the following choices: + * + * _POSIX_JOB_CONTROL not defined: no job control + * _POSIX_SAVED_IDS not defined: no saved uid/gid + * _POSIX_NO_TRUNC defined as -1: long path names are truncated + * _POSIX_CHOWN_RESTRICTED defined: you can't give away files + * _POSIX_VDISABLE defined: tty functions can be disabled + */ +#define _POSIX_NO_TRUNC (-1) +#define _POSIX_CHOWN_RESTRICTED 1 + +/* Function Prototypes. */ +_PROTOTYPE( void _exit, (int _status) ); +_PROTOTYPE( int access, (const char *_path, int _amode) ); +_PROTOTYPE( unsigned int alarm, (unsigned int _seconds) ); +_PROTOTYPE( int chdir, (const char *_path) ); +_PROTOTYPE( int chown, (const char *_path, Uid_t _owner, Gid_t _group) ); +_PROTOTYPE( int close, (int _fd) ); +_PROTOTYPE( char *ctermid, (char *_s) ); +_PROTOTYPE( char *cuserid, (char *_s) ); +_PROTOTYPE( int dup, (int _fd) ); +_PROTOTYPE( int dup2, (int _fd, int _fd2) ); +_PROTOTYPE( int execl, (const char *_path, const char *_arg, ...) ); +_PROTOTYPE( int execle, (const char *_path, const char *_arg, ...) ); +_PROTOTYPE( int execlp, (const char *_file, const char *arg, ...) ); +_PROTOTYPE( int execv, (const char *_path, char *const _argv[]) ); +_PROTOTYPE( int execve, (const char *_path, char *const _argv[], + char *const _envp[]) ); +_PROTOTYPE( int execvp, (const char *_file, char *const _argv[]) ); +_PROTOTYPE( pid_t fork, (void) ); +_PROTOTYPE( long fpathconf, (int _fd, int _name) ); +_PROTOTYPE( char *getcwd, (char *_buf, size_t _size) ); +_PROTOTYPE( gid_t getegid, (void) ); +_PROTOTYPE( uid_t geteuid, (void) ); +_PROTOTYPE( gid_t getgid, (void) ); +_PROTOTYPE( int getgroups, (int _gidsetsize, gid_t _grouplist[]) ); +_PROTOTYPE( char *getlogin, (void) ); +_PROTOTYPE( pid_t getpgrp, (void) ); +_PROTOTYPE( pid_t getpid, (void) ); +_PROTOTYPE( pid_t getppid, (void) ); +_PROTOTYPE( uid_t getuid, (void) ); +_PROTOTYPE( int isatty, (int _fd) ); +_PROTOTYPE( int link, (const char *_existing, const char *_new) ); +_PROTOTYPE( off_t lseek, (int _fd, off_t _offset, int _whence) ); +_PROTOTYPE( long pathconf, (const char *_path, int _name) ); +_PROTOTYPE( int pause, (void) ); +_PROTOTYPE( int pipe, (int _fildes[2]) ); +_PROTOTYPE( ssize_t read, (int _fd, void *_buf, size_t _n) ); +_PROTOTYPE( int rmdir, (const char *_path) ); +_PROTOTYPE( int setgid, (Gid_t _gid) ); +_PROTOTYPE( int setpgid, (pid_t _pid, pid_t _pgid) ); +_PROTOTYPE( pid_t setsid, (void) ); +_PROTOTYPE( int setuid, (Uid_t _uid) ); +_PROTOTYPE( unsigned int sleep, (unsigned int _seconds) ); +_PROTOTYPE( long sysconf, (int _name) ); +_PROTOTYPE( pid_t tcgetpgrp, (int _fd) ); +_PROTOTYPE( int tcsetpgrp, (int _fd, pid_t _pgrp_id) ); +_PROTOTYPE( char *ttyname, (int _fd) ); +_PROTOTYPE( int unlink, (const char *_path) ); +_PROTOTYPE( ssize_t write, (int _fd, const void *_buf, size_t _n) ); + +#ifdef _MINIX +_PROTOTYPE( int brk, (char *_addr) ); +_PROTOTYPE( int chroot, (const char *_name) ); +_PROTOTYPE( int mknod, (const char *_name, Mode_t _mode, Dev_t _addr) ); +_PROTOTYPE( int mknod4, (const char *_name, Mode_t _mode, Dev_t _addr, + long _size) ); +_PROTOTYPE( char *mktemp, (char *_template) ); +_PROTOTYPE( int mount, (char *_spec, char *_name, int _flag) ); +_PROTOTYPE( long ptrace, (int _req, pid_t _pid, long _addr, long _data) ); +_PROTOTYPE( char *sbrk, (int _incr) ); +_PROTOTYPE( int sync, (void) ); +_PROTOTYPE( int umount, (const char *_name) ); +_PROTOTYPE( int reboot, (int _how, ...) ); +_PROTOTYPE( int gethostname, (char *_hostname, size_t _len) ); +_PROTOTYPE( int getdomainname, (char *_domain, size_t _len) ); +_PROTOTYPE( int ttyslot, (void) ); +_PROTOTYPE( int fttyslot, (int _fd) ); +_PROTOTYPE( char *crypt, (const char *_key, const char *_salt) ); +_PROTOTYPE( int getsysinfo, (int who, int what, void *where) ); +#if ENABLE_MESSAGE_STATS +_PROTOTYPE( int mstats, (struct message_statentry *ms, int entries, int reset) ); +#endif +#endif + +_PROTOTYPE( int setcache, (int kb)); + +#endif /* _UNISTD_H */ diff --git a/include/utime.h b/include/utime.h new file mode 100755 index 000000000..b512aa482 --- /dev/null +++ b/include/utime.h @@ -0,0 +1,18 @@ +/* The <utime.h> header is used for the utime() system call. */ + +#ifndef _UTIME_H +#define _UTIME_H + +#ifndef _TYPES_H +#include <sys/types.h> +#endif + +struct utimbuf { + time_t actime; /* access time */ + time_t modtime; /* modification time */ +}; + +/* Function Prototypes. */ +_PROTOTYPE( int utime, (const char *_path, const struct utimbuf *_times) ); + +#endif /* _UTIME_H */ diff --git a/include/utmp.h b/include/utmp.h new file mode 100755 index 000000000..d0f1446a7 --- /dev/null +++ b/include/utmp.h @@ -0,0 +1,32 @@ +/* The <utmp.h> header is used by init, login, who, etc. */ + +#ifndef _UTMP_H +#define _UTMP_H + +#define WTMP "/usr/adm/wtmp" /* the login history file */ +#define BTMP "/usr/adm/btmp" /* the bad-login history file */ +#define UTMP "/etc/utmp" /* the user accouting file */ + + +struct utmp { + char ut_user[8]; /* user name */ + char ut_id[4]; /* /etc/inittab ID */ + char ut_line[12]; /* terminal name */ + char ut_host[16]; /* host name, when remote */ + short ut_pid; /* process id */ + short int ut_type; /* type of entry */ + long ut_time; /* login/logout time */ +}; + +#define ut_name ut_user /* for compatibility with other systems */ + + +/* Definitions for ut_type. */ +#define RUN_LVL 1 /* this is a RUN_LEVEL record */ +#define BOOT_TIME 2 /* this is a REBOOT record */ +#define INIT_PROCESS 5 /* this process was spawned by INIT */ +#define LOGIN_PROCESS 6 /* this is a 'getty' process waiting */ +#define USER_PROCESS 7 /* any other user process */ +#define DEAD_PROCESS 8 /* this process has died (wtmp only) */ + +#endif /* _UTMP_H */ diff --git a/kernel/Makefile b/kernel/Makefile new file mode 100755 index 000000000..28323a77e --- /dev/null +++ b/kernel/Makefile @@ -0,0 +1,183 @@ +# Makefile for kernel + +# Directories +u = /usr +i = $u/include +s = $i/sys +h = $i/minix +b = $i/ibm +l = $u/lib +n = $i/net +g = $n/gen + +# Programs, flags, etc. +CC = exec cc +CPP = $l/cpp +LD = $(CC) -.o +CFLAGS = -I$i +LDFLAGS = -i +LIBS = -lsys -lutils -ltimers + +HEAD = mpx.o + +OBJS = start.o protect.o klibc.o klib.o table.o main.o proc.o \ + i8259.o exception.o system.o clock.o memory.o misc.o \ + dummy.o \ + rtl8139.o pci.o pci_table.o + +SYS = system/system.a + + +# What to make. +kernel build: $(HEAD) $(OBJS) $(SYS) + $(LD) $(LDFLAGS) -o $@ $(HEAD) $(OBJS) $(SYS) $(CLOCK) $(LIBS) + install -S 0 $@ + +$(SYS): + cd system && $(MAKE) + +all install: + cd system && $(MAKE) -$(MAKEFLAGS) $@ + +clean: + cd system && $(MAKE) -$(MAKEFLAGS) $@ + rm -f *.o *.bak kernel + +# Dependencies +a = kernel.h const.h type.h proto.h glo.h \ + $h/config.h $h/const.h $h/type.h $h/ipc.h \ + $s/types.h \ + $i/string.h $i/limits.h $i/errno.h $i/timers.h \ + $b/portio.h $b/interrupt.h $b/bios.h $b/ports.h + +klibc.o: $a + +klib.o: $h/config.h $h/const.h const.h sconst.h protect.h +klib.o: klib88.s klib386.s + +mpx.o: $h/config.h $h/const.h $h/com.h const.h protect.h sconst.h +mpx.o: mpx88.s mpx386.s +mpx.o: mpx88.s mpx386.s + +clock.o: $a +clock.o: $i/signal.h +clock.o: $h/callnr.h +clock.o: $h/com.h +clock.o: proc.h + +start.o: $a +start.o: $i/stdlib.h +start.o: protect.h + +exception.o: $a +exception.o: $i/signal.h +exception.o: $h/com.h +exception.o: proc.h + +driver.o: $a $d +driver.o: $h/ioctl.h +driver.o: $s/ioc_disk.h + +drvlib.o: $a $d $(dl) + +i8259.o: $a + +main.o: $a +main.o: $i/unistd.h +main.o: $i/signal.h +main.o: $i/a.out.h +main.o: $h/callnr.h +main.o: $h/com.h +main.o: proc.h +main.o: sendmask.h + +memory.o: $a +memory.o: proc.h +memory.o: protect.h + +misc.o: $a +misc.o: $i/stdlib.h +misc.o: $h/com.h +misc.o: assert.h + +printer.o: $a +printer.o: $h/callnr.h +printer.o: $h/com.h +printer.o: proc.h + +proc.o: $a +proc.o: $h/callnr.h +proc.o: $h/com.h +proc.o: proc.h +proc.o: sendmask.h + +protect.o: $a +protect.o: $h/com.h +protect.o: proc.h +protect.o: protect.h + +system.o: $a +system.o: $i/stdlib.h +system.o: $i/signal.h +system.o: $i/unistd.h +system.o: $s/sigcontext.h +system.o: $s/ptrace.h +system.o: $h/ioctl.h +system.o: $s/svrctl.h +system.o: $h/callnr.h +system.o: $h/com.h +system.o: system.h +system.o: proc.h +system.o: protect.h +system.o: assert.h +system.o: sendmask.h + +table.o: $a +table.o: $i/stdlib.h +table.o: $i/termios.h +table.o: $h/com.h +table.o: proc.h +table.o: sendmask.h +table.o: $b/int86.h + +rtl8139.o: $a +rtl8139.o: $h/com.h +rtl8139.o: $n/hton.h +rtl8139.o: $g/ether.h +rtl8139.o: $g/eth_io.h +rtl8139.o: $i/stddef.h +rtl8139.o: assert.h +rtl8139.o: pci.h +rtl8139.o: proc.h +rtl8139.o: rtl8139.h + +pci.o: $a +pci.o: assert.h +pci.o: pci.h +pci.o: pci_amd.h +pci.o: pci_intel.h +pci.o: pci_via.h +pci.o: pci_sis.h + +pci_table.o: $a +pci_table.o: pci.h + +dummy.o: $a + +system/system.a: $a $h/devio.h $h/com.h +system/system.a: proc.h protect.h system.h sendmask.h +system/system.a: $s/ptrace.h $s/sigcontext.h +system/system.a: $i/signal.h $i/unistd.h +system/system.a: system/alarms.c +system/system.a: system/copying.c +system/system.a: system/devio.c +system/system.a: system/irqctl.c +system/system.a: system/misc.c +system/system.a: system/proctl.c +system/system.a: system/sigctl.c +system/system.a: system/srvrctl.c +system/system.a: system/tracing.c +system/system.a: system/debugging.c +system/system.a: system/do_copy.c +system/system.a: system/do_vcopy.c + diff --git a/kernel/assert.h b/kernel/assert.h new file mode 100755 index 000000000..87cf7205a --- /dev/null +++ b/kernel/assert.h @@ -0,0 +1,25 @@ +/* assert.h */ + +#ifndef NDEBUG /* 8086 must do without training wheels. */ +#define NDEBUG (_WORD_SIZE == 2) +#endif + +#if !NDEBUG + +#define INIT_ASSERT static char *assert_file= __FILE__; + +void bad_assertion(char *file, int line, char *what); +void bad_compare(char *file, int line, int lhs, char *what, int rhs); + +#define assert(x) (!(x) ? bad_assertion(assert_file, __LINE__, #x) \ + : (void) 0) +#define compare(a,t,b) (!((a) t (b)) ? bad_compare(assert_file, __LINE__, \ + (a), #a " " #t " " #b, (b)) : (void) 0) +#else /* NDEBUG */ + +#define INIT_ASSERT /* nothing */ + +#define assert(x) (void)0 +#define compare(a,t,b) (void)0 + +#endif /* NDEBUG */ diff --git a/kernel/build b/kernel/build new file mode 100755 index 000000000..8573f6fba Binary files /dev/null and b/kernel/build differ diff --git a/kernel/clock.c b/kernel/clock.c new file mode 100755 index 000000000..fce234433 --- /dev/null +++ b/kernel/clock.c @@ -0,0 +1,362 @@ +/* The file contais the clock task, which handles all time related functions. + * Important events that are handled by the CLOCK include alarm timers and + * (re)scheduling user processes. + * The CLOCK offers a direct interface to kernel processes. System services + * can access its services through system calls, such as sys_syncalrm(). The + * CLOCK task thus is hidden for the outside. + * + * Changes: + * Mar 18, 2004 clock interface moved to SYSTEM task (Jorrit N. Herder) + * Oct 10, 2004 call vector + return values allowed (Jorrit N. Herder) + * Sep 30, 2004 source code documentation updated (Jorrit N. Herder) + * Sep 24, 2004 redesigned timers and alarms (Jorrit N. Herder) + * Jun 04, 2004 new timeout flag alarm functionality (Jorrit N. Herder) + * + * The function do_clocktick() is not triggered from the clock library, but + * by the clock's interrupt handler when a watchdog timer has expired or + * another user process must be scheduled. + * + * In addition to the main clock_task() entry point, which starts the main + * loop, there are several other minor entry points: + * clock_stop: called just before MINIX shutdown + * get_uptime: get realtime since boot in clock ticks + * set_timer: set a watchdog timer (*, see note below!) + * reset_timer: reset a watchdog timer (*) + * calc_elapsed: do timing measurements: get delta ticks and pulses + * read_clock: read the counter of channel 0 of the 8253A timer + * + * (*) The CLOCK task keeps tracks of watchdog timers for the entire kernel. + * The watchdog functions of expired timers are executed in do_clocktick(). + * It is crucial that watchdog functions cannot block, or the CLOCK task may + * be blocked. Do not send() a message when the receiver is not expecting it. + * The use of notify(), which always returns, is strictly preferred! + */ + +#include "kernel.h" +#include "proc.h" +#include <signal.h> +#include <minix/com.h> + +/* Function prototype for PRIVATE functions. */ +FORWARD _PROTOTYPE( void init_clock, (void) ); +FORWARD _PROTOTYPE( int clock_handler, (irq_hook_t *hook) ); +FORWARD _PROTOTYPE( int do_clocktick, (message *m_ptr) ); + +/* Constant definitions. */ +#define SCHED_RATE (MILLISEC*HZ/1000) /* number of ticks per schedule */ +#define MILLISEC 100 /* how often to call the scheduler */ + +/* Clock parameters. */ +#if (CHIP == INTEL) +#define COUNTER_FREQ (2*TIMER_FREQ) /* counter frequency using square wave */ +#define LATCH_COUNT 0x00 /* cc00xxxx, c = channel, x = any */ +#define SQUARE_WAVE 0x36 /* ccaammmb, a = access, m = mode, b = BCD */ + /* 11x11, 11 = LSB then MSB, x11 = sq wave */ +#define TIMER_COUNT ((unsigned) (TIMER_FREQ/HZ)) /* initial value for counter*/ +#define TIMER_FREQ 1193182L /* clock frequency for timer in PC and AT */ + +#define CLOCK_ACK_BIT 0x80 /* PS/2 clock interrupt acknowledge bit */ +#endif + +#if (CHIP == M68000) +#define TIMER_FREQ 2457600L /* timer 3 input clock frequency */ +#endif + +/* The CLOCK's timers queue. The functions in <timers.h> operate on this. + * The process structure contains one timer per type of alarm (SIGNALRM, + * SYNCALRM, and FLAGALRM), which means that a process can have a single + * outstanding timer for each alarm type. + * If other kernel parts want to use additional timers, they must declare + * their own persistent timer structure, which can be passed to the clock + * via (re)set_timer(). + * When a timer expires its watchdog function is run by the CLOCK task. + */ +PRIVATE timer_t *clock_timers; /* queue of CLOCK timers */ +PRIVATE clock_t next_timeout; /* realtime that next timer expires */ + +/* The boot time and the current real time. The real time is incremented by + * the clock on each clock tick. The boot time is set by a utility program + * after system startup to prevent troubles reading the CMOS. + */ +PRIVATE clock_t realtime; /* real time clock */ + +/* Variables changed by interrupt handler. */ +PRIVATE clock_t pending_ticks; /* ticks seen by low level only */ +PRIVATE int sched_ticks = SCHED_RATE; /* counter: when 0, call scheduler */ +PRIVATE struct proc *prev_ptr; /* last user process run by clock */ + + +/*===========================================================================* + * clock_task * + *===========================================================================*/ +PUBLIC void clock_task() +{ +/* Main program of clock task. It corrects realtime by adding pending ticks + * seen only by the interrupt service, then it determines which call this is + * by looking at the message type and dispatches. + */ + message m; /* message buffer for both input and output */ + int result; + init_clock(); /* initialize clock task */ + + /* Main loop of the clock task. Get work, process it, sometimes reply. */ + while (TRUE) { + /* Go get a message. */ + receive(ANY, &m); + + /* Transfer ticks seen by the low level handler. */ + lock(); + realtime += pending_ticks; + pending_ticks = 0; + unlock(); + + /* Handle the request. */ + switch (m.m_type) { + case HARD_INT: + result = do_clocktick(&m); /* handle clock tick */ + break; + default: /* illegal message type */ + kprintf("CLOCK got illegal request from %d.\n", m.m_source); + result = EBADREQUEST; + } + + /* Send reply, unless inhibited, e.g. by do_clocktick(). */ + if (result != EDONTREPLY) { + m.m_type = result; + send(m.m_source, &m); + } + } +} + + +/*===========================================================================* + * do_clocktick * + *===========================================================================*/ +PRIVATE int do_clocktick(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* Despite its name, this routine is not called on every clock tick. It + * is called on those clock ticks when a lot of work needs to be done. + */ + + register struct proc *rp; + register int proc_nr; + timer_t *tp; + struct proc *p; + + /* Check if a clock timer expired and run its watchdog function. */ + if (next_timeout <= realtime) { + tmrs_exptimers(&clock_timers, realtime); + next_timeout = clock_timers == NULL ? + TMR_NEVER : clock_timers->tmr_exp_time; + } + + /* If a user process has been running too long, pick another one. */ + if (--sched_ticks == 0) { + if (bill_ptr == prev_ptr) lock_sched(); /* process has run too long */ + sched_ticks = SCHED_RATE; /* reset quantum */ + prev_ptr = bill_ptr; /* new previous process */ + } + + /* Inhibit sending a reply. */ + return(EDONTREPLY); +} + + +/*===========================================================================* + * clock_handler * + *===========================================================================*/ +PRIVATE int clock_handler(hook) +irq_hook_t *hook; +{ +/* This executes on every clock tick (i.e., every time the timer chip + * generates an interrupt). It does a little bit of work so the clock + * task does not have to be called on every tick. + * + * Switch context to do_clocktick() if an alarm has gone off. + * Also switch there to reschedule if the reschedule will do something. + * This happens when + * (1) quantum has expired + * (2) current process received full quantum (as clock sampled it!) + * (3) something else is ready to run. + * + * Many global global and static variables are accessed here. The safety + * of this must be justified. Most of them are not changed here: + * k_reenter: + * This safely tells if the clock interrupt is nested. + * proc_ptr, bill_ptr: + * These are used for accounting. It does not matter if proc.c + * is changing them, provided they are always valid pointers, + * since at worst the previous process would be billed. + * next_timeout, realtime, sched_ticks, bill_ptr, prev_ptr + * rdy_head[PPRI_USER] + * These are tested to decide whether to call notify(). It + * does not matter if the test is sometimes (rarely) backwards + * due to a race, since this will only delay the high-level + * processing by one tick, or call the high level unnecessarily. + * The variables which are changed require more care: + * rp->user_time, rp->sys_time: + * These are protected by explicit locks in system.c. They are + * not properly protected in dmp.c (the increment here is not + * atomic) but that hardly matters. + * pending_ticks: + * This is protected by explicit locks in clock.c. Don't + * update realtime directly, since there are too many + * references to it to guard conveniently. + * lost_ticks: + * Clock ticks counted outside the clock task. + * sched_ticks, prev_ptr: + * Updating these competes with similar code in do_clocktick(). + * No lock is necessary, because if bad things happen here + * (like sched_ticks going negative), the code in do_clocktick() + * will restore the variables to reasonable values, and an + * occasional missed or extra sched() is harmless. + * + * Are these complications worth the trouble? Well, they make the system 15% + * faster on a 5MHz 8088, and make task debugging much easier since there are + * no task switches on an inactive system. + */ + + register struct proc *rp; + register unsigned ticks; + clock_t now; + + /* Acknowledge the PS/2 clock interrupt. */ + if (ps_mca) outb(PORT_B, inb(PORT_B) | CLOCK_ACK_BIT); + + /* Update user and system accounting times. Charge the current process for + * user time. If the current process is not billable, that is, if a non-user + * process is running, charge the billable process for system time as well. + * Thus the unbillable process' user time is the billable user's system time. + */ + ticks = lost_ticks + 1; + lost_ticks = 0; + pending_ticks += ticks; + now = realtime + pending_ticks; + + rp = (k_reenter == 0) ? proc_ptr : proc_addr(HARDWARE); + rp->user_time += ticks; + if (rp != bill_ptr && rp != proc_addr(IDLE)) bill_ptr->sys_time += ticks; + + /* Check if do_clocktick() must be called. Done for alarms and scheduling. + * If bill_ptr == prev_ptr, there are no ready users so don't need sched(). + */ + if (next_timeout <= now || (sched_ticks == 1 && bill_ptr == prev_ptr + && rdy_head[PPRI_USER] != NIL_PROC)) + { + notify(CLOCK, HARD_INT); + } + else if (--sched_ticks == 0) { + sched_ticks = SCHED_RATE; /* reset quantum */ + prev_ptr = bill_ptr; /* new previous process */ + } + return 1; /* reenable clock interrupts */ +} + + +/*===========================================================================* + * get_uptime * + *===========================================================================*/ +PUBLIC clock_t get_uptime() +{ +/* Get and return the current clock uptime in ticks. + * Be careful about pending_ticks. + */ + clock_t uptime; + + lock(); + uptime = realtime + pending_ticks; + unlock(); + return(uptime); +} + + +/*===========================================================================* + * set_timer * + *===========================================================================*/ +PUBLIC void set_timer(tp, exp_time, watchdog) +struct timer *tp; /* pointer to timer structure */ +clock_t exp_time; /* expiration realtime */ +tmr_func_t watchdog; /* watchdog to be called */ +{ +/* Insert the new timer in the active timers list. Always update the + * next timeout time by setting it to the front of the active list. + */ + tmrs_settimer(&clock_timers, tp, exp_time, watchdog); + next_timeout = clock_timers->tmr_exp_time; +} + + +/*===========================================================================* + * reset_timer * + *===========================================================================*/ +PUBLIC void reset_timer(tp) +struct timer *tp; /* pointer to timer structure */ +{ +/* The timer pointed to by 'tp' is no longer needed. Remove it from both the + * active and expired lists. Always update the next timeout time by setting + * it to the front of the active list. + */ + tmrs_clrtimer(&clock_timers, tp); + next_timeout = (clock_timers == NULL) ? + TMR_NEVER : clock_timers->tmr_exp_time; +} + + +#if (CHIP == INTEL) + +/*===========================================================================* + * init_clock * + *===========================================================================*/ +PRIVATE void init_clock() +{ +/* Initialize channel 0 of the 8253A timer to, e.g., 60 Hz. */ + static irq_hook_t clock_hook; + + outb(TIMER_MODE, SQUARE_WAVE); /* set timer to run continuously */ + outb(TIMER0, TIMER_COUNT); /* load timer low byte */ + outb(TIMER0, TIMER_COUNT >> 8); /* load timer high byte */ + put_irq_handler(&clock_hook, CLOCK_IRQ, clock_handler);/* register handler */ + enable_irq(&clock_hook); /* ready for clock interrupts */ +} + +/*===========================================================================* + * clock_stop * + *===========================================================================*/ +PUBLIC void clock_stop() +{ +/* Reset the clock to the BIOS rate. (For rebooting) */ + outb(TIMER_MODE, 0x36); + outb(TIMER0, 0); + outb(TIMER0, 0); +} + + +/*===========================================================================* + * read_clock * + *===========================================================================*/ +PUBLIC unsigned long read_clock() +{ +/* Read the counter of channel 0 of the 8253A timer. This counter counts + * down at a rate of TIMER_FREQ and restarts at TIMER_COUNT-1 when it + * reaches zero. A hardware interrupt (clock tick) occurs when the counter + * gets to zero and restarts its cycle. + */ + unsigned count; + + lock(); + outb(TIMER_MODE, LATCH_COUNT); + count = inb(TIMER0); + count |= (inb(TIMER0) << 8); + unlock(); + + return count; +} + +#endif /* (CHIP == INTEL) */ + +#if (CHIP == M68000) +/* Initialize the timer C in the MFP 68901: implement init_clock() here. */ +#endif /* (CHIP == M68000) */ + + diff --git a/kernel/const.h b/kernel/const.h new file mode 100755 index 000000000..f7921363a --- /dev/null +++ b/kernel/const.h @@ -0,0 +1,55 @@ +/* General constants used by the kernel. */ + +#include <ibm/interrupt.h> /* interrupt numbers and hardware vectors */ +#include <ibm/ports.h> /* port addresses and magic numbers */ +#include <ibm/bios.h> /* BIOS addresses, sizes and magic numbers */ + +#if (CHIP == INTEL) + +/* To translate an address in kernel space to a physical address. This is + * the same as umap_local(proc_ptr, D, vir, sizeof(*vir)), but less costly. + */ +#define vir2phys(vir) (data_base + (vir_bytes) (vir)) + +/* Constants used in virtual_copy(). Values must be 0 and 1, respectively! */ +#define _SRC_ 0 +#define _DST_ 1 + +/* Translate a pointer to a field in a structure to a pointer to the structure + * itself. So it translates '&struct_ptr->field' back to 'struct_ptr'. + */ +#define structof(type, field, ptr) \ + ((type *) (((char *) (ptr)) - offsetof(type, field))) + +/* How many bytes for the kernel stack. Space allocated in mpx.s. */ +#define K_STACK_BYTES 1024 + +/* How many bytes for (port,value)-pairs vector to copy in. */ +#define VDEVIO_BUF_SIZE 128 + +/* Program stack words and masks. */ +#define INIT_PSW 0x0200 /* initial psw */ +#define INIT_TASK_PSW 0x1200 /* initial psw for tasks (with IOPL 1) */ +#define TRACEBIT 0x100 /* OR this with psw in proc[] for tracing */ +#define SETPSW(rp, new) /* permits only certain bits to be set */ \ + ((rp)->p_reg.psw = (rp)->p_reg.psw & ~0xCD5 | (new) & 0xCD5) +#define IF_MASK 0x00000200 +#define IOPL_MASK 0x003000 + +/* Disable/Enable hardware interrupts. */ +#define lock() intr_disable() +#define unlock() intr_enable() + +/* Sizes of memory tables. The boot monitor distinguishes three memory areas, + * namely low mem below 1M, 1M-16M, and mem after 16M. More chunks are needed + * for DOS MINIX. + */ +#define NR_MEMS 8 /* number of chunks of memory */ + + +#endif /* (CHIP == INTEL) */ + +#if (CHIP == M68000) +/* M68000 specific constants go here. */ +#endif /* (CHIP == M68000) */ + diff --git a/kernel/dummy.c b/kernel/dummy.c new file mode 100644 index 000000000..ffb9f8e36 --- /dev/null +++ b/kernel/dummy.c @@ -0,0 +1,86 @@ +/* A dummy task. Used to safely remove old tasks, and get notices if they get + * called by accident (i.e., because of some bug...). + * + * Created: + * Jul 27, 2004 by Jorrit N. Herder + */ + +#include "kernel.h" +#include "proc.h" +#include <minix/com.h> + +/* Allocated space for the global variables. */ +message m_in; /* the input message itself */ +message m_out; /* the output message used for reply */ +int who; /* caller's proc number */ +int callnr; /* system call number */ + +/* Declare some local functions. */ +FORWARD _PROTOTYPE(void get_work, (void) ); +FORWARD _PROTOTYPE(void reply, (int whom, int result) ); + + +/*===========================================================================* + * dummy_task * + *===========================================================================*/ +PUBLIC void dummy_task() +{ +/* This is the main routine of this service. In principle this should block + * forever on getting new work - the dummy task is not supposed to be called. + * If new work is received, somehow, diagnostics are printed. + */ + int result; + + /* kprintf("DUMMY: do nothing kernel task started (proc. nr %d).\n", + proc_number(proc_ptr)); */ + + /* Main loop - get work and do it, forever. */ + while (TRUE) { + + /* Wait for incoming message, sets 'callnr' and 'who'. */ + get_work(); + + /* There is work to do! (re)set some variables first. */ + result = EINVAL; /* illegal send to dummy task */ + + /* Print diagnostics: this was not supposed to happen. */ + kprintf("Dummy task: request received from %d.\n", who); + + /* Finally send reply message, unless disabled. */ + if (result != EDONTREPLY) { + reply(who, result); + } + } +} + + +/*===========================================================================* + * get_work * + *===========================================================================*/ +PRIVATE void get_work() +{ + int status = 0; + status = receive(ANY, &m_in); /* this blocks until message arrives */ + if (OK != status) + kprintf("Dummy task failed to receive message: %d", status); + who = m_in.m_source; /* message arrived! set sender */ + callnr = m_in.m_type; /* set function call number */ +} + + +/*===========================================================================* + * reply * + *===========================================================================*/ +PRIVATE void reply(who, result) +int who; /* destination */ +int result; /* report result to replyee */ +{ + int send_status; + m_out.m_type = result; /* build reply message */ + send_status = send(who, &m_out); /* send the message */ + if (OK != send_status) + kprintf("Dummy task unable to send reply: %d", send_status); +} + + + diff --git a/kernel/exception.c b/kernel/exception.c new file mode 100755 index 000000000..093360848 --- /dev/null +++ b/kernel/exception.c @@ -0,0 +1,88 @@ +/* This file contains a simple exception handler. Exceptions in user + * processes are converted to signals. Exceptions in the kernel, MM and + * FS cause a panic. + * + * Changes: + * Sep 28, 2004: skip_stop_sequence on exceptions in system processes + */ + +#include "kernel.h" +#include <signal.h> +#include "proc.h" + +/*==========================================================================* + * exception * + *==========================================================================*/ +PUBLIC void exception(vec_nr) +unsigned vec_nr; +{ +/* An exception or unexpected interrupt has occurred. */ + + struct ex_s { + char *msg; + int signum; + int minprocessor; + }; + static struct ex_s ex_data[] = { + "Divide error", SIGFPE, 86, + "Debug exception", SIGTRAP, 86, + "Nonmaskable interrupt", SIGBUS, 86, + "Breakpoint", SIGEMT, 86, + "Overflow", SIGFPE, 86, + "Bounds check", SIGFPE, 186, + "Invalid opcode", SIGILL, 186, + "Coprocessor not available", SIGFPE, 186, + "Double fault", SIGBUS, 286, + "Copressor segment overrun", SIGSEGV, 286, + "Invalid TSS", SIGSEGV, 286, + "Segment not present", SIGSEGV, 286, + "Stack exception", SIGSEGV, 286, /* STACK_FAULT already used */ + "General protection", SIGSEGV, 286, + "Page fault", SIGSEGV, 386, /* not close */ + NIL_PTR, SIGILL, 0, /* probably software trap */ + "Coprocessor error", SIGFPE, 386, + }; + register struct ex_s *ep; + struct proc *saved_proc; + + /* Save proc_ptr, because it may be changed by debug statements. */ + saved_proc = proc_ptr; + + ep = &ex_data[vec_nr]; + + if (vec_nr == 2) { /* spurious NMI on some machines */ + kprintf("got spurious NMI\n",NO_ARG); + return; + } + + if (k_reenter == 0 && isuserp(saved_proc)) { + unlock(); /* this is protected like sys_call() */ + cause_sig(proc_number(saved_proc), ep->signum); + return; + } + + /* Exception in system code. This is not supposed to happen. */ + if (ep->msg == NIL_PTR || processor < ep->minprocessor) + kprintf("\nIntel-reserved exception %d\n", vec_nr); + else + kprintf("\n%s\n", karg(ep->msg)); + kprintf("process number %d", proc_number(saved_proc)); + kprintf("pc = %d:", (unsigned) saved_proc->p_reg.cs); + kprintf("%d\n", (unsigned) saved_proc->p_reg.pc); + + /* If the exception originates in the kernel, shut down MINIX. Otherwise, + * kill the process that caused it. If MINIX is shut down and the stop + * sequence is skipped, the kprintf() output cannot be flushed by the TTY + * driver. This leaves the user with a hanging system without proper + * notification ... + */ + if (istaskp(saved_proc)) { /* serious problem */ + skip_stop_sequence = TRUE; /* directly shutdown */ + panic("exception in a kernel task", NO_NUM); + } else { + clear_proc(saved_proc->p_nr); + kprintf("%s was killed by MINIX due to an exception", + karg(saved_proc->p_name)); + } +} + diff --git a/kernel/glo.h b/kernel/glo.h new file mode 100755 index 000000000..36c118f6f --- /dev/null +++ b/kernel/glo.h @@ -0,0 +1,80 @@ +/* Global variables used in the kernel. This file contains the declarations; + * storage space for the variables is allocated in table.c, because EXTERN is + * defined as extern unless the _TABLE definition is seen. + */ +#ifdef _TABLE +#undef EXTERN +#define EXTERN +#endif + +/* MINIX' shutdown sequence uses watchdog timers to stop system services. The + * flag shutting_down must be initialized to FALSE. We rely on the compiler's + * default initialization (0) of global variables here. + */ +EXTERN int skip_stop_sequence; /* set to TRUE in case of an exception() */ +EXTERN int shutting_down; /* TRUE if the system is shutting down */ +EXTERN struct proc *shutdown_process; /* process awaiting shutdown of */ +EXTERN timer_t shutdown_timer; /* watchdog function called after timeout */ + +/* Kernel memory. */ +EXTERN phys_bytes code_base; /* base of kernel code */ +EXTERN phys_bytes data_base; /* base of kernel data */ +EXTERN phys_bytes aout; /* address of a.out headers */ + +/* Low level notifications may be put on the 'held' queue to prevent races. */ +EXTERN struct proc *held_head; /* head of queue of held-up interrupts */ +EXTERN struct proc *held_tail; /* tail of queue of held-up interrupts */ +EXTERN unsigned char k_reenter; /* kernel reentry count (entry count less 1)*/ + +/* Process table. Here to stop too many things having to include proc.h. */ +EXTERN struct proc *proc_ptr; /* pointer to currently running process */ + +/* Memory sizes. */ +EXTERN struct memory mem[NR_MEMS]; /* base and size of chunks of memory */ +EXTERN phys_clicks tot_mem_size; /* total system memory size */ + +/* Miscellaneous. */ +EXTERN unsigned lost_ticks; /* clock ticks counted outside the clock task */ + +#if (CHIP == INTEL) + +/* Machine type. */ +EXTERN int pc_at; /* PC-AT compatible hardware interface */ +EXTERN int ps_mca; /* PS/2 with Micro Channel */ +EXTERN unsigned int processor; /* 86, 186, 286, 386, ... */ +#if _WORD_SIZE == 2 +EXTERN int protected_mode; /* nonzero if running in Intel protected mode*/ +#else +#define protected_mode 1 /* 386 mode implies protected mode */ +#endif + +/* Video card types. */ +EXTERN int ega; /* nonzero if console is EGA */ +EXTERN int vga; /* nonzero if console is VGA */ + +/* Interrupt related variables. */ +EXTERN struct irqtab irqtab[NR_IRQ_VECTORS]; /* table with IRQ policies */ +EXTERN irq_hook_t *irq_hooks[NR_IRQ_VECTORS]; /* list of IRQ handlers */ +EXTERN int irq_actids[NR_IRQ_VECTORS]; /* IRQ ID bits active */ +EXTERN int irq_use; /* bit map of all in-use irq's */ + +/* Miscellaneous. */ +EXTERN struct kmessages kmess; /* diagnostic messages in kernel */ +EXTERN reg_t mon_ss, mon_sp; /* monitor stack */ +EXTERN int mon_return; /* true if return to the monitor possible */ +EXTERN phys_bytes mon_params; /* boot parameter block passed in/out */ +EXTERN size_t mon_parmsize; /* boot parameter block size */ + +/* Variables that are initialized elsewhere are just extern here. */ +extern struct system_image image[]; /* system image processes (table.c) */ +extern char *t_stack[]; /* stack space for kernel tasks (table.c) */ +extern struct segdesc_s gdt[]; /* protected mode global descriptor (protect.c) */ + +EXTERN _PROTOTYPE( void (*level0_func), (void) ); +#endif /* (CHIP == INTEL) */ + +#if (CHIP == M68000) +/* M68000 specific variables go here. */ +#endif + + diff --git a/kernel/i8259.c b/kernel/i8259.c new file mode 100755 index 000000000..f856a840e --- /dev/null +++ b/kernel/i8259.c @@ -0,0 +1,163 @@ +/* This file contains routines for initializing the 8259 interrupt controller: + * put_irq_handler: register an interrupt handler + * intr_handle: handle a hardware interrupt + * intr_init: initialize the interrupt controller(s) + */ + +#include "kernel.h" + +#define ICW1_AT 0x11 /* edge triggered, cascade, need ICW4 */ +#define ICW1_PC 0x13 /* edge triggered, no cascade, need ICW4 */ +#define ICW1_PS 0x19 /* level triggered, cascade, need ICW4 */ +#define ICW4_AT 0x01 /* not SFNM, not buffered, normal EOI, 8086 */ +#define ICW4_PC 0x09 /* not SFNM, buffered, normal EOI, 8086 */ + +#if _WORD_SIZE == 2 +typedef _PROTOTYPE( void (*vecaddr_t), (void) ); + +FORWARD _PROTOTYPE( void set_vec, (int vec_nr, vecaddr_t addr) ); + +PRIVATE vecaddr_t int_vec[] = { + int00, int01, int02, int03, int04, int05, int06, int07, +}; + +PRIVATE vecaddr_t irq_vec[] = { + hwint00, hwint01, hwint02, hwint03, hwint04, hwint05, hwint06, hwint07, + hwint08, hwint09, hwint10, hwint11, hwint12, hwint13, hwint14, hwint15, +}; +#else +#define set_vec(nr, addr) ((void)0) +#endif + + +/*==========================================================================* + * intr_init * + *==========================================================================*/ +PUBLIC void intr_init(mine) +int mine; +{ +/* Initialize the 8259s, finishing with all interrupts disabled. This is + * only done in protected mode, in real mode we don't touch the 8259s, but + * use the BIOS locations instead. The flag "mine" is set if the 8259s are + * to be programmed for Minix, or to be reset to what the BIOS expects. + */ + + int i; + + lock(); + if (protected_mode) { + /* The AT and newer PS/2 have two interrupt controllers, one master, + * one slaved at IRQ 2. (We don't have to deal with the PC that + * has just one controller, because it must run in real mode.) + */ + outb(INT_CTL, ps_mca ? ICW1_PS : ICW1_AT); + outb(INT_CTLMASK, mine ? IRQ0_VECTOR : BIOS_IRQ0_VEC); + /* ICW2 for master */ + outb(INT_CTLMASK, (1 << CASCADE_IRQ)); /* ICW3 tells slaves */ + outb(INT_CTLMASK, ICW4_AT); + outb(INT_CTLMASK, ~(1 << CASCADE_IRQ)); /* IRQ 0-7 mask */ + outb(INT2_CTL, ps_mca ? ICW1_PS : ICW1_AT); + outb(INT2_CTLMASK, mine ? IRQ8_VECTOR : BIOS_IRQ8_VEC); + /* ICW2 for slave */ + outb(INT2_CTLMASK, CASCADE_IRQ); /* ICW3 is slave nr */ + outb(INT2_CTLMASK, ICW4_AT); + outb(INT2_CTLMASK, ~0); /* IRQ 8-15 mask */ + + /* Copy the BIOS vectors from the BIOS to the Minix location, so we + * can still make BIOS calls without reprogramming the i8259s. + */ +#if IRQ0_VECTOR != BIOS_IRQ0_VEC + phys_copy(BIOS_VECTOR(0) * 4L, VECTOR(0) * 4L, 8 * 4L); +#endif +#if IRQ8_VECTOR != BIOS_IRQ8_VEC + phys_copy(BIOS_VECTOR(8) * 4L, VECTOR(8) * 4L, 8 * 4L); +#endif + } else { + /* Use the BIOS interrupt vectors in real mode. We only reprogram the + * exceptions here, the interrupt vectors are reprogrammed on demand. + * SYS_VECTOR is the Minix system call for message passing. + */ + for (i = 0; i < 8; i++) set_vec(i, int_vec[i]); + set_vec(SYS_VECTOR, s_call); + } +} + +/*=========================================================================* + * put_irq_handler * + *=========================================================================*/ +PUBLIC void put_irq_handler(hook, irq, handler) +irq_hook_t *hook; +int irq; +irq_handler_t handler; +{ +/* Register an interrupt handler. */ + int id; + irq_hook_t **line; + + if ((unsigned) irq >= NR_IRQ_VECTORS) + panic("invalid call to put_irq_handler", irq); + + line = &irq_hooks[irq]; + id = 1; + while (*line != NULL) { + if (hook == *line) return; /* extra initialization */ + line = &(*line)->next; + id <<= 1; + } + if (id == 0) panic("Too many handlers for irq", irq); + + hook->next = NULL; + hook->handler = handler; + hook->irq = irq; + hook->id = id; + *line = hook; + + irq_use |= 1 << irq; +} + +/*==========================================================================* + * intr_handle * + *==========================================================================*/ +PUBLIC void intr_handle(hook) +irq_hook_t *hook; +{ +/* Call the interrupt handlers for an interrupt with the given hook list. + * The assembly part of the handler has already masked the IRQ, reenabled the + * controller(s) and enabled interrupts. + */ + + /* Call list of handlers for an IRQ. */ + while (hook != NULL) { + /* For each handler in the list, mark it active by setting its ID bit, + * call the function, and unmark it if the function returns true. + */ + irq_actids[hook->irq] |= hook->id; + if ((*hook->handler)(hook)) irq_actids[hook->irq] &= ~hook->id; + hook = hook->next; + } + + /* The assembly code will now disable interrupts, unmask the IRQ if and only + * if all active ID bits are cleared, and restart a process. + */ +} + +#if _WORD_SIZE == 2 +/*===========================================================================* + * set_vec * + *===========================================================================*/ +PRIVATE void set_vec(vec_nr, addr) +int vec_nr; /* which vector */ +vecaddr_t addr; /* where to start */ +{ +/* Set up a real mode interrupt vector. */ + + u16_t vec[2]; + + /* Build the vector in the array 'vec'. */ + vec[0] = (u16_t) addr; + vec[1] = (u16_t) physb_to_hclick(code_base); + + /* Copy the vector into place. */ + phys_copy(vir2phys(vec), vec_nr * 4L, 4L); +} +#endif /* _WORD_SIZE == 2 */ diff --git a/kernel/kernel.h b/kernel/kernel.h new file mode 100755 index 000000000..780ff8e26 --- /dev/null +++ b/kernel/kernel.h @@ -0,0 +1,26 @@ +/* This is the master header for the kernel. It includes some other files + * and defines the principal constants. + */ +#define _POSIX_SOURCE 1 /* tell headers to include POSIX stuff */ +#define _MINIX 1 /* tell headers to include MINIX stuff */ +#define _SYSTEM 1 /* tell headers that this is the kernel */ + +/* The following are so basic, all the *.c files get them automatically. */ +#include <minix/config.h> /* global configuration, MUST be first */ +#include <ansi.h> /* C style: ANSI or K&R, MUST be second */ +#include <sys/types.h> /* general system types */ +#include <minix/const.h> /* MINIX specific constants */ +#include <minix/type.h> /* MINIX specific types, e.g. message */ +#include <minix/ipc.h> /* MINIX run-time system */ +#include <timers.h> /* watchdog timer management */ +#include <errno.h> /* return codes and error numbers */ + +#if (CHIP == INTEL) +#include <ibm/portio.h> /* device I/O and toggle interrupts */ +#endif + +#include "const.h" /* kernel constants */ +#include "type.h" /* kernel type definitions */ +#include "proto.h" /* kernel function prototypes */ +#include "glo.h" /* kernel global variables */ + diff --git a/kernel/klib.s b/kernel/klib.s new file mode 100755 index 000000000..27ee56f97 --- /dev/null +++ b/kernel/klib.s @@ -0,0 +1,9 @@ +# +! Chooses between the 8086 and 386 versions of the low level kernel code. + +#include <minix/config.h> +#if _WORD_SIZE == 2 +#include "klib88.s" +#else +#include "klib386.s" +#endif diff --git a/kernel/klib386.s b/kernel/klib386.s new file mode 100755 index 000000000..95d2093f1 --- /dev/null +++ b/kernel/klib386.s @@ -0,0 +1,497 @@ +# +! sections + +.sect .text; .sect .rom; .sect .data; .sect .bss + +#include <minix/config.h> +#include <minix/const.h> +#include "const.h" +#include "sconst.h" +#include "protect.h" + +! This file contains a number of assembly code utility routines needed by the +! kernel. They are: + +.define _monitor ! exit Minix and return to the monitor +.define _int86 ! let the monitor make an 8086 interrupt call +.define _cp_mess ! copies messages from source to destination +.define _exit ! dummy for library routines +.define __exit ! dummy for library routines +.define ___exit ! dummy for library routines +.define ___main ! dummy for GCC +.define _phys_insw ! transfer data from (disk controller) port to memory +.define _phys_insb ! likewise byte by byte +.define _phys_outsw ! transfer data from memory to (disk controller) port +.define _phys_outsb ! likewise byte by byte +.define _enable_irq ! enable an irq at the 8259 controller +.define _disable_irq ! disable an irq +.define _phys_copy ! copy data from anywhere to anywhere in memory +.define _mem_rdw ! copy one word from [segment:offset] +.define _reset ! reset the system +.define _idle_task ! task executed when there is no work +.define _level0 ! call a function at level 0 + +! The routines only guarantee to preserve the registers the C compiler +! expects to be preserved (ebx, esi, edi, ebp, esp, segment registers, and +! direction bit in the flags). + +.sect .text +!*===========================================================================* +!* monitor * +!*===========================================================================* +! PUBLIC void monitor(); +! Return to the monitor. + +_monitor: + mov esp, (_mon_sp) ! restore monitor stack pointer + o16 mov dx, SS_SELECTOR ! monitor data segment + mov ds, dx + mov es, dx + mov fs, dx + mov gs, dx + mov ss, dx + pop edi + pop esi + pop ebp + o16 retf ! return to the monitor + + +!*===========================================================================* +!* int86 * +!*===========================================================================* +! PUBLIC void int86(); +_int86: + cmpb (_mon_return), 0 ! is the monitor there? + jnz 0f + movb ah, 0x01 ! an int 13 error seems appropriate + movb (_reg86+ 0), ah ! reg86.w.f = 1 (set carry flag) + movb (_reg86+13), ah ! reg86.b.ah = 0x01 = "invalid command" + ret +0: push ebp ! save C registers + push esi + push edi + push ebx + pushf ! save flags + cli ! no interruptions + + inb INT2_CTLMASK + movb ah, al + inb INT_CTLMASK + push eax ! save interrupt masks + mov eax, (_irq_use) ! map of in-use IRQ`s + and eax, ~[1<<CLOCK_IRQ] ! keep the clock ticking + outb INT_CTLMASK ! enable all unused IRQ`s and vv. + movb al, ah + outb INT2_CTLMASK + + mov eax, SS_SELECTOR ! monitor data segment + mov ss, ax + xchg esp, (_mon_sp) ! switch stacks + push (_reg86+36) ! parameters used in INT call + push (_reg86+32) + push (_reg86+28) + push (_reg86+24) + push (_reg86+20) + push (_reg86+16) + push (_reg86+12) + push (_reg86+ 8) + push (_reg86+ 4) + push (_reg86+ 0) + mov ds, ax ! remaining data selectors + mov es, ax + mov fs, ax + mov gs, ax + push cs + push return ! kernel return address and selector + o16 jmpf 20+2*4+10*4+2*4(esp) ! make the call +return: + pop (_reg86+ 0) + pop (_reg86+ 4) + pop (_reg86+ 8) + pop (_reg86+12) + pop (_reg86+16) + pop (_reg86+20) + pop (_reg86+24) + pop (_reg86+28) + pop (_reg86+32) + pop (_reg86+36) + lgdt (_gdt+GDT_SELECTOR) ! reload global descriptor table + jmpf CS_SELECTOR:csinit ! restore everything +csinit: mov eax, DS_SELECTOR + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + xchg esp, (_mon_sp) ! unswitch stacks + lidt (_gdt+IDT_SELECTOR) ! reload interrupt descriptor table + andb (_gdt+TSS_SELECTOR+DESC_ACCESS), ~0x02 ! clear TSS busy bit + mov eax, TSS_SELECTOR + ltr ax ! set TSS register + + pop eax + outb INT_CTLMASK ! restore interrupt masks + movb al, ah + outb INT2_CTLMASK + + add (_lost_ticks), ecx ! record lost clock ticks + + popf ! restore flags + pop ebx ! restore C registers + pop edi + pop esi + pop ebp + ret + + +!*===========================================================================* +!* cp_mess * +!*===========================================================================* +! PUBLIC void cp_mess(int src, phys_clicks src_clicks, vir_bytes src_offset, +! phys_clicks dst_clicks, vir_bytes dst_offset); +! This routine makes a fast copy of a message from anywhere in the address +! space to anywhere else. It also copies the source address provided as a +! parameter to the call into the first word of the destination message. +! +! Note that the message size, "Msize" is in DWORDS (not bytes) and must be set +! correctly. Changing the definition of message in the type file and not +! changing it here will lead to total disaster. + +CM_ARGS = 4 + 4 + 4 + 4 + 4 ! 4 + 4 + 4 + 4 + 4 +! es ds edi esi eip proc scl sof dcl dof + + .align 16 +_cp_mess: + cld + push esi + push edi + push ds + push es + + mov eax, FLAT_DS_SELECTOR + mov ds, ax + mov es, ax + + mov esi, CM_ARGS+4(esp) ! src clicks + shl esi, CLICK_SHIFT + add esi, CM_ARGS+4+4(esp) ! src offset + mov edi, CM_ARGS+4+4+4(esp) ! dst clicks + shl edi, CLICK_SHIFT + add edi, CM_ARGS+4+4+4+4(esp) ! dst offset + + mov eax, CM_ARGS(esp) ! process number of sender + stos ! copy number of sender to dest message + add esi, 4 ! do not copy first word + mov ecx, Msize - 1 ! remember, first word does not count + rep + movs ! copy the message + + pop es + pop ds + pop edi + pop esi + ret ! that is all folks! + + +!*===========================================================================* +!* exit * +!*===========================================================================* +! PUBLIC void exit(); +! Some library routines use exit, so provide a dummy version. +! Actual calls to exit cannot occur in the kernel. +! GNU CC likes to call ___main from main() for nonobvious reasons. + +_exit: +__exit: +___exit: + sti + jmp ___exit + +___main: + ret + + +!*===========================================================================* +!* phys_insw * +!*===========================================================================* +! PUBLIC void phys_insw(Port_t port, phys_bytes buf, size_t count); +! Input an array from an I/O port. Absolute address version of insw(). + +_phys_insw: + push ebp + mov ebp, esp + cld + push edi + push es + mov ecx, FLAT_DS_SELECTOR + mov es, cx + mov edx, 8(ebp) ! port to read from + mov edi, 12(ebp) ! destination addr + mov ecx, 16(ebp) ! byte count + shr ecx, 1 ! word count +rep o16 ins ! input many words + pop es + pop edi + pop ebp + ret + + +!*===========================================================================* +!* phys_insb * +!*===========================================================================* +! PUBLIC void phys_insb(Port_t port, phys_bytes buf, size_t count); +! Input an array from an I/O port. Absolute address version of insb(). + +_phys_insb: + push ebp + mov ebp, esp + cld + push edi + push es + mov ecx, FLAT_DS_SELECTOR + mov es, cx + mov edx, 8(ebp) ! port to read from + mov edi, 12(ebp) ! destination addr + mov ecx, 16(ebp) ! byte count + shr ecx, 1 ! word count + rep insb ! input many bytes + pop es + pop edi + pop ebp + ret + + +!*===========================================================================* +!* phys_outsw * +!*===========================================================================* +! PUBLIC void phys_outsw(Port_t port, phys_bytes buf, size_t count); +! Output an array to an I/O port. Absolute address version of outsw(). + + .align 16 +_phys_outsw: + push ebp + mov ebp, esp + cld + push esi + push ds + mov ecx, FLAT_DS_SELECTOR + mov ds, cx + mov edx, 8(ebp) ! port to write to + mov esi, 12(ebp) ! source addr + mov ecx, 16(ebp) ! byte count + shr ecx, 1 ! word count +rep o16 outs ! output many words + pop ds + pop esi + pop ebp + ret + + +!*===========================================================================* +!* phys_outsb * +!*===========================================================================* +! PUBLIC void phys_outsb(Port_t port, phys_bytes buf, size_t count); +! Output an array to an I/O port. Absolute address version of outsb(). + + .align 16 +_phys_outsb: + push ebp + mov ebp, esp + cld + push esi + push ds + mov ecx, FLAT_DS_SELECTOR + mov ds, cx + mov edx, 8(ebp) ! port to write to + mov esi, 12(ebp) ! source addr + mov ecx, 16(ebp) ! byte count + rep outsb ! output many bytes + pop ds + pop esi + pop ebp + ret + + +!*==========================================================================* +!* enable_irq * +!*==========================================================================*/ +! PUBLIC void enable_irq(irq_hook_t *hook) +! Enable an interrupt request line by clearing an 8259 bit. +! Equivalent C code for hook->irq < 8: +! if ((irq_actids[hook->irq] &= ~hook->id) == 0) +! outb(INT_CTLMASK, inb(INT_CTLMASK) & ~(1 << irq)); + + .align 16 +_enable_irq: + push ebp + mov ebp, esp + pushf + cli + mov eax, 8(ebp) ! hook + mov ecx, 8(eax) ! irq + mov eax, 12(eax) ! id bit + not eax + and _irq_actids(ecx*4), eax ! clear this id bit + jnz en_done ! still masked by other handlers? + movb ah, ~1 + rolb ah, cl ! ah = ~(1 << (irq % 8)) + mov edx, INT_CTLMASK ! enable irq < 8 at the master 8259 + cmpb cl, 8 + jb 0f + mov edx, INT2_CTLMASK ! enable irq >= 8 at the slave 8259 +0: inb dx + andb al, ah + outb dx ! clear bit at the 8259 +en_done:popf + leave + ret + + +!*==========================================================================* +!* disable_irq * +!*==========================================================================*/ +! PUBLIC int disable_irq(irq_hook_t *hook) +! Disable an interrupt request line by setting an 8259 bit. +! Equivalent C code for irq < 8: +! irq_actids[hook->irq] |= hook->id; +! outb(INT_CTLMASK, inb(INT_CTLMASK) | (1 << irq)); +! Returns true iff the interrupt was not already disabled. + + .align 16 +_disable_irq: + push ebp + mov ebp, esp + pushf + cli + mov eax, 8(ebp) ! hook + mov ecx, 8(eax) ! irq + mov eax, 12(eax) ! id bit + or _irq_actids(ecx*4), eax ! set this id bit + movb ah, 1 + rolb ah, cl ! ah = (1 << (irq % 8)) + mov edx, INT_CTLMASK ! disable irq < 8 at the master 8259 + cmpb cl, 8 + jb 0f + mov edx, INT2_CTLMASK ! disable irq >= 8 at the slave 8259 +0: inb dx + testb al, ah + jnz dis_already ! already disabled? + orb al, ah + outb dx ! set bit at the 8259 + mov eax, 1 ! disabled by this function + popf + leave + ret +dis_already: + xor eax, eax ! already disabled + popf + leave + ret + + +!*===========================================================================* +!* phys_copy * +!*===========================================================================* +! PUBLIC void phys_copy(phys_bytes source, phys_bytes destination, +! phys_bytes bytecount); +! Copy a block of physical memory. + +PC_ARGS = 4 + 4 + 4 + 4 ! 4 + 4 + 4 +! es edi esi eip src dst len + + .align 16 +_phys_copy: + cld + push esi + push edi + push es + + mov eax, FLAT_DS_SELECTOR + mov es, ax + + mov esi, PC_ARGS(esp) + mov edi, PC_ARGS+4(esp) + mov eax, PC_ARGS+4+4(esp) + + cmp eax, 10 ! avoid align overhead for small counts + jb pc_small + mov ecx, esi ! align source, hope target is too + neg ecx + and ecx, 3 ! count for alignment + sub eax, ecx + rep + eseg movsb + mov ecx, eax + shr ecx, 2 ! count of dwords + rep + eseg movs + and eax, 3 +pc_small: + xchg ecx, eax ! remainder + rep + eseg movsb + + pop es + pop edi + pop esi + ret + + +!*===========================================================================* +!* mem_rdw * +!*===========================================================================* +! PUBLIC u16_t mem_rdw(U16_t segment, u16_t *offset); +! Load and return word at far pointer segment:offset. + + .align 16 +_mem_rdw: + mov cx, ds + mov ds, 4(esp) ! segment + mov eax, 4+4(esp) ! offset + movzx eax, (eax) ! word to return + mov ds, cx + ret + + +!*===========================================================================* +!* reset * +!*===========================================================================* +! PUBLIC void reset(); +! Reset the system by loading IDT with offset 0 and interrupting. + +_reset: + lidt (idt_zero) + int 3 ! anything goes, the 386 will not like it +.sect .data +idt_zero: .data4 0, 0 +.sect .text + + +!*===========================================================================* +!* idle_task * +!*===========================================================================* +_idle_task: +! This task is called when the system has nothing else to do. The HLT +! instruction puts the processor in a state where it draws minimum power. + push halt + call _level0 ! level0(halt) + pop eax + jmp _idle_task +halt: + sti + hlt + cli + ret + +!*===========================================================================* +!* level0 * +!*===========================================================================* +! PUBLIC void level0(void (*func)(void)) +! Call a function at permission level 0. This allows kernel tasks to do +! things that are only possible at the most privileged CPU level. +! +_level0: + mov eax, 4(esp) + mov (_level0_func), eax + int LEVEL0_VECTOR + ret diff --git a/kernel/klib88.s b/kernel/klib88.s new file mode 100755 index 000000000..68f28219d --- /dev/null +++ b/kernel/klib88.s @@ -0,0 +1,1057 @@ +# +#include <minix/config.h> +#include <minix/const.h> +#include "const.h" +#include "sconst.h" +#include "protect.h" + +! This file contains a number of assembly code utility routines needed by the +! kernel. They are: + +.define _monitor ! exit Minix and return to the monitor +.define _int86 ! make an 8086 interrupt call +.define real2prot ! switch from real to protected mode +.define prot2real ! switch from protected to real mode +.define _cp_mess ! copies messages from source to destination +.define _exit ! dummy for library routines +.define __exit ! dummy for library routines +.define ___exit ! dummy for library routines +.define .fat, .trp ! dummies for library routines +.define _phys_insw ! transfer data from (disk controller) port to memory +.define _phys_insb ! likewise byte by byte +.define _phys_outsw ! transfer data from memory to (disk controller) port +.define _phys_outsb ! likewise byte by byte +.define _enable_irq ! enable an irq at the 8259 controller +.define _disable_irq ! disable an irq +.define _phys_copy ! copy data from anywhere to anywhere in memory +.define _mem_rdw ! copy one word from [segment:offset] +.define _reset ! reset the system +.define _mem_vid_copy ! copy data to video ram +.define _vid_vid_copy ! move data in video ram +.define _idle_task ! executed when there is no work +.define _level0 ! call a function at level 0 +.define klib_init_prot ! initialize klib functions for protected mode + +! The routines only guarantee to preserve the registers the C compiler +! expects to be preserved (si, di, bp, sp, segment registers, and direction +! bit in the flags), though some of the older ones preserve bx, cx and dx. + +#define DS_286_OFFSET DS_286_INDEX*DESC_SIZE +#define ES_286_OFFSET ES_286_INDEX*DESC_SIZE +# define EM_XFER_FUNC 0x87 +#define JMP_OPCODE 0xE9 /* opcode used for patching */ +#define OFF_MASK 0x000F /* offset mask for phys_b -> hclick:offset */ +#define HCHIGH_MASK 0x0F /* h/w click mask for low byte of hi word */ +#define HCLOW_MASK 0xF0 /* h/w click mask for low byte of low word */ + +! Exported variables +.extern kernel_cs + + .text +!*===========================================================================* +!* monitor * +!*===========================================================================* +! PUBLIC void monitor(); +! Return to the monitor. + +_monitor: + call prot2real ! switch to real mode + mov sp, _mon_sp ! restore monitor stack pointer + mov bx, _mon_ss ! monitor data segment + mov ds, bx + mov es, bx + mov ss, bx + pop di + pop si + pop bp + retf ! return to the monitor + + +!*===========================================================================* +!* int86 * +!*===========================================================================* +! PUBLIC void int86(); +_int86: ! make an 8086 interrupt call. + push bp + push si + push di ! save C variable registers + pushf ! save flags + + call int86 ! make the actual call + + popf ! restore flags + pop di ! restore C registers + pop si + pop bp + ret + +! Do an 8086 interrupt from protected mode +p_int86: + push bp + push si + push di ! save C variable registers + pushf ! save flags + + cli ! no interruptions + inb INT2_CTLMASK + movb ah, al + inb INT_CTLMASK + push ax ! save interrupt masks + mov ax, _irq_use ! map of in-use IRQs + and ax, #~[1<<CLOCK_IRQ] ! keep the clock ticking + outb INT_CTLMASK ! enable all unused IRQs and vv. + movb al, ah + outb INT2_CTLMASK + + call prot2real ! switch to real mode + + xor ax, ax + mov es, ax ! vector & BIOS data segments + eseg mov 0x046C, ax ! clear BIOS clock counter + eseg mov 0x046E, ax + + sti ! enable interrupts + call int86 ! make the actual call + cli ! disable interrupts + + xor ax, ax + mov es, ax + eseg mov ax, 0x046C + add _lost_ticks, ax ! record lost clock ticks + + call real2prot ! back to protected mode + + pop ax ! restore interrupt masks + outb INT_CTLMASK + movb al, ah + outb INT2_CTLMASK + + popf ! restore flags + pop di ! restore C registers + pop si + pop bp + ret + +int86: + mov bp, #_reg86 ! address of parameter block + movb al, #0xCD ! INT instruction + movb ah, (bp) ! Interrupt number? + testb ah, ah + jnz 0f ! nonzero if INT, otherwise far call + push cs + push #intret+2 ! far return address + push 6(bp) + push 4(bp) ! far driver address + mov ax, #0x90CB ! RETF; NOP +0: cseg mov intret, ax ! patch `INT n` or `RETF; NOP` into code + jmp .+2 ! clear instruction queue + + mov ds, 8(bp) ! Load parameters + mov es, 10(bp) + mov ax, 12(bp) ! (sorry, only ax set, not eax) + mov bx, 16(bp) + mov cx, 20(bp) + mov dx, 24(bp) + mov si, 28(bp) + mov di, 32(bp) + mov bp, 36(bp) + +intret: int 0xFF ! do the interrupt or far call + + push bp + pushf + mov bp, #_reg86 ! address of parameter block + pop (bp) ! eflags + mov 8(bp), ds + mov 10(bp), es + mov 12(bp), ax + mov 16(bp), bx + mov 20(bp), cx + mov 24(bp), dx + mov 28(bp), si + mov 32(bp), di + pop 36(bp) ! bp + + mov ax, ss + mov ds, ax ! restore ds and es + mov es, ax + ret + + +!*===========================================================================* +!* real2prot * +!*===========================================================================* +! Switch from real to protected mode. +real2prot: + lgdt _gdt+GDT_SELECTOR ! set global descriptor table + smsw ax + mov msw, ax ! save real mode msw + orb al, #0x01 ! set PE (protection enable) bit + lmsw ax ! set msw, enabling protected mode + + jmpf csinit, CS_SELECTOR ! set code segment selector +csinit: + mov ax, #DS_SELECTOR ! set data selectors + mov ds, ax + mov es, ax + mov ss, ax + lidt _gdt+IDT_SELECTOR ! set interrupt vectors + andb _gdt+TSS_SELECTOR+DESC_ACCESS, #~0x02 ! clear TSS busy bit + mov ax, #TSS_SELECTOR + ltr ax ! set TSS register + + movb ah, #0x02 + jmp gate_A20 ! enable the A20 address line + + +!*===========================================================================* +!* prot2real * +!*===========================================================================* +! Switch from protected to real mode. +prot2real: + mov save_sp, sp ! save stack pointer + cmp _processor, #386 ! is this a 386? + jae p2r386 +p2r286: + xor ax, ax + mov _gdt+ES_286_OFFSET+DESC_BASE, ax + movb _gdt+ES_286_OFFSET+DESC_BASE_MIDDLE, al + movb _gdt+ES_286_OFFSET+DESC_BASE_HIGH, al + mov ax, #ES_286_SELECTOR + mov es, ax ! BIOS data segment + eseg mov 0x0467, #real ! set return from shutdown address + cseg mov ax, kernel_cs + eseg mov 0x0469, ax + movb al, #0x8F + outb 0x70 ! select CMOS byte 0x0F (disable NMI) + jmp .+2 + movb al, #0x0A + outb 0x71 ! set shutdown code to 0x0A "jump far" + jmp p_reset ! cause a processor shutdown +p2r386: + lidt idt_vectors ! real mode interrupt vectors + push _gdt+CS_SELECTOR+0 + push _gdt+DS_SELECTOR+0 ! save CS and DS limits + mov _gdt+CS_SELECTOR+0, #0xFFFF + mov _gdt+DS_SELECTOR+0, #0xFFFF ! set 64k limits + jmpf cs64k, CS_SELECTOR ! reload selectors +cs64k: mov ax, #DS_SELECTOR + mov ds, ax + mov es, ax + mov ss, ax + pop _gdt+DS_SELECTOR+0 + pop _gdt+CS_SELECTOR+0 ! restore CS and DS limits + .data1 0x0F,0x20,0xC0 ! mov eax, cr0 + mov ax, msw ! restore real mode (16 bits) msw + .data1 0x0F,0x22,0xC0 ! mov cr0, eax + .data1 0xEA ! jmpf real, "kernel_cs" + .data2 real +kernel_cs: + .data2 0 +real: + cseg mov ax, kernel_ds ! reload data segment registers + mov ds, ax + mov es, ax + mov ss, ax + mov sp, save_sp ! restore stack + + xorb ah, ah + !jmp gate_A20 ! disable the A20 address line + +! Enable (ah = 0x02) or disable (ah = 0x00) the A20 address line. +gate_A20: + cmp _ps_mca, #0 ! PS/2 bus? + jnz gate_PS_A20 + call kb_wait + movb al, #0xD1 ! Tell keyboard that a command is coming + outb 0x64 + call kb_wait + movb al, #0xDD ! 0xDD = A20 disable code if ah = 0x00 + orb al, ah ! 0xDF = A20 enable code if ah = 0x02 + outb 0x60 + call kb_wait + movb al, #0xFF ! Pulse output port + outb 0x64 + call kb_wait ! Wait for the A20 line to settle down + ret +kb_wait: + inb 0x64 + testb al, #0x02 ! Keyboard input buffer full? + jnz kb_wait ! If so, wait + ret + +gate_PS_A20: ! The PS/2 can twiddle A20 using port A + inb 0x92 ! Read port A + andb al, #0xFD + orb al, ah ! Set A20 bit to the required state + outb 0x92 ! Write port A + jmp .+2 ! Small delay +A20ok: inb 0x92 ! Check port A + andb al, #0x02 + cmpb al, ah ! A20 line settled down to the new state? + jne A20ok ! If not then wait + ret + + +!*===========================================================================* +!* cp_mess * +!*===========================================================================* +! PUBLIC void cp_mess(int src, phys_clicks src_clicks, vir_bytes src_offset, +! phys_clicks dst_clicks, vir_bytes dst_offset); +! This routine makes a fast copy of a message from anywhere in the address +! space to anywhere else. It also copies the source address provided as a +! parameter to the call into the first word of the destination message. +! +! Note that the message size, "Msize" is in WORDS (not bytes) and must be set +! correctly. Changing the definition of message in the type file and not +! changing it here will lead to total disaster. + +_cp_mess: + cld + push es ! save es + push ds ! save ds + mov bx,sp ! index off bx because machine cannot use sp + push si ! save si + push di ! save di + + mov ax,12(bx) ! destination click +#if HCLICK_SHIFT > CLICK_SHIFT +#error /* Small click sizes are not supported (right shift will lose bits). */ +#endif +#if HCLICK_SHIFT < CLICK_SHIFT + movb cl,#CLICK_SHIFT-HCLICK_SHIFT + shl ax,cl ! destination segment +#endif + mov es,ax + mov di,14(bx) ! offset of destination message + +! Be careful not to destroy ds before we are finished with the bx pointer. +! We are using bx and not the more natural bp to save pushing bp. + + mov ax,6(bx) ! process number of sender + mov si,10(bx) ! offset of source message + mov bx,8(bx) ! source click (finished with bx as a pointer) +#if HCLICK_SHIFT < CLICK_SHIFT + shl bx,cl ! source segment +#endif + mov ds,bx + + stos ! copy process number of sender to dest message + add si,*2 ! do not copy first word + mov cx,*Msize-1 ! remember, first word does not count + rep ! iterate cx times to copy 11 words + movs ! copy the message + pop di ! restore di + pop si ! restore si + pop ds ! restore ds + pop es ! restore es + ret ! that is all folks! + + +!*===========================================================================* +!* exit * +!*===========================================================================* +! PUBLIC void exit(); +! Some library routines use exit, so provide a dummy version. +! Actual calls to exit cannot occur in the kernel. +! Same for .fat & .trp. + +_exit: +__exit: +___exit: +.fat: +.trp: + sti + jmp __exit + + +!*===========================================================================* +!* phys_insw * +!*===========================================================================* +! PUBLIC void phys_insw(Port_t port, phys_bytes buf, size_t count); +! Input an array from an I/O port. Absolute address version of insw(). + +_phys_insw: + call portio_intro + mov es, bx ! destination segment + mov di, ax ! destination offset + shr cx, #1 ! word count + rep ins ! input many words + jmp portio_return + +portio_intro: + pop ax ! hold return address + push bp ! create stack frame + mov bp, sp + push si + push di + push ax ! retore return address + mov dx, 4(bp) ! port to do I/O + mov ax, 6(bp) ! source/destination address in bx:ax + mov bx, 8(bp) + mov cx, 10(bp) ! count in bytes + cld ! direction is UP +portio_setup: + push cx ! segment/offset setup + movb ch, bl + mov bx, ax + and ax, #0x000F ! ax = offset = address % HCLICK_SIZE + movb cl, #4 + shr bx, cl + shlb ch, cl + orb bh, ch ! bx = segment = address / HCLICK_SIZE + pop cx + ret + +portio_return: + mov ax, ss ! restore segment registers + mov ds, ax + mov es, ax + pop di ! unwind stack frame + pop si + pop bp + ret + + +!*===========================================================================* +!* phys_insb * +!*===========================================================================* +! PUBLIC void phys_insb(Port_t port, phys_bytes buf, size_t count); +! Input an array from an I/O port. Absolute address version of insb(). +! Note: The 8086 doesn't have string I/O instructions, so a loop is used. + +_phys_insb: + call portio_intro + mov es, bx ! destination segment + mov di, ax ! destination offset + jcxz 1f +0: inb dx ! input 1 byte + stosb ! write 1 byte + loop 0b ! many times +1: + jmp portio_return + + +!*===========================================================================* +!* phys_outsw * +!*===========================================================================* +! PUBLIC void phys_outsw(Port_t port, phys_bytes buf, size_t count); +! Output an array to an I/O port. Absolute address version of outsw(). + +_phys_outsw: + call portio_intro + mov ds, bx ! source segment + mov si, ax ! source offset + shr cx, #1 ! word count + rep outs ! output many words + jmp portio_return + + +!*===========================================================================* +!* phys_outsb * +!*===========================================================================* +! PUBLIC void phys_outsb(Port_t port, phys_bytes buf, size_t count); +! Output an array to an I/O port. Absolute address version of outsb(). + +_phys_outsb: + call portio_intro + mov ds, bx ! source segment + mov si, ax ! source offset + jcxz 1f +0: lodsb ! read 1 byte + outb dx ! output 1 byte + loop 0b ! many times +1: + jmp portio_return + + +!*==========================================================================* +!* enable_irq * +!*==========================================================================*/ +! PUBLIC void enable_irq(irq_hook_t *hook) +! Enable an interrupt request line by clearing an 8259 bit. +! Equivalent code for irq < 8: +! if ((irq_actids[hook->irq] &= ~hook->id) == 0) +! outb(INT_CTLMASK, inb(INT_CTLMASK) & ~(1 << irq)); + +_enable_irq: + push bp + mov bp, sp + pushf + cli + mov bx, 4(bp) ! hook + mov cx, 4(bx) ! irq + mov ax, 6(bx) ! id bit + not ax + mov bx, cx + add bx, bx + and _irq_actids(bx), ax ! clear this id bit + jnz en_done ! still masked by other handlers? + movb ah, #~1 + rolb ah, cl ! ah = ~(1 << (irq % 8)) + mov dx, #INT_CTLMASK ! enable irq < 8 at the master 8259 + cmpb cl, #8 + jb 0f + mov dx, #INT2_CTLMASK ! enable irq >= 8 at the slave 8259 +0: inb dx + andb al, ah + outb dx ! clear bit at the 8259 +en_done:popf + leave + ret + + +!*==========================================================================* +!* disable_irq * +!*==========================================================================*/ +! PUBLIC int disable_irq(irq_hook_t *hook) +! Disable an interrupt request line by setting an 8259 bit. +! Equivalent code for irq < 8: +! irq_actids[hook->irq] |= hook->id; +! outb(INT_CTLMASK, inb(INT_CTLMASK) | (1 << irq)); +! Returns true iff the interrupt was not already disabled. + +_disable_irq: + push bp + mov bp, sp + pushf + cli + mov bx, 4(bp) ! hook + mov cx, 4(bx) ! irq + mov ax, 6(bx) ! id bit + pushf + cli + mov bx, cx + add bx, bx + or _irq_actids(bx), ax ! set this id bit + movb ah, #1 + rolb ah, cl ! ah = (1 << (irq % 8)) + mov dx, #INT_CTLMASK ! disable irq < 8 at the master 8259 + cmpb cl, #8 + jb 0f + mov dx, #INT2_CTLMASK ! disable irq >= 8 at the slave 8259 +0: inb dx + testb al, ah + jnz dis_already ! already disabled? + orb al, ah + outb dx ! set bit at the 8259 + mov ax, #1 ! disabled by this function + popf + leave + ret +dis_already: + xor ax, ax ! already disabled + popf + leave + ret + + +!*===========================================================================* +!* phys_copy * +!*===========================================================================* +! PUBLIC void phys_copy(phys_bytes source, phys_bytes destination, +! phys_bytes bytecount); +! Copy a block of physical memory. + +SRCLO = 4 +SRCHI = 6 +DESTLO = 8 +DESTHI = 10 +COUNTLO = 12 +COUNTHI = 14 + +_phys_copy: + push bp ! save only registers required by C + mov bp,sp ! set bp to point to source arg less 4 + + push si ! save si + push di ! save di + push ds ! save ds + push es ! save es + + mov ax,SRCLO(bp) ! dx:ax = source address (dx is NOT segment) + mov dx,SRCHI(bp) + mov si,ax ! si = source offset = address % 16 + and si,#OFF_MASK + andb dl,#HCHIGH_MASK ! ds = source segment = address / 16 % 0x10000 + andb al,#HCLOW_MASK + orb al,dl ! now bottom 4 bits of dx are in ax + movb cl,#HCLICK_SHIFT ! rotate them to the top 4 + ror ax,cl + mov ds,ax + + mov ax,DESTLO(bp) ! dx:ax = destination addr (dx is NOT segment) + mov dx,DESTHI(bp) + mov di,ax ! di = dest offset = address % 16 + and di,#OFF_MASK + andb dl,#HCHIGH_MASK ! es = dest segment = address / 16 % 0x10000 + andb al,#HCLOW_MASK + orb al,dl + ror ax,cl + mov es,ax + + mov ax,COUNTLO(bp) ! dx:ax = remaining count + mov dx,COUNTHI(bp) + +! copy upwards (cannot handle overlapped copy) + +pc_loop: + mov cx,ax ! provisional count for this iteration + test ax,ax ! if count >= 0x8000, only do 0x8000 per iter + js pc_bigcount ! low byte already >= 0x8000 + test dx,dx + jz pc_upcount ! less than 0x8000 +pc_bigcount: + mov cx,#0x8000 ! use maximum count per iteration +pc_upcount: + sub ax,cx ! update count + sbb dx,#0 ! cannot underflow, so carry clear now for rcr + rcr cx,#1 ! count in words, carry remembers if byte + jnb pc_even ! no odd byte + movb ! copy odd byte +pc_even: + rep ! copy 1 word at a time + movs ! word copy + + mov cx,ax ! test if remaining count is 0 + or cx,dx + jnz pc_more ! more to do + + pop es ! restore es + pop ds ! restore ds + pop di ! restore di + pop si ! restore si + pop bp ! restore bp + ret ! return to caller + +pc_more: + sub si,#0x8000 ! adjust pointers so the offset does not + mov cx,ds ! overflow in the next 0x8000 bytes + add cx,#0x800 ! pointers end up same physical location + mov ds,cx ! the current offsets are known >= 0x8000 + sub di,#0x8000 ! since we just copied that many + mov cx,es + add cx,#0x800 + mov es,cx + jmp pc_loop ! start next iteration + + +!*===========================================================================* +!* mem_rdw * +!*===========================================================================* +! PUBLIC u16_t mem_rdw(u16_t segment, u16_t *offset); +! Load and return the word at the far pointer segment:offset. + +_mem_rdw: + mov cx,ds ! save ds + pop dx ! return adr + pop ds ! segment + pop bx ! offset + sub sp,#2+2 ! adjust for parameters popped + mov ax,(bx) ! load the word to return + mov ds,cx ! restore ds + jmp (dx) ! return + + +!*===========================================================================* +!* reset * +!*===========================================================================* +! PUBLIC void reset(); +! Reset the system. +! In real mode we simply jump to the reset address at F000:FFF0. + +_reset: + jmpf 0xFFF0,0xF000 + + +!*===========================================================================* +!* mem_vid_copy * +!*===========================================================================* +! PUBLIC void mem_vid_copy(u16 *src, unsigned dst, unsigned count); +! +! Copy count characters from kernel memory to video memory. Src is an ordinary +! pointer to a word, but dst and count are character (word) based video offset +! and count. If src is null then screen memory is blanked by filling it with +! blank_color. + +_mem_vid_copy: + push bp + mov bp, sp + push si + push di + push es + mov si, 4(bp) ! source + mov di, 6(bp) ! destination + mov dx, 8(bp) ! count + mov es, _vid_seg ! segment containing video memory + cld ! make sure direction is up +mvc_loop: + and di, _vid_mask ! wrap address + mov cx, dx ! one chunk to copy + mov ax, _vid_size + sub ax, di + cmp cx, ax + jbe 0f + mov cx, ax ! cx = min(cx, vid_size - di) +0: sub dx, cx ! count -= cx + shl di, #1 ! byte address + add di, _vid_off ! in video memory + test si, si ! source == 0 means blank the screen + jz mvc_blank +mvc_copy: + rep ! copy words to video memory + movs + jmp mvc_test +mvc_blank: + mov ax, _blank_color ! ax = blanking character + rep + stos ! copy blanks to video memory + !jmp mvc_test +mvc_test: + sub di, _vid_off + shr di, #1 ! back to a word address + test dx, dx + jnz mvc_loop +mvc_done: + pop es + pop di + pop si + pop bp + ret + + +!*===========================================================================* +!* vid_vid_copy * +!*===========================================================================* +! PUBLIC void vid_vid_copy(unsigned src, unsigned dst, unsigned count); +! +! Copy count characters from video memory to video memory. Handle overlap. +! Used for scrolling, line or character insertion and deletion. Src, dst +! and count are character (word) based video offsets and counts. + +_vid_vid_copy: + push bp + mov bp, sp + push si + push di + push es + mov si, 4(bp) ! source + mov di, 6(bp) ! destination + mov dx, 8(bp) ! count + mov es, _vid_seg ! segment containing video memory + cmp si, di ! copy up or down? + jb vvc_down +vvc_up: + cld ! direction is up +vvc_uploop: + and si, _vid_mask ! wrap addresses + and di, _vid_mask + mov cx, dx ! one chunk to copy + mov ax, _vid_size + sub ax, si + cmp cx, ax + jbe 0f + mov cx, ax ! cx = min(cx, vid_size - si) +0: mov ax, _vid_size + sub ax, di + cmp cx, ax + jbe 0f + mov cx, ax ! cx = min(cx, vid_size - di) +0: sub dx, cx ! count -= cx + call vvc_copy + test dx, dx + jnz vvc_uploop ! again? + jmp vvc_done +vvc_down: + std ! direction is down + add si, dx ! start copying at the top + dec si + add di, dx + dec di +vvc_downloop: + and si, _vid_mask ! wrap addresses + and di, _vid_mask + mov cx, dx ! one chunk to copy + lea ax, 1(si) + cmp cx, ax + jbe 0f + mov cx, ax ! cx = min(cx, si + 1) +0: lea ax, 1(di) + cmp cx, ax + jbe 0f + mov cx, ax ! cx = min(cx, di + 1) +0: sub dx, cx ! count -= cx + call vvc_copy + test dx, dx + jnz vvc_downloop ! again? + cld ! C compiler expect up + !jmp vvc_done +vvc_done: + pop es + pop di + pop si + pop bp + ret + +! Copy video words. (Inner code of both the up and downcopying loop.) +vvc_copy: + shl si, #1 + shl di, #1 ! byte addresses + add si, _vid_off + add di, _vid_off ! in video memory + push ds ! must set ds here, 8086 can't do + mov ds, _vid_seg ! 'rep eseg movs' with interrupts on + rep + movs ! copy video words + pop ds + sub si, _vid_off + sub di, _vid_off + shr si, #1 + shr di, #1 ! back to word addresses + ret + + +!*===========================================================================* +!* level0 * +!*===========================================================================* +! PUBLIC void level0(void (*func)(void)) +! Not very interesting in real mode, see p_level0. +! +_level0: + mov bx, sp + jmp @2(bx) + + +!*===========================================================================* +!* klib_init_prot * +!*===========================================================================* +! PUBLIC void klib_init_prot(); +! Initialize klib for protected mode by patching some real mode functions +! at their starts to jump to their protected mode equivalents, according to +! the patch table. Saves a lot of tests on the "protected_mode" variable. +! Note that this function must be run in real mode, for it writes the code +! segment. (One otherwise has to set up a descriptor, etc, etc.) + +klib_init_prot: + mov si,#patch_table +kip_next: + lods ! original function + mov bx,ax + cseg movb (bx),#JMP_OPCODE ! overwrite start of function by a long jump + lods ! new function - target of jump + sub ax,bx ! relative jump + sub ax,#3 ! adjust by length of jump instruction + cseg mov 1(bx),ax ! set address + cmp si,#end_patch_table ! end of table? + jb kip_next +kip_done: + ret + + +!*===========================================================================* +!* variants for protected mode * +!*===========================================================================* +! Some routines are different in protected mode. +! The only essential difference is the handling of segment registers. +! One complication is that the method of building segment descriptors is not +! reentrant, so the protected mode versions must not be called by interrupt +! handlers. + + +!*===========================================================================* +!* p_cp_mess * +!*===========================================================================* +! The real mode version attempts to be efficient by passing raw segments but +! that just gets in the way here. + +p_cp_mess: + mov bx, sp ! bx -> arguments + push si + push di + push ds + push es + + mov ax, 4(bx) ! Compute source descriptor base + mov dx, ax + shl ax, #CLICK_SHIFT + shr dx, #16-CLICK_SHIFT ! dx:ax = src_clicks * CLICK_SIZE + add ax, 6(bx) + adc dx, #0 ! dx:ax += src_offset + mov _gdt+DS_286_OFFSET+DESC_BASE, ax + movb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE, dl + movb _gdt+DS_286_OFFSET+DESC_BASE_HIGH, dh + + mov ax, 8(bx) ! Compute destination descriptor base + mov dx, ax + shl ax, #CLICK_SHIFT + shr dx, #16-CLICK_SHIFT ! dx:ax = dst_clicks * CLICK_SIZE + add ax, 10(bx) + adc dx, #0 ! dx:ax += dst_offset + mov _gdt+ES_286_OFFSET+DESC_BASE, ax + movb _gdt+ES_286_OFFSET+DESC_BASE_MIDDLE, dl + movb _gdt+ES_286_OFFSET+DESC_BASE_HIGH, dh + + mov bx, 2(bx) ! proc no + mov ax, #DS_286_SELECTOR + mov ds, ax + mov ax, #ES_286_SELECTOR + mov es, ax + + eseg mov 0, bx ! proc no. of sender from arg, not msg + mov si, #2 ! src offset is now 2 relative to start of seg + mov di, si ! and destination offset + mov cx, #Msize-1 ! word count + cld ! direction is up + rep + movs ! copy message (except first word) + + pop es + pop ds + pop di + pop si + ret + + +!*===========================================================================* +!* p_portio_setup * +!*===========================================================================* +! The phys_insw, phys_outsw, etc. functions need an address setup routine that +! uses a segment descriptor. +p_portio_setup: + mov _gdt+DS_286_OFFSET+DESC_BASE, ax + movb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE, bl + movb _gdt+DS_286_OFFSET+DESC_BASE_HIGH, bh + xor ax, ax ! ax = 0 = start of segment + mov bx, #DS_286_SELECTOR ! bx = segment selector + ret + + +!*===========================================================================* +!* p_phys_copy * +!*===========================================================================* +p_phys_copy: + cld + pop dx + pop _gdt+DS_286_OFFSET+DESC_BASE + pop ax ! pop source into base of source descriptor + movb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE,al + movb _gdt+DS_286_OFFSET+DESC_BASE_HIGH,ah + pop _gdt+ES_286_OFFSET+DESC_BASE + pop ax ! pop destination into base of dst descriptor + movb _gdt+ES_286_OFFSET+DESC_BASE_MIDDLE,al + movb _gdt+ES_286_OFFSET+DESC_BASE_HIGH,ah + pop cx ! byte count in bx:cx + pop bx + sub sp,#4+4+4 + + push di + push si + push es + push ds + sub si,si ! src offset is now 0 relative to start of seg + mov di,si ! and destination offset + jmp ppc_next + +! It is too much trouble to align the segment bases, so word alignment is hard. +! Avoiding the book-keeping for alignment may be good anyway. + +ppc_large: + push cx + mov cx,#0x8000 ! copy a large chunk of this many words + rep + movs + pop cx + dec bx + pop ds ! update the descriptors + addb _gdt+DS_286_OFFSET+DESC_BASE_MIDDLE,#1 + adcb _gdt+DS_286_OFFSET+DESC_BASE_HIGH,#0 + addb _gdt+ES_286_OFFSET+DESC_BASE_MIDDLE,#1 + adcb _gdt+ES_286_OFFSET+DESC_BASE_HIGH,#0 + push ds +ppc_next: + mov ax,#DS_286_SELECTOR ! (re)load the selectors + mov ds,ax + mov ax,#ES_286_SELECTOR + mov es,ax + test bx,bx + jnz ppc_large + + shr cx,#1 ! word count + rep + movs ! move any leftover words + rcl cx,#1 ! restore old bit 0 + rep + movb ! move any leftover byte + pop ds + pop es + pop si + pop di + jmp (dx) + +!*===========================================================================* +!* p_reset * +!*===========================================================================* +! Reset the system by loading IDT with offset 0 and interrupting. + +p_reset: + lidt idt_zero + int 3 ! anything goes, the 286 will not like it + +!*===========================================================================* +!* idle_task * +!*===========================================================================* +_idle_task: +! This task is called when the system has nothing else to do. The HLT +! instruction puts the processor in a state where it draws minimum power. + mov ax, #halt + push ax + call _level0 ! level0(halt) + pop ax + jmp _idle_task +halt: + sti + hlt + cli + ret + + +!*===========================================================================* +!* p_level0 * +!*===========================================================================* +! PUBLIC void level0(void (*func)(void)) +! Call a function at permission level 0. This allows kernel tasks to do +! things that are only possible at the most privileged CPU level. +! +p_level0: + mov bx, sp + mov ax, 2(bx) + mov _level0_func, ax + int LEVEL0_VECTOR + ret + + +!*===========================================================================* +!* data * +!*===========================================================================* + .data +patch_table: ! pairs (old function, new function) + .data2 _int86, p_int86 + .data2 _cp_mess, p_cp_mess + .data2 _phys_copy, p_phys_copy + .data2 portio_setup, p_portio_setup + .data2 _reset, p_reset + .data2 _level0, p_level0 + .data2 _restart, p_restart ! in mpx file + .data2 save, p_save ! in mpx file +end_patch_table: ! end of table + +idt_vectors: ! limit and base of real mode interrupt vectors + .data2 0x3FF +idt_zero: ! zero limit IDT to cause a processor shutdown + .data2 0, 0, 0 + + .bss +save_sp: ! place to put sp when switching to real mode + .space 2 +msw: ! saved real mode machine status word + .space 2 diff --git a/kernel/klibc.c b/kernel/klibc.c new file mode 100644 index 000000000..745f171e8 --- /dev/null +++ b/kernel/klibc.c @@ -0,0 +1,267 @@ +/* This file contains simplified versions of the standard libary functions for + * use with the kernel. This way the kernel sources remain separate from user + * sources and can easily be verified. Note that the functionality provided + * can be slightly different. + * March 2005, Jorrit N. Herder. + * Entrypoints into this file: + * katoi: convert string to integer + * kmemcpy: copy n bytes from pointer p1 to pointer p2 + * kmemset: set n bytes to c starting at pointer p + * kprintf: printf for the kernel (see working below) + * kstrcmp: lexicographical comparison of two strings + * kstrlen: get number of non-null characters in string + * kstrncpy: copy string and pad or copy up to n chars + * kstrtoulb: convert string to unsigned long value + * + * This file contains the routines that take care of kernel messages, i.e., + * diagnostic output within the kernel. Kernel messages are not directly + * displayed on the console, because this must be done by the TTY driver. + * Instead, the kernel accumulates characters in a buffer and notifies the + * TTY driver when a new message is ready. + */ + +#include "kernel.h" +#include <minix/com.h> /* need TTY process number */ + +#define isdigit(c) ((unsigned) ((c) - '0') < (unsigned) 10) +#define END_OF_KMESS -1 +FORWARD _PROTOTYPE(void kputc, (int c)); + + +/*=========================================================================* + * katoi * + *=========================================================================*/ +PUBLIC int katoi(register const char *s) +{ + int value = 0; /* default value */ + int sign = 1; /* assume positive */ + + while(*s == ' ') s++; /* skip spaces */ + if (*s == '-') { sign = -1; s++; } /* detect sign */ + while(isdigit(*s)) /* get integer */ + value = value*10 + (*s++) -'0'; + + return(sign * value); /* return result */ +} + + +/*=========================================================================* + * kmemcpy * + *=========================================================================*/ +PUBLIC void *kmemcpy(void *s1, const void *s2, register size_t n) +{ + register char *p1 = s1; + register const char *p2 = s2; + + while (n-- > 0) + *p1++ = *p2++; + return s1; +} + + +/*=========================================================================* + * kmemset * + *=========================================================================*/ +PUBLIC void *kmemset(void *s, register int c, register size_t n) +{ + register char *s1 = s; + if (n++>0) { /* optimized for speed */ + while (--n > 0) + *s1++ = c; + } + return s; +} + + +/*===========================================================================* + * kprintf * + *===========================================================================*/ +PUBLIC void kprintf(fmt, arg) +const char *fmt; /* format string to be printed */ +karg_t arg; /* argument for format string */ +{ + int c; /* next character in fmt */ + unsigned long u; /* hold number argument */ + int base; /* base of number arg */ + int negative = 0; /* print minus sign */ + static char x2c[] = "0123456789ABCDEF"; /* nr conversion table */ + char ascii[8 * sizeof(long) / 3 + 2]; /* string for ascii number */ + char *s = NULL; /* string to be printed */ + + while((c=*fmt++) != 0) { + + if (c == '%') { /* expect format '%key' */ + switch(c = *fmt++) { /* determine what to do */ + + /* Known keys are %d, %u, %x, %s, and %%. This is easily extended + * with number types like %b and %o by providing a different base. + * Number type keys don't set a string to 's', but use the general + * conversion after the switch statement. + */ + case 'd': /* output decimal */ + u = arg < 0 ? -arg : arg; + if (arg < 0) negative = 1; + base = 10; + break; + case 'u': /* output unsigned long */ + u = (unsigned long) arg; + base = 10; + break; + case 'x': /* output hexadecimal */ + u = (unsigned long) arg; + base = 0x10; + break; + case 's': /* output string */ + if ((s=(char *)arg) == NULL) + s = "(null)"; + break; + case '%': /* output percent */ + s = "%"; + break; + + /* Unrecognized key. */ + default: /* echo back %key */ + s = "%?"; + s[1] = c; /* set unknown key */ + } + + /* Assume a number if no string is set. Convert to ascii. */ + if (s == NULL) { + s = ascii + sizeof(ascii)-1; + *s = 0; + do { *--s = x2c[(u % base)]; } /* work backwards */ + while ((u /= base) > 0); + } + + /* This is where the actual output for format "%key" is done. */ + if (negative) kputc('-'); /* print sign if negative */ + while(*s != 0) { kputc(*s++); } /* print string/ number */ + } + else { + kputc(c); /* print and continue */ + } + } + kputc(END_OF_KMESS); /* terminate output */ +} + + +/*===========================================================================* + * kputc * + *===========================================================================*/ +PRIVATE void kputc(c) +int c; /* character to append */ +{ +/* Accumulate a single character for a kernel message. Send a notification + * the to TTY driver if the buffer if a END_OF_KMESS is encountered. + */ + if (c != END_OF_KMESS) { + kmess.km_buf[kmess.km_next] = c; /* put normal char in buffer */ + if (kmess.km_size < KMESS_BUF_SIZE) + kmess.km_size += 1; + kmess.km_next = (kmess.km_next + 1) % KMESS_BUF_SIZE; + } else { + notify(TTY, NEW_KMESS); /* let TTY display the message */ + } +} + + +/*=========================================================================* + * kstrlen * + *=========================================================================*/ +PUBLIC size_t kstrlen(const char *org) +{ + register const char *s = org; + while (*s++) + /* EMPTY */ ; + return --s - org; +} + + +/*=========================================================================* + * kstrcmp * + *=========================================================================*/ +int kstrcmp(register const char *s1, register const char *s2) +{ + while (*s1 == *s2++) { + if (*s1++ == '\0') return 0; + } + if (*s1 == '\0') return -1; + if (*--s2 == '\0') return 1; + return (unsigned char) *s1 - (unsigned char) *s2; +} + + +/*=========================================================================* + * kstrncmp * + *=========================================================================*/ +PUBLIC int kstrncmp(register const char *s1, register const char *s2, register size_t n) +{ + while (n > 0 && *s1 == *s2++) { + if (*s1++ == '\0') return 0; + n--; + } + if (n > 0) { + if (*s1 == '\0') return -1; + if (*--s2 == '\0') return 1; + return (unsigned char) *s1 - (unsigned char) *s2; + } + return 0; +} + + +/*=========================================================================* + * kstrncpy * + *=========================================================================*/ +PUBLIC char *kstrncpy(char *ret, register const char *s2, register size_t n) +{ + register char *s1 = ret; + while((n-- > 0) && (*s1++ = *s2++)) /* copy up to n chars */ + /* EMPTY */ ; + while(n-- > 0) /* possibly pad target */ + *s1++ = '\0'; + return ret; +} + + +/*=========================================================================* + * kstrtoul * + *=========================================================================*/ +PUBLIC unsigned long kstrtoul(strptr, endptr, base) +const char *strptr; /* pointer to string to be parsed */ +char ** const endptr; /* store pointer to end here */ +int base; +{ +/* A simplified version of strtoul() for the kernel to prevent including the + * one in the ASNI library. No whitespaces are skipped, the numeric value is + * expected at the start of 'string'. + */ + register unsigned long val = 0; + register int c; + register unsigned int v; + int overflow = 0; + + /* Get rid of 0x or 0X for hexidecimal values. */ + if (base==16 && *strptr=='0' && (*++strptr=='x' || *strptr=='X')) + strptr++; + + /* Now parse the actual unsigned long number. */ + for (;;) { + c = *strptr; + if ('0' <= c && c <= '9') v = c - '0'; + else if ('a' <= c && c <= 'z') v = c - 'a' + 0xa; + else if ('A' <= c && c <= 'Z') v = c - 'A' + 0xA; + else break; /* end of number */ + if (v >= base) break; /* end of number */ + if (val > (ULONG_MAX - v) / base) overflow = 1; + val = (val*base) + v; + strptr++; + } + + /* Tell caller where parsing ended unless a NULL pointer was passed. */ + if (endptr) *endptr = (char *) strptr; + + /* Done, return parsed value or maximum value on overflow. */ + return (overflow) ? ULONG_MAX : val; +} + + diff --git a/kernel/main.c b/kernel/main.c new file mode 100755 index 000000000..66c182a26 --- /dev/null +++ b/kernel/main.c @@ -0,0 +1,354 @@ +/* This file contains the main program of MINIX as well as its shutdown code. + * The routine main() initializes the system and starts the ball rolling by + * setting up the process table, interrupt vectors, and scheduling each task + * to run to initialize itself. + * The routine prepare_shutdown() tries to cleanly shuts down MINIX by running + * the stop_sequence() to notify all system services and allowing them some + * time to finalize. In case of an exception(), the stop sequence is skipped. + * + * The entries into this file are: + * main: MINIX main program + * prepare_shutdown: prepare to take MINIX down + * stop_sequence: take down all system services + * + * Changes: + * Nov 24, 2004 simplified main() with system image (Jorrit N. Herder) + * Oct 21, 2004 moved copyright to announce() (Jorrit N. Herder) + * Sep 30, 2004 moved mem_init() to this file (Jorrit N. Herder) + * Sep 04, 2004 created stop_sequence() to cleanup (Jorrit N. Herder) + * Aug 20, 2004 split wreboot() and shutdown() (Jorrit N. Herder) + * Jun 15, 2004 moved wreboot() to this file (Jorrit N. Herder) + */ +#include "kernel.h" +#include <signal.h> +#include <unistd.h> +#include <a.out.h> +#include <minix/callnr.h> +#include <minix/com.h> +#include "proc.h" +#include "sendmask.h" + +/* Prototype declarations for PRIVATE function. */ +FORWARD _PROTOTYPE( void announce, (void)); /* display user message */ +#define STOP_TICKS (5*HZ) /* time allowed to stop */ + +/*===========================================================================* + * main * + *===========================================================================*/ +PUBLIC void main() +{ +/* Start the ball rolling. */ + + register struct proc *rp; + register int i; + int hdrindex; /* index to array of a.out headers */ + phys_clicks text_base; + vir_clicks text_clicks; + vir_clicks data_clicks; + reg_t ktsb; /* kernel task stack base */ + struct memory *memp; + struct system_image *ttp; + struct exec e_hdr; /* for a copy of an a.out header */ + + /* Initialize the interrupt controller. */ + intr_init(1); + + /* Make free memory list from memory sizes passed by boot monitor. */ + mem_init(); + + /* Clear the process table. Anounce each slot as empty and + * set up mappings for proc_addr() and proc_number() macros. + */ + for (rp = BEG_PROC_ADDR, i = -NR_TASKS; rp < END_PROC_ADDR; ++rp, ++i) { + rp->p_type = P_NONE; /* isemptyp() tests on this */ + rp->p_nr = i; /* proc number from ptr */ + (pproc_addr + NR_TASKS)[i] = rp; /* proc ptr from number */ + } + + /* Set up proc table entries for tasks and servers. The stacks of the + * kernel tasks are initialized to an array in data space. The stacks + * of the servers have been added to the data segment by the monitor, so + * the stack pointer is set to the end of the data segment. All the + * processes are in low memory on the 8086. On the 386 only the kernel + * is in low memory, the rest is loaded in extended memory. + */ + + /* Task stacks. */ + ktsb = (reg_t) t_stack; + + for (i=0; i < IMAGE_SIZE; ++i) { + ttp = &image[i]; /* t's task attributes */ + rp = proc_addr(ttp->proc_nr); /* t's process slot */ + kstrncpy(rp->p_name, ttp->name, PROC_NAME_LEN); /* set name */ + rp->p_type = ttp->type; /* type of process */ + rp->p_priority = ttp->priority; /* scheduling priority */ + rp->p_sendmask = ttp->sendmask; /* sendmask protection */ + if (i-NR_TASKS < 0) { /* part of the kernel? */ + if (ttp->stksize > 0) { /* HARDWARE stack size is 0 */ + rp->p_stguard = (reg_t *) ktsb; + *rp->p_stguard = STACK_GUARD; + } + ktsb += ttp->stksize; /* point to high end of stack */ + rp->p_reg.sp = ktsb; /* this task's initial stack ptr */ + text_base = code_base >> CLICK_SHIFT; + /* processes that are in the kernel */ + hdrindex = 0; /* all use the first a.out header */ + } else { + hdrindex = 1 + i-NR_TASKS; /* drivers, servers, INIT follow */ + } + + /* The bootstrap loader created an array of the a.out headers at + * absolute address 'aout'. Get one element to e_hdr. + */ + phys_copy(aout + hdrindex * A_MINHDR, vir2phys(&e_hdr), + (phys_bytes) A_MINHDR); + /* Convert addresses to clicks and build process memory map */ + text_base = e_hdr.a_syms >> CLICK_SHIFT; + text_clicks = (e_hdr.a_text + CLICK_SIZE-1) >> CLICK_SHIFT; + if (!(e_hdr.a_flags & A_SEP)) text_clicks = 0; /* Common I&D */ + data_clicks = (e_hdr.a_total + CLICK_SIZE-1) >> CLICK_SHIFT; + rp->p_memmap[T].mem_phys = text_base; + rp->p_memmap[T].mem_len = text_clicks; + rp->p_memmap[D].mem_phys = text_base + text_clicks; + rp->p_memmap[D].mem_len = data_clicks; + rp->p_memmap[S].mem_phys = text_base + text_clicks + data_clicks; + rp->p_memmap[S].mem_vir = data_clicks; /* empty - stack is in data */ + + /* Remove server memory from the free memory list. The boot monitor + * promises to put processes at the start of memory chunks. The + * tasks all use same base address, so only the first task changes + * the memory lists. The servers and init have their own memory + * spaces and their memory will be removed from the list. + */ + for (memp = mem; memp < &mem[NR_MEMS]; memp++) { + if (memp->base == text_base) { + memp->base += text_clicks + data_clicks; + memp->size -= text_clicks + data_clicks; + } + } + + /* Set initial register values. The processor status word for tasks + * is different from that of other processes because tasks can + * access I/O; this is not allowed to less-privileged processes + */ + rp->p_reg.pc = (reg_t) ttp->initial_pc; + rp->p_reg.psw = (isidlep(rp)||istaskp(rp)) ? INIT_TASK_PSW : INIT_PSW; + + /* Initialize the server stack pointer. Take it down one word + * to give crtso.s something to use as "argc". + */ + if (i-NR_TASKS >= 0) { + rp->p_reg.sp = (rp->p_memmap[S].mem_vir + + rp->p_memmap[S].mem_len) << CLICK_SHIFT; + rp->p_reg.sp -= sizeof(reg_t); + } + + /* Set ready. The HARDWARE task is never ready. */ + if (rp->p_nr != HARDWARE) lock_ready(rp); + rp->p_flags = 0; + + /* Code and data segments must be allocated in protected mode. */ + alloc_segments(rp); + } + bill_ptr = proc_addr(IDLE); /* it has to point somewhere */ + + /* This actually is not needed, because ready() already set 'proc_ptr' to + * first task that was announced ready. + */ + lock_pick_proc(); + + /* Expect an image of the root FS to be loaded into memory as well. The + * root FS image is located directly after the programs. + */ + hdrindex ++; + phys_copy(aout+hdrindex * A_MINHDR, vir2phys(&e_hdr), (phys_bytes) A_MINHDR); + kprintf("Root FS image found: %s\n", (e_hdr.a_flags&A_IMG)? "yes" : "no"); + if (e_hdr.a_flags & A_IMG) { + kprintf("Size of root FS: %u, ", e_hdr.a_data); + kprintf("base : %u, ", e_hdr.a_syms); + kprintf("header size : %u, ", e_hdr.a_hdrlen); + rp = proc_addr(MEMORY); + rp->p_farmem[0].mem_phys = e_hdr.a_syms; + rp->p_farmem[0].mem_len = e_hdr.a_data; + + /* Remove from free list, to prevent being overwritten. */ + text_base = e_hdr.a_syms >> CLICK_SHIFT; + text_clicks = (e_hdr.a_text + CLICK_SIZE-1) >> CLICK_SHIFT; + if (!(e_hdr.a_flags & A_SEP)) text_clicks = 0; /* Common I&D */ + data_clicks = (e_hdr.a_total + CLICK_SIZE-1) >> CLICK_SHIFT; + for (memp = mem; memp < &mem[NR_MEMS]; memp++) { + if (memp->base == text_base) { + kprintf("Memory removed from free list.\n", NO_ARG); + memp->base += text_clicks + data_clicks; + memp->size -= text_clicks + data_clicks; + } + } + } + + + /* MINIX and all system services have been loaded. Announce to the user. + * Then go to the assembly code to start running the current process. + */ + announce(); + restart(); +} + + + +/*==========================================================================* + * announce * + *==========================================================================*/ +PRIVATE void announce(void) +{ + /* Display the MINIX startup banner. */ + kprintf("MINIX %s Copyright 2001 Prentice-Hall, Inc.\n", + karg(OS_RELEASE "." OS_VERSION)); + +#if (CHIP == INTEL) + /* Real mode, or 16/32-bit protected mode? */ + kprintf("Executing in %s mode\n\n", + protected_mode ? karg("32-bit protected") : karg("real")); +#endif +} + + +/*==========================================================================* + * prepare_shutdown * + *==========================================================================*/ +PUBLIC void prepare_shutdown(how) +int how; /* 0 = halt, 1 = reboot, 2 = panic!, ... */ +{ +/* This function prepares to shutdown MINIX. It uses a global flag to make + * sure it is only executed once. Unless a CPU exception occurred, the + * stop_sequence() is started. + */ + if (shutting_down) + return; + + /* Show debugging dumps on panics. Make sure that the TTY task is still + * available to handle them. This is done with help of a non-blocking send. + * We rely on TTY to call sys_abort() when it is done with the dumps. + */ + if (how == RBT_PANIC) { + message m; + m.m_type = PANIC_DUMPS; + if (nb_send(TTY, &m) == OK) /* don't block if TTY isn't ready */ + return; /* await sys_abort() from TTY */ + } + + /* The TTY expects two HARD_STOP notifications. One to switch to the + * primary console for stop sequence output, and one to actually exit. + */ + notify(TTY, HARD_STOP); /* let TTY switch to console 0 */ + + /* Run the stop sequence. The timer argument passes the shutdown status. + * The stop sequence is skipped for fatal CPU exceptions. + */ + shutting_down = TRUE; /* flag for sys_exit() */ + tmr_arg(&shutdown_timer)->ta_int = how; /* pass how in timer */ + if (skip_stop_sequence) { /* set in exception() */ + kprintf("\nAn exception occured; skipping stop sequence.\n", NO_ARG); + shutdown(&shutdown_timer); /* TTY isn't scheduled */ + } else { + kprintf("\nNotifying system services about MINIX shutdown.\n", NO_ARG); + stop_sequence(&shutdown_timer); + } +} + +/*==========================================================================* + * stop_sequence * + *==========================================================================*/ +PUBLIC void stop_sequence(tp) +timer_t *tp; +{ +/* Try to cleanly stop all system services before shutting down. For each + * process type, all processes are notified and given STOP_TICKS to cleanly + * shutdown. The notification order is servers, drivers, tasks. The variable + * 'shutdown_process' is set globally to indicate the process next to stop + * so that the stop sequence can directly continue if it has exited. Only if + * stop sequence has finished, MINIX is brought down. + */ + static int level = P_SERVER; /* start at the highest level */ + static struct proc *p = NIL_PROC; /* next process to stop */ + static char *types[] = {"task","system","driver","server","user"}; + + /* See if the last process' shutdown was successfull. Else, force exit. */ + if (p != NIL_PROC) { + kprintf("[%s]\n", isalivep(p) ? "FAILED" : "OK"); + if (isalivep(p)) + clear_proc(p->p_nr); /* now force process to exit */ + } + + /* Find the next process that must be stopped. Continue where last search + * ended or start at begin. Possibly go to next level while searching. If + * the last level is completely searched, shutdown MINIX. Processes are + * stopped in the order of dependencies, that is, from the highest level to + * the lowest level so that, for example, the file system can still rely on + * device drivers to cleanly shutdown. + */ + if (p == NIL_PROC) p = BEG_PROC_ADDR; + while (TRUE) { + if (isalivep(p) && p->p_type == level) { /* found a process */ + kprintf("- Stopping %s ", p->p_name); + kprintf("%s ... ", types[p->p_type]); + shutdown_process = p; /* directly continue if exited */ + notify(proc_number(p), HARD_STOP); + set_timer(tp, get_uptime()+STOP_TICKS, stop_sequence); + return; /* allow the process to shut down */ + } + p++; /* proceed to next process */ + if (p >= END_PROC_ADDR) { /* proceed to next level */ + p = BEG_PROC_ADDR; + level = level - 1; + if (level == P_TASK) { /* done; tasks must remain alive */ + set_timer(tp, get_uptime()+HZ, shutdown); /* shutdown MINIX */ + return; /* user can inspect output */ + } + } + } +} + +/*==========================================================================* + * shutdown * + *==========================================================================*/ +PUBLIC void shutdown(tp) +timer_t *tp; +{ +/* This function is called from prepare_shutdown or stop_sequence to bring + * down MINIX. How to shutdown is in the argument: RBT_REBOOT, RBT_HALT, + * RBT_RESET. + */ + int quiet, code; + static u16_t magic = STOP_MEM_CHECK; + int how = tmr_arg(tp)->ta_int; + + /* Now mask all interrupts, including the clock, and stop the clock. */ + outb(INT_CTLMASK, ~0); + clock_stop(); + + if (mon_return && how != RBT_RESET) { + /* Reinitialize the interrupt controllers to the BIOS defaults. */ + intr_init(0); + outb(INT_CTLMASK, 0); + outb(INT2_CTLMASK, 0); + + /* Return to the boot monitor. Set the program for the boot monitor. + * For RBT_MONITOR, the MM has provided the program. + */ + if (how == RBT_HALT) { + phys_copy(vir2phys(" "), mon_params, 2); + } else if (how == RBT_REBOOT) { + phys_copy(vir2phys("delay;boot"), mon_params, 11); + } + level0(monitor); + } + + /* Stop BIOS memory test. */ + phys_copy(vir2phys(&magic), (phys_bytes) ADR_MEM_CHECK, LEN_MEM_CHECK); + + /* Reset the system by jumping to the reset address (real mode), or by + * forcing a processor shutdown (protected mode). + */ + level0(reset); +} + diff --git a/kernel/memory.c b/kernel/memory.c new file mode 100755 index 000000000..e07fb7165 --- /dev/null +++ b/kernel/memory.c @@ -0,0 +1,120 @@ +/* Entry points into this file: + * mem_init: create a list a free memory + * alloc_segments: allocate segments for 8088 or higher processor + */ + +#include "kernel.h" +#include "protect.h" +#include "proc.h" + + +#if (CHIP == INTEL) + +/* In real mode only 1M can be addressed, and in 16-bit protected we can go + * no further than we can count in clicks. (The 286 is further limited by + * its 24 bit address bus, but we can assume in that case that no more than + * 16M memory is reported by the BIOS.) + */ +#define MAX_REAL 0x00100000L +#define MAX_16BIT (0xFFF0L << CLICK_SHIFT) + +/*=========================================================================* + * mem_init * + *=========================================================================*/ +PUBLIC void mem_init() +{ +/* Initialize the free memory list from the 'memory' boot variable. Translate + * the byte offsets and sizes in this list to clicks, properly truncated. Also + * make sure that we don't exceed the maximum address space of the 286 or the + * 8086, i.e. when running in 16-bit protected mode or real mode. + */ + long base, size, limit; + char *s, *end; /* use to parse boot variable */ + int i; + struct memory *memp; +#if _WORD_SIZE == 2 + unsigned long max_address; +#endif + + /* The available memory is determined by MINIX' boot loader as a list of + * (base:size)-pairs in boothead.s. The 'memory' boot variable is set in + * in boot.s. The format is "b0:s0,b1:s1,b2:s2", where b0:s0 is low mem, + * b1:s1 is mem between 1M and 16M, b2:s2 is mem above 16M. Pairs b1:s1 + * and b2:s2 are combined if the memory is adjacent. + */ + s = getkenv("memory"); /* get memory boot variable */ + tot_mem_size = 0; + for (i = 0; i < NR_MEMS; i++) { + memp = &mem[i]; /* result is stored here */ + base = size = 0; + if (*s != 0) { /* end of boot variable */ + /* Expect base to be read (end != s) and ':' as next char. */ + base = kstrtoul(s, &end, 0x10); /* get number */ + if (end != s && *end == ':') s = ++end; /* skip ':' */ + else *s=0; /* fake end for next; should not happen */ + /* Expect size to be read and skip ',', unless at end. */ + size = kstrtoul(s, &end, 0x10); /* get number */ + if (end != s && *end == ',') s = ++end; /* skip ',' */ + else if (end != s && *end == 0) s = end; /* end found */ + else *s=0; /* fake end for next; should not happen */ + } + limit = base + size; +#if _WORD_SIZE == 2 + max_address = protected_mode ? MAX_16BIT : MAX_REAL; + if (limit > max_address) limit = max_address; +#endif + base = (base + CLICK_SIZE-1) & ~(long)(CLICK_SIZE-1); + limit &= ~(long)(CLICK_SIZE-1); + if (limit <= base) continue; + memp->base = base >> CLICK_SHIFT; + memp->size = (limit - base) >> CLICK_SHIFT; + tot_mem_size += memp->size; + } +} + +/*==========================================================================* + * alloc_segments * + *==========================================================================*/ +PUBLIC void alloc_segments(rp) +register struct proc *rp; +{ +/* This is called at system initialization from main() and by do_newmap(). + * The code has a separate function because of all hardware-dependencies. + * Note that IDLE is part of the kernel and gets TASK_PRIVILEGE here. + */ + phys_bytes code_bytes; + phys_bytes data_bytes; + int privilege; + + if (protected_mode) { + data_bytes = (phys_bytes) (rp->p_memmap[S].mem_vir + + rp->p_memmap[S].mem_len) << CLICK_SHIFT; + if (rp->p_memmap[T].mem_len == 0) + code_bytes = data_bytes; /* common I&D, poor protect */ + else + code_bytes = (phys_bytes) rp->p_memmap[T].mem_len << CLICK_SHIFT; + privilege = (isidlep(rp) || istaskp(rp)) ? + TASK_PRIVILEGE : USER_PRIVILEGE; + init_codeseg(&rp->p_ldt[CS_LDT_INDEX], + (phys_bytes) rp->p_memmap[T].mem_phys << CLICK_SHIFT, + code_bytes, privilege); + init_dataseg(&rp->p_ldt[DS_LDT_INDEX], + (phys_bytes) rp->p_memmap[D].mem_phys << CLICK_SHIFT, + data_bytes, privilege); + rp->p_reg.cs = (CS_LDT_INDEX * DESC_SIZE) | TI | privilege; +#if _WORD_SIZE == 4 + rp->p_reg.gs = + rp->p_reg.fs = +#endif + rp->p_reg.ss = + rp->p_reg.es = + rp->p_reg.ds = (DS_LDT_INDEX*DESC_SIZE) | TI | privilege; + } else { + rp->p_reg.cs = click_to_hclick(rp->p_memmap[T].mem_phys); + rp->p_reg.ss = + rp->p_reg.es = + rp->p_reg.ds = click_to_hclick(rp->p_memmap[D].mem_phys); + } +} +#endif /* (CHIP == INTEL) */ + diff --git a/kernel/misc.c b/kernel/misc.c new file mode 100755 index 000000000..10a9746e2 --- /dev/null +++ b/kernel/misc.c @@ -0,0 +1,74 @@ +/* This file contains a collection of miscellaneous procedures: + * panic abort MINIX due to a fatal error + * bad_assertion for debugging + * bad_compare for debugging + * + * Changes: + * Oct 04, 2004 moved panic() to this file (Jorrit N. Herder) + * Sep 30, 2004 removed mem_init(), env_parse to lib (Jorrit N. Herder) + */ + +#include "kernel.h" +#include "assert.h" +#include <unistd.h> +#include <minix/com.h> + + +/*===========================================================================* + * panic * + *===========================================================================*/ +PUBLIC void panic(s,n) +_CONST char *s; +int n; +{ +/* The system has run aground of a fatal error. Terminate execution. + * If the panic originated in MM or FS, the string will be empty and the + * file system already syncked. If the panic originates in the kernel, we are + * kind of stuck. + */ + static int panicking = 0; + if (panicking ++) /* prevent recursive panics */ + return; + + if (s != NULL) { + kprintf("\nKernel panic: %s", karg(s)); + if (n != NO_NUM) kprintf(" %d", n); + kprintf("\n",NO_ARG); + } + prepare_shutdown(RBT_PANIC); +} + + +#if !NDEBUG +/*=========================================================================* + * bad_assertion * + *=========================================================================*/ +PUBLIC void bad_assertion(file, line, what) +char *file; +int line; +char *what; +{ + kprintf("panic at %s", karg(file)); + kprintf(" (line %d): ", line); + kprintf("assertion \"%s\" failed.\n", karg(what)); + panic(NULL, NO_NUM); +} + +/*=========================================================================* + * bad_compare * + *=========================================================================*/ +PUBLIC void bad_compare(file, line, lhs, what, rhs) +char *file; +int line; +int lhs; +char *what; +int rhs; +{ + kprintf("panic at %s", karg(file)); + kprintf(" (line %d): ", line); + kprintf("compare (%d)", lhs); + kprintf(" %s ", karg(what)); + kprintf("(%d) failed.\n", rhs); + panic(NULL, NO_NUM); +} +#endif /* !NDEBUG */ diff --git a/kernel/mpx.s b/kernel/mpx.s new file mode 100755 index 000000000..d6006c189 --- /dev/null +++ b/kernel/mpx.s @@ -0,0 +1,9 @@ +# +! Chooses between the 8086 and 386 versions of the Minix startup code. + +#include <minix/config.h> +#if _WORD_SIZE == 2 +#include "mpx88.s" +#else +#include "mpx386.s" +#endif diff --git a/kernel/mpx386.s b/kernel/mpx386.s new file mode 100755 index 000000000..0cec0784c --- /dev/null +++ b/kernel/mpx386.s @@ -0,0 +1,553 @@ +# +! This file, mpx386.s, is included by mpx.s when Minix is compiled for +! 32-bit Intel CPUs. The alternative mpx88.s is compiled for 16-bit CPUs. +! +! This contains the assembler startup code for Minix and the 32-bit +! interrupt handlers. It cooperates with start.c to set up a good +! environment for main(). + +! This file is part of the lowest layer of the MINIX kernel. The other part +! is "proc.c". The lowest layer does process switching and message handling. + +! Every transition to the kernel goes through this file. Transitions are +! caused by sending/receiving messages and by most interrupts. (RS232 +! interrupts may be handled in the file "rs2.s" and then they rarely enter +! the kernel.) + +! Transitions to the kernel may be nested. The initial entry may be with a +! system call, exception or hardware interrupt; reentries may only be made +! by hardware interrupts. The count of reentries is kept in "k_reenter". +! It is important for deciding whether to switch to the kernel stack and +! for protecting the message passing code in "proc.c". + +! For the message passing trap, most of the machine state is saved in the +! proc table. (Some of the registers need not be saved.) Then the stack is +! switched to "k_stack", and interrupts are reenabled. Finally, the system +! call handler (in C) is called. When it returns, interrupts are disabled +! again and the code falls into the restart routine, to finish off held-up +! interrupts and run the process or task whose pointer is in "proc_ptr". + +! Hardware interrupt handlers do the same, except (1) The entire state must +! be saved. (2) There are too many handlers to do this inline, so the save +! routine is called. A few cycles are saved by pushing the address of the +! appropiate restart routine for a return later. (3) A stack switch is +! avoided when the stack is already switched. (4) The (master) 8259 interrupt +! controller is reenabled centrally in save(). (5) Each interrupt handler +! masks its interrupt line using the 8259 before enabling (other unmasked) +! interrupts, and unmasks it after servicing the interrupt. This limits the +! nest level to the number of lines and protects the handler from itself. + +! For communication with the boot monitor at startup time some constant +! data are compiled into the beginning of the text segment. This facilitates +! reading the data at the start of the boot process, since only the first +! sector of the file needs to be read. + +! Some data storage is also allocated at the end of this file. This data +! will be at the start of the data segment of the kernel and will be read +! and modified by the boot monitor before the kernel starts. + +! sections + +.sect .text +begtext: +.sect .rom +begrom: +.sect .data +begdata: +.sect .bss +begbss: + +#include <minix/config.h> +#include <minix/const.h> +#include <minix/com.h> +#include "const.h" +#include "protect.h" +#include "sconst.h" + +/* Selected 386 tss offsets. */ +#define TSS3_S_SP0 4 + +! Exported functions +! Note: in assembly language the .define statement applied to a function name +! is loosely equivalent to a prototype in C code -- it makes it possible to +! link to an entity declared in the assembly code but does not create +! the entity. + +.define _restart +.define save + +.define _divide_error +.define _single_step_exception +.define _nmi +.define _breakpoint_exception +.define _overflow +.define _bounds_check +.define _inval_opcode +.define _copr_not_available +.define _double_fault +.define _copr_seg_overrun +.define _inval_tss +.define _segment_not_present +.define _stack_exception +.define _general_protection +.define _page_fault +.define _copr_error + +.define _hwint00 ! handlers for hardware interrupts +.define _hwint01 +.define _hwint02 +.define _hwint03 +.define _hwint04 +.define _hwint05 +.define _hwint06 +.define _hwint07 +.define _hwint08 +.define _hwint09 +.define _hwint10 +.define _hwint11 +.define _hwint12 +.define _hwint13 +.define _hwint14 +.define _hwint15 + +.define _s_call +.define _p_s_call +.define _level0_call + +! Exported variables. +.define begbss +.define begdata + +.sect .text +!*===========================================================================* +!* MINIX * +!*===========================================================================* +MINIX: ! this is the entry point for the MINIX kernel + jmp over_flags ! skip over the next few bytes + .data2 CLICK_SHIFT ! for the monitor: memory granularity +flags: + .data2 0x01FD ! boot monitor flags: + ! call in 386 mode, make bss, make stack, + ! load high, don`t patch, will return, + ! uses generic INT, memory vector, + ! new boot code return + nop ! extra byte to sync up disassembler +over_flags: + +! Set up a C stack frame on the monitor stack. (The monitor sets cs and ds +! right. The ss descriptor still references the monitor data segment.) + movzx esp, sp ! monitor stack is a 16 bit stack + push ebp + mov ebp, esp + push esi + push edi + cmp 4(ebp), 0 ! monitor return vector is + jz noret ! nonzero if return possible + inc (_mon_return) +noret: mov (_mon_sp), esp ! save stack pointer for later return + +! Copy the monitor global descriptor table to the address space of kernel and +! switch over to it. Prot_init() can then update it with immediate effect. + + sgdt (_gdt+GDT_SELECTOR) ! get the monitor gdtr + mov esi, (_gdt+GDT_SELECTOR+2) ! absolute address of GDT + mov ebx, _gdt ! address of kernel GDT + mov ecx, 8*8 ! copying eight descriptors +copygdt: + eseg movb al, (esi) + movb (ebx), al + inc esi + inc ebx + loop copygdt + mov eax, (_gdt+DS_SELECTOR+2) ! base of kernel data + and eax, 0x00FFFFFF ! only 24 bits + add eax, _gdt ! eax = vir2phys(gdt) + mov (_gdt+GDT_SELECTOR+2), eax ! set base of GDT + lgdt (_gdt+GDT_SELECTOR) ! switch over to kernel GDT + +! Locate boot parameters, set up kernel segment registers and stack. + mov ebx, 8(ebp) ! boot parameters offset + mov edx, 12(ebp) ! boot parameters length + mov eax, 16(ebp) ! address of a.out headers + mov (_aout), eax + mov ax, ds ! kernel data + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + mov esp, k_stktop ! set sp to point to the top of kernel stack + +! Call C startup code to set up a proper environment to run main(). + push edx + push ebx + push SS_SELECTOR + push DS_SELECTOR + push CS_SELECTOR + call _cstart ! cstart(cs, ds, mds, parmoff, parmlen) + add esp, 5*4 + +! Reload gdtr, idtr and the segment registers to global descriptor table set +! up by prot_init(). + + lgdt (_gdt+GDT_SELECTOR) + lidt (_gdt+IDT_SELECTOR) + + jmpf CS_SELECTOR:csinit +csinit: + o16 mov ax, DS_SELECTOR + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + o16 mov ax, TSS_SELECTOR ! no other TSS is used + ltr ax + push 0 ! set flags to known good state + popf ! esp, clear nested task and int enable + + jmp _main ! main() + + +!*===========================================================================* +!* interrupt handlers * +!* interrupt handlers for 386 32-bit protected mode * +!*===========================================================================* + +!*===========================================================================* +!* hwint00 - 07 * +!*===========================================================================* +! Note this is a macro, it just looks like a subroutine. +#define hwint_master(irq) \ + call save /* save interrupted process state */;\ + inb INT_CTLMASK ;\ + orb al, [1<<irq] ;\ + outb INT_CTLMASK /* disable the irq */;\ + movb al, ENABLE ;\ + outb INT_CTL /* reenable master 8259 */;\ + push (_irq_hooks+4*irq) /* irq_hooks[irq] */;\ + sti /* enable interrupts */;\ + call _intr_handle /* intr_handle(irq_hooks[irq]) */;\ + cli /* disable interrupts */;\ + pop ecx ;\ + cmp (_irq_actids+4*irq), 0 /* interrupt still active? */;\ + jnz 0f ;\ + inb INT_CTLMASK ;\ + andb al, ~[1<<irq] ;\ + outb INT_CTLMASK /* enable the irq */;\ +0: ret /* restart (another) process */ + +! Each of these entry points is an expansion of the hwint_master macro + .align 16 +_hwint00: ! Interrupt routine for irq 0 (the clock). + hwint_master(0) + + .align 16 +_hwint01: ! Interrupt routine for irq 1 (keyboard) + hwint_master(1) + + .align 16 +_hwint02: ! Interrupt routine for irq 2 (cascade!) + hwint_master(2) + + .align 16 +_hwint03: ! Interrupt routine for irq 3 (second serial) + hwint_master(3) + + .align 16 +_hwint04: ! Interrupt routine for irq 4 (first serial) + hwint_master(4) + + .align 16 +_hwint05: ! Interrupt routine for irq 5 (XT winchester) + hwint_master(5) + + .align 16 +_hwint06: ! Interrupt routine for irq 6 (floppy) + hwint_master(6) + + .align 16 +_hwint07: ! Interrupt routine for irq 7 (printer) + hwint_master(7) + +!*===========================================================================* +!* hwint08 - 15 * +!*===========================================================================* +! Note this is a macro, it just looks like a subroutine. +#define hwint_slave(irq) \ + call save /* save interrupted process state */;\ + inb INT2_CTLMASK ;\ + orb al, [1<<[irq-8]] ;\ + outb INT2_CTLMASK /* disable the irq */;\ + movb al, ENABLE ;\ + outb INT_CTL /* reenable master 8259 */;\ + push (_irq_hooks+4*irq) /* irq_hooks[irq] */;\ + outb INT2_CTL /* reenable slave 8259 */;\ + sti /* enable interrupts */;\ + call _intr_handle /* intr_handle(irq_hooks[irq]) */;\ + cli /* disable interrupts */;\ + pop ecx ;\ + cmp (_irq_actids+4*irq), 0 /* interrupt still active? */;\ + jnz 0f ;\ + inb INT2_CTLMASK ;\ + andb al, ~[1<<[irq-8]] ;\ + outb INT2_CTLMASK /* enable the irq */;\ +0: ret /* restart (another) process */ + +! Each of these entry points is an expansion of the hwint_slave macro + .align 16 +_hwint08: ! Interrupt routine for irq 8 (realtime clock) + hwint_slave(8) + + .align 16 +_hwint09: ! Interrupt routine for irq 9 (irq 2 redirected) + hwint_slave(9) + + .align 16 +_hwint10: ! Interrupt routine for irq 10 + hwint_slave(10) + + .align 16 +_hwint11: ! Interrupt routine for irq 11 + hwint_slave(11) + + .align 16 +_hwint12: ! Interrupt routine for irq 12 + hwint_slave(12) + + .align 16 +_hwint13: ! Interrupt routine for irq 13 (FPU exception) + hwint_slave(13) + + .align 16 +_hwint14: ! Interrupt routine for irq 14 (AT winchester) + hwint_slave(14) + + .align 16 +_hwint15: ! Interrupt routine for irq 15 + hwint_slave(15) + +!*===========================================================================* +!* save * +!*===========================================================================* +! Save for protected mode. +! This is much simpler than for 8086 mode, because the stack already points +! into the process table, or has already been switched to the kernel stack. + + .align 16 +save: + cld ! set direction flag to a known value + pushad ! save "general" registers + o16 push ds ! save ds + o16 push es ! save es + o16 push fs ! save fs + o16 push gs ! save gs + mov dx, ss ! ss is kernel data segment + mov ds, dx ! load rest of kernel segments + mov es, dx ! kernel does not use fs, gs + mov eax, esp ! prepare to return + incb (_k_reenter) ! from -1 if not reentering + jnz set_restart1 ! stack is already kernel stack + mov esp, k_stktop + push _restart ! build return address for int handler + xor ebp, ebp ! for stacktrace + jmp RETADR-P_STACKBASE(eax) + + .align 4 +set_restart1: + push restart1 + jmp RETADR-P_STACKBASE(eax) + +!*===========================================================================* +!* _s_call * +!*===========================================================================* + .align 16 +_s_call: +_p_s_call: + cld ! set direction flag to a known value + sub esp, 6*4 ! skip RETADR, eax, ecx, edx, ebx, est + push ebp ! stack already points into proc table + push esi + push edi + o16 push ds + o16 push es + o16 push fs + o16 push gs + mov dx, ss + mov ds, dx + mov es, dx + incb (_k_reenter) + mov esi, esp ! assumes P_STACKBASE == 0 + mov esp, k_stktop + xor ebp, ebp ! for stacktrace + ! end of inline save + sti ! allow SWITCHER to be interrupted + ! now set up parameters for sys_call() + push ebx ! pointer to user message + push eax ! src/dest + push ecx ! SEND/RECEIVE/BOTH + call _sys_call ! sys_call(function, src_dest, m_ptr) + ! caller is now explicitly in proc_ptr + mov AXREG(esi), eax ! sys_call MUST PRESERVE si + cli ! disable interrupts + +! Fall into code to restart proc/task running. + +!*===========================================================================* +!* restart * +!*===========================================================================* +_restart: + +! Flush any held-up interrupts. +! This reenables interrupts, so the current interrupt handler may reenter. +! This does not matter, because the current handler is about to exit and no +! other handlers can reenter since flushing is only done when k_reenter == 0. + + cmp (_held_head), 0 ! do fast test to usually avoid function call + jz over_call_unhold + call _unhold ! this is rare so overhead acceptable +over_call_unhold: + mov esp, (_proc_ptr) ! will assume P_STACKBASE == 0 + lldt P_LDT_SEL(esp) ! enable segment descriptors for task + lea eax, P_STACKTOP(esp) ! arrange for next interrupt + mov (_tss+TSS3_S_SP0), eax ! to save state in process table +restart1: + decb (_k_reenter) + o16 pop gs + o16 pop fs + o16 pop es + o16 pop ds + popad + add esp, 4 ! skip return adr + iretd ! continue process + +!*===========================================================================* +!* exception handlers * +!*===========================================================================* +_divide_error: + push DIVIDE_VECTOR + jmp exception + +_single_step_exception: + push DEBUG_VECTOR + jmp exception + +_nmi: + push NMI_VECTOR + jmp exception + +_breakpoint_exception: + push BREAKPOINT_VECTOR + jmp exception + +_overflow: + push OVERFLOW_VECTOR + jmp exception + +_bounds_check: + push BOUNDS_VECTOR + jmp exception + +_inval_opcode: + push INVAL_OP_VECTOR + jmp exception + +_copr_not_available: + push COPROC_NOT_VECTOR + jmp exception + +_double_fault: + push DOUBLE_FAULT_VECTOR + jmp errexception + +_copr_seg_overrun: + push COPROC_SEG_VECTOR + jmp exception + +_inval_tss: + push INVAL_TSS_VECTOR + jmp errexception + +_segment_not_present: + push SEG_NOT_VECTOR + jmp errexception + +_stack_exception: + push STACK_FAULT_VECTOR + jmp errexception + +_general_protection: + push PROTECTION_VECTOR + jmp errexception + +_page_fault: + push PAGE_FAULT_VECTOR + jmp errexception + +_copr_error: + push COPROC_ERR_VECTOR + jmp exception + +!*===========================================================================* +!* exception * +!*===========================================================================* +! This is called for all exceptions which do not push an error code. + + .align 16 +exception: + sseg mov (trap_errno), 0 ! clear trap_errno + sseg pop (ex_number) + jmp exception1 + +!*===========================================================================* +!* errexception * +!*===========================================================================* +! This is called for all exceptions which push an error code. + + .align 16 +errexception: + sseg pop (ex_number) + sseg pop (trap_errno) +exception1: ! Common for all exceptions. + push eax ! eax is scratch register + mov eax, 0+4(esp) ! old eip + sseg mov (old_eip), eax + movzx eax, 4+4(esp) ! old cs + sseg mov (old_cs), eax + mov eax, 8+4(esp) ! old eflags + sseg mov (old_eflags), eax + pop eax + call save + push (old_eflags) + push (old_cs) + push (old_eip) + push (trap_errno) + push (ex_number) + call _exception ! (ex_number, trap_errno, old_eip, + ! old_cs, old_eflags) + add esp, 5*4 + cli + ret + +!*===========================================================================* +!* level0_call * +!*===========================================================================* +_level0_call: + call save + jmp (_level0_func) + +!*===========================================================================* +!* data * +!*===========================================================================* + +.sect .rom ! Before the string table please + .data2 0x526F ! this must be the first data entry (magic #) + +.sect .bss +k_stack: + .space K_STACK_BYTES ! kernel stack +k_stktop: ! top of kernel stack + .comm ex_number, 4 + .comm trap_errno, 4 + .comm old_eip, 4 + .comm old_cs, 4 + .comm old_eflags, 4 diff --git a/kernel/mpx88.s b/kernel/mpx88.s new file mode 100755 index 000000000..8b8d6e75e --- /dev/null +++ b/kernel/mpx88.s @@ -0,0 +1,762 @@ +# +! This file, mpx88.s, is included by mpx.s when Minix is compiled for +! 16-bit Intel CPUs. The alternative mpx386.s is compiled for 32-bit CPUs. +! +! This file contains the assembler startup code for Minix and the 16-bit +! interrupt handlers. It cooperates with cstart.c to set up a good +! environment for main(). + +! This file is part of the lowest layer of the MINIX kernel. The other part +! is "proc.c". The lowest layer does process switching and message handling. + +! Every transition to the kernel goes through this file. Transitions are +! caused by sending/receiving messages and by most interrupts. (RS232 +! interrupts may be handled in the file "rs2.s" and then they rarely enter +! the kernel.) + +! Transitions to the kernel may be nested. The initial entry may be with a +! system call, exception or hardware interrupt; reentries may only be made +! by hardware interrupts. The count of reentries is kept in "k_reenter". +! It is important for deciding whether to switch to the kernel stack and +! for protecting the message passing code in "proc.c". + +! For the message passing trap, most of the machine state is saved in the +! proc table. (Some of the registers need not be saved.) Then the stack is +! switched to "k_stack", and interrupts are reenabled. Finally, the system +! call handler (in C) is called. When it returns, interrupts are disabled +! again and the code falls into the restart routine, to finish off held-up +! interrupts and run the process or task whose pointer is in "proc_ptr". + +! Hardware interrupt handlers do the same, except (1) The entire state must +! be saved. (2) There are too many handlers to do this inline, so the save +! routine is called. A few cycles are saved by pushing the address of the +! appropiate restart routine for a return later. (3) A stack switch is +! avoided when the stack is already switched. (4) The (master) 8259 interrupt +! controller is reenabled centrally in save(). (5) Each interrupt handler +! masks its interrupt line using the 8259 before enabling (other unmasked) +! interrupts, and unmasks it after servicing the interrupt. This limits the +! nest level to the number of lines and protects the handler from itself. + +! For communication with the boot monitor at startup time some constant +! data are compiled into the beginning of the text segment. This facilitates +! reading the data at the start of the boot process, since only the first +! sector of the file needs to be read. + +! Some data storage is also allocated at the end of this file. This data +! will be at the start of the data segment of the kernel and will be read +! and modified by the boot monitor before the kernel starts. + +#include <minix/config.h> +#include <minix/const.h> +#include <minix/com.h> +#include "const.h" +#include "sconst.h" +#include "protect.h" + +! The external entry points into this file are: +! Note: in assembly language the .define statement applied to a function name +! is loosely equivalent to a prototype in C code -- it makes it possible to +! link to an entity declared in the assembly code but does not create +! the entity. + +.define _int00 ! handlers for traps and exceptions +.define _int01 +.define _int02 +.define _int03 +.define _int04 +.define _int05 +.define _int06 +.define _int07 +.define _hwint00 ! handlers for hardware interrupts +.define _hwint01 +.define _hwint02 +.define _hwint03 +.define _hwint04 +.define _hwint05 +.define _hwint06 +.define _hwint07 +.define _hwint08 +.define _hwint09 +.define _hwint10 +.define _hwint11 +.define _hwint12 +.define _hwint13 +.define _hwint14 +.define _hwint15 +.define _restart ! start running a task or process +.define save ! save the machine state in the proc table +.define _s_call ! process or task wants to send or receive a message + +! Exported variables. +.define kernel_ds +.define begbss +.define begdata + + .text +!*===========================================================================* +!* MINIX * +!*===========================================================================* +MINIX: ! this is the entry point for the MINIX kernel + jmp over_kernel_ds ! skip over the next few bytes + .data2 CLICK_SHIFT ! for the monitor: memory granularity +kernel_ds: + .data2 0x01B4 ! boot monitor flags: (later kernel DS) + ! call in 8086 mode, make bss, make stack, + ! load low, don`t patch, will return, + ! (has own INT calls), memory vector, + ! new boot code return +over_kernel_ds: + +! Set up a C stack frame on the monitor stack. (The monitor sets cs and ds +! right. The ss register still references the monitor data segment.) + push bp + mov bp, sp + push si + push di + cmp 4(bp), #0 ! monitor code segment is + jz noret ! nonzero if return possible + inc _mon_return +noret: mov _mon_ss, ss ! save stack location for later return + mov _mon_sp, sp + +! Locate boot parameters, set up kernel segment registers and stack. + mov bx, 6(bp) ! boot parameters offset + mov dx, 8(bp) ! boot parameters length + mov ax, 10(bp) ! address of a.out headers + mov _aout+0, ax + mov ax, 12(bp) + mov _aout+2, ax + mov ax, ds ! kernel data + mov es, ax + mov ss, ax + mov sp, #k_stktop ! set sp to point to the top of kernel stack + +! Real mode needs to get kernel DS from the code segment. Protected mode +! needs CS in the jump back to real mode. + + cseg mov kernel_cs, cs + cseg mov kernel_ds, ds + +! Call C startup code to set up a proper environment to run main(). + push dx + push bx + push _mon_ss + push ds + push cs + call _cstart ! cstart(cs, ds, mds, parmoff, parmlen) + add sp, #5*2 + + cmp _protected_mode, #0 + jz nosw ! ok to switch to protected mode? + + call klib_init_prot ! initialize klib functions for protected mode + call real2prot ! switch to protected mode + + push #0 ! set flags to known good state + popf ! especially, clear nested task and int enable +nosw: + jmp _main ! main() + + +!*===========================================================================* +!* interrupt handlers * +!*===========================================================================* + + +!*===========================================================================* +!* hwint00 - 07 * +!*===========================================================================* +! Note that the first few lines are a macro +#define hwint_master(irq) \ + call save /* save interrupted process state */;\ + mov si, *[2*irq] /* load array index offset */;\ + mov di, *[1<<irq] /* irq mask bit */;\ + jmp hwint_master /* continue with common code */ +hwint_master: + inb INT_CTLMASK + or ax, di ! al |= (1 << irq) + outb INT_CTLMASK ! disable the irq + movb al, *ENABLE + outb INT_CTL ! reenable master 8259 + mov cx, _irq_hooks(si) ! irq_hooks[irq] + push cx + sti ! enable interrupts + call _intr_handle ! intr_handle(irq_hooks[irq]) + cli ! disable interrupts + pop cx + cmp _irq_actids(si), *0 ! interrupt still active? + jnz 0f + inb INT_CTLMASK + not di + and ax, di ! al &= ~(1 << irq) + outb INT_CTLMASK ! enable the irq +0: ret ! restart (another) process + +! Each of these entry points is an expansion of the hwint_master macro + +_hwint00: ! Interrupt routine for irq 0 (the clock). + hwint_master(0) + + +_hwint01: ! Interrupt routine for irq 1 (keyboard) + hwint_master(1) + + +_hwint02: ! Interrupt routine for irq 2 (cascade!) + hwint_master(2) + + +_hwint03: ! Interrupt routine for irq 3 (second serial) + hwint_master(3) + + +_hwint04: ! Interrupt routine for irq 4 (first serial) + hwint_master(4) + + +_hwint05: ! Interrupt routine for irq 5 (XT winchester) + hwint_master(5) + + +_hwint06: ! Interrupt routine for irq 6 (floppy) + hwint_master(6) + + +_hwint07: ! Interrupt routine for irq 7 (printer) + hwint_master(7) + + +!*===========================================================================* +!* hwint08 - 15 * +!*===========================================================================* +! Note that the first few lines are a macro +#define hwint_slave(irq) \ + call save /* save interrupted process state */;\ + mov si, *[2*irq] /* load array index offset */;\ + mov di, *[1<<[irq-8]] /* irq mask bit */;\ + jmp hwint_slave /* continue with common code */ +hwint_slave: + inb INT2_CTLMASK + or ax, di ! al |= (1 << (irq-8)) + outb INT2_CTLMASK ! disable the irq + movb al, *ENABLE + outb INT_CTL ! reenable master 8259 + mov cx, _irq_hooks(si) ! irq_hooks[irq] + outb INT2_CTL ! reenable slave 8259 + push cx + sti ! enable interrupts + call _intr_handle ! intr_handle(irq_hooks[irq]) + cli ! disable interrupts + pop cx + cmp _irq_actids(si), *0 ! interrupt still active? + jnz 0f + inb INT2_CTLMASK + not di + and ax, di ! al &= ~(1 << (irq-8)) + outb INT2_CTLMASK ! enable the irq +0: ret ! restart (another) process + +! Each of these entry points is an expansion of the hwint_slave macro + +_hwint08: ! Interrupt routine for irq 8 (realtime clock) + hwint_slave(8) + + +_hwint09: ! Interrupt routine for irq 9 (irq 2 redirected) + hwint_slave(9) + + +_hwint10: ! Interrupt routine for irq 10 + hwint_slave(10) + + +_hwint11: ! Interrupt routine for irq 11 + hwint_slave(11) + + +_hwint12: ! Interrupt routine for irq 12 + hwint_slave(12) + + +_hwint13: ! Interrupt routine for irq 13 (FPU exception) + hwint_slave(13) + + +_hwint14: ! Interrupt routine for irq 14 (AT winchester) + hwint_slave(14) + + +_hwint15: ! Interrupt routine for irq 15 + hwint_slave(15) + + +!*===========================================================================* +!* save * +!*===========================================================================* +save: ! save the machine state in the proc table. + +! In protected mode a jump to p_save is patched over the following +! code during initialization. + + cld ! set direction flag to a known value + push ds + push si + cseg mov ds,kernel_ds + incb _k_reenter ! from -1 if not reentering + jnz push_current_stack ! stack is already kernel stack + + mov si,_proc_ptr + mov AXREG(si),ax + mov BXREG(si),bx + mov CXREG(si),cx + mov DXREG(si),dx + pop SIREG(si) + mov DIREG(si),di + mov BPREG(si),bp + mov ESREG(si),es + pop DSREG(si) + pop bx ! return adr + pop PCREG(si) + pop CSREG(si) + pop PSWREG(si) + mov SPREG(si),sp + mov SSREG(si),ss + + mov dx,ds + mov ss,dx + mov sp,#k_stktop + mov ax,#_restart ! build return address for interrupt handler + push ax + +stack_switched: + mov es,dx + jmp (bx) + +push_current_stack: + push es + push bp + push di + push dx + push cx + push bx + push ax + mov bp,sp + mov bx,18(bp) ! get the return adr; it becomes junk on stack + mov ax,#restart1 + push ax + mov dx,ss + mov ds,dx + jmp stack_switched + + +!*===========================================================================* +!* s_call * +!*===========================================================================* +! This is real mode version. Alternate (_p_s_call) will be used in +! protected mode + +_s_call: ! System calls are vectored here. + ! Do save routine inline for speed, + ! but do not save ax, bx, cx, dx, + ! since C does not require preservation, + ! and ax returns the result code anyway. + ! Regs bp, si, di get saved by sys_call as + ! well, but it is impractical not to preserve + ! them here, in case context gets switched. + ! Some special-case code in pick_proc() + ! could avoid this. + cld ! set direction flag to a known value + push ds + push si + cseg mov ds,kernel_ds + incb _k_reenter + mov si,_proc_ptr + pop SIREG(si) + mov DIREG(si),di + mov BPREG(si),bp + mov ESREG(si),es + pop DSREG(si) + pop PCREG(si) + pop CSREG(si) + pop PSWREG(si) + mov SPREG(si),sp + mov SSREG(si),ss + mov dx,ds + mov es,dx + mov ss,dx ! interrupt handlers may not make system calls + mov sp,#k_stktop ! so stack is not already switched + ! end of inline save + ! now set up parameters for C routine sys_call + push bx ! pointer to user message + push ax ! src/dest + push cx ! SEND/RECEIVE/BOTH + sti ! allow SWITCHER to be interrupted + call _sys_call ! sys_call(function, src_dest, m_ptr) + ! caller is now explicitly in proc_ptr + mov AXREG(si),ax ! sys_call MUST PRESERVE si + cli + +! Fall into code to restart proc/task running. + + +!*===========================================================================* +!* restart * +!*===========================================================================* +_restart: + +! Flush any held-up interrupts. +! This reenables interrupts, so the current interrupt handler may reenter. +! This does not matter, because the current handler is about to exit and no +! other handlers can reenter since flushing is only done when k_reenter == 0. + +! In protected mode a jump to p_restart is patched over the following +! code during initialization. + + cmp _held_head,#0 ! do fast test to usually avoid function call + jz over_call_unhold + call _unhold ! this is rare so overhead is acceptable +over_call_unhold: + + mov si,_proc_ptr + decb _k_reenter + mov ax,AXREG(si) ! start restoring registers from proc table + ! could make AXREG == 0 to use lodw here + mov bx,BXREG(si) + mov cx,CXREG(si) + mov dx,DXREG(si) + mov di,DIREG(si) + mov bp,BPREG(si) + mov es,ESREG(si) + mov ss,SSREG(si) + mov sp,SPREG(si) + push PSWREG(si) ! fake interrupt stack frame + push CSREG(si) + push PCREG(si) + ! could put si:ds together to use + ! lds si,SIREG(si) + push DSREG(si) + mov si,SIREG(si) + pop ds + iret + +restart1: + decb _k_reenter + pop ax + pop bx + pop cx + pop dx + pop di + pop bp + pop es + pop si + pop ds + add sp,#2 ! skip return adr + iret + + +!*===========================================================================* +!* int00-07 * +!*===========================================================================* +! These are entry points for exceptions (processor generated interrupts, +! usually caused by error conditions such as an attempt to divide by zero) + +_int00: ! interrupt through vector 0 + push ax + movb al,#0 + jmp exception + +_int01: ! interrupt through vector 1, etc + push ax + movb al,#1 + jmp exception + +_int02: + push ax + movb al,#2 + jmp exception + +_int03: + push ax + movb al,#3 + jmp exception + +_int04: + push ax + movb al,#4 + jmp exception + +_int05: + push ax + movb al,#5 + jmp exception + +_int06: + push ax + movb al,#6 + jmp exception + +_int07: + push ax + movb al,#7 + !jmp exception + +exception: + cseg movb ex_number,al ! it is cumbersome to get this into dseg + pop ax + call save + cseg push ex_number ! high byte is constant 0 + call _exception ! do whatever is necessary (sti only if safe) + add sp,#2 + cli + ret + + +!*===========================================================================* +!* level0_call * +!*===========================================================================* +_level0_call: + call save + jmp @_level0_func + +!*===========================================================================* +!* data * +!*===========================================================================* +! NB some variables are stored in code segment. + +ex_number: ! exception number + .space 2 + + +!*===========================================================================* +!* variants for 286 protected mode * +!*===========================================================================* + +! Most routines are different in 286 protected mode. +! The only essential difference is that an interrupt in protected mode +! (usually) switches the stack, so there is less to do in software. + +! These functions are reached along jumps patched in by klib_init_prot(): + + .define p_restart ! replaces _restart + .define p_save ! replaces save + +! These exception and software-interrupt handlers are enabled by the new +! interrupt vector table set up in protect.c: + + .define _divide_error ! _int00 + .define _single_step_exception ! _int01 + .define _nmi ! _int02 + .define _breakpoint_exception ! _int03 + .define _overflow ! _int04 + .define _bounds_check ! _int05 + .define _inval_opcode ! _int06 + .define _copr_not_available ! _int07 + .define _double_fault ! (286 trap) + .define _copr_seg_overrun ! (etc) + .define _inval_tss + .define _segment_not_present + .define _stack_exception + .define _general_protection + .define _p_s_call ! _s_call + .define _level0_call + +! The hardware interrupt handlers need not be altered apart from putting +! them in the new table (save() handles the differences). +! Some of the intxx handlers (those for exceptions which do not push an +! error code) need not have been replaced, but the names here are better. + +#include "protect.h" + +/* Selected 286 tss offsets. */ +#define TSS2_S_SP0 2 + +! imported variables + + .extern _tss + .extern _level0_func + +!*===========================================================================* +!* p_save * +!*===========================================================================* +! Save for 286 protected mode. +! This is much simpler than for 8086 mode, because the stack already points +! into process table, or has already been switched to the kernel stack. + +p_save: + cld ! set direction flag to a known value + pusha ! save "general" registers + push ds ! save ds + push es ! save es + mov dx,ss ! ss is kernel data segment + mov ds,dx ! load rest of kernel segments + mov es,dx + mov bp,sp ! prepare to return + incb _k_reenter ! from -1 if not reentering + jnz set_p1_restart ! stack is already kernel stack + mov sp,#k_stktop + push #p_restart ! build return address for interrupt handler + jmp @RETADR-P_STACKBASE(bp) + +set_p1_restart: + push #p1_restart + jmp @RETADR-P_STACKBASE(bp) + + +!*===========================================================================* +!* p_s_call * +!*===========================================================================* +_p_s_call: + cld ! set direction flag to a known value + sub sp,#6*2 ! skip RETADR, ax, cx, dx, bx, st + push bp ! stack already points into process table + push si + push di + push ds + push es + mov dx,ss + mov ds,dx + mov es,dx + incb _k_reenter + mov si,sp ! assumes P_STACKBASE == 0 + mov sp,#k_stktop + ! end of inline save + sti ! allow SWITCHER to be interrupted + ! now set up parameters for C routine sys_call + push bx ! pointer to user message + push ax ! src/dest + push cx ! SEND/RECEIVE/BOTH + call _sys_call ! sys_call(function, src_dest, m_ptr) + ! caller is now explicitly in proc_ptr + mov AXREG(si),ax ! sys_call MUST PRESERVE si + cli + +! Fall into code to restart proc/task running. + +p_restart: + +! Flush any held-up interrupts. +! This reenables interrupts, so the current interrupt handler may reenter. +! This does not matter, because the current handler is about to exit and no +! other handlers can reenter since flushing is only done when k_reenter == 0. + + cmp _held_head,#0 ! do fast test to usually avoid function call + jz p_over_call_unhold + call _unhold ! this is rare so overhead is acceptable +p_over_call_unhold: + mov si,_proc_ptr + lldt P_LDT_SEL(si) ! enable segment descriptors for task + lea ax,P_STACKTOP(si) ! arrange for next interrupt + mov _tss+TSS2_S_SP0,ax ! to save state in process table + mov sp,si ! assumes P_STACKBASE == 0 +p1_restart: + decb _k_reenter + pop es + pop ds + popa + add sp,#2 ! skip return adr + iret ! continue process + + +!*===========================================================================* +!* exception handlers * +!*===========================================================================* +_divide_error: + push #DIVIDE_VECTOR + jmp p_exception + +_single_step_exception: + push #DEBUG_VECTOR + jmp p_exception + +_nmi: + push #NMI_VECTOR + jmp p_exception + +_breakpoint_exception: + push #BREAKPOINT_VECTOR + jmp p_exception + +_overflow: + push #OVERFLOW_VECTOR + jmp p_exception + +_bounds_check: + push #BOUNDS_VECTOR + jmp p_exception + +_inval_opcode: + push #INVAL_OP_VECTOR + jmp p_exception + +_copr_not_available: + push #COPROC_NOT_VECTOR + jmp p_exception + +_double_fault: + push #DOUBLE_FAULT_VECTOR + jmp errexception + +_copr_seg_overrun: + push #COPROC_SEG_VECTOR + jmp p_exception + +_inval_tss: + push #INVAL_TSS_VECTOR + jmp errexception + +_segment_not_present: + push #SEG_NOT_VECTOR + jmp errexception + +_stack_exception: + push #STACK_FAULT_VECTOR + jmp errexception + +_general_protection: + push #PROTECTION_VECTOR + jmp errexception + + +!*===========================================================================* +!* p_exception * +!*===========================================================================* +! This is called for all exceptions which do not push an error code. + +p_exception: + sseg pop ds_ex_number + call p_save + jmp p1_exception + + +!*===========================================================================* +!* errexception * +!*===========================================================================* +! This is called for all exceptions which push an error code. + +errexception: + sseg pop ds_ex_number + sseg pop trap_errno + call p_save +p1_exception: ! Common for all exceptions. + push ds_ex_number + call _exception + add sp,#2 + cli + ret + + +!*===========================================================================* +!* data * +!*===========================================================================* + + .data +begdata: + .data2 0x526F ! this must be the first data entry (magic #) + + .bss +begbss: +k_stack: + .space K_STACK_BYTES ! kernel stack +k_stktop: ! top of kernel stack + .comm ds_ex_number, 2 + .comm trap_errno, 2 diff --git a/kernel/pci.c b/kernel/pci.c new file mode 100755 index 000000000..a71183019 --- /dev/null +++ b/kernel/pci.c @@ -0,0 +1,1224 @@ +/* +pci.c + +Configure devices on the PCI bus + +Created: Jan 2000 by Philip Homburg <philip@cs.vu.nl> +*/ + +#include "kernel.h" + + +#include "pci.h" +#include "pci_amd.h" +#include "pci_intel.h" +#include "pci_sis.h" +#include "pci_via.h" +#if __minix_vmd +#include "config.h" +#endif + +#if ENABLE_PCI + +#if !__minix_vmd +#define debug 0 +#define irq_mode_pci(irq) ((void)0) +#endif + +#include <minix/utils.h> +INIT_SERVER_ASSERT + +#define NR_PCIBUS 2 +#define NR_PCIDEV 40 + +#define PBT_INTEL 1 +#define PBT_PCIBRIDGE 2 + +PRIVATE struct pcibus +{ + int pb_type; + int pb_isabridge_dev; + int pb_isabridge_type; + + int pb_devind; + int pb_bus; + u8_t (*pb_rreg8)(int busind, int devind, int port); + u16_t (*pb_rreg16)(int busind, int devind, int port); + u32_t (*pb_rreg32)(int busind, int devind, int port); + void (*pb_wreg16)(int busind, int devind, int port, U16_t value); + void (*pb_wreg32)(int busind, int devind, int port, u32_t value); + u16_t (*pb_rsts)(int busind); + void (*pb_wsts)(int busind, U16_t value); +} pcibus[NR_PCIBUS]; +PRIVATE int nr_pcibus= 0; + +PRIVATE struct pcidev +{ + u8_t pd_busind; + u8_t pd_dev; + u8_t pd_func; + u8_t pd_baseclass; + u8_t pd_subclass; + u8_t pd_infclass; + u16_t pd_vid; + u16_t pd_did; + u8_t pd_inuse; +} pcidev[NR_PCIDEV]; +PRIVATE int nr_pcidev= 0; + +FORWARD _PROTOTYPE( void pci_intel_init, (void) ); +FORWARD _PROTOTYPE( void probe_bus, (int busind) ); +FORWARD _PROTOTYPE( int do_isabridge, (int busind) ); +FORWARD _PROTOTYPE( void do_pcibridge, (int busind) ); +FORWARD _PROTOTYPE( int do_piix, (int devind) ); +FORWARD _PROTOTYPE( int do_amd_isabr, (int devind) ); +FORWARD _PROTOTYPE( int do_sis_isabr, (int devind) ); +FORWARD _PROTOTYPE( int do_via_isabr, (int devind) ); +FORWARD _PROTOTYPE( char *pci_vid_name, (U16_t vid) ); +FORWARD _PROTOTYPE( char *pci_baseclass_name, (U8_t baseclass) ); +FORWARD _PROTOTYPE( char *pci_subclass_name, (U8_t baseclass, + U8_t subclass, U8_t infclass) ); +FORWARD _PROTOTYPE( void ntostr, (unsigned n, char **str, char *end) ); +FORWARD _PROTOTYPE( u16_t pci_attr_rsts, (int devind) ); +FORWARD _PROTOTYPE( void pci_attr_wsts, (int devind, U16_t value) ); +FORWARD _PROTOTYPE( u16_t pcibr_intel_rsts, (int busind) ); +FORWARD _PROTOTYPE( void pcibr_intel_wsts, (int busind, U16_t value) ); +FORWARD _PROTOTYPE( u16_t pcibr_via_rsts, (int busind) ); +FORWARD _PROTOTYPE( void pcibr_via_wsts, (int busind, U16_t value) ); +FORWARD _PROTOTYPE( u8_t pcii_rreg8, (int busind, int devind, int port) ); +FORWARD _PROTOTYPE( u16_t pcii_rreg16, (int busind, int devind, + int port) ); +FORWARD _PROTOTYPE( u32_t pcii_rreg32, (int busind, int devind, + int port) ); +FORWARD _PROTOTYPE( void pcii_wreg16, (int busind, int devind, int port, + U16_t value) ); +FORWARD _PROTOTYPE( void pcii_wreg32, (int busind, int devind, int port, + u32_t value) ); +FORWARD _PROTOTYPE( u16_t pcii_rsts, (int busind) ); +FORWARD _PROTOTYPE( void pcii_wsts, (int busind, U16_t value) ); + +/*===========================================================================* + * pci_init * + *===========================================================================*/ +PUBLIC void pci_init() +{ + static int first_time= 1; + + if (!first_time) + return; + + /* We don't expect to interrupted */ + server_assert(first_time == 1); + first_time= -1; + + /* Only Intel (compatible) PCI controllers are supported at the + * moment. + */ + pci_intel_init(); + + first_time= 0; +} + +/*===========================================================================* + * pci_find_dev * + *===========================================================================*/ +PUBLIC int pci_find_dev(bus, dev, func, devindp) +u8_t bus; +u8_t dev; +u8_t func; +int *devindp; +{ + int devind; + + for (devind= 0; devind < nr_pcidev; devind++) + { + if (pcidev[devind].pd_busind == bus && + pcidev[devind].pd_dev == dev && + pcidev[devind].pd_func == func) + { + break; + } + } + if (devind >= nr_pcidev) + return 0; + if (pcidev[devind].pd_inuse) + return 0; + *devindp= devind; + return 1; +} + + +/*===========================================================================* + * pci_first_dev * + *===========================================================================*/ +PUBLIC int pci_first_dev(devindp, vidp, didp) +int *devindp; +u16_t *vidp; +u16_t *didp; +{ + int devind; + + for (devind= 0; devind < nr_pcidev; devind++) + { + if (!pcidev[devind].pd_inuse) + break; + } + if (devind >= nr_pcidev) + return 0; + *devindp= devind; + *vidp= pcidev[devind].pd_vid; + *didp= pcidev[devind].pd_did; + return 1; +} + + +/*===========================================================================* + * pci_next_dev * + *===========================================================================*/ +PUBLIC int pci_next_dev(devindp, vidp, didp) +int *devindp; +u16_t *vidp; +u16_t *didp; +{ + int devind; + + for (devind= *devindp+1; devind < nr_pcidev; devind++) + { + if (!pcidev[devind].pd_inuse) + break; + } + if (devind >= nr_pcidev) + return 0; + *devindp= devind; + *vidp= pcidev[devind].pd_vid; + *didp= pcidev[devind].pd_did; + return 1; +} + + +/*===========================================================================* + * pci_reserve * + *===========================================================================*/ +PUBLIC void pci_reserve(devind) +int devind; +{ + server_assert(devind <= nr_pcidev); + server_assert(!pcidev[devind].pd_inuse); + pcidev[devind].pd_inuse= 1; +} + + +/*===========================================================================* + * pci_ids * + *===========================================================================*/ +PUBLIC void pci_ids(devind, vidp, didp) +int devind; +u16_t *vidp; +u16_t *didp; +{ + server_assert(devind <= nr_pcidev); + *vidp= pcidev[devind].pd_vid; + *didp= pcidev[devind].pd_did; +} + + +/*===========================================================================* + * pci_slot_name * + *===========================================================================*/ +PUBLIC char *pci_slot_name(devind) +int devind; +{ + static char label[]= "ddd.ddd.ddd"; + char *end; + char *p; + + p= label; + end= label+sizeof(label); + + ntostr(pcidev[devind].pd_busind, &p, end); + *p++= '.'; + + ntostr(pcidev[devind].pd_dev, &p, end); + *p++= '.'; + + ntostr(pcidev[devind].pd_func, &p, end); + + return label; +} + +/*===========================================================================* + * pci_dev_name * + *===========================================================================*/ +PUBLIC char *pci_dev_name(vid, did) +u16_t vid; +u16_t did; +{ + int i; + + for (i= 0; pci_device_table[i].name; i++) + { + if (pci_device_table[i].vid == vid && + pci_device_table[i].did == did) + { + return pci_device_table[i].name; + } + } + return NULL; +} + + +/*===========================================================================* + * pci_attr_r8 * + *===========================================================================*/ +PUBLIC u8_t pci_attr_r8(devind, port) +int devind; +int port; +{ + int busind; + + busind= pcidev[devind].pd_busind; + return pcibus[busind].pb_rreg8(busind, devind, port); +} + + +/*===========================================================================* + * pci_attr_r16 * + *===========================================================================*/ +PUBLIC u16_t pci_attr_r16(devind, port) +int devind; +int port; +{ + int busind; + + busind= pcidev[devind].pd_busind; + return pcibus[busind].pb_rreg16(busind, devind, port); +} + + +/*===========================================================================* + * pci_attr_r32 * + *===========================================================================*/ +PUBLIC u32_t pci_attr_r32(devind, port) +int devind; +int port; +{ + int busind; + + busind= pcidev[devind].pd_busind; + return pcibus[busind].pb_rreg32(busind, devind, port); +} + + +/*===========================================================================* + * pci_attr_w16 * + *===========================================================================*/ +PUBLIC void pci_attr_w16(devind, port, value) +int devind; +int port; +u16_t value; +{ + int busind; + + busind= pcidev[devind].pd_busind; + pcibus[busind].pb_wreg16(busind, devind, port, value); +} + + +/*===========================================================================* + * pci_attr_w32 * + *===========================================================================*/ +PUBLIC void pci_attr_w32(devind, port, value) +int devind; +int port; +u32_t value; +{ + int busind; + + busind= pcidev[devind].pd_busind; + pcibus[busind].pb_wreg32(busind, devind, port, value); +} + + +/*===========================================================================* + * pci_intel_init * + *===========================================================================*/ +PRIVATE void pci_intel_init() +{ + /* Try to detect a know PCI controller. Read the Vendor ID and + * the Device ID for function 0 of device 0. + * Two times the value 0xffff suggests a system without a (compatible) + * PCI controller. Only controllers with values listed in the table + * pci_intel_ctrl are actually used. + */ + u32_t bus, dev, func; + u16_t vid, did; + int i, r, busind; + char *dstr; + + bus= 0; + dev= 0; + func= 0; + + vid= PCII_RREG16_(bus, dev, func, PCI_VID); + did= PCII_RREG16_(bus, dev, func, PCI_DID); + outl(PCII_CONFADD, PCII_UNSEL); + + if (vid == 0xffff && did == 0xffff) + return; /* Nothing here */ + + for (i= 0; pci_intel_ctrl[i].vid; i++) + { + if (pci_intel_ctrl[i].vid == vid && + pci_intel_ctrl[i].did == did) + { + break; + } + } + + if (!pci_intel_ctrl[i].vid) + { + printf("pci_intel_init: unknown PCI-controller:\n" + "\tvendor %04X (%s), device %04X\n", + vid, pci_vid_name(vid), did); + return; + } + + if (nr_pcibus >= NR_PCIBUS) + server_panic("PCI","too many PCI busses", nr_pcibus); + busind= nr_pcibus; + nr_pcibus++; + pcibus[busind].pb_type= PBT_INTEL; + pcibus[busind].pb_isabridge_dev= -1; + pcibus[busind].pb_isabridge_type= 0; + pcibus[busind].pb_devind= -1; + pcibus[busind].pb_bus= 0; + pcibus[busind].pb_rreg8= pcii_rreg8; + pcibus[busind].pb_rreg16= pcii_rreg16; + pcibus[busind].pb_rreg32= pcii_rreg32; + pcibus[busind].pb_wreg16= pcii_wreg16; + pcibus[busind].pb_wreg32= pcii_wreg32; + pcibus[busind].pb_rsts= pcii_rsts; + pcibus[busind].pb_wsts= pcii_wsts; + + dstr= pci_dev_name(vid, did); + if (!dstr) + dstr= "unknown device"; + if (debug) + { + printf("pci_intel_init: %s (%04X/%04X)\n", + dstr, vid, did); + } + + probe_bus(busind); + + r= do_isabridge(busind); + if (r != OK) + { + /* Disable all devices for this bus */ + for (i= 0; i<nr_pcidev; i++) + { + if (pcidev[i].pd_busind != busind) + continue; + pcidev[i].pd_inuse= 1; + } + return; + } + + /* Look for PCI bridges (for AGP) */ + do_pcibridge(busind); +} + + +/*===========================================================================* + * probe_bus * + *===========================================================================*/ +PRIVATE void probe_bus(busind) +int busind; +{ + u32_t dev, func; + u16_t vid, did, sts; + u8_t headt; + u8_t baseclass, subclass, infclass; + int devind; + char *s, *dstr; + +#if DEBUG +printf("probe_bus(%d)\n", busind); +#endif + if (nr_pcidev >= NR_PCIDEV) + server_panic("PCI","too many PCI devices", nr_pcidev); + devind= nr_pcidev; + + for (dev= 0; dev<32; dev++) + { + + for (func= 0; func < 8; func++) + { + pcidev[devind].pd_busind= busind; + pcidev[devind].pd_dev= dev; + pcidev[devind].pd_func= func; + + pci_attr_wsts(devind, + PSR_SSE|PSR_RMAS|PSR_RTAS); + vid= pci_attr_r16(devind, PCI_VID); + did= pci_attr_r16(devind, PCI_DID); + headt= pci_attr_r8(devind, PCI_HEADT); + sts= pci_attr_rsts(devind); + if (sts & (PSR_SSE|PSR_RMAS|PSR_RTAS)) + break; + if (vid == NO_VID) + { + /* Some bridge implementations do support + * pci_attr_rsts. + */ + break; + } + + dstr= pci_dev_name(vid, did); + if (debug) + { + if (dstr) + { + printf("%d.%lu.%lu: %s (%04X/%04X)\n", + busind, (unsigned long)dev, + (unsigned long)func, dstr, + vid, did); + } + else + { + printf( + "%d.%lu.%lu: Unknown device, vendor %04X (%s), device %04X\n", + busind, (unsigned long)dev, + (unsigned long)func, vid, + pci_vid_name(vid), did); + } + } + + baseclass= pci_attr_r8(devind, PCI_BCR); + subclass= pci_attr_r8(devind, PCI_SCR); + infclass= pci_attr_r8(devind, PCI_PIFR); + s= pci_subclass_name(baseclass, subclass, infclass); + if (!s) + s= pci_baseclass_name(baseclass); + { + if (!s) + s= "(unknown class)"; + } + if (debug) + { + printf("\tclass %s (%X/%X/%X)\n", s, + baseclass, subclass, infclass); + } + + devind= nr_pcidev; + nr_pcidev++; + pcidev[devind].pd_baseclass= baseclass; + pcidev[devind].pd_subclass= subclass; + pcidev[devind].pd_infclass= infclass; + pcidev[devind].pd_vid= vid; + pcidev[devind].pd_did= did; + pcidev[devind].pd_inuse= 0; + + if (nr_pcidev >= NR_PCIDEV) + server_panic("PCI","too many PCI devices", nr_pcidev); + devind= nr_pcidev; + + if (func == 0 && !(headt & PHT_MULTIFUNC)) + break; + } + } +} + + +/*===========================================================================* + * do_isabridge * + *===========================================================================*/ +PRIVATE int do_isabridge(busind) +int busind; +{ + int unknown_bridge= -1; + int bridge_dev= -1; + int i, j, r, type; + u16_t vid, did; + char *dstr; + + j= 0; /* lint */ + vid= did= 0; /* lint */ + for (i= 0; i< nr_pcidev; i++) + { + if (pcidev[i].pd_busind != busind) + continue; + if (pcidev[i].pd_baseclass == 0x06 && + pcidev[i].pd_subclass == 0x01 && + pcidev[i].pd_infclass == 0x00) + { + /* ISA bridge. Report if no supported bridge is + * found. + */ + unknown_bridge= i; + } + + vid= pcidev[i].pd_vid; + did= pcidev[i].pd_did; + for (j= 0; pci_isabridge[j].vid != 0; j++) + { + if (pci_isabridge[j].vid != vid) + continue; + if (pci_isabridge[j].did != did) + continue; + if (pci_isabridge[j].checkclass && + unknown_bridge != i) + { + /* This part of multifunction device is + * not the bridge. + */ + continue; + } + break; + } + if (pci_isabridge[j].vid) + { + bridge_dev= i; + break; + } + } + + if (bridge_dev != -1) + { + dstr= pci_dev_name(vid, did); + if (!dstr) + dstr= "unknown device"; + if (debug) + { + printf("found ISA bridge (%04X/%04X) %s\n", + vid, did, dstr); + } + pcibus[busind].pb_isabridge_dev= bridge_dev; + type= pci_isabridge[j].type; + pcibus[busind].pb_isabridge_type= type; + switch(type) + { + case PCI_IB_PIIX: + r= do_piix(bridge_dev); + break; + case PCI_IB_VIA: + r= do_via_isabr(bridge_dev); + break; + case PCI_IB_AMD: + r= do_amd_isabr(bridge_dev); + break; + case PCI_IB_SIS: + r= do_sis_isabr(bridge_dev); + break; + default: + server_panic("PCI","unknown ISA bridge type", type); + } + return r; + } + + if (unknown_bridge == -1) + { + printf("do_isabridge: no ISA bridge found for bus %d", busind); + return -1; + } + printf("Unsupported ISA bridge %04X/%04X for bus %d\n", + pcidev[unknown_bridge].pd_vid, + pcidev[unknown_bridge].pd_did, + busind); + return -1; +} + + +/*===========================================================================* + * do_pcibridge * + *===========================================================================*/ +PRIVATE void do_pcibridge(busind) +int busind; +{ + int devind, i; + int ind, type; + u16_t vid, did; + u8_t sbusn; + + vid= did= 0; /* lint */ + for (devind= 0; devind< nr_pcidev; devind++) + { + if (pcidev[devind].pd_busind != busind) + continue; + + vid= pcidev[devind].pd_vid; + did= pcidev[devind].pd_did; + for (i= 0; pci_pcibridge[i].vid != 0; i++) + { + if (pci_pcibridge[i].vid != vid) + continue; + if (pci_pcibridge[i].did != did) + continue; + break; + } + if (pci_pcibridge[i].vid == 0) + continue; + type= pci_pcibridge[i].type; + + if (debug) + printf("PCI-to-PCI bridge: %04X/%04X\n", vid, did); + + /* Assume that the BIOS initialized the secondary bus + * number. + */ + sbusn= pci_attr_r8(devind, PPB_SBUSN); +#if DEBUG + printf("sbusn = %d\n", sbusn); +#endif + + if (nr_pcibus >= NR_PCIBUS) + server_panic("PCI","too many PCI busses", nr_pcibus); + ind= nr_pcibus; + nr_pcibus++; + pcibus[ind].pb_type= PBT_PCIBRIDGE; + pcibus[ind].pb_isabridge_dev= -1; + pcibus[ind].pb_isabridge_type= 0; + pcibus[ind].pb_devind= devind; + pcibus[ind].pb_bus= sbusn; + pcibus[ind].pb_rreg8= pcibus[busind].pb_rreg8; + pcibus[ind].pb_rreg16= pcibus[busind].pb_rreg16; + pcibus[ind].pb_rreg32= pcibus[busind].pb_rreg32; + pcibus[ind].pb_wreg16= pcibus[busind].pb_wreg16; + pcibus[ind].pb_wreg32= pcibus[busind].pb_wreg32; + switch(type) + { + case PCI_AGPB_INTEL: + pcibus[ind].pb_rsts= pcibr_intel_rsts; + pcibus[ind].pb_wsts= pcibr_intel_wsts; + break; + case PCI_AGPB_VIA: + pcibus[ind].pb_rsts= pcibr_via_rsts; + pcibus[ind].pb_wsts= pcibr_via_wsts; + break; + default: + server_panic("PCI","unknown PCI-PCI bridge type", type); + } + + probe_bus(ind); + } +} + + +/*===========================================================================* + * do_piix * + *===========================================================================*/ +PRIVATE int do_piix(devind) +int devind; +{ + int i, dev, func, irqrc, irq; + u16_t elcr1, elcr2, elcr; + +#if DEBUG + printf("in piix\n"); +#endif + dev= pcidev[devind].pd_dev; + func= pcidev[devind].pd_func; + elcr1= inb(PIIX_ELCR1); + elcr2= inb(PIIX_ELCR2); + elcr= elcr1 | (elcr2 << 8); + for (i= 0; i<4; i++) + { + irqrc= pci_attr_r8(devind, PIIX_PIRQRCA+i); + if (irqrc & PIIX_IRQ_DI) + { + if (debug) + printf("INT%c: disabled\n", 'A'+i); + } + else + { + irq= irqrc & PIIX_IRQ_MASK; + if (debug) + printf("INT%c: %d\n", 'A'+i, irq); + if (!(elcr & (1 << irq))) + { + printf("IRQ %d is not level triggered\n", + irq); + server_panic(NULL,NULL, NO_NUM); + } + irq_mode_pci(irq); + } + } + return 0; +} + +/*===========================================================================* + * do_amd_isabr * + *===========================================================================*/ +PRIVATE int do_amd_isabr(devind) +int devind; +{ + int i, bus, dev, func, xdevind, irq, edge; + u8_t levmask; + u16_t pciirq; + + /* Find required function */ + func= AMD_ISABR_FUNC; + bus= pcidev[devind].pd_busind; + dev= pcidev[devind].pd_dev; + + /* Fake a device with the required function */ + if (nr_pcidev >= NR_PCIDEV) + server_panic("PCI","too many PCI devices", nr_pcidev); + xdevind= nr_pcidev; + pcidev[xdevind].pd_busind= bus; + pcidev[xdevind].pd_dev= dev; + pcidev[xdevind].pd_func= func; + pcidev[xdevind].pd_inuse= 1; + nr_pcidev++; + + levmask= pci_attr_r8(xdevind, AMD_ISABR_PCIIRQ_LEV); + pciirq= pci_attr_r16(xdevind, AMD_ISABR_PCIIRQ_ROUTE); + for (i= 0; i<4; i++) + { + edge= (levmask >> i) & 1; + irq= (pciirq >> (4*i)) & 0xf; + if (!irq) + { + if (debug) + printf("INT%c: disabled\n", 'A'+i); + } + else + { + if (debug) + printf("INT%c: %d\n", 'A'+i, irq); + if (edge) + { + printf("IRQ %d is not level triggered\n", + irq); + server_panic(NULL, NULL, NO_NUM); + } + irq_mode_pci(irq); + } + } + nr_pcidev--; + return 0; +} + + +/*===========================================================================* + * do_sis_isabr * + *===========================================================================*/ +PRIVATE int do_sis_isabr(devind) +int devind; +{ + int i, dev, func, irq; + + dev= pcidev[devind].pd_dev; + func= pcidev[devind].pd_func; + irq= 0; /* lint */ + for (i= 0; i<4; i++) + { + irq= pci_attr_r8(devind, SIS_ISABR_IRQ_A+i); + if (irq & SIS_IRQ_DISABLED) + { + if (debug) + printf("INT%c: disabled\n", 'A'+i); + } + else + { + irq &= SIS_IRQ_MASK; + if (debug) + printf("INT%c: %d\n", 'A'+i, irq); + irq_mode_pci(irq); + } + } + return 0; +} + + +/*===========================================================================* + * do_via_isabr * + *===========================================================================*/ +PRIVATE int do_via_isabr(devind) +int devind; +{ + int i, dev, func, irq, edge; + u8_t levmask; + + dev= pcidev[devind].pd_dev; + func= pcidev[devind].pd_func; + levmask= pci_attr_r8(devind, VIA_ISABR_EL); + irq= 0; /* lint */ + edge= 0; /* lint */ + for (i= 0; i<4; i++) + { + switch(i) + { + case 0: + edge= (levmask & VIA_ISABR_EL_INTA); + irq= pci_attr_r8(devind, VIA_ISABR_IRQ_R2) >> 4; + break; + case 1: + edge= (levmask & VIA_ISABR_EL_INTB); + irq= pci_attr_r8(devind, VIA_ISABR_IRQ_R2); + break; + case 2: + edge= (levmask & VIA_ISABR_EL_INTC); + irq= pci_attr_r8(devind, VIA_ISABR_IRQ_R3) >> 4; + break; + case 3: + edge= (levmask & VIA_ISABR_EL_INTD); + irq= pci_attr_r8(devind, VIA_ISABR_IRQ_R1) >> 4; + break; + default: + server_assert(0); + } + irq &= 0xf; + if (!irq) + { + if (debug) + printf("INT%c: disabled\n", 'A'+i); + } + else + { + if (debug) + printf("INT%c: %d\n", 'A'+i, irq); + if (edge) + { + printf("IRQ %d is not level triggered\n", + irq); + server_panic(NULL, NULL, NO_NUM); + } + irq_mode_pci(irq); + } + } + return 0; +} + + +/*===========================================================================* + * pci_vid_name * + *===========================================================================*/ +PRIVATE char *pci_vid_name(vid) +u16_t vid; +{ + int i; + + for (i= 0; pci_vendor_table[i].name; i++) + { + if (pci_vendor_table[i].vid == vid) + return pci_vendor_table[i].name; + } + return "unknown"; +} + +/*===========================================================================* + * pci_baseclass_name * + *===========================================================================*/ +PRIVATE char *pci_baseclass_name(baseclass) +u8_t baseclass; +{ + int i; + + for (i= 0; pci_baseclass_table[i].name; i++) + { + if (pci_baseclass_table[i].baseclass == baseclass) + return pci_baseclass_table[i].name; + } + return NULL; +} + +/*===========================================================================* + * pci_subclass_name * + *===========================================================================*/ +PRIVATE char *pci_subclass_name(baseclass, subclass, infclass) +u8_t baseclass; +u8_t subclass; +u8_t infclass; +{ + int i; + + for (i= 0; pci_subclass_table[i].name; i++) + { + if (pci_subclass_table[i].baseclass != baseclass) + continue; + if (pci_subclass_table[i].subclass != subclass) + continue; + if (pci_subclass_table[i].infclass != infclass && + pci_subclass_table[i].infclass != (u16_t)-1) + { + continue; + } + return pci_subclass_table[i].name; + } + return NULL; +} + +/*===========================================================================* + * ntostr * + *===========================================================================*/ +PRIVATE void ntostr(n, str, end) +unsigned n; +char **str; +char *end; +{ + char tmpstr[20]; + int i; + + if (n == 0) + { + tmpstr[0]= '0'; + i= 1; + } + else + { + for (i= 0; n; i++) + { + tmpstr[i]= '0' + (n%10); + n /= 10; + } + } + for (; i>0; i--) + { + if (*str == end) + { + break; + } + **str= tmpstr[i-1]; + (*str)++; + } + if (*str == end) + end[-1]= '\0'; + else + **str= '\0'; +} + + +/*===========================================================================* + * pci_attr_rsts * + *===========================================================================*/ +PRIVATE u16_t pci_attr_rsts(devind) +int devind; +{ + int busind; + + busind= pcidev[devind].pd_busind; + return pcibus[busind].pb_rsts(busind); +} + + +/*===========================================================================* + * pcibr_intel_rsts * + *===========================================================================*/ +PRIVATE u16_t pcibr_intel_rsts(busind) +int busind; +{ + int devind; + devind= pcibus[busind].pb_devind; + + return pci_attr_r16(devind, PPB_SSTS); +} + + +/*===========================================================================* + * pcibr_intel_wsts * + *===========================================================================*/ +PRIVATE void pcibr_intel_wsts(busind, value) +int busind; +u16_t value; +{ + int devind; + devind= pcibus[busind].pb_devind; + +#if 0 + printf("pcibr_intel_wsts(%d, 0x%X), devind= %d\n", + busind, value, devind); +#endif + pci_attr_w16(devind, PPB_SSTS, value); +} + + +/*===========================================================================* + * pcibr_via_rsts * + *===========================================================================*/ +PRIVATE u16_t pcibr_via_rsts(busind) +int busind; +{ + int devind; + devind= pcibus[busind].pb_devind; + + return 0; +} + + +/*===========================================================================* + * pcibr_via_wsts * + *===========================================================================*/ +PRIVATE void pcibr_via_wsts(busind, value) +int busind; +u16_t value; +{ + int devind; + devind= pcibus[busind].pb_devind; + +#if 0 + printf("pcibr_via_wsts(%d, 0x%X), devind= %d (not implemented)\n", + busind, value, devind); +#endif +} + + +/*===========================================================================* + * pci_attr_wsts * + *===========================================================================*/ +PRIVATE void pci_attr_wsts(devind, value) +int devind; +u16_t value; +{ + int busind; + + busind= pcidev[devind].pd_busind; + pcibus[busind].pb_wsts(busind, value); +} + + +/*===========================================================================* + * pcii_rreg8 * + *===========================================================================*/ +PRIVATE u8_t pcii_rreg8(busind, devind, port) +int busind; +int devind; +int port; +{ + u8_t v; + + + v= PCII_RREG8_(pcibus[busind].pb_bus, + pcidev[devind].pd_dev, pcidev[devind].pd_func, + port); + outl(PCII_CONFADD, PCII_UNSEL); +#if 0 + printf("pcii_rreg8(%d, %d, 0x%X): %d.%d.%d= 0x%X\n", + busind, devind, port, + pcibus[busind].pb_bus, pcidev[devind].pd_dev, + pcidev[devind].pd_func, v); +#endif + return v; +} + + +/*===========================================================================* + * pcii_rreg16 * + *===========================================================================*/ +PRIVATE u16_t pcii_rreg16(busind, devind, port) +int busind; +int devind; +int port; +{ + u16_t v; + + v= PCII_RREG16_(pcibus[busind].pb_bus, + pcidev[devind].pd_dev, pcidev[devind].pd_func, + port); + outl(PCII_CONFADD, PCII_UNSEL); +#if 0 + printf("pcii_rreg16(%d, %d, 0x%X): %d.%d.%d= 0x%X\n", + busind, devind, port, + pcibus[busind].pb_bus, pcidev[devind].pd_dev, + pcidev[devind].pd_func, v); +#endif + return v; +} + + +/*===========================================================================* + * pcii_rreg32 * + *===========================================================================*/ +PRIVATE u32_t pcii_rreg32(busind, devind, port) +int busind; +int devind; +int port; +{ + u32_t v; + + v= PCII_RREG32_(pcibus[busind].pb_bus, + pcidev[devind].pd_dev, pcidev[devind].pd_func, + port); + outl(PCII_CONFADD, PCII_UNSEL); +#if 0 + printf("pcii_rreg32(%d, %d, 0x%X): %d.%d.%d= 0x%X\n", + busind, devind, port, + pcibus[busind].pb_bus, pcidev[devind].pd_dev, + pcidev[devind].pd_func, v); +#endif + return v; +} + + +/*===========================================================================* + * pcii_wreg16 * + *===========================================================================*/ +PRIVATE void pcii_wreg16(busind, devind, port, value) +int busind; +int devind; +int port; +u16_t value; +{ +#if 0 + printf("pcii_wreg16(%d, %d, 0x%X, 0x%X): %d.%d.%d\n", + busind, devind, port, value, + pcibus[busind].pb_bus, pcidev[devind].pd_dev, + pcidev[devind].pd_func); +#endif + PCII_WREG16_(pcibus[busind].pb_bus, + pcidev[devind].pd_dev, pcidev[devind].pd_func, + port, value); + outl(PCII_CONFADD, PCII_UNSEL); +} + + +/*===========================================================================* + * pcii_wreg32 * + *===========================================================================*/ +PRIVATE void pcii_wreg32(busind, devind, port, value) +int busind; +int devind; +int port; +u32_t value; +{ +#if 0 + printf("pcii_wreg32(%d, %d, 0x%X, 0x%X): %d.%d.%d\n", + busind, devind, port, value, + pcibus[busind].pb_bus, pcidev[devind].pd_dev, + pcidev[devind].pd_func); +#endif + PCII_WREG32_(pcibus[busind].pb_bus, + pcidev[devind].pd_dev, pcidev[devind].pd_func, + port, value); + outl(PCII_CONFADD, PCII_UNSEL); +} + + +/*===========================================================================* + * pcii_rsts * + *===========================================================================*/ +PRIVATE u16_t pcii_rsts(busind) +int busind; +{ + u16_t v; + v= PCII_RREG16_(pcibus[busind].pb_bus, 0, 0, PCI_PCISTS); + outl(PCII_CONFADD, PCII_UNSEL); + return v; +} + + +/*===========================================================================* + * pcii_wsts * + *===========================================================================*/ +PRIVATE void pcii_wsts(busind, value) +int busind; +u16_t value; +{ + PCII_WREG16_(pcibus[busind].pb_bus, 0, 0, PCI_PCISTS, value); + outl(PCII_CONFADD, PCII_UNSEL); +} +#endif /* ENABLE_PCI */ + +/* + * $PchId: pci.c,v 1.7 2003/08/07 09:06:51 philip Exp $ + */ diff --git a/kernel/pci.h b/kernel/pci.h new file mode 100755 index 000000000..16865d949 --- /dev/null +++ b/kernel/pci.h @@ -0,0 +1,97 @@ +/* +pci.h + +Created: Jan 2000 by Philip Homburg <philip@cs.vu.nl> +*/ + +#define PCI_VID 0x00 /* Vendor ID, 16-bit */ +#define PCI_DID 0x02 /* Device ID, 16-bit */ +#define PCI_CR 0x04 /* Command Register, 16-bit */ +#define PCI_PCISTS 0x06 /* PCI status, 16-bit */ +#define PSR_SSE 0x4000 /* Signaled System Error */ +#define PSR_RMAS 0x2000 /* Received Master Abort Status */ +#define PSR_RTAS 0x1000 /* Received Target Abort Status */ +#define PCI_PIFR 0x09 /* Prog. Interface Register */ +#define PCI_SCR 0x0A /* Sub-Class Register */ +#define PCI_BCR 0x0B /* Base-Class Register */ +#define PCI_HEADT 0x0E /* Header type, 8-bit */ +#define PHT_MULTIFUNC 0x80 /* Multiple functions */ +#define PCI_BAR 0x10 /* Base Address Register */ +#define PCI_ILR 0x3C /* Interrupt Line Register */ +#define PCI_IPR 0x3D /* Interrupt Pin Register */ + +/* PCI bridge devices (AGP) */ +#define PPB_SBUSN 0x19 /* Secondary Bus Number */ + +/* Intel compatible PCI bridge devices (AGP) */ +#define PPB_SSTS 0x1E /* Secondary PCI-to-PCI Status Register */ + +#define NO_VID 0xffff /* No PCI card present */ + +struct pci_vendor +{ + u16_t vid; + char *name; +}; + +struct pci_device +{ + u16_t vid; + u16_t did; + char *name; +}; + +struct pci_baseclass +{ + u8_t baseclass; + char *name; +}; + +struct pci_subclass +{ + u8_t baseclass; + u8_t subclass; + u16_t infclass; + char *name; +}; + +struct pci_intel_ctrl +{ + u16_t vid; + u16_t did; +}; + +struct pci_isabridge +{ + u16_t vid; + u16_t did; + int checkclass; + int type; +}; + +struct pci_pcibridge +{ + u16_t vid; + u16_t did; + int type; +}; + +#define PCI_IB_PIIX 1 /* Intel PIIX compatible ISA bridge */ +#define PCI_IB_VIA 2 /* VIA compatible ISA bridge */ +#define PCI_IB_AMD 3 /* AMD compatible ISA bridge */ +#define PCI_IB_SIS 4 /* SIS compatible ISA bridge */ + +#define PCI_AGPB_INTEL 1 /* Intel compatible AGP bridge */ +#define PCI_AGPB_VIA 2 /* VIA compatible AGP bridge */ + +extern struct pci_vendor pci_vendor_table[]; +extern struct pci_device pci_device_table[]; +extern struct pci_baseclass pci_baseclass_table[]; +extern struct pci_subclass pci_subclass_table[]; +extern struct pci_intel_ctrl pci_intel_ctrl[]; +extern struct pci_isabridge pci_isabridge[]; +extern struct pci_pcibridge pci_pcibridge[]; + +/* + * $PchId: pci.h,v 1.4 2001/12/06 20:21:22 philip Exp $ + */ diff --git a/kernel/pci_amd.h b/kernel/pci_amd.h new file mode 100755 index 000000000..2d709d2f3 --- /dev/null +++ b/kernel/pci_amd.h @@ -0,0 +1,21 @@ +/* +pci_amd.h + +Created: Nov 2001 by Philip Homburg <philip@cs.vu.nl> +*/ + +#define AMD_ISABR_FUNC 3 /* Registers are in function 3 */ +#define AMD_ISABR_PCIIRQ_LEV 0x54 +#define AMD_PCILEV_INTA 0x1 +#define AMD_PCILEV_INTB 0x2 +#define AMD_PCILEV_INTC 4x2 +#define AMD_PCILEV_INTD 4x8 +#define AMD_ISABR_PCIIRQ_ROUTE 0x56 +#define AMD_PCIIRQ_INTA_MASK 0x000F +#define AMD_PCIIRQ_INTB_MASK 0x00F0 +#define AMD_PCIIRQ_INTC_MASK 0x0F00 +#define AMD_PCIIRQ_INTD_MASK 0xF000 + +/* + * $PchId: pci_amd.h,v 1.1 2001/11/09 19:57:37 philip Exp $ + */ diff --git a/kernel/pci_intel.h b/kernel/pci_intel.h new file mode 100755 index 000000000..50c59f0ee --- /dev/null +++ b/kernel/pci_intel.h @@ -0,0 +1,58 @@ +/* +pci_intel.h + +Created: Jan 2000 by Philip Homburg <philip@cs.vu.nl> +*/ + +#define PCII_CONFADD 0xCF8 +#define PCIIC_CODE 0x80000000 +#define PCIIC_BUSNUM_MASK 0xff0000 +#define PCIIC_BUSNUM_SHIFT 16 +#define PCIIC_DEVNUM_MASK 0xf800 +#define PCIIC_DEVNUM_SHIFT 11 +#define PCIIC_FUNCNUM_MASK 0x700 +#define PCIIC_FUNCNUM_SHIFT 8 +#define PCIIC_REGNUM_MASK 0xfc +#define PCIIC_REGNUM_SHIFT 2 +#define PCII_CONFDATA 0xCFC + +#define PCII_SELREG_(bus, dev, func, reg) \ + (PCIIC_CODE | \ + (((bus) << PCIIC_BUSNUM_SHIFT) & PCIIC_BUSNUM_MASK) | \ + (((dev) << PCIIC_DEVNUM_SHIFT) & PCIIC_DEVNUM_MASK) | \ + (((func) << PCIIC_FUNCNUM_SHIFT) & PCIIC_FUNCNUM_MASK) | \ + ((((reg)/4) << PCIIC_REGNUM_SHIFT) & PCIIC_REGNUM_MASK)) +#define PCII_UNSEL (0) + +#define PCII_RREG8_(bus, dev, func, reg) \ + (outl(PCII_CONFADD, PCII_SELREG_(bus, dev, func, reg)), \ + inb(PCII_CONFDATA+((reg)&3))) +#define PCII_RREG16_(bus, dev, func, reg) \ + (PCII_RREG8_(bus, dev, func, reg) | \ + (PCII_RREG8_(bus, dev, func, reg+1) << 8)) +#define PCII_RREG32_(bus, dev, func, reg) \ + (PCII_RREG16_(bus, dev, func, reg) | \ + (PCII_RREG16_(bus, dev, func, reg+2) << 16)) + +#define PCII_WREG8_(bus, dev, func, reg, val) \ + (outl(PCII_CONFADD, PCII_SELREG_(bus, dev, func, reg)), \ + outb(PCII_CONFDATA+((reg)&3), (val))) +#define PCII_WREG16_(bus, dev, func, reg, val) \ + (PCII_WREG8_(bus, dev, func, reg, (val)), \ + (PCII_WREG8_(bus, dev, func, reg+1, (val) >> 8))) +#define PCII_WREG32_(bus, dev, func, reg, val) \ + (PCII_WREG16_(bus, dev, func, reg, (val)), \ + (PCII_WREG16_(bus, dev, func, reg+2, (val) >> 16))) + +/* PIIX configuration registers */ +#define PIIX_PIRQRCA 0x60 +#define PIIX_IRQ_DI 0x80 +#define PIIX_IRQ_MASK 0x0F + +/* PIIX extensions to the PIC */ +#define PIIX_ELCR1 0x4D0 +#define PIIX_ELCR2 0x4D1 + +/* + * $PchId: pci_intel.h,v 1.1 2000/08/12 11:20:17 philip Exp $ + */ diff --git a/kernel/pci_sis.h b/kernel/pci_sis.h new file mode 100755 index 000000000..252300952 --- /dev/null +++ b/kernel/pci_sis.h @@ -0,0 +1,17 @@ +/* +pci_sis.h + +Created: Nov 2001 by Philip Homburg <philip@cs.vu.nl> +*/ + +/* Constants are taken from pci-irq.c in the Linux kernel source */ +#define SIS_ISABR_IRQ_A 0x41 /* IRQA routing */ +#define SIS_ISABR_IRQ_B 0x42 /* IRQB routing */ +#define SIS_ISABR_IRQ_C 0x43 /* IRQC routing */ +#define SIS_ISABR_IRQ_D 0x44 /* IRQD routing */ +#define SIS_IRQ_DISABLED 0x80 +#define SIS_IRQ_MASK 0x0F + +/* + * $PchId: pci_sis.h,v 1.1 2001/12/06 20:22:52 philip Exp $ + */ diff --git a/kernel/pci_table.c b/kernel/pci_table.c new file mode 100755 index 000000000..deaac96eb --- /dev/null +++ b/kernel/pci_table.c @@ -0,0 +1,251 @@ +/* +pci_table.c + +Tables with PCI vendor and device identifiers + +Created: Jan 2000 by Philip Homburg <philip@cs.vu.nl> + +See the Linux PCI ID Repository <http://pciids.sourceforge.net/>. +*/ + +/* Changes from original Minix 2.0.4 version (2003-09-05): + * 2003-11-30 (kjb) Minix 2.0.4 FIX.TAZ add D-Link RTL8139 (0x1186, 0x1300) + * 2004-08-08 (asw) add Intel 82371AB (0x8086, 0x7100) + */ + +#include "kernel.h" +#include "pci.h" +#if __minix_vmd +#include "config.h" +#endif + +#if ENABLE_PCI + +struct pci_vendor pci_vendor_table[]= +{ + { 0x1000, "NCR" }, + { 0x1002, "ATI Technologies" }, + { 0x100B, "National Semiconductor Corporation" }, + { 0x1013, "Cirrus Logic" }, + { 0x1022, "Advanced Micro Devices" }, + { 0x102B, "Matrox Graphics, Inc." }, + { 0x1039, "Silicon Integrated Systems (SiS)" }, + { 0x104C, "Texas Instruments" }, + { 0x105A, "Promise Technology" }, + { 0x10B7, "3Com Corporation" }, + { 0x10B9, "AcerLabs (ALI)" }, + { 0x10DE, "nVidia Corporation" }, + { 0x10EC, "Realtek" }, + { 0x1106, "VIA" }, + { 0x110A, "Siemens Nixdorf AG" }, + { 0x125D, "ESS Technology" }, + { 0x1274, "Ensoniq" }, + { 0x5333, "S3" }, + { 0x8086, "Intel" }, + { 0x9004, "Adaptec" }, + { 0x9005, "Adaptec" }, + { 0x0000, NULL } +}; + +struct pci_device pci_device_table[]= +{ + { 0x1000, 0x0001, "NCR 53C810" }, + { 0x1000, 0x000F, "NCR 53C875" }, + { 0x1002, 0x4752, "ATI Rage XL PCI" }, + { 0x100B, 0xD001, "Nat. Semi. 87410" }, + { 0x1013, 0x00B8, "Cirrus Logic GD 5446" }, + { 0x1013, 0x6003, "Cirrus Logic CS4614/22/24 CrystalClear" }, + { 0x1022, 0x700C, "AMD-762 CPU to PCI Bridge (SMP chipset)" }, + { 0x1022, 0x700D, "AMD-762 CPU to PCI Bridge (AGP 4x)" }, + { 0x1022, 0x7410, "AMD-766 PCI to ISA/LPC Bridge" }, + { 0x1022, 0x7411, "AMD-766 EIDE Controller" }, + { 0x102B, 0x051B, "Matrox MGA 2164W [Millennium II]" }, + { 0x102B, 0x0525, "Matrox MGA G400 AGP" }, + { 0x1039, 0x0008, "SiS 85C503/5513" }, + { 0x1039, 0x0200, "SiS 5597/5598 VGA" }, + { 0x1039, 0x0406, "SiS 85C501/2" }, + { 0x1039, 0x5597, "SiS 5582" }, + { 0x104C, 0xAC1C, "TI PCI1225" }, + { 0x105A, 0x0D30, "Promise Technology 20265" }, + { 0x10B7, 0x9058, "3Com 3c905B-Combo" }, + { 0x10B7, 0x9805, "3Com 3c980-TX Python-T" }, + { 0x10B9, 0x1533, "ALI M1533 ISA-bridge [Aladdin IV]" }, + { 0x10B9, 0x1541, "ALI M1541" }, + { 0x10B9, 0x5229, "ALI M5229 (IDE)" }, + { 0x10B9, 0x5243, "ALI M5243" }, + { 0x10B9, 0x7101, "ALI M7101 PMU" }, + { 0x10DE, 0x0020, "nVidia Riva TnT [NV04]" }, + { 0x10DE, 0x0110, "nVidia GeForce2 MX [NV11]" }, + { 0x10EC, 0x8029, "Realtek RTL8029" }, + { 0x10EC, 0x8139, "Realtek RTL8139" }, + { 0x1106, 0x0305, "VIA VT8363/8365 [KT133/KM133]" }, + { 0x1106, 0x0571, "VIA IDE controller" }, + { 0x1106, 0x0686, "VIA VT82C686 (Apollo South Bridge)" }, + { 0x1106, 0x3038, "VT83C572 PCI USB Controller" }, + { 0x1106, 0x3057, "VT82C686A ACPI Power Management Controller" }, + { 0x1106, 0x3058, "VIA AC97 Audio Controller" }, + { 0x1106, 0x3059, "VIA AC97 Audio Controller" }, + { 0x1106, 0x3074, "VIA VT8233" }, + { 0x1106, 0x3099, "VIA VT8367 [KT266]" }, + { 0x1106, 0x8305, "VIA VT8365 [KM133 AGP]" }, + { 0x1106, 0xB099, "VIA VT8367 [KT266 AGP]" }, + { 0x110A, 0x0005, "Siemens Nixdorf Tulip Cntlr., Power Management" }, + { 0x1186, 0x1300, "D-Link RTL8139" }, + { 0x125D, 0x1969, "ESS ES1969 Solo-1 Audiodrive" }, + { 0x1274, 0x1371, "Ensoniq ES1371 [AudioPCI-97]" }, + { 0x1274, 0x5000, "Ensoniq ES1370" }, + { 0x1274, 0x5880, "Ensoniq CT5880 [AudioPCI]" }, + { 0x5333, 0x8811, "S3 86c764/765 [Trio32/64/64V+]" }, + { 0x5333, 0x883d, "S3 Virge/VX" }, + { 0x5333, 0x88d0, "S3 Vision 964 vers 0" }, + { 0x5333, 0x8a01, "S3 Virge/DX or /GX" }, + { 0x8086, 0x1004, "Intel 82543GC Gigabit Ethernet Controller" }, + { 0x8086, 0x1229, "Intel 82557" }, + { 0x8086, 0x122D, "Intel 82437FX" }, + { 0x8086, 0x122E, "Intel 82371FB (PIIX)" }, + { 0x8086, 0x1230, "Intel 82371FB (IDE)" }, + { 0x8086, 0x1237, "Intel 82441FX (440FX)" }, + { 0x8086, 0x1250, "Intel 82439HX" }, + { 0x8086, 0x7000, "Intel 82371SB" }, + { 0x8086, 0x7010, "Intel 82371SB (IDE)" }, + { 0x8086, 0x7020, "Intel 82371SB (USB)" }, + { 0x8086, 0x7100, "Intel 82371AB" }, + { 0x8086, 0x7110, "Intel 82371AB (PIIX4)" }, + { 0x8086, 0x7111, "Intel 82371AB (IDE)" }, + { 0x8086, 0x7112, "Intel 82371AB (USB)" }, + { 0x8086, 0x7113, "Intel 82371AB (Power)" }, + { 0x8086, 0x7190, "Intel 82443BX" }, + { 0x8086, 0x7191, "Intel 82443BX (AGP bridge)" }, + { 0x9004, 0x8178, "Adaptec AHA-2940U/2940UW Ultra/Ultra-Wide SCSI Ctrlr" }, + { 0x9005, 0x0080, "Adaptec AIC-7892A Ultra160/m PCI SCSI Controller" }, + { 0x0000, 0x0000, NULL } +}; + +struct pci_baseclass pci_baseclass_table[]= +{ + { 0x00, "No device class" }, + { 0x01, "Mass storage controller" }, + { 0x02, "Network controller" }, + { 0x03, "Display controller" }, + { 0x04, "Multimedia device" }, + { 0x05, "Memory controller" }, + { 0x06, "Bridge device" }, + { 0x07, "Simple comm. controller" }, + { 0x08, "Base system peripheral" }, + { 0x09, "Input device" }, + { 0x0A, "Docking station" }, + { 0x0B, "Processor" }, + { 0x0C, "Serial bus controller" }, + { 0x0d, "Wireless controller" }, + { 0x0e, "Intelligent I/O controller" }, + { 0x0f, "Satellite comm. controller" }, + { 0x10, "Encryption/decryption controller" }, + { 0x11, "Data acquisition controller" }, + { 0xff, "Misc. device" }, + + { 0x00, NULL } +}; + +/* -1 in the infclass field is a wildcard for infclass */ +struct pci_subclass pci_subclass_table[]= +{ + { 0x00, 0x01, 0x00, "VGA-compatible device" }, + + { 0x01, 0x00, 0x00, "SCSI bus controller" }, + { 0x01, 0x01, -1, "IDE controller" }, + { 0x01, 0x02, 0x00, "Floppy disk controller" }, + { 0x01, 0x03, 0x00, "IPI controller" }, + { 0x01, 0x04, 0x00, "RAID controller" }, + { 0x01, 0x80, 0x00, "Other mass storage controller" }, + + { 0x02, 0x00, 0x00, "Ethernet controller" }, + { 0x02, 0x01, 0x00, "Token Ring controller" }, + { 0x02, 0x02, 0x00, "FDDI controller" }, + { 0x02, 0x03, 0x00, "ATM controller" }, + { 0x02, 0x04, 0x00, "ISDN controller" }, + { 0x02, 0x80, 0x00, "Other network controller" }, + + { 0x03, 0x00, 0x00, "VGA-compatible controller" }, + { 0x03, 0x00, 0x01, "8514-compatible controller" }, + { 0x03, 0x01, 0x00, "XGA controller" }, + { 0x03, 0x02, 0x00, "3D controller" }, + { 0x03, 0x80, 0x00, "Other display controller" }, + + { 0x04, 0x00, 0x00, "Video device" }, + { 0x04, 0x01, 0x00, "Audio device" }, + { 0x04, 0x02, 0x00, "Computer telephony device" }, + { 0x04, 0x80, 0x00, "Other multimedia device" }, + + { 0x06, 0x00, 0x00, "Host bridge" }, + { 0x06, 0x01, 0x00, "ISA bridge" }, + { 0x06, 0x02, 0x00, "EISA bridge" }, + { 0x06, 0x03, 0x00, "MCA bridge" }, + { 0x06, 0x04, 0x00, "PCI-to-PCI bridge" }, + { 0x06, 0x04, 0x01, "Subtractive decode PCI-to-PCI bridge" }, + { 0x06, 0x05, 0x00, "PCMCIA bridge" }, + { 0x06, 0x06, 0x00, "NuBus bridge" }, + { 0x06, 0x07, 0x00, "CardBus bridge" }, + { 0x06, 0x08, -1, "RACEway bridge" }, + { 0x06, 0x09, -1, "Semi-transparent PCI-to-PCI bridge" }, + { 0x06, 0x80, 0x00, "Other bridge device" }, + + { 0x0C, 0x00, 0x00, "IEEE 1394 (FireWire)" }, + { 0x0C, 0x00, 0x10, "IEEE 1394 (OpenHCI)" }, + { 0x0C, 0x01, 0x00, "ACCESS bus" }, + { 0x0C, 0x02, 0x00, "SSA" }, + { 0x0C, 0x03, 0x00, "USB (with UHC)" }, + { 0x0C, 0x03, 0x10, "USB (with OHC)" }, + { 0x0C, 0x03, 0x80, "USB (other host inf.)" }, + { 0x0C, 0x03, 0xFE, "USB device" }, + { 0x0C, 0x04, 0x00, "Fibre Channel" }, + { 0x0C, 0x05, 0x00, "SMBus" }, + + { 0x00, 0x00, 0x00, NULL } +}; + +struct pci_intel_ctrl pci_intel_ctrl[]= +{ + { 0x1022, 0x700C, }, /* AMD-762 */ + { 0x1039, 0x0406, }, /* SiS 85C501/2 */ + { 0x1039, 0x5597, }, /* SiS 5582 */ + { 0x10B9, 0x1541, }, /* ALI M1541 */ + { 0x1106, 0x0305, }, /* VIA VT8363/8365 */ + { 0x1106, 0x3099, }, /* VIA VT8367 [KT266] */ + { 0x1106, 0x3188, }, /* VIA */ + { 0x1106, 0x0204, }, /* VIA VT8367 [KT266] */ + { 0x8086, 0x122D, }, /* Intel 82437FX */ + { 0x8086, 0x1237, }, /* Intel 82441FX */ + { 0x8086, 0x1250, }, /* Intel 82439HX */ + { 0x8086, 0x7100, }, /* Intel 82371AB */ + { 0x8086, 0x7190, }, /* Intel 82443BX */ + { 0x0000, 0x0000, }, +}; + +struct pci_isabridge pci_isabridge[]= +{ + { 0x1022, 0x7410, 1, PCI_IB_AMD, }, /* AMD-766 */ + { 0x1039, 0x0008, 1, PCI_IB_SIS, }, /* SiS 85C503/5513 */ + { 0x10B9, 0x1533, 1, PCI_IB_PIIX, }, /* ALI M1533 */ + { 0x1106, 0x0686, 1, PCI_IB_VIA, }, /* VIA VT82C686 */ + { 0x1106, 0x3074, 1, PCI_IB_VIA, }, /* VIA VT8233 */ + { 0x1106, 0x3227, 1, PCI_IB_VIA, }, /* VIA */ + { 0x8086, 0x122E, 1, PCI_IB_PIIX, }, /* Intel 82371FB */ + { 0x8086, 0x7000, 1, PCI_IB_PIIX, }, /* Intel 82371SB */ + { 0x8086, 0x7100, 1, PCI_IB_PIIX, }, /* Intel 82371AB */ + { 0x8086, 0x7110, 1, PCI_IB_PIIX, }, /* Intel PIIX4 */ + { 0x0000, 0x0000, 0, 0, }, +}; + +struct pci_pcibridge pci_pcibridge[]= +{ + { 0x8086, 0x7191, PCI_AGPB_INTEL, }, /* Intel 82443BX (AGP bridge) */ + { 0x1022, 0x700D, PCI_AGPB_INTEL, }, /* AMD-762 (AGP 4x) */ + { 0x10B9, 0x5243, PCI_AGPB_INTEL, }, /* ALI M5243 */ + { 0x1106, 0x8305, PCI_AGPB_VIA, }, /* VIA VT8365 [KM133 AGP] */ + { 0x0000, 0x0000, 0, }, +}; +#endif /* ENABLE_PCI */ + +/* + * $PchId: pci_table.c,v 1.7 2003/09/05 10:53:22 philip Exp $ + */ diff --git a/kernel/pci_via.h b/kernel/pci_via.h new file mode 100755 index 000000000..3d2fee378 --- /dev/null +++ b/kernel/pci_via.h @@ -0,0 +1,27 @@ +/* +pci_via.h + +Created: Jun 2001 by Philip Homburg <philip@cs.vu.nl> +*/ + +#define VIA_ISABR_EL 0x54 /* Edge or level triggered */ +#define VIA_ISABR_EL_INTA 0x08 /* Edge (1) or level (0) */ +#define VIA_ISABR_EL_INTB 0x04 +#define VIA_ISABR_EL_INTC 0x02 +#define VIA_ISABR_EL_INTD 0x01 + +#define VIA_ISABR_IRQ_R1 0x55 /* IRQ routing 1 */ +#define VIA_ISABR_IRQ_INTD 0xf0 /* routing for INTD */ +#define VIA_ISABR_IRQ_INT0 0x0f /* routing for INT0 */ +#define VIA_ISABR_IRQ_R2 0x56 /* IRQ routing 2 */ +#define VIA_ISABR_IRQ_INTA 0xf0 /* routing for INTA */ +#define VIA_ISABR_IRQ_INTB 0x0f /* routing for INTB */ +#define VIA_ISABR_IRQ_R3 0x57 /* IRQ routing 3 */ +#define VIA_ISABR_IRQ_INTC 0xf0 /* routing for INTC */ +#define VIA_ISABR_IRQ_INT1 0x0f /* routing for INT1 */ +#define VIA_ISABR_IRQ_R4 0x58 /* IRQ routing 4 */ +#define VIA_ISABR_IRQ_INT2 0x0f /* routing for INT2 */ + +/* + * $PchId: pci_via.h,v 1.1 2001/06/20 15:50:25 philip Exp $ + */ diff --git a/kernel/proc.c b/kernel/proc.c new file mode 100755 index 000000000..8c00d3433 --- /dev/null +++ b/kernel/proc.c @@ -0,0 +1,595 @@ +/* This file contains essentially all of the process and message handling. + * It has two main entry points from the outside: + * + * sys_call: a system call, that is, the kernel is trapped with an INT + * notify: notify process of a system event (notifications aren't queued) + * + * It also has several minor entry points: + * + * lock_ready: put a process on one of the ready queues so it can be run + * lock_unready: remove a process from the ready queues + * lock_sched: a process has run too long; schedule another one + * lock_pick_proc: pick a process to run (used by system initialization) + * unhold: repeat all held-up notifications + * + * Changes: + * Nov 05, 2004 removed lock_mini_send() (Jorrit N. Herder) + * Oct 28, 2004 non-blocking SEND and RECEIVE (Jorrit N. Herder) + * Oct 28, 2004 rewrite of sys_call() (Jorrit N. Herder) + * Oct 10, 2004 require BOTH for kernel sys_call() (Jorrit N. Herder) + * (to protect kernel tasks from being blocked) + * Sep 25, 2004 generalized notify() function (Jorrit N. Herder) + * Sep 23, 2004 removed MM sig check in mini_rec() (Jorrit N. Herder) + * Aug 19, 2004 generalized ready()/unready() (Jorrit N. Herder) + * Aug 18, 2004 added notify() function (Jorrit N. Herder) + * May 01, 2004 check p_sendmask in mini_send() (Jorrit N. Herder) + */ + +#include "kernel.h" +#include <minix/callnr.h> +#include <minix/com.h> +#include "proc.h" +#include "sendmask.h" + +PRIVATE unsigned char switching; /* nonzero to inhibit notify() */ + +FORWARD _PROTOTYPE( int mini_send, (struct proc *caller_ptr, int dest, + message *m_ptr, int may_block) ); +FORWARD _PROTOTYPE( int mini_rec, (struct proc *caller_ptr, int src, + message *m_ptr, int may_block) ); +FORWARD _PROTOTYPE( void ready, (struct proc *rp) ); +FORWARD _PROTOTYPE( void sched, (void) ); +FORWARD _PROTOTYPE( void unready, (struct proc *rp) ); +FORWARD _PROTOTYPE( void pick_proc, (void) ); + +#if (CHIP == M68000) +FORWARD _PROTOTYPE( void cp_mess, (int src, struct proc *src_p, message *src_m, + struct proc *dst_p, message *dst_m) ); +#endif + +#if (CHIP == INTEL) +#define CopyMess(s,sp,sm,dp,dm) \ + cp_mess(s, (sp)->p_memmap[D].mem_phys, (vir_bytes)sm, (dp)->p_memmap[D].mem_phys, (vir_bytes)dm) +#endif /* (CHIP == INTEL) */ + +#if (CHIP == M68000) +/* M68000 does not have cp_mess() in assembly like INTEL. Declare prototype + * for cp_mess() here and define the function below. Also define CopyMess. + */ +#endif /* (CHIP == M68000) */ + + +/* Bit mask operations used to bits of the notification mask. */ +#define set_bit(mask, n) ((mask) |= (1 << (n))) +#define clear_bit(mask, n) ((mask) &= ~(1 << (n))) +#define isset_bit(mask, n) ((mask) & (1 << (n))) + + +/*===========================================================================* + * notify * + *===========================================================================*/ +PUBLIC void notify(proc_nr, notify_type) +int proc_nr; /* number of process to be started */ +int notify_type; /* notification to be sent */ +{ +/* A system event has occurred. Send a notification with source HARDWARE to + * the given process. The notify() function was carefully designed so that it + * (1) can be used safely from both interrupt handlers and the task level, and + * (2) realizes asynchronous message passing with at least once semantics, + * that is, the notifications are not queued. If a race condition occurs, the + * notification is queued and repeated later by unhold(). If the receiver is + * not ready, the notification is blocked and checked later in receive(). + */ + register struct proc *rp; /* pointer to task's proc entry */ + message m; /* message to send the notification */ + unsigned int notify_bit; /* bit for this notification */ + + /* Get notify bit and process pointer. */ + notify_bit = (unsigned int) (notify_type - NOTIFICATION); + rp = proc_addr(proc_nr); + + /* If this call would compete with other process-switching functions, put + * it on the 'held' queue to be flushed at the next non-competing restart(). + * The competing conditions are: + * (1) k_reenter == (typeof k_reenter) -1: + * Call from the task level, typically from an output interrupt + * routine. An interrupt handler might reenter notify(). Rare, + * so not worth special treatment. + * (2) k_reenter > 0: + * Call from a nested interrupt handler. A previous interrupt + * handler might be inside notify() or sys_call(). + * (3) switching != 0: + * A process-switching function other than notify() is being called + * from the task level, typically sched() from CLOCK. An interrupt + * handler might call notify() and pass the 'k_reenter' test. + */ + if (k_reenter != 0 || switching) { + lock(); + if (! rp->p_ntf_held) { /* already on held queue? */ + if (held_head != NIL_PROC) + held_tail->p_ntf_nextheld = rp; + else + held_head = rp; + held_tail = rp; + rp->p_ntf_nextheld = NIL_PROC; + } + set_bit(rp->p_ntf_held, notify_bit); /* add bit to held mask */ + unlock(); + return; + } + switching = TRUE; + + /* If process is not waiting for a notification, record the blockage. */ + if ( (rp->p_flags & (RECEIVING | SENDING)) != RECEIVING || + !isrxhardware(rp->p_getfrom)) { + set_bit(rp->p_ntf_blocked, notify_bit); /* add bit to blocked mask */ + switching = FALSE; + return; + } + + /* Destination is waiting for a notification. Send it a message with source + * HARDWARE and type 'notify_type'. No more information can be reliably + * provided since notifications are not queued. + */ + m.m_source = HARDWARE; /* direct copy does not work for servers */ + m.m_type = notify_type; + CopyMess(HARDWARE, proc_addr(HARDWARE), &m, rp, rp->p_messbuf); + rp->p_flags &= ~RECEIVING; + clear_bit(rp->p_ntf_blocked, notify_bit); + + /* Announce the process ready and select a fresh process to run. */ + ready(rp); + pick_proc(); + switching = FALSE; +} + +/*===========================================================================* + * sys_call * + *===========================================================================*/ +PUBLIC int sys_call(call_nr, src_dst, m_ptr) +int call_nr; /* (NB_)SEND, (NB_)RECEIVE, BOTH */ +int src_dst; /* source to receive from or dest to send to */ +message *m_ptr; /* pointer to message in the caller's space */ +{ +/* System calls are done by trapping to the kernel with an INT instruction. + * The trap is caught and sys_call() is called to send or receive a message + * (or both). The caller is always given by 'proc_ptr'. + */ + register struct proc *caller_ptr = proc_ptr; /* get pointer to caller */ + int function = call_nr & SYSCALL_FUNC; /* get system call function */ + int may_block = ! (call_nr & NON_BLOCKING); /* (dis)allow blocking? */ + int mask_entry; /* bit to check in send mask */ + int result; /* the system call's result */ + + /* Calls directed to the kernel may only be sendrec(), because tasks always + * reply and may not block if the caller doesn't do receive(). Users also + * may only use sendrec() to protect the MM and FS. + */ + if ((iskernel(src_dst) || isuserp(caller_ptr)) && function != BOTH) { + result = ECALLDENIED; /* BOTH was required */ + } + + /* Verify that requested source and/ or destination is a valid process. */ + else if (! isoksrc_dst(src_dst)) { + result = EBADSRCDST; /* invalid process number */ + } + + /* Now check if the call is known and try to perform the request. The only + * system calls that exist in MINIX are sending and receiving messages. + * Receiving is straightforward. Sending requires checks to see if sending + * is allowed by the caller's send mask and to see if the destination is + * alive. + */ + else { + switch(function) { + case SEND: + /* fall through, SEND is done in BOTH */ + case BOTH: + if (! isalive(src_dst)) { + result = EDEADDST; /* cannot send to the dead */ + break; + } + mask_entry = isuser(src_dst) ? USER_PROC_NR : src_dst; + if (! isallowed(caller_ptr->p_sendmask, mask_entry)) { + kprintf("WARNING: sys_call denied %d ", caller_ptr->p_nr); + kprintf("sending to %d\n", proc_addr(src_dst)->p_nr); + result = ECALLDENIED; /* call denied by send mask */ + break; + } + result = mini_send(caller_ptr, src_dst, m_ptr, may_block); + if (function == SEND || result != OK) { + break; /* done, or SEND failed */ + } /* fall through for BOTH */ + case RECEIVE: + result = mini_rec(caller_ptr, src_dst, m_ptr, may_block); + break; + default: + result = EBADCALL; /* illegal system call */ + } + } + + /* Now, return the result of the system call to the caller. */ + return(result); +} + + +/*===========================================================================* + * mini_send * + *===========================================================================*/ +PRIVATE int mini_send(caller_ptr, dest, m_ptr, may_block) +register struct proc *caller_ptr; /* who is trying to send a message? */ +int dest; /* to whom is message being sent? */ +message *m_ptr; /* pointer to message buffer */ +int may_block; /* (dis)allow blocking */ +{ +/* Send a message from 'caller_ptr' to 'dest'. If 'dest' is blocked waiting + * for this message, copy the message to it and unblock 'dest'. If 'dest' is + * not waiting at all, or is waiting for another source, queue 'caller_ptr'. + */ + register struct proc *dest_ptr, *next_ptr; + vir_bytes vb; /* message buffer pointer as vir_bytes */ + vir_clicks vlo, vhi; /* virtual clicks containing message to send */ + + dest_ptr = proc_addr(dest); /* pointer to destination's proc entry */ + +#if ALLOW_GAP_MESSAGES + /* This check allows a message to be anywhere in data or stack or gap. + * It will have to be made more elaborate later for machines which + * don't have the gap mapped. + */ + vb = (vir_bytes) m_ptr; + vlo = vb >> CLICK_SHIFT; /* vir click for bottom of message */ + vhi = (vb + MESS_SIZE - 1) >> CLICK_SHIFT; /* vir click for top of msg */ + if (vlo < caller_ptr->p_memmap[D].mem_vir || vlo > vhi || + vhi >= caller_ptr->p_memmap[S].mem_vir + caller_ptr->p_memmap[S].mem_len) + return(EFAULT); +#else + /* Check for messages wrapping around top of memory or outside data seg. */ + vb = (vir_bytes) m_ptr; + vlo = vb >> CLICK_SHIFT; /* vir click for bottom of message */ + vhi = (vb + MESS_SIZE - 1) >> CLICK_SHIFT; /* vir click for top of msg */ + if (vhi < vlo || + vhi - caller_ptr->p_memmap[D].mem_vir >= caller_ptr->p_memmap[D].mem_len) + return(EFAULT); +#endif + + /* Check for deadlock by 'caller_ptr' and 'dest' sending to each other. */ + if (dest_ptr->p_flags & SENDING) { + next_ptr = proc_addr(dest_ptr->p_sendto); + while (TRUE) { + if (next_ptr == caller_ptr) return(ELOCKED); + if (next_ptr->p_flags & SENDING) + next_ptr = proc_addr(next_ptr->p_sendto); + else + break; + } + } + + /* Check to see if 'dest' is blocked waiting for this message. */ + if ( (dest_ptr->p_flags & (RECEIVING | SENDING)) == RECEIVING && + (dest_ptr->p_getfrom == ANY || + dest_ptr->p_getfrom == proc_number(caller_ptr))) { + /* Destination is indeed waiting for this message. */ + CopyMess(proc_number(caller_ptr), caller_ptr, m_ptr, dest_ptr, + dest_ptr->p_messbuf); + dest_ptr->p_flags &= ~RECEIVING; /* deblock destination */ + if (dest_ptr->p_flags == 0) ready(dest_ptr); + } else if (may_block) { + /* Destination is not waiting. Block and queue caller. */ + caller_ptr->p_messbuf = m_ptr; + if (caller_ptr->p_flags == 0) unready(caller_ptr); + caller_ptr->p_flags |= SENDING; + caller_ptr->p_sendto= dest; + + /* Process is now blocked. Put in on the destination's queue. */ + if ( (next_ptr = dest_ptr->p_callerq) == NIL_PROC) + dest_ptr->p_callerq = caller_ptr; + else { + while (next_ptr->p_sendlink != NIL_PROC) + next_ptr = next_ptr->p_sendlink; + next_ptr->p_sendlink = caller_ptr; + } + caller_ptr->p_sendlink = NIL_PROC; + } else { + return(ENOTREADY); + } + return(OK); +} + +/*===========================================================================* + * mini_rec * + *===========================================================================*/ +PRIVATE int mini_rec(caller_ptr, src, m_ptr, may_block) +register struct proc *caller_ptr; /* process trying to get message */ +int src; /* which message source is wanted */ +message *m_ptr; /* pointer to message buffer */ +int may_block; /* (dis)allow blocking */ +{ +/* A process or task wants to get a message. If one is already queued, + * acquire it and deblock the sender. If no message from the desired source + * is available, block the caller. + */ + register struct proc *sender_ptr; + register struct proc *previous_ptr; + message m; + int i; + + /* Check to see if a message from desired source is already available. */ + if (!(caller_ptr->p_flags & SENDING)) { + + /* Check caller queue. */ + for (sender_ptr = caller_ptr->p_callerq; sender_ptr != NIL_PROC; + previous_ptr = sender_ptr, sender_ptr = sender_ptr->p_sendlink) { + if (src == ANY || src == proc_number(sender_ptr)) { + /* An acceptable message has been found. */ + CopyMess(proc_number(sender_ptr), sender_ptr, + sender_ptr->p_messbuf, caller_ptr, m_ptr); + if (sender_ptr == caller_ptr->p_callerq) + caller_ptr->p_callerq = sender_ptr->p_sendlink; + else + previous_ptr->p_sendlink = sender_ptr->p_sendlink; + if ((sender_ptr->p_flags &= ~SENDING) == 0) + ready(sender_ptr); /* deblock sender */ + return(OK); + } + } + + /* Check bit mask for blocked notifications. If multiple bits are set, + * send the first notification encountered; the rest is handled later. + * This effectively prioritizes notifications. Notification also have + * priority of other messages. + */ + if (caller_ptr->p_ntf_blocked && isrxhardware(src)) { + for (i=0; i<NR_NOTIFICATIONS; i++) { + if (isset_bit(caller_ptr->p_ntf_blocked, i)) { + m.m_source = HARDWARE; + m.m_type = NOTIFICATION + i; + CopyMess(HARDWARE, proc_addr(HARDWARE), &m, caller_ptr, m_ptr); + clear_bit(caller_ptr->p_ntf_blocked, i); + return(OK); + } + } + } + } + + /* No suitable message is available. Block the process trying to receive, + * unless this is not allowed by the system call. + */ + if (may_block) { + caller_ptr->p_getfrom = src; + caller_ptr->p_messbuf = m_ptr; + if (caller_ptr->p_flags == 0) unready(caller_ptr); + caller_ptr->p_flags |= RECEIVING; + return(OK); + } else { + return(ENOTREADY); + } +} + +/*===========================================================================* + * pick_proc * + *===========================================================================*/ +PRIVATE void pick_proc() +{ +/* Decide who to run now. A new process is selected by setting 'proc_ptr'. + * When a fresh user (or idle) process is selected, record it in 'bill_ptr', + * so the clock task can tell who to bill for system time. + */ + register struct proc *rp; /* process to run */ + int q; /* iterate over queues */ + + /* Check each of the scheduling queues for ready processes. The number of + * queues is defined in proc.h, and priorities are set in the task table. + * The lowest queue contains IDLE, which is always ready. + */ + for (q=0; q < NR_SCHED_QUEUES; q++) { + if ( (rp = rdy_head[q]) != NIL_PROC) { + proc_ptr = rp; /* run process 'rp' next */ + if (isuserp(rp) || isidlep(rp)) /* possible bill 'rp' */ + bill_ptr = rp; + return; + } + } +} + +/*===========================================================================* + * ready * + *===========================================================================*/ +PRIVATE void ready(rp) +register struct proc *rp; /* this process is now runnable */ +{ +/* Add 'rp' to one of the queues of runnable processes. */ + int q = rp->p_priority; /* scheduling queue to use */ + + /* Processes, in principle, are added to the end of the queue. However, + * user processes are added in front of the queue, because this is a bit + * fairer to I/O bound processes. + */ + if (isuserp(rp)) { /* add to front of queue */ + if (rdy_head[q] == NIL_PROC) + rdy_tail[q] = rp; + rp->p_nextready = rdy_head[q]; /* add to front of queue */ + rdy_head[q] = rp; + } + else { + if (rdy_head[q] != NIL_PROC) + rdy_tail[q]->p_nextready = rp; /* add to end of queue */ + else + rdy_head[q] = rp; /* add to empty queue */ + rdy_tail[q] = rp; + rp->p_nextready = NIL_PROC; + } + + /* Run 'rp' next if it has a higher priority than 'proc_ptr'. This actually + * should be done via pick_proc(), but mini_send() and mini_rec() rely + * on this side-effect. + */ + if (rp->p_priority < proc_ptr->p_priority) proc_ptr = rp; +} + +/*===========================================================================* + * unready * + *===========================================================================*/ +PRIVATE void unready(rp) +register struct proc *rp; /* this process is no longer runnable */ +{ +/* A process has blocked. See ready for a description of the queues. */ + + register struct proc *xp; + register struct proc **qtail; /* queue's rdy_tail */ + int q = rp->p_priority; /* queue to use */ + + /* Side-effect for tasks: check if the task's stack still is ok? */ + if (istaskp(rp)) { + if (*rp->p_stguard != STACK_GUARD) + panic("stack overrun by task", proc_number(rp)); + } + + /* Now make sure that the process is not in its ready queue. Remove the + * process if it is found. The easy part is to check the front of the queue. + */ + if ( (xp = rdy_head[q]) == NIL_PROC) return; + if (xp == rp) { + rdy_head[q] = xp->p_nextready; /* remove head of queue */ + if (rp == proc_ptr) /* current process removed */ + pick_proc(); /* pick new process to run */ + return; + } + + /* No match yet. Search body of queue. A process can be made unready even + * if it is not running by being sent a signal that kills it. + */ + while (xp->p_nextready != rp) + if ( (xp = xp->p_nextready) == NIL_PROC) return; + xp->p_nextready = xp->p_nextready->p_nextready; + qtail = &rdy_tail[q]; + if (*qtail == rp) *qtail = xp; +} + +/*===========================================================================* + * sched * + *===========================================================================*/ +PRIVATE void sched() +{ +/* The current process has run too long. If another low priority (user) + * process is runnable, put the current process on the end of the user queue, + * possibly promoting another user to head of the queue. + */ + if (rdy_head[PPRI_USER] == NIL_PROC) return; + + /* One or more user processes queued. */ + rdy_tail[PPRI_USER]->p_nextready = rdy_head[PPRI_USER]; + rdy_tail[PPRI_USER] = rdy_head[PPRI_USER]; + rdy_head[PPRI_USER] = rdy_head[PPRI_USER]->p_nextready; + rdy_tail[PPRI_USER]->p_nextready = NIL_PROC; + pick_proc(); +} + +/*==========================================================================* + * lock_pick_proc * + *==========================================================================*/ +PUBLIC void lock_pick_proc() +{ +/* Safe gateway to pick_proc() for tasks. */ + + switching = TRUE; + pick_proc(); + switching = FALSE; +} + +/*==========================================================================* + * lock_ready * + *==========================================================================*/ +PUBLIC void lock_ready(rp) +struct proc *rp; /* this process is now runnable */ +{ +/* Safe gateway to ready() for tasks. */ + + switching = TRUE; + ready(rp); + switching = FALSE; +} + +/*==========================================================================* + * lock_unready * + *==========================================================================*/ +PUBLIC void lock_unready(rp) +struct proc *rp; /* this process is no longer runnable */ +{ +/* Safe gateway to unready() for tasks. */ + + switching = TRUE; + unready(rp); + switching = FALSE; +} + +/*==========================================================================* + * lock_sched * + *==========================================================================*/ +PUBLIC void lock_sched() +{ +/* Safe gateway to sched() for tasks. */ + + switching = TRUE; + sched(); + switching = FALSE; +} + +/*==========================================================================* + * unhold * + *==========================================================================*/ +PUBLIC void unhold() +{ +/* Flush any held-up notifications. 'k_reenter' must be 0. 'held_head' must + * not be NIL_PROC. Interrupts must be disabled. They will be enabled but + * will be disabled when this returns. + */ + register struct proc *rp; /* current head of held queue */ + int i; + + if (switching) return; + rp = held_head; + do { + for (i=0; i<NR_NOTIFICATIONS; i++) { + if (isset_bit(rp->p_ntf_held,i)) { + clear_bit(rp->p_ntf_held,i); + if (! rp->p_ntf_held) /* proceed to next in queue? */ + if ( (held_head = rp->p_ntf_nextheld) == NIL_PROC) + held_tail = NIL_PROC; + unlock(); /* reduce latency; held queue may change! */ + notify(proc_number(rp), NOTIFICATION + i); + lock(); /* protect the held queue again */ + } + } + } + while ( (rp = held_head) != NIL_PROC); +} + +#if (CHIP == M68000) +/*==========================================================================* + * cp_mess * + *==========================================================================*/ +PRIVATE void cp_mess(src, src_p, src_m, dst_p, dst_m) +int src; /* sender process */ +register struct proc *src_p; /* source proc entry */ +message *src_m; /* source message */ +register struct proc *dst_p; /* destination proc entry */ +message *dst_m; /* destination buffer */ +{ + /* convert virtual address to physical address */ + /* The caller has already checked if all addresses are within bounds */ + + src_m = (message *)((char *)src_m + (((phys_bytes)src_p->p_map[D].mem_phys + - src_p->p_map[D].mem_vir) << CLICK_SHIFT)); + dst_m = (message *)((char *)dst_m + (((phys_bytes)dst_p->p_map[D].mem_phys + - dst_p->p_map[D].mem_vir) << CLICK_SHIFT)); + +#ifdef NEEDFSTRUCOPY + phys_copy(src_m,dst_m,(phys_bytes) sizeof(message)); +#else + *dst_m = *src_m; +#endif + dst_m->m_source = src; +} +#endif + diff --git a/kernel/proc.h b/kernel/proc.h new file mode 100755 index 000000000..5d9ea18d8 --- /dev/null +++ b/kernel/proc.h @@ -0,0 +1,154 @@ +#ifndef PROC_H +#define PROC_H + +/* Here is the declaration of the process table. It contains the process' + * registers, memory map, accounting, and message send/receive information. + * Many assembly code routines reference fields in it. The offsets to these + * fields are defined in the assembler include file sconst.h. When changing + * 'proc', be sure to change sconst.h to match. + * + * Changes: + * Nov 10, 2004 separated process types/ priorities (Jorrit N. Herder) + * Sep 30, 2004 bit masks for notifications (Jorrit N. Herder) + * Sep 24, 2004 one timer per type of alarm (Jorrit N. Herder) + * May 01, 2004 new p_sendmask to protect syscalls (Jorrit N. Herder) + */ +#include <minix/com.h> +#include "const.h" + +struct proc { + struct stackframe_s p_reg; /* process' registers saved in stack frame */ + +#if (CHIP == INTEL) + reg_t p_ldt_sel; /* selector in gdt giving ldt base and limit*/ + struct segdesc_s p_ldt[4]; /* local descriptors for code and data */ + /* 4 is LDT_SIZE - avoid include protect.h */ +#endif /* (CHIP == INTEL) */ + +#if (CHIP == M68000) +/* M68000 specific registers and FPU details go here. */ +#endif /* (CHIP == M68000) */ + + reg_t *p_stguard; /* stack guard word */ + + int p_nr; /* number of this process (for fast access) */ + + notify_mask_t p_ntf_blocked; /* bit mask for blocked notifications */ + notify_mask_t p_ntf_held; /* bit mask for held up notify() calls */ + struct proc *p_ntf_nextheld; /* next in chain of held-up int processes */ + + int p_flags; /* SENDING, RECEIVING, etc. */ + struct mem_map p_memmap[NR_LOCAL_SEGS]; /* local memory map (T, D, S) */ + struct far_mem p_farmem[NR_REMOTE_SEGS]; /* remote memory map */ + short p_type; /* task, system, driver, server, user, idle */ + short p_priority; /* scheduling priority */ + + clock_t user_time; /* user time in ticks */ + clock_t sys_time; /* sys time in ticks */ + clock_t child_utime; /* cumulative user time of children */ + clock_t child_stime; /* cumulative sys time of children */ + + timer_t p_signalrm; /* signal alarm timer */ + timer_t p_flagalrm; /* flag alarm timer */ + timer_t p_syncalrm; /* synchronous alarm timer */ + + send_mask_t p_sendmask; /* mask indicating to whom proc may send */ + struct proc *p_callerq; /* head of list of procs wishing to send */ + struct proc *p_sendlink; /* link to next proc wishing to send */ + message *p_messbuf; /* pointer to message buffer */ + int p_getfrom; /* from whom does process want to receive? */ + int p_sendto; /* to whom does process want to send? */ + + struct proc *p_nextready; /* pointer to next ready process */ + sigset_t p_pending; /* bit map for pending signals */ + unsigned p_pendcount; /* count of pending and unfinished signals */ + + char p_name[PROC_NAME_LEN]; /* name of the process, including \0 */ +#if ENABLE_MESSAGE_STATS + int msg_unreplied[NR_TASKS+NR_PROCS]; +#endif +}; + +/* Guard word for task stacks. */ +#define STACK_GUARD ((reg_t) (sizeof(reg_t) == 2 ? 0xBEEF : 0xDEADBEEF)) + +/* Bits for p_flags in proc[]. A process is runnable iff p_flags == 0. */ +#define NO_MAP 0x01 /* keeps unmapped forked child from running */ +#define SENDING 0x02 /* set when process blocked trying to send */ +#define RECEIVING 0x04 /* set when process blocked trying to recv */ +#define PENDING 0x08 /* set when inform() of signal pending */ +#define SIG_PENDING 0x10 /* keeps to-be-signalled proc from running */ +#define P_STOP 0x20 /* set when process is being traced */ + +/* Values for p_type. Non-negative values represent active process types. + * Process types are important to model inter-process relationships. When + * MINIX is shutdown, all system services are notified in order of possible + * dependencies, so that, e.g., the FS can rely on drivers to synchronize. + */ +#define P_RESERVED -2 /* slot is not in use, but reserved */ +#define P_NONE -1 /* slot is not in use, and free */ +#define P_TASK 0 /* kernel process */ +#define P_SYSTEM 1 /* low-level system service */ +#define P_DRIVER 2 /* device driver */ +#define P_SERVER 3 /* system service outside the kernel */ +#define P_USER 4 /* user process */ +#define P_IDLE 5 /* idle process */ + +/* Scheduling priorities for p_priority. Values must start at zero and + * increment. Priorities of system services can be set in the task table. + * Task, user, and idle priorities are fixed; the rest can be selected. + */ +#define PPRI_TASK 0 /* reserved for kernel tasks */ +#define PPRI_HIGHER 1 +#define PPRI_HIGH 2 +#define PPRI_NORMAL 3 +#define PPRI_LOW 4 +#define PPRI_LOWER 5 +#define PPRI_USER 6 /* reserved for user processes */ +#define PPRI_IDLE 7 /* only IDLE process goes here */ + +#define NR_SCHED_QUEUES 8 /* MUST equal minimum priority + 1 */ + +/* Magic process table addresses. */ +#define BEG_PROC_ADDR (&proc[0]) +#define BEG_USER_ADDR (&proc[NR_TASKS]) +#define END_PROC_ADDR (&proc[NR_TASKS + NR_PROCS]) + +#define NIL_PROC ((struct proc *) 0) +#define isidlehardware(n) ((n) == IDLE || (n) == HARDWARE) +#define isokprocn(n) ((unsigned) ((n) + NR_TASKS) < NR_PROCS + NR_TASKS) +#define isokprocp(p) ((p) >= BEG_PROC_ADDR && (p) < END_PROC_ADDR) +#define isoksrc_dst(n) (isokprocn(n) || (n) == ANY) +#define isalive(n) (proc_addr(n)->p_type > P_NONE) +#define isalivep(p) ((p)->p_type > P_NONE) +#define isrxhardware(n) ((n) == ANY || (n) == HARDWARE) +#define iskernel(n) ((n) == CLOCK || (n) == SYSTASK) +#define issysentn(n) ((n) == FS_PROC_NR || (n) == MM_PROC_NR) +#define issysentp(p) (issysentn((p)->p_nr)) +#define isreservedp(p) ((p)->p_type == P_RESERVED) +#define isemptyp(p) ((p)->p_type == P_NONE) +#define istaskp(p) ((p)->p_type == P_TASK) +#define isdriverp(p) ((p)->p_type == P_DRIVER) +#define isserverp(p) ((p)->p_type == P_SERVER) +#define isuserp(p) ((p)->p_type == P_USER) +#define isuser(n) (proc_addr(n)->p_type == P_USER) +#define isidlep(p) ((p)->p_type == P_IDLE) +#define proc_addr(n) (pproc_addr + NR_TASKS)[(n)] +#define cproc_addr(n) (&(proc + NR_TASKS)[(n)]) +#define proc_number(p) ((p)->p_nr) +#define proc_vir2phys(p, vir) \ + (((phys_bytes)(p)->p_map[D].mem_phys << CLICK_SHIFT) \ + + (vir_bytes) (vir)) + +/* The process table and pointers to process table slots. The pointers allow + * faster access because now a process entry can be found by indexing the + * pproc_addr array, while accessing an element i requires a multiplication + * with sizeof(struct proc) to determine the address. + */ +EXTERN struct proc proc[NR_TASKS + NR_PROCS]; /* process table */ +EXTERN struct proc *pproc_addr[NR_TASKS + NR_PROCS]; +EXTERN struct proc *bill_ptr; /* ptr to process to bill for clock ticks */ +EXTERN struct proc *rdy_head[NR_SCHED_QUEUES]; /* ptrs to ready list headers */ +EXTERN struct proc *rdy_tail[NR_SCHED_QUEUES]; /* ptrs to ready list tails */ + +#endif /* PROC_H */ diff --git a/kernel/protect.c b/kernel/protect.c new file mode 100755 index 000000000..7057ac91e --- /dev/null +++ b/kernel/protect.c @@ -0,0 +1,344 @@ +/* This file contains code for initialization of protected mode, to initialize + * code and data segment descriptors, and to initialize global descriptors + * for local descriptors in the process table. + */ + +#include "kernel.h" +#include "proc.h" +#include "protect.h" + +#if _WORD_SIZE == 4 +#define INT_GATE_TYPE (INT_286_GATE | DESC_386_BIT) +#define TSS_TYPE (AVL_286_TSS | DESC_386_BIT) +#else +#define INT_GATE_TYPE INT_286_GATE +#define TSS_TYPE AVL_286_TSS +#endif + +struct desctableptr_s { + char limit[sizeof(u16_t)]; + char base[sizeof(u32_t)]; /* really u24_t + pad for 286 */ +}; + +struct gatedesc_s { + u16_t offset_low; + u16_t selector; + u8_t pad; /* |000|XXXXX| ig & trpg, |XXXXXXXX| task g */ + u8_t p_dpl_type; /* |P|DL|0|TYPE| */ + u16_t offset_high; +}; + +struct tss_s { + reg_t backlink; + reg_t sp0; /* stack pointer to use during interrupt */ + reg_t ss0; /* " segment " " " " */ + reg_t sp1; + reg_t ss1; + reg_t sp2; + reg_t ss2; +#if _WORD_SIZE == 4 + reg_t cr3; +#endif + reg_t ip; + reg_t flags; + reg_t ax; + reg_t cx; + reg_t dx; + reg_t bx; + reg_t sp; + reg_t bp; + reg_t si; + reg_t di; + reg_t es; + reg_t cs; + reg_t ss; + reg_t ds; +#if _WORD_SIZE == 4 + reg_t fs; + reg_t gs; +#endif + reg_t ldt; +#if _WORD_SIZE == 4 + u16_t trap; + u16_t iobase; +/* u8_t iomap[0]; */ +#endif +}; + +PUBLIC struct segdesc_s gdt[GDT_SIZE]; /* used in klib.s and mpx.s */ +PRIVATE struct gatedesc_s idt[IDT_SIZE]; /* zero-init so none present */ +PUBLIC struct tss_s tss; /* zero init */ + +FORWARD _PROTOTYPE( void int_gate, (unsigned vec_nr, vir_bytes offset, + unsigned dpl_type) ); +FORWARD _PROTOTYPE( void sdesc, (struct segdesc_s *segdp, phys_bytes base, + vir_bytes size) ); + +/*=========================================================================* + * prot_init * + *=========================================================================*/ +PUBLIC void prot_init() +{ +/* Set up tables for protected mode. + * All GDT slots are allocated at compile time. + */ + + extern int etext, end; +#define code_bytes ((vir_bytes) &etext) /* Size of code segment. */ +#define data_bytes ((vir_bytes) &end) /* Size of data segment. */ + struct gate_table_s *gtp; + struct desctableptr_s *dtp; + unsigned ldt_index; + register struct proc *rp; + + static struct gate_table_s { + _PROTOTYPE( void (*gate), (void) ); + unsigned char vec_nr; + unsigned char privilege; + } + gate_table[] = { + divide_error, DIVIDE_VECTOR, INTR_PRIVILEGE, + single_step_exception, DEBUG_VECTOR, INTR_PRIVILEGE, + nmi, NMI_VECTOR, INTR_PRIVILEGE, + breakpoint_exception, BREAKPOINT_VECTOR, USER_PRIVILEGE, + overflow, OVERFLOW_VECTOR, USER_PRIVILEGE, + bounds_check, BOUNDS_VECTOR, INTR_PRIVILEGE, + inval_opcode, INVAL_OP_VECTOR, INTR_PRIVILEGE, + copr_not_available, COPROC_NOT_VECTOR, INTR_PRIVILEGE, + double_fault, DOUBLE_FAULT_VECTOR, INTR_PRIVILEGE, + copr_seg_overrun, COPROC_SEG_VECTOR, INTR_PRIVILEGE, + inval_tss, INVAL_TSS_VECTOR, INTR_PRIVILEGE, + segment_not_present, SEG_NOT_VECTOR, INTR_PRIVILEGE, + stack_exception, STACK_FAULT_VECTOR, INTR_PRIVILEGE, + general_protection, PROTECTION_VECTOR, INTR_PRIVILEGE, +#if _WORD_SIZE == 4 + page_fault, PAGE_FAULT_VECTOR, INTR_PRIVILEGE, + copr_error, COPROC_ERR_VECTOR, INTR_PRIVILEGE, +#endif + { hwint00, VECTOR( 0), INTR_PRIVILEGE }, + { hwint01, VECTOR( 1), INTR_PRIVILEGE }, + { hwint02, VECTOR( 2), INTR_PRIVILEGE }, + { hwint03, VECTOR( 3), INTR_PRIVILEGE }, + { hwint04, VECTOR( 4), INTR_PRIVILEGE }, + { hwint05, VECTOR( 5), INTR_PRIVILEGE }, + { hwint06, VECTOR( 6), INTR_PRIVILEGE }, + { hwint07, VECTOR( 7), INTR_PRIVILEGE }, + { hwint08, VECTOR( 8), INTR_PRIVILEGE }, + { hwint09, VECTOR( 9), INTR_PRIVILEGE }, + { hwint10, VECTOR(10), INTR_PRIVILEGE }, + { hwint11, VECTOR(11), INTR_PRIVILEGE }, + { hwint12, VECTOR(12), INTR_PRIVILEGE }, + { hwint13, VECTOR(13), INTR_PRIVILEGE }, + { hwint14, VECTOR(14), INTR_PRIVILEGE }, + { hwint15, VECTOR(15), INTR_PRIVILEGE }, +#if _WORD_SIZE == 2 + { p_s_call, SYS_VECTOR, USER_PRIVILEGE }, /* 286 system call */ +#else + { s_call, SYS386_VECTOR, USER_PRIVILEGE }, /* 386 system call */ +#endif + { level0_call, LEVEL0_VECTOR, TASK_PRIVILEGE }, + }; + + /* Build gdt and idt pointers in GDT where the BIOS expects them. */ + dtp= (struct desctableptr_s *) &gdt[GDT_INDEX]; + * (u16_t *) dtp->limit = (sizeof gdt) - 1; + * (u32_t *) dtp->base = vir2phys(gdt); + + dtp= (struct desctableptr_s *) &gdt[IDT_INDEX]; + * (u16_t *) dtp->limit = (sizeof idt) - 1; + * (u32_t *) dtp->base = vir2phys(idt); + + /* Build segment descriptors for tasks and interrupt handlers. */ + init_codeseg(&gdt[CS_INDEX], code_base, code_bytes, INTR_PRIVILEGE); + init_dataseg(&gdt[DS_INDEX], data_base, data_bytes, INTR_PRIVILEGE); + init_dataseg(&gdt[ES_INDEX], 0L, 0, TASK_PRIVILEGE); + + /* Build scratch descriptors for functions in klib88. */ + init_dataseg(&gdt[DS_286_INDEX], 0L, 0, TASK_PRIVILEGE); + init_dataseg(&gdt[ES_286_INDEX], 0L, 0, TASK_PRIVILEGE); + + /* Build local descriptors in GDT for LDT's in process table. + * The LDT's are allocated at compile time in the process table, and + * initialized whenever a process' map is initialized or changed. + */ + for (rp = BEG_PROC_ADDR, ldt_index = FIRST_LDT_INDEX; + rp < END_PROC_ADDR; ++rp, ldt_index++) { + init_dataseg(&gdt[ldt_index], vir2phys(rp->p_ldt), + sizeof(rp->p_ldt), INTR_PRIVILEGE); + gdt[ldt_index].access = PRESENT | LDT; + rp->p_ldt_sel = ldt_index * DESC_SIZE; + } + + /* Build main TSS. + * This is used only to record the stack pointer to be used after an + * interrupt. + * The pointer is set up so that an interrupt automatically saves the + * current process's registers ip:cs:f:sp:ss in the correct slots in the + * process table. + */ + tss.ss0 = DS_SELECTOR; + init_dataseg(&gdt[TSS_INDEX], vir2phys(&tss), sizeof(tss), INTR_PRIVILEGE); + gdt[TSS_INDEX].access = PRESENT | (INTR_PRIVILEGE << DPL_SHIFT) | TSS_TYPE; + + /* Build descriptors for interrupt gates in IDT. */ + for (gtp = &gate_table[0]; + gtp < &gate_table[sizeof gate_table / sizeof gate_table[0]]; ++gtp) { + int_gate(gtp->vec_nr, (vir_bytes) gtp->gate, + PRESENT | INT_GATE_TYPE | (gtp->privilege << DPL_SHIFT)); + } + +#if _WORD_SIZE == 4 + /* Complete building of main TSS. */ + tss.iobase = sizeof tss; /* empty i/o permissions map */ +#endif +} + +/*=========================================================================* + * init_codeseg * + *=========================================================================*/ +PUBLIC void init_codeseg(segdp, base, size, privilege) +register struct segdesc_s *segdp; +phys_bytes base; +vir_bytes size; +int privilege; +{ +/* Build descriptor for a code segment. */ + + sdesc(segdp, base, size); + segdp->access = (privilege << DPL_SHIFT) + | (PRESENT | SEGMENT | EXECUTABLE | READABLE); + /* CONFORMING = 0, ACCESSED = 0 */ +} + +/*=========================================================================* + * init_dataseg * + *=========================================================================*/ +PUBLIC void init_dataseg(segdp, base, size, privilege) +register struct segdesc_s *segdp; +phys_bytes base; +vir_bytes size; +int privilege; +{ +/* Build descriptor for a data segment. */ + + sdesc(segdp, base, size); + segdp->access = (privilege << DPL_SHIFT) | (PRESENT | SEGMENT | WRITEABLE); + /* EXECUTABLE = 0, EXPAND_DOWN = 0, ACCESSED = 0 */ +} + +/*=========================================================================* + * sdesc * + *=========================================================================*/ +PRIVATE void sdesc(segdp, base, size) +register struct segdesc_s *segdp; +phys_bytes base; +vir_bytes size; +{ +/* Fill in the size fields (base, limit and granularity) of a descriptor. */ + + segdp->base_low = base; + segdp->base_middle = base >> BASE_MIDDLE_SHIFT; + segdp->base_high = base >> BASE_HIGH_SHIFT; + +#if _WORD_SIZE == 4 + --size; /* convert to a limit, 0 size means 4G */ + if (size > BYTE_GRAN_MAX) { + segdp->limit_low = size >> PAGE_GRAN_SHIFT; + segdp->granularity = GRANULAR | (size >> + (PAGE_GRAN_SHIFT + GRANULARITY_SHIFT)); + } else { + segdp->limit_low = size; + segdp->granularity = size >> GRANULARITY_SHIFT; + } + segdp->granularity |= DEFAULT; /* means BIG for data seg */ +#else + segdp->limit_low = size - 1; +#endif +} + +/*=========================================================================* + * seg2phys * + *=========================================================================*/ +PUBLIC phys_bytes seg2phys(seg) +U16_t seg; +{ +/* Return the base address of a segment, with seg being either a 8086 segment + * register, or a 286/386 segment selector. + */ + phys_bytes base; + struct segdesc_s *segdp; + + if (!protected_mode) { + base = hclick_to_physb(seg); + } else { + segdp = &gdt[seg >> 3]; + base = ((u32_t) segdp->base_low << 0) + | ((u32_t) segdp->base_middle << 16) + | ((u32_t) segdp->base_high << 24); + } + return base; +} + + +/*=========================================================================* + * phys2seg * + *=========================================================================*/ +PUBLIC void phys2seg(seg, off, phys) +u16_t *seg; +vir_bytes *off; +phys_bytes phys; +{ +/* Return a segment selector and offset that can be used to reach a physical + * address, for use by a driver doing memory I/O in the A0000 - DFFFF range. + */ +#if _WORD_SIZE == 2 + if (!protected_mode) { + *seg = phys / HCLICK_SIZE; + *off = phys % HCLICK_SIZE; + } else { + unsigned bank = phys >> 16; + unsigned index = bank - 0xA + A_INDEX; + init_dataseg(&gdt[index], (phys_bytes) bank << 16, 0, TASK_PRIVILEGE); + *seg = (index * 0x08) | TASK_PRIVILEGE; + *off = phys & 0xFFFF; + } +#else + *seg = FLAT_DS_SELECTOR; + *off = phys; +#endif +} + +/*=========================================================================* + * int_gate * + *=========================================================================*/ +PRIVATE void int_gate(vec_nr, offset, dpl_type) +unsigned vec_nr; +vir_bytes offset; +unsigned dpl_type; +{ +/* Build descriptor for an interrupt gate. */ + + register struct gatedesc_s *idp; + + idp = &idt[vec_nr]; + idp->offset_low = offset; + idp->selector = CS_SELECTOR; + idp->p_dpl_type = dpl_type; +#if _WORD_SIZE == 4 + idp->offset_high = offset >> OFFSET_HIGH_SHIFT; +#endif +} + +/*=========================================================================* + * enable_iop * + *=========================================================================*/ +PUBLIC void enable_iop(pp) +struct proc *pp; +{ +/* Allow a user process to use I/O instructions. Change the I/O Permission + * Level bits in the psw. These specify least-privileged Current Permission + * Level allowed to execute I/O instructions. Users and servers have CPL 3. + * You can't have less privilege than that. Kernel has CPL 0, tasks CPL 1. + */ + pp->p_reg.psw |= 0x3000; +} diff --git a/kernel/protect.h b/kernel/protect.h new file mode 100755 index 000000000..14b3fef7c --- /dev/null +++ b/kernel/protect.h @@ -0,0 +1,124 @@ +/* Constants for protected mode. */ + +/* Table sizes. */ +#define GDT_SIZE (FIRST_LDT_INDEX + NR_TASKS + NR_PROCS) + /* spec. and LDT's */ +#define IDT_SIZE (IRQ8_VECTOR + 8) /* only up to the highest vector */ +#define LDT_SIZE 4 /* contains CS, DS and two extras */ + +/* Fixed global descriptors. 1 to 7 are prescribed by the BIOS. */ +#define GDT_INDEX 1 /* GDT descriptor */ +#define IDT_INDEX 2 /* IDT descriptor */ +#define DS_INDEX 3 /* kernel DS */ +#define ES_INDEX 4 /* kernel ES (386: flag 4 Gb at startup) */ +#define SS_INDEX 5 /* kernel SS (386: monitor SS at startup) */ +#define CS_INDEX 6 /* kernel CS */ +#define MON_CS_INDEX 7 /* temp for BIOS (386: monitor CS at startup) */ +#define TSS_INDEX 8 /* kernel TSS */ +#define DS_286_INDEX 9 /* scratch 16-bit source segment */ +#define ES_286_INDEX 10 /* scratch 16-bit destination segment */ +#define A_INDEX 11 /* 64K memory segment at A0000 */ +#define B_INDEX 12 /* 64K memory segment at B0000 */ +#define C_INDEX 13 /* 64K memory segment at C0000 */ +#define D_INDEX 14 /* 64K memory segment at D0000 */ +#define FIRST_LDT_INDEX 15 /* rest of descriptors are LDT's */ + +#define GDT_SELECTOR 0x08 /* (GDT_INDEX * DESC_SIZE) bad for asld */ +#define IDT_SELECTOR 0x10 /* (IDT_INDEX * DESC_SIZE) */ +#define DS_SELECTOR 0x18 /* (DS_INDEX * DESC_SIZE) */ +#define ES_SELECTOR 0x20 /* (ES_INDEX * DESC_SIZE) */ +#define FLAT_DS_SELECTOR 0x21 /* less privileged ES */ +#define SS_SELECTOR 0x28 /* (SS_INDEX * DESC_SIZE) */ +#define CS_SELECTOR 0x30 /* (CS_INDEX * DESC_SIZE) */ +#define MON_CS_SELECTOR 0x38 /* (MON_CS_INDEX * DESC_SIZE) */ +#define TSS_SELECTOR 0x40 /* (TSS_INDEX * DESC_SIZE) */ +#define DS_286_SELECTOR 0x49 /* (DS_286_INDEX*DESC_SIZE + TASK_PRIVILEGE) */ +#define ES_286_SELECTOR 0x51 /* (ES_286_INDEX*DESC_SIZE + TASK_PRIVILEGE) */ + +/* Fixed local descriptors. */ +#define CS_LDT_INDEX 0 /* process CS */ +#define DS_LDT_INDEX 1 /* process DS=ES=FS=GS=SS */ +#define EXTRA_LDT_INDEX 2 /* first of the extra LDT entries */ + +/* Privileges. */ +#define INTR_PRIVILEGE 0 /* kernel and interrupt handlers */ +#define TASK_PRIVILEGE 1 /* kernel tasks */ +#define USER_PRIVILEGE 3 /* servers and user processes */ + +/* 286 hardware constants. */ + +/* Exception vector numbers. */ +#define BOUNDS_VECTOR 5 /* bounds check failed */ +#define INVAL_OP_VECTOR 6 /* invalid opcode */ +#define COPROC_NOT_VECTOR 7 /* coprocessor not available */ +#define DOUBLE_FAULT_VECTOR 8 +#define COPROC_SEG_VECTOR 9 /* coprocessor segment overrun */ +#define INVAL_TSS_VECTOR 10 /* invalid TSS */ +#define SEG_NOT_VECTOR 11 /* segment not present */ +#define STACK_FAULT_VECTOR 12 /* stack exception */ +#define PROTECTION_VECTOR 13 /* general protection */ + +/* Selector bits. */ +#define TI 0x04 /* table indicator */ +#define RPL 0x03 /* requester privilege level */ + +/* Descriptor structure offsets. */ +#define DESC_BASE 2 /* to base_low */ +#define DESC_BASE_MIDDLE 4 /* to base_middle */ +#define DESC_ACCESS 5 /* to access byte */ +#define DESC_SIZE 8 /* sizeof (struct segdesc_s) */ + +/* Base and limit sizes and shifts. */ +#define BASE_MIDDLE_SHIFT 16 /* shift for base --> base_middle */ + +/* Access-byte and type-byte bits. */ +#define PRESENT 0x80 /* set for descriptor present */ +#define DPL 0x60 /* descriptor privilege level mask */ +#define DPL_SHIFT 5 +#define SEGMENT 0x10 /* set for segment-type descriptors */ + +/* Access-byte bits. */ +#define EXECUTABLE 0x08 /* set for executable segment */ +#define CONFORMING 0x04 /* set for conforming segment if executable */ +#define EXPAND_DOWN 0x04 /* set for expand-down segment if !executable*/ +#define READABLE 0x02 /* set for readable segment if executable */ +#define WRITEABLE 0x02 /* set for writeable segment if !executable */ +#define TSS_BUSY 0x02 /* set if TSS descriptor is busy */ +#define ACCESSED 0x01 /* set if segment accessed */ + +/* Special descriptor types. */ +#define AVL_286_TSS 1 /* available 286 TSS */ +#define LDT 2 /* local descriptor table */ +#define BUSY_286_TSS 3 /* set transparently to the software */ +#define CALL_286_GATE 4 /* not used */ +#define TASK_GATE 5 /* only used by debugger */ +#define INT_286_GATE 6 /* interrupt gate, used for all vectors */ +#define TRAP_286_GATE 7 /* not used */ + +/* Extra 386 hardware constants. */ + +/* Exception vector numbers. */ +#define PAGE_FAULT_VECTOR 14 +#define COPROC_ERR_VECTOR 16 /* coprocessor error */ + +/* Descriptor structure offsets. */ +#define DESC_GRANULARITY 6 /* to granularity byte */ +#define DESC_BASE_HIGH 7 /* to base_high */ + +/* Base and limit sizes and shifts. */ +#define BASE_HIGH_SHIFT 24 /* shift for base --> base_high */ +#define BYTE_GRAN_MAX 0xFFFFFL /* maximum size for byte granular segment */ +#define GRANULARITY_SHIFT 16 /* shift for limit --> granularity */ +#define OFFSET_HIGH_SHIFT 16 /* shift for (gate) offset --> offset_high */ +#define PAGE_GRAN_SHIFT 12 /* extra shift for page granular limits */ + +/* Type-byte bits. */ +#define DESC_386_BIT 0x08 /* 386 types are obtained by ORing with this */ + /* LDT's and TASK_GATE's don't need it */ + +/* Granularity byte. */ +#define GRANULAR 0x80 /* set for 4K granularilty */ +#define DEFAULT 0x40 /* set for 32-bit defaults (executable seg) */ +#define BIG 0x40 /* set for "BIG" (expand-down seg) */ +#define AVL 0x10 /* 0 for available */ +#define LIMIT_HIGH 0x0F /* mask for high bits of limit */ diff --git a/kernel/proto.h b/kernel/proto.h new file mode 100755 index 000000000..99c095b95 --- /dev/null +++ b/kernel/proto.h @@ -0,0 +1,213 @@ +/* Function prototypes. */ + +#ifndef PROTO_H +#define PROTO_H + +/* Struct declarations. */ +#if TEMP_CODE +struct dpeth; +#endif +struct proc; +struct time_info; +struct timer; + +/* dummy.c */ +_PROTOTYPE( void dummy_task, (void) ); + +/* clock.c */ +_PROTOTYPE( void clock_task, (void) ); +_PROTOTYPE( void clock_stop, (void) ); +_PROTOTYPE( clock_t get_uptime, (void) ); +_PROTOTYPE( unsigned long read_clock, (void) ); +_PROTOTYPE( void set_timer, (struct timer *tp, clock_t t, tmr_func_t f) ); +_PROTOTYPE( void reset_timer, (struct timer *tp) ); + +/* klibc.c */ +_PROTOTYPE( int katoi, (register const char *s)); +_PROTOTYPE( void *kmemcpy, (void *s1, const void *s2, register size_t n)); +_PROTOTYPE( void *kmemset, (void *s, register int c, register size_t n)); +_PROTOTYPE( int kstrcmp, (register const char *s1, register const char *s2)); +_PROTOTYPE( size_t kstrlen, (const char *s)); +_PROTOTYPE( int kstrncmp, + (register const char *s1, register const char *s2, register size_t n)); +_PROTOTYPE( char *kstrncpy, + (char *s1, register const char *s2, register const size_t n)); +_PROTOTYPE( unsigned long kstrtoul, + (const char *string, char ** const end, int base) ); + +/* kprintf.c */ +#define NO_ARG 0 +#define karg(arg) (karg_t) (arg) +_PROTOTYPE( void kprintf, (const char *fmt, karg_t arg) ); + +/* main.c */ +_PROTOTYPE( void main, (void) ); +_PROTOTYPE( void prepare_shutdown, (int how) ); +_PROTOTYPE( void stop_sequence, (struct timer *tp) ); +_PROTOTYPE( void shutdown, (struct timer *tp) ); + +/* memory.c */ +_PROTOTYPE( void mem_init, (void) ); +_PROTOTYPE( void alloc_segments, (struct proc *rp) ); + +/* misc.c */ +_PROTOTYPE( void panic, (_CONST char *s, int n) ); + +#if TEMP_CODE +#if ENABLE_PCI +/* pci.c */ +_PROTOTYPE( void pci_init, (void) ); +_PROTOTYPE( int pci_find_dev, (U8_t bus, U8_t dev, U8_t func, + int *devindp) ); +_PROTOTYPE( int pci_first_dev, (int *devindp, u16_t *vidp, u16_t *didp) ); +_PROTOTYPE( int pci_next_dev, (int *devindp, u16_t *vidp, u16_t *didp) ); +_PROTOTYPE( void pci_reserve, (int devind) ); +_PROTOTYPE( void pci_ids, (int devind, u16_t *vidp, u16_t *didp) ); +_PROTOTYPE( char *pci_slot_name, (int devind) ); +_PROTOTYPE( char *pci_dev_name, (U16_t vid, U16_t did) ); +_PROTOTYPE( u8_t pci_attr_r8, (int devind, int port) ); +_PROTOTYPE( u16_t pci_attr_r16, (int devind, int port) ); +_PROTOTYPE( u32_t pci_attr_r32, (int devind, int port) ); +_PROTOTYPE( void pci_attr_w16, (int devind, int port, U16_t value) ); +_PROTOTYPE( void pci_attr_w32, (int devind, int port, u32_t value) ); + +/* rtl8029.c */ +_PROTOTYPE( int rtl_probe, (struct dpeth *dep) ); +#endif /* ENABLE_PCI */ +/* rtl8139.c */ +_PROTOTYPE( void rtl8139_task, (void) ); +#endif /* TEMP_CODE */ + + +/* proc.c */ +_PROTOTYPE( int sys_call, (int function, int src_dest, message *m_ptr) ); +_PROTOTYPE( void notify, (int proc_nr, int notify_type) ); +_PROTOTYPE( void unhold, (void) ); +_PROTOTYPE( void lock_pick_proc, (void) ); +_PROTOTYPE( void lock_ready, (struct proc *rp) ); +_PROTOTYPE( void lock_sched, (void) ); +_PROTOTYPE( void lock_unready, (struct proc *rp) ); + +/* sb16_dsp.c, sb16_mixer.c */ +_PROTOTYPE( void sb16dsp_task, (void) ); +_PROTOTYPE( void sb16mix_task, (void) ); + +/* start.c */ +_PROTOTYPE( void cstart, (U16_t cs, U16_t ds, U16_t mds, + U16_t parmoff, U16_t parmsize) ); +_PROTOTYPE( char *getkenv, (_CONST char *key) ); + +/* system.c */ +_PROTOTYPE( void cause_sig, (int proc_nr, int sig_nr) ); +_PROTOTYPE( void clear_proc, (int proc_nr) ); +_PROTOTYPE( phys_bytes numap_local, (int proc_nr, vir_bytes vir_addr, + vir_bytes bytes) ); +_PROTOTYPE( void sys_task, (void) ); +_PROTOTYPE( int virtual_copy, (struct vir_addr *src, struct vir_addr *dst, + vir_bytes bytes) ); +_PROTOTYPE( phys_bytes umap_local, (struct proc *rp, int seg, + vir_bytes vir_addr, vir_bytes bytes) ); +_PROTOTYPE( phys_bytes umap_remote, (struct proc *rp, int seg, + vir_bytes vir_addr, vir_bytes bytes) ); +_PROTOTYPE( phys_bytes umap_bios, (struct proc *rp, vir_bytes vir_addr, + vir_bytes bytes) ); +_PROTOTYPE( int vir_copy, (int src_proc, vir_bytes src_vir, + int dst_proc, vir_bytes dst_vir, vir_bytes bytes) ); +_PROTOTYPE( int generic_handler, (irq_hook_t *hook) ); +_PROTOTYPE( void timed_interrupt, (struct timer *tp) ); + +/* table.c */ +_PROTOTYPE( void mapdrivers, (void) ); + +#if (CHIP == INTEL) + +/* exception.c */ +_PROTOTYPE( void exception, (unsigned vec_nr) ); + +/* i8259.c */ +_PROTOTYPE( void intr_init, (int mine) ); +_PROTOTYPE( void intr_handle, (irq_hook_t *hook) ); +_PROTOTYPE( void put_irq_handler, (irq_hook_t *hook, int irq, + irq_handler_t handler) ); +/* klib*.s */ +_PROTOTYPE( void int86, (void) ); +_PROTOTYPE( void cp_mess, (int src,phys_clicks src_clicks,vir_bytes src_offset, + phys_clicks dst_clicks, vir_bytes dst_offset) ); +_PROTOTYPE( void enable_irq, (irq_hook_t *hook) ); +_PROTOTYPE( int disable_irq, (irq_hook_t *hook) ); +_PROTOTYPE( u16_t mem_rdw, (U16_t segm, vir_bytes offset) ); +_PROTOTYPE( void phys_copy, (phys_bytes source, phys_bytes dest, + phys_bytes count) ); +_PROTOTYPE( void phys_insb, (Port_t port, phys_bytes buf, size_t count) ); +_PROTOTYPE( void phys_insw, (Port_t port, phys_bytes buf, size_t count) ); +_PROTOTYPE( void phys_outsb, (Port_t port, phys_bytes buf, size_t count)); +_PROTOTYPE( void phys_outsw, (Port_t port, phys_bytes buf, size_t count)); +_PROTOTYPE( void reset, (void) ); +_PROTOTYPE( void level0, (void (*func)(void)) ); +_PROTOTYPE( void monitor, (void) ); + +/* mpx*.s */ +_PROTOTYPE( void idle_task, (void) ); +_PROTOTYPE( void restart, (void) ); + +/* The following are never called from C (pure asm procs). */ + +/* Exception handlers (real or protected mode), in numerical order. */ +void _PROTOTYPE( int00, (void) ), _PROTOTYPE( divide_error, (void) ); +void _PROTOTYPE( int01, (void) ), _PROTOTYPE( single_step_exception, (void) ); +void _PROTOTYPE( int02, (void) ), _PROTOTYPE( nmi, (void) ); +void _PROTOTYPE( int03, (void) ), _PROTOTYPE( breakpoint_exception, (void) ); +void _PROTOTYPE( int04, (void) ), _PROTOTYPE( overflow, (void) ); +void _PROTOTYPE( int05, (void) ), _PROTOTYPE( bounds_check, (void) ); +void _PROTOTYPE( int06, (void) ), _PROTOTYPE( inval_opcode, (void) ); +void _PROTOTYPE( int07, (void) ), _PROTOTYPE( copr_not_available, (void) ); +void _PROTOTYPE( double_fault, (void) ); +void _PROTOTYPE( copr_seg_overrun, (void) ); +void _PROTOTYPE( inval_tss, (void) ); +void _PROTOTYPE( segment_not_present, (void) ); +void _PROTOTYPE( stack_exception, (void) ); +void _PROTOTYPE( general_protection, (void) ); +void _PROTOTYPE( page_fault, (void) ); +void _PROTOTYPE( copr_error, (void) ); + +/* Hardware interrupt handlers. */ +_PROTOTYPE( void hwint00, (void) ); +_PROTOTYPE( void hwint01, (void) ); +_PROTOTYPE( void hwint02, (void) ); +_PROTOTYPE( void hwint03, (void) ); +_PROTOTYPE( void hwint04, (void) ); +_PROTOTYPE( void hwint05, (void) ); +_PROTOTYPE( void hwint06, (void) ); +_PROTOTYPE( void hwint07, (void) ); +_PROTOTYPE( void hwint08, (void) ); +_PROTOTYPE( void hwint09, (void) ); +_PROTOTYPE( void hwint10, (void) ); +_PROTOTYPE( void hwint11, (void) ); +_PROTOTYPE( void hwint12, (void) ); +_PROTOTYPE( void hwint13, (void) ); +_PROTOTYPE( void hwint14, (void) ); +_PROTOTYPE( void hwint15, (void) ); + +/* Software interrupt handlers, in numerical order. */ +_PROTOTYPE( void trp, (void) ); +_PROTOTYPE( void s_call, (void) ), _PROTOTYPE( p_s_call, (void) ); +_PROTOTYPE( void level0_call, (void) ); + +/* protect.c */ +_PROTOTYPE( void prot_init, (void) ); +_PROTOTYPE( void init_codeseg, (struct segdesc_s *segdp, phys_bytes base, + vir_bytes size, int privilege) ); +_PROTOTYPE( void init_dataseg, (struct segdesc_s *segdp, phys_bytes base, + vir_bytes size, int privilege) ); +_PROTOTYPE( phys_bytes seg2phys, (U16_t seg) ); +_PROTOTYPE( void phys2seg, (u16_t *seg, vir_bytes *off, phys_bytes phys)); +_PROTOTYPE( void enable_iop, (struct proc *pp) ); + + +#endif /* (CHIP == INTEL) */ + +#if (CHIP == M68000) +/* M68000 specific prototypes go here. */ +#endif /* (CHIP == M68000) */ + +#endif /* PROTO_H */ diff --git a/kernel/rtl8139.c b/kernel/rtl8139.c new file mode 100755 index 000000000..ddea69845 --- /dev/null +++ b/kernel/rtl8139.c @@ -0,0 +1,2546 @@ +/* + * rtl8139.c + * + * This file contains a ethernet device driver for Realtek rtl8139 based + * ethernet cards. + * + * The valid messages and their parameters are: + * + * m_type DL_PORT DL_PROC DL_COUNT DL_MODE DL_ADDR + * |------------+----------+---------+----------+---------+---------| + * | HARD_INT | | | | | | + * |------------|----------|---------|----------|---------|---------| + * | HARD_STOP | | | | | | + * |------------|----------|---------|----------|---------|---------| + * | DL_WRITE | port nr | proc nr | count | mode | address | + * |------------|----------|---------|----------|---------|---------| + * | DL_WRITEV | port nr | proc nr | count | mode | address | + * |------------|----------|---------|----------|---------|---------| + * | DL_READ | port nr | proc nr | count | | address | + * |------------|----------|---------|----------|---------|---------| + * | DL_READV | port nr | proc nr | count | | address | + * |------------|----------|---------|----------|---------|---------| + * | DL_INIT | port nr | proc nr | mode | | address | + * |------------|----------|---------|----------|---------|---------| + * | DL_GETSTAT | port nr | proc nr | | | address | + * |------------|----------|---------|----------|---------|---------| + * | DL_STOP | port_nr | | | | | + * |------------|----------|---------|----------|---------|---------| + * + * The messages sent are: + * + * m-type DL_POR T DL_PROC DL_COUNT DL_STAT DL_CLCK + * |------------|----------|---------|----------|---------|---------| + * |DL_TASK_REPL| port nr | proc nr | rd-count | err|stat| clock | + * |------------|----------|---------|----------|---------|---------| + * + * m_type m3_i1 m3_i2 m3_ca1 + * |------------+---------+-----------+---------------| + * |DL_INIT_REPL| port nr | last port | ethernet addr | + * |------------|---------|-----------|---------------| + * + * Created: Aug 2003 by Philip Homburg <philip@cs.vu.nl> + * Changes: + * Aug 15, 2004 sync alarms replace watchdogs timers (Jorrit N. Herder) + * May 02, 2004 flag alarms replace micro_elapsed() (Jorrit N. Herder) + * + */ + +#include "kernel.h" + +#include <stdlib.h> +#include <stddef.h> +#include <minix/com.h> +#include <minix/keymap.h> +#include <minix/syslib.h> +#include <minix/type.h> +#include <minix/utils.h> +#include <timers.h> +#include <ibm/portio.h> +#include <net/hton.h> +#include <net/gen/ether.h> +#include <net/gen/eth_io.h> + +#if __minix_vmd +#include "config.h" +#include "timer.h" +#else +#define tmra_ut timer_t +#define tmra_inittimer(tp) tmr_inittimer(tp) +#define Proc_number(p) proc_number(p) +#define debug 0 +#define RAND_UPDATE /**/ +#define printW() ((void)0) +#define vm_1phys2bus(p) (p) +#endif + +#if ENABLE_RTL8139 +#if !ENABLE_PCI +#error PCI support not enabled +#endif + +#include "pci.h" +#include "proc.h" +#include "rtl8139.h" + +INIT_SERVER_ASSERT + +#define RX_BUFSIZE RL_RCR_RBLEN_64K_SIZE +#define RX_BUFBITS RL_RCR_RBLEN_64K +#define N_TX_BUF RL_N_TX + +#if __minix_vmd +#define RE_PORT_NR 3 /* Minix-vmd */ +#else +#define RE_PORT_NR 1 /* Minix */ +#endif + +/* I/O vectors are handled IOVEC_NR entries at a time. */ +#define IOVEC_NR 16 + +/* Configuration */ +#define RL_ENVVAR "RTLETH" + +#if 0 +typedef struct rl_conf +{ + char *rlc_envvar; +} rl_conf_t; + +rl_conf_t rl_conf[]= /* Card addresses */ +{ + /* Env. var. */ + { "RTLETH0" }, + { "RTLETH1" }, + { "RTLETH2" }, +}; + +/* Test if rl_conf has exactly RE_PORT_NR entries. If not then you will see + * the error: "array size is negative". + */ +extern int ___dummy[RE_PORT_NR == sizeof(rl_conf)/sizeof(rl_conf[0]) ? 1 : -1]; +#endif + +PRIVATE struct pcitab +{ + u16_t vid; + u16_t did; + int checkclass; +} pcitab[]= +{ + { 0x10ec, 0x8139, 0 }, /* Realtek RTL8139 */ + { 0x1186, 0x1300, 0 }, /* D-Link RTL8139 */ + + { 0x0000, 0x0000, 0 } +}; + + +typedef struct re +{ + port_t re_base_port; + int re_irq; + int re_mode; + int re_flags; + int re_client; + int re_link_up; + int re_got_int; + int re_send_int; + int re_report_link; + int re_clear_rx; + int re_need_reset; + int re_tx_alive; + char *re_model; + + /* Rx */ + phys_bytes re_rx_buf; + vir_bytes re_read_s; + + /* Tx */ + int re_tx_head; + int re_tx_tail; + struct + { + int ret_busy; + phys_bytes ret_buf; + } re_tx[N_TX_BUF]; + u32_t re_ertxth; /* Early Tx Threshold */ + + + /* PCI related */ + int re_seen; /* TRUE iff device available */ + u8_t re_pcibus; + u8_t re_pcidev; + u8_t re_pcifunc; + + /* 'large' items */ + irq_hook_t re_hook; + eth_stat_t re_stat; + ether_addr_t re_address; + message re_rx_mess; + message re_tx_mess; + char re_name[sizeof("rtl8139#n")]; + iovec_t re_iovec[IOVEC_NR]; +} +re_t; + +#define REM_DISABLED 0x0 +#define REM_ENABLED 0x1 + +#if 0 +#define REF_STOPPED 0x400 +#endif +#define REF_PACK_SENT 0x001 +#define REF_PACK_RECV 0x002 +#define REF_SEND_AVAIL 0x004 +#define REF_READING 0x010 +#define REF_EMPTY 0x000 +#define REF_PROMISC 0x040 +#define REF_MULTI 0x080 +#define REF_BROAD 0x100 +#define REF_ENABLED 0x200 + + +static re_t re_table[RE_PORT_NR]; + +static int rl_tasknr; +static u16_t eth_ign_proto; +static tmra_ut rl_watchdog; + +#define rl_inb(port, offset) (inb((port) + (offset))) +#define rl_inw(port, offset) (inw((port) + (offset))) +#define rl_inl(port, offset) (inl((port) + (offset))) +#define rl_outb(port, offset, value) (outb((port) + (offset), (value))) +#define rl_outw(port, offset, value) (outw((port) + (offset), (value))) +#define rl_outl(port, offset, value) (outl((port) + (offset), (value))) + +_PROTOTYPE( static void rl_init, (message *mp) ); +_PROTOTYPE( static void rl_pci_conf, (void) ); +_PROTOTYPE( static int rl_probe, (re_t *rep) ); +_PROTOTYPE( static void rl_conf_hw, (re_t *rep) ); +_PROTOTYPE( static void rl_init_buf, (re_t *rep) ); +_PROTOTYPE( static void rl_init_hw, (re_t *rep) ); +_PROTOTYPE( static void rl_reset_hw, (re_t *rep) ); +_PROTOTYPE( static void rl_confaddr, (re_t *rep) ); +_PROTOTYPE( static void rl_rec_mode, (re_t *rep) ); +_PROTOTYPE( static void rl_readv, (message *mp, int from_int, + int vectored) ); +_PROTOTYPE( static void rl_writev, (message *mp, int from_int, + int vectored) ); +_PROTOTYPE( static void rl_check_ints, (re_t *rep) ); +_PROTOTYPE( static void rl_report_link, (re_t *rep) ); +_PROTOTYPE( static void mii_print_techab, (U16_t techab) ); +_PROTOTYPE( static void mii_print_stat_speed, (U16_t stat, + U16_t extstat) ); +_PROTOTYPE( static void rl_clear_rx, (re_t *rep) ); +_PROTOTYPE( static void rl_do_reset, (re_t *rep) ); +_PROTOTYPE( static void rl_getstat, (message *mp) ); +_PROTOTYPE( static void reply, (re_t *rep, int err, int may_block) ); +_PROTOTYPE( static void mess_reply, (message *req, message *reply) ); +_PROTOTYPE( static void put_userdata, (int user_proc, + vir_bytes user_addr, vir_bytes count, void *loc_addr) ); +_PROTOTYPE( static void rtl8139_stop, (void) ); +_PROTOTYPE( static void rtl8139_dump, (message *m) ); +#if 0 +_PROTOTYPE( static void dump_phy, (re_t *rep) ); +#endif +_PROTOTYPE( static int rl_handler, (irq_hook_t *hookp) ); +#if __minix_vmd +_PROTOTYPE( static void rl_watchdog_f, (tmra_ut *tp, tmr_arg_ut arg) ); +#else +_PROTOTYPE( static void rl_watchdog_f, (timer_t *tp) ); +#endif + +/* The message used in the main loop is made global, so that rl_watchdog_f() + * can change its message type to fake a HARD_INT message. + */ +PRIVATE message m; + +/*===========================================================================* + * rtl8139_task * + *===========================================================================*/ +void rtl8139_task() +{ + int i, r; + re_t *rep; + long v; + + if ((v=get_proc_nr(&rl_tasknr, NULL)) != OK) + server_panic("RTL8139", "Couldn't get own proc nr", v); + + v= 0; + (void) env_parse("ETH_IGN_PROTO", "x", 0, &v, 0x0000L, 0xFFFFL); + eth_ign_proto= htons((u16_t) v); + +#if !__minix_vmd + /* Claim buffer memory now under Minix, before MM takes it all. */ + for (rep= &re_table[0]; rep < re_table+RE_PORT_NR; rep++) + rl_init_buf(rep); +#endif + + /* Observe some function key for debug dumps. */ + if ((r=fkey_enable(SF9)) != OK) + printf("Warning: RTL8139 couldn't observe Shift+F9 key: %d\n",r); + + while (TRUE) + { + if ((r= receive(ANY, &m)) != OK) + server_panic("rtl8139","receive failed", r); + + switch (m.m_type) + { + case DL_WRITEV: rl_writev(&m, FALSE, TRUE); break; + case DL_WRITE: rl_writev(&m, FALSE, FALSE); break; +#if 0 + case DL_READ: do_vread(&m, FALSE); break; +#endif + case DL_READV: rl_readv(&m, FALSE, TRUE); break; + case DL_INIT: rl_init(&m); break; + case DL_GETSTAT: rl_getstat(&m); break; +#if 0 + case DL_STOP: do_stop(&m); break; +#endif +#if !__minix_vmd + case SYN_ALARM: + /* Under MINIX, synchronous alarms are used instead of + * watchdog functions. The approach is very different: + * MINIX VMD timeouts are handled within the kernel + * (the wathdog is executed by CLOCK), and notify() + * the driver in some cases. + * MINIX timeouts result in a SYN_ALARM message to the + * driver and thus are handled where they should be + * handled. Interrupt()s are faked by setting m_type to + * HARD_INT in rl_watchdog_f when needed, so that this + * case falls through. + */ + rl_watchdog_f(NULL); /* possibly changes m_type */ + if (m.m_type != HARD_INT) /* to HARD_INT if further */ + break; /* handling is needed */ +#endif /* fall through */ + case HARD_INT: + for (i= 0, rep= &re_table[0]; i<RE_PORT_NR; i++, rep++) + { + if (rep->re_mode != REM_ENABLED) + continue; + if (!rep->re_got_int) + continue; + rep->re_got_int= 0; + server_assert(rep->re_flags & REF_ENABLED); + rl_check_ints(rep); + } + break ; + case FKEY_PRESSED: rtl8139_dump(&m); break; + case HARD_STOP: rtl8139_stop(); break; + default: + server_panic("rtl8139","illegal message", m.m_type); + } + } +} + +/*===========================================================================* + * rtl8139_stop * + *===========================================================================*/ +static void rtl8139_stop() +{ + int i; + re_t *rep; + + for (i= 0, rep= &re_table[0]; i<RE_PORT_NR; i++, rep++) + { + if (rep->re_mode != REM_ENABLED) + continue; + rl_outb(rep->re_base_port, RL_CR, 0); + } + printf("RTL8139 driver stopped.\n", NO_ARG); +} + +/*===========================================================================* + * rtl8139_dump * + *===========================================================================*/ +static void rtl8139_dump(m) +message *m; /* pointer to request message */ +{ + re_t *rep; + int i; + + printf("\n"); + for (i= 0, rep = &re_table[0]; i<RE_PORT_NR; i++, rep++) + { + if (rep->re_mode == REM_DISABLED) + printf("Realtek RTL 8139 port %d is disabled\n", i); + + if (rep->re_mode != REM_ENABLED) + continue; + + printf("Realtek RTL 8139 statistics of port %d:\n", i); + + printf("recvErr :%8ld\t", rep->re_stat.ets_recvErr); + printf("sendErr :%8ld\t", rep->re_stat.ets_sendErr); + printf("OVW :%8ld\n", rep->re_stat.ets_OVW); + + printf("CRCerr :%8ld\t", rep->re_stat.ets_CRCerr); + printf("frameAll :%8ld\t", rep->re_stat.ets_frameAll); + printf("missedP :%8ld\n", rep->re_stat.ets_missedP); + + printf("packetR :%8ld\t", rep->re_stat.ets_packetR); + printf("packetT :%8ld\t", rep->re_stat.ets_packetT); + printf("transDef :%8ld\n", rep->re_stat.ets_transDef); + + printf("collision :%8ld\t", rep->re_stat.ets_collision); + printf("transAb :%8ld\t", rep->re_stat.ets_transAb); + printf("carrSense :%8ld\n", rep->re_stat.ets_carrSense); + + printf("fifoUnder :%8ld\t", rep->re_stat.ets_fifoUnder); + printf("fifoOver :%8ld\t", rep->re_stat.ets_fifoOver); + printf("CDheartbeat:%8ld\n", rep->re_stat.ets_CDheartbeat); + + printf("OWC :%8ld\t", rep->re_stat.ets_OWC); + + printf("re_flags = 0x%x\n", rep->re_flags); + + printf( + "TSAD: 0x%04x, TSD: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", + rl_inw(rep->re_base_port, RL_TSAD), + rl_inl(rep->re_base_port, RL_TSD0+0*4), + rl_inl(rep->re_base_port, RL_TSD0+1*4), + rl_inl(rep->re_base_port, RL_TSD0+2*4), + rl_inl(rep->re_base_port, RL_TSD0+3*4)); + printf("tx_head %d, tx_tail %d, busy: %d %d %d %d\n", + rep->re_tx_head, rep->re_tx_tail, + rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy, + rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy); + } +} + + +/*===========================================================================* + * do_init * + *===========================================================================*/ +static void rl_init(mp) +message *mp; +{ + static int first_time= 1; + + int port; + re_t *rep; +#if __minix_vmd + tmr_arg_ut t_arg; +#endif + message reply_mess; + + if (first_time) + { + first_time= 0; + rl_pci_conf(); /* Configure PCI devices. */ + + tmra_inittimer(&rl_watchdog); +#if __minix_vmd + t_arg.ta_int= 0; + tmra_settimer(&rl_watchdog, get_uptime()+HZ, + rl_watchdog_f, t_arg); +#else + /* Use a synchronous alarm instead of a watchdog timer. */ + sys_syncalrm(SELF, HZ, 0); +#endif + } + + port = mp->DL_PORT; + if (port < 0 || port >= RE_PORT_NR) + { + reply_mess.m_type= DL_INIT_REPLY; + reply_mess.m3_i1= ENXIO; + mess_reply(mp, &reply_mess); + return; + } + rep= &re_table[port]; + if (rep->re_mode == REM_DISABLED) + { + /* This is the default, try to (re)locate the device. */ + rl_conf_hw(rep); + if (rep->re_mode == REM_DISABLED) + { + /* Probe failed, or the device is configured off. */ + reply_mess.m_type= DL_INIT_REPLY; + reply_mess.m3_i1= ENXIO; + mess_reply(mp, &reply_mess); + return; + } + if (rep->re_mode == REM_ENABLED) + rl_init_hw(rep); + rl_report_link(rep); + } + + server_assert(rep->re_mode == REM_ENABLED); + server_assert(rep->re_flags & REF_ENABLED); + + rep->re_flags &= ~(REF_PROMISC | REF_MULTI | REF_BROAD); + + if (mp->DL_MODE & DL_PROMISC_REQ) + rep->re_flags |= REF_PROMISC; + if (mp->DL_MODE & DL_MULTI_REQ) + rep->re_flags |= REF_MULTI; + if (mp->DL_MODE & DL_BROAD_REQ) + rep->re_flags |= REF_BROAD; + + rep->re_client = mp->m_source; + rl_rec_mode(rep); + + reply_mess.m_type = DL_INIT_REPLY; + reply_mess.m3_i1 = mp->DL_PORT; + reply_mess.m3_i2 = RE_PORT_NR; + *(ether_addr_t *) reply_mess.m3_ca1 = rep->re_address; + + mess_reply(mp, &reply_mess); +} + +/*===========================================================================* + * rl_pci_conf * + *===========================================================================*/ +static void rl_pci_conf() +{ + int i, h; + re_t *rep; + static char envvar[] = RL_ENVVAR "#"; + static char envfmt[] = "*:d.d.d"; + static char val[128]; + long v; + + for (i= 0, rep= re_table; i<RE_PORT_NR; i++, rep++) + { + strcpy(rep->re_name, "rtl8139#0"); + rep->re_name[8] += i; + rep->re_seen= FALSE; + envvar[sizeof(RL_ENVVAR)-1]= '0'+i; + if (0 == sys_getkenv(envvar, strlen(envvar), val, sizeof(val)) && + ! env_prefix(envvar, "pci")) { + env_panic(envvar); + } + v= 0; + (void) env_parse(envvar, envfmt, 1, &v, 0, 255); + rep->re_pcibus= v; + v= 0; + (void) env_parse(envvar, envfmt, 2, &v, 0, 255); + rep->re_pcidev= v; + v= 0; + (void) env_parse(envvar, envfmt, 3, &v, 0, 255); + rep->re_pcifunc= v; + } + + pci_init(); + + for (h= 1; h >= 0; h--) { + for (i= 0, rep= re_table; i<RE_PORT_NR; i++, rep++) + { + if (((rep->re_pcibus | rep->re_pcidev | + rep->re_pcifunc) != 0) != h) + { + continue; + } + if (rl_probe(rep)) + rep->re_seen= TRUE; + } + } +} + +/*===========================================================================* + * rl_probe * + *===========================================================================*/ +static int rl_probe(rep) +re_t *rep; +{ + int i, r, devind, just_one; + u16_t vid, did; + u32_t bar; + u8_t ilr; + char *dname; + + if ((rep->re_pcibus | rep->re_pcidev | rep->re_pcifunc) != 0) + { + /* Look for specific PCI device */ + r= pci_find_dev(rep->re_pcibus, rep->re_pcidev, + rep->re_pcifunc, &devind); + if (r == 0) + { + printf("%s: no PCI found at %d.%d.%d\n", + rep->re_name, rep->re_pcibus, + rep->re_pcidev, rep->re_pcifunc); + return 0; + } + pci_ids(devind, &vid, &did); + just_one= TRUE; + } + else + { + r= pci_first_dev(&devind, &vid, &did); + if (r == 0) + return 0; + just_one= FALSE; + } + + for(;;) + { + for (i= 0; pcitab[i].vid != 0; i++) + { + if (pcitab[i].vid != vid) + continue; + if (pcitab[i].did != did) + continue; + if (pcitab[i].checkclass) + { + server_panic("rtl_probe", + "class check not implemented", NO_NUM); + } + break; + } + if (pcitab[i].vid != 0) + break; + + if (just_one) + { + printf( + "%s: wrong PCI device (%04x/%04x) found at %d.%d.%d\n", + rep->re_name, vid, did, + rep->re_pcibus, + rep->re_pcidev, rep->re_pcifunc); + return 0; + } + + r= pci_next_dev(&devind, &vid, &did); + if (!r) + return 0; + } + + dname= pci_dev_name(vid, did); + if (!dname) + dname= "unknown device"; + kprintf("%s: ", rep->re_name); + kprintf("%s ", dname); + kprintf("(%x/", vid); + kprintf("%x) ", did); + kprintf("at %s\n", pci_slot_name(devind)); + pci_reserve(devind); + /* printf("cr = 0x%x\n", pci_attr_r16(devind, PCI_CR)); */ + bar= pci_attr_r32(devind, PCI_BAR) & 0xffffffe0; + if ((bar & 0x3ff) >= 0x100-32 || bar < 0x400) + server_panic("rtl_probe", + "base address is not properly configured", NO_NUM); + rep->re_base_port= bar; + + ilr= pci_attr_r8(devind, PCI_ILR); + rep->re_irq= ilr; + if (debug) + { + printf("%s: using I/O address 0x%lx, IRQ %d\n", + rep->re_name, (unsigned long)bar, ilr); + } + + return TRUE; +} + +/*===========================================================================* + * rl_conf_hw * + *===========================================================================*/ +static void rl_conf_hw(rep) +re_t *rep; +{ + static eth_stat_t empty_stat = {0, 0, 0, 0, 0, 0 /* ,... */ }; + + rep->re_mode= REM_DISABLED; /* Superfluous */ + + if (rep->re_seen) + { + /* PCI device is present */ + rep->re_mode= REM_ENABLED; + } + if (rep->re_mode != REM_ENABLED) + return; + + rep->re_flags= REF_EMPTY; + rep->re_link_up= -1; /* Unknown */ + rep->re_got_int= 0; + rep->re_send_int= 0; + rep->re_report_link= 0; + rep->re_clear_rx= 0; + rep->re_need_reset= 0; + rep->re_tx_alive= 0; + rep->re_read_s= 0; + rep->re_tx_head= 0; + rep->re_tx_tail= 0; + rep->re_ertxth= RL_TSD_ERTXTH_8; + rep->re_stat= empty_stat; +} + +/*===========================================================================* + * rl_init_buf * + *===========================================================================*/ +static void rl_init_buf(rep) +re_t *rep; +{ + size_t rx_bufsize, tx_bufsize, tot_bufsize; + phys_bytes buf; + int i; + + /* Allocate receive and transmit buffers */ + tx_bufsize= ETH_MAX_PACK_SIZE_TAGGED; + if (tx_bufsize % 4) + tx_bufsize += 4-(tx_bufsize % 4); /* Align */ + rx_bufsize= RX_BUFSIZE; + tot_bufsize= N_TX_BUF*tx_bufsize + rx_bufsize; +#if __minix_vmd + buf= (phys_bytes)kalloc_dma_128K(tot_bufsize, FALSE /* not low */); +#else + /* Now try to allocate a kernel memory buffer. */ + if (OK != (i=sys_kmalloc(tot_bufsize, &buf))) + server_panic("RTL8139","Couldn't allocate kernel buffer",i); +#endif + for (i= 0; i<N_TX_BUF; i++) + { + rep->re_tx[i].ret_buf= buf; + buf += tx_bufsize; + } + rep->re_rx_buf= buf; +} + +/*===========================================================================* + * rl_init_hw * + *===========================================================================*/ +static void rl_init_hw(rep) +re_t *rep; +{ + int i; + +#if __minix_vmd + rl_init_buf(rep); +#endif + + rep->re_flags = REF_EMPTY; + rep->re_flags |= REF_ENABLED; + + /* set the interrupt handler */ + put_irq_handler(&rep->re_hook, rep->re_irq, rl_handler); + + rl_reset_hw(rep); + + enable_irq(&rep->re_hook); + + if (rep->re_mode) { + kprintf("%s: ", rep->re_name); + kprintf("model %s\n", rep->re_model); + } else + { + printf("%s: unknown model 0x%08x\n", + rep->re_name, + rl_inl(rep->re_base_port, RL_TCR) & + (RL_TCR_HWVER_AM | RL_TCR_HWVER_BM)); + } + + rl_confaddr(rep); + if (debug) + { + printf("%s: Ethernet address ", rep->re_name); + for (i= 0; i < 6; i++) + { + printf("%x%c", rep->re_address.ea_addr[i], + i < 5 ? ':' : '\n'); + } + } +} + +/*===========================================================================* + * rl_reset_hw * + *===========================================================================*/ +static void rl_reset_hw(rep) +re_t *rep; +{ + port_t port; + u32_t t; + phys_bytes bus_buf; + int i; + static int timeout; /* must be static if not cancelled */ + + port= rep->re_base_port; + +#if 0 + /* Reset the PHY */ + rl_outb(port, RL_BMCR, MII_CTRL_RST); + timeout=0; + sys_flagalrm(HZ, &timeout); + do { + if (!(rl_inb(port, RL_BMCR) & MII_CTRL_RST)) + break; + } while (! timeout); + sys_flagalrm(0, &timeout); + if (rl_inb(port, RL_BMCR) & MII_CTRL_RST) + server_panic("rtl8139","reset PHY failed to complete", NO_NUM); +#endif + + /* Reset the device */ + rl_outb(port, RL_CR, RL_CR_RST); + timeout=0; + sys_flagalrm(HZ, &timeout); + do { + if (!(rl_inb(port, RL_CR) & RL_CR_RST)) + break; + } while (! timeout); + sys_flagalrm(0, &timeout); + if (rl_inb(port, RL_CR) & RL_CR_RST) + server_panic("rtl8139","reset failed to complete", NO_NUM); + + t= rl_inl(port, RL_TCR); + switch(t & (RL_TCR_HWVER_AM | RL_TCR_HWVER_BM)) + { + case RL_TCR_HWVER_RTL8139: rep->re_model= "RTL8139"; break; + case RL_TCR_HWVER_RTL8139A: rep->re_model= "RTL8139A"; break; + case RL_TCR_HWVER_RTL8139AG: + rep->re_model= "RTL8139A-G / RTL8139C"; + break; + case RL_TCR_HWVER_RTL8139B: + rep->re_model= "RTL8139B / RTL8130"; + break; + case RL_TCR_HWVER_RTL8100: rep->re_model= "RTL8100"; break; + case RL_TCR_HWVER_RTL8100B: + rep->re_model= "RTL8100B/RTL8139D"; + break; + case RL_TCR_HWVER_RTL8139CP: rep->re_model= "RTL8139C+"; break; + case RL_TCR_HWVER_RTL8101: rep->re_model= "RTL8101"; break; + default: + rep->re_model= NULL; + break; + } + +#if 0 + printf("REVID: 0x%02x\n", rl_inb(port, RL_REVID)); +#endif + + /* Intialize Rx */ + + /* Should init multicast mask */ +#if 0 +08-0f R/W MAR[0-7] multicast +#endif + bus_buf= vm_1phys2bus(rep->re_rx_buf); + rl_outl(port, RL_RBSTART, bus_buf); + + /* Initialize Tx */ + for (i= 0; i<N_TX_BUF; i++) + { + rep->re_tx[i].ret_busy= FALSE; + bus_buf= vm_1phys2bus(rep->re_tx[i].ret_buf); + rl_outl(port, RL_TSAD0+i*4, bus_buf); + t= rl_inl(port, RL_TSD0+i*4); + server_assert(t & RL_TSD_OWN); + } + +#if 0 + dump_phy(rep); +#endif + + t= rl_inw(port, RL_IMR); + rl_outw(port, RL_IMR, t | (RL_IMR_SERR | RL_IMR_TIMEOUT | + RL_IMR_LENCHG)); + + t= rl_inw(port, RL_IMR); + rl_outw(port, RL_IMR, t | (RL_IMR_FOVW | RL_IMR_PUN | + RL_IMR_RXOVW | RL_IMR_RER | RL_IMR_ROK)); + + t= rl_inw(port, RL_IMR); + rl_outw(port, RL_IMR, t | (RL_IMR_TER | RL_IMR_TOK)); + + t= rl_inb(port, RL_CR); + rl_outb(port, RL_CR, t | RL_CR_RE); + + t= rl_inb(port, RL_CR); + rl_outb(port, RL_CR, t | RL_CR_TE); + + rl_outl(port, RL_RCR, RX_BUFBITS); + + t= rl_inl(port, RL_TCR); + rl_outl(port, RL_TCR, t | RL_TCR_IFG_STD); +} + +/*===========================================================================* + * rl_confaddr * + *===========================================================================*/ +static void rl_confaddr(rep) +re_t *rep; +{ + static char eakey[]= RL_ENVVAR "#_EA"; + static char eafmt[]= "x:x:x:x:x:x"; + + int i; + port_t port; + u32_t w; + long v; + + /* User defined ethernet address? */ + eakey[sizeof(RL_ENVVAR)-1]= '0' + (rep-re_table); + + port= rep->re_base_port; + + for (i= 0; i < 6; i++) + { + if (env_parse(eakey, eafmt, i, &v, 0x00L, 0xFFL) != EP_SET) + break; + rep->re_address.ea_addr[i]= v; + } + + if (i != 0 && i != 6) env_panic(eakey); /* It's all or nothing */ + + + /* Should update ethernet address in hardware */ + if (i == 6) + { + port= rep->re_base_port; + rl_outb(port, RL_9346CR, RL_9346CR_EEM_CONFIG); + w= 0; + for (i= 0; i<4; i++) + w |= (rep->re_address.ea_addr[i] << (i*8)); + rl_outl(port, RL_IDR, w); + w= 0; + for (i= 4; i<6; i++) + w |= (rep->re_address.ea_addr[i] << ((i-4)*8)); + rl_outl(port, RL_IDR+4, w); + rl_outb(port, RL_9346CR, RL_9346CR_EEM_NORMAL); + } + + /* Get ethernet address */ + for (i= 0; i<6; i++) + rep->re_address.ea_addr[i]= rl_inb(port, RL_IDR+i); +} + +/*===========================================================================* + * rl_rec_mode * + *===========================================================================*/ +static void rl_rec_mode(rep) +re_t *rep; +{ + port_t port; + u32_t rcr; + + port= rep->re_base_port; + rcr= rl_inl(port, RL_RCR); + rcr &= ~(RL_RCR_AB|RL_RCR_AM|RL_RCR_APM|RL_RCR_AAP); + if (rep->re_flags & REF_PROMISC) + rcr |= RL_RCR_AB | RL_RCR_AM | RL_RCR_AAP; + if (rep->re_flags & REF_BROAD) + rcr |= RL_RCR_AB; + if (rep->re_flags & REF_MULTI) + rcr |= RL_RCR_AM; + rcr |= RL_RCR_APM; + + rl_outl(port, RL_RCR, rcr); +} + +/*===========================================================================* + * rl_readv * + *===========================================================================*/ +static void rl_readv(mp, from_int, vectored) +message *mp; +int from_int; +int vectored; +{ + int i, j, n, o, s, s1, dl_port, re_client, count, size; + port_t port; + unsigned amount, totlen, packlen; + phys_bytes src_phys, dst_phys, iov_src; + u16_t d_start, d_end; + u32_t l, rxstat; + re_t *rep; + iovec_t *iovp; + + dl_port = mp->DL_PORT; + count = mp->DL_COUNT; + if (dl_port < 0 || dl_port >= RE_PORT_NR) + server_panic("rtl8139"," illegal port", dl_port); + rep= &re_table[dl_port]; + re_client= mp->DL_PROC; + rep->re_client= re_client; + + if (rep->re_clear_rx) + goto suspend; /* Buffer overflow */ + + server_assert(rep->re_mode == REM_ENABLED); + server_assert(rep->re_flags & REF_ENABLED); + + port= rep->re_base_port; + + /* Assume that the RL_CR_BUFE check was been done by rl_checks_ints + */ + if (!from_int && (rl_inb(port, RL_CR) & RL_CR_BUFE)) + { + /* Receive buffer is empty, suspend */ + goto suspend; + } + + d_start= rl_inw(port, RL_CAPR) + RL_CAPR_DATA_OFF; + d_end= rl_inw(port, RL_CBR) % RX_BUFSIZE; + + if (d_start >= RX_BUFSIZE) + { + printf("rl_readv: strange value in RL_CAPR: 0x%x\n", + rl_inw(port, RL_CAPR)); + d_start %= RX_BUFSIZE; + } + + if (d_end > d_start) + amount= d_end-d_start; + else + amount= d_end+RX_BUFSIZE - d_start; + + src_phys= rep->re_rx_buf + d_start; + dst_phys= vir2phys(&rxstat); + phys_copy(src_phys, dst_phys, sizeof(rxstat)); + + if (rep->re_clear_rx) + { +#if 0 + printf("rl_readv: late buffer overflow\n"); +#endif + goto suspend; /* Buffer overflow */ + } + + /* Should convert from little endian to host byte order */ + + if (!(rxstat & RL_RXS_ROK)) + { + printf("rxstat = 0x%08lx\n", rxstat); + printf("d_start: 0x%x, d_end: 0x%x, rxstat: 0x%lx\n", + d_start, d_end, rxstat); + server_panic("rtl8139","received packet not OK", NO_NUM); + } + totlen= (rxstat >> RL_RXS_LEN_S); + if (totlen < 8 || totlen > 2*ETH_MAX_PACK_SIZE) + { + /* Someting went wrong */ + printf( + "rl_readv: bad length (%u) in status 0x%08lx at offset 0x%x\n", + totlen, rxstat, d_start); + printf( + "d_start: 0x%x, d_end: 0x%x, totlen: %d, rxstat: 0x%lx\n", + d_start, d_end, totlen, rxstat); + server_panic(NULL, NULL, NO_NUM); + } + +#if 0 + printf("d_start: 0x%x, d_end: 0x%x, totlen: %d, rxstat: 0x%x\n", + d_start, d_end, totlen, rxstat); +#endif + + if (totlen+4 > amount) + { + printf("rl_readv: packet not yet ready\n"); + goto suspend; + } + + /* Should subtract the CRC */ + packlen= totlen - ETH_CRC_SIZE; + + if (vectored) + { + iov_src = numap_local(re_client, (vir_bytes)mp->DL_ADDR, + count * sizeof(rep->re_iovec[0])); + if (!iov_src) + server_panic("rtl8139","umap_local failed", NO_NUM); + + size= 0; + o= d_start+4; + src_phys= rep->re_rx_buf; + for (i= 0; i<count; i += IOVEC_NR, + iov_src += IOVEC_NR * sizeof(rep->re_iovec[0])) + { + n= IOVEC_NR; + if (i+n > count) + n= count-i; + phys_copy(iov_src, vir2phys(rep->re_iovec), + n * sizeof(rep->re_iovec[0])); + + for (j= 0, iovp= rep->re_iovec; j<n; j++, iovp++) + { + s= iovp->iov_size; + if (size + s > packlen) + { + server_assert(packlen > size); + s= packlen-size; + } + + dst_phys = numap_local(re_client, iovp->iov_addr, s); + if (!dst_phys) + server_panic("rtl8139","umap_local failed\n", + NO_NUM); + + if (o >= RX_BUFSIZE) + { + o -= RX_BUFSIZE; + server_assert(o < RX_BUFSIZE); + } + + if (o+s > RX_BUFSIZE) + { + server_assert(o<RX_BUFSIZE); + s1= RX_BUFSIZE-o; + + phys_copy(src_phys+o, dst_phys, s1); + phys_copy(src_phys, dst_phys+s1, s-s1); + } + else + { + phys_copy(src_phys+o, dst_phys, s); + } + + size += s; + if (size == packlen) + break; + o += s; + } + if (size == packlen) + break; + } + if (size < packlen) + { + server_assert(0); + } + } + else + { + server_assert(0); +#if 0 + size= mp->DL_COUNT; + if (size < ETH_MIN_PACK_SIZE || size > ETH_MAX_PACK_SIZE_TAGGED) + server_panic("rtl8139","invalid packet size", size); + phys_user = numap_local(re_client, (vir_bytes)mp->DL_ADDR, size); + if (!phys_user) + server_panic("rtl8139","umap_local failed", NO_NUM); + + p= rep->re_tx[tx_head].ret_buf; + phys_copy(phys_user, p, size); +#endif + } + + if (rep->re_clear_rx) + { + /* For some reason the receiver FIFO is not stopped when + * the buffer is full. + */ +#if 0 + printf("rl_readv: later buffer overflow\n"); +#endif + goto suspend; /* Buffer overflow */ + } + + + rep->re_stat.ets_packetR++; + rep->re_read_s= packlen; + rep->re_flags= (rep->re_flags & ~REF_READING) | REF_PACK_RECV; + + /* Avoid overflow in 16-bit computations */ + l= d_start; + l += totlen+4; + l= (l+3) & ~3; /* align */ + if (l >= RX_BUFSIZE) + { + l -= RX_BUFSIZE; + server_assert(l < RX_BUFSIZE); + } + rl_outw(port, RL_CAPR, l-RL_CAPR_DATA_OFF); + + if (!from_int) + reply(rep, OK, FALSE); + + return; + +suspend: + if (from_int) + { + server_assert(rep->re_flags & REF_READING); + + /* No need to store any state */ + return; + } + + rep->re_rx_mess= *mp; + server_assert(!(rep->re_flags & REF_READING)); + rep->re_flags |= REF_READING; + + reply(rep, OK, FALSE); +} + +/*===========================================================================* + * rl_writev * + *===========================================================================*/ +static void rl_writev(mp, from_int, vectored) +message *mp; +int from_int; +int vectored; +{ + phys_bytes p, iov_src, phys_user; + int i, j, n, s, port, count, size; + int tx_head, re_client; + re_t *rep; + iovec_t *iovp; + + port = mp->DL_PORT; + count = mp->DL_COUNT; + if (port < 0 || port >= RE_PORT_NR) + server_panic("rtl8139","illegal port", port); + rep= &re_table[port]; + re_client= mp->DL_PROC; + rep->re_client= re_client; + + server_assert(rep->re_mode == REM_ENABLED); + server_assert(rep->re_flags & REF_ENABLED); + + if (from_int) + { + server_assert(rep->re_flags & REF_SEND_AVAIL); + rep->re_flags &= ~REF_SEND_AVAIL; + rep->re_send_int= FALSE; + rep->re_tx_alive= TRUE; + } + + tx_head= rep->re_tx_head; + if (rep->re_tx[tx_head].ret_busy) + { + server_assert(!(rep->re_flags & REF_SEND_AVAIL)); + rep->re_flags |= REF_SEND_AVAIL; + if (rep->re_tx[tx_head].ret_busy) + goto suspend; + + /* Race condition, the interrupt handler may clear re_busy + * before we got a chance to set REF_SEND_AVAIL. Checking + * ret_busy twice should be sufficient. + */ +#if 0 + printf("rl_writev: race detected\n"); +#endif + rep->re_flags &= ~REF_SEND_AVAIL; + rep->re_send_int= FALSE; + } + + server_assert(!(rep->re_flags & REF_SEND_AVAIL)); + server_assert(!(rep->re_flags & REF_PACK_SENT)); + + if (vectored) + { + + iov_src = numap_local(re_client, (vir_bytes)mp->DL_ADDR, + count * sizeof(rep->re_iovec[0])); + if (!iov_src) + server_panic("rtl8139","umap_local failed", NO_NUM); + + size= 0; + p= rep->re_tx[tx_head].ret_buf; + for (i= 0; i<count; i += IOVEC_NR, + iov_src += IOVEC_NR * sizeof(rep->re_iovec[0])) + { + n= IOVEC_NR; + if (i+n > count) + n= count-i; + phys_copy(iov_src, vir2phys(rep->re_iovec), + n * sizeof(rep->re_iovec[0])); + + for (j= 0, iovp= rep->re_iovec; j<n; j++, iovp++) + { + s= iovp->iov_size; + if (size + s > ETH_MAX_PACK_SIZE_TAGGED) + { + server_panic("rtl8139","invalid packet size", + NO_NUM); + } + + phys_user = numap_local(re_client, iovp->iov_addr, s); + if (!phys_user) + server_panic("rtl8139","umap_local failed\n", + NO_NUM); + phys_copy(phys_user, p, s); + size += s; + p += s; + } + } + if (size < ETH_MIN_PACK_SIZE) + server_panic("rtl8139","invalid packet size", size); + } + else + { + size= mp->DL_COUNT; + if (size < ETH_MIN_PACK_SIZE || size > ETH_MAX_PACK_SIZE_TAGGED) + server_panic("rtl8139","invalid packet size", size); + phys_user = numap_local(re_client, (vir_bytes)mp->DL_ADDR, size); + if (!phys_user) + server_panic("rtl8139","umap_local failed\n", NO_NUM); + + p= rep->re_tx[tx_head].ret_buf; + phys_copy(phys_user, p, size); + } + + rl_outl(rep->re_base_port, RL_TSD0+tx_head*4, + rep->re_ertxth | size); + rep->re_tx[tx_head].ret_busy= TRUE; + + if (++tx_head == N_TX_BUF) + tx_head= 0; + server_assert(tx_head < RL_N_TX); + rep->re_tx_head= tx_head; + + rep->re_flags |= REF_PACK_SENT; + + /* If the interrupt handler called, don't send a reply. The reply + * will be sent after all interrupts are handled. + */ + if (from_int) + return; + reply(rep, OK, FALSE); + return; + +suspend: +#if 0 + printf("rl_writev: head %d, tail %d, busy: %d %d %d %d\n", + tx_head, rep->re_tx_tail, + rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy, + rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy); + printf("rl_writev: TSD: 0x%x, 0x%x, 0x%x, 0x%x\n", + rl_inl(rep->re_base_port, RL_TSD0+0*4), + rl_inl(rep->re_base_port, RL_TSD0+1*4), + rl_inl(rep->re_base_port, RL_TSD0+2*4), + rl_inl(rep->re_base_port, RL_TSD0+3*4)); +#endif + + if (from_int) + server_panic("rtl8139","should not be sending\n", NO_NUM); + + rep->re_tx_mess= *mp; + reply(rep, OK, FALSE); +} + +/*===========================================================================* + * rl_check_ints * + *===========================================================================*/ +static void rl_check_ints(rep) +re_t *rep; +{ +#if 0 +10-1f R/W TSD[0-3] Transmit Status of Descriptor [0-3] + 31 R CRS Carrier Sense Lost + 30 R TABT Transmit Abort + 29 R OWC Out of Window Collision + 27-24 R NCC[3-0] Number of Collision Count + 23-22 reserved + 21-16 R/W ERTXH[5-0] Early Tx Threshold + 15 R TOK Transmit OK + 14 R TUN Transmit FIFO Underrun + 13 R/W OWN OWN + 12-0 R/W SIZE Descriptor Size +3e-3f R/W ISR Interrupt Status Register + 6 R/W FOVW Fx FIFO Overflow Interrupt + 5 R/W PUN/LinkChg Packet Underrun / Link Change Interrupt + 3 R/W TER Transmit Error Interrupt + 2 R/W TOK Transmit OK Interrupt +3e-3f R/W ISR Interrupt Status Register + 15 R/W SERR System Error Interrupt + 14 R/W TimeOut Time Out Interrupt + 13 R/W LenChg Cable Length Change Interrupt +3e-3f R/W ISR Interrupt Status Register + 4 R/W RXOVW Rx Buffer Overflow Interrupt + 1 R/W RER Receive Error Interrupt + 0 R/W ROK Receive OK Interrupt +4c-4f R/W MPC Missed Packet Counter +60-61 R TSAD Transmit Status of All Descriptors + 15-12 R TOK[3-0] TOK bit of Descriptor [3-0] + 11-8 R TUN[3-0] TUN bit of Descriptor [3-0] + 7-4 R TABT[3-0] TABT bit of Descriptor [3-0] + 3-0 R OWN[3-0] OWN bit of Descriptor [3-0] +6c-6d R DIS Disconnect Counter + 15-0 R DCNT Disconnect Counter +6e-6f R FCSC False Carrier Sense Counter + 15-0 R FCSCNT False Carrier event counter +72-73 R REC RX_ER Counter + 15-0 R RXERCNT Received packet counter +#endif + + int re_flags; + + re_flags= rep->re_flags; + + if ((re_flags & REF_READING) && + !(rl_inb(rep->re_base_port, RL_CR) & RL_CR_BUFE)) + { + if (rep->re_rx_mess.m_type == DL_READV) + { + rl_readv(&rep->re_rx_mess, TRUE /* from int */, + TRUE /* vectored */); + } + else + { + server_assert(rep->re_rx_mess.m_type == DL_READ); + rl_readv(&rep->re_rx_mess, TRUE /* from int */, + FALSE /* !vectored */); + } + } + if (rep->re_clear_rx) + rl_clear_rx(rep); + + if (rep->re_need_reset) + rl_do_reset(rep); + + if (rep->re_send_int) + { + if (rep->re_tx_mess.m_type == DL_WRITEV) + { + rl_writev(&rep->re_tx_mess, TRUE /* from int */, + TRUE /* vectored */); + } + else + { + server_assert(rep->re_tx_mess.m_type == DL_WRITE); + rl_writev(&rep->re_tx_mess, TRUE /* from int */, + FALSE /* !vectored */); + } + } + + if (rep->re_report_link) + rl_report_link(rep); + + if (rep->re_flags & (REF_PACK_SENT | REF_PACK_RECV)) + reply(rep, OK, TRUE); +} + +/*===========================================================================* + * rl_report_link * + *===========================================================================*/ +static void rl_report_link(rep) +re_t *rep; +{ + port_t port; + u16_t mii_ctrl, mii_status, mii_ana, mii_anlpa, mii_ane, mii_extstat; + u8_t msr; + int f, link_up; + + rep->re_report_link= FALSE; + port= rep->re_base_port; + msr= rl_inb(port, RL_MSR); + link_up= !(msr & RL_MSR_LINKB); + rep->re_link_up= link_up; + if (!link_up) + { + kprintf("%s: link down\n", rep->re_name); + return; + } + + mii_ctrl= rl_inw(port, RL_BMCR); + mii_status= rl_inw(port, RL_BMSR); + mii_ana= rl_inw(port, RL_ANAR); + mii_anlpa= rl_inw(port, RL_ANLPAR); + mii_ane= rl_inw(port, RL_ANER); + mii_extstat= 0; + + if (mii_ctrl & (MII_CTRL_LB|MII_CTRL_PD|MII_CTRL_ISO)) + { + printf("%s: PHY: ", rep->re_name); + f= 1; + if (mii_ctrl & MII_CTRL_LB) + { + printf("loopback mode"); + f= 0; + } + if (mii_ctrl & MII_CTRL_PD) + { + if (!f) printf(", "); + f= 0; + printf("powered down"); + } + if (mii_ctrl & MII_CTRL_ISO) + { + if (!f) printf(", "); + f= 0; + printf("isolated"); + } + printf("\n"); + return; + } + if (!(mii_ctrl & MII_CTRL_ANE)) + { + printf("%s: manual config: ", rep->re_name); + switch(mii_ctrl & (MII_CTRL_SP_LSB|MII_CTRL_SP_MSB)) + { + case MII_CTRL_SP_10: printf("10 Mbps"); break; + case MII_CTRL_SP_100: printf("100 Mbps"); break; + case MII_CTRL_SP_1000: printf("1000 Mbps"); break; + case MII_CTRL_SP_RES: printf("reserved speed"); break; + } + if (mii_ctrl & MII_CTRL_DM) + printf(", full duplex"); + else + printf(", half duplex"); + printf("\n"); + return; + } + + if (!debug) goto resspeed; + + printf("%s: ", rep->re_name); + mii_print_stat_speed(mii_status, mii_extstat); + printf("\n"); + + if (!(mii_status & MII_STATUS_ANC)) + printf("%s: auto-negotiation not complete\n", rep->re_name); + if (mii_status & MII_STATUS_RF) + printf("%s: remote fault detected\n", rep->re_name); + if (!(mii_status & MII_STATUS_ANA)) + { + printf("%s: local PHY has no auto-negotiation ability\n", + rep->re_name); + } + if (!(mii_status & MII_STATUS_LS)) + kprintf("%s: link down\n", rep->re_name); + if (mii_status & MII_STATUS_JD) + printf("%s: jabber condition detected\n", rep->re_name); + if (!(mii_status & MII_STATUS_EC)) + { + printf("%s: no extended register set\n", rep->re_name); + goto resspeed; + } + if (!(mii_status & MII_STATUS_ANC)) + goto resspeed; + + printf("%s: local cap.: ", rep->re_name); + mii_print_techab(mii_ana); + printf("\n"); + + if (mii_ane & MII_ANE_PDF) + printf("%s: parallel detection fault\n", rep->re_name); + if (!(mii_ane & MII_ANE_LPANA)) + { + printf("%s: link-partner does not support auto-negotiation\n", + rep->re_name); + goto resspeed; + } + + printf("%s: remote cap.: ", rep->re_name); + mii_print_techab(mii_anlpa); + printf("\n"); + +resspeed: + kprintf("%s: ", rep->re_name); + kprintf("link up at %d Mbps, ", (msr & RL_MSR_SPEED_10) ? 10 : 100); + kprintf("%s duplex\n", (mii_ctrl & MII_CTRL_DM) ? "full" : "half"); + +} + +static void mii_print_techab(techab) +u16_t techab; +{ + int fs, ft; + if ((techab & MII_ANA_SEL_M) != MII_ANA_SEL_802_3) + { + printf("strange selector 0x%x, value 0x%x", + techab & MII_ANA_SEL_M, + (techab & MII_ANA_TAF_M) >> MII_ANA_TAF_S); + return; + } + fs= 1; + if (techab & (MII_ANA_100T4 | MII_ANA_100TXFD | MII_ANA_100TXHD)) + { + printf("100 Mbps: "); + fs= 0; + ft= 1; + if (techab & MII_ANA_100T4) + { + printf("T4"); + ft= 0; + } + if (techab & (MII_ANA_100TXFD | MII_ANA_100TXHD)) + { + if (!ft) + printf(", "); + ft= 0; + printf("TX-"); + switch(techab & (MII_ANA_100TXFD|MII_ANA_100TXHD)) + { + case MII_ANA_100TXFD: printf("FD"); break; + case MII_ANA_100TXHD: printf("HD"); break; + default: printf("FD/HD"); break; + } + } + } + if (techab & (MII_ANA_10TFD | MII_ANA_10THD)) + { + if (!fs) + printf(", "); + printf("10 Mbps: "); + fs= 0; + printf("T-"); + switch(techab & (MII_ANA_10TFD|MII_ANA_10THD)) + { + case MII_ANA_10TFD: printf("FD"); break; + case MII_ANA_10THD: printf("HD"); break; + default: printf("FD/HD"); break; + } + } + if (techab & MII_ANA_PAUSE_SYM) + { + if (!fs) + printf(", "); + fs= 0; + printf("pause(SYM)"); + } + if (techab & MII_ANA_PAUSE_ASYM) + { + if (!fs) + printf(", "); + fs= 0; + printf("pause(ASYM)"); + } + if (techab & MII_ANA_TAF_RES) + { + if (!fs) + printf(", "); + fs= 0; + printf("0x%x", (techab & MII_ANA_TAF_RES) >> MII_ANA_TAF_S); + } +} + +static void mii_print_stat_speed(stat, extstat) +u16_t stat; +u16_t extstat; +{ + int fs, ft; + fs= 1; + if (stat & MII_STATUS_EXT_STAT) + { + if (extstat & (MII_ESTAT_1000XFD | MII_ESTAT_1000XHD | + MII_ESTAT_1000TFD | MII_ESTAT_1000THD)) + { + printf("1000 Mbps: "); + fs= 0; + ft= 1; + if (extstat & (MII_ESTAT_1000XFD | MII_ESTAT_1000XHD)) + { + ft= 0; + printf("X-"); + switch(extstat & + (MII_ESTAT_1000XFD|MII_ESTAT_1000XHD)) + { + case MII_ESTAT_1000XFD: printf("FD"); break; + case MII_ESTAT_1000XHD: printf("HD"); break; + default: printf("FD/HD"); break; + } + } + if (extstat & (MII_ESTAT_1000TFD | MII_ESTAT_1000THD)) + { + if (!ft) + printf(", "); + ft= 0; + printf("T-"); + switch(extstat & + (MII_ESTAT_1000TFD|MII_ESTAT_1000THD)) + { + case MII_ESTAT_1000TFD: printf("FD"); break; + case MII_ESTAT_1000THD: printf("HD"); break; + default: printf("FD/HD"); break; + } + } + } + } + if (stat & (MII_STATUS_100T4 | + MII_STATUS_100XFD | MII_STATUS_100XHD | + MII_STATUS_100T2FD | MII_STATUS_100T2HD)) + { + if (!fs) + printf(", "); + fs= 0; + printf("100 Mbps: "); + ft= 1; + if (stat & MII_STATUS_100T4) + { + printf("T4"); + ft= 0; + } + if (stat & (MII_STATUS_100XFD | MII_STATUS_100XHD)) + { + if (!ft) + printf(", "); + ft= 0; + printf("TX-"); + switch(stat & (MII_STATUS_100XFD|MII_STATUS_100XHD)) + { + case MII_STATUS_100XFD: printf("FD"); break; + case MII_STATUS_100XHD: printf("HD"); break; + default: printf("FD/HD"); break; + } + } + if (stat & (MII_STATUS_100T2FD | MII_STATUS_100T2HD)) + { + if (!ft) + printf(", "); + ft= 0; + printf("T2-"); + switch(stat & (MII_STATUS_100T2FD|MII_STATUS_100T2HD)) + { + case MII_STATUS_100T2FD: printf("FD"); break; + case MII_STATUS_100T2HD: printf("HD"); break; + default: printf("FD/HD"); break; + } + } + } + if (stat & (MII_STATUS_10FD | MII_STATUS_10HD)) + { + if (!fs) + printf(", "); + printf("10 Mbps: "); + fs= 0; + printf("T-"); + switch(stat & (MII_STATUS_10FD|MII_STATUS_10HD)) + { + case MII_STATUS_10FD: printf("FD"); break; + case MII_STATUS_10HD: printf("HD"); break; + default: printf("FD/HD"); break; + } + } +} + +/*===========================================================================* + * rl_clear_rx * + *===========================================================================*/ +static void rl_clear_rx(rep) +re_t *rep; +{ + port_t port; + u8_t cr; + int i; + static int timeout; /* must be static if not cancelled */ + + rep->re_clear_rx= FALSE; + port= rep->re_base_port; + + /* Reset the receiver */ + cr= rl_inb(port, RL_CR); + cr &= ~RL_CR_RE; + rl_outb(port, RL_CR, cr); + timeout=0; + sys_flagalrm(HZ, &timeout); + do { + if (!(rl_inb(port, RL_CR) & RL_CR_RE)) + break; + } while (! timeout); + sys_flagalrm(0, &timeout); + if (rl_inb(port, RL_CR) & RL_CR_RE) + server_panic("rtl8139","cannot disable receiver", NO_NUM); + +#if 0 + printf("RBSTART = 0x%08x\n", rl_inl(port, RL_RBSTART)); + printf("CAPR = 0x%04x\n", rl_inw(port, RL_CAPR)); + printf("CBR = 0x%04x\n", rl_inw(port, RL_CBR)); + printf("RCR = 0x%08x\n", rl_inl(port, RL_RCR)); +#endif + + rl_outb(port, RL_CR, cr | RL_CR_RE); + + rl_outl(port, RL_RCR, RX_BUFBITS); + + rl_rec_mode(rep); + + rep->re_stat.ets_missedP++; +} + +/*===========================================================================* + * rl_do_reset * + *===========================================================================*/ +static void rl_do_reset(rep) +re_t *rep; +{ + rep->re_need_reset= FALSE; + rl_reset_hw(rep); + rl_rec_mode(rep); + + rep->re_tx_head= 0; + if (rep->re_flags & REF_SEND_AVAIL) + { + rep->re_tx[rep->re_tx_head].ret_busy= FALSE; + rep->re_send_int= TRUE; + } +} + +/*===========================================================================* + * rl_getstat * + *===========================================================================*/ +static void rl_getstat(mp) +message *mp; +{ + int port; + eth_stat_t stats; + re_t *rep; + + port = mp->DL_PORT; + if (port < 0 || port >= RE_PORT_NR) + server_panic("rtl8139","illegal port", port); + rep= &re_table[port]; + rep->re_client= mp->DL_PROC; + + server_assert(rep->re_mode == REM_ENABLED); + server_assert(rep->re_flags & REF_ENABLED); + + lock(); /* Interrupt handler updates stats */ + stats= rep->re_stat; + unlock(); + + put_userdata(mp->DL_PROC, (vir_bytes) mp->DL_ADDR, + (vir_bytes) sizeof(stats), &stats); + reply(rep, OK, FALSE); +} + + +/*===========================================================================* + * reply * + *===========================================================================*/ +static void reply(rep, err, may_block) +re_t *rep; +int err; +int may_block; +{ + message reply; + int status; + int r; + clock_t now; + + status = 0; + if (rep->re_flags & REF_PACK_SENT) + status |= DL_PACK_SEND; + if (rep->re_flags & REF_PACK_RECV) + status |= DL_PACK_RECV; + + reply.m_type = DL_TASK_REPLY; + reply.DL_PORT = rep - re_table; + reply.DL_PROC = rep->re_client; + reply.DL_STAT = status | ((u32_t) err << 16); + reply.DL_COUNT = rep->re_read_s; + if (OK != (r = sys_getuptime(&now))) + server_panic("dp8390","sys_getuptime() failed:", r); + reply.DL_CLCK = now; + + r= send(rep->re_client, &reply); + + if (r == ELOCKED && may_block) + { + printW(); printf("send locked\n"); + return; + } + + if (r < 0) + server_panic("dp8390","send failed:", r); + + rep->re_read_s = 0; + rep->re_flags &= ~(REF_PACK_SENT | REF_PACK_RECV); +} + + + +/*===========================================================================* + * mess_reply * + *===========================================================================*/ +static void mess_reply(req, reply_mess) +message *req; +message *reply_mess; +{ + if (send(req->m_source, reply_mess) != OK) + server_panic("rtl8139","unable to mess_reply", NO_NUM); +} + +/*===========================================================================* + * put_userdata * + *===========================================================================*/ +static void put_userdata(user_proc, user_addr, count, loc_addr) +int user_proc; +vir_bytes user_addr; +vir_bytes count; +void *loc_addr; +{ + phys_bytes dst; + + dst = numap_local(user_proc, user_addr, count); + if (!dst) + server_panic("rtl8139","umap_local failed", NO_NUM); + + phys_copy(vir2phys(loc_addr), dst, (phys_bytes) count); +} + +#if 0 +static void dump_phy(rep) +re_t *rep; +{ + port_t port; + u32_t t; + + port= rep->re_base_port; + + t= rl_inb(port, RL_MSR); + printf("MSR: 0x%02lx\n", t); + if (t & RL_MSR_SPEED_10) + printf("\t10 Mbps\n"); + if (t & RL_MSR_LINKB) + printf("\tLink failed\n"); + + t= rl_inb(port, RL_CONFIG1); + printf("CONFIG1: 0x%02lx\n", t); + + t= rl_inb(port, RL_CONFIG3); + printf("CONFIG3: 0x%02lx\n", t); + + t= rl_inb(port, RL_CONFIG4); + printf("CONFIG4: 0x%02lx\n", t); + + t= rl_inw(port, RL_BMCR); + printf("BMCR (MII_CTRL): 0x%04lx\n", t); + + t= rl_inw(port, RL_BMSR); + printf("BMSR:"); + if (t & MII_STATUS_100T4) + printf(" 100Base-T4"); + if (t & MII_STATUS_100XFD) + printf(" 100Base-X-FD"); + if (t & MII_STATUS_100XHD) + printf(" 100Base-X-HD"); + if (t & MII_STATUS_10FD) + printf(" 10Mbps-FD"); + if (t & MII_STATUS_10HD) + printf(" 10Mbps-HD"); + if (t & MII_STATUS_100T2FD) + printf(" 100Base-T2-FD"); + if (t & MII_STATUS_100T2HD) + printf(" 100Base-T2-HD"); + if (t & MII_STATUS_EXT_STAT) + printf(" Ext-stat"); + if (t & MII_STATUS_RES) + printf(" res-0x%lx", t & MII_STATUS_RES); + if (t & MII_STATUS_MFPS) + printf(" MFPS"); + if (t & MII_STATUS_ANC) + printf(" ANC"); + if (t & MII_STATUS_RF) + printf(" remote-fault"); + if (t & MII_STATUS_ANA) + printf(" ANA"); + if (t & MII_STATUS_LS) + printf(" Link"); + if (t & MII_STATUS_JD) + printf(" Jabber"); + if (t & MII_STATUS_EC) + printf(" Extended-capability"); + printf("\n"); + + t= rl_inw(port, RL_ANAR); + printf("ANAR (MII_ANA): 0x%04lx\n", t); + + t= rl_inw(port, RL_ANLPAR); + printf("ANLPAR: 0x%04lx\n", t); + + t= rl_inw(port, RL_ANER); + printf("ANER (MII_ANE): "); + if (t & MII_ANE_RES) + printf(" res-0x%lx", t & MII_ANE_RES); + if (t & MII_ANE_PDF) + printf(" Par-Detect-Fault"); + if (t & MII_ANE_LPNPA) + printf(" LP-Next-Page-Able"); + if (t & MII_ANE_NPA) + printf(" Loc-Next-Page-Able"); + if (t & MII_ANE_PR) + printf(" Page-Received"); + if (t & MII_ANE_LPANA) + printf(" LP-Auto-Neg-Able"); + printf("\n"); + + t= rl_inw(port, RL_NWAYTR); + printf("NWAYTR: 0x%04lx\n", t); + t= rl_inw(port, RL_CSCR); + printf("CSCR: 0x%04lx\n", t); + + t= rl_inb(port, RL_CONFIG5); + printf("CONFIG5: 0x%02lx\n", t); +} +#endif + + +/*===========================================================================* + * rl_handler * + *===========================================================================*/ +static int rl_handler(hookp) +irq_hook_t *hookp; +{ + int i, port, tx_head, tx_tail, link_up; + u16_t isr, tsad; + u32_t tsd, tcr, ertxth; +#if 0 + u8_t cr; +#endif + re_t *rep; + static int timeout; /* must be static if not cancelled */ + + RAND_UPDATE + + rep= structof(re_t, re_hook, hookp); + + port= rep->re_base_port; + + /* Ack interrupt */ + isr= rl_inw(port, RL_ISR); + rl_outw(port, RL_ISR, isr); + + if (isr & RL_IMR_FOVW) + { + isr &= ~RL_IMR_FOVW; + /* Should do anything? */ + + rep->re_stat.ets_fifoOver++; + } + if (isr & RL_IMR_PUN) + { + isr &= ~RL_IMR_PUN; + + /* Either the link status changed or there was a TX fifo + * underrun. + */ + link_up= !(rl_inb(port, RL_MSR) & RL_MSR_LINKB); + if (link_up != rep->re_link_up) + { + rep->re_report_link= TRUE; + rep->re_got_int= TRUE; + notify(rl_tasknr, HARD_INT); + } + } + if (isr & RL_IMR_RXOVW) + { + isr &= ~RL_IMR_RXOVW; + + /* Clear the receive buffer */ + rep->re_clear_rx= TRUE; + rep->re_got_int= TRUE; + notify(rl_tasknr, HARD_INT); + } + + if (isr & (RL_ISR_RER | RL_ISR_ROK)) + { + isr &= ~(RL_ISR_RER | RL_ISR_ROK); + + if (!rep->re_got_int && (rep->re_flags & REF_READING)) + { + rep->re_got_int= TRUE; + notify(rl_tasknr, HARD_INT); + } + } +#if 0 + if ((isr & (RL_ISR_TER | RL_ISR_TOK)) && + (rep->re_flags & REF_SEND_AVAIL) && + (rep->re_tx[0].ret_busy || rep->re_tx[1].ret_busy || + rep->re_tx[2].ret_busy || rep->re_tx[3].ret_busy)) + + { + printf( + "rl_handler, SEND_AVAIL: tx_head %d, tx_tail %d, busy: %d %d %d %d\n", + rep->re_tx_head, rep->re_tx_tail, + rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy, + rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy); + printf( + "rl_handler: TSAD: 0x%04x, TSD: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", + rl_inw(port, RL_TSAD), + rl_inl(port, RL_TSD0+0*4), + rl_inl(port, RL_TSD0+1*4), + rl_inl(port, RL_TSD0+2*4), + rl_inl(port, RL_TSD0+3*4)); + } +#endif + if ((isr & (RL_ISR_TER | RL_ISR_TOK)) || 1) + { + isr &= ~(RL_ISR_TER | RL_ISR_TOK); + + tsad= rl_inw(port, RL_TSAD); + if (tsad & (RL_TSAD_TABT0|RL_TSAD_TABT1| + RL_TSAD_TABT2|RL_TSAD_TABT3)) + { +#if 0 + /* Do we need a watch dog? */ + /* Just reset the whole chip */ + rep->re_need_reset= TRUE; + rep->re_got_int= TRUE; + notify(rl_tasknr, HARD_INT); +#elif 0 + /* Reset transmitter */ + rep->re_stat.ets_transAb++; + + cr= rl_inb(port, RL_CR); + cr &= ~RL_CR_TE; + rl_outb(port, RL_CR, cr); + timeout=0; + sys_flagalrm(HZ, &timeout); + do { + if (!(rl_inb(port, RL_CR) & RL_CR_TE)) + break; + } while (! timeout); + sys_flagalrm(0, &timeout); + if (rl_inb(port, RL_CR) & RL_CR_TE) + { + server_panic("rtl8139","cannot disable transmitter", + NO_NUM); + } + rl_outb(port, RL_CR, cr | RL_CR_TE); + + tcr= rl_inl(port, RL_TCR); + rl_outl(port, RL_TCR, tcr | RL_TCR_IFG_STD); + + printf("rl_handler: reset after abort\n"); + + if (rep->re_flags & REF_SEND_AVAIL) + { + printf("rl_handler: REF_SEND_AVAIL\n"); + rep->re_send_int= TRUE; + rep->re_got_int= TRUE; + notify(rl_tasknr, HARD_INT); + } + for (i= 0; i< N_TX_BUF; i++) + rep->re_tx[i].ret_busy= FALSE; + rep->re_tx_head= 0; +#else + printf("rl_handler, TABT, tasd = 0x%04x\n", + tsad); + + /* Find the aborted transmit request */ + for (i= 0; i< N_TX_BUF; i++) + { + tsd= rl_inl(port, RL_TSD0+i*4); + if (tsd & RL_TSD_TABT) + break; + } + if (i >= N_TX_BUF) + { + printf( + "rl_handler: can't find aborted TX req.\n"); + } + else + { + printf("TSD%d = 0x%04lx\n", i, tsd); + + /* Set head and tail to this buffer */ + rep->re_tx_head= rep->re_tx_tail= i; + } + + /* Aborted transmission, just kick the device + * and be done with it. + */ + rep->re_stat.ets_transAb++; + tcr= rl_inl(port, RL_TCR); + rl_outl(port, RL_TCR, tcr | RL_TCR_CLRABT); +#endif + } + + /* Transmit completed */ + tx_head= rep->re_tx_head; + tx_tail= rep->re_tx_tail; + for (i= 0; i< 2*N_TX_BUF; i++) + { + if (!rep->re_tx[tx_tail].ret_busy) + { + /* Strange, this buffer is not in-use. + * Increment tx_tail until tx_head is + * reached (or until we find a buffer that + * is in-use. + */ + if (tx_tail == tx_head) + break; + if (++tx_tail >= N_TX_BUF) + tx_tail= 0; + server_assert(tx_tail < RL_N_TX); + rep->re_tx_tail= tx_tail; + continue; + } + tsd= rl_inl(port, RL_TSD0+tx_tail*4); + if (!(tsd & RL_TSD_OWN)) + { + /* Buffer is not yet ready */ + break; + } + + /* Should collect statistics */ + if (tsd & RL_TSD_CRS) + rep->re_stat.ets_carrSense++; + if (tsd & RL_TSD_TABT) + { + printf("rl_handler, TABT, TSD%d = 0x%04lx\n", + tx_tail, tsd); + server_assert(0); /* CLRABT is not all that + * effective, why not? + */ + rep->re_stat.ets_transAb++; + tcr= rl_inl(port, RL_TCR); + rl_outl(port, RL_TCR, tcr | RL_TCR_CLRABT); + } + if (tsd & RL_TSD_OWC) + rep->re_stat.ets_OWC++; + if (tsd & RL_TSD_CDH) + rep->re_stat.ets_CDheartbeat++; + + /* What about collisions? */ + if (tsd & RL_TSD_TOK) + rep->re_stat.ets_packetT++; + else + rep->re_stat.ets_sendErr++; + if (tsd & RL_TSD_TUN) + { + rep->re_stat.ets_fifoUnder++; + + /* Increase ERTXTH */ + ertxth= tsd + (1 << RL_TSD_ERTXTH_S); + ertxth &= RL_TSD_ERTXTH_M; + if (debug && ertxth > rep->re_ertxth) + { + printf("%s: new ertxth: %ld bytes\n", + rep->re_name, + (ertxth >> RL_TSD_ERTXTH_S) * + 32); + rep->re_ertxth= ertxth; + } + } + rep->re_tx[tx_tail].ret_busy= FALSE; + +#if 0 + if (rep->re_flags & REF_SEND_AVAIL) + { + printf("TSD%d: %08lx\n", tx_tail, tsd); + printf( + "rl_handler: head %d, tail %d, busy: %d %d %d %d\n", + tx_head, tx_tail, + rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy, + rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy); + } +#endif + + if (++tx_tail >= N_TX_BUF) + tx_tail= 0; + server_assert(tx_tail < RL_N_TX); + rep->re_tx_tail= tx_tail; + + if (rep->re_flags & REF_SEND_AVAIL) + { +#if 0 + printf("rl_handler: REF_SEND_AVAIL\n"); +#endif + rep->re_send_int= TRUE; + if (!rep->re_got_int) + { + rep->re_got_int= TRUE; + notify(rl_tasknr, HARD_INT); + } + } + } + server_assert(i < 2*N_TX_BUF); + } + if (isr) + { + printf("rl_handler: unhandled interrupt: isr = 0x%04x\n", + isr); + } + + return 1; +} + +/*===========================================================================* + * rl_watchdog_f * + *===========================================================================*/ +#if __minix_vmd +static void rl_watchdog_f(tp, arg) +tmra_ut *tp; +tmr_arg_ut arg; +#else +static void rl_watchdog_f(tp) +timer_t *tp; +#endif +{ + int i; + re_t *rep; +#if __minix_vmd + tmr_arg_ut t_arg; + + server_assert(tp == &rl_watchdog); + t_arg.ta_int= 0; + tmra_settimer(&rl_watchdog, get_uptime()+HZ, rl_watchdog_f, t_arg); +#else + /* Use a synchronous alarm instead of a watchdog timer. */ + sys_syncalrm(SELF, HZ, 0); +#endif + + for (i= 0, rep = &re_table[0]; i<RE_PORT_NR; i++, rep++) + { + if (rep->re_mode != REM_ENABLED) + continue; + if (!(rep->re_flags & REF_SEND_AVAIL)) + { + /* Assume that an idle system is alive */ + rep->re_tx_alive= TRUE; + continue; + } + if (rep->re_tx_alive) + { + rep->re_tx_alive= FALSE; + continue; + } + printf("rl_watchdog_f: resetting port %d\n", i); + printf( + "TSAD: 0x%04x, TSD: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n", + rl_inw(rep->re_base_port, RL_TSAD), + rl_inl(rep->re_base_port, RL_TSD0+0*4), + rl_inl(rep->re_base_port, RL_TSD0+1*4), + rl_inl(rep->re_base_port, RL_TSD0+2*4), + rl_inl(rep->re_base_port, RL_TSD0+3*4)); + printf("tx_head %d, tx_tail %d, busy: %d %d %d %d\n", + rep->re_tx_head, rep->re_tx_tail, + rep->re_tx[0].ret_busy, rep->re_tx[1].ret_busy, + rep->re_tx[2].ret_busy, rep->re_tx[3].ret_busy); + rep->re_need_reset= TRUE; + rep->re_got_int= TRUE; +#if __minix_vmd + notify(rl_tasknr, HARD_INT); +#else + /* Under MINIX, we got here via a synchronous alarm call. + * Change the message type to HARD_INT to fake an interrupt. + * The switch in the main loop 'falls through' if it sees + * the HARD_INT message type. + */ + m.m_type = HARD_INT; +#endif + } +} + + +#if 0 + +_PROTOTYPE( static void rtl_init, (struct dpeth *dep) ); +_PROTOTYPE( static u16_t get_ee_word, (dpeth_t *dep, int a) ); +_PROTOTYPE( static void ee_wen, (dpeth_t *dep) ); +_PROTOTYPE( static void set_ee_word, (dpeth_t *dep, int a, U16_t w) ); +_PROTOTYPE( static void ee_wds, (dpeth_t *dep) ); + +static void rtl_init(dep) +dpeth_t *dep; +{ + u8_t reg_a, reg_b, cr, config0, config2, config3; + int i; + char val[128]; + + printf("rtl_init called\n"); + ne_init(dep); + + /* ID */ + outb_reg0(dep, DP_CR, CR_PS_P0); + reg_a = inb_reg0(dep, DP_DUM1); + reg_b = inb_reg0(dep, DP_DUM2); + + printf("rtl_init: '%c', '%c'\n", reg_a, reg_b); + + outb_reg0(dep, DP_CR, CR_PS_P3); + config0 = inb_reg3(dep, 3); + config2 = inb_reg3(dep, 5); + config3 = inb_reg3(dep, 6); + outb_reg0(dep, DP_CR, CR_PS_P0); + + printf("rtl_init: config 0/2/3 = %x/%x/%x\n", + config0, config2, config3); + + if (0 == sys_getkenv("RTL8029FD",9+1, val, sizeof(val))) + { + printf("rtl_init: setting full-duplex mode\n"); + outb_reg0(dep, DP_CR, CR_PS_P3); + + cr= inb_reg3(dep, 1); + outb_reg3(dep, 1, cr | 0xc0); + + outb_reg3(dep, 6, config3 | 0x40); + config3 = inb_reg3(dep, 6); + + config2= inb_reg3(dep, 5); + outb_reg3(dep, 5, config2 | 0x20); + config2= inb_reg3(dep, 5); + + outb_reg3(dep, 1, cr); + + outb_reg0(dep, DP_CR, CR_PS_P0); + + printf("rtl_init: config 2 = %x\n", config2); + printf("rtl_init: config 3 = %x\n", config3); + } + + for (i= 0; i<64; i++) + printf("%x ", get_ee_word(dep, i)); + printf("\n"); + + if (0 == sys_getkenv("RTL8029MN",9+1, val, sizeof(val))) + { + ee_wen(dep); + + set_ee_word(dep, 0x78/2, 0x10ec); + set_ee_word(dep, 0x7A/2, 0x8029); + set_ee_word(dep, 0x7C/2, 0x10ec); + set_ee_word(dep, 0x7E/2, 0x8029); + + ee_wds(dep); + + server_assert(get_ee_word(dep, 0x78/2) == 0x10ec); + server_assert(get_ee_word(dep, 0x7A/2) == 0x8029); + server_assert(get_ee_word(dep, 0x7C/2) == 0x10ec); + server_assert(get_ee_word(dep, 0x7E/2) == 0x8029); + } + + if (0 == sys_getkenv("RTL8029XXX",10+1, val, sizeof(val))) + { + ee_wen(dep); + + set_ee_word(dep, 0x76/2, 0x8029); + + ee_wds(dep); + + server_assert(get_ee_word(dep, 0x76/2) == 0x8029); + } +} + +static u16_t get_ee_word(dep, a) +dpeth_t *dep; +int a; +{ + int b, i, cmd; + u16_t w; + + outb_reg0(dep, DP_CR, CR_PS_P3); /* Bank 3 */ + + /* Switch to 9346 mode and enable CS */ + outb_reg3(dep, 1, 0x80 | 0x8); + + cmd= 0x180 | (a & 0x3f); /* 1 1 0 a5 a4 a3 a2 a1 a0 */ + for (i= 8; i >= 0; i--) + { + b= (cmd & (1 << i)); + b= (b ? 2 : 0); + + /* Cmd goes out on the rising edge of the clock */ + outb_reg3(dep, 1, 0x80 | 0x8 | b); + outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b); + } + outb_reg3(dep, 1, 0x80 | 0x8); /* End of cmd */ + + w= 0; + for (i= 0; i<16; i++) + { + w <<= 1; + + /* Data is shifted out on the rising edge. Read at the + * falling edge. + */ + outb_reg3(dep, 1, 0x80 | 0x8 | 0x4); + outb_reg3(dep, 1, 0x80 | 0x8 | b); + b= inb_reg3(dep, 1); + w |= (b & 1); + } + + outb_reg3(dep, 1, 0x80); /* drop CS */ + outb_reg3(dep, 1, 0x00); /* back to normal */ + outb_reg0(dep, DP_CR, CR_PS_P0); /* back to bank 0 */ + + return w; +} + +static void ee_wen(dep) +dpeth_t *dep; +{ + int b, i, cmd; + u16_t w; + + outb_reg0(dep, DP_CR, CR_PS_P3); /* Bank 3 */ + + /* Switch to 9346 mode and enable CS */ + outb_reg3(dep, 1, 0x80 | 0x8); + + cmd= 0x130; /* 1 0 0 1 1 x x x x */ + for (i= 8; i >= 0; i--) + { + b= (cmd & (1 << i)); + b= (b ? 2 : 0); + + /* Cmd goes out on the rising edge of the clock */ + outb_reg3(dep, 1, 0x80 | 0x8 | b); + outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b); + } + outb_reg3(dep, 1, 0x80 | 0x8); /* End of cmd */ + outb_reg3(dep, 1, 0x80); /* Drop CS */ + /* micro_delay(1); */ /* Is this required? */ +} + +static void set_ee_word(dep, a, w) +dpeth_t *dep; +int a; +u16_t w; +{ + int b, i, cmd; + static int timeout; /* must be static if not cancelled */ + + outb_reg3(dep, 1, 0x80 | 0x8); /* Set CS */ + + cmd= 0x140 | (a & 0x3f); /* 1 0 1 a5 a4 a3 a2 a1 a0 */ + for (i= 8; i >= 0; i--) + { + b= (cmd & (1 << i)); + b= (b ? 2 : 0); + + /* Cmd goes out on the rising edge of the clock */ + outb_reg3(dep, 1, 0x80 | 0x8 | b); + outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b); + } + for (i= 15; i >= 0; i--) + { + b= (w & (1 << i)); + b= (b ? 2 : 0); + + /* Cmd goes out on the rising edge of the clock */ + outb_reg3(dep, 1, 0x80 | 0x8 | b); + outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b); + } + outb_reg3(dep, 1, 0x80 | 0x8); /* End of data */ + outb_reg3(dep, 1, 0x80); /* Drop CS */ + /* micro_delay(1); */ /* Is this required? */ + outb_reg3(dep, 1, 0x80 | 0x8); /* Set CS */ + timeout = 0; + sys_flagalrm(1, &timeout); + do { + if (inb_reg3(dep, 1) & 1) + break; + } while (! timeout); + sys_flagalrm(0, &timeout); + if (!(inb_reg3(dep, 1) & 1)) + server_panic("set_ee_word","device remains busy", NO_NUM); +} + +static void ee_wds(dep) +dpeth_t *dep; +{ + int b, i, cmd; + u16_t w; + + outb_reg0(dep, DP_CR, CR_PS_P3); /* Bank 3 */ + + /* Switch to 9346 mode and enable CS */ + outb_reg3(dep, 1, 0x80 | 0x8); + + cmd= 0x100; /* 1 0 0 0 0 x x x x */ + for (i= 8; i >= 0; i--) + { + b= (cmd & (1 << i)); + b= (b ? 2 : 0); + + /* Cmd goes out on the rising edge of the clock */ + outb_reg3(dep, 1, 0x80 | 0x8 | b); + outb_reg3(dep, 1, 0x80 | 0x8 | 0x4 | b); + } + outb_reg3(dep, 1, 0x80 | 0x8); /* End of cmd */ + outb_reg3(dep, 1, 0x80); /* Drop CS */ + outb_reg3(dep, 1, 0x00); /* back to normal */ + outb_reg0(dep, DP_CR, CR_PS_P0); /* back to bank 0 */ +} +#endif +#endif /* ENABLE_RTL8139 */ + +/* + * $PchId: rtl8139.c,v 1.3 2003/09/11 14:15:15 philip Exp $ + */ diff --git a/kernel/rtl8139.h b/kernel/rtl8139.h new file mode 100755 index 000000000..1b5ee93fc --- /dev/null +++ b/kernel/rtl8139.h @@ -0,0 +1,430 @@ +/* +ibm/rtl8139.h + +Created: Aug 2003 by Philip Homburg <philip@cs.vu.nl> +*/ + +#define RL_IDR 0x00 /* Ethernet address + * Note: RL_9346CR_EEM_CONFIG mode is + * required the change the ethernet + * address. + * Note: 4-byte write access only. + */ +#define RL_N_TX 4 /* Number of transmit buffers */ +#define RL_TSD0 0x010 /* Transmit Status of Descriptor 0 */ +#define RL_TSD_CRS 0x80000000 /* Carrier Sense Lost */ +#define RL_TSD_TABT 0x40000000 /* Transmit Abort */ +#define RL_TSD_OWC 0x20000000 /* Out of Window Collision */ +#define RL_TSD_CDH 0x10000000 /* CD Heart Beat */ +#define RL_TSD_NCC_M 0x0F000000 /* Number of Collision Count */ +#define RL_TSD_RES 0x00C00000 /* Reserved */ +#define RL_TSD_ERTXTH_M 0x003F0000 /* Early Tx Threshold */ +#define RL_TSD_ERTXTH_S 16 /* shift */ +#define RL_TSD_ERTXTH_8 0x00000000 /* 8 bytes */ +#define RL_TSD_TOK 0x00008000 /* Transmit OK */ +#define RL_TSD_TUN 0x00004000 /* Transmit FIFO Underrun */ +#define RL_TSD_OWN 0x00002000 /* Controller (does not) Own Buf. */ +#define RL_TSD_SIZE 0x00001FFF /* Descriptor Size */ +#define RL_TSAD0 0x20 /* Transmit Start Address of Descriptor 0 */ +#define RL_RBSTART 0x30 /* Receive Buffer Start Address */ +#define RL_CR 0x37 /* Command Register */ +#define RL_CR_RES0 0xE0 /* Reserved */ +#define RL_CR_RST 0x10 /* Reset */ +#define RL_CR_RE 0x08 /* Receiver Enable */ +#define RL_CR_TE 0x04 /* Transmitter Enable * + * Note: start with transmit buffer + * 0 after RL_CR_TE has been reset. + */ +#define RL_CR_RES1 0x02 /* Reserved */ +#define RL_CR_BUFE 0x01 /* Receive Buffer Empty */ +#define RL_CAPR 0x38 /* Current Address of Packet Read */ +#define RL_CAPR_DATA_OFF 0x10 /* Packet Starts at Offset */ +#define RL_CBR 0x3A /* Current Buffer Address */ +#define RL_IMR 0x3C /* Interrupt Mask Register */ +#define RL_IMR_SERR 0x8000 /* System Error */ +#define RL_IMR_TIMEOUT 0x4000 /* Time Out */ +#define RL_IMR_LENCHG 0x2000 /* Cable Length Change */ +#define RL_IMR_RES 0x1F80 /* Reserved */ +#define RL_IMR_FOVW 0x0040 /* Rx FIFO Overflow */ +#define RL_IMR_PUN 0x0020 /* Packet Underrun / Link Change */ +#define RL_IMR_RXOVW 0x0010 /* Rx Buffer Overflow */ +#define RL_IMR_TER 0x0008 /* Transmit Error */ +#define RL_IMR_TOK 0x0004 /* Transmit OK */ +#define RL_IMR_RER 0x0002 /* Receive Error */ +#define RL_IMR_ROK 0x0001 /* Receive OK */ +#define RL_ISR 0x3E /* Interrupt Status Register */ +#define RL_ISR_SERR 0x8000 /* System Error */ +#define RL_ISR_TIMEOUT 0x4000 /* Time Out */ +#define RL_ISR_LENCHG 0x2000 /* Cable Length Change */ +#define RL_ISR_RES 0x1F80 /* Reserved */ +#define RL_ISR_FOVW 0x0040 /* Rx FIFO Overflow */ +#define RL_ISR_PUN 0x0020 /* Packet Underrun / Link Change */ +#define RL_ISR_RXOVW 0x0010 /* Rx Buffer Overflow */ +#define RL_ISR_TER 0x0008 /* Transmit Error */ +#define RL_ISR_TOK 0x0004 /* Transmit OK */ +#define RL_ISR_RER 0x0002 /* Receive Error */ +#define RL_ISR_ROK 0x0001 /* Receive OK */ +#define RL_TCR 0x40 /* Transmit Configuration Register + * Note: RL_CR_TE has to be set to + * set/change RL_TCR. + */ +#define RL_TCR_RES0 0x80000000 /* Reserved */ +#define RL_TCR_HWVER_AM 0x7C000000 /* Hardware Version ID A */ +#define RL_TCR_IFG_M 0x03000000 /* Interframe Gap Time */ +#define RL_TCR_IFG_STD 0x03000000 /* IEEE 802.3 std */ +#if 0 +#undef RL_TCR_IFG_STD +#define RL_TCR_IFG_STD 0x00000000 +#endif +#define RL_TCR_HWVER_BM 0x00C00000 /* Hardware Version ID B */ +#define RL_TCR_HWVER_RTL8139 0x60000000 /* RTL8139 */ +#define RL_TCR_HWVER_RTL8139A 0x70000000 /* RTL8139A */ +#define RL_TCR_HWVER_RTL8139AG 0x74000000 /* RTL8139A-G */ +#define RL_TCR_HWVER_RTL8139B 0x78000000 /* RTL8139B */ +#define RL_TCR_HWVER_RTL8130 0x78000000 /* RTL8130 (dup) */ +#define RL_TCR_HWVER_RTL8139C 0x74000000 /* RTL8139C (dup) */ +#define RL_TCR_HWVER_RTL8100 0x78800000 /* RTL8100 */ +#define RL_TCR_HWVER_RTL8100B 0x74400000 /* RTL8100B / + RTL8139D */ +#define RL_TCR_HWVER_RTL8139CP 0x74800000 /* RTL8139C+ */ +#define RL_TCR_HWVER_RTL8101 0x74C00000 /* RTL8101 */ +#define RL_TCR_RES1 0x00380000 /* Reserved */ +#define RL_TCR_LBK_M 0x00060000 /* Loopback Test */ +#define RL_TCR_LBK_NORMAL 0x00000000 /* Normal */ +#define RL_TCR_LBK_LOOKBOCK 0x00060000 /* Loopback Mode */ +#define RL_TCR_CRC 0x00010000 /* (Do not) Append CRC */ +#define RL_TCR_RES2 0x0000F800 /* Reserved */ +#define RL_TCR_MXDMA_M 0x00000700 /* Max DMA Burst Size Tx */ +#define RL_TCR_MXDMA_16 0x00000000 /* 16 bytes */ +#define RL_TCR_MXDMA_32 0x00000100 /* 32 bytes */ +#define RL_TCR_MXDMA_64 0x00000200 /* 64 bytes */ +#define RL_TCR_MXDMA_128 0x00000300 /* 128 bytes */ +#define RL_TCR_MXDMA_128 0x00000300 /* 128 bytes */ +#define RL_TCR_MXDMA_256 0x00000400 /* 256 bytes */ +#define RL_TCR_MXDMA_512 0x00000500 /* 512 bytes */ +#define RL_TCR_MXDMA_1024 0x00000600 /* 1024 bytes */ +#define RL_TCR_MXDMA_2048 0x00000700 /* 2048 bytes */ +#define RL_TCR_TXRR_M 0x000000F0 /* Tx Retry Count */ +#define RL_TCR_RES3 0x0000000E /* Reserved */ +#define RL_TCR_CLRABT 0x00000001 /* Clear Abort */ +#define RL_RCR 0x44 /* Receive Configuration Register + * Note: RL_CR_RE has to be set to + * set/change RL_RCR. + */ +#define RL_RCR_RES0 0xF0000000 /& Reserved */ +#define RL_RCR_ERTH_M 0x0F000000 /* Early Rx Threshold */ +#define RL_RCR_ERTH_0 0x00000000 /* No threshold */ +#define RL_RCR_ERTH_1 0x01000000 /* 1/16 */ +#define RL_RCR_ERTH_2 0x02000000 /* 2/16 */ +#define RL_RCR_ERTH_3 0x03000000 /* 3/16 */ +#define RL_RCR_ERTH_4 0x04000000 /* 4/16 */ +#define RL_RCR_ERTH_5 0x05000000 /* 5/16 */ +#define RL_RCR_ERTH_6 0x06000000 /* 6/16 */ +#define RL_RCR_ERTH_7 0x07000000 /* 7/16 */ +#define RL_RCR_ERTH_8 0x08000000 /* 8/16 */ +#define RL_RCR_ERTH_9 0x09000000 /* 9/16 */ +#define RL_RCR_ERTH_10 0x0A000000 /* 10/16 */ +#define RL_RCR_ERTH_11 0x0B000000 /* 11/16 */ +#define RL_RCR_ERTH_12 0x0C000000 /* 12/16 */ +#define RL_RCR_ERTH_13 0x0D000000 /* 13/16 */ +#define RL_RCR_ERTH_14 0x0E000000 /* 14/16 */ +#define RL_RCR_ERTH_15 0x0F000000 /* 15/16 */ +#define RL_RCR_RES1 0x00FC0000 /* Reserved */ +#define RL_RCR_MULERINT 0x00020000 /* Multiple Early Int Select */ +#define RL_RCR_RER8 0x00010000 /* Receive small error packet */ +#define RL_RCR_RXFTH_M 0x0000E000 /* Rx FIFO Threshold */ +#define RL_RCR_RXFTH_16 0x00000000 /* 16 bytes */ +#define RL_RCR_RXFTH_32 0x00002000 /* 32 bytes */ +#define RL_RCR_RXFTH_64 0x00004000 /* 64 bytes */ +#define RL_RCR_RXFTH_128 0x00006000 /* 128 bytes */ +#define RL_RCR_RXFTH_256 0x00008000 /* 256 bytes */ +#define RL_RCR_RXFTH_512 0x0000A000 /* 512 bytes */ +#define RL_RCR_RXFTH_1024 0x0000C000 /* 1024 bytes */ +#define RL_RCR_RXFTH_UNLIM 0x0000E000 /* unlimited */ +#define RL_RCR_RBLEM_M 0x00001800 /* Rx Buffer Length */ +#define RL_RCR_RBLEN_8K 0x00000000 /* 8KB + 16 bytes */ +#define RL_RCR_RBLEN_8K_SIZE (8*1024) +#define RL_RCR_RBLEN_16K 0x00000800 /* 16KB + 16 bytes */ +#define RL_RCR_RBLEN_16K_SIZE (16*1024) +#define RL_RCR_RBLEN_32K 0x00001000 /* 32KB + 16 bytes */ +#define RL_RCR_RBLEN_32K_SIZE (32*1024) +#define RL_RCR_RBLEN_64K 0x00001800 /* 64KB + 16 bytes */ +#define RL_RCR_RBLEN_64K_SIZE (64*1024) + /* Note: the documentation for the RTL8139C(L) or + * for the RTL8139D(L) claims that the buffer should + * be 16 bytes larger. Multiples of 8KB are the + * correct values. + */ +#define RL_RCR_MXDMA_M 0x00000700 /* Rx DMA burst size */ +#define RL_RCR_MXDMA_16 0x00000000 /* 16 bytes */ +#define RL_RCR_MXDMA_32 0x00000100 /* 32 bytes */ +#define RL_RCR_MXDMA_64 0x00000200 /* 64 bytes */ +#define RL_RCR_MXDMA_128 0x00000300 /* 128 bytes */ +#define RL_RCR_MXDMA_256 0x00000400 /* 256 bytes */ +#define RL_RCR_MXDMA_512 0x00000500 /* 512 bytes */ +#define RL_RCR_MXDMA_1024 0x00000600 /* 1024 bytes */ +#define RL_RCR_MXDMA_UNLIM 0x00000700 /* unlimited */ +#define RL_RCR_WRAP 0x00000080 /* (Do not) Wrap on receive */ +#define RL_RCR_RES2 0x00000040 /* EEPROM type? */ +#define RL_RCR_AER 0x00000020 /* Accept Error Packets */ +#define RL_RCR_AR 0x00000010 /* Accept Runt Packets */ +#define RL_RCR_AB 0x00000008 /* Accept Broadcast Packets */ +#define RL_RCR_AM 0x00000004 /* Accept Multicast Packets */ +#define RL_RCR_APM 0x00000002 /* Accept Physical Match Packets */ +#define RL_RCR_AAP 0x00000001 /* Accept All Packets */ +#define RL_MPC 0x4c /* Missed Packet Counter */ +#define RL_9346CR 0x50 /* 93C46 Command Register */ +#define RL_9346CR_EEM_M 0xC0 /* Operating Mode */ +#define RL_9346CR_EEM_NORMAL 0x00 /* Normal Mode */ +#define RL_9346CR_EEM_AUTOLOAD 0x40 /* Load from 93C46 */ +#define RL_9346CR_EEM_PROG 0x80 /* 93C46 Programming */ +#define RL_9346CR_EEM_CONFIG 0xC0 /* Config Write Enable */ +#define RL_9346CR_RES 0x30 /* Reserved */ +#define RL_9346CR_EECS 0x08 /* EECS Pin */ +#define RL_9346CR_EESK 0x04 /* EESK Pin */ +#define RL_9346CR_EEDI 0x02 /* EEDI Pin */ +#define RL_9346CR_EEDO 0x01 /* EEDO Pin */ +#define RL_CONFIG0 0x51 /* Configuration Register 0 */ +#define RL_CONFIG1 0x52 /* Configuration Register 1 */ +#define RL_MSR 0x58 /* Media Status Register */ +#define RL_MSR_TXFCE 0x80 /* Tx Flow Control Enable */ +#define RL_MSR_RXFCE 0x40 /* Rx Flow Control Enable */ +#define RL_MSR_RES 0x20 /* Reserved */ +#define RL_MSR_AUXSTAT 0x10 /* Aux. Power Present */ +#define RL_MSR_SPEED_10 0x08 /* In 10 Mbps mode */ +#define RL_MSR_LINKB 0x04 /* link Failed */ +#define RL_MSR_TXPF 0x02 /* Sent Pause Packet */ +#define RL_MSR_RXPF 0x01 /* Received Pause Packet */ +#define RL_CONFIG3 0x59 /* Configuration Register 3 */ +#define RL_CONFIG4 0x5A /* Configuration Register 4 */ +/* 0x5B */ /* Reserved */ +#define RL_REVID 0x5E /* PCI Revision ID */ +/* 0x5F */ /* Reserved */ +#define RL_TSAD 0x60 /* Transmit Status of All Descriptors */ +#define RL_TSAD_TOK3 0x8000 /* TOK bit of Descriptor 3 */ +#define RL_TSAD_TOK2 0x4000 /* TOK bit of Descriptor 2 */ +#define RL_TSAD_TOK1 0x2000 /* TOK bit of Descriptor 1 */ +#define RL_TSAD_TOK0 0x1000 /* TOK bit of Descriptor 0 */ +#define RL_TSAD_TUN3 0x0800 /* TUN bit of Descriptor 3 */ +#define RL_TSAD_TUN2 0x0400 /* TUN bit of Descriptor 2 */ +#define RL_TSAD_TUN1 0x0200 /* TUN bit of Descriptor 1 */ +#define RL_TSAD_TUN0 0x0100 /* TUN bit of Descriptor 0 */ +#define RL_TSAD_TABT3 0x0080 /* TABT bit of Descriptor 3 */ +#define RL_TSAD_TABT2 0x0040 /* TABT bit of Descriptor 2 */ +#define RL_TSAD_TABT1 0x0020 /* TABT bit of Descriptor 1 */ +#define RL_TSAD_TABT0 0x0010 /* TABT bit of Descriptor 0 */ +#define RL_TSAD_OWN3 0x0008 /* OWN bit of Descriptor 3 */ +#define RL_TSAD_OWN2 0x0004 /* OWN bit of Descriptor 2 */ +#define RL_TSAD_OWN1 0x0002 /* OWN bit of Descriptor 1 */ +#define RL_TSAD_OWN0 0x0001 /* OWN bit of Descriptor 0 */ +#define RL_BMCR 0x62 /* Basic Mode Control Register (MII_CTRL) */ +#define RL_BMSR 0x64 /* Basic Mode Status Register (MII_STATUS) */ +#define RL_ANAR 0x66 /* Auto-Neg Advertisement Register (MII_ANA) */ +#define RL_ANLPAR 0x68 /* Auto-Neg Link Partner Register (MII_ANLPA) */ +#define RL_ANER 0x6a /* Auto-Neg Expansion Register (MII_ANE) */ +#define RL_NWAYTR 0x70 /* N-way Test Register */ +#define RL_CSCR 0x74 /* CS Configuration Register */ +#define RL_CONFIG5 0xD8 /* Configuration Register 5 */ + +/* Status word in receive buffer */ +#define RL_RXS_LEN_M 0xFFFF0000 /* Length Field, Excl. Status word */ +#define RL_RXS_LEN_S 16 /* Shift For Length */ +#define RL_RXS_MAR 0x00008000 /* Multicast Address Received */ +#define RL_RXS_PAR 0x00004000 /* Physical Address Matched */ +#define RL_RXS_BAR 0x00002000 /* Broadcast Address Received */ +#define RL_RXS_RES_M 0x00001FC0 /* Reserved */ +#define RL_RXS_ISE 0x00000020 /* Invalid Symbol Error */ +#define RL_RXS_RUNT 0x00000010 /* Runt Packet Received */ +#define RL_RXS_LONG 0x00000008 /* Long (>4KB) Packet */ +#define RL_RXS_CRC 0x00000004 /* CRC Error */ +#define RL_RXS_FAE 0x00000002 /* Frame Alignment Error */ +#define RL_RXS_ROK 0x00000001 /* Receive OK */ + + +/* Registers in the Machine Independent Interface (MII) to the PHY. + * IEEE 802.3 (2000 Edition) Clause 22. + */ +#define MII_CTRL 0x0 /* Control Register (basic) */ +#define MII_CTRL_RST 0x8000 /* Reset PHY */ +#define MII_CTRL_LB 0x4000 /* Enable Loopback Mode */ +#define MII_CTRL_SP_LSB 0x2000 /* Speed Selection (LSB) */ +#define MII_CTRL_ANE 0x1000 /* Auto Negotiation Enable */ +#define MII_CTRL_PD 0x0800 /* Power Down */ +#define MII_CTRL_ISO 0x0400 /* Isolate */ +#define MII_CTRL_RAN 0x0200 /* Restart Auto-Negotiation Process */ +#define MII_CTRL_DM 0x0100 /* Full Duplex */ +#define MII_CTRL_CT 0x0080 /* Enable COL Signal Test */ +#define MII_CTRL_SP_MSB 0x0040 /* Speed Selection (MSB) */ +#define MII_CTRL_SP_10 0x0000 /* 10 Mb/s */ +#define MII_CTRL_SP_100 0x2000 /* 100 Mb/s */ +#define MII_CTRL_SP_1000 0x0040 /* 1000 Mb/s */ +#define MII_CTRL_SP_RES 0x2040 /* Reserved */ +#define MII_CTRL_RES 0x003F /* Reserved */ +#define MII_STATUS 0x1 /* Status Register (basic) */ +#define MII_STATUS_100T4 0x8000 /* 100Base-T4 support */ +#define MII_STATUS_100XFD 0x4000 /* 100Base-X FD support */ +#define MII_STATUS_100XHD 0x2000 /* 100Base-X HD support */ +#define MII_STATUS_10FD 0x1000 /* 10 Mb/s FD support */ +#define MII_STATUS_10HD 0x0800 /* 10 Mb/s HD support */ +#define MII_STATUS_100T2FD 0x0400 /* 100Base-T2 FD support */ +#define MII_STATUS_100T2HD 0x0200 /* 100Base-T2 HD support */ +#define MII_STATUS_EXT_STAT 0x0100 /* Supports MII_EXT_STATUS */ +#define MII_STATUS_RES 0x0080 /* Reserved */ +#define MII_STATUS_MFPS 0x0040 /* MF Preamble Suppression */ +#define MII_STATUS_ANC 0x0020 /* Auto-Negotiation Completed */ +#define MII_STATUS_RF 0x0010 /* Remote Fault Detected */ +#define MII_STATUS_ANA 0x0008 /* Auto-Negotiation Ability */ +#define MII_STATUS_LS 0x0004 /* Link Up */ +#define MII_STATUS_JD 0x0002 /* Jabber Condition Detected */ +#define MII_STATUS_EC 0x0001 /* Ext Register Capabilities */ +#define MII_PHYID_H 0x2 /* PHY ID (high) */ +#define MII_PHYID_L 0x3 /* PHY ID (low) */ +#define MII_ANA 0x4 /* Auto-Negotiation Advertisement */ +#define MII_ANA_NP 0x8000 /* Next PAge */ +#define MII_ANA_RES 0x4000 /* Reserved */ +#define MII_ANA_RF 0x2000 /* Remote Fault */ +#define MII_ANA_TAF_M 0x1FE0 /* Technology Ability Field */ +#define MII_ANA_TAF_S 5 /* Shift */ +#define MII_ANA_TAF_RES 0x1000 /* Reserved */ +#define MII_ANA_PAUSE_ASYM 0x0800 /* Asym. Pause */ +#define MII_ANA_PAUSE_SYM 0x0400 /* Sym. Pause */ +#define MII_ANA_100T4 0x0200 /* 100Base-T4 */ +#define MII_ANA_100TXFD 0x0100 /* 100Base-TX FD */ +#define MII_ANA_100TXHD 0x0080 /* 100Base-TX HD */ +#define MII_ANA_10TFD 0x0040 /* 10Base-T FD */ +#define MII_ANA_10THD 0x0020 /* 10Base-T HD */ +#define MII_ANA_SEL_M 0x001F /* Selector Field */ +#define MII_ANA_SEL_802_3 0x0001 /* 802.3 */ +#define MII_ANLPA 0x5 /* Auto-Neg Link Partner Ability Register */ +#define MII_ANLPA_NP 0x8000 /* Next Page */ +#define MII_ANLPA_ACK 0x4000 /* Acknowledge */ +#define MII_ANLPA_RF 0x2000 /* Remote Fault */ +#define MII_ANLPA_TAF_M 0x1FC0 /* Technology Ability Field */ +#define MII_ANLPA_SEL_M 0x001F /* Selector Field */ +#define MII_ANE 0x6 /* Auto-Negotiation Expansion */ +#define MII_ANE_RES 0xFFE0 /* Reserved */ +#define MII_ANE_PDF 0x0010 /* Parallel Detection Fault */ +#define MII_ANE_LPNPA 0x0008 /* Link Partner is Next Page Able */ +#define MII_ANE_NPA 0x0002 /* Local Device is Next Page Able */ +#define MII_ANE_PR 0x0002 /* New Page has been received */ +#define MII_ANE_LPANA 0x0001 /* Link Partner is Auto-Neg.able */ +#define MII_ANNPT 0x7 /* Auto-Negotiation Next Page Transmit */ +#define MII_ANLPRNP 0x8 /* Auto-Neg Link Partner Received Next Page */ +#define MII_MS_CTRL 0x9 /* MASTER-SLAVE Control Register */ +#define MII_MS_STATUS 0xA /* MASTER-SLAVE Status Register */ +/* 0xB ... 0xE */ /* Reserved */ +#define MII_EXT_STATUS 0xF /* Extended Status */ +#define MII_ESTAT_1000XFD 0x8000 /* 1000Base-X Full Duplex */ +#define MII_ESTAT_1000XHD 0x4000 /* 1000Base-X Half Duplex */ +#define MII_ESTAT_1000TFD 0x2000 /* 1000Base-T Full Duplex */ +#define MII_ESTAT_1000THD 0x1000 /* 1000Base-T Half Duplex */ +#define MII_ESTAT_RES 0x0FFF /* Reserved */ +/* 0x10 ... 0x1F */ /* Vendor Specific */ + +#if 0 +34-35 R ERBCR Early Receive (Rx) Byte Count Register +36 R ERSR Early Rx Status Register + 7-4 reserved + 3 R ERGood Early Rx Good packet + 2 R ERBad Early Rx Bad packet + 1 R EROVW Early Rx OverWrite + 0 R EROK Early Rx OK +51 R/W CONFIG0 Configuration Register 0 + 7 R SCR Scrambler Mode + 6 R PCS PCS Mode + 5 R T10 10 Mbps Mode + 4-3 R PL[1-0] Select 10 Mbps medium type + 2-0 R BS[2-0] Select Boot ROM size +52 R/W CONFIG1 Configuration Register 1 + 7-6 R/W LEDS[1-0] LED PIN + 5 R/W DVRLOAD Driver Load + 4 R/W LWACT LWAKE active mode + 3 R MEMMAP Memory Mapping + 2 R IOMAP I/O Mapping + 1 R/W VPD Set to enable Vital Product Data + 0 R/W PMEn Power Management Enable +59 R/W CONFIG3 Configuration Register 3 + 7 R GNTSel Gnt Select + 6 R/W PARM_En Parameter Enable + 5 R/W Magic Magic Packet + 4 R/W LinkUp Link Up + 3 reserved + 2 R CLKRUN_En CLKRUN Enable + 1 reserved + 0 R FBtBEn Fast Back to Back Enable +5a R/W CONFIG4 Configuration Register 4 + 7 R/W RxFIFOAutoClr Auto Clear the Rx FIFO on overflow + 6 R/W AnaOff Analog Power Off + 5 R/W LongWF Long Wake-up Frame + 4 R/W LWPME LANWAKE vs PMEB + 3 reserved + 2 R/W LWPTN LWAKE pattern + 1 reserved + 0 R/W PBWakeup Pre-Boot Wakeup +5c-5d R/W MULINT Multiple Interrupt Select + 15-12 reserved + 11-0 R/W MISR[11-0] Multiple Interrupt Select +68-69 R ANLPAR Auto-Negotiation Link Partnet Register + 15 R NP Next Page bit + 14 R ACK acknowledge received from link partner + 13 R/W RF received remote fault detection capability + 12-11 reserved + 10 R Pause Flow control is supported + 9 R T4 100Base-T4 is supported + 8 R/W TXFD 100Base-TX full duplex is supported + 7 R/W TX 100Base-TX is supported + 6 R/W 10FD 10Base-T full duplex is supported + 5 R/W 10 10Base-T is supported + 4-0 R/W Selector Binary encoded selector +6a-6b R ANER Auto-Negotiation Expansion Register + 15-5 reserved + 4 R MLF Multiple link fault occured + 3 R LP_NP_ABLE Link partner supports Next Page + 2 R NP_ABLE Local node is able to send add. Next Pages + 1 R PAGE_RX Link Code Word Page received + 0 R LP_NW_ABLE Link partner supports NWay auto-negotiation +70-71 R/W NWAYTR N-way Test Register + 15-8 reserved + 7 R/W NWLPBK NWay loopback mode + 6-4 reserved + 3 R ENNWLE LED0 pin indicates linkpulse + 2 R FLAGABD Auto-neg experienced ability detect state + 1 R FLAGPDF Auto-neg exp. par. detection fault state + 0 R FLAGLSC Auto-neg experienced link status check state +74-75 R/W CSCR CS Configuration Register + 15 W Testfun Auto-neg speeds up internal timer + 14-10 reserved + 9 R/W LD Active low TPI link disable signal + 8 R/W HEARTBEAT HEART BEAT enable + 7 R/W JBEN Enable jabber function + 6 R/W F_LINK_100 Force 100 Mbps + 5 R/W F_Conect Bypass disconnect function + 4 reserved + 3 R Con_status Connected link detected + 2 R/W Con_status_En Configures LED1 to indicate conn. stat. + 1 reserved + 0 R/W PASS_SCR Bypass scramble +76-77 reserved +78-7b R/W PHY1_PARM PHY parameter 1 +7c-7f R/W TW_PARM Twister parameter +80 R/W PHY2_PARM PHY parameter 2 +81-83 reserved +84-8b R/W CRC[0-7] Power Management CRC reg.[0-7] for frame[0-7] +8c-cb R/W Wakeup[0-7] Power Management wakeup frame[0-7] (64 bit) +cc-d3 R/W LSBCRC[0-7] LSB of the mask byte of makeup frame[0-7] +d4-d7 reserved +d8 R/W Config5 Configuration register 5 + 7 reserved + 6 R/W BWF Broadcast Wakeup Frame + 5 R/W MWF Multicast Wakeup Frame + 4 R/W UWF Unicast Wakeup Frame + 3 R/W FifoAddrPtr FIFO Address Pointer + 2 R/W LDPS Link Down Power Saving mode + 1 R/W LANWake LANWake Signal + 0 R/W PME_STS PME_Status bit +d9-ff reserved +#endif + +/* + * $PchId: rtl8139.h,v 1.1 2003/09/05 10:58:50 philip Exp $ + */ diff --git a/kernel/sconst.h b/kernel/sconst.h new file mode 100755 index 000000000..41c6d136b --- /dev/null +++ b/kernel/sconst.h @@ -0,0 +1,36 @@ +! Miscellaneous constants used in assembler code. +W = _WORD_SIZE ! Machine word size. + +! Offsets in struct proc. They MUST match proc.h. +P_STACKBASE = 0 +#if _WORD_SIZE == 2 +ESREG = P_STACKBASE +#else +GSREG = P_STACKBASE +FSREG = GSREG + 2 ! 386 introduces FS and GS segments +ESREG = FSREG + 2 +#endif +DSREG = ESREG + 2 +DIREG = DSREG + 2 +SIREG = DIREG + W +BPREG = SIREG + W +STREG = BPREG + W ! hole for another SP +BXREG = STREG + W +DXREG = BXREG + W +CXREG = DXREG + W +AXREG = CXREG + W +RETADR = AXREG + W ! return address for save() call +PCREG = RETADR + W +CSREG = PCREG + W +PSWREG = CSREG + W +SPREG = PSWREG + W +SSREG = SPREG + W +P_STACKTOP = SSREG + W +P_LDT_SEL = P_STACKTOP +P_LDT = P_LDT_SEL + W + +#if _WORD_SIZE == 2 +Msize = 12 ! size of a message in 16-bit words +#else +Msize = 9 ! size of a message in 32-bit words +#endif diff --git a/kernel/sendmask.h b/kernel/sendmask.h new file mode 100644 index 000000000..6d6f2ed29 --- /dev/null +++ b/kernel/sendmask.h @@ -0,0 +1,168 @@ +/* Definition of the 'p_sendmask' bit mask used in the process table. The bit + * mask of process is checked in mini_send() to see if the caller is allowed + * to send to the destination. The bit masks accomodate bits for NR_TASKS + + * (LOW_USER+1) + 1. This means that there are bits for each task, driver, and + * server process, INIT, and one bit to represent all ordinary user processes. + * + * NOTE: the send masks definitions must be updated!!! + * + * Changes: + * May 01, 2004 created and sendmask definitions (Jorrit N. Herder) + */ + +#ifndef SENDMASK_H +#define SENDMASK_H + +/* Define type for sendmask, if not already done. */ +#include "type.h" + +/* Constants to support the bitmask operations. */ +#define BIT_0 (send_mask_t) 1 +#define MASK_ENTRIES NR_TASKS + (LOW_USER+1) + 1 +#define USER_PROC_NR LOW_USER+1 /* used to set bit for user procs */ +#define ALLOW_ALL_MASK (send_mask_t) -1 +#define DENY_ALL_MASK (send_mask_t) 0 + +/* Check if given process number is in range. */ +#define isvalid(n) ((unsigned) ((n)+NR_TASKS) <= MASK_ENTRIES -1) + +/* Default masks and bit operations that easily allow to construct bit masks. + * Note the one always must start with a default mask like allow_all_mask. + * From that point, one can, for example, deny several processes. + */ +#define allow_all_mask ALLOW_ALL_MASK +#define deny_all_mask DENY_ALL_MASK +#define allow(enabled,n) | (enabled << ((n) + NR_TASKS)) +#define deny(enabled,n) & ~(enabled << ((n) + NR_TASKS)) +#define send_mask_allow(mask,n) ((mask) |= (1 << ((n) + NR_TASKS))) +#define send_mask_deny(mask,n) ((mask) &= ~(1 << ((n) + NR_TASKS))) + +/* Check if the bit for the given process number is set. */ +#define isallowed(mask,n) ((mask) & (BIT_0 << ((n) + NR_TASKS))) + + +/* The masks below match the processes (and order) in src/kernel/table.c. + * Note that the masks are made effective the inclusion in the task table + * which is used to set up the process table on start up. + */ + +#define TTY_SENDMASK \ + allow_all_mask + +#define DP8390_SENDMASK \ + allow_all_mask + +#define RTL8139_SENDMASK \ + deny_all_mask \ + allow(1, USER_PROC_NR) /* inet server starts as user process */ \ + allow(1, TTY) /* need to register function key */ \ + allow(1, SYSTASK) /* need system functionality */ \ + allow(1, CLOCK) /* need clock functionality */ + +#define IDLE_SENDMASK \ + deny_all_mask + +/* The tasktab in src/kernel/table.c supports up to 4 controllers + * it is possible to define separate masks for them here, but then + * a small update in table.c is required to make them effective + */ +#define CTRLR_SENDMASK \ + allow_all_mask + +#define SB16DSP_SENDMASK \ + allow_all_mask + +#define SB16MIX_SENDMASK \ + allow_all_mask + +#define FLOPPY_SENDMASK \ + allow_all_mask + +#define CLOCK_SENDMASK \ + allow_all_mask + +#define SYSTEM_SENDMASK \ + allow_all_mask + +#define HARDWARE_SENDMASK \ + allow_all_mask \ + deny(1, USER_PROC_NR) + +#define MM_SENDMASK \ + deny_all_mask \ + allow(1, IS_PROC_NR) /* output diagnostics */ \ + allow(1, SYSTASK) \ + allow(1, TTY) \ + allow(1, CLOCK) \ + allow(1, INIT_PROC_NR) \ + allow(1, FS_PROC_NR) \ + allow(1, USER_PROC_NR) /* reply to system calls */ + +#define AT_SENDMASK \ + allow_all_mask + +#define FS_SENDMASK \ + allow_all_mask +#if 0 + deny_all_mask \ + allow(1, IS_PROC_NR) /* output diagnostics */ \ + allow(1, SYSTASK) /* need system functionality */ \ + allow(1, CLOCK) /* need clock functionality */ \ + allow(1, IS_PROC_NR) /* output diagnostics */ \ + allow(1, TTY) /* a.o. observe function keys */ \ + allow(1, FLOPPY) \ + allow(ENABLE_SB16, SB16DSP ) \ + allow(ENABLE_SB16, SB16MIX ) \ + allow(ENABLE_PRINTER, PRINTER ) \ + allow(1, MEMORY ) \ + allow((NR_CTRLRS >= 1), CTRLR(0)) \ + allow((NR_CTRLRS >= 2), CTRLR(1)) \ + allow((NR_CTRLRS >= 3), CTRLR(2)) \ + allow((NR_CTRLRS >= 4), CTRLR(3)) \ + allow(1, INIT_PROC_NR) \ + allow(1, MM_PROC_NR) /* cooperates with memory manager */ \ + allow(1, USER_PROC_NR) /* reply to system calls */ +#endif + +#define IS_SENDMASK \ + allow_all_mask /* IS handles all diagnostic messages */ +#if 0 + deny_all_mask \ + allow(1, CLOCK) /* clock delays and flag alarm needed */ \ + allow(1, FS_PROC_NR) /* open /dev/mem to read CMOS clock */ \ + allow(1, SYSTASK) /* copy tables from kernel space */ \ + allow(1, TTY) /* request function key notifications */ \ + allow(1, USER_PROC_NR) /* reply to system calls */ +#endif + +#define MEM_SENDMASK \ + deny_all_mask \ + allow(1, IS_PROC_NR) /* output diagnostics */ \ + allow(1, SYSTASK) /* system functionality needed */ \ + allow(1, CLOCK) /* check clock alarms */ \ + allow(1, TTY) /* output diagnostics */ \ + allow(1, FS_PROC_NR) /* FS is interface to the driver */ + +#define PRN_SENDMASK \ + deny_all_mask \ + allow(1, IS_PROC_NR) /* output diagnostics */ \ + allow(1, SYSTASK) /* device port I/O needed */ \ + allow(1, TTY) /* output diagnostics */ \ + allow(1, CLOCK) /* need small delays */ \ + allow(1, FS_PROC_NR) /* FS is interface to the driver */ + +#define INIT_SENDMASK \ + deny_all_mask \ + allow(1, FS_PROC_NR) /* init makes system calls to FS and MM */ \ + allow(1, MM_PROC_NR) + +#define USER_PROC_SENDMASK \ + deny_all_mask \ + allow(1, FS_PROC_NR) /* users can only make system calls */ \ + allow(1, MM_PROC_NR) \ + allow(1, IS_PROC_NR) \ + allow(ENABLE_TASKSERVER, TS_PROC_NR) + + +#endif /* SENDMASK_H */ + diff --git a/kernel/start.c b/kernel/start.c new file mode 100755 index 000000000..249d957ba --- /dev/null +++ b/kernel/start.c @@ -0,0 +1,95 @@ +/* This file contains the C startup code for Minix on Intel processors. + * It cooperates with mpx.s to set up a good environment for main(). + * + * This code runs in real mode for a 16 bit kernel and may have to switch + * to protected mode for a 286. + * + * For a 32 bit kernel this already runs in protected mode, but the selectors + * are still those given by the BIOS with interrupts disabled, so the + * descriptors need to be reloaded and interrupt descriptors made. + */ + +#include "kernel.h" +#include "protect.h" + +/* Environment strings passed by loader. */ +PRIVATE char k_environ[128*sizeof(char *)]; + + +/*==========================================================================* + * cstart * + *==========================================================================*/ +PUBLIC void cstart(cs, ds, mds, parmoff, parmsize) +U16_t cs, ds; /* kernel code and data segment */ +U16_t mds; /* monitor data segment */ +U16_t parmoff, parmsize; /* boot parameters offset and length */ +{ +/* Perform system initializations prior to calling main(). Most settings are + * determined with help of the environment strings passed by MINIX' loader. + */ + register char *envp; + unsigned mon_start; + + /* Record where the kernel and the monitor are. */ + code_base = seg2phys(cs); + data_base = seg2phys(ds); + + /* Initialize protected mode descriptors. */ + prot_init(); + + /* Copy the boot parameters to kernel memory. */ + mon_params = seg2phys(mds) + parmoff; + mon_parmsize = MAX(parmsize,sizeof(k_environ)); + if (parmsize > sizeof k_environ - 2) parmsize = sizeof k_environ - 2; + phys_copy(mon_params, vir2phys(k_environ), (phys_bytes) parmsize); + + /* Type of VDU: */ + envp = getkenv("video"); + if (kstrcmp(envp, "ega") == 0) ega = TRUE; + if (kstrcmp(envp, "vga") == 0) vga = ega = TRUE; + + /* Processor? */ + processor = katoi(getkenv("processor")); /* 86, 186, 286, 386, ... */ + + /* XT, AT or MCA bus? */ + envp = getkenv("bus"); + if (envp == NIL_PTR || kstrcmp(envp, "at") == 0) { + pc_at = TRUE; + } else + if (kstrcmp(envp, "mca") == 0) { + pc_at = ps_mca = TRUE; + } + + /* Decide if mode is protected. */ +#if _WORD_SIZE == 2 + protected_mode = processor >= 286; + if (!protected_mode) mon_return = 0; +#endif + + /* Return to assembler code to switch to protected mode (if 286), reload + * selectors and call main(). + */ +} + + +/*==========================================================================* + * getkenv * + *==========================================================================*/ +PUBLIC char *getkenv(name) +_CONST char *name; +{ +/* Get environment value - kernel version of getenv to avoid setting up the + * usual environment array. + */ + register _CONST char *namep; + register char *envp; + + for (envp = k_environ; *envp != 0;) { + for (namep = name; *namep != 0 && *namep == *envp; namep++, envp++) + ; + if (*namep == '\0' && *envp == '=') return(envp + 1); + while (*envp++ != 0) + ; + } + return(NIL_PTR); +} diff --git a/kernel/system.c b/kernel/system.c new file mode 100755 index 000000000..c6e1dc856 --- /dev/null +++ b/kernel/system.c @@ -0,0 +1,532 @@ +/* This task handles the interface between the kernel and user-level servers. + * System services can be accessed by doing a system call. System calls are + * transformed into request messages, which are handled by this task. By + * convention, a sys_call() is transformed in a SYS_CALL request message that + * is handled in a function named do_call(). + * + * A private call vector is used to map all system calls to the functions that + * handle them. The actual handler functions are contained in separate files + * to keep this file clean. The call vector is used in the system task's main + * loop to handle all incoming requests. + * + * In addition to the main sys_task() entry point, which starts the main loop, + * there are several other minor entry points: + * cause_sig: take action to cause a signal to occur + * clear_proc: clean up a process in the process table, e.g. on exit + * umap_local: map virtual address in LOCAL_SEG to physical + * umap_remote: map virtual address in REMOTE_SEG to physical + * umap_bios: map virtual address in BIOS_SEG to physical + * numap_local: umap_local D segment from proc nr instead of pointer + * virtual_copy: copy bytes from one virtual address to another + * vir_copy: copy bytes from one process to another + * generic_handler: interrupt handler for user-level device drivers + * + * Changes: + * Oct 29, 2004 new clear_proc() function (Jorrit N. Herder) + * Oct 17, 2004 generic handler and IRQ policies (Jorrit N. Herder) + * Oct 10, 2004 dispatch system calls from call vector (Jorrit N. Herder) + * Sep 30, 2004 source code documentation updated (Jorrit N. Herder) + * Sep 10, 2004 system call functions in library (Jorrit N. Herder) + * 2003/2004 various new syscalls (see syslib.h) (Jorrit N. Herder) + */ + +#include "kernel.h" +#include "system.h" +#include <stdlib.h> +#include <signal.h> +#include <unistd.h> +#include <sys/sigcontext.h> +#include <sys/svrctl.h> +#include <minix/callnr.h> +#include "sendmask.h" +#if (CHIP == INTEL) +#include "protect.h" +#endif + +FORWARD _PROTOTYPE( void initialize, (void)); + +/* Declaration of the call vector that defines the mapping of system calls to + * handler functions. The order of the do_call handler functions must match + * the SYS_CALL numbering defined in <minix/com.h>. + */ +PUBLIC _PROTOTYPE (int (*call_vec[]), (message *m_ptr) ) = { + do_times, /* 0: get uptime and process CPU time consumption */ + do_xit, /* 1: informs kernel that a process has exited */ + do_unused, /* 2: unused */ + do_sigctl, /* 3: MM signal control (incl. POSIX style handling) */ + do_fork, /* 4: informs kernel that a process has forked */ + do_newmap, /* 5: allows MM to set up a process memory map */ + do_copy, /* 6: copy a block of data between processes */ + do_exec, /* 7: sets program counter and stack pointer after EXEC */ + do_unused, /* 8: unused */ + do_abort, /* 9: MM or FS cannot go on; abort MINIX */ + do_kill, /* 10: cause a signal to be sent via MM */ + do_umap, /* 11: compute the physical address for a virtual address */ + do_unused, /* 12: returns the next free chunk of physical memory */ + do_trace, /* 13: request a trace operation */ + do_vcopy, /* 14: request a series of data blocks to be copied */ + do_signalrm, /* 15: schedule an alarm that causes an alarm signal */ + do_syncalrm, /* 16: schedule an alarm that sends a notification message */ + do_flagalrm, /* 17: schedule an alarm that sets a timeout flag to 1 */ + do_unused, /* 18: unused */ + do_svrctl, /* 19: handles miscelleneous kernel control functions */ + do_sdevio, /* 20: device I/O: phys_insb, _insw, _outsb, _outsw */ + do_unused, /* 21: unused */ + do_getinfo, /* 22: request some kind of system information */ + do_devio, /* 23: device I/O: inb, inw, inl, outb, outw, outl */ + do_vdevio, /* 24: device I/O: vector with in[b|w|l], out[b|w|l] */ + do_irqctl, /* 25: request an interrupt control operation */ + do_kmalloc, /* 26: request allocation of (DMA) buffer in mem chunk */ + do_iopenable, /* 27: allow a user process to use I/O instructions */ + do_phys2seg, /* 28: do a phys addr to segment selector/ offset conversion */ + do_exit, /* 29: an server or driver requests to be aborted */ + do_vircopy, /* 30: copy from process to process (virtual addressing) */ + do_physcopy, /* 31: copy from anywhere to anywhere (physical addressing) */ +}; + +/* Check if system call table is correct. This should not fail. No space is + * allocated here, because the dummy is declared extern. If the call vector + * is unbalanced, the array size will be negative and this won't compile. + */ +extern int dummy[sizeof(call_vec)==NR_SYS_CALLS*sizeof(call_vec[0]) ? 1 : -1]; + +/* Some system task variables. */ +PRIVATE message m; /* used to receive requests */ + + +/*===========================================================================* + * sys_task * + *===========================================================================*/ +PUBLIC void sys_task() +{ +/* Main entry point of sys_task. Get the message and dispatch on type. */ + + register int result; + + /* Initialize the system task. */ + initialize(); + + while (TRUE) { + /* Get work. */ + receive(ANY, &m); + + /* Handle the request. */ + if ((unsigned) m.m_type < NR_SYS_CALLS) { + result = (*call_vec[m.m_type])(&m); /* do system call */ + } else { + kprintf("SYS task got illegal request from %d.\n", m.m_source); + result = EBADREQUEST; /* illegal message type */ + } + + /* Send a reply, unless inhibited by a handler function. */ + if (result != EDONTREPLY) { + m.m_type = result; /* report status of call */ + send(m.m_source, &m); /* send reply to caller */ + } + } +} + + +/*===========================================================================* + * initialize * + *===========================================================================*/ +PRIVATE void initialize(void) +{ + register struct proc *rp; + int i; + + /* Initialize IRQ table. */ + for (i=0; i<NR_IRQ_VECTORS; i++) + irqtab[i].proc_nr = NONE; + + /* Initialize all alarm timers for all processes. */ + for (rp=BEG_PROC_ADDR; rp < END_PROC_ADDR; rp++) { + tmr_inittimer(&(rp->p_signalrm)); + tmr_inittimer(&(rp->p_syncalrm)); + tmr_inittimer(&(rp->p_flagalrm)); + } +} + +/*===========================================================================* + * clear_proc * + *===========================================================================*/ +PUBLIC void clear_proc(proc_nr) +int proc_nr; /* slot of process to clean up */ +{ + register struct proc *rp, *rc; + struct proc *np, *xp; + + /* Get a pointer to the process that exited. */ + rc = proc_addr(proc_nr); + + /* Turn off any alarm timers at the clock. */ + reset_timer(&rc->p_signalrm); + reset_timer(&rc->p_flagalrm); + reset_timer(&rc->p_syncalrm); + + /* Make sure the exiting process is no longer scheduled. */ + if (rc->p_flags == 0) lock_unready(rc); + + /* If the process being terminated happens to be queued trying to send a + * message (e.g., the process was killed by a signal, rather than it doing + * an exit or it is forcibly shutdown in the stop sequence), then it must + * be removed from the message queues. + */ + if (rc->p_flags & SENDING) { + /* Check all proc slots to see if the exiting process is queued. */ + for (rp = BEG_PROC_ADDR; rp < END_PROC_ADDR; rp++) { + if (rp->p_callerq == NIL_PROC) continue; + if (rp->p_callerq == rc) { + /* Exiting process is on front of this queue. */ + rp->p_callerq = rc->p_sendlink; + break; + } else { + /* See if exiting process is in middle of queue. */ + np = rp->p_callerq; + while ( ( xp = np->p_sendlink) != NIL_PROC) { + if (xp == rc) { + np->p_sendlink = xp->p_sendlink; + break; + } else { + np = xp; + } + } + } + } + } + + /* Now clean up the process table entry. Reset to defaults. */ + kstrncpy(rc->p_name, "<noname>", PROC_NAME_LEN); /* unset name */ + sigemptyset(&rc->p_pending); /* remove pending signals */ + rc->p_pendcount = 0; /* all signals are gone */ + rc->p_flags = 0; /* remove all flags */ + rc->p_type = P_NONE; /* announce slot empty */ + rc->p_sendmask = DENY_ALL_MASK; /* set most restrictive mask */ + +#if (CHIP == M68000) + pmmu_delete(rc); /* we're done, remove tables */ +#endif +} + + + +/*===========================================================================* + * generic_handler * + *===========================================================================*/ +PUBLIC int generic_handler(hook) +irq_hook_t *hook; +{ +/* This function handles hardware interrupt in a generic way, according to + * the policy set with SYS_IRQCTL. This is rather complicated since different + * devices require different actions. Options are (1) do nothing, (2a) read a + * port and optionally (2b) strobe the port high or (2c) low with the value + * read, or (3) write a value to a port. Finally, the policy may or may not + * reenable IRQs. A notification is sent in all cases. + */ + irq_policy_t policy = irqtab[hook->irq].policy; + int proc_nr = irqtab[hook->irq].proc_nr; + long port = irqtab[hook->irq].port; + phys_bytes addr = irqtab[hook->irq].addr; + long mask_val = irqtab[hook->irq].mask_val; + + /* Read a value from the given port. Possibly also strobe the port with the + * read value. Strobe it high by using the mask provided by the caller; + * strobe it low by writing back the value we read. + */ + if (policy & (IRQ_READ_PORT|IRQ_STROBE|IRQ_ECHO_VAL)) { + switch(policy & (IRQ_BYTE|IRQ_WORD|IRQ_LONG)) { + case IRQ_BYTE: { /* byte values */ + u8_t byteval = inb(port); + if (policy & IRQ_STROBE) outb(port, byteval | mask_val); + if (policy & IRQ_ECHO_VAL) outb(port, byteval); + if (policy & IRQ_READ_PORT) + phys_copy(vir2phys(&byteval), addr, sizeof(u8_t)); + break; + } case IRQ_WORD: { /* word values */ + u16_t wordval = inw(port); + if (policy & IRQ_STROBE) outw(port, wordval | mask_val); + if (policy & IRQ_ECHO_VAL) outw(port, wordval); + if (policy & IRQ_READ_PORT) + phys_copy(vir2phys(&wordval), addr, sizeof(u16_t)); + break; + } case IRQ_LONG: { /* long values */ + u32_t longval = inl(port); + if (policy & IRQ_STROBE) outl(port, longval | mask_val); + if (policy & IRQ_ECHO_VAL) outl(port, longval); + if (policy & IRQ_READ_PORT) + phys_copy(vir2phys(&longval), addr, sizeof(u32_t)); + break; + } default: /* do nothing */ ; /* wrong type flags */ + } + } + /* Write a value to some port. This is straightforward. Note that both + * reading and writing is not possible, hence 'else if' instead of 'if'. + */ + else if (policy & (IRQ_WRITE_PORT)) { + switch(policy & (IRQ_BYTE|IRQ_WORD|IRQ_LONG)) { + case IRQ_BYTE: outb(port, (u8_t) mask_val); break; + case IRQ_WORD: outw(port, (u16_t) mask_val); break; + case IRQ_LONG: outl(port, (u32_t) mask_val); break; + default: /* do nothing */ ; /* wrong type flags */ + } + } + + /* Almost done, send a HARD_INT notification to allow further processing + * and possibly reenable interrupts - this depends on the policy given. + */ + notify(proc_nr, HARD_INT); + return(policy & IRQ_REENABLE); +} + + +/*===========================================================================* + * cause_sig * + *===========================================================================*/ +PUBLIC void cause_sig(proc_nr, sig_nr) +int proc_nr; /* process to be signalled */ +int sig_nr; /* signal to be sent, 1 to _NSIG */ +{ +/* A task wants to send a signal to a process. Examples of such tasks are: + * TTY wanting to cause SIGINT upon getting a DEL + * CLOCK wanting to cause SIGALRM when timer expires + * FS also uses this to send a signal, via the SYS_KILL message. Signals are + * handled by sending a message to MM. This central function handles the + * signals and makes sure the MM gets them by sending a notification. The + * process being signaled is blocked while MM has not finished all signals + * for it. These signals are counted in p_pendcount, and the SIG_PENDING + * flag is kept nonzero while there are some. It is not sufficient to ready + * the process when MM is informed, because MM can block waiting for FS to + * do a core dump. + */ + register struct proc *rp, *mmp; + + rp = proc_addr(proc_nr); + if (sigismember(&rp->p_pending, sig_nr)) + return; /* this signal already pending */ + sigaddset(&rp->p_pending, sig_nr); + ++rp->p_pendcount; /* count new signal pending */ + if (rp->p_flags & PENDING) + return; /* another signal already pending */ + if (rp->p_flags == 0) lock_unready(rp); + rp->p_flags |= PENDING | SIG_PENDING; + notify(MM_PROC_NR, KSIG_PENDING); +} + + +/*===========================================================================* + * umap_bios * + *===========================================================================*/ +PUBLIC phys_bytes umap_bios(rp, vir_addr, bytes) +register struct proc *rp; /* pointer to proc table entry for process */ +vir_bytes vir_addr; /* virtual address in BIOS segment */ +vir_bytes bytes; /* # of bytes to be copied */ +{ +/* Calculate the physical memory address at the BIOS. */ + phys_bytes phys_addr; + + phys_addr = (phys_bytes) vir_addr; /* no check currently! */ + + return phys_addr; +} + +/*===========================================================================* + * umap_local * + *===========================================================================*/ +PUBLIC phys_bytes umap_local(rp, seg, vir_addr, bytes) +register struct proc *rp; /* pointer to proc table entry for process */ +int seg; /* T, D, or S segment */ +vir_bytes vir_addr; /* virtual address in bytes within the seg */ +vir_bytes bytes; /* # of bytes to be copied */ +{ +/* Calculate the physical memory address for a given virtual address. */ + + vir_clicks vc; /* the virtual address in clicks */ + phys_bytes pa; /* intermediate variables as phys_bytes */ +#if (CHIP == INTEL) + phys_bytes seg_base; +#endif + + /* If 'seg' is D it could really be S and vice versa. T really means T. + * If the virtual address falls in the gap, it causes a problem. On the + * 8088 it is probably a legal stack reference, since "stackfaults" are + * not detected by the hardware. On 8088s, the gap is called S and + * accepted, but on other machines it is called D and rejected. + * The Atari ST behaves like the 8088 in this respect. + */ + + if (bytes <= 0) return( (phys_bytes) 0); + vc = (vir_addr + bytes - 1) >> CLICK_SHIFT; /* last click of data */ + +#if (CHIP == INTEL) || (CHIP == M68000) + if (seg != T) + seg = (vc < rp->p_memmap[D].mem_vir + rp->p_memmap[D].mem_len ? D : S); +#else + if (seg != T) + seg = (vc < rp->p_memmap[S].mem_vir ? D : S); +#endif + + if((vir_addr>>CLICK_SHIFT) >= rp->p_memmap[seg].mem_vir + + rp->p_memmap[seg].mem_len) return( (phys_bytes) 0 ); +#if (CHIP == INTEL) + seg_base = (phys_bytes) rp->p_memmap[seg].mem_phys; + seg_base = seg_base << CLICK_SHIFT; /* segment origin in bytes */ +#endif + pa = (phys_bytes) vir_addr; +#if (CHIP != M68000) + pa -= rp->p_memmap[seg].mem_vir << CLICK_SHIFT; + return(seg_base + pa); +#endif +#if (CHIP == M68000) + pa -= (phys_bytes)rp->p_memmap[seg].mem_vir << CLICK_SHIFT; + pa += (phys_bytes)rp->p_memmap[seg].mem_phys << CLICK_SHIFT; + return(pa); +#endif +} + + +/*==========================================================================* + * numap_local * + *==========================================================================*/ +PUBLIC phys_bytes numap_local(proc_nr, vir_addr, bytes) +int proc_nr; /* process number to be mapped */ +vir_bytes vir_addr; /* virtual address in bytes within D seg */ +vir_bytes bytes; /* # of bytes required in segment */ +{ +/* Do umap_local() starting from a process number instead of a pointer. + * This function is used by device drivers, so they need not know about the + * process table. To save time, there is no 'seg' parameter. The segment + * is always D. + */ + return(umap_local(proc_addr(proc_nr), D, vir_addr, bytes)); +} + + +#if ENABLE_MESSAGE_STATS + +/*===========================================================================* + * do_mstats * + *===========================================================================*/ +PRIVATE int do_mstats(m_ptr) +message *m_ptr; /* pointer to request message */ +{ + int r = 0; + + if(m_ptr->m1_i1 > 0) { + struct message_statentry *dest; + struct proc *p; + p = proc_addr(m_ptr->m1_i3); + dest = proc_vir2phys(p, m_ptr->m1_p1); + r = mstat_copy(dest, m_ptr->m1_i1); + } + + if(m_ptr->m1_i2) { + mstat_reset(); + } + + return r; +} + +#endif /* ENABLE_MESSAGE_STATS */ + +/*===========================================================================* + * umap_remote * + *===========================================================================*/ +PUBLIC phys_bytes umap_remote(rp, seg, vir_addr, bytes) +register struct proc *rp; /* pointer to proc table entry for process */ +int seg; /* index of remote segment */ +vir_bytes vir_addr; /* virtual address in bytes within the seg */ +vir_bytes bytes; /* # of bytes to be copied */ +{ +/* Calculate the physical memory address for a given virtual address. */ + phys_bytes phys_addr; + + phys_addr = (phys_bytes) 0; /* no yet supported currently! */ + + return phys_addr; +} + +/*==========================================================================* + * virtual_copy * + *==========================================================================*/ +PUBLIC int virtual_copy(src_addr, dst_addr, bytes) +struct vir_addr *src_addr; /* source virtual address */ +struct vir_addr *dst_addr; /* destination virtual address */ +vir_bytes bytes; /* # of bytes to copy */ +{ +/* Copy bytes from virtual address src_addr to virtual address dst_addr. + * Virtual addresses can be in LOCAL_SEG, REMOTE_SEG, or BIOS_SEG. + */ + struct vir_addr *vir_addr[2]; /* virtual source and destination address */ + phys_bytes phys_addr[2]; /* absolute source and destination */ + int seg_index; + int i; + + /* Check copy count. */ + if (bytes <= 0) { + kprintf("v_cp: copy count problem <= 0\n", NO_ARG); + return(EDOM); + } + + /* Do some more checks and map virtual addresses to physical addresses. */ + vir_addr[_SRC_] = src_addr; + vir_addr[_DST_] = dst_addr; + for (i=_SRC_; i<=_DST_; i++) { + + /* Get physical address. */ + switch((vir_addr[i]->segment & SEGMENT_TYPE)) { + case LOCAL_SEG: + seg_index = vir_addr[i]->segment & SEGMENT_INDEX; + phys_addr[i] = umap_local( proc_addr(vir_addr[i]->proc_nr), + seg_index, vir_addr[i]->offset, bytes ); + break; + case REMOTE_SEG: + seg_index = vir_addr[i]->segment & SEGMENT_INDEX; + phys_addr[i] = umap_remote( proc_addr(vir_addr[i]->proc_nr), + seg_index, vir_addr[i]->offset, bytes ); + break; + case BIOS_SEG: + phys_addr[i] = umap_bios( proc_addr(vir_addr[i]->proc_nr), + vir_addr[i]->offset, bytes ); + break; + default: + kprintf("v_cp: Unknown segment type: %d\n", + vir_addr[i]->segment & SEGMENT_TYPE); + return(EINVAL); + } + + /* Check if mapping succeeded. */ + if (phys_addr[i] <= 0) { + kprintf("v_cp: Mapping failed ... phys <= 0\n", NO_ARG); + return(EFAULT); + } + } + + /* Now copy bytes between physical addresseses. */ + phys_copy(phys_addr[_SRC_], phys_addr[_DST_], (phys_bytes) bytes); + return(OK); +} + +/*==========================================================================* + * vir_copy * + *==========================================================================*/ +PUBLIC int vir_copy(src_proc, src_vir, dst_proc, dst_vir, bytes) +int src_proc; /* source process */ +vir_bytes src_vir; /* source virtual address within D seg */ +int dst_proc; /* destination process */ +vir_bytes dst_vir; /* destination virtual address within D seg */ +vir_bytes bytes; /* # of bytes to copy */ +{ +/* Copy bytes from one process to another. Meant for the easy cases, where + * speed isn't required. (One can normally do without one of the umaps.) + */ + phys_bytes src_phys, dst_phys; + + src_phys = umap_local(proc_addr(src_proc), D, src_vir, bytes); + dst_phys = umap_local(proc_addr(dst_proc), D, dst_vir, bytes); + if (src_phys == 0 || dst_phys == 0) return(EFAULT); + phys_copy(src_phys, dst_phys, (phys_bytes) bytes); + return(OK); +} + + + diff --git a/kernel/system.h b/kernel/system.h new file mode 100644 index 000000000..4d1ac9ec5 --- /dev/null +++ b/kernel/system.h @@ -0,0 +1,67 @@ +/* Function prototypes for the system library. The implementation is contained + * in src/kernel/system/. The system library allows to access system services + * by doing a system call. System calls are transformed into request messages + * to the SYS task that is responsible for handling the call. By convention, a + * sys_call() is transformed into a message with type SYS_CALL that is handled + * in a function named do_call(). + */ + +#ifndef SYSTEM_H +#define SYSTEM_H + +/* Common includes for the system library. */ +#include <minix/com.h> +#include "proc.h" +#include "assert.h" + +_PROTOTYPE( int do_exec, (message *m_ptr) ); /* process control */ +_PROTOTYPE( int do_fork, (message *m_ptr) ); +_PROTOTYPE( int do_newmap, (message *m_ptr) ); +_PROTOTYPE( int do_xit, (message *m_ptr) ); + +_PROTOTYPE( int do_vircopy, (message *m_ptr) ); /* copying */ +_PROTOTYPE( int do_physcopy, (message *m_ptr) ); +_PROTOTYPE( int do_umap, (message *m_ptr) ); +_PROTOTYPE( int do_vcopy, (message *m_ptr) ); +_PROTOTYPE( int do_copy, (message *m_ptr) ); + +_PROTOTYPE( int do_unused, (message *m_ptr) ); /* miscellaneous */ +_PROTOTYPE( int do_abort, (message *m_ptr) ); +_PROTOTYPE( int do_times, (message *m_ptr) ); +_PROTOTYPE( int do_getinfo, (message *m_ptr) ); + +_PROTOTYPE( int do_exit, (message *m_ptr) ); /* server control */ +_PROTOTYPE( int do_svrctl, (message *m_ptr) ); +_PROTOTYPE( int do_kmalloc, (message *m_ptr) ); +_PROTOTYPE( int do_iopenable, (message *m_ptr) ); +_PROTOTYPE( int do_phys2seg, (message *m_ptr) ); + +_PROTOTYPE( int do_devio, (message *m_ptr) ); /* device I/O */ +_PROTOTYPE( int do_vdevio, (message *m_ptr) ); +_PROTOTYPE( int do_sdevio, (message *m_ptr) ); + +_PROTOTYPE( int do_irqctl, (message *m_ptr) ); /* interrupt control */ + +_PROTOTYPE( int do_kill, (message *m_ptr) ); /* signal handling */ +_PROTOTYPE( int do_sigctl, (message *m_ptr) ); + +_PROTOTYPE( int do_setalarm, (message *m_ptr) ); /* alarm functions */ +#define do_flagalrm do_setalarm +#define do_signalrm do_setalarm +#define do_syncalrm do_setalarm + +#if ENABLE_K_TRACING +_PROTOTYPE( int do_trace, (message *m_ptr) ); /* process tracing */ +#else +#define do_trace do_unused +#endif + +#if ENABLE_K_DEBUGGING /* debugging */ +#else +#endif + +_PROTOTYPE( int do_vircopy, (message *m_ptr) ); +_PROTOTYPE( int do_physcopy, (message *m_ptr) ); +_PROTOTYPE( int do_biosio, (message *m_ptr) ); + +#endif /* SYSTEM_H */ diff --git a/kernel/system/Makefile b/kernel/system/Makefile new file mode 100644 index 000000000..dc0126d49 --- /dev/null +++ b/kernel/system/Makefile @@ -0,0 +1,58 @@ +# Makefile for system library implementation + +# Directories +u = /usr +i = $u/include +s = $i/sys +h = $i/minix +m = $i/ibm +l = $u/lib +n = $i/net +g = $n/gen +x = . +k = .. + +# Programs, flags, etc. +CC = exec cc +CPP = $l/cpp +LD = $(CC) -.o +CFLAGS = -I$i +LDFLAGS = -i + +SYS = alarms.o copying.o debugging.o devio.o irqctl.o proctl.o \ + srvrctl.o misc.o sigctl.o tracing.o \ + do_copy.o do_vcopy.o + +# What to make. +all build: $(SYS) + aal cr system.a $(SYS) + +clean: + rm -f *.a *.o *.bak + +# Dependencies from src/kernel/kernel.h +a = $h/config.h $h/const.h $h/type.h $h/ipc.h \ + $i/string.h $i/limits.h $i/errno.h $i/stddef.h \ + $s/types.h \ + $m/portio.h \ + $k/proc.h $k/const.h $k/type.h $k/proto.h $k/glo.h + +# Dependencies from src/kernel/system.h +b = $k/system.h $h/com.h $k/proc.h $k/assert.h + +alarms.o: $a $b +copying.o: $a $b +debugging.o: $a $b +devio.o: $a $b $h/devio.h +irqctl.o: $a $b +misc.o: $a $b $i/unistd.h +proctl.o: $a $b $k/sendmask.h $k/protect.h $i/signal.h +sigctl.o: $a $b $i/signal.h $s/sigcontext.h +srvrctl.o: $a $b $s/svrctl.h $k/sendmask.h +tracing.o: $a $b $s/ptrace.h + +do_copy.o: $a $b +do_vcopy.o: $a $b + + + diff --git a/kernel/system/alarms.c b/kernel/system/alarms.c new file mode 100644 index 000000000..df93ffd87 --- /dev/null +++ b/kernel/system/alarms.c @@ -0,0 +1,130 @@ +/* The system call implemented in this file: + * m_type: CLK_SIGNALRM, CLK_SYNCALRM, CLK_FLAGALRM + * + * The parameters for this system call are: + * m2_i1: ALRM_PROC_NR (set alarm for this process) + * m2_l1: ALRM_EXP_TIME (alarm's expiration time) + * m2_i2: ALRM_ABS_TIME (expiration time is absolute?) + * m2_l1: ALRM_SEC_LEFT (return seconds left of previous) + * m2_p1: ALRM_FLAG_PTR (virtual addr of alarm flag) + * + * Changes: + * Aug 25, 2004 fully rewritten to unite all alarms (Jorrit N. Herder) + * May 02, 2004 added new timeout flag alarm (Jorrit N. Herder) + */ + +#include "../kernel.h" +#include "../system.h" +#include <signal.h> + +FORWARD _PROTOTYPE( void cause_syncalrm, (timer_t *tp) ); +FORWARD _PROTOTYPE( void cause_flagalrm, (timer_t *tp) ); +FORWARD _PROTOTYPE( void cause_signalrm, (timer_t *tp) ); + +/*===========================================================================* + * do_setalarm * + *===========================================================================*/ +PUBLIC int do_setalarm(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* A process requests an alarm, or wants to cancel its alarm. This function + * is shared used for all of SYS_SIGNALRM, SYS_SYNCALRM, and SYS_FLAGALRM. + */ + int proc_nr; /* which process wants the alarm */ + long exp_time; /* expiration time for this alarm */ + int use_abs_time; /* use absolute or relative time */ + timer_t *tp; /* the process' timer structure */ + clock_t uptime; /* placeholder for current uptime */ + + /* Extract shared parameters from the request message. */ + proc_nr = m_ptr->ALRM_PROC_NR; /* process to interrupt later */ + if (SELF == proc_nr) proc_nr = m_ptr->m_source; + if (! isokprocn(proc_nr)) return(EINVAL); + exp_time = m_ptr->ALRM_EXP_TIME; /* alarm's expiration time */ + use_abs_time = m_ptr->ALRM_ABS_TIME; /* flag for absolute time */ + + /* Get the timer structure and set the parameters for this alarm. */ + switch (m_ptr->m_type) { + case SYS_SYNCALRM: /* notify with SYN_ALARM message */ + tp = &(proc_addr(proc_nr)->p_syncalrm); + tmr_arg(tp)->ta_int = proc_nr; + tp->tmr_func = cause_syncalrm; + break; + case SYS_SIGNALRM: /* send process a SIGALRM signal */ + tp = &(proc_addr(proc_nr)->p_signalrm); + tmr_arg(tp)->ta_int = proc_nr; + tp->tmr_func = cause_signalrm; + break; + case SYS_FLAGALRM: /* set caller's timeout flag to 1 */ + tp = &(proc_addr(proc_nr)->p_flagalrm); + tmr_arg(tp)->ta_long = + numap_local(proc_nr,(vir_bytes) m_ptr->ALRM_FLAG_PTR,sizeof(int)); + if (! tmr_arg(tp)->ta_long) return(EFAULT); + tp->tmr_func = cause_flagalrm; + break; + default: /* invalid alarm type */ + return(EINVAL); + } + + /* Return the ticks left on the previous alarm. */ + uptime = get_uptime(); + if ((tp->tmr_exp_time == TMR_NEVER) || (tp->tmr_exp_time < uptime) ) { + m_ptr->ALRM_TIME_LEFT = 0; + } else { + m_ptr->ALRM_TIME_LEFT = (tp->tmr_exp_time - uptime); + } + + /* Finally, (re)set the timer depending on 'exp_time'. */ + if (exp_time == 0) { + reset_timer(tp); + } else { + tp->tmr_exp_time = (use_abs_time) ? exp_time : exp_time + get_uptime(); + set_timer(tp, tp->tmr_exp_time, tp->tmr_func); + } + return(OK); +} + + +/*===========================================================================* + * cause_signalrm * + *===========================================================================*/ +PRIVATE void cause_signalrm(tp) +timer_t *tp; +{ +/* Routine called if a timer goes off for a process that requested an SIGALRM + * signal using the alarm(2) system call. The timer argument 'ta_int' contains + * the process number of the process to signal. + */ + cause_sig(tmr_arg(tp)->ta_int, SIGALRM); +} + +/*===========================================================================* + * cause_flagalrm * + *===========================================================================*/ +PRIVATE void cause_flagalrm(tp) +timer_t *tp; +{ +/* Routine called if a timer goes off for a process that requested a timeout + * flag to be set when the alarm expires. The timer argument 'ta_long' gives + * the physical address of the timeout flag. No validity check was done when + * setting the alarm, so check for 0 here. + */ + int timeout = 1; + phys_bytes timeout_flag = (phys_bytes) tmr_arg(tp)->ta_long; + phys_copy(vir2phys(&timeout), tmr_arg(tp)->ta_long, sizeof(int)); +} + +/*===========================================================================* + * cause_syncalrm * + *===========================================================================*/ +PRIVATE void cause_syncalrm(tp) +timer_t *tp; +{ +/* Routine called if a timer goes off and the process requested a synchronous + * alarm. The process number is stored in timer argument 'ta_int'. Notify that + * process given with a SYN_ALARM message. + */ + notify(tmr_arg(tp)->ta_int, SYN_ALARM); +} + + diff --git a/kernel/system/copying.c b/kernel/system/copying.c new file mode 100644 index 000000000..0ee19f6f4 --- /dev/null +++ b/kernel/system/copying.c @@ -0,0 +1,133 @@ +/* The system call implemented in this file: + * m_type: SYS_VIRCOPY + * + * The parameters for this system call are: + * m5_c1: CP_SRC_SPACE + * m5_l1: CP_SRC_ADDR + * m5_i1: CP_SRC_PROC_NR + * m5_c2: CP_DST_SPACE + * m5_l2: CP_DST_ADDR + * m5_i2: CP_DST_PROC_NR + * m5_l3: CP_NR_BYTES + */ + +#include "../kernel.h" +#include "../system.h" + +/*===========================================================================* + * do_vircopy * + *===========================================================================*/ +PUBLIC int do_vircopy(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_vircopy(). Copy data by using virtual addressing. */ + struct vir_addr vir_addr[2]; /* virtual source and destination address */ + vir_bytes bytes; /* number of bytes to copy */ + int i; + + /* Dismember the command message. */ + vir_addr[_SRC_].proc_nr = m_ptr->CP_SRC_PROC_NR; + vir_addr[_SRC_].segment = m_ptr->CP_SRC_SPACE; + vir_addr[_SRC_].offset = (vir_bytes) m_ptr->CP_SRC_ADDR; + vir_addr[_DST_].proc_nr = m_ptr->CP_DST_PROC_NR; + vir_addr[_DST_].segment = m_ptr->CP_DST_SPACE; + vir_addr[_DST_].offset = (vir_bytes) m_ptr->CP_DST_ADDR; + bytes = (phys_bytes) m_ptr->CP_NR_BYTES; + + /* Now do some checks for both the source and destination virtual address. + * This is done once for _SRC_, then once for _DST_. + */ + for (i=_SRC_; i<=_DST_; i++) { + + /* Check if process number was given implictly with SELF and is valid. */ + if (vir_addr[i].proc_nr == SELF) vir_addr[i].proc_nr = m_ptr->m_source; + if (! isokprocn(vir_addr[i].proc_nr)) { + kprintf("do_vircopy: illegal proc nr\n",NO_ARG); + return(EINVAL); + } + + /* Copying from or to special segments can only done by the owner. */ + if ((vir_addr[i].segment & SEGMENT_TYPE) != LOCAL_SEG && + vir_addr[i].proc_nr != m_ptr->m_source) { + kprintf("do_vircopy: special seg permission denied\n", NO_ARG); + return(EPERM); + } + } + + /* Check for overflow. This would happen for 64K segments and 16-bit + * vir_bytes. Especially copying by the MM on do_fork() is affected. + */ + if (bytes != (vir_bytes) bytes) { + kprintf("do_vircopy: overflow\n", NO_ARG); + return(E2BIG); + } + + /* Now try to make the actual virtual copy. */ + return( virtual_copy(&vir_addr[_SRC_], &vir_addr[_DST_], bytes) ); +} + + +/* The system call implemented in this file: + * m_type: SYS_PHYSCOPY + * + * The parameters for this system call are: + * m5_l1: CP_SRC_ADDR (physical source address) + * m5_l2: CP_DST_ADDR (physical destination address) + * m5_l3: CP_NR_BYTES (number of bytes to copy) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +/*===========================================================================* + * do_physcopy * + *===========================================================================*/ +PUBLIC int do_physcopy(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_physcopy(). Copy data by using physical addressing. */ + + phys_bytes src_phys, dst_phys, bytes; + + /* Dismember the command message. */ + src_phys = (phys_bytes) m_ptr->CP_SRC_ADDR; + dst_phys = (phys_bytes) m_ptr->CP_DST_ADDR; + bytes = (phys_bytes) m_ptr->CP_NR_BYTES; + + /* Do some checks and copy the data. */ + if (src_phys == 0 || dst_phys == 0) return(EFAULT); + phys_copy(src_phys, dst_phys, bytes); + return(OK); +} + + +/* The system call implemented in this file: + * m_type: SYS_UMAP + * + * The parameters for this system call are: + * m5_i1: CP_SRC_PROC_NR (process number) + * m5_c1: CP_SRC_SPACE (segment where address is: T, D, or S) + * m5_l1: CP_SRC_ADDR (virtual address) + * m5_l2: CP_DST_ADDR (returns physical address) + * m5_l3: CP_NR_BYTES (size of datastructure) + */ + +/*==========================================================================* + * do_umap * + *==========================================================================*/ +PUBLIC int do_umap(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Same as umap_local(), for non-kernel processes. */ + int proc_nr = (int) m_ptr->CP_SRC_PROC_NR; + if (proc_nr == SELF) proc_nr = m_ptr->m_source; + if (! isokprocn(proc_nr)) return(EINVAL); + + m_ptr->CP_DST_ADDR = umap_local(proc_addr(proc_nr), + (int) m_ptr->CP_SRC_SPACE, + (vir_bytes) m_ptr->CP_SRC_ADDR, + (vir_bytes) m_ptr->CP_NR_BYTES); + return(OK); +} + + diff --git a/kernel/system/debugging.c b/kernel/system/debugging.c new file mode 100644 index 000000000..2cc5aea66 --- /dev/null +++ b/kernel/system/debugging.c @@ -0,0 +1,16 @@ +/* The system call implemented in this file: + * m_type: SYS_DEBUG + * + * The parameters for this system call are: + */ + +#include "../kernel.h" +#include "../system.h" + +#if ENABLE_K_DEBUGGING /* only include code if enabled */ + +/*==========================================================================* + * do_debug * + *==========================================================================*/ + +#endif /* ENABLE_K_DEBUGGING */ diff --git a/kernel/system/devio.c b/kernel/system/devio.c new file mode 100644 index 000000000..0f5feb211 --- /dev/null +++ b/kernel/system/devio.c @@ -0,0 +1,215 @@ +/* The system call implemented in this file: + * m_type: SYS_DEVIO + * + * The parameters for this system call are: + * m2_i3: DIO_REQUEST (request input or output) + * m2_i1: DIO_TYPE (flag indicating byte, word, or long) + * m2_l1: DIO_PORT (port to read/ write) + * m2_l2: DIO_VALUE (value to write/ return value read) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +#include "../kernel.h" +#include "../system.h" +#include <minix/devio.h> + +/*===========================================================================* + * do_devio * + *===========================================================================*/ +PUBLIC int do_devio(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ + /* perform actual device I/O for byte, word, and long values */ + if (m_ptr->DIO_REQUEST == DIO_INPUT) { + switch (m_ptr->DIO_TYPE) { + case DIO_BYTE: m_ptr->DIO_VALUE = inb(m_ptr->DIO_PORT); break; + case DIO_WORD: m_ptr->DIO_VALUE = inw(m_ptr->DIO_PORT); break; + case DIO_LONG: m_ptr->DIO_VALUE = inl(m_ptr->DIO_PORT); break; + default: return(EINVAL); + } + } else { + switch (m_ptr->DIO_TYPE) { + case DIO_BYTE: outb(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break; + case DIO_WORD: outw(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break; + case DIO_LONG: outl(m_ptr->DIO_PORT, m_ptr->DIO_VALUE); break; + default: return(EINVAL); + } + } + return(OK); +} + + +/* The system call implemented in this file: + * m_type: SYS_SDEVIO + * + * The parameters for this system call are: + * m2_i3: DIO_REQUEST (request input or output) + * m2_i1: DIO_TYPE (flag indicating byte, word, or long) + * m2_l1: DIO_PORT (port to read/ write) + * m2_p1: DIO_VEC_ADDR (virtual address of buffer) + * m2_l2: DIO_VEC_SIZE (number of elements) + * m2_i2: DIO_VEC_PROC (process where buffer is) + */ + +/*===========================================================================* + * do_sdevio * + *===========================================================================*/ +PUBLIC int do_sdevio(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ + int proc_nr = m_ptr->DIO_VEC_PROC; + int count = m_ptr->DIO_VEC_SIZE; + long port = m_ptr->DIO_PORT; + phys_bytes phys_buf; + + /* Check if process number is OK. */ + if (proc_nr == SELF) proc_nr = m_ptr->m_source; + if (! isokprocn(proc_nr)) + return(EINVAL); + + /* Get and check physical address. */ + if ((phys_buf = numap_local(proc_nr, (vir_bytes) m_ptr->DIO_VEC_ADDR, count)) == 0) + return(EFAULT); + + /* Perform device I/O for bytes and words. Longs are not supported. */ + if (m_ptr->DIO_REQUEST == DIO_INPUT) { + switch (m_ptr->DIO_TYPE) { + case DIO_BYTE: phys_insb(port, phys_buf, count); break; + case DIO_WORD: phys_insw(port, phys_buf, count); break; + default: return(EINVAL); + } + } else if (m_ptr->DIO_REQUEST == DIO_OUTPUT) { + switch (m_ptr->DIO_TYPE) { + case DIO_BYTE: phys_outsb(port, phys_buf, count); break; + case DIO_WORD: phys_outsw(port, phys_buf, count); break; + default: return(EINVAL); + } + } + else { + return(EINVAL); + } + return(OK); +} + + +/* The system call implemented in this file: + * m_type: SYS_VDEVIO + * + * The parameters for this system call are: + * m2_i3: DIO_REQUEST (request input or output) + * m2_i1: DIO_TYPE (flag indicating byte, word, or long) + * m2_p1: DIO_VEC_ADDR (pointer to port/ value pairs) + * m2_i2: DIO_VEC_SIZE (number of ports to read or write) + */ + +/* Buffer for SYS_VDEVIO to copy (port,value)-pairs from/ to user. */ +PRIVATE char vdevio_pv_buf[VDEVIO_BUF_SIZE]; + +/* SYS_VDEVIO sends a pointer to a (port,value)-pairs vector at the caller. + * Define the maximum number of (port,value)-pairs that can be handled in a + * in a single SYS_VDEVIO system call based on the struct definitions. + */ +#define MAX_PVB_PAIRS ((VDEVIO_BUF_SIZE * sizeof(char)) / sizeof(pvb_pair_t)) +#define MAX_PVW_PAIRS ((VDEVIO_BUF_SIZE * sizeof(char)) / sizeof(pvw_pair_t)) +#define MAX_PVL_PAIRS ((VDEVIO_BUF_SIZE * sizeof(char)) / sizeof(pvl_pair_t)) + + +/*===========================================================================* + * do_vdevio * + *===========================================================================*/ +PUBLIC int do_vdevio(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Perform a series of device I/O on behalf of a non-kernel process. The + * I/O addresses and I/O values are fetched from and returned to some buffer + * in user space. The actual I/O is wrapped by lock() and unlock() to prevent + * that I/O batch from being interrrupted. + * This is the counterpart of do_devio, which performs a single device I/O. + */ + pvb_pair_t *pvb_pairs; /* needed for byte values */ + pvw_pair_t *pvw_pairs; /* needed for word values */ + pvl_pair_t *pvl_pairs; /* needed for long values */ + int i; + pid_t caller_pid; /* process id of caller */ + size_t bytes; /* # bytes to be copied */ + vir_bytes caller_vir; /* virtual address at caller */ + phys_bytes caller_phys; /* physical address at caller */ + phys_bytes kernel_phys; /* physical address in kernel */ + + + /* Check if nr of ports is ok and get size of (port,value) data. */ + if (m_ptr->DIO_VEC_SIZE <= 0) return(EINVAL); + switch(m_ptr->DIO_TYPE) { + case DIO_BYTE: + if (m_ptr->DIO_VEC_SIZE > MAX_PVB_PAIRS) return(EINVAL); + bytes = (size_t) (m_ptr->DIO_VEC_SIZE * sizeof(pvb_pair_t)); + break; + case DIO_WORD: + if (m_ptr->DIO_VEC_SIZE > MAX_PVW_PAIRS) return(EINVAL); + bytes = (size_t) (m_ptr->DIO_VEC_SIZE * sizeof(pvw_pair_t)); + break; + case DIO_LONG: + if (m_ptr->DIO_VEC_SIZE > MAX_PVL_PAIRS) return(EINVAL); + bytes = (size_t) (m_ptr->DIO_VEC_SIZE * sizeof(pvl_pair_t)); + break; + default: /* this once and for all checks for a correct type */ + return(EINVAL); + } + + /* Calculate physical addresses and copy (port,value)-pairs from user. */ + caller_pid = (pid_t) m_ptr->m_source; + caller_vir = (vir_bytes) m_ptr->DIO_VEC_ADDR; + caller_phys = umap_local(proc_addr(caller_pid), D, caller_vir, bytes); + if (0 == caller_phys) return EFAULT; + kernel_phys = vir2phys(vdevio_pv_buf); + phys_copy(caller_phys, kernel_phys, (phys_bytes) bytes); + + /* Perform actual device I/O for byte, word, and long values. Note that + * the entire switch is wrapped in lock() and unlock() to prevent the I/O + * batch from being interrupted. It may be cleaner to do this just around + * the for loops, but this results in rather lenghty code. + */ + lock(); + switch (m_ptr->DIO_TYPE) { + case DIO_BYTE: /* byte values */ + pvb_pairs = (pvb_pair_t *) vdevio_pv_buf; + if (DIO_INPUT == m_ptr->DIO_REQUEST) { + for (i=0; i < m_ptr->DIO_VEC_SIZE; i++) + pvb_pairs[i].value = inb(pvb_pairs[i].port); + } else { + for (i=0; i < m_ptr->DIO_VEC_SIZE; i++) + outb(pvb_pairs[i].port, pvb_pairs[i].value); + } + break; + case DIO_WORD: /* word values */ + pvw_pairs = (pvw_pair_t *) vdevio_pv_buf; + if (DIO_INPUT == m_ptr->DIO_REQUEST) { + for (i=0; i < m_ptr->DIO_VEC_SIZE; i++) + pvw_pairs[i].value = inw(pvw_pairs[i].port); + } else { + for (i=0; i < m_ptr->DIO_VEC_SIZE; i++) + outw(pvw_pairs[i].port, pvw_pairs[i].value); + } + break; + case DIO_LONG: /* fall through: long values */ + default: /* only DIO_LONG can arrive here, see above switch */ + pvl_pairs = (pvl_pair_t *) vdevio_pv_buf; + if (DIO_INPUT == m_ptr->DIO_REQUEST) { + for (i=0; i < m_ptr->DIO_VEC_SIZE; i++) + pvl_pairs[i].value = inl(pvl_pairs[i].port); + } else { + for (i=0; i < m_ptr->DIO_VEC_SIZE; i++) + outl(pvb_pairs[i].port, pvl_pairs[i].value); + } + } + unlock(); + + /* Almost done, copy back results for input requests. */ + if (DIO_INPUT == m_ptr->REQUEST) + phys_copy(kernel_phys, caller_phys, (phys_bytes) bytes); + return(OK); +} + + diff --git a/kernel/system/do_copy.c b/kernel/system/do_copy.c new file mode 100644 index 000000000..ac3667f80 --- /dev/null +++ b/kernel/system/do_copy.c @@ -0,0 +1,69 @@ +/* The system call implemented in this file: + * m_type: SYS_COPY + * + * The parameters for this system call are: + * m5_c1: CP_SRC_SPACE + * m5_i1: CP_SRC_PROC_NR + * m5_l1: CP_SRC_ADDR + * m5_c2: CP_DST_SPACE + * m5_i2: CP_DST_PROC_NR + * m5_l2: CP_DST_ADDR + * m5_l3: CP_NR_BYTES + */ + +#include "../kernel.h" +#include "../system.h" + +/*===========================================================================* + * do_copy * + *===========================================================================*/ +PUBLIC int do_copy(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_copy(). Copy data by using virtual or physical addressing. */ + + int src_proc, dst_proc, src_space, dst_space; + vir_bytes src_vir, dst_vir; + phys_bytes src_phys, dst_phys, bytes; + + /* Dismember the command message. */ + src_proc = m_ptr->CP_SRC_PROC_NR; + dst_proc = m_ptr->CP_DST_PROC_NR; + src_space = m_ptr->CP_SRC_SPACE; + dst_space = m_ptr->CP_DST_SPACE; + src_vir = (vir_bytes) m_ptr->CP_SRC_ADDR; + dst_vir = (vir_bytes) m_ptr->CP_DST_ADDR; + bytes = (phys_bytes) m_ptr->CP_NR_BYTES; + + /* Check if process number was given implicitly with SELF. */ + if (src_proc == SELF) src_proc = m_ptr->m_source; + if (dst_proc == SELF) dst_proc = m_ptr->m_source; + + /* Compute the source and destination addresses and do the copy. */ + if (src_proc == ABS) { + src_phys = (phys_bytes) m_ptr->CP_SRC_ADDR; + } else { + if (bytes != (vir_bytes) bytes) { + /* This would happen for 64K segments and 16-bit vir_bytes. + * It would happen a lot for do_fork except MM uses ABS + * copies for that case. + */ + panic("overflow in count in do_copy", NO_NUM); + } + src_phys = umap_local(proc_addr(src_proc), src_space, src_vir, + (vir_bytes) bytes); + } + + if (dst_proc == ABS) { + dst_phys = (phys_bytes) m_ptr->CP_DST_ADDR; + } else { + dst_phys = umap_local(proc_addr(dst_proc), dst_space, dst_vir, + (vir_bytes) bytes); + } + + if (src_phys == 0 || dst_phys == 0) return(EFAULT); + phys_copy(src_phys, dst_phys, bytes); + return(OK); +} + + diff --git a/kernel/system/do_vcopy.c b/kernel/system/do_vcopy.c new file mode 100644 index 000000000..cb862fc57 --- /dev/null +++ b/kernel/system/do_vcopy.c @@ -0,0 +1,55 @@ +/* The system call implemented in this file: + * m_type: SYS_VCOPY + * + * The parameters for this system call are: + * m1_i1: VCP_SRC_PROC (source process number) + * m1_i2: VCP_DST_PROC (destination process number) + * m1_i3: VCP_VEC_SIZE (vector size) + * m1_p1: VCP_VEC_ADDR (pointer to vector) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +#include "../kernel.h" +#include "../system.h" + +/*===========================================================================* + * do_vcopy * + *===========================================================================*/ +PUBLIC int do_vcopy(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_vcopy(). Copy multiple blocks of memory */ + + int src_proc, dst_proc, vect_s, i; + vir_bytes src_vir, dst_vir, vect_addr; + phys_bytes src_phys, dst_phys, bytes; + cpvec_t cpvec_table[CPVEC_NR]; + + /* Dismember the command message. */ + src_proc = m_ptr->VCP_SRC_PROC; + dst_proc = m_ptr->VCP_DST_PROC; + vect_s = m_ptr->VCP_VEC_SIZE; + vect_addr = (vir_bytes)m_ptr->VCP_VEC_ADDR; + + if (vect_s > CPVEC_NR) return EDOM; + + src_phys= numap_local(m_ptr->m_source, vect_addr, vect_s * sizeof(cpvec_t)); + if (!src_phys) return EFAULT; + phys_copy(src_phys, vir2phys(cpvec_table), + (phys_bytes) (vect_s * sizeof(cpvec_t))); + + for (i = 0; i < vect_s; i++) { + src_vir= cpvec_table[i].cpv_src; + dst_vir= cpvec_table[i].cpv_dst; + bytes= cpvec_table[i].cpv_size; + src_phys = numap_local(src_proc,src_vir,(vir_bytes)bytes); + dst_phys = numap_local(dst_proc,dst_vir,(vir_bytes)bytes); + if (src_phys == 0 || dst_phys == 0) return(EFAULT); + phys_copy(src_phys, dst_phys, bytes); + } + return(OK); +} + + diff --git a/kernel/system/irqctl.c b/kernel/system/irqctl.c new file mode 100644 index 000000000..3107e71fb --- /dev/null +++ b/kernel/system/irqctl.c @@ -0,0 +1,106 @@ +/* The system call implemented in this file: + * m_type: SYS_IRQCTL + * + * The parameters for this system call are: + * m5_c1: IRQ_REQUEST (control operation to perform) + * m5_c2: IRQ_VECTOR (irq line that must be controlled) + * m5_i1: IRQ_POLICY (flags to control the IRQCTL request) + * m5_i2: IRQ_PROC_NR (process number to notify) + * m5_l1: IRQ_PORT (port to write to / read from) + * m5_l2: IRQ_VIR_ADDR (virtual address at caller) + * m5_l3: IRQ_MASK_VAL (value to be written or strobe mask) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +#include "../kernel.h" +#include "../system.h" + + +/*===========================================================================* + * do_irqctl * + *===========================================================================*/ +PUBLIC int do_irqctl(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ + /* Dismember the request message. */ + int irq = m_ptr->IRQ_VECTOR; /* which IRQ vector */ + int policy = m_ptr->IRQ_POLICY; /* policy field with flags */ + long port = m_ptr->IRQ_PORT; /* port to read or write */ + vir_bytes vir_addr = m_ptr->IRQ_VIR_ADDR; /* address at caller */ + phys_bytes phys_addr = 0; /* calculate physical address */ + long mask_val = m_ptr->IRQ_MASK_VAL; /* mask or value to be written */ + int proc_nr = m_ptr->IRQ_PROC_NR; /* process number to forward to */ + + /* Check if IRQ line is acceptable. */ + if ((unsigned) irq >= NR_IRQ_VECTORS) { + kprintf("ST: irq line %d is not acceptable!\n", irq); + return(EINVAL); + } + + /* See what is requested and take needed actions. */ + switch(m_ptr->IRQ_REQUEST) { + + /* Enable or disable IRQs. This is straightforward. */ + case IRQ_ENABLE: { + enable_irq(&irqtab[irq].hook); + break; + } + case IRQ_DISABLE: { + disable_irq(&irqtab[irq].hook); + break; + } + + /* Control IRQ policies. Set a policy and needed details in the IRQ table. + * This policy is used by a generic function to handle hardware interrupts. + * The generic_handler() is contained in system.c. + */ + case IRQ_SETPOLICY: { + + if (proc_nr == NONE) { /* remove irqtab entry */ + if (irqtab[irq].proc_nr != m_ptr->m_source) { + return(EPERM); /* only owner may do so */ + } + kprintf("ST: notify: cannot remove entry for IRQ %d\n",irq); + return(ENOSYS); /* not yet supported */ + } + else { /* install generic handler */ + if (irqtab[irq].proc_nr != NONE) { /* IRQ entry already taken */ + kprintf("ST: notify: slot for IRQ %d already taken\n", irq); + return(EBUSY); /* cannot overwrite entry */ + } + if (proc_nr == SELF) /* check for magic proc nr */ + proc_nr = m_ptr->m_source; /* set caller's proc nr */ + if (! isokprocn(proc_nr)) { /* check if proc nr is ok */ + kprintf("ST: notify: invalid proc_nr: %d\n", proc_nr); + return(EINVAL); + } + if (policy & IRQ_READ_PORT) { /* get phys_addr at caller */ + switch(policy & (IRQ_BYTE|IRQ_WORD|IRQ_LONG)) { + case IRQ_BYTE: phys_addr=numap_local(proc_nr,vir_addr,sizeof( u8_t)); + break; + case IRQ_WORD: phys_addr=numap_local(proc_nr,vir_addr,sizeof(u16_t)); + break; + case IRQ_LONG: phys_addr=numap_local(proc_nr,vir_addr,sizeof(u32_t)); + break; + default: return(EINVAL); /* wrong type flags */ + } + if (phys_addr==0) return(EFAULT); /* invalid address */ + } + /* Arguments seem to be OK, register them in the IRQ table. */ + irqtab[irq].policy = policy; /* policy for interrupts */ + irqtab[irq].proc_nr = proc_nr; /* process number to notify */ + irqtab[irq].port = port; /* port to read or write */ + irqtab[irq].addr = phys_addr; /* address to store status */ + irqtab[irq].mask_val = mask_val; /* strobe mask or value */ + put_irq_handler(&irqtab[irq].hook, irq, generic_handler); + } + break; + } + default: + return(EINVAL); /* invalid IRQ_REQUEST */ + } + return(OK); +} + diff --git a/kernel/system/misc.c b/kernel/system/misc.c new file mode 100644 index 000000000..9fc33e089 --- /dev/null +++ b/kernel/system/misc.c @@ -0,0 +1,234 @@ +/* The system call implemented in this file: + * m_type: SYS_TIMES + * + * The parameters for this system call are: + * m4_l1: T_PROC_NR (get info for this process) + * m4_l1: T_USER_TIME (return values ...) + * m4_l2: T_SYSTEM_TIME + * m4_l3: T_CHILD_UTIME + * m4_l4: T_CHILD_STIME + * m4_l5: T_BOOT_TICKS + */ + +#include "../kernel.h" +#include "../system.h" +#include <unistd.h> +INIT_ASSERT + +/*===========================================================================* + * do_times * + *===========================================================================*/ +PUBLIC int do_times(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_times(). Retrieve the accounting information. */ + + register struct proc *rp; + int proc_nr; + + /* Insert the times needed by the SYS_TIMES system call in the message. */ + proc_nr = (m_ptr->T_PROC_NR == SELF) ? m_ptr->m_source : m_ptr->T_PROC_NR; + if (isokprocn(proc_nr)) { + rp = proc_addr(m_ptr->T_PROC_NR); + + lock(); /* halt the volatile time counters in rp */ + m_ptr->T_USER_TIME = rp->user_time; + m_ptr->T_SYSTEM_TIME = rp->sys_time; + unlock(); + m_ptr->T_CHILD_UTIME = rp->child_utime; + m_ptr->T_CHILD_STIME = rp->child_stime; + } + m_ptr->T_BOOT_TICKS = get_uptime(); + return(OK); +} + + +/*===========================================================================* + * do_unused * + *===========================================================================*/ +PUBLIC int do_unused(m) +message *m; /* pointer to request message */ +{ + kprintf("SYS task got illegal request from %d.", m->m_source); + return(EBADREQUEST); /* illegal message type */ +} + + +/* The system call implemented in this file: + * m_type: SYS_ABORT + * + * The parameters for this system call are: + * m1_i1: ABRT_HOW (how to abort, possibly fetch monitor params) + * m1_i2: ABRT_MON_PROC (proc nr to get monitor params from) + * m1_i3: ABRT_MON_LEN (length of monitor params) + * m1_p1: ABRT_MON_ADDR (virtual address of params) + */ + +/*===========================================================================* + * do_abort * + *===========================================================================*/ +PUBLIC int do_abort(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_abort. MINIX is unable to continue. This can originate in the + * MM (normal abort or panic) or FS (panic), or TTY (a CTRL-ALT-DEL or ESC + * after debugging dumps). + */ + register struct proc *rp; + phys_bytes src_phys; + vir_bytes len; + int how = m_ptr->ABRT_HOW; + + rp = proc_addr(m_ptr->m_source); + + if (how == RBT_MONITOR) { + /* The monitor is to run user specified instructions. */ + len = m_ptr->ABRT_MON_LEN + 1; + assert(len <= mon_parmsize); + src_phys = numap_local(m_ptr->ABRT_MON_PROC, + (vir_bytes) m_ptr->ABRT_MON_ADDR, len); + assert(src_phys != 0); + phys_copy(src_phys, mon_params, (phys_bytes) len); + } + prepare_shutdown(how); + return(OK); /* pro-forma (really EDISASTER) */ +} + +/* The system call implemented in this file: + * m_type: SYS_GETINFO + * + * The parameters for this system call are: + * m1_i3: I_REQUEST (what info to get) + * m1_i4: I_PROC_NR (process to store value at) + * m1_p1: I_VAL_PTR (where to put it) + * m1_i1: I_VAL_LEN (maximum length expected, optional) + * m1_p2: I_KEY_PTR (environment variable key) + * m1_i2: I_KEY_LEN (lenght of environment variable key) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + + + +/*===========================================================================* + * do_getinfo * + *===========================================================================*/ +PUBLIC int do_getinfo(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Request system information to be copied to caller's address space. */ + size_t length; + phys_bytes src_phys; + phys_bytes dst_phys; + int proc_nr, nr; + + /* Set source address and length based on request type. */ + switch (m_ptr->I_REQUEST) { + case GET_KENVIRON: { + struct kenviron kenv; + extern int end; + + kenv.pc_at = pc_at; kenv.ps_mca = ps_mca; + kenv.processor = processor; kenv.protected = protected_mode; + kenv.ega = ega; kenv.vga = vga; + + kenv.proc_addr = (vir_bytes) proc; + kenv.params_base = mon_params; + kenv.params_size = mon_parmsize; + kenv.kmem_base = vir2phys(0); + kenv.kmem_size = vir2phys(&end) - vir2phys(0) + 1; + kenv.bootfs_base = proc_addr(MEMORY)->p_farmem[0].mem_phys; + kenv.bootfs_size = proc_addr(MEMORY)->p_farmem[0].mem_len; + length = sizeof(struct kenviron); + src_phys = vir2phys(&kenv); + break; + } + case GET_IMAGE: { + length = sizeof(struct system_image) * IMAGE_SIZE; + src_phys = vir2phys(image); + break; + } + case GET_IRQTAB: { + length = sizeof(struct irqtab) * NR_IRQ_VECTORS; + src_phys = vir2phys(irqtab); + break; + } + case GET_MEMCHUNKS: { + length = sizeof(struct memory) * NR_MEMS; + src_phys = vir2phys(mem); + break; + } + case GET_SCHEDINFO: { + /* This is slightly complicated because we need two data structures + * at once, otherwise the scheduling information may be incorrect. + * Copy the queue heads and fall through to copy the process table. + */ + length = sizeof(struct proc *) * NR_SCHED_QUEUES; + src_phys = vir2phys(rdy_head); + dst_phys = numap_local(m_ptr->m_source, (vir_bytes) m_ptr->I_KEY_PTR, + length); + if (src_phys == 0 || dst_phys == 0) return(EFAULT); + phys_copy(src_phys, dst_phys, length); + } + case GET_PROCTAB: { + length = sizeof(struct proc) * (NR_PROCS + NR_TASKS); + src_phys = vir2phys(proc); + break; + } + case GET_PROC: { + nr = (m_ptr->I_KEY_LEN == SELF) ? m_ptr->m_source : m_ptr->I_KEY_LEN; + if (! isokprocn(nr)) return(EINVAL); + length = sizeof(struct proc); + src_phys = vir2phys(proc_addr(nr)); + break; + } + case GET_MONPARAMS: { + src_phys = mon_params; /* already is a physical address! */ + length = mon_parmsize; + break; + } + case GET_PROCNR: { + length = sizeof(int); + if (m_ptr->I_KEY_LEN == 0) { /* get own process nr */ + kprintf("GET_PROCNR (own) from %d\n", m_ptr->m_source); + src_phys = vir2phys(&proc_nr); + } else { /* lookup nr by name */ + int proc_found = FALSE; + struct proc *pp; + char key[8]; /* storage for process name to lookup */ + kprintf("GET_PROCNR (others) from %d\n", m_ptr->m_source); + proc_nr = m_ptr->m_source; /* only caller can request copy */ + if (m_ptr->I_KEY_LEN > sizeof(key)) return(EINVAL); + if (vir_copy(proc_nr, (vir_bytes) m_ptr->I_KEY_PTR, SYSTASK, + (vir_bytes) key, m_ptr->I_KEY_LEN) != OK) return(EFAULT); + for (pp=BEG_PROC_ADDR; pp<END_PROC_ADDR; pp++) { + if (kstrncmp(pp->p_name, key, m_ptr->I_KEY_LEN) == 0) { + src_phys = vir2phys(&(pp->p_nr)); + proc_found = TRUE; + break; + } + } + if (! proc_found) return(ESRCH); + } + break; + } + case GET_KMESSAGES: { + length = sizeof(struct kmessages); + src_phys = vir2phys(&kmess); + break; + } + default: + return(EINVAL); + } + + /* Try to make the actual copy for the requested data. */ + if (m_ptr->I_VAL_LEN > 0 && length > m_ptr->I_VAL_LEN) return (E2BIG); + proc_nr = m_ptr->m_source; /* only caller can request copy */ + dst_phys = numap_local(proc_nr, (vir_bytes) m_ptr->I_VAL_PTR, length); + if (src_phys == 0 || dst_phys == 0) return(EFAULT); + phys_copy(src_phys, dst_phys, length); + return(OK); +} + + diff --git a/kernel/system/old/do_abort.c b/kernel/system/old/do_abort.c new file mode 100644 index 000000000..469208aab --- /dev/null +++ b/kernel/system/old/do_abort.c @@ -0,0 +1,45 @@ +/* The system call implemented in this file: + * m_type: SYS_ABORT + * + * The parameters for this system call are: + * m1_i1: ABRT_HOW (how to abort, possibly fetch monitor params) + * m1_i2: ABRT_MON_PROC (proc nr to get monitor params from) + * m1_i3: ABRT_MON_LEN (length of monitor params) + * m1_p1: ABRT_MON_ADDR (virtual address of params) + */ + +#include "../kernel.h" +#include "../system.h" +#include <unistd.h> +INIT_ASSERT + +/*===========================================================================* + * do_abort * + *===========================================================================*/ +PUBLIC int do_abort(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_abort. MINIX is unable to continue. This can originate in the + * MM (normal abort or panic) or FS (panic), or TTY (a CTRL-ALT-DEL or ESC + * after debugging dumps). + */ + register struct proc *rp; + phys_bytes src_phys; + vir_bytes len; + int how = m_ptr->ABRT_HOW; + + rp = proc_addr(m_ptr->m_source); + + if (how == RBT_MONITOR) { + /* The monitor is to run user specified instructions. */ + len = m_ptr->ABRT_MON_LEN + 1; + assert(len <= mon_parmsize); + src_phys = numap_local(m_ptr->ABRT_MON_PROC, + (vir_bytes) m_ptr->ABRT_MON_ADDR, len); + assert(src_phys != 0); + phys_copy(src_phys, mon_params, (phys_bytes) len); + } + prepare_shutdown(how); + return(OK); /* pro-forma (really EDISASTER) */ +} + diff --git a/kernel/system/old/do_endsig.c b/kernel/system/old/do_endsig.c new file mode 100644 index 000000000..c2d90f8f0 --- /dev/null +++ b/kernel/system/old/do_endsig.c @@ -0,0 +1,34 @@ +/* The system call implemented in this file: + * m_type: SYS_ENDSIG + * + * The parameters for this system call are: + * m2_i1: SIG_PROC (process that was signaled) + */ + +#include "../kernel.h" +#include "../system.h" +INIT_ASSERT + +/*===========================================================================* + * do_endsig * + *===========================================================================*/ +PUBLIC int do_endsig(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Finish up after a KSIG-type signal, caused by a SYS_KILL message or a call + * to cause_sig by a task + */ + + register struct proc *rp; + + rp = proc_addr(m_ptr->SIG_PROC); + if (isemptyp(rp)) return(EINVAL); /* process already dead? */ + assert(isuserp(rp)); + + /* MM has finished one KSIG. */ + if (rp->p_pendcount != 0 && --rp->p_pendcount == 0 + && (rp->p_flags &= ~SIG_PENDING) == 0) + lock_ready(rp); + return(OK); +} + diff --git a/kernel/system/old/do_exec.c b/kernel/system/old/do_exec.c new file mode 100644 index 000000000..91f9e0b6b --- /dev/null +++ b/kernel/system/old/do_exec.c @@ -0,0 +1,65 @@ +/* The system call implemented in this file: + * m_type: SYS_EXEC + * + * The parameters for this system call are: + * m1_i1: PR_PROC_NR (process that did exec call) + * m1_i3: PR_TRACING (flag to indicate tracing is on/ off) + * m1_p1: PR_STACK_PTR (new stack pointer) + * m1_p2: PR_NAME_PTR (pointer to program name) + * m1_p3: PR_IP_PTR (new instruction pointer) + */ + +#include "../kernel.h" +#include "../system.h" +#include <signal.h> +#if (CHIP == INTEL) +#include "../protect.h" +#endif +INIT_ASSERT + +/*===========================================================================* + * do_exec * + *===========================================================================*/ +PUBLIC int do_exec(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_exec(). A process has done a successful EXEC. Patch it up. */ + + register struct proc *rp; + reg_t sp; /* new sp */ + phys_bytes phys_name; + char *np; +#define NLEN (sizeof(rp->p_name)-1) + + rp = proc_addr(m_ptr->PR_PROC_NR); + assert(isuserp(rp)); + if (m_ptr->PR_TRACING) cause_sig(m_ptr->PR_PROC_NR, SIGTRAP); + sp = (reg_t) m_ptr->PR_STACK_PTR; + rp->p_reg.sp = sp; /* set the stack pointer */ +#if (CHIP == M68000) + rp->p_splow = sp; /* set the stack pointer low water */ +#ifdef FPP + /* Initialize fpp for this process */ + fpp_new_state(rp); +#endif +#endif +#if (CHIP == INTEL) /* wipe extra LDT entries */ + memset(&rp->p_ldt[EXTRA_LDT_INDEX], 0, + (LDT_SIZE - EXTRA_LDT_INDEX) * sizeof(rp->p_ldt[0])); +#endif + rp->p_reg.pc = (reg_t) m_ptr->PR_IP_PTR; /* set pc */ + rp->p_flags &= ~RECEIVING; /* MM does not reply to EXEC call */ + if (rp->p_flags == 0) lock_ready(rp); + + /* Save command name for debugging, ps(1) output, etc. */ + phys_name = numap_local(m_ptr->m_source, (vir_bytes) m_ptr->PR_NAME_PTR, + (vir_bytes) NLEN); + if (phys_name != 0) { + phys_copy(phys_name, vir2phys(rp->p_name), (phys_bytes) NLEN); + for (np = rp->p_name; (*np & BYTE) >= ' '; np++) {} + *np = 0; + } + return(OK); +} + + diff --git a/kernel/system/old/do_exit.c b/kernel/system/old/do_exit.c new file mode 100644 index 000000000..210910b97 --- /dev/null +++ b/kernel/system/old/do_exit.c @@ -0,0 +1,45 @@ +/* The system call implemented in this file: + * m_type: SYS_EXIT + * + * The parameters for this system call are: + * m1_i1: EXIT_STATUS (exit status, 0 if normal exit) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +#include "../kernel.h" +#include "../system.h" + +/*===========================================================================* + * do_exit * + *===========================================================================*/ +PUBLIC int do_exit(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_exit. A server or driver wants to exit. This may happen + * on a panic, but also is done when MINIX is shutdown. + */ + register struct proc *rp; + int proc_nr = m_ptr->m_source; /* can only exit own process */ + + if (m_ptr->EXIT_STATUS != 0) { + kprintf("WARNING: system process %d exited with an error.\n", proc_nr ); + } + + /* Now call the routine to clean up of the process table slot. This cancels + * outstanding timers, possibly removes the process from the message queues, + * and reset important process table fields. + */ + clear_proc(proc_nr); + + /* If the shutdown sequence is active, see if it was awaiting the shutdown + * of this system service. If so, directly continue the stop sequence. + */ + if (shutting_down && shutdown_process == proc_addr(proc_nr)) { + stop_sequence(&shutdown_timer); + } + return(EDONTREPLY); /* no reply is sent */ +} + + diff --git a/kernel/system/old/do_fork.c b/kernel/system/old/do_fork.c new file mode 100644 index 000000000..18430ced1 --- /dev/null +++ b/kernel/system/old/do_fork.c @@ -0,0 +1,64 @@ +/* The system call implemented in this file: + * m_type: SYS_FORK + * + * The parameters for this system call are: + * m1_i1: PR_PROC_NR (child's process table slot) + * m1_i2: PR_PPROC_NR (parent, process that forked) + * m1_i3: PR_PID (child pid received from MM) + */ + +#include "../kernel.h" +#include "../system.h" +#include <signal.h> +#include "../sendmask.h" +INIT_ASSERT + +/*===========================================================================* + * do_fork * + *===========================================================================*/ +PUBLIC int do_fork(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_fork(). PR_PPROC_NR has forked. The child is PR_PROC_NR. */ + +#if (CHIP == INTEL) + reg_t old_ldt_sel; +#endif + register struct proc *rpc; + struct proc *rpp; + + rpp = proc_addr(m_ptr->PR_PPROC_NR); + assert(isuserp(rpp)); + rpc = proc_addr(m_ptr->PR_PROC_NR); + assert(isemptyp(rpc)); + + /* Copy parent 'proc' struct to child. */ +#if (CHIP == INTEL) + old_ldt_sel = rpc->p_ldt_sel; /* stop this being obliterated by copy */ +#endif + + *rpc = *rpp; /* copy 'proc' struct */ + +#if (CHIP == INTEL) + rpc->p_ldt_sel = old_ldt_sel; +#endif + rpc->p_nr = m_ptr->PR_PROC_NR; /* this was obliterated by copy */ + + rpc->p_flags |= NO_MAP; /* inhibit the process from running */ + + rpc->p_flags &= ~(PENDING | SIG_PENDING | P_STOP); + + /* Only 1 in group should have PENDING, child does not inherit trace status*/ + sigemptyset(&rpc->p_pending); + rpc->p_pendcount = 0; + rpc->p_reg.retreg = 0; /* child sees pid = 0 to know it is child */ + + rpc->user_time = 0; /* set all the accounting times to 0 */ + rpc->sys_time = 0; + rpc->child_utime = 0; + rpc->child_stime = 0; + + return(OK); +} + + diff --git a/kernel/system/old/do_getinfo.c b/kernel/system/old/do_getinfo.c new file mode 100644 index 000000000..4acdce367 --- /dev/null +++ b/kernel/system/old/do_getinfo.c @@ -0,0 +1,148 @@ +/* The system call implemented in this file: + * m_type: SYS_GETINFO + * + * The parameters for this system call are: + * m1_i3: I_REQUEST (what info to get) + * m1_i4: I_PROC_NR (process to store value at) + * m1_p1: I_VAL_PTR (where to put it) + * m1_i1: I_VAL_LEN (maximum length expected, optional) + * m1_p2: I_KEY_PTR (environment variable key) + * m1_i2: I_KEY_LEN (lenght of environment variable key) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + + +#include "../kernel.h" +#include "../system.h" + + +/*===========================================================================* + * do_getinfo * + *===========================================================================*/ +PUBLIC int do_getinfo(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Request system information to be copied to caller's address space. */ + size_t length; + phys_bytes src_phys; + phys_bytes dst_phys; + int proc_nr; + + /* First get the process number and verify it. */ + proc_nr = (m_ptr->I_PROC_NR == SELF) ? m_ptr->m_source : m_ptr->I_PROC_NR; + if (! isokprocn(proc_nr)) { + printf("Invalid process number: %d from %d\n", proc_nr, m_ptr->m_source); + return(EINVAL); + } + + /* Set source address and length based on request type. */ + switch (m_ptr->I_REQUEST) { + case GET_KENVIRON: { + struct kenviron kenv; + extern int end; + kenv.pc_at = pc_at; kenv.ps_mca = ps_mca; + kenv.processor = processor; kenv.protected = protected_mode; + kenv.ega = ega; kenv.vga = vga; + kenv.proc_addr = (vir_bytes) proc; + kenv.kmem_start = vir2phys(0); + kenv.kmem_end = vir2phys(&end); + length = sizeof(struct kenviron); + src_phys = vir2phys(&kenv); + break; + } + case GET_IMAGE: { + length = sizeof(struct system_image) * IMAGE_SIZE; + src_phys = vir2phys(image); + break; + } + case GET_IRQTAB: { + length = sizeof(struct irqtab) * NR_IRQ_VECTORS; + src_phys = vir2phys(irqtab); + break; + } + case GET_MEMCHUNKS: { + length = sizeof(struct memory) * NR_MEMS; + src_phys = vir2phys(mem); + break; + } + case GET_SCHEDINFO: { + /* This is slightly complicated because we need several variables + * at once, otherwise the scheduling information may be incorrect. + */ + length = sizeof(struct proc *) * NR_SCHED_QUEUES; + src_phys = vir2phys(rdy_head); + dst_phys = numap_local(m_ptr->m_source, (vir_bytes) m_ptr->I_KEY_PTR, + length); + if (src_phys == 0 || dst_phys == 0) return(EFAULT); + phys_copy(src_phys, dst_phys, length); + /* Fall through to also get a copy of the process table. */ + } + case GET_PROCTAB: { + length = sizeof(struct proc) * (NR_PROCS + NR_TASKS); + src_phys = vir2phys(proc); + break; + } + case GET_PROC: { + if (! isokprocn(m_ptr->I_KEY_LEN)) return(EINVAL); + length = sizeof(struct proc); + src_phys = vir2phys(proc_addr(m_ptr->I_KEY_LEN)); + break; + } + case GET_MONPARAMS: { + src_phys = mon_params; /* already is a physical address! */ + length = mon_parmsize; + break; + } + case GET_KENV: { /* get one string by name */ + char key[32]; /* boot variable key provided by caller */ + char *val; /* pointer to actual boot variable value */ + if (m_ptr->I_KEY_LEN > sizeof(key)) return(EINVAL); + if (vir_copy(proc_nr, (vir_bytes) m_ptr->I_KEY_PTR, + SYSTASK, (vir_bytes) key, m_ptr->I_KEY_LEN) != OK) return(EFAULT); + if ((val=getkenv(key)) == NULL) return(ESRCH); + length = strlen(val) + 1; + src_phys = vir2phys(val); + break; + } + case GET_PROCNR: { + length = sizeof(int); + if (m_ptr->I_KEY_LEN == 0) { /* get own process nr */ + src_phys = vir2phys(&proc_nr); + } else { /* lookup nr by name */ + int proc_found = FALSE; + struct proc *pp; + char key[8]; /* storage for process name to lookup */ + if (m_ptr->I_KEY_LEN > sizeof(key)) return(EINVAL); + if (vir_copy(proc_nr, (vir_bytes) m_ptr->I_KEY_PTR, SYSTASK, + (vir_bytes) key, m_ptr->I_KEY_LEN) != OK) return(EFAULT); + for (pp= BEG_PROC_ADDR; pp<END_PROC_ADDR; pp++) { + if (strncmp(pp->p_name, key, m_ptr->I_KEY_LEN) == 0) { + src_phys = vir2phys(&(pp->p_nr)); + proc_found = TRUE; + break; + } + } + if (! proc_found) return(ESRCH); + } + break; + } + case GET_KMESSAGES: { + length = sizeof(struct kmessages); + src_phys = vir2phys(&kmess); + break; + } + default: + return(EINVAL); + } + + /* Try to make the actual copy for the requested data. */ + if (m_ptr->I_VAL_LEN > 0 && length > m_ptr->I_VAL_LEN) return (E2BIG); + dst_phys = numap_local(proc_nr, (vir_bytes) m_ptr->I_VAL_PTR, length); + if (src_phys == 0 || dst_phys == 0) return(EFAULT); + phys_copy(src_phys, dst_phys, length); + return(OK); +} + + diff --git a/kernel/system/old/do_getmap.c b/kernel/system/old/do_getmap.c new file mode 100644 index 000000000..3b6836318 --- /dev/null +++ b/kernel/system/old/do_getmap.c @@ -0,0 +1,44 @@ +/* The system call implemented in this file: + * m_type: SYS_GETMAP + * + * The parameters for this system call are: + * m1_i1: PR_PROC_NR (process to get map of) + * m1_p1: PR_MEM_PTR (copy the memory map here) + */ + +#include "../kernel.h" +#include "../system.h" +INIT_ASSERT + +/*===========================================================================* + * do_getmap * + *===========================================================================*/ +PUBLIC int do_getmap(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_getmap(). Report the memory map to MM. */ + + register struct proc *rp; + phys_bytes dst_phys; + int caller; /* where the map has to be stored */ + int k; /* process whose map is to be loaded */ + struct mem_map *map_ptr; /* virtual address of map inside caller (MM) */ + + /* Extract message parameters and copy new memory map to MM. */ + caller = m_ptr->m_source; + k = m_ptr->PR_PROC_NR; + map_ptr = (struct mem_map *) m_ptr->PR_MEM_PTR; + + assert(isokprocn(k)); /* unlikely: MM sends a bad proc nr. */ + + rp = proc_addr(k); /* ptr to entry of the map */ + + /* Copy the map to MM. */ + dst_phys = umap_local(proc_addr(caller), D, (vir_bytes) map_ptr, sizeof(rp->p_map)); + assert(dst_phys != 0); + phys_copy(vir2phys(rp->p_map), dst_phys, sizeof(rp->p_map)); + + return(OK); +} + + diff --git a/kernel/system/old/do_getsig.c b/kernel/system/old/do_getsig.c new file mode 100644 index 000000000..f2a9897c2 --- /dev/null +++ b/kernel/system/old/do_getsig.c @@ -0,0 +1,46 @@ +/* The system call implemented in this file: + * m_type: SYS_GETSIG + * + * The parameters for this system call are: + * m2_i1: SIG_PROC (return proc nr or NONE here) + * m2_l1: SIG_MAP (return signal map here) + */ + +#include "../kernel.h" +#include "../system.h" +#include <signal.h> + + + +/*===========================================================================* + * do_getsig * + *===========================================================================*/ +PUBLIC int do_getsig(m_ptr) +message *m_ptr; /* pointer to the request message */ +{ +/* MM is ready to accept signals and repeatedly does a system call to get one + * Find a process with pending signals. If no more signals are available, + * return NONE in the process number field. + */ + register struct proc *rp; + + /* Only the MM is allowed to request pending signals. */ + if (m_ptr->m_source != MM_PROC_NR) + return(EPERM); + + /* Find the next process with pending signals. */ + for (rp = BEG_SERV_ADDR; rp < END_PROC_ADDR; rp++) { + if (rp->p_flags & PENDING) { + m_ptr->SIG_PROC = proc_number(rp); + m_ptr->SIG_MAP = rp->p_pending; + sigemptyset(&rp->p_pending); /* the ball is now in MM's court */ + rp->p_flags &= ~PENDING; /* remains inhibited by SIG_PENDING */ + return(OK); + } + } + + /* No process with pending signals was found. */ + m_ptr->SIG_PROC = NONE; + return(OK); +} + diff --git a/kernel/system/old/do_getsp.c b/kernel/system/old/do_getsp.c new file mode 100644 index 000000000..65f14653d --- /dev/null +++ b/kernel/system/old/do_getsp.c @@ -0,0 +1,32 @@ +/* The system call implemented in this file: + * m_type: SYS_GETSP + * + * The parameters for this system call are: + * m1_i1: PR_PROC_NR (process to get stack pointer of) + * m1_p1: PR_STACK_PTR (return stack pointer here) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +#include "../kernel.h" +#include "../system.h" +INIT_ASSERT + +/*===========================================================================* + * do_getsp * + *===========================================================================*/ +PUBLIC int do_getsp(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_getsp(). MM wants to know what sp is. */ + + register struct proc *rp; + + rp = proc_addr(m_ptr->PR_PROC_NR); + assert(isuserp(rp)); + m_ptr->PR_STACK_PTR = (char *) rp->p_reg.sp; /* return sp here (bad type) */ + return(OK); +} + + diff --git a/kernel/system/old/do_iopenable.c b/kernel/system/old/do_iopenable.c new file mode 100644 index 000000000..cb985fe0a --- /dev/null +++ b/kernel/system/old/do_iopenable.c @@ -0,0 +1,28 @@ +/* The system call implemented in this file: + * m_type: SYS_IOPENABLE + * + * The parameters for this system call are: + * m2_i2: PROC_NR (process to give I/O Protection Level bits) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +#include "../kernel.h" +#include "../system.h" + +/*===========================================================================* + * do_iopenable * + *===========================================================================*/ +PUBLIC int do_iopenable(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +#if ENABLE_USERPRIV && ENABLE_USERIOPL + enable_iop(proc_addr(m_ptr->PROC_NR)); + return(OK); +#else + return(EPERM); +#endif +} + + diff --git a/kernel/system/old/do_kill.c b/kernel/system/old/do_kill.c new file mode 100644 index 000000000..c95b1a90d --- /dev/null +++ b/kernel/system/old/do_kill.c @@ -0,0 +1,15 @@ +#include "../kernel.h" +#include "../system.h" + +PUBLIC int do_kill(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_kill(). Cause a signal to be sent to a process via MM. + * Note that this has nothing to do with the kill (2) system call, this + * is how the FS (and possibly other servers) get access to cause_sig. + */ + cause_sig(m_ptr->SIG_PROC, m_ptr->SIG_NUMBER); + return(OK); +} + + diff --git a/kernel/system/old/do_kmalloc.c b/kernel/system/old/do_kmalloc.c new file mode 100644 index 000000000..7d474b1df --- /dev/null +++ b/kernel/system/old/do_kmalloc.c @@ -0,0 +1,36 @@ +/* The system call implemented in this file: + * m_type: SYS_KMALLOC + * + * The parameters for this system call are: + * m4_l2: MEM_CHUNK_SIZE (request a buffer of this size) + * m4_l1: MEM_CHUNK_BASE (return physical address on success) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +#include "../kernel.h" +#include "../system.h" + +/*===========================================================================* + * do_kmalloc * + *===========================================================================*/ +PUBLIC int do_kmalloc(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Request a (DMA) buffer to be allocated in one of the memory chunks. */ + phys_clicks tot_clicks; + struct memory *memp; + + tot_clicks = (m_ptr->MEM_CHUNK_SIZE + CLICK_SIZE-1) >> CLICK_SHIFT; + memp = &mem[NR_MEMS]; + while ((--memp)->size < tot_clicks) { + if (memp == mem) { + return(ENOMEM); + } + } + memp->size -= tot_clicks; + m_ptr->MEM_CHUNK_BASE = (memp->base + memp->size) << CLICK_SHIFT; + return(OK); +} + diff --git a/kernel/system/old/do_mem.c b/kernel/system/old/do_mem.c new file mode 100644 index 000000000..24141157d --- /dev/null +++ b/kernel/system/old/do_mem.c @@ -0,0 +1,33 @@ +/* The system call implemented in this file: + * m_type: SYS_MEM + * + * The parameters for this system call are: + * m4_l1: MEM_CHUNK_BASE (memory base) + * m4_l2: MEM_CHUNK_SIZE (memory size) + * m4_l3: MEM_TOT_SIZE (total memory) + */ + +#include "../kernel.h" +#include "../system.h" + +/*===========================================================================* + * do_mem * + *===========================================================================*/ +PUBLIC int do_mem(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Return the base and size of the next chunk of memory. */ + + struct memory *memp; + + for (memp = mem; memp < &mem[NR_MEMS]; ++memp) { + m_ptr->MEM_CHUNK_BASE = memp->base; + m_ptr->MEM_CHUNK_SIZE = memp->size; + m_ptr->MEM_TOT_SIZE = tot_mem_size; + memp->size = 0; + if (m_ptr->MEM_CHUNK_SIZE != 0) break; /* found a chunk */ + } + return(OK); +} + + diff --git a/kernel/system/old/do_newmap.c b/kernel/system/old/do_newmap.c new file mode 100644 index 000000000..7dd9d45ba --- /dev/null +++ b/kernel/system/old/do_newmap.c @@ -0,0 +1,52 @@ +/* The system call implemented in this file: + * m_type: SYS_NEWMAP + * + * The parameters for this system call are: + * m1_i1: PR_PROC_NR (install new map for this process) + * m1_p1: PR_MEM_PTR (pointer to memory map) + */ + +#include "../kernel.h" +#include "../system.h" +INIT_ASSERT + +/*===========================================================================* + * do_newmap * + *===========================================================================*/ +PUBLIC int do_newmap(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_newmap(). Fetch the memory map from MM. */ + + register struct proc *rp; + phys_bytes src_phys; + int caller; /* whose space has the new map (usually MM) */ + int k; /* process whose map is to be loaded */ + int old_flags; /* value of flags before modification */ + struct mem_map *map_ptr; /* virtual address of map inside caller (MM) */ + + /* Extract message parameters and copy new memory map from MM. */ + caller = m_ptr->m_source; + k = m_ptr->PR_PROC_NR; + map_ptr = (struct mem_map *) m_ptr->PR_MEM_PTR; + if (!isokprocn(k)) return(EINVAL); + rp = proc_addr(k); /* ptr to entry of user getting new map */ + + /* Copy the map from MM. */ + src_phys = umap_local(proc_addr(caller), D, (vir_bytes) map_ptr, sizeof(rp->p_map)); + assert(src_phys != 0); + phys_copy(src_phys, vir2phys(rp->p_map), (phys_bytes) sizeof(rp->p_map)); + +#if (CHIP != M68000) + alloc_segments(rp); +#else + pmmu_init_proc(rp); +#endif + old_flags = rp->p_flags; /* save the previous value of the flags */ + rp->p_flags &= ~NO_MAP; + if (old_flags != 0 && rp->p_flags == 0) lock_ready(rp); + + return(OK); +} + + diff --git a/kernel/system/old/do_phys2seg.c b/kernel/system/old/do_phys2seg.c new file mode 100644 index 000000000..31117d38b --- /dev/null +++ b/kernel/system/old/do_phys2seg.c @@ -0,0 +1,86 @@ +/* The system call implemented in this file: + * m_type: SYS_PHYS2SEG + * + * The parameters for this system call are: + * m4_l1: SEG_SELECT (return segment selector here) + * m4_l2: SEG_OFFSET (return offset within segment here) + * m4_l3: SEG_PHYS (physical address to convert) + * m4_l4: SEG_SIZE (size of segment) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +#include "../kernel.h" +#include "../system.h" +#include "../protect.h" + + +/*===========================================================================* + * do_phys2seg * + *===========================================================================*/ +PUBLIC int do_phys2seg(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Return a segment selector and offset that can be used to reach a physical + * address, for use by a driver doing memory I/O in the A0000 - DFFFF range. + */ + u16_t selector; + vir_bytes offset; + register struct proc *rp; + phys_bytes phys = (phys_bytes) m_ptr->SEG_PHYS; + vir_bytes size = (vir_bytes) m_ptr->SEG_SIZE; + int result; + +#if 0 + kprintf("FLAT DS SELECTOR used\n", NO_ARG); + selector = FLAT_DS_SELECTOR; + offset = phys; +#else + kprintf("Using Experimental LDT selector for video memory\n", NO_ARG); + + if (!protected_mode) { + selector = phys / HCLICK_SIZE; + offset = phys % HCLICK_SIZE; + result = OK; + } else { + /* Check if the segment size can be recorded in bytes, that is, check + * if descriptor's limit field can delimited the allowed memory region + * precisely. This works up to 1MB. If the size is larger, 4K pages + * instead of bytes are used. + */ + if (size < BYTE_GRAN_MAX) { + rp = proc_addr(m_ptr->m_source); + init_dataseg(&rp->p_ldt[EXTRA_LDT_INDEX], phys, size, + USER_PRIVILEGE); + selector = (EXTRA_LDT_INDEX * 0x08) | (1 * 0x04) | USER_PRIVILEGE; + offset = 0; + result = OK; + } else { +#if ENABLE_USERPRIV && ENABLE_LOOSELDT + rp = proc_addr(m_ptr->m_source); + init_dataseg(&rp->p_ldt[EXTRA_LDT_INDEX], phys & ~0xFFFF, 0, + USER_PRIVILEGE); + selector = (EXTRA_LDT_INDEX * 0x08) | (1 * 0x04) | USER_PRIVILEGE; + offset = phys & 0xFFFF; + result = OK; +#else + result = E2BIG; /* allow settings only */ +#endif + } + } +#endif + +#if 0 + kprintf("do_phys2seg: proc %d", m_ptr->m_source); + kprintf(" phys %u", phys); + kprintf(" size %u", size); + kprintf(" sel %u", selector); + kprintf(" off %u\n", offset); +#endif + m_ptr->SEG_SELECT = selector; + m_ptr->SEG_OFFSET = offset; + return(result); +} + + diff --git a/kernel/system/old/do_physcopy.c b/kernel/system/old/do_physcopy.c new file mode 100644 index 000000000..d20feafa8 --- /dev/null +++ b/kernel/system/old/do_physcopy.c @@ -0,0 +1,37 @@ +/* The system call implemented in this file: + * m_type: SYS_PHYSCOPY + * + * The parameters for this system call are: + * m5_l1: CP_SRC_ADDR (physical source address) + * m5_l2: CP_DST_ADDR (physical destination address) + * m5_l3: CP_NR_BYTES (number of bytes to copy) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +#include "../kernel.h" +#include "../system.h" + +/*===========================================================================* + * do_physcopy * + *===========================================================================*/ +PUBLIC int do_physcopy(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_physcopy(). Copy data by using physical addressing. */ + + phys_bytes src_phys, dst_phys, bytes; + + /* Dismember the command message. */ + src_phys = (phys_bytes) m_ptr->CP_SRC_ADDR; + dst_phys = (phys_bytes) m_ptr->CP_DST_ADDR; + bytes = (phys_bytes) m_ptr->CP_NR_BYTES; + + /* Do some checks and copy the data. */ + if (src_phys == 0 || dst_phys == 0) return(EFAULT); + phys_copy(src_phys, dst_phys, bytes); + return(OK); +} + + diff --git a/kernel/system/old/do_sdevio.c b/kernel/system/old/do_sdevio.c new file mode 100644 index 000000000..342bacf76 --- /dev/null +++ b/kernel/system/old/do_sdevio.c @@ -0,0 +1,60 @@ +/* The system call implemented in this file: + * m_type: SYS_SDEVIO + * + * The parameters for this system call are: + * m2_i3: DIO_REQUEST (request input or output) + * m2_i1: DIO_TYPE (flag indicating byte, word, or long) + * m2_l1: DIO_PORT (port to read/ write) + * m2_p1: DIO_VEC_ADDR (virtual address of buffer) + * m2_l2: DIO_VEC_SIZE (number of elements) + * m2_i2: DIO_VEC_PROC (process where buffer is) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +#include "../kernel.h" +#include "../system.h" +#include <minix/devio.h> + +/*===========================================================================* + * do_sdevio * + *===========================================================================*/ +PUBLIC int do_sdevio(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ + int proc_nr = m_ptr->DIO_VEC_PROC; + int count = m_ptr->DIO_VEC_SIZE; + long port = m_ptr->DIO_PORT; + phys_bytes phys_buf; + + /* Check if process number is OK. */ + if (proc_nr == SELF) proc_nr = m_ptr->m_source; + if (! isokprocn(proc_nr)) + return(EINVAL); + + /* Get and check physical address. */ + if ((phys_buf = numap_local(proc_nr, (vir_bytes) m_ptr->DIO_VEC_ADDR, count)) == 0) + return(EFAULT); + + /* Perform device I/O for bytes and words. Longs are not supported. */ + if (m_ptr->DIO_REQUEST == DIO_INPUT) { + switch (m_ptr->DIO_TYPE) { + case DIO_BYTE: phys_insb(port, phys_buf, count); break; + case DIO_WORD: phys_insw(port, phys_buf, count); break; + default: return(EINVAL); + } + } else if (m_ptr->DIO_REQUEST == DIO_OUTPUT) { + switch (m_ptr->DIO_TYPE) { + case DIO_BYTE: phys_outsb(port, phys_buf, count); break; + case DIO_WORD: phys_outsw(port, phys_buf, count); break; + default: return(EINVAL); + } + } + else { + return(EINVAL); + } + return(OK); +} + + diff --git a/kernel/system/old/do_sigreturn.c b/kernel/system/old/do_sigreturn.c new file mode 100644 index 000000000..6e7ea9436 --- /dev/null +++ b/kernel/system/old/do_sigreturn.c @@ -0,0 +1,72 @@ +/* The system call implemented in this file: + * m_type: SYS_SIGRETURN + * + * The parameters for this system call are: + * m2_i1: SIG_PROC (process number) + * m2_i3: SIG_FLAGS (sig return flags) (unused) + * m2_p1: SIG_CTXT_PTR (pointer to sigcontext structure) + */ + +#include "../kernel.h" +#include "../system.h" +#include <signal.h> +#include <sys/sigcontext.h> +INIT_ASSERT + +/*===========================================================================* + * do_sigreturn * + *===========================================================================*/ +PUBLIC int do_sigreturn(m_ptr) +register message *m_ptr; +{ +/* POSIX style signals require sys_sigreturn to put things in order before the + * signalled process can resume execution + */ + + struct sigcontext sc; + register struct proc *rp; + phys_bytes src_phys; + + rp = proc_addr(m_ptr->SIG_PROC); + if (! isuserp(rp)) + printf("message source: %d; rp: %d\n", m_ptr->m_source, rp->p_nr); + assert(isuserp(rp)); + + /* Copy in the sigcontext structure. */ + src_phys = umap_local(rp, D, (vir_bytes) m_ptr->SIG_CTXT_PTR, + (vir_bytes) sizeof(struct sigcontext)); + if (src_phys == 0) return(EFAULT); + phys_copy(src_phys, vir2phys(&sc), (phys_bytes) sizeof(struct sigcontext)); + + /* Make sure that this is not just a jmp_buf. */ + if ((sc.sc_flags & SC_SIGCONTEXT) == 0) return(EINVAL); + + /* Fix up only certain key registers if the compiler doesn't use + * register variables within functions containing setjmp. + */ + if (sc.sc_flags & SC_NOREGLOCALS) { + rp->p_reg.retreg = sc.sc_retreg; + rp->p_reg.fp = sc.sc_fp; + rp->p_reg.pc = sc.sc_pc; + rp->p_reg.sp = sc.sc_sp; + return (OK); + } + sc.sc_psw = rp->p_reg.psw; + +#if (CHIP == INTEL) + /* Don't panic kernel if user gave bad selectors. */ + sc.sc_cs = rp->p_reg.cs; + sc.sc_ds = rp->p_reg.ds; + sc.sc_es = rp->p_reg.es; +#if _WORD_SIZE == 4 + sc.sc_fs = rp->p_reg.fs; + sc.sc_gs = rp->p_reg.gs; +#endif +#endif + + /* Restore the registers. */ + memcpy(&rp->p_reg, (char *)&sc.sc_regs, sizeof(struct sigregs)); + + return(OK); +} + diff --git a/kernel/system/old/do_svrctl.c b/kernel/system/old/do_svrctl.c new file mode 100644 index 000000000..e7653b914 --- /dev/null +++ b/kernel/system/old/do_svrctl.c @@ -0,0 +1,67 @@ +/* The system call implemented in this file: + * m_type: SYS_SVRCTL + * + * The parameters for this system call are: + * m2_i1: CTL_PROC_NR (process number of caller) + * m2_i2: CTL_REQUEST (request type) + * m2_i3: CTL_MM_PRIV (privilege) + * m2_l1: CTL_SEND_MASK (new send mask to be installed) + * m2_l2: CTL_PROC_TYPE (new process type) + * m2_p1: CTL_ARG_PTR (argument pointer) + */ + +#include "../kernel.h" +#include "../system.h" +#include <sys/svrctl.h> +#include "../sendmask.h" + +/*===========================================================================* + * do_svrctl * + *===========================================================================*/ +PUBLIC int do_svrctl(m_ptr) +message *m_ptr; /* pointer to request message */ +{ + register struct proc *rp; + int proc_nr, priv; + int request; + vir_bytes argp; + + /* Extract message parameters. */ + proc_nr = m_ptr->CTL_PROC_NR; + if (proc_nr == SELF) proc_nr = m_ptr->m_source; + if (! isokprocn(proc_nr)) return(EINVAL); + + request = m_ptr->CTL_REQUEST; + priv = m_ptr->CTL_MM_PRIV; + argp = (vir_bytes) m_ptr->CTL_ARG_PTR; + rp = proc_addr(proc_nr); + + /* Check if the MM privileges are super user. */ + if (!priv || !isuserp(rp)) + return(EPERM); + + /* See what is requested and handle the request. */ + switch (request) { + case SYSSIGNON: { + /* Make this process a server. The system processes should be able + * to communicate with this new server, so update their send masks + * as well. + */ + /* fall through */ + } + case SYSSENDMASK: { + rp->p_type = P_SERVER; + rp->p_sendmask = ALLOW_ALL_MASK; + send_mask_allow(proc_addr(RTL8139)->p_sendmask, proc_nr); + send_mask_allow(proc_addr(MM_PROC_NR)->p_sendmask, proc_nr); + send_mask_allow(proc_addr(FS_PROC_NR)->p_sendmask, proc_nr); + send_mask_allow(proc_addr(IS_PROC_NR)->p_sendmask, proc_nr); + send_mask_allow(proc_addr(CLOCK)->p_sendmask, proc_nr); + send_mask_allow(proc_addr(SYSTASK)->p_sendmask, proc_nr); + return(OK); + } + default: + return(EINVAL); + } +} + diff --git a/kernel/system/old/do_umap.c b/kernel/system/old/do_umap.c new file mode 100644 index 000000000..475df95e9 --- /dev/null +++ b/kernel/system/old/do_umap.c @@ -0,0 +1,33 @@ +/* The system call implemented in this file: + * m_type: SYS_UMAP + * + * The parameters for this system call are: + * m5_i1: CP_SRC_PROC_NR (process number) + * m5_c1: CP_SRC_SPACE (segment where address is: T, D, or S) + * m5_l1: CP_SRC_ADDR (virtual address) + * m5_l2: CP_DST_ADDR (returns physical address) + * m5_l3: CP_NR_BYTES (size of datastructure) + */ + +#include "../kernel.h" +#include "../system.h" + +/*==========================================================================* + * do_umap * + *==========================================================================*/ +PUBLIC int do_umap(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Same as umap_local(), for non-kernel processes. */ + int proc_nr = (int) m_ptr->CP_SRC_PROC_NR; + if (proc_nr == SELF) proc_nr = m_ptr->m_source; + if (! isokprocn(proc_nr)) return(EINVAL); + + m_ptr->CP_DST_ADDR = umap_local(proc_addr(proc_nr), + (int) m_ptr->CP_SRC_SPACE, + (vir_bytes) m_ptr->CP_SRC_ADDR, + (vir_bytes) m_ptr->CP_NR_BYTES); + return(OK); +} + + diff --git a/kernel/system/old/do_vdevio.c b/kernel/system/old/do_vdevio.c new file mode 100644 index 000000000..960918467 --- /dev/null +++ b/kernel/system/old/do_vdevio.c @@ -0,0 +1,126 @@ +/* The system call implemented in this file: + * m_type: SYS_VDEVIO + * + * The parameters for this system call are: + * m2_i3: DIO_REQUEST (request input or output) + * m2_i1: DIO_TYPE (flag indicating byte, word, or long) + * m2_p1: DIO_VEC_ADDR (pointer to port/ value pairs) + * m2_i2: DIO_VEC_SIZE (number of ports to read or write) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +#include "../kernel.h" +#include "../system.h" +#include <minix/devio.h> + +/* Buffer for SYS_VDEVIO to copy (port,value)-pairs from/ to user. */ +PRIVATE char vdevio_pv_buf[VDEVIO_BUF_SIZE]; + +/* SYS_VDEVIO sends a pointer to a (port,value)-pairs vector at the caller. + * Define the maximum number of (port,value)-pairs that can be handled in a + * in a single SYS_VDEVIO system call based on the struct definitions. + */ +#define MAX_PVB_PAIRS ((VDEVIO_BUF_SIZE * sizeof(char)) / sizeof(pvb_pair_t)) +#define MAX_PVW_PAIRS ((VDEVIO_BUF_SIZE * sizeof(char)) / sizeof(pvw_pair_t)) +#define MAX_PVL_PAIRS ((VDEVIO_BUF_SIZE * sizeof(char)) / sizeof(pvl_pair_t)) + + +/*===========================================================================* + * do_vdevio * + *===========================================================================*/ +PUBLIC int do_vdevio(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Perform a series of device I/O on behalf of a non-kernel process. The + * I/O addresses and I/O values are fetched from and returned to some buffer + * in user space. The actual I/O is wrapped by lock() and unlock() to prevent + * that I/O batch from being interrrupted. + * This is the counterpart of do_devio, which performs a single device I/O. + */ + pvb_pair_t *pvb_pairs; /* needed for byte values */ + pvw_pair_t *pvw_pairs; /* needed for word values */ + pvl_pair_t *pvl_pairs; /* needed for long values */ + int i; + pid_t caller_pid; /* process id of caller */ + size_t bytes; /* # bytes to be copied */ + vir_bytes caller_vir; /* virtual address at caller */ + phys_bytes caller_phys; /* physical address at caller */ + phys_bytes kernel_phys; /* physical address in kernel */ + + + /* Check if nr of ports is ok and get size of (port,value) data. */ + if (m_ptr->DIO_VEC_SIZE <= 0) return(EINVAL); + switch(m_ptr->DIO_TYPE) { + case DIO_BYTE: + if (m_ptr->DIO_VEC_SIZE > MAX_PVB_PAIRS) return(EINVAL); + bytes = (size_t) (m_ptr->DIO_VEC_SIZE * sizeof(pvb_pair_t)); + break; + case DIO_WORD: + if (m_ptr->DIO_VEC_SIZE > MAX_PVW_PAIRS) return(EINVAL); + bytes = (size_t) (m_ptr->DIO_VEC_SIZE * sizeof(pvw_pair_t)); + break; + case DIO_LONG: + if (m_ptr->DIO_VEC_SIZE > MAX_PVL_PAIRS) return(EINVAL); + bytes = (size_t) (m_ptr->DIO_VEC_SIZE * sizeof(pvl_pair_t)); + break; + default: /* this once and for all checks for a correct type */ + return(EINVAL); + } + + /* Calculate physical addresses and copy (port,value)-pairs from user. */ + caller_pid = (pid_t) m_ptr->m_source; + caller_vir = (vir_bytes) m_ptr->DIO_VEC_ADDR; + caller_phys = umap_local(proc_addr(caller_pid), D, caller_vir, bytes); + if (0 == caller_phys) return EFAULT; + kernel_phys = vir2phys(vdevio_pv_buf); + phys_copy(caller_phys, kernel_phys, (phys_bytes) bytes); + + /* Perform actual device I/O for byte, word, and long values. Note that + * the entire switch is wrapped in lock() and unlock() to prevent the I/O + * batch from being interrupted. It may be cleaner to do this just around + * the for loops, but this results in rather lenghty code. + */ + lock(); + switch (m_ptr->DIO_TYPE) { + case DIO_BYTE: /* byte values */ + pvb_pairs = (pvb_pair_t *) vdevio_pv_buf; + if (DIO_INPUT == m_ptr->DIO_REQUEST) { + for (i=0; i < m_ptr->DIO_VEC_SIZE; i++) + pvb_pairs[i].value = inb(pvb_pairs[i].port); + } else { + for (i=0; i < m_ptr->DIO_VEC_SIZE; i++) + outb(pvb_pairs[i].port, pvb_pairs[i].value); + } + break; + case DIO_WORD: /* word values */ + pvw_pairs = (pvw_pair_t *) vdevio_pv_buf; + if (DIO_INPUT == m_ptr->DIO_REQUEST) { + for (i=0; i < m_ptr->DIO_VEC_SIZE; i++) + pvw_pairs[i].value = inw(pvw_pairs[i].port); + } else { + for (i=0; i < m_ptr->DIO_VEC_SIZE; i++) + outw(pvw_pairs[i].port, pvw_pairs[i].value); + } + break; + case DIO_LONG: /* fall through: long values */ + default: /* only DIO_LONG can arrive here, see above switch */ + pvl_pairs = (pvl_pair_t *) vdevio_pv_buf; + if (DIO_INPUT == m_ptr->DIO_REQUEST) { + for (i=0; i < m_ptr->DIO_VEC_SIZE; i++) + pvl_pairs[i].value = inl(pvl_pairs[i].port); + } else { + for (i=0; i < m_ptr->DIO_VEC_SIZE; i++) + outl(pvb_pairs[i].port, pvl_pairs[i].value); + } + } + unlock(); + + /* Almost done, copy back results for input requests. */ + if (DIO_INPUT == m_ptr->REQUEST) + phys_copy(kernel_phys, caller_phys, (phys_bytes) bytes); + return(OK); +} + + diff --git a/kernel/system/old/do_vircopy.c b/kernel/system/old/do_vircopy.c new file mode 100644 index 000000000..4ebb42249 --- /dev/null +++ b/kernel/system/old/do_vircopy.c @@ -0,0 +1,69 @@ +/* The system call implemented in this file: + * m_type: SYS_VIRCOPY + * + * The parameters for this system call are: + * m5_c1: CP_SRC_SPACE + * m5_l1: CP_SRC_ADDR + * m5_i1: CP_SRC_PROC_NR + * m5_c2: CP_DST_SPACE + * m5_l2: CP_DST_ADDR + * m5_i2: CP_DST_PROC_NR + * m5_l3: CP_NR_BYTES + */ + +#include "../kernel.h" +#include "../system.h" + +/*===========================================================================* + * do_vircopy * + *===========================================================================*/ +PUBLIC int do_vircopy(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_vircopy(). Copy data by using virtual addressing. */ + struct vir_addr vir_addr[2]; /* virtual source and destination address */ + vir_bytes bytes; /* number of bytes to copy */ + int i; + + /* Dismember the command message. */ + vir_addr[_SRC_].proc_nr = m_ptr->CP_SRC_PROC_NR; + vir_addr[_SRC_].segment = m_ptr->CP_SRC_SPACE; + vir_addr[_SRC_].offset = (vir_bytes) m_ptr->CP_SRC_ADDR; + vir_addr[_DST_].proc_nr = m_ptr->CP_DST_PROC_NR; + vir_addr[_DST_].segment = m_ptr->CP_DST_SPACE; + vir_addr[_DST_].offset = (vir_bytes) m_ptr->CP_DST_ADDR; + bytes = (phys_bytes) m_ptr->CP_NR_BYTES; + + /* Now do some checks for both the source and destination virtual address. + * This is done once for _SRC_, then once for _DST_. + */ + for (i=_SRC_; i<=_DST_; i++) { + + /* Check if process number was given implictly with SELF and is valid. */ + if (vir_addr[i].proc_nr == SELF) vir_addr[i].proc_nr = m_ptr->m_source; + if (! isokprocn(vir_addr[i].proc_nr)) { + kprintf("do_vircopy: illegal proc nr\n",NO_ARG); + return(EINVAL); + } + + /* Copying from or to special segments can only done by the owner. */ + if ((vir_addr[i].segment & SEGMENT_TYPE) != LOCAL_SEG && + vir_addr[i].proc_nr != m_ptr->m_source) { + kprintf("do_vircopy: special seg permission denied\n", NO_ARG); + return(EPERM); + } + } + + /* Check for overflow. This would happen for 64K segments and 16-bit + * vir_bytes. Especially copying by the MM on do_fork() is affected. + */ + if (bytes != (vir_bytes) bytes) { + kprintf("do_vircopy: overflow\n", NO_ARG); + return(E2BIG); + } + + /* Now try to make the actual virtual copy. */ + return( virtual_copy(&vir_addr[_SRC_], &vir_addr[_DST_], bytes) ); +} + + diff --git a/kernel/system/old/do_xit.c b/kernel/system/old/do_xit.c new file mode 100644 index 000000000..c9f483360 --- /dev/null +++ b/kernel/system/old/do_xit.c @@ -0,0 +1,38 @@ +/*===========================================================================* + * do_xit * + *===========================================================================*/ +PUBLIC int do_xit(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_exit. A user process has exited (the MM sent the request). + */ + register struct proc *rp, *rc; + struct proc *np, *xp; + int exit_proc_nr; + + /* Get a pointer to the process that exited. */ + exit_proc_nr = m_ptr->PR_PROC_NR; + if (exit_proc_nr == SELF) exit_proc_nr = m_ptr->m_source; + if (! isokprocn(exit_proc_nr)) return(EINVAL); + rc = proc_addr(exit_proc_nr); + + /* If this is a user process and the MM passed in a valid parent process, + * accumulate the child times at the parent. + */ + if (isuserp(rc) && isokprocn(m_ptr->PR_PPROC_NR)) { + rp = proc_addr(m_ptr->PR_PPROC_NR); + lock(); + rp->child_utime += rc->user_time + rc->child_utime; + rp->child_stime += rc->sys_time + rc->child_stime; + unlock(); + } + + /* Now call the routine to clean up of the process table slot. This cancels + * outstanding timers, possibly removes the process from the message queues, + * and resets important process table fields. + */ + clear_proc(exit_proc_nr); + return(OK); /* tell MM that cleanup succeeded */ +} + + diff --git a/kernel/system/proctl.c b/kernel/system/proctl.c new file mode 100644 index 000000000..d03c62052 --- /dev/null +++ b/kernel/system/proctl.c @@ -0,0 +1,223 @@ +/* The system call implemented in this file: + * m_type: SYS_FORK + * + * The parameters for this system call are: + * m1_i1: PR_PROC_NR (child's process table slot) + * m1_i2: PR_PPROC_NR (parent, process that forked) + * m1_i3: PR_PID (child pid received from MM) + */ + +#include "../kernel.h" +#include "../system.h" +#include "../sendmask.h" +#include <signal.h> +#if (CHIP == INTEL) +#include "../protect.h" +#endif + +INIT_ASSERT + +/*===========================================================================* + * do_fork * + *===========================================================================*/ +PUBLIC int do_fork(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_fork(). PR_PPROC_NR has forked. The child is PR_PROC_NR. */ + +#if (CHIP == INTEL) + reg_t old_ldt_sel; +#endif + register struct proc *rpc; + struct proc *rpp; + + rpp = proc_addr(m_ptr->PR_PPROC_NR); + assert(isuserp(rpp)); + rpc = proc_addr(m_ptr->PR_PROC_NR); + assert(isemptyp(rpc)); + + /* Copy parent 'proc' struct to child. */ +#if (CHIP == INTEL) + old_ldt_sel = rpc->p_ldt_sel; /* stop this being obliterated by copy */ +#endif + + *rpc = *rpp; /* copy 'proc' struct */ + +#if (CHIP == INTEL) + rpc->p_ldt_sel = old_ldt_sel; +#endif + rpc->p_nr = m_ptr->PR_PROC_NR; /* this was obliterated by copy */ + + rpc->p_flags |= NO_MAP; /* inhibit the process from running */ + + rpc->p_flags &= ~(PENDING | SIG_PENDING | P_STOP); + + /* Only 1 in group should have PENDING, child does not inherit trace status*/ + sigemptyset(&rpc->p_pending); + rpc->p_pendcount = 0; + rpc->p_reg.retreg = 0; /* child sees pid = 0 to know it is child */ + + rpc->user_time = 0; /* set all the accounting times to 0 */ + rpc->sys_time = 0; + rpc->child_utime = 0; + rpc->child_stime = 0; + + return(OK); +} + + +/* The system call implemented in this file: + * m_type: SYS_NEWMAP + * + * The parameters for this system call are: + * m1_i1: PR_PROC_NR (install new map for this process) + * m1_p1: PR_MEM_PTR (pointer to memory map) + */ + + +/*===========================================================================* + * do_newmap * + *===========================================================================*/ +PUBLIC int do_newmap(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_newmap(). Fetch the memory map from MM. */ + + register struct proc *rp; + phys_bytes src_phys; + int caller; /* whose space has the new map (usually MM) */ + int k; /* process whose map is to be loaded */ + int old_flags; /* value of flags before modification */ + struct mem_map *map_ptr; /* virtual address of map inside caller (MM) */ + + /* Extract message parameters and copy new memory map from MM. */ + caller = m_ptr->m_source; + k = m_ptr->PR_PROC_NR; + map_ptr = (struct mem_map *) m_ptr->PR_MEM_PTR; + if (!isokprocn(k)) return(EINVAL); + rp = proc_addr(k); /* ptr to entry of user getting new map */ + + /* Copy the map from MM. */ + src_phys = umap_local(proc_addr(caller), D, (vir_bytes) map_ptr, + sizeof(rp->p_memmap)); + assert(src_phys != 0); + phys_copy(src_phys,vir2phys(rp->p_memmap),(phys_bytes)sizeof(rp->p_memmap)); + +#if (CHIP != M68000) + alloc_segments(rp); +#else + pmmu_init_proc(rp); +#endif + old_flags = rp->p_flags; /* save the previous value of the flags */ + rp->p_flags &= ~NO_MAP; + if (old_flags != 0 && rp->p_flags == 0) lock_ready(rp); + + return(OK); +} + + +/* The system call implemented in this file: + * m_type: SYS_EXEC + * + * The parameters for this system call are: + * m1_i1: PR_PROC_NR (process that did exec call) + * m1_i3: PR_TRACING (flag to indicate tracing is on/ off) + * m1_p1: PR_STACK_PTR (new stack pointer) + * m1_p2: PR_NAME_PTR (pointer to program name) + * m1_p3: PR_IP_PTR (new instruction pointer) + */ + +/*===========================================================================* + * do_exec * + *===========================================================================*/ +PUBLIC int do_exec(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_exec(). A process has done a successful EXEC. Patch it up. */ + + register struct proc *rp; + reg_t sp; /* new sp */ + phys_bytes phys_name; + char *np; +#define NLEN (sizeof(rp->p_name)-1) + + rp = proc_addr(m_ptr->PR_PROC_NR); + assert(isuserp(rp)); + if (m_ptr->PR_TRACING) cause_sig(m_ptr->PR_PROC_NR, SIGTRAP); + sp = (reg_t) m_ptr->PR_STACK_PTR; + rp->p_reg.sp = sp; /* set the stack pointer */ +#if (CHIP == M68000) + rp->p_splow = sp; /* set the stack pointer low water */ +#ifdef FPP + /* Initialize fpp for this process */ + fpp_new_state(rp); +#endif +#endif +#if (CHIP == INTEL) /* wipe extra LDT entries */ + kmemset(&rp->p_ldt[EXTRA_LDT_INDEX], 0, + (LDT_SIZE - EXTRA_LDT_INDEX) * sizeof(rp->p_ldt[0])); +#endif + rp->p_reg.pc = (reg_t) m_ptr->PR_IP_PTR; /* set pc */ + rp->p_flags &= ~RECEIVING; /* MM does not reply to EXEC call */ + if (rp->p_flags == 0) lock_ready(rp); + + /* Save command name for debugging, ps(1) output, etc. */ + phys_name = numap_local(m_ptr->m_source, (vir_bytes) m_ptr->PR_NAME_PTR, + (vir_bytes) NLEN); + if (phys_name != 0) { + phys_copy(phys_name, vir2phys(rp->p_name), (phys_bytes) NLEN); + for (np = rp->p_name; (*np & BYTE) >= ' '; np++) {} + *np = 0; + } + return(OK); +} + + +/* The system call implemented in this file: + * m_type: SYS_XIT + * + * The parameters for this system call are: + * m1_i1: PR_PROC_NR (slot number of exiting process) + * m1_i2: PR_PPROC_NR (slot number of parent process) + */ + + + +/*===========================================================================* + * do_xit * + *===========================================================================*/ +PUBLIC int do_xit(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_exit. A user process has exited (the MM sent the request). + */ + register struct proc *rp, *rc; + struct proc *np, *xp; + int exit_proc_nr; + + /* Get a pointer to the process that exited. */ + exit_proc_nr = m_ptr->PR_PROC_NR; + if (exit_proc_nr == SELF) exit_proc_nr = m_ptr->m_source; + if (! isokprocn(exit_proc_nr)) return(EINVAL); + rc = proc_addr(exit_proc_nr); + + /* If this is a user process and the MM passed in a valid parent process, + * accumulate the child times at the parent. + */ + if (isuserp(rc) && isokprocn(m_ptr->PR_PPROC_NR)) { + rp = proc_addr(m_ptr->PR_PPROC_NR); + lock(); + rp->child_utime += rc->user_time + rc->child_utime; + rp->child_stime += rc->sys_time + rc->child_stime; + unlock(); + } + + /* Now call the routine to clean up of the process table slot. This cancels + * outstanding timers, possibly removes the process from the message queues, + * and resets important process table fields. + */ + clear_proc(exit_proc_nr); + return(OK); /* tell MM that cleanup succeeded */ +} + + diff --git a/kernel/system/sigctl.c b/kernel/system/sigctl.c new file mode 100644 index 000000000..6aad28339 --- /dev/null +++ b/kernel/system/sigctl.c @@ -0,0 +1,227 @@ +/* The system call that is implemented in this file: + * SYS_SIGCTL # signal handling functionality + * + * The parameters and types for this system call are: + * SIG_REQUEST # request to perform (long) + * SIG_PROC # process to signal/ pending (int) + * SIG_CTXT_PTR # pointer to sigcontext structure (pointer) + * SIG_FLAGS # flags for S_SIGRETURN call (int) + * SIG_MAP # bit map with pending signals (long) + * SIG_NUMBER # signal number to send to process (int) + * + * Supported request types are in the parameter SIG_REQUEST: + * S_GETSIG # get a pending kernel signal + * S_ENDSIG # signal has been processed + * S_SENDSIG # deliver a POSIX-style signal + * S_SIGRETURN # return from a POSIX-style signal + * S_KILL # send a signal to a process + */ + +#include "../kernel.h" +#include "../system.h" +#include <signal.h> +#include <sys/sigcontext.h> +INIT_ASSERT + +/*===========================================================================* + * do_sigctl * + *===========================================================================*/ +PUBLIC int do_sigctl(m_ptr) +message *m_ptr; /* pointer to request message */ +{ + /* Only the MM and FS are allowed to use signal control operations. */ + if (m_ptr->m_source != MM_PROC_NR && m_ptr->m_source != FS_PROC_NR) + return(EPERM); + + /* Now see what request we got. The supported requests are S_GETSIG, + * S_ENDSIG, S_SENDSIG, S_SIGRETURN, and S_KILL. Unsupported requests + * result in an EINVAL error. + */ + switch(m_ptr->SIG_REQUEST) { + + /* MM is ready to accept signals and repeatedly does a system call to get + * one. Find a process with pending signals. If no signals are available, + * return NONE in the process number field. + */ + case S_GETSIG: { + + register struct proc *rp; + + /* Find the next process with pending signals. */ + for (rp = BEG_USER_ADDR; rp < END_PROC_ADDR; rp++) { + if (rp->p_flags & PENDING) { + m_ptr->SIG_PROC = proc_number(rp); + m_ptr->SIG_MAP = rp->p_pending; + sigemptyset(&rp->p_pending); /* ball is in MM's court */ + rp->p_flags &= ~PENDING; /* blocked by SIG_PENDING */ + return(OK); + } + } + + /* No process with pending signals was found. */ + m_ptr->SIG_PROC = NONE; + return(OK); + } + + /* Finish up after a KSIG-type signal, caused by a SYS_KILL message or a + * call to cause_sig by a task + */ + case S_ENDSIG: { + + register struct proc *rp; + + rp = proc_addr(m_ptr->SIG_PROC); + if (isemptyp(rp)) return(EINVAL); /* process already dead? */ + assert(isuserp(rp)); + + /* MM has finished one KSIG. Perhaps process is ready now? */ + if (rp->p_pendcount != 0 && --rp->p_pendcount == 0 + && (rp->p_flags &= ~SIG_PENDING) == 0) + lock_ready(rp); + return(OK); + } + + /* Handle sys_sendsig, POSIX-style signal handling. + */ + case S_SENDSIG: { + + struct sigmsg smsg; + register struct proc *rp; + phys_bytes src_phys, dst_phys; + struct sigcontext sc, *scp; + struct sigframe fr, *frp; + + rp = proc_addr(m_ptr->SIG_PROC); + assert(isuserp(rp)); + + /* Get the sigmsg structure into our address space. */ + src_phys = umap_local(proc_addr(MM_PROC_NR), D, (vir_bytes) + m_ptr->SIG_CTXT_PTR, (vir_bytes) sizeof(struct sigmsg)); + assert(src_phys != 0); + phys_copy(src_phys,vir2phys(&smsg),(phys_bytes) sizeof(struct sigmsg)); + + /* Compute the user stack pointer where sigcontext will be stored. */ + scp = (struct sigcontext *) smsg.sm_stkptr - 1; + + /* Copy the registers to the sigcontext structure. */ + kmemcpy(&sc.sc_regs, &rp->p_reg, sizeof(struct sigregs)); + + /* Finish the sigcontext initialization. */ + sc.sc_flags = SC_SIGCONTEXT; + sc.sc_mask = smsg.sm_mask; + + /* Copy the sigcontext structure to the user's stack. */ + dst_phys = umap_local(rp, D, (vir_bytes) scp, + (vir_bytes) sizeof(struct sigcontext)); + if (dst_phys == 0) return(EFAULT); + phys_copy(vir2phys(&sc), dst_phys, + (phys_bytes) sizeof(struct sigcontext)); + + /* Initialize the sigframe structure. */ + frp = (struct sigframe *) scp - 1; + fr.sf_scpcopy = scp; + fr.sf_retadr2= (void (*)()) rp->p_reg.pc; + fr.sf_fp = rp->p_reg.fp; + rp->p_reg.fp = (reg_t) &frp->sf_fp; + fr.sf_scp = scp; + fr.sf_code = 0; /* XXX - should be used for type of FP exception */ + fr.sf_signo = smsg.sm_signo; + fr.sf_retadr = (void (*)()) smsg.sm_sigreturn; + + /* Copy the sigframe structure to the user's stack. */ + dst_phys = umap_local(rp, D, (vir_bytes) frp, + (vir_bytes) sizeof(struct sigframe)); + if (dst_phys == 0) return(EFAULT); + phys_copy(vir2phys(&fr), dst_phys, + (phys_bytes) sizeof(struct sigframe)); + + /* Reset user registers to execute the signal handler. */ + rp->p_reg.sp = (reg_t) frp; + rp->p_reg.pc = (reg_t) smsg.sm_sighandler; + + return(OK); + } + + /* POSIX style signals require sys_sigreturn to put things in order before + * the signalled process can resume execution + */ + case S_SIGRETURN: { + + struct sigcontext sc; + register struct proc *rp; + phys_bytes src_phys; + + rp = proc_addr(m_ptr->SIG_PROC); + if (! isuserp(rp)) { + kprintf("S_SIGRETURN: message source: %d; ", m_ptr->m_source); + kprintf("got non-user process rp: %d\n", rp->p_nr); + } + assert(isuserp(rp)); + + /* Copy in the sigcontext structure. */ + src_phys = umap_local(rp, D, (vir_bytes) m_ptr->SIG_CTXT_PTR, + (vir_bytes) sizeof(struct sigcontext)); + if (src_phys == 0) return(EFAULT); + phys_copy(src_phys, vir2phys(&sc), + (phys_bytes) sizeof(struct sigcontext)); + + /* Make sure that this is not just a jmp_buf. */ + if ((sc.sc_flags & SC_SIGCONTEXT) == 0) return(EINVAL); + + /* Fix up only certain key registers if the compiler doesn't use + * register variables within functions containing setjmp. + */ + if (sc.sc_flags & SC_NOREGLOCALS) { + rp->p_reg.retreg = sc.sc_retreg; + rp->p_reg.fp = sc.sc_fp; + rp->p_reg.pc = sc.sc_pc; + rp->p_reg.sp = sc.sc_sp; + return(OK); + } + sc.sc_psw = rp->p_reg.psw; + +#if (CHIP == INTEL) + /* Don't panic kernel if user gave bad selectors. */ + sc.sc_cs = rp->p_reg.cs; + sc.sc_ds = rp->p_reg.ds; + sc.sc_es = rp->p_reg.es; +#if _WORD_SIZE == 4 + sc.sc_fs = rp->p_reg.fs; + sc.sc_gs = rp->p_reg.gs; +#endif +#endif + + /* Restore the registers. */ + kmemcpy(&rp->p_reg, (char *)&sc.sc_regs, sizeof(struct sigregs)); + + return(OK); + } + + /* Handle sys_kill(). Cause a signal to be sent to a process via MM. + * Note that this has nothing to do with the kill(2) system call, this + * is how the FS (and possibly other servers) get access to cause_sig. + */ + case S_KILL: { + cause_sig(m_ptr->SIG_PROC, m_ptr->SIG_NUMBER); + return(OK); + } + + default: + return(EINVAL); + } +} + + + +PUBLIC int do_kill(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_kill(). Cause a signal to be sent to a process via MM. + * Note that this has nothing to do with the kill (2) system call, this + * is how the FS (and possibly other servers) get access to cause_sig. + */ + cause_sig(m_ptr->SIG_PROC, m_ptr->SIG_NUMBER); + return(OK); +} + + diff --git a/kernel/system/srvrctl.c b/kernel/system/srvrctl.c new file mode 100644 index 000000000..7ec27283e --- /dev/null +++ b/kernel/system/srvrctl.c @@ -0,0 +1,241 @@ + +/* The system call implemented in this file: + * m_type: SYS_EXIT + * + * The parameters for this system call are: + * m1_i1: EXIT_STATUS (exit status, 0 if normal exit) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +#include "../kernel.h" +#include "../system.h" +#include "../protect.h" +#include <sys/svrctl.h> +#include "../sendmask.h" + +/*===========================================================================* + * do_exit * + *===========================================================================*/ +PUBLIC int do_exit(m_ptr) +message *m_ptr; /* pointer to request message */ +{ +/* Handle sys_exit. A server or driver wants to exit. This may happen + * on a panic, but also is done when MINIX is shutdown. + */ + register struct proc *rp; + int proc_nr = m_ptr->m_source; /* can only exit own process */ + + if (m_ptr->EXIT_STATUS != 0) { + kprintf("WARNING: system process %d exited with an error.\n", proc_nr ); + } + + /* Now call the routine to clean up of the process table slot. This cancels + * outstanding timers, possibly removes the process from the message queues, + * and reset important process table fields. + */ + clear_proc(proc_nr); + + /* If the shutdown sequence is active, see if it was awaiting the shutdown + * of this system service. If so, directly continue the stop sequence. + */ + if (shutting_down && shutdown_process == proc_addr(proc_nr)) { + stop_sequence(&shutdown_timer); + } + return(EDONTREPLY); /* no reply is sent */ +} + + + +/* The system call implemented in this file: + * m_type: SYS_SVRCTL + * + * The parameters for this system call are: + * m2_i1: CTL_PROC_NR (process number of caller) + * m2_i2: CTL_REQUEST (request type) + * m2_i3: CTL_MM_PRIV (privilege) + * m2_l1: CTL_SEND_MASK (new send mask to be installed) + * m2_l2: CTL_PROC_TYPE (new process type) + * m2_p1: CTL_ARG_PTR (argument pointer) + */ + + +/*===========================================================================* + * do_svrctl * + *===========================================================================*/ +PUBLIC int do_svrctl(m_ptr) +message *m_ptr; /* pointer to request message */ +{ + register struct proc *rp; + int proc_nr, priv; + int request; + vir_bytes argp; + + /* Extract message parameters. */ + proc_nr = m_ptr->CTL_PROC_NR; + if (proc_nr == SELF) proc_nr = m_ptr->m_source; + if (! isokprocn(proc_nr)) return(EINVAL); + + request = m_ptr->CTL_REQUEST; + priv = m_ptr->CTL_MM_PRIV; + argp = (vir_bytes) m_ptr->CTL_ARG_PTR; + rp = proc_addr(proc_nr); + + /* Check if the MM privileges are super user. */ + if (!priv || !isuserp(rp)) + return(EPERM); + + /* See what is requested and handle the request. */ + switch (request) { + case SYSSIGNON: { + /* Make this process a server. The system processes should be able + * to communicate with this new server, so update their send masks + * as well. + */ + /* fall through */ + } + case SYSSENDMASK: { + rp->p_type = P_SERVER; + rp->p_sendmask = ALLOW_ALL_MASK; + send_mask_allow(proc_addr(RTL8139)->p_sendmask, proc_nr); + send_mask_allow(proc_addr(MM_PROC_NR)->p_sendmask, proc_nr); + send_mask_allow(proc_addr(FS_PROC_NR)->p_sendmask, proc_nr); + send_mask_allow(proc_addr(IS_PROC_NR)->p_sendmask, proc_nr); + send_mask_allow(proc_addr(CLOCK)->p_sendmask, proc_nr); + send_mask_allow(proc_addr(SYSTASK)->p_sendmask, proc_nr); + return(OK); + } + default: + return(EINVAL); + } +} + +/* The system call implemented in this file: + * m_type: SYS_MEMCTL + * + * The parameters for this system call are: + * m4_l3: SEG_PHYS (physical base address) + * m4_l4: SEG_SIZE (size of segment) + * m4_l1: SEG_SELECT (return segment selector here) + * m4_l2: SEG_OFFSET (return offset within segment here) + * m4_l5: SEG_INDEX (return index into remote memory map here) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + + +/*===========================================================================* + * do_phys2seg * + *===========================================================================*/ +PUBLIC int do_phys2seg(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Return a segment selector and offset that can be used to reach a physical + * address, for use by a driver doing memory I/O in the A0000 - DFFFF range. + */ + u16_t selector; + vir_bytes offset; + register struct proc *rp; + phys_bytes phys = (phys_bytes) m_ptr->SEG_PHYS; + vir_bytes size = (vir_bytes) m_ptr->SEG_SIZE; + int result; + + kprintf("Using Experimental LDT selector for video memory\n", NO_ARG); + + if (!protected_mode) { + selector = phys / HCLICK_SIZE; + offset = phys % HCLICK_SIZE; + result = OK; + } else { + /* Check if the segment size can be recorded in bytes, that is, check + * if descriptor's limit field can delimited the allowed memory region + * precisely. This works up to 1MB. If the size is larger, 4K pages + * instead of bytes are used. + */ + if (size < BYTE_GRAN_MAX) { + rp = proc_addr(m_ptr->m_source); + init_dataseg(&rp->p_ldt[EXTRA_LDT_INDEX], phys, size, + USER_PRIVILEGE); + selector = (EXTRA_LDT_INDEX * 0x08) | (1 * 0x04) | USER_PRIVILEGE; + offset = 0; + result = OK; + } else { +#if ENABLE_USERPRIV && ENABLE_LOOSELDT + rp = proc_addr(m_ptr->m_source); + init_dataseg(&rp->p_ldt[EXTRA_LDT_INDEX], phys & ~0xFFFF, 0, + USER_PRIVILEGE); + selector = (EXTRA_LDT_INDEX * 0x08) | (1 * 0x04) | USER_PRIVILEGE; + offset = phys & 0xFFFF; + result = OK; +#else + result = E2BIG; /* allow settings only */ +#endif + } + } + + m_ptr->SEG_SELECT = selector; + m_ptr->SEG_OFFSET = offset; + return(result); +} + + +/* The system call implemented in this file: + * m_type: SYS_IOPENABLE + * + * The parameters for this system call are: + * m2_i2: PROC_NR (process to give I/O Protection Level bits) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +/*===========================================================================* + * do_iopenable * + *===========================================================================*/ +PUBLIC int do_iopenable(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +#if ENABLE_USERPRIV && ENABLE_USERIOPL + enable_iop(proc_addr(m_ptr->PROC_NR)); + return(OK); +#else + return(EPERM); +#endif +} + + +/* The system call implemented in this file: + * m_type: SYS_KMALLOC + * + * The parameters for this system call are: + * m4_l2: MEM_CHUNK_SIZE (request a buffer of this size) + * m4_l1: MEM_CHUNK_BASE (return physical address on success) + * + * Author: + * Jorrit N. Herder <jnherder@cs.vu.nl> + */ + +/*===========================================================================* + * do_kmalloc * + *===========================================================================*/ +PUBLIC int do_kmalloc(m_ptr) +register message *m_ptr; /* pointer to request message */ +{ +/* Request a (DMA) buffer to be allocated in one of the memory chunks. */ + phys_clicks tot_clicks; + struct memory *memp; + + tot_clicks = (m_ptr->MEM_CHUNK_SIZE + CLICK_SIZE-1) >> CLICK_SHIFT; + memp = &mem[NR_MEMS]; + while ((--memp)->size < tot_clicks) { + if (memp == mem) { + return(ENOMEM); + } + } + memp->size -= tot_clicks; + m_ptr->MEM_CHUNK_BASE = (memp->base + memp->size) << CLICK_SHIFT; + return(OK); +} + diff --git a/kernel/system/tracing.c b/kernel/system/tracing.c new file mode 100644 index 000000000..d33f227ba --- /dev/null +++ b/kernel/system/tracing.c @@ -0,0 +1,142 @@ +/* The system call implemented in this file: + * m_type: SYS_TRACE + * + * The parameters for this system call are: + * m2_i1: CTL_PROC_NR process that is traced + * m2_i2: CTL_REQUEST trace request + * m2_l1: CTL_ADDRESS address at traced process' space + * m2_l2: CTL_DATA data to be written or returned here + */ + +#include "../kernel.h" +#include "../system.h" +#include <sys/ptrace.h> + +#if ENABLE_K_TRACING /* only include code if tracing is enabled */ + +/*==========================================================================* + * do_trace * + *==========================================================================*/ +#define TR_VLSIZE ((vir_bytes) sizeof(long)) + +PUBLIC int do_trace(m_ptr) +register message *m_ptr; +{ +/* Handle the debugging commands supported by the ptrace system call + * The commands are: + * T_STOP stop the process + * T_OK enable tracing by parent for this process + * T_GETINS return value from instruction space + * T_GETDATA return value from data space + * T_GETUSER return value from user process table + * T_SETINS set value from instruction space + * T_SETDATA set value from data space + * T_SETUSER set value in user process table + * T_RESUME resume execution + * T_EXIT exit + * T_STEP set trace bit + * + * The T_OK and T_EXIT commands are handled completely by the memory manager, + * all others come here. + */ + + register struct proc *rp; + phys_bytes src, dst; + vir_bytes tr_addr = (vir_bytes) m_ptr->CTL_ADDRESS; + long tr_data = m_ptr->CTL_DATA; + int tr_request = m_ptr->CTL_REQUEST; + int tr_proc_nr = m_ptr->CTL_PROC_NR; + int i; + + rp = proc_addr(tr_proc_nr); + if (isemptyp(rp)) return(EIO); + switch (tr_request) { + case T_STOP: /* stop process */ + if (rp->p_flags == 0) lock_unready(rp); + rp->p_flags |= P_STOP; + rp->p_reg.psw &= ~TRACEBIT; /* clear trace bit */ + return(OK); + + case T_GETINS: /* return value from instruction space */ + if (rp->p_memmap[T].mem_len != 0) { + if ((src = umap_local(rp, T, tr_addr, TR_VLSIZE)) == 0) return(EIO); + phys_copy(src, vir2phys(&tr_data), (phys_bytes) sizeof(long)); + break; + } + /* Text space is actually data space - fall through. */ + + case T_GETDATA: /* return value from data space */ + if ((src = umap_local(rp, D, tr_addr, TR_VLSIZE)) == 0) return(EIO); + phys_copy(src, vir2phys(&tr_data), (phys_bytes) sizeof(long)); + break; + + case T_GETUSER: /* return value from process table */ + if ((tr_addr & (sizeof(long) - 1)) != 0 || + tr_addr > sizeof(struct proc) - sizeof(long)) + return(EIO); + tr_data = *(long *) ((char *) rp + (int) tr_addr); + break; + + case T_SETINS: /* set value in instruction space */ + if (rp->p_memmap[T].mem_len != 0) { + if ((dst = umap_local(rp, T, tr_addr, TR_VLSIZE)) == 0) return(EIO); + phys_copy(vir2phys(&tr_data), dst, (phys_bytes) sizeof(long)); + tr_data = 0; + break; + } + /* Text space is actually data space - fall through. */ + + case T_SETDATA: /* set value in data space */ + if ((dst = umap_local(rp, D, tr_addr, TR_VLSIZE)) == 0) return(EIO); + phys_copy(vir2phys(&tr_data), dst, (phys_bytes) sizeof(long)); + tr_data = 0; + break; + + case T_SETUSER: /* set value in process table */ + if ((tr_addr & (sizeof(reg_t) - 1)) != 0 || + tr_addr > sizeof(struct stackframe_s) - sizeof(reg_t)) + return(EIO); + i = (int) tr_addr; +#if (CHIP == INTEL) + /* Altering segment registers might crash the kernel when it + * tries to load them prior to restarting a process, so do + * not allow it. + */ + if (i == (int) &((struct proc *) 0)->p_reg.cs || + i == (int) &((struct proc *) 0)->p_reg.ds || + i == (int) &((struct proc *) 0)->p_reg.es || +#if _WORD_SIZE == 4 + i == (int) &((struct proc *) 0)->p_reg.gs || + i == (int) &((struct proc *) 0)->p_reg.fs || +#endif + i == (int) &((struct proc *) 0)->p_reg.ss) + return(EIO); +#endif + if (i == (int) &((struct proc *) 0)->p_reg.psw) + /* only selected bits are changeable */ + SETPSW(rp, tr_data); + else + *(reg_t *) ((char *) &rp->p_reg + i) = (reg_t) tr_data; + tr_data = 0; + break; + + case T_RESUME: /* resume execution */ + rp->p_flags &= ~P_STOP; + if (rp->p_flags == 0) lock_ready(rp); + tr_data = 0; + break; + + case T_STEP: /* set trace bit */ + rp->p_reg.psw |= TRACEBIT; + rp->p_flags &= ~P_STOP; + if (rp->p_flags == 0) lock_ready(rp); + tr_data = 0; + break; + + default: + return(EIO); + } + return(OK); +} + +#endif /* ENABLE_K_TRACING */ diff --git a/kernel/table.c b/kernel/table.c new file mode 100755 index 000000000..662d5251e --- /dev/null +++ b/kernel/table.c @@ -0,0 +1,96 @@ +/* The object file of "table.c" contains most kernel data. Variables that + * are declared in the *.h files appear with EXTERN in front of them, as in + * + * EXTERN int x; + * + * Normally EXTERN is defined as extern, so when they are included in another + * file, no storage is allocated. If EXTERN were not present, but just say, + * + * int x; + * + * then including this file in several source files would cause 'x' to be + * declared several times. While some linkers accept this, others do not, + * so they are declared extern when included normally. However, it must be + * declared for real somewhere. That is done here, by redefining EXTERN as + * the null string, so that inclusion of all *.h files in table.c actually + * generates storage for them. + * + * Various variables could not be declared EXTERN, but are declared PUBLIC + * or PRIVATE. The reason for this is that extern variables cannot have a + * default initialization. If such variables are shared, they must also be + * declared in one of the *.h files without the initialization. Examples + * include 'tasktab' (this file) and 'idt'/'gdt' (protect.c). + * + * Changes: + * Nov 10, 2004 removed controller->driver mappings (Jorrit N. Herder) + * Oct 17, 2004 updated above and tasktab comments (Jorrit N. Herder) + * Aug 18, 2004 included p_type in tasktab (Jorrit N. Herder) + * May 01, 2004 included p_sendmask in tasktab (Jorrit N. Herder) + */ + +#define _TABLE + +#include "kernel.h" +#include "proc.h" +#include "sendmask.h" +#include <minix/com.h> +#include <ibm/int86.h> + +/* Define stack sizes for all tasks included in the system image. */ +#define NO_STACK 0 +#define SMALL_STACK (128 * sizeof(char *)) +#if (CHIP == INTEL) +#define IDLE_STACK ((3+3+4) * sizeof(char *)) /* 3 intr, 3 temps, 4 db */ +#else +#define IDLE_STACK SMALL_STACK +#endif +#define HARDWARE_STACK NO_STACK /* dummy task, uses kernel stack */ +#define SYS_STACK SMALL_STACK +#define CLOCK_STACK SMALL_STACK +#define RTL8139_STACK (2 * SMALL_STACK * ENABLE_RTL8139) + +/* Stack space for all the task stacks. Declared as (char *) to align it. */ +#define TOT_STACK_SPACE (IDLE_STACK + HARDWARE_STACK + CLOCK_STACK + SYS_STACK \ + + RTL8139_STACK ) +PUBLIC char *t_stack[TOT_STACK_SPACE / sizeof(char *)]; + + +/* The system image table lists all programs that are part of the boot image. + * The order of the entries here MUST agree with the order of the programs + * in the boot image and all kernel tasks must come first. + * Each entry provides the process number, type, scheduling priority, send + * mask, and a name for the process table. For kernel processes, the startup + * routine and stack size is also provided. + */ +PUBLIC struct system_image image[] = { +#if ENABLE_RTL8139 + { RTL8139, rtl8139_task, P_TASK, PPRI_TASK, RTL8139_STACK, RTL8139_SENDMASK, "RTL8139" }, +#endif + { IDLE, idle_task, P_IDLE, PPRI_IDLE, IDLE_STACK, IDLE_SENDMASK, "IDLE" }, + { CLOCK, clock_task, P_TASK, PPRI_TASK, CLOCK_STACK, CLOCK_SENDMASK, "CLOCK" }, + { SYSTASK, sys_task, P_TASK, PPRI_TASK, SYS_STACK, SYSTEM_SENDMASK, "SYS" }, + { HARDWARE, 0, P_TASK, PPRI_TASK, HARDWARE_STACK,HARDWARE_SENDMASK,"HARDWAR" }, + { MM_PROC_NR, 0, P_SERVER, PPRI_NORMAL, 0, MM_SENDMASK, "MM" }, + { FS_PROC_NR, 0, P_SERVER, PPRI_NORMAL, 0, FS_SENDMASK, "FS" }, + { IS_PROC_NR, 0, P_SYSTEM, PPRI_HIGHER, 0, IS_SENDMASK, "IS" }, + { TTY, 0, P_SYSTEM, PPRI_HIGHER, 0, TTY_SENDMASK, "TTY" }, + { MEMORY, 0, P_DRIVER, PPRI_HIGH, 0, MEM_SENDMASK, "MEMORY" }, +#if ENABLE_AT_WINI + { AT_WINI, 0, P_DRIVER, PPRI_HIGHER, 0, AT_SENDMASK, "AT_WINI" }, +#endif +#if ENABLE_FLOPPY + { FLOPPY, 0, P_DRIVER, PPRI_HIGH, 0, FLOPPY_SENDMASK, "FLOPPY" }, +#endif +#if ENABLE_PRINTER + { PRINTER, 0, P_DRIVER, PPRI_NORMAL, 0, PRN_SENDMASK, "PRINTER" }, +#endif + { INIT_PROC_NR, 0, P_USER, PPRI_USER, 0, INIT_SENDMASK, "INIT" }, +}; + +/* Verify the size of the system image table at compile time. If the number + * is not correct, the size of the 'dummy' array will be negative, causing + * a compile time error. Note that no space is allocated because 'dummy' is + * declared extern. + */ +extern int dummy[(IMAGE_SIZE==sizeof(image)/sizeof(struct system_image))?1:-1]; + diff --git a/kernel/type.h b/kernel/type.h new file mode 100755 index 000000000..ba009ad71 --- /dev/null +++ b/kernel/type.h @@ -0,0 +1,119 @@ +#ifndef TYPE_H +#define TYPE_H + +typedef _PROTOTYPE( void task_t, (void) ); +typedef _PROTOTYPE( int (*rdwt_t), (message *m_ptr) ); +typedef _PROTOTYPE( void (*watchdog_t), (void) ); + +/* This is used within the kernel to handle virtual copying. */ +struct vir_addr { + int proc_nr; + int segment; + vir_bytes offset; +}; + +/* Type accepted by kprintf(). This is a hack to accept both integers and + * char pointers in the same argument. + */ +typedef long karg_t; /* use largest type here */ + +typedef unsigned int notify_mask_t; /* bit mask for notifications */ +typedef unsigned long send_mask_t; /* bit mask for sender */ + +struct system_image { + int proc_nr; /* process number to use */ + task_t *initial_pc; /* start function for tasks */ + int type; /* type of process */ + int priority; /* scheduling priority */ + int stksize; /* stack size for tasks */ + send_mask_t sendmask; /* send mask protection */ + char name[PROC_NAME_LEN]; /* name in process table */ +}; + +struct memory { + phys_clicks base; /* start address of chunk */ + phys_clicks size; /* size of memory chunk */ +}; + +struct bios { + phys_bytes bios_addr; /* physical address at BIOS */ + size_t bios_length; /* size of value */ +}; + + +#if (CHIP == INTEL) +typedef u16_t port_t; +typedef U16_t Port_t; +typedef unsigned reg_t; /* machine register */ + +/* The stack frame layout is determined by the software, but for efficiency + * it is laid out so the assembly code to use it is as simple as possible. + * 80286 protected mode and all real modes use the same frame, built with + * 16-bit registers. Real mode lacks an automatic stack switch, so little + * is lost by using the 286 frame for it. The 386 frame differs only in + * having 32-bit registers and more segment registers. The same names are + * used for the larger registers to avoid differences in the code. + */ +struct stackframe_s { /* proc_ptr points here */ +#if _WORD_SIZE == 4 + u16_t gs; /* last item pushed by save */ + u16_t fs; /* ^ */ +#endif + u16_t es; /* | */ + u16_t ds; /* | */ + reg_t di; /* di through cx are not accessed in C */ + reg_t si; /* order is to match pusha/popa */ + reg_t fp; /* bp */ + reg_t st; /* hole for another copy of sp */ + reg_t bx; /* | */ + reg_t dx; /* | */ + reg_t cx; /* | */ + reg_t retreg; /* ax and above are all pushed by save */ + reg_t retadr; /* return address for assembly code save() */ + reg_t pc; /* ^ last item pushed by interrupt */ + reg_t cs; /* | */ + reg_t psw; /* | */ + reg_t sp; /* | */ + reg_t ss; /* these are pushed by CPU during interrupt */ +}; + +struct segdesc_s { /* segment descriptor for protected mode */ + u16_t limit_low; + u16_t base_low; + u8_t base_middle; + u8_t access; /* |P|DL|1|X|E|R|A| */ + u8_t granularity; /* |G|X|0|A|LIMT| */ + u8_t base_high; +}; + +typedef struct irq_hook { + struct irq_hook *next; + int (*handler)(struct irq_hook *); + int irq; + int id; +} irq_hook_t; + +typedef int (*irq_handler_t)(struct irq_hook *); + +/* The IRQ table is used to handle harware interrupts based on a policy set + * by a device driver. The policy is stored with a SYS_IRQCTL system call and + * used by a generic function to handle hardware interrupts in an appropriate + * way for the device. + */ +typedef unsigned long irq_policy_t; +struct irqtab { + irq_hook_t hook; /* its irq hook */ + irq_policy_t policy; /* bit mask for the policy */ + int proc_nr; /* process number to be notified */ + long port; /* port to be read or written */ + phys_bytes addr; /* absolute address to store or get value */ + long mask_val; /* mask for strobing or value to be written */ +}; + +#endif /* (CHIP == INTEL) */ + +#if (CHIP == M68000) +/* M68000 specific types go here. */ +#endif /* (CHIP == M68000) */ + +#endif /* TYPE_H */ diff --git a/lib/Makefile b/lib/Makefile new file mode 100755 index 000000000..354015101 --- /dev/null +++ b/lib/Makefile @@ -0,0 +1,203 @@ +# Makefile for the libraries + +# This makefile runs make in all the subdirectories of the src/lib tree. +# See ansi/Makefile for a further explanation. + +MAKE = exec make -$(MAKEFLAGS) + +usage: + @echo "Usage: make all" >&2 # Compile all library functions" >&2 + @echo " make install # Backup /usr/lib/*.a first!" >&2 + @echo " make clean # Delete .o and .a files" >&2 + @false + +all: + cd ansi && $(MAKE) + cd curses && $(MAKE) + cd dummy && $(MAKE) + cd editline && $(MAKE) + cd end && $(MAKE) + cd float && $(MAKE) + cd fphook && $(MAKE) + cd ip && $(MAKE) + cd libm2 && $(MAKE) + cd libp && $(MAKE) + cd liby && $(MAKE) + cd math && $(MAKE) + cd other && $(MAKE) + cd posix && $(MAKE) + test `arch` = i86 || { cd regex && $(MAKE); } + cd rts && $(MAKE) + cd stdio && $(MAKE) + cd socket && $(MAKE) + cd syscall && $(MAKE) + cd syslib && $(MAKE) + cd utils && $(MAKE) + cd timers && $(MAKE) + cd `arch` && $(MAKE) + +install: all + $(MAKE) install_`arch` + +# Installation rules for PC Minix. +LIB= /usr/lib + +install_i86: \ + $(LIB)/ncrtso.o \ + $(LIB)/nm2rtso.o \ + $(LIB)/nprtso.o \ + $(LIB)/libc.a \ + $(LIB)/libd.a \ + $(LIB)/libe.a \ + $(LIB)/libfp.a \ + $(LIB)/libm.a \ + $(LIB)/libm2.a \ + $(LIB)/libp.a \ + $(LIB)/end.a \ + $(LIB)/libsys.a \ + $(LIB)/libtimers.a \ + $(LIB)/utils.a \ + $(LIB)/libcurses.a \ + $(LIB)/libedit.a \ + $(LIB)/liby.a \ + +$(LIB)/ncrtso.o: ncrtso.o + install -c -o bin $? $@ + +$(LIB)/nm2rtso.o: nm2rtso.o + install -c -o bin $? $@ + +$(LIB)/nprtso.o: nprtso.o + install -c -o bin $? $@ + +$(LIB)/libc.a: libc.a + install -c -o bin $? $@ + +$(LIB)/libd.a: libd.a + install -c -o bin $? $@ + +$(LIB)/libe.a: libe.a + install -c -o bin $? $@ + +$(LIB)/libfp.a: libfp.a + install -c -o bin $? $@ + +$(LIB)/libm.a: libm.a + install -c -o bin $? $@ + +$(LIB)/libm2.a: libm2.a + install -c -o bin $? $@ + +$(LIB)/libp.a: libp.a + install -c -o bin $? $@ + +$(LIB)/end.a: end.a + install -c -o bin $? $@ + +$(LIB)/libsys.a: libsys.a + install -c -o bin $? $@ + +$(LIB)/libtimers.a: libtimers.a + install -c -o bin $? $@ + +$(LIB)/libutils.a: libutils.a + install -c -o bin $? $@ + +$(LIB)/libcurses.a: libcurses.a + install -c -o bin $? $@ + +$(LIB)/libedit.a: libedit.a + install -c -o bin $? $@ + +$(LIB)/liby.a: liby.a + install -c -o bin $? $@ + +# Installation rules for Minix-386. +LIB386= /usr/lib/i386 +LIB86= /usr/lib/i86 + +install_i386: \ + $(LIB386)/crtso.o \ + $(LIB386)/m2rtso.o \ + $(LIB386)/prtso.o \ + $(LIB386)/libc.a \ + $(LIB386)/libd.a \ + $(LIB386)/libe.a \ + $(LIB386)/libfp.a \ + $(LIB386)/libm.a \ + $(LIB386)/libm2.a \ + $(LIB386)/libp.a \ + $(LIB386)/libtimers.a \ + $(LIB386)/end.a \ + $(LIB386)/libsys.a \ + $(LIB386)/libtimers.a \ + $(LIB386)/libutils.a \ + $(LIB386)/libcurses.a \ + $(LIB386)/libedit.a \ + $(LIB386)/liby.a \ + $(LIB386)/libsocket.a \ +# $(LIB86)/libc.a \ +# $(LIB86)/end.a \ + +$(LIB386)/crtso.o: crtso.o + install -c -o bin $? $@ + +$(LIB386)/m2rtso.o: m2rtso.o + install -c -o bin $? $@ + +$(LIB386)/prtso.o: prtso.o + install -c -o bin $? $@ + +$(LIB386)/libc.a: libc.a + install -c -o bin $? $@ + +$(LIB386)/libd.a: libd.a + install -c -o bin $? $@ + +$(LIB386)/libe.a: libe.a + install -c -o bin $? $@ + +$(LIB386)/libfp.a: libfp.a + install -c -o bin $? $@ + +$(LIB386)/libm.a: libm.a + install -c -o bin $? $@ + +$(LIB386)/libm2.a: libm2.a + install -c -o bin $? $@ + +$(LIB386)/libp.a: libp.a + install -c -o bin $? $@ + +$(LIB386)/end.a: end.a + install -c -o bin $? $@ + +$(LIB386)/libsys.a: libsys.a + install -c -o bin $? $@ + +$(LIB386)/libtimers.a: libtimers.a + install -c -o bin $? $@ + +$(LIB386)/libutils.a: libutils.a + install -c -o bin $? $@ + +$(LIB386)/libcurses.a: libcurses.a + install -c -o bin $? $@ + +$(LIB386)/libedit.a: libedit.a + install -c -o bin $? $@ + +$(LIB386)/liby.a: liby.a + install -c -o bin $? $@ + +$(LIB386)/libsocket.a: libsocket.a + install -c -o bin $? $@ + +#$(LIB86)/libc.a: libc86.a +# install -c -o bin $? $@ +# +#$(LIB86)/end.a: end86.a +# install -c -o bin $? $@ + +clean: + find . -name '*.[oa]' -o -name '*.bak' | xargs rm diff --git a/lib/ansi/Makefile b/lib/ansi/Makefile new file mode 100755 index 000000000..ac1b8bb12 --- /dev/null +++ b/lib/ansi/Makefile @@ -0,0 +1,330 @@ +# Makefile for lib/ansi. + +# This Makefile compiles part of the C library, the functions required by the +# ANSI C standard. This Makefile, and those in the other subdirectories use +# a little known feature of make, the ability to refer to a file within a +# library. The construct 'libc.a(abs.o)' names the file 'abs.o' contained +# in 'libc.a'. So the rule +# +# libc.a(abs.o): abs.c +# cc -c abs.c +# aal cr libc.a abs.o +# rm abs.o +# +# compiles abs.c and installs the result abs.o in libc.a if abs.c is newer +# than the abs.o in the library. This Makefile does not work like this +# precisely, it first compiles all changed source files and than installs +# them all in one 'aal' command. + +# Many of the string functions in this directory are not used, because the +# have fast assembly implementations. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../libc.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(abort.o) \ + $(LIBRARY)(abs.o) \ + $(LIBRARY)(asctime.o) \ + $(LIBRARY)(assert.o) \ + $(LIBRARY)(atexit.o) \ + $(LIBRARY)(atof.o) \ + $(LIBRARY)(atoi.o) \ + $(LIBRARY)(atol.o) \ + $(LIBRARY)(bsearch.o) \ + $(LIBRARY)(calloc.o) \ + $(LIBRARY)(chartab.o) \ + $(LIBRARY)(clock.o) \ + $(LIBRARY)(ctime.o) \ + $(LIBRARY)(difftime.o) \ + $(LIBRARY)(div.o) \ + $(LIBRARY)(errlist.o) \ + $(LIBRARY)(exit.o) \ + $(LIBRARY)(ext_comp.o) \ + $(LIBRARY)(getenv.o) \ + $(LIBRARY)(gmtime.o) \ + $(LIBRARY)(isalnum.o) \ + $(LIBRARY)(isalpha.o) \ + $(LIBRARY)(isascii.o) \ + $(LIBRARY)(iscntrl.o) \ + $(LIBRARY)(isdigit.o) \ + $(LIBRARY)(isgraph.o) \ + $(LIBRARY)(islower.o) \ + $(LIBRARY)(isprint.o) \ + $(LIBRARY)(ispunct.o) \ + $(LIBRARY)(isspace.o) \ + $(LIBRARY)(isupper.o) \ + $(LIBRARY)(isxdigit.o) \ + $(LIBRARY)(labs.o) \ + $(LIBRARY)(ldiv.o) \ + $(LIBRARY)(localeconv.o) \ + $(LIBRARY)(localtime.o) \ + $(LIBRARY)(malloc.o) \ + $(LIBRARY)(mblen.o) \ + $(LIBRARY)(mbstowcs.o) \ + $(LIBRARY)(mbtowc.o) \ + $(LIBRARY)(misc.o) \ + $(LIBRARY)(mktime.o) \ + $(LIBRARY)(qsort.o) \ + $(LIBRARY)(raise.o) \ + $(LIBRARY)(rand.o) \ + $(LIBRARY)(setlocale.o) \ + $(LIBRARY)(sigmisc.o) \ + $(LIBRARY)(signal.o) \ + $(LIBRARY)(strcoll.o) \ + $(LIBRARY)(strcspn.o) \ + $(LIBRARY)(strerror.o) \ + $(LIBRARY)(strftime.o) \ + $(LIBRARY)(strpbrk.o) \ + $(LIBRARY)(strspn.o) \ + $(LIBRARY)(strstr.o) \ + $(LIBRARY)(strtok.o) \ + $(LIBRARY)(strtol.o) \ + $(LIBRARY)(strxfrm.o) \ + $(LIBRARY)(system.o) \ + $(LIBRARY)(tolower.o) \ + $(LIBRARY)(toupper.o) \ + $(LIBRARY)(tzset.o) \ + $(LIBRARY)(wcstombs.o) \ + $(LIBRARY)(wctomb.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(abort.o): abort.c + $(CC1) abort.c + +$(LIBRARY)(abs.o): abs.c + $(CC1) abs.c + +$(LIBRARY)(asctime.o): asctime.c + $(CC1) asctime.c + +$(LIBRARY)(assert.o): assert.c + $(CC1) assert.c + +$(LIBRARY)(atexit.o): atexit.c + $(CC1) atexit.c + +$(LIBRARY)(atof.o): atof.c + $(CC1) atof.c + +$(LIBRARY)(atoi.o): atoi.c + $(CC1) atoi.c + +$(LIBRARY)(atol.o): atol.c + $(CC1) atol.c + +$(LIBRARY)(bsearch.o): bsearch.c + $(CC1) bsearch.c + +$(LIBRARY)(calloc.o): calloc.c + $(CC1) calloc.c + +$(LIBRARY)(chartab.o): chartab.c + $(CC1) chartab.c + +$(LIBRARY)(clock.o): clock.c + $(CC1) clock.c + +$(LIBRARY)(ctime.o): ctime.c + $(CC1) ctime.c + +$(LIBRARY)(difftime.o): difftime.c + $(CC1) difftime.c + +$(LIBRARY)(div.o): div.c + $(CC1) div.c + +$(LIBRARY)(errlist.o): errlist.c + $(CC1) errlist.c + +$(LIBRARY)(exit.o): exit.c + $(CC1) exit.c + +$(LIBRARY)(ext_comp.o): ext_comp.c + $(CC1) ext_comp.c + +$(LIBRARY)(getenv.o): getenv.c + $(CC1) getenv.c + +$(LIBRARY)(gmtime.o): gmtime.c + $(CC1) gmtime.c + +$(LIBRARY)(isalnum.o): isalnum.c + $(CC1) isalnum.c + +$(LIBRARY)(isalpha.o): isalpha.c + $(CC1) isalpha.c + +$(LIBRARY)(isascii.o): isascii.c + $(CC1) isascii.c + +$(LIBRARY)(iscntrl.o): iscntrl.c + $(CC1) iscntrl.c + +$(LIBRARY)(isdigit.o): isdigit.c + $(CC1) isdigit.c + +$(LIBRARY)(isgraph.o): isgraph.c + $(CC1) isgraph.c + +$(LIBRARY)(islower.o): islower.c + $(CC1) islower.c + +$(LIBRARY)(isprint.o): isprint.c + $(CC1) isprint.c + +$(LIBRARY)(ispunct.o): ispunct.c + $(CC1) ispunct.c + +$(LIBRARY)(isspace.o): isspace.c + $(CC1) isspace.c + +$(LIBRARY)(isupper.o): isupper.c + $(CC1) isupper.c + +$(LIBRARY)(isxdigit.o): isxdigit.c + $(CC1) isxdigit.c + +$(LIBRARY)(labs.o): labs.c + $(CC1) labs.c + +$(LIBRARY)(ldiv.o): ldiv.c + $(CC1) ldiv.c + +$(LIBRARY)(localeconv.o): localeconv.c + $(CC1) localeconv.c + +$(LIBRARY)(localtime.o): localtime.c + $(CC1) localtime.c + +$(LIBRARY)(malloc.o): malloc.c + $(CC1) malloc.c + +$(LIBRARY)(mblen.o): mblen.c + $(CC1) mblen.c + +$(LIBRARY)(mbstowcs.o): mbstowcs.c + $(CC1) mbstowcs.c + +$(LIBRARY)(mbtowc.o): mbtowc.c + $(CC1) mbtowc.c + +$(LIBRARY)(memchr.o): memchr.c + $(CC1) memchr.c + +$(LIBRARY)(memcmp.o): memcmp.c + $(CC1) memcmp.c + +$(LIBRARY)(memcpy.o): memcpy.c + $(CC1) memcpy.c + +$(LIBRARY)(memmove.o): memmove.c + $(CC1) memmove.c + +$(LIBRARY)(memset.o): memset.c + $(CC1) memset.c + +$(LIBRARY)(misc.o): misc.c + $(CC1) misc.c + +$(LIBRARY)(mktime.o): mktime.c + $(CC1) mktime.c + +$(LIBRARY)(qsort.o): qsort.c + $(CC1) qsort.c + +$(LIBRARY)(raise.o): raise.c + $(CC1) raise.c + +$(LIBRARY)(rand.o): rand.c + $(CC1) rand.c + +$(LIBRARY)(setlocale.o): setlocale.c + $(CC1) setlocale.c + +$(LIBRARY)(sigmisc.o): sigmisc.c + $(CC1) sigmisc.c + +$(LIBRARY)(signal.o): signal.c + $(CC1) signal.c + +$(LIBRARY)(strcat.o): strcat.c + $(CC1) strcat.c + +$(LIBRARY)(strchr.o): strchr.c + $(CC1) strchr.c + +$(LIBRARY)(strcmp.o): strcmp.c + $(CC1) strcmp.c + +$(LIBRARY)(strcoll.o): strcoll.c + $(CC1) strcoll.c + +$(LIBRARY)(strcpy.o): strcpy.c + $(CC1) strcpy.c + +$(LIBRARY)(strcspn.o): strcspn.c + $(CC1) strcspn.c + +$(LIBRARY)(strerror.o): strerror.c + $(CC1) strerror.c + +$(LIBRARY)(strftime.o): strftime.c + $(CC1) strftime.c + +$(LIBRARY)(strlen.o): strlen.c + $(CC1) strlen.c + +$(LIBRARY)(strncat.o): strncat.c + $(CC1) strncat.c + +$(LIBRARY)(strncmp.o): strncmp.c + $(CC1) strncmp.c + +$(LIBRARY)(strncpy.o): strncpy.c + $(CC1) strncpy.c + +$(LIBRARY)(strpbrk.o): strpbrk.c + $(CC1) strpbrk.c + +$(LIBRARY)(strrchr.o): strrchr.c + $(CC1) strrchr.c + +$(LIBRARY)(strspn.o): strspn.c + $(CC1) strspn.c + +$(LIBRARY)(strstr.o): strstr.c + $(CC1) strstr.c + +$(LIBRARY)(strtok.o): strtok.c + $(CC1) strtok.c + +$(LIBRARY)(strtol.o): strtol.c + $(CC1) strtol.c + +$(LIBRARY)(strxfrm.o): strxfrm.c + $(CC1) strxfrm.c + +$(LIBRARY)(system.o): system.c + $(CC1) system.c + +$(LIBRARY)(tolower.o): tolower.c + $(CC1) tolower.c + +$(LIBRARY)(toupper.o): toupper.c + $(CC1) toupper.c + +$(LIBRARY)(tzset.o): tzset.c + $(CC1) tzset.c + +$(LIBRARY)(wcstombs.o): wcstombs.c + $(CC1) wcstombs.c + +$(LIBRARY)(wctomb.o): wctomb.c + $(CC1) wctomb.c diff --git a/lib/ansi/abort.c b/lib/ansi/abort.c new file mode 100755 index 000000000..4ace48a6c --- /dev/null +++ b/lib/ansi/abort.c @@ -0,0 +1,21 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#if defined(_POSIX_SOURCE) +#include <sys/types.h> +#endif +#include <signal.h> +#include <stdlib.h> + +extern void (*_clean)(void); + +void +abort(void) +{ + if (_clean) _clean(); /* flush all output files */ + raise(SIGABRT); +} + diff --git a/lib/ansi/abs.c b/lib/ansi/abs.c new file mode 100755 index 000000000..63b893c3d --- /dev/null +++ b/lib/ansi/abs.c @@ -0,0 +1,13 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> + +int +abs(register int i) +{ + return i >= 0 ? i : -i; +} diff --git a/lib/ansi/asctime.c b/lib/ansi/asctime.c new file mode 100755 index 000000000..ed0757816 --- /dev/null +++ b/lib/ansi/asctime.c @@ -0,0 +1,60 @@ +/* + * asctime - print a date + */ +/* $Header$ */ + +#include <string.h> +#include <time.h> +#include "loc_time.h" + +#define DATE_STR "??? ??? ?? ??:??:?? ????\n" + +static char * +two_digits(register char *pb, int i, int nospace) +{ + *pb = (i / 10) % 10 + '0'; + if (!nospace && *pb == '0') *pb = ' '; + pb++; + *pb++ = (i % 10) + '0'; + return ++pb; +} + +static char * +four_digits(register char *pb, int i) +{ + i %= 10000; + *pb++ = (i / 1000) + '0'; + i %= 1000; + *pb++ = (i / 100) + '0'; + i %= 100; + *pb++ = (i / 10) + '0'; + *pb++ = (i % 10) + '0'; + return ++pb; +} + +char *asctime(const struct tm *timeptr) +{ + static char buf[26]; + register char *pb = buf; + register const char *ps; + register int n; + + strcpy(pb, DATE_STR); + ps = _days[timeptr->tm_wday]; + n = ABB_LEN; + while(--n >= 0) *pb++ = *ps++; + pb++; + ps = _months[timeptr->tm_mon]; + n = ABB_LEN; + while(--n >= 0) *pb++ = *ps++; + pb++; + pb = two_digits( + two_digits( + two_digits(two_digits(pb, timeptr->tm_mday, 0) + , timeptr->tm_hour, 1) + , timeptr->tm_min, 1) + , timeptr->tm_sec, 1); + + four_digits(pb, timeptr->tm_year + 1900); + return buf; +} diff --git a/lib/ansi/assert.c b/lib/ansi/assert.c new file mode 100755 index 000000000..3dab83b7e --- /dev/null +++ b/lib/ansi/assert.c @@ -0,0 +1,14 @@ +/* + * assert.c - diagnostics + */ +/* $Header$ */ + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> + +void __bad_assertion(const char *mess) { + + fputs(mess, stderr); + abort(); +} diff --git a/lib/ansi/atexit.c b/lib/ansi/atexit.c new file mode 100755 index 000000000..3f10bdbbe --- /dev/null +++ b/lib/ansi/atexit.c @@ -0,0 +1,17 @@ +/* $Header$ */ + +#include <stdlib.h> + +#define NEXITS 32 + +extern void (*__functab[NEXITS])(void); +extern int __funccnt; + +int +atexit(void (*func)(void)) +{ + if (__funccnt >= NEXITS) + return 1; + __functab[__funccnt++] = func; + return 0; +} diff --git a/lib/ansi/atof.c b/lib/ansi/atof.c new file mode 100755 index 000000000..e527b5354 --- /dev/null +++ b/lib/ansi/atof.c @@ -0,0 +1,19 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> +#include <errno.h> + +double +atof(const char *nptr) +{ + double d; + int e = errno; + + d = strtod(nptr, (char **) NULL); + errno = e; + return d; +} diff --git a/lib/ansi/atoi.c b/lib/ansi/atoi.c new file mode 100755 index 000000000..ec829c704 --- /dev/null +++ b/lib/ansi/atoi.c @@ -0,0 +1,14 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <ctype.h> +#include <stdlib.h> + +int +atoi(register const char *nptr) +{ + return strtol(nptr, (char **) NULL, 10); +} diff --git a/lib/ansi/atol.c b/lib/ansi/atol.c new file mode 100755 index 000000000..63235d9fc --- /dev/null +++ b/lib/ansi/atol.c @@ -0,0 +1,30 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <ctype.h> +#include <stdlib.h> + +/* We do not use strtol here for backwards compatibility in behaviour on + overflow. +*/ +long +atol(register const char *nptr) +{ + long total = 0; + int minus = 0; + + while (isspace(*nptr)) nptr++; + if (*nptr == '+') nptr++; + else if (*nptr == '-') { + minus = 1; + nptr++; + } + while (isdigit(*nptr)) { + total *= 10; + total += (*nptr++ - '0'); + } + return minus ? -total : total; +} diff --git a/lib/ansi/bsearch.c b/lib/ansi/bsearch.c new file mode 100755 index 000000000..88f35216e --- /dev/null +++ b/lib/ansi/bsearch.c @@ -0,0 +1,28 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> + +void * +bsearch(register const void *key, register const void *base, + register size_t nmemb, register size_t size, + int (*compar)(const void *, const void *)) +{ + register const void *mid_point; + register int cmp; + + while (nmemb > 0) { + mid_point = (char *)base + size * (nmemb >> 1); + if ((cmp = (*compar)(key, mid_point)) == 0) + return (void *)mid_point; + if (cmp >= 0) { + base = (char *)mid_point + size; + nmemb = (nmemb - 1) >> 1; + } else + nmemb >>= 1; + } + return (void *)NULL; +} diff --git a/lib/ansi/calloc.c b/lib/ansi/calloc.c new file mode 100755 index 000000000..cccd79ab7 --- /dev/null +++ b/lib/ansi/calloc.c @@ -0,0 +1,19 @@ +/* $Header$ */ +#include <stdlib.h> + +#define ALIGN(x) (((x) + (sizeof(size_t) - 1)) & ~(sizeof(size_t) - 1)) + +void * +calloc(size_t nelem, size_t elsize) +{ + register char *p; + register size_t *q; + size_t size = ALIGN(nelem * elsize); + + p = malloc(size); + if (p == NULL) return NULL; + q = (size_t *) (p + size); + while ((char *) q > p) *--q = 0; + return p; +} + diff --git a/lib/ansi/chartab.c b/lib/ansi/chartab.c new file mode 100755 index 000000000..334e04107 --- /dev/null +++ b/lib/ansi/chartab.c @@ -0,0 +1,261 @@ +#include <ctype.h> + +char __ctype[] = { +0, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C|_S, +_C|_S, +_C|_S, +_C|_S, +_C|_S, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_C, +_S, +_P, +_P, +_P, +_P, +_P, +_P, +_P, +_P, +_P, +_P, +_P, +_P, +_P, +_P, +_P, +_N, +_N, +_N, +_N, +_N, +_N, +_N, +_N, +_N, +_N, +_P, +_P, +_P, +_P, +_P, +_P, +_P, +_U|_X, +_U|_X, +_U|_X, +_U|_X, +_U|_X, +_U|_X, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_U, +_P, +_P, +_P, +_P, +_P, +_P, +_L|_X, +_L|_X, +_L|_X, +_L|_X, +_L|_X, +_L|_X, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_L, +_P, +_P, +_P, +_P, +_C, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +0, +}; diff --git a/lib/ansi/clock.c b/lib/ansi/clock.c new file mode 100755 index 000000000..049d51259 --- /dev/null +++ b/lib/ansi/clock.c @@ -0,0 +1,15 @@ +/* + * clock - determine the processor time used + */ + +#define times _times +#include <time.h> +#include <sys/times.h> + +clock_t clock(void) +{ + struct tms tms; + + times(&tms); + return tms.tms_utime; +} diff --git a/lib/ansi/ctime.c b/lib/ansi/ctime.c new file mode 100755 index 000000000..9f01f50ce --- /dev/null +++ b/lib/ansi/ctime.c @@ -0,0 +1,12 @@ +/* + * ctime - convers the calendar time to a string + */ +/* $Header$ */ + +#include <time.h> + +char * +ctime(const time_t *timer) +{ + return asctime(localtime(timer)); +} diff --git a/lib/ansi/difftime.c b/lib/ansi/difftime.c new file mode 100755 index 000000000..ed6e64cfe --- /dev/null +++ b/lib/ansi/difftime.c @@ -0,0 +1,17 @@ +/* + * difftime - compute the difference between two calendar times + */ +/* $Header$ */ + +#include <time.h> + +double +difftime(time_t time1, time_t time0) +{ + /* be careful: time_t may be unsigned */ + if ((time_t)-1 > 0 && time0 > time1) { + return - (double) (time0 - time1); + } else { + return (double)(time1 - time0); + } +} diff --git a/lib/ansi/div.c b/lib/ansi/div.c new file mode 100755 index 000000000..79c35a33e --- /dev/null +++ b/lib/ansi/div.c @@ -0,0 +1,31 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> + +static int tmp = -1; + +div_t +div(register int numer, register int denom) +{ + div_t r; + + /* The assignment of tmp should not be optimized !! */ + if (tmp == -1) { + tmp = (tmp / 2 == 0); + } + if (numer == 0) { + r.quot = numer / denom; /* might trap if denom == 0 */ + r.rem = numer % denom; + } else if ( !tmp && ((numer < 0) != (denom < 0))) { + r.quot = (numer / denom) + 1; + r.rem = numer - (numer / denom + 1) * denom; + } else { + r.quot = numer / denom; + r.rem = numer % denom; + } + return r; +} diff --git a/lib/ansi/errlist.c b/lib/ansi/errlist.c new file mode 100755 index 000000000..4aaeefa48 --- /dev/null +++ b/lib/ansi/errlist.c @@ -0,0 +1,83 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <errno.h> + +static const char unknown[] = "Unknown error"; + +const char *_sys_errlist[] = { + "Error 0", /* EGENERIC */ + "Not owner", /* EPERM */ + "No such file or directory", /* ENOENT */ + "No such process", /* ESRCH */ + "Interrupted system call", /* EINTR */ + "I/O error", /* EIO */ + "No such device or address", /* ENXIO */ + "Arg list too long", /* E2BIG */ + "Exec format error", /* ENOEXEC */ + "Bad file number", /* EBADF */ + "No children", /* ECHILD */ + "Resource temporarily unavailable",/* EAGAIN */ + "Not enough core", /* ENOMEM */ + "Permission denied", /* EACCES */ + "Bad address", /* EFAULT */ + "Block device required", /* ENOTBLK */ + "Resource busy", /* EBUSY */ + "File exists", /* EEXIST */ + "Cross-device link", /* EXDEV */ + "No such device", /* ENODEV */ + "Not a directory", /* ENOTDIR */ + "Is a directory", /* EISDIR */ + "Invalid argument", /* EINVAL */ + "File table overflow", /* ENFILE */ + "Too many open files", /* EMFILE */ + "Not a typewriter", /* ENOTTY */ + "Text file busy", /* ETXTBSY */ + "File too large", /* EFBIG */ + "No space left on device", /* ENOSPC */ + "Illegal seek", /* ESPIPE */ + "Read-only file system", /* EROFS */ + "Too many links", /* EMLINK */ + "Broken pipe", /* EPIPE */ + "Math argument", /* EDOM */ + "Result too large", /* ERANGE */ + "Resource deadlock avoided", /* EDEADLK */ + "File name too long", /* ENAMETOOLONG */ + "No locks available", /* ENOLCK */ + "Function not implemented", /* ENOSYS */ + "Directory not empty", /* ENOTEMPTY */ + unknown, /* 40 */ + unknown, /* 41 */ + unknown, /* 42 */ + unknown, /* 43 */ + unknown, /* 44 */ + unknown, /* 45 */ + unknown, /* 46 */ + unknown, /* 47 */ + unknown, /* 48 */ + unknown, /* 49 */ + "Invalid packet size", /* EPACKSIZE */ + "Not enough buffers left", /* EOUTOFBUFS */ + "Illegal ioctl for device", /* EBADIOCTL */ + "Bad mode for ioctl", /* EBADMODE */ + "Would block", /* EWOULDBLOCK */ + "Bad destination address", /* EBADDEST */ + "Destination not reachable", /* EDSTNOTRCH */ + "Already connected", /* EISCONN */ + "Address in use", /* EADDRINUSE */ + "Connection refused", /* ECONNREFUSED */ + "Connection reset", /* ECONNRESET */ + "Connection timed out", /* ETIMEDOUT */ + "Urgent data present", /* EURG */ + "No urgent data present", /* ENOURG */ + "No connection", /* ENOTCONN */ + "Already shutdown", /* ESHUTDOWN */ + "No such connection", /* ENOCONN */ + "Address family not supported", /* EAFNOSUPPORT */ + "Protocol not supported by AF", /* EPROTONOSUPPORT */ +}; + +const int _sys_nerr = sizeof(_sys_errlist) / sizeof(_sys_errlist[0]); diff --git a/lib/ansi/exit.c b/lib/ansi/exit.c new file mode 100755 index 000000000..353413f05 --- /dev/null +++ b/lib/ansi/exit.c @@ -0,0 +1,36 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdio.h> +#include <stdlib.h> + +#define NEXITS 32 + +void (*__functab[NEXITS])(void); +int __funccnt = 0; + +extern void _exit(int); + +/* only flush output buffers when necessary */ +int (*_clean)(void) = NULL; + +static void +_calls(void) +{ + register int i = __funccnt; + + /* "Called in reversed order of their registration" */ + while (--i >= 0) + (*__functab[i])(); +} + +void +exit(int status) +{ + _calls(); + if (_clean) _clean(); + _exit(status) ; +} diff --git a/lib/ansi/ext_comp.c b/lib/ansi/ext_comp.c new file mode 100755 index 000000000..41ee65dba --- /dev/null +++ b/lib/ansi/ext_comp.c @@ -0,0 +1,743 @@ +/* + (c) copyright 1989 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Id$ */ + +/* extended precision arithmetic for the strtod() and cvt() routines */ + +/* This may require some more work when long doubles get bigger than 8 + bytes. In this case, these routines may become obsolete. ??? +*/ + +#include "ext_fmt.h" +#include <float.h> +#include <errno.h> +#include <ctype.h> + +static int b64_add(struct mantissa *e1, struct mantissa *e2); +static b64_sft(struct mantissa *e1, int n); + +static +mul_ext(struct EXTEND *e1, struct EXTEND *e2, struct EXTEND *e3) +{ + /* Multiply the extended numbers e1 and e2, and put the + result in e3. + */ + register int i,j; /* loop control */ + unsigned short mp[4]; + unsigned short mc[4]; + unsigned short result[8]; /* result */ + + register unsigned short *pres; + + /* first save the sign (XOR) */ + e3->sign = e1->sign ^ e2->sign; + + /* compute new exponent */ + e3->exp = e1->exp + e2->exp + 1; + + /* check for overflow/underflow ??? */ + + /* 128 bit multiply of mantissas */ + + /* assign unknown long formats */ + /* to known unsigned word formats */ + mp[0] = e1->m1 >> 16; + mp[1] = (unsigned short) e1->m1; + mp[2] = e1->m2 >> 16; + mp[3] = (unsigned short) e1->m2; + mc[0] = e2->m1 >> 16; + mc[1] = (unsigned short) e2->m1; + mc[2] = e2->m2 >> 16; + mc[3] = (unsigned short) e2->m2; + for (i = 8; i--;) { + result[i] = 0; + } + /* + * fill registers with their components + */ + for(i=4, pres = &result[4];i--;pres--) if (mp[i]) { + unsigned short k = 0; + unsigned long mpi = mp[i]; + for(j=4;j--;) { + unsigned long tmp = (unsigned long)pres[j] + k; + if (mc[j]) tmp += mpi * mc[j]; + pres[j] = tmp; + k = tmp >> 16; + } + pres[-1] = k; + } + + if (! (result[0] & 0x8000)) { + e3->exp--; + for (i = 0; i <= 3; i++) { + result[i] <<= 1; + if (result[i+1]&0x8000) result[i] |= 1; + } + result[4] <<= 1; + } + /* + * combine the registers to a total + */ + e3->m1 = ((unsigned long)(result[0]) << 16) + result[1]; + e3->m2 = ((unsigned long)(result[2]) << 16) + result[3]; + if (result[4] & 0x8000) { + if (++e3->m2 == 0) { + if (++e3->m1 == 0) { + e3->m1 = 0x80000000; + e3->exp++; + } + } + } +} + +static +add_ext(struct EXTEND *e1, struct EXTEND *e2, struct EXTEND *e3) +{ + /* Add two extended numbers e1 and e2, and put the result + in e3 + */ + struct EXTEND ce2; + int diff; + + if ((e2->m1 | e2->m2) == 0L) { + *e3 = *e1; + return; + } + if ((e1->m1 | e1->m2) == 0L) { + *e3 = *e2; + return; + } + ce2 = *e2; + *e3 = *e1; + e1 = &ce2; + + /* adjust mantissas to equal power */ + diff = e3->exp - e1->exp; + if (diff < 0) { + diff = -diff; + e3->exp += diff; + b64_sft(&(e3->mantissa), diff); + } + else if (diff > 0) { + e1->exp += diff; + b64_sft(&(e1->mantissa), diff); + } + if (e1->sign != e3->sign) { + /* e3 + e1 = e3 - (-e1) */ + if (e1->m1 > e3->m1 || + (e1->m1 == e3->m1 && e1->m2 > e3->m2)) { + /* abs(e1) > abs(e3) */ + if (e3->m2 > e1->m2) { + e1->m1 -= 1; /* carry in */ + } + e1->m1 -= e3->m1; + e1->m2 -= e3->m2; + *e3 = *e1; + } + else { + if (e1->m2 > e3->m2) + e3->m1 -= 1; /* carry in */ + e3->m1 -= e1->m1; + e3->m2 -= e1->m2; + } + } + else { + if (b64_add(&e3->mantissa,&e1->mantissa)) {/* addition carry */ + b64_sft(&e3->mantissa,1);/* shift mantissa one bit RIGHT */ + e3->m1 |= 0x80000000L; /* set max bit */ + e3->exp++; /* increase the exponent */ + } + } + if ((e3->m2 | e3->m1) != 0L) { + /* normalize */ + if (e3->m1 == 0L) { + e3->m1 = e3->m2; e3->m2 = 0L; e3->exp -= 32; + } + if (!(e3->m1 & 0x80000000)) { + unsigned long l = 0x40000000; + int cnt = -1; + + while (! (l & e3->m1)) { + l >>= 1; cnt--; + } + e3->exp += cnt; + b64_sft(&(e3->mantissa), cnt); + } + } +} + +static int +cmp_ext(struct EXTEND *e1, struct EXTEND *e2) +{ + struct EXTEND tmp; + + e2->sign = ! e2->sign; + add_ext(e1, e2, &tmp); + e2->sign = ! e2->sign; + if (tmp.m1 == 0 && tmp.m2 == 0) return 0; + if (tmp.sign) return -1; + return 1; +} + +static +b64_sft(struct mantissa *e1, int n) +{ + if (n > 0) { + if (n > 63) { + e1->l_32 = 0; + e1->h_32 = 0; + return; + } + if (n >= 32) { + e1->l_32 = e1->h_32; + e1->h_32 = 0; + n -= 32; + } + if (n > 0) { + e1->l_32 >>= n; + if (e1->h_32 != 0) { + e1->l_32 |= (e1->h_32 << (32 - n)); + e1->h_32 >>= n; + } + } + return; + } + n = -n; + if (n > 0) { + if (n > 63) { + e1->l_32 = 0; + e1->h_32 = 0; + return; + } + if (n >= 32) { + e1->h_32 = e1->l_32; + e1->l_32 = 0; + n -= 32; + } + if (n > 0) { + e1->h_32 <<= n; + if (e1->l_32 != 0) { + e1->h_32 |= (e1->l_32 >> (32 - n)); + e1->l_32 <<= n; + } + } + } +} + +static int +b64_add(struct mantissa *e1, struct mantissa *e2) + /* + * pointers to 64 bit 'registers' + */ +{ + register int overflow; + int carry; + + /* add higher pair of 32 bits */ + overflow = ((unsigned long) 0xFFFFFFFF - e1->h_32 < e2->h_32); + e1->h_32 += e2->h_32; + + /* add lower pair of 32 bits */ + carry = ((unsigned long) 0xFFFFFFFF - e1->l_32 < e2->l_32); + e1->l_32 += e2->l_32; + if ((carry) && (++e1->h_32 == 0)) + return(1); /* had a 64 bit overflow */ + else + return(overflow); /* return status from higher add */ +} + +/* The following tables can be computed with the following bc(1) + program: + +obase=16 +scale=0 +define t(x){ + auto a, b, c + a=2;b=1;c=2^32;n=1 + while(a<x) { + b=a;n+=n;a*=a + } + n/=2 + a=b + while(b<x) { + a=b;b*=c;n+=32 + } + n-=32 + b=a + while(a<x) { + b=a;a+=a;n+=1 + } + n-=1 + x*=16^16 + b=x%a + x/=a + if(a<=(2*b)) x+=1 + obase=10 + n + obase=16 + return(x) +} +for (i=1;i<28;i++) { + t(10^i) +} +0 +for (i=1;i<20;i++) { + t(10^(28*i)) +} +0 +define r(x){ + auto a, b, c + a=2;b=1;c=2^32;n=1 + while(a<x) { + b=a;n+=n;a*=a + } + n/=2 + a=b + while(b<x) { + a=b;b*=c;n+=32 + } + n-=32 + b=a + while(a<x) { + b=a;a+=a;n+=1 + } + a=b + a*=16^16 + b=a%x + a/=x + if(x<=(2*b)) a+=1 + obase=10 + -n + obase=16 + return(a) +} +for (i=1;i<28;i++) { + r(10^i) +} +0 +for (i=1;i<20;i++) { + r(10^(28*i)) +} +0 + +*/ +static struct EXTEND ten_powers[] = { /* representation of 10 ** i */ + { 0, 0, 0x80000000, 0 }, + { 0, 3, 0xA0000000, 0 }, + { 0, 6, 0xC8000000, 0 }, + { 0, 9, 0xFA000000, 0 }, + { 0, 13, 0x9C400000, 0 }, + { 0, 16, 0xC3500000, 0 }, + { 0, 19, 0xF4240000, 0 }, + { 0, 23, 0x98968000, 0 }, + { 0, 26, 0xBEBC2000, 0 }, + { 0, 29, 0xEE6B2800, 0 }, + { 0, 33, 0x9502F900, 0 }, + { 0, 36, 0xBA43B740, 0 }, + { 0, 39, 0xE8D4A510, 0 }, + { 0, 43, 0x9184E72A, 0 }, + { 0, 46, 0xB5E620F4, 0x80000000 }, + { 0, 49, 0xE35FA931, 0xA0000000 }, + { 0, 53, 0x8E1BC9BF, 0x04000000 }, + { 0, 56, 0xB1A2BC2E, 0xC5000000 }, + { 0, 59, 0xDE0B6B3A, 0x76400000 }, + { 0, 63, 0x8AC72304, 0x89E80000 }, + { 0, 66, 0xAD78EBC5, 0xAC620000 }, + { 0, 69, 0xD8D726B7, 0x177A8000 }, + { 0, 73, 0x87867832, 0x6EAC9000 }, + { 0, 76, 0xA968163F, 0x0A57B400 }, + { 0, 79, 0xD3C21BCE, 0xCCEDA100 }, + { 0, 83, 0x84595161, 0x401484A0 }, + { 0, 86, 0xA56FA5B9, 0x9019A5C8 }, + { 0, 89, 0xCECB8F27, 0xF4200F3A } +}; +static struct EXTEND big_ten_powers[] = { /* representation of 10 ** (28*i) */ + { 0, 0, 0x80000000, 0 }, + { 0, 93, 0x813F3978, 0xF8940984 }, + { 0, 186, 0x82818F12, 0x81ED44A0 }, + { 0, 279, 0x83C7088E, 0x1AAB65DB }, + { 0, 372, 0x850FADC0, 0x9923329E }, + { 0, 465, 0x865B8692, 0x5B9BC5C2 }, + { 0, 558, 0x87AA9AFF, 0x79042287 }, + { 0, 651, 0x88FCF317, 0xF22241E2 }, + { 0, 744, 0x8A5296FF, 0xE33CC930 }, + { 0, 837, 0x8BAB8EEF, 0xB6409C1A }, + { 0, 930, 0x8D07E334, 0x55637EB3 }, + { 0, 1023, 0x8E679C2F, 0x5E44FF8F }, + { 0, 1116, 0x8FCAC257, 0x558EE4E6 }, + { 0, 1209, 0x91315E37, 0xDB165AA9 }, + { 0, 1302, 0x929B7871, 0xDE7F22B9 }, + { 0, 1395, 0x940919BB, 0xD4620B6D }, + { 0, 1488, 0x957A4AE1, 0xEBF7F3D4 }, + { 0, 1581, 0x96EF14C6, 0x454AA840 }, + { 0, 1674, 0x98678061, 0x27ECE4F5 }, + { 0, 1767, 0x99E396C1, 0x3A3ACFF2 } +}; + +static struct EXTEND r_ten_powers[] = { /* representation of 10 ** -i */ + { 0, 0, 0x80000000, 0 }, + { 0, -4, 0xCCCCCCCC, 0xCCCCCCCD }, + { 0, -7, 0xA3D70A3D, 0x70A3D70A }, + { 0, -10, 0x83126E97, 0x8D4FDF3B }, + { 0, -14, 0xD1B71758, 0xE219652C }, + { 0, -17, 0xA7C5AC47, 0x1B478423 }, + { 0, -20, 0x8637BD05, 0xAF6C69B6 }, + { 0, -24, 0xD6BF94D5, 0xE57A42BC }, + { 0, -27, 0xABCC7711, 0x8461CEFD }, + { 0, -30, 0x89705F41, 0x36B4A597 }, + { 0, -34, 0xDBE6FECE, 0xBDEDD5BF }, + { 0, -37, 0xAFEBFF0B, 0xCB24AAFF }, + { 0, -40, 0x8CBCCC09, 0x6F5088CC }, + { 0, -44, 0xE12E1342, 0x4BB40E13 }, + { 0, -47, 0xB424DC35, 0x095CD80F }, + { 0, -50, 0x901D7CF7, 0x3AB0ACD9 }, + { 0, -54, 0xE69594BE, 0xC44DE15B }, + { 0, -57, 0xB877AA32, 0x36A4B449 }, + { 0, -60, 0x9392EE8E, 0x921D5D07 }, + { 0, -64, 0xEC1E4A7D, 0xB69561A5 }, + { 0, -67, 0xBCE50864, 0x92111AEB }, + { 0, -70, 0x971DA050, 0x74DA7BEF }, + { 0, -74, 0xF1C90080, 0xBAF72CB1 }, + { 0, -77, 0xC16D9A00, 0x95928A27 }, + { 0, -80, 0x9ABE14CD, 0x44753B53 }, + { 0, -84, 0xF79687AE, 0xD3EEC551 }, + { 0, -87, 0xC6120625, 0x76589DDB }, + { 0, -90, 0x9E74D1B7, 0x91E07E48 } +}; + +static struct EXTEND r_big_ten_powers[] = { /* representation of 10 ** -(28*i) */ + { 0, 0, 0x80000000, 0 }, + { 0, -94, 0xFD87B5F2, 0x8300CA0E }, + { 0, -187, 0xFB158592, 0xBE068D2F }, + { 0, -280, 0xF8A95FCF, 0x88747D94 }, + { 0, -373, 0xF64335BC, 0xF065D37D }, + { 0, -466, 0xF3E2F893, 0xDEC3F126 }, + { 0, -559, 0xF18899B1, 0xBC3F8CA2 }, + { 0, -652, 0xEF340A98, 0x172AACE5 }, + { 0, -745, 0xECE53CEC, 0x4A314EBE }, + { 0, -838, 0xEA9C2277, 0x23EE8BCB }, + { 0, -931, 0xE858AD24, 0x8F5C22CA }, + { 0, -1024, 0xE61ACF03, 0x3D1A45DF }, + { 0, -1117, 0xE3E27A44, 0x4D8D98B8 }, + { 0, -1210, 0xE1AFA13A, 0xFBD14D6E }, + { 0, -1303, 0xDF82365C, 0x497B5454 }, + { 0, -1396, 0xDD5A2C3E, 0xAB3097CC }, + { 0, -1489, 0xDB377599, 0xB6074245 }, + { 0, -1582, 0xD91A0545, 0xCDB51186 }, + { 0, -1675, 0xD701CE3B, 0xD387BF48 }, + { 0, -1768, 0xD4EEC394, 0xD6258BF8 } +}; + +#define TP (int)(sizeof(ten_powers)/sizeof(ten_powers[0])) +#define BTP (int)(sizeof(big_ten_powers)/sizeof(big_ten_powers[0])) +#define MAX_EXP (TP * BTP - 1) + +static +add_exponent(struct EXTEND *e, int exp) +{ + int neg = exp < 0; + int divsz, modsz; + struct EXTEND x; + + if (neg) exp = -exp; + divsz = exp / TP; + modsz = exp % TP; + if (neg) { + mul_ext(e, &r_ten_powers[modsz], &x); + mul_ext(&x, &r_big_ten_powers[divsz], e); + } + else { + mul_ext(e, &ten_powers[modsz], &x); + mul_ext(&x, &big_ten_powers[divsz], e); + } +} + +_str_ext_cvt(const char *s, char **ss, struct EXTEND *e) +{ + /* Like strtod, but for extended precision */ + register int c; + int dotseen = 0; + int digitseen = 0; + int exp = 0; + + if (ss) *ss = (char *)s; + while (isspace(*s)) s++; + + e->sign = 0; + e->exp = 0; + e->m1 = e->m2 = 0; + + c = *s; + switch(c) { + case '-': + e->sign = 1; + case '+': + s++; + } + while (c = *s++, isdigit(c) || (c == '.' && ! dotseen++)) { + if (c == '.') continue; + digitseen = 1; + if (e->m1 <= (unsigned long)(0xFFFFFFFF)/10) { + struct mantissa a1; + + a1 = e->mantissa; + b64_sft(&(e->mantissa), -3); + b64_sft(&a1, -1); + b64_add(&(e->mantissa), &a1); + a1.h_32 = 0; + a1.l_32 = c - '0'; + b64_add(&(e->mantissa), &a1); + } + else exp++; + if (dotseen) exp--; + } + if (! digitseen) return; + + if (ss) *ss = (char *)s - 1; + + if (c == 'E' || c == 'e') { + int exp1 = 0; + int sign = 1; + int exp_overflow = 0; + + switch(*s) { + case '-': + sign = -1; + case '+': + s++; + } + if (c = *s, isdigit(c)) { + do { + int tmp; + + exp1 = 10 * exp1 + (c - '0'); + if ((tmp = sign * exp1 + exp) > MAX_EXP || + tmp < -MAX_EXP) { + exp_overflow = 1; + } + } while (c = *++s, isdigit(c)); + if (ss) *ss = (char *)s; + } + exp += sign * exp1; + if (exp_overflow) { + exp = sign * MAX_EXP; + if (e->m1 != 0 || e->m2 != 0) errno = ERANGE; + } + } + if (e->m1 == 0 && e->m2 == 0) return; + e->exp = 63; + while (! (e->m1 & 0x80000000)) { + b64_sft(&(e->mantissa),-1); + e->exp--; + } + add_exponent(e, exp); +} + +#include <math.h> + +static +ten_mult(struct EXTEND *e) +{ + struct EXTEND e1 = *e; + + e1.exp++; + e->exp += 3; + add_ext(e, &e1, e); +} + +#define NDIGITS 128 +#define NSIGNIFICANT 19 + +char * +_ext_str_cvt(struct EXTEND *e, int ndigit, int *decpt, int *sign, int ecvtflag) +{ + /* Like cvt(), but for extended precision */ + + static char buf[NDIGITS+1]; + struct EXTEND m; + register char *p = buf; + register char *pe; + int findex = 0; + + if (ndigit < 0) ndigit = 0; + if (ndigit > NDIGITS) ndigit = NDIGITS; + pe = &buf[ndigit]; + buf[0] = '\0'; + + *sign = 0; + if (e->sign) { + *sign = 1; + e->sign = 0; + } + + *decpt = 0; + if (e->m1 != 0) { + register struct EXTEND *pp = &big_ten_powers[1]; + + while(cmp_ext(e,pp) >= 0) { + pp++; + findex = pp - big_ten_powers; + if (findex >= BTP) break; + } + pp--; + findex = pp - big_ten_powers; + mul_ext(e,&r_big_ten_powers[findex],e); + *decpt += findex * TP; + pp = &ten_powers[1]; + while(pp < &ten_powers[TP] && cmp_ext(e, pp) >= 0) pp++; + pp--; + findex = pp - ten_powers; + *decpt += findex; + + if (cmp_ext(e, &ten_powers[0]) < 0) { + pp = &r_big_ten_powers[1]; + while(cmp_ext(e,pp) < 0) pp++; + pp--; + findex = pp - r_big_ten_powers; + mul_ext(e, &big_ten_powers[findex], e); + *decpt -= findex * TP; + /* here, value >= 10 ** -28 */ + ten_mult(e); + (*decpt)--; + pp = &r_ten_powers[0]; + while(cmp_ext(e, pp) < 0) pp++; + findex = pp - r_ten_powers; + mul_ext(e, &ten_powers[findex], e); + *decpt -= findex; + findex = 0; + } + (*decpt)++; /* because now value in [1.0, 10.0) */ + } + if (! ecvtflag) { + /* for fcvt() we need ndigit digits behind the dot */ + pe += *decpt; + if (pe > &buf[NDIGITS]) pe = &buf[NDIGITS]; + } + m.exp = -62; + m.sign = 0; + m.m1 = 0xA0000000; + m.m2 = 0; + while (p <= pe) { + struct EXTEND oneminm; + + if (p - pe > NSIGNIFICANT) { + findex = 0; + e->m1 = 0; + } + if (findex) { + struct EXTEND tc, oldtc; + int count = 0; + + oldtc.exp = 0; + oldtc.sign = 0; + oldtc.m1 = 0; + oldtc.m2 = 0; + tc = ten_powers[findex]; + while (cmp_ext(e, &tc) >= 0) { + oldtc = tc; + add_ext(&tc, &ten_powers[findex], &tc); + count++; + } + *p++ = count + '0'; + oldtc.sign = 1; + add_ext(e, &oldtc, e); + findex--; + continue; + } + if (e->m1) { + m.sign = 1; + add_ext(&ten_powers[0], &m, &oneminm); + m.sign = 0; + if (e->exp >= 0) { + struct EXTEND x; + + x.m2 = 0; x.exp = e->exp; + x.sign = 1; + x.m1 = e->m1>>(31-e->exp); + *p++ = (x.m1) + '0'; + x.m1 = x.m1 << (31-e->exp); + add_ext(e, &x, e); + } + else *p++ = '0'; + /* Check that remainder is still significant */ + if (cmp_ext(&m, e) > 0 || cmp_ext(e, &oneminm) > 0) { + if (e->m1 && e->exp >= -1) *(p-1) += 1; + e->m1 = 0; + continue; + } + ten_mult(&m); + ten_mult(e); + } + else *p++ = '0'; + } + if (pe >= buf) { + p = pe; + *p += 5; /* round of at the end */ + while (*p > '9') { + *p = '0'; + if (p > buf) ++*--p; + else { + *p = '1'; + ++*decpt; + if (! ecvtflag) { + /* maybe add another digit at the end, + because the point was shifted right + */ + if (pe > buf) *pe = '0'; + pe++; + } + } + } + *pe = '\0'; + } + return buf; +} + +_dbl_ext_cvt(double value, struct EXTEND *e) +{ + /* Convert double to extended + */ + int exponent; + + value = frexp(value, &exponent); + e->sign = value < 0.0; + if (e->sign) value = -value; + e->exp = exponent - 1; + value *= 4294967296.0; + e->m1 = value; + value -= e->m1; + value *= 4294967296.0; + e->m2 = value; +} + +static struct EXTEND max_d; + +double +_ext_dbl_cvt(struct EXTEND *e) +{ + /* Convert extended to double + */ + double f; + int sign = e->sign; + + e->sign = 0; + if (e->m1 == 0 && e->m2 == 0) { + return 0.0; + } + if (max_d.exp == 0) { + _dbl_ext_cvt(DBL_MAX, &max_d); + } + if (cmp_ext(&max_d, e) < 0) { + f = HUGE_VAL; + errno = ERANGE; + } + else f = ldexp((double)e->m1*4294967296.0 + (double)e->m2, e->exp-63); + if (sign) f = -f; + if (f == 0.0 && (e->m1 != 0 || e->m2 != 0)) { + errno = ERANGE; + } + return f; +} diff --git a/lib/ansi/ext_fmt.h b/lib/ansi/ext_fmt.h new file mode 100755 index 000000000..e8a5db1b8 --- /dev/null +++ b/lib/ansi/ext_fmt.h @@ -0,0 +1,13 @@ +struct mantissa { + unsigned long h_32; + unsigned long l_32; +}; + +struct EXTEND { + short sign; + short exp; + struct mantissa mantissa; +#define m1 mantissa.h_32 +#define m2 mantissa.l_32 +}; + diff --git a/lib/ansi/getenv.c b/lib/ansi/getenv.c new file mode 100755 index 000000000..6ec5e0fc2 --- /dev/null +++ b/lib/ansi/getenv.c @@ -0,0 +1,28 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> + +extern const char ***_penviron; + +char * +getenv(const char *name) +{ + register const char **v = *_penviron; + register const char *p, *q; + + if (v == NULL || name == NULL) + return (char *)NULL; + while ((p = *v++) != NULL) { + q = name; + while (*q && (*q == *p++)) + q++; + if (*q || (*p != '=')) + continue; + return (char *)p + 1; + } + return (char *)NULL; +} diff --git a/lib/ansi/gmtime.c b/lib/ansi/gmtime.c new file mode 100755 index 000000000..92c57e7d3 --- /dev/null +++ b/lib/ansi/gmtime.c @@ -0,0 +1,41 @@ +/* + * gmtime - convert the calendar time into broken down time + */ +/* $Header$ */ + +#include <time.h> +#include <limits.h> +#include "loc_time.h" + +struct tm * +gmtime(register const time_t *timer) +{ + static struct tm br_time; + register struct tm *timep = &br_time; + time_t time = *timer; + register unsigned long dayclock, dayno; + int year = EPOCH_YR; + + dayclock = (unsigned long)time % SECS_DAY; + dayno = (unsigned long)time / SECS_DAY; + + timep->tm_sec = dayclock % 60; + timep->tm_min = (dayclock % 3600) / 60; + timep->tm_hour = dayclock / 3600; + timep->tm_wday = (dayno + 4) % 7; /* day 0 was a thursday */ + while (dayno >= YEARSIZE(year)) { + dayno -= YEARSIZE(year); + year++; + } + timep->tm_year = year - YEAR0; + timep->tm_yday = dayno; + timep->tm_mon = 0; + while (dayno >= _ytab[LEAPYEAR(year)][timep->tm_mon]) { + dayno -= _ytab[LEAPYEAR(year)][timep->tm_mon]; + timep->tm_mon++; + } + timep->tm_mday = dayno + 1; + timep->tm_isdst = 0; + + return timep; +} diff --git a/lib/ansi/isalnum.c b/lib/ansi/isalnum.c new file mode 100755 index 000000000..2431df45c --- /dev/null +++ b/lib/ansi/isalnum.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int (isalnum)(int c) { + return isalnum(c); +} diff --git a/lib/ansi/isalpha.c b/lib/ansi/isalpha.c new file mode 100755 index 000000000..f884c0599 --- /dev/null +++ b/lib/ansi/isalpha.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int (isalpha)(int c) { + return isalpha(c); +} diff --git a/lib/ansi/isascii.c b/lib/ansi/isascii.c new file mode 100755 index 000000000..64165e07e --- /dev/null +++ b/lib/ansi/isascii.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int (isascii)(int c) { + return isascii(c); +} diff --git a/lib/ansi/iscntrl.c b/lib/ansi/iscntrl.c new file mode 100755 index 000000000..f778ea354 --- /dev/null +++ b/lib/ansi/iscntrl.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int (iscntrl)(int c) { + return iscntrl(c); +} diff --git a/lib/ansi/isdigit.c b/lib/ansi/isdigit.c new file mode 100755 index 000000000..ebcbe489a --- /dev/null +++ b/lib/ansi/isdigit.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int (isdigit)(int c) { + return isdigit(c); +} diff --git a/lib/ansi/isgraph.c b/lib/ansi/isgraph.c new file mode 100755 index 000000000..1dccf8b3c --- /dev/null +++ b/lib/ansi/isgraph.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int (isgraph)(int c) { + return isgraph(c); +} diff --git a/lib/ansi/islower.c b/lib/ansi/islower.c new file mode 100755 index 000000000..8565dd187 --- /dev/null +++ b/lib/ansi/islower.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int (islower)(int c) { + return islower(c); +} diff --git a/lib/ansi/isprint.c b/lib/ansi/isprint.c new file mode 100755 index 000000000..10a9e58b1 --- /dev/null +++ b/lib/ansi/isprint.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int (isprint)(int c) { + return isprint(c); +} diff --git a/lib/ansi/ispunct.c b/lib/ansi/ispunct.c new file mode 100755 index 000000000..35df5c6f7 --- /dev/null +++ b/lib/ansi/ispunct.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int (ispunct)(int c) { + return ispunct(c); +} diff --git a/lib/ansi/isspace.c b/lib/ansi/isspace.c new file mode 100755 index 000000000..2e5fcf47b --- /dev/null +++ b/lib/ansi/isspace.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int (isspace)(int c) { + return isspace(c); +} diff --git a/lib/ansi/isupper.c b/lib/ansi/isupper.c new file mode 100755 index 000000000..40a22ac93 --- /dev/null +++ b/lib/ansi/isupper.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int (isupper)(int c) { + return isupper(c); +} diff --git a/lib/ansi/isxdigit.c b/lib/ansi/isxdigit.c new file mode 100755 index 000000000..28a3ab059 --- /dev/null +++ b/lib/ansi/isxdigit.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int (isxdigit)(int c) { + return isxdigit(c); +} diff --git a/lib/ansi/labs.c b/lib/ansi/labs.c new file mode 100755 index 000000000..ca593eadf --- /dev/null +++ b/lib/ansi/labs.c @@ -0,0 +1,13 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> + +long +labs(register long l) +{ + return l >= 0 ? l : -l; +} diff --git a/lib/ansi/ldiv.c b/lib/ansi/ldiv.c new file mode 100755 index 000000000..42c761977 --- /dev/null +++ b/lib/ansi/ldiv.c @@ -0,0 +1,31 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> + +static long tmp = -1; + +ldiv_t +ldiv(register long numer, register long denom) +{ + ldiv_t r; + + /* The assignment of tmp should not be optimized !! */ + if (tmp == -1) { + tmp = (tmp / 2 == 0); + } + if (numer == 0) { + r.quot = numer / denom; /* might trap if denom == 0 */ + r.rem = numer % denom; + } else if ( !tmp && ((numer < 0) != (denom < 0))) { + r.quot = (numer / denom) + 1; + r.rem = numer - (numer / denom + 1) * denom; + } else { + r.quot = numer / denom; + r.rem = numer % denom; + } + return r; +} diff --git a/lib/ansi/loc_time.h b/lib/ansi/loc_time.h new file mode 100755 index 000000000..cc2dd95b9 --- /dev/null +++ b/lib/ansi/loc_time.h @@ -0,0 +1,26 @@ +/* + * loc_time.h - some local definitions + */ +/* $Header$ */ + +#define YEAR0 1900 /* the first year */ +#define EPOCH_YR 1970 /* EPOCH = Jan 1 1970 00:00:00 */ +#define SECS_DAY (24L * 60L * 60L) +#define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400))) +#define YEARSIZE(year) (LEAPYEAR(year) ? 366 : 365) +#define FIRSTSUNDAY(timp) (((timp)->tm_yday - (timp)->tm_wday + 420) % 7) +#define FIRSTDAYOF(timp) (((timp)->tm_wday - (timp)->tm_yday + 420) % 7) +#define TIME_MAX ULONG_MAX +#define ABB_LEN 3 + +extern const int _ytab[2][12]; +extern const char *_days[]; +extern const char *_months[]; + +void _tzset(void); +unsigned _dstget(struct tm *timep); + +extern long _timezone; +extern long _dst_off; +extern int _daylight; +extern char *_tzname[2]; diff --git a/lib/ansi/localeconv.c b/lib/ansi/localeconv.c new file mode 100755 index 000000000..fdc03a0d6 --- /dev/null +++ b/lib/ansi/localeconv.c @@ -0,0 +1,36 @@ +/* + * localeconv - set components of a struct according to current locale + */ +/* $Header$ */ + +#include <limits.h> +#include <locale.h> + +extern struct lconv _lc; + +struct lconv * +localeconv(void) +{ + register struct lconv *lcp = &_lc; + + lcp->decimal_point = "."; + lcp->thousands_sep = ""; + lcp->grouping = ""; + lcp->int_curr_symbol = ""; + lcp->currency_symbol = ""; + lcp->mon_decimal_point = ""; + lcp->mon_thousands_sep = ""; + lcp->mon_grouping = ""; + lcp->positive_sign = ""; + lcp->negative_sign = ""; + lcp->int_frac_digits = CHAR_MAX; + lcp->frac_digits = CHAR_MAX; + lcp->p_cs_precedes = CHAR_MAX; + lcp->p_sep_by_space = CHAR_MAX; + lcp->n_cs_precedes = CHAR_MAX; + lcp->n_sep_by_space = CHAR_MAX; + lcp->p_sign_posn = CHAR_MAX; + lcp->n_sign_posn = CHAR_MAX; + + return lcp; +} diff --git a/lib/ansi/localtime.c b/lib/ansi/localtime.c new file mode 100755 index 000000000..36d22546a --- /dev/null +++ b/lib/ansi/localtime.c @@ -0,0 +1,34 @@ +/* + * localtime - convert a calendar time into broken down time + */ +/* $Header$ */ + +#include <time.h> +#include "loc_time.h" + +/* We must be careful, since an int can't represent all the seconds in a day. + * Hence the adjustment of minutes when adding timezone and dst information. + * This assumes that both must be expressable in multiples of a minute. + * Furthermore, it is assumed that both fit into an integer when expressed as + * minutes (this is about 22 days, so this should not cause any problems). + */ +struct tm * +localtime(const time_t *timer) +{ + struct tm *timep; + unsigned dst; + + _tzset(); + timep = gmtime(timer); /* tm->tm_isdst == 0 */ + timep->tm_min -= _timezone / 60; + timep->tm_sec -= _timezone % 60; + mktime(timep); + + dst = _dstget(timep); + if (dst) { + timep->tm_min += dst / 60; + timep->tm_sec += dst % 60; + mktime(timep); + } + return timep; +} diff --git a/lib/ansi/malloc.c b/lib/ansi/malloc.c new file mode 100755 index 000000000..aa0bc7af2 --- /dev/null +++ b/lib/ansi/malloc.c @@ -0,0 +1,205 @@ +/* $Header$ */ + +/* replace undef by define */ +#undef DEBUG /* check assertions */ +#undef SLOWDEBUG /* some extra test loops (requires DEBUG) */ + +#ifndef DEBUG +#define NDEBUG +#endif + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> + +#if _EM_WSIZE == _EM_PSIZE +#define ptrint int +#else +#define ptrint long +#endif + +#if _EM_PSIZE == 2 +#define BRKSIZE 1024 +#else +#define BRKSIZE 4096 +#endif +#define PTRSIZE ((int) sizeof(void *)) +#define Align(x,a) (((x) + (a - 1)) & ~(a - 1)) +#define NextSlot(p) (* (void **) ((p) - PTRSIZE)) +#define NextFree(p) (* (void **) (p)) + +/* + * A short explanation of the data structure and algorithms. + * An area returned by malloc() is called a slot. Each slot + * contains the number of bytes requested, but preceeded by + * an extra pointer to the next the slot in memory. + * '_bottom' and '_top' point to the first/last slot. + * More memory is asked for using brk() and appended to top. + * The list of free slots is maintained to keep malloc() fast. + * '_empty' points the the first free slot. Free slots are + * linked together by a pointer at the start of the + * user visable part, so just after the next-slot pointer. + * Free slots are merged together by free(). + */ + +extern void *_sbrk(int); +extern int _brk(void *); +static void *_bottom, *_top, *_empty; + +static int grow(size_t len) +{ + register char *p; + + assert(NextSlot((char *)_top) == 0); + if ((char *) _top + len < (char *) _top + || (p = (char *)Align((ptrint)_top + len, BRKSIZE)) < (char *) _top ) { + errno = ENOMEM; + return(0); + } + if (_brk(p) != 0) + return(0); + NextSlot((char *)_top) = p; + NextSlot(p) = 0; + free(_top); + _top = p; + return 1; +} + +void * +malloc(size_t size) +{ + register char *prev, *p, *next, *new; + register unsigned len, ntries; + + if (size == 0) + return NULL; + + for (ntries = 0; ntries < 2; ntries++) { + if ((len = Align(size, PTRSIZE) + PTRSIZE) < 2 * PTRSIZE) { + errno = ENOMEM; + return NULL; + } + if (_bottom == 0) { + if ((p = _sbrk(2 * PTRSIZE)) == (char *) -1) + return NULL; + p = (char *) Align((ptrint)p, PTRSIZE); + p += PTRSIZE; + _top = _bottom = p; + NextSlot(p) = 0; + } +#ifdef SLOWDEBUG + for (p = _bottom; (next = NextSlot(p)) != 0; p = next) + assert(next > p); + assert(p == _top); +#endif + for (prev = 0, p = _empty; p != 0; prev = p, p = NextFree(p)) { + next = NextSlot(p); + new = p + len; /* easily overflows!! */ + if (new > next || new <= p) + continue; /* too small */ + if (new + PTRSIZE < next) { /* too big, so split */ + /* + PTRSIZE avoids tiny slots on free list */ + NextSlot(new) = next; + NextSlot(p) = new; + NextFree(new) = NextFree(p); + NextFree(p) = new; + } + if (prev) + NextFree(prev) = NextFree(p); + else + _empty = NextFree(p); + return p; + } + if (grow(len) == 0) + break; + } + assert(ntries != 2); + return NULL; +} + +void * +realloc(void *oldp, size_t size) +{ + register char *prev, *p, *next, *new; + char *old = oldp; + register size_t len, n; + + if (old == 0) + return malloc(size); + if (size == 0) { + free(old); + return NULL; + } + len = Align(size, PTRSIZE) + PTRSIZE; + next = NextSlot(old); + n = (int)(next - old); /* old length */ + /* + * extend old if there is any free space just behind it + */ + for (prev = 0, p = _empty; p != 0; prev = p, p = NextFree(p)) { + if (p > next) + break; + if (p == next) { /* 'next' is a free slot: merge */ + NextSlot(old) = NextSlot(p); + if (prev) + NextFree(prev) = NextFree(p); + else + _empty = NextFree(p); + next = NextSlot(old); + break; + } + } + new = old + len; + /* + * Can we use the old, possibly extended slot? + */ + if (new <= next && new >= old) { /* it does fit */ + if (new + PTRSIZE < next) { /* too big, so split */ + /* + PTRSIZE avoids tiny slots on free list */ + NextSlot(new) = next; + NextSlot(old) = new; + free(new); + } + return old; + } + if ((new = malloc(size)) == NULL) /* it didn't fit */ + return NULL; + memcpy(new, old, n); /* n < size */ + free(old); + return new; +} + +void +free(void *ptr) +{ + register char *prev, *next; + char *p = ptr; + + if (p == 0) + return; + + assert(NextSlot(p) > p); + for (prev = 0, next = _empty; next != 0; prev = next, next = NextFree(next)) + if (p < next) + break; + NextFree(p) = next; + if (prev) + NextFree(prev) = p; + else + _empty = p; + if (next) { + assert(NextSlot(p) <= next); + if (NextSlot(p) == next) { /* merge p and next */ + NextSlot(p) = NextSlot(next); + NextFree(p) = NextFree(next); + } + } + if (prev) { + assert(NextSlot(prev) <= p); + if (NextSlot(prev) == p) { /* merge prev and p */ + NextSlot(prev) = NextSlot(p); + NextFree(prev) = NextFree(p); + } + } +} diff --git a/lib/ansi/mblen.c b/lib/ansi/mblen.c new file mode 100755 index 000000000..53b7dc20d --- /dev/null +++ b/lib/ansi/mblen.c @@ -0,0 +1,18 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> +#include <limits.h> + +#define CHAR_SHIFT 8 + +int +mblen(const char *s, size_t n) +{ + if (s == (const char *)NULL) return 0; /* no state dependent codings */ + if (n <= 0) return 0; + return (*s != 0); +} diff --git a/lib/ansi/mbstowcs.c b/lib/ansi/mbstowcs.c new file mode 100755 index 000000000..2749cbace --- /dev/null +++ b/lib/ansi/mbstowcs.c @@ -0,0 +1,20 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> + +size_t +mbstowcs(register wchar_t *pwcs, register const char *s, size_t n) +{ + register int i = n; + + while (--i >= 0) { + if (!(*pwcs++ = *s++)) + return n - i - 1; + } + return n - i; +} + diff --git a/lib/ansi/mbtowc.c b/lib/ansi/mbtowc.c new file mode 100755 index 000000000..3e6dd051d --- /dev/null +++ b/lib/ansi/mbtowc.c @@ -0,0 +1,17 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> +#include <limits.h> + +int +mbtowc(wchar_t *pwc, register const char *s, size_t n) +{ + if (s == (const char *)NULL) return 0; + if (n <= 0) return 0; + if (pwc) *pwc = *s; + return (*s != 0); +} diff --git a/lib/ansi/memchr.c b/lib/ansi/memchr.c new file mode 100755 index 000000000..eb3086984 --- /dev/null +++ b/lib/ansi/memchr.c @@ -0,0 +1,23 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +void * +memchr(const void *s, register int c, register size_t n) +{ + register const unsigned char *s1 = s; + + c = (unsigned char) c; + if (n) { + n++; + while (--n > 0) { + if (*s1++ != c) continue; + return (void *) --s1; + } + } + return NULL; +} diff --git a/lib/ansi/memcmp.c b/lib/ansi/memcmp.c new file mode 100755 index 000000000..b44eefcf4 --- /dev/null +++ b/lib/ansi/memcmp.c @@ -0,0 +1,22 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Id$ */ + +#include <string.h> + +int +memcmp(const void *s1, const void *s2, size_t n) +{ + register const unsigned char *p1 = s1, *p2 = s2; + + if (n) { + n++; + while (--n > 0) { + if (*p1++ == *p2++) continue; + return *--p1 - *--p2; + } + } + return 0; +} diff --git a/lib/ansi/memcpy.c b/lib/ansi/memcpy.c new file mode 100755 index 000000000..6082fa4c1 --- /dev/null +++ b/lib/ansi/memcpy.c @@ -0,0 +1,23 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +void * +memcpy(void *s1, const void *s2, register size_t n) +{ + register char *p1 = s1; + register const char *p2 = s2; + + + if (n) { + n++; + while (--n > 0) { + *p1++ = *p2++; + } + } + return s1; +} diff --git a/lib/ansi/memmove.c b/lib/ansi/memmove.c new file mode 100755 index 000000000..e8d653993 --- /dev/null +++ b/lib/ansi/memmove.c @@ -0,0 +1,32 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +void * +memmove(void *s1, const void *s2, register size_t n) +{ + register char *p1 = s1; + register const char *p2 = s2; + + if (n>0) { + if (p2 <= p1 && p2 + n > p1) { + /* overlap, copy backwards */ + p1 += n; + p2 += n; + n++; + while (--n > 0) { + *--p1 = *--p2; + } + } else { + n++; + while (--n > 0) { + *p1++ = *p2++; + } + } + } + return s1; +} diff --git a/lib/ansi/memset.c b/lib/ansi/memset.c new file mode 100755 index 000000000..2138cf35e --- /dev/null +++ b/lib/ansi/memset.c @@ -0,0 +1,21 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +void * +memset(void *s, register int c, register size_t n) +{ + register char *s1 = s; + + if (n>0) { + n++; + while (--n > 0) { + *s1++ = c; + } + } + return s; +} diff --git a/lib/ansi/misc.c b/lib/ansi/misc.c new file mode 100755 index 000000000..bce1d8f1b --- /dev/null +++ b/lib/ansi/misc.c @@ -0,0 +1,410 @@ +/* + * misc - data and miscellaneous routines + */ +/* $Header$ */ + +#include <ctype.h> +#include <time.h> +#include <stdlib.h> +#include <string.h> + +#if defined(__BSD4_2) + +struct timeval { + long tv_sec; /* seconds */ + long tv_usec; /* and microseconds */ +}; + +struct timezone { + int tz_minuteswest; /* minutes west of Greenwich */ + int tz_dsttime; /* type of dst correction */ +}; + +int _gettimeofday(struct timeval *tp, struct timezone *tzp); + +#elif !defined(_POSIX_SOURCE) && !defined(__USG) +#if !defined(_MINIX) /* MINIX has no ftime() */ +struct timeb { + long time; + unsigned short millitm; + short timezone; + short dstflag; +}; +void _ftime(struct timeb *bp); +#endif +#endif + +#include "loc_time.h" + +#define RULE_LEN 120 +#define TZ_LEN 10 + +/* Make sure that the strings do not end up in ROM. + * These strings probably contain the wrong value, and we cannot obtain the + * right value from the system. TZ is the only help. + */ +static char ntstr[TZ_LEN + 1] = "GMT"; /* string for normal time */ +static char dststr[TZ_LEN + 1] = "GDT"; /* string for daylight saving */ + +long _timezone = 0; +long _dst_off = 60 * 60; +int _daylight = 0; +char *_tzname[2] = {ntstr, dststr}; + +#if defined(__USG) || defined(_POSIX_SOURCE) +char *tzname[2] = {ntstr, dststr}; + +#if defined(__USG) +long timezone = 0; +int daylight = 0; +#endif +#endif + +static struct dsttype { + char ds_type; /* Unknown, Julian, Zero-based or M */ + int ds_date[3]; /* months, weeks, days */ + long ds_sec; /* usually 02:00:00 */ +} dststart = { 'U', { 0, 0, 0 }, 2 * 60 * 60 } + , dstend = { 'U', { 0, 0, 0 }, 2 * 60 * 60 }; + +const char *_days[] = { + "Sunday", "Monday", "Tuesday", "Wednesday", + "Thursday", "Friday", "Saturday" + }; + +const char *_months[] = { + "January", "February", "March", + "April", "May", "June", + "July", "August", "September", + "October", "November", "December" + }; + +const int _ytab[2][12] = { + { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, + { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } + }; + +static const char * +parseZoneName(register char *buf, register const char *p) +{ + register int n = 0; + + if (*p == ':') return NULL; + while (*p && !isdigit(*p) && *p != ',' && *p != '-' && *p != '+') { + if (n < TZ_LEN) + *buf++ = *p; + p++; + n++; + } + if (n < 3) return NULL; /* error */ + *buf = '\0'; + return p; +} + +static const char * +parseTime(register long *tm, const char *p, register struct dsttype *dst) +{ + register int n = 0; + register const char *q = p; + char ds_type = (dst ? dst->ds_type : '\0'); + + if (dst) dst->ds_type = 'U'; + + *tm = 0; + while(*p >= '0' && *p <= '9') { + n = 10 * n + (*p++ - '0'); + } + if (q == p) return NULL; /* "The hour shall be required" */ + if (n < 0 || n >= 24) return NULL; + *tm = n * 60 * 60; + if (*p == ':') { + p++; + n = 0; + while(*p >= '0' && *p <= '9') { + n = 10 * n + (*p++ - '0'); + } + if (q == p) return NULL; /* format error */ + if (n < 0 || n >= 60) return NULL; + *tm += n * 60; + if (*p == ':') { + p++; + n = 0; + while(*p >= '0' && *p <= '9') { + n = 10 * n + (*p++ - '0'); + } + if (q == p) return NULL; /* format error */ + if (n < 0 || n >= 60) return NULL; + *tm += n; + } + } + if (dst) { + dst->ds_type = ds_type; + dst->ds_sec = *tm; + } + return p; +} + +static const char * +parseDate(register char *buf, register const char *p, struct dsttype *dstinfo) +{ + register const char *q; + register int n = 0; + int cnt = 0; + const int bnds[3][2] = { { 1, 12 }, + { 1, 5 }, + { 0, 6} + }; + char ds_type; + + if (*p != 'M') { + if (*p == 'J') { + *buf++ = *p++; + ds_type = 'J'; + } + else ds_type = 'Z'; + q = p; + while(*p >= '0' && *p <= '9') { + n = 10 * n + (*p - '0'); + *buf++ = *p++; + } + if (q == p) return NULL; /* format error */ + if (n < (ds_type == 'J') || n > 365) return NULL; + dstinfo->ds_type = ds_type; + dstinfo->ds_date[0] = n; + return p; + } + ds_type = 'M'; + do { + *buf++ = *p++; + q = p; + n = 0; + while(*p >= '0' && *p <= '9') { + n = 10 * n + (*p - '0'); + *buf++ = *p++; + } + if (q == p) return NULL; /* format error */ + if (n < bnds[cnt][0] || n > bnds[cnt][1]) return NULL; + dstinfo->ds_date[cnt] = n; + cnt++; + } while (cnt < 3 && *p == '.'); + if (cnt != 3) return NULL; + *buf = '\0'; + dstinfo->ds_type = ds_type; + return p; +} + +static const char * +parseRule(register char *buf, register const char *p) +{ + long time; + register const char *q; + + if (!(p = parseDate(buf, p, &dststart))) return NULL; + buf += strlen(buf); + if (*p == '/') { + q = ++p; + if (!(p = parseTime(&time, p, &dststart))) return NULL; + while( p != q) *buf++ = *q++; + } + if (*p != ',') return NULL; + p++; + if (!(p = parseDate(buf, p, &dstend))) return NULL; + buf += strlen(buf); + if (*p == '/') { + q = ++p; + if (!(p = parseTime(&time, p, &dstend))) return NULL; + while(*buf++ = *q++); + } + if (*p) return NULL; + return p; +} + +/* The following routine parses timezone information in POSIX-format. For + * the requirements, see IEEE Std 1003.1-1988 section 8.1.1. + * The function returns as soon as it spots an error. + */ +static void +parseTZ(const char *p) +{ + long tz, dst = 60 * 60, sign = 1; + static char lastTZ[2 * RULE_LEN]; + static char buffer[RULE_LEN]; + + if (!p) return; + + if (*p == ':') { + /* + * According to POSIX, this is implementation defined. + * Since it depends on the particular operating system, we + * can do nothing. + */ + return; + } + + if (!strcmp(lastTZ, p)) return; /* nothing changed */ + + *_tzname[0] = '\0'; + *_tzname[1] = '\0'; + dststart.ds_type = 'U'; + dststart.ds_sec = 2 * 60 * 60; + dstend.ds_type = 'U'; + dstend.ds_sec = 2 * 60 * 60; + + if (strlen(p) > 2 * RULE_LEN) return; + strcpy(lastTZ, p); + + if (!(p = parseZoneName(buffer, p))) return; + + if (*p == '-') { + sign = -1; + p++; + } else if (*p == '+') p++; + + if (!(p = parseTime(&tz, p, NULL))) return; + tz *= sign; + _timezone = tz; + strncpy(_tzname[0], buffer, TZ_LEN); + + if (!(_daylight = (*p != '\0'))) return; + + buffer[0] = '\0'; + if (!(p = parseZoneName(buffer, p))) return; + strncpy(_tzname[1], buffer, TZ_LEN); + + buffer[0] = '\0'; + if (*p && (*p != ',')) + if (!(p = parseTime(&dst, p, NULL))) return; + _dst_off = dst; /* dst was initialized to 1 hour */ + if (*p) { + if (*p != ',') return; + p++; + if (strlen(p) > RULE_LEN) return; + if (!(p = parseRule(buffer, p))) return; + } +} + +void +_tzset(void) +{ +#if defined(__BSD4_2) + + struct timeval tv; + struct timezone tz; + + _gettimeofday(&tv, &tz); + _daylight = tz.tz_dsttime; + _timezone = tz.tz_minuteswest * 60L; + +#elif !defined(_POSIX_SOURCE) && !defined(__USG) + +#if !defined(_MINIX) /* MINIX has no ftime() */ + struct timeb time; + + _ftime(&time); + _timezone = time.timezone * 60L; + _daylight = time.dstflag; +#endif + +#endif /* !_POSIX_SOURCE && !__USG */ + + parseTZ(getenv("TZ")); /* should go inside #if */ + +#if defined(__USG) || defined(_POSIX_SOURCE) + tzname[0] = _tzname[0]; + tzname[1] = _tzname[1]; +#if defined(__USG) + timezone = _timezone; + daylight = _daylight; +#endif +#endif /* __USG || _POSIX_SOURCE */ +} + +static int +last_sunday(register int day, register struct tm *timep) +{ + int first = FIRSTSUNDAY(timep); + + if (day >= 58 && LEAPYEAR(YEAR0 + timep->tm_year)) day++; + if (day < first) return first; + return day - (day - first) % 7; +} + +static int +date_of(register struct dsttype *dst, struct tm *timep) +{ + int leap = LEAPYEAR(YEAR0 + timep->tm_year); + int firstday, tmpday; + register int day, month; + + if (dst->ds_type != 'M') { + return dst->ds_date[0] - + (dst->ds_type == 'J' + && leap + && dst->ds_date[0] < 58); + } + day = 0; + month = 1; + while (month < dst->ds_date[0]) { + day += _ytab[leap][month - 1]; + month++; + } + firstday = (day + FIRSTDAYOF(timep)) % 7; + tmpday = day; + day += (dst->ds_date[2] - firstday + 7) % 7 + + 7 * (dst->ds_date[1] - 1); + if (day >= tmpday + _ytab[leap][month]) day -= 7; + return day; +} + +/* + * The default dst transitions are those for Western Europe (except Great + * Britain). + */ +unsigned +_dstget(register struct tm *timep) +{ + int begindst, enddst; + register struct dsttype *dsts = &dststart, *dste = &dstend; + int do_dst = 0; + + if (_daylight == -1) + _tzset(); + + timep->tm_isdst = _daylight; + if (!_daylight) return 0; + + if (dsts->ds_type != 'U') + begindst = date_of(dsts, timep); + else begindst = last_sunday(89, timep); /* last Sun before Apr */ + if (dste->ds_type != 'U') + enddst = date_of(dste, timep); + else enddst = last_sunday(272, timep); /* last Sun in Sep */ + + /* assume begindst != enddst (otherwise it would be no use) */ + if (begindst < enddst) { /* northern hemisphere */ + if (timep->tm_yday > begindst && timep->tm_yday < enddst) + do_dst = 1; + } else { /* southern hemisphere */ + if (timep->tm_yday > begindst || timep->tm_yday < enddst) + do_dst = 1; + } + + if (!do_dst + && (timep->tm_yday == begindst || timep->tm_yday == enddst)) { + long dsttranssec; /* transition when day is this old */ + long cursec; + + if (timep->tm_yday == begindst) + dsttranssec = dsts->ds_sec; + else dsttranssec = dste->ds_sec; + cursec = ((timep->tm_hour * 60) + timep->tm_min) * 60L + + timep->tm_sec; + + if ((timep->tm_yday == begindst && cursec >= dsttranssec) + || (timep->tm_yday == enddst && cursec < dsttranssec)) + do_dst = 1; + } + if (do_dst) return _dst_off; + timep->tm_isdst = 0; + return 0; +} diff --git a/lib/ansi/mktime.c b/lib/ansi/mktime.c new file mode 100755 index 000000000..fd9587044 --- /dev/null +++ b/lib/ansi/mktime.c @@ -0,0 +1,132 @@ +/* + * mktime - convert local time into calendar time + */ +/* $Header$ */ + +/* Michael A. Temari <temari@ix.netcom.com> 03/01/96 */ +/* - fixed bug is structure fixup code */ + +#include <time.h> +#include <limits.h> +#include "loc_time.h" + +/* The code assumes that unsigned long can be converted to time_t. + * A time_t should not be wider than unsigned long, since this would mean + * that the check for overflow at the end could fail. + */ +time_t +mktime(register struct tm *timep) +{ + register long day, year; + register int tm_year; + int yday, month; + register unsigned long seconds; + int overflow; + unsigned dst; + + timep->tm_min += timep->tm_sec / 60; + timep->tm_sec %= 60; + if (timep->tm_sec < 0) { + timep->tm_sec += 60; + timep->tm_min--; + } + timep->tm_hour += timep->tm_min / 60; + timep->tm_min = timep->tm_min % 60; + if (timep->tm_min < 0) { + timep->tm_min += 60; + timep->tm_hour--; + } + day = timep->tm_hour / 24; + timep->tm_hour= timep->tm_hour % 24; + if (timep->tm_hour < 0) { + timep->tm_hour += 24; + day--; + } + timep->tm_year += timep->tm_mon / 12; + timep->tm_mon %= 12; + if (timep->tm_mon < 0) { + timep->tm_mon += 12; + timep->tm_year--; + } + day += (timep->tm_mday - 1); + while (day < 0) { + if(--timep->tm_mon < 0) { + timep->tm_year--; + timep->tm_mon = 11; + } + day += _ytab[LEAPYEAR(YEAR0 + timep->tm_year)][timep->tm_mon]; + } + while (day >= _ytab[LEAPYEAR(YEAR0 + timep->tm_year)][timep->tm_mon]) { + day -= _ytab[LEAPYEAR(YEAR0 + timep->tm_year)][timep->tm_mon]; + if (++(timep->tm_mon) == 12) { + timep->tm_mon = 0; + timep->tm_year++; + } + } + timep->tm_mday = day + 1; + _tzset(); /* set timezone and dst info */ + year = EPOCH_YR; + if (timep->tm_year < year - YEAR0) return (time_t)-1; + seconds = 0; + day = 0; /* means days since day 0 now */ + overflow = 0; + + /* Assume that when day becomes negative, there will certainly + * be overflow on seconds. + * The check for overflow needs not to be done for leapyears + * divisible by 400. + * The code only works when year (1970) is not a leapyear. + */ +#if EPOCH_YR != 1970 +#error EPOCH_YR != 1970 +#endif + tm_year = timep->tm_year + YEAR0; + + if (LONG_MAX / 365 < tm_year - year) overflow++; + day = (tm_year - year) * 365; + if (LONG_MAX - day < (tm_year - year) / 4 + 1) overflow++; + day += (tm_year - year) / 4 + + ((tm_year % 4) && tm_year % 4 < year % 4); + day -= (tm_year - year) / 100 + + ((tm_year % 100) && tm_year % 100 < year % 100); + day += (tm_year - year) / 400 + + ((tm_year % 400) && tm_year % 400 < year % 400); + + yday = month = 0; + while (month < timep->tm_mon) { + yday += _ytab[LEAPYEAR(tm_year)][month]; + month++; + } + yday += (timep->tm_mday - 1); + if (day + yday < 0) overflow++; + day += yday; + + timep->tm_yday = yday; + timep->tm_wday = (day + 4) % 7; /* day 0 was thursday (4) */ + + seconds = ((timep->tm_hour * 60L) + timep->tm_min) * 60L + timep->tm_sec; + + if ((TIME_MAX - seconds) / SECS_DAY < day) overflow++; + seconds += day * SECS_DAY; + + /* Now adjust according to timezone and daylight saving time */ + + if (((_timezone > 0) && (TIME_MAX - _timezone < seconds)) + || ((_timezone < 0) && (seconds < -_timezone))) + overflow++; + seconds += _timezone; + + if (timep->tm_isdst < 0) + dst = _dstget(timep); + else if (timep->tm_isdst) + dst = _dst_off; + else dst = 0; + + if (dst > seconds) overflow++; /* dst is always non-negative */ + seconds -= dst; + + if (overflow) return (time_t)-1; + + if ((time_t)seconds != seconds) return (time_t)-1; + return (time_t)seconds; +} diff --git a/lib/ansi/qsort.c b/lib/ansi/qsort.c new file mode 100755 index 000000000..c9b2d33c4 --- /dev/null +++ b/lib/ansi/qsort.c @@ -0,0 +1,139 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> + +static void qsort1(char *, char *, size_t); +static int (*qcompar)(const char *, const char *); +static void qexchange(char *, char *, size_t); +static void q3exchange(char *, char *, char *, size_t); + +void +qsort(void *base, size_t nel, size_t width, + int (*compar)(const void *, const void *)) +{ + /* when nel is 0, the expression '(nel - 1) * width' is wrong */ + if (!nel) return; + qcompar = (int (*)(const char *, const char *)) compar; + qsort1(base, (char *)base + (nel - 1) * width, width); +} + +static void +qsort1(char *a1, char *a2, register size_t width) +{ + register char *left, *right; + register char *lefteq, *righteq; + int cmp; + + for (;;) { + if (a2 <= a1) return; + left = a1; + right = a2; + lefteq = righteq = a1 + width * (((a2-a1)+width)/(2*width)); + /* + Pick an element in the middle of the array. + We will collect the equals around it. + "lefteq" and "righteq" indicate the left and right + bounds of the equals respectively. + Smaller elements end up left of it, larger elements end + up right of it. + */ +again: + while (left < lefteq && (cmp = (*qcompar)(left, lefteq)) <= 0) { + if (cmp < 0) { + /* leave it where it is */ + left += width; + } + else { + /* equal, so exchange with the element to + the left of the "equal"-interval. + */ + lefteq -= width; + qexchange(left, lefteq, width); + } + } + while (right > righteq) { + if ((cmp = (*qcompar)(right, righteq)) < 0) { + /* smaller, should go to left part + */ + if (left < lefteq) { + /* yes, we had a larger one at the + left, so we can just exchange + */ + qexchange(left, right, width); + left += width; + right -= width; + goto again; + } + /* no more room at the left part, so we + move the "equal-interval" one place to the + right, and the smaller element to the + left of it. + This is best expressed as a three-way + exchange. + */ + righteq += width; + q3exchange(left, righteq, right, width); + lefteq += width; + left = lefteq; + } + else if (cmp == 0) { + /* equal, so exchange with the element to + the right of the "equal-interval" + */ + righteq += width; + qexchange(right, righteq, width); + } + else /* just leave it */ right -= width; + } + if (left < lefteq) { + /* larger element to the left, but no more room, + so move the "equal-interval" one place to the + left, and the larger element to the right + of it. + */ + lefteq -= width; + q3exchange(right, lefteq, left, width); + righteq -= width; + right = righteq; + goto again; + } + /* now sort the "smaller" part */ + qsort1(a1, lefteq - width, width); + /* and now the larger, saving a subroutine call + because of the for(;;) + */ + a1 = righteq + width; + } + /*NOTREACHED*/ +} + +static void +qexchange(register char *p, register char *q, + register size_t n) +{ + register int c; + + while (n-- > 0) { + c = *p; + *p++ = *q; + *q++ = c; + } +} + +static void +q3exchange(register char *p, register char *q, register char *r, + register size_t n) +{ + register int c; + + while (n-- > 0) { + c = *p; + *p++ = *r; + *r++ = *q; + *q++ = c; + } +} diff --git a/lib/ansi/raise.c b/lib/ansi/raise.c new file mode 100755 index 000000000..fb444291a --- /dev/null +++ b/lib/ansi/raise.c @@ -0,0 +1,21 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#if defined(_POSIX_SOURCE) +#include <sys/types.h> +#endif +#include <signal.h> + +int _kill(int pid, int sig); +pid_t _getpid(void); + +int +raise(int sig) +{ + if (sig < 0 || sig > _NSIG) + return -1; + return _kill(_getpid(), sig); +} diff --git a/lib/ansi/rand.c b/lib/ansi/rand.c new file mode 100755 index 000000000..82afb2e86 --- /dev/null +++ b/lib/ansi/rand.c @@ -0,0 +1,20 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> + +static unsigned long int next = 1; + +int rand(void) +{ + next = next * 1103515245 + 12345; + return (unsigned int)(next/(2 * (RAND_MAX +1L)) % (RAND_MAX+1L)); +} + +void srand(unsigned int seed) +{ + next = seed; +} diff --git a/lib/ansi/setlocale.c b/lib/ansi/setlocale.c new file mode 100755 index 000000000..87bb74a55 --- /dev/null +++ b/lib/ansi/setlocale.c @@ -0,0 +1,28 @@ +/* + * setlocale - set the programs locale + */ +/* $Header$ */ + +#include <locale.h> +#include <string.h> + +struct lconv _lc; + +char * +setlocale(int category, const char *locale) +{ + if (!locale) return "C"; + if (*locale && strcmp(locale, "C")) return (char *)NULL; + + switch(category) { + case LC_ALL: + case LC_CTYPE: + case LC_COLLATE: + case LC_TIME: + case LC_NUMERIC: + case LC_MONETARY: + return *locale ? (char *)locale : "C"; + default: + return (char *)NULL; + } +} diff --git a/lib/ansi/sigmisc.c b/lib/ansi/sigmisc.c new file mode 100755 index 000000000..b58aee2ad --- /dev/null +++ b/lib/ansi/sigmisc.c @@ -0,0 +1,39 @@ +/* + * sigmisc.c - used to get a signal mask + */ +/* $Header$ */ + +#if defined(_POSIX_SOURCE) + +/* This can't be done in setjmp.e, since SIG_SETMASK is defined in + * <signal.h>. This is a C-file, which can't be included. + */ + +#include <sys/types.h> +#include <signal.h> +#include <stddef.h> + +int _sigprocmask(int, sigset_t *, sigset_t *); + +static void +__testsigset(void) { + /* This switch compiles when a sigset_t has the right size. */ + switch(0) { + case 0: + case sizeof(sigset_t) <= sizeof(long): break; + } +} + +void +__newsigset(sigset_t *p) +{ + /* The SIG_SETMASK is not significant */ + _sigprocmask(SIG_SETMASK, NULL, p); +} + +void +__oldsigset(sigset_t *p) +{ + _sigprocmask(SIG_SETMASK, p, NULL); +} +#endif /* _POSIX_SOURCE */ diff --git a/lib/ansi/signal.c b/lib/ansi/signal.c new file mode 100755 index 000000000..a8cdca1fe --- /dev/null +++ b/lib/ansi/signal.c @@ -0,0 +1,35 @@ +/* SYSVR4 and ANSI compatible signal(2). */ + +#include <lib.h> +#define sigaction _sigaction +#define sigemptyset _sigemptyset +#include <signal.h> + +PUBLIC sighandler_t signal(sig, disp) +int sig; /* signal number */ +sighandler_t disp; /* signal handler, or SIG_DFL, or SIG_IGN */ +{ + struct sigaction sa, osa; + + if (sig <= 0 || sig > _NSIG || sig == SIGKILL) { + errno = EINVAL; + return(SIG_ERR); + } + sigemptyset(&sa.sa_mask); + +#ifdef WANT_UNRELIABLE_SIGNALS + /* Allow the signal being handled to interrupt the signal handler. */ + sa.sa_flags = SA_NODEFER; + + /* When signal is caught, reset signal handler to SIG_DFL for all but + * SIGILL and SIGTRAP. + */ + if (sig != SIGILL && sig != SIGTRAP) sa.sa_flags |= SA_RESETHAND; +#else + sa.sa_flags = 0; +#endif + + sa.sa_handler = disp; + if (sigaction(sig, &sa, &osa) < 0) return(SIG_ERR); + return(osa.sa_handler); +} diff --git a/lib/ansi/strcat.c b/lib/ansi/strcat.c new file mode 100755 index 000000000..133bd3189 --- /dev/null +++ b/lib/ansi/strcat.c @@ -0,0 +1,20 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +char * +strcat(char *ret, register const char *s2) +{ + register char *s1 = ret; + + while (*s1++ != '\0') + /* EMPTY */ ; + s1--; + while (*s1++ = *s2++) + /* EMPTY */ ; + return ret; +} diff --git a/lib/ansi/strchr.c b/lib/ansi/strchr.c new file mode 100755 index 000000000..4c738c39c --- /dev/null +++ b/lib/ansi/strchr.c @@ -0,0 +1,18 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +char * +strchr(register const char *s, register int c) +{ + c = (char) c; + + while (c != *s) { + if (*s++ == '\0') return NULL; + } + return (char *)s; +} diff --git a/lib/ansi/strcmp.c b/lib/ansi/strcmp.c new file mode 100755 index 000000000..bfe801905 --- /dev/null +++ b/lib/ansi/strcmp.c @@ -0,0 +1,20 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Id$ */ + +#include <string.h> + +int +strcmp(register const char *s1, register const char *s2) +{ + while (*s1 == *s2++) { + if (*s1++ == '\0') { + return 0; + } + } + if (*s1 == '\0') return -1; + if (*--s2 == '\0') return 1; + return (unsigned char) *s1 - (unsigned char) *s2; +} diff --git a/lib/ansi/strcoll.c b/lib/ansi/strcoll.c new file mode 100755 index 000000000..0d2ddda96 --- /dev/null +++ b/lib/ansi/strcoll.c @@ -0,0 +1,19 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> +#include <locale.h> + +int +strcoll(register const char *s1, register const char *s2) +{ + while (*s1 == *s2++) { + if (*s1++ == '\0') { + return 0; + } + } + return *s1 - *--s2; +} diff --git a/lib/ansi/strcpy.c b/lib/ansi/strcpy.c new file mode 100755 index 000000000..914ceaf6d --- /dev/null +++ b/lib/ansi/strcpy.c @@ -0,0 +1,18 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +char * +strcpy(char *ret, register const char *s2) +{ + register char *s1 = ret; + + while (*s1++ = *s2++) + /* EMPTY */ ; + + return ret; +} diff --git a/lib/ansi/strcspn.c b/lib/ansi/strcspn.c new file mode 100755 index 000000000..fbdc1d022 --- /dev/null +++ b/lib/ansi/strcspn.c @@ -0,0 +1,21 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +size_t +strcspn(const char *string, const char *notin) +{ + register const char *s1, *s2; + + for (s1 = string; *s1; s1++) { + for(s2 = notin; *s2 != *s1 && *s2; s2++) + /* EMPTY */ ; + if (*s2) + break; + } + return s1 - string; +} diff --git a/lib/ansi/strerror.c b/lib/ansi/strerror.c new file mode 100755 index 000000000..ee110ebfe --- /dev/null +++ b/lib/ansi/strerror.c @@ -0,0 +1,22 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +/* + * I don't know why, but X3J11 says that strerror() should be in declared + * in <string.h>. That is why the function is defined here. + */ +char * +strerror(register int errnum) +{ + extern const char *_sys_errlist[]; + extern const int _sys_nerr; + + if (errnum < 0 || errnum >= _sys_nerr) + return "unknown error"; + return (char *)_sys_errlist[errnum]; +} diff --git a/lib/ansi/strftime.c b/lib/ansi/strftime.c new file mode 100755 index 000000000..5f4056270 --- /dev/null +++ b/lib/ansi/strftime.c @@ -0,0 +1,172 @@ +/* + * strftime - convert a structure to a string, controlled by an argument + */ +/* $Header$ */ + +#include <time.h> +#include "loc_time.h" + +/* The width can be -1 in both s_prnt() as in u_prnt(). This + * indicates that as many characters as needed should be printed. + */ +static char * +s_prnt(char *s, size_t maxsize, const char *str, int width) +{ + while (width > 0 || (width < 0 && *str)) { + if (!maxsize) break; + *s++ = *str++; + maxsize--; + width--; + } + return s; +} + +static char * +u_prnt(char *s, size_t maxsize, unsigned val, int width) +{ + int c; + + c = val % 10; + val = val / 10; + if (--width > 0 || (width < 0 && val != 0)) + s = u_prnt(s, (maxsize ? maxsize - 1 : 0), val, width); + if (maxsize) *s++ = c + '0'; + return s; +} + +size_t +strftime(char *s, size_t maxsize, + const char *format, const struct tm *timeptr) +{ + size_t n; + char *firsts, *olds; + + if (!format) return 0; + + _tzset(); /* for %Z conversion */ + firsts = s; + while (maxsize && *format) { + while (maxsize && *format && *format != '%') { + *s++ = *format++; + maxsize--; + } + if (!maxsize || !*format) break; + format++; + + olds = s; + switch (*format++) { + case 'a': + s = s_prnt(s, maxsize, + _days[timeptr->tm_wday], ABB_LEN); + maxsize -= s - olds; + break; + case 'A': + s = s_prnt(s, maxsize, _days[timeptr->tm_wday], -1); + maxsize -= s - olds; + break; + case 'b': + s = s_prnt(s, maxsize, + _months[timeptr->tm_mon], ABB_LEN); + maxsize -= s - olds; + break; + case 'B': + s = s_prnt(s, maxsize, _months[timeptr->tm_mon], -1); + maxsize -= s - olds; + break; + case 'c': + n = strftime(s, maxsize, + "%a %b %d %H:%M:%S %Y", timeptr); + if (n) maxsize -= n; + else maxsize = 0; + s += n; + break; + case 'd': + s = u_prnt(s, maxsize, timeptr->tm_mday, 2); + maxsize -= s - olds; + break; + case 'H': + s = u_prnt(s, maxsize, timeptr->tm_hour, 2); + maxsize -= s - olds; + break; + case 'I': + s = u_prnt(s, maxsize, + (timeptr->tm_hour + 11) % 12 + 1, 2); + maxsize -= s - olds; + break; + case 'j': + s = u_prnt(s, maxsize, timeptr->tm_yday + 1, 3); + maxsize -= s - olds; + break; + case 'm': + s = u_prnt(s, maxsize, timeptr->tm_mon + 1, 2); + maxsize -= s - olds; + break; + case 'M': + s = u_prnt(s, maxsize, timeptr->tm_min, 2); + maxsize -= s - olds; + break; + case 'p': + s = s_prnt(s, maxsize, + (timeptr->tm_hour < 12) ? "AM" : "PM", 2); + maxsize -= s - olds; + break; + case 'S': + s = u_prnt(s, maxsize, timeptr->tm_sec, 2); + maxsize -= s - olds; + break; + case 'U': + s = u_prnt(s, maxsize, /* ??? */ + (timeptr->tm_yday + 7 - timeptr->tm_wday) / 7, 2); + maxsize -= s - olds; + break; + case 'w': + s = u_prnt(s, maxsize, timeptr->tm_wday, 1); + maxsize -= s - olds; + break; + case 'W': + s = u_prnt(s, maxsize, /* ??? */ + (timeptr->tm_yday+7-(timeptr->tm_wday+6)%7)/7,2); + maxsize -= s - olds; + break; + case 'x': + n = strftime(s, maxsize, "%a %b %d %Y", timeptr); + if (n) maxsize -= n; + else maxsize = 0; + s += n; + break; + case 'X': + n = strftime(s, maxsize, "%H:%M:%S", timeptr); + if (n) maxsize -= n; + else maxsize = 0; + s += n; + break; + case 'y': + s = u_prnt(s, maxsize, timeptr->tm_year % 100, 2); + maxsize -= s - olds; + break; + case 'Y': + s = u_prnt(s, maxsize, timeptr->tm_year + YEAR0, -1); + maxsize -= s - olds; + break; + case 'Z': + s = s_prnt(s, maxsize, + _tzname[(timeptr->tm_isdst > 0)], -1); + maxsize -= s - olds; + break; + case '%': + *s++ = '%'; + maxsize--; + break; + default: + /* A conversion error. Leave the loop. */ + while (*format) format++; + break; + } + + } + if (maxsize) { + *s = '\0'; + return s - firsts; + } + return 0; /* The buffer is full */ +} diff --git a/lib/ansi/strlen.c b/lib/ansi/strlen.c new file mode 100755 index 000000000..ff673e1df --- /dev/null +++ b/lib/ansi/strlen.c @@ -0,0 +1,18 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +size_t +strlen(const char *org) +{ + register const char *s = org; + + while (*s++) + /* EMPTY */ ; + + return --s - org; +} diff --git a/lib/ansi/strncat.c b/lib/ansi/strncat.c new file mode 100755 index 000000000..6a0a3b0ca --- /dev/null +++ b/lib/ansi/strncat.c @@ -0,0 +1,25 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +char * +strncat(char *ret, register const char *s2, size_t n) +{ + register char *s1 = ret; + + if (n > 0) { + while (*s1++) + /* EMPTY */ ; + s1--; + while (*s1++ = *s2++) { + if (--n > 0) continue; + *s1 = '\0'; + break; + } + return ret; + } else return s1; +} diff --git a/lib/ansi/strncmp.c b/lib/ansi/strncmp.c new file mode 100755 index 000000000..950ede4fe --- /dev/null +++ b/lib/ansi/strncmp.c @@ -0,0 +1,26 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Id$ */ + +#include <string.h> + +int +strncmp(register const char *s1, register const char *s2, register size_t n) +{ + if (n) { + do { + if (*s1 != *s2++) + break; + if (*s1++ == '\0') + return 0; + } while (--n > 0); + if (n > 0) { + if (*s1 == '\0') return -1; + if (*--s2 == '\0') return 1; + return (unsigned char) *s1 - (unsigned char) *s2; + } + } + return 0; +} diff --git a/lib/ansi/strncpy.c b/lib/ansi/strncpy.c new file mode 100755 index 000000000..ed3195fdc --- /dev/null +++ b/lib/ansi/strncpy.c @@ -0,0 +1,24 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +char * +strncpy(char *ret, register const char *s2, register size_t n) +{ + register char *s1 = ret; + + if (n>0) { + while((*s1++ = *s2++) && --n > 0) + /* EMPTY */ ; + if ((*--s2 == '\0') && --n > 0) { + do { + *s1++ = '\0'; + } while(--n > 0); + } + } + return ret; +} diff --git a/lib/ansi/strpbrk.c b/lib/ansi/strpbrk.c new file mode 100755 index 000000000..5f08731bb --- /dev/null +++ b/lib/ansi/strpbrk.c @@ -0,0 +1,22 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +char * +strpbrk(register const char *string, register const char *brk) +{ + register const char *s1; + + while (*string) { + for (s1 = brk; *s1 && *s1 != *string; s1++) + /* EMPTY */ ; + if (*s1) + return (char *)string; + string++; + } + return (char *)NULL; +} diff --git a/lib/ansi/strrchr.c b/lib/ansi/strrchr.c new file mode 100755 index 000000000..0d350532b --- /dev/null +++ b/lib/ansi/strrchr.c @@ -0,0 +1,22 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +char * +strrchr(register const char *s, int c) +{ + register const char *result = NULL; + + c = (char) c; + + do { + if (c == *s) + result = s; + } while (*s++ != '\0'); + + return (char *)result; +} diff --git a/lib/ansi/strspn.c b/lib/ansi/strspn.c new file mode 100755 index 000000000..322b70363 --- /dev/null +++ b/lib/ansi/strspn.c @@ -0,0 +1,21 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +size_t +strspn(const char *string, const char *in) +{ + register const char *s1, *s2; + + for (s1 = string; *s1; s1++) { + for (s2 = in; *s2 && *s2 != *s1; s2++) + /* EMPTY */ ; + if (*s2 == '\0') + break; + } + return s1 - string; +} diff --git a/lib/ansi/strstr.c b/lib/ansi/strstr.c new file mode 100755 index 000000000..7d15a70f0 --- /dev/null +++ b/lib/ansi/strstr.c @@ -0,0 +1,19 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +char * +strstr(register const char *s, register const char *wanted) +{ + register const size_t len = strlen(wanted); + + if (len == 0) return (char *)s; + while (*s != *wanted || strncmp(s, wanted, len)) + if (*s++ == '\0') + return (char *)NULL; + return (char *)s; +} diff --git a/lib/ansi/strtok.c b/lib/ansi/strtok.c new file mode 100755 index 000000000..f0788499c --- /dev/null +++ b/lib/ansi/strtok.c @@ -0,0 +1,31 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +char * +strtok(register char *string, const char *separators) +{ + register char *s1, *s2; + static char *savestring; + + if (string == NULL) { + string = savestring; + if (string == NULL) return (char *)NULL; + } + + s1 = string + strspn(string, separators); + if (*s1 == '\0') { + savestring = NULL; + return (char *)NULL; + } + + s2 = strpbrk(s1, separators); + if (s2 != NULL) + *s2++ = '\0'; + savestring = s2; + return s1; +} diff --git a/lib/ansi/strtol.c b/lib/ansi/strtol.c new file mode 100755 index 000000000..9a200cc16 --- /dev/null +++ b/lib/ansi/strtol.c @@ -0,0 +1,101 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <ctype.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> + +static unsigned long +string2long(register const char *nptr, char **endptr, + int base, int is_signed); + +long int +strtol(register const char *nptr, char **endptr, int base) +{ + return (signed long)string2long(nptr, endptr, base, 1); +} + +unsigned long int +strtoul(register const char *nptr, char **endptr, int base) +{ + return (unsigned long)string2long(nptr, endptr, base, 0); +} + +#define between(a, c, z) ((unsigned) ((c) - (a)) <= (unsigned) ((z) - (a))) + +static unsigned long +string2long(register const char *nptr, char ** const endptr, + int base, int is_signed) +{ + register unsigned int v; + register unsigned long val = 0; + register int c; + int ovfl = 0, sign = 1; + const char *startnptr = nptr, *nrstart; + + if (endptr) *endptr = (char *)nptr; + while (isspace(*nptr)) nptr++; + c = *nptr; + + if (c == '-' || c == '+') { + if (c == '-') sign = -1; + nptr++; + } + nrstart = nptr; /* start of the number */ + + /* When base is 0, the syntax determines the actual base */ + if (base == 0) + if (*nptr == '0') + if (*++nptr == 'x' || *nptr == 'X') { + base = 16; + nptr++; + } + else base = 8; + else base = 10; + else if (base==16 && *nptr=='0' && (*++nptr =='x' || *nptr =='X')) + nptr++; + + for (;;) { + c = *nptr; + if (between('0', c, '9')) { + v = c - '0'; + } else + if (between('a', c, 'z')) { + v = c - 'a' + 0xa; + } else + if (between('A', c, 'Z')) { + v = c - 'A' + 0xA; + } else { + break; + } + if (v >= base) break; + if (val > (ULONG_MAX - v) / base) ovfl++; + val = (val * base) + v; + nptr++; + } + if (endptr) { + if (nrstart == nptr) *endptr = (char *)startnptr; + else *endptr = (char *)nptr; + } + + if (!ovfl) { + /* Overflow is only possible when converting a signed long. */ + if (is_signed + && ( (sign < 0 && val > -(unsigned long)LONG_MIN) + || (sign > 0 && val > LONG_MAX))) + ovfl++; + } + + if (ovfl) { + errno = ERANGE; + if (is_signed) + if (sign < 0) return LONG_MIN; + else return LONG_MAX; + else return ULONG_MAX; + } + return (long) sign * val; +} diff --git a/lib/ansi/strxfrm.c b/lib/ansi/strxfrm.c new file mode 100755 index 000000000..ca32913e0 --- /dev/null +++ b/lib/ansi/strxfrm.c @@ -0,0 +1,24 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +size_t +strxfrm(register char *s1, register const char *save, register size_t n) +{ + register const char *s2 = save; + + while (*s2) { + if (n > 1) { + n--; + *s1++ = *s2++; + } else + s2++; + } + if (n > 0) + *s1++ = '\0'; + return s2 - save; +} diff --git a/lib/ansi/system.c b/lib/ansi/system.c new file mode 100755 index 000000000..8c9bd5284 --- /dev/null +++ b/lib/ansi/system.c @@ -0,0 +1,59 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#if defined(_POSIX_SOURCE) +#include <sys/types.h> +#endif +#include <stdlib.h> +#include <signal.h> + +extern pid_t _fork(void); +extern pid_t _wait(int *); +extern void _exit(int); +extern void _execve(const char *path, const char ** argv, const char ** envp); +extern int _close(int); + +#define FAIL 127 + +extern const char ***_penviron; +static const char *exec_tab[] = { + "sh", /* argv[0] */ + "-c", /* argument to the shell */ + NULL, /* to be filled with user command */ + NULL /* terminating NULL */ + }; + +int +system(const char *str) +{ + int pid, exitstatus, waitval; + int i; + + if ((pid = _fork()) < 0) return str ? -1 : 0; + + if (pid == 0) { + for (i = 3; i <= 20; i++) + _close(i); + if (!str) str = "cd ."; /* just testing for a shell */ + exec_tab[2] = str; /* fill in command */ + _execve("/bin/sh", exec_tab, *_penviron); + /* get here if execve fails ... */ + _exit(FAIL); /* see manual page */ + } + while ((waitval = _wait(&exitstatus)) != pid) { + if (waitval == -1) break; + } + if (waitval == -1) { + /* no child ??? or maybe interrupted ??? */ + exitstatus = -1; + } + if (!str) { + if (exitstatus == FAIL << 8) /* execve() failed */ + exitstatus = 0; + else exitstatus = 1; /* /bin/sh exists */ + } + return exitstatus; +} diff --git a/lib/ansi/tolower.c b/lib/ansi/tolower.c new file mode 100755 index 000000000..ff95ae0d8 --- /dev/null +++ b/lib/ansi/tolower.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int tolower(int c) { + return isupper(c) ? c - 'A' + 'a' : c ; +} diff --git a/lib/ansi/toupper.c b/lib/ansi/toupper.c new file mode 100755 index 000000000..521ae5569 --- /dev/null +++ b/lib/ansi/toupper.c @@ -0,0 +1,5 @@ +#include <ctype.h> + +int toupper(int c) { + return islower(c) ? c - 'a' + 'A' : c ; +} diff --git a/lib/ansi/tzset.c b/lib/ansi/tzset.c new file mode 100755 index 000000000..a61f94571 --- /dev/null +++ b/lib/ansi/tzset.c @@ -0,0 +1,15 @@ +/* + * tzset - set timezone information + */ +/* $Header$ */ + +/* This function is present for System V && POSIX */ + +#include <time.h> +#include "loc_time.h" + +void +tzset(void) +{ + _tzset(); /* does the job */ +} diff --git a/lib/ansi/wcstombs.c b/lib/ansi/wcstombs.c new file mode 100755 index 000000000..91a14624d --- /dev/null +++ b/lib/ansi/wcstombs.c @@ -0,0 +1,21 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> +#include <locale.h> +#include <limits.h> + +size_t +wcstombs(register char *s, register const wchar_t *pwcs, size_t n) +{ + register int i = n; + + while (--i >= 0) { + if (!(*s++ = *pwcs++)) + break; + } + return n - i - 1; +} diff --git a/lib/ansi/wctomb.c b/lib/ansi/wctomb.c new file mode 100755 index 000000000..9f273c13e --- /dev/null +++ b/lib/ansi/wctomb.c @@ -0,0 +1,20 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> +#include <limits.h> + +int +/* was: wctomb(char *s, wchar_t wchar) + * This conflicts with prototype, so it was changed to: + */ +wctomb(char *s, wchar_t wchar) +{ + if (!s) return 0; /* no state dependent codings */ + + *s = wchar; + return 1; +} diff --git a/lib/curses/Makefile b/lib/curses/Makefile new file mode 100755 index 000000000..c599be638 --- /dev/null +++ b/lib/curses/Makefile @@ -0,0 +1,165 @@ +# Makefile for lib/curses. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../libcurses.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(beep.o) \ + $(LIBRARY)(charpick.o) \ + $(LIBRARY)(curs_set.o) \ + $(LIBRARY)(cursesio.o) \ + $(LIBRARY)(endwin.o) \ + $(LIBRARY)(flash.o) \ + $(LIBRARY)(initscr.o) \ + $(LIBRARY)(longname.o) \ + $(LIBRARY)(move.o) \ + $(LIBRARY)(mvcursor.o) \ + $(LIBRARY)(newwin.o) \ + $(LIBRARY)(options.o) \ + $(LIBRARY)(overlay.o) \ + $(LIBRARY)(prntscan.o) \ + $(LIBRARY)(refresh.o) \ + $(LIBRARY)(scrreg.o) \ + $(LIBRARY)(setterm.o) \ + $(LIBRARY)(tabsize.o) \ + $(LIBRARY)(termmisc.o) \ + $(LIBRARY)(unctrl.o) \ + $(LIBRARY)(update.o) \ + $(LIBRARY)(waddch.o) \ + $(LIBRARY)(waddstr.o) \ + $(LIBRARY)(wbox.o) \ + $(LIBRARY)(wclear.o) \ + $(LIBRARY)(wclrtobot.o) \ + $(LIBRARY)(wclrtoeol.o) \ + $(LIBRARY)(wdelch.o) \ + $(LIBRARY)(wdeleteln.o) \ + $(LIBRARY)(werase.o) \ + $(LIBRARY)(wgetch.o) \ + $(LIBRARY)(wgetstr.o) \ + $(LIBRARY)(windel.o) \ + $(LIBRARY)(winmove.o) \ + $(LIBRARY)(winsch.o) \ + $(LIBRARY)(winscrol.o) \ + $(LIBRARY)(winsertln.o) \ + $(LIBRARY)(wintouch.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(beep.o): beep.c + $(CC1) beep.c + +$(LIBRARY)(charpick.o): charpick.c + $(CC1) charpick.c + +$(LIBRARY)(curs_set.o): curs_set.c + $(CC1) curs_set.c + +$(LIBRARY)(cursesio.o): cursesio.c + $(CC1) cursesio.c + +$(LIBRARY)(endwin.o): endwin.c + $(CC1) endwin.c + +$(LIBRARY)(flash.o): flash.c + $(CC1) flash.c + +$(LIBRARY)(initscr.o): initscr.c + $(CC1) initscr.c + +$(LIBRARY)(longname.o): longname.c + $(CC1) longname.c + +$(LIBRARY)(move.o): move.c + $(CC1) move.c + +$(LIBRARY)(mvcursor.o): mvcursor.c + $(CC1) mvcursor.c + +$(LIBRARY)(newwin.o): newwin.c + $(CC1) newwin.c + +$(LIBRARY)(options.o): options.c + $(CC1) options.c + +$(LIBRARY)(overlay.o): overlay.c + $(CC1) overlay.c + +$(LIBRARY)(prntscan.o): prntscan.c + $(CC1) prntscan.c + +$(LIBRARY)(refresh.o): refresh.c + $(CC1) refresh.c + +$(LIBRARY)(scrreg.o): scrreg.c + $(CC1) scrreg.c + +$(LIBRARY)(setterm.o): setterm.c + $(CC1) setterm.c + +$(LIBRARY)(tabsize.o): tabsize.c + $(CC1) tabsize.c + +$(LIBRARY)(termmisc.o): termmisc.c + $(CC1) termmisc.c + +$(LIBRARY)(unctrl.o): unctrl.c + $(CC1) unctrl.c + +$(LIBRARY)(update.o): update.c + $(CC1) update.c + +$(LIBRARY)(waddch.o): waddch.c + $(CC1) waddch.c + +$(LIBRARY)(waddstr.o): waddstr.c + $(CC1) waddstr.c + +$(LIBRARY)(wbox.o): wbox.c + $(CC1) wbox.c + +$(LIBRARY)(wclear.o): wclear.c + $(CC1) wclear.c + +$(LIBRARY)(wclrtobot.o): wclrtobot.c + $(CC1) wclrtobot.c + +$(LIBRARY)(wclrtoeol.o): wclrtoeol.c + $(CC1) wclrtoeol.c + +$(LIBRARY)(wdelch.o): wdelch.c + $(CC1) wdelch.c + +$(LIBRARY)(wdeleteln.o): wdeleteln.c + $(CC1) wdeleteln.c + +$(LIBRARY)(werase.o): werase.c + $(CC1) werase.c + +$(LIBRARY)(wgetch.o): wgetch.c + $(CC1) wgetch.c + +$(LIBRARY)(wgetstr.o): wgetstr.c + $(CC1) wgetstr.c + +$(LIBRARY)(windel.o): windel.c + $(CC1) windel.c + +$(LIBRARY)(winmove.o): winmove.c + $(CC1) winmove.c + +$(LIBRARY)(winsch.o): winsch.c + $(CC1) winsch.c + +$(LIBRARY)(winscrol.o): winscrol.c + $(CC1) winscrol.c + +$(LIBRARY)(winsertln.o): winsertln.c + $(CC1) winsertln.c + +$(LIBRARY)(wintouch.o): wintouch.c + $(CC1) wintouch.c diff --git a/lib/curses/beep.c b/lib/curses/beep.c new file mode 100755 index 000000000..1fa9a4fff --- /dev/null +++ b/lib/curses/beep.c @@ -0,0 +1,14 @@ +#include <curses.h> +#include "curspriv.h" +#include <termcap.h> + +extern char *bl, *vb; + +/* Beep() sounds the terminal bell. */ +void beep() +{ + if (bl) + tputs(bl, 1, outc); + else if (vb) + tputs(vb, 1, outc); +} diff --git a/lib/curses/charpick.c b/lib/curses/charpick.c new file mode 100755 index 000000000..5c76db4ac --- /dev/null +++ b/lib/curses/charpick.c @@ -0,0 +1,40 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Winch(win) returns the character at the current position in */ +/* Window 'win'. */ +/****************************************************************/ + +int winch(win) +WINDOW *win; +{ + return((win->_line[win->_cury][win->_curx]) & 0xff); +} /* winch */ + +/****************************************************************/ +/* Mvinch() moves the stdscr cursor to a new position, then */ +/* Returns the character at that position. */ +/****************************************************************/ + +int mvinch(y, x) +int y; +int x; +{ + if (wmove(stdscr, y, x) == ERR) return(ERR); + return((stdscr->_line[stdscr->_cury][stdscr->_curx]) & 0xff); +} + +/****************************************************************/ +/* Mvwinch() moves the cursor of window 'win' to a new posi- */ +/* Tion, then returns the character at that position. */ +/****************************************************************/ + +int mvwinch(win, y, x) +WINDOW *win; +int y; +int x; +{ + if (wmove(win, y, x) == ERR) return(ERR); + return((win->_line[win->_cury][win->_curx]) & 0xff); +} diff --git a/lib/curses/curs_set.c b/lib/curses/curs_set.c new file mode 100755 index 000000000..3bf5ec153 --- /dev/null +++ b/lib/curses/curs_set.c @@ -0,0 +1,26 @@ +#include <curses.h> +#include "curspriv.h" +#include <termcap.h> + +extern char *vi, *ve, *vs; + +/* Sets cursor visibility to unvisible=0; normal visible=1 or very good + * visible=2. +*/ +void curs_set(visibility) +int visibility; +{ + switch (visibility) { + case 0: + if (vi) tputs(vi, 1, outc); + break; + case 1: + if (ve) tputs(ve, 1, outc); + break; + case 2: + if (vs) + tputs(vs, 1, outc); + else if (ve) + tputs(ve, 1, outc); + } +} diff --git a/lib/curses/cursesio.c b/lib/curses/cursesio.c new file mode 100755 index 000000000..22e75e40a --- /dev/null +++ b/lib/curses/cursesio.c @@ -0,0 +1,223 @@ +#include <stdlib.h> +#include <termcap.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <curses.h> +#include "curspriv.h" + +struct termios _orig_tty, _tty; +cursv _cursvar; + +WINDOW *stdscr, *curscr; +int LINES, COLS; +bool NONL; + +char termcap[1024]; /* termcap buffer */ +char tc[200]; /* area to hold string capabilities */ +char *ttytype; /* terminal type from env */ +static char *arp; /* pointer for use in tgetstr */ +char *cp; /* character pointer */ + +char *cl; /* clear screen capability */ +char *cm; /* cursor motion capability */ +char *so; /* start standout capability */ +char *se; /* end standout capability */ +char *mr; /* start of reverse */ +char *me; /* revert to normal */ +char *mb; /* start of blink */ +char *md; /* start of bold */ +char *us; /* start of underscore */ +char *ue; /* end of underscore */ +char *vi; /* cursor invisible */ +char *ve; /* cursor normal */ +char *vs; /* cursor good visible */ +char *as; /* alternative charset start */ +char *ae; /* alternative charset end */ +char *bl; /* ring the bell */ +char *vb; /* visual bell */ + +/* fatal - report error and die. Never returns */ +void fatal(s) +char *s; +{ + (void) fprintf(stderr, "curses: %s\n", s); + exit(1); +} + +/* Outc - call putchar, necessary because putchar is a macro. */ +void outc(c) +int c; +{ + putchar(c); +} + +/* Move cursor to r,c */ +void poscur(r, c) +int r, c; +{ + tputs(tgoto(cm, c, r), 1, outc); +} + +/* Clear the screen */ +void clrscr() +{ + tputs(cl, 1, outc); +} + +/* This are terminal independent characters which can be used in curses */ + +unsigned int ACS_ULCORNER; +unsigned int ACS_LLCORNER; +unsigned int ACS_URCORNER; +unsigned int ACS_LRCORNER; +unsigned int ACS_RTEE; +unsigned int ACS_LTEE; +unsigned int ACS_BTEE; +unsigned int ACS_TTEE; +unsigned int ACS_HLINE; +unsigned int ACS_VLINE; +unsigned int ACS_PLUS; +unsigned int ACS_S1; +unsigned int ACS_S9; +unsigned int ACS_DIAMOND; +unsigned int ACS_CKBOARD; +unsigned int ACS_DEGREE; +unsigned int ACS_PLMINUS; +unsigned int ACS_BULLET; +unsigned int ACS_LARROW; +unsigned int ACS_RARROW; +unsigned int ACS_DARROW; +unsigned int ACS_UARROW; +unsigned int ACS_BOARD; +unsigned int ACS_LANTERN; +unsigned int ACS_BLOCK; + +/* These defines describe the full set of grafic block characters which + * can be defined via termcap. + */ + +#define RIGHTARROW 0 +#define LEFTARROW 1 +#define DOWNARROW 2 +#define UPARROW 3 +#define FULLSQUARE 4 +#define GREYSQUARE 5 +#define EMPTYSQUARE 6 +#define LATERN 7 +#define DIAMOND 8 +#define DEGREE 9 +#define PLUSMINUS 10 +#define DOWNRIGHT 11 +#define UPRIGHT 12 +#define UPLEFT 13 +#define DOWNLEFT 14 +#define CROSS 15 +#define UPLINE 16 +#define UPMIDLINE 17 +#define MIDLINE 18 +#define DOMIDLINE 19 +#define DOWNLINE 20 +#define TEELEFT 21 +#define TEERIGHT 22 +#define TEEHEAD 23 +#define TEENORMAL 24 +#define VERTLINE 25 +#define PARAGRAPH 26 + +unsigned int _cursgraftable[27] = +{ + '>', '<', 'v', '^', '#', ':', ' ', '#', '+', '\'', '#', '+', '+', + '+', '+', '+', '-', ' ', '-', ' ', '_', '+', '+', '+', '+', '|' +}; +char _cursident[28] = "+,.-0ahI`fgjklmnopqrstuvwx~"; + +int setterm(type) +char *type; +{ + unsigned char *ac; + int i; +#ifdef TIOCGWINSZ + struct winsize wsize; +#endif + + if (tgetent(termcap, type) != 1) return ERR; + +#ifdef TIOCGWINSZ + if (ioctl(0, TIOCGWINSZ, &wsize) == 0) { + LINES = wsize.ws_row != 0 ? wsize.ws_row : tgetnum("li"); + COLS = wsize.ws_col != 0 ? wsize.ws_col : tgetnum("co"); + } else { +#endif + LINES = tgetnum("li"); + COLS = tgetnum("co"); +#ifdef TIOCGWINSZ + } +#endif + arp = tc; + cl = tgetstr("cl", &arp); + so = tgetstr("so", &arp); + se = tgetstr("se", &arp); + cm = tgetstr("cm", &arp); + mr = tgetstr("mr", &arp); + me = tgetstr("me", &arp); + mb = tgetstr("mb", &arp); + md = tgetstr("md", &arp); + us = tgetstr("us", &arp); + ue = tgetstr("ue", &arp); + vi = tgetstr("vi", &arp); + ve = tgetstr("ve", &arp); + vs = tgetstr("vs", &arp); + as = tgetstr("as", &arp); + ae = tgetstr("ae", &arp); + ac = (unsigned char *) tgetstr("ac", &arp); + bl = tgetstr("bl", &arp); + vb = tgetstr("vb", &arp); + + if (ac) { + while (*ac) { + i = 0; + while (*ac != _cursident[i]) i++; + _cursgraftable[i] = *++ac | A_ALTCHARSET; + ac++; + } + } + + ACS_ULCORNER = _cursgraftable[UPLEFT]; + ACS_LLCORNER = _cursgraftable[DOWNLEFT]; + ACS_URCORNER = _cursgraftable[UPRIGHT]; + ACS_LRCORNER = _cursgraftable[DOWNRIGHT]; + ACS_RTEE = _cursgraftable[TEERIGHT]; + ACS_LTEE = _cursgraftable[TEELEFT]; + ACS_BTEE = _cursgraftable[TEEHEAD]; + ACS_TTEE = _cursgraftable[TEENORMAL]; + ACS_HLINE = _cursgraftable[MIDLINE]; + ACS_VLINE = _cursgraftable[VERTLINE]; + ACS_PLUS = _cursgraftable[CROSS]; + ACS_S1 = _cursgraftable[UPLINE]; + ACS_S9 = _cursgraftable[DOWNLINE]; + ACS_DIAMOND = _cursgraftable[DIAMOND]; + ACS_CKBOARD = _cursgraftable[GREYSQUARE]; + ACS_DEGREE = _cursgraftable[DEGREE]; + ACS_PLMINUS = _cursgraftable[PLUSMINUS]; + ACS_BULLET = 'o'; /* where the hell is a bullet defined in + * termcap ??? */ + ACS_LARROW = _cursgraftable[LEFTARROW]; + ACS_RARROW = _cursgraftable[RIGHTARROW]; + ACS_DARROW = _cursgraftable[DOWNARROW]; + ACS_UARROW = _cursgraftable[UPARROW]; + ACS_BOARD = _cursgraftable[EMPTYSQUARE]; + ACS_LANTERN = _cursgraftable[LATERN]; + ACS_BLOCK = _cursgraftable[FULLSQUARE]; + /* Wow, I got it! */ + return OK; +} + +void gettmode() +{ + tcgetattr(0, &_orig_tty); + tcgetattr(0, &_tty); + _cursvar.echoit = (_tty.c_lflag & ECHO) != 0; + _cursvar.rawmode = (_tty.c_lflag & (ICANON|ISIG)) == 0; + _cursvar.cbrkmode = (_tty.c_lflag & (ICANON|ISIG)) == ISIG; + NONL = (_tty.c_iflag & ICRNL) != 0; +} diff --git a/lib/curses/curspriv.h b/lib/curses/curspriv.h new file mode 100755 index 000000000..af53d177b --- /dev/null +++ b/lib/curses/curspriv.h @@ -0,0 +1,36 @@ +/* Constants */ +#define _SUBWIN 1 /* window is a subwindow */ +#define _ENDLINE 2 /* last winline is last screen line */ +#define _FULLWIN 4 /* window fills screen */ +#define _SCROLLWIN 8 /* window lwr rgt is screen lwr rgt */ + +#define _NO_CHANGE -1 /* flags line edge unchanged */ +#define _BREAKCHAR 0x03 /* ^C character */ +#define _DCCHAR 0x08 /* Delete Char char (BS) */ +#define _DLCHAR 0x1b /* Delete Line char (ESC) */ +#define _GOCHAR 0x11 /* ^Q character */ +#define _PRINTCHAR 0x10 /* ^P character */ +#define _STOPCHAR 0x13 /* ^S character */ +#define NUNGETCH 10 /* max # chars to ungetch() */ + +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#define min(a,b) (((a) < (b)) ? (a) : (b)) + +/* Character mask definitions. */ +#define CHR_MSK ((int) 0x00ff) /* ASCIIZ character mask */ +#define ATR_MSK ((int) 0xff00) /* attribute mask */ +#define ATR_NRM ((int) 0x0000) /* no special attributes */ + +/* Type declarations. */ + +typedef struct { + WINDOW *tmpwin; /* window used for updates */ + int cursrow; /* position of physical cursor */ + int curscol; + bool rawmode; + bool cbrkmode; + bool echoit; +} cursv; + +/* External variables */ +extern cursv _cursvar; /* curses variables */ diff --git a/lib/curses/endwin.c b/lib/curses/endwin.c new file mode 100755 index 000000000..269cbfa55 --- /dev/null +++ b/lib/curses/endwin.c @@ -0,0 +1,18 @@ +#include <curses.h> +#include "curspriv.h" +#include <termcap.h> + +int endwin() +{ + extern char *me; + + curs_set(1); + poscur(LINES - 1, 0); + refresh(); + tputs(me, 1, outc); + delwin(stdscr); + delwin(curscr); + delwin(_cursvar.tmpwin); + resetty(); + return(OK); +} diff --git a/lib/curses/flash.c b/lib/curses/flash.c new file mode 100755 index 000000000..2de45606c --- /dev/null +++ b/lib/curses/flash.c @@ -0,0 +1,14 @@ +#include <curses.h> +#include "curspriv.h" +#include <termcap.h> + +extern char *bl, *vb; + +/* Flash() flashes the terminal screen. */ +void flash() +{ + if (vb) + tputs(vb, 1, outc); + else if (bl) + tputs(bl, 1, outc); +} diff --git a/lib/curses/initscr.c b/lib/curses/initscr.c new file mode 100755 index 000000000..7ed2f9d11 --- /dev/null +++ b/lib/curses/initscr.c @@ -0,0 +1,19 @@ +/* initscr.c - initialize the curses library */ + +#include <stdlib.h> +#include <curses.h> +#include "curspriv.h" + +WINDOW *initscr() +{ + char *term; + + if ((term = getenv("TERM")) == NULL) return NULL; + setterm(term); + gettmode(); + if ((_cursvar.tmpwin = newwin(LINES, COLS, 0, 0)) == NULL) return NULL; + if ((curscr = newwin(LINES, COLS, 0, 0)) == NULL) return NULL; + if ((stdscr = newwin(LINES, COLS, 0, 0)) == NULL) return NULL; + clearok(curscr, TRUE); + return(stdscr); +} diff --git a/lib/curses/longname.c b/lib/curses/longname.c new file mode 100755 index 000000000..ca9ac523d --- /dev/null +++ b/lib/curses/longname.c @@ -0,0 +1,12 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Longname() returns a pointer to a string describing the */ +/* User terminal. */ +/****************************************************************/ + +char *longname() +{ + return("not implemented"); +} diff --git a/lib/curses/move.c b/lib/curses/move.c new file mode 100755 index 000000000..e5059c959 --- /dev/null +++ b/lib/curses/move.c @@ -0,0 +1,18 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Wmove() moves the cursor in window 'win' to position (x,y). */ +/****************************************************************/ + +int wmove(win, y, x) +WINDOW *win; +int y; +int x; +{ + if ((x<0) || (x>win->_maxx) || (y<win->_regtop) || (y>win->_regbottom)) + return(ERR); + win->_curx = x; + win->_cury = y; + return(OK); +} diff --git a/lib/curses/mvcursor.c b/lib/curses/mvcursor.c new file mode 100755 index 000000000..e16615439 --- /dev/null +++ b/lib/curses/mvcursor.c @@ -0,0 +1,20 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Mvcur(oldy,oldx,newy,newx) the display cursor to <newy,newx> */ +/****************************************************************/ + +int mvcur(oldy, oldx, newy, newx) +int oldy; +int oldx; +int newy; +int newx; +{ + if ((newy >= LINES) || (newx >= COLS) || (newy < 0) || (newx < 0)) + return(ERR); + poscur(newy, newx); + _cursvar.cursrow = newy; + _cursvar.curscol = newx; + return(OK); +} diff --git a/lib/curses/newwin.c b/lib/curses/newwin.c new file mode 100755 index 000000000..69214707e --- /dev/null +++ b/lib/curses/newwin.c @@ -0,0 +1,144 @@ +#include <stdlib.h> +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Makenew() allocates all data for a new window except the */ +/* Actual lines themselves. */ +/****************************************************************/ + +_PROTOTYPE(static WINDOW *makenew, (int nlines, int ncols, int begy,int begx)); + +static WINDOW *makenew(num_lines, num_columns, begy, begx) +int num_lines, num_columns, begy, begx; +{ + int i; + WINDOW *win; + + /* Allocate the window structure itself */ + if ((win = (WINDOW *) malloc(sizeof(WINDOW))) == NULL) + return((WINDOW *) ERR); + + /* Allocate the line pointer array */ + if ((win->_line = (int **) calloc(num_lines, sizeof(int *))) == NULL) { + free(win); + return((WINDOW *) ERR); + } + + /* Allocate the minchng and maxchng arrays */ + if ((win->_minchng = (int *) calloc(num_lines, sizeof(int))) == NULL) { + free(win); + free(win->_line); + return((WINDOW *) ERR); + } + if ((win->_maxchng = (int *) calloc(num_lines, sizeof(int))) == NULL) { + free(win); + free(win->_line); + free(win->_minchng); + return((WINDOW *) ERR); + } + + /* Initialize window variables */ + win->_curx = 0; + win->_cury = 0; + win->_maxy = num_lines - 1; + win->_maxx = num_columns - 1; + win->_begy = begy; + win->_begx = begx; + win->_flags = 0; + win->_attrs = ATR_NRM; + win->_tabsize = 8; + win->_clear = FALSE; + win->_leave = FALSE; + win->_scroll = FALSE; + win->_nodelay = FALSE; + win->_keypad = FALSE; + win->_regtop = 0; + win->_regbottom = num_lines - 1; + + /* Init to say window unchanged */ + for (i = 0; i < num_lines; i++) { + win->_minchng[i] = 0; + win->_maxchng[i] = num_columns - 1; + } + + /* Set flags for window properties */ + if ((begy + num_lines) == LINES) { + win->_flags |= _ENDLINE; + if ((begx == 0) && (num_columns == COLS) && (begy == 0)) + win->_flags |= _FULLWIN; + } /* if */ + if (((begy + num_lines) == LINES) && ((begx + num_columns) == COLS)) + win->_flags |= _SCROLLWIN; + return(win); +} + + +/****************************************************************/ +/* Newwin() creates a new window with size num_lines * num_co- */ +/* Lumns, and origin begx,begy relative to the SCREEN. Special */ +/* Case: if num_lines and/or num_columns is 0, the remainder of */ +/* The screen is used. */ +/****************************************************************/ +WINDOW *newwin(num_lines, num_columns, begy, begx) +int num_lines, num_columns, begy, begx; +{ + WINDOW *win; + int *ptr; + int i, j; + + if (num_lines == 0) num_lines = LINES - begy; + if (num_columns == 0) num_columns = COLS - begx; + if ((win = makenew(num_lines, num_columns, begy, begx)) == (WINDOW *) ERR) + return((WINDOW *) ERR); + for (i = 0; i < num_lines; i++) { /* make and clear the lines */ + if ((win->_line[i] = (int *)calloc(num_columns, sizeof(int))) == NULL){ + for (j = 0; j < i; j++) /* if error, free all the data */ + free(win->_line[j]); + free(win->_minchng); + free(win->_maxchng); + free(win->_line); + free(win); + return((WINDOW *) ERR); + } else { + for (ptr = win->_line[i]; ptr < win->_line[i] + num_columns;) + *ptr++ = ' ' | ATR_NRM; + } + } + return(win); +} + + +/****************************************************************/ +/* Subwin() creates a sub-window in the 'orig' window, with */ +/* Size num_lines * num_columns, and with origin begx, begy */ +/* Relative to the SCREEN. Special case: if num_lines and/or */ +/* Num_columns is 0, the remainder of the original window is */ +/* Used. The subwindow uses the original window's line buffers */ +/* To store it's own lines. */ +/****************************************************************/ +WINDOW *subwin(orig, num_lines, num_columns, begy, begx) +WINDOW *orig; +int num_lines, num_columns, begy, begx; +{ + WINDOW *win; + int i, j, k; + + /* Make sure window fits inside the original one */ + if (begy < orig->_begy || begx < orig->_begx || + (begy + num_lines) > (orig->_begy + orig->_maxy) || + (begx + num_columns) > (orig->_begx + orig->_maxx) ) + return((WINDOW *) ERR); + + if (num_lines == 0) num_lines = orig->_maxy - (begy - orig->_begy); + if (num_columns == 0) num_columns = orig->_maxx - (begx - orig->_begx); + if ((win = makenew(num_lines, num_columns, begy, begx)) == (WINDOW *) ERR) + return((WINDOW *) ERR); + + /* Set line pointers the same as in the original window */ + j = begy - orig->_begy; + k = begx - orig->_begx; + for (i = 0; i < num_lines; i++) win->_line[i] = (orig->_line[j++]) + k; + win->_flags |= _SUBWIN; + return(win); +} diff --git a/lib/curses/options.c b/lib/curses/options.c new file mode 100755 index 000000000..00c888915 --- /dev/null +++ b/lib/curses/options.c @@ -0,0 +1,87 @@ +#include <curses.h> +#include "curspriv.h" + +static bool hasold = FALSE; /* for remembering old cursor type */ +static int oldmode; + +/****************************************************************/ +/* Idlok() is used to set flag for using the terminal insert/ */ +/* Delete line capabilities. This is not relevant for the PC */ +/* Version of curses, and thus nothing is done. */ +/****************************************************************/ +void idlok(win, flag) +WINDOW *win; +bool flag; +{ +} + +/****************************************************************/ +/* Clearok() marks window 'win' to cause screen clearing and */ +/* Redraw the next time a refresh is done. */ +/****************************************************************/ +void clearok(win, flag) +WINDOW *win; +bool flag; +{ + if (win == curscr) + _cursvar.tmpwin->_clear = flag; + else + win->_clear = flag; +} + +/****************************************************************/ +/* Leaveok() marks window 'win' to allow the update routines */ +/* To leave the hardware cursor where it happens to be at the */ +/* End of update. Usually used in combination with cursoff(). */ +/****************************************************************/ + +void leaveok(win, flag) +WINDOW *win; +bool flag; +{ + win->_leave = flag; +} + +/****************************************************************/ +/* Scrollok() marks window 'win' to allow the scrolling region */ +/* Of it to actually scroll. */ +/****************************************************************/ +void scrollok(win, flag) +WINDOW *win; +bool flag; +{ + win->_scroll = flag; +} + +/****************************************************************/ +/* Nodelay() marks the window to make character input non- */ +/* Waiting, i.e. if there is no character to get, -1 will be */ +/* Returned. */ +/****************************************************************/ +void nodelay(win, flag) +WINDOW *win; +bool flag; +{ + win->_nodelay = flag; +} + +/****************************************************************/ +/* Keypad() marks window 'win' to use the special keypad mode. */ +/****************************************************************/ +void keypad(win, flag) +WINDOW *win; +bool flag; +{ + win->_keypad = flag; +} + +/****************************************************************/ +/* Meta() allows use of any alternate character set allowed by */ +/* The terminal. We always allow this on the PC, so this one */ +/* Does nothing. */ +/****************************************************************/ +void meta(win, flag) +WINDOW *win; +bool flag; +{ +} diff --git a/lib/curses/overlay.c b/lib/curses/overlay.c new file mode 100755 index 000000000..c62b5ad97 --- /dev/null +++ b/lib/curses/overlay.c @@ -0,0 +1,124 @@ +/****************************************************************/ +/* Overlay() and overwrite() functions of the PCcurses package */ +/* */ +/****************************************************************/ +/* This version of curses is based on ncurses, a curses version */ +/* Originally written by Pavel Curtis at Cornell University. */ +/* I have made substantial changes to make it run on IBM PC's, */ +/* And therefore consider myself free to make it public domain. */ +/* Bjorn Larsson (...mcvax!enea!infovax!bl) */ +/****************************************************************/ +/* 1.0: Release: 870515 */ +/****************************************************************/ +/* Modified to run under the MINIX operating system by Don Cope */ +/* These changes are also released into the public domain. */ +/* 900906 */ +/****************************************************************/ + +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Overlay() overwrites 'win1' upon 'win2', with origins alig- */ +/* Ned. Overlay is transparent; blanks from 'win1' are not */ +/* Copied to 'win2'. */ +/****************************************************************/ +void overlay(win1, win2) +WINDOW *win1, *win2; +{ + int *minchng; + int *maxchng; + int *w1ptr; + int *w2ptr; + int attrs; + int col; + int line; + int last_line; + int last_col; + + last_col = min(win1->_maxx, win2->_maxx); + last_line = min(win1->_maxy, win2->_maxy); + attrs = win2->_attrs & ATR_MSK; + minchng = win2->_minchng; + maxchng = win2->_maxchng; + + for (line = 0; line <= last_line; line++) { + register short fc, lc = 0; + w1ptr = win1->_line[line]; + w2ptr = win2->_line[line]; + fc = _NO_CHANGE; + for (col = 0; col <= last_col; col++) { + if ((*w1ptr & CHR_MSK) != ' ') { + *w2ptr = (*w1ptr & CHR_MSK) | attrs; + if (fc == _NO_CHANGE) fc = col; + lc = col; + } + w1ptr++; + w2ptr++; + } + + if (*minchng == _NO_CHANGE) { + *minchng = fc; + *maxchng = lc; + } else if (fc != _NO_CHANGE) { + if (fc < *minchng) *minchng = fc; + if (lc > *maxchng) *maxchng = lc; + } + minchng++; + maxchng++; + } /* for */ +} /* overlay */ + +/****************************************************************/ +/* Overwrite() overwrites 'win1' upon 'win2', with origins */ +/* Aligned. Overwrite is non-transparent; blanks from 'win1' */ +/* Are copied to 'win2'. */ +/****************************************************************/ +void overwrite(win1, win2) +WINDOW *win1, *win2; +{ + int *minchng; + int *maxchng; + int *w1ptr; + int *w2ptr; + int attrs; + int col; + int line; + int last_line; + int last_col; + + last_col = min(win1->_maxx, win2->_maxx); + last_line = min(win1->_maxy, win2->_maxy); + attrs = win2->_attrs & ATR_MSK; + minchng = win2->_minchng; + maxchng = win2->_maxchng; + + for (line = 0; line <= last_line; line++) { + register short fc, lc = 0; + + w1ptr = win1->_line[line]; + w2ptr = win2->_line[line]; + fc = _NO_CHANGE; + + for (col = 0; col <= last_col; col++) { + if ((*w1ptr & CHR_MSK) != (*w2ptr & CHR_MSK)) { + *w2ptr = (*w1ptr & CHR_MSK) | attrs; + + if (fc == _NO_CHANGE) fc = col; + lc = col; + } + w1ptr++; + w2ptr++; + } /* for */ + + if (*minchng == _NO_CHANGE) { + *minchng = fc; + *maxchng = lc; + } else if (fc != _NO_CHANGE) { + if (fc < *minchng) *minchng = fc; + if (lc > *maxchng) *maxchng = lc; + } + minchng++; + maxchng++; + } +} diff --git a/lib/curses/prntscan.c b/lib/curses/prntscan.c new file mode 100755 index 000000000..80257f1b8 --- /dev/null +++ b/lib/curses/prntscan.c @@ -0,0 +1,129 @@ +#include <string.h> +#include <curses.h> +#include "curspriv.h" + +static char printscanbuf[513]; /* buffer used during I/O */ + +/****************************************************************/ +/* Wprintw(win,fmt,args) does a printf() in window 'win'. */ +/****************************************************************/ +int wprintw(WINDOW *win, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsprintf(printscanbuf, fmt, args); + if (waddstr(win, printscanbuf) == ERR) return(ERR); + return(strlen(printscanbuf)); +} + +/****************************************************************/ +/* Printw(fmt,args) does a printf() in stdscr. */ +/****************************************************************/ +int printw(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + vsprintf(printscanbuf, fmt, args); + if (waddstr(stdscr, printscanbuf) == ERR) return(ERR); + return(strlen(printscanbuf)); +} /* printw */ + +/****************************************************************/ +/* Mvprintw(fmt,args) moves the stdscr cursor to a new posi- */ +/* tion, then does a printf() in stdscr. */ +/****************************************************************/ +int mvprintw(int y, int x, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (wmove(stdscr, y, x) == ERR) return(ERR); + vsprintf(printscanbuf, fmt, args); + if (waddstr(stdscr, printscanbuf) == ERR) return(ERR); + return(strlen(printscanbuf)); +} + +/****************************************************************/ +/* Mvwprintw(win,fmt,args) moves the window 'win's cursor to */ +/* A new position, then does a printf() in window 'win'. */ +/****************************************************************/ +int mvwprintw(WINDOW *win, int y, int x, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (wmove(win, y, x) == ERR) return(ERR); + vsprintf(printscanbuf, fmt, args); + if (waddstr(win, printscanbuf) == ERR) return(ERR); + return(strlen(printscanbuf)); +} /* mvwprintw */ + +/****************************************************************/ +/* Wscanw(win,fmt,args) gets a string via window 'win', then */ +/* Scans the string using format 'fmt' to extract the values */ +/* And put them in the variables pointed to the arguments. */ +/****************************************************************/ +int wscanw(WINDOW *win, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + wrefresh(win); /* set cursor */ + if (wgetstr(win, printscanbuf) == ERR) /* get string */ + return(ERR); + return(vsscanf(printscanbuf, fmt, args)); +} /* wscanw */ + +/****************************************************************/ +/* Scanw(fmt,args) gets a string via stdscr, then scans the */ +/* String using format 'fmt' to extract the values and put them */ +/* In the variables pointed to the arguments. */ +/****************************************************************/ +int scanw(const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + wrefresh(stdscr); /* set cursor */ + if (wgetstr(stdscr, printscanbuf) == ERR) /* get string */ + return(ERR); + return(vsscanf(printscanbuf, fmt, args)); +} /* scanw */ + +/****************************************************************/ +/* Mvscanw(y,x,fmt,args) moves stdscr's cursor to a new posi- */ +/* Tion, then gets a string via stdscr and scans the string */ +/* Using format 'fmt' to extract the values and put them in the */ +/* Variables pointed to the arguments. */ +/****************************************************************/ +int mvscanw(int y, int x, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (wmove(stdscr, y, x) == ERR) return(ERR); + wrefresh(stdscr); /* set cursor */ + if (wgetstr(stdscr, printscanbuf) == ERR) /* get string */ + return(ERR); + return(vsscanf(printscanbuf, fmt, args)); +} /* mvscanw */ + +/****************************************************************/ +/* Mvwscanw(win,y,x,fmt,args) moves window 'win's cursor to a */ +/* New position, then gets a string via 'win' and scans the */ +/* String using format 'fmt' to extract the values and put them */ +/* In the variables pointed to the arguments. */ +/****************************************************************/ +int mvwscanw(WINDOW *win, int y, int x, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + if (wmove(win, y, x) == ERR) return(ERR); + wrefresh(win); /* set cursor */ + if (wgetstr(win, printscanbuf) == ERR) /* get string */ + return(ERR); + return(vsscanf(printscanbuf, fmt, args)); +} /* mvwscanw */ diff --git a/lib/curses/refresh.c b/lib/curses/refresh.c new file mode 100755 index 000000000..a16a1b93d --- /dev/null +++ b/lib/curses/refresh.c @@ -0,0 +1,72 @@ +/* refresh.c */ + +#include <curses.h> +#include "curspriv.h" + +/* Wrefresh() updates window win's area of the physical screen. */ +void wrefresh(win) +WINDOW *win; +{ + if (win == curscr) + curscr->_clear = TRUE; + else + wnoutrefresh(win); + doupdate(); +} + +/****************************************************************/ +/* Wnoutrefresh() updates the image of the desired screen, */ +/* Without doing physical update (copies window win's image to */ +/* The _cursvar.tmpwin window, which is hidden from the user). */ +/****************************************************************/ + +void wnoutrefresh(win) +register WINDOW *win; +{ + register int *dst; /* start destination in temp window */ + register int *end; /* end destination in temp window */ + register int *src; /* source in user window */ + register int first; /* first changed char on line */ + register int last; /* last changed char on line */ + WINDOW *nscr; + int begy; /* window's place on screen */ + int begx; + int i; + int j; + + nscr = _cursvar.tmpwin; + begy = win->_begy; + begx = win->_begx; + + for (i = 0, j = begy; i <= win->_maxy; i++, j++) { + if (win->_minchng[i] != _NO_CHANGE) { + first = win->_minchng[i]; + last = win->_maxchng[i]; + dst = &(nscr->_line[j][begx + first]); + end = &(nscr->_line[j][begx + last]); + src = &(win->_line[i][first]); + + while (dst <= end) /* copy user line to temp window */ + *dst++ = *src++; + + first += begx; /* nscr's min/max change positions */ + last += begx; + + if ((nscr->_minchng[j] == _NO_CHANGE) || (nscr->_minchng[j] > first)) + nscr->_minchng[j] = first; + if (last > nscr->_maxchng[j]) nscr->_maxchng[j] = last; + + win->_minchng[i] = _NO_CHANGE; /* updated now */ + } /* if */ + win->_maxchng[i] = _NO_CHANGE; /* updated now */ + } /* for */ + + if (win->_clear) { + win->_clear = FALSE; + nscr->_clear = TRUE; + } /* if */ + if (!win->_leave) { + nscr->_cury = win->_cury + begy; + nscr->_curx = win->_curx + begx; + } /* if */ +} /* wnoutrefresh */ diff --git a/lib/curses/scrreg.c b/lib/curses/scrreg.c new file mode 100755 index 000000000..0b3c944dc --- /dev/null +++ b/lib/curses/scrreg.c @@ -0,0 +1,58 @@ +/****************************************************************/ +/* Wsetscrreg() routine of the PCcurses package */ +/* */ +/****************************************************************/ +/* This version of curses is based on ncurses, a curses version */ +/* Originally written by Pavel Curtis at Cornell University. */ +/* I have made substantial changes to make it run on IBM PC's, */ +/* And therefore consider myself free to make it public domain. */ +/* Bjorn Larsson (...mcvax!enea!infovax!bl) */ +/****************************************************************/ +/* 1.0: Release: 870515 */ +/****************************************************************/ +/* Modified to run under the MINIX operating system by Don Cope */ +/* These changes are also released into the public domain. */ +/* 900906 */ +/****************************************************************/ + +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Wsetscrreg() set the scrolling region of window 'win' to in- */ +/* Clude all lines between 'top' and 'bottom'. */ +/****************************************************************/ + +int wsetscrreg(win, top, bottom) +WINDOW *win; +int top; +int bottom; +{ + if ((0 <= top) && + (top <= win->_cury) + && + (win->_cury <= bottom) + && + (bottom <= win->_maxy) + ) { + win->_regtop = top; + win->_regbottom = bottom; + return(OK); + } + + /* If */ + else + return(ERR); +} /* wsetscrreg */ + +/****************************************************************/ +/* Setscrreg() set the scrolling region of stdscr to include */ +/* All lines between 'top' and 'bottom'. */ +/****************************************************************/ + +int setscrreg(top, bottom) +int top; +int bottom; +{ + return(wsetscrreg(stdscr, top, bottom)); +} /* setscrreg */ diff --git a/lib/curses/setterm.c b/lib/curses/setterm.c new file mode 100755 index 000000000..ac3c30925 --- /dev/null +++ b/lib/curses/setterm.c @@ -0,0 +1,76 @@ +#include <curses.h> +#include "curspriv.h" + +_PROTOTYPE( static void ttysetflags, (void) ); + +static void ttysetflags() +{ + _tty.c_iflag |= ICRNL | IXON; + _tty.c_oflag |= OPOST | ONLCR; + _tty.c_lflag |= ECHO | ICANON | IEXTEN | ISIG; + + if (_cursvar.rawmode) { + _tty.c_iflag &= ~(ICRNL | IXON); + _tty.c_oflag &= ~(OPOST); + _tty.c_lflag &= ~(ICANON | IEXTEN | ISIG); + } + if (_cursvar.cbrkmode) { + _tty.c_lflag &= ~(ICANON); + } + if (!_cursvar.echoit) { + _tty.c_lflag &= ~(ECHO | ECHONL); + } + if (NONL) { + _tty.c_iflag &= ~(ICRNL); + _tty.c_oflag &= ~(ONLCR); + } + tcsetattr(0, TCSANOW, &_tty); +} /* ttysetflags */ + +void raw() +{ + _cursvar.rawmode = TRUE; + ttysetflags(); +} /* raw */ + +void noraw() +{ + _cursvar.rawmode = FALSE; + ttysetflags(); +} /* noraw */ + +void echo() +{ + _cursvar.echoit = TRUE; + ttysetflags(); +} + +void noecho() +{ + _cursvar.echoit = FALSE; + ttysetflags(); +} + +void nl() +{ + NONL = FALSE; + ttysetflags(); +} /* nl */ + +void nonl() +{ + NONL = TRUE; + ttysetflags(); +} /* nonl */ + +void cbreak() +{ + _cursvar.cbrkmode = TRUE; + ttysetflags(); +} /* cbreak */ + +void nocbreak() +{ + _cursvar.cbrkmode = FALSE; + ttysetflags(); +} /* nocbreak */ diff --git a/lib/curses/tabsize.c b/lib/curses/tabsize.c new file mode 100755 index 000000000..9fd6230d9 --- /dev/null +++ b/lib/curses/tabsize.c @@ -0,0 +1,50 @@ +/****************************************************************/ +/* Tabsize() routines of the PCcurses package */ +/* */ +/****************************************************************/ +/* This version of curses is based on ncurses, a curses version */ +/* Originally written by Pavel Curtis at Cornell University. */ +/* I have made substantial changes to make it run on IBM PC's, */ +/* And therefore consider myself free to make it public domain. */ +/* Bjorn Larsson (...mcvax!enea!infovax!bl) */ +/****************************************************************/ +/* 1.0: Release: 870515 */ +/****************************************************************/ +/* Modified to run under the MINIX operating system by Don Cope */ +/* These changes are also released into the public domain. */ +/* 900906 */ +/****************************************************************/ + +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Wtabsize(win,ts) sets the tabsize of window 'win' to 'ts', */ +/* And returns the original value. */ +/****************************************************************/ + +int wtabsize(win, ts) +WINDOW *win; +int ts; +{ + int origval; + + origval = win->_tabsize; + win->_tabsize = ts; + return(origval); +} /* wtabsize */ + +/****************************************************************/ +/* Tabsize(ts) sets the tabsize of stdscr to 'ts', and returns */ +/* The original value. */ +/****************************************************************/ + +int tabsize(ts) +int ts; +{ + int origval; + + origval = stdscr->_tabsize; + stdscr->_tabsize = ts; + return(origval); +} /* tabsize */ diff --git a/lib/curses/termmisc.c b/lib/curses/termmisc.c new file mode 100755 index 000000000..267ee4c98 --- /dev/null +++ b/lib/curses/termmisc.c @@ -0,0 +1,63 @@ +#include <curses.h> +#include "curspriv.h" + +/* Static variables or saving terminal modes */ + +int fixterm() +{ + return(OK); +} /* fixterm */ + +int resetterm() +{ + return(OK); +} + +int saveoldterm() +{ + return(OK); +} /* saveoldterm */ + +int saveterm() +{ + return(OK); +} /* saveterm */ + +int baudrate() +{ + return(19200); +} /* baudrate */ + +/****************************************************************/ +/* Erasechar(), killchar() returns std MSDOS erase chars. */ +/****************************************************************/ + +int erasechar() +{ + return(_DCCHAR); /* character delete char */ +} /* erasechar */ + +int killchar() +{ + return(_DLCHAR); /* line delete char */ +} /* killchar */ + +/****************************************************************/ +/* Savetty() and resetty() saves and restores the terminal I/O */ +/* Settings. */ +/****************************************************************/ + +int savetty() +{ + return(OK); +} /* savetty */ + +/****************************************************************/ +/* Setupterm() sets up the terminal. On a PC, it is always suc- */ +/* Cessful, and returns 1. */ +/****************************************************************/ + +int setupterm() +{ + return(1); +} /* setupterm */ diff --git a/lib/curses/unctrl.c b/lib/curses/unctrl.c new file mode 100755 index 000000000..93c740657 --- /dev/null +++ b/lib/curses/unctrl.c @@ -0,0 +1,45 @@ +/****************************************************************/ +/* Unctrl() routines of the PCcurses package */ +/* */ +/****************************************************************/ +/* This version of curses is based on ncurses, a curses version */ +/* Originally written by Pavel Curtis at Cornell University. */ +/* I have made substantial changes to make it run on IBM PC's, */ +/* And therefore consider myself free to make it public domain. */ +/* Bjorn Larsson (...mcvax!enea!infovax!bl) */ +/****************************************************************/ +/* 1.0: Release: 870515 */ +/****************************************************************/ +/* Modified to run under the MINIX operating system by Don Cope */ +/* These changes are also released into the public domain. */ +/* 900906 */ +/****************************************************************/ + +#include <curses.h> +#include "curspriv.h" + +static char strbuf[3] = {0, 0, 0}; + +/****************************************************************/ +/* Unctrl() returns a char pointer to a string corresponding to */ +/* Argument character 'c'. */ +/****************************************************************/ + +char *unctrl(c) +char c; +{ + int ic = c; + ic &= 0xff; + + if ((ic >= ' ') && (ic != 0x7f)) { /* normal characters */ + strbuf[0] = ic; + strbuf[1] = '\0'; + return(strbuf); + } /* if */ + strbuf[0] = '^'; /* '^' prefix */ + if (c == 0x7f) /* DEL */ + strbuf[1] = '?'; + else /* other control */ + strbuf[1] = ic + '@'; + return(strbuf); +} /* unctrl */ diff --git a/lib/curses/update.c b/lib/curses/update.c new file mode 100755 index 000000000..968ca9836 --- /dev/null +++ b/lib/curses/update.c @@ -0,0 +1,173 @@ +#include <curses.h> +#include "curspriv.h" +#include <termcap.h> + +static WINDOW *twin; /* used by many routines */ + +/****************************************************************/ +/* Gotoxy() moves the physical cursor to the desired address on */ +/* The screen. We don't optimize here - on a PC, it takes more */ +/* Time to optimize than to do things directly. */ +/****************************************************************/ + +_PROTOTYPE(static void gotoxy, (int row, int col )); +_PROTOTYPE(static void newattr, (int ch )); +_PROTOTYPE(static void Putchar, (int ch )); +_PROTOTYPE(static void clrupdate, (WINDOW *scr )); +_PROTOTYPE(static void transformline, (int lineno )); + +static void gotoxy(row, col) +int row, col; +{ + poscur(row, col); + _cursvar.cursrow = row; + _cursvar.curscol = col; +} + +/* Update attributes */ +static void newattr(ch) +int ch; +{ + extern char *me, *as, *ae, *mb, *md, *mr, *so, *us; + static int lastattr = 0; + + if (lastattr != (ch &= ATR_MSK)) { + lastattr = ch; + + tputs(me, 1, outc); + if (ae) tputs(ae, 1, outc); + + if (ch & A_ALTCHARSET) + if (as) tputs(as, 1, outc); + if (ch & A_BLINK) tputs(mb, 1, outc); + if (ch & A_BOLD) tputs(md, 1, outc); + if (ch & A_REVERSE) tputs(mr, 1, outc); + if (ch & A_STANDOUT) tputs(so, 1, outc); + if (ch & A_UNDERLINE) tputs(us, 1, outc); + } +} + +/* Putchar() writes a character, with attributes, to the physical + screen, but avoids writing to the lower right screen position. + Should it care about am? +*/ + +/* Output char with attribute */ +static void Putchar(ch) +int ch; +{ + if ((_cursvar.cursrow < LINES) || (_cursvar.curscol < COLS)) { + newattr(ch); + putchar(ch); + } +} + +/****************************************************************/ +/* Clrupdate(scr) updates the screen by clearing it and then */ +/* Redraw it in it's entirety. */ +/****************************************************************/ + +static void clrupdate(scr) +WINDOW *scr; +{ + register int *src; + register int *dst; + register int i; + register int j; + WINDOW *w; + + w = curscr; + + if (scr != w) { /* copy scr to curscr */ + for (i = 0; i < LINES; i++) { + src = scr->_line[i]; + dst = w->_line[i]; + for (j = 0; j < COLS; j++) *dst++ = *src++; + } /* for */ + } /* if */ + newattr(scr->_attrs); + clrscr(); + scr->_clear = FALSE; + for (i = 0; i < LINES; i++) { /* update physical screen */ + src = w->_line[i]; + j = 0; + while (j < COLS) { + if (*src != (' ' | ATR_NRM)) { + gotoxy(i, j); + while (j < COLS && (*src != (' ' | ATR_NRM))) { + Putchar(*src++); + j++; + } + } else { + src++; + j++; + } + } /* for */ + } /* for */ + fflush(stdout); +} /* clrupdate */ + +/****************************************************************/ +/* Transformline() updates the given physical line to look */ +/* Like the corresponding line in _cursvar.tmpwin. */ +/****************************************************************/ + +static void transformline(lineno) +register int lineno; +{ + register int *dstp; + register int *srcp; + register int dstc; + register int srcc; + int x; + int endx; + + x = twin->_minchng[lineno]; + endx = twin->_maxchng[lineno]; + dstp = curscr->_line[lineno] + x; + srcp = twin->_line[lineno] + x; + + while (x <= endx) { + if ((*dstp != *srcp) || (dstc != srcc)) { + gotoxy(lineno, x); + while (x <= endx && ((*dstp != *srcp) || (dstc != srcc))) { + Putchar(*srcp); + *dstp++ = *srcp++; + x++; + } + } else { + *dstp++ = *srcp++; + x++; + } + } /* for */ + twin->_minchng[lineno] = _NO_CHANGE; + twin->_maxchng[lineno] = _NO_CHANGE; +} /* transformline */ + +/****************************************************************/ +/* Doupdate() updates the physical screen to look like _curs- */ +/* Var.tmpwin if curscr is not 'Clear-marked'. Otherwise it */ +/* Updates the screen to look like curscr. */ +/****************************************************************/ + +void doupdate() +{ + int i; + + twin = _cursvar.tmpwin; + if (curscr->_clear) + clrupdate(curscr); + else { + if (twin->_clear) + clrupdate(twin); + else { + for (i = 0; i < LINES; i++) + if (twin->_minchng[i] != _NO_CHANGE) + transformline(i); + } + } + curscr->_curx = twin->_curx; + curscr->_cury = twin->_cury; + gotoxy(curscr->_cury, curscr->_curx); + fflush(stdout); +} /* doupdate */ diff --git a/lib/curses/waddch.c b/lib/curses/waddch.c new file mode 100755 index 000000000..780b26b21 --- /dev/null +++ b/lib/curses/waddch.c @@ -0,0 +1,95 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Newline() does line advance and returns the new cursor line. */ +/* If error, return -1. */ +/****************************************************************/ + +_PROTOTYPE( static short newline, (WINDOW *win, int lin)); + +static short newline(win, lin) +WINDOW *win; +int lin; +{ + if (++lin > win->_regbottom) { + lin--; + if (win->_scroll) + scroll(win); + else + return(-1); + } /* if */ + return(lin); +} /* newline */ + +/****************************************************************/ +/* Waddch() inserts character 'c' at the current cursor posi- */ +/* Tion in window 'win', and takes any actions as dictated by */ +/* The character. */ +/****************************************************************/ + +int waddch(win, c) +WINDOW *win; +int c; +{ + int x = win->_curx; + int y = win->_cury; + int newx; + int ch = c; + int ts = win->_tabsize; + + ch &= (A_ALTCHARSET | 0xff); + if (y > win->_maxy || x > win->_maxx || y < 0 || x < 0) return(ERR); + switch (ch) { + case '\t': + for (newx = ((x / ts) + 1) * ts; x < newx; x++) { + if (waddch(win, ' ') == ERR) return(ERR); + if (win->_curx == 0) /* if tab to next line */ + return(OK); /* exit the loop */ + } + return(OK); + + case '\n': + if (NONL) x = 0; + if ((y = newline(win, y)) < 0) return (ERR); + break; + + case '\r': x = 0; break; + + case '\b': + if (--x < 0) /* no back over left margin */ + x = 0; + break; + + case 0x7f: + { + if (waddch(win, '^') == ERR) return(ERR); + return(waddch(win, '?')); + } + + default: + if (ch < ' ') { /* handle control chars */ + if (waddch(win, '^') == ERR) return(ERR); + return(waddch(win, c + '@')); + } + ch |= (win->_attrs & ATR_MSK); + if (win->_line[y][x] != ch) { /* only if data change */ + if (win->_minchng[y] == _NO_CHANGE) + win->_minchng[y] = win->_maxchng[y] = x; + else if (x < win->_minchng[y]) + win->_minchng[y] = x; + else if (x > win->_maxchng[y]) + win->_maxchng[y] = x; + } /* if */ + win->_line[y][x++] = ch; + if (x > win->_maxx) { /* wrap around test */ + x = 0; + if ((y = newline(win, y)) < 0) return(ERR); + } + break; + + } /* switch */ + win->_curx = x; + win->_cury = y; + return(OK); +} diff --git a/lib/curses/waddstr.c b/lib/curses/waddstr.c new file mode 100755 index 000000000..b32bbb331 --- /dev/null +++ b/lib/curses/waddstr.c @@ -0,0 +1,18 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Waddstr() inserts string 'str' at the current cursor posi- */ +/* Tion in window 'win', and takes any actions as dictated by */ +/* The characters. */ +/****************************************************************/ + +int waddstr(win, str) +WINDOW *win; +char *str; +{ + while (*str) { + if (waddch(win, *str++) == ERR) return(ERR); + } + return(OK); +} diff --git a/lib/curses/wbox.c b/lib/curses/wbox.c new file mode 100755 index 000000000..1ae4fe85e --- /dev/null +++ b/lib/curses/wbox.c @@ -0,0 +1,65 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Wbox(win,ymin,xmin,ymax,xmax,v,h) draws a box in window */ +/* 'win', enclosing the area xmin-xmax and ymin-xmax. If */ +/* Xmax and/or ymax is 0, the window max value is used. 'v' and */ +/* 'h' are the vertical and horizontal characters to use. If */ +/* 'v' and 'h' are 0, wbox will use the alternate character set */ +/* In a pretty way. */ +/****************************************************************/ + +int wbox(win, ymin, xmin, ymax, xmax, v, h) +WINDOW *win; +int ymin, xmin, ymax, xmax; +unsigned int v; +unsigned int h; +{ + unsigned int vc, hc, ulc, urc, llc, lrc; /* corner chars */ + int i; + + if (ymax == 0) ymax = win->_maxy; + if (xmax == 0) xmax = win->_maxx; + + if (ymin >= win->_maxy || ymax > win->_maxy || + xmin >= win->_maxx || xmax > win->_maxx || + ymin >= ymax || xmin >= xmax) + return(ERR); + + vc = v; + hc = h; + ulc = urc = llc = lrc = vc; /* default same as vertical */ + + if (v == 0 && h == 0) { + ulc = ACS_ULCORNER; + urc = ACS_URCORNER; + llc = ACS_LLCORNER; + lrc = ACS_LRCORNER; + hc = ACS_HLINE; + vc = ACS_VLINE; + } + for (i = xmin + 1; i <= xmax - 1; i++) { + win->_line[ymin][i] = hc | win->_attrs; + win->_line[ymax][i] = hc | win->_attrs; + } + for (i = ymin + 1; i <= ymax - 1; i++) { + win->_line[i][xmin] = vc | win->_attrs; + win->_line[i][xmax] = vc | win->_attrs; + } + win->_line[ymin][xmin] = ulc | win->_attrs; + win->_line[ymin][xmax] = urc | win->_attrs; + win->_line[ymax][xmin] = llc | win->_attrs; + win->_line[ymax][xmax] = lrc | win->_attrs; + + for (i = ymin; i <= ymax; i++) { + if (win->_minchng[i] == _NO_CHANGE) { + win->_minchng[i] = xmin; + win->_maxchng[i] = xmax; + } else { + win->_minchng[i] = min(win->_minchng[i], xmin); + win->_maxchng[i] = max(win->_maxchng[i], xmax); + } + } + return(OK); +} diff --git a/lib/curses/wclear.c b/lib/curses/wclear.c new file mode 100755 index 000000000..ba4f08fb9 --- /dev/null +++ b/lib/curses/wclear.c @@ -0,0 +1,14 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Wclear() fills all lines of window 'win' with blanks, and */ +/* Marks the window to be cleared at next refresh operation. */ +/****************************************************************/ + +void wclear(win) +WINDOW *win; +{ + werase(win); + win->_clear = TRUE; +} /* wclear */ diff --git a/lib/curses/wclrtobot.c b/lib/curses/wclrtobot.c new file mode 100755 index 000000000..379ace470 --- /dev/null +++ b/lib/curses/wclrtobot.c @@ -0,0 +1,35 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Wclrtobot() fills the right half of the cursor line of */ +/* Window 'win', and all lines below it with blanks. */ +/****************************************************************/ + +int wclrtobot(win) +WINDOW *win; +{ + int y, minx, startx, *ptr, *end, *maxx, blank; + + blank = ' ' | (win->_attrs & ATR_MSK); + startx = win->_curx; + for (y = win->_cury; y <= win->_regbottom; y++) { + minx = _NO_CHANGE; + end = &win->_line[y][win->_maxx]; + for (ptr = &win->_line[y][startx]; ptr <= end; ptr++) { + if (*ptr != blank) { + maxx = ptr; + if (minx == _NO_CHANGE) minx = ptr - win->_line[y]; + *ptr = blank; + } /* if */ + } /* for */ + if (minx != _NO_CHANGE) { + if ((win->_minchng[y] > minx) || (win->_minchng[y] == _NO_CHANGE)) + win->_minchng[y] = minx; + if (win->_maxchng[y] < maxx - win->_line[y]) + win->_maxchng[y] = maxx - win->_line[y]; + } /* if */ + startx = 0; + } + return(OK); +} diff --git a/lib/curses/wclrtoeol.c b/lib/curses/wclrtoeol.c new file mode 100755 index 000000000..ea72976a8 --- /dev/null +++ b/lib/curses/wclrtoeol.c @@ -0,0 +1,36 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Wclrtoeol() fills the half of the cursor line to the right */ +/* Of the cursor in window 'win' with blanks. */ +/****************************************************************/ + +int wclrtoeol(win) +WINDOW *win; +{ + int *maxx, *ptr, *end, y, x, minx, blank; + + y = win->_cury; + x = win->_curx; + blank = ' ' | (win->_attrs & ATR_MSK); + + end = &win->_line[y][win->_maxx]; + minx = _NO_CHANGE; + maxx = &win->_line[y][x]; + for (ptr = maxx; ptr <= end; ptr++) { + if (*ptr != blank) { + maxx = ptr; + if (minx == _NO_CHANGE) minx = ptr - win->_line[y]; + *ptr = blank; + } /* if */ + } /* for */ + + if (minx != _NO_CHANGE) { + if (win->_minchng[y] > minx || win->_minchng[y] == _NO_CHANGE) + win->_minchng[y] = minx; + if (win->_maxchng[y] < maxx - win->_line[y]) + win->_maxchng[y] = maxx - win->_line[y]; + } + return(OK); +} diff --git a/lib/curses/wdelch.c b/lib/curses/wdelch.c new file mode 100755 index 000000000..0fe8c2cfe --- /dev/null +++ b/lib/curses/wdelch.c @@ -0,0 +1,28 @@ +#include <curses.h> +#include "curspriv.h" + +/* Wdelch() deletes the character at the window cursor, and the + characters to the right of it are shifted left, inserting a + space at the last position of the line. +*/ + +int wdelch(win) +WINDOW *win; +{ + int *temp1; + int *temp2; + int *end; + int y = win->_cury; + int x = win->_curx; + int maxx = win->_maxx; + + end = &win->_line[y][maxx]; + temp1 = &win->_line[y][x]; + temp2 = temp1 + 1; + while (temp1 < end) *temp1++ = *temp2++; + *temp1 = ' ' | (win->_attrs & ATR_MSK); + win->_maxchng[y] = maxx; + if (win->_minchng[y] == _NO_CHANGE || win->_minchng[y] > x) + win->_minchng[y] = x; + return(OK); +} diff --git a/lib/curses/wdeleteln.c b/lib/curses/wdeleteln.c new file mode 100755 index 000000000..50faa4461 --- /dev/null +++ b/lib/curses/wdeleteln.c @@ -0,0 +1,28 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Wdeleteln() deletes the line at the window cursor, and the */ +/* Lines below it are shifted up, inserting a blank line at */ +/* The bottom of the window. */ +/****************************************************************/ + +int wdeleteln(win) +WINDOW *win; +{ + int *end, *temp, y, blank; + + blank = ' ' | (win->_attrs & ATR_MSK); + + temp = win->_line[win->_cury]; + for (y = win->_cury; y < win->_regbottom; y++) { + win->_line[y] = win->_line[y + 1]; + win->_minchng[y] = 0; + win->_maxchng[y] = win->_maxx; + } + win->_minchng[y] = 0; + win->_maxchng[y] = win->_maxx; + win->_line[win->_regbottom] = temp; + for (end = &(temp[win->_maxx]); temp <= end;) *temp++ = blank; + return(OK); +} diff --git a/lib/curses/werase.c b/lib/curses/werase.c new file mode 100755 index 000000000..46872b8ce --- /dev/null +++ b/lib/curses/werase.c @@ -0,0 +1,26 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Werase() fills all lines of window 'win' with blanks and po- */ +/* Sitions the cursor at home in the scroll region. */ +/****************************************************************/ + +void werase(win) +WINDOW *win; +{ + int *end, *start, y, blank; + + blank = ' ' | (win->_attrs & ATR_MSK); + + for (y = win->_regtop; y <= win->_regbottom; y++) { /* clear all lines */ + start = win->_line[y]; + end = &start[win->_maxx]; + while (start <= end) /* clear all line */ + *start++ = blank; + win->_minchng[y] = 0; + win->_maxchng[y] = win->_maxx; + } + win->_cury = win->_regtop; /* cursor home */ + win->_curx = 0; +} diff --git a/lib/curses/wgetch.c b/lib/curses/wgetch.c new file mode 100755 index 000000000..1582f10b2 --- /dev/null +++ b/lib/curses/wgetch.c @@ -0,0 +1,26 @@ +#include <curses.h> +#include <stdio.h> +#include "curspriv.h" + +int wgetch(win) +WINDOW *win; +{ + bool weset = FALSE; + char inp; + + if (!win->_scroll && (win->_flags & _FULLWIN) + && win->_curx == win->_maxx - 1 && win->_cury == win->_maxy - 1) + return ERR; + if (_cursvar.echoit && !_cursvar.rawmode) { + cbreak(); + weset++; + } + inp = getchar(); + if (_cursvar.echoit) { + mvwaddch(curscr, win->_cury + win->_begy, + win->_curx + win->_begx, inp); + waddch(win, inp); + } + if (weset) nocbreak(); + return inp; +} diff --git a/lib/curses/wgetstr.c b/lib/curses/wgetstr.c new file mode 100755 index 000000000..2458e4b89 --- /dev/null +++ b/lib/curses/wgetstr.c @@ -0,0 +1,22 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Wgetstr(win,str) reads in a string (terminated by \n or \r) */ +/* To the buffer pointed to by 'str', and displays the input */ +/* In window 'win'. The user's erase and kill characters are */ +/* Active. */ +/****************************************************************/ + +int wgetstr(win, str) +WINDOW *win; +char *str; +{ + while ((*str = wgetch(win)) != ERR && *str != '\n') str++; + if (*str == ERR) { + *str = '\0'; + return ERR; + } + *str = '\0'; + return OK; +} diff --git a/lib/curses/windel.c b/lib/curses/windel.c new file mode 100755 index 000000000..ad67bd872 --- /dev/null +++ b/lib/curses/windel.c @@ -0,0 +1,41 @@ +/****************************************************************/ +/* Delwin() routine of the PCcurses package. */ +/* */ +/****************************************************************/ +/* This version of curses is based on ncurses, a curses version */ +/* Originally written by Pavel Curtis at Cornell University. */ +/* I have made substantial changes to make it run on IBM PC's, */ +/* And therefore consider myself free to make it public domain. */ +/* Bjorn Larsson (...mcvax!enea!infovax!bl) */ +/****************************************************************/ +/* 1.0: Release: 870515 */ +/****************************************************************/ +/* Modified to run under the MINIX operating system by Don Cope */ +/* These changes are also released into the public domain. */ +/* 900906 */ +/****************************************************************/ + +#include <stdlib.h> +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Delwin() deallocates all data allocated by 'win'. If 'win' */ +/* Is a subwindow, it uses the original window's lines for sto- */ +/* Rage, and thus the line arrays are not deallocated. */ +/****************************************************************/ + +void delwin(win) +WINDOW *win; +{ + int i; + + if (!(win->_flags & _SUBWIN)) { /* subwindow uses 'parent's' lines */ + for (i = 0; i <= win->_maxy && win->_line[i]; i++) + free(win->_line[i]); + } + free(win->_minchng); + free(win->_maxchng); + free(win->_line); + free(win); +} /* delwin */ diff --git a/lib/curses/winmove.c b/lib/curses/winmove.c new file mode 100755 index 000000000..ac8c13308 --- /dev/null +++ b/lib/curses/winmove.c @@ -0,0 +1,36 @@ +/****************************************************************/ +/* Mvwin() routine of the PCcurses package */ +/* */ +/****************************************************************/ +/* This version of curses is based on ncurses, a curses version */ +/* Originally written by Pavel Curtis at Cornell University. */ +/* I have made substantial changes to make it run on IBM PC's, */ +/* And therefore consider myself free to make it public domain. */ +/* Bjorn Larsson (...mcvax!enea!infovax!bl) */ +/****************************************************************/ +/* 1.0: Release: 870515 */ +/****************************************************************/ +/* Modified to run under the MINIX operating system by Don Cope */ +/* These changes are also released into the public domain. */ +/* 900906 */ +/****************************************************************/ + +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Mvwin() moves window 'win' to position (begx, begy) on the */ +/* Screen. */ +/****************************************************************/ + +int mvwin(win, begy, begx) +WINDOW *win; +int begy, begx; +{ + if ((begy + win->_maxy) > (LINES - 1) || (begx + win->_maxx) > (COLS - 1)) + return(ERR); + win->_begy = begy; + win->_begx = begx; + touchwin(win); + return(OK); +} /* mvwin */ diff --git a/lib/curses/winsch.c b/lib/curses/winsch.c new file mode 100755 index 000000000..b7c943089 --- /dev/null +++ b/lib/curses/winsch.c @@ -0,0 +1,31 @@ +#include <curses.h> +#include "curspriv.h" + +/* Winsch() inserts character 'c' at the cursor position in + window 'win'. The cursor is advanced. +*/ + +int winsch(win, c) +WINDOW *win; +char c; +{ + int *temp1; + int *temp2; + int *end; + int x = win->_curx; + int y = win->_cury; + int maxx = win->_maxx; + + if ((c < ' ') && (c == '\n' || c == '\r' || c == '\t' || c == '\b')) + return(waddch(win, c)); + end = &win->_line[y][x]; + temp1 = &win->_line[y][maxx]; + temp2 = temp1 - 1; + if (c < ' ') /* if CTRL-char make space for 2 */ + temp2--; + while (temp1 > end) *temp1-- = *temp2--; + win->_maxchng[y] = maxx; + if ((win->_minchng[y] == _NO_CHANGE) || (win->_minchng[y] > x)) + win->_minchng[y] = x; + return(waddch(win, c)); /* fixes CTRL-chars too */ +} /* winsch */ diff --git a/lib/curses/winscrol.c b/lib/curses/winscrol.c new file mode 100755 index 000000000..7fd9dd3d3 --- /dev/null +++ b/lib/curses/winscrol.c @@ -0,0 +1,55 @@ +/****************************************************************/ +/* Scroll() routine of the PCcurses package */ +/* */ +/****************************************************************/ +/* This version of curses is based on ncurses, a curses version */ +/* Originally written by Pavel Curtis at Cornell University. */ +/* I have made substantial changes to make it run on IBM PC's, */ +/* And therefore consider myself free to make it public domain. */ +/* Bjorn Larsson (...mcvax!enea!infovax!bl) */ +/****************************************************************/ +/* 1.0: Release: 870515 */ +/****************************************************************/ +/* Modified to run under the MINIX operating system by Don Cope */ +/* These changes are also released into the public domain. */ +/* 900906 */ +/****************************************************************/ + +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Scroll() scrolls the scrolling region of 'win', but only if */ +/* Scrolling is allowed and if the cursor is inside the scrol- */ +/* Ling region. */ +/****************************************************************/ + +void scroll(win) +WINDOW *win; +{ + int i; + int *ptr; + int *temp; + static int blank; + + blank = ' ' | (win->_attrs & ATR_MSK); + if ((!win->_scroll) /* check if window scrolls */ + ||(win->_cury < win->_regtop) /* and cursor in region */ + ||(win->_cury > win->_regbottom) + ) + return; + + temp = win->_line[win->_regtop]; + for (i = win->_regtop; i < win->_regbottom; i++) { + win->_line[i] = win->_line[i + 1]; /* re-arrange line pointers */ + win->_minchng[i] = 0; + win->_maxchng[i] = win->_maxx; + } + for (ptr = temp; ptr - temp <= win->_maxx; ptr++) + *ptr = blank; /* make a blank line */ + win->_line[win->_regbottom] = temp; + if (win->_cury > win->_regtop)/* if not on top line */ + win->_cury--; /* cursor scrolls too */ + win->_minchng[win->_regbottom] = 0; + win->_maxchng[win->_regbottom] = win->_maxx; +} /* scroll */ diff --git a/lib/curses/winsertln.c b/lib/curses/winsertln.c new file mode 100755 index 000000000..fa6b83f27 --- /dev/null +++ b/lib/curses/winsertln.c @@ -0,0 +1,26 @@ +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Winsertln() inserts a blank line instead of the cursor line */ +/* In window 'win' and pushes other lines down. */ +/****************************************************************/ + +int winsertln(win) +WINDOW *win; +{ + int *temp, *end, y, blank; + + blank = ' ' | (win->_attrs & ATR_MSK); + temp = win->_line[win->_regbottom]; + for (y = win->_regbottom; y > win->_cury; y--) { + win->_line[y] = win->_line[y - 1]; + win->_minchng[y] = 0; + win->_maxchng[y] = win->_maxx; + } + win->_line[win->_cury] = temp; + for (end = &temp[win->_maxx]; temp <= end; temp++) *temp = blank; + win->_minchng[win->_cury] = 0; + win->_maxchng[win->_cury] = win->_maxx; + return(OK); +} diff --git a/lib/curses/wintouch.c b/lib/curses/wintouch.c new file mode 100755 index 000000000..05f5d78d6 --- /dev/null +++ b/lib/curses/wintouch.c @@ -0,0 +1,40 @@ +/****************************************************************/ +/* Touchwin() routine of the PCcurses package */ +/* */ +/****************************************************************/ +/* This version of curses is based on ncurses, a curses version */ +/* Originally written by Pavel Curtis at Cornell University. */ +/* I have made substantial changes to make it run on IBM PC's, */ +/* And therefore consider myself free to make it public domain. */ +/* Bjorn Larsson (...mcvax!enea!infovax!bl) */ +/****************************************************************/ +/* 1.0: Release: 870515 */ +/****************************************************************/ +/* Modified to run under the MINIX operating system by Don Cope */ +/* These changes are also released into the public domain. */ +/* 900906 */ +/****************************************************************/ + +#include <curses.h> +#include "curspriv.h" + +/****************************************************************/ +/* Touchwin() marks all lines of window 'win' as changed, from */ +/* The first to the last character on the line. */ +/****************************************************************/ + +void touchwin(win) +WINDOW *win; +{ + int y; + int maxy; + int maxx; + + maxy = win->_maxy; + maxx = win->_maxx; + + for (y = 0; y <= maxy; y++) { + win->_minchng[y] = 0; + win->_maxchng[y] = maxx; + } /* for */ +} /* touchwin */ diff --git a/lib/dummy/Makefile b/lib/dummy/Makefile new file mode 100755 index 000000000..41b2cf9ac --- /dev/null +++ b/lib/dummy/Makefile @@ -0,0 +1,11 @@ +# Makefile for lib/dummy. + +# Make a dummy libm library so that -lm works. + +all: ../libm.a + +../libm.a: + echo "int __dummy__;" >dummy.c + $(CC) -c dummy.c + aal cr $@ dummy.o + rm dummy.? diff --git a/lib/editline/Makefile b/lib/editline/Makefile new file mode 100755 index 000000000..c39591a71 --- /dev/null +++ b/lib/editline/Makefile @@ -0,0 +1,69 @@ +## $Revision$ +## +## Unix makefile for editline library. +## + +## Set your options: +## -DANSI_ARROWS ANSI arrows keys work like emacs. +## -DHAVE_STDLIB Have <stdlib.h>. +## -DHAVE_TCGETATTR Have tcgetattr(), tcsetattr(). +## -DHAVE_TERMIO Have "struct termio" and <termio.h> +## (If neither of above two, we use <sgttyb.h> and BSD ioctl's) +## -DHIDE Make static functions static (non debug). +## -DHIST_SIZE=n History size. +## -DNEED_STRDUP Don't have strdup(). +## -DUNIQUE_HISTORY Don't save command if same as last one. +## -DUSE_DIRENT Use <dirent.h>, not <sys/dir.h>? +## -DUSE_TERMCAP Use the termcap library for terminal size +## see LDFLAGS, below, if you set this. +## -DNEED_PERROR Don't have perror() (used in testit) +DEFS = -DANSI_ARROWS -DHAVE_STDLIB -DHAVE_TCGETATTR -DHIDE -DUSE_DIRENT \ + -DHIST_SIZE=100 -DUSE_TERMCAP -DSYS_UNIX -DNEED_STRDUP + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE $(DEFS) -wo +CC1 = $(CC) $(CFLAGS) -c + +## If you have -DUSE_TERMCAP, set this as appropriate: +#LDFLAGS = -ltermlib +#LDFLAGS = -ltermcap + +## End of configuration. + +SOURCES = editline.c complete.c sysunix.c +LIBRARY = ../libedit.a +OBJECTS = $(LIBRARY)(editline.o) $(LIBRARY)(complete.o) $(LIBRARY)(sysunix.o) +SHARFILES = README Makefile editline.3 editline.h unix.h editline.c \ + complete.c sysunix.c testit.c + +install: $(LIBRARY) + +testit: testit.c $(LIBRARY) + $(CC) $(CFLAGS) -o testit testit.c $(LIBRARY) $(LDFLAGS) + +shar: $(SHARFILES) + shar $(SHARFILES) >shar + +clean: + rm -f *.[oa] testit foo core tags lint lint.all a.out shar + +lint: testit + lint -a -b -u -x $(DEFS) $(SOURCES) testit.c >lint.all + sed -e '/warning: function prototype not in scope/d' \ + -e '/warning: old style argument declaration/'d \ + -e '/mix of old and new style function declaration/'d \ + <lint.all >lint + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(OBJECTS): editline.h + +$(LIBRARY)(editline.o): editline.c + $(CC1) editline.c + +$(LIBRARY)(complete.o): complete.c + $(CC1) complete.c + +$(LIBRARY)(sysunix.o): sysunix.c + $(CC1) sysunix.c diff --git a/lib/editline/README b/lib/editline/README new file mode 100755 index 000000000..2edd1a640 --- /dev/null +++ b/lib/editline/README @@ -0,0 +1,59 @@ +$Revision$ + +This is a line-editing library. It can be linked into almost any +program to provide command-line editing and recall. + +It is call-compatible with the FSF readline library, but it is a +fraction of the size (and offers fewer features). It does not use +standard I/O. It is distributed under a "C News-like" copyright. + +Configuration is done in the Makefile. Type "make testit" to get +a small slow shell for testing. + +An earlier version was distributed with Byron's rc. Principal +changes over that version include: + Faster. + Is eight-bit clean (thanks to brendan@cs.widener.edu) + Written in K&R C, but ANSI compliant (gcc all warnings) + Propagates EOF properly; rc trip test now passes + Doesn't need or use or provide memmove. + More robust + Calling sequence changed to be compatible with readline. + Test program, new manpage, better configuration + More system-independant; includes Unix and OS-9 support. + +This contains some changes since the posting to comp.sources.misc: + Bugfix for completion on absolute pathnames. + Better handling of M-n versus showing raw 8bit chars. + Better signal handling. + Now supports termios/termio/sgttyb ioctl's. + Add M-m command to toggle how 8bit data is displayed. + +There is one known bug: + History-searching redraws the line wrong if the text + retrieved is shorter then the prompt. + +Enjoy, + Rich $alz + <rsalz@osf.org> + + Copyright 1992,1993 Simmule Turner and Rich Salz. All rights reserved. + + This software is not subject to any license of the American Telephone + and Telegraph Company or of the Regents of the University of California. + + Permission is granted to anyone to use this software for any purpose on + any computer system, and to alter it and redistribute it freely, subject + to the following restrictions: + 1. The authors are not responsible for the consequences of use of this + software, no matter how awful, even if they arise from flaws in it. + 2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. Since few users ever read sources, + credits must appear in the documentation. + 3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. Since few users + ever read sources, credits must appear in the documentation. + 4. This notice may not be removed or altered. + +-- +$PchId: README,v 1.3 1996/02/22 21:18:51 philip Exp $ diff --git a/lib/editline/complete.c b/lib/editline/complete.c new file mode 100755 index 000000000..e34c491b6 --- /dev/null +++ b/lib/editline/complete.c @@ -0,0 +1,254 @@ +/* $Revision$ +** +** History and file completion functions for editline library. +*/ +#include "editline.h" + + +#if defined(NEED_STRDUP) +/* +** Return an allocated copy of a string. +*/ +char * +strdup(p) + char *p; +{ + char *new; + + if ((new = NEW(char, strlen(p) + 1)) != NULL) + (void)strcpy(new, p); + return new; +} +#endif /* defined(NEED_STRDUP) */ + +/* +** strcmp-like sorting predicate for qsort. +*/ +STATIC int +compare(p1, p2) + CONST void *p1; + CONST void *p2; +{ + CONST char **v1; + CONST char **v2; + + v1 = (CONST char **)p1; + v2 = (CONST char **)p2; + return strcmp(*v1, *v2); +} + +/* +** Fill in *avp with an array of names that match file, up to its length. +** Ignore . and .. . +*/ +STATIC int +FindMatches(dir, file, avp) + char *dir; + char *file; + char ***avp; +{ + char **av; + char **new; + char *p; + DIR *dp; + DIRENTRY *ep; + SIZE_T ac; + SIZE_T len; + SIZE_T choices; + SIZE_T total; +#define MAX_TOTAL (256 << sizeof(char *)) + + if ((dp = opendir(dir)) == NULL) + return 0; + + av = NULL; + ac = 0; + len = strlen(file); + choices = 0; + total = 0; + while ((ep = readdir(dp)) != NULL) { + p = ep->d_name; + if (p[0] == '.' && (p[1] == '\0' || (p[1] == '.' && p[2] == '\0'))) + continue; + if (len && strncmp(p, file, len) != 0) + continue; + + choices++; + if ((total += strlen(p)) > MAX_TOTAL) { + /* This is a bit too much. */ + while (ac > 0) DISPOSE(av[--ac]); + continue; + } + + if ((ac % MEM_INC) == 0) { + if ((new = NEW(char*, ac + MEM_INC)) == NULL) { + total = 0; + break; + } + if (ac) { + COPYFROMTO(new, av, ac * sizeof (char **)); + DISPOSE(av); + } + *avp = av = new; + } + + if ((av[ac] = strdup(p)) == NULL) { + if (ac == 0) + DISPOSE(av); + total = 0; + break; + } + ac++; + } + + /* Clean up and return. */ + (void)closedir(dp); + if (total > MAX_TOTAL) { + char many[sizeof(total) * 3]; + p = many + sizeof(many); + *--p = '\0'; + while (choices > 0) { + *--p = '0' + choices % 10; + choices /= 10; + } + while (p > many + sizeof(many) - 8) *--p = ' '; + if ((p = strdup(p)) != NULL) av[ac++] = p; + if ((p = strdup("choices")) != NULL) av[ac++] = p; + } else { + if (ac) + qsort(av, ac, sizeof (char **), compare); + } + return ac; +} + +/* +** Split a pathname into allocated directory and trailing filename parts. +*/ +STATIC int +SplitPath(path, dirpart, filepart) + char *path; + char **dirpart; + char **filepart; +{ + static char DOT[] = "."; + char *dpart; + char *fpart; + + if ((fpart = strrchr(path, '/')) == NULL) { + if ((dpart = strdup(DOT)) == NULL) + return -1; + if ((fpart = strdup(path)) == NULL) { + DISPOSE(dpart); + return -1; + } + } + else { + if ((dpart = strdup(path)) == NULL) + return -1; + dpart[fpart - path + 1] = '\0'; + if ((fpart = strdup(++fpart)) == NULL) { + DISPOSE(dpart); + return -1; + } + } + *dirpart = dpart; + *filepart = fpart; + return 0; +} + +/* +** Attempt to complete the pathname, returning an allocated copy. +** Fill in *unique if we completed it, or set it to 0 if ambiguous. +*/ +char * +rl_complete(pathname, unique) + char *pathname; + int *unique; +{ + char **av; + char *dir; + char *file; + char *new; + char *p; + SIZE_T ac; + SIZE_T end; + SIZE_T i; + SIZE_T j; + SIZE_T len; + + if (SplitPath(pathname, &dir, &file) < 0) + return NULL; + if ((ac = FindMatches(dir, file, &av)) == 0) { + DISPOSE(dir); + DISPOSE(file); + return NULL; + } + + p = NULL; + len = strlen(file); + if (ac == 1) { + /* Exactly one match -- finish it off. */ + *unique = 1; + j = strlen(av[0]) - len + 2; + if ((p = NEW(char, j + 1)) != NULL) { + COPYFROMTO(p, av[0] + len, j); + if ((new = NEW(char, strlen(dir) + strlen(av[0]) + 2)) != NULL) { + (void)strcpy(new, dir); + (void)strcat(new, "/"); + (void)strcat(new, av[0]); + rl_add_slash(new, p); + DISPOSE(new); + } + } + } + else { + *unique = 0; + if (len) { + /* Find largest matching substring. */ + for (i = len, end = strlen(av[0]); i < end; i++) + for (j = 1; j < ac; j++) + if (av[0][i] != av[j][i]) + goto breakout; + breakout: + if (i > len) { + j = i - len + 1; + if ((p = NEW(char, j)) != NULL) { + COPYFROMTO(p, av[0] + len, j); + p[j - 1] = '\0'; + } + } + } + } + + /* Clean up and return. */ + DISPOSE(dir); + DISPOSE(file); + for (i = 0; i < ac; i++) + DISPOSE(av[i]); + DISPOSE(av); + return p; +} + +/* +** Return all possible completions. +*/ +int +rl_list_possib(pathname, avp) + char *pathname; + char ***avp; +{ + char *dir; + char *file; + int ac; + + if (SplitPath(pathname, &dir, &file) < 0) + return 0; + ac = FindMatches(dir, file, avp); + DISPOSE(dir); + DISPOSE(file); + return ac; +} + +/* + * $PchId: complete.c,v 1.3 1996/02/22 21:18:51 philip Exp $ + */ diff --git a/lib/editline/editline.c b/lib/editline/editline.c new file mode 100755 index 000000000..50a776718 --- /dev/null +++ b/lib/editline/editline.c @@ -0,0 +1,1465 @@ +/* $Revision$ +** +** Main editing routines for editline library. +*/ +#include "editline.h" +#include <signal.h> +#include <ctype.h> + +/* +** Manifest constants. +*/ +#define SCREEN_WIDTH 80 +#define SCREEN_ROWS 24 +#define NO_ARG (-1) +#define DEL 127 +#define CTL(x) ((x) & 0x1F) +#define ISCTL(x) ((x) && (x) < ' ') +#define UNCTL(x) ((x) + 64) +#define META(x) ((x) | 0x80) +#define ISMETA(x) ((x) & 0x80) +#define UNMETA(x) ((x) & 0x7F) +#if !defined(HIST_SIZE) +#define HIST_SIZE 20 +#endif /* !defined(HIST_SIZE) */ + +/* +** Command status codes. +*/ +typedef enum _STATUS { + CSdone, CSeof, CSmove, CSdispatch, CSstay, CSsignal +} STATUS; + +/* +** The type of case-changing to perform. +*/ +typedef enum _CASE { + TOupper, TOlower +} CASE; + +/* +** Key to command mapping. +*/ +typedef struct _KEYMAP { + CHAR Key; + STATUS (*Function)(); +} KEYMAP; + +/* +** Command history structure. +*/ +typedef struct _HISTORY { + int Size; + int Pos; + CHAR *Lines[HIST_SIZE]; +} HISTORY; + +/* +** Globals. +*/ +int rl_eof; +int rl_erase; +int rl_intr; +int rl_kill; +int rl_quit; + +STATIC CHAR NIL[] = ""; +STATIC CONST CHAR *Input = NIL; +STATIC CHAR *Line; +STATIC CONST char *Prompt; +STATIC CHAR *Yanked; +STATIC char *Screen; +STATIC char NEWLINE[]= CRLF; +STATIC HISTORY H; +STATIC int Repeat; +STATIC int End; +STATIC int Mark; +STATIC int OldPoint; +STATIC int Point; +STATIC int PushBack; +STATIC int Pushed; +STATIC int Signal; +FORWARD KEYMAP Map[33]; +FORWARD KEYMAP MetaMap[17]; +STATIC SIZE_T Length; +STATIC SIZE_T ScreenCount; +STATIC SIZE_T ScreenSize; +STATIC char *backspace; +STATIC int TTYwidth; +STATIC int TTYrows; + +/* Display print 8-bit chars as `M-x' or as the actual 8-bit char? */ +int rl_meta_chars = 0; + +/* +** Declarations. +*/ +STATIC CHAR *editinput(); +extern int read(); +extern int write(); +#if defined(USE_TERMCAP) +extern char *getenv(); +extern char *tgetstr(); +extern int tgetent(); +#endif /* defined(USE_TERMCAP) */ + +/* +** TTY input/output functions. +*/ + +STATIC void +TTYflush() +{ + if (ScreenCount) { + (void)write(1, Screen, ScreenCount); + ScreenCount = 0; + } +} + +STATIC void +TTYput(c) + CHAR c; +{ + Screen[ScreenCount] = c; + if (++ScreenCount >= ScreenSize - 1) { + ScreenSize += SCREEN_INC; + RENEW(Screen, char, ScreenSize); + } +} + +STATIC void +TTYputs(p) + CHAR *p; +{ + while (*p) + TTYput(*p++); +} + +STATIC void +TTYshow(c) + CHAR c; +{ + if (c == DEL) { + TTYput('^'); + TTYput('?'); + } + else if (ISCTL(c)) { + TTYput('^'); + TTYput(UNCTL(c)); + } + else if (rl_meta_chars && ISMETA(c)) { + TTYput('M'); + TTYput('-'); + TTYput(UNMETA(c)); + } + else + TTYput(c); +} + +STATIC void +TTYstring(p) + CHAR *p; +{ + while (*p) + TTYshow(*p++); +} + +STATIC unsigned int +TTYget() +{ + CHAR c; + + TTYflush(); + if (Pushed) { + Pushed = 0; + return PushBack; + } + if (*Input) + return *Input++; + return read(0, &c, (SIZE_T)1) == 1 ? c : EOF; +} + +#define TTYback() (backspace ? TTYputs((CHAR *)backspace) : TTYput('\b')) + +STATIC void +TTYbackn(n) + int n; +{ + while (--n >= 0) + TTYback(); +} + +STATIC void +TTYinfo() +{ + static int init; +#if defined(USE_TERMCAP) + char *term; + char buff[2048]; + char *bp; +#endif /* defined(USE_TERMCAP) */ +#if defined(TIOCGWINSZ) + struct winsize W; +#endif /* defined(TIOCGWINSZ) */ + + if (init) { +#if defined(TIOCGWINSZ) + /* Perhaps we got resized. */ + if (ioctl(0, TIOCGWINSZ, &W) >= 0 + && W.ws_col > 0 && W.ws_row > 0) { + TTYwidth = (int)W.ws_col; + TTYrows = (int)W.ws_row; + } +#endif /* defined(TIOCGWINSZ) */ + return; + } + init++; + + TTYwidth = TTYrows = 0; +#if defined(USE_TERMCAP) + bp = &buff[0]; + if ((term = getenv("TERM")) == NULL) + term = "dumb"; + if (tgetent(buff, term) < 0) { + TTYwidth = SCREEN_WIDTH; + TTYrows = SCREEN_ROWS; + return; + } + if ((backspace = tgetstr("le", &bp)) != NULL) + backspace = strdup(backspace); + TTYwidth = tgetnum("co"); + TTYrows = tgetnum("li"); +#endif /* defined(USE_TERMCAP) */ + +#if defined(TIOCGWINSZ) + if (ioctl(0, TIOCGWINSZ, &W) >= 0) { + TTYwidth = (int)W.ws_col; + TTYrows = (int)W.ws_row; + } +#endif /* defined(TIOCGWINSZ) */ + + if (TTYwidth <= 0 || TTYrows <= 0) { + TTYwidth = SCREEN_WIDTH; + TTYrows = SCREEN_ROWS; + } +} + + +/* +** Print an array of words in columns. +*/ +STATIC void +columns(ac, av) + int ac; + CHAR **av; +{ + CHAR *p; + int i; + int j; + int k; + int len; + int skip; + int longest; + int cols; + + /* Find longest name, determine column count from that. */ + for (longest = 0, i = 0; i < ac; i++) + if ((j = strlen((char *)av[i])) > longest) + longest = j; + cols = TTYwidth / (longest + 3); + + TTYputs((CHAR *)NEWLINE); + for (skip = ac / cols + 1, i = 0; i < skip; i++) { + for (j = i; j < ac; j += skip) { + for (p = av[j], len = strlen((char *)p), k = len; --k >= 0; p++) + TTYput(*p); + if (j + skip < ac) + while (++len < longest + 3) + TTYput(' '); + } + TTYputs((CHAR *)NEWLINE); + } +} + +STATIC void +reposition() +{ + int i; + CHAR *p; + + TTYput('\r'); + TTYputs((CONST CHAR *)Prompt); + for (i = Point, p = Line; --i >= 0; p++) + TTYshow(*p); +} + +STATIC void +left(Change) + STATUS Change; +{ + TTYback(); + if (Point) { + if (ISCTL(Line[Point - 1])) + TTYback(); + else if (rl_meta_chars && ISMETA(Line[Point - 1])) { + TTYback(); + TTYback(); + } + } + if (Change == CSmove) + Point--; +} + +STATIC void +right(Change) + STATUS Change; +{ + TTYshow(Line[Point]); + if (Change == CSmove) + Point++; +} + +STATIC STATUS +ring_bell() +{ + TTYput('\07'); + TTYflush(); + return CSstay; +} + +STATIC STATUS +do_macro(c) + unsigned int c; +{ + CHAR name[4]; + + name[0] = '_'; + name[1] = c; + name[2] = '_'; + name[3] = '\0'; + + if ((Input = (CHAR *)getenv((char *)name)) == NULL) { + Input = NIL; + return ring_bell(); + } + return CSstay; +} + +STATIC STATUS +do_forward(move) + STATUS move; +{ + int i; + CHAR *p; + + i = 0; + do { + p = &Line[Point]; + for ( ; Point < End && (*p == ' ' || !isalnum(*p)); Point++, p++) + if (move == CSmove) + right(CSstay); + + for (; Point < End && isalnum(*p); Point++, p++) + if (move == CSmove) + right(CSstay); + + if (Point == End) + break; + } while (++i < Repeat); + + return CSstay; +} + +STATIC STATUS +do_case(type) + CASE type; +{ + int i; + int end; + int count; + CHAR *p; + + (void)do_forward(CSstay); + if (OldPoint != Point) { + if ((count = Point - OldPoint) < 0) + count = -count; + Point = OldPoint; + if ((end = Point + count) > End) + end = End; + for (i = Point, p = &Line[i]; i < end; i++, p++) { + if (type == TOupper) { + if (islower(*p)) + *p = toupper(*p); + } + else if (isupper(*p)) + *p = tolower(*p); + right(CSmove); + } + } + return CSstay; +} + +STATIC STATUS +case_down_word() +{ + return do_case(TOlower); +} + +STATIC STATUS +case_up_word() +{ + return do_case(TOupper); +} + +STATIC void +ceol() +{ + int extras; + int i; + CHAR *p; + + for (extras = 0, i = Point, p = &Line[i]; i <= End; i++, p++) { + TTYput(' '); + if (ISCTL(*p)) { + TTYput(' '); + extras++; + } + else if (rl_meta_chars && ISMETA(*p)) { + TTYput(' '); + TTYput(' '); + extras += 2; + } + } + + for (i += extras; i > Point; i--) + TTYback(); +} + +STATIC void +clear_line() +{ + Point = -strlen(Prompt); + TTYput('\r'); + ceol(); + Point = 0; + End = 0; + Line[0] = '\0'; +} + +STATIC STATUS +insert_string(p) + CHAR *p; +{ + SIZE_T len; + int i; + CHAR *new; + CHAR *q; + + len = strlen((char *)p); + if (End + len >= Length) { + if ((new = NEW(CHAR, Length + len + MEM_INC)) == NULL) + return CSstay; + if (Length) { + COPYFROMTO(new, Line, Length); + DISPOSE(Line); + } + Line = new; + Length += len + MEM_INC; + } + + for (q = &Line[Point], i = End - Point; --i >= 0; ) + q[len + i] = q[i]; + COPYFROMTO(&Line[Point], p, len); + End += len; + Line[End] = '\0'; + TTYstring(&Line[Point]); + Point += len; + + return Point == End ? CSstay : CSmove; +} + +STATIC STATUS +redisplay() +{ + TTYputs((CONST CHAR *)NEWLINE); + TTYputs((CONST CHAR *)Prompt); + TTYstring(Line); + return CSmove; +} + +STATIC STATUS +toggle_meta_mode() +{ + rl_meta_chars = ! rl_meta_chars; + return redisplay(); +} + + +STATIC CHAR * +next_hist() +{ + return H.Pos >= H.Size - 1 ? NULL : H.Lines[++H.Pos]; +} + +STATIC CHAR * +prev_hist() +{ + return H.Pos == 0 ? NULL : H.Lines[--H.Pos]; +} + +STATIC STATUS +do_insert_hist(p) + CHAR *p; +{ + if (p == NULL) + return ring_bell(); + Point = 0; + reposition(); + ceol(); + End = 0; + return insert_string(p); +} + +STATIC STATUS +do_hist(move) + CHAR *(*move)(); +{ + CHAR *p; + int i; + + i = 0; + do { + if ((p = (*move)()) == NULL) + return ring_bell(); + } while (++i < Repeat); + return do_insert_hist(p); +} + +STATIC STATUS +h_next() +{ + return do_hist(next_hist); +} + +STATIC STATUS +h_prev() +{ + return do_hist(prev_hist); +} + +STATIC STATUS +h_first() +{ + return do_insert_hist(H.Lines[H.Pos = 0]); +} + +STATIC STATUS +h_last() +{ + return do_insert_hist(H.Lines[H.Pos = H.Size - 1]); +} + +/* +** Return zero if pat appears as a substring in text. +*/ +STATIC int +substrcmp(text, pat, len) + char *text; + char *pat; + int len; +{ + char c; + + if ((c = *pat) == '\0') + return *text == '\0'; + for ( ; *text; text++) + if (*text == c && strncmp(text, pat, len) == 0) + return 0; + return 1; +} + +STATIC CHAR * +search_hist(search, move) + CHAR *search; + CHAR *(*move)(); +{ + static CHAR *old_search; + int len; + int pos; + int (*match)(); + char *pat; + + /* Save or get remembered search pattern. */ + if (search && *search) { + if (old_search) + DISPOSE(old_search); + old_search = (CHAR *)strdup((char *)search); + } + else { + if (old_search == NULL || *old_search == '\0') + return NULL; + search = old_search; + } + + /* Set up pattern-finder. */ + if (*search == '^') { + match = strncmp; + pat = (char *)(search + 1); + } + else { + match = substrcmp; + pat = (char *)search; + } + len = strlen(pat); + + for (pos = H.Pos; (*move)() != NULL; ) + if ((*match)((char *)H.Lines[H.Pos], pat, len) == 0) + return H.Lines[H.Pos]; + H.Pos = pos; + return NULL; +} + +STATIC STATUS +h_search() +{ + static int Searching; + CONST char *old_prompt; + CHAR *(*move)(); + CHAR *p; + + if (Searching) + return ring_bell(); + Searching = 1; + + clear_line(); + old_prompt = Prompt; + Prompt = "Search: "; + TTYputs((CONST CHAR *)Prompt); + move = Repeat == NO_ARG ? prev_hist : next_hist; + p = editinput(); + Prompt = old_prompt; + Searching = 0; + TTYputs((CONST CHAR *)Prompt); + if (p == NULL && Signal > 0) { + Signal = 0; + clear_line(); + return redisplay(); + } + p = search_hist(p, move); + clear_line(); + if (p == NULL) { + (void)ring_bell(); + return redisplay(); + } + return do_insert_hist(p); +} + +STATIC STATUS +fd_char() +{ + int i; + + i = 0; + do { + if (Point >= End) + break; + right(CSmove); + } while (++i < Repeat); + return CSstay; +} + +STATIC void +save_yank(begin, i) + int begin; + int i; +{ + if (Yanked) { + DISPOSE(Yanked); + Yanked = NULL; + } + + if (i < 1) + return; + + if ((Yanked = NEW(CHAR, (SIZE_T)i + 1)) != NULL) { + COPYFROMTO(Yanked, &Line[begin], i); + Yanked[i] = '\0'; + } +} + +STATIC STATUS +delete_string(count) + int count; +{ + int i; + CHAR *p; + + if (count <= 0 || End == Point) + return ring_bell(); + + if (count == 1 && Point == End - 1) { + /* Optimize common case of delete at end of line. */ + End--; + p = &Line[Point]; + i = 1; + TTYput(' '); + if (ISCTL(*p)) { + i = 2; + TTYput(' '); + } + else if (rl_meta_chars && ISMETA(*p)) { + i = 3; + TTYput(' '); + TTYput(' '); + } + TTYbackn(i); + *p = '\0'; + return CSmove; + } + if (Point + count > End && (count = End - Point) <= 0) + return CSstay; + + if (count > 1) + save_yank(Point, count); + + for (p = &Line[Point], i = End - (Point + count) + 1; --i >= 0; p++) + p[0] = p[count]; + ceol(); + End -= count; + TTYstring(&Line[Point]); + return CSmove; +} + +STATIC STATUS +bk_char() +{ + int i; + + i = 0; + do { + if (Point == 0) + break; + left(CSmove); + } while (++i < Repeat); + + return CSstay; +} + +STATIC STATUS +bk_del_char() +{ + int i; + + i = 0; + do { + if (Point == 0) + break; + left(CSmove); + } while (++i < Repeat); + + return delete_string(i); +} + +STATIC STATUS +kill_line() +{ + int i; + + if (Repeat != NO_ARG) { + if (Repeat < Point) { + i = Point; + Point = Repeat; + reposition(); + (void)delete_string(i - Point); + } + else if (Repeat > Point) { + right(CSmove); + (void)delete_string(Repeat - Point - 1); + } + return CSmove; + } + + save_yank(Point, End - Point); + Line[Point] = '\0'; + ceol(); + End = Point; + return CSstay; +} + +STATIC STATUS +insert_char(c) + int c; +{ + STATUS s; + CHAR buff[2]; + CHAR *p; + CHAR *q; + int i; + + if (Repeat == NO_ARG || Repeat < 2) { + buff[0] = c; + buff[1] = '\0'; + return insert_string(buff); + } + + if ((p = NEW(CHAR, Repeat + 1)) == NULL) + return CSstay; + for (i = Repeat, q = p; --i >= 0; ) + *q++ = c; + *q = '\0'; + Repeat = 0; + s = insert_string(p); + DISPOSE(p); + return s; +} + +STATIC STATUS +meta() +{ + unsigned int c; + KEYMAP *kp; + + if ((c = TTYget()) == EOF) + return CSeof; +#if defined(ANSI_ARROWS) + /* Also include VT-100 arrows. */ + if (c == '[' || c == 'O') + switch (c = TTYget()) { + default: return ring_bell(); + case EOF: return CSeof; + case 'A': return h_prev(); + case 'B': return h_next(); + case 'C': return fd_char(); + case 'D': return bk_char(); + } +#endif /* defined(ANSI_ARROWS) */ + + if (isdigit(c)) { + for (Repeat = c - '0'; (c = TTYget()) != EOF && isdigit(c); ) + Repeat = Repeat * 10 + c - '0'; + Pushed = 1; + PushBack = c; + return CSstay; + } + + if (isupper(c)) + return do_macro(c); + for (kp = MetaMap; kp->Function; kp++) + if (kp->Key == c) + return (*kp->Function)(); + + return ring_bell(); +} + +STATIC STATUS +emacs(c) + unsigned int c; +{ + STATUS s; + KEYMAP *kp; + + OldPoint = Point; + if (rl_meta_chars && ISMETA(c)) { + Pushed = 1; + PushBack = UNMETA(c); + return meta(); + } + for (kp = Map; kp->Function; kp++) + if (kp->Key == c) + break; + s = kp->Function ? (*kp->Function)() : insert_char((int)c); + if (!Pushed) + /* No pushback means no repeat count; hacky, but true. */ + Repeat = NO_ARG; + return s; +} + +STATIC STATUS +TTYspecial(c) + unsigned int c; +{ + if (ISMETA(c)) + return CSdispatch; + + if (c == rl_erase || c == DEL) + return bk_del_char(); + if (c == rl_kill) { + if (Point != 0) { + Point = 0; + reposition(); + } + Repeat = NO_ARG; + return kill_line(); + } + if (c == rl_eof && Point == 0 && End == 0) + return CSeof; + if (c == rl_intr) { + Signal = SIGINT; + return CSsignal; + } + if (c == rl_quit) { + Signal = SIGQUIT; + return CSeof; + } + + return CSdispatch; +} + +STATIC CHAR * +editinput() +{ + unsigned int c; + + Repeat = NO_ARG; + OldPoint = Point = Mark = End = 0; + Line[0] = '\0'; + + Signal = -1; + while ((c = TTYget()) != EOF) + switch (TTYspecial(c)) { + case CSdone: + return Line; + case CSeof: + return NULL; + case CSsignal: + return (CHAR *)""; + case CSmove: + reposition(); + break; + case CSdispatch: + switch (emacs(c)) { + case CSdone: + return Line; + case CSeof: + return NULL; + case CSsignal: + return (CHAR *)""; + case CSmove: + reposition(); + break; + case CSdispatch: + case CSstay: + break; + } + break; + case CSstay: + break; + } + return NULL; +} + +STATIC void +hist_add(p) + CHAR *p; +{ + int i; + + if ((p = (CHAR *)strdup((char *)p)) == NULL) + return; + if (H.Size < HIST_SIZE) + H.Lines[H.Size++] = p; + else { + DISPOSE(H.Lines[0]); + for (i = 0; i < HIST_SIZE - 1; i++) + H.Lines[i] = H.Lines[i + 1]; + H.Lines[i] = p; + } + H.Pos = H.Size - 1; +} + +/* +** For compatibility with FSF readline. +*/ +/* ARGSUSED0 */ +void +rl_reset_terminal(p) + char *p; +{ +} + +void +rl_initialize() +{ +} + +char * +readline(prompt) + CONST char *prompt; +{ + CHAR *line; + int s; + + if (Line == NULL) { + Length = MEM_INC; + if ((Line = NEW(CHAR, Length)) == NULL) + return NULL; + } + + TTYinfo(); + rl_ttyset(0); + hist_add(NIL); + ScreenSize = SCREEN_INC; + Screen = NEW(char, ScreenSize); + Prompt = prompt ? prompt : (char *)NIL; + TTYputs((CONST CHAR *)Prompt); + if ((line = editinput()) != NULL) { + line = (CHAR *)strdup((char *)line); + TTYputs((CHAR *)NEWLINE); + TTYflush(); + } + rl_ttyset(1); + DISPOSE(Screen); + DISPOSE(H.Lines[--H.Size]); + + if (line != NULL && *line != '\0' +#if defined(UNIQUE_HISTORY) + && !(H.Pos && strcmp((char *) line, (char *) H.Lines[H.Pos - 1]) == 0) +#endif /* defined(UNIQUE_HISTORY) */ + && !(H.Size && strcmp((char *) line, (char *) H.Lines[H.Size - 1]) == 0) + ) { + hist_add(line); + } + + if (Signal > 0) { + s = Signal; + Signal = 0; + (void)kill(getpid(), s); + } + return (char *)line; +} + +void +add_history(p) + char *p; +{ +#ifdef obsolete /* Made part of readline(). -- kjb */ + if (p == NULL || *p == '\0') + return; + +#if defined(UNIQUE_HISTORY) + if (H.Pos && strcmp(p, (char *) H.Lines[H.Pos - 1]) == 0) + return; +#endif /* defined(UNIQUE_HISTORY) */ + if (H.Size && strcmp(p, (char *) H.Lines[H.Size - 1]) == 0) + return; + hist_add((CHAR *)p); +#endif +} + + +STATIC STATUS +beg_line() +{ + if (Point) { + Point = 0; + return CSmove; + } + return CSstay; +} + +STATIC STATUS +del_char() +{ + return delete_string(Repeat == NO_ARG ? 1 : Repeat); +} + +STATIC STATUS +end_line() +{ + if (Point != End) { + Point = End; + return CSmove; + } + return CSstay; +} + +STATIC char SEPS[] = "\"#$&'()*:;<=>?[\\]^`{|}~\n\t "; + +/* +** Move back to the beginning of the current word and return an +** allocated copy of it. +*/ +STATIC CHAR * +find_word() +{ + CHAR *p, *q; + CHAR *new; + SIZE_T len; + + p = &Line[Point]; + while (p > Line) { + p--; + if (p > Line && p[-1] == '\\') { + p--; + } else { + if (strchr(SEPS, (char) *p) != NULL) { + p++; + break; + } + } + } + len = Point - (p - Line) + 1; + if ((new = NEW(CHAR, len)) == NULL) + return NULL; + q = new; + while (p < &Line[Point]) { + if (*p == '\\') { + if (++p == &Line[Point]) break; + } + *q++ = *p++; + } + *q = '\0'; + return new; +} + +STATIC STATUS +c_possible() +{ + CHAR **av; + CHAR *word; + int ac; + + word = find_word(); + ac = rl_list_possib((char *)word, (char ***)&av); + if (word) + DISPOSE(word); + if (ac) { + columns(ac, av); + while (--ac >= 0) + DISPOSE(av[ac]); + DISPOSE(av); + return CSmove; + } + return ring_bell(); +} + +STATIC STATUS +c_complete() +{ + CHAR *p, *q; + CHAR *word, *new; + SIZE_T len; + int unique; + STATUS s; + + word = find_word(); + p = (CHAR *)rl_complete((char *)word, &unique); + if (word) + DISPOSE(word); + if (p) { + len = strlen((char *)p); + word = p; + new = q = NEW(CHAR, 2 * len + 1); + while (*p) { + if ((*p < ' ' || strchr(SEPS, (char) *p) != NULL) + && (!unique || p[1] != 0)) { + *q++ = '\\'; + } + *q++ = *p++; + } + *q = '\0'; + DISPOSE(word); + if (len > 0) { + s = insert_string(new); +#if ANNOYING_NOISE + if (!unique) + (void)ring_bell(); +#endif + } + DISPOSE(new); + if (len > 0) return s; + } + return c_possible(); +} + +STATIC STATUS +accept_line() +{ + Line[End] = '\0'; + return CSdone; +} + +STATIC STATUS +transpose() +{ + CHAR c; + + if (Point) { + if (Point == End) + left(CSmove); + c = Line[Point - 1]; + left(CSstay); + Line[Point - 1] = Line[Point]; + TTYshow(Line[Point - 1]); + Line[Point++] = c; + TTYshow(c); + } + return CSstay; +} + +STATIC STATUS +quote() +{ + unsigned int c; + + return (c = TTYget()) == EOF ? CSeof : insert_char((int)c); +} + +STATIC STATUS +wipe() +{ + int i; + + if (Mark > End) + return ring_bell(); + + if (Point > Mark) { + i = Point; + Point = Mark; + Mark = i; + reposition(); + } + + return delete_string(Mark - Point); +} + +STATIC STATUS +mk_set() +{ + Mark = Point; + return CSstay; +} + +STATIC STATUS +exchange() +{ + unsigned int c; + + if ((c = TTYget()) != CTL('X')) + return c == EOF ? CSeof : ring_bell(); + + if ((c = Mark) <= End) { + Mark = Point; + Point = c; + return CSmove; + } + return CSstay; +} + +STATIC STATUS +yank() +{ + if (Yanked && *Yanked) + return insert_string(Yanked); + return CSstay; +} + +STATIC STATUS +copy_region() +{ + if (Mark > End) + return ring_bell(); + + if (Point > Mark) + save_yank(Mark, Point - Mark); + else + save_yank(Point, Mark - Point); + + return CSstay; +} + +STATIC STATUS +move_to_char() +{ + unsigned int c; + int i; + CHAR *p; + + if ((c = TTYget()) == EOF) + return CSeof; + for (i = Point + 1, p = &Line[i]; i < End; i++, p++) + if (*p == c) { + Point = i; + return CSmove; + } + return CSstay; +} + +STATIC STATUS +fd_word() +{ + return do_forward(CSmove); +} + +STATIC STATUS +fd_kill_word() +{ + int i; + + (void)do_forward(CSstay); + if (OldPoint != Point) { + i = Point - OldPoint; + Point = OldPoint; + return delete_string(i); + } + return CSstay; +} + +STATIC STATUS +bk_word() +{ + int i; + CHAR *p; + + i = 0; + do { + for (p = &Line[Point]; p > Line && !isalnum(p[-1]); p--) + left(CSmove); + + for (; p > Line && p[-1] != ' ' && isalnum(p[-1]); p--) + left(CSmove); + + if (Point == 0) + break; + } while (++i < Repeat); + + return CSstay; +} + +STATIC STATUS +bk_kill_word() +{ + (void)bk_word(); + if (OldPoint != Point) + return delete_string(OldPoint - Point); + return CSstay; +} + +STATIC int +argify(line, avp) + CHAR *line; + CHAR ***avp; +{ + CHAR *c; + CHAR **p; + CHAR **new; + int ac; + int i; + + i = MEM_INC; + if ((*avp = p = NEW(CHAR*, i))== NULL) + return 0; + + for (c = line; isspace(*c); c++) + continue; + if (*c == '\n' || *c == '\0') + return 0; + + for (ac = 0, p[ac++] = c; *c && *c != '\n'; ) { + if (isspace(*c)) { + *c++ = '\0'; + if (*c && *c != '\n') { + if (ac + 1 == i) { + new = NEW(CHAR*, i + MEM_INC); + if (new == NULL) { + p[ac] = NULL; + return ac; + } + COPYFROMTO(new, p, i * sizeof (char **)); + i += MEM_INC; + DISPOSE(p); + *avp = p = new; + } + p[ac++] = c; + } + } + else + c++; + } + *c = '\0'; + p[ac] = NULL; + return ac; +} + +STATIC STATUS +last_argument() +{ + CHAR **av; + CHAR *p; + STATUS s; + int ac; + + if (H.Size == 1 || (p = H.Lines[H.Size - 2]) == NULL) + return ring_bell(); + + if ((p = (CHAR *)strdup((char *)p)) == NULL) + return CSstay; + ac = argify(p, &av); + + if (Repeat != NO_ARG) + s = Repeat < ac ? insert_string(av[Repeat]) : ring_bell(); + else + s = ac ? insert_string(av[ac - 1]) : CSstay; + + if (ac) + DISPOSE(av); + DISPOSE(p); + return s; +} + +STATIC KEYMAP Map[33] = { + { CTL('@'), mk_set }, + { CTL('A'), beg_line }, + { CTL('B'), bk_char }, + { CTL('D'), del_char }, + { CTL('E'), end_line }, + { CTL('F'), fd_char }, + { CTL('G'), ring_bell }, + { CTL('H'), bk_del_char }, + { CTL('I'), c_complete }, + { CTL('J'), accept_line }, + { CTL('K'), kill_line }, + { CTL('L'), redisplay }, + { CTL('M'), accept_line }, + { CTL('N'), h_next }, + { CTL('O'), ring_bell }, + { CTL('P'), h_prev }, + { CTL('Q'), ring_bell }, + { CTL('R'), h_search }, + { CTL('S'), ring_bell }, + { CTL('T'), transpose }, + { CTL('U'), ring_bell }, + { CTL('V'), quote }, + { CTL('W'), bk_kill_word }, + { CTL('X'), exchange }, + { CTL('Y'), yank }, + { CTL('Z'), end_line }, + { CTL('['), meta }, + { CTL(']'), move_to_char }, + { CTL('^'), ring_bell }, + { CTL('_'), ring_bell }, + { 0, NULL } +}; + +STATIC KEYMAP MetaMap[17]= { + { CTL('H'), wipe }, + { DEL, wipe }, + { ' ', mk_set }, + { '.', last_argument }, + { '<', h_first }, + { '>', h_last }, + { '?', c_possible }, + { 'b', bk_word }, + { 'd', fd_kill_word }, + { 'f', fd_word }, + { 'l', case_down_word }, + { 'm', toggle_meta_mode }, + { 'u', case_up_word }, + { 'y', yank }, + { 'w', copy_region }, + { 0, NULL } +}; + +/* + * $PchId: editline.c,v 1.4 1996/02/22 21:16:56 philip Exp $ + */ diff --git a/lib/editline/editline.h b/lib/editline/editline.h new file mode 100755 index 000000000..145fc193f --- /dev/null +++ b/lib/editline/editline.h @@ -0,0 +1,79 @@ +/* $Revision$ +** +** Internal header file for editline library. +*/ +#include <stdio.h> +#if defined(HAVE_STDLIB) +#include <stdlib.h> +#include <string.h> +#endif /* defined(HAVE_STDLIB) */ +#if defined(SYS_UNIX) +#include "unix.h" +#endif /* defined(SYS_UNIX) */ +#if defined(SYS_OS9) +#include "os9.h" +#endif /* defined(SYS_OS9) */ + +#if !defined(SIZE_T) +#define SIZE_T unsigned int +#endif /* !defined(SIZE_T) */ + +typedef unsigned char CHAR; + +#if defined(HIDE) +#define STATIC static +#else +#define STATIC /* NULL */ +#endif /* !defined(HIDE) */ + +#if !defined(CONST) +#if defined(__STDC__) +#define CONST const +#else +#define CONST +#endif /* defined(__STDC__) */ +#endif /* !defined(CONST) */ + + +#define MEM_INC 64 +#define SCREEN_INC 256 + +#define DISPOSE(p) free((char *)(p)) +#define NEW(T, c) \ + ((T *)malloc((unsigned int)(sizeof (T) * (c)))) +#define RENEW(p, T, c) \ + (p = (T *)realloc((char *)(p), (unsigned int)(sizeof (T) * (c)))) +#define COPYFROMTO(new, p, len) \ + (void)memcpy((char *)(new), (char *)(p), (int)(len)) + + +/* +** Variables and routines internal to this package. +*/ +extern int rl_eof; +extern int rl_erase; +extern int rl_intr; +extern int rl_kill; +extern int rl_quit; +extern char *rl_complete(); +extern int rl_list_possib(); +extern void rl_ttyset(); +extern void rl_add_slash(); + +#if !defined(HAVE_STDLIB) +extern char *getenv(); +extern char *malloc(); +extern char *realloc(); +extern char *memcpy(); +extern char *strcat(); +extern char *strchr(); +extern char *strrchr(); +extern char *strcpy(); +extern int strcmp(); +extern int strlen(); +extern int strncmp(); +#endif /* !defined(HAVE_STDLIB) */ + +#if defined(NEED_STRDUP) +extern char *strdup(); +#endif diff --git a/lib/editline/sysunix.c b/lib/editline/sysunix.c new file mode 100755 index 000000000..89bfa8dba --- /dev/null +++ b/lib/editline/sysunix.c @@ -0,0 +1,120 @@ +/* $Revision$ +** +** Unix system-dependant routines for editline library. +*/ +#include "editline.h" + +#if defined(HAVE_TCGETATTR) +#include <termios.h> + +void +rl_ttyset(Reset) + int Reset; +{ + static struct termios old; + struct termios new; + + if (Reset == 0) { + (void)tcgetattr(0, &old); + rl_erase = old.c_cc[VERASE]; + rl_kill = old.c_cc[VKILL]; + rl_eof = old.c_cc[VEOF]; + rl_intr = old.c_cc[VINTR]; + rl_quit = old.c_cc[VQUIT]; + + new = old; + new.c_lflag &= ~(ECHO | ICANON | ISIG | IEXTEN); + new.c_iflag &= ~(ICRNL); + new.c_cc[VMIN] = 1; + new.c_cc[VTIME] = 0; + (void)tcsetattr(0, TCSADRAIN, &new); + } + else + (void)tcsetattr(0, TCSADRAIN, &old); +} + +#else +#if defined(HAVE_TERMIO) +#include <termio.h> + +void +rl_ttyset(Reset) + int Reset; +{ + static struct termio old; + struct termio new; + + if (Reset == 0) { + (void)ioctl(0, TCGETA, &old); + rl_erase = old.c_cc[VERASE]; + rl_kill = old.c_cc[VKILL]; + rl_eof = old.c_cc[VEOF]; + rl_intr = old.c_cc[VINTR]; + rl_quit = old.c_cc[VQUIT]; + + new = old; + new.c_cc[VINTR] = -1; + new.c_cc[VQUIT] = -1; + new.c_lflag &= ~(ECHO | ICANON); + new.c_cc[VMIN] = 1; + new.c_cc[VTIME] = 0; + (void)ioctl(0, TCSETAW, &new); + } + else + (void)ioctl(0, TCSETAW, &old); +} + +#else +#include <sgtty.h> + +void +rl_ttyset(Reset) + int Reset; +{ + static struct sgttyb old_sgttyb; + static struct tchars old_tchars; + struct sgttyb new_sgttyb; + struct tchars new_tchars; + + if (Reset == 0) { + (void)ioctl(0, TIOCGETP, &old_sgttyb); + rl_erase = old_sgttyb.sg_erase; + rl_kill = old_sgttyb.sg_kill; + + (void)ioctl(0, TIOCGETC, &old_tchars); + rl_eof = old_tchars.t_eofc; + rl_intr = old_tchars.t_intrc; + rl_quit = old_tchars.t_quitc; + + new_sgttyb = old_sgttyb; + new_sgttyb.sg_flags &= ~ECHO; + new_sgttyb.sg_flags |= RAW; + (void)ioctl(0, TIOCSETP, &new_sgttyb); + + new_tchars = old_tchars; + new_tchars.t_intrc = -1; + new_tchars.t_quitc = -1; + (void)ioctl(0, TIOCSETC, &new_tchars); + } + else { + (void)ioctl(0, TIOCSETP, &old_sgttyb); + (void)ioctl(0, TIOCSETC, &old_tchars); + } +} +#endif /* defined(HAVE_TERMIO) */ +#endif /* defined(HAVE_TCGETATTR) */ + +void +rl_add_slash(path, p) + char *path; + char *p; +{ + struct stat Sb; + + if (stat(path, &Sb) >= 0) + (void)strcat(p, S_ISDIR(Sb.st_mode) ? "/" : " "); +} + +/* + * $PchId: sysunix.c,v 1.4 1996/02/22 21:16:56 philip Exp $ + */ diff --git a/lib/editline/testit.c b/lib/editline/testit.c new file mode 100755 index 000000000..df2f5aa51 --- /dev/null +++ b/lib/editline/testit.c @@ -0,0 +1,68 @@ +/* $Revision$ +** +** A "micro-shell" to test editline library. +** If given any arguments, commands aren't executed. +*/ +#include <stdio.h> +#if defined(HAVE_STDLIB) +#include <stdlib.h> +#endif /* defined(HAVE_STDLIB) */ + +extern char *readline(); +extern void add_history(); + +#if !defined(HAVE_STDLIB) +extern int chdir(); +extern int free(); +extern int strncmp(); +extern int system(); +extern void exit(); +extern char *getenv(); +#endif /* !defined(HAVE_STDLIB) */ + + +#if defined(NEED_PERROR) +void +perror(s) + char *s; +{ + extern int errno; + + (voidf)printf(stderr, "%s: error %d\n", s, errno); +} +#endif /* defined(NEED_PERROR) */ + + +/* ARGSUSED1 */ +int +main(ac, av) + int ac; + char *av[]; +{ + char *prompt; + char *p; + int doit; + + doit = ac == 1; + if ((prompt = getenv("TESTPROMPT")) == NULL) + prompt = "testit> "; + + while ((p = readline(prompt)) != NULL) { + (void)printf("\t\t\t|%s|\n", p); + if (doit) + if (strncmp(p, "cd ", 3) == 0) { + if (chdir(&p[3]) < 0) + perror(&p[3]); + } + else if (system(p) != 0) + perror(p); + add_history(p); + free(p); + } + exit(0); + /* NOTREACHED */ +} + +/* + * $PchId: testit.c,v 1.3 1996/02/22 21:18:51 philip Exp $ + */ diff --git a/lib/editline/unix.h b/lib/editline/unix.h new file mode 100755 index 000000000..af884a92c --- /dev/null +++ b/lib/editline/unix.h @@ -0,0 +1,26 @@ +/* $Revision$ +** +** Editline system header file for Unix. +*/ + +#define CRLF "\r\n" +#define FORWARD STATIC + +#include <sys/types.h> +#include <sys/stat.h> + +#if defined(USE_DIRENT) +#include <dirent.h> +typedef struct dirent DIRENTRY; +#else +#include <sys/dir.h> +typedef struct direct DIRENTRY; +#endif /* defined(USE_DIRENT) */ + +#if !defined(S_ISDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) +#endif /* !defined(S_ISDIR) */ + +/* + * $PchId: unix.h,v 1.3 1996/02/22 21:18:51 philip Exp $ + */ diff --git a/lib/end/Makefile b/lib/end/Makefile new file mode 100755 index 000000000..b97a77d0a --- /dev/null +++ b/lib/end/Makefile @@ -0,0 +1,28 @@ +# Makefile for lib/end. + +CC1 = $(CC) -c + +LIBRARY = ../end.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(edata.o) \ + $(LIBRARY)(em_end.o) \ + $(LIBRARY)(end.o) \ + $(LIBRARY)(etext.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(edata.o): edata.s + $(CC1) edata.s + +$(LIBRARY)(em_end.o): em_end.s + $(CC1) em_end.s + +$(LIBRARY)(end.o): end.s + $(CC1) end.s + +$(LIBRARY)(etext.o): etext.s + $(CC1) etext.s diff --git a/lib/end/edata.s b/lib/end/edata.s new file mode 100755 index 000000000..ad44a9795 --- /dev/null +++ b/lib/end/edata.s @@ -0,0 +1,7 @@ +# +.sect .text +.sect .rom +.sect .data +.align _EM_WSIZE +.define _edata +_edata: diff --git a/lib/end/em_end.s b/lib/end/em_end.s new file mode 100755 index 000000000..ad10ad1f5 --- /dev/null +++ b/lib/end/em_end.s @@ -0,0 +1,21 @@ +# +.sect .text +.align _EM_WSIZE +.define __etext, endtext +__etext: +endtext: +.sect .rom +.align _EM_WSIZE +.define endrom +endrom: +.sect .data +.align _EM_WSIZE +.define __edata, enddata +__edata: +enddata: +.sect .bss +.align _EM_WSIZE +.sect .end ! only for declaration of _end, __end or endbss. +.define __end, endbss +__end: +endbss: diff --git a/lib/end/end.s b/lib/end/end.s new file mode 100755 index 000000000..bbd338d9e --- /dev/null +++ b/lib/end/end.s @@ -0,0 +1,7 @@ +.sect .text +.sect .rom +.sect .data +.sect .bss +.sect .end ! only for declaration of _end, __end or endbss. +.define _end +_end: diff --git a/lib/end/etext.s b/lib/end/etext.s new file mode 100755 index 000000000..7ff13474e --- /dev/null +++ b/lib/end/etext.s @@ -0,0 +1,5 @@ +# +.sect .text +.align _EM_WSIZE +.define _etext +_etext: diff --git a/lib/float/FP.compile b/lib/float/FP.compile new file mode 100755 index 000000000..6e24ac3a3 --- /dev/null +++ b/lib/float/FP.compile @@ -0,0 +1,20 @@ +#!/bin/sh +# Author: Kees J. Bot +# Compile one soft FP source file. +# (These files shouldn't be optimized normally, but the 16-bit C compiler +# only optimizes scratch register allocation a bit with -O. To the 32-bit +# compiler -O is a no-op.) + +case $#:$1 in +1:*.c) ;; +*) echo "$0: $1: not a C file" >&2; exit 1 +esac + +base="`basename "$1" .c`" +trap 'rm -f tmp.s "$base.s"; exit 1' 2 + +cc -O -I. -D_MINIX -D_POSIX_SOURCE -S "$1" && +mv "$base.s" tmp.s && +sed -f FP.script tmp.s > "$base.s" && +cc -c "$base.s" && +rm tmp.s "$base.s" diff --git a/lib/float/FP.script b/lib/float/FP.script new file mode 100755 index 000000000..56a3fd608 --- /dev/null +++ b/lib/float/FP.script @@ -0,0 +1,39 @@ +s/_adf4/.adf4/ +s/_adf8/.adf8/ +s/_cff4/.cff4/ +s/_cff8/.cff8/ +s/_cfi/.cfi/ +s/_cfu/.cfu/ +s/_cif4/.cif4/ +s/_cif8/.cif8/ +s/_cmf4/.cmf4/ +s/_cmf8/.cmf8/ +s/_cuf4/.cuf4/ +s/_cuf8/.cuf8/ +s/_dvf4/.dvf4/ +s/_dvf8/.dvf8/ +s/_fef4/.fef4/ +s/_fef8/.fef8/ +s/_fif4/.fif4/ +s/_fif8/.fif8/ +s/_mlf4/.mlf4/ +s/_mlf8/.mlf8/ +s/_ngf4/.ngf4/ +s/_ngf8/.ngf8/ +s/_sbf4/.sbf4/ +s/_sbf8/.sbf8/ +s/_zrf4/.zrf4/ +s/_zrf8/.zrf8/ +s/_add_ext/.add_ext/ +s/_div_ext/.div_ext/ +s/_mul_ext/.mul_ext/ +s/_nrm_ext/.nrm_ext/ +s/_sft_ext/.sft_ext/ +s/_sub_ext/.sub_ext/ +s/_zrf_ext/.zrf_ext/ +s/_compact/.compact/ +s/_extend/.extend/ +s/_b64_add/.b64_add/ +s/_b64_sft/.b64_sft/ +s/_b64_rsft/.b64_rsft/ +s/_b64_lsft/.b64_lsft/ diff --git a/lib/float/FP_bias.h b/lib/float/FP_bias.h new file mode 100755 index 000000000..db17a4132 --- /dev/null +++ b/lib/float/FP_bias.h @@ -0,0 +1,28 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + include file for floating point package +*/ + + /* FLOAT FORMAT EXPONENT BIAS */ + +#define SGL_BIAS 127 /* excess 128 notation used */ +#define DBL_BIAS 1023 /* excess 1024 notation used */ +#define EXT_BIAS 0 /* 2s-complement notation used */ + /* this is possible because the */ + /* sign is in a seperate word */ + + /* VARIOUS MAX AND MIN VALUES */ + /* 1) FOR THE DIFFERENT FORMATS */ + +#define SGL_MAX 254 /* standard definition */ +#define SGL_MIN 1 /* standard definition */ +#define DBL_MAX 2046 /* standard definition */ +#define DBL_MIN 1 /* standard definition */ +#define EXT_MAX 16383 /* standard minimum */ +#define EXT_MIN -16382 /* standard minimum */ diff --git a/lib/float/FP_shift.h b/lib/float/FP_shift.h new file mode 100755 index 000000000..5b685636f --- /dev/null +++ b/lib/float/FP_shift.h @@ -0,0 +1,49 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + include file for floating point package +*/ + +# define CARRYBIT 0x80000000L +# define NORMBIT 0x80000000L +# define EXP_STORE 16 + + + /* parameters for Single Precision */ +#define SGL_EXPSHIFT 7 +#define SGL_M1LEFT 8 +#define SGL_ZERO 0xffffff80L +#define SGL_EXACT 0xff +#define SGL_RUNPACK SGL_M1LEFT + +#define SGL_ROUNDUP 0x80 +#define SGL_CARRYOUT 0x01000000L +#define SGL_MASK 0x007fffffL + + /* parameters for Double Precision */ + /* used in extend.c */ + +#define DBL_EXPSHIFT 4 + +#define DBL_M1LEFT 11 + +#define DBL_RPACK (32-DBL_M1LEFT) +#define DBL_LPACK DBL_M1LEFT + + /* used in compact.c */ + +#define DBL_ZERO 0xfffffd00L + +#define DBL_EXACT 0x7ff + +#define DBL_RUNPACK DBL_M1LEFT +#define DBL_LUNPACK (32-DBL_RUNPACK) + +#define DBL_ROUNDUP 0x400 +#define DBL_CARRYOUT 0x00200000L +#define DBL_MASK 0x000fffffL diff --git a/lib/float/FP_trap.h b/lib/float/FP_trap.h new file mode 100755 index 000000000..045897bc3 --- /dev/null +++ b/lib/float/FP_trap.h @@ -0,0 +1,22 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + include file for floating point package +*/ + + /* EM TRAPS */ + +#define EIOVFL 3 /* Integer Overflow */ +#define EFOVFL 4 /* Floating Overflow */ +#define EFUNFL 5 /* Floating Underflow */ +#define EIDIVZ 6 /* Integer Divide by 0 */ +#define EFDIVZ 7 /* Floating Divide by 0.0 */ +#define EIUND 8 /* Integer Undefined Number */ +#define EFUND 9 /* Floating Undefined Number */ +#define ECONV 10 /* Conversion Error */ +# define trap(x) _fptrp(x) diff --git a/lib/float/FP_types.h b/lib/float/FP_types.h new file mode 100755 index 000000000..a23f32b89 --- /dev/null +++ b/lib/float/FP_types.h @@ -0,0 +1,113 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/********************************************************/ +/* + Type definitions for C Floating Point Package + include file for floating point package +*/ +/********************************************************/ +/* + THESE STRUCTURES ARE USED TO ADDRESS THE INDIVIDUAL + PARTS OF THE FLOATING POINT NUMBER REPRESENTATIONS. + + THREE STRUCTURES ARE DEFINED: + SINGLE: single precision floating format + DOUBLE: double precision floating format + EXTEND: double precision extended format +*/ +/********************************************************/ + +#ifndef __FPTYPES +#define __FPTYPES + +typedef struct { + unsigned long h_32; /* higher 32 bits of 64 */ + unsigned long l_32; /* lower 32 bits of 64 */ +} B64; + +typedef unsigned long SINGLE; + +typedef struct { + unsigned long d[2]; +} DOUBLE; + +typedef struct { /* expanded float format */ + short sign; + short exp; + B64 mantissa; +#define m1 mantissa.h_32 +#define m2 mantissa.l_32 +} EXTEND; + +struct fef4_returns { + int e; + SINGLE f; +}; + +struct fef8_returns { + int e; + DOUBLE f; +}; + +struct fif4_returns { + SINGLE ipart; + SINGLE fpart; +}; + +struct fif8_returns { + DOUBLE ipart; + DOUBLE fpart; +}; + +#if __STDC__ +#define _PROTOTYPE(function, params) function params +#else +#define _PROTOTYPE(function, params) function() +#endif +_PROTOTYPE( void add_ext, (EXTEND *e1, EXTEND *e2)); +_PROTOTYPE( void mul_ext, (EXTEND *e1, EXTEND *e2)); +_PROTOTYPE( void div_ext, (EXTEND *e1, EXTEND *e2)); +_PROTOTYPE( void sub_ext, (EXTEND *e1, EXTEND *e2)); +_PROTOTYPE( void sft_ext, (EXTEND *e1, EXTEND *e2)); +_PROTOTYPE( void nrm_ext, (EXTEND *e1)); +_PROTOTYPE( void zrf_ext, (EXTEND *e1)); +_PROTOTYPE( void extend, (unsigned long *from, EXTEND *to, int size)); +_PROTOTYPE( void compact, (EXTEND *from, unsigned long *to, int size)); +_PROTOTYPE( void _fptrp, (int)); +_PROTOTYPE( void adf4, (SINGLE s2, SINGLE s1)); +_PROTOTYPE( void adf8, (DOUBLE s2, DOUBLE s1)); +_PROTOTYPE( void sbf4, (SINGLE s2, SINGLE s1)); +_PROTOTYPE( void sbf8, (DOUBLE s2, DOUBLE s1)); +_PROTOTYPE( void dvf4, (SINGLE s2, SINGLE s1)); +_PROTOTYPE( void dvf8, (DOUBLE s2, DOUBLE s1)); +_PROTOTYPE( void mlf4, (SINGLE s2, SINGLE s1)); +_PROTOTYPE( void mlf8, (DOUBLE s2, DOUBLE s1)); +_PROTOTYPE( void ngf4, (SINGLE f)); +_PROTOTYPE( void ngf8, (DOUBLE f)); +_PROTOTYPE( void zrf4, (SINGLE *l)); +_PROTOTYPE( void zrf8, (DOUBLE *z)); +_PROTOTYPE( void cff4, (DOUBLE src)); +_PROTOTYPE( void cff8, (SINGLE src)); +_PROTOTYPE( void cif4, (int ss, long src)); +_PROTOTYPE( void cif8, (int ss, long src)); +_PROTOTYPE( void cuf4, (int ss, long src)); +_PROTOTYPE( void cuf8, (int ss, long src)); +_PROTOTYPE( long cfu, (int ds, int ss, DOUBLE src)); +_PROTOTYPE( long cfi, (int ds, int ss, DOUBLE src)); +_PROTOTYPE( int cmf4, (SINGLE s2, SINGLE s1)); +_PROTOTYPE( int cmf8, (DOUBLE d1, DOUBLE d2)); +_PROTOTYPE( void fef4, (struct fef4_returns *r, SINGLE s1)); +_PROTOTYPE( void fef8, (struct fef8_returns *r, DOUBLE s1)); +_PROTOTYPE( void fif4, (struct fif4_returns *p, SINGLE x, SINGLE y)); +_PROTOTYPE( void fif8, (struct fif8_returns *p, DOUBLE x, DOUBLE y)); + +_PROTOTYPE( void b64_sft, (B64 *, int)); +_PROTOTYPE( void b64_lsft, (B64 *)); +_PROTOTYPE( void b64_rsft, (B64 *)); +_PROTOTYPE( int b64_add, (B64 *, B64 *)); +#endif diff --git a/lib/float/Makefile b/lib/float/Makefile new file mode 100755 index 000000000..f4e251c4b --- /dev/null +++ b/lib/float/Makefile @@ -0,0 +1,164 @@ +# Makefile for lib/float. + +CC1 = ./FP.compile + +LIBRARY = ../libfp.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(add_ext.o) \ + $(LIBRARY)(adder.o) \ + $(LIBRARY)(adf4.o) \ + $(LIBRARY)(adf8.o) \ + $(LIBRARY)(cff4.o) \ + $(LIBRARY)(cff8.o) \ + $(LIBRARY)(cfi.o) \ + $(LIBRARY)(cfu.o) \ + $(LIBRARY)(cif4.o) \ + $(LIBRARY)(cif8.o) \ + $(LIBRARY)(cmf4.o) \ + $(LIBRARY)(cmf8.o) \ + $(LIBRARY)(compact.o) \ + $(LIBRARY)(cuf4.o) \ + $(LIBRARY)(cuf8.o) \ + $(LIBRARY)(div_ext.o) \ + $(LIBRARY)(dvf4.o) \ + $(LIBRARY)(dvf8.o) \ + $(LIBRARY)(extend.o) \ + $(LIBRARY)(fef4.o) \ + $(LIBRARY)(fef8.o) \ + $(LIBRARY)(fif4.o) \ + $(LIBRARY)(fif8.o) \ + $(LIBRARY)(fptrp.o) \ + $(LIBRARY)(mlf4.o) \ + $(LIBRARY)(mlf8.o) \ + $(LIBRARY)(mul_ext.o) \ + $(LIBRARY)(ngf4.o) \ + $(LIBRARY)(ngf8.o) \ + $(LIBRARY)(nrm_ext.o) \ + $(LIBRARY)(sbf4.o) \ + $(LIBRARY)(sbf8.o) \ + $(LIBRARY)(sft_ext.o) \ + $(LIBRARY)(shifter.o) \ + $(LIBRARY)(sub_ext.o) \ + $(LIBRARY)(zrf4.o) \ + $(LIBRARY)(zrf8.o) \ + $(LIBRARY)(zrf_ext.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(add_ext.o): add_ext.c + $(CC1) add_ext.c + +$(LIBRARY)(adder.o): adder.c + $(CC1) adder.c + +$(LIBRARY)(adf4.o): adf4.c + $(CC1) adf4.c + +$(LIBRARY)(adf8.o): adf8.c + $(CC1) adf8.c + +$(LIBRARY)(cff4.o): cff4.c + $(CC1) cff4.c + +$(LIBRARY)(cff8.o): cff8.c + $(CC1) cff8.c + +$(LIBRARY)(cfi.o): cfi.c + $(CC1) cfi.c + +$(LIBRARY)(cfu.o): cfu.c + $(CC1) cfu.c + +$(LIBRARY)(cif4.o): cif4.c + $(CC1) cif4.c + +$(LIBRARY)(cif8.o): cif8.c + $(CC1) cif8.c + +$(LIBRARY)(cmf4.o): cmf4.c + $(CC1) cmf4.c + +$(LIBRARY)(cmf8.o): cmf8.c + $(CC1) cmf8.c + +$(LIBRARY)(compact.o): compact.c + $(CC1) compact.c + +$(LIBRARY)(cuf4.o): cuf4.c + $(CC1) cuf4.c + +$(LIBRARY)(cuf8.o): cuf8.c + $(CC1) cuf8.c + +$(LIBRARY)(div_ext.o): div_ext.c + $(CC1) div_ext.c + +$(LIBRARY)(dvf4.o): dvf4.c + $(CC1) dvf4.c + +$(LIBRARY)(dvf8.o): dvf8.c + $(CC1) dvf8.c + +$(LIBRARY)(extend.o): extend.c + $(CC1) extend.c + +$(LIBRARY)(fef4.o): fef4.c + $(CC1) fef4.c + +$(LIBRARY)(fef8.o): fef8.c + $(CC1) fef8.c + +$(LIBRARY)(fif4.o): fif4.c + $(CC1) fif4.c + +$(LIBRARY)(fif8.o): fif8.c + $(CC1) fif8.c + +$(LIBRARY)(fptrp.o): fptrp.s + $(CC) -c fptrp.s + +$(LIBRARY)(mlf4.o): mlf4.c + $(CC1) mlf4.c + +$(LIBRARY)(mlf8.o): mlf8.c + $(CC1) mlf8.c + +$(LIBRARY)(mul_ext.o): mul_ext.c + $(CC1) mul_ext.c + +$(LIBRARY)(ngf4.o): ngf4.c + $(CC1) ngf4.c + +$(LIBRARY)(ngf8.o): ngf8.c + $(CC1) ngf8.c + +$(LIBRARY)(nrm_ext.o): nrm_ext.c + $(CC1) nrm_ext.c + +$(LIBRARY)(sbf4.o): sbf4.c + $(CC1) sbf4.c + +$(LIBRARY)(sbf8.o): sbf8.c + $(CC1) sbf8.c + +$(LIBRARY)(sft_ext.o): sft_ext.c + $(CC1) sft_ext.c + +$(LIBRARY)(shifter.o): shifter.c + $(CC1) shifter.c + +$(LIBRARY)(sub_ext.o): sub_ext.c + $(CC1) sub_ext.c + +$(LIBRARY)(zrf4.o): zrf4.c + $(CC1) zrf4.c + +$(LIBRARY)(zrf8.o): zrf8.c + $(CC1) zrf8.c + +$(LIBRARY)(zrf_ext.o): zrf_ext.c + $(CC1) zrf_ext.c diff --git a/lib/float/add_ext.c b/lib/float/add_ext.c new file mode 100755 index 000000000..a077663ab --- /dev/null +++ b/lib/float/add_ext.c @@ -0,0 +1,56 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + ADD TWO EXTENDED FORMAT NUMBERS +*/ + +#include "FP_types.h" + +void +add_ext(e1,e2) +register EXTEND *e1,*e2; +{ + if ((e2->m1 | e2->m2) == 0L) { + return; + } + if ((e1->m1 | e1->m2) == 0L) { + *e1 = *e2; + return; + } + sft_ext(e1, e2); /* adjust mantissas to equal powers */ + if (e1->sign != e2->sign) { + /* e1 + e2 = e1 - (-e2) */ + if (e2->m1 > e1->m1 || + (e2->m1 == e1->m1 && e2->m2 > e1->m2)) { + /* abs(e2) > abs(e1) */ + EXTEND x; + + x = *e1; + *e1 = *e2; + if (x.m2 > e1->m2) { + e1->m1 -= 1; /* carry in */ + } + e1->m1 -= x.m1; + e1->m2 -= x.m2; + } + else { + if (e2->m2 > e1->m2) + e1->m1 -= 1; /* carry in */ + e1->m1 -= e2->m1; + e1->m2 -= e2->m2; + } + } + else { + if (b64_add(&e1->mantissa,&e2->mantissa)) { /* addition carry */ + b64_rsft(&e1->mantissa); /* shift mantissa one bit RIGHT */ + e1->m1 |= 0x80000000L; /* set max bit */ + e1->exp++; /* increase the exponent */ + } + } + nrm_ext(e1); +} diff --git a/lib/float/adder.c b/lib/float/adder.c new file mode 100755 index 000000000..a0af3ce75 --- /dev/null +++ b/lib/float/adder.c @@ -0,0 +1,50 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + * these are the routines the routines to do 32 and 64-bit addition + */ + +# ifdef EXT_DEBUG +# include <stdio.h> +# endif + +# include "FP_types.h" +# define UNKNOWN -1 +# define TRUE 1 +# define FALSE 0 +# define MAXBIT 0x80000000L + + /* + * add 64 bits + */ +int +b64_add(e1,e2) + /* + * pointers to 64 bit 'registers' + */ +register B64 *e1,*e2; +{ + register int overflow; + int carry; + + /* add higher pair of 32 bits */ + overflow = ((unsigned long) 0xFFFFFFFF - e1->h_32 < e2->h_32); + e1->h_32 += e2->h_32; + + /* add lower pair of 32 bits */ + carry = ((unsigned long) 0xFFFFFFFF - e1->l_32 < e2->l_32); + e1->l_32 += e2->l_32; +# ifdef EXT_DEBUG + printf("\t\t\t\t\tb64_add: overflow (%d); internal carry(%d)\n", + overflow,carry); + fflush(stdout); +# endif + if ((carry) && (++e1->h_32 == 0)) + return(TRUE); /* had a 64 bit overflow */ + return(overflow); /* return status from higher add */ +} diff --git a/lib/float/adder.h b/lib/float/adder.h new file mode 100755 index 000000000..2fed41451 --- /dev/null +++ b/lib/float/adder.h @@ -0,0 +1,15 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + * include file for 32 & 64 bit addition + */ + +typedef struct B64 { + unsigned long h_32; /* higher 32 bits of 64 */ + unsigned long l_32; /* lower 32 bits of 64 */ +} B64; diff --git a/lib/float/adf4.c b/lib/float/adf4.c new file mode 100755 index 000000000..572f3daa3 --- /dev/null +++ b/lib/float/adf4.c @@ -0,0 +1,32 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + ADD TWO FLOATS - SINGLE (ADF 4) +*/ + +#include "FP_types.h" + +void +adf4(s2,s1) +SINGLE s1,s2; +{ + EXTEND e1,e2; + int swap = 0; + + if (s1 == (SINGLE) 0) { + s1 = s2; + return; + } + if (s2 == (SINGLE) 0) { + return; + } + extend(&s1,&e1,sizeof(SINGLE)); + extend(&s2,&e2,sizeof(SINGLE)); + add_ext(&e1,&e2); + compact(&e1,&s1,sizeof(SINGLE)); +} diff --git a/lib/float/adf8.c b/lib/float/adf8.c new file mode 100755 index 000000000..387c975c0 --- /dev/null +++ b/lib/float/adf8.c @@ -0,0 +1,32 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + ADD TWO FLOATS - DOUBLE (ADF 8) +*/ + +#include "FP_types.h" + +void +adf8(s2,s1) +DOUBLE s1,s2; +{ + EXTEND e1,e2; + + if (s1.d[0] == 0 && s1.d[1] == 0) { + s1 = s2; + return; + } + if (s2.d[0] == 0 && s2.d[1] == 0) { + return; + } + + extend(&s1.d[0],&e1,sizeof(DOUBLE)); + extend(&s2.d[0],&e2,sizeof(DOUBLE)); + add_ext(&e1,&e2); + compact(&e1,&s1.d[0],sizeof(DOUBLE)); +} diff --git a/lib/float/byte_order.h b/lib/float/byte_order.h new file mode 100755 index 000000000..d08b45a5d --- /dev/null +++ b/lib/float/byte_order.h @@ -0,0 +1,6 @@ +#define CHAR_UNSIGNED 0 +#define MSB_AT_LOW_ADDRESS 0 +#define MSW_AT_LOW_ADDRESS 0 +#define FL_MSB_AT_LOW_ADDRESS 0 +#define FL_MSW_AT_LOW_ADDRESS 0 +#define FL_MSL_AT_LOW_ADDRESS 0 diff --git a/lib/float/cff4.c b/lib/float/cff4.c new file mode 100755 index 000000000..ae3b740c5 --- /dev/null +++ b/lib/float/cff4.c @@ -0,0 +1,28 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + CONVERT DOUBLE TO SINGLE (CFF 8 4) + + This routine works quite simply. A floating point + of size 08 is converted to extended format. + This extended variable is converted back to + a floating point of size 04. + +*/ + +#include "FP_types.h" + +void +cff4(src) +DOUBLE src; /* the source itself - THIS TIME it's DOUBLE */ +{ + EXTEND buf; + + extend(&src.d[0],&buf,sizeof(DOUBLE)); /* no matter what */ + compact(&buf,&(src.d[1]),sizeof(SINGLE)); +} diff --git a/lib/float/cff8.c b/lib/float/cff8.c new file mode 100755 index 000000000..a851803ad --- /dev/null +++ b/lib/float/cff8.c @@ -0,0 +1,28 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + CONVERT SINGLE TO DOUBLE (CFF 4 8) + + This routine works quite simply. A floating point + of size 04 is converted to extended format. + This extended variable is converted back to + a floating point of size 08. + +*/ + +#include "FP_types.h" + +void +cff8(src) +SINGLE src; +{ + EXTEND buf; + + extend(&src,&buf,sizeof(SINGLE)); /* no matter what */ + compact(&buf, &src,sizeof(DOUBLE)); +} diff --git a/lib/float/cfi.c b/lib/float/cfi.c new file mode 100755 index 000000000..cfd28230e --- /dev/null +++ b/lib/float/cfi.c @@ -0,0 +1,52 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + CONVERT FLOAT TO SIGNED (CFI m n) + + N.B. The caller must know what it is getting. + A LONG is always returned. If it is an + integer the high byte is cleared first. +*/ + +#include "FP_trap.h" +#include "FP_types.h" +#include "FP_shift.h" + +long +cfi(ds,ss,src) +int ds; /* destination size (2 or 4) */ +int ss; /* source size (4 or 8) */ +DOUBLE src; /* assume worst case */ +{ + EXTEND buf; + long new; + short max_exp; + + extend(&src.d[0],&buf,ss); /* get extended format */ + if (buf.exp < 0) { /* no conversion needed */ + src.d[ss == 8] = 0L; + return(0L); + } + max_exp = (ds << 3) - 2; /* signed numbers */ + /* have more limited max_exp */ + if (buf.exp > max_exp) { + if (buf.exp == max_exp+1 && buf.sign && buf.m1 == NORMBIT && + buf.m2 == 0L) { + } + else { + trap(EIOVFL); /* integer overflow */ + buf.exp %= max_exp; /* truncate */ + } + } + new = buf.m1 >> (31-buf.exp); + if (buf.sign) + new = -new; +done: + src.d[ss == 8] = new; + return(new); +} diff --git a/lib/float/cfu.c b/lib/float/cfu.c new file mode 100755 index 000000000..e9a551ac7 --- /dev/null +++ b/lib/float/cfu.c @@ -0,0 +1,43 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + CONVERT FLOAT TO UNSIGNED (CFU m n) + + N.B. The caller must know what it is getting. + A LONG is always returned. If it is an + integer the high byte is cleared first. +*/ + +#include "FP_trap.h" +#include "FP_types.h" + +long +cfu(ds,ss,src) +int ds; /* destination size (2 or 4) */ +int ss; /* source size (4 or 8) */ +DOUBLE src; /* assume worst case */ +{ + EXTEND buf; + long new; + short newint, max_exp; + + extend(&src.d[0],&buf,ss); /* get extended format */ + if (buf.exp < 0) { /* no conversion needed */ + src.d[ss == 8] = 0L; + return(0L); + } + max_exp = (ds << 3) - 1; + if (buf.exp > max_exp) { + trap(EIOVFL); /* integer overflow */ + buf.exp %= max_exp; + } + new = buf.m1 >> (31-buf.exp); +done: + src.d[ss == 8] = new; + return(new); +} diff --git a/lib/float/cif4.c b/lib/float/cif4.c new file mode 100755 index 000000000..160d5f6d2 --- /dev/null +++ b/lib/float/cif4.c @@ -0,0 +1,56 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + CONVERT INTEGER TO SINGLE (CIF n 4) + + THIS ROUTINE WORKS BY FILLING AN EXTENDED + WITH THE INTEGER VALUE IN EXTENDED FORMAT + AND USES COMPACT() TO PUT IT INTO THE PROPER + FLOATING POINT PRECISION. +*/ + +#include "FP_types.h" + +void +cif4(ss,src) +int ss; /* source size */ +long src; /* largest possible integer to convert */ +{ + EXTEND buf; + short *ipt; + long i_src; + SINGLE *result; + + zrf_ext(&buf); + if (ss == sizeof(long)) { + buf.exp = 31; + i_src = src; + result = (SINGLE *) &src; + } + else { + ipt = (short *) &src; + i_src = (long) *ipt; + buf.exp = 15; + result = (SINGLE *) &ss; + } + if (i_src == 0) { + *result = (SINGLE) 0L; + return; + } + /* ESTABLISHED THAT src != 0 */ + /* adjust exponent field */ + buf.sign = (i_src < 0) ? 0x8000 : 0; + /* clear sign bit of integer */ + /* move to mantissa field */ + buf.m1 = (i_src < 0) ? -i_src : i_src; + /* adjust mantissa field */ + if (ss != sizeof(long)) + buf.m1 <<= 16; + nrm_ext(&buf); /* adjust mantissa field */ + compact(&buf, result,sizeof(SINGLE)); /* put on stack */ +} diff --git a/lib/float/cif8.c b/lib/float/cif8.c new file mode 100755 index 000000000..1ab979813 --- /dev/null +++ b/lib/float/cif8.c @@ -0,0 +1,55 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + CONVERT INTEGER TO FLOAT (CIF n 8) + + THIS ROUTINE WORKS BY FILLING AN EXTENDED + WITH THE INTEGER VALUE IN EXTENDED FORMAT + AND USES COMPACT() TO PUT IT INTO THE PROPER + FLOATING POINT PRECISION. +*/ + +#include "FP_types.h" + +void +cif8(ss,src) +int ss; /* source size */ +long src; /* largest possible integer to convert */ +{ + EXTEND buf; + DOUBLE *result; /* for return value */ + short *ipt; + long i_src; + + result = (DOUBLE *) ((void *) &ss); /* always */ + zrf_ext(&buf); + if (ss == sizeof(long)) { + buf.exp = 31; + i_src = src; + } + else { + ipt = (short *) &src; + i_src = (long) *ipt; + buf.exp = 15; + } + if (i_src == 0) { + zrf8(result); + return; + } + /* ESTABLISHED THAT src != 0 */ + /* adjust exponent field */ + buf.sign = (i_src < 0) ? 0x8000 : 0; + /* clear sign bit of integer */ + /* move to mantissa field */ + buf.m1 = (i_src < 0) ? -i_src : i_src; + /* adjust mantissa field */ + if (ss != sizeof(long)) + buf.m1 <<= 16; + nrm_ext(&buf); + compact(&buf,&result->d[0],8); +} diff --git a/lib/float/cmf4.c b/lib/float/cmf4.c new file mode 100755 index 000000000..ee186ff4c --- /dev/null +++ b/lib/float/cmf4.c @@ -0,0 +1,40 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + COMPARE SINGLES (CMF 4) +*/ + +#include "FP_types.h" +#include "get_put.h" + +int +cmf4(f1,f2) +SINGLE f1,f2; +{ + /* + * return ((f1 < f2) ? 1 : (f1 - f2)) + */ +#define SIGN(x) (((x) < 0) ? -1 : 1) + int sign1,sign2; + long l1,l2; + + l1 = get4((char *) &f1); + l2 = get4((char *) &f2); + + if (l1 == l2) return 0; + + sign1 = SIGN(l1); + sign2 = SIGN(l2); + if (sign1 != sign2) { + if ((l1 & 0x7fffffff) == 0 && + (l2 & 0x7fffffff) == 0) return 0; + return ((sign1 > 0) ? -1 : 1); + } + + return (sign1 * ((l1 < l2) ? 1 : -1)); +} diff --git a/lib/float/cmf8.c b/lib/float/cmf8.c new file mode 100755 index 000000000..5badab0c4 --- /dev/null +++ b/lib/float/cmf8.c @@ -0,0 +1,61 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + COMPARE DOUBLES (CMF 8) +*/ + +#include "FP_types.h" +#include "get_put.h" + +int +cmf8(d1,d2) +DOUBLE d1,d2; +{ +#define SIGN(x) (((x) < 0) ? -1 : 1) + /* + * return ((d1 < d2) ? 1 : (d1 > d2) ? -1 : 0)) + */ + long l1,l2; + int sign1,sign2; + int rv; + +#if FL_MSL_AT_LOW_ADDRESS + l1 = get4((char *)&d1); + l2 = get4((char *)&d2); +#else + l1 = get4(((char *)&d1+4)); + l2 = get4(((char *)&d2+4)); +#endif + sign1 = SIGN(l1); + sign2 = SIGN(l2); + if (sign1 != sign2) { + l1 &= 0x7fffffff; + l2 &= 0x7fffffff; + if (l1 != 0 || l2 != 0) { + return ((sign1 > 0) ? -1 : 1); + } + } + if (l1 != l2) { /* we can decide here */ + rv = l1 < l2 ? 1 : -1; + } + else { /* decide in 2nd half */ + unsigned long u1, u2; +#if FL_MSL_AT_LOW_ADDRESS + u1 = get4(((char *)&d1 + 4)); + u2 = get4(((char *)&d2 + 4)); +#else + u1 = get4((char *)&d1); + u2 = get4((char *)&d2); +#endif + if (u1 == u2) + return(0); + if (u1 < u2) rv = 1; + else rv = -1; + } + return sign1 * rv; +} diff --git a/lib/float/compact.c b/lib/float/compact.c new file mode 100755 index 000000000..a5a1074d1 --- /dev/null +++ b/lib/float/compact.c @@ -0,0 +1,202 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + COMPACT EXTEND FORMAT INTO FLOAT OF PROPER SIZE +*/ + +# include "FP_bias.h" +# include "FP_shift.h" +# include "FP_trap.h" +# include "FP_types.h" +# include "get_put.h" + +void +compact(f,to,size) +EXTEND *f; +unsigned long *to; +int size; +{ + int error = 0; + + if (size == sizeof(DOUBLE)) { + /* + * COMPACT EXTENDED INTO DOUBLE + */ + DOUBLE *DBL = (DOUBLE *) (void *) to; + + if ((f->m1|(f->m2 & DBL_ZERO)) == 0L) { + zrf8(DBL); + return; + } + f->exp += DBL_BIAS; /* restore proper bias */ + if (f->exp > DBL_MAX) { +dbl_over: trap(EFOVFL); + f->exp = DBL_MAX+1; + f->m1 = 0; + f->m2 = 0; + if (error++) + return; + } + else if (f->exp < DBL_MIN) { + b64_rsft(&(f->mantissa)); + if (f->exp < 0) { + b64_sft(&(f->mantissa), -f->exp); + f->exp = 0; + } + /* underflow ??? */ + } + + /* local CAST conversion */ + + /* because of special format shift only 10 bits */ + /* bit shift mantissa 10 bits */ + + /* first align within words, then do store operation */ + + DBL->d[0] = f->m1 >> DBL_RUNPACK; /* plus 22 == 32 */ + DBL->d[1] = f->m2 >> DBL_RUNPACK; /* plus 22 == 32 */ + DBL->d[1] |= (f->m1 << DBL_LUNPACK); /* plus 10 == 32 */ + + /* if not exact then round to nearest */ + /* on a tie, round to even */ + +#ifdef EXCEPTION_INEXACT + if ((f->m2 & DBL_EXACT) != 0) { + INEXACT(); +#endif + if (((f->m2 & DBL_EXACT) > DBL_ROUNDUP) + || ((f->m2 & DBL_EXACT) == DBL_ROUNDUP + && (f->m2 & (DBL_ROUNDUP << 1)))) { + DBL->d[1]++; /* rounding up */ + if (DBL->d[1] == 0L) { /* carry out */ + DBL->d[0]++; + + if (f->exp == 0 && (DBL->d[0] & ~DBL_MASK)) { + f->exp++; + } + if (DBL->d[0] & DBL_CARRYOUT) { /* carry out */ + if (DBL->d[0] & 01) + DBL->d[1] = CARRYBIT; + DBL->d[0] >>= 1; + f->exp++; + } + } + /* check for overflow */ + if (f->exp > DBL_MAX) + goto dbl_over; + } +#ifdef EXCEPTION_INEXACT + } +#endif + + /* + * STORE EXPONENT AND SIGN: + * + * 1) clear leading bits (B4-B15) + * 2) shift and store exponent + */ + + DBL->d[0] &= DBL_MASK; + DBL->d[0] |= + ((long) (f->exp << DBL_EXPSHIFT) << EXP_STORE); + if (f->sign) + DBL->d[0] |= CARRYBIT; + + /* + * STORE MANTISSA + */ + +#if FL_MSL_AT_LOW_ADDRESS + put4(DBL->d[0], (char *) &DBL->d[0]); + put4(DBL->d[1], (char *) &DBL->d[1]); +#else + { unsigned long l; + put4(DBL->d[1], (char *) &l); + put4(DBL->d[0], (char *) &DBL->d[1]); + DBL->d[0] = l; + } +#endif + } + else { + /* + * COMPACT EXTENDED INTO FLOAT + */ + SINGLE *SGL; + + /* local CAST conversion */ + SGL = (SINGLE *) (void *) to; + if ((f->m1 & SGL_ZERO) == 0L) { + *SGL = 0L; + return; + } + f->exp += SGL_BIAS; /* restore bias */ + if (f->exp > SGL_MAX) { +sgl_over: trap(EFOVFL); + f->exp = SGL_MAX+1; + f->m1 = 0L; + f->m2 = 0L; + if (error++) + return; + } + else if (f->exp < SGL_MIN) { + b64_rsft(&(f->mantissa)); + if (f->exp < 0) { + b64_sft(&(f->mantissa), -f->exp); + f->exp = 0; + } + /* underflow ??? */ + } + + /* shift mantissa and store */ + *SGL = (f->m1 >> SGL_RUNPACK); + + /* check for rounding to nearest */ + /* on a tie, round to even */ +#ifdef EXCEPTION_INEXACT + if (f->m2 != 0 || + (f->m1 & SGL_EXACT) != 0L) { + INEXACT(); +#endif + if (((f->m1 & SGL_EXACT) > SGL_ROUNDUP) + || ((f->m1 & SGL_EXACT) == SGL_ROUNDUP + && (f->m1 & (SGL_ROUNDUP << 1)))) { + (*SGL)++; + if (f->exp == 0 && (*SGL & ~SGL_MASK)) { + f->exp++; + } + /* check normal */ + if (*SGL & SGL_CARRYOUT) { + *SGL >>= 1; + f->exp++; + } + if (f->exp > SGL_MAX) + goto sgl_over; + } +#ifdef EXCEPTION_INEXACT + } +#endif + + /* + * STORE EXPONENT AND SIGN: + * + * 1) clear leading bit of fraction + * 2) shift and store exponent + */ + + *SGL &= SGL_MASK; /* B23-B31 are 0 */ + *SGL |= ((long) (f->exp << SGL_EXPSHIFT) << EXP_STORE); + if (f->sign) + *SGL |= CARRYBIT; + + /* + * STORE MANTISSA + */ + + put4(*SGL, (char *) &SGL); + } +} diff --git a/lib/float/cuf4.c b/lib/float/cuf4.c new file mode 100755 index 000000000..c022f0e90 --- /dev/null +++ b/lib/float/cuf4.c @@ -0,0 +1,57 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + CONVERT INTEGER TO SINGLE (CUF n 4) + + THIS ROUTINE WORKS BY FILLING AN EXTENDED + WITH THE INTEGER VALUE IN EXTENDED FORMAT + AND USES COMPACT() TO PUT IT INTO THE PROPER + FLOATING POINT PRECISION. +*/ + +#include "FP_types.h" + +void +cuf4(ss,src) +int ss; /* source size */ +long src; /* largest possible integer to convert */ +{ + EXTEND buf; + short *ipt; + SINGLE *result; + long i_src; + + zrf_ext(&buf); + if (ss == sizeof(long)) { + buf.exp = 31; + i_src = src; + result = (SINGLE *) &src; + } + else { + ipt = (short *) &src; + i_src = (long) *ipt; + buf.exp = 15; + result = (SINGLE *) ((void *) &ss); + } + if (i_src == 0) { + *result = (SINGLE) 0L; + return; + } + /* ESTABLISHED THAT src != 0 */ + + /* adjust exponent field */ + if (ss != sizeof(long)) + i_src <<= 16; + + /* move to mantissa field */ + buf.m1 = i_src; + + /* adjust mantissa field */ + nrm_ext(&buf); + compact(&buf,result,4); +} diff --git a/lib/float/cuf8.c b/lib/float/cuf8.c new file mode 100755 index 000000000..d18ec6c7a --- /dev/null +++ b/lib/float/cuf8.c @@ -0,0 +1,54 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + CONVERT INTEGER TO FLOAT (CUF n 8) + + THIS ROUTINE WORKS BY FILLING AN EXTENDED + WITH THE INTEGER VALUE IN EXTENDED FORMAT + AND USES COMPACT() TO PUT IT INTO THE PROPER + FLOATING POINT PRECISION. +*/ + +#include "FP_types.h" + +void +cuf8(ss,src) +int ss; /* source size */ +long src; /* largest possible integer to convert */ +{ + EXTEND buf; + short *ipt; + long i_src; + + zrf_ext(&buf); + if (ss == sizeof(long)) { + buf.exp = 31; + i_src = src; + } + else { + ipt = (short *) &src; + i_src = (long) *ipt; + buf.exp = 15; + } + if (i_src == 0) { + zrf8((DOUBLE *)((void *)&ss)); + return; + } + /* ESTABLISHED THAT src != 0 */ + + /* adjust exponent field */ + if (ss != sizeof(long)) + i_src <<= 16; + + /* move to mantissa field */ + buf.m1 = i_src; + + /* adjust mantissa field */ + nrm_ext(&buf); + compact(&buf,(unsigned long *) (void *)&ss,8); +} diff --git a/lib/float/div_ext.c b/lib/float/div_ext.c new file mode 100755 index 000000000..bb9531178 --- /dev/null +++ b/lib/float/div_ext.c @@ -0,0 +1,266 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + DIVIDE EXTENDED FORMAT +*/ + +#include "FP_bias.h" +#include "FP_trap.h" +#include "FP_types.h" + +/* + November 15, 1984 + + This is a routine to do the work. + There are two versions: + One is based on the partial products method + and makes no use possible machine instructions + to divide (hardware dividers). + The other is used when USE_DIVIDE is defined. It is much faster on + machines with fast 4 byte operations. +*/ +/********************************************************/ + +void +div_ext(e1,e2) +EXTEND *e1,*e2; +{ + short error = 0; + B64 result; + register unsigned long *lp; +#ifndef USE_DIVIDE + short count; +#else + unsigned short u[9], v[5]; + register int j; + register unsigned short *u_p = u; + int maxv = 4; +#endif + + if ((e2->m1 | e2->m2) == 0) { + /* + * Exception 8.2 - Divide by zero + */ + trap(EFDIVZ); + e1->m1 = e1->m2 = 0L; + e1->exp = EXT_MAX; + return; + } + if ((e1->m1 | e1->m2) == 0) { /* 0 / anything == 0 */ + e1->exp = 0; /* make sure */ + return; + } +#ifndef USE_DIVIDE + /* + * numbers are right shifted one bit to make sure + * that m1 is quaranteed to be larger if its + * maximum bit is set + */ + b64_rsft(&e1->mantissa); /* 64 bit shift right */ + b64_rsft(&e2->mantissa); /* 64 bit shift right */ + e1->exp++; + e2->exp++; +#endif + /* check for underflow, divide by zero, etc */ + e1->sign ^= e2->sign; + e1->exp -= e2->exp; + +#ifndef USE_DIVIDE + /* do division of mantissas */ + /* uses partial product method */ + /* init control variables */ + + count = 64; + result.h_32 = 0L; + result.l_32 = 0L; + + /* partial product division loop */ + + while (count--) { + /* first left shift result 1 bit */ + /* this is ALWAYS done */ + + b64_lsft(&result); + + /* compare dividend and divisor */ + /* if dividend >= divisor add a bit */ + /* and subtract divisior from dividend */ + + if ( (e1->m1 < e2->m1) || + ((e1->m1 == e2->m1) && (e1->m2 < e2->m2) )) + ; /* null statement */ + /* i.e., don't add or subtract */ + else { + result.l_32++; /* ADD */ + if (e2->m2 > e1->m2) + e1->m1 -= 1; /* carry in */ + e1->m1 -= e2->m1; /* do SUBTRACTION */ + e1->m2 -= e2->m2; /* SUBTRACTION */ + } + + /* shift dividend left one bit OR */ + /* IF it equals ZERO we can break out */ + /* of the loop, but still must shift */ + /* the quotient the remaining count bits */ + /* NB save the results of this test in error */ + /* if not zero, then the result is inexact. */ + /* this would be reported in IEEE standard */ + + /* lp points to dividend */ + lp = &e1->m1; + + error = ((*lp | *(lp+1)) != 0L) ? 1 : 0; + if (error) { /* more work */ + /* assume max bit == 0 (see above) */ + b64_lsft(&e1->mantissa); + continue; + } + else + break; /* leave loop */ + } /* end of divide by subtraction loop */ + + if (count > 0) { + lp = &result.h_32; + if (count > 31) { /* move to higher word */ + *lp = *(lp+1); + count -= 32; + *(lp+1) = 0L; /* clear low word */ + } + if (*lp) + *lp <<= count; /* shift rest of way */ + lp++; /* == &result.l_32 */ + if (*lp) { + result.h_32 |= (*lp >> 32-count); + *lp <<= count; + } + } +#else /* USE_DIVIDE */ + + u[4] = (e1->m2 & 1) << 15; + b64_rsft(&(e1->mantissa)); + u[0] = e1->m1 >> 16; + u[1] = e1->m1; + u[2] = e1->m2 >> 16; + u[3] = e1->m2; + u[5] = 0; u[6] = 0; u[7] = 0; + v[1] = e2->m1 >> 16; + v[2] = e2->m1; + v[3] = e2->m2 >> 16; + v[4] = e2->m2; + while (! v[maxv]) maxv--; + result.h_32 = 0; + result.l_32 = 0; + lp = &result.h_32; + + /* + * Use an algorithm of Knuth (The art of programming, Seminumerical + * algorithms), to divide u by v. u and v are both seen as numbers + * with base 65536. + */ + for (j = 0; j <= 3; j++, u_p++) { + unsigned long q_est, temp; + + if (j == 2) lp++; + if (u_p[0] == 0 && u_p[1] < v[1]) continue; + temp = ((unsigned long)u_p[0] << 16) + u_p[1]; + if (u_p[0] >= v[1]) { + q_est = 0x0000FFFFL; + } + else { + q_est = temp / v[1]; + } + temp -= q_est * v[1]; + while (temp < 0x10000 && v[2]*q_est > ((temp<<16)+u_p[2])) { + q_est--; + temp += v[1]; + } + /* Now, according to Knuth, we have an estimate of the + quotient, that is either correct or one too big, but + almost always correct. + */ + if (q_est != 0) { + int i; + unsigned long k = 0; + int borrow = 0; + + for (i = maxv; i > 0; i--) { + unsigned long tmp = q_est * v[i] + k + borrow; + unsigned short md = tmp; + + borrow = (md > u_p[i]); + u_p[i] -= md; + k = tmp >> 16; + } + k += borrow; + borrow = u_p[0] < k; + u_p[0] -= k; + + if (borrow) { + /* So, this does not happen often; the estimate + was one too big; correct this + */ + *lp |= (j & 1) ? (q_est - 1) : ((q_est-1)<<16); + borrow = 0; + for (i = maxv; i > 0; i--) { + unsigned long tmp + = v[i]+(unsigned long)u_p[i]+borrow; + + u_p[i] = tmp; + borrow = tmp >> 16; + } + u_p[0] += borrow; + } + else *lp |= (j & 1) ? q_est : (q_est<<16); + } + } +#ifdef EXCEPTION_INEXACT + u_p = &u[0]; + for (j = 7; j >= 0; j--) { + if (*u_p++) { + error = 1; + break; + } + } +#endif +#endif + +#ifdef EXCEPTION_INEXACT + if (error) { + /* + * report here exception 8.5 - Inexact + * from Draft 8.0 of IEEE P754: + * In the absence of an invalid operation exception, + * if the rounded result of an operation is not exact or if + * it overflows without a trap, then the inexact exception + * shall be assigned. The rounded or overflowed result + * shall be delivered to the destination. + */ + INEXACT(); +#endif + e1->mantissa = result; + + nrm_ext(e1); + if (e1->exp < EXT_MIN) { + /* + * Exception 8.4 - Underflow + */ + trap(EFUNFL); /* underflow */ + e1->exp = EXT_MIN; + e1->m1 = e1->m2 = 0L; + return; + } + if (e1->exp >= EXT_MAX) { + /* + * Exception 8.3 - Overflow + */ + trap(EFOVFL); /* overflow */ + e1->exp = EXT_MAX; + e1->m1 = e1->m2 = 0L; + return; + } +} diff --git a/lib/float/dvf4.c b/lib/float/dvf4.c new file mode 100755 index 000000000..7d82cd8b8 --- /dev/null +++ b/lib/float/dvf4.c @@ -0,0 +1,26 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + DIVIDE TWO SINGLES - SINGLE Precision (dvf 4) +*/ + +#include "FP_types.h" + +void +dvf4(s2,s1) +SINGLE s1,s2; +{ + EXTEND e1,e2; + + extend(&s1,&e1,sizeof(SINGLE)); + extend(&s2,&e2,sizeof(SINGLE)); + + /* do a divide */ + div_ext(&e1,&e2); + compact(&e1,&s1,sizeof(SINGLE)); +} diff --git a/lib/float/dvf8.c b/lib/float/dvf8.c new file mode 100755 index 000000000..fafe50f53 --- /dev/null +++ b/lib/float/dvf8.c @@ -0,0 +1,26 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + DIVIDE TWO FLOATS - DOUBLE Precision (DVF 8) +*/ + +#include "FP_types.h" + +void +dvf8(s2,s1) +DOUBLE s1,s2; +{ + EXTEND e1,e2; + + extend(&s1.d[0],&e1,sizeof(DOUBLE)); + extend(&s2.d[0],&e2,sizeof(DOUBLE)); + + /* do a divide */ + div_ext(&e1,&e2); + compact(&e1,&s1.d[0],sizeof(DOUBLE)); +} diff --git a/lib/float/extend.c b/lib/float/extend.c new file mode 100755 index 000000000..70febb1e3 --- /dev/null +++ b/lib/float/extend.c @@ -0,0 +1,111 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + CONVERTS FLOATING POINT TO EXTENDED FORMAT + + Two sizes of FLOATING Point are known: + SINGLE and DOUBLE +*/ +/********************************************************/ +/* + It is not required to normalize in extended + format, but it has been chosen to do so. + Extended Format is as follows (at exit): + +->sign S000 0000 | 0000 0000 <SIGN> +->exp 0EEE EEEE | EEEE EEEE <EXPONENT> +->m1 LFFF FFFF | FFFF FFFF <L.Fraction> + FFFF FFFF | FFFF FFFF <Fraction> +->m2 FFFF FFFF | FFFF FFFF <Fraction> + FFFF F000 | 0000 0000 <Fraction> +*/ +/********************************************************/ + +#include "FP_bias.h" +#include "FP_shift.h" +#include "FP_types.h" +#include "get_put.h" +/********************************************************/ + +void +extend(from,to,size) +unsigned long *from; +EXTEND *to; +int size; +{ + register char *cpt1; + unsigned long tmp; + int leadbit = 0; + + cpt1 = (char *) from; + +#if FL_MSL_AT_LOW_ADDRESS +#if FL_MSW_AT_LOW_ADDRESS + to->exp = uget2(cpt1); +#else + to->exp = uget2(cpt1+2); +#endif +#else +#if FL_MSW_AT_LOW_ADDRESS + to->exp = uget2(cpt1+(size == sizeof(DOUBLE) ? 4 : 0)); +#else + to->exp = uget2(cpt1+(size == sizeof(DOUBLE) ? 6 : 2)); +#endif +#endif + to->sign = (to->exp & 0x8000); /* set sign bit */ + to->exp ^= to->sign; + if (size == sizeof(DOUBLE)) + to->exp >>= DBL_EXPSHIFT; + else + to->exp >>= SGL_EXPSHIFT; + if (to->exp > 0) + leadbit++; /* will set Lead bit later */ + else to->exp++; + + if (size == sizeof(DOUBLE)) { +#if FL_MSL_AT_LOW_ADDRESS + to->m1 = get4(cpt1); + cpt1 += 4; + tmp = get4(cpt1); +#else + tmp = get4(cpt1); + cpt1 += 4; + to->m1 = get4(cpt1); +#endif + if (to->exp == 1 && to->m1 == 0 && tmp == 0) { + to->exp = 0; + to->sign = 0; + to->m1 = 0; + to->m2 = 0; + return; + } + to->m1 <<= DBL_M1LEFT; /* shift */ + to->exp -= DBL_BIAS; /* remove bias */ + to->m1 |= (tmp>>DBL_RPACK); /* plus 10 == 32 */ + to->m2 = (tmp<<DBL_LPACK); /* plus 22 == 32 */ + } + else { /* size == sizeof(SINGLE) */ + to->m1 = get4(cpt1); + to->m1 <<= SGL_M1LEFT; /* shift */ + if (to->exp == 1 && to->m1 == 0) { + to->exp = 0; + to->sign = 0; + to->m1 = 0; + to->m2 = 0; + return; + } + to->exp -= SGL_BIAS; /* remove bias */ + to->m2 = 0L; + } + + to->m1 |= NORMBIT; /* set bit L */ + if (leadbit == 0) { /* set or clear Leading Bit */ + to->m1 &= ~NORMBIT; /* clear bit L */ + nrm_ext(to); /* and normalize */ + } +} diff --git a/lib/float/fef4.c b/lib/float/fef4.c new file mode 100755 index 000000000..ff426f57b --- /dev/null +++ b/lib/float/fef4.c @@ -0,0 +1,33 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + SEPERATE INTO EXPONENT AND FRACTION (FEF 4) +*/ + +#include "FP_types.h" + +void +fef4(r,s1) +SINGLE s1; +struct fef4_returns *r; +{ + EXTEND buf; + register struct fef4_returns *p = r; /* make copy; r might refer + to itself (see table) + */ + + extend(&s1,&buf,sizeof(SINGLE)); + if (buf.exp == 0 && buf.m1 == 0 && buf.m2 == 0) { + p->e = 0; + } + else { + p->e = buf.exp+1; + buf.exp = -1; + } + compact(&buf,&p->f,sizeof(SINGLE)); +} diff --git a/lib/float/fef8.c b/lib/float/fef8.c new file mode 100755 index 000000000..1c3b3b0b5 --- /dev/null +++ b/lib/float/fef8.c @@ -0,0 +1,33 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + SEPERATE DOUBLE INTO EXPONENT AND FRACTION (FEF 8) +*/ + +#include "FP_types.h" + +void +fef8(r, s1) +DOUBLE s1; +struct fef8_returns *r; +{ + EXTEND buf; + register struct fef8_returns *p = r; /* make copy, r might refer + to itself (see table) + */ + + extend(&s1.d[0],&buf,sizeof(DOUBLE)); + if (buf.exp == 0 && buf.m1 == 0 && buf.m2 == 0) { + p->e = 0; + } + else { + p->e = buf.exp + 1; + buf.exp = -1; + } + compact(&buf,&p->f.d[0],sizeof(DOUBLE)); +} diff --git a/lib/float/fif4.c b/lib/float/fif4.c new file mode 100755 index 000000000..059372469 --- /dev/null +++ b/lib/float/fif4.c @@ -0,0 +1,46 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + MULTIPLY AND DISMEMBER PARTS (FIF 4) +*/ + +#include "FP_types.h" +#include "FP_shift.h" + +void +fif4(p,x,y) +SINGLE x,y; +struct fif4_returns *p; +{ + + EXTEND e1,e2; + + extend(&y,&e1,sizeof(SINGLE)); + extend(&x,&e2,sizeof(SINGLE)); + /* do a multiply */ + mul_ext(&e1,&e2); + e2 = e1; + compact(&e2,&y,sizeof(SINGLE)); + if (e1.exp < 0) { + p->ipart = 0; + p->fpart = y; + return; + } + if (e1.exp > 30 - SGL_M1LEFT) { + p->ipart = y; + p->fpart = 0; + return; + } + b64_sft(&e1.mantissa, 63 - e1.exp); + b64_sft(&e1.mantissa, e1.exp - 63); /* "loose" low order bits */ + compact(&e1,&(p->ipart),sizeof(SINGLE)); + extend(&(p->ipart), &e2, sizeof(SINGLE)); + extend(&y, &e1, sizeof(SINGLE)); + sub_ext(&e1, &e2); + compact(&e1, &(p->fpart), sizeof(SINGLE)); +} diff --git a/lib/float/fif8.c b/lib/float/fif8.c new file mode 100755 index 000000000..9f1b9b15e --- /dev/null +++ b/lib/float/fif8.c @@ -0,0 +1,48 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + MULTIPLY AND DISMEMBER PARTS (FIF 8) +*/ + +#include "FP_types.h" +#include "FP_shift.h" + +void +fif8(p,x,y) +DOUBLE x,y; +struct fif8_returns *p; +{ + + EXTEND e1,e2; + + extend(&y.d[0],&e1,sizeof(DOUBLE)); + extend(&x.d[0],&e2,sizeof(DOUBLE)); + /* do a multiply */ + mul_ext(&e1,&e2); + e2 = e1; + compact(&e2, &y.d[0], sizeof(DOUBLE)); + if (e1.exp < 0) { + p->ipart.d[0] = 0; + p->ipart.d[1] = 0; + p->fpart = y; + return; + } + if (e1.exp > 62 - DBL_M1LEFT) { + p->ipart = y; + p->fpart.d[0] = 0; + p->fpart.d[1] = 0; + return; + } + b64_sft(&e1.mantissa, 63 - e1.exp); + b64_sft(&e1.mantissa, e1.exp - 63); /* "loose" low order bits */ + compact(&e1, &(p->ipart.d[0]), sizeof(DOUBLE)); + extend(&(p->ipart.d[0]), &e2, sizeof(DOUBLE)); + extend(&y.d[0], &e1, sizeof(DOUBLE)); + sub_ext(&e1, &e2); + compact(&e1, &(p->fpart.d[0]), sizeof(DOUBLE)); +} diff --git a/lib/float/fptrp.s b/lib/float/fptrp.s new file mode 100755 index 000000000..d2823f4d7 --- /dev/null +++ b/lib/float/fptrp.s @@ -0,0 +1,19 @@ +# +.sect .text; .sect .rom; .sect .data; .sect .bss +.define __fptrp +.sect .text +__fptrp: +#if __i386 + push ebp + mov ebp, esp + mov eax, 8(bp) + call .Xtrp + leave + ret +#else /* i86 */ + push bp + mov bp, sp + mov ax, 4(bp) + call .Xtrp + jmp .cret +#endif diff --git a/lib/float/get_put.h b/lib/float/get_put.h new file mode 100755 index 000000000..9fd7f606f --- /dev/null +++ b/lib/float/get_put.h @@ -0,0 +1,41 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +#include <byte_order.h> + +#if CHAR_UNSIGNED +#define Xchar(ch) (ch) +#else +#define Xchar(ch) ((ch) & 0377) +#endif + +#define BYTES_REVERSED (MSB_AT_LOW_ADDRESS != FL_MSB_AT_LOW_ADDRESS) +#define WORDS_REVERSED (MSW_AT_LOW_ADDRESS != FL_MSW_AT_LOW_ADDRESS) +#define LONGS_REVERSED (FL_MSL_AT_LOW_ADDRESS) + +#if BYTES_REVERSED +#define uget2(c) (Xchar((c)[1]) | ((unsigned) Xchar((c)[0]) << 8)) +#define Xput2(i, c) (((c)[1] = (i)), ((c)[0] = (i) >> 8)) +#define put2(i, c) { register int j = (i); Xput2(j, c); } +#else +#define uget2(c) (* ((unsigned short *) (c))) +#define Xput2(i, c) (* ((short *) (c)) = (i)) +#define put2(i, c) Xput2(i, c) +#endif + +#define get2(c) ((short) uget2(c)) + +#if WORDS_REVERSED || BYTES_REVERSED +#define get4(c) (uget2((c)+2) | ((long) uget2(c) << 16)) +#define put4(l, c) { register long x=(l); \ + Xput2((int)x,(c)+2); \ + Xput2((int)(x>>16),(c)); \ + } +#else +#define get4(c) (* ((long *) (c))) +#define put4(l, c) (* ((long *) (c)) = (l)) +#endif diff --git a/lib/float/mlf4.c b/lib/float/mlf4.c new file mode 100755 index 000000000..d5f515d19 --- /dev/null +++ b/lib/float/mlf4.c @@ -0,0 +1,25 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + * Multiply Single Precesion Float (MLF 4) + */ + +#include "FP_types.h" + +void +mlf4(s2,s1) +SINGLE s1,s2; +{ + EXTEND e1,e2; + + extend(&s1,&e1,sizeof(SINGLE)); + extend(&s2,&e2,sizeof(SINGLE)); + /* do a multiply */ + mul_ext(&e1,&e2); + compact(&e1,&s1,sizeof(SINGLE)); +} diff --git a/lib/float/mlf8.c b/lib/float/mlf8.c new file mode 100755 index 000000000..b43cdf3c1 --- /dev/null +++ b/lib/float/mlf8.c @@ -0,0 +1,25 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + * Multiply Double Precision Float (MLF 8) + */ + +#include "FP_types.h" + +void +mlf8(s2,s1) +DOUBLE s1,s2; +{ + EXTEND e1,e2; + + extend(&s1.d[0],&e1,sizeof(DOUBLE)); + extend(&s2.d[0],&e2,sizeof(DOUBLE)); + /* do a multiply */ + mul_ext(&e1,&e2); + compact(&e1,&s1.d[0],sizeof(DOUBLE)); +} diff --git a/lib/float/mul_ext.c b/lib/float/mul_ext.c new file mode 100755 index 000000000..78a614014 --- /dev/null +++ b/lib/float/mul_ext.c @@ -0,0 +1,98 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + ROUTINE TO MULTIPLY TWO EXTENDED FORMAT NUMBERS +*/ + +# include "FP_bias.h" +# include "FP_trap.h" +# include "FP_types.h" +# include "FP_shift.h" + +void +mul_ext(e1,e2) +EXTEND *e1,*e2; +{ + register int i,j; /* loop control */ + unsigned short mp[4]; /* multiplier */ + unsigned short mc[4]; /* multipcand */ + unsigned short result[8]; /* result */ + register unsigned short *pres; + + /* first save the sign (XOR) */ + e1->sign ^= e2->sign; + + /* compute new exponent */ + e1->exp += e2->exp + 1; + /* 128 bit multiply of mantissas */ + + /* assign unknown long formats */ + /* to known unsigned word formats */ + mp[0] = e1->m1 >> 16; + mp[1] = (unsigned short) e1->m1; + mp[2] = e1->m2 >> 16; + mp[3] = (unsigned short) e1->m2; + mc[0] = e2->m1 >> 16; + mc[1] = (unsigned short) e2->m1; + mc[2] = e2->m2 >> 16; + mc[3] = (unsigned short) e2->m2; + for (i = 8; i--;) { + result[i] = 0; + } + /* + * fill registers with their components + */ + for(i=4, pres = &result[4];i--;pres--) if (mp[i]) { + unsigned short k = 0; + unsigned long mpi = mp[i]; + for(j=4;j--;) { + unsigned long tmp = (unsigned long)pres[j] + k; + if (mc[j]) tmp += mpi * mc[j]; + pres[j] = tmp; + k = tmp >> 16; + } + pres[-1] = k; + } + if (! (result[0] & 0x8000)) { + e1->exp--; + for (i = 0; i <= 3; i++) { + result[i] <<= 1; + if (result[i+1]&0x8000) result[i] |= 1; + } + result[4] <<= 1; + } + + /* + * combine the registers to a total + */ + e1->m1 = ((unsigned long)(result[0]) << 16) + result[1]; + e1->m2 = ((unsigned long)(result[2]) << 16) + result[3]; + if (result[4] & 0x8000) { + if (++e1->m2 == 0) + if (++e1->m1 == 0) { + e1->m1 = NORMBIT; + e1->exp++; + } + } + + /* check for overflow */ + if (e1->exp >= EXT_MAX) { + trap(EFOVFL); + /* if caught */ + /* return signed infinity */ + e1->exp = EXT_MAX; +infinity: e1->m1 = e1->m2 =0L; + return; + } + /* check for underflow */ + if (e1->exp < EXT_MIN) { + trap(EFUNFL); + e1->exp = EXT_MIN; + goto infinity; + } +} diff --git a/lib/float/ngf4.c b/lib/float/ngf4.c new file mode 100755 index 000000000..9f1f812ea --- /dev/null +++ b/lib/float/ngf4.c @@ -0,0 +1,27 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + NEGATE A FLOATING POINT (NGF 4) +*/ +/********************************************************/ + +#include "FP_types.h" +#include "get_put.h" + +#define OFF ((FL_MSW_AT_LOW_ADDRESS ? 0 : 2) + (FL_MSB_AT_LOW_ADDRESS ? 0 : 1)) +void +ngf4(f) +SINGLE f; +{ + unsigned char *p; + + if (f != (SINGLE) 0) { + p = (unsigned char *) &f + OFF; + *p ^= 0x80; + } +} diff --git a/lib/float/ngf8.c b/lib/float/ngf8.c new file mode 100755 index 000000000..473ffa510 --- /dev/null +++ b/lib/float/ngf8.c @@ -0,0 +1,28 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + NEGATE A FLOATING POINT (NGF 8) +*/ +/********************************************************/ + +#include "FP_types.h" +#include "get_put.h" + +#define OFF ((FL_MSL_AT_LOW_ADDRESS ? 0 : 4) + (FL_MSW_AT_LOW_ADDRESS ? 0 : 2) + (FL_MSB_AT_LOW_ADDRESS ? 0 : 1)) + +void +ngf8(f) +DOUBLE f; +{ + unsigned char *p; + + if (f.d[0] != 0 || f.d[1] != 0) { + p = (unsigned char *) &f + OFF; + *p ^= 0x80; + } +} diff --git a/lib/float/nrm_ext.c b/lib/float/nrm_ext.c new file mode 100755 index 000000000..dc83554b1 --- /dev/null +++ b/lib/float/nrm_ext.c @@ -0,0 +1,50 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/********************************************************/ +/* + NORMALIZE an EXTENDED FORMAT NUMBER +*/ +/********************************************************/ + +#include "FP_shift.h" +#include "FP_types.h" + +void +nrm_ext(e1) +EXTEND *e1; +{ + /* we assume that the mantissa != 0 */ + /* if it is then just return */ + /* to let it be a problem elsewhere */ + /* THAT IS, The exponent is not set to */ + /* zero. If we don't test here an */ + /* infinite loop is generated when */ + /* mantissa is zero */ + + if ((e1->m1 | e1->m2) == 0L) + return; + + /* if top word is zero mov low word */ + /* to top word, adjust exponent value */ + if (e1->m1 == 0L) { + e1->m1 = e1->m2; + e1->m2 = 0L; + e1->exp -= 32; + } + if ((e1->m1 & NORMBIT) == 0) { + unsigned long l = ((unsigned long)NORMBIT >> 1); + int cnt = -1; + + while (! (l & e1->m1)) { + l >>= 1; + cnt--; + } + e1->exp += cnt; + b64_sft(&(e1->mantissa), cnt); + } +} diff --git a/lib/float/sbf4.c b/lib/float/sbf4.c new file mode 100755 index 000000000..368c11102 --- /dev/null +++ b/lib/float/sbf4.c @@ -0,0 +1,27 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + SUBTRACT TWO FLOATS - SINGLE Precision (SBF 4) +*/ + +#include "FP_types.h" + +void +sbf4(s2,s1) +SINGLE s1,s2; +{ + EXTEND e1,e2; + + if (s2 == (SINGLE) 0) { + return; + } + extend(&s1,&e1,sizeof(SINGLE)); + extend(&s2,&e2,sizeof(SINGLE)); + sub_ext(&e1,&e2); + compact(&e1,&s1,sizeof(SINGLE)); +} diff --git a/lib/float/sbf8.c b/lib/float/sbf8.c new file mode 100755 index 000000000..9d4c1067c --- /dev/null +++ b/lib/float/sbf8.c @@ -0,0 +1,27 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + SUBTRACT TWO FLOATS - DOUBLE Precision (SBF 8) +*/ + +#include "FP_types.h" + +void +sbf8(s2,s1) +DOUBLE s1,s2; +{ + EXTEND e1, e2; + + if (s2.d[0] == 0 && s2.d[1] == 0) { + return; + } + extend(&s1.d[0],&e1,sizeof(DOUBLE)); + extend(&s2.d[0],&e2,sizeof(DOUBLE)); + sub_ext(&e1,&e2); + compact(&e1,&s1.d[0],sizeof(DOUBLE)); +} diff --git a/lib/float/sft_ext.c b/lib/float/sft_ext.c new file mode 100755 index 000000000..a933f9dc9 --- /dev/null +++ b/lib/float/sft_ext.c @@ -0,0 +1,39 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + SHIFT TWO EXTENDED NUMBERS INTO PROPER + ALIGNMENT FOR ADDITION (exponents are equal) + Numbers should not be zero on entry. +*/ + +#include "FP_types.h" + +void +sft_ext(e1,e2) +EXTEND *e1,*e2; +{ + register EXTEND *s; + register int diff; + + diff = e1->exp - e2->exp; + + if (!diff) + return; /* exponents are equal */ + + if (diff < 0) { /* e2 is larger */ + /* shift e1 */ + diff = -diff; + s = e1; + } + else /* e1 is larger */ + /* shift e2 */ + s = e2; + + s->exp += diff; + b64_sft(&(s->mantissa), diff); +} diff --git a/lib/float/shifter.c b/lib/float/shifter.c new file mode 100755 index 000000000..089da20c2 --- /dev/null +++ b/lib/float/shifter.c @@ -0,0 +1,75 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +# include "FP_types.h" + +void +b64_sft(e1,n) +B64 *e1; +int n; +{ + if (n > 0) { + if (n > 63) { + e1->l_32 = 0; + e1->h_32 = 0; + return; + } + if (n >= 32) { + e1->l_32 = e1->h_32; + e1->h_32 = 0; + n -= 32; + } + if (n > 0) { + e1->l_32 >>= n; + if (e1->h_32 != 0) { + e1->l_32 |= (e1->h_32 << (32 - n)); + e1->h_32 >>= n; + } + } + return; + } + n = -n; + if (n > 0) { + if (n > 63) { + e1->l_32 = 0; + e1->h_32 = 0; + return; + } + if (n >= 32) { + e1->h_32 = e1->l_32; + e1->l_32 = 0; + n -= 32; + } + if (n > 0) { + e1->h_32 <<= n; + if (e1->l_32 != 0) { + e1->h_32 |= (e1->l_32 >> (32 - n)); + e1->l_32 <<= n; + } + } + } +} + +void +b64_lsft(e1) +B64 *e1; +{ + /* shift left 1 bit */ + e1->h_32 <<= 1; + if (e1->l_32 & 0x80000000L) e1->h_32 |= 1; + e1->l_32 <<= 1; +} + +void +b64_rsft(e1) +B64 *e1; +{ + /* shift right 1 bit */ + e1->l_32 >>= 1; + if (e1->h_32 & 1) e1->l_32 |= 0x80000000L; + e1->h_32 >>= 1; +} diff --git a/lib/float/sub_ext.c b/lib/float/sub_ext.c new file mode 100755 index 000000000..64180aa6f --- /dev/null +++ b/lib/float/sub_ext.c @@ -0,0 +1,53 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + SUBTRACT 2 EXTENDED FORMAT NUMBERS +*/ + +#include "FP_types.h" + +void +sub_ext(e1,e2) +EXTEND *e1,*e2; +{ + if ((e2->m1 | e2->m2) == 0L) { + return; + } + if ((e1->m1 | e1->m2) == 0L) { + *e1 = *e2; + e1->sign = e2->sign ? 0 : 1; + return; + } + sft_ext(e1, e2); + if (e1->sign != e2->sign) { + /* e1 - e2 = e1 + (-e2) */ + if (b64_add(&e1->mantissa,&e2->mantissa)) { /* addition carry */ + b64_rsft(&e1->mantissa); /* shift mantissa one bit RIGHT */ + e1->m1 |= 0x80000000L; /* set max bit */ + e1->exp++; /* increase the exponent */ + } + } + else if (e2->m1 > e1->m1 || + (e2->m1 == e1->m1 && e2->m2 > e1->m2)) { + /* abs(e2) > abs(e1) */ + if (e1->m2 > e2->m2) { + e2->m1 -= 1; /* carry in */ + } + e2->m1 -= e1->m1; + e2->m2 -= e1->m2; + *e1 = *e2; + e1->sign = e2->sign ? 0 : 1; + } + else { + if (e2->m2 > e1->m2) + e1->m1 -= 1; /* carry in */ + e1->m1 -= e2->m1; + e1->m2 -= e2->m2; + } + nrm_ext(e1); +} diff --git a/lib/float/zrf4.c b/lib/float/zrf4.c new file mode 100755 index 000000000..a913e813d --- /dev/null +++ b/lib/float/zrf4.c @@ -0,0 +1,19 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + return a zero float (ZRF 4) +*/ + +#include "FP_types.h" + +void +zrf4(l) +SINGLE *l; +{ + *l = 0L; +} diff --git a/lib/float/zrf8.c b/lib/float/zrf8.c new file mode 100755 index 000000000..4fcdbb85b --- /dev/null +++ b/lib/float/zrf8.c @@ -0,0 +1,21 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + return a zero double (ZRF 8) +*/ + +#include "FP_types.h" + +void +zrf8(z) +DOUBLE *z; +{ + + z->d[0] = 0L; + z->d[1] = 0L; +} diff --git a/lib/float/zrf_ext.c b/lib/float/zrf_ext.c new file mode 100755 index 000000000..8f5878989 --- /dev/null +++ b/lib/float/zrf_ext.c @@ -0,0 +1,22 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* $Header$ */ + +/* + ZERO and return EXTEND FORMAT FLOAT +*/ + +#include "FP_types.h" + +void +zrf_ext(e) +EXTEND *e; +{ + e->m1 = 0; + e->m2 = 0; + e->exp = 0; + e->sign = 0; +} diff --git a/lib/fphook/Makefile b/lib/fphook/Makefile new file mode 100755 index 000000000..d3904734f --- /dev/null +++ b/lib/fphook/Makefile @@ -0,0 +1,32 @@ +# Makefile for lib/i86/fphook. + +# The ACK ANSI C compiler has an nice trick to reduce the size of programs +# that do not use floating point. If a program uses floating point then the +# compiler generates an external reference to the label '_fp_hook'. This makes +# the loader bring in the floating point printing and conversion routines +# '_f_print' and 'strtod' from the library libd.a. Otherwise two dummy +# routines are found in libc.a. (The printf and scanf need floating point +# for the %f formats, whether you use them or not.) + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE +CC1 = $(CC) $(CFLAGS) -c + +LIBD = ../libd.a +LIBC = ../libc.a + +all: $(LIBD) $(LIBC) + +$(LIBD): fphook.c + $(CC1) fphook.c + aal cr $@ fphook.o + rm fphook.o + +$(LIBC): $(LIBC)(fltpr.o) $(LIBC)(strtod.o) + aal cr $@ *.o + rm *.o + +$(LIBC)(fltpr.o): + $(CC1) fltpr.c + +$(LIBC)(strtod.o): + $(CC1) strtod.c diff --git a/lib/fphook/fltpr.c b/lib/fphook/fltpr.c new file mode 100755 index 000000000..7ba3fb018 --- /dev/null +++ b/lib/fphook/fltpr.c @@ -0,0 +1,12 @@ +#include <stdio.h> +#include <stdlib.h> +#include "../stdio/loc_incl.h" + +int _fp_hook = 1; + +char * +_f_print(va_list *ap, int flags, char *s, char c, int precision) +{ + fprintf(stderr,"cannot print floating point\n"); + exit(EXIT_FAILURE); +} diff --git a/lib/fphook/fphook.c b/lib/fphook/fphook.c new file mode 100755 index 000000000..61730d609 --- /dev/null +++ b/lib/fphook/fphook.c @@ -0,0 +1,195 @@ +/* + * fltpr.c - print floating point numbers + */ +/* $Header$ */ + +#ifndef NOFLOAT +#include <string.h> +#include <stdarg.h> +#include "../stdio/loc_incl.h" +int _fp_hook = 1; + +static char * +_pfloat(long double r, register char *s, int n, int flags) +{ + register char *s1; + int sign, dp; + register int i; + + s1 = _fcvt(r, n, &dp, &sign); + if (sign) + *s++ = '-'; + else if (flags & FL_SIGN) + *s++ = '+'; + else if (flags & FL_SPACE) + *s++ = ' '; + + if (dp<=0) + *s++ = '0'; + for (i=dp; i>0; i--) + if (*s1) *s++ = *s1++; + else *s++ = '0'; + if (((i=n) > 0) || (flags & FL_ALT)) + *s++ = '.'; + while (++dp <= 0) { + if (--i<0) + break; + *s++ = '0'; + } + while (--i >= 0) + if (*s1) *s++ = *s1++; + else *s++ = '0'; + return s; +} + +static char * +_pscien(long double r, register char *s, int n, int flags) +{ + int sign, dp; + register char *s1; + + s1 = _ecvt(r, n + 1, &dp, &sign); + if (sign) + *s++ = '-'; + else if (flags & FL_SIGN) + *s++ = '+'; + else if (flags & FL_SPACE) + *s++ = ' '; + + *s++ = *s1++; + if ((n > 0) || (flags & FL_ALT)) + *s++ = '.'; + while (--n >= 0) + if (*s1) *s++ = *s1++; + else *s++ = '0'; + *s++ = 'e'; + if ( r != 0 ) --dp ; + if ( dp<0 ) { + *s++ = '-' ; dp= -dp ; + } else { + *s++ = '+' ; + } + if (dp >= 100) { + *s++ = '0' + (dp / 100); + dp %= 100; + } + *s++ = '0' + (dp/10); + *s++ = '0' + (dp%10); + return s; +} + +#define NDIGINEXP(exp) (((exp) >= 100 || (exp) <= -100) ? 3 : 2) +#define LOW_EXP -4 +#define USE_EXP(exp, ndigits) (((exp) < LOW_EXP + 1) || (exp >= ndigits + 1)) + +static char * +_gcvt(long double value, int ndigit, char *s, int flags) +{ + int sign, dp; + register char *s1, *s2; + register int i; + register int nndigit = ndigit; + + s1 = _ecvt(value, ndigit, &dp, &sign); + s2 = s; + if (sign) *s2++ = '-'; + else if (flags & FL_SIGN) + *s2++ = '+'; + else if (flags & FL_SPACE) + *s2++ = ' '; + + if (!(flags & FL_ALT)) + for (i = nndigit - 1; i > 0 && s1[i] == '0'; i--) + nndigit--; + + if (USE_EXP(dp,ndigit)) { + /* Use E format */ + dp--; + *s2++ = *s1++; + if ((nndigit > 1) || (flags & FL_ALT)) *s2++ = '.'; + while (--nndigit > 0) *s2++ = *s1++; + *s2++ = 'e'; + if (dp < 0) { + *s2++ = '-'; + dp = -dp; + } + else *s2++ = '+'; + s2 += NDIGINEXP(dp); + *s2 = 0; + for (i = NDIGINEXP(dp); i > 0; i--) { + *--s2 = dp % 10 + '0'; + dp /= 10; + } + return s; + } + /* Use f format */ + if (dp <= 0) { + if (*s1 != '0') { + /* otherwise the whole number is 0 */ + *s2++ = '0'; + *s2++ = '.'; + } + while (dp < 0) { + dp++; + *s2++ = '0'; + } + } + for (i = 1; i <= nndigit; i++) { + *s2++ = *s1++; + if (i == dp) *s2++ = '.'; + } + if (i <= dp) { + while (i++ <= dp) *s2++ = '0'; + *s2++ = '.'; + } + if ((s2[-1]=='.') && !(flags & FL_ALT)) s2--; + *s2 = '\0'; + return s; +} + +char * +_f_print(va_list *ap, int flags, char *s, char c, int precision) +{ + register char *old_s = s; + long double ld_val; + + if (flags & FL_LONGDOUBLE) ld_val = va_arg(*ap, long double); + else ld_val = (long double) va_arg(*ap, double); + + switch(c) { + case 'f': + s = _pfloat(ld_val, s, precision, flags); + break; + case 'e': + case 'E': + s = _pscien(ld_val, s, precision , flags); + break; + case 'g': + case 'G': + s = _gcvt(ld_val, precision, s, flags); + s += strlen(s); + break; + } + if ( c == 'E' || c == 'G') { + while (*old_s && *old_s != 'e') old_s++; + if (*old_s == 'e') *old_s = 'E'; + } + return s; +} +#endif /* NOFLOAT */ +/* $Header$ */ + +#include <stdlib.h> +#include "../ansi/ext_fmt.h" + +void _str_ext_cvt(const char *s, char **ss, struct EXTEND *e); +double _ext_dbl_cvt(struct EXTEND *e); + +double +strtod(const char *p, char **pp) +{ + struct EXTEND e; + + _str_ext_cvt(p, pp, &e); + return _ext_dbl_cvt(&e); +} diff --git a/lib/fphook/strtod.c b/lib/fphook/strtod.c new file mode 100755 index 000000000..8f58baa08 --- /dev/null +++ b/lib/fphook/strtod.c @@ -0,0 +1,9 @@ +#include <stdio.h> +#include <stdlib.h> + +double +strtod(const char *p, char **pp) +{ + fprintf(stderr,"cannot print floating point\n"); + exit(EXIT_FAILURE); +} diff --git a/lib/h/em_abs.h b/lib/h/em_abs.h new file mode 100755 index 000000000..9855cff5a --- /dev/null +++ b/lib/h/em_abs.h @@ -0,0 +1,35 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#define LINO_AD 0 +#define FILN_AD 4 + +#define LINO (*(int *)(_hol0()+LINO_AD)) +#define FILN (*(char **)(_hol0()+FILN_AD)) + +#define EARRAY 0 +#define ERANGE 1 +#define ESET 2 +#define EIOVFL 3 +#define EFOVFL 4 +#define EFUNFL 5 +#define EIDIVZ 6 +#define EFDIVZ 7 +#define EIUND 8 +#define EFUND 9 +#define ECONV 10 + +#define ESTACK 16 +#define EHEAP 17 +#define EILLINS 18 +#define EODDZ 19 +#define ECASE 20 +#define EMEMFLT 21 +#define EBADPTR 22 +#define EBADPC 23 +#define EBADLAE 24 +#define EBADMON 25 +#define EBADLIN 26 +#define EBADGTO 27 diff --git a/lib/h/m2_traps.h b/lib/h/m2_traps.h new file mode 100755 index 000000000..9ea6330f0 --- /dev/null +++ b/lib/h/m2_traps.h @@ -0,0 +1,16 @@ +/* $Header$ */ +/* + * (c) copyright 1990 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ + +/* Modula-2 runtime errors */ + +#define M2_TOOLARGE 64 /* stack of process too large */ +#define M2_TOOMANY 65 /* too many nested traps & handlers */ +#define M2_NORESULT 66 /* no RETURN from procedure function */ +#define M2_UOVFL 67 /* cardinal overflow */ +#define M2_FORCH 68 /* FOR-loop control variable changed */ +#define M2_UUVFL 69 /* cardinal underflow */ +#define M2_INTERNAL 70 /* internal error, should not happen */ +#define M2_UNIXSIG 71 /* unix signal */ diff --git a/lib/h/pc_err.h b/lib/h/pc_err.h new file mode 100755 index 000000000..bf72386c7 --- /dev/null +++ b/lib/h/pc_err.h @@ -0,0 +1,29 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#define EARGC 64 +#define EEXP 65 +#define ELOG 66 +#define ESQT 67 +#define EASS 68 +#define EPACK 69 +#define EUNPACK 70 +#define EMOD 71 +#define EBADF 72 +#define EFREE 73 +#define EFUNASS 74 +#define EWIDTH 75 + +#define EWRITEF 96 +#define EREADF 97 +#define EEOF 98 +#define EFTRUNC 99 +#define ERESET 100 +#define EREWR 101 +#define ECLOSE 102 +#define EREAD 103 +#define EWRITE 104 +#define EDIGIT 105 +#define EASCII 106 diff --git a/lib/h/pc_file.h b/lib/h/pc_file.h new file mode 100755 index 000000000..3cd9f9dcd --- /dev/null +++ b/lib/h/pc_file.h @@ -0,0 +1,24 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#define WRBIT 0100000 +#define TXTBIT 040000 +#define EOFBIT 020000 +#define ELNBIT 010000 +#define WINDOW 04000 +#define MAGIC 0252 + +#define PC_BUFLEN 1024 + +struct file { + char *ptr; + unsigned flags; + char *fname; + int ufd; + int size; + int count; + int buflen; + char bufadr[PC_BUFLEN]; +}; diff --git a/lib/h/pc_math.h b/lib/h/pc_math.h new file mode 100755 index 000000000..0b2aaea83 --- /dev/null +++ b/lib/h/pc_math.h @@ -0,0 +1,42 @@ +/* + * localmath.h - This header is used by the mathematical library. + */ +/* $Header$ */ + +/* some constants (Hart & Cheney) */ +#define M_PI 3.14159265358979323846264338327950288 +#define M_2PI 6.28318530717958647692528676655900576 +#define M_3PI_4 2.35619449019234492884698253745962716 +#define M_PI_2 1.57079632679489661923132169163975144 +#define M_3PI_8 1.17809724509617246442349126872981358 +#define M_PI_4 0.78539816339744830961566084581987572 +#define M_PI_8 0.39269908169872415480783042290993786 +#define M_1_PI 0.31830988618379067153776752674502872 +#define M_2_PI 0.63661977236758134307553505349005744 +#define M_4_PI 1.27323954473516268615107010698011488 +#define M_E 2.71828182845904523536028747135266250 +#define M_LOG2E 1.44269504088896340735992468100189213 +#define M_LOG10E 0.43429448190325182765112891891660508 +#define M_LN2 0.69314718055994530941723212145817657 +#define M_LN10 2.30258509299404568401799145468436421 +#define M_SQRT2 1.41421356237309504880168872420969808 +#define M_1_SQRT2 0.70710678118654752440084436210484904 +#define M_EULER 0.57721566490153286060651209008240243 + +/* macros for constructing polynomials */ +#define POLYNOM1(x, a) ((a)[1]*(x)+(a)[0]) +#define POLYNOM2(x, a) (POLYNOM1((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM3(x, a) (POLYNOM2((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM4(x, a) (POLYNOM3((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM5(x, a) (POLYNOM4((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM6(x, a) (POLYNOM5((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM7(x, a) (POLYNOM6((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM8(x, a) (POLYNOM7((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM9(x, a) (POLYNOM8((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM10(x, a) (POLYNOM9((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM11(x, a) (POLYNOM10((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM12(x, a) (POLYNOM11((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM13(x, a) (POLYNOM12((x),(a)+1)*(x)+(a)[0]) + +#define M_LN_MAX_D (M_LN2 * DBL_MAX_EXP) +#define M_LN_MIN_D (M_LN2 * (DBL_MIN_EXP - 1)) diff --git a/lib/i386/Makefile b/lib/i386/Makefile new file mode 100755 index 000000000..678507247 --- /dev/null +++ b/lib/i386/Makefile @@ -0,0 +1,11 @@ +# Makefile for lib/i386. + +MAKE = exec make -$(MAKEFLAGS) + +install: + cd em && $(MAKE) + cd head && $(MAKE) + cd int64 && $(MAKE) + cd misc && $(MAKE) + cd rts && $(MAKE) + cd string && $(MAKE) diff --git a/lib/i386/em/Makefile b/lib/i386/em/Makefile new file mode 100755 index 000000000..bac9b638a --- /dev/null +++ b/lib/i386/em/Makefile @@ -0,0 +1,289 @@ +# Makefile for lib/i386/em. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -Was-ack +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../../libe.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(em_adf4.o) \ + $(LIBRARY)(em_adf8.o) \ + $(LIBRARY)(em_adi.o) \ + $(LIBRARY)(em_and.o) \ + $(LIBRARY)(em_blm.o) \ + $(LIBRARY)(em_cff4.o) \ + $(LIBRARY)(em_cff8.o) \ + $(LIBRARY)(em_cfi.o) \ + $(LIBRARY)(em_cfu.o) \ + $(LIBRARY)(em_cif4.o) \ + $(LIBRARY)(em_cif8.o) \ + $(LIBRARY)(em_cii.o) \ + $(LIBRARY)(em_cmf4.o) \ + $(LIBRARY)(em_cmf8.o) \ + $(LIBRARY)(em_cms.o) \ + $(LIBRARY)(em_com.o) \ + $(LIBRARY)(em_csa4.o) \ + $(LIBRARY)(em_csb4.o) \ + $(LIBRARY)(em_cuf4.o) \ + $(LIBRARY)(em_cuf8.o) \ + $(LIBRARY)(em_cuu.o) \ + $(LIBRARY)(em_dup.o) \ + $(LIBRARY)(em_dvf4.o) \ + $(LIBRARY)(em_dvf8.o) \ + $(LIBRARY)(em_dvi.o) \ + $(LIBRARY)(em_dvu.o) \ + $(LIBRARY)(em_error.o) \ + $(LIBRARY)(em_exg.o) \ + $(LIBRARY)(em_fat.o) \ + $(LIBRARY)(em_fef4.o) \ + $(LIBRARY)(em_fef8.o) \ + $(LIBRARY)(em_fif4.o) \ + $(LIBRARY)(em_fif8.o) \ + $(LIBRARY)(em_fp8087.o) \ + $(LIBRARY)(em_gto.o) \ + $(LIBRARY)(em_hol0.o) \ + $(LIBRARY)(em_iaar.o) \ + $(LIBRARY)(em_ilar.o) \ + $(LIBRARY)(em_inn.o) \ + $(LIBRARY)(em_ior.o) \ + $(LIBRARY)(em_isar.o) \ + $(LIBRARY)(em_lar4.o) \ + $(LIBRARY)(em_loi.o) \ + $(LIBRARY)(em_mlf4.o) \ + $(LIBRARY)(em_mlf8.o) \ + $(LIBRARY)(em_mli.o) \ + $(LIBRARY)(em_mon.o) \ + $(LIBRARY)(em_ngf4.o) \ + $(LIBRARY)(em_ngf8.o) \ + $(LIBRARY)(em_ngi.o) \ + $(LIBRARY)(em_nop.o) \ + $(LIBRARY)(em_print.o) \ + $(LIBRARY)(em_rck.o) \ + $(LIBRARY)(em_rmi.o) \ + $(LIBRARY)(em_rmu.o) \ + $(LIBRARY)(em_rol.o) \ + $(LIBRARY)(em_ror.o) \ + $(LIBRARY)(em_sar4.o) \ + $(LIBRARY)(em_sbf4.o) \ + $(LIBRARY)(em_sbf8.o) \ + $(LIBRARY)(em_sbi.o) \ + $(LIBRARY)(em_set.o) \ + $(LIBRARY)(em_sli.o) \ + $(LIBRARY)(em_sri.o) \ + $(LIBRARY)(em_sti.o) \ + $(LIBRARY)(em_stop.o) \ + $(LIBRARY)(em_trp.o) \ + $(LIBRARY)(em_unknown.o) \ + $(LIBRARY)(em_xor.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(em_adf4.o): em_adf4.s + $(CC1) em_adf4.s + +$(LIBRARY)(em_adf8.o): em_adf8.s + $(CC1) em_adf8.s + +$(LIBRARY)(em_adi.o): em_adi.s + $(CC1) em_adi.s + +$(LIBRARY)(em_and.o): em_and.s + $(CC1) em_and.s + +$(LIBRARY)(em_blm.o): em_blm.s + $(CC1) em_blm.s + +$(LIBRARY)(em_cff4.o): em_cff4.s + $(CC1) em_cff4.s + +$(LIBRARY)(em_cff8.o): em_cff8.s + $(CC1) em_cff8.s + +$(LIBRARY)(em_cfi.o): em_cfi.s + $(CC1) em_cfi.s + +$(LIBRARY)(em_cfu.o): em_cfu.s + $(CC1) em_cfu.s + +$(LIBRARY)(em_cif4.o): em_cif4.s + $(CC1) em_cif4.s + +$(LIBRARY)(em_cif8.o): em_cif8.s + $(CC1) em_cif8.s + +$(LIBRARY)(em_cii.o): em_cii.s + $(CC1) em_cii.s + +$(LIBRARY)(em_cmf4.o): em_cmf4.s + $(CC1) em_cmf4.s + +$(LIBRARY)(em_cmf8.o): em_cmf8.s + $(CC1) em_cmf8.s + +$(LIBRARY)(em_cms.o): em_cms.s + $(CC1) em_cms.s + +$(LIBRARY)(em_com.o): em_com.s + $(CC1) em_com.s + +$(LIBRARY)(em_csa4.o): em_csa4.s + $(CC1) em_csa4.s + +$(LIBRARY)(em_csb4.o): em_csb4.s + $(CC1) em_csb4.s + +$(LIBRARY)(em_cuf4.o): em_cuf4.s + $(CC1) em_cuf4.s + +$(LIBRARY)(em_cuf8.o): em_cuf8.s + $(CC1) em_cuf8.s + +$(LIBRARY)(em_cuu.o): em_cuu.s + $(CC1) em_cuu.s + +$(LIBRARY)(em_dup.o): em_dup.s + $(CC1) em_dup.s + +$(LIBRARY)(em_dvf4.o): em_dvf4.s + $(CC1) em_dvf4.s + +$(LIBRARY)(em_dvf8.o): em_dvf8.s + $(CC1) em_dvf8.s + +$(LIBRARY)(em_dvi.o): em_dvi.s + $(CC1) em_dvi.s + +$(LIBRARY)(em_dvu.o): em_dvu.s + $(CC1) em_dvu.s + +$(LIBRARY)(em_error.o): em_error.s + $(CC1) em_error.s + +$(LIBRARY)(em_exg.o): em_exg.s + $(CC1) em_exg.s + +$(LIBRARY)(em_fat.o): em_fat.s + $(CC1) em_fat.s + +$(LIBRARY)(em_fef4.o): em_fef4.s + $(CC1) em_fef4.s + +$(LIBRARY)(em_fef8.o): em_fef8.s + $(CC1) em_fef8.s + +$(LIBRARY)(em_fif4.o): em_fif4.s + $(CC1) em_fif4.s + +$(LIBRARY)(em_fif8.o): em_fif8.s + $(CC1) em_fif8.s + +$(LIBRARY)(em_fp8087.o): em_fp8087.s + $(CC1) em_fp8087.s + +$(LIBRARY)(em_gto.o): em_gto.s + $(CC1) em_gto.s + +$(LIBRARY)(em_hol0.o): em_hol0.s + $(CC1) em_hol0.s + +$(LIBRARY)(em_iaar.o): em_iaar.s + $(CC1) em_iaar.s + +$(LIBRARY)(em_ilar.o): em_ilar.s + $(CC1) em_ilar.s + +$(LIBRARY)(em_inn.o): em_inn.s + $(CC1) em_inn.s + +$(LIBRARY)(em_ior.o): em_ior.s + $(CC1) em_ior.s + +$(LIBRARY)(em_isar.o): em_isar.s + $(CC1) em_isar.s + +$(LIBRARY)(em_lar4.o): em_lar4.s + $(CC1) em_lar4.s + +$(LIBRARY)(em_loi.o): em_loi.s + $(CC1) em_loi.s + +$(LIBRARY)(em_mlf4.o): em_mlf4.s + $(CC1) em_mlf4.s + +$(LIBRARY)(em_mlf8.o): em_mlf8.s + $(CC1) em_mlf8.s + +$(LIBRARY)(em_mli.o): em_mli.s + $(CC1) em_mli.s + +$(LIBRARY)(em_mon.o): em_mon.s + $(CC1) em_mon.s + +$(LIBRARY)(em_ngf4.o): em_ngf4.s + $(CC1) em_ngf4.s + +$(LIBRARY)(em_ngf8.o): em_ngf8.s + $(CC1) em_ngf8.s + +$(LIBRARY)(em_ngi.o): em_ngi.s + $(CC1) em_ngi.s + +$(LIBRARY)(em_nop.o): em_nop.s + $(CC1) em_nop.s + +$(LIBRARY)(em_print.o): em_print.s + $(CC1) em_print.s + +$(LIBRARY)(em_rck.o): em_rck.s + $(CC1) em_rck.s + +$(LIBRARY)(em_rmi.o): em_rmi.s + $(CC1) em_rmi.s + +$(LIBRARY)(em_rmu.o): em_rmu.s + $(CC1) em_rmu.s + +$(LIBRARY)(em_rol.o): em_rol.s + $(CC1) em_rol.s + +$(LIBRARY)(em_ror.o): em_ror.s + $(CC1) em_ror.s + +$(LIBRARY)(em_sar4.o): em_sar4.s + $(CC1) em_sar4.s + +$(LIBRARY)(em_sbf4.o): em_sbf4.s + $(CC1) em_sbf4.s + +$(LIBRARY)(em_sbf8.o): em_sbf8.s + $(CC1) em_sbf8.s + +$(LIBRARY)(em_sbi.o): em_sbi.s + $(CC1) em_sbi.s + +$(LIBRARY)(em_set.o): em_set.s + $(CC1) em_set.s + +$(LIBRARY)(em_sli.o): em_sli.s + $(CC1) em_sli.s + +$(LIBRARY)(em_sri.o): em_sri.s + $(CC1) em_sri.s + +$(LIBRARY)(em_sti.o): em_sti.s + $(CC1) em_sti.s + +$(LIBRARY)(em_stop.o): em_stop.s + $(CC1) em_stop.s + +$(LIBRARY)(em_trp.o): em_trp.s + $(CC1) em_trp.s + +$(LIBRARY)(em_unknown.o): em_unknown.s + $(CC1) em_unknown.s + +$(LIBRARY)(em_xor.o): em_xor.s + $(CC1) em_xor.s diff --git a/lib/i386/em/byte_order.h b/lib/i386/em/byte_order.h new file mode 100755 index 000000000..d08b45a5d --- /dev/null +++ b/lib/i386/em/byte_order.h @@ -0,0 +1,6 @@ +#define CHAR_UNSIGNED 0 +#define MSB_AT_LOW_ADDRESS 0 +#define MSW_AT_LOW_ADDRESS 0 +#define FL_MSB_AT_LOW_ADDRESS 0 +#define FL_MSW_AT_LOW_ADDRESS 0 +#define FL_MSL_AT_LOW_ADDRESS 0 diff --git a/lib/i386/em/em_adf4.s b/lib/i386/em/em_adf4.s new file mode 100755 index 000000000..c36c7b4d3 --- /dev/null +++ b/lib/i386/em/em_adf4.s @@ -0,0 +1,11 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .adf4 + + .sect .text +.adf4: + mov bx,sp + flds 4(bx) + fadds 8(bx) + fstps 8(bx) + wait + ret diff --git a/lib/i386/em/em_adf8.s b/lib/i386/em/em_adf8.s new file mode 100755 index 000000000..253dda9e7 --- /dev/null +++ b/lib/i386/em/em_adf8.s @@ -0,0 +1,11 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .adf8 + + .sect .text +.adf8: + mov bx,sp + fldd 4(bx) + faddd 12(bx) + fstpd 12(bx) + wait + ret diff --git a/lib/i386/em/em_adi.s b/lib/i386/em/em_adi.s new file mode 100755 index 000000000..396c197fe --- /dev/null +++ b/lib/i386/em/em_adi.s @@ -0,0 +1,18 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .adi + + ! #bytes in ecx , top of stack in eax + .sect .text +.adi: + pop ebx ! return address + cmp ecx,4 + jne 9f + pop ecx + add eax,ecx + jmp ebx +9: +.extern EODDZ +.extern .trp + mov eax,EODDZ + push ebx + jmp .trp diff --git a/lib/i386/em/em_and.s b/lib/i386/em/em_and.s new file mode 100755 index 000000000..eddfa7976 --- /dev/null +++ b/lib/i386/em/em_and.s @@ -0,0 +1,20 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .and + + ! #bytes in ecx + ! save edi; it might be a register variable + + .sect .text +.and: + pop ebx ! return address + mov edx,edi + mov edi,esp + add edi,ecx + sar ecx,2 +1: + pop eax + and eax,(edi) + stos + loop 1b + mov edi,edx + jmp ebx diff --git a/lib/i386/em/em_blm.s b/lib/i386/em/em_blm.s new file mode 100755 index 000000000..a2a1c6814 --- /dev/null +++ b/lib/i386/em/em_blm.s @@ -0,0 +1,16 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .blm + + ! ecx: count in words +.blm: + mov ebx,esp + mov eax,esi + mov edx,edi + mov edi,4(ebx) + mov esi,8(ebx) + rep movs + mov esi,eax + mov edi,edx + ret 8 + diff --git a/lib/i386/em/em_cff4.s b/lib/i386/em/em_cff4.s new file mode 100755 index 000000000..42b946433 --- /dev/null +++ b/lib/i386/em/em_cff4.s @@ -0,0 +1,19 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .cff4 + + .sect .text +.cff4: + mov bx,sp + fldd 4(bx) + fstcw 4(bx) + wait + mov dx,4(bx) + and 4(bx),0xf3ff ! set to rounding mode + wait + fldcw 4(bx) + fstps 8(bx) + mov 4(bx),dx + wait + fldcw 4(bx) + wait + ret diff --git a/lib/i386/em/em_cff8.s b/lib/i386/em/em_cff8.s new file mode 100755 index 000000000..cc038a818 --- /dev/null +++ b/lib/i386/em/em_cff8.s @@ -0,0 +1,10 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .cff8 + + .sect .text +.cff8: + mov bx,sp + flds 4(bx) + fstpd 4(bx) + wait + ret diff --git a/lib/i386/em/em_cfi.s b/lib/i386/em/em_cfi.s new file mode 100755 index 000000000..900f4e5f3 --- /dev/null +++ b/lib/i386/em/em_cfi.s @@ -0,0 +1,27 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .cfi + + .sect .text +.cfi: + mov bx,sp + fstcw 4(bx) + wait + mov dx,4(bx) + or 4(bx),0xc00 ! truncating mode + wait + fldcw 4(bx) + cmp 8(bx),4 + jne 2f + ! loc 4 loc ? cfi + flds 12(bx) + fistpl 12(bx) +1: + mov 4(bx),dx + wait + fldcw 4(bx) + ret +2: + ! loc 8 loc ? cfi + fldd 12(bx) + fistpl 16(bx) + jmp 1b diff --git a/lib/i386/em/em_cfu.s b/lib/i386/em/em_cfu.s new file mode 100755 index 000000000..96c98bb95 --- /dev/null +++ b/lib/i386/em/em_cfu.s @@ -0,0 +1,38 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .cfu + + .sect .text +.cfu: + mov bx,sp + fstcw 4(bx) + wait + mov dx,4(bx) + or 4(bx),0xc00 ! truncating mode + wait + fldcw 4(bx) + cmp 8(bx),4 + jne 2f + ! loc 4 loc ? cfu + flds 12(bx) + fabs ! ??? + fiaddl (bigmin) + fistpl 12(bx) + wait + mov ax,12(bx) + sub ax,(bigmin) + mov 12(bx),ax +1: + mov 4(bx),dx + wait + fldcw 4(bx) + ret +2: + ! loc 8 loc ? cfu + fldd 12(bx) + fabs ! ??? + fiaddl (bigmin) + fistpl 16(bx) + mov ax,16(bx) + sub ax,(bigmin) + mov 16(bx),ax + jmp 1b diff --git a/lib/i386/em/em_cif4.s b/lib/i386/em/em_cif4.s new file mode 100755 index 000000000..0ccb187d5 --- /dev/null +++ b/lib/i386/em/em_cif4.s @@ -0,0 +1,10 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .cif4 + + .sect .text +.cif4: + mov bx,sp + fildl 8(bx) + fstps 8(bx) + wait + ret diff --git a/lib/i386/em/em_cif8.s b/lib/i386/em/em_cif8.s new file mode 100755 index 000000000..94be62af0 --- /dev/null +++ b/lib/i386/em/em_cif8.s @@ -0,0 +1,10 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .cif8 + + .sect .text +.cif8: + mov bx,sp + fildl 8(bx) + fstpd 4(bx) + wait + ret diff --git a/lib/i386/em/em_cii.s b/lib/i386/em/em_cii.s new file mode 100755 index 000000000..0ad7ce302 --- /dev/null +++ b/lib/i386/em/em_cii.s @@ -0,0 +1,31 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .cii + +.sect .text +.cii: + pop ebx ! return address + ! pop ecx, dest. size + ! pop edx, src. size + ! eax is source + cmp edx,1 + jne 2f + movsxb eax,al + mov edx,4 + jmp 1f +2: + cmp edx,2 + jne 1f + cwde ! convert from 2 to 4 bytes + mov edx,4 +1: + cmp edx,ecx + jne 9f + cmp edx,4 + jne 9f + jmp ebx +9: +.extern EILLINS +.extern .fat + mov eax,EILLINS + push eax + jmp .fat diff --git a/lib/i386/em/em_cmf4.s b/lib/i386/em/em_cmf4.s new file mode 100755 index 000000000..0e2f02361 --- /dev/null +++ b/lib/i386/em/em_cmf4.s @@ -0,0 +1,22 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .cmf4 + + .sect .text +.cmf4: + mov bx,sp + xor cx,cx + flds 8(bx) + flds 4(bx) + fcompp ! compare and pop operands + fstsw ax + wait + sahf + je 1f + jb 2f + dec cx + jmp 1f +2: + inc cx +1: + mov ax,cx + ret diff --git a/lib/i386/em/em_cmf8.s b/lib/i386/em/em_cmf8.s new file mode 100755 index 000000000..00a15db7d --- /dev/null +++ b/lib/i386/em/em_cmf8.s @@ -0,0 +1,22 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .cmf8 + + .sect .text +.cmf8: + mov bx,sp + xor cx,cx + fldd 12(bx) + fldd 4(bx) + fcompp ! compare and pop operands + fstsw ax + wait + sahf + je 1f + jb 2f + dec cx + jmp 1f +2: + inc cx +1: + mov ax,cx + ret diff --git a/lib/i386/em/em_cms.s b/lib/i386/em/em_cms.s new file mode 100755 index 000000000..d8d263918 --- /dev/null +++ b/lib/i386/em/em_cms.s @@ -0,0 +1,23 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .cms + + ! #bytes in ecx + .sect .text +.cms: + pop ebx ! return address + mov edx,esp + push esi + push edi + mov esi,edx + add edx,ecx + mov edi,edx + add edx,ecx + sar ecx,2 + repe cmps + je 1f + inc ecx +1: + pop edi + pop esi + mov esp,edx + jmp ebx diff --git a/lib/i386/em/em_com.s b/lib/i386/em/em_com.s new file mode 100755 index 000000000..dfc5f702b --- /dev/null +++ b/lib/i386/em/em_com.s @@ -0,0 +1,14 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .com + + ! #bytes in ecx + .sect .text +.com: + mov ebx,esp + add ebx,4 + sar ecx,2 +1: + not (ebx) + add ebx,4 + loop 1b + ret diff --git a/lib/i386/em/em_csa4.s b/lib/i386/em/em_csa4.s new file mode 100755 index 000000000..f90245062 --- /dev/null +++ b/lib/i386/em/em_csa4.s @@ -0,0 +1,27 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .csa4 + +.sect .text +.csa4: + ! ebx, descriptor address + ! eax, index + mov edx,(ebx) ! default + sub eax,4(ebx) + cmp eax,8(ebx) + ja 1f + sal eax,2 + add ebx,eax + mov ebx,12(ebx) + test ebx,ebx + jnz 2f +1: + mov ebx,edx + test ebx,ebx + jnz 2f +.extern ECASE +.extern .fat + mov eax,ECASE + push eax + jmp .fat +2: + jmp ebx diff --git a/lib/i386/em/em_csb4.s b/lib/i386/em/em_csb4.s new file mode 100755 index 000000000..4a8214183 --- /dev/null +++ b/lib/i386/em/em_csb4.s @@ -0,0 +1,29 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .csb4 + +.sect .text +.csb4: + !ebx, descriptor address + !eax, index + mov edx,(ebx) + mov ecx,4(ebx) +1: + add ebx,8 + dec ecx + jl 4f + cmp eax,(ebx) + jne 1b + mov ebx,4(ebx) +2: + test ebx,ebx + jnz 3f +.extern ECASE +.extern .fat + mov eax,ECASE + push eax + jmp .fat +3: + jmp ebx +4: + mov ebx,edx + jmp 2b diff --git a/lib/i386/em/em_cuf4.s b/lib/i386/em/em_cuf4.s new file mode 100755 index 000000000..a99961b7a --- /dev/null +++ b/lib/i386/em/em_cuf4.s @@ -0,0 +1,15 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .cuf4 + + .sect .text +.cuf4: + mov bx,sp + fildl 8(bx) + cmp 8(bx),0 + jge 1f + fisubl (bigmin) + fisubl (bigmin) +1: + fstps 8(bx) + wait + ret diff --git a/lib/i386/em/em_cuf8.s b/lib/i386/em/em_cuf8.s new file mode 100755 index 000000000..928cb9fa9 --- /dev/null +++ b/lib/i386/em/em_cuf8.s @@ -0,0 +1,15 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .cuf8 + + .sect .text +.cuf8: + mov bx,sp + fildl 8(bx) + cmp 8(bx),0 + jge 1f + fisubl (bigmin) + fisubl (bigmin) +1: + fstpd 4(bx) + wait + ret diff --git a/lib/i386/em/em_cuu.s b/lib/i386/em/em_cuu.s new file mode 100755 index 000000000..16177b6c3 --- /dev/null +++ b/lib/i386/em/em_cuu.s @@ -0,0 +1,22 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .ciu +.define .cui +.define .cuu + +.sect .text +.ciu: +.cui: +.cuu: + pop ebx ! return address + ! pop ecx, dest. size + ! pop edx, source size + ! eax is source + cmp edx,ecx + jne 8f + jmp ebx +8: +.extern EILLINS +.extern .fat + mov eax,EILLINS + push eax + jmp .fat diff --git a/lib/i386/em/em_dup.s b/lib/i386/em/em_dup.s new file mode 100755 index 000000000..071a46c32 --- /dev/null +++ b/lib/i386/em/em_dup.s @@ -0,0 +1,17 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .dup + + ! #bytes in ecx + .sect .text +.dup: + pop ebx ! return address + mov eax,esi + mov edx,edi + mov esi,esp + sub esp,ecx + mov edi,esp + sar ecx,2 + rep movs + mov esi,eax + mov edi,edx + jmp ebx diff --git a/lib/i386/em/em_dvf4.s b/lib/i386/em/em_dvf4.s new file mode 100755 index 000000000..8897227bf --- /dev/null +++ b/lib/i386/em/em_dvf4.s @@ -0,0 +1,11 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .dvf4 + + .sect .text +.dvf4: + mov bx,sp + flds 8(bx) + fdivs 4(bx) + fstps 8(bx) + wait + ret diff --git a/lib/i386/em/em_dvf8.s b/lib/i386/em/em_dvf8.s new file mode 100755 index 000000000..8fb6a47fe --- /dev/null +++ b/lib/i386/em/em_dvf8.s @@ -0,0 +1,11 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .dvf8 + + .sect .text +.dvf8: + mov bx,sp + fldd 12(bx) + fdivd 4(bx) + fstpd 12(bx) + wait + ret diff --git a/lib/i386/em/em_dvi.s b/lib/i386/em/em_dvi.s new file mode 100755 index 000000000..f7a87300e --- /dev/null +++ b/lib/i386/em/em_dvi.s @@ -0,0 +1,21 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .dvi + + ! #bytes in eax + .sect .text +.dvi: + pop ebx ! return address + cmp eax,4 + jne 1f + pop eax + cwd + pop ecx + idiv ecx + push eax + jmp ebx +1: +.extern EODDZ +.extern .trp + mov eax,EODDZ + push ebx + jmp .trp diff --git a/lib/i386/em/em_dvu.s b/lib/i386/em/em_dvu.s new file mode 100755 index 000000000..5f5a71784 --- /dev/null +++ b/lib/i386/em/em_dvu.s @@ -0,0 +1,21 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .dvu + + ! #bytes in eax + .sect .text +.dvu: + pop ebx ! return address + cmp eax,4 + jne 1f + pop eax + xor edx,edx + pop ecx + div ecx + push eax + jmp ebx +1: +.extern EODDZ +.extern .trp + mov eax,EODDZ + push ebx + jmp .trp diff --git a/lib/i386/em/em_error.s b/lib/i386/em/em_error.s new file mode 100755 index 000000000..f2501e152 --- /dev/null +++ b/lib/i386/em/em_error.s @@ -0,0 +1,32 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .error +.define .Xtrp + + ! eax is trap number + ! all registers must be saved + ! because return is possible + ! May only be called with error no's <16 +.sect .text +.error: + mov ecx,eax + mov ebx,1 + sal ebx,cl +.extern .ignmask +.extern .trp + test ebx,(.ignmask) + jne 2f + call .trp +2: + ret + +.Xtrp: + pusha + cmp eax,16 + jge 1f + call .error + popa + ret +1: + call .trp + popa + ret diff --git a/lib/i386/em/em_exg.s b/lib/i386/em/em_exg.s new file mode 100755 index 000000000..ad693dbaa --- /dev/null +++ b/lib/i386/em/em_exg.s @@ -0,0 +1,22 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .exg + + ! #bytes in ecx +.sect .text +.exg: + push edi + mov edi,esp + add edi,8 + mov ebx,edi + add ebx,ecx + sar ecx,2 +1: + mov eax,(ebx) + xchg eax,(edi) + mov (ebx),eax + add edi,4 + add ebx,4 + loop 1b +2: + pop edi + ret diff --git a/lib/i386/em/em_fat.s b/lib/i386/em/em_fat.s new file mode 100755 index 000000000..0302552dc --- /dev/null +++ b/lib/i386/em/em_fat.s @@ -0,0 +1,10 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .fat + +.fat: +.extern .trp +.extern .stop + call .trp + call .stop + ! no return diff --git a/lib/i386/em/em_fef4.s b/lib/i386/em/em_fef4.s new file mode 100755 index 000000000..adc69799e --- /dev/null +++ b/lib/i386/em/em_fef4.s @@ -0,0 +1,49 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .fef4 + + .sect .text +.fef4: + ! this could be simpler, if only the + ! fxtract instruction was emulated properly + mov bx,sp + mov ax,8(bx) + and ax,0x7f800000 + je 1f ! zero exponent + shr ax,23 + sub ax,126 + mov cx,ax ! exponent in cx + mov ax,8(bx) + and ax,0x807fffff + or ax,0x3f000000 ! load -1 exponent + mov bx,4(bx) + mov 4(bx),ax + mov (bx),cx + ret +1: ! we get here on zero exp + mov ax,8(bx) + and ax,0x007fffff + jne 1f ! zero result + mov bx,4(bx) + mov (bx),ax + mov 4(bx),ax + ret +1: ! otherwise unnormalized number + mov cx,8(bx) + and cx,0x807fffff + mov dx,cx + and cx,0x80000000 + mov ax,-125 +2: + test dx,0x800000 + jne 1f + dec ax + shl dx,1 + or dx,cx + jmp 2b +1: + mov bx,4(bx) + mov (bx),ax + and dx,0x807fffff + or dx,0x3f000000 ! load -1 exponent + mov 4(bx),dx + ret diff --git a/lib/i386/em/em_fef8.s b/lib/i386/em/em_fef8.s new file mode 100755 index 000000000..48234edb2 --- /dev/null +++ b/lib/i386/em/em_fef8.s @@ -0,0 +1,56 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .fef8 + + .sect .text +.fef8: + ! this could be simpler, if only the + ! fxtract instruction was emulated properly + mov bx,sp + mov ax,12(bx) + and ax,0x7ff00000 + je 1f ! zero exponent + shr ax,20 + sub ax,1022 + mov cx,ax ! exponent in cx + mov ax,12(bx) + and ax,0x800fffff + or ax,0x3fe00000 ! load -1 exponent + mov dx,8(bx) + mov bx,4(bx) + mov 4(bx),dx + mov 8(bx),ax + mov (bx),cx + ret +1: ! we get here on zero exp + mov ax,12(bx) + and ax,0xfffff + or ax,8(bx) + jne 1f ! zero result + mov bx,4(bx) + mov (bx),ax + mov 4(bx),ax + mov 8(bx),ax + ret +1: ! otherwise unnormalized number + mov cx,12(bx) + and cx,0x800fffff + mov dx,cx + and cx,0x80000000 + mov ax,-1021 +2: + test dx,0x100000 + jne 1f + dec ax + shl 8(bx),1 + rcl dx,1 + or dx,cx + jmp 2b +1: + and dx,0x800fffff + or dx,0x3fe00000 ! load -1 exponent + mov cx,8(bx) + mov bx,4(bx) + mov (bx),ax + mov 8(bx),dx + mov 4(bx),cx + ret diff --git a/lib/i386/em/em_fif4.s b/lib/i386/em/em_fif4.s new file mode 100755 index 000000000..04f702f0e --- /dev/null +++ b/lib/i386/em/em_fif4.s @@ -0,0 +1,37 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .fif4 + + .sect .text +.fif4: + mov bx,sp + flds 8(bx) + fmuls 12(bx) ! multiply + fld st ! copy result + ftst ! test sign; handle negative separately + fstsw ax + wait + sahf ! result of test in condition codes + jb 1f + frndint ! this one rounds (?) + fcom st(1) ! compare with original; if <=, then OK + fstsw ax + wait + sahf + jbe 2f + fisubs (one) ! else subtract 1 + jmp 2f +1: ! here, negative case + frndint ! this one rounds (?) + fcom st(1) ! compare with original; if >=, then OK + fstsw ax + wait + sahf + jae 2f + fiadds (one) ! else add 1 +2: + fsub st(1),st ! subtract integer part + mov bx,4(bx) + fstps (bx) + fstps 4(bx) + wait + ret diff --git a/lib/i386/em/em_fif8.s b/lib/i386/em/em_fif8.s new file mode 100755 index 000000000..2b8154d11 --- /dev/null +++ b/lib/i386/em/em_fif8.s @@ -0,0 +1,37 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .fif8 + + .sect .text +.fif8: + mov bx,sp + fldd 8(bx) + fmuld 16(bx) ! multiply + fld st ! and copy result + ftst ! test sign; handle negative separately + fstsw ax + wait + sahf ! result of test in condition codes + jb 1f + frndint ! this one rounds (?) + fcom st(1) ! compare with original; if <=, then OK + fstsw ax + wait + sahf + jbe 2f + fisubs (one) ! else subtract 1 + jmp 2f +1: ! here, negative case + frndint ! this one rounds (?) + fcom st(1) ! compare with original; if >=, then OK + fstsw ax + wait + sahf + jae 2f + fiadds (one) ! else add 1 +2: + fsub st(1),st ! subtract integer part + mov bx,4(bx) + fstpd (bx) + fstpd 8(bx) + wait + ret diff --git a/lib/i386/em/em_fp8087.s b/lib/i386/em/em_fp8087.s new file mode 100755 index 000000000..53f6b7f93 --- /dev/null +++ b/lib/i386/em/em_fp8087.s @@ -0,0 +1,10 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define one, bigmin + + .sect .rom +one: + .data2 1 +two: + .data2 2 +bigmin: + .data4 -2147483648 diff --git a/lib/i386/em/em_gto.s b/lib/i386/em/em_gto.s new file mode 100755 index 000000000..0149f27ca --- /dev/null +++ b/lib/i386/em/em_gto.s @@ -0,0 +1,8 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .gto + +.gto: + mov ebp,8(ebx) + mov esp,4(ebx) + jmp (ebx) diff --git a/lib/i386/em/em_hol0.s b/lib/i386/em/em_hol0.s new file mode 100755 index 000000000..8c919aeee --- /dev/null +++ b/lib/i386/em/em_hol0.s @@ -0,0 +1,7 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss + +.define hol0 +.sect .data +hol0: + .data4 0, 0 + .data4 0, 0 diff --git a/lib/i386/em/em_iaar.s b/lib/i386/em/em_iaar.s new file mode 100755 index 000000000..b74dc3fa4 --- /dev/null +++ b/lib/i386/em/em_iaar.s @@ -0,0 +1,18 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .iaar + +.iaar: + pop ecx + pop edx + cmp edx,4 +.extern .unknown + jne .unknown + pop ebx ! descriptor address + pop eax ! index + sub eax,(ebx) + mul 8(ebx) + pop ebx ! array base + add ebx,eax + push ecx + ret diff --git a/lib/i386/em/em_ilar.s b/lib/i386/em/em_ilar.s new file mode 100755 index 000000000..15514863e --- /dev/null +++ b/lib/i386/em/em_ilar.s @@ -0,0 +1,15 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .ilar + +.ilar: + pop ecx + pop edx +.extern .unknown + cmp edx,4 + jne .unknown + pop ebx ! descriptor address + pop eax ! index + push ecx +.extern .lar4 + jmp .lar4 diff --git a/lib/i386/em/em_inn.s b/lib/i386/em/em_inn.s new file mode 100755 index 000000000..95d8ac01b --- /dev/null +++ b/lib/i386/em/em_inn.s @@ -0,0 +1,32 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .inn + + ! #bytes in ecx + ! bit # in eax +.inn: + xor edx,edx + mov ebx,8 + div ebx + mov ebx,esp + add ebx,4 + add ebx,eax + cmp eax,ecx + jae 1f + movb al,(ebx) + mov ebx,edx + testb al,bits(ebx) + jz 1f + mov eax,1 + jmp 2f +1: + xor eax,eax +2: + pop ebx + add esp,ecx + ! eax is result + jmp ebx + + .sect .rom +bits: + .data1 1,2,4,8,16,32,64,128 diff --git a/lib/i386/em/em_ior.s b/lib/i386/em/em_ior.s new file mode 100755 index 000000000..3981ff5ae --- /dev/null +++ b/lib/i386/em/em_ior.s @@ -0,0 +1,18 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .ior + + ! #bytes in ecx +.ior: + pop ebx ! return address + mov edx,edi + mov edi,esp + add edi,ecx + sar ecx,2 +1: + pop eax + or eax,(edi) + stos + loop 1b + mov edi,edx + jmp ebx diff --git a/lib/i386/em/em_isar.s b/lib/i386/em/em_isar.s new file mode 100755 index 000000000..75b46b7b5 --- /dev/null +++ b/lib/i386/em/em_isar.s @@ -0,0 +1,15 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .isar + +.isar: + pop ecx + pop eax + cmp eax,4 +.extern .unknown + jne .unknown + pop ebx ! descriptor address + pop eax ! index + push ecx +.extern .sar4 + jmp .sar4 diff --git a/lib/i386/em/em_lar4.s b/lib/i386/em/em_lar4.s new file mode 100755 index 000000000..643ec961f --- /dev/null +++ b/lib/i386/em/em_lar4.s @@ -0,0 +1,37 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .lar4 + +.lar4: + ! ebx, descriptor address + ! eax, index + sub eax,(ebx) + mov ecx,8(ebx) + imul ecx + pop ebx + pop edx ! base address + add edx,eax + sar ecx,1 + jnb 1f + xor eax,eax + movb al,(edx) + push eax + jmp ebx +1: + sar ecx,1 + jnb 1f + xor eax,eax + o16 mov ax,(edx) + push eax + jmp ebx +1: + xchg edx,esi ! saved esi + mov eax,ecx + sal eax,2 + sub esp,eax + mov eax,edi ! save edi + mov edi,esp + rep movs + mov edi,eax + mov esi,edx + jmp ebx diff --git a/lib/i386/em/em_loi.s b/lib/i386/em/em_loi.s new file mode 100755 index 000000000..c0ed170d5 --- /dev/null +++ b/lib/i386/em/em_loi.s @@ -0,0 +1,44 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .loi +.define .los + + ! #bytes in ecx + ! address in ebx + ! save esi/edi. they might be register variables +.los: + pop edx + mov eax,ecx + sar ecx,1 + jnb 1f + movsxb eax,(ebx) + push eax + jmp edx +1: + sar ecx,1 + jnb 1f + movsx eax,(ebx) + push eax + jmp edx +1: + push edx + mov edx,esi + mov esi,ebx + pop ebx + sub esp,eax + jmp 1f + +.loi: + ! only called with size >= 4 + mov edx,esi + mov esi,ebx + pop ebx + sub esp,ecx + sar ecx,2 +1: + mov eax,edi + mov edi,esp + rep movs + mov esi,edx + mov edi,eax + jmp ebx diff --git a/lib/i386/em/em_mlf4.s b/lib/i386/em/em_mlf4.s new file mode 100755 index 000000000..e3068ac6c --- /dev/null +++ b/lib/i386/em/em_mlf4.s @@ -0,0 +1,11 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .mlf4 + + .sect .text +.mlf4: + mov bx,sp + flds 4(bx) + fmuls 8(bx) + fstps 8(bx) + wait + ret diff --git a/lib/i386/em/em_mlf8.s b/lib/i386/em/em_mlf8.s new file mode 100755 index 000000000..56ff130ef --- /dev/null +++ b/lib/i386/em/em_mlf8.s @@ -0,0 +1,11 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .mlf8 + + .sect .text +.mlf8: + mov bx,sp + fldd 4(bx) + fmuld 12(bx) + fstpd 12(bx) + wait + ret diff --git a/lib/i386/em/em_mli.s b/lib/i386/em/em_mli.s new file mode 100755 index 000000000..84185fb62 --- /dev/null +++ b/lib/i386/em/em_mli.s @@ -0,0 +1,20 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .mli + + ! #bytes in eax +.mli: + pop ebx ! return address + cmp eax,4 + jne 1f + pop eax + pop ecx + mul ecx + push eax + jmp ebx +1: +.extern EODDZ +.extern .trp + mov eax,EODDZ + push ebx + jmp .trp diff --git a/lib/i386/em/em_mon.s b/lib/i386/em/em_mon.s new file mode 100755 index 000000000..42cac7ca8 --- /dev/null +++ b/lib/i386/em/em_mon.s @@ -0,0 +1,7 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .mon + +.mon: +.extern .stop + call .stop diff --git a/lib/i386/em/em_ngf4.s b/lib/i386/em/em_ngf4.s new file mode 100755 index 000000000..e21fe2b0c --- /dev/null +++ b/lib/i386/em/em_ngf4.s @@ -0,0 +1,11 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .ngf4 + + .sect .text +.ngf4: + mov bx,sp + flds 4(bx) + fchs + fstps 4(bx) + wait + ret diff --git a/lib/i386/em/em_ngf8.s b/lib/i386/em/em_ngf8.s new file mode 100755 index 000000000..baec4bcf3 --- /dev/null +++ b/lib/i386/em/em_ngf8.s @@ -0,0 +1,11 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .ngf8 + + .sect .text +.ngf8: + mov bx,sp + fldd 4(bx) + fchs + fstpd 4(bx) + wait + ret diff --git a/lib/i386/em/em_ngi.s b/lib/i386/em/em_ngi.s new file mode 100755 index 000000000..b15a1fab8 --- /dev/null +++ b/lib/i386/em/em_ngi.s @@ -0,0 +1,19 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .ngi + + ! #bytes in eax +.ngi: + pop ebx ! return address + cmp eax,4 + jne 1f + pop ecx + neg ecx + push ecx + jmp ebx +1: +.extern EODDZ +.extern .trp + mov eax,EODDZ + push ebx + jmp .trp diff --git a/lib/i386/em/em_nop.s b/lib/i386/em/em_nop.s new file mode 100755 index 000000000..46fbbb7ff --- /dev/null +++ b/lib/i386/em/em_nop.s @@ -0,0 +1,10 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .nop +.extern printd, printc, hol0 + +.nop: + mov eax,(hol0) + call printd + movb al,'\n' + jmp printc diff --git a/lib/i386/em/em_print.s b/lib/i386/em/em_print.s new file mode 100755 index 000000000..8b09b5044 --- /dev/null +++ b/lib/i386/em/em_print.s @@ -0,0 +1,47 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define printc,printd,prints + + ! argument in eax + ! uses ebx +prints: + xchg eax,ebx +1: + movb al,(ebx) + inc ebx + testb al,al + jz 2f + call printc + jmp 1b +2: + ret + + ! argument in eax + ! uses ecx and edx +printd: + xor edx,edx + mov ecx,10 + div ecx + test eax,eax + jz 1f + push edx + call printd + pop edx +1: + xchg eax,edx + addb al,'0' + + ! argument in eax +printc: + push eax + mov ebx,esp + mov eax,1 + push eax + push ebx + push eax + call __write + pop ebx + pop ebx + pop ebx + pop ebx + ret diff --git a/lib/i386/em/em_rck.s b/lib/i386/em/em_rck.s new file mode 100755 index 000000000..e1a6669e4 --- /dev/null +++ b/lib/i386/em/em_rck.s @@ -0,0 +1,20 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .rck + + ! descriptor address in ebx + ! value in eax, must be left there +.rck: + cmp eax,(ebx) + jl 2f + cmp eax,4(ebx) + jg 2f + ret +2: + push eax +.extern ERANGE +.extern .error + mov eax,ERANGE + call .error + pop eax + ret diff --git a/lib/i386/em/em_rmi.s b/lib/i386/em/em_rmi.s new file mode 100755 index 000000000..775abbdd7 --- /dev/null +++ b/lib/i386/em/em_rmi.s @@ -0,0 +1,21 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .rmi + + ! #bytes in eax +.rmi: + pop ebx ! return address + cmp eax,4 + jne 1f + pop eax + cwd + pop ecx + idiv ecx + push edx + jmp ebx +1: +.extern EODDZ +.extern .trp + mov eax,EODDZ + push ebx + jmp .trp diff --git a/lib/i386/em/em_rmu.s b/lib/i386/em/em_rmu.s new file mode 100755 index 000000000..d51029d58 --- /dev/null +++ b/lib/i386/em/em_rmu.s @@ -0,0 +1,21 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .rmu + + ! #bytes in eax +.rmu: + pop ebx ! return address + cmp eax,4 + jne 1f + pop eax + xor edx,edx + pop ecx + idiv ecx + push edx + jmp ebx +1: +.extern EODDZ +.extern .trp + mov eax,EODDZ + push ebx + jmp .trp diff --git a/lib/i386/em/em_rol.s b/lib/i386/em/em_rol.s new file mode 100755 index 000000000..68365d7fc --- /dev/null +++ b/lib/i386/em/em_rol.s @@ -0,0 +1,20 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .rol + + ! #bytes in eax +.rol: + pop edx ! return address + cmp eax,4 + jne 1f + pop eax + pop ecx + rol eax,cl + push eax + jmp edx +1: +.extern EODDZ +.extern .trp + mov eax,EODDZ + push edx + jmp .trp diff --git a/lib/i386/em/em_ror.s b/lib/i386/em/em_ror.s new file mode 100755 index 000000000..ef34a9658 --- /dev/null +++ b/lib/i386/em/em_ror.s @@ -0,0 +1,20 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .ror + + ! #bytes in eax +.ror: + pop edx ! return address + cmp eax,4 + jne 1f + pop eax + pop ecx + ror eax,cl + push eax + jmp edx +1: +.extern EODDZ +.extern .trp + mov eax,EODDZ + push edx + jmp .trp diff --git a/lib/i386/em/em_sar4.s b/lib/i386/em/em_sar4.s new file mode 100755 index 000000000..52a1f6192 --- /dev/null +++ b/lib/i386/em/em_sar4.s @@ -0,0 +1,33 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .sar4 + +.sar4: + ! ebx, descriptor address + ! eax, index + sub eax,(ebx) + mov ecx,8(ebx) + imul ecx + pop ebx + pop edx ! base address + add edx,eax + sar ecx,1 + jnb 1f + pop eax + movb (edx),al + jmp ebx +1: + sar ecx,1 + jnb 1f + pop eax + o16 mov (edx),ax + jmp ebx +1: + xchg edi,edx ! edi = base address, edx is saved edi + mov eax,esi + mov esi,esp + rep movs + mov esp,esi + mov esi,eax + mov edi,edx + jmp ebx diff --git a/lib/i386/em/em_sbf4.s b/lib/i386/em/em_sbf4.s new file mode 100755 index 000000000..e76f9d1aa --- /dev/null +++ b/lib/i386/em/em_sbf4.s @@ -0,0 +1,11 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .sbf4 + + .sect .text +.sbf4: + mov bx,sp + flds 8(bx) + fsubs 4(bx) + fstps 8(bx) + wait + ret diff --git a/lib/i386/em/em_sbf8.s b/lib/i386/em/em_sbf8.s new file mode 100755 index 000000000..cab3d7830 --- /dev/null +++ b/lib/i386/em/em_sbf8.s @@ -0,0 +1,11 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .sbf8 + + .sect .text +.sbf8: + mov bx,sp + fldd 12(bx) + fsubd 4(bx) + fstpd 12(bx) + wait + ret diff --git a/lib/i386/em/em_sbi.s b/lib/i386/em/em_sbi.s new file mode 100755 index 000000000..2897d3181 --- /dev/null +++ b/lib/i386/em/em_sbi.s @@ -0,0 +1,19 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .sbi + + ! #bytes in ecx , top of stack in eax +.sbi: + pop ebx ! return subress + cmp ecx,4 + jne 1f + pop ecx + sub eax,ecx + neg eax + jmp ebx +1: +.extern EODDZ +.extern .trp + mov eax,EODDZ + push ebx + jmp .trp diff --git a/lib/i386/em/em_set.s b/lib/i386/em/em_set.s new file mode 100755 index 000000000..3493eac52 --- /dev/null +++ b/lib/i386/em/em_set.s @@ -0,0 +1,42 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .set + + ! #bytes in ecx + ! bit # in eax +.set: + pop ebx ! return address + xor edx,edx +!ifdef create set + sub esp,ecx + push ebx + push edi + mov ebx,esp + xor edi,edi + sar ecx,2 +1: + mov 8(ebx)(edi),edx + add edi,4 + loop 1b +!endif + mov ebx,8 + div ebx + cmp eax,edi + jae 2f + mov edi,edx + movb dl,bits(edi) + mov edi,esp + add edi,eax + orb 8(edi),dl + pop edi + ret +2: +.extern ESET +.extern .trp + pop edi + mov eax,ESET + jmp .trp + + .sect .rom +bits: + .data1 1,2,4,8,16,32,64,128 diff --git a/lib/i386/em/em_sli.s b/lib/i386/em/em_sli.s new file mode 100755 index 000000000..dd5b61667 --- /dev/null +++ b/lib/i386/em/em_sli.s @@ -0,0 +1,20 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .sli + + ! #bytes in eax +.sli: + pop edx ! return address + cmp eax,4 + jne 1f + pop eax + pop ecx + sal eax,cl + push eax + jmp edx +1: +.extern EODDZ +.extern .trp + mov eax,EODDZ + push edx + jmp .trp diff --git a/lib/i386/em/em_sri.s b/lib/i386/em/em_sri.s new file mode 100755 index 000000000..2fb78715f --- /dev/null +++ b/lib/i386/em/em_sri.s @@ -0,0 +1,20 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .sri + + ! #bytes in eax +.sri: + pop edx ! return address + cmp eax,4 + jne 1f + pop eax + pop ecx + sar eax,cl + push eax + jmp edx +1: +.extern EODDZ +.extern .trp + mov eax,EODDZ + push edx + jmp .trp diff --git a/lib/i386/em/em_sti.s b/lib/i386/em/em_sti.s new file mode 100755 index 000000000..12385eedd --- /dev/null +++ b/lib/i386/em/em_sti.s @@ -0,0 +1,41 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .sti +.define .sts + + ! #bytes in ecx + ! address in ebx + ! save edi/esi. they might be register variables +.sts: + pop edx + sar ecx,1 + jnb 1f + pop eax + movb (ebx),al + jmp edx +1: + sar ecx,1 + jnb 1f + pop eax + o16 mov (ebx),ax + jmp edx +1: + push edx + mov edx,edi + mov edi,ebx + pop ebx + jmp 1f +.sti: + ! only called with count >> 4 + mov edx,edi + mov edi,ebx + pop ebx + sar ecx,2 +1: + mov eax,esi + mov esi,esp + rep movs + mov esp,esi + mov edi,edx + mov esi,eax + jmp ebx diff --git a/lib/i386/em/em_stop.s b/lib/i386/em/em_stop.s new file mode 100755 index 000000000..476045f0a --- /dev/null +++ b/lib/i386/em/em_stop.s @@ -0,0 +1,5 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .stop +.stop: + jmp ___exit diff --git a/lib/i386/em/em_trp.s b/lib/i386/em/em_trp.s new file mode 100755 index 000000000..24af0e6f4 --- /dev/null +++ b/lib/i386/em/em_trp.s @@ -0,0 +1,18 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .trp +.extern .trppc, .stop + + ! eax is trap number +.trp: + xor ebx,ebx + xchg ebx,(.trppc) + test ebx,ebx + jz 2f + push eax + call ebx + pop eax + ret +2: + push eax + call .stop diff --git a/lib/i386/em/em_unknown.s b/lib/i386/em/em_unknown.s new file mode 100755 index 000000000..59425f25c --- /dev/null +++ b/lib/i386/em/em_unknown.s @@ -0,0 +1,9 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .unknown +.extern EILLINS, .fat + +.unknown: + mov eax,EILLINS + push eax + jmp .fat diff --git a/lib/i386/em/em_xor.s b/lib/i386/em/em_xor.s new file mode 100755 index 000000000..2dfb30d96 --- /dev/null +++ b/lib/i386/em/em_xor.s @@ -0,0 +1,18 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define .xor + + ! #bytes in ecx +.xor: + pop ebx ! return address + mov edx,edi + mov edi,esp + add edi,ecx + sar ecx,2 +1: + pop eax + xor eax,(edi) + stos + loop 1b + mov edi,edx + jmp ebx diff --git a/lib/i386/head/Makefile b/lib/i386/head/Makefile new file mode 100755 index 000000000..e18d65829 --- /dev/null +++ b/lib/i386/head/Makefile @@ -0,0 +1,13 @@ +# Makefile for lib/i386/head. + +CC1 = $(CC) -Was-ack -c + +LIBRARY = ../../libe.a +all: $(LIBRARY) + +$(LIBRARY): $(LIBRARY)(em_head.o) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(em_head.o): em_head.s + $(CC1) -I. em_head.s diff --git a/lib/i386/head/em_abs.h b/lib/i386/head/em_abs.h new file mode 100755 index 000000000..9855cff5a --- /dev/null +++ b/lib/i386/head/em_abs.h @@ -0,0 +1,35 @@ +/* $Header$ */ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +#define LINO_AD 0 +#define FILN_AD 4 + +#define LINO (*(int *)(_hol0()+LINO_AD)) +#define FILN (*(char **)(_hol0()+FILN_AD)) + +#define EARRAY 0 +#define ERANGE 1 +#define ESET 2 +#define EIOVFL 3 +#define EFOVFL 4 +#define EFUNFL 5 +#define EIDIVZ 6 +#define EFDIVZ 7 +#define EIUND 8 +#define EFUND 9 +#define ECONV 10 + +#define ESTACK 16 +#define EHEAP 17 +#define EILLINS 18 +#define EODDZ 19 +#define ECASE 20 +#define EMEMFLT 21 +#define EBADPTR 22 +#define EBADPC 23 +#define EBADLAE 24 +#define EBADMON 25 +#define EBADLIN 26 +#define EBADGTO 27 diff --git a/lib/i386/head/em_head.s b/lib/i386/head/em_head.s new file mode 100755 index 000000000..2164dbcc7 --- /dev/null +++ b/lib/i386/head/em_head.s @@ -0,0 +1,20 @@ +# +.sect .text; .sect .rom; .sect .data; .sect .bss +.define ERANGE,ESET,EHEAP,ECASE,EILLINS,EIDIVZ,EODDZ +.define .trppc, .ignmask + +ERANGE = 1 +ESET = 2 +EIDIVZ = 6 +EHEAP = 17 +EILLINS = 18 +EODDZ = 19 +ECASE = 20 + +#include <em_abs.h> + +.sect .data +.trppc: + .data4 0 +.ignmask: + .data4 EIOVFL | EIUND | ECONV | EFOVFL | EFUNFL diff --git a/lib/i386/int64/Makefile b/lib/i386/int64/Makefile new file mode 100755 index 000000000..5c302a0d0 --- /dev/null +++ b/lib/i386/int64/Makefile @@ -0,0 +1,61 @@ +# Makefile for lib/i386/int64. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -Was-ack +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../../libc.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(add64.o) \ + $(LIBRARY)(add64u.o) \ + $(LIBRARY)(cmp64.o) \ + $(LIBRARY)(cv64u.o) \ + $(LIBRARY)(cvu64.o) \ + $(LIBRARY)(diff64.o) \ + $(LIBRARY)(div64u.o) \ + $(LIBRARY)(ex64.o) \ + $(LIBRARY)(make64.o) \ + $(LIBRARY)(mul64u.o) \ + $(LIBRARY)(sub64.o) \ + $(LIBRARY)(sub64u.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(add64.o): add64.s + $(CC1) add64.s + +$(LIBRARY)(add64u.o): add64u.s + $(CC1) add64u.s + +$(LIBRARY)(cmp64.o): cmp64.s + $(CC1) cmp64.s + +$(LIBRARY)(cv64u.o): cv64u.s + $(CC1) cv64u.s + +$(LIBRARY)(cvu64.o): cvu64.s + $(CC1) cvu64.s + +$(LIBRARY)(diff64.o): diff64.s + $(CC1) diff64.s + +$(LIBRARY)(div64u.o): div64u.s + $(CC1) div64u.s + +$(LIBRARY)(ex64.o): ex64.s + $(CC1) ex64.s + +$(LIBRARY)(make64.o): make64.s + $(CC1) make64.s + +$(LIBRARY)(mul64u.o): mul64u.s + $(CC1) mul64u.s + +$(LIBRARY)(sub64.o): sub64.s + $(CC1) sub64.s + +$(LIBRARY)(sub64u.o): sub64u.s + $(CC1) sub64u.s diff --git a/lib/i386/int64/add64.s b/lib/i386/int64/add64.s new file mode 100755 index 000000000..3cc994f8b --- /dev/null +++ b/lib/i386/int64/add64.s @@ -0,0 +1,17 @@ +! add64() - 64 bit addition Author: Kees J. Bot +! 7 Dec 1995 +.sect .text +.define _add64 + +_add64: ! u64_t add64(u64_t i, u64_t j); + mov eax, 4(esp) + mov edx, 8(esp) + add edx, 16(esp) + mov (eax), edx + mov edx, 12(esp) + adc edx, 20(esp) + mov 4(eax), edx + ret + +! +! $PchId: add64.ack.s,v 1.2 1996/04/11 18:59:57 philip Exp $ diff --git a/lib/i386/int64/add64u.s b/lib/i386/int64/add64u.s new file mode 100755 index 000000000..58c71fd64 --- /dev/null +++ b/lib/i386/int64/add64u.s @@ -0,0 +1,18 @@ +! add64u() - unsigned to 64 bit addition Author: Kees J. Bot +! 7 Dec 1995 +.sect .text +.define _add64u, _add64ul + +_add64u: ! u64_t add64u(u64_t i, unsigned j); +_add64ul: ! u64_t add64ul(u64_t i, unsigned long j); + mov eax, 4(esp) + mov edx, 8(esp) + add edx, 16(esp) + mov (eax), edx + mov edx, 12(esp) + adc edx, 0 + mov 4(eax), edx + ret + +! +! $PchId: add64u.ack.s,v 1.2 1996/04/11 18:59:57 philip Exp $ diff --git a/lib/i386/int64/cmp64.s b/lib/i386/int64/cmp64.s new file mode 100755 index 000000000..d3ba6bfc8 --- /dev/null +++ b/lib/i386/int64/cmp64.s @@ -0,0 +1,31 @@ +! cmp64*() - 64 bit compare Author: Kees J. Bot +! 7 Dec 1995 +.sect .text +.define _cmp64, _cmp64u, _cmp64ul + +_cmp64: ! int cmp64(u64_t i, u64_t j); + mov ecx, esp +cmp64: xor eax, eax + mov edx, 4(ecx) + sub edx, 12(ecx) + mov edx, 8(ecx) + sbb edx, 16(ecx) + sbb eax, eax ! eax = - (i < j) + mov edx, 12(ecx) + sub edx, 4(ecx) + mov edx, 16(ecx) + sbb edx, 8(ecx) + adc eax, 0 ! eax = (i > j) - (i < j) + ret + +_cmp64u: ! int cmp64u(u64_t i, unsigned j); +_cmp64ul: ! int cmp64ul(u64_t i, unsigned long j); + mov ecx, esp + push 16(ecx) + mov 16(ecx), 0 + call cmp64 + pop 16(ecx) + ret + +! +! $PchId: cmp64.ack.s,v 1.2 1996/04/11 18:59:57 philip Exp $ diff --git a/lib/i386/int64/cv64u.s b/lib/i386/int64/cv64u.s new file mode 100755 index 000000000..a4b41f349 --- /dev/null +++ b/lib/i386/int64/cv64u.s @@ -0,0 +1,15 @@ +! cv64u() - 64 bit converted to unsigned Author: Kees J. Bot +! 7 Dec 1995 +.sect .text +.define _cv64u, _cv64ul + +_cv64u: ! unsigned cv64u(u64_t i); +_cv64ul: ! unsigned long cv64ul(u64_t i); + mov eax, 4(esp) + cmp 8(esp), 0 ! return ULONG_MAX if really big + jz 0f + mov eax, -1 +0: ret + +! +! $PchId: cv64u.ack.s,v 1.2 1996/04/11 18:59:57 philip Exp $ diff --git a/lib/i386/int64/cvu64.s b/lib/i386/int64/cvu64.s new file mode 100755 index 000000000..a35441e5a --- /dev/null +++ b/lib/i386/int64/cvu64.s @@ -0,0 +1,15 @@ +! cvu64() - unsigned converted to 64 bit Author: Kees J. Bot +! 7 Dec 1995 +.sect .text +.define _cvu64, _cvul64 + +_cvu64: ! u64_t cvu64(unsigned i); +_cvul64: ! u64_t cvul64(unsigned long i); + mov eax, 4(esp) + mov edx, 8(esp) + mov (eax), edx + mov 4(eax), 0 + ret + +! +! $PchId: cvu64.ack.s,v 1.2 1996/04/11 18:59:57 philip Exp $ diff --git a/lib/i386/int64/diff64.s b/lib/i386/int64/diff64.s new file mode 100755 index 000000000..e895a75c8 --- /dev/null +++ b/lib/i386/int64/diff64.s @@ -0,0 +1,12 @@ +! diff64() - 64 bit subtraction giving unsigned Author: Kees J. Bot +! 7 Dec 1995 +.sect .text +.define _diff64 + +_diff64: ! unsigned diff64(u64_t i, u64_t j); + mov eax, 4(esp) + sub eax, 12(esp) + ret + +! +! $PchId: diff64.ack.s,v 1.2 1996/04/11 18:59:57 philip Exp $ diff --git a/lib/i386/int64/div64u.s b/lib/i386/int64/div64u.s new file mode 100755 index 000000000..5d3629dc8 --- /dev/null +++ b/lib/i386/int64/div64u.s @@ -0,0 +1,22 @@ +! div64u() - 64 bit divided by unsigned giving unsigned long +! Author: Kees J. Bot +! 7 Dec 1995 +.sect .text +.define _div64u, _rem64u + +_div64u: ! unsigned long div64u(u64_t i, unsigned j); + xor edx, edx + mov eax, 8(esp) ! i = (ih<<32) + il + div 12(esp) ! ih = q * j + r + mov eax, 4(esp) + div 12(esp) ! i / j = (q<<32) + ((r<<32) + il) / j + ret + +_rem64u: ! unsigned rem64u(u64_t i, unsigned j); + pop ecx + call _div64u + mov eax, edx + jmp ecx + +! +! $PchId: div64u.ack.s,v 1.2 1996/04/11 18:59:57 philip Exp $ diff --git a/lib/i386/int64/ex64.s b/lib/i386/int64/ex64.s new file mode 100755 index 000000000..1bfc90d38 --- /dev/null +++ b/lib/i386/int64/ex64.s @@ -0,0 +1,16 @@ +! ex64*() - extract low or high 32 bits of a 64 bit number +! Author: Kees J. Bot +! 7 Dec 1995 +.sect .text +.define _ex64lo, _ex64hi + +_ex64lo: ! unsigned long ex64lo(u64_t i); + mov eax, 4(esp) + ret + +_ex64hi: ! unsigned long ex64hi(u64_t i); + mov eax, 8(esp) + ret + +! +! $PchId: ex64.ack.s,v 1.2 1996/04/11 18:59:57 philip Exp $ diff --git a/lib/i386/int64/make64.s b/lib/i386/int64/make64.s new file mode 100755 index 000000000..7e57727c2 --- /dev/null +++ b/lib/i386/int64/make64.s @@ -0,0 +1,16 @@ +! make64() - make a 64 bit number from two 32 bit halves +! Author: Kees J. Bot +! 7 Dec 1995 +.sect .text +.define _make64 + +_make64: ! u64_t make64(unsigned long lo, unsigned long hi); + mov eax, 4(esp) + mov edx, 8(esp) + mov (eax), edx + mov edx, 12(esp) + mov 4(eax), edx + ret + +! +! $PchId: make64.ack.s,v 1.2 1996/04/11 18:59:57 philip Exp $ diff --git a/lib/i386/int64/mul64u.s b/lib/i386/int64/mul64u.s new file mode 100755 index 000000000..f28ecf03d --- /dev/null +++ b/lib/i386/int64/mul64u.s @@ -0,0 +1,17 @@ +! mul64u() - unsigned long by unsigned multiply giving 64 bit result +! Author: Kees J. Bot +! 7 Dec 1995 +.sect .text +.define _mul64u + +_mul64u: ! u64_t mul64u(unsigned long i, unsigned j); + mov ecx, 4(esp) + mov eax, 8(esp) + mul 12(esp) + mov (ecx), eax + mov 4(ecx), edx + mov eax, ecx + ret + +! +! $PchId: mul64u.ack.s,v 1.2 1996/04/11 18:59:57 philip Exp $ diff --git a/lib/i386/int64/sub64.s b/lib/i386/int64/sub64.s new file mode 100755 index 000000000..15c7c12c5 --- /dev/null +++ b/lib/i386/int64/sub64.s @@ -0,0 +1,17 @@ +! sub64() - 64 bit subtraction Author: Kees J. Bot +! 7 Dec 1995 +.sect .text +.define _sub64 + +_sub64: ! u64_t sub64(u64_t i, u64_t j); + mov eax, 4(esp) + mov edx, 8(esp) + sub edx, 16(esp) + mov (eax), edx + mov edx, 12(esp) + sbb edx, 20(esp) + mov 4(eax), edx + ret + +! +! $PchId: sub64.ack.s,v 1.2 1996/04/11 18:59:57 philip Exp $ diff --git a/lib/i386/int64/sub64u.s b/lib/i386/int64/sub64u.s new file mode 100755 index 000000000..52e209296 --- /dev/null +++ b/lib/i386/int64/sub64u.s @@ -0,0 +1,18 @@ +! sub64() - unsigned from 64 bit subtraction Author: Kees J. Bot +! 7 Dec 1995 +.sect .text +.define _sub64u, _sub64ul + +_sub64u: ! u64_t sub64u(u64_t i, unsigned j); +_sub64ul: ! u64_t sub64ul(u64_t i, unsigned long j); + mov eax, 4(esp) + mov edx, 8(esp) + sub edx, 16(esp) + mov (eax), edx + mov edx, 12(esp) + sbb edx, 0 + mov 4(eax), edx + ret + +! +! $PchId: sub64u.ack.s,v 1.2 1996/04/11 18:59:57 philip Exp $ diff --git a/lib/i386/misc/Makefile b/lib/i386/misc/Makefile new file mode 100755 index 000000000..bbf3b7b8b --- /dev/null +++ b/lib/i386/misc/Makefile @@ -0,0 +1,81 @@ +# Makefile for lib/i386/misc. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -Was-ack +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../../libc.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(alloca.o) \ + $(LIBRARY)(get_bp.o) \ + $(LIBRARY)(getprocessor.o) \ + $(LIBRARY)(io_inb.o) \ + $(LIBRARY)(io_inl.o) \ + $(LIBRARY)(io_insb.o) \ + $(LIBRARY)(io_insl.o) \ + $(LIBRARY)(io_insw.o) \ + $(LIBRARY)(io_intr.o) \ + $(LIBRARY)(io_inw.o) \ + $(LIBRARY)(io_outb.o) \ + $(LIBRARY)(io_outl.o) \ + $(LIBRARY)(io_outsb.o) \ + $(LIBRARY)(io_outsl.o) \ + $(LIBRARY)(io_outsw.o) \ + $(LIBRARY)(io_outw.o) \ + $(LIBRARY)(oneC_sum.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(alloca.o): alloca.s + $(CC1) alloca.s + +$(LIBRARY)(get_bp.o): get_bp.s + $(CC1) get_bp.s + +$(LIBRARY)(getprocessor.o): getprocessor.s + $(CC1) getprocessor.s + +$(LIBRARY)(io_inb.o): io_inb.s + $(CC1) io_inb.s + +$(LIBRARY)(io_inl.o): io_inl.s + $(CC1) io_inl.s + +$(LIBRARY)(io_insb.o): io_insb.s + $(CC1) io_insb.s + +$(LIBRARY)(io_insl.o): io_insl.s + $(CC1) io_insl.s + +$(LIBRARY)(io_insw.o): io_insw.s + $(CC1) io_insw.s + +$(LIBRARY)(io_intr.o): io_intr.s + $(CC1) io_intr.s + +$(LIBRARY)(io_inw.o): io_inw.s + $(CC1) io_inw.s + +$(LIBRARY)(io_outb.o): io_outb.s + $(CC1) io_outb.s + +$(LIBRARY)(io_outl.o): io_outl.s + $(CC1) io_outl.s + +$(LIBRARY)(io_outsb.o): io_outsb.s + $(CC1) io_outsb.s + +$(LIBRARY)(io_outsl.o): io_outsl.s + $(CC1) io_outsl.s + +$(LIBRARY)(io_outsw.o): io_outsw.s + $(CC1) io_outsw.s + +$(LIBRARY)(io_outw.o): io_outw.s + $(CC1) io_outw.s + +$(LIBRARY)(oneC_sum.o): oneC_sum.s + $(CC1) oneC_sum.s diff --git a/lib/i386/misc/alloca.s b/lib/i386/misc/alloca.s new file mode 100755 index 000000000..8147ac6e9 --- /dev/null +++ b/lib/i386/misc/alloca.s @@ -0,0 +1,33 @@ +# +! alloca() - allocate space on the stack Author: Kees J. Bot +! 2 Dec 1993 +.sect .text; .sect .rom; .sect .data; .sect .bss + +.sect .text + .align 16 +.define _alloca +_alloca: +#if __ACK__ + pop ecx ! Return address + pop eax ! Bytes to allocate + add eax, 2*4+3 ! Add space for two saved register variables + andb al, 0xFC ! Align + mov ebx, esp ! Keep current esp + sub esp, eax ! Lower stack + mov eax, esp ! Return value + push 4(ebx) ! Push what is probably the saved esi + push (ebx) ! Saved edi + ! Now ACK can still do: + ! pop edi; pop esi; leave; ret + push eax ! Dummy argument + jmp ecx +#else + pop ecx ! Return address + pop eax ! Bytes to allocate + add eax, 3 + andb al, 0xFC ! Align + sub esp, eax ! Lower stack + mov eax, esp ! Return value + push eax ! Dummy argument + jmp ecx +#endif diff --git a/lib/i386/misc/get_bp.s b/lib/i386/misc/get_bp.s new file mode 100755 index 000000000..1ba81dcb4 --- /dev/null +++ b/lib/i386/misc/get_bp.s @@ -0,0 +1,16 @@ +! get_bp.s +! +! return EBP in EAX +! +! Created: Sep 7, 1992 by Philip Homburg + +.sect .text; .sect .rom; .sect .data; .sect .bss + +.sect .text +.define _get_bp +_get_bp: + mov eax, ebp + ret + +! $PchId: get_bp.ack.s,v 1.3 1996/02/23 08:30:52 philip Exp $ + diff --git a/lib/i386/misc/getprocessor.s b/lib/i386/misc/getprocessor.s new file mode 100755 index 000000000..3f6fe174c --- /dev/null +++ b/lib/i386/misc/getprocessor.s @@ -0,0 +1,52 @@ +! getprocessor() - determine processor type Author: Kees J. Bot +! 26 Jan 1994 + +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text + +! int getprocessor(void); +! Return 386, 486, 586, ... + +.define _getprocessor + +_getprocessor: + push ebp + mov ebp, esp + and esp, 0xFFFFFFFC ! Align stack to avoid AC fault + mov ecx, 0x00040000 ! Try to flip the AC bit introduced on the 486 + call flip + mov eax, 386 ! 386 if it didn't react to "flipping" + jz gotprocessor + mov ecx, 0x00200000 ! Try to flip the ID bit introduced on the 586 + call flip + mov eax, 486 ! 486 if it didn't react + jz gotprocessor + pushf + pusha ! Save the world + mov eax, 1 + .data1 0x0F, 0xA2 ! CPUID instruction tells the processor type + andb ah, 0x0F ! Extract the family (5, 6, ...) + movzxb eax, ah + imul eax, 100 ! 500, 600, ... + add eax, 86 ! 586, 686, ... + mov 7*4(esp), eax ! Pass eax through + popa + popf +gotprocessor: + leave + ret + +flip: + pushf ! Push eflags + pop eax ! eax = eflags + mov edx, eax ! Save original eflags + xor eax, ecx ! Flip the bit to test + push eax ! Push modified eflags value + popf ! Load modified eflags register + pushf + pop eax ! Get it again + push edx + popf ! Restore original eflags register + xor eax, edx ! See if the bit changed + test eax, ecx + ret diff --git a/lib/i386/misc/io_inb.s b/lib/i386/misc/io_inb.s new file mode 100755 index 000000000..ab59d7310 --- /dev/null +++ b/lib/i386/misc/io_inb.s @@ -0,0 +1,14 @@ +! inb() - Input one byte Author: Kees J. Bot +! 18 Mar 1996 +! unsigned inb(U16_t port); + +.sect .text +.define _inb +_inb: + push ebp + mov ebp, esp + mov edx, 8(ebp) ! port + xor eax, eax + inb dx ! read 1 byte + pop ebp + ret diff --git a/lib/i386/misc/io_inl.s b/lib/i386/misc/io_inl.s new file mode 100755 index 000000000..f09cf5cb1 --- /dev/null +++ b/lib/i386/misc/io_inl.s @@ -0,0 +1,13 @@ +! inl() - Input one dword Author: Kees J. Bot +! 18 Mar 1996 +! unsigned inl(U16_t port); + +.sect .text +.define _inl +_inl: + push ebp + mov ebp, esp + mov edx, 8(ebp) ! port + in dx ! read 1 dword + pop ebp + ret diff --git a/lib/i386/misc/io_insb.s b/lib/i386/misc/io_insb.s new file mode 100755 index 000000000..91b20dc31 --- /dev/null +++ b/lib/i386/misc/io_insb.s @@ -0,0 +1,18 @@ +! insb() - Input a byte array Author: Kees J. Bot +! 18 Mar 1996 +! void insb(U16_t port, void *buf, size_t count); + +.sect .text +.define _insb +_insb: + push ebp + mov ebp, esp + cld + push edi + mov edx, 8(ebp) ! port + mov edi, 12(ebp) ! buf + mov ecx, 16(ebp) ! byte count + rep insb ! input many bytes + pop edi + pop ebp + ret diff --git a/lib/i386/misc/io_insl.s b/lib/i386/misc/io_insl.s new file mode 100755 index 000000000..29316820d --- /dev/null +++ b/lib/i386/misc/io_insl.s @@ -0,0 +1,19 @@ +! insl() - Input a dword array Author: Kees J. Bot +! 18 Mar 1996 +! void insl(U16_t port, void *buf, size_t count); + +.sect .text +.define _insl +_insl: + push ebp + mov ebp, esp + cld + push edi + mov edx, 8(ebp) ! port + mov edi, 12(ebp) ! buf + mov ecx, 16(ebp) ! byte count + shr ecx, 2 ! dword count + rep ins ! input many dwords + pop edi + pop ebp + ret diff --git a/lib/i386/misc/io_insw.s b/lib/i386/misc/io_insw.s new file mode 100755 index 000000000..039f8d8d5 --- /dev/null +++ b/lib/i386/misc/io_insw.s @@ -0,0 +1,19 @@ +! insw() - Input a word array Author: Kees J. Bot +! 18 Mar 1996 +! void insw(U16_t port, void *buf, size_t count); + +.sect .text +.define _insw +_insw: + push ebp + mov ebp, esp + cld + push edi + mov edx, 8(ebp) ! port + mov edi, 12(ebp) ! buf + mov ecx, 16(ebp) ! byte count + shr ecx, 1 ! word count +rep o16 ins ! input many words + pop edi + pop ebp + ret diff --git a/lib/i386/misc/io_intr.s b/lib/i386/misc/io_intr.s new file mode 100755 index 000000000..f29277089 --- /dev/null +++ b/lib/i386/misc/io_intr.s @@ -0,0 +1,16 @@ +! intr_disable(), intr_enable - Disable/Enable hardware interrupts. +! Author: Kees J. Bot +! 18 Mar 1996 +! void intr_disable(void); +! void intr_enable(void); + +.sect .text +.define _intr_disable +_intr_disable: + cli + ret + +.define _intr_enable +_intr_enable: + sti + ret diff --git a/lib/i386/misc/io_inw.s b/lib/i386/misc/io_inw.s new file mode 100755 index 000000000..773943711 --- /dev/null +++ b/lib/i386/misc/io_inw.s @@ -0,0 +1,14 @@ +! inw() - Input one word Author: Kees J. Bot +! 18 Mar 1996 +! unsigned inw(U16_t port); + +.sect .text +.define _inw +_inw: + push ebp + mov ebp, esp + mov edx, 8(ebp) ! port + xor eax, eax + o16 in dx ! read 1 word + pop ebp + ret diff --git a/lib/i386/misc/io_outb.s b/lib/i386/misc/io_outb.s new file mode 100755 index 000000000..0ae7b06f1 --- /dev/null +++ b/lib/i386/misc/io_outb.s @@ -0,0 +1,14 @@ +! outb() - Output one byte Author: Kees J. Bot +! 18 Mar 1996 +! void outb(U16_t port, U8_t value); + +.sect .text +.define _outb +_outb: + push ebp + mov ebp, esp + mov edx, 8(ebp) ! port + mov eax, 8+4(ebp) ! value + outb dx ! output 1 byte + pop ebp + ret diff --git a/lib/i386/misc/io_outl.s b/lib/i386/misc/io_outl.s new file mode 100755 index 000000000..1a414448e --- /dev/null +++ b/lib/i386/misc/io_outl.s @@ -0,0 +1,14 @@ +! outl() - Output one dword Author: Kees J. Bot +! 18 Mar 1996 +! void outl(U16_t port, u32_t value); + +.sect .text +.define _outl +_outl: + push ebp + mov ebp, esp + mov edx, 8(ebp) ! port + mov eax, 8+4(ebp) ! value + out dx ! output 1 dword + pop ebp + ret diff --git a/lib/i386/misc/io_outsb.s b/lib/i386/misc/io_outsb.s new file mode 100755 index 000000000..d3789cc00 --- /dev/null +++ b/lib/i386/misc/io_outsb.s @@ -0,0 +1,18 @@ +! outsb() - Output a byte array Author: Kees J. Bot +! 18 Mar 1996 +! void outsb(U16_t port, void *buf, size_t count); + +.sect .text +.define _outsb +_outsb: + push ebp + mov ebp, esp + cld + push esi + mov edx, 8(ebp) ! port + mov esi, 12(ebp) ! buf + mov ecx, 16(ebp) ! byte count + rep outsb ! output many bytes + pop esi + pop ebp + ret diff --git a/lib/i386/misc/io_outsl.s b/lib/i386/misc/io_outsl.s new file mode 100755 index 000000000..a57eba7c1 --- /dev/null +++ b/lib/i386/misc/io_outsl.s @@ -0,0 +1,19 @@ +! outsl() - Output a dword array Author: Kees J. Bot +! 18 Mar 1996 +! void outsl(U16_t port, void *buf, size_t count); + +.sect .text +.define _outsl +_outsl: + push ebp + mov ebp, esp + cld + push esi + mov edx, 8(ebp) ! port + mov esi, 12(ebp) ! buf + mov ecx, 16(ebp) ! byte count + shr ecx, 2 ! dword count + rep outs ! output many dwords + pop esi + pop ebp + ret diff --git a/lib/i386/misc/io_outsw.s b/lib/i386/misc/io_outsw.s new file mode 100755 index 000000000..dc4f1bf93 --- /dev/null +++ b/lib/i386/misc/io_outsw.s @@ -0,0 +1,19 @@ +! outsw() - Output a word array Author: Kees J. Bot +! 18 Mar 1996 +! void outsw(U16_t port, void *buf, size_t count); + +.sect .text +.define _outsw +_outsw: + push ebp + mov ebp, esp + cld + push esi + mov edx, 8(ebp) ! port + mov esi, 12(ebp) ! buf + mov ecx, 16(ebp) ! byte count + shr ecx, 1 ! word count +rep o16 outs ! output many words + pop esi + pop ebp + ret diff --git a/lib/i386/misc/io_outw.s b/lib/i386/misc/io_outw.s new file mode 100755 index 000000000..88a04567e --- /dev/null +++ b/lib/i386/misc/io_outw.s @@ -0,0 +1,14 @@ +! outw() - Output one word Author: Kees J. Bot +! 18 Mar 1996 +! void outw(U16_t port, U16_t value); + +.sect .text +.define _outw +_outw: + push ebp + mov ebp, esp + mov edx, 8(ebp) ! port + mov eax, 8+4(ebp) ! value + o16 out dx ! output 1 word + pop ebp + ret diff --git a/lib/i386/misc/oneC_sum.s b/lib/i386/misc/oneC_sum.s new file mode 100755 index 000000000..bfc001d9b --- /dev/null +++ b/lib/i386/misc/oneC_sum.s @@ -0,0 +1,80 @@ +! oneC_sum() - One complement`s checksum Author: Kees J. Bot +! 9 May 1995 +! See RFC 1071, "Computing the Internet checksum" +! See also the C version of this code. + +.sect .text + +.define _oneC_sum + .align 16 +_oneC_sum: + push ebp + mov ebp, esp + push esi + push edi + movzx eax, 8(ebp) ! Checksum of previous block + mov esi, 12(ebp) ! Data to compute checksum over + mov edi, 16(ebp) ! Number of bytes + + xor edx, edx + xorb cl, cl +align: test esi, 3 ! Is the data aligned? + jz aligned + test edi, edi + jz 0f + movb dl, (esi) ! Rotate the first unaligned bytes + dec edi ! into the edx register +0: inc esi + ror edx, 8 + ror eax, 8 ! Rotate the checksum likewise + addb cl, 8 ! Number of bits rotated + jmp align +aligned:add eax, edx ! Summate the unaligned bytes + adc eax, 0 ! Add carry back in for one`s complement + + jmp add6test + .align 16 +add6: add eax, (esi) ! Six times unrolled loop, see below + adc eax, 4(esi) + adc eax, 8(esi) + adc eax, 12(esi) + adc eax, 16(esi) + adc eax, 20(esi) + adc eax, 0 + add esi, 24 +add6test: + sub edi, 24 + jae add6 + add edi, 24 + + jmp add1test + .align 16 +add1: add eax, (esi) ! while ((edi -= 4) >= 0) + adc eax, 0 ! eax += *esi++; + add esi, 4 ! edi += 4; +add1test: + sub edi, 4 + jae add1 + add edi, 4 + + jz done ! Are there extra bytes? + mov edx, (esi) ! Load extra bytes in a full dword + and edx, mask-4(edi*4) ! Mask off excess + add eax, edx ! Add in the last bits + adc eax, 0 +done: rol eax, cl ! Undo the rotation at the beginning + mov edx, eax + shr eax, 16 + o16 add ax, dx ! Add the two words in eax to form + o16 adc ax, 0 ! a 16 bit sum + pop edi + pop esi + pop ebp + ret + +.sect .rom + .align 4 +mask: .data4 0x000000FF, 0x0000FFFF, 0x00FFFFFF + +! +! $PchId: oneC_sum.ack.s,v 1.2 1996/03/12 19:33:51 philip Exp $ diff --git a/lib/i386/rts/Makefile b/lib/i386/rts/Makefile new file mode 100755 index 000000000..75ac6490a --- /dev/null +++ b/lib/i386/rts/Makefile @@ -0,0 +1,42 @@ +# Makefile for lib/i386/rts. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -Was-ack +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../../libc.a + +all: \ + ../../crtso.o \ + ../../m2rtso.o \ + ../../prtso.o \ + $(LIBRARY) + +../../crtso.o: crtso.s + $(CC1) -c crtso.s + mv crtso.o $@ + +../../m2rtso.o: m2rtso.s + $(CC1) -c m2rtso.s + mv m2rtso.o $@ + +../../prtso.o: prtso.s + $(CC1) -c prtso.s + mv prtso.o $@ + +OBJECTS = \ + $(LIBRARY)(__sigreturn.o) \ + $(LIBRARY)(_sendrec.o) \ + $(LIBRARY)(brksize.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(__sigreturn.o): __sigreturn.s + $(CC1) __sigreturn.s + +$(LIBRARY)(_sendrec.o): _sendrec.s + $(CC1) _sendrec.s + +$(LIBRARY)(brksize.o): brksize.s + $(CC1) brksize.s diff --git a/lib/i386/rts/__sigreturn.s b/lib/i386/rts/__sigreturn.s new file mode 100755 index 000000000..783d34f33 --- /dev/null +++ b/lib/i386/rts/__sigreturn.s @@ -0,0 +1,10 @@ +! This routine is the low-level code for returning from signals. +! It calls __sigreturn, which is the normal "system call" routine. +! Both ___sigreturn and __sigreturn are needed. +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define ___sigreturn +.extern __sigreturn +___sigreturn: + add esp, 16 + jmp __sigreturn diff --git a/lib/i386/rts/_sendrec.s b/lib/i386/rts/_sendrec.s new file mode 100755 index 000000000..3e8a59dc4 --- /dev/null +++ b/lib/i386/rts/_sendrec.s @@ -0,0 +1,80 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define __send, __receive, __sendrec + +! See ../h/com.h for C definitions +SEND = 1 +RECEIVE = 2 +BOTH = 3 +NB_SEND = 1 + 16 ! SEND | 0xF0 +NB_RECEIVE = 2 + 16 ! RECEIVE | 0xF0 +SYSVEC = 33 + +SRCDEST = 8 +MESSAGE = 12 + +!*========================================================================* +! _send and _receive * +!*========================================================================* +! _send(), _nb_send(), _receive(), _nb_receive(), and _sendrec() all +! save ebp, but destroy eax and ecx. +.define __send, __nb_send, __receive, __nb_receive, __sendrec +.sect .text +__send: + push ebp + mov ebp, esp + push ebx + mov eax, SRCDEST(ebp) ! eax = dest-src + mov ebx, MESSAGE(ebp) ! ebx = message pointer + mov ecx, SEND ! _send(dest, ptr) + int SYSVEC ! trap to the kernel + pop ebx + pop ebp + ret + +__nb_send: + push ebp + mov ebp, esp + push ebx + mov eax, SRCDEST(ebp) ! eax = dest-src + mov ebx, MESSAGE(ebp) ! ebx = message pointer + mov ecx, NB_SEND ! _nb_send(dest, ptr) + int SYSVEC ! trap to the kernel + pop ebx + pop ebp + ret + +__receive: + push ebp + mov ebp, esp + push ebx + mov eax, SRCDEST(ebp) ! eax = dest-src + mov ebx, MESSAGE(ebp) ! ebx = message pointer + mov ecx, RECEIVE ! _receive(src, ptr) + int SYSVEC ! trap to the kernel + pop ebx + pop ebp + ret + +__nb_receive: + push ebp + mov ebp, esp + push ebx + mov eax, SRCDEST(ebp) ! eax = dest-src + mov ebx, MESSAGE(ebp) ! ebx = message pointer + mov ecx, NB_RECEIVE ! _nb_receive(src, ptr) + int SYSVEC ! trap to the kernel + pop ebx + pop ebp + ret + +__sendrec: + push ebp + mov ebp, esp + push ebx + mov eax, SRCDEST(ebp) ! eax = dest-src + mov ebx, MESSAGE(ebp) ! ebx = message pointer + mov ecx, BOTH ! _sendrec(srcdest, ptr) + int SYSVEC ! trap to the kernel + pop ebx + pop ebp + ret diff --git a/lib/i386/rts/brksize.s b/lib/i386/rts/brksize.s new file mode 100755 index 000000000..b24888b4c --- /dev/null +++ b/lib/i386/rts/brksize.s @@ -0,0 +1,5 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define __brksize +.sect .data +.extern endbss, __brksize +__brksize: .data4 endbss diff --git a/lib/i386/rts/crtso.s b/lib/i386/rts/crtso.s new file mode 100755 index 000000000..f26bcd0df --- /dev/null +++ b/lib/i386/rts/crtso.s @@ -0,0 +1,75 @@ +! This is the C run-time start-off routine. It's job is to take the +! arguments as put on the stack by EXEC, and to parse them and set them up the +! way _main expects them. +! It also initializes _environ when this variable isn't defined by the +! programmer. The detection of whether _environ belong to us is rather +! simplistic. We simply check for some magic value, but there is no other +! way. + +.sect .text; .sect .rom; .sect .data; .sect .bss + +.define begtext, begdata, begbss +.sect .text +begtext: +.sect .rom +begrom: +.sect .data +begdata: +.sect .bss +begbss: + +.define crtso, ___main, __penviron, __penvp, __fpu_present +.extern _main, _exit +.sect .text +crtso: + xor ebp, ebp ! clear for backtrace of core files + mov eax, (esp) ! argc + lea edx, 4(esp) ! argv + lea ecx, 8(esp)(eax*4) ! envp + + ! Test if environ is in the initialized data area and is set to our + ! magic number. If so then it is not redefined by the user. + mov ebx, _environ + cmp ebx, __edata ! within initialized data? + jae 0f + testb bl, 3 ! aligned? + jnz 0f + cmp (ebx), 0x53535353 ! is it our _environ? + jne 0f + mov (__penviron), ebx ! _penviron = &environ; +0: mov ebx, (__penviron) + mov (ebx), ecx ! *_penviron = envp; + + push ecx ! push envp + push edx ! push argv + push eax ! push argc + + ! Test the EM bit of the MSW to determine if an FPU is present and + ! set __fpu_present if one is found. + smsw ax + testb al, 0x4 ! EM bit in MSW + setz (__fpu_present) ! True if not set + + call _main ! main(argc, argv, envp) + + push eax ! push exit status + call _exit + + hlt ! force a trap if exit fails + +___main: ! for GCC + ret + +.sect .rom + .data4 0 ! Separate I&D: *NULL == 0 + ! Also keeps the first string in the + ! program from appearing at location 0! +.sect .data +__penviron: + .data4 __penvp ! Pointer to environ, or hidden pointer + +.sect .bss + .comm __penvp, 4 ! Hidden environment vector + .comm __fpu_present, 4 ! FPU present flag + +.extern endtext ! Force loading of end labels. diff --git a/lib/i386/rts/m2rtso.s b/lib/i386/rts/m2rtso.s new file mode 100755 index 000000000..92b6ce080 --- /dev/null +++ b/lib/i386/rts/m2rtso.s @@ -0,0 +1,67 @@ +! This is the Modula-2 run-time start-off routine. It's job is to take the +! arguments as put on the stack by EXEC, and to parse them and set them up the +! way _m_a_i_n expects them. + +.sect .text; .sect .rom; .sect .data; .sect .bss + +.define begtext, begdata, begbss +.sect .text +begtext: +.sect .rom +begrom: +.sect .data +begdata: +.sect .bss +begbss: + +.define m2rtso, hol0, __penviron, __penvp, __fpu_present +.sect .text +m2rtso: + xor ebp, ebp ! clear for backtrace of core files + mov eax, (esp) ! argc + lea edx, 4(esp) ! argv + lea ecx, 8(esp)(eax*4) ! envp + + ! Test if environ is in the initialized data area and is set to our + ! magic number. If so then it is not redefined by the user. + mov ebx, _environ + cmp ebx, __edata ! within initialized data? + jae 0f + testb bl, 3 ! aligned? + jnz 0f + cmp (ebx), 0x53535353 ! is it our environ? + jne 0f + mov (__penviron), ebx ! _penviron = &environ; +0: mov ebx, (__penviron) + mov (ebx), ecx ! *_penviron = envp; + + push ecx ! push envp + push edx ! push argv + push eax ! push argc + + ! Test the EM bit of the MSW to determine if an FPU is present and + ! set __fpu_present if one is found. + smsw ax + testb al, 0x4 ! EM bit in MSW + setz (__fpu_present) ! True if not set + + call __m_a_i_n ! run Modula-2 program + + push eax ! push exit status + call __exit + + hlt ! force a trap if exit fails + +.sect .rom + .data4 0 ! Separate I&D: *NULL == 0 + ! Also keeps the first string in the + ! program from appearing at location 0! +.sect .data +__penviron: + .data4 __penvp ! Pointer to environ, or hidden pointer + +.sect .bss + .comm __penvp, 4 ! Hidden environment vector + .comm __fpu_present, 4 ! FPU present flag + +.extern endtext ! Force loading of end labels. diff --git a/lib/i386/rts/prtso.s b/lib/i386/rts/prtso.s new file mode 100755 index 000000000..0defa7de2 --- /dev/null +++ b/lib/i386/rts/prtso.s @@ -0,0 +1,69 @@ +! This is the Pascal run-time start-off routine. It's job is to take the +! arguments as put on the stack by EXEC, and to parse them and set them up the +! way _m_a_i_n expects them. + +.sect .text; .sect .rom; .sect .data; .sect .bss + +.define begtext, begdata, begbss +.sect .text +begtext: +.sect .rom +begrom: +.sect .data +begdata: +.sect .bss +begbss: + +.define prtso, hol0, __penviron, __penvp, __fpu_present +.sect .text +prtso: + xor ebp, ebp ! clear for backtrace of core files + mov eax, (esp) ! argc + lea edx, 4(esp) ! argv + lea ecx, 8(esp)(eax*4) ! envp + + ! Test if environ is in the initialized data area and is set to our + ! magic number. If so then it is not redefined by the user. + mov ebx, _environ + cmp ebx, __edata ! within initialized data? + jae 0f + testb bl, 3 ! aligned? + jnz 0f + cmp (ebx), 0x53535353 ! is it our environ? + jne 0f + mov (__penviron), ebx ! _penviron = &environ; +0: mov ebx, (__penviron) + mov (ebx), ecx ! *_penviron = envp; + + push ecx ! push envp + push edx ! push argv + push eax ! push argc + + ! Test the EM bit of the MSW to determine if an FPU is present and + ! set __fpu_present if one is found. + smsw ax + testb al, 0x4 ! EM bit in MSW + setz (__fpu_present) ! True if not set + + mov (.ignmask), 56 + + call __m_a_i_n ! Run Pascal program + + push eax ! push exit status + call __exit + + hlt ! force a trap if exit fails + +.sect .rom + .data4 0 ! Separate I&D: *NULL == 0 + ! Also keeps the first string in the + ! program from appearing at location 0! +.sect .data +__penviron: + .data4 __penvp ! Pointer to environ, or hidden pointer + +.sect .bss + .comm __penvp, 4 ! Hidden environment vector + .comm __fpu_present, 4 ! FPU present flag + +.extern endtext ! Force loading of end labels. diff --git a/lib/i386/string/Makefile b/lib/i386/string/Makefile new file mode 100755 index 000000000..af5d5f646 --- /dev/null +++ b/lib/i386/string/Makefile @@ -0,0 +1,112 @@ +# Makefile for lib/i386/string. + +CC1 = $(CC) -Was-ack -c + +LIBRARY = ../../libc.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(_memmove.o) \ + $(LIBRARY)(_strncat.o) \ + $(LIBRARY)(_strncmp.o) \ + $(LIBRARY)(_strncpy.o) \ + $(LIBRARY)(_strnlen.o) \ + $(LIBRARY)(bcmp.o) \ + $(LIBRARY)(bcopy.o) \ + $(LIBRARY)(bzero.o) \ + $(LIBRARY)(index.o) \ + $(LIBRARY)(memchr.o) \ + $(LIBRARY)(memcmp.o) \ + $(LIBRARY)(memcpy.o) \ + $(LIBRARY)(memmove.o) \ + $(LIBRARY)(memset.o) \ + $(LIBRARY)(rindex.o) \ + $(LIBRARY)(strcat.o) \ + $(LIBRARY)(strchr.o) \ + $(LIBRARY)(strcmp.o) \ + $(LIBRARY)(strcpy.o) \ + $(LIBRARY)(strlen.o) \ + $(LIBRARY)(strncat.o) \ + $(LIBRARY)(strncmp.o) \ + $(LIBRARY)(strncpy.o) \ + $(LIBRARY)(strnlen.o) \ + $(LIBRARY)(strrchr.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(_memmove.o): _memmove.s + $(CC1) _memmove.s + +$(LIBRARY)(_strncat.o): _strncat.s + $(CC1) _strncat.s + +$(LIBRARY)(_strncmp.o): _strncmp.s + $(CC1) _strncmp.s + +$(LIBRARY)(_strncpy.o): _strncpy.s + $(CC1) _strncpy.s + +$(LIBRARY)(_strnlen.o): _strnlen.s + $(CC1) _strnlen.s + +$(LIBRARY)(bcmp.o): bcmp.s + $(CC1) bcmp.s + +$(LIBRARY)(bcopy.o): bcopy.s + $(CC1) bcopy.s + +$(LIBRARY)(bzero.o): bzero.s + $(CC1) bzero.s + +$(LIBRARY)(index.o): index.s + $(CC1) index.s + +$(LIBRARY)(memchr.o): memchr.s + $(CC1) memchr.s + +$(LIBRARY)(memcmp.o): memcmp.s + $(CC1) memcmp.s + +$(LIBRARY)(memcpy.o): memcpy.s + $(CC1) memcpy.s + +$(LIBRARY)(memmove.o): memmove.s + $(CC1) memmove.s + +$(LIBRARY)(memset.o): memset.s + $(CC1) memset.s + +$(LIBRARY)(rindex.o): rindex.s + $(CC1) rindex.s + +$(LIBRARY)(strcat.o): strcat.s + $(CC1) strcat.s + +$(LIBRARY)(strchr.o): strchr.s + $(CC1) strchr.s + +$(LIBRARY)(strcmp.o): strcmp.s + $(CC1) strcmp.s + +$(LIBRARY)(strcpy.o): strcpy.s + $(CC1) strcpy.s + +$(LIBRARY)(strlen.o): strlen.s + $(CC1) strlen.s + +$(LIBRARY)(strncat.o): strncat.s + $(CC1) strncat.s + +$(LIBRARY)(strncmp.o): strncmp.s + $(CC1) strncmp.s + +$(LIBRARY)(strncpy.o): strncpy.s + $(CC1) strncpy.s + +$(LIBRARY)(strnlen.o): strnlen.s + $(CC1) strnlen.s + +$(LIBRARY)(strrchr.o): strrchr.s + $(CC1) strrchr.s diff --git a/lib/i386/string/README b/lib/i386/string/README new file mode 100755 index 000000000..93bad4426 --- /dev/null +++ b/lib/i386/string/README @@ -0,0 +1,52 @@ +Notes on i80386 string assembly routines. Author: Kees J. Bot + 2 Jan 1994 + +Remarks. + All routines set up proper stack frames, so that stack traces can be + derived from core dumps. String routines are often the ones that + get the bad pointer. + + Flags are often not right in boundary cases (zero string length) on + repeated string scanning or comparing instructions. This has been + handled in sometimes nonobvious ways. + + Only the eax, edx, and ecx registers are not preserved, all other + registers are. This is what GCC expects. (ACK sees ebx as scratch + too.) The direction byte is assumed to be wrong, and left clear on + exit. + +Assumptions. + The average string is short, so short strings should not suffer from + smart tricks to copy, compare, or search large strings fast. This + means that the routines are fast on average, but not optimal for + long strings. + + It doesn't pay to use word or longword operations on strings, the + setup time hurts the average case. + + Memory blocks are probably large and on word or longword boundaries. + + No unaligned word moves are done. Again the setup time may hurt the + average case. Furthermore, the author likes to enable the alignment + check on a 486. + +String routines. + They have been implemented using byte string instructions. The + length of a string it usually determined first, followed by the + actual operation. + +Strcmp. + This is the only string routine that uses a loop, and not + instructions with a repeat prefix. Problem is that we don't know + how long the string is. Scanning for the end costs if the strings + are unequal in the first few bytes. + +Strchr. + The character we look for is often not there, or at some distance + from the start. The string is scanned twice, for the terminating + zero and the character searched, in chunks of increasing length. + +Memory routines. + Memmove, memcpy, and memset use word or longword instructions if the + address(es) are at word or longword boundaries. No tricks to get + alignment after doing a few bytes. No unaligned operations. diff --git a/lib/i386/string/_memmove.s b/lib/i386/string/_memmove.s new file mode 100755 index 000000000..45336400f --- /dev/null +++ b/lib/i386/string/_memmove.s @@ -0,0 +1,58 @@ +! _memmove() Author: Kees J. Bot +! 2 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void *_memmove(void *s1, const void *s2, size_t n) +! Copy a chunk of memory. Handle overlap. +! +.sect .text +.define __memmove, __memcpy + .align 16 +__memmove: + push ebp + mov ebp, esp + push esi + push edi + mov edi, 8(ebp) ! String s1 + mov esi, 12(ebp) ! String s2 + mov ecx, 16(ebp) ! Length + mov eax, edi + sub eax, esi + cmp eax, ecx + jb downwards ! if (s2 - s1) < n then copy downwards +__memcpy: + cld ! Clear direction bit: upwards + cmp ecx, 16 + jb upbyte ! Don't bother being smart with short arrays + mov eax, esi + or eax, edi + testb al, 1 + jnz upbyte ! Bit 0 set, use byte copy + testb al, 2 + jnz upword ! Bit 1 set, use word copy +uplword:shrd eax, ecx, 2 ! Save low 2 bits of ecx in eax + shr ecx, 2 + rep + movs ! Copy longwords. + shld ecx, eax, 2 ! Restore excess count +upword: shr ecx, 1 + rep + o16 movs ! Copy words + adc ecx, ecx ! One more byte? +upbyte: rep + movsb ! Copy bytes +done: mov eax, 8(ebp) ! Absolutely noone cares about this value + pop edi + pop esi + pop ebp + ret + +! Handle bad overlap by copying downwards, don't bother to do word copies. +downwards: + std ! Set direction bit: downwards + lea esi, -1(esi)(ecx*1) + lea edi, -1(edi)(ecx*1) + rep + movsb ! Copy bytes + cld + jmp done diff --git a/lib/i386/string/_strncat.s b/lib/i386/string/_strncat.s new file mode 100755 index 000000000..6c9873bf0 --- /dev/null +++ b/lib/i386/string/_strncat.s @@ -0,0 +1,41 @@ +! _strncat() Author: Kees J. Bot +! 1 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *_strncat(char *s1, const char *s2, size_t edx) +! Append string s2 to s1. +! +.sect .text +.define __strncat + .align 16 +__strncat: + push ebp + mov ebp, esp + push esi + push edi + mov edi, 8(ebp) ! String s1 + mov ecx, -1 + xorb al, al ! Null byte + cld + repne + scasb ! Look for the zero byte in s1 + dec edi ! Back one up (and clear 'Z' flag) + push edi ! Save end of s1 + mov edi, 12(ebp) ! edi = string s2 + mov ecx, edx ! Maximum count + repne + scasb ! Look for the end of s2 + jne no0 + inc ecx ! Exclude null byte +no0: sub edx, ecx ! Number of bytes in s2 + mov ecx, edx + mov esi, 12(ebp) ! esi = string s2 + pop edi ! edi = end of string s1 + rep + movsb ! Copy bytes + stosb ! Add a terminating null + mov eax, 8(ebp) ! Return s1 + pop edi + pop esi + pop ebp + ret diff --git a/lib/i386/string/_strncmp.s b/lib/i386/string/_strncmp.s new file mode 100755 index 000000000..8aea8d393 --- /dev/null +++ b/lib/i386/string/_strncmp.s @@ -0,0 +1,35 @@ +! strncmp() Author: Kees J. Bot +! 1 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! int strncmp(const char *s1, const char *s2, size_t ecx) +! Compare two strings. +! +.sect .text +.define __strncmp + .align 16 +__strncmp: + push ebp + mov ebp, esp + push esi + push edi + test ecx, ecx ! Max length is zero? + je done + mov esi, 8(ebp) ! esi = string s1 + mov edi, 12(ebp) ! edi = string s2 + cld +compare: + cmpsb ! Compare two bytes + jne done + cmpb -1(esi), 0 ! End of string? + je done + dec ecx ! Length limit reached? + jne compare +done: seta al ! al = (s1 > s2) + setb ah ! ah = (s1 < s2) + subb al, ah + movsxb eax, al ! eax = (s1 > s2) - (s1 < s2), i.e. -1, 0, 1 + pop edi + pop esi + pop ebp + ret diff --git a/lib/i386/string/_strncpy.s b/lib/i386/string/_strncpy.s new file mode 100755 index 000000000..15b8966ad --- /dev/null +++ b/lib/i386/string/_strncpy.s @@ -0,0 +1,24 @@ +! _strncpy() Author: Kees J. Bot +! 1 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *_strncpy(char *s1, const char *s2, size_t ecx) +! Copy string s2 to s1. +! +.sect .text +.define __strncpy + .align 16 +__strncpy: + mov edi, 12(ebp) ! edi = string s2 + xorb al, al ! Look for a zero byte + mov edx, ecx ! Save maximum count + cld + repne + scasb ! Look for end of s2 + sub edx, ecx ! Number of bytes in s2 including null + xchg ecx, edx + mov esi, 12(ebp) ! esi = string s2 + mov edi, 8(ebp) ! edi = string s1 + rep + movsb ! Copy bytes + ret diff --git a/lib/i386/string/_strnlen.s b/lib/i386/string/_strnlen.s new file mode 100755 index 000000000..b17b37363 --- /dev/null +++ b/lib/i386/string/_strnlen.s @@ -0,0 +1,28 @@ +! _strnlen() Author: Kees J. Bot +! 1 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! size_t _strnlen(const char *s, size_t ecx) +! Return the length of a string. +! +.sect .text +.define __strnlen + .align 16 +__strnlen: + push ebp + mov ebp, esp + push edi + mov edi, 8(ebp) ! edi = string + xorb al, al ! Look for a zero byte + mov edx, ecx ! Save maximum count + cmpb cl, 1 ! 'Z' bit must be clear if ecx = 0 + cld + repne + scasb ! Look for zero + jne no0 + inc ecx ! Don't count zero byte +no0: mov eax, edx + sub eax, ecx ! Compute bytes scanned + pop edi + pop ebp + ret diff --git a/lib/i386/string/bcmp.s b/lib/i386/string/bcmp.s new file mode 100755 index 000000000..e9f99c15b --- /dev/null +++ b/lib/i386/string/bcmp.s @@ -0,0 +1,28 @@ +! bcmp() Author: Kees J. Bot +! 2 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! int bcmp(const void *s1, const void *s2, size_t n) +! Compare two chunks of memory. +! This is a BSD routine that escaped from the kernel. Don't use. +! (Alas it is not without some use, it reports the number of bytes +! after the bytes that are equal. So it can't be simply replaced.) +! +.sect .text +.define _bcmp + .align 16 +_bcmp: + push ebp + mov ebp, esp + push 16(ebp) + push 12(ebp) + push 8(ebp) + call _memcmp ! Let memcmp do the work + test eax, eax + jz equal + sub edx, 8(ebp) ! Memcmp was nice enough to leave "esi" in edx + dec edx ! Number of bytes that are equal + mov eax, 16(ebp) + sub eax, edx ! Number of bytes that are unequal +equal: leave + ret diff --git a/lib/i386/string/bcopy.s b/lib/i386/string/bcopy.s new file mode 100755 index 000000000..dc6bf89f0 --- /dev/null +++ b/lib/i386/string/bcopy.s @@ -0,0 +1,16 @@ +! bcopy() Author: Kees J. Bot +! 2 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void bcopy(const void *s1, void *s2, size_t n) +! Copy a chunk of memory. Handle overlap. +! This is a BSD routine that escaped from the kernel. Don't use. +! +.sect .text +.define _bcopy + .align 16 +_bcopy: + mov eax, 4(esp) ! Exchange string arguments + xchg eax, 8(esp) + mov 4(esp), eax + jmp __memmove ! Call the proper routine diff --git a/lib/i386/string/bzero.s b/lib/i386/string/bzero.s new file mode 100755 index 000000000..4223472c5 --- /dev/null +++ b/lib/i386/string/bzero.s @@ -0,0 +1,20 @@ +! bzero() Author: Kees J. Bot +! 2 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void bzero(void *s, size_t n) +! Set a chunk of memory to zero. +! This is a BSD routine that escaped from the kernel. Don't use. +! +.sect .text +.define _bzero + .align 16 +_bzero: + push ebp + mov ebp, esp + push 12(ebp) ! Size + push 0 ! Zero + push 8(ebp) ! String + call _memset ! Call the proper routine + leave + ret diff --git a/lib/i386/string/index.s b/lib/i386/string/index.s new file mode 100755 index 000000000..704afe583 --- /dev/null +++ b/lib/i386/string/index.s @@ -0,0 +1,13 @@ +! index() Author: Kees J. Bot +! 2 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *index(const char *s, int c) +! Look for a character in a string. Has suffered from a hostile +! takeover by strchr(). +! +.sect .text +.define _index + .align 16 +_index: + jmp _strchr diff --git a/lib/i386/string/memchr.s b/lib/i386/string/memchr.s new file mode 100755 index 000000000..6eb3a678d --- /dev/null +++ b/lib/i386/string/memchr.s @@ -0,0 +1,30 @@ +! memchr() Author: Kees J. Bot +! 2 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void *memchr(const void *s, int c, size_t n) +! Look for a character in a chunk of memory. +! +.sect .text +.define _memchr + .align 16 +_memchr: + push ebp + mov ebp, esp + push edi + mov edi, 8(ebp) ! edi = string + movb al, 12(ebp) ! The character to look for + mov ecx, 16(ebp) ! Length + cmpb cl, 1 ! 'Z' bit must be clear if ecx = 0 + cld + repne + scasb + jne failure + lea eax, -1(edi) ! Found + pop edi + pop ebp + ret +failure:xor eax, eax + pop edi + pop ebp + ret diff --git a/lib/i386/string/memcmp.s b/lib/i386/string/memcmp.s new file mode 100755 index 000000000..6ac8e35c1 --- /dev/null +++ b/lib/i386/string/memcmp.s @@ -0,0 +1,56 @@ +! memcmp() Author: Kees J. Bot +! 2 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! int memcmp(const void *s1, const void *s2, size_t n) +! Compare two chunks of memory. +! +.sect .text +.define _memcmp + .align 16 +_memcmp: + cld + push ebp + mov ebp, esp + push esi + push edi + mov esi, 8(ebp) ! String s1 + mov edi, 12(ebp) ! String s2 + mov ecx, 16(ebp) ! Length + cmp ecx, 16 + jb cbyte ! Don't bother being smart with short arrays + mov eax, esi + or eax, edi + testb al, 1 + jnz cbyte ! Bit 0 set, use byte compare + testb al, 2 + jnz cword ! Bit 1 set, use word compare +clword: shrd eax, ecx, 2 ! Save low two bits of ecx in eax + shr ecx, 2 + repe + cmps ! Compare longwords + sub esi, 4 + sub edi, 4 + inc ecx ! Recompare the last longword + shld ecx, eax, 2 ! And any excess bytes + jmp last +cword: shrd eax, ecx, 1 ! Save low bit of ecx in eax + shr ecx, 1 + repe + o16 cmps ! Compare words + sub esi, 2 + sub edi, 2 + inc ecx ! Recompare the last word + shld ecx, eax, 1 ! And one more byte? +cbyte: test ecx, ecx ! Set 'Z' flag if ecx = 0 +last: repe + cmpsb ! Look for the first differing byte + seta al ! al = (s1 > s2) + setb ah ! ah = (s1 < s2) + subb al, ah + movsxb eax, al ! eax = (s1 > s2) - (s1 < s2), i.e. -1, 0, 1 + mov edx, esi ! For bcmp() to play with + pop edi + pop esi + pop ebp + ret diff --git a/lib/i386/string/memcpy.s b/lib/i386/string/memcpy.s new file mode 100755 index 000000000..32a23b221 --- /dev/null +++ b/lib/i386/string/memcpy.s @@ -0,0 +1,24 @@ +! memcpy() Author: Kees J. Bot +! 2 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void *memcpy(void *s1, const void *s2, size_t n) +! Copy a chunk of memory. +! This routine need not handle overlap, so it does not handle overlap. +! One could simply call __memmove, the cost of the overlap check is +! negligible, but you are dealing with a programmer who believes that +! if anything can go wrong, it should go wrong. +! +.sect .text +.define _memcpy + .align 16 +_memcpy: + push ebp + mov ebp, esp + push esi + push edi + mov edi, 8(ebp) ! String s1 + mov esi, 12(ebp) ! String s2 + mov ecx, 16(ebp) ! Length + ! No overlap check here + jmp __memcpy ! Call the part of __memmove that copies up diff --git a/lib/i386/string/memmove.s b/lib/i386/string/memmove.s new file mode 100755 index 000000000..5962ffab5 --- /dev/null +++ b/lib/i386/string/memmove.s @@ -0,0 +1,12 @@ +! memmove() Author: Kees J. Bot +! 2 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void *memmove(void *s1, const void *s2, size_t n) +! Copy a chunk of memory. Handle overlap. +! +.sect .text +.define _memmove + .align 16 +_memmove: + jmp __memmove ! Call common code diff --git a/lib/i386/string/memset.s b/lib/i386/string/memset.s new file mode 100755 index 000000000..4da22df4c --- /dev/null +++ b/lib/i386/string/memset.s @@ -0,0 +1,44 @@ +! memset() Author: Kees J. Bot +! 2 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void *memset(void *s, int c, size_t n) +! Set a chunk of memory to the same byte value. +! +.sect .text +.define _memset + .align 16 +_memset: + push ebp + mov ebp, esp + push edi + mov edi, 8(ebp) ! The string + movzxb eax, 12(ebp) ! The fill byte + mov ecx, 16(ebp) ! Length + cld + cmp ecx, 16 + jb sbyte ! Don't bother being smart with short arrays + test edi, 1 + jnz sbyte ! Bit 0 set, use byte store + test edi, 2 + jnz sword ! Bit 1 set, use word store +slword: movb ah, al + mov edx, eax + sal edx, 16 + or eax, edx ! One byte to four bytes + shrd edx, ecx, 2 ! Save low two bits of ecx in edx + shr ecx, 2 + rep + stos ! Store longwords. + shld ecx, edx, 2 ! Restore low two bits +sword: movb ah, al ! One byte to two bytes + shr ecx, 1 + rep + o16 stos ! Store words + adc ecx, ecx ! One more byte? +sbyte: rep + stosb ! Store bytes +done: mov eax, 8(ebp) ! Return some value you have no need for + pop edi + pop ebp + ret diff --git a/lib/i386/string/rindex.s b/lib/i386/string/rindex.s new file mode 100755 index 000000000..3c8163e05 --- /dev/null +++ b/lib/i386/string/rindex.s @@ -0,0 +1,13 @@ +! rindex() Author: Kees J. Bot +! 2 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *rindex(const char *s, int c) +! Look for the last occurrence a character in a string. Has suffered +! from a hostile takeover by strrchr(). +! +.sect .text +.define _rindex + .align 16 +_rindex: + jmp _strrchr diff --git a/lib/i386/string/strcat.s b/lib/i386/string/strcat.s new file mode 100755 index 000000000..6b9a14ae9 --- /dev/null +++ b/lib/i386/string/strcat.s @@ -0,0 +1,13 @@ +! strcat() Author: Kees J. Bot +! 1 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *strcat(char *s1, const char *s2) +! Append string s2 to s1. +! +.sect .text +.define _strcat + .align 16 +_strcat: + mov edx, -1 ! Unlimited length + jmp __strncat ! Common code diff --git a/lib/i386/string/strchr.s b/lib/i386/string/strchr.s new file mode 100755 index 000000000..777503dc3 --- /dev/null +++ b/lib/i386/string/strchr.s @@ -0,0 +1,41 @@ +! strchr() Author: Kees J. Bot +! 1 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *strchr(const char *s, int c) +! Look for a character in a string. +! +.sect .text +.define _strchr + .align 16 +_strchr: + push ebp + mov ebp, esp + push edi + cld + mov edi, 8(ebp) ! edi = string + mov edx, 16 ! Look at small chunks of the string +next: shl edx, 1 ! Chunks become bigger each time + mov ecx, edx + xorb al, al ! Look for the zero at the end + repne + scasb + pushf ! Remember the flags + sub ecx, edx + neg ecx ! Some or all of the chunk + sub edi, ecx ! Step back + movb al, 12(ebp) ! The character to look for + repne + scasb + je found + popf ! Did we find the end of string earlier? + jne next ! No, try again + xor eax, eax ! Return NULL + pop edi + pop ebp + ret +found: pop eax ! Get rid of those flags + lea eax, -1(edi) ! Address of byte found + pop edi + pop ebp + ret diff --git a/lib/i386/string/strcmp.s b/lib/i386/string/strcmp.s new file mode 100755 index 000000000..85b37e3b0 --- /dev/null +++ b/lib/i386/string/strcmp.s @@ -0,0 +1,13 @@ +! strcmp() Author: Kees J. Bot +! 1 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! int strcmp(const char *s1, const char *s2) +! Compare two strings. +! +.sect .text +.define _strcmp + .align 16 +_strcmp: + mov ecx, -1 ! Unlimited length + jmp __strncmp ! Common code diff --git a/lib/i386/string/strcpy.s b/lib/i386/string/strcpy.s new file mode 100755 index 000000000..04e6217d8 --- /dev/null +++ b/lib/i386/string/strcpy.s @@ -0,0 +1,22 @@ +! strcpy() Author: Kees J. Bot +! 1 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *strcpy(char *s1, const char *s2) +! Copy string s2 to s1. +! +.sect .text +.define _strcpy + .align 16 +_strcpy: + push ebp + mov ebp, esp + push esi + push edi + mov ecx, -1 ! Unlimited length + call __strncpy ! Common code + mov eax, 8(ebp) ! Return s1 + pop edi + pop esi + pop ebp + ret diff --git a/lib/i386/string/strlen.s b/lib/i386/string/strlen.s new file mode 100755 index 000000000..2a1a24d6f --- /dev/null +++ b/lib/i386/string/strlen.s @@ -0,0 +1,13 @@ +! strlen() Author: Kees J. Bot +! 1 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! size_t strlen(const char *s) +! Return the length of a string. +! +.sect .text +.define _strlen + .align 16 +_strlen: + mov ecx, -1 ! Unlimited length + jmp __strnlen ! Common code diff --git a/lib/i386/string/strncat.s b/lib/i386/string/strncat.s new file mode 100755 index 000000000..0756d2e9e --- /dev/null +++ b/lib/i386/string/strncat.s @@ -0,0 +1,13 @@ +! strncat() Author: Kees J. Bot +! 1 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! size_t strncat(char *s1, const char *s2, size_t n) +! Append string s2 to s1. +! +.sect .text +.define _strncat + .align 16 +_strncat: + mov edx, 12(esp) ! Maximum length + jmp __strncat ! Common code diff --git a/lib/i386/string/strncmp.s b/lib/i386/string/strncmp.s new file mode 100755 index 000000000..6f1382656 --- /dev/null +++ b/lib/i386/string/strncmp.s @@ -0,0 +1,13 @@ +! strncmp() Author: Kees J. Bot +! 1 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! int strncmp(const char *s1, const char *s2, size_t n) +! Compare two strings. +! +.sect .text +.define _strncmp + .align 16 +_strncmp: + mov ecx, 12(esp) ! Maximum length + jmp __strncmp ! Common code diff --git a/lib/i386/string/strncpy.s b/lib/i386/string/strncpy.s new file mode 100755 index 000000000..52d392d46 --- /dev/null +++ b/lib/i386/string/strncpy.s @@ -0,0 +1,25 @@ +! strncpy() Author: Kees J. Bot +! 1 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *strncpy(char *s1, const char *s2, size_t n) +! Copy string s2 to s1. +! +.sect .text +.define _strncpy + .align 16 +_strncpy: + push ebp + mov ebp, esp + push esi + push edi + mov ecx, 16(ebp) ! Maximum length + call __strncpy ! Common code + mov ecx, edx ! Number of bytes not copied + rep + stosb ! strncpy always copies n bytes by null padding + mov eax, 8(ebp) ! Return s1 + pop edi + pop esi + pop ebp + ret diff --git a/lib/i386/string/strnlen.s b/lib/i386/string/strnlen.s new file mode 100755 index 000000000..99d5a0ce0 --- /dev/null +++ b/lib/i386/string/strnlen.s @@ -0,0 +1,13 @@ +! strnlen() Author: Kees J. Bot +! 1 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! size_t strnlen(const char *s, size_t n) +! Return the length of a string. +! +.sect .text +.define _strnlen + .align 16 +_strnlen: + mov ecx, 8(esp) ! Maximum length + jmp __strnlen ! Common code diff --git a/lib/i386/string/strrchr.s b/lib/i386/string/strrchr.s new file mode 100755 index 000000000..510d24da1 --- /dev/null +++ b/lib/i386/string/strrchr.s @@ -0,0 +1,36 @@ +! strrchr() Author: Kees J. Bot +! 2 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *strrchr(const char *s, int c) +! Look for the last occurrence a character in a string. +! +.sect .text +.define _strrchr + .align 16 +_strrchr: + push ebp + mov ebp, esp + push edi + mov edi, 8(ebp) ! edi = string + mov ecx, -1 + xorb al, al + cld + repne + scasb ! Look for the end of the string + not ecx ! -1 - ecx = Length of the string + null + dec edi ! Put edi back on the zero byte + movb al, 12(ebp) ! The character to look for + std ! Downwards search + repne + scasb + cld ! Direction bit back to default + jne failure + lea eax, 1(edi) ! Found it + pop edi + pop ebp + ret +failure:xor eax, eax ! Not there + pop edi + pop ebp + ret diff --git a/lib/i86/Makefile b/lib/i86/Makefile new file mode 100755 index 000000000..dc1d7f876 --- /dev/null +++ b/lib/i86/Makefile @@ -0,0 +1,10 @@ +# Makefile for lib/i86. + +MAKE = exec make -$(MAKEFLAGS) + +install: + cd em && $(MAKE) + cd int64 && $(MAKE) + cd misc && $(MAKE) + cd rts && $(MAKE) + cd string && $(MAKE) diff --git a/lib/i86/em/Makefile b/lib/i86/em/Makefile new file mode 100755 index 000000000..c415a1605 --- /dev/null +++ b/lib/i86/em/Makefile @@ -0,0 +1,217 @@ +# Makefile for lib/i86/em. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -Was-ncc +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../../libe.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(em_adi.o) \ + $(LIBRARY)(em_and.o) \ + $(LIBRARY)(em_blm.o) \ + $(LIBRARY)(em_cii.o) \ + $(LIBRARY)(em_cmi4.o) \ + $(LIBRARY)(em_cms.o) \ + $(LIBRARY)(em_cmu4.o) \ + $(LIBRARY)(em_com.o) \ + $(LIBRARY)(em_csa2.o) \ + $(LIBRARY)(em_csa4.o) \ + $(LIBRARY)(em_csb2.o) \ + $(LIBRARY)(em_csb4.o) \ + $(LIBRARY)(em_cuu.o) \ + $(LIBRARY)(em_dup.o) \ + $(LIBRARY)(em_dvi4.o) \ + $(LIBRARY)(em_dvu4.o) \ + $(LIBRARY)(em_error.o) \ + $(LIBRARY)(em_exg.o) \ + $(LIBRARY)(em_fat.o) \ + $(LIBRARY)(em_fp8087.o) \ + $(LIBRARY)(em_gto.o) \ + $(LIBRARY)(em_hol0.o) \ + $(LIBRARY)(em_iaar.o) \ + $(LIBRARY)(em_ilar.o) \ + $(LIBRARY)(em_inn.o) \ + $(LIBRARY)(em_ior.o) \ + $(LIBRARY)(em_isar.o) \ + $(LIBRARY)(em_lar2.o) \ + $(LIBRARY)(em_lfr6.o) \ + $(LIBRARY)(em_lfr8.o) \ + $(LIBRARY)(em_loi.o) \ + $(LIBRARY)(em_mli4.o) \ + $(LIBRARY)(em_mon.o) \ + $(LIBRARY)(em_nop.o) \ + $(LIBRARY)(em_rck.o) \ + $(LIBRARY)(em_ret6.o) \ + $(LIBRARY)(em_ret8.o) \ + $(LIBRARY)(em_retarea.o) \ + $(LIBRARY)(em_return.o) \ + $(LIBRARY)(em_rmi4.o) \ + $(LIBRARY)(em_rmu4.o) \ + $(LIBRARY)(em_sar2.o) \ + $(LIBRARY)(em_sbi.o) \ + $(LIBRARY)(em_set.o) \ + $(LIBRARY)(em_stb.o) \ + $(LIBRARY)(em_sti.o) \ + $(LIBRARY)(em_stop.o) \ + $(LIBRARY)(em_trp.o) \ + $(LIBRARY)(em_unknown.o) \ + $(LIBRARY)(em_vars.o) \ + $(LIBRARY)(em_xor.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(em_adi.o): em_adi.s + $(CC1) em_adi.s + +$(LIBRARY)(em_and.o): em_and.s + $(CC1) em_and.s + +$(LIBRARY)(em_blm.o): em_blm.s + $(CC1) em_blm.s + +$(LIBRARY)(em_cii.o): em_cii.s + $(CC1) em_cii.s + +$(LIBRARY)(em_cmi4.o): em_cmi4.s + $(CC1) em_cmi4.s + +$(LIBRARY)(em_cms.o): em_cms.s + $(CC1) em_cms.s + +$(LIBRARY)(em_cmu4.o): em_cmu4.s + $(CC1) em_cmu4.s + +$(LIBRARY)(em_com.o): em_com.s + $(CC1) em_com.s + +$(LIBRARY)(em_csa2.o): em_csa2.s + $(CC1) em_csa2.s + +$(LIBRARY)(em_csa4.o): em_csa4.s + $(CC1) em_csa4.s + +$(LIBRARY)(em_csb2.o): em_csb2.s + $(CC1) em_csb2.s + +$(LIBRARY)(em_csb4.o): em_csb4.s + $(CC1) em_csb4.s + +$(LIBRARY)(em_cuu.o): em_cuu.s + $(CC1) em_cuu.s + +$(LIBRARY)(em_dup.o): em_dup.s + $(CC1) em_dup.s + +$(LIBRARY)(em_dvi4.o): em_dvi4.s + $(CC1) em_dvi4.s + +$(LIBRARY)(em_dvu4.o): em_dvu4.s + $(CC1) em_dvu4.s + +$(LIBRARY)(em_error.o): em_error.s + $(CC1) em_error.s + +$(LIBRARY)(em_exg.o): em_exg.s + $(CC1) em_exg.s + +$(LIBRARY)(em_fat.o): em_fat.s + $(CC1) em_fat.s + +$(LIBRARY)(em_fp8087.o): em_fp8087.s + $(CC1) em_fp8087.s + +$(LIBRARY)(em_gto.o): em_gto.s + $(CC1) em_gto.s + +$(LIBRARY)(em_hol0.o): em_hol0.s + $(CC1) em_hol0.s + +$(LIBRARY)(em_iaar.o): em_iaar.s + $(CC1) em_iaar.s + +$(LIBRARY)(em_ilar.o): em_ilar.s + $(CC1) em_ilar.s + +$(LIBRARY)(em_inn.o): em_inn.s + $(CC1) em_inn.s + +$(LIBRARY)(em_ior.o): em_ior.s + $(CC1) em_ior.s + +$(LIBRARY)(em_isar.o): em_isar.s + $(CC1) em_isar.s + +$(LIBRARY)(em_lar2.o): em_lar2.s + $(CC1) em_lar2.s + +$(LIBRARY)(em_lfr6.o): em_lfr6.s + $(CC1) em_lfr6.s + +$(LIBRARY)(em_lfr8.o): em_lfr8.s + $(CC1) em_lfr8.s + +$(LIBRARY)(em_loi.o): em_loi.s + $(CC1) em_loi.s + +$(LIBRARY)(em_mli4.o): em_mli4.s + $(CC1) em_mli4.s + +$(LIBRARY)(em_mon.o): em_mon.s + $(CC1) em_mon.s + +$(LIBRARY)(em_nop.o): em_nop.s + $(CC1) em_nop.s + +$(LIBRARY)(em_rck.o): em_rck.s + $(CC1) em_rck.s + +$(LIBRARY)(em_ret6.o): em_ret6.s + $(CC1) em_ret6.s + +$(LIBRARY)(em_ret8.o): em_ret8.s + $(CC1) em_ret8.s + +$(LIBRARY)(em_retarea.o): em_retarea.s + $(CC1) em_retarea.s + +$(LIBRARY)(em_return.o): em_return.s + $(CC1) em_return.s + +$(LIBRARY)(em_rmi4.o): em_rmi4.s + $(CC1) em_rmi4.s + +$(LIBRARY)(em_rmu4.o): em_rmu4.s + $(CC1) em_rmu4.s + +$(LIBRARY)(em_sar2.o): em_sar2.s + $(CC1) em_sar2.s + +$(LIBRARY)(em_sbi.o): em_sbi.s + $(CC1) em_sbi.s + +$(LIBRARY)(em_set.o): em_set.s + $(CC1) em_set.s + +$(LIBRARY)(em_stb.o): em_stb.s + $(CC1) em_stb.s + +$(LIBRARY)(em_sti.o): em_sti.s + $(CC1) em_sti.s + +$(LIBRARY)(em_stop.o): em_stop.s + $(CC1) em_stop.s + +$(LIBRARY)(em_trp.o): em_trp.s + $(CC1) em_trp.s + +$(LIBRARY)(em_unknown.o): em_unknown.s + $(CC1) em_unknown.s + +$(LIBRARY)(em_vars.o): em_vars.s + $(CC1) em_vars.s + +$(LIBRARY)(em_xor.o): em_xor.s + $(CC1) em_xor.s diff --git a/lib/i86/em/em_adi.s b/lib/i86/em/em_adi.s new file mode 100755 index 000000000..dc53e4bc3 --- /dev/null +++ b/lib/i86/em/em_adi.s @@ -0,0 +1,24 @@ +.define .adi + + .text +.adi: + pop bx + cmp cx,#2 + jne 1f + pop cx + add ax,cx + jmp (bx) +1: + cmp cx,#4 + jne 9f + pop dx + pop cx + add ax,cx + pop cx + adc dx,cx + push dx + jmp (bx) +9: +.extern .trpilin + push bx + jmp .trpilin diff --git a/lib/i86/em/em_and.s b/lib/i86/em/em_and.s new file mode 100755 index 000000000..166c853d6 --- /dev/null +++ b/lib/i86/em/em_and.s @@ -0,0 +1,19 @@ +.define .and + + ! #bytes in cx + ! save di; it might be a register variable + + .text +.and: + pop bx ! return address + mov dx,di + mov di,sp + add di,cx + sar cx,#1 +1: + pop ax + and ax,(di) + stos + loop 1b + mov di,dx + jmp (bx) diff --git a/lib/i86/em/em_blm.s b/lib/i86/em/em_blm.s new file mode 100755 index 000000000..058a22a99 --- /dev/null +++ b/lib/i86/em/em_blm.s @@ -0,0 +1,15 @@ +.define .blm +.text + + ! cx: count in words +.blm: + mov bx,sp + mov ax,si + mov dx,di + mov di,2(bx) + mov si,4(bx) + rep + mov + mov si,ax + mov di,dx + ret 4 diff --git a/lib/i86/em/em_cii.s b/lib/i86/em/em_cii.s new file mode 100755 index 000000000..2bda938d5 --- /dev/null +++ b/lib/i86/em/em_cii.s @@ -0,0 +1,37 @@ +.define .cii + +.text +.cii: + pop bx ! return address + ! pop cx, dest. size + ! pop dx, src. size + ! ax is first word of source + cmp dx,#1 + jne 2f + cbw + mov dx,#2 +2: + cmp dx,cx + je 8f + cmp dx,#2 + je 1f + cmp dx,#4 + jne 9f + cmp cx,#2 + jne 9f + pop dx +8: + jmp (bx) +1: + cmp cx,#4 + jne 9f + cwd + push dx + jmp (bx) +9: + push ax ! push low source +EILLINS = 18 +.extern .fat + mov ax,#EILLINS + push ax + jmp .fat diff --git a/lib/i86/em/em_cmi4.s b/lib/i86/em/em_cmi4.s new file mode 100755 index 000000000..d09679bb0 --- /dev/null +++ b/lib/i86/em/em_cmi4.s @@ -0,0 +1,27 @@ +.define .cmi4 + +.text +.cmi4: + pop bx ! return address + pop cx + pop dx + pop ax + push si + mov si,sp + xchg bx,2(si) + pop si + cmp bx,dx + jg 1f + jl 2f + cmp ax,cx + ja 1f + je 3f +2: + mov ax,#-1 + ret +3: + xor ax,ax + ret +1: + mov ax,#1 + ret diff --git a/lib/i86/em/em_cms.s b/lib/i86/em/em_cms.s new file mode 100755 index 000000000..150395eae --- /dev/null +++ b/lib/i86/em/em_cms.s @@ -0,0 +1,23 @@ +.define .cms + + ! #bytes in cx + .text +.cms: + pop bx ! return address + mov dx,sp + push si + push di + mov si,dx + add dx,cx + mov di,dx + add dx,cx + sar cx,#1 + repe + cmp + je 1f + inc cx +1: + pop di + pop si + mov sp,dx + jmp (bx) diff --git a/lib/i86/em/em_cmu4.s b/lib/i86/em/em_cmu4.s new file mode 100755 index 000000000..446a45d2d --- /dev/null +++ b/lib/i86/em/em_cmu4.s @@ -0,0 +1,27 @@ +.define .cmu4 + +.text +.cmu4: + pop bx ! return address + pop cx + pop dx + pop ax + push si + mov si,sp + xchg bx,2(si) + pop si + cmp bx,dx + ja 1f + jb 2f + cmp ax,cx + ja 1f + je 3f +2: + mov ax,#-1 + ret +3: + xor ax,ax + ret +1: + mov ax,#1 + ret diff --git a/lib/i86/em/em_com.s b/lib/i86/em/em_com.s new file mode 100755 index 000000000..66a1b66d2 --- /dev/null +++ b/lib/i86/em/em_com.s @@ -0,0 +1,15 @@ +.define .com + + ! #bytes in cx + .text +.com: + mov bx,sp + inc bx + inc bx + sar cx,#1 +1: + not (bx) + inc bx + inc bx + loop 1b + ret diff --git a/lib/i86/em/em_csa2.s b/lib/i86/em/em_csa2.s new file mode 100755 index 000000000..c2a182443 --- /dev/null +++ b/lib/i86/em/em_csa2.s @@ -0,0 +1,26 @@ +.define .csa2 + +.text +.csa2: + ! bx, descriptor address + ! ax, index + mov dx,(bx) ! default + sub ax,2(bx) + cmp ax,4(bx) + ja 1f + sal ax,#1 + add bx,ax + mov bx,6(bx) + test bx,bx + jnz 2f +1: + mov bx,dx + test bx,bx + jnz 2f +ECASE = 20 +.extern .fat + mov ax,#ECASE + push ax + jmp .fat +2: + jmp (bx) diff --git a/lib/i86/em/em_csa4.s b/lib/i86/em/em_csa4.s new file mode 100755 index 000000000..fb78fe015 --- /dev/null +++ b/lib/i86/em/em_csa4.s @@ -0,0 +1,30 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .csa4 + +.sect .text +.csa4: + ! bx, descriptor address + ! ax, dx: index + mov cx,(bx) ! default + sub ax,2(bx) + ! ignore high order word; if non-zero, the + ! case descriptor would not fit anyway + cmp ax,6(bx) + ja 1f +2: + sal ax,#1 + add bx,ax + mov bx,10(bx) + test bx,bx + jnz 2f +1: + mov bx,cx + test bx,bx + jnz 2f +ECASE = 20 +.extern .fat + mov ax,#ECASE + push ax + jmp .fat +2: + jmp (bx) diff --git a/lib/i86/em/em_csb2.s b/lib/i86/em/em_csb2.s new file mode 100755 index 000000000..bc812eb6c --- /dev/null +++ b/lib/i86/em/em_csb2.s @@ -0,0 +1,28 @@ +.define .csb2 + +.text +.csb2: + !bx, descriptor address + !ax, index + mov dx,(bx) + mov cx,2(bx) +1: + add bx,#4 + dec cx + jl 4f + cmp ax,(bx) + jne 1b + mov bx,2(bx) +2: + test bx,bx + jnz 3f +ECASE = 20 +.extern .fat + mov ax,#ECASE + push ax + jmp .fat +3: + jmp (bx) +4: + mov bx,dx + jmp 2b diff --git a/lib/i86/em/em_csb4.s b/lib/i86/em/em_csb4.s new file mode 100755 index 000000000..e60a67f67 --- /dev/null +++ b/lib/i86/em/em_csb4.s @@ -0,0 +1,33 @@ +.sect .text; .sect .rom; .sect .data; .sect .bss +.define .csb4 + +.sect .text +.csb4: + !bx: descriptor address + !ax, dx: index + push (bx) ! default + mov cx,2(bx) ! count (ignore high order word, the descriptor + ! would not fit anyway) +1: + add bx,#6 + dec cx + jl 4f + cmp ax,(bx) + jne 1b + cmp dx,2(bx) + jne 1b + pop bx + mov bx,4(bx) +2: + test bx,bx + jnz 3f +ECASE = 20 +.extern .fat + mov ax,#ECASE + push ax + jmp .fat +3: + jmp (bx) +4: + pop bx + jmp 2b diff --git a/lib/i86/em/em_cuu.s b/lib/i86/em/em_cuu.s new file mode 100755 index 000000000..0cba3fc3b --- /dev/null +++ b/lib/i86/em/em_cuu.s @@ -0,0 +1,36 @@ +.define .ciu +.define .cui +.define .cuu + +.text +.ciu: +.cui: +.cuu: + pop bx ! return address + ! pop cx, dest. size + ! pop dx, source size + ! ax is low word of source + cmp dx,cx + je 8f + cmp dx,#2 + je 1f + cmp dx,#4 + jne 9f + cmp cx,#2 + jne 9f + pop dx +8: + jmp (bx) +1: + cmp cx,#4 + jne 9f + xor dx,dx + push dx + jmp (bx) +9: + push ax ! to help debugging ? +EILLINS = 18 +.extern .fat + mov ax,#EILLINS + push ax + jmp .fat diff --git a/lib/i86/em/em_dup.s b/lib/i86/em/em_dup.s new file mode 100755 index 000000000..533fd5166 --- /dev/null +++ b/lib/i86/em/em_dup.s @@ -0,0 +1,17 @@ +.define .dup + + ! #bytes in cx + .text +.dup: + pop bx ! return address + mov ax,si + mov dx,di + mov si,sp + sub sp,cx + mov di,sp + sar cx,#1 + rep + mov + mov si,ax + mov di,dx + jmp (bx) diff --git a/lib/i86/em/em_dvi4.s b/lib/i86/em/em_dvi4.s new file mode 100755 index 000000000..74bc078c9 --- /dev/null +++ b/lib/i86/em/em_dvi4.s @@ -0,0 +1,90 @@ +.define .dvi4 + +yl=6 +yh=8 +xl=10 +xh=12 + +.text +.dvi4: + push si + push di + mov si,sp ! copy of sp + mov bx,yl(si) + mov ax,yh(si) + cwd + mov di,dx + cmp dx,ax + jne 7f + and dx,dx + jge 1f + neg bx + je 7f +1: + xor dx,dx + mov cx,xl(si) + mov ax,xh(si) + and ax,ax + jge 2f + neg ax + neg cx + sbb ax,dx + not di +2: + div bx + xchg ax,cx + div bx ! cx = high abs(result), ax=low abs(result) +9: + and di,di + jge 1f + neg cx + neg ax + sbb cx,#0 +1: + ! cx is high order result + ! ax is low order result + mov dx,cx + pop di + pop si + ret 8 ! result in ax/dx + +7: + push dx ! sign of y + mov di,ax + xor bx,bx + and di,di + jge 1f + neg di + neg yl(si) + sbb di,bx +1: + mov ax,xl(si) + mov dx,xh(si) + and dx,dx + jge 1f + neg dx + neg ax + sbb dx,bx + not -2(si) +1: + mov cx,#16 +1: + shl ax,#1 + rcl dx,#1 + rcl bx,#1 + cmp di,bx + ja 3f + jb 2f + cmp yl(si),dx + jbe 2f +3: + loop 1b + jmp 1f +2: + sub dx,yl(si) + sbb bx,di + inc ax + loop 1b +1: + pop di ! di=sign of result,ax= result + jmp 9b diff --git a/lib/i86/em/em_dvu4.s b/lib/i86/em/em_dvu4.s new file mode 100755 index 000000000..1e2455358 --- /dev/null +++ b/lib/i86/em/em_dvu4.s @@ -0,0 +1,54 @@ +.define .dvu4 + +yl=6 +yh=8 +xl=10 +xh=12 + +.text +.dvu4: + push si + push di + mov si,sp ! copy of sp + mov bx,yl(si) + mov ax,yh(si) + or ax,ax + jne 7f + xor dx,dx + mov cx,xl(si) + mov ax,xh(si) + div bx + xchg ax,cx + div bx +9: + ! cx is high order result + ! ax is low order result + mov dx,cx + pop di + pop si + ret 8 ! result in ax/dx + +7: + mov di,ax + xor bx,bx + mov ax,xl(si) + mov dx,xh(si) + mov cx,#16 +1: + shl ax,#1 + rcl dx,#1 + rcl bx,#1 + cmp di,bx + ja 3f + jb 2f + cmp yl(si),dx + jbe 2f +3: + loop 1b + jmp 9b +2: + sub dx,yl(si) + sbb bx,di + inc ax + loop 1b + jmp 9b diff --git a/lib/i86/em/em_error.s b/lib/i86/em/em_error.s new file mode 100755 index 000000000..4694ebb4a --- /dev/null +++ b/lib/i86/em/em_error.s @@ -0,0 +1,41 @@ +.define .error +.define .Xtrp + + ! ax is trap number + ! all registers must be saved + ! because return is possible + ! May only be called with error no's <16 +.text +.error: + push bp + push si + push di + push dx + push cx + push bx + push ax + mov cx,ax + mov bx,#1 + sal bx,cl +.extern .ignmask +.extern .trp + test bx,.ignmask + jne 2f + call .trp +2: + pop ax + pop bx + pop cx + pop dx + pop di + pop si + pop bp + ret + +.Xtrp: + cmp ax,#16 + jge 1f + call .error + ret +1: + jmp .trp diff --git a/lib/i86/em/em_exg.s b/lib/i86/em/em_exg.s new file mode 100755 index 000000000..4bd40e155 --- /dev/null +++ b/lib/i86/em/em_exg.s @@ -0,0 +1,21 @@ +.define .exg + + ! #bytes in cx +.text +.exg: + push di + mov sp,di + add di,#4 + mov bx,di + add bx,cx + sar cx,#1 +1: + mov ax,(bx) + xchg ax,(di) + mov (bx),ax + add di,#2 + add bx,#2 + loop 1b +2: + pop di + ret diff --git a/lib/i86/em/em_fat.s b/lib/i86/em/em_fat.s new file mode 100755 index 000000000..4cfe4da15 --- /dev/null +++ b/lib/i86/em/em_fat.s @@ -0,0 +1,9 @@ +.define .fat +.text + +.fat: +.extern .trp +.extern .stop + call .trp + call .stop + ! no return diff --git a/lib/i86/em/em_fp8087.s b/lib/i86/em/em_fp8087.s new file mode 100755 index 000000000..4ac49ea15 --- /dev/null +++ b/lib/i86/em/em_fp8087.s @@ -0,0 +1,615 @@ +.define .adf4, .adf8, .sbf4, .sbf8, .mlf4, .mlf8, .dvf4, .dvf8 +.define .ngf4, .ngf8, .fif4, .fif8, .fef4, .fef8 +.define .cif4, .cif8, .cuf4, .cuf8, .cfi, .cfu, .cff4, .cff8 +.define .cmf4, .cmf8 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! $Header$ + +! Implement interface to floating point package for Intel 8087 + + .sect .rom +one: + .data2 1 +two: + .data2 2 +bigmin: + .data2 0, -32768 + + .sect .text +.adf4: + mov bx,sp + wait + flds 2(bx) + wait + fadds 6(bx) + wait + fstps 6(bx) + wait + ret +.adf8: + mov bx,sp + wait + fldd 2(bx) + wait + faddd 10(bx) + wait + fstpd 10(bx) + wait + ret + +.sbf4: + mov bx,sp + wait + flds 6(bx) + wait + fsubs 2(bx) + wait + fstps 6(bx) + wait + ret + +.sbf8: + mov bx,sp + wait + fldd 10(bx) + wait + fsubd 2(bx) + wait + fstpd 10(bx) + wait + ret + +.mlf4: + mov bx,sp + wait + flds 2(bx) + wait + fmuls 6(bx) + wait + fstps 6(bx) + wait + ret +.mlf8: + mov bx,sp + wait + fldd 2(bx) + wait + fmuld 10(bx) + wait + fstpd 10(bx) + wait + ret + +.dvf4: + mov bx,sp + wait + flds 6(bx) + wait + fdivs 2(bx) + wait + fstps 6(bx) + wait + ret + +.dvf8: + mov bx,sp + wait + fldd 10(bx) + wait + fdivd 2(bx) + wait + fstpd 10(bx) + wait + ret + +.ngf4: + mov bx,sp + wait + flds 2(bx) + wait + fchs + wait + fstps 2(bx) + wait + ret + +.ngf8: + mov bx,sp + wait + fldd 2(bx) + wait + fchs + wait + fstpd 2(bx) + wait + ret + +.fif4: + mov bx,sp + push bx ! make room for FP status word + wait + flds 4(bx) + wait + fmuls 8(bx) ! multiply + wait + fld st ! copy result + wait + ftst ! test sign; handle negative separately + wait + fstsw -2(bx) + wait + mov ax,-2(bx) + sahf ! result of test in condition codes + jb 1f + frndint ! this one rounds (?) + wait + fcom st(1) ! compare with original; if <=, then OK + wait + fstsw -2(bx) + wait + mov ax,-2(bx) + sahf + jbe 2f + fisubs one ! else subtract 1 + wait + jmp 2f +1: ! here, negative case + frndint ! this one rounds (?) + wait + fcom st(1) ! compare with original; if >=, then OK + wait + fstsw -2(bx) + wait + mov ax,-2(bx) + sahf + jae 2f + fiadds one ! else add 1 + wait +2: + fsub st(1),st ! subtract integer part + wait + mov bx,2(bx) + fstps (bx) + wait + fstps 4(bx) + wait + pop bx + ret + +.fif8: + mov bx,sp + push bx ! make room for FP status word + wait + fldd 4(bx) + wait + fmuld 12(bx) ! multiply + wait + fld st ! and copy result + wait + ftst ! test sign; handle negative separately + wait + fstsw -2(bx) + wait + mov ax,-2(bx) + sahf ! result of test in condition codes + jb 1f + frndint ! this one rounds (?) + wait + fcom st(1) ! compare with original; if <=, then OK + wait + fstsw -2(bx) + wait + mov ax,-2(bx) + sahf + jbe 2f + fisubs one ! else subtract 1 + wait + jmp 2f +1: ! here, negative case + frndint ! this one rounds (?) + wait + fcom st(1) ! compare with original; if >=, then OK + wait + fstsw -2(bx) + wait + mov ax,-2(bx) + sahf + jae 2f + fiadds one ! else add 1 + wait +2: + fsub st(1),st ! subtract integer part + mov bx,2(bx) + fstpd (bx) + wait + fstpd 8(bx) + wait + pop bx + ret + +.fef4: + ! this could be simpler, if only the + ! fxtract instruction was emulated properly + mov bx,sp + mov ax,6(bx) + and ax,#077600 + je 1f ! zero exponent + mov cx,#7 + shr ax,cl + sub ax,#126 + mov cx,ax ! exponent in cx + mov ax,6(bx) + and ax,#0100177 + or ax,#0037400 ! load -1 exponent + mov dx,4(bx) + mov bx,2(bx) + mov 4(bx),ax + mov 2(bx),dx + mov (bx),cx + ret +1: ! we get here on zero exp + mov ax,6(bx) + and ax,#0177 + or ax,4(bx) + jne 1f ! zero result + xor ax,ax + mov bx,2(bx) + mov (bx),ax + mov 2(bx),ax + mov 4(bx),ax + ret +1: ! otherwise unnormalized number + mov cx,6(bx) + and cx,#0100177 + mov dx,cx + and cx,#0x8000 + mov ax,#-125 +2: + test dx,#0x80 + jne 1f + dec ax + shl 4(bx),#1 + rcl dx,#1 + or dx,cx + jmp 2b +1: + mov cx,4(bx) + mov bx,2(bx) + mov (bx),ax + mov 2(bx),cx + and dx,#0100177 + or dx,#0037400 ! load -1 exponent + mov 4(bx),dx + ret + +.fef8: + ! this could be simpler, if only the + ! fxtract instruction was emulated properly + mov bx,sp + mov ax,10(bx) + and ax,#077760 + je 1f ! zero exponent + mov cx,#4 + shr ax,cl + sub ax,#1022 + mov cx,ax ! exponent in cx + mov ax,10(bx) + and ax,#0100017 + or ax,#0037740 ! load -1 exponent + push 8(bx) + push 6(bx) + push 4(bx) + mov bx,2(bx) + pop 2(bx) + pop 4(bx) + pop 6(bx) + mov 8(bx),ax + mov (bx),cx + ret +1: ! we get here on zero exp + mov ax,10(bx) + and ax,#017 + or ax,8(bx) + or ax,6(bx) + or ax,4(bx) + jne 1f ! zero result + xor ax,ax + mov bx,2(bx) + mov (bx),ax + mov 2(bx),ax + mov 4(bx),ax + mov 6(bx),ax + mov 8(bx),ax + ret +1: ! otherwise unnormalized number + mov cx,10(bx) + and cx,#0100017 + mov dx,cx + and cx,#0x8000 + mov ax,#-1021 +2: + test dx,#0x10 + jne 1f + dec ax + shl 4(bx),#1 + rcl 6(bx),#1 + rcl 8(bx),#1 + rcl dx,#1 + or dx,cx + jmp 2b +1: + and dx,#0100017 + or dx,#0037740 ! load -1 exponent + mov cx,8(bx) + push 6(bx) + push 4(bx) + mov bx,2(bx) + mov (bx),ax + mov 8(bx),dx + mov 6(bx),cx + pop 2(bx) + pop 4(bx) + ret + +.cif4: + mov bx,sp + cmp 2(bx),#2 + jne 1f + wait + filds 4(bx) + wait + fstps 2(bx) + wait + ret +1: + wait + fildl 4(bx) + wait + fstps 4(bx) + wait + ret + +.cif8: + mov bx,sp + cmp 2(bx),#2 + jne 1f + wait + filds 4(bx) + wait + fstpd 2(bx) + wait + ret +1: + wait + fildl 4(bx) + wait + fstpd 2(bx) + wait + ret + +.cuf4: + mov bx,sp + cmp 2(bx),#2 + jne 1f + mov ax,4(bx) + mov 2(bx),ax + mov 4(bx),#0 + wait + fildl 2(bx) + wait + fstps 2(bx) + wait + ret +1: + wait + fildl 4(bx) + wait + cmp 6(bx),#0 + jge 1f +2: + wait + fisubl bigmin + wait + fisubl bigmin +1: + wait + fstps 4(bx) + wait + ret + +.cuf8: + mov bx,sp + cmp 2(bx),#2 + jne 1f + mov 6(bx),#0 +1: + wait + fildl 4(bx) + wait + cmp 6(bx),#0 + jge 1f +2: + wait + fisubl bigmin + wait + fisubl bigmin +1: + wait + fstpd 2(bx) + wait + ret + +.cfi: + mov bx,sp + push bx + wait + fstcw -2(bx) + wait + mov dx,-2(bx) + or -2(bx),#0xc00 ! truncating mode + wait + fldcw -2(bx) + pop ax + cmp 4(bx),#4 + jne 2f + ! loc 4 loc ? cfi + wait + flds 6(bx) + wait + fistpl 6(bx) + wait + cmp 2(bx),#2 + jne 1f + mov ax,6(bx) +1: + mov 4(bx),dx + wait + fldcw 4(bx) + wait + ret +2: + ! loc 8 loc ? cfi + wait + fldd 6(bx) + wait + fistpl 10(bx) + wait + cmp 2(bx),#2 + jne 1b + mov ax,10(bx) + jmp 1b + +.cfu: + mov bx,sp + push bx + wait + fstcw -2(bx) + wait + mov dx,-2(bx) + and -2(bx),#0xf3ff + or -2(bx),#0x400 ! to -infinity + wait + fldcw -2(bx) + wait + pop ax + cmp 4(bx),#4 + jne 2f + ! loc 4 loc ? cfu + flds 6(bx) + wait + fabs ! ??? + wait + fiaddl bigmin + fistpl 6(bx) + wait + mov ax,8(bx) + sub ax,bigmin+2 + mov 8(bx),ax + cmp 2(bx),#2 + jne 1f + mov ax,6(bx) +1: + mov 4(bx),dx + wait + fldcw 4(bx) + wait + ret +2: + wait + ! loc 8 loc ? cfu + fldd 6(bx) + wait + fabs ! ??? + wait + fiaddl bigmin + fistpl 10(bx) + wait + mov ax,12(bx) + sub ax,bigmin+2 + mov 12(bx),ax + cmp 2(bx),#2 + jne 1b + mov ax,10(bx) + jmp 1b + +.cff4: + mov bx,sp + wait + fldd 2(bx) + wait + fstcw 2(bx) + wait + mov dx,2(bx) + and 2(bx),#0xf3ff ! set to rounding mode + wait + fldcw 2(bx) + wait + fstps 6(bx) + mov 2(bx),dx + wait + fldcw 2(bx) + wait + ret + +.cff8: + mov bx,sp + wait + flds 2(bx) + wait + fstpd 2(bx) + wait + ret + +.cmf4: + mov bx,sp + push bx ! room for 8087 status word + xor cx,cx + wait + flds 6(bx) + wait + flds 2(bx) + wait + fcompp ! compare and pop operands + wait + fstsw -2(bx) + wait + mov ax,-2(bx) + sahf + je 1f + jb 2f + dec cx + jmp 1f +2: + inc cx +1: + mov ax,cx + pop bx + ret + + +.cmf8: + mov bx,sp + push bx ! room for 8087 status word + xor cx,cx + wait + fldd 10(bx) + wait + fldd 2(bx) + wait + fcompp ! compare and pop operands + wait + fstsw -2(bx) + wait + mov ax,-2(bx) + sahf + je 1f + jb 2f + dec cx + jmp 1f +2: + inc cx +1: + mov ax,cx + pop bx + ret diff --git a/lib/i86/em/em_gto.s b/lib/i86/em/em_gto.s new file mode 100755 index 000000000..dc409fef3 --- /dev/null +++ b/lib/i86/em/em_gto.s @@ -0,0 +1,7 @@ +.define .gto +.text + +.gto: + mov bp,4(bx) + mov sp,2(bx) + jmp @(bx) diff --git a/lib/i86/em/em_hol0.s b/lib/i86/em/em_hol0.s new file mode 100755 index 000000000..9ee449755 --- /dev/null +++ b/lib/i86/em/em_hol0.s @@ -0,0 +1,5 @@ +.define hol0 +.data +hol0: + .data2 0, 0 + .data2 0, 0 diff --git a/lib/i86/em/em_iaar.s b/lib/i86/em/em_iaar.s new file mode 100755 index 000000000..9b2da555f --- /dev/null +++ b/lib/i86/em/em_iaar.s @@ -0,0 +1,17 @@ +.define .iaar +.text + +.iaar: + pop cx + pop dx + cmp dx,#2 +.extern .unknown + jne .unknown + pop bx ! descriptor address + pop ax ! index + sub ax,(bx) + mul 4(bx) + pop bx ! array base + add bx,ax + push cx + ret diff --git a/lib/i86/em/em_ilar.s b/lib/i86/em/em_ilar.s new file mode 100755 index 000000000..0898136f4 --- /dev/null +++ b/lib/i86/em/em_ilar.s @@ -0,0 +1,14 @@ +.define .ilar +.text + +.ilar: + pop cx + pop dx +.extern .unknown + cmp dx,#2 + jne .unknown + pop bx ! descriptor address + pop ax ! index + push cx +.extern .lar2 + jmp .lar2 diff --git a/lib/i86/em/em_inn.s b/lib/i86/em/em_inn.s new file mode 100755 index 000000000..12bb0a7cc --- /dev/null +++ b/lib/i86/em/em_inn.s @@ -0,0 +1,31 @@ +.define .inn +.text + + ! #bytes in cx + ! bit # in ax +.inn: + xor dx,dx + mov bx,#8 + div bx + mov bx,sp + add bx,#2 + add bx,ax + cmp ax,cx + jae 1f + movb al,(bx) + mov bx,dx + testb al,bits(bx) + jz 1f + mov ax,#1 + jmp 2f +1: + xor ax,ax +2: + pop bx + add sp,cx + ! ax is result + jmp (bx) + + .data +bits: + .data1 1,2,4,8,16,32,64,128 diff --git a/lib/i86/em/em_ior.s b/lib/i86/em/em_ior.s new file mode 100755 index 000000000..394afcfc0 --- /dev/null +++ b/lib/i86/em/em_ior.s @@ -0,0 +1,17 @@ +.define .ior +.text + + ! #bytes in cx +.ior: + pop bx ! return address + mov dx,di + mov di,sp + add di,cx + sar cx,#1 +1: + pop ax + or ax,(di) + stos + loop 1b + mov di,dx + jmp (bx) diff --git a/lib/i86/em/em_isar.s b/lib/i86/em/em_isar.s new file mode 100755 index 000000000..17c2694f6 --- /dev/null +++ b/lib/i86/em/em_isar.s @@ -0,0 +1,14 @@ +.define .isar +.text + +.isar: + pop cx + pop ax + cmp ax,#2 +.extern .unknown + jne .unknown + pop bx ! descriptor address + pop ax ! index + push cx +.extern .sar2 + jmp .sar2 diff --git a/lib/i86/em/em_lar2.s b/lib/i86/em/em_lar2.s new file mode 100755 index 000000000..728697b8c --- /dev/null +++ b/lib/i86/em/em_lar2.s @@ -0,0 +1,35 @@ +.define .lar2 +.text + +.lar2: + ! bx, descriptor address + ! ax, index + pop cx + pop dx ! base address + push cx + push si + mov si,dx + sub ax,(bx) + mov cx,4(bx) + imul cx + add si,ax + sar cx,#1 + jnb 1f + xorb ah,ah + lodsb + pop si + pop bx + push ax + jmp (bx) +1: + pop dx ! saved si + mov ax,4(bx) + pop bx ! return address + sub sp,ax + mov ax,di ! save di + mov di,sp + rep + mov + mov di,ax + mov si,dx + jmp (bx) diff --git a/lib/i86/em/em_lfr6.s b/lib/i86/em/em_lfr6.s new file mode 100755 index 000000000..820a8158a --- /dev/null +++ b/lib/i86/em/em_lfr6.s @@ -0,0 +1,10 @@ +.define .lfr6 +.text +.extern .retarea + +.lfr6: + pop bx + push .retarea+4 + push .retarea+2 + push .retarea + jmp (bx) diff --git a/lib/i86/em/em_lfr8.s b/lib/i86/em/em_lfr8.s new file mode 100755 index 000000000..510532892 --- /dev/null +++ b/lib/i86/em/em_lfr8.s @@ -0,0 +1,11 @@ +.define .lfr8 +.text +.extern .retarea + +.lfr8: + pop bx + push .retarea+6 + push .retarea+4 + push .retarea+2 + push .retarea + jmp (bx) diff --git a/lib/i86/em/em_loi.s b/lib/i86/em/em_loi.s new file mode 100755 index 000000000..96aa1d820 --- /dev/null +++ b/lib/i86/em/em_loi.s @@ -0,0 +1,38 @@ +.define .loi +.define .los +.text + + ! #bytes in cx + ! address in bx + ! save si/di. they might be register variables +.los: + mov dx,si + mov si,bx + pop bx + mov ax,cx + sar cx,#1 + jnb 1f + xorb ah,ah + lodsb + mov si,dx + push ax + jmp (bx) +1: + sub sp,ax + jmp 1f + +.loi: + ! only called with size > 4 + mov dx,si + mov si,bx + pop bx + sub sp,cx + sar cx,#1 +1: + mov ax,di + mov di,sp + rep + mov + mov si,dx + mov di,ax + jmp (bx) diff --git a/lib/i86/em/em_mli4.s b/lib/i86/em/em_mli4.s new file mode 100755 index 000000000..1dd31c8a6 --- /dev/null +++ b/lib/i86/em/em_mli4.s @@ -0,0 +1,25 @@ +.define .mli4 +.text + +yl=2 +yh=4 + ! x * y + ! xl in ax + ! xh in dx + +.mli4: + mov bx,sp + push dx + mov cx,ax + mul yh(bx) ! xl*yh + pop dx + push ax + mov ax,dx + mul yl(bx) ! xh * yl + pop dx + add dx,ax ! xh*yl+xl*yh + mov ax,cx + mov cx,dx + mul yl(bx) ! xl*yl + add dx,cx + ret 4 diff --git a/lib/i86/em/em_mon.s b/lib/i86/em/em_mon.s new file mode 100755 index 000000000..c73857305 --- /dev/null +++ b/lib/i86/em/em_mon.s @@ -0,0 +1,6 @@ +.define .mon +.text + +.mon: +.extern .stop + call .stop diff --git a/lib/i86/em/em_nop.s b/lib/i86/em/em_nop.s new file mode 100755 index 000000000..6eaaf6486 --- /dev/null +++ b/lib/i86/em/em_nop.s @@ -0,0 +1,4 @@ +.define .nop +.text +.nop: + ret diff --git a/lib/i86/em/em_rck.s b/lib/i86/em/em_rck.s new file mode 100755 index 000000000..e4d3dc150 --- /dev/null +++ b/lib/i86/em/em_rck.s @@ -0,0 +1,19 @@ +.define .rck +.text + + ! descriptor address in bx + ! value in ax, must be left there +.rck: + cmp ax,(bx) + jl 2f + cmp ax,2(bx) + jg 2f + ret +2: + push ax +ERANGE = 1 +.extern .error + mov ax,#ERANGE + call .error + pop ax + ret diff --git a/lib/i86/em/em_ret6.s b/lib/i86/em/em_ret6.s new file mode 100755 index 000000000..a9972ab8e --- /dev/null +++ b/lib/i86/em/em_ret6.s @@ -0,0 +1,10 @@ +.define .ret6 +.text +.extern .retarea + +.ret6: + pop bx + pop .retarea + pop .retarea+2 + pop .retarea+4 + jmp (bx) diff --git a/lib/i86/em/em_ret8.s b/lib/i86/em/em_ret8.s new file mode 100755 index 000000000..96b2e8237 --- /dev/null +++ b/lib/i86/em/em_ret8.s @@ -0,0 +1,11 @@ +.define .ret8 +.text +.extern .retarea + +.ret8: + pop bx + pop .retarea + pop .retarea+2 + pop .retarea+4 + pop .retarea+6 + jmp (bx) diff --git a/lib/i86/em/em_retarea.s b/lib/i86/em/em_retarea.s new file mode 100755 index 000000000..ad70eeb6d --- /dev/null +++ b/lib/i86/em/em_retarea.s @@ -0,0 +1,5 @@ +.define .retarea + +.bss +.retarea: + .zerow 8/2 diff --git a/lib/i86/em/em_return.s b/lib/i86/em/em_return.s new file mode 100755 index 000000000..60365635d --- /dev/null +++ b/lib/i86/em/em_return.s @@ -0,0 +1,17 @@ +.define .sdret, .dsret, .sret, .dret, .cret +.text + +.dsret: + pop di +.sret: + pop si +.cret: + mov sp,bp + pop bp + ret + +.sdret: + pop si +.dret: + pop di + jmp .cret diff --git a/lib/i86/em/em_rmi4.s b/lib/i86/em/em_rmi4.s new file mode 100755 index 000000000..dea9cb5d7 --- /dev/null +++ b/lib/i86/em/em_rmi4.s @@ -0,0 +1,89 @@ +.define .rmi4 +.text + +yl=6 +yh=8 +xl=10 +xh=12 + +.rmi4: + push si + push di + mov si,sp ! copy of sp + mov bx,yl(si) + mov ax,yh(si) + cwd + cmp dx,ax + jne 7f + and dx,dx + jge 1f + neg bx + je 7f +1: + xor dx,dx + mov cx,xl(si) + mov ax,xh(si) + and ax,ax + jge 2f + neg ax + neg cx + sbb ax,dx +2: + div bx + xchg ax,cx + div bx ! dx= result(low), 0=result(high) + xor bx,bx +9: + cmp xh(si),#0 + jge 1f + neg bx + neg dx + sbb bx,#0 +1: + ! bx is high order result + ! dx is low order result + mov ax,dx + mov dx,bx ! result in ax/dx + pop di + pop si + ret 8 + +7: + mov di,ax + xor bx,bx + and di,di + jge 1f + neg di + neg yl(si) + sbb di,bx +1: + mov ax,xl(si) + mov dx,xh(si) + and dx,dx + jge 1f + neg dx + neg ax + sbb dx,bx +1: + mov cx,#16 +1: + shl ax,#1 + rcl dx,#1 + rcl bx,#1 + cmp di,bx + ja 3f + jb 2f + cmp yl(si),dx + jbe 2f +3: + loop 1b + ! dx=result(low), bx=result(high) + jmp 9b +2: + sub dx,yl(si) + sbb bx,di + inc ax + loop 1b +1: + ! dx=result(low), bx=result(high) + jmp 9b diff --git a/lib/i86/em/em_rmu4.s b/lib/i86/em/em_rmu4.s new file mode 100755 index 000000000..3997993fe --- /dev/null +++ b/lib/i86/em/em_rmu4.s @@ -0,0 +1,61 @@ +.define .rmu4 +.text + +yl=6 +yh=8 +xl=10 +xh=12 + +.rmu4: + push si + push di + mov si,sp ! copy of sp + mov bx,yl(si) + mov ax,yh(si) + or ax,ax + jne 7f +1: + xor dx,dx + mov cx,xl(si) + mov ax,xh(si) +2: + div bx + xchg ax,cx + div bx + xor bx,bx +9: + ! bx is high order result + ! dx is low order result + mov ax,dx + mov dx,bx + pop di + pop si + ret 8 ! result in ax/dx + +7: + mov di,ax + xor bx,bx + mov ax,xl(si) + mov dx,xh(si) + mov cx,#16 +1: + shl ax,#1 + rcl dx,#1 + rcl bx,#1 + cmp di,bx + ja 3f + jb 2f + cmp yl(si),dx + jbe 2f +3: + loop 1b + ! dx=result(low), bx=result(high) + jmp 9b +2: + sub dx,yl(si) + sbb bx,di + inc ax + loop 1b +1: + ! dx=result(low), bx=result(high) + jmp 9b diff --git a/lib/i86/em/em_sar2.s b/lib/i86/em/em_sar2.s new file mode 100755 index 000000000..a577ed9da --- /dev/null +++ b/lib/i86/em/em_sar2.s @@ -0,0 +1,33 @@ +.define .sar2 +.text + +.sar2: + ! bx, descriptor address + ! ax, index + pop cx + pop dx ! base address + push cx + xchg di,dx ! di = base address, dx is saved di + sub ax,(bx) + mov cx,4(bx) + push dx + imul cx + pop dx + add di,ax + sar cx,#1 + jnb 1f + pop bx + pop ax + stosb + mov di,dx + jmp (bx) +1: + pop bx + mov ax,si + mov si,sp + rep + mov + mov sp,si + mov si,ax + mov di,dx + jmp (bx) diff --git a/lib/i86/em/em_sbi.s b/lib/i86/em/em_sbi.s new file mode 100755 index 000000000..3fb8a9496 --- /dev/null +++ b/lib/i86/em/em_sbi.s @@ -0,0 +1,27 @@ +.define .sbi +.text + + ! #bytes in cx , top of stack in ax +.sbi: + pop bx ! return subress + cmp cx,#2 + jne 1f + pop cx + sub ax,cx + neg ax + jmp (bx) +1: + cmp cx,#4 + jne 9f + pop dx + pop cx + sub cx,ax + mov ax,cx + pop cx + sbb cx,dx + push cx + jmp (bx) +9: +.extern .trpilin + push bx + jmp .trpilin diff --git a/lib/i86/em/em_set.s b/lib/i86/em/em_set.s new file mode 100755 index 000000000..65a66363a --- /dev/null +++ b/lib/i86/em/em_set.s @@ -0,0 +1,43 @@ +.define .set +.text + + ! #bytes in cx + ! bit # in ax +.set: + pop bx ! return address + xor dx,dx +!ifdef create set + sub sp,cx + push bx + push di + mov bx,sp + xor di,di + sar cx,#1 +1: + mov 4(bx)(di),dx + inc di + inc di + loop 1b +!endif + mov bx,#8 + div bx + cmp ax,di + jae 2f + mov di,dx + movb dl,bits(di) + mov di,sp + add di,ax + orb 4(di),dl + pop di + ret +2: +ESET = 2 +.extern .error + pop di + mov ax,#ESET + call .error + ret + + .data +bits: + .data1 1,2,4,8,16,32,64,128 diff --git a/lib/i86/em/em_stb.s b/lib/i86/em/em_stb.s new file mode 100755 index 000000000..581b91814 --- /dev/null +++ b/lib/i86/em/em_stb.s @@ -0,0 +1,16 @@ +.define ___stb +.text + + ! Routine for copying structs. +___stb: + mov bx,sp + push si + push di + mov cx,2(bx) + mov si,4(bx) + mov di,6(bx) + rep + movb + pop di + pop si + ret diff --git a/lib/i86/em/em_sti.s b/lib/i86/em/em_sti.s new file mode 100755 index 000000000..8c158ab92 --- /dev/null +++ b/lib/i86/em/em_sti.s @@ -0,0 +1,32 @@ +.define .sti +.define .sts +.text + + ! #bytes in cx + ! address in bx + ! save di/si. they might be register variables +.sts: + mov dx,di ! save di + mov di,bx + pop bx ! return address + sar cx,#1 + jnb 1f + pop ax + stosb + mov di,dx + jmp (bx) +.sti: + ! only called with count > 4 + mov dx,di + mov di,bx + pop bx + sar cx,#1 +1: + mov ax,si + mov si,sp + rep + mov + mov sp,si + mov di,dx + mov si,ax + jmp (bx) diff --git a/lib/i86/em/em_stop.s b/lib/i86/em/em_stop.s new file mode 100755 index 000000000..8b96d4ec8 --- /dev/null +++ b/lib/i86/em/em_stop.s @@ -0,0 +1,4 @@ +.define .stop +.text +.stop: + call __exit diff --git a/lib/i86/em/em_trp.s b/lib/i86/em/em_trp.s new file mode 100755 index 000000000..d687818b2 --- /dev/null +++ b/lib/i86/em/em_trp.s @@ -0,0 +1,138 @@ +.define .trpdivz +.define .trpilin +.define .trpcase +.define .trprang +.define .trpset +.define .trpnofp +.define .trpheap +.define .trp + +.bss +.M: .zerow 24/2 + +.text +.extern .trpdivz +.extern .trpilin +.extern .trpcase +.extern .trprang +.extern .trpset +.extern .trpnofp +.extern .trpheap +.extern .trp + +.trpdivz: +mov ax,#6 +mov dx,#.Mdivz +jmp .Trp +.trpilin: +mov ax,#18 +mov dx,#.Milin +jmp .Trp +.trpcase: +mov ax,#20 +mov dx,#.Mcase +jmp .Trp +.trprang: +mov ax,#1 +mov dx,#.Mrang +jmp .Trp +.trpset: +mov ax,#2 +mov dx,#.Mset +jmp .Trp +.trpnofp: +mov ax,#18 +mov dx,#.Mnofp +jmp .Trp +.trpheap: +mov ax,#17 +mov dx,#.Mheap +jmp .Trp + +.Trp: +xor bx,bx +.extern .trppc +xchg bx,.trppc +test bx,bx +jz 2f +push ax +call (bx) +pop ax +ret +2: +mov bx,#22 +push bx +push dx +mov ax,#2 +push ax +call .Write +call __exit + +.trp: +mov dx,ax +cmp dx,#21 +jae 1f +sal dx,#1 +mov bx,#.Mtable +add bx,dx +mov bx,(bx) +test bx,bx +jz 1f +mov dx,bx +jmp 2f +1: +mov bx,#.Mtrp+14 +mov cx,#6 +mov dx,ax +1: +and dx,#7 +add dx,'0' +movb (bx),dl +dec bx +sar dx,#1 +sar dx,#1 +sar dx,#1 +loop 1b +mov dx,#.Mtrp +2: +jmp .Trp + +.Write: +push bp +mov bp,sp +mov .M+2,#4 +mov bx,4(bp) +mov .M+4,bx +mov bx,8(bp) +mov .M+6,bx +mov bx,6(bp) +mov .M+10,bx +mov ax,#.M +push ax +mov ax,#1 +push ax + +mov ax,#1 +mov bx,#.M +mov cx,#3 +int 32 +mov sp,bp +pop bp +ret + + +.data +.Mtable: + .data2 0, .Mrang, .Mset, 0, 0, 0, .Mdivz, 0 + .data2 0, 0, 0, 0, 0, 0, 0, 0 + .data2 0, .Mheap, .Milin, .Milin, .Mcase + +.Mdivz: .asciz "Error: Division by 0 \n" +.Milin: .asciz "Illegal EM instruct'n\n" +.Mcase: .asciz "Err in EM case instr \n" +.Mrang: .asciz "Variable out of range\n" +.Mset: .asciz "Err in EM set instr \n" +.Mnofp: .asciz "Floating pt not impl.\n" +.Mheap: .asciz "Heap overflow \n" + +.Mtrp: .asciz "EM trap 0000000 octal\n" diff --git a/lib/i86/em/em_unknown.s b/lib/i86/em/em_unknown.s new file mode 100755 index 000000000..34f81cdbc --- /dev/null +++ b/lib/i86/em/em_unknown.s @@ -0,0 +1,9 @@ +.define .unknown +.text +.extern .fat +EILLINS = 18 + +.unknown: + mov ax,#EILLINS + push ax + jmp .fat diff --git a/lib/i86/em/em_vars.s b/lib/i86/em/em_vars.s new file mode 100755 index 000000000..9a73a899c --- /dev/null +++ b/lib/i86/em/em_vars.s @@ -0,0 +1,12 @@ +.define .reghp, .limhp, .ignmask, .trppc +.extern .reghp, .limhp, .ignmask, .trppc + +.data +.reghp: + .data2 endbss +.limhp: + .data2 endbss +.ignmask: + .data2 1336 +.trppc: + .data2 0 diff --git a/lib/i86/em/em_xor.s b/lib/i86/em/em_xor.s new file mode 100755 index 000000000..25b7a8de0 --- /dev/null +++ b/lib/i86/em/em_xor.s @@ -0,0 +1,17 @@ +.define .xor +.text + + ! #bytes in cx +.xor: + pop bx ! return address + mov dx,di + mov di,sp + add di,cx + sar cx,#1 +1: + pop ax + xor ax,(di) + stos + loop 1b + mov di,dx + jmp (bx) diff --git a/lib/i86/int64/Makefile b/lib/i86/int64/Makefile new file mode 100755 index 000000000..70db03d67 --- /dev/null +++ b/lib/i86/int64/Makefile @@ -0,0 +1,61 @@ +# Makefile for lib/i386/int64. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -Was-ncc +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../../libc.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(add64.o) \ + $(LIBRARY)(add64u.o) \ + $(LIBRARY)(cmp64.o) \ + $(LIBRARY)(cv64u.o) \ + $(LIBRARY)(cvu64.o) \ + $(LIBRARY)(diff64.o) \ + $(LIBRARY)(div64u.o) \ + $(LIBRARY)(ex64.o) \ + $(LIBRARY)(make64.o) \ + $(LIBRARY)(mul64u.o) \ + $(LIBRARY)(sub64.o) \ + $(LIBRARY)(sub64u.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(add64.o): add64.s + $(CC1) add64.s + +$(LIBRARY)(add64u.o): add64u.s + $(CC1) add64u.s + +$(LIBRARY)(cmp64.o): cmp64.s + $(CC1) cmp64.s + +$(LIBRARY)(cv64u.o): cv64u.s + $(CC1) cv64u.s + +$(LIBRARY)(cvu64.o): cvu64.s + $(CC1) cvu64.s + +$(LIBRARY)(diff64.o): diff64.s + $(CC1) diff64.s + +$(LIBRARY)(div64u.o): div64u.s + $(CC1) div64u.s + +$(LIBRARY)(ex64.o): ex64.s + $(CC1) ex64.s + +$(LIBRARY)(make64.o): make64.s + $(CC1) make64.s + +$(LIBRARY)(mul64u.o): mul64u.s + $(CC1) mul64u.s + +$(LIBRARY)(sub64.o): sub64.s + $(CC1) sub64.s + +$(LIBRARY)(sub64u.o): sub64u.s + $(CC1) sub64u.s diff --git a/lib/i86/int64/add64.s b/lib/i86/int64/add64.s new file mode 100755 index 000000000..f396cfb37 --- /dev/null +++ b/lib/i86/int64/add64.s @@ -0,0 +1,24 @@ +! add64() - 64 bit addition Author: Kees J. Bot +! 24 Dec 1995 +.sect .text +.define _add64 + +_add64: ! u64_t add64(u64_t i, u64_t j); + push bp + mov bp, sp + mov bx, 4(bp) + mov ax, 6(bp) + add ax, 14(bp) + mov (bx), ax + mov ax, 8(bp) + adc ax, 16(bp) + mov 2(bx), ax + mov ax, 10(bp) + adc ax, 18(bp) + mov 4(bx), ax + mov ax, 12(bp) + adc ax, 20(bp) + mov 6(bx), ax + mov ax, bx + pop bp + ret diff --git a/lib/i86/int64/add64u.s b/lib/i86/int64/add64u.s new file mode 100755 index 000000000..6c3a1b379 --- /dev/null +++ b/lib/i86/int64/add64u.s @@ -0,0 +1,30 @@ +! add64u() - unsigned to 64 bit addition Author: Kees J. Bot +! 24 Dec 1995 +.sect .text +.define _add64u, _add64ul + +_add64u: ! u64_t add64u(u64_t i, unsigned j); + push bp + mov bp, sp + xor cx, cx + jmp 0f +_add64ul: ! u64_t add64ul(u64_t i, unsigned long j); + push bp + mov bp, sp + mov cx, 16(bp) +0: mov bx, 4(bp) + mov ax, 6(bp) + add ax, 14(bp) + mov (bx), ax + mov ax, 8(bp) + adc ax, cx + mov 2(bx), ax + mov ax, 10(bp) + adc ax, #0 + mov 4(bx), ax + mov ax, 12(bp) + adc ax, #0 + mov 6(bx), ax + mov ax, bx + pop bp + ret diff --git a/lib/i86/int64/cmp64.s b/lib/i86/int64/cmp64.s new file mode 100755 index 000000000..f3623cc88 --- /dev/null +++ b/lib/i86/int64/cmp64.s @@ -0,0 +1,52 @@ +! cmp64*() - 64 bit compare Author: Kees J. Bot +! 24 Dec 1995 +.sect .text +.define _cmp64, _cmp64u, _cmp64ul + +_cmp64: ! int cmp64(u64_t i, u64_t j); + mov bx, sp +cmp64: xor ax, ax + mov dx, 2(bx) + sub dx, 10(bx) + mov dx, 4(bx) + sbb dx, 12(bx) + mov dx, 6(bx) + sbb dx, 14(bx) + mov dx, 8(bx) + sbb dx, 16(bx) + sbb ax, ax ! ax = - (i < j) + mov dx, 10(bx) + sub dx, 2(bx) + mov dx, 12(bx) + sbb dx, 4(bx) + mov dx, 14(bx) + sbb dx, 6(bx) + mov dx, 16(bx) + sbb dx, 8(bx) + adc ax, #0 ! ax = (i > j) - (i < j) + ret + +_cmp64u: ! int cmp64u(u64_t i, unsigned j); + mov bx, sp + push 16(bx) + mov 16(bx), #0 + push 14(bx) + mov 14(bx), #0 + push 12(bx) + mov 12(bx), #0 + call cmp64 + pop 12(bx) + pop 14(bx) + pop 16(bx) + ret + +_cmp64ul: ! int cmp64ul(u64_t i, unsigned long j); + mov bx, sp + push 14(bx) + mov 14(bx), #0 + push 12(bx) + mov 12(bx), #0 + call cmp64 + pop 12(bx) + pop 14(bx) + ret diff --git a/lib/i86/int64/cv64u.s b/lib/i86/int64/cv64u.s new file mode 100755 index 000000000..2b5e2b317 --- /dev/null +++ b/lib/i86/int64/cv64u.s @@ -0,0 +1,21 @@ +! cv64u() - 64 bit converted to unsigned Author: Kees J. Bot +! 24 Dec 1995 +.sect .text +.define _cv64u, _cv64ul + +_cv64u: ! unsigned cv64u(u64_t i); + mov bx, sp + mov cx, 4(bx) + jmp 0f + +_cv64ul: ! unsigned long cv64ul(u64_t i); + mov bx, sp + xor cx, cx +0: mov ax, 2(bx) + mov dx, 4(bx) + or cx, 6(bx) + or cx, 8(bx) ! return UINT/ULONG_MAX if really big + jz 0f + mov ax, #-1 + mov dx, ax +0: ret diff --git a/lib/i86/int64/cvu64.s b/lib/i86/int64/cvu64.s new file mode 100755 index 000000000..d9a50873e --- /dev/null +++ b/lib/i86/int64/cvu64.s @@ -0,0 +1,21 @@ +! cvu64() - unsigned converted to 64 bit Author: Kees J. Bot +! 24 Dec 1995 +.sect .text +.define _cvu64, _cvul64 + +_cvu64: ! u64_t cvu64(unsigned i); + mov bx, sp + xor dx, dx + jmp 0f + +_cvul64: ! u64_t cvul64(unsigned long i); + mov bx, sp + mov dx, 6(bx) +0: mov ax, 4(bx) + mov bx, 2(bx) + mov (bx), ax + mov 2(bx), dx + mov 4(bx), #0 + mov 6(bx), #0 + mov ax, bx + ret diff --git a/lib/i86/int64/diff64.s b/lib/i86/int64/diff64.s new file mode 100755 index 000000000..2939a0312 --- /dev/null +++ b/lib/i86/int64/diff64.s @@ -0,0 +1,10 @@ +! diff64() - 64 bit subtraction giving unsigned Author: Kees J. Bot +! 24 Dec 1995 +.sect .text +.define _diff64 + +_diff64: ! unsigned diff64(u64_t i, u64_t j); + mov bx, sp + mov ax, 2(bx) + sub ax, 10(bx) + ret diff --git a/lib/i86/int64/div64u.s b/lib/i86/int64/div64u.s new file mode 100755 index 000000000..8ceef4b0d --- /dev/null +++ b/lib/i86/int64/div64u.s @@ -0,0 +1,26 @@ +! div64u() - 64 bit divided by unsigned giving unsigned long +! Author: Kees J. Bot +! 24 Dec 1995 +.sect .text +.define _div64u, _rem64u + +_div64u: ! unsigned long div64u(u64_t i, unsigned j); + mov bx, sp +div64u: xor dx, dx + mov ax, 8(bx) + div 10(bx) + mov ax, 6(bx) + div 10(bx) + mov ax, 4(bx) + div 10(bx) ! division bits 16-31 + mov cx, ax + mov ax, 2(bx) + div 10(bx) ! division bits 0-15 + xchg dx, cx ! division in dx:ax, remainder in cx + ret + +_rem64u: ! unsigned rem64u(u64_t i, unsigned j); + mov bx, sp + call div64u + mov ax, cx + ret diff --git a/lib/i86/int64/ex64.s b/lib/i86/int64/ex64.s new file mode 100755 index 000000000..d0c60ad0e --- /dev/null +++ b/lib/i86/int64/ex64.s @@ -0,0 +1,17 @@ +! ex64*() - extract low or high 32 bits of a 64 bit number +! Author: Kees J. Bot +! 24 Dec 1995 +.sect .text +.define _ex64lo, _ex64hi + +_ex64lo: ! unsigned long ex64lo(u64_t i); + mov bx, sp + mov ax, 2(bx) + mov dx, 4(bx) + ret + +_ex64hi: ! unsigned long ex64hi(u64_t i); + mov bx, sp + mov ax, 6(bx) + mov dx, 8(bx) + ret diff --git a/lib/i86/int64/make64.s b/lib/i86/int64/make64.s new file mode 100755 index 000000000..7a165c66a --- /dev/null +++ b/lib/i86/int64/make64.s @@ -0,0 +1,19 @@ +! make64() - make a 64 bit number from two 32 bit halves +! Author: Kees J. Bot +! 24 Dec 1995 +.sect .text +.define _make64 + +_make64: ! u64_t make64(unsigned long lo, unsigned long hi); + mov bx, sp + mov ax, 4(bx) + mov dx, 6(bx) + mov cx, 8(bx) + push 10(bx) + mov bx, 2(bx) + mov (bx), ax + mov 2(bx), dx + mov 4(bx), cx + pop 6(bx) + mov ax, bx + ret diff --git a/lib/i86/int64/mul64u.s b/lib/i86/int64/mul64u.s new file mode 100755 index 000000000..fa7397b73 --- /dev/null +++ b/lib/i86/int64/mul64u.s @@ -0,0 +1,23 @@ +! mul64u() - unsigned long by unsigned multiply giving 64 bit result +! Author: Kees J. Bot +! 24 Dec 1995 +.sect .text +.define _mul64u + +_mul64u: ! u64_t mul64u(unsigned long i, unsigned j); + push bp + mov bp, sp + mov bx, 4(bp) + mov ax, 6(bp) + mul 10(bp) + mov (bx), ax + mov 2(bx), dx + mov ax, 8(bp) + mul 10(bp) + add 2(bx), ax + adc dx, #0 + mov 4(bx), dx + mov 6(bx), #0 + mov ax, bx + pop bp + ret diff --git a/lib/i86/int64/sub64.s b/lib/i86/int64/sub64.s new file mode 100755 index 000000000..5f596e774 --- /dev/null +++ b/lib/i86/int64/sub64.s @@ -0,0 +1,24 @@ +! sub64() - 64 bit subtraction Author: Kees J. Bot +! 24 Dec 1995 +.sect .text +.define _sub64 + +_sub64: ! u64_t sub64(u64_t i, u64_t j); + push bp + mov bp, sp + mov bx, 4(bp) + mov ax, 6(bp) + sub ax, 14(bp) + mov (bx), ax + mov ax, 8(bp) + sbb ax, 16(bp) + mov 2(bx), ax + mov ax, 10(bp) + sbb ax, 18(bp) + mov 4(bx), ax + mov ax, 12(bp) + sbb ax, 20(bp) + mov 6(bx), ax + mov ax, bx + pop bp + ret diff --git a/lib/i86/int64/sub64u.s b/lib/i86/int64/sub64u.s new file mode 100755 index 000000000..722d5c3bc --- /dev/null +++ b/lib/i86/int64/sub64u.s @@ -0,0 +1,30 @@ +! sub64u() - unsigned from 64 bit subtraction Author: Kees J. Bot +! 24 Dec 1995 +.sect .text +.define _sub64u, _sub64ul + +_sub64u: ! u64_t sub64u(u64_t i, unsigned j); + push bp + mov bp, sp + xor cx, cx + jmp 0f +_sub64ul: ! u64_t sub64ul(u64_t i, unsigned long j); + push bp + mov bp, sp + mov cx, 16(bp) +0: mov bx, 4(bp) + mov ax, 6(bp) + sub ax, 14(bp) + mov (bx), ax + mov ax, 8(bp) + sbb ax, cx + mov 2(bx), ax + mov ax, 10(bp) + sbb ax, #0 + mov 4(bx), ax + mov ax, 12(bp) + sbb ax, #0 + mov 6(bx), ax + mov ax, bx + pop bp + ret diff --git a/lib/i86/misc/Makefile b/lib/i86/misc/Makefile new file mode 100755 index 000000000..8b7f73618 --- /dev/null +++ b/lib/i86/misc/Makefile @@ -0,0 +1,85 @@ +# Makefile for lib/i86/misc. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -Was-ncc +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../../libc.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(alloca.o) \ + $(LIBRARY)(get_bp.o) \ + $(LIBRARY)(getprocessor.o) \ + $(LIBRARY)(hton86.o) \ + $(LIBRARY)(io_inb.o) \ + $(LIBRARY)(io_inl.o) \ + $(LIBRARY)(io_insb.o) \ + $(LIBRARY)(io_insl.o) \ + $(LIBRARY)(io_insw.o) \ + $(LIBRARY)(io_intr.o) \ + $(LIBRARY)(io_inw.o) \ + $(LIBRARY)(io_outb.o) \ + $(LIBRARY)(io_outl.o) \ + $(LIBRARY)(io_outsb.o) \ + $(LIBRARY)(io_outsl.o) \ + $(LIBRARY)(io_outsw.o) \ + $(LIBRARY)(io_outw.o) \ + $(LIBRARY)(oneC_sum.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(alloca.o): alloca.s + $(CC1) alloca.s + +$(LIBRARY)(get_bp.o): get_bp.s + $(CC1) get_bp.s + +$(LIBRARY)(getprocessor.o): getprocessor.s + $(CC1) getprocessor.s + +$(LIBRARY)(hton86.o): hton86.s + $(CC1) hton86.s + +$(LIBRARY)(io_inb.o): io_inb.s + $(CC1) io_inb.s + +$(LIBRARY)(io_inl.o): io_inl.s + $(CC1) io_inl.s + +$(LIBRARY)(io_insb.o): io_insb.s + $(CC1) io_insb.s + +$(LIBRARY)(io_insl.o): io_insl.s + $(CC1) io_insl.s + +$(LIBRARY)(io_insw.o): io_insw.s + $(CC1) io_insw.s + +$(LIBRARY)(io_intr.o): io_intr.s + $(CC1) io_intr.s + +$(LIBRARY)(io_inw.o): io_inw.s + $(CC1) io_inw.s + +$(LIBRARY)(io_outb.o): io_outb.s + $(CC1) io_outb.s + +$(LIBRARY)(io_outl.o): io_outl.s + $(CC1) io_outl.s + +$(LIBRARY)(io_outsb.o): io_outsb.s + $(CC1) io_outsb.s + +$(LIBRARY)(io_outsl.o): io_outsl.s + $(CC1) io_outsl.s + +$(LIBRARY)(io_outsw.o): io_outsw.s + $(CC1) io_outsw.s + +$(LIBRARY)(io_outw.o): io_outw.s + $(CC1) io_outw.s + +$(LIBRARY)(oneC_sum.o): oneC_sum.s + $(CC1) oneC_sum.s diff --git a/lib/i86/misc/alloca.s b/lib/i86/misc/alloca.s new file mode 100755 index 000000000..560f096d1 --- /dev/null +++ b/lib/i86/misc/alloca.s @@ -0,0 +1,23 @@ +# +! alloca() - allocate space on the stack Author: Kees J. Bot +! 26 Jan 1994 + +#if __ACK__ /* BCC can't do alloca(), register saving is wrong. */ + +.text +.define _alloca +_alloca: + pop cx ! Return address + pop ax ! Bytes to allocate + add ax, #2*2+1 ! Add space for two saved register variables + andb al, #0xFE ! Align + mov bx, sp ! Keep current sp + sub sp, ax ! Lower stack + mov ax, sp ! Return value + push 2(bx) ! Push what is probably the saved si + push (bx) ! Saved di + ! Now ACK can still do: + ! pop di; pop si; mov sp, bp; pop bp; ret + push ax ! Dummy argument + jmp (cx) +#endif diff --git a/lib/i86/misc/get_bp.s b/lib/i86/misc/get_bp.s new file mode 100755 index 000000000..51711f767 --- /dev/null +++ b/lib/i86/misc/get_bp.s @@ -0,0 +1,15 @@ +! get_bp.s +! +! return BP in AX +! +! Created: Sep 7, 1992 by Philip Homburg + +.sect .text; .sect .rom; .sect .data; .sect .bss + +.sect .text +.define _get_bp +_get_bp: + mov ax, bp + ret + +! $PchId: get_bp.ack.s,v 1.3 1996/02/23 08:27:48 philip Exp $ diff --git a/lib/i86/misc/getprocessor.s b/lib/i86/misc/getprocessor.s new file mode 100755 index 000000000..ed4c1a1ae --- /dev/null +++ b/lib/i86/misc/getprocessor.s @@ -0,0 +1,92 @@ +! getprocessor() - determine processor type Author: Kees J. Bot +! 26 Jan 1994 + +.text + + o32 = 0x66 ! 32 bit operand size prefix + +! int getprocessor(void); +! Return 86, 186, 286, 386, 486, 586, ... + +.define _getprocessor + +_getprocessor: + push bp + mov bp, sp + push sp ! see if pushed sp == sp + pop ax + cmp ax, sp + jz new_processor + mov cx, #0x0120 ! see if shifts are mod 32 + shlb ch, cl ! zero tells if 86 + mov ax, #86 + jz got_processor + mov ax, #186 + jmp got_processor + +new_processor: ! see if high bits are set in saved IDT + sub sp, #6 ! space for IDT ptr + sidt -6(bp) ! save 3 word IDT ptr + cmpb -1(bp), #0xFF ! top byte of IDT ptr is always FF on 286 + mov ax, #286 + je got_processor + +! 386, 486, 586 + and sp, #0xFFFC ! Align stack to avoid AC fault (needed?) + mov cx, #0x0004 ! Try to flip the AC bit introduced on the 486 + call flip + mov ax, #386 ! 386 if it didn't react to "flipping" + jz got_processor + mov cx, #0x0020 ! Try to flip the ID bit introduced on the 586 + call flip + mov ax, #486 ! 486 if it didn't react + jz got_processor + .data1 o32 + pushf + .data1 o32 + pusha ! Save the world + .data1 o32 + xor ax, ax + inc ax ! eax = 1 + .data1 0x0F, 0xA2 ! CPUID instruction tells the processor type + andb ah, #0x0F ! Extract the family (5, 6, ...) + movb al, ah + movb ah, #100 + mulb ah ! 500, 600, ... + add ax, #86 ! 586, 686, ... + mov bx, sp + mov 7*4(bx), ax ! Pass ax through + .data1 o32 + popa + .data1 o32 + popf + +got_processor: + mov sp, bp + pop bp + ret + +flip: + push bx ! Save bx and realign stack to multiple of 4 + .data1 o32 ! About to operate on a 32 bit object + pushf ! Push eflags + pop ax + pop dx ! dx:ax = eflags + mov bx, dx ! Save original eflags (high word only) + xor dx, cx ! Flip the bit to test + push dx + push ax ! Push modified eflags value + .data1 o32 + popf ! Load modified eflags register + .data1 o32 + pushf + pop ax + pop dx ! Get it again + push bx + push ax + .data1 o32 + popf ! Restore original eflags register + xor dx, bx ! See if the bit changed + test dx, cx + pop bx ! Restore bx + ret diff --git a/lib/i86/misc/hton86.s b/lib/i86/misc/hton86.s new file mode 100755 index 000000000..dd2246b34 --- /dev/null +++ b/lib/i86/misc/hton86.s @@ -0,0 +1,25 @@ +! htonX(), ntohX() - Host to network byte order conversion +! Author: Kees J. Bot +! 7 Jan 1997 +! +! This is a little endian 8086, so we swap bytes to/from the big endian network +! order. The normal <net/hton.h> macros are not used, they give lousy code. + +.text +.define _htons, _ntohs +_htons: +_ntohs: + mov bx, sp + movb ah, 2(bx) ! Load bytes into ax in reverse order + movb al, 3(bx) + ret + +.define _htonl, _ntohl +_htonl: +_ntohl: + mov bx, sp + movb dh, 2(bx) ! Load bytes into dx:ax in reverse order + movb dl, 3(bx) + movb ah, 4(bx) + movb al, 5(bx) + ret diff --git a/lib/i86/misc/io_inb.s b/lib/i86/misc/io_inb.s new file mode 100755 index 000000000..7a213faeb --- /dev/null +++ b/lib/i86/misc/io_inb.s @@ -0,0 +1,14 @@ +! inb() - Input one byte Author: Kees J. Bot +! 18 Mar 1996 +! unsigned inb(U16_t port); + +.sect .text +.define _inb +_inb: + push bp + mov bp, sp + mov dx, 4(bp) ! port + inb dx ! read 1 byte + xorb ah, ah + pop bp + ret diff --git a/lib/i86/misc/io_inl.s b/lib/i86/misc/io_inl.s new file mode 100755 index 000000000..4dab9a33d --- /dev/null +++ b/lib/i86/misc/io_inl.s @@ -0,0 +1,23 @@ +! inl() - Input one dword Author: Kees J. Bot +! 18 Mar 1996 +! unsigned inl(U16_t port); + + o32 = 0x66 + +.sect .text +.define _inl +_inl: + push bp + mov bp, sp + pushf + cli ! eax is not interrupt safe + mov dx, 4(bp) ! port + .data1 o32 + in dx ! read 1 dword + .data1 o32 + push ax ! push eax + pop ax + pop dx ! dx:ax = eax + popf + pop bp + ret diff --git a/lib/i86/misc/io_insb.s b/lib/i86/misc/io_insb.s new file mode 100755 index 000000000..65c55c1db --- /dev/null +++ b/lib/i86/misc/io_insb.s @@ -0,0 +1,21 @@ +! insb() - Input a byte array Author: Kees J. Bot +! 18 Mar 1996 +! void insb(U16_t port, void *buf, size_t count); + +.sect .text +.define _insb +_insb: + push bp + mov bp, sp + cld + push di + mov dx, 4(bp) ! port + mov di, 6(bp) ! buf + mov cx, 8(bp) ! byte count + jcxz 1f +0: inb dx ! input 1 byte + stosb ! write 1 byte + loop 0b ! many times +1: pop di + pop bp + ret diff --git a/lib/i86/misc/io_insl.s b/lib/i86/misc/io_insl.s new file mode 100755 index 000000000..591ceb0f3 --- /dev/null +++ b/lib/i86/misc/io_insl.s @@ -0,0 +1,22 @@ +! insl() - Input a dword array Author: Kees J. Bot +! 18 Mar 1996 +! void insl(U16_t port, void *buf, size_t count); + + o32 = 0x66 + +.sect .text +.define _insl +_insl: + push bp + mov bp, sp + cld + push di + mov dx, 4(bp) ! port + mov di, 6(bp) ! buf + mov cx, 8(bp) ! byte count + shr cx, #2 ! dword count + .data1 o32 + rep ins ! input many dwords + pop di + pop bp + ret diff --git a/lib/i86/misc/io_insw.s b/lib/i86/misc/io_insw.s new file mode 100755 index 000000000..a4e585178 --- /dev/null +++ b/lib/i86/misc/io_insw.s @@ -0,0 +1,19 @@ +! insw() - Input a word array Author: Kees J. Bot +! 18 Mar 1996 +! void insw(U16_t port, void *buf, size_t count); + +.sect .text +.define _insw +_insw: + push bp + mov bp, sp + cld + push di + mov dx, 4(bp) ! port + mov di, 6(bp) ! buf + mov cx, 8(bp) ! byte count + shr cx, #1 ! word count + rep ins ! input many words + pop di + pop bp + ret diff --git a/lib/i86/misc/io_intr.s b/lib/i86/misc/io_intr.s new file mode 100755 index 000000000..f29277089 --- /dev/null +++ b/lib/i86/misc/io_intr.s @@ -0,0 +1,16 @@ +! intr_disable(), intr_enable - Disable/Enable hardware interrupts. +! Author: Kees J. Bot +! 18 Mar 1996 +! void intr_disable(void); +! void intr_enable(void); + +.sect .text +.define _intr_disable +_intr_disable: + cli + ret + +.define _intr_enable +_intr_enable: + sti + ret diff --git a/lib/i86/misc/io_inw.s b/lib/i86/misc/io_inw.s new file mode 100755 index 000000000..5659c93f6 --- /dev/null +++ b/lib/i86/misc/io_inw.s @@ -0,0 +1,13 @@ +! inw() - Input one word Author: Kees J. Bot +! 18 Mar 1996 +! unsigned inw(U16_t port); + +.sect .text +.define _inw +_inw: + push bp + mov bp, sp + mov dx, 4(bp) ! port + in dx ! read 1 word + pop bp + ret diff --git a/lib/i86/misc/io_outb.s b/lib/i86/misc/io_outb.s new file mode 100755 index 000000000..1dd4c413a --- /dev/null +++ b/lib/i86/misc/io_outb.s @@ -0,0 +1,14 @@ +! outb() - Output one byte Author: Kees J. Bot +! 18 Mar 1996 +! void outb(U16_t port, U8_t value); + +.sect .text +.define _outb +_outb: + push bp + mov bp, sp + mov dx, 4(bp) ! port + mov ax, 4+2(bp) ! value + outb dx ! output 1 byte + pop bp + ret diff --git a/lib/i86/misc/io_outl.s b/lib/i86/misc/io_outl.s new file mode 100755 index 000000000..81200eda5 --- /dev/null +++ b/lib/i86/misc/io_outl.s @@ -0,0 +1,21 @@ +! outl() - Output one dword Author: Kees J. Bot +! 18 Mar 1996 +! void outl(U16_t port, u32_t value); + + o32 = 0x66 + +.sect .text +.define _outl +_outl: + push bp + mov bp, sp + pushf + cli ! eax is not interrupt safe + mov dx, 4(bp) ! port + .data1 o32 + mov ax, 4+2(bp) ! value + .data1 o32 + out dx ! output 1 dword + popf + pop bp + ret diff --git a/lib/i86/misc/io_outsb.s b/lib/i86/misc/io_outsb.s new file mode 100755 index 000000000..0fe64d17e --- /dev/null +++ b/lib/i86/misc/io_outsb.s @@ -0,0 +1,21 @@ +! outsb() - Output a byte array Author: Kees J. Bot +! 18 Mar 1996 +! void outsb(U16_t port, void *buf, size_t count); + +.sect .text +.define _outsb +_outsb: + push bp + mov bp, sp + cld + push si + mov dx, 4(bp) ! port + mov si, 6(bp) ! buf + mov cx, 8(bp) ! byte count + jcxz 1f +0: lodsb ! read 1 byte + outb dx ! output 1 byte + loop 0b ! many times +1: pop si + pop bp + ret diff --git a/lib/i86/misc/io_outsl.s b/lib/i86/misc/io_outsl.s new file mode 100755 index 000000000..6e49ffc24 --- /dev/null +++ b/lib/i86/misc/io_outsl.s @@ -0,0 +1,22 @@ +! outsl() - Output a dword array Author: Kees J. Bot +! 18 Mar 1996 +! void outsl(U16_t port, void *buf, size_t count); + + o32 = 0x66 + +.sect .text +.define _outsl +_outsl: + push bp + mov bp, sp + cld + push si + mov dx, 4(bp) ! port + mov si, 6(bp) ! buf + mov cx, 8(bp) ! byte count + shr cx, #2 ! dword count + .data1 o32 + rep outs ! output many dwords + pop si + pop bp + ret diff --git a/lib/i86/misc/io_outsw.s b/lib/i86/misc/io_outsw.s new file mode 100755 index 000000000..5510bfb77 --- /dev/null +++ b/lib/i86/misc/io_outsw.s @@ -0,0 +1,19 @@ +! outsw() - Output a word array Author: Kees J. Bot +! 18 Mar 1996 +! void outsw(U16_t port, void *buf, size_t count); + +.sect .text +.define _outsw +_outsw: + push bp + mov bp, sp + cld + push si + mov dx, 4(bp) ! port + mov si, 6(bp) ! buf + mov cx, 8(bp) ! byte count + shr cx, #1 ! word count + rep outs ! output many words + pop si + pop bp + ret diff --git a/lib/i86/misc/io_outw.s b/lib/i86/misc/io_outw.s new file mode 100755 index 000000000..064429d83 --- /dev/null +++ b/lib/i86/misc/io_outw.s @@ -0,0 +1,14 @@ +! outw() - Output one word Author: Kees J. Bot +! 18 Mar 1996 +! void outw(U16_t port, U16_t value); + +.sect .text +.define _outw +_outw: + push bp + mov bp, sp + mov dx, 4(bp) ! port + mov ax, 4+2(bp) ! value + out dx ! output 1 word + pop bp + ret diff --git a/lib/i86/misc/oneC_sum.s b/lib/i86/misc/oneC_sum.s new file mode 100755 index 000000000..e5a6e4434 --- /dev/null +++ b/lib/i86/misc/oneC_sum.s @@ -0,0 +1,68 @@ +! oneC_sum() - One complement`s checksum Author: Kees J. Bot +! 23 May 1998 +! See RFC 1071, "Computing the Internet checksum" +! See also the C version of this code. + +.text + +.define _oneC_sum + .align 4 +_oneC_sum: + push bp + mov bp, sp + push si + push di + mov ax, 4(bp) ! Checksum of previous block + mov si, 6(bp) ! Data to compute checksum over + mov di, 8(bp) ! Number of bytes + + xor dx, dx + xorb cl, cl +align: test si, #1 ! Is the data aligned? + jz aligned + test di, di + jz 0f + movb dh, (si) ! First unaligned byte in high half of + dec di ! the dx register, i.e. rotate 8 bits +0: inc si + movb cl, #8 ! Number of bits "rotated" + ror ax, cl ! Rotate the checksum likewise +aligned:add ax, dx ! Summate the unaligned byte + adc ax, #0 ! Add carry back in for one`s complement + + jmp add6test + .align 4 +add6: add ax, (si) ! Six times unrolled loop, see below + adc ax, 2(si) + adc ax, 4(si) + adc ax, 6(si) + adc ax, 8(si) + adc ax, 10(si) + adc ax, #0 + add si, #12 +add6test: + sub di, #12 + jae add6 + add di, #12 + + jmp add1test + .align 4 +add1: add ax, (si) ! while ((di -= 2) >= 0) + adc ax, #0 ! ax += *si++; + add si, #2 ! di += 2; +add1test: + sub di, #2 + jae add1 + add di, #2 + + jz done ! Is there an extra byte? + movb dl, (si) ! Load extra byte in word + xorb dh, dh + add ax, dx ! Add in the last bits + adc ax, #0 +done: + rol ax, cl ! Undo the rotation at the beginning + pop di + pop si + pop bp + ret diff --git a/lib/i86/rts/Makefile b/lib/i86/rts/Makefile new file mode 100755 index 000000000..2e9d18701 --- /dev/null +++ b/lib/i86/rts/Makefile @@ -0,0 +1,42 @@ +# Makefile for lib/i86/rts. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -Was-ncc +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../../libc.a + +all: \ + ../../ncrtso.o \ + ../../nm2rtso.o \ + ../../nprtso.o \ + $(LIBRARY) + +../../ncrtso.o: ncrtso.s + $(CC1) -c ncrtso.s + mv ncrtso.o $@ + +../../nm2rtso.o: nm2rtso.s + $(CC1) -c nm2rtso.s + mv nm2rtso.o $@ + +../../nprtso.o: nprtso.s + $(CC1) -c nprtso.s + mv nprtso.o $@ + +OBJECTS = \ + $(LIBRARY)(__sigreturn.o) \ + $(LIBRARY)(_sendrec.o) \ + $(LIBRARY)(brksize.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(__sigreturn.o): __sigreturn.s + $(CC1) __sigreturn.s + +$(LIBRARY)(_sendrec.o): _sendrec.s + $(CC1) _sendrec.s + +$(LIBRARY)(brksize.o): brksize.s + $(CC1) brksize.s diff --git a/lib/i86/rts/__sigreturn.s b/lib/i86/rts/__sigreturn.s new file mode 100755 index 000000000..3578e3a8d --- /dev/null +++ b/lib/i86/rts/__sigreturn.s @@ -0,0 +1,10 @@ +! This routine is the low-level code for returning from signals. +! It calls __sigreturn, which is the normal "system call" routine. +! Both ___sigreturn and __sigreturn are needed. +.sect .text; .sect .rom; .sect .data; .sect .bss +.sect .text +.define ___sigreturn +.extern __sigreturn +___sigreturn: + add sp, #8 + jmp __sigreturn diff --git a/lib/i86/rts/_sendrec.s b/lib/i86/rts/_sendrec.s new file mode 100755 index 000000000..71af81fd3 --- /dev/null +++ b/lib/i86/rts/_sendrec.s @@ -0,0 +1,32 @@ +.define __send, __receive, __sendrec + +! See ../h/com.h for C definitions +SEND = 1 +RECEIVE = 2 +BOTH = 3 +SYSVEC = 32 + +!*========================================================================* +! _send and _receive * +!*========================================================================* +! _send(), _receive(), _sendrec() all save bp, but destroy ax, bx, and cx. +.extern __send, __receive, __sendrec +__send: mov cx,*SEND ! _send(dest, ptr) + jmp L0 + +__receive: + mov cx,*RECEIVE ! _receive(src, ptr) + jmp L0 + +__sendrec: + mov cx,*BOTH ! _sendrec(srcdest, ptr) + jmp L0 + + L0: push bp ! save bp + mov bp,sp ! can't index off sp + mov ax,4(bp) ! ax = dest-src + mov bx,6(bp) ! bx = message pointer + int SYSVEC ! trap to the kernel + pop bp ! restore bp + ret ! return + diff --git a/lib/i86/rts/brksize.s b/lib/i86/rts/brksize.s new file mode 100755 index 000000000..793600bc9 --- /dev/null +++ b/lib/i86/rts/brksize.s @@ -0,0 +1,4 @@ +.define __brksize +.data +.extern endbss, __brksize +__brksize: .data2 endbss diff --git a/lib/i86/rts/ncrtso.s b/lib/i86/rts/ncrtso.s new file mode 100755 index 000000000..4ce989cf9 --- /dev/null +++ b/lib/i86/rts/ncrtso.s @@ -0,0 +1,54 @@ +! This is the C run-time start-off routine. It's job is to take the +! arguments as put on the stack by EXEC, and to parse them and set them up the +! way _main expects them. +! It also initializes _environ when this variable isn't defined by the +! programmer. The detection of whether _environ belong to us is rather +! simplistic. We simply check for some magic value, but there is no other +! way. + +.extern _main, _exit, crtso, __penviron, __penvp +.extern begtext, begdata, begbss, endtext, enddata, endbss +.text +begtext: +crtso: + xor bp, bp ! clear for backtrace of core files + mov bx, sp + mov ax, (bx) ! argc + lea dx, 2(bx) ! argv + lea cx, 4(bx) + add cx, ax + add cx, ax ! envp + + ! Test if environ is in the initialized data area and is set to our + ! magic number. If so then it is not redefined by the user. + mov bx, #_environ + cmp bx, #__edata ! within initialized data? + jae 0f + testb bl, #1 ! aligned? + jnz 0f + cmp (bx), #0x5353 ! is it our environ? + jne 0f + mov __penviron, bx ! _penviron = &environ; +0: mov bx, __penviron + mov (bx), cx ! *_penviron = envp; + + push cx ! push envp + push dx ! push argv + push ax ! push argc + + call _main ! main(argc, argv, envp) + + push ax ! push exit status + call _exit + + hlt ! force a trap if exit fails + +.data +begdata: + .data2 0 ! for sep I&D: *NULL == 0 +__penviron: + .data2 __penvp ! Pointer to environ, or hidden pointer + +.bss +begbss: + .comm __penvp, 2 ! Hidden environment vector diff --git a/lib/i86/rts/nm2rtso.s b/lib/i86/rts/nm2rtso.s new file mode 100755 index 000000000..5f6ad649b --- /dev/null +++ b/lib/i86/rts/nm2rtso.s @@ -0,0 +1,50 @@ +! This is the Modula-2 run-time start-off routine. It's job is to take the +! arguments as put on the stack by EXEC, and to parse them and set them up the +! way _m_a_i_n expects them. + +.extern __m_a_i_n, _exit, m2rtso, hol0, __penvp +.extern begtext, begdata, begbss, endtext, enddata, endbss +.text +begtext: +m2rtso: + xor bp, bp ! clear for backtrace of core files + mov bx, sp + mov ax, (bx) ! argc + lea dx, 2(bx) ! argv + lea cx, 4(bx) + add cx, ax + add cx, ax ! envp + + ! Test if environ is in the initialized data area and is set to our + ! magic number. If so then it is not redefined by the user. + mov bx, #_environ + cmp bx, #__edata ! within initialized data? + jae 0f + testb bl, #1 ! aligned? + jnz 0f + cmp (bx), #0x5353 ! is it our environ? + jne 0f + mov __penviron, bx ! _penviron = &environ; +0: mov bx, __penviron + mov (bx), cx ! *_penviron = envp; + + push cx ! push envp + push dx ! push argv + push ax ! push argc + + call __m_a_i_n ! run Modula-2 program + + push ax ! push exit status + call __exit + + hlt ! force a trap if exit fails + +.data +begdata: + .data2 0 ! for sep I&D: *NULL == 0 +__penviron: + .data2 __penvp ! Pointer to environ, or hidden pointer + +.bss +begbss: + .comm __penvp, 2 ! Hidden environment vector diff --git a/lib/i86/rts/nprtso.s b/lib/i86/rts/nprtso.s new file mode 100755 index 000000000..83c2bb76f --- /dev/null +++ b/lib/i86/rts/nprtso.s @@ -0,0 +1,52 @@ +! This is the Pascal run-time start-off routine. It's job is to take the +! arguments as put on the stack by EXEC, and to parse them and set them up the +! way _m_a_i_n expects them. + +.extern __m_a_i_n, _exit, prtso, hol0, __penvp +.extern begtext, begdata, begbss, endtext, enddata, endbss +.text +begtext: +prtso: + xor bp, bp ! clear for backtrace of core files + mov bx, sp + mov ax, (bx) ! argc + lea dx, 2(bx) ! argv + lea cx, 4(bx) + add cx, ax + add cx, ax ! envp + + ! Test if environ is in the initialized data area and is set to our + ! magic number. If so then it is not redefined by the user. + mov bx, #_environ + cmp bx, #__edata ! within initialized data? + jae 0f + testb bl, #1 ! aligned? + jnz 0f + cmp (bx), #0x5353 ! is it our environ? + jne 0f + mov __penviron, bx ! _penviron = &environ; +0: mov bx, __penviron + mov (bx), cx ! *_penviron = envp; + + push cx ! push envp + push dx ! push argv + push ax ! push argc + + mov .ignmask, #56 + + call __m_a_i_n ! run Pascal program + + push ax ! push exit status + call __exit + + hlt ! force a trap if exit fails + +.data +begdata: + .data2 0 ! for sep I&D: *NULL == 0 +__penviron: + .data2 __penvp ! Pointer to environ, or hidden pointer + +.bss +begbss: + .comm __penvp, 2 ! Hidden environment vector diff --git a/lib/i86/string/Makefile b/lib/i86/string/Makefile new file mode 100755 index 000000000..762105431 --- /dev/null +++ b/lib/i86/string/Makefile @@ -0,0 +1,112 @@ +# Makefile for lib/i86/string. + +CC1 = $(CC) -Was-ncc -c + +LIBRARY = ../../libc.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(_memmove.o) \ + $(LIBRARY)(_strncat.o) \ + $(LIBRARY)(_strncmp.o) \ + $(LIBRARY)(_strncpy.o) \ + $(LIBRARY)(_strnlen.o) \ + $(LIBRARY)(bcmp.o) \ + $(LIBRARY)(bcopy.o) \ + $(LIBRARY)(bzero.o) \ + $(LIBRARY)(index.o) \ + $(LIBRARY)(memchr.o) \ + $(LIBRARY)(memcmp.o) \ + $(LIBRARY)(memcpy.o) \ + $(LIBRARY)(memmove.o) \ + $(LIBRARY)(memset.o) \ + $(LIBRARY)(rindex.o) \ + $(LIBRARY)(strcat.o) \ + $(LIBRARY)(strchr.o) \ + $(LIBRARY)(strcmp.o) \ + $(LIBRARY)(strcpy.o) \ + $(LIBRARY)(strlen.o) \ + $(LIBRARY)(strncat.o) \ + $(LIBRARY)(strncmp.o) \ + $(LIBRARY)(strncpy.o) \ + $(LIBRARY)(strnlen.o) \ + $(LIBRARY)(strrchr.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(_memmove.o): _memmove.s + $(CC1) _memmove.s + +$(LIBRARY)(_strncat.o): _strncat.s + $(CC1) _strncat.s + +$(LIBRARY)(_strncmp.o): _strncmp.s + $(CC1) _strncmp.s + +$(LIBRARY)(_strncpy.o): _strncpy.s + $(CC1) _strncpy.s + +$(LIBRARY)(_strnlen.o): _strnlen.s + $(CC1) _strnlen.s + +$(LIBRARY)(bcmp.o): bcmp.s + $(CC1) bcmp.s + +$(LIBRARY)(bcopy.o): bcopy.s + $(CC1) bcopy.s + +$(LIBRARY)(bzero.o): bzero.s + $(CC1) bzero.s + +$(LIBRARY)(index.o): index.s + $(CC1) index.s + +$(LIBRARY)(memchr.o): memchr.s + $(CC1) memchr.s + +$(LIBRARY)(memcmp.o): memcmp.s + $(CC1) memcmp.s + +$(LIBRARY)(memcpy.o): memcpy.s + $(CC1) memcpy.s + +$(LIBRARY)(memmove.o): memmove.s + $(CC1) memmove.s + +$(LIBRARY)(memset.o): memset.s + $(CC1) memset.s + +$(LIBRARY)(rindex.o): rindex.s + $(CC1) rindex.s + +$(LIBRARY)(strcat.o): strcat.s + $(CC1) strcat.s + +$(LIBRARY)(strchr.o): strchr.s + $(CC1) strchr.s + +$(LIBRARY)(strcmp.o): strcmp.s + $(CC1) strcmp.s + +$(LIBRARY)(strcpy.o): strcpy.s + $(CC1) strcpy.s + +$(LIBRARY)(strlen.o): strlen.s + $(CC1) strlen.s + +$(LIBRARY)(strncat.o): strncat.s + $(CC1) strncat.s + +$(LIBRARY)(strncmp.o): strncmp.s + $(CC1) strncmp.s + +$(LIBRARY)(strncpy.o): strncpy.s + $(CC1) strncpy.s + +$(LIBRARY)(strnlen.o): strnlen.s + $(CC1) strnlen.s + +$(LIBRARY)(strrchr.o): strrchr.s + $(CC1) strrchr.s diff --git a/lib/i86/string/README b/lib/i86/string/README new file mode 100755 index 000000000..e00324a01 --- /dev/null +++ b/lib/i86/string/README @@ -0,0 +1,5 @@ +Notes on i8086 string assembly routines. Author: Kees J. Bot + 27 Jan 1994 + +These routines are simply translations of the 386 code, so all comments +to that code apply here. diff --git a/lib/i86/string/_memmove.s b/lib/i86/string/_memmove.s new file mode 100755 index 000000000..456c2dc57 --- /dev/null +++ b/lib/i86/string/_memmove.s @@ -0,0 +1,50 @@ +! _memmove() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void *_memmove(void *s1, const void *s2, size_t n) +! Copy a chunk of memory. Handle overlap. +! +.sect .text +.define __memmove, __memcpy +__memmove: + push bp + mov bp, sp + push si + push di + mov di, 4(bp) ! String s1 + mov si, 6(bp) ! String s2 + mov cx, 8(bp) ! Length + mov ax, di + sub ax, si + cmp ax, cx + jb downwards ! if (s2 - s1) < n then copy downwards +__memcpy: + cld ! Clear direction bit: upwards + cmp cx, #16 + jb upbyte ! Don't bother being smart with short arrays + mov ax, si + or ax, di + testb al, #1 + jnz upbyte ! Bit 0 set, use byte copy +upword: shr cx, #1 + rep movs ! Copy words + adc cx, cx ! One more byte? +upbyte: + rep movsb ! Copy bytes +done: mov ax, 4(bp) ! Absolutely noone cares about this value + pop di + pop si + pop bp + ret + +! Handle bad overlap by copying downwards, don't bother to do word copies. +downwards: + std ! Set direction bit: downwards + add si, cx + dec si + add di, cx + dec di + rep movsb ! Copy bytes + cld + jmp done diff --git a/lib/i86/string/_strncat.s b/lib/i86/string/_strncat.s new file mode 100755 index 000000000..390fed919 --- /dev/null +++ b/lib/i86/string/_strncat.s @@ -0,0 +1,37 @@ +! _strncat() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *_strncat(char *s1, const char *s2, size_t dx) +! Append string s2 to s1. +! +.sect .text +.define __strncat +__strncat: + push bp + mov bp, sp + push si + push di + mov di, 4(bp) ! String s1 + mov cx, #-1 + xorb al, al ! Null byte + cld + repne scasb ! Look for the zero byte in s1 + dec di ! Back one up (and clear 'Z' flag) + push di ! Save end of s1 + mov di, 6(bp) ! di = string s2 + mov cx, dx ! Maximum count + repne scasb ! Look for the end of s2 + jne no0 + inc cx ! Exclude null byte +no0: sub dx, cx ! Number of bytes in s2 + mov cx, dx + mov si, 6(bp) ! si = string s2 + pop di ! di = end of string s1 + rep movsb ! Copy bytes + stosb ! Add a terminating null + mov ax, 4(bp) ! Return s1 + pop di + pop si + pop bp + ret diff --git a/lib/i86/string/_strncmp.s b/lib/i86/string/_strncmp.s new file mode 100755 index 000000000..ec2ffe975 --- /dev/null +++ b/lib/i86/string/_strncmp.s @@ -0,0 +1,36 @@ +! strncmp() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! int strncmp(const char *s1, const char *s2, size_t cx) +! Compare two strings. +! +.sect .text +.define __strncmp +__strncmp: + push bp + mov bp, sp + push si + push di + xor ax, ax ! Prepare return value + test cx, cx ! Max length is zero? + je equal + mov si, 4(bp) ! si = string s1 + mov di, 6(bp) ! di = string s2 + cld +compare: + cmpsb ! Compare two bytes + jne unequal + cmpb -1(si), #0 ! End of string? + je equal + dec cx ! Length limit reached? + jne compare + jmp equal +unequal: + ja after + sub ax, #2 ! if (s1 < s2) ax -= 2; +after: inc ax ! ax++, now it's -1 or 1 +equal: pop di + pop si + pop bp + ret diff --git a/lib/i86/string/_strncpy.s b/lib/i86/string/_strncpy.s new file mode 100755 index 000000000..aa075915b --- /dev/null +++ b/lib/i86/string/_strncpy.s @@ -0,0 +1,21 @@ +! _strncpy() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *_strncpy(char *s1, const char *s2, size_t cx) +! Copy string s2 to s1. +! +.sect .text +.define __strncpy +__strncpy: + mov di, 6(bp) ! di = string s2 + xorb al, al ! Look for a zero byte + mov dx, cx ! Save maximum count + cld + repne scasb ! Look for end of s2 + sub dx, cx ! Number of bytes in s2 including null + xchg cx, dx + mov si, 6(bp) ! si = string s2 + mov di, 4(bp) ! di = string s1 + rep movsb ! Copy bytes + ret diff --git a/lib/i86/string/_strnlen.s b/lib/i86/string/_strnlen.s new file mode 100755 index 000000000..dd0de1f7f --- /dev/null +++ b/lib/i86/string/_strnlen.s @@ -0,0 +1,26 @@ +! _strnlen() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! size_t _strnlen(const char *s, size_t cx) +! Return the length of a string. +! +.sect .text +.define __strnlen +__strnlen: + push bp + mov bp, sp + push di + mov di, 4(bp) ! di = string + xorb al, al ! Look for a zero byte + mov dx, cx ! Save maximum count + cmpb cl, #1 ! 'Z' bit must be clear if cx = 0 + cld + repne scasb ! Look for zero + jne no0 + inc cx ! Don't count zero byte +no0: mov ax, dx + sub ax, cx ! Compute bytes scanned + pop di + pop bp + ret diff --git a/lib/i86/string/bcmp.s b/lib/i86/string/bcmp.s new file mode 100755 index 000000000..c6be9db23 --- /dev/null +++ b/lib/i86/string/bcmp.s @@ -0,0 +1,28 @@ +! bcmp() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! int bcmp(const void *s1, const void *s2, size_t n) +! Compare two chunks of memory. +! This is a BSD routine that escaped from the kernel. Don't use. +! (Alas it is not without some use, it reports the number of bytes +! after the bytes that are equal. So it can't be simply replaced.) +! +.sect .text +.define _bcmp +_bcmp: + push bp + mov bp, sp + push 8(bp) + push 6(bp) + push 4(bp) + call _memcmp ! Let memcmp do the work + mov sp, bp + test ax, ax + jz equal + sub dx, 4(bp) ! Memcmp was nice enough to leave "si" in dx + dec dx ! Number of bytes that are equal + mov ax, 8(bp) + sub ax, dx ! Number of bytes that are unequal +equal: pop bp + ret diff --git a/lib/i86/string/bcopy.s b/lib/i86/string/bcopy.s new file mode 100755 index 000000000..c63f582e9 --- /dev/null +++ b/lib/i86/string/bcopy.s @@ -0,0 +1,19 @@ +! bcopy() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void bcopy(const void *s1, void *s2, size_t n) +! Copy a chunk of memory. Handle overlap. +! This is a BSD routine that escaped from the kernel. Don't use. +! +.sect .text +.define _bcopy +.extern __memmove +_bcopy: + pop cx + pop ax + pop dx ! Pop return address and arguments + push ax + push dx ! Arguments reversed + push cx + jmp __memmove ! Call the proper routine diff --git a/lib/i86/string/bzero.s b/lib/i86/string/bzero.s new file mode 100755 index 000000000..edc4397f7 --- /dev/null +++ b/lib/i86/string/bzero.s @@ -0,0 +1,21 @@ +! bzero() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void bzero(void *s, size_t n) +! Set a chunk of memory to zero. +! This is a BSD routine that escaped from the kernel. Don't use. +! +.sect .text +.define _bzero +_bzero: + push bp + mov bp, sp + push 6(bp) ! Size + xor ax, ax + push ax ! Zero + push 4(bp) ! String + call _memset ! Call the proper routine + mov sp, bp + pop bp + ret diff --git a/lib/i86/string/index.s b/lib/i86/string/index.s new file mode 100755 index 000000000..f8b60ef39 --- /dev/null +++ b/lib/i86/string/index.s @@ -0,0 +1,12 @@ +! index() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *index(const char *s, int c) +! Look for a character in a string. Has suffered from a hostile +! takeover by strchr(). +! +.sect .text +.define _index +_index: + jmp _strchr diff --git a/lib/i86/string/memchr.s b/lib/i86/string/memchr.s new file mode 100755 index 000000000..462efdbe4 --- /dev/null +++ b/lib/i86/string/memchr.s @@ -0,0 +1,28 @@ +! memchr() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void *memchr(const void *s, int c, size_t n) +! Look for a character in a chunk of memory. +! +.sect .text +.define _memchr +_memchr: + push bp + mov bp, sp + push di + mov di, 4(bp) ! di = string + movb al, 6(bp) ! The character to look for + mov cx, 8(bp) ! Length + cmpb cl, #1 ! 'Z' bit must be clear if cx = 0 + cld + repne scasb + jne failure + lea ax, -1(di) ! Found + pop di + pop bp + ret +failure:xor ax, ax + pop di + pop bp + ret diff --git a/lib/i86/string/memcmp.s b/lib/i86/string/memcmp.s new file mode 100755 index 000000000..223970292 --- /dev/null +++ b/lib/i86/string/memcmp.s @@ -0,0 +1,44 @@ +! memcmp() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! int memcmp(const void *s1, const void *s2, size_t n) +! Compare two chunks of memory. +! +.sect .text +.define _memcmp +_memcmp: + cld + push bp + mov bp, sp + push si + push di + xor ax, ax ! Prepare return value + mov si, 4(bp) ! String s1 + mov di, 6(bp) ! String s2 + mov cx, 8(bp) ! Length + cmp cx, #16 + jb cbyte ! Don't bother being smart with short arrays + mov dx, si + or dx, di + andb dl, #1 + jnz cbyte ! Bit 0 set, use byte compare +cword: sar cx, #1 + adcb dl, dl ! Save carry + repe cmps ! Compare words + mov cx, #2 ! Recompare the last word + sub si, cx + sub di, cx + addb cl, dl ! One more byte? +cbyte: test cx, cx ! Set 'Z' flag if cx = 0 +last: + repe cmpsb ! Look for the first differing byte + je equal + ja after + sub ax, #2 ! if (s1 < s2) ax -= 2; +after: inc ax ! ax++, now it's -1 or 1 +equal: mov dx, si ! For bcmp() to play with + pop di + pop si + pop bp + ret diff --git a/lib/i86/string/memcpy.s b/lib/i86/string/memcpy.s new file mode 100755 index 000000000..a4a9b536a --- /dev/null +++ b/lib/i86/string/memcpy.s @@ -0,0 +1,23 @@ +! memcpy() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void *memcpy(void *s1, const void *s2, size_t n) +! Copy a chunk of memory. +! This routine need not handle overlap, so it does not handle overlap. +! One could simply call __memmove, the cost of the overlap check is +! negligible, but you are dealing with a programmer who believes that +! if anything can go wrong, it should go wrong. +! +.sect .text +.define _memcpy +_memcpy: + push bp + mov bp, sp + push si + push di + mov di, 4(bp) ! String s1 + mov si, 6(bp) ! String s2 + mov cx, 8(bp) ! Length + ! No overlap check here + jmp __memcpy ! Call the part of __memmove that copies up diff --git a/lib/i86/string/memmove.s b/lib/i86/string/memmove.s new file mode 100755 index 000000000..834e083fd --- /dev/null +++ b/lib/i86/string/memmove.s @@ -0,0 +1,11 @@ +! memmove() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void *memmove(void *s1, const void *s2, size_t n) +! Copy a chunk of memory. Handle overlap. +! +.sect .text +.define _memmove +_memmove: + jmp __memmove ! Call common code diff --git a/lib/i86/string/memset.s b/lib/i86/string/memset.s new file mode 100755 index 000000000..a36a31a33 --- /dev/null +++ b/lib/i86/string/memset.s @@ -0,0 +1,31 @@ +! memset() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! void *memset(void *s, int c, size_t n) +! Set a chunk of memory to the same byte value. +! +.sect .text +.define _memset +_memset: + push bp + mov bp, sp + push di + mov di, 4(bp) ! The string + movb al, 6(bp) ! The fill byte + mov cx, 8(bp) ! Length + cld + cmp cx, #16 + jb sbyte ! Don't bother being smart with short arrays + test di, #1 + jnz sbyte ! Bit 0 set, use byte store +sword: movb ah, al ! One byte to two bytes + sar cx, #1 + rep stos ! Store words + adc cx, cx ! One more byte? +sbyte: + rep stosb ! Store bytes +done: mov ax, 4(bp) ! Return some value you have no need for + pop di + pop bp + ret diff --git a/lib/i86/string/rindex.s b/lib/i86/string/rindex.s new file mode 100755 index 000000000..b5f64deab --- /dev/null +++ b/lib/i86/string/rindex.s @@ -0,0 +1,12 @@ +! rindex() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *rindex(const char *s, int c) +! Look for the last occurrence a character in a string. Has suffered +! from a hostile takeover by strrchr(). +! +.sect .text +.define _rindex +_rindex: + jmp _strrchr diff --git a/lib/i86/string/strcat.s b/lib/i86/string/strcat.s new file mode 100755 index 000000000..7d4d7d6ca --- /dev/null +++ b/lib/i86/string/strcat.s @@ -0,0 +1,12 @@ +! strcat() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *strcat(char *s1, const char *s2) +! Append string s2 to s1. +! +.sect .text +.define _strcat +_strcat: + mov dx, #-1 ! Unlimited length + jmp __strncat ! Common code diff --git a/lib/i86/string/strchr.s b/lib/i86/string/strchr.s new file mode 100755 index 000000000..13bde0710 --- /dev/null +++ b/lib/i86/string/strchr.s @@ -0,0 +1,38 @@ +! strchr() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *strchr(const char *s, int c) +! Look for a character in a string. +! +.sect .text +.define _strchr +_strchr: + push bp + mov bp, sp + push di + cld + mov di, 4(bp) ! di = string + mov dx, #16 ! Look at small chunks of the string +next: shl dx, #1 ! Chunks become bigger each time + mov cx, dx + xorb al, al ! Look for the zero at the end + repne scasb + pushf ! Remember the flags + sub cx, dx + neg cx ! Some or all of the chunk + sub di, cx ! Step back + movb al, 6(bp) ! The character to look for + repne scasb + je found + popf ! Did we find the end of string earlier? + jne next ! No, try again + xor ax, ax ! Return NULL + pop di + pop bp + ret +found: pop ax ! Get rid of those flags + lea ax, -1(di) ! Address of byte found + pop di + pop bp + ret diff --git a/lib/i86/string/strcmp.s b/lib/i86/string/strcmp.s new file mode 100755 index 000000000..da026a9c7 --- /dev/null +++ b/lib/i86/string/strcmp.s @@ -0,0 +1,12 @@ +! strcmp() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! int strcmp(const char *s1, const char *s2) +! Compare two strings. +! +.sect .text +.define _strcmp +_strcmp: + mov cx, #-1 ! Unlimited length + jmp __strncmp ! Common code diff --git a/lib/i86/string/strcpy.s b/lib/i86/string/strcpy.s new file mode 100755 index 000000000..0c2e55bbe --- /dev/null +++ b/lib/i86/string/strcpy.s @@ -0,0 +1,21 @@ +! strcpy() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *strcpy(char *s1, const char *s2) +! Copy string s2 to s1. +! +.sect .text +.define _strcpy +_strcpy: + push bp + mov bp, sp + push si + push di + mov cx, #-1 ! Unlimited length + call __strncpy ! Common code + mov ax, 4(bp) ! Return s1 + pop di + pop si + pop bp + ret diff --git a/lib/i86/string/strlen.s b/lib/i86/string/strlen.s new file mode 100755 index 000000000..41cab909f --- /dev/null +++ b/lib/i86/string/strlen.s @@ -0,0 +1,12 @@ +! strlen() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! size_t strlen(const char *s) +! Return the length of a string. +! +.sect .text +.define _strlen +_strlen: + mov cx, #-1 ! Unlimited length + jmp __strnlen ! Common code diff --git a/lib/i86/string/strncat.s b/lib/i86/string/strncat.s new file mode 100755 index 000000000..80afe1e91 --- /dev/null +++ b/lib/i86/string/strncat.s @@ -0,0 +1,13 @@ +! strncat() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! size_t strncat(char *s1, const char *s2, size_t n) +! Append string s2 to s1. +! +.sect .text +.define _strncat +_strncat: + mov bx, sp + mov dx, 6(bx) ! Maximum length + jmp __strncat ! Common code diff --git a/lib/i86/string/strncmp.s b/lib/i86/string/strncmp.s new file mode 100755 index 000000000..089a089ac --- /dev/null +++ b/lib/i86/string/strncmp.s @@ -0,0 +1,13 @@ +! strncmp() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! int strncmp(const char *s1, const char *s2, size_t n) +! Compare two strings. +! +.sect .text +.define _strncmp +_strncmp: + mov bx, sp + mov cx, 6(bx) ! Maximum length + jmp __strncmp ! Common code diff --git a/lib/i86/string/strncpy.s b/lib/i86/string/strncpy.s new file mode 100755 index 000000000..fb1ded00d --- /dev/null +++ b/lib/i86/string/strncpy.s @@ -0,0 +1,23 @@ +! strncpy() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *strncpy(char *s1, const char *s2, size_t n) +! Copy string s2 to s1. +! +.sect .text +.define _strncpy +_strncpy: + push bp + mov bp, sp + push si + push di + mov cx, 8(bp) ! Maximum length + call __strncpy ! Common code + mov cx, dx ! Number of bytes not copied + rep stosb ! strncpy always copies n bytes by null padding + mov ax, 4(bp) ! Return s1 + pop di + pop si + pop bp + ret diff --git a/lib/i86/string/strnlen.s b/lib/i86/string/strnlen.s new file mode 100755 index 000000000..b18748a34 --- /dev/null +++ b/lib/i86/string/strnlen.s @@ -0,0 +1,13 @@ +! strnlen() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! size_t strnlen(const char *s, size_t n) +! Return the length of a string. +! +.sect .text +.define _strnlen +_strnlen: + mov bx, sp + mov cx, 4(bx) ! Maximum length + jmp __strnlen ! Common code diff --git a/lib/i86/string/strrchr.s b/lib/i86/string/strrchr.s new file mode 100755 index 000000000..5daa30df0 --- /dev/null +++ b/lib/i86/string/strrchr.s @@ -0,0 +1,33 @@ +! strrchr() Author: Kees J. Bot +! 27 Jan 1994 +.sect .text; .sect .rom; .sect .data; .sect .bss + +! char *strrchr(const char *s, int c) +! Look for the last occurrence a character in a string. +! +.sect .text +.define _strrchr +_strrchr: + push bp + mov bp, sp + push di + mov di, 4(bp) ! di = string + mov cx, #-1 + xorb al, al + cld + repne scasb ! Look for the end of the string + not cx ! -1 - cx = Length of the string + null + dec di ! Put di back on the zero byte + movb al, 6(bp) ! The character to look for + std ! Downwards search + repne scasb + cld ! Direction bit back to default + jne failure + lea ax, 1(di) ! Found it + pop di + pop bp + ret +failure:xor ax, ax ! Not there + pop di + pop bp + ret diff --git a/lib/ip/Makefile b/lib/ip/Makefile new file mode 100755 index 000000000..4d309d305 --- /dev/null +++ b/lib/ip/Makefile @@ -0,0 +1,151 @@ +# Makefile for lib/ip. +# +# Note: The oneC_sum.c file is not used if there is an assembly equivalent. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -I. -DNDEBUG +CC1 = $(CC) $(CFLAGS) -c +MAKE = exec make -$(MAKEFLAGS) + +LIBRARY = ../libc.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(dhcp_gettag.o) \ + $(LIBRARY)(dhcp_settag.o) \ + $(LIBRARY)(ether_line.o) \ + $(LIBRARY)(ethera2n.o) \ + $(LIBRARY)(ethere2a.o) \ + $(LIBRARY)(etherh2n.o) \ + $(LIBRARY)(ethern2h.o) \ + $(LIBRARY)(getdomain.o) \ + $(LIBRARY)(gethnmadr.o) \ + $(LIBRARY)(gethostent.o) \ + $(LIBRARY)(gethostname.o) \ + $(LIBRARY)(getnetent.o) \ + $(LIBRARY)(getnetbyname.o) \ + $(LIBRARY)(getnetbyaddr.o) \ + $(LIBRARY)(getproto.o) \ + $(LIBRARY)(getprotoent.o) \ + $(LIBRARY)(getservent.o) \ + $(LIBRARY)(getsrvbyname.o) \ + $(LIBRARY)(getsrvbyport.o) \ + $(LIBRARY)(hton.o) \ + $(LIBRARY)(inet_addr.o) \ + $(LIBRARY)(inet_network.o) \ + $(LIBRARY)(inet_ntoa.o) \ + $(LIBRARY)(memcspn.o) \ + $(LIBRARY)(rcmd.o) \ + $(LIBRARY)(res_comp.o) \ + $(LIBRARY)(res_init.o) \ + $(LIBRARY)(res_mkquery.o) \ + $(LIBRARY)(res_query.o) \ + $(LIBRARY)(res_send.o) \ + $(LIBRARY)(ruserok.o) \ + $(LIBRARY)(servxcheck.o) \ + $(LIBRARY)(strcasecmp.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(dhcp_gettag.o): dhcp_gettag.c + $(CC1) dhcp_gettag.c + +$(LIBRARY)(dhcp_settag.o): dhcp_settag.c + $(CC1) dhcp_settag.c + +$(LIBRARY)(ether_line.o): ether_line.c + $(CC1) ether_line.c + +$(LIBRARY)(ethera2n.o): ethera2n.c + $(CC1) ethera2n.c + +$(LIBRARY)(ethere2a.o): ethere2a.c + $(CC1) ethere2a.c + +$(LIBRARY)(etherh2n.o): etherh2n.c + $(CC1) etherh2n.c + +$(LIBRARY)(ethern2h.o): ethern2h.c + $(CC1) ethern2h.c + +$(LIBRARY)(getdomain.o): getdomain.c + $(CC1) getdomain.c + +$(LIBRARY)(gethnmadr.o): gethnmadr.c + $(CC1) gethnmadr.c + +$(LIBRARY)(gethostent.o): gethostent.c + $(CC1) gethostent.c + +$(LIBRARY)(gethostname.o): gethostname.c + $(CC1) gethostname.c + +$(LIBRARY)(getnetent.o): getnetent.c + $(CC1) getnetent.c + +$(LIBRARY)(getnetbyname.o): getnetbyname.c + $(CC1) getnetbyname.c + +$(LIBRARY)(getnetbyaddr.o): getnetbyaddr.c + $(CC1) getnetbyaddr.c + +$(LIBRARY)(getproto.o): getproto.c + $(CC1) getproto.c + +$(LIBRARY)(getprotoent.o): getprotoent.c + $(CC1) getprotoent.c + +$(LIBRARY)(getservent.o): getservent.c + $(CC1) getservent.c + +$(LIBRARY)(getsrvbyname.o): getsrvbyname.c + $(CC1) getsrvbyname.c + +$(LIBRARY)(getsrvbyport.o): getsrvbyport.c + $(CC1) getsrvbyport.c + +$(LIBRARY)(hton.o): hton.c + $(CC1) hton.c + +$(LIBRARY)(inet_addr.o): inet_addr.c + $(CC1) inet_addr.c + +$(LIBRARY)(inet_network.o): inet_network.c + $(CC1) inet_network.c + +$(LIBRARY)(inet_ntoa.o): inet_ntoa.c + $(CC1) inet_ntoa.c + +$(LIBRARY)(memcspn.o): memcspn.c + $(CC1) memcspn.c + +$(LIBRARY)(oneC_sum.o): oneC_sum.c + $(CC1) oneC_sum.c + +$(LIBRARY)(rcmd.o): rcmd.c + $(CC1) rcmd.c + +$(LIBRARY)(res_comp.o): res_comp.c + $(CC1) res_comp.c + +$(LIBRARY)(res_init.o): res_init.c + $(CC1) res_init.c + +$(LIBRARY)(res_mkquery.o): res_mkquery.c + $(CC1) res_mkquery.c + +$(LIBRARY)(res_query.o): res_query.c + $(CC1) res_query.c + +$(LIBRARY)(res_send.o): res_send.c + $(CC1) res_send.c + +$(LIBRARY)(ruserok.o): ruserok.c + $(CC1) ruserok.c + +$(LIBRARY)(servxcheck.o): servxcheck.c + $(CC1) servxcheck.c + +$(LIBRARY)(strcasecmp.o): strcasecmp.c + $(CC1) strcasecmp.c diff --git a/lib/ip/dhcp_gettag.c b/lib/ip/dhcp_gettag.c new file mode 100755 index 000000000..3dcb6e827 --- /dev/null +++ b/lib/ip/dhcp_gettag.c @@ -0,0 +1,55 @@ +/* dhcp_gettag() Author: Kees J. Bot + * 1 Dec 2000 + */ +#define nil ((void*)0) +#include <stddef.h> +#include <string.h> +#include <sys/types.h> +#include <net/hton.h> +#include <net/gen/in.h> +#include <net/gen/dhcp.h> + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) + +int dhcp_gettag(dhcp_t *dp, int searchtag, u8_t **pdata, size_t *plen) +{ + /* Find a tag in the options field, or possibly in the file or sname + * fields. Return true iff found, and return the data and/or length if + * their pointers are non-null. + */ + u8_t *p; + u8_t *optfield[3]; + size_t optlen[3]; + int i, tag, len; + + /* The DHCP magic number must be correct, or no tags. */ + if (dp->magic != DHCP_MAGIC) return 0; + + optfield[0]= dp->options; + optlen[0]= arraysize(dp->options); + optfield[1]= dp->file; + optlen[1]= 0; /* Unknown if used for options yet. */ + optfield[2]= dp->sname; + optlen[2]= 0; + + for (i= 0; i < 3; i++) { + p= optfield[i]; + while (p < optfield[i] + optlen[i]) { + tag= *p++; + if (tag == 255) break; + len= tag == 0 ? 0 : *p++; + if (tag == searchtag) { + if (pdata != nil) *pdata= p; + if (plen != nil) *plen= len; + return 1; + } + if (tag == DHCP_TAG_OVERLOAD) { + /* There are also options in the file or sname field. */ + if (*p & 1) optlen[1]= arraysize(dp->file); + if (*p & 2) optlen[1]= arraysize(dp->sname); + } + p += len; + } + } + return 0; +} diff --git a/lib/ip/dhcp_settag.c b/lib/ip/dhcp_settag.c new file mode 100755 index 000000000..7cba82d50 --- /dev/null +++ b/lib/ip/dhcp_settag.c @@ -0,0 +1,57 @@ +/* dhcp_init(), dhcp_settag() Author: Kees J. Bot + * 1 Dec 2000 + */ +#define nil ((void*)0) +#include <stddef.h> +#include <string.h> +#include <sys/types.h> +#include <net/hton.h> +#include <net/gen/in.h> +#include <net/gen/dhcp.h> + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +void dhcp_init(dhcp_t *dp) +{ + /* Initialize a DHCP packet. */ + memset(dp, 0, offsetof(dhcp_t, magic)); + dp->magic= DHCP_MAGIC; + memset(dp->options, 255, sizeof(dp->options)); +} + +int dhcp_settag(dhcp_t *dp, int tag, void *data, size_t len) +{ + /* Add a tag to a DHCP packet. No padding. Only do the options field. + * (This is Minix, we don't need megabytes of silly bits of data.) + * The length may be zero to delete a tag. + */ + u8_t *p; + int n; + + if (tag <= 0 || tag >= 255) return 0; + + for (p= dp->options; p < arraylimit(dp->options) && *p != 255; p += n) { + n= 1 + 1 + p[1]; + if (*p == tag) { + /* The tag is already there, remove it so it gets replaced. */ + memmove(p, p + n, arraylimit(dp->options) - (p + n)); + memset(arraylimit(dp->options) - n, 255, n); + n= 0; + } + } + + /* Add tag. */ + if (len == 0) { + /* We're merely deleting a tag. */ + } else + if (p + 1 + 1 + len <= arraylimit(dp->options)) { + *p++ = tag; + *p++ = len; + memcpy(p, data, len); + } else { + /* Oops, it didn't fit? Is this really Minix??? */ + return 0; + } + return 1; +} diff --git a/lib/ip/domainname.c b/lib/ip/domainname.c new file mode 100755 index 000000000..c82d81f24 --- /dev/null +++ b/lib/ip/domainname.c @@ -0,0 +1,30 @@ +/* +domainname.c +*/ + +#include <stdio.h> +#include <string.h> +#include <net/netlib.h> + +int getdomainname(domain, size) +char *domain; +size_t size; +{ + FILE *domainfile; + char *line; + + domainfile= fopen("/etc/domainname", "r"); + if (!domainfile) + { + return -1; + } + + line= fgets(domain, size, domainfile); + fclose(domainfile); + if (!line) + return -1; + line= strchr(domain, '\n'); + if (line) + *line= '\0'; + return 0; +} diff --git a/lib/ip/ether.h b/lib/ip/ether.h new file mode 100755 index 000000000..276b95c59 --- /dev/null +++ b/lib/ip/ether.h @@ -0,0 +1,151 @@ +/* $Id$ */ + +/* Interface definitions for ethernet access library */ + +typedef union etheraddr +{ + unsigned char bytes[6]; /* byteorder safe initialization */ + unsigned short shorts[3]; /* force 2-byte alignment */ +} + ether_addr; + +typedef struct etherpacket +{ + ether_addr dest; + ether_addr src; + unsigned char type[2]; /* in network byte order! */ + unsigned short pktlen; /* length of pktbuf ONLY */ + char *pktbuf; +} + ether_packet; + +typedef struct ethervec +{ + ether_addr dest; + ether_addr src; + unsigned char type[2]; /* in network byte order! */ + unsigned short iovcnt; /* number of iovec to use */ + struct iovec *iov; /* ptr to array of iovec */ +} + ether_vec; + +#ifndef __ETHER_BCAST_ADDR__ +extern ether_addr ether_bcast_addr; +#endif + +#ifdef __STDC__ + +int ether_open (char *name, unsigned type, ether_addr * address); + +ether_addr *ether_address (int fd, ether_addr * address); + +ether_addr *ether_intfaddr (char *intf, ether_addr * address); + +char **ether_interfaces (void); + +int ether_write (int fd, ether_packet * packet); + +int ether_writev (int fd, ether_vec * packet); + +int ether_read (int fd, ether_packet * packet); + +int ether_readv (int fd, ether_vec * packet); + +int ether_blocking (int fd, int state); + +int ether_send_self (int fd); + +int ether_mcast_self (int fd); + +int ether_bcast_self (int fd); + +char *ether_ntoa (ether_addr *); + +ether_addr *ether_aton (char *); + +#ifdef __GNUC__ + +/* + * Avoid stupid warnings if structs aren't defined + */ + +typedef struct in_addr *_ether_NoNsEnSe; +typedef struct hostent *_ether_nOnSeNsE; + +#endif + +char *ether_e2a (ether_addr *, char *); + +ether_addr *ether_a2e (char *, ether_addr *); + +struct in_addr *ether_e2ip (ether_addr *, struct in_addr *); + +ether_addr *ether_ip2e (struct in_addr *, ether_addr *); + +char *ether_e2host (ether_addr *, char *); + +ether_addr *ether_host2e (char *, ether_addr *); + +ether_addr *ether_hostent2e (struct hostent *, ether_addr *); + +#else + +int ether_open (); +ether_addr *ether_address (); +ether_addr *ether_intfaddr (); +char **ether_interfaces (); +int ether_write (); +int ether_writev (); +int ether_read (); +int ether_readv (); +int ether_blocking (); +int ether_send_self (); +int ether_mcast_self (); +int ether_bcast_self (); + +char *ether_ntoa (); +ether_addr *ether_aton (); +char *ether_e2a (); +ether_addr *ether_a2e (); +struct in_addr *ether_e2ip (); +ether_addr *ether_ip2e (); +char *ether_e2host (); +ether_addr *ether_host2e (); +ether_addr *ether_hostent2e (); + +#endif + +#undef ether_cmp /* lose def from netinet/if_ether.h */ + +#define ether_cmp(addr1,addr2) \ + ((addr1)->shorts[0] != (addr2)->shorts[0] \ + || (addr1)->shorts[1] != (addr2)->shorts[1] \ + || (addr1)->shorts[2] != (addr2)->shorts[2]) + +#define ETHERSTRLEN 18 /* max length of "xx:xx:xx:xx:xx:xx" */ + +#ifdef NOFILE /* i.e. we have included sys/param.h */ +#ifndef MAXHOSTNAMELEN /* but MAXHOSTNAMELEN still isnt set */ +#define MAXHOSTNAMELEN 64 +#endif +#endif + +/* should be defined in terms of ether_packet struct; need offsetof() macro */ + +#define ETHER_DST 0 +#define ETHER_SRC 6 +#define ETHER_TYPE 12 +#define ETHER_PKT 14 +#define ETHER_MIN 46 +#define ETHER_MAX 1500 + +#define ETHER_MINTYPE 0x5DD /* lowest protocol not valid IEEE802 */ +#define ETHER_MAXTYPE 0xFFFF /* largest possible protocol */ + +#define ETHER_MCAST(addr) (((unsigned char *) (addr))[0] & 0x01) + +#ifdef NT_ALLTYPES +#define ETHER_ALLTYPES NT_ALLTYPES +#else +#define ETHER_ALLTYPES ((unsigned) -1) +#endif diff --git a/lib/ip/ether_line.c b/lib/ip/ether_line.c new file mode 100755 index 000000000..b88fa3403 --- /dev/null +++ b/lib/ip/ether_line.c @@ -0,0 +1,58 @@ +/* +** ETHER_LINE +** +** This routine parses the array pointed to by "line" (which should be +** from a file in the format of /etc/ethers) and returns in "eaddr" the +** ethernet address at the start of the line and the corresponding host +** name in "hostname". It assumes either tabs or spaces separate the +** two. The buffer pointed to by "hostname" must be big enough to hold +** the host name plus a NULL byte. +** The function returns 0 on success and 1 on failure. +** Arguments are assumed sensible. Null pointers will probably cause +** exceptions. +** Author: Gregory J. Sharp, July 1990 +** Adapted to MINIX: Philip Homburg, May 1992 +*/ + +#include <sys/types.h> +#include <ctype.h> +#include <stdlib.h> +#include <net/gen/ether.h> +#include <net/gen/if_ether.h> + +int +ether_line(line, eaddr, hostname) +char * line; +struct ether_addr * eaddr; +char * hostname; +{ + register int i; + register unsigned long val; + +/* skip leading white space */ + while (*line != '\n' && (*line == ' ' || *line == '\t')) + line++; + +/* read the ethernet address */ + for (i = 0; i < 5; i++) + { + val = (unsigned long) strtol(line, &line, 16); + if (val > 255 || *line++ != ':') + return 1; + eaddr->ea_addr[i] = val & 0xff; + } + val = (unsigned long) strtol(line, &line, 16); + if (val > 255 || (*line != ' ' && *line != '\t')) + return 1; + eaddr->ea_addr[i] = val & 0xff; + +/* skip leading white space */ + while (*line != '\n' && (*line == ' ' || *line == '\t')) + line++; + +/* read in the hostname */ + while (!isspace(*line)) + *hostname++ = *line++; + *hostname = '\0'; + return 0; +} diff --git a/lib/ip/ethera2n.c b/lib/ip/ethera2n.c new file mode 100755 index 000000000..35f25d41f --- /dev/null +++ b/lib/ip/ethera2n.c @@ -0,0 +1,42 @@ +/* +ethera2n.c + +Convert an ASCII string with an ethernet address into a struct ether_addr. + +Created: Nov 17, 1992 by Philip Homburg +*/ + +#include <sys/types.h> +#include <stdlib.h> +#include <net/netlib.h> +#include <net/gen/ether.h> +#include <net/gen/if_ether.h> + +struct ether_addr *ether_aton(s) +_CONST char *s; +{ + static struct ether_addr ea; + + int i; + long v; + char *check; + + if (s == NULL) + return NULL; + + for (i=0; i<6; i++) + { + v= strtol(s, &check, 16); + if (v<0 || v>255) + return NULL; + if ((i<5 && check[0] != ':') || (i == 5 && check[0] != '\0')) + return NULL; + ea.ea_addr[i]= v; + s= check+1; + } + return &ea; +} + +/* + * $PchId: ethera2n.c,v 1.3 1996/02/22 21:10:01 philip Exp $ + */ diff --git a/lib/ip/ethere2a.c b/lib/ip/ethere2a.c new file mode 100755 index 000000000..1fb55fcac --- /dev/null +++ b/lib/ip/ethere2a.c @@ -0,0 +1,50 @@ +/* $Id$ */ +/* This file was part of the etherlib package. */ + +#include <stdio.h> + +#ifdef _MINIX +#include <sys/types.h> +#include <stdlib.h> + +#include <net/gen/ether.h> +#include <net/gen/if_ether.h> + +#define ETHERSTRLEN 18 /* max length of "xx:xx:xx:xx:xx:xx" */ +#define ether_addr ether_addr_t +#define bytes ea_addr +char *ether_e2a _ARGS(( ether_addr_t *a, char *e )); +#else +#include "libether.h" +#endif + +char * +ether_e2a (addr, estring) +ether_addr *addr; +char *estring; +{ +#ifdef lint + char *sprintf (); +#endif + if (estring == NULL) + estring = (char *) malloc (ETHERSTRLEN); + + if (estring != NULL) + (void) sprintf (estring, "%x:%x:%x:%x:%x:%x", + addr->bytes[0], addr->bytes[1], addr->bytes[2], + addr->bytes[3], addr->bytes[4], addr->bytes[5]); + return (estring); +} + +#ifndef ETHERDB + +char * +ether_ntoa (addr) +ether_addr *addr; +{ + static char estring[ETHERSTRLEN]; + + return (ether_e2a (addr, estring)); +} + +#endif diff --git a/lib/ip/etherh2n.c b/lib/ip/etherh2n.c new file mode 100755 index 000000000..fa4b188f4 --- /dev/null +++ b/lib/ip/etherh2n.c @@ -0,0 +1,33 @@ +/* +etherh2n.c + +Created: May 20, 1992 by Philip Homburg +*/ + +#include <stdio.h> +#include <string.h> +#include <net/gen/if_ether.h> + +int +ether_hostton(hostname, e) +char *hostname; +struct ether_addr *e; +{ + FILE *etherf; + char b[256], hn[256]; + + etherf= fopen(_PATH_ETHERS, "r"); + if (etherf == NULL) + return 1; + + while(fgets(b, sizeof(b), etherf) != NULL) + { + if (ether_line(b, e, hn) == 0 && strcmp(hn, hostname) == 0) + { + fclose(etherf); + return 0; + } + } + fclose(etherf); + return 1; +} diff --git a/lib/ip/ethern2h.c b/lib/ip/ethern2h.c new file mode 100755 index 000000000..cf4e79171 --- /dev/null +++ b/lib/ip/ethern2h.c @@ -0,0 +1,37 @@ +/* +ethern2h.c + +Created: Nov 12, 1992 by Philip Homburg +*/ + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <net/gen/ether.h> +#include <net/gen/if_ether.h> + +int +ether_ntohost(hostname, e) +char *hostname; +struct ether_addr *e; +{ + FILE *etherf; + char b[256]; + struct ether_addr e_tmp; + + etherf= fopen(_PATH_ETHERS, "r"); + if (etherf == NULL) + return 1; + + while(fgets(b, sizeof(b), etherf) != NULL) + { + if (ether_line(b, &e_tmp, hostname) == 0 && + memcmp(&e_tmp, e, sizeof(e_tmp)) == 0) + { + fclose(etherf); + return 0; + } + } + fclose(etherf); + return 1; +} diff --git a/lib/ip/getdomain.c b/lib/ip/getdomain.c new file mode 100755 index 000000000..190d88277 --- /dev/null +++ b/lib/ip/getdomain.c @@ -0,0 +1,23 @@ +/* getdomainname() Author: Kees J. Bot + * 2 Dec 1994 + */ +#define nil 0 +#include <sys/types.h> +#include <sys/utsname.h> +#include <unistd.h> +#include <string.h> + +int getdomainname(char *domain, size_t size) +{ + char nodename[256]; + char *dot; + + if (gethostname(nodename, sizeof(nodename)) < 0) + return -1; + nodename[sizeof(nodename)-1]= 0; + if ((dot= strchr(nodename, '.')) == nil) dot= "."; + + strncpy(domain, dot+1, size); + if (size > 0) domain[size-1]= 0; + return 0; +} diff --git a/lib/ip/gethnmadr.c b/lib/ip/gethnmadr.c new file mode 100755 index 000000000..3f79d1b0c --- /dev/null +++ b/lib/ip/gethnmadr.c @@ -0,0 +1,338 @@ +/* + * Copyright (c) 1985, 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)gethostnamadr.c 6.41 (Berkeley) 6/1/90"; +#endif /* LIBC_SCCS and not lint */ + +#ifdef _MINIX +#include <sys/types.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> + +#include <net/hton.h> +#include <net/gen/nameser.h> +#include <net/gen/netdb.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/resolv.h> +#include <net/gen/socket.h> +#else +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <ctype.h> +#include <netdb.h> +#include <stdio.h> +#include <errno.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <resolv.h> +#endif /* AMOEABA */ + +#define MAXALIASES 35 +#define MAXADDRS 35 + +static char *h_addr_ptrs[MAXADDRS + 1]; + +#ifdef _MINIX +struct in_addr +{ + ipaddr_t s_addr; +}; +typedef u32_t u_long; +typedef u16_t u_short; +typedef u8_t u_char; +union querybuf; + +extern int dn_skipname _ARGS(( const u_char *comp_dn, const u_char *eom )); +#define getshort _getshort +static struct hostent *getanswer _ARGS(( union querybuf *answer, int anslen, + int iquery )); +#define bcmp memcmp +#define bcopy(s, d, l) memcpy(d, s, l) +#endif /* _MINIX */ + +static struct hostent host; +static char *host_aliases[MAXALIASES]; +static char hostbuf[BUFSIZ+1]; +static struct in_addr host_addr; + +#ifndef _MINIX +char *strpbrk(); +#endif /* !_MINIX */ + +#if PACKETSZ > 1024 +#define MAXPACKET PACKETSZ +#else +#define MAXPACKET 1024 +#endif + +typedef union querybuf +{ + dns_hdr_t hdr; + u_char buf[MAXPACKET]; +} querybuf_t; + +typedef union align { + long al; + char ac; +} align_t; + +static struct hostent * +getanswer(answer, anslen, iquery) + querybuf_t *answer; + int anslen; + int iquery; +{ + register dns_hdr_t *hp; + register u_char *cp; + register int n; + u_char *eom; + char *bp, **ap; + int type, class, buflen, ancount, qdcount; + int haveanswer, getclass = C_ANY; + char **hap; + + eom = answer->buf + anslen; + /* + * find first satisfactory answer + */ + hp = &answer->hdr; + ancount = ntohs(hp->dh_ancount); + qdcount = ntohs(hp->dh_qdcount); + bp = hostbuf; + buflen = sizeof(hostbuf); + cp = answer->buf + sizeof(dns_hdr_t); + if (qdcount) { + if (iquery) { + if ((n = dn_expand((u_char *)answer->buf, eom, + cp, (u_char *)bp, buflen)) < 0) { + h_errno = NO_RECOVERY; + return ((struct hostent *) NULL); + } + cp += n + QFIXEDSZ; + host.h_name = bp; + n = strlen(bp) + 1; + bp += n; + buflen -= n; + } else + cp += dn_skipname(cp, eom) + QFIXEDSZ; + while (--qdcount > 0) + cp += dn_skipname(cp, eom) + QFIXEDSZ; + } else if (iquery) { + if (hp->dh_flag1 & DHF_AA) + h_errno = HOST_NOT_FOUND; + else + h_errno = TRY_AGAIN; + return ((struct hostent *) NULL); + } + ap = host_aliases; + *ap = NULL; + host.h_aliases = host_aliases; + hap = h_addr_ptrs; + *hap = NULL; +#if BSD >= 43 || defined(h_addr) /* new-style hostent structure */ + host.h_addr_list = h_addr_ptrs; +#endif + haveanswer = 0; + while (--ancount >= 0 && cp < eom) { + if ((n = dn_expand((u_char *)answer->buf, eom, cp, (u_char *)bp, + buflen)) < 0) + break; + cp += n; + type = getshort(cp); + cp += sizeof(u_short); + class = getshort(cp); + cp += sizeof(u_short) + sizeof(u_long); + n = getshort(cp); + cp += sizeof(u_short); + if (type == T_CNAME) { + cp += n; + if (ap >= &host_aliases[MAXALIASES-1]) + continue; + *ap++ = bp; + n = strlen(bp) + 1; + bp += n; + buflen -= n; + continue; + } + if (iquery && type == T_PTR) { + if ((n = dn_expand((u8_t *)answer->buf, eom, + cp, (u8_t *)bp, buflen)) < 0) { + cp += n; + continue; + } + cp += n; + host.h_name = bp; + return(&host); + } + if (iquery || type != T_A) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("unexpected answer type %d, size %d\n", + type, n); +#endif + cp += n; + continue; + } + if (haveanswer) { + if (n != host.h_length) { + cp += n; + continue; + } + if (class != getclass) { + cp += n; + continue; + } + } else { + host.h_length = n; + getclass = class; + host.h_addrtype = (class == C_IN) ? AF_INET : AF_UNSPEC; + if (!iquery) { + host.h_name = bp; + bp += strlen(bp) + 1; + } + } + + bp += (size_t)(sizeof(align_t) - + ((u_long)bp % sizeof(align_t))); + + if (bp + n >= &hostbuf[sizeof(hostbuf)]) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("size (%d) too big\n", n); +#endif + break; + } + bcopy(cp, *hap++ = bp, n); + bp +=n; + cp += n; + haveanswer++; + } + if (haveanswer) { + *ap = NULL; +#if BSD >= 43 || defined(h_addr) /* new-style hostent structure */ + *hap = NULL; +#else + host.h_addr = h_addr_ptrs[0]; +#endif + return (&host); + } else { + h_errno = TRY_AGAIN; + return ((struct hostent *) NULL); + } +} + +struct hostent * +gethostbyname(name) + _CONST char *name; +{ + querybuf_t buf; + register _CONST char *cp; + int n; + + /* + * disallow names consisting only of digits/dots, unless + * they end in a dot. + */ + if (isdigit(name[0])) + for (cp = name;; ++cp) { + if (!*cp) { + if (*--cp == '.') + break; + /* + * All-numeric, no dot at the end. + * Fake up a hostent as if we'd actually + * done a lookup. What if someone types + * 255.255.255.255? The test below will + * succeed spuriously... ??? + */ + if ((host_addr.s_addr = inet_addr(name)) == -1) { + h_errno = HOST_NOT_FOUND; + return((struct hostent *) NULL); + } + host.h_name = (char *) name; + host.h_aliases = host_aliases; + host_aliases[0] = NULL; + host.h_addrtype = AF_INET; + host.h_length = sizeof(u_long); + h_addr_ptrs[0] = (char *)&host_addr; + h_addr_ptrs[1] = (char *)0; +#if BSD >= 43 || defined(h_addr) /* new-style hostent structure */ + host.h_addr_list = h_addr_ptrs; +#else + host.h_addr = h_addr_ptrs[0]; +#endif + return (&host); + } + if (!isdigit(*cp) && *cp != '.') + break; + } + + if ((n = res_search((char*)name, C_IN, T_A, buf.buf, sizeof(buf))) < 0) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("res_search failed\n"); +#endif + return ((struct hostent *) NULL); + } + return (getanswer(&buf, n, 0)); +} + +struct hostent * +gethostbyaddr(addr, len, type) + const char *addr; + int len, type; +{ + int n; + querybuf_t buf; + register struct hostent *hp; + char qbuf[MAXDNAME]; + + if (type != AF_INET) + return ((struct hostent *) NULL); + (void)sprintf(qbuf, "%u.%u.%u.%u.in-addr.arpa", + ((unsigned)addr[3] & 0xff), + ((unsigned)addr[2] & 0xff), + ((unsigned)addr[1] & 0xff), + ((unsigned)addr[0] & 0xff)); + n = res_query(qbuf, C_IN, T_PTR, (u8_t *)&buf, sizeof(buf)); + if (n < 0) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("res_query failed\n"); +#endif + return ((struct hostent *) NULL); + } + hp = getanswer(&buf, n, 1); + if (hp == NULL) + return ((struct hostent *) NULL); + hp->h_addrtype = type; + hp->h_length = len; + h_addr_ptrs[0] = (char *)&host_addr; + h_addr_ptrs[1] = (char *)0; + host_addr = *(struct in_addr *)addr; +#if BSD < 43 && !defined(h_addr) /* new-style hostent structure */ + hp->h_addr = h_addr_ptrs[0]; +#endif + return(hp); +} diff --git a/lib/ip/gethostent.c b/lib/ip/gethostent.c new file mode 100755 index 000000000..1a9aed20d --- /dev/null +++ b/lib/ip/gethostent.c @@ -0,0 +1,168 @@ +/* gethostent() - Interface to /etc/hosts Author: Kees J. Bot + * 31 May 1999 + */ + +/* Prefix the functions defined here with underscores to distinguish them + * from the newer replacements in the resolver library. + */ +#define sethostent _sethostent +#define endhostent _endhostent +#define gethostent _gethostent +#define gethostbyname _gethostbyname +#define gethostbyaddr _gethostbyaddr + +#define nil 0 +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <net/gen/netdb.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/socket.h> + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) +#define isspace(c) ((unsigned) (c) <= ' ') + +static char HOSTS[]= _PATH_HOSTS; +static char *hosts= HOSTS; /* Current hosts file. */ +static FILE *hfp; /* Open hosts file. */ + +void sethostent(int stayopen) +/* Start search. (Same as ending it.) */ +{ + endhostent(); +} + +void endhostent(void) +/* End search and reinitialize. */ +{ + if (hfp != nil) { + fclose(hfp); + hfp= nil; + } + hosts= _PATH_HOSTS; +} + +struct hostent *gethostent(void) +/* Return the next entry from the hosts files. */ +{ + static char line[256]; /* One line in a hosts file. */ + static ipaddr_t addr; /* IP address found first on the line. */ + static char *names[16]; /* Pointers to the words on the line. */ + static char *addrs[2]= { /* List of IP addresses (just one.) */ + (char *) &addr, + nil, + }; + static struct hostent host = { + nil, /* h_name, will set to names[1]. */ + names + 2, /* h_aliases, the rest of the names. */ + AF_INET, /* h_addrtype */ + sizeof(ipaddr_t), /* Size of an address in the address list. */ + addrs, /* List of IP addresses. */ + }; + static char nexthosts[128]; /* Next hosts file to include. */ + char *lp, **np; + int c; + + for (;;) { + if (hfp == nil) { + /* No hosts file open, try to open the next one. */ + if (hosts == 0) return nil; + if ((hfp= fopen(hosts, "r")) == nil) { hosts= nil; continue; } + } + + /* Read a line. */ + lp= line; + while ((c= getc(hfp)) != EOF && c != '\n') { + if (lp < arraylimit(line)) *lp++= c; + } + + /* EOF? Then close and prepare for reading the next file. */ + if (c == EOF) { + fclose(hfp); + hfp= nil; + hosts= nil; + continue; + } + + if (lp == arraylimit(line)) continue; + *lp= 0; + + /* Break the line up in words. */ + np= names; + lp= line; + for (;;) { + while (isspace(*lp) && *lp != 0) lp++; + if (*lp == 0 || *lp == '#') break; + if (np == arraylimit(names)) break; + *np++= lp; + while (!isspace(*lp) && *lp != 0) lp++; + if (*lp == 0) break; + *lp++= 0; + } + + if (np == arraylimit(names)) continue; + *np= nil; + + /* Special "include file" directive. */ + if (np == names + 2 && strcmp(names[0], "include") == 0) { + fclose(hfp); + hfp= nil; + hosts= nil; + if (strlen(names[1]) < sizeof(nexthosts)) { + strcpy(nexthosts, names[1]); + hosts= nexthosts; + } + continue; + } + + /* At least two words, the first of which is an IP address. */ + if (np < names + 2) continue; + if (!inet_aton((char *) names[0], &addr)) continue; + host.h_name= (char *) names[1]; + + return &host; + } +} + +/* Rest kept in reserve, we probably never need 'em. */ +#if XXX +struct hostent *gethostbyname(const char *name) +{ + struct hostent *he; + char **pa; + char alias[256]; + char *domain; + + sethostent(0); + while ((he= gethostent()) != nil) { + if (strcasecmp(he->h_name, name) == 0) goto found; + + domain= strchr(he->h_name, '.'); + for (pa= he->h_aliases; *pa != nil; pa++) { + strcpy(alias, *pa); + if (domain != nil && strchr(alias, '.') == nil) { + strcat(alias, domain); + } + if (strcasecmp(alias, name) == 0) goto found; + } + } + found: + endhostent(); + return he; +} + +struct hostent *gethostbyaddr(const char *addr, int len, int type) +{ + struct hostent *he; + + sethostent(0); + while ((he= gethostent()) != nil) { + if (he->h_name[0] == '%') continue; + if (type == AF_INET && memcmp(he->h_addr, addr, len) == 0) break; + } + endhostent(); + return he; +} +#endif diff --git a/lib/ip/gethostname.c b/lib/ip/gethostname.c new file mode 100755 index 000000000..74cb13588 --- /dev/null +++ b/lib/ip/gethostname.c @@ -0,0 +1,27 @@ +/* gethostname(2) system call emulation */ + +#include <sys/types.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <net/gen/netdb.h> + +#define HOSTNAME_FILE "/etc/hostname.file" + +int gethostname(char *buf, size_t len) +{ + int fd; + int r; + char *nl; + + if ((fd= open(HOSTNAME_FILE, O_RDONLY)) < 0) return -1; + + r= read(fd, buf, len); + close(fd); + if (r == -1) return -1; + + buf[len-1]= '\0'; + if ((nl= strchr(buf, '\n')) != NULL) *nl= '\0'; + return 0; +} diff --git a/lib/ip/getnetbyaddr.c b/lib/ip/getnetbyaddr.c new file mode 100755 index 000000000..3ddb1009a --- /dev/null +++ b/lib/ip/getnetbyaddr.c @@ -0,0 +1,56 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getnetbyaddr.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include <net/gen/netdb.h> + +extern int _net_stayopen; + +struct netent * +getnetbyaddr(net, type) + register long net; + register int type; +{ + register struct netent *p; + + setnetent(_net_stayopen); + while (p = getnetent()) + if (p->n_addrtype == type && p->n_net == net) + break; + if (!_net_stayopen) + endnetent(); + return (p); +} diff --git a/lib/ip/getnetbyname.c b/lib/ip/getnetbyname.c new file mode 100755 index 000000000..c8e92c654 --- /dev/null +++ b/lib/ip/getnetbyname.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getnetbyname.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include <string.h> +#include <net/gen/netdb.h> + +extern int _net_stayopen; + +struct netent * +getnetbyname(name) + register const char *name; +{ + register struct netent *p; + register char **cp; + + setnetent(_net_stayopen); + while (p = getnetent()) { + if (strcmp(p->n_name, name) == 0) + break; + for (cp = p->n_aliases; *cp != 0; cp++) + if (strcmp(*cp, name) == 0) + goto found; + } +found: + if (!_net_stayopen) + endnetent(); + return (p); +} diff --git a/lib/ip/getnetent.c b/lib/ip/getnetent.c new file mode 100755 index 000000000..22a8faf30 --- /dev/null +++ b/lib/ip/getnetent.c @@ -0,0 +1,121 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getnetent.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#include <net/gen/in.h> +#include <net/gen/netdb.h> +#include <net/gen/inet.h> +#include <net/gen/socket.h> + +#define MAXALIASES 35 + +static FILE *netf; +static char line[BUFSIZ+1]; +static struct netent net; +static char *net_aliases[MAXALIASES]; +int _net_stayopen; + +void +setnetent(f) + int f; +{ + if (netf == NULL) + netf = fopen(_PATH_NETWORKS, "r" ); + else + rewind(netf); + _net_stayopen |= f; +} + +void +endnetent() +{ + if (netf) { + fclose(netf); + netf = NULL; + } + _net_stayopen = 0; +} + +struct netent * +getnetent() +{ + char *p; + register char *cp, **q; + + if (netf == NULL && (netf = fopen(_PATH_NETWORKS, "r" )) == NULL) + return (NULL); +again: + p = fgets(line, BUFSIZ, netf); + if (p == NULL) + return (NULL); + if (*p == '#') + goto again; + cp = strpbrk(p, "#\n"); + if (cp == NULL) + goto again; + *cp = '\0'; + net.n_name = p; + cp = strpbrk(p, " \t"); + if (cp == NULL) + goto again; + *cp++ = '\0'; + while (*cp == ' ' || *cp == '\t') + cp++; + p = strpbrk(cp, " \t"); + if (p != NULL) + *p++ = '\0'; + net.n_net = inet_network(cp); + net.n_addrtype = AF_INET; + q = net.n_aliases = net_aliases; + if (p != NULL) + cp = p; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &net_aliases[MAXALIASES - 1]) + *q++ = cp; + cp = strpbrk(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + } + *q = NULL; + return (&net); +} diff --git a/lib/ip/getproto.c b/lib/ip/getproto.c new file mode 100755 index 000000000..34bf8ae06 --- /dev/null +++ b/lib/ip/getproto.c @@ -0,0 +1,46 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getproto.c 5.6 (Berkeley) 6/1/90"; +#endif /* LIBC_SCCS and not lint */ + +#include <stddef.h> + +#ifdef _MINIX +#include <ansi.h> +#include <net/gen/netdb.h> +#endif + +extern int _proto_stayopen; + +struct protoent * +getprotobynumber(proto) + register int proto; +{ + register struct protoent *p; + + setprotoent(_proto_stayopen); + while (p = getprotoent()) + if (p->p_proto == proto) + break; + if (!_proto_stayopen) + endprotoent(); + return (p); +} diff --git a/lib/ip/getprotoent.c b/lib/ip/getprotoent.c new file mode 100755 index 000000000..8c5671688 --- /dev/null +++ b/lib/ip/getprotoent.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getprotoent.c 5.7 (Berkeley) 6/1/90"; +#endif /* LIBC_SCCS and not lint */ + +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> + +#ifdef _MINIX +#include <net/gen/netdb.h> + +static char *any _ARGS(( char *cp, char *match )); +#endif + +#define MAXALIASES 35 + +static FILE *protof = NULL; +static char line[BUFSIZ+1]; +static struct protoent proto; +static char *proto_aliases[MAXALIASES]; +int _proto_stayopen; + +void +setprotoent(f) + int f; +{ + if (protof == NULL) + protof = fopen(_PATH_PROTOCOLS, "r" ); + else + rewind(protof); + _proto_stayopen |= f; +} + +void +endprotoent() +{ + if (protof) { + fclose(protof); + protof = NULL; + } + _proto_stayopen = 0; +} + +struct protoent * +getprotoent() +{ + char *p; + register char *cp, **q; + + if (protof == NULL && (protof = fopen(_PATH_PROTOCOLS, "r" )) == NULL) + return (NULL); +again: + if ((p = fgets(line, BUFSIZ, protof)) == NULL) + return (NULL); + if (*p == '#') + goto again; + cp = any(p, "#\n"); + if (cp == NULL) + goto again; + *cp = '\0'; + proto.p_name = p; + cp = any(p, " \t"); + if (cp == NULL) + goto again; + *cp++ = '\0'; + while (*cp == ' ' || *cp == '\t') + cp++; + p = any(cp, " \t"); + if (p != NULL) + *p++ = '\0'; + proto.p_proto = atoi(cp); + q = proto.p_aliases = proto_aliases; + if (p != NULL) { + cp = p; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &proto_aliases[MAXALIASES - 1]) + *q++ = cp; + cp = any(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + } + } + *q = NULL; + return (&proto); +} + +static char * +any(cp, match) + register char *cp; + char *match; +{ + register char *mp, c; + + while (c = *cp) { + for (mp = match; *mp; mp++) + if (*mp == c) + return (cp); + cp++; + } + return ((char *)0); +} diff --git a/lib/ip/getservent.c b/lib/ip/getservent.c new file mode 100755 index 000000000..9a8ce398f --- /dev/null +++ b/lib/ip/getservent.c @@ -0,0 +1,126 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getservent.c 5.8 (Berkeley) 6/1/90"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> + +#include <net/hton.h> +#include <net/gen/netdb.h> + +#define MAXALIASES 35 + +static FILE *servf = NULL; +static char line[BUFSIZ+1]; +static struct servent serv; +static char *serv_aliases[MAXALIASES]; +int _serv_stayopen; + +static char *any _ARGS(( char *cp, char *match )); + +void +setservent(f) + int f; +{ + if (servf == NULL) + servf = fopen(_PATH_SERVICES, "r" ); + else + rewind(servf); + _serv_stayopen |= f; +} + +void +endservent() +{ + if (servf) { + fclose(servf); + servf = NULL; + } + _serv_stayopen = 0; +} + +struct servent * +getservent() +{ + char *p; + register char *cp, **q; + + if (servf == NULL && (servf = fopen(_PATH_SERVICES, "r" )) == NULL) + return (NULL); +again: + if ((p = fgets(line, BUFSIZ, servf)) == NULL) + return (NULL); + if (*p == '#') + goto again; + cp = any(p, "#\n"); + if (cp == NULL) + goto again; + *cp = '\0'; + serv.s_name = p; + p = any(p, " \t"); + if (p == NULL) + goto again; + *p++ = '\0'; + while (*p == ' ' || *p == '\t') + p++; + cp = any(p, ",/"); + if (cp == NULL) + goto again; + *cp++ = '\0'; + serv.s_port = htons((u16_t)atoi(p)); + serv.s_proto = cp; + q = serv.s_aliases = serv_aliases; + cp = any(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + while (cp && *cp) { + if (*cp == ' ' || *cp == '\t') { + cp++; + continue; + } + if (q < &serv_aliases[MAXALIASES - 1]) + *q++ = cp; + cp = any(cp, " \t"); + if (cp != NULL) + *cp++ = '\0'; + } + *q = NULL; + return (&serv); +} + +static char * +any(cp, match) + register char *cp; + char *match; +{ + register char *mp, c; + + while (c = *cp) { + for (mp = match; *mp; mp++) + if (*mp == c) + return (cp); + cp++; + } + return ((char *)0); +} diff --git a/lib/ip/getsrvbyname.c b/lib/ip/getsrvbyname.c new file mode 100755 index 000000000..1af2e3457 --- /dev/null +++ b/lib/ip/getsrvbyname.c @@ -0,0 +1,52 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getservbyname.c 5.6 (Berkeley) 6/1/90"; +#endif /* LIBC_SCCS and not lint */ + +#include <string.h> + +#include <net/gen/netdb.h> + +extern int _serv_stayopen; + +struct servent * +getservbyname(name, proto) + const char *name, *proto; +{ + register struct servent *p; + register char **cp; + + setservent(_serv_stayopen); + while (p = getservent()) { + if (strcmp(name, p->s_name) == 0) + goto gotname; + for (cp = p->s_aliases; *cp; cp++) + if (strcmp(name, *cp) == 0) + goto gotname; + continue; +gotname: + if (proto == 0 || strcmp(p->s_proto, proto) == 0) + break; + } + if (!_serv_stayopen) + endservent(); + return (p); +} diff --git a/lib/ip/getsrvbyport.c b/lib/ip/getsrvbyport.c new file mode 100755 index 000000000..066b3592d --- /dev/null +++ b/lib/ip/getsrvbyport.c @@ -0,0 +1,50 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)getservbyport.c 5.6 (Berkeley) 6/1/90"; +#endif /* LIBC_SCCS and not lint */ + +#include <stddef.h> +#include <string.h> + +#ifdef _MINIX +#include <net/gen/netdb.h> +#endif + +extern int _serv_stayopen; + +struct servent * +getservbyport(port, proto) + int port; + const char *proto; +{ + register struct servent *p; + + setservent(_serv_stayopen); + while (p = getservent()) { + if (p->s_port != port) + continue; + if (proto == 0 || strcmp(p->s_proto, proto) == 0) + break; + } + if (!_serv_stayopen) + endservent(); + return (p); +} diff --git a/lib/ip/hton.c b/lib/ip/hton.c new file mode 100755 index 000000000..69e3dbd57 --- /dev/null +++ b/lib/ip/hton.c @@ -0,0 +1,10 @@ +/* +hton.c +*/ + +#include <sys/types.h> +#include <minix/config.h> +#include <net/hton.h> + +u16_t _tmp; +u32_t _tmp_l; diff --git a/lib/ip/inet_addr.c b/lib/ip/inet_addr.c new file mode 100755 index 000000000..2e771dd96 --- /dev/null +++ b/lib/ip/inet_addr.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 1983, 1990 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)inet_addr.c 5.8 (Berkeley) 6/23/90"; +#endif /* LIBC_SCCS and not lint */ + +#if _MINIX +#include <sys/types.h> +#include <ctype.h> +#include <errno.h> + +#include <net/hton.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#endif + +#ifdef __STDC__ +#define _CONST const +#else +#define _CONST +#endif + +/* + * Ascii internet address interpretation routine. + * The value returned is in network order. + */ +ipaddr_t +inet_addr(cp) + register _CONST char *cp; +{ + ipaddr_t val; + + if (inet_aton(cp, &val)) + return (val); + errno= EINVAL; + return (ipaddr_t)-1; +} + +/* + * Check whether "cp" is a valid ascii representation + * of an Internet address and convert to a binary address. + * Returns 1 if the address is valid, 0 if not. + * This replaces inet_addr, the return value from which + * cannot distinguish between failure and a local broadcast address. + */ + +int +inet_aton(cp, addr) + register _CONST char *cp; + ipaddr_t *addr; +{ + register u32_t val, base, n; + register char c; + u32_t parts[4], *pp = parts; + + for (;;) { + /* + * Collect number up to ``.''. + * Values are specified as for C: + * 0x=hex, 0=octal, other=decimal. + */ + val = 0; base = 10; + if (*cp == '0') { + if (*++cp == 'x' || *cp == 'X') + base = 16, cp++; + else + base = 8; + } + while ((c = *cp) != '\0') { + if (isascii(c) && isdigit(c)) { + val = (val * base) + (c - '0'); + cp++; + continue; + } + if (base == 16 && isascii(c) && isxdigit(c)) { + val = (val << 4) + + (c + 10 - (islower(c) ? 'a' : 'A')); + cp++; + continue; + } + break; + } + if (*cp == '.') { + /* + * Internet format: + * a.b.c.d + * a.b.c (with c treated as 16-bits) + * a.b (with b treated as 24 bits) + */ + if (pp >= parts + 3 || val > 0xff) + return (0); + *pp++ = val, cp++; + } else + break; + } + /* + * Check for trailing characters. + */ + if (*cp && (!isascii(*cp) || !isspace(*cp))) + return (0); + /* + * Concoct the address according to + * the number of parts specified. + */ + n = pp - parts + 1; + switch (n) { + + case 1: /* a -- 32 bits */ + break; + + case 2: /* a.b -- 8.24 bits */ + if (val > 0xffffff) + return (0); + val |= parts[0] << 24; + break; + + case 3: /* a.b.c -- 8.8.16 bits */ + if (val > 0xffff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16); + break; + + case 4: /* a.b.c.d -- 8.8.8.8 bits */ + if (val > 0xff) + return (0); + val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8); + break; + } + if (addr) + *addr = htonl(val); + return (1); +} diff --git a/lib/ip/inet_network.c b/lib/ip/inet_network.c new file mode 100755 index 000000000..92513e342 --- /dev/null +++ b/lib/ip/inet_network.c @@ -0,0 +1,95 @@ +/* + * Copyright (c) 1983, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)inet_network.c 8.1 (Berkeley) 6/4/93"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <ctype.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> + +#define INADDR_NONE ((ipaddr_t) -1) + +/* + * Internet network address interpretation routine. + * The library routines call this routine to interpret + * network numbers. + */ +ipaddr_t +inet_network(cp) + register const char *cp; +{ + register ipaddr_t val, base, n; + register char c; + ipaddr_t parts[4], *pp = parts; + register int i; + +again: + val = 0; base = 10; + if (*cp == '0') + base = 8, cp++; + if (*cp == 'x' || *cp == 'X') + base = 16, cp++; + while (c = *cp) { + if (isdigit(c)) { + val = (val * base) + (c - '0'); + cp++; + continue; + } + if (base == 16 && isxdigit(c)) { + val = (val << 4) + (c + 10 - (islower(c) ? 'a' : 'A')); + cp++; + continue; + } + break; + } + if (*cp == '.') { + if (pp >= parts + 4) + return (INADDR_NONE); + *pp++ = val, cp++; + goto again; + } + if (*cp && !isspace(*cp)) + return (INADDR_NONE); + *pp++ = val; + n = pp - parts; + if (n > 4) + return (INADDR_NONE); + for (val = 0, i = 0; i < n; i++) { + val <<= 8; + val |= parts[i] & 0xff; + } + return (val); +} diff --git a/lib/ip/inet_ntoa.c b/lib/ip/inet_ntoa.c new file mode 100755 index 000000000..97a9a53ab --- /dev/null +++ b/lib/ip/inet_ntoa.c @@ -0,0 +1,45 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)inet_ntoa.c 5.5 (Berkeley) 6/1/90"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Convert network-format internet address + * to base 256 d.d.d.d representation. + */ + +#include <sys/types.h> +#include <stdio.h> + +#include <net/gen/in.h> +#include <net/gen/inet.h> + +char * +inet_ntoa(in) + ipaddr_t in; +{ + static char b[18]; + register u8_t *p; + + p = (u8_t *)∈ + sprintf(b, "%d.%d.%d.%d", p[0], p[1], p[2], p[3]); + return (b); +} diff --git a/lib/ip/memcspn.c b/lib/ip/memcspn.c new file mode 100755 index 000000000..8b5d1e2cf --- /dev/null +++ b/lib/ip/memcspn.c @@ -0,0 +1,22 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <string.h> + +size_t +memcspn(const char *string, size_t strlen, const char *notin, size_t notinlen) +{ + register const char *s1, *s2; + int i,j; + + for (s1 = string, i = 0; i<strlen; s1++, i++) { + for(s2 = notin, j = 0; *s2 != *s1 && j < notinlen; s2++, j++) + /* EMPTY */ ; + if (j != notinlen) + break; + } + return s1 - string; +} diff --git a/lib/ip/oneC_sum.c b/lib/ip/oneC_sum.c new file mode 100755 index 000000000..cc3d9af09 --- /dev/null +++ b/lib/ip/oneC_sum.c @@ -0,0 +1,61 @@ +/* oneC_sum() - One complement's checksum Author: Kees J. Bot + * 8 May 1995 + * See RFC 1071, "Computing the Internet checksum" + */ + +#include <sys/types.h> +#include <net/gen/oneCsum.h> + +u16_t oneC_sum(U16_t prev, void *data, size_t size) +{ + u8_t *dptr; + size_t n; + u16_t word; + u32_t sum; + int swap= 0; + + sum= prev; + dptr= data; + n= size; + + swap= ((size_t) dptr & 1); + if (swap) { + sum= ((sum & 0xFF) << 8) | ((sum & 0xFF00) >> 8); + if (n > 0) { + ((u8_t *) &word)[0]= 0; + ((u8_t *) &word)[1]= dptr[0]; + sum+= (u32_t) word; + dptr+= 1; + n-= 1; + } + } + + while (n >= 8) { + sum+= (u32_t) ((u16_t *) dptr)[0] + + (u32_t) ((u16_t *) dptr)[1] + + (u32_t) ((u16_t *) dptr)[2] + + (u32_t) ((u16_t *) dptr)[3]; + dptr+= 8; + n-= 8; + } + + while (n >= 2) { + sum+= (u32_t) ((u16_t *) dptr)[0]; + dptr+= 2; + n-= 2; + } + + if (n > 0) { + ((u8_t *) &word)[0]= dptr[0]; + ((u8_t *) &word)[1]= 0; + sum+= (u32_t) word; + } + + sum= (sum & 0xFFFF) + (sum >> 16); + if (sum > 0xFFFF) sum++; + + if (swap) { + sum= ((sum & 0xFF) << 8) | ((sum & 0xFF00) >> 8); + } + return sum; +} diff --git a/lib/ip/rcmd.c b/lib/ip/rcmd.c new file mode 100755 index 000000000..bc79909ec --- /dev/null +++ b/lib/ip/rcmd.c @@ -0,0 +1,284 @@ +/* + * Copyright (c) 1983 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)rcmd.c 5.22 (Berkeley) 6/1/90"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include <net/gen/netdb.h> +#include <net/gen/in.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> +#include <net/hton.h> +#include <net/netlib.h> + +#define MAXHOSTNAMELEN 256 +#define MAXPATHLEN PATH_MAX + +#ifdef __STDC__ +#define CONST const +#else +#define CONST +#endif + +extern errno; + +int rcmd(ahost, rport, locuser, remuser, cmd, fd2p) +char **ahost; +int rport; +CONST char *locuser, *remuser, *cmd; +int *fd2p; +{ + int fd, fd2, result; + struct hostent *hp; + int n; + static tcpport_t lport; + nwio_tcpconf_t tcpconf; + nwio_tcpcl_t tcpconnopt; + pid_t pid; + char num[8]; + char c; + char *tcp_device; + + fd= -1; + fd2= -1; + + if (lport == 0) { + pid = getpid(); + lport = 1; + do { + lport = (lport << 1) | (pid & 1); + + pid >>= 1; + } while (lport < TCPPORT_RESERVED/2); + } + + tcp_device= getenv("TCP_DEVICE"); + if (tcp_device == NULL) + tcp_device= TCP_DEVICE; + hp= gethostbyname(*ahost); + if (!hp) + { + fprintf(stderr, "%s: unknown host\n", *ahost); + return -1; + } + *ahost= hp->h_name; + n = TCPPORT_RESERVED/2; + do + { + if (--lport < TCPPORT_RESERVED/2) + lport = TCPPORT_RESERVED-1; + fd= open (tcp_device, O_RDWR); + if (fd<0) + { + fprintf(stderr, "unable to open %s: %s\n", + tcp_device, strerror(errno)); + goto bad; + } + tcpconf.nwtc_flags= NWTC_LP_SET | NWTC_SET_RA | NWTC_SET_RP | + NWTC_EXCL; + tcpconf.nwtc_locport= htons(lport); + tcpconf.nwtc_remport= rport; + tcpconf.nwtc_remaddr= *(ipaddr_t *)hp->h_addr; + + result= ioctl(fd, NWIOSTCPCONF, &tcpconf); + if (result<0) + { + if (errno == EADDRINUSE) + { + close(fd); + continue; + } + fprintf(stderr, "unable to ioctl(NWIOSTCPCONF): %s\n", + strerror(errno)); + goto bad; + } + tcpconf.nwtc_flags= NWTC_SHARED; + result= ioctl(fd, NWIOSTCPCONF, &tcpconf); + if (result<0) + { + fprintf(stderr, "unable to ioctl(NWIOSTCPCONF): %s\n", + strerror(errno)); + goto bad; + } + tcpconnopt.nwtcl_flags= 0; + + do + { + result= ioctl (fd, NWIOTCPCONN, &tcpconnopt); + if (result<0 && errno == EAGAIN) + { + sleep(2); + } + } while (result<0 && errno == EAGAIN); + if (result<0 && errno != EADDRINUSE) + { + fprintf(stderr, + "unable to ioctl(NWIOTCPCONN): %s\n", + strerror(errno)); + goto bad; + } + if (result>=0) + break; + } while (--n > 0); + if (n == 0) + { + fprintf(stderr, "can't get port\n"); + return -1; + } + if (!fd2p) + { + if (write(fd, "", 1) != 1) + { + fprintf(stderr, "unable to write: %s", strerror(errno)); + goto bad; + } + } + else + { + fd2= open (tcp_device, O_RDWR); + if (fd2<0) + { + fprintf(stderr, "unable to open %s: %s\n", + tcp_device, strerror(errno)); + goto bad; + } + tcpconf.nwtc_flags= NWTC_LP_SET | NWTC_UNSET_RA | + NWTC_UNSET_RP | NWTC_SHARED; + tcpconf.nwtc_locport= htons(lport); + + result= ioctl(fd2, NWIOSTCPCONF, &tcpconf); + if (result<0) + { + fprintf(stderr, + "unable to ioctl(NWIOSTCPCONF): %s\n", + strerror(errno)); + goto bad; + } + pid= fork(); + if (pid<0) + { + fprintf(stderr, "unable to fork: %s\n", + strerror(errno)); + goto bad; + } + if (!pid) + { + alarm(0); + signal(SIGALRM, SIG_DFL); + alarm(30); /* give up after half a minute */ + tcpconnopt.nwtcl_flags= 0; + + do + { + result= ioctl (fd2, NWIOTCPLISTEN, + &tcpconnopt); + if (result<0 && errno == EAGAIN) + { + sleep(2); + } + } while (result<0 && errno == EAGAIN); + if (result<0 && errno != EADDRINUSE) + { + fprintf(stderr, + "unable to ioctl(NWIOTCPLISTEN): %s\n", + strerror(errno)); + exit(1); + } + if (result>=0) + exit(0); + else + exit(1); + } + /* + * This sleep is a HACK. The command that we are starting + * will try to connect to the fd2 port. It seems that for + * this to succeed the child process must have already made + * the call to ioctl above (the NWIOTCPLISTEN) call. + * The sleep gives the child a chance to make the call + * before the parent sends the port number to the + * command being started. + */ + sleep(1); + + sprintf(num, "%d", lport); + if (write(fd, num, strlen(num)+1) != strlen(num)+1) + { + fprintf(stderr, "unable to write: %s\n", + strerror(errno)); + goto bad; + } + + } + write (fd, locuser, strlen(locuser)+1); + write (fd, remuser, strlen(remuser)+1); + write (fd, cmd, strlen(cmd)+1); + if (read(fd, &c, 1) != 1) + { + fprintf(stderr, "unable to read: %s\n", strerror(errno) ); + goto bad; + } + if (c != 0) + { + while (read(fd, &c, 1) == 1) + { + write(2, &c, 1); + if (c == '\n') + break; + } + goto bad; + } + if (fd2p) + { + *fd2p= fd2; + result= ioctl(fd2, NWIOGTCPCONF, &tcpconf); + if (result<0) + { + fprintf(stderr, "unable to ioctl(NWIOGTCPCONF): %s\n", + strerror(errno) ); + goto bad; + } + if (ntohs(tcpconf.nwtc_remport) >= TCPPORT_RESERVED) + { + fprintf(stderr, "unable to setup 2nd channel\n"); + goto bad; + } + } + return fd; + +bad: + if (fd>=0) + close(fd); + if (fd2>=0) + close(fd2); + return -1; +} diff --git a/lib/ip/res_comp.c b/lib/ip/res_comp.c new file mode 100755 index 000000000..36a6b79e9 --- /dev/null +++ b/lib/ip/res_comp.c @@ -0,0 +1,351 @@ +/* + * Copyright (c) 1985 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)res_comp.c 6.18 (Berkeley) 6/27/90"; +#endif /* LIBC_SCCS and not lint */ + +#if _MINIX +#include <sys/types.h> +#include <stdlib.h> + +#include <net/gen/in.h> +#include <net/gen/nameser.h> +#include <net/gen/resolv.h> + +typedef u8_t u_char; +typedef u16_t u_short; +typedef u32_t u_long; + +static int dn_find _ARGS(( const u_char *exp_dn, const u_char *msg, + u_char **dnptrs, u_char **lastdnptr )); +int dn_skipname _ARGS(( const u_char *comp_dn, const u_char *eom )); + +#define getshort _getshort +#define getlong _getlong +#define putshort __putshort +#define putlong __putlong +#else +#include <sys/types.h> +#include <stdio.h> +#include <arpa/nameser.h> + +static dn_find(); +#endif + +#ifdef __STDC__ +#define CONST const +#else +#define CONST +#endif + +/* + * Expand compressed domain name 'comp_dn' to full domain name. + * 'msg' is a pointer to the begining of the message, + * 'eomorig' points to the first location after the message, + * 'exp_dn' is a pointer to a buffer of size 'length' for the result. + * Return size of compressed name or -1 if there was an error. + */ +dn_expand(msg, eomorig, comp_dn, exp_dn, length) + CONST u_char *msg, *eomorig, *comp_dn; + u_char *exp_dn; + int length; +{ + register CONST u_char *cp; + register u_char *dn; + register int n, c; + CONST u_char *eom; + int len = -1, checked = 0; + + dn = exp_dn; + cp = comp_dn; + eom = exp_dn + length; + /* + * fetch next label in domain name + */ + while (n = *cp++) { + /* + * Check for indirection + */ + switch (n & INDIR_MASK) { + case 0: + if (dn != exp_dn) { + if (dn >= eom) + return (-1); + *dn++ = '.'; + } + if (dn+n >= eom) + return (-1); + checked += n + 1; + while (--n >= 0) { + if ((c = *cp++) == '.') { + if (dn + n + 2 >= eom) + return (-1); + *dn++ = '\\'; + } + *dn++ = c; + if (cp >= eomorig) /* out of range */ + return(-1); + } + break; + + case INDIR_MASK: + if (len < 0) + len = cp - comp_dn + 1; + cp = msg + (((n & 0x3f) << 8) | (*cp & 0xff)); + if (cp < msg || cp >= eomorig) /* out of range */ + return(-1); + checked += 2; + /* + * Check for loops in the compressed name; + * if we've looked at the whole message, + * there must be a loop. + */ + if (checked >= eomorig - msg) + return (-1); + break; + + default: + return (-1); /* flag error */ + } + } + *dn = '\0'; + if (len < 0) + len = cp - comp_dn; + return (len); +} + +/* + * Compress domain name 'exp_dn' into 'comp_dn'. + * Return the size of the compressed name or -1. + * 'length' is the size of the array pointed to by 'comp_dn'. + * 'dnptrs' is a list of pointers to previous compressed names. dnptrs[0] + * is a pointer to the beginning of the message. The list ends with NULL. + * 'lastdnptr' is a pointer to the end of the arrary pointed to + * by 'dnptrs'. Side effect is to update the list of pointers for + * labels inserted into the message as we compress the name. + * If 'dnptr' is NULL, we don't try to compress names. If 'lastdnptr' + * is NULL, we don't update the list. + */ +int +dn_comp(exp_dn, comp_dn, length, dnptrs, lastdnptr) + CONST u_char *exp_dn; + u_char *comp_dn; + int length; + u_char **dnptrs, **lastdnptr; +{ + register u_char *cp; + register CONST u_char *dn; + register int c, l; + u_char **cpp, **lpp, *sp, *eob; + u_char *msg; + + dn = exp_dn; + cp = comp_dn; + eob = cp + length; + if (dnptrs != NULL) { + if ((msg = *dnptrs++) != NULL) { + for (cpp = dnptrs; *cpp != NULL; cpp++) + ; + lpp = cpp; /* end of list to search */ + } + } else + msg = NULL; + for (c = *dn++; c != '\0'; ) { + /* look to see if we can use pointers */ + if (msg != NULL) { + if ((l = dn_find(dn-1, msg, dnptrs, lpp)) >= 0) { + if (cp+1 >= eob) + return (-1); + *cp++ = (l >> 8) | INDIR_MASK; + *cp++ = l % 256; + return (cp - comp_dn); + } + /* not found, save it */ + if (lastdnptr != NULL && cpp < lastdnptr-1) { + *cpp++ = cp; + *cpp = NULL; + } + } + sp = cp++; /* save ptr to length byte */ + do { + if (c == '.') { + c = *dn++; + break; + } + if (c == '\\') { + if ((c = *dn++) == '\0') + break; + } + if (cp >= eob) { + if (msg != NULL) + *lpp = NULL; + return (-1); + } + *cp++ = c; + } while ((c = *dn++) != '\0'); + /* catch trailing '.'s but not '..' */ + if ((l = cp - sp - 1) == 0 && c == '\0') { + cp--; + break; + } + if (l <= 0 || l > MAXLABEL) { + if (msg != NULL) + *lpp = NULL; + return (-1); + } + *sp = l; + } + if (cp >= eob) { + if (msg != NULL) + *lpp = NULL; + return (-1); + } + *cp++ = '\0'; + return (cp - comp_dn); +} + +/* + * Skip over a compressed domain name. Return the size or -1. + */ +dn_skipname(comp_dn, eom) + CONST u_char *comp_dn, *eom; +{ + register CONST u_char *cp; + register int n; + + cp = comp_dn; + while (cp < eom && (n = *cp++)) { + /* + * check for indirection + */ + switch (n & INDIR_MASK) { + case 0: /* normal case, n == len */ + cp += n; + continue; + default: /* illegal type */ + return (-1); + case INDIR_MASK: /* indirection */ + cp++; + } + break; + } + return (cp - comp_dn); +} + +/* + * Search for expanded name from a list of previously compressed names. + * Return the offset from msg if found or -1. + * dnptrs is the pointer to the first name on the list, + * not the pointer to the start of the message. + */ +static int +dn_find(exp_dn, msg, dnptrs, lastdnptr) + CONST u_char *exp_dn, *msg; + u_char **dnptrs, **lastdnptr; +{ + CONST register u_char *dn, *cp; + register u_char **cpp; + register int n; + CONST u_char *sp; + + for (cpp = dnptrs; cpp < lastdnptr; cpp++) { + dn = exp_dn; + sp = cp = *cpp; + while (n = *cp++) { + /* + * check for indirection + */ + switch (n & INDIR_MASK) { + case 0: /* normal case, n == len */ + while (--n >= 0) { + if (*dn == '.') + goto next; + if (*dn == '\\') + dn++; + if (*dn++ != *cp++) + goto next; + } + if ((n = *dn++) == '\0' && *cp == '\0') + return (sp - msg); + if (n == '.') + continue; + goto next; + + default: /* illegal type */ + return (-1); + + case INDIR_MASK: /* indirection */ + cp = msg + (((n & 0x3f) << 8) | *cp); + } + } + if (*dn == '\0') + return (sp - msg); + next: ; + } + return (-1); +} + +/* + * Routines to insert/extract short/long's. Must account for byte + * order and non-alignment problems. This code at least has the + * advantage of being portable. + * + * used by sendmail. + */ + +u16_t +getshort(msgp) + CONST u8_t *msgp; +{ + return ((msgp[0] << 8) | (msgp[1] << 0)); +} + +u32_t +getlong(msgp) + CONST u8_t *msgp; +{ + return ( ((u32_t) msgp[0] << 24) + | ((u32_t) msgp[1] << 16) + | ((u32_t) msgp[2] << 8) + | ((u32_t) msgp[3] << 0)); +} + + +void +putshort(s, msgp) + register U16_t s; + register u8_t *msgp; +{ + + msgp[1] = s; + msgp[0] = s >> 8; +} + +void +putlong(l, msgp) + register u32_t l; + register u8_t *msgp; +{ + + msgp[3] = l; + msgp[2] = (l >>= 8); + msgp[1] = (l >>= 8); + msgp[0] = l >> 8; +} diff --git a/lib/ip/res_init.c b/lib/ip/res_init.c new file mode 100755 index 000000000..741f0b23d --- /dev/null +++ b/lib/ip/res_init.c @@ -0,0 +1,197 @@ +/*- + * Copyright (c) 1985, 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted provided + * that: (1) source distributions retain this entire copyright notice and + * comment, and (2) distributions including binaries display the following + * acknowledgement: ``This product includes software developed by the + * University of California, Berkeley and its contributors'' in the + * documentation or other materials provided with the distribution and in + * all advertising materials mentioning features or use of this software. + * Neither the name of the University nor the names of its contributors may + * be used to endorse or promote products derived from this software without + * specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)res_init.c 6.14 (Berkeley) 6/27/90"; +#endif /* LIBC_SCCS and not lint */ + +#if _MINIX +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <net/hton.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/nameser.h> +#include <net/gen/netdb.h> +#include <net/gen/resolv.h> +#include <net/gen/socket.h> + +#define index(s,c) strchr(s,c) +#else +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <stdio.h> +#include <arpa/nameser.h> +#include <resolv.h> +#endif + +/* + * Resolver state + */ +struct state _res; + +/* + * Set up default settings. If the configuration file exist, the values + * there will have precedence. Otherwise, the server address is set to + * 127.0.0.1 (localhost) and the default domain name comes from gethostname(). + * + * The configuration file should only be used if you want to redefine your + * domain or run without a server on your machine. + * + * Return 0 if completes successfully, -1 on error + */ +int +res_init() +{ + register FILE *fp; + register char *cp, **pp; + register int n; + char buf[BUFSIZ]; + int haveenv = 0; + int havesearch = 0; + struct servent* servent; + u16_t nameserver_port; + + /* Resolver state default settings */ + _res.retrans = RES_TIMEOUT; /* retransmition time interval */ + _res.retry = 4; /* number of times to retransmit */ + _res.options = RES_DEFAULT; /* options flags */ + _res.nscount = 0; /* number of name servers */ + _res.defdname[0] = 0; /* domain */ + + servent= getservbyname("domain", NULL); + if (!servent) + { + h_errno= NO_RECOVERY; + return -1; + } + nameserver_port= servent->s_port; + + /* Allow user to override the local domain definition */ + if ((cp = getenv("LOCALDOMAIN")) != NULL) { + (void)strncpy(_res.defdname, cp, sizeof(_res.defdname)); + haveenv++; + } + + if ((fp = fopen(_PATH_RESCONF, "r")) != NULL) { + /* read the config file */ + while (fgets(buf, sizeof(buf), fp) != NULL) { + /* read default domain name */ + if (!strncmp(buf, "domain", sizeof("domain") - 1)) { + if (haveenv) /* skip if have from environ */ + continue; + cp = buf + sizeof("domain") - 1; + while (*cp == ' ' || *cp == '\t') + cp++; + if ((*cp == '\0') || (*cp == '\n')) + continue; + (void)strncpy(_res.defdname, cp, sizeof(_res.defdname) - 1); + if ((cp = index(_res.defdname, '\n')) != NULL) + *cp = '\0'; + havesearch = 0; + continue; + } + /* set search list */ + if (!strncmp(buf, "search", sizeof("search") - 1)) { + if (haveenv) /* skip if have from environ */ + continue; + cp = buf + sizeof("search") - 1; + while (*cp == ' ' || *cp == '\t') + cp++; + if ((*cp == '\0') || (*cp == '\n')) + continue; + (void)strncpy(_res.defdname, cp, sizeof(_res.defdname) - 1); + if ((cp = index(_res.defdname, '\n')) != NULL) + *cp = '\0'; + /* + * Set search list to be blank-separated strings + * on rest of line. + */ + cp = _res.defdname; + pp = _res.dnsrch; + *pp++ = cp; + for (n = 0; *cp && pp < _res.dnsrch + MAXDNSRCH; cp++) { + if (*cp == ' ' || *cp == '\t') { + *cp = 0; + n = 1; + } else if (n) { + *pp++ = cp; + n = 0; + } + } + /* null terminate last domain if there are excess */ + while (*cp != '\0' && *cp != ' ' && *cp != '\t') + cp++; + *cp = '\0'; + *pp++ = 0; + havesearch = 1; + continue; + } + /* read nameservers to query */ + if (!strncmp(buf, "nameserver", sizeof("nameserver") - 1) && + _res.nscount < MAXNS) { + cp = buf + sizeof("nameserver") - 1; + while (*cp == ' ' || *cp == '\t') + cp++; + if ((*cp == '\0') || (*cp == '\n')) + continue; + if (!inet_aton(cp, &_res.nsaddr_list[_res.nscount])) + continue; + _res.nsport_list[_res.nscount]= nameserver_port; + _res.nscount++; + continue; + } + } + (void) fclose(fp); + } + if (_res.nscount == 0) { + /* "localhost" is the default nameserver. */ + _res.nsaddr_list[0]= HTONL(0x7F000001); + _res.nsport_list[0]= nameserver_port; + _res.nscount= 1; + } + if (_res.defdname[0] == 0) { + if (gethostname(buf, sizeof(_res.defdname)) == 0 && + (cp = index(buf, '.'))) + (void)strcpy(_res.defdname, cp + 1); + } + + /* find components of local domain that might be searched */ + if (havesearch == 0) { + pp = _res.dnsrch; + *pp++ = _res.defdname; + for (cp = _res.defdname, n = 0; *cp; cp++) + if (*cp == '.') + n++; + cp = _res.defdname; + for (; n >= LOCALDOMAINPARTS && pp < _res.dnsrch + MAXDFLSRCH; + n--) { + cp = index(cp, '.'); + *pp++ = ++cp; + } + *pp++ = 0; + } + _res.options |= RES_INIT; + return (0); +} diff --git a/lib/ip/res_mkquery.c b/lib/ip/res_mkquery.c new file mode 100755 index 000000000..bf1800091 --- /dev/null +++ b/lib/ip/res_mkquery.c @@ -0,0 +1,222 @@ +/* + * Copyright (c) 1985 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)res_mkquery.c 6.12 (Berkeley) 6/1/90"; +#endif /* LIBC_SCCS and not lint */ + +#if _MINIX +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <net/hton.h> +#include <net/gen/in.h> +#include <net/gen/nameser.h> +#include <net/gen/resolv.h> + +typedef u16_t u_short; +typedef unsigned u_int; +typedef u32_t u_long; + +#define bzero(b,l) memset(b,0,l) +#define bcopy(s,d,l) memcpy(d,s,l) + +#define putshort __putshort +#define putlong __putlong +#else +#include <stdio.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <resolv.h> +#endif + +#ifdef __STDC__ +#define _CONST const +#else +#define _CONST +#endif + +/* + * Form all types of queries. + * Returns the size of the result or -1. + */ +res_mkquery(op, dname, class, type, data, datalen, newrr, buf, buflen) + int op; /* opcode of query */ + _CONST char *dname; /* domain name */ + int class, type; /* class and type of query */ + _CONST char *data; /* resource record data */ + int datalen; /* length of data */ + _CONST struct rrec *newrr; /* new rr for modify or append */ + char *buf; /* buffer to put query */ + int buflen; /* size of buffer */ +{ + register dns_hdr_t *hp; + register char *cp; + register int n; + char *dnptrs[10], **dpp, **lastdnptr; + +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("res_mkquery(%d, %s, %d, %d)\n", op, dname, class, type); +#endif /* DEBUG */ + /* + * Initialize header fields. + */ + if ((buf == NULL) || (buflen < sizeof(dns_hdr_t))) + return(-1); + bzero(buf, sizeof(dns_hdr_t)); + hp = (dns_hdr_t *) buf; + hp->dh_id = htons(++_res.id); + hp->dh_flag1= 0; + hp->dh_flag2= 0; + hp->dh_flag1 |= (op << 3) & DHF_OPCODE; + hp->dh_flag2 |= ((_res.options & RES_PRIMARY) != 0 ? 1 : 0) << 6; + hp->dh_flag1 |= (_res.options & RES_RECURSE) != 0 ? 1 : 0; + hp->dh_flag2 |= NOERROR & DHF_RCODE; + cp = buf + sizeof(dns_hdr_t); + buflen -= sizeof(dns_hdr_t); + dpp = dnptrs; + *dpp++ = buf; + *dpp++ = NULL; + lastdnptr = dnptrs + sizeof(dnptrs)/sizeof(dnptrs[0]); + /* + * perform opcode specific processing + */ + switch (op) { + case QUERY: + if ((buflen -= QFIXEDSZ) < 0) + return(-1); + if ((n = dn_comp((u8_t *)dname, (u8_t *)cp, buflen, + (u8_t **)dnptrs, (u8_t **)lastdnptr)) < 0) + return (-1); + cp += n; + buflen -= n; + putshort(type, (u8_t *)cp); + cp += sizeof(u_short); + putshort(class, (u8_t *)cp); + cp += sizeof(u_short); + hp->dh_qdcount = HTONS(1); + if (op == QUERY || data == NULL) + break; + /* + * Make an additional record for completion domain. + */ + buflen -= RRFIXEDSZ; + if ((n = dn_comp((u8_t *)data, (u8_t *)cp, buflen, + (u8_t **)dnptrs, (u8_t **)lastdnptr)) < 0) + return (-1); + cp += n; + buflen -= n; + putshort(T_NULL, (u8_t *)cp); + cp += sizeof(u_short); + putshort(class, (u8_t *)cp); + cp += sizeof(u_short); + putlong(0, (u8_t *)cp); + cp += sizeof(u_long); + putshort(0, (u8_t *)cp); + cp += sizeof(u_short); + hp->dh_arcount = HTONS(1); + break; + + case IQUERY: + /* + * Initialize answer section + */ + if (buflen < 1 + RRFIXEDSZ + datalen) + return (-1); + *cp++ = '\0'; /* no domain name */ + putshort(type, (u8_t *)cp); + cp += sizeof(u_short); + putshort(class, (u8_t *)cp); + cp += sizeof(u_short); + putlong(0, (u8_t *)cp); + cp += sizeof(u_long); + putshort(datalen, (u8_t *)cp); + cp += sizeof(u_short); + if (datalen) { + bcopy(data, cp, datalen); + cp += datalen; + } + hp->dh_ancount = HTONS(1); + break; + +#ifdef ALLOW_UPDATES + /* + * For UPDATEM/UPDATEMA, do UPDATED/UPDATEDA followed by UPDATEA + * (Record to be modified is followed by its replacement in msg.) + */ + case UPDATEM: + case UPDATEMA: + + case UPDATED: + /* + * The res code for UPDATED and UPDATEDA is the same; user + * calls them differently: specifies data for UPDATED; server + * ignores data if specified for UPDATEDA. + */ + case UPDATEDA: + buflen -= RRFIXEDSZ + datalen; + if ((n = dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0) + return (-1); + cp += n; + putshort(type, cp); + cp += sizeof(u_short); + putshort(class, cp); + cp += sizeof(u_short); + putlong(0, cp); + cp += sizeof(u_long); + putshort(datalen, cp); + cp += sizeof(u_short); + if (datalen) { + bcopy(data, cp, datalen); + cp += datalen; + } + if ( (op == UPDATED) || (op == UPDATEDA) ) { + hp->ancount = HTONS(0); + break; + } + /* Else UPDATEM/UPDATEMA, so drop into code for UPDATEA */ + + case UPDATEA: /* Add new resource record */ + buflen -= RRFIXEDSZ + datalen; + if ((n = dn_comp(dname, cp, buflen, dnptrs, lastdnptr)) < 0) + return (-1); + cp += n; + putshort(newrr->r_type, cp); + cp += sizeof(u_short); + putshort(newrr->r_class, cp); + cp += sizeof(u_short); + putlong(0, cp); + cp += sizeof(u_long); + putshort(newrr->r_size, cp); + cp += sizeof(u_short); + if (newrr->r_size) { + bcopy(newrr->r_data, cp, newrr->r_size); + cp += newrr->r_size; + } + hp->ancount = HTONS(0); + break; + +#endif /* ALLOW_UPDATES */ + } + return (cp - buf); +} diff --git a/lib/ip/res_query.c b/lib/ip/res_query.c new file mode 100755 index 000000000..6f6330d9a --- /dev/null +++ b/lib/ip/res_query.c @@ -0,0 +1,299 @@ +/* + * Copyright (c) 1988 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms are permitted + * provided that: (1) source distributions retain this entire copyright + * notice and comment, and (2) distributions including binaries display + * the following acknowledgement: ``This product includes software + * developed by the University of California, Berkeley and its contributors'' + * in the documentation or other materials provided with the distribution + * and in all advertising materials mentioning features or use of this + * software. Neither the name of the University nor the names of its + * contributors may be used to endorse or promote products derived + * from this software without specific prior written permission. + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)res_query.c 5.7 (Berkeley) 6/1/90"; +#endif /* LIBC_SCCS and not lint */ + +#if _MINIX +#include <sys/types.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <net/hton.h> +#include <net/gen/in.h> +#include <net/gen/nameser.h> +#include <net/gen/netdb.h> +#include <net/gen/resolv.h> + +typedef u8_t u_char; + +#define bcopy(s,d,l) memcpy(d,s,l) + +#define hostalias __hostalias +#else +#include <sys/param.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <ctype.h> +#include <netdb.h> +#include <stdio.h> +#include <errno.h> +#include <string.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> +#include <resolv.h> + +extern int errno; +#endif + +#if __STDC__ +#define CONST const +#else +#define CONST +#endif + +#if PACKETSZ > 1024 +#define MAXPACKET PACKETSZ +#else +#define MAXPACKET 1024 +#endif + +int h_errno; + +/* + * Formulate a normal query, send, and await answer. + * Returned answer is placed in supplied buffer "answer". + * Perform preliminary check of answer, returning success only + * if no error is indicated and the answer count is nonzero. + * Return the size of the response on success, -1 on error. + * Error number is left in h_errno. + * Caller must parse answer and determine whether it answers the question. + */ +int +res_query(name, class, type, answer, anslen) + char *name; /* domain name */ + int class, type; /* class and type of query */ + u_char *answer; /* buffer to put answer */ + int anslen; /* size of answer buffer */ +{ + char buf[MAXPACKET]; + dns_hdr_t *hp; + int n; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) + return (-1); +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("res_query(%s, %d, %d)\n", name, class, type); +#endif + n = res_mkquery(QUERY, name, class, type, (char *)NULL, 0, NULL, + buf, sizeof(buf)); + + if (n <= 0) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("res_query: mkquery failed\n"); +#endif + h_errno = NO_RECOVERY; + return (n); + } + n = res_send(buf, n, (char *)answer, anslen); + if (n < 0) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("res_query: send error(%d)\n", errno); +#endif + h_errno = TRY_AGAIN; + return(n); + } + + hp = (dns_hdr_t *) answer; + if ((hp->dh_flag2 & DHF_RCODE) != NOERROR || + ntohs(hp->dh_ancount) == 0) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("rcode = %d, ancount=%d\n", + hp->dh_flag2 & DHF_RCODE, + ntohs(hp->dh_ancount)); +#endif + switch (hp->dh_flag2 & DHF_RCODE) { + case NXDOMAIN: + h_errno = HOST_NOT_FOUND; + break; + case SERVFAIL: + h_errno = TRY_AGAIN; + break; + case NOERROR: + h_errno = NO_DATA; + break; + case FORMERR: + case NOTIMP: + case REFUSED: + default: + h_errno = NO_RECOVERY; + break; + } + return (-1); + } + return(n); +} + +/* + * Formulate a normal query, send, and retrieve answer in supplied buffer. + * Return the size of the response on success, -1 on error. + * If enabled, implement search rules until answer or unrecoverable failure + * is detected. Error number is left in h_errno. + * Only useful for queries in the same name hierarchy as the local host + * (not, for example, for host address-to-name lookups in domain in-addr.arpa). + */ +res_search(name, class, type, answer, anslen) + char *name; /* domain name */ + int class, type; /* class and type of query */ + u_char *answer; /* buffer to put answer */ + int anslen; /* size of answer */ +{ + register char *cp, **domain; + int n, ret, got_nodata = 0; + + if ((_res.options & RES_INIT) == 0 && res_init() == -1) + return (-1); + + errno = 0; + h_errno = HOST_NOT_FOUND; /* default, if we never query */ + for (cp = name, n = 0; *cp; cp++) + if (*cp == '.') + n++; + if (n == 0 && (cp = hostalias(name))) + return (res_query(cp, class, type, answer, anslen)); + + /* + * We do at least one level of search if + * - there is no dot and RES_DEFNAME is set, or + * - there is at least one dot, there is no trailing dot, + * and RES_DNSRCH is set. + */ + if ((n == 0 && _res.options & RES_DEFNAMES) || + (n != 0 && *--cp != '.' && _res.options & RES_DNSRCH)) + for (domain = _res.dnsrch; *domain; domain++) { + ret = res_querydomain(name, *domain, class, type, + answer, anslen); + if (ret > 0) + return (ret); + /* + * If no server present, give up. + * If name isn't found in this domain, + * keep trying higher domains in the search list + * (if that's enabled). + * On a NO_DATA error, keep trying, otherwise + * a wildcard entry of another type could keep us + * from finding this entry higher in the domain. + * If we get some other error (negative answer or + * server failure), then stop searching up, + * but try the input name below in case it's fully-qualified. + */ + if (errno == ECONNREFUSED) { + h_errno = TRY_AGAIN; + return (-1); + } + if (h_errno == NO_DATA) + got_nodata++; + if ((h_errno != HOST_NOT_FOUND && h_errno != NO_DATA) || + (_res.options & RES_DNSRCH) == 0) + break; + } + /* + * If the search/default failed, try the name as fully-qualified, + * but only if it contained at least one dot (even trailing). + * This is purely a heuristic; we assume that any reasonable query + * about a top-level domain (for servers, SOA, etc) will not use + * res_search. + */ + if (n && (ret = res_querydomain(name, (char *)NULL, class, type, + answer, anslen)) > 0) + return (ret); + if (got_nodata) + h_errno = NO_DATA; + return (-1); +} + +/* + * Perform a call on res_query on the concatenation of name and domain, + * removing a trailing dot from name if domain is NULL. + */ +int +res_querydomain(name, domain, class, type, answer, anslen) + char *name, *domain; + int class, type; /* class and type of query */ + u_char *answer; /* buffer to put answer */ + int anslen; /* size of answer */ +{ + char nbuf[2*MAXDNAME+2]; + char *longname = nbuf; + int n; + +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("res_querydomain(%s, %s, %d, %d)\n", + name, domain, class, type); +#endif + if (domain == NULL) { + /* + * Check for trailing '.'; + * copy without '.' if present. + */ + n = strlen(name) - 1; + if (name[n] == '.' && n < sizeof(nbuf) - 1) { + bcopy(name, nbuf, n); + nbuf[n] = '\0'; + } else + longname = name; + } else + (void)sprintf(nbuf, "%.*s.%.*s", + MAXDNAME, name, MAXDNAME, domain); + + return (res_query(longname, class, type, answer, anslen)); +} + +char * +hostalias(name) + register CONST char *name; +{ + register char *C1, *C2; + FILE *fp; + char *file; + char buf[BUFSIZ]; + static char abuf[MAXDNAME]; + + file = getenv("HOSTALIASES"); + if (file == NULL || (fp = fopen(file, "r")) == NULL) + return (NULL); + buf[sizeof(buf) - 1] = '\0'; + while (fgets(buf, sizeof(buf), fp)) { + for (C1 = buf; *C1 && !isspace(*C1); ++C1); + if (!*C1) + break; + *C1 = '\0'; + if (!strcasecmp(buf, name)) { + while (isspace(*++C1)); + if (!*C1) + break; + for (C2 = C1 + 1; *C2 && !isspace(*C2); ++C2); + abuf[sizeof(abuf) - 1] = *C2 = '\0'; + (void)strncpy(abuf, C1, sizeof(abuf) - 1); + fclose(fp); + return (abuf); + } + } + fclose(fp); + return (NULL); +} diff --git a/lib/ip/res_send.c b/lib/ip/res_send.c new file mode 100755 index 000000000..dd6ac99fc --- /dev/null +++ b/lib/ip/res_send.c @@ -0,0 +1,905 @@ +/* + * Copyright (c) 1985, 1989 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)res_send.c 6.27 (Berkeley) 2/24/91"; +#endif /* LIBC_SCCS and not lint */ + +/* + * Send query to name server and wait for reply. + */ + +#if !_MINIX +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> +#include <sys/uio.h> +#include <netinet/in.h> +#include <arpa/nameser.h> +#include <stdio.h> +#include <errno.h> +#include <resolv.h> +#include <unistd.h> +#include <string.h> + +#else /* _MINIX */ + +#include <sys/types.h> +#include <sys/ioctl.h> +#include <sys/stat.h> +#include <assert.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <net/hton.h> + +#include <net/netlib.h> +#include <net/gen/in.h> +#include <net/gen/inet.h> +#include <net/gen/netdb.h> +#include <net/gen/nameser.h> +#include <net/gen/resolv.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> +#include <net/gen/udp.h> +#include <net/gen/udp_hdr.h> +#include <net/gen/udp_io.h> + +typedef u16_t u_short; + +static int tcp_connect _ARGS(( ipaddr_t host, Tcpport_t port, int *terrno )); +static int tcpip_writeall _ARGS(( int fd, const char *buf, size_t siz )); +static int udp_connect _ARGS(( void )); +static int udp_sendto _ARGS(( int fd, const char *buf, unsigned buflen, + ipaddr_t addr, Udpport_t port )); +static int udp_receive _ARGS(( int fd, char *buf, unsigned buflen, + time_t timeout )); +static void alarm_handler _ARGS(( int sig )); + +#endif /* !_MINIX */ + +static int s = -1; /* socket used for communications */ +#if !_MINIX +static struct sockaddr no_addr; + +#ifndef FD_SET +#define NFDBITS 32 +#define FD_SETSIZE 32 +#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS))) +#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS))) +#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS))) +#define FD_ZERO(p) bzero((char *)(p), sizeof(*(p))) +#endif /* FD_SET */ +#endif /* _MINIX */ + +res_send(buf, buflen, answer, anslen) + const char *buf; + int buflen; + char *answer; + int anslen; +{ + register int n; + int try, v_circuit, resplen, ns; + int gotsomewhere = 0, connected = 0; + int connreset = 0; +#if !_MINIX + u_short id, len; +#else /* _MINIX */ + u16_t id, len; +#endif /* !_MINIX */ + char *cp; +#if !_MINIX + fd_set dsmask; + struct timeval timeout; + HEADER *hp = (HEADER *) buf; + HEADER *anhp = (HEADER *) answer; + struct iovec iov[2]; +#else /* _MINIX */ + time_t timeout; + dns_hdr_t *hp = (dns_hdr_t *) buf; + dns_hdr_t *anhp = (dns_hdr_t *) answer; +#endif /* !_MINIX */ + int terrno = ETIMEDOUT; + char junk[512]; + +#ifdef DEBUG + if (_res.options & RES_DEBUG) { + printf("res_send()\n"); + __p_query(buf); + } +#endif /* DEBUG */ + if (!(_res.options & RES_INIT)) + if (res_init() == -1) { + return(-1); + } + + v_circuit = (_res.options & RES_USEVC) || buflen > PACKETSZ; +#if !_MINIX + id = hp->id; +#else /* _MINIX */ + id = hp->dh_id; +#endif /* !_MINIX */ + /* + * Send request, RETRY times, or until successful + */ + for (try = 0; try < _res.retry; try++) { + for (ns = 0; ns < _res.nscount; ns++) { +#ifdef DEBUG +#if !_MINIX + if (_res.options & RES_DEBUG) + printf("Querying server (# %d) address = %s\n", ns+1, + inet_ntoa(_res.nsaddr_list[ns].sin_addr)); +#else /* _MINIX */ + if (_res.options & RES_DEBUG) + printf("Querying server (# %d) address = %s\n", ns+1, + inet_ntoa(_res.nsaddr_list[ns])); +#endif /* !_MINIX */ +#endif /* DEBUG */ + usevc: + if (v_circuit) { +#if !_MINIX + int truncated = 0; + + /* + * Use virtual circuit; + * at most one attempt per server. + */ + try = _res.retry; + if (s < 0) { + s = socket(AF_INET, SOCK_STREAM, 0); + if (s < 0) { + terrno = errno; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("socket (vc) failed"); +#endif /* DEBUG */ + continue; + } + if (connect(s, + (struct sockaddr *)&(_res.nsaddr_list[ns]), + sizeof(struct sockaddr)) < 0) { + terrno = errno; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("connect failed"); +#endif /* DEBUG */ + (void) close(s); + s = -1; + continue; + } + } + /* + * Send length & message + */ + len = htons((u_short)buflen); + iov[0].iov_base = (caddr_t)&len; + iov[0].iov_len = sizeof(len); + iov[1].iov_base = (char *)buf; + iov[1].iov_len = buflen; + if (writev(s, iov, 2) != sizeof(len) + buflen) { + terrno = errno; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("write failed"); +#endif /* DEBUG */ + (void) close(s); + s = -1; + continue; + } + /* + * Receive length & response + */ + cp = answer; + len = sizeof(short); + while (len != 0 && + (n = read(s, (char *)cp, (int)len)) > 0) { + cp += n; + len -= n; + } + if (n <= 0) { + terrno = errno; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("read failed"); +#endif /* DEBUG */ + (void) close(s); + s = -1; + /* + * A long running process might get its TCP + * connection reset if the remote server was + * restarted. Requery the server instead of + * trying a new one. When there is only one + * server, this means that a query might work + * instead of failing. We only allow one reset + * per query to prevent looping. + */ + if (terrno == ECONNRESET && !connreset) { + connreset = 1; + ns--; + } + continue; + } + cp = answer; + if ((resplen = ntohs(*(u_short *)cp)) > anslen) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + fprintf(stderr, "response truncated\n"); +#endif /* DEBUG */ + len = anslen; + truncated = 1; + } else + len = resplen; + while (len != 0 && + (n = read(s, (char *)cp, (int)len)) > 0) { + cp += n; + len -= n; + } + if (n <= 0) { + terrno = errno; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("read failed"); +#endif /* DEBUG */ + (void) close(s); + s = -1; + continue; + } + if (truncated) { + /* + * Flush rest of answer + * so connection stays in synch. + */ + anhp->tc = 1; + len = resplen - anslen; + while (len != 0) { + n = (len > sizeof(junk) ? + sizeof(junk) : len); + if ((n = read(s, junk, n)) > 0) + len -= n; + else + break; + } + } +#else /* _MINIX */ + int truncated = 0; + int nbytes; + + /* + * Use virtual circuit; + * at most one attempt per server. + */ + try = _res.retry; + if (s < 0) + { + s= tcp_connect(_res.nsaddr_list[ns], + _res.nsport_list[ns], &terrno); + if (s == -1) + continue; + } + /* + * Send length & message + */ + len = htons((u_short)buflen); + nbytes= tcpip_writeall(s, (char *)&len, + sizeof(len)); + if (nbytes != sizeof(len)) + { + terrno= errno; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + fprintf(stderr, "write failed: %s\n", + strerror(terrno)); +#endif /* DEBUG */ + close(s); + s= -1; + continue; + } + nbytes= tcpip_writeall(s, buf, buflen); + if (nbytes != buflen) + { + terrno= errno; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + fprintf(stderr, "write failed: %s\n", + strerror(terrno)); +#endif /* DEBUG */ + close(s); + s= -1; + continue; + } + /* + * Receive length & response + */ + cp = answer; + len = sizeof(short); + while (len != 0) + { + n = read(s, (char *)cp, (int)len); + if (n <= 0) + break; + cp += n; + assert(len >= n); + len -= n; + } + if (len) { + terrno = errno; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + fprintf(stderr, "read failed: %s\n", + strerror(terrno)); +#endif /* DEBUG */ + close(s); + s= -1; + /* + * A long running process might get its TCP + * connection reset if the remote server was + * restarted. Requery the server instead of + * trying a new one. When there is only one + * server, this means that a query might work + * instead of failing. We only allow one reset + * per query to prevent looping. + */ + if (terrno == ECONNRESET && !connreset) { + connreset = 1; + ns--; + } + continue; + } + cp = answer; + if ((resplen = ntohs(*(u_short *)cp)) > anslen) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + fprintf(stderr, "response truncated\n"); +#endif /* DEBUG */ + len = anslen; + truncated = 1; + } else + len = resplen; + while (len != 0) + { + n= read(s, (char *)cp, (int)len); + if (n <= 0) + break; + cp += n; + assert(len >= n); + len -= n; + } + if (len) { + terrno = errno; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + fprintf(stderr, "read failed: %s\n", + strerror(terrno)); +#endif /* DEBUG */ + close(s); + s= -1; + continue; + } + if (truncated) { + /* + * Flush rest of answer + * so connection stays in synch. + */ + anhp->dh_flag1 |= DHF_TC; + len = resplen - anslen; + while (len != 0) { + n = (len > sizeof(junk) ? + sizeof(junk) : len); + n = read(s, junk, n); + if (n <= 0) + { + assert(len >= n); + len -= n; + } + else + break; + } + } +#endif /* _MINIX */ + } else { +#if !_MINIX + /* + * Use datagrams. + */ + if (s < 0) { + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + terrno = errno; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("socket (dg) failed"); +#endif /* DEBUG */ + continue; + } + } +#if BSD >= 43 + /* + * I'm tired of answering this question, so: + * On a 4.3BSD+ machine (client and server, + * actually), sending to a nameserver datagram + * port with no nameserver will cause an + * ICMP port unreachable message to be returned. + * If our datagram socket is "connected" to the + * server, we get an ECONNREFUSED error on the next + * socket operation, and select returns if the + * error message is received. We can thus detect + * the absence of a nameserver without timing out. + * If we have sent queries to at least two servers, + * however, we don't want to remain connected, + * as we wish to receive answers from the first + * server to respond. + */ + if (_res.nscount == 1 || (try == 0 && ns == 0)) { + /* + * Don't use connect if we might + * still receive a response + * from another server. + */ + if (connected == 0) { + if (connect(s, (struct sockaddr *)&_res.nsaddr_list[ns], + sizeof(struct sockaddr)) < 0) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("connect"); +#endif /* DEBUG */ + continue; + } + connected = 1; + } + if (send(s, buf, buflen, 0) != buflen) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("send"); +#endif /* DEBUG */ + continue; + } + } else { + /* + * Disconnect if we want to listen + * for responses from more than one server. + */ + if (connected) { + (void) connect(s, &no_addr, + sizeof(no_addr)); + connected = 0; + } +#endif /* BSD */ + if (sendto(s, buf, buflen, 0, + (struct sockaddr *)&_res.nsaddr_list[ns], + sizeof(struct sockaddr)) != buflen) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("sendto"); +#endif /* DEBUG */ + continue; + } +#if BSD >= 43 + } +#endif /* BSD */ + + /* + * Wait for reply + */ + timeout.tv_sec = (_res.retrans << try); + if (try > 0) + timeout.tv_sec /= _res.nscount; + if (timeout.tv_sec <= 0) + timeout.tv_sec = 1; + timeout.tv_usec = 0; +wait: + FD_ZERO(&dsmask); + FD_SET(s, &dsmask); + n = select(s+1, &dsmask, (fd_set *)NULL, + (fd_set *)NULL, &timeout); + if (n < 0) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("select"); +#endif /* DEBUG */ + continue; + } + if (n == 0) { + /* + * timeout + */ +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("timeout\n"); +#endif /* DEBUG */ +#if BSD >= 43 + gotsomewhere = 1; +#endif + continue; + } + if ((resplen = recv(s, answer, anslen, 0)) <= 0) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("recvfrom"); +#endif /* DEBUG */ + continue; + } + gotsomewhere = 1; + if (id != anhp->id) { + /* + * response from old query, ignore it + */ +#ifdef DEBUG + if (_res.options & RES_DEBUG) { + printf("old answer:\n"); + __p_query(answer); + } +#endif /* DEBUG */ + goto wait; + } + if (!(_res.options & RES_IGNTC) && anhp->tc) { + /* + * get rest of answer; + * use TCP with same server. + */ +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("truncated answer\n"); +#endif /* DEBUG */ + (void) close(s); + s = -1; + v_circuit = 1; + goto usevc; + } +#else /* _MINIX */ + /* + * Use datagrams. + */ + if (s < 0) { + s = udp_connect(); + if (s < 0) { + terrno = errno; +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("udp_connect failed"); +#endif /* DEBUG */ + continue; + } + } + if (udp_sendto(s, buf, buflen, _res.nsaddr_list[ns], + _res.nsport_list[ns]) != buflen) { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("sendto"); +#endif /* DEBUG */ + continue; + } + + /* + * Wait for reply + */ + timeout= (_res.retrans << try); + if (try > 0) + timeout /= _res.nscount; + if (timeout <= 0) + timeout= 1; +wait: + if ((resplen= udp_receive(s, answer, anslen, timeout)) + == -1) + { + if (errno == EINTR) + { + /* + * timeout + */ +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("timeout\n"); +#endif /* DEBUG */ + gotsomewhere = 1; + } + else + { +#ifdef DEBUG + if (_res.options & RES_DEBUG) + perror("udp_receive"); +#endif /* DEBUG */ + } + continue; + } + gotsomewhere = 1; + if (id != anhp->dh_id) { + /* + * response from old query, ignore it + */ +#ifdef DEBUG + if (_res.options & RES_DEBUG) { + printf("old answer:\n"); + __p_query(answer); + } +#endif /* DEBUG */ + goto wait; + } + if (!(_res.options & RES_IGNTC) && + (anhp->dh_flag1 & DHF_TC)) { + /* + * get rest of answer; + * use TCP with same server. + */ +#ifdef DEBUG + if (_res.options & RES_DEBUG) + printf("truncated answer\n"); +#endif /* DEBUG */ + (void) close(s); + s = -1; + v_circuit = 1; + goto usevc; + } +#endif /* !_MINIX */ + } +#ifdef DEBUG + if (_res.options & RES_DEBUG) { + printf("got answer:\n"); + __p_query(answer); + } +#endif /* DEBUG */ + /* + * If using virtual circuits, we assume that the first server + * is preferred * over the rest (i.e. it is on the local + * machine) and only keep that one open. + * If we have temporarily opened a virtual circuit, + * or if we haven't been asked to keep a socket open, + * close the socket. + */ + if ((v_circuit && + ((_res.options & RES_USEVC) == 0 || ns != 0)) || + (_res.options & RES_STAYOPEN) == 0) { + (void) close(s); + s = -1; + } + return (resplen); + } + } + if (s >= 0) { + (void) close(s); + s = -1; + } + if (v_circuit == 0) + if (gotsomewhere == 0) + errno = ECONNREFUSED; /* no nameservers found */ + else + errno = ETIMEDOUT; /* no answer obtained */ + else + errno = terrno; + return (-1); +} + +/* + * This routine is for closing the socket if a virtual circuit is used and + * the program wants to close it. This provides support for endhostent() + * which expects to close the socket. + * + * This routine is not expected to be user visible. + */ +void +_res_close() +{ + if (s != -1) { + (void) close(s); + s = -1; + } +} + +#if _MINIX +static int tcp_connect(host, port, terrno) +ipaddr_t host; +tcpport_t port; +int *terrno; +{ + char *dev_name; + int fd; + int error; + nwio_tcpconf_t tcpconf; + nwio_tcpcl_t clopt; + + dev_name= getenv("TCP_DEVICE"); + if (!dev_name) + dev_name= TCP_DEVICE; + fd= open(dev_name, O_RDWR); + if (fd == -1) + { + *terrno= errno; + return -1; + } + tcpconf.nwtc_flags= NWTC_EXCL | NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; + tcpconf.nwtc_remaddr= host; + tcpconf.nwtc_remport= port; + error= ioctl(fd, NWIOSTCPCONF, &tcpconf); + if (error == -1) + { + *terrno= errno; + close(fd); + return -1; + } + clopt.nwtcl_flags= 0; + error= ioctl(fd, NWIOTCPCONN, &clopt); + if (error == -1) + { + *terrno= errno; + close(fd); + return -1; + } + *terrno= 0; + return fd; +} + +static int tcpip_writeall(fd, buf, siz) +int fd; +const char *buf; +size_t siz; +{ + size_t siz_org; + int nbytes; + + siz_org= siz; + + while (siz) + { + nbytes= write(fd, buf, siz); + if (nbytes <= 0) + return siz_org-siz; + assert(siz >= nbytes); + buf += nbytes; + siz -= nbytes; + } + return siz_org; +} + + +static int udp_connect() +{ + nwio_udpopt_t udpopt; + char *dev_name; + int fd, r, terrno; + + dev_name= getenv("UDP_DEVICE"); + if (!dev_name) + dev_name= UDP_DEVICE; + fd= open(dev_name, O_RDWR); + if (fd == -1) + return -1; + + udpopt.nwuo_flags= NWUO_COPY | NWUO_LP_SEL | NWUO_EN_LOC | + NWUO_EN_BROAD | NWUO_RP_ANY | NWUO_RA_ANY | NWUO_RWDATALL | + NWUO_DI_IPOPT; + r= ioctl(fd, NWIOSUDPOPT, &udpopt); + if (r == -1) + { + terrno= errno; + close(fd); + errno= terrno; + return -1; + } + return fd; +} + +static int udp_sendto(fd, buf, buflen, addr, port) +int fd; +const char *buf; +unsigned buflen; +ipaddr_t addr; +udpport_t port; +{ + char *newbuf; + udp_io_hdr_t *udp_io_hdr; + int r, terrno; + + newbuf= malloc(sizeof(*udp_io_hdr) + buflen); + if (newbuf == NULL) + { + errno= ENOMEM; + return -1; + } + udp_io_hdr= (udp_io_hdr_t *)newbuf; + udp_io_hdr->uih_dst_addr= addr; + udp_io_hdr->uih_dst_port= port; + udp_io_hdr->uih_ip_opt_len= 0; + udp_io_hdr->uih_data_len= buflen; + + memcpy(newbuf + sizeof(*udp_io_hdr), buf, buflen); + r= write(fd, newbuf, sizeof(*udp_io_hdr) + buflen); + terrno= errno; + free(newbuf); + if (r >= sizeof(*udp_io_hdr)) + r -= sizeof(*udp_io_hdr); + errno= terrno; + return r; +} + +static void alarm_handler(sig) +int sig; +{ + signal(SIGALRM, alarm_handler); + alarm(1); +} + +static int udp_receive(fd, buf, buflen, timeout) +int fd; +char *buf; +unsigned buflen; +time_t timeout; +{ + char *newbuf; + udp_io_hdr_t *udp_io_hdr; + int r, terrno; + void (*u_handler) _ARGS(( int sig )); + time_t u_timeout; + + newbuf= malloc(sizeof(*udp_io_hdr) + buflen); + if (newbuf == NULL) + { + errno= ENOMEM; + return -1; + } + + u_handler= signal(SIGALRM, alarm_handler); + u_timeout= alarm(timeout); + + r= read(fd, newbuf, sizeof(*udp_io_hdr) + buflen); + terrno= errno; + + if (r < 0 || r <= sizeof(*udp_io_hdr)) + { + if (r > 0) + r= 0; + free(newbuf); + + + alarm(0); + signal(SIGALRM, u_handler); + alarm(u_timeout); + + errno= terrno; + return r; + } + + memcpy(buf, newbuf + sizeof(*udp_io_hdr), r - sizeof(*udp_io_hdr)); + free(newbuf); + + alarm(0); + signal(SIGALRM, u_handler); + alarm(u_timeout); + + return r-sizeof(*udp_io_hdr); +} + +#endif diff --git a/lib/ip/ruserok.c b/lib/ip/ruserok.c new file mode 100755 index 000000000..ad6651771 --- /dev/null +++ b/lib/ip/ruserok.c @@ -0,0 +1,187 @@ +/* ruserok() - hosts.equiv and .rhosts check Author: Kees J. Bot + * 25 May 2001 + * + * Under Minix one can use IP addresses, CIDR network blocks, and hostnames + * with wildcards in .rhosts files. Only the iruserok() interface can be + * used, and the IP address is reverse/forward crosschecked if a hostname + * match is done. Ruserok() is dead and buried. The superuser parameter is + * ignored, because it makes root too special. Most users on Minix can be + * root, so hosts.equiv would become useless if root can't use it. Likewise + * .rhosts isn't checked to be root or user owned and stuff, users have to + * be careful themselves. + */ + +#include <sys/types.h> +#include <string.h> +#include <stdio.h> +#include <ctype.h> +#include <limits.h> +#include <pwd.h> +#include <net/hton.h> +#include <net/gen/in.h> +#include <net/gen/netdb.h> +#include <net/gen/inet.h> +#include <net/gen/socket.h> +#include <net/gen/nameser.h> + +/* Odd global variable. Seems to be used by lpd(8). */ +int __check_rhosts_file = 1; + +static int cidr_aton(char *word, ipaddr_t *addr, ipaddr_t *mask) +/* Try to interpret 'word' as an CIDR spec, e.g. 172.16.102.64/27. */ +{ + char *slash; + int r; + static char S32[]= "/32"; + + if (*word == 0) return 0; + + if ((slash= strchr(word, '/')) == NULL) slash= S32; + + *slash= 0; + r= inet_aton(word, addr); + *slash++= '/'; + if (!r) return 0; + + r= 0; + while ((*slash - '0') < 10u) { + r= 10*r + (*slash++ - '0'); + if (r > 32) return 0; + } + if (*slash != 0 || slash[-1] == '/') return 0; + *mask= htonl(r == 0 ? 0L : (0xFFFFFFFFUL >> (32 - r)) << (32 - r)); + return 1; +} + +static int match(const char *word, const char *pattern) +/* Match word onto a pattern. Pattern may contain the * wildcard. */ +{ + unsigned cw, cp; +#define lc(c, d) ((((c)= (d)) - 'A') <= ('Z' - 'A') ? (c)+= ('a' - 'A') : 0) + + for (;;) { + lc(cw, *word); + lc(cp, *pattern); + + if (cp == '*') { + do pattern++; while (*pattern == '*'); + lc(cp, *pattern); + if (cp == 0) return 1; + + while (cw != 0) { + if (cw == cp && match(word+1, pattern+1)) return 1; + word++; + lc(cw, *word); + } + return 0; + } else + if (cw == 0 || cp == 0) { + return cw == cp; + } else + if (cw == cp) { + word++; + pattern++; + } else { + return 0; + } + } +#undef lc +} + +static int get_name(ipaddr_t addr, char *name) +/* Do a reverse lookup on the remote IP address followed by a forward lookup + * to check if the host has that address. Return true if this is so, return + * either the true name or the ascii IP address in name[]. + */ +{ + struct hostent *he; + int ok, i; + + ok= 0; + he= gethostbyaddr((char *) &addr, sizeof(addr), AF_INET); + if (he != NULL) { + strcpy(name, he->h_name); + he= gethostbyname(name); + + if (he != NULL && he->h_addrtype == AF_INET) { + for (i= 0; he->h_addr_list[i] != NULL; i++) { + if (memcmp(he->h_addr_list[i], &addr, sizeof(addr)) == 0) { + ok= 1; + break; + } + } + } + } + strcpy(name, ok ? he->h_name : inet_ntoa(addr)); + return ok; +} + +int __ivaliduser(FILE *hostf, unsigned long raddr, + const char *luser, const char *ruser) +{ + register char *p; + char buf[MAXDNAME + 128]; /* host + login */ + char rhost[MAXDNAME]; /* remote host */ + char *word[2]; + int i, ch, got_name; + ipaddr_t addr, mask; + + got_name = -1; + + while (fgets(buf, sizeof(buf), hostf)) { + /* Skip lines that are too long. */ + if (strchr(buf, '\n') == NULL) { + while ((ch = fgetc(hostf)) != '\n' && ch != EOF); + continue; + } + i = 0; + p = buf; + for (;;) { + while (isspace(*p)) *p++ = '\0'; + if (*p == '\0') break; + if (i < 2) word[i] = p; + i++; + while (*p != '\0' && !isspace(*p)) p++; + } + if (i != 1 && i != 2) continue; + if (word[0][0] == '#') continue; + if (strcmp(ruser, i == 2 ? word[1] : luser) != 0) continue; + + if (cidr_aton(word[0], &addr, &mask)) { + if (((raddr ^ addr) & mask) == 0) return (0); + continue; + } + + if (got_name == -1) got_name = get_name(raddr, rhost); + if (match(rhost, word[0])) return (0); + } + return (-1); +} + +int iruserok(unsigned long raddr, int superuser, + const char *ruser, const char *luser) +{ + /* Returns 0 if ok, -1 if not ok. */ + struct passwd *pwd; + FILE *hostf; + int i, r; + char pbuf[PATH_MAX]; + + for (i = 0; i < 2; i++) { + if (i == 0) { + strcpy(pbuf, _PATH_HEQUIV); + } else { + if (!__check_rhosts_file) return (-1); + if ((pwd = getpwnam(luser)) == NULL) return (-1); + (void)strcpy(pbuf, pwd->pw_dir); + (void)strcat(pbuf, "/.rhosts"); + } + + if ((hostf = fopen(pbuf, "r")) == NULL) return (-1); + + r = __ivaliduser(hostf, raddr, luser, ruser); + (void)fclose(hostf); + if (r == 0) return (0); + } + return (-1); +} diff --git a/lib/ip/servxcheck.c b/lib/ip/servxcheck.c new file mode 100755 index 000000000..a6229628a --- /dev/null +++ b/lib/ip/servxcheck.c @@ -0,0 +1,302 @@ +/* servxcheck() - Service access check. Author: Kees J. Bot + * 8 Jan 1997 + */ +#define nil 0 +#define ioctl _ioctl +#define open _open +#define write _write +#define close _close +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <fcntl.h> +#include <unistd.h> +#include <time.h> +#include <sys/ioctl.h> +#include <net/hton.h> +#include <net/gen/in.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_io.h> +#include <net/gen/inet.h> +#include <net/gen/socket.h> +#include <net/gen/netdb.h> + +/* Default service access file. */ +static const char *path_servacces = _PATH_SERVACCES; + +#define WLEN 256 + +static int getword(FILE *fp, char *word) +/* Read a word from the file open by 'fp', skip whitespace and comments. + * Colon and semicolon are returned as a one character "word". Returns + * word[0] or EOF. + */ +{ + int c; + char *pw; + int wc; + + wc= 0; + for (;;) { + if ((c= getc(fp)) == EOF) return EOF; + if (c == '#') { wc= 1; continue; } + if (c == '\n') { wc= 0; continue; } + if (wc) continue; + if (c <= ' ') continue; + break; + } + + pw= word; + if (c == ':' || c == ';') { + *pw++ = c; + } else { + do { + if (pw < word + WLEN-1) *pw++ = c; + c= getc(fp); + } while (c != EOF && c > ' ' && c != ':' && c != ';'); + if (c != EOF) ungetc(c, fp); + } + *pw= 0; + return word[0]; +} + +static int netspec(char *word, ipaddr_t *addr, ipaddr_t *mask) +/* Try to interpret 'word' as an network spec, e.g. 172.16.102.64/27. */ +{ + char *slash; + int r; + static char S32[]= "/32"; + + if (*word == 0) return 0; + + if ((slash= strchr(word, '/')) == NULL) slash= S32; + + *slash= 0; + r= inet_aton(word, addr); + *slash++= '/'; + if (!r) return 0; + + r= 0; + while ((*slash - '0') < 10u) { + r= 10*r + (*slash++ - '0'); + if (r > 32) return 0; + } + if (*slash != 0 || slash[-1] == '/') return 0; + *mask= htonl(r == 0 ? 0L : (0xFFFFFFFFUL >> (32 - r)) << (32 - r)); + return 1; +} + +static int match(const char *word, const char *pattern) +/* Match word onto a pattern. Pattern may contain the * wildcard. */ +{ + unsigned cw, cp; +#define lc(c, d) ((((c)= (d)) - 'A') <= ('Z' - 'A') ? (c)+= ('a' - 'A') : 0) + + for (;;) { + lc(cw, *word); + lc(cp, *pattern); + + if (cp == '*') { + do pattern++; while (*pattern == '*'); + lc(cp, *pattern); + if (cp == 0) return 1; + + while (cw != 0) { + if (cw == cp && match(word+1, pattern+1)) return 1; + word++; + lc(cw, *word); + } + return 0; + } else + if (cw == 0 || cp == 0) { + return cw == cp; + } else + if (cw == cp) { + word++; + pattern++; + } else { + return 0; + } + } +#undef lc +} + +static int get_name(ipaddr_t addr, char *name) +/* Do a reverse lookup on the remote IP address followed by a forward lookup + * to check if the host has that address. Return true if this is so, return + * either the true name or the ascii IP address in name[]. + */ +{ + struct hostent *he; + int i; + + he= gethostbyaddr((char *) &addr, sizeof(addr), AF_INET); + if (he != NULL) { + strcpy(name, he->h_name); + he= gethostbyname(name); + + if (he != NULL && he->h_addrtype == AF_INET) { + for (i= 0; he->h_addr_list[i] != NULL; i++) { + if (memcmp(he->h_addr_list[i], &addr, sizeof(addr)) == 0) { + strcpy(name, he->h_name); + return 1; + } + } + } + } + strcpy(name, inet_ntoa(addr)); + return 0; +} + +/* "state" and "log" flags, made to be bitwise comparable. */ +#define DEFFAIL 0x01 +#define FAIL (0x02 | DEFFAIL) +#define PASS 0x04 + +int servxcheck(ipaddr_t peer, const char *service, + void (*logf)(int pass, const char *name)) +{ + FILE *fp; + char word[WLEN]; + char name[WLEN]; + int c; + int got_name, slist, seen, explicit, state, log; + ipaddr_t addr, mask; + + /* Localhost? */ + if ((peer & HTONL(0xFF000000)) == HTONL(0x7F000000)) return 1; + + if ((fp= fopen(path_servacces, "r")) == nil) { + /* Succeed on error, fail if simply nonexistent. */ + return (errno != ENOENT); + } + + slist= 1; /* Services list (before the colon.) */ + seen= 0; /* Given service not yet seen. */ + explicit= 0; /* Service mentioned explicitly. */ + got_name= -1; /* No reverse lookup done yet. */ + log= FAIL; /* By default log failures only. */ + state= DEFFAIL; /* Access denied until we know better. */ + + while ((c= getword(fp, word)) != EOF) { + if (c == ':') { + slist= 0; /* Switch to access list. */ + } else + if (c == ';') { + slist= 1; /* Back to list of services. */ + seen= 0; + } else + if (slist) { + /* Traverse services list. */ + + if (match(service, word)) { + /* Service has been spotted! */ + if (match(word, service)) { + /* Service mentioned without wildcards. */ + seen= explicit= 1; + } else { + /* Matched by a wildcard. */ + if (!explicit) seen= 1; + } + } + } else { + /* Traverse access list. */ + + if (c == 'l' && strcmp(word, "log") == 0) { + if (seen) { + /* Log failures and successes. */ + log= FAIL|PASS; + } + continue; + } + + if (c != '-' && c != '+') { + if (logf == nil) { + fprintf(stderr, "%s: strange check word '%s'\n", + path_servacces, word); + } + continue; + } + + if (seen) { + if (state == DEFFAIL) { + /* First check determines the default. */ + state= c == '+' ? FAIL : PASS; + } + + if ((state == PASS) == (c == '+')) { + /* This check won't change state. */ + } else + if (word[1] == 0) { + /* Lone + or - allows all or none. */ + state= c == '-' ? FAIL : PASS; + } else + if (netspec(word+1, &addr, &mask)) { + /* Remote host is on the specified network? */ + if (((peer ^ addr) & mask) == 0) { + state= c == '-' ? FAIL : PASS; + } + } else { + /* Name check. */ + if (got_name == -1) { + got_name= get_name(peer, name); + } + + /* Remote host name matches the word? */ + if (!got_name) { + state= FAIL; + } else + if (match(name, word+1)) { + state= c == '-' ? FAIL : PASS; + } + } + } + } + } + fclose(fp); + + if ((log & state) != 0) { + /* Log the result of the check. */ + if (got_name == -1) (void) get_name(peer, name); + + if (logf != nil) { + (*logf)(state == PASS, name); + } else { + int lfd; + char line[128+WLEN]; + time_t t; + struct tm *tm; + char month[][4]= { + "Jan", "Feb", "Mar", "Apr", "May", "Jun", + "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", + }; + + if ((lfd= open("/usr/adm/log", O_WRONLY|O_APPEND)) != -1) { + time(&t); + tm= localtime(&t); + sprintf(line, "%s %02d %02d:%02d:%02d service '%s' %s to %s\n", + month[tm->tm_mon], + tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + service, + state == PASS ? "granted" : "denied", + name); + (void) write(lfd, line, strlen(line)); + close(lfd); + } + } + } + return state == PASS; +} + +char *servxfile(const char *file) +/* Specify a file to use for the access checks other than the default. Return + * the old path. + */ +{ + const char *oldpath= path_servacces; + path_servacces= file; + return (char *) oldpath; /* (avoid const poisoning) */ +} diff --git a/lib/ip/strcasecmp.c b/lib/ip/strcasecmp.c new file mode 100755 index 000000000..72556d502 --- /dev/null +++ b/lib/ip/strcasecmp.c @@ -0,0 +1,43 @@ +/* +strcasecmp.c + +Created Oct 14, 1991 by Philip Homburg +*/ + +#include <ctype.h> +#include <string.h> + +#ifdef __STDC__ +#define _CONST const +#else +#define _CONST +#endif + +int +strcasecmp(s1, s2) +_CONST char *s1, *s2; +{ + int c1, c2; + while (c1= toupper(*s1++), c2= toupper(*s2++), c1 == c2 && (c1 & c2)) + ; + if (c1 & c2) + return c1 < c2 ? -1 : 1; + return c1 ? 1 : (c2 ? -1 : 0); +} + +int +strncasecmp(s1, s2, len) +_CONST char *s1, *s2; +size_t len; +{ + int c1, c2; + do { + if (len == 0) + return 0; + len--; + } while (c1= toupper(*s1++), c2= toupper(*s2++), c1 == c2 && (c1 & c2)) + ; + if (c1 & c2) + return c1 < c2 ? -1 : 1; + return c1 ? 1 : (c2 ? -1 : 0); +} diff --git a/lib/libm2/Arguments.c b/lib/libm2/Arguments.c new file mode 100755 index 000000000..eaf696236 --- /dev/null +++ b/lib/libm2/Arguments.c @@ -0,0 +1,73 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: Access to program arguments and environment + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ + +extern char **argv, ***_penviron; +extern int argc; +unsigned int _Arguments__Argc; + +static char * +findname(s1, s2) +register char *s1, *s2; +{ + + while (*s1 == *s2++) s1++; + if (*s1 == '\0' && *(s2-1) == '=') return s2; + return 0; +} + +static unsigned int +scopy(src, dst, max) + register char *src, *dst; + unsigned int max; +{ + register unsigned int i = 0; + + while (*src && i <= max) { + i++; + *dst++ = *src++; + } + if (i <= max) { + *dst = '\0'; + return i+1; + } + while (*src++) i++; + return i + 1; +} + +_Arguments_() +{ + _Arguments__Argc = argc; +} + +unsigned +_Arguments__Argv(n, argument, l, u, s) + unsigned int u; + char *argument; +{ + + if (n >= argc) return 0; + return scopy(argv[n], argument, u); +} + +unsigned +_Arguments__GetEnv(name, nn, nu, ns, value, l, u, s) + char *name, *value; + unsigned int nu, u; +{ + register char **p = *_penviron; + register char *v = 0; + + while (*p && !(v = findname(name, *p++))) { + /* nothing */ + } + if (!v) return 0; + return scopy(v, value, u); +} diff --git a/lib/libm2/ArraySort.mod b/lib/libm2/ArraySort.mod new file mode 100755 index 000000000..147ca9e60 --- /dev/null +++ b/lib/libm2/ArraySort.mod @@ -0,0 +1,155 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE ArraySort; +(* + Module: Array sorting module. + Author: Ceriel J.H. Jacobs + Version: $Header$ +*) + FROM SYSTEM IMPORT ADDRESS, BYTE; (* no generics in Modula-2, sorry *) + + TYPE BytePtr = POINTER TO BYTE; + + VAR compareproc: CompareProc; + + PROCEDURE Sort(base: ADDRESS; (* address of array *) + nel: CARDINAL; (* number of elements in array *) + size: CARDINAL; (* size of each element *) + compar: CompareProc); (* the comparison procedure *) + BEGIN + compareproc := compar; + qsort(base, base+(nel-1)*size, size); + END Sort; + + PROCEDURE qsort(a1, a2: ADDRESS; size: CARDINAL); + (* Implemented with quick-sort, with some extra's *) + VAR left, right, lefteq, righteq: ADDRESS; + cmp: CompareResult; + mainloop: BOOLEAN; + BEGIN + WHILE a2 > a1 DO + left := a1; + right := a2; + lefteq := a1 + size * (((a2 - a1) + size) DIV (2 * size)); + righteq := lefteq; + (* + Pick an element in the middle of the array. + We will collect the equals around it. + "lefteq" and "righteq" indicate the left and right + bounds of the equals respectively. + Smaller elements end up left of it, larger elements end + up right of it. + *) + LOOP + LOOP + IF left >= lefteq THEN EXIT END; + cmp := compareproc(left, lefteq); + IF cmp = greater THEN EXIT END; + IF cmp = less THEN + left := left + size; + ELSE + (* equal, so exchange with the element + to the left of the "equal"-interval. + *) + lefteq := lefteq - size; + exchange(left, lefteq, size); + END; + END; + mainloop := FALSE; + LOOP + IF right <= righteq THEN EXIT END; + cmp := compareproc(right, righteq); + IF cmp = less THEN + IF left < lefteq THEN + (* larger one at the left, + so exchange + *) + exchange(left,right,size); + left := left + size; + right := right - size; + mainloop := TRUE; + EXIT; + END; + (* + no more room at the left part, so we + move the "equal-interval" one place to the + right, and the smaller element to the + left of it. + This is best expressed as a three-way + exchange. + *) + righteq := righteq + size; + threewayexchange(left, righteq, right, + size); + lefteq := lefteq + size; + left := lefteq; + ELSIF cmp = equal THEN + (* equal, zo exchange with the element + to the right of the "equal" + interval + *) + righteq := righteq + size; + exchange(right, righteq, size); + ELSE + (* leave it where it is *) + right := right - size; + END; + END; + IF (NOT mainloop) THEN + IF left >= lefteq THEN + (* sort "smaller" part *) + qsort(a1, lefteq - size, size); + (* and now the "larger" part, saving a + procedure call, because of this big + WHILE loop + *) + a1 := righteq + size; + EXIT; (* from the LOOP *) + END; + (* larger element to the left, but no more room, + so move the "equal-interval" one place to the + left, and the larger element to the right + of it. + *) + lefteq := lefteq - size; + threewayexchange(right, lefteq, left, size); + righteq := righteq - size; + right := righteq; + END; + END; + END; + END qsort; + + PROCEDURE exchange(a,b: BytePtr; size : CARDINAL); + VAR c: BYTE; + BEGIN + WHILE size > 0 DO + DEC(size); + c := a^; + a^ := b^; + a := ADDRESS(a) + 1; + b^ := c; + b := ADDRESS(b) + 1; + END; + END exchange; + + PROCEDURE threewayexchange(p,q,r: BytePtr; size: CARDINAL); + VAR c: BYTE; + BEGIN + WHILE size > 0 DO + DEC(size); + c := p^; + p^ := r^; + p := ADDRESS(p) + 1; + r^ := q^; + r := ADDRESS(r) + 1; + q^ := c; + q := ADDRESS(q) + 1; + END; + END threewayexchange; + +END ArraySort. diff --git a/lib/libm2/CSP.mod b/lib/libm2/CSP.mod new file mode 100755 index 000000000..7a50df425 --- /dev/null +++ b/lib/libm2/CSP.mod @@ -0,0 +1,347 @@ +(*$R-*) +IMPLEMENTATION MODULE CSP; +(* + Module: Communicating Sequential Processes + From: "A Modula-2 Implementation of CSP", + M. Collado, R. Morales, J.J. Moreno, + SIGPlan Notices, Volume 22, Number 6, June 1987. + Some modifications by Ceriel J.H. Jacobs + Version: $Header$ + + See this article for an explanation of the use of this module. +*) + + FROM random IMPORT Uniform; + FROM SYSTEM IMPORT BYTE, ADDRESS, NEWPROCESS, TRANSFER; + FROM Storage IMPORT Allocate, Deallocate; + FROM Traps IMPORT Message; + + CONST WorkSpaceSize = 2000; + + TYPE ByteAddress = POINTER TO BYTE; + Channel = POINTER TO ChannelDescriptor; + ProcessType = POINTER TO ProcessDescriptor; + ProcessDescriptor = RECORD + next: ProcessType; + father: ProcessType; + cor: ADDRESS; + wsp: ADDRESS; + guardindex: INTEGER; + guardno: CARDINAL; + guardcount: CARDINAL; + opened: Channel; + sons: CARDINAL; + msgadr: ADDRESS; + msglen: CARDINAL; + END; + + Queue = RECORD + head, tail: ProcessType; + END; + + ChannelDescriptor = RECORD + senders: Queue; + owner: ProcessType; + guardindex: INTEGER; + next: Channel; + END; + + VAR cp: ProcessType; + free, ready: Queue; + +(* ------------ Private modules and procedures ------------- *) + + MODULE ProcessQueue; + + IMPORT ProcessType, Queue; + EXPORT Push, Pop, InitQueue, IsEmpty; + + PROCEDURE InitQueue(VAR q: Queue); + BEGIN + WITH q DO + head := NIL; + tail := NIL + END + END InitQueue; + + PROCEDURE Push(p: ProcessType; VAR q: Queue); + BEGIN + p^.next := NIL; + WITH q DO + IF head = NIL THEN + tail := p + ELSE + head^.next := p + END; + head := p + END + END Push; + + PROCEDURE Pop(VAR q: Queue; VAR p: ProcessType); + BEGIN + WITH q DO + p := tail; + IF p # NIL THEN + tail := tail^.next; + IF head = p THEN + head := NIL + END + END + END + END Pop; + + PROCEDURE IsEmpty(q: Queue): BOOLEAN; + BEGIN + RETURN q.head = NIL + END IsEmpty; + + END ProcessQueue; + + + PROCEDURE DoTransfer; + VAR aux: ProcessType; + BEGIN + aux := cp; + Pop(ready, cp); + IF cp = NIL THEN + HALT + ELSE + TRANSFER(aux^.cor, cp^.cor) + END + END DoTransfer; + + PROCEDURE OpenChannel(ch: Channel; n: INTEGER); + BEGIN + WITH ch^ DO + IF guardindex = 0 THEN + guardindex := n; + next := cp^.opened; + cp^.opened := ch + END + END + END OpenChannel; + + PROCEDURE CloseChannels(p: ProcessType); + BEGIN + WITH p^ DO + WHILE opened # NIL DO + opened^.guardindex := 0; + opened := opened^.next + END + END + END CloseChannels; + + PROCEDURE ThereAreOpenChannels(): BOOLEAN; + BEGIN + RETURN cp^.opened # NIL; + END ThereAreOpenChannels; + + PROCEDURE Sending(ch: Channel): BOOLEAN; + BEGIN + RETURN NOT IsEmpty(ch^.senders) + END Sending; + +(* -------------- Public Procedures ----------------- *) + + PROCEDURE COBEGIN; + (* Beginning of a COBEGIN .. COEND structure *) + BEGIN + END COBEGIN; + + PROCEDURE COEND; + (* End of a COBEGIN .. COEND structure *) + (* VAR aux: ProcessType; *) + BEGIN + IF cp^.sons > 0 THEN + DoTransfer + END + END COEND; + + PROCEDURE StartProcess(P: PROC); + (* Start an anonimous process that executes the procedure P *) + VAR newprocess: ProcessType; + BEGIN + Pop(free, newprocess); + IF newprocess = NIL THEN + Allocate(newprocess,SIZE(ProcessDescriptor)); + Allocate(newprocess^.wsp, WorkSpaceSize) + END; + WITH newprocess^ DO + father := cp; + sons := 0; + msglen := 0; + NEWPROCESS(P, wsp, WorkSpaceSize, cor) + END; + cp^.sons := cp^.sons + 1; + Push(newprocess, ready) + END StartProcess; + + PROCEDURE StopProcess; + (* Terminate a Process (itself) *) + VAR aux: ProcessType; + BEGIN + aux := cp^.father; + aux^.sons := aux^.sons - 1; + IF aux^.sons = 0 THEN + Push(aux, ready) + END; + aux := cp; + Push(aux, free); + Pop(ready, cp); + IF cp = NIL THEN + HALT + ELSE + TRANSFER(aux^.cor, cp^.cor) + END + END StopProcess; + + PROCEDURE InitChannel(VAR ch: Channel); + (* Initialize the channel ch *) + BEGIN + Allocate(ch, SIZE(ChannelDescriptor)); + WITH ch^ DO + InitQueue(senders); + owner := NIL; + next := NIL; + guardindex := 0 + END + END InitChannel; + + PROCEDURE GetChannel(ch: Channel); + (* Assign the channel ch to the process that gets it *) + BEGIN + WITH ch^ DO + IF owner # NIL THEN + Message("Channel already has an owner"); + HALT + END; + owner := cp + END + END GetChannel; + + PROCEDURE Send(data: ARRAY OF BYTE; VAR ch: Channel); + (* Send a message with the data to the cvhannel ch *) + VAR m: ByteAddress; + (* aux: ProcessType; *) + i: CARDINAL; + BEGIN + WITH ch^ DO + Push(cp, senders); + Allocate(cp^.msgadr, SIZE(data)); + m := cp^.msgadr; + cp^.msglen := HIGH(data); + FOR i := 0 TO HIGH(data) DO + m^ := data[i]; + m := ADDRESS(m) + 1 + END; + IF guardindex # 0 THEN + owner^.guardindex := guardindex; + CloseChannels(owner); + Push(owner, ready) + END + END; + DoTransfer + END Send; + + PROCEDURE Receive(VAR ch: Channel; VAR dest: ARRAY OF BYTE); + (* Receive a message from the channel ch into the dest variable *) + VAR aux: ProcessType; + m: ByteAddress; + i: CARDINAL; + BEGIN + WITH ch^ DO + IF cp # owner THEN + Message("Only owner of channel can receive from it"); + HALT + END; + IF Sending(ch) THEN + Pop(senders, aux); + m := aux^.msgadr; + FOR i := 0 TO aux^.msglen DO + dest[i] := m^; + m := ADDRESS(m) + 1 + END; + Push(aux, ready); + Push(cp, ready); + CloseChannels(cp) + ELSE + OpenChannel(ch, -1); + DoTransfer; + Pop(senders, aux); + m := aux^.msgadr; + FOR i := 0 TO aux^.msglen DO + dest[i] := m^; + m := ADDRESS(m) + 1 + END; + Push(cp, ready); + Push(aux, ready) + END; + Deallocate(aux^.msgadr, aux^.msglen+1); + DoTransfer + END + END Receive; + + PROCEDURE SELECT(n: CARDINAL); + (* Beginning of a SELECT structure with n guards *) + BEGIN + cp^.guardindex := Uniform(1,n); + cp^.guardno := n; + cp^.guardcount := n + END SELECT; + + PROCEDURE NEXTGUARD(): CARDINAL; + (* Returns an index to the next guard to be evaluated in a SELECT *) + BEGIN + RETURN cp^.guardindex + END NEXTGUARD; + + PROCEDURE GUARD(cond: BOOLEAN; ch: Channel; + VAR dest: ARRAY OF BYTE): BOOLEAN; + (* Evaluates a guard, including reception management *) + (* VAR aux: ProcessType; *) + BEGIN + IF NOT cond THEN + RETURN FALSE + ELSIF ch = NIL THEN + CloseChannels(cp); + cp^.guardindex := 0; + RETURN TRUE + ELSIF Sending(ch) THEN + Receive(ch, dest); + cp^.guardindex := 0; + RETURN TRUE + ELSE + OpenChannel(ch, cp^.guardindex); + RETURN FALSE + END + END GUARD; + + PROCEDURE ENDSELECT(): BOOLEAN; + (* End of a SELECT structure *) + BEGIN + WITH cp^ DO + IF guardindex <= 0 THEN + RETURN TRUE + END; + guardcount := guardcount - 1; + IF guardcount # 0 THEN + guardindex := (guardindex MOD INTEGER(guardno)) + 1 + ELSIF ThereAreOpenChannels() THEN + DoTransfer + ELSE + guardindex := 0 + END + END; + RETURN FALSE + END ENDSELECT; + +BEGIN + InitQueue(free); + InitQueue(ready); + Allocate(cp,SIZE(ProcessDescriptor)); + WITH cp^ DO + sons := 0; + father := NIL + END +END CSP. + diff --git a/lib/libm2/Conversion.mod b/lib/libm2/Conversion.mod new file mode 100755 index 000000000..b64ebc42c --- /dev/null +++ b/lib/libm2/Conversion.mod @@ -0,0 +1,73 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE Conversions; +(* + Module: numeric-to-string conversions + Author: Ceriel J.H. Jacobs + Version: $Header$ +*) + + PROCEDURE ConvertNum(num, len, base: CARDINAL; + neg: BOOLEAN; + VAR str: ARRAY OF CHAR); + VAR i: CARDINAL; + r: CARDINAL; + tmp: ARRAY [0..20] OF CHAR; + BEGIN + i := 0; + REPEAT + r := num MOD base; + num := num DIV base; + IF r <= 9 THEN + tmp[i] := CHR(r + ORD('0')); + ELSE + tmp[i] := CHR(r - 10 + ORD('A')); + END; + INC(i); + UNTIL num = 0; + IF neg THEN + tmp[i] := '-'; + INC(i) + END; + IF len > HIGH(str) + 1 THEN len := HIGH(str) + 1; END; + IF i > HIGH(str) + 1 THEN i := HIGH(str) + 1; END; + r := 0; + WHILE len > i DO str[r] := ' '; INC(r); DEC(len); END; + WHILE i > 0 DO str[r] := tmp[i-1]; DEC(i); INC(r); END; + WHILE r <= HIGH(str) DO + str[r] := 0C; + INC(r); + END; + END ConvertNum; + + PROCEDURE ConvertOctal(num, len: CARDINAL; VAR str: ARRAY OF CHAR); + BEGIN + ConvertNum(num, len, 8, FALSE, str); + END ConvertOctal; + + PROCEDURE ConvertHex(num, len: CARDINAL; VAR str: ARRAY OF CHAR); + BEGIN + ConvertNum(num, len, 16, FALSE, str); + END ConvertHex; + + PROCEDURE ConvertCardinal(num, len: CARDINAL; VAR str: ARRAY OF CHAR); + BEGIN + ConvertNum(num, len, 10, FALSE, str); + END ConvertCardinal; + + PROCEDURE ConvertInteger(num: INTEGER; + len: CARDINAL; + VAR str: ARRAY OF CHAR); + BEGIN + IF (num < 0) AND (num >= -MAX(INTEGER)) THEN + ConvertNum(-num, len, 10, TRUE, str); + ELSE + ConvertNum(CARDINAL(num), len, 10, num < 0, str); + END; + END ConvertInteger; + +END Conversions. diff --git a/lib/libm2/EM.e b/lib/libm2/EM.e new file mode 100755 index 000000000..ae6f42e62 --- /dev/null +++ b/lib/libm2/EM.e @@ -0,0 +1,100 @@ +# +; +; (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. +; See the copyright notice in the ACK home directory, in the file "Copyright". +; +; +; Module: Interface to some EM instructions and data +; Author: Ceriel J.H. Jacobs +; Version: $Header$ +; + mes 2,_EM_WSIZE,_EM_PSIZE + +#define ARG1 0 +#define ARG2 _EM_DSIZE +#define IRES 2*_EM_DSIZE + +; FIF is called with three parameters: +; - address of integer part result (IRES) +; - float two (ARG2) +; - float one (ARG1) +; and returns an _EM_DSIZE-byte floating point number +; Definition: +; PROCEDURE FIF(ARG1, ARG2: LONGREAL; VAR IRES: LONGREAL) : LONGREAL; + + exp $FIF + pro $FIF,0 + lal 0 + loi 2*_EM_DSIZE + fif _EM_DSIZE + lal IRES + loi _EM_PSIZE + sti _EM_DSIZE + ret _EM_DSIZE + end ? + +#define FARG 0 +#define ERES _EM_DSIZE + +; FEF is called with two parameters: +; - address of base 2 exponent result (ERES) +; - floating point number to be split (FARG) +; and returns an _EM_DSIZE-byte floating point number (the mantissa) +; Definition: +; PROCEDURE FEF(FARG: LONGREAL; VAR ERES: integer): LONGREAL; + + exp $FEF + pro $FEF,0 + lal FARG + loi _EM_DSIZE + fef _EM_DSIZE + lal ERES + loi _EM_PSIZE + sti _EM_WSIZE + ret _EM_DSIZE + end ? + +#define TRAP 0 + +; TRP is called with one parameter: +; - trap number (TRAP) +; Definition: +; PROCEDURE TRP(trapno: INTEGER); + + exp $TRP + pro $TRP, 0 + lol TRAP + trp + ret 0 + end ? + +#define PROC 0 + +; SIG is called with one parameter: +; - procedure instance identifier (PROC) +; and returns the old traphandler. + + exa handler + exp $SIG + pro $SIG, 0 + lae handler + loi _EM_PSIZE + lal PROC + loi _EM_PSIZE + lae handler + sti _EM_PSIZE + ret _EM_PSIZE + end ? + + exp $LINO + pro $LINO,0 + loe 0 + ret _EM_WSIZE + end ? + + exp $FILN + pro $FILN,0 + lae 4 + loi _EM_PSIZE + ret _EM_PSIZE + end ? diff --git a/lib/libm2/InOut.mod b/lib/libm2/InOut.mod new file mode 100755 index 000000000..74e56af52 --- /dev/null +++ b/lib/libm2/InOut.mod @@ -0,0 +1,371 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE InOut ; +(* + Module: Wirth's Input/Output module + Author: Ceriel J.H. Jacobs + Version: $Header$ +*) + + IMPORT Streams; + FROM Conversions IMPORT + ConvertCardinal, ConvertInteger, + ConvertOctal, ConvertHex; + FROM Traps IMPORT Message; + + CONST TAB = 11C; + + TYPE numbuf = ARRAY[0..255] OF CHAR; + + VAR unread: BOOLEAN; + unreadch: CHAR; + CurrIn, CurrOut: Streams.Stream; + result: Streams.StreamResult; + + PROCEDURE Read(VAR c : CHAR); + + BEGIN + IF unread THEN + unread := FALSE; + c := unreadch; + Done := TRUE; + ELSE + Streams.Read(CurrIn, c, result); + Done := result = Streams.succeeded; + END; + END Read; + + PROCEDURE UnRead(ch: CHAR); + BEGIN + unread := TRUE; + unreadch := ch; + END UnRead; + + PROCEDURE Write(c: CHAR); + BEGIN + Streams.Write(CurrOut, c, result); + END Write; + + PROCEDURE OpenInput(defext: ARRAY OF CHAR); + VAR namebuf : ARRAY [1..128] OF CHAR; + BEGIN + IF CurrIn # Streams.InputStream THEN + Streams.CloseStream(CurrIn, result); + END; + MakeFileName("Name of input file: ", defext, namebuf); + IF NOT Done THEN RETURN; END; + openinput(namebuf); + END OpenInput; + + PROCEDURE OpenInputFile(filename: ARRAY OF CHAR); + BEGIN + IF CurrIn # Streams.InputStream THEN + Streams.CloseStream(CurrIn, result); + END; + openinput(filename); + END OpenInputFile; + + PROCEDURE openinput(namebuf: ARRAY OF CHAR); + BEGIN + IF (namebuf[0] = '-') AND (namebuf[1] = 0C) THEN + CurrIn := Streams.InputStream; + Done := TRUE; + ELSE + Streams.OpenStream(CurrIn, namebuf, Streams.text, + Streams.reading, result); + Done := result = Streams.succeeded; + END; + END openinput; + + PROCEDURE CloseInput; + BEGIN + IF CurrIn # Streams.InputStream THEN + Streams.CloseStream(CurrIn, result); + END; + CurrIn := Streams.InputStream; + END CloseInput; + + PROCEDURE OpenOutput(defext: ARRAY OF CHAR); + VAR namebuf : ARRAY [1..128] OF CHAR; + BEGIN + IF CurrOut # Streams.OutputStream THEN + Streams.CloseStream(CurrOut, result); + END; + MakeFileName("Name of output file: ", defext, namebuf); + IF NOT Done THEN RETURN; END; + openoutput(namebuf); + END OpenOutput; + + PROCEDURE OpenOutputFile(filename: ARRAY OF CHAR); + BEGIN + IF CurrOut # Streams.OutputStream THEN + Streams.CloseStream(CurrOut, result); + END; + openoutput(filename); + END OpenOutputFile; + + PROCEDURE openoutput(namebuf: ARRAY OF CHAR); + BEGIN + IF (namebuf[1] = '-') AND (namebuf[2] = 0C) THEN + CurrOut := Streams.OutputStream; + Done := TRUE; + ELSE + Streams.OpenStream(CurrOut, namebuf, Streams.text, + Streams.writing, result); + Done := result = Streams.succeeded; + END; + END openoutput; + + PROCEDURE CloseOutput; + BEGIN + IF CurrOut # Streams.OutputStream THEN + Streams.CloseStream(CurrOut, result); + END; + CurrOut := Streams.OutputStream; + END CloseOutput; + + PROCEDURE MakeFileName(prompt, defext : ARRAY OF CHAR; + VAR buf : ARRAY OF CHAR); + VAR i : INTEGER; + j : CARDINAL; + BEGIN + Done := TRUE; + IF Streams.isatty(Streams.InputStream, result) THEN + XWriteString(prompt); + END; + XReadString(buf); + i := 0; + WHILE buf[i] # 0C DO i := i + 1 END; + IF i # 0 THEN + i := i - 1; + IF buf[i] = '.' THEN + FOR j := 0 TO HIGH(defext) DO + i := i + 1; + buf[i] := defext[j]; + END; + buf[i+1] := 0C; + END; + RETURN; + END; + Done := FALSE; + END MakeFileName; + + PROCEDURE ReadInt(VAR integ : INTEGER); + CONST + SAFELIMITDIV10 = MAX(INTEGER) DIV 10; + SAFELIMITREM10 = MAX(INTEGER) MOD 10; + TYPE + itype = [0..31]; + ibuf = ARRAY itype OF CHAR; + VAR + int : INTEGER; + neg : BOOLEAN; + safedigit: [0 .. 9]; + chvalue: CARDINAL; + buf : ibuf; + index : itype; + BEGIN + ReadString(buf); + IF NOT Done THEN + RETURN + END; + index := 0; + IF buf[index] = '-' THEN + neg := TRUE; + INC(index); + ELSIF buf[index] = '+' THEN + neg := FALSE; + INC(index); + ELSE + neg := FALSE + END; + + safedigit := SAFELIMITREM10; + IF neg THEN safedigit := safedigit + 1 END; + int := 0; + WHILE (buf[index] >= '0') & (buf[index] <= '9') DO + chvalue := ORD(buf[index]) - ORD('0'); + IF (int > SAFELIMITDIV10) OR + ( (int = SAFELIMITDIV10) AND + (chvalue > safedigit)) THEN + Message("integer too large"); + HALT; + ELSE + int := 10*int + VAL(INTEGER, chvalue); + INC(index) + END; + END; + IF neg THEN + integ := -int + ELSE + integ := int + END; + IF buf[index] > " " THEN + Message("illegal integer"); + HALT; + END; + Done := TRUE; + END ReadInt; + + PROCEDURE ReadCard(VAR card : CARDINAL); + CONST + SAFELIMITDIV10 = MAX(CARDINAL) DIV 10; + SAFELIMITREM10 = MAX(CARDINAL) MOD 10; + + TYPE + itype = [0..31]; + ibuf = ARRAY itype OF CHAR; + + VAR + int : CARDINAL; + index : itype; + buf : ibuf; + safedigit: [0 .. 9]; + chvalue: CARDINAL; + BEGIN + ReadString(buf); + IF NOT Done THEN RETURN; END; + index := 0; + safedigit := SAFELIMITREM10; + int := 0; + WHILE (buf[index] >= '0') & (buf[index] <= '9') DO + chvalue := ORD(buf[index]) - ORD('0'); + IF (int > SAFELIMITDIV10) OR + ( (int = SAFELIMITDIV10) AND + (chvalue > safedigit)) THEN + Message("cardinal too large"); + HALT; + ELSE + int := 10*int + chvalue; + INC(index); + END; + END; + IF buf[index] > " " THEN + Message("illegal cardinal"); + HALT; + END; + card := int; + Done := TRUE; + END ReadCard; + + PROCEDURE ReadString(VAR s : ARRAY OF CHAR); + TYPE charset = SET OF CHAR; + VAR i : CARDINAL; + ch : CHAR; + + BEGIN + i := 0; + REPEAT + Read(ch); + UNTIL NOT (ch IN charset{' ', TAB, 12C, 15C}); + IF NOT Done THEN + RETURN; + END; + UnRead(ch); + REPEAT + Read(ch); + termCH := ch; + IF i <= HIGH(s) THEN + s[i] := ch; + IF (NOT Done) OR (ch <= " ") THEN + s[i] := 0C; + END; + END; + INC(i); + UNTIL (NOT Done) OR (ch <= " "); + IF Done THEN UnRead(ch); END; + END ReadString; + + PROCEDURE XReadString(VAR s : ARRAY OF CHAR); + VAR j : CARDINAL; + ch : CHAR; + + BEGIN + j := 0; + LOOP + Streams.Read(Streams.InputStream, ch, result); + IF result # Streams.succeeded THEN + EXIT; + END; + IF ch <= " " THEN + s[j] := 0C; + EXIT; + END; + IF j < HIGH(s) THEN + s[j] := ch; + INC(j); + END; + END; + END XReadString; + + PROCEDURE XWriteString(s: ARRAY OF CHAR); + VAR i: CARDINAL; + BEGIN + i := 0; + LOOP + IF (i <= HIGH(s)) AND (s[i] # 0C) THEN + Streams.Write(Streams.OutputStream, s[i], result); + INC(i); + ELSE + EXIT; + END; + END; + END XWriteString; + + PROCEDURE WriteCard(card, width : CARDINAL); + VAR + buf : numbuf; + BEGIN + ConvertCardinal(card, width, buf); + WriteString(buf); + END WriteCard; + + PROCEDURE WriteInt(int : INTEGER; width : CARDINAL); + VAR + buf : numbuf; + BEGIN + ConvertInteger(int, width, buf); + WriteString(buf); + END WriteInt; + + PROCEDURE WriteHex(card, width : CARDINAL); + VAR + buf : numbuf; + BEGIN + ConvertHex(card, width, buf); + WriteString(buf); + END WriteHex; + + PROCEDURE WriteLn; + BEGIN + Write(EOL) + END WriteLn; + + PROCEDURE WriteOct(card, width : CARDINAL); + VAR + buf : numbuf; + BEGIN + ConvertOctal(card, width, buf); + WriteString(buf); + END WriteOct; + + PROCEDURE WriteString(str : ARRAY OF CHAR); + VAR + nbytes : CARDINAL; + BEGIN + nbytes := 0; + WHILE (nbytes <= HIGH(str)) AND (str[nbytes] # 0C) DO + Write(str[nbytes]); + INC(nbytes) + END; + END WriteString; + +BEGIN (* InOut initialization *) + CurrIn := Streams.InputStream; + CurrOut := Streams.OutputStream; + unread := FALSE; +END InOut. diff --git a/lib/libm2/LtoUset.e b/lib/libm2/LtoUset.e new file mode 100755 index 000000000..a724aff44 --- /dev/null +++ b/lib/libm2/LtoUset.e @@ -0,0 +1,61 @@ +# +; +; (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. +; See the copyright notice in the ACK home directory, in the file "Copyright". +; +; +; Module: Compute non-constant set displays +; Author: Ceriel J.H. Jacobs +; Version: $Header$ +; + mes 2,_EM_WSIZE,_EM_PSIZE + + ; LtoUset is called for set displays containing { expr1 .. expr2 }. + ; It has six parameters, of which the caller must pop five: + ; - The set in which bits must be set. + ; - the lower bound of the set type. + ; - The set size in bytes. + ; - The upper bound of set elements, specified by the set-type. + ; - "expr2", the upper bound + ; - "expr1", the lower bound + +#define SETBASE 5*_EM_WSIZE +#define SETLOW 4*_EM_WSIZE +#define SETSIZE 3*_EM_WSIZE +#define USETSIZ 2*_EM_WSIZE +#define LWB _EM_WSIZE +#define UPB 0 + exp $LtoUset + pro $LtoUset,0 + lal SETBASE ; address of initial set + lol SETSIZE + los _EM_WSIZE ; load initial set + lol LWB ; low bound + lol SETLOW + sbu _EM_WSIZE + stl LWB + lol UPB ; high bound + lol SETLOW + sbu _EM_WSIZE + stl UPB +1 + lol LWB + lol UPB + cmu _EM_WSIZE + zgt *2 ; while low <= high + lol LWB + lol SETSIZE + set ? ; create [low] + lol SETSIZE + ior ? ; merge with initial set + lol LWB + loc 1 + adu _EM_WSIZE + stl LWB + bra *1 ; loop back +2 + lal SETBASE + lol SETSIZE + sts _EM_WSIZE ; store result over initial set + ret 0 + end 0 diff --git a/lib/libm2/Makefile b/lib/libm2/Makefile new file mode 100755 index 000000000..17b2fffdb --- /dev/null +++ b/lib/libm2/Makefile @@ -0,0 +1,204 @@ +# Makefile for lib/ack/libm2. + +M2 = m2 +CFLAGS = -O -I../h -wo +CC1 = $(CC) $(CFLAGS) -c +M2FLAGS = -O -ws -n +M21 = $(M2) $(M2FLAGS) -c + +LIBRARY = ../libm2.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(Arguments.o) \ + $(LIBRARY)(ArraySort.o) \ + $(LIBRARY)(CSP.o) \ + $(LIBRARY)(Conversion.o) \ + $(LIBRARY)(EM.o) \ + $(LIBRARY)(InOut.o) \ + $(LIBRARY)(LtoUset.o) \ + $(LIBRARY)(MathLib0.o) \ + $(LIBRARY)(Mathlib.o) \ + $(LIBRARY)(PascalIO.o) \ + $(LIBRARY)(Processes.o) \ + $(LIBRARY)(RealConver.o) \ + $(LIBRARY)(RealInOut.o) \ + $(LIBRARY)(SYSTEM.o) \ + $(LIBRARY)(Semaphores.o) \ + $(LIBRARY)(Storage.o) \ + $(LIBRARY)(StrAss.o) \ + $(LIBRARY)(Streams.o) \ + $(LIBRARY)(Strings.o) \ + $(LIBRARY)(Termcap.o) \ + $(LIBRARY)(Terminal.o) \ + $(LIBRARY)(Traps.o) \ + $(LIBRARY)(XXTermcap.o) \ + $(LIBRARY)(absd.o) \ + $(LIBRARY)(absf.o) \ + $(LIBRARY)(absi.o) \ + $(LIBRARY)(absl.o) \ + $(LIBRARY)(blockmove.o) \ + $(LIBRARY)(cap.o) \ + $(LIBRARY)(catch.o) \ + $(LIBRARY)(confarray.o) \ + $(LIBRARY)(dvi.o) \ + $(LIBRARY)(halt.o) \ + $(LIBRARY)(head_m2.o) \ + $(LIBRARY)(init.o) \ + $(LIBRARY)(load.o) \ + $(LIBRARY)(par_misc.o) \ + $(LIBRARY)(random.o) \ + $(LIBRARY)(rcka.o) \ + $(LIBRARY)(rcki.o) \ + $(LIBRARY)(rckil.o) \ + $(LIBRARY)(rcku.o) \ + $(LIBRARY)(rckul.o) \ + $(LIBRARY)(sigtrp.o) \ + $(LIBRARY)(stackprio.o) \ + $(LIBRARY)(store.o) \ + $(LIBRARY)(ucheck.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(Arguments.o): Arguments.c + $(CC1) Arguments.c + +$(LIBRARY)(ArraySort.o): ArraySort.mod + $(M21) ArraySort.mod + +$(LIBRARY)(CSP.o): CSP.mod + $(M21) CSP.mod + +$(LIBRARY)(Conversion.o): Conversion.mod + $(M21) Conversion.mod + +$(LIBRARY)(EM.o): EM.e + $(CC1) EM.e + +$(LIBRARY)(InOut.o): InOut.mod + $(M21) InOut.mod + +$(LIBRARY)(LtoUset.o): LtoUset.e + $(CC1) LtoUset.e + +$(LIBRARY)(MathLib0.o): MathLib0.mod + $(M21) MathLib0.mod + +$(LIBRARY)(Mathlib.o): Mathlib.mod + $(M21) Mathlib.mod + +$(LIBRARY)(PascalIO.o): PascalIO.mod + $(M21) PascalIO.mod + +$(LIBRARY)(Processes.o): Processes.mod + $(M21) Processes.mod + +$(LIBRARY)(RealConver.o): RealConver.mod + $(M21) RealConver.mod + +$(LIBRARY)(RealInOut.o): RealInOut.mod + $(M21) RealInOut.mod + +$(LIBRARY)(SYSTEM.o): SYSTEM.c + $(CC1) SYSTEM.c + +$(LIBRARY)(Semaphores.o): Semaphores.mod + $(M21) Semaphores.mod + +$(LIBRARY)(Storage.o): Storage.mod + $(M21) Storage.mod + +$(LIBRARY)(StrAss.o): StrAss.c + $(CC1) StrAss.c + +$(LIBRARY)(Streams.o): Streams.mod + $(M21) Streams.mod + +$(LIBRARY)(Strings.o): Strings.mod + $(M21) Strings.mod + +$(LIBRARY)(Termcap.o): Termcap.mod + $(M21) Termcap.mod + +$(LIBRARY)(Terminal.o): Terminal.mod + $(M21) -D__USG Terminal.mod + +$(LIBRARY)(Traps.o): Traps.mod + $(M21) Traps.mod + +$(LIBRARY)(XXTermcap.o): XXTermcap.c + $(CC1) XXTermcap.c + +$(LIBRARY)(absd.o): absd.c + $(CC1) absd.c + +$(LIBRARY)(absf.o): absf.e + $(CC1) absf.e + +$(LIBRARY)(absi.o): absi.c + $(CC1) absi.c + +$(LIBRARY)(absl.o): absl.c + $(CC1) absl.c + +$(LIBRARY)(blockmove.o): blockmove.c + $(CC1) blockmove.c + +$(LIBRARY)(cap.o): cap.c + $(CC1) cap.c + +$(LIBRARY)(catch.o): catch.c + $(CC1) catch.c + +$(LIBRARY)(confarray.o): confarray.c + $(CC1) confarray.c + +$(LIBRARY)(dvi.o): dvi.c + $(CC1) dvi.c + +$(LIBRARY)(halt.o): halt.c + $(CC1) halt.c + +$(LIBRARY)(head_m2.o): head_m2.e + $(CC1) head_m2.e + +$(LIBRARY)(init.o): init.c + $(CC1) init.c + +$(LIBRARY)(load.o): load.c + $(CC1) load.c + +$(LIBRARY)(par_misc.o): par_misc.e + $(CC1) par_misc.e + +$(LIBRARY)(random.o): random.mod + $(M21) random.mod + +$(LIBRARY)(rcka.o): rcka.c + $(CC1) rcka.c + +$(LIBRARY)(rcki.o): rcki.c + $(CC1) rcki.c + +$(LIBRARY)(rckil.o): rckil.c + $(CC1) rckil.c + +$(LIBRARY)(rcku.o): rcku.c + $(CC1) rcku.c + +$(LIBRARY)(rckul.o): rckul.c + $(CC1) rckul.c + +$(LIBRARY)(sigtrp.o): sigtrp.c + $(CC1) sigtrp.c + +$(LIBRARY)(stackprio.o): stackprio.c + $(CC1) stackprio.c + +$(LIBRARY)(store.o): store.c + $(CC1) store.c + +$(LIBRARY)(ucheck.o): ucheck.c + $(CC1) ucheck.c diff --git a/lib/libm2/MathLib0.mod b/lib/libm2/MathLib0.mod new file mode 100755 index 000000000..ba40da625 --- /dev/null +++ b/lib/libm2/MathLib0.mod @@ -0,0 +1,69 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE MathLib0; +(* + Module: Some mathematical functions + Author: Ceriel J.H. Jacobs + Version: $Header$ +*) + + IMPORT Mathlib; + + PROCEDURE cos(arg: REAL): REAL; + BEGIN + RETURN Mathlib.cos(arg); + END cos; + + PROCEDURE sin(arg: REAL): REAL; + BEGIN + RETURN Mathlib.sin(arg); + END sin; + + PROCEDURE arctan(arg: REAL): REAL; + BEGIN + RETURN Mathlib.arctan(arg); + END arctan; + + PROCEDURE sqrt(arg: REAL): REAL; + BEGIN + RETURN Mathlib.sqrt(arg); + END sqrt; + + PROCEDURE ln(arg: REAL): REAL; + BEGIN + RETURN Mathlib.ln(arg); + END ln; + + PROCEDURE exp(arg: REAL): REAL; + BEGIN + RETURN Mathlib.exp(arg); + END exp; + + PROCEDURE entier(x: REAL): INTEGER; + VAR i: INTEGER; + BEGIN + IF x < 0.0 THEN + i := TRUNC(-x); + IF FLOAT(i) = -x THEN + RETURN -i; + ELSE + RETURN -i -1; + END; + END; + RETURN TRUNC(x); + END entier; + + PROCEDURE real(x: INTEGER): REAL; + BEGIN + IF x < 0 THEN + RETURN - FLOAT(-x); + END; + RETURN FLOAT(x); + END real; + +BEGIN +END MathLib0. diff --git a/lib/libm2/Mathlib.mod b/lib/libm2/Mathlib.mod new file mode 100755 index 000000000..31899e8d3 --- /dev/null +++ b/lib/libm2/Mathlib.mod @@ -0,0 +1,576 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE Mathlib; +(* + Module: Mathematical functions + Author: Ceriel J.H. Jacobs + Version: $Header$ +*) + + FROM EM IMPORT FIF, FEF; + FROM Traps IMPORT Message; + + CONST + OneRadianInDegrees = 57.295779513082320876798155D; + OneDegreeInRadians = 0.017453292519943295769237D; + OneOverSqrt2 = 0.70710678118654752440084436210484904D; + + (* basic functions *) + + PROCEDURE pow(x: REAL; i: INTEGER): REAL; + BEGIN + RETURN SHORT(longpow(LONG(x), i)); + END pow; + + PROCEDURE longpow(x: LONGREAL; i: INTEGER): LONGREAL; + VAR val: LONGREAL; + ri: LONGREAL; + BEGIN + ri := FLOATD(i); + IF x < 0.0D THEN + val := longexp(longln(-x) * ri); + IF ODD(i) THEN RETURN -val; + ELSE RETURN val; + END; + ELSIF x = 0.0D THEN + RETURN 0.0D; + ELSE + RETURN longexp(longln(x) * ri); + END; + END longpow; + + PROCEDURE sqrt(x: REAL): REAL; + BEGIN + RETURN SHORT(longsqrt(LONG(x))); + END sqrt; + + PROCEDURE longsqrt(x: LONGREAL): LONGREAL; + VAR + temp: LONGREAL; + exp, i: INTEGER; + BEGIN + IF x <= 0.0D THEN + IF x < 0.0D THEN + Message("sqrt: negative argument"); + HALT + END; + RETURN 0.0D; + END; + temp := FEF(x,exp); + (* + * NOTE + * this wont work on 1's comp + *) + IF ODD(exp) THEN + temp := 2.0D * temp; + DEC(exp); + END; + temp := 0.5D*(1.0D + temp); + + WHILE exp > 28 DO + temp := temp * 16384.0D; + exp := exp - 28; + END; + WHILE exp < -28 DO + temp := temp / 16384.0D; + exp := exp + 28; + END; + WHILE exp >= 2 DO + temp := temp * 2.0D; + exp := exp - 2; + END; + WHILE exp <= -2 DO + temp := temp / 2.0D; + exp := exp + 2; + END; + FOR i := 0 TO 5 DO + temp := 0.5D*(temp + x/temp); + END; + RETURN temp; + END longsqrt; + + PROCEDURE ldexp(x:LONGREAL; n: INTEGER): LONGREAL; + BEGIN + WHILE n >= 16 DO + x := x * 65536.0D; + n := n - 16; + END; + WHILE n > 0 DO + x := x * 2.0D; + DEC(n); + END; + WHILE n <= -16 DO + x := x / 65536.0D; + n := n + 16; + END; + WHILE n < 0 DO + x := x / 2.0D; + INC(n); + END; + RETURN x; + END ldexp; + + PROCEDURE exp(x: REAL): REAL; + BEGIN + RETURN SHORT(longexp(LONG(x))); + END exp; + + PROCEDURE longexp(x: LONGREAL): LONGREAL; + (* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + *) + CONST + p0 = 0.25000000000000000000D+00; + p1 = 0.75753180159422776666D-02; + p2 = 0.31555192765684646356D-04; + q0 = 0.50000000000000000000D+00; + q1 = 0.56817302698551221787D-01; + q2 = 0.63121894374398503557D-03; + q3 = 0.75104028399870046114D-06; + + VAR + neg: BOOLEAN; + n: INTEGER; + xn, g, x1, x2: LONGREAL; + BEGIN + neg := x < 0.0D; + IF neg THEN + x := -x; + END; + n := TRUNC(x/longln2 + 0.5D); + xn := FLOATD(n); + x1 := FLOATD(TRUNCD(x)); + x2 := x - x1; + g := ((x1 - xn * 0.693359375D)+x2) - xn * (-2.1219444005469058277D-4); + IF neg THEN + g := -g; + n := -n; + END; + xn := g*g; + x := g*((p2*xn+p1)*xn+p0); + INC(n); + RETURN ldexp(0.5D + x/((((q3*xn+q2)*xn+q1)*xn+q0) - x), n); + END longexp; + + PROCEDURE ln(x: REAL): REAL; (* natural log *) + BEGIN + RETURN SHORT(longln(LONG(x))); + END ln; + + PROCEDURE longln(x: LONGREAL): LONGREAL; (* natural log *) + (* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + *) + CONST + p0 = -0.64124943423745581147D+02; + p1 = 0.16383943563021534222D+02; + p2 = -0.78956112887491257267D+00; + q0 = -0.76949932108494879777D+03; + q1 = 0.31203222091924532844D+03; + q2 = -0.35667977739034646171D+02; + q3 = 1.0D; + VAR + exp: INTEGER; + z, znum, zden, w: LONGREAL; + + BEGIN + IF x <= 0.0D THEN + Message("ln: argument <= 0"); + HALT + END; + x := FEF(x, exp); + IF x > OneOverSqrt2 THEN + znum := (x - 0.5D) - 0.5D; + zden := x * 0.5D + 0.5D; + ELSE + znum := x - 0.5D; + zden := znum * 0.5D + 0.5D; + DEC(exp); + END; + z := znum / zden; + w := z * z; + x := z + z * w * (((p2*w+p1)*w+p0)/(((q3*w+q2)*w+q1)*w+q0)); + z := FLOATD(exp); + x := x + z * (-2.121944400546905827679D-4); + RETURN x + z * 0.693359375D; + END longln; + + PROCEDURE log(x: REAL): REAL; (* log with base 10 *) + BEGIN + RETURN SHORT(longlog(LONG(x))); + END log; + + PROCEDURE longlog(x: LONGREAL): LONGREAL; (* log with base 10 *) + BEGIN + RETURN longln(x)/longln10; + END longlog; + + (* trigonometric functions; arguments in radians *) + + PROCEDURE sin(x: REAL): REAL; + BEGIN + RETURN SHORT(longsin(LONG(x))); + END sin; + + PROCEDURE sinus(x: LONGREAL; cosflag: BOOLEAN) : LONGREAL; + (* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + *) + CONST + r0 = -0.16666666666666665052D+00; + r1 = 0.83333333333331650314D-02; + r2 = -0.19841269841201840457D-03; + r3 = 0.27557319210152756119D-05; + r4 = -0.25052106798274584544D-07; + r5 = 0.16058936490371589114D-09; + r6 = -0.76429178068910467734D-12; + r7 = 0.27204790957888846175D-14; + A1 = 3.1416015625D; + A2 = -8.908910206761537356617D-6; + VAR + x1, x2, y : LONGREAL; + neg : BOOLEAN; + BEGIN + IF x < 0.0D THEN + neg := TRUE; + x := -x + ELSE neg := FALSE + END; + IF cosflag THEN + neg := FALSE; + y := longhalfpi + x + ELSE + y := x + END; + y := y / longpi + 0.5D; + + IF FIF(y, 1.0D, y) < 0.0D THEN ; END; + IF FIF(y, 0.5D, x1) # 0.0D THEN neg := NOT neg END; + IF cosflag THEN y := y - 0.5D END; + x2 := FIF(x, 1.0, x1); + x := x1 - y * A1; + x := x + x2; + x := x - y * A2; + + IF x < 0.0D THEN + neg := NOT neg; + x := -x + END; + y := x * x; + x := x + x * y * (((((((r7*y+r6)*y+r5)*y+r4)*y+r3)*y+r2)*y+r1)*y+r0); + IF neg THEN RETURN -x END; + RETURN x; + END sinus; + + PROCEDURE longsin(x: LONGREAL): LONGREAL; + BEGIN + RETURN sinus(x, FALSE); + END longsin; + + PROCEDURE cos(x: REAL): REAL; + BEGIN + RETURN SHORT(longcos(LONG(x))); + END cos; + + PROCEDURE longcos(x: LONGREAL): LONGREAL; + BEGIN + IF x < 0.0D THEN x := -x; END; + RETURN sinus(x, TRUE); + END longcos; + + PROCEDURE tan(x: REAL): REAL; + BEGIN + RETURN SHORT(longtan(LONG(x))); + END tan; + + PROCEDURE longtan(x: LONGREAL): LONGREAL; + (* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + *) + + CONST + p1 = -0.13338350006421960681D+00; + p2 = 0.34248878235890589960D-02; + p3 = -0.17861707342254426711D-04; + + q0 = 1.0D; + q1 = -0.46671683339755294240D+00; + q2 = 0.25663832289440112864D-01; + q3 = -0.31181531907010027307D-03; + q4 = 0.49819433993786512270D-06; + + A1 = 1.57080078125D; + A2 = -4.454455103380768678308D-06; + + VAR y, x1, x2: LONGREAL; + negative: BOOLEAN; + invert: BOOLEAN; + BEGIN + negative := x < 0.0D; + y := x / longhalfpi + 0.5D; + + (* Use extended precision to calculate reduced argument. + Here we used 12 bits of the mantissa for a1. + Also split x in integer part x1 and fraction part x2. + *) + IF FIF(y, 1.0D, y) < 0.0D THEN ; END; + invert := FIF(y, 0.5D, x1) # 0.0D; + x2 := FIF(x, 1.0D, x1); + x := x1 - y * A1; + x := x + x2; + x := x - y * A2; + + y := x * x; + x := x + x * y * ((p3*y+p2)*y+p1); + y := (((q4*y+q3)*y+q2)*y+q1)*y+q0; + IF negative THEN x := -x END; + IF invert THEN RETURN -y/x END; + RETURN x/y; + END longtan; + + PROCEDURE arcsin(x: REAL): REAL; + BEGIN + RETURN SHORT(longarcsin(LONG(x))); + END arcsin; + + PROCEDURE arcsincos(x: LONGREAL; cosfl: BOOLEAN): LONGREAL; + CONST + p0 = -0.27368494524164255994D+02; + p1 = 0.57208227877891731407D+02; + p2 = -0.39688862997540877339D+02; + p3 = 0.10152522233806463645D+02; + p4 = -0.69674573447350646411D+00; + + q0 = -0.16421096714498560795D+03; + q1 = 0.41714430248260412556D+03; + q2 = -0.38186303361750149284D+03; + q3 = 0.15095270841030604719D+03; + q4 = -0.23823859153670238830D+02; + q5 = 1.0D; + VAR + negative : BOOLEAN; + big: BOOLEAN; + g: LONGREAL; + BEGIN + negative := x < 0.0D; + IF negative THEN x := -x; END; + IF x > 0.5D THEN + big := TRUE; + IF x > 1.0D THEN + Message("arcsin or arccos: argument > 1"); + HALT + END; + g := 0.5D - 0.5D * x; + x := -longsqrt(g); + x := x + x; + ELSE + big := FALSE; + g := x * x; + END; + x := x + x * g * + ((((p4*g+p3)*g+p2)*g+p1)*g+p0)/(((((q5*g+q4)*g+q3)*g+q2)*g+q1)*g+q0); + IF cosfl AND NOT negative THEN x := -x END; + IF cosfl = NOT big THEN + x := (x + longquartpi) + longquartpi; + ELSIF cosfl AND negative AND big THEN + x := (x + longhalfpi) + longhalfpi; + END; + IF negative AND NOT cosfl THEN x := -x END; + RETURN x; + END arcsincos; + + PROCEDURE longarcsin(x: LONGREAL): LONGREAL; + BEGIN + RETURN arcsincos(x, FALSE); + END longarcsin; + + PROCEDURE arccos(x: REAL): REAL; + BEGIN + RETURN SHORT(longarccos(LONG(x))); + END arccos; + + PROCEDURE longarccos(x: LONGREAL): LONGREAL; + BEGIN + RETURN arcsincos(x, TRUE); + END longarccos; + + PROCEDURE arctan(x: REAL): REAL; + BEGIN + RETURN SHORT(longarctan(LONG(x))); + END arctan; + + VAR A: ARRAY[0..3] OF LONGREAL; + arctaninit: BOOLEAN; + + PROCEDURE longarctan(x: LONGREAL): LONGREAL; + (* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + *) + CONST + p0 = -0.13688768894191926929D+02; + p1 = -0.20505855195861651981D+02; + p2 = -0.84946240351320683534D+01; + p3 = -0.83758299368150059274D+00; + q0 = 0.41066306682575781263D+02; + q1 = 0.86157349597130242515D+02; + q2 = 0.59578436142597344465D+02; + q3 = 0.15024001160028576121D+02; + q4 = 1.0D; + VAR + g: LONGREAL; + neg: BOOLEAN; + n: INTEGER; + BEGIN + IF NOT arctaninit THEN + arctaninit := TRUE; + A[0] := 0.0D; + A[1] := 0.52359877559829887307710723554658381D; (* p1/6 *) + A[2] := longhalfpi; + A[3] := 1.04719755119659774615421446109316763D; (* pi/3 *) + END; + neg := FALSE; + IF x < 0.0D THEN + neg := TRUE; + x := -x; + END; + IF x > 1.0D THEN + x := 1.0D/x; + n := 2 + ELSE + n := 0 + END; + IF x > 0.26794919243112270647D (* 2-sqrt(3) *) THEN + INC(n); + x := (((0.73205080756887729353D*x-0.5D)-0.5D)+x)/ + (1.73205080756887729353D + x); + END; + g := x*x; + x := x + x * g * (((p3*g+p2)*g+p1)*g+p0) / ((((q4*g+q3)*g+q2)*g+q1)*g+q0); + IF n > 1 THEN x := -x END; + x := x + A[n]; + IF neg THEN RETURN -x; END; + RETURN x; + END longarctan; + + (* hyperbolic functions *) + (* The C math library has better implementations for some of these, but + they depend on some properties of the floating point implementation, + and, for now, we don't want that in the Modula-2 system. + *) + + PROCEDURE sinh(x: REAL): REAL; + BEGIN + RETURN SHORT(longsinh(LONG(x))); + END sinh; + + PROCEDURE longsinh(x: LONGREAL): LONGREAL; + VAR expx: LONGREAL; + BEGIN + expx := longexp(x); + RETURN (expx - 1.0D/expx)/2.0D; + END longsinh; + + PROCEDURE cosh(x: REAL): REAL; + BEGIN + RETURN SHORT(longcosh(LONG(x))); + END cosh; + + PROCEDURE longcosh(x: LONGREAL): LONGREAL; + VAR expx: LONGREAL; + BEGIN + expx := longexp(x); + RETURN (expx + 1.0D/expx)/2.0D; + END longcosh; + + PROCEDURE tanh(x: REAL): REAL; + BEGIN + RETURN SHORT(longtanh(LONG(x))); + END tanh; + + PROCEDURE longtanh(x: LONGREAL): LONGREAL; + VAR expx: LONGREAL; + BEGIN + expx := longexp(x); + RETURN (expx - 1.0D/expx) / (expx + 1.0D/expx); + END longtanh; + + PROCEDURE arcsinh(x: REAL): REAL; + BEGIN + RETURN SHORT(longarcsinh(LONG(x))); + END arcsinh; + + PROCEDURE longarcsinh(x: LONGREAL): LONGREAL; + VAR neg: BOOLEAN; + BEGIN + neg := FALSE; + IF x < 0.0D THEN + neg := TRUE; + x := -x; + END; + x := longln(x + longsqrt(x*x+1.0D)); + IF neg THEN RETURN -x; END; + RETURN x; + END longarcsinh; + + PROCEDURE arccosh(x: REAL): REAL; + BEGIN + RETURN SHORT(longarccosh(LONG(x))); + END arccosh; + + PROCEDURE longarccosh(x: LONGREAL): LONGREAL; + BEGIN + IF x < 1.0D THEN + Message("arccosh: argument < 1"); + HALT + END; + RETURN longln(x + longsqrt(x*x - 1.0D)); + END longarccosh; + + PROCEDURE arctanh(x: REAL): REAL; + BEGIN + RETURN SHORT(longarctanh(LONG(x))); + END arctanh; + + PROCEDURE longarctanh(x: LONGREAL): LONGREAL; + BEGIN + IF (x <= -1.0D) OR (x >= 1.0D) THEN + Message("arctanh: ABS(argument) >= 1"); + HALT + END; + RETURN longln((1.0D + x)/(1.0D - x)) / 2.0D; + END longarctanh; + + (* conversions *) + + PROCEDURE RadianToDegree(x: REAL): REAL; + BEGIN + RETURN SHORT(longRadianToDegree(LONG(x))); + END RadianToDegree; + + PROCEDURE longRadianToDegree(x: LONGREAL): LONGREAL; + BEGIN + RETURN x * OneRadianInDegrees; + END longRadianToDegree; + + PROCEDURE DegreeToRadian(x: REAL): REAL; + BEGIN + RETURN SHORT(longDegreeToRadian(LONG(x))); + END DegreeToRadian; + + PROCEDURE longDegreeToRadian(x: LONGREAL): LONGREAL; + BEGIN + RETURN x * OneDegreeInRadians; + END longDegreeToRadian; + +BEGIN + arctaninit := FALSE; +END Mathlib. diff --git a/lib/libm2/PascalIO.mod b/lib/libm2/PascalIO.mod new file mode 100755 index 000000000..b29049bd3 --- /dev/null +++ b/lib/libm2/PascalIO.mod @@ -0,0 +1,437 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE PascalIO; +(* + Module: Pascal-like Input/Output + Author: Ceriel J.H. Jacobs + Version: $Header$ +*) + + FROM Conversions IMPORT + ConvertInteger, ConvertCardinal; + FROM RealConversions IMPORT + LongRealToString, StringToLongReal; + FROM Traps IMPORT Message; + FROM Streams IMPORT Stream, StreamKind, StreamMode, StreamResult, + InputStream, OutputStream, OpenStream, CloseStream, + EndOfStream, Read, Write, StreamBuffering; + FROM Storage IMPORT Allocate; + FROM SYSTEM IMPORT ADR; + + TYPE charset = SET OF CHAR; + btype = (Preading, Pwriting, free); + + CONST spaces = charset{11C, 12C, 13C, 14C, 15C, ' '}; + + TYPE IOstream = RECORD + type: btype; + done, eof : BOOLEAN; + ch: CHAR; + next: Text; + stream: Stream; + END; + Text = POINTER TO IOstream; + numbuf = ARRAY[0..255] OF CHAR; + + VAR ibuf, obuf: IOstream; + head: Text; + result: StreamResult; + + PROCEDURE Reset(VAR InputText: Text; Filename: ARRAY OF CHAR); + BEGIN + doclose(InputText); + getstruct(InputText); + WITH InputText^ DO + OpenStream(stream, Filename, text, reading, result); + IF result # succeeded THEN + Message("could not open input file"); + HALT; + END; + type := Preading; + done := FALSE; + eof := FALSE; + END; + END Reset; + + PROCEDURE Rewrite(VAR OutputText: Text; Filename: ARRAY OF CHAR); + BEGIN + doclose(OutputText); + getstruct(OutputText); + WITH OutputText^ DO + OpenStream(stream, Filename, text, writing, result); + IF result # succeeded THEN + Message("could not open output file"); + HALT; + END; + type := Pwriting; + END; + END Rewrite; + + PROCEDURE CloseOutput(); + VAR p: Text; + BEGIN + p := head; + WHILE p # NIL DO + doclose(p); + p := p^.next; + END; + END CloseOutput; + + PROCEDURE doclose(Xtext: Text); + BEGIN + IF Xtext # Notext THEN + WITH Xtext^ DO + IF type # free THEN + CloseStream(stream, result); + type := free; + END; + END; + END; + END doclose; + + PROCEDURE getstruct(VAR Xtext: Text); + BEGIN + Xtext := head; + WHILE (Xtext # NIL) AND (Xtext^.type # free) DO + Xtext := Xtext^.next; + END; + IF Xtext = NIL THEN + Allocate(Xtext,SIZE(IOstream)); + Xtext^.next := head; + head := Xtext; + END; + END getstruct; + + PROCEDURE Error(tp: btype); + BEGIN + IF tp = Preading THEN + Message("input text expected"); + ELSE + Message("output text expected"); + END; + HALT; + END Error; + + PROCEDURE ReadChar(InputText: Text; VAR ch : CHAR); + BEGIN + ch := NextChar(InputText); + IF InputText^.eof THEN + Message("unexpected EOF"); + HALT; + END; + InputText^.done := FALSE; + END ReadChar; + + PROCEDURE NextChar(InputText: Text): CHAR; + BEGIN + WITH InputText^ DO + IF type # Preading THEN Error(Preading); END; + IF NOT done THEN + IF EndOfStream(stream, result) THEN + eof := TRUE; + ch := 0C; + ELSE + Read(stream, ch, result); + done := TRUE; + END; + END; + RETURN ch; + END; + END NextChar; + + PROCEDURE Get(InputText: Text); + VAR dummy: CHAR; + BEGIN + ReadChar(InputText, dummy); + END Get; + + PROCEDURE Eoln(InputText: Text): BOOLEAN; + BEGIN + RETURN NextChar(InputText) = 12C; + END Eoln; + + PROCEDURE Eof(InputText: Text): BOOLEAN; + BEGIN + RETURN (NextChar(InputText) = 0C) AND InputText^.eof; + END Eof; + + PROCEDURE ReadLn(InputText: Text); + VAR ch: CHAR; + BEGIN + REPEAT + ReadChar(InputText, ch) + UNTIL ch = 12C; + END ReadLn; + + PROCEDURE WriteChar(OutputText: Text; char: CHAR); + BEGIN + WITH OutputText^ DO + IF type # Pwriting THEN Error(Pwriting); END; + Write(stream, char, result); + END; + END WriteChar; + + PROCEDURE WriteLn(OutputText: Text); + BEGIN + WriteChar(OutputText, 12C); + END WriteLn; + + PROCEDURE Page(OutputText: Text); + BEGIN + WriteChar(OutputText, 14C); + END Page; + + PROCEDURE ReadInteger(InputText: Text; VAR int : INTEGER); + CONST + SAFELIMITDIV10 = MAX(INTEGER) DIV 10; + SAFELIMITREM10 = MAX(INTEGER) MOD 10; + VAR + neg : BOOLEAN; + safedigit: CARDINAL; + ch: CHAR; + chvalue: CARDINAL; + BEGIN + WHILE NextChar(InputText) IN spaces DO + Get(InputText); + END; + ch := NextChar(InputText); + IF ch = '-' THEN + Get(InputText); + ch := NextChar(InputText); + neg := TRUE; + ELSIF ch = '+' THEN + Get(InputText); + ch := NextChar(InputText); + neg := FALSE; + ELSE + neg := FALSE + END; + + safedigit := SAFELIMITREM10; + IF neg THEN safedigit := safedigit + 1 END; + int := 0; + IF (ch >= '0') AND (ch <= '9') THEN + WHILE (ch >= '0') & (ch <= '9') DO + chvalue := ORD(ch) - ORD('0'); + IF (int < -SAFELIMITDIV10) OR + ( (int = -SAFELIMITDIV10) AND + (chvalue > safedigit)) THEN + Message("integer too large"); + HALT; + ELSE + int := 10*int - VAL(INTEGER, chvalue); + Get(InputText); + ch := NextChar(InputText); + END; + END; + IF NOT neg THEN + int := -int + END; + ELSE + Message("integer expected"); + HALT; + END; + END ReadInteger; + + PROCEDURE ReadCardinal(InputText: Text; VAR card : CARDINAL); + CONST + SAFELIMITDIV10 = MAX(CARDINAL) DIV 10; + SAFELIMITREM10 = MAX(CARDINAL) MOD 10; + + VAR + ch : CHAR; + safedigit: CARDINAL; + chvalue: CARDINAL; + BEGIN + WHILE NextChar(InputText) IN spaces DO + Get(InputText); + END; + ch := NextChar(InputText); + safedigit := SAFELIMITREM10; + card := 0; + IF (ch >= '0') AND (ch <= '9') THEN + WHILE (ch >= '0') & (ch <= '9') DO + chvalue := ORD(ch) - ORD('0'); + IF (card > SAFELIMITDIV10) OR + ( (card = SAFELIMITDIV10) AND + (chvalue > safedigit)) THEN + Message("cardinal too large"); + HALT; + ELSE + card := 10*card + chvalue; + Get(InputText); + ch := NextChar(InputText); + END; + END; + ELSE + Message("cardinal expected"); + HALT; + END; + END ReadCardinal; + + PROCEDURE ReadReal(InputText: Text; VAR real: REAL); + VAR x1: LONGREAL; + BEGIN + ReadLongReal(InputText, x1); + real := x1 + END ReadReal; + + PROCEDURE ReadLongReal(InputText: Text; VAR real: LONGREAL); + VAR + buf: numbuf; + ch: CHAR; + ok: BOOLEAN; + index: INTEGER; + + PROCEDURE inch(): CHAR; + BEGIN + buf[index] := ch; + INC(index); + Get(InputText); + RETURN NextChar(InputText); + END inch; + + BEGIN + index := 0; + ok := TRUE; + WHILE NextChar(InputText) IN spaces DO + Get(InputText); + END; + ch := NextChar(InputText); + IF (ch ='+') OR (ch = '-') THEN + ch := inch(); + END; + IF (ch >= '0') AND (ch <= '9') THEN + WHILE (ch >= '0') AND (ch <= '9') DO + ch := inch(); + END; + IF (ch = '.') THEN + ch := inch(); + IF (ch >= '0') AND (ch <= '9') THEN + WHILE (ch >= '0') AND (ch <= '9') DO + ch := inch(); + END; + ELSE + ok := FALSE; + END; + END; + IF ok AND (ch = 'E') THEN + ch := inch(); + IF (ch ='+') OR (ch = '-') THEN + ch := inch(); + END; + IF (ch >= '0') AND (ch <= '9') THEN + WHILE (ch >= '0') AND (ch <= '9') DO + ch := inch(); + END; + ELSE + ok := FALSE; + END; + END; + ELSE + ok := FALSE; + END; + IF ok THEN + buf[index] := 0C; + StringToLongReal(buf, real, ok); + END; + IF NOT ok THEN + Message("Illegal real"); + HALT; + END; + END ReadLongReal; + + PROCEDURE WriteCardinal(OutputText: Text; card: CARDINAL; width: CARDINAL); + VAR + buf : numbuf; + BEGIN + ConvertCardinal(card, 1, buf); + WriteString(OutputText, buf, width); + END WriteCardinal; + + PROCEDURE WriteInteger(OutputText: Text; int: INTEGER; width: CARDINAL); + VAR + buf : numbuf; + BEGIN + ConvertInteger(int, 1, buf); + WriteString(OutputText, buf, width); + END WriteInteger; + + PROCEDURE WriteBoolean(OutputText: Text; bool: BOOLEAN; width: CARDINAL); + BEGIN + IF bool THEN + WriteString(OutputText, " TRUE", width); + ELSE + WriteString(OutputText, "FALSE", width); + END; + END WriteBoolean; + + PROCEDURE WriteReal(OutputText: Text; real: REAL; width, nfrac: CARDINAL); + BEGIN + WriteLongReal(OutputText, LONG(real), width, nfrac) + END WriteReal; + + PROCEDURE WriteLongReal(OutputText: Text; real: LONGREAL; width, nfrac: CARDINAL); + VAR + buf: numbuf; + ok: BOOLEAN; + digits: INTEGER; + BEGIN + IF width > SIZE(buf) THEN + width := SIZE(buf); + END; + IF nfrac > 0 THEN + LongRealToString(real, width, nfrac, buf, ok); + ELSE + IF width < 9 THEN width := 9; END; + IF real < 0.0D THEN + digits := 7 - INTEGER(width); + ELSE + digits := 6 - INTEGER(width); + END; + LongRealToString(real, width, digits, buf, ok); + END; + WriteString(OutputText, buf, 0); + END WriteLongReal; + + PROCEDURE WriteString(OutputText: Text; str: ARRAY OF CHAR; width: CARDINAL); + VAR index: CARDINAL; + BEGIN + index := 0; + WHILE (index <= HIGH(str)) AND (str[index] # Eos) DO + INC(index); + END; + WHILE index < width DO + WriteChar(OutputText, " "); + INC(index); + END; + index := 0; + WHILE (index <= HIGH(str)) AND (str[index] # Eos) DO + WriteChar(OutputText, str[index]); + INC(index); + END; + END WriteString; + +BEGIN (* PascalIO initialization *) + WITH ibuf DO + stream := InputStream; + eof := FALSE; + type := Preading; + done := FALSE; + END; + WITH obuf DO + stream := OutputStream; + eof := FALSE; + type := Pwriting; + END; + Notext := NIL; + Input := ADR(ibuf); + Output := ADR(obuf); + Input^.next := Output; + Output^.next := NIL; + head := Input; +END PascalIO. diff --git a/lib/libm2/Processes.mod b/lib/libm2/Processes.mod new file mode 100755 index 000000000..152c7e640 --- /dev/null +++ b/lib/libm2/Processes.mod @@ -0,0 +1,101 @@ +(*$R-*) +IMPLEMENTATION MODULE Processes [1]; +(* + Module: Processes + From: "Programming in Modula-2", 3rd, corrected edition, by N. Wirth + Version: $Header$ +*) + + FROM SYSTEM IMPORT ADDRESS, TSIZE, NEWPROCESS, TRANSFER; + FROM Storage IMPORT Allocate; + FROM Traps IMPORT Message; + + TYPE SIGNAL = POINTER TO ProcessDescriptor; + + ProcessDescriptor = + RECORD next: SIGNAL; (* ring *) + queue: SIGNAL; (* queue of waiting processes *) + cor: ADDRESS; + ready: BOOLEAN; + END; + + VAR cp: SIGNAL; (* current process *) + + PROCEDURE StartProcess(P: PROC; n: CARDINAL); + VAR s0: SIGNAL; + wsp: ADDRESS; + BEGIN + s0 := cp; + Allocate(wsp, n); + Allocate(cp, TSIZE(ProcessDescriptor)); + WITH cp^ DO + next := s0^.next; + s0^.next := cp; + ready := TRUE; + queue := NIL + END; + NEWPROCESS(P, wsp, n, cp^.cor); + TRANSFER(s0^.cor, cp^.cor); + END StartProcess; + + PROCEDURE SEND(VAR s: SIGNAL); + VAR s0: SIGNAL; + BEGIN + IF s # NIL THEN + s0 := cp; + cp := s; + WITH cp^ DO + s := queue; + ready := TRUE; + queue := NIL + END; + TRANSFER(s0^.cor, cp^.cor); + END + END SEND; + + PROCEDURE WAIT(VAR s: SIGNAL); + VAR s0, s1: SIGNAL; + BEGIN + (* insert cp in queue s *) + IF s = NIL THEN + s := cp + ELSE + s0 := s; + s1 := s0^.queue; + WHILE s1 # NIL DO + s0 := s1; + s1 := s0^.queue + END; + s0^.queue := cp + END; + s0 := cp; + REPEAT + cp := cp^.next + UNTIL cp^.ready; + IF cp = s0 THEN + (* deadlock *) + Message("deadlock"); + HALT + END; + s0^.ready := FALSE; + TRANSFER(s0^.cor, cp^.cor) + END WAIT; + + PROCEDURE Awaited(s: SIGNAL): BOOLEAN; + BEGIN + RETURN s # NIL + END Awaited; + + PROCEDURE Init(VAR s: SIGNAL); + BEGIN + s := NIL + END Init; + +BEGIN + Allocate(cp, TSIZE(ProcessDescriptor)); + WITH cp^ DO + next := cp; + ready := TRUE; + queue := NIL + END +END Processes. diff --git a/lib/libm2/RealConver.mod b/lib/libm2/RealConver.mod new file mode 100755 index 000000000..c663f5ed0 --- /dev/null +++ b/lib/libm2/RealConver.mod @@ -0,0 +1,337 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE RealConversions; +(* + Module: string-to-real and real-to-string conversions + Author: Ceriel J.H. Jacobs + Version: $Header$ +*) + + + PROCEDURE RealToString(arg: REAL; + width, digits: INTEGER; + VAR str: ARRAY OF CHAR; + VAR ok: BOOLEAN); + BEGIN + LongRealToString(LONG(arg), width, digits, str, ok); + END RealToString; + + TYPE + Powers = RECORD + pval: LONGREAL; + rpval: LONGREAL; + exp: INTEGER + END; + + VAR Powers10: ARRAY[1..6] OF Powers; + + PROCEDURE LongRealToString(arg: LONGREAL; + width, digits: INTEGER; + VAR str: ARRAY OF CHAR; + VAR ok: BOOLEAN); + VAR pointpos: INTEGER; + i: CARDINAL; + ecvtflag: BOOLEAN; + r: LONGREAL; + ind1, ind2 : CARDINAL; + sign: BOOLEAN; + ndigits: CARDINAL; + + BEGIN + r := arg; + IF digits < 0 THEN + ecvtflag := TRUE; + ndigits := -digits; + ELSE + ecvtflag := FALSE; + ndigits := digits; + END; + IF (HIGH(str) < ndigits + 3) THEN + str[0] := 0C; ok := FALSE; RETURN + END; + pointpos := 0; + sign := r < 0.0D; + IF sign THEN r := -r END; + ok := TRUE; + IF NOT (r / 10.0D < r) THEN + (* assume Nan or Infinity *) + r := 0.0D; + ok := FALSE; + END; + IF r # 0.0D THEN + IF r >= 10.0D THEN + FOR i := 1 TO 6 DO + WITH Powers10[i] DO + WHILE r >= pval DO + r := r * rpval; + INC(pointpos, exp) + END; + END; + END; + END; + IF r < 1.0D THEN + FOR i := 1 TO 6 DO + WITH Powers10[i] DO + WHILE r*pval < 10.0D DO + r := r * pval; + DEC(pointpos, exp) + END; + END; + END; + END; + (* Now, we have r in [1.0, 10.0) *) + INC(pointpos); + END; + ind1 := 0; + ind2 := ndigits+1; + + IF NOT ecvtflag THEN + IF INTEGER(ind2) + pointpos <= 0 THEN + ind2 := 1; + ELSE + ind2 := INTEGER(ind2) + pointpos + END; + END; + IF ind2 > HIGH(str) THEN + ok := FALSE; + str[0] := 0C; + RETURN; + END; + WHILE ind1 < ind2 DO + str[ind1] := CHR(TRUNC(r)+ORD('0')); + r := 10.0D * (r - FLOATD(TRUNC(r))); + INC(ind1); + END; + IF ind2 > 0 THEN + DEC(ind2); + ind1 := ind2; + str[ind2] := CHR(ORD(str[ind2])+5); + WHILE str[ind2] > '9' DO + str[ind2] := '0'; + IF ind2 > 0 THEN + DEC(ind2); + str[ind2] := CHR(ORD(str[ind2])+1); + ELSE + str[ind2] := '1'; + INC(pointpos); + IF NOT ecvtflag THEN + IF ind1 > 0 THEN str[ind1] := '0'; END; + INC(ind1); + END; + END; + END; + IF (NOT ecvtflag) AND (ind1 = 0) THEN + str[0] := CHR(ORD(str[0])-5); + INC(ind1); + END; + END; + IF ecvtflag THEN + FOR i := ind1 TO 2 BY -1 DO + str[i] := str[i-1]; + END; + str[1] := '.'; + INC(ind1); + IF sign THEN + FOR i := ind1 TO 1 BY -1 DO + str[i] := str[i-1]; + END; + INC(ind1); + str[0] := '-'; + END; + IF (ind1 + 4) > HIGH(str) THEN + str[0] := 0C; + ok := FALSE; + RETURN; + END; + str[ind1] := 'E'; INC(ind1); + IF arg # 0.0D THEN DEC(pointpos); END; + IF pointpos < 0 THEN + pointpos := -pointpos; + str[ind1] := '-'; + ELSE + str[ind1] := '+'; + END; + INC(ind1); + str[ind1] := CHR(ORD('0') + CARDINAL(pointpos DIV 100)); + pointpos := pointpos MOD 100; + INC(ind1); + str[ind1] := CHR(ORD('0') + CARDINAL(pointpos DIV 10)); + INC(ind1); + str[ind1] := CHR(ORD('0') + CARDINAL(pointpos MOD 10)); + ELSE + IF pointpos <= 0 THEN + FOR i := ind1 TO 1 BY -1 DO + str[i+CARDINAL(-pointpos)] := str[i-1]; + END; + FOR i := 0 TO CARDINAL(-pointpos) DO + str[i] := '0'; + END; + ind1 := ind1 + CARDINAL(1 - pointpos); + pointpos := 1; + END; + FOR i := ind1 TO CARDINAL(pointpos+1) BY -1 DO + str[i] := str[i-1]; + END; + IF ndigits = 0 THEN + str[pointpos] := 0C; + ind1 := pointpos - 1; + ELSE + str[pointpos] := '.'; + IF INTEGER(ind1) > pointpos+INTEGER(ndigits) THEN + ind1 := pointpos+INTEGER(ndigits); + END; + str[pointpos+INTEGER(ndigits)+1] := 0C; + END; + IF sign THEN + FOR i := ind1 TO 0 BY -1 DO + str[i+1] := str[i]; + END; + str[0] := '-'; + INC(ind1); + END; + END; + IF (ind1+1) <= HIGH(str) THEN str[ind1+1] := 0C; END; + IF ind1 >= CARDINAL(width) THEN + ok := FALSE; + RETURN; + END; + IF width > 0 THEN + DEC(width); + END; + IF (width > 0) AND (ind1 < CARDINAL(width)) THEN + FOR i := ind1 TO 0 BY -1 DO + str[i + CARDINAL(width) - ind1] := str[i]; + END; + FOR i := 0 TO CARDINAL(width)-(ind1+1) DO + str[i] := ' '; + END; + ind1 := CARDINAL(width); + IF (ind1+1) <= HIGH(str) THEN + FOR ind1 := ind1+1 TO HIGH(str) DO + str[ind1] := 0C; + END; + END; + END; + + END LongRealToString; + + + PROCEDURE StringToReal(str: ARRAY OF CHAR; + VAR r: REAL; VAR ok: BOOLEAN); + VAR x: LONGREAL; + BEGIN + StringToLongReal(str, x, ok); + IF ok THEN + r := x; + END; + END StringToReal; + + PROCEDURE StringToLongReal(str: ARRAY OF CHAR; + VAR r: LONGREAL; VAR ok: BOOLEAN); + CONST BIG = 1.0D17; + TYPE SETOFCHAR = SET OF CHAR; + VAR pow10 : INTEGER; + i : INTEGER; + e : LONGREAL; + ch : CHAR; + signed: BOOLEAN; + signedexp: BOOLEAN; + iB: CARDINAL; + + BEGIN + r := 0.0D; + pow10 := 0; + iB := 0; + ok := TRUE; + signed := FALSE; + WHILE (str[iB] = ' ') OR (str[iB] = CHR(9)) DO + INC(iB); + IF iB > HIGH(str) THEN + ok := FALSE; + RETURN; + END; + END; + IF str[iB] = '-' THEN signed := TRUE; INC(iB) + ELSIF str[iB] = '+' THEN INC(iB) + END; + ch := str[iB]; INC(iB); + IF NOT (ch IN SETOFCHAR{'0'..'9'}) THEN ok := FALSE; RETURN END; + REPEAT + IF r>BIG THEN INC(pow10) ELSE r:= 10.0D*r+FLOATD(ORD(ch)-ORD('0')) END; + IF iB <= HIGH(str) THEN + ch := str[iB]; INC(iB); + END; + UNTIL (iB > HIGH(str)) OR NOT (ch IN SETOFCHAR{'0'..'9'}); + IF (ch = '.') AND (iB <= HIGH(str)) THEN + ch := str[iB]; INC(iB); + IF NOT (ch IN SETOFCHAR{'0'..'9'}) THEN ok := FALSE; RETURN END; + REPEAT + IF r < BIG THEN + r := 10.0D * r + FLOATD(ORD(ch)-ORD('0')); + DEC(pow10); + END; + IF iB <= HIGH(str) THEN + ch := str[iB]; INC(iB); + END; + UNTIL (iB > HIGH(str)) OR NOT (ch IN SETOFCHAR{'0'..'9'}); + END; + IF (ch = 'E') THEN + IF iB > HIGH(str) THEN + ok := FALSE; + RETURN; + ELSE + ch := str[iB]; INC(iB); + END; + i := 0; + signedexp := FALSE; + IF (ch = '-') OR (ch = '+') THEN + signedexp := ch = '-'; + IF iB > HIGH(str) THEN + ok := FALSE; + RETURN; + ELSE + ch := str[iB]; INC(iB); + END; + END; + IF NOT (ch IN SETOFCHAR{'0'..'9'}) THEN ok := FALSE; RETURN END; + REPEAT + i := i*10 + INTEGER(ORD(ch) - ORD('0')); + IF iB <= HIGH(str) THEN + ch := str[iB]; INC(iB); + END; + UNTIL (iB > HIGH(str)) OR NOT (ch IN SETOFCHAR{'0'..'9'}); + IF signedexp THEN i := -i END; + pow10 := pow10 + i; + END; + IF pow10 < 0 THEN i := -pow10; ELSE i := pow10; END; + e := 1.0D; + DEC(i); + WHILE i >= 10 DO + e := e * 10000000000.0D; + DEC(i,10); + END; + WHILE i >= 0 DO + e := e * 10.0D; + DEC(i) + END; + IF pow10<0 THEN + r := r / e; + ELSE + r := r * e; + END; + IF signed THEN r := -r; END; + IF (iB <= HIGH(str)) AND (ORD(ch) > ORD(' ')) THEN ok := FALSE; END + END StringToLongReal; + +BEGIN + WITH Powers10[1] DO pval := 1.0D32; rpval := 1.0D-32; exp := 32 END; + WITH Powers10[2] DO pval := 1.0D16; rpval := 1.0D-16; exp := 16 END; + WITH Powers10[3] DO pval := 1.0D8; rpval := 1.0D-8; exp := 8 END; + WITH Powers10[4] DO pval := 1.0D4; rpval := 1.0D-4; exp := 4 END; + WITH Powers10[5] DO pval := 1.0D2; rpval := 1.0D-2; exp := 2 END; + WITH Powers10[6] DO pval := 1.0D1; rpval := 1.0D-1; exp := 1 END; +END RealConversions. diff --git a/lib/libm2/RealInOut.mod b/lib/libm2/RealInOut.mod new file mode 100755 index 000000000..e2565cc2f --- /dev/null +++ b/lib/libm2/RealInOut.mod @@ -0,0 +1,97 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE RealInOut; +(* + Module: InOut for REAL numbers + Author: Ceriel J.H. Jacobs + Version: $Header$ +*) + + FROM InOut IMPORT ReadString, WriteString, WriteOct; + FROM Traps IMPORT Message; + FROM SYSTEM IMPORT WORD; + FROM RealConversions IMPORT + LongRealToString, StringToLongReal; + + CONST MAXNDIG = 32; + MAXWIDTH = MAXNDIG+7; + TYPE RBUF = ARRAY [0..MAXWIDTH+1] OF CHAR; + + PROCEDURE WriteReal(arg: REAL; ndigits: CARDINAL); + BEGIN + WriteLongReal(LONG(arg), ndigits) + END WriteReal; + + PROCEDURE WriteLongReal(arg: LONGREAL; ndigits: CARDINAL); + VAR buf : RBUF; + ok : BOOLEAN; + + BEGIN + IF ndigits > MAXWIDTH THEN ndigits := MAXWIDTH; END; + IF ndigits < 10 THEN ndigits := 10; END; + LongRealToString(arg, ndigits, -INTEGER(ndigits - 7), buf, ok); + WriteString(buf); + END WriteLongReal; + + PROCEDURE WriteFixPt(arg: REAL; n, k: CARDINAL); + BEGIN + WriteLongFixPt(LONG(arg), n, k) + END WriteFixPt; + + PROCEDURE WriteLongFixPt(arg: LONGREAL; n, k: CARDINAL); + VAR buf: RBUF; + ok : BOOLEAN; + + BEGIN + IF n > MAXWIDTH THEN n := MAXWIDTH END; + LongRealToString(arg, n, k, buf, ok); + WriteString(buf); + END WriteLongFixPt; + + PROCEDURE ReadReal(VAR x: REAL); + VAR x1: LONGREAL; + BEGIN + ReadLongReal(x1); + x := x1 + END ReadReal; + + PROCEDURE ReadLongReal(VAR x: LONGREAL); + VAR Buf: ARRAY[0..512] OF CHAR; + ok: BOOLEAN; + + BEGIN + ReadString(Buf); + StringToLongReal(Buf, x, ok); + IF NOT ok THEN + Message("real expected"); + HALT; + END; + Done := TRUE; + END ReadLongReal; + + PROCEDURE wroct(x: ARRAY OF WORD); + VAR i: CARDINAL; + BEGIN + FOR i := 0 TO HIGH(x) DO + WriteOct(CARDINAL(x[i]), 0); + WriteString(" "); + END; + END wroct; + + PROCEDURE WriteRealOct(x: REAL); + BEGIN + wroct(x); + END WriteRealOct; + + PROCEDURE WriteLongRealOct(x: LONGREAL); + BEGIN + wroct(x); + END WriteLongRealOct; + +BEGIN + Done := FALSE; +END RealInOut. diff --git a/lib/libm2/SYSTEM.c b/lib/libm2/SYSTEM.c new file mode 100755 index 000000000..51def5596 --- /dev/null +++ b/lib/libm2/SYSTEM.c @@ -0,0 +1,115 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: SYSTEM + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ + +/* + An implementation of the Modula-2 NEWPROCESS and TRANSFER facilities + using the topsize, topsave, and topload facilities. + For each coroutine, a proc structure is built. For the main routine, + a static space is declared to save its stack. For the other coroutines, + the user specifies this space. +*/ + +#include <m2_traps.h> + +#define MAXMAIN 2048 + +struct proc { + unsigned size; /* size of saved stackframe(s) */ + int (*proc)(); /* address of coroutine procedure */ + char *brk; /* stack break of this coroutine */ +}; + +extern unsigned topsize(); + +static struct proc mainproc[MAXMAIN/sizeof(struct proc) + 1]; + +static struct proc *curproc = 0;/* current coroutine */ +extern char *MainLB; /* stack break of main routine */ + +_SYSTEM__NEWPROCESS(p, a, n, p1) + int (*p)(); /* coroutine procedure */ + struct proc *a; /* pointer to area for saved stack-frame */ + unsigned n; /* size of this area */ + struct proc **p1; /* where to leave coroutine descriptor, + in this implementation the address of + the area for saved stack-frame(s) */ +{ + /* This procedure creates a new coroutine, but does not + transfer control to it. The routine "topsize" will compute the + stack break, which will be the local base of this routine. + Notice that we can do this because we do not need the stack + above this point for this coroutine. In Modula-2, coroutines + must be level 0 procedures without parameters. + */ + char *brk = 0; + unsigned sz = topsize(&brk); + + if (sz + sizeof(struct proc) > n) { + /* not enough space */ + TRP(M2_TOOLARGE); + } + a->size = n; + a->proc = p; + a->brk = brk; + *p1 = a; + if (topsave(brk, a+1)) + /* stack frame saved; now just return */ + ; + else { + /* We get here through the first transfer to the coroutine + created above. + This also means that curproc is now set to this coroutine. + We cannot trust the parameters anymore. + Just call the coroutine procedure. + */ + (*(curproc->proc))(); + _cleanup(); + _exit(0); + } +} + +_SYSTEM__TRANSFER(a, b) + struct proc **a, **b; +{ + /* transfer from one coroutine to another, saving the current + descriptor in the space indicated by "a", and transfering to + the coroutine in descriptor "b". + */ + unsigned size; + + if (! curproc) { + /* the current coroutine is the main process; + initialize a coroutine descriptor for it ... + */ + mainproc[0].brk = MainLB; + mainproc[0].size = sizeof(mainproc); + curproc = &mainproc[0]; + } + *a = curproc; /* save current descriptor in "a" */ + if (*b == curproc) { + /* transfer to itself is a no-op */ + return; + } + size = topsize(&(curproc->brk)); + if (size + sizeof(struct proc) > curproc->size) { + TRP(M2_TOOLARGE); + } + if (topsave(curproc->brk, curproc+1)) { + /* stack top saved. Now restore context of target + coroutine + */ + curproc = *b; + topload(curproc+1); + /* we never get here ... */ + } + /* but we do get here, when a transfer is done to the coroutine in "a". + */ +} diff --git a/lib/libm2/Semaphores.mod b/lib/libm2/Semaphores.mod new file mode 100755 index 000000000..d6c056649 --- /dev/null +++ b/lib/libm2/Semaphores.mod @@ -0,0 +1,118 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE Semaphores [1]; +(* + Module: Processes with semaphores + Author: Ceriel J.H. Jacobs + Version: $Header$ + + Quasi-concurrency implementation +*) + + FROM SYSTEM IMPORT ADDRESS, NEWPROCESS, TRANSFER; + FROM Storage IMPORT Allocate; + FROM random IMPORT Uniform; + FROM Traps IMPORT Message; + + TYPE Sema = POINTER TO Semaphore; + Processes = POINTER TO Process; + Semaphore = + RECORD + level: CARDINAL; + END; + Process = + RECORD next: Processes; + proc: ADDRESS; + waiting: Sema; + END; + + VAR cp: Processes; (* current process *) + + PROCEDURE StartProcess(P: PROC; n: CARDINAL); + VAR s0: Processes; + wsp: ADDRESS; + BEGIN + s0 := cp; + Allocate(wsp, n); + Allocate(cp, SIZE(Process)); + WITH cp^ DO + next := s0^.next; + s0^.next := cp; + waiting := NIL; + END; + NEWPROCESS(P, wsp, n, cp^.proc); + TRANSFER(s0^.proc, cp^.proc); + END StartProcess; + + PROCEDURE Up(VAR s: Sema); + BEGIN + s^.level := s^.level + 1; + ReSchedule; + END Up; + + PROCEDURE Down(VAR s: Sema); + BEGIN + IF s^.level = 0 THEN + cp^.waiting := s; + ELSE + s^.level := s^.level - 1; + END; + ReSchedule; + END Down; + + PROCEDURE NewSema(n: CARDINAL): Sema; + VAR s: Sema; + BEGIN + Allocate(s, SIZE(Semaphore)); + s^.level := n; + RETURN s; + END NewSema; + + PROCEDURE Level(s: Sema): CARDINAL; + BEGIN + RETURN s^.level; + END Level; + + PROCEDURE ReSchedule; + VAR s0: Processes; + i, j: CARDINAL; + BEGIN + s0 := cp; + i := Uniform(1, 5); + j := i; + LOOP + cp := cp^.next; + IF Runnable(cp) THEN + DEC(i); + IF i = 0 THEN EXIT END; + END; + IF (cp = s0) AND (j = i) THEN + (* deadlock *) + Message("deadlock"); + HALT + END; + END; + IF cp # s0 THEN TRANSFER(s0^.proc, cp^.proc); END; + END ReSchedule; + + PROCEDURE Runnable(p: Processes): BOOLEAN; + BEGIN + IF p^.waiting = NIL THEN RETURN TRUE; END; + IF p^.waiting^.level > 0 THEN + p^.waiting^.level := p^.waiting^.level - 1; + p^.waiting := NIL; + RETURN TRUE; + END; + RETURN FALSE; + END Runnable; +BEGIN + Allocate(cp, SIZE(Process)); + WITH cp^ DO + next := cp; + waiting := NIL; + END +END Semaphores. diff --git a/lib/libm2/Storage.mod b/lib/libm2/Storage.mod new file mode 100755 index 000000000..a416f4901 --- /dev/null +++ b/lib/libm2/Storage.mod @@ -0,0 +1,353 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE Storage; +(* + Module: Dynamic Storage Allocation + Author: Ceriel J.H. Jacobs + Adapted from a version in C by Hans Tebra + Version: $Header$ +*) +(* This storage manager maintains an array of lists of objects with the + same size. Commonly used sizes have their own bucket. The larger ones + are put in a single list. +*) + FROM Unix IMPORT sbrk, ILLBREAK; + FROM SYSTEM IMPORT ADDRESS, ADR; + FROM Traps IMPORT Message; + + CONST + NLISTS = 20; + MAGICW = 0A5A5H; + MAGICC = 175C; + + TYPE + ALIGNTYPE = + RECORD + CASE : INTEGER OF + 1: l: LONGINT | + 2: p: ADDRESS | + 3: d: LONGREAL + END + END; (* A type with high alignment requirements *) + BucketPtr = POINTER TO Bucket; + Bucket = + RECORD + CASE : BOOLEAN OF + FALSE: + BNEXT: BucketPtr; (* next free Bucket *) + BSIZE: CARDINAL; | (* size of user part in UNITs *) + TRUE: BXX: ALIGNTYPE + END; + BSTORE: ALIGNTYPE; + END; + + CONST + UNIT = SIZE(ALIGNTYPE); + + VAR + FreeLists: ARRAY[0..NLISTS] OF BucketPtr; (* small blocks *) + Llist: BucketPtr; (* others *) + Compacted: BOOLEAN; (* avoid recursive reorganization *) + FirstBlock: BucketPtr; + USED: ADDRESS; + + PROCEDURE MyAllocate(size: CARDINAL) : ADDRESS; + VAR nu : CARDINAL; + b : CARDINAL; + p, q: BucketPtr; + pc: POINTER TO CHAR; + brk : ADDRESS; + BEGIN + IF size > CARDINAL(MAX(INTEGER)-2*UNIT + 1) THEN + RETURN NIL; + END; + nu := (size + (UNIT-1)) DIV UNIT; + IF nu = 0 THEN + nu := 1; + END; + IF nu <= NLISTS THEN + b := nu; + IF FreeLists[b] # NIL THEN + (* Exact fit *) + p := FreeLists[b]; + FreeLists[b] := p^.BNEXT; + p^.BNEXT := USED; + IF p^.BSIZE * UNIT # size THEN + pc := ADR(p^.BSTORE) + size; + pc^ := MAGICC; + END; + p^.BSIZE := size; + RETURN ADR(p^.BSTORE); + END; + + (* Search for a block with >= 2 units more than requested. + We pay for an additional header when the block is split. + *) + FOR b := b+2 TO NLISTS DO + IF FreeLists[b] # NIL THEN + q := FreeLists[b]; + FreeLists[b] := q^.BNEXT; + p := ADDRESS(q) + (nu+1)*UNIT; + (* p indicates the block that must be given + back + *) + p^.BSIZE := q^.BSIZE - nu - 1; + p^.BNEXT := FreeLists[p^.BSIZE]; + FreeLists[p^.BSIZE] := p; + q^.BSIZE := nu; + q^.BNEXT := USED; + IF q^.BSIZE * UNIT # size THEN + pc := ADR(q^.BSTORE) + size; + pc^ := MAGICC; + END; + q^.BSIZE := size; + RETURN ADR(q^.BSTORE); + END; + END; + END; + + p := Llist; + IF p # NIL THEN + q := NIL; + WHILE (p # NIL) AND (p^.BSIZE < nu) DO + q := p; + p := p^.BNEXT; + END; + + IF p # NIL THEN + (* p^.BSIZE >= nu *) + IF p^.BSIZE <= nu + NLISTS + 1 THEN + (* Remove p from this list *) + IF q # NIL THEN q^.BNEXT := p^.BNEXT + ELSE Llist := p^.BNEXT; + END; + p^.BNEXT := USED; + IF p^.BSIZE > nu + 1 THEN + (* split block, + tail goes to FreeLists area + *) + q := ADDRESS(p) + (nu+1)*UNIT; + q^.BSIZE := p^.BSIZE -nu -1; + q^.BNEXT := FreeLists[q^.BSIZE]; + FreeLists[q^.BSIZE] := q; + p^.BSIZE := nu; + END; + IF p^.BSIZE * UNIT # size THEN + pc := ADR(p^.BSTORE) + size; + pc^ := MAGICC; + END; + p^.BSIZE := size; + RETURN ADR(p^.BSTORE); + END; + (* Give part of tail of original block. + Block stays in this list. + *) + q := ADDRESS(p) + (p^.BSIZE-nu)*UNIT; + q^.BSIZE := nu; + p^.BSIZE := p^.BSIZE - nu - 1; + q^.BNEXT := USED; + IF q^.BSIZE * UNIT # size THEN + pc := ADR(q^.BSTORE) + size; + pc^ := MAGICC; + END; + q^.BSIZE := size; + RETURN ADR(q^.BSTORE); + END; + END; + + IF Compacted THEN + (* reorganization did not yield sufficient memory *) + RETURN NIL; + END; + + brk := sbrk(UNIT * (nu + 1)); + IF brk = ILLBREAK THEN + ReOrganize(); + Compacted := TRUE; + brk := MyAllocate(size); + Compacted := FALSE; + RETURN brk; + END; + + p := brk; + p^.BSIZE := nu; + p^.BNEXT := USED; + IF p^.BSIZE * UNIT # size THEN + pc := ADR(p^.BSTORE) + size; + pc^ := MAGICC; + END; + p^.BSIZE := size; + RETURN ADR(p^.BSTORE); + END MyAllocate; + + PROCEDURE ALLOCATE(VAR a: ADDRESS; size: CARDINAL); + BEGIN + Allocate(a, size); + END ALLOCATE; + + PROCEDURE Allocate(VAR a: ADDRESS; size: CARDINAL); + BEGIN + a := MyAllocate(size); + IF a = NIL THEN + Message("out of core"); + HALT; + END; + END Allocate; + + PROCEDURE Available(size: CARDINAL): BOOLEAN; + VAR a: ADDRESS; + BEGIN + a:= MyAllocate(size); + IF a # NIL THEN + Deallocate(a, size); + RETURN TRUE; + END; + RETURN FALSE; + END Available; + + PROCEDURE DEALLOCATE(VAR a: ADDRESS; size: CARDINAL); + BEGIN + Deallocate(a, size); + END DEALLOCATE; + + PROCEDURE Deallocate(VAR a: ADDRESS; size: CARDINAL); + VAR p: BucketPtr; + pc: POINTER TO CHAR; + BEGIN + IF (a = NIL) THEN + Message("(Warning) Deallocate: NIL pointer deallocated"); + RETURN; + END; + p := a - UNIT; + IF (p^.BNEXT # BucketPtr(USED)) THEN + Message("(Warning) Deallocate: area already deallocated or heap corrupted"); + a := NIL; + RETURN; + END; + WITH p^ DO + IF BSIZE # size THEN + Message("(Warning) Deallocate: wrong size or heap corrupted"); + END; + BSIZE := (size + (UNIT - 1)) DIV UNIT; + IF (BSIZE*UNIT # size) THEN + pc := a + size; + IF pc^ # MAGICC THEN + Message("(Warning) Deallocate: heap corrupted"); + END; + END; + IF BSIZE <= NLISTS THEN + BNEXT := FreeLists[BSIZE]; + FreeLists[BSIZE] := p; + ELSE + BNEXT := Llist; + Llist := p; + END; + END; + a := NIL + END Deallocate; + + PROCEDURE ReOrganize(); + VAR lastblock: BucketPtr; + b, be: BucketPtr; + i: CARDINAL; + BEGIN + lastblock := NIL; + FOR i := 1 TO NLISTS DO + b := FreeLists[i]; + WHILE b # NIL DO + IF ADDRESS(b) > ADDRESS(lastblock) THEN + lastblock := b; + END; + be := b^.BNEXT; + b^.BNEXT := NIL; (* temporary free mark *) + b := be; + END; + END; + + b := Llist; + WHILE b # NIL DO + IF ADDRESS(b) > ADDRESS(lastblock) THEN + lastblock := b; + END; + be := b^.BNEXT; + b^.BNEXT := NIL; + b := be; + END; + + (* Now, all free blocks have b^.BNEXT = NIL *) + + b := FirstBlock; + WHILE ADDRESS(b) < ADDRESS(lastblock) DO + LOOP + be := ADDRESS(b)+(b^.BSIZE+1)*UNIT; + IF b^.BNEXT # NIL THEN + (* this block is not free *) + EXIT; + END; + IF ADDRESS(be) > ADDRESS(lastblock) THEN + (* no next block *) + EXIT; + END; + IF be^.BNEXT # NIL THEN + (* next block is not free *) + EXIT; + END; + (* this block and the next one are free, + so merge them, but only if it is not too big + *) + IF MAX(CARDINAL) - b^.BSIZE > be^.BSIZE THEN + b^.BSIZE := b^.BSIZE + be^.BSIZE + 1; + ELSE + EXIT; + END; + END; + b := be; + END; + + (* clear all free lists *) + FOR i := 1 TO NLISTS DO FreeLists[i] := NIL; END; + Llist := NIL; + + (* collect free blocks in them again *) + b := FirstBlock; + WHILE ADDRESS(b) <= ADDRESS(lastblock) DO + WITH b^ DO + IF BNEXT = NIL THEN + IF BSIZE <= NLISTS THEN + BNEXT := FreeLists[BSIZE]; + FreeLists[BSIZE] := b; + ELSE + BNEXT := Llist; + Llist := b; + END; + b := ADDRESS(b) + (BSIZE+1) * UNIT; + ELSE + b := ADDRESS(b) + + ((BSIZE + (UNIT - 1)) DIV UNIT + 1) * UNIT; + END; + END; + END; + END ReOrganize; + + PROCEDURE InitStorage(); + VAR i: CARDINAL; + brk: ADDRESS; + BEGIN + FOR i := 1 TO NLISTS DO + FreeLists[i] := NIL; + END; + Llist := NIL; + brk := sbrk(0); + brk := sbrk(UNIT - brk MOD UNIT); + FirstBlock := sbrk(0); + Compacted := FALSE; + USED := MAGICW; + END InitStorage; + +BEGIN + InitStorage(); +END Storage. diff --git a/lib/libm2/StrAss.c b/lib/libm2/StrAss.c new file mode 100755 index 000000000..c8c49f713 --- /dev/null +++ b/lib/libm2/StrAss.c @@ -0,0 +1,23 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: assign string to character array, with possible 0-byte + extension + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ +StringAssign(dstsiz, srcsiz, dstaddr, srcaddr) + register char *dstaddr, *srcaddr; +{ + while (srcsiz > 0) { + *dstaddr++ = *srcaddr++; + srcsiz--; + dstsiz--; + } + if (dstsiz > 0) { + *dstaddr = 0; + } +} diff --git a/lib/libm2/Streams.mod b/lib/libm2/Streams.mod new file mode 100755 index 000000000..451470149 --- /dev/null +++ b/lib/libm2/Streams.mod @@ -0,0 +1,443 @@ +# +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE Streams; +(* + Module: Stream Input/Output + Author: Ceriel J.H. Jacobs + Version: $Header$ + + Implementation for Unix +*) + + FROM SYSTEM IMPORT BYTE, ADR; + FROM Epilogue IMPORT CallAtEnd; + FROM Storage IMPORT Allocate, Available; + FROM StripUnix IMPORT + open, close, lseek, read, write, creat; + IMPORT StripUnix; + + CONST BUFSIZ = 1024; (* tunable *) + TYPE IOB = RECORD + kind: StreamKind; + mode: StreamMode; + eof: BOOLEAN; + buffering: StreamBuffering; + next : Stream; + fildes: INTEGER; + cnt, maxcnt: INTEGER; + bufferedcnt: INTEGER; + buf: ARRAY[1..BUFSIZ] OF BYTE; + END; + Stream = POINTER TO IOB; + VAR + ibuf, obuf, ebuf: IOB; + head: Stream; + + PROCEDURE getstruct(VAR stream: Stream); + BEGIN + stream := head; + WHILE (stream # NIL) AND (stream^.kind # none) DO + stream := stream^.next; + END; + IF stream = NIL THEN + IF NOT Available(SIZE(IOB)) THEN + RETURN; + END; + Allocate(stream,SIZE(IOB)); + stream^.next := head; + head := stream; + END; + END getstruct; + + PROCEDURE freestruct(stream: Stream); + BEGIN + stream^.kind := none; + END freestruct; + + PROCEDURE OpenStream(VAR stream: Stream; + filename: ARRAY OF CHAR; + kind: StreamKind; + mode: StreamMode; + VAR result: StreamResult); + VAR fd: INTEGER; + i: CARDINAL; + BEGIN + IF kind = none THEN + result := illegaloperation; + RETURN; + END; + getstruct(stream); + IF stream = NIL THEN + result := nomemory; + RETURN; + END; + WITH stream^ DO + FOR i := 0 TO HIGH(filename) DO + buf[i+1] := BYTE(filename[i]); + END; + buf[HIGH(filename)+2] := BYTE(0C); + END; + IF (mode = reading) THEN + fd := open(ADR(stream^.buf), 0); + ELSE + fd := -1; + IF (mode = appending) THEN + fd := open(ADR(stream^.buf), 1); + IF fd >= 0 THEN + IF (lseek(fd, 0D , 2) < 0D) THEN ; END; + END; + END; + IF fd < 0 THEN + fd := creat(ADR(stream^.buf), 666B); + END; + END; + IF fd < 0 THEN + result := openfailed; + freestruct(stream); + stream := NIL; + RETURN; + END; + result := succeeded; + stream^.fildes := fd; + stream^.kind := kind; + stream^.mode := mode; + stream^.buffering := blockbuffered; + stream^.bufferedcnt := BUFSIZ; + stream^.maxcnt := 0; + stream^.eof := FALSE; + IF mode = reading THEN + stream^.cnt := 1; + ELSE + stream^.cnt := 0; + END; + END OpenStream; + + PROCEDURE SetStreamBuffering( stream: Stream; + b: StreamBuffering; + VAR result: StreamResult); + BEGIN + result := succeeded; + IF (stream = NIL) OR (stream^.kind = none) THEN + result := nostream; + RETURN; + END; + IF (stream^.mode = reading) OR + ((b = linebuffered) AND (stream^.kind = binary)) THEN + result := illegaloperation; + RETURN; + END; + FlushStream(stream, result); + IF b = unbuffered THEN + stream^.bufferedcnt := 1; + END; + stream^.buffering := b; + END SetStreamBuffering; + + PROCEDURE FlushStream(stream: Stream; VAR result: StreamResult); + VAR cnt1: INTEGER; + BEGIN + result := succeeded; + IF (stream = NIL) OR (stream^.kind = none) THEN + result := nostream; + RETURN; + END; + WITH stream^ DO + IF mode = reading THEN + result := illegaloperation; + RETURN; + END; + IF (cnt > 0) THEN + cnt1 := cnt; + cnt := 0; + IF write(fildes, ADR(buf), cnt1) < 0 THEN END; + END; + END; + END FlushStream; + + PROCEDURE CloseStream(VAR stream: Stream; VAR result: StreamResult); + BEGIN + IF (stream # NIL) AND (stream^.kind # none) THEN + result := succeeded; + IF stream^.mode # reading THEN + FlushStream(stream, result); + END; + IF close(stream^.fildes) < 0 THEN ; END; + freestruct(stream); + ELSE + result := nostream; + END; + stream := NIL; + END CloseStream; + + PROCEDURE EndOfStream(stream: Stream; VAR result: StreamResult): BOOLEAN; + BEGIN + result := succeeded; + IF (stream = NIL) OR (stream^.kind = none) THEN + result := nostream; + RETURN FALSE; + END; + IF stream^.mode # reading THEN + result := illegaloperation; + RETURN FALSE; + END; + IF stream^.eof THEN RETURN TRUE; END; + RETURN (CHAR(NextByte(stream)) = 0C) AND stream^.eof; + END EndOfStream; + + PROCEDURE FlushLineBuffers(); + VAR s: Stream; + result: StreamResult; + BEGIN + s := head; + WHILE s # NIL DO + IF (s^.kind # none) AND (s^.buffering = linebuffered) THEN + FlushStream(s, result); + END; + s := s^.next; + END; + END FlushLineBuffers; + + PROCEDURE NextByte(stream: Stream): BYTE; + VAR c: BYTE; + BEGIN + WITH stream^ DO + IF cnt <= maxcnt THEN + c := buf[cnt]; + ELSE + IF eof THEN RETURN BYTE(0C); END; + IF stream = InputStream THEN + FlushLineBuffers(); + END; + maxcnt := read(fildes, ADR(buf), bufferedcnt); + cnt := 1; + IF maxcnt <= 0 THEN + eof := TRUE; + c := BYTE(0C); + ELSE + c := buf[1]; + END; + END; + END; + RETURN c; + END NextByte; + + PROCEDURE Read(stream: Stream; VAR ch: CHAR; VAR result: StreamResult); + VAR EoF: BOOLEAN; + BEGIN + ch := 0C; + EoF := EndOfStream(stream, result); + IF result # succeeded THEN RETURN; END; + IF EoF THEN + result := endoffile; + RETURN; + END; + WITH stream^ DO + ch := CHAR(buf[cnt]); + INC(cnt); + END; + END Read; + + PROCEDURE ReadByte(stream: Stream; VAR byte: BYTE; VAR result: StreamResult); + VAR EoF: BOOLEAN; + BEGIN + byte := BYTE(0C); + EoF := EndOfStream(stream, result); + IF result # succeeded THEN RETURN; END; + IF EoF THEN + result := endoffile; + RETURN; + END; + WITH stream^ DO + byte := buf[cnt]; + INC(cnt); + END; + END ReadByte; + + PROCEDURE ReadBytes(stream: Stream; + VAR bytes: ARRAY OF BYTE; + VAR result: StreamResult); + VAR i: CARDINAL; + BEGIN + FOR i := 0 TO HIGH(bytes) DO + ReadByte(stream, bytes[i], result); + END; + END ReadBytes; + + PROCEDURE Write(stream: Stream; ch: CHAR; VAR result: StreamResult); + BEGIN + IF (stream = NIL) OR (stream^.kind = none) THEN + result := nostream; + RETURN; + END; + IF (stream^.kind # text) OR (stream^.mode = reading) THEN + result := illegaloperation; + RETURN; + END; + WITH stream^ DO + INC(cnt); + buf[cnt] := BYTE(ch); + IF (cnt >= bufferedcnt) OR + ((ch = 12C) AND (buffering = linebuffered)) + THEN + FlushStream(stream, result); + END; + END; + END Write; + + PROCEDURE WriteByte(stream: Stream; byte: BYTE; VAR result: StreamResult); + BEGIN + IF (stream = NIL) OR (stream^.kind = none) THEN + result := nostream; + RETURN; + END; + IF (stream^.kind # binary) OR (stream^.mode = reading) THEN + result := illegaloperation; + RETURN; + END; + WITH stream^ DO + INC(cnt); + buf[cnt] := byte; + IF cnt >= bufferedcnt THEN + FlushStream(stream, result); + END; + END; + END WriteByte; + + PROCEDURE WriteBytes(stream: Stream; bytes: ARRAY OF BYTE; VAR result: StreamResult); + VAR i: CARDINAL; + BEGIN + FOR i := 0 TO HIGH(bytes) DO + WriteByte(stream, bytes[i], result); + END; + END WriteBytes; + + PROCEDURE EndIt; + VAR h, h1 : Stream; + result: StreamResult; + BEGIN + h := head; + WHILE h # NIL DO + h1 := h; + CloseStream(h1, result); + h := h^.next; + END; + END EndIt; + + PROCEDURE GetPosition(s: Stream; VAR position: LONGINT; + VAR result: StreamResult); + BEGIN + IF (s = NIL) OR (s^.kind = none) THEN + result := illegaloperation; + RETURN; + END; + IF (s^.mode # reading) THEN FlushStream(s, result); END; + position := lseek(s^.fildes, 0D, 1); + IF position < 0D THEN + result := illegaloperation; + RETURN; + END; + IF s^.mode = reading THEN + position := position + LONG(s^.maxcnt - s^.cnt + 1); + END; + END GetPosition; + + PROCEDURE SetPosition(s: Stream; position: LONGINT; VAR result: StreamResult); + VAR currpos: LONGINT; + BEGIN + currpos := 0D; + IF (s = NIL) OR (s^.kind = none) THEN + result := nostream; + RETURN; + END; + IF (s^.mode # reading) THEN + FlushStream(s, result); + ELSE + s^.maxcnt := 0; + s^.eof := FALSE; + END; + IF s^.mode = appending THEN + currpos := lseek(s^.fildes, 0D, 1); + IF currpos < 0D THEN + result := illegaloperation; + RETURN; + END; + END; + IF position < currpos THEN + result := illegaloperation; + RETURN; + END; + currpos := lseek(s^.fildes, position, 0); + IF currpos < 0D THEN + result := illegaloperation; + RETURN; + END; + result := succeeded; + END SetPosition; + + PROCEDURE isatty(stream: Stream; VAR result: StreamResult): BOOLEAN; + BEGIN + IF (stream = NIL) OR (stream^.kind = none) THEN + result := nostream; + RETURN FALSE; + END; + RETURN StripUnix.isatty(stream^.fildes); + END isatty; + + PROCEDURE InitStreams; + VAR result: StreamResult; + BEGIN + InputStream := ADR(ibuf); + OutputStream := ADR(obuf); + ErrorStream := ADR(ebuf); + WITH ibuf DO + kind := text; + mode := reading; + eof := FALSE; + next := ADR(obuf); + fildes := 0; + maxcnt := 0; + cnt := 1; + bufferedcnt := BUFSIZ; + END; + WITH obuf DO + kind := text; + mode := writing; + eof := TRUE; + next := ADR(ebuf); + fildes := 1; + maxcnt := 0; + cnt := 0; + bufferedcnt := BUFSIZ; + IF isatty(OutputStream, result) THEN + buffering := linebuffered; + ELSE + buffering := blockbuffered; + END; + END; + WITH ebuf DO + kind := text; + mode := writing; + eof := TRUE; + next := NIL; + fildes := 2; + maxcnt := 0; + cnt := 0; + bufferedcnt := BUFSIZ; + IF isatty(ErrorStream, result) THEN + buffering := linebuffered; + ELSE + buffering := blockbuffered; + END; + END; + head := InputStream; + IF CallAtEnd(EndIt) THEN ; END; + END InitStreams; + +BEGIN + InitStreams +END Streams. diff --git a/lib/libm2/Strings.mod b/lib/libm2/Strings.mod new file mode 100755 index 000000000..8ae31eea5 --- /dev/null +++ b/lib/libm2/Strings.mod @@ -0,0 +1,171 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE Strings; +(* + Module: String manipulations + Author: Ceriel J.H. Jacobs + Version: $Header$ +*) + + PROCEDURE Assign(source: ARRAY OF CHAR; VAR dest: ARRAY OF CHAR); + (* Assign string source to dest + *) + VAR i: CARDINAL; + max: CARDINAL; + BEGIN + max := HIGH(source); + IF HIGH(dest) < max THEN max := HIGH(dest); END; + i := 0; + WHILE (i <= max) AND (source[i] # 0C) DO + dest[i] := source[i]; + INC(i); + END; + IF i < HIGH(dest) THEN dest[i] := 0C; END; + END Assign; + + PROCEDURE Insert(substr: ARRAY OF CHAR; VAR str: ARRAY OF CHAR; inx: CARDINAL); + (* Insert the string substr into str, starting at str[inx]. + If inx is equal to or greater than Length(str) then substr is appended + to the end of str. + *) + VAR sublen, length, i: CARDINAL; + BEGIN + sublen := Length(substr); + IF sublen = 0 THEN RETURN; END; + length := Length(str); + IF inx > length THEN inx := length; END; + i := length; + IF i + sublen - 1 > HIGH(str) THEN i := HIGH(str); END; + WHILE i > inx DO + str[i+sublen-1] := str[i-1]; + DEC(i); + END; + FOR i := 0 TO sublen - 1 DO + IF i + inx <= HIGH(str) THEN + str[i + inx] := substr[i]; + ELSE + RETURN; + END; + END; + IF length + sublen <= HIGH(str) THEN + str[length + sublen] := 0C; + END; + END Insert; + + PROCEDURE Delete(VAR str: ARRAY OF CHAR; inx, len: CARDINAL); + (* Delete len characters from str, starting at str[inx]. + If inx >= Length(str) then nothing happens. + If there are not len characters to delete, characters to the end of the + string are deleted. + *) + VAR length: CARDINAL; + BEGIN + IF len = 0 THEN RETURN; END; + length := Length(str); + IF inx >= length THEN RETURN; END; + WHILE inx + len < length DO + str[inx] := str[inx + len]; + INC(inx); + END; + str[inx] := 0C; + END Delete; + + PROCEDURE Pos(substr, str: ARRAY OF CHAR): CARDINAL; + (* Return the index into str of the first occurrence of substr. + Pos returns a value greater than HIGH(str) of no occurrence is found. + *) + VAR i, j, max, subl: CARDINAL; + BEGIN + max := Length(str); + subl := Length(substr); + IF subl > max THEN RETURN HIGH(str) + 1; END; + IF subl = 0 THEN RETURN 0; END; + max := max - subl; + FOR i := 0 TO max DO + j := 0; + WHILE (j <= subl-1) AND (str[i+j] = substr[j]) DO + INC(j); + END; + IF j = subl THEN RETURN i; END; + END; + RETURN HIGH(str) + 1; + END Pos; + + PROCEDURE Copy(str: ARRAY OF CHAR; + inx, len: CARDINAL; + VAR result: ARRAY OF CHAR); + (* Copy at most len characters from str into result, starting at str[inx]. + *) + VAR i: CARDINAL; + BEGIN + IF Length(str) <= inx THEN RETURN END; + i := 0; + LOOP + IF i > HIGH(result) THEN RETURN; END; + IF len = 0 THEN EXIT; END; + IF inx > HIGH(str) THEN EXIT; END; + result[i] := str[inx]; + INC(i); INC(inx); DEC(len); + END; + IF i <= HIGH(result) THEN result[i] := 0C; END; + END Copy; + + PROCEDURE Concat(s1, s2: ARRAY OF CHAR; VAR result: ARRAY OF CHAR); + (* Concatenate two strings. + *) + VAR i, j: CARDINAL; + BEGIN + i := 0; + WHILE (i <= HIGH(s1)) AND (s1[i] # 0C) DO + IF i > HIGH(result) THEN RETURN END; + result[i] := s1[i]; + INC(i); + END; + j := 0; + WHILE (j <= HIGH(s2)) AND (s2[j] # 0C) DO + IF i > HIGH(result) THEN RETURN END; + result[i] := s2[j]; + INC(i); + INC(j); + END; + IF i <= HIGH(result) THEN result[i] := 0C; END; + END Concat; + + PROCEDURE Length(str: ARRAY OF CHAR): CARDINAL; + (* Return number of characters in str. + *) + VAR i: CARDINAL; + BEGIN + i := 0; + WHILE (i <= HIGH(str)) DO + IF str[i] = 0C THEN RETURN i; END; + INC(i); + END; + RETURN i; + END Length; + + PROCEDURE CompareStr(s1, s2: ARRAY OF CHAR): INTEGER; + (* Compare two strings, return -1 if s1 < s2, 0 if s1 = s2, and 1 if s1 > s2. + *) + VAR i: CARDINAL; + max: CARDINAL; + BEGIN + max := HIGH(s1); + IF HIGH(s2) < max THEN max := HIGH(s2); END; + i := 0; + WHILE (i <= max) DO + IF s1[i] < s2[i] THEN RETURN -1; END; + IF s1[i] > s2[i] THEN RETURN 1; END; + IF s1[i] = 0C THEN RETURN 0; END; + INC(i); + END; + IF (i <= HIGH(s1)) AND (s1[i] # 0C) THEN RETURN 1; END; + IF (i <= HIGH(s2)) AND (s2[i] # 0C) THEN RETURN -1; END; + RETURN 0; + END CompareStr; + +END Strings. diff --git a/lib/libm2/Termcap.mod b/lib/libm2/Termcap.mod new file mode 100755 index 000000000..1098f7884 --- /dev/null +++ b/lib/libm2/Termcap.mod @@ -0,0 +1,99 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(* + Module: Interface to termcap database + From: Unix manual chapter 3 + Version: $Header$ +*) + +(*$R-*) +IMPLEMENTATION MODULE Termcap; + + IMPORT XXTermcap; + FROM SYSTEM IMPORT ADR, ADDRESS; + FROM Unix IMPORT isatty; + FROM Arguments IMPORT + GetEnv; + + TYPE STR = ARRAY[1..32] OF CHAR; + STRCAP = POINTER TO STR; + + VAR Buf, Buf1 : ARRAY [1..1024] OF CHAR; + BufCnt : INTEGER; + + PROCEDURE Tgetent(name: ARRAY OF CHAR) : INTEGER; + VAR i: INTEGER; + x: STRCAP; + BEGIN + i := XXTermcap.tgetent(ADR(Buf), ADR(name)); + BufCnt := 1; + IF isatty(1) THEN + ELSE + (* This used to be something returned by gtty(). To increase + * portability we forget about old terminals needing delays. + * (kjb) + *) + XXTermcap.ospeed := 0; + END; + IF i > 0 THEN + IF Tgetstr("pc", x) THEN + XXTermcap.PC := x^[1]; + ELSE XXTermcap.PC := 0C; + END; + IF Tgetstr("up", x) THEN ; END; XXTermcap.UP := x; + IF Tgetstr("bc", x) THEN ; END; XXTermcap.BC := x; + END; + RETURN i; + END Tgetent; + + PROCEDURE Tgetnum(id: ARRAY OF CHAR): INTEGER; + BEGIN + RETURN XXTermcap.tgetnum(ADR(id)); + END Tgetnum; + + PROCEDURE Tgetflag(id: ARRAY OF CHAR): BOOLEAN; + BEGIN + RETURN XXTermcap.tgetflag(ADR(id)) = 1; + END Tgetflag; + + PROCEDURE Tgoto(cm: STRCAP; col, line: INTEGER): STRCAP; + BEGIN + RETURN XXTermcap.tgoto(cm, col, line); + END Tgoto; + + PROCEDURE Tgetstr(id: ARRAY OF CHAR; VAR res: STRCAP) : BOOLEAN; + VAR a, a2: ADDRESS; + b: CARDINAL; + BEGIN + a := ADR(Buf1[BufCnt]); + a2 := XXTermcap.tgetstr(ADR(id), ADR(a)); + res := a2; + IF a2 = NIL THEN + RETURN FALSE; + END; + b := a - a2; + INC(BufCnt, b); + RETURN TRUE; + END Tgetstr; + + PROCEDURE Tputs(cp: STRCAP; affcnt: INTEGER; p: PUTPROC); + BEGIN + XXTermcap.tputs(cp, affcnt, XXTermcap.PUTPROC(p)); + END Tputs; + + PROCEDURE InitTermcap; + VAR Bf: STR; + BEGIN + IF GetEnv("TERM", Bf) = 0 THEN + Bf := "dumb"; + END; + IF Tgetent(Bf) <= 0 THEN + END; + END InitTermcap; + +BEGIN + InitTermcap; +END Termcap. diff --git a/lib/libm2/Terminal.mod b/lib/libm2/Terminal.mod new file mode 100755 index 000000000..7a6c0a66f --- /dev/null +++ b/lib/libm2/Terminal.mod @@ -0,0 +1,114 @@ +# +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE Terminal; +(* + Module: Input/Output to/from terminals + Author: Ceriel J.H. Jacobs + Version: $Header$ + + Implementation for Unix. +*) + FROM SYSTEM IMPORT ADR; +#ifdef __USG + FROM Unix IMPORT read, write, open, fcntl; +#else + FROM Unix IMPORT read, write, open, ioctl; +#endif + VAR fildes: INTEGER; + unreadch: CHAR; + unread: BOOLEAN; + tty: ARRAY[0..8] OF CHAR; + + PROCEDURE Read(VAR ch: CHAR); + BEGIN + IF unread THEN + ch := unreadch; + unread := FALSE + ELSE + IF read(fildes, ADR(ch), 1) < 0 THEN + ; + END; + END; + unreadch := ch; + END Read; + + PROCEDURE BusyRead(VAR ch: CHAR); + VAR l: INTEGER; + BEGIN + IF unread THEN + ch := unreadch; + unread := FALSE + ELSE +#ifdef __USG + l := fcntl(fildes, (*FGETFL*) 3, 0); + IF fcntl(fildes, + (* FSETFL *) 4, + l + (*ONDELAY*) 2) < 0 THEN + ; + END; + IF read(fildes, ADR(ch), 1) = 0 THEN + ch := 0C; + ELSE + unreadch := ch; + END; + IF fcntl(fildes, (*FSETFL*)4, l) < 0 THEN + ; + END; +#else +#ifdef __BSD4_2 + IF ioctl(fildes, INTEGER(ORD('f')*256+127+4*65536+40000000H), ADR(l)) < 0 THEN +#else + IF ioctl(fildes, INTEGER(ORD('f')*256+127), ADR(l)) < 0 THEN +#endif + ; + END; + + IF l = 0 THEN + ch := 0C; + ELSE + IF read(fildes, ADR(ch), 1) < 0 THEN + ; + END; + unreadch := ch; + END; +#endif + END; + END BusyRead; + + PROCEDURE ReadAgain; + BEGIN + unread := TRUE; + END ReadAgain; + + PROCEDURE Write(ch: CHAR); + BEGIN + IF write(fildes, ADR(ch), 1) < 0 THEN + ; + END; + END Write; + + PROCEDURE WriteLn; + BEGIN + Write(12C); + END WriteLn; + + PROCEDURE WriteString(s: ARRAY OF CHAR); + VAR i: CARDINAL; + BEGIN + i := 0; + WHILE (i <= HIGH(s)) & (s[i] # 0C) DO + Write(s[i]); + INC(i) + END + END WriteString; + +BEGIN + tty := "/dev/tty"; + fildes := open(ADR(tty), 2); + unread := FALSE; +END Terminal. diff --git a/lib/libm2/Traps.mod b/lib/libm2/Traps.mod new file mode 100755 index 000000000..913ee4a8d --- /dev/null +++ b/lib/libm2/Traps.mod @@ -0,0 +1,96 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE Traps; +(* + Module: Facility for handling traps + Author: Ceriel J.H. Jacobs + Version: $Header$ +*) + + FROM EM IMPORT SIG, LINO, FILN, TRP; + FROM Unix IMPORT write; + FROM SYSTEM IMPORT ADDRESS, ADR; + FROM Arguments IMPORT + Argv; + + PROCEDURE InstallTrapHandler(t: TrapHandler): TrapHandler; + (* Install a new trap handler, and return the previous one. + Parameter of trap handler is the trap number. + *) + BEGIN + RETURN SIG(t); + END InstallTrapHandler; + + PROCEDURE Message(str: ARRAY OF CHAR); + (* Write message "str" on standard error, preceeded by filename and + linenumber if possible + *) + VAR p: POINTER TO CHAR; + l: CARDINAL; + lino: INTEGER; + buf, buf2: ARRAY [0..255] OF CHAR; + i, j: CARDINAL; + BEGIN + p := FILN(); + IF p # NIL THEN + i := 1; + buf[0] := '"'; + WHILE p^ # 0C DO + buf[i] := p^; + INC(i); + p := ADDRESS(p) + 1; + END; + buf[i] := '"'; + INC(i); + IF write(2, ADR(buf), i) < 0 THEN END; + ELSE + l := Argv(0, buf); + IF write(2, ADR(buf), l-1) < 0 THEN END; + END; + lino := LINO(); + i := 0; + IF lino # 0 THEN + i := 7; + buf[0] := ','; buf[1] := ' '; + buf[2] := 'l'; buf[3] := 'i'; buf[4] := 'n'; buf[5] := 'e'; + buf[6] := ' '; + IF lino < 0 THEN + buf[7] := '-'; + i := 8; + lino := - lino; + END; + j := 0; + REPEAT + buf2[j] := CHR(CARDINAL(lino) MOD 10 + ORD('0')); + lino := lino DIV 10; + INC(j); + UNTIL lino = 0; + WHILE j > 0 DO + DEC(j); + buf[i] := buf2[j]; + INC(i); + END; + END; + buf[i] := ':'; + buf[i+1] := ' '; + IF write(2, ADR(buf), i+2) < 0 THEN END; + i := 0; + WHILE (i <= HIGH(str)) AND (str[i] # 0C) DO + INC(i); + END; + IF write(2, ADR(str), i) < 0 THEN END; + buf[0] := 12C; + IF write(2, ADR(buf), 1) < 0 THEN END; + END Message; + + PROCEDURE Trap(n: INTEGER); + (* cause trap number "n" to occur *) + BEGIN + TRP(n); + END Trap; + +END Traps. diff --git a/lib/libm2/XXTermcap.c b/lib/libm2/XXTermcap.c new file mode 100755 index 000000000..56a4f9c77 --- /dev/null +++ b/lib/libm2/XXTermcap.c @@ -0,0 +1,573 @@ +/* + * termcap.c 1.1 20/7/87 agc Joypace Ltd + * + * Copyright Joypace Ltd, London, UK, 1987. All rights reserved. + * This file may be freely distributed provided that this notice + * remains attached. + * + * A public domain implementation of the termcap(3) routines. + * + * Made fully functional by Ceriel J.H. Jacobs. + * + * BUGS: + * - does not check termcap entry sizes + * - not fully tested + */ + +#define CAPABLEN 2 + +#define ISSPACE(c) ((c) == ' ' || (c) == '\t' || (c) == '\r' || (c) == '\n') +#define ISDIGIT(x) ((x) >= '0' && (x) <= '9') + +short ospeed = 0; /* output speed */ +char PC = 0; /* padding character */ +char *BC = 0; /* back cursor movement */ +char *UP = 0; /* up cursor movement */ + +static char *capab = 0; /* the capability itself */ +static int check_for_tc(); +static int match_name(); + +#define NULL 0 + +/* Some things from C-library, needed here because the C-library is not + loaded with Modula-2 programs +*/ + +static char * +strcat(s1, s2) +register char *s1, *s2; +{ + /* Append s2 to the end of s1. */ + + char *original = s1; + + /* Find the end of s1. */ + while (*s1 != 0) s1++; + + /* Now copy s2 to the end of s1. */ + while (*s1++ = *s2++) /* nothing */ ; + return(original); +} + +static char * +strcpy(s1, s2) +register char *s1, *s2; +{ +/* Copy s2 to s1. */ + char *original = s1; + + while (*s1++ = *s2++) /* nothing */; + return(original); +} + +static int +strlen(s) +char *s; +{ +/* Return length of s. */ + + char *original = s; + + while (*s != 0) s++; + return(s - original); +} + +static int +strcmp(s1, s2) +register char *s1, *s2; +{ +/* Compare 2 strings. */ + + for(;;) { + if (*s1 != *s2) { + if (!*s1) return -1; + if (!*s2) return 1; + return(*s1 - *s2); + } + if (*s1++ == 0) return(0); + s2++; + } +} + +static int +strncmp(s1, s2, n) + register char *s1, *s2; + int n; +{ +/* Compare two strings, but at most n characters. */ + + while (n-- > 0) { + if (*s1 != *s2) { + if (!*s1) return -1; + if (!*s2) return 1; + return(*s1 - *s2); + } + if (*s1++ == 0) break; + s2++; + } + return 0; +} + +static char * +getenv(name) +register char *name; +{ + extern char ***_penviron; + register char **v = *_penviron, *p, *q; + + if (v == 0 || name == 0) return 0; + while ((p = *v++) != 0) { + q = name; + while (*q && *q++ == *p++) /* nothing */ ; + if (*q || *p != '=') continue; + return(p+1); + } + return(0); +} + +static char * +fgets(buf, count, fd) + char *buf; +{ + static char bf[1024]; + static int cnt = 0; + static char *pbf = &bf[0]; + register char *c = buf; + + + while (--count) { + if (pbf >= &bf[cnt]) { + if ((cnt = read(fd, bf, 1024)) <= 0) { + if (c == buf) return (char *) NULL; + *c = 0; + return buf; + } + pbf = &bf[0]; + } + *c = *pbf++; + if (*c++ == '\n') { + *c = 0; + return buf; + } + } + *c = 0; + return buf; +} + +/* + * tgetent - get the termcap entry for terminal name, and put it + * in bp (which must be an array of 1024 chars). Returns 1 if + * termcap entry found, 0 if not found, and -1 if file not found. + */ +int +tgetent(bp, name) +char *bp; +char *name; +{ + int fp; + char *file; + char *cp; + short len = strlen(name); + char buf[1024]; + + capab = bp; + if ((file = getenv("TERMCAP")) != (char *) NULL) { + if (*file != '/' && + (cp = getenv("TERM")) != NULL && strcmp(name, cp) == 0) { + (void) strcpy(bp, file); + return(1); + } + else file = "/etc/termcap"; + } else + file = "/etc/termcap"; + if ((fp = open(file, 0)) < 0) { + capab = 0; + return(-1); + } + while (fgets(buf, 1024, fp) != NULL) { + if (buf[0] == '#') continue; + while (*(cp = &buf[strlen(buf) - 2]) == '\\') + if (fgets(cp, 1024, fp) == NULL) + return (0); + if (match_name(buf, name)) { + strcpy(bp, buf); + close(fp); + if(check_for_tc() == 0) { + capab = 0; + return 0; + } + return 1; + } + } + capab = 0; + close(fp); + return(0); +} + +/* + * Compare the terminal name with each termcap entry name; Return 1 if a + * match is found. + */ +static int +match_name(buf, name) + char *buf; + char *name; +{ + register char *tp = buf; + register char *np; + + for (;;) { + for (np = name; *np && *tp == *np; np++, tp++) { } + if (*np == 0 && (*tp == '|' || *tp == ':' || *tp == 0)) + return(1); + while (*tp != 0 && *tp != '|' && *tp != ':') tp++; + if (*tp++ != '|') return (0); + } +} + +/* + * Handle tc= definitions recursively. + */ +static int +check_for_tc() +{ + static int count = 0; + char *savcapab = capab; + char buf[1024]; + char terminalname[128]; + register char *p = capab + strlen(capab) - 2, *q; + + while (*p != ':') + if (--p < capab) + return(0); /* no : in termcap entry */ + if (p[1] != 't' || p[2] != 'c') + return(1); + if (count > 16) { + return(0); /* recursion in tc= definitions */ + } + count++; + strcpy(terminalname, &p[4]); + q = terminalname; + while (*q && *q != ':') q++; + *q = 0; + if (tgetent(buf, terminalname) != 1) { + --count; + return(0); + } + --count; + for (q = buf; *q && *q != ':'; q++) { } + strcpy(p, q); + capab = savcapab; + return(1); +} + +/* + * tgetnum - get the numeric terminal capability corresponding + * to id. Returns the value, -1 if invalid. + */ +int +tgetnum(id) +char *id; +{ + char *cp; + int ret; + + if ((cp = capab) == NULL || id == NULL || *cp == 0) + return(-1); + while (*++cp && *cp != ':') + ; + while (*cp) { + cp++; + while (ISSPACE(*cp)) + cp++; + if (strncmp(cp, id, CAPABLEN) == 0) { + while (*cp && *cp != ':' && *cp != '#') + cp++; + if (*cp != '#') + return(-1); + for (ret = 0, cp++ ; *cp && ISDIGIT(*cp) ; cp++) + ret = ret * 10 + *cp - '0'; + return(ret); + } + while (*cp && *cp != ':') + cp++; + } + return(-1); +} + +/* + * tgetflag - get the boolean flag corresponding to id. Returns -1 + * if invalid, 0 if the flag is not in termcap entry, or 1 if it is + * present. + */ +int +tgetflag(id) +char *id; +{ + char *cp; + + if ((cp = capab) == NULL || id == NULL || *cp == 0) + return(-1); + while (*++cp && *cp != ':') + ; + while (*cp) { + cp++; + while (ISSPACE(*cp)) + cp++; + if (strncmp(cp, id, CAPABLEN) == 0) + return(1); + while (*cp && *cp != ':') + cp++; + } + return(0); +} + +/* + * tgetstr - get the string capability corresponding to id and place + * it in area (advancing area at same time). Expand escape sequences + * etc. Returns the string, or NULL if it can't do it. + */ +char * +tgetstr(id, area) +char *id; +char **area; +{ + char *cp; + char *ret; + int i; + + if ((cp = capab) == NULL || id == NULL || *cp == 0) + return(NULL); + while (*++cp != ':') + ; + while (*cp) { + cp++; + while (ISSPACE(*cp)) + cp++; + if (strncmp(cp, id, CAPABLEN) == 0) { + while (*cp && *cp != ':' && *cp != '=') + cp++; + if (*cp != '=') + return(NULL); + for (ret = *area, cp++; *cp && *cp != ':' ; (*area)++, cp++) + switch(*cp) { + case '^' : + **area = *++cp - 'A' + 1; + break; + case '\\' : + switch(*++cp) { + case 'E' : + **area = '\033'; + break; + case 'n' : + **area = '\n'; + break; + case 'r' : + **area = '\r'; + break; + case 't' : + **area = '\t'; + break; + case 'b' : + **area = '\b'; + break; + case 'f' : + **area = '\f'; + break; + case '0' : + case '1' : + case '2' : + case '3' : + for (i=0 ; *cp && ISDIGIT(*cp) ; cp++) + i = i * 8 + *cp - '0'; + **area = i; + cp--; + break; + case '^' : + case '\\' : + **area = *cp; + break; + } + break; + default : + **area = *cp; + } + *(*area)++ = '\0'; + return(ret); + } + while (*cp && *cp != ':') + cp++; + } + return(NULL); +} + +/* + * tgoto - given the cursor motion string cm, make up the string + * for the cursor to go to (destcol, destline), and return the string. + * Returns "OOPS" if something's gone wrong, or the string otherwise. + */ +char * +tgoto(cm, destcol, destline) +char *cm; +int destcol; +int destline; +{ + register char *rp; + static char ret[32]; + char added[16]; + int *dp = &destline; + int numval; + int swapped = 0; + + added[0] = 0; + for (rp = ret ; *cm ; cm++) { + if (*cm == '%') { + switch(*++cm) { + case '>' : + if (dp == NULL) + return("OOPS"); + cm++; + if (*dp > *cm++) { + *dp += *cm; + } + break; + case '+' : + case '.' : + if (dp == NULL) + return("OOPS"); + if (*cm == '+') *dp = *dp + *++cm; + for (;;) { + switch(*dp) { + case 0: + case 04: + case '\t': + case '\n': + /* filter these out */ + if (dp == &destcol || swapped || UP) { + strcat(added, dp == &destcol || swapped ? + (BC ? BC : "\b") : + UP); + (*dp)++; + continue; + } + } + break; + } + *rp++ = *dp; + dp = (dp == &destline) ? &destcol : NULL; + break; + + case 'r' : { + int tmp = destline; + + destline = destcol; + destcol = tmp; + swapped = 1 - swapped; + break; + } + case 'n' : + destcol ^= 0140; + destline ^= 0140; + break; + + case '%' : + *rp++ = '%'; + break; + + case 'i' : + destcol++; + destline++; + break; + + case 'B' : + if (dp == NULL) + return("OOPS"); + *dp = 16 * (*dp / 10) + *dp % 10; + break; + + case 'D' : + if (dp == NULL) + return("OOPS"); + *dp = *dp - 2 * (*dp % 16); + break; + + case 'd' : + case '2' : + case '3' : + if (dp == NULL) + return("OOPS"); + numval = *dp; + dp = (dp == &destline) ? &destcol : NULL; + if (numval >= 100) { + *rp++ = '0' + numval / 100; + } + else if (*cm == '3') { + *rp++ = ' '; + } + if (numval >= 10) { + *rp++ = '0' + ((numval%100)/10); + } + else if (*cm == '3' || *cm == '2') { + *rp++ = ' '; + } + *rp++ = '0' + (numval%10); + break; + default : + return("OOPS"); + } + } + else *rp++ = *cm; + } + *rp = '\0'; + strcpy(rp, added); + return(ret); +} + +static int tens_of_ms_p_char[] = { /* index as returned by gtty */ + /* assume 10 bits per char */ + 0, 2000, 1333, 909, 743, 666, 500, 333, 166, 83, 55, 41, 20, 10, 5, 2 +}; +/* + * tputs - put the string cp out onto the terminal, using the function + * outc. Also handle padding. + */ +int +tputs(cp, affcnt, outc) +register char *cp; +int affcnt; +int (*outc)(); +{ + int delay = 0; + if (cp == NULL) + return(1); + while (ISDIGIT(*cp)) { + delay = delay * 10 + (*cp++ - '0'); + } + delay *= 10; + if (*cp == '.') { + cp++; + if (ISDIGIT(*cp)) { + delay += *cp++ - '0'; + } + while (ISDIGIT(*cp)) cp++; + } + if (*cp == '*') { + delay *= affcnt; + cp++; + } + while (*cp) + (*outc)(*cp++); + if (delay != 0 && + ospeed > 0 && + ospeed < (sizeof tens_of_ms_p_char / sizeof tens_of_ms_p_char[0])) { + delay = (delay + tens_of_ms_p_char[ospeed] - 1) / + tens_of_ms_p_char[ospeed]; + while (delay--) (*outc)(PC); + } + return(1); +} + +/* + * That's all, folks... + */ diff --git a/lib/libm2/absd.c b/lib/libm2/absd.c new file mode 100755 index 000000000..07aa4d242 --- /dev/null +++ b/lib/libm2/absd.c @@ -0,0 +1,18 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: double abs function + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ +#ifndef NOFLOAT +double +absd(i) + double i; +{ + return i >= 0 ? i : -i; +} +#endif diff --git a/lib/libm2/absf.e b/lib/libm2/absf.e new file mode 100755 index 000000000..c8a9b880a --- /dev/null +++ b/lib/libm2/absf.e @@ -0,0 +1,30 @@ +# +; +; (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. +; See the copyright notice in the ACK home directory, in the file "Copyright". +; +; +; Module: REAL abs function +; Author: Ceriel J.H. Jacobs +; Version: $Header$ +; + mes 2,_EM_WSIZE,_EM_PSIZE + exp $absf + pro $absf,0 + mes 5 + mes 9,8 + lal 0 + loi _EM_FSIZE + zrf _EM_FSIZE + cmf _EM_FSIZE + zlt *3 + lal 0 + loi _EM_FSIZE + bra *4 +3 + lal 0 + loi _EM_FSIZE + ngf _EM_FSIZE +4 + ret _EM_FSIZE + end 0 diff --git a/lib/libm2/absi.c b/lib/libm2/absi.c new file mode 100755 index 000000000..6306dbfb9 --- /dev/null +++ b/lib/libm2/absi.c @@ -0,0 +1,15 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: integer abs function + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ + +absi(i) +{ + return i >= 0 ? i : -i; +} diff --git a/lib/libm2/absl.c b/lib/libm2/absl.c new file mode 100755 index 000000000..27f5ddb0d --- /dev/null +++ b/lib/libm2/absl.c @@ -0,0 +1,16 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: longint abs function + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ +long +absl(i) + long i; +{ + return i >= 0 ? i : -i; +} diff --git a/lib/libm2/blockmove.c b/lib/libm2/blockmove.c new file mode 100755 index 000000000..18f2d3b5c --- /dev/null +++ b/lib/libm2/blockmove.c @@ -0,0 +1,23 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: block moves + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ + +#if _EM_WSIZE==_EM_PSIZE +typedef unsigned pcnt; +#else +typedef unsigned long pcnt; +#endif + +blockmove(siz, dst, src) + pcnt siz; + register char *dst, *src; +{ + while (siz--) *dst++ = *src++; +} diff --git a/lib/libm2/cap.c b/lib/libm2/cap.c new file mode 100755 index 000000000..28f03b5e3 --- /dev/null +++ b/lib/libm2/cap.c @@ -0,0 +1,18 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: cap; implementation of CAP + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ + +cap(u) + unsigned u; +{ + register unsigned *p = &u; + + if (*p >= 'a' && *p <= 'z') *p += 'A'-'a'; +} diff --git a/lib/libm2/catch.c b/lib/libm2/catch.c new file mode 100755 index 000000000..378bac1cc --- /dev/null +++ b/lib/libm2/catch.c @@ -0,0 +1,99 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: default modula-2 trap handler + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ +#include <em_abs.h> +#include <m2_traps.h> +#include <signal.h> + +static struct errm { + int errno; + char *errmes; +} errors[] = { + { EARRAY, "array bound error"}, + { ERANGE, "range bound error"}, + { ESET, "set bound error"}, + { EIOVFL, "integer overflow"}, + { EFOVFL, "real overflow"}, + { EFUNFL, "real underflow"}, + { EIDIVZ, "divide by 0"}, + { EFDIVZ, "divide by 0.0"}, + { EIUND, "undefined integer"}, + { EFUND, "undefined real"}, + { ECONV, "conversion error"}, + + { ESTACK, "stack overflow"}, + { EHEAP, "heap overflow"}, + { EILLINS, "illegal instruction"}, + { EODDZ, "illegal size argument"}, + { ECASE, "case error"}, + { EMEMFLT, "addressing non existent memory"}, + { EBADPTR, "bad pointer used"}, + { EBADPC, "program counter out of range"}, + { EBADLAE, "bad argument of lae"}, + { EBADMON, "bad monitor call"}, + { EBADLIN, "argument if LIN too high"}, + { EBADGTO, "GTO descriptor error"}, + + { M2_TOOLARGE, "stack size of process too large"}, + { M2_TOOMANY, "too many nested traps + handlers"}, + { M2_NORESULT, "no RETURN from function procedure"}, + { M2_UOVFL, "cardinal overflow"}, + { M2_FORCH, "(warning) FOR-loop control variable was changed in the body"}, + { M2_UUVFL, "cardinal underflow"}, + { M2_INTERNAL, "internal error; ask an expert for help"}, + { M2_UNIXSIG, "got a unix signal"}, + { -1, 0} +}; + +catch(trapno) + int trapno; +{ + register struct errm *ep = &errors[0]; + char *errmessage; + char buf[20]; + register char *p, *s; + + while (ep->errno != trapno && ep->errmes != 0) ep++; + if (p = ep->errmes) { + while (*p) p++; + _Traps__Message(ep->errmes, 0, (int) (p - ep->errmes), 1); + } + else { + int i = trapno; + static char q[] = "error number xxxxxxxxxxxxx"; + + p = &q[13]; + s = buf; + if (i < 0) { + i = -i; + *p++ = '-'; + } + do + *s++ = i % 10 + '0'; + while (i /= 10); + while (s > buf) *p++ = *--s; + *p = 0; + _Traps__Message(q, 0, (int) (p - q), 1); + } +#if !defined(__em24) && !defined(__em44) && !defined(__em22) + if (trapno == M2_UNIXSIG) { + extern int __signo; + signal(__signo, SIG_DFL); + _cleanup(); + kill(getpid(), __signo); + _exit(trapno); + } +#endif + if (trapno != M2_FORCH) { + _cleanup(); + _exit(trapno); + } + SIG(catch); +} diff --git a/lib/libm2/confarray.c b/lib/libm2/confarray.c new file mode 100755 index 000000000..d282a270c --- /dev/null +++ b/lib/libm2/confarray.c @@ -0,0 +1,72 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: runtime support for conformant arrays + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ +#include <m2_traps.h> + +#ifndef EM_WSIZE +#define EM_WSIZE _EM_WSIZE +#define EM_PSIZE _EM_PSIZE +#endif + +#if EM_WSIZE==EM_PSIZE +typedef unsigned pcnt; +#else +typedef unsigned long pcnt; +#endif + +struct descr { + char *addr; + int low; + unsigned int highminlow; + unsigned int size; +}; + +static struct descr *descrs[10]; +static struct descr **ppdescr = descrs; + +pcnt +new_stackptr(pdscr, a) + struct descr *pdscr; +{ + register struct descr *pdescr = pdscr; + pcnt size = (((pdescr->highminlow + 1) * pdescr->size + + (EM_WSIZE - 1)) & ~(EM_WSIZE - 1)); + + if (ppdescr >= &descrs[10]) { + /* to many nested traps + handlers ! */ + TRP(M2_TOOMANY); + } + *ppdescr++ = pdescr; + if ((char *) &a - (char *) &pdscr > 0) { + /* stack grows downwards */ + return - size; + } + return size; +} + +copy_array(pp, a) + char *pp; +{ + register char *p = pp; + register char *q; + register pcnt sz; + char dummy; + + ppdescr--; + sz = ((*ppdescr)->highminlow + 1) * (*ppdescr)->size; + + if ((char *) &a - (char *) &pp > 0) { + (*ppdescr)->addr = q = (char *) &a; + } + else (*ppdescr)->addr = q = (char *) &a - + ((sz + (EM_WSIZE - 1)) & ~ (EM_WSIZE - 1)); + + while (sz--) *q++ = *p++; +} diff --git a/lib/libm2/dvi.c b/lib/libm2/dvi.c new file mode 100755 index 000000000..d72076def --- /dev/null +++ b/lib/libm2/dvi.c @@ -0,0 +1,68 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: implementation of DIV and MOD + Author: Ceriel J.H. Jacobs + Version: $Header$ + Reason: We cannot use DVI and RMI, because DVI rounds towards 0 + and Modula-2 requires truncation +*/ + +#include <em_abs.h> + +int +dvi(j,i) + int j,i; +{ + if (j == 0) TRP(EIDIVZ); + if ((i < 0) != (j < 0)) { + if (i < 0) i = -i; + else j = -j; + return -((i+j-1)/j); + } + else return i/j; +} + +long +dvil(j,i) + long j,i; +{ + if (j == 0) TRP(EIDIVZ); + if ((i < 0) != (j < 0)) { + if (i < 0) i = -i; + else j = -j; + return -((i+j-1)/j); + } + else return i/j; +} + +int +rmi(j,i) + int j,i; +{ + if (j == 0) TRP(EIDIVZ); + if (i == 0) return 0; + if ((i < 0) != (j < 0)) { + if (i < 0) i = -i; + else j = -j; + return j*((i+j-1)/j)-i; + } + else return i%j; +} + +long +rmil(j,i) + long j,i; +{ + if (j == 0) TRP(EIDIVZ); + if (i == 0) return 0L; + if ((i < 0) != (j < 0)) { + if (i < 0) i = -i; + else j = -j; + return j*((i+j-1)/j)-i; + } + else return i%j; +} diff --git a/lib/libm2/halt.c b/lib/libm2/halt.c new file mode 100755 index 000000000..0c257d0db --- /dev/null +++ b/lib/libm2/halt.c @@ -0,0 +1,37 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: program termination routines + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ +#define MAXPROCS 32 + +static int callindex = 0; +static int (*proclist[MAXPROCS])(); + +_cleanup() +{ + while (--callindex >= 0) + (*proclist[callindex])(); + callindex = 0; +} + +CallAtEnd(p) + int (*p)(); +{ + if (callindex >= MAXPROCS) { + return 0; + } + proclist[callindex++] = p; + return 1; +} + +halt() +{ + _cleanup(); + _exit(0); +} diff --git a/lib/libm2/head_m2.e b/lib/libm2/head_m2.e new file mode 100755 index 000000000..6de740921 --- /dev/null +++ b/lib/libm2/head_m2.e @@ -0,0 +1,63 @@ +# +; +; (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. +; See the copyright notice in the ACK home directory, in the file "Copyright". +; +; +; Module: Modula-2 runtime startoff +; Author: Ceriel J.H. Jacobs +; Version: $Header$ +; + + mes 2,_EM_WSIZE,_EM_PSIZE + + exa handler + exa argv + exa argc + exa MainLB + exa bkillbss + exp $catch + exp $init + inp $trap_handler + +bkillbss + bss _EM_PSIZE,0,0 + + exp $_m_a_i_n + pro $_m_a_i_n, 0 + + lor 0 + lae MainLB + sti _EM_PSIZE + + lal _EM_WSIZE + loi _EM_PSIZE + lae argv ; save argument pointer + sti _EM_PSIZE + + lol 0 + ste argc ; save argument count + + lpi $trap_handler + sig + asp _EM_PSIZE + cal $init + cal $__M2M_ + cal $halt + loc 0 ; should not get here + ret _EM_WSIZE + end + + pro $trap_handler,0 + lpi $trap_handler + sig + lol 0 ; trap number + lae handler + loi _EM_PSIZE + lpi $catch + lae handler + sti _EM_PSIZE + cai + asp _EM_PSIZE+_EM_WSIZE + rtt + end 0 diff --git a/lib/libm2/init.c b/lib/libm2/init.c new file mode 100755 index 000000000..29a0f3d1b --- /dev/null +++ b/lib/libm2/init.c @@ -0,0 +1,47 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: initialization and some global vars + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ + +#include <signal.h> +#include <em_abs.h> +#include <m2_traps.h> + +/* map unix signals onto EM traps */ +init() +{ + sigtrp(M2_UNIXSIG, SIGHUP); + sigtrp(M2_UNIXSIG, SIGINT); + sigtrp(M2_UNIXSIG, SIGQUIT); + sigtrp(EILLINS, SIGILL); + sigtrp(M2_UNIXSIG, SIGTRAP); + sigtrp(M2_UNIXSIG, SIGIOT); + sigtrp(M2_UNIXSIG, SIGEMT); + sigtrp(M2_UNIXSIG, SIGFPE); + sigtrp(M2_UNIXSIG, SIGBUS); + sigtrp(M2_UNIXSIG, SIGSEGV); +#ifdef SIGSYS + sigtrp(EBADMON, SIGSYS); +#endif + sigtrp(M2_UNIXSIG, SIGPIPE); + sigtrp(M2_UNIXSIG, SIGALRM); + sigtrp(M2_UNIXSIG, SIGTERM); +} + +killbss() +{ + /* Fill bss with junk? Make lots of VM pages dirty? No way! */ +} + +extern int catch(); + +int (*handler)() = catch; +char **argv; +int argc; +char *MainLB; diff --git a/lib/libm2/load.c b/lib/libm2/load.c new file mode 100755 index 000000000..3ec6321b2 --- /dev/null +++ b/lib/libm2/load.c @@ -0,0 +1,45 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: get value on stack, byte by byte + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ + +#include <m2_traps.h> + +#ifndef EM_WSIZE +#define EM_WSIZE _EM_WSIZE +#define EM_PSIZE _EM_PSIZE +#endif + +#if EM_WSIZE==EM_PSIZE +typedef unsigned pcnt; +#else +typedef long pcnt; +#endif + +load(siz, addr, p) + register char *addr; + register pcnt siz; +{ + /* Make sure, that a value with a size that could have been + handled by the LOI instruction ends up at the same place, + where it would, were the LOI instruction used. + */ + register char *q = (char *) &p; + char t[4]; + + if (siz < EM_WSIZE && EM_WSIZE % siz == 0) { + /* as long as EM_WSIZE <= 4 ... */ + if (siz != 2) TRP(M2_INTERNAL); /* internal error */ + q = &t[0]; + } + while (siz--) *q++ = *addr++; + if (q - t == 2) { + *((unsigned *)(&p)) = *((unsigned short *) (&t[0])); + } +} diff --git a/lib/libm2/par_misc.e b/lib/libm2/par_misc.e new file mode 100755 index 000000000..b0e86969e --- /dev/null +++ b/lib/libm2/par_misc.e @@ -0,0 +1,175 @@ +# +; +; (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. +; See the copyright notice in the ACK home directory, in the file "Copyright". +; + +; +; Module: coroutine primitives +; Author: Kees Bot, Edwin Scheffer, Ceriel Jacobs +; Version: $Header$ +; + + mes 2,_EM_WSIZE,_EM_PSIZE + + ; topsize takes care of two things: + ; - given a stack-break, + ; it computes the size of the chunk of memory needed to save the stack; + ; - also, if this stack-break = 0, it creates one, assuming that caller is + ; the stack-break. + ; + ; This implementation assumes a continuous stack growing downwards + + exp $topsize +#ifdef __sparc + inp $topsize2 + pro $topsize, 0 + mes 11 + zer _EM_PSIZE + lal 0 + loi _EM_PSIZE + cal $topsize2 + asp 2*_EM_PSIZE + lfr _EM_WSIZE + ret _EM_WSIZE + end 0 + pro $topsize2, (3*_EM_WSIZE+3*_EM_PSIZE) +#else + pro $topsize, (3*_EM_WSIZE+3*_EM_PSIZE) +#endif + ; local space for line-number, ignoremask, filename, stack-break, size, + ; and stack-pointer (see the topsave routine) + mes 11 + lal 0 + loi _EM_PSIZE + loi _EM_PSIZE ; stack-break or 0 + zer _EM_PSIZE + cmp + zne *1 + lxl 0 + dch ; local base of caller +#ifdef __sparc + dch ; because of the extra layer +#endif + lal 0 + loi _EM_PSIZE + sti _EM_PSIZE +1 + lal 0 + loi _EM_PSIZE + loi _EM_PSIZE + lpb ; convert this local base to an argument base. + ; An implementation of a sort of "topsize" EM + ; instruction should take a local base, and save + ; the whole frame. + + lor 1 ; stack-break SP + sbs _EM_WSIZE ; stack-break-SP + ret _EM_WSIZE ; return size of block to be saved + end 3*_EM_WSIZE+3*_EM_PSIZE + + exp $topsave +#ifdef __sparc + inp $topsave2 + pro $topsave,0 + mes 11 + lal 0 + loi 2*_EM_PSIZE + cal $topsave2 + asp 2*_EM_PSIZE + lfr _EM_WSIZE + ret _EM_WSIZE + end 0 + pro $topsave2,0 +#else + pro $topsave, 0 +#endif + mes 11 + loe 0 + lae 4 ; load line number and file name + loi _EM_PSIZE + lim ; ignore mask + lor 0 ; LB + lal 0 + loi _EM_PSIZE ; stack-break + lpb + lor 1 + sbs _EM_WSIZE + loc _EM_WSIZE + adu _EM_WSIZE ; gives size + dup _EM_WSIZE + stl 0 ; save size + lor 1 ; SP (the SP BEFORE pushing) + lor 1 ; SP (address of stack top to save) + lal _EM_PSIZE ; area + loi _EM_PSIZE + lol 0 ; size + bls _EM_WSIZE ; move whole block + asp 3*_EM_PSIZE+3*_EM_WSIZE ; remove the lot from the stack + loc 1 + ret _EM_WSIZE ; return 1 + end 0 + +sv + bss _EM_PSIZE,0,0 + + exp $topload +#ifdef __sparc + inp $topload1 + pro $topload,0 + lal 0 + loi _EM_PSIZE + cal $topload1 + asp _EM_PSIZE + lfr _EM_WSIZE + ret _EM_WSIZE + end 0 + pro $topload1, 0 +#else + pro $topload, 0 +#endif + mes 11 + + lal 0 + loi _EM_PSIZE + lae sv + sti _EM_PSIZE ; saved parameter + + lxl 0 +2 + dup _EM_PSIZE + adp -3*_EM_PSIZE + lal 0 + loi _EM_PSIZE ; compare target SP with current LB to see if we must + loi _EM_PSIZE + cmp ; find another LB first + zgt *1 + dch ; just follow dynamic chain to make sure we find + ; a legal one + bra *2 +1 + str 0 + + lae sv + loi _EM_PSIZE + loi _EM_PSIZE ; load indirect to + str 1 ; restore SP + asp 0-_EM_PSIZE ; to stop int from complaining about non-existent memory + lae sv + loi _EM_PSIZE ; source address + lor 1 + adp _EM_PSIZE ; destination address + lae sv + loi _EM_PSIZE + adp _EM_PSIZE + loi _EM_WSIZE ; size of block + bls _EM_WSIZE + asp _EM_PSIZE+_EM_WSIZE ; drop size + SP + str 0 ; restore local base + sim ; ignore mask + lae 4 + sti _EM_PSIZE + ste 0 ; line and file + loc 0 + ret _EM_WSIZE + end 0 diff --git a/lib/libm2/random.mod b/lib/libm2/random.mod new file mode 100755 index 000000000..826e99f69 --- /dev/null +++ b/lib/libm2/random.mod @@ -0,0 +1,58 @@ +(* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*) + +(*$R-*) +IMPLEMENTATION MODULE random; +(* + Module: random numbers + Author: Ceriel J.H. Jacobs + Version: $Header$ +*) + + FROM Unix IMPORT getpid, time; + TYPE index = [1..55]; + + VAR X: ARRAY index OF CARDINAL; + j, k: index; + tm: LONGINT; + + PROCEDURE Random(): CARDINAL; + BEGIN + IF k-1 <= 0 THEN k := 55; ELSE DEC(k) END; + IF j-1 <= 0 THEN j := 55; ELSE DEC(j) END; + X[k] := X[k] + X[j]; + RETURN X[k] + END Random; + + PROCEDURE Uniform (lwb, upb: CARDINAL): CARDINAL; + BEGIN + IF upb <= lwb THEN RETURN lwb; END; + RETURN lwb + (Random() MOD (upb - lwb + 1)); + END Uniform; + + PROCEDURE StartSeed(seed: CARDINAL); + VAR v: CARDINAL; + BEGIN + FOR k := 1 TO 55 DO + seed := 1297 * seed + 123; + X[k] := seed; + END; + FOR k := 1 TO 15 DO + j := tm MOD 55D + 1D; + v := X[j]; + tm := tm DIV 7D; + j := tm MOD 55D + 1D; + X[j] := v; + tm := tm * 3D; + END; + k := 1; + j := 25; + END StartSeed; + +BEGIN + tm := time(NIL); + X[1] := tm; + StartSeed(CARDINAL(getpid()) * X[1]); +END random. diff --git a/lib/libm2/rcka.c b/lib/libm2/rcka.c new file mode 100755 index 000000000..6178bd6fb --- /dev/null +++ b/lib/libm2/rcka.c @@ -0,0 +1,25 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * + * Module: range checks for INTEGER, now for array indexing + * Author: Ceriel J.H. Jacobs + * Version: $Header$ +*/ + +#include <em_abs.h> + +extern TRP(); + +struct array_descr { + int lbound; + int n_elts_min_one; + unsigned size; +}; + +rcka(descr, indx) + struct array_descr *descr; +{ + if (indx < 0 || indx > descr->n_elts_min_one) TRP(EARRAY); +} diff --git a/lib/libm2/rcki.c b/lib/libm2/rcki.c new file mode 100755 index 000000000..289646170 --- /dev/null +++ b/lib/libm2/rcki.c @@ -0,0 +1,23 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * + * Module: range checks for INTEGER + * Author: Ceriel J.H. Jacobs + * Version: $Header$ +*/ + +#include <em_abs.h> + +extern TRP(); + +struct range_descr { + int low, high; +}; + +rcki(descr, val) + struct range_descr *descr; +{ + if (val < descr->low || val > descr->high) TRP(ERANGE); +} diff --git a/lib/libm2/rckil.c b/lib/libm2/rckil.c new file mode 100755 index 000000000..2c4328d4c --- /dev/null +++ b/lib/libm2/rckil.c @@ -0,0 +1,24 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * + * Module: range checks for LONGINT + * Author: Ceriel J.H. Jacobs + * Version: $Header$ +*/ + +#include <em_abs.h> + +extern TRP(); + +struct range_descr { + long low, high; +}; + +rckil(descr, val) + struct range_descr *descr; + long val; +{ + if (val < descr->low || val > descr->high) TRP(ERANGE); +} diff --git a/lib/libm2/rcku.c b/lib/libm2/rcku.c new file mode 100755 index 000000000..8dfb3204f --- /dev/null +++ b/lib/libm2/rcku.c @@ -0,0 +1,24 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * + * Module: range checks for CARDINAL + * Author: Ceriel J.H. Jacobs + * Version: $Header$ +*/ + +#include <em_abs.h> + +extern TRP(); + +struct range_descr { + unsigned low, high; +}; + +rcku(descr, val) + struct range_descr *descr; + unsigned val; +{ + if (val < descr->low || val > descr->high) TRP(ERANGE); +} diff --git a/lib/libm2/rckul.c b/lib/libm2/rckul.c new file mode 100755 index 000000000..86647502c --- /dev/null +++ b/lib/libm2/rckul.c @@ -0,0 +1,24 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * + * Module: range checks for LONGCARD + * Author: Ceriel J.H. Jacobs + * Version: $Header$ +*/ + +#include <em_abs.h> + +extern TRP(); + +struct range_descr { + unsigned long low, high; +}; + +rckul(descr, val) + struct range_descr *descr; + unsigned long val; +{ + if (val < descr->low || val > descr->high) TRP(ERANGE); +} diff --git a/lib/libm2/sigtrp.c b/lib/libm2/sigtrp.c new file mode 100755 index 000000000..6dc777d69 --- /dev/null +++ b/lib/libm2/sigtrp.c @@ -0,0 +1,82 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: Mapping of Unix signals to EM traps + (only when not using the MON instruction) + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ + +#if !defined(__em22) && !defined(__em24) && !defined(__em44) + +#define EM_trap(n) TRP(n) /* define to whatever is needed to cause the trap */ + +#include <signal.h> +#include <errno.h> + +int __signo; + +static int __traps[] = { + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, + -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, -2, +}; + +static void +__ctchsig(signo) +{ + signal(signo,__ctchsig); +#ifdef __BSD4_2 + sigsetmask(sigblock(0) & ~(1<<(signo - 1))); +#endif + __signo = signo; + EM_trap(__traps[signo]); +} + +int +sigtrp(trapno, signo) +{ + /* Let Unix signal signo cause EM trap trapno to occur. + If trapno = -2, restore default, + If trapno = -3, ignore. + Return old trapnumber. + Careful, this could be -2 or -3; But return value of -1 + indicates failure, with error number in errno. + */ + extern int errno; + void (*ctch)() = __ctchsig; + void (*oldctch)(); + int oldtrap; + + if (signo <= 0 || signo >= sizeof(__traps)/sizeof(__traps[0])) { + errno = EINVAL; + return -1; + } + + if (trapno == -3) + ctch = SIG_IGN; + else if (trapno == -2) + ctch = SIG_DFL; + else if (trapno >= 0 && trapno <= 252) + ; + else { + errno = EINVAL; + return -1; + } + + oldtrap = __traps[signo]; + + if ((oldctch = signal(signo, ctch)) == (void (*)())-1) /* errno set by signal */ + return -1; + + else if (oldctch == SIG_IGN) { + signal(signo, SIG_IGN); + } + else __traps[signo] = trapno; + + return oldtrap; +} +#endif diff --git a/lib/libm2/stackprio.c b/lib/libm2/stackprio.c new file mode 100755 index 000000000..ecd3fee2a --- /dev/null +++ b/lib/libm2/stackprio.c @@ -0,0 +1,27 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: Dummy priority routines + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ + +static unsigned prio = 0; + +stackprio(n) + unsigned n; +{ + unsigned old = prio; + + if (n > prio) prio = n; + return old; +} + +unstackprio(n) + unsigned n; +{ + prio = n; +} diff --git a/lib/libm2/store.c b/lib/libm2/store.c new file mode 100755 index 000000000..d69aa4023 --- /dev/null +++ b/lib/libm2/store.c @@ -0,0 +1,43 @@ +/* + (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + See the copyright notice in the ACK home directory, in the file "Copyright". +*/ + +/* + Module: store values from stack, byte by byte + Author: Ceriel J.H. Jacobs + Version: $Header$ +*/ + +#include <m2_traps.h> + +#ifndef EM_WSIZE +#define EM_WSIZE _EM_WSIZE +#define EM_PSIZE _EM_PSIZE +#endif + +#if EM_WSIZE==EM_PSIZE +typedef unsigned pcnt; +#else +typedef long pcnt; +#endif + +store(siz, addr, p) + register char *addr; + register pcnt siz; +{ + /* Make sure, that a value with a size that could have been + handled by the LOI instruction is handled as if it was + loaded with the LOI instruction. + */ + register char *q = (char *) &p; + char t[4]; + + if (siz < EM_WSIZE && EM_WSIZE % siz == 0) { + /* as long as EM_WSIZE <= 4 ... */ + if (siz != 2) TRP(M2_INTERNAL); /* internal error */ + *((unsigned short *) (&t[0])) = *((unsigned *) q); + q = &t[0]; + } + while (siz--) *addr++ = *q++; +} diff --git a/lib/libm2/ucheck.c b/lib/libm2/ucheck.c new file mode 100755 index 000000000..411a7ce33 --- /dev/null +++ b/lib/libm2/ucheck.c @@ -0,0 +1,65 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * + * Module: CARDINAL operations with overflow checking + * Author: Ceriel J.H. Jacobs + * Version: $Header$ +*/ + +#ifndef EM_WSIZE +#define EM_WSIZE _EM_WSIZE +#endif +#ifndef EM_LSIZE +#define EM_LSIZE _EM_LSIZE +#endif + +#include <m2_traps.h> + +#define MAXCARD ((unsigned)-1) +#if EM_WSIZE < EM_LSIZE +#define MAXLONGCARD ((unsigned long) -1L) +#endif + +adduchk(a,b) + unsigned a,b; +{ + if (MAXCARD - a < b) TRP(M2_UOVFL); +} + +#if EM_WSIZE < EM_LSIZE +addulchk(a,b) + unsigned long a,b; +{ + if (MAXLONGCARD - a < b) TRP(M2_UOVFL); +} +#endif + +muluchk(a,b) + unsigned a,b; +{ + if (a != 0 && MAXCARD/a < b) TRP(M2_UOVFL); +} + +#if EM_WSIZE < EM_LSIZE +mululchk(a,b) + unsigned long a,b; +{ + if (a != 0 && MAXLONGCARD/a < b) TRP(M2_UOVFL); +} +#endif + +subuchk(a,b) + unsigned a,b; +{ + if (b < a) TRP(M2_UUVFL); +} + +#if EM_WSIZE < EM_LSIZE +subulchk(a,b) + unsigned long a,b; +{ + if (b < a) TRP(M2_UUVFL); +} +#endif diff --git a/lib/libp/Makefile b/lib/libp/Makefile new file mode 100755 index 000000000..17e143071 --- /dev/null +++ b/lib/libp/Makefile @@ -0,0 +1,305 @@ +# Makefile for lib/libp. + +CFLAGS = -O -I../h -wo +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../libp.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(abi.o) \ + $(LIBRARY)(abl.o) \ + $(LIBRARY)(abr.o) \ + $(LIBRARY)(arg.o) \ + $(LIBRARY)(ass.o) \ + $(LIBRARY)(asz.o) \ + $(LIBRARY)(atn.o) \ + $(LIBRARY)(bcp.o) \ + $(LIBRARY)(bts.o) \ + $(LIBRARY)(buff.o) \ + $(LIBRARY)(catch.o) \ + $(LIBRARY)(clock.o) \ + $(LIBRARY)(cls.o) \ + $(LIBRARY)(cvt.o) \ + $(LIBRARY)(diag.o) \ + $(LIBRARY)(dis.o) \ + $(LIBRARY)(efl.o) \ + $(LIBRARY)(eln.o) \ + $(LIBRARY)(encaps.o) \ + $(LIBRARY)(exp.o) \ + $(LIBRARY)(fef.o) \ + $(LIBRARY)(fif.o) \ + $(LIBRARY)(get.o) \ + $(LIBRARY)(gto.o) \ + $(LIBRARY)(head_pc.o) \ + $(LIBRARY)(hlt.o) \ + $(LIBRARY)(hol0.o) \ + $(LIBRARY)(incpt.o) \ + $(LIBRARY)(ini.o) \ + $(LIBRARY)(log.o) \ + $(LIBRARY)(mdi.o) \ + $(LIBRARY)(mdl.o) \ + $(LIBRARY)(new.o) \ + $(LIBRARY)(nfa.o) \ + $(LIBRARY)(nobuff.o) \ + $(LIBRARY)(notext.o) \ + $(LIBRARY)(opn.o) \ + $(LIBRARY)(outcpt.o) \ + $(LIBRARY)(pac.o) \ + $(LIBRARY)(pclose.o) \ + $(LIBRARY)(pcreat.o) \ + $(LIBRARY)(pentry.o) \ + $(LIBRARY)(perrno.o) \ + $(LIBRARY)(pexit.o) \ + $(LIBRARY)(popen.o) \ + $(LIBRARY)(put.o) \ + $(LIBRARY)(rcka.o) \ + $(LIBRARY)(rdc.o) \ + $(LIBRARY)(rdi.o) \ + $(LIBRARY)(rdl.o) \ + $(LIBRARY)(rdr.o) \ + $(LIBRARY)(rf.o) \ + $(LIBRARY)(rln.o) \ + $(LIBRARY)(rnd.o) \ + $(LIBRARY)(sav.o) \ + $(LIBRARY)(sig.o) \ + $(LIBRARY)(sin.o) \ + $(LIBRARY)(sqt.o) \ + $(LIBRARY)(string.o) \ + $(LIBRARY)(trap.o) \ + $(LIBRARY)(trp.o) \ + $(LIBRARY)(unp.o) \ + $(LIBRARY)(uread.o) \ + $(LIBRARY)(uwrite.o) \ + $(LIBRARY)(wdw.o) \ + $(LIBRARY)(wf.o) \ + $(LIBRARY)(wrc.o) \ + $(LIBRARY)(wrf.o) \ + $(LIBRARY)(wri.o) \ + $(LIBRARY)(wrl.o) \ + $(LIBRARY)(wrr.o) \ + $(LIBRARY)(wrs.o) \ + $(LIBRARY)(wrz.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(abi.o): abi.c + $(CC1) abi.c + +$(LIBRARY)(abl.o): abl.c + $(CC1) abl.c + +$(LIBRARY)(abr.o): abr.c + $(CC1) abr.c + +$(LIBRARY)(arg.o): arg.c + $(CC1) arg.c + +$(LIBRARY)(ass.o): ass.c + $(CC1) ass.c + +$(LIBRARY)(asz.o): asz.c + $(CC1) asz.c + +$(LIBRARY)(atn.o): atn.c + $(CC1) atn.c + +$(LIBRARY)(bcp.o): bcp.c + $(CC1) bcp.c + +$(LIBRARY)(bts.o): bts.e + $(CC1) bts.e + +$(LIBRARY)(buff.o): buff.c + $(CC1) buff.c + +$(LIBRARY)(catch.o): catch.c + $(CC1) catch.c + +$(LIBRARY)(clock.o): clock.c + $(CC1) clock.c + +$(LIBRARY)(cls.o): cls.c + $(CC1) cls.c + +$(LIBRARY)(cvt.o): cvt.c + $(CC1) cvt.c + +$(LIBRARY)(diag.o): diag.c + $(CC1) diag.c + +$(LIBRARY)(dis.o): dis.c + $(CC1) dis.c + +$(LIBRARY)(efl.o): efl.c + $(CC1) efl.c + +$(LIBRARY)(eln.o): eln.c + $(CC1) eln.c + +$(LIBRARY)(encaps.o): encaps.e + $(CC1) encaps.e + +$(LIBRARY)(exp.o): exp.c + $(CC1) exp.c + +$(LIBRARY)(fef.o): fef.e + $(CC1) fef.e + +$(LIBRARY)(fif.o): fif.e + $(CC1) fif.e + +$(LIBRARY)(get.o): get.c + $(CC1) get.c + +$(LIBRARY)(gto.o): gto.e + $(CC1) gto.e + +$(LIBRARY)(head_pc.o): head_pc.e + $(CC1) head_pc.e + +$(LIBRARY)(hlt.o): hlt.c + $(CC1) hlt.c + +$(LIBRARY)(hol0.o): hol0.e + $(CC1) hol0.e + +$(LIBRARY)(incpt.o): incpt.c + $(CC1) incpt.c + +$(LIBRARY)(ini.o): ini.c + $(CC1) ini.c + +$(LIBRARY)(log.o): log.c + $(CC1) log.c + +$(LIBRARY)(mdi.o): mdi.c + $(CC1) mdi.c + +$(LIBRARY)(mdl.o): mdl.c + $(CC1) mdl.c + +$(LIBRARY)(new.o): new.c + $(CC1) new.c + +$(LIBRARY)(nfa.o): nfa.c + $(CC1) nfa.c + +$(LIBRARY)(nobuff.o): nobuff.c + $(CC1) nobuff.c + +$(LIBRARY)(notext.o): notext.c + $(CC1) notext.c + +$(LIBRARY)(opn.o): opn.c + $(CC1) opn.c + +$(LIBRARY)(outcpt.o): outcpt.c + $(CC1) outcpt.c + +$(LIBRARY)(pac.o): pac.c + $(CC1) pac.c + +$(LIBRARY)(pclose.o): pclose.c + $(CC1) pclose.c + +$(LIBRARY)(pcreat.o): pcreat.c + $(CC1) pcreat.c + +$(LIBRARY)(pentry.o): pentry.c + $(CC1) pentry.c + +$(LIBRARY)(perrno.o): perrno.c + $(CC1) perrno.c + +$(LIBRARY)(pexit.o): pexit.c + $(CC1) pexit.c + +$(LIBRARY)(popen.o): popen.c + $(CC1) popen.c + +$(LIBRARY)(put.o): put.c + $(CC1) put.c + +$(LIBRARY)(rcka.o): rcka.c + $(CC1) rcka.c + +$(LIBRARY)(rdc.o): rdc.c + $(CC1) rdc.c + +$(LIBRARY)(rdi.o): rdi.c + $(CC1) rdi.c + +$(LIBRARY)(rdl.o): rdl.c + $(CC1) rdl.c + +$(LIBRARY)(rdr.o): rdr.c + $(CC1) rdr.c + +$(LIBRARY)(rf.o): rf.c + $(CC1) rf.c + +$(LIBRARY)(rln.o): rln.c + $(CC1) rln.c + +$(LIBRARY)(rnd.o): rnd.c + $(CC1) rnd.c + +$(LIBRARY)(sav.o): sav.e + $(CC1) sav.e + +$(LIBRARY)(sig.o): sig.e + $(CC1) sig.e + +$(LIBRARY)(sin.o): sin.c + $(CC1) sin.c + +$(LIBRARY)(sqt.o): sqt.c + $(CC1) sqt.c + +$(LIBRARY)(string.o): string.c + $(CC1) string.c + +$(LIBRARY)(trap.o): trap.e + $(CC1) trap.e + +$(LIBRARY)(trp.o): trp.e + $(CC1) trp.e + +$(LIBRARY)(unp.o): unp.c + $(CC1) unp.c + +$(LIBRARY)(uread.o): uread.c + $(CC1) uread.c + +$(LIBRARY)(uwrite.o): uwrite.c + $(CC1) uwrite.c + +$(LIBRARY)(wdw.o): wdw.c + $(CC1) wdw.c + +$(LIBRARY)(wf.o): wf.c + $(CC1) wf.c + +$(LIBRARY)(wrc.o): wrc.c + $(CC1) wrc.c + +$(LIBRARY)(wrf.o): wrf.c + $(CC1) wrf.c + +$(LIBRARY)(wri.o): wri.c + $(CC1) wri.c + +$(LIBRARY)(wrl.o): wrl.c + $(CC1) wrl.c + +$(LIBRARY)(wrr.o): wrr.c + $(CC1) wrr.c + +$(LIBRARY)(wrs.o): wrs.c + $(CC1) wrs.c + +$(LIBRARY)(wrz.o): wrz.c + $(CC1) wrz.c diff --git a/lib/libp/abi.c b/lib/libp/abi.c new file mode 100755 index 000000000..abfe4e9be --- /dev/null +++ b/lib/libp/abi.c @@ -0,0 +1,23 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +int _abi(i) int i; { + return(i>=0 ? i : -i); +} diff --git a/lib/libp/abl.c b/lib/libp/abl.c new file mode 100755 index 000000000..9ffbfbe96 --- /dev/null +++ b/lib/libp/abl.c @@ -0,0 +1,23 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +long _abl(i) long i; { + return(i>=0 ? i : -i); +} diff --git a/lib/libp/abr.c b/lib/libp/abr.c new file mode 100755 index 000000000..9a8c0bdfd --- /dev/null +++ b/lib/libp/abr.c @@ -0,0 +1,23 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +double _abr(r) double r; { + return(r>=0 ? r : -r); +} diff --git a/lib/libp/arg.c b/lib/libp/arg.c new file mode 100755 index 000000000..fdbf14c3f --- /dev/null +++ b/lib/libp/arg.c @@ -0,0 +1,56 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +/* function argc:integer; extern; */ +/* function argv(i:integer):string; extern; */ +/* procedure argshift; extern; */ +/* function environ(i:integer):string; extern; */ + +extern int _pargc; +extern char **_pargv; +extern char ***_penviron; + +int argc() { + return(_pargc); +} + +char *argv(i) { + if (i >= _pargc) + return(0); + return(_pargv[i]); +} + +argshift() { + + if (_pargc > 1) { + --_pargc; + _pargv++; + } +} + +char *environ(i) { + char **p; char *q; + + if (p = *_penviron) + while (q = *p++) + if (i-- < 0) + return(q); + return(0); +} diff --git a/lib/libp/ass.c b/lib/libp/ass.c new file mode 100755 index 000000000..8522d9426 --- /dev/null +++ b/lib/libp/ass.c @@ -0,0 +1,33 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <em_abs.h> +#include <pc_err.h> + +extern char *_hol0(); +extern _trp(); + +_ass(line,bool) int line,bool; { + + if (bool==0) { + LINO = line; + _trp(EASS); + } +} diff --git a/lib/libp/asz.c b/lib/libp/asz.c new file mode 100755 index 000000000..271b882da --- /dev/null +++ b/lib/libp/asz.c @@ -0,0 +1,29 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +struct descr { + int low; + int diff; + int size; +}; + +int _asz(dp) struct descr *dp; { + return(dp->size * (dp->diff + 1)); +} diff --git a/lib/libp/atn.c b/lib/libp/atn.c new file mode 100755 index 000000000..320c18923 --- /dev/null +++ b/lib/libp/atn.c @@ -0,0 +1,72 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ + +/* $Header$ */ + +#define __NO_DEFS +#include <math.h> + +#if __STDC__ +#include <pc_math.h> +#endif + +double +_atn(x) + double x; +{ + /* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + */ + + static double p[] = { + -0.13688768894191926929e+2, + -0.20505855195861651981e+2, + -0.84946240351320683534e+1, + -0.83758299368150059274e+0 + }; + static double q[] = { + 0.41066306682575781263e+2, + 0.86157349597130242515e+2, + 0.59578436142597344465e+2, + 0.15024001160028576121e+2, + 1.0 + }; + static double a[] = { + 0.0, + 0.52359877559829887307710723554658381, /* pi/6 */ + M_PI_2, + 1.04719755119659774615421446109316763 /* pi/3 */ + }; + + int neg = x < 0; + int n; + double g; + + if (neg) { + x = -x; + } + if (x > 1.0) { + x = 1.0/x; + n = 2; + } + else n = 0; + + if (x > 0.26794919243112270647) { /* 2-sqtr(3) */ + n = n + 1; + x = (((0.73205080756887729353*x-0.5)-0.5)+x)/ + (1.73205080756887729353+x); + } + + /* ??? avoid underflow ??? */ + + g = x * x; + x += x * g * POLYNOM3(g, p) / POLYNOM4(g, q); + if (n > 1) x = -x; + x += a[n]; + return neg ? -x : x; +} diff --git a/lib/libp/bcp.c b/lib/libp/bcp.c new file mode 100755 index 000000000..ef8edf605 --- /dev/null +++ b/lib/libp/bcp.c @@ -0,0 +1,30 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +int _bcp(sz,y,x) int sz; unsigned char *y,*x; { + + while (--sz >= 0) { + if (*x < *y) + return(-1); + if (*x++ > *y++) + return(1); + } + return(0); +} diff --git a/lib/libp/bts.e b/lib/libp/bts.e new file mode 100755 index 000000000..adb57f360 --- /dev/null +++ b/lib/libp/bts.e @@ -0,0 +1,56 @@ +# +; $Header$ +; +; (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. +; +; This product is part of the Amsterdam Compiler Kit. +; +; Permission to use, sell, duplicate or disclose this software must be +; obtained in writing. Requests for such permissions may be sent to +; +; Dr. Andrew S. Tanenbaum +; Wiskundig Seminarium +; Vrije Universiteit +; Postbox 7161 +; 1007 MC Amsterdam +; The Netherlands +; +; + +; Author: J.W. Stevenson */ + + mes 2,_EM_WSIZE,_EM_PSIZE + +#define SIZE 0 +#define HIGH _EM_WSIZE +#define LOWB 2*_EM_WSIZE +#define BASE 3*_EM_WSIZE + +; _bts is called with four parameters: +; - the initial set (BASE) +; - low bound of range of bits (LOWB) +; - high bound of range of bits (HIGH) +; - set size in bytes (SIZE) + + exp $_bts + pro $_bts,0 + lal BASE ; address of initial set + lol SIZE + los _EM_WSIZE ; load initial set +1 + lol LOWB ; low bound + lol HIGH ; high bound + bgt *2 ; while low <= high + lol LOWB + lol SIZE + set ? ; create [low] + lol SIZE + ior ? ; merge with initial set + inl LOWB ; increment low bound + bra *1 ; loop back +2 + lal BASE + lol SIZE + sts _EM_WSIZE ; store result over initial set + ret 0 + end ? diff --git a/lib/libp/buff.c b/lib/libp/buff.c new file mode 100755 index 000000000..471025c48 --- /dev/null +++ b/lib/libp/buff.c @@ -0,0 +1,35 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> + +extern _flush(); + +/* procedure buff(var f:file of ?); */ + +buff(f) struct file *f; { + int sz; + + if ((f->flags & (0377|WRBIT)) != (MAGIC|WRBIT)) + return; + _flush(f); + sz = f->size; + f->count = f->buflen = (sz>PC_BUFLEN ? sz : PC_BUFLEN-PC_BUFLEN%sz); +} diff --git a/lib/libp/catch.c b/lib/libp/catch.c new file mode 100755 index 000000000..204a8a1cf --- /dev/null +++ b/lib/libp/catch.c @@ -0,0 +1,154 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <em_abs.h> +#include <pc_err.h> +#include <pc_file.h> + +/* to make it easier to patch ... */ +extern struct file *_curfil; + +static struct errm { + int errno; + char *errmes; +} errors[] = { + { EARRAY, "array bound error"}, + { ERANGE, "range bound error"}, + { ESET, "set bound error"}, + { EIOVFL, "integer overflow"}, + { EFOVFL, "real overflow"}, + { EFUNFL, "real underflow"}, + { EIDIVZ, "divide by 0"}, + { EFDIVZ, "divide by 0.0"}, + { EIUND, "undefined integer"}, + { EFUND, "undefined real"}, + { ECONV, "conversion error"}, + + { ESTACK, "stack overflow"}, + { EHEAP, "heap overflow"}, + { EILLINS, "illegal instruction"}, + { EODDZ, "illegal size argument"}, + { ECASE, "case error"}, + { EMEMFLT, "addressing non existent memory"}, + { EBADPTR, "bad pointer used"}, + { EBADPC, "program counter out of range"}, + { EBADLAE, "bad argument of lae"}, + { EBADMON, "bad monitor call"}, + { EBADLIN, "argument if LIN too high"}, + { EBADGTO, "GTO descriptor error"}, + + { EARGC, "more args expected" }, + { EEXP, "error in exp" }, + { ELOG, "error in ln" }, + { ESQT, "error in sqrt" }, + { EASS, "assertion failed" }, + { EPACK, "array bound error in pack" }, + { EUNPACK, "array bound error in unpack" }, + { EMOD, "only positive j in 'i mod j'" }, + { EBADF, "file not yet open" }, + { EFREE, "dispose error" }, + { EFUNASS, "function not assigned" }, + { EWIDTH, "illegal field width" }, + + { EWRITEF, "not writable" }, + { EREADF, "not readable" }, + { EEOF, "end of file" }, + { EFTRUNC, "truncated" }, + { ERESET, "reset error" }, + { EREWR, "rewrite error" }, + { ECLOSE, "close error" }, + { EREAD, "read error" }, + { EWRITE, "write error" }, + { EDIGIT, "digit expected" }, + { EASCII, "non-ASCII char read" }, + { -1, 0} +}; + +extern int _pargc; +extern char **_pargv; +extern char ***_penviron; + +extern char *_hol0(); +extern _trp(); +extern _exit(); +extern int _write(); + +_catch(erno) unsigned erno; { + register struct errm *ep = &errors[0]; + char *p,*q,*s,**qq; + char buf[20]; + unsigned i; + int j = erno; + char *pp[11]; + char xbuf[100]; + + qq = pp; + if (p = FILN) + *qq++ = p; + else + *qq++ = _pargv[0]; + + while (ep->errno != erno && ep->errmes != 0) ep++; + p = buf; + s = xbuf; + if (i = LINO) { + *qq++ = ", "; + do + *p++ = i % 10 + '0'; + while (i /= 10); + while (p > buf) *s++ = *--p; + } + *s++ = ':'; + *s++ = ' '; + *s++ = '\0'; + *qq++ = xbuf; + if ((erno & ~037) == 0140 && (_curfil->flags&0377)==MAGIC) { + /* file error */ + *qq++ = "file "; + *qq++ = _curfil->fname; + *qq++ = ": "; + } + if (ep->errmes) *qq++ = ep->errmes; + else { + *qq++ = "error number "; + *qq++ = s; + p = buf; + if (j < 0) { + j = -j; + *s++ = '-'; + } + do + *p++ = j % 10 + '0'; + while (j /= 10); + while (p > buf) *s++ = *--p; + *s = 0; + } + *qq++ = "\n"; + *qq = 0; + qq = pp; + while (q = *qq++) { + p = q; + while (*p) + p++; + if (_write(2,q,(int)(p-q)) < 0) + ; + } + _exit(erno); +error: + _trp(erno); +} diff --git a/lib/libp/clock.c b/lib/libp/clock.c new file mode 100755 index 000000000..f5847696a --- /dev/null +++ b/lib/libp/clock.c @@ -0,0 +1,47 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +/* function clock:integer; extern; */ + +extern int _times(); + +struct tbuf { + long utime; + long stime; + long cutime; + long cstime; +}; + +#ifndef EM_WSIZE +#define EM_WSIZE _EM_WSIZE +#endif + +int clock() { + struct tbuf t; + + _times(&t); + return( (int)(t.utime + t.stime) & +#if EM_WSIZE <= 2 + 077777 +#else + 0x7fffffffL +#endif + ); +} diff --git a/lib/libp/cls.c b/lib/libp/cls.c new file mode 100755 index 000000000..f891a3658 --- /dev/null +++ b/lib/libp/cls.c @@ -0,0 +1,67 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> +#include <pc_err.h> + +extern struct file *_curfil; +extern _trp(); +extern _flush(); +extern _outcpt(); +extern int _close(); + +_xcls(f) struct file *f; { + + if ((f->flags & WRBIT) == 0) + return; + if ((f->flags & (TXTBIT|ELNBIT)) == TXTBIT) { +#ifdef CPM + *f->ptr = '\r'; + _outcpt(f); +#endif + *f->ptr = '\n'; + _outcpt(f); + } + _flush(f); +} + +_cls(f) struct file *f; { +#ifdef MAYBE + char *p; +#endif + + _curfil = f; + if ((f->flags&0377) != MAGIC) + return; +#ifdef MAYBE + p = f->bufadr; + if (f->ptr < p) + return; + if (f->buflen <= 0) + return; + p += f->buflen; + if (f->ptr >= p) + return; +#endif + _xcls(f); + if (_close(f->ufd) != 0) + _trp(ECLOSE); + f->flags = 0; +} diff --git a/lib/libp/cvt.c b/lib/libp/cvt.c new file mode 100755 index 000000000..d3c2116dc --- /dev/null +++ b/lib/libp/cvt.c @@ -0,0 +1,119 @@ +/* $Header$ */ +#ifndef NOFLOAT + +#if __STDC__ +#include <float.h> +#else +#include <math.h> +#define DBL_MAX M_MAX_D +#endif + +static char *cvt(); +#define NDIGITS 128 + +char * +_ecvt(value, ndigit, decpt, sign) + double value; + int ndigit, *decpt, *sign; +{ + return cvt(value, ndigit, decpt, sign, 1); +} + +char * +_fcvt(value, ndigit, decpt, sign) + double value; + int ndigit, *decpt, *sign; +{ + return cvt(value, ndigit, decpt, sign, 0); +} + +static struct powers_of_10 { + double pval; + double rpval; + int exp; +} p10[] = { + 1.0e32, 1.0e-32, 32, + 1.0e16, 1.0e-16, 16, + 1.0e8, 1.0e-8, 8, + 1.0e4, 1.0e-4, 4, + 1.0e2, 1.0e-2, 2, + 1.0e1, 1.0e-1, 1, + 1.0e0, 1.0e0, 0 +}; + +static char * +cvt(value, ndigit, decpt, sign, ecvtflag) + double value; + int ndigit, *decpt, *sign; +{ + static char buf[NDIGITS+1]; + register char *p = buf; + register char *pe; + + if (ndigit < 0) ndigit = 0; + if (ndigit > NDIGITS) ndigit = NDIGITS; + pe = &buf[ndigit]; + buf[0] = '\0'; + + *sign = 0; + if (value < 0) { + *sign = 1; + value = -value; + } + + *decpt = 0; + if (value >= DBL_MAX) { + value = DBL_MAX; + } + if (value != 0.0) { + register struct powers_of_10 *pp = &p10[0]; + + if (value >= 10.0) do { + while (value >= pp->pval) { + value *= pp->rpval; + *decpt += pp->exp; + } + } while ((++pp)->exp > 0); + + pp = &p10[0]; + if (value < 1.0) do { + while (value * pp->pval < 10.0) { + value *= pp->pval; + *decpt -= pp->exp; + } + } while ((++pp)->exp > 0); + + (*decpt)++; /* because now value in [1.0, 10.0) */ + } + if (! ecvtflag) { + /* for fcvt() we need ndigit digits behind the dot */ + pe += *decpt; + if (pe > &buf[NDIGITS]) pe = &buf[NDIGITS]; + } + while (p <= pe) { + *p++ = (int)value + '0'; + value = 10.0 * (value - (int)value); + } + if (pe >= buf) { + p = pe; + *p += 5; /* round of at the end */ + while (*p > '9') { + *p = '0'; + if (p > buf) ++*--p; + else { + *p = '1'; + ++*decpt; + if (! ecvtflag) { + /* maybe add another digit at the end, + because the point was shifted right + */ + if (pe > buf) *pe = '0'; + pe++; + } + } + } + *pe = '\0'; + } + return buf; +} +#endif diff --git a/lib/libp/diag.c b/lib/libp/diag.c new file mode 100755 index 000000000..ea16c0b7d --- /dev/null +++ b/lib/libp/diag.c @@ -0,0 +1,34 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> + +/* procedure diag(var f:text); */ + +diag(f) struct file *f; { + + f->ptr = f->bufadr; + f->flags = WRBIT|EOFBIT|ELNBIT|TXTBIT|MAGIC; + f->fname = "DIAG"; + f->ufd = 2; + f->size = 1; + f->count = 1; + f->buflen = 1; +} diff --git a/lib/libp/dis.c b/lib/libp/dis.c new file mode 100755 index 000000000..7d8c738e2 --- /dev/null +++ b/lib/libp/dis.c @@ -0,0 +1,87 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_err.h> + +#define assert() /* nothing */ + +/* + * use circular list of free blocks from low to high addresses + * _highp points to free block with highest address + */ +struct adm { + struct adm *next; + int size; +}; + +extern struct adm *_lastp; +extern struct adm *_highp; +extern _trp(); + +static int merge(p1,p2) struct adm *p1,*p2; { + struct adm *p; + + p = (struct adm *)((char *)p1 + p1->size); + if (p > p2) + _trp(EFREE); + if (p != p2) + return(0); + p1->size += p2->size; + p1->next = p2->next; + return(1); +} + +_dis(n,pp) int n; struct adm **pp; { + struct adm *p1,*p2; + + /* + * NOTE: dispose only objects whose size is a multiple of sizeof(*pp). + * this is always true for objects allocated by _new() + */ + n = ((n+sizeof(*p1)-1) / sizeof(*p1)) * sizeof(*p1); + if (n == 0) + return; + if ((p1= *pp) == (struct adm *) 0) + _trp(EFREE); + p1->size = n; + if ((p2 = _highp) == 0) /*p1 is the only free block*/ + p1->next = p1; + else { + if (p2 > p1) { + /*search for the preceding free block*/ + if (_lastp < p1) /*reduce search*/ + p2 = _lastp; + while (p2->next < p1) + p2 = p2->next; + } + /* if p2 preceeds p1 in the circular list, + * try to merge them */ + p1->next = p2->next; p2->next = p1; + if (p2 <= p1 && merge(p2,p1)) + p1 = p2; + p2 = p1->next; + /* p1 preceeds p2 in the circular list */ + if (p2 > p1) merge(p1,p2); + } + if (p1 >= p1->next) + _highp = p1; + _lastp = p1; + *pp = (struct adm *) 0; +} diff --git a/lib/libp/efl.c b/lib/libp/efl.c new file mode 100755 index 000000000..888de603a --- /dev/null +++ b/lib/libp/efl.c @@ -0,0 +1,36 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> +#include <pc_err.h> + +extern struct file *_curfil; +extern _trp(); +extern _incpt(); + +int _efl(f) struct file *f; { + + _curfil = f; + if ((f->flags & 0377) != MAGIC) + _trp(EBADF); + if ((f->flags & (WINDOW|WRBIT|EOFBIT)) == 0) + _incpt(f); + return((f->flags & EOFBIT) != 0); +} diff --git a/lib/libp/eln.c b/lib/libp/eln.c new file mode 100755 index 000000000..08be0a547 --- /dev/null +++ b/lib/libp/eln.c @@ -0,0 +1,33 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> +#include <pc_err.h> + +extern _trp(); +extern _rf(); + +int _eln(f) struct file *f; { + + _rf(f); + if (f->flags & EOFBIT) + _trp(EEOF); + return((f->flags & ELNBIT) != 0); +} diff --git a/lib/libp/encaps.e b/lib/libp/encaps.e new file mode 100755 index 000000000..43557e904 --- /dev/null +++ b/lib/libp/encaps.e @@ -0,0 +1,144 @@ +# + + +; $Header$ +; (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. +; +; This product is part of the Amsterdam Compiler Kit. +; +; Permission to use, sell, duplicate or disclose this software must be +; obtained in writing. Requests for such permissions may be sent to +; +; Dr. Andrew S. Tanenbaum +; Wiskundig Seminarium +; Vrije Universiteit +; Postbox 7161 +; 1007 MC Amsterdam +; The Netherlands +; + + mes 2,_EM_WSIZE,_EM_PSIZE + +; procedure encaps(procedure p; procedure(q(n:integer)); +; {call q if a trap occurs during the execution of p} +; {if q returns, continue execution of p} + + + inp $handler + +#define PIISZ 2*_EM_PSIZE + +#define PARG 0 +#define QARG PIISZ +#define E_ELB 0-_EM_PSIZE +#define E_EHA -2*_EM_PSIZE + +; encaps is called with two parameters: +; - procedure instance identifier of q (QARG) +; - procedure instance identifier of p (PARG) +; and two local variables: +; - the lb of the previous encaps (E_ELB) +; - the procedure identifier of the previous handler (E_EHA) +; +; One static variable: +; - the lb of the currently active encaps (enc_lb) + +enc_lb + bss _EM_PSIZE,0,0 + + exp $encaps + pro $encaps,PIISZ + ; save lb of previous encaps + lae enc_lb + loi _EM_PSIZE + lal E_ELB + sti _EM_PSIZE + ; set new lb + lxl 0 + lae enc_lb + sti _EM_PSIZE + ; save old handler id while setting up the new handler + lpi $handler + sig + lal E_EHA + sti _EM_PSIZE + ; handler is ready, p can be called + ; p doesn't expect parameters except possibly the static link + ; always passing the link won't hurt + lal PARG + loi PIISZ + cai + asp _EM_PSIZE + ; reinstate old handler + lal E_ELB + loi _EM_PSIZE + lae enc_lb + sti _EM_PSIZE + lal E_EHA + loi _EM_PSIZE + sig + asp _EM_PSIZE + ret 0 + end ? + +#define TRAP 0 +#define H_ELB 0-_EM_PSIZE + +; handler is called with one parameter: +; - trap number (TRAP) +; one local variable +; - the current LB of the enclosing encaps (H_ELB) + + + pro $handler,_EM_PSIZE + ; save LB of nearest encaps + lae enc_lb + loi _EM_PSIZE + lal H_ELB + sti _EM_PSIZE + ; fetch setting for previous encaps via LB of nearest + lal H_ELB + loi _EM_PSIZE + adp E_ELB + loi _EM_PSIZE ; LB of previous encaps + lae enc_lb + sti _EM_PSIZE + lal H_ELB + loi _EM_PSIZE + adp E_EHA + loi _EM_PSIZE ; previous handler + sig + asp _EM_PSIZE + ; previous handler is re-instated, time to call Q + lol TRAP ; the one and only real parameter + lal H_ELB + loi _EM_PSIZE + lpb ; argument base of enclosing encaps + adp QARG + loi PIISZ + exg _EM_PSIZE + dup _EM_PSIZE ; The static link is now on top + zer _EM_PSIZE + cmp + zeq *1 + ; non-zero LB + exg _EM_PSIZE + cai + asp _EM_WSIZE+_EM_PSIZE + bra *2 +1 + ; zero LB + asp _EM_PSIZE + cai + asp _EM_WSIZE +2 + ; now reinstate handler for continued execution of p + lal H_ELB + loi _EM_PSIZE + lae enc_lb + sti _EM_PSIZE + lpi $handler + sig + asp _EM_PSIZE + rtt + end ? diff --git a/lib/libp/exp.c b/lib/libp/exp.c new file mode 100755 index 000000000..f1f78498a --- /dev/null +++ b/lib/libp/exp.c @@ -0,0 +1,118 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ + +/* $Header$ */ +#define __NO_DEFS +#include <math.h> +#include <pc_err.h> +extern _trp(); + +#if __STDC__ +#include <float.h> +#include <pc_math.h> +#define M_MIN_D DBL_MIN +#define M_MAX_D DBL_MAX +#define M_DMINEXP DBL_MIN_EXP +#endif +#undef HUGE +#define HUGE 1e1000 + +static double +Ldexp(fl,exp) + double fl; + int exp; +{ + extern double _fef(); + int sign = 1; + int currexp; + + if (fl<0) { + fl = -fl; + sign = -1; + } + fl = _fef(fl,&currexp); + exp += currexp; + if (exp > 0) { + while (exp>30) { + fl *= (double) (1L << 30); + exp -= 30; + } + fl *= (double) (1L << exp); + } + else { + while (exp<-30) { + fl /= (double) (1L << 30); + exp += 30; + } + fl /= (double) (1L << -exp); + } + return sign * fl; +} + +double +_exp(x) + double x; +{ + /* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + */ + + static double p[] = { + 0.25000000000000000000e+0, + 0.75753180159422776666e-2, + 0.31555192765684646356e-4 + }; + + static double q[] = { + 0.50000000000000000000e+0, + 0.56817302698551221787e-1, + 0.63121894374398503557e-3, + 0.75104028399870046114e-6 + }; + double xn, g; + int n; + int negative = x < 0; + + if (x <= M_LN_MIN_D) { + g = M_MIN_D/4.0; + + if (g != 0.0) { + /* unnormalized numbers apparently exist */ + if (x < (M_LN2 * (M_DMINEXP - 53))) return 0.0; + } + else { + if (x < M_LN_MIN_D) return 0.0; + return M_MIN_D; + } + } + if (x >= M_LN_MAX_D) { + if (x > M_LN_MAX_D) { + _trp(EEXP); + return HUGE; + } + return M_MAX_D; + } + if (negative) x = -x; + + n = x * M_LOG2E + 0.5; /* 1/ln(2) = log2(e), 0.5 added for rounding */ + xn = n; + { + double x1 = (long) x; + double x2 = x - x1; + + g = ((x1-xn*0.693359375)+x2) - xn*(-2.1219444005469058277e-4); + } + if (negative) { + g = -g; + n = -n; + } + xn = g * g; + x = g * POLYNOM2(xn, p); + n += 1; + return (Ldexp(0.5 + x/(POLYNOM3(xn, q) - x), n)); +} diff --git a/lib/libp/fef.e b/lib/libp/fef.e new file mode 100755 index 000000000..0caaedcd6 --- /dev/null +++ b/lib/libp/fef.e @@ -0,0 +1,39 @@ +# +; $Header$ +; +; (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. +; +; This product is part of the Amsterdam Compiler Kit. +; +; Permission to use, sell, duplicate or disclose this software must be +; obtained in writing. Requests for such permissions may be sent to +; +; Dr. Andrew S. Tanenbaum +; Wiskundig Seminarium +; Vrije Universiteit +; Postbox 7161 +; 1007 MC Amsterdam +; The Netherlands +; +; + + mes 2,_EM_WSIZE,_EM_PSIZE + +#define FARG 0 +#define ERES _EM_DSIZE + +; _fef is called with two parameters: +; - address of exponent result (ERES) +; - floating point number to be split (FARG) +; and returns an _EM_DSIZE-byte floating point number + + exp $_fef + pro $_fef,0 + lal FARG + loi _EM_DSIZE + fef _EM_DSIZE + lal ERES + loi _EM_PSIZE + sti _EM_WSIZE + ret _EM_DSIZE + end ? diff --git a/lib/libp/fif.e b/lib/libp/fif.e new file mode 100755 index 000000000..2e11cf3b3 --- /dev/null +++ b/lib/libp/fif.e @@ -0,0 +1,41 @@ +# +; $Header$ +; +; (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. +; +; This product is part of the Amsterdam Compiler Kit. +; +; Permission to use, sell, duplicate or disclose this software must be +; obtained in writing. Requests for such permissions may be sent to +; +; Dr. Andrew S. Tanenbaum +; Wiskundig Seminarium +; Vrije Universiteit +; Postbox 7161 +; 1007 MC Amsterdam +; The Netherlands +; +; + + mes 2,_EM_WSIZE,_EM_PSIZE + +#define ARG1 0 +#define ARG2 _EM_DSIZE +#define IRES 2*_EM_DSIZE + +; _fif is called with three parameters: +; - address of integer part result (IRES) +; - float two (ARG2) +; - float one (ARG1) +; and returns an _EM_DSIZE-byte floating point number + + exp $_fif + pro $_fif,0 + lal 0 + loi 2*_EM_DSIZE + fif _EM_DSIZE + lal IRES + loi _EM_PSIZE + sti _EM_DSIZE + ret _EM_DSIZE + end ? diff --git a/lib/libp/get.c b/lib/libp/get.c new file mode 100755 index 000000000..ec8420772 --- /dev/null +++ b/lib/libp/get.c @@ -0,0 +1,31 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <pc_file.h> +#include <pc_err.h> + +extern _rf(); +extern _trp(); + +_get(f) struct file *f; { + + _rf(f); + if (f->flags&EOFBIT) + _trp(EEOF); + f->flags &= ~WINDOW; +} diff --git a/lib/libp/gto.e b/lib/libp/gto.e new file mode 100755 index 000000000..be9efa0ff --- /dev/null +++ b/lib/libp/gto.e @@ -0,0 +1,85 @@ +# +; $Header$ +; (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. +; +; This product is part of the Amsterdam Compiler Kit. +; +; Permission to use, sell, duplicate or disclose this software must be +; obtained in writing. Requests for such permissions may be sent to +; +; Dr. Andrew S. Tanenbaum +; Wiskundig Seminarium +; Vrije Universiteit +; Postbox 7161 +; 1007 MC Amsterdam +; The Netherlands +; + +/* Author: J.W. Stevenson */ + + + mes 2,_EM_WSIZE,_EM_PSIZE + +#define TARLB 0 +#define DESCR _EM_PSIZE + +#define NEWPC 0 +#define SAVSP _EM_PSIZE + +#define D_PC 0 +#define D_SP _EM_PSIZE +#define D_LB _EM_PSIZE+_EM_PSIZE + +#define LOCLB 0-_EM_PSIZE + +; _gto is called with two arguments: +; - pointer to the label descriptor (DESCR) +; - local base (LB) of target procedure (TARLB) +; the label descriptor contains two items: +; - label address i.e. new PC (NEWPC) +; - offset in target procedure frame (SAVSP) +; using this offset and the LB of the target procedure, the address of +; of local variable of the target procedure is constructed. +; the target procedure must have stored the correct target SP there. + +descr + bss 3*_EM_PSIZE,0,0 + + exp $_gto + pro $_gto,_EM_PSIZE + lal DESCR + loi _EM_PSIZE + adp NEWPC + loi _EM_PSIZE + lae descr+D_PC + sti _EM_PSIZE + lal TARLB + loi _EM_PSIZE + zer _EM_PSIZE + cmp + zeq *1 + lal TARLB + loi _EM_PSIZE + bra *2 +1 + lae _m_lb + loi _EM_PSIZE +2 + lal LOCLB + sti _EM_PSIZE + lal LOCLB + loi _EM_PSIZE + lal DESCR + loi _EM_PSIZE + adp SAVSP + loi _EM_WSIZE ; or _EM_PSIZE ? + ads _EM_WSIZE ; or _EM_PSIZE ? + loi _EM_PSIZE + lae descr+D_SP + sti _EM_PSIZE + lal LOCLB + loi _EM_PSIZE + lae descr+D_LB + sti _EM_PSIZE + gto descr + end ? diff --git a/lib/libp/head_pc.e b/lib/libp/head_pc.e new file mode 100755 index 000000000..63ad6ae5d --- /dev/null +++ b/lib/libp/head_pc.e @@ -0,0 +1,3 @@ +# +; $Header$ + mes 2,_EM_WSIZE,_EM_PSIZE diff --git a/lib/libp/hlt.c b/lib/libp/hlt.c new file mode 100755 index 000000000..0c04c592e --- /dev/null +++ b/lib/libp/hlt.c @@ -0,0 +1,35 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> + +extern struct file **_extfl; +extern int _extflc; +extern _cls(); +extern _exit(); + +_hlt(ecode) int ecode; { + int i; + + for (i = 0; i < _extflc; i++) + if (_extfl[i] != (struct file *) 0) + _cls(_extfl[i]); + _exit(ecode); +} diff --git a/lib/libp/hol0.e b/lib/libp/hol0.e new file mode 100755 index 000000000..d84d7731f --- /dev/null +++ b/lib/libp/hol0.e @@ -0,0 +1,29 @@ +# + +; $Header$ +; +; (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. +; +; This product is part of the Amsterdam Compiler Kit. +; +; Permission to use, sell, duplicate or disclose this software must be +; obtained in writing. Requests for such permissions may be sent to +; +; Dr. Andrew S. Tanenbaum +; Wiskundig Seminarium +; Vrije Universiteit +; Postbox 7161 +; 1007 MC Amsterdam +; The Netherlands +; +; + + mes 2,_EM_WSIZE,_EM_PSIZE + +; _hol0 return the address of the ABS block (hol0) + + exp $_hol0 + pro $_hol0,0 + lae 0 + ret _EM_PSIZE + end ? diff --git a/lib/libp/incpt.c b/lib/libp/incpt.c new file mode 100755 index 000000000..5818a8604 --- /dev/null +++ b/lib/libp/incpt.c @@ -0,0 +1,75 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> +#include <pc_err.h> + +#define EINTR 4 + +extern int errno; +extern _trp(); +extern int _read(); + +_incpt(f) struct file *f; { + + if (f->flags & EOFBIT) + _trp(EEOF); + f->flags |= WINDOW; + f->flags &= ~ELNBIT; +#ifdef CPM + do { +#endif + f->ptr += f->size; + if (f->count == 0) { + f->ptr = f->bufadr; + for(;;) { + f->count=_read(f->ufd,f->bufadr,f->buflen); + if ( f->count<0 ) { + if (errno != EINTR) _trp(EREAD) ; + continue ; + } + break ; + } + if (f->count == 0) { + f->flags |= EOFBIT; + *f->ptr = '\0'; + return; + } + } + if ((f->count -= f->size) < 0) + _trp(EFTRUNC); +#ifdef CPM + } while ((f->flags&TXTBIT) && *f->ptr == '\r'); +#endif + if (f->flags & TXTBIT) { + if (*f->ptr & 0200) + _trp(EASCII); + if (*f->ptr == '\n') { + f->flags |= ELNBIT; + *f->ptr = ' '; + } +#ifdef CPM + if (*f->ptr == 26) { + f->flags |= EOFBIT; + *f->ptr = 0; + } +#endif + } +} diff --git a/lib/libp/ini.c b/lib/libp/ini.c new file mode 100755 index 000000000..149bbe444 --- /dev/null +++ b/lib/libp/ini.c @@ -0,0 +1,72 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> +#include <pc_err.h> + +extern (*_sig())(); +extern _catch(); +#ifndef CPM +extern int _isatty(); +#endif + +struct file **_extfl; +int _extflc; /* number of external files */ +char *_m_lb; /* LB of _m_a_i_n */ +struct file *_curfil; /* points to file struct in case of errors */ +int _pargc; +char **_pargv; +char ***_penviron; +int _fp_hook = 1; /* This is for Minix, but does not harm others */ + +_ini(args,c,p,mainlb) char *args,*mainlb; int c; struct file **p; { + struct file *f; + char buf[128]; + + _pargc= *(int *)args; args += sizeof (int); + _pargv= *(char ***)args; + _sig(_catch); + _extfl = p; + _extflc = c; + if( !c ) return; + _m_lb = mainlb; + if ( (f = _extfl[0]) != (struct file *) 0) { + f->ptr = f->bufadr; + f->flags = MAGIC|TXTBIT; + f->fname = "INPUT"; + f->ufd = 0; + f->size = 1; + f->count = 0; + f->buflen = PC_BUFLEN; + } + if ( (f = _extfl[1]) != (struct file *) 0) { + f->ptr = f->bufadr; + f->flags = MAGIC|TXTBIT|WRBIT|EOFBIT|ELNBIT; + f->fname = "OUTPUT"; + f->ufd = 1; + f->size = 1; +#ifdef CPM + f->count = 1; +#else + f->count = (_isatty(1) ? 1 : PC_BUFLEN); +#endif + f->buflen = f->count; + } +} diff --git a/lib/libp/log.c b/lib/libp/log.c new file mode 100755 index 000000000..d5ba0e4e1 --- /dev/null +++ b/lib/libp/log.c @@ -0,0 +1,65 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ + +/* $Header$ */ + +#define __NO_DEFS +#include <math.h> +#include <pc_err.h> + +#if __STDC__ +#include <pc_math.h> +#include <float.h> +#endif +#undef HUGE +#define HUGE 1e1000 + +double +_log(x) + double x; +{ + /* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + */ + static double a[] = { + -0.64124943423745581147e2, + 0.16383943563021534222e2, + -0.78956112887491257267e0 + }; + static double b[] = { + -0.76949932108494879777e3, + 0.31203222091924532844e3, + -0.35667977739034646171e2, + 1.0 + }; + + extern double _fef(); + double znum, zden, z, w; + int exponent; + + if (x <= 0) { + _trp(ELOG); + return -HUGE; + } + + x = _fef(x, &exponent); + if (x > M_1_SQRT2) { + znum = (x - 0.5) - 0.5; + zden = x * 0.5 + 0.5; + } + else { + znum = x - 0.5; + zden = znum * 0.5 + 0.5; + exponent--; + } + z = znum/zden; w = z * z; + x = z + z * w * (POLYNOM2(w,a)/POLYNOM3(w,b)); + z = exponent; + x += z * (-2.121944400546905827679e-4); + return x + z * 0.693359375; +} diff --git a/lib/libp/mdi.c b/lib/libp/mdi.c new file mode 100755 index 000000000..af9438d74 --- /dev/null +++ b/lib/libp/mdi.c @@ -0,0 +1,71 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_err.h> + +extern _trp(); + +int _mdi(j,i) int j,i; { + + if (j <= 0) + _trp(EMOD); + i = i % j; + if (i < 0) + i += j; + return(i); +} + +long _mdil(j,i) long j,i; { + + if (j <= 0) + _trp(EMOD); + i = i % j; + if (i < 0) + i += j; + return(i); +} + +int _dvi(j, i) unsigned int j,i; { + int neg = 0; + + if ((int)j < 0) { + j = -(int)j; neg = 1; + } + if ((int)i < 0) { + i = -(int)i; neg = !neg; + } + i = i / j; + if (neg) return -(int)i; + return i; +} + +long _dvil(j, i) unsigned long j,i; { + int neg = 0; + + if ((long)j < 0) { + j = -(long)j; neg = 1; + } + if ((long)i < 0) { + i = -(long)i; neg = !neg; + } + i = i / j; + if (neg) return -(long)i; + return i; +} diff --git a/lib/libp/mdl.c b/lib/libp/mdl.c new file mode 100755 index 000000000..8c8272f44 --- /dev/null +++ b/lib/libp/mdl.c @@ -0,0 +1,33 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_err.h> + +extern _trp(); + +long _mdl(j,i) long j,i; { + + if (j <= 0) + _trp(EMOD); + i = i % j; + if (i < 0) + i += j; + return(i); +} diff --git a/lib/libp/new.c b/lib/libp/new.c new file mode 100755 index 000000000..6ea6a304a --- /dev/null +++ b/lib/libp/new.c @@ -0,0 +1,69 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +extern _sav(); +extern _rst(); + +#define assert(x) /* nothing */ +#define UNDEF 0x8000 + +struct adm { + struct adm *next; + int size; +}; + +struct adm *_lastp = 0; +struct adm *_highp = 0; + +_new(n,pp) int n; struct adm **pp; { + struct adm *p,*q; + int *ptmp; + + n = ((n+sizeof(*p)-1) / sizeof(*p)) * sizeof(*p); + if ((p = _lastp) != 0) + do { + q = p->next; + if (q->size >= n) { + assert(q->size%sizeof(adm) == 0); + if ((q->size -= n) == 0) { + if (p == q) + p = 0; + else + p->next = q->next; + if (q == _highp) + _highp = p; + } + _lastp = p; + p = (struct adm *)((char *)q + q->size); + q = (struct adm *)((char *)p + n); + goto initialize; + } + p = q; + } while (p != _lastp); + /*no free block big enough*/ + _sav(&p); + q = (struct adm *)((char *)p + n); + _rst(&q); +initialize: + *pp = p; + ptmp = (int *)p; + while (ptmp < (int *)q) + *ptmp++ = UNDEF; +} diff --git a/lib/libp/nfa.c b/lib/libp/nfa.c new file mode 100755 index 000000000..cfdb9af4d --- /dev/null +++ b/lib/libp/nfa.c @@ -0,0 +1,16 @@ +/* $Header$ */ +/* + * (c) copyright 1990 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ + +/* Author: Hans van Eck */ + +#include <pc_err.h> + +extern _trp(); + +_nfa(bool) +{ + if (! bool) _trp(EFUNASS); +} diff --git a/lib/libp/nobuff.c b/lib/libp/nobuff.c new file mode 100755 index 000000000..10f80cb48 --- /dev/null +++ b/lib/libp/nobuff.c @@ -0,0 +1,33 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> + +extern _flush(); + +/* procedure nobuff(var f:file of ?); */ + +nobuff(f) struct file *f; { + + if ((f->flags & (0377|WRBIT)) != (MAGIC|WRBIT)) + return; + _flush(f); + f->count = f->buflen = f->size; +} diff --git a/lib/libp/notext.c b/lib/libp/notext.c new file mode 100755 index 000000000..8a46e5f35 --- /dev/null +++ b/lib/libp/notext.c @@ -0,0 +1,23 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <pc_file.h> + +notext(f) struct file *f; { + f->flags &= ~TXTBIT; +} diff --git a/lib/libp/opn.c b/lib/libp/opn.c new file mode 100755 index 000000000..2df6d3834 --- /dev/null +++ b/lib/libp/opn.c @@ -0,0 +1,118 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> +#include <pc_err.h> + +extern struct file **_extfl; +extern int _extflc; +extern struct file *_curfil; +extern int _pargc; +extern char **_pargv; +extern char ***_penviron; + +extern _cls(); +extern _xcls(); +extern _trp(); +extern int _getpid(); +extern int _creat(); +extern int _open(); +extern int _close(); +extern int _unlink(); +extern long _lseek(); + +static int tmpfil() { + static char namebuf[] = "/usr/tmp/plf.xxxxx"; + int i; char *p,*q; + + i = _getpid(); + p = namebuf; + q = p + 13; + do + *q++ = (i & 07) + '0'; + while (i >>= 3); + *q = '\0'; + if ((i = _creat(p,0644)) < 0) + if ((i = _creat(p += 4,0644)) < 0) + if ((i = _creat(p += 5,0644)) < 0) + goto error; + if (_close(i) != 0) + goto error; + if ((i = _open(p,2)) < 0) + goto error; + if (_unlink(p) != 0) +error: _trp(EREWR); + return(i); +} + +static int initfl(descr,sz,f) int descr; int sz; struct file *f; { + int i; + + _curfil = f; + if (sz == 0) { + sz++; + descr |= TXTBIT; + } + for (i=0; i<_extflc; i++) + if (f == _extfl[i]) + break; + if (i >= _extflc) { /* local file */ + f->fname = "LOCAL"; + if ((descr & WRBIT) == 0 && (f->flags & 0377) == MAGIC) { + _xcls(f); + if (_lseek(f->ufd,(long)0,0) == -1) + _trp(ERESET); + } else { + _cls(f); + f->ufd = tmpfil(); + } + } else { /* external file */ + if (--i <= 0) + return(0); + if (i >= _pargc) + _trp(EARGC); + f->fname = _pargv[i]; + _cls(f); + if ((descr & WRBIT) == 0) { + if ((f->ufd = _open(f->fname,0)) < 0) + _trp(ERESET); + } else { + if ((f->ufd = _creat(f->fname,0644)) < 0) + _trp(EREWR); + } + } + f->buflen = (sz>PC_BUFLEN ? sz : PC_BUFLEN-PC_BUFLEN%sz); + f->size = sz; + f->ptr = f->bufadr; + f->flags = descr; + return(1); +} + +_opn(sz,f) int sz; struct file *f; { + + if (initfl(MAGIC,sz,f)) + f->count = 0; +} + +_cre(sz,f) int sz; struct file *f; { + + if (initfl(WRBIT|EOFBIT|ELNBIT|MAGIC,sz,f)) + f->count = f->buflen; +} diff --git a/lib/libp/outcpt.c b/lib/libp/outcpt.c new file mode 100755 index 000000000..98b02e5b3 --- /dev/null +++ b/lib/libp/outcpt.c @@ -0,0 +1,50 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> +#include <pc_err.h> + +#define EINTR 4 + +extern int errno; +extern _trp(); +extern int _write(); + +_flush(f) struct file *f; { + int i,n; + + f->ptr = f->bufadr; + n = f->buflen - f->count; + if (n <= 0) + return; + f->count = f->buflen; + if ((i = _write(f->ufd,f->bufadr,n)) < 0 && errno == EINTR) + return; + if (i != n) + _trp(EWRITE); +} + +_outcpt(f) struct file *f; { + + f->flags &= ~ELNBIT; + f->ptr += f->size; + if ((f->count -= f->size) <= 0) + _flush(f); +} diff --git a/lib/libp/pac.c b/lib/libp/pac.c new file mode 100755 index 000000000..6ce3751ff --- /dev/null +++ b/lib/libp/pac.c @@ -0,0 +1,63 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_err.h> + +extern _trp(); + +#define assert(x) /* nothing */ + +#ifndef EM_WSIZE +#define EM_WSIZE _EM_WSIZE +#endif + +struct descr { + int low; + int diff; + int size; +}; + +_pac(ad,zd,zp,i,ap) int i; struct descr *ad,*zd; char *zp,*ap; { + + if (zd->diff > ad->diff || + (i -= ad->low) < 0 || + (i+zd->diff) > ad->diff) + _trp(EPACK); + ap += (i * ad->size); + i = (zd->diff + 1) * zd->size; + if (zd->size == 1) { + int *aptmp = (int *)ap; + assert(ad->size == EM_WSIZE); + while (--i >= 0) + *zp++ = *aptmp++; +#if EM_WSIZE > 2 + } else if (zd->size == 2) { + int *aptmp = (int *)ap; + short *zptmp = (short *) zp; + assert(ad->size == EM_WSIZE); + while (--i >= 0) + *zptmp++ = *aptmp++; +#endif + } else { + assert(ad->size == zd->size); + while (--i >= 0) + *zp++ = *ap++; + } +} diff --git a/lib/libp/pclose.c b/lib/libp/pclose.c new file mode 100755 index 000000000..88ba88aea --- /dev/null +++ b/lib/libp/pclose.c @@ -0,0 +1,27 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <pc_file.h> + +extern _cls(); + +/* procedure pclose(var f:file of ??); */ + +pclose(f) struct file *f; { + _cls(f); +} diff --git a/lib/libp/pcreat.c b/lib/libp/pcreat.c new file mode 100755 index 000000000..f1190b922 --- /dev/null +++ b/lib/libp/pcreat.c @@ -0,0 +1,41 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> +#include <pc_err.h> + +extern _cls(); +extern _trp(); +extern int _creat(); + +/* procedure pcreat(var f:text; s:string); */ + +pcreat(f,s) struct file *f; char *s; { + + _cls(f); /* initializes _curfil */ + f->ptr = f->bufadr; + f->flags = WRBIT|EOFBIT|ELNBIT|TXTBIT|MAGIC; + f->fname = s; + f->size = 1; + f->count = PC_BUFLEN; + f->buflen = PC_BUFLEN; + if ((f->ufd = _creat(s,0644)) < 0) + _trp(EREWR); +} diff --git a/lib/libp/pentry.c b/lib/libp/pentry.c new file mode 100755 index 000000000..bac8aad0c --- /dev/null +++ b/lib/libp/pentry.c @@ -0,0 +1,35 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> + +extern struct file **_extfl; +extern _wrs(); +extern _wrz(); +extern _wln(); + +procentry(name) char *name; { + struct file *f; + + f = _extfl[1]; + _wrs(5,"call ",f); + _wrz(name,f); + _wln(f); +} diff --git a/lib/libp/perrno.c b/lib/libp/perrno.c new file mode 100755 index 000000000..3cc6a1b6d --- /dev/null +++ b/lib/libp/perrno.c @@ -0,0 +1,25 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* function perrno:integer; extern; */ + +extern int errno; + +int perrno() { + return(errno); +} diff --git a/lib/libp/pexit.c b/lib/libp/pexit.c new file mode 100755 index 000000000..2b00a28ac --- /dev/null +++ b/lib/libp/pexit.c @@ -0,0 +1,33 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <pc_file.h> + +extern struct file **_extfl; +extern _wrs(); +extern _wrz(); +extern _wln(); + +procexit(name) char *name; { + struct file *f; + + f = _extfl[1]; + _wrs(5,"exit ",f); + _wrz(name,f); + _wln(f); +} diff --git a/lib/libp/popen.c b/lib/libp/popen.c new file mode 100755 index 000000000..b542c7ee3 --- /dev/null +++ b/lib/libp/popen.c @@ -0,0 +1,41 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> +#include <pc_err.h> + +extern _cls(); +extern _trp(); +extern int _open(); + +/* procedure popen(var f:text; s:string); */ + +popen(f,s) struct file *f; char *s; { + + _cls(f); /* initializes _curfil */ + f->ptr = f->bufadr; + f->flags = TXTBIT|MAGIC; + f->fname = s; + f->size = 1; + f->count = 0; + f->buflen = PC_BUFLEN; + if ((f->ufd = _open(s,0)) < 0) + _trp(ERESET); +} diff --git a/lib/libp/put.c b/lib/libp/put.c new file mode 100755 index 000000000..dcc86e196 --- /dev/null +++ b/lib/libp/put.c @@ -0,0 +1,27 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <pc_file.h> + +extern _wf(); +extern _outcpt(); + +_put(f) struct file *f; { + _wf(f); + _outcpt(f); +} diff --git a/lib/libp/rcka.c b/lib/libp/rcka.c new file mode 100755 index 000000000..b08b6b0be --- /dev/null +++ b/lib/libp/rcka.c @@ -0,0 +1,25 @@ +/* $Header$ */ +/* + * (c) copyright 1990 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ + +/* Author: Hans van Eck */ + +#include <em_abs.h> + +extern _trp(); + +struct array_descr { + int lbound; + unsigned n_elts_min_one; + unsigned size; /* doesn't really matter */ + }; + +_rcka(descr, index) +struct array_descr *descr; +{ + if( index < descr->lbound || + index > (int) descr->n_elts_min_one + descr->lbound ) + _trp(EARRAY); +} diff --git a/lib/libp/rdc.c b/lib/libp/rdc.c new file mode 100755 index 000000000..17f0708a8 --- /dev/null +++ b/lib/libp/rdc.c @@ -0,0 +1,31 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <pc_file.h> + +extern _rf(); +extern _incpt(); + +int _rdc(f) struct file *f; { + int c; + + _rf(f); + c = *f->ptr; + _incpt(f); + return(c); +} diff --git a/lib/libp/rdi.c b/lib/libp/rdi.c new file mode 100755 index 000000000..fa1909b81 --- /dev/null +++ b/lib/libp/rdi.c @@ -0,0 +1,78 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> +#include <pc_err.h> + +extern _trp(); +extern _rf(); +extern _incpt(); + +_skipsp(f) struct file *f; { + while ((*f->ptr == ' ') || (*f->ptr == '\t')) + _incpt(f); +} + +int _getsig(f) struct file *f; { + int sign; + + if ((sign = (*f->ptr == '-')) || *f->ptr == '+') + _incpt(f); + return(sign); +} + +int _fstdig(f) struct file *f; { + int ch; + + ch = *f->ptr - '0'; + if ((unsigned) ch > 9) { + _trp(EDIGIT); + ch = 0; + } + return(ch); +} + +int _nxtdig(f) struct file *f; { + int ch; + + _incpt(f); + ch = *f->ptr - '0'; + if ((unsigned) ch > 9) + return(-1); + return(ch); +} + +int _getint(f) struct file *f; { + int is_signed,i,ch; + + is_signed = _getsig(f); + ch = _fstdig(f); + i = 0; + do + i = i*10 - ch; + while ((ch = _nxtdig(f)) >= 0); + return(is_signed ? i : -i); +} + +int _rdi(f) struct file *f; { + _rf(f); + _skipsp(f); + return(_getint(f)); +} diff --git a/lib/libp/rdl.c b/lib/libp/rdl.c new file mode 100755 index 000000000..76fa6a3f8 --- /dev/null +++ b/lib/libp/rdl.c @@ -0,0 +1,41 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> + +extern _rf(); +extern _skipsp(); +extern int _getsig(); +extern int _fstdig(); +extern int _nxtdig(); + +long _rdl(f) struct file *f; { + int is_signed,ch; long l; + + _rf(f); + _skipsp(f); + is_signed = _getsig(f); + ch = _fstdig(f); + l = 0; + do + l = l*10 - ch; + while ((ch = _nxtdig(f)) >= 0); + return(is_signed ? l : -l); +} diff --git a/lib/libp/rdr.c b/lib/libp/rdr.c new file mode 100755 index 000000000..a9ea1c8f3 --- /dev/null +++ b/lib/libp/rdr.c @@ -0,0 +1,78 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_file.h> + +#define BIG 1e17 + +extern _rf(); +extern _incpt(); +extern _skipsp(); +extern int _getsig(); +extern int _getint(); +extern int _fstdig(); +extern int _nxtdig(); + +static double r; +static int pow10; + +static dig(ch) int ch; { + + if (r>BIG) + pow10++; + else + r = r*10.0 + ch; +} + +double _rdr(f) struct file *f; { + int i; double e; int is_signed,ch; + + r = 0; + pow10 = 0; + _rf(f); + _skipsp(f); + is_signed = _getsig(f); + ch = _fstdig(f); + do + dig(ch); + while ((ch = _nxtdig(f)) >= 0); + if (*f->ptr == '.') { + _incpt(f); + ch = _fstdig(f); + do { + dig(ch); + pow10--; + } while ((ch = _nxtdig(f)) >= 0); + } + if ((*f->ptr == 'e') || (*f->ptr == 'E')) { + _incpt(f); + pow10 += _getint(f); + } + if ((i = pow10) < 0) + i = -i; + e = 1.0; + while (--i >= 0) + e *= 10.0; + if (pow10<0) + r /= e; + else + r *= e; + return(is_signed? -r : r); +} diff --git a/lib/libp/rf.c b/lib/libp/rf.c new file mode 100755 index 000000000..dee966838 --- /dev/null +++ b/lib/libp/rf.c @@ -0,0 +1,35 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <pc_file.h> +#include <pc_err.h> + +extern struct file *_curfil; +extern _trp(); +extern _incpt(); + +_rf(f) struct file *f; { + + _curfil = f; + if ((f->flags&0377) != MAGIC) + _trp(EBADF); + if (f->flags & WRBIT) + _trp(EREADF); + if ((f->flags & WINDOW) == 0) + _incpt(f); +} diff --git a/lib/libp/rln.c b/lib/libp/rln.c new file mode 100755 index 000000000..16e93c0ce --- /dev/null +++ b/lib/libp/rln.c @@ -0,0 +1,30 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <pc_file.h> + +extern _rf(); +extern _incpt(); + +_rln(f) struct file *f; { + + _rf(f); + while ((f->flags & ELNBIT) == 0) + _incpt(f); + f->flags &= ~WINDOW; +} diff --git a/lib/libp/rnd.c b/lib/libp/rnd.c new file mode 100755 index 000000000..0345caa87 --- /dev/null +++ b/lib/libp/rnd.c @@ -0,0 +1,21 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +double _rnd(r) double r; { + return(r + (r<0 ? -0.5 : 0.5)); +} diff --git a/lib/libp/sav.e b/lib/libp/sav.e new file mode 100755 index 000000000..317567833 --- /dev/null +++ b/lib/libp/sav.e @@ -0,0 +1,49 @@ +# +; $Header$ +; (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. +; +; This product is part of the Amsterdam Compiler Kit. +; +; Permission to use, sell, duplicate or disclose this software must be +; obtained in writing. Requests for such permissions may be sent to +; +; Dr. Andrew S. Tanenbaum +; Wiskundig Seminarium +; Vrije Universiteit +; Postbox 7161 +; 1007 MC Amsterdam +; The Netherlands +; + +/* Author: J.W. Stevenson */ + + + mes 2,_EM_WSIZE,_EM_PSIZE + +#define PTRAD 0 + +#define HP 2 + +; _sav called with one parameter: +; - address of pointer variable (PTRAD) + + exp $_sav + pro $_sav,0 + lor HP + lal PTRAD + loi _EM_PSIZE + sti _EM_PSIZE + ret 0 + end ? + +; _rst is called with one parameter: +; - address of pointer variable (PTRAD) + + exp $_rst + pro $_rst,0 + lal PTRAD + loi _EM_PSIZE + loi _EM_PSIZE + str HP + ret 0 + end ? diff --git a/lib/libp/sig.e b/lib/libp/sig.e new file mode 100755 index 000000000..8aec2651e --- /dev/null +++ b/lib/libp/sig.e @@ -0,0 +1,35 @@ +#define PROC 0 + +; $Header$ +; +; (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. +; +; This product is part of the Amsterdam Compiler Kit. +; +; Permission to use, sell, duplicate or disclose this software must be +; obtained in writing. Requests for such permissions may be sent to +; +; Dr. Andrew S. Tanenbaum +; Wiskundig Seminarium +; Vrije Universiteit +; Postbox 7161 +; 1007 MC Amsterdam +; The Netherlands +; +; + + mes 2,_EM_WSIZE,_EM_PSIZE + +; _sig is called with one parameter: +; - procedure instance identifier (PROC) +; and returns nothing. +; only the procedure identifier inside the PROC is used. + + exp $_sig + pro $_sig,0 + lal PROC + loi _EM_PSIZE + sig + asp _EM_PSIZE + ret 0 ; ignore the result of sig + end ? diff --git a/lib/libp/sin.c b/lib/libp/sin.c new file mode 100755 index 000000000..47132a45d --- /dev/null +++ b/lib/libp/sin.c @@ -0,0 +1,101 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ + +/* $Header$ */ + +#define __NO_DEFS +#include <math.h> + +#if __STDC__ +#include <pc_math.h> +#endif + +static double +sinus(x, cos_flag) + double x; +{ + /* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + */ + + static double r[] = { + -0.16666666666666665052e+0, + 0.83333333333331650314e-2, + -0.19841269841201840457e-3, + 0.27557319210152756119e-5, + -0.25052106798274584544e-7, + 0.16058936490371589114e-9, + -0.76429178068910467734e-12, + 0.27204790957888846175e-14 + }; + + double xsqr; + double y; + int neg = 0; + + if (x < 0) { + x = -x; + neg = 1; + } + if (cos_flag) { + neg = 0; + y = M_PI_2 + x; + } + else y = x; + + /* ??? avoid loss of significance, if y is too large, error ??? */ + + y = y * M_1_PI + 0.5; + + /* Use extended precision to calculate reduced argument. + Here we used 12 bits of the mantissa for a1. + Also split x in integer part x1 and fraction part x2. + */ +#define A1 3.1416015625 +#define A2 -8.908910206761537356617e-6 + { + double x1, x2; + extern double _fif(); + + _fif(y, 1.0, &y); + if (_fif(y, 0.5, &x1)) neg = !neg; + if (cos_flag) y -= 0.5; + x2 = _fif(x, 1.0, &x1); + x = x1 - y * A1; + x += x2; + x -= y * A2; +#undef A1 +#undef A2 + } + + if (x < 0) { + neg = !neg; + x = -x; + } + + /* ??? avoid underflow ??? */ + + y = x * x; + x += x * y * POLYNOM7(y, r); + return neg ? -x : x; +} + +double +_sin(x) + double x; +{ + return sinus(x, 0); +} + +double +_cos(x) + double x; +{ + if (x < 0) x = -x; + return sinus(x, 1); +} diff --git a/lib/libp/sqt.c b/lib/libp/sqt.c new file mode 100755 index 000000000..5f6f1b9d7 --- /dev/null +++ b/lib/libp/sqt.c @@ -0,0 +1,72 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ + +/* $Header$ */ +#define __NO_DEFS +#include <math.h> +#include <pc_err.h> +extern _trp(); + +#define NITER 5 + +static double +Ldexp(fl,exp) + double fl; + int exp; +{ + extern double _fef(); + int sign = 1; + int currexp; + + if (fl<0) { + fl = -fl; + sign = -1; + } + fl = _fef(fl,&currexp); + exp += currexp; + if (exp > 0) { + while (exp>30) { + fl *= (double) (1L << 30); + exp -= 30; + } + fl *= (double) (1L << exp); + } + else { + while (exp<-30) { + fl /= (double) (1L << 30); + exp += 30; + } + fl /= (double) (1L << -exp); + } + return sign * fl; +} + +double +_sqt(x) + double x; +{ + extern double _fef(); + int exponent; + double val; + + if (x <= 0) { + if (x < 0) _trp(ESQT); + return 0; + } + + val = _fef(x, &exponent); + if (exponent & 1) { + exponent--; + val *= 2; + } + val = Ldexp(val + 1.0, exponent/2 - 1); + /* was: val = (val + 1.0)/2.0; val = Ldexp(val, exponent/2); */ + for (exponent = NITER - 1; exponent >= 0; exponent--) { + val = (val + x / val) / 2.0; + } + return val; +} diff --git a/lib/libp/string.c b/lib/libp/string.c new file mode 100755 index 000000000..a36f608a0 --- /dev/null +++ b/lib/libp/string.c @@ -0,0 +1,60 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* function strbuf(var b:charbuf):string; */ + +char *strbuf(s) char *s; { + return(s); +} + +/* function strtobuf(s:string; var b:charbuf; blen:integer):integer; */ + +int strtobuf(s,b,l) char *s,*b; { + int i; + + i = 0; + while (--l>=0) { + if ((*b++ = *s++) == 0) + break; + i++; + } + return(i); +} + +/* function strlen(s:string):integer; */ + +int strlen(s) char *s; { + int i; + + i = 0; + while (*s++) + i++; + return(i); +} + +/* function strfetch(s:string; i:integer):char; */ + +int strfetch(s,i) char *s; { + return(s[i-1]); +} + +/* procedure strstore(s:string; i:integer; c:char); */ + +strstore(s,i,c) char *s; { + s[i-1] = c; +} diff --git a/lib/libp/trap.e b/lib/libp/trap.e new file mode 100755 index 000000000..cb4424ba9 --- /dev/null +++ b/lib/libp/trap.e @@ -0,0 +1,33 @@ +# + +; $Header$ +; +; (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. +; +; This product is part of the Amsterdam Compiler Kit. +; +; Permission to use, sell, duplicate or disclose this software must be +; obtained in writing. Requests for such permissions may be sent to +; +; Dr. Andrew S. Tanenbaum +; Wiskundig Seminarium +; Vrije Universiteit +; Postbox 7161 +; 1007 MC Amsterdam +; The Netherlands +; +; + + mes 2,_EM_WSIZE,_EM_PSIZE + +#define TRAP 0 + +; trap is called with one parameter: +; - trap number (TRAP) + + exp $trap + pro $trap,0 + lol TRAP + trp + ret 0 + end ? diff --git a/lib/libp/trp.e b/lib/libp/trp.e new file mode 100755 index 000000000..430669aa7 --- /dev/null +++ b/lib/libp/trp.e @@ -0,0 +1,38 @@ +# + +; $Header$ +; +; (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. +; +; This product is part of the Amsterdam Compiler Kit. +; +; Permission to use, sell, duplicate or disclose this software must be +; obtained in writing. Requests for such permissions may be sent to +; +; Dr. Andrew S. Tanenbaum +; Wiskundig Seminarium +; Vrije Universiteit +; Postbox 7161 +; 1007 MC Amsterdam +; The Netherlands +; +; + + mes 2,_EM_WSIZE,_EM_PSIZE + +#define TRAP 0 + +; _trp() and trap() perform the same function, +; but have to be separate. trap exists to facilitate the user. +; _trp is there for the system, trap cannot be used for that purpose +; because a user might define its own Pascal routine called trap. + +; _trp is called with one parameter: +; - trap number (TRAP) + + exp $_trp + pro $_trp,0 + lol TRAP + trp + ret 0 + end ? diff --git a/lib/libp/unp.c b/lib/libp/unp.c new file mode 100755 index 000000000..d9d5a5fa8 --- /dev/null +++ b/lib/libp/unp.c @@ -0,0 +1,65 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_err.h> + +extern _trp(); + +#define assert(x) /* nothing */ + +#ifndef EM_WSIZE +#define EM_WSIZE _EM_WSIZE +#endif + +struct descr { + int low; + int diff; + int size; +}; + +_unp(ad,zd,i,ap,zp,noext) int i; struct descr *ad,*zd; char *ap,*zp; int noext; { + + if (zd->diff > ad->diff || + (i -= ad->low) < 0 || + (i+zd->diff) > ad->diff) + _trp(EUNPACK); + ap += (i * ad->size); + i = (zd->diff + 1) * zd->size; + if (zd->size == 1) { + int *aptmp = (int *) ap; + assert(ad->size == EM_WSIZE); + while (--i >= 0) + if (noext) *aptmp++ = *zp++ & 0377; + else *aptmp++ = *zp++; +#if EM_WSIZE > 2 + } else if (zd->size == 2) { + int *aptmp = (int *) ap; + short *zptmp = (short *) zp; + assert(ad->size == EM_WSIZE); + while (--i >= 0) + if (noext) *aptmp++ = *zptmp++ & 0177777; + else *aptmp++ = *zptmp++; +#endif + } else { + assert(ad->size == zd->size); + while (--i >= 0) + *ap++ = *zp++; + } +} diff --git a/lib/libp/uread.c b/lib/libp/uread.c new file mode 100755 index 000000000..ce700470e --- /dev/null +++ b/lib/libp/uread.c @@ -0,0 +1,25 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* function uread(fd:integer; var b:buf; n:integer):integer; */ + +extern int _read(); + +int uread(fd,b,n) char *b; int fd,n; { + return(_read(fd,b,n)); +} diff --git a/lib/libp/uwrite.c b/lib/libp/uwrite.c new file mode 100755 index 000000000..1a1a20c0a --- /dev/null +++ b/lib/libp/uwrite.c @@ -0,0 +1,25 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* function uwrite(fd:integer; var b:buf; n:integer):integer; */ + +extern int _write(); + +int uwrite(fd,b,n) char *b; int fd,n; { + return(_write(fd,b,n)); +} diff --git a/lib/libp/wdw.c b/lib/libp/wdw.c new file mode 100755 index 000000000..33ac2f77a --- /dev/null +++ b/lib/libp/wdw.c @@ -0,0 +1,30 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <pc_file.h> + +extern struct file *_curfil; +extern _incpt(); + +char *_wdw(f) struct file *f; { + + _curfil = f; + if ((f->flags & (WINDOW|WRBIT|0377)) == MAGIC) + _incpt(f); + return(f->ptr); +} diff --git a/lib/libp/wf.c b/lib/libp/wf.c new file mode 100755 index 000000000..cd0f2b9f0 --- /dev/null +++ b/lib/libp/wf.c @@ -0,0 +1,32 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <pc_file.h> +#include <pc_err.h> + +extern struct file *_curfil; +extern _trp(); + +_wf(f) struct file *f; { + + _curfil = f; + if ((f->flags&0377) != MAGIC) + _trp(EBADF); + if ((f->flags & WRBIT) == 0) + _trp(EWRITEF); +} diff --git a/lib/libp/wrc.c b/lib/libp/wrc.c new file mode 100755 index 000000000..95b6ea25e --- /dev/null +++ b/lib/libp/wrc.c @@ -0,0 +1,41 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <pc_file.h> + +extern _wf(); +extern _outcpt(); + +_wrc(c,f) int c; struct file *f; { + *f->ptr = c; + _wf(f); + _outcpt(f); +} + +_wln(f) struct file *f; { +#ifdef CPM + _wrc('\r',f); +#endif + _wrc('\n',f); + f->flags |= ELNBIT; +} + +_pag(f) struct file *f; { + _wrc('\014',f); + f->flags |= ELNBIT; +} diff --git a/lib/libp/wrf.c b/lib/libp/wrf.c new file mode 100755 index 000000000..5ee6e629a --- /dev/null +++ b/lib/libp/wrf.c @@ -0,0 +1,68 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_err.h> +#include <pc_file.h> + +extern _wstrin(); +extern char *_fcvt(); + +#define assert(x) /* nothing */ + +#if __STDC__ +#include <float.h> +#define HUGE_DIG DBL_MAX_10_EXP /* log10(maxreal) */ +#else +#define HUGE_DIG 400 /* log10(maxreal) */ +#endif +#define PREC_DIG 80 /* the maximum digits returned by _fcvt() */ +#define FILL_CHAR '0' /* char printed if all of _fcvt() used */ +#define BUFSIZE HUGE_DIG + PREC_DIG + 3 + +_wrf(n,w,r,f) int n,w; double r; struct file *f; { + char *p,*b; int s,d; char buf[BUFSIZE]; + + if ( n < 0 || w < 0) _trp(EWIDTH); + p = buf; + if (n > PREC_DIG) + n = PREC_DIG; + b = _fcvt(r,n,&d,&s); + assert(abs(d) <= HUGE_DIG); + if (s) + *p++ = '-'; + if (d<=0) + *p++ = '0'; + else + do + *p++ = (*b ? *b++ : FILL_CHAR); + while (--d > 0); + if (n > 0) + *p++ = '.'; + while (++d <= 0) { + if (--n < 0) + break; + *p++ = '0'; + } + while (--n >= 0) { + *p++ = (*b ? *b++ : FILL_CHAR); + assert(p <= buf+BUFSIZE); + } + _wstrin(w,(int)(p-buf),buf,f); +} diff --git a/lib/libp/wri.c b/lib/libp/wri.c new file mode 100755 index 000000000..5c06e5290 --- /dev/null +++ b/lib/libp/wri.c @@ -0,0 +1,72 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <pc_err.h> +#include <pc_file.h> + +extern _wstrin(); + +#ifndef EM_WSIZE +#ifdef _EM_WSIZE +#define EM_WSIZE _EM_WSIZE +#endif +#endif + +#if EM_WSIZE==4 +#define SZ 11 +#define MININT -2147483648 +#define STRMININT "-2147483648" +#endif +#if EM_WSIZE==2 +#define SZ 6 +#define MININT -32768 +#define STRMININT "-32768" +#endif +#if EM_WSIZE==1 +#define SZ 4 +#define MININT -128 +#define STRMININT "-128" +#endif + +#ifndef STRMININT +Something wrong here! +#endif + +_wsi(w,i,f) int w,i; struct file *f; { + char *p; int j; char buf[SZ]; + + if (w < 0) _trp(EWIDTH); + p = &buf[SZ]; + if ((j=i) < 0) { + if (i == MININT) { + _wstrin(w,SZ,STRMININT,f); + return; + } + j = -j; + } + do + *--p = '0' + j%10; + while (j /= 10); + if (i<0) + *--p = '-'; + _wstrin(w,(int)(&buf[SZ]-p),p,f); +} + +_wri(i,f) int i; struct file *f; { + _wsi(SZ,i,f); +} diff --git a/lib/libp/wrl.c b/lib/libp/wrl.c new file mode 100755 index 000000000..f5a3d67ca --- /dev/null +++ b/lib/libp/wrl.c @@ -0,0 +1,51 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_err.h> +#include <pc_file.h> + +extern _wstrin(); + +#define MAXNEGLONG -2147483648 + +_wsl(w,l,f) int w; long l; struct file *f; { + char *p,c; long j; char buf[11]; + + if (w < 0) _trp(EWIDTH); + p = &buf[11]; + if ((j=l) < 0) { + if (l == MAXNEGLONG) { + _wstrin(w,11,"-2147483648",f); + return; + } + j = -j; + } + do { + c = j%10; + *--p = c + '0'; + } while (j /= 10); + if (l<0) + *--p = '-'; + _wstrin(w,(int)(&buf[11]-p),p,f); +} + +_wrl(l,f) long l; struct file *f; { + _wsl(11,l,f); +} diff --git a/lib/libp/wrr.c b/lib/libp/wrr.c new file mode 100755 index 000000000..5c2df66e7 --- /dev/null +++ b/lib/libp/wrr.c @@ -0,0 +1,67 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_err.h> +#include <pc_file.h> + +extern _wstrin(); +extern char *_ecvt(); + +#define PREC_DIG 80 /* maximum digits produced by _ecvt() */ + +_wsr(w,r,f) int w; double r; struct file *f; { + char *p,*b; int s,d,i; char buf[PREC_DIG+7]; + + if (w < 0) _trp(EWIDTH); + p = buf; + if ((i = w-6) < 2) + i = 2; + b = _ecvt(r,i,&d,&s); + *p++ = s? '-' : ' '; + if (*b == '0') + d++; + *p++ = *b++; + *p++ = '.'; + while (--i > 0) + *p++ = *b++; + *p++ = 'e'; + d--; + if (d < 0) { + d = -d; + *p++ = '-'; + } else + *p++ = '+'; + + if (d >= 1000) { + *p++ = '*'; + *p++ = '*'; + *p++ = '*'; + } + else { + *p++ = '0' + d/100; + *p++ = '0' + (d/10) % 10; + *p++ = '0' + d%10; + } + _wstrin(w,(int)(p-buf),buf,f); +} + +_wrr(r,f) double r; struct file *f; { + _wsr(13,r,f); +} diff --git a/lib/libp/wrs.c b/lib/libp/wrs.c new file mode 100755 index 000000000..a10951c61 --- /dev/null +++ b/lib/libp/wrs.c @@ -0,0 +1,68 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +/* Author: J.W. Stevenson */ + +#include <pc_err.h> +#include <pc_file.h> + +extern _wf(); +extern _outcpt(); + +_wstrin(width,len,buf,f) int width,len; char *buf; struct file *f; { + + _wf(f); + for (width -= len; width>0; width--) { + *f->ptr = ' '; + _outcpt(f); + } + while (--len >= 0) { + *f->ptr = *buf++; + _outcpt(f); + } +} + +_wsc(w,c,f) int w; char c; struct file *f; { + + if (w < 0) _trp(EWIDTH); + _wss(w,1,&c,f); +} + +_wss(w,len,s,f) int w,len; char *s; struct file *f; { + + if (w < 0 || len < 0) _trp(EWIDTH); + if (w < len) + len = w; + _wstrin(w,len,s,f); +} + +_wrs(len,s,f) int len; char *s; struct file *f; { + if (len < 0) _trp(EWIDTH); + _wss(len,len,s,f); +} + +_wsb(w,b,f) int w,b; struct file *f; { + if (b) + _wss(w,4,"true",f); + else + _wss(w,5,"false",f); +} + +_wrb(b,f) int b; struct file *f; { + _wsb(5,b,f); +} diff --git a/lib/libp/wrz.c b/lib/libp/wrz.c new file mode 100755 index 000000000..220c66a49 --- /dev/null +++ b/lib/libp/wrz.c @@ -0,0 +1,38 @@ +/* $Header$ */ +/* + * (c) copyright 1983 by the Vrije Universiteit, Amsterdam, The Netherlands. + * + * This product is part of the Amsterdam Compiler Kit. + * + * Permission to use, sell, duplicate or disclose this software must be + * obtained in writing. Requests for such permissions may be sent to + * + * Dr. Andrew S. Tanenbaum + * Wiskundig Seminarium + * Vrije Universiteit + * Postbox 7161 + * 1007 MC Amsterdam + * The Netherlands + * + */ + +#include <pc_err.h> +#include <pc_file.h> + +extern _wss(); +extern _wrs(); + +_wsz(w,s,f) int w; char *s; struct file *f; { + char *p; + + if (w < 0) _trp(EWIDTH); + for (p=s; *p; p++); + _wss(w,(int)(p-s),s,f); +} + +_wrz(s,f) char *s; struct file *f; { + char *p; + + for (p=s; *p; p++); + _wrs((int)(p-s),s,f); +} diff --git a/lib/liby/Makefile b/lib/liby/Makefile new file mode 100755 index 000000000..55923a48d --- /dev/null +++ b/lib/liby/Makefile @@ -0,0 +1,21 @@ +# Makefile for lib/liby. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -wo +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../liby.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(main.o) \ + $(LIBRARY)(yyerror.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(main.o): main.c + $(CC1) main.c + +$(LIBRARY)(yyerror.o): yyerror.c + $(CC1) yyerror.c diff --git a/lib/liby/main.c b/lib/liby/main.c new file mode 100755 index 000000000..bd5041975 --- /dev/null +++ b/lib/liby/main.c @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)main.c 5.3 (Berkeley) 1/13/91"; +#endif /* not lint */ + +main() +{ + exit(yyparse()); +} diff --git a/lib/liby/yyerror.c b/lib/liby/yyerror.c new file mode 100755 index 000000000..f20628e64 --- /dev/null +++ b/lib/liby/yyerror.c @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1990 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)yyerror.c 5.2 (Berkeley) 5/15/90"; +#endif /* not lint */ + +#include <stdio.h> + +yyerror(msg) +char *msg; +{ + (void)fprintf(stderr, "%s\n", msg); + return(0); +} diff --git a/lib/math/Makefile b/lib/math/Makefile new file mode 100755 index 000000000..160c2bf00 --- /dev/null +++ b/lib/math/Makefile @@ -0,0 +1,97 @@ +# Makefile for lib/math. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../libc.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(asin.o) \ + $(LIBRARY)(atan.o) \ + $(LIBRARY)(atan2.o) \ + $(LIBRARY)(ceil.o) \ + $(LIBRARY)(exp.o) \ + $(LIBRARY)(fabs.o) \ + $(LIBRARY)(floor.o) \ + $(LIBRARY)(fmod.o) \ + $(LIBRARY)(frexp.o) \ + $(LIBRARY)(hugeval.o) \ + $(LIBRARY)(isnan.o) \ + $(LIBRARY)(ldexp.o) \ + $(LIBRARY)(log.o) \ + $(LIBRARY)(log10.o) \ + $(LIBRARY)(modf.o) \ + $(LIBRARY)(pow.o) \ + $(LIBRARY)(sin.o) \ + $(LIBRARY)(sinh.o) \ + $(LIBRARY)(sqrt.o) \ + $(LIBRARY)(tan.o) \ + $(LIBRARY)(tanh.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(asin.o): asin.c + $(CC1) asin.c + +$(LIBRARY)(atan.o): atan.c + $(CC1) atan.c + +$(LIBRARY)(atan2.o): atan2.c + $(CC1) atan2.c + +$(LIBRARY)(ceil.o): ceil.c + $(CC1) ceil.c + +$(LIBRARY)(exp.o): exp.c + $(CC1) exp.c + +$(LIBRARY)(fabs.o): fabs.c + $(CC1) fabs.c + +$(LIBRARY)(floor.o): floor.c + $(CC1) floor.c + +$(LIBRARY)(fmod.o): fmod.c + $(CC1) fmod.c + +$(LIBRARY)(frexp.o): frexp.s + $(CC1) frexp.s + +$(LIBRARY)(hugeval.o): hugeval.c + $(CC1) hugeval.c + +$(LIBRARY)(isnan.o): isnan.c + $(CC1) isnan.c + +$(LIBRARY)(ldexp.o): ldexp.c + $(CC1) ldexp.c + +$(LIBRARY)(log.o): log.c + $(CC1) log.c + +$(LIBRARY)(log10.o): log10.c + $(CC1) log10.c + +$(LIBRARY)(modf.o): modf.s + $(CC1) modf.s + +$(LIBRARY)(pow.o): pow.c + $(CC1) pow.c + +$(LIBRARY)(sin.o): sin.c + $(CC1) sin.c + +$(LIBRARY)(sinh.o): sinh.c + $(CC1) sinh.c + +$(LIBRARY)(sqrt.o): sqrt.c + $(CC1) sqrt.c + +$(LIBRARY)(tan.o): tan.c + $(CC1) tan.c + +$(LIBRARY)(tanh.o): tanh.c + $(CC1) tanh.c diff --git a/lib/math/asin.c b/lib/math/asin.c new file mode 100755 index 000000000..ea90fce89 --- /dev/null +++ b/lib/math/asin.c @@ -0,0 +1,82 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <math.h> +#include <errno.h> +#include "localmath.h" + +static double +asin_acos(double x, int cosfl) +{ + int negative = x < 0; + int i; + double g; + static double p[] = { + -0.27368494524164255994e+2, + 0.57208227877891731407e+2, + -0.39688862997540877339e+2, + 0.10152522233806463645e+2, + -0.69674573447350646411e+0 + }; + static double q[] = { + -0.16421096714498560795e+3, + 0.41714430248260412556e+3, + -0.38186303361750149284e+3, + 0.15095270841030604719e+3, + -0.23823859153670238830e+2, + 1.0 + }; + + if (__IsNan(x)) { + errno = EDOM; + return x; + } + + if (negative) { + x = -x; + } + if (x > 0.5) { + i = 1; + if (x > 1) { + errno = EDOM; + return 0; + } + g = 0.5 - 0.5 * x; + x = - sqrt(g); + x += x; + } + else { + /* ??? avoid underflow ??? */ + i = 0; + g = x * x; + } + x += x * g * POLYNOM4(g, p) / POLYNOM5(g, q); + if (cosfl) { + if (! negative) x = -x; + } + if ((cosfl == 0) == (i == 1)) { + x = (x + M_PI_4) + M_PI_4; + } + else if (cosfl && negative && i == 1) { + x = (x + M_PI_2) + M_PI_2; + } + if (! cosfl && negative) x = -x; + return x; +} + +double +asin(double x) +{ + return asin_acos(x, 0); +} + +double +acos(double x) +{ + return asin_acos(x, 1); +} diff --git a/lib/math/atan.c b/lib/math/atan.c new file mode 100755 index 000000000..ff1c91275 --- /dev/null +++ b/lib/math/atan.c @@ -0,0 +1,72 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <float.h> +#include <math.h> +#include <errno.h> +#include "localmath.h" + +double +atan(double x) +{ + /* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + */ + + static double p[] = { + -0.13688768894191926929e+2, + -0.20505855195861651981e+2, + -0.84946240351320683534e+1, + -0.83758299368150059274e+0 + }; + static double q[] = { + 0.41066306682575781263e+2, + 0.86157349597130242515e+2, + 0.59578436142597344465e+2, + 0.15024001160028576121e+2, + 1.0 + }; + static double a[] = { + 0.0, + 0.52359877559829887307710723554658381, /* pi/6 */ + M_PI_2, + 1.04719755119659774615421446109316763 /* pi/3 */ + }; + + int neg = x < 0; + int n; + double g; + + if (__IsNan(x)) { + errno = EDOM; + return x; + } + if (neg) { + x = -x; + } + if (x > 1.0) { + x = 1.0/x; + n = 2; + } + else n = 0; + + if (x > 0.26794919243112270647) { /* 2-sqtr(3) */ + n = n + 1; + x = (((0.73205080756887729353*x-0.5)-0.5)+x)/ + (1.73205080756887729353+x); + } + + /* ??? avoid underflow ??? */ + + g = x * x; + x += x * g * POLYNOM3(g, p) / POLYNOM4(g, q); + if (n > 1) x = -x; + x += a[n]; + return neg ? -x : x; +} diff --git a/lib/math/atan2.c b/lib/math/atan2.c new file mode 100755 index 000000000..0e253c746 --- /dev/null +++ b/lib/math/atan2.c @@ -0,0 +1,42 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <math.h> +#include <errno.h> +#include "localmath.h" + +double +atan2(double y, double x) +{ + double absx, absy, val; + + if (x == 0 && y == 0) { + errno = EDOM; + return 0; + } + absy = y < 0 ? -y : y; + absx = x < 0 ? -x : x; + if (absy - absx == absy) { + /* x negligible compared to y */ + return y < 0 ? -M_PI_2 : M_PI_2; + } + if (absx - absy == absx) { + /* y negligible compared to x */ + val = 0.0; + } + else val = atan(y/x); + if (x > 0) { + /* first or fourth quadrant; already correct */ + return val; + } + if (y < 0) { + /* third quadrant */ + return val - M_PI; + } + return val + M_PI; +} diff --git a/lib/math/ceil.c b/lib/math/ceil.c new file mode 100755 index 000000000..15f649288 --- /dev/null +++ b/lib/math/ceil.c @@ -0,0 +1,20 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <math.h> + +double +ceil(double x) +{ + double val; + + return modf(x, &val) > 0 ? val + 1.0 : val ; + /* this also works if modf always returns a positive + fractional part + */ +} diff --git a/lib/math/exp.c b/lib/math/exp.c new file mode 100755 index 000000000..ff76afb17 --- /dev/null +++ b/lib/math/exp.c @@ -0,0 +1,72 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <math.h> +#include <float.h> +#include <errno.h> +#include "localmath.h" + + +double +exp(double x) +{ + /* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + */ + + static double p[] = { + 0.25000000000000000000e+0, + 0.75753180159422776666e-2, + 0.31555192765684646356e-4 + }; + + static double q[] = { + 0.50000000000000000000e+0, + 0.56817302698551221787e-1, + 0.63121894374398503557e-3, + 0.75104028399870046114e-6 + }; + double xn, g; + int n; + int negative = x < 0; + + if (__IsNan(x)) { + errno = EDOM; + return x; + } + if (x < M_LN_MIN_D) { + errno = ERANGE; + return 0.0; + } + if (x > M_LN_MAX_D) { + errno = ERANGE; + return HUGE_VAL; + } + + if (negative) x = -x; + + /* ??? avoid underflow ??? */ + + n = x * M_LOG2E + 0.5; /* 1/ln(2) = log2(e), 0.5 added for rounding */ + xn = n; + { + double x1 = (long) x; + double x2 = x - x1; + + g = ((x1-xn*0.693359375)+x2) - xn*(-2.1219444005469058277e-4); + } + if (negative) { + g = -g; + n = -n; + } + xn = g * g; + x = g * POLYNOM2(xn, p); + n += 1; + return (ldexp(0.5 + x/(POLYNOM3(xn, q) - x), n)); +} diff --git a/lib/math/fabs.c b/lib/math/fabs.c new file mode 100755 index 000000000..8c190c2c6 --- /dev/null +++ b/lib/math/fabs.c @@ -0,0 +1,13 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +double +fabs(double x) +{ + return x < 0 ? -x : x; +} diff --git a/lib/math/floor.c b/lib/math/floor.c new file mode 100755 index 000000000..ee0e6de05 --- /dev/null +++ b/lib/math/floor.c @@ -0,0 +1,20 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <math.h> + +double +floor(double x) +{ + double val; + + return modf(x, &val) < 0 ? val - 1.0 : val ; + /* this also works if modf always returns a positive + fractional part + */ +} diff --git a/lib/math/fmod.c b/lib/math/fmod.c new file mode 100755 index 000000000..8117b2b6e --- /dev/null +++ b/lib/math/fmod.c @@ -0,0 +1,34 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Hans van Eck + */ +/* $Header$ */ + +#include <math.h> +#include <errno.h> + +double +fmod(double x, double y) +{ + long i; + double val; + double frac; + + if (y == 0) { + errno = EDOM; + return 0; + } + frac = modf( x / y, &val); + + return frac * y; + +/* + val = x / y; + if (val > LONG_MIN && val < LONG_MAX) { + i = val; + return x - i * y; + } +*/ +} diff --git a/lib/math/frexp.s b/lib/math/frexp.s new file mode 100755 index 000000000..502caf783 --- /dev/null +++ b/lib/math/frexp.s @@ -0,0 +1,35 @@ +# +.sect .text; .sect .rom; .sect .data; .sect .bss +.extern _frexp +.sect .text +_frexp: +#if __i386 + push ebp + mov ebp, esp + push 12(ebp) + push 8(ebp) + mov eax, esp + add eax, -4 + push eax + call .fef8 + mov eax, 16(ebp) + pop (eax) + pop eax + pop edx + leave + ret +#else /* i86 */ + push bp + mov bp, sp + lea bx, 4(bp) + mov cx, #8 + call .loi + mov ax, sp + add ax, #-2 + push ax + call .fef8 + mov bx, 12(bp) + pop (bx) + call .ret8 + jmp .cret +#endif diff --git a/lib/math/hugeval.c b/lib/math/hugeval.c new file mode 100755 index 000000000..2a5b52684 --- /dev/null +++ b/lib/math/hugeval.c @@ -0,0 +1,14 @@ +/* + * (c) copyright 1990 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Hans van Eck + */ +/* $Header$ */ +#include <math.h> + +double +__huge_val(void) +{ + return 1.0e+1000; /* This will generate a warning */ +} diff --git a/lib/math/isnan.c b/lib/math/isnan.c new file mode 100755 index 000000000..97257f45b --- /dev/null +++ b/lib/math/isnan.c @@ -0,0 +1,11 @@ +int __IsNan(double d) +{ +#if defined(vax) || defined(pdp) +#else + float f = d; + + if ((*((long *) &f) & 0x7f800000) == 0x7f800000 && + (*((long *) &f) & 0x007fffff) != 0) return 1; +#endif + return 0; +} diff --git a/lib/math/ldexp.c b/lib/math/ldexp.c new file mode 100755 index 000000000..501dac452 --- /dev/null +++ b/lib/math/ldexp.c @@ -0,0 +1,55 @@ +/* + * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <math.h> +#include <float.h> +#include <errno.h> + +double +ldexp(double fl, int exp) +{ + int sign = 1; + int currexp; + + if (__IsNan(fl)) { + errno = EDOM; + return fl; + } + if (fl == 0.0) return 0.0; + if (fl<0) { + fl = -fl; + sign = -1; + } + if (fl > DBL_MAX) { /* for infinity */ + errno = ERANGE; + return sign * fl; + } + fl = frexp(fl,&currexp); + exp += currexp; + if (exp > 0) { + if (exp > DBL_MAX_EXP) { + errno = ERANGE; + return sign * HUGE_VAL; + } + while (exp>30) { + fl *= (double) (1L << 30); + exp -= 30; + } + fl *= (double) (1L << exp); + } + else { + /* number need not be normalized */ + if (exp < DBL_MIN_EXP - DBL_MANT_DIG) { + return 0.0; + } + while (exp<-30) { + fl /= (double) (1L << 30); + exp += 30; + } + fl /= (double) (1L << -exp); + } + return sign * fl; +} diff --git a/lib/math/localmath.h b/lib/math/localmath.h new file mode 100755 index 000000000..0b2aaea83 --- /dev/null +++ b/lib/math/localmath.h @@ -0,0 +1,42 @@ +/* + * localmath.h - This header is used by the mathematical library. + */ +/* $Header$ */ + +/* some constants (Hart & Cheney) */ +#define M_PI 3.14159265358979323846264338327950288 +#define M_2PI 6.28318530717958647692528676655900576 +#define M_3PI_4 2.35619449019234492884698253745962716 +#define M_PI_2 1.57079632679489661923132169163975144 +#define M_3PI_8 1.17809724509617246442349126872981358 +#define M_PI_4 0.78539816339744830961566084581987572 +#define M_PI_8 0.39269908169872415480783042290993786 +#define M_1_PI 0.31830988618379067153776752674502872 +#define M_2_PI 0.63661977236758134307553505349005744 +#define M_4_PI 1.27323954473516268615107010698011488 +#define M_E 2.71828182845904523536028747135266250 +#define M_LOG2E 1.44269504088896340735992468100189213 +#define M_LOG10E 0.43429448190325182765112891891660508 +#define M_LN2 0.69314718055994530941723212145817657 +#define M_LN10 2.30258509299404568401799145468436421 +#define M_SQRT2 1.41421356237309504880168872420969808 +#define M_1_SQRT2 0.70710678118654752440084436210484904 +#define M_EULER 0.57721566490153286060651209008240243 + +/* macros for constructing polynomials */ +#define POLYNOM1(x, a) ((a)[1]*(x)+(a)[0]) +#define POLYNOM2(x, a) (POLYNOM1((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM3(x, a) (POLYNOM2((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM4(x, a) (POLYNOM3((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM5(x, a) (POLYNOM4((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM6(x, a) (POLYNOM5((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM7(x, a) (POLYNOM6((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM8(x, a) (POLYNOM7((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM9(x, a) (POLYNOM8((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM10(x, a) (POLYNOM9((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM11(x, a) (POLYNOM10((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM12(x, a) (POLYNOM11((x),(a)+1)*(x)+(a)[0]) +#define POLYNOM13(x, a) (POLYNOM12((x),(a)+1)*(x)+(a)[0]) + +#define M_LN_MAX_D (M_LN2 * DBL_MAX_EXP) +#define M_LN_MIN_D (M_LN2 * (DBL_MIN_EXP - 1)) diff --git a/lib/math/log.c b/lib/math/log.c new file mode 100755 index 000000000..e6a673984 --- /dev/null +++ b/lib/math/log.c @@ -0,0 +1,67 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <math.h> +#include <float.h> +#include <errno.h> +#include "localmath.h" + +double +log(double x) +{ + /* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + */ + static double a[] = { + -0.64124943423745581147e2, + 0.16383943563021534222e2, + -0.78956112887491257267e0 + }; + static double b[] = { + -0.76949932108494879777e3, + 0.31203222091924532844e3, + -0.35667977739034646171e2, + 1.0 + }; + + double znum, zden, z, w; + int exponent; + + if (__IsNan(x)) { + errno = EDOM; + return x; + } + if (x < 0) { + errno = EDOM; + return -HUGE_VAL; + } + else if (x == 0) { + errno = ERANGE; + return -HUGE_VAL; + } + + if (x <= DBL_MAX) { + } + else return x; /* for infinity and Nan */ + x = frexp(x, &exponent); + if (x > M_1_SQRT2) { + znum = (x - 0.5) - 0.5; + zden = x * 0.5 + 0.5; + } + else { + znum = x - 0.5; + zden = znum * 0.5 + 0.5; + exponent--; + } + z = znum/zden; w = z * z; + x = z + z * w * (POLYNOM2(w,a)/POLYNOM3(w,b)); + z = exponent; + x += z * (-2.121944400546905827679e-4); + return x + z * 0.693359375; +} diff --git a/lib/math/log10.c b/lib/math/log10.c new file mode 100755 index 000000000..85e4296e2 --- /dev/null +++ b/lib/math/log10.c @@ -0,0 +1,30 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <math.h> +#include <errno.h> +#include "localmath.h" + +double +log10(double x) +{ + if (__IsNan(x)) { + errno = EDOM; + return x; + } + if (x < 0) { + errno = EDOM; + return -HUGE_VAL; + } + else if (x == 0) { + errno = ERANGE; + return -HUGE_VAL; + } + + return log(x) / M_LN10; +} diff --git a/lib/math/modf.s b/lib/math/modf.s new file mode 100755 index 000000000..5d1e39c0b --- /dev/null +++ b/lib/math/modf.s @@ -0,0 +1,49 @@ +# +.sect .text; .sect .rom; .sect .data; .sect .bss +.extern _modf +.sect .text +_modf: +#if __i386 + push ebp + mov ebp, esp + push 12(ebp) + push 8(ebp) + push 1 + push 4 + call .cif8 + mov eax, esp + push eax + call .fif8 + pop ecx + mov edx, 16(ebp) + pop ecx + pop ebx + mov 0(edx), ecx + mov 4(edx), ebx + pop eax + pop edx + leave + ret +#else /* i86 */ + push bp + mov bp, sp + lea bx, 4(bp) + mov cx, #8 + call .loi + mov dx, #1 + push dx + push dx + push dx + mov ax, #2 + push ax + call .cif8 + mov ax, sp + push ax + call .fif8 + pop bx + mov bx, 12(bp) + mov cx, #8 + call .sti + call .ret8 + jmp .cret +#endif diff --git a/lib/math/pow.c b/lib/math/pow.c new file mode 100755 index 000000000..b4a9006e3 --- /dev/null +++ b/lib/math/pow.c @@ -0,0 +1,54 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <math.h> +#include <float.h> +#include <errno.h> +#include "localmath.h" + +double +pow(double x, double y) +{ + /* Simple version for now. The Cody and Waite book has + a very complicated, much more precise version, but + this version has machine-dependent arrays A1 and A2, + and I don't know yet how to solve this ??? + */ + double dummy; + int result_neg = 0; + + if ((x == 0 && y == 0) || + (x < 0 && modf(y, &dummy) != 0)) { + errno = EDOM; + return 0; + } + + if (x == 0) return x; + + if (x < 0) { + if (modf(y/2.0, &dummy) != 0) { + /* y was odd */ + result_neg = 1; + } + x = -x; + } + x = log(x); + + if (x < 0) { + x = -x; + y = -y; + } + /* Beware of overflow in the multiplication */ + if (x > 1.0 && y > DBL_MAX/x) { + errno = ERANGE; + return result_neg ? -HUGE_VAL : HUGE_VAL; + } + + x = exp(x * y); + return result_neg ? -x : x; +} diff --git a/lib/math/sin.c b/lib/math/sin.c new file mode 100755 index 000000000..1f00f8394 --- /dev/null +++ b/lib/math/sin.c @@ -0,0 +1,99 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <math.h> +#include <float.h> +#include <errno.h> +#include "localmath.h" + +static double +sinus(double x, int cos_flag) +{ + /* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + */ + + static double r[] = { + -0.16666666666666665052e+0, + 0.83333333333331650314e-2, + -0.19841269841201840457e-3, + 0.27557319210152756119e-5, + -0.25052106798274584544e-7, + 0.16058936490371589114e-9, + -0.76429178068910467734e-12, + 0.27204790957888846175e-14 + }; + + double y; + int neg = 1; + + if (__IsNan(x)) { + errno = EDOM; + return x; + } + if (x < 0) { + x = -x; + neg = -1; + } + if (cos_flag) { + neg = 1; + y = M_PI_2 + x; + } + else y = x; + + /* ??? avoid loss of significance, if y is too large, error ??? */ + + y = y * M_1_PI + 0.5; + + if (y >= DBL_MAX/M_PI) return 0.0; + + /* Use extended precision to calculate reduced argument. + Here we used 12 bits of the mantissa for a1. + Also split x in integer part x1 and fraction part x2. + */ +#define A1 3.1416015625 +#define A2 -8.908910206761537356617e-6 + { + double x1, x2; + + modf(y, &y); + if (modf(0.5*y, &x1)) neg = -neg; + if (cos_flag) y -= 0.5; + x2 = modf(x, &x1); + x = x1 - y * A1; + x += x2; + x -= y * A2; +#undef A1 +#undef A2 + } + + if (x < 0) { + neg = -neg; + x = -x; + } + + /* ??? avoid underflow ??? */ + + y = x * x; + x += x * y * POLYNOM7(y, r); + return neg==-1 ? -x : x; +} + +double +sin(double x) +{ + return sinus(x, 0); +} + +double +cos(double x) +{ + if (x < 0) x = -x; + return sinus(x, 1); +} diff --git a/lib/math/sinh.c b/lib/math/sinh.c new file mode 100755 index 000000000..356604a52 --- /dev/null +++ b/lib/math/sinh.c @@ -0,0 +1,81 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <math.h> +#include <float.h> +#include <errno.h> +#include "localmath.h" + +static double +sinh_cosh(double x, int cosh_flag) +{ + /* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + */ + + static double p[] = { + -0.35181283430177117881e+6, + -0.11563521196851768270e+5, + -0.16375798202630751372e+3, + -0.78966127417357099479e+0 + }; + static double q[] = { + -0.21108770058106271242e+7, + 0.36162723109421836460e+5, + -0.27773523119650701167e+3, + 1.0 + }; + int negative = x < 0; + double y = negative ? -x : x; + + if (__IsNan(x)) { + errno = EDOM; + return x; + } + if (! cosh_flag && y <= 1.0) { + /* ??? check for underflow ??? */ + y = y * y; + return x + x * y * POLYNOM3(y, p)/POLYNOM3(y,q); + } + + if (y >= M_LN_MAX_D) { + /* exp(y) would cause overflow */ +#define LNV 0.69316101074218750000e+0 +#define VD2M1 0.52820835025874852469e-4 + double w = y - LNV; + + if (w < M_LN_MAX_D+M_LN2-LNV) { + x = exp(w); + x += VD2M1 * x; + } + else { + errno = ERANGE; + x = HUGE_VAL; + } + } + else { + double z = exp(y); + + x = 0.5 * (z + (cosh_flag ? 1.0 : -1.0)/z); + } + return negative ? -x : x; +} + +double +sinh(double x) +{ + return sinh_cosh(x, 0); +} + +double +cosh(double x) +{ + if (x < 0) x = -x; + return sinh_cosh(x, 1); +} diff --git a/lib/math/sqrt.c b/lib/math/sqrt.c new file mode 100755 index 000000000..e02638812 --- /dev/null +++ b/lib/math/sqrt.c @@ -0,0 +1,43 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <math.h> +#include <float.h> +#include <errno.h> + +#define NITER 5 + +double +sqrt(double x) +{ + int exponent; + double val; + + if (__IsNan(x)) { + errno = EDOM; + return x; + } + if (x <= 0) { + if (x < 0) errno = EDOM; + return 0; + } + + if (x > DBL_MAX) return x; /* for infinity */ + + val = frexp(x, &exponent); + if (exponent & 1) { + exponent--; + val *= 2; + } + val = ldexp(val + 1.0, exponent/2 - 1); + /* was: val = (val + 1.0)/2.0; val = ldexp(val, exponent/2); */ + for (exponent = NITER - 1; exponent >= 0; exponent--) { + val = (val + x / val) / 2.0; + } + return val; +} diff --git a/lib/math/tan.c b/lib/math/tan.c new file mode 100755 index 000000000..809b49a26 --- /dev/null +++ b/lib/math/tan.c @@ -0,0 +1,76 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <math.h> +#include <float.h> +#include <errno.h> +#include "localmath.h" + +double +tan(double x) +{ + /* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + */ + + int negative = x < 0; + int invert = 0; + double y; + static double p[] = { + 1.0, + -0.13338350006421960681e+0, + 0.34248878235890589960e-2, + -0.17861707342254426711e-4 + }; + static double q[] = { + 1.0, + -0.46671683339755294240e+0, + 0.25663832289440112864e-1, + -0.31181531907010027307e-3, + 0.49819433993786512270e-6 + }; + + if (__IsNan(x)) { + errno = EDOM; + return x; + } + if (negative) x = -x; + + /* ??? avoid loss of significance, error if x is too large ??? */ + + y = x * M_2_PI + 0.5; + + if (y >= DBL_MAX/M_PI_2) return 0.0; + + /* Use extended precision to calculate reduced argument. + Here we used 12 bits of the mantissa for a1. + Also split x in integer part x1 and fraction part x2. + */ + #define A1 1.57080078125 + #define A2 -4.454455103380768678308e-6 + { + double x1, x2; + + modf(y, &y); + if (modf(0.5*y, &x1)) invert = 1; + x2 = modf(x, &x1); + x = x1 - y * A1; + x += x2; + x -= y * A2; + #undef A1 + #undef A2 + } + + /* ??? avoid underflow ??? */ + y = x * x; + x += x * y * POLYNOM2(y, p+1); + y = POLYNOM4(y, q); + if (negative) x = -x; + return invert ? -y/x : x/y; +} diff --git a/lib/math/tanh.c b/lib/math/tanh.c new file mode 100755 index 000000000..ea29c61b6 --- /dev/null +++ b/lib/math/tanh.c @@ -0,0 +1,55 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ +/* $Header$ */ + +#include <float.h> +#include <math.h> +#include <errno.h> +#include "localmath.h" + +double +tanh(double x) +{ + /* Algorithm and coefficients from: + "Software manual for the elementary functions" + by W.J. Cody and W. Waite, Prentice-Hall, 1980 + */ + + static double p[] = { + -0.16134119023996228053e+4, + -0.99225929672236083313e+2, + -0.96437492777225469787e+0 + }; + static double q[] = { + 0.48402357071988688686e+4, + 0.22337720718962312926e+4, + 0.11274474380534949335e+3, + 1.0 + }; + int negative = x < 0; + + if (__IsNan(x)) { + errno = EDOM; + return x; + } + if (negative) x = -x; + + if (x >= 0.5*M_LN_MAX_D) { + x = 1.0; + } +#define LN3D2 0.54930614433405484570e+0 /* ln(3)/2 */ + else if (x > LN3D2) { + x = 0.5 - 1.0/(exp(x+x)+1.0); + x += x; + } + else { + /* ??? avoid underflow ??? */ + double g = x*x; + x += x * g * POLYNOM2(g, p)/POLYNOM3(g, q); + } + return negative ? -x : x; +} diff --git a/lib/other/Makefile b/lib/other/Makefile new file mode 100755 index 000000000..7808c02af --- /dev/null +++ b/lib/other/Makefile @@ -0,0 +1,226 @@ +# Makefile for lib/other. + +# The bxxx(), and *index() functions are not used, because they have assembly +# equivalents. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -I../../servers +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../libc.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(_brk.o) \ + $(LIBRARY)(_reboot.o) \ + $(LIBRARY)(_seekdir.o) \ + $(LIBRARY)(_svrctl.o) \ + $(LIBRARY)(_getsysinfo.o) \ + $(LIBRARY)(asynchio.o) \ + $(LIBRARY)(configfile.o) \ + $(LIBRARY)(crypt.o) \ + $(LIBRARY)(ctermid.o) \ + $(LIBRARY)(cuserid.o) \ + $(LIBRARY)(environ.o) \ + $(LIBRARY)(errno.o) \ + $(LIBRARY)(fdopen.o) \ + $(LIBRARY)(ffs.o) \ + $(LIBRARY)(fslib.o) \ + $(LIBRARY)(fsversion.o) \ + $(LIBRARY)(getgrent.o) \ + $(LIBRARY)(getlogin.o) \ + $(LIBRARY)(getopt.o) \ + $(LIBRARY)(getpass.o) \ + $(LIBRARY)(getpwent.o) \ + $(LIBRARY)(getttyent.o) \ + $(LIBRARY)(getw.o) \ + $(LIBRARY)(hypot.o) \ + $(LIBRARY)(itoa.o) \ + $(LIBRARY)(loadname.o) \ + $(LIBRARY)(lock.o) \ + $(LIBRARY)(lrand.o) \ + $(LIBRARY)(lsearch.o) \ + $(LIBRARY)(memccpy.o) \ + $(LIBRARY)(mstats.o) \ + $(LIBRARY)(mtab.o) \ + $(LIBRARY)(nlist.o) \ + $(LIBRARY)(peekpoke.o) \ + $(LIBRARY)(popen.o) \ + $(LIBRARY)(putenv.o) \ + $(LIBRARY)(putw.o) \ + $(LIBRARY)(setcache.o) \ + $(LIBRARY)(stderr.o) \ + $(LIBRARY)(swab.o) \ + $(LIBRARY)(syscall.o) \ + $(LIBRARY)(sysconf.o) \ + $(LIBRARY)(telldir.o) \ + $(LIBRARY)(termcap.o) \ + $(LIBRARY)(ttyname.o) \ + $(LIBRARY)(ttyslot.o) \ + $(LIBRARY)(v8regexp.o) \ + $(LIBRARY)(v8regsub.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(_brk.o): _brk.c + $(CC1) _brk.c + +$(LIBRARY)(_reboot.o): _reboot.c + $(CC1) _reboot.c + +$(LIBRARY)(_seekdir.o): _seekdir.c + $(CC1) _seekdir.c + +$(LIBRARY)(_svrctl.o): _svrctl.c + $(CC1) _svrctl.c + +$(LIBRARY)(_getsysinfo.o): _getsysinfo.c + $(CC1) _getsysinfo.c + +$(LIBRARY)(asynchio.o): asynchio.c + $(CC1) asynchio.c + +$(LIBRARY)(bcmp.o): bcmp.c + $(CC1) bcmp.c + +$(LIBRARY)(bcopy.o): bcopy.c + $(CC1) bcopy.c + +$(LIBRARY)(bzero.o): bzero.c + $(CC1) bzero.c + +$(LIBRARY)(configfile.o): configfile.c + $(CC1) configfile.c + +$(LIBRARY)(crypt.o): crypt.c + $(CC1) crypt.c + +$(LIBRARY)(ctermid.o): ctermid.c + $(CC1) ctermid.c + +$(LIBRARY)(cuserid.o): cuserid.c + $(CC1) cuserid.c + +$(LIBRARY)(environ.o): environ.c + $(CC1) environ.c + +$(LIBRARY)(errno.o): errno.c + $(CC1) errno.c + +$(LIBRARY)(fdopen.o): fdopen.c + $(CC1) fdopen.c + +$(LIBRARY)(ffs.o): ffs.c + $(CC1) ffs.c + +$(LIBRARY)(fslib.o): fslib.c + $(CC1) fslib.c + +$(LIBRARY)(fsversion.o): fsversion.c + $(CC1) fsversion.c + +$(LIBRARY)(getgrent.o): getgrent.c + $(CC1) getgrent.c + +$(LIBRARY)(getlogin.o): getlogin.c + $(CC1) getlogin.c + +$(LIBRARY)(getopt.o): getopt.c + $(CC1) getopt.c + +$(LIBRARY)(getpass.o): getpass.c + $(CC1) getpass.c + +$(LIBRARY)(getpwent.o): getpwent.c + $(CC1) getpwent.c + +$(LIBRARY)(getttyent.o): getttyent.c + $(CC1) getttyent.c + +$(LIBRARY)(getw.o): getw.c + $(CC1) getw.c + +$(LIBRARY)(hypot.o): hypot.c + $(CC1) hypot.c + +$(LIBRARY)(index.o): index.c + $(CC1) index.c + +$(LIBRARY)(itoa.o): itoa.c + $(CC1) itoa.c + +$(LIBRARY)(loadname.o): loadname.c + $(CC1) loadname.c + +$(LIBRARY)(lock.o): lock.c + $(CC1) lock.c + +$(LIBRARY)(lrand.o): lrand.c + $(CC1) lrand.c + +$(LIBRARY)(lsearch.o): lsearch.c + $(CC1) lsearch.c + +$(LIBRARY)(memccpy.o): memccpy.c + $(CC1) memccpy.c + +$(LIBRARY)(mstats.o): mstats.c + $(CC1) mstats.c + +$(LIBRARY)(mtab.o): mtab.c + $(CC1) mtab.c + +$(LIBRARY)(nlist.o): nlist.c + $(CC1) nlist.c + +$(LIBRARY)(peekpoke.o): peekpoke.c + $(CC1) peekpoke.c + +$(LIBRARY)(popen.o): popen.c + $(CC1) popen.c + +$(LIBRARY)(putenv.o): putenv.c + $(CC1) putenv.c + +$(LIBRARY)(putw.o): putw.c + $(CC1) putw.c + +$(LIBRARY)(rindex.o): rindex.c + $(CC1) rindex.c + +$(LIBRARY)(setcache.o): setcache.c + $(CC1) setcache.c + +$(LIBRARY)(stderr.o): stderr.c + $(CC1) stderr.c + +$(LIBRARY)(swab.o): swab.c + $(CC1) swab.c + +$(LIBRARY)(syscall.o): syscall.c + $(CC1) syscall.c + +$(LIBRARY)(sysconf.o): sysconf.c + $(CC1) sysconf.c + +$(LIBRARY)(syslib.o): syslib.c + $(CC1) syslib.c + +$(LIBRARY)(telldir.o): telldir.c + $(CC1) telldir.c + +$(LIBRARY)(termcap.o): termcap.c + $(CC1) termcap.c + +$(LIBRARY)(ttyname.o): ttyname.c + $(CC1) ttyname.c + +$(LIBRARY)(ttyslot.o): ttyslot.c + $(CC1) ttyslot.c + +$(LIBRARY)(v8regexp.o): v8regexp.c + $(CC1) v8regexp.c + +$(LIBRARY)(v8regsub.o): v8regsub.c + $(CC1) v8regsub.c diff --git a/lib/other/_brk.c b/lib/other/_brk.c new file mode 100755 index 000000000..391b36e18 --- /dev/null +++ b/lib/other/_brk.c @@ -0,0 +1,44 @@ +#include <lib.h> +#define brk _brk +#define sbrk _sbrk +#include <unistd.h> + +extern char *_brksize; + +/* Both OSF/1 and SYSVR4 man pages specify that brk(2) returns int. + * However, BSD4.3 specifies that brk() returns char*. POSIX omits + * brk() on the grounds that it imposes a memory model on an architecture. + * For this reason, brk() and sbrk() are not in the lib/posix directory. + * On the other hand, they are so crucial to correct operation of so many + * parts of the system, that we have chosen to hide the name brk using _brk, + * as with system calls. In this way, if a user inadvertently defines a + * procedure brk, MINIX may continue to work because the true call is _brk. + */ +PUBLIC int brk(addr) +char *addr; +{ + message m; + + if (addr != _brksize) { + m.m1_p1 = addr; + if (_syscall(MM, BRK, &m) < 0) return(-1); + _brksize = m.m2_p1; + } + return(0); +} + + +PUBLIC char *sbrk(incr) +int incr; +{ + char *newsize, *oldsize; + + oldsize = _brksize; + newsize = _brksize + incr; + if ((incr > 0 && newsize < oldsize) || (incr < 0 && newsize > oldsize)) + return( (char *) -1); + if (brk(newsize) == 0) + return(oldsize); + else + return( (char *) -1); +} diff --git a/lib/other/_getsysinfo.c b/lib/other/_getsysinfo.c new file mode 100644 index 000000000..d98b8833f --- /dev/null +++ b/lib/other/_getsysinfo.c @@ -0,0 +1,19 @@ +#include <lib.h> +#define getsysinfo _getsysinfo +#define getsysinfo _getsysinfo +#include <unistd.h> + + +PUBLIC int getsysinfo(who, what, where) +int who; /* from whom to request info */ +int what; /* what information is requested */ +void *where; /* where to put it */ +{ + message m; + + m.m1_i1 = what; + m.m1_p1 = where; + if (_syscall(who, GETSYSINFO, &m) < 0) return(-1); + return(0); +} + diff --git a/lib/other/_reboot.c b/lib/other/_reboot.c new file mode 100755 index 000000000..8e87b6958 --- /dev/null +++ b/lib/other/_reboot.c @@ -0,0 +1,24 @@ +/* reboot.c - Systemcall interface to mm/signal.c::do_reboot() + + author: Edvard Tuinder v892231@si.hhs.NL + */ + +#include <lib.h> +#define reboot _reboot +#include <unistd.h> +#include <stdarg.h> + +int reboot(int how, ...) +{ + message m; + va_list ap; + + va_start(ap, how); + if ((m.m1_i1 = how) == RBT_MONITOR) { + m.m1_p1 = va_arg(ap, char *); + m.m1_i2 = va_arg(ap, size_t); + } + va_end(ap); + + return _syscall(MM, REBOOT, &m); +} diff --git a/lib/other/_seekdir.c b/lib/other/_seekdir.c new file mode 100755 index 000000000..5e9746428 --- /dev/null +++ b/lib/other/_seekdir.c @@ -0,0 +1,32 @@ +/* seekdir() Author: Kees J. Bot + * 24 Apr 1989 + */ +#define nil 0 +#include <lib.h> +#define lseek _lseek +#define readdir _readdir +#define seekdir _seekdir +#include <sys/types.h> +#include <dirent.h> +#include <unistd.h> +#include <errno.h> + +int seekdir(DIR *dp, off_t pos) +/* Seek to position pos in a directory. */ +{ + int off; + + if (dp == nil) { errno= EBADF; return -1; } + + dp->_count= 0; + dp->_ptr= dp->_buf; + + off= pos & (sizeof(dp->_buf) - 1); + dp->_pos= pos - off; + + if (lseek(dp->_fd, dp->_pos, SEEK_SET) == -1) return -1; + + while (dp->_pos < pos && readdir(dp) != nil) {} + + return 0; +} diff --git a/lib/other/_svrctl.c b/lib/other/_svrctl.c new file mode 100755 index 000000000..f48d49230 --- /dev/null +++ b/lib/other/_svrctl.c @@ -0,0 +1,28 @@ +/* svrctl() - special server control functions. Author: Kees J. Bot + * 24 Apr 1994 + */ +#include <lib.h> +#define svrctl _svrctl +#include <sys/svrctl.h> + +int svrctl(int request, void *argp) +{ + message m; + + m.m2_i1 = request; + m.m2_p1 = argp; + + switch ((request >> 8) & 0xFF) { + case 'M': + case 'S': + /* MM handles calls for itself and the kernel. */ + return _syscall(MM, SVRCTL, &m); + case 'F': + case 'I': + /* FS handles calls for itself and inet. */ + return _syscall(FS, SVRCTL, &m); + default: + errno = EINVAL; + return -1; + } +} diff --git a/lib/other/asynchio.c b/lib/other/asynchio.c new file mode 100755 index 000000000..aed3ecb1d --- /dev/null +++ b/lib/other/asynchio.c @@ -0,0 +1,154 @@ +/* asyn_init(), asyn_read(), asyn_write(), asyn_ioctl(), + * asyn_wait(), asyn_synch(), asyn_close() + * Author: Kees J. Bot + * 26 Jan 1995 + * Thise are just stub routines that are call compatible with + * the asynchio(3) library of Minix-vmd. See asynchio.h. + */ +#define nil 0 +#define alarm _alarm +#define ioctl _ioctl +#define read _read +#define sigaction _sigaction +#define sigfillset _sigfillset +#define time _time +#define write _write +#include <lib.h> +#include <sys/ioctl.h> +#include <sys/asynchio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <time.h> +#include <signal.h> + +#define IDLE 0 +#define INPROGRESS 1 +#define RESULT 2 + +#define OP_NOOP 0 +#define OP_READ 1 +#define OP_WRITE 2 +#define OP_IOCTL 3 + +static asynchio_t *asyn_current; + +void asyn_init(asynchio_t *asyn) +{ + asyn->state= IDLE; + asyn->op= OP_NOOP; +} + +static ssize_t operation(int op, asynchio_t *asyn, int fd, int req, + void *data, ssize_t count) +{ + switch (asyn->state) { + case INPROGRESS: + if (asyn_current != asyn && asyn->op != op) abort(); + /*FALL THROUGH*/ + case IDLE: + asyn_current= asyn; + asyn->op= op; + asyn->fd= fd; + asyn->req= req; + asyn->data= data; + asyn->count= count; + asyn->state= INPROGRESS; + errno= EINPROGRESS; + return -1; + case RESULT: + if (asyn_current != asyn && asyn->op != op) abort(); + errno= asyn->errno; + return asyn->count; + } +} + +ssize_t asyn_read(asynchio_t *asyn, int fd, void *buf, size_t len) +{ + return operation(OP_READ, asyn, fd, 0, buf, len); +} + +ssize_t asyn_write(asynchio_t *asyn, int fd, const void *buf, size_t len) +{ + return operation(OP_WRITE, asyn, fd, 0, (void *) buf, len); +} + +int asyn_ioctl(asynchio_t *asyn, int fd, unsigned long request, void *data) +{ + return operation(OP_IOCTL, asyn, fd, request, data, 0); +} + +static void time_out(int sig) +{ + alarm(1); +} + +int asyn_wait(asynchio_t *asyn, int flags, struct timeval *to) +{ + time_t now; + unsigned old_timer, new_timer; + struct sigaction old_sa, new_sa; + + if (asyn_current != asyn) abort(); + if (flags & ASYN_NONBLOCK) abort(); + + if (asyn->state == RESULT) { + asyn->state= IDLE; + asyn->op= OP_NOOP; + return 0; + } + + if (to != nil) { + now= time(nil); + if (to->tv_sec <= now) { errno= EINTR; return -1; } + old_timer= alarm(0); + new_sa.sa_handler= time_out; + sigfillset(&new_sa.sa_mask); + new_sa.sa_flags= 0; + sigaction(SIGALRM, &new_sa, &old_sa); + new_timer= to->tv_sec - now; + if (new_timer < old_timer) { + new_timer= old_timer; + } + alarm(new_timer); + } + switch (asyn->op) { + case OP_NOOP: + asyn->count= pause(); + asyn->errno= errno; + case OP_READ: + asyn->count= read(asyn->fd, asyn->data, asyn->count); + asyn->errno= errno; + break; + case OP_WRITE: + asyn->count= write(asyn->fd, asyn->data, asyn->count); + asyn->errno= errno; + break; + case OP_IOCTL: + asyn->count= ioctl(asyn->fd, asyn->req, asyn->data); + asyn->errno= errno; + break; + } + if (to != nil) { + alarm(0); + sigaction(SIGALRM, &old_sa, (struct sigaction *)0); + alarm(old_timer); + } + + if (asyn->count == -1 && asyn->errno == EINTR) { + errno= EINTR; + return -1; + } else { + asyn->state= RESULT; + return 0; + } +} + +int asyn_synch(asynchio_t *asyn, int fd) +{ +} + +int asyn_close(asynchio_t *asyn, int fd) +{ + asyn_init(asyn); +} diff --git a/lib/other/bcmp.c b/lib/other/bcmp.c new file mode 100755 index 000000000..e9b5f0f6b --- /dev/null +++ b/lib/other/bcmp.c @@ -0,0 +1,12 @@ +#include <lib.h> +/* bcmp - Berklix equivalent of memcmp */ + +#include <string.h> + +int bcmp(s1, s2, length) /* == 0 or != 0 for equality and inequality */ +_CONST void *s1; +_CONST void *s2; +size_t length; +{ + return(memcmp(s1, s2, length)); +} diff --git a/lib/other/bcopy.c b/lib/other/bcopy.c new file mode 100755 index 000000000..e8f4f7a07 --- /dev/null +++ b/lib/other/bcopy.c @@ -0,0 +1,12 @@ +#include <lib.h> +/* bcopy - Berklix equivalent of memcpy */ + +#include <string.h> + +void bcopy(src, dst, length) +_CONST void *src; +void *dst; +size_t length; +{ + (void) memcpy(dst, src, length); +} diff --git a/lib/other/bzero.c b/lib/other/bzero.c new file mode 100755 index 000000000..fc842499b --- /dev/null +++ b/lib/other/bzero.c @@ -0,0 +1,11 @@ +#include <lib.h> +/* bzero - Berklix subset of memset */ + +#include <string.h> + +void bzero(dst, length) +void *dst; +size_t length; +{ + (void) memset(dst, 0, length); +} diff --git a/lib/other/configfile.c b/lib/other/configfile.c new file mode 100755 index 000000000..4c6300fea --- /dev/null +++ b/lib/other/configfile.c @@ -0,0 +1,574 @@ +/* config_read(), _delete(), _length() - Generic config file routines. + * Author: Kees J. Bot + * 5 Jun 1999 + */ +#define nil ((void*)0) +#if __minix_vmd +#include <minix/stubs.h> +#else +#define fstat _fstat +#define stat _stat +#endif +#include <sys/types.h> +#include <stdio.h> +#include <stddef.h> +#include <stdlib.h> +#include <errno.h> +#include <string.h> +#include <time.h> +#include <sys/stat.h> +#if __minix_vmd +#include <minix/asciictype.h> +#else +#include <ctype.h> +#endif +#define _c /* not const */ +#include <configfile.h> + +typedef struct configfile { /* List of (included) configuration files. */ + struct configfile *next; /* A list indeed. */ + time_t ctime; /* Last changed time, -1 if no file. */ + char name[1]; /* File name. */ +} configfile_t; + +/* Size of a configfile_t given a file name of length 'len'. */ +#define configfilesize(len) (offsetof(configfile_t, name) + 1 + (len)) + +typedef struct firstconfig { /* First file and first word share a slot. */ + configfile_t *filelist; + char new; /* Set when created. */ + config_t config1; +} firstconfig_t; + +/* Size of a config_t given a word of lenght 'len'. Same for firstconfig_t. */ +#define config0size() (offsetof(config_t, word)) +#define configsize(len) (config0size() + 1 + (len)) +#define firstconfigsize(len) \ + (offsetof(firstconfig_t, config1) + configsize(len)) + +/* Translate address of first config word to enclosing firstconfig_t and vv. */ +#define cfg2fcfg(p) \ + ((firstconfig_t *) ((char *) (p) - offsetof(firstconfig_t, config1))) +#define fcfg2cfg(p) (&(p)->config1) + +/* Variables used while building data. */ +static configfile_t *c_files; /* List of (included) config files. */ +static int c_flags; /* Flags argument of config_read(). */ +static FILE *c_fp; /* Current open file. */ +static char *c_file; /* Current open file name. */ +static unsigned c_line; /* Current line number. */ +static int c; /* Next character. */ + +static void *allocate(void *mem, size_t size) +/* Like realloc(), but checked. */ +{ + if ((mem= realloc(mem, size)) == nil) { + fprintf(stderr, "\"%s\", line %u: Out of memory\n", c_file, c_line); + exit(1); + } + return mem; +} + +#define deallocate(mem) free(mem) + +static void delete_filelist(configfile_t *cfgf) +/* Delete configuration file list. */ +{ + void *junk; + + while (cfgf != nil) { + junk= cfgf; + cfgf= cfgf->next; + deallocate(junk); + } +} + +static void delete_config(config_t *cfg) +/* Delete configuration file data. */ +{ + config_t *next, *list, *junk; + + next= cfg; + list= nil; + for (;;) { + if (next != nil) { + /* Push the 'next' chain in reverse on the 'list' chain, putting + * a leaf cell (next == nil) on top of 'list'. + */ + junk= next; + next= next->next; + junk->next= list; + list= junk; + } else + if (list != nil) { + /* Delete the leaf cell. If it has a sublist then that becomes + * the 'next' chain. + */ + junk= list; + next= list->list; + list= list->next; + deallocate(junk); + } else { + /* Both chains are gone. */ + break; + } + } +} + +void config_delete(config_t *cfg1) +/* Delete configuration file data, being careful with the odd first one. */ +{ + firstconfig_t *fcfg= cfg2fcfg(cfg1); + + delete_filelist(fcfg->filelist); + delete_config(fcfg->config1.next); + delete_config(fcfg->config1.list); + deallocate(fcfg); +} + +static void nextc(void) +/* Read the next character of the current file into 'c'. */ +{ + if (c == '\n') c_line++; + c= getc(c_fp); + if (c == EOF && ferror(c_fp)) { + fprintf(stderr, "\"%s\", line %u: %s\n", + c_file, c_line, strerror(errno)); + exit(1); + } +} + +static void skipwhite(void) +/* Skip whitespace and comments. */ +{ + while (isspace(c)) { + nextc(); + if (c == '#') { + do nextc(); while (c != EOF && c != '\n'); + } + } +} + +static void parse_err(void) +/* Tell user that you can't parse past the current character. */ +{ + char sc[2]; + + sc[0]= c; + sc[1]= 0; + fprintf(stderr, "\"%s\", line %u: parse error at '%s'\n", + c_file, c_line, c == EOF ? "EOF" : sc); + exit(1); +} + +static config_t *read_word(void) +/* Read a word or string. */ +{ + config_t *w; + size_t i, len; + int q; + static char SPECIAL[] = "!#$%&*+-./:<=>?[\\]^_|~"; + + i= 0; + len= 32; + w= allocate(nil, configsize(32)); + w->next= nil; + w->list= nil; + w->file= c_file; + w->line= c_line; + w->flags= 0; + + /* Is it a quoted string? */ + if (c == '\'' || c == '"') { + q= c; /* yes */ + nextc(); + } else { + q= -1; /* no */ + } + + for (;;) { + if (i == len) { + len+= 32; + w= allocate(w, configsize(len)); + } + + if (q == -1) { + /* A word consists of letters, numbers and a few special chars. */ + if (!isalnum(c) && c < 0x80 && strchr(SPECIAL, c) == nil) break; + } else { + /* Strings are made up of anything except newlines. */ + if (c == EOF || c == '\n') { + fprintf(stderr, + "\"%s\", line %u: string at line %u not closed\n", + c_file, c_line, w->line); + exit(1); + break; + } + if (c == q) { /* Closing quote? */ + nextc(); + break; + } + } + + if (c != '\\') { /* Simply add non-escapes. */ + w->word[i++]= c; + nextc(); + } else { /* Interpret an escape. */ + nextc(); + if (isspace(c)) { + skipwhite(); + continue; + } + + if (c_flags & CFG_ESCAPED) { + w->word[i++]= '\\'; /* Keep the \ for the caller. */ + if (i == len) { + len+= 32; + w= allocate(w, configsize(len)); + } + w->flags |= CFG_ESCAPED; + } + + if (isdigit(c)) { /* Octal escape */ + int n= 3; + int d= 0; + + do { + d= d * 010 + (c - '0'); + nextc(); + } while (--n > 0 && isdigit(c)); + w->word[i++]= d; + } else + if (c == 'x' || c == 'X') { /* Hex escape */ + int n= 2; + int d= 0; + + nextc(); + if (!isxdigit(c)) { + fprintf(stderr, "\"%s\", line %u: bad hex escape\n", + c_file, c_line); + exit(1); + } + do { + d= d * 0x10 + (islower(c) ? (c - 'a' + 0xa) : + isupper(c) ? (c - 'A' + 0xA) : + (c - '0')); + nextc(); + } while (--n > 0 && isxdigit(c)); + w->word[i++]= d; + } else { + switch (c) { + case 'a': c= '\a'; break; + case 'b': c= '\b'; break; + case 'e': c= '\033'; break; + case 'f': c= '\f'; break; + case 'n': c= '\n'; break; + case 'r': c= '\r'; break; + case 's': c= ' '; break; + case 't': c= '\t'; break; + case 'v': c= '\v'; break; + default: /* Anything else is kept as-is. */; + } + w->word[i++]= c; + nextc(); + } + } + } + w->word[i]= 0; + if (q != -1) { + w->flags |= CFG_STRING; + } else { + int f; + char *end; + static char base[]= { 0, 010, 10, 0x10 }; + + if (i == 0) parse_err(); + + /* Can the word be used as a number? */ + for (f= 0; f < 4; f++) { + (void) strtol(w->word, &end, base[f]); + if (*end == 0) w->flags |= 1 << (f + 0); + (void) strtoul(w->word, &end, base[f]); + if (*end == 0) w->flags |= 1 << (f + 4); + } + } + return allocate(w, configsize(i)); +} + +static config_t *read_file(const char *file); +static config_t *read_list(void); + +static config_t *read_line(void) +/* Read and return one line of the config file. */ +{ + config_t *cline, **pcline, *clist; + + cline= nil; + pcline= &cline; + + for (;;) { + skipwhite(); + + if (c == EOF || c == '}') { +if(0) if (cline != nil) parse_err(); + break; + } else + if (c == ';') { + nextc(); + if (cline != nil) break; + } else + if (cline != nil && c == '{') { + /* A sublist. */ + nextc(); + clist= allocate(nil, config0size()); + clist->next= nil; + clist->file= c_file; + clist->line= c_line; + clist->list= read_list(); + clist->flags= CFG_SUBLIST; + *pcline= clist; + pcline= &clist->next; + if (c != '}') parse_err(); + nextc(); + } else { + *pcline= read_word(); + pcline= &(*pcline)->next; + } + } + return cline; +} + +static config_t *read_list(void) +/* Read and return a list of config file commands. */ +{ + config_t *clist, **pclist, *cline; + + clist= nil; + pclist= &clist; + + while ((cline= read_line()) != nil) { + if (strcmp(cline->word, "include") == 0) { + config_t *file= cline->next; + if (file == nil || file->next != nil || !config_isatom(file)) { + fprintf(stderr, + "\"%s\", line %u: 'include' command requires an argument\n", + c_file, cline->line); + exit(1); + } + if (file->flags & CFG_ESCAPED) { + char *p, *q; + p= q= file->word; + for (;;) { + if ((*q = *p) == '\\') *q = *++p; + if (*q == 0) break; + p++; + q++; + } + } + file= read_file(file->word); + delete_config(cline); + *pclist= file; + while (*pclist != nil) pclist= &(*pclist)->next; + } else { + config_t *cfg= allocate(nil, config0size()); + cfg->next= nil; + cfg->list= cline; + cfg->file= cline->file; + cfg->line= cline->line; + cfg->flags= CFG_SUBLIST; + *pclist= cfg; + pclist= &cfg->next; + } + } + return clist; +} + +static config_t *read_file(const char *file) +/* Read and return a configuration file. */ +{ + configfile_t *cfgf; + config_t *cfg; + struct stat st; + FILE *old_fp; /* old_* variables store current file context. */ + char *old_file; + unsigned old_line; + int old_c; + size_t n; + char *slash; + + old_fp= c_fp; + old_file= c_file; + old_line= c_line; + old_c= c; + + n= 0; + if (file[0] != '/' && old_file != nil + && (slash= strrchr(old_file, '/')) != nil) { + n= slash - old_file + 1; + } + cfgf= allocate(nil, configfilesize(n + strlen(file))); + memcpy(cfgf->name, old_file, n); + strcpy(cfgf->name + n, file); + cfgf->next= c_files; + c_files= cfgf; + + c_file= cfgf->name; + c_line= 0; + + if ((c_fp= fopen(file, "r")) == nil || fstat(fileno(c_fp), &st) < 0) { + if (errno != ENOENT) { + fprintf(stderr, "\"%s\", line 1: %s\n", file, strerror(errno)); + exit(1); + } + cfgf->ctime= -1; + c= EOF; + } else { + cfgf->ctime= st.st_ctime; + c= '\n'; + } + + cfg= read_list(); + if (c != EOF) parse_err(); + + if (c_fp != nil) fclose(c_fp); + c_fp= old_fp; + c_file= old_file; + c_line= old_line; + c= old_c; + return cfg; +} + +config_t *config_read(const char *file, int flags, config_t *cfg) +/* Read and parse a configuration file. */ +{ + if (cfg != nil) { + /* First check if any of the involved files has changed. */ + firstconfig_t *fcfg; + configfile_t *cfgf; + struct stat st; + + fcfg= cfg2fcfg(cfg); + for (cfgf= fcfg->filelist; cfgf != nil; cfgf= cfgf->next) { + if (stat(cfgf->name, &st) < 0) { + if (errno != ENOENT) break; + st.st_ctime= -1; + } + if (st.st_ctime != cfgf->ctime) break; + } + + if (cfgf == nil) return cfg; /* Everything as it was. */ + config_delete(cfg); /* Otherwise delete and reread. */ + } + + errno= 0; + c_files= nil; + c_flags= flags; + cfg= read_file(file); + + if (cfg != nil) { + /* Change first word to have a hidden pointer to a file list. */ + size_t len= strlen(cfg->word); + firstconfig_t *fcfg; + + fcfg= allocate(cfg, firstconfigsize(len)); + memmove(&fcfg->config1, fcfg, configsize(len)); + fcfg->filelist= c_files; + fcfg->new= 1; + return fcfg2cfg(fcfg); + } + /* Couldn't read (errno != 0) of nothing read (errno == 0). */ + delete_filelist(c_files); + delete_config(cfg); + return nil; +} + +int config_renewed(config_t *cfg) +{ + int new; + + if (cfg == nil) { + new= 1; + } else { + new= cfg2fcfg(cfg)->new; + cfg2fcfg(cfg)->new= 0; + } + return new; +} + +size_t config_length(config_t *cfg) +/* Count the number of items on a list. */ +{ + size_t n= 0; + + while (cfg != nil) { + n++; + cfg= cfg->next; + } + return n; +} + +#if TEST +#include <unistd.h> + +static void print_list(int indent, config_t *cfg); + +static void print_words(int indent, config_t *cfg) +{ + while (cfg != nil) { + if (config_isatom(cfg)) { + if (config_isstring(cfg)) fputc('"', stdout); + printf("%s", cfg->word); + if (config_isstring(cfg)) fputc('"', stdout); + } else { + printf("{\n"); + print_list(indent+4, cfg->list); + printf("%*s}", indent, ""); + } + cfg= cfg->next; + if (cfg != nil) fputc(' ', stdout); + } + printf(";\n"); +} + +static void print_list(int indent, config_t *cfg) +{ + while (cfg != nil) { + if (!config_issub(cfg)) { + fprintf(stderr, "Cell at \"%s\", line %u is not a sublist\n"); + break; + } + printf("%*s", indent, ""); + print_words(indent, cfg->list); + cfg= cfg->next; + } +} + +static void print_config(config_t *cfg) +{ + if (!config_renewed(cfg)) { + printf("# Config didn't change\n"); + } else { + print_list(0, cfg); + } +} + +int main(int argc, char **argv) +{ + config_t *cfg; + int c; + + if (argc != 2) { + fprintf(stderr, "One config file name please\n"); + exit(1); + } + + cfg= nil; + do { + cfg= config_read(argv[1], CFG_ESCAPED, cfg); + print_config(cfg); + if (!isatty(0)) break; + while ((c= getchar()) != EOF && c != '\n') {} + } while (c != EOF); + return 0; +} +#endif /* TEST */ diff --git a/lib/other/crypt.c b/lib/other/crypt.c new file mode 100755 index 000000000..a82b3b0c2 --- /dev/null +++ b/lib/other/crypt.c @@ -0,0 +1,115 @@ +/* crypt() - one-way password encryption function Author: Kees J. Bot + * 7 Feb 1994 + * This routine does not encrypt anything, it uses the pwdauth + * program to do the hard work. + */ +#define nil ((void*)0) +#define pipe _pipe +#define fork _fork +#define close _close +#define dup2 _dup2 +#define execl _execl +#define read _read +#define _exit __exit +#define write _write +#define waitpid _waitpid +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> +#include <errno.h> +#include <stdarg.h> +#include <sys/wait.h> + +/* Set-uid root program to read /etc/shadow or encrypt passwords. */ +static char PWDAUTH[] = "/usr/lib/pwdauth"; +#define LEN 1024 + +static void tell(const char *s0, ...) +{ + va_list ap; + const char *s; + + va_start(ap, s0); + s= s0; + while (s != nil) { + (void) write(2, s, strlen(s)); + s= va_arg(ap, const char *); + } + va_end(ap); +} + +char *crypt(const char *key, const char *salt) +{ + pid_t pid; + int status; + int pfd[2]; + static char pwdata[LEN]; + char *p= pwdata; + const char *k= key; + const char *s= salt; + int n; + + /* Fill pwdata[] with the key and salt. */ + while ((*p++ = *k++) != 0) if (p == pwdata+LEN-1) goto fail; + while ((*p++ = *s++) != 0) if (p == pwdata+LEN-0) goto fail; + + if (pipe(pfd) < 0) goto fail; + + /* Prefill the pipe. */ + (void) write(pfd[1], pwdata, p - pwdata); + + switch ((pid= fork())) { + case -1: + close(pfd[0]); + close(pfd[1]); + goto fail; + case 0: + /* Connect both input and output to the pipe. */ + if (pfd[0] != 0) { + dup2(pfd[0], 0); + close(pfd[0]); + } + if (pfd[1] != 1) { + dup2(pfd[1], 1); + close(pfd[1]); + } + + execl(PWDAUTH, PWDAUTH, (char *) nil); + + tell("crypt(): ", PWDAUTH, ": ", strerror(errno), "\r\n", + (char *) nil); + /* No pwdauth? Fail! */ + (void) read(0, pwdata, LEN); + _exit(1); + } + close(pfd[1]); + + status= -1; + while (waitpid(pid, &status, 0) == -1 && errno == EINTR) {} + if (status != 0) { + close(pfd[0]); + goto fail; + } + + /* Read and return the result. Check if it contains exactly one + * string. + */ + n= read(pfd[0], pwdata, LEN); + close(pfd[0]); + if (n < 0) goto fail; + p = pwdata + n; + n = 0; + while (p > pwdata) if (*--p == 0) n++; + if (n != 1) goto fail; + return pwdata; + +fail: + pwdata[0] = salt[0] ^ 1; /* make result != salt */ + pwdata[1] = 0; + return pwdata; +} + +/* + * $PchId: crypt.c,v 1.5 1996/04/11 07:46:11 philip Exp $ + */ diff --git a/lib/other/ctermid.c b/lib/other/ctermid.c new file mode 100755 index 000000000..8d92d9ef6 --- /dev/null +++ b/lib/other/ctermid.c @@ -0,0 +1,32 @@ +/* ctermid(3) + * + * Author: Terrence Holm Aug. 1988 + * + * + * Ctermid(3) returns a pointer to a string naming the controlling + * terminal. If <name_space> is NULL then local PRIVATE storage + * is used, otherwise <name_space> must point to storage of at + * least L_ctermid characters. + * + * Returns a pointer to "/dev/tty". + */ + +#include <lib.h> +#include <string.h> +#include <stdio.h> + +_PROTOTYPE( char *ctermid, (char *name_space)); + +#ifndef L_ctermid +#define L_ctermid 9 +#endif + +char *ctermid(name_space) +char *name_space; +{ + PRIVATE char termid[L_ctermid]; + + if (name_space == (char *)NULL) name_space = termid; + strcpy(name_space, "/dev/tty"); + return(name_space); +} diff --git a/lib/other/cuserid.c b/lib/other/cuserid.c new file mode 100755 index 000000000..47640e751 --- /dev/null +++ b/lib/other/cuserid.c @@ -0,0 +1,33 @@ +/* cuserid(3) + * + * Author: Terrence W. Holm Sept. 1987 + */ + +#include <lib.h> +#include <pwd.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +#ifndef L_cuserid +#define L_cuserid 9 +#endif + +char *cuserid(user_name) +char *user_name; +{ + PRIVATE char userid[L_cuserid]; + struct passwd *pw_entry; + + if (user_name == (char *)NULL) user_name = userid; + + pw_entry = getpwuid(geteuid()); + + if (pw_entry == (struct passwd *)NULL) { + *user_name = '\0'; + return((char *)NULL); + } + strcpy(user_name, pw_entry->pw_name); + + return(user_name); +} diff --git a/lib/other/environ.c b/lib/other/environ.c new file mode 100755 index 000000000..7e7fe73b0 --- /dev/null +++ b/lib/other/environ.c @@ -0,0 +1,20 @@ +/* + * environ.c - define the variable environ + */ +/* $Header$ */ +/* + * This file defines the variable environ and initializes it with a magic + * value. The C run-time start-off routine tests whether the variable + * environ is initialized with this value. If it is not, it is assumed + * that it is defined by the user. Only two bytes are tested, since we + * don't know the endian-ness and alignment restrictions of the machine. + * This means that the low-order two-bytes should be equal to the + * high-order two-bytes on machines with four-byte pointers. In fact, all + * the bytes in the pointer are the same, just in case. + */ + +#if _EM_PSIZE==2 +char **environ = (char **) 0x5353; +#else +char **environ = (char **) 0x53535353; +#endif diff --git a/lib/other/errno.c b/lib/other/errno.c new file mode 100755 index 000000000..538fd572f --- /dev/null +++ b/lib/other/errno.c @@ -0,0 +1,4 @@ +#include <lib.h> +/* errno.c - declare variable errno Author: F. Meulenbroeks */ + +int errno = 0; diff --git a/lib/other/fdopen.c b/lib/other/fdopen.c new file mode 100755 index 000000000..9842fd941 --- /dev/null +++ b/lib/other/fdopen.c @@ -0,0 +1,63 @@ +/* + * fdopen - convert a (UNIX) file descriptor into a FILE pointer + */ +/* $Header$ */ + +#include <stdlib.h> +#include "../stdio/loc_incl.h" +#include <stdio.h> + +FILE * +fdopen(fd, mode) +int fd; +_CONST char *mode; +{ + register int i; + FILE *stream; + int flags = 0; + + if (fd < 0) return (FILE *)NULL; + for (i = 0; __iotab[i] != 0 ; i++) + if (i >= FOPEN_MAX-1) + return (FILE *)NULL; + + switch(*mode++) { + case 'r': + flags |= _IOREAD | _IOREADING; + break; + case 'a': + flags |= _IOAPPEND; + case 'w': + flags |= _IOWRITE | _IOWRITING; + break; + default: + return (FILE *)NULL; + } + while(*mode) { + switch(*mode++) { + case 'b': + continue; + case '+': + flags |= _IOREAD | _IOWRITE; + continue; + /* The sequence may be followed by aditional characters */ + default: + break; + } + break; + } + + if ((stream = (FILE *) malloc(sizeof(FILE))) == NULL) { + return (FILE *)NULL; + } + + if ((flags & _IOREAD) && (flags & _IOWRITE)) + flags &= ~(_IOREADING | _IOWRITING); + + stream->_count = 0; + stream->_fd = fd; + stream->_flags = flags; + stream->_buf = NULL; + __iotab[i] = stream; + return stream; +} diff --git a/lib/other/ffs.c b/lib/other/ffs.c new file mode 100755 index 000000000..bae6947a8 --- /dev/null +++ b/lib/other/ffs.c @@ -0,0 +1,17 @@ +#include <lib.h> +/* ffs(3) + * + * Author: Terrence W. Holm Sep. 1988 + */ +_PROTOTYPE( int ffs, (int word)); + +int ffs(word) +int word; +{ + int i; + + if (word == 0) return(0); + + for (i = 1;; ++i, word >>= 1) + if (word & 1) return(i); +} diff --git a/lib/other/fslib.c b/lib/other/fslib.c new file mode 100755 index 000000000..a3572c064 --- /dev/null +++ b/lib/other/fslib.c @@ -0,0 +1,191 @@ +/* fslib.c - routines needed by fs and fs utilities */ + +#include <minix/config.h> /* for unused stuff in <minix/type.h> :-( */ +#include <ansi.h> +#include <limits.h> +#include <dirent.h> +#include <sys/types.h> +#include <minix/const.h> +#include <minix/type.h> /* for unshort :-( */ +#include "fs/const.h" /* depends of -I flag in Makefile */ +#include "fs/type.h" /* ditto */ +#include "fs/inode.h" /* ditto */ +#include "fs/super.h" +#include <minix/fslib.h> + +/* The next routine is copied from fsck.c and mkfs.c... (Re)define some + * things for consistency. Some things should be done better. + */ + +/* Convert from bit count to a block count. The usual expression + * + * (nr_bits + (1 << BITMAPSHIFT) - 1) >> BITMAPSHIFT + * + * doesn't work because of overflow. + * + * Other overflow bugs, such as the expression for N_ILIST overflowing when + * s_inodes is just over V*_INODES_PER_BLOCK less than the maximum+1, are not + * fixed yet, because that number of inodes is silly. + */ +/* The above comment doesn't all apply now bit_t is long. Overflow is now + * unlikely, but negative bit counts are now possible (though unlikely) + * and give silly results. + */ +PUBLIC int bitmapsize(nr_bits, block_size) +bit_t nr_bits; +int block_size; +{ + int nr_blocks; + + nr_blocks = (int) (nr_bits / BITS_PER_BLOCK(block_size)); + if (((bit_t) nr_blocks * BITS_PER_BLOCK(block_size)) < nr_bits) ++nr_blocks; + return(nr_blocks); +} + + +/*===========================================================================* + * conv2 * + *===========================================================================*/ +PUBLIC unsigned conv2(norm, w) +int norm; /* TRUE if no swap, FALSE for byte swap */ +int w; /* promotion of 16-bit word to be swapped */ +{ +/* Possibly swap a 16-bit word between 8086 and 68000 byte order. */ + + if (norm) return( (unsigned) w & 0xFFFF); + return( ((w&BYTE) << 8) | ( (w>>8) & BYTE)); +} + + +/*===========================================================================* + * conv4 * + *===========================================================================*/ +PUBLIC long conv4(norm, x) +int norm; /* TRUE if no swap, FALSE for byte swap */ +long x; /* 32-bit long to be byte swapped */ +{ +/* Possibly swap a 32-bit long between 8086 and 68000 byte order. */ + + unsigned lo, hi; + long l; + + if (norm) return(x); /* byte order was already ok */ + lo = conv2(FALSE, (int) x & 0xFFFF); /* low-order half, byte swapped */ + hi = conv2(FALSE, (int) (x>>16) & 0xFFFF); /* high-order half, swapped */ + l = ( (long) lo <<16) | hi; + return(l); +} + + +/*===========================================================================* + * conv_inode * + *===========================================================================*/ +PUBLIC void conv_inode(rip, dip, dip2, rw_flag, magic) +register struct inode *rip; /* pointer to the in-core inode struct */ +register d1_inode *dip; /* pointer to the V1 on-disk inode struct */ +register d2_inode *dip2; /* pointer to the V2 on-disk inode struct */ +int rw_flag; /* READING or WRITING */ +int magic; /* magic number of file system */ +{ +/* Copy the inode from the disk block to the in-core table or vice versa. + * If the fourth parameter below is FALSE, the bytes are swapped. + */ + switch (magic) { + case SUPER_MAGIC: old_icopy(rip, dip, rw_flag, TRUE); break; + case SUPER_REV: old_icopy(rip, dip, rw_flag, FALSE); break; + case SUPER_V3: + case SUPER_V2: new_icopy(rip, dip2, rw_flag, TRUE); break; + case SUPER_V2_REV: new_icopy(rip, dip2, rw_flag, FALSE); break; + } +} + + +/*===========================================================================* + * old_icopy * + *===========================================================================*/ +PUBLIC void old_icopy(rip, dip, direction, norm) +register struct inode *rip; /* pointer to the in-core inode struct */ +register d1_inode *dip; /* pointer to the d1_inode inode struct */ +int direction; /* READING (from disk) or WRITING (to disk) */ +int norm; /* TRUE = do not swap bytes; FALSE = swap */ + +{ +/* 4 different on-disk inode layouts are supported, one for each combination + * of V1.x/V2.x * bytes-swapped/not-swapped. When an inode is read or written + * this routine handles the conversions so that the information in the inode + * table is independent of the disk structure from which the inode came. + * The old_icopy routine copies to and from V1 disks. + */ + + int i; + + if (direction == READING) { + /* Copy V1.x inode to the in-core table, swapping bytes if need be. */ + rip->i_mode = conv2(norm, dip->d1_mode); + rip->i_uid = conv2(norm,dip->d1_uid ); + rip->i_size = conv4(norm,dip->d1_size); + rip->i_mtime = conv4(norm,dip->d1_mtime); + rip->i_atime = 0; + rip->i_ctime = 0; + rip->i_nlinks = (nlink_t) dip->d1_nlinks; /* 1 char */ + rip->i_gid = (gid_t) dip->d1_gid; /* 1 char */ + rip->i_ndzones = V1_NR_DZONES; + rip->i_nindirs = V1_INDIRECTS; + for (i = 0; i < V1_NR_TZONES; i++) + rip->i_zone[i] = conv2(norm, (int) dip->d1_zone[i]); + } else { + /* Copying V1.x inode to disk from the in-core table. */ + dip->d1_mode = conv2(norm,rip->i_mode); + dip->d1_uid = conv2(norm,rip->i_uid ); + dip->d1_size = conv4(norm,rip->i_size); + dip->d1_mtime = conv4(norm,rip->i_mtime); + dip->d1_nlinks = (nlink_t) rip->i_nlinks; /* 1 char */ + dip->d1_gid = (gid_t) rip->i_gid; /* 1 char */ + for (i = 0; i < V1_NR_TZONES; i++) + dip->d1_zone[i] = conv2(norm, (int) rip->i_zone[i]); + } +} + + +/*===========================================================================* + * new_icopy * + *===========================================================================*/ +PUBLIC void new_icopy(rip, dip, direction, norm) +register struct inode *rip; /* pointer to the in-core inode struct */ +register d2_inode *dip; /* pointer to the d2_inode struct */ +int direction; /* READING (from disk) or WRITING (to disk) */ +int norm; /* TRUE = do not swap bytes; FALSE = swap */ + +{ +/* Same as old_icopy, but to/from V2 disk layout. */ + + int i; + + if (direction == READING) { + /* Copy V2.x inode to the in-core table, swapping bytes if need be. */ + rip->i_mode = conv2(norm,dip->d2_mode); + rip->i_uid = conv2(norm,dip->d2_uid ); + rip->i_nlinks = conv2(norm,(int) dip->d2_nlinks); + rip->i_gid = conv2(norm,(int) dip->d2_gid ); + rip->i_size = conv4(norm,dip->d2_size); + rip->i_atime = conv4(norm,dip->d2_atime); + rip->i_ctime = conv4(norm,dip->d2_ctime); + rip->i_mtime = conv4(norm,dip->d2_mtime); + rip->i_ndzones = V2_NR_DZONES; + rip->i_nindirs = V2_INDIRECTS(rip->i_sp->s_block_size); + for (i = 0; i < V2_NR_TZONES; i++) + rip->i_zone[i] = conv4(norm, (long) dip->d2_zone[i]); + } else { + /* Copying V2.x inode to disk from the in-core table. */ + dip->d2_mode = conv2(norm,rip->i_mode); + dip->d2_uid = conv2(norm,rip->i_uid ); + dip->d2_nlinks = conv2(norm,rip->i_nlinks); + dip->d2_gid = conv2(norm,rip->i_gid ); + dip->d2_size = conv4(norm,rip->i_size); + dip->d2_atime = conv4(norm,rip->i_atime); + dip->d2_ctime = conv4(norm,rip->i_ctime); + dip->d2_mtime = conv4(norm,rip->i_mtime); + for (i = 0; i < V2_NR_TZONES; i++) + dip->d2_zone[i] = conv4(norm, (long) rip->i_zone[i]); + } +} diff --git a/lib/other/fsversion.c b/lib/other/fsversion.c new file mode 100755 index 000000000..6e74d0d4e --- /dev/null +++ b/lib/other/fsversion.c @@ -0,0 +1,52 @@ +/* This procedure examines a file system and figures out whether it is + * version 1 or version 2. It returns the result as an int. If the + * file system is neither, it returns -1. A typical call is: + * + * n = fsversion("/dev/hd1", "df"); + * + * The first argument is the special file for the file system. + * The second is the program name, which is used in error messages. + */ + +#include <sys/types.h> +#include <minix/config.h> +#include <minix/const.h> +#include <minix/minlib.h> +#include <minix/type.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> + +#include "fs/const.h" +#include "fs/type.h" +#include "fs/super.h" + +static struct super_block super, *sp; + +int fsversion(dev, prog) +char *dev, *prog; +{ + int fd; + + if ((fd = open(dev, O_RDONLY)) < 0) { + std_err(prog); + std_err(" cannot open "); + perror(dev); + return(-1); + } + + lseek(fd, (off_t) SUPER_BLOCK_BYTES, SEEK_SET); /* skip boot block */ + if (read(fd, (char *) &super, (unsigned) SUPER_SIZE) != SUPER_SIZE) { + std_err(prog); + std_err(" cannot read super block on "); + perror(dev); + close(fd); + return(-1); + } + close(fd); + sp = &super; + if (sp->s_magic == SUPER_MAGIC) return(1); + if (sp->s_magic == SUPER_V2) return(2); + if (sp->s_magic == SUPER_V3) return(3); + return(-1); +} diff --git a/lib/other/getgrent.c b/lib/other/getgrent.c new file mode 100755 index 000000000..2ee524bbc --- /dev/null +++ b/lib/other/getgrent.c @@ -0,0 +1,150 @@ +/* getgrent(), getgrgid(), getgrnam() - group file routines + * + * Author: Kees J. Bot + * 31 Jan 1994 + */ +#define nil 0 +#define open _open +#define fcntl _fcntl +#define read _read +#define close _close +#include <sys/types.h> +#include <grp.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +static char GROUP[]= "/etc/group"; /* The group file. */ +static const char *grfile; /* Current group file. */ + +static char buf[1024]; /* Read buffer. */ +static char grline[512]; /* One line from the group file. */ +static struct group entry; /* Entry to fill and return. */ +static char *members[64]; /* Group members with the entry. */ +static int grfd= -1; /* Filedescriptor to the file. */ +static char *bufptr; /* Place in buf. */ +static ssize_t buflen= 0; /* Remaining characters in buf. */ +static char *lineptr; /* Place in the line. */ + +void endgrent(void) +/* Close the group file. */ +{ + if (grfd >= 0) { + (void) close(grfd); + grfd= -1; + buflen= 0; + } +} + +int setgrent(void) +/* Open the group file. */ +{ + if (grfd >= 0) endgrent(); + + if (grfile == nil) grfile= GROUP; + + if ((grfd= open(grfile, O_RDONLY)) < 0) return -1; + (void) fcntl(grfd, F_SETFD, fcntl(grfd, F_GETFD) | FD_CLOEXEC); + return 0; +} + +void setgrfile(const char *file) +/* Prepare for reading an alternate group file. */ +{ + endgrent(); + grfile= file; +} + +static int getline(void) +/* Get one line from the group file, return 0 if bad or EOF. */ +{ + lineptr= grline; + + do { + if (buflen == 0) { + if ((buflen= read(grfd, buf, sizeof(buf))) <= 0) + return 0; + bufptr= buf; + } + + if (lineptr == arraylimit(grline)) return 0; + buflen--; + } while ((*lineptr++ = *bufptr++) != '\n'); + + lineptr= grline; + return 1; +} + +static char *scan_punct(int punct) +/* Scan for a field separator in a line, return the start of the field. */ +{ + char *field= lineptr; + char *last; + + for (;;) { + last= lineptr; + if (*lineptr == 0) return nil; + if (*lineptr == '\n') break; + if (*lineptr++ == punct) break; + if (lineptr[-1] == ':') return nil; /* :::,,,:,,,? */ + } + *last= 0; + return field; +} + +struct group *getgrent(void) +/* Read one entry from the group file. */ +{ + char *p; + char **mem; + + /* Open the file if not yet open. */ + if (grfd < 0 && setgrent() < 0) return nil; + + /* Until a good line is read. */ + for (;;) { + if (!getline()) return nil; /* EOF or corrupt. */ + + if ((entry.gr_name= scan_punct(':')) == nil) continue; + if ((entry.gr_passwd= scan_punct(':')) == nil) continue; + if ((p= scan_punct(':')) == nil) continue; + entry.gr_gid= strtol(p, nil, 0); + + entry.gr_mem= mem= members; + if (*lineptr != '\n') { + do { + if ((*mem= scan_punct(',')) == nil) goto again; + if (mem < arraylimit(members) - 1) mem++; + } while (*lineptr != 0); + } + *mem= nil; + return &entry; + again:; + } +} + +struct group *getgrgid(Gid_t gid) +/* Return the group file entry belonging to the user-id. */ +{ + struct group *gr; + + endgrent(); + while ((gr= getgrent()) != nil && gr->gr_gid != gid) {} + endgrent(); + return gr; +} + +struct group *getgrnam(const char *name) +/* Return the group file entry belonging to the user name. */ +{ + struct group *gr; + + endgrent(); + while ((gr= getgrent()) != nil && strcmp(gr->gr_name, name) != 0) {} + endgrent(); + return gr; +} diff --git a/lib/other/getlogin.c b/lib/other/getlogin.c new file mode 100755 index 000000000..fe0a34e45 --- /dev/null +++ b/lib/other/getlogin.c @@ -0,0 +1,28 @@ +/* getlogin(3) + * + * Author: Terrence W. Holm Aug. 1988 + */ + +#include <lib.h> +#include <pwd.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> + +#ifndef L_cuserid +#define L_cuserid 9 +#endif + +char *getlogin() +{ + PRIVATE char userid[L_cuserid]; + struct passwd *pw_entry; + + pw_entry = getpwuid(getuid()); + + if (pw_entry == (struct passwd *)NULL) return((char *)NULL); + + strcpy(userid, pw_entry->pw_name); + + return(userid); +} diff --git a/lib/other/getopt.c b/lib/other/getopt.c new file mode 100755 index 000000000..23c397f3c --- /dev/null +++ b/lib/other/getopt.c @@ -0,0 +1,66 @@ +/* + * getopt - parse command-line options + */ +/* $Header$ */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +#define ERR(s, c) if(opterr){\ + fputs(argv[0], stderr);\ + fputs(s, stderr);\ + fputc(c, stderr);\ + fputc('\n', stderr);} + +int opterr = 1; +int optind = 1; +int optopt; +char *optarg; + +int +getopt(argc, argv, opts) +int argc; +char **argv; +char *opts; +{ + static int sp = 1; + register c; + register char *cp; + + if (sp == 1) + if (optind >= argc || + argv[optind][0] != '-' || argv[optind][1] == '\0') + return EOF; + else if (!strcmp(argv[optind], "--")) { + optind++; + return EOF; + } + optopt = c = argv[optind][sp]; + if (c == ':' || (cp=strchr(opts, c)) == NULL) { + ERR (": illegal option -- ", c); + if (argv[optind][++sp] == '\0') { + optind++; + sp = 1; + } + return '?'; + } + if (*++cp == ':') { + if (argv[optind][sp+1] != '\0') + optarg = &argv[optind++][sp+1]; + else if (++optind >= argc) { + ERR (": option requires an argument -- ", c); + sp = 1; + return '?'; + } else + optarg = argv[optind++]; + sp = 1; + } else { + if (argv[optind][++sp] == '\0') { + sp = 1; + optind++; + } + optarg = NULL; + } + return c; +} diff --git a/lib/other/getpass.c b/lib/other/getpass.c new file mode 100755 index 000000000..0a9be0c64 --- /dev/null +++ b/lib/other/getpass.c @@ -0,0 +1,75 @@ +/* getpass() - read a password Author: Kees J. Bot + * Feb 16 1993 + */ +#define open _open +#define sigaction _sigaction +#define sigemptyset _sigemptyset +#define tcgetattr _tcgetattr +#define tcsetattr _tcsetattr +#define write _write +#define read _read +#define close _close +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <signal.h> +#include <termios.h> +#include <string.h> + +static int intr; + +static void catch(int sig) +{ + intr= 1; +} + +char *getpass(const char *prompt) +{ + struct sigaction osa, sa; + struct termios cooked, raw; + static char password[32+1]; + int fd, n= 0; + + /* Try to open the controlling terminal. */ + if ((fd= open("/dev/tty", O_RDONLY)) < 0) return NULL; + + /* Trap interrupts unless ignored. */ + intr= 0; + sigaction(SIGINT, NULL, &osa); + if (osa.sa_handler != SIG_IGN) { + sigemptyset(&sa.sa_mask); + sa.sa_flags= 0; + sa.sa_handler= catch; + sigaction(SIGINT, &sa, &osa); + } + + /* Set the terminal to non-echo mode. */ + tcgetattr(fd, &cooked); + raw= cooked; + raw.c_iflag|= ICRNL; + raw.c_lflag&= ~ECHO; + raw.c_lflag|= ECHONL; + raw.c_oflag|= OPOST | ONLCR; + tcsetattr(fd, TCSANOW, &raw); + + /* Print the prompt. (After setting non-echo!) */ + write(2, prompt, strlen(prompt)); + + /* Read the password, 32 characters max. */ + while (read(fd, password+n, 1) > 0) { + if (password[n] == '\n') break; + if (n < 32) n++; + } + password[n]= 0; + + /* Terminal back to cooked mode. */ + tcsetattr(fd, TCSANOW, &cooked); + + close(fd); + + /* Interrupt? */ + sigaction(SIGINT, &osa, NULL); + if (intr) raise(SIGINT); + + return password; +} diff --git a/lib/other/getpwent.c b/lib/other/getpwent.c new file mode 100755 index 000000000..b6c74b6d4 --- /dev/null +++ b/lib/other/getpwent.c @@ -0,0 +1,143 @@ +/* getpwent(), getpwuid(), getpwnam() - password file routines + * + * Author: Kees J. Bot + * 31 Jan 1994 + */ +#define nil 0 +#define open _open +#define fcntl _fcntl +#define read _read +#define close _close +#include <sys/types.h> +#include <pwd.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +static char PASSWD[]= "/etc/passwd"; /* The password file. */ +static const char *pwfile; /* Current password file. */ + +static char buf[1024]; /* Read buffer. */ +static char pwline[256]; /* One line from the password file. */ +static struct passwd entry; /* Entry to fill and return. */ +static int pwfd= -1; /* Filedescriptor to the file. */ +static char *bufptr; /* Place in buf. */ +static ssize_t buflen= 0; /* Remaining characters in buf. */ +static char *lineptr; /* Place in the line. */ + +void endpwent(void) +/* Close the password file. */ +{ + if (pwfd >= 0) { + (void) close(pwfd); + pwfd= -1; + buflen= 0; + } +} + +int setpwent(void) +/* Open the password file. */ +{ + if (pwfd >= 0) endpwent(); + + if (pwfile == nil) pwfile= PASSWD; + + if ((pwfd= open(pwfile, O_RDONLY)) < 0) return -1; + (void) fcntl(pwfd, F_SETFD, fcntl(pwfd, F_GETFD) | FD_CLOEXEC); + return 0; +} + +void setpwfile(const char *file) +/* Prepare for reading an alternate password file. */ +{ + endpwent(); + pwfile= file; +} + +static int getline(void) +/* Get one line from the password file, return 0 if bad or EOF. */ +{ + lineptr= pwline; + + do { + if (buflen == 0) { + if ((buflen= read(pwfd, buf, sizeof(buf))) <= 0) + return 0; + bufptr= buf; + } + + if (lineptr == arraylimit(pwline)) return 0; + buflen--; + } while ((*lineptr++ = *bufptr++) != '\n'); + + lineptr= pwline; + return 1; +} + +static char *scan_colon(void) +/* Scan for a field separator in a line, return the start of the field. */ +{ + char *field= lineptr; + char *last; + + for (;;) { + last= lineptr; + if (*lineptr == 0) return nil; + if (*lineptr == '\n') break; + if (*lineptr++ == ':') break; + } + *last= 0; + return field; +} + +struct passwd *getpwent(void) +/* Read one entry from the password file. */ +{ + char *p; + + /* Open the file if not yet open. */ + if (pwfd < 0 && setpwent() < 0) return nil; + + /* Until a good line is read. */ + for (;;) { + if (!getline()) return nil; /* EOF or corrupt. */ + + if ((entry.pw_name= scan_colon()) == nil) continue; + if ((entry.pw_passwd= scan_colon()) == nil) continue; + if ((p= scan_colon()) == nil) continue; + entry.pw_uid= strtol(p, nil, 0); + if ((p= scan_colon()) == nil) continue; + entry.pw_gid= strtol(p, nil, 0); + if ((entry.pw_gecos= scan_colon()) == nil) continue; + if ((entry.pw_dir= scan_colon()) == nil) continue; + if ((entry.pw_shell= scan_colon()) == nil) continue; + + if (*lineptr == 0) return &entry; + } +} + +struct passwd *getpwuid(Uid_t uid) +/* Return the password file entry belonging to the user-id. */ +{ + struct passwd *pw; + + endpwent(); + while ((pw= getpwent()) != nil && pw->pw_uid != uid) {} + endpwent(); + return pw; +} + +struct passwd *getpwnam(const char *name) +/* Return the password file entry belonging to the user name. */ +{ + struct passwd *pw; + + endpwent(); + while ((pw= getpwent()) != nil && strcmp(pw->pw_name, name) != 0) {} + endpwent(); + return pw; +} diff --git a/lib/other/getttyent.c b/lib/other/getttyent.c new file mode 100755 index 000000000..f26216247 --- /dev/null +++ b/lib/other/getttyent.c @@ -0,0 +1,153 @@ +/* getttyent(3) - get a ttytab entry Author: Kees J. Bot + * 28 Oct 1995 + */ +#define nil 0 +#define open _open +#define close _close +#define fcntl _fcntl +#define read _read +#include <string.h> +#include <sys/types.h> +#include <ttyent.h> +#include <unistd.h> +#include <fcntl.h> + +#define arraysize(a) (sizeof(a) / sizeof((a)[0])) +#define arraylimit(a) ((a) + arraysize(a)) + +static char TTYTAB[]= "/etc/ttytab"; /* The table of terminal devices. */ + +static char buf[512]; /* Read buffer. */ +static char ttline[256]; /* One line from the ttytab file. */ +static char *ttargv[32]; /* Compound arguments. */ +static struct ttyent entry; /* Entry to fill and return. */ +static int ttfd= -1; /* Filedescriptor to the file. */ +static char *bufptr; /* Place in buf. */ +static ssize_t buflen= 0; /* Remaining characters in buf. */ +static char *lineptr; /* Place in the line. */ +static char **argvptr; /* Place in word lists. */ + +void endttyent(void) +/* Close the ttytab file. */ +{ + if (ttfd >= 0) { + (void) close(ttfd); + ttfd= -1; + buflen= 0; + } +} + +int setttyent(void) +/* Open the ttytab file. */ +{ + if (ttfd >= 0) endttyent(); + + if ((ttfd= open(TTYTAB, O_RDONLY)) < 0) return -1; + (void) fcntl(ttfd, F_SETFD, fcntl(ttfd, F_GETFD) | FD_CLOEXEC); + return 0; +} + +static int getline(void) +/* Get one line from the ttytab file, return 0 if bad or EOF. */ +{ + lineptr= ttline; + argvptr= ttargv; + + do { + if (buflen == 0) { + if ((buflen= read(ttfd, buf, sizeof(buf))) <= 0) + return 0; + bufptr= buf; + } + + if (lineptr == arraylimit(ttline)) return 0; + buflen--; + } while ((*lineptr++ = *bufptr++) != '\n'); + + lineptr= ttline; + return 1; +} + +static int white(int c) +/* Whitespace? */ +{ + return c == ' ' || c == '\t'; +} + +static char *scan_white(int quoted) +/* Scan for a field separator in a line, return the start of the field. + * "quoted" is set if we have to watch out for double quotes. + */ +{ + char *field, *last; + + while (white(*lineptr)) lineptr++; + if (!quoted && *lineptr == '#') return nil; + + field= lineptr; + for (;;) { + last= lineptr; + if (*lineptr == 0) return nil; + if (*lineptr == '\n') break; + if (quoted && *lineptr == '"') return field; + if (white(*lineptr++)) break; + } + *last= 0; + return *field == 0 ? nil : field; +} + +static char **scan_quoted(void) +/* Read a field that may be a quoted list of words. */ +{ + char *p, **field= argvptr; + + while (white(*lineptr)) lineptr++; + + if (*lineptr == '"') { + /* Quoted list of words. */ + lineptr++; + while ((p= scan_white(1)) != nil && *p != '"') { + if (argvptr == arraylimit(ttargv)) return nil; + *argvptr++= p; + } + if (*lineptr == '"') *lineptr++= 0; + } else { + /* Just one word. */ + if ((p= scan_white(0)) == nil) return nil; + if (argvptr == arraylimit(ttargv)) return nil; + *argvptr++= p; + } + if (argvptr == arraylimit(ttargv)) return nil; + *argvptr++= nil; + return field; +} + +struct ttyent *getttyent(void) +/* Read one entry from the ttytab file. */ +{ + /* Open the file if not yet open. */ + if (ttfd < 0 && setttyent() < 0) return nil; + + /* Look for a line with something on it. */ + for (;;) { + if (!getline()) return nil; /* EOF or corrupt. */ + + if ((entry.ty_name= scan_white(0)) == nil) continue; + entry.ty_type= scan_white(0); + entry.ty_getty= scan_quoted(); + entry.ty_init= scan_quoted(); + + return &entry; + } +} + +struct ttyent *getttynam(const char *name) +/* Return the ttytab file entry for a given tty. */ +{ + struct ttyent *tty; + + endttyent(); + while ((tty= getttyent()) != nil && strcmp(tty->ty_name, name) != 0) {} + endttyent(); + return tty; +} diff --git a/lib/other/getw.c b/lib/other/getw.c new file mode 100755 index 000000000..324a9f180 --- /dev/null +++ b/lib/other/getw.c @@ -0,0 +1,22 @@ +/* + * getw - read a word from a stream + */ +/* $Header$ */ + +#include <stdio.h> + +_PROTOTYPE(int getw, (FILE *stream )); + +int getw(stream) +register FILE *stream; +{ + register int cnt = sizeof(int); + int w; + register char *p = (char *) &w; + + while (cnt--) { + *p++ = getc(stream); + } + if (feof(stream) || ferror(stream)) return EOF; + return w; +} diff --git a/lib/other/hypot.c b/lib/other/hypot.c new file mode 100755 index 000000000..8fa1a6cb7 --- /dev/null +++ b/lib/other/hypot.c @@ -0,0 +1,43 @@ +/* + * (c) copyright 1988 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + * + * Author: Ceriel J.H. Jacobs + */ + +#include <math.h> + +struct complex { + double r,i; +}; + +_PROTOTYPE(double hypot, (double x, double y )); +_PROTOTYPE(double cabs, (struct complex p_compl )); + +/* $Header$ */ + +double +hypot(x, y) +double x, y; +{ + /* Computes sqrt(x*x+y*y), avoiding overflow */ + + if (x < 0) x = -x; + if (y < 0) y = -y; + if (x > y) { + double t = y; + y = x; + x = t; + } + /* sqrt(x*x+y*y) = sqrt(y*y*(x*x/(y*y)+1.0)) = y*sqrt(x*x/(y*y)+1.0) */ + if (y == 0.0) return 0.0; + x /= y; + return y*sqrt(x*x+1.0); +} + +double +cabs(p_compl) +struct complex p_compl; +{ + return hypot(p_compl.r, p_compl.i); +} diff --git a/lib/other/index.c b/lib/other/index.c new file mode 100755 index 000000000..4c648de8b --- /dev/null +++ b/lib/other/index.c @@ -0,0 +1,11 @@ +#include <lib.h> +/* index - find first occurrence of a character in a string */ + +#include <string.h> + +char *index(s, charwanted) /* found char, or NULL if none */ +_CONST char *s; +char charwanted; +{ + return(strchr(s, charwanted)); +} diff --git a/lib/other/itoa.c b/lib/other/itoa.c new file mode 100755 index 000000000..f78717d24 --- /dev/null +++ b/lib/other/itoa.c @@ -0,0 +1,36 @@ +#include <lib.h> +/* Integer to ASCII for signed decimal integers. */ + +PRIVATE int next; +PRIVATE char qbuf[8]; + +_PROTOTYPE( char *itoa, (int n)); + +char *itoa(n) +int n; +{ + register int r, k; + int flag = 0; + + next = 0; + if (n < 0) { + qbuf[next++] = '-'; + n = -n; + } + if (n == 0) { + qbuf[next++] = '0'; + } else { + k = 10000; + while (k > 0) { + r = n / k; + if (flag || r > 0) { + qbuf[next++] = '0' + r; + flag = 1; + } + n -= r * k; + k = k / 10; + } + } + qbuf[next] = 0; + return(qbuf); +} diff --git a/lib/other/loadname.c b/lib/other/loadname.c new file mode 100755 index 000000000..1329b2674 --- /dev/null +++ b/lib/other/loadname.c @@ -0,0 +1,19 @@ +#include <lib.h> +#include <string.h> + +PUBLIC void _loadname(name, msgptr) +_CONST char *name; +message *msgptr; +{ +/* This function is used to load a string into a type m3 message. If the + * string fits in the message, it is copied there. If not, a pointer to + * it is passed. + */ + + register size_t k; + + k = strlen(name) + 1; + msgptr->m3_i1 = k; + msgptr->m3_p1 = (char *) name; + if (k <= sizeof msgptr->m3_ca1) strcpy(msgptr->m3_ca1, name); +} diff --git a/lib/other/lock.c b/lib/other/lock.c new file mode 100755 index 000000000..94b6010bc --- /dev/null +++ b/lib/other/lock.c @@ -0,0 +1,62 @@ +#include <lib.h> +#include <errno.h> +#include <sys/types.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <stdio.h> +#if _ANSI +#include <stdlib.h> +#endif + +typedef enum { + False, True +} BOOLEAN; + +#define LOCKDIR "/tmp/" /* or /usr/tmp/ as the case may be */ +#define MAXTRIES 3 +#define NAPTIME (unsigned int)5 + +PRIVATE _PROTOTYPE( char *lockpath, (char *name)); +_PROTOTYPE( void syserr, (char *errstring)); +_PROTOTYPE( BOOLEAN lock, (char *name)); +_PROTOTYPE( void unlock, (char *name)); + +void +syserr(errstring) +char *errstring; +{ + fprintf(stderr,"couldn't %s\n", errstring); + exit(1); +} + +BOOLEAN lock(name) /* acquire lock */ +char *name; +{ + char *path; + int fd, tries; + + path = lockpath(name); + tries = 0; + while ((fd = creat(path, 0)) == -1 && errno == EACCES) { + if (++tries >= MAXTRIES) return(False); + sleep(NAPTIME); + } + if (fd == -1 || close(fd) == -1) syserr("lock"); + return(True); +} + +void unlock(name) /* free lock */ +char *name; +{ + if (unlink(lockpath(name)) == -1) syserr("unlock"); +} + +PRIVATE char *lockpath(name) /* generate lock file path */ +char *name; +{ + PRIVATE char path[20]; + + strcpy(path, LOCKDIR); + return(strcat(path, name)); +} diff --git a/lib/other/lrand.c b/lib/other/lrand.c new file mode 100755 index 000000000..ff475db5c --- /dev/null +++ b/lib/other/lrand.c @@ -0,0 +1,53 @@ +/* lrand(3) + * + * Author: Terrence W. Holm Nov. 1988 + * + * + * A prime modulus multiplicative linear congruential + * generator (PMMLCG), or "Lehmer generator". + * Implementation directly derived from the article: + * + * S. K. Park and K. W. Miller + * Random Number Generators: Good Ones are Hard to Find + * CACM vol 31, #10. Oct. 1988. pp 1192-1201. + * + * + * Using the following multiplier and modulus, we obtain a + * generator which: + * + * 1) Has a full period: 1 to 2^31 - 2. + * 2) Is testably "random" (see the article). + * 3) Has a known implementation by E. L. Schrage. + */ + +#include <lib.h> + +_PROTOTYPE( long seed, (long lseed)); +_PROTOTYPE( long lrand, (void)); + +#define A 16807L /* A "good" multiplier */ +#define M 2147483647L /* Modulus: 2^31 - 1 */ +#define Q 127773L /* M / A */ +#define R 2836L /* M % A */ + +PRIVATE long _lseed = 1L; + +long seed(lseed) +long lseed; +{ + long previous_seed = _lseed; + + _lseed = lseed; + + return(previous_seed); +} + + +long lrand() +{ + _lseed = A * (_lseed % Q) - R * (_lseed / Q); + + if (_lseed < 0) _lseed += M; + + return(_lseed); +} diff --git a/lib/other/lsearch.c b/lib/other/lsearch.c new file mode 100755 index 000000000..8f834ce58 --- /dev/null +++ b/lib/other/lsearch.c @@ -0,0 +1,50 @@ +#include <lib.h> +#include <string.h> +/* lsearch(3) and lfind(3) + * + * Author: Terrence W. Holm Sep. 1988 + */ + +#include <stddef.h> + +_PROTOTYPE( char *lsearch, (char *key, char *base, + unsigned *count, unsigned width, + int (*keycmp)(const void *, const void *))); +_PROTOTYPE( char *lfind, (char *key, char *base, + unsigned *count, unsigned width, + int (*keycmp)(const void *, const void *))); + +char *lsearch(key, base, count, width, keycmp) +char *key; +char *base; +unsigned *count; +unsigned width; +_PROTOTYPE( int (*keycmp), (const void *, const void *)); +{ + char *entry; + char *last = base + *count * width; + + for (entry = base; entry < last; entry += width) + if (keycmp(key, entry) == 0) return(entry); + + bcopy(key, last, width); + *count += 1; + return(last); +} + + +char *lfind(key, base, count, width, keycmp) +char *key; +char *base; +unsigned *count; +unsigned width; +_PROTOTYPE( int (*keycmp), (const void *, const void *)); +{ + char *entry; + char *last = base + *count * width; + + for (entry = base; entry < last; entry += width) + if (keycmp(key, entry) == 0) return(entry); + + return((char *)NULL); +} diff --git a/lib/other/memccpy.c b/lib/other/memccpy.c new file mode 100755 index 000000000..59ddb711d --- /dev/null +++ b/lib/other/memccpy.c @@ -0,0 +1,39 @@ +#include <lib.h> +/* memccpy - copy bytes up to a certain char + * + * CHARBITS should be defined only if the compiler lacks "unsigned char". + * It should be a mask, e.g. 0377 for an 8-bit machine. + */ + +#include <ansi.h> +#include <stddef.h> + +_PROTOTYPE( void *memccpy, (void *dst, const void *src, + int ucharstop, size_t size)); +#ifndef CHARBITS +# define UNSCHAR(c) ((unsigned char)(c)) +#else +# define UNSCHAR(c) ((c)&CHARBITS) +#endif + +void *memccpy(dst, src, ucharstop, size) +void * dst; +_CONST void * src; +int ucharstop; +_SIZET size; +{ + register char *d; + register _CONST char *s; + register _SIZET n; + register int uc; + + if (size <= 0) return( (void *) NULL); + + s = (char *) src; + d = (char *) dst; + uc = UNSCHAR(ucharstop); + for (n = size; n > 0; n--) + if (UNSCHAR(*d++ = *s++) == (char) uc) return( (void *) d); + + return( (void *) NULL); +} diff --git a/lib/other/mstats.c b/lib/other/mstats.c new file mode 100644 index 000000000..da4a754f5 --- /dev/null +++ b/lib/other/mstats.c @@ -0,0 +1,23 @@ +#include <minix/config.h> + +#if ENABLE_MESSAGE_STATS + +#include <lib.h> +#include <unistd.h> + +PUBLIC int mstats(struct message_statentry *ms, int entries, int reset) +{ + message m; + + m.m1_i1 = entries; + m.m1_i2 = reset; + m.m1_p1 = (void *) ms; + + if(_syscall(MM, MSTATS, &m) < 0) { + return -1; + } + + return m.m_type; +} + +#endif diff --git a/lib/other/mtab.c b/lib/other/mtab.c new file mode 100755 index 000000000..69b91b823 --- /dev/null +++ b/lib/other/mtab.c @@ -0,0 +1,205 @@ +/* This package consists of 4 routines for handling the /etc/mtab file. + * The /etc/mtab file contains information about the root and mounted file + * systems as a series of lines, each one with exactly four fields separated + * by one space as follows: + * + * special mounted_on version rw_flag + * + * where + * special is the name of the block special file + * mounted_on is the directory on which it is mounted + * version is either 1 or 2 for MINIX V1 and V2 file systems + * rw_flag is rw or ro for read/write or read only + * + * An example /etc/mtab: + * + * /dev/ram / 2 rw + * /dev/hd1 /usr 2 rw + * /dev/fd0 /user 1 ro + * + * + * The four routines for handling /etc/mtab are as follows. They use two + * (hidden) internal buffers, mtab_in for input and mtab_out for output. + * + * load_mtab(&prog_name) - read /etc/mtab into mtab_in + * get_mtab_entry(&s1, &s2, &s3, &s4) - arrays that are filled in + * put_mtab_entry(&s1, &s2, &s3, &s4) - append a line to mtab_out + * rewrite_mtab(&prog_name) - write mtab_out to /etc/mtab + * + * If load_mtab and rewrite_mtab work, they return 0. If they fail, they + * print their own error messages on stderr and return -1. When get_mtab_entry + * runs out of entries to return, it sets the first pointer to NULL and returns + * -1 instead of 0. Also, rewrite_mtab returns -1 if it fails. + */ + +#include <sys/types.h> +#include <minix/minlib.h> +#include <ctype.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +#define BUF_SIZE 512 /* size of the /etc/mtab buffer */ + +char *etc_mtab = "/etc/mtab"; /* name of the /etc/mtab file */ +static char mtab_in[BUF_SIZE+1]; /* holds /etc/mtab when it is read in */ +static char mtab_out[BUF_SIZE+1]; /* buf to build /etc/mtab for output later */ +static char *iptr = mtab_in; /* pointer to next line to feed out. */ +static char *optr = mtab_out; /* pointer to place where next line goes */ + +_PROTOTYPE(int load_mtab, (char *prog_name )); +_PROTOTYPE(int rewrite_mtab, (char *prog_name )); +_PROTOTYPE(int get_mtab_entry, (char *special, char *mounted_on, + char *version, char *rw_flag)); +_PROTOTYPE(int put_mtab_entry, (char *special, char *mounted_on, + char *version, char *rw_flag)); +_PROTOTYPE(void err, (char *prog_name, char *str )); + + +int load_mtab(prog_name) +char *prog_name; +{ +/* Read in /etc/mtab and store it in /etc/mtab. */ + + int fd, n; + char *ptr; + + /* Open the file. */ + fd = open(etc_mtab, O_RDONLY); + if (fd < 0) { + err(prog_name, ": cannot open "); + return(-1); + } + + /* File opened. Read it in. */ + n = read(fd, mtab_in, BUF_SIZE); + if (n <= 0) { + /* Read failed. */ + err(prog_name, ": cannot read "); + return(-1); + } + if (n == BUF_SIZE) { + /* Some nut has mounted 50 file systems or something like that. */ + std_err(prog_name); + std_err(": file too large: "); + std_err(etc_mtab); + return(-1); + } + + close(fd); + + /* Replace all the whitespace by '\0'. */ + ptr = mtab_in; + while (*ptr != '\0') { + if (isspace(*ptr)) *ptr = '\0'; + ptr++; + } + return(0); +} + + +int rewrite_mtab(prog_name) +char *prog_name; +{ +/* Write mtab_out to /etc/mtab. */ + + int fd, n; + + /* Do a creat to truncate the file. */ + fd = creat(etc_mtab, 0777); + if (fd < 0) { + err(prog_name, ": cannot overwrite "); + return(-1); + } + + /* File created. Write it. */ + n = write(fd, mtab_out, (unsigned int)(optr - mtab_out)); + if (n <= 0) { + /* Write failed. */ + err(prog_name, " could not write "); + return(-1); + } + + close(fd); + return(0); +} + + +int get_mtab_entry(special, mounted_on, version, rw_flag) +char *special; +char *mounted_on; +char *version; +char *rw_flag; +{ +/* Return the next entry from mtab_in. */ + + if (iptr >= &mtab_in[BUF_SIZE]) { + special[0] = '\0'; + return(-1); + } + + strcpy(special, iptr); + while (isprint(*iptr)) iptr++; + while (*iptr == '\0'&& iptr < &mtab_in[BUF_SIZE]) iptr++; + + strcpy(mounted_on, iptr); + while (isprint(*iptr)) iptr++; + while (*iptr == '\0'&& iptr < &mtab_in[BUF_SIZE]) iptr++; + + strcpy(version, iptr); + while (isprint(*iptr)) iptr++; + while (*iptr == '\0'&& iptr < &mtab_in[BUF_SIZE]) iptr++; + + strcpy(rw_flag, iptr); + while (isprint(*iptr)) iptr++; + while (*iptr == '\0'&& iptr < &mtab_in[BUF_SIZE]) iptr++; + return(0); +} + + +int put_mtab_entry(special, mounted_on, version, rw_flag) +char *special; +char *mounted_on; +char *version; +char *rw_flag; +{ +/* Append an entry to the mtab_out buffer. */ + + int n1, n2, n3, n4; + + n1 = strlen(special); + n2 = strlen(mounted_on); + n3 = strlen(version); + n4 = strlen(rw_flag); + + if (optr + n1 + n2 + n3 + n4 + 5 >= &mtab_out[BUF_SIZE]) return(-1); + strcpy(optr, special); + optr += n1; + *optr++ = ' '; + + strcpy(optr, mounted_on); + optr += n2; + *optr++ = ' '; + + strcpy(optr, version); + optr += n3; + *optr++ = ' '; + + strcpy(optr, rw_flag); + optr += n4; + *optr++ = '\n'; + return(0); +} + + +void +err(prog_name, str) +char *prog_name, *str; +{ + std_err(prog_name); + std_err(str); + std_err(etc_mtab); + perror(" "); +} diff --git a/lib/other/nlist.c b/lib/other/nlist.c new file mode 100755 index 000000000..4901032f1 --- /dev/null +++ b/lib/other/nlist.c @@ -0,0 +1,71 @@ +/* + * "nlist.c", Peter Valkenburg, january 1989. + */ + +#include <lib.h> +#include <string.h> +#include <a.out.h> +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> + +#define fail(fp) (fclose(fp), -1) /* ret. exp. when nlist fails */ + +_PROTOTYPE( int nlist, (char *file, struct nlist nl[])); + +/* + * Nlist fills fields n_sclass and n_value of array nl with values found in + * non-stripped executable file. Entries that are not found have their + * n_value/n_sclass fields set to 0. Nl ends with a 0 or nul string n_name. + * The return value is -1 on failure, else the number of entries not found. + */ +int nlist(file, nl) +char *file; +struct nlist nl[]; +{ + int nents, nsrch, nfound, i; + struct nlist nlent; + FILE *fp; + struct exec hd; + + /* open executable with namelist */ + if ((fp = fopen(file, "r")) == NULL) + return -1; + + /* get header and seek to start of namelist */ + if (fread((char *) &hd, sizeof(struct exec), 1, fp) != 1 || + BADMAG(hd) || fseek(fp, A_SYMPOS(hd), SEEK_SET) != 0) + return fail(fp); + + /* determine number of entries searched for & reset fields */ + nsrch = 0; + while (nl[nsrch].n_name != NULL && *(nl[nsrch].n_name) != '\0') { + nl[nsrch].n_sclass = 0; + nl[nsrch].n_value = 0; + nl[nsrch].n_type = 0; /* for compatability */ + nsrch++; + } + + /* loop through namelist & fill in user array */ + nfound = 0; + for (nents = (hd.a_syms & 0xFFFF) / sizeof(struct nlist); + nents > 0; nents--) { + if (nsrch == nfound) + break; /* no need to look further */ + if (fread((char *) &nlent, sizeof(struct nlist), 1, fp) != 1) + return fail(fp); + for (i = 0; i < nsrch; i++) + if (nl[i].n_sclass == 0 && + strncmp(nl[i].n_name, nlent.n_name, + sizeof(nlent.n_name)) == 0) { + nl[i] = nlent; + nfound++; + break; + } + } + + (void) fclose(fp); + + return nsrch - nfound; +} diff --git a/lib/other/peekpoke.c b/lib/other/peekpoke.c new file mode 100755 index 000000000..d311a123b --- /dev/null +++ b/lib/other/peekpoke.c @@ -0,0 +1,49 @@ +/* Peek and poke using /dev/mem. + * + * Callers now ought to check the return values. + * + * Calling peek() requires read permission on /dev/mem, and consumes + * a file descriptor. Calling poke() requires write permission, and + * consumes another file descriptor. + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <unistd.h> + +_PROTOTYPE( int peek, (unsigned segment, unsigned offset)); +_PROTOTYPE( int poke, (unsigned segment, unsigned offset, unsigned value)); + +#define SEGSIZE 0x10 + +int peek(segment, offset) +unsigned segment; +unsigned offset; +{ + unsigned char chvalue; + static int infd = -1; + + if (infd < 0) infd = open("/dev/mem", O_RDONLY); + if (infd < 0 || + lseek(infd, (unsigned long) segment * SEGSIZE + offset, SEEK_SET) < 0 || + read(infd, (char *) &chvalue, (unsigned) 1) != 1) + return(-1); + return(chvalue); +} + +int poke(segment, offset, value) +unsigned segment; +unsigned offset; +unsigned value; +{ + unsigned char chvalue; + static int outfd = -1; + + chvalue = value; + if (outfd < 0) outfd = open("/dev/mem", O_WRONLY); + if (outfd < 0 || + lseek(outfd, (unsigned long) segment * SEGSIZE + offset, SEEK_SET) < 0 || + write(outfd, (char *) &chvalue, (unsigned) 1) != 1) + return(-1); + return(chvalue); +} diff --git a/lib/other/popen.c b/lib/other/popen.c new file mode 100755 index 000000000..7f117322e --- /dev/null +++ b/lib/other/popen.c @@ -0,0 +1,122 @@ +/* + * popen - open a pipe + */ +/* $Header$ */ + +#include <sys/types.h> +#include <limits.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> + +#if defined(__BSD4_2) +union wait { + int w_status; +}; +typedef union wait wait_arg; +#else +typedef int wait_arg; +#endif /* __BSD4_2 */ + +#include "../stdio/loc_incl.h" + +#ifdef _ANSI +int _close(int d); +int _dup2(int oldd, int newd); /* not present in System 5 */ +int _execl(const char *name, const char *_arg, ... ); +pid_t _fork(void); +int _pipe(int fildes[2]); +pid_t _wait(wait_arg *status); +void _exit(int status); +#endif + +static int pids[OPEN_MAX]; + +FILE * +popen(command, type) +_CONST char *command; +_CONST char *type; +{ + int piped[2]; + int Xtype = *type == 'r' ? 0 : *type == 'w' ? 1 : 2; + int pid; + + if (Xtype == 2 || + _pipe(piped) < 0 || + (pid = _fork()) < 0) return 0; + + if (pid == 0) { + /* child */ + register int *p; + + for (p = pids; p < &pids[OPEN_MAX]; p++) { + if (*p) _close((int)(p - pids)); + } + _close(piped[Xtype]); + _dup2(piped[!Xtype], !Xtype); + _close(piped[!Xtype]); + _execl("/bin/sh", "sh", "-c", command, (char *) 0); + _exit(127); /* like system() ??? */ + } + + pids[piped[Xtype]] = pid; + _close(piped[!Xtype]); + return fdopen(piped[Xtype], type); +} + +#if defined(__BSD4_2) +#define ret_val status.w_status +#else +#define ret_val status +#endif + +int +pclose(stream) +FILE *stream; +{ + int fd = fileno(stream); + wait_arg status; + int wret; + +#ifdef _ANSI + void (*intsave)(int) = signal(SIGINT, SIG_IGN); + void (*quitsave)(int) = signal(SIGQUIT, SIG_IGN); +#else + void (*intsave)() = signal(SIGINT, SIG_IGN); + void (*quitsave)() = signal(SIGQUIT, SIG_IGN); +#endif + fclose(stream); + while ((wret = _wait(&status)) != -1) { + if (wret == pids[fd]) break; + } + if (wret == -1) ret_val = -1; + signal(SIGINT, intsave); + signal(SIGQUIT, quitsave); + pids[fd] = 0; + return ret_val; +} + +#if defined(__USG) +int _dup(int fildes); + +static int +_dup2(oldd, newd) +int oldd, newd; +{ + int i = 0, fd, tmp; + int fdbuf[_NFILES]; + + /* ignore the error on the close() */ + tmp = errno; (void) _close(newd); errno = tmp; + while ((fd = _dup(oldd)) != newd) { + if (fd == -1) break; + fdbuf[i++] = fd; + } + tmp = errno; + while (--i >= 0) { + _close(fdbuf[i]); + } + errno = tmp; + return -(fd == -1); +} +#endif /* __USG */ diff --git a/lib/other/putenv.c b/lib/other/putenv.c new file mode 100755 index 000000000..d49ab9d0a --- /dev/null +++ b/lib/other/putenv.c @@ -0,0 +1,79 @@ +/* + * (c) copyright 1989 by the Vrije Universiteit, Amsterdam, The Netherlands. + * See the copyright notice in the ACK home directory, in the file "Copyright". + */ +/* $Header$ */ + +#include <stdlib.h> +#include <string.h> + +#define ENTRY_INC 10 +#define rounded(x) (((x / ENTRY_INC) + 1) * ENTRY_INC) + +extern _CONST char ***_penviron; + +int +putenv(name) +_CONST char *name; +{ + register _CONST char **v = *_penviron; + register char *r; + static int size = 0; + /* When size != 0, it contains the number of entries in the + * table (including the final NULL pointer). This means that the + * last non-null entry is environ[size - 2]. + */ + + if (!name) return 0; + if (*_penviron == NULL) return 1; + if (r = strchr(name, '=')) { + register _CONST char *p, *q; + + *r = '\0'; + + if (v != NULL) { + while ((p = *v) != NULL) { + q = name; + while (*q && (*q++ == *p++)) + /* EMPTY */ ; + if (*q || (*p != '=')) { + v++; + } else { + /* The name was already in the + * environment. + */ + *r = '='; + *v = name; + return 0; + } + } + } + *r = '='; + v = *_penviron; + } + + if (!size) { + register _CONST char **p; + register int i = 0; + + if (v) + do { + i++; + } while (*v++); + if (!(v = malloc(rounded(i) * sizeof(char **)))) + return 1; + size = i; + p = *_penviron; + *_penviron = v; + while (*v++ = *p++); /* copy the environment */ + v = *_penviron; + } else if (!(size % ENTRY_INC)) { + if (!(v = realloc(*_penviron, rounded(size) * sizeof(char **)))) + return 1; + *_penviron = v; + } + v[size - 1] = name; + v[size] = NULL; + size++; + return 0; +} diff --git a/lib/other/putw.c b/lib/other/putw.c new file mode 100755 index 000000000..807f67f4e --- /dev/null +++ b/lib/other/putw.c @@ -0,0 +1,23 @@ +/* + * putw - write an word on a stream + */ +/* $Header$ */ + +#include <stdio.h> + +_PROTOTYPE(int putw, (int w, FILE *stream )); + +int +putw(w, stream) +int w; +register FILE *stream; +{ + register int cnt = sizeof(int); + register char *p = (char *) &w; + + while (cnt--) { + putc(*p++, stream); + } + if (ferror(stream)) return EOF; + return w; +} diff --git a/lib/other/rindex.c b/lib/other/rindex.c new file mode 100755 index 000000000..0c2896c20 --- /dev/null +++ b/lib/other/rindex.c @@ -0,0 +1,11 @@ +#include <lib.h> +/* rindex - find last occurrence of a character in a string */ + +#include <string.h> + +char *rindex(s, charwanted) /* found char, or NULL if none */ +_CONST char *s; +char charwanted; +{ + return(strrchr(s, charwanted)); +} diff --git a/lib/other/setcache.c b/lib/other/setcache.c new file mode 100644 index 000000000..6e516ed3b --- /dev/null +++ b/lib/other/setcache.c @@ -0,0 +1,21 @@ + +#include <minix/config.h> + +#include <lib.h> +#include <unistd.h> +#include <minix/type.h> +#include <minix/com.h> + +int +setcache(int kb) +{ + message m; + int r; + + m.m1_i1 = kb; + m.m_type = SETCACHE; + if ((r=_syscall(FS, SETCACHE, &m)) < 0) + return(-1); + + return(m.m_type); +} diff --git a/lib/other/stderr.c b/lib/other/stderr.c new file mode 100755 index 000000000..b7031287e --- /dev/null +++ b/lib/other/stderr.c @@ -0,0 +1,14 @@ +#include <lib.h> +#include <sys/types.h> +#include <unistd.h> + +_PROTOTYPE( void std_err, (char *s)); + +void std_err(s) +char *s; +{ + register char *p = s; + + while (*p != 0) p++; + write(2, s, (int) (p - s)); +} diff --git a/lib/other/swab.c b/lib/other/swab.c new file mode 100755 index 000000000..8f02161b9 --- /dev/null +++ b/lib/other/swab.c @@ -0,0 +1,22 @@ +#include <lib.h> +/* swab(3) + * + * Author: Terrence W. Holm Sep. 1988 + */ +_PROTOTYPE( void swab, (char *from, char *to, int count)); + +void swab(from, to, count) +char *from; +char *to; +int count; +{ + register char temp; + + count >>= 1; + + while (--count >= 0) { + temp = *from++; + *to++ = *from++; + *to++ = temp; + } +} diff --git a/lib/other/syscall.c b/lib/other/syscall.c new file mode 100755 index 000000000..e397ae32a --- /dev/null +++ b/lib/other/syscall.c @@ -0,0 +1,22 @@ +#include <lib.h> + +PUBLIC int _syscall(who, syscallnr, msgptr) +int who; +int syscallnr; +register message *msgptr; +{ + int status; + + msgptr->m_type = syscallnr; + status = _sendrec(who, msgptr); + if (status != 0) { + /* 'sendrec' itself failed. */ + /* XXX - strerror doesn't know all the codes */ + msgptr->m_type = status; + } + if (msgptr->m_type < 0) { + errno = -msgptr->m_type; + return(-1); + } + return(msgptr->m_type); +} diff --git a/lib/other/sysconf.c b/lib/other/sysconf.c new file mode 100755 index 000000000..9cc52b8b3 --- /dev/null +++ b/lib/other/sysconf.c @@ -0,0 +1,52 @@ +/* sysconf.c POSIX 4.8.1 + * long int sysconf(int name); + * + * POSIX allows some of the values in <limits.h> to be increased at + * run time. The sysconf() function allows such values to be checked + * at run time. MINIX does not use this facility - the run time + * limits are those given in <limits.h>. + */ + +#include <lib.h> +#include <unistd.h> +#include <time.h> + +PUBLIC long int sysconf(name) +int name; /* property being inspected */ +{ + switch(name) { + case _SC_ARG_MAX: + return (long) ARG_MAX; + + case _SC_CHILD_MAX: + return (long) CHILD_MAX; + + case _SC_CLK_TCK: + return (long) CLOCKS_PER_SEC; + + case _SC_NGROUPS_MAX: + return (long) NGROUPS_MAX; + + case _SC_OPEN_MAX: + return (long) OPEN_MAX; + + case _SC_JOB_CONTROL: + return -1L; /* no job control */ + + case _SC_SAVED_IDS: + return -1L; /* no saved uid/gid */ + + case _SC_VERSION: + return (long) _POSIX_VERSION; + + case _SC_STREAM_MAX: + return (long) STREAM_MAX; + + case _SC_TZNAME_MAX: + return (long) TZNAME_MAX; + + default: + errno = EINVAL; + return -1L; + } +} diff --git a/lib/other/telldir.c b/lib/other/telldir.c new file mode 100755 index 000000000..f13ee0135 --- /dev/null +++ b/lib/other/telldir.c @@ -0,0 +1,16 @@ +/* telldir() Author: Kees J. Bot + * 24 Apr 1989 + */ +#define nil 0 +#include <lib.h> +#include <sys/types.h> +#include <dirent.h> +#include <errno.h> + +off_t telldir(DIR *dp) +/* Return the current read position in a directory. */ +{ + if (dp == nil) { errno= EBADF; return -1; } + + return dp->_pos; +} diff --git a/lib/other/termcap.c b/lib/other/termcap.c new file mode 100755 index 000000000..4d3fa2f02 --- /dev/null +++ b/lib/other/termcap.c @@ -0,0 +1,325 @@ +/* + * termcap.c V1.1 20/7/87 agc Joypace Ltd + * + * Copyright Joypace Ltd, London, UK, 1987. All rights reserved. + * This file may be freely distributed provided that this notice + * remains attached. + * + * A public domain implementation of the termcap(3) routines. + * + * + * + * Klamer Schutte V1.2 Nov. 1988 + * + * - Can match multiple terminal names [tgetent] + * - Removal of **area assignments [tgetstr] + * + * Terrence W. Holm V1.3 May, Sep, Oct. 1988 + * + * - Correct when TERM != name and TERMCAP is defined [tgetent] + * - Correct the comparison for the terminal name [tgetent] + * - Correct the value of ^x escapes [tgetstr] + * - Added %r to reverse row/column [tgoto] + * - Fixed end of definition test [tgetnum/flag/str] + * + * Terrence W. Holm V1.4 Jan. 1989 + * + * - Incorporated Klamer's V1.2 fixes into V1.3 + * - Added %d, (old %d is now %2) [tgoto] + * - Allow '#' comments in definition file [tgetent] + */ + +#include <lib.h> +#include <termcap.h> +#include <ctype.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> + +char *capab = (char *)NULL; /* the capability itself */ + +#if 0 +/* The following are not yet used. */ +extern short ospeed; /* output speed */ +extern char PC; /* padding character */ +extern char *BC; /* back cursor movement */ +extern char *UP; /* up cursor movement */ +#endif + +/* + * tgetent - get the termcap entry for terminal name, and put it + * in bp (which must be an array of 1024 chars). Returns 1 if + * termcap entry found, 0 if not found, and -1 if file not found. + */ + +int tgetent(bp, name) +char *bp; +char *name; +{ + FILE *fp; + char *file; + char *term; + short len = strlen(name); + + capab = bp; + + /* If TERMCAP begins with a '/' then use TERMCAP as the path */ + /* Name of the termcap definitions file. If TERMCAP is a */ + /* Definition and TERM equals "name" then use TERMCAP as the */ + /* Definition. Otherwise use "/etc/termcap" as the path name. */ + + if ((file = getenv("TERMCAP")) == (char *)NULL) + file = "/etc/termcap"; + else if (*file != '/') + if ((term = getenv("TERM")) != (char *)NULL && strcmp(term, name) == 0) { + *bp = '\0'; + strncat(bp, file, 1023); + return(1); + } else + file = "/etc/termcap"; + + if ((fp = fopen(file, "r")) == (FILE *) NULL) { + capab = (char *)NULL; /* no valid termcap */ + return(-1); + } + for (;;) { + /* Read in each definition */ + int def_len = 0; + char *cp = bp; + + do { + if (fgets(&bp[def_len], (unsigned int)(1024 - def_len), fp) == (char *)NULL) { + fclose(fp); + capab = (char *)NULL; /* no valid termcap */ + return(0); + } + def_len = strlen(bp) - 2; + } while (bp[def_len] == '\\'); + + while (isspace(*cp)) cp++; + + /* Comment lines start with a '#' */ + if (*cp == '#') continue; + + /* See if any of the terminal names in this definition */ + /* Match "name". */ + + do { + if (strncmp(name, cp, len) == 0 && + (cp[len] == '|' || cp[len] == ':')) { + fclose(fp); + return(1); + } + while ((*cp) && (*cp != '|') && (*cp != ':')) cp++; + } while (*cp++ == '|'); + } +} + + +/* + * tgetnum - get the numeric terminal capability corresponding + * to id. Returns the value, -1 if invalid. + */ + +int tgetnum(id) +char *id; +{ + register char *cp = capab; + + if (cp == (char *)NULL || id == (char *)NULL) return(-1); + + for (;;) { + while (*cp++ != ':') + if (cp[-1] == '\0') return(-1); + + while (isspace(*cp)) cp++; + + if (strncmp(cp, id, 2) == 0 && cp[2] == '#') return(atoi(cp + 3)); + } +} + + +/* + * tgetflag - get the boolean flag corresponding to id. Returns -1 + * if invalid, 0 if the flag is not in termcap entry, or 1 if it is + * present. + */ + +int tgetflag(id) +char *id; +{ + register char *cp = capab; + + if (cp == (char *)NULL || id == (char *)NULL) return(-1); + + for (;;) { + while (*cp++ != ':') + if (cp[-1] == '\0') return(0); + + while (isspace(*cp)) cp++; + + if (strncmp(cp, id, 2) == 0) return(1); + } +} + + +/* + * tgetstr - get the string capability corresponding to id and place + * it in area (advancing area at same time). Expand escape sequences + * etc. Returns the string, or NULL if it can't do it. + */ + +char *tgetstr(id, area) +char *id; +char **area; +{ + register char *cp = capab; + register char *wsp = *area; /* workspace pointer */ + + if (cp == (char *)NULL || id == (char *)NULL) return((char *)NULL); + + for (;;) { + while (*cp++ != ':') + if (cp[-1] == '\0') return((char *)NULL); + + while (isspace(*cp)) cp++; + + if (strncmp(cp, id, 2) == 0 && cp[2] == '=') { + for (cp += 3; *cp && *cp != ':'; wsp++, cp++) switch (*cp) { + case '^': + *wsp = *++cp - '@'; + break; + + case '\\': + switch (*++cp) { + case 'E': + *wsp = '\033'; + break; + case 'n': + *wsp = '\n'; + break; + case 'r': + *wsp = '\r'; + break; + case 't': + *wsp = '\t'; + break; + case 'b': + *wsp = '\b'; + break; + case 'f': + *wsp = '\f'; + break; + case '0': + case '1': + case '2': + case '3': + { + int i; + int t = 0; + for (i = 0; i < 3 && + isdigit(*cp); ++i, ++cp) + t = t * 8 + *cp - '0'; + *wsp = t; + cp--; + break; + } + default: + *wsp = *cp; + } + break; + + default: *wsp = *cp; + } + + *wsp++ = '\0'; + + { + char *ret = *area; + *area = wsp; + return(ret); + } + } + } /* end for(;;) */ +} + + + +/* + * tgoto - given the cursor motion string cm, make up the string + * for the cursor to go to (destcol, destline), and return the string. + * Returns "OOPS" if something's gone wrong, or the string otherwise. + */ + +char *tgoto(cm, destcol, destline) +char *cm; +int destcol; +int destline; +{ + PRIVATE char ret[24]; + char *rp = ret; + int incr = 0; + int argno = 0; + int numval; + + for (; *cm; cm++) { + if (*cm == '%') { + switch (*++cm) { + case 'i': incr = 1; break; + + case 'r': argno = 1; break; + + case '+': + numval = (argno == 0 ? destline : destcol); + *rp++ = numval + incr + *++cm; + argno = 1 - argno; + break; + + case '2': + numval = (argno == 0 ? destline : destcol); + numval = (numval + incr) % 100; + *rp++ = '0' + (numval / 10); + *rp++ = '0' + (numval % 10); + argno = 1 - argno; + break; + + case 'd': + numval = (argno == 0 ? destline : destcol); + numval = (numval + incr) % 1000; + if (numval > 99) *rp++ = '0' + (numval / 100); + if (numval > 9) *rp++ = '0' + (numval / 10) % 10; + *rp++ = '0' + (numval % 10); + argno = 1 - argno; + break; + + case '%': *rp++ = '%'; break; + + default: return("OOPS"); + } + + } else + *rp++ = *cm; + } + + *rp = '\0'; + return(ret); +} + + + +/* + * tputs - put the string cp out onto the terminal, using the function + * outc. This should do padding for the terminal, but I can't find a + * terminal that needs padding at the moment... + */ + +int tputs(cp, affcnt, outc) +register char *cp; +int affcnt; +_PROTOTYPE( void (*outc), (int ch)); +{ + if (cp == (char *)NULL) return(1); + /* Do any padding interpretation - left null for MINIX just now */ + while (*cp) (*outc) (*cp++); + return(1); +} diff --git a/lib/other/ttyname.c b/lib/other/ttyname.c new file mode 100755 index 000000000..16b421c3c --- /dev/null +++ b/lib/other/ttyname.c @@ -0,0 +1,53 @@ +/* ttyname.c POSIX 4.7.2 + * char *ttyname(int fildes); + * + * Determines name of a terminal device. + */ + +#include <lib.h> +#include <sys/stat.h> +#include <dirent.h> +#include <fcntl.h> +#include <stddef.h> +#include <string.h> +#include <unistd.h> + +PRIVATE char base[] = "/dev"; +PRIVATE char path[sizeof(base) + 1 + NAME_MAX]; /* extra 1 for '/' */ + +PUBLIC char *ttyname(fildes) +int fildes; +{ + DIR *devices; + struct dirent *entry; + struct stat tty_stat; + struct stat dev_stat; + + /* Simple first test: file descriptor must be a character device */ + if (fstat(fildes, &tty_stat) < 0 || !S_ISCHR(tty_stat.st_mode)) + return (char *) NULL; + + /* Open device directory for reading */ + if ((devices = opendir(base)) == (DIR *) NULL) + return (char *) NULL; + + /* Scan the entries for one that matches perfectly */ + while ((entry = readdir(devices)) != (struct dirent *) NULL) { + if (tty_stat.st_ino != entry->d_ino) + continue; + strcpy(path, base); + strcat(path, "/"); + strcat(path, entry->d_name); + if (stat(path, &dev_stat) < 0 || !S_ISCHR(dev_stat.st_mode)) + continue; + if (tty_stat.st_ino == dev_stat.st_ino && + tty_stat.st_dev == dev_stat.st_dev && + tty_stat.st_rdev == dev_stat.st_rdev) { + closedir(devices); + return path; + } + } + + closedir(devices); + return (char *) NULL; +} diff --git a/lib/other/ttyslot.c b/lib/other/ttyslot.c new file mode 100755 index 000000000..a3e9c98d4 --- /dev/null +++ b/lib/other/ttyslot.c @@ -0,0 +1,62 @@ +/* +ttyslot.c + +Return the index in the utmp file for the current user's terminal. The +current user's terminal is the first file descriptor in the range 0..2 +for which ttyname() returns a name. The index is the line number in the +/etc/ttytab file. 0 will be returned in case of an error. + +Created: Oct 11, 1992 by Philip Homburg +*/ + +#define _MINIX_SOURCE + +#include <sys/types.h> +#include <ttyent.h> +#include <string.h> +#include <unistd.h> + +int ttyslot() +{ + int slot; + + slot= fttyslot(0); + if (slot == 0) slot= fttyslot(1); + if (slot == 0) slot= fttyslot(2); + return slot; +} + +int fttyslot(fd) +int fd; +{ + char *tname; + int lineno; + struct ttyent *ttyp; + + tname= ttyname(fd); + if (tname == NULL) return 0; + + /* Assume that tty devices are in /dev */ + if (strncmp(tname, "/dev/", 5) != 0) + return 0; /* Malformed tty name. */ + tname += 5; + + /* Scan /etc/ttytab. */ + lineno= 1; + while ((ttyp= getttyent()) != NULL) + { + if (strcmp(tname, ttyp->ty_name) == 0) + { + endttyent(); + return lineno; + } + lineno++; + } + /* No match */ + endttyent(); + return 0; +} + +/* + * $PchHeader: /mount/hd2/minix/lib/misc/RCS/ttyslot.c,v 1.3 1994/12/22 13:49:12 philip Exp $ + */ diff --git a/lib/other/v8regerror.c b/lib/other/v8regerror.c new file mode 100755 index 000000000..71a149083 --- /dev/null +++ b/lib/other/v8regerror.c @@ -0,0 +1,15 @@ +/* regerror() - Default regexp error report Author: Kees J. Bot + * 12 Jun 1999 + * + * A better version of this routine should be supplied by the user in + * the program using regexps. + */ +#include <stdio.h> +#define const /* avoid "const poisoning" */ +#include <regexp.h> +#undef const + +void regerror(char *message) +{ + fprintf(stderr, "regexp error: %s\n", message); +} diff --git a/lib/other/v8regexp.c b/lib/other/v8regexp.c new file mode 100755 index 000000000..c4a7d412a --- /dev/null +++ b/lib/other/v8regexp.c @@ -0,0 +1,1075 @@ +/* regcomp and regexec -- regsub and regerror are elsewhere + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + * + * Beware that some of this code is subtly aware of the way operator + * precedence is structured in regular expressions. Serious changes in + * regular-expression syntax might require a total rethink. + * + * The third parameter to regexec was added by Martin C. Atkins. + * Andy Tanenbaum also made some changes. + */ + +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#define const /* avoid "const poisoning" */ +#include <regexp.h> +#undef const + +/* The first byte of the regexp internal "program" is actually this magic + * number; the start node begins in the second byte. + */ +#define MAGIC 0234 + +/* The "internal use only" fields in regexp.h are present to pass info from + * compile to execute that permits the execute phase to run lots faster on + * simple cases. They are: + * + * regstart char that must begin a match; '\0' if none obvious + * reganch is the match anchored (at beginning-of-line only)? + * regmust string (pointer into program) that match must include, or NULL + * regmlen length of regmust string + * + * Regstart and reganch permit very fast decisions on suitable starting points + * for a match, cutting down the work a lot. Regmust permits fast rejection + * of lines that cannot possibly match. The regmust tests are costly enough + * that regcomp() supplies a regmust only if the r.e. contains something + * potentially expensive (at present, the only such thing detected is * or + + * at the start of the r.e., which can involve a lot of backup). Regmlen is + * supplied because the test in regexec() needs it and regcomp() is computing + * it anyway. + */ + +/* Structure for regexp "program". This is essentially a linear encoding + * of a nondeterministic finite-state machine (aka syntax charts or + * "railroad normal form" in parsing technology). Each node is an opcode + * plus a "next" pointer, possibly plus an operand. "Next" pointers of + * all nodes except BRANCH implement concatenation; a "next" pointer with + * a BRANCH on both ends of it is connecting two alternatives. (Here we + * have one of the subtle syntax dependencies: an individual BRANCH (as + * opposed to a collection of them) is never concatenated with anything + * because of operator precedence.) The operand of some types of node is + * a literal string; for others, it is a node leading into a sub-FSM. In + * particular, the operand of a BRANCH node is the first node of the branch. + * (NB this is *not* a tree structure: the tail of the branch connects + * to the thing following the set of BRANCHes.) The opcodes are: + */ + +/* Definition number opnd? meaning */ +#define END 0 /* no End of program. */ +#define BOL 1 /* no Match "" at beginning of line. */ +#define EOL 2 /* no Match "" at end of line. */ +#define ANY 3 /* no Match any one character. */ +#define ANYOF 4 /* str Match any character in this string. */ +#define ANYBUT 5 /* str Match any character not in this + * string. */ +#define BRANCH 6 /* node Match this alternative, or the + * next... */ +#define BACK 7 /* no Match "", "next" ptr points backward. */ +#define EXACTLY 8 /* str Match this string. */ +#define NOTHING 9 /* no Match empty string. */ +#define STAR 10 /* node Match this (simple) thing 0 or more + * times. */ +#define PLUS 11 /* node Match this (simple) thing 1 or more + * times. */ +#define OPEN 20 /* no Mark this point in input as start of + * #n. */ + /* OPEN+1 is number 1, etc. */ +#define CLOSE 30 /* no Analogous to OPEN. */ + +/* Opcode notes: + * + * BRANCH The set of branches constituting a single choice are hooked + * together with their "next" pointers, since precedence prevents + * anything being concatenated to any individual branch. The + * "next" pointer of the last BRANCH in a choice points to the + * thing following the whole choice. This is also where the + * final "next" pointer of each individual branch points; each + * branch starts with the operand node of a BRANCH node. + * + * BACK Normal "next" pointers all implicitly point forward; BACK + * exists to make loop structures possible. + * + * STAR,PLUS '?', and complex '*' and '+', are implemented as circular + * BRANCH structures using BACK. Simple cases (one character + * per match) are implemented with STAR and PLUS for speed + * and to minimize recursive plunges. + * + * OPEN,CLOSE ...are numbered at compile time. + */ + +/* A node is one char of opcode followed by two chars of "next" pointer. + * "Next" pointers are stored as two 8-bit pieces, high order first. The + * value is a positive offset from the opcode of the node containing it. + * An operand, if any, simply follows the node. (Note that much of the + * code generation knows about this implicit relationship.) + * + * Using two bytes for the "next" pointer is vast overkill for most things, + * but allows patterns to get big without disasters. + */ +#define OP(p) (*(p)) +#define NEXT(p) (((*((p)+1)&0377)<<8) + (*((p)+2)&0377)) +#define OPERAND(p) ((p) + 3) + +/* Utility definitions. + */ +#ifndef CHARBITS +#define UCHARAT(p) ((int)*(unsigned char *)(p)) +#else +#define UCHARAT(p) ((int)*(p)&CHARBITS) +#endif + +#define CFAIL(m) { regerror(m); return((char *)NULL); } +#define RFAIL(m) { regerror(m); return((regexp *)NULL); } +#define ISMULT(c) ((c) == '*' || (c) == '+' || (c) == '?') +#define META "^$.[()|?+*\\" + +/* Flags to be passed up and down. + */ +#define HASWIDTH 01 /* Known never to match null string. */ +#define SIMPLE 02 /* Simple enough to be STAR/PLUS operand. */ +#define SPSTART 04 /* Starts with * or +. */ +#define WORST 0 /* Worst case. */ + +/* Global work variables for regcomp(). + */ +static char *regparse; /* Input-scan pointer. */ +static int regnpar; /* () count. */ +static char regdummy; +static char *regcode; /* Code-emit pointer; ®dummy = don't. */ +static long regsize; /* Code size. */ + +/* Forward declarations for regcomp()'s friends. + */ +static char *reg(int paren, int *flagp); +static char *regbranch(int *flagp); +static char *regpiece(int *flagp); +static char *regatom(int *flagp); +static char *regnode(int op); +static char *regnext(char *p); +static void regc(int b); +static void reginsert(int op, char *opnd); +static void regtail(char *p, char *val); +static void regoptail(char *p, char *val); + +/* + - regcomp - compile a regular expression into internal code + * + * We can't allocate space until we know how big the compiled form will be, + * but we can't compile it (and thus know how big it is) until we've got a + * place to put the code. So we cheat: we compile it twice, once with code + * generation turned off and size counting turned on, and once "for real". + * This also means that we don't allocate space until we are sure that the + * thing really will compile successfully, and we never have to move the + * code and thus invalidate pointers into it. (Note that it has to be in + * one piece because free() must be able to free it all.) + * + * Beware that the optimization-preparation code in here knows about some + * of the structure of the compiled regexp. + */ +regexp *regcomp(exp) +char *exp; +{ + register regexp *r; + register char *scan; + register char *longest; + register int len; + int flags; + + if (exp == (char *)NULL) RFAIL("NULL argument"); + + /* First pass: determine size, legality. */ + regparse = exp; + regnpar = 1; + regsize = 0L; + regcode = ®dummy; + regc(MAGIC); + if (reg(0, &flags) == (char *)NULL) return((regexp *)NULL); + + /* Small enough for pointer-storage convention? */ + if (regsize >= 32767L) /* Probably could be 65535L. */ + RFAIL("regexp too big"); + + /* Allocate space. */ + r = (regexp *) malloc(sizeof(regexp) + (unsigned) regsize); + if (r == (regexp *)NULL) RFAIL("out of space"); + + /* Second pass: emit code. */ + regparse = exp; + regnpar = 1; + regcode = r->program; + regc(MAGIC); + if (reg(0, &flags) == (char *)NULL) return((regexp *)NULL); + + /* Dig out information for optimizations. */ + r->regstart = '\0'; /* Worst-case defaults. */ + r->reganch = 0; + r->regmust = (char *)NULL; + r->regmlen = 0; + scan = r->program + 1; /* First BRANCH. */ + if (OP(regnext(scan)) == END) { /* Only one top-level choice. */ + scan = OPERAND(scan); + + /* Starting-point info. */ + if (OP(scan) == EXACTLY) + r->regstart = *OPERAND(scan); + else if (OP(scan) == BOL) + r->reganch++; + + /* If there's something expensive in the r.e., find the + * longest literal string that must appear and make it the + * regmust. Resolve ties in favor of later strings, since + * the regstart check works with the beginning of the r.e. + * and avoiding duplication strengthens checking. Not a + * strong reason, but sufficient in the absence of others. */ + if (flags & SPSTART) { + longest = (char *)NULL; + len = 0; + for (; scan != (char *)NULL; scan = regnext(scan)) + if (OP(scan) == EXACTLY && strlen(OPERAND(scan)) >= len) { + longest = OPERAND(scan); + len = strlen(OPERAND(scan)); + } + r->regmust = longest; + r->regmlen = len; + } + } + return(r); +} + +/* + - reg - regular expression, i.e. main body or parenthesized thing + * + * Caller must absorb opening parenthesis. + * + * Combining parenthesis handling with the base level of regular expression + * is a trifle forced, but the need to tie the tails of the branches to what + * follows makes it hard to avoid. + */ +static char *reg(paren, flagp) +int paren; /* Parenthesized? */ +int *flagp; +{ + register char *ret; + register char *br; + register char *ender; + register int parno; + int flags; + + *flagp = HASWIDTH; /* Tentatively. */ + + /* Make an OPEN node, if parenthesized. */ + if (paren) { + if (regnpar >= NSUBEXP) CFAIL("too many ()"); + parno = regnpar; + regnpar++; + ret = regnode(OPEN + parno); + } else { + parno = 0; /* not actually used, keep compiler quiet */ + ret = (char *)NULL; + } + + /* Pick up the branches, linking them together. */ + br = regbranch(&flags); + if (br == (char *)NULL) return((char *)NULL); + if (ret != (char *)NULL) + regtail(ret, br); /* OPEN -> first. */ + else + ret = br; + if (!(flags & HASWIDTH)) *flagp &= ~HASWIDTH; + *flagp |= flags & SPSTART; + while (*regparse == '|') { + regparse++; + br = regbranch(&flags); + if (br == (char *)NULL) return((char *)NULL); + regtail(ret, br); /* BRANCH -> BRANCH. */ + if (!(flags & HASWIDTH)) *flagp &= ~HASWIDTH; + *flagp |= flags & SPSTART; + } + + /* Make a closing node, and hook it on the end. */ + ender = regnode((paren) ? CLOSE + parno : END); + regtail(ret, ender); + + /* Hook the tails of the branches to the closing node. */ + for (br = ret; br != (char *)NULL; br = regnext(br)) regoptail(br, ender); + + /* Check for proper termination. */ + if (paren && *regparse++ != ')') { + CFAIL("unmatched ()"); + } else if (!paren && *regparse != '\0') { + if (*regparse == ')') { + CFAIL("unmatched ()"); + } else + CFAIL("junk on end"); /* "Can't happen". */ + /* NOTREACHED */ + } + return(ret); +} + +/* + - regbranch - one alternative of an | operator + * + * Implements the concatenation operator. + */ +static char *regbranch(flagp) +int *flagp; +{ + register char *ret; + register char *chain; + register char *latest; + int flags; + + *flagp = WORST; /* Tentatively. */ + + ret = regnode(BRANCH); + chain = (char *)NULL; + while (*regparse != '\0' && *regparse != '|' && *regparse != ')') { + latest = regpiece(&flags); + if (latest == (char *)NULL) return((char *)NULL); + *flagp |= flags & HASWIDTH; + if (chain == (char *)NULL) /* First piece. */ + *flagp |= flags & SPSTART; + else + regtail(chain, latest); + chain = latest; + } + if (chain == (char *)NULL) /* Loop ran zero times. */ + regnode(NOTHING); + + return(ret); +} + +/* + - regpiece - something followed by possible [*+?] + * + * Note that the branching code sequences used for ? and the general cases + * of * and + are somewhat optimized: they use the same NOTHING node as + * both the endmarker for their branch list and the body of the last branch. + * It might seem that this node could be dispensed with entirely, but the + * endmarker role is not redundant. + */ +static char *regpiece(flagp) +int *flagp; +{ + register char *ret; + register char op; + register char *next; + int flags; + + ret = regatom(&flags); + if (ret == (char *)NULL) return((char *)NULL); + + op = *regparse; + if (!ISMULT(op)) { + *flagp = flags; + return(ret); + } + if (!(flags & HASWIDTH) && op != '?') CFAIL("*+ operand could be empty"); + *flagp = (op != '+') ? (WORST | SPSTART) : (WORST | HASWIDTH); + + if (op == '*' && (flags & SIMPLE)) + reginsert(STAR, ret); + else if (op == '*') { + /* Emit x* as (x&|), where & means "self". */ + reginsert(BRANCH, ret); /* Either x */ + regoptail(ret, regnode(BACK)); /* and loop */ + regoptail(ret, ret); /* back */ + regtail(ret, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '+' && (flags & SIMPLE)) + reginsert(PLUS, ret); + else if (op == '+') { + /* Emit x+ as x(&|), where & means "self". */ + next = regnode(BRANCH); /* Either */ + regtail(ret, next); + regtail(regnode(BACK), ret); /* loop back */ + regtail(next, regnode(BRANCH)); /* or */ + regtail(ret, regnode(NOTHING)); /* null. */ + } else if (op == '?') { + /* Emit x? as (x|) */ + reginsert(BRANCH, ret); /* Either x */ + regtail(ret, regnode(BRANCH)); /* or */ + next = regnode(NOTHING);/* null. */ + regtail(ret, next); + regoptail(ret, next); + } + regparse++; + if (ISMULT(*regparse)) CFAIL("nested *?+"); + + return(ret); +} + +/* + - regatom - the lowest level + * + * Optimization: gobbles an entire sequence of ordinary characters so that + * it can turn them into a single node, which is smaller to store and + * faster to run. Backslashed characters are exceptions, each becoming a + * separate node; the code is simpler that way and it's not worth fixing. + */ +static char *regatom(flagp) +int *flagp; +{ + register char *ret; + int flags; + + *flagp = WORST; /* Tentatively. */ + + switch (*regparse++) { + case '^': ret = regnode(BOL); break; + case '$': ret = regnode(EOL); break; + case '.': + ret = regnode(ANY); + *flagp |= HASWIDTH | SIMPLE; + break; + case '[':{ + register int class; + register int classend; + + if (*regparse == '^') { /* Complement of range. */ + ret = regnode(ANYBUT); + regparse++; + } else + ret = regnode(ANYOF); + if (*regparse == ']' || *regparse == '-') regc(*regparse++); + while (*regparse != '\0' && *regparse != ']') { + if (*regparse == '-') { + regparse++; + if (*regparse == ']' || *regparse == '\0') + regc('-'); + else { + class = UCHARAT(regparse - 2) + 1; + classend = UCHARAT(regparse); + if (class > classend + 1) + CFAIL("invalid [] range"); + for (; class <= classend; class++) + regc(class); + regparse++; + } + } else + regc(*regparse++); + } + regc('\0'); + if (*regparse != ']') CFAIL("unmatched []"); + regparse++; + *flagp |= HASWIDTH | SIMPLE; + } + break; + case '(': + ret = reg(1, &flags); + if (ret == (char *)NULL) return((char *)NULL); + *flagp |= flags & (HASWIDTH | SPSTART); + break; + case '\0': + case '|': + case ')': + CFAIL("internal urp"); /* Supposed to be caught earlier. */ + break; + case '?': + case '+': + case '*': CFAIL("?+* follows nothing"); break; + case '\\': + if (*regparse == '\0') CFAIL("trailing \\"); + ret = regnode(EXACTLY); + regc(*regparse++); + regc('\0'); + *flagp |= HASWIDTH | SIMPLE; + break; + default:{ + register int len; + register char ender; + + regparse--; + len = strcspn(regparse, META); + if (len <= 0) CFAIL("internal disaster"); + ender = *(regparse + len); + if (len > 1 && ISMULT(ender)) + len--; /* Back off clear of ?+* operand. */ + *flagp |= HASWIDTH; + if (len == 1) *flagp |= SIMPLE; + ret = regnode(EXACTLY); + while (len > 0) { + regc(*regparse++); + len--; + } + regc('\0'); + } + break; + } + + return(ret); +} + +/* + - regnode - emit a node + */ +static char *regnode(op) +char op; +{ + register char *ret; + register char *ptr; + + ret = regcode; + if (ret == ®dummy) { + regsize += 3; + return(ret); + } + ptr = ret; + *ptr++ = op; + *ptr++ = '\0'; /* Null "next" pointer. */ + *ptr++ = '\0'; + regcode = ptr; + + return(ret); +} + +/* + - regc - emit (if appropriate) a byte of code + */ +static void regc(b) +char b; +{ + if (regcode != ®dummy) + *regcode++ = b; + else + regsize++; +} + +/* + - reginsert - insert an operator in front of already-emitted operand + * + * Means relocating the operand. + */ +static void reginsert(op, opnd) +char op; +char *opnd; +{ + register char *src; + register char *dst; + register char *place; + + if (regcode == ®dummy) { + regsize += 3; + return; + } + src = regcode; + regcode += 3; + dst = regcode; + while (src > opnd) *--dst = *--src; + + place = opnd; /* Op node, where operand used to be. */ + *place++ = op; + *place++ = '\0'; + *place++ = '\0'; +} + +/* + - regtail - set the next-pointer at the end of a node chain + */ +static void regtail(p, val) +char *p; +char *val; +{ + register char *scan; + register char *temp; + register int offset; + + if (p == ®dummy) return; + + /* Find last node. */ + scan = p; + for (;;) { + temp = (char *)regnext(scan); + if (temp == (char *)NULL) break; + scan = temp; + } + + if (OP(scan) == BACK) + offset = scan - val; + else + offset = val - scan; + *(scan + 1) = (offset >> 8) & 0377; + *(scan + 2) = offset & 0377; +} + +/* + - regoptail - regtail on operand of first argument; nop if operandless + */ +static void regoptail(p, val) +char *p; +char *val; +{ + /* "Operandless" and "op != BRANCH" are synonymous in practice. */ + if (p == (char *)NULL || p == ®dummy || OP(p) != BRANCH) return; + regtail(OPERAND(p), val); +} + +/* regexec and friends + */ + +/* Global work variables for regexec(). + */ +static char *reginput; /* String-input pointer. */ +static char *regbol; /* Beginning of input, for ^ check. */ +static char **regstartp; /* Pointer to startp array. */ +static char **regendp; /* Ditto for endp. */ + +/* Forwards. + */ +static int regtry(regexp *prog, char *string); +static int regmatch(char *prog); +static int regrepeat(char *p); + +#ifdef DEBUG +int regnarrate = 0; +void regdump(); +static char *regprop(char *op); +#endif + +/* + - regexec - match a regexp against a string + */ +int regexec(prog, string, bolflag) +register regexp *prog; +register char *string; +int bolflag; +{ + register char *s; + + /* Be paranoid... */ + if (prog == (regexp *)NULL || string == (char *)NULL) { + regerror("NULL parameter"); + return(0); + } + + /* Check validity of program. */ + if (UCHARAT(prog->program) != MAGIC) { + regerror("corrupted program"); + return(0); + } + + /* If there is a "must appear" string, look for it. */ + if (prog->regmust != (char *)NULL) { + s = string; + while ((s = strchr(s, prog->regmust[0])) != (char *)NULL) { + if (strncmp(s, prog->regmust, prog->regmlen) == 0) + break; /* Found it. */ + s++; + } + if (s == (char *)NULL) /* Not present. */ + return(0); + } + + /* Mark beginning of line for ^ . */ + if (bolflag) + regbol = string; + else + regbol = (char *)NULL; + + /* Simplest case: anchored match need be tried only once. */ + if (prog->reganch) return(regtry(prog, string)); + + /* Messy cases: unanchored match. */ + s = string; + if (prog->regstart != '\0') /* We know what char it must start with. */ + while ((s = strchr(s, prog->regstart)) != (char *)NULL) { + if (regtry(prog, s)) return(1); + s++; + } + else + /* We don't -- general case. */ + do { + if (regtry(prog, s)) return(1); + } while (*s++ != '\0'); + + /* Failure. */ + return(0); +} + +/* + - regtry - try match at specific point + */ +static int regtry(prog, string) /* 0 failure, 1 success */ +regexp *prog; +char *string; +{ + register int i; + register char **sp; + register char **ep; + + reginput = string; + regstartp = prog->startp; + regendp = prog->endp; + + sp = prog->startp; + ep = prog->endp; + for (i = NSUBEXP; i > 0; i--) { + *sp++ = (char *)NULL; + *ep++ = (char *)NULL; + } + if (regmatch(prog->program + 1)) { + prog->startp[0] = string; + prog->endp[0] = reginput; + return(1); + } else + return(0); +} + +/* + - regmatch - main matching routine + * + * Conceptually the strategy is simple: check to see whether the current + * node matches, call self recursively to see whether the rest matches, + * and then act accordingly. In practice we make some effort to avoid + * recursion, in particular by going through "ordinary" nodes (that don't + * need to know whether the rest of the match failed) by a loop instead of + * by recursion. + */ +static int regmatch(prog) /* 0 failure, 1 success */ +char *prog; +{ + register char *scan; /* Current node. */ + char *next; /* Next node. */ + + scan = prog; +#ifdef DEBUG + if (scan != (char *)NULL && regnarrate) fprintf(stderr, "%s(\n", regprop(scan)); +#endif + while (scan != (char *)NULL) { +#ifdef DEBUG + if (regnarrate) fprintf(stderr, "%s...\n", regprop(scan)); +#endif + next = regnext(scan); + + switch (OP(scan)) { + case BOL: + if (reginput != regbol) return(0); + break; + case EOL: + if (*reginput != '\0') return(0); + break; + case ANY: + if (*reginput == '\0') return(0); + reginput++; + break; + case EXACTLY:{ + register int len; + register char *opnd; + + opnd = OPERAND(scan); + /* Inline the first character, for speed. */ + if (*opnd != *reginput) return(0); + len = strlen(opnd); + if (len > 1 && strncmp(opnd, reginput, len) != 0) + return(0); + reginput += len; + } + break; + case ANYOF: + if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) == (char *)NULL) + return(0); + reginput++; + break; + case ANYBUT: + if (*reginput == '\0' || strchr(OPERAND(scan), *reginput) != (char *)NULL) + return(0); + reginput++; + break; + case NOTHING: + break; + case BACK: + break; + case OPEN + 1: + case OPEN + 2: + case OPEN + 3: + case OPEN + 4: + case OPEN + 5: + case OPEN + 6: + case OPEN + 7: + case OPEN + 8: + case OPEN + 9:{ + register int no; + register char *save; + + no = OP(scan) - OPEN; + save = reginput; + + if (regmatch(next)) { + /* Don't set startp if some later + * invocation of the same parentheses + * already has. */ + if (regstartp[no] == (char *)NULL) + regstartp[no] = save; + return(1); + } else + return(0); + } + break; + case CLOSE + 1: + case CLOSE + 2: + case CLOSE + 3: + case CLOSE + 4: + case CLOSE + 5: + case CLOSE + 6: + case CLOSE + 7: + case CLOSE + 8: + case CLOSE + 9:{ + register int no; + register char *save; + + no = OP(scan) - CLOSE; + save = reginput; + + if (regmatch(next)) { + /* Don't set endp if some later + * invocation of the same parentheses + * already has. */ + if (regendp[no] == (char *)NULL) regendp[no] = save; + return(1); + } else + return(0); + } + break; + case BRANCH:{ + register char *save; + + if (OP(next) != BRANCH) /* No choice. */ + next = OPERAND(scan); /* Avoid recursion. */ + else { + do { + save = reginput; + if (regmatch(OPERAND(scan))) + return(1); + reginput = save; + scan = regnext(scan); + } while (scan != (char *)NULL && OP(scan) == BRANCH); + return(0); + /* NOTREACHED */ + } + } + break; + case STAR: + case PLUS:{ + register char nextch; + register int no; + register char *save; + register int min; + + /* Lookahead to avoid useless match attempts + * when we know what character comes next. */ + nextch = '\0'; + if (OP(next) == EXACTLY) nextch = *OPERAND(next); + min = (OP(scan) == STAR) ? 0 : 1; + save = reginput; + no = regrepeat(OPERAND(scan)); + while (no >= min) { + /* If it could work, try it. */ + if (nextch == '\0' || *reginput == nextch) + if (regmatch(next)) return(1); + /* Couldn't or didn't -- back up. */ + no--; + reginput = save + no; + } + return(0); + } + break; + case END: + return(1); /* Success! */ + break; + default: + regerror("memory corruption"); + return(0); + break; + } + + scan = next; + } + + /* We get here only if there's trouble -- normally "case END" is the + * terminating point. */ + regerror("corrupted pointers"); + return(0); +} + +/* + - regrepeat - repeatedly match something simple, report how many + */ +static int regrepeat(p) +char *p; +{ + register int count = 0; + register char *scan; + register char *opnd; + + scan = reginput; + opnd = OPERAND(p); + switch (OP(p)) { + case ANY: + count = strlen(scan); + scan += count; + break; + case EXACTLY: + while (*opnd == *scan) { + count++; + scan++; + } + break; + case ANYOF: + while (*scan != '\0' && strchr(opnd, *scan) != (char *)NULL) { + count++; + scan++; + } + break; + case ANYBUT: + while (*scan != '\0' && strchr(opnd, *scan) == (char *)NULL) { + count++; + scan++; + } + break; + default: /* Oh dear. Called inappropriately. */ + regerror("internal foulup"); + count = 0; /* Best compromise. */ + break; + } + reginput = scan; + + return(count); +} + +/* + - regnext - dig the "next" pointer out of a node + */ +static char *regnext(p) +register char *p; +{ + register int offset; + + if (p == ®dummy) return((char *)NULL); + + offset = NEXT(p); + if (offset == 0) return((char *)NULL); + + if (OP(p) == BACK) + return(p - offset); + else + return(p + offset); +} + +#ifdef DEBUG + +static char *regprop(); + +/* + - regdump - dump a regexp onto stdout in vaguely comprehensible form + */ +void regdump(r) +regexp *r; +{ + register char *s; + register char op = EXACTLY; /* Arbitrary non-END op. */ + register char *next; + + s = r->program + 1; + while (op != END) { /* While that wasn't END last time... */ + op = OP(s); + printf("%2d%s", (int) (s - r->program), regprop(s)); /* Where, what. */ + next = regnext(s); + if (next == (char *)NULL) /* Next ptr. */ + printf("(0)"); + else + printf("(%d)", (int) (s - r->program) + (int) (next - s)); + s += 3; + if (op == ANYOF || op == ANYBUT || op == EXACTLY) { + /* Literal string, where present. */ + while (*s != '\0') { + putchar(*s); + s++; + } + s++; + } + putchar('\n'); + } + + /* Header fields of interest. */ + if (r->regstart != '\0') printf("start `%c' ", r->regstart); + if (r->reganch) printf("anchored "); + if (r->regmust != (char *)NULL) printf("must have \"%s\"", r->regmust); + printf("\n"); +} + +/* + - regprop - printable representation of opcode + */ +static char *regprop(op) +char *op; +{ + register char *p; + static char buf[50]; + + (void) strcpy(buf, ":"); + + switch (OP(op)) { + case BOL: p = "BOL"; break; + case EOL: p = "EOL"; break; + case ANY: p = "ANY"; break; + case ANYOF: p = "ANYOF"; break; + case ANYBUT: p = "ANYBUT"; break; + case BRANCH: p = "BRANCH"; break; + case EXACTLY: p = "EXACTLY"; break; + case NOTHING: p = "NOTHING"; break; + case BACK: p = "BACK"; break; + case END: p = "END"; break; + case OPEN + 1: + case OPEN + 2: + case OPEN + 3: + case OPEN + 4: + case OPEN + 5: + case OPEN + 6: + case OPEN + 7: + case OPEN + 8: + case OPEN + 9: + sprintf(buf + strlen(buf), "OPEN%d", OP(op) - OPEN); + p = (char *)NULL; + break; + case CLOSE + 1: + case CLOSE + 2: + case CLOSE + 3: + case CLOSE + 4: + case CLOSE + 5: + case CLOSE + 6: + case CLOSE + 7: + case CLOSE + 8: + case CLOSE + 9: + sprintf(buf + strlen(buf), "CLOSE%d", OP(op) - CLOSE); + p = (char *)NULL; + break; + case STAR: p = "STAR"; break; + case PLUS: p = "PLUS"; break; + default: regerror("corrupted opcode"); p = (char *) NULL; break; + } + if (p != (char *)NULL) (void) strcat(buf, p); + return(buf); +} + +#endif + +/* + * $PchId: regexp.c,v 1.4 1996/02/22 09:03:07 philip Exp $ + */ diff --git a/lib/other/v8regsub.c b/lib/other/v8regsub.c new file mode 100755 index 000000000..c2b7819a7 --- /dev/null +++ b/lib/other/v8regsub.c @@ -0,0 +1,90 @@ +/* regsub + * + * Copyright (c) 1986 by University of Toronto. + * Written by Henry Spencer. Not derived from licensed software. + * + * Permission is granted to anyone to use this software for any + * purpose on any computer system, and to redistribute it freely, + * subject to the following restrictions: + * + * 1. The author is not responsible for the consequences of use of + * this software, no matter how awful, even if they arise + * from defects in it. + * + * 2. The origin of this software must not be misrepresented, either + * by explicit claim or by omission. + * + * 3. Altered versions must be plainly marked as such, and must not + * be misrepresented as being the original software. + */ + +#include <string.h> +#include <stdio.h> +#define const /* avoid "const poisoning" */ +#include <regexp.h> +#undef const + +/* The first byte of the regexp internal "program" is actually this magic + * number; the start node begins in the second byte. + */ +#define MAGIC 0234 + +#define CHARBITS 0377 +#ifndef CHARBITS +#define UCHARAT(p) ((int)*(unsigned char *)(p)) +#else +#define UCHARAT(p) ((int)*(p)&CHARBITS) +#endif + +/* + - regsub - perform substitutions after a regexp match + */ +void regsub(prog, source, dest) +regexp *prog; +char *source; +char *dest; +{ + register char *src; + register char *dst; + register char c; + register int no; + register int len; + + if (prog == (regexp *)NULL || source == (char *)NULL || dest == (char *)NULL) { + regerror("NULL parm to regsub"); + return; + } + if (UCHARAT(prog->program) != MAGIC) { + regerror("damaged regexp fed to regsub"); + return; + } + src = source; + dst = dest; + while ((c = *src++) != '\0') { + if (c == '&') + no = 0; + else if (c == '\\' && '0' <= *src && *src <= '9') + no = *src++ - '0'; + else + no = -1; + + if (no < 0) { /* Ordinary character. */ + if (c == '\\' && (*src == '\\' || *src == '&')) c = *src++; + *dst++ = c; + } else + if (prog->startp[no] != (char *)NULL && prog->endp[no] != (char *)NULL) { + len = (int) (prog->endp[no] - prog->startp[no]); + strncpy(dst, prog->startp[no], len); + dst += len; + if (len != 0 && *(dst - 1) == '\0') { /* strncpy hit NUL. */ + regerror("damaged match string"); + return; + } + } + } + *dst++ = '\0'; +} + +/* + * $PchId: regsub.c,v 1.3 1995/11/27 20:18:16 philip Exp $ + */ diff --git a/lib/posix/Makefile b/lib/posix/Makefile new file mode 100755 index 000000000..10171c1b4 --- /dev/null +++ b/lib/posix/Makefile @@ -0,0 +1,361 @@ +# Makefile for lib/posix. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../libc.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(__exit.o) \ + $(LIBRARY)(_access.o) \ + $(LIBRARY)(_alarm.o) \ + $(LIBRARY)(_cfgetispeed.o) \ + $(LIBRARY)(_cfgetospeed.o) \ + $(LIBRARY)(_cfsetispeed.o) \ + $(LIBRARY)(_cfsetospeed.o) \ + $(LIBRARY)(_chdir.o) \ + $(LIBRARY)(_chmod.o) \ + $(LIBRARY)(_chown.o) \ + $(LIBRARY)(_chroot.o) \ + $(LIBRARY)(_close.o) \ + $(LIBRARY)(_closedir.o) \ + $(LIBRARY)(_creat.o) \ + $(LIBRARY)(_dup.o) \ + $(LIBRARY)(_dup2.o) \ + $(LIBRARY)(_execl.o) \ + $(LIBRARY)(_execle.o) \ + $(LIBRARY)(_execlp.o) \ + $(LIBRARY)(_execv.o) \ + $(LIBRARY)(_execve.o) \ + $(LIBRARY)(_execvp.o) \ + $(LIBRARY)(_fcntl.o) \ + $(LIBRARY)(_fork.o) \ + $(LIBRARY)(_fpathconf.o) \ + $(LIBRARY)(_fstat.o) \ + $(LIBRARY)(_fstatfs.o) \ + $(LIBRARY)(_getcwd.o) \ + $(LIBRARY)(_getegid.o) \ + $(LIBRARY)(_geteuid.o) \ + $(LIBRARY)(_getgid.o) \ + $(LIBRARY)(_getgroups.o) \ + $(LIBRARY)(_getpgrp.o) \ + $(LIBRARY)(_getpid.o) \ + $(LIBRARY)(_getppid.o) \ + $(LIBRARY)(_getuid.o) \ + $(LIBRARY)(_ioctl.o) \ + $(LIBRARY)(_isatty.o) \ + $(LIBRARY)(_kill.o) \ + $(LIBRARY)(_link.o) \ + $(LIBRARY)(_lseek.o) \ + $(LIBRARY)(_mkdir.o) \ + $(LIBRARY)(_mkfifo.o) \ + $(LIBRARY)(_mknod.o) \ + $(LIBRARY)(_mktemp.o) \ + $(LIBRARY)(_mount.o) \ + $(LIBRARY)(_open.o) \ + $(LIBRARY)(_opendir.o) \ + $(LIBRARY)(_pathconf.o) \ + $(LIBRARY)(_pause.o) \ + $(LIBRARY)(_pipe.o) \ + $(LIBRARY)(_ptrace.o) \ + $(LIBRARY)(_read.o) \ + $(LIBRARY)(_readdir.o) \ + $(LIBRARY)(_rename.o) \ + $(LIBRARY)(_rewinddir.o) \ + $(LIBRARY)(_rmdir.o) \ + $(LIBRARY)(_setgid.o) \ + $(LIBRARY)(_setsid.o) \ + $(LIBRARY)(_setuid.o) \ + $(LIBRARY)(_sigaction.o) \ + $(LIBRARY)(_sigpending.o) \ + $(LIBRARY)(_sigprocmask.o) \ + $(LIBRARY)(_sigreturn.o) \ + $(LIBRARY)(_sigset.o) \ + $(LIBRARY)(_sigsetjmp.o) \ + $(LIBRARY)(_sigsuspend.o) \ + $(LIBRARY)(_sleep.o) \ + $(LIBRARY)(_stat.o) \ + $(LIBRARY)(_stime.o) \ + $(LIBRARY)(_sync.o) \ + $(LIBRARY)(_tcdrain.o) \ + $(LIBRARY)(_tcflow.o) \ + $(LIBRARY)(_tcflush.o) \ + $(LIBRARY)(_tcgetattr.o) \ + $(LIBRARY)(_tcsendbreak.o) \ + $(LIBRARY)(_tcsetattr.o) \ + $(LIBRARY)(_time.o) \ + $(LIBRARY)(_times.o) \ + $(LIBRARY)(_umask.o) \ + $(LIBRARY)(_umount.o) \ + $(LIBRARY)(_uname.o) \ + $(LIBRARY)(_unlink.o) \ + $(LIBRARY)(_utime.o) \ + $(LIBRARY)(_wait.o) \ + $(LIBRARY)(_waitpid.o) \ + $(LIBRARY)(_write.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(__exit.o): __exit.c + $(CC1) __exit.c + +$(LIBRARY)(_access.o): _access.c + $(CC1) _access.c + +$(LIBRARY)(_alarm.o): _alarm.c + $(CC1) _alarm.c + +$(LIBRARY)(_cfgetispeed.o): _cfgetispeed.c + $(CC1) _cfgetispeed.c + +$(LIBRARY)(_cfgetospeed.o): _cfgetospeed.c + $(CC1) _cfgetospeed.c + +$(LIBRARY)(_cfsetispeed.o): _cfsetispeed.c + $(CC1) _cfsetispeed.c + +$(LIBRARY)(_cfsetospeed.o): _cfsetospeed.c + $(CC1) _cfsetospeed.c + +$(LIBRARY)(_chdir.o): _chdir.c + $(CC1) _chdir.c + +$(LIBRARY)(_chmod.o): _chmod.c + $(CC1) _chmod.c + +$(LIBRARY)(_chown.o): _chown.c + $(CC1) _chown.c + +$(LIBRARY)(_chroot.o): _chroot.c + $(CC1) _chroot.c + +$(LIBRARY)(_close.o): _close.c + $(CC1) _close.c + +$(LIBRARY)(_closedir.o): _closedir.c + $(CC1) _closedir.c + +$(LIBRARY)(_creat.o): _creat.c + $(CC1) _creat.c + +$(LIBRARY)(_dup.o): _dup.c + $(CC1) _dup.c + +$(LIBRARY)(_dup2.o): _dup2.c + $(CC1) _dup2.c + +$(LIBRARY)(_execl.o): _execl.c + $(CC1) _execl.c + +$(LIBRARY)(_execle.o): _execle.c + $(CC1) _execle.c + +$(LIBRARY)(_execlp.o): _execlp.c + $(CC1) _execlp.c + +$(LIBRARY)(_execv.o): _execv.c + $(CC1) _execv.c + +$(LIBRARY)(_execve.o): _execve.c + $(CC1) _execve.c + +$(LIBRARY)(_execvp.o): _execvp.c + $(CC1) _execvp.c + +$(LIBRARY)(_fcntl.o): _fcntl.c + $(CC1) _fcntl.c + +$(LIBRARY)(_fork.o): _fork.c + $(CC1) _fork.c + +$(LIBRARY)(_fpathconf.o): _fpathconf.c + $(CC1) _fpathconf.c + +$(LIBRARY)(_fstat.o): _fstat.c + $(CC1) _fstat.c + +$(LIBRARY)(_fstatfs.o): _fstatfs.c + $(CC1) _fstatfs.c + +$(LIBRARY)(_getcwd.o): _getcwd.c + $(CC1) _getcwd.c + +$(LIBRARY)(_getegid.o): _getegid.c + $(CC1) _getegid.c + +$(LIBRARY)(_geteuid.o): _geteuid.c + $(CC1) _geteuid.c + +$(LIBRARY)(_getgid.o): _getgid.c + $(CC1) _getgid.c + +$(LIBRARY)(_getgroups.o): _getgroups.c + $(CC1) _getgroups.c + +$(LIBRARY)(_getpgrp.o): _getpgrp.c + $(CC1) _getpgrp.c + +$(LIBRARY)(_getpid.o): _getpid.c + $(CC1) _getpid.c + +$(LIBRARY)(_getppid.o): _getppid.c + $(CC1) _getppid.c + +$(LIBRARY)(_getuid.o): _getuid.c + $(CC1) _getuid.c + +$(LIBRARY)(_ioctl.o): _ioctl.c + $(CC1) _ioctl.c + +$(LIBRARY)(_isatty.o): _isatty.c + $(CC1) _isatty.c + +$(LIBRARY)(_kill.o): _kill.c + $(CC1) _kill.c + +$(LIBRARY)(_link.o): _link.c + $(CC1) _link.c + +$(LIBRARY)(_lseek.o): _lseek.c + $(CC1) _lseek.c + +$(LIBRARY)(_mkdir.o): _mkdir.c + $(CC1) _mkdir.c + +$(LIBRARY)(_mkfifo.o): _mkfifo.c + $(CC1) _mkfifo.c + +$(LIBRARY)(_mknod.o): _mknod.c + $(CC1) _mknod.c + +$(LIBRARY)(_mktemp.o): _mktemp.c + $(CC1) _mktemp.c + +$(LIBRARY)(_mount.o): _mount.c + $(CC1) _mount.c + +$(LIBRARY)(_open.o): _open.c + $(CC1) _open.c + +$(LIBRARY)(_opendir.o): _opendir.c + $(CC1) _opendir.c + +$(LIBRARY)(_pathconf.o): _pathconf.c + $(CC1) _pathconf.c + +$(LIBRARY)(_pause.o): _pause.c + $(CC1) _pause.c + +$(LIBRARY)(_pipe.o): _pipe.c + $(CC1) _pipe.c + +$(LIBRARY)(_ptrace.o): _ptrace.c + $(CC1) _ptrace.c + +$(LIBRARY)(_read.o): _read.c + $(CC1) _read.c + +$(LIBRARY)(_readdir.o): _readdir.c + $(CC1) _readdir.c + +$(LIBRARY)(_rename.o): _rename.c + $(CC1) _rename.c + +$(LIBRARY)(_rewinddir.o): _rewinddir.c + $(CC1) _rewinddir.c + +$(LIBRARY)(_rmdir.o): _rmdir.c + $(CC1) _rmdir.c + +$(LIBRARY)(_setgid.o): _setgid.c + $(CC1) _setgid.c + +$(LIBRARY)(_setsid.o): _setsid.c + $(CC1) _setsid.c + +$(LIBRARY)(_setuid.o): _setuid.c + $(CC1) _setuid.c + +$(LIBRARY)(_sigaction.o): _sigaction.c + $(CC1) _sigaction.c + +$(LIBRARY)(_sigpending.o): _sigpending.c + $(CC1) _sigpending.c + +$(LIBRARY)(_sigprocmask.o): _sigprocmask.c + $(CC1) _sigprocmask.c + +$(LIBRARY)(_sigreturn.o): _sigreturn.c + $(CC1) _sigreturn.c + +$(LIBRARY)(_sigset.o): _sigset.c + $(CC1) _sigset.c + +$(LIBRARY)(_sigsetjmp.o): _sigsetjmp.c + $(CC1) _sigsetjmp.c + +$(LIBRARY)(_sigsuspend.o): _sigsuspend.c + $(CC1) _sigsuspend.c + +$(LIBRARY)(_sleep.o): _sleep.c + $(CC1) _sleep.c + +$(LIBRARY)(_stat.o): _stat.c + $(CC1) _stat.c + +$(LIBRARY)(_stime.o): _stime.c + $(CC1) _stime.c + +$(LIBRARY)(_sync.o): _sync.c + $(CC1) _sync.c + +$(LIBRARY)(_tcdrain.o): _tcdrain.c + $(CC1) _tcdrain.c + +$(LIBRARY)(_tcflow.o): _tcflow.c + $(CC1) _tcflow.c + +$(LIBRARY)(_tcflush.o): _tcflush.c + $(CC1) _tcflush.c + +$(LIBRARY)(_tcgetattr.o): _tcgetattr.c + $(CC1) _tcgetattr.c + +$(LIBRARY)(_tcsendbreak.o): _tcsendbreak.c + $(CC1) _tcsendbreak.c + +$(LIBRARY)(_tcsetattr.o): _tcsetattr.c + $(CC1) _tcsetattr.c + +$(LIBRARY)(_time.o): _time.c + $(CC1) _time.c + +$(LIBRARY)(_times.o): _times.c + $(CC1) _times.c + +$(LIBRARY)(_umask.o): _umask.c + $(CC1) _umask.c + +$(LIBRARY)(_umount.o): _umount.c + $(CC1) _umount.c + +$(LIBRARY)(_uname.o): _uname.c /usr/include/minix/config.h + $(CC1) _uname.c + +$(LIBRARY)(_unlink.o): _unlink.c + $(CC1) _unlink.c + +$(LIBRARY)(_utime.o): _utime.c + $(CC1) _utime.c + +$(LIBRARY)(_wait.o): _wait.c + $(CC1) _wait.c + +$(LIBRARY)(_waitpid.o): _waitpid.c + $(CC1) _waitpid.c + +$(LIBRARY)(_write.o): _write.c + $(CC1) _write.c diff --git a/lib/posix/__exit.c b/lib/posix/__exit.c new file mode 100755 index 000000000..90f5ae41a --- /dev/null +++ b/lib/posix/__exit.c @@ -0,0 +1,12 @@ +#define _exit __exit +#include <lib.h> +#include <unistd.h> + +PUBLIC void _exit(status) +int status; +{ + message m; + + m.m1_i1 = status; + _syscall(MM, EXIT, &m); +} diff --git a/lib/posix/_access.c b/lib/posix/_access.c new file mode 100755 index 000000000..a39a4079a --- /dev/null +++ b/lib/posix/_access.c @@ -0,0 +1,14 @@ +#include <lib.h> +#define access _access +#include <unistd.h> + +PUBLIC int access(name, mode) +_CONST char *name; +int mode; +{ + message m; + + m.m3_i2 = mode; + _loadname(name, &m); + return(_syscall(FS, ACCESS, &m)); +} diff --git a/lib/posix/_alarm.c b/lib/posix/_alarm.c new file mode 100755 index 000000000..a0cf3d9c6 --- /dev/null +++ b/lib/posix/_alarm.c @@ -0,0 +1,12 @@ +#include <lib.h> +#define alarm _alarm +#include <unistd.h> + +PUBLIC unsigned int alarm(sec) +unsigned int sec; +{ + message m; + + m.m1_i1 = (int) sec; + return( (unsigned) _syscall(MM, ALARM, &m)); +} diff --git a/lib/posix/_cfgetispeed.c b/lib/posix/_cfgetispeed.c new file mode 100755 index 000000000..7bdaa63b6 --- /dev/null +++ b/lib/posix/_cfgetispeed.c @@ -0,0 +1,12 @@ +/* +posix/_cfgetispeed + +Created: June 11, 1993 by Philip Homburg +*/ + +#include <termios.h> + +speed_t _cfgetispeed(const struct termios *termios_p) +{ + return termios_p->c_ispeed; +} diff --git a/lib/posix/_cfgetospeed.c b/lib/posix/_cfgetospeed.c new file mode 100755 index 000000000..10bbda963 --- /dev/null +++ b/lib/posix/_cfgetospeed.c @@ -0,0 +1,12 @@ +/* +posix/_cfgetospeed + +Created: June 11, 1993 by Philip Homburg +*/ + +#include <termios.h> + +speed_t _cfgetospeed(const struct termios *termios_p) +{ + return termios_p->c_ospeed; +} diff --git a/lib/posix/_cfsetispeed.c b/lib/posix/_cfsetispeed.c new file mode 100755 index 000000000..76b4974e9 --- /dev/null +++ b/lib/posix/_cfsetispeed.c @@ -0,0 +1,13 @@ +/* +posix/_cfsetispeed + +Created: June 11, 1993 by Philip Homburg +*/ + +#include <termios.h> + +int _cfsetispeed(struct termios *termios_p, speed_t speed) +{ + termios_p->c_ispeed= speed; + return 0; +} diff --git a/lib/posix/_cfsetospeed.c b/lib/posix/_cfsetospeed.c new file mode 100755 index 000000000..ddc162d5d --- /dev/null +++ b/lib/posix/_cfsetospeed.c @@ -0,0 +1,13 @@ +/* +posix/_cfsetospeed + +Created: June 11, 1993 by Philip Homburg +*/ + +#include <termios.h> + +int _cfsetospeed(struct termios *termios_p, speed_t speed) +{ + termios_p->c_ospeed= speed; + return 0; +} diff --git a/lib/posix/_chdir.c b/lib/posix/_chdir.c new file mode 100755 index 000000000..566cde1e8 --- /dev/null +++ b/lib/posix/_chdir.c @@ -0,0 +1,12 @@ +#include <lib.h> +#define chdir _chdir +#include <unistd.h> + +PUBLIC int chdir(name) +_CONST char *name; +{ + message m; + + _loadname(name, &m); + return(_syscall(FS, CHDIR, &m)); +} diff --git a/lib/posix/_chmod.c b/lib/posix/_chmod.c new file mode 100755 index 000000000..efbf248c9 --- /dev/null +++ b/lib/posix/_chmod.c @@ -0,0 +1,14 @@ +#include <lib.h> +#define chmod _chmod +#include <sys/stat.h> + +PUBLIC int chmod(name, mode) +_CONST char *name; +Mode_t mode; +{ + message m; + + m.m3_i2 = mode; + _loadname(name, &m); + return(_syscall(FS, CHMOD, &m)); +} diff --git a/lib/posix/_chown.c b/lib/posix/_chown.c new file mode 100755 index 000000000..9b261f6e2 --- /dev/null +++ b/lib/posix/_chown.c @@ -0,0 +1,18 @@ +#include <lib.h> +#define chown _chown +#include <string.h> +#include <unistd.h> + +PUBLIC int chown(name, owner, grp) +_CONST char *name; +Uid_t owner; +Gid_t grp; +{ + message m; + + m.m1_i1 = strlen(name) + 1; + m.m1_i2 = owner; + m.m1_i3 = grp; + m.m1_p1 = (char *) name; + return(_syscall(FS, CHOWN, &m)); +} diff --git a/lib/posix/_chroot.c b/lib/posix/_chroot.c new file mode 100755 index 000000000..ca186966a --- /dev/null +++ b/lib/posix/_chroot.c @@ -0,0 +1,12 @@ +#include <lib.h> +#define chroot _chroot +#include <unistd.h> + +PUBLIC int chroot(name) +_CONST char *name; +{ + message m; + + _loadname(name, &m); + return(_syscall(FS, CHROOT, &m)); +} diff --git a/lib/posix/_close.c b/lib/posix/_close.c new file mode 100755 index 000000000..be4f32390 --- /dev/null +++ b/lib/posix/_close.c @@ -0,0 +1,12 @@ +#include <lib.h> +#define close _close +#include <unistd.h> + +PUBLIC int close(fd) +int fd; +{ + message m; + + m.m1_i1 = fd; + return(_syscall(FS, CLOSE, &m)); +} diff --git a/lib/posix/_closedir.c b/lib/posix/_closedir.c new file mode 100755 index 000000000..50fcec281 --- /dev/null +++ b/lib/posix/_closedir.c @@ -0,0 +1,24 @@ +/* closedir() Author: Kees J. Bot + * 24 Apr 1989 + */ +#define nil 0 +#include <lib.h> +#define close _close +#define closedir _closedir +#include <sys/types.h> +#include <dirent.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> + +int closedir(DIR *dp) +/* Finish reading a directory. */ +{ + int d; + + if (dp == nil) { errno= EBADF; return -1; } + + d= dp->_fd; + free((void *) dp); + return close(d); +} diff --git a/lib/posix/_creat.c b/lib/posix/_creat.c new file mode 100755 index 000000000..ab797b107 --- /dev/null +++ b/lib/posix/_creat.c @@ -0,0 +1,14 @@ +#include <lib.h> +#define creat _creat +#include <fcntl.h> + +PUBLIC int creat(name, mode) +_CONST char *name; +Mode_t mode; +{ + message m; + + m.m3_i2 = mode; + _loadname(name, &m); + return(_syscall(FS, CREAT, &m)); +} diff --git a/lib/posix/_dup.c b/lib/posix/_dup.c new file mode 100755 index 000000000..2150415c6 --- /dev/null +++ b/lib/posix/_dup.c @@ -0,0 +1,11 @@ +#include <lib.h> +#define dup _dup +#define fcntl _fcntl +#include <fcntl.h> +#include <unistd.h> + +PUBLIC int dup(fd) +int fd; +{ + return(fcntl(fd, F_DUPFD, 0)); +} diff --git a/lib/posix/_dup2.c b/lib/posix/_dup2.c new file mode 100755 index 000000000..e630a51d5 --- /dev/null +++ b/lib/posix/_dup2.c @@ -0,0 +1,30 @@ +#include <lib.h> +#define close _close +#define dup2 _dup2 +#define fcntl _fcntl +#include <fcntl.h> +#include <unistd.h> + +PUBLIC int dup2(fd, fd2) +int fd, fd2; +{ +/* The behavior of dup2 is defined by POSIX in 6.2.1.2 as almost, but not + * quite the same as fcntl. + */ + + if (fd2 < 0 || fd2 > OPEN_MAX) { + errno = EBADF; + return(-1); + } + + /* Check to see if fildes is valid. */ + if (fcntl(fd, F_GETFL) < 0) { + /* 'fd' is not valid. */ + return(-1); + } else { + /* 'fd' is valid. */ + if (fd == fd2) return(fd2); + close(fd2); + return(fcntl(fd, F_DUPFD, fd2)); + } +} diff --git a/lib/posix/_execl.c b/lib/posix/_execl.c new file mode 100755 index 000000000..4592d0731 --- /dev/null +++ b/lib/posix/_execl.c @@ -0,0 +1,18 @@ +/* execl() - execute Author: Kees J. Bot + * 21 Jan 1994 + */ +#define execl _execl +#define execve _execve +#include <unistd.h> + +extern char * const **_penviron; /* The default environment. */ + +int execl(const char *path, const char *arg1, ...) +/* execl("/bin/sh", "sh", "-c", "example", (char *) 0); */ +{ + /* Assumption: The C-implementation for this machine pushes + * function arguments downwards on the stack making a perfect + * argument array. Luckily this is almost always so. + */ + return execve(path, (char * const *) &arg1, *_penviron); +} diff --git a/lib/posix/_execle.c b/lib/posix/_execle.c new file mode 100755 index 000000000..10a814dcc --- /dev/null +++ b/lib/posix/_execle.c @@ -0,0 +1,25 @@ +/* execle() - execute with a custom environment Author: Kees J. Bot + * 21 Jan 1994 + */ +#define nil 0 +#define execle _execle +#define execve _execve +#include <unistd.h> +#include <stdarg.h> + +int execle(const char *path, const char *arg1, ...) +/* execle("/bin/sh", "sh", "-c", "example", (char *) 0, my_env_array); */ +{ + char * const * envp; + va_list ap; + + va_start(ap, arg1); + + /* Find the end of the argument array. */ + if (arg1 != nil) while (va_arg(ap, const char *) != nil) {} + + envp = va_arg(ap, char * const *); + va_end(ap); + + return execve(path, (char * const *) &arg1, envp); +} diff --git a/lib/posix/_execlp.c b/lib/posix/_execlp.c new file mode 100755 index 000000000..73dfdc389 --- /dev/null +++ b/lib/posix/_execlp.c @@ -0,0 +1,12 @@ +/* execlp() - execute with PATH search Author: Kees J. Bot + * 22 Jan 1994 + */ +#define execlp _execlp +#define execvp _execvp +#include <unistd.h> + +int execlp(const char *file, const char *arg1, ...) +/* execlp("sh", "sh", "-c", "example", (char *) 0); */ +{ + return execvp(file, (char * const *) &arg1); +} diff --git a/lib/posix/_execv.c b/lib/posix/_execv.c new file mode 100755 index 000000000..9f8f377ba --- /dev/null +++ b/lib/posix/_execv.c @@ -0,0 +1,13 @@ +/* execv() - execute with prepared arguments Author: Kees J. Bot + * 21 Jan 1994 + */ +#define execv _execv +#define execve _execve +#include <unistd.h> + +extern char * const **_penviron; /* The default environment. */ + +int execv(const char *path, char * const *argv) +{ + return execve(path, argv, *_penviron); +} diff --git a/lib/posix/_execve.c b/lib/posix/_execve.c new file mode 100755 index 000000000..3a5b6c106 --- /dev/null +++ b/lib/posix/_execve.c @@ -0,0 +1,115 @@ +/* execve() - basic program execution call Author: Kees J. Bot + * 21 Jan 1994 + */ + +#define _MINIX_SOURCE + +#define nil 0 +#define execve _execve +#define sbrk _sbrk +#include <lib.h> +#include <unistd.h> +#include <string.h> +#include <stddef.h> + +int execve(const char *path, char * const *argv, char * const *envp) +{ + char * const *ap; + char * const *ep; + char *frame; + char **vp; + char *sp; + size_t argc; + size_t frame_size; + size_t string_off; + size_t n; + int ov; + message m; + + /* Assumptions: size_t and char *, it's all the same thing. */ + + /* Create a stack image that only needs to be patched up slightly + * by the kernel to be used for the process to be executed. + */ + + ov= 0; /* No overflow yet. */ + frame_size= 0; /* Size of the new initial stack. */ + string_off= 0; /* Offset to start of the strings. */ + argc= 0; /* Argument count. */ + + for (ap= argv; *ap != nil; ap++) { + n = sizeof(*ap) + strlen(*ap) + 1; + frame_size+= n; + if (frame_size < n) ov= 1; + string_off+= sizeof(*ap); + argc++; + } + + for (ep= envp; *ep != nil; ep++) { + n = sizeof(*ep) + strlen(*ep) + 1; + frame_size+= n; + if (frame_size < n) ov= 1; + string_off+= sizeof(*ap); + } + + /* Add an argument count and two terminating nulls. */ + frame_size+= sizeof(argc) + sizeof(*ap) + sizeof(*ep); + string_off+= sizeof(argc) + sizeof(*ap) + sizeof(*ep); + + /* Align. */ + frame_size= (frame_size + sizeof(char *) - 1) & ~(sizeof(char *) - 1); + + /* The party is off if there is an overflow. */ + if (ov || frame_size < 3 * sizeof(char *)) { + errno= E2BIG; + return -1; + } + + /* Allocate space for the stack frame. */ + if ((frame = (char *) sbrk(frame_size)) == (char *) -1) { + errno = E2BIG; + return -1; + } + + /* Set arg count, init pointers to vector and string tables. */ + * (size_t *) frame = argc; + vp = (char **) (frame + sizeof(argc)); + sp = frame + string_off; + + /* Load the argument vector and strings. */ + for (ap= argv; *ap != nil; ap++) { + *vp++= (char *) (sp - frame); + n= strlen(*ap) + 1; + memcpy(sp, *ap, n); + sp+= n; + } + *vp++= nil; + + /* Load the environment vector and strings. */ + for (ep= envp; *ep != nil; ep++) { + *vp++= (char *) (sp - frame); + n= strlen(*ep) + 1; + memcpy(sp, *ep, n); + sp+= n; + } + *vp++= nil; + + /* Padding. */ + while (sp < frame + frame_size) *sp++= 0; + + /* We can finally make the system call. */ + m.m1_i1 = strlen(path) + 1; + m.m1_i2 = frame_size; + m.m1_p1 = (char *) path; + m.m1_p2 = frame; + + /* Clear unused fields */ + m.m1_i3 = 0; + m.m1_p3 = NULL; + + (void) _syscall(MM, EXEC, &m); + + /* Failure, return the memory used for the frame and exit. */ + (void) sbrk(-frame_size); + return -1; +} diff --git a/lib/posix/_execvp.c b/lib/posix/_execvp.c new file mode 100755 index 000000000..14b768b1d --- /dev/null +++ b/lib/posix/_execvp.c @@ -0,0 +1,73 @@ +/* execvp() - execute with PATH search and prepared arguments + * Author: Kees J. Bot + * 21 Jan 1994 + */ + +#define _MINIX_SOURCE + +#define nil 0 +#define execve _execve +#define execvp _execvp +#define sbrk _sbrk +#define stat _stat +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <sys/stat.h> + +extern char * const **_penviron; /* The default environment. */ + +int execvp(const char *file, char * const *argv) +/* Execute the file with a path search on $PATH, just like the shell. The + * search continues on the errors ENOENT (not there), and EACCES (file not + * executable or leading directories protected.) + * Unlike other execvp implementations there is no default path, and no shell + * is started for scripts. One is supposed to define $PATH, and use #!/bin/sh. + */ +{ + struct stat sb; + const char *path; /* $PATH */ + char *full; /* Full name to try. */ + char *f; + size_t full_size; + int err= ENOENT; /* Error return on failure. */ + + if (strchr(file, '/') != nil || (path= getenv("PATH")) == nil) + path= ""; + + /* Compute the maximum length the full name may have, and align. */ + full_size= strlen(path) + 1 + strlen(file) + 1 + sizeof(char *) - 1; + full_size&= ~(sizeof(char *) - 1); + + /* Claim space. */ + if ((full= (char *) sbrk(full_size)) == (char *) -1) { + errno= E2BIG; + return -1; + } + + /* For each directory in the path... */ + do { + f= full; + while (*path != 0 && *path != ':') *f++= *path++; + + if (f > full) *f++= '/'; + + strcpy(f, file); + + /* Stat first, small speed-up, better for ptrace. */ + if (stat(full, &sb) == -1) continue; + + (void) execve(full, argv, *_penviron); + + /* Prefer more interesting errno values then "not there". */ + if (errno != ENOENT) err= errno; + + /* Continue only on some errors. */ + if (err != ENOENT && err != EACCES) break; + } while (*path++ != 0); + + (void) sbrk(-full_size); + errno= err; + return -1; +} diff --git a/lib/posix/_fcntl.c b/lib/posix/_fcntl.c new file mode 100755 index 000000000..d92610ef3 --- /dev/null +++ b/lib/posix/_fcntl.c @@ -0,0 +1,44 @@ +#include <lib.h> +#define fcntl _fcntl +#include <fcntl.h> +#include <stdarg.h> + +#if _ANSI +PUBLIC int fcntl(int fd, int cmd, ...) +#else +PUBLIC int fcntl(fd, cmd) +int fd; +int cmd; +#endif +{ + va_list argp; + message m; + + va_start(argp, cmd); + + /* Set up for the sensible case where there is no variable parameter. This + * covers F_GETFD, F_GETFL and invalid commands. + */ + m.m1_i3 = 0; + m.m1_p1 = NIL_PTR; + + /* Adjust for the stupid cases. */ + switch(cmd) { + case F_DUPFD: + case F_SETFD: + case F_SETFL: + m.m1_i3 = va_arg(argp, int); + break; + case F_GETLK: + case F_SETLK: + case F_SETLKW: + m.m1_p1 = (char *) va_arg(argp, struct flock *); + break; + } + + /* Clean up and make the system call. */ + va_end(argp); + m.m1_i1 = fd; + m.m1_i2 = cmd; + return(_syscall(FS, FCNTL, &m)); +} diff --git a/lib/posix/_fork.c b/lib/posix/_fork.c new file mode 100755 index 000000000..54b565d33 --- /dev/null +++ b/lib/posix/_fork.c @@ -0,0 +1,10 @@ +#include <lib.h> +#define fork _fork +#include <unistd.h> + +PUBLIC pid_t fork() +{ + message m; + + return(_syscall(MM, FORK, &m)); +} diff --git a/lib/posix/_fpathconf.c b/lib/posix/_fpathconf.c new file mode 100755 index 000000000..72c45073c --- /dev/null +++ b/lib/posix/_fpathconf.c @@ -0,0 +1,61 @@ +/* POSIX fpathconf (Sec. 5.7.1) Author: Andy Tanenbaum */ + +#include <lib.h> +#define fstat _fstat +#define fpathconf _fpathconf +#include <sys/stat.h> +#include <errno.h> +#include <limits.h> +#include <unistd.h> +#include <termios.h> + +PUBLIC long fpathconf(fd, name) +int fd; /* file descriptor being interrogated */ +int name; /* property being inspected */ +{ +/* POSIX allows some of the values in <limits.h> to be increased at + * run time. The pathconf and fpathconf functions allow these values + * to be checked at run time. MINIX does not use this facility. + * The run-time limits are those given in <limits.h>. + */ + + struct stat stbuf; + + switch(name) { + case _PC_LINK_MAX: + /* Fstat the file. If that fails, return -1. */ + if (fstat(fd, &stbuf) != 0) return(-1); + if (S_ISDIR(stbuf.st_mode)) + return(1L); /* no links to directories */ + else + return( (long) LINK_MAX); + + case _PC_MAX_CANON: + return( (long) MAX_CANON); + + case _PC_MAX_INPUT: + return( (long) MAX_INPUT); + + case _PC_NAME_MAX: + return( (long) NAME_MAX); + + case _PC_PATH_MAX: + return( (long) PATH_MAX); + + case _PC_PIPE_BUF: + return( (long) PIPE_BUF); + + case _PC_CHOWN_RESTRICTED: + return( (long) _POSIX_CHOWN_RESTRICTED); + + case _PC_NO_TRUNC: + return( (long) _POSIX_NO_TRUNC); + + case _PC_VDISABLE: + return( (long) _POSIX_VDISABLE); + + default: + errno = EINVAL; + return(-1); + } +} diff --git a/lib/posix/_fstat.c b/lib/posix/_fstat.c new file mode 100755 index 000000000..5143fbc1b --- /dev/null +++ b/lib/posix/_fstat.c @@ -0,0 +1,14 @@ +#include <lib.h> +#define fstat _fstat +#include <sys/stat.h> + +PUBLIC int fstat(fd, buffer) +int fd; +struct stat *buffer; +{ + message m; + + m.m1_i1 = fd; + m.m1_p1 = (char *) buffer; + return(_syscall(FS, FSTAT, &m)); +} diff --git a/lib/posix/_fstatfs.c b/lib/posix/_fstatfs.c new file mode 100644 index 000000000..7b0dd036a --- /dev/null +++ b/lib/posix/_fstatfs.c @@ -0,0 +1,13 @@ +#include <lib.h> +#define fstatfs _fstatfs +#include <sys/stat.h> +#include <sys/statfs.h> + +PUBLIC int fstatfs(int fd, struct statfs *buffer) +{ + message m; + + m.m1_i1 = fd; + m.m1_p1 = (char *) buffer; + return(_syscall(FS, FSTATFS, &m)); +} diff --git a/lib/posix/_getcwd.c b/lib/posix/_getcwd.c new file mode 100755 index 000000000..6dcca5d85 --- /dev/null +++ b/lib/posix/_getcwd.c @@ -0,0 +1,143 @@ +/* getcwd() - get the name of the current working directory. + * Author: Kees J. Bot + * 30 Apr 1989 + */ +#define nil 0 +#define chdir _chdir +#define closedir _closedir +#define getcwd _getcwd +#define opendir _opendir +#define readdir _readdir +#define rewinddir _rewinddir +#define stat _stat +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <unistd.h> +#include <dirent.h> +#include <limits.h> +#include <string.h> + +static int addpath(const char *path, char **ap, const char *entry) +/* Add the name of a directory entry at the front of the path being built. + * Note that the result always starts with a slash. + */ +{ + const char *e= entry; + char *p= *ap; + + while (*e != 0) e++; + + while (e > entry && p > path) *--p = *--e; + + if (p == path) return -1; + *--p = '/'; + *ap= p; + return 0; +} + +static int recover(char *p) +/* Undo all those chdir("..")'s that have been recorded by addpath. This + * has to be done entry by entry, because the whole pathname may be too long. + */ +{ + int e= errno, slash; + char *p0; + + while (*p != 0) { + p0= ++p; + + do p++; while (*p != 0 && *p != '/'); + slash= *p; *p= 0; + + if (chdir(p0) < 0) return -1; + *p= slash; + } + errno= e; + return 0; +} + +char *getcwd(char *path, size_t size) +{ + struct stat above, current, tmp; + struct dirent *entry; + DIR *d; + char *p, *up, *dotdot; + int cycle; + + if (path == nil || size <= 1) { errno= EINVAL; return nil; } + + p= path + size; + *--p = 0; + + if (stat(".", ¤t) < 0) return nil; + + while (1) { + dotdot= ".."; + if (stat(dotdot, &above) < 0) { recover(p); return nil; } + + if (above.st_dev == current.st_dev + && above.st_ino == current.st_ino) + break; /* Root dir found */ + + if ((d= opendir(dotdot)) == nil) { recover(p); return nil; } + + /* Cycle is 0 for a simple inode nr search, or 1 for a search + * for inode *and* device nr. + */ + cycle= above.st_dev == current.st_dev ? 0 : 1; + + do { + char name[3 + NAME_MAX + 1]; + + tmp.st_ino= 0; + if ((entry= readdir(d)) == nil) { + switch (++cycle) { + case 1: + rewinddir(d); + continue; + case 2: + closedir(d); + errno= ENOENT; + recover(p); + return nil; + } + } + if (strcmp(entry->d_name, ".") == 0) continue; + if (strcmp(entry->d_name, "..") == 0) continue; + + switch (cycle) { + case 0: + /* Simple test on inode nr. */ + if (entry->d_ino != current.st_ino) continue; + /*FALL THROUGH*/ + + case 1: + /* Current is mounted. */ + strcpy(name, "../"); + strcpy(name+3, entry->d_name); + if (stat(name, &tmp) < 0) continue; + break; + } + } while (tmp.st_ino != current.st_ino + || tmp.st_dev != current.st_dev); + + up= p; + if (addpath(path, &up, entry->d_name) < 0) { + closedir(d); + errno = ERANGE; + recover(p); + return nil; + } + closedir(d); + + if (chdir(dotdot) < 0) { recover(p); return nil; } + p= up; + + current= above; + } + if (recover(p) < 0) return nil; /* Undo all those chdir("..")'s. */ + if (*p == 0) *--p = '/'; /* Cwd is "/" if nothing added */ + if (p > path) strcpy(path, p); /* Move string to start of path. */ + return path; +} diff --git a/lib/posix/_getegid.c b/lib/posix/_getegid.c new file mode 100755 index 000000000..dd1de6262 --- /dev/null +++ b/lib/posix/_getegid.c @@ -0,0 +1,16 @@ +#include <lib.h> +#define getegid _getegid +#include <unistd.h> + +PUBLIC gid_t getegid() +{ + message m; + + /* POSIX says that this function is always successful and that no + * return value is reserved to indicate an error. Minix syscalls + * are not always successful and Minix returns the unreserved value + * (gid_t) -1 when there is an error. + */ + if (_syscall(MM, GETGID, &m) < 0) return ( (gid_t) -1); + return( (gid_t) m.m2_i1); +} diff --git a/lib/posix/_geteuid.c b/lib/posix/_geteuid.c new file mode 100755 index 000000000..9e272f9b2 --- /dev/null +++ b/lib/posix/_geteuid.c @@ -0,0 +1,16 @@ +#include <lib.h> +#define geteuid _geteuid +#include <unistd.h> + +PUBLIC uid_t geteuid() +{ + message m; + + /* POSIX says that this function is always successful and that no + * return value is reserved to indicate an error. Minix syscalls + * are not always successful and Minix returns the unreserved value + * (uid_t) -1 when there is an error. + */ + if (_syscall(MM, GETUID, &m) < 0) return ( (uid_t) -1); + return( (uid_t) m.m2_i1); +} diff --git a/lib/posix/_getgid.c b/lib/posix/_getgid.c new file mode 100755 index 000000000..5a7174b73 --- /dev/null +++ b/lib/posix/_getgid.c @@ -0,0 +1,10 @@ +#include <lib.h> +#define getgid _getgid +#include <unistd.h> + +PUBLIC gid_t getgid() +{ + message m; + + return( (gid_t) _syscall(MM, GETGID, &m)); +} diff --git a/lib/posix/_getgroups.c b/lib/posix/_getgroups.c new file mode 100755 index 000000000..80bfb4560 --- /dev/null +++ b/lib/posix/_getgroups.c @@ -0,0 +1,18 @@ +/* getgroups.c POSIX 4.2.3 + * int getgroups(gidsetsize, grouplist); + * + * This call relates to suplementary group ids, which are not + * supported in MINIX. + */ + +#include <lib.h> +#define getgroups _getgroups +#include <unistd.h> +#include <time.h> + +PUBLIC int getgroups(gidsetsize, grouplist) +int gidsetsize; +gid_t grouplist[]; +{ + return(0); +} diff --git a/lib/posix/_getpgrp.c b/lib/posix/_getpgrp.c new file mode 100755 index 000000000..26d7afab4 --- /dev/null +++ b/lib/posix/_getpgrp.c @@ -0,0 +1,10 @@ +#include <lib.h> +#define getpgrp _getpgrp +#include <unistd.h> + +PUBLIC pid_t getpgrp() +{ + message m; + + return(_syscall(MM, GETPGRP, &m)); +} diff --git a/lib/posix/_getpid.c b/lib/posix/_getpid.c new file mode 100755 index 000000000..25e843a69 --- /dev/null +++ b/lib/posix/_getpid.c @@ -0,0 +1,10 @@ +#include <lib.h> +#define getpid _getpid +#include <unistd.h> + +PUBLIC pid_t getpid() +{ + message m; + + return(_syscall(MM, GETPID, &m)); +} diff --git a/lib/posix/_getppid.c b/lib/posix/_getppid.c new file mode 100755 index 000000000..7cc5c574c --- /dev/null +++ b/lib/posix/_getppid.c @@ -0,0 +1,16 @@ +#include <lib.h> +#define getppid _getppid +#include <unistd.h> + +PUBLIC pid_t getppid() +{ + message m; + + /* POSIX says that this function is always successful and that no + * return value is reserved to indicate an error. Minix syscalls + * are not always successful and Minix returns the reserved value + * (pid_t) -1 when there is an error. + */ + if (_syscall(MM, GETPID, &m) < 0) return ( (pid_t) -1); + return( (pid_t) m.m2_i1); +} diff --git a/lib/posix/_getuid.c b/lib/posix/_getuid.c new file mode 100755 index 000000000..93c996056 --- /dev/null +++ b/lib/posix/_getuid.c @@ -0,0 +1,10 @@ +#include <lib.h> +#define getuid _getuid +#include <unistd.h> + +PUBLIC uid_t getuid() +{ + message m; + + return( (uid_t) _syscall(MM, GETUID, &m)); +} diff --git a/lib/posix/_ioctl.c b/lib/posix/_ioctl.c new file mode 100755 index 000000000..b1f5a26f8 --- /dev/null +++ b/lib/posix/_ioctl.c @@ -0,0 +1,17 @@ +#include <lib.h> +#define ioctl _ioctl +#include <minix/com.h> +#include <sys/ioctl.h> + +PUBLIC int ioctl(fd, request, data) +int fd; +int request; +void *data; +{ + message m; + + m.TTY_LINE = fd; + m.TTY_REQUEST = request; + m.ADDRESS = (char *) data; + return(_syscall(FS, IOCTL, &m)); +} diff --git a/lib/posix/_isatty.c b/lib/posix/_isatty.c new file mode 100755 index 000000000..39d0069fe --- /dev/null +++ b/lib/posix/_isatty.c @@ -0,0 +1,13 @@ +#include <lib.h> +#define isatty _isatty +#define tcgetattr _tcgetattr +#include <termios.h> +#include <unistd.h> + +PUBLIC int isatty(fd) +int fd; +{ + struct termios dummy; + + return(tcgetattr(fd, &dummy) == 0); +} diff --git a/lib/posix/_kill.c b/lib/posix/_kill.c new file mode 100755 index 000000000..abc44a0df --- /dev/null +++ b/lib/posix/_kill.c @@ -0,0 +1,14 @@ +#include <lib.h> +#define kill _kill +#include <signal.h> + +PUBLIC int kill(proc, sig) +int proc; /* which process is to be sent the signal */ +int sig; /* signal number */ +{ + message m; + + m.m1_i1 = proc; + m.m1_i2 = sig; + return(_syscall(MM, KILL, &m)); +} diff --git a/lib/posix/_link.c b/lib/posix/_link.c new file mode 100755 index 000000000..0789cc631 --- /dev/null +++ b/lib/posix/_link.c @@ -0,0 +1,16 @@ +#include <lib.h> +#define link _link +#include <string.h> +#include <unistd.h> + +PUBLIC int link(name, name2) +_CONST char *name, *name2; +{ + message m; + + m.m1_i1 = strlen(name) + 1; + m.m1_i2 = strlen(name2) + 1; + m.m1_p1 = (char *) name; + m.m1_p2 = (char *) name2; + return(_syscall(FS, LINK, &m)); +} diff --git a/lib/posix/_lseek.c b/lib/posix/_lseek.c new file mode 100755 index 000000000..f152fad0d --- /dev/null +++ b/lib/posix/_lseek.c @@ -0,0 +1,17 @@ +#include <lib.h> +#define lseek _lseek +#include <unistd.h> + +PUBLIC off_t lseek(fd, offset, whence) +int fd; +off_t offset; +int whence; +{ + message m; + + m.m2_i1 = fd; + m.m2_l1 = offset; + m.m2_i2 = whence; + if (_syscall(FS, LSEEK, &m) < 0) return( (off_t) -1); + return( (off_t) m.m2_l1); +} diff --git a/lib/posix/_mkdir.c b/lib/posix/_mkdir.c new file mode 100755 index 000000000..da3aae8cc --- /dev/null +++ b/lib/posix/_mkdir.c @@ -0,0 +1,16 @@ +#include <lib.h> +#define mkdir _mkdir +#include <sys/stat.h> +#include <string.h> + +PUBLIC int mkdir(name, mode) +_CONST char *name; +Mode_t mode; +{ + message m; + + m.m1_i1 = strlen(name) + 1; + m.m1_i2 = mode; + m.m1_p1 = (char *) name; + return(_syscall(FS, MKDIR, &m)); +} diff --git a/lib/posix/_mkfifo.c b/lib/posix/_mkfifo.c new file mode 100755 index 000000000..78d4a632f --- /dev/null +++ b/lib/posix/_mkfifo.c @@ -0,0 +1,12 @@ +#include <lib.h> +#define mkfifo _mkfifo +#define mknod _mknod +#include <sys/stat.h> +#include <unistd.h> + +PUBLIC int mkfifo(name, mode) +_CONST char *name; +Mode_t mode; +{ + return mknod(name, mode | S_IFIFO, (Dev_t) 0); +} diff --git a/lib/posix/_mknod.c b/lib/posix/_mknod.c new file mode 100755 index 000000000..c08beee13 --- /dev/null +++ b/lib/posix/_mknod.c @@ -0,0 +1,20 @@ +#include <lib.h> +#define mknod _mknod +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +PUBLIC int mknod(name, mode, dev) +_CONST char *name; +Mode_t mode; +Dev_t dev; +{ + message m; + + m.m1_i1 = strlen(name) + 1; + m.m1_i2 = mode; + m.m1_i3 = dev; + m.m1_p1 = (char *) name; + m.m1_p2 = (char *) ((int) 0); /* obsolete size field */ + return(_syscall(FS, MKNOD, &m)); +} diff --git a/lib/posix/_mktemp.c b/lib/posix/_mktemp.c new file mode 100755 index 000000000..fb5c50ec5 --- /dev/null +++ b/lib/posix/_mktemp.c @@ -0,0 +1,34 @@ +/* mktemp - make a name for a temporary file */ + +#include <lib.h> +#define access _access +#define getpid _getpid +#define mktemp _mktemp +#include <unistd.h> + +PUBLIC char *mktemp(template) +char *template; +{ + register int k; + register char *p; + register pid_t pid; + + pid = getpid(); /* get process id as semi-unique number */ + p = template; + while (*p != 0) p++; /* find end of string */ + + /* Replace XXXXXX at end of template with a letter, then as many of the + * trailing digits of the pid as fit. + */ + while (*--p == 'X') { + *p = '0' + (pid % 10); + pid /= 10; + } + if (*++p != 0) { + for (k = 'a'; k <= 'z'; k++) { + *p = k; + if (access(template, F_OK) < 0) return(template); + } + } + return("/"); +} diff --git a/lib/posix/_mount.c b/lib/posix/_mount.c new file mode 100755 index 000000000..c32542ae9 --- /dev/null +++ b/lib/posix/_mount.c @@ -0,0 +1,18 @@ +#include <lib.h> +#define mount _mount +#include <string.h> +#include <unistd.h> + +PUBLIC int mount(special, name, rwflag) +char *name, *special; +int rwflag; +{ + message m; + + m.m1_i1 = strlen(special) + 1; + m.m1_i2 = strlen(name) + 1; + m.m1_i3 = rwflag; + m.m1_p1 = special; + m.m1_p2 = name; + return(_syscall(FS, MOUNT, &m)); +} diff --git a/lib/posix/_open.c b/lib/posix/_open.c new file mode 100755 index 000000000..45dd409c9 --- /dev/null +++ b/lib/posix/_open.c @@ -0,0 +1,30 @@ +#include <lib.h> +#define open _open +#include <fcntl.h> +#include <stdarg.h> +#include <string.h> + +#if _ANSI +PUBLIC int open(const char *name, int flags, ...) +#else +PUBLIC int open(name, flags) +_CONST char *name; +int flags; +#endif +{ + va_list argp; + message m; + + va_start(argp, flags); + if (flags & O_CREAT) { + m.m1_i1 = strlen(name) + 1; + m.m1_i2 = flags; + m.m1_i3 = va_arg(argp, Mode_t); + m.m1_p1 = (char *) name; + } else { + _loadname(name, &m); + m.m3_i2 = flags; + } + va_end(argp); + return (_syscall(FS, OPEN, &m)); +} diff --git a/lib/posix/_opendir.c b/lib/posix/_opendir.c new file mode 100755 index 000000000..5f174aa95 --- /dev/null +++ b/lib/posix/_opendir.c @@ -0,0 +1,53 @@ +/* opendir() Author: Kees J. Bot + * 24 Apr 1989 + */ +#define nil 0 +#include <lib.h> +#define close _close +#define fcntl _fcntl +#define fstat _fstat +#define open _open +#define opendir _opendir +#define stat _stat +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <errno.h> + +DIR *opendir(const char *name) +/* Open a directory for reading. */ +{ + int d, f; + DIR *dp; + struct stat st; + + /* Only read directories. */ + if (stat(name, &st) < 0) return nil; + if (!S_ISDIR(st.st_mode)) { errno= ENOTDIR; return nil; } + + if ((d= open(name, O_RDONLY | O_NONBLOCK)) < 0) return nil; + + /* Check the type again, mark close-on-exec, get a buffer. */ + if (fstat(d, &st) < 0 + || (errno= ENOTDIR, !S_ISDIR(st.st_mode)) + || (f= fcntl(d, F_GETFD)) < 0 + || fcntl(d, F_SETFD, f | FD_CLOEXEC) < 0 + || (dp= (DIR *) malloc(sizeof(*dp))) == nil + ) { + int err= errno; + (void) close(d); + errno= err; + return nil; + } + + dp->_fd= d; + dp->_v7= -1; + dp->_count= 0; + dp->_pos= 0; + + return dp; +} + diff --git a/lib/posix/_pathconf.c b/lib/posix/_pathconf.c new file mode 100755 index 000000000..ba8797c8c --- /dev/null +++ b/lib/posix/_pathconf.c @@ -0,0 +1,28 @@ +/* POSIX pathconf (Sec. 5.7.1) Author: Andy Tanenbaum */ + +#include <lib.h> +#define close _close +#define open _open +#define pathconf _pathconf +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> + +PUBLIC long pathconf(path, name) +_CONST char *path; /* name of file being interrogated */ +int name; /* property being inspected */ +{ +/* POSIX allows some of the values in <limits.h> to be increased at + * run time. The pathconf and fpathconf functions allow these values + * to be checked at run time. MINIX does not use this facility. + * The run-time limits are those given in <limits.h>. + */ + + int fd; + long val; + + if ( (fd = open(path, O_RDONLY)) < 0) return(-1L); + val = fpathconf(fd, name); + close(fd); + return(val); +} diff --git a/lib/posix/_pause.c b/lib/posix/_pause.c new file mode 100755 index 000000000..c8a388f0d --- /dev/null +++ b/lib/posix/_pause.c @@ -0,0 +1,10 @@ +#include <lib.h> +#define pause _pause +#include <unistd.h> + +PUBLIC int pause() +{ + message m; + + return(_syscall(MM, PAUSE, &m)); +} diff --git a/lib/posix/_pipe.c b/lib/posix/_pipe.c new file mode 100755 index 000000000..f59e89cf4 --- /dev/null +++ b/lib/posix/_pipe.c @@ -0,0 +1,14 @@ +#include <lib.h> +#define pipe _pipe +#include <unistd.h> + +PUBLIC int pipe(fild) +int fild[2]; +{ + message m; + + if (_syscall(FS, PIPE, &m) < 0) return(-1); + fild[0] = m.m1_i1; + fild[1] = m.m1_i2; + return(0); +} diff --git a/lib/posix/_ptrace.c b/lib/posix/_ptrace.c new file mode 100755 index 000000000..35c13aed8 --- /dev/null +++ b/lib/posix/_ptrace.c @@ -0,0 +1,25 @@ +#include <lib.h> +#define ptrace _ptrace +#include <unistd.h> + +PUBLIC long ptrace(req, pid, addr, data) +int req; +pid_t pid; +long addr; +long data; +{ + message m; + + m.m2_i1 = pid; + m.m2_i2 = req; + m.m2_l1 = addr; + m.m2_l2 = data; + if (_syscall(MM, PTRACE, &m) < 0) return(-1); + + /* There was no error, but -1 is a legal return value. Clear errno if + * necessary to distinguish this case. _syscall has set errno to nonzero + * for the error case. + */ + if (m.m2_l2 == -1) errno = 0; + return(m.m2_l2); +} diff --git a/lib/posix/_read.c b/lib/posix/_read.c new file mode 100755 index 000000000..ac4ceb0f6 --- /dev/null +++ b/lib/posix/_read.c @@ -0,0 +1,16 @@ +#include <lib.h> +#define read _read +#include <unistd.h> + +PUBLIC ssize_t read(fd, buffer, nbytes) +int fd; +void *buffer; +size_t nbytes; +{ + message m; + + m.m1_i1 = fd; + m.m1_i2 = nbytes; + m.m1_p1 = (char *) buffer; + return(_syscall(FS, READ, &m)); +} diff --git a/lib/posix/_readdir.c b/lib/posix/_readdir.c new file mode 100755 index 000000000..4187ed573 --- /dev/null +++ b/lib/posix/_readdir.c @@ -0,0 +1,59 @@ +/* readdir() Author: Kees J. Bot + * 24 Apr 1989 + */ +#define nil 0 +#include <lib.h> +#define read _read +#define readdir _readdir +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <string.h> + +#define v7ent(p) ((struct _v7_direct *) (p)) +#define V7_EXTENT (sizeof(struct _v7_direct) / sizeof(struct _fl_direct) - 1) + +struct dirent *readdir(DIR *dp) +/* Return the next entry in a directory. Handle V7 and FLEX format dirs. */ +{ + struct dirent *e; + + if (dp == nil) { errno= EBADF; return nil; } + + do { + if (dp->_count <= 0) { + /* Read the next directory block. */ + dp->_count= read(dp->_fd, dp->_buf, sizeof(dp->_buf)); + if (dp->_count <= 0) return nil; + + dp->_count/= sizeof(dp->_buf[0]); + dp->_ptr= dp->_buf; + + /* Extent is zero of the first flex entry. */ + if (dp->_v7 == (char)-1) dp->_v7= dp->_buf[0].d_extent; + } + + if (!dp->_v7) { + /* FLEX. */ + e= (struct dirent *) dp->_ptr; + } else { + /* V7: transform to FLEX. */ + e= (struct dirent *) dp->_v7f; + e->d_ino= v7ent(dp->_ptr)->d_ino; + e->d_extent= V7_EXTENT; + memcpy(e->d_name, v7ent(dp->_ptr)->d_name, DIRSIZ); + e->d_name[DIRSIZ]= 0; + } + + dp->_ptr+= 1 + e->d_extent; + dp->_count-= 1 + e->d_extent; + dp->_pos+= (1 + e->d_extent) * sizeof(*dp->_ptr); + + } while (e->d_ino == 0); + return e; +} diff --git a/lib/posix/_rename.c b/lib/posix/_rename.c new file mode 100755 index 000000000..a0d85b963 --- /dev/null +++ b/lib/posix/_rename.c @@ -0,0 +1,16 @@ +#include <lib.h> +#define rename _rename +#include <string.h> +#include <stdio.h> + +PUBLIC int rename(name, name2) +_CONST char *name, *name2; +{ + message m; + + m.m1_i1 = strlen(name) + 1; + m.m1_i2 = strlen(name2) + 1; + m.m1_p1 = (char *) name; + m.m1_p2 = (char *) name2; + return(_syscall(FS, RENAME, &m)); +} diff --git a/lib/posix/_rewinddir.c b/lib/posix/_rewinddir.c new file mode 100755 index 000000000..b2f65f18f --- /dev/null +++ b/lib/posix/_rewinddir.c @@ -0,0 +1,14 @@ +/* rewinddir() Author: Kees J. Bot + * 24 Apr 1989 + */ +#define nil 0 +#include <lib.h> +#define rewinddir _rewinddir +#define seekdir _seekdir +#include <sys/types.h> +#include <dirent.h> + +void rewinddir(DIR *dp) +{ + (void) seekdir(dp, 0); +} diff --git a/lib/posix/_rmdir.c b/lib/posix/_rmdir.c new file mode 100755 index 000000000..720110e70 --- /dev/null +++ b/lib/posix/_rmdir.c @@ -0,0 +1,12 @@ +#include <lib.h> +#define rmdir _rmdir +#include <unistd.h> + +PUBLIC int rmdir(name) +_CONST char *name; +{ + message m; + + _loadname(name, &m); + return(_syscall(FS, RMDIR, &m)); +} diff --git a/lib/posix/_setgid.c b/lib/posix/_setgid.c new file mode 100755 index 000000000..8c12f2f60 --- /dev/null +++ b/lib/posix/_setgid.c @@ -0,0 +1,12 @@ +#include <lib.h> +#define setgid _setgid +#include <unistd.h> + +PUBLIC int setgid(grp) +gid_t grp; +{ + message m; + + m.m1_i1 = (int) grp; + return(_syscall(MM, SETGID, &m)); +} diff --git a/lib/posix/_setsid.c b/lib/posix/_setsid.c new file mode 100755 index 000000000..9e9127381 --- /dev/null +++ b/lib/posix/_setsid.c @@ -0,0 +1,10 @@ +#include <lib.h> +#define setsid _setsid +#include <unistd.h> + +PUBLIC pid_t setsid() +{ + message m; + + return(_syscall(MM, SETSID, &m)); +} diff --git a/lib/posix/_setuid.c b/lib/posix/_setuid.c new file mode 100755 index 000000000..92411437d --- /dev/null +++ b/lib/posix/_setuid.c @@ -0,0 +1,12 @@ +#include <lib.h> +#define setuid _setuid +#include <unistd.h> + +PUBLIC int setuid(usr) +Uid_t usr; +{ + message m; + + m.m1_i1 = usr; + return(_syscall(MM, SETUID, &m)); +} diff --git a/lib/posix/_sigaction.c b/lib/posix/_sigaction.c new file mode 100755 index 000000000..472301700 --- /dev/null +++ b/lib/posix/_sigaction.c @@ -0,0 +1,23 @@ +#include <lib.h> +#define sigaction _sigaction +#include <sys/sigcontext.h> +#include <signal.h> + +_PROTOTYPE(int __sigreturn, (void)); + +PUBLIC int sigaction(sig, act, oact) +int sig; +_CONST struct sigaction *act; +struct sigaction *oact; +{ + message m; + + m.m1_i2 = sig; + + /* XXX - yet more type puns because message struct is short of types. */ + m.m1_p1 = (char *) act; + m.m1_p2 = (char *) oact; + m.m1_p3 = (char *) __sigreturn; + + return(_syscall(MM, SIGACTION, &m)); +} diff --git a/lib/posix/_sigpending.c b/lib/posix/_sigpending.c new file mode 100755 index 000000000..e214058e2 --- /dev/null +++ b/lib/posix/_sigpending.c @@ -0,0 +1,13 @@ +#include <lib.h> +#define sigpending _sigpending +#include <signal.h> + +PUBLIC int sigpending(set) +sigset_t *set; +{ + message m; + + if (_syscall(MM, SIGPENDING, &m) < 0) return(-1); + *set = (sigset_t) m.m2_l1; + return(m.m_type); +} diff --git a/lib/posix/_sigprocmask.c b/lib/posix/_sigprocmask.c new file mode 100755 index 000000000..cd25cd80a --- /dev/null +++ b/lib/posix/_sigprocmask.c @@ -0,0 +1,22 @@ +#include <lib.h> +#define sigprocmask _sigprocmask +#include <signal.h> + +PUBLIC int sigprocmask(how, set, oset) +int how; +_CONST sigset_t *set; +sigset_t *oset; +{ + message m; + + if (set == (sigset_t *) NULL) { + m.m2_i1 = SIG_INQUIRE; + m.m2_l1 = 0; + } else { + m.m2_i1 = how; + m.m2_l1 = (long) *set; + } + if (_syscall(MM, SIGPROCMASK, &m) < 0) return(-1); + if (oset != (sigset_t *) NULL) *oset = (sigset_t) (m.m2_l1); + return(m.m_type); +} diff --git a/lib/posix/_sigreturn.c b/lib/posix/_sigreturn.c new file mode 100755 index 000000000..5098cbebf --- /dev/null +++ b/lib/posix/_sigreturn.c @@ -0,0 +1,60 @@ +#include <lib.h> +#define sigfillset _sigfillset +#define sigjmp _sigjmp +#define sigprocmask _sigprocmask +#define sigreturn _sigreturn +#include <sys/sigcontext.h> +#include <setjmp.h> +#include <signal.h> + +_PROTOTYPE( int sigjmp, (jmp_buf jb, int retval)); + +#if (_SETJMP_SAVES_REGS == 0) +/* 'sigreturn' using a short format jmp_buf (no registers saved). */ +PUBLIC int sigjmp(jb, retval) +jmp_buf jb; +int retval; +{ + struct sigcontext sc; + + sc.sc_flags = jb[0].__flags; + sc.sc_mask = jb[0].__mask; + +#if (CHIP == INTEL) + sc.sc_pc = (int) jb[0].__pc; + sc.sc_sp = (int) jb[0].__sp; + sc.sc_fp = (int) jb[0].__lb; +#endif + +#if (CHIP == M68000) + sc.sc_pc = (long) jb[0].__pc; + sc.sc_sp = (long) jb[0].__sp; + sc.sc_fp = (long) jb[0].__lb; +#endif + + sc.sc_retreg = retval; + return sigreturn(&sc); +} +#endif + +PUBLIC int sigreturn(scp) +register struct sigcontext *scp; +{ + sigset_t set; + + /* The message can't be on the stack, because the stack will vanish out + * from under us. The send part of sendrec will succeed, but when + * a message is sent to restart the current process, who knows what will + * be in the place formerly occupied by the message? + */ + static message m; + + /* Protect against race conditions by blocking all interrupts. */ + sigfillset(&set); /* splhi */ + sigprocmask(SIG_SETMASK, &set, (sigset_t *) NULL); + + m.m2_l1 = scp->sc_mask; + m.m2_i2 = scp->sc_flags; + m.m2_p1 = (char *) scp; + return(_syscall(MM, SIGRETURN, &m)); /* normally this doesn't return */ +} diff --git a/lib/posix/_sigset.c b/lib/posix/_sigset.c new file mode 100755 index 000000000..d199546f1 --- /dev/null +++ b/lib/posix/_sigset.c @@ -0,0 +1,75 @@ +#include <lib.h> +/* XXX - these have to be hidden because signal() uses them and signal() is + * ANSI and not POSIX. It would be surely be better to use macros for the + * library and system uses, and perhaps macros as well as functions for the + * POSIX user interface. The macros would not need underlines. It may be + * inconvenient to match the exact semantics of the current functions + * because the interface is bloated by reporting errors. For library and + * system uses, the signal number is mostly already known to be valid + * before the sigset-changing routines are called. + */ +#define sigaddset _sigaddset +#define sigdelset _sigdelset +#define sigemptyset _sigemptyset +#define sigfillset _sigfillset +#define sigismember _sigismember +#include <signal.h> + +/* Low bit of signal masks. */ +#define SIGBIT_0 ((sigset_t) 1) + +/* Mask of valid signals (0 - _NSIG). */ +#define SIGMASK (((SIGBIT_0 << _NSIG) << 1) - 1) + +#define sigisvalid(signo) ((unsigned) (signo) <= _NSIG) + +PUBLIC int sigaddset(set, signo) +sigset_t *set; +int signo; +{ + if (!sigisvalid(signo)) { + errno = EINVAL; + return -1; + } + *set |= SIGBIT_0 << signo; + return 0; +} + +PUBLIC int sigdelset(set, signo) +sigset_t *set; +int signo; +{ + if (!sigisvalid(signo)) { + errno = EINVAL; + return -1; + } + *set &= ~(SIGBIT_0 << signo); + return 0; +} + +PUBLIC int sigemptyset(set) +sigset_t *set; +{ + *set = 0; + return 0; +} + +PUBLIC int sigfillset(set) +sigset_t *set; +{ + *set = SIGMASK; + return 0; +} + +PUBLIC int sigismember(set, signo) +_CONST sigset_t *set; +int signo; +{ + if (!sigisvalid(signo)) { + errno = EINVAL; + return -1; + } + if (*set & (SIGBIT_0 << signo)) + return 1; + return 0; +} diff --git a/lib/posix/_sigsetjmp.c b/lib/posix/_sigsetjmp.c new file mode 100755 index 000000000..f04cf016c --- /dev/null +++ b/lib/posix/_sigsetjmp.c @@ -0,0 +1,13 @@ +#include <lib.h> +#include <sys/sigcontext.h> +#include <setjmp.h> + +PUBLIC void siglongjmp(env, val) +sigjmp_buf env; +int val; +{ + if (env[0].__flags & SC_SIGCONTEXT) + longjmp(env, val); + else + _longjmp(env, val); +} diff --git a/lib/posix/_sigsuspend.c b/lib/posix/_sigsuspend.c new file mode 100755 index 000000000..01649a37f --- /dev/null +++ b/lib/posix/_sigsuspend.c @@ -0,0 +1,12 @@ +#include <lib.h> +#define sigsuspend _sigsuspend +#include <signal.h> + +PUBLIC int sigsuspend(set) +_CONST sigset_t *set; +{ + message m; + + m.m2_l1 = (long) *set; + return(_syscall(MM, SIGSUSPEND, &m)); +} diff --git a/lib/posix/_sleep.c b/lib/posix/_sleep.c new file mode 100755 index 000000000..2be6fa58f --- /dev/null +++ b/lib/posix/_sleep.c @@ -0,0 +1,69 @@ +/* sleep() - Sleep for a number of seconds. Author: Kees J. Bot + * 24 Apr 2000 + * (Inspired by the Minix-vmd version of same, except that + * this implementation doesn't bother to check if all the signal + * functions succeed. Under Minix that is no problem.) + */ + +#include <lib.h> +#define sleep _sleep +#include <signal.h> +#include <unistd.h> +#include <time.h> + +static void handler(int sig) +{ + /* Dummy signal handler. */ +} + +unsigned sleep(unsigned sleep_seconds) +{ + sigset_t ss_full, ss_orig, ss_alarm; + struct sigaction action_alarm, action_orig; + unsigned alarm_seconds, nap_seconds; + + if (sleep_seconds == 0) return 0; /* No rest for the wicked */ + + /* Mask all signals. */ + sigfillset(&ss_full); + sigprocmask(SIG_BLOCK, &ss_full, &ss_orig); + + /* Cancel currently running alarm. */ + alarm_seconds= alarm(0); + + /* How long can we nap without interruptions? */ + nap_seconds= sleep_seconds; + if (alarm_seconds != 0 && alarm_seconds < sleep_seconds) { + nap_seconds= alarm_seconds; + } + + /* Now sleep. */ + action_alarm.sa_handler= handler; + sigemptyset(&action_alarm.sa_mask); + action_alarm.sa_flags= 0; + sigaction(SIGALRM, &action_alarm, &action_orig); + alarm(nap_seconds); + + /* Wait for a wakeup call, either our alarm, or some other signal. */ + ss_alarm= ss_orig; + sigdelset(&ss_alarm, SIGALRM); + sigsuspend(&ss_alarm); + + /* Cancel alarm, set mask and stuff back to normal. */ + nap_seconds -= alarm(0); + sigaction(SIGALRM, &action_orig, NULL); + sigprocmask(SIG_SETMASK, &ss_orig, NULL); + + /* Restore alarm counter to the time remaining. */ + if (alarm_seconds != 0 && alarm_seconds >= nap_seconds) { + alarm_seconds -= nap_seconds; + if (alarm_seconds == 0) { + raise(SIGALRM); /* Alarm expires now! */ + } else { + alarm(alarm_seconds); /* Count time remaining. */ + } + } + + /* Return time not slept. */ + return sleep_seconds - nap_seconds; +} diff --git a/lib/posix/_stat.c b/lib/posix/_stat.c new file mode 100755 index 000000000..ce907fb33 --- /dev/null +++ b/lib/posix/_stat.c @@ -0,0 +1,16 @@ +#include <lib.h> +#define stat _stat +#include <sys/stat.h> +#include <string.h> + +PUBLIC int stat(name, buffer) +_CONST char *name; +struct stat *buffer; +{ + message m; + + m.m1_i1 = strlen(name) + 1; + m.m1_p1 = (char *) name; + m.m1_p2 = (char *) buffer; + return(_syscall(FS, STAT, &m)); +} diff --git a/lib/posix/_stime.c b/lib/posix/_stime.c new file mode 100755 index 000000000..e38f9057a --- /dev/null +++ b/lib/posix/_stime.c @@ -0,0 +1,13 @@ +#include <lib.h> +#define stime _stime +#include <minix/minlib.h> +#include <time.h> + +PUBLIC int stime(top) +long *top; +{ + message m; + + m.m2_l1 = *top; + return(_syscall(FS, STIME, &m)); +} diff --git a/lib/posix/_sync.c b/lib/posix/_sync.c new file mode 100755 index 000000000..2d803a4b4 --- /dev/null +++ b/lib/posix/_sync.c @@ -0,0 +1,10 @@ +#include <lib.h> +#define sync _sync +#include <unistd.h> + +PUBLIC int sync() +{ + message m; + + return(_syscall(FS, SYNC, &m)); +} diff --git a/lib/posix/_tcdrain.c b/lib/posix/_tcdrain.c new file mode 100755 index 000000000..f708527ae --- /dev/null +++ b/lib/posix/_tcdrain.c @@ -0,0 +1,16 @@ +/* +posix/_tcdrain.c + +Created: July 26, 1994 by Philip Homburg +*/ + +#define tcdrain _tcdrain +#define ioctl _ioctl +#include <termios.h> +#include <sys/ioctl.h> + +int tcdrain(fd) +int fd; +{ + return(ioctl(fd, TCDRAIN, (void *)0)); +} diff --git a/lib/posix/_tcflow.c b/lib/posix/_tcflow.c new file mode 100755 index 000000000..c85e20672 --- /dev/null +++ b/lib/posix/_tcflow.c @@ -0,0 +1,17 @@ +/* +posix/_tcflow.c + +Created: June 8, 1993 by Philip Homburg +*/ + +#define tcflow _tcflow +#define ioctl _ioctl +#include <termios.h> +#include <sys/ioctl.h> + +int tcflow(fd, action) +int fd; +int action; +{ + return(ioctl(fd, TCFLOW, &action)); +} diff --git a/lib/posix/_tcflush.c b/lib/posix/_tcflush.c new file mode 100755 index 000000000..84a6663ce --- /dev/null +++ b/lib/posix/_tcflush.c @@ -0,0 +1,12 @@ +/* tcflush() - flush buffered characters Author: Kees J. Bot + * 13 Jan 1994 + */ +#define tcflush _tcflush +#define ioctl _ioctl +#include <termios.h> +#include <sys/ioctl.h> + +int tcflush(int fd, int queue_selector) +{ + return(ioctl(fd, TCFLSH, &queue_selector)); +} diff --git a/lib/posix/_tcgetattr.c b/lib/posix/_tcgetattr.c new file mode 100755 index 000000000..cb1942ba6 --- /dev/null +++ b/lib/posix/_tcgetattr.c @@ -0,0 +1,12 @@ +#define tcgetattr _tcgetattr +#define ioctl _ioctl +#include <sys/ioctl.h> +#include <errno.h> +#include <termios.h> + +int tcgetattr(fd, termios_p) +int fd; +struct termios *termios_p; +{ + return(ioctl(fd, TCGETS, termios_p)); +} diff --git a/lib/posix/_tcsendbreak.c b/lib/posix/_tcsendbreak.c new file mode 100755 index 000000000..6052e2ba8 --- /dev/null +++ b/lib/posix/_tcsendbreak.c @@ -0,0 +1,12 @@ +/* tcsendbreak() - send a break Author: Kees J. Bot + * 13 Jan 1994 + */ +#define tcsendbreak _tcsendbreak +#define ioctl _ioctl +#include <termios.h> +#include <sys/ioctl.h> + +int tcsendbreak(int fd, int duration) +{ + return(ioctl(fd, TCSBRK, &duration)); +} diff --git a/lib/posix/_tcsetattr.c b/lib/posix/_tcsetattr.c new file mode 100755 index 000000000..b8c90d57c --- /dev/null +++ b/lib/posix/_tcsetattr.c @@ -0,0 +1,29 @@ +/* +posix/_tcsetattr.c + +Created: June 11, 1993 by Philip Homburg +*/ + +#define tcsetattr _tcsetattr +#define ioctl _ioctl +#include <errno.h> +#include <termios.h> +#include <sys/ioctl.h> +#include <sys/types.h> + +int tcsetattr(fd, opt_actions, termios_p) +int fd; +int opt_actions; +_CONST struct termios *termios_p; +{ + int request; + + switch(opt_actions) + { + case TCSANOW: request = TCSETS; break; + case TCSADRAIN: request = TCSETSW; break; + case TCSAFLUSH: request = TCSETSF; break; + default: errno = EINVAL; return(-1); + }; + return(ioctl(fd, request, (void *) termios_p)); +} diff --git a/lib/posix/_time.c b/lib/posix/_time.c new file mode 100755 index 000000000..f459d82aa --- /dev/null +++ b/lib/posix/_time.c @@ -0,0 +1,13 @@ +#include <lib.h> +#define time _time +#include <time.h> + +PUBLIC time_t time(tp) +time_t *tp; +{ + message m; + + if (_syscall(FS, TIME, &m) < 0) return( (time_t) -1); + if (tp != (time_t *) 0) *tp = m.m2_l1; + return(m.m2_l1); +} diff --git a/lib/posix/_times.c b/lib/posix/_times.c new file mode 100755 index 000000000..296d7b132 --- /dev/null +++ b/lib/posix/_times.c @@ -0,0 +1,18 @@ +#include <lib.h> +#define times _times +#include <sys/times.h> +#include <time.h> + +PUBLIC clock_t times(buf) +struct tms *buf; +{ + message m; + + m.m4_l5 = 0; /* return this if system is pre-1.6 */ + if (_syscall(FS, TIMES, &m) < 0) return( (clock_t) -1); + buf->tms_utime = m.m4_l1; + buf->tms_stime = m.m4_l2; + buf->tms_cutime = m.m4_l3; + buf->tms_cstime = m.m4_l4; + return(m.m4_l5); +} diff --git a/lib/posix/_umask.c b/lib/posix/_umask.c new file mode 100755 index 000000000..87e93bc01 --- /dev/null +++ b/lib/posix/_umask.c @@ -0,0 +1,12 @@ +#include <lib.h> +#define umask _umask +#include <sys/stat.h> + +PUBLIC mode_t umask(complmode) +Mode_t complmode; +{ + message m; + + m.m1_i1 = complmode; + return( (mode_t) _syscall(FS, UMASK, &m)); +} diff --git a/lib/posix/_umount.c b/lib/posix/_umount.c new file mode 100755 index 000000000..71e712544 --- /dev/null +++ b/lib/posix/_umount.c @@ -0,0 +1,12 @@ +#include <lib.h> +#define umount _umount +#include <unistd.h> + +PUBLIC int umount(name) +_CONST char *name; +{ + message m; + + _loadname(name, &m); + return(_syscall(FS, UMOUNT, &m)); +} diff --git a/lib/posix/_uname.c b/lib/posix/_uname.c new file mode 100755 index 000000000..80803c3d0 --- /dev/null +++ b/lib/posix/_uname.c @@ -0,0 +1,55 @@ +/* uname() - get system info Author: Kees J. Bot + * 7 Nov 1994 + * Returns information about the Minix system. Alas most + * of it is gathered at compile time, so machine is wrong, and + * release and version become wrong if not recompiled. + * More chip types and Minix versions need to be added. + */ +#define uname _uname +#define open _open +#define read _read +#define close _close +#include <sys/types.h> +#include <sys/utsname.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <minix/config.h> +#include <minix/minlib.h> + +int uname(name) struct utsname *name; +{ + int hf, n, err; + char *nl; + + /* Read the node name from /etc/hostname.file. */ + if ((hf = open("/etc/hostname.file", O_RDONLY)) < 0) { + if (errno != ENOENT) return(-1); + strcpy(name->nodename, "noname"); + } else { + n = read(hf, name->nodename, sizeof(name->nodename) - 1); + err = errno; + close(hf); + errno = err; + if (n < 0) return(-1); + name->nodename[n] = 0; + if ((nl = strchr(name->nodename, '\n')) != NULL) { + memset(nl, 0, (name->nodename + sizeof(name->nodename)) - nl); + } + } + + strcpy(name->sysname, "Minix"); + strcpy(name->release, OS_RELEASE); + strcpy(name->version, OS_VERSION); +#if (CHIP == INTEL) + name->machine[0] = 'i'; + strcpy(name->machine + 1, itoa(getprocessor())); +#if _WORD_SIZE == 4 + strcpy(name->arch, "i386"); +#else + strcpy(name->arch, "i86"); +#endif +#endif + return(0); +} diff --git a/lib/posix/_unlink.c b/lib/posix/_unlink.c new file mode 100755 index 000000000..e3ddc074f --- /dev/null +++ b/lib/posix/_unlink.c @@ -0,0 +1,12 @@ +#include <lib.h> +#define unlink _unlink +#include <unistd.h> + +PUBLIC int unlink(name) +_CONST char *name; +{ + message m; + + _loadname(name, &m); + return(_syscall(FS, UNLINK, &m)); +} diff --git a/lib/posix/_utime.c b/lib/posix/_utime.c new file mode 100755 index 000000000..f28f640f2 --- /dev/null +++ b/lib/posix/_utime.c @@ -0,0 +1,24 @@ +/* utime(2) for POSIX Authors: Terrence W. Holm & Edwin L. Froese */ + +#include <lib.h> +#define utime _utime +#include <string.h> +#include <utime.h> + +PUBLIC int utime(name, timp) +_CONST char *name; +_CONST struct utimbuf *timp; +{ + message m; + + if (timp == NULL) { + m.m2_i1 = 0; /* name size 0 means NULL `timp' */ + m.m2_i2 = strlen(name) + 1; /* actual size here */ + } else { + m.m2_l1 = timp->actime; + m.m2_l2 = timp->modtime; + m.m2_i1 = strlen(name) + 1; + } + m.m2_p1 = (char *) name; + return(_syscall(FS, UTIME, &m)); +} diff --git a/lib/posix/_wait.c b/lib/posix/_wait.c new file mode 100755 index 000000000..f8d59f806 --- /dev/null +++ b/lib/posix/_wait.c @@ -0,0 +1,13 @@ +#include <lib.h> +#define wait _wait +#include <sys/wait.h> + +PUBLIC pid_t wait(status) +int *status; +{ + message m; + + if (_syscall(MM, WAIT, &m) < 0) return(-1); + if (status != 0) *status = m.m2_i1; + return(m.m_type); +} diff --git a/lib/posix/_waitpid.c b/lib/posix/_waitpid.c new file mode 100755 index 000000000..ddab2b090 --- /dev/null +++ b/lib/posix/_waitpid.c @@ -0,0 +1,17 @@ +#include <lib.h> +#define waitpid _waitpid +#include <sys/wait.h> + +PUBLIC pid_t waitpid(pid, status, options) +pid_t pid; +int *status; +int options; +{ + message m; + + m.m1_i1 = pid; + m.m1_i2 = options; + if (_syscall(MM, WAITPID, &m) < 0) return(-1); + if (status != 0) *status = m.m2_i1; + return m.m_type; +} diff --git a/lib/posix/_write.c b/lib/posix/_write.c new file mode 100755 index 000000000..39e96c05f --- /dev/null +++ b/lib/posix/_write.c @@ -0,0 +1,16 @@ +#include <lib.h> +#define write _write +#include <unistd.h> + +PUBLIC ssize_t write(fd, buffer, nbytes) +int fd; +_CONST void *buffer; +size_t nbytes; +{ + message m; + + m.m1_i1 = fd; + m.m1_i2 = nbytes; + m.m1_p1 = (char *) buffer; + return(_syscall(FS, WRITE, &m)); +} diff --git a/lib/regex/COPYRIGHT b/lib/regex/COPYRIGHT new file mode 100755 index 000000000..574f6bcec --- /dev/null +++ b/lib/regex/COPYRIGHT @@ -0,0 +1,56 @@ +Copyright 1992, 1993, 1994 Henry Spencer. All rights reserved. +This software is not subject to any license of the American Telephone +and Telegraph Company or of the Regents of the University of California. + +Permission is granted to anyone to use this software for any purpose on +any computer system, and to alter it and redistribute it, subject +to the following restrictions: + +1. The author is not responsible for the consequences of use of this + software, no matter how awful, even if they arise from flaws in it. + +2. The origin of this software must not be misrepresented, either by + explicit claim or by omission. Since few users ever read sources, + credits must appear in the documentation. + +3. Altered versions must be plainly marked as such, and must not be + misrepresented as being the original software. Since few users + ever read sources, credits must appear in the documentation. + +4. This notice may not be removed or altered. + +=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +/*- + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)COPYRIGHT 8.1 (Berkeley) 3/16/94 + */ diff --git a/lib/regex/Makefile b/lib/regex/Makefile new file mode 100755 index 000000000..ffdd35280 --- /dev/null +++ b/lib/regex/Makefile @@ -0,0 +1,30 @@ +# Makefile for lib/regex. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../libc.a + +all: $(LIBRARY) + +OBJ = \ + $(LIBRARY)(regcomp.o) \ + $(LIBRARY)(regerror.o) \ + $(LIBRARY)(regexec.o) \ + $(LIBRARY)(regfree.o) \ + +$(LIBRARY): $(OBJ) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(regcomp.o): regcomp.c + $(CC1) regcomp.c + +$(LIBRARY)(regerror.o): regerror.c + $(CC1) regerror.c + +$(LIBRARY)(regexec.o): regexec.c + $(CC1) regexec.c + +$(LIBRARY)(regfree.o): regfree.c + $(CC1) regfree.c diff --git a/lib/regex/WHATSNEW b/lib/regex/WHATSNEW new file mode 100755 index 000000000..f4301d300 --- /dev/null +++ b/lib/regex/WHATSNEW @@ -0,0 +1,94 @@ +# @(#)WHATSNEW 8.3 (Berkeley) 3/18/94 + +New in alpha3.4: The complex bug alluded to below has been fixed (in a +slightly kludgey temporary way that may hurt efficiency a bit; this is +another "get it out the door for 4.4" release). The tests at the end of +the tests file have accordingly been uncommented. The primary sign of +the bug was that something like a?b matching ab matched b rather than ab. +(The bug was essentially specific to this exact situation, else it would +have shown up earlier.) + +New in alpha3.3: The definition of word boundaries has been altered +slightly, to more closely match the usual programming notion that "_" +is an alphabetic. Stuff used for pre-ANSI systems is now in a subdir, +and the makefile no longer alludes to it in mysterious ways. The +makefile has generally been cleaned up some. Fixes have been made +(again!) so that the regression test will run without -DREDEBUG, at +the cost of weaker checking. A workaround for a bug in some folks' +<assert.h> has been added. And some more things have been added to +tests, including a couple right at the end which are commented out +because the code currently flunks them (complex bug; fix coming). +Plus the usual minor cleanup. + +New in alpha3.2: Assorted bits of cleanup and portability improvement +(the development base is now a BSDI system using GCC instead of an ancient +Sun system, and the newer compiler exposed some glitches). Fix for a +serious bug that affected REs using many [] (including REG_ICASE REs +because of the way they are implemented), *sometimes*, depending on +memory-allocation patterns. The header-file prototypes no longer name +the parameters, avoiding possible name conflicts. The possibility that +some clot has defined CHAR_MIN as (say) `-128' instead of `(-128)' is +now handled gracefully. "uchar" is no longer used as an internal type +name (too many people have the same idea). Still the same old lousy +performance, alas. + +New in alpha3.1: Basically nothing, this release is just a bookkeeping +convenience. Stay tuned. + +New in alpha3.0: Performance is no better, alas, but some fixes have been +made and some functionality has been added. (This is basically the "get +it out the door in time for 4.4" release.) One bug fix: regfree() didn't +free the main internal structure (how embarrassing). It is now possible +to put NULs in either the RE or the target string, using (resp.) a new +REG_PEND flag and the old REG_STARTEND flag. The REG_NOSPEC flag to +regcomp() makes all characters ordinary, so you can match a literal +string easily (this will become more useful when performance improves!). +There are now primitives to match beginnings and ends of words, although +the syntax is disgusting and so is the implementation. The REG_ATOI +debugging interface has changed a bit. And there has been considerable +internal cleanup of various kinds. + +New in alpha2.3: Split change list out of README, and moved flags notes +into Makefile. Macro-ized the name of regex(7) in regex(3), since it has +to change for 4.4BSD. Cleanup work in engine.c, and some new regression +tests to catch tricky cases thereof. + +New in alpha2.2: Out-of-date manpages updated. Regerror() acquires two +small extensions -- REG_ITOA and REG_ATOI -- which avoid debugging kludges +in my own test program and might be useful to others for similar purposes. +The regression test will now compile (and run) without REDEBUG. The +BRE \$ bug is fixed. Most uses of "uchar" are gone; it's all chars now. +Char/uchar parameters are now written int/unsigned, to avoid possible +portability problems with unpromoted parameters. Some unsigned casts have +been introduced to minimize portability problems with shifting into sign +bits. + +New in alpha2.1: Lots of little stuff, cleanup and fixes. The one big +thing is that regex.h is now generated, using mkh, rather than being +supplied in the distribution; due to circularities in dependencies, +you have to build regex.h explicitly by "make h". The two known bugs +have been fixed (and the regression test now checks for them), as has a +problem with assertions not being suppressed in the absence of REDEBUG. +No performance work yet. + +New in alpha2: Backslash-anything is an ordinary character, not an +error (except, of course, for the handful of backslashed metacharacters +in BREs), which should reduce script breakage. The regression test +checks *where* null strings are supposed to match, and has generally +been tightened up somewhat. Small bug fixes in parameter passing (not +harmful, but technically errors) and some other areas. Debugging +invoked by defining REDEBUG rather than not defining NDEBUG. + +New in alpha+3: full prototyping for internal routines, using a little +helper program, mkh, which extracts prototypes given in stylized comments. +More minor cleanup. Buglet fix: it's CHAR_BIT, not CHAR_BITS. Simple +pre-screening of input when a literal string is known to be part of the +RE; this does wonders for performance. + +New in alpha+2: minor bits of cleanup. Notably, the number "32" for the +word width isn't hardwired into regexec.c any more, the public header +file prototypes the functions if __STDC__ is defined, and some small typos +in the manpages have been fixed. + +New in alpha+1: improvements to the manual pages, and an important +extension, the REG_STARTEND option to regexec(). diff --git a/lib/regex/cclass.h b/lib/regex/cclass.h new file mode 100755 index 000000000..a29a92ee9 --- /dev/null +++ b/lib/regex/cclass.h @@ -0,0 +1,70 @@ +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cclass.h 8.3 (Berkeley) 3/20/94 + */ + +/* character-class table */ +static struct cclass { + char *name; + char *chars; + char *multis; +} cclasses[] = { + "alnum", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ +0123456789", "", + "alpha", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz", + "", + "blank", " \t", "", + "cntrl", "\007\b\t\n\v\f\r\1\2\3\4\5\6\16\17\20\21\22\23\24\ +\25\26\27\30\31\32\33\34\35\36\37\177", "", + "digit", "0123456789", "", + "graph", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ +0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + "", + "lower", "abcdefghijklmnopqrstuvwxyz", + "", + "print", "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\ +0123456789!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ", + "", + "punct", "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~", + "", + "space", "\t\n\v\f\r ", "", + "upper", "ABCDEFGHIJKLMNOPQRSTUVWXYZ", + "", + "xdigit", "0123456789ABCDEFabcdef", + "", + NULL, 0, "" +}; diff --git a/lib/regex/cname.h b/lib/regex/cname.h new file mode 100755 index 000000000..c1632ebb1 --- /dev/null +++ b/lib/regex/cname.h @@ -0,0 +1,141 @@ +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)cname.h 8.3 (Berkeley) 3/20/94 + */ + +/* character-name table */ +static struct cname { + char *name; + char code; +} cnames[] = { + "NUL", '\0', + "SOH", '\001', + "STX", '\002', + "ETX", '\003', + "EOT", '\004', + "ENQ", '\005', + "ACK", '\006', + "BEL", '\007', + "alert", '\007', + "BS", '\010', + "backspace", '\b', + "HT", '\011', + "tab", '\t', + "LF", '\012', + "newline", '\n', + "VT", '\013', + "vertical-tab", '\v', + "FF", '\014', + "form-feed", '\f', + "CR", '\015', + "carriage-return", '\r', + "SO", '\016', + "SI", '\017', + "DLE", '\020', + "DC1", '\021', + "DC2", '\022', + "DC3", '\023', + "DC4", '\024', + "NAK", '\025', + "SYN", '\026', + "ETB", '\027', + "CAN", '\030', + "EM", '\031', + "SUB", '\032', + "ESC", '\033', + "IS4", '\034', + "FS", '\034', + "IS3", '\035', + "GS", '\035', + "IS2", '\036', + "RS", '\036', + "IS1", '\037', + "US", '\037', + "space", ' ', + "exclamation-mark", '!', + "quotation-mark", '"', + "number-sign", '#', + "dollar-sign", '$', + "percent-sign", '%', + "ampersand", '&', + "apostrophe", '\'', + "left-parenthesis", '(', + "right-parenthesis", ')', + "asterisk", '*', + "plus-sign", '+', + "comma", ',', + "hyphen", '-', + "hyphen-minus", '-', + "period", '.', + "full-stop", '.', + "slash", '/', + "solidus", '/', + "zero", '0', + "one", '1', + "two", '2', + "three", '3', + "four", '4', + "five", '5', + "six", '6', + "seven", '7', + "eight", '8', + "nine", '9', + "colon", ':', + "semicolon", ';', + "less-than-sign", '<', + "equals-sign", '=', + "greater-than-sign", '>', + "question-mark", '?', + "commercial-at", '@', + "left-square-bracket", '[', + "backslash", '\\', + "reverse-solidus", '\\', + "right-square-bracket", ']', + "circumflex", '^', + "circumflex-accent", '^', + "underscore", '_', + "low-line", '_', + "grave-accent", '`', + "left-brace", '{', + "left-curly-bracket", '{', + "vertical-line", '|', + "right-brace", '}', + "right-curly-bracket", '}', + "tilde", '~', + "DEL", '\177', + NULL, 0, +}; diff --git a/lib/regex/engine.c b/lib/regex/engine.c new file mode 100755 index 000000000..b3e5f5bad --- /dev/null +++ b/lib/regex/engine.c @@ -0,0 +1,1091 @@ +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)engine.c 8.5 (Berkeley) 3/20/94 + */ + +/* + * The matching engine and friends. This file is #included by regexec.c + * after suitable #defines of a variety of macros used herein, so that + * different state representations can be used without duplicating masses + * of code. + */ + +#ifdef SNAMES +#define matcher smatcher +#define fast sfast +#define slow sslow +#define dissect sdissect +#define backref sbackref +#define step sstep +#define print sprint +#define at sat +#define match smat +#endif +#ifdef LNAMES +#define matcher lmatcher +#define fast lfast +#define slow lslow +#define dissect ldissect +#define backref lbackref +#define step lstep +#define print lprint +#define at lat +#define match lmat +#endif + +/* another structure passed up and down to avoid zillions of parameters */ +struct match { + struct re_guts *g; + int eflags; + regmatch_t *pmatch; /* [nsub+1] (0 element unused) */ + char *offp; /* offsets work from here */ + char *beginp; /* start of string -- virtual NUL precedes */ + char *endp; /* end of string -- virtual NUL here */ + char *coldp; /* can be no match starting before here */ + char **lastpos; /* [nplus+1] */ + STATEVARS; + states st; /* current states */ + states fresh; /* states for a fresh start */ + states tmp; /* temporary */ + states empty; /* empty set of states */ +}; + +/* ========= begin header generated by ./mkh ========= */ +#ifdef __cplusplus +extern "C" { +#endif + +/* === engine.c === */ +static int matcher(struct re_guts *g, char *string, size_t nmatch, regmatch_t pmatch[], int eflags); +static char *dissect(struct match *m, char *start, char *stop, sopno startst, sopno stopst); +static char *backref(struct match *m, char *start, char *stop, sopno startst, sopno stopst, sopno lev); +static char *fast(struct match *m, char *start, char *stop, sopno startst, sopno stopst); +static char *slow(struct match *m, char *start, char *stop, sopno startst, sopno stopst); +static states step(struct re_guts *g, sopno start, sopno stop, states bef, int ch, states aft); +#define BOL (OUT+1) +#define EOL (BOL+1) +#define BOLEOL (BOL+2) +#define NOTHING (BOL+3) +#define BOW (BOL+4) +#define EOW (BOL+5) +#define CODEMAX (BOL+5) /* highest code used */ +#define NONCHAR(c) ((c) > CHAR_MAX) +#define NNONCHAR (CODEMAX-CHAR_MAX) +#ifdef REDEBUG +static void print(struct match *m, char *caption, states st, int ch, FILE *d); +#endif +#ifdef REDEBUG +static void at(struct match *m, char *title, char *start, char *stop, sopno startst, sopno stopst); +#endif +#ifdef REDEBUG +static char *pchar(int ch); +#endif + +#ifdef __cplusplus +} +#endif +/* ========= end header generated by ./mkh ========= */ + +#ifdef REDEBUG +#define SP(t, s, c) print(m, t, s, c, stdout) +#define AT(t, p1, p2, s1, s2) at(m, t, p1, p2, s1, s2) +#define NOTE(str) { if (m->eflags®_TRACE) printf("=%s\n", (str)); } +#else +#define SP(t, s, c) /* nothing */ +#define AT(t, p1, p2, s1, s2) /* nothing */ +#define NOTE(s) /* nothing */ +#endif + +/* + - matcher - the actual matching engine + == static int matcher(register struct re_guts *g, char *string, \ + == size_t nmatch, regmatch_t pmatch[], int eflags); + */ +static int /* 0 success, REG_NOMATCH failure */ +matcher(g, string, nmatch, pmatch, eflags) +register struct re_guts *g; +char *string; +size_t nmatch; +regmatch_t pmatch[]; +int eflags; +{ + register char *endp; + register int i; + struct match mv; + register struct match *m = &mv; + register char *dp; + const register sopno gf = g->firststate+1; /* +1 for OEND */ + const register sopno gl = g->laststate; + char *start; + char *stop; + + /* simplify the situation where possible */ + if (g->cflags®_NOSUB) + nmatch = 0; + if (eflags®_STARTEND) { + start = string + pmatch[0].rm_so; + stop = string + pmatch[0].rm_eo; + } else { + start = string; + stop = start + strlen(start); + } + if (stop < start) + return(REG_INVARG); + + /* prescreening; this does wonders for this rather slow code */ + if (g->must != NULL) { + for (dp = start; dp < stop; dp++) + if (*dp == g->must[0] && stop - dp >= g->mlen && + memcmp(dp, g->must, (size_t)g->mlen) == 0) + break; + if (dp == stop) /* we didn't find g->must */ + return(REG_NOMATCH); + } + + /* match struct setup */ + m->g = g; + m->eflags = eflags; + m->pmatch = NULL; + m->lastpos = NULL; + m->offp = string; + m->beginp = start; + m->endp = stop; + STATESETUP(m, 4); + SETUP(m->st); + SETUP(m->fresh); + SETUP(m->tmp); + SETUP(m->empty); + CLEAR(m->empty); + + /* this loop does only one repetition except for backrefs */ + for (;;) { + endp = fast(m, start, stop, gf, gl); + if (endp == NULL) { /* a miss */ + STATETEARDOWN(m); + return(REG_NOMATCH); + } + if (nmatch == 0 && !g->backrefs) + break; /* no further info needed */ + + /* where? */ + assert(m->coldp != NULL); + for (;;) { + NOTE("finding start"); + endp = slow(m, m->coldp, stop, gf, gl); + if (endp != NULL) + break; + assert(m->coldp < m->endp); + m->coldp++; + } + if (nmatch == 1 && !g->backrefs) + break; /* no further info needed */ + + /* oh my, he wants the subexpressions... */ + if (m->pmatch == NULL) + m->pmatch = (regmatch_t *)malloc((m->g->nsub + 1) * + sizeof(regmatch_t)); + if (m->pmatch == NULL) { + STATETEARDOWN(m); + return(REG_ESPACE); + } + for (i = 1; i <= m->g->nsub; i++) + m->pmatch[i].rm_so = m->pmatch[i].rm_eo = -1; + if (!g->backrefs && !(m->eflags®_BACKR)) { + NOTE("dissecting"); + dp = dissect(m, m->coldp, endp, gf, gl); + } else { + if (g->nplus > 0 && m->lastpos == NULL) + m->lastpos = (char **)malloc((g->nplus+1) * + sizeof(char *)); + if (g->nplus > 0 && m->lastpos == NULL) { + free(m->pmatch); + STATETEARDOWN(m); + return(REG_ESPACE); + } + NOTE("backref dissect"); + dp = backref(m, m->coldp, endp, gf, gl, (sopno)0); + } + if (dp != NULL) + break; + + /* uh-oh... we couldn't find a subexpression-level match */ + assert(g->backrefs); /* must be back references doing it */ + assert(g->nplus == 0 || m->lastpos != NULL); + for (;;) { + if (dp != NULL || endp <= m->coldp) + break; /* defeat */ + NOTE("backoff"); + endp = slow(m, m->coldp, endp-1, gf, gl); + if (endp == NULL) + break; /* defeat */ + /* try it on a shorter possibility */ +#ifndef NDEBUG + for (i = 1; i <= m->g->nsub; i++) { + assert(m->pmatch[i].rm_so == -1); + assert(m->pmatch[i].rm_eo == -1); + } +#endif + NOTE("backoff dissect"); + dp = backref(m, m->coldp, endp, gf, gl, (sopno)0); + } + assert(dp == NULL || dp == endp); + if (dp != NULL) /* found a shorter one */ + break; + + /* despite initial appearances, there is no match here */ + NOTE("false alarm"); + start = m->coldp + 1; /* recycle starting later */ + assert(start <= stop); + } + + /* fill in the details if requested */ + if (nmatch > 0) { + pmatch[0].rm_so = m->coldp - m->offp; + pmatch[0].rm_eo = endp - m->offp; + } + if (nmatch > 1) { + assert(m->pmatch != NULL); + for (i = 1; i < nmatch; i++) + if (i <= m->g->nsub) + pmatch[i] = m->pmatch[i]; + else { + pmatch[i].rm_so = -1; + pmatch[i].rm_eo = -1; + } + } + + if (m->pmatch != NULL) + free((char *)m->pmatch); + if (m->lastpos != NULL) + free((char *)m->lastpos); + STATETEARDOWN(m); + return(0); +} + +/* + - dissect - figure out what matched what, no back references + == static char *dissect(register struct match *m, char *start, \ + == char *stop, sopno startst, sopno stopst); + */ +static char * /* == stop (success) always */ +dissect(m, start, stop, startst, stopst) +register struct match *m; +char *start; +char *stop; +sopno startst; +sopno stopst; +{ + register int i; + register sopno ss; /* start sop of current subRE */ + register sopno es; /* end sop of current subRE */ + register char *sp; /* start of string matched by it */ + register char *stp; /* string matched by it cannot pass here */ + register char *rest; /* start of rest of string */ + register char *tail; /* string unmatched by rest of RE */ + register sopno ssub; /* start sop of subsubRE */ + register sopno esub; /* end sop of subsubRE */ + register char *ssp; /* start of string matched by subsubRE */ + register char *sep; /* end of string matched by subsubRE */ + register char *oldssp; /* previous ssp */ + register char *dp; + + AT("diss", start, stop, startst, stopst); + sp = start; + for (ss = startst; ss < stopst; ss = es) { + /* identify end of subRE */ + es = ss; + switch (OP(m->g->strip[es])) { + case OPLUS_: + case OQUEST_: + es += OPND(m->g->strip[es]); + break; + case OCH_: + while (OP(m->g->strip[es]) != O_CH) + es += OPND(m->g->strip[es]); + break; + } + es++; + + /* figure out what it matched */ + switch (OP(m->g->strip[ss])) { + case OEND: + assert(nope); + break; + case OCHAR: + sp++; + break; + case OBOL: + case OEOL: + case OBOW: + case OEOW: + break; + case OANY: + case OANYOF: + sp++; + break; + case OBACK_: + case O_BACK: + assert(nope); + break; + /* cases where length of match is hard to find */ + case OQUEST_: + stp = stop; + for (;;) { + /* how long could this one be? */ + rest = slow(m, sp, stp, ss, es); + assert(rest != NULL); /* it did match */ + /* could the rest match the rest? */ + tail = slow(m, rest, stop, es, stopst); + if (tail == stop) + break; /* yes! */ + /* no -- try a shorter match for this one */ + stp = rest - 1; + assert(stp >= sp); /* it did work */ + } + ssub = ss + 1; + esub = es - 1; + /* did innards match? */ + if (slow(m, sp, rest, ssub, esub) != NULL) { + dp = dissect(m, sp, rest, ssub, esub); + assert(dp == rest); + } else /* no */ + assert(sp == rest); + sp = rest; + break; + case OPLUS_: + stp = stop; + for (;;) { + /* how long could this one be? */ + rest = slow(m, sp, stp, ss, es); + assert(rest != NULL); /* it did match */ + /* could the rest match the rest? */ + tail = slow(m, rest, stop, es, stopst); + if (tail == stop) + break; /* yes! */ + /* no -- try a shorter match for this one */ + stp = rest - 1; + assert(stp >= sp); /* it did work */ + } + ssub = ss + 1; + esub = es - 1; + ssp = sp; + oldssp = ssp; + for (;;) { /* find last match of innards */ + sep = slow(m, ssp, rest, ssub, esub); + if (sep == NULL || sep == ssp) + break; /* failed or matched null */ + oldssp = ssp; /* on to next try */ + ssp = sep; + } + if (sep == NULL) { + /* last successful match */ + sep = ssp; + ssp = oldssp; + } + assert(sep == rest); /* must exhaust substring */ + assert(slow(m, ssp, sep, ssub, esub) == rest); + dp = dissect(m, ssp, sep, ssub, esub); + assert(dp == sep); + sp = rest; + break; + case OCH_: + stp = stop; + for (;;) { + /* how long could this one be? */ + rest = slow(m, sp, stp, ss, es); + assert(rest != NULL); /* it did match */ + /* could the rest match the rest? */ + tail = slow(m, rest, stop, es, stopst); + if (tail == stop) + break; /* yes! */ + /* no -- try a shorter match for this one */ + stp = rest - 1; + assert(stp >= sp); /* it did work */ + } + ssub = ss + 1; + esub = ss + OPND(m->g->strip[ss]) - 1; + assert(OP(m->g->strip[esub]) == OOR1); + for (;;) { /* find first matching branch */ + if (slow(m, sp, rest, ssub, esub) == rest) + break; /* it matched all of it */ + /* that one missed, try next one */ + assert(OP(m->g->strip[esub]) == OOR1); + esub++; + assert(OP(m->g->strip[esub]) == OOR2); + ssub = esub + 1; + esub += OPND(m->g->strip[esub]); + if (OP(m->g->strip[esub]) == OOR2) + esub--; + else + assert(OP(m->g->strip[esub]) == O_CH); + } + dp = dissect(m, sp, rest, ssub, esub); + assert(dp == rest); + sp = rest; + break; + case O_PLUS: + case O_QUEST: + case OOR1: + case OOR2: + case O_CH: + assert(nope); + break; + case OLPAREN: + i = OPND(m->g->strip[ss]); + assert(0 < i && i <= m->g->nsub); + m->pmatch[i].rm_so = sp - m->offp; + break; + case ORPAREN: + i = OPND(m->g->strip[ss]); + assert(0 < i && i <= m->g->nsub); + m->pmatch[i].rm_eo = sp - m->offp; + break; + default: /* uh oh */ + assert(nope); + break; + } + } + + assert(sp == stop); + return(sp); +} + +/* + - backref - figure out what matched what, figuring in back references + == static char *backref(register struct match *m, char *start, \ + == char *stop, sopno startst, sopno stopst, sopno lev); + */ +static char * /* == stop (success) or NULL (failure) */ +backref(m, start, stop, startst, stopst, lev) +register struct match *m; +char *start; +char *stop; +sopno startst; +sopno stopst; +sopno lev; /* PLUS nesting level */ +{ + register int i; + register sopno ss; /* start sop of current subRE */ + register char *sp; /* start of string matched by it */ + register sopno ssub; /* start sop of subsubRE */ + register sopno esub; /* end sop of subsubRE */ + register char *ssp; /* start of string matched by subsubRE */ + register char *dp; + register size_t len; + register int hard; + register sop s; + register regoff_t offsave; + register cset *cs; + + AT("back", start, stop, startst, stopst); + sp = start; + + /* get as far as we can with easy stuff */ + hard = 0; + for (ss = startst; !hard && ss < stopst; ss++) + switch (OP(s = m->g->strip[ss])) { + case OCHAR: + if (sp == stop || *sp++ != (char)OPND(s)) + return(NULL); + break; + case OANY: + if (sp == stop) + return(NULL); + sp++; + break; + case OANYOF: + cs = &m->g->sets[OPND(s)]; + if (sp == stop || !CHIN(cs, *sp++)) + return(NULL); + break; + case OBOL: + if ( (sp == m->beginp && !(m->eflags®_NOTBOL)) || + (sp < m->endp && *(sp-1) == '\n' && + (m->g->cflags®_NEWLINE)) ) + { /* yes */ } + else + return(NULL); + break; + case OEOL: + if ( (sp == m->endp && !(m->eflags®_NOTEOL)) || + (sp < m->endp && *sp == '\n' && + (m->g->cflags®_NEWLINE)) ) + { /* yes */ } + else + return(NULL); + break; + case OBOW: + if (( (sp == m->beginp && !(m->eflags®_NOTBOL)) || + (sp < m->endp && *(sp-1) == '\n' && + (m->g->cflags®_NEWLINE)) || + (sp > m->beginp && + !ISWORD(*(sp-1))) ) && + (sp < m->endp && ISWORD(*sp)) ) + { /* yes */ } + else + return(NULL); + break; + case OEOW: + if (( (sp == m->endp && !(m->eflags®_NOTEOL)) || + (sp < m->endp && *sp == '\n' && + (m->g->cflags®_NEWLINE)) || + (sp < m->endp && !ISWORD(*sp)) ) && + (sp > m->beginp && ISWORD(*(sp-1))) ) + { /* yes */ } + else + return(NULL); + break; + case O_QUEST: + break; + case OOR1: /* matches null but needs to skip */ + ss++; + s = m->g->strip[ss]; + do { + assert(OP(s) == OOR2); + ss += OPND(s); + } while (OP(s = m->g->strip[ss]) != O_CH); + /* note that the ss++ gets us past the O_CH */ + break; + default: /* have to make a choice */ + hard = 1; + break; + } + if (!hard) { /* that was it! */ + if (sp != stop) + return(NULL); + return(sp); + } + ss--; /* adjust for the for's final increment */ + + /* the hard stuff */ + AT("hard", sp, stop, ss, stopst); + s = m->g->strip[ss]; + switch (OP(s)) { + case OBACK_: /* the vilest depths */ + i = OPND(s); + assert(0 < i && i <= m->g->nsub); + if (m->pmatch[i].rm_eo == -1) + return(NULL); + assert(m->pmatch[i].rm_so != -1); + len = m->pmatch[i].rm_eo - m->pmatch[i].rm_so; + assert(stop - m->beginp >= len); + if (sp > stop - len) + return(NULL); /* not enough left to match */ + ssp = m->offp + m->pmatch[i].rm_so; + if (memcmp(sp, ssp, len) != 0) + return(NULL); + while (m->g->strip[ss] != SOP(O_BACK, i)) + ss++; + return(backref(m, sp+len, stop, ss+1, stopst, lev)); + break; + case OQUEST_: /* to null or not */ + dp = backref(m, sp, stop, ss+1, stopst, lev); + if (dp != NULL) + return(dp); /* not */ + return(backref(m, sp, stop, ss+OPND(s)+1, stopst, lev)); + break; + case OPLUS_: + assert(m->lastpos != NULL); + assert(lev+1 <= m->g->nplus); + m->lastpos[lev+1] = sp; + return(backref(m, sp, stop, ss+1, stopst, lev+1)); + break; + case O_PLUS: + if (sp == m->lastpos[lev]) /* last pass matched null */ + return(backref(m, sp, stop, ss+1, stopst, lev-1)); + /* try another pass */ + m->lastpos[lev] = sp; + dp = backref(m, sp, stop, ss-OPND(s)+1, stopst, lev); + if (dp == NULL) + return(backref(m, sp, stop, ss+1, stopst, lev-1)); + else + return(dp); + break; + case OCH_: /* find the right one, if any */ + ssub = ss + 1; + esub = ss + OPND(s) - 1; + assert(OP(m->g->strip[esub]) == OOR1); + for (;;) { /* find first matching branch */ + dp = backref(m, sp, stop, ssub, esub, lev); + if (dp != NULL) + return(dp); + /* that one missed, try next one */ + if (OP(m->g->strip[esub]) == O_CH) + return(NULL); /* there is none */ + esub++; + assert(OP(m->g->strip[esub]) == OOR2); + ssub = esub + 1; + esub += OPND(m->g->strip[esub]); + if (OP(m->g->strip[esub]) == OOR2) + esub--; + else + assert(OP(m->g->strip[esub]) == O_CH); + } + break; + case OLPAREN: /* must undo assignment if rest fails */ + i = OPND(s); + assert(0 < i && i <= m->g->nsub); + offsave = m->pmatch[i].rm_so; + m->pmatch[i].rm_so = sp - m->offp; + dp = backref(m, sp, stop, ss+1, stopst, lev); + if (dp != NULL) + return(dp); + m->pmatch[i].rm_so = offsave; + return(NULL); + break; + case ORPAREN: /* must undo assignment if rest fails */ + i = OPND(s); + assert(0 < i && i <= m->g->nsub); + offsave = m->pmatch[i].rm_eo; + m->pmatch[i].rm_eo = sp - m->offp; + dp = backref(m, sp, stop, ss+1, stopst, lev); + if (dp != NULL) + return(dp); + m->pmatch[i].rm_eo = offsave; + return(NULL); + break; + default: /* uh oh */ + assert(nope); + break; + } + + /* "can't happen" */ + assert(nope); + /* NOTREACHED */ +} + +/* + - fast - step through the string at top speed + == static char *fast(register struct match *m, char *start, \ + == char *stop, sopno startst, sopno stopst); + */ +static char * /* where tentative match ended, or NULL */ +fast(m, start, stop, startst, stopst) +register struct match *m; +char *start; +char *stop; +sopno startst; +sopno stopst; +{ + register states st = m->st; + register states fresh = m->fresh; + register states tmp = m->tmp; + register char *p = start; + register int c = (start == m->beginp) ? OUT : *(start-1); + register int lastc; /* previous c */ + register int flagch; + register int i; + register char *coldp; /* last p after which no match was underway */ + + CLEAR(st); + SET1(st, startst); + st = step(m->g, startst, stopst, st, NOTHING, st); + ASSIGN(fresh, st); + SP("start", st, *p); + coldp = NULL; + for (;;) { + /* next character */ + lastc = c; + c = (p == m->endp) ? OUT : *p; + if (EQ(st, fresh)) + coldp = p; + + /* is there an EOL and/or BOL between lastc and c? */ + flagch = '\0'; + i = 0; + if ( (lastc == '\n' && m->g->cflags®_NEWLINE) || + (lastc == OUT && !(m->eflags®_NOTBOL)) ) { + flagch = BOL; + i = m->g->nbol; + } + if ( (c == '\n' && m->g->cflags®_NEWLINE) || + (c == OUT && !(m->eflags®_NOTEOL)) ) { + flagch = (flagch == BOL) ? BOLEOL : EOL; + i += m->g->neol; + } + if (i != 0) { + for (; i > 0; i--) + st = step(m->g, startst, stopst, st, flagch, st); + SP("boleol", st, c); + } + + /* how about a word boundary? */ + if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) && + (c != OUT && ISWORD(c)) ) { + flagch = BOW; + } + if ( (lastc != OUT && ISWORD(lastc)) && + (flagch == EOL || (c != OUT && !ISWORD(c))) ) { + flagch = EOW; + } + if (flagch == BOW || flagch == EOW) { + st = step(m->g, startst, stopst, st, flagch, st); + SP("boweow", st, c); + } + + /* are we done? */ + if (ISSET(st, stopst) || p == stop) + break; /* NOTE BREAK OUT */ + + /* no, we must deal with this character */ + ASSIGN(tmp, st); + ASSIGN(st, fresh); + assert(c != OUT); + st = step(m->g, startst, stopst, tmp, c, st); + SP("aft", st, c); + assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st)); + p++; + } + + assert(coldp != NULL); + m->coldp = coldp; + if (ISSET(st, stopst)) + return(p+1); + else + return(NULL); +} + +/* + - slow - step through the string more deliberately + == static char *slow(register struct match *m, char *start, \ + == char *stop, sopno startst, sopno stopst); + */ +static char * /* where it ended */ +slow(m, start, stop, startst, stopst) +register struct match *m; +char *start; +char *stop; +sopno startst; +sopno stopst; +{ + register states st = m->st; + register states empty = m->empty; + register states tmp = m->tmp; + register char *p = start; + register int c = (start == m->beginp) ? OUT : *(start-1); + register int lastc; /* previous c */ + register int flagch; + register int i; + register char *matchp; /* last p at which a match ended */ + + AT("slow", start, stop, startst, stopst); + CLEAR(st); + SET1(st, startst); + SP("sstart", st, *p); + st = step(m->g, startst, stopst, st, NOTHING, st); + matchp = NULL; + for (;;) { + /* next character */ + lastc = c; + c = (p == m->endp) ? OUT : *p; + + /* is there an EOL and/or BOL between lastc and c? */ + flagch = '\0'; + i = 0; + if ( (lastc == '\n' && m->g->cflags®_NEWLINE) || + (lastc == OUT && !(m->eflags®_NOTBOL)) ) { + flagch = BOL; + i = m->g->nbol; + } + if ( (c == '\n' && m->g->cflags®_NEWLINE) || + (c == OUT && !(m->eflags®_NOTEOL)) ) { + flagch = (flagch == BOL) ? BOLEOL : EOL; + i += m->g->neol; + } + if (i != 0) { + for (; i > 0; i--) + st = step(m->g, startst, stopst, st, flagch, st); + SP("sboleol", st, c); + } + + /* how about a word boundary? */ + if ( (flagch == BOL || (lastc != OUT && !ISWORD(lastc))) && + (c != OUT && ISWORD(c)) ) { + flagch = BOW; + } + if ( (lastc != OUT && ISWORD(lastc)) && + (flagch == EOL || (c != OUT && !ISWORD(c))) ) { + flagch = EOW; + } + if (flagch == BOW || flagch == EOW) { + st = step(m->g, startst, stopst, st, flagch, st); + SP("sboweow", st, c); + } + + /* are we done? */ + if (ISSET(st, stopst)) + matchp = p; + if (EQ(st, empty) || p == stop) + break; /* NOTE BREAK OUT */ + + /* no, we must deal with this character */ + ASSIGN(tmp, st); + ASSIGN(st, empty); + assert(c != OUT); + st = step(m->g, startst, stopst, tmp, c, st); + SP("saft", st, c); + assert(EQ(step(m->g, startst, stopst, st, NOTHING, st), st)); + p++; + } + + return(matchp); +} + + +/* + - step - map set of states reachable before char to set reachable after + == static states step(register struct re_guts *g, sopno start, sopno stop, \ + == register states bef, int ch, register states aft); + == #define BOL (OUT+1) + == #define EOL (BOL+1) + == #define BOLEOL (BOL+2) + == #define NOTHING (BOL+3) + == #define BOW (BOL+4) + == #define EOW (BOL+5) + == #define CODEMAX (BOL+5) // highest code used + == #define NONCHAR(c) ((c) > CHAR_MAX) + == #define NNONCHAR (CODEMAX-CHAR_MAX) + */ +static states +step(g, start, stop, bef, ch, aft) +register struct re_guts *g; +sopno start; /* start state within strip */ +sopno stop; /* state after stop state within strip */ +register states bef; /* states reachable before */ +int ch; /* character or NONCHAR code */ +register states aft; /* states already known reachable after */ +{ + register cset *cs; + register sop s; + register sopno pc; + register onestate here; /* note, macros know this name */ + register sopno look; + register int i; + + for (pc = start, INIT(here, pc); pc != stop; pc++, INC(here)) { + s = g->strip[pc]; + switch (OP(s)) { + case OEND: + assert(pc == stop-1); + break; + case OCHAR: + /* only characters can match */ + assert(!NONCHAR(ch) || ch != (char)OPND(s)); + if (ch == (char)OPND(s)) + FWD(aft, bef, 1); + break; + case OBOL: + if (ch == BOL || ch == BOLEOL) + FWD(aft, bef, 1); + break; + case OEOL: + if (ch == EOL || ch == BOLEOL) + FWD(aft, bef, 1); + break; + case OBOW: + if (ch == BOW) + FWD(aft, bef, 1); + break; + case OEOW: + if (ch == EOW) + FWD(aft, bef, 1); + break; + case OANY: + if (!NONCHAR(ch)) + FWD(aft, bef, 1); + break; + case OANYOF: + cs = &g->sets[OPND(s)]; + if (!NONCHAR(ch) && CHIN(cs, ch)) + FWD(aft, bef, 1); + break; + case OBACK_: /* ignored here */ + case O_BACK: + FWD(aft, aft, 1); + break; + case OPLUS_: /* forward, this is just an empty */ + FWD(aft, aft, 1); + break; + case O_PLUS: /* both forward and back */ + FWD(aft, aft, 1); + i = ISSETBACK(aft, OPND(s)); + BACK(aft, aft, OPND(s)); + if (!i && ISSETBACK(aft, OPND(s))) { + /* oho, must reconsider loop body */ + pc -= OPND(s) + 1; + INIT(here, pc); + } + break; + case OQUEST_: /* two branches, both forward */ + FWD(aft, aft, 1); + FWD(aft, aft, OPND(s)); + break; + case O_QUEST: /* just an empty */ + FWD(aft, aft, 1); + break; + case OLPAREN: /* not significant here */ + case ORPAREN: + FWD(aft, aft, 1); + break; + case OCH_: /* mark the first two branches */ + FWD(aft, aft, 1); + assert(OP(g->strip[pc+OPND(s)]) == OOR2); + FWD(aft, aft, OPND(s)); + break; + case OOR1: /* done a branch, find the O_CH */ + if (ISSTATEIN(aft, here)) { + for (look = 1; + OP(s = g->strip[pc+look]) != O_CH; + look += OPND(s)) + assert(OP(s) == OOR2); + FWD(aft, aft, look); + } + break; + case OOR2: /* propagate OCH_'s marking */ + FWD(aft, aft, 1); + if (OP(g->strip[pc+OPND(s)]) != O_CH) { + assert(OP(g->strip[pc+OPND(s)]) == OOR2); + FWD(aft, aft, OPND(s)); + } + break; + case O_CH: /* just empty */ + FWD(aft, aft, 1); + break; + default: /* ooooops... */ + assert(nope); + break; + } + } + + return(aft); +} + +#ifdef REDEBUG +/* + - print - print a set of states + == #ifdef REDEBUG + == static void print(struct match *m, char *caption, states st, \ + == int ch, FILE *d); + == #endif + */ +static void +print(m, caption, st, ch, d) +struct match *m; +char *caption; +states st; +int ch; +FILE *d; +{ + register struct re_guts *g = m->g; + register int i; + register int first = 1; + + if (!(m->eflags®_TRACE)) + return; + + fprintf(d, "%s", caption); + if (ch != '\0') + fprintf(d, " %s", pchar(ch)); + for (i = 0; i < g->nstates; i++) + if (ISSET(st, i)) { + fprintf(d, "%s%d", (first) ? "\t" : ", ", i); + first = 0; + } + fprintf(d, "\n"); +} + +/* + - at - print current situation + == #ifdef REDEBUG + == static void at(struct match *m, char *title, char *start, char *stop, \ + == sopno startst, sopno stopst); + == #endif + */ +static void +at(m, title, start, stop, startst, stopst) +struct match *m; +char *title; +char *start; +char *stop; +sopno startst; +sopno stopst; +{ + if (!(m->eflags®_TRACE)) + return; + + printf("%s %s-", title, pchar(*start)); + printf("%s ", pchar(*stop)); + printf("%ld-%ld\n", (long)startst, (long)stopst); +} + +#ifndef PCHARDONE +#define PCHARDONE /* never again */ +/* + - pchar - make a character printable + == #ifdef REDEBUG + == static char *pchar(int ch); + == #endif + * + * Is this identical to regchar() over in debug.c? Well, yes. But a + * duplicate here avoids having a debugging-capable regexec.o tied to + * a matching debug.o, and this is convenient. It all disappears in + * the non-debug compilation anyway, so it doesn't matter much. + */ +static char * /* -> representation */ +pchar(ch) +int ch; +{ + static char pbuf[10]; + + if (isprint(ch) || ch == ' ') + sprintf(pbuf, "%c", ch); + else + sprintf(pbuf, "\\%o", ch); + return(pbuf); +} +#endif +#endif + +#undef matcher +#undef fast +#undef slow +#undef dissect +#undef backref +#undef step +#undef print +#undef at +#undef match diff --git a/lib/regex/re_format.7 b/lib/regex/re_format.7 new file mode 100755 index 000000000..c02c93fda --- /dev/null +++ b/lib/regex/re_format.7 @@ -0,0 +1,269 @@ +.\" Copyright (c) 1992, 1993, 1994 Henry Spencer. +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Henry Spencer. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)re_format.7 8.3 (Berkeley) 3/20/94 +.\" +.TH RE_FORMAT 7 "March 20, 1994" +.SH NAME +re_format \- POSIX 1003.2 regular expressions +.SH DESCRIPTION +Regular expressions (``RE''s), +as defined in POSIX 1003.2, come in two forms: +modern REs (roughly those of +.BR egrep ; +1003.2 calls these ``extended'' REs) +and obsolete REs (roughly those of +.BR ed ; +1003.2 ``basic'' REs). +Obsolete REs mostly exist for backward compatibility in some old programs; +they will be discussed at the end. +1003.2 leaves some aspects of RE syntax and semantics open; +`\(dg' marks decisions on these aspects that +may not be fully portable to other 1003.2 implementations. +.PP +A (modern) RE is one\(dg or more non-empty\(dg \fIbranches\fR, +separated by `|'. +It matches anything that matches one of the branches. +.PP +A branch is one\(dg or more \fIpieces\fR, concatenated. +It matches a match for the first, followed by a match for the second, etc. +.PP +A piece is an \fIatom\fR possibly followed +by a single\(dg `*', `+', `?', or \fIbound\fR. +An atom followed by `*' matches a sequence of 0 or more matches of the atom. +An atom followed by `+' matches a sequence of 1 or more matches of the atom. +An atom followed by `?' matches a sequence of 0 or 1 matches of the atom. +.PP +A \fIbound\fR is `{' followed by an unsigned decimal integer, +possibly followed by `,' +possibly followed by another unsigned decimal integer, +always followed by `}'. +The integers must lie between 0 and RE_DUP_MAX (255\(dg) inclusive, +and if there are two of them, the first may not exceed the second. +An atom followed by a bound containing one integer \fIi\fR +and no comma matches +a sequence of exactly \fIi\fR matches of the atom. +An atom followed by a bound +containing one integer \fIi\fR and a comma matches +a sequence of \fIi\fR or more matches of the atom. +An atom followed by a bound +containing two integers \fIi\fR and \fIj\fR matches +a sequence of \fIi\fR through \fIj\fR (inclusive) matches of the atom. +.PP +An atom is a regular expression enclosed in `()' (matching a match for the +regular expression), +an empty set of `()' (matching the null string)\(dg, +a \fIbracket expression\fR (see below), `.' +(matching any single character), `^' (matching the null string at the +beginning of a line), `$' (matching the null string at the +end of a line), a `\e' followed by one of the characters +`^.[$()|*+?{\e' +(matching that character taken as an ordinary character), +a `\e' followed by any other character\(dg +(matching that character taken as an ordinary character, +as if the `\e' had not been present\(dg), +or a single character with no other significance (matching that character). +A `{' followed by a character other than a digit is an ordinary +character, not the beginning of a bound\(dg. +It is illegal to end an RE with `\e'. +.PP +A \fIbracket expression\fR is a list of characters enclosed in `[]'. +It normally matches any single character from the list (but see below). +If the list begins with `^', +it matches any single character +(but see below) \fInot\fR from the rest of the list. +If two characters in the list are separated by `\-', this is shorthand +for the full \fIrange\fR of characters between those two (inclusive) in the +collating sequence, +e.g. `[0-9]' in ASCII matches any decimal digit. +It is illegal\(dg for two ranges to share an +endpoint, e.g. `a-c-e'. +Ranges are very collating-sequence-dependent, +and portable programs should avoid relying on them. +.PP +To include a literal `]' in the list, make it the first character +(following a possible `^'). +To include a literal `\-', make it the first or last character, +or the second endpoint of a range. +To use a literal `\-' as the first endpoint of a range, +enclose it in `[.' and `.]' to make it a collating element (see below). +With the exception of these and some combinations using `[' (see next +paragraphs), all other special characters, including `\e', lose their +special significance within a bracket expression. +.PP +Within a bracket expression, a collating element (a character, +a multi-character sequence that collates as if it were a single character, +or a collating-sequence name for either) +enclosed in `[.' and `.]' stands for the +sequence of characters of that collating element. +The sequence is a single element of the bracket expression's list. +A bracket expression containing a multi-character collating element +can thus match more than one character, +e.g. if the collating sequence includes a `ch' collating element, +then the RE `[[.ch.]]*c' matches the first five characters +of `chchcc'. +.PP +Within a bracket expression, a collating element enclosed in `[=' and +`=]' is an equivalence class, standing for the sequences of characters +of all collating elements equivalent to that one, including itself. +(If there are no other equivalent collating elements, +the treatment is as if the enclosing delimiters were `[.' and `.]'.) +For example, if o and \o'o^' are the members of an equivalence class, +then `[[=o=]]', `[[=\o'o^'=]]', and `[o\o'o^']' are all synonymous. +An equivalence class may not\(dg be an endpoint +of a range. +.PP +Within a bracket expression, the name of a \fIcharacter class\fR enclosed +in `[:' and `:]' stands for the list of all characters belonging to that +class. +Standard character class names are: +.PP +.RS +.nf +.ta 3c 6c 9c +alnum digit punct +alpha graph space +blank lower upper +cntrl print xdigit +.fi +.RE +.PP +These stand for the character classes defined in +.BR ctype (3). +A locale may provide others. +A character class may not be used as an endpoint of a range. +.PP +There are two special cases\(dg of bracket expressions: +the bracket expressions `[[:<:]]' and `[[:>:]]' match the null string at +the beginning and end of a word respectively. +A word is defined as a sequence of +word characters +which is neither preceded nor followed by +word characters. +A word character is an +.B alnum +character (as defined by +.BR ctype (3)) +or an underscore. +This is an extension, +compatible with but not specified by POSIX 1003.2, +and should be used with +caution in software intended to be portable to other systems. +.PP +In the event that an RE could match more than one substring of a given +string, +the RE matches the one starting earliest in the string. +If the RE could match more than one substring starting at that point, +it matches the longest. +Subexpressions also match the longest possible substrings, subject to +the constraint that the whole match be as long as possible, +with subexpressions starting earlier in the RE taking priority over +ones starting later. +Note that higher-level subexpressions thus take priority over +their lower-level component subexpressions. +.PP +Match lengths are measured in characters, not collating elements. +A null string is considered longer than no match at all. +For example, +`bb*' matches the three middle characters of `abbbc', +`(wee|week)(knights|nights)' matches all ten characters of `weeknights', +when `(.*).*' is matched against `abc' the parenthesized subexpression +matches all three characters, and +when `(a*)*' is matched against `bc' both the whole RE and the parenthesized +subexpression match the null string. +.PP +If case-independent matching is specified, +the effect is much as if all case distinctions had vanished from the +alphabet. +When an alphabetic that exists in multiple cases appears as an +ordinary character outside a bracket expression, it is effectively +transformed into a bracket expression containing both cases, +e.g. `x' becomes `[xX]'. +When it appears inside a bracket expression, all case counterparts +of it are added to the bracket expression, so that (e.g.) `[x]' +becomes `[xX]' and `[^x]' becomes `[^xX]'. +.PP +No particular limit is imposed on the length of REs\(dg. +Programs intended to be portable should not employ REs longer +than 256 bytes, +as an implementation can refuse to accept such REs and remain +POSIX-compliant. +.PP +Obsolete (``basic'') regular expressions differ in several respects. +`|', `+', and `?' are ordinary characters and there is no equivalent +for their functionality. +The delimiters for bounds are `\e{' and `\e}', +with `{' and `}' by themselves ordinary characters. +The parentheses for nested subexpressions are `\e(' and `\e)', +with `(' and `)' by themselves ordinary characters. +`^' is an ordinary character except at the beginning of the +RE or\(dg the beginning of a parenthesized subexpression, +`$' is an ordinary character except at the end of the +RE or\(dg the end of a parenthesized subexpression, +and `*' is an ordinary character if it appears at the beginning of the +RE or the beginning of a parenthesized subexpression +(after a possible leading `^'). +Finally, there is one new type of atom, a \fIback reference\fR: +`\e' followed by a non-zero decimal digit \fId\fR +matches the same sequence of characters +matched by the \fId\fRth parenthesized subexpression +(numbering subexpressions by the positions of their opening parentheses, +left to right), +so that (e.g.) `\e([bc]\e)\e1' matches `bb' or `cc' but not `bc'. +.SH SEE ALSO +regex(3) +.PP +POSIX 1003.2, section 2.8 (Regular Expression Notation). +.SH BUGS +Having two kinds of REs is a botch. +.PP +The current 1003.2 spec says that `)' is an ordinary character in +the absence of an unmatched `('; +this was an unintentional result of a wording error, +and change is likely. +Avoid relying on it. +.PP +Back references are a dreadful botch, +posing major problems for efficient implementations. +They are also somewhat vaguely defined +(does +`a\e(\e(b\e)*\e2\e)*d' match `abbbd'?). +Avoid using them. +.PP +1003.2's specification of case-independent matching is vague. +The ``one case implies all cases'' definition given above +is current consensus among implementors as to the right interpretation. +.PP +The syntax for word boundaries is incredibly ugly. diff --git a/lib/regex/regcomp.c b/lib/regex/regcomp.c new file mode 100755 index 000000000..cd15a1680 --- /dev/null +++ b/lib/regex/regcomp.c @@ -0,0 +1,1706 @@ +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regcomp.c 8.5 (Berkeley) 3/20/94 + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)regcomp.c 8.5 (Berkeley) 3/20/94"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#ifdef __minix_vmd +#include <bsd/asciictype.h> +#else +#include <ctype.h> +#endif +#include <limits.h> +#include <stdlib.h> +#include <regex.h> + +#include "utils.h" +#include "regex2.h" + +#include "cclass.h" +#include "cname.h" + +/* + * parse structure, passed up and down to avoid global variables and + * other clumsinesses + */ +struct parse { + char *next; /* next character in RE */ + char *end; /* end of string (-> NUL normally) */ + int error; /* has an error been seen? */ + sop *strip; /* malloced strip */ + sopno ssize; /* malloced strip size (allocated) */ + sopno slen; /* malloced strip length (used) */ + int ncsalloc; /* number of csets allocated */ + struct re_guts *g; +# define NPAREN 10 /* we need to remember () 1-9 for back refs */ + sopno pbegin[NPAREN]; /* -> ( ([0] unused) */ + sopno pend[NPAREN]; /* -> ) ([0] unused) */ +}; + +/* ========= begin header generated by ./mkh ========= */ +#ifdef __cplusplus +extern "C" { +#endif + +/* === regcomp.c === */ +static void p_ere(struct parse *p, int stop); +static void p_ere_exp(struct parse *p); +static void p_str(struct parse *p); +static void p_bre(struct parse *p, int end1, int end2); +static int p_simp_re(struct parse *p, int starordinary); +static int p_count(struct parse *p); +static void p_bracket(struct parse *p); +static void p_b_term(struct parse *p, cset *cs); +static void p_b_cclass(struct parse *p, cset *cs); +static void p_b_eclass(struct parse *p, cset *cs); +static char p_b_symbol(struct parse *p); +static char p_b_coll_elem(struct parse *p, int endc); +static char othercase(int ch); +static void bothcases(struct parse *p, int ch); +static void ordinary(struct parse *p, int ch); +static void nonnewline(struct parse *p); +static void repeat(struct parse *p, sopno start, int from, int to); +static int seterr(struct parse *p, int e); +static cset *allocset(struct parse *p); +static void freeset(struct parse *p, cset *cs); +static int freezeset(struct parse *p, cset *cs); +static int firstch(struct parse *p, cset *cs); +static int nch(struct parse *p, cset *cs); +static void mcadd(struct parse *p, cset *cs, char *cp); +static void mcsub(cset *cs, char *cp); +static int mcin(cset *cs, char *cp); +static char *mcfind(cset *cs, char *cp); +static void mcinvert(struct parse *p, cset *cs); +static void mccase(struct parse *p, cset *cs); +static int isinsets(struct re_guts *g, int c); +static int samesets(struct re_guts *g, int c1, int c2); +static void categorize(struct parse *p, struct re_guts *g); +static sopno dupl(struct parse *p, sopno start, sopno finish); +static void doemit(struct parse *p, sop op, size_t opnd); +static void doinsert(struct parse *p, sop op, size_t opnd, sopno pos); +static void dofwd(struct parse *p, sopno pos, sop value); +static void enlarge(struct parse *p, sopno size); +static void stripsnug(struct parse *p, struct re_guts *g); +static void findmust(struct parse *p, struct re_guts *g); +static sopno pluscount(struct parse *p, struct re_guts *g); + +#ifdef __cplusplus +} +#endif +/* ========= end header generated by ./mkh ========= */ + +static char nuls[10]; /* place to point scanner in event of error */ + +/* + * macros for use with parse structure + * BEWARE: these know that the parse structure is named `p' !!! + */ +#define PEEK() (*p->next) +#define PEEK2() (*(p->next+1)) +#define MORE() (p->next < p->end) +#define MORE2() (p->next+1 < p->end) +#define SEE(c) (MORE() && PEEK() == (c)) +#define SEETWO(a, b) (MORE() && MORE2() && PEEK() == (a) && PEEK2() == (b)) +#define EAT(c) ((SEE(c)) ? (NEXT(), 1) : 0) +#define EATTWO(a, b) ((SEETWO(a, b)) ? (NEXT2(), 1) : 0) +#define NEXT() (p->next++) +#define NEXT2() (p->next += 2) +#define NEXTn(n) (p->next += (n)) +#define GETNEXT() (*p->next++) +#define SETERROR(e) seterr(p, (e)) +#define REQUIRE(co, e) ((co) || SETERROR(e)) +#define MUSTSEE(c, e) (REQUIRE(MORE() && PEEK() == (c), e)) +#define MUSTEAT(c, e) (REQUIRE(MORE() && GETNEXT() == (c), e)) +#define MUSTNOTSEE(c, e) (REQUIRE(!MORE() || PEEK() != (c), e)) +#define EMIT(op, sopnd) doemit(p, (sop)(op), (size_t)(sopnd)) +#define INSERT(op, pos) doinsert(p, (sop)(op), HERE()-(pos)+1, pos) +#define AHEAD(pos) dofwd(p, pos, HERE()-(pos)) +#define ASTERN(sop, pos) EMIT(sop, HERE()-pos) +#define HERE() (p->slen) +#define THERE() (p->slen - 1) +#define THERETHERE() (p->slen - 2) +#define DROP(n) (p->slen -= (n)) + +#ifndef NDEBUG +static int never = 0; /* for use in asserts; shuts lint up */ +#else +#define never 0 /* some <assert.h>s have bugs too */ +#endif + +/* + - regcomp - interface for parser and compilation + = extern int regcomp(regex_t *, const char *, int); + = #define REG_BASIC 0000 + = #define REG_EXTENDED 0001 + = #define REG_ICASE 0002 + = #define REG_NOSUB 0004 + = #define REG_NEWLINE 0010 + = #define REG_NOSPEC 0020 + = #define REG_PEND 0040 + = #define REG_DUMP 0200 + */ +int /* 0 success, otherwise REG_something */ +regcomp(preg, pattern, cflags) +regex_t *preg; +const char *pattern; +int cflags; +{ + struct parse pa; + register struct re_guts *g; + register struct parse *p = &pa; + register int i; + register size_t len; +#ifdef REDEBUG +# define GOODFLAGS(f) (f) +#else +# define GOODFLAGS(f) ((f)&~REG_DUMP) +#endif + + cflags = GOODFLAGS(cflags); + if ((cflags®_EXTENDED) && (cflags®_NOSPEC)) + return(REG_INVARG); + + if (cflags®_PEND) { + if (preg->re_endp < pattern) + return(REG_INVARG); + len = preg->re_endp - pattern; + } else + len = strlen((char *)pattern); + + /* do the mallocs early so failure handling is easy */ + g = (struct re_guts *)malloc(sizeof(struct re_guts) + + (NC-1)*sizeof(cat_t)); + if (g == NULL) + return(REG_ESPACE); + p->ssize = len/(size_t)2*(size_t)3 + (size_t)1; /* ugh */ + p->strip = (sop *)malloc(p->ssize * sizeof(sop)); + p->slen = 0; + if (p->strip == NULL) { + free((char *)g); + return(REG_ESPACE); + } + + /* set things up */ + p->g = g; + p->next = (char *)pattern; /* convenience; we do not modify it */ + p->end = p->next + len; + p->error = 0; + p->ncsalloc = 0; + for (i = 0; i < NPAREN; i++) { + p->pbegin[i] = 0; + p->pend[i] = 0; + } + g->csetsize = NC; + g->sets = NULL; + g->setbits = NULL; + g->ncsets = 0; + g->cflags = cflags; + g->iflags = 0; + g->nbol = 0; + g->neol = 0; + g->must = NULL; + g->mlen = 0; + g->nsub = 0; + g->ncategories = 1; /* category 0 is "everything else" */ + g->categories = &g->catspace[-(CHAR_MIN)]; + (void) memset((char *)g->catspace, 0, NC*sizeof(cat_t)); + g->backrefs = 0; + + /* do it */ + EMIT(OEND, 0); + g->firststate = THERE(); + if (cflags®_EXTENDED) + p_ere(p, OUT); + else if (cflags®_NOSPEC) + p_str(p); + else + p_bre(p, OUT, OUT); + EMIT(OEND, 0); + g->laststate = THERE(); + + /* tidy up loose ends and fill things in */ + categorize(p, g); + stripsnug(p, g); + findmust(p, g); + g->nplus = pluscount(p, g); + g->magic = MAGIC2; + preg->re_nsub = g->nsub; + preg->re_g = g; + preg->re_magic = MAGIC1; +#ifndef REDEBUG + /* not debugging, so can't rely on the assert() in regexec() */ + if (g->iflags&BAD) + SETERROR(REG_ASSERT); +#endif + + /* win or lose, we're done */ + if (p->error != 0) /* lose */ + regfree(preg); + return(p->error); +} + +/* + - p_ere - ERE parser top level, concatenation and alternation + == static void p_ere(register struct parse *p, int stop); + */ +static void +p_ere(p, stop) +register struct parse *p; +int stop; /* character this ERE should end at */ +{ + register char c; + register sopno prevback; + register sopno prevfwd; + register sopno conc; + register int first = 1; /* is this the first alternative? */ + + for (;;) { + /* do a bunch of concatenated expressions */ + conc = HERE(); + while (MORE() && (c = PEEK()) != '|' && c != stop) + p_ere_exp(p); + REQUIRE(HERE() != conc, REG_EMPTY); /* require nonempty */ + + if (!EAT('|')) + break; /* NOTE BREAK OUT */ + + if (first) { + INSERT(OCH_, conc); /* offset is wrong */ + prevfwd = conc; + prevback = conc; + first = 0; + } + ASTERN(OOR1, prevback); + prevback = THERE(); + AHEAD(prevfwd); /* fix previous offset */ + prevfwd = HERE(); + EMIT(OOR2, 0); /* offset is very wrong */ + } + + if (!first) { /* tail-end fixups */ + AHEAD(prevfwd); + ASTERN(O_CH, prevback); + } + + assert(!MORE() || SEE(stop)); +} + +/* + - p_ere_exp - parse one subERE, an atom possibly followed by a repetition op + == static void p_ere_exp(register struct parse *p); + */ +static void +p_ere_exp(p) +register struct parse *p; +{ + register char c; + register sopno pos; + register int count; + register int count2; + register sopno subno; + int wascaret = 0; + + assert(MORE()); /* caller should have ensured this */ + c = GETNEXT(); + + pos = HERE(); + switch (c) { + case '(': + REQUIRE(MORE(), REG_EPAREN); + p->g->nsub++; + subno = p->g->nsub; + if (subno < NPAREN) + p->pbegin[subno] = HERE(); + EMIT(OLPAREN, subno); + if (!SEE(')')) + p_ere(p, ')'); + if (subno < NPAREN) { + p->pend[subno] = HERE(); + assert(p->pend[subno] != 0); + } + EMIT(ORPAREN, subno); + MUSTEAT(')', REG_EPAREN); + break; +#ifndef POSIX_MISTAKE + case ')': /* happens only if no current unmatched ( */ + /* + * You may ask, why the ifndef? Because I didn't notice + * this until slightly too late for 1003.2, and none of the + * other 1003.2 regular-expression reviewers noticed it at + * all. So an unmatched ) is legal POSIX, at least until + * we can get it fixed. + */ + SETERROR(REG_EPAREN); + break; +#endif + case '^': + EMIT(OBOL, 0); + p->g->iflags |= USEBOL; + p->g->nbol++; + wascaret = 1; + break; + case '$': + EMIT(OEOL, 0); + p->g->iflags |= USEEOL; + p->g->neol++; + break; + case '|': + SETERROR(REG_EMPTY); + break; + case '*': + case '+': + case '?': + SETERROR(REG_BADRPT); + break; + case '.': + if (p->g->cflags®_NEWLINE) + nonnewline(p); + else + EMIT(OANY, 0); + break; + case '[': + p_bracket(p); + break; + case '\\': + REQUIRE(MORE(), REG_EESCAPE); + c = GETNEXT(); + ordinary(p, c); + break; + case '{': /* okay as ordinary except if digit follows */ + REQUIRE(!MORE() || !isdigit(PEEK()), REG_BADRPT); + /* FALLTHROUGH */ + default: + ordinary(p, c); + break; + } + + if (!MORE()) + return; + c = PEEK(); + /* we call { a repetition if followed by a digit */ + if (!( c == '*' || c == '+' || c == '?' || + (c == '{' && MORE2() && isdigit(PEEK2())) )) + return; /* no repetition, we're done */ + NEXT(); + + REQUIRE(!wascaret, REG_BADRPT); + switch (c) { + case '*': /* implemented as +? */ + /* this case does not require the (y|) trick, noKLUDGE */ + INSERT(OPLUS_, pos); + ASTERN(O_PLUS, pos); + INSERT(OQUEST_, pos); + ASTERN(O_QUEST, pos); + break; + case '+': + INSERT(OPLUS_, pos); + ASTERN(O_PLUS, pos); + break; + case '?': + /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ + INSERT(OCH_, pos); /* offset slightly wrong */ + ASTERN(OOR1, pos); /* this one's right */ + AHEAD(pos); /* fix the OCH_ */ + EMIT(OOR2, 0); /* offset very wrong... */ + AHEAD(THERE()); /* ...so fix it */ + ASTERN(O_CH, THERETHERE()); + break; + case '{': + count = p_count(p); + if (EAT(',')) { + if (isdigit(PEEK())) { + count2 = p_count(p); + REQUIRE(count <= count2, REG_BADBR); + } else /* single number with comma */ + count2 = INFINITY; + } else /* just a single number */ + count2 = count; + repeat(p, pos, count, count2); + if (!EAT('}')) { /* error heuristics */ + while (MORE() && PEEK() != '}') + NEXT(); + REQUIRE(MORE(), REG_EBRACE); + SETERROR(REG_BADBR); + } + break; + } + + if (!MORE()) + return; + c = PEEK(); + if (!( c == '*' || c == '+' || c == '?' || + (c == '{' && MORE2() && isdigit(PEEK2())) ) ) + return; + SETERROR(REG_BADRPT); +} + +/* + - p_str - string (no metacharacters) "parser" + == static void p_str(register struct parse *p); + */ +static void +p_str(p) +register struct parse *p; +{ + REQUIRE(MORE(), REG_EMPTY); + while (MORE()) + ordinary(p, GETNEXT()); +} + +/* + - p_bre - BRE parser top level, anchoring and concatenation + == static void p_bre(register struct parse *p, register int end1, \ + == register int end2); + * Giving end1 as OUT essentially eliminates the end1/end2 check. + * + * This implementation is a bit of a kludge, in that a trailing $ is first + * taken as an ordinary character and then revised to be an anchor. The + * only undesirable side effect is that '$' gets included as a character + * category in such cases. This is fairly harmless; not worth fixing. + * The amount of lookahead needed to avoid this kludge is excessive. + */ +static void +p_bre(p, end1, end2) +register struct parse *p; +register int end1; /* first terminating character */ +register int end2; /* second terminating character */ +{ + register sopno start = HERE(); + register int first = 1; /* first subexpression? */ + register int wasdollar = 0; + + if (EAT('^')) { + EMIT(OBOL, 0); + p->g->iflags |= USEBOL; + p->g->nbol++; + } + while (MORE() && !SEETWO(end1, end2)) { + wasdollar = p_simp_re(p, first); + first = 0; + } + if (wasdollar) { /* oops, that was a trailing anchor */ + DROP(1); + EMIT(OEOL, 0); + p->g->iflags |= USEEOL; + p->g->neol++; + } + + REQUIRE(HERE() != start, REG_EMPTY); /* require nonempty */ +} + +/* + - p_simp_re - parse a simple RE, an atom possibly followed by a repetition + == static int p_simp_re(register struct parse *p, int starordinary); + */ +static int /* was the simple RE an unbackslashed $? */ +p_simp_re(p, starordinary) +register struct parse *p; +int starordinary; /* is a leading * an ordinary character? */ +{ + register int c; + register int count; + register int count2; + register sopno pos; + register int i; + register sopno subno; +# define BACKSL (1<<CHAR_BIT) + + pos = HERE(); /* repetion op, if any, covers from here */ + + assert(MORE()); /* caller should have ensured this */ + c = GETNEXT(); + if (c == '\\') { + REQUIRE(MORE(), REG_EESCAPE); + c = BACKSL | (unsigned char)GETNEXT(); + } + switch (c) { + case '.': + if (p->g->cflags®_NEWLINE) + nonnewline(p); + else + EMIT(OANY, 0); + break; + case '[': + p_bracket(p); + break; + case BACKSL|'{': + SETERROR(REG_BADRPT); + break; + case BACKSL|'(': + p->g->nsub++; + subno = p->g->nsub; + if (subno < NPAREN) + p->pbegin[subno] = HERE(); + EMIT(OLPAREN, subno); + /* the MORE here is an error heuristic */ + if (MORE() && !SEETWO('\\', ')')) + p_bre(p, '\\', ')'); + if (subno < NPAREN) { + p->pend[subno] = HERE(); + assert(p->pend[subno] != 0); + } + EMIT(ORPAREN, subno); + REQUIRE(EATTWO('\\', ')'), REG_EPAREN); + break; + case BACKSL|')': /* should not get here -- must be user */ + case BACKSL|'}': + SETERROR(REG_EPAREN); + break; + case BACKSL|'1': + case BACKSL|'2': + case BACKSL|'3': + case BACKSL|'4': + case BACKSL|'5': + case BACKSL|'6': + case BACKSL|'7': + case BACKSL|'8': + case BACKSL|'9': + i = (c&~BACKSL) - '0'; + assert(i < NPAREN); + if (p->pend[i] != 0) { + assert(i <= p->g->nsub); + EMIT(OBACK_, i); + assert(p->pbegin[i] != 0); + assert(OP(p->strip[p->pbegin[i]]) == OLPAREN); + assert(OP(p->strip[p->pend[i]]) == ORPAREN); + (void) dupl(p, p->pbegin[i]+1, p->pend[i]); + EMIT(O_BACK, i); + } else + SETERROR(REG_ESUBREG); + p->g->backrefs = 1; + break; + case '*': + REQUIRE(starordinary, REG_BADRPT); + /* FALLTHROUGH */ + default: + ordinary(p, c &~ BACKSL); + break; + } + + if (EAT('*')) { /* implemented as +? */ + /* this case does not require the (y|) trick, noKLUDGE */ + INSERT(OPLUS_, pos); + ASTERN(O_PLUS, pos); + INSERT(OQUEST_, pos); + ASTERN(O_QUEST, pos); + } else if (EATTWO('\\', '{')) { + count = p_count(p); + if (EAT(',')) { + if (MORE() && isdigit(PEEK())) { + count2 = p_count(p); + REQUIRE(count <= count2, REG_BADBR); + } else /* single number with comma */ + count2 = INFINITY; + } else /* just a single number */ + count2 = count; + repeat(p, pos, count, count2); + if (!EATTWO('\\', '}')) { /* error heuristics */ + while (MORE() && !SEETWO('\\', '}')) + NEXT(); + REQUIRE(MORE(), REG_EBRACE); + SETERROR(REG_BADBR); + } + } else if (c == (unsigned char)'$') /* $ (but not \$) ends it */ + return(1); + + return(0); +} + +/* + - p_count - parse a repetition count + == static int p_count(register struct parse *p); + */ +static int /* the value */ +p_count(p) +register struct parse *p; +{ + register int count = 0; + register int ndigits = 0; + + while (MORE() && isdigit(PEEK()) && count <= DUPMAX) { + count = count*10 + (GETNEXT() - '0'); + ndigits++; + } + + REQUIRE(ndigits > 0 && count <= DUPMAX, REG_BADBR); + return(count); +} + +/* + - p_bracket - parse a bracketed character list + == static void p_bracket(register struct parse *p); + * + * Note a significant property of this code: if the allocset() did SETERROR, + * no set operations are done. + */ +static void +p_bracket(p) +register struct parse *p; +{ + register char c; + register cset *cs = allocset(p); + register int invert = 0; + + /* Dept of Truly Sickening Special-Case Kludges */ + if (p->next + 5 < p->end && strncmp(p->next, "[:<:]]", 6) == 0) { + EMIT(OBOW, 0); + NEXTn(6); + return; + } + if (p->next + 5 < p->end && strncmp(p->next, "[:>:]]", 6) == 0) { + EMIT(OEOW, 0); + NEXTn(6); + return; + } + + if (EAT('^')) + invert++; /* make note to invert set at end */ + if (EAT(']')) + CHadd(cs, ']'); + else if (EAT('-')) + CHadd(cs, '-'); + while (MORE() && PEEK() != ']' && !SEETWO('-', ']')) + p_b_term(p, cs); + if (EAT('-')) + CHadd(cs, '-'); + MUSTEAT(']', REG_EBRACK); + + if (p->error != 0) /* don't mess things up further */ + return; + + if (p->g->cflags®_ICASE) { + register int i; + register int ci; + + for (i = p->g->csetsize - 1; i >= 0; i--) + if (CHIN(cs, i) && isalpha(i)) { + ci = othercase(i); + if (ci != i) + CHadd(cs, ci); + } + if (cs->multis != NULL) + mccase(p, cs); + } + if (invert) { + register int i; + + for (i = p->g->csetsize - 1; i >= 0; i--) + if (CHIN(cs, i)) + CHsub(cs, i); + else + CHadd(cs, i); + if (p->g->cflags®_NEWLINE) + CHsub(cs, '\n'); + if (cs->multis != NULL) + mcinvert(p, cs); + } + + assert(cs->multis == NULL); /* xxx */ + + if (nch(p, cs) == 1) { /* optimize singleton sets */ + ordinary(p, firstch(p, cs)); + freeset(p, cs); + } else + EMIT(OANYOF, freezeset(p, cs)); +} + +/* + - p_b_term - parse one term of a bracketed character list + == static void p_b_term(register struct parse *p, register cset *cs); + */ +static void +p_b_term(p, cs) +register struct parse *p; +register cset *cs; +{ + register char c; + register char start, finish; + register int i; + + /* classify what we've got */ + switch ((MORE()) ? PEEK() : '\0') { + case '[': + c = (MORE2()) ? PEEK2() : '\0'; + break; + case '-': + SETERROR(REG_ERANGE); + return; /* NOTE RETURN */ + break; + default: + c = '\0'; + break; + } + + switch (c) { + case ':': /* character class */ + NEXT2(); + REQUIRE(MORE(), REG_EBRACK); + c = PEEK(); + REQUIRE(c != '-' && c != ']', REG_ECTYPE); + p_b_cclass(p, cs); + REQUIRE(MORE(), REG_EBRACK); + REQUIRE(EATTWO(':', ']'), REG_ECTYPE); + break; + case '=': /* equivalence class */ + NEXT2(); + REQUIRE(MORE(), REG_EBRACK); + c = PEEK(); + REQUIRE(c != '-' && c != ']', REG_ECOLLATE); + p_b_eclass(p, cs); + REQUIRE(MORE(), REG_EBRACK); + REQUIRE(EATTWO('=', ']'), REG_ECOLLATE); + break; + default: /* symbol, ordinary character, or range */ +/* xxx revision needed for multichar stuff */ + start = p_b_symbol(p); + if (SEE('-') && MORE2() && PEEK2() != ']') { + /* range */ + NEXT(); + if (EAT('-')) + finish = '-'; + else + finish = p_b_symbol(p); + } else + finish = start; +/* xxx what about signed chars here... */ + REQUIRE(start <= finish, REG_ERANGE); + for (i = start; i <= finish; i++) + CHadd(cs, i); + break; + } +} + +/* + - p_b_cclass - parse a character-class name and deal with it + == static void p_b_cclass(register struct parse *p, register cset *cs); + */ +static void +p_b_cclass(p, cs) +register struct parse *p; +register cset *cs; +{ + register char *sp = p->next; + register struct cclass *cp; + register size_t len; + register char *u; + register char c; + + while (MORE() && isalpha(PEEK())) + NEXT(); + len = p->next - sp; + for (cp = cclasses; cp->name != NULL; cp++) + if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') + break; + if (cp->name == NULL) { + /* oops, didn't find it */ + SETERROR(REG_ECTYPE); + return; + } + + u = cp->chars; + while ((c = *u++) != '\0') + CHadd(cs, c); + for (u = cp->multis; *u != '\0'; u += strlen(u) + 1) + MCadd(p, cs, u); +} + +/* + - p_b_eclass - parse an equivalence-class name and deal with it + == static void p_b_eclass(register struct parse *p, register cset *cs); + * + * This implementation is incomplete. xxx + */ +static void +p_b_eclass(p, cs) +register struct parse *p; +register cset *cs; +{ + register char c; + + c = p_b_coll_elem(p, '='); + CHadd(cs, c); +} + +/* + - p_b_symbol - parse a character or [..]ed multicharacter collating symbol + == static char p_b_symbol(register struct parse *p); + */ +static char /* value of symbol */ +p_b_symbol(p) +register struct parse *p; +{ + register char value; + + REQUIRE(MORE(), REG_EBRACK); + if (!EATTWO('[', '.')) + return(GETNEXT()); + + /* collating symbol */ + value = p_b_coll_elem(p, '.'); + REQUIRE(EATTWO('.', ']'), REG_ECOLLATE); + return(value); +} + +/* + - p_b_coll_elem - parse a collating-element name and look it up + == static char p_b_coll_elem(register struct parse *p, int endc); + */ +static char /* value of collating element */ +p_b_coll_elem(p, endc) +register struct parse *p; +int endc; /* name ended by endc,']' */ +{ + register char *sp = p->next; + register struct cname *cp; + register int len; + register char c; + + while (MORE() && !SEETWO(endc, ']')) + NEXT(); + if (!MORE()) { + SETERROR(REG_EBRACK); + return(0); + } + len = p->next - sp; + for (cp = cnames; cp->name != NULL; cp++) + if (strncmp(cp->name, sp, len) == 0 && cp->name[len] == '\0') + return(cp->code); /* known name */ + if (len == 1) + return(*sp); /* single character */ + SETERROR(REG_ECOLLATE); /* neither */ + return(0); +} + +/* + - othercase - return the case counterpart of an alphabetic + == static char othercase(int ch); + */ +static char /* if no counterpart, return ch */ +othercase(ch) +int ch; +{ + assert(isalpha(ch)); + if (isupper(ch)) + return(tolower(ch)); + else if (islower(ch)) + return(toupper(ch)); + else /* peculiar, but could happen */ + return(ch); +} + +/* + - bothcases - emit a dualcase version of a two-case character + == static void bothcases(register struct parse *p, int ch); + * + * Boy, is this implementation ever a kludge... + */ +static void +bothcases(p, ch) +register struct parse *p; +int ch; +{ + register char *oldnext = p->next; + register char *oldend = p->end; + char bracket[3]; + + assert(othercase(ch) != ch); /* p_bracket() would recurse */ + p->next = bracket; + p->end = bracket+2; + bracket[0] = ch; + bracket[1] = ']'; + bracket[2] = '\0'; + p_bracket(p); + assert(p->next == bracket+2); + p->next = oldnext; + p->end = oldend; +} + +/* + - ordinary - emit an ordinary character + == static void ordinary(register struct parse *p, register int ch); + */ +static void +ordinary(p, ch) +register struct parse *p; +register int ch; +{ + register cat_t *cap = p->g->categories; + + if ((p->g->cflags®_ICASE) && isalpha(ch) && othercase(ch) != ch) + bothcases(p, ch); + else { + EMIT(OCHAR, (unsigned char)ch); + if (cap[ch] == 0) + cap[ch] = p->g->ncategories++; + } +} + +/* + - nonnewline - emit REG_NEWLINE version of OANY + == static void nonnewline(register struct parse *p); + * + * Boy, is this implementation ever a kludge... + */ +static void +nonnewline(p) +register struct parse *p; +{ + register char *oldnext = p->next; + register char *oldend = p->end; + char bracket[4]; + + p->next = bracket; + p->end = bracket+3; + bracket[0] = '^'; + bracket[1] = '\n'; + bracket[2] = ']'; + bracket[3] = '\0'; + p_bracket(p); + assert(p->next == bracket+3); + p->next = oldnext; + p->end = oldend; +} + +/* + - repeat - generate code for a bounded repetition, recursively if needed + == static void repeat(register struct parse *p, sopno start, int from, int to); + */ +static void +repeat(p, start, from, to) +register struct parse *p; +sopno start; /* operand from here to end of strip */ +int from; /* repeated from this number */ +int to; /* to this number of times (maybe INFINITY) */ +{ + register sopno finish = HERE(); +# define N 2 +# define INF 3 +# define REP(f, t) ((f)*8 + (t)) +# define MAP(n) (((n) <= 1) ? (n) : ((n) == INFINITY) ? INF : N) + register sopno copy; + + if (p->error != 0) /* head off possible runaway recursion */ + return; + + assert(from <= to); + + switch (REP(MAP(from), MAP(to))) { + case REP(0, 0): /* must be user doing this */ + DROP(finish-start); /* drop the operand */ + break; + case REP(0, 1): /* as x{1,1}? */ + case REP(0, N): /* as x{1,n}? */ + case REP(0, INF): /* as x{1,}? */ + /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ + INSERT(OCH_, start); /* offset is wrong... */ + repeat(p, start+1, 1, to); + ASTERN(OOR1, start); + AHEAD(start); /* ... fix it */ + EMIT(OOR2, 0); + AHEAD(THERE()); + ASTERN(O_CH, THERETHERE()); + break; + case REP(1, 1): /* trivial case */ + /* done */ + break; + case REP(1, N): /* as x?x{1,n-1} */ + /* KLUDGE: emit y? as (y|) until subtle bug gets fixed */ + INSERT(OCH_, start); + ASTERN(OOR1, start); + AHEAD(start); + EMIT(OOR2, 0); /* offset very wrong... */ + AHEAD(THERE()); /* ...so fix it */ + ASTERN(O_CH, THERETHERE()); + copy = dupl(p, start+1, finish+1); + assert(copy == finish+4); + repeat(p, copy, 1, to-1); + break; + case REP(1, INF): /* as x+ */ + INSERT(OPLUS_, start); + ASTERN(O_PLUS, start); + break; + case REP(N, N): /* as xx{m-1,n-1} */ + copy = dupl(p, start, finish); + repeat(p, copy, from-1, to-1); + break; + case REP(N, INF): /* as xx{n-1,INF} */ + copy = dupl(p, start, finish); + repeat(p, copy, from-1, to); + break; + default: /* "can't happen" */ + SETERROR(REG_ASSERT); /* just in case */ + break; + } +} + +/* + - seterr - set an error condition + == static int seterr(register struct parse *p, int e); + */ +static int /* useless but makes type checking happy */ +seterr(p, e) +register struct parse *p; +int e; +{ + if (p->error == 0) /* keep earliest error condition */ + p->error = e; + p->next = nuls; /* try to bring things to a halt */ + p->end = nuls; + return(0); /* make the return value well-defined */ +} + +/* + - allocset - allocate a set of characters for [] + == static cset *allocset(register struct parse *p); + */ +static cset * +allocset(p) +register struct parse *p; +{ + register int no = p->g->ncsets++; + register size_t nc; + register size_t nbytes; + register cset *cs; + register size_t css = (size_t)p->g->csetsize; + register int i; + + if (no >= p->ncsalloc) { /* need another column of space */ + p->ncsalloc += CHAR_BIT; + nc = p->ncsalloc; + assert(nc % CHAR_BIT == 0); + nbytes = nc / CHAR_BIT * css; + if (p->g->sets == NULL) + p->g->sets = (cset *)malloc(nc * sizeof(cset)); + else + p->g->sets = (cset *)realloc((char *)p->g->sets, + nc * sizeof(cset)); + if (p->g->setbits == NULL) + p->g->setbits = (uch *)malloc(nbytes); + else { + p->g->setbits = (uch *)realloc((char *)p->g->setbits, + nbytes); + /* xxx this isn't right if setbits is now NULL */ + for (i = 0; i < no; i++) + p->g->sets[i].ptr = p->g->setbits + css*(i/CHAR_BIT); + } + if (p->g->sets != NULL && p->g->setbits != NULL) + (void) memset((char *)p->g->setbits + (nbytes - css), + 0, css); + else { + no = 0; + SETERROR(REG_ESPACE); + /* caller's responsibility not to do set ops */ + } + } + + assert(p->g->sets != NULL); /* xxx */ + cs = &p->g->sets[no]; + cs->ptr = p->g->setbits + css*((no)/CHAR_BIT); + cs->mask = 1 << ((no) % CHAR_BIT); + cs->hash = 0; + cs->smultis = 0; + cs->multis = NULL; + + return(cs); +} + +/* + - freeset - free a now-unused set + == static void freeset(register struct parse *p, register cset *cs); + */ +static void +freeset(p, cs) +register struct parse *p; +register cset *cs; +{ + register int i; + register cset *top = &p->g->sets[p->g->ncsets]; + register size_t css = (size_t)p->g->csetsize; + + for (i = 0; i < css; i++) + CHsub(cs, i); + if (cs == top-1) /* recover only the easy case */ + p->g->ncsets--; +} + +/* + - freezeset - final processing on a set of characters + == static int freezeset(register struct parse *p, register cset *cs); + * + * The main task here is merging identical sets. This is usually a waste + * of time (although the hash code minimizes the overhead), but can win + * big if REG_ICASE is being used. REG_ICASE, by the way, is why the hash + * is done using addition rather than xor -- all ASCII [aA] sets xor to + * the same value! + */ +static int /* set number */ +freezeset(p, cs) +register struct parse *p; +register cset *cs; +{ + register uch h = cs->hash; + register int i; + register cset *top = &p->g->sets[p->g->ncsets]; + register cset *cs2; + register size_t css = (size_t)p->g->csetsize; + + /* look for an earlier one which is the same */ + for (cs2 = &p->g->sets[0]; cs2 < top; cs2++) + if (cs2->hash == h && cs2 != cs) { + /* maybe */ + for (i = 0; i < css; i++) + if (!!CHIN(cs2, i) != !!CHIN(cs, i)) + break; /* no */ + if (i == css) + break; /* yes */ + } + + if (cs2 < top) { /* found one */ + freeset(p, cs); + cs = cs2; + } + + return((int)(cs - p->g->sets)); +} + +/* + - firstch - return first character in a set (which must have at least one) + == static int firstch(register struct parse *p, register cset *cs); + */ +static int /* character; there is no "none" value */ +firstch(p, cs) +register struct parse *p; +register cset *cs; +{ + register int i; + register size_t css = (size_t)p->g->csetsize; + + for (i = 0; i < css; i++) + if (CHIN(cs, i)) + return((char)i); + assert(never); + return(0); /* arbitrary */ +} + +/* + - nch - number of characters in a set + == static int nch(register struct parse *p, register cset *cs); + */ +static int +nch(p, cs) +register struct parse *p; +register cset *cs; +{ + register int i; + register size_t css = (size_t)p->g->csetsize; + register int n = 0; + + for (i = 0; i < css; i++) + if (CHIN(cs, i)) + n++; + return(n); +} + +/* + - mcadd - add a collating element to a cset + == static void mcadd(register struct parse *p, register cset *cs, \ + == register char *cp); + */ +static void +mcadd(p, cs, cp) +register struct parse *p; +register cset *cs; +register char *cp; +{ + register size_t oldend = cs->smultis; + + cs->smultis += strlen(cp) + 1; + if (cs->multis == NULL) + cs->multis = malloc(cs->smultis); + else + cs->multis = realloc(cs->multis, cs->smultis); + if (cs->multis == NULL) { + SETERROR(REG_ESPACE); + return; + } + + (void) strcpy(cs->multis + oldend - 1, cp); + cs->multis[cs->smultis - 1] = '\0'; +} + +/* + - mcsub - subtract a collating element from a cset + == static void mcsub(register cset *cs, register char *cp); + */ +static void +mcsub(cs, cp) +register cset *cs; +register char *cp; +{ + register char *fp = mcfind(cs, cp); + register size_t len = strlen(fp); + + assert(fp != NULL); + (void) memmove(fp, fp + len + 1, + cs->smultis - (fp + len + 1 - cs->multis)); + cs->smultis -= len; + + if (cs->smultis == 0) { + free(cs->multis); + cs->multis = NULL; + return; + } + + cs->multis = realloc(cs->multis, cs->smultis); + assert(cs->multis != NULL); +} + +/* + - mcin - is a collating element in a cset? + == static int mcin(register cset *cs, register char *cp); + */ +static int +mcin(cs, cp) +register cset *cs; +register char *cp; +{ + return(mcfind(cs, cp) != NULL); +} + +/* + - mcfind - find a collating element in a cset + == static char *mcfind(register cset *cs, register char *cp); + */ +static char * +mcfind(cs, cp) +register cset *cs; +register char *cp; +{ + register char *p; + + if (cs->multis == NULL) + return(NULL); + for (p = cs->multis; *p != '\0'; p += strlen(p) + 1) + if (strcmp(cp, p) == 0) + return(p); + return(NULL); +} + +/* + - mcinvert - invert the list of collating elements in a cset + == static void mcinvert(register struct parse *p, register cset *cs); + * + * This would have to know the set of possibilities. Implementation + * is deferred. + */ +static void +mcinvert(p, cs) +register struct parse *p; +register cset *cs; +{ + assert(cs->multis == NULL); /* xxx */ +} + +/* + - mccase - add case counterparts of the list of collating elements in a cset + == static void mccase(register struct parse *p, register cset *cs); + * + * This would have to know the set of possibilities. Implementation + * is deferred. + */ +static void +mccase(p, cs) +register struct parse *p; +register cset *cs; +{ + assert(cs->multis == NULL); /* xxx */ +} + +/* + - isinsets - is this character in any sets? + == static int isinsets(register struct re_guts *g, int c); + */ +static int /* predicate */ +isinsets(g, c) +register struct re_guts *g; +int c; +{ + register uch *col; + register int i; + register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT; + register unsigned uc = (unsigned char)c; + + for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize) + if (col[uc] != 0) + return(1); + return(0); +} + +/* + - samesets - are these two characters in exactly the same sets? + == static int samesets(register struct re_guts *g, int c1, int c2); + */ +static int /* predicate */ +samesets(g, c1, c2) +register struct re_guts *g; +int c1; +int c2; +{ + register uch *col; + register int i; + register int ncols = (g->ncsets+(CHAR_BIT-1)) / CHAR_BIT; + register unsigned uc1 = (unsigned char)c1; + register unsigned uc2 = (unsigned char)c2; + + for (i = 0, col = g->setbits; i < ncols; i++, col += g->csetsize) + if (col[uc1] != col[uc2]) + return(0); + return(1); +} + +/* + - categorize - sort out character categories + == static void categorize(struct parse *p, register struct re_guts *g); + */ +static void +categorize(p, g) +struct parse *p; +register struct re_guts *g; +{ + register cat_t *cats = g->categories; + register int c; + register int c2; + register cat_t cat; + + /* avoid making error situations worse */ + if (p->error != 0) + return; + + for (c = CHAR_MIN; c <= CHAR_MAX; c++) + if (cats[c] == 0 && isinsets(g, c)) { + cat = g->ncategories++; + cats[c] = cat; + for (c2 = c+1; c2 <= CHAR_MAX; c2++) + if (cats[c2] == 0 && samesets(g, c, c2)) + cats[c2] = cat; + } +} + +/* + - dupl - emit a duplicate of a bunch of sops + == static sopno dupl(register struct parse *p, sopno start, sopno finish); + */ +static sopno /* start of duplicate */ +dupl(p, start, finish) +register struct parse *p; +sopno start; /* from here */ +sopno finish; /* to this less one */ +{ + register sopno ret = HERE(); + register sopno len = finish - start; + + assert(finish >= start); + if (len == 0) + return(ret); + enlarge(p, p->ssize + len); /* this many unexpected additions */ + assert(p->ssize >= p->slen + len); + (void) memcpy((char *)(p->strip + p->slen), + (char *)(p->strip + start), (size_t)len*sizeof(sop)); + p->slen += len; + return(ret); +} + +/* + - doemit - emit a strip operator + == static void doemit(register struct parse *p, sop op, size_t opnd); + * + * It might seem better to implement this as a macro with a function as + * hard-case backup, but it's just too big and messy unless there are + * some changes to the data structures. Maybe later. + */ +static void +doemit(p, op, opnd) +register struct parse *p; +sop op; +size_t opnd; +{ + /* avoid making error situations worse */ + if (p->error != 0) + return; + + /* deal with oversize operands ("can't happen", more or less) */ + assert(opnd < 1<<OPSHIFT); + + /* deal with undersized strip */ + if (p->slen >= p->ssize) + enlarge(p, (p->ssize+1) / 2 * 3); /* +50% */ + assert(p->slen < p->ssize); + + /* finally, it's all reduced to the easy case */ + p->strip[p->slen++] = SOP(op, opnd); +} + +/* + - doinsert - insert a sop into the strip + == static void doinsert(register struct parse *p, sop op, size_t opnd, sopno pos); + */ +static void +doinsert(p, op, opnd, pos) +register struct parse *p; +sop op; +size_t opnd; +sopno pos; +{ + register sopno sn; + register sop s; + register int i; + + /* avoid making error situations worse */ + if (p->error != 0) + return; + + sn = HERE(); + EMIT(op, opnd); /* do checks, ensure space */ + assert(HERE() == sn+1); + s = p->strip[sn]; + + /* adjust paren pointers */ + assert(pos > 0); + for (i = 1; i < NPAREN; i++) { + if (p->pbegin[i] >= pos) { + p->pbegin[i]++; + } + if (p->pend[i] >= pos) { + p->pend[i]++; + } + } + + memmove((char *)&p->strip[pos+1], (char *)&p->strip[pos], + (HERE()-pos-1)*sizeof(sop)); + p->strip[pos] = s; +} + +/* + - dofwd - complete a forward reference + == static void dofwd(register struct parse *p, sopno pos, sop value); + */ +static void +dofwd(p, pos, value) +register struct parse *p; +register sopno pos; +sop value; +{ + /* avoid making error situations worse */ + if (p->error != 0) + return; + + assert(value < 1<<OPSHIFT); + p->strip[pos] = OP(p->strip[pos]) | value; +} + +/* + - enlarge - enlarge the strip + == static void enlarge(register struct parse *p, sopno size); + */ +static void +enlarge(p, size) +register struct parse *p; +register sopno size; +{ + register sop *sp; + + if (p->ssize >= size) + return; + + sp = (sop *)realloc(p->strip, size*sizeof(sop)); + if (sp == NULL) { + SETERROR(REG_ESPACE); + return; + } + p->strip = sp; + p->ssize = size; +} + +/* + - stripsnug - compact the strip + == static void stripsnug(register struct parse *p, register struct re_guts *g); + */ +static void +stripsnug(p, g) +register struct parse *p; +register struct re_guts *g; +{ + g->nstates = p->slen; + g->strip = (sop *)realloc((char *)p->strip, p->slen * sizeof(sop)); + if (g->strip == NULL) { + SETERROR(REG_ESPACE); + g->strip = p->strip; + } +} + +/* + - findmust - fill in must and mlen with longest mandatory literal string + == static void findmust(register struct parse *p, register struct re_guts *g); + * + * This algorithm could do fancy things like analyzing the operands of | + * for common subsequences. Someday. This code is simple and finds most + * of the interesting cases. + * + * Note that must and mlen got initialized during setup. + */ +static void +findmust(p, g) +struct parse *p; +register struct re_guts *g; +{ + register sop *scan; + sop *start; + register sop *newstart; + register sopno newlen; + register sop s; + register char *cp; + register sopno i; + + /* avoid making error situations worse */ + if (p->error != 0) + return; + + /* find the longest OCHAR sequence in strip */ + newlen = 0; + scan = g->strip + 1; + do { + s = *scan++; + switch (OP(s)) { + case OCHAR: /* sequence member */ + if (newlen == 0) /* new sequence */ + newstart = scan - 1; + newlen++; + break; + case OPLUS_: /* things that don't break one */ + case OLPAREN: + case ORPAREN: + break; + case OQUEST_: /* things that must be skipped */ + case OCH_: + scan--; + do { + scan += OPND(s); + s = *scan; + /* assert() interferes w debug printouts */ + if (OP(s) != O_QUEST && OP(s) != O_CH && + OP(s) != OOR2) { + g->iflags |= BAD; + return; + } + } while (OP(s) != O_QUEST && OP(s) != O_CH); + /* fallthrough */ + default: /* things that break a sequence */ + if (newlen > g->mlen) { /* ends one */ + start = newstart; + g->mlen = newlen; + } + newlen = 0; + break; + } + } while (OP(s) != OEND); + + if (g->mlen == 0) /* there isn't one */ + return; + + /* turn it into a character string */ + g->must = malloc((size_t)g->mlen + 1); + if (g->must == NULL) { /* argh; just forget it */ + g->mlen = 0; + return; + } + cp = g->must; + scan = start; + for (i = g->mlen; i > 0; i--) { + while (OP(s = *scan++) != OCHAR) + continue; + assert(cp < g->must + g->mlen); + *cp++ = (char)OPND(s); + } + assert(cp == g->must + g->mlen); + *cp++ = '\0'; /* just on general principles */ +} + +/* + - pluscount - count + nesting + == static sopno pluscount(register struct parse *p, register struct re_guts *g); + */ +static sopno /* nesting depth */ +pluscount(p, g) +struct parse *p; +register struct re_guts *g; +{ + register sop *scan; + register sop s; + register sopno plusnest = 0; + register sopno maxnest = 0; + + if (p->error != 0) + return(0); /* there may not be an OEND */ + + scan = g->strip + 1; + do { + s = *scan++; + switch (OP(s)) { + case OPLUS_: + plusnest++; + break; + case O_PLUS: + if (plusnest > maxnest) + maxnest = plusnest; + plusnest--; + break; + } + } while (OP(s) != OEND); + if (plusnest != 0) + g->iflags |= BAD; + return(maxnest); +} + +/* + * $PchId: regcomp.c,v 1.2 1996/03/12 19:10:15 philip Exp $ + */ diff --git a/lib/regex/regerror.c b/lib/regex/regerror.c new file mode 100755 index 000000000..aacdf66d5 --- /dev/null +++ b/lib/regex/regerror.c @@ -0,0 +1,188 @@ +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regerror.c 8.4 (Berkeley) 3/20/94 + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)regerror.c 8.4 (Berkeley) 3/20/94"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <stdio.h> +#include <string.h> +#ifdef __minix_vmd +#include <bsd/asciictype.h> +#else +#include <ctype.h> +#endif +#include <limits.h> +#include <stdlib.h> +#include <regex.h> + +#include "utils.h" + +/* ========= begin header generated by ./mkh ========= */ +#ifdef __cplusplus +extern "C" { +#endif + +/* === regerror.c === */ +static char *regatoi(const regex_t *preg, char *localbuf); + +#ifdef __cplusplus +} +#endif +/* ========= end header generated by ./mkh ========= */ +/* + = #define REG_NOMATCH 1 + = #define REG_BADPAT 2 + = #define REG_ECOLLATE 3 + = #define REG_ECTYPE 4 + = #define REG_EESCAPE 5 + = #define REG_ESUBREG 6 + = #define REG_EBRACK 7 + = #define REG_EPAREN 8 + = #define REG_EBRACE 9 + = #define REG_BADBR 10 + = #define REG_ERANGE 11 + = #define REG_ESPACE 12 + = #define REG_BADRPT 13 + = #define REG_EMPTY 14 + = #define REG_ASSERT 15 + = #define REG_INVARG 16 + = #define REG_ATOI 255 // convert name to number (!) + = #define REG_ITOA 0400 // convert number to name (!) + */ +static struct rerr { + int code; + char *name; + char *explain; +} rerrs[] = { + REG_NOMATCH, "REG_NOMATCH", "regexec() failed to match", + REG_BADPAT, "REG_BADPAT", "invalid regular expression", + REG_ECOLLATE, "REG_ECOLLATE", "invalid collating element", + REG_ECTYPE, "REG_ECTYPE", "invalid character class", + REG_EESCAPE, "REG_EESCAPE", "trailing backslash (\\)", + REG_ESUBREG, "REG_ESUBREG", "invalid backreference number", + REG_EBRACK, "REG_EBRACK", "brackets ([ ]) not balanced", + REG_EPAREN, "REG_EPAREN", "parentheses not balanced", + REG_EBRACE, "REG_EBRACE", "braces not balanced", + REG_BADBR, "REG_BADBR", "invalid repetition count(s)", + REG_ERANGE, "REG_ERANGE", "invalid character range", + REG_ESPACE, "REG_ESPACE", "out of memory", + REG_BADRPT, "REG_BADRPT", "repetition-operator operand invalid", + REG_EMPTY, "REG_EMPTY", "empty (sub)expression", + REG_ASSERT, "REG_ASSERT", "\"can't happen\" -- you found a bug", + REG_INVARG, "REG_INVARG", "invalid argument to regex routine", + 0, "", "*** unknown regexp error code ***", +}; + +/* + - regerror - the interface to error numbers + = extern size_t regerror(int, const regex_t *, char *, size_t); + */ +/* ARGSUSED */ +size_t +regerror(errcode, preg, errbuf, errbuf_size) +int errcode; +const regex_t *preg; +char *errbuf; +size_t errbuf_size; +{ + register struct rerr *r; + register size_t len; + register int target = errcode &~ REG_ITOA; + register char *s; + char convbuf[50]; + + if (errcode == REG_ATOI) + s = regatoi(preg, convbuf); + else { + for (r = rerrs; r->code != 0; r++) + if (r->code == target) + break; + + if (errcode®_ITOA) { + if (r->code != 0) + (void) strcpy(convbuf, r->name); + else + sprintf(convbuf, "REG_0x%x", target); + assert(strlen(convbuf) < sizeof(convbuf)); + s = convbuf; + } else + s = r->explain; + } + + len = strlen(s) + 1; + if (errbuf_size > 0) { + if (errbuf_size > len) + (void) strcpy(errbuf, s); + else { + (void) strncpy(errbuf, s, errbuf_size-1); + errbuf[errbuf_size-1] = '\0'; + } + } + + return(len); +} + +/* + - regatoi - internal routine to implement REG_ATOI + == static char *regatoi(const regex_t *preg, char *localbuf); + */ +static char * +regatoi(preg, localbuf) +const regex_t *preg; +char *localbuf; +{ + register struct rerr *r; + register size_t siz; + register char *p; + + for (r = rerrs; r->code != 0; r++) + if (strcmp(r->name, preg->re_endp) == 0) + break; + if (r->code == 0) + return("0"); + + sprintf(localbuf, "%d", r->code); + return(localbuf); +} + +/* + * $PchId: regerror.c,v 1.2 1996/03/12 19:10:15 philip Exp $ + */ diff --git a/lib/regex/regex.3 b/lib/regex/regex.3 new file mode 100755 index 000000000..fbe2eca20 --- /dev/null +++ b/lib/regex/regex.3 @@ -0,0 +1,541 @@ +.\" Copyright (c) 1992, 1993, 1994 Henry Spencer. +.\" Copyright (c) 1992, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Henry Spencer. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)regex.3 8.4 (Berkeley) 3/20/94 +.\" +.TH REGEX 3 "March 20, 1994" +.de ZR +.\" one other place knows this name: the SEE ALSO section +.BR re_format (7) \\$1 +.. +.SH NAME +regex, regcomp, regexec, regerror, regfree \- regular-expression library +.SH SYNOPSIS +.ft B +.\".na +#include <sys/types.h> +.br +#include <regex.h> +.sp +.in +.5i +.ti -.5i +int regcomp(regex_t *\fIpreg\fP, const char *\fIpattern\fP, int \fIcflags\fP); +.ti -.5i +int regexec(const regex_t *\fIpreg\fP, const char *\fIstring\fP, +size_t \fInmatch\fP, regmatch_t \fIpmatch\fP[], int \fIeflags\fP); +.ti -.5i +size_t regerror(int \fIerrcode\fP, const regex_t *\fIpreg\fP, +char *\fIerrbuf\fP, size_t \fIerrbuf_size\fP); +.ti -.5i +void regfree(regex_t *\fIpreg\fP); +.in -.5i +.ft R +.SH DESCRIPTION +These routines implement POSIX 1003.2 regular expressions (``RE''s); +see +.ZR . +.B Regcomp +compiles an RE written as a string into an internal form, +.B regexec +matches that internal form against a string and reports results, +.B regerror +transforms error codes from either into human-readable messages, +and +.B regfree +frees any dynamically-allocated storage used by the internal form +of an RE. +.PP +The header +.I <regex.h> +declares two structure types, +.B regex_t +and +.BR regmatch_t , +the former for compiled internal forms and the latter for match reporting. +It also declares the four functions, +a type +.BR regoff_t , +and a number of constants with names starting with ``REG_''. +.PP +.B Regcomp +compiles the regular expression contained in the +.I pattern +string, +subject to the flags in +.IR cflags , +and places the results in the +.B regex_t +structure pointed to by +.IR preg . +.I Cflags +is the bitwise OR of zero or more of the following flags: +.IP REG_EXTENDED \w'REG_EXTENDED'u+2n +Compile modern (``extended'') REs, +rather than the obsolete (``basic'') REs that +are the default. +.IP REG_BASIC +This is a synonym for 0, +provided as a counterpart to REG_EXTENDED to improve readability. +.IP REG_NOSPEC +Compile with recognition of all special characters turned off. +All characters are thus considered ordinary, +so the ``RE'' is a literal string. +This is an extension, +compatible with but not specified by POSIX 1003.2, +and should be used with +caution in software intended to be portable to other systems. +REG_EXTENDED and REG_NOSPEC may not be used +in the same call to +.IR regcomp . +.IP REG_ICASE +Compile for matching that ignores upper/lower case distinctions. +See +.ZR . +.IP REG_NOSUB +Compile for matching that need only report success or failure, +not what was matched. +.IP REG_NEWLINE +Compile for newline-sensitive matching. +By default, newline is a completely ordinary character with no special +meaning in either REs or strings. +With this flag, +`[^' bracket expressions and `.' never match newline, +a `^' anchor matches the null string after any newline in the string +in addition to its normal function, +and the `$' anchor matches the null string before any newline in the +string in addition to its normal function. +.IP REG_PEND +The regular expression ends, +not at the first NUL, +but just before the character pointed to by the +.B re_endp +member of the structure pointed to by +.IR preg . +The +.B re_endp +member is of type +.BR "const\ char\ *" . +This flag permits inclusion of NULs in the RE; +they are considered ordinary characters. +This is an extension, +compatible with but not specified by POSIX 1003.2, +and should be used with +caution in software intended to be portable to other systems. +.PP +When successful, +.B regcomp +returns 0 and fills in the structure pointed to by +.IR preg . +One member of that structure +(other than +.BR re_endp ) +is publicized: +.BR re_nsub , +of type +.BR size_t , +contains the number of parenthesized subexpressions within the RE +(except that the value of this member is undefined if the +REG_NOSUB flag was used). +If +.B regcomp +fails, it returns a non-zero error code; +see DIAGNOSTICS. +.PP +.B Regexec +matches the compiled RE pointed to by +.I preg +against the +.IR string , +subject to the flags in +.IR eflags , +and reports results using +.IR nmatch , +.IR pmatch , +and the returned value. +The RE must have been compiled by a previous invocation of +.BR regcomp . +The compiled form is not altered during execution of +.BR regexec , +so a single compiled RE can be used simultaneously by multiple threads. +.PP +By default, +the NUL-terminated string pointed to by +.I string +is considered to be the text of an entire line, minus any terminating +newline. +The +.I eflags +argument is the bitwise OR of zero or more of the following flags: +.IP REG_NOTBOL \w'REG_STARTEND'u+2n +The first character of +the string +is not the beginning of a line, so the `^' anchor should not match before it. +This does not affect the behavior of newlines under REG_NEWLINE. +.IP REG_NOTEOL +The NUL terminating +the string +does not end a line, so the `$' anchor should not match before it. +This does not affect the behavior of newlines under REG_NEWLINE. +.IP REG_STARTEND +The string is considered to start at +\fIstring\fR\ + \fIpmatch\fR[0].\fBrm_so\fR +and to have a terminating NUL located at +\fIstring\fR\ + \fIpmatch\fR[0].\fBrm_eo\fR +(there need not actually be a NUL at that location), +regardless of the value of +.IR nmatch . +See below for the definition of +.IR pmatch +and +.IR nmatch . +This is an extension, +compatible with but not specified by POSIX 1003.2, +and should be used with +caution in software intended to be portable to other systems. +Note that a non-zero \fBrm_so\fR does not imply REG_NOTBOL; +REG_STARTEND affects only the location of the string, +not how it is matched. +.PP +See +.ZR +for a discussion of what is matched in situations where an RE or a +portion thereof could match any of several substrings of +.IR string . +.PP +Normally, +.B regexec +returns 0 for success and the non-zero code REG_NOMATCH for failure. +Other non-zero error codes may be returned in exceptional situations; +see DIAGNOSTICS. +.PP +If REG_NOSUB was specified in the compilation of the RE, +or if +.I nmatch +is 0, +.B regexec +ignores the +.I pmatch +argument (but see below for the case where REG_STARTEND is specified). +Otherwise, +.I pmatch +points to an array of +.I nmatch +structures of type +.BR regmatch_t . +Such a structure has at least the members +.B rm_so +and +.BR rm_eo , +both of type +.B regoff_t +(a signed arithmetic type at least as large as an +.B off_t +and a +.BR ssize_t ), +containing respectively the offset of the first character of a substring +and the offset of the first character after the end of the substring. +Offsets are measured from the beginning of the +.I string +argument given to +.BR regexec . +An empty substring is denoted by equal offsets, +both indicating the character following the empty substring. +.PP +The 0th member of the +.I pmatch +array is filled in to indicate what substring of +.I string +was matched by the entire RE. +Remaining members report what substring was matched by parenthesized +subexpressions within the RE; +member +.I i +reports subexpression +.IR i , +with subexpressions counted (starting at 1) by the order of their opening +parentheses in the RE, left to right. +Unused entries in the array\(emcorresponding either to subexpressions that +did not participate in the match at all, or to subexpressions that do not +exist in the RE (that is, \fIi\fR\ > \fIpreg\fR\->\fBre_nsub\fR)\(emhave both +.B rm_so +and +.B rm_eo +set to \-1. +If a subexpression participated in the match several times, +the reported substring is the last one it matched. +(Note, as an example in particular, that when the RE `(b*)+' matches `bbb', +the parenthesized subexpression matches each of the three `b's and then +an infinite number of empty strings following the last `b', +so the reported substring is one of the empties.) +.PP +If REG_STARTEND is specified, +.I pmatch +must point to at least one +.B regmatch_t +(even if +.I nmatch +is 0 or REG_NOSUB was specified), +to hold the input offsets for REG_STARTEND. +Use for output is still entirely controlled by +.IR nmatch ; +if +.I nmatch +is 0 or REG_NOSUB was specified, +the value of +.IR pmatch [0] +will not be changed by a successful +.BR regexec . +.PP +.B Regerror +maps a non-zero +.I errcode +from either +.B regcomp +or +.B regexec +to a human-readable, printable message. +If +.I preg +is non-NULL, +the error code should have arisen from use of +the +.B regex_t +pointed to by +.IR preg , +and if the error code came from +.BR regcomp , +it should have been the result from the most recent +.B regcomp +using that +.BR regex_t . +.RI ( Regerror +may be able to supply a more detailed message using information +from the +.BR regex_t .) +.B Regerror +places the NUL-terminated message into the buffer pointed to by +.IR errbuf , +limiting the length (including the NUL) to at most +.I errbuf_size +bytes. +If the whole message won't fit, +as much of it as will fit before the terminating NUL is supplied. +In any case, +the returned value is the size of buffer needed to hold the whole +message (including terminating NUL). +If +.I errbuf_size +is 0, +.I errbuf +is ignored but the return value is still correct. +.PP +If the +.I errcode +given to +.B regerror +is first ORed with REG_ITOA, +the ``message'' that results is the printable name of the error code, +e.g. ``REG_NOMATCH'', +rather than an explanation thereof. +If +.I errcode +is REG_ATOI, +then +.I preg +shall be non-NULL and the +.B re_endp +member of the structure it points to +must point to the printable name of an error code; +in this case, the result in +.I errbuf +is the decimal digits of +the numeric value of the error code +(0 if the name is not recognized). +REG_ITOA and REG_ATOI are intended primarily as debugging facilities; +they are extensions, +compatible with but not specified by POSIX 1003.2, +and should be used with +caution in software intended to be portable to other systems. +Be warned also that they are considered experimental and changes are possible. +.PP +.B Regfree +frees any dynamically-allocated storage associated with the compiled RE +pointed to by +.IR preg . +The remaining +.B regex_t +is no longer a valid compiled RE +and the effect of supplying it to +.B regexec +or +.B regerror +is undefined. +.PP +None of these functions references global variables except for tables +of constants; +all are safe for use from multiple threads if the arguments are safe. +.SH IMPLEMENTATION CHOICES +There are a number of decisions that 1003.2 leaves up to the implementor, +either by explicitly saying ``undefined'' or by virtue of them being +forbidden by the RE grammar. +This implementation treats them as follows. +.PP +See +.ZR +for a discussion of the definition of case-independent matching. +.PP +There is no particular limit on the length of REs, +except insofar as memory is limited. +Memory usage is approximately linear in RE size, and largely insensitive +to RE complexity, except for bounded repetitions. +See BUGS for one short RE using them +that will run almost any system out of memory. +.PP +A backslashed character other than one specifically given a magic meaning +by 1003.2 (such magic meanings occur only in obsolete [``basic''] REs) +is taken as an ordinary character. +.PP +Any unmatched [ is a REG_EBRACK error. +.PP +Equivalence classes cannot begin or end bracket-expression ranges. +The endpoint of one range cannot begin another. +.PP +RE_DUP_MAX, the limit on repetition counts in bounded repetitions, is 255. +.PP +A repetition operator (?, *, +, or bounds) cannot follow another +repetition operator. +A repetition operator cannot begin an expression or subexpression +or follow `^' or `|'. +.PP +`|' cannot appear first or last in a (sub)expression or after another `|', +i.e. an operand of `|' cannot be an empty subexpression. +An empty parenthesized subexpression, `()', is legal and matches an +empty (sub)string. +An empty string is not a legal RE. +.PP +A `{' followed by a digit is considered the beginning of bounds for a +bounded repetition, which must then follow the syntax for bounds. +A `{' \fInot\fR followed by a digit is considered an ordinary character. +.PP +`^' and `$' beginning and ending subexpressions in obsolete (``basic'') +REs are anchors, not ordinary characters. +.SH SEE ALSO +.BR grep (1), +.BR re_format (7). +.PP +POSIX 1003.2, sections 2.8 (Regular Expression Notation) +and +B.5 (C Binding for Regular Expression Matching). +.SH DIAGNOSTICS +Non-zero error codes from +.B regcomp +and +.B regexec +include the following: +.PP +.nf +.ta \w'REG_ECOLLATE'u+3n +REG_NOMATCH regexec() failed to match +REG_BADPAT invalid regular expression +REG_ECOLLATE invalid collating element +REG_ECTYPE invalid character class +REG_EESCAPE \e applied to unescapable character +REG_ESUBREG invalid backreference number +REG_EBRACK brackets [ ] not balanced +REG_EPAREN parentheses ( ) not balanced +REG_EBRACE braces { } not balanced +REG_BADBR invalid repetition count(s) in { } +REG_ERANGE invalid character range in [ ] +REG_ESPACE ran out of memory +REG_BADRPT ?, *, or + operand invalid +REG_EMPTY empty (sub)expression +REG_ASSERT ``can't happen''\(emyou found a bug +REG_INVARG invalid argument, e.g. negative-length string +.fi +.SH HISTORY +Originally written by Henry Spencer. +Altered for inclusion in the 4.4BSD distribution. +.SH BUGS +This is an alpha release with known defects. +Please report problems. +.PP +There is one known functionality bug. +The implementation of internationalization is incomplete: +the locale is always assumed to be the default one of 1003.2, +and only the collating elements etc. of that locale are available. +.PP +The back-reference code is subtle and doubts linger about its correctness +in complex cases. +.PP +.B Regexec +performance is poor. +This will improve with later releases. +.I Nmatch +exceeding 0 is expensive; +.I nmatch +exceeding 1 is worse. +.B Regexec +is largely insensitive to RE complexity \fIexcept\fR that back +references are massively expensive. +RE length does matter; in particular, there is a strong speed bonus +for keeping RE length under about 30 characters, +with most special characters counting roughly double. +.PP +.B Regcomp +implements bounded repetitions by macro expansion, +which is costly in time and space if counts are large +or bounded repetitions are nested. +An RE like, say, +`((((a{1,100}){1,100}){1,100}){1,100}){1,100}' +will (eventually) run almost any existing machine out of swap space. +.PP +There are suspected problems with response to obscure error conditions. +Notably, +certain kinds of internal overflow, +produced only by truly enormous REs or by multiply nested bounded repetitions, +are probably not handled well. +.PP +Due to a mistake in 1003.2, things like `a)b' are legal REs because `)' is +a special character only in the presence of a previous unmatched `('. +This can't be fixed until the spec is fixed. +.PP +The standard's definition of back references is vague. +For example, does +`a\e(\e(b\e)*\e2\e)*d' match `abbbd'? +Until the standard is clarified, +behavior in such cases should not be relied on. +.PP +The implementation of word-boundary matching is a bit of a kludge, +and bugs may lurk in combinations of word-boundary matching and anchoring. diff --git a/lib/regex/regex2.h b/lib/regex/regex2.h new file mode 100755 index 000000000..64b62121f --- /dev/null +++ b/lib/regex/regex2.h @@ -0,0 +1,173 @@ +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regex2.h 8.4 (Berkeley) 3/20/94 + */ + +/* + * First, the stuff that ends up in the outside-world include file + = typedef off_t regoff_t; + = typedef struct { + = int re_magic; + = size_t re_nsub; // number of parenthesized subexpressions + = const char *re_endp; // end pointer for REG_PEND + = struct re_guts *re_g; // none of your business :-) + = } regex_t; + = typedef struct { + = regoff_t rm_so; // start of match + = regoff_t rm_eo; // end of match + = } regmatch_t; + */ +/* + * internals of regex_t + */ +#define MAGIC1 ((('r'^0200)<<8) | 'e') + +/* + * The internal representation is a *strip*, a sequence of + * operators ending with an endmarker. (Some terminology etc. is a + * historical relic of earlier versions which used multiple strips.) + * Certain oddities in the representation are there to permit running + * the machinery backwards; in particular, any deviation from sequential + * flow must be marked at both its source and its destination. Some + * fine points: + * + * - OPLUS_ and O_PLUS are *inside* the loop they create. + * - OQUEST_ and O_QUEST are *outside* the bypass they create. + * - OCH_ and O_CH are *outside* the multi-way branch they create, while + * OOR1 and OOR2 are respectively the end and the beginning of one of + * the branches. Note that there is an implicit OOR2 following OCH_ + * and an implicit OOR1 preceding O_CH. + * + * In state representations, an operator's bit is on to signify a state + * immediately *preceding* "execution" of that operator. + */ +typedef unsigned long sop; /* strip operator */ +typedef long sopno; +#define OPRMASK 0xf8000000 +#define OPDMASK 0x07ffffff +#define OPSHIFT ((unsigned)27) +#define OP(n) ((n)&OPRMASK) +#define OPND(n) ((n)&OPDMASK) +#define SOP(op, opnd) ((op)|(opnd)) +/* operators meaning operand */ +/* (back, fwd are offsets) */ +#define OEND (1<<OPSHIFT) /* endmarker - */ +#define OCHAR (2<<OPSHIFT) /* character unsigned char */ +#define OBOL (3<<OPSHIFT) /* left anchor - */ +#define OEOL (4<<OPSHIFT) /* right anchor - */ +#define OANY (5<<OPSHIFT) /* . - */ +#define OANYOF (6<<OPSHIFT) /* [...] set number */ +#define OBACK_ (7<<OPSHIFT) /* begin \d paren number */ +#define O_BACK (8<<OPSHIFT) /* end \d paren number */ +#define OPLUS_ (9<<OPSHIFT) /* + prefix fwd to suffix */ +#define O_PLUS (10<<OPSHIFT) /* + suffix back to prefix */ +#define OQUEST_ (11<<OPSHIFT) /* ? prefix fwd to suffix */ +#define O_QUEST (12<<OPSHIFT) /* ? suffix back to prefix */ +#define OLPAREN (13<<OPSHIFT) /* ( fwd to ) */ +#define ORPAREN (14<<OPSHIFT) /* ) back to ( */ +#define OCH_ (15<<OPSHIFT) /* begin choice fwd to OOR2 */ +#define OOR1 (16<<OPSHIFT) /* | pt. 1 back to OOR1 or OCH_ */ +#define OOR2 (17<<OPSHIFT) /* | pt. 2 fwd to OOR2 or O_CH */ +#define O_CH (18<<OPSHIFT) /* end choice back to OOR1 */ +#define OBOW (19<<OPSHIFT) /* begin word - */ +#define OEOW (20<<OPSHIFT) /* end word - */ + +/* + * Structure for [] character-set representation. Character sets are + * done as bit vectors, grouped 8 to a byte vector for compactness. + * The individual set therefore has both a pointer to the byte vector + * and a mask to pick out the relevant bit of each byte. A hash code + * simplifies testing whether two sets could be identical. + * + * This will get trickier for multicharacter collating elements. As + * preliminary hooks for dealing with such things, we also carry along + * a string of multi-character elements, and decide the size of the + * vectors at run time. + */ +typedef struct { + uch *ptr; /* -> uch [csetsize] */ + uch mask; /* bit within array */ + uch hash; /* hash code */ + size_t smultis; + char *multis; /* -> char[smulti] ab\0cd\0ef\0\0 */ +} cset; +/* note that CHadd and CHsub are unsafe, and CHIN doesn't yield 0/1 */ +#define CHadd(cs, c) ((cs)->ptr[(uch)(c)] |= (cs)->mask, (cs)->hash += (c)) +#define CHsub(cs, c) ((cs)->ptr[(uch)(c)] &= ~(cs)->mask, (cs)->hash -= (c)) +#define CHIN(cs, c) ((cs)->ptr[(uch)(c)] & (cs)->mask) +#define MCadd(p, cs, cp) mcadd(p, cs, cp) /* regcomp() internal fns */ +#define MCsub(p, cs, cp) mcsub(p, cs, cp) +#define MCin(p, cs, cp) mcin(p, cs, cp) + +/* stuff for character categories */ +typedef unsigned char cat_t; + +/* + * main compiled-expression structure + */ +struct re_guts { + int magic; +# define MAGIC2 ((('R'^0200)<<8)|'E') + sop *strip; /* malloced area for strip */ + int csetsize; /* number of bits in a cset vector */ + int ncsets; /* number of csets in use */ + cset *sets; /* -> cset [ncsets] */ + uch *setbits; /* -> uch[csetsize][ncsets/CHAR_BIT] */ + int cflags; /* copy of regcomp() cflags argument */ + sopno nstates; /* = number of sops */ + sopno firststate; /* the initial OEND (normally 0) */ + sopno laststate; /* the final OEND */ + int iflags; /* internal flags */ +# define USEBOL 01 /* used ^ */ +# define USEEOL 02 /* used $ */ +# define BAD 04 /* something wrong */ + int nbol; /* number of ^ used */ + int neol; /* number of $ used */ + int ncategories; /* how many character categories */ + cat_t *categories; /* ->catspace[-CHAR_MIN] */ + char *must; /* match must contain this string */ + int mlen; /* length of must */ + size_t nsub; /* copy of re_nsub */ + int backrefs; /* does it use back references? */ + sopno nplus; /* how deep does it nest +s? */ + /* catspace must be last */ + cat_t catspace[1]; /* actually [NC] */ +}; + +/* misc utilities */ +#define OUT (CHAR_MAX+1) /* a non-character value */ +#define ISWORD(c) (isalnum(c) || (c) == '_') diff --git a/lib/regex/regexec.c b/lib/regex/regexec.c new file mode 100755 index 000000000..7890a88c4 --- /dev/null +++ b/lib/regex/regexec.c @@ -0,0 +1,189 @@ +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regexec.c 8.3 (Berkeley) 3/20/94 + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)regexec.c 8.3 (Berkeley) 3/20/94"; +#endif /* LIBC_SCCS and not lint */ + +/* + * the outer shell of regexec() + * + * This file includes engine.c *twice*, after muchos fiddling with the + * macros that code uses. This lets the same code operate on two different + * representations for state sets. + */ +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#ifdef __minix_vmd +#include <bsd/asciictype.h> +#else +#include <ctype.h> +#endif +#include <regex.h> + +#include "utils.h" +#include "regex2.h" + +static int nope = 0; /* for use in asserts; shuts lint up */ + +/* macros for manipulating states, small version */ +#define states long +#define states1 states /* for later use in regexec() decision */ +#define CLEAR(v) ((v) = 0) +#define SET0(v, n) ((v) &= ~(1 << (n))) +#define SET1(v, n) ((v) |= 1 << (n)) +#define ISSET(v, n) ((v) & (1 << (n))) +#define ASSIGN(d, s) ((d) = (s)) +#define EQ(a, b) ((a) == (b)) +#define STATEVARS int dummy /* dummy version */ +#define STATESETUP(m, n) /* nothing */ +#define STATETEARDOWN(m) /* nothing */ +#define SETUP(v) ((v) = 0) +#define onestate int +#define INIT(o, n) ((o) = (unsigned)1 << (n)) +#define INC(o) ((o) <<= 1) +#define ISSTATEIN(v, o) ((v) & (o)) +/* some abbreviations; note that some of these know variable names! */ +/* do "if I'm here, I can also be there" etc without branches */ +#define FWD(dst, src, n) ((dst) |= ((unsigned)(src)&(here)) << (n)) +#define BACK(dst, src, n) ((dst) |= ((unsigned)(src)&(here)) >> (n)) +#define ISSETBACK(v, n) ((v) & ((unsigned)here >> (n))) +/* function names */ +#define SNAMES /* engine.c looks after details */ + +#include "engine.c" + +/* now undo things */ +#undef states +#undef CLEAR +#undef SET0 +#undef SET1 +#undef ISSET +#undef ASSIGN +#undef EQ +#undef STATEVARS +#undef STATESETUP +#undef STATETEARDOWN +#undef SETUP +#undef onestate +#undef INIT +#undef INC +#undef ISSTATEIN +#undef FWD +#undef BACK +#undef ISSETBACK +#undef SNAMES + +/* macros for manipulating states, large version */ +#define states char * +#define CLEAR(v) memset(v, 0, m->g->nstates) +#define SET0(v, n) ((v)[n] = 0) +#define SET1(v, n) ((v)[n] = 1) +#define ISSET(v, n) ((v)[n]) +#define ASSIGN(d, s) memcpy(d, s, m->g->nstates) +#define EQ(a, b) (memcmp(a, b, m->g->nstates) == 0) +#define STATEVARS int vn; char *space +#define STATESETUP(m, nv) { (m)->space = malloc((nv)*(m)->g->nstates); \ + if ((m)->space == NULL) return(REG_ESPACE); \ + (m)->vn = 0; } +#define STATETEARDOWN(m) { free((m)->space); } +#define SETUP(v) ((v) = &m->space[m->vn++ * m->g->nstates]) +#define onestate int +#define INIT(o, n) ((o) = (n)) +#define INC(o) ((o)++) +#define ISSTATEIN(v, o) ((v)[o]) +/* some abbreviations; note that some of these know variable names! */ +/* do "if I'm here, I can also be there" etc without branches */ +#define FWD(dst, src, n) ((dst)[here+(n)] |= (src)[here]) +#define BACK(dst, src, n) ((dst)[here-(n)] |= (src)[here]) +#define ISSETBACK(v, n) ((v)[here - (n)]) +/* function names */ +#define LNAMES /* flag */ + +#include "engine.c" + +/* + - regexec - interface for matching + = extern int regexec(const regex_t *, const char *, size_t, \ + = regmatch_t [], int); + = #define REG_NOTBOL 00001 + = #define REG_NOTEOL 00002 + = #define REG_STARTEND 00004 + = #define REG_TRACE 00400 // tracing of execution + = #define REG_LARGE 01000 // force large representation + = #define REG_BACKR 02000 // force use of backref code + * + * We put this here so we can exploit knowledge of the state representation + * when choosing which matcher to call. Also, by this point the matchers + * have been prototyped. + */ +int /* 0 success, REG_NOMATCH failure */ +regexec(preg, string, nmatch, pmatch, eflags) +const regex_t *preg; +const char *string; +size_t nmatch; +regmatch_t pmatch[]; +int eflags; +{ + register struct re_guts *g = preg->re_g; +#ifdef REDEBUG +# define GOODFLAGS(f) (f) +#else +# define GOODFLAGS(f) ((f)&(REG_NOTBOL|REG_NOTEOL|REG_STARTEND)) +#endif + + if (preg->re_magic != MAGIC1 || g->magic != MAGIC2) + return(REG_BADPAT); + assert(!(g->iflags&BAD)); + if (g->iflags&BAD) /* backstop for no-debug case */ + return(REG_BADPAT); + eflags = GOODFLAGS(eflags); + + if (g->nstates <= CHAR_BIT*sizeof(states1) && !(eflags®_LARGE)) + return(smatcher(g, (char *)string, nmatch, pmatch, eflags)); + else + return(lmatcher(g, (char *)string, nmatch, pmatch, eflags)); +} + +/* + * $PchId: regexec.c,v 1.2 1996/03/12 19:10:15 philip Exp $ + */ diff --git a/lib/regex/regfree.c b/lib/regex/regfree.c new file mode 100755 index 000000000..fe6230e92 --- /dev/null +++ b/lib/regex/regfree.c @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)regfree.c 8.3 (Berkeley) 3/20/94 + */ + +#if defined(LIBC_SCCS) && !defined(lint) +static char sccsid[] = "@(#)regfree.c 8.3 (Berkeley) 3/20/94"; +#endif /* LIBC_SCCS and not lint */ + +#include <sys/types.h> +#include <stdio.h> +#include <stdlib.h> +#include <regex.h> + +#include "utils.h" +#include "regex2.h" + +/* + - regfree - free everything + = extern void regfree(regex_t *); + */ +void +regfree(preg) +regex_t *preg; +{ + register struct re_guts *g; + + if (preg->re_magic != MAGIC1) /* oops */ + return; /* nice to complain, but hard */ + + g = preg->re_g; + if (g == NULL || g->magic != MAGIC2) /* oops again */ + return; + preg->re_magic = 0; /* mark it invalid */ + g->magic = 0; /* mark it invalid */ + + if (g->strip != NULL) + free((char *)g->strip); + if (g->sets != NULL) + free((char *)g->sets); + if (g->setbits != NULL) + free((char *)g->setbits); + if (g->must != NULL) + free(g->must); + free((char *)g); +} + +/* + * $PchId: regfree.c,v 1.2 1996/03/12 19:10:15 philip Exp $ + */ diff --git a/lib/regex/utils.h b/lib/regex/utils.h new file mode 100755 index 000000000..9eae97c0f --- /dev/null +++ b/lib/regex/utils.h @@ -0,0 +1,61 @@ +/*- + * Copyright (c) 1992, 1993, 1994 Henry Spencer. + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Henry Spencer. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)utils.h 8.3 (Berkeley) 3/20/94 + */ + +/* utility definitions */ +#ifdef _POSIX2_RE_DUP_MAX +#define DUPMAX _POSIX2_RE_DUP_MAX /* xxx is this right? */ +#else +#define DUPMAX 255 +#endif +#define INFINITY (DUPMAX + 1) +#define NC (CHAR_MAX - CHAR_MIN + 1) +typedef unsigned char uch; + +/* switch off assertions (if not already off) if no REDEBUG */ +#ifndef REDEBUG +#ifndef NDEBUG +#define NDEBUG /* no assertions please */ +#endif +#endif +#include <assert.h> + +/* for old systems with bcopy() but no memmove() */ +#ifdef USEBCOPY +#define memmove(d, s, c) bcopy(s, d, c) +#endif diff --git a/lib/rts/Makefile b/lib/rts/Makefile new file mode 100755 index 000000000..eb20a01f8 --- /dev/null +++ b/lib/rts/Makefile @@ -0,0 +1,19 @@ +# Makefile for lib/rts. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../libc.a + +all: \ + $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(setjmp.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(setjmp.o): setjmp.e + $(CC1) setjmp.e diff --git a/lib/rts/setjmp.e b/lib/rts/setjmp.e new file mode 100755 index 000000000..7036e2418 --- /dev/null +++ b/lib/rts/setjmp.e @@ -0,0 +1,112 @@ +# + mes 2,_EM_WSIZE,_EM_PSIZE +; +; layout of a setjmp buffer: +; +; ----------------- +; | flag | (!0 when blocked signals saved (POSIX)) +; ----------------- +; | signal mask/set | (for Berkeley 4.[2-] / POSIX) +; ----------------- +; | | +; | GTO descriptor | +; | (SP, LB, PC) | +; | | +; ----------------- +; +; setjmp saves the signalmask, PC, SP, and LB of caller, and creates a +; GTO descriptor from this. +; The big problem here is how to get the return address, i.e. the PC of +; the caller; This problem is solved by the front-end, which must pass +; it as an extra parameter to setjmp. + +; a GTO descriptor must be in the global data area +gtobuf + bss 3*_EM_PSIZE,0,0 + + inp $fill_ret_area + exp $__setjmp + pro $__setjmp,0 +#if defined(_POSIX_SOURCE) +; save mask of currently blocked signals. +; longjmp must restore this mask + lol _EM_PSIZE ; the flag integer at offset _EM_PSIZE + lal 0 + loi _EM_PSIZE + stf 3*_EM_PSIZE+_EM_LSIZE + lol _EM_PSIZE ; the flag integer at offset _EM_PSIZE + zeq *1 + lal 0 + loi _EM_PSIZE + adp 3*_EM_PSIZE + cal $__newsigset + asp _EM_PSIZE +1 +#elif defined(__BSD4_2) + loc 0 + cal $sigblock + asp _EM_WSIZE + lfr _EM_WSIZE + lal 0 + loi _EM_PSIZE + stf 3*_EM_PSIZE +#endif +; create GTO descriptor for longjmp + lxl 0 + dch ; Local Base of caller + lxa 0 ; Stackpointer of caller + lal _EM_PSIZE+_EM_WSIZE + loi _EM_PSIZE ; Return address of caller + lal 0 + loi _EM_PSIZE ; address of jmpbuf + sti 3*_EM_PSIZE ; LB, SP, and PC stored in jmpbuf + loc 0 + ret _EM_WSIZE ; setjmp must return 0 + end 0 + + pro $fill_ret_area,0 +; put argument in function result area + lol 0 + ret _EM_WSIZE + end 0 + + exp $longjmp + pro $longjmp,? +#if defined(_POSIX_SOURCE) +; restore blocked mask + lal 0 + loi _EM_PSIZE + lof 3*_EM_PSIZE+_EM_LSIZE + zeq *2 + lal 0 + loi _EM_PSIZE + adp 3*_EM_PSIZE + cal $__oldsigset + asp _EM_PSIZE +2 +#elif defined(__BSD4_2) +; restore signal mask + lal 0 + loi _EM_PSIZE + lof 3*_EM_PSIZE + cal $_sigsetmask + asp _EM_WSIZE + lfr _EM_WSIZE + asp _EM_WSIZE +#endif + lal 0 + loi _EM_PSIZE ; address of jmpbuf + lae gtobuf + blm 3*_EM_PSIZE ; fill GTO descriptor from jmpbuf + lol _EM_PSIZE ; second parameter of longjmp: the return value + dup _EM_WSIZE + zne *3 +; of course, longjmp may not return 0! + inc +3 +; put return value in function result area + cal $fill_ret_area + asp _EM_WSIZE + gto gtobuf ; there we go ... +; ASP and GTO do not damage function result area + end 0 diff --git a/lib/socket/Makefile b/lib/socket/Makefile new file mode 100644 index 000000000..ac99b763f --- /dev/null +++ b/lib/socket/Makefile @@ -0,0 +1,39 @@ + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE -I../../servers +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../libsocket.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(socket.o) \ + $(LIBRARY)(listen.o) \ + $(LIBRARY)(connect.o) \ + $(LIBRARY)(shutdown.o) \ + $(LIBRARY)(extra.o) \ + $(LIBRARY)(bind.o) + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(socket.o): socket.c + $(CC1) socket.c + +$(LIBRARY)(connect.o): connect.c + $(CC1) connect.c + +$(LIBRARY)(listen.o): listen.c + $(CC1) listen.c + +$(LIBRARY)(shutdown.o): shutdown.c + $(CC1) shutdown.c + +$(LIBRARY)(extra.o): extra.c + $(CC1) extra.c + +$(LIBRARY)(bind.o): bind.c + $(CC1) bind.c + +clean: + -rm *.o $(LIBRARY) diff --git a/lib/socket/bind.c b/lib/socket/bind.c new file mode 100644 index 000000000..3ad6661f1 --- /dev/null +++ b/lib/socket/bind.c @@ -0,0 +1,34 @@ + +/* bsd-socket(2)-lookalike */ + +#include <errno.h> +#include <string.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/types.h> +#include <net/ioctl.h> +#include <net/gen/socket.h> +#include <net/gen/emu.h> +#include <net/gen/tcp.h> +#include <net/gen/in.h> +#include <net/gen/tcp_hdr.h> +#include <net/gen/tcp_io.h> + +int +bind(int s, struct sockaddr *addr, socklen_t len) +{ + nwio_tcpconf_t tcpconf; + struct sockaddr_in *in_local; + + in_local = (struct sockaddr_in *) addr; + + memset(&tcpconf, 0, sizeof(tcpconf)); + tcpconf.nwtc_flags = NWTC_EXCL | NWTC_LP_SET | NWTC_UNSET_RA | NWTC_UNSET_RP; + tcpconf.nwtc_locport = in_local->sin_port; + + if(ioctl(s, NWIOSTCPCONF, &tcpconf) < 0) + return -1; + + return 0; +} + diff --git a/lib/socket/connect.c b/lib/socket/connect.c new file mode 100644 index 000000000..c794eb177 --- /dev/null +++ b/lib/socket/connect.c @@ -0,0 +1,46 @@ + +/* bsd-socket(2)-lookalike */ + +#include <errno.h> +#include <string.h> +#include <sys/types.h> +#include <net/ioctl.h> +#include <net/gen/in.h> +#include <net/gen/socket.h> +#include <net/gen/emu.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_hdr.h> +#include <net/gen/tcp_io.h> + +int +connect(int s, struct sockaddr *peer, socklen_t len) +{ + nwio_tcpcl_t tcpopt; + nwio_tcpconf_t tcpconf; + struct sockaddr_in *in_peer; + + if(!peer || peer->sa_family != AF_INET || + len != sizeof(struct sockaddr_in)) { + errno = EAFNOSUPPORT; + return -1; + } + + in_peer = (struct sockaddr_in *) peer; + + memset(&tcpconf, 0, sizeof(tcpconf)); + tcpconf.nwtc_flags = NWTC_EXCL | NWTC_LP_SEL | NWTC_SET_RA | NWTC_SET_RP; + tcpconf.nwtc_remaddr = in_peer->sin_addr.s_addr; + tcpconf.nwtc_remport = in_peer->sin_port; + + if(ioctl(s, NWIOSTCPCONF, &tcpconf) < 0) + return -1; + + tcpopt.nwtcl_flags = 0; + tcpopt.nwtcl_ttl = 0; + + if(ioctl(s, NWIOTCPCONN, &tcpopt) < 0) + return -1; + + return 0; +} + diff --git a/lib/socket/extra.c b/lib/socket/extra.c new file mode 100644 index 000000000..f76d261a1 --- /dev/null +++ b/lib/socket/extra.c @@ -0,0 +1,26 @@ + +/* bsd-socket(2)-lookalike */ + +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/types.h> +#include <net/gen/socket.h> +#include <net/gen/emu.h> +#include <net/gen/tcp.h> +#include <net/gen/in.h> +#include <net/gen/tcp_hdr.h> +#include <net/gen/tcp_io.h> + +ssize_t +recv(int s, void *buf, size_t len, int flags) +{ + return read(s, buf, len); +} + +ssize_t +send(int s, void *buf, size_t len, int flags) +{ + return write(s, buf, len); +} + diff --git a/lib/socket/listen.c b/lib/socket/listen.c new file mode 100644 index 000000000..ab271d0a9 --- /dev/null +++ b/lib/socket/listen.c @@ -0,0 +1,26 @@ + +/* bsd-socket(2)-lookalike */ + +#include <sys/types.h> +#include <net/gen/socket.h> +#include <net/gen/in.h> +#include <net/ioctl.h> +#include <net/gen/emu.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_hdr.h> +#include <net/gen/tcp_io.h> + +int +listen(int s, int backlog) +{ + nwio_tcpcl_t tcpopt; + struct sockaddr_in *in_peer; + + tcpopt.nwtcl_flags = tcpopt.nwtcl_ttl = 0; + + if(ioctl(s, NWIOTCPLISTEN, &tcpopt) < 0) + return -1; + + return 0; +} + diff --git a/lib/socket/shutdown.c b/lib/socket/shutdown.c new file mode 100644 index 000000000..c65034aff --- /dev/null +++ b/lib/socket/shutdown.c @@ -0,0 +1,26 @@ + +/* bsd-socket(2)-lookalike */ + +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/ioctl.h> +#include <net/gen/socket.h> +#include <net/gen/emu.h> +#include <net/gen/tcp.h> +#include <net/gen/in.h> +#include <net/gen/tcp_hdr.h> +#include <net/gen/tcp_io.h> + +int +shutdown(int s, int how) +{ + nwio_tcpcl_t tcpopt; + + if(ioctl(s, NWIOTCPSHUTDOWN, NULL) < 0) + return -1; + + return 0; +} + diff --git a/lib/socket/socket.c b/lib/socket/socket.c new file mode 100644 index 000000000..ca3684fbd --- /dev/null +++ b/lib/socket/socket.c @@ -0,0 +1,56 @@ + +/* bsd-socket(2)-lookalike */ + +#include <errno.h> +#include <stdlib.h> +#include <fcntl.h> +#include <sys/types.h> +#include <net/gen/socket.h> +#include <net/gen/emu.h> +#include <net/gen/tcp.h> +#include <net/gen/in.h> +#include <net/gen/tcp_hdr.h> +#include <net/gen/tcp_io.h> + +int +socket(int domain, int type, int protocol) +{ + int s; + char *tcpname; + + /* only domain is AF_INET */ + if(domain != AF_INET) { + errno = EAFNOSUPPORT; + return -1; + } + + /* only type is SOCK_STREAM */ + if(type != SOCK_STREAM) { + errno = EPROTONOSUPPORT; + return -1; + } + + /* default protocol type is TCP */ + if(!protocol) + protocol = IPPROTO_TCP; + + /* only protocol type is TCP */ + if(protocol != IPPROTO_TCP) { + errno = EPROTONOSUPPORT; + return -1; + } + + /* tcp device name */ + if(!tcpname) + tcpname = getenv("TCP_DEVICE"); + if(!tcpname || !*tcpname) + tcpname = "/dev/tcp"; + + if((s = open(tcpname, O_RDWR)) < 0) { + perror(tcpname); + return -1; + } + + return s; +} + diff --git a/lib/stdio/Makefile b/lib/stdio/Makefile new file mode 100755 index 000000000..fad183d08 --- /dev/null +++ b/lib/stdio/Makefile @@ -0,0 +1,213 @@ +# Makefile for lib/stdio. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE +CC1 = $(CC) $(CFLAGS) -c + +LIBRARY = ../libc.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(clearerr.o) \ + $(LIBRARY)(data.o) \ + $(LIBRARY)(doprnt.o) \ + $(LIBRARY)(doscan.o) \ + $(LIBRARY)(ecvt.o) \ + $(LIBRARY)(fclose.o) \ + $(LIBRARY)(feof.o) \ + $(LIBRARY)(ferror.o) \ + $(LIBRARY)(fflush.o) \ + $(LIBRARY)(fgetc.o) \ + $(LIBRARY)(fgetpos.o) \ + $(LIBRARY)(fgets.o) \ + $(LIBRARY)(fileno.o) \ + $(LIBRARY)(fillbuf.o) \ + $(LIBRARY)(flushbuf.o) \ + $(LIBRARY)(fopen.o) \ + $(LIBRARY)(fprintf.o) \ + $(LIBRARY)(fputc.o) \ + $(LIBRARY)(fputs.o) \ + $(LIBRARY)(fread.o) \ + $(LIBRARY)(freopen.o) \ + $(LIBRARY)(fscanf.o) \ + $(LIBRARY)(fseek.o) \ + $(LIBRARY)(fsetpos.o) \ + $(LIBRARY)(ftell.o) \ + $(LIBRARY)(fwrite.o) \ + $(LIBRARY)(getc.o) \ + $(LIBRARY)(getchar.o) \ + $(LIBRARY)(gets.o) \ + $(LIBRARY)(icompute.o) \ + $(LIBRARY)(perror.o) \ + $(LIBRARY)(printf.o) \ + $(LIBRARY)(putc.o) \ + $(LIBRARY)(putchar.o) \ + $(LIBRARY)(puts.o) \ + $(LIBRARY)(remove.o) \ + $(LIBRARY)(rewind.o) \ + $(LIBRARY)(scanf.o) \ + $(LIBRARY)(setbuf.o) \ + $(LIBRARY)(setvbuf.o) \ + $(LIBRARY)(sprintf.o) \ + $(LIBRARY)(sscanf.o) \ + $(LIBRARY)(tmpfile.o) \ + $(LIBRARY)(tmpnam.o) \ + $(LIBRARY)(ungetc.o) \ + $(LIBRARY)(vfprintf.o) \ + $(LIBRARY)(vprintf.o) \ + $(LIBRARY)(vscanf.o) \ + $(LIBRARY)(vsprintf.o) \ + $(LIBRARY)(vsscanf.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(clearerr.o): clearerr.c + $(CC1) clearerr.c + +$(LIBRARY)(data.o): data.c + $(CC1) data.c + +$(LIBRARY)(doprnt.o): doprnt.c + $(CC1) doprnt.c + +$(LIBRARY)(doscan.o): doscan.c + $(CC1) doscan.c + +$(LIBRARY)(ecvt.o): ecvt.c + $(CC1) ecvt.c + +$(LIBRARY)(fclose.o): fclose.c + $(CC1) fclose.c + +$(LIBRARY)(feof.o): feof.c + $(CC1) feof.c + +$(LIBRARY)(ferror.o): ferror.c + $(CC1) ferror.c + +$(LIBRARY)(fflush.o): fflush.c + $(CC1) fflush.c + +$(LIBRARY)(fgetc.o): fgetc.c + $(CC1) fgetc.c + +$(LIBRARY)(fgetpos.o): fgetpos.c + $(CC1) fgetpos.c + +$(LIBRARY)(fgets.o): fgets.c + $(CC1) fgets.c + +$(LIBRARY)(fileno.o): fileno.c + $(CC1) fileno.c + +$(LIBRARY)(fillbuf.o): fillbuf.c + $(CC1) fillbuf.c + +$(LIBRARY)(flushbuf.o): flushbuf.c + $(CC1) flushbuf.c + +$(LIBRARY)(fopen.o): fopen.c + $(CC1) fopen.c + +$(LIBRARY)(fprintf.o): fprintf.c + $(CC1) fprintf.c + +$(LIBRARY)(fputc.o): fputc.c + $(CC1) fputc.c + +$(LIBRARY)(fputs.o): fputs.c + $(CC1) fputs.c + +$(LIBRARY)(fread.o): fread.c + $(CC1) fread.c + +$(LIBRARY)(freopen.o): freopen.c + $(CC1) freopen.c + +$(LIBRARY)(fscanf.o): fscanf.c + $(CC1) fscanf.c + +$(LIBRARY)(fseek.o): fseek.c + $(CC1) fseek.c + +$(LIBRARY)(fsetpos.o): fsetpos.c + $(CC1) fsetpos.c + +$(LIBRARY)(ftell.o): ftell.c + $(CC1) ftell.c + +$(LIBRARY)(fwrite.o): fwrite.c + $(CC1) fwrite.c + +$(LIBRARY)(getc.o): getc.c + $(CC1) getc.c + +$(LIBRARY)(getchar.o): getchar.c + $(CC1) getchar.c + +$(LIBRARY)(gets.o): gets.c + $(CC1) gets.c + +$(LIBRARY)(icompute.o): icompute.c + $(CC1) icompute.c + +$(LIBRARY)(perror.o): perror.c + $(CC1) perror.c + +$(LIBRARY)(printf.o): printf.c + $(CC1) printf.c + +$(LIBRARY)(putc.o): putc.c + $(CC1) putc.c + +$(LIBRARY)(putchar.o): putchar.c + $(CC1) putchar.c + +$(LIBRARY)(puts.o): puts.c + $(CC1) puts.c + +$(LIBRARY)(remove.o): remove.c + $(CC1) remove.c + +$(LIBRARY)(rewind.o): rewind.c + $(CC1) rewind.c + +$(LIBRARY)(scanf.o): scanf.c + $(CC1) scanf.c + +$(LIBRARY)(setbuf.o): setbuf.c + $(CC1) setbuf.c + +$(LIBRARY)(setvbuf.o): setvbuf.c + $(CC1) setvbuf.c + +$(LIBRARY)(sprintf.o): sprintf.c + $(CC1) sprintf.c + +$(LIBRARY)(sscanf.o): sscanf.c + $(CC1) sscanf.c + +$(LIBRARY)(tmpfile.o): tmpfile.c + $(CC1) tmpfile.c + +$(LIBRARY)(tmpnam.o): tmpnam.c + $(CC1) tmpnam.c + +$(LIBRARY)(ungetc.o): ungetc.c + $(CC1) ungetc.c + +$(LIBRARY)(vfprintf.o): vfprintf.c + $(CC1) vfprintf.c + +$(LIBRARY)(vprintf.o): vprintf.c + $(CC1) vprintf.c + +$(LIBRARY)(vscanf.o): vscanf.c + $(CC1) vscanf.c + +$(LIBRARY)(vsprintf.o): vsprintf.c + $(CC1) vsprintf.c + +$(LIBRARY)(vsscanf.o): vsscanf.c + $(CC1) vsscanf.c diff --git a/lib/stdio/clearerr.c b/lib/stdio/clearerr.c new file mode 100755 index 000000000..6379aae54 --- /dev/null +++ b/lib/stdio/clearerr.c @@ -0,0 +1,12 @@ +/* + * clearerr.c - clear error and end-of-file indicators of a stream + */ +/* $Header$ */ + +#include <stdio.h> + +void +(clearerr)(FILE *stream) +{ + clearerr(stream); +} diff --git a/lib/stdio/data.c b/lib/stdio/data.c new file mode 100755 index 000000000..3ca1585f6 --- /dev/null +++ b/lib/stdio/data.c @@ -0,0 +1,28 @@ +/* + * data.c - this is the initialization for the standard streams + */ +/* $Header$ */ + +#include <stdio.h> + +struct __iobuf __stdin = { + 0, 0, _IOREAD, 0, + (unsigned char *)NULL, (unsigned char *)NULL, +}; + +struct __iobuf __stdout = { + 0, 1, _IOWRITE, 0, + (unsigned char *)NULL, (unsigned char *)NULL, +}; + +struct __iobuf __stderr = { + 0, 2, _IOWRITE | _IOLBF, 0, + (unsigned char *)NULL, (unsigned char *)NULL, +}; + +FILE *__iotab[FOPEN_MAX] = { + &__stdin, + &__stdout, + &__stderr, + 0 +}; diff --git a/lib/stdio/doprnt.c b/lib/stdio/doprnt.c new file mode 100755 index 000000000..309505958 --- /dev/null +++ b/lib/stdio/doprnt.c @@ -0,0 +1,310 @@ +/* + * doprnt.c - print formatted output + */ +/* $Header$ */ + +#include <ctype.h> +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include "loc_incl.h" + +/* gnum() is used to get the width and precision fields of a format. */ +static const char * +gnum(register const char *f, int *ip, va_list *app) +{ + register int i, c; + + if (*f == '*') { + *ip = va_arg((*app), int); + f++; + } else { + i = 0; + while ((c = *f - '0') >= 0 && c <= 9) { + i = i*10 + c; + f++; + } + *ip = i; + } + return f; +} + +#if _EM_WSIZE == _EM_PSIZE +#define set_pointer(flags) /* nothing */ +#elif _EM_LSIZE == _EM_PSIZE +#define set_pointer(flags) (flags |= FL_LONG) +#else +#error garbage pointer size +#define set_pointer(flags) /* compilation might continue */ +#endif + +/* print an ordinal number */ +static char * +o_print(va_list *ap, int flags, char *s, char c, int precision, int is_signed) +{ + long signed_val; + unsigned long unsigned_val; + char *old_s = s; + int base; + + switch (flags & (FL_SHORT | FL_LONG)) { + case FL_SHORT: + if (is_signed) { + signed_val = (short) va_arg(*ap, int); + } else { + unsigned_val = (unsigned short) va_arg(*ap, unsigned); + } + break; + case FL_LONG: + if (is_signed) { + signed_val = va_arg(*ap, long); + } else { + unsigned_val = va_arg(*ap, unsigned long); + } + break; + default: + if (is_signed) { + signed_val = va_arg(*ap, int); + } else { + unsigned_val = va_arg(*ap, unsigned int); + } + break; + } + + if (is_signed) { + if (signed_val < 0) { + *s++ = '-'; + signed_val = -signed_val; + } else if (flags & FL_SIGN) *s++ = '+'; + else if (flags & FL_SPACE) *s++ = ' '; + unsigned_val = signed_val; + } + if ((flags & FL_ALT) && (c == 'o')) *s++ = '0'; + if (!unsigned_val) { + if (!precision) + return s; + } else if (((flags & FL_ALT) && (c == 'x' || c == 'X')) + || c == 'p') { + *s++ = '0'; + *s++ = (c == 'X' ? 'X' : 'x'); + } + + switch (c) { + case 'b': base = 2; break; + case 'o': base = 8; break; + case 'd': + case 'i': + case 'u': base = 10; break; + case 'x': + case 'X': + case 'p': base = 16; break; + } + + s = _i_compute(unsigned_val, base, s, precision); + + if (c == 'X') + while (old_s != s) { + *old_s = toupper(*old_s); + old_s++; + } + + return s; +} + +int +_doprnt(register const char *fmt, va_list ap, FILE *stream) +{ + register char *s; + register int j; + int i, c, width, precision, zfill, flags, between_fill; + int nrchars=0; + const char *oldfmt; + char *s1, buf[1025]; + + while (c = *fmt++) { + if (c != '%') { +#ifdef CPM + if (c == '\n') { + if (putc('\r', stream) == EOF) + return nrchars ? -nrchars : -1; + nrchars++; + } +#endif + if (putc(c, stream) == EOF) + return nrchars ? -nrchars : -1; + nrchars++; + continue; + } + flags = 0; + do { + switch(*fmt) { + case '-': flags |= FL_LJUST; break; + case '+': flags |= FL_SIGN; break; + case ' ': flags |= FL_SPACE; break; + case '#': flags |= FL_ALT; break; + case '0': flags |= FL_ZEROFILL; break; + default: flags |= FL_NOMORE; continue; + } + fmt++; + } while(!(flags & FL_NOMORE)); + + oldfmt = fmt; + fmt = gnum(fmt, &width, &ap); + if (fmt != oldfmt) flags |= FL_WIDTHSPEC; + + if (*fmt == '.') { + fmt++; oldfmt = fmt; + fmt = gnum(fmt, &precision, &ap); + if (precision >= 0) flags |= FL_PRECSPEC; + } + + if ((flags & FL_WIDTHSPEC) && width < 0) { + width = -width; + flags |= FL_LJUST; + } + if (!(flags & FL_WIDTHSPEC)) width = 0; + + if (flags & FL_SIGN) flags &= ~FL_SPACE; + + if (flags & FL_LJUST) flags &= ~FL_ZEROFILL; + + + s = s1 = buf; + + switch (*fmt) { + case 'h': flags |= FL_SHORT; fmt++; break; + case 'l': flags |= FL_LONG; fmt++; break; + case 'L': flags |= FL_LONGDOUBLE; fmt++; break; + } + + switch (c = *fmt++) { + default: +#ifdef CPM + if (c == '\n') { + if (putc('\r', stream) == EOF) + return nrchars ? -nrchars : -1; + nrchars++; + } +#endif + if (putc(c, stream) == EOF) + return nrchars ? -nrchars : -1; + nrchars++; + continue; + case 'n': + if (flags & FL_SHORT) + *va_arg(ap, short *) = (short) nrchars; + else if (flags & FL_LONG) + *va_arg(ap, long *) = (long) nrchars; + else + *va_arg(ap, int *) = (int) nrchars; + continue; + case 's': + s1 = va_arg(ap, char *); + if (s1 == NULL) + s1 = "(null)"; + s = s1; + while (precision || !(flags & FL_PRECSPEC)) { + if (*s == '\0') + break; + s++; + precision--; + } + break; + case 'p': + set_pointer(flags); + /* fallthrough */ + case 'b': + case 'o': + case 'u': + case 'x': + case 'X': + if (!(flags & FL_PRECSPEC)) precision = 1; + else if (c != 'p') flags &= ~FL_ZEROFILL; + s = o_print(&ap, flags, s, c, precision, 0); + break; + case 'd': + case 'i': + flags |= FL_SIGNEDCONV; + if (!(flags & FL_PRECSPEC)) precision = 1; + else flags &= ~FL_ZEROFILL; + s = o_print(&ap, flags, s, c, precision, 1); + break; + case 'c': + *s++ = va_arg(ap, int); + break; +#ifndef NOFLOAT + case 'G': + case 'g': + if ((flags & FL_PRECSPEC) && (precision == 0)) + precision = 1; + case 'f': + case 'E': + case 'e': + if (!(flags & FL_PRECSPEC)) + precision = 6; + + if (precision >= sizeof(buf)) + precision = sizeof(buf) - 1; + + flags |= FL_SIGNEDCONV; + s = _f_print(&ap, flags, s, c, precision); + break; +#endif /* NOFLOAT */ + case 'r': + ap = va_arg(ap, va_list); + fmt = va_arg(ap, char *); + continue; + } + zfill = ' '; + if (flags & FL_ZEROFILL) zfill = '0'; + j = s - s1; + + /* between_fill is true under the following conditions: + * 1- the fill character is '0' + * and + * 2a- the number is of the form 0x... or 0X... + * or + * 2b- the number contains a sign or space + */ + between_fill = 0; + if ((flags & FL_ZEROFILL) + && (((c == 'x' || c == 'X') && (flags & FL_ALT)) + || (c == 'p') + || ((flags & FL_SIGNEDCONV) + && ( *s1 == '+' || *s1 == '-' || *s1 == ' ')))) + between_fill++; + + if ((i = width - j) > 0) + if (!(flags & FL_LJUST)) { /* right justify */ + nrchars += i; + if (between_fill) { + if (flags & FL_SIGNEDCONV) { + j--; nrchars++; + if (putc(*s1++, stream) == EOF) + return nrchars ? -nrchars : -1; + } else { + j -= 2; nrchars += 2; + if ((putc(*s1++, stream) == EOF) + || (putc(*s1++, stream) == EOF)) + return nrchars ? -nrchars : -1; + } + } + do { + if (putc(zfill, stream) == EOF) + return nrchars ? -nrchars : -1; + } while (--i); + } + + nrchars += j; + while (--j >= 0) { + if (putc(*s1++, stream) == EOF) + return nrchars ? -nrchars : -1; + } + + if (i > 0) nrchars += i; + while (--i >= 0) + if (putc(zfill, stream) == EOF) + return nrchars ? -nrchars : -1; + } + return nrchars; +} diff --git a/lib/stdio/doscan.c b/lib/stdio/doscan.c new file mode 100755 index 000000000..2c1034e64 --- /dev/null +++ b/lib/stdio/doscan.c @@ -0,0 +1,449 @@ +/* + * doscan.c - scan formatted input + */ +/* $Header$ */ + +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <stdarg.h> +#include "loc_incl.h" + +#if _EM_WSIZE == _EM_PSIZE +#define set_pointer(flags) /* nothing */ +#elif _EM_LSIZE == _EM_PSIZE +#define set_pointer(flags) (flags |= FL_LONG) +#else +#error garbage pointer size +#define set_pointer(flags) /* compilation might continue */ +#endif + +#define NUMLEN 512 +#define NR_CHARS 256 + +static char Xtable[NR_CHARS]; +static char inp_buf[NUMLEN]; + +/* Collect a number of characters which constitite an ordinal number. + * When the type is 'i', the base can be 8, 10, or 16, depending on the + * first 1 or 2 characters. This means that the base must be adjusted + * according to the format of the number. At the end of the function, base + * is then set to 0, so strtol() will get the right argument. + */ +static char * +o_collect(register int c, register FILE *stream, char type, + unsigned int width, int *basep) +{ + register char *bufp = inp_buf; + register int base; + + switch (type) { + case 'i': /* i means octal, decimal or hexadecimal */ + case 'p': + case 'x': + case 'X': base = 16; break; + case 'd': + case 'u': base = 10; break; + case 'o': base = 8; break; + case 'b': base = 2; break; + } + + if (c == '-' || c == '+') { + *bufp++ = c; + if (--width) + c = getc(stream); + } + + if (width && c == '0' && base == 16) { + *bufp++ = c; + if (--width) + c = getc(stream); + if (c != 'x' && c != 'X') { + if (type == 'i') base = 8; + } + else if (width) { + *bufp++ = c; + if (--width) + c = getc(stream); + } + } + else if (type == 'i') base = 10; + + while (width) { + if (((base == 10) && isdigit(c)) + || ((base == 16) && isxdigit(c)) + || ((base == 8) && isdigit(c) && (c < '8')) + || ((base == 2) && isdigit(c) && (c < '2'))) { + *bufp++ = c; + if (--width) + c = getc(stream); + } + else break; + } + + if (width && c != EOF) ungetc(c, stream); + if (type == 'i') base = 0; + *basep = base; + *bufp = '\0'; + return bufp - 1; +} + +#ifndef NOFLOAT +/* The function f_collect() reads a string that has the format of a + * floating-point number. The function returns as soon as a format-error + * is encountered, leaving the offending character in the input. This means + * that 1.el leaves the 'l' in the input queue. Since all detection of + * format errors is done here, _doscan() doesn't call strtod() when it's + * not necessary, although the use of the width field can cause incomplete + * numbers to be passed to strtod(). (e.g. 1.3e+) + */ +static char * +f_collect(register int c, register FILE *stream, register unsigned int width) +{ + register char *bufp = inp_buf; + int digit_seen = 0; + + if (c == '-' || c == '+') { + *bufp++ = c; + if (--width) + c = getc(stream); + } + + while (width && isdigit(c)) { + digit_seen++; + *bufp++ = c; + if (--width) + c = getc(stream); + } + if (width && c == '.') { + *bufp++ = c; + if(--width) + c = getc(stream); + while (width && isdigit(c)) { + digit_seen++; + *bufp++ = c; + if (--width) + c = getc(stream); + } + } + + if (!digit_seen) { + if (width && c != EOF) ungetc(c, stream); + return inp_buf - 1; + } + else digit_seen = 0; + + if (width && (c == 'e' || c == 'E')) { + *bufp++ = c; + if (--width) + c = getc(stream); + if (width && (c == '+' || c == '-')) { + *bufp++ = c; + if (--width) + c = getc(stream); + } + while (width && isdigit(c)) { + digit_seen++; + *bufp++ = c; + if (--width) + c = getc(stream); + } + if (!digit_seen) { + if (width && c != EOF) ungetc(c,stream); + return inp_buf - 1; + } + } + + if (width && c != EOF) ungetc(c, stream); + *bufp = '\0'; + return bufp - 1; +} +#endif /* NOFLOAT */ + + +/* + * the routine that does the scanning + */ + +int +_doscan(register FILE *stream, const char *format, va_list ap) +{ + int done = 0; /* number of items done */ + int nrchars = 0; /* number of characters read */ + int conv = 0; /* # of conversions */ + int base; /* conversion base */ + unsigned long val; /* an integer value */ + register char *str; /* temporary pointer */ + char *tmp_string; /* ditto */ + unsigned width = 0; /* width of field */ + int flags; /* some flags */ + int reverse; /* reverse the checking in [...] */ + int kind; + register int ic = EOF; /* the input character */ +#ifndef NOFLOAT + long double ld_val; +#endif + + if (!*format) return 0; + + while (1) { + if (isspace(*format)) { + while (isspace(*format)) + format++; /* skip whitespace */ + ic = getc(stream); + nrchars++; + while (isspace (ic)) { + ic = getc(stream); + nrchars++; + } + if (ic != EOF) ungetc(ic,stream); + nrchars--; + } + if (!*format) break; /* end of format */ + + if (*format != '%') { + ic = getc(stream); + nrchars++; + if (ic != *format++) break; /* error */ + continue; + } + format++; + if (*format == '%') { + ic = getc(stream); + nrchars++; + if (ic == '%') { + format++; + continue; + } + else break; + } + flags = 0; + if (*format == '*') { + format++; + flags |= FL_NOASSIGN; + } + if (isdigit (*format)) { + flags |= FL_WIDTHSPEC; + for (width = 0; isdigit (*format);) + width = width * 10 + *format++ - '0'; + } + + switch (*format) { + case 'h': flags |= FL_SHORT; format++; break; + case 'l': flags |= FL_LONG; format++; break; + case 'L': flags |= FL_LONGDOUBLE; format++; break; + } + kind = *format; + if ((kind != 'c') && (kind != '[') && (kind != 'n')) { + do { + ic = getc(stream); + nrchars++; + } while (isspace(ic)); + if (ic == EOF) break; /* outer while */ + } else if (kind != 'n') { /* %c or %[ */ + ic = getc(stream); + if (ic == EOF) break; /* outer while */ + nrchars++; + } + switch (kind) { + default: + /* not recognized, like %q */ + return conv || (ic != EOF) ? done : EOF; + break; + case 'n': + if (!(flags & FL_NOASSIGN)) { /* silly, though */ + if (flags & FL_SHORT) + *va_arg(ap, short *) = (short) nrchars; + else if (flags & FL_LONG) + *va_arg(ap, long *) = (long) nrchars; + else + *va_arg(ap, int *) = (int) nrchars; + } + break; + case 'p': /* pointer */ + set_pointer(flags); + /* fallthrough */ + case 'b': /* binary */ + case 'd': /* decimal */ + case 'i': /* general integer */ + case 'o': /* octal */ + case 'u': /* unsigned */ + case 'x': /* hexadecimal */ + case 'X': /* ditto */ + if (!(flags & FL_WIDTHSPEC) || width > NUMLEN) + width = NUMLEN; + if (!width) return done; + + str = o_collect(ic, stream, kind, width, &base); + if (str < inp_buf + || (str == inp_buf + && (*str == '-' + || *str == '+'))) return done; + + /* + * Although the length of the number is str-inp_buf+1 + * we don't add the 1 since we counted it already + */ + nrchars += str - inp_buf; + + if (!(flags & FL_NOASSIGN)) { + if (kind == 'd' || kind == 'i') + val = strtol(inp_buf, &tmp_string, base); + else + val = strtoul(inp_buf, &tmp_string, base); + if (flags & FL_LONG) + *va_arg(ap, unsigned long *) = (unsigned long) val; + else if (flags & FL_SHORT) + *va_arg(ap, unsigned short *) = (unsigned short) val; + else + *va_arg(ap, unsigned *) = (unsigned) val; + } + break; + case 'c': + if (!(flags & FL_WIDTHSPEC)) + width = 1; + if (!(flags & FL_NOASSIGN)) + str = va_arg(ap, char *); + if (!width) return done; + + while (width && ic != EOF) { + if (!(flags & FL_NOASSIGN)) + *str++ = (char) ic; + if (--width) { + ic = getc(stream); + nrchars++; + } + } + + if (width) { + if (ic != EOF) ungetc(ic,stream); + nrchars--; + } + break; + case 's': + if (!(flags & FL_WIDTHSPEC)) + width = 0xffff; + if (!(flags & FL_NOASSIGN)) + str = va_arg(ap, char *); + if (!width) return done; + + while (width && ic != EOF && !isspace(ic)) { + if (!(flags & FL_NOASSIGN)) + *str++ = (char) ic; + if (--width) { + ic = getc(stream); + nrchars++; + } + } + /* terminate the string */ + if (!(flags & FL_NOASSIGN)) + *str = '\0'; + if (width) { + if (ic != EOF) ungetc(ic,stream); + nrchars--; + } + break; + case '[': + if (!(flags & FL_WIDTHSPEC)) + width = 0xffff; + if (!width) return done; + + if ( *++format == '^' ) { + reverse = 1; + format++; + } else + reverse = 0; + + for (str = Xtable; str < &Xtable[NR_CHARS] + ; str++) + *str = 0; + + if (*format == ']') Xtable[*format++] = 1; + + while (*format && *format != ']') { + Xtable[*format++] = 1; + if (*format == '-') { + format++; + if (*format + && *format != ']' + && *(format) >= *(format -2)) { + int c; + + for( c = *(format -2) + 1 + ; c <= *format ; c++) + Xtable[c] = 1; + format++; + } + else Xtable['-'] = 1; + } + } + if (!*format) return done; + + if (!(Xtable[ic] ^ reverse)) { + /* MAT 8/9/96 no match must return character */ + ungetc(ic, stream); + return done; + } + + if (!(flags & FL_NOASSIGN)) + str = va_arg(ap, char *); + + do { + if (!(flags & FL_NOASSIGN)) + *str++ = (char) ic; + if (--width) { + ic = getc(stream); + nrchars++; + } + } while (width && ic != EOF && (Xtable[ic] ^ reverse)); + + if (width) { + if (ic != EOF) ungetc(ic, stream); + nrchars--; + } + if (!(flags & FL_NOASSIGN)) { /* terminate string */ + *str = '\0'; + } + break; +#ifndef NOFLOAT + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (!(flags & FL_WIDTHSPEC) || width > NUMLEN) + width = NUMLEN; + + if (!width) return done; + str = f_collect(ic, stream, width); + + if (str < inp_buf + || (str == inp_buf + && (*str == '-' + || *str == '+'))) return done; + + /* + * Although the length of the number is str-inp_buf+1 + * we don't add the 1 since we counted it already + */ + nrchars += str - inp_buf; + + if (!(flags & FL_NOASSIGN)) { + ld_val = strtod(inp_buf, &tmp_string); + if (flags & FL_LONGDOUBLE) + *va_arg(ap, long double *) = (long double) ld_val; + else + if (flags & FL_LONG) + *va_arg(ap, double *) = (double) ld_val; + else + *va_arg(ap, float *) = (float) ld_val; + } + break; +#endif + } /* end switch */ + conv++; + if (!(flags & FL_NOASSIGN) && kind != 'n') done++; + format++; + } + return conv || (ic != EOF) ? done : EOF; +} diff --git a/lib/stdio/ecvt.c b/lib/stdio/ecvt.c new file mode 100755 index 000000000..d4e0ed7f8 --- /dev/null +++ b/lib/stdio/ecvt.c @@ -0,0 +1,31 @@ +/* $Header$ */ + +#ifndef NOFLOAT + +#include "../ansi/ext_fmt.h" +void _dbl_ext_cvt(double value, struct EXTEND *e); +char *_ext_str_cvt(struct EXTEND *e, int ndigit, int *decpt, int * sign, int ecvtflag); + +static char * +cvt(long double value, int ndigit, int *decpt, int *sign, int ecvtflag) +{ + struct EXTEND e; + + _dbl_ext_cvt(value, &e); + return _ext_str_cvt(&e, ndigit, decpt, sign, ecvtflag); +} + +char * +_ecvt(long double value, int ndigit, int *decpt, int *sign) +{ + + return cvt(value, ndigit, decpt, sign, 1); +} + +char * +_fcvt(long double value, int ndigit, int *decpt, int *sign) +{ + return cvt(value, ndigit, decpt, sign, 0); +} + +#endif /* NOFLOAT */ diff --git a/lib/stdio/fclose.c b/lib/stdio/fclose.c new file mode 100755 index 000000000..56bd0ac4a --- /dev/null +++ b/lib/stdio/fclose.c @@ -0,0 +1,31 @@ +/* + * fclose.c - flush a stream and close the file + */ +/* $Header$ */ + +#include <stdio.h> +#include <stdlib.h> +#include "loc_incl.h" + +int _close(int d); + +int +fclose(FILE *fp) +{ + register int i, retval = 0; + + for (i=0; i<FOPEN_MAX; i++) + if (fp == __iotab[i]) { + __iotab[i] = 0; + break; + } + if (i >= FOPEN_MAX) + return EOF; + if (fflush(fp)) retval = EOF; + if (_close(fileno(fp))) retval = EOF; + if ( io_testflag(fp,_IOMYBUF) && fp->_buf ) + free((void *)fp->_buf); + if (fp != stdin && fp != stdout && fp != stderr) + free((void *)fp); + return retval; +} diff --git a/lib/stdio/feof.c b/lib/stdio/feof.c new file mode 100755 index 000000000..98eade6c6 --- /dev/null +++ b/lib/stdio/feof.c @@ -0,0 +1,12 @@ +/* + * feof.c - test if eof on a stream occurred + */ +/* $Header$ */ + +#include <stdio.h> + +int +(feof)(FILE *stream) +{ + return feof(stream); +} diff --git a/lib/stdio/ferror.c b/lib/stdio/ferror.c new file mode 100755 index 000000000..c52c84b6b --- /dev/null +++ b/lib/stdio/ferror.c @@ -0,0 +1,12 @@ +/* + * ferror .c - test if an error on a stream occurred + */ +/* $Header$ */ + +#include <stdio.h> + +int +(ferror)(FILE *stream) +{ + return ferror(stream); +} diff --git a/lib/stdio/fflush.c b/lib/stdio/fflush.c new file mode 100755 index 000000000..e99b18416 --- /dev/null +++ b/lib/stdio/fflush.c @@ -0,0 +1,79 @@ +/* + * fflush.c - flush stream(s) + */ +/* $Header$ */ + +#include <sys/types.h> +#include <stdio.h> +#include "loc_incl.h" + +ssize_t _write(int d, const char *buf, size_t nbytes); +off_t _lseek(int fildes, off_t offset, int whence); + +int +fflush(FILE *stream) +{ + int count, c1, i, retval = 0; + + if (!stream) { + for(i= 0; i < FOPEN_MAX; i++) + if (__iotab[i] && fflush(__iotab[i])) + retval = EOF; + return retval; + } + + if (!stream->_buf + || (!io_testflag(stream, _IOREADING) + && !io_testflag(stream, _IOWRITING))) + return 0; + if (io_testflag(stream, _IOREADING)) { + /* (void) fseek(stream, 0L, SEEK_CUR); */ + int adjust = 0; + if (stream->_buf && !io_testflag(stream,_IONBF)) + adjust = -stream->_count; + stream->_count = 0; + if (_lseek(fileno(stream), (off_t) adjust, SEEK_CUR) == -1) { + stream->_flags |= _IOERR; + return EOF; + } + if (io_testflag(stream, _IOWRITE)) + stream->_flags &= ~(_IOREADING | _IOWRITING); + stream->_ptr = stream->_buf; + return 0; + } else if (io_testflag(stream, _IONBF)) return 0; + + if (io_testflag(stream, _IOREAD)) /* "a" or "+" mode */ + stream->_flags &= ~_IOWRITING; + + count = stream->_ptr - stream->_buf; + stream->_ptr = stream->_buf; + + if ( count <= 0 ) + return 0; + + if (io_testflag(stream, _IOAPPEND)) { + if (_lseek(fileno(stream), 0L, SEEK_END) == -1) { + stream->_flags |= _IOERR; + return EOF; + } + } + c1 = _write(stream->_fd, (char *)stream->_buf, count); + + stream->_count = 0; + + if ( count == c1 ) + return 0; + + stream->_flags |= _IOERR; + return EOF; +} + +void +__cleanup(void) +{ + register int i; + + for(i= 0; i < FOPEN_MAX; i++) + if (__iotab[i] && io_testflag(__iotab[i], _IOWRITING)) + (void) fflush(__iotab[i]); +} diff --git a/lib/stdio/fgetc.c b/lib/stdio/fgetc.c new file mode 100755 index 000000000..e7a488326 --- /dev/null +++ b/lib/stdio/fgetc.c @@ -0,0 +1,12 @@ +/* + * fgetc - get an unsigned character and return it as an int + */ +/* $Header$ */ + +#include <stdio.h> + +int +fgetc(FILE *stream) +{ + return getc(stream); +} diff --git a/lib/stdio/fgetpos.c b/lib/stdio/fgetpos.c new file mode 100755 index 000000000..d92e30c00 --- /dev/null +++ b/lib/stdio/fgetpos.c @@ -0,0 +1,14 @@ +/* + * fgetpos.c - get the position in the file + */ +/* $Header$ */ + +#include <stdio.h> + +int +fgetpos(FILE *stream, fpos_t *pos) +{ + *pos = ftell(stream); + if (*pos == -1) return -1; + return 0; +} diff --git a/lib/stdio/fgets.c b/lib/stdio/fgets.c new file mode 100755 index 000000000..1072a736b --- /dev/null +++ b/lib/stdio/fgets.c @@ -0,0 +1,27 @@ +/* + * fgets.c - get a string from a file + */ +/* $Header$ */ + +#include <stdio.h> + +char * +fgets(char *s, register int n, register FILE *stream) +{ + register int ch; + register char *ptr; + + ptr = s; + while (--n > 0 && (ch = getc(stream)) != EOF) { + *ptr++ = ch; + if ( ch == '\n') + break; + } + if (ch == EOF) { + if (feof(stream)) { + if (ptr == s) return NULL; + } else return NULL; + } + *ptr = '\0'; + return s; +} diff --git a/lib/stdio/fileno.c b/lib/stdio/fileno.c new file mode 100755 index 000000000..1619a3095 --- /dev/null +++ b/lib/stdio/fileno.c @@ -0,0 +1,12 @@ +/* + * fileno .c - map a stream to a file descriptor + */ +/* $Header$ */ + +#include <stdio.h> + +int +(fileno)(FILE *stream) +{ + return stream->_fd; +} diff --git a/lib/stdio/fillbuf.c b/lib/stdio/fillbuf.c new file mode 100755 index 000000000..148f4b9ec --- /dev/null +++ b/lib/stdio/fillbuf.c @@ -0,0 +1,69 @@ +/* + * fillbuf.c - fill a buffer + */ +/* $Header$ */ + +#if defined(_POSIX_SOURCE) +#include <sys/types.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include "loc_incl.h" + +ssize_t _read(ssize_t d, char *buf, size_t nbytes); + +int +__fillbuf(register FILE *stream) +{ + static unsigned char ch[FOPEN_MAX]; + register int i; + + stream->_count = 0; + if (fileno(stream) < 0) return EOF; + if (io_testflag(stream, (_IOEOF | _IOERR ))) return EOF; + if (!io_testflag(stream, _IOREAD)) + { stream->_flags |= _IOERR; return EOF; } + if (io_testflag(stream, _IOWRITING)) + { stream->_flags |= _IOERR; return EOF; } + + if (!io_testflag(stream, _IOREADING)) + stream->_flags |= _IOREADING; + + if (!io_testflag(stream, _IONBF) && !stream->_buf) { + stream->_buf = (unsigned char *) malloc(BUFSIZ); + if (!stream->_buf) { + stream->_flags |= _IONBF; + } + else { + stream->_flags |= _IOMYBUF; + stream->_bufsiz = BUFSIZ; + } + } + + /* flush line-buffered output when filling an input buffer */ + for (i = 0; i < FOPEN_MAX; i++) { + if (__iotab[i] && io_testflag(__iotab[i], _IOLBF)) + if (io_testflag(__iotab[i], _IOWRITING)) + (void) fflush(__iotab[i]); + } + + if (!stream->_buf) { + stream->_buf = &ch[fileno(stream)]; + stream->_bufsiz = 1; + } + stream->_ptr = stream->_buf; + stream->_count = _read(stream->_fd, (char *)stream->_buf, stream->_bufsiz); + + if (stream->_count <= 0){ + if (stream->_count == 0) { + stream->_flags |= _IOEOF; + } + else + stream->_flags |= _IOERR; + + return EOF; + } + stream->_count--; + + return *stream->_ptr++; +} diff --git a/lib/stdio/flushbuf.c b/lib/stdio/flushbuf.c new file mode 100755 index 000000000..a6a8bfb92 --- /dev/null +++ b/lib/stdio/flushbuf.c @@ -0,0 +1,127 @@ +/* + * flushbuf.c - flush a buffer + */ +/* $Id$ */ + +#include <stdio.h> +#include <stdlib.h> +#include "loc_incl.h" + +#include <sys/types.h> + +off_t _lseek(int fildes, off_t offset, int whence); +ssize_t _write(int d, const char *buf, int nbytes); +int _isatty(int d); +extern void (*_clean)(void); + +static int +do_write(int d, char *buf, int nbytes) +{ + int c; + + /* POSIX actually allows write() to return a positive value less + than nbytes, so loop ... + */ + while ((c = _write(d, buf, nbytes)) > 0 && c < nbytes) { + nbytes -= c; + buf += c; + } + return c > 0; +} + +int +__flushbuf(int c, FILE * stream) +{ + _clean = __cleanup; + if (fileno(stream) < 0) return (unsigned char) c; + if (!io_testflag(stream, _IOWRITE)) return EOF; + if (io_testflag(stream, _IOREADING) && !feof(stream)) return EOF; + + stream->_flags &= ~_IOREADING; + stream->_flags |= _IOWRITING; + if (!io_testflag(stream, _IONBF)) { + if (!stream->_buf) { + if (stream == stdout && _isatty(fileno(stdout))) { + if (!(stream->_buf = + (unsigned char *) malloc(BUFSIZ))) { + stream->_flags |= _IONBF; + } else { + stream->_flags |= _IOLBF|_IOMYBUF; + stream->_bufsiz = BUFSIZ; + stream->_count = -1; + } + } else { + if (!(stream->_buf = + (unsigned char *) malloc(BUFSIZ))) { + stream->_flags |= _IONBF; + } else { + stream->_flags |= _IOMYBUF; + stream->_bufsiz = BUFSIZ; + if (!io_testflag(stream, _IOLBF)) + stream->_count = BUFSIZ - 1; + else stream->_count = -1; + } + } + stream->_ptr = stream->_buf; + } + } + + if (io_testflag(stream, _IONBF)) { + char c1 = c; + + stream->_count = 0; + if (io_testflag(stream, _IOAPPEND)) { + if (_lseek(fileno(stream), 0L, SEEK_END) == -1) { + stream->_flags |= _IOERR; + return EOF; + } + } + if (_write(fileno(stream), &c1, 1) != 1) { + stream->_flags |= _IOERR; + return EOF; + } + return (unsigned char) c; + } else if (io_testflag(stream, _IOLBF)) { + *stream->_ptr++ = c; + /* stream->_count has been updated in putc macro. */ + if (c == '\n' || stream->_count == -stream->_bufsiz) { + int count = -stream->_count; + + stream->_ptr = stream->_buf; + stream->_count = 0; + + if (io_testflag(stream, _IOAPPEND)) { + if (_lseek(fileno(stream), 0L, SEEK_END) == -1) { + stream->_flags |= _IOERR; + return EOF; + } + } + if (! do_write(fileno(stream), (char *)stream->_buf, + count)) { + stream->_flags |= _IOERR; + return EOF; + } + } + } else { + int count = stream->_ptr - stream->_buf; + + stream->_count = stream->_bufsiz - 1; + stream->_ptr = stream->_buf + 1; + + if (count > 0) { + if (io_testflag(stream, _IOAPPEND)) { + if (_lseek(fileno(stream), 0L, SEEK_END) == -1) { + stream->_flags |= _IOERR; + return EOF; + } + } + if (! do_write(fileno(stream), (char *)stream->_buf, count)) { + *(stream->_buf) = c; + stream->_flags |= _IOERR; + return EOF; + } + } + *(stream->_buf) = c; + } + return (unsigned char) c; +} diff --git a/lib/stdio/fopen.c b/lib/stdio/fopen.c new file mode 100755 index 000000000..cf3637687 --- /dev/null +++ b/lib/stdio/fopen.c @@ -0,0 +1,120 @@ +/* + * fopen.c - open a stream + */ +/* $Header$ */ + +#if defined(_POSIX_SOURCE) +#include <sys/types.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include "loc_incl.h" + +#define PMODE 0666 + +/* The next 3 defines are true in all UNIX systems known to me. + */ +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 + +/* Since the O_CREAT flag is not available on all systems, we can't get it + * from the standard library. Furthermore, even if we know that <fcntl.h> + * contains such a flag, it's not sure whether it can be used, since we + * might be cross-compiling for another system, which may use an entirely + * different value for O_CREAT (or not support such a mode). The safest + * thing is to just use the Version 7 semantics for open, and use creat() + * whenever necessary. + * + * Another problem is O_APPEND, for which the same holds. When "a" + * open-mode is used, an lseek() to the end is done before every write() + * system-call. + * + * The O_CREAT, O_TRUNC and O_APPEND given here, are only for convenience. + * They are not passed to open(), so the values don't have to match a value + * from the real world. It is enough when they are unique. + */ +#define O_CREAT 0x010 +#define O_TRUNC 0x020 +#define O_APPEND 0x040 + +int _open(const char *path, int flags); +int _creat(const char *path, Mode_t mode); +int _close(int d); + +FILE * +fopen(const char *name, const char *mode) +{ + register int i; + int rwmode = 0, rwflags = 0; + FILE *stream; + int fd, flags = 0; + + for (i = 0; __iotab[i] != 0 ; i++) + if ( i >= FOPEN_MAX-1 ) + return (FILE *)NULL; + + switch(*mode++) { + case 'r': + flags |= _IOREAD | _IOREADING; + rwmode = O_RDONLY; + break; + case 'w': + flags |= _IOWRITE | _IOWRITING; + rwmode = O_WRONLY; + rwflags = O_CREAT | O_TRUNC; + break; + case 'a': + flags |= _IOWRITE | _IOWRITING | _IOAPPEND; + rwmode = O_WRONLY; + rwflags |= O_APPEND | O_CREAT; + break; + default: + return (FILE *)NULL; + } + + while (*mode) { + switch(*mode++) { + case 'b': + continue; + case '+': + rwmode = O_RDWR; + flags |= _IOREAD | _IOWRITE; + continue; + /* The sequence may be followed by additional characters */ + default: + break; + } + break; + } + + /* Perform a creat() when the file should be truncated or when + * the file is opened for writing and the open() failed. + */ + if ((rwflags & O_TRUNC) + || (((fd = _open(name, rwmode)) < 0) + && (rwflags & O_CREAT))) { + if (((fd = _creat(name, PMODE)) > 0) && flags | _IOREAD) { + (void) _close(fd); + fd = _open(name, rwmode); + } + + } + + if (fd < 0) return (FILE *)NULL; + + if (( stream = (FILE *) malloc(sizeof(FILE))) == NULL ) { + _close(fd); + return (FILE *)NULL; + } + + if ((flags & (_IOREAD | _IOWRITE)) == (_IOREAD | _IOWRITE)) + flags &= ~(_IOREADING | _IOWRITING); + + stream->_count = 0; + stream->_fd = fd; + stream->_flags = flags; + stream->_buf = NULL; + __iotab[i] = stream; + return stream; +} diff --git a/lib/stdio/fprintf.c b/lib/stdio/fprintf.c new file mode 100755 index 000000000..d4c224e80 --- /dev/null +++ b/lib/stdio/fprintf.c @@ -0,0 +1,23 @@ +/* + * fprintf - write output on a stream + */ +/* $Header$ */ + +#include <stdio.h> +#include <stdarg.h> +#include "loc_incl.h" + +int +fprintf(FILE *stream, const char *format, ...) +{ + va_list ap; + int retval; + + va_start(ap, format); + + retval = _doprnt (format, ap, stream); + + va_end(ap); + + return retval; +} diff --git a/lib/stdio/fputc.c b/lib/stdio/fputc.c new file mode 100755 index 000000000..ec759144c --- /dev/null +++ b/lib/stdio/fputc.c @@ -0,0 +1,12 @@ +/* + * fputc.c - print an unsigned character + */ +/* $Header$ */ + +#include <stdio.h> + +int +fputc(int c, FILE *stream) +{ + return putc(c, stream); +} diff --git a/lib/stdio/fputs.c b/lib/stdio/fputs.c new file mode 100755 index 000000000..99fe5a712 --- /dev/null +++ b/lib/stdio/fputs.c @@ -0,0 +1,18 @@ +/* + * fputs - print a string + */ +/* $Header$ */ + +#include <stdio.h> + +int +fputs(register const char *s, register FILE *stream) +{ + register int i = 0; + + while (*s) + if (putc(*s++, stream) == EOF) return EOF; + else i++; + + return i; +} diff --git a/lib/stdio/fread.c b/lib/stdio/fread.c new file mode 100755 index 000000000..a02f83ad2 --- /dev/null +++ b/lib/stdio/fread.c @@ -0,0 +1,29 @@ +/* + * fread.c - read a number of members into an array + */ +/* $Header$ */ + +#include <stdio.h> + +size_t +fread(void *ptr, size_t size, size_t nmemb, register FILE *stream) +{ + register char *cp = ptr; + register int c; + size_t ndone = 0; + register size_t s; + + if (size) + while ( ndone < nmemb ) { + s = size; + do { + if ((c = getc(stream)) != EOF) + *cp++ = c; + else + return ndone; + } while (--s); + ndone++; + } + + return ndone; +} diff --git a/lib/stdio/freopen.c b/lib/stdio/freopen.c new file mode 100755 index 000000000..5cea4870c --- /dev/null +++ b/lib/stdio/freopen.c @@ -0,0 +1,99 @@ +/* + * freopen.c - open a file and associate a stream with it + */ +/* $Header$ */ + +#if defined(_POSIX_SOURCE) +#include <sys/types.h> +#endif +#include <stdio.h> +#include <stdlib.h> +#include "loc_incl.h" + +#define PMODE 0666 + +/* Do not "optimize" this file to use the open with O_CREAT if the file + * does not exist. The reason is given in fopen.c. + */ +#define O_RDONLY 0 +#define O_WRONLY 1 +#define O_RDWR 2 + +#define O_CREAT 0x010 +#define O_TRUNC 0x020 +#define O_APPEND 0x040 + +int _open(const char *path, int flags); +int _creat(const char *path, Mode_t mode); +int _close(int d); + +FILE * +freopen(const char *name, const char *mode, FILE *stream) +{ + register int i; + int rwmode = 0, rwflags = 0; + int fd, flags = stream->_flags & (_IONBF | _IOFBF | _IOLBF | _IOMYBUF); + + (void) fflush(stream); /* ignore errors */ + (void) _close(fileno(stream)); + + switch(*mode++) { + case 'r': + flags |= _IOREAD; + rwmode = O_RDONLY; + break; + case 'w': + flags |= _IOWRITE; + rwmode = O_WRONLY; + rwflags = O_CREAT | O_TRUNC; + break; + case 'a': + flags |= _IOWRITE | _IOAPPEND; + rwmode = O_WRONLY; + rwflags |= O_APPEND | O_CREAT; + break; + default: + return (FILE *)NULL; + } + + while (*mode) { + switch(*mode++) { + case 'b': + continue; + case '+': + rwmode = O_RDWR; + flags |= _IOREAD | _IOWRITE; + continue; + /* The sequence may be followed by aditional characters */ + default: + break; + } + break; + } + + if ((rwflags & O_TRUNC) + || (((fd = _open(name, rwmode)) < 0) + && (rwflags & O_CREAT))) { + if (((fd = _creat(name, PMODE)) < 0) && flags | _IOREAD) { + (void) _close(fd); + fd = _open(name, rwmode); + } + } + + if (fd < 0) { + for( i = 0; i < FOPEN_MAX; i++) { + if (stream == __iotab[i]) { + __iotab[i] = 0; + break; + } + } + if (stream != stdin && stream != stdout && stream != stderr) + free((void *)stream); + return (FILE *)NULL; + } + + stream->_count = 0; + stream->_fd = fd; + stream->_flags = flags; + return stream; +} diff --git a/lib/stdio/fscanf.c b/lib/stdio/fscanf.c new file mode 100755 index 000000000..913a47071 --- /dev/null +++ b/lib/stdio/fscanf.c @@ -0,0 +1,23 @@ +/* + * fscanf.c - read formatted input from stream + */ +/* $Header$ */ + +#include <stdio.h> +#include <stdarg.h> +#include "loc_incl.h" + +int +fscanf(FILE *stream, const char *format, ...) +{ + va_list ap; + int retval; + + va_start(ap, format); + + retval = _doscan(stream, format, ap); + + va_end(ap); + + return retval; +} diff --git a/lib/stdio/fseek.c b/lib/stdio/fseek.c new file mode 100755 index 000000000..4e0d70948 --- /dev/null +++ b/lib/stdio/fseek.c @@ -0,0 +1,44 @@ +/* + * fseek.c - perform an fseek + */ +/* $Header$ */ + +#include <stdio.h> + +#if (SEEK_CUR != 1) || (SEEK_END != 2) || (SEEK_SET != 0) +#error SEEK_* values are wrong +#endif + +#include "loc_incl.h" + +#include <sys/types.h> + +off_t _lseek(int fildes, off_t offset, int whence); + +int +fseek(FILE *stream, long int offset, int whence) +{ + int adjust = 0; + long pos; + + stream->_flags &= ~(_IOEOF | _IOERR); + /* Clear both the end of file and error flags */ + + if (io_testflag(stream, _IOREADING)) { + if (whence == SEEK_CUR + && stream->_buf + && !io_testflag(stream,_IONBF)) + adjust = stream->_count; + stream->_count = 0; + } else if (io_testflag(stream,_IOWRITING)) { + fflush(stream); + } else /* neither reading nor writing. The buffer must be empty */ + /* EMPTY */ ; + + pos = _lseek(fileno(stream), offset - adjust, whence); + if (io_testflag(stream, _IOREAD) && io_testflag(stream, _IOWRITE)) + stream->_flags &= ~(_IOREADING | _IOWRITING); + + stream->_ptr = stream->_buf; + return ((pos == -1) ? -1 : 0); +} diff --git a/lib/stdio/fsetpos.c b/lib/stdio/fsetpos.c new file mode 100755 index 000000000..ef67ad05f --- /dev/null +++ b/lib/stdio/fsetpos.c @@ -0,0 +1,12 @@ +/* + * fsetpos.c - set the position in the file + */ +/* $Header$ */ + +#include <stdio.h> + +int +fsetpos(FILE *stream, fpos_t *pos) +{ + return fseek(stream, *pos, SEEK_SET); +} diff --git a/lib/stdio/ftell.c b/lib/stdio/ftell.c new file mode 100755 index 000000000..d0c147174 --- /dev/null +++ b/lib/stdio/ftell.c @@ -0,0 +1,38 @@ +/* + * ftell.c - obtain the value of the file-position indicator of a stream + */ +/* $Header$ */ + +#include <stdio.h> + +#if (SEEK_CUR != 1) || (SEEK_SET != 0) || (SEEK_END != 2) +#error SEEK_* values are wrong +#endif + +#include "loc_incl.h" + +#include <sys/types.h> + +off_t _lseek(int fildes, off_t offset, int whence); + +long ftell(FILE *stream) +{ + long result; + int adjust = 0; + + if (io_testflag(stream,_IOREADING)) + adjust = -stream->_count; + else if (io_testflag(stream,_IOWRITING) + && stream->_buf + && !io_testflag(stream,_IONBF)) + adjust = stream->_ptr - stream->_buf; + else adjust = 0; + + result = _lseek(fileno(stream), (off_t)0, SEEK_CUR); + + if ( result == -1 ) + return result; + + result += (long) adjust; + return result; +} diff --git a/lib/stdio/fwrite.c b/lib/stdio/fwrite.c new file mode 100755 index 000000000..f1a04452e --- /dev/null +++ b/lib/stdio/fwrite.c @@ -0,0 +1,29 @@ +/* + * fwrite.c - write a number of array elements on a file + */ +/* $Header$ */ + +#include <stdio.h> + +size_t +fwrite(const void *ptr, size_t size, size_t nmemb, + register FILE *stream) +{ + register const unsigned char *cp = ptr; + register size_t s; + size_t ndone = 0; + + if (size) + while ( ndone < nmemb ) { + s = size; + do { + if (putc((int)*cp, stream) + == EOF) + return ndone; + cp++; + } + while (--s); + ndone++; + } + return ndone; +} diff --git a/lib/stdio/getc.c b/lib/stdio/getc.c new file mode 100755 index 000000000..ff8745ecf --- /dev/null +++ b/lib/stdio/getc.c @@ -0,0 +1,12 @@ +/* + * getc.c - read an unsigned character + */ +/* $Header$ */ + +#include <stdio.h> + +int +(getc)(FILE *stream) +{ + return getc(stream); +} diff --git a/lib/stdio/getchar.c b/lib/stdio/getchar.c new file mode 100755 index 000000000..759bf60bc --- /dev/null +++ b/lib/stdio/getchar.c @@ -0,0 +1,12 @@ +/* + * getchar.c - read a character from the standard input stream + */ +/* $Header$ */ + +#include <stdio.h> + +int +(getchar)(void) +{ + return getchar(); +} diff --git a/lib/stdio/gets.c b/lib/stdio/gets.c new file mode 100755 index 000000000..53150b0cd --- /dev/null +++ b/lib/stdio/gets.c @@ -0,0 +1,27 @@ +/* + * gets.c - read a line from a stream + */ +/* $Header$ */ + +#include <stdio.h> + +char * +gets(char *s) +{ + register FILE *stream = stdin; + register int ch; + register char *ptr; + + ptr = s; + while ((ch = getc(stream)) != EOF && ch != '\n') + *ptr++ = ch; + + if (ch == EOF) { + if (feof(stream)) { + if (ptr == s) return NULL; + } else return NULL; + } + + *ptr = '\0'; + return s; +} diff --git a/lib/stdio/icompute.c b/lib/stdio/icompute.c new file mode 100755 index 000000000..5e7fa80f6 --- /dev/null +++ b/lib/stdio/icompute.c @@ -0,0 +1,21 @@ +/* + * icompute.c - compute an integer + */ +/* $Header$ */ + +#include "loc_incl.h" + +/* This routine is used in doprnt.c as well as in tmpfile.c and tmpnam.c. */ + +char * +_i_compute(unsigned long val, int base, char *s, int nrdigits) +{ + int c; + + c= val % base ; + val /= base ; + if (val || nrdigits > 1) + s = _i_compute(val, base, s, nrdigits - 1); + *s++ = (c>9 ? c-10+'a' : c+'0'); + return s; +} diff --git a/lib/stdio/loc_incl.h b/lib/stdio/loc_incl.h new file mode 100755 index 000000000..77dee58a3 --- /dev/null +++ b/lib/stdio/loc_incl.h @@ -0,0 +1,40 @@ +/* + * loc_incl.h - local include file for stdio library + */ +/* $Header$ */ + +#include <stdio.h> + +#define io_testflag(p,x) ((p)->_flags & (x)) + +#include <stdarg.h> + +#ifdef _ANSI +int _doprnt(const char *format, va_list ap, FILE *stream); +int _doscan(FILE * stream, const char *format, va_list ap); +char *_i_compute(unsigned long val, int base, char *s, int nrdigits); +char *_f_print(va_list *ap, int flags, char *s, char c, int precision); +void __cleanup(void); + +FILE *popen(const char *command, const char *type); +FILE *fdopen(int fd, const char *mode); + +#ifndef NOFLOAT +char *_ecvt(long double value, int ndigit, int *decpt, int *sign); +char *_fcvt(long double value, int ndigit, int *decpt, int *sign); +#endif /* NOFLOAT */ +#endif + +#define FL_LJUST 0x0001 /* left-justify field */ +#define FL_SIGN 0x0002 /* sign in signed conversions */ +#define FL_SPACE 0x0004 /* space in signed conversions */ +#define FL_ALT 0x0008 /* alternate form */ +#define FL_ZEROFILL 0x0010 /* fill with zero's */ +#define FL_SHORT 0x0020 /* optional h */ +#define FL_LONG 0x0040 /* optional l */ +#define FL_LONGDOUBLE 0x0080 /* optional L */ +#define FL_WIDTHSPEC 0x0100 /* field width is specified */ +#define FL_PRECSPEC 0x0200 /* precision is specified */ +#define FL_SIGNEDCONV 0x0400 /* may contain a sign */ +#define FL_NOASSIGN 0x0800 /* do not assign (in scanf) */ +#define FL_NOMORE 0x1000 /* all flags collected */ diff --git a/lib/stdio/perror.c b/lib/stdio/perror.c new file mode 100755 index 000000000..1d929da93 --- /dev/null +++ b/lib/stdio/perror.c @@ -0,0 +1,33 @@ +/* + * perror.c - print an error message on the standard error output + */ +/* $Header$ */ + +#if defined(_POSIX_SOURCE) +#include <sys/types.h> +#endif +#include <stdio.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include "loc_incl.h" + +ssize_t _write(int d, const char *buf, size_t nbytes); + +void +perror(const char *s) +{ + char *p; + int fd; + + p = strerror(errno); + fd = fileno(stderr); + fflush(stdout); + fflush(stderr); + if (s && *s) { + _write(fd, s, strlen(s)); + _write(fd, ": ", 2); + } + _write(fd, p, strlen(p)); + _write(fd, "\n", 1); +} diff --git a/lib/stdio/printf.c b/lib/stdio/printf.c new file mode 100755 index 000000000..aeadad81f --- /dev/null +++ b/lib/stdio/printf.c @@ -0,0 +1,23 @@ +/* + * printf - write on the standard output stream + */ +/* $Header$ */ + +#include <stdio.h> +#include <stdarg.h> +#include "loc_incl.h" + +int +printf(const char *format, ...) +{ + va_list ap; + int retval; + + va_start(ap, format); + + retval = _doprnt(format, ap, stdout); + + va_end(ap); + + return retval; +} diff --git a/lib/stdio/putc.c b/lib/stdio/putc.c new file mode 100755 index 000000000..fc8971c3c --- /dev/null +++ b/lib/stdio/putc.c @@ -0,0 +1,12 @@ +/* + * putc.c - print (or buffer) one character + */ +/* $Header$ */ + +#include <stdio.h> + +int +(putc)(int c, FILE *stream) +{ + return putc(c, stream); +} diff --git a/lib/stdio/putchar.c b/lib/stdio/putchar.c new file mode 100755 index 000000000..6ed235613 --- /dev/null +++ b/lib/stdio/putchar.c @@ -0,0 +1,12 @@ +/* + * putchar.c - print (or buffer) a character on the standard output stream + */ +/* $Header$ */ + +#include <stdio.h> + +int +(putchar)(int c) +{ + return putchar(c); +} diff --git a/lib/stdio/puts.c b/lib/stdio/puts.c new file mode 100755 index 000000000..56248b37f --- /dev/null +++ b/lib/stdio/puts.c @@ -0,0 +1,20 @@ +/* + * puts.c - print a string onto the standard output stream + */ +/* $Header$ */ + +#include <stdio.h> + +int +puts(register const char *s) +{ + register FILE *file = stdout; + register int i = 0; + + while (*s) { + if (putc(*s++, file) == EOF) return EOF; + else i++; + } + if (putc('\n', file) == EOF) return EOF; + return i + 1; +} diff --git a/lib/stdio/remove.c b/lib/stdio/remove.c new file mode 100755 index 000000000..72c5780ea --- /dev/null +++ b/lib/stdio/remove.c @@ -0,0 +1,13 @@ +/* + * remove.c - remove a file + */ +/* $Header$ */ + +#include <stdio.h> + +int _unlink(const char *path); + +int +remove(const char *filename) { + return _unlink(filename); +} diff --git a/lib/stdio/rewind.c b/lib/stdio/rewind.c new file mode 100755 index 000000000..5b835881b --- /dev/null +++ b/lib/stdio/rewind.c @@ -0,0 +1,14 @@ +/* + * rewind.c - set the file position indicator of a stream to the start + */ +/* $Header$ */ + +#include <stdio.h> +#include "loc_incl.h" + +void +rewind(FILE *stream) +{ + (void) fseek(stream, 0L, SEEK_SET); + clearerr(stream); +} diff --git a/lib/stdio/scanf.c b/lib/stdio/scanf.c new file mode 100755 index 000000000..1485d703a --- /dev/null +++ b/lib/stdio/scanf.c @@ -0,0 +1,25 @@ +/* + * scanf.c - read formatted input from the standard input stream + */ +/* $Header$ */ + +#include <stdio.h> +#include <stdarg.h> +#include "loc_incl.h" + +int +scanf(const char *format, ...) +{ + va_list ap; + int retval; + + va_start(ap, format); + + retval = _doscan(stdin, format, ap); + + va_end(ap); + + return retval; +} + + diff --git a/lib/stdio/setbuf.c b/lib/stdio/setbuf.c new file mode 100755 index 000000000..42fe3a266 --- /dev/null +++ b/lib/stdio/setbuf.c @@ -0,0 +1,13 @@ +/* + * setbuf.c - control buffering of a stream + */ +/* $Header$ */ + +#include <stdio.h> +#include "loc_incl.h" + +void +setbuf(register FILE *stream, char *buf) +{ + (void) setvbuf(stream, buf, (buf ? _IOFBF : _IONBF), (size_t) BUFSIZ); +} diff --git a/lib/stdio/setvbuf.c b/lib/stdio/setvbuf.c new file mode 100755 index 000000000..2c5e769df --- /dev/null +++ b/lib/stdio/setvbuf.c @@ -0,0 +1,48 @@ +/* + * setbuf.c - control buffering of a stream + */ +/* $Id$ */ + +#include <stdio.h> +#include <stdlib.h> +#include "loc_incl.h" + +extern void (*_clean)(void); + +int +setvbuf(register FILE *stream, char *buf, int mode, size_t size) +{ + int retval = 0; + + _clean = __cleanup; + if (mode != _IOFBF && mode != _IOLBF && mode != _IONBF) + return EOF; + + if (stream->_buf && io_testflag(stream,_IOMYBUF) ) + free((void *)stream->_buf); + + stream->_flags &= ~(_IOMYBUF | _IONBF | _IOLBF); + + if (buf && size <= 0) retval = EOF; + if (!buf && (mode != _IONBF)) { + if (size <= 0 || (buf = (char *) malloc(size)) == NULL) { + retval = EOF; + } else { + stream->_flags |= _IOMYBUF; + } + } + + stream->_buf = (unsigned char *) buf; + + stream->_count = 0; + stream->_flags |= mode; + stream->_ptr = stream->_buf; + + if (!buf) { + stream->_bufsiz = 1; + } else { + stream->_bufsiz = size; + } + + return retval; +} diff --git a/lib/stdio/sprintf.c b/lib/stdio/sprintf.c new file mode 100755 index 000000000..c40b080de --- /dev/null +++ b/lib/stdio/sprintf.c @@ -0,0 +1,39 @@ +/* + * sprintf - print formatted output on an array + */ +/* $Header$ */ + +#include <stdio.h> +#include <stdarg.h> +#include <limits.h> +#include "loc_incl.h" + +int +sprintf(char *s, const char *format, ...) +{ + va_list ap; + int retval; + + va_start(ap, format); + + retval = vsnprintf(s, INT_MAX, format, ap); + + va_end(ap); + + return retval; +} + +int +snprintf(char *s, size_t n, const char *format, ...) +{ + va_list ap; + int retval; + + va_start(ap, format); + + retval = vsnprintf(s, n, format, ap); + + va_end(ap); + + return retval; +} diff --git a/lib/stdio/sscanf.c b/lib/stdio/sscanf.c new file mode 100755 index 000000000..6e1ad56c7 --- /dev/null +++ b/lib/stdio/sscanf.c @@ -0,0 +1,30 @@ +/* + * sscanf - read formatted output from a string + */ +/* $Header$ */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include "loc_incl.h" + +int sscanf(const char *s, const char *format, ...) +{ + va_list ap; + int retval; + FILE tmp_stream; + + va_start(ap, format); + + tmp_stream._fd = -1; + tmp_stream._flags = _IOREAD + _IONBF + _IOREADING; + tmp_stream._buf = (unsigned char *) s; + tmp_stream._ptr = (unsigned char *) s; + tmp_stream._count = strlen(s); + + retval = _doscan(&tmp_stream, format, ap); + + va_end(ap); + + return retval; +} diff --git a/lib/stdio/tmpfile.c b/lib/stdio/tmpfile.c new file mode 100755 index 000000000..c24dced0d --- /dev/null +++ b/lib/stdio/tmpfile.c @@ -0,0 +1,31 @@ +/* + * tmpfile.c - create and open a temporary file + */ +/* $Header$ */ + +#if defined(_POSIX_SOURCE) +#include <sys/types.h> +#endif +#include <stdio.h> +#include <string.h> +#include "loc_incl.h" + +pid_t _getpid(void); + +FILE * +tmpfile(void) { + static char name_buffer[L_tmpnam] = "/tmp/tmp." ; + static char *name = NULL; + FILE *file; + + if (!name) { + name = name_buffer + strlen(name_buffer); + name = _i_compute(_getpid(), 10, name, 5); + *name = '\0'; + } + + file = fopen(name_buffer,"wb+"); + if (!file) return (FILE *)NULL; + (void) remove(name_buffer); + return file; +} diff --git a/lib/stdio/tmpnam.c b/lib/stdio/tmpnam.c new file mode 100755 index 000000000..71e5f13c2 --- /dev/null +++ b/lib/stdio/tmpnam.c @@ -0,0 +1,31 @@ +/* + * tmpnam.c - create a unique filename + */ +/* $Header$ */ + +#if defined(_POSIX_SOURCE) +#include <sys/types.h> +#endif +#include <stdio.h> +#include <string.h> +#include "loc_incl.h" + +pid_t _getpid(void); + +char * +tmpnam(char *s) { + static char name_buffer[L_tmpnam] = "/tmp/tmp."; + static unsigned long count = 0; + static char *name = NULL; + + if (!name) { + name = name_buffer + strlen(name_buffer); + name = _i_compute((unsigned long)_getpid(), 10, name, 5); + *name++ = '.'; + *name = '\0'; + } + if (++count > TMP_MAX) count = 1; /* wrap-around */ + *_i_compute(count, 10, name, 3) = '\0'; + if (s) return strcpy(s, name_buffer); + else return name_buffer; +} diff --git a/lib/stdio/ungetc.c b/lib/stdio/ungetc.c new file mode 100755 index 000000000..e78bf265c --- /dev/null +++ b/lib/stdio/ungetc.c @@ -0,0 +1,26 @@ +/* + * ungetc.c - push a character back onto an input stream + */ +/* $Header$ */ + +#include <stdio.h> +#include "loc_incl.h" + +int +ungetc(int ch, FILE *stream) +{ + unsigned char *p; + + if (ch == EOF || !io_testflag(stream,_IOREADING)) + return EOF; + if (stream->_ptr == stream->_buf) { + if (stream->_count != 0) return EOF; + stream->_ptr++; + } + stream->_count++; + p = --(stream->_ptr); /* ??? Bloody vax assembler !!! */ + /* ungetc() in sscanf() shouldn't write in rom */ + if (*p != (unsigned char) ch) + *p = (unsigned char) ch; + return ch; +} diff --git a/lib/stdio/vfprintf.c b/lib/stdio/vfprintf.c new file mode 100755 index 000000000..e7c0e0806 --- /dev/null +++ b/lib/stdio/vfprintf.c @@ -0,0 +1,14 @@ +/* + * vfprintf - formatted output without ellipsis + */ +/* $Header$ */ + +#include <stdio.h> +#include <stdarg.h> +#include "loc_incl.h" + +int +vfprintf(FILE *stream, const char *format, va_list arg) +{ + return _doprnt (format, arg, stream); +} diff --git a/lib/stdio/vprintf.c b/lib/stdio/vprintf.c new file mode 100755 index 000000000..a123cf24c --- /dev/null +++ b/lib/stdio/vprintf.c @@ -0,0 +1,14 @@ +/* + * vprintf - formatted output without ellipsis to the standard output stream + */ +/* $Header$ */ + +#include <stdio.h> +#include <stdarg.h> +#include "loc_incl.h" + +int +vprintf(const char *format, va_list arg) +{ + return _doprnt(format, arg, stdout); +} diff --git a/lib/stdio/vscanf.c b/lib/stdio/vscanf.c new file mode 100755 index 000000000..89ee8abd5 --- /dev/null +++ b/lib/stdio/vscanf.c @@ -0,0 +1,13 @@ +/* + * vscanf.c - read formatted input from the standard input stream + */ + +#include <stdio.h> +#include <stdarg.h> +#include "loc_incl.h" + +int +vscanf(const char *format, va_list ap) +{ + return _doscan(stdin, format, ap); +} diff --git a/lib/stdio/vsprintf.c b/lib/stdio/vsprintf.c new file mode 100755 index 000000000..7452a29e0 --- /dev/null +++ b/lib/stdio/vsprintf.c @@ -0,0 +1,34 @@ +/* + * vsprintf - print formatted output without ellipsis on an array + */ +/* $Header$ */ + +#include <stdio.h> +#include <stdarg.h> +#include <limits.h> +#include "loc_incl.h" + +int +vsnprintf(char *s, size_t n, const char *format, va_list arg) +{ + int retval; + FILE tmp_stream; + + tmp_stream._fd = -1; + tmp_stream._flags = _IOWRITE + _IONBF + _IOWRITING; + tmp_stream._buf = (unsigned char *) s; + tmp_stream._ptr = (unsigned char *) s; + tmp_stream._count = n-1; + + retval = _doprnt(format, arg, &tmp_stream); + tmp_stream._count = 1; + putc('\0',&tmp_stream); + + return retval; +} + +int +vsprintf(char *s, const char *format, va_list arg) +{ + return vsnprintf(s, INT_MAX, format, arg); +} diff --git a/lib/stdio/vsscanf.c b/lib/stdio/vsscanf.c new file mode 100755 index 000000000..52075187b --- /dev/null +++ b/lib/stdio/vsscanf.c @@ -0,0 +1,21 @@ +/* + * vsscanf - read formatted output from a string + */ + +#include <stdio.h> +#include <stdarg.h> +#include <string.h> +#include "loc_incl.h" + +int vsscanf(const char *s, const char *format, va_list ap) +{ + FILE tmp_stream; + + tmp_stream._fd = -1; + tmp_stream._flags = _IOREAD + _IONBF + _IOREADING; + tmp_stream._buf = (unsigned char *) s; + tmp_stream._ptr = (unsigned char *) s; + tmp_stream._count = strlen(s); + + return _doscan(&tmp_stream, format, ap); +} diff --git a/lib/syscall/Makefile b/lib/syscall/Makefile new file mode 100755 index 000000000..1b179c2e0 --- /dev/null +++ b/lib/syscall/Makefile @@ -0,0 +1,392 @@ +# Makefile for lib/syscall. + +CC1 = $(CC) -c + +LIBRARY = ../libc.a +all: $(LIBRARY) + +OBJECTS = \ + $(LIBRARY)(_exit.o) \ + $(LIBRARY)(access.o) \ + $(LIBRARY)(alarm.o) \ + $(LIBRARY)(brk.o) \ + $(LIBRARY)(cfgetispeed.o) \ + $(LIBRARY)(cfgetospeed.o) \ + $(LIBRARY)(cfsetispeed.o) \ + $(LIBRARY)(cfsetospeed.o) \ + $(LIBRARY)(chdir.o) \ + $(LIBRARY)(chmod.o) \ + $(LIBRARY)(chown.o) \ + $(LIBRARY)(chroot.o) \ + $(LIBRARY)(close.o) \ + $(LIBRARY)(closedir.o) \ + $(LIBRARY)(creat.o) \ + $(LIBRARY)(dup.o) \ + $(LIBRARY)(dup2.o) \ + $(LIBRARY)(execl.o) \ + $(LIBRARY)(execle.o) \ + $(LIBRARY)(execlp.o) \ + $(LIBRARY)(execv.o) \ + $(LIBRARY)(execve.o) \ + $(LIBRARY)(execvp.o) \ + $(LIBRARY)(fcntl.o) \ + $(LIBRARY)(fork.o) \ + $(LIBRARY)(fpathconf.o) \ + $(LIBRARY)(fstat.o) \ + $(LIBRARY)(fstatfs.o) \ + $(LIBRARY)(getcwd.o) \ + $(LIBRARY)(getegid.o) \ + $(LIBRARY)(geteuid.o) \ + $(LIBRARY)(getgid.o) \ + $(LIBRARY)(getgroups.o) \ + $(LIBRARY)(getpgrp.o) \ + $(LIBRARY)(getpid.o) \ + $(LIBRARY)(getppid.o) \ + $(LIBRARY)(getuid.o) \ + $(LIBRARY)(ioctl.o) \ + $(LIBRARY)(isatty.o) \ + $(LIBRARY)(kill.o) \ + $(LIBRARY)(link.o) \ + $(LIBRARY)(lseek.o) \ + $(LIBRARY)(mkdir.o) \ + $(LIBRARY)(mkfifo.o) \ + $(LIBRARY)(mknod.o) \ + $(LIBRARY)(mktemp.o) \ + $(LIBRARY)(mount.o) \ + $(LIBRARY)(open.o) \ + $(LIBRARY)(opendir.o) \ + $(LIBRARY)(pathconf.o) \ + $(LIBRARY)(pause.o) \ + $(LIBRARY)(pipe.o) \ + $(LIBRARY)(ptrace.o) \ + $(LIBRARY)(read.o) \ + $(LIBRARY)(readdir.o) \ + $(LIBRARY)(reboot.o) \ + $(LIBRARY)(rename.o) \ + $(LIBRARY)(rewinddir.o) \ + $(LIBRARY)(rmdir.o) \ + $(LIBRARY)(sbrk.o) \ + $(LIBRARY)(seekdir.o) \ + $(LIBRARY)(setgid.o) \ + $(LIBRARY)(setsid.o) \ + $(LIBRARY)(setuid.o) \ + $(LIBRARY)(sigaction.o) \ + $(LIBRARY)(sigaddset.o) \ + $(LIBRARY)(sigdelset.o) \ + $(LIBRARY)(sigemptyset.o) \ + $(LIBRARY)(sigfillset.o) \ + $(LIBRARY)(sigismember.o) \ + $(LIBRARY)(sigpending.o) \ + $(LIBRARY)(sigprocmask.o) \ + $(LIBRARY)(sigreturn.o) \ + $(LIBRARY)(sigsuspend.o) \ + $(LIBRARY)(sleep.o) \ + $(LIBRARY)(stat.o) \ + $(LIBRARY)(stime.o) \ + $(LIBRARY)(sync.o) \ + $(LIBRARY)(svrctl.o) \ + $(LIBRARY)(tcdrain.o) \ + $(LIBRARY)(tcflow.o) \ + $(LIBRARY)(tcflush.o) \ + $(LIBRARY)(tcgetattr.o) \ + $(LIBRARY)(tcsendbreak.o) \ + $(LIBRARY)(tcsetattr.o) \ + $(LIBRARY)(time.o) \ + $(LIBRARY)(times.o) \ + $(LIBRARY)(umask.o) \ + $(LIBRARY)(umount.o) \ + $(LIBRARY)(uname.o) \ + $(LIBRARY)(unlink.o) \ + $(LIBRARY)(utime.o) \ + $(LIBRARY)(wait.o) \ + $(LIBRARY)(waitpid.o) \ + $(LIBRARY)(write.o) \ + +$(LIBRARY): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBRARY)(_exit.o): _exit.s + $(CC1) _exit.s + +$(LIBRARY)(access.o): access.s + $(CC1) access.s + +$(LIBRARY)(alarm.o): alarm.s + $(CC1) alarm.s + +$(LIBRARY)(brk.o): brk.s + $(CC1) brk.s + +$(LIBRARY)(cfgetispeed.o): cfgetispeed.s + $(CC1) cfgetispeed.s + +$(LIBRARY)(cfgetospeed.o): cfgetospeed.s + $(CC1) cfgetospeed.s + +$(LIBRARY)(cfsetispeed.o): cfsetispeed.s + $(CC1) cfsetispeed.s + +$(LIBRARY)(cfsetospeed.o): cfsetospeed.s + $(CC1) cfsetospeed.s + +$(LIBRARY)(chdir.o): chdir.s + $(CC1) chdir.s + +$(LIBRARY)(chmod.o): chmod.s + $(CC1) chmod.s + +$(LIBRARY)(chown.o): chown.s + $(CC1) chown.s + +$(LIBRARY)(chroot.o): chroot.s + $(CC1) chroot.s + +$(LIBRARY)(close.o): close.s + $(CC1) close.s + +$(LIBRARY)(closedir.o): closedir.s + $(CC1) closedir.s + +$(LIBRARY)(creat.o): creat.s + $(CC1) creat.s + +$(LIBRARY)(dup.o): dup.s + $(CC1) dup.s + +$(LIBRARY)(dup2.o): dup2.s + $(CC1) dup2.s + +$(LIBRARY)(execl.o): execl.s + $(CC1) execl.s + +$(LIBRARY)(execle.o): execle.s + $(CC1) execle.s + +$(LIBRARY)(execlp.o): execlp.s + $(CC1) execlp.s + +$(LIBRARY)(execv.o): execv.s + $(CC1) execv.s + +$(LIBRARY)(execve.o): execve.s + $(CC1) execve.s + +$(LIBRARY)(execvp.o): execvp.s + $(CC1) execvp.s + +$(LIBRARY)(fcntl.o): fcntl.s + $(CC1) fcntl.s + +$(LIBRARY)(fork.o): fork.s + $(CC1) fork.s + +$(LIBRARY)(fpathconf.o): fpathconf.s + $(CC1) fpathconf.s + +$(LIBRARY)(fstat.o): fstat.s + $(CC1) fstat.s + +$(LIBRARY)(fstatfs.o): fstatfs.s + $(CC1) fstatfs.s + +$(LIBRARY)(getcwd.o): getcwd.s + $(CC1) getcwd.s + +$(LIBRARY)(getegid.o): getegid.s + $(CC1) getegid.s + +$(LIBRARY)(geteuid.o): geteuid.s + $(CC1) geteuid.s + +$(LIBRARY)(getgid.o): getgid.s + $(CC1) getgid.s + +$(LIBRARY)(getgroups.o): getgroups.s + $(CC1) getgroups.s + +$(LIBRARY)(getpgrp.o): getpgrp.s + $(CC1) getpgrp.s + +$(LIBRARY)(getpid.o): getpid.s + $(CC1) getpid.s + +$(LIBRARY)(getppid.o): getppid.s + $(CC1) getppid.s + +$(LIBRARY)(getuid.o): getuid.s + $(CC1) getuid.s + +$(LIBRARY)(ioctl.o): ioctl.s + $(CC1) ioctl.s + +$(LIBRARY)(isatty.o): isatty.s + $(CC1) isatty.s + +$(LIBRARY)(kill.o): kill.s + $(CC1) kill.s + +$(LIBRARY)(link.o): link.s + $(CC1) link.s + +$(LIBRARY)(lseek.o): lseek.s + $(CC1) lseek.s + +$(LIBRARY)(mkdir.o): mkdir.s + $(CC1) mkdir.s + +$(LIBRARY)(mkfifo.o): mkfifo.s + $(CC1) mkfifo.s + +$(LIBRARY)(mknod.o): mknod.s + $(CC1) mknod.s + +$(LIBRARY)(mktemp.o): mktemp.s + $(CC1) mktemp.s + +$(LIBRARY)(mount.o): mount.s + $(CC1) mount.s + +$(LIBRARY)(open.o): open.s + $(CC1) open.s + +$(LIBRARY)(opendir.o): opendir.s + $(CC1) opendir.s + +$(LIBRARY)(pathconf.o): pathconf.s + $(CC1) pathconf.s + +$(LIBRARY)(pause.o): pause.s + $(CC1) pause.s + +$(LIBRARY)(pipe.o): pipe.s + $(CC1) pipe.s + +$(LIBRARY)(ptrace.o): ptrace.s + $(CC1) ptrace.s + +$(LIBRARY)(read.o): read.s + $(CC1) read.s + +$(LIBRARY)(readdir.o): readdir.s + $(CC1) readdir.s + +$(LIBRARY)(reboot.o): reboot.s + $(CC1) reboot.s + +$(LIBRARY)(rename.o): rename.s + $(CC1) rename.s + +$(LIBRARY)(rewinddir.o): rewinddir.s + $(CC1) rewinddir.s + +$(LIBRARY)(rmdir.o): rmdir.s + $(CC1) rmdir.s + +$(LIBRARY)(sbrk.o): sbrk.s + $(CC1) sbrk.s + +$(LIBRARY)(seekdir.o): seekdir.s + $(CC1) seekdir.s + +$(LIBRARY)(setgid.o): setgid.s + $(CC1) setgid.s + +$(LIBRARY)(setsid.o): setsid.s + $(CC1) setsid.s + +$(LIBRARY)(setuid.o): setuid.s + $(CC1) setuid.s + +$(LIBRARY)(sigaction.o): sigaction.s + $(CC1) sigaction.s + +$(LIBRARY)(sigaddset.o): sigaddset.s + $(CC1) sigaddset.s + +$(LIBRARY)(sigdelset.o): sigdelset.s + $(CC1) sigdelset.s + +$(LIBRARY)(sigemptyset.o): sigemptyset.s + $(CC1) sigemptyset.s + +$(LIBRARY)(sigfillset.o): sigfillset.s + $(CC1) sigfillset.s + +$(LIBRARY)(sigismember.o): sigismember.s + $(CC1) sigismember.s + +$(LIBRARY)(sigpending.o): sigpending.s + $(CC1) sigpending.s + +$(LIBRARY)(sigprocmask.o): sigprocmask.s + $(CC1) sigprocmask.s + +$(LIBRARY)(sigreturn.o): sigreturn.s + $(CC1) sigreturn.s + +$(LIBRARY)(sigsuspend.o): sigsuspend.s + $(CC1) sigsuspend.s + +$(LIBRARY)(sleep.o): sleep.s + $(CC1) sleep.s + +$(LIBRARY)(stat.o): stat.s + $(CC1) stat.s + +$(LIBRARY)(stime.o): stime.s + $(CC1) stime.s + +$(LIBRARY)(sync.o): sync.s + $(CC1) sync.s + +$(LIBRARY)(svrctl.o): svrctl.s + $(CC1) svrctl.s + +$(LIBRARY)(tcdrain.o): tcdrain.s + $(CC1) tcdrain.s + +$(LIBRARY)(tcflow.o): tcflow.s + $(CC1) tcflow.s + +$(LIBRARY)(tcflush.o): tcflush.s + $(CC1) tcflush.s + +$(LIBRARY)(tcgetattr.o): tcgetattr.s + $(CC1) tcgetattr.s + +$(LIBRARY)(tcsendbreak.o): tcsendbreak.s + $(CC1) tcsendbreak.s + +$(LIBRARY)(tcsetattr.o): tcsetattr.s + $(CC1) tcsetattr.s + +$(LIBRARY)(time.o): time.s + $(CC1) time.s + +$(LIBRARY)(times.o): times.s + $(CC1) times.s + +$(LIBRARY)(umask.o): umask.s + $(CC1) umask.s + +$(LIBRARY)(umount.o): umount.s + $(CC1) umount.s + +$(LIBRARY)(uname.o): uname.s + $(CC1) uname.s + +$(LIBRARY)(unlink.o): unlink.s + $(CC1) unlink.s + +$(LIBRARY)(utime.o): utime.s + $(CC1) utime.s + +$(LIBRARY)(wait.o): wait.s + $(CC1) wait.s + +$(LIBRARY)(waitpid.o): waitpid.s + $(CC1) waitpid.s + +$(LIBRARY)(write.o): write.s + $(CC1) write.s diff --git a/lib/syscall/_exit.s b/lib/syscall/_exit.s new file mode 100755 index 000000000..b0e91298c --- /dev/null +++ b/lib/syscall/_exit.s @@ -0,0 +1,7 @@ +.sect .text +.extern ___exit +.define __exit +.align 2 + +__exit: + jmp ___exit diff --git a/lib/syscall/access.s b/lib/syscall/access.s new file mode 100755 index 000000000..5c6a91366 --- /dev/null +++ b/lib/syscall/access.s @@ -0,0 +1,7 @@ +.sect .text +.extern __access +.define _access +.align 2 + +_access: + jmp __access diff --git a/lib/syscall/alarm.s b/lib/syscall/alarm.s new file mode 100755 index 000000000..b303b3dab --- /dev/null +++ b/lib/syscall/alarm.s @@ -0,0 +1,7 @@ +.sect .text +.extern __alarm +.define _alarm +.align 2 + +_alarm: + jmp __alarm diff --git a/lib/syscall/brk.s b/lib/syscall/brk.s new file mode 100755 index 000000000..3d24a8da2 --- /dev/null +++ b/lib/syscall/brk.s @@ -0,0 +1,7 @@ +.sect .text +.extern __brk +.define _brk +.align 2 + +_brk: + jmp __brk diff --git a/lib/syscall/cfgetispeed.s b/lib/syscall/cfgetispeed.s new file mode 100755 index 000000000..54429defe --- /dev/null +++ b/lib/syscall/cfgetispeed.s @@ -0,0 +1,7 @@ +.sect .text +.extern __cfgetispeed +.define _cfgetispeed +.align 2 + +_cfgetispeed: + jmp __cfgetispeed diff --git a/lib/syscall/cfgetospeed.s b/lib/syscall/cfgetospeed.s new file mode 100755 index 000000000..0e21c9b6b --- /dev/null +++ b/lib/syscall/cfgetospeed.s @@ -0,0 +1,7 @@ +.sect .text +.extern __cfgetospeed +.define _cfgetospeed +.align 2 + +_cfgetospeed: + jmp __cfgetospeed diff --git a/lib/syscall/cfsetispeed.s b/lib/syscall/cfsetispeed.s new file mode 100755 index 000000000..559ef1a1a --- /dev/null +++ b/lib/syscall/cfsetispeed.s @@ -0,0 +1,7 @@ +.sect .text +.extern __cfsetispeed +.define _cfsetispeed +.align 2 + +_cfsetispeed: + jmp __cfsetispeed diff --git a/lib/syscall/cfsetospeed.s b/lib/syscall/cfsetospeed.s new file mode 100755 index 000000000..ffbe49c0f --- /dev/null +++ b/lib/syscall/cfsetospeed.s @@ -0,0 +1,7 @@ +.sect .text +.extern __cfsetospeed +.define _cfsetospeed +.align 2 + +_cfsetospeed: + jmp __cfsetospeed diff --git a/lib/syscall/chdir.s b/lib/syscall/chdir.s new file mode 100755 index 000000000..fc872136e --- /dev/null +++ b/lib/syscall/chdir.s @@ -0,0 +1,7 @@ +.sect .text +.extern __chdir +.define _chdir +.align 2 + +_chdir: + jmp __chdir diff --git a/lib/syscall/chmod.s b/lib/syscall/chmod.s new file mode 100755 index 000000000..caf12bf9e --- /dev/null +++ b/lib/syscall/chmod.s @@ -0,0 +1,7 @@ +.sect .text +.extern __chmod +.define _chmod +.align 2 + +_chmod: + jmp __chmod diff --git a/lib/syscall/chown.s b/lib/syscall/chown.s new file mode 100755 index 000000000..6f7cff3da --- /dev/null +++ b/lib/syscall/chown.s @@ -0,0 +1,7 @@ +.sect .text +.extern __chown +.define _chown +.align 2 + +_chown: + jmp __chown diff --git a/lib/syscall/chroot.s b/lib/syscall/chroot.s new file mode 100755 index 000000000..f2d4f1860 --- /dev/null +++ b/lib/syscall/chroot.s @@ -0,0 +1,7 @@ +.sect .text +.extern __chroot +.define _chroot +.align 2 + +_chroot: + jmp __chroot diff --git a/lib/syscall/close.s b/lib/syscall/close.s new file mode 100755 index 000000000..94803d637 --- /dev/null +++ b/lib/syscall/close.s @@ -0,0 +1,7 @@ +.sect .text +.extern __close +.define _close +.align 2 + +_close: + jmp __close diff --git a/lib/syscall/closedir.s b/lib/syscall/closedir.s new file mode 100755 index 000000000..44bba250d --- /dev/null +++ b/lib/syscall/closedir.s @@ -0,0 +1,7 @@ +.sect .text +.extern __closedir +.define _closedir +.align 2 + +_closedir: + jmp __closedir diff --git a/lib/syscall/creat.s b/lib/syscall/creat.s new file mode 100755 index 000000000..3bc6b273e --- /dev/null +++ b/lib/syscall/creat.s @@ -0,0 +1,7 @@ +.sect .text +.extern __creat +.define _creat +.align 2 + +_creat: + jmp __creat diff --git a/lib/syscall/dup.s b/lib/syscall/dup.s new file mode 100755 index 000000000..5049f5fbc --- /dev/null +++ b/lib/syscall/dup.s @@ -0,0 +1,7 @@ +.sect .text +.extern __dup +.define _dup +.align 2 + +_dup: + jmp __dup diff --git a/lib/syscall/dup2.s b/lib/syscall/dup2.s new file mode 100755 index 000000000..de110aa77 --- /dev/null +++ b/lib/syscall/dup2.s @@ -0,0 +1,7 @@ +.sect .text +.extern __dup2 +.define _dup2 +.align 2 + +_dup2: + jmp __dup2 diff --git a/lib/syscall/execl.s b/lib/syscall/execl.s new file mode 100755 index 000000000..647e6dde5 --- /dev/null +++ b/lib/syscall/execl.s @@ -0,0 +1,7 @@ +.sect .text +.extern __execl +.define _execl +.align 2 + +_execl: + jmp __execl diff --git a/lib/syscall/execle.s b/lib/syscall/execle.s new file mode 100755 index 000000000..fa0456298 --- /dev/null +++ b/lib/syscall/execle.s @@ -0,0 +1,7 @@ +.sect .text +.extern __execle +.define _execle +.align 2 + +_execle: + jmp __execle diff --git a/lib/syscall/execlp.s b/lib/syscall/execlp.s new file mode 100755 index 000000000..7a360fe06 --- /dev/null +++ b/lib/syscall/execlp.s @@ -0,0 +1,7 @@ +.sect .text +.extern __execlp +.define _execlp +.align 2 + +_execlp: + jmp __execlp diff --git a/lib/syscall/execv.s b/lib/syscall/execv.s new file mode 100755 index 000000000..692d20469 --- /dev/null +++ b/lib/syscall/execv.s @@ -0,0 +1,7 @@ +.sect .text +.extern __execv +.define _execv +.align 2 + +_execv: + jmp __execv diff --git a/lib/syscall/execve.s b/lib/syscall/execve.s new file mode 100755 index 000000000..ad2193450 --- /dev/null +++ b/lib/syscall/execve.s @@ -0,0 +1,7 @@ +.sect .text +.extern __execve +.define _execve +.align 2 + +_execve: + jmp __execve diff --git a/lib/syscall/execvp.s b/lib/syscall/execvp.s new file mode 100755 index 000000000..e03c26489 --- /dev/null +++ b/lib/syscall/execvp.s @@ -0,0 +1,7 @@ +.sect .text +.extern __execvp +.define _execvp +.align 2 + +_execvp: + jmp __execvp diff --git a/lib/syscall/fcntl.s b/lib/syscall/fcntl.s new file mode 100755 index 000000000..97b78940b --- /dev/null +++ b/lib/syscall/fcntl.s @@ -0,0 +1,7 @@ +.sect .text +.extern __fcntl +.define _fcntl +.align 2 + +_fcntl: + jmp __fcntl diff --git a/lib/syscall/fork.s b/lib/syscall/fork.s new file mode 100755 index 000000000..913497617 --- /dev/null +++ b/lib/syscall/fork.s @@ -0,0 +1,7 @@ +.sect .text +.extern __fork +.define _fork +.align 2 + +_fork: + jmp __fork diff --git a/lib/syscall/fpathconf.s b/lib/syscall/fpathconf.s new file mode 100755 index 000000000..130a45dac --- /dev/null +++ b/lib/syscall/fpathconf.s @@ -0,0 +1,7 @@ +.sect .text +.extern __fpathconf +.define _fpathconf +.align 2 + +_fpathconf: + jmp __fpathconf diff --git a/lib/syscall/fstat.s b/lib/syscall/fstat.s new file mode 100755 index 000000000..5ee5b9935 --- /dev/null +++ b/lib/syscall/fstat.s @@ -0,0 +1,7 @@ +.sect .text +.extern __fstat +.define _fstat +.align 2 + +_fstat: + jmp __fstat diff --git a/lib/syscall/fstatfs.s b/lib/syscall/fstatfs.s new file mode 100644 index 000000000..3357f8a19 --- /dev/null +++ b/lib/syscall/fstatfs.s @@ -0,0 +1,7 @@ +.sect .text +.extern __fstatfs +.define _fstatfs +.align 2 + +_fstatfs: + jmp __fstatfs diff --git a/lib/syscall/getcwd.s b/lib/syscall/getcwd.s new file mode 100755 index 000000000..262c92d15 --- /dev/null +++ b/lib/syscall/getcwd.s @@ -0,0 +1,7 @@ +.sect .text +.extern __getcwd +.define _getcwd +.align 2 + +_getcwd: + jmp __getcwd diff --git a/lib/syscall/getegid.s b/lib/syscall/getegid.s new file mode 100755 index 000000000..0e65bc9ba --- /dev/null +++ b/lib/syscall/getegid.s @@ -0,0 +1,7 @@ +.sect .text +.extern __getegid +.define _getegid +.align 2 + +_getegid: + jmp __getegid diff --git a/lib/syscall/geteuid.s b/lib/syscall/geteuid.s new file mode 100755 index 000000000..9cbc952b2 --- /dev/null +++ b/lib/syscall/geteuid.s @@ -0,0 +1,7 @@ +.sect .text +.extern __geteuid +.define _geteuid +.align 2 + +_geteuid: + jmp __geteuid diff --git a/lib/syscall/getgid.s b/lib/syscall/getgid.s new file mode 100755 index 000000000..7f4caa38e --- /dev/null +++ b/lib/syscall/getgid.s @@ -0,0 +1,7 @@ +.sect .text +.extern __getgid +.define _getgid +.align 2 + +_getgid: + jmp __getgid diff --git a/lib/syscall/getgroups.s b/lib/syscall/getgroups.s new file mode 100755 index 000000000..bcbb40afa --- /dev/null +++ b/lib/syscall/getgroups.s @@ -0,0 +1,7 @@ +.sect .text +.extern __getgroups +.define _getgroups +.align 2 + +_getgroups: + jmp __getgroups diff --git a/lib/syscall/getpgrp.s b/lib/syscall/getpgrp.s new file mode 100755 index 000000000..2f7abb332 --- /dev/null +++ b/lib/syscall/getpgrp.s @@ -0,0 +1,7 @@ +.sect .text +.extern __getpgrp +.define _getpgrp +.align 2 + +_getpgrp: + jmp __getpgrp diff --git a/lib/syscall/getpid.s b/lib/syscall/getpid.s new file mode 100755 index 000000000..ea176ae0a --- /dev/null +++ b/lib/syscall/getpid.s @@ -0,0 +1,7 @@ +.sect .text +.extern __getpid +.define _getpid +.align 2 + +_getpid: + jmp __getpid diff --git a/lib/syscall/getppid.s b/lib/syscall/getppid.s new file mode 100755 index 000000000..d0ee47400 --- /dev/null +++ b/lib/syscall/getppid.s @@ -0,0 +1,7 @@ +.sect .text +.extern __getppid +.define _getppid +.align 2 + +_getppid: + jmp __getppid diff --git a/lib/syscall/getsysinfo.s b/lib/syscall/getsysinfo.s new file mode 100644 index 000000000..0f6327af3 --- /dev/null +++ b/lib/syscall/getsysinfo.s @@ -0,0 +1,7 @@ +.sect .text +.extern __getsysinfo +.define _getsysinfo +.align 2 + +_getsysinfo: + jmp __getsysinfo diff --git a/lib/syscall/getuid.s b/lib/syscall/getuid.s new file mode 100755 index 000000000..fb4d40dbc --- /dev/null +++ b/lib/syscall/getuid.s @@ -0,0 +1,7 @@ +.sect .text +.extern __getuid +.define _getuid +.align 2 + +_getuid: + jmp __getuid diff --git a/lib/syscall/ioctl.s b/lib/syscall/ioctl.s new file mode 100755 index 000000000..863165d39 --- /dev/null +++ b/lib/syscall/ioctl.s @@ -0,0 +1,7 @@ +.sect .text +.extern __ioctl +.define _ioctl +.align 2 + +_ioctl: + jmp __ioctl diff --git a/lib/syscall/isatty.s b/lib/syscall/isatty.s new file mode 100755 index 000000000..bbb5ed971 --- /dev/null +++ b/lib/syscall/isatty.s @@ -0,0 +1,7 @@ +.sect .text +.extern __isatty +.define _isatty +.align 2 + +_isatty: + jmp __isatty diff --git a/lib/syscall/kill.s b/lib/syscall/kill.s new file mode 100755 index 000000000..db8775b12 --- /dev/null +++ b/lib/syscall/kill.s @@ -0,0 +1,7 @@ +.sect .text +.extern __kill +.define _kill +.align 2 + +_kill: + jmp __kill diff --git a/lib/syscall/link.s b/lib/syscall/link.s new file mode 100755 index 000000000..4a96c4342 --- /dev/null +++ b/lib/syscall/link.s @@ -0,0 +1,7 @@ +.sect .text +.extern __link +.define _link +.align 2 + +_link: + jmp __link diff --git a/lib/syscall/lseek.s b/lib/syscall/lseek.s new file mode 100755 index 000000000..936c27860 --- /dev/null +++ b/lib/syscall/lseek.s @@ -0,0 +1,7 @@ +.sect .text +.extern __lseek +.define _lseek +.align 2 + +_lseek: + jmp __lseek diff --git a/lib/syscall/mkdir.s b/lib/syscall/mkdir.s new file mode 100755 index 000000000..43bd99414 --- /dev/null +++ b/lib/syscall/mkdir.s @@ -0,0 +1,7 @@ +.sect .text +.extern __mkdir +.define _mkdir +.align 2 + +_mkdir: + jmp __mkdir diff --git a/lib/syscall/mkfifo.s b/lib/syscall/mkfifo.s new file mode 100755 index 000000000..91ec0f151 --- /dev/null +++ b/lib/syscall/mkfifo.s @@ -0,0 +1,7 @@ +.sect .text +.extern __mkfifo +.define _mkfifo +.align 2 + +_mkfifo: + jmp __mkfifo diff --git a/lib/syscall/mknod.s b/lib/syscall/mknod.s new file mode 100755 index 000000000..045cdfdd2 --- /dev/null +++ b/lib/syscall/mknod.s @@ -0,0 +1,7 @@ +.sect .text +.extern __mknod +.define _mknod +.align 2 + +_mknod: + jmp __mknod diff --git a/lib/syscall/mktemp.s b/lib/syscall/mktemp.s new file mode 100755 index 000000000..5415a5378 --- /dev/null +++ b/lib/syscall/mktemp.s @@ -0,0 +1,7 @@ +.sect .text +.extern __mktemp +.define _mktemp +.align 2 + +_mktemp: + jmp __mktemp diff --git a/lib/syscall/mount.s b/lib/syscall/mount.s new file mode 100755 index 000000000..4bdbac562 --- /dev/null +++ b/lib/syscall/mount.s @@ -0,0 +1,7 @@ +.sect .text +.extern __mount +.define _mount +.align 2 + +_mount: + jmp __mount diff --git a/lib/syscall/open.s b/lib/syscall/open.s new file mode 100755 index 000000000..255c9836b --- /dev/null +++ b/lib/syscall/open.s @@ -0,0 +1,7 @@ +.sect .text +.extern __open +.define _open +.align 2 + +_open: + jmp __open diff --git a/lib/syscall/opendir.s b/lib/syscall/opendir.s new file mode 100755 index 000000000..07f35c63c --- /dev/null +++ b/lib/syscall/opendir.s @@ -0,0 +1,7 @@ +.sect .text +.extern __opendir +.define _opendir +.align 2 + +_opendir: + jmp __opendir diff --git a/lib/syscall/pathconf.s b/lib/syscall/pathconf.s new file mode 100755 index 000000000..6215a76f6 --- /dev/null +++ b/lib/syscall/pathconf.s @@ -0,0 +1,7 @@ +.sect .text +.extern __pathconf +.define _pathconf +.align 2 + +_pathconf: + jmp __pathconf diff --git a/lib/syscall/pause.s b/lib/syscall/pause.s new file mode 100755 index 000000000..c1e7adb6e --- /dev/null +++ b/lib/syscall/pause.s @@ -0,0 +1,7 @@ +.sect .text +.extern __pause +.define _pause +.align 2 + +_pause: + jmp __pause diff --git a/lib/syscall/pipe.s b/lib/syscall/pipe.s new file mode 100755 index 000000000..8115ea72b --- /dev/null +++ b/lib/syscall/pipe.s @@ -0,0 +1,7 @@ +.sect .text +.extern __pipe +.define _pipe +.align 2 + +_pipe: + jmp __pipe diff --git a/lib/syscall/ptrace.s b/lib/syscall/ptrace.s new file mode 100755 index 000000000..ac30974da --- /dev/null +++ b/lib/syscall/ptrace.s @@ -0,0 +1,7 @@ +.sect .text +.extern __ptrace +.define _ptrace +.align 2 + +_ptrace: + jmp __ptrace diff --git a/lib/syscall/read.s b/lib/syscall/read.s new file mode 100755 index 000000000..a61010cfb --- /dev/null +++ b/lib/syscall/read.s @@ -0,0 +1,7 @@ +.sect .text +.extern __read +.define _read +.align 2 + +_read: + jmp __read diff --git a/lib/syscall/readdir.s b/lib/syscall/readdir.s new file mode 100755 index 000000000..ab6784de8 --- /dev/null +++ b/lib/syscall/readdir.s @@ -0,0 +1,7 @@ +.sect .text +.extern __readdir +.define _readdir +.align 2 + +_readdir: + jmp __readdir diff --git a/lib/syscall/reboot.s b/lib/syscall/reboot.s new file mode 100755 index 000000000..2883b7790 --- /dev/null +++ b/lib/syscall/reboot.s @@ -0,0 +1,7 @@ +.sect .text +.extern __reboot +.define _reboot +.align 2 + +_reboot: + jmp __reboot diff --git a/lib/syscall/rename.s b/lib/syscall/rename.s new file mode 100755 index 000000000..43e65ec22 --- /dev/null +++ b/lib/syscall/rename.s @@ -0,0 +1,7 @@ +.sect .text +.extern __rename +.define _rename +.align 2 + +_rename: + jmp __rename diff --git a/lib/syscall/rewinddir.s b/lib/syscall/rewinddir.s new file mode 100755 index 000000000..7b12a16f2 --- /dev/null +++ b/lib/syscall/rewinddir.s @@ -0,0 +1,7 @@ +.sect .text +.extern __rewinddir +.define _rewinddir +.align 2 + +_rewinddir: + jmp __rewinddir diff --git a/lib/syscall/rmdir.s b/lib/syscall/rmdir.s new file mode 100755 index 000000000..009875946 --- /dev/null +++ b/lib/syscall/rmdir.s @@ -0,0 +1,7 @@ +.sect .text +.extern __rmdir +.define _rmdir +.align 2 + +_rmdir: + jmp __rmdir diff --git a/lib/syscall/sbrk.s b/lib/syscall/sbrk.s new file mode 100755 index 000000000..273b63fed --- /dev/null +++ b/lib/syscall/sbrk.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sbrk +.define _sbrk +.align 2 + +_sbrk: + jmp __sbrk diff --git a/lib/syscall/seekdir.s b/lib/syscall/seekdir.s new file mode 100755 index 000000000..9c90b95a1 --- /dev/null +++ b/lib/syscall/seekdir.s @@ -0,0 +1,7 @@ +.sect .text +.extern __seekdir +.define _seekdir +.align 2 + +_seekdir: + jmp __seekdir diff --git a/lib/syscall/setgid.s b/lib/syscall/setgid.s new file mode 100755 index 000000000..12aa77755 --- /dev/null +++ b/lib/syscall/setgid.s @@ -0,0 +1,7 @@ +.sect .text +.extern __setgid +.define _setgid +.align 2 + +_setgid: + jmp __setgid diff --git a/lib/syscall/setsid.s b/lib/syscall/setsid.s new file mode 100755 index 000000000..a834bb71f --- /dev/null +++ b/lib/syscall/setsid.s @@ -0,0 +1,7 @@ +.sect .text +.extern __setsid +.define _setsid +.align 2 + +_setsid: + jmp __setsid diff --git a/lib/syscall/setuid.s b/lib/syscall/setuid.s new file mode 100755 index 000000000..85e87c36a --- /dev/null +++ b/lib/syscall/setuid.s @@ -0,0 +1,7 @@ +.sect .text +.extern __setuid +.define _setuid +.align 2 + +_setuid: + jmp __setuid diff --git a/lib/syscall/sigaction.s b/lib/syscall/sigaction.s new file mode 100755 index 000000000..bac3e796c --- /dev/null +++ b/lib/syscall/sigaction.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sigaction +.define _sigaction +.align 2 + +_sigaction: + jmp __sigaction diff --git a/lib/syscall/sigaddset.s b/lib/syscall/sigaddset.s new file mode 100755 index 000000000..88d2fc920 --- /dev/null +++ b/lib/syscall/sigaddset.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sigaddset +.define _sigaddset +.align 2 + +_sigaddset: + jmp __sigaddset diff --git a/lib/syscall/sigdelset.s b/lib/syscall/sigdelset.s new file mode 100755 index 000000000..f91178876 --- /dev/null +++ b/lib/syscall/sigdelset.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sigdelset +.define _sigdelset +.align 2 + +_sigdelset: + jmp __sigdelset diff --git a/lib/syscall/sigemptyset.s b/lib/syscall/sigemptyset.s new file mode 100755 index 000000000..c92e2475d --- /dev/null +++ b/lib/syscall/sigemptyset.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sigemptyset +.define _sigemptyset +.align 2 + +_sigemptyset: + jmp __sigemptyset diff --git a/lib/syscall/sigfillset.s b/lib/syscall/sigfillset.s new file mode 100755 index 000000000..b9f729285 --- /dev/null +++ b/lib/syscall/sigfillset.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sigfillset +.define _sigfillset +.align 2 + +_sigfillset: + jmp __sigfillset diff --git a/lib/syscall/sigismember.s b/lib/syscall/sigismember.s new file mode 100755 index 000000000..93ed0e64f --- /dev/null +++ b/lib/syscall/sigismember.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sigismember +.define _sigismember +.align 2 + +_sigismember: + jmp __sigismember diff --git a/lib/syscall/sigpending.s b/lib/syscall/sigpending.s new file mode 100755 index 000000000..608c46d85 --- /dev/null +++ b/lib/syscall/sigpending.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sigpending +.define _sigpending +.align 2 + +_sigpending: + jmp __sigpending diff --git a/lib/syscall/sigprocmask.s b/lib/syscall/sigprocmask.s new file mode 100755 index 000000000..fc4ddc034 --- /dev/null +++ b/lib/syscall/sigprocmask.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sigprocmask +.define _sigprocmask +.align 2 + +_sigprocmask: + jmp __sigprocmask diff --git a/lib/syscall/sigreturn.s b/lib/syscall/sigreturn.s new file mode 100755 index 000000000..2f5e9e301 --- /dev/null +++ b/lib/syscall/sigreturn.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sigreturn +.define _sigreturn +.align 2 + +_sigreturn: + jmp __sigreturn diff --git a/lib/syscall/sigsuspend.s b/lib/syscall/sigsuspend.s new file mode 100755 index 000000000..ca4934cfb --- /dev/null +++ b/lib/syscall/sigsuspend.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sigsuspend +.define _sigsuspend +.align 2 + +_sigsuspend: + jmp __sigsuspend diff --git a/lib/syscall/sleep.s b/lib/syscall/sleep.s new file mode 100755 index 000000000..6760731b3 --- /dev/null +++ b/lib/syscall/sleep.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sleep +.define _sleep +.align 2 + +_sleep: + jmp __sleep diff --git a/lib/syscall/stat.s b/lib/syscall/stat.s new file mode 100755 index 000000000..7b966ca7f --- /dev/null +++ b/lib/syscall/stat.s @@ -0,0 +1,7 @@ +.sect .text +.extern __stat +.define _stat +.align 2 + +_stat: + jmp __stat diff --git a/lib/syscall/stime.s b/lib/syscall/stime.s new file mode 100755 index 000000000..516469a70 --- /dev/null +++ b/lib/syscall/stime.s @@ -0,0 +1,7 @@ +.sect .text +.extern __stime +.define _stime +.align 2 + +_stime: + jmp __stime diff --git a/lib/syscall/svrctl.s b/lib/syscall/svrctl.s new file mode 100755 index 000000000..f477737b0 --- /dev/null +++ b/lib/syscall/svrctl.s @@ -0,0 +1,7 @@ +.sect .text +.extern __svrctl +.define _svrctl +.align 2 + +_svrctl: + jmp __svrctl diff --git a/lib/syscall/sync.s b/lib/syscall/sync.s new file mode 100755 index 000000000..8ad7e3bed --- /dev/null +++ b/lib/syscall/sync.s @@ -0,0 +1,7 @@ +.sect .text +.extern __sync +.define _sync +.align 2 + +_sync: + jmp __sync diff --git a/lib/syscall/tcdrain.s b/lib/syscall/tcdrain.s new file mode 100755 index 000000000..7dfaa9db3 --- /dev/null +++ b/lib/syscall/tcdrain.s @@ -0,0 +1,7 @@ +.sect .text +.extern __tcdrain +.define _tcdrain +.align 2 + +_tcdrain: + jmp __tcdrain diff --git a/lib/syscall/tcflow.s b/lib/syscall/tcflow.s new file mode 100755 index 000000000..69b8f9ca4 --- /dev/null +++ b/lib/syscall/tcflow.s @@ -0,0 +1,7 @@ +.sect .text +.extern __tcflow +.define _tcflow +.align 2 + +_tcflow: + jmp __tcflow diff --git a/lib/syscall/tcflush.s b/lib/syscall/tcflush.s new file mode 100755 index 000000000..c1067fd09 --- /dev/null +++ b/lib/syscall/tcflush.s @@ -0,0 +1,7 @@ +.sect .text +.extern __tcflush +.define _tcflush +.align 2 + +_tcflush: + jmp __tcflush diff --git a/lib/syscall/tcgetattr.s b/lib/syscall/tcgetattr.s new file mode 100755 index 000000000..5fbb7f9d4 --- /dev/null +++ b/lib/syscall/tcgetattr.s @@ -0,0 +1,7 @@ +.sect .text +.extern __tcgetattr +.define _tcgetattr +.align 2 + +_tcgetattr: + jmp __tcgetattr diff --git a/lib/syscall/tcsendbreak.s b/lib/syscall/tcsendbreak.s new file mode 100755 index 000000000..e91d6892d --- /dev/null +++ b/lib/syscall/tcsendbreak.s @@ -0,0 +1,7 @@ +.sect .text +.extern __tcsendbreak +.define _tcsendbreak +.align 2 + +_tcsendbreak: + jmp __tcsendbreak diff --git a/lib/syscall/tcsetattr.s b/lib/syscall/tcsetattr.s new file mode 100755 index 000000000..896c6c0aa --- /dev/null +++ b/lib/syscall/tcsetattr.s @@ -0,0 +1,7 @@ +.sect .text +.extern __tcsetattr +.define _tcsetattr +.align 2 + +_tcsetattr: + jmp __tcsetattr diff --git a/lib/syscall/time.s b/lib/syscall/time.s new file mode 100755 index 000000000..e0d424bec --- /dev/null +++ b/lib/syscall/time.s @@ -0,0 +1,7 @@ +.sect .text +.extern __time +.define _time +.align 2 + +_time: + jmp __time diff --git a/lib/syscall/times.s b/lib/syscall/times.s new file mode 100755 index 000000000..34624d42b --- /dev/null +++ b/lib/syscall/times.s @@ -0,0 +1,7 @@ +.sect .text +.extern __times +.define _times +.align 2 + +_times: + jmp __times diff --git a/lib/syscall/umask.s b/lib/syscall/umask.s new file mode 100755 index 000000000..b882d1967 --- /dev/null +++ b/lib/syscall/umask.s @@ -0,0 +1,7 @@ +.sect .text +.extern __umask +.define _umask +.align 2 + +_umask: + jmp __umask diff --git a/lib/syscall/umount.s b/lib/syscall/umount.s new file mode 100755 index 000000000..0e44c9516 --- /dev/null +++ b/lib/syscall/umount.s @@ -0,0 +1,7 @@ +.sect .text +.extern __umount +.define _umount +.align 2 + +_umount: + jmp __umount diff --git a/lib/syscall/uname.s b/lib/syscall/uname.s new file mode 100755 index 000000000..4a87f57ae --- /dev/null +++ b/lib/syscall/uname.s @@ -0,0 +1,7 @@ +.sect .text +.extern __uname +.define _uname +.align 2 + +_uname: + jmp __uname diff --git a/lib/syscall/unlink.s b/lib/syscall/unlink.s new file mode 100755 index 000000000..d0bea4183 --- /dev/null +++ b/lib/syscall/unlink.s @@ -0,0 +1,7 @@ +.sect .text +.extern __unlink +.define _unlink +.align 2 + +_unlink: + jmp __unlink diff --git a/lib/syscall/utime.s b/lib/syscall/utime.s new file mode 100755 index 000000000..01e60e7a2 --- /dev/null +++ b/lib/syscall/utime.s @@ -0,0 +1,7 @@ +.sect .text +.extern __utime +.define _utime +.align 2 + +_utime: + jmp __utime diff --git a/lib/syscall/wait.s b/lib/syscall/wait.s new file mode 100755 index 000000000..fc700f452 --- /dev/null +++ b/lib/syscall/wait.s @@ -0,0 +1,7 @@ +.sect .text +.extern __wait +.define _wait +.align 2 + +_wait: + jmp __wait diff --git a/lib/syscall/waitpid.s b/lib/syscall/waitpid.s new file mode 100755 index 000000000..ca4fac6f8 --- /dev/null +++ b/lib/syscall/waitpid.s @@ -0,0 +1,7 @@ +.sect .text +.extern __waitpid +.define _waitpid +.align 2 + +_waitpid: + jmp __waitpid diff --git a/lib/syscall/write.s b/lib/syscall/write.s new file mode 100755 index 000000000..276fed473 --- /dev/null +++ b/lib/syscall/write.s @@ -0,0 +1,7 @@ +.sect .text +.extern __write +.define _write +.align 2 + +_write: + jmp __write diff --git a/lib/syslib/Makefile b/lib/syslib/Makefile new file mode 100755 index 000000000..555274e88 --- /dev/null +++ b/lib/syslib/Makefile @@ -0,0 +1,162 @@ +# Makefile for lib/syslib. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE +CC1 = $(CC) $(CFLAGS) -c + +LIBSYS = ../libsys.a +all: $(LIBSYS) + +OBJECTS = \ + $(LIBSYS)(kmalloc.o) \ + $(LIBSYS)(kprintf.o) \ + $(LIBSYS)(kputc.o) \ + $(LIBSYS)(sys_times.o) \ + $(LIBSYS)(sys_getuptm.o) \ + $(LIBSYS)(sys_abort.o) \ + $(LIBSYS)(sys_copy.o) \ + $(LIBSYS)(sys_exec.o) \ + $(LIBSYS)(sys_fork.o) \ + $(LIBSYS)(sys_kill.o) \ + $(LIBSYS)(sys_newmap.o) \ + $(LIBSYS)(sys_sigctl.o) \ + $(LIBSYS)(sys_svrctl.o) \ + $(LIBSYS)(sys_trace.o) \ + $(LIBSYS)(sys_xit.o) \ + $(LIBSYS)(sys_sdevio.o) \ + $(LIBSYS)(sys_getinfo.o) \ + $(LIBSYS)(sys_kmalloc.o) \ + $(LIBSYS)(sys_irqctl.o) \ + $(LIBSYS)(sys_eniop.o) \ + $(LIBSYS)(sys_ph2sg.o) \ + $(LIBSYS)(sys_umap.o) \ + $(LIBSYS)(sys_exit.o) \ + $(LIBSYS)(sys_physcp.o) \ + $(LIBSYS)(sys_vircp.o) \ + $(LIBSYS)(sys_in.o) \ + $(LIBSYS)(sys_out.o) \ + $(LIBSYS)(sys_vinb.o) \ + $(LIBSYS)(sys_vinw.o) \ + $(LIBSYS)(sys_vinl.o) \ + $(LIBSYS)(sys_voutb.o) \ + $(LIBSYS)(sys_voutw.o) \ + $(LIBSYS)(sys_voutl.o) \ + $(LIBSYS)(sys_signalrm.o) \ + $(LIBSYS)(sys_flagalrm.o) \ + $(LIBSYS)(sys_syncalrm.o) \ + $(LIBSYS)(taskcall.o) \ + +$(LIBSYS): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBSYS)(kmalloc.o): kmalloc.c + $(CC1) kmalloc.c + +$(LIBSYS)(kprintf.o): kprintf.c + $(CC1) kprintf.c + +$(LIBSYS)(kputc.o): kputc.c + $(CC1) kputc.c + +$(LIBSYS)(sys_times.o): sys_times.c + $(CC1) sys_times.c + +$(LIBSYS)(sys_getuptm.o): sys_getuptm.c + $(CC1) sys_getuptm.c + +$(LIBSYS)(sys_abort.o): sys_abort.c + $(CC1) sys_abort.c + +$(LIBSYS)(sys_copy.o): sys_copy.c + $(CC1) sys_copy.c + +$(LIBSYS)(sys_exec.o): sys_exec.c + $(CC1) sys_exec.c + +$(LIBSYS)(sys_fork.o): sys_fork.c + $(CC1) sys_fork.c + +$(LIBSYS)(sys_kill.o): sys_kill.c + $(CC1) sys_kill.c + +$(LIBSYS)(sys_newmap.o): sys_newmap.c + $(CC1) sys_newmap.c + +$(LIBSYS)(sys_svrctl.o): sys_svrctl.c + $(CC1) sys_svrctl.c + +$(LIBSYS)(sys_trace.o): sys_trace.c + $(CC1) sys_trace.c + +$(LIBSYS)(sys_xit.o): sys_xit.c + $(CC1) sys_xit.c + +$(LIBSYS)(sys_sdevio.o): sys_sdevio.c + $(CC1) sys_sdevio.c + +$(LIBSYS)(sys_getinfo.o): sys_getinfo.c + $(CC1) sys_getinfo.c + +$(LIBSYS)(sys_kmalloc.o): sys_kmalloc.c + $(CC1) sys_kmalloc.c + +$(LIBSYS)(sys_irqctl.o): sys_irqctl.c + $(CC1) sys_irqctl.c + +$(LIBSYS)(sys_eniop.o): sys_eniop.c + $(CC1) sys_eniop.c + +$(LIBSYS)(sys_ph2sg.o): sys_ph2sg.c + $(CC1) sys_ph2sg.c + +$(LIBSYS)(sys_umap.o): sys_umap.c + $(CC1) sys_umap.c + +$(LIBSYS)(sys_exit.o): sys_exit.c + $(CC1) sys_exit.c + +$(LIBSYS)(sys_sigctl.o): sys_sigctl.c + $(CC1) sys_sigctl.c + +$(LIBSYS)(sys_physcp.o): sys_physcp.c + $(CC1) sys_physcp.c + +$(LIBSYS)(sys_vircp.o): sys_vircp.c + $(CC1) sys_vircp.c + +$(LIBSYS)(sys_out.o): sys_out.c + $(CC1) sys_out.c + +$(LIBSYS)(sys_in.o): sys_in.c + $(CC1) sys_in.c + +$(LIBSYS)(sys_voutb.o): sys_voutb.c + $(CC1) sys_voutb.c + +$(LIBSYS)(sys_voutw.o): sys_voutw.c + $(CC1) sys_voutw.c + +$(LIBSYS)(sys_voutl.o): sys_voutl.c + $(CC1) sys_voutl.c + +$(LIBSYS)(sys_vinb.o): sys_vinb.c + $(CC1) sys_vinb.c + +$(LIBSYS)(sys_vinw.o): sys_vinw.c + $(CC1) sys_vinw.c + +$(LIBSYS)(sys_vinl.o): sys_vinl.c + $(CC1) sys_vinl.c + +$(LIBSYS)(sys_signalrm.o): sys_signalrm.c + $(CC1) sys_signalrm.c + +$(LIBSYS)(sys_flagalrm.o): sys_flagalrm.c + $(CC1) sys_flagalrm.c + +$(LIBSYS)(sys_syncalrm.o): sys_syncalrm.c + $(CC1) sys_syncalrm.c + +$(LIBSYS)(taskcall.o): taskcall.c + $(CC1) taskcall.c + diff --git a/lib/syslib/kmalloc.c b/lib/syslib/kmalloc.c new file mode 100755 index 000000000..b9ed72c53 --- /dev/null +++ b/lib/syslib/kmalloc.c @@ -0,0 +1,177 @@ +/* malloc(), realloc(), free() - simple memory allocation routines + * + * This is a very small and simple minded malloc Author: Kees J. Bot + * implementation. Ideal for things like a 29 Jan 1994 + * bootstrap program, or for debugging. Six times + * slower than any good malloc. + */ +#define nil 0 + +#define sbrk _sbrk +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <limits.h> +#if !DEBUG +#define NDEBUG 1 +#define debug(expr) ((void) 0) +#else +#define debug(expr) expr +#endif +#include <assert.h> + +typedef struct cell { + size_t size; /* Size of a malloc()'ed object. */ +#if DEBUG + unsigned magic; /* To recognize a cell. */ +#endif + struct cell *next; /* Next cell on the free list. */ +#if DEBUG + unsigned sacred; /* Don't touch while unallocated. */ +#endif +} cell_t; + +#if UINT_MAX <= 0xFFFF +#define MAGIC 0x537B +#else +#define MAGIC 0x537BC0D8 +#endif + +/* Size of the header of an object. */ +#define HDR_SIZE offsetof(cell_t, next) + +/* An offset from a cell pointer to a next cell. */ +#define offset(cp, size) ((cell_t *) ((char *) (cp) + (size))) + +/* Address of the object in a cell and back. */ +#define cell2obj(cp) ((void *) ((char *) (cp) + HDR_SIZE)) +#define obj2cell(op) ((cell_t *) ((char *) (op) - HDR_SIZE)) + +/* The free list. */ +static cell_t *freelist; + +void *malloc(size_t size) +/* Allocate an object of at least the given size. */ +{ + cell_t **pcp, *cp; + + size += HDR_SIZE; + if (size < sizeof(cell_t)) size= sizeof(cell_t); + + /* Align to a word. Use a real malloc if you need better alignment. */ + size= (size + sizeof(int) - 1) & ~(sizeof(int) - 1); + + /* Space for a magic number at the end of the chunk. */ + debug(size += sizeof(unsigned)); + + for (;;) { + /* Do a first fit search. */ + pcp= &freelist; + while ((cp= *pcp) != nil) { + cell_t *next= cp->next; + + assert(cp->magic == MAGIC); + assert(cp->sacred == MAGIC); + + if (offset(cp, cp->size) == next) { + /* Join adjacent free cells. */ + assert(next->magic == MAGIC); + assert(next->sacred == MAGIC); + + cp->size+= next->size; + cp->next= next->next; + + continue; /* Try again. */ + } + if (size <= cp->size) break; /* Big enough. */ + + /* Next cell. */ + pcp= &cp->next; + } + + if (cp != nil) break; /* Found a big enough chunk. */ + + /* Allocate a new chunk at the break. */ + if ((cp= (cell_t *) sbrk(size)) == (cell_t *) -1) { + return nil; + } + + cp->size= size; + cp->next= nil; + debug(cp->magic= MAGIC); + debug(cp->sacred= MAGIC); + *pcp= cp; + } + + /* We've got a cell that is big enough. Can we break it up? */ + if (cp->size >= size + sizeof(cell_t)) { + cell_t *next= offset(cp, size); + + next->size= cp->size - size; + next->next= cp->next; + debug(next->magic= MAGIC); + debug(next->sacred= MAGIC); + cp->size= size; + cp->next= next; + } + + /* Unchain the cell we've found and return an address in it. */ + *pcp= cp->next; + debug(memset(cell2obj(cp), 0xAA, cp->size - HDR_SIZE)); + debug(((unsigned *) offset(cp, cp->size))[-1]= MAGIC); + + return cell2obj(cp); +} + +void free(void *op) +/* Deallocate an object. */ +{ + cell_t **prev, *next, *cp; + + if (op == nil) return; /* Aaargh. */ + + cp= obj2cell(op); + assert(cp->magic == MAGIC); + assert(((unsigned *) offset(cp, cp->size))[-1] == MAGIC); + debug(cp->sacred= MAGIC); + + /* Find the spot where the object belongs. */ + prev= &freelist; + while ((next= *prev) != nil && next < cp) { + assert(next->magic == MAGIC); + assert(next->sacred == MAGIC); + prev= &next->next; + } + + /* Put the new free cell in the list. */ + *prev= cp; + cp->next= next; + +#if DEBUG + /* Check the rest of the list. */ + while (next != nil) { + assert(next->magic == MAGIC); + assert(next->sacred == MAGIC); + next= next->next; + } +#endif +} + +void *realloc(void *op, size_t size) +/* Change the size of an object. Don't bother being smart, just copy it. */ +{ + size_t oldsize; + void *new; + + oldsize= op == nil ? 0 : obj2cell(op)->size - HDR_SIZE; + + new= malloc(size); + memcpy(new, op, oldsize > size ? size : oldsize); + free(op); + return new; +} + +/* + * $PchId: malloc.c,v 1.4 1996/02/22 09:15:56 philip Exp $ + */ diff --git a/lib/syslib/kprintf.c b/lib/syslib/kprintf.c new file mode 100755 index 000000000..5428b75d8 --- /dev/null +++ b/lib/syslib/kprintf.c @@ -0,0 +1,189 @@ +/* printf() - kernel printf() Author: Kees J. Bot + * 15 Jan 1994 + */ +#define nil 0 +#include <stdarg.h> +#include <stddef.h> +#include <limits.h> + +#define isdigit(c) ((unsigned) ((c) - '0') < (unsigned) 10) + +#if !__STDC__ +/* Classic C stuff, ignore. */ +void kputc(); +int printf(fmt) char *fmt; +#else + +/* Printf() uses kputc() to print characters. */ +void kputc(int c); + +int printf(const char *fmt, ...) +#endif +{ + int c; + enum { LEFT, RIGHT } adjust; + enum { LONG, INT } intsize; + int fill; + int width, max, len, base; + static char X2C_tab[]= "0123456789ABCDEF"; + static char x2c_tab[]= "0123456789abcdef"; + char *x2c; + char *p; + long i; + unsigned long u; + char temp[8 * sizeof(long) / 3 + 2]; + + va_list argp; + + va_start(argp, fmt); + + while ((c= *fmt++) != 0) { + if (c != '%') { + /* Ordinary character. */ + kputc(c); + continue; + } + + /* Format specifier of the form: + * %[adjust][fill][width][.max]keys + */ + c= *fmt++; + + adjust= RIGHT; + if (c == '-') { + adjust= LEFT; + c= *fmt++; + } + + fill= ' '; + if (c == '0') { + fill= '0'; + c= *fmt++; + } + + width= 0; + if (c == '*') { + /* Width is specified as an argument, e.g. %*d. */ + width= va_arg(argp, int); + c= *fmt++; + } else + if (isdigit(c)) { + /* A number tells the width, e.g. %10d. */ + do { + width= width * 10 + (c - '0'); + } while (isdigit(c= *fmt++)); + } + + max= INT_MAX; + if (c == '.') { + /* Max field length coming up. */ + if ((c= *fmt++) == '*') { + max= va_arg(argp, int); + c= *fmt++; + } else + if (isdigit(c)) { + max= 0; + do { + max= max * 10 + (c - '0'); + } while (isdigit(c= *fmt++)); + } + } + + /* Set a few flags to the default. */ + x2c= x2c_tab; + i= 0; + base= 10; + intsize= INT; + if (c == 'l' || c == 'L') { + /* "Long" key, e.g. %ld. */ + intsize= LONG; + c= *fmt++; + } + if (c == 0) break; + + switch (c) { + /* Decimal. */ + case 'd': + i= intsize == LONG ? va_arg(argp, long) + : va_arg(argp, int); + u= i < 0 ? -i : i; + goto int2ascii; + + /* Octal. */ + case 'o': + base= 010; + goto getint; + + /* Pointer, interpret as %X or %lX. */ + case 'p': + if (sizeof(char *) > sizeof(int)) intsize= LONG; + + /* Hexadecimal. %X prints upper case A-F, not %lx. */ + case 'X': + x2c= X2C_tab; + case 'x': + base= 0x10; + goto getint; + + /* Unsigned decimal. */ + case 'u': + getint: + u= intsize == LONG ? va_arg(argp, unsigned long) + : va_arg(argp, unsigned int); + int2ascii: + p= temp + sizeof(temp)-1; + *p= 0; + do { + *--p= x2c[(ptrdiff_t) (u % base)]; + } while ((u /= base) > 0); + goto string_length; + + /* A character. */ + case 'c': + p= temp; + *p= va_arg(argp, int); + len= 1; + goto string_print; + + /* Simply a percent. */ + case '%': + p= temp; + *p= '%'; + len= 1; + goto string_print; + + /* A string. The other cases will join in here. */ + case 's': + p= va_arg(argp, char *); + + string_length: + for (len= 0; p[len] != 0 && len < max; len++) {} + + string_print: + width -= len; + if (i < 0) width--; + if (fill == '0' && i < 0) kputc('-'); + if (adjust == RIGHT) { + while (width > 0) { kputc(fill); width--; } + } + if (fill == ' ' && i < 0) kputc('-'); + while (len > 0) { kputc((unsigned char) *p++); len--; } + while (width > 0) { kputc(fill); width--; } + break; + + /* Unrecognized format key, echo it back. */ + default: + kputc('%'); + kputc(c); + } + } + + /* Mark the end with a null (should be something else, like -1). */ + kputc(0); + va_end(argp); + return 0; +} + +/* + * $PchId: kprintf.c,v 1.5 1996/04/11 06:59:05 philip Exp $ + */ diff --git a/lib/syslib/kputc.c b/lib/syslib/kputc.c new file mode 100644 index 000000000..77e123498 --- /dev/null +++ b/lib/syslib/kputc.c @@ -0,0 +1,47 @@ +/* A server must occasionally print some message. It uses a simple version of + * printf() found in the system lib that calls kputc() to output characters. + * Printing is done with a call to the kernel, and not by going through FS. + * This way system messages end up in the kernel messages buffer and can be + * reviewed at a later time. + * + * This routine can only be used by servers and device drivers. The kernel + * must define its own kputc(). Note that the IS also defines its own kputc() + * to directly call the TTY instead of the kernel, because it does not want + * to pollute the kernel message buffer with its debug dumps. + */ + +#include "syslib.h" +#include <minix/callnr.h> +#include <minix/minlib.h> + +/*===========================================================================* + * kputc * + *===========================================================================*/ +void kputc(c) +int c; +{ +/* Accumulate another character. If 0 or buffer full, print it. */ + + static int buf_count; /* # characters in the buffer */ + static char print_buf[80]; /* output is buffered here */ + message m; + + if ((c == 0 && buf_count > 0) || buf_count == sizeof(print_buf)) { + /* Send the buffer to the system task, or, if this process is not a + * server yet, to standard error. + */ + m.DIAG_BUF_COUNT = buf_count; + m.DIAG_PRINT_BUF = print_buf; + m.DIAG_PROC_NR = SELF; + m.m_type = DIAGNOSTICS; + if (_sendrec(TTY, &m) != 0) { + m.m1_i1 = 2; + m.m1_i2 = buf_count; + m.m1_p1 = print_buf; + m.m_type = WRITE; + (void) _sendrec(FS, &m); + } + buf_count = 0; + } + if (c != 0) print_buf[buf_count++] = c; +} diff --git a/lib/syslib/sys_abort.c b/lib/syslib/sys_abort.c new file mode 100755 index 000000000..94876cb52 --- /dev/null +++ b/lib/syslib/sys_abort.c @@ -0,0 +1,21 @@ +#include "syslib.h" +#include <stdarg.h> +#include <unistd.h> + +PUBLIC int sys_abort(int how, ...) +{ +/* Something awful has happened. Abandon ship. */ + + message m; + va_list ap; + + va_start(ap, how); + if ((m.ABRT_HOW = how) == RBT_MONITOR) { + m.ABRT_MON_PROC = va_arg(ap, int); + m.ABRT_MON_ADDR = va_arg(ap, char *); + m.ABRT_MON_LEN = va_arg(ap, size_t); + } + va_end(ap); + + return(_taskcall(SYSTASK, SYS_ABORT, &m)); +} diff --git a/lib/syslib/sys_copy.c b/lib/syslib/sys_copy.c new file mode 100755 index 000000000..e360bf767 --- /dev/null +++ b/lib/syslib/sys_copy.c @@ -0,0 +1,31 @@ +#include "syslib.h" + +PUBLIC int sys_copy(src_proc, src_seg, src_vir, + dst_proc, dst_seg, dst_vir, bytes) +int src_proc; /* source process */ +int src_seg; /* source segment: T, D, or S */ +phys_bytes src_vir; /* source virtual address (phys addr for ABS)*/ +int dst_proc; /* dest process */ +int dst_seg; /* dest segment: T, D, or S */ +phys_bytes dst_vir; /* dest virtual address (phys addr for ABS) */ +phys_bytes bytes; /* how many bytes */ +{ +/* Transfer a block of data. The source and destination can each either be a + * process number, SELF (to set own process number), or ABS (to indicate the + * use of an absolute memory address). + */ + + message copy_mess; + + if (bytes == 0L) return(OK); + copy_mess.CP_SRC_SPACE = src_seg; + copy_mess.CP_SRC_PROC_NR = src_proc; + copy_mess.CP_SRC_ADDR = (long) src_vir; + + copy_mess.CP_DST_SPACE = dst_seg; + copy_mess.CP_DST_PROC_NR = dst_proc; + copy_mess.CP_DST_ADDR = (long) dst_vir; + + copy_mess.CP_NR_BYTES = (long) bytes; + return(_taskcall(SYSTASK, SYS_COPY, ©_mess)); +} diff --git a/lib/syslib/sys_endsig.c b/lib/syslib/sys_endsig.c new file mode 100755 index 000000000..acf07c7c1 --- /dev/null +++ b/lib/syslib/sys_endsig.c @@ -0,0 +1,10 @@ +#include "syslib.h" + +PUBLIC int sys_endsig(proc) +int proc; +{ + message m; + + m.m1_i1 = proc; + return(_taskcall(SYSTASK, SYS_ENDSIG, &m)); +} diff --git a/lib/syslib/sys_eniop.c b/lib/syslib/sys_eniop.c new file mode 100644 index 000000000..248ab8e33 --- /dev/null +++ b/lib/syslib/sys_eniop.c @@ -0,0 +1,14 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_enable_iop * + *===========================================================================*/ +PUBLIC int sys_enable_iop(proc_nr) +int proc_nr; /* number of process to allow I/O */ +{ + message m_iop; + m_iop.PROC_NR = proc_nr; + return _taskcall(SYSTASK, SYS_IOPENABLE, &m_iop); +} + + diff --git a/lib/syslib/sys_enirq.c b/lib/syslib/sys_enirq.c new file mode 100644 index 000000000..6c1a1eda5 --- /dev/null +++ b/lib/syslib/sys_enirq.c @@ -0,0 +1,14 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_enable_irq * + *===========================================================================*/ +PUBLIC int sys_enable_irq(int irq_nr) +{ + message m_irq; + m_irq.IRQ_NR = irq_nr; + m_irq.IRQ_CTL_OP = IRQ_ENABLE; + return _taskcall(SYSTASK, SYS_IRQCTL, &m_irq); +} + + diff --git a/lib/syslib/sys_exec.c b/lib/syslib/sys_exec.c new file mode 100755 index 000000000..5adf09641 --- /dev/null +++ b/lib/syslib/sys_exec.c @@ -0,0 +1,20 @@ +#include "syslib.h" + +PUBLIC int sys_exec(proc, ptr, traced, prog_name, initpc) +int proc; /* process that did exec */ +char *ptr; /* new stack pointer */ +int traced; /* is tracing enabled? */ +char *prog_name; /* name of the new program */ +vir_bytes initpc; +{ +/* A process has exec'd. Tell the kernel. */ + + message m; + + m.PR_PROC_NR = proc; + m.PR_TRACING = traced; + m.PR_STACK_PTR = ptr; + m.PR_NAME_PTR = prog_name; + m.PR_IP_PTR = (char *)initpc; + return(_taskcall(SYSTASK, SYS_EXEC, &m)); +} diff --git a/lib/syslib/sys_exit.c b/lib/syslib/sys_exit.c new file mode 100644 index 000000000..770366c88 --- /dev/null +++ b/lib/syslib/sys_exit.c @@ -0,0 +1,15 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_exit * + *===========================================================================*/ +PUBLIC int sys_exit(int status) +{ +/* A server wants to exit. The exit status is passed on, possibly a panic. + */ + message m; + m.EXIT_STATUS = status; + return(_taskcall(SYSTASK, SYS_EXIT, &m)); + /* NOT REACHED */ +} + diff --git a/lib/syslib/sys_findproc.c b/lib/syslib/sys_findproc.c new file mode 100755 index 000000000..4b648df6a --- /dev/null +++ b/lib/syslib/sys_findproc.c @@ -0,0 +1,28 @@ +#include "syslib.h" +#include <string.h> + +int sys_findproc(name, tasknr, flags) +char *name; +int *tasknr; +int flags; +{ +/* Map a task name to a task number. */ + message m; + int r; + + strncpy(m.m3_ca1, name, M3_STRING); + m.m3_i1= flags; + + /* Clear unused fields */ + m.m3_i2 = 0; + m.m3_p1= NULL; + + r= _taskcall(SYSTASK, SYS_FINDPROC, &m); + + *tasknr= m.m3_i1; + return r; +} + +/* + * $PchId: sys_findproc.c,v 1.2 1996/04/11 05:46:27 philip Exp $ + */ diff --git a/lib/syslib/sys_flagalrm.c b/lib/syslib/sys_flagalrm.c new file mode 100644 index 000000000..f1d87a3fc --- /dev/null +++ b/lib/syslib/sys_flagalrm.c @@ -0,0 +1,23 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_flagalrm * + *===========================================================================*/ +PUBLIC int sys_flagalrm(ticks, flag_ptr) +clock_t ticks; /* number of ticks until the flag is set */ +int *flag_ptr; /* pointer to timeout flag to be set */ +{ +/* Make a call to the clock to schedule a timeout flag alarm for the caller. */ + message m; + + if (flag_ptr != NULL) /* expect the worst */ + *flag_ptr = 0; /* reset timeout flag first */ + m.m_type = SYS_FLAGALRM; /* alarm type requested */ + m.ALRM_PROC_NR = SELF; /* m_source will be used */ + m.ALRM_EXP_TIME = ticks; /* alarm is due after ticks */ + m.ALRM_ABS_TIME = 0; /* ticks are relative to now */ + m.ALRM_FLAG_PTR = (char *) flag_ptr; + return _taskcall(SYSTASK, SYS_FLAGALRM, &m); +} + + diff --git a/lib/syslib/sys_fork.c b/lib/syslib/sys_fork.c new file mode 100755 index 000000000..f57aaa3e7 --- /dev/null +++ b/lib/syslib/sys_fork.c @@ -0,0 +1,16 @@ +#include "syslib.h" + +PUBLIC int sys_fork(parent, child, pid) +int parent; /* process doing the fork */ +int child; /* which proc has been created by the fork */ +int pid; /* process id assigned by MM */ +{ +/* A process has forked. Tell the kernel. */ + + message m; + + m.PR_PPROC_NR = parent; + m.PR_PROC_NR = child; + m.PR_PID = pid; + return(_taskcall(SYSTASK, SYS_FORK, &m)); +} diff --git a/lib/syslib/sys_fwdirq.c b/lib/syslib/sys_fwdirq.c new file mode 100644 index 000000000..cf5ffb71b --- /dev/null +++ b/lib/syslib/sys_fwdirq.c @@ -0,0 +1,14 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_forward_irq * + *===========================================================================*/ +PUBLIC int sys_forward_irq(int irq_nr) +{ + message m_irq; + m_irq.IRQ_NR = irq_nr; + m_irq.IRQ_CTL_OP = IRQ_DO_FWD; + return _taskcall(SYSTASK, SYS_IRQCTL, &m_irq); +} + + diff --git a/lib/syslib/sys_getinfo.c b/lib/syslib/sys_getinfo.c new file mode 100644 index 000000000..e033d420a --- /dev/null +++ b/lib/syslib/sys_getinfo.c @@ -0,0 +1,25 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_getinfo * + *===========================================================================*/ +PUBLIC int sys_getinfo(request, val_ptr, val_len, key_ptr, key_len) +int request; /* system info requested */ +void *val_ptr; /* pointer where to store it */ +int val_len; /* max length of value to get */ +void *key_ptr; /* pointer to key requested */ +int key_len; /* length of key */ +{ + message m; + + m.I_REQUEST = request; + m.I_PROC_NR = SELF; /* always store values at caller */ + m.I_VAL_PTR = val_ptr; + m.I_VAL_LEN = val_len; + m.I_KEY_PTR = key_ptr; + m.I_KEY_LEN = key_len; + + return(_taskcall(SYSTASK, SYS_GETINFO, &m)); +} + + diff --git a/lib/syslib/sys_getmap.c b/lib/syslib/sys_getmap.c new file mode 100755 index 000000000..5cf3d175a --- /dev/null +++ b/lib/syslib/sys_getmap.c @@ -0,0 +1,14 @@ +#include "syslib.h" + +PUBLIC int sys_getmap(proc, ptr) +int proc; /* process whose map is to be fetched */ +struct mem_map *ptr; /* pointer to new map */ +{ +/* Want to know map of a process, ask the kernel. */ + + message m; + + m.m1_i1 = proc; + m.m1_p1 = (char *) ptr; + return(_taskcall(SYSTASK, SYS_GETMAP, &m)); +} diff --git a/lib/syslib/sys_getsp.c b/lib/syslib/sys_getsp.c new file mode 100755 index 000000000..a3c320617 --- /dev/null +++ b/lib/syslib/sys_getsp.c @@ -0,0 +1,16 @@ +#include "syslib.h" + +PUBLIC int sys_getsp(proc, newsp) +int proc; /* process whose sp is wanted */ +vir_bytes *newsp; /* place to put sp read from kernel */ +{ +/* Ask the kernel what the sp is. */ + + message m; + int r; + + m.m1_i1 = proc; + r = _taskcall(SYSTASK, SYS_GETSP, &m); + *newsp = (vir_bytes) m.STACK_PTR; + return(r); +} diff --git a/lib/syslib/sys_getuptm.c b/lib/syslib/sys_getuptm.c new file mode 100644 index 000000000..952a42688 --- /dev/null +++ b/lib/syslib/sys_getuptm.c @@ -0,0 +1,14 @@ +#include "syslib.h" + +PUBLIC int sys_getuptime(ticks) +clock_t *ticks; /* pointer to store ticks */ +{ +/* Fetch the system time. */ + message m; + int r; + + m.T_PROC_NR = NONE; + r = _taskcall(SYSTASK, SYS_TIMES, &m); + *ticks = m.T_BOOT_TICKS; + return(r); +} diff --git a/lib/syslib/sys_in.c b/lib/syslib/sys_in.c new file mode 100644 index 000000000..cfa203ad7 --- /dev/null +++ b/lib/syslib/sys_in.c @@ -0,0 +1,22 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_in * + *===========================================================================*/ +PUBLIC int sys_in(port, value, type) +int port; /* port address to read from */ +unsigned long *value; /* pointer where to store value */ +int type; /* byte, word, long */ +{ + message m_io; + int result; + + m_io.DIO_TYPE = type; + m_io.DIO_REQUEST = DIO_INPUT; + m_io.DIO_PORT = port; + + result = _taskcall(SYSTASK, SYS_DEVIO, &m_io); + *value = m_io.DIO_VALUE; + return(result); +} + diff --git a/lib/syslib/sys_irqctl.c b/lib/syslib/sys_irqctl.c new file mode 100644 index 000000000..961ab6564 --- /dev/null +++ b/lib/syslib/sys_irqctl.c @@ -0,0 +1,30 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_irqctl * + *===========================================================================*/ +PUBLIC int sys_irqctl(req, irq_vec, policy, proc_nr, port, val_ptr, mask_val) +int req; /* IRQ control request */ +int irq_vec; /* IRQ vector to control */ +int policy; /* bit mask for policy flags */ +int proc_nr; /* process number to notify */ +long port; /* port to read or write */ +void *val_ptr; /* address store value read */ +long mask_val; /* strobe mask or value to write */ +{ + message m_irq; + int s; + + m_irq.m_type = SYS_IRQCTL; + m_irq.IRQ_REQUEST = req; + m_irq.IRQ_VECTOR = irq_vec; + m_irq.IRQ_POLICY = policy; + m_irq.IRQ_PROC_NR = proc_nr; + m_irq.IRQ_PORT = port; + m_irq.IRQ_VIR_ADDR = (vir_bytes) val_ptr; + m_irq.IRQ_MASK_VAL = mask_val; + + return _taskcall(SYSTASK, SYS_IRQCTL, &m_irq); +} + + diff --git a/lib/syslib/sys_kill.c b/lib/syslib/sys_kill.c new file mode 100755 index 000000000..19f016029 --- /dev/null +++ b/lib/syslib/sys_kill.c @@ -0,0 +1,14 @@ +#include "syslib.h" + +PUBLIC int sys_kill(proc, signr) +int proc; /* which proc has exited */ +int signr; /* signal number: 1 - 16 */ +{ +/* A proc has to be signaled via MM. Tell the kernel. */ + message m; + + m.SIG_PROC = proc; + m.SIG_NUMBER = signr; + return(_taskcall(SYSTASK, SYS_KILL, &m)); +} + diff --git a/lib/syslib/sys_kmalloc.c b/lib/syslib/sys_kmalloc.c new file mode 100644 index 000000000..cd158c2d2 --- /dev/null +++ b/lib/syslib/sys_kmalloc.c @@ -0,0 +1,19 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_kmalloc * + *===========================================================================*/ +PUBLIC int sys_kmalloc(size, phys_base) +size_t size; /* size in bytes */ +phys_bytes *phys_base; /* return physical base address */ +{ + message m; + int result; + + m.MEM_CHUNK_SIZE = size; + + if (OK == (result = _taskcall(SYSTASK, SYS_KMALLOC, &m))) + *phys_base = (phys_bytes) m.MEM_CHUNK_BASE; + return(result); +} + diff --git a/lib/syslib/sys_newmap.c b/lib/syslib/sys_newmap.c new file mode 100755 index 000000000..3db37f470 --- /dev/null +++ b/lib/syslib/sys_newmap.c @@ -0,0 +1,14 @@ +#include "syslib.h" + +PUBLIC int sys_newmap(proc, ptr) +int proc; /* process whose map is to be changed */ +struct mem_map *ptr; /* pointer to new map */ +{ +/* A process has been assigned a new memory map. Tell the kernel. */ + + message m; + + m.PR_PROC_NR = proc; + m.PR_MEM_PTR = (char *) ptr; + return(_taskcall(SYSTASK, SYS_NEWMAP, &m)); +} diff --git a/lib/syslib/sys_oldsig.c b/lib/syslib/sys_oldsig.c new file mode 100755 index 000000000..844416670 --- /dev/null +++ b/lib/syslib/sys_oldsig.c @@ -0,0 +1,16 @@ +#include "syslib.h" + +PUBLIC int sys_oldsig(proc, sig, sighandler) +int proc; /* process to be signaled */ +int sig; /* signal number: 1 to _NSIG */ +sighandler_t sighandler; /* pointer to signal handler in user space */ +{ +/* A proc has to be signaled. Tell the kernel. This function is obsolete. */ + + message m; + + m.m6_i1 = proc; + m.m6_i2 = sig; + m.m6_f1 = sighandler; + return(_taskcall(SYSTASK, SYS_OLDSIG, &m)); +} diff --git a/lib/syslib/sys_out.c b/lib/syslib/sys_out.c new file mode 100644 index 000000000..0d62d5ffc --- /dev/null +++ b/lib/syslib/sys_out.c @@ -0,0 +1,20 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_out * + *===========================================================================*/ +PUBLIC int sys_out(port, value, type) +int port; /* port address to write to */ +unsigned long value; /* value to write */ +int type; /* byte, word, long */ +{ + message m_io; + + m_io.DIO_TYPE = type; + m_io.DIO_REQUEST = DIO_OUTPUT; + m_io.DIO_PORT = port; + m_io.DIO_VALUE = value; + + return _taskcall(SYSTASK, SYS_DEVIO, &m_io); +} + diff --git a/lib/syslib/sys_ph2sg.c b/lib/syslib/sys_ph2sg.c new file mode 100644 index 000000000..de0c1ee95 --- /dev/null +++ b/lib/syslib/sys_ph2sg.c @@ -0,0 +1,22 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_phys2seg * + *===========================================================================*/ +PUBLIC int sys_phys2seg(seg, off, phys, size) +u16_t *seg; /* return segment selector here */ +vir_bytes *off; /* return offset in segment here */ +phys_bytes phys; /* physical address to convert */ +vir_bytes size; /* size of segment */ +{ + message m; + int s; + m.SEG_PHYS = phys; + m.SEG_SIZE = size; + s = _taskcall(SYSTASK, SYS_PHYS2SEG, &m); + *seg = (u16_t) m.SEG_SELECT; + *off = (vir_bytes) m.SEG_OFFSET; + return s; +} + + diff --git a/lib/syslib/sys_physcp.c b/lib/syslib/sys_physcp.c new file mode 100644 index 000000000..cfa8112bf --- /dev/null +++ b/lib/syslib/sys_physcp.c @@ -0,0 +1,18 @@ +#include "syslib.h" + +PUBLIC int sys_physcopy(src_phys, dst_phys, bytes) +phys_bytes src_phys; /* source physical address */ +phys_bytes dst_phys; /* destination physical address */ +phys_bytes bytes; /* how many bytes */ +{ +/* Transfer a block of data. This uses absolute memory addresses only. Use + * sys_umap to convert a virtual address into a physical address if needed. + */ + message copy_mess; + + if (bytes == 0L) return(OK); + copy_mess.CP_SRC_ADDR = (long) src_phys; + copy_mess.CP_DST_ADDR = (long) dst_phys; + copy_mess.CP_NR_BYTES = (long) bytes; + return(_taskcall(SYSTASK, SYS_PHYSCOPY, ©_mess)); +} diff --git a/lib/syslib/sys_sdevio.c b/lib/syslib/sys_sdevio.c new file mode 100644 index 000000000..5b9cb4d6b --- /dev/null +++ b/lib/syslib/sys_sdevio.c @@ -0,0 +1,26 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_sdevio * + *===========================================================================*/ +PUBLIC int sys_sdevio(req, port, type, proc_nr, buffer, count) +int req; /* request: DIO_INPUT/ DIO_OUTPUT */ +long port; /* port address to read from */ +int type; /* byte, word, long */ +int proc_nr; /* process where buffer is */ +void *buffer; /* pointer to buffer */ +int count; /* number of elements */ +{ + message m_io; + int result; + + m_io.DIO_REQUEST = req; + m_io.DIO_TYPE = type; + m_io.DIO_PORT = port; + m_io.DIO_VEC_PROC = proc_nr; + m_io.DIO_VEC_ADDR = buffer; + m_io.DIO_VEC_SIZE = count; + + return(_taskcall(SYSTASK, SYS_SDEVIO, &m_io)); +} + diff --git a/lib/syslib/sys_sendsig.c b/lib/syslib/sys_sendsig.c new file mode 100755 index 000000000..277e18096 --- /dev/null +++ b/lib/syslib/sys_sendsig.c @@ -0,0 +1,12 @@ +#include "syslib.h" + +PUBLIC int sys_sendsig(proc, smp) +int proc; +struct sigmsg *smp; +{ + message m; + + m.m1_i1 = proc; + m.m1_p1 = (char *) smp; + return(_taskcall(SYSTASK, SYS_SENDSIG, &m)); +} diff --git a/lib/syslib/sys_sigctl.c b/lib/syslib/sys_sigctl.c new file mode 100644 index 000000000..29cd4fcec --- /dev/null +++ b/lib/syslib/sys_sigctl.c @@ -0,0 +1,29 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_sigctl * + *===========================================================================*/ +PUBLIC int sys_sigctl(request, proc_nr, sig_ctxt, flags, k_proc_nr, k_sig_map) +int request; /* control operation requested */ +int proc_nr; /* for which process */ +struct sigmsg *sig_ctxt; /* POSIX style handling */ +int flags; /* flags for POSIX handling */ +int *k_proc_nr; /* return process number here */ +sigset_t *k_sig_map; /* return signal map here */ +{ + message m; + int result; + + m.m_type = SYS_SIGCTL; + m.SIG_REQUEST = request; + m.SIG_PROC = proc_nr; + m.SIG_FLAGS = flags; + m.SIG_CTXT_PTR = (char *) sig_ctxt; + result = _taskcall(SYSTASK, SYS_SIGCTL, &m); + if (request == S_GETSIG && k_proc_nr != 0 && k_sig_map != 0) { + *k_proc_nr = m.SIG_PROC; + *k_sig_map = (sigset_t) m.SIG_MAP; + } + return(result); +} + diff --git a/lib/syslib/sys_signalrm.c b/lib/syslib/sys_signalrm.c new file mode 100644 index 000000000..8eb6e1cc5 --- /dev/null +++ b/lib/syslib/sys_signalrm.c @@ -0,0 +1,26 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_signalrm * + *===========================================================================*/ +PUBLIC int sys_signalrm(proc_nr, ticks) +int proc_nr; /* process to send SYN_ALARM message to */ +clock_t *ticks; /* how many ticks / return ticks left here */ +{ +/* Ask the clock to schedule a synchronous alarm for the caller. The process + * number can be SELF if the caller doesn't know its process number. + */ + message m; + int s; + + m.m_type= SYS_SIGNALRM; /* the alarm type requested */ + m.ALRM_PROC_NR = proc_nr; /* receiving process */ + m.ALRM_EXP_TIME = *ticks; /* the expiration time */ + m.ALRM_ABS_TIME = 0; /* ticks are relative to now */ + + s = _taskcall(SYSTASK, SYS_SIGNALRM, &m); + + *ticks = m.ALRM_TIME_LEFT; /* returned by SYSTEM task */ + return s; +} + diff --git a/lib/syslib/sys_sigret.c b/lib/syslib/sys_sigret.c new file mode 100755 index 000000000..d65551ced --- /dev/null +++ b/lib/syslib/sys_sigret.c @@ -0,0 +1,14 @@ +#include "syslib.h" + +PUBLIC int sys_sigreturn(proc, scp, flags) +int proc; +vir_bytes scp; +int flags; +{ + message m; + + m.m1_i1 = proc; + m.m1_i2 = flags; + m.m1_p1 = (char *) scp; + return(_taskcall(SYSTASK, SYS_SIGRETURN, &m)); +} diff --git a/lib/syslib/sys_svrctl.c b/lib/syslib/sys_svrctl.c new file mode 100644 index 000000000..7fa2a1d9f --- /dev/null +++ b/lib/syslib/sys_svrctl.c @@ -0,0 +1,13 @@ +#include "syslib.h" + +int sys_svrctl(int proc, int request, int priv, vir_bytes argp) +{ + message m; + + m.CTL_PROC_NR = proc; + m.CTL_REQUEST = request; + m.CTL_MM_PRIV = priv; + m.CTL_ARG_PTR = (char *) argp; + + return _taskcall(SYSTASK, SYS_SVRCTL, &m); +} diff --git a/lib/syslib/sys_syncalrm.c b/lib/syslib/sys_syncalrm.c new file mode 100644 index 000000000..161a48fdb --- /dev/null +++ b/lib/syslib/sys_syncalrm.c @@ -0,0 +1,22 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_syncalrm * + *===========================================================================*/ +PUBLIC int sys_syncalrm(proc_nr, exp_time, abs_time) +int proc_nr; /* process to send SYN_ALARM message to */ +clock_t exp_time; /* expiration time for the alarm */ +int abs_time; /* use absolute or relative expiration time */ +{ +/* Ask the SYSTEM schedule a synchronous alarm for the caller. The process + * number can be SELF if the caller doesn't know its process number. + */ + message m; + + m.m_type= SYS_SYNCALRM; /* the alarm type requested */ + m.ALRM_PROC_NR = proc_nr; /* receiving process */ + m.ALRM_EXP_TIME = exp_time; /* the expiration time */ + m.ALRM_ABS_TIME = abs_time; /* time is absolute? */ + return _taskcall(SYSTASK, SYS_SYNCALRM, &m); +} + diff --git a/lib/syslib/sys_sysctl.c b/lib/syslib/sys_sysctl.c new file mode 100755 index 000000000..6a972cf7e --- /dev/null +++ b/lib/syslib/sys_sysctl.c @@ -0,0 +1,13 @@ +#include "syslib.h" + +int sys_sysctl(int proc, int request, int priv, vir_bytes argp) +{ + message m; + + m.m2_i1 = proc; + m.m2_i2 = request; + m.m2_i3 = priv; + m.m2_p1 = (char *) argp; + + return _taskcall(SYSTASK, SYS_SYSCTL, &m); +} diff --git a/lib/syslib/sys_times.c b/lib/syslib/sys_times.c new file mode 100755 index 000000000..b23da1806 --- /dev/null +++ b/lib/syslib/sys_times.c @@ -0,0 +1,19 @@ +#include "syslib.h" + +PUBLIC int sys_times(proc, ptr) +int proc; /* proc whose times are needed */ +clock_t ptr[5]; /* pointer to time buffer */ +{ +/* Fetch the accounting info for a proc. */ + message m; + int r; + + m.T_PROC_NR = proc; + r = _taskcall(SYSTASK, SYS_TIMES, &m); + ptr[0] = m.T_USER_TIME; + ptr[1] = m.T_SYSTEM_TIME; + ptr[2] = m.T_CHILD_UTIME; + ptr[3] = m.T_CHILD_STIME; + ptr[4] = m.T_BOOT_TICKS; + return(r); +} diff --git a/lib/syslib/sys_trace.c b/lib/syslib/sys_trace.c new file mode 100755 index 000000000..31a0851fb --- /dev/null +++ b/lib/syslib/sys_trace.c @@ -0,0 +1,17 @@ +#include "syslib.h" + +PUBLIC int sys_trace(req, proc_nr, addr, data_p) +int req, proc_nr; +long addr, *data_p; +{ + message m; + int r; + + m.CTL_PROC_NR = proc_nr; + m.CTL_REQUEST = req; + m.CTL_ADDRESS = addr; + if (data_p) m.CTL_DATA = *data_p; + r = _taskcall(SYSTASK, SYS_TRACE, &m); + if (data_p) *data_p = m.CTL_DATA; + return(r); +} diff --git a/lib/syslib/sys_umap.c b/lib/syslib/sys_umap.c new file mode 100644 index 000000000..71ee4324a --- /dev/null +++ b/lib/syslib/sys_umap.c @@ -0,0 +1,25 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_umap * + *===========================================================================*/ +PUBLIC int sys_umap(proc_nr, seg, vir_addr, bytes, phys_addr) +int proc_nr; /* process number to do umap for */ +int seg; /* T, D, or S segment */ +vir_bytes vir_addr; /* address in bytes with segment*/ +vir_bytes bytes; /* number of bytes to be copied */ +phys_bytes *phys_addr; /* placeholder for result */ +{ + message m; + int result; + + m.CP_SRC_PROC_NR = proc_nr; + m.CP_SRC_SPACE = seg; + m.CP_SRC_ADDR = vir_addr; + m.CP_NR_BYTES = bytes; + + result = _taskcall(SYSTASK, SYS_UMAP, &m); + *phys_addr = m.CP_DST_ADDR; + return(result); +} + diff --git a/lib/syslib/sys_vidcp.c b/lib/syslib/sys_vidcp.c new file mode 100644 index 000000000..53356b66e --- /dev/null +++ b/lib/syslib/sys_vidcp.c @@ -0,0 +1,22 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_vidcopy * + *===========================================================================*/ +PUBLIC int sys_vidcopy(request, src_mem, src_vid, dst_vid, count) +int request; /* MEM_VID_COPY or VID_VID_COPY */ +u16_t *src_mem; /* source address in memory */ +unsigned src_vid; /* source address in video memory */ +unsigned dst_vid; /* destination address in video */ +unsigned count; /* number of words to copy */ +{ +/* Console wants to display something on the screen. */ + message m; + m.VID_REQUEST = request; + m.VID_SRC_ADDR = src_mem; + m.VID_SRC_OFFSET = src_vid; + m.VID_DST_OFFSET = dst_vid; + m.VID_CP_COUNT = count; + return(_taskcall(SYSTASK, SYS_VIDCOPY, &m)); +} + diff --git a/lib/syslib/sys_vinb.c b/lib/syslib/sys_vinb.c new file mode 100644 index 000000000..c74d79970 --- /dev/null +++ b/lib/syslib/sys_vinb.c @@ -0,0 +1,18 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_vinb * + *===========================================================================*/ +PUBLIC int sys_vinb(pvb_pairs, nr_ports) +pvb_pair_t *pvb_pairs; /* (port,byte-value)-pairs */ +int nr_ports; /* nr of pairs to be processed */ +{ + message m_io; + + m_io.DIO_TYPE = DIO_BYTE; + m_io.DIO_REQUEST = DIO_INPUT; + m_io.DIO_VEC_ADDR = (char *) pvb_pairs; + m_io.DIO_VEC_SIZE = nr_ports; + return _taskcall(SYSTASK, SYS_VDEVIO, &m_io); +} + diff --git a/lib/syslib/sys_vinl.c b/lib/syslib/sys_vinl.c new file mode 100644 index 000000000..8ff83c34c --- /dev/null +++ b/lib/syslib/sys_vinl.c @@ -0,0 +1,18 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_vinl * + *===========================================================================*/ +PUBLIC int sys_vinl(pvl_pairs, nr_ports) +pvl_pair_t *pvl_pairs; /* (port,long-value)-pairs */ +int nr_ports; /* nr of pairs to be processed */ +{ + message m_io; + + m_io.DIO_TYPE = DIO_LONG; + m_io.DIO_REQUEST = DIO_INPUT; + m_io.DIO_VEC_ADDR = (char *) pvl_pairs; + m_io.DIO_VEC_SIZE = nr_ports; + return _taskcall(SYSTASK, SYS_VDEVIO, &m_io); +} + diff --git a/lib/syslib/sys_vinw.c b/lib/syslib/sys_vinw.c new file mode 100644 index 000000000..e01c46610 --- /dev/null +++ b/lib/syslib/sys_vinw.c @@ -0,0 +1,19 @@ +#include "syslib.h" + + +/*===========================================================================* + * sys_vinw * + *===========================================================================*/ +PUBLIC int sys_vinw(pvw_pairs, nr_ports) +pvw_pair_t *pvw_pairs; /* (port,word-value)-pairs */ +int nr_ports; /* nr of pairs to be processed */ +{ + message m_io; + + m_io.DIO_TYPE = DIO_WORD; + m_io.DIO_REQUEST = DIO_INPUT; + m_io.DIO_VEC_ADDR = (char *) pvw_pairs; + m_io.DIO_VEC_SIZE = nr_ports; + return _taskcall(SYSTASK, SYS_VDEVIO, &m_io); +} + diff --git a/lib/syslib/sys_vircp.c b/lib/syslib/sys_vircp.c new file mode 100644 index 000000000..c4e95e036 --- /dev/null +++ b/lib/syslib/sys_vircp.c @@ -0,0 +1,29 @@ +#include "syslib.h" + +PUBLIC int sys_vircopy(src_proc, src_seg, src_vir, + dst_proc, dst_seg, dst_vir, bytes) +int src_proc; /* source process */ +int src_seg; /* source memory segment */ +vir_bytes src_vir; /* source virtual address */ +int dst_proc; /* destination process */ +int dst_seg; /* destination memory segment */ +vir_bytes dst_vir; /* destination virtual address */ +phys_bytes bytes; /* how many bytes */ +{ +/* Transfer a block of data. The source and destination can each either be a + * process number or SELF (to set own process number). The virtual address is + * in the data segment (D). + */ + + message copy_mess; + + if (bytes == 0L) return(OK); + copy_mess.CP_SRC_PROC_NR = src_proc; + copy_mess.CP_SRC_SPACE = src_seg; + copy_mess.CP_SRC_ADDR = (long) src_vir; + copy_mess.CP_DST_PROC_NR = dst_proc; + copy_mess.CP_DST_SPACE = dst_seg; + copy_mess.CP_DST_ADDR = (long) dst_vir; + copy_mess.CP_NR_BYTES = (long) bytes; + return(_taskcall(SYSTASK, SYS_VIRCOPY, ©_mess)); +} diff --git a/lib/syslib/sys_voutb.c b/lib/syslib/sys_voutb.c new file mode 100644 index 000000000..35d379bd9 --- /dev/null +++ b/lib/syslib/sys_voutb.c @@ -0,0 +1,18 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_voutb * + *===========================================================================*/ +PUBLIC int sys_voutb(pvb_pairs, nr_ports) +pvb_pair_t *pvb_pairs; /* (port,byte-value)-pairs */ +int nr_ports; /* nr of pairs to be processed */ +{ + message m_io; + m_io.DIO_TYPE = DIO_BYTE; + m_io.DIO_REQUEST = DIO_OUTPUT; + m_io.DIO_VEC_ADDR = (char *) pvb_pairs; + m_io.DIO_VEC_SIZE = nr_ports; + return _taskcall(SYSTASK, SYS_VDEVIO, &m_io); +} + + diff --git a/lib/syslib/sys_voutl.c b/lib/syslib/sys_voutl.c new file mode 100644 index 000000000..184223dae --- /dev/null +++ b/lib/syslib/sys_voutl.c @@ -0,0 +1,18 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_voutl * + *===========================================================================*/ +PUBLIC int sys_voutl(pvl_pairs, nr_ports) +pvl_pair_t *pvl_pairs; /* (port,long-value)-pairs */ +int nr_ports; /* nr of pairs to be processed */ +{ + message m_io; + + m_io.DIO_TYPE = DIO_LONG; + m_io.DIO_REQUEST = DIO_OUTPUT; + m_io.DIO_VEC_ADDR = (char *) pvl_pairs; + m_io.DIO_VEC_SIZE = nr_ports; + return _taskcall(SYSTASK, SYS_VDEVIO, &m_io); +} + diff --git a/lib/syslib/sys_voutw.c b/lib/syslib/sys_voutw.c new file mode 100644 index 000000000..a8d20f70e --- /dev/null +++ b/lib/syslib/sys_voutw.c @@ -0,0 +1,19 @@ +#include "syslib.h" + + +/*===========================================================================* + * sys_voutw * + *===========================================================================*/ +PUBLIC int sys_voutw(pvw_pairs, nr_ports) +pvw_pair_t *pvw_pairs; /* (port,word-value)-pairs */ +int nr_ports; /* nr of pairs to be processed */ +{ + message m_io; + + m_io.DIO_TYPE = DIO_WORD; + m_io.DIO_REQUEST = DIO_OUTPUT; + m_io.DIO_VEC_ADDR = (char *) pvw_pairs; + m_io.DIO_VEC_SIZE = nr_ports; + return _taskcall(SYSTASK, SYS_VDEVIO, &m_io); +} + diff --git a/lib/syslib/sys_xit.c b/lib/syslib/sys_xit.c new file mode 100755 index 000000000..0702686af --- /dev/null +++ b/lib/syslib/sys_xit.c @@ -0,0 +1,17 @@ +#include "syslib.h" + +/*===========================================================================* + * sys_xit * + *===========================================================================*/ +PUBLIC int sys_xit(parent, proc) +int parent; /* parent of exiting process */ +int proc; /* which process has exited */ +{ +/* A process has exited. Tell the kernel. */ + + message m; + + m.PR_PPROC_NR = parent; + m.PR_PROC_NR = proc; + return(_taskcall(SYSTASK, SYS_XIT, &m)); +} diff --git a/lib/syslib/syslib.h b/lib/syslib/syslib.h new file mode 100755 index 000000000..7b0028d73 --- /dev/null +++ b/lib/syslib/syslib.h @@ -0,0 +1,7 @@ +/* syslib.h - System library common definitions. */ + +#define _SYSTEM + +#include <lib.h> +#include <minix/com.h> +#include <minix/syslib.h> diff --git a/lib/syslib/taskcall.c b/lib/syslib/taskcall.c new file mode 100755 index 000000000..0a2024aa1 --- /dev/null +++ b/lib/syslib/taskcall.c @@ -0,0 +1,20 @@ +/* _taskcall() is the same as _syscall() except it returns negative error + * codes directly and not in errno. This is a better interface for MM and + * FS. + */ + +#include <lib.h> +#include <minix/syslib.h> + +PUBLIC int _taskcall(who, syscallnr, msgptr) +int who; +int syscallnr; +register message *msgptr; +{ + int status; + + msgptr->m_type = syscallnr; + status = _sendrec(who, msgptr); + if (status != 0) return(status); + return(msgptr->m_type); +} diff --git a/lib/timers/Makefile b/lib/timers/Makefile new file mode 100644 index 000000000..942c204fb --- /dev/null +++ b/lib/timers/Makefile @@ -0,0 +1,28 @@ +# Makefile for lib/tmrslib. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE +CC1 = $(CC) $(CFLAGS) -c + +LIBTMRS = ../libtimers.a +all: $(LIBTMRS) + +OBJECTS = \ + $(LIBTMRS)(tmrs_set.o) \ + $(LIBTMRS)(tmrs_clr.o) \ + $(LIBTMRS)(tmrs_exp.o) \ + + +$(LIBTMRS): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBTMRS)(tmrs_set.o): tmrs_set.c + $(CC1) tmrs_set.c + +$(LIBTMRS)(tmrs_clr.o): tmrs_clr.c + $(CC1) tmrs_clr.c + +$(LIBTMRS)(tmrs_exp.o): tmrs_exp.c + $(CC1) tmrs_exp.c + + diff --git a/lib/timers/timers.h b/lib/timers/timers.h new file mode 100644 index 000000000..c9a38cd05 --- /dev/null +++ b/lib/timers/timers.h @@ -0,0 +1,7 @@ +/* This library provides generic watchdog timer management functionality. + * See the comments in <timers.h> for details. + */ + +#include <timers.h> /* definitions and function prototypes */ +#define NULL (void *) 0 /* null-pointer definition */ + diff --git a/lib/timers/tmrs_clr.c b/lib/timers/tmrs_clr.c new file mode 100644 index 000000000..0a4ff29ef --- /dev/null +++ b/lib/timers/tmrs_clr.c @@ -0,0 +1,24 @@ +#include "timers.h" + +/*===========================================================================* + * tmrs_clrtimer * + *===========================================================================*/ +void tmrs_clrtimer(tmrs, tp) +timer_t **tmrs; /* pointer to timers queue */ +timer_t *tp; /* timer to be removed */ +{ +/* Deactivate a timer and remove it from the timers queue. + */ + timer_t **atp; + struct proc *p; + + tp->tmr_exp_time = TMR_NEVER; + + for (atp = tmrs; *atp != NULL; atp = &(*atp)->tmr_next) { + if (*atp == tp) { + *atp = tp->tmr_next; + return; + } + } +} + diff --git a/lib/timers/tmrs_exp.c b/lib/timers/tmrs_exp.c new file mode 100644 index 000000000..d265bf8bd --- /dev/null +++ b/lib/timers/tmrs_exp.c @@ -0,0 +1,23 @@ +#include "timers.h" + +/*===========================================================================* + * tmrs_exptimers * + *===========================================================================*/ +void tmrs_exptimers(tmrs, now) +timer_t **tmrs; /* pointer to timers queue */ +clock_t now; /* current time */ +{ +/* Use the current time to check the timers queue list for expired timers. + * Run the watchdog functions for all expired timers and deactivate them. + * The caller is responsible for scheduling a new alarm if needed. + */ + timer_t *tp; + + while ((tp = *tmrs) != NULL && tp->tmr_exp_time <= now) { + *tmrs = tp->tmr_next; + tp->tmr_exp_time = TMR_NEVER; + (*tp->tmr_func)(tp); + } +} + + diff --git a/lib/timers/tmrs_set.c b/lib/timers/tmrs_set.c new file mode 100644 index 000000000..3c3d1309e --- /dev/null +++ b/lib/timers/tmrs_set.c @@ -0,0 +1,32 @@ +#include "timers.h" + +/*===========================================================================* + * tmrs_settimer * + *===========================================================================*/ +void tmrs_settimer(tmrs, tp, exp_time, watchdog) +timer_t **tmrs; /* pointer to timers queue */ +timer_t *tp; /* the timer to be added */ +clock_t exp_time; /* its expiration time */ +tmr_func_t watchdog; /* watchdog function to be run */ +{ +/* Activate a timer to run function 'fp' at time 'exp_time'. If the timer is + * already in use it is first removed from the timers queue. Then, it is put + * in the list of active timers with the first to expire in front. + * The caller responsible for scheduling a new alarm for the timer if needed. + */ + timer_t **atp; + + /* Possibly remove an old timer. Then set the timer's variables. */ + if (tp->tmr_exp_time != TMR_NEVER) + (void) tmrs_clrtimer(tmrs,tp); + tp->tmr_exp_time = exp_time; + tp->tmr_func = watchdog; + + /* Add the timer to the active timers. The next timer due is in front. */ + for (atp = tmrs; *atp != NULL; atp = &(*atp)->tmr_next) { + if (exp_time < (*atp)->tmr_exp_time) break; + } + tp->tmr_next = *atp; + *atp = tp; +} + diff --git a/lib/utils/Makefile b/lib/utils/Makefile new file mode 100644 index 000000000..ff9dfeef4 --- /dev/null +++ b/lib/utils/Makefile @@ -0,0 +1,60 @@ +# Makefile for lib/utils. + +CFLAGS = -O -D_MINIX -D_POSIX_SOURCE +CC1 = $(CC) $(CFLAGS) -c + +LIBUTILS = ../libutils.a +all: $(LIBUTILS) + +OBJECTS = \ + $(LIBUTILS)(tick_delay.o) \ + $(LIBUTILS)(get_mon_prm.o) \ + $(LIBUTILS)(env_parse.o) \ + $(LIBUTILS)(env_panic.o) \ + $(LIBUTILS)(env_prefix.o) \ + $(LIBUTILS)(fkey_ctl.o) \ + $(LIBUTILS)(get_proc_nr.o) \ + $(LIBUTILS)(srvr_assert.o) \ + $(LIBUTILS)(srvr_panic.o) \ + $(LIBUTILS)(srvr_report.o) \ + $(LIBUTILS)(taskcall.o) \ + + +$(LIBUTILS): $(OBJECTS) + aal cr $@ *.o + rm *.o + +$(LIBUTILS)(tick_delay.o): tick_delay.c + $(CC1) tick_delay.c + +$(LIBUTILS)(get_mon_prm.o): get_mon_prm.c + $(CC1) get_mon_prm.c + +$(LIBUTILS)(env_parse.o): env_parse.c + $(CC1) env_parse.c + +$(LIBUTILS)(env_prefix.o): env_prefix.c + $(CC1) env_prefix.c + +$(LIBUTILS)(env_panic.o): env_panic.c + $(CC1) env_panic.c + +$(LIBUTILS)(fkey_ctl.o): fkey_ctl.c + $(CC1) fkey_ctl.c + +$(LIBUTILS)(get_proc_nr.o): get_proc_nr.c + $(CC1) get_proc_nr.c + +$(LIBUTILS)(srvr_assert.o): srvr_assert.c + $(CC1) srvr_assert.c + +$(LIBUTILS)(srvr_panic.o): srvr_panic.c + $(CC1) srvr_panic.c + +$(LIBUTILS)(srvr_report.o): srvr_report.c + $(CC1) srvr_report.c + +$(LIBUTILS)(taskcall.o): taskcall.c + $(CC1) taskcall.c + + diff --git a/lib/utils/env_panic.c b/lib/utils/env_panic.c new file mode 100644 index 000000000..a2df1b26b --- /dev/null +++ b/lib/utils/env_panic.c @@ -0,0 +1,19 @@ +#include "utils.h" +#include <string.h> + +/*=========================================================================* + * env_panic * + *=========================================================================*/ +PUBLIC void env_panic(key) +char *key; /* environment variable whose value is bogus */ +{ + static char value[EP_BUF_SIZE] = "<unknown>"; + int s; + if ((s=sys_getkenv(key, strlen(key), value, sizeof(value))) == 0) { + if (s != ESRCH) /* only error allowed */ + printf("WARNING: sys_getkenv() failed in env_panic(): %d\n", s); + } + printf("Bad environment setting: '%s = %s'\n", key, value); + server_panic("","", NO_NUM); +} + diff --git a/lib/utils/env_parse.c b/lib/utils/env_parse.c new file mode 100644 index 000000000..da69757e5 --- /dev/null +++ b/lib/utils/env_parse.c @@ -0,0 +1,92 @@ +#include "utils.h" +#include <stdlib.h> +#include <string.h> + + +/*=========================================================================* + * env_parse * + *=========================================================================*/ +PUBLIC int env_parse(env, fmt, field, param, min, max) +char *env; /* environment variable to inspect */ +char *fmt; /* template to parse it with */ +int field; /* field number of value to return */ +long *param; /* address of parameter to get */ +long min, max; /* minimum and maximum values for the parameter */ +{ +/* Parse an environment variable setting, something like "DPETH0=300:3". + * Panic if the parsing fails. Return EP_UNSET if the environment variable + * is not set, EP_OFF if it is set to "off", EP_ON if set to "on" or a + * field is left blank, or EP_SET if a field is given (return value through + * *param). Punctuation may be used in the environment and format string, + * fields in the environment string may be empty, and punctuation may be + * missing to skip fields. The format string contains characters 'd', 'o', + * 'x' and 'c' to indicate that 10, 8, 16, or 0 is used as the last argument + * to strtol(). A '*' means that a field should be skipped. If the format + * string contains something like "\4" then the string is repeated 4 characters + * to the left. + */ + char *val, *end; + char value[EP_BUF_SIZE]; + char PUNCT[] = ":,;."; + long newpar; + int s, i = 0, radix, r; + +#if DEAD_CODE + if ((s=sys_getkenv(env, strlen(env), value, sizeof(value))) != 0) { +#endif + if ((s=get_mon_param(env, value, sizeof(value))) != 0) { + if (s == ESRCH) return(EP_UNSET); /* only error allowed */ + printf("WARNING: sys_getkenv() failed in env_parse(): %d\n",s); + return(EP_EGETKENV); + } + val = value; + if (strcmp(val, "off") == 0) return(EP_OFF); + if (strcmp(val, "on") == 0) return(EP_ON); + + r = EP_ON; + for (;;) { + while (*val == ' ') val++; /* skip spaces */ + if (*val == 0) return(r); /* the proper exit point */ + if (*fmt == 0) break; /* too many values */ + + if (strchr(PUNCT, *val) != NULL) { + /* Time to go to the next field. */ + if (strchr(PUNCT, *fmt) != NULL) i++; + if (*fmt++ == *val) val++; + if (*fmt < 32) fmt -= *fmt; /* step back? */ + } else { + /* Environment contains a value, get it. */ + switch (*fmt) { + case '*': radix = -1; break; + case 'd': radix = 10; break; + case 'o': radix = 010; break; + case 'x': radix = 0x10; break; + case 'c': radix = 0; break; + default: goto badenv; + } + + if (radix < 0) { + /* Skip. */ + while (strchr(PUNCT, *val) == NULL) val++; + continue; + } else { + /* A number. */ + newpar = strtol(val, &end, radix); + + if (end == val) break; /* not a number */ + val = end; + } + + if (i == field) { + /* The field requested. */ + if (newpar < min || newpar > max) break; + *param = newpar; + r = EP_SET; + } + } + } +badenv: + env_panic(env); +} + + diff --git a/lib/utils/env_prefix.c b/lib/utils/env_prefix.c new file mode 100644 index 000000000..992a86cd1 --- /dev/null +++ b/lib/utils/env_prefix.c @@ -0,0 +1,29 @@ +#include "utils.h" +#include <stdlib.h> +#include <string.h> + +/*=========================================================================* + * env_prefix * + *=========================================================================*/ +PUBLIC int env_prefix(env, prefix) +char *env; /* environment variable to inspect */ +char *prefix; /* prefix to test for */ +{ +/* An environment setting may be prefixed by a word, usually "pci". + * Return TRUE if a given prefix is used. + */ + char value[EP_BUF_SIZE]; + char punct[] = ":,;."; + int s; + size_t n; + + if ((s = sys_getkenv(env, strlen(env), value, sizeof(value))) != 0) { + if (s != ESRCH) /* only error allowed */ + printf("WARNING: sys_getkenv() failed in env_prefix(): %d\n", s); + } + n = strlen(prefix); + return(value != NULL + && strncmp(value, prefix, n) == 0 + && strchr(punct, value[n]) != NULL); +} + diff --git a/lib/utils/fkey_ctl.c b/lib/utils/fkey_ctl.c new file mode 100644 index 000000000..4c7720869 --- /dev/null +++ b/lib/utils/fkey_ctl.c @@ -0,0 +1,21 @@ +#include "utils.h" + +/*===========================================================================* + * fkey_ctl * + *===========================================================================*/ +PUBLIC int fkey_ctl(fkey_code, enable_disable) +int fkey_code; /* function key code it concerns */ +int enable_disable; /* enable or disable notifications */ +{ +/* Send a message to the TTY server to request notifications for function + * key presses or to disable notifications. Enabling succeeds unless the key + * is already bound to another process. Disabling only succeeds if the key is + * bound to the current process. + */ + message m; + m.FKEY_CODE = fkey_code; + m.FKEY_ENABLE = enable_disable; + return(_taskcall(TTY, FKEY_CONTROL, &m)); +} + + diff --git a/lib/utils/get_mon_prm.c b/lib/utils/get_mon_prm.c new file mode 100644 index 000000000..93c49ba15 --- /dev/null +++ b/lib/utils/get_mon_prm.c @@ -0,0 +1,67 @@ +#include "utils.h" +#include <minix/config.h> +#include <string.h> + +FORWARD _PROTOTYPE( char *find_key, (const char *params, const char *key)); + +/*===========================================================================* + * get_mon_param * + *===========================================================================*/ +PUBLIC int get_mon_param(key, value, max_len) +char *key; /* which key to look up */ +char *value; /* where to store value */ +int max_len; /* maximum length of value */ +{ + message m; + static char mon_params[128*sizeof(char *)]; /* copy parameters here */ + char *key_value; + int s; + + if (key != NULL) { + /* Get copy of boot monitor parameters. */ + m.m_type = SYS_GETINFO; + m.I_REQUEST = GET_MONPARAMS; + m.I_PROC_NR = SELF; + m.I_VAL_LEN = sizeof(mon_params); + m.I_VAL_PTR = mon_params; + if ((s=_taskcall(SYSTASK, SYS_GETINFO, &m)) != OK) { + printf("SYS_GETINFO: %d (size %u)\n", s, sizeof(mon_params)); + return(s); + } + + /* We got a copy, now search requested key. */ + if ((key_value = find_key(mon_params, key)) == NULL) + return(ESRCH); + + /* Value found, make the actual copy (as far as possible). */ + strncpy(value, key_value, max_len); + + /* See if it fits in the client's buffer. */ + if ((strlen(key_value)+1) > max_len) return(E2BIG); + return(OK); + } + return(EINVAL); +} + + +/*==========================================================================* + * find_key * + *==========================================================================*/ +PRIVATE char *find_key(params,name) +const char *params; +const char *name; +{ + register const char *namep; + register char *envp; + + for (envp = (char *) params; *envp != 0;) { + for (namep = name; *namep != 0 && *namep == *envp; namep++, envp++) + ; + if (*namep == '\0' && *envp == '=') + return(envp + 1); + while (*envp++ != 0) + ; + } + return(NULL); +} + diff --git a/lib/utils/get_proc_nr.c b/lib/utils/get_proc_nr.c new file mode 100644 index 000000000..3d5227708 --- /dev/null +++ b/lib/utils/get_proc_nr.c @@ -0,0 +1,33 @@ +#include "utils.h" +#include <minix/config.h> +#include <timers.h> +#include "../../kernel/const.h" +#include "../../kernel/type.h" +#include "../../kernel/proc.h" + +/*===========================================================================* + * get_proc_nr * + *===========================================================================*/ +PUBLIC int get_proc_nr(proc_nr, proc_name) +int *proc_nr; /* store process number here */ +char *proc_name; /* lookup process by name */ +{ + struct proc proc; + message m; + int s; + if (proc_name != NULL) { /* lookup by name */ + + } else { /* get own process number */ + m.m_type = SYS_GETINFO; + m.I_REQUEST = GET_PROC; + m.I_PROC_NR = SELF; + m.I_KEY_LEN = SELF; + m.I_VAL_PTR = (char *) &proc; + if ((s=_taskcall(SYSTASK, SYS_GETINFO, &m)) != OK) + return(s); + *proc_nr = proc.p_nr; + } + return(OK); +} + + diff --git a/lib/utils/srvr_assert.c b/lib/utils/srvr_assert.c new file mode 100644 index 000000000..ae01de9e1 --- /dev/null +++ b/lib/utils/srvr_assert.c @@ -0,0 +1,38 @@ +#include "utils.h" + +/* This file contains two very related procedures for debugging purposes: + * server_assert_failed + * server_compare_failed + * Also see <minix/serverassert.h>. + */ + +#if !NDEBUG +/*=========================================================================* + * server_assert_failed * + *=========================================================================*/ +PUBLIC void server_assert_failed(file, line, what) +char *file; +int line; +char *what; +{ + printf("server panic at %s(%d): assertion \"%s\" failed\n", + file, line, what); + server_panic(NULL, NULL, NO_NUM); +} + +/*=========================================================================* + * server_compare_failed * + *=========================================================================*/ +PUBLIC void server_compare_failed(file, line, lhs, what, rhs) +char *file; +int line; +int lhs; +char *what; +int rhs; +{ + printf("server panic at %s(%d): compare (%d) %s (%d) failed\n", + file, line, lhs, what, rhs); + server_panic(NULL, NULL, NO_NUM); +} +#endif /* !NDEBUG */ + diff --git a/lib/utils/srvr_panic.c b/lib/utils/srvr_panic.c new file mode 100644 index 000000000..b1473687c --- /dev/null +++ b/lib/utils/srvr_panic.c @@ -0,0 +1,35 @@ +#include "utils.h" +#include <unistd.h> /* need RBT_PANIC flag */ + +PRIVATE int panicking; /* inhibits recursive panics */ + +/*===========================================================================* + * server_panic * + *===========================================================================*/ +PUBLIC void server_panic(who, mess, num) +char *who; /* server identification */ +char *mess; /* message format string */ +int num; /* number to go with format string */ +{ +/* Something awful has happened. Panics are caused when an internal + * inconsistency is detected, e.g., a programming error or illegal + * value of a defined constant. + */ + message m; + + if (panicking) return; /* already a panic */ + panicking = TRUE; /* prevent recursive panics */ + if (NULL != who && NULL != mess) { + if (num != NO_NUM) { + printf("Panic in %s: %s: %d\n", who, mess, num); + } else { + printf("Panic in %s: %s\n", who, mess); + } + } + + m.m_type = SYS_EXIT; + m.EXIT_STATUS = 1; + _taskcall(SYSTASK, SYS_EXIT, &m); + /* never reached */ +} + diff --git a/lib/utils/srvr_report.c b/lib/utils/srvr_report.c new file mode 100644 index 000000000..cfbde8261 --- /dev/null +++ b/lib/utils/srvr_report.c @@ -0,0 +1,20 @@ +#include "utils.h" + +/*===========================================================================* + * server_report * + *===========================================================================*/ +PUBLIC void server_report(who, mess, num) +char *who; /* server identification */ +char *mess; /* message format to print */ +int num; /* number to go with the message */ +{ +/* Display a message for a server. */ + + if (num != NO_NUM) { + printf("%s: %s %d\n", who, mess, num); + } else { + printf("%s: %s\n", who, mess); + } +} + + diff --git a/lib/utils/taskcall.c b/lib/utils/taskcall.c new file mode 100644 index 000000000..0a2024aa1 --- /dev/null +++ b/lib/utils/taskcall.c @@ -0,0 +1,20 @@ +/* _taskcall() is the same as _syscall() except it returns negative error + * codes directly and not in errno. This is a better interface for MM and + * FS. + */ + +#include <lib.h> +#include <minix/syslib.h> + +PUBLIC int _taskcall(who, syscallnr, msgptr) +int who; +int syscallnr; +register message *msgptr; +{ + int status; + + msgptr->m_type = syscallnr; + status = _sendrec(who, msgptr); + if (status != 0) return(status); + return(msgptr->m_type); +} diff --git a/lib/utils/tick_delay.c b/lib/utils/tick_delay.c new file mode 100644 index 000000000..985234c05 --- /dev/null +++ b/lib/utils/tick_delay.c @@ -0,0 +1,28 @@ +#include "utils.h" + +/*===========================================================================* + * tick_delay * + *===========================================================================*/ +PUBLIC int tick_delay(ticks) +long ticks; /* number of ticks to wait */ +{ + message m; + int s; + + if (ticks <= 0) return; /* check for robustness */ + + m.m_type = SYS_SYNCALRM; /* request a synchronous alarm */ + m.ALRM_PROC_NR = SELF; /* SELF means this process nr */ + m.ALRM_EXP_TIME = ticks; /* request message after ticks */ + m.ALRM_ABS_TIME = 0; /* ticks are relative to now */ + s = _taskcall(SYSTASK, SYS_SYNCALRM, &m); + + if (OK == s) receive(HARDWARE,&m); /* await synchronous alarm */ + return(s); + +} + + + + + diff --git a/lib/utils/utils.h b/lib/utils/utils.h new file mode 100644 index 000000000..14578d3bf --- /dev/null +++ b/lib/utils/utils.h @@ -0,0 +1,8 @@ +/* utils.h - System library utilities. */ + +#define _SYSTEM + +#include <lib.h> /* common to all libraries */ +#include <minix/com.h> /* need task numbers + message types */ +#include <minix/syslib.h> /* need sendrec, _taskcall, etc */ +#include <minix/utils.h> /* prototypes in this library */ diff --git a/servers/Makefile b/servers/Makefile new file mode 100644 index 000000000..904f03282 --- /dev/null +++ b/servers/Makefile @@ -0,0 +1,22 @@ +# Makefile for all system servers. +# +MAKE = exec make -$(MAKEFLAGS) + +usage: + @echo "" >&2 + @echo "Makefile for all system servers." >&2 + @echo "Usage:" >&2 + @echo " make build # Compile all system servers locally" >&2 + @echo " make clean # Remove local compiler results" >&2 + @echo " make install # Install servers to /etc/servers/" >&2 + @echo " (requires root privileges)" >&2 + @echo "" >&2 + +build: all +all install clean: + cd ./mm && $(MAKE) $@ + cd ./fs && $(MAKE) $@ + cd ./is && $(MAKE) $@ + cd ./init && $(MAKE) $@ + cd ./inet && $(MAKE) $@ + diff --git a/servers/fs/Makefile b/servers/fs/Makefile new file mode 100644 index 000000000..cde1641e6 --- /dev/null +++ b/servers/fs/Makefile @@ -0,0 +1,267 @@ +# Makefile for File System (FS) +SERVER = fs + +# directories +u = /usr +i = $u/include +s = $i/sys +h = $i/minix + +# programs, flags, etc. +CC = exec cc +CFLAGS = -I$i +LDFLAGS = -i +LIBS = -lsys -lutils + +OBJ = main.o open.o read.o write.o pipe.o dmap.o dmp.o \ + device.o path.o mount.o link.o super.o inode.o \ + cache.o cache2.o filedes.o stadir.o protect.o time.o \ + cmostime.o lock.o misc.o utility.o table.o + +# build local binary +all build: $(SERVER) +$(SERVER): $(OBJ) + $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS) + install -S 512w $@ + +# install with other servers +install: /usr/sbin/servers/$(SERVER) +/usr/sbin/servers/$(SERVER): $(SERVER) + install -o root -cs $? $@ + +# clean up local files +clean: + rm -f $(SERVER) *.o *.bak + +# dependencies +a= fs.h $h/config.h $s/types.h $h/const.h $h/type.h \ + $i/limits.h $i/errno.h $i/ansi.h $h/syslib.h \ + const.h type.h proto.h glo.h $s/dir.h + +cache.o: $a +cache.o: $h/com.h +cache.o: buf.h +cache.o: file.h +cache.o: fproc.h +cache.o: super.h + +cache2.o: $a +cache2.o: $h/com.h +cache2.o: buf.h + +device.o: $a +device.o: $i/fcntl.h +device.o: $h/callnr.h +device.o: $h/com.h +device.o: dmap.h +device.o: file.h +device.o: fproc.h +device.o: inode.h +device.o: param.h + +dmap.o: $a +dmap.o: $i/string.h +dmap.o: $h/com.h +dmap.o: $h/utils.h +dmap.o: dmap.h + +dmp.o: $a +dmp.o: dmap.h +dmp.o: $h/callnr.h +dmp.o: $h/com.h +dmp.o: $h/keymap.h + +filedes.o: $a +filedes.o: file.h +filedes.o: fproc.h +filedes.o: inode.h + +inode.o: $a +inode.o: buf.h +inode.o: file.h +inode.o: fproc.h +inode.o: inode.h +inode.o: super.h + +link.o: $a +link.o: $s/stat.h +link.o: $i/string.h +link.o: $h/com.h +link.o: $h/callnr.h +link.o: buf.h +link.o: file.h +link.o: fproc.h +link.o: inode.h +link.o: param.h +link.o: super.h + +lock.o: $a +lock.o: $h/com.h +lock.o: $i/fcntl.h +lock.o: $i/unistd.h +lock.o: file.h +lock.o: fproc.h +lock.o: inode.h +lock.o: lock.h +lock.o: param.h + +main.o: $a +main.o: $i/fcntl.h +main.o: $i/string.h +main.o: $i/stdlib.h +main.o: $h/ioctl.h +main.o: $h/utils.h +main.o: $s/ioc_memory.h +main.o: $s/svrctl.h +main.o: $h/callnr.h +main.o: $h/com.h +main.o: $h/keymap.h +main.o: buf.h +main.o: dmap.h +main.o: file.h +main.o: fproc.h +main.o: inode.h +main.o: param.h +main.o: super.h + +misc.o: $a +misc.o: $i/fcntl.h +misc.o: $i/unistd.h +misc.o: $h/callnr.h +misc.o: $h/com.h +misc.o: $s/svrctl.h +misc.o: buf.h +misc.o: file.h +misc.o: fproc.h +misc.o: inode.h +misc.o: dmap.h +misc.o: param.h +misc.o: super.h + +mount.o: $a +mount.o: $i/fcntl.h +mount.o: $h/com.h +mount.o: $s/stat.h +mount.o: buf.h +mount.o: dmap.h +mount.o: file.h +mount.o: fproc.h +mount.o: inode.h +mount.o: param.h +mount.o: super.h + +open.o: $a +open.o: $s/stat.h +open.o: $i/fcntl.h +open.o: $h/callnr.h +open.o: $h/com.h +open.o: buf.h +open.o: dmap.h +open.o: file.h +open.o: fproc.h +open.o: inode.h +open.o: lock.h +open.o: param.h +open.o: super.h + +path.o: $a +path.o: $i/string.h +path.o: $h/callnr.h +path.o: buf.h +path.o: file.h +path.o: fproc.h +path.o: inode.h +path.o: super.h + +pipe.o: $a +pipe.o: $i/fcntl.h +pipe.o: $i/signal.h +pipe.o: $h/callnr.h +pipe.o: $h/com.h +pipe.o: dmap.h +pipe.o: file.h +pipe.o: fproc.h +pipe.o: inode.h +pipe.o: param.h + +protect.o: $a +protect.o: $i/unistd.h +protect.o: $h/callnr.h +protect.o: buf.h +protect.o: file.h +protect.o: fproc.h +protect.o: inode.h +protect.o: param.h +protect.o: super.h + +read.o: $a +read.o: $i/fcntl.h +read.o: $h/com.h +read.o: buf.h +read.o: file.h +read.o: fproc.h +read.o: inode.h +read.o: param.h +read.o: super.h + +stadir.o: $a +stadir.o: $s/stat.h +stadir.o: $h/com.h +stadir.o: file.h +stadir.o: fproc.h +stadir.o: inode.h +stadir.o: param.h + +super.o: $a +super.o: $i/string.h +super.o: buf.h +super.o: inode.h +super.o: super.h + +table.o: $a +table.o: $h/callnr.h +table.o: $h/com.h +table.o: buf.h +table.o: file.h +table.o: fproc.h +table.o: inode.h +table.o: lock.h +table.o: super.h + +time.o: $a +time.o: $h/callnr.h +time.o: $h/com.h +time.o: file.h +time.o: fproc.h +time.o: inode.h +time.o: param.h + +cmostime.o: $a +cmostime.o: $i/time.h +cmostime.o: $i/ibm/cmos.h +cmostime.o: $h/callnr.h +cmostime.o: $h/com.h + +utility.o: $a +utility.o: $h/com.h +utility.o: $i/unistd.h +utility.o: buf.h +utility.o: file.h +utility.o: fproc.h +utility.o: inode.h +utility.o: param.h +utility.o: symtab.h + + +symtab.h: + sh makenmtab.sh $@ + + +write.o: $a +write.o: $i/string.h +write.o: buf.h +write.o: file.h +write.o: fproc.h +write.o: inode.h +write.o: super.h + diff --git a/servers/fs/buf.h b/servers/fs/buf.h new file mode 100644 index 000000000..9dfa3159c --- /dev/null +++ b/servers/fs/buf.h @@ -0,0 +1,69 @@ +/* Buffer (block) cache. To acquire a block, a routine calls get_block(), + * telling which block it wants. The block is then regarded as "in use" + * and has its 'b_count' field incremented. All the blocks that are not + * in use are chained together in an LRU list, with 'front' pointing + * to the least recently used block, and 'rear' to the most recently used + * block. A reverse chain, using the field b_prev is also maintained. + * Usage for LRU is measured by the time the put_block() is done. The second + * parameter to put_block() can violate the LRU order and put a block on the + * front of the list, if it will probably not be needed soon. If a block + * is modified, the modifying routine must set b_dirt to DIRTY, so the block + * will eventually be rewritten to the disk. + */ + +#include <sys/dir.h> /* need struct direct */ +#include <dirent.h> + +EXTERN struct buf { + /* Data portion of the buffer. */ + union { + char b__data[MAX_BLOCK_SIZE]; /* ordinary user data */ + struct direct b__dir[NR_DIR_ENTRIES(MAX_BLOCK_SIZE)]; /* directory block */ + zone1_t b__v1_ind[V1_INDIRECTS]; /* V1 indirect block */ + zone_t b__v2_ind[V2_INDIRECTS(MAX_BLOCK_SIZE)]; /* V2 indirect block */ + d1_inode b__v1_ino[V1_INODES_PER_BLOCK]; /* V1 inode block */ + d2_inode b__v2_ino[V2_INODES_PER_BLOCK(MAX_BLOCK_SIZE)]; /* V2 inode block */ + bitchunk_t b__bitmap[BITMAP_CHUNKS(MAX_BLOCK_SIZE)]; /* bit map block */ + } b; + + /* Header portion of the buffer. */ + struct buf *b_next; /* used to link all free bufs in a chain */ + struct buf *b_prev; /* used to link all free bufs the other way */ + struct buf *b_hash; /* used to link bufs on hash chains */ + block_t b_blocknr; /* block number of its (minor) device */ + dev_t b_dev; /* major | minor device where block resides */ + char b_dirt; /* CLEAN or DIRTY */ + char b_count; /* number of users of this buffer */ +} buf[NR_BUFS]; + +/* A block is free if b_dev == NO_DEV. */ + +#define NIL_BUF ((struct buf *) 0) /* indicates absence of a buffer */ + +/* These defs make it possible to use to bp->b_data instead of bp->b.b__data */ +#define b_data b.b__data +#define b_dir b.b__dir +#define b_v1_ind b.b__v1_ind +#define b_v2_ind b.b__v2_ind +#define b_v1_ino b.b__v1_ino +#define b_v2_ino b.b__v2_ino +#define b_bitmap b.b__bitmap + +EXTERN struct buf *buf_hash[NR_BUF_HASH]; /* the buffer hash table */ + +EXTERN struct buf *front; /* points to least recently used free block */ +EXTERN struct buf *rear; /* points to most recently used free block */ +EXTERN int bufs_in_use; /* # bufs currently in use (not on free list)*/ + +/* When a block is released, the type of usage is passed to put_block(). */ +#define WRITE_IMMED 0100 /* block should be written to disk now */ +#define ONE_SHOT 0200 /* set if block not likely to be needed soon */ + +#define INODE_BLOCK 0 /* inode block */ +#define DIRECTORY_BLOCK 1 /* directory block */ +#define INDIRECT_BLOCK 2 /* pointer block */ +#define MAP_BLOCK 3 /* bit map */ +#define FULL_DATA_BLOCK 5 /* data, fully used */ +#define PARTIAL_DATA_BLOCK 6 /* data, partly used*/ + +#define HASH_MASK (NR_BUF_HASH - 1) /* mask for hashing block numbers */ diff --git a/servers/fs/cache.c b/servers/fs/cache.c new file mode 100644 index 000000000..6f9f401b2 --- /dev/null +++ b/servers/fs/cache.c @@ -0,0 +1,435 @@ +/* The file system maintains a buffer cache to reduce the number of disk + * accesses needed. Whenever a read or write to the disk is done, a check is + * first made to see if the block is in the cache. This file manages the + * cache. + * + * The entry points into this file are: + * get_block: request to fetch a block for reading or writing from cache + * put_block: return a block previously requested with get_block + * alloc_zone: allocate a new zone (to increase the length of a file) + * free_zone: release a zone (when a file is removed) + * rw_block: read or write a block from the disk itself + * invalidate: remove all the cache blocks on some device + */ + +#include "fs.h" +#include <minix/com.h> +#include "buf.h" +#include "file.h" +#include "fproc.h" +#include "super.h" + +FORWARD _PROTOTYPE( void rm_lru, (struct buf *bp) ); + +/*===========================================================================* + * get_block * + *===========================================================================*/ +PUBLIC struct buf *get_block(dev, block, only_search) +register dev_t dev; /* on which device is the block? */ +register block_t block; /* which block is wanted? */ +int only_search; /* if NO_READ, don't read, else act normal */ +{ +/* Check to see if the requested block is in the block cache. If so, return + * a pointer to it. If not, evict some other block and fetch it (unless + * 'only_search' is 1). All the blocks in the cache that are not in use + * are linked together in a chain, with 'front' pointing to the least recently + * used block and 'rear' to the most recently used block. If 'only_search' is + * 1, the block being requested will be overwritten in its entirety, so it is + * only necessary to see if it is in the cache; if it is not, any free buffer + * will do. It is not necessary to actually read the block in from disk. + * If 'only_search' is PREFETCH, the block need not be read from the disk, + * and the device is not to be marked on the block, so callers can tell if + * the block returned is valid. + * In addition to the LRU chain, there is also a hash chain to link together + * blocks whose block numbers end with the same bit strings, for fast lookup. + */ + + int b; + register struct buf *bp, *prev_ptr; + + /* Search the hash chain for (dev, block). Do_read() can use + * get_block(NO_DEV ...) to get an unnamed block to fill with zeros when + * someone wants to read from a hole in a file, in which case this search + * is skipped + */ + if (dev != NO_DEV) { + b = (int) block & HASH_MASK; + bp = buf_hash[b]; + while (bp != NIL_BUF) { + if (bp->b_blocknr == block && bp->b_dev == dev) { + /* Block needed has been found. */ + if (bp->b_count == 0) rm_lru(bp); + bp->b_count++; /* record that block is in use */ + return(bp); + } else { + /* This block is not the one sought. */ + bp = bp->b_hash; /* move to next block on hash chain */ + } + } + } + + /* Desired block is not on available chain. Take oldest block ('front'). */ + if ((bp = front) == NIL_BUF) panic("all buffers in use", NR_BUFS); + rm_lru(bp); + + /* Remove the block that was just taken from its hash chain. */ + b = (int) bp->b_blocknr & HASH_MASK; + prev_ptr = buf_hash[b]; + if (prev_ptr == bp) { + buf_hash[b] = bp->b_hash; + } else { + /* The block just taken is not on the front of its hash chain. */ + while (prev_ptr->b_hash != NIL_BUF) + if (prev_ptr->b_hash == bp) { + prev_ptr->b_hash = bp->b_hash; /* found it */ + break; + } else { + prev_ptr = prev_ptr->b_hash; /* keep looking */ + } + } + + /* If the block taken is dirty, make it clean by writing it to the disk. + * Avoid hysteresis by flushing all other dirty blocks for the same device. + */ + if (bp->b_dev != NO_DEV) { + if (bp->b_dirt == DIRTY) flushall(bp->b_dev); +#if ENABLE_CACHE2 + put_block2(bp); +#endif + } + + /* Fill in block's parameters and add it to the hash chain where it goes. */ + bp->b_dev = dev; /* fill in device number */ + bp->b_blocknr = block; /* fill in block number */ + bp->b_count++; /* record that block is being used */ + b = (int) bp->b_blocknr & HASH_MASK; + bp->b_hash = buf_hash[b]; + buf_hash[b] = bp; /* add to hash list */ + + /* Go get the requested block unless searching or prefetching. */ + if (dev != NO_DEV) { +#if ENABLE_CACHE2 + if (get_block2(bp, only_search)) /* in 2nd level cache */; + else +#endif + if (only_search == PREFETCH) bp->b_dev = NO_DEV; + else + if (only_search == NORMAL) rw_block(bp, READING); + } + return(bp); /* return the newly acquired block */ +} + + +/*===========================================================================* + * put_block * + *===========================================================================*/ +PUBLIC void put_block(bp, block_type) +register struct buf *bp; /* pointer to the buffer to be released */ +int block_type; /* INODE_BLOCK, DIRECTORY_BLOCK, or whatever */ +{ +/* Return a block to the list of available blocks. Depending on 'block_type' + * it may be put on the front or rear of the LRU chain. Blocks that are + * expected to be needed again shortly (e.g., partially full data blocks) + * go on the rear; blocks that are unlikely to be needed again shortly + * (e.g., full data blocks) go on the front. Blocks whose loss can hurt + * the integrity of the file system (e.g., inode blocks) are written to + * disk immediately if they are dirty. + */ + if (bp == NIL_BUF) return; /* it is easier to check here than in caller */ + + bp->b_count--; /* there is one use fewer now */ + if (bp->b_count != 0) return; /* block is still in use */ + + bufs_in_use--; /* one fewer block buffers in use */ + + /* Put this block back on the LRU chain. If the ONE_SHOT bit is set in + * 'block_type', the block is not likely to be needed again shortly, so put + * it on the front of the LRU chain where it will be the first one to be + * taken when a free buffer is needed later. + */ + if (block_type & ONE_SHOT) { + /* Block probably won't be needed quickly. Put it on front of chain. + * It will be the next block to be evicted from the cache. + */ + bp->b_prev = NIL_BUF; + bp->b_next = front; + if (front == NIL_BUF) + rear = bp; /* LRU chain was empty */ + else + front->b_prev = bp; + front = bp; + } else { + /* Block probably will be needed quickly. Put it on rear of chain. + * It will not be evicted from the cache for a long time. + */ + bp->b_prev = rear; + bp->b_next = NIL_BUF; + if (rear == NIL_BUF) + front = bp; + else + rear->b_next = bp; + rear = bp; + } + + /* Some blocks are so important (e.g., inodes, indirect blocks) that they + * should be written to the disk immediately to avoid messing up the file + * system in the event of a crash. + */ + if ((block_type & WRITE_IMMED) && bp->b_dirt==DIRTY && bp->b_dev != NO_DEV) + rw_block(bp, WRITING); +} + + +/*===========================================================================* + * alloc_zone * + *===========================================================================*/ +PUBLIC zone_t alloc_zone(dev, z) +dev_t dev; /* device where zone wanted */ +zone_t z; /* try to allocate new zone near this one */ +{ +/* Allocate a new zone on the indicated device and return its number. */ + + int major, minor; + bit_t b, bit; + struct super_block *sp; + + /* Note that the routine alloc_bit() returns 1 for the lowest possible + * zone, which corresponds to sp->s_firstdatazone. To convert a value + * between the bit number, 'b', used by alloc_bit() and the zone number, 'z', + * stored in the inode, use the formula: + * z = b + sp->s_firstdatazone - 1 + * Alloc_bit() never returns 0, since this is used for NO_BIT (failure). + */ + sp = get_super(dev); + + /* If z is 0, skip initial part of the map known to be fully in use. */ + if (z == sp->s_firstdatazone) { + bit = sp->s_zsearch; + } else { + bit = (bit_t) z - (sp->s_firstdatazone - 1); + } + b = alloc_bit(sp, ZMAP, bit); + if (b == NO_BIT) { + err_code = ENOSPC; + major = (int) (sp->s_dev >> MAJOR) & BYTE; + minor = (int) (sp->s_dev >> MINOR) & BYTE; + printf("No space on %sdevice %d/%d\n", + sp->s_dev == root_dev ? "root " : "", major, minor); + return(NO_ZONE); + } + if (z == sp->s_firstdatazone) sp->s_zsearch = b; /* for next time */ + return(sp->s_firstdatazone - 1 + (zone_t) b); +} + + +/*===========================================================================* + * free_zone * + *===========================================================================*/ +PUBLIC void free_zone(dev, numb) +dev_t dev; /* device where zone located */ +zone_t numb; /* zone to be returned */ +{ +/* Return a zone. */ + + register struct super_block *sp; + bit_t bit; + + /* Locate the appropriate super_block and return bit. */ + sp = get_super(dev); + if (numb < sp->s_firstdatazone || numb >= sp->s_zones) return; + bit = (bit_t) (numb - (sp->s_firstdatazone - 1)); + free_bit(sp, ZMAP, bit); + if (bit < sp->s_zsearch) sp->s_zsearch = bit; +} + + +/*===========================================================================* + * rw_block * + *===========================================================================*/ +PUBLIC void rw_block(bp, rw_flag) +register struct buf *bp; /* buffer pointer */ +int rw_flag; /* READING or WRITING */ +{ +/* Read or write a disk block. This is the only routine in which actual disk + * I/O is invoked. If an error occurs, a message is printed here, but the error + * is not reported to the caller. If the error occurred while purging a block + * from the cache, it is not clear what the caller could do about it anyway. + */ + + int r, op; + off_t pos; + dev_t dev; + int block_size; + + block_size = get_block_size(bp->b_dev); + + if ( (dev = bp->b_dev) != NO_DEV) { + pos = (off_t) bp->b_blocknr * block_size; + op = (rw_flag == READING ? DEV_READ : DEV_WRITE); + r = dev_io(op, dev, FS_PROC_NR, bp->b_data, pos, block_size, 0); + if (r != block_size) { + if (r >= 0) r = END_OF_FILE; + if (r != END_OF_FILE) + printf("Unrecoverable disk error on device %d/%d, block %ld\n", + (dev>>MAJOR)&BYTE, (dev>>MINOR)&BYTE, bp->b_blocknr); + bp->b_dev = NO_DEV; /* invalidate block */ + + /* Report read errors to interested parties. */ + if (rw_flag == READING) rdwt_err = r; + } + } + + bp->b_dirt = CLEAN; +} + + +/*===========================================================================* + * invalidate * + *===========================================================================*/ +PUBLIC void invalidate(device) +dev_t device; /* device whose blocks are to be purged */ +{ +/* Remove all the blocks belonging to some device from the cache. */ + + register struct buf *bp; + + for (bp = &buf[0]; bp < &buf[NR_BUFS]; bp++) + if (bp->b_dev == device) bp->b_dev = NO_DEV; + +#if ENABLE_CACHE2 + invalidate2(device); +#endif +} + + +/*==========================================================================* + * flushall * + *==========================================================================*/ +PUBLIC void flushall(dev) +dev_t dev; /* device to flush */ +{ +/* Flush all dirty blocks for one device. */ + + register struct buf *bp; + static struct buf *dirty[NR_BUFS]; /* static so it isn't on stack */ + int ndirty; + + for (bp = &buf[0], ndirty = 0; bp < &buf[NR_BUFS]; bp++) + if (bp->b_dirt == DIRTY && bp->b_dev == dev) dirty[ndirty++] = bp; + rw_scattered(dev, dirty, ndirty, WRITING); +} + + +/*===========================================================================* + * rw_scattered * + *===========================================================================*/ +PUBLIC void rw_scattered(dev, bufq, bufqsize, rw_flag) +dev_t dev; /* major-minor device number */ +struct buf **bufq; /* pointer to array of buffers */ +int bufqsize; /* number of buffers */ +int rw_flag; /* READING or WRITING */ +{ +/* Read or write scattered data from a device. */ + + register struct buf *bp; + int gap; + register int i; + register iovec_t *iop; + static iovec_t iovec[NR_IOREQS]; /* static so it isn't on stack */ + int j, r; + int block_size; + + block_size = get_block_size(dev); + + /* (Shell) sort buffers on b_blocknr. */ + gap = 1; + do + gap = 3 * gap + 1; + while (gap <= bufqsize); + while (gap != 1) { + gap /= 3; + for (j = gap; j < bufqsize; j++) { + for (i = j - gap; + i >= 0 && bufq[i]->b_blocknr > bufq[i + gap]->b_blocknr; + i -= gap) { + bp = bufq[i]; + bufq[i] = bufq[i + gap]; + bufq[i + gap] = bp; + } + } + } + + /* Set up I/O vector and do I/O. The result of dev_io is OK if everything + * went fine, otherwise the error code for the first failed transfer. + */ + while (bufqsize > 0) { + for (j = 0, iop = iovec; j < NR_IOREQS && j < bufqsize; j++, iop++) { + bp = bufq[j]; + if (bp->b_blocknr != bufq[0]->b_blocknr + j) break; + iop->iov_addr = (vir_bytes) bp->b_data; + iop->iov_size = block_size; + } + r = dev_io(rw_flag == WRITING ? DEV_SCATTER : DEV_GATHER, + dev, FS_PROC_NR, iovec, + (off_t) bufq[0]->b_blocknr * block_size, j, 0); + + /* Harvest the results. Dev_io reports the first error it may have + * encountered, but we only care if it's the first block that failed. + */ + for (i = 0, iop = iovec; i < j; i++, iop++) { + bp = bufq[i]; + if (iop->iov_size != 0) { + /* Transfer failed. An error? Do we care? */ + if (r != OK && i == 0) { + printf( + "fs: I/O error on device %d/%d, block %lu\n", + (dev>>MAJOR)&BYTE, (dev>>MINOR)&BYTE, + bp->b_blocknr); + bp->b_dev = NO_DEV; /* invalidate block */ + } + break; + } + if (rw_flag == READING) { + bp->b_dev = dev; /* validate block */ + put_block(bp, PARTIAL_DATA_BLOCK); + } else { + bp->b_dirt = CLEAN; + } + } + bufq += i; + bufqsize -= i; + if (rw_flag == READING) { + /* Don't bother reading more than the device is willing to + * give at this time. Don't forget to release those extras. + */ + while (bufqsize > 0) { + put_block(*bufq++, PARTIAL_DATA_BLOCK); + bufqsize--; + } + } + } +} + + +/*===========================================================================* + * rm_lru * + *===========================================================================*/ +PRIVATE void rm_lru(bp) +struct buf *bp; +{ +/* Remove a block from its LRU chain. */ + struct buf *next_ptr, *prev_ptr; + + bufs_in_use++; + next_ptr = bp->b_next; /* successor on LRU chain */ + prev_ptr = bp->b_prev; /* predecessor on LRU chain */ + if (prev_ptr != NIL_BUF) + prev_ptr->b_next = next_ptr; + else + front = next_ptr; /* this block was at front of chain */ + + if (next_ptr != NIL_BUF) + next_ptr->b_prev = prev_ptr; + else + rear = prev_ptr; /* this block was at rear of chain */ +} diff --git a/servers/fs/cache2.c b/servers/fs/cache2.c new file mode 100644 index 000000000..65387cc8f --- /dev/null +++ b/servers/fs/cache2.c @@ -0,0 +1,130 @@ +/* Second level block cache to supplement the file system cache. The block + * cache of a 16-bit Minix system is very small, too small to prevent trashing. + * A generic 32-bit system also doesn't have a very large cache to allow it + * to run on systems with little memory. On a system with lots of memory one + * can use the RAM disk as a read-only second level cache. Any blocks pushed + * out of the primary cache are cached on the RAM disk. This code manages the + * second level cache. The cache is a simple FIFO where old blocks are put + * into and drop out at the other end. Must be searched backwards. + * + * The entry points into this file are: + * init_cache2: initialize the second level cache + * get_block2: get a block from the 2nd level cache + * put_block2: store a block in the 2nd level cache + * invalidate2: remove all the cache blocks on some device + */ + +#include "fs.h" +#include <minix/com.h> +#include "buf.h" + +#if ENABLE_CACHE2 + +#define MAX_BUF2 (256 * sizeof(char *)) + +PRIVATE struct buf2 { /* 2nd level cache per block administration */ + block_t b2_blocknr; /* block number */ + dev_t b2_dev; /* device number */ + u16_t b2_count; /* count of in-cache block groups */ +} buf2[MAX_BUF2]; + +PRIVATE unsigned nr_buf2; /* actual cache size */ +PRIVATE unsigned buf2_idx; /* round-robin reuse index */ + +#define hash2(block) ((unsigned) ((block) & (MAX_BUF2 - 1))) + + +/*===========================================================================* + * init_cache2 * + *===========================================================================*/ +PUBLIC void init_cache2(size) +unsigned long size; +{ +/* Initialize the second level disk buffer cache of 'size' blocks. */ + + nr_buf2 = size > MAX_BUF2 ? MAX_BUF2 : (unsigned) size; +} + + +/*===========================================================================* + * get_block2 * + *===========================================================================*/ +PUBLIC int get_block2(bp, only_search) +struct buf *bp; /* buffer to get from the 2nd level cache */ +int only_search; /* if NO_READ, do nothing, else act normal */ +{ +/* Fill a buffer from the 2nd level cache. Return true iff block acquired. */ + unsigned b; + struct buf2 *bp2; + + /* If the block wanted is in the RAM disk then our game is over. */ + if (bp->b_dev == DEV_RAM) nr_buf2 = 0; + + /* Cache enabled? NO_READ? Any blocks with the same hash key? */ + if (nr_buf2 == 0 || only_search == NO_READ + || buf2[hash2(bp->b_blocknr)].b2_count == 0) return(0); + + /* Search backwards (there may be older versions). */ + b = buf2_idx; + for (;;) { + if (b == 0) b = nr_buf2; + bp2 = &buf2[--b]; + if (bp2->b2_blocknr == bp->b_blocknr && bp2->b2_dev == bp->b_dev) break; + if (b == buf2_idx) return(0); + } + + /* Block is in the cache, get it. */ + if (dev_io(DEV_READ, DEV_RAM, FS_PROC_NR, bp->b_data, + (off_t) b * BLOCK_SIZE, BLOCK_SIZE, 0) == BLOCK_SIZE) { + return(1); + } + return(0); +} + + +/*===========================================================================* + * put_block2 * + *===========================================================================*/ +PUBLIC void put_block2(bp) +struct buf *bp; /* buffer to store in the 2nd level cache */ +{ +/* Store a buffer into the 2nd level cache. */ + unsigned b; + struct buf2 *bp2; + + if (nr_buf2 == 0) return; /* no 2nd level cache */ + + b = buf2_idx++; + if (buf2_idx == nr_buf2) buf2_idx = 0; + + bp2 = &buf2[b]; + + if (dev_io(DEV_WRITE, DEV_RAM, FS_PROC_NR, bp->b_data, + (off_t) b * BLOCK_SIZE, BLOCK_SIZE, 0) == BLOCK_SIZE) { + if (bp2->b2_dev != NO_DEV) buf2[hash2(bp2->b2_blocknr)].b2_count--; + bp2->b2_dev = bp->b_dev; + bp2->b2_blocknr = bp->b_blocknr; + buf2[hash2(bp2->b2_blocknr)].b2_count++; + } +} + + +/*===========================================================================* + * invalidate2 * + *===========================================================================*/ +PUBLIC void invalidate2(device) +dev_t device; +{ +/* Invalidate all blocks from a given device in the 2nd level cache. */ + unsigned b; + struct buf2 *bp2; + + for (b = 0; b < nr_buf2; b++) { + bp2 = &buf2[b]; + if (bp2->b2_dev == device) { + bp2->b2_dev = NO_DEV; + buf2[hash2(bp2->b2_blocknr)].b2_count--; + } + } +} +#endif /* ENABLE_CACHE2 */ diff --git a/servers/fs/cmostime.c b/servers/fs/cmostime.c new file mode 100644 index 000000000..e63f653a2 --- /dev/null +++ b/servers/fs/cmostime.c @@ -0,0 +1,161 @@ +#include "fs.h" +#include <minix/com.h> +#include <minix/callnr.h> +#include <time.h> +#include <ibm/cmos.h> +#include <ibm/bios.h> + + +/* Manufacturers usually use the ID value of the IBM model they emulate. + * However some manufacturers, notably HP and COMPAQ, have had different + * ideas in the past. + * + * Machine ID byte information source: + * _The Programmer's PC Sourcebook_ by Thom Hogan, + * published by Microsoft Press + */ + +FORWARD _PROTOTYPE( int read_register, (int register_address)); +FORWARD _PROTOTYPE( int get_cmostime, (struct tm *tmp, int y2kflag)); +FORWARD _PROTOTYPE( int dec_to_bcd, (int dec)); +FORWARD _PROTOTYPE( int bcd_to_dec, (int bcd)); + + + +PUBLIC int do_cmostime(void) +{ + unsigned char mach_id, cmos_state; + struct tm time1; + int i, s; + int y2kflag = m_in.REQUEST; + vir_bytes dst_time = (vir_bytes) m_in.ADDRESS; + + /* First obtain the machine ID to see if we can read the CMOS clock. Only + * for PS_386 and PC_AT this is possible. Otherwise, return an error. + */ + sys_vircopy(SELF, BIOS_SEG, (vir_bytes) ADR_MACHINE_ID, + SELF, D, (vir_bytes) &mach_id, LEN_MACHINE_ID); + if (mach_id != PS_386_MACHINE && mach_id != PC_AT_MACHINE) { + printf("IS: Machine ID unknown. ID byte = %02x.\n", mach_id); + return(EFAULT); + } + + /* Now check the CMOS' state to see if we can read a proper time from it. + * If the state is crappy, return an error. + */ + cmos_state = read_register(CMOS_STATUS); + if (cmos_state & (CS_LOST_POWER | CS_BAD_CHKSUM | CS_BAD_TIME)) { + printf( "IS: CMOS RAM error(s) found. State = 0x%02x\n", cmos_state ); + if (cmos_state & CS_LOST_POWER) + printf("IS: RTC lost power. Reset CMOS RAM with SETUP." ); + if (cmos_state & CS_BAD_CHKSUM) + printf("IS: CMOS RAM checksum is bad. Run SETUP." ); + if (cmos_state & CS_BAD_TIME) + printf("IS: Time invalid in CMOS RAM. Reset clock." ); + return(EFAULT); + } + + /* Everything seems to be OK. Read the CMOS real time clock and copy the + * result back to the caller. + */ + if (get_cmostime(&time1, y2kflag) != 0) + return(EFAULT); + sys_datacopy(SELF, (vir_bytes) &time1, + who, dst_time, sizeof(struct tm)); + + return(OK); +} + + +PRIVATE int get_cmostime(struct tm *t, int y2kflag) +{ +/* Update the structure pointed to by time with the current time as read + * from CMOS RAM of the RTC. If necessary, the time is converted into a + * binary format before being stored in the structure. + */ + int osec, n; + unsigned long i; + static int timeout_flag; + + /* Start a timer to keep us from getting stuck on a dead clock. */ + timeout_flag = 0; + sys_flagalrm(5*HZ, &timeout_flag); + do { + osec = -1; + n = 0; + do { + if (timeout_flag) { + printf("readclock: CMOS clock appears dead\n"); + return(1); + } + + /* Clock update in progress? */ + if (read_register(RTC_REG_A) & RTC_A_UIP) continue; + + t->tm_sec = read_register(RTC_SEC); + if (t->tm_sec != osec) { + /* Seconds changed. First from -1, then because the + * clock ticked, which is what we're waiting for to + * get a precise reading. + */ + osec = t->tm_sec; + n++; + } + } while (n < 2); + + /* Read the other registers. */ + t->tm_min = read_register(RTC_MIN); + t->tm_hour = read_register(RTC_HOUR); + t->tm_mday = read_register(RTC_MDAY); + t->tm_mon = read_register(RTC_MONTH); + t->tm_year = read_register(RTC_YEAR); + + /* Time stable? */ + } while (read_register(RTC_SEC) != t->tm_sec + || read_register(RTC_MIN) != t->tm_min + || read_register(RTC_HOUR) != t->tm_hour + || read_register(RTC_MDAY) != t->tm_mday + || read_register(RTC_MONTH) != t->tm_mon + || read_register(RTC_YEAR) != t->tm_year); + sys_flagalrm(0, &timeout_flag); /* not strictly necessarily; flag is static */ + + if ((read_register(RTC_REG_B) & RTC_B_DM_BCD) == 0) { + /* Convert BCD to binary (default RTC mode). */ + t->tm_year = bcd_to_dec(t->tm_year); + t->tm_mon = bcd_to_dec(t->tm_mon); + t->tm_mday = bcd_to_dec(t->tm_mday); + t->tm_hour = bcd_to_dec(t->tm_hour); + t->tm_min = bcd_to_dec(t->tm_min); + t->tm_sec = bcd_to_dec(t->tm_sec); + } + t->tm_mon--; /* Counts from 0. */ + + /* Correct the year, good until 2080. */ + if (t->tm_year < 80) t->tm_year += 100; + + if (y2kflag) { + /* Clock with Y2K bug, interpret 1980 as 2000, good until 2020. */ + if (t->tm_year < 100) t->tm_year += 20; + } + return 0; +} + +PRIVATE int read_register(int reg_addr) +{ +/* Read a single CMOS register value. */ + int r = 0; + sys_outb(RTC_INDEX, reg_addr); + sys_inb(RTC_IO, &r); + return r; +} + +PRIVATE int bcd_to_dec(int n) +{ + return ((n >> 4) & 0x0F) * 10 + (n & 0x0F); +} + +PRIVATE int dec_to_bcd(int n) +{ + return ((n / 10) << 4) | (n % 10); +} + diff --git a/servers/fs/const.h b/servers/fs/const.h new file mode 100644 index 000000000..a7f73bb6d --- /dev/null +++ b/servers/fs/const.h @@ -0,0 +1,84 @@ +/* Tables sizes */ +#define V1_NR_DZONES 7 /* # direct zone numbers in a V1 inode */ +#define V1_NR_TZONES 9 /* total # zone numbers in a V1 inode */ +#define V2_NR_DZONES 7 /* # direct zone numbers in a V2 inode */ +#define V2_NR_TZONES 10 /* total # zone numbers in a V2 inode */ + +#define NR_FILPS 128 /* # slots in filp table */ +#define NR_INODES 64 /* # slots in "in core" inode table */ +#define NR_SUPERS 8 /* # slots in super block table */ +#define NR_LOCKS 8 /* # slots in the file locking table */ + +/* The type of sizeof may be (unsigned) long. Use the following macro for + * taking the sizes of small objects so that there are no surprises like + * (small) long constants being passed to routines expecting an int. + */ +#define usizeof(t) ((unsigned) sizeof(t)) + +/* File system types. */ +#define SUPER_MAGIC 0x137F /* magic number contained in super-block */ +#define SUPER_REV 0x7F13 /* magic # when 68000 disk read on PC or vv */ +#define SUPER_V2 0x2468 /* magic # for V2 file systems */ +#define SUPER_V2_REV 0x6824 /* V2 magic written on PC, read on 68K or vv */ +#define SUPER_V3 0x4d5a /* magic # for V3 file systems */ + +#define V1 1 /* version number of V1 file systems */ +#define V2 2 /* version number of V2 file systems */ +#define V3 3 /* version number of V3 file systems */ + +/* Miscellaneous constants */ +#define SU_UID ((uid_t) 0) /* super_user's uid_t */ +#define SYS_UID ((uid_t) 0) /* uid_t for processes MM and INIT */ +#define SYS_GID ((gid_t) 0) /* gid_t for processes MM and INIT */ +#define NORMAL 0 /* forces get_block to do disk read */ +#define NO_READ 1 /* prevents get_block from doing disk read */ +#define PREFETCH 2 /* tells get_block not to read or mark dev */ + +#define XPIPE (-NR_TASKS-1) /* used in fp_task when susp'd on pipe */ +#define XLOCK (-NR_TASKS-2) /* used in fp_task when susp'd on lock */ +#define XPOPEN (-NR_TASKS-3) /* used in fp_task when susp'd on pipe open */ + +#define NO_BIT ((bit_t) 0) /* returned by alloc_bit() to signal failure */ + +#define DUP_MASK 0100 /* mask to distinguish dup2 from dup */ + +#define LOOK_UP 0 /* tells search_dir to lookup string */ +#define ENTER 1 /* tells search_dir to make dir entry */ +#define DELETE 2 /* tells search_dir to delete entry */ +#define IS_EMPTY 3 /* tells search_dir to ret. OK or ENOTEMPTY */ + +#define CLEAN 0 /* disk and memory copies identical */ +#define DIRTY 1 /* disk and memory copies differ */ +#define ATIME 002 /* set if atime field needs updating */ +#define CTIME 004 /* set if ctime field needs updating */ +#define MTIME 010 /* set if mtime field needs updating */ + +#define BYTE_SWAP 0 /* tells conv2/conv4 to swap bytes */ +#define DONT_SWAP 1 /* tells conv2/conv4 not to swap bytes */ + +#define END_OF_FILE (-104) /* eof detected */ + +#define ROOT_INODE 1 /* inode number for root directory */ +#define BOOT_BLOCK ((block_t) 0) /* block number of boot block */ +#define SUPER_BLOCK_BYTES (1024) /* bytes offset */ +#define START_BLOCK 2 /* first block of FS (not counting SB) */ + +#define DIR_ENTRY_SIZE usizeof (struct direct) /* # bytes/dir entry */ +#define NR_DIR_ENTRIES(b) ((b)/DIR_ENTRY_SIZE) /* # dir entries/blk */ +#define SUPER_SIZE usizeof (struct super_block) /* super_block size */ +#define PIPE_SIZE(b) (V1_NR_DZONES*(b)) /* pipe size in bytes */ +#define BITMAP_CHUNKS(b) ((b)/usizeof (bitchunk_t))/* # map chunks/blk */ +#define BITCHUNK_BITS (usizeof(bitchunk_t) * CHAR_BIT) +#define BITS_PER_BLOCK(b) (BITMAP_CHUNKS(b) * BITCHUNK_BITS) + +/* Derived sizes pertaining to the V1 file system. */ +#define V1_ZONE_NUM_SIZE usizeof (zone1_t) /* # bytes in V1 zone */ +#define V1_INODE_SIZE usizeof (d1_inode) /* bytes in V1 dsk ino */ +#define V1_INDIRECTS (STATIC_BLOCK_SIZE/V1_ZONE_NUM_SIZE) /* # zones/indir block */ +#define V1_INODES_PER_BLOCK (STATIC_BLOCK_SIZE/V1_INODE_SIZE)/* # V1 dsk inodes/blk */ + +/* Derived sizes pertaining to the V2 file system. */ +#define V2_ZONE_NUM_SIZE usizeof (zone_t) /* # bytes in V2 zone */ +#define V2_INODE_SIZE usizeof (d2_inode) /* bytes in V2 dsk ino */ +#define V2_INDIRECTS(b) ((b)/V2_ZONE_NUM_SIZE) /* # zones/indir block */ +#define V2_INODES_PER_BLOCK(b) ((b)/V2_INODE_SIZE)/* # V2 dsk inodes/blk */ diff --git a/servers/fs/device.c b/servers/fs/device.c new file mode 100644 index 000000000..92a591e7d --- /dev/null +++ b/servers/fs/device.c @@ -0,0 +1,431 @@ +/* When a needed block is not in the cache, it must be fetched from the disk. + * Special character files also require I/O. The routines for these are here. + * + * The entry points in this file are: + * dev_open: FS opens a device + * dev_close: FS closes a device + * dev_io: FS does a read or write on a device + * gen_opcl: generic call to a task to perform an open/close + * gen_io: generic call to a task to perform an I/O operation + * no_dev: open/close processing for devices that don't exist + * tty_opcl: perform tty-specific processing for open/close + * ctty_opcl: perform controlling-tty-specific processing for open/close + * ctty_io: perform controlling-tty-specific processing for I/O + * do_ioctl: perform the IOCTL system call + * do_setsid: perform the SETSID system call (FS side) + */ + +#include "fs.h" +#include <fcntl.h> +#include <minix/callnr.h> +#include <minix/com.h> +#include "dmap.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "param.h" + + +/*===========================================================================* + * dev_open * + *===========================================================================*/ +PUBLIC int dev_open(dev, proc, flags) +dev_t dev; /* device to open */ +int proc; /* process to open for */ +int flags; /* mode bits and flags */ +{ + int major, r; + struct dmap *dp; + + /* Determine the major device number call the device class specific + * open/close routine. (This is the only routine that must check the + * device number for being in range. All others can trust this check.) + */ + major = (dev >> MAJOR) & BYTE; + if (major >= max_major) major = 0; + dp = &dmap[major]; + r = (*dp->dmap_opcl)(DEV_OPEN, dev, proc, flags); + if (r == SUSPEND) panic("Suspend on open from", dp->dmap_driver); + return(r); +} + + +/*===========================================================================* + * dev_close * + *===========================================================================*/ +PUBLIC void dev_close(dev) +dev_t dev; /* device to close */ +{ + (void) (*dmap[(dev >> MAJOR) & BYTE].dmap_opcl)(DEV_CLOSE, dev, 0, 0); +} + + +/*===========================================================================* + * dev_io * + *===========================================================================*/ +PUBLIC int dev_io(op, dev, proc, buf, pos, bytes, flags) +int op; /* DEV_READ, DEV_WRITE, DEV_IOCTL, etc. */ +dev_t dev; /* major-minor device number */ +int proc; /* in whose address space is buf? */ +void *buf; /* virtual address of the buffer */ +off_t pos; /* byte position */ +int bytes; /* how many bytes to transfer */ +int flags; /* special flags, like O_NONBLOCK */ +{ +/* Read or write from a device. The parameter 'dev' tells which one. */ + struct dmap *dp; + message dev_mess; + + /* Determine task dmap. */ + dp = &dmap[(dev >> MAJOR) & BYTE]; + + /* Set up the message passed to task. */ + dev_mess.m_type = op; + dev_mess.DEVICE = (dev >> MINOR) & BYTE; + dev_mess.POSITION = pos; + dev_mess.PROC_NR = proc; + dev_mess.ADDRESS = buf; + dev_mess.COUNT = bytes; + dev_mess.TTY_FLAGS = flags; + + /* Call the task. */ + (*dp->dmap_io)(dp->dmap_driver, &dev_mess); + + /* Task has completed. See if call completed. */ + if (dev_mess.REP_STATUS == SUSPEND) { + if (flags & O_NONBLOCK) { + /* Not supposed to block. */ + dev_mess.m_type = CANCEL; + dev_mess.PROC_NR = proc; + dev_mess.DEVICE = (dev >> MINOR) & BYTE; + (*dp->dmap_io)(dp->dmap_driver, &dev_mess); + if (dev_mess.REP_STATUS == EINTR) dev_mess.REP_STATUS = EAGAIN; + } else { + /* Suspend user. */ + suspend(dp->dmap_driver); + return(SUSPEND); + } + } + return(dev_mess.REP_STATUS); +} + + +/*===========================================================================* + * gen_opcl * + *===========================================================================*/ +PUBLIC int gen_opcl(op, dev, proc, flags) +int op; /* operation, DEV_OPEN or DEV_CLOSE */ +dev_t dev; /* device to open or close */ +int proc; /* process to open/close for */ +int flags; /* mode bits and flags */ +{ +/* Called from the dmap struct in table.c on opens & closes of special files.*/ + struct dmap *dp; + message dev_mess; + + /* Determine task dmap. */ + dp = &dmap[(dev >> MAJOR) & BYTE]; + + dev_mess.m_type = op; + dev_mess.DEVICE = (dev >> MINOR) & BYTE; + dev_mess.PROC_NR = proc; + dev_mess.COUNT = flags; + + /* Call the task. */ + (*dp->dmap_io)(dp->dmap_driver, &dev_mess); + + return(dev_mess.REP_STATUS); +} + + +/*===========================================================================* + * tty_opcl * + *===========================================================================*/ +PUBLIC int tty_opcl(op, dev, proc, flags) +int op; /* operation, DEV_OPEN or DEV_CLOSE */ +dev_t dev; /* device to open or close */ +int proc; /* process to open/close for */ +int flags; /* mode bits and flags */ +{ +/* This procedure is called from the dmap struct on tty open/close. */ + + int r; + register struct fproc *rfp; + + /* Add O_NOCTTY to the flags if this process is not a session leader, or + * if it already has a controlling tty, or if it is someone elses + * controlling tty. + */ + if (!fp->fp_sesldr || fp->fp_tty != 0) { + flags |= O_NOCTTY; + } else { + for (rfp = &fproc[LOW_USER]; rfp < &fproc[NR_PROCS]; rfp++) { +#if FUTURE_CODE /* check for all processes later */ + for (rfp = &fproc[0]; rfp < &fproc[NR_PROCS]; rfp++) { +#endif + if (rfp->fp_tty == dev) flags |= O_NOCTTY; + } + } + + r = gen_opcl(op, dev, proc, flags); + + /* Did this call make the tty the controlling tty? */ + if (r == 1) { + fp->fp_tty = dev; + r = OK; + } + return(r); +} + + +/*===========================================================================* + * ctty_opcl * + *===========================================================================*/ +PUBLIC int ctty_opcl(op, dev, proc, flags) +int op; /* operation, DEV_OPEN or DEV_CLOSE */ +dev_t dev; /* device to open or close */ +int proc; /* process to open/close for */ +int flags; /* mode bits and flags */ +{ +/* This procedure is called from the dmap struct in table.c on opening/closing + * /dev/tty, the magic device that translates to the controlling tty. + */ + + return(fp->fp_tty == 0 ? ENXIO : OK); +} + + +/*===========================================================================* + * do_setsid * + *===========================================================================*/ +PUBLIC int do_setsid() +{ +/* Perform the FS side of the SETSID call, i.e. get rid of the controlling + * terminal of a process, and make the process a session leader. + */ + register struct fproc *rfp; + + /* Only MM may do the SETSID call directly. */ + if (who != MM_PROC_NR) return(ENOSYS); + + /* Make the process a session leader with no controlling tty. */ + rfp = &fproc[m_in.slot1]; + rfp->fp_sesldr = TRUE; + rfp->fp_tty = 0; +} + + +/*===========================================================================* + * do_ioctl * + *===========================================================================*/ +PUBLIC int do_ioctl() +{ +/* Perform the ioctl(ls_fd, request, argx) system call (uses m2 fmt). */ + + struct filp *f; + register struct inode *rip; + dev_t dev; + + if ( (f = get_filp(m_in.ls_fd)) == NIL_FILP) return(err_code); + rip = f->filp_ino; /* get inode pointer */ + if ( (rip->i_mode & I_TYPE) != I_CHAR_SPECIAL + && (rip->i_mode & I_TYPE) != I_BLOCK_SPECIAL) return(ENOTTY); + dev = (dev_t) rip->i_zone[0]; + +#if ENABLE_BINCOMPAT + if ((m_in.TTY_REQUEST >> 8) == 't') { + /* Obsolete sgtty ioctl, message contains more than is sane. */ + struct dmap *dp; + message dev_mess; + + dp = &dmap[(dev >> MAJOR) & BYTE]; + + dev_mess = m; /* Copy full message with all the weird bits. */ + dev_mess.m_type = DEV_IOCTL; + dev_mess.PROC_NR = who; + dev_mess.TTY_LINE = (dev >> MINOR) & BYTE; + + /* Call the task. */ + (*dp->dmap_io)(dp->dmap_driver, &dev_mess); + + m_out.TTY_SPEK = dev_mess.TTY_SPEK; /* erase and kill */ + m_out.TTY_FLAGS = dev_mess.TTY_FLAGS; /* flags */ + return(dev_mess.REP_STATUS); + } +#endif + + return(dev_io(DEV_IOCTL, dev, who, m_in.ADDRESS, 0L, + m_in.REQUEST, f->filp_flags)); +} + + +/*===========================================================================* + * gen_io * + *===========================================================================*/ +PUBLIC void gen_io(task_nr, mess_ptr) +int task_nr; /* which task to call */ +message *mess_ptr; /* pointer to message for task */ +{ +/* All file system I/O ultimately comes down to I/O on major/minor device + * pairs. These lead to calls on the following routines via the dmap table. + */ + + int r, proc_nr; + message local_m; + + proc_nr = mess_ptr->PROC_NR; + if (! isokprocnr(proc_nr)) { + printf("FS: warning, got illegal process number from %d.\n", + mess_ptr->PROC_NR); + return; + } + + while ((r = sendrec(task_nr, mess_ptr)) == ELOCKED) { + /* sendrec() failed to avoid deadlock. The task 'task_nr' is + * trying to send a REVIVE message for an earlier request. + * Handle it and go try again. + */ + if ((r = receive(task_nr, &local_m)) != OK) break; + + /* If we're trying to send a cancel message to a task which has just + * sent a completion reply, ignore the reply and abort the cancel + * request. The caller will do the revive for the process. + */ + if (mess_ptr->m_type == CANCEL && local_m.REP_PROC_NR == proc_nr) + return; + + /* Otherwise it should be a REVIVE. */ + if (local_m.m_type != REVIVE) { + printf( + "fs: strange device reply from %d, type = %d, proc = %d\n", + local_m.m_source, + local_m.m_type, local_m.REP_PROC_NR); + continue; + } + + revive(local_m.REP_PROC_NR, local_m.REP_STATUS); + } + + /* The message received may be a reply to this call, or a REVIVE for some + * other process. + */ + for (;;) { + if (r != OK) { + if (r == EDEADDST) return; /* give up */ + else panic("call_task: can't send/receive", r); + } + + /* Did the process we did the sendrec() for get a result? */ + if (mess_ptr->REP_PROC_NR == proc_nr) break; + + /* Otherwise it should be a REVIVE. */ + if (mess_ptr->m_type != REVIVE) { + printf( + "fs: strange device reply from %d, type = %d, proc = %d\n", + mess_ptr->m_source, + mess_ptr->m_type, mess_ptr->REP_PROC_NR); + continue; + } + revive(mess_ptr->REP_PROC_NR, mess_ptr->REP_STATUS); + + r = receive(task_nr, mess_ptr); + } +} + + +/*===========================================================================* + * ctty_io * + *===========================================================================*/ +PUBLIC void ctty_io(task_nr, mess_ptr) +int task_nr; /* not used - for compatibility with dmap_t */ +message *mess_ptr; /* pointer to message for task */ +{ +/* This routine is only called for one device, namely /dev/tty. Its job + * is to change the message to use the controlling terminal, instead of the + * major/minor pair for /dev/tty itself. + */ + + struct dmap *dp; + + if (fp->fp_tty == 0) { + /* No controlling tty present anymore, return an I/O error. */ + mess_ptr->REP_STATUS = EIO; + } else { + /* Substitute the controlling terminal device. */ + dp = &dmap[(fp->fp_tty >> MAJOR) & BYTE]; + mess_ptr->DEVICE = (fp->fp_tty >> MINOR) & BYTE; + (*dp->dmap_io)(dp->dmap_driver, mess_ptr); + } +} + + +/*===========================================================================* + * no_dev * + *===========================================================================*/ +PUBLIC int no_dev(op, dev, proc, flags) +int op; /* operation, DEV_OPEN or DEV_CLOSE */ +dev_t dev; /* device to open or close */ +int proc; /* process to open/close for */ +int flags; /* mode bits and flags */ +{ +/* Called when opening a nonexistent device. */ + + return(ENODEV); +} + + +/*===========================================================================* + * clone_opcl * + *===========================================================================*/ +PUBLIC int clone_opcl(op, dev, proc, flags) +int op; /* operation, DEV_OPEN or DEV_CLOSE */ +dev_t dev; /* device to open or close */ +int proc; /* process to open/close for */ +int flags; /* mode bits and flags */ +{ +/* Some devices need special processing upon open. Such a device is "cloned", + * i.e. on a succesful open it is replaced by a new device with a new unique + * minor device number. This new device number identifies a new object (such + * as a new network connection) that has been allocated within a task. + */ + struct dmap *dp; + int minor; + message dev_mess; + + /* Determine task dmap. */ + dp = &dmap[(dev >> MAJOR) & BYTE]; + minor = (dev >> MINOR) & BYTE; + + dev_mess.m_type = op; + dev_mess.DEVICE = minor; + dev_mess.PROC_NR = proc; + dev_mess.COUNT = flags; + + /* Call the task. */ + (*dp->dmap_io)(dp->dmap_driver, &dev_mess); + + if (op == DEV_OPEN && dev_mess.REP_STATUS >= 0) { + if (dev_mess.REP_STATUS != minor) { + /* A new minor device number has been returned. Create a + * temporary device file to hold it. + */ + struct inode *ip; + + /* Device number of the new device. */ + dev = (dev & ~(BYTE << MINOR)) | (dev_mess.REP_STATUS << MINOR); + + ip = alloc_inode(root_dev, ALL_MODES | I_CHAR_SPECIAL); + if (ip == NIL_INODE) { + /* Oops, that didn't work. Undo open. */ + (void) clone_opcl(DEV_CLOSE, dev, proc, 0); + return(err_code); + } + ip->i_zone[0] = dev; + + put_inode(fp->fp_filp[m_in.fd]->filp_ino); + fp->fp_filp[m_in.fd]->filp_ino = ip; + } + dev_mess.REP_STATUS = OK; + } + return(dev_mess.REP_STATUS); +} diff --git a/servers/fs/dmap.c b/servers/fs/dmap.c new file mode 100644 index 000000000..c0c227c95 --- /dev/null +++ b/servers/fs/dmap.c @@ -0,0 +1,162 @@ +/* This file contains the table with device <-> driver mappings. It also + * contains some routines to dynamically add and/ or remove device drivers + * or change mappings. + */ + +#include "fs.h" +#include "fproc.h" +#include "dmap.h" +#include <string.h> +#include <minix/com.h> +#include <minix/utils.h> + +/* Some devices may or may not be there in the next table. */ +#define DT(enable, opcl, io, driver, flags) \ + { (enable?(opcl):no_dev), (enable?(io):0), \ + (enable?(driver):0), (flags) }, + +/* The order of the entries here determines the mapping between major device + * numbers and tasks. The first entry (major device 0) is not used. The + * next entry is major device 1, etc. Character and block devices can be + * intermixed at random. The ordering determines the device numbers in /dev/. + * Note that FS knows the device number of /dev/ram/ to load the RAM disk. + * Also note that the major device numbers used in /dev/ are NOT the same as + * the process numbers of the device drivers. + */ +/* + Driver enabled Open/Cls I/O Driver # Flags Device File + -------------- -------- ------ ----------- ----- ------ ---- + */ +struct dmap dmap[] = { + DT(1, no_dev, 0, 0, 0) /* 0 = not used */ + DT(1, gen_opcl, gen_io, MEMORY, 0) /* 1 = /dev/mem */ + DT(ENABLE_FLOPPY, gen_opcl, gen_io, FLOPPY, 0) /* 2 = /dev/fd0 */ + DT(NR_CTRLRS >= 1, gen_opcl, gen_io, CTRLR(0), DMAP_MUTABLE) /* 3 = /dev/c0 */ +#if ENABLE_USER_TTY + DT(1, tty_opcl, gen_io, TERMINAL, 0) /* 4 = /dev/tty00 */ + DT(1, ctty_opcl,ctty_io,TERMINAL, 0) /* 5 = /dev/tty */ +#else + DT(1, tty_opcl, gen_io, TTY, 0) /* 4 = /dev/tty00 */ + DT(1, ctty_opcl,ctty_io,TTY, 0) /* 5 = /dev/tty */ +#endif + DT(ENABLE_PRINTER, gen_opcl, gen_io, PRINTER, 0) /* 6 = /dev/lp */ + +#if (MACHINE == IBM_PC) + DT(1, no_dev, 0, 0, DMAP_MUTABLE) /* 7 = /dev/ip */ + DT(NR_CTRLRS >= 2, gen_opcl, gen_io, CTRLR(1), DMAP_MUTABLE) /* 8 = /dev/c1 */ + DT(0, 0, 0, 0, DMAP_MUTABLE) /* 9 = not used */ + DT(NR_CTRLRS >= 3, gen_opcl, gen_io, CTRLR(2), DMAP_MUTABLE) /*10 = /dev/c2 */ + DT(0, 0, 0, 0, DMAP_MUTABLE) /*11 = not used */ + DT(NR_CTRLRS >= 4, gen_opcl, gen_io, CTRLR(3), DMAP_MUTABLE) /*12 = /dev/c3 */ + DT(ENABLE_SB16, gen_opcl, gen_io, NONE, 0) /*13 = /dev/audio */ + DT(ENABLE_SB16, gen_opcl, gen_io, NONE, 0) /*14 = /dev/mixer */ +#endif /* IBM_PC */ +}; + +/* This is the maximum major number at compile time. This may change when + * devices are dynamically added or removed. + */ +int max_major = sizeof(dmap)/sizeof(struct dmap); + + +/*===========================================================================* + * map_driver * + *===========================================================================*/ +PUBLIC int map_driver(major, proc_nr, dev_style) +int major; /* major number of the device */ +int proc_nr; /* process number of the driver */ +int dev_style; /* style of the device */ +{ +/* Set a new device driver mapping in the dmap table. Given that correct + * arguments are given, this only works if the entry is mutable and the + * current driver is not busy. + * Normal error codes are returned so that this function can be used from + * a system call that tries to dynamically install a new driver. + */ + struct dmap *dp; + + /* Get pointer to device entry in the dmap table. */ + if (major >= max_major) /* verify bounds */ + return(ENODEV); + dp = &dmap[major]; + + /* See if updating the entry is allowed. */ + if (! (dp->dmap_flags & DMAP_MUTABLE)) + return(EPERM); + if (dp->dmap_flags & DMAP_BUSY) + return(EBUSY); + + /* Check process number of new driver. */ + if (! isokprocnr(proc_nr)) + return(EINVAL); + + /* Try to update the entry. */ + switch (dev_style) { + case STYLE_DEV: dp->dmap_opcl = gen_opcl; break; + case STYLE_TTY: dp->dmap_opcl = tty_opcl; break; + case STYLE_CLONE: dp->dmap_opcl = clone_opcl; break; + default: return(EINVAL); + } + dp->dmap_io = gen_io; + dp->dmap_driver = proc_nr; + return(OK); +} + +/*===========================================================================* + * map_controllers * + *===========================================================================*/ +PUBLIC void map_controllers() +{ +/* Map drivers to controllers and update the dmap table to that selection. + * For each controller, the environment variable set by the boot monitor is + * analyzed to see what type of Winchester disk is attached. Then, the name + * of the driver that handles the device is looked up in the local drivertab. + * Finally, the process number of the driver is looked up, and, if found, is + * installed in the dmap table. + */ + static char ctrlr_nr[] = "c0"; /* controller currently analyzed */ + char ctrlr_type[8]; /* type of Winchester disk */ + int i, c, s; + int proc_nr=0; /* process number of driver */ + struct drivertab *dp; + struct drivertab { + char wini_type[8]; + char proc_name[8]; + } drivertab[] = { + { "at", "AT_WINI" }, /* AT Winchester */ + { "bios", "..." }, + { "esdi", "..." }, + { "xt", "..." }, + { "aha1540", "..." }, + { "dosfile", "..." }, + { "fatfile", "..." }, + }; + + for (c=0; c < NR_CTRLRS; c++) { + ctrlr_nr[1] = '0' + c; + if ((s = get_mon_param(ctrlr_nr, ctrlr_type, 8)) != OK) { + if (s != ESRCH) + printf("FS: warning, couldn't get monitor param: %d\n", s); + continue; + } + for (dp = drivertab; + dp < drivertab + sizeof(drivertab)/sizeof(drivertab[0]); dp++) { + if (strcmp(ctrlr_type, dp->wini_type) == 0) { /* found driver name */ + if ((s=sys_getprocnr(&proc_nr, /* lookup proc nr */ + dp->proc_name, strlen(dp->proc_name)+1)) == OK) { + for (i=0; i< max_major; i++) { /* find mapping */ + if (dmap[i].dmap_driver == CTRLR(c)) { + if (map_driver(i, proc_nr, STYLE_DEV) == OK) { + printf("FS: controller %s (%s) mapped to %s driver (proc. nr %d)\n", + ctrlr_nr, dp->wini_type, dp->proc_name, dmap[i].dmap_driver); + } + } + } + } + } + } + } +} + + + diff --git a/servers/fs/dmap.h b/servers/fs/dmap.h new file mode 100644 index 000000000..2f82fe87a --- /dev/null +++ b/servers/fs/dmap.h @@ -0,0 +1,25 @@ +#ifndef _DMAP_H +#define _DMAP_H + +#include <minix/config.h> +#include <minix/ipc.h> + +/* Device table. This table is indexed by major device number. It provides + * the link between major device numbers and the routines that process them. + * The table can be update dynamically. The field 'dmap_flags' describe an + * entry's current status and determines what control options are possible. + */ +#define DMAP_MUTABLE 0x01 /* mapping can be overtaken */ +#define DMAP_BUSY 0x02 /* driver busy with request */ + +enum dev_style { STYLE_DEV, STYLE_NDEV, STYLE_TTY, STYLE_CLONE }; + +extern struct dmap { + int _PROTOTYPE ((*dmap_opcl), (int, Dev_t, int, int) ); + void _PROTOTYPE ((*dmap_io), (int, message *) ); + int dmap_driver; + int dmap_flags; +} dmap[]; + + +#endif /* _DMAP_H */ diff --git a/servers/fs/dmp.c b/servers/fs/dmp.c new file mode 100644 index 000000000..2268eb5f7 --- /dev/null +++ b/servers/fs/dmp.c @@ -0,0 +1,100 @@ +/* This file contains procedures to dump to FS' data structures. + * + * The entry points into this file are + * do_fkey_pressed: a function key was pressed + * dtab_dump: display device<->driver table + * fproc_dump: display FS process table + * + * Created: + * Oct 01, 2004: by Jorrit N. Herder + */ + +#include "fs.h" +#include <minix/callnr.h> +#include <minix/com.h> +#include <minix/keymap.h> +#include "dmap.h" +#include "fproc.h" + +FORWARD _PROTOTYPE( void dtab_dmp, (void)); +FORWARD _PROTOTYPE( void fproc_dmp, (void)); + + +/*===========================================================================* + * do_fkey_pressed * + *===========================================================================*/ +PUBLIC int do_fkey_pressed(void) +{ + printf("Debug dump of FS data structure: "); + switch (m_in.FKEY_CODE) { + case SF5: fproc_dmp(); break; + case SF6: dtab_dmp(); break; + + default: + printf("FS: unhandled notification for Shift+F%d key.\n", + m_in.FKEY_NUM); + } +} + + +/*===========================================================================* + * fproc_dmp * + *===========================================================================*/ +PRIVATE void fproc_dmp() +{ + struct fproc *fp; + int i, n=0; + static int prev_i; + printf("PROCESS TABLE (beta)\n"); + + printf("-pid- -tty- -umask- --uid-- --gid-- -ldr- -sus-rev-proc- -cloexec-\n"); + for (i=prev_i; i<NR_PROCS; i++) { + fp = &fproc[i]; + if (fp->fp_pid <= 0) continue; + if (++n > 22) break; + printf("%4d %2d/%d 0x%05x %2d (%d) %2d (%d) %3d %3d %3d %4d 0x%05x\n", + fp->fp_pid, + ((fp->fp_tty>>MAJOR)&BYTE), ((fp->fp_tty>>MINOR)&BYTE), + fp->fp_umask, + fp->fp_realuid, fp->fp_effuid, fp->fp_realgid, fp->fp_effgid, + fp->fp_sesldr, + fp->fp_suspended, fp->fp_revived, fp->fp_task, + fp->fp_cloexec + ); + } + if (i >= NR_PROCS) i = 0; + else printf("--more--\r"); + prev_i = i; +} + + +/*===========================================================================* + * dtab_dmp * + *===========================================================================*/ +PRIVATE void dtab_dmp() +{ + int i; + char *file[] = { + "not used ", "/dev/mem ", "/dev/fd0 ", "/dev/c0 ", "/dev/tty0 ", + "/dev/tty ", "/dev/lp ", "/dev/ip ", "/dev/c1 ", "not used ", + "/dev/c2 ", "not used ", "/dev/c3 ", "/dev/audio", "/dev/mixer", + }; + printf("DEVICE MAP\n"); + printf("Dev File Open/Cls I/O Proc\n"); + printf("--- ---------- -------- ------ ----\n"); + for (i=0; i<max_major; i++) { + printf("%3d %s ", i, file[i] ); + + if (dmap[i].dmap_opcl == no_dev) printf(" no_dev"); + else if (dmap[i].dmap_opcl == gen_opcl) printf("gen_opcl"); + else printf("%8x", dmap[i].dmap_opcl); + + if ((void *)dmap[i].dmap_io == (void *)no_dev) printf(" no_dev"); + else if (dmap[i].dmap_io == gen_io) printf(" gen_io"); + else printf("%8x", dmap[i].dmap_io); + + printf("%6d\n", dmap[i].dmap_driver); + } +} + + diff --git a/servers/fs/file.h b/servers/fs/file.h new file mode 100644 index 000000000..04ea3d41b --- /dev/null +++ b/servers/fs/file.h @@ -0,0 +1,15 @@ +/* This is the filp table. It is an intermediary between file descriptors and + * inodes. A slot is free if filp_count == 0. + */ + +EXTERN struct filp { + mode_t filp_mode; /* RW bits, telling how file is opened */ + int filp_flags; /* flags from open and fcntl */ + int filp_count; /* how many file descriptors share this slot?*/ + struct inode *filp_ino; /* pointer to the inode */ + off_t filp_pos; /* file position */ +} filp[NR_FILPS]; + +#define FILP_CLOSED 0 /* filp_mode: associated device closed */ + +#define NIL_FILP (struct filp *) 0 /* indicates absence of a filp slot */ diff --git a/servers/fs/filedes.c b/servers/fs/filedes.c new file mode 100644 index 000000000..b0dce3d64 --- /dev/null +++ b/servers/fs/filedes.c @@ -0,0 +1,99 @@ +/* This file contains the procedures that manipulate file descriptors. + * + * The entry points into this file are + * get_fd: look for free file descriptor and free filp slots + * get_filp: look up the filp entry for a given file descriptor + * find_filp: find a filp slot that points to a given inode + */ + +#include "fs.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" + +/*===========================================================================* + * get_fd * + *===========================================================================*/ +PUBLIC int get_fd(start, bits, k, fpt) +int start; /* start of search (used for F_DUPFD) */ +mode_t bits; /* mode of the file to be created (RWX bits) */ +int *k; /* place to return file descriptor */ +struct filp **fpt; /* place to return filp slot */ +{ +/* Look for a free file descriptor and a free filp slot. Fill in the mode word + * in the latter, but don't claim either one yet, since the open() or creat() + * may yet fail. + */ + + register struct filp *f; + register int i; + + *k = -1; /* we need a way to tell if file desc found */ + + /* Search the fproc fp_filp table for a free file descriptor. */ + for (i = start; i < OPEN_MAX; i++) { + if (fp->fp_filp[i] == NIL_FILP) { + /* A file descriptor has been located. */ + *k = i; + break; + } + } + + /* Check to see if a file descriptor has been found. */ + if (*k < 0) return(EMFILE); /* this is why we initialized k to -1 */ + + /* Now that a file descriptor has been found, look for a free filp slot. */ + for (f = &filp[0]; f < &filp[NR_FILPS]; f++) { + if (f->filp_count == 0) { + f->filp_mode = bits; + f->filp_pos = 0L; + f->filp_flags = 0; + *fpt = f; + return(OK); + } + } + + /* If control passes here, the filp table must be full. Report that back. */ + return(ENFILE); +} + + +/*===========================================================================* + * get_filp * + *===========================================================================*/ +PUBLIC struct filp *get_filp(fild) +int fild; /* file descriptor */ +{ +/* See if 'fild' refers to a valid file descr. If so, return its filp ptr. */ + + err_code = EBADF; + if (fild < 0 || fild >= OPEN_MAX ) return(NIL_FILP); + return(fp->fp_filp[fild]); /* may also be NIL_FILP */ +} + + +/*===========================================================================* + * find_filp * + *===========================================================================*/ +PUBLIC struct filp *find_filp(rip, bits) +register struct inode *rip; /* inode referred to by the filp to be found */ +Mode_t bits; /* mode of the filp to be found (RWX bits) */ +{ +/* Find a filp slot that refers to the inode 'rip' in a way as described + * by the mode bit 'bits'. Used for determining whether somebody is still + * interested in either end of a pipe. Also used when opening a FIFO to + * find partners to share a filp field with (to shared the file position). + * Like 'get_fd' it performs its job by linear search through the filp table. + */ + + register struct filp *f; + + for (f = &filp[0]; f < &filp[NR_FILPS]; f++) { + if (f->filp_count != 0 && f->filp_ino == rip && (f->filp_mode & bits)){ + return(f); + } + } + + /* If control passes here, the filp wasn't there. Report that back. */ + return(NIL_FILP); +} diff --git a/servers/fs/fproc.h b/servers/fs/fproc.h new file mode 100644 index 000000000..6610245ce --- /dev/null +++ b/servers/fs/fproc.h @@ -0,0 +1,39 @@ +/* This is the per-process information. A slot is reserved for each potential + * process. Thus NR_PROCS must be the same as in the kernel. It is not + * possible or even necessary to tell when a slot is free here. + */ + + +EXTERN struct fproc { + mode_t fp_umask; /* mask set by umask system call */ + struct inode *fp_workdir; /* pointer to working directory's inode */ + struct inode *fp_rootdir; /* pointer to current root dir (see chroot) */ + struct filp *fp_filp[OPEN_MAX];/* the file descriptor table */ + uid_t fp_realuid; /* real user id */ + uid_t fp_effuid; /* effective user id */ + gid_t fp_realgid; /* real group id */ + gid_t fp_effgid; /* effective group id */ + dev_t fp_tty; /* major/minor of controlling tty */ + int fp_fd; /* place to save fd if rd/wr can't finish */ + char *fp_buffer; /* place to save buffer if rd/wr can't finish*/ + int fp_nbytes; /* place to save bytes if rd/wr can't finish */ + int fp_cum_io_partial; /* partial byte count if rd/wr can't finish */ + char fp_suspended; /* set to indicate process hanging */ + char fp_revived; /* set to indicate process being revived */ + char fp_task; /* which task is proc suspended on */ + char fp_sesldr; /* true if proc is a session leader */ + pid_t fp_pid; /* process id */ + long fp_cloexec; /* bit map for POSIX Table 6-2 FD_CLOEXEC */ +} fproc[NR_PROCS]; + +/* Field values. */ +#define NOT_SUSPENDED 0 /* process is not suspended on pipe or task */ +#define SUSPENDED 1 /* process is suspended on pipe or task */ +#define NOT_REVIVING 0 /* process is not being revived */ +#define REVIVING 1 /* process is being revived from suspension */ +#define PID_FREE 0 /* process slot free */ +#define PID_SERVER (-1) /* process has become a server */ + +/* Check is process number is acceptable - includes system processes. */ +#define isokprocnr(n) ((unsigned)((n)+NR_TASKS) < NR_PROCS + NR_TASKS) + diff --git a/servers/fs/fs.h b/servers/fs/fs.h new file mode 100644 index 000000000..3912d32c7 --- /dev/null +++ b/servers/fs/fs.h @@ -0,0 +1,23 @@ +/* This is the master header for fs. It includes some other files + * and defines the principal constants. + */ +#define _POSIX_SOURCE 1 /* tell headers to include POSIX stuff */ +#define _MINIX 1 /* tell headers to include MINIX stuff */ +#define _SYSTEM 1 /* tell headers that this is the kernel */ + +/* The following are so basic, all the *.c files get them automatically. */ +#include <minix/config.h> /* MUST be first */ +#include <ansi.h> /* MUST be second */ +#include <sys/types.h> +#include <minix/const.h> +#include <minix/type.h> + +#include <limits.h> +#include <errno.h> + +#include <minix/syslib.h> + +#include "const.h" +#include "type.h" +#include "proto.h" +#include "glo.h" diff --git a/servers/fs/glo.h b/servers/fs/glo.h new file mode 100644 index 000000000..de49bab1f --- /dev/null +++ b/servers/fs/glo.h @@ -0,0 +1,33 @@ +/* EXTERN should be extern except for the table file */ +#ifdef _TABLE +#undef EXTERN +#define EXTERN +#endif + +/* File System global variables */ +EXTERN struct fproc *fp; /* pointer to caller's fproc struct */ +EXTERN int super_user; /* 1 if caller is super_user, else 0 */ +EXTERN int susp_count; /* number of procs suspended on pipe */ +EXTERN int nr_locks; /* number of locks currently in place */ +EXTERN int reviving; /* number of pipe processes to be revived */ +EXTERN off_t rdahedpos; /* position to read ahead */ +EXTERN struct inode *rdahed_inode; /* pointer to inode to read ahead */ +EXTERN Dev_t root_dev; /* device number of the root device */ +EXTERN time_t boottime; /* time in seconds at system boot */ + +/* The parameters of the call are kept here. */ +EXTERN message m_in; /* the input message itself */ +EXTERN message m_out; /* the output message used for reply */ +EXTERN int who; /* caller's proc number */ +EXTERN int call_nr; /* system call number */ +EXTERN char user_path[PATH_MAX];/* storage for user path name */ + +/* The following variables are used for returning results to the caller. */ +EXTERN int err_code; /* temporary storage for error number */ +EXTERN int rdwt_err; /* status of last disk i/o request */ + +/* Data initialized elsewhere. */ +extern _PROTOTYPE (int (*call_vec[]), (void) ); /* sys call table */ +extern int max_major; /* maximum major device (+ 1) */ +extern char dot1[2]; /* dot1 (&dot1[0]) and dot2 (&dot2[0]) have a special */ +extern char dot2[3]; /* meaning to search_dir: no access permission check. */ diff --git a/servers/fs/inode.c b/servers/fs/inode.c new file mode 100644 index 000000000..80caa1b66 --- /dev/null +++ b/servers/fs/inode.c @@ -0,0 +1,373 @@ +/* This file manages the inode table. There are procedures to allocate and + * deallocate inodes, acquire, erase, and release them, and read and write + * them from the disk. + * + * The entry points into this file are + * get_inode: search inode table for a given inode; if not there, read it + * put_inode: indicate that an inode is no longer needed in memory + * alloc_inode: allocate a new, unused inode + * wipe_inode: erase some fields of a newly allocated inode + * free_inode: mark an inode as available for a new file + * update_times: update atime, ctime, and mtime + * rw_inode: read a disk block and extract an inode, or corresp. write + * old_icopy: copy to/from in-core inode struct and disk inode (V1.x) + * new_icopy: copy to/from in-core inode struct and disk inode (V2.x) + * dup_inode: indicate that someone else is using an inode table entry + */ + +#include "fs.h" +#include "buf.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "super.h" + +FORWARD _PROTOTYPE( void old_icopy, (struct inode *rip, d1_inode *dip, + int direction, int norm)); +FORWARD _PROTOTYPE( void new_icopy, (struct inode *rip, d2_inode *dip, + int direction, int norm)); + + +/*===========================================================================* + * get_inode * + *===========================================================================*/ +PUBLIC struct inode *get_inode(dev, numb) +dev_t dev; /* device on which inode resides */ +int numb; /* inode number (ANSI: may not be unshort) */ +{ +/* Find a slot in the inode table, load the specified inode into it, and + * return a pointer to the slot. If 'dev' == NO_DEV, just return a free slot. + */ + + register struct inode *rip, *xp; + + /* Search the inode table both for (dev, numb) and a free slot. */ + xp = NIL_INODE; + for (rip = &inode[0]; rip < &inode[NR_INODES]; rip++) { + if (rip->i_count > 0) { /* only check used slots for (dev, numb) */ + if (rip->i_dev == dev && rip->i_num == numb) { + /* This is the inode that we are looking for. */ + rip->i_count++; + return(rip); /* (dev, numb) found */ + } + } else { + xp = rip; /* remember this free slot for later */ + } + } + + /* Inode we want is not currently in use. Did we find a free slot? */ + if (xp == NIL_INODE) { /* inode table completely full */ + err_code = ENFILE; + return(NIL_INODE); + } + + /* A free inode slot has been located. Load the inode into it. */ + xp->i_dev = dev; + xp->i_num = numb; + xp->i_count = 1; + if (dev != NO_DEV) rw_inode(xp, READING); /* get inode from disk */ + xp->i_update = 0; /* all the times are initially up-to-date */ + + return(xp); +} + + +/*===========================================================================* + * put_inode * + *===========================================================================*/ +PUBLIC void put_inode(rip) +register struct inode *rip; /* pointer to inode to be released */ +{ +/* The caller is no longer using this inode. If no one else is using it either + * write it back to the disk immediately. If it has no links, truncate it and + * return it to the pool of available inodes. + */ + + if (rip == NIL_INODE) return; /* checking here is easier than in caller */ + if (--rip->i_count == 0) { /* i_count == 0 means no one is using it now */ + if (rip->i_nlinks == 0) { + /* i_nlinks == 0 means free the inode. */ + truncate(rip); /* return all the disk blocks */ + rip->i_mode = I_NOT_ALLOC; /* clear I_TYPE field */ + rip->i_dirt = DIRTY; + free_inode(rip->i_dev, rip->i_num); + } else { + if (rip->i_pipe == I_PIPE) truncate(rip); + } + rip->i_pipe = NO_PIPE; /* should always be cleared */ + if (rip->i_dirt == DIRTY) rw_inode(rip, WRITING); + } +} + +/*===========================================================================* + * alloc_inode * + *===========================================================================*/ +PUBLIC struct inode *alloc_inode(dev, bits) +dev_t dev; /* device on which to allocate the inode */ +mode_t bits; /* mode of the inode */ +{ +/* Allocate a free inode on 'dev', and return a pointer to it. */ + + register struct inode *rip; + register struct super_block *sp; + int major, minor, inumb; + bit_t b; + + sp = get_super(dev); /* get pointer to super_block */ + if (sp->s_rd_only) { /* can't allocate an inode on a read only device. */ + err_code = EROFS; + return(NIL_INODE); + } + + /* Acquire an inode from the bit map. */ + b = alloc_bit(sp, IMAP, sp->s_isearch); + if (b == NO_BIT) { + err_code = ENFILE; + major = (int) (sp->s_dev >> MAJOR) & BYTE; + minor = (int) (sp->s_dev >> MINOR) & BYTE; + printf("Out of i-nodes on %sdevice %d/%d\n", + sp->s_dev == root_dev ? "root " : "", major, minor); + return(NIL_INODE); + } + sp->s_isearch = b; /* next time start here */ + inumb = (int) b; /* be careful not to pass unshort as param */ + + /* Try to acquire a slot in the inode table. */ + if ((rip = get_inode(NO_DEV, inumb)) == NIL_INODE) { + /* No inode table slots available. Free the inode just allocated. */ + free_bit(sp, IMAP, b); + } else { + /* An inode slot is available. Put the inode just allocated into it. */ + rip->i_mode = bits; /* set up RWX bits */ + rip->i_nlinks = 0; /* initial no links */ + rip->i_uid = fp->fp_effuid; /* file's uid is owner's */ + rip->i_gid = fp->fp_effgid; /* ditto group id */ + rip->i_dev = dev; /* mark which device it is on */ + rip->i_ndzones = sp->s_ndzones; /* number of direct zones */ + rip->i_nindirs = sp->s_nindirs; /* number of indirect zones per blk*/ + rip->i_sp = sp; /* pointer to super block */ + + /* Fields not cleared already are cleared in wipe_inode(). They have + * been put there because truncate() needs to clear the same fields if + * the file happens to be open while being truncated. It saves space + * not to repeat the code twice. + */ + wipe_inode(rip); + } + + return(rip); +} + +/*===========================================================================* + * wipe_inode * + *===========================================================================*/ +PUBLIC void wipe_inode(rip) +register struct inode *rip; /* the inode to be erased */ +{ +/* Erase some fields in the inode. This function is called from alloc_inode() + * when a new inode is to be allocated, and from truncate(), when an existing + * inode is to be truncated. + */ + + register int i; + + rip->i_size = 0; + rip->i_update = ATIME | CTIME | MTIME; /* update all times later */ + rip->i_dirt = DIRTY; + for (i = 0; i < V2_NR_TZONES; i++) rip->i_zone[i] = NO_ZONE; +} + + +/*===========================================================================* + * free_inode * + *===========================================================================*/ +PUBLIC void free_inode(dev, inumb) +dev_t dev; /* on which device is the inode */ +ino_t inumb; /* number of inode to be freed */ +{ +/* Return an inode to the pool of unallocated inodes. */ + + register struct super_block *sp; + bit_t b; + + /* Locate the appropriate super_block. */ + sp = get_super(dev); + if (inumb <= 0 || inumb > sp->s_ninodes) return; + b = inumb; + free_bit(sp, IMAP, b); + if (b < sp->s_isearch) sp->s_isearch = b; +} + +/*===========================================================================* + * update_times * + *===========================================================================*/ +PUBLIC void update_times(rip) +register struct inode *rip; /* pointer to inode to be read/written */ +{ +/* Various system calls are required by the standard to update atime, ctime, + * or mtime. Since updating a time requires sending a message to the clock + * task--an expensive business--the times are marked for update by setting + * bits in i_update. When a stat, fstat, or sync is done, or an inode is + * released, update_times() may be called to actually fill in the times. + */ + + time_t cur_time; + struct super_block *sp; + + sp = rip->i_sp; /* get pointer to super block. */ + if (sp->s_rd_only) return; /* no updates for read-only file systems */ + + cur_time = clock_time(); + if (rip->i_update & ATIME) rip->i_atime = cur_time; + if (rip->i_update & CTIME) rip->i_ctime = cur_time; + if (rip->i_update & MTIME) rip->i_mtime = cur_time; + rip->i_update = 0; /* they are all up-to-date now */ +} + + +/*===========================================================================* + * rw_inode * + *===========================================================================*/ +PUBLIC void rw_inode(rip, rw_flag) +register struct inode *rip; /* pointer to inode to be read/written */ +int rw_flag; /* READING or WRITING */ +{ +/* An entry in the inode table is to be copied to or from the disk. */ + + register struct buf *bp; + register struct super_block *sp; + d1_inode *dip; + d2_inode *dip2; + block_t b, offset; + + /* Get the block where the inode resides. */ + sp = get_super(rip->i_dev); /* get pointer to super block */ + rip->i_sp = sp; /* inode must contain super block pointer */ + offset = sp->s_imap_blocks + sp->s_zmap_blocks + 2; + b = (block_t) (rip->i_num - 1)/sp->s_inodes_per_block + offset; + bp = get_block(rip->i_dev, b, NORMAL); + dip = bp->b_v1_ino + (rip->i_num - 1) % V1_INODES_PER_BLOCK; + dip2 = bp->b_v2_ino + (rip->i_num - 1) % V2_INODES_PER_BLOCK(sp->s_block_size); + + /* Do the read or write. */ + if (rw_flag == WRITING) { + if (rip->i_update) update_times(rip); /* times need updating */ + if (sp->s_rd_only == FALSE) bp->b_dirt = DIRTY; + } + + /* Copy the inode from the disk block to the in-core table or vice versa. + * If the fourth parameter below is FALSE, the bytes are swapped. + */ + if (sp->s_version == V1) + old_icopy(rip, dip, rw_flag, sp->s_native); + else + new_icopy(rip, dip2, rw_flag, sp->s_native); + + put_block(bp, INODE_BLOCK); + rip->i_dirt = CLEAN; +} + + +/*===========================================================================* + * old_icopy * + *===========================================================================*/ +PRIVATE void old_icopy(rip, dip, direction, norm) +register struct inode *rip; /* pointer to the in-core inode struct */ +register d1_inode *dip; /* pointer to the d1_inode inode struct */ +int direction; /* READING (from disk) or WRITING (to disk) */ +int norm; /* TRUE = do not swap bytes; FALSE = swap */ + +{ +/* The V1.x IBM disk, the V1.x 68000 disk, and the V2 disk (same for IBM and + * 68000) all have different inode layouts. When an inode is read or written + * this routine handles the conversions so that the information in the inode + * table is independent of the disk structure from which the inode came. + * The old_icopy routine copies to and from V1 disks. + */ + + int i; + + if (direction == READING) { + /* Copy V1.x inode to the in-core table, swapping bytes if need be. */ + rip->i_mode = conv2(norm, (int) dip->d1_mode); + rip->i_uid = conv2(norm, (int) dip->d1_uid ); + rip->i_size = conv4(norm, dip->d1_size); + rip->i_mtime = conv4(norm, dip->d1_mtime); + rip->i_atime = rip->i_mtime; + rip->i_ctime = rip->i_mtime; + rip->i_nlinks = dip->d1_nlinks; /* 1 char */ + rip->i_gid = dip->d1_gid; /* 1 char */ + rip->i_ndzones = V1_NR_DZONES; + rip->i_nindirs = V1_INDIRECTS; + for (i = 0; i < V1_NR_TZONES; i++) + rip->i_zone[i] = conv2(norm, (int) dip->d1_zone[i]); + } else { + /* Copying V1.x inode to disk from the in-core table. */ + dip->d1_mode = conv2(norm, (int) rip->i_mode); + dip->d1_uid = conv2(norm, (int) rip->i_uid ); + dip->d1_size = conv4(norm, rip->i_size); + dip->d1_mtime = conv4(norm, rip->i_mtime); + dip->d1_nlinks = rip->i_nlinks; /* 1 char */ + dip->d1_gid = rip->i_gid; /* 1 char */ + for (i = 0; i < V1_NR_TZONES; i++) + dip->d1_zone[i] = conv2(norm, (int) rip->i_zone[i]); + } +} + + +/*===========================================================================* + * new_icopy * + *===========================================================================*/ +PRIVATE void new_icopy(rip, dip, direction, norm) +register struct inode *rip; /* pointer to the in-core inode struct */ +register d2_inode *dip; /* pointer to the d2_inode struct */ +int direction; /* READING (from disk) or WRITING (to disk) */ +int norm; /* TRUE = do not swap bytes; FALSE = swap */ + +{ +/* Same as old_icopy, but to/from V2 disk layout. */ + + int i; + + if (direction == READING) { + /* Copy V2.x inode to the in-core table, swapping bytes if need be. */ + rip->i_mode = conv2(norm,dip->d2_mode); + rip->i_uid = conv2(norm,dip->d2_uid); + rip->i_nlinks = conv2(norm,dip->d2_nlinks); + rip->i_gid = conv2(norm,dip->d2_gid); + rip->i_size = conv4(norm,dip->d2_size); + rip->i_atime = conv4(norm,dip->d2_atime); + rip->i_ctime = conv4(norm,dip->d2_ctime); + rip->i_mtime = conv4(norm,dip->d2_mtime); + rip->i_ndzones = V2_NR_DZONES; + rip->i_nindirs = V2_INDIRECTS(rip->i_sp->s_block_size); + for (i = 0; i < V2_NR_TZONES; i++) + rip->i_zone[i] = conv4(norm, (long) dip->d2_zone[i]); + } else { + /* Copying V2.x inode to disk from the in-core table. */ + dip->d2_mode = conv2(norm,rip->i_mode); + dip->d2_uid = conv2(norm,rip->i_uid); + dip->d2_nlinks = conv2(norm,rip->i_nlinks); + dip->d2_gid = conv2(norm,rip->i_gid); + dip->d2_size = conv4(norm,rip->i_size); + dip->d2_atime = conv4(norm,rip->i_atime); + dip->d2_ctime = conv4(norm,rip->i_ctime); + dip->d2_mtime = conv4(norm,rip->i_mtime); + for (i = 0; i < V2_NR_TZONES; i++) + dip->d2_zone[i] = conv4(norm, (long) rip->i_zone[i]); + } +} + + +/*===========================================================================* + * dup_inode * + *===========================================================================*/ +PUBLIC void dup_inode(ip) +struct inode *ip; /* The inode to be duplicated. */ +{ +/* This routine is a simplified form of get_inode() for the case where + * the inode pointer is already known. + */ + + ip->i_count++; +} diff --git a/servers/fs/inode.h b/servers/fs/inode.h new file mode 100644 index 000000000..6967c3e77 --- /dev/null +++ b/servers/fs/inode.h @@ -0,0 +1,45 @@ +/* Inode table. This table holds inodes that are currently in use. In some + * cases they have been opened by an open() or creat() system call, in other + * cases the file system itself needs the inode for one reason or another, + * such as to search a directory for a path name. + * The first part of the struct holds fields that are present on the + * disk; the second part holds fields not present on the disk. + * The disk inode part is also declared in "type.h" as 'd1_inode' for V1 + * file systems and 'd2_inode' for V2 file systems. + */ + +EXTERN struct inode { + mode_t i_mode; /* file type, protection, etc. */ + nlink_t i_nlinks; /* how many links to this file */ + uid_t i_uid; /* user id of the file's owner */ + gid_t i_gid; /* group number */ + off_t i_size; /* current file size in bytes */ + time_t i_atime; /* time of last access (V2 only) */ + time_t i_mtime; /* when was file data last changed */ + time_t i_ctime; /* when was inode itself changed (V2 only)*/ + zone_t i_zone[V2_NR_TZONES]; /* zone numbers for direct, ind, and dbl ind */ + + /* The following items are not present on the disk. */ + dev_t i_dev; /* which device is the inode on */ + ino_t i_num; /* inode number on its (minor) device */ + int i_count; /* # times inode used; 0 means slot is free */ + int i_ndzones; /* # direct zones (Vx_NR_DZONES) */ + int i_nindirs; /* # indirect zones per indirect block */ + struct super_block *i_sp; /* pointer to super block for inode's device */ + char i_dirt; /* CLEAN or DIRTY */ + char i_pipe; /* set to I_PIPE if pipe */ + char i_mount; /* this bit is set if file mounted on */ + char i_seek; /* set on LSEEK, cleared on READ/WRITE */ + char i_update; /* the ATIME, CTIME, and MTIME bits are here */ +} inode[NR_INODES]; + + +#define NIL_INODE (struct inode *) 0 /* indicates absence of inode slot */ + +/* Field values. Note that CLEAN and DIRTY are defined in "const.h" */ +#define NO_PIPE 0 /* i_pipe is NO_PIPE if inode is not a pipe */ +#define I_PIPE 1 /* i_pipe is I_PIPE if inode is a pipe */ +#define NO_MOUNT 0 /* i_mount is NO_MOUNT if file not mounted on*/ +#define I_MOUNT 1 /* i_mount is I_MOUNT if file mounted on */ +#define NO_SEEK 0 /* i_seek = NO_SEEK if last op was not SEEK */ +#define ISEEK 1 /* i_seek = ISEEK if last op was SEEK */ diff --git a/servers/fs/link.c b/servers/fs/link.c new file mode 100644 index 000000000..195264004 --- /dev/null +++ b/servers/fs/link.c @@ -0,0 +1,452 @@ +/* This file handles the LINK and UNLINK system calls. It also deals with + * deallocating the storage used by a file when the last UNLINK is done to a + * file and the blocks must be returned to the free block pool. + * + * The entry points into this file are + * do_link: perform the LINK system call + * do_unlink: perform the UNLINK and RMDIR system calls + * do_rename: perform the RENAME system call + * truncate: release all the blocks associated with an inode + */ + +#include "fs.h" +#include <sys/stat.h> +#include <string.h> +#include <minix/com.h> +#include <minix/callnr.h> +#include "buf.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "param.h" +#include "super.h" + +#define SAME 1000 + +FORWARD _PROTOTYPE( int remove_dir, (struct inode *rldirp, struct inode *rip, + char dir_name[NAME_MAX]) ); + +FORWARD _PROTOTYPE( int unlink_file, (struct inode *dirp, struct inode *rip, + char file_name[NAME_MAX]) ); + + +/*===========================================================================* + * do_link * + *===========================================================================*/ +PUBLIC int do_link() +{ +/* Perform the link(name1, name2) system call. */ + + register struct inode *ip, *rip; + register int r; + char string[NAME_MAX]; + struct inode *new_ip; + + /* See if 'name' (file to be linked) exists. */ + if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); + if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); + + /* Check to see if the file has maximum number of links already. */ + r = OK; + if (rip->i_nlinks >= (rip->i_sp->s_version == V1 ? CHAR_MAX : SHRT_MAX)) + r = EMLINK; + + /* Only super_user may link to directories. */ + if (r == OK) + if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) r = EPERM; + + /* If error with 'name', return the inode. */ + if (r != OK) { + put_inode(rip); + return(r); + } + + /* Does the final directory of 'name2' exist? */ + if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) { + put_inode(rip); + return(err_code); + } + if ( (ip = last_dir(user_path, string)) == NIL_INODE) r = err_code; + + /* If 'name2' exists in full (even if no space) set 'r' to error. */ + if (r == OK) { + if ( (new_ip = advance(ip, string)) == NIL_INODE) { + r = err_code; + if (r == ENOENT) r = OK; + } else { + put_inode(new_ip); + r = EEXIST; + } + } + + /* Check for links across devices. */ + if (r == OK) + if (rip->i_dev != ip->i_dev) r = EXDEV; + + /* Try to link. */ + if (r == OK) + r = search_dir(ip, string, &rip->i_num, ENTER); + + /* If success, register the linking. */ + if (r == OK) { + rip->i_nlinks++; + rip->i_update |= CTIME; + rip->i_dirt = DIRTY; + } + + /* Done. Release both inodes. */ + put_inode(rip); + put_inode(ip); + return(r); +} + + +/*===========================================================================* + * do_unlink * + *===========================================================================*/ +PUBLIC int do_unlink() +{ +/* Perform the unlink(name) or rmdir(name) system call. The code for these two + * is almost the same. They differ only in some condition testing. Unlink() + * may be used by the superuser to do dangerous things; rmdir() may not. + */ + + register struct inode *rip; + struct inode *rldirp; + int r; + char string[NAME_MAX]; + + /* Get the last directory in the path. */ + if (fetch_name(m_in.name, m_in.name_length, M3) != OK) return(err_code); + if ( (rldirp = last_dir(user_path, string)) == NIL_INODE) + return(err_code); + + /* The last directory exists. Does the file also exist? */ + r = OK; + if ( (rip = advance(rldirp, string)) == NIL_INODE) r = err_code; + + /* If error, return inode. */ + if (r != OK) { + put_inode(rldirp); + return(r); + } + + /* Do not remove a mount point. */ + if (rip->i_num == ROOT_INODE) { + put_inode(rldirp); + put_inode(rip); + return(EBUSY); + } + + /* Now test if the call is allowed, separately for unlink() and rmdir(). */ + if (call_nr == UNLINK) { + /* Only the su may unlink directories, but the su can unlink any dir.*/ + if ( (rip->i_mode & I_TYPE) == I_DIRECTORY && !super_user) r = EPERM; + + /* Don't unlink a file if it is the root of a mounted file system. */ + if (rip->i_num == ROOT_INODE) r = EBUSY; + + /* Actually try to unlink the file; fails if parent is mode 0 etc. */ + if (r == OK) r = unlink_file(rldirp, rip, string); + + } else { + r = remove_dir(rldirp, rip, string); /* call is RMDIR */ + } + + /* If unlink was possible, it has been done, otherwise it has not. */ + put_inode(rip); + put_inode(rldirp); + return(r); +} + + +/*===========================================================================* + * do_rename * + *===========================================================================*/ +PUBLIC int do_rename() +{ +/* Perform the rename(name1, name2) system call. */ + + struct inode *old_dirp, *old_ip; /* ptrs to old dir, file inodes */ + struct inode *new_dirp, *new_ip; /* ptrs to new dir, file inodes */ + struct inode *new_superdirp, *next_new_superdirp; + int r = OK; /* error flag; initially no error */ + int odir, ndir; /* TRUE iff {old|new} file is dir */ + int same_pdir; /* TRUE iff parent dirs are the same */ + char old_name[NAME_MAX], new_name[NAME_MAX]; + ino_t numb; + int r1; + + /* See if 'name1' (existing file) exists. Get dir and file inodes. */ + if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); + if ( (old_dirp = last_dir(user_path, old_name))==NIL_INODE) return(err_code); + + if ( (old_ip = advance(old_dirp, old_name)) == NIL_INODE) r = err_code; + + /* See if 'name2' (new name) exists. Get dir and file inodes. */ + if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) r = err_code; + if ( (new_dirp = last_dir(user_path, new_name)) == NIL_INODE) r = err_code; + new_ip = advance(new_dirp, new_name); /* not required to exist */ + + if (old_ip != NIL_INODE) + odir = ((old_ip->i_mode & I_TYPE) == I_DIRECTORY); /* TRUE iff dir */ + + /* If it is ok, check for a variety of possible errors. */ + if (r == OK) { + same_pdir = (old_dirp == new_dirp); + + /* The old inode must not be a superdirectory of the new last dir. */ + if (odir && !same_pdir) { + dup_inode(new_superdirp = new_dirp); + while (TRUE) { /* may hang in a file system loop */ + if (new_superdirp == old_ip) { + r = EINVAL; + break; + } + next_new_superdirp = advance(new_superdirp, dot2); + put_inode(new_superdirp); + if (next_new_superdirp == new_superdirp) + break; /* back at system root directory */ + new_superdirp = next_new_superdirp; + if (new_superdirp == NIL_INODE) { + /* Missing ".." entry. Assume the worst. */ + r = EINVAL; + break; + } + } + put_inode(new_superdirp); + } + + /* The old or new name must not be . or .. */ + if (strcmp(old_name, ".")==0 || strcmp(old_name, "..")==0 || + strcmp(new_name, ".")==0 || strcmp(new_name, "..")==0) r = EINVAL; + + /* Both parent directories must be on the same device. */ + if (old_dirp->i_dev != new_dirp->i_dev) r = EXDEV; + + /* Parent dirs must be writable, searchable and on a writable device */ + if ((r1 = forbidden(old_dirp, W_BIT | X_BIT)) != OK || + (r1 = forbidden(new_dirp, W_BIT | X_BIT)) != OK) r = r1; + + /* Some tests apply only if the new path exists. */ + if (new_ip == NIL_INODE) { + /* don't rename a file with a file system mounted on it. */ + if (old_ip->i_dev != old_dirp->i_dev) r = EXDEV; + if (odir && new_dirp->i_nlinks >= + (new_dirp->i_sp->s_version == V1 ? CHAR_MAX : SHRT_MAX) && + !same_pdir && r == OK) r = EMLINK; + } else { + if (old_ip == new_ip) r = SAME; /* old=new */ + + /* has the old file or new file a file system mounted on it? */ + if (old_ip->i_dev != new_ip->i_dev) r = EXDEV; + + ndir = ((new_ip->i_mode & I_TYPE) == I_DIRECTORY); /* dir ? */ + if (odir == TRUE && ndir == FALSE) r = ENOTDIR; + if (odir == FALSE && ndir == TRUE) r = EISDIR; + } + } + + /* If a process has another root directory than the system root, we might + * "accidently" be moving it's working directory to a place where it's + * root directory isn't a super directory of it anymore. This can make + * the function chroot useless. If chroot will be used often we should + * probably check for it here. + */ + + /* The rename will probably work. Only two things can go wrong now: + * 1. being unable to remove the new file. (when new file already exists) + * 2. being unable to make the new directory entry. (new file doesn't exists) + * [directory has to grow by one block and cannot because the disk + * is completely full]. + */ + if (r == OK) { + if (new_ip != NIL_INODE) { + /* There is already an entry for 'new'. Try to remove it. */ + if (odir) + r = remove_dir(new_dirp, new_ip, new_name); + else + r = unlink_file(new_dirp, new_ip, new_name); + } + /* if r is OK, the rename will succeed, while there is now an + * unused entry in the new parent directory. + */ + } + + if (r == OK) { + /* If the new name will be in the same parent directory as the old one, + * first remove the old name to free an entry for the new name, + * otherwise first try to create the new name entry to make sure + * the rename will succeed. + */ + numb = old_ip->i_num; /* inode number of old file */ + + if (same_pdir) { + r = search_dir(old_dirp, old_name, (ino_t *) 0, DELETE); + /* shouldn't go wrong. */ + if (r==OK) (void) search_dir(old_dirp, new_name, &numb, ENTER); + } else { + r = search_dir(new_dirp, new_name, &numb, ENTER); + if (r == OK) + (void) search_dir(old_dirp, old_name, (ino_t *) 0, DELETE); + } + } + /* If r is OK, the ctime and mtime of old_dirp and new_dirp have been marked + * for update in search_dir. + */ + + if (r == OK && odir && !same_pdir) { + /* Update the .. entry in the directory (still points to old_dirp). */ + numb = new_dirp->i_num; + (void) unlink_file(old_ip, NIL_INODE, dot2); + if (search_dir(old_ip, dot2, &numb, ENTER) == OK) { + /* New link created. */ + new_dirp->i_nlinks++; + new_dirp->i_dirt = DIRTY; + } + } + + /* Release the inodes. */ + put_inode(old_dirp); + put_inode(old_ip); + put_inode(new_dirp); + put_inode(new_ip); + return(r == SAME ? OK : r); +} + + +/*===========================================================================* + * truncate * + *===========================================================================*/ +PUBLIC void truncate(rip) +register struct inode *rip; /* pointer to inode to be truncated */ +{ +/* Remove all the zones from the inode 'rip' and mark it dirty. */ + + register block_t b; + zone_t z, zone_size, z1; + off_t position; + int i, scale, file_type, waspipe, single, nr_indirects; + struct buf *bp; + dev_t dev; + + file_type = rip->i_mode & I_TYPE; /* check to see if file is special */ + if (file_type == I_CHAR_SPECIAL || file_type == I_BLOCK_SPECIAL) return; + dev = rip->i_dev; /* device on which inode resides */ + scale = rip->i_sp->s_log_zone_size; + zone_size = (zone_t) rip->i_sp->s_block_size << scale; + nr_indirects = rip->i_nindirs; + + /* Pipes can shrink, so adjust size to make sure all zones are removed. */ + waspipe = rip->i_pipe == I_PIPE; /* TRUE is this was a pipe */ + if (waspipe) rip->i_size = PIPE_SIZE(rip->i_sp->s_block_size); + + /* Step through the file a zone at a time, finding and freeing the zones. */ + for (position = 0; position < rip->i_size; position += zone_size) { + if ( (b = read_map(rip, position)) != NO_BLOCK) { + z = (zone_t) b >> scale; + free_zone(dev, z); + } + } + + /* All the data zones have been freed. Now free the indirect zones. */ + rip->i_dirt = DIRTY; + if (waspipe) { + wipe_inode(rip); /* clear out inode for pipes */ + return; /* indirect slots contain file positions */ + } + single = rip->i_ndzones; + free_zone(dev, rip->i_zone[single]); /* single indirect zone */ + if ( (z = rip->i_zone[single+1]) != NO_ZONE) { + /* Free all the single indirect zones pointed to by the double. */ + b = (block_t) z << scale; + bp = get_block(dev, b, NORMAL); /* get double indirect zone */ + for (i = 0; i < nr_indirects; i++) { + z1 = rd_indir(bp, i); + free_zone(dev, z1); + } + + /* Now free the double indirect zone itself. */ + put_block(bp, INDIRECT_BLOCK); + free_zone(dev, z); + } + + /* Leave zone numbers for de(1) to recover file after an unlink(2). */ +} + + +/*===========================================================================* + * remove_dir * + *===========================================================================*/ +PRIVATE int remove_dir(rldirp, rip, dir_name) +struct inode *rldirp; /* parent directory */ +struct inode *rip; /* directory to be removed */ +char dir_name[NAME_MAX]; /* name of directory to be removed */ +{ + /* A directory file has to be removed. Five conditions have to met: + * - The file must be a directory + * - The directory must be empty (except for . and ..) + * - The final component of the path must not be . or .. + * - The directory must not be the root of a mounted file system + * - The directory must not be anybody's root/working directory + */ + + int r; + register struct fproc *rfp; + + /* search_dir checks that rip is a directory too. */ + if ((r = search_dir(rip, "", (ino_t *) 0, IS_EMPTY)) != OK) return r; + + if (strcmp(dir_name, ".") == 0 || strcmp(dir_name, "..") == 0)return(EINVAL); + if (rip->i_num == ROOT_INODE) return(EBUSY); /* can't remove 'root' */ + + for (rfp = &fproc[INIT_PROC_NR + 1]; rfp < &fproc[NR_PROCS]; rfp++) + if (rfp->fp_workdir == rip || rfp->fp_rootdir == rip) return(EBUSY); + /* can't remove anybody's working dir */ + + /* Actually try to unlink the file; fails if parent is mode 0 etc. */ + if ((r = unlink_file(rldirp, rip, dir_name)) != OK) return r; + + /* Unlink . and .. from the dir. The super user can link and unlink any dir, + * so don't make too many assumptions about them. + */ + (void) unlink_file(rip, NIL_INODE, dot1); + (void) unlink_file(rip, NIL_INODE, dot2); + return(OK); +} + + +/*===========================================================================* + * unlink_file * + *===========================================================================*/ +PRIVATE int unlink_file(dirp, rip, file_name) +struct inode *dirp; /* parent directory of file */ +struct inode *rip; /* inode of file, may be NIL_INODE too. */ +char file_name[NAME_MAX]; /* name of file to be removed */ +{ +/* Unlink 'file_name'; rip must be the inode of 'file_name' or NIL_INODE. */ + + ino_t numb; /* inode number */ + int r; + + /* If rip is not NIL_INODE, it is used to get faster access to the inode. */ + if (rip == NIL_INODE) { + /* Search for file in directory and try to get its inode. */ + err_code = search_dir(dirp, file_name, &numb, LOOK_UP); + if (err_code == OK) rip = get_inode(dirp->i_dev, (int) numb); + if (err_code != OK || rip == NIL_INODE) return(err_code); + } else { + dup_inode(rip); /* inode will be returned with put_inode */ + } + + r = search_dir(dirp, file_name, (ino_t *) 0, DELETE); + + if (r == OK) { + rip->i_nlinks--; /* entry deleted from parent's dir */ + rip->i_update |= CTIME; + rip->i_dirt = DIRTY; + } + + put_inode(rip); + return(r); +} diff --git a/servers/fs/lock.c b/servers/fs/lock.c new file mode 100644 index 000000000..61b3c1904 --- /dev/null +++ b/servers/fs/lock.c @@ -0,0 +1,185 @@ +/* This file handles advisory file locking as required by POSIX. + * + * The entry points into this file are + * lock_op: perform locking operations for FCNTL system call + * lock_revive: revive processes when a lock is released + */ + +#include "fs.h" +#include <minix/com.h> +#include <fcntl.h> +#include <unistd.h> +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "lock.h" +#include "param.h" + +/*===========================================================================* + * lock_op * + *===========================================================================*/ +PUBLIC int lock_op(f, req) +struct filp *f; +int req; /* either F_SETLK or F_SETLKW */ +{ +/* Perform the advisory locking required by POSIX. */ + + int r, ltype, i, conflict = 0, unlocking = 0; + mode_t mo; + off_t first, last; + struct flock flock; + vir_bytes user_flock; + struct file_lock *flp, *flp2, *empty; + + /* Fetch the flock structure from user space. */ + user_flock = (vir_bytes) m_in.name1; + r = sys_datacopy(who, (vir_bytes) user_flock, + FS_PROC_NR, (vir_bytes) &flock, (phys_bytes) sizeof(flock)); + if (r != OK) return(EINVAL); + + /* Make some error checks. */ + ltype = flock.l_type; + mo = f->filp_mode; + if (ltype != F_UNLCK && ltype != F_RDLCK && ltype != F_WRLCK) return(EINVAL); + if (req == F_GETLK && ltype == F_UNLCK) return(EINVAL); + if ( (f->filp_ino->i_mode & I_TYPE) != I_REGULAR) return(EINVAL); + if (req != F_GETLK && ltype == F_RDLCK && (mo & R_BIT) == 0) return(EBADF); + if (req != F_GETLK && ltype == F_WRLCK && (mo & W_BIT) == 0) return(EBADF); + + /* Compute the first and last bytes in the lock region. */ + switch (flock.l_whence) { + case SEEK_SET: first = 0; break; + case SEEK_CUR: first = f->filp_pos; break; + case SEEK_END: first = f->filp_ino->i_size; break; + default: return(EINVAL); + } + /* Check for overflow. */ + if (((long)flock.l_start > 0) && ((first + flock.l_start) < first)) + return(EINVAL); + if (((long)flock.l_start < 0) && ((first + flock.l_start) > first)) + return(EINVAL); + first = first + flock.l_start; + last = first + flock.l_len - 1; + if (flock.l_len == 0) last = MAX_FILE_POS; + if (last < first) return(EINVAL); + + /* Check if this region conflicts with any existing lock. */ + empty = (struct file_lock *) 0; + for (flp = &file_lock[0]; flp < & file_lock[NR_LOCKS]; flp++) { + if (flp->lock_type == 0) { + if (empty == (struct file_lock *) 0) empty = flp; + continue; /* 0 means unused slot */ + } + if (flp->lock_inode != f->filp_ino) continue; /* different file */ + if (last < flp->lock_first) continue; /* new one is in front */ + if (first > flp->lock_last) continue; /* new one is afterwards */ + if (ltype == F_RDLCK && flp->lock_type == F_RDLCK) continue; + if (ltype != F_UNLCK && flp->lock_pid == fp->fp_pid) continue; + + /* There might be a conflict. Process it. */ + conflict = 1; + if (req == F_GETLK) break; + + /* If we are trying to set a lock, it just failed. */ + if (ltype == F_RDLCK || ltype == F_WRLCK) { + if (req == F_SETLK) { + /* For F_SETLK, just report back failure. */ + return(EAGAIN); + } else { + /* For F_SETLKW, suspend the process. */ + suspend(XLOCK); + return(SUSPEND); + } + } + + /* We are clearing a lock and we found something that overlaps. */ + unlocking = 1; + if (first <= flp->lock_first && last >= flp->lock_last) { + flp->lock_type = 0; /* mark slot as unused */ + nr_locks--; /* number of locks is now 1 less */ + continue; + } + + /* Part of a locked region has been unlocked. */ + if (first <= flp->lock_first) { + flp->lock_first = last + 1; + continue; + } + + if (last >= flp->lock_last) { + flp->lock_last = first - 1; + continue; + } + + /* Bad luck. A lock has been split in two by unlocking the middle. */ + if (nr_locks == NR_LOCKS) return(ENOLCK); + for (i = 0; i < NR_LOCKS; i++) + if (file_lock[i].lock_type == 0) break; + flp2 = &file_lock[i]; + flp2->lock_type = flp->lock_type; + flp2->lock_pid = flp->lock_pid; + flp2->lock_inode = flp->lock_inode; + flp2->lock_first = last + 1; + flp2->lock_last = flp->lock_last; + flp->lock_last = first - 1; + nr_locks++; + } + if (unlocking) lock_revive(); + + if (req == F_GETLK) { + if (conflict) { + /* GETLK and conflict. Report on the conflicting lock. */ + flock.l_type = flp->lock_type; + flock.l_whence = SEEK_SET; + flock.l_start = flp->lock_first; + flock.l_len = flp->lock_last - flp->lock_first + 1; + flock.l_pid = flp->lock_pid; + + } else { + /* It is GETLK and there is no conflict. */ + flock.l_type = F_UNLCK; + } + + /* Copy the flock structure back to the caller. */ + r = sys_datacopy(FS_PROC_NR, (vir_bytes) &flock, + who, (vir_bytes) user_flock, (phys_bytes) sizeof(flock)); + return(r); + } + + if (ltype == F_UNLCK) return(OK); /* unlocked a region with no locks */ + + /* There is no conflict. If space exists, store new lock in the table. */ + if (empty == (struct file_lock *) 0) return(ENOLCK); /* table full */ + empty->lock_type = ltype; + empty->lock_pid = fp->fp_pid; + empty->lock_inode = f->filp_ino; + empty->lock_first = first; + empty->lock_last = last; + nr_locks++; + return(OK); +} + +/*===========================================================================* + * lock_revive * + *===========================================================================*/ +PUBLIC void lock_revive() +{ +/* Go find all the processes that are waiting for any kind of lock and + * revive them all. The ones that are still blocked will block again when + * they run. The others will complete. This strategy is a space-time + * tradeoff. Figuring out exactly which ones to unblock now would take + * extra code, and the only thing it would win would be some performance in + * extremely rare circumstances (namely, that somebody actually used + * locking). + */ + + int task; + struct fproc *fptr; + + for (fptr = &fproc[INIT_PROC_NR + 1]; fptr < &fproc[NR_PROCS]; fptr++){ + task = -fptr->fp_task; + if (fptr->fp_suspended == SUSPENDED && task == XLOCK) { + revive( (int) (fptr - fproc), 0); + } + } +} diff --git a/servers/fs/lock.h b/servers/fs/lock.h new file mode 100644 index 000000000..a81ba2051 --- /dev/null +++ b/servers/fs/lock.h @@ -0,0 +1,10 @@ +/* This is the file locking table. Like the filp table, it points to the + * inode table, however, in this case to achieve advisory locking. + */ +EXTERN struct file_lock { + short lock_type; /* F_RDLOCK or F_WRLOCK; 0 means unused slot */ + pid_t lock_pid; /* pid of the process holding the lock */ + struct inode *lock_inode; /* pointer to the inode locked */ + off_t lock_first; /* offset of first byte locked */ + off_t lock_last; /* offset of last byte locked */ +} file_lock[NR_LOCKS]; diff --git a/servers/fs/main.c b/servers/fs/main.c new file mode 100644 index 000000000..e1786afab --- /dev/null +++ b/servers/fs/main.c @@ -0,0 +1,406 @@ +/* This file contains the main program of the File System. It consists of + * a loop that gets messages requesting work, carries out the work, and sends + * replies. + * + * The entry points into this file are: + * main: main program of the File System + * reply: send a reply to a process after the requested work is done + * + * Changes: + * Mar 23, 2005 allow arbitrary partitions as RAM disk (Jorrit N. Herder) + * Jan 10, 2005 register fkeys with TTY for debug dumps (Jorrit N. Herder) + */ + +struct super_block; /* proto.h needs to know this */ + +#include "fs.h" +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <sys/ioc_memory.h> +#include <sys/svrctl.h> +#include <minix/utils.h> +#include <minix/callnr.h> +#include <minix/com.h> +#include <minix/keymap.h> +#include "buf.h" +#include "dmap.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "param.h" +#include "super.h" + +FORWARD _PROTOTYPE( void fs_init, (void) ); +FORWARD _PROTOTYPE( int igetenv, (char *var) ); +FORWARD _PROTOTYPE( void get_work, (void) ); +FORWARD _PROTOTYPE( void load_ram, (void) ); +FORWARD _PROTOTYPE( void load_super, (Dev_t super_dev) ); + + +/*===========================================================================* + * main * + *===========================================================================*/ +PUBLIC void main() +{ +/* This is the main program of the file system. The main loop consists of + * three major activities: getting new work, processing the work, and sending + * the reply. This loop never terminates as long as the file system runs. + */ + int error; + + fs_init(); + + /* This is the main loop that gets work, processes it, and sends replies. */ + while (TRUE) { + get_work(); /* sets who and call_nr */ + + fp = &fproc[who]; /* pointer to proc table struct */ + super_user = (fp->fp_effuid == SU_UID ? TRUE : FALSE); /* su? */ + + /* Check for special control messages first. */ + if (call_nr == HARD_STOP) { + do_sync(); + sys_exit(0); /* never returns */ + } else if (call_nr == FKEY_PRESSED) { + do_fkey_pressed(); + continue; /* get work again */ + } + + /* Call the internal function that does the work. */ + if (call_nr < 0 || call_nr >= NCALLS) + error = ENOSYS; + else + error = (*call_vec[call_nr])(); + + /* Copy the results back to the user and send reply. */ + if (error != SUSPEND) { reply(who, error); } + if (rdahed_inode != NIL_INODE) { + read_ahead(); /* do block read ahead */ + } + + } +} + + +/*===========================================================================* + * get_work * + *===========================================================================*/ +PRIVATE void get_work() +{ + /* Normally wait for new input. However, if 'reviving' is + * nonzero, a suspended process must be awakened. + */ + + register struct fproc *rp; + + if (reviving != 0) { + /* Revive a suspended process. */ + for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++) + if (rp->fp_revived == REVIVING) { + who = (int)(rp - fproc); + call_nr = rp->fp_fd & BYTE; + m_in.fd = (rp->fp_fd >>8) & BYTE; + m_in.buffer = rp->fp_buffer; + m_in.nbytes = rp->fp_nbytes; + rp->fp_suspended = NOT_SUSPENDED; /*no longer hanging*/ + rp->fp_revived = NOT_REVIVING; + reviving--; + return; + } + panic("get_work couldn't revive anyone", NO_NUM); + } + + /* Normal case. No one to revive. */ + if (receive(ANY, &m_in) != OK) panic("fs receive error", NO_NUM); + who = m_in.m_source; + call_nr = m_in.m_type; +} + +/*===========================================================================* + * buf_pool * + *===========================================================================*/ +PRIVATE void buf_pool(void) +{ +/* Initialize the buffer pool. */ + + register struct buf *bp; + + bufs_in_use = 0; + front = &buf[0]; + rear = &buf[NR_BUFS - 1]; + + for (bp = &buf[0]; bp < &buf[NR_BUFS]; bp++) { + bp->b_blocknr = NO_BLOCK; + bp->b_dev = NO_DEV; + bp->b_next = bp + 1; + bp->b_prev = bp - 1; + } + buf[0].b_prev = NIL_BUF; + buf[NR_BUFS - 1].b_next = NIL_BUF; + + for (bp = &buf[0]; bp < &buf[NR_BUFS]; bp++) bp->b_hash = bp->b_next; + buf_hash[0] = front; + +} + +/*===========================================================================* + * reply * + *===========================================================================*/ +PUBLIC void reply(whom, result) +int whom; /* process to reply to */ +int result; /* result of the call (usually OK or error #) */ +{ +/* Send a reply to a user process. It may fail (if the process has just + * been killed by a signal), so don't check the return code. If the send + * fails, just ignore it. + */ + int s; + m_out.reply_type = result; + s = send(whom, &m_out); + if (s != OK) printf("FS: couldn't send reply: %d\n", s); +} + + +/*===========================================================================* + * fs_init * + *===========================================================================*/ +PRIVATE void fs_init() +{ +/* Initialize global variables, tables, etc. */ + + register struct inode *rip; + int key, i; + message mess; + + /* The following initializations are needed to let dev_opcl succeed .*/ + fp = (struct fproc *) NULL; + who = FS_PROC_NR; + + map_controllers(); /* map controller devices to drivers */ + buf_pool(); /* initialize buffer pool */ + load_ram(); /* init RAM disk, load if it is root */ + load_super(root_dev); /* load super block for root device */ + + /* Initialize the 'fproc' fields for process 0 .. INIT. */ + for (i = 0; i <= LOW_USER; i+= 1) { + if (i == FS_PROC_NR) continue; /* do not initialize FS */ + fp = &fproc[i]; + rip = get_inode(root_dev, ROOT_INODE); + fp->fp_rootdir = rip; + dup_inode(rip); + fp->fp_workdir = rip; + fp->fp_realuid = (uid_t) SYS_UID; + fp->fp_effuid = (uid_t) SYS_UID; + fp->fp_realgid = (gid_t) SYS_GID; + fp->fp_effgid = (gid_t) SYS_GID; + fp->fp_umask = ~0; + fp->fp_pid = i < LOW_USER ? PID_SERVER : 1; + } + + /* Certain relations must hold for the file system to work at all. + * (Some extra block_size requirements are checked at super-block-read-in + * time.) + */ + if (OPEN_MAX > 127) panic("OPEN_MAX > 127", NO_NUM); + if (NR_BUFS < 6) panic("NR_BUFS < 6", NO_NUM); + if (V1_INODE_SIZE != 32) panic("V1 inode size != 32", NO_NUM); + if (V2_INODE_SIZE != 64) panic("V2 inode size != 64", NO_NUM); + if (OPEN_MAX > 8 * sizeof(long)) panic("Too few bits in fp_cloexec", NO_NUM); + + /* Tell the memory task where my process table is for the sake of ps(1). */ + mess.m_type = DEV_IOCTL; + mess.PROC_NR = FS_PROC_NR; + mess.DEVICE = RAM_DEV; + mess.REQUEST = MIOCSPSINFO; + mess.ADDRESS = (void *) fproc; + (void) sendrec(MEMORY, &mess); + + /* Register function keys with TTY. */ + for (key=SF5; key<=SF6; key++) { + if ((i=fkey_enable(key))!=OK) { + printf("Warning: FS couldn't register Shift+F%d key: %d\n", + SF1-key+1, i); + } + } +} + + +/*===========================================================================* + * igetenv * + *===========================================================================*/ +PRIVATE int igetenv(key) +char *key; +{ +/* Ask kernel for an integer valued boot environment variable. */ + char value[64]; + int i; + + if ((i = get_mon_param(key, value, sizeof(value))) != OK) + printf("FS: Warning, couldn't get monitor param: %d\n", i); + return(atoi(value)); +} + + +/*===========================================================================* + * load_ram * + *===========================================================================*/ +PRIVATE void load_ram(void) +{ +/* Allocate a RAM disk with size given in the boot parameters. If a RAM disk + * image is given, the copy the entire image device block-by-block to a RAM + * disk with the same size as the image. + * If the root device is not set, the RAM disk will be used as root instead. + */ + + register struct buf *bp, *bp1; + u32_t lcount, ram_size_kb; + zone_t zones; + struct super_block *sp, *dsp; + block_t b; + Dev_t image_dev; + int r; + static char sbbuf[MIN_BLOCK_SIZE]; + + /* Get some boot environment variables. */ + root_dev = igetenv("rootdev"); + image_dev = igetenv("ramimagedev"); + ram_size_kb = igetenv("ramsize"); + + /* Open the root device. */ + if (dev_open(root_dev, FS_PROC_NR, R_BIT|W_BIT) != OK) { + panic("Cannot open root device",NO_NUM); + } + + /* If we must initialize a ram disk, get details from the image device. */ + if (root_dev == DEV_RAM || root_dev != image_dev) { + u32_t fsmax; + if (dev_open(image_dev, FS_PROC_NR, R_BIT) != OK) + panic("Cannot open RAM image device", NO_NUM); + + /* Get size of RAM disk image from the super block. */ + sp = &super_block[0]; + sp->s_dev = image_dev; + if (read_super(sp) != OK) panic("Bad RAM disk image FS", NO_NUM); + + lcount = sp->s_zones << sp->s_log_zone_size; /* # blks on root dev*/ + + /* Stretch the RAM disk file system to the boot parameters size, but + * no further than the last zone bit map block allows. + */ + if (ram_size_kb*1024 < lcount*sp->s_block_size) + ram_size_kb = lcount*sp->s_block_size/1024; + fsmax = (u32_t) sp->s_zmap_blocks * CHAR_BIT * sp->s_block_size; + fsmax = (fsmax + (sp->s_firstdatazone-1)) << sp->s_log_zone_size; + if (ram_size_kb*1024 > fsmax*sp->s_block_size) + ram_size_kb = fsmax*sp->s_block_size/1024; + } + + /* Tell RAM driver how big the RAM disk must be. */ + m_out.m_type = DEV_IOCTL; + m_out.PROC_NR = FS_PROC_NR; + m_out.DEVICE = RAM_DEV; + m_out.REQUEST = MIOCRAMSIZE; + m_out.POSITION = ram_size_kb*1024; + if (sendrec(MEMORY, &m_out) != OK || m_out.REP_STATUS != OK) + panic("Can't set RAM disk size", NO_NUM); + + /* Tell MM the RAM disk size, and wait for it to come "on-line". */ + m_out.MEM_CHUNK_SIZE = ((long) ram_size_kb * 1024) >> CLICK_SHIFT; + if (sendrec(MM_PROC_NR, &m_out) != OK) + panic("FS can't sync up with MM", NO_NUM); + +#if ENABLE_CACHE2 + /* The RAM disk is a second level block cache while not otherwise used. */ + init_cache2(ram_size); +#endif + + /* See if we must load the RAM disk image, otherwise return. */ + if (root_dev != DEV_RAM && root_dev == image_dev) { + printf("Created empty RAM disk of %u kb.\n", ram_size_kb); + return; + } + + /* Copy the blocks one at a time from the image to the RAM disk. */ + printf("Loading RAM disk.\33[23CLoaded: 0K "); + + inode[0].i_mode = I_BLOCK_SPECIAL; /* temp inode for rahead() */ + inode[0].i_size = LONG_MAX; + inode[0].i_dev = image_dev; + inode[0].i_zone[0] = image_dev; + + panic("RAM disk loading doesn't work yet.", NO_NUM); + +#if 0 + for (b = 0; b < (block_t) lcount; b++) { + bp = rahead(&inode[0], b, (off_t)io_block_size * b, io_block_size); + bp1 = get_block(root_dev, b, NO_READ); + memcpy(bp1->b_data, bp->b_data, (size_t) io_block_size); + bp1->b_dirt = DIRTY; + put_block(bp, FULL_DATA_BLOCK); + put_block(bp1, FULL_DATA_BLOCK); + printf("\b\b\b\b\b\b\b%5ldK ", ((long) b * io_block_size)/1024L); + } + + printf("\rRAM disk of %u kb loaded.\33[K", ram_size_kb); + if (root_dev == DEV_RAM) printf(" RAM disk is used as root FS."); + printf("\n\n"); + + /* Invalidate and close the image device. */ + invalidate(image_dev); + dev_close(image_dev); + + /* Resize the RAM disk root file system. */ + if(dev_io(DEV_READ, root_dev, FS_PROC_NR, + sbbuf, SUPER_BLOCK_BYTES, MIN_BLOCK_SIZE, 0) != MIN_BLOCK_SIZE) { + printf("WARNING: ramdisk read for resizing failed\n"); + } + dsp = (struct super_block *) sbbuf; + if(dsp->s_magic == SUPER_V3) + ram_block_size = dsp->s_block_size; + else + ram_block_size = STATIC_BLOCK_SIZE; + zones = (ram_size_kb * 1024 / ram_block_size) >> sp->s_log_zone_size; + + dsp->s_nzones = conv2(sp->s_native, (u16_t) zones); + dsp->s_zones = conv4(sp->s_native, zones); + if(dev_io(DEV_WRITE, root_dev, FS_PROC_NR, + sbbuf, SUPER_BLOCK_BYTES, MIN_BLOCK_SIZE, 0) != MIN_BLOCK_SIZE) { + printf("WARNING: ramdisk write for resizing failed\n"); + } +#endif +} + + +/*===========================================================================* + * load_super * + *===========================================================================*/ +PRIVATE void load_super(super_dev) +dev_t super_dev; /* place to get superblock from */ +{ + int bad; + register struct super_block *sp; + register struct inode *rip; + + /* Initialize the super_block table. */ + for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) + sp->s_dev = NO_DEV; + + /* Read in super_block for the root file system. */ + sp = &super_block[0]; + sp->s_dev = super_dev; + + /* Check super_block for consistency. */ + bad = (read_super(sp) != OK); + if (!bad) { + rip = get_inode(super_dev, ROOT_INODE); /* inode for root dir */ + if ( (rip->i_mode & I_TYPE) != I_DIRECTORY || rip->i_nlinks < 3) bad++; + } + if (bad) panic("Invalid root file system", NO_NUM); + + sp->s_imount = rip; + dup_inode(rip); + sp->s_isup = rip; + sp->s_rd_only = 0; + return; +} diff --git a/servers/fs/makenmtab.sh b/servers/fs/makenmtab.sh new file mode 100644 index 000000000..4ca16f4f3 --- /dev/null +++ b/servers/fs/makenmtab.sh @@ -0,0 +1,6 @@ +#!/bin/sh +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin + +echo "struct fs_sym_entry { unsigned long symoffset; char *symname; } fs_sym_entries[] = {" >$1 +nm -n fs | grep -v "^fs:$" | grep ' [tT] ' | awk '{ print "{ 0x" $1 ", \"" $3 "\" }, " }' >>$1 +echo "};" >>$1 diff --git a/servers/fs/misc.c b/servers/fs/misc.c new file mode 100644 index 000000000..9641cf7fb --- /dev/null +++ b/servers/fs/misc.c @@ -0,0 +1,412 @@ +/* This file contains a collection of miscellaneous procedures. Some of them + * perform simple system calls. Some others do a little part of system calls + * that are mostly performed by the Memory Manager. + * + * The entry points into this file are + * do_dup: perform the DUP system call + * do_fcntl: perform the FCNTL system call + * do_sync: perform the SYNC system call + * do_reboot: sync disks and prepare for shutdown + * do_fork: adjust the tables after MM has performed a FORK system call + * do_exec: handle files with FD_CLOEXEC on after MM has done an EXEC + * do_exit: a process has exited; note that in the tables + * do_set: set uid or gid for some process + * do_revive: revive a process that was waiting for something (e.g. TTY) + * do_svrctl: file system control + * do_getsysinfo: request copy of FS data structure + */ + +#include "fs.h" +#include <fcntl.h> +#include <unistd.h> /* cc runs out of memory with unistd.h :-( */ +#include <minix/callnr.h> +#include <minix/com.h> +#include <sys/svrctl.h> +#include "buf.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "dmap.h" +#include "param.h" +#include "super.h" + +/*===========================================================================* + * do_getsysinfo * + *===========================================================================*/ +PUBLIC int do_getsysinfo() +{ + return(OK); +} + + +/*===========================================================================* + * do_dup * + *===========================================================================*/ +PUBLIC int do_dup() +{ +/* Perform the dup(fd) or dup2(fd,fd2) system call. These system calls are + * obsolete. In fact, it is not even possible to invoke them using the + * current library because the library routines call fcntl(). They are + * provided to permit old binary programs to continue to run. + */ + + register int rfd; + register struct filp *f; + struct filp *dummy; + int r; + + /* Is the file descriptor valid? */ + rfd = m_in.fd & ~DUP_MASK; /* kill off dup2 bit, if on */ + if ((f = get_filp(rfd)) == NIL_FILP) return(err_code); + + /* Distinguish between dup and dup2. */ + if (m_in.fd == rfd) { /* bit not on */ + /* dup(fd) */ + if ( (r = get_fd(0, 0, &m_in.fd2, &dummy)) != OK) return(r); + } else { + /* dup2(fd, fd2) */ + if (m_in.fd2 < 0 || m_in.fd2 >= OPEN_MAX) return(EBADF); + if (rfd == m_in.fd2) return(m_in.fd2); /* ignore the call: dup2(x, x) */ + m_in.fd = m_in.fd2; /* prepare to close fd2 */ + (void) do_close(); /* cannot fail */ + } + + /* Success. Set up new file descriptors. */ + f->filp_count++; + fp->fp_filp[m_in.fd2] = f; + return(m_in.fd2); +} + +/*===========================================================================* + * do_fcntl * + *===========================================================================*/ +PUBLIC int do_fcntl() +{ +/* Perform the fcntl(fd, request, ...) system call. */ + + register struct filp *f; + int new_fd, r, fl; + long cloexec_mask; /* bit map for the FD_CLOEXEC flag */ + long clo_value; /* FD_CLOEXEC flag in proper position */ + struct filp *dummy; + + /* Is the file descriptor valid? */ + if ((f = get_filp(m_in.fd)) == NIL_FILP) return(err_code); + + switch (m_in.request) { + case F_DUPFD: + /* This replaces the old dup() system call. */ + if (m_in.addr < 0 || m_in.addr >= OPEN_MAX) return(EINVAL); + if ((r = get_fd(m_in.addr, 0, &new_fd, &dummy)) != OK) return(r); + f->filp_count++; + fp->fp_filp[new_fd] = f; + return(new_fd); + + case F_GETFD: + /* Get close-on-exec flag (FD_CLOEXEC in POSIX Table 6-2). */ + return( ((fp->fp_cloexec >> m_in.fd) & 01) ? FD_CLOEXEC : 0); + + case F_SETFD: + /* Set close-on-exec flag (FD_CLOEXEC in POSIX Table 6-2). */ + cloexec_mask = 1L << m_in.fd; /* singleton set position ok */ + clo_value = (m_in.addr & FD_CLOEXEC ? cloexec_mask : 0L); + fp->fp_cloexec = (fp->fp_cloexec & ~cloexec_mask) | clo_value; + return(OK); + + case F_GETFL: + /* Get file status flags (O_NONBLOCK and O_APPEND). */ + fl = f->filp_flags & (O_NONBLOCK | O_APPEND | O_ACCMODE); + return(fl); + + case F_SETFL: + /* Set file status flags (O_NONBLOCK and O_APPEND). */ + fl = O_NONBLOCK | O_APPEND; + f->filp_flags = (f->filp_flags & ~fl) | (m_in.addr & fl); + return(OK); + + case F_GETLK: + case F_SETLK: + case F_SETLKW: + /* Set or clear a file lock. */ + r = lock_op(f, m_in.request); + return(r); + default: + return(EINVAL); + } +} + + +/*===========================================================================* + * do_sync * + *===========================================================================*/ +PUBLIC int do_sync() +{ +/* Perform the sync() system call. Flush all the tables. + * The order in which the various tables are flushed is critical. The + * blocks must be flushed last, since rw_inode() leaves its results in + * the block cache. + */ + register struct inode *rip; + register struct buf *bp; + + /* Write all the dirty inodes to the disk. */ + for (rip = &inode[0]; rip < &inode[NR_INODES]; rip++) + if (rip->i_count > 0 && rip->i_dirt == DIRTY) rw_inode(rip, WRITING); + + /* Write all the dirty blocks to the disk, one drive at a time. */ + for (bp = &buf[0]; bp < &buf[NR_BUFS]; bp++) + if (bp->b_dev != NO_DEV && bp->b_dirt == DIRTY) flushall(bp->b_dev); + + return(OK); /* sync() can't fail */ +} + + +/*===========================================================================* + * do_reboot * + *===========================================================================*/ +PUBLIC int do_reboot() +{ + /* Perform the FS side of the reboot call. */ + int i; + struct super_block *sp; + struct inode dummy; + + /* Only MM may make this call directly. */ + if (who != MM_PROC_NR) return(EGENERIC); + + /* Do exit processing for all leftover processes and servers. */ + for (i = 0; i < NR_PROCS; i++) { m_in.slot1 = i; do_exit(); } + + /* The root file system is mounted onto itself, which keeps it from being + * unmounted. Pull an inode out of thin air and put the root on it. + */ + put_inode(super_block[0].s_imount); + super_block[0].s_imount= &dummy; + dummy.i_count = 2; /* expect one "put" */ + + /* Unmount all filesystems. File systems are mounted on other file systems, + * so you have to pull off the loose bits repeatedly to get it all undone. + */ + for (i= 0; i < NR_SUPERS; i++) { + /* Unmount at least one. */ + for (sp= &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) { + if (sp->s_dev != NO_DEV) (void) unmount(sp->s_dev); + } + } + + return(OK); +} + + +/*===========================================================================* + * do_fork * + *===========================================================================*/ +PUBLIC int do_fork() +{ +/* Perform those aspects of the fork() system call that relate to files. + * In particular, let the child inherit its parent's file descriptors. + * The parent and child parameters tell who forked off whom. The file + * system uses the same slot numbers as the kernel. Only MM makes this call. + */ + + register struct fproc *cp; + int i; + + /* Only MM may make this call directly. */ + if (who != MM_PROC_NR) return(EGENERIC); + + /* Copy the parent's fproc struct to the child. */ + fproc[m_in.child] = fproc[m_in.parent]; + + /* Increase the counters in the 'filp' table. */ + cp = &fproc[m_in.child]; + for (i = 0; i < OPEN_MAX; i++) + if (cp->fp_filp[i] != NIL_FILP) cp->fp_filp[i]->filp_count++; + + /* Fill in new process id. */ + cp->fp_pid = m_in.pid; + + /* A child is not a process leader. */ + cp->fp_sesldr = 0; + + /* Record the fact that both root and working dir have another user. */ + dup_inode(cp->fp_rootdir); + dup_inode(cp->fp_workdir); + return(OK); +} + + +/*===========================================================================* + * do_exec * + *===========================================================================*/ +PUBLIC int do_exec() +{ +/* Files can be marked with the FD_CLOEXEC bit (in fp->fp_cloexec). When + * MM does an EXEC, it calls FS to allow FS to find these files and close them. + */ + + register int i; + long bitmap; + + /* Only MM may make this call directly. */ + if (who != MM_PROC_NR) return(EGENERIC); + + /* The array of FD_CLOEXEC bits is in the fp_cloexec bit map. */ + fp = &fproc[m_in.slot1]; /* get_filp() needs 'fp' */ + bitmap = fp->fp_cloexec; + if (bitmap == 0) return(OK); /* normal case, no FD_CLOEXECs */ + + /* Check the file desriptors one by one for presence of FD_CLOEXEC. */ + for (i = 0; i < OPEN_MAX; i++) { + m_in.fd = i; + if ( (bitmap >> i) & 01) (void) do_close(); + } + + return(OK); +} + + +/*===========================================================================* + * do_exit * + *===========================================================================*/ +PUBLIC int do_exit() +{ +/* Perform the file system portion of the exit(status) system call. */ + + register int i, exitee, task; + register struct fproc *rfp; + register struct filp *rfilp; + register struct inode *rip; + dev_t dev; + + /* Only MM may do the EXIT call directly. */ + if (who != MM_PROC_NR) return(EGENERIC); + + /* Nevertheless, pretend that the call came from the user. */ + fp = &fproc[m_in.slot1]; /* get_filp() needs 'fp' */ + exitee = m_in.slot1; + + if (fp->fp_suspended == SUSPENDED) { + task = -fp->fp_task; + if (task == XPIPE || task == XPOPEN) susp_count--; + m_in.pro = exitee; + (void) do_unpause(); /* this always succeeds for MM */ + fp->fp_suspended = NOT_SUSPENDED; + } + + /* Loop on file descriptors, closing any that are open. */ + for (i = 0; i < OPEN_MAX; i++) { + m_in.fd = i; + (void) do_close(); + } + + /* Release root and working directories. */ + put_inode(fp->fp_rootdir); + put_inode(fp->fp_workdir); + fp->fp_rootdir = NIL_INODE; + fp->fp_workdir = NIL_INODE; + + /* If a session leader exits then revoke access to its controlling tty from + * all other processes using it. + */ + if (!fp->fp_sesldr) return(OK); /* not a session leader */ + fp->fp_sesldr = FALSE; + if (fp->fp_tty == 0) return(OK); /* no controlling tty */ + dev = fp->fp_tty; + + for (rfp = &fproc[LOW_USER]; rfp < &fproc[NR_PROCS]; rfp++) { + if (rfp->fp_tty == dev) rfp->fp_tty = 0; + + for (i = 0; i < OPEN_MAX; i++) { + if ((rfilp = rfp->fp_filp[i]) == NIL_FILP) continue; + if (rfilp->filp_mode == FILP_CLOSED) continue; + rip = rfilp->filp_ino; + if ((rip->i_mode & I_TYPE) != I_CHAR_SPECIAL) continue; + if ((dev_t) rip->i_zone[0] != dev) continue; + dev_close(dev); + rfilp->filp_mode = FILP_CLOSED; + } + } + + /* Truly exiting, or becoming a server? */ + fp->fp_pid = PID_FREE; + return(OK); +} + + +/*===========================================================================* + * do_set * + *===========================================================================*/ +PUBLIC int do_set() +{ +/* Set uid_t or gid_t field. */ + + register struct fproc *tfp; + + /* Only MM may make this call directly. */ + if (who != MM_PROC_NR) return(EGENERIC); + + tfp = &fproc[m_in.slot1]; + if (call_nr == SETUID) { + tfp->fp_realuid = (uid_t) m_in.real_user_id; + tfp->fp_effuid = (uid_t) m_in.eff_user_id; + } + if (call_nr == SETGID) { + tfp->fp_effgid = (gid_t) m_in.eff_grp_id; + tfp->fp_realgid = (gid_t) m_in.real_grp_id; + } + return(OK); +} + + +/*===========================================================================* + * do_revive * + *===========================================================================*/ +PUBLIC int do_revive() +{ +/* A task, typically TTY, has now gotten the characters that were needed for a + * previous read. The process did not get a reply when it made the call. + * Instead it was suspended. Now we can send the reply to wake it up. This + * business has to be done carefully, since the incoming message is from + * a task (to which no reply can be sent), and the reply must go to a process + * that blocked earlier. The reply to the caller is inhibited by returning the + * 'SUSPEND' pseudo error, and the reply to the blocked process is done + * explicitly in revive(). + */ + + if (who >= LOW_USER && fp->fp_pid != PID_SERVER) return(EPERM); + + revive(m_in.REP_PROC_NR, m_in.REP_STATUS); + return(SUSPEND); /* don't reply to the TTY task */ +} + +/*===========================================================================* + * do_svrctl * + *===========================================================================*/ +PUBLIC int do_svrctl() +{ + switch (m_in.svrctl_req) { + case FSSIGNON: { + /* A server in user space calls in to manage a device. */ + struct fssignon device; + int r, major; + struct dmap *dp; + + if (fp->fp_effuid != SU_UID) return(EPERM); + + /* Try to copy request structure to FS. */ + if ((r = sys_datacopy(who, (vir_bytes) m_in.svrctl_argp, + FS_PROC_NR, (vir_bytes) &device, + (phys_bytes) sizeof(device))) != OK) + return(r); + + /* Try to update device mapping. */ + major = (device.dev >> MAJOR) & BYTE; + if ((r=map_driver(major, who, device.style)) == OK) + fp->fp_pid = PID_SERVER; + return(r); + } + default: + return(EINVAL); + } +} diff --git a/servers/fs/mount.c b/servers/fs/mount.c new file mode 100644 index 000000000..cb58eba85 --- /dev/null +++ b/servers/fs/mount.c @@ -0,0 +1,221 @@ +/* This file performs the MOUNT and UMOUNT system calls. + * + * The entry points into this file are + * do_mount: perform the MOUNT system call + * do_umount: perform the UMOUNT system call + */ + +#include "fs.h" +#include <fcntl.h> +#include <minix/com.h> +#include <sys/stat.h> +#include "buf.h" +#include "dmap.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "param.h" +#include "super.h" + +FORWARD _PROTOTYPE( dev_t name_to_dev, (char *path) ); + +/*===========================================================================* + * do_mount * + *===========================================================================*/ +PUBLIC int do_mount() +{ +/* Perform the mount(name, mfile, rd_only) system call. */ + + register struct inode *rip, *root_ip; + struct super_block *xp, *sp; + dev_t dev; + mode_t bits; + int rdir, mdir; /* TRUE iff {root|mount} file is dir */ + int r, found; + + /* Only the super-user may do MOUNT. */ + if (!super_user) return(EPERM); + + /* If 'name' is not for a block special file, return error. */ + if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); + if ( (dev = name_to_dev(user_path)) == NO_DEV) return(err_code); + + /* Scan super block table to see if dev already mounted & find a free slot.*/ + sp = NIL_SUPER; + found = FALSE; + for (xp = &super_block[0]; xp < &super_block[NR_SUPERS]; xp++) { + if (xp->s_dev == dev) found = TRUE; /* is it mounted already? */ + if (xp->s_dev == NO_DEV) sp = xp; /* record free slot */ + } + if (found) return(EBUSY); /* already mounted */ + if (sp == NIL_SUPER) return(ENFILE); /* no super block available */ + + /* Open the device the file system lives on. */ + if (dev_open(dev, who, m_in.rd_only ? R_BIT : (R_BIT|W_BIT)) != OK) + return(EINVAL); + + /* Make the cache forget about blocks it has open on the filesystem */ + (void) do_sync(); + invalidate(dev); + + /* Fill in the super block. */ + sp->s_dev = dev; /* read_super() needs to know which dev */ + r = read_super(sp); + + /* Is it recognized as a Minix filesystem? */ + if (r != OK) { + dev_close(dev); + sp->s_dev = NO_DEV; + return(r); + } + + /* Now get the inode of the file to be mounted on. */ + if (fetch_name(m_in.name2, m_in.name2_length, M1) != OK) { + dev_close(dev); + sp->s_dev = NO_DEV; + return(err_code); + } + if ( (rip = eat_path(user_path)) == NIL_INODE) { + dev_close(dev); + sp->s_dev = NO_DEV; + return(err_code); + } + + /* It may not be busy. */ + r = OK; + if (rip->i_count > 1) r = EBUSY; + + /* It may not be special. */ + bits = rip->i_mode & I_TYPE; + if (bits == I_BLOCK_SPECIAL || bits == I_CHAR_SPECIAL) r = ENOTDIR; + + /* Get the root inode of the mounted file system. */ + root_ip = NIL_INODE; /* if 'r' not OK, make sure this is defined */ + if (r == OK) { + if ( (root_ip = get_inode(dev, ROOT_INODE)) == NIL_INODE) r = err_code; + } + if (root_ip != NIL_INODE && root_ip->i_mode == 0) { + r = EINVAL; + } + + /* File types of 'rip' and 'root_ip' may not conflict. */ + if (r == OK) { + mdir = ((rip->i_mode & I_TYPE) == I_DIRECTORY); /* TRUE iff dir */ + rdir = ((root_ip->i_mode & I_TYPE) == I_DIRECTORY); + if (!mdir && rdir) r = EISDIR; + } + + /* If error, return the super block and both inodes; release the maps. */ + if (r != OK) { + put_inode(rip); + put_inode(root_ip); + (void) do_sync(); + invalidate(dev); + dev_close(dev); + sp->s_dev = NO_DEV; + return(r); + } + + /* Nothing else can go wrong. Perform the mount. */ + rip->i_mount = I_MOUNT; /* this bit says the inode is mounted on */ + sp->s_imount = rip; + sp->s_isup = root_ip; + sp->s_rd_only = m_in.rd_only; + return(OK); +} + + +/*===========================================================================* + * do_umount * + *===========================================================================*/ +PUBLIC int do_umount() +{ +/* Perform the umount(name) system call. */ + dev_t dev; + + /* Only the super-user may do UMOUNT. */ + if (!super_user) return(EPERM); + + /* If 'name' is not for a block special file, return error. */ + if (fetch_name(m_in.name, m_in.name_length, M3) != OK) return(err_code); + if ( (dev = name_to_dev(user_path)) == NO_DEV) return(err_code); + + return(unmount(dev)); +} + + +/*===========================================================================* + * unmount * + *===========================================================================*/ +PUBLIC int unmount(dev) +Dev_t dev; +{ +/* Unmount a file system by device number. */ + register struct inode *rip; + struct super_block *sp, *sp1; + int count; + + /* See if the mounted device is busy. Only 1 inode using it should be + * open -- the root inode -- and that inode only 1 time. + */ + count = 0; + for (rip = &inode[0]; rip< &inode[NR_INODES]; rip++) + if (rip->i_count > 0 && rip->i_dev == dev) count += rip->i_count; + if (count > 1) return(EBUSY); /* can't umount a busy file system */ + + /* Find the super block. */ + sp = NIL_SUPER; + for (sp1 = &super_block[0]; sp1 < &super_block[NR_SUPERS]; sp1++) { + if (sp1->s_dev == dev) { + sp = sp1; + break; + } + } + + /* Sync the disk, and invalidate cache. */ + (void) do_sync(); /* force any cached blocks out of memory */ + invalidate(dev); /* invalidate cache entries for this dev */ + if (sp == NIL_SUPER) { + return(EINVAL); + } + + /* Close the device the file system lives on. */ + dev_close(dev); + + /* Finish off the unmount. */ + sp->s_imount->i_mount = NO_MOUNT; /* inode returns to normal */ + put_inode(sp->s_imount); /* release the inode mounted on */ + put_inode(sp->s_isup); /* release the root inode of the mounted fs */ + sp->s_imount = NIL_INODE; + sp->s_dev = NO_DEV; + return(OK); +} + +/*===========================================================================* + * name_to_dev * + *===========================================================================*/ +PRIVATE dev_t name_to_dev(path) +char *path; /* pointer to path name */ +{ +/* Convert the block special file 'path' to a device number. If 'path' + * is not a block special file, return error code in 'err_code'. + */ + + register struct inode *rip; + register dev_t dev; + + /* If 'path' can't be opened, give up immediately. */ + if ( (rip = eat_path(path)) == NIL_INODE) return(NO_DEV); + + /* If 'path' is not a block special file, return error. */ + if ( (rip->i_mode & I_TYPE) != I_BLOCK_SPECIAL) { + err_code = ENOTBLK; + put_inode(rip); + return(NO_DEV); + } + + /* Extract the device number. */ + dev = (dev_t) rip->i_zone[0]; + put_inode(rip); + return(dev); +} diff --git a/servers/fs/open.c b/servers/fs/open.c new file mode 100644 index 000000000..c567b3f28 --- /dev/null +++ b/servers/fs/open.c @@ -0,0 +1,487 @@ +/* This file contains the procedures for creating, opening, closing, and + * seeking on files. + * + * The entry points into this file are + * do_creat: perform the CREAT system call + * do_open: perform the OPEN system call + * do_mknod: perform the MKNOD system call + * do_mkdir: perform the MKDIR system call + * do_close: perform the CLOSE system call + * do_lseek: perform the LSEEK system call + */ + +#include "fs.h" +#include <sys/stat.h> +#include <fcntl.h> +#include <minix/callnr.h> +#include <minix/com.h> +#include "buf.h" +#include "dmap.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "lock.h" +#include "param.h" +#include "super.h" + +PRIVATE char mode_map[] = {R_BIT, W_BIT, R_BIT|W_BIT, 0}; + +FORWARD _PROTOTYPE( int common_open, (int oflags, Mode_t omode) ); +FORWARD _PROTOTYPE( int pipe_open, (struct inode *rip,Mode_t bits,int oflags)); +FORWARD _PROTOTYPE( struct inode *new_node, (char *path, Mode_t bits, + zone_t z0) ); + + +/*===========================================================================* + * do_creat * + *===========================================================================*/ +PUBLIC int do_creat() +{ +/* Perform the creat(name, mode) system call. */ + int r; + + if (fetch_name(m_in.name, m_in.name_length, M3) != OK) return(err_code); + r = common_open(O_WRONLY | O_CREAT | O_TRUNC, (mode_t) m_in.mode); + return(r); +} + + +/*===========================================================================* + * do_open * + *===========================================================================*/ +PUBLIC int do_open() +{ +/* Perform the open(name, flags,...) system call. */ + + int create_mode = 0; /* is really mode_t but this gives problems */ + int r; + + /* If O_CREAT is set, open has three parameters, otherwise two. */ + if (m_in.mode & O_CREAT) { + create_mode = m_in.c_mode; + r = fetch_name(m_in.c_name, m_in.name1_length, M1); + } else { + r = fetch_name(m_in.name, m_in.name_length, M3); + } + + if (r != OK) return(err_code); /* name was bad */ + r = common_open(m_in.mode, create_mode); + return(r); +} + + +/*===========================================================================* + * common_open * + *===========================================================================*/ +PRIVATE int common_open(oflags, omode) +register int oflags; +mode_t omode; +{ +/* Common code from do_creat and do_open. */ + + register struct inode *rip; + int r, b, exist = TRUE; + dev_t dev; + mode_t bits; + off_t pos; + struct filp *fil_ptr, *filp2; + + /* Remap the bottom two bits of oflags. */ + bits = (mode_t) mode_map[oflags & O_ACCMODE]; + + /* See if file descriptor and filp slots are available. */ + if ( (r = get_fd(0, bits, &m_in.fd, &fil_ptr)) != OK) return(r); + + /* If O_CREATE is set, try to make the file. */ + if (oflags & O_CREAT) { + /* Create a new inode by calling new_node(). */ + omode = I_REGULAR | (omode & ALL_MODES & fp->fp_umask); + rip = new_node(user_path, omode, NO_ZONE); + r = err_code; + if (r == OK) exist = FALSE; /* we just created the file */ + else if (r != EEXIST) return(r); /* other error */ + else exist = !(oflags & O_EXCL); /* file exists, if the O_EXCL + flag is set this is an error */ + } else { + /* Scan path name. */ + if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); + } + + /* Claim the file descriptor and filp slot and fill them in. */ + fp->fp_filp[m_in.fd] = fil_ptr; + fil_ptr->filp_count = 1; + fil_ptr->filp_ino = rip; + fil_ptr->filp_flags = oflags; + + /* Only do the normal open code if we didn't just create the file. */ + if (exist) { + /* Check protections. */ + if ((r = forbidden(rip, bits)) == OK) { + /* Opening reg. files directories and special files differ. */ + switch (rip->i_mode & I_TYPE) { + case I_REGULAR: + /* Truncate regular file if O_TRUNC. */ + if (oflags & O_TRUNC) { + if ((r = forbidden(rip, W_BIT)) !=OK) break; + truncate(rip); + wipe_inode(rip); + /* Send the inode from the inode cache to the + * block cache, so it gets written on the next + * cache flush. + */ + rw_inode(rip, WRITING); + } + break; + + case I_DIRECTORY: + /* Directories may be read but not written. */ + r = (bits & W_BIT ? EISDIR : OK); + break; + + case I_CHAR_SPECIAL: + case I_BLOCK_SPECIAL: + /* Invoke the driver for special processing. */ + dev = (dev_t) rip->i_zone[0]; + r = dev_open(dev, who, bits | (oflags & ~O_ACCMODE)); + break; + + case I_NAMED_PIPE: + oflags |= O_APPEND; /* force append mode */ + fil_ptr->filp_flags = oflags; + r = pipe_open(rip, bits, oflags); + if (r != ENXIO) { + /* See if someone else is doing a rd or wt on + * the FIFO. If so, use its filp entry so the + * file position will be automatically shared. + */ + b = (bits & R_BIT ? R_BIT : W_BIT); + fil_ptr->filp_count = 0; /* don't find self */ + if ((filp2 = find_filp(rip, b)) != NIL_FILP) { + /* Co-reader or writer found. Use it.*/ + fp->fp_filp[m_in.fd] = filp2; + filp2->filp_count++; + filp2->filp_ino = rip; + filp2->filp_flags = oflags; + + /* i_count was incremented incorrectly + * by eatpath above, not knowing that + * we were going to use an existing + * filp entry. Correct this error. + */ + rip->i_count--; + } else { + /* Nobody else found. Restore filp. */ + fil_ptr->filp_count = 1; + if (b == R_BIT) + pos = rip->i_zone[V2_NR_DZONES+0]; + else + pos = rip->i_zone[V2_NR_DZONES+1]; + fil_ptr->filp_pos = pos; + } + } + break; + } + } + } + + /* If error, release inode. */ + if (r != OK) { + if (r == SUSPEND) return(r); /* Oops, just suspended */ + fp->fp_filp[m_in.fd] = NIL_FILP; + fil_ptr->filp_count= 0; + put_inode(rip); + return(r); + } + + return(m_in.fd); +} + + +/*===========================================================================* + * new_node * + *===========================================================================*/ +PRIVATE struct inode *new_node(path, bits, z0) +char *path; /* pointer to path name */ +mode_t bits; /* mode of the new inode */ +zone_t z0; /* zone number 0 for new inode */ +{ +/* New_node() is called by common_open(), do_mknod(), and do_mkdir(). + * In all cases it allocates a new inode, makes a directory entry for it on + * the path 'path', and initializes it. It returns a pointer to the inode if + * it can do this; otherwise it returns NIL_INODE. It always sets 'err_code' + * to an appropriate value (OK or an error code). + */ + + register struct inode *rlast_dir_ptr, *rip; + register int r; + char string[NAME_MAX]; + + /* See if the path can be opened down to the last directory. */ + if ((rlast_dir_ptr = last_dir(path, string)) == NIL_INODE) return(NIL_INODE); + + /* The final directory is accessible. Get final component of the path. */ + rip = advance(rlast_dir_ptr, string); + if ( rip == NIL_INODE && err_code == ENOENT) { + /* Last path component does not exist. Make new directory entry. */ + if ( (rip = alloc_inode(rlast_dir_ptr->i_dev, bits)) == NIL_INODE) { + /* Can't creat new inode: out of inodes. */ + put_inode(rlast_dir_ptr); + return(NIL_INODE); + } + + /* Force inode to the disk before making directory entry to make + * the system more robust in the face of a crash: an inode with + * no directory entry is much better than the opposite. + */ + rip->i_nlinks++; + rip->i_zone[0] = z0; /* major/minor device numbers */ + rw_inode(rip, WRITING); /* force inode to disk now */ + + /* New inode acquired. Try to make directory entry. */ + if ((r = search_dir(rlast_dir_ptr, string, &rip->i_num,ENTER)) != OK) { + put_inode(rlast_dir_ptr); + rip->i_nlinks--; /* pity, have to free disk inode */ + rip->i_dirt = DIRTY; /* dirty inodes are written out */ + put_inode(rip); /* this call frees the inode */ + err_code = r; + return(NIL_INODE); + } + + } else { + /* Either last component exists, or there is some problem. */ + if (rip != NIL_INODE) + r = EEXIST; + else + r = err_code; + } + + /* Return the directory inode and exit. */ + put_inode(rlast_dir_ptr); + err_code = r; + return(rip); +} + + +/*===========================================================================* + * pipe_open * + *===========================================================================*/ +PRIVATE int pipe_open(rip, bits, oflags) +register struct inode *rip; +register mode_t bits; +register int oflags; +{ +/* This function is called from common_open. It checks if + * there is at least one reader/writer pair for the pipe, if not + * it suspends the caller, otherwise it revives all other blocked + * processes hanging on the pipe. + */ + + rip->i_pipe = I_PIPE; + if (find_filp(rip, bits & W_BIT ? R_BIT : W_BIT) == NIL_FILP) { + if (oflags & O_NONBLOCK) { + if (bits & W_BIT) return(ENXIO); + } else { + suspend(XPOPEN); /* suspend caller */ + return(SUSPEND); + } + } else if (susp_count > 0) {/* revive blocked processes */ + release(rip, OPEN, susp_count); + release(rip, CREAT, susp_count); + } + return(OK); +} + + +/*===========================================================================* + * do_mknod * + *===========================================================================*/ +PUBLIC int do_mknod() +{ +/* Perform the mknod(name, mode, addr) system call. */ + + register mode_t bits, mode_bits; + struct inode *ip; + + /* Only the super_user may make nodes other than fifos. */ + mode_bits = (mode_t) m_in.mk_mode; /* mode of the inode */ + if (!super_user && ((mode_bits & I_TYPE) != I_NAMED_PIPE)) return(EPERM); + if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); + bits = (mode_bits & I_TYPE) | (mode_bits & ALL_MODES & fp->fp_umask); + ip = new_node(user_path, bits, (zone_t) m_in.mk_z0); + put_inode(ip); + return(err_code); +} + + +/*===========================================================================* + * do_mkdir * + *===========================================================================*/ +PUBLIC int do_mkdir() +{ +/* Perform the mkdir(name, mode) system call. */ + + int r1, r2; /* status codes */ + ino_t dot, dotdot; /* inode numbers for . and .. */ + mode_t bits; /* mode bits for the new inode */ + char string[NAME_MAX]; /* last component of the new dir's path name */ + register struct inode *rip, *ldirp; + + /* Check to see if it is possible to make another link in the parent dir. */ + if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); + ldirp = last_dir(user_path, string); /* pointer to new dir's parent */ + if (ldirp == NIL_INODE) return(err_code); + if (ldirp->i_nlinks >= (ldirp->i_sp->s_version == V1 ? CHAR_MAX : SHRT_MAX)) { + put_inode(ldirp); /* return parent */ + return(EMLINK); + } + + /* Next make the inode. If that fails, return error code. */ + bits = I_DIRECTORY | (m_in.mode & RWX_MODES & fp->fp_umask); + rip = new_node(user_path, bits, (zone_t) 0); + if (rip == NIL_INODE || err_code == EEXIST) { + put_inode(rip); /* can't make dir: it already exists */ + put_inode(ldirp); /* return parent too */ + return(err_code); + } + + /* Get the inode numbers for . and .. to enter in the directory. */ + dotdot = ldirp->i_num; /* parent's inode number */ + dot = rip->i_num; /* inode number of the new dir itself */ + + /* Now make dir entries for . and .. unless the disk is completely full. */ + /* Use dot1 and dot2, so the mode of the directory isn't important. */ + rip->i_mode = bits; /* set mode */ + r1 = search_dir(rip, dot1, &dot, ENTER); /* enter . in the new dir */ + r2 = search_dir(rip, dot2, &dotdot, ENTER); /* enter .. in the new dir */ + + /* If both . and .. were successfully entered, increment the link counts. */ + if (r1 == OK && r2 == OK) { + /* Normal case. It was possible to enter . and .. in the new dir. */ + rip->i_nlinks++; /* this accounts for . */ + ldirp->i_nlinks++; /* this accounts for .. */ + ldirp->i_dirt = DIRTY; /* mark parent's inode as dirty */ + } else { + /* It was not possible to enter . or .. probably disk was full. */ + (void) search_dir(ldirp, string, (ino_t *) 0, DELETE); + rip->i_nlinks--; /* undo the increment done in new_node() */ + } + rip->i_dirt = DIRTY; /* either way, i_nlinks has changed */ + + put_inode(ldirp); /* return the inode of the parent dir */ + put_inode(rip); /* return the inode of the newly made dir */ + return(err_code); /* new_node() always sets 'err_code' */ +} + + +/*===========================================================================* + * do_close * + *===========================================================================*/ +PUBLIC int do_close() +{ +/* Perform the close(fd) system call. */ + + register struct filp *rfilp; + register struct inode *rip; + struct file_lock *flp; + int rw, mode_word, lock_count; + dev_t dev; + + /* First locate the inode that belongs to the file descriptor. */ + if ( (rfilp = get_filp(m_in.fd)) == NIL_FILP) return(err_code); + rip = rfilp->filp_ino; /* 'rip' points to the inode */ + + if (rfilp->filp_count - 1 == 0 && rfilp->filp_mode != FILP_CLOSED) { + /* Check to see if the file is special. */ + mode_word = rip->i_mode & I_TYPE; + if (mode_word == I_CHAR_SPECIAL || mode_word == I_BLOCK_SPECIAL) { + dev = (dev_t) rip->i_zone[0]; + if (mode_word == I_BLOCK_SPECIAL) { + /* Invalidate cache entries unless special is mounted + * or ROOT + */ + if (!mounted(rip)) { + (void) do_sync(); /* purge cache */ + invalidate(dev); + } + } + /* Do any special processing on device close. */ + dev_close(dev); + } + } + + /* If the inode being closed is a pipe, release everyone hanging on it. */ + if (rip->i_pipe == I_PIPE) { + rw = (rfilp->filp_mode & R_BIT ? WRITE : READ); + release(rip, rw, NR_PROCS); + } + + /* If a write has been done, the inode is already marked as DIRTY. */ + if (--rfilp->filp_count == 0) { + if (rip->i_pipe == I_PIPE && rip->i_count > 1) { + /* Save the file position in the i-node in case needed later. + * The read and write positions are saved separately. The + * last 3 zones in the i-node are not used for (named) pipes. + */ + if (rfilp->filp_mode == R_BIT) + rip->i_zone[V2_NR_DZONES+0] = (zone_t) rfilp->filp_pos; + else + rip->i_zone[V2_NR_DZONES+1] = (zone_t) rfilp->filp_pos; + } + put_inode(rip); + } + + fp->fp_cloexec &= ~(1L << m_in.fd); /* turn off close-on-exec bit */ + fp->fp_filp[m_in.fd] = NIL_FILP; + + /* Check to see if the file is locked. If so, release all locks. */ + if (nr_locks == 0) return(OK); + lock_count = nr_locks; /* save count of locks */ + for (flp = &file_lock[0]; flp < &file_lock[NR_LOCKS]; flp++) { + if (flp->lock_type == 0) continue; /* slot not in use */ + if (flp->lock_inode == rip && flp->lock_pid == fp->fp_pid) { + flp->lock_type = 0; + nr_locks--; + } + } + if (nr_locks < lock_count) lock_revive(); /* lock released */ + return(OK); +} + + +/*===========================================================================* + * do_lseek * + *===========================================================================*/ +PUBLIC int do_lseek() +{ +/* Perform the lseek(ls_fd, offset, whence) system call. */ + + register struct filp *rfilp; + register off_t pos; + + /* Check to see if the file descriptor is valid. */ + if ( (rfilp = get_filp(m_in.ls_fd)) == NIL_FILP) return(err_code); + + /* No lseek on pipes. */ + if (rfilp->filp_ino->i_pipe == I_PIPE) return(ESPIPE); + + /* The value of 'whence' determines the start position to use. */ + switch(m_in.whence) { + case 0: pos = 0; break; + case 1: pos = rfilp->filp_pos; break; + case 2: pos = rfilp->filp_ino->i_size; break; + default: return(EINVAL); + } + + /* Check for overflow. */ + if (((long)m_in.offset > 0) && ((long)(pos + m_in.offset) < (long)pos)) + return(EINVAL); + if (((long)m_in.offset < 0) && ((long)(pos + m_in.offset) > (long)pos)) + return(EINVAL); + pos = pos + m_in.offset; + + if (pos != rfilp->filp_pos) + rfilp->filp_ino->i_seek = ISEEK; /* inhibit read ahead */ + rfilp->filp_pos = pos; + m_out.reply_l1 = pos; /* insert the long into the output message */ + return(OK); +} diff --git a/servers/fs/param.h b/servers/fs/param.h new file mode 100644 index 000000000..dfd5402e8 --- /dev/null +++ b/servers/fs/param.h @@ -0,0 +1,58 @@ +/* The following names are synonyms for the variables in the input message. */ +#define acc_time m2_l1 +#define addr m1_i3 +#define buffer m1_p1 +#define child m1_i2 +#define co_mode m1_i1 +#define eff_grp_id m1_i3 +#define eff_user_id m1_i3 +#define erki m1_p1 +#define fd m1_i1 +#define fd2 m1_i2 +#define ioflags m1_i3 +#define group m1_i3 +#define real_grp_id m1_i2 +#define ls_fd m2_i1 +#define mk_mode m1_i2 +#define mk_z0 m1_i3 +#define mode m3_i2 +#define c_mode m1_i3 +#define c_name m1_p1 +#define name m3_p1 +#define name1 m1_p1 +#define name2 m1_p2 +#define name_length m3_i1 +#define name1_length m1_i1 +#define name2_length m1_i2 +#define nbytes m1_i2 +#define offset m2_l1 +#define owner m1_i2 +#define parent m1_i1 +#define pathname m3_ca1 +#define pid m1_i3 +#define pro m1_i1 +#define rd_only m1_i3 +#define real_user_id m1_i2 +#define request m1_i2 +#define sig m1_i2 +#define slot1 m1_i1 +#define tp m2_l1 +#define utime_actime m2_l1 +#define utime_modtime m2_l2 +#define utime_file m2_p1 +#define utime_length m2_i1 +#define utime_strlen m2_i2 +#define whence m2_i2 +#define svrctl_req m2_i1 +#define svrctl_argp m2_p1 + +/* The following names are synonyms for the variables in the output message. */ +#define reply_type m_type +#define reply_l1 m2_l1 +#define reply_i1 m1_i1 +#define reply_i2 m1_i2 +#define reply_t1 m4_l1 +#define reply_t2 m4_l2 +#define reply_t3 m4_l3 +#define reply_t4 m4_l4 +#define reply_t5 m4_l5 diff --git a/servers/fs/path.c b/servers/fs/path.c new file mode 100644 index 000000000..419ccb54d --- /dev/null +++ b/servers/fs/path.c @@ -0,0 +1,380 @@ +/* This file contains the procedures that look up path names in the directory + * system and determine the inode number that goes with a given path name. + * + * The entry points into this file are + * eat_path: the 'main' routine of the path-to-inode conversion mechanism + * last_dir: find the final directory on a given path + * advance: parse one component of a path name + * search_dir: search a directory for a string and return its inode number + */ + +#include "fs.h" +#include <string.h> +#include <minix/callnr.h> +#include "buf.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "super.h" + +PUBLIC char dot1[2] = "."; /* used for search_dir to bypass the access */ +PUBLIC char dot2[3] = ".."; /* permissions for . and .. */ + +FORWARD _PROTOTYPE( char *get_name, (char *old_name, char string [NAME_MAX]) ); + +/*===========================================================================* + * eat_path * + *===========================================================================*/ +PUBLIC struct inode *eat_path(path) +char *path; /* the path name to be parsed */ +{ +/* Parse the path 'path' and put its inode in the inode table. If not possible, + * return NIL_INODE as function value and an error code in 'err_code'. + */ + + register struct inode *ldip, *rip; + char string[NAME_MAX]; /* hold 1 path component name here */ + + /* First open the path down to the final directory. */ + if ( (ldip = last_dir(path, string)) == NIL_INODE) { + return(NIL_INODE); /* we couldn't open final directory */ + } + + /* The path consisting only of "/" is a special case, check for it. */ + if (string[0] == '\0') return(ldip); + + /* Get final component of the path. */ + rip = advance(ldip, string); + put_inode(ldip); + return(rip); +} + + +/*===========================================================================* + * last_dir * + *===========================================================================*/ +PUBLIC struct inode *last_dir(path, string) +char *path; /* the path name to be parsed */ +char string[NAME_MAX]; /* the final component is returned here */ +{ +/* Given a path, 'path', located in the fs address space, parse it as + * far as the last directory, fetch the inode for the last directory into + * the inode table, and return a pointer to the inode. In + * addition, return the final component of the path in 'string'. + * If the last directory can't be opened, return NIL_INODE and + * the reason for failure in 'err_code'. + */ + + register struct inode *rip; + register char *new_name; + register struct inode *new_ip; + + /* Is the path absolute or relative? Initialize 'rip' accordingly. */ + rip = (*path == '/' ? fp->fp_rootdir : fp->fp_workdir); + + /* If dir has been removed or path is empty, return ENOENT. */ + if (rip->i_nlinks == 0 || *path == '\0') { + err_code = ENOENT; + return(NIL_INODE); + } + + dup_inode(rip); /* inode will be returned with put_inode */ + + /* Scan the path component by component. */ + while (TRUE) { + /* Extract one component. */ + if ( (new_name = get_name(path, string)) == (char*) 0) { + put_inode(rip); /* bad path in user space */ + return(NIL_INODE); + } + if (*new_name == '\0') + if ( (rip->i_mode & I_TYPE) == I_DIRECTORY) { + return(rip); /* normal exit */ + } else { + /* last file of path prefix is not a directory */ + put_inode(rip); + err_code = ENOTDIR; + return(NIL_INODE); + } + + /* There is more path. Keep parsing. */ + new_ip = advance(rip, string); + put_inode(rip); /* rip either obsolete or irrelevant */ + if (new_ip == NIL_INODE) return(NIL_INODE); + + /* The call to advance() succeeded. Fetch next component. */ + path = new_name; + rip = new_ip; + } +} + + +/*===========================================================================* + * get_name * + *===========================================================================*/ +PRIVATE char *get_name(old_name, string) +char *old_name; /* path name to parse */ +char string[NAME_MAX]; /* component extracted from 'old_name' */ +{ +/* Given a pointer to a path name in fs space, 'old_name', copy the next + * component to 'string' and pad with zeros. A pointer to that part of + * the name as yet unparsed is returned. Roughly speaking, + * 'get_name' = 'old_name' - 'string'. + * + * This routine follows the standard convention that /usr/ast, /usr//ast, + * //usr///ast and /usr/ast/ are all equivalent. + */ + + register int c; + register char *np, *rnp; + + np = string; /* 'np' points to current position */ + rnp = old_name; /* 'rnp' points to unparsed string */ + while ( (c = *rnp) == '/') rnp++; /* skip leading slashes */ + + /* Copy the unparsed path, 'old_name', to the array, 'string'. */ + while ( rnp < &old_name[PATH_MAX] && c != '/' && c != '\0') { + if (np < &string[NAME_MAX]) *np++ = c; + c = *++rnp; /* advance to next character */ + } + + /* To make /usr/ast/ equivalent to /usr/ast, skip trailing slashes. */ + while (c == '/' && rnp < &old_name[PATH_MAX]) c = *++rnp; + + if (np < &string[NAME_MAX]) *np = '\0'; /* Terminate string */ + + if (rnp >= &old_name[PATH_MAX]) { + err_code = ENAMETOOLONG; + return((char *) 0); + } + return(rnp); +} + + +/*===========================================================================* + * advance * + *===========================================================================*/ +PUBLIC struct inode *advance(dirp, string) +struct inode *dirp; /* inode for directory to be searched */ +char string[NAME_MAX]; /* component name to look for */ +{ +/* Given a directory and a component of a path, look up the component in + * the directory, find the inode, open it, and return a pointer to its inode + * slot. If it can't be done, return NIL_INODE. + */ + + register struct inode *rip; + struct inode *rip2; + register struct super_block *sp; + int r, inumb; + dev_t mnt_dev; + ino_t numb; + + /* If 'string' is empty, yield same inode straight away. */ + if (string[0] == '\0') { return(get_inode(dirp->i_dev, (int) dirp->i_num)); } + + /* Check for NIL_INODE. */ + if (dirp == NIL_INODE) { return(NIL_INODE); } + + /* If 'string' is not present in the directory, signal error. */ + if ( (r = search_dir(dirp, string, &numb, LOOK_UP)) != OK) { + err_code = r; + return(NIL_INODE); + } + + /* Don't go beyond the current root directory, unless the string is dot2. */ + if (dirp == fp->fp_rootdir && strcmp(string, "..") == 0 && string != dot2) + return(get_inode(dirp->i_dev, (int) dirp->i_num)); + + /* The component has been found in the directory. Get inode. */ + if ( (rip = get_inode(dirp->i_dev, (int) numb)) == NIL_INODE) { + return(NIL_INODE); + } + + if (rip->i_num == ROOT_INODE) + if (dirp->i_num == ROOT_INODE) { + if (string[1] == '.') { + for (sp = &super_block[1]; sp < &super_block[NR_SUPERS]; sp++){ + if (sp->s_dev == rip->i_dev) { + /* Release the root inode. Replace by the + * inode mounted on. + */ + put_inode(rip); + mnt_dev = sp->s_imount->i_dev; + inumb = (int) sp->s_imount->i_num; + rip2 = get_inode(mnt_dev, inumb); + rip = advance(rip2, string); + put_inode(rip2); + break; + } + } + } + } + if (rip == NIL_INODE) return(NIL_INODE); + + /* See if the inode is mounted on. If so, switch to root directory of the + * mounted file system. The super_block provides the linkage between the + * inode mounted on and the root directory of the mounted file system. + */ + while (rip != NIL_INODE && rip->i_mount == I_MOUNT) { + /* The inode is indeed mounted on. */ + for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) { + if (sp->s_imount == rip) { + /* Release the inode mounted on. Replace by the + * inode of the root inode of the mounted device. + */ + put_inode(rip); + rip = get_inode(sp->s_dev, ROOT_INODE); + break; + } + } + } + return(rip); /* return pointer to inode's component */ +} + + +/*===========================================================================* + * search_dir * + *===========================================================================*/ +PUBLIC int search_dir(ldir_ptr, string, numb, flag) +register struct inode *ldir_ptr; /* ptr to inode for dir to search */ +char string[NAME_MAX]; /* component to search for */ +ino_t *numb; /* pointer to inode number */ +int flag; /* LOOK_UP, ENTER, DELETE or IS_EMPTY */ +{ +/* This function searches the directory whose inode is pointed to by 'ldip': + * if (flag == ENTER) enter 'string' in the directory with inode # '*numb'; + * if (flag == DELETE) delete 'string' from the directory; + * if (flag == LOOK_UP) search for 'string' and return inode # in 'numb'; + * if (flag == IS_EMPTY) return OK if only . and .. in dir else ENOTEMPTY; + * + * if 'string' is dot1 or dot2, no access permissions are checked. + */ + + register struct direct *dp; + register struct buf *bp; + int i, r, e_hit, t, match; + mode_t bits; + off_t pos; + unsigned new_slots, old_slots; + block_t b; + struct super_block *sp; + int extended = 0; + + /* If 'ldir_ptr' is not a pointer to a dir inode, error. */ + if ( (ldir_ptr->i_mode & I_TYPE) != I_DIRECTORY) return(ENOTDIR); + + r = OK; + + if (flag != IS_EMPTY) { + bits = (flag == LOOK_UP ? X_BIT : W_BIT | X_BIT); + + if (string == dot1 || string == dot2) { + if (flag != LOOK_UP) r = read_only(ldir_ptr); + /* only a writable device is required. */ + } + else r = forbidden(ldir_ptr, bits); /* check access permissions */ + } + if (r != OK) return(r); + + /* Step through the directory one block at a time. */ + old_slots = (unsigned) (ldir_ptr->i_size/DIR_ENTRY_SIZE); + new_slots = 0; + e_hit = FALSE; + match = 0; /* set when a string match occurs */ + + for (pos = 0; pos < ldir_ptr->i_size; pos += ldir_ptr->i_sp->s_block_size) { + b = read_map(ldir_ptr, pos); /* get block number */ + + /* Since directories don't have holes, 'b' cannot be NO_BLOCK. */ + bp = get_block(ldir_ptr->i_dev, b, NORMAL); /* get a dir block */ + + if(bp == NO_BLOCK) + panic("get_block returned NO_BLOCK", NO_NUM); + + /* Search a directory block. */ + for (dp = &bp->b_dir[0]; dp < &bp->b_dir[NR_DIR_ENTRIES(ldir_ptr->i_sp->s_block_size)]; dp++) { + if (++new_slots > old_slots) { /* not found, but room left */ + if (flag == ENTER) e_hit = TRUE; + break; + } + + /* Match occurs if string found. */ + if (flag != ENTER && dp->d_ino != 0) { + if (flag == IS_EMPTY) { + /* If this test succeeds, dir is not empty. */ + if (strcmp(dp->d_name, "." ) != 0 && + strcmp(dp->d_name, "..") != 0) match = 1; + } else { + if (strncmp(dp->d_name, string, NAME_MAX) == 0) { + match = 1; + } + } + } + + if (match) { + /* LOOK_UP or DELETE found what it wanted. */ + r = OK; + if (flag == IS_EMPTY) r = ENOTEMPTY; + else if (flag == DELETE) { + /* Save d_ino for recovery. */ + t = NAME_MAX - sizeof(ino_t); + *((ino_t *) &dp->d_name[t]) = dp->d_ino; + dp->d_ino = 0; /* erase entry */ + bp->b_dirt = DIRTY; + ldir_ptr->i_update |= CTIME | MTIME; + ldir_ptr->i_dirt = DIRTY; + } else { + sp = ldir_ptr->i_sp; /* 'flag' is LOOK_UP */ + *numb = conv4(sp->s_native, (int) dp->d_ino); + } + put_block(bp, DIRECTORY_BLOCK); + return(r); + } + + + /* Check for free slot for the benefit of ENTER. */ + if (flag == ENTER && dp->d_ino == 0) { + e_hit = TRUE; /* we found a free slot */ + break; + } + } + + /* The whole block has been searched or ENTER has a free slot. */ + if (e_hit) break; /* e_hit set if ENTER can be performed now */ + put_block(bp, DIRECTORY_BLOCK); /* otherwise, continue searching dir */ + } + + /* The whole directory has now been searched. */ + if (flag != ENTER) { + return(flag == IS_EMPTY ? OK : ENOENT); + } + + /* This call is for ENTER. If no free slot has been found so far, try to + * extend directory. + */ + if (e_hit == FALSE) { /* directory is full and no room left in last block */ + new_slots++; /* increase directory size by 1 entry */ + if (new_slots == 0) return(EFBIG); /* dir size limited by slot count */ + if ( (bp = new_block(ldir_ptr, ldir_ptr->i_size)) == NIL_BUF) + return(err_code); + dp = &bp->b_dir[0]; + extended = 1; + } + + /* 'bp' now points to a directory block with space. 'dp' points to slot. */ + (void) memset(dp->d_name, 0, (size_t) NAME_MAX); /* clear entry */ + for (i = 0; string[i] && i < NAME_MAX; i++) dp->d_name[i] = string[i]; + sp = ldir_ptr->i_sp; + dp->d_ino = conv4(sp->s_native, (int) *numb); + bp->b_dirt = DIRTY; + put_block(bp, DIRECTORY_BLOCK); + ldir_ptr->i_update |= CTIME | MTIME; /* mark mtime for update later */ + ldir_ptr->i_dirt = DIRTY; + if (new_slots > old_slots) { + ldir_ptr->i_size = (off_t) new_slots * DIR_ENTRY_SIZE; + /* Send the change to disk if the directory is extended. */ + if (extended) rw_inode(ldir_ptr, WRITING); + } + return(OK); +} diff --git a/servers/fs/pipe.c b/servers/fs/pipe.c new file mode 100644 index 000000000..9262620c7 --- /dev/null +++ b/servers/fs/pipe.c @@ -0,0 +1,305 @@ +/* This file deals with the suspension and revival of processes. A process can + * be suspended because it wants to read or write from a pipe and can't, or + * because it wants to read or write from a special file and can't. When a + * process can't continue it is suspended, and revived later when it is able + * to continue. + * + * The entry points into this file are + * do_pipe: perform the PIPE system call + * pipe_check: check to see that a read or write on a pipe is feasible now + * suspend: suspend a process that cannot do a requested read or write + * release: check to see if a suspended process can be released and do it + * revive: mark a suspended process as able to run again + * do_unpause: a signal has been sent to a process; see if it suspended + */ + +#include "fs.h" +#include <fcntl.h> +#include <signal.h> +#include <minix/callnr.h> +#include <minix/com.h> +#include "dmap.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "param.h" +#include "super.h" + +/*===========================================================================* + * do_pipe * + *===========================================================================*/ +PUBLIC int do_pipe() +{ +/* Perform the pipe(fil_des) system call. */ + + register struct fproc *rfp; + register struct inode *rip; + int r; + struct filp *fil_ptr0, *fil_ptr1; + int fil_des[2]; /* reply goes here */ + + /* Acquire two file descriptors. */ + rfp = fp; + if ( (r = get_fd(0, R_BIT, &fil_des[0], &fil_ptr0)) != OK) return(r); + rfp->fp_filp[fil_des[0]] = fil_ptr0; + fil_ptr0->filp_count = 1; + if ( (r = get_fd(0, W_BIT, &fil_des[1], &fil_ptr1)) != OK) { + rfp->fp_filp[fil_des[0]] = NIL_FILP; + fil_ptr0->filp_count = 0; + return(r); + } + rfp->fp_filp[fil_des[1]] = fil_ptr1; + fil_ptr1->filp_count = 1; + + /* Make the inode on the pipe device. */ + if ( (rip = alloc_inode(root_dev, I_REGULAR) ) == NIL_INODE) { + rfp->fp_filp[fil_des[0]] = NIL_FILP; + fil_ptr0->filp_count = 0; + rfp->fp_filp[fil_des[1]] = NIL_FILP; + fil_ptr1->filp_count = 0; + return(err_code); + } + + if (read_only(rip) != OK) panic("pipe device is read only", NO_NUM); + + rip->i_pipe = I_PIPE; + rip->i_mode &= ~I_REGULAR; + rip->i_mode |= I_NAMED_PIPE; /* pipes and FIFOs have this bit set */ + fil_ptr0->filp_ino = rip; + fil_ptr0->filp_flags = O_RDONLY; + dup_inode(rip); /* for double usage */ + fil_ptr1->filp_ino = rip; + fil_ptr1->filp_flags = O_WRONLY; + rw_inode(rip, WRITING); /* mark inode as allocated */ + m_out.reply_i1 = fil_des[0]; + m_out.reply_i2 = fil_des[1]; + rip->i_update = ATIME | CTIME | MTIME; + return(OK); +} + + +/*===========================================================================* + * pipe_check * + *===========================================================================*/ +PUBLIC int pipe_check(rip, rw_flag, oflags, bytes, position, canwrite) +register struct inode *rip; /* the inode of the pipe */ +int rw_flag; /* READING or WRITING */ +int oflags; /* flags set by open or fcntl */ +register int bytes; /* bytes to be read or written (all chunks) */ +register off_t position; /* current file position */ +int *canwrite; /* return: number of bytes we can write */ +{ +/* Pipes are a little different. If a process reads from an empty pipe for + * which a writer still exists, suspend the reader. If the pipe is empty + * and there is no writer, return 0 bytes. If a process is writing to a + * pipe and no one is reading from it, give a broken pipe error. + */ + + /* If reading, check for empty pipe. */ + if (rw_flag == READING) { + if (position >= rip->i_size) { + /* Process is reading from an empty pipe. */ + int r = 0; + if (find_filp(rip, W_BIT) != NIL_FILP) { + /* Writer exists */ + if (oflags & O_NONBLOCK) { + r = EAGAIN; + } else { + suspend(XPIPE); /* block reader */ + r = SUSPEND; + } + /* If need be, activate sleeping writers. */ + if (susp_count > 0) release(rip, WRITE, susp_count); + } + return(r); + } + } else { + /* Process is writing to a pipe. */ + if (find_filp(rip, R_BIT) == NIL_FILP) { + /* Tell kernel to generate a SIGPIPE signal. */ + sys_kill((int)(fp - fproc), SIGPIPE); + return(EPIPE); + } + + if (position + bytes > PIPE_SIZE(rip->i_sp->s_block_size)) { + if ((oflags & O_NONBLOCK) && bytes < PIPE_SIZE(rip->i_sp->s_block_size)) + return(EAGAIN); + else if ((oflags & O_NONBLOCK) && bytes > PIPE_SIZE(rip->i_sp->s_block_size)) { + if ( (*canwrite = (PIPE_SIZE(rip->i_sp->s_block_size) - position)) > 0) { + /* Do a partial write. Need to wakeup reader */ + release(rip, READ, susp_count); + return(1); + } else { + return(EAGAIN); + } + } + if (bytes > PIPE_SIZE(rip->i_sp->s_block_size)) { + if ((*canwrite = PIPE_SIZE(rip->i_sp->s_block_size) - position) > 0) { + /* Do a partial write. Need to wakeup reader + * since we'll suspend ourself in read_write() + */ + release(rip, READ, susp_count); + return(1); + } + } + suspend(XPIPE); /* stop writer -- pipe full */ + return(SUSPEND); + } + + /* Writing to an empty pipe. Search for suspended reader. */ + if (position == 0) release(rip, READ, susp_count); + } + + *canwrite = 0; + return(1); +} + + +/*===========================================================================* + * suspend * + *===========================================================================*/ +PUBLIC void suspend(task) +int task; /* who is proc waiting for? (PIPE = pipe) */ +{ +/* Take measures to suspend the processing of the present system call. + * Store the parameters to be used upon resuming in the process table. + * (Actually they are not used when a process is waiting for an I/O device, + * but they are needed for pipes, and it is not worth making the distinction.) + * The SUSPEND pseudo error should be returned after calling suspend(). + */ + + if (task == XPIPE || task == XPOPEN) susp_count++;/* #procs susp'ed on pipe*/ + fp->fp_suspended = SUSPENDED; + fp->fp_fd = m_in.fd << 8 | call_nr; + fp->fp_task = -task; + if (task == XLOCK) { + fp->fp_buffer = (char *) m_in.name1; /* third arg to fcntl() */ + fp->fp_nbytes = m_in.request; /* second arg to fcntl() */ + } else { + fp->fp_buffer = m_in.buffer; /* for reads and writes */ + fp->fp_nbytes = m_in.nbytes; + } +} + + +/*===========================================================================* + * release * + *===========================================================================*/ +PUBLIC void release(ip, call_nr, count) +register struct inode *ip; /* inode of pipe */ +int call_nr; /* READ, WRITE, OPEN or CREAT */ +int count; /* max number of processes to release */ +{ +/* Check to see if any process is hanging on the pipe whose inode is in 'ip'. + * If one is, and it was trying to perform the call indicated by 'call_nr', + * release it. + */ + + register struct fproc *rp; + + /* Search the proc table. */ + for (rp = &fproc[0]; rp < &fproc[NR_PROCS]; rp++) { + if (rp->fp_suspended == SUSPENDED && + rp->fp_revived == NOT_REVIVING && + (rp->fp_fd & BYTE) == call_nr && + rp->fp_filp[rp->fp_fd>>8]->filp_ino == ip) { + revive((int)(rp - fproc), 0); + susp_count--; /* keep track of who is suspended */ + if (--count == 0) return; + } + } +} + + +/*===========================================================================* + * revive * + *===========================================================================*/ +PUBLIC void revive(proc_nr, bytes) +int proc_nr; /* process to revive */ +int bytes; /* if hanging on task, how many bytes read */ +{ +/* Revive a previously blocked process. When a process hangs on tty, this + * is the way it is eventually released. + */ + + register struct fproc *rfp; + register int task; + + if (proc_nr < 0 || proc_nr >= NR_PROCS) panic("revive err", proc_nr); + rfp = &fproc[proc_nr]; + if (rfp->fp_suspended == NOT_SUSPENDED || rfp->fp_revived == REVIVING)return; + + /* The 'reviving' flag only applies to pipes. Processes waiting for TTY get + * a message right away. The revival process is different for TTY and pipes. + * For TTY revival, the work is already done, for pipes it is not: the proc + * must be restarted so it can try again. + */ + task = -rfp->fp_task; + if (task == XPIPE || task == XLOCK) { + /* Revive a process suspended on a pipe or lock. */ + rfp->fp_revived = REVIVING; + reviving++; /* process was waiting on pipe or lock */ + } else { + rfp->fp_suspended = NOT_SUSPENDED; + if (task == XPOPEN) /* process blocked in open or create */ + reply(proc_nr, rfp->fp_fd>>8); + else { + /* Revive a process suspended on TTY or other device. */ + rfp->fp_nbytes = bytes; /*pretend it wants only what there is*/ + reply(proc_nr, bytes); /* unblock the process */ + } + } +} + + +/*===========================================================================* + * do_unpause * + *===========================================================================*/ +PUBLIC int do_unpause() +{ +/* A signal has been sent to a user who is paused on the file system. + * Abort the system call with the EINTR error message. + */ + + register struct fproc *rfp; + int proc_nr, task, fild; + struct filp *f; + dev_t dev; + message mess; + + if (who > MM_PROC_NR) return(EPERM); + proc_nr = m_in.pro; + if (proc_nr < 0 || proc_nr >= NR_PROCS) panic("unpause err 1", proc_nr); + rfp = &fproc[proc_nr]; + if (rfp->fp_suspended == NOT_SUSPENDED) return(OK); + task = -rfp->fp_task; + + switch (task) { + case XPIPE: /* process trying to read or write a pipe */ + break; + + case XLOCK: /* process trying to set a lock with FCNTL */ + break; + + case XPOPEN: /* process trying to open a fifo */ + break; + + default: /* process trying to do device I/O (e.g. tty)*/ + fild = (rfp->fp_fd >> 8) & BYTE;/* extract file descriptor */ + if (fild < 0 || fild >= OPEN_MAX)panic("unpause err 2",NO_NUM); + f = rfp->fp_filp[fild]; + dev = (dev_t) f->filp_ino->i_zone[0]; /* device hung on */ + mess.TTY_LINE = (dev >> MINOR) & BYTE; + mess.PROC_NR = proc_nr; + + /* Tell kernel R or W. Mode is from current call, not open. */ + mess.COUNT = (rfp->fp_fd & BYTE) == READ ? R_BIT : W_BIT; + mess.m_type = CANCEL; + fp = rfp; /* hack - ctty_io uses fp */ + (*dmap[(dev >> MAJOR) & BYTE].dmap_io)(task, &mess); + } + + rfp->fp_suspended = NOT_SUSPENDED; + reply(proc_nr, EINTR); /* signal interrupted call */ + return(OK); +} diff --git a/servers/fs/protect.c b/servers/fs/protect.c new file mode 100644 index 000000000..cbed88849 --- /dev/null +++ b/servers/fs/protect.c @@ -0,0 +1,218 @@ +/* This file deals with protection in the file system. It contains the code + * for four system calls that relate to protection. + * + * The entry points into this file are + * do_chmod: perform the CHMOD system call + * do_chown: perform the CHOWN system call + * do_umask: perform the UMASK system call + * do_access: perform the ACCESS system call + * forbidden: check to see if a given access is allowed on a given inode + */ + +#include "fs.h" +#include <unistd.h> +#include <minix/callnr.h> +#include "buf.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "param.h" +#include "super.h" + +/*===========================================================================* + * do_chmod * + *===========================================================================*/ +PUBLIC int do_chmod() +{ +/* Perform the chmod(name, mode) system call. */ + + register struct inode *rip; + register int r; + + /* Temporarily open the file. */ + if (fetch_name(m_in.name, m_in.name_length, M3) != OK) return(err_code); + if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); + + /* Only the owner or the super_user may change the mode of a file. + * No one may change the mode of a file on a read-only file system. + */ + if (rip->i_uid != fp->fp_effuid && !super_user) + r = EPERM; + else + r = read_only(rip); + + /* If error, return inode. */ + if (r != OK) { + put_inode(rip); + return(r); + } + + /* Now make the change. Clear setgid bit if file is not in caller's grp */ + rip->i_mode = (rip->i_mode & ~ALL_MODES) | (m_in.mode & ALL_MODES); + if (!super_user && rip->i_gid != fp->fp_effgid)rip->i_mode &= ~I_SET_GID_BIT; + rip->i_update |= CTIME; + rip->i_dirt = DIRTY; + + put_inode(rip); + return(OK); +} + + +/*===========================================================================* + * do_chown * + *===========================================================================*/ +PUBLIC int do_chown() +{ +/* Perform the chown(name, owner, group) system call. */ + + register struct inode *rip; + register int r; + + /* Temporarily open the file. */ + if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); + if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); + + /* Not permitted to change the owner of a file on a read-only file sys. */ + r = read_only(rip); + if (r == OK) { + /* FS is R/W. Whether call is allowed depends on ownership, etc. */ + if (super_user) { + /* The super user can do anything. */ + rip->i_uid = m_in.owner; /* others later */ + } else { + /* Regular users can only change groups of their own files. */ + if (rip->i_uid != fp->fp_effuid) r = EPERM; + if (rip->i_uid != m_in.owner) r = EPERM; /* no giving away */ + if (fp->fp_effgid != m_in.group) r = EPERM; + } + } + if (r == OK) { + rip->i_gid = m_in.group; + rip->i_mode &= ~(I_SET_UID_BIT | I_SET_GID_BIT); + rip->i_update |= CTIME; + rip->i_dirt = DIRTY; + } + + put_inode(rip); + return(r); +} + + +/*===========================================================================* + * do_umask * + *===========================================================================*/ +PUBLIC int do_umask() +{ +/* Perform the umask(co_mode) system call. */ + register mode_t r; + + r = ~fp->fp_umask; /* set 'r' to complement of old mask */ + fp->fp_umask = ~(m_in.co_mode & RWX_MODES); + return(r); /* return complement of old mask */ +} + + +/*===========================================================================* + * do_access * + *===========================================================================*/ +PUBLIC int do_access() +{ +/* Perform the access(name, mode) system call. */ + + struct inode *rip; + register int r; + + /* First check to see if the mode is correct. */ + if ( (m_in.mode & ~(R_OK | W_OK | X_OK)) != 0 && m_in.mode != F_OK) + return(EINVAL); + + /* Temporarily open the file whose access is to be checked. */ + if (fetch_name(m_in.name, m_in.name_length, M3) != OK) return(err_code); + if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); + + /* Now check the permissions. */ + r = forbidden(rip, (mode_t) m_in.mode); + put_inode(rip); + return(r); +} + + +/*===========================================================================* + * forbidden * + *===========================================================================*/ +PUBLIC int forbidden(rip, access_desired) +register struct inode *rip; /* pointer to inode to be checked */ +mode_t access_desired; /* RWX bits */ +{ +/* Given a pointer to an inode, 'rip', and the access desired, determine + * if the access is allowed, and if not why not. The routine looks up the + * caller's uid in the 'fproc' table. If access is allowed, OK is returned + * if it is forbidden, EACCES is returned. + */ + + register struct inode *old_rip = rip; + register struct super_block *sp; + register mode_t bits, perm_bits; + int r, shift, test_uid, test_gid, type; + + if (rip->i_mount == I_MOUNT) /* The inode is mounted on. */ + for (sp = &super_block[1]; sp < &super_block[NR_SUPERS]; sp++) + if (sp->s_imount == rip) { + rip = get_inode(sp->s_dev, ROOT_INODE); + break; + } /* if */ + + /* Isolate the relevant rwx bits from the mode. */ + bits = rip->i_mode; + test_uid = (call_nr == ACCESS ? fp->fp_realuid : fp->fp_effuid); + test_gid = (call_nr == ACCESS ? fp->fp_realgid : fp->fp_effgid); + if (test_uid == SU_UID) { + /* Grant read and write permission. Grant search permission for + * directories. Grant execute permission (for non-directories) if + * and only if one of the 'X' bits is set. + */ + if ( (bits & I_TYPE) == I_DIRECTORY || + bits & ((X_BIT << 6) | (X_BIT << 3) | X_BIT)) + perm_bits = R_BIT | W_BIT | X_BIT; + else + perm_bits = R_BIT | W_BIT; + } else { + if (test_uid == rip->i_uid) shift = 6; /* owner */ + else if (test_gid == rip->i_gid ) shift = 3; /* group */ + else shift = 0; /* other */ + perm_bits = (bits >> shift) & (R_BIT | W_BIT | X_BIT); + } + + /* If access desired is not a subset of what is allowed, it is refused. */ + r = OK; + if ((perm_bits | access_desired) != perm_bits) r = EACCES; + + /* Check to see if someone is trying to write on a file system that is + * mounted read-only. + */ + type = rip->i_mode & I_TYPE; + if (r == OK) + if (access_desired & W_BIT) + r = read_only(rip); + + if (rip != old_rip) put_inode(rip); + + return(r); +} + + +/*===========================================================================* + * read_only * + *===========================================================================*/ +PUBLIC int read_only(ip) +struct inode *ip; /* ptr to inode whose file sys is to be cked */ +{ +/* Check to see if the file system on which the inode 'ip' resides is mounted + * read only. If so, return EROFS, else return OK. + */ + + register struct super_block *sp; + + sp = ip->i_sp; + return(sp->s_rd_only ? EROFS : OK); +} diff --git a/servers/fs/proto.h b/servers/fs/proto.h new file mode 100644 index 000000000..4045273a2 --- /dev/null +++ b/servers/fs/proto.h @@ -0,0 +1,174 @@ +/* Function prototypes. */ + +/* Structs used in prototypes must be declared as such first. */ +struct buf; +struct filp; +struct inode; +struct super_block; + +/* cache.c */ +_PROTOTYPE( zone_t alloc_zone, (Dev_t dev, zone_t z) ); +_PROTOTYPE( void flushall, (Dev_t dev) ); +_PROTOTYPE( void free_zone, (Dev_t dev, zone_t numb) ); +_PROTOTYPE( struct buf *get_block, (Dev_t dev, block_t block,int only_search)); +_PROTOTYPE( void invalidate, (Dev_t device) ); +_PROTOTYPE( void put_block, (struct buf *bp, int block_type) ); +_PROTOTYPE( void rw_block, (struct buf *bp, int rw_flag) ); +_PROTOTYPE( void rw_scattered, (Dev_t dev, + struct buf **bufq, int bufqsize, int rw_flag) ); + +#if ENABLE_CACHE2 +/* cache2.c */ +_PROTOTYPE( void init_cache2, (unsigned long size) ); +_PROTOTYPE( int get_block2, (struct buf *bp, int only_search) ); +_PROTOTYPE( void put_block2, (struct buf *bp) ); +_PROTOTYPE( void invalidate2, (Dev_t device) ); +#endif + +/* device.c */ +_PROTOTYPE( int dev_open, (Dev_t dev, int proc, int flags) ); +_PROTOTYPE( void dev_close, (Dev_t dev) ); +_PROTOTYPE( int dev_io, (int op, Dev_t dev, int proc, void *buf, + off_t pos, int bytes, int flags) ); +_PROTOTYPE( int gen_opcl, (int op, Dev_t dev, int proc, int flags) ); +_PROTOTYPE( void gen_io, (int task_nr, message *mess_ptr) ); +_PROTOTYPE( int no_dev, (int op, Dev_t dev, int proc, int flags) ); +_PROTOTYPE( int tty_opcl, (int op, Dev_t dev, int proc, int flags) ); +_PROTOTYPE( int ctty_opcl, (int op, Dev_t dev, int proc, int flags) ); +_PROTOTYPE( int clone_opcl, (int op, Dev_t dev, int proc, int flags) ); +_PROTOTYPE( void ctty_io, (int task_nr, message *mess_ptr) ); +_PROTOTYPE( int do_ioctl, (void) ); +_PROTOTYPE( int do_setsid, (void) ); + +/* dmp.c */ +_PROTOTYPE( int do_fkey_pressed, (void) ); + +/* dmap.c */ +_PROTOTYPE( void map_controllers, (void) ); +_PROTOTYPE( int map_driver, (int major, int proc_nr, int dev_style) ); + +/* filedes.c */ +_PROTOTYPE( struct filp *find_filp, (struct inode *rip, Mode_t bits) ); +_PROTOTYPE( int get_fd, (int start, Mode_t bits, int *k, struct filp **fpt) ); +_PROTOTYPE( struct filp *get_filp, (int fild) ); + +/* inode.c */ +_PROTOTYPE( struct inode *alloc_inode, (Dev_t dev, Mode_t bits) ); +_PROTOTYPE( void dup_inode, (struct inode *ip) ); +_PROTOTYPE( void free_inode, (Dev_t dev, Ino_t numb) ); +_PROTOTYPE( struct inode *get_inode, (Dev_t dev, int numb) ); +_PROTOTYPE( void put_inode, (struct inode *rip) ); +_PROTOTYPE( void update_times, (struct inode *rip) ); +_PROTOTYPE( void rw_inode, (struct inode *rip, int rw_flag) ); +_PROTOTYPE( void wipe_inode, (struct inode *rip) ); + +/* link.c */ +_PROTOTYPE( int do_link, (void) ); +_PROTOTYPE( int do_unlink, (void) ); +_PROTOTYPE( int do_rename, (void) ); +_PROTOTYPE( void truncate, (struct inode *rip) ); + +/* lock.c */ +_PROTOTYPE( int lock_op, (struct filp *f, int req) ); +_PROTOTYPE( void lock_revive, (void) ); + +/* main.c */ +_PROTOTYPE( void main, (void) ); +_PROTOTYPE( void reply, (int whom, int result) ); + +/* misc.c */ +_PROTOTYPE( int do_dup, (void) ); +_PROTOTYPE( int do_exit, (void) ); +_PROTOTYPE( int do_fcntl, (void) ); +_PROTOTYPE( int do_fork, (void) ); +_PROTOTYPE( int do_exec, (void) ); +_PROTOTYPE( int do_revive, (void) ); +_PROTOTYPE( int do_set, (void) ); +_PROTOTYPE( int do_sync, (void) ); +_PROTOTYPE( int do_reboot, (void) ); +_PROTOTYPE( int do_svrctl, (void) ); +_PROTOTYPE( int do_getsysinfo, (void) ); + +/* mount.c */ +_PROTOTYPE( int do_mount, (void) ); +_PROTOTYPE( int do_umount, (void) ); +_PROTOTYPE( int unmount, (Dev_t dev) ); + +/* open.c */ +_PROTOTYPE( int do_close, (void) ); +_PROTOTYPE( int do_creat, (void) ); +_PROTOTYPE( int do_lseek, (void) ); +_PROTOTYPE( int do_mknod, (void) ); +_PROTOTYPE( int do_mkdir, (void) ); +_PROTOTYPE( int do_open, (void) ); + +/* path.c */ +_PROTOTYPE( struct inode *advance,(struct inode *dirp, char string[NAME_MAX])); +_PROTOTYPE( int search_dir, (struct inode *ldir_ptr, + char string [NAME_MAX], ino_t *numb, int flag) ); +_PROTOTYPE( struct inode *eat_path, (char *path) ); +_PROTOTYPE( struct inode *last_dir, (char *path, char string [NAME_MAX])); + +/* pipe.c */ +_PROTOTYPE( int do_pipe, (void) ); +_PROTOTYPE( int do_unpause, (void) ); +_PROTOTYPE( int pipe_check, (struct inode *rip, int rw_flag, + int oflags, int bytes, off_t position, int *canwrite)); +_PROTOTYPE( void release, (struct inode *ip, int call_nr, int count) ); +_PROTOTYPE( void revive, (int proc_nr, int bytes) ); +_PROTOTYPE( void suspend, (int task) ); + +/* protect.c */ +_PROTOTYPE( int do_access, (void) ); +_PROTOTYPE( int do_chmod, (void) ); +_PROTOTYPE( int do_chown, (void) ); +_PROTOTYPE( int do_umask, (void) ); +_PROTOTYPE( int forbidden, (struct inode *rip, Mode_t access_desired) ); +_PROTOTYPE( int read_only, (struct inode *ip) ); + +/* read.c */ +_PROTOTYPE( int do_read, (void) ); +_PROTOTYPE( struct buf *rahead, (struct inode *rip, block_t baseblock, + off_t position, unsigned bytes_ahead) ); +_PROTOTYPE( void read_ahead, (void) ); +_PROTOTYPE( block_t read_map, (struct inode *rip, off_t position) ); +_PROTOTYPE( int read_write, (int rw_flag) ); +_PROTOTYPE( zone_t rd_indir, (struct buf *bp, int index) ); + +/* stadir.c */ +_PROTOTYPE( int do_chdir, (void) ); +_PROTOTYPE( int do_chroot, (void) ); +_PROTOTYPE( int do_fstat, (void) ); +_PROTOTYPE( int do_stat, (void) ); +_PROTOTYPE( int do_fstatfs, (void) ); + +/* super.c */ +_PROTOTYPE( bit_t alloc_bit, (struct super_block *sp, int map, bit_t origin)); +_PROTOTYPE( void free_bit, (struct super_block *sp, int map, + bit_t bit_returned) ); +_PROTOTYPE( struct super_block *get_super, (Dev_t dev) ); +_PROTOTYPE( int mounted, (struct inode *rip) ); +_PROTOTYPE( int read_super, (struct super_block *sp) ); +_PROTOTYPE( int get_block_size, (dev_t dev) ); + +/* time.c */ +_PROTOTYPE( int do_stime, (void) ); +_PROTOTYPE( int do_time, (void) ); +_PROTOTYPE( int do_tims, (void) ); +_PROTOTYPE( int do_utime, (void) ); +/* cmostime.c */ +_PROTOTYPE( int do_cmostime, (void) ); + +/* utility.c */ +_PROTOTYPE( time_t clock_time, (void) ); +_PROTOTYPE( unsigned conv2, (int norm, int w) ); +_PROTOTYPE( long conv4, (int norm, long x) ); +_PROTOTYPE( int fetch_name, (char *path, int len, int flag) ); +_PROTOTYPE( int no_sys, (void) ); +_PROTOTYPE( void panic, (char *format, int num) ); + +/* write.c */ +_PROTOTYPE( void clear_zone, (struct inode *rip, off_t pos, int flag) ); +_PROTOTYPE( int do_write, (void) ); +_PROTOTYPE( struct buf *new_block, (struct inode *rip, off_t position) ); +_PROTOTYPE( void zero_block, (struct buf *bp) ); diff --git a/servers/fs/read.c b/servers/fs/read.c new file mode 100644 index 000000000..8239b98b7 --- /dev/null +++ b/servers/fs/read.c @@ -0,0 +1,528 @@ +/* This file contains the heart of the mechanism used to read (and write) + * files. Read and write requests are split up into chunks that do not cross + * block boundaries. Each chunk is then processed in turn. Reads on special + * files are also detected and handled. + * + * The entry points into this file are + * do_read: perform the READ system call by calling read_write + * read_write: actually do the work of READ and WRITE + * read_map: given an inode and file position, look up its zone number + * rd_indir: read an entry in an indirect block + * read_ahead: manage the block read ahead business + */ + +#include "fs.h" +#include <fcntl.h> +#include <minix/com.h> +#include "buf.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "param.h" +#include "super.h" + + +FORWARD _PROTOTYPE( int rw_chunk, (struct inode *rip, off_t position, + unsigned off, int chunk, unsigned left, int rw_flag, + char *buff, int seg, int usr, int block_size) ); + +/*===========================================================================* + * do_read * + *===========================================================================*/ +PUBLIC int do_read() +{ + return(read_write(READING)); +} + + +/*===========================================================================* + * read_write * + *===========================================================================*/ +PUBLIC int read_write(rw_flag) +int rw_flag; /* READING or WRITING */ +{ +/* Perform read(fd, buffer, nbytes) or write(fd, buffer, nbytes) call. */ + + register struct inode *rip; + register struct filp *f; + off_t bytes_left, f_size, position; + unsigned int off, cum_io; + int op, oflags, r, chunk, usr, seg, block_spec, char_spec; + int regular, partial_pipe = 0, partial_cnt = 0; + mode_t mode_word; + struct filp *wf; + int block_size; + + /* MM loads segments by putting funny things in upper 10 bits of 'fd'. */ + if (who == MM_PROC_NR && (m_in.fd & (~BYTE)) ) { + usr = m_in.fd >> 7; + seg = (m_in.fd >> 5) & 03; + m_in.fd &= 037; /* get rid of user and segment bits */ + } else { + usr = who; /* normal case */ + seg = D; + } + + /* If the file descriptor is valid, get the inode, size and mode. */ + if (m_in.nbytes < 0) return(EINVAL); + if ((f = get_filp(m_in.fd)) == NIL_FILP) return(err_code); + if (((f->filp_mode) & (rw_flag == READING ? R_BIT : W_BIT)) == 0) { + return(f->filp_mode == FILP_CLOSED ? EIO : EBADF); + } + if (m_in.nbytes == 0) return(0); /* so char special files need not check for 0*/ + position = f->filp_pos; + oflags = f->filp_flags; + rip = f->filp_ino; + f_size = rip->i_size; + r = OK; + if (rip->i_pipe == I_PIPE) { + /* fp->fp_cum_io_partial is only nonzero when doing partial writes */ + cum_io = fp->fp_cum_io_partial; + } else { + cum_io = 0; + } + op = (rw_flag == READING ? DEV_READ : DEV_WRITE); + mode_word = rip->i_mode & I_TYPE; + regular = mode_word == I_REGULAR || mode_word == I_NAMED_PIPE; + + if((char_spec = (mode_word == I_CHAR_SPECIAL ? 1 : 0))) { + if(rip->i_zone[0] == NO_DEV) + panic("read_write tries to read from character device NO_DEV", NO_NUM); + block_size = get_block_size(rip->i_zone[0]); + } + if((block_spec = (mode_word == I_BLOCK_SPECIAL ? 1 : 0))) { + f_size = LONG_MAX; + if(rip->i_zone[0] == NO_DEV) + panic("read_write tries to read from block device NO_DEV", NO_NUM); + block_size = get_block_size(rip->i_zone[0]); + } + + if(!char_spec && !block_spec) + block_size = rip->i_sp->s_block_size; + + rdwt_err = OK; /* set to EIO if disk error occurs */ + + /* Check for character special files. */ + if (char_spec) { + dev_t dev; + dev = (dev_t) rip->i_zone[0]; + r = dev_io(op, dev, usr, m_in.buffer, position, m_in.nbytes, oflags); + if (r >= 0) { + cum_io = r; + position += r; + r = OK; + } + } else { + if (rw_flag == WRITING && block_spec == 0) { + /* Check in advance to see if file will grow too big. */ + if (position > rip->i_sp->s_max_size - m_in.nbytes) + return(EFBIG); + + /* Check for O_APPEND flag. */ + if (oflags & O_APPEND) position = f_size; + + /* Clear the zone containing present EOF if hole about + * to be created. This is necessary because all unwritten + * blocks prior to the EOF must read as zeros. + */ + if (position > f_size) clear_zone(rip, f_size, 0); + } + + /* Pipes are a little different. Check. */ + if (rip->i_pipe == I_PIPE) { + r = pipe_check(rip,rw_flag,oflags, + m_in.nbytes,position,&partial_cnt); + if (r <= 0) return(r); + } + + if (partial_cnt > 0) partial_pipe = 1; + + /* Split the transfer into chunks that don't span two blocks. */ + while (m_in.nbytes != 0) { + off = (unsigned int) (position % block_size);/* offset in blk*/ + if (partial_pipe) { /* pipes only */ + chunk = MIN(partial_cnt, block_size - off); + } else + chunk = MIN(m_in.nbytes, block_size - off); + if (chunk < 0) chunk = block_size - off; + + if (rw_flag == READING) { + bytes_left = f_size - position; + if (position >= f_size) break; /* we are beyond EOF */ + if (chunk > bytes_left) chunk = (int) bytes_left; + } + + /* Read or write 'chunk' bytes. */ + r = rw_chunk(rip, position, off, chunk, (unsigned) m_in.nbytes, + rw_flag, m_in.buffer, seg, usr, block_size); + if (r != OK) break; /* EOF reached */ + if (rdwt_err < 0) break; + + /* Update counters and pointers. */ + m_in.buffer += chunk; /* user buffer address */ + m_in.nbytes -= chunk; /* bytes yet to be read */ + cum_io += chunk; /* bytes read so far */ + position += chunk; /* position within the file */ + + if (partial_pipe) { + partial_cnt -= chunk; + if (partial_cnt <= 0) break; + } + } + } + + /* On write, update file size and access time. */ + if (rw_flag == WRITING) { + if (regular || mode_word == I_DIRECTORY) { + if (position > f_size) rip->i_size = position; + } + } else { + if (rip->i_pipe == I_PIPE && position >= rip->i_size) { + /* Reset pipe pointers. */ + rip->i_size = 0; /* no data left */ + position = 0; /* reset reader(s) */ + if ( (wf = find_filp(rip, W_BIT)) != NIL_FILP) wf->filp_pos =0; + } + } + f->filp_pos = position; + + /* Check to see if read-ahead is called for, and if so, set it up. */ + if (rw_flag == READING && rip->i_seek == NO_SEEK && position % block_size== 0 + && (regular || mode_word == I_DIRECTORY)) { + rdahed_inode = rip; + rdahedpos = position; + } + rip->i_seek = NO_SEEK; + + if (rdwt_err != OK) r = rdwt_err; /* check for disk error */ + if (rdwt_err == END_OF_FILE) r = OK; + if (r == OK) { + if (rw_flag == READING) rip->i_update |= ATIME; + if (rw_flag == WRITING) rip->i_update |= CTIME | MTIME; + rip->i_dirt = DIRTY; /* inode is thus now dirty */ + if (partial_pipe) { + partial_pipe = 0; + /* partial write on pipe with */ + /* O_NONBLOCK, return write count */ + if (!(oflags & O_NONBLOCK)) { + fp->fp_cum_io_partial = cum_io; + suspend(XPIPE); /* partial write on pipe with */ + return(SUSPEND); /* nbyte > PIPE_SIZE - non-atomic */ + } + } + fp->fp_cum_io_partial = 0; + return(cum_io); + } else { + return(r); + } +} + + +/*===========================================================================* + * rw_chunk * + *===========================================================================*/ +PRIVATE int rw_chunk(rip, position, off, chunk, left, rw_flag, buff, seg, usr, block_size) +register struct inode *rip; /* pointer to inode for file to be rd/wr */ +off_t position; /* position within file to read or write */ +unsigned off; /* off within the current block */ +int chunk; /* number of bytes to read or write */ +unsigned left; /* max number of bytes wanted after position */ +int rw_flag; /* READING or WRITING */ +char *buff; /* virtual address of the user buffer */ +int seg; /* T or D segment in user space */ +int usr; /* which user process */ +int block_size; +{ +/* Read or write (part of) a block. */ + + register struct buf *bp; + register int r; + int n, block_spec; + block_t b; + dev_t dev; + + block_spec = (rip->i_mode & I_TYPE) == I_BLOCK_SPECIAL; + if (block_spec) { + b = position/block_size; + dev = (dev_t) rip->i_zone[0]; + } else { + b = read_map(rip, position); + dev = rip->i_dev; + } + + if (!block_spec && b == NO_BLOCK) { + if (rw_flag == READING) { + /* Reading from a nonexistent block. Must read as all zeros.*/ + bp = get_block(NO_DEV, NO_BLOCK, NORMAL); /* get a buffer */ + zero_block(bp); + } else { + /* Writing to a nonexistent block. Create and enter in inode.*/ + if ((bp= new_block(rip, position)) == NIL_BUF)return(err_code); + } + } else if (rw_flag == READING) { + /* Read and read ahead if convenient. */ + bp = rahead(rip, b, position, left); + } else { + /* Normally an existing block to be partially overwritten is first read + * in. However, a full block need not be read in. If it is already in + * the cache, acquire it, otherwise just acquire a free buffer. + */ + n = (chunk == block_size ? NO_READ : NORMAL); + if (!block_spec && off == 0 && position >= rip->i_size) n = NO_READ; + bp = get_block(dev, b, n); + } + + /* In all cases, bp now points to a valid buffer. */ + if (rw_flag == WRITING && chunk != block_size && !block_spec && + position >= rip->i_size && off == 0) { + zero_block(bp); + } + if (rw_flag == READING) { + /* Copy a chunk from the block buffer to user space. */ + r = sys_vircopy(FS_PROC_NR, D, (phys_bytes) (bp->b_data+off), + usr, seg, (phys_bytes) buff, + (phys_bytes) chunk); + } else { + /* Copy a chunk from user space to the block buffer. */ + r = sys_vircopy(usr, seg, (phys_bytes) buff, + FS_PROC_NR, D, (phys_bytes) (bp->b_data+off), + (phys_bytes) chunk); + bp->b_dirt = DIRTY; + } + n = (off + chunk == block_size ? FULL_DATA_BLOCK : PARTIAL_DATA_BLOCK); + put_block(bp, n); + return(r); +} + + +/*===========================================================================* + * read_map * + *===========================================================================*/ +PUBLIC block_t read_map(rip, position) +register struct inode *rip; /* ptr to inode to map from */ +off_t position; /* position in file whose blk wanted */ +{ +/* Given an inode and a position within the corresponding file, locate the + * block (not zone) number in which that position is to be found and return it. + */ + + register struct buf *bp; + register zone_t z; + int scale, boff, dzones, nr_indirects, index, zind, ex; + block_t b; + long excess, zone, block_pos; + + scale = rip->i_sp->s_log_zone_size; /* for block-zone conversion */ + block_pos = position/rip->i_sp->s_block_size; /* relative blk # in file */ + zone = block_pos >> scale; /* position's zone */ + boff = (int) (block_pos - (zone << scale) ); /* relative blk # within zone */ + dzones = rip->i_ndzones; + nr_indirects = rip->i_nindirs; + + /* Is 'position' to be found in the inode itself? */ + if (zone < dzones) { + zind = (int) zone; /* index should be an int */ + z = rip->i_zone[zind]; + if (z == NO_ZONE) return(NO_BLOCK); + b = ((block_t) z << scale) + boff; + return(b); + } + + /* It is not in the inode, so it must be single or double indirect. */ + excess = zone - dzones; /* first Vx_NR_DZONES don't count */ + + if (excess < nr_indirects) { + /* 'position' can be located via the single indirect block. */ + z = rip->i_zone[dzones]; + } else { + /* 'position' can be located via the double indirect block. */ + if ( (z = rip->i_zone[dzones+1]) == NO_ZONE) return(NO_BLOCK); + excess -= nr_indirects; /* single indir doesn't count*/ + b = (block_t) z << scale; + bp = get_block(rip->i_dev, b, NORMAL); /* get double indirect block */ + index = (int) (excess/nr_indirects); + z = rd_indir(bp, index); /* z= zone for single*/ + put_block(bp, INDIRECT_BLOCK); /* release double ind block */ + excess = excess % nr_indirects; /* index into single ind blk */ + } + + /* 'z' is zone num for single indirect block; 'excess' is index into it. */ + if (z == NO_ZONE) return(NO_BLOCK); + b = (block_t) z << scale; /* b is blk # for single ind */ + bp = get_block(rip->i_dev, b, NORMAL); /* get single indirect block */ + ex = (int) excess; /* need an integer */ + z = rd_indir(bp, ex); /* get block pointed to */ + put_block(bp, INDIRECT_BLOCK); /* release single indir blk */ + if (z == NO_ZONE) return(NO_BLOCK); + b = ((block_t) z << scale) + boff; + return(b); +} + + +/*===========================================================================* + * rd_indir * + *===========================================================================*/ +PUBLIC zone_t rd_indir(bp, index) +struct buf *bp; /* pointer to indirect block */ +int index; /* index into *bp */ +{ +/* Given a pointer to an indirect block, read one entry. The reason for + * making a separate routine out of this is that there are four cases: + * V1 (IBM and 68000), and V2 (IBM and 68000). + */ + + struct super_block *sp; + zone_t zone; /* V2 zones are longs (shorts in V1) */ + + sp = get_super(bp->b_dev); /* need super block to find file sys type */ + + /* read a zone from an indirect block */ + if (sp->s_version == V1) + zone = (zone_t) conv2(sp->s_native, (int) bp->b_v1_ind[index]); + else + zone = (zone_t) conv4(sp->s_native, (long) bp->b_v2_ind[index]); + + if (zone != NO_ZONE && + (zone < (zone_t) sp->s_firstdatazone || zone >= sp->s_zones)) { + printf("Illegal zone number %ld in indirect block, index %d\n", + (long) zone, index); + panic("check file system", NO_NUM); + } + return(zone); +} + + +/*===========================================================================* + * read_ahead * + *===========================================================================*/ +PUBLIC void read_ahead() +{ +/* Read a block into the cache before it is needed. */ + int block_size; + register struct inode *rip; + struct buf *bp; + block_t b; + + rip = rdahed_inode; /* pointer to inode to read ahead from */ + block_size = get_block_size(rip->i_dev); + rdahed_inode = NIL_INODE; /* turn off read ahead */ + if ( (b = read_map(rip, rdahedpos)) == NO_BLOCK) return; /* at EOF */ + bp = rahead(rip, b, rdahedpos, block_size); + put_block(bp, PARTIAL_DATA_BLOCK); +} + + +/*===========================================================================* + * rahead * + *===========================================================================*/ +PUBLIC struct buf *rahead(rip, baseblock, position, bytes_ahead) +register struct inode *rip; /* pointer to inode for file to be read */ +block_t baseblock; /* block at current position */ +off_t position; /* position within file */ +unsigned bytes_ahead; /* bytes beyond position for immediate use */ +{ +/* Fetch a block from the cache or the device. If a physical read is + * required, prefetch as many more blocks as convenient into the cache. + * This usually covers bytes_ahead and is at least BLOCKS_MINIMUM. + * The device driver may decide it knows better and stop reading at a + * cylinder boundary (or after an error). Rw_scattered() puts an optional + * flag on all reads to allow this. + */ + int block_size; +/* Minimum number of blocks to prefetch. */ +# define BLOCKS_MINIMUM (NR_BUFS < 50 ? 18 : 32) + int block_spec, scale, read_q_size; + unsigned int blocks_ahead, fragment; + block_t block, blocks_left; + off_t ind1_pos; + dev_t dev; + struct buf *bp; + static struct buf *read_q[NR_BUFS]; + + block_spec = (rip->i_mode & I_TYPE) == I_BLOCK_SPECIAL; + if (block_spec) { + dev = (dev_t) rip->i_zone[0]; + } else { + dev = rip->i_dev; + } + block_size = get_block_size(dev); + + block = baseblock; + bp = get_block(dev, block, PREFETCH); + if (bp->b_dev != NO_DEV) return(bp); + + /* The best guess for the number of blocks to prefetch: A lot. + * It is impossible to tell what the device looks like, so we don't even + * try to guess the geometry, but leave it to the driver. + * + * The floppy driver can read a full track with no rotational delay, and it + * avoids reading partial tracks if it can, so handing it enough buffers to + * read two tracks is perfect. (Two, because some diskette types have + * an odd number of sectors per track, so a block may span tracks.) + * + * The disk drivers don't try to be smart. With todays disks it is + * impossible to tell what the real geometry looks like, so it is best to + * read as much as you can. With luck the caching on the drive allows + * for a little time to start the next read. + * + * The current solution below is a bit of a hack, it just reads blocks from + * the current file position hoping that more of the file can be found. A + * better solution must look at the already available zone pointers and + * indirect blocks (but don't call read_map!). + */ + + fragment = position % block_size; + position -= fragment; + bytes_ahead += fragment; + + blocks_ahead = (bytes_ahead + block_size - 1) / block_size; + + if (block_spec && rip->i_size == 0) { + blocks_left = NR_IOREQS; + } else { + blocks_left = (rip->i_size - position + block_size - 1) / block_size; + + /* Go for the first indirect block if we are in its neighborhood. */ + if (!block_spec) { + scale = rip->i_sp->s_log_zone_size; + ind1_pos = (off_t) rip->i_ndzones * (block_size << scale); + if (position <= ind1_pos && rip->i_size > ind1_pos) { + blocks_ahead++; + blocks_left++; + } + } + } + + /* No more than the maximum request. */ + if (blocks_ahead > NR_IOREQS) blocks_ahead = NR_IOREQS; + + /* Read at least the minimum number of blocks, but not after a seek. */ + if (blocks_ahead < BLOCKS_MINIMUM && rip->i_seek == NO_SEEK) + blocks_ahead = BLOCKS_MINIMUM; + + /* Can't go past end of file. */ + if (blocks_ahead > blocks_left) blocks_ahead = blocks_left; + + read_q_size = 0; + + /* Acquire block buffers. */ + for (;;) { + read_q[read_q_size++] = bp; + + if (--blocks_ahead == 0) break; + + /* Don't trash the cache, leave 4 free. */ + if (bufs_in_use >= NR_BUFS - 4) break; + + block++; + + bp = get_block(dev, block, PREFETCH); + if (bp->b_dev != NO_DEV) { + /* Oops, block already in the cache, get out. */ + put_block(bp, FULL_DATA_BLOCK); + break; + } + } + rw_scattered(dev, read_q, read_q_size, READING); + return(get_block(dev, baseblock, NORMAL)); +} diff --git a/servers/fs/stadir.c b/servers/fs/stadir.c new file mode 100644 index 000000000..ac534b38d --- /dev/null +++ b/servers/fs/stadir.c @@ -0,0 +1,219 @@ +/* This file contains the code for performing four system calls relating to + * status and directories. + * + * The entry points into this file are + * do_chdir: perform the CHDIR system call + * do_chroot: perform the CHROOT system call + * do_stat: perform the STAT system call + * do_fstat: perform the FSTAT system call + * do_fstatfs: perform the FSTATFS system call + */ + +#include "fs.h" +#include <sys/stat.h> +#include <sys/statfs.h> +#include <minix/com.h> +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "param.h" +#include "super.h" + +FORWARD _PROTOTYPE( int change, (struct inode **iip, char *name_ptr, int len)); +FORWARD _PROTOTYPE( int stat_inode, (struct inode *rip, struct filp *fil_ptr, + char *user_addr) ); + +/*===========================================================================* + * do_chdir * + *===========================================================================*/ +PUBLIC int do_chdir() +{ +/* Change directory. This function is also called by MM to simulate a chdir + * in order to do EXEC, etc. It also changes the root directory, the uids and + * gids, and the umask. + */ + + int r; + register struct fproc *rfp; + + if (who == MM_PROC_NR) { + rfp = &fproc[m_in.slot1]; + put_inode(fp->fp_rootdir); + dup_inode(fp->fp_rootdir = rfp->fp_rootdir); + put_inode(fp->fp_workdir); + dup_inode(fp->fp_workdir = rfp->fp_workdir); + + /* MM uses access() to check permissions. To make this work, pretend + * that the user's real ids are the same as the user's effective ids. + * FS calls other than access() do not use the real ids, so are not + * affected. + */ + fp->fp_realuid = + fp->fp_effuid = rfp->fp_effuid; + fp->fp_realgid = + fp->fp_effgid = rfp->fp_effgid; + fp->fp_umask = rfp->fp_umask; + return(OK); + } + + /* Perform the chdir(name) system call. */ + r = change(&fp->fp_workdir, m_in.name, m_in.name_length); + return(r); +} + + +/*===========================================================================* + * do_chroot * + *===========================================================================*/ +PUBLIC int do_chroot() +{ +/* Perform the chroot(name) system call. */ + + register int r; + + if (!super_user) return(EPERM); /* only su may chroot() */ + r = change(&fp->fp_rootdir, m_in.name, m_in.name_length); + return(r); +} + + +/*===========================================================================* + * change * + *===========================================================================*/ +PRIVATE int change(iip, name_ptr, len) +struct inode **iip; /* pointer to the inode pointer for the dir */ +char *name_ptr; /* pointer to the directory name to change to */ +int len; /* length of the directory name string */ +{ +/* Do the actual work for chdir() and chroot(). */ + + struct inode *rip; + register int r; + + /* Try to open the new directory. */ + if (fetch_name(name_ptr, len, M3) != OK) return(err_code); + if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); + + /* It must be a directory and also be searchable. */ + if ( (rip->i_mode & I_TYPE) != I_DIRECTORY) + r = ENOTDIR; + else + r = forbidden(rip, X_BIT); /* check if dir is searchable */ + + /* If error, return inode. */ + if (r != OK) { + put_inode(rip); + return(r); + } + + /* Everything is OK. Make the change. */ + put_inode(*iip); /* release the old directory */ + *iip = rip; /* acquire the new one */ + return(OK); +} + + +/*===========================================================================* + * do_stat * + *===========================================================================*/ +PUBLIC int do_stat() +{ +/* Perform the stat(name, buf) system call. */ + + register struct inode *rip; + register int r; + + /* Both stat() and fstat() use the same routine to do the real work. That + * routine expects an inode, so acquire it temporarily. + */ + if (fetch_name(m_in.name1, m_in.name1_length, M1) != OK) return(err_code); + if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); + r = stat_inode(rip, NIL_FILP, m_in.name2); /* actually do the work.*/ + put_inode(rip); /* release the inode */ + return(r); +} + + +/*===========================================================================* + * do_fstat * + *===========================================================================*/ +PUBLIC int do_fstat() +{ +/* Perform the fstat(fd, buf) system call. */ + + register struct filp *rfilp; + + /* Is the file descriptor valid? */ + if ( (rfilp = get_filp(m_in.fd)) == NIL_FILP) return(err_code); + + return(stat_inode(rfilp->filp_ino, rfilp, m_in.buffer)); +} + + +/*===========================================================================* + * stat_inode * + *===========================================================================*/ +PRIVATE int stat_inode(rip, fil_ptr, user_addr) +register struct inode *rip; /* pointer to inode to stat */ +struct filp *fil_ptr; /* filp pointer, supplied by 'fstat' */ +char *user_addr; /* user space address where stat buf goes */ +{ +/* Common code for stat and fstat system calls. */ + + struct stat statbuf; + mode_t mo; + int r, s; + + /* Update the atime, ctime, and mtime fields in the inode, if need be. */ + if (rip->i_update) update_times(rip); + + /* Fill in the statbuf struct. */ + mo = rip->i_mode & I_TYPE; + s = (mo == I_CHAR_SPECIAL || mo == I_BLOCK_SPECIAL); /* true iff special */ + statbuf.st_dev = rip->i_dev; + statbuf.st_ino = rip->i_num; + statbuf.st_mode = rip->i_mode; + statbuf.st_nlink = rip->i_nlinks; + statbuf.st_uid = rip->i_uid; + statbuf.st_gid = rip->i_gid; + statbuf.st_rdev = (dev_t) (s ? rip->i_zone[0] : NO_DEV); + statbuf.st_size = rip->i_size; + + if (rip->i_pipe == I_PIPE) { + statbuf.st_mode &= ~I_REGULAR; /* wipe out I_REGULAR bit for pipes */ + if (fil_ptr != NIL_FILP && fil_ptr->filp_mode & R_BIT) + statbuf.st_size -= fil_ptr->filp_pos; + } + + statbuf.st_atime = rip->i_atime; + statbuf.st_mtime = rip->i_mtime; + statbuf.st_ctime = rip->i_ctime; + + /* Copy the struct to user space. */ + r = sys_datacopy(FS_PROC_NR, (vir_bytes) &statbuf, + who, (vir_bytes) user_addr, (phys_bytes) sizeof(statbuf)); + return(r); +} + +/*===========================================================================* + * do_fstatfs * + *===========================================================================*/ +PUBLIC int do_fstatfs() +{ + /* Perform the fstatfs(fd, buf) system call. */ + struct statfs st; + register struct filp *rfilp; + int r; + + /* Is the file descriptor valid? */ + if ( (rfilp = get_filp(m_in.fd)) == NIL_FILP) return(err_code); + + st.f_bsize = rfilp->filp_ino->i_sp->s_block_size; + + r = sys_datacopy(FS_PROC_NR, (vir_bytes) &st, + who, (vir_bytes) m_in.buffer, (phys_bytes) sizeof(st)); + + return(r); +} + + diff --git a/servers/fs/super.c b/servers/fs/super.c new file mode 100644 index 000000000..6d3c2f3e3 --- /dev/null +++ b/servers/fs/super.c @@ -0,0 +1,322 @@ +/* This file manages the super block table and the related data structures, + * namely, the bit maps that keep track of which zones and which inodes are + * allocated and which are free. When a new inode or zone is needed, the + * appropriate bit map is searched for a free entry. + * + * The entry points into this file are + * alloc_bit: somebody wants to allocate a zone or inode; find one + * free_bit: indicate that a zone or inode is available for allocation + * get_super: search the 'superblock' table for a device + * mounted: tells if file inode is on mounted (or ROOT) file system + * read_super: read a superblock + */ + +#include "fs.h" +#include <string.h> +#include <minix/com.h> +#include "buf.h" +#include "inode.h" +#include "super.h" +#include "const.h" + +/*===========================================================================* + * alloc_bit * + *===========================================================================*/ +PUBLIC bit_t alloc_bit(sp, map, origin) +struct super_block *sp; /* the filesystem to allocate from */ +int map; /* IMAP (inode map) or ZMAP (zone map) */ +bit_t origin; /* number of bit to start searching at */ +{ +/* Allocate a bit from a bit map and return its bit number. */ + + block_t start_block; /* first bit block */ + bit_t map_bits; /* how many bits are there in the bit map? */ + unsigned bit_blocks; /* how many blocks are there in the bit map? */ + unsigned block, word, bcount, wcount; + struct buf *bp; + bitchunk_t *wptr, *wlim, k; + bit_t i, b; + + if (sp->s_rd_only) + panic("can't allocate bit on read-only filesys.", NO_NUM); + + if (map == IMAP) { + start_block = START_BLOCK; + map_bits = sp->s_ninodes + 1; + bit_blocks = sp->s_imap_blocks; + } else { + start_block = START_BLOCK + sp->s_imap_blocks; + map_bits = sp->s_zones - (sp->s_firstdatazone - 1); + bit_blocks = sp->s_zmap_blocks; + } + + /* Figure out where to start the bit search (depends on 'origin'). */ + if (origin >= map_bits) origin = 0; /* for robustness */ + + /* Locate the starting place. */ + block = origin / BITS_PER_BLOCK(sp->s_block_size); + word = (origin % BITS_PER_BLOCK(sp->s_block_size)) / BITCHUNK_BITS; + + /* Iterate over all blocks plus one, because we start in the middle. */ + bcount = bit_blocks + 1; + do { + bp = get_block(sp->s_dev, start_block + block, NORMAL); + wlim = &bp->b_bitmap[BITMAP_CHUNKS(sp->s_block_size)]; + + /* Iterate over the words in block. */ + for (wptr = &bp->b_bitmap[word]; wptr < wlim; wptr++) { + + /* Does this word contain a free bit? */ + if (*wptr == (bitchunk_t) ~0) continue; + + /* Find and allocate the free bit. */ + k = conv2(sp->s_native, (int) *wptr); + for (i = 0; (k & (1 << i)) != 0; ++i) {} + + /* Bit number from the start of the bit map. */ + b = ((bit_t) block * BITS_PER_BLOCK(sp->s_block_size)) + + (wptr - &bp->b_bitmap[0]) * BITCHUNK_BITS + + i; + + /* Don't allocate bits beyond the end of the map. */ + if (b >= map_bits) break; + + /* Allocate and return bit number. */ + k |= 1 << i; + *wptr = conv2(sp->s_native, (int) k); + bp->b_dirt = DIRTY; + put_block(bp, MAP_BLOCK); + return(b); + } + put_block(bp, MAP_BLOCK); + if (++block >= bit_blocks) block = 0; /* last block, wrap around */ + word = 0; + } while (--bcount > 0); + return(NO_BIT); /* no bit could be allocated */ +} + + +/*===========================================================================* + * free_bit * + *===========================================================================*/ +PUBLIC void free_bit(sp, map, bit_returned) +struct super_block *sp; /* the filesystem to operate on */ +int map; /* IMAP (inode map) or ZMAP (zone map) */ +bit_t bit_returned; /* number of bit to insert into the map */ +{ +/* Return a zone or inode by turning off its bitmap bit. */ + + unsigned block, word, bit; + struct buf *bp; + bitchunk_t k, mask; + block_t start_block; + + if (sp->s_rd_only) + panic("can't free bit on read-only filesys.", NO_NUM); + + if (map == IMAP) { + start_block = START_BLOCK; + } else { + start_block = START_BLOCK + sp->s_imap_blocks; + } + block = bit_returned / BITS_PER_BLOCK(sp->s_block_size); + word = (bit_returned % BITS_PER_BLOCK(sp->s_block_size)) / BITCHUNK_BITS; + bit = bit_returned % BITCHUNK_BITS; + mask = 1 << bit; + + bp = get_block(sp->s_dev, start_block + block, NORMAL); + + k = conv2(sp->s_native, (int) bp->b_bitmap[word]); + if (!(k & mask)) { + panic(map == IMAP ? "tried to free unused inode" : + "tried to free unused block", NO_NUM); + } + + k &= ~mask; + bp->b_bitmap[word] = conv2(sp->s_native, (int) k); + bp->b_dirt = DIRTY; + + put_block(bp, MAP_BLOCK); +} + + +/*===========================================================================* + * get_super * + *===========================================================================*/ +PUBLIC struct super_block *get_super(dev) +dev_t dev; /* device number whose super_block is sought */ +{ +/* Search the superblock table for this device. It is supposed to be there. */ + + register struct super_block *sp; + + if(dev == NO_DEV) + panic("request for super_block of NO_DEV in get_super()", NO_NUM); + + for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) + if (sp->s_dev == dev) return(sp); + + /* Search failed. Something wrong. */ + panic("can't find superblock for device (in decimal)", (int) dev); + + return(NIL_SUPER); /* to keep the compiler and lint quiet */ +} + +/*===========================================================================* + * get_block_size * + *===========================================================================*/ +PUBLIC int get_block_size(dev_t dev) +{ +/* Search the superblock table for this device. */ + + register struct super_block *sp; + + if(dev == NO_DEV) + panic("request for block size of NO_DEV in get_block_size()", NO_NUM); + + for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) + if (sp->s_dev == dev) + return(sp->s_block_size); + + /* no mounted filesystem? use this block size then. */ + return MIN_BLOCK_SIZE; +} + + +/*===========================================================================* + * mounted * + *===========================================================================*/ +PUBLIC int mounted(rip) +register struct inode *rip; /* pointer to inode */ +{ +/* Report on whether the given inode is on a mounted (or ROOT) file system. */ + + register struct super_block *sp; + register dev_t dev; + + dev = (dev_t) rip->i_zone[0]; + if (dev == root_dev) return(TRUE); /* inode is on root file system */ + + for (sp = &super_block[0]; sp < &super_block[NR_SUPERS]; sp++) + if (sp->s_dev == dev) return(TRUE); + + return(FALSE); +} + + +/*===========================================================================* + * read_super * + *===========================================================================*/ +PUBLIC int read_super(sp) +register struct super_block *sp; /* pointer to a superblock */ +{ +/* Read a superblock. */ + dev_t dev; + int magic; + int version, native, sb_block, r; + off_t sb_bytes_offset, sb_io_offset; + static char sbbuf[MIN_BLOCK_SIZE]; + + dev = sp->s_dev; /* save device (will be overwritten by copy) */ + if(dev == NO_DEV) + panic("request for super_block of NO_DEV in read_super()", NO_NUM); + r = dev_io(DEV_READ, dev, FS_PROC_NR, + sbbuf, SUPER_BLOCK_BYTES, MIN_BLOCK_SIZE, 0); + if(r != MIN_BLOCK_SIZE) { + printf("dev_io failed for super block (%d)\n", r); + return r; + } + memcpy(sp, sbbuf, sizeof(*sp)); + sp->s_dev = NO_DEV; /* restore later */ + magic = sp->s_magic; /* determines file system type */ + + /* Get file system version and type. */ + if (magic == SUPER_MAGIC || magic == conv2(BYTE_SWAP, SUPER_MAGIC)) { + version = V1; + native = (magic == SUPER_MAGIC); + } else if (magic == SUPER_V2 || magic == conv2(BYTE_SWAP, SUPER_V2)) { + version = V2; + native = (magic == SUPER_V2); + } else if (magic == SUPER_V3) { + version = V3; + native = 1; + } else { + printf("unrecognized magic number\n"); + return(EINVAL); + } + + /* If the super block has the wrong byte order, swap the fields; the magic + * number doesn't need conversion. */ + sp->s_ninodes = conv4(native, sp->s_ninodes); + sp->s_nzones = conv2(native, (int) sp->s_nzones); + sp->s_imap_blocks = conv2(native, (int) sp->s_imap_blocks); + sp->s_zmap_blocks = conv2(native, (int) sp->s_zmap_blocks); + sp->s_firstdatazone = conv2(native, (int) sp->s_firstdatazone); + sp->s_log_zone_size = conv2(native, (int) sp->s_log_zone_size); + sp->s_max_size = conv4(native, sp->s_max_size); + sp->s_zones = conv4(native, sp->s_zones); + + /* In V1, the device size was kept in a short, s_nzones, which limited + * devices to 32K zones. For V2, it was decided to keep the size as a + * long. However, just changing s_nzones to a long would not work, since + * then the position of s_magic in the super block would not be the same + * in V1 and V2 file systems, and there would be no way to tell whether + * a newly mounted file system was V1 or V2. The solution was to introduce + * a new variable, s_zones, and copy the size there. + * + * Calculate some other numbers that depend on the version here too, to + * hide some of the differences. + */ + if (version == V1) { + sp->s_block_size = STATIC_BLOCK_SIZE; + sp->s_zones = sp->s_nzones; /* only V1 needs this copy */ + sp->s_inodes_per_block = V1_INODES_PER_BLOCK; + sp->s_ndzones = V1_NR_DZONES; + sp->s_nindirs = V1_INDIRECTS; + } else { + if(version == V2) + sp->s_block_size = STATIC_BLOCK_SIZE; + if(sp->s_block_size < MIN_BLOCK_SIZE) + return EINVAL; + sp->s_inodes_per_block = V2_INODES_PER_BLOCK(sp->s_block_size); + sp->s_ndzones = V2_NR_DZONES; + sp->s_nindirs = V2_INDIRECTS(sp->s_block_size); + } + + if(sp->s_block_size < MIN_BLOCK_SIZE || sp->s_block_size > MAX_BLOCK_SIZE) { + printf("block size (%d) out of range\n", sp->s_block_size); + return EINVAL; + } + if((sp->s_block_size % 512) != 0) { + printf("block size (%d) not multiple of sector size\n", + sp->s_block_size); + return EINVAL; + } + if(SUPER_SIZE > sp->s_block_size) { + printf("super block size size (%d) larger than block size (%d)\n", + SUPER_SIZE, sp->s_block_size); + return EINVAL; + } + if((sp->s_block_size % V2_INODE_SIZE) != 0 || + (sp->s_block_size % V1_INODE_SIZE) != 0) { + printf("super block size size (%d) not multiple of inode size\n", + sp->s_block_size); + return EINVAL; + } + + sp->s_isearch = 0; /* inode searches initially start at 0 */ + sp->s_zsearch = 0; /* zone searches initially start at 0 */ + sp->s_version = version; + sp->s_native = native; + + /* Make a few basic checks to see if super block looks reasonable. */ + if (sp->s_imap_blocks < 1 || sp->s_zmap_blocks < 1 + || sp->s_ninodes < 1 || sp->s_zones < 1 + || (unsigned) sp->s_log_zone_size > 4) { + printf("not enough imap or zone map blocks, \n"); + printf("or not enough inodes, or not enough zones, or zone size too large\n"); + return(EINVAL); + } + sp->s_dev = dev; /* restore device number */ + return(OK); +} diff --git a/servers/fs/super.h b/servers/fs/super.h new file mode 100644 index 000000000..e391f53ce --- /dev/null +++ b/servers/fs/super.h @@ -0,0 +1,60 @@ +/* Super block table. The root file system and every mounted file system + * has an entry here. The entry holds information about the sizes of the bit + * maps and inodes. The s_ninodes field gives the number of inodes available + * for files and directories, including the root directory. Inode 0 is + * on the disk, but not used. Thus s_ninodes = 4 means that 5 bits will be + * used in the bit map, bit 0, which is always 1 and not used, and bits 1-4 + * for files and directories. The disk layout is: + * + * Item # blocks + * boot block 1 + * super block 1 (offset 1kB) + * inode map s_imap_blocks + * zone map s_zmap_blocks + * inodes (s_ninodes + 'inodes per block' - 1)/'inodes per block' + * unused whatever is needed to fill out the current zone + * data zones (s_zones - s_firstdatazone) << s_log_zone_size + * + * A super_block slot is free if s_dev == NO_DEV. + */ + + +EXTERN struct super_block { + ino_t s_ninodes; /* # usable inodes on the minor device */ + zone1_t s_nzones; /* total device size, including bit maps etc */ + short s_imap_blocks; /* # of blocks used by inode bit map */ + short s_zmap_blocks; /* # of blocks used by zone bit map */ + zone1_t s_firstdatazone; /* number of first data zone */ + short s_log_zone_size; /* log2 of blocks/zone */ + short s_pad; /* try to avoid compiler-dependent padding */ + off_t s_max_size; /* maximum file size on this device */ + zone_t s_zones; /* number of zones (replaces s_nzones in V2) */ + short s_magic; /* magic number to recognize super-blocks */ + + /* The following items are valid on disk only for V3 and above */ + + /* The block size in bytes. Minimum MIN_BLOCK SIZE. SECTOR_SIZE + * multiple. If V1 or V2 filesystem, this should be + * initialised to STATIC_BLOCK_SIZE. Maximum MAX_BLOCK_SIZE. + */ + short s_pad2; /* try to avoid compiler-dependent padding */ + unsigned short s_block_size; /* block size in bytes. */ + char s_disk_version; /* filesystem format sub-version */ + + /* The following items are only used when the super_block is in memory. */ + struct inode *s_isup; /* inode for root dir of mounted file sys */ + struct inode *s_imount; /* inode mounted on */ + unsigned s_inodes_per_block; /* precalculated from magic number */ + dev_t s_dev; /* whose super block is this? */ + int s_rd_only; /* set to 1 iff file sys mounted read only */ + int s_native; /* set to 1 iff not byte swapped file system */ + int s_version; /* file system version, zero means bad magic */ + int s_ndzones; /* # direct zones in an inode */ + int s_nindirs; /* # indirect zones per indirect block */ + bit_t s_isearch; /* inodes below this bit number are in use */ + bit_t s_zsearch; /* all zones below this bit number are in use*/ +} super_block[NR_SUPERS]; + +#define NIL_SUPER (struct super_block *) 0 +#define IMAP 0 /* operating on the inode bit map */ +#define ZMAP 1 /* operating on the zone bit map */ diff --git a/servers/fs/symtab.h b/servers/fs/symtab.h new file mode 100644 index 000000000..a058948a1 --- /dev/null +++ b/servers/fs/symtab.h @@ -0,0 +1,200 @@ +struct fs_sym_entry { unsigned long symoffset; char *symname; } fs_sym_entries[] = { +{ 0x00000000, "" }, +{ 0x00000000, "begtext" }, +{ 0x00000000, "crtso" }, +{ 0x00000050, "___main" }, +{ 0x00000054, "_main" }, +{ 0x0000010d, "_get_wor" }, +{ 0x000001dc, "_buf_poo" }, +{ 0x00000282, "_reply" }, +{ 0x000002ba, "_fs_init" }, +{ 0x0000041a, "_igetenv" }, +{ 0x0000044e, "_load_ra" }, +{ 0x00000756, "_load_su" }, +{ 0x000007f8, "_do_crea" }, +{ 0x0000083c, "_do_open" }, +{ 0x000008a7, "_common_" }, +{ 0x00000b76, "_new_nod" }, +{ 0x00000c6e, "_pipe_op" }, +{ 0x00000cf7, "_do_mkno" }, +{ 0x00000da6, "_do_mkdi" }, +{ 0x00000f28, "_do_clos" }, +{ 0x000010a6, "_do_lsee" }, +{ 0x00001160, "_do_read" }, +{ 0x0000116d, "_read_wr" }, +{ 0x0000164a, "_rw_chun" }, +{ 0x000017ee, "_read_ma" }, +{ 0x0000194d, "_rd_indi" }, +{ 0x000019d2, "_read_ah" }, +{ 0x00001a36, "_rahead" }, +{ 0x00001bdc, "_do_writ" }, +{ 0x00001be9, "_write_m" }, +{ 0x00001e0b, "_wr_indi" }, +{ 0x00001e5f, "_clear_z" }, +{ 0x00001f29, "_new_blo" }, +{ 0x00002028, "_zero_bl" }, +{ 0x0000205c, "_do_pipe" }, +{ 0x000021d1, "_pipe_ch" }, +{ 0x0000234d, "_suspend" }, +{ 0x000023de, "_release" }, +{ 0x0000244c, "_revive" }, +{ 0x000024df, "_do_unpa" }, +{ 0x000025f0, "_map_dri" }, +{ 0x0000268d, "_map_con" }, +{ 0x000027b4, "_do_fkey" }, +{ 0x000027f7, "_fproc_d" }, +{ 0x000028c5, "_dtab_dm" }, +{ 0x000029f4, "_dev_ope" }, +{ 0x00002a51, "_dev_clo" }, +{ 0x00002a79, "_dev_io" }, +{ 0x00002b2d, "_gen_opc" }, +{ 0x00002b77, "_tty_opc" }, +{ 0x00002bfa, "_ctty_op" }, +{ 0x00002c16, "_do_sets" }, +{ 0x00002c4a, "_do_ioct" }, +{ 0x00002ccb, "_gen_io" }, +{ 0x00002da1, "_ctty_io" }, +{ 0x00002dfb, "_no_dev" }, +{ 0x00002e05, "_clone_o" }, +{ 0x00002ef8, "_eat_pat" }, +{ 0x00002f3d, "_last_di" }, +{ 0x00003009, "_get_nam" }, +{ 0x000030a3, "_advance" }, +{ 0x00003237, "_search_" }, +{ 0x000035a0, "_do_moun" }, +{ 0x0000394a, "_do_umou" }, +{ 0x000039ab, "_unmount" }, +{ 0x00003a68, "_name_to" }, +{ 0x00003aec, "_do_link" }, +{ 0x00003c76, "_do_unli" }, +{ 0x00003d86, "_do_rena" }, +{ 0x00004194, "_truncat" }, +{ 0x000042f0, "_remove_" }, +{ 0x000043bd, "_unlink_" }, +{ 0x0000445c, "_alloc_b" }, +{ 0x0000464f, "_free_bi" }, +{ 0x00004782, "_get_sup" }, +{ 0x000047d7, "_get_blo" }, +{ 0x0000481f, "_mounted" }, +{ 0x0000486f, "_read_su" }, +{ 0x00004b2c, "_get_ino" }, +{ 0x00004bab, "_put_ino" }, +{ 0x00004c0c, "_alloc_i" }, +{ 0x00004d16, "_wipe_in" }, +{ 0x00004d45, "_free_in" }, +{ 0x00004d7f, "_update_" }, +{ 0x00004dc0, "_rw_inod" }, +{ 0x00004ede, "_old_ico" }, +{ 0x00005030, "_new_ico" }, +{ 0x000051f2, "_dup_ino" }, +{ 0x00005200, "_get_blo" }, +{ 0x0000537c, "_put_blo" }, +{ 0x00005454, "_alloc_z" }, +{ 0x00005506, "_free_zo" }, +{ 0x0000554d, "_rw_bloc" }, +{ 0x0000561c, "_invalid" }, +{ 0x00005650, "_flushal" }, +{ 0x000056ae, "_rw_scat" }, +{ 0x000058c7, "_rm_lru" }, +{ 0x0000590c, "_get_fd" }, +{ 0x00005989, "_get_fil" }, +{ 0x000059b7, "_find_fi" }, +{ 0x000059f0, "_do_chdi" }, +{ 0x00005abb, "_do_chro" }, +{ 0x00005af4, "_change" }, +{ 0x00005b78, "_do_stat" }, +{ 0x00005bd6, "_do_fsta" }, +{ 0x00005c08, "_stat_in" }, +{ 0x00005d00, "_do_fsta" }, +{ 0x00005d54, "_do_chmo" }, +{ 0x00005e41, "_do_chow" }, +{ 0x00005f29, "_do_umas" }, +{ 0x00005f66, "_do_acce" }, +{ 0x00005fe3, "_forbidd" }, +{ 0x00006149, "_read_on" }, +{ 0x00006168, "_do_utim" }, +{ 0x0000624e, "_do_time" }, +{ 0x0000625f, "_do_stim" }, +{ 0x000062bd, "_do_tims" }, +{ 0x00006308, "_do_cmos" }, +{ 0x00006404, "_get_cmo" }, +{ 0x00006594, "_read_re" }, +{ 0x000065c5, "_bcd_to_" }, +{ 0x000065e2, "_dec_to_" }, +{ 0x00006604, "_lock_op" }, +{ 0x000069d3, "_lock_re" }, +{ 0x00006a20, "_do_gets" }, +{ 0x00006a27, "_do_dup" }, +{ 0x00006ad0, "_do_fcnt" }, +{ 0x00006c0e, "_do_sync" }, +{ 0x00006c99, "_do_rebo" }, +{ 0x00006d1a, "_do_fork" }, +{ 0x00006dc6, "_do_exec" }, +{ 0x00006e21, "_do_exit" }, +{ 0x00006fc5, "_do_set" }, +{ 0x0000702f, "_do_revi" }, +{ 0x00007068, "_do_svrc" }, +{ 0x0000710c, "_clock_t" }, +{ 0x00007151, "_fetch_n" }, +{ 0x000071e3, "_no_sys" }, +{ 0x000071ed, "_panic" }, +{ 0x0000727f, "_conv2" }, +{ 0x000072a9, "_conv4" }, +{ 0x000072f8, "_printf" }, +{ 0x00007610, "_kputc" }, +{ 0x000076bc, "_sys_abo" }, +{ 0x00007708, "_sys_exi" }, +{ 0x00007728, "_sys_get" }, +{ 0x00007768, "_sys_in" }, +{ 0x000077a4, "_sys_kil" }, +{ 0x000077c8, "_sys_out" }, +{ 0x000077fc, "_sys_vir" }, +{ 0x0000784c, "__taskca" }, +{ 0x00007878, "_clk_fla" }, +{ 0x000078c0, "_clk_tim" }, +{ 0x00007908, "_fkey_ct" }, +{ 0x0000792c, "_server_" }, +{ 0x00007968, "_atoi" }, +{ 0x00007980, "__calls" }, +{ 0x00007999, "_exit" }, +{ 0x000079bc, "_strtol" }, +{ 0x000079d7, "_strtoul" }, +{ 0x000079f2, "_string2" }, +{ 0x00007b8c, "__exit" }, +{ 0x00007b94, "__send" }, +{ 0x00007ba8, "__nb_sen" }, +{ 0x00007bbc, "__receiv" }, +{ 0x00007bd0, "__nb_rec" }, +{ 0x00007be4, "__sendre" }, +{ 0x00007bf8, "_memcpy" }, +{ 0x00007c18, "_memset" }, +{ 0x00007c3c, "slword" }, +{ 0x00007c52, "sword" }, +{ 0x00007c5b, "sbyte" }, +{ 0x00007c5d, "done" }, +{ 0x00007c68, "_strcmp" }, +{ 0x00007c78, "_strlen" }, +{ 0x00007c88, "_strncmp" }, +{ 0x00007c98, "___exit" }, +{ 0x00007cb8, "__memmov" }, +{ 0x00007cce, "__memcpy" }, +{ 0x00007ce0, "uplword" }, +{ 0x00007ced, "upword" }, +{ 0x00007cf4, "upbyte" }, +{ 0x00007cf6, "done" }, +{ 0x00007cfd, "downward" }, +{ 0x00007d18, "__strncm" }, +{ 0x00007d28, "compare" }, +{ 0x00007d34, "done" }, +{ 0x00007d48, "__strnle" }, +{ 0x00007d5c, "no0" }, +{ 0x00007d68, "__syscal" }, +{ 0x00007dac, ".blm" }, +{ 0x00007dc4, ".csa4" }, +{ 0x00007df0, ".csb4" }, +{ 0x00007e18, ".fat" }, +{ 0x00007e24, ".stop" }, +{ 0x00007e2c, ".trp" }, +{ 0x00007e44, "__etext" }, +{ 0x00007e44, "endtext" }, +}; diff --git a/servers/fs/table.c b/servers/fs/table.c new file mode 100644 index 000000000..8757ccba3 --- /dev/null +++ b/servers/fs/table.c @@ -0,0 +1,106 @@ +/* This file contains the table used to map system call numbers onto the + * routines that perform them. + */ + +#define _TABLE + +#include "fs.h" +#include <minix/callnr.h> +#include <minix/com.h> +#include "buf.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "lock.h" +#include "super.h" + +PUBLIC _PROTOTYPE (int (*call_vec[]), (void) ) = { + no_sys, /* 0 = unused */ + do_exit, /* 1 = exit */ + do_fork, /* 2 = fork */ + do_read, /* 3 = read */ + do_write, /* 4 = write */ + do_open, /* 5 = open */ + do_close, /* 6 = close */ + no_sys, /* 7 = wait */ + do_creat, /* 8 = creat */ + do_link, /* 9 = link */ + do_unlink, /* 10 = unlink */ + no_sys, /* 11 = waitpid */ + do_chdir, /* 12 = chdir */ + do_time, /* 13 = time */ + do_mknod, /* 14 = mknod */ + do_chmod, /* 15 = chmod */ + do_chown, /* 16 = chown */ + no_sys, /* 17 = break */ + do_stat, /* 18 = stat */ + do_lseek, /* 19 = lseek */ + no_sys, /* 20 = getpid */ + do_mount, /* 21 = mount */ + do_umount, /* 22 = umount */ + do_set, /* 23 = setuid */ + no_sys, /* 24 = getuid */ + do_stime, /* 25 = stime */ + no_sys, /* 26 = ptrace */ + no_sys, /* 27 = alarm */ + do_fstat, /* 28 = fstat */ + no_sys, /* 29 = pause */ + do_utime, /* 30 = utime */ + no_sys, /* 31 = (stty) */ + no_sys, /* 32 = (gtty) */ + do_access, /* 33 = access */ + no_sys, /* 34 = (nice) */ + no_sys, /* 35 = (ftime) */ + do_sync, /* 36 = sync */ + no_sys, /* 37 = kill */ + do_rename, /* 38 = rename */ + do_mkdir, /* 39 = mkdir */ + do_unlink, /* 40 = rmdir */ + do_dup, /* 41 = dup */ + do_pipe, /* 42 = pipe */ + do_tims, /* 43 = times */ + no_sys, /* 44 = (prof) */ + no_sys, /* 45 = unused */ + do_set, /* 46 = setgid */ + no_sys, /* 47 = getgid */ + no_sys, /* 48 = (signal)*/ + no_sys, /* 49 = unused */ + no_sys, /* 50 = unused */ + no_sys, /* 51 = (acct) */ + no_sys, /* 52 = (phys) */ + no_sys, /* 53 = (lock) */ + do_ioctl, /* 54 = ioctl */ + do_fcntl, /* 55 = fcntl */ + no_sys, /* 56 = (mpx) */ + no_sys, /* 57 = unused */ + no_sys, /* 58 = unused */ + do_exec, /* 59 = execve */ + do_umask, /* 60 = umask */ + do_chroot, /* 61 = chroot */ + do_setsid, /* 62 = setsid */ + no_sys, /* 63 = getpgrp */ + + no_sys, /* 64 = KSIG: signals originating in the kernel */ + do_unpause, /* 65 = UNPAUSE */ + no_sys, /* 66 = unused */ + do_revive, /* 67 = REVIVE */ + no_sys, /* 68 = TASK_REPLY */ + no_sys, /* 69 = unused */ + no_sys, /* 70 = unused */ + no_sys, /* 71 = si */ + no_sys, /* 72 = sigsuspend */ + no_sys, /* 73 = sigpending */ + no_sys, /* 74 = sigprocmask */ + no_sys, /* 75 = sigreturn */ + do_reboot, /* 76 = reboot */ + do_svrctl, /* 77 = svrctl */ + + do_cmostime, /* 78 = cmostime */ + do_getsysinfo, /* 79 = getsysinfo */ + no_sys, /* 80 = unused */ + no_sys, /* 81 = unused */ + do_fstatfs, /* 82 = fstatfs */ +}; +/* This should not fail with "array size is negative": */ +extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1]; + diff --git a/servers/fs/time.c b/servers/fs/time.c new file mode 100644 index 000000000..d3e2125a5 --- /dev/null +++ b/servers/fs/time.c @@ -0,0 +1,111 @@ +/* This file takes care of those system calls that deal with time. + * + * The entry points into this file are + * do_utime: perform the UTIME system call + * do_time: perform the TIME system call + * do_stime: perform the STIME system call + * do_tims: perform the TIMES system call + */ + +#include "fs.h" +#include <minix/callnr.h> +#include <minix/com.h> +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "param.h" + + +/*===========================================================================* + * do_utime * + *===========================================================================*/ +PUBLIC int do_utime() +{ +/* Perform the utime(name, timep) system call. */ + + register struct inode *rip; + register int len, r; + + /* Adjust for case of 'timep' being NULL; + * utime_strlen then holds the actual size: strlen(name)+1. + */ + len = m_in.utime_length; + if (len == 0) len = m_in.utime_strlen; + + /* Temporarily open the file. */ + if (fetch_name(m_in.utime_file, len, M1) != OK) return(err_code); + if ( (rip = eat_path(user_path)) == NIL_INODE) return(err_code); + + /* Only the owner of a file or the super_user can change its time. */ + r = OK; + if (rip->i_uid != fp->fp_effuid && !super_user) r = EPERM; + if (m_in.utime_length == 0 && r != OK) r = forbidden(rip, W_BIT); + if (read_only(rip) != OK) r = EROFS; /* not even su can touch if R/O */ + if (r == OK) { + if (m_in.utime_length == 0) { + rip->i_atime = clock_time(); + rip->i_mtime = rip->i_atime; + } else { + rip->i_atime = m_in.utime_actime; + rip->i_mtime = m_in.utime_modtime; + } + rip->i_update = CTIME; /* discard any stale ATIME and MTIME flags */ + rip->i_dirt = DIRTY; + } + + put_inode(rip); + return(r); +} + + + +/*===========================================================================* + * do_time * + *===========================================================================*/ +PUBLIC int do_time() + +{ +/* Perform the time(tp) system call. */ + + m_out.reply_l1 = clock_time(); /* return time in seconds */ + return(OK); +} + + +/*===========================================================================* + * do_stime * + *===========================================================================*/ +PUBLIC int do_stime() +{ +/* Perform the stime(tp) system call. Retrieve the system's uptime (ticks + * since boot) and store the time in seconds at system boot in the global + * variable 'boottime'. + */ + + register int k; + clock_t uptime; + + if (!super_user) return(EPERM); + if ( (k=sys_getuptime(&uptime)) != OK) panic("do_stime error", k); + boottime = (long) m_in.tp - (uptime/HZ); + return(OK); +} + + +/*===========================================================================* + * do_tims * + *===========================================================================*/ +PUBLIC int do_tims() +{ +/* Perform the times(buffer) system call. */ + + clock_t t[5]; + + sys_times(who, t); + m_out.reply_t1 = t[0]; + m_out.reply_t2 = t[1]; + m_out.reply_t3 = t[2]; + m_out.reply_t4 = t[3]; + m_out.reply_t5 = t[4]; + return(OK); +} diff --git a/servers/fs/type.h b/servers/fs/type.h new file mode 100644 index 000000000..19749fef0 --- /dev/null +++ b/servers/fs/type.h @@ -0,0 +1,23 @@ +/* Declaration of the V1 inode as it is on the disk (not in core). */ +typedef struct { /* V1.x disk inode */ + mode_t d1_mode; /* file type, protection, etc. */ + uid_t d1_uid; /* user id of the file's owner */ + off_t d1_size; /* current file size in bytes */ + time_t d1_mtime; /* when was file data last changed */ + u8_t d1_gid; /* group number */ + u8_t d1_nlinks; /* how many links to this file */ + u16_t d1_zone[V1_NR_TZONES]; /* block nums for direct, ind, and dbl ind */ +} d1_inode; + +/* Declaration of the V2 inode as it is on the disk (not in core). */ +typedef struct { /* V2.x disk inode */ + mode_t d2_mode; /* file type, protection, etc. */ + u16_t d2_nlinks; /* how many links to this file. HACK! */ + uid_t d2_uid; /* user id of the file's owner. */ + u16_t d2_gid; /* group number HACK! */ + off_t d2_size; /* current file size in bytes */ + time_t d2_atime; /* when was file data last accessed */ + time_t d2_mtime; /* when was file data last changed */ + time_t d2_ctime; /* when was inode data last changed */ + zone_t d2_zone[V2_NR_TZONES]; /* block nums for direct, ind, and dbl ind */ +} d2_inode; diff --git a/servers/fs/utility.c b/servers/fs/utility.c new file mode 100644 index 000000000..cb9e05ccd --- /dev/null +++ b/servers/fs/utility.c @@ -0,0 +1,195 @@ +/* This file contains a few general purpose utility routines. + * + * The entry points into this file are + * clock_time: ask the clock task for the real time + * copy: copy a block of data + * fetch_name: go get a path name from user space + * no_sys: reject a system call that FS does not handle + * panic: something awful has occurred; MINIX cannot continue + * conv2: do byte swapping on a 16-bit int + * conv4: do byte swapping on a 32-bit long + */ + +#define STACKINFO 1 + +#if STACKINFO +#include "symtab.h" +#endif + +#include "fs.h" +#include <minix/com.h> +#include <unistd.h> +#include "buf.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "param.h" + +#if STACKINFO + +struct i386_frame { + struct i386_frame *next; + int retaddr; + int arg0; +}; + +PUBLIC void +show_stack(void) +{ + unsigned long *s; + int i, t, p = 0; + s = (unsigned long *) &i; + for(i = 0; i < 120; i++, s++) { + char *symname = NULL; + if(!*s) continue; +#define ELEMENTS (sizeof(fs_sym_entries)/sizeof(struct fs_sym_entry)) + for(t = 0; t < ELEMENTS-1; t++) { + if(*s >= fs_sym_entries[t].symoffset && + *s < fs_sym_entries[t+1].symoffset) { + symname = fs_sym_entries[t].symname; + break; + } + } + if(!symname) continue; + if(!(p%7)) printf("\n"); + p++; + printf("%-9s ", symname); + } + printf("\n"); +} +#endif + +PRIVATE int panicking; /* inhibits recursive panics during sync */ + +/*===========================================================================* + * clock_time * + *===========================================================================*/ +PUBLIC time_t clock_time() +{ +/* This routine returns the time in seconds since 1.1.1970. MINIX is an + * astrophysically naive system that assumes the earth rotates at a constant + * rate and that such things as leap seconds do not exist. + */ + + register int k; + clock_t uptime; + + if ( (k=sys_getuptime(&uptime)) != OK) panic("clock_time err", k); + return( (time_t) (boottime + (uptime/HZ))); +} + + +/*===========================================================================* + * fetch_name * + *===========================================================================*/ +PUBLIC int fetch_name(path, len, flag) +char *path; /* pointer to the path in user space */ +int len; /* path length, including 0 byte */ +int flag; /* M3 means path may be in message */ +{ +/* Go get path and put it in 'user_path'. + * If 'flag' = M3 and 'len' <= M3_STRING, the path is present in 'message'. + * If it is not, go copy it from user space. + */ + + register char *rpu, *rpm; + int r; + + /* Check name length for validity. */ + if (len <= 0) { + err_code = EINVAL; + return(EGENERIC); + } + + if (len > PATH_MAX) { + err_code = ENAMETOOLONG; + return(EGENERIC); + } + + if (flag == M3 && len <= M3_STRING) { + /* Just copy the path from the message to 'user_path'. */ + rpu = &user_path[0]; + rpm = m_in.pathname; /* contained in input message */ + do { *rpu++ = *rpm++; } while (--len); + r = OK; + } else { + /* String is not contained in the message. Get it from user space. */ + r = sys_datacopy(who, (vir_bytes) path, + FS_PROC_NR, (vir_bytes) user_path, (phys_bytes) len); + } + return(r); +} + + +/*===========================================================================* + * no_sys * + *===========================================================================*/ +PUBLIC int no_sys() +{ +/* Somebody has used an illegal system call number */ + + return(EINVAL); +} + + +/*===========================================================================* + * panic * + *===========================================================================*/ +PUBLIC void panic(format, num) +char *format; /* format string */ +int num; /* number to go with format string */ +{ +/* Something awful has happened. Panics are caused when an internal + * inconsistency is detected, e.g., a programming error or illegal value of a + * defined constant. + */ + int i; + + if (panicking) return; /* do not panic during a sync */ + panicking = TRUE; /* prevent another panic during the sync */ + +#if STACKINFO + show_stack(); +#endif + + printf("File system panic: %s ", format); + if (num != NO_NUM) printf("%d",num); + (void) do_sync(); /* flush everything to the disk */ + sys_exit(1); + printf("FS: I'm still alive after exit!\n"); + while(1) ; +} + + +/*===========================================================================* + * conv2 * + *===========================================================================*/ +PUBLIC unsigned conv2(norm, w) +int norm; /* TRUE if no swap, FALSE for byte swap */ +int w; /* promotion of 16-bit word to be swapped */ +{ +/* Possibly swap a 16-bit word between 8086 and 68000 byte order. */ + + if (norm) return( (unsigned) w & 0xFFFF); + return( ((w&BYTE) << 8) | ( (w>>8) & BYTE)); +} + + +/*===========================================================================* + * conv4 * + *===========================================================================*/ +PUBLIC long conv4(norm, x) +int norm; /* TRUE if no swap, FALSE for byte swap */ +long x; /* 32-bit long to be byte swapped */ +{ +/* Possibly swap a 32-bit long between 8086 and 68000 byte order. */ + + unsigned lo, hi; + long l; + + if (norm) return(x); /* byte order was already ok */ + lo = conv2(FALSE, (int) x & 0xFFFF); /* low-order half, byte swapped */ + hi = conv2(FALSE, (int) (x>>16) & 0xFFFF); /* high-order half, swapped */ + l = ( (long) lo <<16) | hi; + return(l); +} diff --git a/servers/fs/write.c b/servers/fs/write.c new file mode 100644 index 000000000..0ad890572 --- /dev/null +++ b/servers/fs/write.c @@ -0,0 +1,252 @@ +/* This file is the counterpart of "read.c". It contains the code for writing + * insofar as this is not contained in read_write(). + * + * The entry points into this file are + * do_write: call read_write to perform the WRITE system call + * clear_zone: erase a zone in the middle of a file + * new_block: acquire a new block + */ + +#include "fs.h" +#include <string.h> +#include "buf.h" +#include "file.h" +#include "fproc.h" +#include "inode.h" +#include "super.h" + +FORWARD _PROTOTYPE( int write_map, (struct inode *rip, off_t position, + zone_t new_zone) ); + +FORWARD _PROTOTYPE( void wr_indir, (struct buf *bp, int index, zone_t zone) ); + +/*===========================================================================* + * do_write * + *===========================================================================*/ +PUBLIC int do_write() +{ +/* Perform the write(fd, buffer, nbytes) system call. */ + + return(read_write(WRITING)); +} + + +/*===========================================================================* + * write_map * + *===========================================================================*/ +PRIVATE int write_map(rip, position, new_zone) +register struct inode *rip; /* pointer to inode to be changed */ +off_t position; /* file address to be mapped */ +zone_t new_zone; /* zone # to be inserted */ +{ +/* Write a new zone into an inode. */ + int scale, ind_ex, new_ind, new_dbl, zones, nr_indirects, single, zindex, ex; + zone_t z, z1; + register block_t b; + long excess, zone; + struct buf *bp; + + rip->i_dirt = DIRTY; /* inode will be changed */ + bp = NIL_BUF; + scale = rip->i_sp->s_log_zone_size; /* for zone-block conversion */ + zone = (position/rip->i_sp->s_block_size) >> scale; /* relative zone # to insert */ + zones = rip->i_ndzones; /* # direct zones in the inode */ + nr_indirects = rip->i_nindirs;/* # indirect zones per indirect block */ + + /* Is 'position' to be found in the inode itself? */ + if (zone < zones) { + zindex = (int) zone; /* we need an integer here */ + rip->i_zone[zindex] = new_zone; + return(OK); + } + + /* It is not in the inode, so it must be single or double indirect. */ + excess = zone - zones; /* first Vx_NR_DZONES don't count */ + new_ind = FALSE; + new_dbl = FALSE; + + if (excess < nr_indirects) { + /* 'position' can be located via the single indirect block. */ + z1 = rip->i_zone[zones]; /* single indirect zone */ + single = TRUE; + } else { + /* 'position' can be located via the double indirect block. */ + if ( (z = rip->i_zone[zones+1]) == NO_ZONE) { + /* Create the double indirect block. */ + if ( (z = alloc_zone(rip->i_dev, rip->i_zone[0])) == NO_ZONE) + return(err_code); + rip->i_zone[zones+1] = z; + new_dbl = TRUE; /* set flag for later */ + } + + /* Either way, 'z' is zone number for double indirect block. */ + excess -= nr_indirects; /* single indirect doesn't count */ + ind_ex = (int) (excess / nr_indirects); + excess = excess % nr_indirects; + if (ind_ex >= nr_indirects) return(EFBIG); + b = (block_t) z << scale; + bp = get_block(rip->i_dev, b, (new_dbl ? NO_READ : NORMAL)); + if (new_dbl) zero_block(bp); + z1 = rd_indir(bp, ind_ex); + single = FALSE; + } + + /* z1 is now single indirect zone; 'excess' is index. */ + if (z1 == NO_ZONE) { + /* Create indirect block and store zone # in inode or dbl indir blk. */ + z1 = alloc_zone(rip->i_dev, rip->i_zone[0]); + if (single) + rip->i_zone[zones] = z1; /* update inode */ + else + wr_indir(bp, ind_ex, z1); /* update dbl indir */ + + new_ind = TRUE; + if (bp != NIL_BUF) bp->b_dirt = DIRTY; /* if double ind, it is dirty*/ + if (z1 == NO_ZONE) { + put_block(bp, INDIRECT_BLOCK); /* release dbl indirect blk */ + return(err_code); /* couldn't create single ind */ + } + } + put_block(bp, INDIRECT_BLOCK); /* release double indirect blk */ + + /* z1 is indirect block's zone number. */ + b = (block_t) z1 << scale; + bp = get_block(rip->i_dev, b, (new_ind ? NO_READ : NORMAL) ); + if (new_ind) zero_block(bp); + ex = (int) excess; /* we need an int here */ + wr_indir(bp, ex, new_zone); + bp->b_dirt = DIRTY; + put_block(bp, INDIRECT_BLOCK); + + return(OK); +} + + +/*===========================================================================* + * wr_indir * + *===========================================================================*/ +PRIVATE void wr_indir(bp, index, zone) +struct buf *bp; /* pointer to indirect block */ +int index; /* index into *bp */ +zone_t zone; /* zone to write */ +{ +/* Given a pointer to an indirect block, write one entry. */ + + struct super_block *sp; + + sp = get_super(bp->b_dev); /* need super block to find file sys type */ + + /* write a zone into an indirect block */ + if (sp->s_version == V1) + bp->b_v1_ind[index] = (zone1_t) conv2(sp->s_native, (int) zone); + else + bp->b_v2_ind[index] = (zone_t) conv4(sp->s_native, (long) zone); +} + + +/*===========================================================================* + * clear_zone * + *===========================================================================*/ +PUBLIC void clear_zone(rip, pos, flag) +register struct inode *rip; /* inode to clear */ +off_t pos; /* points to block to clear */ +int flag; /* 0 if called by read_write, 1 by new_block */ +{ +/* Zero a zone, possibly starting in the middle. The parameter 'pos' gives + * a byte in the first block to be zeroed. Clearzone() is called from + * read_write and new_block(). + */ + + register struct buf *bp; + register block_t b, blo, bhi; + register off_t next; + register int scale; + register zone_t zone_size; + + /* If the block size and zone size are the same, clear_zone() not needed. */ + scale = rip->i_sp->s_log_zone_size; + if (scale == 0) return; + + zone_size = (zone_t) rip->i_sp->s_block_size << scale; + if (flag == 1) pos = (pos/zone_size) * zone_size; + next = pos + rip->i_sp->s_block_size - 1; + + /* If 'pos' is in the last block of a zone, do not clear the zone. */ + if (next/zone_size != pos/zone_size) return; + if ( (blo = read_map(rip, next)) == NO_BLOCK) return; + bhi = ( ((blo>>scale)+1) << scale) - 1; + + /* Clear all the blocks between 'blo' and 'bhi'. */ + for (b = blo; b <= bhi; b++) { + bp = get_block(rip->i_dev, b, NO_READ); + zero_block(bp); + put_block(bp, FULL_DATA_BLOCK); + } +} + + +/*===========================================================================* + * new_block * + *===========================================================================*/ +PUBLIC struct buf *new_block(rip, position) +register struct inode *rip; /* pointer to inode */ +off_t position; /* file pointer */ +{ +/* Acquire a new block and return a pointer to it. Doing so may require + * allocating a complete zone, and then returning the initial block. + * On the other hand, the current zone may still have some unused blocks. + */ + + register struct buf *bp; + block_t b, base_block; + zone_t z; + zone_t zone_size; + int scale, r; + struct super_block *sp; + + /* Is another block available in the current zone? */ + if ( (b = read_map(rip, position)) == NO_BLOCK) { + /* Choose first zone if possible. */ + /* Lose if the file is nonempty but the first zone number is NO_ZONE + * corresponding to a zone full of zeros. It would be better to + * search near the last real zone. + */ + if (rip->i_zone[0] == NO_ZONE) { + sp = rip->i_sp; + z = sp->s_firstdatazone; + } else { + z = rip->i_zone[0]; /* hunt near first zone */ + } + if ( (z = alloc_zone(rip->i_dev, z)) == NO_ZONE) return(NIL_BUF); + if ( (r = write_map(rip, position, z)) != OK) { + free_zone(rip->i_dev, z); + err_code = r; + return(NIL_BUF); + } + + /* If we are not writing at EOF, clear the zone, just to be safe. */ + if ( position != rip->i_size) clear_zone(rip, position, 1); + scale = rip->i_sp->s_log_zone_size; + base_block = (block_t) z << scale; + zone_size = (zone_t) rip->i_sp->s_block_size << scale; + b = base_block + (block_t)((position % zone_size)/rip->i_sp->s_block_size); + } + + bp = get_block(rip->i_dev, b, NO_READ); + zero_block(bp); + return(bp); +} + + +/*===========================================================================* + * zero_block * + *===========================================================================*/ +PUBLIC void zero_block(bp) +register struct buf *bp; /* pointer to buffer to zero */ +{ +/* Zero a block. */ + struct super_block *sb; + int block_size; + memset(bp->b_data, 0, MAX_BLOCK_SIZE); + bp->b_dirt = DIRTY; +} diff --git a/servers/inet/Makefile b/servers/inet/Makefile new file mode 100644 index 000000000..a37f7c8a3 --- /dev/null +++ b/servers/inet/Makefile @@ -0,0 +1,410 @@ +# Makefile for Network Server (INET). +SERVER = inet + +# directories +u = /usr +i = $u/include +s = $i/sys +h = $i/minix +n = $i/net +g = generic + +# programs, flags, and libraries +CC = cc +CPPFLAGS = -I. -I.. +CFLAGS = $(OPT) $(CPPFLAGS) -m +LDFLAGS = -i +LIBS = -lsys -lutils + +OBJ = buf.o clock.o inet.o inet_config.o \ + mnx_eth.o mq.o sr.o stacktrace.o \ + $g/udp.o $g/arp.o $g/eth.o $g/event.o \ + $g/icmp.o $g/io.o $g/ip.o $g/ip_ioctl.o \ + $g/ip_lib.o $g/ip_read.o $g/ip_write.o \ + $g/ipr.o $g/tcp.o $g/tcp_lib.o \ + $g/tcp_recv.o $g/tcp_send.o $g/ip_eth.o \ + $g/ip_ps.o $g/psip.o + +# build local binary +all build: $(SERVER) +$(SERVER): inet.a + $(CC) -o $@ $(LDFLAGS) inet.a version.c $(LIBS) + install -S 2kw $@ + +inet.a: $(OBJ) + @rm -f $@ + aal cr $@ $(OBJ) + +# install with other servers +install: /usr/sbin/servers/$(SERVER) +/usr/sbin/servers/$(SERVER): $(SERVER) + install -o root -cs $? $@ + +clean: + rm -f $(SERVER) *.a *.o *.bak .all + +# depencencies +.all: $h/com.h +.all: $h/config.h +.all: $h/const.h +.all: $h/syslib.h +.all: $h/type.h +.all: $i/ansi.h +.all: $i/errno.h +.all: $i/stddef.h +.all: $i/stdlib.h +.all: $i/string.h +.all: $n/gen/eth_hdr.h +.all: $n/gen/eth_io.h +.all: $n/gen/ether.h +.all: $n/gen/icmp.h +.all: $n/gen/icmp_hdr.h +.all: $n/gen/in.h +.all: $n/gen/ip_hdr.h +.all: $n/gen/ip_io.h +.all: $n/gen/oneCsum.h +.all: $n/gen/psip_hdr.h +.all: $n/gen/psip_io.h +.all: $n/gen/route.h +.all: $n/gen/tcp.h +.all: $n/gen/tcp_hdr.h +.all: $n/gen/tcp_io.h +.all: $n/gen/udp.h +.all: $n/gen/udp_hdr.h +.all: $n/gen/udp_io.h +.all: $n/hton.h +.all: $h/ioctl.h +.all: $n/ioctl.h +.all: $s/types.h +.all: const.h +.all: inet_config.h +.all: inet.h + touch .all +a = .all # Trick to avoid many dependencies (that overload make) + +buf.o: $g/assert.h +buf.o: $g/buf.h +buf.o: $g/type.h +buf.o: $i/stdlib.h +buf.o: $i/string.h +buf.o: inet.h +buf.o: $a + +clock.o: $g/assert.h +clock.o: $g/buf.h +clock.o: $g/clock.h +clock.o: $g/type.h +clock.o: inet.h +clock.o: proto.h +clock.o: $a + +inet.o: $g/arp.h +inet.o: $g/assert.h +inet.o: $g/buf.h +inet.o: $g/clock.h +inet.o: $g/eth.h +inet.o: $g/event.h +inet.o: $g/ip.h +inet.o: $g/psip.h +inet.o: $g/sr.h +inet.o: $g/tcp.h +inet.o: $g/type.h +inet.o: $g/udp.h +inet.o: $i/unistd.h +inet.o: $h/utils.h +inet.o: $s/svrctl.h +inet.o: inet.h +inet.o: mq.h +inet.o: proto.h +inet.o: $a + +inet_config.o: $i/unistd.h +inet_config.o: $i/fcntl.h +inet_config.o: $s/stat.h +inet_config.o: $a + +mnx_eth.o: $g/assert.h +mnx_eth.o: $g/buf.h +mnx_eth.o: $g/clock.h +mnx_eth.o: $g/eth.h +mnx_eth.o: $g/eth_int.h +mnx_eth.o: $g/sr.h +mnx_eth.o: $g/type.h +mnx_eth.o: inet.h +mnx_eth.o: osdep_eth.h +mnx_eth.o: $g/event.h +mnx_eth.o: proto.h +mnx_eth.o: $a + +mq.o: $g/assert.h +mq.o: inet.h +mq.o: mq.h +mq.o: $a + +sr.o: $g/assert.h +sr.o: $g/buf.h +sr.o: $g/sr.h +sr.o: $g/type.h +sr.o: $h/callnr.h +sr.o: inet.h +sr.o: mq.h +sr.o: proto.h +sr.o: $a + +stacktrace.o: inet.h +stacktrace.o: $a + +$g/arp.o: $g/arp.h +$g/arp.o: $g/assert.h +$g/arp.o: $g/buf.h +$g/arp.o: $g/clock.h +$g/arp.o: $g/eth.h +$g/arp.o: inet.h +$g/arp.o: $g/io.h +$g/arp.o: $g/sr.h +$g/arp.o: $g/type.h +$g/arp.o: $a +$g/arp.o: $g/arp.c + cd generic && $(CC) -c $(CFLAGS) arp.c + +$g/eth.o: $g/assert.h +$g/eth.o: $g/buf.h +$g/eth.o: $g/buf.h +$g/eth.o: $g/clock.h +$g/eth.o: $g/eth.h +$g/eth.o: $g/eth_int.h +$g/eth.o: $g/event.h +$g/eth.o: inet.h +$g/eth.o: $g/io.h +$g/eth.o: osdep_eth.h +$g/eth.o: $g/event.h +$g/eth.o: $g/sr.h +$g/eth.o: $g/type.h +$g/eth.o: $a +$g/eth.o: $g/eth.c + cd generic && $(CC) -c $(CFLAGS) eth.c + +$g/event.o: $g/assert.h +$g/event.o: $g/event.h +$g/event.o: inet.h +$g/event.o: $a +$g/event.o: $g/event.c + cd generic && $(CC) -c $(CFLAGS) event.c + +$g/icmp.o: $g/assert.h +$g/icmp.o: $g/buf.h +$g/icmp.o: $g/event.h +$g/icmp.o: $g/icmp.h +$g/icmp.o: $g/icmp_lib.h +$g/icmp.o: inet.h +$g/icmp.o: $g/io.h +$g/icmp.o: $g/ip.h +$g/icmp.o: $g/ip_int.h +$g/icmp.o: $g/ipr.h +$g/icmp.o: $g/type.h +$g/icmp.o: $a +$g/icmp.o: $g/icmp.c + cd generic && $(CC) -c $(CFLAGS) icmp.c + +$g/io.o: $i/stdlib.h +$g/io.o: inet.h +$g/io.o: $g/io.h +$g/io.o: $a +$g/io.o: $g/io.c + cd generic && $(CC) -c $(CFLAGS) io.c + +$g/ip.o: $g/arp.h +$g/ip.o: $g/assert.h +$g/ip.o: $g/buf.h +$g/ip.o: $g/clock.h +$g/ip.o: $g/eth.h +$g/ip.o: $g/event.h +$g/ip.o: $g/icmp.h +$g/ip.o: $g/icmp_lib.h +$g/ip.o: inet.h +$g/ip.o: $g/io.h +$g/ip.o: $g/ip.h +$g/ip.o: $g/ip_int.h +$g/ip.o: $g/ipr.h +$g/ip.o: $g/sr.h +$g/ip.o: $g/type.h +$g/ip.o: $a +$g/ip.o: $g/ip.c + cd generic && $(CC) -c $(CFLAGS) ip.c + +$g/ip_eth.o: $g/arp.h +$g/ip_eth.o: $g/assert.h +$g/ip_eth.o: $g/buf.h +$g/ip_eth.o: $g/clock.h +$g/ip_eth.o: $g/eth.h +$g/ip_eth.o: $g/event.h +$g/ip_eth.o: inet.h +$g/ip_eth.o: $g/ip.h +$g/ip_eth.o: $g/ip_int.h +$g/ip_eth.o: $g/type.h +$g/ip_eth.o: $a +$g/ip_eth.o: $g/ip_eth.c + cd generic && $(CC) -c $(CFLAGS) ip_eth.c + +$g/ip_ioctl.o: $g/arp.h +$g/ip_ioctl.o: $g/assert.h +$g/ip_ioctl.o: $g/buf.h +$g/ip_ioctl.o: $g/clock.h +$g/ip_ioctl.o: $g/event.h +$g/ip_ioctl.o: $g/icmp_lib.h +$g/ip_ioctl.o: inet.h +$g/ip_ioctl.o: $g/ip.h +$g/ip_ioctl.o: $g/ip_int.h +$g/ip_ioctl.o: $g/ipr.h +$g/ip_ioctl.o: $g/type.h +$g/ip_ioctl.o: $a +$g/ip_ioctl.o: $g/ip_ioctl.c + cd generic && $(CC) -c $(CFLAGS) ip_ioctl.c + +$g/ip_lib.o: $g/assert.h +$g/ip_lib.o: $g/buf.h +$g/ip_lib.o: $g/event.h +$g/ip_lib.o: inet.h +$g/ip_lib.o: $g/io.h +$g/ip_lib.o: $g/ip_int.h +$g/ip_lib.o: $g/type.h +$g/ip_lib.o: $a +$g/ip_lib.o: $g/ip_lib.c + cd generic && $(CC) -c $(CFLAGS) ip_lib.c + +$g/ip_ps.o: $g/assert.h +$g/ip_ps.o: $g/buf.h +$g/ip_ps.o: $g/event.h +$g/ip_ps.o: inet.h +$g/ip_ps.o: $g/ip.h +$g/ip_ps.o: $g/ip_int.h +$g/ip_ps.o: $g/psip.h +$g/ip_ps.o: $g/type.h +$g/ip_ps.o: $a +$g/ip_ps.o: $g/ip_ps.c + cd generic && $(CC) -c $(CFLAGS) ip_ps.c + +$g/ip_read.o: $g/assert.h +$g/ip_read.o: $g/buf.h +$g/ip_read.o: $g/clock.h +$g/ip_read.o: $g/event.h +$g/ip_read.o: $g/icmp_lib.h +$g/ip_read.o: inet.h +$g/ip_read.o: $g/io.h +$g/ip_read.o: $g/ip.h +$g/ip_read.o: $g/ip_int.h +$g/ip_read.o: $g/ipr.h +$g/ip_read.o: $g/type.h +$g/ip_read.o: $a +$g/ip_read.o: $g/ip_read.c + cd generic && $(CC) -c $(CFLAGS) ip_read.c + +$g/ip_write.o: $g/arp.h +$g/ip_write.o: $g/assert.h +$g/ip_write.o: $g/buf.h +$g/ip_write.o: $g/clock.h +$g/ip_write.o: $g/eth.h +$g/ip_write.o: $g/event.h +$g/ip_write.o: $g/icmp_lib.h +$g/ip_write.o: inet.h +$g/ip_write.o: $g/io.h +$g/ip_write.o: $g/ip.h +$g/ip_write.o: $g/ip_int.h +$g/ip_write.o: $g/ipr.h +$g/ip_write.o: $g/type.h +$g/ip_write.o: $a +$g/ip_write.o: $g/ip_write.c + cd generic && $(CC) -c $(CFLAGS) ip_write.c + +$g/ipr.o: $g/assert.h +$g/ipr.o: $g/buf.h +$g/ipr.o: $g/clock.h +$g/ipr.o: $g/event.h +$g/ipr.o: inet.h +$g/ipr.o: $g/io.h +$g/ipr.o: $g/ip_int.h +$g/ipr.o: $g/ipr.h +$g/ipr.o: $g/type.h +$g/ipr.o: $a +$g/ipr.o: $g/ipr.c + cd generic && $(CC) -c $(CFLAGS) ipr.c + +$g/psip.o: $g/assert.h +$g/psip.o: $g/buf.h +$g/psip.o: $g/event.h +$g/psip.o: inet.h +$g/psip.o: $g/ip_int.h +$g/psip.o: $g/psip.h +$g/psip.o: $g/sr.h +$g/psip.o: $g/type.h +$g/psip.o: $a +$g/psip.o: $g/psip.c + cd generic && $(CC) -c $(CFLAGS) psip.c + +$g/tcp.o: $g/assert.h +$g/tcp.o: $g/buf.h +$g/tcp.o: $g/clock.h +$g/tcp.o: $g/event.h +$g/tcp.o: inet.h +$g/tcp.o: $g/io.h +$g/tcp.o: $g/ip.h +$g/tcp.o: $g/sr.h +$g/tcp.o: $g/tcp.h +$g/tcp.o: $g/tcp_int.h +$g/tcp.o: $g/type.h +$g/tcp.o: $a +$g/tcp.o: $g/tcp.c + cd generic && $(CC) -c $(CFLAGS) tcp.c + +$g/tcp_lib.o: $g/assert.h +$g/tcp_lib.o: $g/buf.h +$g/tcp_lib.o: $g/clock.h +$g/tcp_lib.o: $g/event.h +$g/tcp_lib.o: inet.h +$g/tcp_lib.o: $g/io.h +$g/tcp_lib.o: $g/tcp_int.h +$g/tcp_lib.o: $g/type.h +$g/tcp_lib.o: $a +$g/tcp_lib.o: $g/tcp_lib.c + cd generic && $(CC) -c $(CFLAGS) tcp_lib.c + +$g/tcp_recv.o: $g/assert.h +$g/tcp_recv.o: $g/buf.h +$g/tcp_recv.o: $g/clock.h +$g/tcp_recv.o: $g/event.h +$g/tcp_recv.o: inet.h +$g/tcp_recv.o: $g/io.h +$g/tcp_recv.o: $g/tcp.h +$g/tcp_recv.o: $g/tcp_int.h +$g/tcp_recv.o: $g/type.h +$g/tcp_recv.o: $a +$g/tcp_recv.o: $g/tcp_recv.c + cd generic && $(CC) -c $(CFLAGS) tcp_recv.c + +$g/tcp_send.o: $g/assert.h +$g/tcp_send.o: $g/buf.h +$g/tcp_send.o: $g/clock.h +$g/tcp_send.o: $g/event.h +$g/tcp_send.o: inet.h +$g/tcp_send.o: $g/io.h +$g/tcp_send.o: $g/ip.h +$g/tcp_send.o: $g/tcp.h +$g/tcp_send.o: $g/tcp_int.h +$g/tcp_send.o: $g/type.h +$g/tcp_send.o: $a +$g/tcp_send.o: $g/tcp_send.c + cd generic && $(CC) -c $(CFLAGS) tcp_send.c + +$g/udp.o: $g/assert.h +$g/udp.o: $g/buf.h +$g/udp.o: $g/clock.h +$g/udp.o: $g/icmp_lib.h +$g/udp.o: inet.h +$g/udp.o: $g/io.h +$g/udp.o: $g/ip.h +$g/udp.o: $g/sr.h +$g/udp.o: $g/type.h +$g/udp.o: $g/udp.h +$g/udp.o: $a +$g/udp.o: $g/udp.c + cd generic && $(CC) -c $(CFLAGS) udp.c diff --git a/servers/inet/buf.c b/servers/inet/buf.c new file mode 100644 index 000000000..6d9ec94e4 --- /dev/null +++ b/servers/inet/buf.c @@ -0,0 +1,1205 @@ +/* +This file contains routines for buffer management. + +Copyright 1995 Philip Homburg +*/ + +#define BUF_IMPLEMENTATION 1 /* Avoid some macros */ + +#include "inet.h" + +#include <stdlib.h> +#include <string.h> + +#include "generic/assert.h" +#include "generic/buf.h" +#include "generic/type.h" + +THIS_FILE + +#ifndef BUF_USEMALLOC +#define BUF_USEMALLOC 0 +#endif + +#ifndef BUF512_NR +#if CRAMPED +#define BUF512_NR 32 +#else +#define BUF512_NR 128 +#endif +#endif +#ifndef BUF2K_NR +#define BUF2K_NR 0 +#endif +#ifndef BUF32K_NR +#define BUF32K_NR 0 +#endif + +#define ACC_NR ((BUF512_NR+BUF2K_NR+BUF32K_NR)*3/2) +#define CLIENT_NR 6 + +#define DECLARE_TYPE(Tag, Type, Size) \ + typedef struct Tag \ + { \ + buf_t buf_header; \ + char buf_data[Size]; \ + } Type + +#if BUF_USEMALLOC +#define DECLARE_STORAGE(Type, Ident, Nitems) \ + PRIVATE Type *Ident + +#define ALLOC_STORAGE(Ident, Nitems, Label) \ + do \ + { \ + printf("buf.c: malloc %d %s\n", Nitems, Label); \ + Ident= malloc(sizeof(*Ident) * Nitems); \ + if (!Ident) \ + ip_panic(( "unable to alloc %s", Label )); \ + } while(0) +#else +#define DECLARE_STORAGE(Type, Ident, Nitems) \ + PRIVATE Type Ident[Nitems] + +#define ALLOC_STORAGE(Ident, Nitems, Label) \ + (void)0 +#endif + +#if BUF512_NR +DECLARE_TYPE(buf512, buf512_t, 512); +PRIVATE acc_t *buf512_freelist; +DECLARE_STORAGE(buf512_t, buffers512, BUF512_NR); +FORWARD void bf_512free ARGS(( acc_t *acc )); +#endif +#if BUF2K_NR +DECLARE_TYPE(buf2K, buf2K_t, (2*1024)); +PRIVATE acc_t *buf2K_freelist; +DECLARE_STORAGE(buf2K_t, buffers2K, BUF2K_NR); +FORWARD void bf_2Kfree ARGS(( acc_t *acc )); +#endif +#if BUF32K_NR +DECLARE_TYPE(buf32K, buf32K_t, (32*1024)); +PRIVATE acc_t *buf32K_freelist; +DECLARE_STORAGE(buf32K_t, buffers32K, BUF32K_NR); +FORWARD void bf_32Kfree ARGS(( acc_t *acc )); +#endif + +PRIVATE acc_t *acc_freelist; +DECLARE_STORAGE(acc_t, accessors, ACC_NR); + +PRIVATE bf_freereq_t freereq[CLIENT_NR]; +PRIVATE size_t bf_buf_gran; + +PUBLIC size_t bf_free_bufsize; +PUBLIC acc_t *bf_temporary_acc; + +#ifdef BUF_CONSISTENCY_CHECK +int inet_buf_debug; +unsigned buf_generation; +PRIVATE bf_checkreq_t checkreq[CLIENT_NR]; +#endif + +#ifndef BUF_TRACK_ALLOC_FREE +FORWARD acc_t *bf_small_memreq ARGS(( size_t size )); +#else +FORWARD acc_t *_bf_small_memreq ARGS(( char *clnt_file, int clnt_line, + size_t size )); +#define bf_small_memreq(a) _bf_small_memreq(clnt_file, clnt_line, a) +#endif +FORWARD void free_accs ARGS(( void )); +#ifdef BUF_CONSISTENCY_CHECK +FORWARD void count_free_bufs ARGS(( acc_t *list )); +FORWARD int report_buffer ARGS(( buf_t *buf, char *label, int i )); +#endif + +PUBLIC void bf_init() +{ + int i; + size_t size; + size_t buf_s; + acc_t *acc; + + bf_buf_gran= BUF_S; + buf_s= 0; + + for (i=0;i<CLIENT_NR;i++) + freereq[i]=0; +#ifdef BUF_CONSISTENCY_CHECK + for (i=0;i<CLIENT_NR;i++) + checkreq[i]=0; +#endif + +#if BUF512_NR + ALLOC_STORAGE(buffers512, BUF512_NR, "512B-buffers"); +#endif +#if BUF2K_NR + ALLOC_STORAGE(buffers2K, BUF2K_NR, "2K-buffers"); +#endif +#if BUF32K_NR + ALLOC_STORAGE(buffers32K, BUF32K_NR, "32K-buffers"); +#endif + ALLOC_STORAGE(accessors, ACC_NR, "accs"); + + acc_freelist= NULL; + for (i=0;i<ACC_NR;i++) + { + memset(&accessors[i], '\0', sizeof(accessors[i])); + + accessors[i].acc_linkC= 0; + accessors[i].acc_next= acc_freelist; + acc_freelist= &accessors[i]; + } + +#define INIT_BUFFERS(Ident, Nitems, Freelist, Freefunc) \ + do \ + { \ + Freelist= NULL; \ + for (i=0;i<Nitems;i++) \ + { \ + acc= acc_freelist; \ + if (!acc) \ + ip_panic(( "fewer accessors than buffers")); \ + acc_freelist= acc->acc_next; \ + acc->acc_linkC= 0; \ + \ + memset(&Ident[i], '\0', sizeof(Ident[i])); \ + Ident[i].buf_header.buf_linkC= 0; \ + Ident[i].buf_header.buf_free= Freefunc; \ + Ident[i].buf_header.buf_size= \ + sizeof(Ident[i].buf_data); \ + Ident[i].buf_header.buf_data_p= \ + Ident[i].buf_data; \ + \ + acc->acc_buffer= &Ident[i].buf_header; \ + acc->acc_next= Freelist; \ + Freelist= acc; \ + } \ + if (sizeof(Ident[0].buf_data) < bf_buf_gran) \ + bf_buf_gran= sizeof(Ident[0].buf_data); \ + if (sizeof(Ident[0].buf_data) > buf_s) \ + buf_s= sizeof(Ident[0].buf_data); \ + } while(0) + +#if BUF512_NR + INIT_BUFFERS(buffers512, BUF512_NR, buf512_freelist, bf_512free); +#endif +#if BUF2K_NR + INIT_BUFFERS(buffers2K, BUF2K_NR, buf2K_freelist, bf_2Kfree); +#endif +#if BUF32K_NR + INIT_BUFFERS(buffers32K, BUF32K_NR, buf32K_freelist, bf_32Kfree); +#endif + +#undef INIT_BUFFERS + + assert (buf_s == BUF_S); +} + +#ifndef BUF_CONSISTENCY_CHECK +PUBLIC void bf_logon(func) +bf_freereq_t func; +#else +PUBLIC void bf_logon(func, checkfunc) +bf_freereq_t func; +bf_checkreq_t checkfunc; +#endif +{ + int i; + + for (i=0;i<CLIENT_NR;i++) + if (!freereq[i]) + { + freereq[i]=func; +#ifdef BUF_CONSISTENCY_CHECK + checkreq[i]= checkfunc; +#endif + return; + } + + ip_panic(( "buf.c: to many clients" )); +} + +/* +bf_memreq +*/ + +#ifndef BUF_TRACK_ALLOC_FREE +PUBLIC acc_t *bf_memreq(size) +#else +PUBLIC acc_t *_bf_memreq(clnt_file, clnt_line, size) +char *clnt_file; +int clnt_line; +#endif +size_t size; +{ + acc_t *head, *tail, *new_acc; + buf_t *buf; + int i,j; + size_t count; + + assert (size>0); + + head= NULL; + while (size) + { + new_acc= NULL; + + /* Note the tricky dangling else... */ +#define ALLOC_BUF(Freelist, Bufsize) \ + if (Freelist && (Bufsize == BUF_S || size <= Bufsize)) \ + { \ + new_acc= Freelist; \ + Freelist= new_acc->acc_next; \ + \ + assert(new_acc->acc_linkC == 0); \ + new_acc->acc_linkC= 1; \ + buf= new_acc->acc_buffer; \ + assert(buf->buf_linkC == 0); \ + buf->buf_linkC= 1; \ + } \ + else + + /* Sort attempts by buffer size */ +#if BUF512_NR + ALLOC_BUF(buf512_freelist, 512) +#endif +#if BUF2K_NR + ALLOC_BUF(buf2K_freelist, 2*1024) +#endif +#if BUF32K_NR + ALLOC_BUF(buf32K_freelist, 32*1024) +#endif +#undef ALLOC_BUF + { + DBLOCK(1, printf("freeing buffers\n")); + + bf_free_bufsize= 0; + for (i=0; bf_free_bufsize<size && i<MAX_BUFREQ_PRI; + i++) + { + for (j=0; j<CLIENT_NR; j++) + { + if (freereq[j]) + (*freereq[j])(i); + } +#if DEBUG + { acc_t *acc; + j= 0; for(acc= buf512_freelist; acc; acc= acc->acc_next) j++; + printf("# of free 512-bytes buffer is now %d\n", j); } +#endif + } +#if DEBUG + { printf("last level was level %d\n", i-1); } +#endif + if (bf_free_bufsize<size) + ip_panic(( "not enough buffers freed" )); + + continue; + } + +#ifdef BUF_TRACK_ALLOC_FREE + new_acc->acc_alloc_file= clnt_file; + new_acc->acc_alloc_line= clnt_line; + buf->buf_alloc_file= clnt_file; + buf->buf_alloc_line= clnt_line; +#endif + + if (!head) + head= new_acc; + else + tail->acc_next= new_acc; + tail= new_acc; + + count= tail->acc_buffer->buf_size; + if (count > size) + count= size; + + tail->acc_offset= 0; + tail->acc_length= count; + size -= count; + } + tail->acc_next= 0; + +#if DEBUG + bf_chkbuf(head); +#endif + + return head; +} + +/* +bf_small_memreq +*/ + +#ifndef BUF_TRACK_ALLOC_FREE +PRIVATE acc_t *bf_small_memreq(size) +#else +PRIVATE acc_t *_bf_small_memreq(clnt_file, clnt_line, size) +char *clnt_file; +int clnt_line; +#endif +size_t size; +{ + return bf_memreq(size); +} + +#ifndef BUF_TRACK_ALLOC_FREE +PUBLIC void bf_afree(acc) +#else +PUBLIC void _bf_afree(clnt_file, clnt_line, acc) +char *clnt_file; +int clnt_line; +#endif +acc_t *acc; +{ + acc_t *next_acc; + buf_t *buf; + + while (acc) + { +#if defined(bf_afree) + DIFBLOCK(1, (acc->acc_linkC <= 0), + printf("clnt_file= %s, clnt_line= %d\n", + clnt_file, clnt_line)); +#endif + assert (acc->acc_linkC>0); + if (--acc->acc_linkC > 0) + break; + +#ifdef BUF_TRACK_ALLOC_FREE + acc->acc_free_file= clnt_file; + acc->acc_free_line= clnt_line; +#endif + buf= acc->acc_buffer; + assert (buf); + +#if defined(bf_afree) + DIFBLOCK(1, (buf->buf_linkC == 0), + printf("clnt_file= %s, clnt_line= %d\n", + clnt_file, clnt_line)); +#endif + assert (buf->buf_linkC>0); + if (--buf->buf_linkC > 0) + { + acc->acc_buffer= NULL; + next_acc= acc->acc_next; + acc->acc_next= acc_freelist; + acc_freelist= acc; +#ifdef BUF_CONSISTENCY_CHECK + if (inet_buf_debug) + { + acc->acc_offset= 0xdeadbeaf; + acc->acc_length= 0xdeadbeaf; + acc->acc_buffer= (buf_t *)0xdeadbeaf; + acc->acc_ext_link= (acc_t *)0xdeadbeaf; + } +#endif + acc= next_acc; + continue; + } + + bf_free_bufsize += buf->buf_size; +#ifdef BUF_TRACK_ALLOC_FREE + buf->buf_free_file= clnt_file; + buf->buf_free_line= clnt_line; +#endif + next_acc= acc->acc_next; + buf->buf_free(acc); + acc= next_acc; + continue; + } +} + +#ifndef BUF_TRACK_ALLOC_FREE +PUBLIC acc_t *bf_dupacc(acc_ptr) +#else +PUBLIC acc_t *_bf_dupacc(clnt_file, clnt_line, acc_ptr) +char *clnt_file; +int clnt_line; +#endif +register acc_t *acc_ptr; +{ + register acc_t *new_acc; + int i, j; + + if (!acc_freelist) + { + free_accs(); + if (!acc_freelist) + ip_panic(( "buf.c: out of accessors" )); + } + new_acc= acc_freelist; + acc_freelist= new_acc->acc_next; + + *new_acc= *acc_ptr; + if (acc_ptr->acc_next) + acc_ptr->acc_next->acc_linkC++; + if (acc_ptr->acc_buffer) + acc_ptr->acc_buffer->buf_linkC++; + new_acc->acc_linkC= 1; +#ifdef BUF_TRACK_ALLOC_FREE + new_acc->acc_alloc_file= clnt_file; + new_acc->acc_alloc_line= clnt_line; +#endif + return new_acc; +} + +PUBLIC size_t bf_bufsize(acc_ptr) +register acc_t *acc_ptr; +{ + register size_t size; + +assert(acc_ptr); + + size=0; + + while (acc_ptr) + { +assert(acc_ptr >= accessors && acc_ptr <= &accessors[ACC_NR-1]); + size += acc_ptr->acc_length; + acc_ptr= acc_ptr->acc_next; + } + return size; +} + +#ifndef BUF_TRACK_ALLOC_FREE +PUBLIC acc_t *bf_packIffLess(pack, min_len) +#else +PUBLIC acc_t *_bf_packIffLess(clnt_file, clnt_line, pack, min_len) +char *clnt_file; +int clnt_line; +#endif +acc_t *pack; +int min_len; +{ + if (!pack || pack->acc_length >= min_len) + return pack; + +#if DEBUG +#ifdef bf_packIffLess + { where(); printf("calling bf_pack because of %s %d: %d\n", bf_pack_file, + bf_pack_line, min_len); } +#endif +#endif + return bf_pack(pack); +} + +#ifndef BUF_TRACK_ALLOC_FREE +PUBLIC acc_t *bf_pack(old_acc) +#else +PUBLIC acc_t *_bf_pack(clnt_file, clnt_line, old_acc) +char *clnt_file; +int clnt_line; +#endif +acc_t *old_acc; +{ + acc_t *new_acc, *acc_ptr_old, *acc_ptr_new; + size_t size, offset_old, offset_new, block_size, block_size_old; + + /* Check if old acc is good enough. */ + if (!old_acc || !old_acc->acc_next && old_acc->acc_linkC == 1 && + old_acc->acc_buffer->buf_linkC == 1) + { + return old_acc; + } + + size= bf_bufsize(old_acc); + assert(size > 0); + new_acc= bf_memreq(size); + acc_ptr_old= old_acc; + acc_ptr_new= new_acc; + offset_old= 0; + offset_new= 0; + while (size) + { + assert (acc_ptr_old); + if (offset_old == acc_ptr_old->acc_length) + { + offset_old= 0; + acc_ptr_old= acc_ptr_old->acc_next; + continue; + } + assert (offset_old < acc_ptr_old->acc_length); + block_size_old= acc_ptr_old->acc_length - offset_old; + assert (acc_ptr_new); + if (offset_new == acc_ptr_new->acc_length) + { + offset_new= 0; + acc_ptr_new= acc_ptr_new->acc_next; + continue; + } + assert (offset_new < acc_ptr_new->acc_length); + block_size= acc_ptr_new->acc_length - offset_new; + if (block_size > block_size_old) + block_size= block_size_old; + memcpy(ptr2acc_data(acc_ptr_new)+offset_new, + ptr2acc_data(acc_ptr_old)+offset_old, block_size); + offset_new += block_size; + offset_old += block_size; + size -= block_size; + } + bf_afree(old_acc); + return new_acc; +} + +#ifndef BUF_TRACK_ALLOC_FREE +PUBLIC acc_t *bf_cut (data, offset, length) +#else +PUBLIC acc_t *_bf_cut (clnt_file, clnt_line, data, offset, length) +char *clnt_file; +int clnt_line; +#endif +register acc_t *data; +register unsigned offset; +register unsigned length; +{ + register acc_t *head, *tail; + + if (!data && !offset && !length) + return 0; +#ifdef BUF_TRACK_ALLOC_FREE + assert(data || + (printf("from %s, %d: %u, %u\n", + clnt_file, clnt_line, offset, length), 0)); +#else + assert(data); +#endif + + assert(data); +#if DEBUG + bf_chkbuf(data); +#endif + + if (!length) + { + head= bf_dupacc(data); + bf_afree(head->acc_next); + head->acc_next= 0; + head->acc_length= 0; +#if DEBUG + bf_chkbuf(data); +#endif + return head; + } + while (data && offset>=data->acc_length) + { + offset -= data->acc_length; + data= data->acc_next; + } + + assert (data); + + head= bf_dupacc(data); + bf_afree(head->acc_next); + head->acc_next= 0; + head->acc_offset += offset; + head->acc_length -= offset; + if (length >= head->acc_length) + length -= head->acc_length; + else + { + head->acc_length= length; + length= 0; + } + tail= head; + data= data->acc_next; + while (data && length && length>=data->acc_length) + { + tail->acc_next= bf_dupacc(data); + tail= tail->acc_next; + bf_afree(tail->acc_next); + tail->acc_next= 0; + data= data->acc_next; + length -= tail->acc_length; + } + if (length) + { +#ifdef bf_cut + assert (data || + (printf("bf_cut called from %s:%d\n", + clnt_file, clnt_line), 0)); +#else + assert (data); +#endif + tail->acc_next= bf_dupacc(data); + tail= tail->acc_next; + bf_afree(tail->acc_next); + tail->acc_next= 0; + tail->acc_length= length; + } +#if DEBUG + bf_chkbuf(data); +#endif + return head; +} + +#ifndef BUF_TRACK_ALLOC_FREE +PUBLIC acc_t *bf_delhead (data, offset) +#else +PUBLIC acc_t *_bf_delhead (clnt_file, clnt_line, data, offset) +char *clnt_file; +int clnt_line; +#endif +register acc_t *data; +register unsigned offset; +{ + acc_t *new_acc; + + assert(data); + + /* Find the acc we need to modify. */ + new_acc= data; + while(offset >= new_acc->acc_length) + { + offset -= new_acc->acc_length; + new_acc= new_acc->acc_next; +#ifdef BUF_TRACK_ALLOC_FREE + assert(new_acc || (printf("called from %s, %d\n", + clnt_file, clnt_line),0)); +#else + assert(new_acc); +#endif + } + + /* Discard the old acc(s) */ + if (new_acc != data) + { + new_acc->acc_linkC++; + bf_afree(data); + data= new_acc; + } + + /* Make sure that acc_linkC == 1 */ + if (data->acc_linkC != 1) + { + new_acc= bf_dupacc(data); + bf_afree(data); + data= new_acc; + } + + /* Delete the last bit by modifying acc_offset and acc_length */ + data->acc_offset += offset; + data->acc_length -= offset; + return data; +} + +/* +bf_append +*/ + +#ifndef BUF_TRACK_ALLOC_FREE +PUBLIC acc_t *bf_append(data_first, data_second) +#else +PUBLIC acc_t *_bf_append(clnt_file, clnt_line, data_first, data_second) +char *clnt_file; +int clnt_line; +#endif +acc_t *data_first; +acc_t *data_second; +{ + acc_t *head, *tail, *new_acc, *acc_ptr_new, tmp_acc, *curr; + char *src_ptr, *dst_ptr; + size_t size, offset_old, offset_new, block_size_old, block_size; + + if (!data_first) + return data_second; + if (!data_second) + return data_first; + + head= 0; + while (data_first) + { + if (data_first->acc_linkC == 1) + curr= data_first; + else + { + curr= bf_dupacc(data_first); + assert (curr->acc_linkC == 1); + bf_afree(data_first); + } + data_first= curr->acc_next; + if (!curr->acc_length) + { + curr->acc_next= 0; + bf_afree(curr); + continue; + } + if (!head) + head= curr; + else + tail->acc_next= curr; + tail= curr; + } + if (!head) + return data_second; + tail->acc_next= 0; + + while (data_second && !data_second->acc_length) + { + curr= data_second; + data_second= data_second->acc_next; + if (data_second) + data_second->acc_linkC++; + bf_afree(curr); + } + if (!data_second) + return head; + + if (tail->acc_length + data_second->acc_length > + tail->acc_buffer->buf_size) + { + tail->acc_next= data_second; + return head; + } + + if (tail->acc_buffer->buf_size == bf_buf_gran && + tail->acc_buffer->buf_linkC == 1) + { + if (tail->acc_offset) + { + memmove(tail->acc_buffer->buf_data_p, + ptr2acc_data(tail), tail->acc_length); + tail->acc_offset= 0; + } + dst_ptr= ptr2acc_data(tail) + tail->acc_length; + src_ptr= ptr2acc_data(data_second); + memcpy(dst_ptr, src_ptr, data_second->acc_length); + tail->acc_length += data_second->acc_length; + tail->acc_next= data_second->acc_next; + if (data_second->acc_next) + data_second->acc_next->acc_linkC++; + bf_afree(data_second); + return head; + } + + new_acc= bf_small_memreq(tail->acc_length+data_second->acc_length); + acc_ptr_new= new_acc; + offset_old= 0; + offset_new= 0; + size= tail->acc_length; + while (size) + { +assert (acc_ptr_new); + if (offset_new == acc_ptr_new->acc_length) + { + offset_new= 0; + acc_ptr_new= acc_ptr_new->acc_next; + continue; + } +assert (offset_new < acc_ptr_new->acc_length); +assert (offset_old < tail->acc_length); + block_size_old= tail->acc_length - offset_old; + block_size= acc_ptr_new->acc_length - offset_new; + if (block_size > block_size_old) + block_size= block_size_old; + memcpy(ptr2acc_data(acc_ptr_new)+offset_new, + ptr2acc_data(tail)+offset_old, block_size); + offset_new += block_size; + offset_old += block_size; + size -= block_size; + } + offset_old= 0; + size= data_second->acc_length; + while (size) + { +assert (acc_ptr_new); + if (offset_new == acc_ptr_new->acc_length) + { + offset_new= 0; + acc_ptr_new= acc_ptr_new->acc_next; + continue; + } +assert (offset_new < acc_ptr_new->acc_length); +assert (offset_old < data_second->acc_length); + block_size_old= data_second->acc_length - offset_old; + block_size= acc_ptr_new->acc_length - offset_new; + if (block_size > block_size_old) + block_size= block_size_old; + memcpy(ptr2acc_data(acc_ptr_new)+offset_new, + ptr2acc_data(data_second)+offset_old, block_size); + offset_new += block_size; + offset_old += block_size; + size -= block_size; + } + tmp_acc= *tail; + *tail= *new_acc; + *new_acc= tmp_acc; + + bf_afree(new_acc); + while (tail->acc_next) + tail= tail->acc_next; + + tail->acc_next= data_second->acc_next; + if (data_second->acc_next) + data_second->acc_next->acc_linkC++; + bf_afree(data_second); + return head; +} + +#if BUF512_NR +PRIVATE void bf_512free(acc) +acc_t *acc; +{ +#ifdef BUF_CONSISTENCY_CHECK + if (inet_buf_debug) + memset(acc->acc_buffer->buf_data_p, 0xa5, 512); +#endif + acc->acc_next= buf512_freelist; + buf512_freelist= acc; +} +#endif +#if BUF2K_NR +PRIVATE void bf_2Kfree(acc) +acc_t *acc; +{ +#ifdef BUF_CONSISTENCY_CHECK + if (inet_buf_debug) + memset(acc->acc_buffer->buf_data_p, 0xa5, 2*1024); +#endif + acc->acc_next= buf2K_freelist; + buf2K_freelist= acc; +} +#endif +#if BUF32K_NR +PRIVATE void bf_32Kfree(acc) +acc_t *acc; +{ +#ifdef BUF_CONSISTENCY_CHECK + if (inet_buf_debug) + memset(acc->acc_buffer->buf_data_p, 0xa5, 32*1024); +#endif + acc->acc_next= buf32K_freelist; + buf32K_freelist= acc; +} +#endif + +#ifdef BUF_CONSISTENCY_CHECK +PUBLIC int bf_consistency_check() +{ + acc_t *acc; + buf_t *buf; + int silent; + int error; + int i; + + buf_generation++; + + for (i=0; i<CLIENT_NR; i++) + { + if (checkreq[i]) + (*checkreq[i])(); + } + + /* Add information about free accessors */ + for(acc= acc_freelist; acc; acc= acc->acc_next) + { + if (acc->acc_generation == buf_generation-1) + { + acc->acc_generation= buf_generation; + acc->acc_check_linkC= 0; + } + else + { + assert(acc->acc_generation == buf_generation && + acc->acc_check_linkC > 0); + acc->acc_check_linkC= -acc->acc_check_linkC; + } + } + +#if BUF512_NR + count_free_bufs(buf512_freelist); +#endif +#if BUF2K_NR + count_free_bufs(buf2K_freelist); +#endif +#if BUF32K_NR + count_free_bufs(buf32K_freelist); +#endif + + error= 0; + + /* Report about accessors */ + silent= 0; + for (i=0, acc= accessors; i<ACC_NR; i++, acc++) + { + if (acc->acc_generation != buf_generation) + { + error++; + assert(acc->acc_generation == buf_generation-1); + acc->acc_generation= buf_generation; + if (!silent) + { + printf( +"acc[%d] (0x%x) has been lost with count %d, last allocated at %s, %d\n", + i, acc, acc->acc_linkC, acc->acc_alloc_file, acc->acc_alloc_line); +#if 0 + silent= 1; +#endif + } + continue; + } + if (acc->acc_check_linkC == acc->acc_linkC) + continue; + error++; + if (acc->acc_check_linkC < 0) + { + if (!silent) + { + printf( +"acc[%d] is freed but still in use, allocated at %s, %d, freed at %s, %d\n", + i, acc->acc_alloc_file, acc->acc_alloc_line, + acc->acc_free_file, acc->acc_free_line); + } + acc->acc_check_linkC= -acc->acc_check_linkC; + if (acc->acc_check_linkC == acc->acc_linkC) + { + silent= 1; + continue; + } + } + if (!silent) + { + printf( +"# of tracked links (%d) for acc[%d] don't match with stored link count %d\n", + acc->acc_check_linkC, i, acc->acc_linkC); + printf("acc[%d] was allocated at %s, %d\n", + i, acc->acc_alloc_file, acc->acc_alloc_line); + silent=1; + } + } + + /* Report about buffers */ +#if BUF512_NR + { + for (i= 0; i<BUF512_NR; i++) + { + error |= report_buffer(&buffers512[i].buf_header, + "512-buffer", i); + } + } +#endif +#if BUF2K_NR + { + for (i= 0; i<BUF2K_NR; i++) + { + error |= report_buffer(&buffers2K[i].buf_header, + "2K-buffer", i); + } + } +#endif +#if BUF32K_NR + { + for (i= 0; i<BUF32K_NR; i++) + { + error |= report_buffer(&buffers32K[i].buf_header, + "32K-buffer", i); + } + } +#endif + + return !error; +} + +PRIVATE void count_free_bufs(list) +acc_t *list; +{ + acc_t *acc; + buf_t *buf; + + for(acc= list; acc; acc= acc->acc_next) + { + if (acc->acc_generation != buf_generation-1) + { + assert(acc->acc_generation == buf_generation && + acc->acc_check_linkC > 0); + acc->acc_check_linkC= -acc->acc_check_linkC; + continue; + } + acc->acc_generation= buf_generation; + acc->acc_check_linkC= 0; + + buf= acc->acc_buffer; + if (buf->buf_generation == buf_generation-1) + { + buf->buf_generation= buf_generation; + buf->buf_check_linkC= 0; + continue; + } + assert(buf->buf_generation == buf_generation && + buf->buf_check_linkC > 0); + buf->buf_check_linkC= -buf->buf_check_linkC; + } +} + +PRIVATE int report_buffer(buf, label, i) +buf_t *buf; +char *label; +int i; +{ + if (buf->buf_generation != buf_generation) + { + assert(buf->buf_generation == buf_generation-1); + buf->buf_generation= buf_generation; + printf( +"%s[%d] (0x%x) has been lost with count %d, last allocated at %s, %d\n", + label, i, buf, + buf->buf_linkC, buf->buf_alloc_file, + buf->buf_alloc_line); + return 1; + } + if (buf->buf_check_linkC == buf->buf_linkC) + return 0; + if (buf->buf_check_linkC < 0) + { + printf( +"%s[%d] is freed but still in use, allocated at %s, %d, freed at %s, %d\n", + label, i, buf->buf_alloc_file, buf->buf_alloc_line, + buf->buf_free_file, buf->buf_free_line); + buf->buf_check_linkC= -buf->buf_check_linkC; + if (buf->buf_check_linkC == buf->buf_linkC) + return 1; + } + printf( +"# of tracked links (%d) for %s[%d] don't match with stored link count %d\n", + buf->buf_check_linkC, label, i, buf->buf_linkC); + printf("%s[%d] was allocated at %s, %d\n", + label, i, buf->buf_alloc_file, buf->buf_alloc_line); + return 1; +} + +PUBLIC void bf_check_acc(acc) +acc_t *acc; +{ + buf_t *buf; + + while(acc != NULL) + { + if (acc->acc_generation == buf_generation) + { + assert(acc->acc_check_linkC > 0); + acc->acc_check_linkC++; + return; + } + assert(acc->acc_generation == buf_generation-1); + acc->acc_generation= buf_generation; + acc->acc_check_linkC= 1; + + buf= acc->acc_buffer; + if (buf->buf_generation == buf_generation) + { + assert(buf->buf_check_linkC > 0); + buf->buf_check_linkC++; + } + else + { + assert(buf->buf_generation == buf_generation-1); + buf->buf_generation= buf_generation; + buf->buf_check_linkC= 1; + } + + acc= acc->acc_next; + } +} + +PUBLIC void _bf_mark_acc(clnt_file, clnt_line, acc) +char *clnt_file; +int clnt_line; +acc_t *acc; +{ + buf_t *buf; + + for (; acc; acc= acc->acc_next) + { + acc->acc_alloc_file= clnt_file; + acc->acc_alloc_line= clnt_line; + buf= acc->acc_buffer; + buf->buf_alloc_file= clnt_file; + buf->buf_alloc_line= clnt_line; + } +} +#endif + +PRIVATE void free_accs() +{ + int i, j; + + DBLOCK(1, printf("free_accs\n")); + + for (i=0; !acc_freelist && i<MAX_BUFREQ_PRI; i++) + { + for (j=0; j<CLIENT_NR; j++) + { + bf_free_bufsize= 0; + if (freereq[j]) + { + (*freereq[j])(i); + } + } + } +#if DEBUG + printf("last level was level %d\n", i-1); +#endif +} + +#ifndef BUF_TRACK_ALLOC_FREE +PUBLIC acc_t *bf_align(acc, size, alignment) +#else +PUBLIC acc_t *_bf_align(clnt_file, clnt_line, acc, size, alignment) +char *clnt_file; +int clnt_line; +#endif +acc_t *acc; +size_t size; +size_t alignment; +{ + char *ptr; + size_t buf_size; + acc_t *head, *tail; + + /* Fast check if the buffer is aligned already. */ + if (acc->acc_length >= size) + { + ptr= ptr2acc_data(acc); + if (((unsigned)ptr & (alignment-1)) == 0) + return acc; + } + buf_size= bf_bufsize(acc); +#ifdef bf_align + assert(size != 0 && buf_size != 0 || + (printf("bf_align(..., %d, %d) from %s, %d\n", + size, alignment, clnt_file, clnt_line),0)); +#else + assert(size != 0 && buf_size != 0); +#endif + if (buf_size <= size) + { + acc= bf_pack(acc); + return acc; + } + head= bf_cut(acc, 0, size); + tail= bf_cut(acc, size, buf_size-size); + bf_afree(acc); + head= bf_pack(head); + assert(head->acc_next == NULL); + head->acc_next= tail; + return head; +} + +#if 0 +int chk_acc(acc) +acc_t *acc; +{ + int acc_nr; + + if (!acc) + return 1; + if (acc < accessors || acc >= &accessors[ACC_NR]) + return 0; + acc_nr= acc-accessors; + return acc == &accessors[acc_nr]; +} +#endif + +/* + * $PchId: buf.c,v 1.10 1995/11/23 11:25:25 philip Exp $ + */ diff --git a/servers/inet/clock.c b/servers/inet/clock.c new file mode 100644 index 000000000..19777456c --- /dev/null +++ b/servers/inet/clock.c @@ -0,0 +1,188 @@ +/* +clock.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "proto.h" +#include "generic/assert.h" +#include "generic/buf.h" +#include "generic/clock.h" +#include "generic/type.h" +#include <minix/syslib.h> + +THIS_FILE + +PUBLIC int clck_call_expire; + +PRIVATE time_t curr_time; +PRIVATE timer_t *timer_chain; +PRIVATE time_t next_timeout; + +FORWARD _PROTOTYPE( void clck_fast_release, (timer_t *timer) ); +FORWARD _PROTOTYPE( void set_timer, (void) ); + +PUBLIC void clck_init() +{ +#if ZERO + clck_call_expire= 0; + curr_time= 0; + next_timeout= 0; + timer_chain= 0; +#endif +} + +PUBLIC time_t get_time() +{ + if (!curr_time) + { + int s; + if ((s=sys_getuptime(&curr_time)) != OK) + ip_panic(("can't read clock")); + } + return curr_time; +} + +PUBLIC void set_time (tim) +time_t tim; +{ + if (!curr_time) + { + /* Some code assumes that no time elapses while it is + * running. + */ + curr_time= tim; + } +} + +PUBLIC void reset_time() +{ + curr_time= 0; +} + +PUBLIC void clck_timer(timer, timeout, func, fd) +timer_t *timer; +time_t timeout; +timer_func_t func; +int fd; +{ + timer_t *timer_index; + + if (timer->tim_active) + clck_fast_release(timer); + assert(!timer->tim_active); + + timer->tim_next= 0; + timer->tim_func= func; + timer->tim_ref= fd; + timer->tim_time= timeout; + timer->tim_active= 1; + + if (!timer_chain) + timer_chain= timer; + else if (timeout < timer_chain->tim_time) + { + timer->tim_next= timer_chain; + timer_chain= timer; + } + else + { + timer_index= timer_chain; + while (timer_index->tim_next && + timer_index->tim_next->tim_time < timeout) + timer_index= timer_index->tim_next; + timer->tim_next= timer_index->tim_next; + timer_index->tim_next= timer; + } + if (next_timeout == 0 || timer_chain->tim_time < next_timeout) + set_timer(); +} + +PUBLIC void clck_tick (mess) +message *mess; +{ + next_timeout= 0; + set_timer(); +} + +PRIVATE void clck_fast_release (timer) +timer_t *timer; +{ + timer_t *timer_index; + + if (!timer->tim_active) + return; + + if (timer == timer_chain) + timer_chain= timer_chain->tim_next; + else + { + timer_index= timer_chain; + while (timer_index && timer_index->tim_next != timer) + timer_index= timer_index->tim_next; + assert(timer_index); + timer_index->tim_next= timer->tim_next; + } + timer->tim_active= 0; +} + +PRIVATE void set_timer() +{ + time_t new_time; + time_t curr_time; + + if (!timer_chain) + return; + + curr_time= get_time(); + new_time= timer_chain->tim_time; + if (new_time <= curr_time) + { + clck_call_expire= 1; + return; + } + + if (next_timeout == 0 || new_time < next_timeout) + { + + next_timeout= new_time; + new_time -= curr_time; + + if (sys_syncalrm(SELF, new_time, 0) != OK) + ip_panic(("can't set timer")); + } +} + +PUBLIC void clck_untimer (timer) +timer_t *timer; +{ + clck_fast_release (timer); + set_timer(); +} + +PUBLIC void clck_expire_timers() +{ + time_t curr_time; + timer_t *timer_index; + + clck_call_expire= 0; + + if (timer_chain == NULL) + return; + + curr_time= get_time(); + while (timer_chain && timer_chain->tim_time<=curr_time) + { + assert(timer_chain->tim_active); + timer_chain->tim_active= 0; + timer_index= timer_chain; + timer_chain= timer_chain->tim_next; + (*timer_index->tim_func)(timer_index->tim_ref, timer_index); + } + set_timer(); +} + +/* + * $PchId: clock.c,v 1.6 1995/11/21 06:54:39 philip Exp $ + */ diff --git a/servers/inet/const.h b/servers/inet/const.h new file mode 100644 index 000000000..a455af7fe --- /dev/null +++ b/servers/inet/const.h @@ -0,0 +1,36 @@ +/* +inet/const.h + +Created: Dec 30, 1991 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +#ifndef INET__CONST_H +#define INET__CONST_H + +#ifndef DEBUG +#define DEBUG 0 +#endif + +#ifndef NDEBUG +#define NDEBUG (CRAMPED) +#endif + +#define CLOCK_GRAN 1 /* in HZ */ + +#if DEBUG +#define where() printf("%s, %d: ", __FILE__, __LINE__) +#endif + +#define NW_SUSPEND SUSPEND +#define NW_WOULDBLOCK EWOULDBLOCK +#define NW_OK OK + +#define BUF_S 512 + +#endif /* INET__CONST_H */ + +/* + * $PchId: const.h,v 1.6 1995/11/21 06:54:39 philip Exp $ + */ diff --git a/servers/inet/generic/arp.c b/servers/inet/generic/arp.c new file mode 100644 index 000000000..24e2cea30 --- /dev/null +++ b/servers/inet/generic/arp.c @@ -0,0 +1,776 @@ +/* +arp.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "type.h" + +#include "arp.h" +#include "assert.h" +#include "buf.h" +#include "clock.h" +#include "eth.h" +#include "io.h" +#include "sr.h" + +THIS_FILE + +#define ARP_CACHE_NR 64 + +#define MAX_ARP_RETRIES 5 +#define ARP_TIMEOUT (HZ/2+1) /* .5 seconds */ +#ifndef ARP_EXP_TIME +#define ARP_EXP_TIME (20L*60L*HZ) /* 20 minutes */ +#endif +#define ARP_NOTRCH_EXP_TIME (5*HZ) /* 5 seconds */ +#define ARP_INUSE_OFFSET (60*HZ) /* an entry in the cache can be deleted + if its not used for 1 minute */ + +typedef struct arp46 +{ + ether_addr_t a46_dstaddr; + ether_addr_t a46_srcaddr; + ether_type_t a46_ethtype; + union + { + struct + { + u16_t a_hdr, a_pro; + u8_t a_hln, a_pln; + u16_t a_op; + ether_addr_t a_sha; + u8_t a_spa[4]; + ether_addr_t a_tha; + u8_t a_tpa[4]; + } a46_data; + char a46_dummy[ETH_MIN_PACK_SIZE-ETH_HDR_SIZE]; + } a46_data; +} arp46_t; + +#define a46_hdr a46_data.a46_data.a_hdr +#define a46_pro a46_data.a46_data.a_pro +#define a46_hln a46_data.a46_data.a_hln +#define a46_pln a46_data.a46_data.a_pln +#define a46_op a46_data.a46_data.a_op +#define a46_sha a46_data.a46_data.a_sha +#define a46_spa a46_data.a46_data.a_spa +#define a46_tha a46_data.a46_data.a_tha +#define a46_tpa a46_data.a46_data.a_tpa + +typedef struct arp_port +{ + int ap_flags; + int ap_state; + int ap_eth_port; + int ap_ip_port; + int ap_eth_fd; + ether_addr_t ap_ethaddr; + ipaddr_t ap_ipaddr; + timer_t ap_timer; + + ether_addr_t ap_write_ethaddr; + ipaddr_t ap_write_ipaddr; + int ap_write_code; + + ipaddr_t ap_req_ipaddr; + int ap_req_count; + + arp_func_t ap_arp_func; +} arp_port_t; + +#define APF_EMPTY 0 +#define APF_ARP_RD_IP 0x4 +#define APF_ARP_RD_SP 0x8 +#define APF_ARP_WR_IP 0x10 +#define APF_ARP_WR_SP 0x20 +#define APF_INADDR_SET 0x100 +#define APF_MORE2WRITE 0x200 +#define APF_CLIENTREQ 0x400 +#define APF_CLIENTWRITE 0x1000 +#define APF_SUSPEND 0x2000 + +#define APS_INITIAL 0x00 +#define APS_GETADDR 0x01 +#define APS_ARPSTART 0x10 +#define APS_ARPPROTO 0x20 +#define APS_ARPMAIN 0x40 +#define APS_ERROR 0x80 + +typedef struct arp_cache +{ + int ac_flags; + int ac_state; + ether_addr_t ac_ethaddr; + ipaddr_t ac_ipaddr; + arp_port_t *ac_port; + time_t ac_expire; + time_t ac_lastuse; +} arp_cache_t; + +#define ACF_EMPTY 0 +#define ACF_GOTREQ 1 + +#define ACS_UNUSED 0 +#define ACS_INCOMPLETE 1 +#define ACS_VALID 2 +#define ACS_UNREACHABLE 3 + +FORWARD acc_t *arp_getdata ARGS(( int fd, size_t offset, + size_t count, int for_ioctl )); +FORWARD int arp_putdata ARGS(( int fd, size_t offset, + acc_t *data, int for_ioctl )); +FORWARD void arp_main ARGS(( arp_port_t *arp_port )); +FORWARD void arp_timeout ARGS(( int fd, timer_t *timer )); +FORWARD void setup_write ARGS(( arp_port_t *arp_port )); +FORWARD void setup_read ARGS(( arp_port_t *arp_port )); +FORWARD void process_arp_req ARGS(( arp_port_t *arp_port, acc_t *data )); +FORWARD void client_reply ARGS(( arp_port_t *arp_port, + ipaddr_t ipaddr, ether_addr_t *ethaddr )); +FORWARD arp_cache_t *find_cache_ent ARGS(( arp_port_t *arp_port, + ipaddr_t ipaddr )); +FORWARD arp_cache_t *alloc_cache_ent ARGS(( void )); + +PRIVATE arp_port_t *arp_port_table; +PRIVATE arp_cache_t arp_cache[ARP_CACHE_NR]; + +PUBLIC void arp_prep() +{ + arp_port_table= alloc(eth_conf_nr * sizeof(arp_port_table[0])); +} + +PUBLIC void arp_init() +{ + arp_port_t *arp_port; + int i; + + assert (BUF_S >= sizeof(struct nwio_ethstat)); + assert (BUF_S >= sizeof(struct nwio_ethopt)); + assert (BUF_S >= sizeof(arp46_t)); + + for (i=0, arp_port= arp_port_table; i<eth_conf_nr; i++, arp_port++) + { + arp_port->ap_state= APS_ERROR; /* Mark all ports as + * unavailable */ + } + +} + +PRIVATE void arp_main(arp_port) +arp_port_t *arp_port; +{ + int result; + + switch (arp_port->ap_state) + { + case APS_INITIAL: + arp_port->ap_eth_fd= eth_open(arp_port->ap_eth_port, + arp_port->ap_eth_port, arp_getdata, arp_putdata, 0); + + if (arp_port->ap_eth_fd<0) + { + DBLOCK(1, printf("arp.c: unable to open ethernet\n")); + return; + } + + arp_port->ap_state= APS_GETADDR; + + result= eth_ioctl (arp_port->ap_eth_fd, NWIOGETHSTAT); + + if ( result == NW_SUSPEND) + { + arp_port->ap_flags |= APF_SUSPEND; + return; + } + assert(result == NW_OK); + + /* fall through */ + case APS_GETADDR: + /* Wait for IP address */ + if (!(arp_port->ap_flags & APF_INADDR_SET)) + return; + + /* fall through */ + case APS_ARPSTART: + arp_port->ap_state= APS_ARPPROTO; + + { + arp_cache_t *cache; + int i; + + cache= arp_cache; + for (i=0; i<ARP_CACHE_NR; i++, cache++) + { + cache->ac_state= ACS_UNUSED; + cache->ac_flags= ACF_EMPTY; + cache->ac_expire= 0; + cache->ac_lastuse= 0; + } + } + result= eth_ioctl (arp_port->ap_eth_fd, NWIOSETHOPT); + + if (result==NW_SUSPEND) + { + arp_port->ap_flags |= APF_SUSPEND; + return; + } + assert(result == NW_OK); + + /* fall through */ + case APS_ARPPROTO: + arp_port->ap_state= APS_ARPMAIN; + if (arp_port->ap_flags & APF_MORE2WRITE) + setup_write(arp_port); + setup_read(arp_port); + return; + +#if !CRAMPED + default: + ip_panic(( + "arp_main(&arp_port_table[%d]) called but ap_state=0x%x\n", + arp_port->ap_eth_port, arp_port->ap_state )); +#endif + } +} + +PRIVATE acc_t *arp_getdata (fd, offset, count, for_ioctl) +int fd; +size_t offset; +size_t count; +int for_ioctl; +{ + arp_port_t *arp_port; + arp46_t *arp; + acc_t *data; + int result; + + arp_port= &arp_port_table[fd]; + + switch (arp_port->ap_state) + { + case APS_ARPPROTO: + if (!count) + { + result= (int)offset; + if (result<0) + { + arp_port->ap_state= APS_ERROR; + break; + } + if (arp_port->ap_flags & APF_SUSPEND) + { + arp_port->ap_flags &= ~APF_SUSPEND; + arp_main(arp_port); + } + return NW_OK; + } + assert ((!offset) && (count == sizeof(struct nwio_ethopt))); + { + struct nwio_ethopt *ethopt; + acc_t *acc; + + acc= bf_memreq(sizeof(*ethopt)); + ethopt= (struct nwio_ethopt *)ptr2acc_data(acc); + ethopt->nweo_flags= NWEO_COPY|NWEO_EN_BROAD| + NWEO_TYPESPEC; + ethopt->nweo_type= HTONS(ETH_ARP_PROTO); + return acc; + } + case APS_ARPMAIN: + assert (arp_port->ap_flags & APF_ARP_WR_IP); + if (!count) + { + result= (int)offset; + if (result<0) + { + DIFBLOCK(1, (result != NW_SUSPEND), + printf( + "arp.c: write error on port %d: error %d\n", + fd, result)); + + arp_port->ap_state= APS_ERROR; + break; + } + arp_port->ap_flags &= ~APF_ARP_WR_IP; + if (arp_port->ap_flags & APF_ARP_WR_SP) + setup_write(arp_port); + return NW_OK; + } + assert (offset+count <= sizeof(arp46_t)); + data= bf_memreq(sizeof(arp46_t)); + arp= (arp46_t *)ptr2acc_data(data); + data->acc_offset += offset; + data->acc_length= count; + if (arp_port->ap_write_code == ARP_REPLY) + arp->a46_dstaddr= arp_port->ap_write_ethaddr; + else + { + arp->a46_dstaddr.ea_addr[0]= 0xff; + arp->a46_dstaddr.ea_addr[1]= 0xff; + arp->a46_dstaddr.ea_addr[2]= 0xff; + arp->a46_dstaddr.ea_addr[3]= 0xff; + arp->a46_dstaddr.ea_addr[4]= 0xff; + arp->a46_dstaddr.ea_addr[5]= 0xff; + } + arp->a46_hdr= HTONS(ARP_ETHERNET); + arp->a46_pro= HTONS(ETH_IP_PROTO); + arp->a46_hln= 6; + arp->a46_pln= 4; + arp->a46_op= htons(arp_port->ap_write_code); + arp->a46_sha= arp_port->ap_ethaddr; + memcpy (arp->a46_spa, &arp_port->ap_ipaddr, sizeof(ipaddr_t)); + arp->a46_tha= arp_port->ap_write_ethaddr; + memcpy (arp->a46_tpa, &arp_port->ap_write_ipaddr, + sizeof(ipaddr_t)); + return data; + default: +#if !CRAMPED + printf("arp_getdata(%d, 0x%d, 0x%d) called but ap_state=0x%x\n", + fd, offset, count, arp_port->ap_state); +#endif + break; + } + return 0; +} + +PRIVATE int arp_putdata (fd, offset, data, for_ioctl) +int fd; +size_t offset; +acc_t *data; +int for_ioctl; +{ + arp_port_t *arp_port; + int result; + struct nwio_ethstat *ethstat; + + arp_port= &arp_port_table[fd]; + + if (arp_port->ap_flags & APF_ARP_RD_IP) + { + if (!data) + { + result= (int)offset; + if (result<0) + { + DIFBLOCK(1, (result != NW_SUSPEND), printf( + "arp.c: read error on port %d: error %d\n", + fd, result)); + + return NW_OK; + } + if (arp_port->ap_flags & APF_ARP_RD_SP) + { + arp_port->ap_flags &= ~(APF_ARP_RD_IP| + APF_ARP_RD_SP); + setup_read(arp_port); + } + else + arp_port->ap_flags &= ~(APF_ARP_RD_IP| + APF_ARP_RD_SP); + return NW_OK; + } + assert (!offset); + /* Warning: the above assertion is illegal; puts and gets of + data can be brokenup in any piece the server likes. However + we assume that the server is eth.c and it transfers only + whole packets. */ + data= bf_packIffLess(data, sizeof(arp46_t)); + if (data->acc_length >= sizeof(arp46_t)) + process_arp_req(arp_port,data); + bf_afree(data); + return NW_OK; + } + switch (arp_port->ap_state) + { + case APS_GETADDR: + if (!data) + { + result= (int)offset; + if (result<0) + { + arp_port->ap_state= APS_ERROR; + break; + } + if (arp_port->ap_flags & APF_SUSPEND) + { + arp_port->ap_flags &= ~APF_SUSPEND; + arp_main(arp_port); + } + return NW_OK; + } + compare (bf_bufsize(data), ==, sizeof(*ethstat)); + data= bf_packIffLess(data, sizeof(*ethstat)); + compare (data->acc_length, ==, sizeof(*ethstat)); + ethstat= (struct nwio_ethstat *)ptr2acc_data(data); + arp_port->ap_ethaddr= ethstat->nwes_addr; + bf_afree(data); + return NW_OK; + default: +#if !CRAMPED + printf("arp_putdata(%d, 0x%d, 0x%lx) called but ap_state=0x%x\n", + fd, offset, (unsigned long)data, arp_port->ap_state); +#endif + break; + } + return EGENERIC; +} + +PRIVATE void setup_read(arp_port) +arp_port_t *arp_port; +{ + int result; + + while (!(arp_port->ap_flags & APF_ARP_RD_IP)) + { + arp_port->ap_flags |= APF_ARP_RD_IP; + result= eth_read (arp_port->ap_eth_fd, ETH_MAX_PACK_SIZE); + if (result == NW_SUSPEND) + { + arp_port->ap_flags |= APF_ARP_RD_SP; + return; + } + DIFBLOCK(1, (result != NW_OK), + printf("arp.c: eth_read(..,%d)=%d\n", + ETH_MAX_PACK_SIZE, result)); + } +} + +PRIVATE void setup_write(arp_port) +arp_port_t *arp_port; +{ + int i, result; + + while (arp_port->ap_flags & APF_MORE2WRITE) + { + if (arp_port->ap_flags & APF_CLIENTWRITE) + { + arp_port->ap_flags &= ~APF_CLIENTWRITE; + arp_port->ap_write_ipaddr= arp_port->ap_req_ipaddr; + arp_port->ap_write_code= ARP_REQUEST; + clck_timer(&arp_port->ap_timer, + get_time() + ARP_TIMEOUT, + arp_timeout, arp_port->ap_eth_port); + } + else + { + arp_cache_t *cache; + + cache= arp_cache; + for (i=0; i<ARP_CACHE_NR; i++, cache++) + { + if ((cache->ac_flags & ACF_GOTREQ) && + cache->ac_port == arp_port) + { + cache->ac_flags &= ~ACF_GOTREQ; + arp_port->ap_write_ethaddr= cache-> + ac_ethaddr; + arp_port->ap_write_ipaddr= cache-> + ac_ipaddr; + arp_port->ap_write_code= ARP_REPLY; + break; + } + } + if (i>=ARP_CACHE_NR) + { + arp_port->ap_flags &= ~APF_MORE2WRITE; + break; + } + } + arp_port->ap_flags= (arp_port->ap_flags & ~APF_ARP_WR_SP) | + APF_ARP_WR_IP; + result= eth_write(arp_port->ap_eth_fd, sizeof(arp46_t)); + if (result == NW_SUSPEND) + arp_port->ap_flags |= APF_ARP_WR_SP; + if (result<0) + { + DIFBLOCK(1, (result != NW_SUSPEND), + printf("arp.c: eth_write(..,%d)=%d\n", + sizeof(arp46_t), result)); + return; + } + } +} + +PRIVATE void process_arp_req (arp_port, data) +arp_port_t *arp_port; +acc_t *data; +{ + arp46_t *arp; + arp_cache_t *ce; + int level; + time_t curr_time; + ipaddr_t spa, tpa; + + curr_time= get_time(); + + arp= (arp46_t *)ptr2acc_data(data); + memcpy(&spa, arp->a46_spa, sizeof(ipaddr_t)); + memcpy(&tpa, arp->a46_tpa, sizeof(ipaddr_t)); + + if (arp->a46_hdr != HTONS(ARP_ETHERNET) || + arp->a46_hln != 6 || + arp->a46_pro != HTONS(ETH_IP_PROTO) || + arp->a46_pln != 4) + return; + ce= find_cache_ent(arp_port, spa); + if (ce && ce->ac_expire < curr_time) + { + DBLOCK(0x10, printf("arp: expiring entry for "); + writeIpAddr(ce->ac_ipaddr); printf("\n")); + ce->ac_state= ACS_UNUSED; + ce= NULL; + } + if (ce == NULL) + { + if (tpa != arp_port->ap_ipaddr) + return; + + DBLOCK(0x10, printf("arp: allocating entry for "); + writeIpAddr(spa); printf("\n")); + + ce= alloc_cache_ent(); + ce->ac_flags= ACF_EMPTY; + ce->ac_state= ACS_VALID; + ce->ac_ethaddr= arp->a46_sha; + ce->ac_ipaddr= spa; + ce->ac_port= arp_port; + ce->ac_expire= curr_time+ARP_EXP_TIME; + ce->ac_lastuse= curr_time-ARP_INUSE_OFFSET; /* never used */ + } + + if (ce->ac_state == ACS_INCOMPLETE || ce->ac_state == ACS_UNREACHABLE) + { + ce->ac_ethaddr= arp->a46_sha; + if (ce->ac_state == ACS_INCOMPLETE) + { + ce->ac_state= ACS_VALID; + client_reply(arp_port, spa, &arp->a46_sha); + } + else + ce->ac_state= ACS_VALID; + } + + /* Update fields in the arp cache. */ +#if !CRAMPED + if (memcmp(&ce->ac_ethaddr, &arp->a46_sha, + sizeof(ce->ac_ethaddr)) != 0) + { + printf("arp: ethernet address for IP address "); + writeIpAddr(spa); + printf(" changed from "); + writeEtherAddr(&ce->ac_ethaddr); + printf(" to "); + writeEtherAddr(&arp->a46_sha); + printf("\n"); + ce->ac_ethaddr= arp->a46_sha; + } +#else + ce->ac_ethaddr= arp->a46_sha; +#endif + ce->ac_expire= curr_time+ARP_EXP_TIME; + + if (arp->a46_op == HTONS(ARP_REQUEST) && (tpa == arp_port->ap_ipaddr)) + { + ce->ac_flags |= ACF_GOTREQ; + arp_port->ap_flags |= APF_MORE2WRITE; + if (!(arp_port->ap_flags & APF_ARP_WR_IP)) + setup_write(arp_port); + } +} + +PRIVATE void client_reply (arp_port, ipaddr, ethaddr) +arp_port_t *arp_port; +ipaddr_t ipaddr; +ether_addr_t *ethaddr; +{ + if ((arp_port->ap_flags & APF_CLIENTREQ) && + ipaddr == arp_port->ap_req_ipaddr) + { + arp_port->ap_flags &= ~(APF_CLIENTREQ|APF_CLIENTWRITE); + clck_untimer(&arp_port->ap_timer); + } + (*arp_port->ap_arp_func)(arp_port->ap_ip_port, ipaddr, ethaddr); +} + +PRIVATE arp_cache_t *find_cache_ent (arp_port, ipaddr) +arp_port_t *arp_port; +ipaddr_t ipaddr; +{ + arp_cache_t *cache; + int i; + + for (i=0, cache= arp_cache; i<ARP_CACHE_NR; i++, cache++) + { + if (cache->ac_state != ACS_UNUSED && + cache->ac_port == arp_port && + cache->ac_ipaddr == ipaddr) + { + return cache; + } + } + return NULL; +} + +PRIVATE arp_cache_t *alloc_cache_ent() +{ + arp_cache_t *cache, *old; + int i; + + old= NULL; + for (i=0, cache= arp_cache; i<ARP_CACHE_NR; i++, cache++) + { + if (cache->ac_state == ACS_UNUSED) + return cache; + if (cache->ac_state == ACS_INCOMPLETE) + continue; + if (!old || cache->ac_lastuse < old->ac_lastuse) + old= cache; + } + assert(old); + return old; +} + +PUBLIC void arp_set_ipaddr (eth_port, ipaddr) +int eth_port; +ipaddr_t ipaddr; +{ + arp_port_t *arp_port; + int i; + + if (eth_port < 0 || eth_port >= eth_conf_nr) + return; + arp_port= &arp_port_table[eth_port]; + + arp_port->ap_ipaddr= ipaddr; + arp_port->ap_flags |= APF_INADDR_SET; + arp_port->ap_flags &= ~APF_SUSPEND; + if (arp_port->ap_state == APS_GETADDR) + arp_main(arp_port); +} + +PUBLIC int arp_set_cb(eth_port, ip_port, arp_func) +int eth_port; +int ip_port; +arp_func_t arp_func; +{ + arp_port_t *arp_port; + int i; + + assert(eth_port >= 0); + if (eth_port >= eth_conf_nr) + return ENXIO; + + arp_port= &arp_port_table[eth_port]; + arp_port->ap_eth_port= eth_port; + arp_port->ap_ip_port= ip_port; + arp_port->ap_state= APS_INITIAL; + arp_port->ap_flags= APF_EMPTY; + arp_port->ap_arp_func= arp_func; + + arp_main(arp_port); + + return NW_OK; +} + +PUBLIC int arp_ip_eth (eth_port, ipaddr, ethaddr) +int eth_port; +ipaddr_t ipaddr; +ether_addr_t *ethaddr; +{ + arp_port_t *arp_port; + int i; + arp_cache_t *ce; + time_t curr_time; + + assert(eth_port >= 0 && eth_port < eth_conf_nr); + arp_port= &arp_port_table[eth_port]; + assert(arp_port->ap_state == APS_ARPMAIN || + (printf("ap_state= %d\n", arp_port->ap_state), 0)); + + curr_time= get_time(); + + ce= find_cache_ent (arp_port, ipaddr); + if (ce && ce->ac_expire < curr_time) + { + ce->ac_state= ACS_UNUSED; + ce= NULL; + } + if (ce) + { + /* Found an entry. This entry should be valid, unreachable + * or incomplete. + */ + ce->ac_lastuse= curr_time; + if (ce->ac_state == ACS_VALID) + { + *ethaddr= ce->ac_ethaddr; + return NW_OK; + } + if (ce->ac_state == ACS_UNREACHABLE) + return EDSTNOTRCH; + assert(ce->ac_state == ACS_INCOMPLETE); + return NW_SUSPEND; + } + + if (arp_port->ap_flags & APF_CLIENTREQ) + { + /* We should implement something to be able to do + * multiple arp lookups at the same time. At the moment + * we just return SUSPEND. + */ + return NW_SUSPEND; + } + ce= alloc_cache_ent(); + ce->ac_flags= 0; + ce->ac_state= ACS_INCOMPLETE; + ce->ac_ipaddr= ipaddr; + ce->ac_port= arp_port; + ce->ac_expire= curr_time+ARP_EXP_TIME; + ce->ac_lastuse= curr_time; + arp_port->ap_flags |= APF_CLIENTREQ|APF_MORE2WRITE | APF_CLIENTWRITE; + arp_port->ap_req_ipaddr= ipaddr; + arp_port->ap_req_count= 0; + if (!(arp_port->ap_flags & APF_ARP_WR_IP)) + setup_write(arp_port); + return NW_SUSPEND; +} + +PRIVATE void arp_timeout (fd, timer) +int fd; +timer_t *timer; +{ + arp_port_t *arp_port; + arp_cache_t *ce; + int level; + time_t curr_time; + + arp_port= &arp_port_table[fd]; + + assert (timer == &arp_port->ap_timer); + + if (++arp_port->ap_req_count < MAX_ARP_RETRIES) + { + arp_port->ap_flags |= APF_CLIENTWRITE|APF_MORE2WRITE; + if (!(arp_port->ap_flags & APF_ARP_WR_IP)) + setup_write(arp_port); + } + else + { + ce= find_cache_ent(arp_port, arp_port->ap_req_ipaddr); + if (ce) { + assert(ce->ac_state == ACS_INCOMPLETE || + (printf("ce->ac_state= %d\n", ce->ac_state),0)); + curr_time= get_time(); + ce->ac_state= ACS_UNREACHABLE; + ce->ac_expire= curr_time+ ARP_NOTRCH_EXP_TIME; + ce->ac_lastuse= curr_time; + + client_reply(arp_port, ce->ac_ipaddr, NULL); + } + } +} + +/* + * $PchId: arp.c,v 1.6 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/arp.h b/servers/inet/generic/arp.h new file mode 100644 index 000000000..5dadb1929 --- /dev/null +++ b/servers/inet/generic/arp.h @@ -0,0 +1,29 @@ +/* +arp.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef ARP_H +#define ARP_H + +#define ARP_ETHERNET 1 + +#define ARP_REQUEST 1 +#define ARP_REPLY 2 + +/* Prototypes */ +typedef void (*arp_func_t) ARGS(( int fd, ipaddr_t ipaddr, + ether_addr_t *ethaddr )); + +void arp_prep ARGS(( void )); +void arp_init ARGS(( void )); +void arp_set_ipaddr ARGS(( int eth_port, ipaddr_t ipaddr )); +int arp_set_cb ARGS(( int eth_port, int ip_port, arp_func_t arp_func )); +int arp_ip_eth ARGS(( int eth_port, ipaddr_t ipaddr, ether_addr_t *ethaddr )); + +#endif /* ARP_H */ + +/* + * $PchId: arp.h,v 1.5 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/assert.h b/servers/inet/generic/assert.h new file mode 100644 index 000000000..929cc1a3b --- /dev/null +++ b/servers/inet/generic/assert.h @@ -0,0 +1,31 @@ +/* +assert.h + +Copyright 1995 Philip Homburg +*/ +#ifndef INET_ASSERT_H +#define INET_ASSERT_H + +#if !NDEBUG + +void bad_assertion(char *file, int line, char *what); +void bad_compare(char *file, int line, int lhs, char *what, int rhs); + +#define assert(x) (!(x) ? bad_assertion(this_file, __LINE__, #x) \ + : (void) 0) +#define compare(a,t,b) (!((a) t (b)) ? bad_compare(this_file, __LINE__, \ + (a), #a " " #t " " #b, (b)) : (void) 0) + +#else /* NDEBUG */ + +#define assert(x) 0 +#define compare(a,t,b) 0 + +#endif /* NDEBUG */ + +#endif /* INET_ASSERT_H */ + + +/* + * $PchId: assert.h,v 1.4 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/buf.h b/servers/inet/generic/buf.h new file mode 100644 index 000000000..6b0223039 --- /dev/null +++ b/servers/inet/generic/buf.h @@ -0,0 +1,235 @@ +/* +buf.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef BUF_H +#define BUF_H + +/* Note: BUF_S should be defined in const.h */ + +#define MAX_BUFREQ_PRI 10 + +#define ETH_PRI_PORTBUFS 3 +#define ETH_PRI_FDBUFS_EXTRA 5 +#define ETH_PRI_FDBUFS 6 + +#define IP_PRI_PORTBUFS 3 +#define IP_PRI_ASSBUFS 4 +#define IP_PRI_FDBUFS_EXTRA 5 +#define IP_PRI_FDBUFS 6 + +#define ICMP_PRI_QUEUE 1 + +#define TCP_PRI_FRAG2SEND 4 +#define TCP_PRI_CONN_EXTRA 5 +#define TCP_PRI_CONNwoUSER 7 +#define TCP_PRI_CONN_INUSE 9 + +#define UDP_PRI_FDBUFS_EXTRA 5 +#define UDP_PRI_FDBUFS 6 + +#define PSIP_PRI_EXP_PROMISC 2 + +struct acc; +typedef void (*buffree_t) ARGS(( struct acc *acc )); +typedef void (*bf_freereq_t) ARGS(( int priority )); + +#ifdef BUF_CONSISTENCY_CHECK +typedef void (*bf_checkreq_t) ARGS(( void )); +#endif + +typedef struct buf +{ + int buf_linkC; + buffree_t buf_free; + size_t buf_size; + char *buf_data_p; + +#ifdef BUF_TRACK_ALLOC_FREE + char *buf_alloc_file; + int buf_alloc_line; + char *buf_free_file; + int buf_free_line; +#endif +#ifdef BUF_CONSISTENCY_CHECK + unsigned buf_generation; + int buf_check_linkC; +#endif +} buf_t; + +typedef struct acc +{ + int acc_linkC; + int acc_offset, acc_length; + buf_t *acc_buffer; + struct acc *acc_next, *acc_ext_link; + +#ifdef BUF_TRACK_ALLOC_FREE + char *acc_alloc_file; + int acc_alloc_line; + char *acc_free_file; + int acc_free_line; +#endif +#ifdef BUF_CONSISTENCY_CHECK + unsigned acc_generation; + int acc_check_linkC; +#endif +} acc_t; + +extern acc_t *bf_temporary_acc; + +/* For debugging... */ + +#ifdef BUF_TRACK_ALLOC_FREE + +#ifndef BUF_IMPLEMENTATION + +#define bf_memreq(a) _bf_memreq(this_file, __LINE__, a) +#define bf_cut(a,b,c) _bf_cut(this_file, __LINE__, a, b, c) +#define bf_delhead(a,b) _bf_delhead(this_file, __LINE__, a, b) +#define bf_packIffLess(a,b) _bf_packIffLess(this_file, __LINE__, \ + a, b) +#define bf_afree(a) _bf_afree(this_file, __LINE__, a) +#define bf_pack(a) _bf_pack(this_file, __LINE__, a) +#define bf_append(a,b) _bf_append(this_file, __LINE__, a, b) +#define bf_dupacc(a) _bf_dupacc(this_file, __LINE__, a) +#define bf_mark_acc(a) _bf_mark_acc(this_file, __LINE__, a) +#define bf_align(a,s,al) _bf_align(this_file, __LINE__, a, s, al) + +#else /* BUF_IMPLEMENTATION */ + +#define bf_afree(a) _bf_afree(clnt_file, clnt_line, a) +#define bf_pack(a) _bf_pack(clnt_file, clnt_line, a) +#define bf_memreq(a) _bf_memreq(clnt_file, clnt_line, a) +#define bf_dupacc(a) _bf_dupacc(clnt_file, clnt_line, a) +#define bf_cut(a,b,c) _bf_cut(clnt_file, clnt_line, a, b, c) +#define bf_delhead(a,b) _bf_delhead(clnt_file, clnt_line, a, b) +#define bf_align(a,s,al) _bf_align(clnt_file, clnt_line, a, s, al) + +#endif /* !BUF_IMPLEMENTATION */ + +#else + +#define bf_mark_acc(acc) ((void)0) + +#endif /* BUF_TRACK_ALLOC_FREE */ + +/* Prototypes */ + +void bf_init ARGS(( void )); +#ifndef BUF_CONSISTENCY_CHECK +void bf_logon ARGS(( bf_freereq_t func )); +#else +void bf_logon ARGS(( bf_freereq_t func, bf_checkreq_t checkfunc )); +#endif + +#ifndef BUF_TRACK_ALLOC_FREE +acc_t *bf_memreq ARGS(( unsigned size)); +#else +acc_t *_bf_memreq ARGS(( char *clnt_file, int clnt_line, + unsigned size)); +#endif +/* the result is an acc with linkC == 1 */ + +#ifndef BUF_TRACK_ALLOC_FREE +acc_t *bf_dupacc ARGS(( acc_t *acc )); +#else +acc_t *_bf_dupacc ARGS(( char *clnt_file, int clnt_line, + acc_t *acc )); +#endif +/* the result is an acc with linkC == 1 identical to the given one */ + +#ifndef BUF_TRACK_ALLOC_FREE +void bf_afree ARGS(( acc_t *acc)); +#else +void _bf_afree ARGS(( char *clnt_file, int clnt_line, + acc_t *acc)); +#endif +/* this reduces the linkC off the given acc with one */ + +#ifndef BUF_TRACK_ALLOC_FREE +acc_t *bf_pack ARGS(( acc_t *pack)); +#else +acc_t *_bf_pack ARGS(( char *clnt_file, int clnt_line, + acc_t *pack)); +#endif +/* this gives a packed copy of the given acc, the linkC of the given acc is + reduced by one, the linkC of the result == 1 */ + +#ifndef BUF_TRACK_ALLOC_FREE +acc_t *bf_packIffLess ARGS(( acc_t *pack, int min_len )); +#else +acc_t *_bf_packIffLess ARGS(( char *clnt_file, int clnt_line, + acc_t *pack, int min_len )); +#endif +/* this performs a bf_pack iff pack->acc_length<min_len */ + +size_t bf_bufsize ARGS(( acc_t *pack)); +/* this gives the length of the buffer specified by the given acc. The linkC + of the given acc remains the same */ + +#ifndef BUF_TRACK_ALLOC_FREE +acc_t *bf_cut ARGS(( acc_t *data, unsigned offset, unsigned length )); +#else +acc_t *_bf_cut ARGS(( char *clnt_file, int clnt_line, + acc_t *data, unsigned offset, unsigned length )); +#endif +/* the result is a cut of the buffer from offset with length length. + The linkC of the result == 1, the linkC of the given acc remains the + same. */ + +#ifndef BUF_TRACK_ALLOC_FREE +acc_t *bf_delhead ARGS(( acc_t *data, unsigned offset )); +#else +acc_t *_bf_delhead ARGS(( char *clnt_file, int clnt_line, + acc_t *data, unsigned offset )); +#endif +/* the result is a cut of the buffer from offset until the end. + The linkC of the result == 1, the linkC of the given acc is + decremented. */ + +#ifndef BUF_TRACK_ALLOC_FREE +acc_t *bf_append ARGS(( acc_t *data_first, acc_t *data_second )); +#else +acc_t *_bf_append ARGS(( char *clnt_file, int clnt_line, + acc_t *data_first, acc_t *data_second )); +#endif +/* data_second is appended after data_first, a link is returned to the + result and the linkCs of data_first and data_second are reduced. + further more, if the contents of the last part of data_first and + the first part of data_second fit in a buffer, both parts are + copied into a (possibly fresh) buffer +*/ + +#ifndef BUF_TRACK_ALLOC_FREE +acc_t *bf_align ARGS(( acc_t *acc, size_t size, size_t alignment )); +#else +acc_t *_bf_align ARGS(( char *clnt_file, int clnt_line, + acc_t *acc, size_t size, size_t alignment )); +#endif +/* size bytes of acc (or all bytes of acc if the size buffer is smaller + than size) are aligned on an address that is multiple of alignment. + Size must be less than or equal to BUF_S. +*/ + +#define ptr2acc_data(/* acc_t * */ a) (bf_temporary_acc=(a), \ + (&bf_temporary_acc->acc_buffer->buf_data_p[bf_temporary_acc-> \ + acc_offset])) + +#define bf_chkbuf(buf) ((buf)? (compare((buf)->acc_linkC,>,0), \ + compare((buf)->acc_buffer, !=, 0), \ + compare((buf)->acc_buffer->buf_linkC,>,0)) : 0) + +#ifdef BUF_CONSISTENCY_CHECK +int bf_consistency_check ARGS(( void )); +void bf_check_acc ARGS(( acc_t *acc )); +void _bf_mark_acc ARGS(( char *clnt_file, int clnt_line, acc_t *acc )); +#endif + +#endif /* BUF_H */ + +/* + * $PchId: buf.h,v 1.8 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/clock.h b/servers/inet/generic/clock.h new file mode 100644 index 000000000..f52a51db5 --- /dev/null +++ b/servers/inet/generic/clock.h @@ -0,0 +1,39 @@ +/* +clock.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef CLOCK_H +#define CLOCK_H + +struct timer; + +typedef void (*timer_func_t) ARGS(( int fd, struct timer *timer )); + +typedef struct timer +{ + struct timer *tim_next; + timer_func_t tim_func; + int tim_ref; + time_t tim_time; + int tim_active; +} timer_t; + +extern int clck_call_expire; /* Call clck_expire_timer from the mainloop */ + +void clck_init ARGS(( void )); +void set_time ARGS(( time_t time )); +time_t get_time ARGS(( void )); +void reset_time ARGS(( void )); +/* set a timer to go off at the time specified by timeout */ +void clck_timer ARGS(( struct timer *timer, time_t timeout, timer_func_t func, + int fd )); +void clck_untimer ARGS(( struct timer *timer )); +void clck_expire_timers ARGS(( void )); + +#endif /* CLOCK_H */ + +/* + * $PchId: clock.h,v 1.5 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/eth.c b/servers/inet/generic/eth.c new file mode 100644 index 000000000..23fe72f07 --- /dev/null +++ b/servers/inet/generic/eth.c @@ -0,0 +1,973 @@ +/* +eth.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "buf.h" +#include "clock.h" +#include "event.h" +#include "osdep_eth.h" +#include "type.h" + +#include "assert.h" +#include "buf.h" +#include "eth.h" +#include "eth_int.h" +#include "io.h" +#include "sr.h" + +THIS_FILE + +#define ETH_FD_NR (4*IP_PORT_MAX) +#define EXPIRE_TIME 60*HZ /* seconds */ + +typedef struct eth_fd +{ + int ef_flags; + nwio_ethopt_t ef_ethopt; + eth_port_t *ef_port; + struct eth_fd *ef_type_next; + int ef_srfd; + acc_t *ef_rdbuf_head; + acc_t *ef_rdbuf_tail; + get_userdata_t ef_get_userdata; + put_userdata_t ef_put_userdata; + put_pkt_t ef_put_pkt; + time_t ef_exp_time; + size_t ef_write_count; +} eth_fd_t; + +#define EFF_FLAGS 0xf +# define EFF_EMPTY 0x0 +# define EFF_INUSE 0x1 +# define EFF_BUSY 0x6 +# define EFF_READ_IP 0x2 +# define EFF_WRITE_IP 0x4 +# define EFF_OPTSET 0x8 + +FORWARD int eth_checkopt ARGS(( eth_fd_t *eth_fd )); +FORWARD void hash_fd ARGS(( eth_fd_t *eth_fd )); +FORWARD void unhash_fd ARGS(( eth_fd_t *eth_fd )); +FORWARD void eth_buffree ARGS(( int priority )); +#ifdef BUF_CONSISTENCY_CHECK +FORWARD void eth_bufcheck ARGS(( void )); +#endif +FORWARD void packet2user ARGS(( eth_fd_t *fd, acc_t *pack, time_t exp_time )); +FORWARD void reply_thr_get ARGS(( eth_fd_t *eth_fd, + size_t result, int for_ioctl )); +FORWARD void reply_thr_put ARGS(( eth_fd_t *eth_fd, + size_t result, int for_ioctl )); +FORWARD u32_t compute_rec_conf ARGS(( eth_port_t *eth_port )); + +PUBLIC eth_port_t *eth_port_table; + +PRIVATE eth_fd_t eth_fd_table[ETH_FD_NR]; +PRIVATE ether_addr_t broadcast= { { 255, 255, 255, 255, 255, 255 } }; + +PUBLIC void eth_prep() +{ + eth_port_table= alloc(eth_conf_nr * sizeof(eth_port_table[0])); +} + +PUBLIC void eth_init() +{ + int i, j; + + assert (BUF_S >= sizeof(nwio_ethopt_t)); + assert (BUF_S >= ETH_HDR_SIZE); /* these are in fact static assertions, + thus a good compiler doesn't + generate any code for this */ + +#if ZERO + for (i=0; i<ETH_FD_NR; i++) + eth_fd_table[i].ef_flags= EFF_EMPTY; + for (i=0; i<eth_conf_nr; i++) + { + eth_port_table[i].etp_flags= EFF_EMPTY; + eth_port_table[i].etp_type_any= NULL; + ev_init(ð_port_table[i].etp_sendev); + for (j= 0; j<ETH_TYPE_HASH_NR; j++) + eth_port_table[i].etp_type[j]= NULL; + } +#endif + +#ifndef BUF_CONSISTENCY_CHECK + bf_logon(eth_buffree); +#else + bf_logon(eth_buffree, eth_bufcheck); +#endif + + osdep_eth_init(); +} + +PUBLIC int eth_open(port, srfd, get_userdata, put_userdata, put_pkt) +int port, srfd; +get_userdata_t get_userdata; +put_userdata_t put_userdata; +put_pkt_t put_pkt; +{ + int i; + eth_port_t *eth_port; + eth_fd_t *eth_fd; + + DBLOCK(0x20, printf("eth_open (%d, %d, %lx, %lx)\n", port, srfd, + (unsigned long)get_userdata, (unsigned long)put_userdata)); + eth_port= ð_port_table[port]; + if (!(eth_port->etp_flags & EPF_ENABLED)) + return EGENERIC; + + for (i=0; i<ETH_FD_NR && (eth_fd_table[i].ef_flags & EFF_INUSE); + i++); + + if (i>=ETH_FD_NR) + { + DBLOCK(1, printf("out of fds\n")); + return EAGAIN; + } + + eth_fd= ð_fd_table[i]; + + eth_fd->ef_flags= EFF_INUSE; + eth_fd->ef_ethopt.nweo_flags=NWEO_DEFAULT; + eth_fd->ef_port= eth_port; + eth_fd->ef_srfd= srfd; + assert(eth_fd->ef_rdbuf_head == NULL); + eth_fd->ef_get_userdata= get_userdata; + eth_fd->ef_put_userdata= put_userdata; + eth_fd->ef_put_pkt= put_pkt; + return i; +} + +PUBLIC int eth_ioctl(fd, req) +int fd; +ioreq_t req; +{ + acc_t *data; + eth_fd_t *eth_fd; + eth_port_t *eth_port; + + DBLOCK(0x20, printf("eth_ioctl (%d, %lu)\n", fd, req)); + eth_fd= ð_fd_table[fd]; + eth_port= eth_fd->ef_port; + + assert (eth_fd->ef_flags & EFF_INUSE); + + switch (req) + { + case NWIOSETHOPT: + { + nwio_ethopt_t *ethopt; + nwio_ethopt_t oldopt, newopt; + int result; + u32_t new_en_flags, new_di_flags, + old_en_flags, old_di_flags; + u32_t flags; + + data= (*eth_fd->ef_get_userdata)(eth_fd-> + ef_srfd, 0, sizeof(nwio_ethopt_t), TRUE); + + ethopt= (nwio_ethopt_t *)ptr2acc_data(data); + oldopt= eth_fd->ef_ethopt; + newopt= *ethopt; + + old_en_flags= oldopt.nweo_flags & 0xffff; + old_di_flags= (oldopt.nweo_flags >> 16) & 0xffff; + new_en_flags= newopt.nweo_flags & 0xffff; + new_di_flags= (newopt.nweo_flags >> 16) & 0xffff; + if (new_en_flags & new_di_flags) + { + bf_afree(data); + reply_thr_get (eth_fd, EBADMODE, TRUE); + return NW_OK; + } + + /* NWEO_ACC_MASK */ + if (new_di_flags & NWEO_ACC_MASK) + { + bf_afree(data); + reply_thr_get (eth_fd, EBADMODE, TRUE); + return NW_OK; + } + /* you can't disable access modes */ + + if (!(new_en_flags & NWEO_ACC_MASK)) + new_en_flags |= (old_en_flags & NWEO_ACC_MASK); + + + /* NWEO_LOC_MASK */ + if (!((new_en_flags | new_di_flags) & NWEO_LOC_MASK)) + { + new_en_flags |= (old_en_flags & NWEO_LOC_MASK); + new_di_flags |= (old_di_flags & NWEO_LOC_MASK); + } + + /* NWEO_BROAD_MASK */ + if (!((new_en_flags | new_di_flags) & NWEO_BROAD_MASK)) + { + new_en_flags |= (old_en_flags & NWEO_BROAD_MASK); + new_di_flags |= (old_di_flags & NWEO_BROAD_MASK); + } + + /* NWEO_MULTI_MASK */ + if (!((new_en_flags | new_di_flags) & NWEO_MULTI_MASK)) + { + new_en_flags |= (old_en_flags & NWEO_MULTI_MASK); + new_di_flags |= (old_di_flags & NWEO_MULTI_MASK); + newopt.nweo_multi= oldopt.nweo_multi; + } + + /* NWEO_PROMISC_MASK */ + if (!((new_en_flags | new_di_flags) & NWEO_PROMISC_MASK)) + { + new_en_flags |= (old_en_flags & NWEO_PROMISC_MASK); + new_di_flags |= (old_di_flags & NWEO_PROMISC_MASK); + } + + /* NWEO_REM_MASK */ + if (!((new_en_flags | new_di_flags) & NWEO_REM_MASK)) + { + new_en_flags |= (old_en_flags & NWEO_REM_MASK); + new_di_flags |= (old_di_flags & NWEO_REM_MASK); + newopt.nweo_rem= oldopt.nweo_rem; + } + + /* NWEO_TYPE_MASK */ + if (!((new_en_flags | new_di_flags) & NWEO_TYPE_MASK)) + { + new_en_flags |= (old_en_flags & NWEO_TYPE_MASK); + new_di_flags |= (old_di_flags & NWEO_TYPE_MASK); + newopt.nweo_type= oldopt.nweo_type; + } + + /* NWEO_RW_MASK */ + if (!((new_en_flags | new_di_flags) & NWEO_RW_MASK)) + { + new_en_flags |= (old_en_flags & NWEO_RW_MASK); + new_di_flags |= (old_di_flags & NWEO_RW_MASK); + } + + if (eth_fd->ef_flags & EFF_OPTSET) + unhash_fd(eth_fd); + + newopt.nweo_flags= ((unsigned long)new_di_flags << 16) | + new_en_flags; + eth_fd->ef_ethopt= newopt; + + result= eth_checkopt(eth_fd); + + if (result<0) + eth_fd->ef_ethopt= oldopt; + else + { + unsigned long opt_flags; + unsigned changes; + opt_flags= oldopt.nweo_flags ^ + eth_fd->ef_ethopt.nweo_flags; + changes= ((opt_flags >> 16) | opt_flags) & + 0xffff; + if (changes & (NWEO_BROAD_MASK | + NWEO_MULTI_MASK | NWEO_PROMISC_MASK)) + { + flags= compute_rec_conf(eth_port); + eth_set_rec_conf(eth_port, flags); + } + } + + if (eth_fd->ef_flags & EFF_OPTSET) + hash_fd(eth_fd); + + bf_afree(data); + reply_thr_get (eth_fd, result, TRUE); + return NW_OK; + } + + case NWIOGETHOPT: + { + nwio_ethopt_t *ethopt; + acc_t *acc; + int result; + + acc= bf_memreq(sizeof(nwio_ethopt_t)); + + ethopt= (nwio_ethopt_t *)ptr2acc_data(acc); + + *ethopt= eth_fd->ef_ethopt; + + result= (*eth_fd->ef_put_userdata)(eth_fd-> + ef_srfd, 0, acc, TRUE); + if (result >= 0) + reply_thr_put(eth_fd, NW_OK, TRUE); + return result; + } + case NWIOGETHSTAT: + { + nwio_ethstat_t *ethstat; + acc_t *acc; + int result; + +assert (sizeof(nwio_ethstat_t) <= BUF_S); + + eth_port= eth_fd->ef_port; + if (!(eth_port->etp_flags & EPF_ENABLED)) + { + reply_thr_put(eth_fd, EGENERIC, TRUE); + return NW_OK; + } + + acc= bf_memreq(sizeof(nwio_ethstat_t)); +compare (bf_bufsize(acc), ==, sizeof(*ethstat)); + + ethstat= (nwio_ethstat_t *)ptr2acc_data(acc); + + ethstat->nwes_addr= eth_port->etp_ethaddr; + + result= eth_get_stat(eth_port, ðstat->nwes_stat); +assert (result == 0); +compare (bf_bufsize(acc), ==, sizeof(*ethstat)); + result= (*eth_fd->ef_put_userdata)(eth_fd-> + ef_srfd, 0, acc, TRUE); + if (result >= 0) + reply_thr_put(eth_fd, NW_OK, TRUE); + return result; + } + default: + break; + } + reply_thr_put(eth_fd, EBADIOCTL, TRUE); + return NW_OK; +} + +PUBLIC int eth_write(fd, count) +int fd; +size_t count; +{ + eth_fd_t *eth_fd; + eth_port_t *eth_port; + acc_t *user_data; + int r; + + eth_fd= ð_fd_table[fd]; + eth_port= eth_fd->ef_port; + + if (!(eth_fd->ef_flags & EFF_OPTSET)) + { + reply_thr_get (eth_fd, EBADMODE, FALSE); + return NW_OK; + } + + assert (!(eth_fd->ef_flags & EFF_WRITE_IP)); + + eth_fd->ef_write_count= count; + if (eth_fd->ef_ethopt.nweo_flags & NWEO_RWDATONLY) + count += ETH_HDR_SIZE; + + if (count<ETH_MIN_PACK_SIZE || count>ETH_MAX_PACK_SIZE) + { + DBLOCK(1, printf("illegal packetsize (%d)\n",count)); + reply_thr_get (eth_fd, EPACKSIZE, FALSE); + return NW_OK; + } + eth_fd->ef_flags |= EFF_WRITE_IP; + if (eth_port->etp_wr_pack) + { + eth_port->etp_flags |= EPF_MORE2WRITE; + return NW_SUSPEND; + } + + user_data= (*eth_fd->ef_get_userdata)(eth_fd->ef_srfd, 0, + eth_fd->ef_write_count, FALSE); + if (!user_data) + { + eth_fd->ef_flags &= ~EFF_WRITE_IP; + reply_thr_get (eth_fd, EFAULT, FALSE); + return NW_OK; + } + r= eth_send(fd, user_data, eth_fd->ef_write_count); + assert(r == NW_OK); + + eth_fd->ef_flags &= ~EFF_WRITE_IP; + reply_thr_get(eth_fd, eth_fd->ef_write_count, FALSE); + return NW_OK; +} + +PUBLIC int eth_send(fd, data, data_len) +int fd; +acc_t *data; +size_t data_len; +{ + eth_fd_t *eth_fd; + eth_port_t *eth_port; + eth_hdr_t *eth_hdr; + acc_t *eth_pack; + unsigned long nweo_flags; + size_t count; + ev_arg_t ev_arg; + + eth_fd= ð_fd_table[fd]; + eth_port= eth_fd->ef_port; + + if (!(eth_fd->ef_flags & EFF_OPTSET)) + return EBADMODE; + + count= data_len; + if (eth_fd->ef_ethopt.nweo_flags & NWEO_RWDATONLY) + count += ETH_HDR_SIZE; + + if (count<ETH_MIN_PACK_SIZE || count>ETH_MAX_PACK_SIZE) + { + DBLOCK(1, printf("illegal packetsize (%d)\n",count)); + return EPACKSIZE; + } + if (eth_port->etp_wr_pack) + return NW_WOULDBLOCK; + + nweo_flags= eth_fd->ef_ethopt.nweo_flags; + + if (nweo_flags & NWEO_RWDATONLY) + { + eth_pack= bf_memreq(ETH_HDR_SIZE); + eth_pack->acc_next= data; + } + else + eth_pack= bf_packIffLess(data, ETH_HDR_SIZE); + + eth_hdr= (eth_hdr_t *)ptr2acc_data(eth_pack); + + if (nweo_flags & NWEO_REMSPEC) + eth_hdr->eh_dst= eth_fd->ef_ethopt.nweo_rem; + + if (!(nweo_flags & NWEO_EN_PROMISC)) + eth_hdr->eh_src= eth_port->etp_ethaddr; + + if (nweo_flags & NWEO_TYPESPEC) + eth_hdr->eh_proto= eth_fd->ef_ethopt.nweo_type; + + if (eth_addrcmp(eth_hdr->eh_dst, eth_port->etp_ethaddr) == 0) + { + /* Local loopback. */ + eth_port->etp_wr_pack= eth_pack; + ev_arg.ev_ptr= eth_port; + ev_enqueue(ð_port->etp_sendev, eth_loop_ev, ev_arg); + } + else + eth_write_port(eth_port, eth_pack); + return NW_OK; +} + +PUBLIC int eth_read (fd, count) +int fd; +size_t count; +{ + eth_fd_t *eth_fd; + acc_t *pack; + + eth_fd= ð_fd_table[fd]; + if (!(eth_fd->ef_flags & EFF_OPTSET)) + { + reply_thr_put(eth_fd, EBADMODE, FALSE); + return NW_OK; + } + if (count < ETH_MAX_PACK_SIZE) + { + reply_thr_put(eth_fd, EPACKSIZE, FALSE); + return NW_OK; + } + + assert(!(eth_fd->ef_flags & EFF_READ_IP)); + eth_fd->ef_flags |= EFF_READ_IP; + + while (eth_fd->ef_rdbuf_head) + { + pack= eth_fd->ef_rdbuf_head; + eth_fd->ef_rdbuf_head= pack->acc_ext_link; + if (get_time() <= eth_fd->ef_exp_time) + { + packet2user(eth_fd, pack, eth_fd->ef_exp_time); + if (!(eth_fd->ef_flags & EFF_READ_IP)) + return NW_OK; + } + else + bf_afree(pack); + } + return NW_SUSPEND; +} + +PUBLIC int eth_cancel(fd, which_operation) +int fd; +int which_operation; +{ + eth_fd_t *eth_fd; + + DBLOCK(2, printf("eth_cancel (%d)\n", fd)); + eth_fd= ð_fd_table[fd]; + + switch (which_operation) + { + case SR_CANCEL_READ: +assert (eth_fd->ef_flags & EFF_READ_IP); + eth_fd->ef_flags &= ~EFF_READ_IP; + reply_thr_put(eth_fd, EINTR, FALSE); + break; + case SR_CANCEL_WRITE: +assert (eth_fd->ef_flags & EFF_WRITE_IP); + eth_fd->ef_flags &= ~EFF_WRITE_IP; + reply_thr_get(eth_fd, EINTR, FALSE); + break; +#if !CRAMPED + default: + ip_panic(( "got unknown cancel request" )); +#endif + } + return NW_OK; +} + +PUBLIC void eth_close(fd) +int fd; +{ + eth_fd_t *eth_fd; + eth_port_t *eth_port; + u32_t flags; + acc_t *pack; + + eth_fd= ð_fd_table[fd]; + + assert ((eth_fd->ef_flags & EFF_INUSE) && + !(eth_fd->ef_flags & EFF_BUSY)); + + if (eth_fd->ef_flags & EFF_OPTSET) + unhash_fd(eth_fd); + while (eth_fd->ef_rdbuf_head != NULL) + { + pack= eth_fd->ef_rdbuf_head; + eth_fd->ef_rdbuf_head= pack->acc_ext_link; + bf_afree(pack); + } + eth_fd->ef_flags= EFF_EMPTY; + + eth_port= eth_fd->ef_port; + flags= compute_rec_conf(eth_port); + eth_set_rec_conf(eth_port, flags); +} + +PUBLIC void eth_loop_ev(ev, ev_arg) +event_t *ev; +ev_arg_t ev_arg; +{ + acc_t *pack; + eth_port_t *eth_port; + + eth_port= ev_arg.ev_ptr; + assert(ev == ð_port->etp_sendev); + + pack= eth_port->etp_wr_pack; + eth_arrive(eth_port, pack, bf_bufsize(pack)); + eth_port->etp_wr_pack= NULL; + eth_restart_write(eth_port); +} + +PRIVATE int eth_checkopt (eth_fd) +eth_fd_t *eth_fd; +{ +/* bug: we don't check access modes yet */ + + unsigned long flags; + unsigned int en_di_flags; + eth_port_t *eth_port; + acc_t *pack; + + eth_port= eth_fd->ef_port; + flags= eth_fd->ef_ethopt.nweo_flags; + en_di_flags= (flags >>16) | (flags & 0xffff); + + if ((en_di_flags & NWEO_ACC_MASK) && + (en_di_flags & NWEO_LOC_MASK) && + (en_di_flags & NWEO_BROAD_MASK) && + (en_di_flags & NWEO_MULTI_MASK) && + (en_di_flags & NWEO_PROMISC_MASK) && + (en_di_flags & NWEO_REM_MASK) && + (en_di_flags & NWEO_TYPE_MASK) && + (en_di_flags & NWEO_RW_MASK)) + { + eth_fd->ef_flags |= EFF_OPTSET; + } + else + eth_fd->ef_flags &= ~EFF_OPTSET; + + while (eth_fd->ef_rdbuf_head != NULL) + { + pack= eth_fd->ef_rdbuf_head; + eth_fd->ef_rdbuf_head= pack->acc_ext_link; + bf_afree(pack); + } + + return NW_OK; +} + +PRIVATE void hash_fd(eth_fd) +eth_fd_t *eth_fd; +{ + eth_port_t *eth_port; + int hash; + + eth_port= eth_fd->ef_port; + if (eth_fd->ef_ethopt.nweo_flags & NWEO_TYPEANY) + { + eth_fd->ef_type_next= eth_port->etp_type_any; + eth_port->etp_type_any= eth_fd; + } + else + { + hash= eth_fd->ef_ethopt.nweo_type; + hash ^= (hash >> 8); + hash &= (ETH_TYPE_HASH_NR-1); + + eth_fd->ef_type_next= eth_port->etp_type[hash]; + eth_port->etp_type[hash]= eth_fd; + } +} + +PRIVATE void unhash_fd(eth_fd) +eth_fd_t *eth_fd; +{ + eth_port_t *eth_port; + eth_fd_t *prev, *curr, **eth_fd_p; + int hash; + + eth_port= eth_fd->ef_port; + if (eth_fd->ef_ethopt.nweo_flags & NWEO_TYPEANY) + { + eth_fd_p= ð_port->etp_type_any; + } + else + { + hash= eth_fd->ef_ethopt.nweo_type; + hash ^= (hash >> 8); + hash &= (ETH_TYPE_HASH_NR-1); + + eth_fd_p= ð_port->etp_type[hash]; + } + for (prev= NULL, curr= *eth_fd_p; curr; + prev= curr, curr= curr->ef_type_next) + { + if (curr == eth_fd) + break; + } + assert(curr); + if (prev) + prev->ef_type_next= curr->ef_type_next; + else + *eth_fd_p= curr->ef_type_next; +} + +PUBLIC void eth_restart_write(eth_port) +eth_port_t *eth_port; +{ + eth_fd_t *eth_fd; + int i, r; + + if (eth_port->etp_wr_pack) + return; + + if (!(eth_port->etp_flags & EPF_MORE2WRITE)) + return; + eth_port->etp_flags &= ~EPF_MORE2WRITE; + + for (i=0, eth_fd= eth_fd_table; i<ETH_FD_NR; i++, eth_fd++) + { + if ((eth_fd->ef_flags & (EFF_INUSE|EFF_WRITE_IP)) != + (EFF_INUSE|EFF_WRITE_IP)) + { + continue; + } + if (eth_fd->ef_port != eth_port) + continue; + + if (eth_port->etp_wr_pack) + { + eth_port->etp_flags |= EPF_MORE2WRITE; + return; + } + + eth_fd->ef_flags &= ~EFF_WRITE_IP; + r= eth_write(eth_fd-eth_fd_table, eth_fd->ef_write_count); + assert(r == NW_OK); + } +} + +PUBLIC void eth_arrive (eth_port, pack, pack_size) +eth_port_t *eth_port; +acc_t *pack; +size_t pack_size; +{ + + eth_hdr_t *eth_hdr; + ether_addr_t *dst_addr; + int pack_stat; + ether_type_t type; + eth_fd_t *eth_fd, *first_fd, *share_fd; + int hash, i; + time_t exp_time; + + exp_time= get_time() + EXPIRE_TIME; + + pack= bf_packIffLess(pack, ETH_HDR_SIZE); + + eth_hdr= (eth_hdr_t*)ptr2acc_data(pack); + dst_addr= ð_hdr->eh_dst; + + DIFBLOCK(0x20, dst_addr->ea_addr[0] != 0xFF && + (dst_addr->ea_addr[0] & 0x1), + printf("got multicast packet\n")); + + if (dst_addr->ea_addr[0] & 0x1) + { + /* multi cast or broadcast */ + if (eth_addrcmp(*dst_addr, broadcast) == 0) + pack_stat= NWEO_EN_BROAD; + else + pack_stat= NWEO_EN_MULTI; + } + else + { + if (eth_addrcmp (*dst_addr, eth_port->etp_ethaddr) == 0) + pack_stat= NWEO_EN_LOC; + else + pack_stat= NWEO_EN_PROMISC; + } + type= eth_hdr->eh_proto; + hash= type; + hash ^= (hash >> 8); + hash &= (ETH_TYPE_HASH_NR-1); + + first_fd= NULL; + for (i= 0; i<2; i++) + { + share_fd= NULL; + + eth_fd= (i == 0) ? eth_port->etp_type_any : + eth_port->etp_type[hash]; + for (; eth_fd; eth_fd= eth_fd->ef_type_next) + { + if (i && eth_fd->ef_ethopt.nweo_type != type) + continue; + if (!(eth_fd->ef_ethopt.nweo_flags & pack_stat)) + continue; + if (eth_fd->ef_ethopt.nweo_flags & NWEO_REMSPEC && + eth_addrcmp(eth_hdr->eh_src, + eth_fd->ef_ethopt.nweo_rem) != 0) + { + continue; + } + if ((eth_fd->ef_ethopt.nweo_flags & NWEO_ACC_MASK) == + NWEO_SHARED) + { + if (!share_fd) + { + share_fd= eth_fd; + continue; + } + if (!eth_fd->ef_rdbuf_head) + share_fd= eth_fd; + continue; + } + if (!first_fd) + { + first_fd= eth_fd; + continue; + } + pack->acc_linkC++; + packet2user(eth_fd, pack, exp_time); + } + if (share_fd) + { + pack->acc_linkC++; + packet2user(share_fd, pack, exp_time); + } + } + if (first_fd) + { + if (first_fd->ef_put_pkt && + (first_fd->ef_flags & EFF_READ_IP) && + !(first_fd->ef_ethopt.nweo_flags & NWEO_RWDATONLY)) + { + (*first_fd->ef_put_pkt)(first_fd->ef_srfd, pack, + pack_size); + } + else + packet2user(first_fd, pack, exp_time); + } + else + { + if (pack_stat == NWEO_EN_LOC) + { + DBLOCK(0x01, + printf("eth_arrive: dropping packet for proto 0x%x\n", + ntohs(type))); + } + else + { + DBLOCK(0x20, printf("dropping packet for proto 0x%x\n", + ntohs(type))); + } + bf_afree(pack); + } +} + +PRIVATE void packet2user (eth_fd, pack, exp_time) +eth_fd_t *eth_fd; +acc_t *pack; +time_t exp_time; +{ + int result; + acc_t *tmp_pack; + size_t size; + + assert (eth_fd->ef_flags & EFF_INUSE); + if (!(eth_fd->ef_flags & EFF_READ_IP)) + { + if (pack->acc_linkC != 1) + { + tmp_pack= bf_dupacc(pack); + bf_afree(pack); + pack= tmp_pack; + tmp_pack= NULL; + } + pack->acc_ext_link= NULL; + if (eth_fd->ef_rdbuf_head == NULL) + { + eth_fd->ef_rdbuf_head= pack; + eth_fd->ef_exp_time= exp_time; + } + else + eth_fd->ef_rdbuf_tail->acc_ext_link= pack; + eth_fd->ef_rdbuf_tail= pack; + return; + } + + if (eth_fd->ef_ethopt.nweo_flags & NWEO_RWDATONLY) + pack= bf_delhead(pack, ETH_HDR_SIZE); + + size= bf_bufsize(pack); + + if (eth_fd->ef_put_pkt) + { + (*eth_fd->ef_put_pkt)(eth_fd->ef_srfd, pack, size); + return; + } + + eth_fd->ef_flags &= ~EFF_READ_IP; + result= (*eth_fd->ef_put_userdata)(eth_fd->ef_srfd, (size_t)0, pack, + FALSE); + if (result >=0) + reply_thr_put(eth_fd, size, FALSE); + else + reply_thr_put(eth_fd, result, FALSE); +} + +PRIVATE void eth_buffree (priority) +int priority; +{ + int i; + eth_fd_t *eth_fd; + acc_t *pack; + + if (priority == ETH_PRI_FDBUFS_EXTRA) + { + for (i= 0, eth_fd= eth_fd_table; i<ETH_FD_NR; i++, eth_fd++) + { + while (eth_fd->ef_rdbuf_head && + eth_fd->ef_rdbuf_head->acc_ext_link) + { + pack= eth_fd->ef_rdbuf_head; + eth_fd->ef_rdbuf_head= pack->acc_ext_link; + bf_afree(pack); + } + } + } + if (priority == ETH_PRI_FDBUFS) + { + for (i= 0, eth_fd= eth_fd_table; i<ETH_FD_NR; i++, eth_fd++) + { + while (eth_fd->ef_rdbuf_head) + { + pack= eth_fd->ef_rdbuf_head; + eth_fd->ef_rdbuf_head= pack->acc_ext_link; + bf_afree(pack); + } + } + } +} + +#ifdef BUF_CONSISTENCY_CHECK +PRIVATE void eth_bufcheck() +{ + int i; + eth_fd_t *eth_fd; + acc_t *pack; + + for (i= 0; i<eth_conf_nr; i++) + { + bf_check_acc(eth_port_table[i].etp_rd_pack); + bf_check_acc(eth_port_table[i].etp_wr_pack); + } + for (i= 0, eth_fd= eth_fd_table; i<ETH_FD_NR; i++, eth_fd++) + { + for (pack= eth_fd->ef_rdbuf_head; pack; + pack= pack->acc_ext_link) + { + bf_check_acc(pack); + } + } +} +#endif + +PRIVATE u32_t compute_rec_conf(eth_port) +eth_port_t *eth_port; +{ + eth_fd_t *eth_fd; + u32_t flags; + int i; + + flags= NWEO_NOFLAGS; + for (i=0, eth_fd= eth_fd_table; i<ETH_FD_NR; i++, eth_fd++) + { + if ((eth_fd->ef_flags & (EFF_INUSE|EFF_OPTSET)) != + (EFF_INUSE|EFF_OPTSET)) + { + continue; + } + if (eth_fd->ef_port != eth_port) + continue; + flags |= eth_fd->ef_ethopt.nweo_flags; + } + return flags; +} + +PRIVATE void reply_thr_get (eth_fd, result, for_ioctl) +eth_fd_t *eth_fd; +size_t result; +int for_ioctl; +{ + acc_t *data; + + data= (*eth_fd->ef_get_userdata)(eth_fd->ef_srfd, result, 0, for_ioctl); + assert (!data); +} + +PRIVATE void reply_thr_put (eth_fd, result, for_ioctl) +eth_fd_t *eth_fd; +size_t result; +int for_ioctl; +{ + int error; + + error= (*eth_fd->ef_put_userdata)(eth_fd->ef_srfd, result, (acc_t *)0, + for_ioctl); + assert(error == NW_OK); +} + +/* + * $PchId: eth.c,v 1.11 1996/08/02 07:04:58 philip Exp $ + */ diff --git a/servers/inet/generic/eth.h b/servers/inet/generic/eth.h new file mode 100644 index 000000000..b084c3f98 --- /dev/null +++ b/servers/inet/generic/eth.h @@ -0,0 +1,38 @@ +/* +eth.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef ETH_H +#define ETH_H + +#define NWEO_DEFAULT (NWEO_EN_LOC | NWEO_DI_BROAD | NWEO_DI_MULTI | \ + NWEO_DI_PROMISC | NWEO_REMANY | NWEO_RWDATALL) + +#define eth_addrcmp(a,b) (memcmp((_VOIDSTAR)&a, (_VOIDSTAR)&b, \ + sizeof(a))) + +/* Forward declatations */ + +struct acc; + +/* prototypes */ + +void eth_prep ARGS(( void )); +void eth_init ARGS(( void )); +int eth_open ARGS(( int port, int srfd, + get_userdata_t get_userdata, put_userdata_t put_userdata, + put_pkt_t put_pkt )); +int eth_ioctl ARGS(( int fd, ioreq_t req)); +int eth_read ARGS(( int port, size_t count )); +int eth_write ARGS(( int port, size_t count )); +int eth_cancel ARGS(( int fd, int which_operation )); +void eth_close ARGS(( int fd )); +int eth_send ARGS(( int port, struct acc *data, size_t data_len )); + +#endif /* ETH_H */ + +/* + * $PchId: eth.h,v 1.6 1996/05/07 20:49:07 philip Exp $ + */ diff --git a/servers/inet/generic/eth_int.h b/servers/inet/generic/eth_int.h new file mode 100644 index 000000000..5b49c9255 --- /dev/null +++ b/servers/inet/generic/eth_int.h @@ -0,0 +1,52 @@ +/* +eth_int.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef ETH_INT_H +#define ETH_INT_H + +#define ETH_TYPE_HASH_NR 16 + +typedef struct eth_port +{ + int etp_flags; + ether_addr_t etp_ethaddr; + acc_t *etp_wr_pack, *etp_rd_pack; + struct eth_fd *etp_type_any; + struct eth_fd *etp_type[ETH_TYPE_HASH_NR]; + event_t etp_sendev; + + osdep_eth_port_t etp_osdep; +} eth_port_t; + +#define EPF_EMPTY 0x0 +#define EPF_ENABLED 0x1 +#define EPF_MORE2WRITE 0x10 +#define EPF_READ_IP 0x20 +#define EPF_READ_SP 0x40 + +#if 0 +#define EPS_EMPTY 0x0 +#define EPS_LOC 0x1 +#define EPS_BROAD 0x2 +#define EPS_MULTI 0x4 +#define EPS_PROMISC 0x8 +#endif + +extern eth_port_t *eth_port_table; + +void osdep_eth_init ARGS(( void )); +int eth_get_stat ARGS(( eth_port_t *eth_port, eth_stat_t *eth_stat )); +void eth_write_port ARGS(( eth_port_t *eth_port, acc_t *pack )); +void eth_arrive ARGS(( eth_port_t *port, acc_t *pack, size_t pack_size )); +void eth_set_rec_conf ARGS(( eth_port_t *eth_port, u32_t flags )); +void eth_restart_write ARGS(( eth_port_t *eth_port )); +void eth_loop_ev ARGS(( event_t *ev, ev_arg_t ev_arg )); + +#endif /* ETH_INT_H */ + +/* + * $PchId: eth_int.h,v 1.6 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/event.c b/servers/inet/generic/event.c new file mode 100644 index 000000000..ae0b2faf4 --- /dev/null +++ b/servers/inet/generic/event.c @@ -0,0 +1,69 @@ +/* +inet/generic/event.c + +Created: April 1995 by Philip Homburg <philip@cs.vu.nl> + +Implementation of an event queue. + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "assert.h" +#include "event.h" + +THIS_FILE + +event_t *ev_head; +static event_t *ev_tail; + +void ev_init(ev) +event_t *ev; +{ + ev->ev_func= 0; + ev->ev_next= NULL; +} + +void ev_enqueue(ev, func, ev_arg) +event_t *ev; +ev_func_t func; +ev_arg_t ev_arg; +{ + assert(ev->ev_func == 0); + ev->ev_func= func; + ev->ev_arg= ev_arg; + ev->ev_next= NULL; + if (ev_head == NULL) + ev_head= ev; + else + ev_tail->ev_next= ev; + ev_tail= ev; +} + +void ev_process() +{ + ev_func_t func; + event_t *curr; + + while (ev_head) + { + curr= ev_head; + ev_head= curr->ev_next; + func= curr->ev_func; + curr->ev_func= 0; + + assert(func != 0); + func(curr, curr->ev_arg); + } +} + +int ev_in_queue(ev) +event_t *ev; +{ + return ev->ev_func != 0; +} + + +/* + * $PchId: event.c,v 1.4 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/event.h b/servers/inet/generic/event.h new file mode 100644 index 000000000..1c50c962f --- /dev/null +++ b/servers/inet/generic/event.h @@ -0,0 +1,42 @@ +/* +inet/generic/event.h + +Created: April 1995 by Philip Homburg <philip@cs.vu.nl> + +Header file for an event mechanism. + +Copyright 1995 Philip Homburg +*/ + +#ifndef INET__GENERIC__EVENT_H +#define INET__GENERIC__EVENT_H + +struct event; + +typedef union ev_arg +{ + int ev_int; + void *ev_ptr; +} ev_arg_t; + +typedef void (*ev_func_t) ARGS(( struct event *ev, union ev_arg eva )); + +typedef struct event +{ + ev_func_t ev_func; + ev_arg_t ev_arg; + struct event *ev_next; +} event_t; + +extern event_t *ev_head; + +void ev_init ARGS(( event_t *ev )); +void ev_enqueue ARGS(( event_t *ev, ev_func_t func, ev_arg_t ev_arg )); +void ev_process ARGS(( void )); +int ev_in_queue ARGS(( event_t *ev )); + +#endif /* INET__GENERIC__EVENT_H */ + +/* + * $PchId: event.h,v 1.4 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/icmp.c b/servers/inet/generic/icmp.c new file mode 100644 index 000000000..b05b1aa95 --- /dev/null +++ b/servers/inet/generic/icmp.c @@ -0,0 +1,986 @@ +/* +icmp.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "buf.h" +#include "event.h" +#include "type.h" + +#include "assert.h" +#include "icmp.h" +#include "icmp_lib.h" +#include "io.h" +#include "ip.h" +#include "ip_int.h" +#include "ipr.h" + +THIS_FILE + +typedef struct icmp_port +{ + int icp_flags; + int icp_state; + int icp_ipport; + int icp_ipfd; + acc_t *icp_head_queue; + acc_t *icp_tail_queue; + acc_t *icp_write_pack; +} icmp_port_t; + +#define ICPF_EMPTY 0x0 +#define ICPF_SUSPEND 0x1 +#define ICPF_READ_IP 0x2 +#define ICPF_READ_SP 0x4 +#define ICPF_WRITE_IP 0x8 +#define ICPF_WRITE_SP 0x10 + +#define ICPS_BEGIN 0 +#define ICPS_IPOPT 1 +#define ICPS_MAIN 2 +#define ICPS_ERROR 3 + +PRIVATE icmp_port_t *icmp_port_table; + +FORWARD void icmp_main ARGS(( icmp_port_t *icmp_port )); +FORWARD acc_t *icmp_getdata ARGS(( int port, size_t offset, + size_t count, int for_ioctl )); +FORWARD int icmp_putdata ARGS(( int port, size_t offset, + acc_t *data, int for_ioctl )); +FORWARD void icmp_read ARGS(( icmp_port_t *icmp_port )); +FORWARD void process_data ARGS(( icmp_port_t *icmp_port, + acc_t *data )); +FORWARD u16_t icmp_pack_oneCsum ARGS(( acc_t *ip_pack )); +FORWARD void icmp_echo_request ARGS(( icmp_port_t *icmp_port, + acc_t *ip_pack, int ip_hdr_len, ip_hdr_t *ip_hdr, + acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr )); +FORWARD void icmp_dst_unreach ARGS(( icmp_port_t *icmp_port, + acc_t *ip_pack, int ip_hdr_len, ip_hdr_t *ip_hdr, + acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr )); +FORWARD void icmp_time_exceeded ARGS(( icmp_port_t *icmp_port, + acc_t *ip_pack, int ip_hdr_len, ip_hdr_t *ip_hdr, + acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr )); +FORWARD void icmp_router_advertisement ARGS(( icmp_port_t *icmp_port, + acc_t *icmp_pack, int icmp_len, icmp_hdr_t *icmp_hdr )); +FORWARD void icmp_redirect ARGS(( icmp_port_t *icmp_port, + ip_hdr_t *ip_hdr, acc_t *icmp_pack, int icmp_len, + icmp_hdr_t *icmp_hdr )); +FORWARD acc_t *make_repl_ip ARGS(( ip_hdr_t *ip_hdr, + int ip_len )); +FORWARD void enqueue_pack ARGS(( icmp_port_t *icmp_port, + acc_t *reply_ip_hdr )); +FORWARD void icmp_write ARGS(( icmp_port_t *icmp_port )); +FORWARD void icmp_buffree ARGS(( int priority )); +FORWARD acc_t *icmp_err_pack ARGS(( acc_t *pack, icmp_hdr_t **icmp_hdr )); +#ifdef BUF_CONSISTENCY_CHECK +FORWARD void icmp_bufcheck ARGS(( void )); +#endif + +PUBLIC void icmp_prep() +{ + icmp_port_table= alloc(ip_conf_nr * sizeof(icmp_port_table[0])); +} + +PUBLIC void icmp_init() +{ + int i; + icmp_port_t *icmp_port; + + assert (BUF_S >= sizeof (nwio_ipopt_t)); + + for (i= 0, icmp_port= icmp_port_table; i<ip_conf_nr; i++, icmp_port++) + { +#if ZERO + icmp_port->icp_flags= ICPF_EMPTY; + icmp_port->icp_state= ICPS_BEGIN; +#endif + icmp_port->icp_ipport= i; + } + +#ifndef BUF_CONSISTENCY_CHECK + bf_logon(icmp_buffree); +#else + bf_logon(icmp_buffree, icmp_bufcheck); +#endif + + for (i= 0, icmp_port= icmp_port_table; i<ip_conf_nr; i++, icmp_port++) + { + icmp_main (icmp_port); + } +} + +PRIVATE void icmp_main(icmp_port) +icmp_port_t *icmp_port; +{ + int result; + switch (icmp_port->icp_state) + { + case ICPS_BEGIN: + icmp_port->icp_head_queue= 0; + icmp_port->icp_ipfd= ip_open (icmp_port->icp_ipport, + icmp_port->icp_ipport, icmp_getdata, icmp_putdata, 0); + if (icmp_port->icp_ipfd<0) + { + DBLOCK(1, printf("unable to open ip_port %d\n", + icmp_port->icp_ipport)); + break; + } + icmp_port->icp_state= ICPS_IPOPT; + icmp_port->icp_flags &= ~ICPF_SUSPEND; + result= ip_ioctl (icmp_port->icp_ipfd, NWIOSIPOPT); + if (result == NW_SUSPEND) + { + icmp_port->icp_flags |= ICPF_SUSPEND; + break; + } + assert(result == NW_OK); + + /* falls through */ + case ICPS_IPOPT: + icmp_port->icp_state= ICPS_MAIN; + icmp_port->icp_flags &= ~ICPF_SUSPEND; + icmp_read(icmp_port); + break; + default: + DBLOCK(1, printf("unknown state %d\n", + icmp_port->icp_state)); + break; + } +} + +PRIVATE acc_t *icmp_getdata(port, offset, count, for_ioctl) +int port; +size_t offset, count; +int for_ioctl; +{ + icmp_port_t *icmp_port; + nwio_ipopt_t *ipopt; + acc_t *data; + int result; + + icmp_port= &icmp_port_table[port]; + + if (icmp_port->icp_flags & ICPF_WRITE_IP) + { + if (!count) + { + bf_afree(icmp_port->icp_write_pack); + icmp_port->icp_write_pack= 0; + + result= (int)offset; + if (result<0) + { + DBLOCK(1, printf("got write error %d\n", + result)); + } + if (icmp_port->icp_flags & ICPF_WRITE_SP) + { + icmp_port->icp_flags &= + ~(ICPF_WRITE_IP|ICPF_WRITE_SP); + icmp_write (icmp_port); + } + return NW_OK; + } + return bf_cut(icmp_port->icp_write_pack, offset, count); + } + switch (icmp_port->icp_state) + { + case ICPS_IPOPT: + if (!count) + { + result= (int)offset; + assert(result == NW_OK); + if (result < 0) + { + icmp_port->icp_state= ICPS_ERROR; + break; + } + if (icmp_port->icp_flags & ICPF_SUSPEND) + icmp_main(icmp_port); + return NW_OK; + } + +assert (count == sizeof (*ipopt)); + data= bf_memreq (sizeof (*ipopt)); +assert (data->acc_length == sizeof(*ipopt)); + ipopt= (nwio_ipopt_t *)ptr2acc_data(data); + ipopt->nwio_flags= NWIO_COPY | NWIO_EN_LOC | + NWIO_EN_BROAD | + NWIO_REMANY | NWIO_PROTOSPEC | + NWIO_HDR_O_ANY | NWIO_RWDATALL; + ipopt->nwio_proto= IPPROTO_ICMP; + return data; + default: + DBLOCK(1, printf("unknown state %d\n", + icmp_port->icp_state)); + return 0; + } +} + +PRIVATE int icmp_putdata(port, offset, data, for_ioctl) +int port; +size_t offset; +acc_t *data; +int for_ioctl; +{ + icmp_port_t *icmp_port; + int result; + + icmp_port= &icmp_port_table[port]; + + if (icmp_port->icp_flags & ICPF_READ_IP) + { +assert (!for_ioctl); + if (!data) + { + result= (int)offset; + if (result<0) + { + DBLOCK(1, printf("got read error %d\n", + result)); + } + if (icmp_port->icp_flags & ICPF_READ_SP) + { + icmp_port->icp_flags &= + ~(ICPF_READ_IP|ICPF_READ_SP); + icmp_read (icmp_port); + } + return NW_OK; + } + process_data(icmp_port, data); + return NW_OK; + } + switch (icmp_port->icp_state) + { + default: + DBLOCK(1, printf("unknown state %d\n", + icmp_port->icp_state)); + return 0; + } +} + +PRIVATE void icmp_read(icmp_port) +icmp_port_t *icmp_port; +{ + int result; + +assert (!(icmp_port->icp_flags & (ICPF_READ_IP|ICPF_READ_SP) || + (icmp_port->icp_flags & (ICPF_READ_IP|ICPF_READ_SP)) == + (ICPF_READ_IP|ICPF_READ_SP))); + + for (;;) + { + icmp_port->icp_flags |= ICPF_READ_IP; + icmp_port->icp_flags &= ~ICPF_READ_SP; + + result= ip_read(icmp_port->icp_ipfd, ICMP_MAX_DATAGRAM); + if (result == NW_SUSPEND) + { + icmp_port->icp_flags |= ICPF_READ_SP; + return; + } + } +} + +PUBLIC void icmp_snd_time_exceeded(port_nr, pack, code) +int port_nr; +acc_t *pack; +int code; +{ + acc_t *icmp_acc; + icmp_hdr_t *icmp_hdr; + icmp_port_t *icmp_port; + + assert(0 <= port_nr && port_nr < ip_conf_nr); + icmp_port= &icmp_port_table[port_nr]; + pack= icmp_err_pack(pack, &icmp_hdr); + if (pack == NULL) + return; + icmp_hdr->ih_type= ICMP_TYPE_TIME_EXCEEDED; + icmp_hdr->ih_code= code; + icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum, + (u16_t *)&icmp_hdr->ih_type, 2); + enqueue_pack(icmp_port, pack); +} + +PUBLIC void icmp_snd_redirect(port_nr, pack, code, gw) +int port_nr; +acc_t *pack; +int code; +ipaddr_t gw; +{ + acc_t *icmp_acc; + icmp_hdr_t *icmp_hdr; + icmp_port_t *icmp_port; + + assert(0 <= port_nr && port_nr < ip_conf_nr); + icmp_port= &icmp_port_table[port_nr]; + pack= icmp_err_pack(pack, &icmp_hdr); + if (pack == NULL) + return; + icmp_hdr->ih_type= ICMP_TYPE_REDIRECT; + icmp_hdr->ih_code= code; + icmp_hdr->ih_hun.ihh_gateway= gw; + icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum, + (u16_t *)&icmp_hdr->ih_type, 2); + icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum, + (u16_t *)&icmp_hdr->ih_hun.ihh_gateway, 4); + enqueue_pack(icmp_port, pack); +} + +PUBLIC void icmp_snd_unreachable(port_nr, pack, code) +int port_nr; +acc_t *pack; +int code; +{ + acc_t *icmp_acc; + icmp_hdr_t *icmp_hdr; + icmp_port_t *icmp_port; + + assert(0 <= port_nr && port_nr < ip_conf_nr); + icmp_port= &icmp_port_table[port_nr]; + pack= icmp_err_pack(pack, &icmp_hdr); + if (pack == NULL) + return; + icmp_hdr->ih_type= ICMP_TYPE_DST_UNRCH; + icmp_hdr->ih_code= code; + icmp_hdr->ih_chksum= ~oneC_sum(~icmp_hdr->ih_chksum, + (u16_t *)&icmp_hdr->ih_type, 2); + enqueue_pack(icmp_port, pack); +} + +PRIVATE void process_data(icmp_port, data) +icmp_port_t *icmp_port; +acc_t *data; +{ + ip_hdr_t *ip_hdr; + icmp_hdr_t *icmp_hdr; + acc_t *icmp_data; + int ip_hdr_len; + size_t pack_len; + + /* Align entire packet */ + data= bf_align(data, BUF_S, 4); + + data= bf_packIffLess(data, IP_MIN_HDR_SIZE); + ip_hdr= (ip_hdr_t *)ptr2acc_data(data); + DIFBLOCK(0x10, (ip_hdr->ih_dst & HTONL(0xf0000000)) == HTONL(0xe0000000), + printf("got multicast packet\n")); + ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; + + if (ip_hdr_len>IP_MIN_HDR_SIZE) + { + data= bf_packIffLess(data, ip_hdr_len); + ip_hdr= (ip_hdr_t *)ptr2acc_data(data); + } + + pack_len= bf_bufsize(data); + pack_len -= ip_hdr_len; + if (pack_len < ICMP_MIN_HDR_LEN) + { + DBLOCK(1, printf("got an incomplete icmp packet\n")); + bf_afree(data); + return; + } + + icmp_data= bf_cut(data, ip_hdr_len, pack_len); + + icmp_data= bf_packIffLess (icmp_data, ICMP_MIN_HDR_LEN); + icmp_hdr= (icmp_hdr_t *)ptr2acc_data(icmp_data); + + if ((u16_t)~icmp_pack_oneCsum(icmp_data)) + { + DBLOCK(1, printf( + "got packet with bad checksum (= 0x%x, 0x%x)\n", + icmp_hdr->ih_chksum, + (u16_t)~icmp_pack_oneCsum(icmp_data))); + bf_afree(data); + bf_afree(icmp_data); + return; + } + + switch (icmp_hdr->ih_type) + { + case ICMP_TYPE_ECHO_REPL: + break; + case ICMP_TYPE_DST_UNRCH: + icmp_dst_unreach (icmp_port, data, ip_hdr_len, ip_hdr, + icmp_data, pack_len, icmp_hdr); + break; + case ICMP_TYPE_SRC_QUENCH: + /* Ignore src quench ICMPs */ + DBLOCK(2, printf("ignoring SRC QUENCH ICMP.\n")); + break; + case ICMP_TYPE_REDIRECT: + icmp_redirect (icmp_port, ip_hdr, icmp_data, pack_len, + icmp_hdr); + break; + case ICMP_TYPE_ECHO_REQ: + icmp_echo_request(icmp_port, data, ip_hdr_len, ip_hdr, + icmp_data, pack_len, icmp_hdr); + return; + case ICMP_TYPE_ROUTER_ADVER: + icmp_router_advertisement(icmp_port, icmp_data, pack_len, + icmp_hdr); + break; + case ICMP_TYPE_ROUTE_SOL: + break; /* Should be handled by a routing deamon. */ + case ICMP_TYPE_TIME_EXCEEDED: + icmp_time_exceeded (icmp_port, data, ip_hdr_len, ip_hdr, + icmp_data, pack_len, icmp_hdr); + break; + default: + DBLOCK(1, printf("got an unknown icmp (%d) from ", + icmp_hdr->ih_type); + writeIpAddr(ip_hdr->ih_src); printf("\n")); + break; + } + bf_afree(data); + bf_afree(icmp_data); +} + +PRIVATE void icmp_echo_request(icmp_port, ip_data, ip_len, ip_hdr, + icmp_data, icmp_len, icmp_hdr) +icmp_port_t *icmp_port; +acc_t *ip_data, *icmp_data; +int ip_len, icmp_len; +ip_hdr_t *ip_hdr; +icmp_hdr_t *icmp_hdr; +{ + acc_t *repl_ip_hdr, *repl_icmp; + icmp_hdr_t *repl_icmp_hdr; + i32_t tmp_chksum; + u16_t u16; + + if (icmp_hdr->ih_code != 0) + { + DBLOCK(1, + printf("got an icmp echo request with unknown code (%d)\n", + icmp_hdr->ih_code)); + bf_afree(ip_data); + bf_afree(icmp_data); + return; + } + if (icmp_len < ICMP_MIN_HDR_LEN + sizeof(icmp_id_seq_t)) + { + DBLOCK(1, printf("got an incomplete icmp echo request\n")); + bf_afree(ip_data); + bf_afree(icmp_data); + return; + } + repl_ip_hdr= make_repl_ip(ip_hdr, ip_len); + repl_icmp= bf_memreq (ICMP_MIN_HDR_LEN); +assert (repl_icmp->acc_length == ICMP_MIN_HDR_LEN); + repl_icmp_hdr= (icmp_hdr_t *)ptr2acc_data(repl_icmp); + repl_icmp_hdr->ih_type= ICMP_TYPE_ECHO_REPL; + repl_icmp_hdr->ih_code= 0; + + DBLOCK(2, + printf("ih_chksum= 0x%x, ih_type= 0x%x, repl->ih_type= 0x%x\n", + icmp_hdr->ih_chksum, *(u16_t *)&icmp_hdr->ih_type, + *(u16_t *)&repl_icmp_hdr->ih_type)); + tmp_chksum= (~icmp_hdr->ih_chksum & 0xffff) - + (i32_t)*(u16_t *)&icmp_hdr->ih_type+ + *(u16_t *)&repl_icmp_hdr->ih_type; + tmp_chksum= (tmp_chksum >> 16) + (tmp_chksum & 0xffff); + tmp_chksum= (tmp_chksum >> 16) + (tmp_chksum & 0xffff); + repl_icmp_hdr->ih_chksum= ~tmp_chksum; + DBLOCK(2, printf("sending chksum 0x%x\n", repl_icmp_hdr->ih_chksum)); + + repl_ip_hdr->acc_next= repl_icmp; + repl_icmp->acc_next= bf_cut (icmp_data, ICMP_MIN_HDR_LEN, + icmp_len - ICMP_MIN_HDR_LEN); + + bf_afree(ip_data); + bf_afree(icmp_data); + + enqueue_pack(icmp_port, repl_ip_hdr); +} + +PRIVATE u16_t icmp_pack_oneCsum(icmp_pack) +acc_t *icmp_pack; +{ + u16_t prev; + int odd_byte; + char *data_ptr; + int length; + char byte_buf[2]; + + assert (icmp_pack); + + prev= 0; + + odd_byte= FALSE; + for (; icmp_pack; icmp_pack= icmp_pack->acc_next) + { + data_ptr= ptr2acc_data(icmp_pack); + length= icmp_pack->acc_length; + + if (!length) + continue; + if (odd_byte) + { + byte_buf[1]= *data_ptr; + prev= oneC_sum(prev, (u16_t *)byte_buf, 2); + data_ptr++; + length--; + odd_byte= FALSE; + } + if (length & 1) + { + odd_byte= TRUE; + length--; + byte_buf[0]= data_ptr[length]; + } + if (!length) + continue; + prev= oneC_sum (prev, (u16_t *)data_ptr, length); + } + if (odd_byte) + prev= oneC_sum (prev, (u16_t *)byte_buf, 1); + return prev; +} + +PRIVATE acc_t *make_repl_ip(ip_hdr, ip_len) +ip_hdr_t *ip_hdr; +int ip_len; +{ + ip_hdr_t *repl_ip_hdr; + acc_t *repl; + int repl_hdr_len; + + if (ip_len>IP_MIN_HDR_SIZE) + { + DBLOCK(1, printf("ip_hdr options NOT supported (yet?)\n")); + ip_len= IP_MIN_HDR_SIZE; + } + + repl_hdr_len= IP_MIN_HDR_SIZE; + + repl= bf_memreq(repl_hdr_len); +assert (repl->acc_length == repl_hdr_len); + + repl_ip_hdr= (ip_hdr_t *)ptr2acc_data(repl); + + repl_ip_hdr->ih_vers_ihl= repl_hdr_len >> 2; + repl_ip_hdr->ih_tos= ip_hdr->ih_tos; + repl_ip_hdr->ih_ttl= ICMP_DEF_TTL; + repl_ip_hdr->ih_proto= IPPROTO_ICMP; + repl_ip_hdr->ih_dst= ip_hdr->ih_src; + repl_ip_hdr->ih_flags_fragoff= 0; + + return repl; +} + +PRIVATE void enqueue_pack(icmp_port, reply_ip_hdr) +icmp_port_t *icmp_port; +acc_t *reply_ip_hdr; +{ + reply_ip_hdr->acc_ext_link= 0; + + if (icmp_port->icp_head_queue) + { + icmp_port->icp_tail_queue->acc_ext_link= + reply_ip_hdr; + } + else + { + icmp_port->icp_head_queue= reply_ip_hdr; + } + reply_ip_hdr->acc_ext_link= NULL; + icmp_port->icp_tail_queue= reply_ip_hdr; + + if (!(icmp_port->icp_flags & ICPF_WRITE_IP)) + icmp_write(icmp_port); +} + +PRIVATE void icmp_write(icmp_port) +icmp_port_t *icmp_port; +{ + int result; + +assert (!(icmp_port->icp_flags & ICPF_WRITE_IP)); + + while (icmp_port->icp_head_queue != NULL) + { + assert(icmp_port->icp_write_pack == NULL); + icmp_port->icp_write_pack= icmp_port->icp_head_queue; + icmp_port->icp_head_queue= icmp_port->icp_head_queue-> + acc_ext_link; + + icmp_port->icp_flags |= ICPF_WRITE_IP; + + result= ip_write(icmp_port->icp_ipfd, + bf_bufsize(icmp_port->icp_write_pack)); + if (result == NW_SUSPEND) + { + icmp_port->icp_flags |= ICPF_WRITE_SP; + return; + } + icmp_port->icp_flags &= ~ICPF_WRITE_IP; + } +} + +PRIVATE void icmp_buffree(priority) +int priority; +{ + acc_t *tmp_acc; + int i; + icmp_port_t *icmp_port; + + if (priority == ICMP_PRI_QUEUE) + { + for (i=0, icmp_port= icmp_port_table; i<ip_conf_nr; + i++, icmp_port++) + { + while(icmp_port->icp_head_queue) + { + tmp_acc= icmp_port->icp_head_queue; + icmp_port->icp_head_queue= + tmp_acc->acc_ext_link; + bf_afree(tmp_acc); + } + } + } +} + +#ifdef BUF_CONSISTENCY_CHECK +PRIVATE void icmp_bufcheck() +{ + int i; + icmp_port_t *icmp_port; + acc_t *pack; + + for (i= 0, icmp_port= icmp_port_table; i<ip_conf_nr; i++, icmp_port++) + { + for (pack= icmp_port->icp_head_queue; pack; + pack= pack->acc_ext_link) + { + bf_check_acc(pack); + } + bf_check_acc(icmp_port->icp_write_pack); + } +} +#endif + +PRIVATE void icmp_dst_unreach(icmp_port, ip_pack, ip_hdr_len, ip_hdr, icmp_pack, + icmp_len, icmp_hdr) +icmp_port_t *icmp_port; +acc_t *ip_pack; +int ip_hdr_len; +ip_hdr_t *ip_hdr; +acc_t *icmp_pack; +int icmp_len; +icmp_hdr_t *icmp_hdr; +{ + acc_t *old_ip_pack; + ip_hdr_t *old_ip_hdr; + int ip_port_nr; + ipaddr_t dst, mask; + + if (icmp_len < 8 + IP_MIN_HDR_SIZE) + { + DBLOCK(1, printf("dest unrch with wrong size\n")); + return; + } + old_ip_pack= bf_cut (icmp_pack, 8, icmp_len-8); + old_ip_pack= bf_packIffLess(old_ip_pack, IP_MIN_HDR_SIZE); + old_ip_hdr= (ip_hdr_t *)ptr2acc_data(old_ip_pack); + + if (old_ip_hdr->ih_src != ip_hdr->ih_dst) + { + DBLOCK(1, printf("dest unrch based on wrong packet\n")); + bf_afree(old_ip_pack); + return; + } + + ip_port_nr= icmp_port->icp_ipport; + + switch(icmp_hdr->ih_code) + { + case ICMP_NET_UNRCH: + dst= old_ip_hdr->ih_dst; + mask= ip_get_netmask(dst); + ipr_destunrch (ip_port_nr, dst & mask, mask, + IPR_UNRCH_TIMEOUT); + break; + case ICMP_HOST_UNRCH: + ipr_destunrch (ip_port_nr, old_ip_hdr->ih_dst, (ipaddr_t)-1, + IPR_UNRCH_TIMEOUT); + break; + case ICMP_PORT_UNRCH: + /* At the moment we don't do anything with this information. + * It should be handed to the appropriate transport layer. + */ + break; + default: + DBLOCK(1, printf("icmp_dst_unreach: got strange code %d from ", + icmp_hdr->ih_code); + writeIpAddr(ip_hdr->ih_src); + printf("; original destination: "); + writeIpAddr(old_ip_hdr->ih_dst); + printf("; protocol: %d\n", + old_ip_hdr->ih_proto)); + break; + } + bf_afree(old_ip_pack); +} + +PRIVATE void icmp_time_exceeded(icmp_port, ip_pack, ip_hdr_len, ip_hdr, + icmp_pack, icmp_len, icmp_hdr) +icmp_port_t *icmp_port; +acc_t *ip_pack; +int ip_hdr_len; +ip_hdr_t *ip_hdr; +acc_t *icmp_pack; +int icmp_len; +icmp_hdr_t *icmp_hdr; +{ + acc_t *old_ip_pack; + ip_hdr_t *old_ip_hdr; + int ip_port_nr; + + if (icmp_len < 8 + IP_MIN_HDR_SIZE) + { + DBLOCK(1, printf("time exceeded with wrong size\n")); + return; + } + old_ip_pack= bf_cut (icmp_pack, 8, icmp_len-8); + old_ip_pack= bf_packIffLess(old_ip_pack, IP_MIN_HDR_SIZE); + old_ip_hdr= (ip_hdr_t *)ptr2acc_data(old_ip_pack); + + if (old_ip_hdr->ih_src != ip_hdr->ih_dst) + { + DBLOCK(1, printf("time exceeded based on wrong packet\n")); + bf_afree(old_ip_pack); + return; + } + + ip_port_nr= icmp_port->icp_ipport; + + switch(icmp_hdr->ih_code) + { + case ICMP_TTL_EXC: + ipr_ttl_exc (ip_port_nr, old_ip_hdr->ih_dst, (ipaddr_t)-1, + IPR_TTL_TIMEOUT); + break; + case ICMP_FRAG_REASSEM: + /* Ignore reassembly time-outs. */ + break; + default: + DBLOCK(1, printf("got strange code: %d\n", + icmp_hdr->ih_code)); + break; + } + bf_afree(old_ip_pack); +} + +PRIVATE void icmp_router_advertisement(icmp_port, icmp_pack, icmp_len, icmp_hdr) +icmp_port_t *icmp_port; +acc_t *icmp_pack; +int icmp_len; +icmp_hdr_t *icmp_hdr; +{ + int entries; + int entry_size; + u16_t lifetime; + int i; + char *bufp; + + if (icmp_len < 8) + { + DBLOCK(1, + printf("router advertisement with wrong size (%d)\n", + icmp_len)); + return; + } + if (icmp_hdr->ih_code != 0) + { + DBLOCK(1, + printf("router advertisement with wrong code (%d)\n", + icmp_hdr->ih_code)); + return; + } + entries= icmp_hdr->ih_hun.ihh_ram.iram_na; + entry_size= icmp_hdr->ih_hun.ihh_ram.iram_aes * 4; + if (entries < 1) + { + DBLOCK(1, printf( + "router advertisement with wrong number of entries (%d)\n", + entries)); + return; + } + if (entry_size < 8) + { + DBLOCK(1, printf( + "router advertisement with wrong entry size (%d)\n", + entry_size)); + return; + } + if (icmp_len < 8 + entries * entry_size) + { + DBLOCK(1, + printf("router advertisement with wrong size\n"); + printf( + "\t(entries= %d, entry_size= %d, icmp_len= %d)\n", + entries, entry_size, icmp_len)); + return; + } + lifetime= ntohs(icmp_hdr->ih_hun.ihh_ram.iram_lt); + if (lifetime > 9000) + { + DBLOCK(1, printf( + "router advertisement with wrong lifetime (%d)\n", + lifetime)); + return; + } + for (i= 0, bufp= (char *)&icmp_hdr->ih_dun.uhd_data[0]; i< entries; i++, + bufp += entry_size) + { + ipr_add_oroute(icmp_port->icp_ipport, HTONL(0L), HTONL(0L), + *(ipaddr_t *)bufp, lifetime * HZ, 1, 0, + ntohl(*(i32_t *)(bufp+4)), NULL); + } +} + +PRIVATE void icmp_redirect(icmp_port, ip_hdr, icmp_pack, icmp_len, icmp_hdr) +icmp_port_t *icmp_port; +ip_hdr_t *ip_hdr; +acc_t *icmp_pack; +int icmp_len; +icmp_hdr_t *icmp_hdr; +{ + acc_t *old_ip_pack; + ip_hdr_t *old_ip_hdr; + int ip_port_nr; + ipaddr_t dst, mask; + + if (icmp_len < 8 + IP_MIN_HDR_SIZE) + { + DBLOCK(1, printf("redirect with wrong size\n")); + return; + } + old_ip_pack= bf_cut (icmp_pack, 8, icmp_len-8); + old_ip_pack= bf_packIffLess(old_ip_pack, IP_MIN_HDR_SIZE); + old_ip_hdr= (ip_hdr_t *)ptr2acc_data(old_ip_pack); + + ip_port_nr= icmp_port->icp_ipport; + + switch(icmp_hdr->ih_code) + { + case ICMP_REDIRECT_NET: + dst= old_ip_hdr->ih_dst; + mask= ip_get_netmask(dst); + ipr_redirect (ip_port_nr, dst & mask, mask, + ip_hdr->ih_src, icmp_hdr->ih_hun.ihh_gateway, + IPR_REDIRECT_TIMEOUT); + break; + case ICMP_REDIRECT_HOST: + ipr_redirect (ip_port_nr, old_ip_hdr->ih_dst, (ipaddr_t)-1, + ip_hdr->ih_src, icmp_hdr->ih_hun.ihh_gateway, + IPR_REDIRECT_TIMEOUT); + break; + default: + DBLOCK(1, printf("got strange code: %d\n", + icmp_hdr->ih_code)); + break; + } + bf_afree(old_ip_pack); +} + +PRIVATE acc_t *icmp_err_pack(pack, icmp_hdr) +acc_t *pack; +icmp_hdr_t **icmp_hdr; +{ + ip_hdr_t *ip_hdr; + acc_t *ip_pack, *icmp_pack, *tmp_pack; + int ip_hdr_len, icmp_hdr_len; + size_t size; + ipaddr_t dest, netmask; + nettype_t nettype; + + pack= bf_packIffLess(pack, IP_MIN_HDR_SIZE); + ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); + + /* If the IP protocol is ICMP or the fragment offset is non-zero, + * drop the packet. Also check if the source address is valid. + */ + if (ip_hdr->ih_proto == IPPROTO_ICMP || + (ntohs(ip_hdr->ih_flags_fragoff) & IH_FRAGOFF_MASK) != 0) + { + bf_afree(pack); + return NULL; + } + dest= ip_hdr->ih_src; + nettype= ip_nettype(dest); + netmask= ip_netmask(nettype); + if ((nettype != IPNT_CLASS_A && nettype != IPNT_LOCAL && + nettype != IPNT_CLASS_B && nettype != IPNT_CLASS_C) || + (dest & ~netmask) == 0 || (dest & ~netmask) == ~netmask) + { +#if !CRAMPED + printf("icmp_err_pack: invalid source address: "); + writeIpAddr(dest); + printf("\n"); +#endif + bf_afree(pack); + return NULL; + } + + /* Take the IP header and the first 64 bits of user data. */ + size= ntohs(ip_hdr->ih_length); + ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; + if (size < ip_hdr_len || bf_bufsize(pack) < size) + { +#if !CRAMPED + printf("icmp_err_pack: wrong packet size:\n"); + printf("\thdrlen= %d, ih_length= %d, bufsize= %d\n", + ip_hdr_len, size, bf_bufsize(pack)); +#endif + bf_afree(pack); + return NULL; + } + if (ip_hdr_len + 8 < size) + size= ip_hdr_len+8; + tmp_pack= bf_cut(pack, 0, size); + bf_afree(pack); + pack= tmp_pack; + tmp_pack= NULL; + + /* Create a minimal size ICMP hdr. */ + icmp_hdr_len= offsetof(icmp_hdr_t, ih_dun); + icmp_pack= bf_memreq(icmp_hdr_len); + pack= bf_append(icmp_pack, pack); + size += icmp_hdr_len; + pack= bf_packIffLess(pack, icmp_hdr_len); + *icmp_hdr= (icmp_hdr_t *)ptr2acc_data(pack); + (*icmp_hdr)->ih_type= 0; + (*icmp_hdr)->ih_code= 0; + (*icmp_hdr)->ih_chksum= 0; + (*icmp_hdr)->ih_hun.ihh_unused= 0; + (*icmp_hdr)->ih_chksum= ~icmp_pack_oneCsum(pack); + + /* Create an IP header */ + ip_hdr_len= IP_MIN_HDR_SIZE; + + ip_pack= bf_memreq(ip_hdr_len); + ip_hdr= (ip_hdr_t *)ptr2acc_data(ip_pack); + + ip_hdr->ih_vers_ihl= ip_hdr_len >> 2; + ip_hdr->ih_tos= 0; + ip_hdr->ih_length= htons(ip_hdr_len + size); + ip_hdr->ih_flags_fragoff= 0; + ip_hdr->ih_ttl= ICMP_DEF_TTL; + ip_hdr->ih_proto= IPPROTO_ICMP; + ip_hdr->ih_dst= dest; + + assert(ip_pack->acc_next == NULL); + ip_pack->acc_next= pack; + return ip_pack; +} + +/* + * $PchId: icmp.c,v 1.8 1996/12/17 07:53:34 philip Exp $ + */ diff --git a/servers/inet/generic/icmp.h b/servers/inet/generic/icmp.h new file mode 100644 index 000000000..7bd6b0b6b --- /dev/null +++ b/servers/inet/generic/icmp.h @@ -0,0 +1,23 @@ +/* +icmp.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef ICMP_H +#define ICMP_H + +#define ICMP_MAX_DATAGRAM 8196 +#define ICMP_DEF_TTL 60 + +/* Prototypes */ + +void icmp_prep ARGS(( void )); +void icmp_init ARGS(( void )); + + +#endif /* ICMP_H */ + +/* + * $PchId: icmp.h,v 1.4 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/icmp_lib.h b/servers/inet/generic/icmp_lib.h new file mode 100644 index 000000000..9174f4cb2 --- /dev/null +++ b/servers/inet/generic/icmp_lib.h @@ -0,0 +1,24 @@ +/* +icmp_lib.h + +Created Sept 30, 1991 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +#ifndef ICMP_LIB_H +#define ICMP_LIB_H + +/* Prototypes */ + +void icmp_snd_parmproblem ARGS(( acc_t *pack )); +void icmp_snd_time_exceeded ARGS(( int port_nr, acc_t *pack, int code )); +void icmp_snd_unreachable ARGS(( int port_nr, acc_t *pack, int code )); +void icmp_snd_redirect ARGS(( int port_nr, acc_t *pack, int code, + ipaddr_t gw )); + +#endif /* ICMP_LIB_H */ + +/* + * $PchId: icmp_lib.h,v 1.5 1996/12/17 07:54:09 philip Exp $ + */ diff --git a/servers/inet/generic/io.c b/servers/inet/generic/io.c new file mode 100644 index 000000000..df641b3ed --- /dev/null +++ b/servers/inet/generic/io.c @@ -0,0 +1,34 @@ +/* +io.c + +Copyright 1995 Philip Homburg +*/ + +#include <stdlib.h> + +#include "inet.h" +#include "io.h" + +PUBLIC void writeIpAddr(addr) +ipaddr_t addr; +{ +#define addrInBytes ((u8_t *)&addr) + + printf("%d.%d.%d.%d", addrInBytes[0], addrInBytes[1], + addrInBytes[2], addrInBytes[3]); +#undef addrInBytes +} + +PUBLIC void writeEtherAddr(addr) +ether_addr_t *addr; +{ +#define addrInBytes ((u8_t *)addr->ea_addr) + + printf("%x:%x:%x:%x:%x:%x", addrInBytes[0], addrInBytes[1], + addrInBytes[2], addrInBytes[3], addrInBytes[4], addrInBytes[5]); +#undef addrInBytes +} + +/* + * $PchId: io.c,v 1.5 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/io.h b/servers/inet/generic/io.h new file mode 100644 index 000000000..43d98ff5c --- /dev/null +++ b/servers/inet/generic/io.h @@ -0,0 +1,21 @@ +/* +io.h + +Created Sept 30, 1991 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +#ifndef IO_H +#define IO_H + +/* Prototypes */ + +void writeIpAddr ARGS(( ipaddr_t addr )); +void writeEtherAddr ARGS(( ether_addr_t *addr )); + +#endif /* IO_H */ + +/* + * $PchId: io.h,v 1.4 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/ip.c b/servers/inet/generic/ip.c new file mode 100644 index 000000000..f04bac6ec --- /dev/null +++ b/servers/inet/generic/ip.c @@ -0,0 +1,440 @@ +/* +ip.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "buf.h" +#include "event.h" +#include "type.h" + +#include "arp.h" +#include "assert.h" +#include "clock.h" +#include "eth.h" +#include "icmp.h" +#include "icmp_lib.h" +#include "io.h" +#include "ip.h" +#include "ip_int.h" +#include "ipr.h" +#include "sr.h" + +THIS_FILE + +FORWARD void ip_close ARGS(( int fd )); +FORWARD int ip_cancel ARGS(( int fd, int which_operation )); + +FORWARD void ip_buffree ARGS(( int priority )); +#ifdef BUF_CONSISTENCY_CHECK +FORWARD void ip_bufcheck ARGS(( void )); +#endif +FORWARD void ip_bad_callback ARGS(( struct ip_port *ip_port )); + +PUBLIC ip_port_t *ip_port_table; +PUBLIC ip_fd_t ip_fd_table[IP_FD_NR]; +PUBLIC ip_ass_t ip_ass_table[IP_ASS_NR]; + +PUBLIC void ip_prep() +{ + ip_port_table= alloc(ip_conf_nr * sizeof(ip_port_table[0])); + icmp_prep(); +} + +PUBLIC void ip_init() +{ + int i, j, result; + ip_ass_t *ip_ass; + ip_fd_t *ip_fd; + ip_port_t *ip_port; + struct ip_conf *icp; + + assert (BUF_S >= sizeof(struct nwio_ethopt)); + assert (BUF_S >= IP_MAX_HDR_SIZE + ETH_HDR_SIZE); + assert (BUF_S >= sizeof(nwio_ipopt_t)); + assert (BUF_S >= sizeof(nwio_route_t)); + +#if ZERO + for (i=0, ip_ass= ip_ass_table; i<IP_ASS_NR; i++, ip_ass++) + { + ip_ass->ia_frags= 0; + ip_ass->ia_first_time= 0; + ip_ass->ia_port= 0; + } + + for (i=0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++) + { + ip_fd->if_flags= IFF_EMPTY; + ip_fd->if_rdbuf_head= 0; + } +#endif + + for (i=0, ip_port= ip_port_table, icp= ip_conf; + i<ip_conf_nr; i++, ip_port++, icp++) + { + ip_port->ip_port= i; +#if ZERO + ip_port->ip_flags= IPF_EMPTY; +#endif + ip_port->ip_dev_main= (ip_dev_t)ip_bad_callback; + ip_port->ip_dev_set_ipaddr= (ip_dev_t)ip_bad_callback; + ip_port->ip_dev_send= (ip_dev_send_t)ip_bad_callback; + ip_port->ip_dl_type= icp->ic_devtype; + ip_port->ip_mss= IP_DEF_MSS; + + switch(ip_port->ip_dl_type) + { + case IPDL_ETH: + ip_port->ip_dl.dl_eth.de_port= icp->ic_port; + result= ipeth_init(ip_port); + if (result == -1) + continue; + assert(result == NW_OK); + break; +#if ENABLE_PSIP + case IPDL_PSIP: + ip_port->ip_dl.dl_ps.ps_port= icp->ic_port; + result= ipps_init(ip_port); + if (result == -1) + continue; + assert(result == NW_OK); + break; +#endif +#if !CRAMPED + default: + ip_panic(( "unknown ip_dl_type %d", + ip_port->ip_dl_type )); +#endif + } +#if ZERO + ip_port->ip_loopb_head= NULL; + ip_port->ip_loopb_tail= NULL; + ev_init(&ip_port->ip_loopb_event); +#endif + ip_port->ip_flags |= IPF_CONFIGURED; +#if ZERO + ip_port->ip_proto_any= NULL; + for (j= 0; j<IP_PROTO_HASH_NR; j++) + ip_port->ip_proto[j]= NULL; +#endif + } + +#ifndef BUF_CONSISTENCY_CHECK + bf_logon(ip_buffree); +#else + bf_logon(ip_buffree, ip_bufcheck); +#endif + + icmp_init(); + ipr_init(); + + for (i=0, ip_port= ip_port_table; i<ip_conf_nr; i++, ip_port++) + { + if (!(ip_port->ip_flags & IPF_CONFIGURED)) + continue; + ip_port->ip_frame_id= (u16_t)get_time(); + + sr_add_minor(if2minor(ip_conf[i].ic_ifno, IP_DEV_OFF), + i, ip_open, ip_close, ip_read, + ip_write, ip_ioctl, ip_cancel); + + (*ip_port->ip_dev_main)(ip_port); + } +} + +PRIVATE int ip_cancel (fd, which_operation) +int fd; +int which_operation; +{ + ip_fd_t *ip_fd; + acc_t *repl_res; + int result; + + ip_fd= &ip_fd_table[fd]; + + switch (which_operation) + { + case SR_CANCEL_IOCTL: + assert (ip_fd->if_flags & IFF_GIPCONF_IP); + ip_fd->if_flags &= ~IFF_GIPCONF_IP; + repl_res= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, + (size_t)EINTR, (size_t)0, TRUE); + assert (!repl_res); + break; + case SR_CANCEL_READ: + assert (ip_fd->if_flags & IFF_READ_IP); + ip_fd->if_flags &= ~IFF_READ_IP; + result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, + (size_t)EINTR, (acc_t *)0, FALSE); + assert (!result); + break; +#if 0 + case SR_CANCEL_WRITE: + assert(0); + assert (ip_fd->if_flags & IFF_WRITE_MASK); + ip_fd->if_flags &= ~IFF_WRITE_MASK; + repl_res= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, + (size_t)EINTR, (size_t)0, FALSE); + assert (!repl_res); + break; +#endif +#if !CRAMPED + default: + ip_panic(( "unknown cancel request" )); +#endif + } + return NW_OK; +} + + +PUBLIC int ip_open (port, srfd, get_userdata, put_userdata, put_pkt) +int port; +int srfd; +get_userdata_t get_userdata; +put_userdata_t put_userdata; +put_pkt_t put_pkt; +{ + int i; + ip_fd_t *ip_fd; + ip_port_t *ip_port; + + ip_port= &ip_port_table[port]; + if (!(ip_port->ip_flags & IPF_CONFIGURED)) + return ENXIO; + + for (i=0; i<IP_FD_NR && (ip_fd_table[i].if_flags & IFF_INUSE); + i++); + + if (i>=IP_FD_NR) + { + DBLOCK(1, printf("out of fds\n")); + return EAGAIN; + } + + ip_fd= &ip_fd_table[i]; + + ip_fd->if_flags= IFF_INUSE; + + ip_fd->if_ipopt.nwio_flags= NWIO_DEFAULT; + ip_fd->if_ipopt.nwio_tos= 0; + ip_fd->if_ipopt.nwio_df= FALSE; + ip_fd->if_ipopt.nwio_ttl= 255; + ip_fd->if_ipopt.nwio_hdropt.iho_opt_siz= 0; + + ip_fd->if_port= ip_port; + ip_fd->if_srfd= srfd; + assert(ip_fd->if_rdbuf_head == NULL); + ip_fd->if_get_userdata= get_userdata; + ip_fd->if_put_userdata= put_userdata; + ip_fd->if_put_pkt= put_pkt; + return i; +} + +PRIVATE void ip_close (fd) +int fd; +{ + ip_fd_t *ip_fd; + acc_t *pack; + + ip_fd= &ip_fd_table[fd]; + + assert ((ip_fd->if_flags & IFF_INUSE) && + !(ip_fd->if_flags & IFF_BUSY)); + + if (ip_fd->if_flags & IFF_OPTSET) + ip_unhash_proto(ip_fd); + while (ip_fd->if_rdbuf_head) + { + pack= ip_fd->if_rdbuf_head; + ip_fd->if_rdbuf_head= pack->acc_ext_link; + bf_afree(pack); + } + ip_fd->if_flags= IFF_EMPTY; +} + +PRIVATE void ip_buffree(priority) +int priority; +{ + int i; + ip_port_t *ip_port; + ip_fd_t *ip_fd; + ip_ass_t *ip_ass; + acc_t *pack, *next_pack; + + for (i= 0, ip_port= ip_port_table; i<ip_conf_nr; i++, ip_port++) + { + if (ip_port->ip_dl_type == IPDL_ETH) + { + /* Can't free de_frame. + * bf_check_acc(ip_port->ip_dl.dl_eth.de_frame); + */ + if (priority == IP_PRI_PORTBUFS) + { + next_pack= ip_port->ip_dl.dl_eth.de_arp_head; + while(next_pack != NULL) + { + pack= next_pack; + next_pack= pack->acc_ext_link; + bf_afree(pack); + } + ip_port->ip_dl.dl_eth.de_arp_head= next_pack; + + next_pack= ip_port->ip_dl.dl_eth.de_q_head; + while(next_pack != NULL) + { + pack= next_pack; + next_pack= pack->acc_ext_link; + bf_afree(pack); + } + ip_port->ip_dl.dl_eth.de_q_head= next_pack; + } + } + else if (ip_port->ip_dl_type == IPDL_PSIP) + { + if (priority == IP_PRI_PORTBUFS) + { + next_pack= ip_port->ip_dl.dl_ps.ps_send_head; + while(next_pack != NULL) + { + pack= next_pack; + next_pack= pack->acc_ext_link; + bf_afree(pack); + } + ip_port->ip_dl.dl_ps.ps_send_head= next_pack; + } + } + if (priority == IP_PRI_PORTBUFS) + { + next_pack= ip_port->ip_loopb_head; + while(next_pack && next_pack->acc_ext_link) + { + pack= next_pack; + next_pack= pack->acc_ext_link; + bf_afree(pack); + } + if (next_pack) + { + if (ev_in_queue(&ip_port->ip_loopb_event)) + { +#if !CRAMPED + printf( +"not freeing ip_loopb_head, ip_loopb_event enqueued\n"); +#endif + } + else + { + bf_afree(next_pack); + next_pack= NULL; + } + } + ip_port->ip_loopb_head= next_pack; + } + } + if (priority == IP_PRI_FDBUFS_EXTRA) + { + for (i= 0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++) + { + while (ip_fd->if_rdbuf_head && + ip_fd->if_rdbuf_head->acc_ext_link) + { + pack= ip_fd->if_rdbuf_head; + ip_fd->if_rdbuf_head= pack->acc_ext_link; + bf_afree(pack); + } + } + } + if (priority == IP_PRI_FDBUFS) + { + for (i= 0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++) + { + while (ip_fd->if_rdbuf_head) + { + pack= ip_fd->if_rdbuf_head; + ip_fd->if_rdbuf_head= pack->acc_ext_link; + bf_afree(pack); + } + } + } + if (priority == IP_PRI_ASSBUFS) + { + for (i= 0, ip_ass= ip_ass_table; i<IP_ASS_NR; i++, ip_ass++) + { + next_pack= ip_ass->ia_frags; + while(ip_ass->ia_frags != NULL) + { + pack= ip_ass->ia_frags; + ip_ass->ia_frags= pack->acc_ext_link; + bf_afree(pack); + } + ip_ass->ia_first_time= 0; + } + } +} + +#ifdef BUF_CONSISTENCY_CHECK +PRIVATE void ip_bufcheck() +{ + int i; + ip_port_t *ip_port; + ip_fd_t *ip_fd; + ip_ass_t *ip_ass; + acc_t *pack; + + for (i= 0, ip_port= ip_port_table; i<ip_conf_nr; i++, ip_port++) + { + if (ip_port->ip_dl_type == IPDL_ETH) + { + bf_check_acc(ip_port->ip_dl.dl_eth.de_frame); + for (pack= ip_port->ip_dl.dl_eth.de_q_head; pack; + pack= pack->acc_ext_link) + { + bf_check_acc(pack); + } + for (pack= ip_port->ip_dl.dl_eth.de_arp_head; pack; + pack= pack->acc_ext_link) + { + bf_check_acc(pack); + } + } + else if (ip_port->ip_dl_type == IPDL_PSIP) + { + for (pack= ip_port->ip_dl.dl_ps.ps_send_head; pack; + pack= pack->acc_ext_link) + { + bf_check_acc(pack); + } + } + for (pack= ip_port->ip_loopb_head; pack; + pack= pack->acc_ext_link) + { + bf_check_acc(pack); + } + } + for (i= 0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++) + { + for (pack= ip_fd->if_rdbuf_head; pack; + pack= pack->acc_ext_link) + { + bf_check_acc(pack); + } + } + for (i= 0, ip_ass= ip_ass_table; i<IP_ASS_NR; i++, ip_ass++) + { + for (pack= ip_ass->ia_frags; pack; pack= pack->acc_ext_link) + bf_check_acc(pack); + } +} +#endif /* BUF_CONSISTENCY_CHECK */ + +PRIVATE void ip_bad_callback(ip_port) +struct ip_port *ip_port; +{ +#if !CRAMPED + ip_panic(( "no callback filled in for port %d", ip_port->ip_port )); +#endif +} + +/* + * $PchId: ip.c,v 1.7 1996/12/17 07:54:47 philip Exp $ + */ diff --git a/servers/inet/generic/ip.h b/servers/inet/generic/ip.h new file mode 100644 index 000000000..927eced83 --- /dev/null +++ b/servers/inet/generic/ip.h @@ -0,0 +1,28 @@ +/* +ip.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef INET_IP_H +#define INET_IP_H + +/* Prototypes */ + +struct acc; + +void ip_prep ARGS(( void )); +void ip_init ARGS(( void )); +int ip_open ARGS(( int port, int srfd, + get_userdata_t get_userdata, put_userdata_t put_userdata, + put_pkt_t put_pkt )); +int ip_ioctl ARGS(( int fd, ioreq_t req )); +int ip_read ARGS(( int fd, size_t count )); +int ip_write ARGS(( int fd, size_t count )); +int ip_send ARGS(( int fd, struct acc *data, size_t data_len )); + +#endif /* INET_IP_H */ + +/* + * $PchId: ip.h,v 1.6 1996/05/07 20:49:28 philip Exp $ + */ diff --git a/servers/inet/generic/ip_eth.c b/servers/inet/generic/ip_eth.c new file mode 100644 index 000000000..d05d77758 --- /dev/null +++ b/servers/inet/generic/ip_eth.c @@ -0,0 +1,704 @@ +/* +generic/ip_eth.c + +Ethernet specific part of the IP implementation + +Created: Apr 22, 1993 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "type.h" +#include "arp.h" +#include "assert.h" +#include "buf.h" +#include "clock.h" +#include "eth.h" +#include "event.h" +#include "ip.h" +#include "ip_int.h" + +THIS_FILE + +typedef struct xmit_hdr +{ + time_t xh_time; + ipaddr_t xh_ipaddr; +} xmit_hdr_t; + +PRIVATE ether_addr_t broadcast_ethaddr= { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; +PRIVATE ipaddr_t broadcast_ipaddr= 0xFFFFFFFFL; + +FORWARD void do_eth_read ARGS(( ip_port_t *port )); +FORWARD acc_t *get_eth_data ARGS(( int fd, size_t offset, + size_t count, int for_ioctl )); +FORWARD int put_eth_data ARGS(( int fd, size_t offset, + acc_t *data, int for_ioctl )); +FORWARD void ipeth_main ARGS(( ip_port_t *port )); +FORWARD void ipeth_set_ipaddr ARGS(( ip_port_t *port )); +FORWARD void ipeth_restart_send ARGS(( ip_port_t *ip_port )); +FORWARD int ipeth_send ARGS(( struct ip_port *ip_port, ipaddr_t dest, + acc_t *pack, int broadcast )); +FORWARD void ipeth_arp_reply ARGS(( int ip_port_nr, ipaddr_t ipaddr, + ether_addr_t *dst_ether_ptr )); +FORWARD int ipeth_update_ttl ARGS(( time_t enq_time, time_t now, + acc_t *eth_pack )); +FORWARD void ip_eth_arrived ARGS(( int port, acc_t *pack, + size_t pack_size )); + + +PUBLIC int ipeth_init(ip_port) +ip_port_t *ip_port; +{ + assert(BUF_S >= sizeof(xmit_hdr_t)); + assert(BUF_S >= sizeof(eth_hdr_t)); + + ip_port->ip_dl.dl_eth.de_fd= eth_open(ip_port-> + ip_dl.dl_eth.de_port, ip_port->ip_port, + get_eth_data, put_eth_data, ip_eth_arrived); + if (ip_port->ip_dl.dl_eth.de_fd < 0) + { + DBLOCK(1, printf("ip.c: unable to open eth port\n")); + return -1; + } + ip_port->ip_dl.dl_eth.de_state= IES_EMPTY; + ip_port->ip_dl.dl_eth.de_flags= IEF_EMPTY; + ip_port->ip_dl.dl_eth.de_q_head= NULL; + ip_port->ip_dl.dl_eth.de_q_tail= NULL; + ip_port->ip_dl.dl_eth.de_arp_head= NULL; + ip_port->ip_dl.dl_eth.de_arp_tail= NULL; + ip_port->ip_dev_main= ipeth_main; + ip_port->ip_dev_set_ipaddr= ipeth_set_ipaddr; + ip_port->ip_dev_send= ipeth_send; + ip_port->ip_mss= ETH_MAX_PACK_SIZE-ETH_HDR_SIZE; + return 0; +} + +PRIVATE void ipeth_main(ip_port) +ip_port_t *ip_port; +{ + int result, i; + ip_fd_t *ip_fd; + + switch (ip_port->ip_dl.dl_eth.de_state) + { + case IES_EMPTY: + ip_port->ip_dl.dl_eth.de_state= IES_SETPROTO; + + result= eth_ioctl(ip_port->ip_dl.dl_eth.de_fd, NWIOSETHOPT); + if (result == NW_SUSPEND) + ip_port->ip_dl.dl_eth.de_flags |= IEF_SUSPEND; + if (result<0) + { + DBLOCK(1, printf("eth_ioctl(..,%lx)=%d\n", + NWIOSETHOPT, result)); + return; + } + if (ip_port->ip_dl.dl_eth.de_state != IES_SETPROTO) + return; + /* drops through */ + case IES_SETPROTO: + result= arp_set_cb(ip_port->ip_dl.dl_eth.de_port, + ip_port->ip_port, + ipeth_arp_reply); + if (result != NW_OK) + { +#if !CRAMPED + printf("ipeth_main: arp_set_cb failed: %d\n", + result); +#endif + return; + } + + /* Wait until the interface is configured up. */ + ip_port->ip_dl.dl_eth.de_state= IES_GETIPADDR; + if (!(ip_port->ip_flags & IPF_IPADDRSET)) + { + ip_port->ip_dl.dl_eth.de_flags |= IEF_SUSPEND; + return; + } + + /* fall through */ + case IES_GETIPADDR: + ip_port->ip_dl.dl_eth.de_state= IES_MAIN; + for (i=0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++) + { + if (!(ip_fd->if_flags & IFF_INUSE)) + { + continue; + } + if (ip_fd->if_port != ip_port) + { + continue; + } + if (ip_fd->if_flags & IFF_GIPCONF_IP) + { + ip_ioctl (i, NWIOGIPCONF); + } + } + do_eth_read(ip_port); + return; +#if !CRAMPED + default: + ip_panic(( "unknown state: %d", + ip_port->ip_dl.dl_eth.de_state)); +#endif + } +} + +PRIVATE acc_t *get_eth_data (fd, offset, count, for_ioctl) +int fd; +size_t offset; +size_t count; +int for_ioctl; +{ + ip_port_t *ip_port; + acc_t *data; + int result; + + ip_port= &ip_port_table[fd]; + + switch (ip_port->ip_dl.dl_eth.de_state) + { + case IES_SETPROTO: + if (!count) + { + result= (int)offset; + if (result<0) + { + ip_port->ip_dl.dl_eth.de_state= IES_ERROR; + break; + } + if (ip_port->ip_dl.dl_eth.de_flags & IEF_SUSPEND) + ipeth_main(ip_port); + return NW_OK; + } + assert ((!offset) && (count == sizeof(struct nwio_ethopt))); + { + struct nwio_ethopt *ethopt; + acc_t *acc; + + acc= bf_memreq(sizeof(*ethopt)); + ethopt= (struct nwio_ethopt *)ptr2acc_data(acc); + ethopt->nweo_flags= NWEO_COPY|NWEO_EN_BROAD| + NWEO_EN_MULTI|NWEO_TYPESPEC; + ethopt->nweo_type= HTONS(ETH_IP_PROTO); + return acc; + } + + case IES_MAIN: + if (!count) + { + result= (int)offset; + if (result<0) + ip_warning(( "error on write: %d\n", result )); + bf_afree (ip_port->ip_dl.dl_eth.de_frame); + ip_port->ip_dl.dl_eth.de_frame= 0; + + if (ip_port->ip_dl.dl_eth.de_flags & IEF_WRITE_SP) + { + ip_port->ip_dl.dl_eth.de_flags &= + ~IEF_WRITE_SP; + ipeth_restart_send(ip_port); + } + return NW_OK; + } + data= bf_cut (ip_port->ip_dl.dl_eth.de_frame, offset, count); + assert (data); + return data; + default: +#if !CRAMPED + printf( + "get_eth_data(%d, 0x%d, 0x%d) called but ip_state=0x%x\n", + fd, offset, count, ip_port->ip_dl.dl_eth.de_state); +#endif + break; + } + return 0; +} + +PRIVATE int put_eth_data (port, offset, data, for_ioctl) +int port; +size_t offset; +acc_t *data; +int for_ioctl; +{ + ip_port_t *ip_port; + acc_t *pack; + int result; + + ip_port= &ip_port_table[port]; + + assert(0); + + if (ip_port->ip_dl.dl_eth.de_flags & IEF_READ_IP) + { + if (!data) + { + result= (int)offset; + if (result<0) + { + DBLOCK(1, printf( + "ip.c: put_eth_data(..,%d,..)\n", result)); + return NW_OK; + } + if (ip_port->ip_dl.dl_eth.de_flags & IEF_READ_SP) + { + ip_port->ip_dl.dl_eth.de_flags &= + ~(IEF_READ_IP|IEF_READ_SP); + do_eth_read(ip_port); + } + else + ip_port->ip_dl.dl_eth.de_flags &= ~IEF_READ_IP; + return NW_OK; + } + assert (!offset); + /* Warning: the above assertion is illegal; puts and + gets of data can be brokenup in any piece the server + likes. However we assume that the server is eth.c + and it transfers only whole packets. */ + ip_eth_arrived(port, data, bf_bufsize(data)); + return NW_OK; + } +#if !CRAMPED + printf("ip_port->ip_dl.dl_eth.de_state= 0x%x", + ip_port->ip_dl.dl_eth.de_state); + ip_panic (( "strange status" )); +#endif +} + +PRIVATE void ipeth_set_ipaddr(ip_port) +ip_port_t *ip_port; +{ + arp_set_ipaddr (ip_port->ip_dl.dl_eth.de_port, ip_port->ip_ipaddr); + if (ip_port->ip_dl.dl_eth.de_state == IES_GETIPADDR) + ipeth_main(ip_port); +} + +PRIVATE int ipeth_send(ip_port, dest, pack, broadcast) +struct ip_port *ip_port; +ipaddr_t dest; +acc_t *pack; +int broadcast; +{ + int r; + acc_t *eth_pack, *tail; + size_t pack_size; + eth_hdr_t *eth_hdr; + xmit_hdr_t *xmit_hdr; + ipaddr_t hostpart; + time_t t; + + /* Start optimistic: the arp will succeed without blocking and the + * ethernet packet can be sent without blocking also. Start with + * the allocation of the ethernet header. + */ + eth_pack= bf_memreq(sizeof(*eth_hdr)); + assert(eth_pack->acc_next == NULL); + eth_pack->acc_next= pack; + pack_size= bf_bufsize(eth_pack); + if (pack_size<ETH_MIN_PACK_SIZE) + { + tail= bf_memreq(ETH_MIN_PACK_SIZE-pack_size); + eth_pack= bf_append(eth_pack, tail); + } + eth_hdr= (eth_hdr_t *)ptr2acc_data(eth_pack); + + /* Lookup the ethernet address */ + if (broadcast) + eth_hdr->eh_dst= broadcast_ethaddr; + else + { + if ((dest & ip_port->ip_subnetmask) != + (ip_port->ip_ipaddr & ip_port->ip_subnetmask)) + { +#if !CRAMPED + ip_panic(( "invalid destination" )); +#endif + } + + hostpart= (dest & ~ip_port->ip_subnetmask); + + assert(hostpart != 0); + assert(dest != ip_port->ip_ipaddr); + + r= arp_ip_eth(ip_port->ip_dl.dl_eth.de_port, + dest, ð_hdr->eh_dst); + if (r == NW_SUSPEND) + { + /* Unfortunately, the arp takes some time, use + * the ethernet header to store the next hop + * ip address and the current time. + */ + xmit_hdr= (xmit_hdr_t *)eth_hdr; + xmit_hdr->xh_time= get_time(); + xmit_hdr->xh_ipaddr= dest; + eth_pack->acc_ext_link= NULL; + if (ip_port->ip_dl.dl_eth.de_arp_head == NULL) + ip_port->ip_dl.dl_eth.de_arp_head= eth_pack; + else + { + ip_port->ip_dl.dl_eth.de_arp_tail-> + acc_ext_link= eth_pack; + } + ip_port->ip_dl.dl_eth.de_arp_tail= eth_pack; + return NW_OK; + } + if (r == EDSTNOTRCH) + { + bf_afree(eth_pack); + return EDSTNOTRCH; + } + assert(r == NW_OK); + } + + /* If we have no write in progress, we can try to send the ethernet + * packet using eth_send. If the IP packet is larger than mss, + * unqueue the packet and let ipeth_restart_send deal with it. + */ + pack_size= bf_bufsize(eth_pack); + if (ip_port->ip_dl.dl_eth.de_frame == NULL && pack_size <= + ip_port->ip_mss + sizeof(*eth_hdr)) + { + r= eth_send(ip_port->ip_dl.dl_eth.de_fd, + eth_pack, pack_size); + if (r == NW_OK) + return NW_OK; + + /* A non-blocking send is not possible, start a regular + * send. + */ + assert(r == NW_WOULDBLOCK); + ip_port->ip_dl.dl_eth.de_frame= eth_pack; + r= eth_write(ip_port->ip_dl.dl_eth.de_fd, pack_size); + if (r == NW_SUSPEND) + { + assert(!(ip_port->ip_dl.dl_eth.de_flags & + IEF_WRITE_SP)); + ip_port->ip_dl.dl_eth.de_flags |= IEF_WRITE_SP; + } + assert(r == NW_OK || r == NW_SUSPEND); + return NW_OK; + } + + /* Enqueue the packet, and store the current time, in the + * room for the ethernet source address. + */ + t= get_time(); + assert(sizeof(t) <= sizeof(eth_hdr->eh_src)); + memcpy(ð_hdr->eh_src, &t, sizeof(t)); + + eth_pack->acc_ext_link= NULL; + if (ip_port->ip_dl.dl_eth.de_q_head == NULL) + ip_port->ip_dl.dl_eth.de_q_head= eth_pack; + else + { + ip_port->ip_dl.dl_eth.de_q_tail->acc_ext_link= eth_pack; + } + ip_port->ip_dl.dl_eth.de_q_tail= eth_pack; + if (ip_port->ip_dl.dl_eth.de_frame == NULL) + ipeth_restart_send(ip_port); + return NW_OK; +} + +PRIVATE void ipeth_restart_send(ip_port) +ip_port_t *ip_port; +{ + time_t now, enq_time; + int r; + acc_t *eth_pack, *ip_pack, *next_eth_pack, *next_part, *tail; + size_t pack_size; + eth_hdr_t *eth_hdr, *next_eth_hdr; + + now= get_time(); + + while (ip_port->ip_dl.dl_eth.de_q_head != NULL) + { + eth_pack= ip_port->ip_dl.dl_eth.de_q_head; + ip_port->ip_dl.dl_eth.de_q_head= eth_pack->acc_ext_link; + + eth_hdr= (eth_hdr_t *)ptr2acc_data(eth_pack); + + pack_size= bf_bufsize(eth_pack); + + if (pack_size > ip_port->ip_mss+sizeof(*eth_hdr)) + { + /* Split the IP packet */ + ip_pack= eth_pack->acc_next; + next_part= ip_pack; + ip_pack= ip_split_pack(ip_port, &next_part, + ip_port->ip_mss); + if (ip_pack == NULL) + { + bf_afree(eth_pack); + continue; + } + + /* Allocate new ethernet header */ + next_eth_pack= bf_memreq(sizeof(*next_eth_hdr)); + next_eth_hdr= (eth_hdr_t *)ptr2acc_data(next_eth_pack); + *next_eth_hdr= *eth_hdr; + next_eth_pack->acc_next= next_part; + + next_eth_pack->acc_ext_link= NULL; + if (ip_port->ip_dl.dl_eth.de_q_head == NULL) + ip_port->ip_dl.dl_eth.de_q_head= next_eth_pack; + else + ip_port->ip_dl.dl_eth.de_q_tail->acc_ext_link= + next_eth_pack; + ip_port->ip_dl.dl_eth.de_q_tail= next_eth_pack; + + eth_pack->acc_next= ip_pack; + pack_size= bf_bufsize(eth_pack); + } + + memcpy(&enq_time, ð_hdr->eh_src, sizeof(enq_time)); + if (enq_time + HZ < now) + { + r= ipeth_update_ttl(enq_time, now, eth_pack); + if (r == ETIMEDOUT) + { + ip_warning(( "should send ICMP ttl exceded" )); + bf_afree(eth_pack); + continue; + } + assert(r == NW_OK); + } + + if (pack_size<ETH_MIN_PACK_SIZE) + { + tail= bf_memreq(ETH_MIN_PACK_SIZE-pack_size); + eth_pack= bf_append(eth_pack, tail); + } + + assert(ip_port->ip_dl.dl_eth.de_frame == NULL); + + r= eth_send(ip_port->ip_dl.dl_eth.de_fd, eth_pack, pack_size); + if (r == NW_OK) + continue; + + /* A non-blocking send is not possible, start a regular + * send. + */ + assert(r == NW_WOULDBLOCK); + ip_port->ip_dl.dl_eth.de_frame= eth_pack; + r= eth_write(ip_port->ip_dl.dl_eth.de_fd, pack_size); + if (r == NW_SUSPEND) + { + assert(!(ip_port->ip_dl.dl_eth.de_flags & + IEF_WRITE_SP)); + ip_port->ip_dl.dl_eth.de_flags |= IEF_WRITE_SP; + return; + } + assert(r == NW_OK); + } +} + + +PRIVATE void ipeth_arp_reply(ip_port_nr, ipaddr, eth_addr) +int ip_port_nr; +ipaddr_t ipaddr; +ether_addr_t *eth_addr; +{ + acc_t *prev, *eth_pack; + int r; + xmit_hdr_t *xmit_hdr; + ip_port_t *ip_port; + time_t t; + eth_hdr_t *eth_hdr; + ether_addr_t tmp_eth_addr; + + assert (ip_port_nr >= 0 && ip_port_nr < ip_conf_nr); + ip_port= &ip_port_table[ip_port_nr]; + + for (;;) + { + for (prev= 0, eth_pack= ip_port->ip_dl.dl_eth.de_arp_head; + eth_pack; + prev= eth_pack, eth_pack= eth_pack->acc_ext_link) + { + xmit_hdr= (xmit_hdr_t *)ptr2acc_data(eth_pack); + if (xmit_hdr->xh_ipaddr == ipaddr) + break; + } + + if (eth_pack == NULL) + { + /* No packet found. */ + break; + } + + /* Delete packet from the queue. */ + if (prev == NULL) + { + ip_port->ip_dl.dl_eth.de_arp_head= + eth_pack->acc_ext_link; + } + else + { + prev->acc_ext_link= eth_pack->acc_ext_link; + if (prev->acc_ext_link == NULL) + ip_port->ip_dl.dl_eth.de_arp_tail= prev; + } + + if (eth_addr == NULL) + { + /* Destination is unreachable, delete packet. */ + bf_afree(eth_pack); + continue; + } + + /* Fill in the ethernet address and put the packet on the + * transmit queue. + */ + t= xmit_hdr->xh_time; + eth_hdr= (eth_hdr_t *)ptr2acc_data(eth_pack); + eth_hdr->eh_dst= *eth_addr; + memcpy(ð_hdr->eh_src, &t, sizeof(t)); + + eth_pack->acc_ext_link= NULL; + if (ip_port->ip_dl.dl_eth.de_q_head == NULL) + ip_port->ip_dl.dl_eth.de_q_head= eth_pack; + else + { + ip_port->ip_dl.dl_eth.de_q_tail->acc_ext_link= + eth_pack; + } + ip_port->ip_dl.dl_eth.de_q_tail= eth_pack; + } + + /* Try to get some more ARPs in progress. */ + while (ip_port->ip_dl.dl_eth.de_arp_head) + { + eth_pack= ip_port->ip_dl.dl_eth.de_arp_head; + xmit_hdr= (xmit_hdr_t *)ptr2acc_data(eth_pack); + r= arp_ip_eth(ip_port->ip_dl.dl_eth.de_port, + xmit_hdr->xh_ipaddr, &tmp_eth_addr); + if (r == NW_SUSPEND) + break; /* Normal case */ + + /* Dequeue the packet */ + ip_port->ip_dl.dl_eth.de_arp_head= eth_pack->acc_ext_link; + + if (r == EDSTNOTRCH) + { + bf_afree(eth_pack); + continue; + } + assert(r == NW_OK); + + /* Fill in the ethernet address and put the packet on the + * transmit queue. + */ + t= xmit_hdr->xh_time; + eth_hdr= (eth_hdr_t *)ptr2acc_data(eth_pack); + eth_hdr->eh_dst= tmp_eth_addr; + memcpy(ð_hdr->eh_src, &t, sizeof(t)); + + eth_pack->acc_ext_link= NULL; + if (ip_port->ip_dl.dl_eth.de_q_head == NULL) + ip_port->ip_dl.dl_eth.de_q_head= eth_pack; + else + { + ip_port->ip_dl.dl_eth.de_q_tail->acc_ext_link= + eth_pack; + } + ip_port->ip_dl.dl_eth.de_q_tail= eth_pack; + } + + /* Restart sending ethernet packets. */ + if (ip_port->ip_dl.dl_eth.de_frame == NULL) + ipeth_restart_send(ip_port); +} + +PRIVATE int ipeth_update_ttl(enq_time, now, eth_pack) +time_t enq_time; +time_t now; +acc_t *eth_pack; +{ + int ttl_diff; + ip_hdr_t *ip_hdr; + u32_t sum; + u16_t word; + acc_t *ip_pack; + + ttl_diff= (now-enq_time)/HZ; + enq_time += ttl_diff*HZ; + assert(enq_time <= now && enq_time + HZ > now); + + ip_pack= eth_pack->acc_next; + assert(ip_pack->acc_length >= sizeof(*ip_hdr)); + assert(ip_pack->acc_linkC == 1 && + ip_pack->acc_buffer->buf_linkC == 1); + + ip_hdr= (ip_hdr_t *)ptr2acc_data(ip_pack); + if (ip_hdr->ih_ttl <= ttl_diff) + return ETIMEDOUT; + sum= (u16_t)~ip_hdr->ih_hdr_chk; + word= *(u16_t *)&ip_hdr->ih_ttl; + if (word > sum) + sum += 0xffff - word; + else + sum -= word; + ip_hdr->ih_ttl -= ttl_diff; + word= *(u16_t *)&ip_hdr->ih_ttl; + sum += word; + if (sum > 0xffff) + sum -= 0xffff; + assert(!(sum & 0xffff0000)); + ip_hdr->ih_hdr_chk= ~sum; + assert(ip_hdr->ih_ttl > 0); + return NW_OK; +} + +PRIVATE void do_eth_read(ip_port) +ip_port_t *ip_port; +{ + int result; + + assert(!(ip_port->ip_dl.dl_eth.de_flags & IEF_READ_IP)); + + for (;;) + { + ip_port->ip_dl.dl_eth.de_flags |= IEF_READ_IP; + + result= eth_read (ip_port->ip_dl.dl_eth.de_fd, + ETH_MAX_PACK_SIZE); + if (result == NW_SUSPEND) + { + assert(!(ip_port->ip_dl.dl_eth.de_flags & + IEF_READ_SP)); + ip_port->ip_dl.dl_eth.de_flags |= IEF_READ_SP; + return; + } + ip_port->ip_dl.dl_eth.de_flags &= ~IEF_READ_IP; + if (result<0) + { + return; + } + } +} + +PRIVATE void ip_eth_arrived(port, pack, pack_size) +int port; +acc_t *pack; +size_t pack_size; +{ + int broadcast; + ip_port_t *ip_port; + + ip_port= &ip_port_table[port]; + broadcast= (*(u8_t *)ptr2acc_data(pack) & 1); + + pack= bf_delhead(pack, ETH_HDR_SIZE); + + if (broadcast) + ip_arrived_broadcast(ip_port, pack); + else + ip_arrived(ip_port, pack); +} + +/* + * $PchId: ip_eth.c,v 1.9 1996/12/17 07:55:21 philip Exp $ + */ diff --git a/servers/inet/generic/ip_int.h b/servers/inet/generic/ip_int.h new file mode 100644 index 000000000..d09932df3 --- /dev/null +++ b/servers/inet/generic/ip_int.h @@ -0,0 +1,175 @@ +/* +ip_int.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef INET_IP_INT_H +#define INET_IP_INT_H + +#define IP_FD_NR (8*IP_PORT_MAX) +#define IP_ASS_NR 3 + +#define IP_42BSD_BCAST 1 /* hostnumber 0 is also network + broadcast */ + +struct ip_port; +struct ip_fd; +typedef void (*ip_dev_t) ARGS(( struct ip_port *ip_port )); +typedef int (*ip_dev_send_t) ARGS(( struct ip_port *ip_port, ipaddr_t dest, + acc_t *pack, int broadcast )); + +#define IP_PROTO_HASH_NR 32 + +typedef struct ip_port +{ + int ip_flags, ip_dl_type; + int ip_port; + union + { + struct + { + int de_state; + int de_flags; + int de_port; + int de_fd; + acc_t *de_frame; + acc_t *de_q_head; + acc_t *de_q_tail; + acc_t *de_arp_head; + acc_t *de_arp_tail; + } dl_eth; + struct + { + int ps_port; + acc_t *ps_send_head; + acc_t *ps_send_tail; + } dl_ps; + } ip_dl; + ipaddr_t ip_ipaddr; + ipaddr_t ip_netmask; + ipaddr_t ip_subnetmask; + u16_t ip_frame_id; + u16_t ip_mss; + ip_dev_t ip_dev_main; + ip_dev_t ip_dev_set_ipaddr; + ip_dev_send_t ip_dev_send; + acc_t *ip_loopb_head; + acc_t *ip_loopb_tail; + event_t ip_loopb_event; + struct ip_fd *ip_proto_any; + struct ip_fd *ip_proto[IP_PROTO_HASH_NR]; +} ip_port_t; + +#define IES_EMPTY 0x0 +#define IES_SETPROTO 0x1 +#define IES_GETIPADDR 0x2 +#define IES_MAIN 0x3 +#define IES_ERROR 0x4 + +#define IEF_EMPTY 0x1 +#define IEF_SUSPEND 0x8 +#define IEF_READ_IP 0x10 +#define IEF_READ_SP 0x20 +#define IEF_WRITE_SP 0x80 + +#define IPF_EMPTY 0x0 +#define IPF_CONFIGURED 0x1 +#define IPF_IPADDRSET 0x2 +#define IPF_NETMASKSET 0x4 + +#define IPDL_ETH NETTYPE_ETH +#define IPDL_PSIP NETTYPE_PSIP + +typedef struct ip_ass +{ + acc_t *ia_frags; + int ia_min_ttl; + ip_port_t *ia_port; + time_t ia_first_time; + ipaddr_t ia_srcaddr, ia_dstaddr; + int ia_proto, ia_id; +} ip_ass_t; + +typedef struct ip_fd +{ + int if_flags; + struct nwio_ipopt if_ipopt; + ip_port_t *if_port; + struct ip_fd *if_proto_next; + int if_srfd; + acc_t *if_rdbuf_head; + acc_t *if_rdbuf_tail; + get_userdata_t if_get_userdata; + put_userdata_t if_put_userdata; + put_pkt_t if_put_pkt; + time_t if_exp_time; + size_t if_rd_count; +} ip_fd_t; + +#define IFF_EMPTY 0x0 +#define IFF_INUSE 0x1 +#define IFF_OPTSET 0x2 +#define IFF_BUSY 0xC +# define IFF_READ_IP 0x4 +# define IFF_GIPCONF_IP 0x8 + +typedef enum nettype +{ + IPNT_ZERO, /* 0.xx.xx.xx */ + IPNT_CLASS_A, /* 1.xx.xx.xx .. 126.xx.xx.xx */ + IPNT_LOCAL, /* 127.xx.xx.xx */ + IPNT_CLASS_B, /* 128.xx.xx.xx .. 191.xx.xx.xx */ + IPNT_CLASS_C, /* 192.xx.xx.xx .. 223.xx.xx.xx */ + IPNT_CLASS_D, /* 224.xx.xx.xx .. 239.xx.xx.xx */ + IPNT_CLASS_E, /* 240.xx.xx.xx .. 247.xx.xx.xx */ + IPNT_MARTIAN, /* 248.xx.xx.xx .. 254.xx.xx.xx + others */ + IPNT_BROADCAST /* 255.255.255.255 */ +} nettype_t; + +/* ip_eth.c */ +int ipeth_init ARGS(( ip_port_t *ip_port )); + +/* ip_ioctl.c */ +void ip_hash_proto ARGS(( ip_fd_t *ip_fd )); +void ip_unhash_proto ARGS(( ip_fd_t *ip_fd )); + +/* ip_lib.c */ +ipaddr_t ip_get_netmask ARGS(( ipaddr_t hostaddr )); +ipaddr_t ip_get_ifaddr ARGS(( int ip_port_nr )); +int ip_chk_hdropt ARGS(( u8_t *opt, int optlen )); +void ip_print_frags ARGS(( acc_t *acc )); +nettype_t ip_nettype ARGS(( ipaddr_t ipaddr )); +ipaddr_t ip_netmask ARGS(( nettype_t nettype )); +char *ip_nettoa ARGS(( nettype_t nettype )); + +/* ip_ps.c */ +int ipps_init ARGS(( ip_port_t *ip_port )); +void ipps_get ARGS(( int ip_port_nr )); +void ipps_put ARGS(( int ip_port_nr, acc_t *pack )); + +/* ip_read.c */ +void ip_port_arrive ARGS(( ip_port_t *port, acc_t *pack, ip_hdr_t *ip_hdr )); +void ip_arrived ARGS(( ip_port_t *port, acc_t *pack )); +void ip_arrived_broadcast ARGS(( ip_port_t *port, acc_t *pack )); +void ip_process_loopb ARGS(( event_t *ev, ev_arg_t arg )); + +/* ip_write.c */ +void dll_eth_write_frame ARGS(( ip_port_t *port )); +acc_t *ip_split_pack ARGS(( ip_port_t *ip_port, acc_t **ref_last, + int first_size )); +void ip_hdr_chksum ARGS(( ip_hdr_t *ip_hdr, int ip_hdr_len )); + + +extern ip_fd_t ip_fd_table[IP_FD_NR]; +extern ip_port_t *ip_port_table; +extern ip_ass_t ip_ass_table[IP_ASS_NR]; + +#define NWIO_DEFAULT (NWIO_EN_LOC | NWIO_EN_BROAD | NWIO_REMANY | \ + NWIO_RWDATALL | NWIO_HDR_O_SPEC) + +#endif /* INET_IP_INT_H */ + +/* + * $PchId: ip_int.h,v 1.6 1996/12/17 07:59:36 philip Exp $ + */ diff --git a/servers/inet/generic/ip_ioctl.c b/servers/inet/generic/ip_ioctl.c new file mode 100644 index 000000000..a65ede67a --- /dev/null +++ b/servers/inet/generic/ip_ioctl.c @@ -0,0 +1,472 @@ +/* +ip_ioctl.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "buf.h" +#include "event.h" +#include "type.h" + +#include "arp.h" +#include "assert.h" +#include "clock.h" +#include "icmp_lib.h" +#include "ip.h" +#include "ip_int.h" +#include "ipr.h" + +THIS_FILE + +FORWARD int ip_checkopt ARGS(( ip_fd_t *ip_fd )); +FORWARD void reply_thr_get ARGS(( ip_fd_t *ip_fd, size_t + reply, int for_ioctl )); + +PUBLIC int ip_ioctl (fd, req) +int fd; +ioreq_t req; +{ + ip_fd_t *ip_fd; + ip_port_t *ip_port; + nwio_ipopt_t *ipopt; + nwio_ipopt_t oldopt, newopt; + nwio_ipconf_t *ipconf; + nwio_route_t *route_ent; + acc_t *data; + int result; + unsigned int new_en_flags, new_di_flags, + old_en_flags, old_di_flags; + unsigned long new_flags; + int old_ip_flags; + int ent_no; + + assert (fd>=0 && fd<=IP_FD_NR); + ip_fd= &ip_fd_table[fd]; + + assert (ip_fd->if_flags & IFF_INUSE); + + switch (req) + { + case NWIOSIPOPT: + data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, 0, + sizeof(nwio_ipopt_t), TRUE); + + data= bf_packIffLess (data, sizeof(nwio_ipopt_t)); + assert (data->acc_length == sizeof(nwio_ipopt_t)); + + ipopt= (nwio_ipopt_t *)ptr2acc_data(data); + oldopt= ip_fd->if_ipopt; + newopt= *ipopt; + + old_en_flags= oldopt.nwio_flags & 0xffff; + old_di_flags= (oldopt.nwio_flags >> 16) & 0xffff; + new_en_flags= newopt.nwio_flags & 0xffff; + new_di_flags= (newopt.nwio_flags >> 16) & 0xffff; + if (new_en_flags & new_di_flags) + { + bf_afree(data); + reply_thr_get (ip_fd, EBADMODE, TRUE); + return NW_OK; + } + + /* NWIO_ACC_MASK */ + if (new_di_flags & NWIO_ACC_MASK) + { + bf_afree(data); + reply_thr_get (ip_fd, EBADMODE, TRUE); + return NW_OK; + /* access modes can't be disable */ + } + + if (!(new_en_flags & NWIO_ACC_MASK)) + new_en_flags |= (old_en_flags & NWIO_ACC_MASK); + + /* NWIO_LOC_MASK */ + if (!((new_en_flags|new_di_flags) & NWIO_LOC_MASK)) + { + new_en_flags |= (old_en_flags & NWIO_LOC_MASK); + new_di_flags |= (old_di_flags & NWIO_LOC_MASK); + } + + /* NWIO_BROAD_MASK */ + if (!((new_en_flags|new_di_flags) & NWIO_BROAD_MASK)) + { + new_en_flags |= (old_en_flags & NWIO_BROAD_MASK); + new_di_flags |= (old_di_flags & NWIO_BROAD_MASK); + } + + /* NWIO_REM_MASK */ + if (!((new_en_flags|new_di_flags) & NWIO_REM_MASK)) + { + new_en_flags |= (old_en_flags & NWIO_REM_MASK); + new_di_flags |= (old_di_flags & NWIO_REM_MASK); + newopt.nwio_rem= oldopt.nwio_rem; + } + + /* NWIO_PROTO_MASK */ + if (!((new_en_flags|new_di_flags) & NWIO_PROTO_MASK)) + { + new_en_flags |= (old_en_flags & NWIO_PROTO_MASK); + new_di_flags |= (old_di_flags & NWIO_PROTO_MASK); + newopt.nwio_proto= oldopt.nwio_proto; + } + + /* NWIO_HDR_O_MASK */ + if (!((new_en_flags|new_di_flags) & NWIO_HDR_O_MASK)) + { + new_en_flags |= (old_en_flags & NWIO_HDR_O_MASK); + new_di_flags |= (old_di_flags & NWIO_HDR_O_MASK); + newopt.nwio_tos= oldopt.nwio_tos; + newopt.nwio_ttl= oldopt.nwio_ttl; + newopt.nwio_df= oldopt.nwio_df; + newopt.nwio_hdropt= oldopt.nwio_hdropt; + } + + /* NWIO_RW_MASK */ + if (!((new_en_flags|new_di_flags) & NWIO_RW_MASK)) + { + new_en_flags |= (old_en_flags & NWIO_RW_MASK); + new_di_flags |= (old_di_flags & NWIO_RW_MASK); + } + + new_flags= ((unsigned long)new_di_flags << 16) | new_en_flags; + + if ((new_flags & NWIO_RWDATONLY) && (new_flags & + (NWIO_REMANY|NWIO_PROTOANY|NWIO_HDR_O_ANY))) + { + bf_afree(data); + reply_thr_get(ip_fd, EBADMODE, TRUE); + return NW_OK; + } + + if (ip_fd->if_flags & IFF_OPTSET) + ip_unhash_proto(ip_fd); + + newopt.nwio_flags= new_flags; + ip_fd->if_ipopt= newopt; + + result= ip_checkopt(ip_fd); + + if (result<0) + ip_fd->if_ipopt= oldopt; + + bf_afree(data); + reply_thr_get (ip_fd, result, TRUE); + return NW_OK; + + case NWIOGIPOPT: + data= bf_memreq(sizeof(nwio_ipopt_t)); + + ipopt= (nwio_ipopt_t *)ptr2acc_data(data); + + *ipopt= ip_fd->if_ipopt; + + result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, 0, data, + TRUE); + return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, result, + (acc_t *)0, TRUE); + + case NWIOSIPCONF: + ip_port= ip_fd->if_port; + + data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, 0, + sizeof(nwio_ipconf_t), TRUE); + + data= bf_packIffLess (data, sizeof(nwio_ipconf_t)); + assert (data->acc_length == sizeof(nwio_ipconf_t)); + + old_ip_flags= ip_port->ip_flags; + + ipconf= (nwio_ipconf_t *)ptr2acc_data(data); + + if (ipconf->nwic_flags & ~NWIC_FLAGS) + { + bf_afree(data); + return (*ip_fd->if_put_userdata)(ip_fd-> if_srfd, + EBADMODE, (acc_t *)0, TRUE); + } + + if (ipconf->nwic_flags & NWIC_IPADDR_SET) + { + ip_port->ip_ipaddr= ipconf->nwic_ipaddr; + ip_port->ip_flags |= IPF_IPADDRSET; + ip_port->ip_netmask= + ip_netmask(ip_nettype(ipconf->nwic_ipaddr)); + if (!(ip_port->ip_flags & IPF_NETMASKSET)) { + ip_port->ip_subnetmask= ip_port->ip_netmask; + } + (*ip_port->ip_dev_set_ipaddr)(ip_port); + } + if (ipconf->nwic_flags & NWIC_NETMASK_SET) + { + ip_port->ip_subnetmask= ipconf->nwic_netmask; + ip_port->ip_flags |= IPF_NETMASKSET; + } + + bf_afree(data); + return (*ip_fd->if_put_userdata)(ip_fd-> if_srfd, NW_OK, + (acc_t *)0, TRUE); + + case NWIOGIPCONF: + ip_port= ip_fd->if_port; + + if (!(ip_port->ip_flags & IPF_IPADDRSET)) + { + ip_fd->if_flags |= IFF_GIPCONF_IP; + return NW_SUSPEND; + } + ip_fd->if_flags &= ~IFF_GIPCONF_IP; + data= bf_memreq(sizeof(nwio_ipconf_t)); + ipconf= (nwio_ipconf_t *)ptr2acc_data(data); + ipconf->nwic_flags= NWIC_IPADDR_SET; + ipconf->nwic_ipaddr= ip_port->ip_ipaddr; + ipconf->nwic_netmask= ip_port->ip_subnetmask; + if (ip_port->ip_flags & IPF_NETMASKSET) + ipconf->nwic_flags |= NWIC_NETMASK_SET; + + result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, 0, data, + TRUE); + return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, result, + (acc_t *)0, TRUE); + + case NWIOGIPIROUTE: + data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, + 0, sizeof(nwio_route_t), TRUE); + if (data == NULL) + { + return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, + EFAULT, NULL, TRUE); + } + + data= bf_packIffLess (data, sizeof(nwio_route_t) ); + route_ent= (nwio_route_t *)ptr2acc_data(data); + ent_no= route_ent->nwr_ent_no; + bf_afree(data); + + data= bf_memreq(sizeof(nwio_route_t)); + route_ent= (nwio_route_t *)ptr2acc_data(data); + result= ipr_get_iroute(ent_no, route_ent); + if (result < 0) + bf_afree(data); + else + { + assert(result == NW_OK); + result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, 0, + data, TRUE); + } + return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, + result, (acc_t *)0, TRUE); + + case NWIOSIPIROUTE: + data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, + 0, sizeof(nwio_route_t), TRUE); + if (data == NULL) + { + return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, + EFAULT, NULL, TRUE); + } + + data= bf_packIffLess (data, sizeof(nwio_route_t) ); + route_ent= (nwio_route_t *)ptr2acc_data(data); + result= ipr_add_iroute(ip_fd->if_port->ip_port, + route_ent->nwr_dest, route_ent->nwr_netmask, + route_ent->nwr_gateway, + (route_ent->nwr_flags & NWRF_UNREACHABLE) ? + IRTD_UNREACHABLE : route_ent->nwr_dist, + !!(route_ent->nwr_flags & NWRF_STATIC), NULL); + bf_afree(data); + + return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, + result, (acc_t *)0, TRUE); + + case NWIOGIPOROUTE: + data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, + 0, sizeof(nwio_route_t), TRUE); + if (data == NULL) + { + return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, + EFAULT, NULL, TRUE); + } + + data= bf_packIffLess (data, sizeof(nwio_route_t) ); + route_ent= (nwio_route_t *)ptr2acc_data(data); + ent_no= route_ent->nwr_ent_no; + bf_afree(data); + + data= bf_memreq(sizeof(nwio_route_t)); + route_ent= (nwio_route_t *)ptr2acc_data(data); + result= ipr_get_oroute(ent_no, route_ent); + if (result < 0) + bf_afree(data); + else + { + assert(result == NW_OK); + result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, 0, + data, TRUE); + } + return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, + result, (acc_t *)0, TRUE); + + case NWIODIPIROUTE: + data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, + 0, sizeof(nwio_route_t), TRUE); + if (data == NULL) + { + return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, + EFAULT, NULL, TRUE); + } + + data= bf_packIffLess (data, sizeof(nwio_route_t) ); + route_ent= (nwio_route_t *)ptr2acc_data(data); + result= ipr_del_iroute(ip_fd->if_port->ip_port, + route_ent->nwr_dest, route_ent->nwr_netmask, + route_ent->nwr_gateway, + (route_ent->nwr_flags & NWRF_UNREACHABLE) ? + IRTD_UNREACHABLE : route_ent->nwr_dist, + !!(route_ent->nwr_flags & NWRF_STATIC)); + bf_afree(data); + + return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, + result, (acc_t *)0, TRUE); + + case NWIOSIPOROUTE: + data= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, + 0, sizeof(nwio_route_t), TRUE); + if (data == NULL) + { + return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, + EFAULT, NULL, TRUE); + } + + data= bf_packIffLess (data, sizeof(nwio_route_t) ); + route_ent= (nwio_route_t *)ptr2acc_data(data); + result= ipr_add_oroute(ip_fd->if_port->ip_port, + route_ent->nwr_dest, route_ent->nwr_netmask, + route_ent->nwr_gateway, (time_t)0, + route_ent->nwr_dist, + !!(route_ent->nwr_flags & NWRF_STATIC), + route_ent->nwr_pref, NULL); + bf_afree(data); + + return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, + result, (acc_t *)0, TRUE); + + default: + break; + } + DBLOCK(1, printf("replying EBADIOCTL\n")); + return (*ip_fd->if_put_userdata)(ip_fd-> if_srfd, EBADIOCTL, + (acc_t *)0, TRUE); +} + +PUBLIC void ip_hash_proto(ip_fd) +ip_fd_t *ip_fd; +{ + ip_port_t *ip_port; + int hash; + + ip_port= ip_fd->if_port; + if (ip_fd->if_ipopt.nwio_flags & NWIO_PROTOANY) + { + ip_fd->if_proto_next= ip_port->ip_proto_any; + ip_port->ip_proto_any= ip_fd; + } + else + { + hash= ip_fd->if_ipopt.nwio_proto & (IP_PROTO_HASH_NR-1); + ip_fd->if_proto_next= ip_port->ip_proto[hash]; + ip_port->ip_proto[hash]= ip_fd; + } +} + +PUBLIC void ip_unhash_proto(ip_fd) +ip_fd_t *ip_fd; +{ + ip_port_t *ip_port; + ip_fd_t *prev, *curr, **ip_fd_p; + int hash; + + ip_port= ip_fd->if_port; + if (ip_fd->if_ipopt.nwio_flags & NWIO_PROTOANY) + { + ip_fd_p= &ip_port->ip_proto_any; + } + else + { + hash= ip_fd->if_ipopt.nwio_proto & (IP_PROTO_HASH_NR-1); + ip_fd_p= &ip_port->ip_proto[hash]; + } + for (prev= NULL, curr= *ip_fd_p; curr; + prev= curr, curr= curr->if_proto_next) + { + if (curr == ip_fd) + break; + } + assert(curr); + if (prev) + prev->if_proto_next= curr->if_proto_next; + else + *ip_fd_p= curr->if_proto_next; +} + +PRIVATE int ip_checkopt (ip_fd) +ip_fd_t *ip_fd; +{ +/* bug: we don't check access modes yet */ + + unsigned long flags; + unsigned int en_di_flags; + ip_port_t *port; + acc_t *pack; + int result; + + flags= ip_fd->if_ipopt.nwio_flags; + en_di_flags= (flags >>16) | (flags & 0xffff); + + if (flags & NWIO_HDR_O_SPEC) + { + result= ip_chk_hdropt (ip_fd->if_ipopt.nwio_hdropt.iho_data, + ip_fd->if_ipopt.nwio_hdropt.iho_opt_siz); + if (result<0) + return result; + } + if ((en_di_flags & NWIO_ACC_MASK) && + (en_di_flags & NWIO_LOC_MASK) && + (en_di_flags & NWIO_BROAD_MASK) && + (en_di_flags & NWIO_REM_MASK) && + (en_di_flags & NWIO_PROTO_MASK) && + (en_di_flags & NWIO_HDR_O_MASK) && + (en_di_flags & NWIO_RW_MASK)) + { + ip_fd->if_flags |= IFF_OPTSET; + + ip_hash_proto(ip_fd); + } + + else + ip_fd->if_flags &= ~IFF_OPTSET; + + while (ip_fd->if_rdbuf_head) + { + pack= ip_fd->if_rdbuf_head; + ip_fd->if_rdbuf_head= pack->acc_ext_link; + bf_afree(pack); + } + return NW_OK; +} + +PRIVATE void reply_thr_get(ip_fd, reply, for_ioctl) +ip_fd_t *ip_fd; +size_t reply; +int for_ioctl; +{ + acc_t *result; + result= (ip_fd->if_get_userdata)(ip_fd->if_srfd, reply, + (size_t)0, for_ioctl); + assert (!result); +} + +/* + * $PchId: ip_ioctl.c,v 1.8 1996/12/17 07:56:18 philip Exp $ + */ diff --git a/servers/inet/generic/ip_lib.c b/servers/inet/generic/ip_lib.c new file mode 100644 index 000000000..151b08bb4 --- /dev/null +++ b/servers/inet/generic/ip_lib.c @@ -0,0 +1,231 @@ +/* +ip_lib.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "buf.h" +#include "event.h" +#include "type.h" + +#include "assert.h" +#include "io.h" +#include "ip_int.h" + +THIS_FILE + +PUBLIC ipaddr_t ip_get_netmask (hostaddr) +ipaddr_t hostaddr; +{ + return ip_netmask(ip_nettype(hostaddr)); +} + +PUBLIC int ip_chk_hdropt (opt, optlen) +u8_t *opt; +int optlen; +{ + int i, security_present= FALSE, lose_source_present= FALSE, + strict_source_present= FALSE, record_route_present= FALSE, + timestamp_present= FALSE; + +assert (!(optlen & 3)); + i= 0; + while (i<optlen) + { + DBLOCK(1, printf("*opt= %d\n", *opt)); + + switch (*opt) + { + case 0x0: /* End of Option list */ + return NW_OK; + case 0x1: /* No Operation */ + i++; + opt++; + break; + case 0x82: /* Security */ + if (security_present) + return EINVAL; + security_present= TRUE; + if (opt[1] != 11) + return EINVAL; + i += opt[1]; + opt += opt[1]; + break; + case 0x83: /* Lose Source and Record Route */ + if (lose_source_present) + { + DBLOCK(1, printf("snd lose soruce route\n")); + return EINVAL; + } + lose_source_present= TRUE; + if (opt[1]<3) + { + DBLOCK(1, + printf("wrong length in source route\n")); + return EINVAL; + } + i += opt[1]; + opt += opt[1]; + break; + case 0x89: /* Strict Source and Record Route */ + if (strict_source_present) + return EINVAL; + strict_source_present= TRUE; + if (opt[1]<3) + return EINVAL; + i += opt[1]; + opt += opt[1]; + break; + case 0x7: /* Record Route */ + if (record_route_present) + return EINVAL; + record_route_present= TRUE; + if (opt[1]<3) + return EINVAL; + i += opt[1]; + opt += opt[1]; + break; + case 0x88: + if (timestamp_present) + return EINVAL; + timestamp_present= TRUE; + if (opt[1] != 4) + return EINVAL; + switch (opt[3] & 0xff) + { + case 0: + case 1: + case 3: + break; + default: + return EINVAL; + } + i += opt[1]; + opt += opt[1]; + break; + default: + return EINVAL; + } + } + if (i > optlen) + { + DBLOCK(1, printf("option of wrong length\n")); + return EINVAL; + } + return NW_OK; +} + +PUBLIC void ip_print_frags(acc) +acc_t *acc; +{ +#if DEBUG + ip_hdr_t *ip_hdr; + int first; + + if (!acc) + printf("(null)"); + + for (first= 1; acc; acc= acc->acc_ext_link, first= 0) + { +assert (acc->acc_length >= IP_MIN_HDR_SIZE); + ip_hdr= (ip_hdr_t *)ptr2acc_data(acc); + if (first) + { + writeIpAddr(ip_hdr->ih_src); + printf(" > "); + writeIpAddr(ip_hdr->ih_dst); + } + printf(" {%x:%d@%d%c}", ntohs(ip_hdr->ih_id), + ntohs(ip_hdr->ih_length), + (ntohs(ip_hdr->ih_flags_fragoff) & IH_FRAGOFF_MASK)*8, + (ntohs(ip_hdr->ih_flags_fragoff) & IH_MORE_FRAGS) ? + '+' : '\0'); + } +#endif +} + +PUBLIC ipaddr_t ip_get_ifaddr(port_nr) +int port_nr; +{ + assert(port_nr >= 0 && port_nr < ip_conf_nr); + + return ip_port_table[port_nr].ip_ipaddr; +} + +PUBLIC nettype_t ip_nettype(ipaddr) +ipaddr_t ipaddr; +{ + u8_t highbyte; + nettype_t nettype; + + ipaddr= ntohl(ipaddr); + highbyte= (ipaddr >> 24) & 0xff; + if (highbyte == 0) + { + if (ipaddr == 0) + nettype= IPNT_ZERO; + else + nettype= IPNT_MARTIAN; + } + else if (highbyte < 127) + nettype= IPNT_CLASS_A; + else if (highbyte == 127) + nettype= IPNT_LOCAL; + else if (highbyte < 192) + nettype= IPNT_CLASS_B; + else if (highbyte < 224) + nettype= IPNT_CLASS_C; + else if (highbyte < 240) + nettype= IPNT_CLASS_D; + else if (highbyte < 248) + nettype= IPNT_CLASS_E; + else if (highbyte < 255) + nettype= IPNT_MARTIAN; + else + { + if (ipaddr == (ipaddr_t)-1) + nettype= IPNT_BROADCAST; + else + nettype= IPNT_MARTIAN; + } + return nettype; +} + +PUBLIC ipaddr_t ip_netmask(nettype) +nettype_t nettype; +{ + switch(nettype) + { + case IPNT_ZERO: return HTONL(0x00000000); + case IPNT_CLASS_A: + case IPNT_LOCAL: return HTONL(0xff000000); + case IPNT_CLASS_B: return HTONL(0xffff0000); + case IPNT_CLASS_C: return HTONL(0xffffff00); + default: return HTONL(0xffffffff); + } +} + +#if 0 +PUBLIC char *ip_nettoa(nettype) +nettype_t nettype; +{ + switch(nettype) + { + case IPNT_ZERO: return "zero"; + case IPNT_CLASS_A: return "class A"; + case IPNT_LOCAL: return "local"; + case IPNT_CLASS_B: return "class B"; + case IPNT_CLASS_C: return "class C"; + case IPNT_CLASS_D: return "class D"; + case IPNT_CLASS_E: return "class E"; + case IPNT_MARTIAN: return "martian"; + case IPNT_BROADCAST: return "broadcast"; + default: return "<unknown>"; + } +} +#endif + +/* + * $PchId: ip_lib.c,v 1.6 1996/12/17 07:59:36 philip Exp $ + */ diff --git a/servers/inet/generic/ip_ps.c b/servers/inet/generic/ip_ps.c new file mode 100644 index 000000000..853e39b31 --- /dev/null +++ b/servers/inet/generic/ip_ps.c @@ -0,0 +1,148 @@ +/* +generic/ip_ps.c + +pseudo IP specific part of the IP implementation + +Created: Apr 23, 1993 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "assert.h" +#include "type.h" +#include "buf.h" +#include "event.h" +#include "ip.h" +#include "ip_int.h" +#include "psip.h" + +THIS_FILE + +FORWARD void ipps_main ARGS(( ip_port_t *ip_port )); +FORWARD void ipps_set_ipaddr ARGS(( ip_port_t *ip_port )); +FORWARD int ipps_send ARGS(( struct ip_port *ip_port, ipaddr_t dest, + acc_t *pack, int broadcast )); + +PUBLIC int ipps_init(ip_port) +ip_port_t *ip_port; +{ + int result; + + result= psip_enable(ip_port->ip_dl.dl_ps.ps_port, ip_port->ip_port); + if (result == -1) + return -1; +#if ZERO + ip_port->ip_dl.dl_ps.ps_send_head= NULL; + ip_port->ip_dl.dl_ps.ps_send_tail= NULL; +#endif + ip_port->ip_dev_main= ipps_main; + ip_port->ip_dev_set_ipaddr= ipps_set_ipaddr; + ip_port->ip_dev_send= ipps_send; + return result; +} + +PUBLIC void ipps_get(ip_port_nr) +int ip_port_nr; +{ + int result; + acc_t *pack; + ip_port_t *ip_port; + + assert(ip_port_nr >= 0 && ip_port_nr < ip_conf_nr); + ip_port= &ip_port_table[ip_port_nr]; + assert(ip_port->ip_dl_type == IPDL_PSIP); + + while (ip_port->ip_dl.dl_ps.ps_send_head != NULL) + { + pack= ip_port->ip_dl.dl_ps.ps_send_head; + ip_port->ip_dl.dl_ps.ps_send_head= pack->acc_ext_link; + result= psip_send(ip_port->ip_dl.dl_ps.ps_port, pack); + if (result != NW_SUSPEND) + { + assert(result == NW_OK); + continue; + } + pack->acc_ext_link= ip_port->ip_dl.dl_ps.ps_send_head; + ip_port->ip_dl.dl_ps.ps_send_head= pack; + if (pack->acc_ext_link == NULL) + ip_port->ip_dl.dl_ps.ps_send_tail= pack; + break; + } +} + +PUBLIC void ipps_put(ip_port_nr, pack) +int ip_port_nr; +acc_t *pack; +{ + ip_port_t *ip_port; + + assert(ip_port_nr >= 0 && ip_port_nr < ip_conf_nr); + ip_port= &ip_port_table[ip_port_nr]; + assert(ip_port->ip_dl_type == IPDL_PSIP); + ip_arrived(ip_port, pack); +} + +PRIVATE void ipps_main(ip_port) +ip_port_t *ip_port; +{ + /* nothing to do */ +} + +PRIVATE void ipps_set_ipaddr(ip_port) +ip_port_t *ip_port; +{ + int i; + ip_fd_t *ip_fd; + + /* revive calls waiting for an ip addresses */ + for (i=0, ip_fd= ip_fd_table; i<IP_FD_NR; i++, ip_fd++) + { + if (!(ip_fd->if_flags & IFF_INUSE)) + { + continue; + } + if (ip_fd->if_port != ip_port) + { + continue; + } + if (ip_fd->if_flags & IFF_GIPCONF_IP) + { + ip_ioctl (i, NWIOGIPCONF); + } + } +} + +PRIVATE int ipps_send(ip_port, dest, pack, broadcast) +struct ip_port *ip_port; +ipaddr_t dest; +acc_t *pack; +int broadcast; +{ + int result; + + if (broadcast) + ip_arrived_broadcast(ip_port, bf_dupacc(pack)); + + if (ip_port->ip_dl.dl_ps.ps_send_head == NULL) + { + result= psip_send(ip_port->ip_dl.dl_ps.ps_port, pack); + if (result != NW_SUSPEND) + { + assert(result == NW_OK); + return result; + } + assert (ip_port->ip_dl.dl_ps.ps_send_head == NULL); + ip_port->ip_dl.dl_ps.ps_send_head= pack; + } + else + ip_port->ip_dl.dl_ps.ps_send_tail->acc_ext_link= pack; + ip_port->ip_dl.dl_ps.ps_send_tail= pack; + pack->acc_ext_link= NULL; + + return NW_OK; +} + +/* + * $PchId: ip_ps.c,v 1.5 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/ip_read.c b/servers/inet/generic/ip_read.c new file mode 100644 index 000000000..0937f0039 --- /dev/null +++ b/servers/inet/generic/ip_read.c @@ -0,0 +1,864 @@ +/* +ip_read.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "buf.h" +#include "clock.h" +#include "event.h" +#include "type.h" + +#include "assert.h" +#include "icmp_lib.h" +#include "io.h" +#include "ip.h" +#include "ip_int.h" +#include "ipr.h" + +THIS_FILE + +FORWARD ip_ass_t *find_ass_ent ARGS(( ip_port_t *ip_port, U16_t id, + int proto, ipaddr_t src, ipaddr_t dst )); +FORWARD acc_t *merge_frags ARGS(( acc_t *first, acc_t *second )); +FORWARD int ip_frag_chk ARGS(( acc_t *pack )); +FORWARD acc_t *reassemble ARGS(( ip_port_t *ip_port, acc_t *pack, + ip_hdr_t *ip_hdr )); +FORWARD int broadcast_dst ARGS(( ip_port_t *ip_port, ipaddr_t dest )); +FORWARD void packet2user ARGS(( ip_fd_t *ip_fd, acc_t *pack, + time_t exp_time )); + +PUBLIC int ip_read (fd, count) +int fd; +size_t count; +{ + ip_fd_t *ip_fd; + acc_t *pack; + + ip_fd= &ip_fd_table[fd]; + if (!(ip_fd->if_flags & IFF_OPTSET)) + return (*ip_fd->if_put_userdata)(ip_fd->if_srfd, EBADMODE, + (acc_t *)0, FALSE); + + ip_fd->if_rd_count= count; + + ip_fd->if_flags |= IFF_READ_IP; + if (ip_fd->if_rdbuf_head) + { + if (get_time() <= ip_fd->if_exp_time) + { + pack= ip_fd->if_rdbuf_head; + ip_fd->if_rdbuf_head= pack->acc_ext_link; + packet2user (ip_fd, pack, ip_fd->if_exp_time); + assert(!(ip_fd->if_flags & IFF_READ_IP)); + return NW_OK; + } + while (ip_fd->if_rdbuf_head) + { + pack= ip_fd->if_rdbuf_head; + ip_fd->if_rdbuf_head= pack->acc_ext_link; + bf_afree(pack); + } + } + return NW_SUSPEND; +} + +PRIVATE acc_t *reassemble (ip_port, pack, pack_hdr) +ip_port_t *ip_port; +acc_t *pack; +ip_hdr_t *pack_hdr; +{ + ip_ass_t *ass_ent; + size_t pack_hdr_len, pack_data_len, pack_offset, tmp_offset; + u16_t pack_flags_fragoff; + acc_t *prev_acc, *curr_acc, *next_acc, *head_acc, *tmp_acc; + ip_hdr_t *tmp_hdr; + time_t first_time; + + ass_ent= find_ass_ent (ip_port, pack_hdr->ih_id, + pack_hdr->ih_proto, pack_hdr->ih_src, pack_hdr->ih_dst); + + pack_flags_fragoff= ntohs(pack_hdr->ih_flags_fragoff); + pack_hdr_len= (pack_hdr->ih_vers_ihl & IH_IHL_MASK) * 4; + pack_data_len= ntohs(pack_hdr->ih_length)-pack_hdr_len; + pack_offset= (pack_flags_fragoff & IH_FRAGOFF_MASK)*8; + pack->acc_ext_link= NULL; + + head_acc= ass_ent->ia_frags; + ass_ent->ia_frags= NULL; + if (head_acc == NULL) + { + ass_ent->ia_frags= pack; + return NULL; + } + + prev_acc= NULL; + curr_acc= NULL; + next_acc= head_acc; + + while(next_acc) + { + tmp_hdr= (ip_hdr_t *)ptr2acc_data(next_acc); + tmp_offset= (ntohs(tmp_hdr->ih_flags_fragoff) & + IH_FRAGOFF_MASK)*8; + + if (pack_offset < tmp_offset) + break; + + prev_acc= curr_acc; + curr_acc= next_acc; + next_acc= next_acc->acc_ext_link; + } + if (curr_acc == NULL) + { + assert(prev_acc == NULL); + assert(next_acc != NULL); + + curr_acc= merge_frags(pack, next_acc); + head_acc= curr_acc; + } + else + { + curr_acc= merge_frags(curr_acc, pack); + if (next_acc != NULL) + curr_acc= merge_frags(curr_acc, next_acc); + if (prev_acc != NULL) + prev_acc->acc_ext_link= curr_acc; + else + head_acc= curr_acc; + } + ass_ent->ia_frags= head_acc; + + pack= ass_ent->ia_frags; + pack_hdr= (ip_hdr_t *)ptr2acc_data(pack); + pack_flags_fragoff= ntohs(pack_hdr->ih_flags_fragoff); + + if (!(pack_flags_fragoff & (IH_FRAGOFF_MASK|IH_MORE_FRAGS))) + /* it's now a complete packet */ + { + first_time= ass_ent->ia_first_time; + + ass_ent->ia_frags= 0; + ass_ent->ia_first_time= 0; + + while (pack->acc_ext_link) + { + tmp_acc= pack->acc_ext_link; + pack->acc_ext_link= tmp_acc->acc_ext_link; + bf_afree(tmp_acc); + } + if ((ass_ent->ia_min_ttl) * HZ + first_time < + get_time()) + icmp_snd_time_exceeded(ip_port->ip_port, pack, + ICMP_FRAG_REASSEM); + else + return pack; + } + return NULL; +} + +PRIVATE acc_t *merge_frags (first, second) +acc_t *first, *second; +{ + ip_hdr_t *first_hdr, *second_hdr; + size_t first_hdr_size, second_hdr_size, first_datasize, second_datasize, + first_offset, second_offset; + acc_t *cut_second, *tmp_acc; + + if (!second) + { + first->acc_ext_link= NULL; + return first; + } + +assert (first->acc_length >= IP_MIN_HDR_SIZE); +assert (second->acc_length >= IP_MIN_HDR_SIZE); + + first_hdr= (ip_hdr_t *)ptr2acc_data(first); + first_offset= (ntohs(first_hdr->ih_flags_fragoff) & + IH_FRAGOFF_MASK) * 8; + first_hdr_size= (first_hdr->ih_vers_ihl & IH_IHL_MASK) * 4; + first_datasize= ntohs(first_hdr->ih_length) - first_hdr_size; + + second_hdr= (ip_hdr_t *)ptr2acc_data(second); + second_offset= (ntohs(second_hdr->ih_flags_fragoff) & + IH_FRAGOFF_MASK) * 8; + second_hdr_size= (second_hdr->ih_vers_ihl & IH_IHL_MASK) * 4; + second_datasize= ntohs(second_hdr->ih_length) - second_hdr_size; + + assert (first_hdr_size + first_datasize == bf_bufsize(first)); + assert (second_hdr_size + second_datasize == bf_bufsize(second)); + assert (second_offset >= first_offset); + + if (second_offset > first_offset+first_datasize) + { + DBLOCK(1, printf("ip fragments out of order\n")); + first->acc_ext_link= second; + return first; + } + + if (second_offset + second_datasize <= first_offset + + first_datasize) + { + /* May cause problems if we try to merge. */ + bf_afree(first); + return second; + } + + if (!(second_hdr->ih_flags_fragoff & HTONS(IH_MORE_FRAGS))) + first_hdr->ih_flags_fragoff &= ~HTONS(IH_MORE_FRAGS); + + second_datasize= second_offset+second_datasize-(first_offset+ + first_datasize); + cut_second= bf_cut(second, second_hdr_size + first_offset+ + first_datasize-second_offset, second_datasize); + tmp_acc= second->acc_ext_link; + bf_afree(second); + second= tmp_acc; + + first_datasize += second_datasize; + first_hdr->ih_length= htons(first_hdr_size + first_datasize); + + first= bf_append (first, cut_second); + first->acc_ext_link= second; + +assert (first_hdr_size + first_datasize == bf_bufsize(first)); + + return first; +} + +PRIVATE ip_ass_t *find_ass_ent (ip_port, id, proto, src, dst) +ip_port_t *ip_port; +u16_t id; +ipproto_t proto; +ipaddr_t src; +ipaddr_t dst; +{ + ip_ass_t *new_ass_ent, *tmp_ass_ent; + int i; + acc_t *tmp_acc, *curr_acc; + + new_ass_ent= 0; + + for (i=0, tmp_ass_ent= ip_ass_table; i<IP_ASS_NR; i++, + tmp_ass_ent++) + { + if (!tmp_ass_ent->ia_frags && tmp_ass_ent->ia_first_time) + { + DBLOCK(1, + printf("strange ip_ass entry (can be a race condition)\n")); + continue; + } + + if ((tmp_ass_ent->ia_srcaddr == src) && + (tmp_ass_ent->ia_dstaddr == dst) && + (tmp_ass_ent->ia_proto == proto) && + (tmp_ass_ent->ia_id == id) && + (tmp_ass_ent->ia_port == ip_port)) + { + return tmp_ass_ent; + } + if (!new_ass_ent || tmp_ass_ent->ia_first_time < + new_ass_ent->ia_first_time) + { + new_ass_ent= tmp_ass_ent; + } + } + + if (new_ass_ent->ia_frags) + { + DBLOCK(1, printf("old frags id= %u, proto= %u, src= ", + ntohs(new_ass_ent->ia_id), + ntohs(new_ass_ent->ia_proto)); + writeIpAddr(new_ass_ent->ia_srcaddr); printf(" dst= "); + writeIpAddr(new_ass_ent->ia_dstaddr); printf(": "); + ip_print_frags(new_ass_ent->ia_frags); printf("\n")); + curr_acc= new_ass_ent->ia_frags->acc_ext_link; + while (curr_acc) + { + tmp_acc= curr_acc->acc_ext_link; + bf_afree(curr_acc); + curr_acc= tmp_acc; + } + curr_acc= new_ass_ent->ia_frags; + new_ass_ent->ia_frags= 0; + icmp_snd_time_exceeded(ip_port->ip_port, curr_acc, + ICMP_FRAG_REASSEM); + } + new_ass_ent->ia_min_ttl= IP_MAX_TTL; + new_ass_ent->ia_port= ip_port; + new_ass_ent->ia_first_time= get_time(); + new_ass_ent->ia_srcaddr= src; + new_ass_ent->ia_dstaddr= dst; + new_ass_ent->ia_proto= proto; + new_ass_ent->ia_id= id; + + return new_ass_ent; +} + +PRIVATE int ip_frag_chk(pack) +acc_t *pack; +{ + ip_hdr_t *ip_hdr; + int hdr_len; + + if (pack->acc_length < sizeof(ip_hdr_t)) + { + DBLOCK(1, printf("wrong length\n")); + return FALSE; + } + + ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); + + hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) * 4; + if (pack->acc_length < hdr_len) + { + DBLOCK(1, printf("wrong length\n")); + + return FALSE; + } + + if (((ip_hdr->ih_vers_ihl >> 4) & IH_VERSION_MASK) != + IP_VERSION) + { + DBLOCK(1, printf("wrong version (ih_vers_ihl=0x%x)\n", + ip_hdr->ih_vers_ihl)); + return FALSE; + } + if (ntohs(ip_hdr->ih_length) != bf_bufsize(pack)) + { + DBLOCK(1, printf("wrong size\n")); + + return FALSE; + } + if ((u16_t)~oneC_sum(0, (u16_t *)ip_hdr, hdr_len)) + { + DBLOCK(1, printf("packet with wrong checksum (= %x)\n", + (u16_t)~oneC_sum(0, (u16_t *)ip_hdr, hdr_len))); + return FALSE; + } + if (hdr_len>IP_MIN_HDR_SIZE && ip_chk_hdropt((u8_t *) + (ptr2acc_data(pack) + IP_MIN_HDR_SIZE), + hdr_len-IP_MIN_HDR_SIZE)) + { + DBLOCK(1, printf("packet with wrong options\n")); + return FALSE; + } + return TRUE; +} + +PRIVATE void packet2user (ip_fd, pack, exp_time) +ip_fd_t *ip_fd; +acc_t *pack; +time_t exp_time; +{ + acc_t *tmp_pack; + ip_hdr_t *ip_hdr; + int result, ip_hdr_len; + size_t size, transf_size; + + assert (ip_fd->if_flags & IFF_INUSE); + if (!(ip_fd->if_flags & IFF_READ_IP)) + { + if (pack->acc_linkC != 1) + { + tmp_pack= bf_dupacc(pack); + bf_afree(pack); + pack= tmp_pack; + tmp_pack= NULL; + } + pack->acc_ext_link= NULL; + if (ip_fd->if_rdbuf_head == NULL) + { + ip_fd->if_rdbuf_head= pack; + ip_fd->if_exp_time= exp_time; + } + else + ip_fd->if_rdbuf_tail->acc_ext_link= pack; + ip_fd->if_rdbuf_tail= pack; + return; + } + + size= bf_bufsize (pack); + if (ip_fd->if_ipopt.nwio_flags & NWIO_RWDATONLY) + { + + pack= bf_packIffLess (pack, IP_MIN_HDR_SIZE); + assert (pack->acc_length >= IP_MIN_HDR_SIZE); + + ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); + ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) * 4; + + assert (size >= ip_hdr_len); + size -= ip_hdr_len; + pack= bf_delhead(pack, ip_hdr_len); + } + + if (size>ip_fd->if_rd_count) + { + tmp_pack= bf_cut (pack, 0, ip_fd->if_rd_count); + bf_afree(pack); + pack= tmp_pack; + transf_size= ip_fd->if_rd_count; + } + else + transf_size= size; + + if (ip_fd->if_put_pkt) + { + (*ip_fd->if_put_pkt)(ip_fd->if_srfd, pack, transf_size); + return; + } + + result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, + (size_t)0, pack, FALSE); + if (result >= 0) + if (size > transf_size) + result= EPACKSIZE; + else + result= transf_size; + + ip_fd->if_flags &= ~IFF_READ_IP; + result= (*ip_fd->if_put_userdata)(ip_fd->if_srfd, result, + (acc_t *)0, FALSE); + assert (result >= 0); + return; +} + +PUBLIC void ip_port_arrive (ip_port, pack, ip_hdr) +ip_port_t *ip_port; +acc_t *pack; +ip_hdr_t *ip_hdr; +{ + ip_fd_t *ip_fd, *first_fd, *share_fd; + ip_hdr_t *hdr; + int port_nr; + unsigned long ip_pack_stat; + int i; + int hash, proto; + time_t exp_time; + + assert (pack->acc_linkC>0); + assert (pack->acc_length >= IP_MIN_HDR_SIZE); + + if (ntohs(ip_hdr->ih_flags_fragoff) & (IH_FRAGOFF_MASK|IH_MORE_FRAGS)) + { + pack= reassemble (ip_port, pack, ip_hdr); + if (!pack) + return; + assert (pack->acc_length >= IP_MIN_HDR_SIZE); + ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); + assert (!(ntohs(ip_hdr->ih_flags_fragoff) & + (IH_FRAGOFF_MASK|IH_MORE_FRAGS))); + } + + exp_time= get_time() + (ip_hdr->ih_ttl+1) * HZ; + + if (ip_hdr->ih_dst == ip_port->ip_ipaddr) + ip_pack_stat= NWIO_EN_LOC; + else + ip_pack_stat= NWIO_EN_BROAD; + + proto= ip_hdr->ih_proto; + hash= proto & (IP_PROTO_HASH_NR-1); + + first_fd= NULL; + for (i= 0; i<2; i++) + { + share_fd= NULL; + + ip_fd= (i == 0) ? ip_port->ip_proto_any : + ip_port->ip_proto[hash]; + for (; ip_fd; ip_fd= ip_fd->if_proto_next) + { + if (i && ip_fd->if_ipopt.nwio_proto != proto) + continue; + if (!(ip_fd->if_ipopt.nwio_flags & ip_pack_stat)) + continue; + if ((ip_fd->if_ipopt.nwio_flags & NWIO_REMSPEC) && + ip_hdr->ih_src != ip_fd->if_ipopt.nwio_rem) + { + continue; + } + if ((ip_fd->if_ipopt.nwio_flags & NWIO_ACC_MASK) == + NWIO_SHARED) + { + if (!share_fd) + { + share_fd= ip_fd; + continue; + } + if (!ip_fd->if_rdbuf_head) + share_fd= ip_fd; + continue; + } + if (!first_fd) + { + first_fd= ip_fd; + continue; + } + pack->acc_linkC++; + packet2user(ip_fd, pack, exp_time); + + } + if (share_fd) + { + pack->acc_linkC++; + packet2user(share_fd, pack, exp_time); + } + } + if (first_fd) + { + if (first_fd->if_put_pkt && + (first_fd->if_flags & IFF_READ_IP) && + !(first_fd->if_ipopt.nwio_flags & NWIO_RWDATONLY)) + { + (*first_fd->if_put_pkt)(first_fd->if_srfd, pack, + ntohs(ip_hdr->ih_length)); + } + else + packet2user(first_fd, pack, exp_time); + } + else + { + if (ip_pack_stat == NWIO_EN_LOC) + { + DBLOCK(0x01, + printf("ip_port_arrive: dropping packet for proto %d\n", + proto)); + } + else + { + DBLOCK(0x20, printf("dropping packet for proto %d\n", + proto)); + } + bf_afree(pack); + } +} + +PUBLIC void ip_arrived(ip_port, pack) +ip_port_t *ip_port; +acc_t *pack; +{ + ip_port_t *next_port; + ip_hdr_t *ip_hdr; + iroute_t *iroute; + ipaddr_t dest; + nettype_t nettype; + int ip_frag_len, ip_hdr_len; + size_t pack_size; + acc_t *tmp_pack; + int broadcast; + + pack_size= bf_bufsize(pack); + + if (pack_size < IP_MIN_HDR_SIZE) + { + DBLOCK(1, printf("wrong acc_length\n")); + bf_afree(pack); + return; + } + pack= bf_align(pack, IP_MIN_HDR_SIZE, 4); + pack= bf_packIffLess(pack, IP_MIN_HDR_SIZE); +assert (pack->acc_length >= IP_MIN_HDR_SIZE); + + ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); + ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; + if (ip_hdr_len>IP_MIN_HDR_SIZE) + { + pack= bf_align(pack, IP_MIN_HDR_SIZE, 4); + pack= bf_packIffLess(pack, ip_hdr_len); + ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); + } + ip_frag_len= ntohs(ip_hdr->ih_length); + if (ip_frag_len<pack_size) + { + tmp_pack= pack; + pack= bf_cut(tmp_pack, 0, ip_frag_len); + bf_afree(tmp_pack); + } + + if (!ip_frag_chk(pack)) + { + DBLOCK(1, printf("fragment not allright\n")); + bf_afree(pack); + return; + } + + /* Decide about local delivery or routing. Local delivery can happen + * when the destination is the local ip address, or one of the + * broadcast addresses and the packet happens to be delivered + * point-to-point. + */ + + dest= ip_hdr->ih_dst; + + if (dest == ip_port->ip_ipaddr) + { + ip_port_arrive (ip_port, pack, ip_hdr); + return; + } + if (broadcast_dst(ip_port, dest)) + { + ip_port_arrive (ip_port, pack, ip_hdr); + return; + } + + /* Try to decrement the ttl field with one. */ + if (ip_hdr->ih_ttl < 2) + { + icmp_snd_time_exceeded(ip_port->ip_port, pack, ICMP_TTL_EXC); + return; + } + ip_hdr->ih_ttl--; + ip_hdr_chksum(ip_hdr, ip_hdr_len); + + /* Avoid routing to bad destinations. */ + nettype= ip_nettype(dest); + if (nettype != IPNT_CLASS_A && nettype != IPNT_CLASS_B && nettype != + IPNT_CLASS_C) + { + /* Bogus destination address */ + if (nettype == IPNT_CLASS_D || nettype == IPNT_CLASS_E) + bf_afree(pack); + else + { + icmp_snd_unreachable(ip_port->ip_port, pack, + ICMP_HOST_UNRCH); + } + return; + } + iroute= iroute_frag(ip_port->ip_port, dest); + if (iroute == NULL || iroute->irt_dist == IRTD_UNREACHABLE) + { + /* Also unreachable */ + /* Finding out if we send a network unreachable is too much + * trouble. + */ + icmp_snd_unreachable(ip_port->ip_port, pack, + ICMP_HOST_UNRCH); + return; + } + next_port= &ip_port_table[iroute->irt_port]; + if (next_port != ip_port) + { + if (iroute->irt_gateway != 0) + { + /* Just send the packet to the next gateway */ + next_port->ip_dev_send(next_port, iroute->irt_gateway, + pack, /* no bradcast */ 0); + return; + } + /* The packet is for the attached network. Special addresses + * are the ip address of the interface and net.0 if + * no IP_42BSD_BCAST. + */ + if (dest == next_port->ip_ipaddr) + { + ip_port_arrive (next_port, pack, ip_hdr); + return; + } + if (dest == iroute->irt_dest) + { +#if IP_42BSD_BCAST + broadcast= 1; +#else + /* Bogus destination address */ + icmp_snd_dstunrch(pack); + return; +#endif + } + else if (dest == (iroute->irt_dest | ~iroute->irt_subnetmask)) + broadcast= 1; + else + broadcast= 0; + + /* Just send the packet to it's destination */ + next_port->ip_dev_send(next_port, dest, pack, broadcast); + return; + } + + /* Now we know that the packet should be route over the same network + * as it came from. If there is a next hop gateway, we can send + * the packet to that gateway and send a redirect ICMP to the sender + * if the sender is on the attached network. If there is no gateway + * complain. + */ + if (iroute->irt_gateway == 0) + { +#if !CRAMPED + printf("packet should not be here, src="); + writeIpAddr(ip_hdr->ih_src); + printf(" dst="); + writeIpAddr(ip_hdr->ih_dst); + printf("\n"); +#endif + bf_afree(pack); + return; + } + if (((ip_hdr->ih_src ^ ip_port->ip_ipaddr) & + ip_port->ip_subnetmask) == 0) + { + /* Finding out if we can send a network redirect instead of + * a host redirect is too much trouble. + */ + pack->acc_linkC++; + icmp_snd_redirect(ip_port->ip_port, pack, + ICMP_REDIRECT_HOST, iroute->irt_gateway); + } + else + { +#if !CRAMPED + printf("packet is wrongly routed, src="); + writeIpAddr(ip_hdr->ih_src); + printf(" dst="); + writeIpAddr(ip_hdr->ih_dst); + printf("\n"); +#endif + bf_afree(pack); + return; + } + ip_port->ip_dev_send(ip_port, iroute->irt_gateway, pack, + /* no broadcast */ 0); +} + +PUBLIC void ip_arrived_broadcast(ip_port, pack) +ip_port_t *ip_port; +acc_t *pack; +{ + ip_hdr_t *ip_hdr; + int ip_frag_len, ip_hdr_len; + size_t pack_size; + acc_t *tmp_pack; + + pack_size= bf_bufsize(pack); + + if (pack_size < IP_MIN_HDR_SIZE) + { + DBLOCK(1, printf("wrong acc_length\n")); + bf_afree(pack); + return; + } + pack= bf_align(pack, IP_MIN_HDR_SIZE, 4); + pack= bf_packIffLess(pack, IP_MIN_HDR_SIZE); +assert (pack->acc_length >= IP_MIN_HDR_SIZE); + + ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); + + DIFBLOCK(0x20, (ip_hdr->ih_dst & HTONL(0xf0000000)) == HTONL(0xe0000000), + printf("got multicast packet\n")); + + ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; + if (ip_hdr_len>IP_MIN_HDR_SIZE) + { + pack= bf_align(pack, IP_MIN_HDR_SIZE, 4); + pack= bf_packIffLess(pack, ip_hdr_len); + ip_hdr= (ip_hdr_t *)ptr2acc_data(pack); + } + ip_frag_len= ntohs(ip_hdr->ih_length); + if (ip_frag_len<pack_size) + { + tmp_pack= pack; + pack= bf_cut(tmp_pack, 0, ip_frag_len); + bf_afree(tmp_pack); + } + + if (!ip_frag_chk(pack)) + { + DBLOCK(1, printf("fragment not allright\n")); + bf_afree(pack); + return; + } + + if (!broadcast_dst(ip_port, ip_hdr->ih_dst)) + { +#if !CRAMPED + printf( + "ip[%d]: broadcast packet for ip-nonbroadcast addr, src=", + ip_port->ip_port); + writeIpAddr(ip_hdr->ih_src); + printf(" dst="); + writeIpAddr(ip_hdr->ih_dst); + printf("\n"); +#endif + bf_afree(pack); + return; + } + + ip_port_arrive (ip_port, pack, ip_hdr); +} + +PRIVATE int broadcast_dst(ip_port, dest) +ip_port_t *ip_port; +ipaddr_t dest; +{ + /* Treat class D (multicast) address as broadcasts. */ + if ((dest & HTONL(0xF0000000)) == HTONL(0xE0000000)) + { + return 1; + } + + /* Accept without complaint if netmask not yet configured. */ + if (!(ip_port->ip_flags & IPF_NETMASKSET)) + { + return 1; + } + + if (((ip_port->ip_ipaddr ^ dest) & ip_port->ip_netmask) != 0) + { + /* Two possibilities, 0 (iff IP_42BSD_BCAST) and -1 */ + if (dest == HTONL((ipaddr_t)-1)) + return 1; +#if IP_42BSD_BCAST + if (dest == HTONL((ipaddr_t)0)) + return 1; +#endif + return 0; + } + if (((ip_port->ip_ipaddr ^ dest) & ip_port->ip_subnetmask) != 0) + { + /* Two possibilities, netwerk.0 (iff IP_42BSD_BCAST) and + * netwerk.-1 + */ + if ((dest & ~ip_port->ip_netmask) == ~ip_port->ip_netmask) + return 1; +#if IP_42BSD_BCAST + if ((dest & ~ip_port->ip_netmask) == 0) + return 1; +#endif + return 0; + } + + /* Two possibilities, netwerk.subnet.0 (iff IP_42BSD_BCAST) and + * netwerk.subnet.-1 + */ + if ((dest & ~ip_port->ip_subnetmask) == ~ip_port->ip_subnetmask) + return 1; +#if IP_42BSD_BCAST + if ((dest & ~ip_port->ip_subnetmask) == 0) + return 1; +#endif + return 0; +} + +void ip_process_loopb(ev, arg) +event_t *ev; +ev_arg_t arg; +{ + ip_port_t *ip_port; + acc_t *pack; + + ip_port= arg.ev_ptr; + assert(ev == &ip_port->ip_loopb_event); + + while(pack= ip_port->ip_loopb_head) + { + ip_port->ip_loopb_head= pack->acc_ext_link; + ip_arrived(ip_port, pack); + } +} + +/* + * $PchId: ip_read.c,v 1.9 1997/01/31 08:51:39 philip Exp $ + */ diff --git a/servers/inet/generic/ip_write.c b/servers/inet/generic/ip_write.c new file mode 100644 index 000000000..8840e57d7 --- /dev/null +++ b/servers/inet/generic/ip_write.c @@ -0,0 +1,451 @@ +/* +ip_write.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "buf.h" +#include "event.h" +#include "type.h" + +#include "arp.h" +#include "assert.h" +#include "clock.h" +#include "eth.h" +#include "icmp_lib.h" +#include "io.h" +#include "ip.h" +#include "ip_int.h" +#include "ipr.h" + +THIS_FILE + +FORWARD void error_reply ARGS(( ip_fd_t *fd, int error )); + +PUBLIC int ip_write (fd, count) +int fd; +size_t count; +{ + ip_fd_t *ip_fd; + acc_t *pack; + int r; + + ip_fd= &ip_fd_table[fd]; + if (count > IP_MAX_PACKSIZE) + { + error_reply (ip_fd, EPACKSIZE); + return NW_OK; + } + pack= (*ip_fd->if_get_userdata)(ip_fd->if_srfd, (size_t)0, + count, FALSE); + if (!pack) + return NW_OK; + r= ip_send(fd, pack, count); + assert(r != NW_WOULDBLOCK); + + if (r == NW_OK) + error_reply (ip_fd, count); + else + error_reply (ip_fd, r); + return NW_OK; +} + +PUBLIC int ip_send(fd, data, data_len) +int fd; +acc_t *data; +size_t data_len; +{ + ip_port_t *ip_port; + ip_fd_t *ip_fd; + ip_hdr_t *ip_hdr, *tmp_hdr; + ipaddr_t dstaddr, netmask, nexthop, hostrep_dst; + u8_t *addrInBytes; + acc_t *tmp_pack, *tmp_pack1; + int hdr_len, hdr_opt_len, r; + int broadcast, ttl; + ev_arg_t arg; + + ip_fd= &ip_fd_table[fd]; + ip_port= ip_fd->if_port; + + if (!(ip_fd->if_flags & IFF_OPTSET)) + { + bf_afree(data); + return EBADMODE; + } + + data_len= bf_bufsize(data); + + assert(ip_fd->if_port->ip_flags & IPF_IPADDRSET); + + if (ip_fd->if_ipopt.nwio_flags & NWIO_RWDATONLY) + { + tmp_pack= bf_memreq(IP_MIN_HDR_SIZE); + tmp_pack->acc_next= data; + data= tmp_pack; + data_len += IP_MIN_HDR_SIZE; + } + if (data_len<IP_MIN_HDR_SIZE) + { + bf_afree(data); + return EPACKSIZE; + } + + data= bf_packIffLess(data, IP_MIN_HDR_SIZE); + ip_hdr= (ip_hdr_t *)ptr2acc_data(data); + if (data->acc_linkC != 1 || data->acc_buffer->buf_linkC != 1) + { + tmp_pack= bf_memreq(IP_MIN_HDR_SIZE); + tmp_hdr= (ip_hdr_t *)ptr2acc_data(tmp_pack); + *tmp_hdr= *ip_hdr; + tmp_pack->acc_next= bf_cut(data, IP_MIN_HDR_SIZE, + data_len-IP_MIN_HDR_SIZE); + bf_afree(data); + ip_hdr= tmp_hdr; + data= tmp_pack; + assert (data->acc_length >= IP_MIN_HDR_SIZE); + } + + if (ip_fd->if_ipopt.nwio_flags & NWIO_HDR_O_SPEC) + { + hdr_opt_len= ip_fd->if_ipopt.nwio_hdropt.iho_opt_siz; + if (hdr_opt_len) + { + tmp_pack= bf_cut(data, 0, IP_MIN_HDR_SIZE); + tmp_pack1= bf_cut (data, IP_MIN_HDR_SIZE, + data_len-IP_MIN_HDR_SIZE); + bf_afree(data); + data= bf_packIffLess(tmp_pack, IP_MIN_HDR_SIZE); + ip_hdr= (ip_hdr_t *)ptr2acc_data(data); + tmp_pack= bf_memreq (hdr_opt_len); + memcpy (ptr2acc_data(tmp_pack), ip_fd->if_ipopt. + nwio_hdropt.iho_data, hdr_opt_len); + data->acc_next= tmp_pack; + tmp_pack->acc_next= tmp_pack1; + hdr_len= IP_MIN_HDR_SIZE+hdr_opt_len; + } + else + hdr_len= IP_MIN_HDR_SIZE; + ip_hdr->ih_vers_ihl= hdr_len/4; + ip_hdr->ih_tos= ip_fd->if_ipopt.nwio_tos; + ip_hdr->ih_flags_fragoff= 0; + if (ip_fd->if_ipopt.nwio_df) + ip_hdr->ih_flags_fragoff |= HTONS(IH_DONT_FRAG); + ip_hdr->ih_ttl= ip_fd->if_ipopt.nwio_ttl; + ttl= ORTD_UNREACHABLE+1; /* Don't check TTL */ + } + else + { + hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK)*4; + r= NW_OK; + if (hdr_len<IP_MIN_HDR_SIZE) + r= EINVAL; + else if (hdr_len>data_len) + r= EPACKSIZE; + else if (!ip_hdr->ih_ttl) + r= EINVAL; + if (r != NW_OK) + { + bf_afree(data); + return r; + } + + data= bf_packIffLess(data, hdr_len); + ip_hdr= (ip_hdr_t *)ptr2acc_data(data); + if (hdr_len != IP_MIN_HDR_SIZE) + { + r= ip_chk_hdropt((u8_t *)(ptr2acc_data(data) + + IP_MIN_HDR_SIZE), + hdr_len-IP_MIN_HDR_SIZE); + if (r != NW_OK) + { + bf_afree(data); + return r; + } + } + ttl= ip_hdr->ih_ttl; + } + + ip_hdr->ih_vers_ihl= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) | + (IP_VERSION << 4); + ip_hdr->ih_length= htons(data_len); + ip_hdr->ih_flags_fragoff &= ~HTONS(IH_FRAGOFF_MASK | + IH_FLAGS_UNUSED | IH_MORE_FRAGS); + if (ip_fd->if_ipopt.nwio_flags & NWIO_PROTOSPEC) + ip_hdr->ih_proto= ip_fd->if_ipopt.nwio_proto; + ip_hdr->ih_id= htons(ip_port->ip_frame_id++); + ip_hdr->ih_src= ip_fd->if_port->ip_ipaddr; + if (ip_fd->if_ipopt.nwio_flags & NWIO_REMSPEC) + ip_hdr->ih_dst= ip_fd->if_ipopt.nwio_rem; + + dstaddr= ip_hdr->ih_dst; + hostrep_dst= ntohl(dstaddr); + r= 0; + if (hostrep_dst == (ipaddr_t)-1) + ; /* OK, local broadcast */ + else if ((hostrep_dst & 0xe0000000l) == 0xe0000000l) + ; /* OK, Multicast */ + else if ((hostrep_dst & 0xf0000000l) == 0xf0000000l) + r= EBADDEST; /* Bad class */ + else if ((dstaddr ^ ip_port->ip_ipaddr) & ip_port->ip_subnetmask) + ; /* OK, remote destination */ + else if (!(dstaddr & ~ip_port->ip_subnetmask)) + r= EBADDEST; /* Zero host part */ + if (r<0) + { + DIFBLOCK(1, r == EBADDEST, + printf("bad destination: "); + writeIpAddr(ip_hdr->ih_dst); + printf("\n")); + bf_afree(data); + return r; + } + ip_hdr_chksum(ip_hdr, hdr_len); + + data= bf_packIffLess(data, IP_MIN_HDR_SIZE); + assert (data->acc_length >= IP_MIN_HDR_SIZE); + ip_hdr= (ip_hdr_t *)ptr2acc_data(data); + + addrInBytes= (u8_t *)&dstaddr; + + if ((addrInBytes[0] & 0xff) == 0x7f) /* local loopback */ + { + assert (data->acc_linkC == 1); + dstaddr= ip_hdr->ih_dst; /* swap src and dst + * addresses */ + ip_hdr->ih_dst= ip_hdr->ih_src; + ip_hdr->ih_src= dstaddr; + data->acc_ext_link= NULL; + if (ip_port->ip_loopb_head == NULL) + { + ip_port->ip_loopb_head= data; + arg.ev_ptr= ip_port; + ev_enqueue(&ip_port->ip_loopb_event, + ip_process_loopb, arg); + } + else + ip_port->ip_loopb_tail->acc_ext_link= data; + ip_port->ip_loopb_tail= data; + + return NW_OK; + } + + if (dstaddr == (ipaddr_t)-1) + { + r= (*ip_port->ip_dev_send)(ip_port, dstaddr, data, + /* broadcast */ 1); + return r; + } + netmask= ip_get_netmask(dstaddr); + + if (dstaddr == ip_port->ip_ipaddr) + { + assert (data->acc_linkC == 1); + + data->acc_ext_link= NULL; + if (ip_port->ip_loopb_head == NULL) + { + ip_port->ip_loopb_head= data; + arg.ev_ptr= ip_port; + ev_enqueue(&ip_port->ip_loopb_event, + ip_process_loopb, arg); + } + else + ip_port->ip_loopb_tail->acc_ext_link= data; + ip_port->ip_loopb_tail= data; + + return NW_OK; + } + + if (((dstaddr ^ ip_port->ip_ipaddr) & ip_port->ip_subnetmask) == 0) + { + broadcast= (dstaddr == (ip_port->ip_ipaddr | + ~ip_port->ip_subnetmask)); + + r= (*ip_port->ip_dev_send)(ip_port, dstaddr, data, + broadcast); + return r; + } + + r= oroute_frag (ip_port - ip_port_table, dstaddr, ttl, &nexthop); + + if (r == NW_OK) + { + if (nexthop == ip_port->ip_ipaddr) + { + data->acc_ext_link= NULL; + if (ip_port->ip_loopb_head == NULL) + { + ip_port->ip_loopb_head= data; + arg.ev_ptr= ip_port; + ev_enqueue(&ip_port->ip_loopb_event, + ip_process_loopb, arg); + } + else + ip_port->ip_loopb_tail->acc_ext_link= data; + ip_port->ip_loopb_tail= data; + } + else + { + r= (*ip_port->ip_dev_send)(ip_port, + nexthop, data, /* no broadcast */ 0); + } + } + else + { + DBLOCK(0x10, printf("got error %d\n", r)); + bf_afree(data); + } + return r; +} + +PUBLIC void ip_hdr_chksum(ip_hdr, ip_hdr_len) +ip_hdr_t *ip_hdr; +int ip_hdr_len; +{ + ip_hdr->ih_hdr_chk= 0; + ip_hdr->ih_hdr_chk= ~oneC_sum (0, (u16_t *)ip_hdr, ip_hdr_len); +} + +PUBLIC acc_t *ip_split_pack (ip_port, ref_last, first_size) +ip_port_t *ip_port; +acc_t **ref_last; +int first_size; +{ + int pack_siz; + ip_hdr_t *first_hdr, *second_hdr; + int first_hdr_len, second_hdr_len; + int first_data_len, second_data_len; + int new_first_data_len; + int first_opt_size, second_opt_size; + acc_t *first_pack, *second_pack, *tmp_pack, *tmp_pack1; + u8_t *first_optptr, *second_optptr; + int i, optlen; + + first_pack= *ref_last; + *ref_last= 0; + second_pack= 0; + + first_pack= bf_packIffLess(first_pack, IP_MIN_HDR_SIZE); + assert (first_pack->acc_length >= IP_MIN_HDR_SIZE); + + first_hdr= (ip_hdr_t *)ptr2acc_data(first_pack); + first_hdr_len= (first_hdr->ih_vers_ihl & IH_IHL_MASK) * 4; + + pack_siz= bf_bufsize(first_pack); + assert(pack_siz > first_size); + + if (first_hdr->ih_flags_fragoff & HTONS(IH_DONT_FRAG)) + { + icmp_snd_unreachable(ip_port->ip_port, first_pack, + ICMP_FRAGM_AND_DF); + return NULL; + } + + first_data_len= ntohs(first_hdr->ih_length) - first_hdr_len; + new_first_data_len= (first_size- first_hdr_len) & ~7; + /* data goes in 8 byte chuncks */ + second_data_len= first_data_len-new_first_data_len; + second_pack= bf_cut(first_pack, first_hdr_len+ + new_first_data_len, second_data_len); + tmp_pack= first_pack; + first_data_len= new_first_data_len; + first_pack= bf_cut (tmp_pack, 0, first_hdr_len+first_data_len); + bf_afree(tmp_pack); + tmp_pack= bf_memreq(first_hdr_len); + tmp_pack->acc_next= second_pack; + second_pack= tmp_pack; + second_hdr= (ip_hdr_t *)ptr2acc_data(second_pack); + *second_hdr= *first_hdr; + second_hdr->ih_flags_fragoff= htons( + ntohs(first_hdr->ih_flags_fragoff)+(first_data_len/8)); + + first_opt_size= first_hdr_len-IP_MIN_HDR_SIZE; + second_opt_size= 0; + if (first_opt_size) + { + first_pack= bf_packIffLess (first_pack, + first_hdr_len); + first_hdr= (ip_hdr_t *)ptr2acc_data(first_pack); + assert (first_pack->acc_length>=first_hdr_len); + first_optptr= (u8_t *)ptr2acc_data(first_pack)+ + IP_MIN_HDR_SIZE; + second_optptr= (u8_t *)ptr2acc_data( + second_pack)+IP_MIN_HDR_SIZE; + i= 0; + while (i<first_opt_size) + { + switch (*first_optptr & IP_OPT_NUMBER) + { + case 0: + case 1: + optlen= 1; + break; + default: + optlen= first_optptr[1]; + break; + } + assert (i + optlen <= first_opt_size); + i += optlen; + if (*first_optptr & IP_OPT_COPIED) + { + second_opt_size += optlen; + while (optlen--) + *second_optptr++= + *first_optptr++; + } + else + first_optptr += optlen; + } + while (second_opt_size & 3) + { + *second_optptr++= 0; + second_opt_size++; + } + } + second_hdr_len= IP_MIN_HDR_SIZE + second_opt_size; + + second_hdr->ih_vers_ihl= second_hdr->ih_vers_ihl & 0xf0 + + (second_hdr_len/4); + second_hdr->ih_length= htons(second_data_len+ + second_hdr_len); + second_pack->acc_length= second_hdr_len; + + assert(first_pack->acc_linkC == 1); + assert(first_pack->acc_buffer->buf_linkC == 1); + + first_hdr->ih_flags_fragoff |= HTONS(IH_MORE_FRAGS); + first_hdr->ih_length= htons(first_data_len+ + first_hdr_len); + assert (!(second_hdr->ih_flags_fragoff & HTONS(IH_DONT_FRAG))); + + ip_hdr_chksum(first_hdr, first_hdr_len); + if (second_data_len+second_hdr_len <= first_size) + { + /* second_pack will not be split any further, so we have to + * calculate the header checksum. + */ + ip_hdr_chksum(second_hdr, second_hdr_len); + } + + *ref_last= second_pack; + return first_pack; +} + +PRIVATE void error_reply (ip_fd, error) +ip_fd_t *ip_fd; +int error; +{ + if ((*ip_fd->if_get_userdata)(ip_fd->if_srfd, (size_t)error, + (size_t)0, FALSE)) + { +#if !CRAMPED + ip_panic(( "can't error_reply" )); +#endif + } +} + +/* + * $PchId: ip_write.c,v 1.7.1.1.1.1 2001/01/22 19:59:07 philip Exp $ + */ diff --git a/servers/inet/generic/ipr.c b/servers/inet/generic/ipr.c new file mode 100644 index 000000000..cb2996b9b --- /dev/null +++ b/servers/inet/generic/ipr.c @@ -0,0 +1,1016 @@ +/* +ipr.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "clock.h" + +#include "type.h" +#include "assert.h" +#include "buf.h" +#include "event.h" +#include "io.h" +#include "ip_int.h" +#include "ipr.h" + +THIS_FILE + +#define OROUTE_NR 32 +#define OROUTE_STATIC_NR 16 +#define OROUTE_HASH_ASS_NR 4 +#define OROUTE_HASH_NR 32 +#define OROUTE_HASH_MASK (OROUTE_HASH_NR-1) + +#define hash_oroute(port_nr, ipaddr, hash_tmp) (hash_tmp= (ipaddr), \ + hash_tmp= (hash_tmp >> 20) ^ hash_tmp, \ + hash_tmp= (hash_tmp >> 10) ^ hash_tmp, \ + hash_tmp= (hash_tmp >> 5) ^ hash_tmp, \ + (hash_tmp + (port_nr)) & OROUTE_HASH_MASK) + +typedef struct oroute_hash +{ + ipaddr_t orh_addr; + oroute_t *orh_route; +} oroute_hash_t; + +PRIVATE oroute_t oroute_table[OROUTE_NR]; +PRIVATE oroute_t *oroute_head; +PRIVATE int static_oroute_nr; +PRIVATE oroute_hash_t oroute_hash_table[OROUTE_HASH_NR][OROUTE_HASH_ASS_NR]; + +#define IROUTE_NR (sizeof(int) == 2 ? 64 : 512) +#define IROUTE_HASH_ASS_NR 4 +#define IROUTE_HASH_NR 32 +#define IROUTE_HASH_MASK (IROUTE_HASH_NR-1) + +#define hash_iroute(port_nr, ipaddr, hash_tmp) (hash_tmp= (ipaddr), \ + hash_tmp= (hash_tmp >> 20) ^ hash_tmp, \ + hash_tmp= (hash_tmp >> 10) ^ hash_tmp, \ + hash_tmp= (hash_tmp >> 5) ^ hash_tmp, \ + (hash_tmp + (port_nr)) & IROUTE_HASH_MASK) + +typedef struct iroute_hash +{ + ipaddr_t irh_addr; + iroute_t *irh_route; +} iroute_hash_t; + +PRIVATE iroute_t iroute_table[IROUTE_NR]; +PRIVATE iroute_hash_t iroute_hash_table[IROUTE_HASH_NR][IROUTE_HASH_ASS_NR]; + +FORWARD oroute_t *oroute_find_ent ARGS(( int port_nr, ipaddr_t dest )); +FORWARD void oroute_del ARGS(( oroute_t *oroute )); +FORWARD oroute_t *sort_dists ARGS(( oroute_t *oroute )); +FORWARD oroute_t *sort_gws ARGS(( oroute_t *oroute )); +FORWARD oroute_uncache_nw ARGS(( ipaddr_t dest, ipaddr_t netmask )); +FORWARD iroute_uncache_nw ARGS(( ipaddr_t dest, ipaddr_t netmask )); + +PUBLIC void ipr_init() +{ + int i; + oroute_t *oroute; + iroute_t *iroute; + +#if ZERO + for (i= 0, oroute= oroute_table; i<OROUTE_NR; i++, oroute++) + oroute->ort_flags= ORTF_EMPTY; + static_oroute_nr= 0; +#endif + assert(OROUTE_HASH_ASS_NR == 4); + +#if ZERO + for (i= 0, iroute= iroute_table; i<IROUTE_NR; i++, iroute++) + iroute->irt_flags= IRTF_EMPTY; +#endif + assert(IROUTE_HASH_ASS_NR == 4); +} + + +PUBLIC iroute_t *iroute_frag(port_nr, dest) +int port_nr; +ipaddr_t dest; +{ + int hash, i, r_hash_ind; + iroute_hash_t *iroute_hash; + iroute_hash_t tmp_hash; + iroute_t *iroute, *bestroute; + time_t currtim; + unsigned long hash_tmp; + + currtim= get_time(); + + hash= hash_iroute(port_nr, dest, hash_tmp); + iroute_hash= &iroute_hash_table[hash][0]; + if (iroute_hash[0].irh_addr == dest) + iroute= iroute_hash[0].irh_route; + else if (iroute_hash[1].irh_addr == dest) + { + tmp_hash= iroute_hash[1]; + iroute_hash[1]= iroute_hash[0]; + iroute_hash[0]= tmp_hash; + iroute= tmp_hash.irh_route; + } + else if (iroute_hash[2].irh_addr == dest) + { + tmp_hash= iroute_hash[2]; + iroute_hash[2]= iroute_hash[1]; + iroute_hash[1]= iroute_hash[0]; + iroute_hash[0]= tmp_hash; + iroute= tmp_hash.irh_route; + } + else if (iroute_hash[3].irh_addr == dest) + { + tmp_hash= iroute_hash[3]; + iroute_hash[3]= iroute_hash[2]; + iroute_hash[2]= iroute_hash[1]; + iroute_hash[1]= iroute_hash[0]; + iroute_hash[0]= tmp_hash; + iroute= tmp_hash.irh_route; + } + else + iroute= NULL; + if (iroute) + return iroute; + + bestroute= NULL; + for (i= 0, iroute= iroute_table; i < IROUTE_NR; i++, iroute++) + { + if (!(iroute->irt_flags & IRTF_INUSE)) + continue; + if (((dest ^ iroute->irt_dest) & iroute->irt_subnetmask) != 0) + continue; + if (!bestroute) + { + bestroute= iroute; + continue; + } + + /* More specific netmasks are better */ + if (iroute->irt_subnetmask != bestroute->irt_subnetmask) + { + if (ntohl(iroute->irt_subnetmask) > + ntohl(bestroute->irt_subnetmask)) + { + bestroute= iroute; + } + continue; + } + + /* Dynamic routes override static routes */ + if ((iroute->irt_flags & IRTF_STATIC) != + (bestroute->irt_flags & IRTF_STATIC)) + { + if (bestroute->irt_flags & IRTF_STATIC) + bestroute= iroute; + continue; + } + + /* A route to the local interface give an opportunity + * to send redirects. + */ + if (iroute->irt_port != bestroute->irt_port) + { + if (iroute->irt_port == port_nr) + bestroute= iroute; + continue; + } + } + if (bestroute == NULL) + return NULL; + + iroute_hash[3]= iroute_hash[2]; + iroute_hash[2]= iroute_hash[1]; + iroute_hash[1]= iroute_hash[0]; + iroute_hash[0].irh_addr= dest; + iroute_hash[0].irh_route= bestroute; + + return bestroute; +} + +PUBLIC int oroute_frag(port_nr, dest, ttl, nexthop) +int port_nr; +ipaddr_t dest; +int ttl; +ipaddr_t *nexthop; +{ + oroute_t *oroute; + + oroute= oroute_find_ent(port_nr, dest); + if (!oroute || oroute->ort_dist > ttl) + return EDSTNOTRCH; + + *nexthop= oroute->ort_gateway; + return NW_OK; +} + + +PUBLIC int ipr_add_oroute(port_nr, dest, subnetmask, gateway, + timeout, dist, static_route, preference, oroute_p) +int port_nr; +ipaddr_t dest; +ipaddr_t subnetmask; +ipaddr_t gateway; +time_t timeout; +int dist; +int static_route; +i32_t preference; +oroute_t **oroute_p; +{ + int i; + ip_port_t *ip_port; + oroute_t *oroute, *oldest_route, *prev, *nw_route, *gw_route, + *prev_route; + time_t currtim; + + oldest_route= 0; + currtim= get_time(); + + DBLOCK(0x10, + printf("adding oroute to "); writeIpAddr(dest); + printf("["); writeIpAddr(subnetmask); printf("] through "); + writeIpAddr(gateway); + printf(" timeout: %lds, distance %d\n", + (long)timeout/HZ, dist)); + + ip_port= &ip_port_table[port_nr]; + + /* Validate gateway */ + if (((gateway ^ ip_port->ip_ipaddr) & ip_port->ip_subnetmask) != 0) + { + DBLOCK(2, printf("ipr_add_oroute: invalid gateway: "); writeIpAddr(gateway); printf("\n")); + return EINVAL; + } + + if (static_route) + { + if (static_oroute_nr >= OROUTE_STATIC_NR) + return ENOMEM; + static_oroute_nr++; + } + else + { + /* Try to track down any old routes. */ + for(oroute= oroute_head; oroute; oroute= oroute->ort_nextnw) + { + if (oroute->ort_port != port_nr) + continue; + if (oroute->ort_dest == dest && + oroute->ort_subnetmask == subnetmask) + { + break; + } + } + for(; oroute; oroute= oroute->ort_nextgw) + { + if (oroute->ort_gateway == gateway) + break; + } + for(; oroute; oroute= oroute->ort_nextdist) + { + if ((oroute->ort_flags & ORTF_STATIC) != 0) + continue; + if (oroute->ort_dist > dist) + continue; + if (oroute->ort_dist == dist && + oroute->ort_pref == preference) + { + if (timeout) + oroute->ort_exp_tim= currtim + timeout; + else + oroute->ort_exp_tim= 0; + oroute->ort_timestamp= currtim; + assert(oroute->ort_port == port_nr); + if (oroute_p != NULL) + *oroute_p= oroute; + return NW_OK; + } + break; + } + if (oroute) + { + assert(oroute->ort_port == port_nr); + oroute_del(oroute); + oroute->ort_flags= 0; + oldest_route= oroute; + } + } + + if (oldest_route == NULL) + { + /* Look for an unused entry, or remove an existing one */ + for (i= 0, oroute= oroute_table; i<OROUTE_NR; i++, oroute++) + { + if ((oroute->ort_flags & ORTF_INUSE) == 0) + break; + if (oroute->ort_exp_tim && oroute->ort_exp_tim < + currtim) + { + oroute_del(oroute); + oroute->ort_flags= 0; + break; + } + if (oroute->ort_flags & ORTF_STATIC) + continue; + if (oroute->ort_dest == 0) + { + /* Never remove default routes. */ + continue; + } + if (oldest_route == NULL) + { + oldest_route= oroute; + continue; + } + if (oroute->ort_timestamp < oldest_route->ort_timestamp) + { + oldest_route= oroute; + } + } + if (i < OROUTE_NR) + oldest_route= oroute; + else + { + assert(oldest_route); + oroute_del(oldest_route); + oldest_route->ort_flags= 0; + } + } + + oldest_route->ort_dest= dest; + oldest_route->ort_gateway= gateway; + oldest_route->ort_subnetmask= subnetmask; + if (timeout) + oldest_route->ort_exp_tim= currtim + timeout; + else + oldest_route->ort_exp_tim= 0; + oldest_route->ort_timestamp= currtim; + oldest_route->ort_dist= dist; + oldest_route->ort_port= port_nr; + oldest_route->ort_flags= ORTF_INUSE; + oldest_route->ort_pref= preference; + if (static_route) + oldest_route->ort_flags |= ORTF_STATIC; + + /* Insert the route by tearing apart the routing table, + * and insert the entry during the reconstruction. + */ + for (prev= 0, nw_route= oroute_head; nw_route; + prev= nw_route, nw_route= nw_route->ort_nextnw) + { + if (nw_route->ort_port != port_nr) + continue; + if (nw_route->ort_dest == dest && + nw_route->ort_subnetmask == subnetmask) + { + if (prev) + prev->ort_nextnw= nw_route->ort_nextnw; + else + oroute_head= nw_route->ort_nextnw; + break; + } + } + prev_route= nw_route; + for(prev= NULL, gw_route= nw_route; gw_route; + prev= gw_route, gw_route= gw_route->ort_nextgw) + { + if (gw_route->ort_gateway == gateway) + { + if (prev) + prev->ort_nextgw= gw_route->ort_nextgw; + else + nw_route= gw_route->ort_nextgw; + break; + } + } + oldest_route->ort_nextdist= gw_route; + gw_route= oldest_route; + gw_route= sort_dists(gw_route); + gw_route->ort_nextgw= nw_route; + nw_route= gw_route; + nw_route= sort_gws(nw_route); + nw_route->ort_nextnw= oroute_head; + oroute_head= nw_route; + if (nw_route != prev_route) + oroute_uncache_nw(nw_route->ort_dest, nw_route->ort_subnetmask); + if (oroute_p != NULL) + *oroute_p= oldest_route; + return NW_OK; +} + + +PUBLIC void ipr_gateway_down(port_nr, gateway, timeout) +int port_nr; +ipaddr_t gateway; +time_t timeout; +{ + oroute_t *route_ind; + time_t currtim; + int i; + int result; + + currtim= get_time(); + for (i= 0, route_ind= oroute_table; i<OROUTE_NR; i++, route_ind++) + { + if (!(route_ind->ort_flags & ORTF_INUSE)) + continue; + if (route_ind->ort_gateway != gateway) + continue; + if (route_ind->ort_exp_tim && route_ind->ort_exp_tim < currtim) + continue; + result= ipr_add_oroute(port_nr, route_ind->ort_dest, + route_ind->ort_subnetmask, gateway, + timeout, ORTD_UNREACHABLE, FALSE, 0, NULL); + assert(result == NW_OK); + } +} + + +PUBLIC void ipr_destunrch(port_nr, dest, netmask, timeout) +int port_nr; +ipaddr_t dest; +ipaddr_t netmask; +time_t timeout; +{ + oroute_t *oroute; + int result; + + oroute= oroute_find_ent(port_nr, dest); + + if (!oroute) + { + DBLOCK(1, printf("got a dest unreachable for "); + writeIpAddr(dest); printf("but no route present\n")); + + return; + } + result= ipr_add_oroute(port_nr, dest, netmask, oroute->ort_gateway, + timeout, ORTD_UNREACHABLE, FALSE, 0, NULL); + assert(result == NW_OK); +} + + +PUBLIC void ipr_redirect(port_nr, dest, netmask, old_gateway, new_gateway, + timeout) +int port_nr; +ipaddr_t dest; +ipaddr_t netmask; +ipaddr_t old_gateway; +ipaddr_t new_gateway; +time_t timeout; +{ + oroute_t *oroute; + int result; + + oroute= oroute_find_ent(port_nr, dest); + + if (!oroute) + { + DBLOCK(1, printf("got a redirect for "); + writeIpAddr(dest); printf("but no route present\n")); + return; + } + if (oroute->ort_gateway != old_gateway) + { + DBLOCK(1, printf("got a redirect from "); + writeIpAddr(old_gateway); printf(" for "); + writeIpAddr(dest); printf(" but curr gateway is "); + writeIpAddr(oroute->ort_gateway); printf("\n")); + return; + } + if (oroute->ort_flags & ORTF_STATIC) + { + if (oroute->ort_dest == dest) + { + DBLOCK(1, printf("got a redirect for "); + writeIpAddr(dest); + printf("but route is fixed\n")); + return; + } + } + else + { + result= ipr_add_oroute(port_nr, dest, netmask, + oroute->ort_gateway, HZ, ORTD_UNREACHABLE, + FALSE, 0, NULL); + assert(result == NW_OK); + } + result= ipr_add_oroute(port_nr, dest, netmask, new_gateway, + timeout, 1, FALSE, 0, NULL); + assert(result == NW_OK); +} + + +PUBLIC void ipr_ttl_exc(port_nr, dest, netmask, timeout) +int port_nr; +ipaddr_t dest; +ipaddr_t netmask; +time_t timeout; +{ + oroute_t *oroute; + int new_dist; + int result; + + oroute= oroute_find_ent(port_nr, dest); + + if (!oroute) + { + DBLOCK(1, printf("got a ttl exceeded for "); + writeIpAddr(dest); printf("but no route present\n")); + return; + } + + new_dist= oroute->ort_dist * 2; + if (new_dist>IP_MAX_TTL) + { + new_dist= oroute->ort_dist+1; + if (new_dist>IP_MAX_TTL) + { + DBLOCK(1, printf("got a ttl exceeded for "); + writeIpAddr(dest); + printf(" but dist is %d\n", + oroute->ort_dist)); + return; + } + } + + result= ipr_add_oroute(port_nr, dest, netmask, oroute->ort_gateway, + timeout, new_dist, FALSE, 0, NULL); + assert(result == NW_OK); +} + + +PUBLIC int ipr_get_oroute(ent_no, route_ent) +int ent_no; +nwio_route_t *route_ent; +{ + oroute_t *oroute; + + if (ent_no<0 || ent_no>= OROUTE_NR) + return ENOENT; + + oroute= &oroute_table[ent_no]; + if ((oroute->ort_flags & ORTF_INUSE) && oroute->ort_exp_tim && + oroute->ort_exp_tim < get_time()) + { + oroute_del(oroute); + oroute->ort_flags &= ~ORTF_INUSE; + } + + route_ent->nwr_ent_no= ent_no; + route_ent->nwr_ent_count= OROUTE_NR; + route_ent->nwr_dest= oroute->ort_dest; + route_ent->nwr_netmask= oroute->ort_subnetmask; + route_ent->nwr_gateway= oroute->ort_gateway; + route_ent->nwr_dist= oroute->ort_dist; + route_ent->nwr_flags= NWRF_EMPTY; + if (oroute->ort_flags & ORTF_INUSE) + { + route_ent->nwr_flags |= NWRF_INUSE; + if (oroute->ort_flags & ORTF_STATIC) + route_ent->nwr_flags |= NWRF_STATIC; + } + route_ent->nwr_pref= oroute->ort_pref; + route_ent->nwr_ifaddr= ip_get_ifaddr(oroute->ort_port); + return NW_OK; +} + + +PRIVATE oroute_t *oroute_find_ent(port_nr, dest) +int port_nr; +ipaddr_t dest; +{ + int hash, i, r_hash_ind; + oroute_hash_t *oroute_hash; + oroute_hash_t tmp_hash; + oroute_t *oroute, *bestroute; + time_t currtim; + unsigned long hash_tmp; + + currtim= get_time(); + + hash= hash_oroute(port_nr, dest, hash_tmp); + oroute_hash= &oroute_hash_table[hash][0]; + if (oroute_hash[0].orh_addr == dest) + oroute= oroute_hash[0].orh_route; + else if (oroute_hash[1].orh_addr == dest) + { + tmp_hash= oroute_hash[1]; + oroute_hash[1]= oroute_hash[0]; + oroute_hash[0]= tmp_hash; + oroute= tmp_hash.orh_route; + } + else if (oroute_hash[2].orh_addr == dest) + { + tmp_hash= oroute_hash[2]; + oroute_hash[2]= oroute_hash[1]; + oroute_hash[1]= oroute_hash[0]; + oroute_hash[0]= tmp_hash; + oroute= tmp_hash.orh_route; + } + else if (oroute_hash[3].orh_addr == dest) + { + tmp_hash= oroute_hash[3]; + oroute_hash[3]= oroute_hash[2]; + oroute_hash[2]= oroute_hash[1]; + oroute_hash[1]= oroute_hash[0]; + oroute_hash[0]= tmp_hash; + oroute= tmp_hash.orh_route; + } + else + oroute= NULL; + if (oroute) + { + assert(oroute->ort_port == port_nr); + if (oroute->ort_exp_tim && oroute->ort_exp_tim<currtim) + { + oroute_del(oroute); + oroute->ort_flags &= ~ORTF_INUSE; + } + else + return oroute; + } + + bestroute= NULL; + for (oroute= oroute_head; oroute; oroute= oroute->ort_nextnw) + { + if (((dest ^ oroute->ort_dest) & oroute->ort_subnetmask) != 0) + continue; + if (oroute->ort_port != port_nr) + continue; + if (!bestroute) + { + bestroute= oroute; + continue; + } + assert(oroute->ort_dest != bestroute->ort_dest); + if (ntohl(oroute->ort_subnetmask) > + ntohl(bestroute->ort_subnetmask)) + { + bestroute= oroute; + continue; + } + } + if (bestroute == NULL) + return NULL; + + oroute_hash[3]= oroute_hash[2]; + oroute_hash[2]= oroute_hash[1]; + oroute_hash[1]= oroute_hash[0]; + oroute_hash[0].orh_addr= dest; + oroute_hash[0].orh_route= bestroute; + + return bestroute; +} + + +PRIVATE void oroute_del(oroute) +oroute_t *oroute; +{ + oroute_t *prev, *nw_route, *gw_route, *dist_route, *prev_route; + + for (prev= NULL, nw_route= oroute_head; nw_route; + prev= nw_route, nw_route= nw_route->ort_nextnw) + { + if (oroute->ort_port == nw_route->ort_port && + oroute->ort_dest == nw_route->ort_dest && + oroute->ort_subnetmask == nw_route->ort_subnetmask) + { + break; + } + } + assert(nw_route); + if (prev) + prev->ort_nextnw= nw_route->ort_nextnw; + else + oroute_head= nw_route->ort_nextnw; + prev_route= nw_route; + for (prev= NULL, gw_route= nw_route; gw_route; + prev= gw_route, gw_route= gw_route->ort_nextgw) + { + if (oroute->ort_gateway == gw_route->ort_gateway) + break; + } + assert(gw_route); + if (prev) + prev->ort_nextgw= gw_route->ort_nextgw; + else + nw_route= gw_route->ort_nextgw; + for (prev= NULL, dist_route= gw_route; dist_route; + prev= dist_route, dist_route= dist_route->ort_nextdist) + { + if (oroute == dist_route) + break; + } + assert(dist_route); + if (prev) + prev->ort_nextdist= dist_route->ort_nextdist; + else + gw_route= dist_route->ort_nextdist; + gw_route= sort_dists(gw_route); + if (gw_route != NULL) + { + gw_route->ort_nextgw= nw_route; + nw_route= gw_route; + } + nw_route= sort_gws(nw_route); + if (nw_route != NULL) + { + nw_route->ort_nextnw= oroute_head; + oroute_head= nw_route; + } + if (nw_route != prev_route) + { + oroute_uncache_nw(prev_route->ort_dest, + prev_route->ort_subnetmask); + } +} + + +PRIVATE oroute_t *sort_dists(oroute) +oroute_t *oroute; +{ + oroute_t *r, *prev, *best, *best_prev; + int best_dist, best_pref; + + best= NULL; + for (prev= NULL, r= oroute; r; prev= r, r= r->ort_nextdist) + { + if (best == NULL) + ; /* Force assignment to best */ + else if (r->ort_dist != best_dist) + { + if (r->ort_dist > best_dist) + continue; + } + else + { + if (r->ort_pref <= best_pref) + continue; + } + best= r; + best_prev= prev; + best_dist= r->ort_dist; + best_pref= r->ort_pref; + } + if (!best) + { + assert(oroute == NULL); + return oroute; + } + if (!best_prev) + { + assert(best == oroute); + return oroute; + } + best_prev->ort_nextdist= best->ort_nextdist; + best->ort_nextdist= oroute; + return best; +} + + +PRIVATE oroute_t *sort_gws(oroute) +oroute_t *oroute; +{ + oroute_t *r, *prev, *best, *best_prev; + int best_dist, best_pref; + + best= NULL; + for (prev= NULL, r= oroute; r; prev= r, r= r->ort_nextgw) + { + if (best == NULL) + ; /* Force assignment to best */ + else if (r->ort_dist != best_dist) + { + if (r->ort_dist > best_dist) + continue; + } + else + { + if (r->ort_pref <= best_pref) + continue; + } + best= r; + best_prev= prev; + best_dist= r->ort_dist; + best_pref= r->ort_pref; + } + if (!best) + { + assert(oroute == NULL); + return oroute; + } + if (!best_prev) + { + assert(best == oroute); + return oroute; + } + best_prev->ort_nextgw= best->ort_nextgw; + best->ort_nextgw= oroute; + return best; +} + + +PRIVATE oroute_uncache_nw(dest, netmask) +ipaddr_t dest; +ipaddr_t netmask; +{ + int i, j; + oroute_hash_t *oroute_hash; + + for (i= 0, oroute_hash= &oroute_hash_table[0][0]; + i<OROUTE_HASH_NR; i++, oroute_hash += OROUTE_HASH_ASS_NR) + { + for (j= 0; j<OROUTE_HASH_ASS_NR; j++) + { + if (((oroute_hash[j].orh_addr ^ dest) & netmask) == 0) + { + oroute_hash[j].orh_addr= 0; + oroute_hash[j].orh_route= NULL; + } + } + } +} + + +/* + * Input routing + */ + +PUBLIC int ipr_get_iroute(ent_no, route_ent) +int ent_no; +nwio_route_t *route_ent; +{ + iroute_t *iroute; + + if (ent_no<0 || ent_no>= IROUTE_NR) + return ENOENT; + + iroute= &iroute_table[ent_no]; + + route_ent->nwr_ent_count= IROUTE_NR; + route_ent->nwr_dest= iroute->irt_dest; + route_ent->nwr_netmask= iroute->irt_subnetmask; + route_ent->nwr_gateway= iroute->irt_gateway; + route_ent->nwr_dist= iroute->irt_dist; + route_ent->nwr_flags= NWRF_EMPTY; + if (iroute->irt_flags & IRTF_INUSE) + { + route_ent->nwr_flags |= NWRF_INUSE; + if (iroute->irt_flags & IRTF_STATIC) + route_ent->nwr_flags |= NWRF_STATIC; + if (iroute->irt_dist == IRTD_UNREACHABLE) + route_ent->nwr_flags |= NWRF_UNREACHABLE; + } + route_ent->nwr_pref= 0; + route_ent->nwr_ifaddr= ip_get_ifaddr(iroute->irt_port); + return NW_OK; +} + + +PUBLIC int ipr_add_iroute(port_nr, dest, subnetmask, gateway, + dist, static_route, iroute_p) +int port_nr; +ipaddr_t dest; +ipaddr_t subnetmask; +ipaddr_t gateway; +int dist; +int static_route; +iroute_t **iroute_p; +{ + int i; + iroute_t *iroute, *unused_route; + + unused_route= NULL; + if (static_route) + { + /* Static routes are not reused automatically, so we look + * for an unused entry. + */ + for(i= 0, iroute= iroute_table; i<IROUTE_NR; i++, iroute++) + { + if ((iroute->irt_flags & IRTF_INUSE) == 0) + break; + } + if (i != IROUTE_NR) + unused_route= iroute; + } + else + { + /* Try to track down any old routes, and look for an + * unused one. + */ + for(i= 0, iroute= iroute_table; i<IROUTE_NR; i++, iroute++) + { + if ((iroute->irt_flags & IRTF_INUSE) == 0) + { + unused_route= iroute; + continue; + } + if ((iroute->irt_flags & IRTF_STATIC) != 0) + continue; + if (iroute->irt_port != port_nr || + iroute->irt_dest != dest || + iroute->irt_subnetmask != subnetmask || + iroute->irt_gateway != gateway) + { + continue; + } + break; + } + if (i != IROUTE_NR) + unused_route= iroute; + } + + if (unused_route == NULL) + return ENOMEM; + iroute= unused_route; + + iroute->irt_port= port_nr; + iroute->irt_dest= dest; + iroute->irt_subnetmask= subnetmask; + iroute->irt_gateway= gateway; + iroute->irt_dist= dist; + iroute->irt_flags= IRTF_INUSE; + if (static_route) + iroute->irt_flags |= IRTF_STATIC; + + iroute_uncache_nw(iroute->irt_dest, iroute->irt_subnetmask); + if (iroute_p != NULL) + *iroute_p= iroute; + return NW_OK; +} + + +PUBLIC int ipr_del_iroute(port_nr, dest, subnetmask, gateway, + dist, static_route) +int port_nr; +ipaddr_t dest; +ipaddr_t subnetmask; +ipaddr_t gateway; +int dist; +int static_route; +{ + int i; + iroute_t *iroute; + + /* Try to track down any old routes, and look for an + * unused one. + */ + for(i= 0, iroute= iroute_table; i<IROUTE_NR; i++, iroute++) + { + if ((iroute->irt_flags & IRTF_INUSE) == 0) + continue; + if (iroute->irt_port != port_nr || + iroute->irt_dest != dest || + iroute->irt_subnetmask != subnetmask || + iroute->irt_gateway != gateway) + { + continue; + } + if (!!(iroute->irt_flags & IRTF_STATIC) != static_route) + continue; + break; + } + + if (i == IROUTE_NR) + return ESRCH; + + iroute_uncache_nw(iroute->irt_dest, iroute->irt_subnetmask); + iroute->irt_flags= IRTF_EMPTY; + return NW_OK; +} + + +PRIVATE iroute_uncache_nw(dest, netmask) +ipaddr_t dest; +ipaddr_t netmask; +{ + int i, j; + iroute_hash_t *iroute_hash; + + for (i= 0, iroute_hash= &iroute_hash_table[0][0]; + i<IROUTE_HASH_NR; i++, iroute_hash += IROUTE_HASH_ASS_NR) + { + for (j= 0; j<IROUTE_HASH_ASS_NR; j++) + { + if (((iroute_hash[j].irh_addr ^ dest) & + netmask) == 0) + { + iroute_hash[j].irh_addr= 0; + iroute_hash[j].irh_route= NULL; + } + } + } +} + + + +/* + * Debugging, management + */ + +/* + * $PchId: ipr.c,v 1.9 1996/07/31 17:26:33 philip Exp $ + */ diff --git a/servers/inet/generic/ipr.h b/servers/inet/generic/ipr.h new file mode 100644 index 000000000..552515d76 --- /dev/null +++ b/servers/inet/generic/ipr.h @@ -0,0 +1,81 @@ +/* +ipr.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef IPR_H +#define IPR_H + +typedef struct oroute +{ + int ort_port; + ipaddr_t ort_dest; + ipaddr_t ort_subnetmask; + int ort_dist; + i32_t ort_pref; + ipaddr_t ort_gateway; + time_t ort_exp_tim; + time_t ort_timestamp; + int ort_flags; + + struct oroute *ort_nextnw; + struct oroute *ort_nextgw; + struct oroute *ort_nextdist; +} oroute_t; + +#define ORTD_UNREACHABLE 512 + +#define ORTF_EMPTY 0 +#define ORTF_INUSE 1 +#define ORTF_STATIC 2 + +typedef struct iroute +{ + ipaddr_t irt_dest; + ipaddr_t irt_gateway; + ipaddr_t irt_subnetmask; + int irt_dist; + int irt_port; + int irt_flags; +} iroute_t; + +#define IRTD_UNREACHABLE 512 + +#define IRTF_EMPTY 0 +#define IRTF_INUSE 1 +#define IRTF_STATIC 2 + +#define IPR_UNRCH_TIMEOUT (60L * HZ) +#define IPR_TTL_TIMEOUT (60L * HZ) +#define IPR_REDIRECT_TIMEOUT (20 * 60L * HZ) +#define IPR_GW_DOWN_TIMEOUT (60L * HZ) + +/* Prototypes */ + +iroute_t *iroute_frag ARGS(( int port_nr, ipaddr_t dest )); +int oroute_frag ARGS(( int port_nr, ipaddr_t dest, int ttl, + ipaddr_t *nexthop )); +void ipr_init ARGS(( void )); +int ipr_get_iroute ARGS(( int ent_no, nwio_route_t *route_ent )); +int ipr_add_iroute ARGS(( int port_nr, ipaddr_t dest, ipaddr_t subnetmask, + ipaddr_t gateway, int dist, int static_route, iroute_t **route_p )); +int ipr_del_iroute ARGS(( int port_nr, ipaddr_t dest, ipaddr_t subnetmask, + ipaddr_t gateway, int dist, int static_route )); +int ipr_get_oroute ARGS(( int ent_no, nwio_route_t *route_ent )); +int ipr_add_oroute ARGS(( int port_nr, ipaddr_t dest, ipaddr_t subnetmask, + ipaddr_t gateway, time_t timeout, int dist, int static_route, + i32_t preference, oroute_t **route_p )); +void ipr_gateway_down ARGS(( int port_nr, ipaddr_t gateway, time_t timeout )); +void ipr_redirect ARGS(( int port_nr, ipaddr_t dest, ipaddr_t subnetmask, + ipaddr_t old_gateway, ipaddr_t new_gateway, time_t timeout )); +void ipr_destunrch ARGS(( int port_nr, ipaddr_t dest, ipaddr_t subnetmask, + time_t timeout )); +void ipr_ttl_exc ARGS(( int port_nr, ipaddr_t dest, ipaddr_t subnetmask, + time_t timeout )); + +#endif /* IPR_H */ + +/* + * $PchId: ipr.h,v 1.4 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/psip.c b/servers/inet/generic/psip.c new file mode 100644 index 000000000..7fdeb443b --- /dev/null +++ b/servers/inet/generic/psip.c @@ -0,0 +1,686 @@ +/* +generic/psip.c + +Implementation of a pseudo IP device. + +Created: Apr 22, 1993 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "assert.h" +#include "buf.h" +#include "event.h" +#include "type.h" +#include "ip_int.h" +#include "psip.h" +#include "sr.h" + +#if ENABLE_PSIP + +THIS_FILE + +typedef struct psip_port +{ + int pp_flags; + int pp_ipdev; + int pp_opencnt; + struct psip_fd *pp_rd_head; + struct psip_fd *pp_rd_tail; + acc_t *pp_promisc_head; + acc_t *pp_promisc_tail; +} psip_port_t; + +#define PPF_EMPTY 0 +#define PPF_CONFIGURED 1 +#define PPF_ENABLED 2 +#define PPF_PROMISC 4 + +#define PSIP_FD_NR (1*IP_PORT_MAX) + +typedef struct psip_fd +{ + int pf_flags; + int pf_srfd; + psip_port_t *pf_port; + get_userdata_t pf_get_userdata; + put_userdata_t pf_put_userdata; + struct psip_fd *pf_rd_next; + size_t pf_rd_count; + nwio_psipopt_t pf_psipopt; +} psip_fd_t; + +#define PFF_EMPTY 0 +#define PFF_INUSE 1 +#define PFF_READ_IP 2 +#define PFF_PROMISC 4 + +PRIVATE psip_port_t *psip_port_table; +PRIVATE psip_fd_t psip_fd_table[PSIP_FD_NR]; + +FORWARD int psip_open ARGS(( int port, int srfd, + get_userdata_t get_userdata, put_userdata_t put_userdata, + put_pkt_t pkt_pkt )); +FORWARD int psip_ioctl ARGS(( int fd, ioreq_t req )); +FORWARD int psip_read ARGS(( int fd, size_t count )); +FORWARD int psip_write ARGS(( int fd, size_t count )); +FORWARD void psip_close ARGS(( int fd )); +FORWARD int psip_cancel ARGS(( int fd, int which_operation )); +FORWARD void promisc_restart_read ARGS(( psip_port_t *psip_port )); +FORWARD int psip_setopt ARGS(( psip_fd_t *psip_fd, nwio_psipopt_t *newoptp )); +FORWARD void psip_buffree ARGS(( int priority )); +#ifdef BUF_CONSISTENCY_CHECK +FORWARD void psip_bufcheck ARGS(( void )); +#endif +FORWARD void reply_thr_put ARGS(( psip_fd_t *psip_fd, int reply, + int for_ioctl )); +FORWARD void reply_thr_get ARGS(( psip_fd_t *psip_fd, int reply, + int for_ioctl )); + +PUBLIC void psip_prep() +{ + psip_port_table= alloc(psip_conf_nr * sizeof(psip_port_table[0])); +} + +PUBLIC void psip_init() +{ + int i; + psip_port_t *psip_port; + psip_fd_t *psip_fd; + +#if ZERO + for (i=0, psip_port= psip_port_table; i<psip_conf_nr; i++, psip_port++) + psip_port->pp_flags= PPF_EMPTY; + + for (i=0, psip_fd= psip_fd_table; i<PSIP_FD_NR; i++, psip_fd++) + psip_fd->pf_flags= PFF_EMPTY; +#endif + + for (i=0, psip_port= psip_port_table; i<psip_conf_nr; i++, psip_port++) + { + psip_port->pp_flags |= PPF_CONFIGURED; +#if ZERO + psip_port->pp_opencnt= 0; + psip_port->pp_rd_head= NULL; + psip_port->pp_promisc_head= NULL; +#endif + } + +#ifndef BUF_CONSISTENCY_CHECK + bf_logon(psip_buffree); +#else + bf_logon(psip_buffree, psip_bufcheck); +#endif +} + +PUBLIC int psip_enable(port_nr, ip_port_nr) +int port_nr; +int ip_port_nr; +{ + psip_port_t *psip_port; + + assert(port_nr >= 0 && port_nr < psip_conf_nr); + + psip_port= &psip_port_table[port_nr]; + assert(psip_port->pp_flags & PPF_CONFIGURED); + + psip_port->pp_ipdev= ip_port_nr; + psip_port->pp_flags |= PPF_ENABLED; + + sr_add_minor(if2minor(psip_conf[port_nr].pc_ifno, PSIP_DEV_OFF), + port_nr, psip_open, psip_close, psip_read, + psip_write, psip_ioctl, psip_cancel); + + return NW_OK; +} + +PUBLIC int psip_send(port_nr, pack) +int port_nr; +acc_t *pack; +{ + psip_port_t *psip_port; + psip_fd_t *psip_fd, *mark_fd; + int i, result, result1; + size_t buf_size; + acc_t *hdr_pack; + psip_io_hdr_t *hdr; + + assert(port_nr >= 0 && port_nr < psip_conf_nr); + psip_port= &psip_port_table[port_nr]; + + if (psip_port->pp_opencnt == 0) + { + bf_afree(pack); + return NW_OK; + } + + for(;;) + { + mark_fd= psip_port->pp_rd_tail; + + for(i= 0; i<PSIP_FD_NR; i++) + { + psip_fd= psip_port->pp_rd_head; + if (!psip_fd) + return NW_SUSPEND; + psip_port->pp_rd_head= psip_fd->pf_rd_next; + if (!(psip_fd->pf_flags & PFF_PROMISC)) + break; + psip_fd->pf_rd_next= NULL; + if (psip_port->pp_rd_head == NULL) + psip_port->pp_rd_head= psip_fd; + else + psip_port->pp_rd_tail->pf_rd_next= psip_fd; + psip_port->pp_rd_tail= psip_fd; + if (psip_fd == mark_fd) + return NW_SUSPEND; + } + if (i == PSIP_FD_NR) + ip_panic(( "psip_send: loop" )); + + assert(psip_fd->pf_flags & PFF_READ_IP); + psip_fd->pf_flags &= ~PFF_READ_IP; + + buf_size= bf_bufsize(pack); + if (buf_size <= psip_fd->pf_rd_count) + { + if (psip_port->pp_flags & PPF_PROMISC) + { + /* Deal with promiscuous mode. */ + hdr_pack= bf_memreq(sizeof(*hdr)); + hdr= (psip_io_hdr_t *)ptr2acc_data(hdr_pack); + memset(hdr, '\0', sizeof(*hdr)); + hdr->pih_flags |= PF_LOC2REM; + + pack->acc_linkC++; + hdr_pack->acc_next= pack; + hdr_pack->acc_ext_link= NULL; + if (psip_port->pp_promisc_head) + { + /* Append at the end. */ + psip_port->pp_promisc_tail-> + acc_ext_link= hdr_pack; + psip_port->pp_promisc_tail= hdr_pack; + } + else + { + /* First packet. */ + psip_port->pp_promisc_head= hdr_pack; + psip_port->pp_promisc_tail= hdr_pack; + if (psip_port->pp_rd_head) + promisc_restart_read(psip_port); + } + } + result= (*psip_fd->pf_put_userdata)(psip_fd->pf_srfd, + (size_t)0, pack, FALSE); + if (result == NW_OK) + result= buf_size; + } + else + result= EPACKSIZE; + + result1= (*psip_fd->pf_put_userdata)(psip_fd->pf_srfd, + (size_t)result, NULL, FALSE); + assert(result1 == NW_OK); + if (result == EPACKSIZE) + continue; + return NW_OK; + } + return NW_SUSPEND; +} + +PRIVATE int psip_open(port, srfd, get_userdata, put_userdata, put_pkt) +int port; +int srfd; +get_userdata_t get_userdata; +put_userdata_t put_userdata; +put_pkt_t put_pkt; +{ + psip_port_t *psip_port; + psip_fd_t *psip_fd; + int i; + + assert(port >= 0 && port < psip_conf_nr); + psip_port= &psip_port_table[port]; + + if (!(psip_port->pp_flags & PPF_ENABLED)) + return ENXIO; + + for (i= 0, psip_fd= psip_fd_table; i<PSIP_FD_NR; i++, psip_fd++) + { + if (psip_fd->pf_flags & PFF_INUSE) + continue; + break; + } + if (i == PSIP_FD_NR) + return ENFILE; + psip_fd->pf_flags |= PFF_INUSE; + psip_fd->pf_srfd= srfd; + psip_fd->pf_port= psip_port; + psip_fd->pf_get_userdata= get_userdata; + psip_fd->pf_put_userdata= put_userdata; + psip_port->pp_opencnt++; + + return i; +} + +PRIVATE int psip_ioctl(fd, req) +int fd; +ioreq_t req; +{ + int result; + psip_fd_t *psip_fd; + acc_t *data; + nwio_psipopt_t *psip_opt, *newoptp; + + assert(fd >= 0 && fd < PSIP_FD_NR); + psip_fd= &psip_fd_table[fd]; + + switch(req) + { + case NWIOSPSIPOPT: + data= (*psip_fd->pf_get_userdata)(psip_fd->pf_srfd, 0, + sizeof(*psip_opt), TRUE); + if (!data) + { + result= EFAULT; + break; + } + data= bf_packIffLess(data, sizeof(*psip_opt)); + assert (data->acc_length == sizeof(*psip_opt)); + + newoptp= (nwio_psipopt_t *)ptr2acc_data(data); + result= psip_setopt(psip_fd, newoptp); + bf_afree(data); + if (result == NW_OK) + { + if (psip_fd->pf_psipopt.nwpo_flags & NWPO_EN_PROMISC) + { + psip_fd->pf_flags |= PFF_PROMISC; + psip_fd->pf_port->pp_flags |= PPF_PROMISC; + } + else + { + psip_fd->pf_flags &= ~PFF_PROMISC; + /* XXX check port flags */ + } + } + reply_thr_get(psip_fd, result, TRUE); + break; + case NWIOGPSIPOPT: + data= bf_memreq(sizeof(*psip_opt)); + psip_opt= (nwio_psipopt_t *)ptr2acc_data(data); + + *psip_opt= psip_fd->pf_psipopt; + result= (*psip_fd->pf_put_userdata)(psip_fd->pf_srfd, 0, + data, TRUE); + if (result == NW_OK) + reply_thr_put(psip_fd, NW_OK, TRUE); + break; + default: + reply_thr_put(psip_fd, ENOTTY, TRUE); + break; + } + return NW_OK; +} + +PRIVATE int psip_read(fd, count) +int fd; +size_t count; +{ + psip_port_t *psip_port; + psip_fd_t *psip_fd; + + assert(fd >= 0 && fd < PSIP_FD_NR); + psip_fd= &psip_fd_table[fd]; + psip_port= psip_fd->pf_port; + + psip_fd->pf_rd_count= count; + if (psip_port->pp_rd_head == NULL) + psip_port->pp_rd_head= psip_fd; + else + psip_port->pp_rd_tail->pf_rd_next= psip_fd; + psip_fd->pf_rd_next= NULL; + psip_port->pp_rd_tail= psip_fd; + + psip_fd->pf_flags |= PFF_READ_IP; + if (psip_fd->pf_flags & PFF_PROMISC) + promisc_restart_read(psip_port); + else + ipps_get(psip_port->pp_ipdev); + if (psip_fd->pf_flags & PFF_READ_IP) + return NW_SUSPEND; + return NW_OK; +} + +PRIVATE int psip_write(fd, count) +int fd; +size_t count; +{ + psip_port_t *psip_port; + psip_fd_t *psip_fd; + acc_t *pack, *hdr_pack; + psip_io_hdr_t *hdr; + + assert(fd >= 0 && fd < PSIP_FD_NR); + psip_fd= &psip_fd_table[fd]; + psip_port= psip_fd->pf_port; + + pack= (*psip_fd->pf_get_userdata)(psip_fd->pf_srfd, (size_t)0, + count, FALSE); + if (pack == NULL) + { + pack= (*psip_fd->pf_get_userdata)(psip_fd->pf_srfd, + (size_t)EFAULT, (size_t)0, FALSE); + assert(pack == NULL); + return NW_OK; + } + if (psip_port->pp_flags & PPF_PROMISC) + { + /* Deal with promiscuous mode. */ + hdr_pack= bf_memreq(sizeof(*hdr)); + hdr= (psip_io_hdr_t *)ptr2acc_data(hdr_pack); + memset(hdr, '\0', sizeof(*hdr)); + hdr->pih_flags |= PF_REM2LOC; + + pack->acc_linkC++; + hdr_pack->acc_next= pack; + hdr_pack->acc_ext_link= NULL; + if (psip_port->pp_promisc_head) + { + /* Append at the end. */ + psip_port->pp_promisc_tail->acc_ext_link= hdr_pack; + } + else + { + /* First packet. */ + psip_port->pp_promisc_head= hdr_pack; + if (psip_port->pp_rd_head) + promisc_restart_read(psip_port); + } + } + ipps_put(psip_port->pp_ipdev, pack); + pack= (*psip_fd->pf_get_userdata)(psip_fd->pf_srfd, (size_t)count, + (size_t)0, FALSE); + assert(pack == NULL); + return NW_OK; +} + +PRIVATE void psip_close(fd) +int fd; +{ + psip_port_t *psip_port; + psip_fd_t *psip_fd; + acc_t *acc, *acc_next; + int i; + + assert(fd >= 0 && fd < PSIP_FD_NR); + psip_fd= &psip_fd_table[fd]; + psip_port= psip_fd->pf_port; + + assert(psip_port->pp_opencnt >0); + psip_port->pp_opencnt--; + psip_fd->pf_flags= PFF_EMPTY; + ipps_get(psip_port->pp_ipdev); + + /* Check if the port should still be in promiscuous mode. */ + if (psip_port->pp_flags & PPF_PROMISC) + { + psip_port->pp_flags &= ~PPF_PROMISC; + for (i= 0, psip_fd= psip_fd_table; i<PSIP_FD_NR; + i++, psip_fd++) + { + if ((psip_fd->pf_flags & (PFF_INUSE|PFF_PROMISC)) != + (PFF_INUSE|PFF_PROMISC)) + { + continue; + } + if (psip_fd->pf_port != psip_port) + continue; + psip_port->pp_flags |= PPF_PROMISC; + break; + } + if (!(psip_port->pp_flags & PPF_PROMISC)) + { + /* Delete queued packets. */ + acc= psip_port->pp_promisc_head; + psip_port->pp_promisc_head= NULL; + while (acc) + { + acc_next= acc->acc_ext_link; + bf_afree(acc); + acc= acc_next; + } + } + } +} + +PRIVATE int psip_cancel(fd, which_operation) +int fd; +int which_operation; +{ + psip_port_t *psip_port; + psip_fd_t *psip_fd, *prev_fd, *tmp_fd; + int result; + + DBLOCK(1, printf("psip_cancel(%d, %d)\n", fd, which_operation)); + + assert(fd >= 0 && fd < PSIP_FD_NR); + psip_fd= &psip_fd_table[fd]; + psip_port= psip_fd->pf_port; + + switch(which_operation) + { +#if !CRAMPED + case SR_CANCEL_IOCTL: + ip_panic(( "should not be here" )); +#endif + case SR_CANCEL_READ: + assert(psip_fd->pf_flags & PFF_READ_IP); + for (prev_fd= NULL, tmp_fd= psip_port->pp_rd_head; tmp_fd; + prev_fd= tmp_fd, tmp_fd= tmp_fd->pf_rd_next) + { + if (tmp_fd == psip_fd) + break; + } +#if !CRAMPED + if (tmp_fd == NULL) + ip_panic(( "unable to find to request to cancel" )); +#endif + if (prev_fd == NULL) + psip_port->pp_rd_head= psip_fd->pf_rd_next; + else + prev_fd->pf_rd_next= psip_fd->pf_rd_next; + if (psip_fd->pf_rd_next == NULL) + psip_port->pp_rd_tail= prev_fd; + psip_fd->pf_flags &= ~PFF_READ_IP; + result= (*psip_fd->pf_put_userdata)(psip_fd->pf_srfd, + (size_t)EINTR, NULL, FALSE); + assert(result == NW_OK); + break; +#if !CRAMPED + case SR_CANCEL_WRITE: + ip_panic(( "should not be here" )); + default: + ip_panic(( "invalid operation for cancel" )); +#endif + } + return NW_OK; +} + +PRIVATE void promisc_restart_read(psip_port) +psip_port_t *psip_port; +{ + psip_fd_t *psip_fd, *mark_fd; + acc_t *pack; + size_t buf_size; + int i, result, result1; + + while (psip_port->pp_promisc_head) + { + mark_fd= psip_port->pp_rd_tail; + + for(i= 0; i<PSIP_FD_NR; i++) + { + psip_fd= psip_port->pp_rd_head; + if (!psip_fd) + return; + psip_port->pp_rd_head= psip_fd->pf_rd_next; + if (psip_fd->pf_flags & PFF_PROMISC) + break; + psip_fd->pf_rd_next= NULL; + if (psip_port->pp_rd_head == NULL) + psip_port->pp_rd_head= psip_fd; + else + psip_port->pp_rd_tail->pf_rd_next= psip_fd; + psip_port->pp_rd_tail= psip_fd; + if (psip_fd == mark_fd) + return; + } + if (i == PSIP_FD_NR) + ip_panic(( "psip'promisc_restart_read: loop" )); + + assert(psip_fd->pf_flags & PFF_READ_IP); + psip_fd->pf_flags &= ~PFF_READ_IP; + + pack= psip_port->pp_promisc_head; + buf_size= bf_bufsize(pack); + if (buf_size <= psip_fd->pf_rd_count) + { + psip_port->pp_promisc_head= pack->acc_ext_link; + result= (*psip_fd->pf_put_userdata)(psip_fd->pf_srfd, + (size_t)0, pack, FALSE); + if (result == NW_OK) + result= buf_size; + } + else + result= EPACKSIZE; + + result1= (*psip_fd->pf_put_userdata)(psip_fd->pf_srfd, + (size_t)result, NULL, FALSE); + assert(result1 == NW_OK); + } +} + +PRIVATE int psip_setopt(psip_fd, newoptp) +psip_fd_t *psip_fd; +nwio_psipopt_t *newoptp; +{ + nwio_psipopt_t oldopt; + int result; + unsigned int new_en_flags, new_di_flags, old_en_flags, old_di_flags, + all_flags, flags; + unsigned long new_flags; + int i; + + oldopt= psip_fd->pf_psipopt; + + old_en_flags= oldopt.nwpo_flags & 0xffff; + old_di_flags= (oldopt.nwpo_flags >> 16) & 0xffff; + + new_en_flags= newoptp->nwpo_flags & 0xffff; + new_di_flags= (newoptp->nwpo_flags >> 16) & 0xffff; + + if (new_en_flags & new_di_flags) + return EBADMODE; + + /* NWUO_LOCADDR_MASK */ + if (!((new_en_flags | new_di_flags) & NWPO_PROMISC_MASK)) + { + new_en_flags |= (old_en_flags & NWPO_PROMISC_MASK); + new_di_flags |= (old_di_flags & NWPO_PROMISC_MASK); + } + + new_flags= ((unsigned long)new_di_flags << 16) | new_en_flags; + + psip_fd->pf_psipopt= *newoptp; + psip_fd->pf_psipopt.nwpo_flags= new_flags; + + return NW_OK; +} + +PRIVATE void psip_buffree (priority) +int priority; +{ + int i; + psip_port_t *psip_port; + acc_t *tmp_acc, *next_acc; + + if (priority == PSIP_PRI_EXP_PROMISC) + { + for (i=0, psip_port= psip_port_table; i<psip_conf_nr; + i++, psip_port++) + { + if (!(psip_port->pp_flags & PPF_CONFIGURED) ) + continue; + if (psip_port->pp_promisc_head) + { + tmp_acc= psip_port->pp_promisc_head; + while(tmp_acc) + { + next_acc= tmp_acc->acc_ext_link; + bf_afree(tmp_acc); + tmp_acc= next_acc; + } + psip_port->pp_promisc_head= NULL; + } + } + } +} + +#ifdef BUF_CONSISTENCY_CHECK +PRIVATE void psip_bufcheck() +{ + int i; + psip_port_t *psip_port; + acc_t *tmp_acc; + + for (i= 0, psip_port= psip_port_table; i<psip_conf_nr; + i++, psip_port++) + { + for (tmp_acc= psip_port->pp_promisc_head; tmp_acc; + tmp_acc= tmp_acc->acc_ext_link) + { + bf_check_acc(tmp_acc); + } + } +} +#endif + +/* +reply_thr_put +*/ + +PRIVATE void reply_thr_put(psip_fd, reply, for_ioctl) +psip_fd_t *psip_fd; +int reply; +int for_ioctl; +{ + int result; + + result= (*psip_fd->pf_put_userdata)(psip_fd->pf_srfd, reply, + (acc_t *)0, for_ioctl); + assert(result == NW_OK); +} + +/* +reply_thr_get +*/ + +PRIVATE void reply_thr_get(psip_fd, reply, for_ioctl) +psip_fd_t *psip_fd; +int reply; +int for_ioctl; +{ + acc_t *result; + result= (*psip_fd->pf_get_userdata)(psip_fd->pf_srfd, reply, + (size_t)0, for_ioctl); + assert (!result); +} + +#endif /* ENABLE_PSIP */ + +/* + * $PchId: psip.c,v 1.6 1996/05/07 20:50:31 philip Exp $ + */ diff --git a/servers/inet/generic/psip.h b/servers/inet/generic/psip.h new file mode 100644 index 000000000..570ccc7e0 --- /dev/null +++ b/servers/inet/generic/psip.h @@ -0,0 +1,23 @@ +/* +generic/psip.h + +Public interface to the pseudo IP module + +Created: Apr 22, 1993 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +#ifndef PSIP_H +#define PSIP_H + +void psip_prep ARGS(( void )); +void psip_init ARGS(( void )); +int psip_enable ARGS(( int port_nr, int ip_port_nr )); +int psip_send ARGS(( int port_nr, acc_t *pack )); + +#endif /* PSIP_H */ + +/* + * $PchId: psip.h,v 1.4 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/sr.h b/servers/inet/generic/sr.h new file mode 100644 index 000000000..11c41a00f --- /dev/null +++ b/servers/inet/generic/sr.h @@ -0,0 +1,41 @@ +/* +sr.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef SR_H +#define SR_H + +#define MAX_IOCTL_S 512 + +#define SR_CANCEL_IOCTL 1 +#define SR_CANCEL_READ 2 +#define SR_CANCEL_WRITE 3 + +/* Forward struct declarations */ + +struct acc; + +/* prototypes */ + +typedef int (*sr_open_t) ARGS(( int port, int srfd, + get_userdata_t get_userdata, put_userdata_t put_userdata, + put_pkt_t put_pkt )); +typedef void (*sr_close_t) ARGS(( int fd )); +typedef int (*sr_read_t) ARGS(( int fd, size_t count )); +typedef int (*sr_write_t) ARGS(( int fd, size_t count )); +typedef int (*sr_ioctl_t) ARGS(( int fd, ioreq_t req )); +typedef int (*sr_cancel_t) ARGS(( int fd, int which_operation )); + +void sr_init ARGS(( void )); +void sr_add_minor ARGS(( int minor, int port, sr_open_t openf, + sr_close_t closef, sr_read_t sr_read, sr_write_t sr_write, + sr_ioctl_t ioctlf, sr_cancel_t cancelf )); + +#endif /* SR_H */ + + +/* + * $PchId: sr.h,v 1.6 1996/05/07 20:50:51 philip Exp $ + */ diff --git a/servers/inet/generic/tcp.c b/servers/inet/generic/tcp.c new file mode 100644 index 000000000..1458744f8 --- /dev/null +++ b/servers/inet/generic/tcp.c @@ -0,0 +1,2127 @@ +/* +tcp.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "buf.h" +#include "clock.h" +#include "event.h" +#include "type.h" + +#if !CRAMPED +#include "io.h" +#include "ip.h" +#endif +#include "sr.h" +#include "assert.h" +#include "tcp.h" +#include "tcp_int.h" + +THIS_FILE + +PUBLIC tcp_port_t *tcp_port_table; +PUBLIC tcp_fd_t tcp_fd_table[TCP_FD_NR]; +PUBLIC tcp_conn_t tcp_conn_table[TCP_CONN_NR]; + +FORWARD void tcp_main ARGS(( tcp_port_t *port )); +FORWARD acc_t *tcp_get_data ARGS(( int fd, size_t offset, + size_t count, int for_ioctl )); +FORWARD int tcp_put_data ARGS(( int fd, size_t offset, + acc_t *data, int for_ioctl )); +FORWARD void tcp_put_pkt ARGS(( int fd, acc_t *data, size_t datalen )); +FORWARD void read_ip_packets ARGS(( tcp_port_t *port )); +FORWARD int tcp_setconf ARGS(( tcp_fd_t *tcp_fd )); +FORWARD int tcp_setopt ARGS(( tcp_fd_t *tcp_fd )); +FORWARD int tcp_connect ARGS(( tcp_fd_t *tcp_fd )); +FORWARD int tcp_listen ARGS(( tcp_fd_t *tcp_fd )); +FORWARD tcpport_t find_unused_port ARGS(( int fd )); +FORWARD int is_unused_port ARGS(( Tcpport_t port )); +FORWARD int reply_thr_put ARGS(( tcp_fd_t *tcp_fd, int reply, + int for_ioctl )); +FORWARD void reply_thr_get ARGS(( tcp_fd_t *tcp_fd, int reply, + int for_ioctl )); +FORWARD tcp_conn_t *find_conn_entry ARGS(( Tcpport_t locport, + ipaddr_t locaddr, Tcpport_t remport, ipaddr_t readaddr )); +FORWARD tcp_conn_t *find_empty_conn ARGS(( void )); +FORWARD tcp_conn_t *find_best_conn ARGS(( ip_hdr_t *ip_hdr, + tcp_hdr_t *tcp_hdr )); +FORWARD int maybe_listen ARGS(( ipaddr_t locaddr, Tcpport_t locport, + ipaddr_t remaddr, Tcpport_t remport )); +FORWARD int conn_right4fd ARGS(( tcp_conn_t *tcp_conn, tcp_fd_t *tcp_fd )); +FORWARD int tcp_su4connect ARGS(( tcp_fd_t *tcp_fd )); +FORWARD void tcp_buffree ARGS(( int priority )); +#ifdef BUF_CONSISTENCY_CHECK +FORWARD void tcp_bufcheck ARGS(( void )); +#endif +FORWARD void tcp_setup_conn ARGS(( tcp_conn_t *tcp_conn )); + +PUBLIC void tcp_prep() +{ + tcp_port_table= alloc(ip_conf_nr * sizeof(tcp_port_table[0])); +} + +PUBLIC void tcp_init() +{ + int i, j, k; + tcp_fd_t *tcp_fd; + tcp_port_t *tcp_port; + tcp_conn_t *tcp_conn; + + assert (BUF_S >= sizeof(struct nwio_ipopt)); + assert (BUF_S >= sizeof(struct nwio_ipconf)); + assert (BUF_S >= sizeof(struct nwio_tcpconf)); + assert (BUF_S >= IP_MAX_HDR_SIZE + TCP_MAX_HDR_SIZE); + +#if ZERO + for (i=0, tcp_fd= tcp_fd_table; i<TCP_FD_NR; i++, tcp_fd++) + { + tcp_fd->tf_flags= TFF_EMPTY; + } + + for (i=0, tcp_conn= tcp_conn_table; i<TCP_CONN_NR; i++, + tcp_fd++) + { + tcp_conn->tc_flags= TCF_EMPTY; + tcp_conn->tc_busy= 0; + } +#endif + +#ifndef BUF_CONSISTENCY_CHECK + bf_logon(tcp_buffree); +#else + bf_logon(tcp_buffree, tcp_bufcheck); +#endif + + for (i=0, tcp_port= tcp_port_table; i<ip_conf_nr; i++, tcp_port++) + { + tcp_port->tp_ipdev= i; + +#if ZERO + tcp_port->tp_flags= TPF_EMPTY; + tcp_port->tp_state= TPS_EMPTY; + tcp_port->tp_snd_head= NULL; + tcp_port->tp_snd_tail= NULL; + ev_init(&tcp_port->tp_snd_event); +#endif + for (j= 0; j<TCP_CONN_HASH_NR; j++) + { + for (k= 0; k<4; k++) + { + tcp_port->tp_conn_hash[j][k]= + &tcp_conn_table[0]; + } + } + + sr_add_minor(if2minor(ip_conf[i].ic_ifno, TCP_DEV_OFF), + i, tcp_open, tcp_close, tcp_read, + tcp_write, tcp_ioctl, tcp_cancel); + + tcp_main(tcp_port); + } +} + +PRIVATE void tcp_main(tcp_port) +tcp_port_t *tcp_port; +{ + int result, i; + tcp_conn_t *tcp_conn; + tcp_fd_t *tcp_fd; + + switch (tcp_port->tp_state) + { + case TPS_EMPTY: + tcp_port->tp_state= TPS_SETPROTO; + tcp_port->tp_ipfd= ip_open(tcp_port->tp_ipdev, + tcp_port->tp_ipdev, tcp_get_data, + tcp_put_data, tcp_put_pkt); + if (tcp_port->tp_ipfd < 0) + { + tcp_port->tp_state= TPS_ERROR; + DBLOCK(1, printf("%s, %d: unable to open ip port\n", + __FILE__, __LINE__)); + return; + } + + result= ip_ioctl(tcp_port->tp_ipfd, NWIOSIPOPT); + if (result == NW_SUSPEND) + tcp_port->tp_flags |= TPF_SUSPEND; + if (result < 0) + { + return; + } + if (tcp_port->tp_state != TPS_GETCONF) + return; + /* drops through */ + case TPS_GETCONF: + tcp_port->tp_flags &= ~TPF_SUSPEND; + + result= ip_ioctl(tcp_port->tp_ipfd, NWIOGIPCONF); + if (result == NW_SUSPEND) + tcp_port->tp_flags |= TPF_SUSPEND; + if (result < 0) + { + return; + } + if (tcp_port->tp_state != TPS_MAIN) + return; + /* drops through */ + case TPS_MAIN: + tcp_port->tp_flags &= ~TPF_SUSPEND; + tcp_port->tp_pack= 0; + + tcp_conn= &tcp_conn_table[tcp_port->tp_ipdev]; + tcp_conn->tc_flags= TCF_INUSE; + assert(!tcp_conn->tc_busy); + tcp_conn->tc_locport= 0; + tcp_conn->tc_locaddr= tcp_port->tp_ipaddr; + tcp_conn->tc_remport= 0; + tcp_conn->tc_remaddr= 0; + tcp_conn->tc_state= TCS_CLOSED; + tcp_conn->tc_fd= 0; + tcp_conn->tc_connInprogress= 0; + tcp_conn->tc_orglisten= FALSE; + tcp_conn->tc_senddis= 0; + tcp_conn->tc_ISS= 0; + tcp_conn->tc_SND_UNA= tcp_conn->tc_ISS; + tcp_conn->tc_SND_TRM= tcp_conn->tc_ISS; + tcp_conn->tc_SND_NXT= tcp_conn->tc_ISS; + tcp_conn->tc_SND_UP= tcp_conn->tc_ISS; + tcp_conn->tc_IRS= 0; + tcp_conn->tc_RCV_LO= tcp_conn->tc_IRS; + tcp_conn->tc_RCV_NXT= tcp_conn->tc_IRS; + tcp_conn->tc_RCV_HI= tcp_conn->tc_IRS; + tcp_conn->tc_RCV_UP= tcp_conn->tc_IRS; + tcp_conn->tc_port= tcp_port; + tcp_conn->tc_rcvd_data= NULL; + tcp_conn->tc_adv_data= NULL; + tcp_conn->tc_send_data= 0; + tcp_conn->tc_remipopt= NULL; + tcp_conn->tc_tcpopt= NULL; + tcp_conn->tc_frag2send= 0; + tcp_conn->tc_tos= TCP_DEF_TOS; + tcp_conn->tc_ttl= IP_MAX_TTL; + tcp_conn->tc_rcv_wnd= TCP_MAX_RCV_WND_SIZE; + tcp_conn->tc_rt_dead= TCP_DEF_RT_DEAD; + tcp_conn->tc_stt= 0; + tcp_conn->tc_0wnd_to= 0; + tcp_conn->tc_rtt= TCP_DEF_RTT; + tcp_conn->tc_mss= TCP_DEF_MSS; + tcp_conn->tc_error= NW_OK; + tcp_conn->tc_snd_wnd= TCP_MAX_SND_WND_SIZE; + tcp_conn->tc_snd_cinc= + (long)TCP_DEF_MSS*TCP_DEF_MSS/TCP_MAX_SND_WND_SIZE+1; + + tcp_conn->tc_rt_time= 0; + tcp_conn->tc_rt_seq= 0; + tcp_conn->tc_rt_threshold= tcp_conn->tc_ISS; + + for (i=0, tcp_fd= tcp_fd_table; i<TCP_FD_NR; i++, + tcp_fd++) + { + if (!(tcp_fd->tf_flags & TFF_INUSE)) + continue; + if (tcp_fd->tf_port != tcp_port) + continue; + if (tcp_fd->tf_flags & TFF_IOC_INIT_SP) + { + tcp_fd->tf_flags &= ~TFF_IOC_INIT_SP; + tcp_ioctl(i, tcp_fd->tf_ioreq); + } + } + read_ip_packets(tcp_port); + return; + +#if !CRAMPED + default: + ip_panic(( "unknown state" )); +#endif + } +} + +PRIVATE acc_t *tcp_get_data (port, offset, count, for_ioctl) +int port; +size_t offset; +size_t count; +int for_ioctl; +{ + tcp_port_t *tcp_port; + int result; + + tcp_port= &tcp_port_table[port]; + + switch (tcp_port->tp_state) + { + case TPS_SETPROTO: + if (!count) + { + result= (int)offset; + if (result<0) + { + tcp_port->tp_state= TPS_ERROR; + break; + } + tcp_port->tp_state= TPS_GETCONF; + if (tcp_port->tp_flags & TPF_SUSPEND) + tcp_main(tcp_port); + return NW_OK; + } +assert (!offset); +assert (count == sizeof(struct nwio_ipopt)); + { + struct nwio_ipopt *ipopt; + acc_t *acc; + + acc= bf_memreq(sizeof(*ipopt)); + ipopt= (struct nwio_ipopt *)ptr2acc_data(acc); + ipopt->nwio_flags= NWIO_COPY | + NWIO_EN_LOC | NWIO_DI_BROAD | + NWIO_REMANY | NWIO_PROTOSPEC | + NWIO_HDR_O_ANY | NWIO_RWDATALL; + ipopt->nwio_proto= IPPROTO_TCP; + return acc; + } + case TPS_MAIN: + assert(tcp_port->tp_flags & TPF_WRITE_IP); + if (!count) + { + result= (int)offset; + if (result<0) + { + if (result == EDSTNOTRCH) + { + if (tcp_port->tp_snd_head) + { + tcp_notreach(tcp_port-> + tp_snd_head); + } + } + else + { + ip_warning(( + "ip_write failed with error: %d\n", + result )); + } + } + assert (tcp_port->tp_pack); + bf_afree (tcp_port->tp_pack); + tcp_port->tp_pack= 0; + + if (tcp_port->tp_flags & TPF_WRITE_SP) + { + tcp_port->tp_flags &= ~(TPF_WRITE_SP| + TPF_WRITE_IP); + if (tcp_port->tp_snd_head) + tcp_port_write(tcp_port); + } + else + tcp_port->tp_flags &= ~TPF_WRITE_IP; + } + else + { + return bf_cut (tcp_port->tp_pack, offset, + count); + } + break; + default: +#if !CRAMPED + printf("tcp_get_data(%d, 0x%x, 0x%x) called but tp_state= 0x%x\n", + port, offset, count, tcp_port->tp_state); +#endif + break; + } + return NW_OK; +} + +PRIVATE int tcp_put_data (fd, offset, data, for_ioctl) +int fd; +size_t offset; +acc_t *data; +int for_ioctl; +{ + tcp_port_t *tcp_port; + int result; + + tcp_port= &tcp_port_table[fd]; + + switch (tcp_port->tp_state) + { + case TPS_GETCONF: + if (!data) + { + result= (int)offset; + if (result<0) + { + tcp_port->tp_state= TPS_ERROR; + return NW_OK; + } + tcp_port->tp_state= TPS_MAIN; + if (tcp_port->tp_flags & TPF_SUSPEND) + tcp_main(tcp_port); + } + else + { + struct nwio_ipconf *ipconf; + + data= bf_packIffLess(data, sizeof(*ipconf)); + ipconf= (struct nwio_ipconf *)ptr2acc_data(data); +assert (ipconf->nwic_flags & NWIC_IPADDR_SET); + tcp_port->tp_ipaddr= ipconf->nwic_ipaddr; + bf_afree(data); + } + break; + case TPS_MAIN: + assert(tcp_port->tp_flags & TPF_READ_IP); + if (!data) + { + result= (int)offset; + if (result<0) + ip_panic(( "ip_read() failed" )); + + if (tcp_port->tp_flags & TPF_READ_SP) + { + tcp_port->tp_flags &= ~(TPF_READ_SP| + TPF_READ_IP); + read_ip_packets(tcp_port); + } + else + tcp_port->tp_flags &= ~TPF_READ_IP; + } + else + { + assert(!offset); + /* this is an invalid assertion but ip sends + * only whole datagrams up */ + tcp_put_pkt(fd, data, bf_bufsize(data)); + } + break; + default: +#if !CRAMPED + printf("tcp_put_data(%d, 0x%x, 0x%x) called but tp_state= 0x%x\n", + fd, offset, data, tcp_port->tp_state); +#endif + break; + } + return NW_OK; +} + +/* +tcp_put_pkt +*/ + +PRIVATE void tcp_put_pkt(fd, data, datalen) +int fd; +acc_t *data; +size_t datalen; +{ + tcp_port_t *tcp_port; + tcp_conn_t *tcp_conn, **conn_p; + ip_hdr_t *ip_hdr; + tcp_hdr_t *tcp_hdr; + acc_t *ip_pack, *tcp_pack; + size_t ip_datalen, tcp_datalen, ip_hdr_len, tcp_hdr_len; + u16_t sum; + u32_t bits; + int hash; + ipaddr_t srcaddr, dstaddr; + tcpport_t srcport, dstport; + + tcp_port= &tcp_port_table[fd]; + + /* Extract the IP header. */ + ip_hdr= (ip_hdr_t *)ptr2acc_data(data); + ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; + ip_datalen= datalen - ip_hdr_len; + if (ip_datalen == 0) + { + DBLOCK(1, printf("tcp_put_pkt: no TCP header\n")); + bf_afree(data); + return; + } + data->acc_linkC++; + ip_pack= data; + ip_pack= bf_align(ip_pack, ip_hdr_len, 4); + ip_hdr= (ip_hdr_t *)ptr2acc_data(ip_pack); + data= bf_delhead(data, ip_hdr_len); + + /* Compute the checksum */ + sum= tcp_pack_oneCsum(ip_hdr, data); + + /* Extract the TCP header */ + if (ip_datalen < TCP_MIN_HDR_SIZE) + { + DBLOCK(1, printf("truncated TCP header\n")); + bf_afree(ip_pack); + bf_afree(data); + return; + } + data= bf_packIffLess(data, TCP_MIN_HDR_SIZE); + tcp_hdr= (tcp_hdr_t *)ptr2acc_data(data); + tcp_hdr_len= (tcp_hdr->th_data_off & TH_DO_MASK) >> 2; + /* actualy (>> 4) << 2 */ + if (ip_datalen < tcp_hdr_len || tcp_hdr_len < TCP_MIN_HDR_SIZE) + { + if (tcp_hdr_len < TCP_MIN_HDR_SIZE) + { + DBLOCK(1, printf("strange tcp header length %d\n", + tcp_hdr_len)); + } + else + { + DBLOCK(1, printf("truncated TCP header\n")); + } + bf_afree(ip_pack); + bf_afree(data); + return; + } + data->acc_linkC++; + tcp_pack= data; + tcp_pack= bf_align(tcp_pack, tcp_hdr_len, 4); + tcp_hdr= (tcp_hdr_t *)ptr2acc_data(tcp_pack); + if (ip_datalen == tcp_hdr_len) + { + bf_afree(data); + data= NULL; + } + else + data= bf_delhead(data, tcp_hdr_len); + tcp_datalen= ip_datalen-tcp_hdr_len; + + if ((u16_t)~sum) + { + DBLOCK(1, printf("checksum error in tcp packet\n"); + printf("tcp_pack_oneCsum(...)= 0x%x length= %d\n", + (u16_t)~sum, tcp_datalen); + printf("src ip_addr= "); writeIpAddr(ip_hdr->ih_src); + printf("\n")); + bf_afree(ip_pack); + bf_afree(tcp_pack); + bf_afree(data); + return; + } + + srcaddr= ip_hdr->ih_src; + dstaddr= ip_hdr->ih_dst; + srcport= tcp_hdr->th_srcport; + dstport= tcp_hdr->th_dstport; + bits= srcaddr ^ dstaddr ^ srcport ^ dstport; + bits= (bits >> 16) ^ bits; + bits= (bits >> 8) ^ bits; + hash= ((bits >> TCP_CONN_HASH_SHIFT) ^ bits) & (TCP_CONN_HASH_NR-1); + conn_p= tcp_port->tp_conn_hash[hash]; + if (conn_p[0]->tc_locport == dstport && + conn_p[0]->tc_remport == srcport && + conn_p[0]->tc_remaddr == srcaddr && + conn_p[0]->tc_locaddr == dstaddr) + { + tcp_conn= conn_p[0]; + } + else if (conn_p[1]->tc_locport == dstport && + conn_p[1]->tc_remport == srcport && + conn_p[1]->tc_remaddr == srcaddr && + conn_p[1]->tc_locaddr == dstaddr) + { + tcp_conn= conn_p[1]; + conn_p[1]= conn_p[0]; + conn_p[0]= tcp_conn; + } + else if (conn_p[2]->tc_locport == dstport && + conn_p[2]->tc_remport == srcport && + conn_p[2]->tc_remaddr == srcaddr && + conn_p[2]->tc_locaddr == dstaddr) + { + tcp_conn= conn_p[2]; + conn_p[2]= conn_p[1]; + conn_p[1]= conn_p[0]; + conn_p[0]= tcp_conn; + } + else if (conn_p[3]->tc_locport == dstport && + conn_p[3]->tc_remport == srcport && + conn_p[3]->tc_remaddr == srcaddr && + conn_p[3]->tc_locaddr == dstaddr) + { + tcp_conn= conn_p[3]; + conn_p[3]= conn_p[2]; + conn_p[2]= conn_p[1]; + conn_p[1]= conn_p[0]; + conn_p[0]= tcp_conn; + } + else + tcp_conn= NULL; + if (tcp_conn != NULL && tcp_conn->tc_state == TCS_CLOSED || + (tcp_hdr->th_flags & THF_SYN)) + { + tcp_conn= NULL; + } + + if (tcp_conn == NULL) + { + tcp_conn= find_best_conn(ip_hdr, tcp_hdr); + if (!tcp_conn) + { + /* listen backlog hack */ + bf_afree(ip_pack); + bf_afree(tcp_pack); + bf_afree(data); + return; + } + if (tcp_conn->tc_state != TCS_CLOSED) + { + conn_p[3]= conn_p[2]; + conn_p[2]= conn_p[1]; + conn_p[1]= conn_p[0]; + conn_p[0]= tcp_conn; + } + } + assert(tcp_conn->tc_busy == 0); + tcp_conn->tc_busy++; + tcp_frag2conn(tcp_conn, ip_hdr, tcp_hdr, data, tcp_datalen); + tcp_conn->tc_busy--; + bf_afree(ip_pack); + bf_afree(tcp_pack); +} + + +PUBLIC int tcp_open (port, srfd, get_userdata, put_userdata, put_pkt) +int port; +int srfd; +get_userdata_t get_userdata; +put_userdata_t put_userdata; +put_pkt_t put_pkt; +{ + int i; + tcp_fd_t *tcp_fd; + + for (i=0; i<TCP_FD_NR && (tcp_fd_table[i].tf_flags & TFF_INUSE); + i++); + if (i>=TCP_FD_NR) + { + return EAGAIN; + } + + tcp_fd= &tcp_fd_table[i]; + + tcp_fd->tf_flags= TFF_INUSE; + tcp_fd->tf_flags |= TFF_PUSH_DATA; /* XXX */ + + tcp_fd->tf_port= &tcp_port_table[port]; + tcp_fd->tf_srfd= srfd; + tcp_fd->tf_tcpconf.nwtc_flags= TCP_DEF_CONF; + tcp_fd->tf_tcpconf.nwtc_remaddr= 0; + tcp_fd->tf_tcpconf.nwtc_remport= 0; + tcp_fd->tf_tcpopt.nwto_flags= TCP_DEF_OPT; + tcp_fd->tf_get_userdata= get_userdata; + tcp_fd->tf_put_userdata= put_userdata; + tcp_fd->tf_conn= 0; + return i; +} + +/* +tcp_ioctl +*/ +PUBLIC int tcp_ioctl (fd, req) +int fd; +ioreq_t req; +{ + tcp_fd_t *tcp_fd; + tcp_port_t *tcp_port; + tcp_conn_t *tcp_conn; + nwio_tcpconf_t *tcp_conf; + nwio_tcpopt_t *tcp_opt; + acc_t *conf_acc, *opt_acc; + int result; + + tcp_fd= &tcp_fd_table[fd]; + +assert (tcp_fd->tf_flags & TFF_INUSE); + + tcp_port= tcp_fd->tf_port; + tcp_fd->tf_flags |= TFF_IOCTL_IP; + tcp_fd->tf_ioreq= req; + + if (tcp_port->tp_state != TPS_MAIN) + { + tcp_fd->tf_flags |= TFF_IOC_INIT_SP; + return NW_SUSPEND; + } + + switch (req) + { + case NWIOSTCPCONF: + if (tcp_fd->tf_flags & TFF_CONNECTED) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get (tcp_fd, EISCONN, TRUE); + result= NW_OK; + break; + } + result= tcp_setconf(tcp_fd); + break; + case NWIOGTCPCONF: + conf_acc= bf_memreq(sizeof(*tcp_conf)); +assert (conf_acc->acc_length == sizeof(*tcp_conf)); + tcp_conf= (nwio_tcpconf_t *)ptr2acc_data(conf_acc); + + *tcp_conf= tcp_fd->tf_tcpconf; + if (tcp_fd->tf_flags & TFF_CONNECTED) + { + tcp_conn= tcp_fd->tf_conn; + tcp_conf->nwtc_locport= tcp_conn->tc_locport; + tcp_conf->nwtc_remaddr= tcp_conn->tc_remaddr; + tcp_conf->nwtc_remport= tcp_conn->tc_remport; + } + tcp_conf->nwtc_locaddr= tcp_fd->tf_port->tp_ipaddr; + result= (*tcp_fd->tf_put_userdata)(tcp_fd->tf_srfd, + 0, conf_acc, TRUE); + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_put(tcp_fd, result, TRUE); + result= NW_OK; + break; + case NWIOSTCPOPT: + result= tcp_setopt(tcp_fd); + break; + case NWIOGTCPOPT: + opt_acc= bf_memreq(sizeof(*tcp_opt)); + assert (opt_acc->acc_length == sizeof(*tcp_opt)); + tcp_opt= (nwio_tcpopt_t *)ptr2acc_data(opt_acc); + + *tcp_opt= tcp_fd->tf_tcpopt; + result= (*tcp_fd->tf_put_userdata)(tcp_fd->tf_srfd, + 0, opt_acc, TRUE); + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_put(tcp_fd, result, TRUE); + result= NW_OK; + break; + case NWIOTCPCONN: + if (tcp_fd->tf_flags & TFF_CONNECTED) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get (tcp_fd, EISCONN, TRUE); + result= NW_OK; + break; + } + result= tcp_connect(tcp_fd); + break; + case NWIOTCPLISTEN: + if (tcp_fd->tf_flags & TFF_CONNECTED) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get (tcp_fd, EISCONN, TRUE); + result= NW_OK; + break; + } + result= tcp_listen(tcp_fd); + break; + case NWIOTCPSHUTDOWN: + if (!(tcp_fd->tf_flags & TFF_CONNECTED)) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get (tcp_fd, ENOTCONN, TRUE); + result= NW_OK; + break; + } + tcp_fd->tf_flags |= TFF_IOCTL_IP; + tcp_fd->tf_ioreq= req; + tcp_conn= tcp_fd->tf_conn; + + tcp_conn->tc_busy++; + tcp_fd_write(tcp_conn); + tcp_conn->tc_busy--; + tcp_conn_write(tcp_conn, 0); + if (!(tcp_fd->tf_flags & TFF_IOCTL_IP)) + return NW_OK; + else + return NW_SUSPEND; + break; + default: + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, EBADIOCTL, TRUE); + result= NW_OK; + break; + } + return result; +} + + +/* +tcp_setconf +*/ + +PRIVATE int tcp_setconf(tcp_fd) +tcp_fd_t *tcp_fd; +{ + nwio_tcpconf_t *tcpconf; + nwio_tcpconf_t oldconf, newconf; + acc_t *data; + int result; + tcpport_t port; + tcp_fd_t *fd_ptr; + unsigned int new_en_flags, new_di_flags, + old_en_flags, old_di_flags, all_flags, flags; + int i; + + data= (*tcp_fd->tf_get_userdata) + (tcp_fd->tf_srfd, 0, + sizeof(nwio_tcpconf_t), TRUE); + + if (!data) + return EFAULT; + + data= bf_packIffLess(data, sizeof(nwio_tcpconf_t)); +assert (data->acc_length == sizeof(nwio_tcpconf_t)); + + tcpconf= (nwio_tcpconf_t *)ptr2acc_data(data); + oldconf= tcp_fd->tf_tcpconf; + newconf= *tcpconf; + + old_en_flags= oldconf.nwtc_flags & 0xffff; + old_di_flags= (oldconf.nwtc_flags >> 16) & + 0xffff; + new_en_flags= newconf.nwtc_flags & 0xffff; + new_di_flags= (newconf.nwtc_flags >> 16) & + 0xffff; + if (new_en_flags & new_di_flags) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, EBADMODE, TRUE); + bf_afree(data); + return NW_OK; + } + + /* NWTC_ACC_MASK */ + if (new_di_flags & NWTC_ACC_MASK) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, EBADMODE, TRUE); + bf_afree(data); + return NW_OK; + /* access modes can't be disabled */ + } + + if (!(new_en_flags & NWTC_ACC_MASK)) + new_en_flags |= (old_en_flags & NWTC_ACC_MASK); + + /* NWTC_LOCPORT_MASK */ + if (new_di_flags & NWTC_LOCPORT_MASK) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, EBADMODE, TRUE); + bf_afree(data); + return NW_OK; + /* the loc ports can't be disabled */ + } + if (!(new_en_flags & NWTC_LOCPORT_MASK)) + { + new_en_flags |= (old_en_flags & + NWTC_LOCPORT_MASK); + newconf.nwtc_locport= oldconf.nwtc_locport; + } + else if ((new_en_flags & NWTC_LOCPORT_MASK) == NWTC_LP_SEL) + { + newconf.nwtc_locport= find_unused_port(tcp_fd- + tcp_fd_table); + } + else if ((new_en_flags & NWTC_LOCPORT_MASK) == NWTC_LP_SET) + { + if (!newconf.nwtc_locport) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, EBADMODE, TRUE); + bf_afree(data); + return NW_OK; + } + } + + /* NWTC_REMADDR_MASK */ + if (!((new_en_flags | new_di_flags) & + NWTC_REMADDR_MASK)) + { + new_en_flags |= (old_en_flags & + NWTC_REMADDR_MASK); + new_di_flags |= (old_di_flags & + NWTC_REMADDR_MASK); + newconf.nwtc_remaddr= oldconf.nwtc_remaddr; + } + else if (new_en_flags & NWTC_SET_RA) + { + if (!newconf.nwtc_remaddr) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, EBADMODE, TRUE); + bf_afree(data); + return NW_OK; + } + } + else + { +assert (new_di_flags & NWTC_REMADDR_MASK); + newconf.nwtc_remaddr= 0; + } + + /* NWTC_REMPORT_MASK */ + if (!((new_en_flags | new_di_flags) & NWTC_REMPORT_MASK)) + { + new_en_flags |= (old_en_flags & + NWTC_REMPORT_MASK); + new_di_flags |= (old_di_flags & + NWTC_REMPORT_MASK); + newconf.nwtc_remport= + oldconf.nwtc_remport; + } + else if (new_en_flags & NWTC_SET_RP) + { + if (!newconf.nwtc_remport) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, EBADMODE, TRUE); + bf_afree(data); + return NW_OK; + } + } + else + { +assert (new_di_flags & NWTC_REMPORT_MASK); + newconf.nwtc_remport= 0; + } + + newconf.nwtc_flags= ((unsigned long)new_di_flags + << 16) | new_en_flags; + all_flags= new_en_flags | new_di_flags; + + /* check the access modes */ + if ((all_flags & NWTC_LOCPORT_MASK) != NWTC_LP_UNSET) + { + for (i=0, fd_ptr= tcp_fd_table; i<TCP_FD_NR; i++, fd_ptr++) + { + if (fd_ptr == tcp_fd) + continue; + if (!(fd_ptr->tf_flags & TFF_INUSE)) + continue; + if (fd_ptr->tf_port != tcp_fd->tf_port) + continue; + flags= fd_ptr->tf_tcpconf.nwtc_flags; + if ((flags & NWTC_LOCPORT_MASK) == NWTC_LP_UNSET) + continue; + if (fd_ptr->tf_tcpconf.nwtc_locport != + newconf.nwtc_locport) + continue; + if ((flags & NWTC_ACC_MASK) != (all_flags & + NWTC_ACC_MASK) || + (all_flags & NWTC_ACC_MASK) == NWTC_EXCL) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, EADDRINUSE, TRUE); + bf_afree(data); + return NW_OK; + } + } + } + + tcp_fd->tf_tcpconf= newconf; + + if ((all_flags & NWTC_ACC_MASK) && + ((all_flags & NWTC_LOCPORT_MASK) == NWTC_LP_SET || + (all_flags & NWTC_LOCPORT_MASK) == NWTC_LP_SEL) && + (all_flags & NWTC_REMADDR_MASK) && + (all_flags & NWTC_REMPORT_MASK)) + tcp_fd->tf_flags |= TFF_CONF_SET; + else + { + tcp_fd->tf_flags &= ~TFF_CONF_SET; + } + bf_afree(data); + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, NW_OK, TRUE); + return NW_OK; +} + + +/* +tcp_setopt +*/ + +PRIVATE int tcp_setopt(tcp_fd) +tcp_fd_t *tcp_fd; +{ + nwio_tcpopt_t *tcpopt; + nwio_tcpopt_t oldopt, newopt; + acc_t *data; + int result; + tcpport_t port; + tcp_fd_t *fd_ptr; + unsigned int new_en_flags, new_di_flags, + old_en_flags, old_di_flags, all_flags, flags; + int i; + + data= (*tcp_fd->tf_get_userdata) (tcp_fd->tf_srfd, 0, + sizeof(nwio_tcpopt_t), TRUE); + + if (!data) + return EFAULT; + + data= bf_packIffLess(data, sizeof(nwio_tcpopt_t)); +assert (data->acc_length == sizeof(nwio_tcpopt_t)); + + tcpopt= (nwio_tcpopt_t *)ptr2acc_data(data); + oldopt= tcp_fd->tf_tcpopt; + newopt= *tcpopt; + + old_en_flags= oldopt.nwto_flags & 0xffff; + old_di_flags= (oldopt.nwto_flags >> 16) & + 0xffff; + new_en_flags= newopt.nwto_flags & 0xffff; + new_di_flags= (newopt.nwto_flags >> 16) & + 0xffff; + if (new_en_flags & new_di_flags) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, EBADMODE, TRUE); + return NW_OK; + } + + /* NWTO_SND_URG_MASK */ + if (!((new_en_flags | new_di_flags) & + NWTO_SND_URG_MASK)) + { + new_en_flags |= (old_en_flags & + NWTO_SND_URG_MASK); + new_di_flags |= (old_di_flags & + NWTO_SND_URG_MASK); + } + + /* NWTO_RCV_URG_MASK */ + if (!((new_en_flags | new_di_flags) & + NWTO_RCV_URG_MASK)) + { + new_en_flags |= (old_en_flags & + NWTO_RCV_URG_MASK); + new_di_flags |= (old_di_flags & + NWTO_RCV_URG_MASK); + } + + /* NWTO_BSD_URG_MASK */ + if (!((new_en_flags | new_di_flags) & + NWTO_BSD_URG_MASK)) + { + new_en_flags |= (old_en_flags & + NWTO_BSD_URG_MASK); + new_di_flags |= (old_di_flags & + NWTO_BSD_URG_MASK); + } + else + { + if (tcp_fd->tf_conn == NULL) + { + bf_afree(data); + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, EINVAL, TRUE); + return NW_OK; + } + } + + /* NWTO_DEL_RST_MASK */ + if (!((new_en_flags | new_di_flags) & + NWTO_DEL_RST_MASK)) + { + new_en_flags |= (old_en_flags & + NWTO_DEL_RST_MASK); + new_di_flags |= (old_di_flags & + NWTO_DEL_RST_MASK); + } + + newopt.nwto_flags= ((unsigned long)new_di_flags + << 16) | new_en_flags; + tcp_fd->tf_tcpopt= newopt; + if (newopt.nwto_flags & NWTO_SND_URG) + tcp_fd->tf_flags |= TFF_WR_URG; + else + tcp_fd->tf_flags &= ~TFF_WR_URG; + + if (newopt.nwto_flags & NWTO_RCV_URG) + tcp_fd->tf_flags |= TFF_RECV_URG; + else + tcp_fd->tf_flags &= ~TFF_RECV_URG; + + if (tcp_fd->tf_conn) + { + if (newopt.nwto_flags & NWTO_BSD_URG) + { + tcp_fd->tf_conn->tc_flags |= TCF_BSD_URG; + } + else + { + tcp_fd->tf_conn->tc_flags &= ~TCF_BSD_URG; + } + } + + if (newopt.nwto_flags & NWTO_DEL_RST) + tcp_fd->tf_flags |= TFF_DEL_RST; + else + tcp_fd->tf_flags &= ~TFF_DEL_RST; + + bf_afree(data); + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, NW_OK, TRUE); + return NW_OK; +} + + +PRIVATE tcpport_t find_unused_port(fd) +int fd; +{ + tcpport_t port, nw_port; + + nw_port= htons(0xC000+fd); + if (is_unused_port(nw_port)) + return nw_port; + + for (port= 0xC000+TCP_FD_NR; port < 0xFFFF; port++) + { + nw_port= htons(port); + if (is_unused_port(nw_port)) + return nw_port; + } +#if !CRAMPED + ip_panic(( "unable to find unused port (shouldn't occur)" )); + return 0; +#endif +} + +PRIVATE int is_unused_port(port) +tcpport_t port; +{ + int i; + tcp_fd_t *tcp_fd; + tcp_conn_t *tcp_conn; + + for (i= 0, tcp_fd= tcp_fd_table; i<TCP_FD_NR; i++, + tcp_fd++) + { + if (!(tcp_fd->tf_flags & TFF_CONF_SET)) + continue; + if (tcp_fd->tf_tcpconf.nwtc_locport == port) + return FALSE; + } + for (i= ip_conf_nr, tcp_conn= tcp_conn_table+i; + i<TCP_CONN_NR; i++, tcp_conn++) + /* the first ip_conf_nr ports are special */ + { + if (!(tcp_conn->tc_flags & TCF_INUSE)) + continue; + if (tcp_conn->tc_locport == port) + return FALSE; + } + return TRUE; +} + +PRIVATE int reply_thr_put(tcp_fd, reply, for_ioctl) +tcp_fd_t *tcp_fd; +int reply; +int for_ioctl; +{ + assert (tcp_fd); + + return (*tcp_fd->tf_put_userdata)(tcp_fd->tf_srfd, reply, + (acc_t *)0, for_ioctl); +} + +PRIVATE void reply_thr_get(tcp_fd, reply, for_ioctl) +tcp_fd_t *tcp_fd; +int reply; +int for_ioctl; +{ + acc_t *result; + result= (*tcp_fd->tf_get_userdata)(tcp_fd->tf_srfd, reply, + (size_t)0, for_ioctl); + assert (!result); +} + +PUBLIC int tcp_su4listen(tcp_fd) +tcp_fd_t *tcp_fd; +{ + tcp_conn_t *tcp_conn; + acc_t *tmp_acc; + + tcp_conn= tcp_fd->tf_conn; + + tcp_conn->tc_locport= tcp_fd->tf_tcpconf.nwtc_locport; + tcp_conn->tc_locaddr= tcp_fd->tf_port->tp_ipaddr; + if (tcp_fd->tf_tcpconf.nwtc_flags & NWTC_SET_RP) + tcp_conn->tc_remport= tcp_fd->tf_tcpconf.nwtc_remport; + else + tcp_conn->tc_remport= 0; + if (tcp_fd->tf_tcpconf.nwtc_flags & NWTC_SET_RA) + tcp_conn->tc_remaddr= tcp_fd->tf_tcpconf.nwtc_remaddr; + else + tcp_conn->tc_remaddr= 0; + + tcp_setup_conn(tcp_conn); + tcp_conn->tc_port= tcp_fd->tf_port; + tcp_conn->tc_fd= tcp_fd; + tcp_conn->tc_connInprogress= 1; + tcp_conn->tc_orglisten= TRUE; + tcp_conn->tc_state= TCS_LISTEN; + tcp_conn->tc_rt_dead= TCP_DEF_RT_MAX_LISTEN; + return NW_SUSPEND; +} + +/* +find_empty_conn + +This function returns a connection that is not inuse. +This includes connections that are never used, and connections without a +user that are not used for a while. +*/ + +PRIVATE tcp_conn_t *find_empty_conn() +{ + int i; + tcp_conn_t *tcp_conn; + int state; + + for (i=ip_conf_nr, tcp_conn= tcp_conn_table+i; + i<TCP_CONN_NR; i++, tcp_conn++) + /* the first ip_conf_nr connections are reserved for + * RSTs + */ + { + if (tcp_conn->tc_flags == TCF_EMPTY) + { + tcp_conn->tc_connInprogress= 0; + tcp_conn->tc_fd= NULL; + return tcp_conn; + } + if (tcp_conn->tc_fd) + continue; + if (tcp_conn->tc_senddis > get_time()) + continue; + if (tcp_conn->tc_state != TCS_CLOSED) + { + tcp_close_connection (tcp_conn, ENOCONN); + } + tcp_conn->tc_flags= 0; + return tcp_conn; + } + return NULL; +} + + +/* +find_conn_entry + +This function return a connection matching locport, locaddr, remport, remaddr. +If no such connection exists NULL is returned. +If a connection exists without mainuser it is closed. +*/ + +PRIVATE tcp_conn_t *find_conn_entry(locport, locaddr, remport, remaddr) +tcpport_t locport; +ipaddr_t locaddr; +tcpport_t remport; +ipaddr_t remaddr; +{ + tcp_conn_t *tcp_conn; + int i, state; + + assert(remport); + assert(remaddr); + for (i=ip_conf_nr, tcp_conn= tcp_conn_table+i; i<TCP_CONN_NR; + i++, tcp_conn++) + /* the first ip_conf_nr connections are reserved for + RSTs */ + { + if (tcp_conn->tc_flags == TCF_EMPTY) + continue; + if (tcp_conn->tc_locport != locport || + tcp_conn->tc_locaddr != locaddr || + tcp_conn->tc_remport != remport || + tcp_conn->tc_remaddr != remaddr) + continue; + if (tcp_conn->tc_fd) + return tcp_conn; + state= tcp_conn->tc_state; + if (state != TCS_CLOSED) + { + tcp_close_connection(tcp_conn, ENOCONN); + } + return tcp_conn; + } + return NULL; +} + +PRIVATE void read_ip_packets(tcp_port) +tcp_port_t *tcp_port; +{ + int result; + + do + { + tcp_port->tp_flags |= TPF_READ_IP; + result= ip_read(tcp_port->tp_ipfd, TCP_MAX_DATAGRAM); + if (result == NW_SUSPEND) + { + tcp_port->tp_flags |= TPF_READ_SP; + return; + } + assert(result == NW_OK); + tcp_port->tp_flags &= ~TPF_READ_IP; + } while(!(tcp_port->tp_flags & TPF_READ_IP)); +} + +/* +find_best_conn +*/ + +PRIVATE tcp_conn_t *find_best_conn(ip_hdr, tcp_hdr) +ip_hdr_t *ip_hdr; +tcp_hdr_t *tcp_hdr; +{ + + int best_level, new_level; + tcp_conn_t *best_conn, *listen_conn, *tcp_conn; + tcp_fd_t *tcp_fd; + int i; + ipaddr_t locaddr; + ipaddr_t remaddr; + tcpport_t locport; + tcpport_t remport; + + locaddr= ip_hdr->ih_dst; + remaddr= ip_hdr->ih_src; + locport= tcp_hdr->th_dstport; + remport= tcp_hdr->th_srcport; + if (!remport) /* This can interfere with a listen, so we reject it + * by clearing the requested port + */ + locport= 0; + + best_level= 0; + best_conn= NULL; + listen_conn= NULL; + for (i= ip_conf_nr, tcp_conn= tcp_conn_table+i; + i<TCP_CONN_NR; i++, tcp_conn++) + /* the first ip_conf_nr connections are reserved for + RSTs */ + { + if (!(tcp_conn->tc_flags & TCF_INUSE)) + continue; + /* First fast check for open connections. */ + if (tcp_conn->tc_locaddr == locaddr && + tcp_conn->tc_locport == locport && + tcp_conn->tc_remport == remport && + tcp_conn->tc_remaddr == remaddr && + tcp_conn->tc_fd) + { + return tcp_conn; + } + + /* Now check for listens and abandoned connections. */ + if (tcp_conn->tc_locaddr != locaddr) + { + continue; + } + new_level= 0; + if (tcp_conn->tc_locport) + { + if (tcp_conn->tc_locport != locport) + { + continue; + } + new_level += 4; + } + if (tcp_conn->tc_remport) + { + if (tcp_conn->tc_remport != remport) + { + continue; + } + new_level += 1; + } + if (tcp_conn->tc_remaddr) + { + if (tcp_conn->tc_remaddr != remaddr) + { + continue; + } + new_level += 2; + } + if (new_level<best_level) + continue; + if (new_level != 7 && tcp_conn->tc_state != TCS_LISTEN) + continue; + if (new_level == 7 && !tcp_conn->tc_fd) + /* We found an abandoned connection */ + { + if (best_conn && tcp_Lmod4G(tcp_conn->tc_ISS, + best_conn->tc_ISS)) + { + continue; + } + best_conn= tcp_conn; + continue; + } + if (!(tcp_hdr->th_flags & THF_SYN)) + continue; + best_level= new_level; + listen_conn= tcp_conn; + } + if (!best_conn && !listen_conn) + { + if ((tcp_hdr->th_flags & THF_SYN) && + maybe_listen(locaddr, locport, remaddr, remport)) + { + /* Quick hack to implement listen back logs: + * if a SYN arrives and there is no listen waiting + * for that packet, then no reply is sent. + */ + return NULL; + } + + for (i=0, tcp_conn= tcp_conn_table; i<ip_conf_nr; + i++, tcp_conn++) + { + /* find valid port to send RST */ + if ((tcp_conn->tc_flags & TCF_INUSE) && + tcp_conn->tc_locaddr==locaddr) + { + break; + } + } + assert (tcp_conn); + assert (tcp_conn->tc_state == TCS_CLOSED); + + tcp_conn->tc_locport= locport; + tcp_conn->tc_locaddr= locaddr; + tcp_conn->tc_remport= remport; + tcp_conn->tc_remaddr= remaddr; + assert (!tcp_conn->tc_fd); + return tcp_conn; + } + + if (best_conn) + { + assert(!best_conn->tc_fd); + if (!listen_conn) + return best_conn; + + tcp_fd= listen_conn->tc_fd; + assert(tcp_fd && listen_conn->tc_connInprogress && + tcp_fd->tf_conn == listen_conn); + + if (best_conn->tc_state != TCS_CLOSED) + tcp_close_connection(best_conn, ENOCONN); + + listen_conn->tc_ISS= best_conn->tc_ISS; + if (best_conn->tc_senddis > listen_conn->tc_senddis) + listen_conn->tc_senddis= best_conn->tc_senddis; + return listen_conn; + } + assert (listen_conn); + return listen_conn; +} + +/* +maybe_listen +*/ +PRIVATE int maybe_listen(locaddr, locport, remaddr, remport) +ipaddr_t locaddr; +tcpport_t locport; +ipaddr_t remaddr; +tcpport_t remport; +{ + int i; + tcp_conn_t *tcp_conn; + tcp_fd_t *fd; + + for (i= ip_conf_nr, tcp_conn= tcp_conn_table+i; + i<TCP_CONN_NR; i++, tcp_conn++) + { + if (!(tcp_conn->tc_flags & TCF_INUSE)) + continue; + + if (tcp_conn->tc_locaddr != locaddr) + { + continue; + } + if (tcp_conn->tc_locport != locport ) + { + continue; + } + if (!tcp_conn->tc_orglisten) + continue; + fd= tcp_conn->tc_fd; + if (!fd) + continue; + if ((fd->tf_tcpconf.nwtc_flags & NWTC_SET_RP) && + tcp_conn->tc_remport != remport) + { + continue; + } + if ((fd->tf_tcpconf.nwtc_flags & NWTC_SET_RA) && + tcp_conn->tc_remaddr != remaddr) + { + continue; + } + if (!(fd->tf_flags & TFF_DEL_RST)) + continue; + return 1; + + } + return 0; +} + + +PUBLIC void tcp_reply_ioctl(tcp_fd, reply) +tcp_fd_t *tcp_fd; +int reply; +{ + assert (tcp_fd->tf_flags & TFF_IOCTL_IP); + assert (tcp_fd->tf_ioreq == NWIOTCPSHUTDOWN || + tcp_fd->tf_ioreq == NWIOTCPLISTEN || + tcp_fd->tf_ioreq == NWIOTCPCONN); + + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get (tcp_fd, reply, TRUE); +} + +PUBLIC void tcp_reply_write(tcp_fd, reply) +tcp_fd_t *tcp_fd; +size_t reply; +{ + assert (tcp_fd->tf_flags & TFF_WRITE_IP); + + tcp_fd->tf_flags &= ~TFF_WRITE_IP; + reply_thr_get (tcp_fd, reply, FALSE); +} + +PUBLIC void tcp_reply_read(tcp_fd, reply) +tcp_fd_t *tcp_fd; +size_t reply; +{ + assert (tcp_fd->tf_flags & TFF_READ_IP); + + tcp_fd->tf_flags &= ~TFF_READ_IP; + reply_thr_put (tcp_fd, reply, FALSE); +} + +PUBLIC int tcp_write(fd, count) +int fd; +size_t count; +{ + tcp_fd_t *tcp_fd; + tcp_conn_t *tcp_conn; + + tcp_fd= &tcp_fd_table[fd]; + + assert (tcp_fd->tf_flags & TFF_INUSE); + + if (!(tcp_fd->tf_flags & TFF_CONNECTED)) + { + reply_thr_get (tcp_fd, ENOTCONN, FALSE); + return NW_OK; + } + tcp_conn= tcp_fd->tf_conn; + if (tcp_conn->tc_state == TCS_CLOSED) + { + reply_thr_get(tcp_fd, tcp_conn->tc_error, FALSE); + return NW_OK; + } + if (tcp_conn->tc_flags & TCF_FIN_SENT) + { + reply_thr_get (tcp_fd, ESHUTDOWN, FALSE); + return NW_OK; + } + + tcp_fd->tf_flags |= TFF_WRITE_IP; + tcp_fd->tf_write_offset= 0; + tcp_fd->tf_write_count= count; + + /* Start the timer (if necessary) */ + if (tcp_conn->tc_SND_UNA == tcp_conn->tc_SND_NXT && + tcp_conn->tc_transmit_seq == tcp_conn->tc_SND_UNA) + { + tcp_set_send_timer(tcp_conn); + } + assert(tcp_conn->tc_transmit_timer.tim_active || + (tcp_print_conn(tcp_conn), printf("\n"), 0)); + + assert(tcp_conn->tc_busy == 0); + tcp_conn->tc_busy++; + tcp_fd_write(tcp_conn); + tcp_conn->tc_busy--; + tcp_conn_write(tcp_conn, 0); + + if (!(tcp_fd->tf_flags & TFF_WRITE_IP)) + return NW_OK; + else + return NW_SUSPEND; +} + +PUBLIC int +tcp_read(fd, count) +int fd; +size_t count; +{ + tcp_fd_t *tcp_fd; + tcp_conn_t *tcp_conn; + + tcp_fd= &tcp_fd_table[fd]; + + assert (tcp_fd->tf_flags & TFF_INUSE); + + if (!(tcp_fd->tf_flags & TFF_CONNECTED)) + { + reply_thr_put (tcp_fd, ENOTCONN, FALSE); + return NW_OK; + } + tcp_conn= tcp_fd->tf_conn; + + tcp_fd->tf_flags |= TFF_READ_IP; + tcp_fd->tf_read_offset= 0; + tcp_fd->tf_read_count= count; + + assert(tcp_conn->tc_busy == 0); + tcp_conn->tc_busy++; + tcp_fd_read(tcp_conn, 0); + tcp_conn->tc_busy--; + if (!(tcp_fd->tf_flags & TFF_READ_IP)) + return NW_OK; + else + return NW_SUSPEND; +} + +/* +tcp_restart_connect + +reply the success or failure of a connect to the user. +*/ + + +PUBLIC void tcp_restart_connect(tcp_fd) +tcp_fd_t *tcp_fd; +{ + tcp_conn_t *tcp_conn; + int reply; + + assert(tcp_fd); + assert(tcp_fd->tf_flags & TFF_IOCTL_IP); + assert(tcp_fd->tf_ioreq == NWIOTCPLISTEN || + tcp_fd->tf_ioreq == NWIOTCPCONN); + + tcp_conn= tcp_fd->tf_conn; + + assert(tcp_conn); + assert(tcp_conn->tc_connInprogress); + + if (tcp_conn->tc_state == TCS_CLOSED) + { + reply= tcp_conn->tc_error; + assert(tcp_conn->tc_fd == tcp_fd); + tcp_fd->tf_conn= NULL; + tcp_conn->tc_fd= NULL; + } + else + { + tcp_fd->tf_flags |= TFF_CONNECTED; + reply= NW_OK; + } + tcp_conn->tc_connInprogress= 0; + tcp_reply_ioctl (tcp_fd, reply); +} + +/* +tcp_close +*/ + +PUBLIC void tcp_close(fd) +int fd; +{ + tcp_fd_t *tcp_fd; + tcp_conn_t *tcp_conn; + + tcp_fd= &tcp_fd_table[fd]; + + assert (tcp_fd->tf_flags & TFF_INUSE); + assert (!(tcp_fd->tf_flags & + (TFF_IOCTL_IP|TFF_READ_IP|TFF_WRITE_IP))); + + tcp_fd->tf_flags &= ~TFF_INUSE; + if (!tcp_fd->tf_conn) + return; + + tcp_conn= tcp_fd->tf_conn; + assert(tcp_conn->tc_fd == tcp_fd); + tcp_conn->tc_fd= 0; + + assert (!tcp_conn->tc_connInprogress); + + tcp_shutdown (tcp_conn); + if (tcp_conn->tc_state == TCS_ESTABLISHED) + { + tcp_conn->tc_state= TCS_CLOSING; + } + + /* Set the retransmission timeout a bit smaller. */ + tcp_conn->tc_rt_dead= TCP_DEF_RT_MAX_CLOSING; + + /* If all data has been acknowledged, close the connection. */ + if (tcp_conn->tc_SND_UNA == tcp_conn->tc_SND_NXT) + tcp_close_connection(tcp_conn, ENOTCONN); +} + +PUBLIC int tcp_cancel(fd, which_operation) +int fd; +int which_operation; +{ + tcp_fd_t *tcp_fd; + tcp_conn_t *tcp_conn; + int i; + + tcp_fd= &tcp_fd_table[fd]; + + assert (tcp_fd->tf_flags & TFF_INUSE); + + tcp_conn= tcp_fd->tf_conn; + + switch (which_operation) + { + case SR_CANCEL_WRITE: + assert (tcp_fd->tf_flags & TFF_WRITE_IP); + tcp_fd->tf_flags &= ~TFF_WRITE_IP; + + if (tcp_fd->tf_write_offset) + reply_thr_get (tcp_fd, tcp_fd->tf_write_offset, FALSE); + else + reply_thr_get (tcp_fd, EINTR, FALSE); + break; + case SR_CANCEL_READ: + assert (tcp_fd->tf_flags & TFF_READ_IP); + tcp_fd->tf_flags &= ~TFF_READ_IP; + if (tcp_fd->tf_read_offset) + reply_thr_put (tcp_fd, tcp_fd->tf_read_offset, FALSE); + else + reply_thr_put (tcp_fd, EINTR, FALSE); + break; + case SR_CANCEL_IOCTL: +assert (tcp_fd->tf_flags & TFF_IOCTL_IP); + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + + switch (tcp_fd->tf_ioreq) + { + case NWIOGTCPCONF: + reply_thr_put (tcp_fd, EINTR, TRUE); + break; + case NWIOSTCPCONF: + case NWIOTCPSHUTDOWN: + reply_thr_get (tcp_fd, EINTR, TRUE); + break; + case NWIOTCPCONN: + case NWIOTCPLISTEN: + assert (tcp_conn->tc_connInprogress); + tcp_conn->tc_connInprogress= 0; + tcp_conn->tc_fd= 0; + tcp_fd->tf_conn= 0; + tcp_close_connection(tcp_conn, ENOCONN); + reply_thr_get (tcp_fd, EINTR, TRUE); + break; + default: + ip_warning(( "unknown ioctl inprogress: 0x%x", + tcp_fd->tf_ioreq )); + reply_thr_get (tcp_fd, EINTR, TRUE); + break; + } + break; +#if !CRAMPED + default: + ip_panic(( "unknown cancel request" )); +#endif + } + return NW_OK; +} + +/* +tcp_connect +*/ + +PRIVATE int tcp_connect(tcp_fd) +tcp_fd_t *tcp_fd; +{ + tcp_conn_t *tcp_conn; + int state; + + if (!(tcp_fd->tf_flags & TFF_CONF_SET)) + { + tcp_reply_ioctl(tcp_fd, EBADMODE); + return NW_OK; + } + if (tcp_fd->tf_flags & TFF_CONNECT) + { + tcp_reply_ioctl(tcp_fd, EISCONN); + return NW_OK; + } + if ((tcp_fd->tf_tcpconf.nwtc_flags & (NWTC_SET_RA|NWTC_SET_RP)) + != (NWTC_SET_RA|NWTC_SET_RP)) + { + tcp_reply_ioctl(tcp_fd, EBADMODE); + return NW_OK; + } + + assert(!tcp_fd->tf_conn); + tcp_conn= find_conn_entry(tcp_fd->tf_tcpconf.nwtc_locport, + tcp_fd->tf_port->tp_ipaddr, + tcp_fd->tf_tcpconf.nwtc_remport, + tcp_fd->tf_tcpconf.nwtc_remaddr); + if (tcp_conn) + { + if (tcp_conn->tc_fd) + { + tcp_reply_ioctl(tcp_fd, EADDRINUSE); + return NW_OK; + } + } + else + { + tcp_conn= find_empty_conn(); + if (!tcp_conn) + { + tcp_reply_ioctl(tcp_fd, EAGAIN); + return NW_OK; + } + } + tcp_fd->tf_conn= tcp_conn; + + return tcp_su4connect(tcp_fd); +} + +/* +tcp_su4connect +*/ + +PRIVATE int tcp_su4connect(tcp_fd) +tcp_fd_t *tcp_fd; +{ + tcp_conn_t *tcp_conn; + acc_t *tmp_acc; + + tcp_conn= tcp_fd->tf_conn; + + tcp_conn->tc_locport= tcp_fd->tf_tcpconf.nwtc_locport; + tcp_conn->tc_locaddr= tcp_fd->tf_port->tp_ipaddr; + + assert (tcp_fd->tf_tcpconf.nwtc_flags & NWTC_SET_RP); + assert (tcp_fd->tf_tcpconf.nwtc_flags & NWTC_SET_RA); + tcp_conn->tc_remport= tcp_fd->tf_tcpconf.nwtc_remport; + tcp_conn->tc_remaddr= tcp_fd->tf_tcpconf.nwtc_remaddr; + + tcp_setup_conn(tcp_conn); + + tcp_conn->tc_fd= tcp_fd; + tcp_conn->tc_port= tcp_fd->tf_port; + tcp_conn->tc_connInprogress= 1; + tcp_conn->tc_orglisten= FALSE; + tcp_conn->tc_state= TCS_SYN_SENT; + tcp_conn->tc_rt_dead= TCP_DEF_RT_MAX_CONNECT; + + /* Start the timer (if necessary) */ + tcp_set_send_timer(tcp_conn); + + tcp_conn_write(tcp_conn, 0); + + if (tcp_conn->tc_connInprogress) + return NW_SUSPEND; + else + return NW_OK; +} + +PRIVATE int conn_right4fd(tcp_conn, tcp_fd) +tcp_fd_t *tcp_fd; +tcp_conn_t *tcp_conn; +{ + unsigned long flags; + + flags= tcp_fd->tf_tcpconf.nwtc_flags; + + if (tcp_fd->tf_tcpconf.nwtc_locport != tcp_conn->tc_locport) + return FALSE; + + if ((flags & NWTC_SET_RA) && tcp_fd->tf_tcpconf.nwtc_remaddr != + tcp_conn->tc_remaddr) + return FALSE; + + if ((flags & NWTC_SET_RP) && tcp_fd->tf_tcpconf.nwtc_remport != + tcp_conn->tc_remport) + return FALSE; + + if (tcp_fd->tf_port != tcp_conn->tc_port) + return FALSE; + + return TRUE; +} + +/* +tcp_listen +*/ + +PRIVATE int tcp_listen(tcp_fd) +tcp_fd_t *tcp_fd; +{ + tcp_conn_t *tcp_conn; + int state; + + if (!(tcp_fd->tf_flags & TFF_CONF_SET)) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, EBADMODE, TRUE); + return NW_OK; + } + if (tcp_fd->tf_flags & TFF_CONNECT) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get(tcp_fd, EISCONN, TRUE); + return NW_OK; + } + tcp_conn= tcp_fd->tf_conn; + assert(!tcp_conn); + + if ((tcp_fd->tf_tcpconf.nwtc_flags & (NWTC_SET_RA|NWTC_SET_RP)) + == (NWTC_SET_RA|NWTC_SET_RP)) + { + tcp_conn= find_conn_entry( + tcp_fd->tf_tcpconf.nwtc_locport, + tcp_fd->tf_port->tp_ipaddr, + tcp_fd->tf_tcpconf.nwtc_remport, + tcp_fd->tf_tcpconf.nwtc_remaddr); + if (tcp_conn) + { + if (tcp_conn->tc_fd) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get (tcp_fd, EADDRINUSE, TRUE); + return NW_OK; + } + tcp_fd->tf_conn= tcp_conn; + return tcp_su4listen(tcp_fd); + } + } + tcp_conn= find_empty_conn(); + if (!tcp_conn) + { + tcp_fd->tf_flags &= ~TFF_IOCTL_IP; + reply_thr_get (tcp_fd, EAGAIN, TRUE); + return NW_OK; + } + tcp_fd->tf_conn= tcp_conn; + return tcp_su4listen(tcp_fd); +} + +PRIVATE void tcp_buffree (priority) +int priority; +{ + int i; + tcp_conn_t *tcp_conn; + + if (priority == TCP_PRI_FRAG2SEND) + { + for (i=0, tcp_conn= tcp_conn_table; i<TCP_CONN_NR; i++, + tcp_conn++) + { + if (!(tcp_conn->tc_flags & TCF_INUSE)) + continue; + if (!tcp_conn->tc_frag2send) + continue; + if (tcp_conn->tc_busy) + continue; + bf_afree(tcp_conn->tc_frag2send); + tcp_conn->tc_frag2send= 0; + } + } + + if (priority == TCP_PRI_CONN_EXTRA) + { + for (i=0, tcp_conn= tcp_conn_table; i<TCP_CONN_NR; i++, + tcp_conn++) + { + if (!(tcp_conn->tc_flags & TCF_INUSE)) + continue; + if (tcp_conn->tc_busy) + continue; + if (tcp_conn->tc_adv_data) + { + bf_afree(tcp_conn->tc_adv_data); + tcp_conn->tc_adv_data= NULL; + } + } + } + + if (priority == TCP_PRI_CONNwoUSER) + { + for (i=0, tcp_conn= tcp_conn_table; i<TCP_CONN_NR; i++, + tcp_conn++) + { + if (!(tcp_conn->tc_flags & TCF_INUSE)) + continue; + if (tcp_conn->tc_busy) + continue; + if (tcp_conn->tc_fd) + continue; + if (tcp_conn->tc_state == TCS_CLOSED) + continue; + if (tcp_conn->tc_rcvd_data == NULL && + tcp_conn->tc_send_data == NULL) + { + continue; + } + tcp_close_connection (tcp_conn, EOUTOFBUFS); + } + } + + if (priority == TCP_PRI_CONN_INUSE) + { + for (i=0, tcp_conn= tcp_conn_table; i<TCP_CONN_NR; i++, + tcp_conn++) + { + if (!(tcp_conn->tc_flags & TCF_INUSE)) + continue; + if (tcp_conn->tc_busy) + continue; + if (tcp_conn->tc_state == TCS_CLOSED) + continue; + if (tcp_conn->tc_rcvd_data == NULL && + tcp_conn->tc_send_data == NULL) + { + continue; + } + tcp_close_connection (tcp_conn, EOUTOFBUFS); + } + } +} + +#ifdef BUF_CONSISTENCY_CHECK +PRIVATE void tcp_bufcheck() +{ + int i; + tcp_conn_t *tcp_conn; + tcp_port_t *tcp_port; + + for (i= 0, tcp_port= tcp_port_table; i<ip_conf_nr; i++, tcp_port++) + { + if (tcp_port->tp_pack) + bf_check_acc(tcp_port->tp_pack); + } + for (i= 0, tcp_conn= tcp_conn_table; i<TCP_CONN_NR; i++, tcp_conn++) + { + assert(!tcp_conn->tc_busy); + if (tcp_conn->tc_rcvd_data) + bf_check_acc(tcp_conn->tc_rcvd_data); + if (tcp_conn->tc_adv_data) + bf_check_acc(tcp_conn->tc_adv_data); + if (tcp_conn->tc_send_data) + bf_check_acc(tcp_conn->tc_send_data); + if (tcp_conn->tc_remipopt) + bf_check_acc(tcp_conn->tc_remipopt); + if (tcp_conn->tc_tcpopt) + bf_check_acc(tcp_conn->tc_tcpopt); + if (tcp_conn->tc_frag2send) + bf_check_acc(tcp_conn->tc_frag2send); + } +} +#endif + +PUBLIC void tcp_notreach(tcp_conn) +tcp_conn_t *tcp_conn; +{ + int new_ttl; + + new_ttl= tcp_conn->tc_ttl; + if (new_ttl == IP_MAX_TTL) + { + if (tcp_conn->tc_state == TCS_SYN_SENT) + tcp_close_connection(tcp_conn, EDSTNOTRCH); + return; + } + else if (new_ttl == TCP_DEF_TTL) + new_ttl= TCP_DEF_TTL_NEXT; + else + { + new_ttl *= 2; + if (new_ttl> IP_MAX_TTL) + new_ttl= IP_MAX_TTL; + } + tcp_conn->tc_ttl= new_ttl; + tcp_conn->tc_stt= 0; + tcp_conn->tc_SND_TRM= tcp_conn->tc_SND_UNA; + tcp_conn_write(tcp_conn, 1); +} + +/* +tcp_setup_conn +*/ + +PRIVATE void tcp_setup_conn(tcp_conn) +tcp_conn_t *tcp_conn; +{ + assert(!tcp_conn->tc_connInprogress); + if (tcp_conn->tc_flags & TCF_INUSE) + { + assert (tcp_conn->tc_state == TCS_CLOSED); + assert (!tcp_conn->tc_send_data); + if (tcp_conn->tc_senddis < get_time()) + tcp_conn->tc_ISS= 0; + } + else + { + assert(!tcp_conn->tc_busy); + tcp_conn->tc_senddis= 0; + tcp_conn->tc_ISS= 0; + tcp_conn->tc_tos= TCP_DEF_TOS; + tcp_conn->tc_ttl= TCP_DEF_TTL; + tcp_conn->tc_rcv_wnd= TCP_MAX_RCV_WND_SIZE; + tcp_conn->tc_fd= NULL; + } + if (!tcp_conn->tc_ISS) + { + tcp_conn->tc_ISS= (get_time()/HZ)*ISS_INC_FREQ; + } + tcp_conn->tc_SND_UNA= tcp_conn->tc_ISS; + tcp_conn->tc_SND_TRM= tcp_conn->tc_ISS; + tcp_conn->tc_SND_NXT= tcp_conn->tc_ISS+1; + tcp_conn->tc_SND_UP= tcp_conn->tc_ISS; + tcp_conn->tc_SND_PSH= tcp_conn->tc_ISS; + tcp_conn->tc_IRS= 0; + tcp_conn->tc_RCV_LO= tcp_conn->tc_IRS; + tcp_conn->tc_RCV_NXT= tcp_conn->tc_IRS; + tcp_conn->tc_RCV_HI= tcp_conn->tc_IRS; + tcp_conn->tc_RCV_UP= tcp_conn->tc_IRS; + + assert(tcp_conn->tc_rcvd_data == NULL); + assert(tcp_conn->tc_adv_data == NULL); + assert(tcp_conn->tc_send_data == NULL); + tcp_conn->tc_remipopt= NULL; + tcp_conn->tc_tcpopt= NULL; + + assert(tcp_conn->tc_frag2send == NULL); + + tcp_conn->tc_stt= 0; + tcp_conn->tc_rt_dead= TCP_DEF_RT_DEAD; + tcp_conn->tc_0wnd_to= 0; + tcp_conn->tc_rtt= TCP_DEF_RTT; + tcp_conn->tc_mss= TCP_DEF_MSS; + tcp_conn->tc_error= NW_OK; + tcp_conn->tc_snd_cwnd= tcp_conn->tc_SND_UNA + 2*tcp_conn->tc_mss; + tcp_conn->tc_snd_cthresh= TCP_MAX_SND_WND_SIZE; + tcp_conn->tc_snd_cinc= + (long)TCP_DEF_MSS*TCP_DEF_MSS/TCP_MAX_SND_WND_SIZE+1; + tcp_conn->tc_snd_wnd= TCP_MAX_SND_WND_SIZE; + tcp_conn->tc_rt_time= 0; + tcp_conn->tc_rt_seq= 0; + tcp_conn->tc_rt_threshold= tcp_conn->tc_ISS; + tcp_conn->tc_flags= TCF_INUSE; + + clck_untimer(&tcp_conn->tc_transmit_timer); + tcp_conn->tc_transmit_seq= 0; +} + +/* + * $PchId: tcp.c,v 1.14.2.2 1999/11/17 22:05:27 philip Exp $ + */ diff --git a/servers/inet/generic/tcp.h b/servers/inet/generic/tcp.h new file mode 100644 index 000000000..53194d07c --- /dev/null +++ b/servers/inet/generic/tcp.h @@ -0,0 +1,79 @@ +/* +tcp.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef TCP_H +#define TCP_H + +#define ISS_INC_FREQ 250000L +#define TCP_MAX_DATAGRAM 8192 + +#ifndef TCP_MAX_SND_WND_SIZE +#define TCP_MAX_SND_WND_SIZE ((CRAMPED ? 4 : 16) * 1024) +#endif + +#ifndef TCP_MIN_RCV_WND_SIZE +#define TCP_MIN_RCV_WND_SIZE (4 * 1024) +#endif + +#ifndef TCP_MAX_RCV_WND_SIZE +#define TCP_MAX_RCV_WND_SIZE ((CRAMPED ? 4 : 8) * 1024) +#endif + +#define TCP_DEF_TOS 0 +#define TCP_DEF_TTL 5 /* hops/seconds */ +#define TCP_DEF_TTL_NEXT 30 /* hops/seconds */ + +/* An established TCP connection times out if no communication is possible + * for TCP_DEF_RT_DEAD clock ticks + */ +#ifndef TCP_DEF_RT_DEAD +#define TCP_DEF_RT_DEAD (20L*60*HZ) +#endif + +#define TCP_DEF_RT_MAX_CONNECT (5L*60*HZ) /* 5 minutes in ticks */ +#define TCP_DEF_RT_MAX_LISTEN (1L*60*HZ) /* 1 minute in ticks */ +#define TCP_DEF_RT_MAX_CLOSING (1L*60*HZ) /* 1 minute in ticks */ + +/* Minimum and maximum intervals for zero window probes. */ +#define TCP_0WND_MIN (HZ) +#define TCP_0WND_MAX (5*60*HZ) + +#define TCP_DEF_RTT 15 /* initial retransmission time in + * ticks + */ +#define TCP_RTT_GRAN 5 /* minimal value of the rtt is + * TCP_RTT_GRAN * CLOCK_GRAN + */ +#define TCP_RTT_MAX (10*HZ) /* The maximum retransmission interval + * is TCP_RTT_MAX ticks + */ + +#ifndef TCP_DEF_MSS +#define TCP_DEF_MSS 1400 +#endif + +#define TCP_DEF_CONF (NWTC_COPY | NWTC_LP_UNSET | NWTC_UNSET_RA | \ + NWTC_UNSET_RP) +#define TCP_DEF_OPT (NWTO_NOFLAG) + +struct acc; + +void tcp_prep ARGS(( void )); +void tcp_init ARGS(( void )); +int tcp_open ARGS(( int port, int srfd, + get_userdata_t get_userdata, put_userdata_t put_userdata, + put_pkt_t put_pkt )); +int tcp_read ARGS(( int fd, size_t count)); +int tcp_write ARGS(( int fd, size_t count)); +int tcp_ioctl ARGS(( int fd, ioreq_t req)); +int tcp_cancel ARGS(( int fd, int which_operation )); +void tcp_close ARGS(( int fd)); + +#endif /* TCP_H */ + +/* + * $PchId: tcp.h,v 1.8 1996/05/07 20:51:37 philip Exp $ + */ diff --git a/servers/inet/generic/tcp_int.h b/servers/inet/generic/tcp_int.h new file mode 100644 index 000000000..0e59ca55a --- /dev/null +++ b/servers/inet/generic/tcp_int.h @@ -0,0 +1,227 @@ +/* +tcp_int.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef TCP_INT_H +#define TCP_INT_H + +#define TCP_CONN_HASH_SHIFT 4 +#define TCP_CONN_HASH_NR (1 << TCP_CONN_HASH_SHIFT) + +typedef struct tcp_port +{ + int tp_ipdev; + int tp_flags; + int tp_state; + int tp_ipfd; + acc_t *tp_pack; + ipaddr_t tp_ipaddr; + struct tcp_conn *tp_snd_head; + struct tcp_conn *tp_snd_tail; + event_t tp_snd_event; + struct tcp_conn *tp_conn_hash[TCP_CONN_HASH_NR][4]; +} tcp_port_t; + +#define TPF_EMPTY 0x0 +#define TPF_SUSPEND 0x1 +#define TPF_READ_IP 0x2 +#define TPF_READ_SP 0x4 +#define TPF_WRITE_IP 0x8 +#define TPF_WRITE_SP 0x10 +#define TPF_DELAY_TCP 0x40 + +#define TPS_EMPTY 0 +#define TPS_SETPROTO 1 +#define TPS_GETCONF 2 +#define TPS_MAIN 3 +#define TPS_ERROR 4 + +typedef struct tcp_fd +{ + int tf_flags; + tcp_port_t *tf_port; + int tf_srfd; + ioreq_t tf_ioreq; + nwio_tcpconf_t tf_tcpconf; + nwio_tcpopt_t tf_tcpopt; + get_userdata_t tf_get_userdata; + put_userdata_t tf_put_userdata; + struct tcp_conn *tf_conn; + size_t tf_write_offset; + size_t tf_write_count; + size_t tf_read_offset; + size_t tf_read_count; +} tcp_fd_t; + +#define TFF_EMPTY 0x0 +#define TFF_INUSE 0x1 +#define TFF_IOCTL_IP 0x2 +#define TFF_CONF_SET 0x4 +#define TFF_IOC_INIT_SP 0x8 +#define TFF_CONNECT 0x20 +#define TFF_WRITE_IP 0x80 +#define TFF_WR_URG 0x100 +#define TFF_PUSH_DATA 0x200 +#define TFF_READ_IP 0x400 +#define TFF_RECV_URG 0x800 +#define TFF_CONNECTED 0x1000 +#define TFF_DEL_RST 0x2000 + +typedef struct tcp_conn +{ + int tc_flags; + int tc_state; + int tc_busy; /* do not steal buffer when a counnection is + * busy + */ + tcp_port_t *tc_port; + tcp_fd_t *tc_fd; + + tcpport_t tc_locport; + ipaddr_t tc_locaddr; + tcpport_t tc_remport; + ipaddr_t tc_remaddr; + +#if 1 + int tc_connInprogress; +#endif + int tc_orglisten; + time_t tc_senddis; + + /* Sending side */ + u32_t tc_ISS; /* initial sequence number */ + u32_t tc_SND_UNA; /* least unacknowledged sequence number */ + u32_t tc_SND_TRM; /* next sequence number to be transmitted */ + u32_t tc_SND_NXT; /* next sequence number for new data */ + u32_t tc_SND_UP; /* urgent pointer, first sequence number not + * urgent */ + u32_t tc_SND_PSH; /* push pointer, data should be pushed until + * the push pointer is reached */ + + u32_t tc_snd_cwnd; /* highest sequence number to be sent */ + u32_t tc_snd_cthresh; /* threshold for send window */ + u32_t tc_snd_cinc; /* increment for send window threshold */ + u16_t tc_snd_wnd; /* max send queue size */ + + /* round trip calculation. */ + time_t tc_rt_time; + u32_t tc_rt_seq; + u32_t tc_rt_threshold; + time_t tc_rtt; + + acc_t *tc_send_data; + acc_t *tc_frag2send; + struct tcp_conn *tc_send_link; + + /* Receiving side */ + u32_t tc_IRS; + u32_t tc_RCV_LO; + u32_t tc_RCV_NXT; + u32_t tc_RCV_HI; + u32_t tc_RCV_UP; + + u16_t tc_rcv_wnd; + acc_t *tc_rcvd_data; + acc_t *tc_adv_data; + u32_t tc_adv_seq; + + acc_t *tc_remipopt; + acc_t *tc_tcpopt; + u8_t tc_tos; + u8_t tc_ttl; + u16_t tc_mss; + + struct timer tc_transmit_timer; + u32_t tc_transmit_seq; + time_t tc_0wnd_to; + time_t tc_stt; /* time of first send after last ack */ + time_t tc_rt_dead; + + int tc_error; + int tc_inconsistent; +} tcp_conn_t; + +#define TCF_EMPTY 0x0 +#define TCF_INUSE 0x1 +#define TCF_FIN_RECV 0x2 +#define TCF_RCV_PUSH 0x4 +#define TCF_MORE2WRITE 0x8 +#define TCF_SEND_ACK 0x10 +#define TCF_FIN_SENT 0x20 +#define TCF_BSD_URG 0x40 + +#if DEBUG & 0x200 +#define TCF_DEBUG 0x1000 +#endif + +#define TCS_CLOSED 0 +#define TCS_LISTEN 1 +#define TCS_SYN_RECEIVED 2 +#define TCS_SYN_SENT 3 +#define TCS_ESTABLISHED 4 +#define TCS_CLOSING 5 + +/* tcp_recv.c */ +void tcp_frag2conn ARGS(( tcp_conn_t *tcp_conn, ip_hdr_t *ip_hdr, + tcp_hdr_t *tcp_hdr, acc_t *tcp_data, size_t data_len )); +void tcp_fd_read ARGS(( tcp_conn_t *tcp_conn, int enq )); + +/* tcp_send.c */ +void tcp_conn_write ARGS(( tcp_conn_t *tcp_conn, int enq )); +void tcp_release_retrans ARGS(( tcp_conn_t *tcp_conn, u32_t seg_ack, + U16_t new_win )); +void tcp_set_send_timer ARGS(( tcp_conn_t *tcp_conn )); +void tcp_fd_write ARGS(( tcp_conn_t *tcp_conn )); +void tcp_close_connection ARGS(( tcp_conn_t *tcp_conn, + int error )); +void tcp_port_write ARGS(( tcp_port_t *tcp_port )); +void tcp_shutdown ARGS(( tcp_conn_t *tcp_conn )); + +/* tcp_lib.c */ +void tcp_extract_ipopt ARGS(( tcp_conn_t *tcp_conn, + ip_hdr_t *ip_hdr )); +void tcp_extract_tcpopt ARGS(( tcp_conn_t *tcp_conn, + tcp_hdr_t *tcp_hdr )); +void tcp_get_ipopt ARGS(( tcp_conn_t *tcp_conn, ip_hdropt_t + *ip_hdropt )); +void tcp_get_tcpopt ARGS(( tcp_conn_t *tcp_conn, tcp_hdropt_t + *tcp_hdropt )); +acc_t *tcp_make_header ARGS(( tcp_conn_t *tcp_conn, + ip_hdr_t **ref_ip_hdr, tcp_hdr_t **ref_tcp_hdr, acc_t *data )); +u16_t tcp_pack_oneCsum ARGS(( ip_hdr_t *ip_hdr, acc_t *tcp_pack )); +int tcp_check_conn ARGS(( tcp_conn_t *tcp_conn )); +void tcp_print_pack ARGS(( ip_hdr_t *ip_hdr, tcp_hdr_t *tcp_hdr )); +void tcp_print_state ARGS(( tcp_conn_t *tcp_conn )); +void tcp_print_conn ARGS(( tcp_conn_t *tcp_conn )); +int tcp_LEmod4G ARGS(( u32_t n1, u32_t n2 )); +int tcp_Lmod4G ARGS(( u32_t n1, u32_t n2 )); +int tcp_GEmod4G ARGS(( u32_t n1, u32_t n2 )); +int tcp_Gmod4G ARGS(( u32_t n1, u32_t n2 )); + +/* tcp.c */ +void tcp_restart_connect ARGS(( tcp_fd_t *tcp_fd )); +int tcp_su4listen ARGS(( tcp_fd_t *tcp_fd )); +void tcp_reply_ioctl ARGS(( tcp_fd_t *tcp_fd, int reply )); +void tcp_reply_write ARGS(( tcp_fd_t *tcp_fd, size_t reply )); +void tcp_reply_read ARGS(( tcp_fd_t *tcp_fd, size_t reply )); +void tcp_notreach ARGS(( tcp_conn_t *tcp_conn )); + +#define TCP_FD_NR (10*IP_PORT_MAX) +#define TCP_CONN_NR (2*TCP_FD_NR) + +EXTERN tcp_port_t *tcp_port_table; +EXTERN tcp_conn_t tcp_conn_table[TCP_CONN_NR]; +EXTERN tcp_fd_t tcp_fd_table[TCP_FD_NR]; + +#define tcp_Lmod4G(n1,n2) (!!(((n1)-(n2)) & 0x80000000L)) +#define tcp_GEmod4G(n1,n2) (!(((n1)-(n2)) & 0x80000000L)) +#define tcp_Gmod4G(n1,n2) (!!(((n2)-(n1)) & 0x80000000L)) +#define tcp_LEmod4G(n1,n2) (!(((n2)-(n1)) & 0x80000000L)) + +#endif /* TCP_INT_H */ + +/* + * $PchId: tcp_int.h,v 1.10 1996/05/07 20:51:59 philip Exp $ + */ diff --git a/servers/inet/generic/tcp_lib.c b/servers/inet/generic/tcp_lib.c new file mode 100644 index 000000000..243262e68 --- /dev/null +++ b/servers/inet/generic/tcp_lib.c @@ -0,0 +1,540 @@ +/* +tcp_lib.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "buf.h" +#include "clock.h" +#include "event.h" +#include "io.h" +#include "type.h" + +#include "assert.h" +#include "tcp_int.h" + +THIS_FILE + +#if you_want_to_be_complete + +#undef tcp_LEmod4G +PUBLIC int tcp_LEmod4G(n1, n2) +u32_t n1; +u32_t n2; +{ + return !((u32_t)(n2-n1) & 0x80000000L); +} + +#undef tcp_GEmod4G +PUBLIC int tcp_GEmod4G(n1, n2) +u32_t n1; +u32_t n2; +{ + return !((u32_t)(n1-n2) & 0x80000000L); +} + +#undef tcp_Lmod4G +PUBLIC int tcp_Lmod4G(n1, n2) +u32_t n1; +u32_t n2; +{ + return !!((u32_t)(n1-n2) & 0x80000000L); +} + +#undef tcp_Gmod4G +PUBLIC int tcp_Gmod4G(n1, n2) +u32_t n1; +u32_t n2; +{ + return !!((u32_t)(n2-n1) & 0x80000000L); +} +#endif + +PUBLIC void tcp_extract_ipopt(tcp_conn, ip_hdr) +tcp_conn_t *tcp_conn; +ip_hdr_t *ip_hdr; +{ + int ip_hdr_len; + + ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; + if (ip_hdr_len == IP_MIN_HDR_SIZE) + return; + + DBLOCK(1, printf("ip_hdr options NOT supported (yet?)\n")); +} + +PUBLIC void tcp_extract_tcpopt(tcp_conn, tcp_hdr) +tcp_conn_t *tcp_conn; +tcp_hdr_t *tcp_hdr; +{ + int tcp_hdr_len; + + tcp_hdr_len= (tcp_hdr->th_data_off & TH_DO_MASK) >> 2; + if (tcp_hdr_len == TCP_MIN_HDR_SIZE) + return; + + DBLOCK(2, printf("tcp_hdr options NOT supported (yet?)\n")); +} + +PUBLIC u16_t tcp_pack_oneCsum(ip_hdr, tcp_pack) +ip_hdr_t *ip_hdr; +acc_t *tcp_pack; +{ + size_t ip_hdr_len; + acc_t *pack; + u16_t sum; + u16_t word_buf[6]; + int odd_length; + char *data_ptr; + int length; + + ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; + word_buf[0]= ip_hdr->ih_src & 0xffff; + word_buf[1]= (ip_hdr->ih_src >> 16) & 0xffff; + word_buf[2]= ip_hdr->ih_dst & 0xffff; + word_buf[3]= (ip_hdr->ih_dst >> 16) & 0xffff; + word_buf[4]= HTONS(IPPROTO_TCP); + word_buf[5]= htons(ntohs(ip_hdr->ih_length)-ip_hdr_len); + sum= oneC_sum(0, word_buf, sizeof(word_buf)); + + pack= tcp_pack; + odd_length= 0; + for (; pack; pack= pack->acc_next) + { + + data_ptr= ptr2acc_data(pack); + length= pack->acc_length; + + if (!length) + continue; + sum= oneC_sum (sum, (u16_t *)data_ptr, length); + if (length & 1) + { + odd_length= !odd_length; + sum= ((sum >> 8) & 0xff) | ((sum & 0xff) << 8); + } + } + if (odd_length) + { + /* Undo the last swap */ + sum= ((sum >> 8) & 0xff) | ((sum & 0xff) << 8); + } + return sum; +} + +PUBLIC void tcp_get_ipopt(tcp_conn, ip_hdropt) +tcp_conn_t *tcp_conn; +ip_hdropt_t *ip_hdropt; +{ + if (!tcp_conn->tc_remipopt) + { + ip_hdropt->iho_opt_siz= 0; + return; + } + DBLOCK(1, printf("ip_hdr options NOT supported (yet?)\n")); + ip_hdropt->iho_opt_siz= 0; + return; +} + +PUBLIC void tcp_get_tcpopt(tcp_conn, tcp_hdropt) +tcp_conn_t *tcp_conn; +tcp_hdropt_t *tcp_hdropt; +{ + int optsiz; + + if (!tcp_conn->tc_tcpopt) + { + tcp_hdropt->tho_opt_siz= 0; + return; + } + tcp_conn->tc_tcpopt= bf_pack(tcp_conn->tc_tcpopt); + optsiz= bf_bufsize(tcp_conn->tc_tcpopt); + memcpy(tcp_hdropt->tho_data, ptr2acc_data(tcp_conn->tc_tcpopt), + optsiz); + if ((optsiz & 3) != 0) + { + tcp_hdropt->tho_data[optsiz]= TCP_OPT_EOL; + optsiz= (optsiz+3) & ~3; + } + tcp_hdropt->tho_opt_siz= optsiz; + + return; +} + +PUBLIC acc_t *tcp_make_header(tcp_conn, ref_ip_hdr, ref_tcp_hdr, data) +tcp_conn_t *tcp_conn; +ip_hdr_t **ref_ip_hdr; +tcp_hdr_t **ref_tcp_hdr; +acc_t *data; +{ + ip_hdropt_t ip_hdropt; + tcp_hdropt_t tcp_hdropt; + ip_hdr_t *ip_hdr; + tcp_hdr_t *tcp_hdr; + acc_t *hdr_acc; + char *ptr2hdr; + int closed_connection; + + closed_connection= (tcp_conn->tc_state == TCS_CLOSED); + + if (tcp_conn->tc_remipopt || tcp_conn->tc_tcpopt) + { + tcp_get_ipopt (tcp_conn, &ip_hdropt); + tcp_get_tcpopt (tcp_conn, &tcp_hdropt); + assert (!(ip_hdropt.iho_opt_siz & 3)); + assert (!(tcp_hdropt.tho_opt_siz & 3)); + + hdr_acc= bf_memreq(IP_MIN_HDR_SIZE+ + ip_hdropt.iho_opt_siz+TCP_MIN_HDR_SIZE+ + tcp_hdropt.tho_opt_siz); + ptr2hdr= ptr2acc_data(hdr_acc); + + ip_hdr= (ip_hdr_t *)ptr2hdr; + ptr2hdr += IP_MIN_HDR_SIZE; + + if (ip_hdropt.iho_opt_siz) + { + memcpy(ptr2hdr, (char *)ip_hdropt.iho_data, + ip_hdropt.iho_opt_siz); + } + ptr2hdr += ip_hdropt.iho_opt_siz; + + tcp_hdr= (tcp_hdr_t *)ptr2hdr; + ptr2hdr += TCP_MIN_HDR_SIZE; + + if (tcp_hdropt.tho_opt_siz) + { + memcpy (ptr2hdr, (char *)tcp_hdropt.tho_data, + tcp_hdropt.tho_opt_siz); + } + hdr_acc->acc_next= data; + + ip_hdr->ih_vers_ihl= (IP_MIN_HDR_SIZE+ + ip_hdropt.iho_opt_siz) >> 2; + tcp_hdr->th_data_off= (TCP_MIN_HDR_SIZE+ + tcp_hdropt.tho_opt_siz) << 2; + } + else + { + hdr_acc= bf_memreq(IP_MIN_HDR_SIZE+TCP_MIN_HDR_SIZE); + ip_hdr= (ip_hdr_t *)ptr2acc_data(hdr_acc); + tcp_hdr= (tcp_hdr_t *)&ip_hdr[1]; + hdr_acc->acc_next= data; + + ip_hdr->ih_vers_ihl= IP_MIN_HDR_SIZE >> 2; + tcp_hdr->th_data_off= TCP_MIN_HDR_SIZE << 2; + } + + if (!closed_connection && (tcp_conn->tc_state == TCS_CLOSED)) + { + DBLOCK(1, printf("connection closed while inuse\n")); + bf_afree(hdr_acc); + return 0; + } + + ip_hdr->ih_tos= tcp_conn->tc_tos; + ip_hdr->ih_ttl= tcp_conn->tc_ttl; + ip_hdr->ih_proto= IPPROTO_TCP; + ip_hdr->ih_src= tcp_conn->tc_locaddr; + ip_hdr->ih_dst= tcp_conn->tc_remaddr; + ip_hdr->ih_flags_fragoff= 0; + + tcp_hdr->th_srcport= tcp_conn->tc_locport; + tcp_hdr->th_dstport= tcp_conn->tc_remport; + tcp_hdr->th_seq_nr= tcp_conn->tc_RCV_NXT; + tcp_hdr->th_flags= 0; + tcp_hdr->th_window= htons(tcp_conn->tc_RCV_HI-tcp_conn->tc_RCV_LO); + tcp_hdr->th_chksum= 0; + *ref_ip_hdr= ip_hdr; + *ref_tcp_hdr= tcp_hdr; + return hdr_acc; +} + +#if !CRAMPED +PUBLIC void tcp_print_state (tcp_conn) +tcp_conn_t *tcp_conn; +{ + printf("tcp_conn_table[%d]->tc_state= ", tcp_conn- + tcp_conn_table); + if (!(tcp_conn->tc_flags & TCF_INUSE)) + { + printf("not inuse\n"); + return; + } + switch (tcp_conn->tc_state) + { + case TCS_CLOSED: printf("CLOSED"); break; + case TCS_LISTEN: printf("LISTEN"); break; + case TCS_SYN_RECEIVED: printf("SYN_RECEIVED"); break; + case TCS_SYN_SENT: printf("SYN_SENT"); break; + case TCS_ESTABLISHED: printf("ESTABLISHED"); break; + case TCS_CLOSING: printf("CLOSING"); break; + default: printf("unknown (=%d)", tcp_conn->tc_state); break; + } +} +#endif + +PUBLIC int tcp_check_conn(tcp_conn) +tcp_conn_t *tcp_conn; +{ + int allright; + u32_t lo_queue, hi_queue; + int size; + + allright= TRUE; + if (tcp_conn->tc_inconsistent) + { + assert(tcp_conn->tc_inconsistent == 1); +#if !CRAMPED + printf("tcp_check_conn: connection is inconsistent\n"); +#endif + return allright; + } + + /* checking receive queue */ + lo_queue= tcp_conn->tc_RCV_LO; + if (lo_queue == tcp_conn->tc_IRS) + lo_queue++; + if (lo_queue == tcp_conn->tc_RCV_NXT && (tcp_conn->tc_flags & + TCF_FIN_RECV)) + lo_queue--; + hi_queue= tcp_conn->tc_RCV_NXT; + if (hi_queue == tcp_conn->tc_IRS) + hi_queue++; + if (tcp_conn->tc_flags & TCF_FIN_RECV) + hi_queue--; + + size= hi_queue-lo_queue; + if (size<0) + { +#if !CRAMPED + printf("rcv hi_queue-lo_queue < 0\n"); + printf("SND_NXT= 0x%x, SND_UNA= 0x%x\n", + tcp_conn->tc_SND_NXT, tcp_conn->tc_SND_UNA); + printf("lo_queue= 0x%x, hi_queue= 0x%x\n", + lo_queue, hi_queue); + printf("size= %d\n", size); +#endif + allright= FALSE; + } + else if (!tcp_conn->tc_rcvd_data) + { + if (size) + { +#if !CRAMPED + printf("RCV_NXT-RCV_LO != 0\n"); + tcp_print_conn(tcp_conn); + printf("lo_queue= %lu, hi_queue= %lu\n", + lo_queue, hi_queue); +#endif + allright= FALSE; + } + } + else if (size != bf_bufsize(tcp_conn->tc_rcvd_data)) + { +#if !CRAMPED + printf("RCV_NXT-RCV_LO != sizeof tc_rcvd_data\n"); + tcp_print_conn(tcp_conn); + printf( + "lo_queue= %lu, hi_queue= %lu, sizeof tc_rcvd_data= %d\n", + lo_queue, hi_queue, bf_bufsize(tcp_conn->tc_rcvd_data)); +#endif + allright= FALSE; + } + else if (size != 0 && (tcp_conn->tc_state == TCS_CLOSED || + tcp_conn->tc_state == TCS_LISTEN || + tcp_conn->tc_state == TCS_SYN_RECEIVED || + tcp_conn->tc_state == TCS_SYN_SENT)) + { +#if !CRAMPED + printf("received data but not connected\n"); + tcp_print_conn(tcp_conn); +#endif + allright= FALSE; + } + if (tcp_Lmod4G(tcp_conn->tc_RCV_HI, tcp_conn->tc_RCV_NXT)) + { +#if !CRAMPED + printf("tc_RCV_HI (%d) < tc_RCV_NXT (%d)\n", + tcp_conn->tc_RCV_HI, tcp_conn->tc_RCV_NXT); +#endif + allright= FALSE; + } + + /* checking send data */ + lo_queue= tcp_conn->tc_SND_UNA; + if (lo_queue == tcp_conn->tc_ISS) + lo_queue++; + if (lo_queue == tcp_conn->tc_SND_NXT && + (tcp_conn->tc_flags & TCF_FIN_SENT)) + { + lo_queue--; + } + hi_queue= tcp_conn->tc_SND_NXT; + if (hi_queue == tcp_conn->tc_ISS) + hi_queue++; + if (tcp_conn->tc_flags & TCF_FIN_SENT) + hi_queue--; + + size= hi_queue-lo_queue; + if (size<0) + { +#if !CRAMPED + printf("snd hi_queue-lo_queue < 0\n"); + printf("SND_ISS= 0x%x, SND_UNA= 0x%x, SND_NXT= 0x%x\n", + tcp_conn->tc_ISS, tcp_conn->tc_SND_UNA, + tcp_conn->tc_SND_NXT); + printf("hi_queue= 0x%x, lo_queue= 0x%x, size= %d\n", + hi_queue, lo_queue, size); +#endif + allright= FALSE; + } + else if (!tcp_conn->tc_send_data) + { + if (size) + { +#if !CRAMPED + printf("SND_NXT-SND_UNA != 0\n"); + printf("SND_NXT= %d, SND_UNA= %d\n", + tcp_conn->tc_SND_NXT, tcp_conn->tc_SND_UNA); + printf("lo_queue= %d, hi_queue= %d\n", + lo_queue, hi_queue); +#endif + allright= FALSE; + } + } + else if (size != bf_bufsize(tcp_conn->tc_send_data)) + { +#if !CRAMPED + printf("SND_NXT-SND_UNA != sizeof tc_send_data\n"); + printf("SND_NXT= %d, SND_UNA= %d\n", + tcp_conn->tc_SND_NXT, tcp_conn->tc_SND_UNA); + printf("lo_queue= %d, lo_queue= %d\n", + lo_queue, hi_queue); + printf("bf_bufsize(data)= %d\n", + bf_bufsize(tcp_conn->tc_send_data)); +#endif + allright= FALSE; + } + + /* checking counters */ + if (!tcp_GEmod4G(tcp_conn->tc_SND_UNA, tcp_conn->tc_ISS)) + { +#if !CRAMPED + printf("SND_UNA < ISS\n"); +#endif + allright= FALSE; + } + if (!tcp_GEmod4G(tcp_conn->tc_SND_NXT, tcp_conn->tc_SND_UNA)) + { +#if !CRAMPED + printf("SND_NXT<SND_UNA\n"); +#endif + allright= FALSE; + } + if (!tcp_GEmod4G(tcp_conn->tc_SND_TRM, tcp_conn->tc_SND_UNA)) + { +#if !CRAMPED + printf("SND_TRM<SND_UNA\n"); +#endif + allright= FALSE; + } + if (!tcp_GEmod4G(tcp_conn->tc_SND_NXT, tcp_conn->tc_SND_TRM)) + { +#if !CRAMPED + printf("SND_NXT<SND_TRM\n"); +#endif + allright= FALSE; + } + + DIFBLOCK(1, (!allright), printf("tcp_check_conn: not allright\n")); + return allright; +} + +#if !CRAMPED +PUBLIC void tcp_print_pack(ip_hdr, tcp_hdr) +ip_hdr_t *ip_hdr; +tcp_hdr_t *tcp_hdr; +{ + int tcp_hdr_len; + + assert(tcp_hdr); + if (ip_hdr) + writeIpAddr(ip_hdr->ih_src); + else + printf("???"); + printf(",%u ", ntohs(tcp_hdr->th_srcport)); + if (ip_hdr) + writeIpAddr(ip_hdr->ih_dst); + else + printf("???"); + printf(",%u ", ntohs(tcp_hdr->th_dstport)); + printf(" 0x%lx", ntohl(tcp_hdr->th_seq_nr)); + if (tcp_hdr->th_flags & THF_FIN) + printf(" <FIN>"); + if (tcp_hdr->th_flags & THF_SYN) + printf(" <SYN>"); + if (tcp_hdr->th_flags & THF_RST) + printf(" <RST>"); + if (tcp_hdr->th_flags & THF_PSH) + printf(" <PSH>"); + if (tcp_hdr->th_flags & THF_ACK) + printf(" <ACK 0x%x %u>", ntohl(tcp_hdr->th_ack_nr), + ntohs(tcp_hdr->th_window)); + if (tcp_hdr->th_flags & THF_URG) + printf(" <URG %u>", tcp_hdr->th_urgptr); + tcp_hdr_len= (tcp_hdr->th_data_off & TH_DO_MASK) >> 2; + if (tcp_hdr_len != TCP_MIN_HDR_SIZE) + printf(" <options %d>", tcp_hdr_len-TCP_MIN_HDR_SIZE); +} +#endif + +#if !CRAMPED +PUBLIC void tcp_print_conn(tcp_conn) +tcp_conn_t *tcp_conn; +{ + int iss, irs; + + iss= tcp_conn->tc_ISS; + irs= tcp_conn->tc_IRS; + + tcp_print_state (tcp_conn); + printf( + " ISS 0x%lx UNA +0x%lx(0x%lx) TRM +0x%lx(0x%lx) NXT +0x%lx(0x%lx)", + iss, tcp_conn->tc_SND_UNA-iss, tcp_conn->tc_SND_UNA, + tcp_conn->tc_SND_TRM-iss, tcp_conn->tc_SND_TRM, + tcp_conn->tc_SND_NXT-iss, tcp_conn->tc_SND_NXT); + printf(" snd_cwnd +0x%lx(0x%lx)", + tcp_conn->tc_snd_cwnd-tcp_conn->tc_SND_UNA, + tcp_conn->tc_snd_cwnd); + printf(" transmit_seq "); + if (tcp_conn->tc_transmit_seq == 0) + printf("0"); + else + { + printf("+0x%lx(0x%lx)", tcp_conn->tc_transmit_seq-iss, + tcp_conn->tc_transmit_seq); + } + printf(" IRS 0x%lx LO +0x%x(0x%lx) NXT +0x%x(0x%lx) HI +0x%x(0x%lx)", + irs, tcp_conn->tc_RCV_LO-irs, tcp_conn->tc_RCV_LO, + tcp_conn->tc_RCV_NXT-irs, tcp_conn->tc_RCV_NXT, + tcp_conn->tc_RCV_HI-irs, tcp_conn->tc_RCV_HI); + if (tcp_conn->tc_flags & TCF_INUSE) + printf(" TCF_INUSE"); + if (tcp_conn->tc_flags & TCF_FIN_RECV) + printf(" TCF_FIN_RECV"); + if (tcp_conn->tc_flags & TCF_RCV_PUSH) + printf(" TCF_RCV_PUSH"); + if (tcp_conn->tc_flags & TCF_MORE2WRITE) + printf(" TCF_MORE2WRITE"); + if (tcp_conn->tc_flags & TCF_SEND_ACK) + printf(" TCF_SEND_ACK"); + if (tcp_conn->tc_flags & TCF_FIN_SENT) + printf(" TCF_FIN_SENT"); +} +#endif + +/* + * $PchId: tcp_lib.c,v 1.7 1995/11/21 06:45:27 philip Exp $ + */ diff --git a/servers/inet/generic/tcp_recv.c b/servers/inet/generic/tcp_recv.c new file mode 100644 index 000000000..ed73fb3af --- /dev/null +++ b/servers/inet/generic/tcp_recv.c @@ -0,0 +1,1253 @@ +/* +tcp_recv.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "buf.h" +#include "clock.h" +#include "event.h" +#include "type.h" + +#include "io.h" +#include "tcp_int.h" +#include "tcp.h" +#include "assert.h" + +THIS_FILE + +FORWARD void create_RST ARGS(( tcp_conn_t *tcp_conn, + ip_hdr_t *ip_hdr, tcp_hdr_t *tcp_hdr, int data_len )); +FORWARD void process_data ARGS(( tcp_conn_t *tcp_conn, + tcp_hdr_t *tcp_hdr, acc_t *tcp_data, int data_len )); +FORWARD void process_advanced_data ARGS(( tcp_conn_t *tcp_conn, + tcp_hdr_t *tcp_hdr, acc_t *tcp_data, int data_len )); + +PUBLIC void tcp_frag2conn(tcp_conn, ip_hdr, tcp_hdr, tcp_data, data_len) +tcp_conn_t *tcp_conn; +ip_hdr_t *ip_hdr; +tcp_hdr_t *tcp_hdr; +acc_t *tcp_data; +size_t data_len; +{ + tcp_fd_t *connuser; + int tcp_hdr_flags; + int ip_hdr_len, tcp_hdr_len; + u32_t seg_ack, seg_seq, rcv_hi; + u16_t seg_wnd; + int acceptable_ACK, segm_acceptable; + + ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; + tcp_hdr_len= (tcp_hdr->th_data_off & TH_DO_MASK) >> 2; + + tcp_hdr_flags= tcp_hdr->th_flags & TH_FLAGS_MASK; + seg_ack= ntohl(tcp_hdr->th_ack_nr); + seg_seq= ntohl(tcp_hdr->th_seq_nr); + seg_wnd= ntohs(tcp_hdr->th_window); + + switch (tcp_conn->tc_state) + { + case TCS_CLOSED: +/* +CLOSED: + discard all data. + !RST ? + ACK ? + <SEQ=SEG.ACK><CTL=RST> + exit + : + <SEQ=0><ACK=SEG.SEQ+SEG.LEN><CTL=RST,ACK> + exit + : + discard packet + exit +*/ + + if (!(tcp_hdr_flags & THF_RST)) + { + create_RST(tcp_conn, ip_hdr, tcp_hdr, data_len); + tcp_conn_write(tcp_conn, 1); + } + break; + case TCS_LISTEN: +/* +LISTEN: + RST ? + discard packet + exit + ACK ? + <SEQ=SEG.ACK><CTL=RST> + exit + SYN ? + BUG: no security check + RCV.NXT= SEG.SEQ+1 + IRS= SEG.SEQ + ISS should already be selected + <SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK> + SND.NXT=ISS+1 + SND.UNA=ISS + state= SYN-RECEIVED + exit + : + shouldnot occur + discard packet + exit +*/ + if (tcp_hdr_flags & THF_RST) + break; + if (tcp_hdr_flags & THF_ACK) + { + create_RST (tcp_conn, ip_hdr, tcp_hdr, data_len); + tcp_conn_write(tcp_conn, 1); + break; + } + if (tcp_hdr_flags & THF_SYN) + { + tcp_extract_ipopt(tcp_conn, ip_hdr); + tcp_extract_tcpopt(tcp_conn, tcp_hdr); + tcp_conn->tc_RCV_LO= seg_seq+1; + tcp_conn->tc_RCV_NXT= seg_seq+1; + tcp_conn->tc_RCV_HI= tcp_conn->tc_RCV_LO+ + tcp_conn->tc_rcv_wnd; + tcp_conn->tc_RCV_UP= seg_seq; + tcp_conn->tc_IRS= seg_seq; + tcp_conn->tc_SND_UNA= tcp_conn->tc_ISS; + tcp_conn->tc_SND_TRM= tcp_conn->tc_ISS; + tcp_conn->tc_SND_NXT= tcp_conn->tc_ISS+1; + tcp_conn->tc_SND_UP= tcp_conn->tc_ISS-1; + tcp_conn->tc_SND_PSH= tcp_conn->tc_ISS-1; + tcp_conn->tc_state= TCS_SYN_RECEIVED; + tcp_conn->tc_stt= 0; + assert (tcp_check_conn(tcp_conn)); + tcp_conn->tc_locaddr= ip_hdr->ih_dst; + tcp_conn->tc_locport= tcp_hdr->th_dstport; + tcp_conn->tc_remaddr= ip_hdr->ih_src; + tcp_conn->tc_remport= tcp_hdr->th_srcport; + tcp_conn_write(tcp_conn, 1); + + DIFBLOCK(0x10, seg_seq == 0, + printf("warning got 0 IRS from "); + writeIpAddr(tcp_conn->tc_remaddr); + printf("\n")); + + /* Start the timer (if necessary) */ + tcp_set_send_timer(tcp_conn); + + break; + } + /* do nothing */ + break; + case TCS_SYN_SENT: +/* +SYN-SENT: + ACK ? + SEG.ACK <= ISS || SEG.ACK > SND.NXT ? + RST ? + discard packet + exit + : + <SEQ=SEG.ACK><CTL=RST> + exit + SND.UNA <= SEG.ACK && SEG.ACK <= SND.NXT ? + ACK is acceptable + : + ACK is !acceptable + : + ACK is !acceptable + RST ? + ACK acceptable ? + discard segment + state= CLOSED + error "connection refused" + exit + : + discard packet + exit + BUG: no security check + SYN ? + IRS= SEG.SEQ + RCV.NXT= IRS+1 + ACK ? + SND.UNA= SEG.ACK + SND.UNA > ISS ? + state= ESTABLISHED + <SEQ=SND.NXT><ACK= RCV.NXT><CTL=ACK> + process ev. URG and text + exit + : + state= SYN-RECEIVED + SND.WND= SEG.WND + SND.WL1= SEG.SEQ + SND.WL2= SEG.ACK + <SEQ=ISS><ACK=RCV.NXT><CTL=SYN,ACK> + exit + : + discard segment + exit +*/ + if (tcp_hdr_flags & THF_ACK) + { + if (tcp_LEmod4G(seg_ack, tcp_conn->tc_ISS) || + tcp_Gmod4G(seg_ack, tcp_conn->tc_SND_NXT)) + if (tcp_hdr_flags & THF_RST) + break; + else + { + create_RST (tcp_conn, ip_hdr, + tcp_hdr, data_len); + tcp_conn_write(tcp_conn, 1); + break; + } + acceptable_ACK= (tcp_LEmod4G(tcp_conn->tc_SND_UNA, + seg_ack) && tcp_LEmod4G(seg_ack, + tcp_conn->tc_SND_NXT)); + } + else + acceptable_ACK= FALSE; + if (tcp_hdr_flags & THF_RST) + { + if (acceptable_ACK) + { + DBLOCK(1, printf( + "calling tcp_close_connection\n")); + + tcp_close_connection(tcp_conn, + ECONNREFUSED); + } + break; + } + if (tcp_hdr_flags & THF_SYN) + { + tcp_conn->tc_RCV_LO= seg_seq+1; + tcp_conn->tc_RCV_NXT= seg_seq+1; + tcp_conn->tc_RCV_HI= tcp_conn->tc_RCV_LO + + tcp_conn->tc_rcv_wnd; + tcp_conn->tc_RCV_UP= seg_seq; + tcp_conn->tc_IRS= seg_seq; + if (tcp_hdr_flags & THF_ACK) + tcp_conn->tc_SND_UNA= seg_ack; + if (tcp_Gmod4G(tcp_conn->tc_SND_UNA, + tcp_conn->tc_ISS)) + { + tcp_conn->tc_state= TCS_ESTABLISHED; + tcp_conn->tc_rt_dead= TCP_DEF_RT_DEAD; + + assert (tcp_check_conn(tcp_conn)); + assert(tcp_conn->tc_connInprogress); + + tcp_restart_connect(tcp_conn->tc_fd); + + tcp_conn->tc_flags |= TCF_SEND_ACK; + tcp_conn_write(tcp_conn, 1); + if (data_len != 0) + { + tcp_frag2conn(tcp_conn, ip_hdr, + tcp_hdr, tcp_data, data_len); + /* tcp_data is already freed */ + return; + } + break; + } + tcp_conn->tc_state= TCS_SYN_RECEIVED; + + assert (tcp_check_conn(tcp_conn)); + + tcp_conn->tc_SND_TRM= tcp_conn->tc_ISS; + tcp_conn_write(tcp_conn, 1); + } + break; + + case TCS_SYN_RECEIVED: +/* +SYN-RECEIVED: + test if segment is acceptable: + Segment Receive Test + Length Window + 0 0 SEG.SEQ == RCV.NXT + 0 >0 RCV.NXT <= SEG.SEQ && SEG.SEQ < RCV.NXT+RCV.WND + >0 0 not acceptable + >0 >0 (RCV.NXT <= SEG.SEQ && SEG.SEQ < RCV.NXT+RCV.WND) + || (RCV.NXT <= SEG.SEQ+SEG.LEN-1 && + SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND) + for urgent data: use RCV.WND+1 for RCV.WND +*/ + rcv_hi= tcp_conn->tc_RCV_HI; + if (tcp_hdr_flags & THF_URG) + rcv_hi++; + if (!data_len) + { + if (rcv_hi == tcp_conn->tc_RCV_NXT) + segm_acceptable= (seg_seq == rcv_hi); + else + { + assert (tcp_Gmod4G(rcv_hi, + tcp_conn->tc_RCV_NXT)); + segm_acceptable= (tcp_LEmod4G(tcp_conn-> + tc_RCV_NXT, seg_seq) && + tcp_Lmod4G(seg_seq, rcv_hi)); + } + } + else + { + if (tcp_Gmod4G(rcv_hi, tcp_conn->tc_RCV_NXT)) + { + segm_acceptable= (tcp_LEmod4G(tcp_conn-> + tc_RCV_NXT, seg_seq) && + tcp_Lmod4G(seg_seq, rcv_hi)) || + (tcp_LEmod4G(tcp_conn->tc_RCV_NXT, + seg_seq+data_len-1) && + tcp_Lmod4G(seg_seq+data_len-1, + rcv_hi)); + } + else + { + segm_acceptable= FALSE; + } + } +/* + !segment acceptable ? + RST ? + discard packet + exit + : + <SEG=SND.NXT><ACK=RCV.NXT><CTL=ACK> + exit +*/ + if (!segm_acceptable) + { + if (!(tcp_hdr_flags & THF_RST)) + { + tcp_conn->tc_flags |= TCF_SEND_ACK; + tcp_conn_write(tcp_conn, 1); + } + break; + } +/* + RST ? + initiated by a LISTEN ? + state= LISTEN + exit + : + state= CLOSED + error "connection refused" + exit +*/ + if (tcp_hdr_flags & THF_RST) + { + if (tcp_conn->tc_orglisten) + { + connuser= tcp_conn->tc_fd; + + tcp_conn->tc_connInprogress= 0; + tcp_conn->tc_fd= NULL; + + tcp_close_connection (tcp_conn, ECONNREFUSED); + if (connuser) + (void)tcp_su4listen(connuser); + break; + } + else + { + tcp_close_connection(tcp_conn, ECONNREFUSED); + break; + } + } +/* + SYN in window ? + initiated by a LISTEN ? + state= LISTEN + exit + : + state= CLOSED + error "connection reset" + exit +*/ + if ((tcp_hdr_flags & THF_SYN) && tcp_GEmod4G(seg_seq, + tcp_conn->tc_RCV_NXT)) + { + if (tcp_conn->tc_orglisten) + { + connuser= tcp_conn->tc_fd; + + tcp_conn->tc_connInprogress= 0; + tcp_conn->tc_fd= NULL; + + tcp_close_connection(tcp_conn, ECONNRESET); + if (connuser) + (void)tcp_su4listen(connuser); + break; + } + tcp_close_connection(tcp_conn, ECONNRESET); + break; + } +/* + !ACK ? + discard packet + exit +*/ + if (!(tcp_hdr_flags & THF_ACK)) + break; +/* + SND.UNA < SEG.ACK <= SND.NXT ? + state= ESTABLISHED + : + <SEG=SEG.ACK><CTL=RST> + exit +*/ + if (tcp_Lmod4G(tcp_conn->tc_SND_UNA, seg_ack) && + tcp_LEmod4G(seg_ack, tcp_conn->tc_SND_NXT)) + { + tcp_conn->tc_state= TCS_ESTABLISHED; + tcp_conn->tc_rt_dead= TCP_DEF_RT_DEAD; + + tcp_release_retrans(tcp_conn, seg_ack, seg_wnd); + + assert (tcp_check_conn(tcp_conn)); + assert(tcp_conn->tc_connInprogress); + + tcp_restart_connect(tcp_conn->tc_fd); + tcp_frag2conn(tcp_conn, ip_hdr, tcp_hdr, tcp_data, + data_len); + /* tcp_data is already freed */ + return; + } + else + { + create_RST (tcp_conn, ip_hdr, tcp_hdr, data_len); + tcp_conn_write(tcp_conn, 1); + break; + } + break; + + case TCS_ESTABLISHED: + case TCS_CLOSING: +/* +ESTABLISHED: +FIN-WAIT-1: +FIN-WAIT-2: +CLOSE-WAIT: +CLOSING: +LAST-ACK: +TIME-WAIT: + test if segment is acceptable: + Segment Receive Test + Length Window + 0 0 SEG.SEQ == RCV.NXT + 0 >0 RCV.NXT <= SEG.SEQ && SEG.SEQ < RCV.NXT+RCV.WND + >0 0 not acceptable + >0 >0 (RCV.NXT <= SEG.SEQ && SEG.SEQ < RCV.NXT+RCV.WND) + || (RCV.NXT <= SEG.SEQ+SEG.LEN-1 && + SEG.SEQ+SEG.LEN-1 < RCV.NXT+RCV.WND) + for urgent data: use RCV.WND+1 for RCV.WND +*/ + rcv_hi= tcp_conn->tc_RCV_HI; + if (tcp_hdr_flags & THF_URG) + rcv_hi++; + if (!data_len) + { + if (rcv_hi == tcp_conn->tc_RCV_NXT) + segm_acceptable= (seg_seq == rcv_hi); + else + { + assert (tcp_Gmod4G(rcv_hi, + tcp_conn->tc_RCV_NXT)); + segm_acceptable= (tcp_LEmod4G(tcp_conn-> + tc_RCV_NXT, seg_seq) && + tcp_Lmod4G(seg_seq, rcv_hi)); + } + } + else + { + if (tcp_Gmod4G(rcv_hi, tcp_conn->tc_RCV_NXT)) + { + segm_acceptable= (tcp_LEmod4G(tcp_conn-> + tc_RCV_NXT, seg_seq) && + tcp_Lmod4G(seg_seq, rcv_hi)) || + (tcp_LEmod4G(tcp_conn->tc_RCV_NXT, + seg_seq+data_len-1) && + tcp_Lmod4G(seg_seq+data_len-1, + rcv_hi)); + } + else + { + segm_acceptable= FALSE; + } + } +/* + !segment acceptable ? + RST ? + discard packet + exit + : + <SEG=SND.NXT><ACK=RCV.NXT><CTL=ACK> + exit +*/ + if (!segm_acceptable) + { + if (!(tcp_hdr_flags & THF_RST)) + { + DBLOCK(0x20, + printf("segment is not acceptable\n"); + printf("\t"); + tcp_print_pack(ip_hdr, tcp_hdr); + printf("\n\t"); + tcp_print_conn(tcp_conn); + printf("\n")); + tcp_conn->tc_flags |= TCF_SEND_ACK; + tcp_conn_write(tcp_conn, 1); + + /* Sometimes, a retransmission sets the PSH + * flag (Solaris 2.4) + */ + if (tcp_conn->tc_rcvd_data != NULL && + (tcp_hdr_flags & THF_PSH)) + { + tcp_conn->tc_flags |= TCF_RCV_PUSH; + if (tcp_conn->tc_fd && + (tcp_conn->tc_fd->tf_flags & + TFF_READ_IP)) + { + tcp_fd_read(tcp_conn, 1); + } + } + } + break; + } +/* + RST ? + state == CLOSING || state == LAST-ACK || + state == TIME-WAIT ? + state= CLOSED + exit + : + state= CLOSED + error "connection reset" + exit +*/ + if (tcp_hdr_flags & THF_RST) + { + if ((tcp_conn->tc_flags & + (TCF_FIN_SENT|TCF_FIN_RECV)) == + (TCF_FIN_SENT|TCF_FIN_RECV) && + tcp_conn->tc_send_data == NULL) + { + /* Clean shutdown, but the other side + * doesn't want to ACK our FIN. + */ + tcp_close_connection (tcp_conn, 0); + } + else + tcp_close_connection(tcp_conn, ECONNRESET); + break; + } +/* + SYN in window ? + state= CLOSED + error "connection reset" + exit +*/ + if ((tcp_hdr_flags & THF_SYN) && tcp_GEmod4G(seg_seq, + tcp_conn->tc_RCV_NXT)) + { + tcp_close_connection(tcp_conn, ECONNRESET); + break; + } +/* + !ACK ? + discard packet + exit +*/ + if (!(tcp_hdr_flags & THF_ACK)) + break; + +/* + SND.UNA < SEG.ACK <= SND.NXT ? + SND.UNA= SEG.ACK + reply "send ok" + SND.WL1 < SEG.SEQ || (SND.WL1 == SEG.SEQ && + SND.WL2 <= SEG.ACK ? + SND.WND= SEG.WND + SND.Wl1= SEG.SEQ + SND.WL2= SEG.ACK + SEG.ACK <= SND.UNA ? + ignore ACK + SEG.ACK > SND.NXT ? + <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK> + discard packet + exit +*/ + + /* Always reset the send timer after a valid ack is + * received. The assumption is that either the ack really + * acknowledges some data (normal case), contains a zero + * window, or the remote host has another reason not + * to accept any data. In all cases, the remote host is + * alive, so the connection should stay alive too. + * Do not reset stt if the state is CLOSING, i.e. if + * the user closed the connection and we still have + * some data to deliver. We don't want a zero window + * to keep us from closing the connection. + */ + if (tcp_conn->tc_state != TCS_CLOSING) + tcp_conn->tc_stt= 0; + + if (seg_ack == tcp_conn->tc_SND_UNA) + { + /* This ACK doesn't acknowledge any new data, this + * is a likely situation if we are only receiving + * data. We only update the window if we are + * actually sending or if we currently have a + * zero window. + */ + if (tcp_conn->tc_snd_cwnd == tcp_conn->tc_SND_UNA && + seg_wnd != 0) + { + DBLOCK(2, printf("zero window opened\n")); + /* The other side opened up its receive + * window. */ + if (seg_wnd > 2*tcp_conn->tc_mss) + seg_wnd= 2*tcp_conn->tc_mss; + tcp_conn->tc_snd_cwnd= + tcp_conn->tc_SND_UNA+seg_wnd; + tcp_conn_write(tcp_conn, 1); + } + if (seg_wnd == 0) + { + tcp_conn->tc_snd_cwnd= tcp_conn->tc_SND_TRM= + tcp_conn->tc_SND_UNA; + } + } + else if (tcp_Lmod4G(tcp_conn->tc_SND_UNA, seg_ack) + && tcp_LEmod4G(seg_ack, tcp_conn-> + tc_SND_NXT)) + { + tcp_release_retrans(tcp_conn, seg_ack, seg_wnd); + if (tcp_conn->tc_state == TCS_CLOSED) + break; + } + else if (tcp_Gmod4G(seg_ack, + tcp_conn->tc_SND_NXT)) + { + tcp_conn->tc_flags |= TCF_SEND_ACK; + tcp_conn_write(tcp_conn, 1); + DBLOCK(1, printf( + "got an ack of something I haven't send\n"); + printf( "seg_ack= %lu, SND_NXT= %lu\n", + seg_ack, tcp_conn->tc_SND_NXT)); + break; + } + +/* + process data... +*/ + tcp_extract_ipopt(tcp_conn, ip_hdr); + tcp_extract_tcpopt(tcp_conn, tcp_hdr); + + if (data_len) + { + if (tcp_LEmod4G(seg_seq, tcp_conn->tc_RCV_NXT)) + { + process_data (tcp_conn, tcp_hdr, + tcp_data, data_len); + } + else + { + process_advanced_data (tcp_conn, + tcp_hdr, tcp_data, data_len); + } + tcp_conn->tc_flags |= TCF_SEND_ACK; + tcp_conn_write(tcp_conn, 1); + + /* Don't process a FIN if we got new data */ + break; + } +/* + FIN ? + reply pending receives + advace RCV.NXT over the FIN + <SEQ=SND.NXT><ACK=RCV.NXT><CTL=ACK> + + state == ESTABLISHED ? + state= CLOSE-WAIT + state == FIN-WAIT-1 ? + state= CLOSING + state == FIN-WAIT-2 ? + state= TIME-WAIT + state == TIME-WAIT ? + restart the TIME-WAIT timer + exit +*/ + if ((tcp_hdr_flags & THF_FIN) && tcp_LEmod4G(seg_seq, + tcp_conn->tc_RCV_NXT)) + { + if (!(tcp_conn->tc_flags & TCF_FIN_RECV) && + tcp_Lmod4G(tcp_conn->tc_RCV_NXT, + tcp_conn->tc_RCV_HI)) + { + tcp_conn->tc_RCV_NXT++; + tcp_conn->tc_flags |= TCF_FIN_RECV; + } + tcp_conn->tc_flags |= TCF_SEND_ACK; + tcp_conn_write(tcp_conn, 1); + if (tcp_conn->tc_fd && + (tcp_conn->tc_fd->tf_flags & TFF_READ_IP)) + { + tcp_fd_read(tcp_conn, 1); + } + } + break; + default: +#if !CRAMPED + printf("tcp_frag2conn: unknown state "); + tcp_print_state(tcp_conn); +#endif + break; + } + if (tcp_data != NULL) + bf_afree(tcp_data); +} + + +PRIVATE void +process_data(tcp_conn, tcp_hdr, tcp_data, data_len) +tcp_conn_t *tcp_conn; +tcp_hdr_t *tcp_hdr; +acc_t *tcp_data; +int data_len; +{ + u32_t lo_seq, hi_seq, urg_seq, seq_nr, adv_seq, nxt; + u16_t urgptr; + int tcp_hdr_flags; + unsigned int offset; + acc_t *tmp_data, *rcvd_data, *adv_data; + int len_diff; + + assert(tcp_conn->tc_busy); + + /* Note, tcp_data will be freed by the caller. */ + assert (!(tcp_hdr->th_flags & THF_SYN)); + + seq_nr= ntohl(tcp_hdr->th_seq_nr); + urgptr= ntohs(tcp_hdr->th_urgptr); + + tcp_data->acc_linkC++; + + lo_seq= seq_nr; + tcp_hdr_flags= tcp_hdr->th_flags & TH_FLAGS_MASK; + + if (tcp_hdr_flags & THF_URG) + { + if (urgptr > data_len) + urgptr= data_len; + urg_seq= lo_seq+ urgptr; + + if (tcp_GEmod4G(urg_seq, tcp_conn->tc_RCV_HI)) + urg_seq= tcp_conn->tc_RCV_HI; + if (tcp_conn->tc_flags & TCF_BSD_URG) + { + if (tcp_Gmod4G(tcp_conn->tc_RCV_NXT, + tcp_conn->tc_RCV_LO)) + { + DBLOCK(1, printf( + "ignoring urgent data\n")); + + bf_afree(tcp_data); + /* Should set advertised window to + * zero */ + + /* Flush */ + tcp_conn->tc_flags |= TCF_RCV_PUSH; + if (tcp_conn->tc_fd && + (tcp_conn->tc_fd->tf_flags & + TFF_READ_IP)) + { + tcp_fd_read(tcp_conn, 1); + } + return; + } + } + if (tcp_Gmod4G(urg_seq, tcp_conn->tc_RCV_UP)) + tcp_conn->tc_RCV_UP= urg_seq; + if (urgptr < data_len) + { + data_len= urgptr; + tmp_data= bf_cut(tcp_data, 0, data_len); + bf_afree(tcp_data); + tcp_data= tmp_data; + tcp_hdr_flags &= ~THF_FIN; + } + tcp_conn->tc_flags |= TCF_RCV_PUSH; + } + else + { + /* Normal data. */ + } + + if (tcp_hdr_flags & THF_PSH) + { + tcp_conn->tc_flags |= TCF_RCV_PUSH; + } + + if (tcp_Lmod4G(lo_seq, tcp_conn->tc_RCV_NXT)) + { + DBLOCK(0x10, + printf("segment is a retransmission\n")); + offset= tcp_conn->tc_RCV_NXT-lo_seq; + tcp_data= bf_delhead(tcp_data, offset); + lo_seq += offset; + data_len -= offset; + } + assert (lo_seq == tcp_conn->tc_RCV_NXT); + + hi_seq= lo_seq+data_len; + if (tcp_Gmod4G(hi_seq, tcp_conn->tc_RCV_HI)) + { + data_len= tcp_conn->tc_RCV_HI-lo_seq; + tmp_data= bf_cut(tcp_data, 0, data_len); + bf_afree(tcp_data); + tcp_data= tmp_data; + hi_seq= lo_seq+data_len; + tcp_hdr_flags &= ~THF_FIN; + } + assert (tcp_LEmod4G (hi_seq, tcp_conn->tc_RCV_HI)); + + rcvd_data= tcp_conn->tc_rcvd_data; + tcp_conn->tc_rcvd_data= 0; + tmp_data= bf_append(rcvd_data, tcp_data); + tcp_conn->tc_rcvd_data= tmp_data; + tcp_conn->tc_RCV_NXT= hi_seq; + + if ((tcp_hdr_flags & THF_FIN) && + tcp_Lmod4G(tcp_conn->tc_RCV_NXT, tcp_conn->tc_RCV_HI) && + !(tcp_conn->tc_flags & TCF_FIN_RECV)) + { + tcp_conn->tc_RCV_NXT++; + tcp_conn->tc_flags |= TCF_FIN_RECV; + } + + if (tcp_conn->tc_fd && (tcp_conn->tc_fd->tf_flags & TFF_READ_IP)) + tcp_fd_read(tcp_conn, 1); + + DIFBLOCK(2, (tcp_conn->tc_RCV_NXT == tcp_conn->tc_RCV_HI), + printf("conn[[%d] full receive buffer\n", + tcp_conn-tcp_conn_table)); + + if (tcp_conn->tc_adv_data == NULL) + return; + if (tcp_hdr_flags & THF_FIN) + { +#if !CRAMPED + printf("conn[%d]: advanced data after FIN\n", + tcp_conn-tcp_conn_table); +#endif + tcp_data= tcp_conn->tc_adv_data; + tcp_conn->tc_adv_data= NULL; + bf_afree(tcp_data); + return; + } + + lo_seq= tcp_conn->tc_adv_seq; + if (tcp_Gmod4G(lo_seq, tcp_conn->tc_RCV_NXT)) + return; /* Not yet */ + + tcp_data= tcp_conn->tc_adv_data; + tcp_conn->tc_adv_data= NULL; + + data_len= bf_bufsize(tcp_data); + if (tcp_Lmod4G(lo_seq, tcp_conn->tc_RCV_NXT)) + { + offset= tcp_conn->tc_RCV_NXT-lo_seq; + if (offset >= data_len) + { + bf_afree(tcp_data); + return; + } + tcp_data= bf_delhead(tcp_data, offset); + lo_seq += offset; + data_len -= offset; + } + assert (lo_seq == tcp_conn->tc_RCV_NXT); + + hi_seq= lo_seq+data_len; + assert (tcp_LEmod4G (hi_seq, tcp_conn->tc_RCV_HI)); + + rcvd_data= tcp_conn->tc_rcvd_data; + tcp_conn->tc_rcvd_data= 0; + tmp_data= bf_append(rcvd_data, tcp_data); + tcp_conn->tc_rcvd_data= tmp_data; + tcp_conn->tc_RCV_NXT= hi_seq; + + assert (tcp_conn->tc_RCV_LO + bf_bufsize(tcp_conn->tc_rcvd_data) == + tcp_conn->tc_RCV_NXT || + (tcp_print_conn(tcp_conn), printf("\n"), 0)); + + if (tcp_conn->tc_fd && (tcp_conn->tc_fd->tf_flags & TFF_READ_IP)) + tcp_fd_read(tcp_conn, 1); + + adv_data= tcp_conn->tc_adv_data; + if (adv_data != NULL) + { + /* Try to use advanced data. */ + adv_seq= tcp_conn->tc_adv_seq; + nxt= tcp_conn->tc_RCV_NXT; + + if (tcp_Gmod4G(adv_seq, nxt)) + return; /* not yet */ + + tcp_conn->tc_adv_data= NULL; + data_len= bf_bufsize(adv_data); + + if (tcp_Lmod4G(adv_seq, nxt)) + { + if (tcp_LEmod4G(adv_seq+data_len, nxt)) + { + /* Data is not needed anymore. */ + bf_afree(adv_data); + return; + } + + len_diff= nxt-adv_seq; + adv_data= bf_delhead(adv_data, len_diff); + data_len -= len_diff; + } + + DBLOCK(1, printf("using advanced data\n")); + + /* Append data to the input buffer */ + if (tcp_conn->tc_rcvd_data == NULL) + { + tcp_conn->tc_rcvd_data= adv_data; + } + else + { + tcp_conn->tc_rcvd_data= + bf_append(tcp_conn->tc_rcvd_data, adv_data); + } + tcp_conn->tc_SND_NXT += data_len; + assert(tcp_check_conn(tcp_conn)); + + if (tcp_conn->tc_fd && + (tcp_conn->tc_fd->tf_flags & TFF_READ_IP)) + { + tcp_fd_read(tcp_conn, 1); + } + } +} + +PRIVATE void process_advanced_data(tcp_conn, tcp_hdr, tcp_data, data_len) +tcp_conn_t *tcp_conn; +tcp_hdr_t *tcp_hdr; +acc_t *tcp_data; +int data_len; +{ + u32_t seq, adv_seq; + acc_t *adv_data; + + assert(tcp_conn->tc_busy); + + /* Note, tcp_data will be freed by the caller. */ + + /* Always send an ACK, this allows the sender to do a fast + * retransmit. + */ + tcp_conn->tc_flags |= TCF_SEND_ACK; + tcp_conn_write(tcp_conn, 1); + + if (tcp_hdr->th_flags & THF_URG) + return; /* Urgent data is to complicated */ + if (tcp_hdr->th_flags & THF_PSH) + tcp_conn->tc_flags |= TCF_RCV_PUSH; + seq= ntohl(tcp_hdr->th_seq_nr); + + /* Make sure that the packet doesn't fall outside of the window + * we offered. + */ + if (tcp_Gmod4G(seq+data_len, tcp_conn->tc_RCV_HI)) + return; + + adv_data= tcp_conn->tc_adv_data; + adv_seq= tcp_conn->tc_adv_seq; + tcp_conn->tc_adv_data= NULL; + + tcp_data->acc_linkC++; + if (adv_data == NULL) + { + adv_seq= seq; + adv_data= tcp_data; + } + else if (seq + data_len == adv_seq) + { + /* New data fits right before exiting data. */ + adv_data= bf_append(tcp_data, adv_data); + adv_seq= seq; + } + else if (adv_seq + bf_bufsize(adv_data) == seq) + { + /* New data fits right after exiting data. */ + adv_data= bf_append(adv_data, tcp_data); + } + else + { + /* New data doesn't fit. */ + bf_afree(tcp_data); + } + tcp_conn->tc_adv_data= adv_data; + tcp_conn->tc_adv_seq= adv_seq; +} + +PRIVATE void create_RST(tcp_conn, ip_hdr, tcp_hdr, data_len) +tcp_conn_t *tcp_conn; +ip_hdr_t *ip_hdr; +tcp_hdr_t *tcp_hdr; +int data_len; +{ + acc_t *tmp_ipopt, *tmp_tcpopt, *tcp_pack; + ip_hdropt_t ip_hdropt; + tcp_hdropt_t tcp_hdropt; + acc_t *RST_acc; + ip_hdr_t *RST_ip_hdr; + tcp_hdr_t *RST_tcp_hdr; + char *ptr2RSThdr; + size_t pack_size, ip_hdr_len; + + DBLOCK(0x10, printf("in create_RST, bad pack is:\n"); + tcp_print_pack(ip_hdr, tcp_hdr); tcp_print_state(tcp_conn); + printf("\n")); + + assert(tcp_conn->tc_busy); + + /* Only send RST packets in reponse to actual data (or SYN, FIN) + * this solves a problem during connection shutdown. The problem + * is the follow senario: a senders closes the connection instead + * of doing a shutdown and waiting for the receiver to shutdown. + * The receiver is slow in processing the last data. After the + * sender has completely closed the connection, the receiver + * sends a window update which triggers the sender to send a + * RST. The receiver closes the connection in reponse to the RST. + */ + if ((tcp_hdr->th_flags & (THF_FIN|THF_SYN)) == 0 && + data_len == 0) + { +#if DEBUG + { printf("tcp_recv`create_RST: no data, no RST\n"); } +#endif + return; + } + + tmp_ipopt= tcp_conn->tc_remipopt; + if (tmp_ipopt) + tmp_ipopt->acc_linkC++; + tmp_tcpopt= tcp_conn->tc_tcpopt; + if (tmp_tcpopt) + tmp_tcpopt->acc_linkC++; + + tcp_extract_ipopt (tcp_conn, ip_hdr); + tcp_extract_tcpopt (tcp_conn, tcp_hdr); + + RST_acc= tcp_make_header (tcp_conn, &RST_ip_hdr, &RST_tcp_hdr, + (acc_t *)0); + + if (tcp_conn->tc_remipopt) + bf_afree(tcp_conn->tc_remipopt); + tcp_conn->tc_remipopt= tmp_ipopt; + if (tcp_conn->tc_tcpopt) + bf_afree(tcp_conn->tc_tcpopt); + tcp_conn->tc_tcpopt= tmp_tcpopt; + + RST_ip_hdr->ih_src= ip_hdr->ih_dst; + RST_ip_hdr->ih_dst= ip_hdr->ih_src; + + RST_tcp_hdr->th_srcport= tcp_hdr->th_dstport; + RST_tcp_hdr->th_dstport= tcp_hdr->th_srcport; + if (tcp_hdr->th_flags & THF_ACK) + { + RST_tcp_hdr->th_seq_nr= tcp_hdr->th_ack_nr; + RST_tcp_hdr->th_flags= THF_RST; + } + else + { + RST_tcp_hdr->th_seq_nr= 0; + RST_tcp_hdr->th_ack_nr= + htonl( + ntohl(tcp_hdr->th_seq_nr)+ + data_len + + (tcp_hdr->th_flags & THF_SYN ? 1 : 0) + + (tcp_hdr->th_flags & THF_FIN ? 1 : 0)); + RST_tcp_hdr->th_flags= THF_RST|THF_ACK; + } + + pack_size= bf_bufsize(RST_acc); + RST_ip_hdr->ih_length= htons(pack_size); + RST_tcp_hdr->th_window= htons(tcp_conn->tc_rcv_wnd); + RST_tcp_hdr->th_chksum= 0; + + RST_acc->acc_linkC++; + ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; + tcp_pack= bf_delhead(RST_acc, ip_hdr_len); + RST_tcp_hdr->th_chksum= ~tcp_pack_oneCsum (RST_ip_hdr, tcp_pack); + bf_afree(tcp_pack); + + DBLOCK(2, tcp_print_pack(ip_hdr, tcp_hdr); printf("\n"); + tcp_print_pack(RST_ip_hdr, RST_tcp_hdr); printf("\n")); + + if (tcp_conn->tc_frag2send) + bf_afree(tcp_conn->tc_frag2send); + tcp_conn->tc_frag2send= RST_acc; + tcp_conn_write(tcp_conn, 1); +} + +PUBLIC void +tcp_fd_read(tcp_conn, enq) +tcp_conn_t *tcp_conn; +int enq; /* Enqueue writes. */ +{ + tcp_fd_t *tcp_fd; + size_t data_size, read_size; + acc_t *data; + int fin_recv, urg, push, result; + i32_t old_window, new_window; + + assert(tcp_conn->tc_busy); + + tcp_fd= tcp_conn->tc_fd; + + assert (tcp_fd->tf_flags & TFF_READ_IP); + if (tcp_conn->tc_state == TCS_CLOSED) + { + if (tcp_fd->tf_read_offset) + tcp_reply_read (tcp_fd, tcp_fd->tf_read_offset); + else + tcp_reply_read (tcp_fd, tcp_conn->tc_error); + return; + } + + urg= tcp_Gmod4G(tcp_conn->tc_RCV_UP, tcp_conn->tc_RCV_LO); + push= (tcp_conn->tc_flags & TCF_RCV_PUSH); + fin_recv= (tcp_conn->tc_flags & TCF_FIN_RECV); + + data_size= tcp_conn->tc_RCV_NXT-tcp_conn->tc_RCV_LO; + if (fin_recv) + data_size--; + if (urg) + read_size= tcp_conn->tc_RCV_UP-tcp_conn->tc_RCV_LO; + else + read_size= data_size; + + if (read_size >= tcp_fd->tf_read_count) + read_size= tcp_fd->tf_read_count; + else if (!push && !fin_recv && !urg && + data_size < TCP_MIN_RCV_WND_SIZE) + { + /* Defer the copy out until later. */ + return; + } + else if (data_size == 0 && !fin_recv) + { + /* No data, and no end of file. */ + return; + } + + if (read_size) + { + if (urg && !(tcp_fd->tf_flags & TFF_RECV_URG)) + { + if (tcp_fd->tf_read_offset) + { + tcp_reply_read (tcp_fd, + tcp_fd->tf_read_offset); + } + else + { + tcp_reply_read (tcp_fd, EURG); + } + return; + } + else if (!urg && (tcp_fd->tf_flags & TFF_RECV_URG)) + { + if (tcp_fd->tf_read_offset) + { + tcp_reply_read (tcp_fd, + tcp_fd->tf_read_offset); + } + else + { + tcp_reply_read(tcp_fd, ENOURG); + } + return; + } + + if (read_size == data_size) + { + data= tcp_conn->tc_rcvd_data; + data->acc_linkC++; + } + else + { + data= bf_cut(tcp_conn->tc_rcvd_data, 0, read_size); + } + result= (*tcp_fd->tf_put_userdata) (tcp_fd->tf_srfd, + tcp_fd->tf_read_offset, data, FALSE); + if (result<0) + { + if (tcp_fd->tf_read_offset) + tcp_reply_read(tcp_fd, tcp_fd-> + tf_read_offset); + else + tcp_reply_read(tcp_fd, result); + return; + } + tcp_fd->tf_read_offset += read_size; + tcp_fd->tf_read_count -= read_size; + + if (data_size == read_size) + { + bf_afree(tcp_conn->tc_rcvd_data); + tcp_conn->tc_rcvd_data= 0; + } + else + { + tcp_conn->tc_rcvd_data= + bf_delhead(tcp_conn->tc_rcvd_data, + read_size); + } + tcp_conn->tc_RCV_LO += read_size; + data_size -= read_size; + } + if (tcp_conn->tc_RCV_HI-tcp_conn->tc_RCV_LO <= (tcp_conn-> + tc_rcv_wnd-tcp_conn->tc_mss)) + { + old_window= tcp_conn->tc_RCV_HI-tcp_conn->tc_RCV_NXT; + tcp_conn->tc_RCV_HI= tcp_conn->tc_RCV_LO + + tcp_conn->tc_rcv_wnd; + new_window= tcp_conn->tc_RCV_HI-tcp_conn->tc_RCV_NXT; + assert(old_window >=0 && new_window >= old_window); + if (old_window < tcp_conn->tc_mss && + new_window >= tcp_conn->tc_mss) + { + tcp_conn->tc_flags |= TCF_SEND_ACK; + DBLOCK(2, printf("opening window\n")); + tcp_conn_write(tcp_conn, 1); + } + } + if (tcp_conn->tc_rcvd_data == NULL && + tcp_conn->tc_adv_data == NULL) + { + /* Out of data, clear PUSH flag and reply to a read. */ + tcp_conn->tc_flags &= ~TCF_RCV_PUSH; + } + if (fin_recv || urg || !tcp_fd->tf_read_count) + { + tcp_reply_read (tcp_fd, tcp_fd->tf_read_offset); + return; + } + if (tcp_fd->tf_read_offset) + { + tcp_reply_read (tcp_fd, tcp_fd->tf_read_offset); + return; + } +} + +/* + * $PchId: tcp_recv.c,v 1.13.2.1 2000/05/02 18:53:06 philip Exp $ + */ diff --git a/servers/inet/generic/tcp_send.c b/servers/inet/generic/tcp_send.c new file mode 100644 index 000000000..45803381e --- /dev/null +++ b/servers/inet/generic/tcp_send.c @@ -0,0 +1,1124 @@ +/* +tcp_send.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "buf.h" +#include "clock.h" +#include "event.h" +#include "type.h" + +#include "assert.h" +#include "io.h" +#include "ip.h" +#include "tcp.h" +#include "tcp_int.h" + +THIS_FILE + +FORWARD acc_t *make_pack ARGS(( tcp_conn_t *tcp_conn )); +FORWARD void tcp_send_timeout ARGS(( int conn, struct timer *timer )); +FORWARD void do_snd_event ARGS(( event_t *ev, ev_arg_t arg )); + +PUBLIC void tcp_conn_write (tcp_conn, enq) +tcp_conn_t *tcp_conn; +int enq; /* Writes need to be enqueued. */ +{ + tcp_port_t *tcp_port; + ev_arg_t snd_arg; + + assert (tcp_conn->tc_flags & TCF_INUSE); + + tcp_port= tcp_conn->tc_port; + if (tcp_conn->tc_flags & TCF_MORE2WRITE) + return; + + /* XXX - do we really have something to send here? */ + + tcp_conn->tc_flags |= TCF_MORE2WRITE; + tcp_conn->tc_send_link= NULL; + if (!tcp_port->tp_snd_head) + { + tcp_port->tp_snd_head= tcp_conn; + tcp_port->tp_snd_tail= tcp_conn; + if (enq) + { + snd_arg.ev_ptr= tcp_port; + if (!ev_in_queue(&tcp_port->tp_snd_event)) + { + ev_enqueue(&tcp_port->tp_snd_event, + do_snd_event, snd_arg); + } + } + else + tcp_port_write(tcp_port); + } + else + { + tcp_port->tp_snd_tail->tc_send_link= tcp_conn; + tcp_port->tp_snd_tail= tcp_conn; + } +} + +PRIVATE void do_snd_event(ev, arg) +event_t *ev; +ev_arg_t arg; +{ + tcp_port_t *tcp_port; + + tcp_port= arg.ev_ptr; + + assert(ev == &tcp_port->tp_snd_event); + tcp_port_write(tcp_port); +} + +PUBLIC void tcp_port_write(tcp_port) +tcp_port_t *tcp_port; +{ + tcp_conn_t *tcp_conn; + acc_t *pack2write; + int r; + + assert (!(tcp_port->tp_flags & TPF_WRITE_IP)); + + while(tcp_port->tp_snd_head) + { + tcp_conn= tcp_port->tp_snd_head; + assert(tcp_conn->tc_flags & TCF_MORE2WRITE); + + for(;;) + { + if (tcp_conn->tc_frag2send) + { + pack2write= tcp_conn->tc_frag2send; + tcp_conn->tc_frag2send= 0; + } + else + { + tcp_conn->tc_busy++; + pack2write= make_pack(tcp_conn); + tcp_conn->tc_busy--; + if (!pack2write) + break; + } + r= ip_send(tcp_port->tp_ipfd, pack2write, + bf_bufsize(pack2write)); + if (r != NW_OK) + { + if (r == NW_WOULDBLOCK) + break; + if (r == EDSTNOTRCH) + { + tcp_notreach(tcp_conn); + continue; + } + else if (r == EBADDEST) + continue; + } + assert(r == NW_OK || + (printf("ip_send failed, error %d\n", r),0)); + } + + if (pack2write) + { + tcp_port->tp_flags |= TPF_WRITE_IP; + tcp_port->tp_pack= pack2write; + + r= ip_write (tcp_port->tp_ipfd, + bf_bufsize(pack2write)); + if (r == NW_SUSPEND) + { + tcp_port->tp_flags |= TPF_WRITE_SP; + return; + } + assert(r == NW_OK); + tcp_port->tp_flags &= ~TPF_WRITE_IP; + assert(!(tcp_port->tp_flags & + (TPF_WRITE_IP|TPF_WRITE_SP))); + continue; + } + tcp_conn->tc_flags &= ~TCF_MORE2WRITE; + tcp_port->tp_snd_head= tcp_conn->tc_send_link; + + } +} + +PRIVATE acc_t *make_pack(tcp_conn) +tcp_conn_t *tcp_conn; +{ + acc_t *pack2write, *tmp_pack, *tcp_pack; + tcp_hdr_t *tcp_hdr; + ip_hdr_t *ip_hdr; + int tot_hdr_size, ip_hdr_len; + u32_t seg_seq, seg_lo_data, queue_lo_data, seg_hi, seg_hi_data; + u16_t seg_up; + u8_t seg_flags; + time_t new_dis; + size_t pack_size; + time_t curr_time; + u8_t *optptr; + + assert(tcp_conn->tc_busy); + curr_time= get_time(); + switch (tcp_conn->tc_state) + { + case TCS_CLOSED: + return 0; + case TCS_SYN_RECEIVED: + case TCS_SYN_SENT: + + if (tcp_conn->tc_SND_TRM == tcp_conn->tc_SND_NXT && + !(tcp_conn->tc_flags & TCF_SEND_ACK)) + { + return 0; + } + + tcp_conn->tc_flags &= ~TCF_SEND_ACK; + + /* Include a max segment size option. */ + assert(tcp_conn->tc_tcpopt == NULL); + tcp_conn->tc_tcpopt= bf_memreq(4); + optptr= (u8_t *)ptr2acc_data(tcp_conn->tc_tcpopt); + optptr[0]= TCP_OPT_MSS; + optptr[1]= 4; + optptr[2]= tcp_conn->tc_mss >> 8; + optptr[3]= tcp_conn->tc_mss & 0xFF; + + pack2write= tcp_make_header(tcp_conn, &ip_hdr, &tcp_hdr, + (acc_t *)0); + + bf_afree(tcp_conn->tc_tcpopt); + tcp_conn->tc_tcpopt= NULL; + + if (!pack2write) + { + DBLOCK(1, printf("connection closed while inuse\n")); + return 0; + } + tot_hdr_size= bf_bufsize(pack2write); + seg_seq= tcp_conn->tc_SND_TRM; + if (tcp_conn->tc_state == TCS_SYN_SENT) + seg_flags= 0; + else + seg_flags= THF_ACK; /* except for TCS_SYN_SENT + * ack is always present */ + + if (seg_seq == tcp_conn->tc_ISS) + { + assert(tcp_conn->tc_transmit_timer.tim_active || + (tcp_print_conn(tcp_conn), printf("\n"), 0)); + seg_flags |= THF_SYN; + tcp_conn->tc_SND_TRM++; + } + + tcp_hdr->th_seq_nr= htonl(seg_seq); + tcp_hdr->th_ack_nr= htonl(tcp_conn->tc_RCV_NXT); + tcp_hdr->th_flags= seg_flags; + tcp_hdr->th_window= htons(tcp_conn->tc_mss); + /* Initially we allow one segment */ + + ip_hdr->ih_length= htons(tot_hdr_size); + + pack2write->acc_linkC++; + ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; + tcp_pack= bf_delhead(pack2write, ip_hdr_len); + tcp_hdr->th_chksum= ~tcp_pack_oneCsum(ip_hdr, tcp_pack); + bf_afree(tcp_pack); + + new_dis= curr_time + 2*HZ*tcp_conn->tc_ttl; + if (new_dis > tcp_conn->tc_senddis) + tcp_conn->tc_senddis= new_dis; + return pack2write; + + case TCS_ESTABLISHED: + case TCS_CLOSING: + seg_seq= tcp_conn->tc_SND_TRM; + + seg_flags= 0; + pack2write= 0; + seg_up= 0; + if (tcp_conn->tc_flags & TCF_SEND_ACK) + { + seg_flags= THF_ACK; + tcp_conn->tc_flags &= ~TCF_SEND_ACK; + + pack2write= tcp_make_header (tcp_conn, &ip_hdr, + &tcp_hdr, (acc_t *)0); + if (!pack2write) + { + return NULL; + } + } + + if (tcp_conn->tc_SND_UNA != tcp_conn->tc_SND_NXT) + { + assert(tcp_LEmod4G(seg_seq, tcp_conn->tc_SND_NXT)); + + if (seg_seq == tcp_conn->tc_snd_cwnd) + { + DBLOCK(2, + printf("no data: window is closed\n")); + goto after_data; + } + + /* Assert that our SYN has been ACKed. */ + assert(tcp_conn->tc_SND_UNA != tcp_conn->tc_ISS); + + seg_lo_data= seg_seq; + queue_lo_data= tcp_conn->tc_SND_UNA; + + seg_hi= tcp_conn->tc_SND_NXT; + seg_hi_data= seg_hi; + if (tcp_conn->tc_flags & TCF_FIN_SENT) + { + if (seg_seq != seg_hi) + seg_flags |= THF_FIN; + if (queue_lo_data == seg_hi_data) + queue_lo_data--; + if (seg_lo_data == seg_hi_data) + seg_lo_data--; + seg_hi_data--; + } + + if (!pack2write) + { + pack2write= tcp_make_header (tcp_conn, + &ip_hdr, &tcp_hdr, (acc_t *)0); + if (!pack2write) + { + return NULL; + } + } + + tot_hdr_size= bf_bufsize(pack2write); + if (seg_hi_data - seg_lo_data > tcp_conn->tc_mss - + tot_hdr_size) + { + seg_hi_data= seg_lo_data + tcp_conn->tc_mss - + tot_hdr_size; + seg_hi= seg_hi_data; + seg_flags &= ~THF_FIN; + } + + if (tcp_Gmod4G(seg_hi, tcp_conn->tc_snd_cwnd)) + { + seg_hi_data= tcp_conn->tc_snd_cwnd; + seg_hi= seg_hi_data; + seg_flags &= ~THF_FIN; + } + + if (seg_hi-seg_seq == 0) + { + DBLOCK(0x20, + printf("no data: no data available\n")); + goto after_data; + } + + if (seg_seq != tcp_conn->tc_SND_UNA && + seg_hi_data-seg_lo_data+tot_hdr_size < + tcp_conn->tc_mss) + { + DBLOCK(0x20, + printf("no data: partial packet\n")); + seg_flags &= ~THF_FIN; + goto after_data; + } + + if (tcp_GEmod4G(tcp_conn->tc_SND_UP, seg_lo_data)) + { + if (tcp_GEmod4G(tcp_conn->tc_SND_UP, + seg_hi_data)) + { + seg_up= seg_hi_data-seg_seq; + } + else + { + seg_up= tcp_conn->tc_SND_UP-seg_seq; + } + seg_flags |= THF_URG; + if ((tcp_conn->tc_flags & TCF_BSD_URG) && + seg_up == 0) + { + /* A zero urgent pointer doesn't mean + * anything when BSD semantics are + * used (urgent pointer points to the + * first no urgent byte). The use of + * a zero urgent pointer also crashes + * a Solaris 2.3 kernel. If urgent + * pointer doesn't have BSD semantics + * then an urgent pointer of zero + * simply indicates that there is one + * urgent byte. + */ + seg_flags &= ~THF_URG; + } + } + else + seg_up= 0; + + if (tcp_Gmod4G(tcp_conn->tc_SND_PSH, seg_lo_data) && + tcp_LEmod4G(tcp_conn->tc_SND_PSH, seg_hi_data)) + { + seg_flags |= THF_PSH; + } + + tcp_conn->tc_SND_TRM= seg_hi; + + assert(tcp_conn->tc_transmit_timer.tim_active || + (tcp_print_conn(tcp_conn), printf("\n"), 0)); + if (tcp_conn->tc_rt_seq == 0 && + tcp_Gmod4G(seg_seq, tcp_conn->tc_rt_threshold)) + { + tcp_conn->tc_rt_time= curr_time; + tcp_conn->tc_rt_seq= + tcp_conn->tc_rt_threshold= seg_seq; + } + + if (seg_hi_data-seg_lo_data) + { +#if DEBUG & 0 + assert(tcp_check_conn(tcp_conn)); + assert((seg_hi_data-queue_lo_data <= + bf_bufsize(tcp_conn->tc_send_data) && + seg_lo_data-queue_lo_data <= + bf_bufsize(tcp_conn->tc_send_data) && + seg_hi_data>seg_lo_data)|| + (tcp_print_conn(tcp_conn), + printf( + " seg_hi_data= 0x%x, seg_lo_data= 0x%x, queue_lo_data= 0x%x\n", + seg_hi_data, seg_lo_data, + queue_lo_data), 0)); +#endif + + tmp_pack= pack2write; + while (tmp_pack->acc_next) + tmp_pack= tmp_pack->acc_next; + tmp_pack->acc_next= + bf_cut(tcp_conn->tc_send_data, + (unsigned)(seg_lo_data-queue_lo_data), + (unsigned) (seg_hi_data-seg_lo_data)); + } + seg_flags |= THF_ACK; + } + +after_data: + if (!(seg_flags & THF_ACK)) + { + if (pack2write) + bf_afree(pack2write); + return NULL; + } + + tcp_hdr->th_seq_nr= htonl(seg_seq); + tcp_hdr->th_ack_nr= htonl(tcp_conn->tc_RCV_NXT); + tcp_hdr->th_flags= seg_flags; + tcp_hdr->th_window= htons(tcp_conn->tc_RCV_HI - + tcp_conn->tc_RCV_NXT); + tcp_hdr->th_urgptr= htons(seg_up); + + pack_size= bf_bufsize(pack2write); + ip_hdr->ih_length= htons(pack_size); + + pack2write->acc_linkC++; + ip_hdr_len= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; + tcp_pack= bf_delhead(pack2write, ip_hdr_len); + tcp_hdr->th_chksum= ~tcp_pack_oneCsum(ip_hdr, tcp_pack); + bf_afree(tcp_pack); + + new_dis= curr_time + 2*HZ*tcp_conn->tc_ttl; + if (new_dis > tcp_conn->tc_senddis) + tcp_conn->tc_senddis= new_dis; + + return pack2write; +#if !CRAMPED + default: + DBLOCK(1, tcp_print_conn(tcp_conn); printf("\n")); + ip_panic(( "Illegal state" )); +#endif + } + assert(0); + return NULL; +} + +/* +tcp_release_retrans +*/ + +PUBLIC void tcp_release_retrans(tcp_conn, seg_ack, new_win) +tcp_conn_t *tcp_conn; +u32_t seg_ack; +u16_t new_win; +{ + size_t size, offset; + acc_t *pack; + time_t retrans_time, curr_time, rtt; + u32_t queue_lo, queue_hi; + u16_t mss, cthresh; + unsigned window; + + assert(tcp_conn->tc_busy); + assert (tcp_GEmod4G(seg_ack, tcp_conn->tc_SND_UNA)); + assert (tcp_LEmod4G(seg_ack, tcp_conn->tc_SND_NXT)); + + curr_time= get_time(); + if (tcp_conn->tc_rt_seq != 0 && + tcp_Gmod4G(seg_ack, tcp_conn->tc_rt_seq)) + { + assert(curr_time >= tcp_conn->tc_rt_time); + retrans_time= curr_time-tcp_conn->tc_rt_time; + rtt= tcp_conn->tc_rtt; + + DBLOCK(0x20, printf( + "tcp_release_retrans, conn[%d]: retrans_time= %ld ms\n", + tcp_conn-tcp_conn_table, retrans_time*1000/HZ)); + + + tcp_conn->tc_rt_seq= 0; + + if (rtt == TCP_RTT_GRAN*CLOCK_GRAN && + retrans_time <= TCP_RTT_GRAN*CLOCK_GRAN) + { + /* Common in fast networks. Nothing to do. */ + } + else if (rtt >= retrans_time && rtt <= 2*retrans_time) + { + /* Nothing to do. We assume that a factor 2 for + * variance is enough. + */ + } + else if (retrans_time > rtt) + { + /* Retrans time is really too small. */ + + tcp_conn->tc_rtt= rtt*2; + if (tcp_conn->tc_rtt > TCP_RTT_MAX) + { +#if DEBUG + static int warned /* = 0 */; + + if (!warned) + { + printf( +"tcp_release_retrans: warning retransmission time is limited to %d ms\n", + TCP_RTT_MAX*1000/HZ); + warned= 1; + } +#endif + tcp_conn->tc_rtt= TCP_RTT_MAX; + } + assert (tcp_conn->tc_rtt); + + DBLOCK(0x10, printf( +"tcp_release_retrans, conn[%d]: (was too small) retrans_time= %ld ms, rtt= %ld ms\n", + tcp_conn-tcp_conn_table, retrans_time*1000/HZ, + tcp_conn->tc_rtt*1000/HZ)); + + + } + else if (seg_ack - tcp_conn->tc_rt_seq == tcp_conn->tc_mss) + { + /* Retrans time is really too big. */ + rtt= (rtt*3)>>2; + if (rtt < TCP_RTT_GRAN*CLOCK_GRAN) + rtt= TCP_RTT_GRAN*CLOCK_GRAN; + tcp_conn->tc_rtt= rtt; + assert (tcp_conn->tc_rtt); + + DBLOCK(0x10, printf( +"tcp_release_retrans, conn[%d]: (was too big) retrans_time= %ld ms, rtt= %ld ms\n", + tcp_conn-tcp_conn_table, retrans_time*1000/HZ, + tcp_conn->tc_rtt*1000/HZ)); + } + else + { + /* Retrans time might be too big. Try a bit smaller. */ + rtt= (rtt*31)>>5; + if (rtt < TCP_RTT_GRAN*CLOCK_GRAN) + rtt= TCP_RTT_GRAN*CLOCK_GRAN; + tcp_conn->tc_rtt= rtt; + assert (tcp_conn->tc_rtt); + + DBLOCK(0x20, printf( +"tcp_release_retrans, conn[%d]: (maybe too big) retrans_time= %ld ms, rtt= %ld ms\n", + tcp_conn-tcp_conn_table, retrans_time*1000/HZ, + tcp_conn->tc_rtt*1000/HZ)); + } + } + + /* Update the current window. */ + window= tcp_conn->tc_snd_cwnd-tcp_conn->tc_SND_UNA; + mss= tcp_conn->tc_mss; + assert(seg_ack != tcp_conn->tc_SND_UNA); + + /* For every real ACK we try to increase the current window + * with 1 mss. + */ + window += mss; + + /* If the window becomes larger than the current threshold, + * increment the threshold by a small amount and set the + * window to the threshold. + */ + cthresh= tcp_conn->tc_snd_cthresh; + if (window > cthresh) + { + cthresh += tcp_conn->tc_snd_cinc; + tcp_conn->tc_snd_cthresh= cthresh; + window= cthresh; + } + + /* If the window is larger than the window advertised by the + * receiver, set the window size to the advertisement. + */ + if (window > new_win) + window= new_win; + + tcp_conn->tc_snd_cwnd= seg_ack+window; + + /* Release data queued for retransmissions. */ + queue_lo= tcp_conn->tc_SND_UNA; + queue_hi= tcp_conn->tc_SND_NXT; + + tcp_conn->tc_SND_UNA= seg_ack; + if (tcp_Lmod4G(tcp_conn->tc_SND_TRM, seg_ack)) + { + tcp_conn->tc_SND_TRM= seg_ack; + } + assert(tcp_GEmod4G(tcp_conn->tc_snd_cwnd, seg_ack)); + + if (queue_lo == tcp_conn->tc_ISS) + queue_lo++; + + if (tcp_conn->tc_flags & TCF_FIN_SENT) + { + if (seg_ack == queue_hi) + seg_ack--; + if (queue_lo == queue_hi) + queue_lo--; + queue_hi--; + } + + offset= seg_ack - queue_lo; + size= queue_hi - seg_ack; + pack= tcp_conn->tc_send_data; + tcp_conn->tc_send_data= 0; + + if (!size) + { + bf_afree(pack); + + /* Reset window if a write is completed */ + tcp_conn->tc_snd_cwnd= tcp_conn->tc_SND_UNA + + 2*tcp_conn->tc_mss; + } + else + { + pack= bf_delhead(pack, offset); + tcp_conn->tc_send_data= pack; + } + + if (tcp_Gmod4G(tcp_conn->tc_SND_TRM, tcp_conn->tc_snd_cwnd)) + tcp_conn->tc_SND_TRM= tcp_conn->tc_snd_cwnd; + + /* Copy in new data if a write request is pending and + * SND_NXT-SND_TRM is less than 1 mss. + */ + if (tcp_conn->tc_fd) + { + if ((tcp_conn->tc_fd->tf_flags & + (TFF_WRITE_IP|TFF_IOCTL_IP)) && + tcp_conn->tc_SND_NXT-tcp_conn->tc_SND_TRM < + tcp_conn->tc_mss) + { + tcp_fd_write(tcp_conn); + } + } + else + { + if (tcp_conn->tc_SND_UNA == tcp_conn->tc_SND_NXT) + { + assert(tcp_conn->tc_state == TCS_CLOSING); + DBLOCK(0x10, + printf("all data sent in abondoned connection\n")); + tcp_close_connection(tcp_conn, ENOTCONN); + return; + } + } + + DIFBLOCK(2, (tcp_conn->tc_snd_cwnd == tcp_conn->tc_SND_TRM), + printf("not sending: zero window\n")); + + if (tcp_conn->tc_snd_cwnd != tcp_conn->tc_SND_TRM && + tcp_conn->tc_SND_NXT != tcp_conn->tc_SND_TRM) + { + tcp_conn_write(tcp_conn, 1); + } + +} + +/* +tcp_send_timeout +*/ + +PRIVATE void tcp_send_timeout(conn, timer) +int conn; +struct timer *timer; +{ + tcp_conn_t *tcp_conn; + u16_t mss, mss2; + time_t curr_time, stt, timeout; + + curr_time= get_time(); + + tcp_conn= &tcp_conn_table[conn]; + assert(tcp_conn->tc_flags & TCF_INUSE); + assert(tcp_conn->tc_state != TCS_CLOSED); + assert(tcp_conn->tc_state != TCS_LISTEN); + + if (tcp_conn->tc_SND_NXT == tcp_conn->tc_SND_UNA) + { + /* Nothing to do */ + assert(tcp_conn->tc_SND_TRM == tcp_conn->tc_SND_UNA); + + /* A new write sets the timer if tc_transmit_seq == SND_UNA */ + tcp_conn->tc_transmit_seq= tcp_conn->tc_SND_UNA; + tcp_conn->tc_stt= 0; + tcp_conn->tc_0wnd_to= 0; + assert(!tcp_conn->tc_fd || + !(tcp_conn->tc_fd->tf_flags & TFF_WRITE_IP)); + return; + } + + if (tcp_conn->tc_transmit_seq != tcp_conn->tc_SND_UNA) + { + /* Some data has been acknowledged since the last time the + * timer was set, set the timer again. */ + tcp_conn->tc_transmit_seq= tcp_conn->tc_SND_UNA; + tcp_conn->tc_stt= 0; + tcp_conn->tc_0wnd_to= 0; + + DBLOCK(0x20, printf( + "tcp_send_timeout: conn[%d] setting timer to %ld ms (+%ld ms)\n", + tcp_conn-tcp_conn_table, + (curr_time+tcp_conn->tc_rtt)*1000/HZ, + tcp_conn->tc_rtt*1000/HZ)); + + clck_timer(&tcp_conn->tc_transmit_timer, + curr_time+tcp_conn->tc_rtt, + tcp_send_timeout, tcp_conn-tcp_conn_table); + return; + } + + if (tcp_conn->tc_stt == 0) + { + /* Some packet arrived but did not acknowledge any data. + * Apparently, the other side is still alive and has a + * reason to transmit. We can asume a zero window. + */ + + DBLOCK(0x10, printf("conn[%d] setting zero window timer\n", + tcp_conn-tcp_conn_table)); + + if (tcp_conn->tc_0wnd_to < TCP_0WND_MIN) + tcp_conn->tc_0wnd_to= TCP_0WND_MIN; + else if (tcp_conn->tc_0wnd_to < tcp_conn->tc_rtt) + tcp_conn->tc_0wnd_to= tcp_conn->tc_rtt; + else + { + tcp_conn->tc_0wnd_to *= 2; + if (tcp_conn->tc_0wnd_to > TCP_0WND_MAX) + tcp_conn->tc_0wnd_to= TCP_0WND_MAX; + } + tcp_conn->tc_stt= curr_time; + + tcp_conn->tc_rt_seq= 0; + + DBLOCK(0x20, printf( + "tcp_send_timeout: conn[%d] setting timer to %ld ms (+%ld ms)\n", + tcp_conn-tcp_conn_table, + (curr_time+tcp_conn->tc_0wnd_to)*1000/HZ, + tcp_conn->tc_0wnd_to*1000/HZ)); + + clck_timer(&tcp_conn->tc_transmit_timer, + curr_time+tcp_conn->tc_0wnd_to, + tcp_send_timeout, tcp_conn-tcp_conn_table); + return; + } + + DIFBLOCK(0x10, (tcp_conn->tc_fd == 0), + printf("conn[%d] timeout in abondoned connection\n", + tcp_conn-tcp_conn_table)); + + /* At this point, we have do a retransmission, or send a zero window + * probe, which is almost the same. + */ + + DBLOCK(0x20, printf("tcp_send_timeout: conn[%d] una= %u, rtt= %dms\n", + tcp_conn-tcp_conn_table, + tcp_conn->tc_SND_UNA, tcp_conn->tc_rtt*1000/HZ)); + + /* Update threshold sequence number for retransmission calculation. */ + if (tcp_Gmod4G(tcp_conn->tc_SND_TRM, tcp_conn->tc_rt_threshold)) + tcp_conn->tc_rt_threshold= tcp_conn->tc_SND_TRM; + + tcp_conn->tc_SND_TRM= tcp_conn->tc_SND_UNA; + + mss= tcp_conn->tc_mss; + mss2= 2*mss; + + if (tcp_conn->tc_snd_cwnd == tcp_conn->tc_SND_UNA) + tcp_conn->tc_snd_cwnd++; + if (tcp_Gmod4G(tcp_conn->tc_snd_cwnd, tcp_conn->tc_SND_UNA + mss2)) + { + tcp_conn->tc_snd_cwnd= tcp_conn->tc_SND_UNA + mss2; + if (tcp_Gmod4G(tcp_conn->tc_SND_TRM, tcp_conn->tc_snd_cwnd)) + tcp_conn->tc_SND_TRM= tcp_conn->tc_snd_cwnd; + + tcp_conn->tc_snd_cthresh /= 2; + if (tcp_conn->tc_snd_cthresh < mss2) + tcp_conn->tc_snd_cthresh= mss2; + } + + stt= tcp_conn->tc_stt; + assert(stt <= curr_time); + if (curr_time-stt > tcp_conn->tc_rt_dead) + { + tcp_close_connection(tcp_conn, ETIMEDOUT); + return; + } + + timeout= (curr_time-stt) >> 3; + if (timeout < tcp_conn->tc_rtt) + timeout= tcp_conn->tc_rtt; + timeout += curr_time; + + DBLOCK(0x20, printf( + "tcp_send_timeout: conn[%d] setting timer to %ld ms (+%ld ms)\n", + tcp_conn-tcp_conn_table, timeout*1000/HZ, + (timeout-curr_time)*1000/HZ)); + + clck_timer(&tcp_conn->tc_transmit_timer, timeout, + tcp_send_timeout, tcp_conn-tcp_conn_table); + + if (tcp_conn->tc_rt_seq == 0) + { + tcp_conn->tc_rt_time= curr_time-tcp_conn->tc_rtt; + tcp_conn->tc_rt_seq= tcp_conn->tc_SND_UNA; + } + + tcp_conn_write(tcp_conn, 0); +} + + +PUBLIC void tcp_fd_write(tcp_conn) +tcp_conn_t *tcp_conn; +{ + tcp_fd_t *tcp_fd; + int urg, nourg, push; + u32_t max_seq; + size_t max_count, max_trans, write_count, send_count; + acc_t *data, *tmp_acc, *send_data; + + assert(tcp_conn->tc_busy); + tcp_fd= tcp_conn->tc_fd; + + if ((tcp_fd->tf_flags & TFF_IOCTL_IP) && + !(tcp_fd->tf_flags & TFF_WRITE_IP)) + { + if (tcp_fd->tf_ioreq != NWIOTCPSHUTDOWN) + return; + DBLOCK(0x10, printf("NWIOTCPSHUTDOWN\n")); + if (tcp_conn->tc_state == TCS_CLOSED) + { + tcp_reply_ioctl (tcp_fd, tcp_conn->tc_error); + return; + } + if (!(tcp_conn->tc_flags & TCF_FIN_SENT)) + { + DBLOCK(0x10, printf("calling tcp_shutdown\n")); + tcp_shutdown (tcp_conn); + } + else + { + if (tcp_conn->tc_SND_UNA == tcp_conn->tc_SND_NXT) + { + tcp_reply_ioctl (tcp_fd, NW_OK); + DBLOCK(0x10, printf("shutdown completed\n")); + } + else + { + DBLOCK(0x10, + printf("shutdown still inprogress\n")); + } + } + return; + } + + assert (tcp_fd->tf_flags & TFF_WRITE_IP); + if (tcp_conn->tc_state == TCS_CLOSED) + { + if (tcp_fd->tf_write_offset) + { + tcp_reply_write(tcp_fd, + tcp_fd->tf_write_offset); + } + else + tcp_reply_write(tcp_fd, tcp_conn->tc_error); + return; + } + + urg= (tcp_fd->tf_flags & TFF_WR_URG); + push= (tcp_fd->tf_flags & TFF_PUSH_DATA); + + max_seq= tcp_conn->tc_SND_UNA + tcp_conn->tc_snd_wnd; + if (urg) + max_seq++; + max_count= max_seq - tcp_conn->tc_SND_UNA; + max_trans= max_seq - tcp_conn->tc_SND_NXT; + if (tcp_fd->tf_write_count <= max_trans) + write_count= tcp_fd->tf_write_count; + else + write_count= max_trans; + if (write_count) + { + if (tcp_conn->tc_flags & TCF_BSD_URG) + { + if (tcp_Gmod4G(tcp_conn->tc_SND_NXT, + tcp_conn->tc_SND_UNA)) + { + nourg= tcp_LEmod4G(tcp_conn->tc_SND_UP, + tcp_conn->tc_SND_UNA); + if ((urg && nourg) || (!urg && !nourg)) + { + DBLOCK(0x20, + printf("not sending\n")); + return; + } + } + } + data= (*tcp_fd->tf_get_userdata) + (tcp_fd->tf_srfd, tcp_fd->tf_write_offset, + write_count, FALSE); + + if (!data) + { + if (tcp_fd->tf_write_offset) + { + tcp_reply_write(tcp_fd, + tcp_fd->tf_write_offset); + } + else + tcp_reply_write(tcp_fd, EFAULT); + return; + } + tcp_fd->tf_write_offset += write_count; + tcp_fd->tf_write_count -= write_count; + + send_data= tcp_conn->tc_send_data; + tcp_conn->tc_send_data= 0; + send_data= bf_append(send_data, data); + tcp_conn->tc_send_data= send_data; + tcp_conn->tc_SND_NXT += write_count; + if (urg) + { + if (tcp_conn->tc_flags & TCF_BSD_URG) + tcp_conn->tc_SND_UP= tcp_conn->tc_SND_NXT; + else + tcp_conn->tc_SND_UP= tcp_conn->tc_SND_NXT-1; + } + if (push && !tcp_fd->tf_write_count) + tcp_conn->tc_SND_PSH= tcp_conn->tc_SND_NXT; + } + if (!tcp_fd->tf_write_count) + { + tcp_reply_write(tcp_fd, tcp_fd->tf_write_offset); + } +} + +/* +tcp_shutdown +*/ + +PUBLIC void tcp_shutdown(tcp_conn) +tcp_conn_t *tcp_conn; +{ + switch (tcp_conn->tc_state) + { + case TCS_CLOSED: + case TCS_LISTEN: + case TCS_SYN_SENT: + case TCS_SYN_RECEIVED: + tcp_close_connection(tcp_conn, ENOTCONN); + return; + } + + if (tcp_conn->tc_flags & TCF_FIN_SENT) + return; + tcp_conn->tc_flags |= TCF_FIN_SENT; + tcp_conn->tc_SND_NXT++; + + assert (tcp_check_conn(tcp_conn) || + (tcp_print_conn(tcp_conn), printf("\n"), 0)); + + tcp_conn_write(tcp_conn, 1); + + /* Start the timer (if necessary) */ + tcp_set_send_timer(tcp_conn); +} + +PUBLIC void tcp_set_send_timer(tcp_conn) +tcp_conn_t *tcp_conn; +{ + time_t curr_time; + + assert(tcp_conn->tc_state != TCS_CLOSED); + assert(tcp_conn->tc_state != TCS_LISTEN); + + curr_time= get_time(); + + /* Start the timer */ + + DBLOCK(0x20, printf( + "tcp_set_send_timer: conn[%d] setting timer to %ld ms (+%ld ms)\n", + tcp_conn-tcp_conn_table, + (curr_time+tcp_conn->tc_rtt)*1000/HZ, + tcp_conn->tc_rtt*1000/HZ)); + + clck_timer(&tcp_conn->tc_transmit_timer, + curr_time+tcp_conn->tc_rtt, + tcp_send_timeout, tcp_conn-tcp_conn_table); + tcp_conn->tc_stt= curr_time; + + tcp_conn->tc_stt= curr_time; +} + +/* +tcp_close_connection + +*/ + +PUBLIC void tcp_close_connection(tcp_conn, error) +tcp_conn_t *tcp_conn; +int error; +{ + tcp_port_t *tcp_port; + tcp_fd_t *tcp_fd; + tcp_conn_t *tc; + + assert (tcp_check_conn(tcp_conn)); + assert (tcp_conn->tc_flags & TCF_INUSE); + + tcp_conn->tc_error= error; + tcp_port= tcp_conn->tc_port; + tcp_fd= tcp_conn->tc_fd; + if (tcp_conn->tc_state == TCS_CLOSED) + return; + + tcp_conn->tc_state= TCS_CLOSED; + DBLOCK(0x10, tcp_print_state(tcp_conn); printf("\n")); + + if (tcp_fd) + { + tcp_conn->tc_busy++; + assert(tcp_fd->tf_conn == tcp_conn); + + if (tcp_fd->tf_flags & TFF_READ_IP) + tcp_fd_read (tcp_conn, 1); + assert (!(tcp_fd->tf_flags & TFF_READ_IP)); + + if (tcp_fd->tf_flags & TFF_WRITE_IP) + { + tcp_fd_write(tcp_conn); + tcp_conn_write(tcp_conn, 1); + } + assert (!(tcp_fd->tf_flags & TFF_WRITE_IP)); + if (tcp_fd->tf_flags & TFF_IOCTL_IP) + { + tcp_fd_write(tcp_conn); + tcp_conn_write(tcp_conn, 1); + } + if (tcp_fd->tf_flags & TFF_IOCTL_IP) + assert(tcp_fd->tf_ioreq != NWIOTCPSHUTDOWN); + + if (tcp_conn->tc_connInprogress) + tcp_restart_connect(tcp_conn->tc_fd); + assert (!tcp_conn->tc_connInprogress); + assert (!(tcp_fd->tf_flags & TFF_IOCTL_IP) || + (printf("req= 0x%lx\n", tcp_fd->tf_ioreq), 0)); + tcp_conn->tc_busy--; + } + + if (tcp_conn->tc_rcvd_data) + { + bf_afree(tcp_conn->tc_rcvd_data); + tcp_conn->tc_rcvd_data= NULL; + } + tcp_conn->tc_flags &= ~TCF_FIN_RECV; + tcp_conn->tc_RCV_LO= tcp_conn->tc_RCV_NXT; + + if (tcp_conn->tc_adv_data) + { + bf_afree(tcp_conn->tc_adv_data); + tcp_conn->tc_adv_data= NULL; + } + + if (tcp_conn->tc_send_data) + { + bf_afree(tcp_conn->tc_send_data); + tcp_conn->tc_send_data= NULL; + tcp_conn->tc_SND_TRM= + tcp_conn->tc_SND_NXT= tcp_conn->tc_SND_UNA; + } + tcp_conn->tc_SND_TRM= tcp_conn->tc_SND_NXT= tcp_conn->tc_SND_UNA; + + if (tcp_conn->tc_remipopt) + { + bf_afree(tcp_conn->tc_remipopt); + tcp_conn->tc_remipopt= NULL; + } + + if (tcp_conn->tc_tcpopt) + { + bf_afree(tcp_conn->tc_tcpopt); + tcp_conn->tc_tcpopt= NULL; + } + + if (tcp_conn->tc_frag2send) + { + bf_afree(tcp_conn->tc_frag2send); + tcp_conn->tc_frag2send= NULL; + } + if (tcp_conn->tc_flags & TCF_MORE2WRITE) + { + for (tc= tcp_port->tp_snd_head; tc; tc= tc->tc_send_link) + { + if (tc->tc_send_link == tcp_conn) + break; + } + if (tc == NULL) + { + assert(tcp_port->tp_snd_head == tcp_conn); + tcp_port->tp_snd_head= tcp_conn->tc_send_link; + } + else + { + tc->tc_send_link= tcp_conn->tc_send_link; + if (tc->tc_send_link == NULL) + tcp_port->tp_snd_tail= tc; + } + tcp_conn->tc_flags &= ~TCF_MORE2WRITE; + } + + clck_untimer (&tcp_conn->tc_transmit_timer); + tcp_conn->tc_transmit_seq= 0; + + /* clear all flags but TCF_INUSE */ + tcp_conn->tc_flags &= TCF_INUSE; + assert (tcp_check_conn(tcp_conn)); +} + +/* + * $PchId: tcp_send.c,v 1.12 1996/12/17 07:57:11 philip Exp $ + */ diff --git a/servers/inet/generic/type.h b/servers/inet/generic/type.h new file mode 100644 index 000000000..d4a23b0ab --- /dev/null +++ b/servers/inet/generic/type.h @@ -0,0 +1,20 @@ +/* +type.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef INET_TYPE_H +#define INET_TYPE_H + +typedef struct acc *(*get_userdata_t) ARGS(( int fd, size_t offset, + size_t count, int for_ioctl )); +typedef int (*put_userdata_t) ARGS(( int fd, size_t offset, + struct acc *data, int for_ioctl )); +typedef void (*put_pkt_t) ARGS(( int fd, struct acc *data, size_t datalen )); + +#endif /* INET_TYPE_H */ + +/* + * $PchId: type.h,v 1.5 1995/11/21 06:51:58 philip Exp $ + */ diff --git a/servers/inet/generic/udp.c b/servers/inet/generic/udp.c new file mode 100644 index 000000000..95211a7c8 --- /dev/null +++ b/servers/inet/generic/udp.c @@ -0,0 +1,1603 @@ +/* +udp.c + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "type.h" + +#include "assert.h" +#include "buf.h" +#include "clock.h" +#include "icmp_lib.h" +#include "io.h" +#include "ip.h" +#include "sr.h" +#include "udp.h" + +THIS_FILE + +#define UDP_FD_NR (4*IP_PORT_MAX) +#define UDP_PORT_HASH_NR 16 /* Must be a power of 2 */ + +typedef struct udp_port +{ + int up_flags; + int up_state; + int up_ipfd; + int up_ipdev; + acc_t *up_wr_pack; + ipaddr_t up_ipaddr; + struct udp_fd *up_next_fd; + struct udp_fd *up_write_fd; + struct udp_fd *up_port_any; + struct udp_fd *up_port_hash[UDP_PORT_HASH_NR]; +} udp_port_t; + +#define UPF_EMPTY 0x0 +#define UPF_WRITE_IP 0x1 +#define UPF_WRITE_SP 0x2 +#define UPF_READ_IP 0x4 +#define UPF_READ_SP 0x8 +#define UPF_SUSPEND 0x10 +#define UPF_MORE2WRITE 0x20 + +#define UPS_EMPTY 0 +#define UPS_SETPROTO 1 +#define UPS_GETCONF 2 +#define UPS_MAIN 3 +#define UPS_ERROR 4 + +typedef struct udp_fd +{ + int uf_flags; + udp_port_t *uf_port; + ioreq_t uf_ioreq; + int uf_srfd; + nwio_udpopt_t uf_udpopt; + get_userdata_t uf_get_userdata; + put_userdata_t uf_put_userdata; + acc_t *uf_rdbuf_head; + acc_t *uf_rdbuf_tail; + size_t uf_rd_count; + size_t uf_wr_count; + time_t uf_exp_tim; + struct udp_fd *uf_port_next; +} udp_fd_t; + +#define UFF_EMPTY 0x0 +#define UFF_INUSE 0x1 +#define UFF_IOCTL_IP 0x2 +#define UFF_READ_IP 0x4 +#define UFF_WRITE_IP 0x8 +#define UFF_OPTSET 0x10 + +FORWARD void read_ip_packets ARGS(( udp_port_t *udp_port )); +FORWARD void udp_buffree ARGS(( int priority )); +#ifdef BUF_CONSISTENCY_CHECK +FORWARD void udp_bufcheck ARGS(( void )); +#endif +FORWARD void udp_main ARGS(( udp_port_t *udp_port )); +FORWARD acc_t *udp_get_data ARGS(( int fd, size_t offset, size_t count, + int for_ioctl )); +FORWARD int udp_put_data ARGS(( int fd, size_t offset, acc_t *data, + int for_ioctl )); +FORWARD void udp_restart_write_port ARGS(( udp_port_t *udp_port )); +FORWARD void udp_ip_arrived ARGS(( int port, acc_t *pack, size_t pack_size )); +FORWARD void reply_thr_put ARGS(( udp_fd_t *udp_fd, int reply, + int for_ioctl )); +FORWARD void reply_thr_get ARGS(( udp_fd_t *udp_fd, int reply, + int for_ioctl )); +FORWARD int udp_setopt ARGS(( udp_fd_t *udp_fd )); +FORWARD udpport_t find_unused_port ARGS(( int fd )); +FORWARD int is_unused_port ARGS(( Udpport_t port )); +FORWARD int udp_packet2user ARGS(( udp_fd_t *udp_fd )); +FORWARD void restart_write_fd ARGS(( udp_fd_t *udp_fd )); +FORWARD u16_t pack_oneCsum ARGS(( acc_t *pack )); +FORWARD void udp_rd_enqueue ARGS(( udp_fd_t *udp_fd, acc_t *pack, + time_t exp_tim )); +FORWARD void hash_fd ARGS(( udp_fd_t *udp_fd )); +FORWARD void unhash_fd ARGS(( udp_fd_t *udp_fd )); + +PRIVATE udp_port_t *udp_port_table; +PRIVATE udp_fd_t udp_fd_table[UDP_FD_NR]; + +PUBLIC void udp_prep() +{ + udp_port_table= alloc(ip_conf_nr * sizeof(udp_port_table[0])); +} + +PUBLIC void udp_init() +{ + udp_fd_t *udp_fd; + udp_port_t *udp_port; + struct ip_conf *icp; + int i, j; + + assert (BUF_S >= sizeof(struct nwio_ipopt)); + assert (BUF_S >= sizeof(struct nwio_ipconf)); + assert (BUF_S >= sizeof(struct nwio_udpopt)); + assert (BUF_S >= sizeof(struct udp_io_hdr)); + assert (UDP_HDR_SIZE == sizeof(udp_hdr_t)); + assert (UDP_IO_HDR_SIZE == sizeof(udp_io_hdr_t)); + +#if ZERO + for (i= 0, udp_fd= udp_fd_table; i<UDP_FD_NR; i++, udp_fd++) + { + udp_fd->uf_flags= UFF_EMPTY; + udp_fd->uf_rdbuf_head= NULL; + } +#endif + +#ifndef BUF_CONSISTENCY_CHECK + bf_logon(udp_buffree); +#else + bf_logon(udp_buffree, udp_bufcheck); +#endif + + for (i= 0, udp_port= udp_port_table, icp= ip_conf; + i<ip_conf_nr; i++, udp_port++, icp++) + { + udp_port->up_ipdev= i; + +#if ZERO + udp_port->up_flags= UPF_EMPTY; + udp_port->up_state= UPS_EMPTY; +#endif + udp_port->up_next_fd= udp_fd_table; +#if ZERO + udp_port->up_write_fd= NULL; + udp_port->up_port_any= NULL; + for (j= 0; j<UDP_PORT_HASH_NR; j++) + udp_port->up_port_hash[j]= NULL; +#endif + + sr_add_minor(if2minor(icp->ic_ifno, UDP_DEV_OFF), + i, udp_open, udp_close, udp_read, + udp_write, udp_ioctl, udp_cancel); + + udp_main(udp_port); + } +} + +PRIVATE void udp_main(udp_port) +udp_port_t *udp_port; +{ + udp_fd_t *udp_fd; + int result, i; + + switch (udp_port->up_state) + { + case UPS_EMPTY: + udp_port->up_state= UPS_SETPROTO; + + udp_port->up_ipfd= ip_open(udp_port->up_ipdev, + udp_port->up_ipdev, udp_get_data, udp_put_data, + udp_ip_arrived); + if (udp_port->up_ipfd < 0) + { + udp_port->up_state= UPS_ERROR; + DBLOCK(1, printf("%s, %d: unable to open ip port\n", + __FILE__, __LINE__)); + return; + } + + result= ip_ioctl(udp_port->up_ipfd, NWIOSIPOPT); + if (result == NW_SUSPEND) + udp_port->up_flags |= UPF_SUSPEND; + if (result<0) + { + return; + } + if (udp_port->up_state != UPS_GETCONF) + return; + /* drops through */ + case UPS_GETCONF: + udp_port->up_flags &= ~UPF_SUSPEND; + + result= ip_ioctl(udp_port->up_ipfd, NWIOGIPCONF); + if (result == NW_SUSPEND) + udp_port->up_flags |= UPF_SUSPEND; + if (result<0) + { + return; + } + if (udp_port->up_state != UPS_MAIN) + return; + /* drops through */ + case UPS_MAIN: + udp_port->up_flags &= ~UPF_SUSPEND; + + for (i= 0, udp_fd= udp_fd_table; i<UDP_FD_NR; i++, udp_fd++) + { + if (!(udp_fd->uf_flags & UFF_INUSE)) + continue; + if (udp_fd->uf_port != udp_port) + continue; + if (udp_fd->uf_flags & UFF_IOCTL_IP) + udp_ioctl(i, udp_fd->uf_ioreq); + } + read_ip_packets(udp_port); + return; +#if !CRAMPED + default: + DBLOCK(1, printf("udp_port_table[%d].up_state= %d\n", + udp_port->up_ipdev, udp_port->up_state)); + ip_panic(( "unknown state" )); +#endif + } +} + +int udp_open (port, srfd, get_userdata, put_userdata, put_pkt) +int port; +int srfd; +get_userdata_t get_userdata; +put_userdata_t put_userdata; +put_pkt_t put_pkt; +{ + int i; + udp_fd_t *udp_fd; + + for (i= 0; i<UDP_FD_NR && (udp_fd_table[i].uf_flags & UFF_INUSE); + i++); + + if (i>= UDP_FD_NR) + { + DBLOCK(1, printf("out of fds\n")); + return EAGAIN; + } + + udp_fd= &udp_fd_table[i]; + + udp_fd->uf_flags= UFF_INUSE; + udp_fd->uf_port= &udp_port_table[port]; + udp_fd->uf_srfd= srfd; + udp_fd->uf_udpopt.nwuo_flags= UDP_DEF_OPT; + udp_fd->uf_get_userdata= get_userdata; + udp_fd->uf_put_userdata= put_userdata; + assert(udp_fd->uf_rdbuf_head == NULL); + udp_fd->uf_port_next= NULL; + + return i; + +} + +PRIVATE acc_t *udp_get_data (port, offset, count, for_ioctl) +int port; +size_t offset; +size_t count; +int for_ioctl; +{ + udp_port_t *udp_port; + udp_fd_t *udp_fd; + int result; + + udp_port= &udp_port_table[port]; + + switch(udp_port->up_state) + { + case UPS_SETPROTO: +assert (for_ioctl); + if (!count) + { + result= (int)offset; + if (result<0) + { + udp_port->up_state= UPS_ERROR; + break; + } + udp_port->up_state= UPS_GETCONF; + if (udp_port->up_flags & UPF_SUSPEND) + udp_main(udp_port); + return NULL; + } + else + { + struct nwio_ipopt *ipopt; + acc_t *acc; + +assert (!offset); +assert (count == sizeof(*ipopt)); + + acc= bf_memreq(sizeof(*ipopt)); + ipopt= (struct nwio_ipopt *)ptr2acc_data(acc); + ipopt->nwio_flags= NWIO_COPY | NWIO_EN_LOC | + NWIO_EN_BROAD | NWIO_REMANY | NWIO_PROTOSPEC | + NWIO_HDR_O_ANY | NWIO_RWDATALL; + ipopt->nwio_proto= IPPROTO_UDP; + return acc; + } + case UPS_MAIN: +assert (!for_ioctl); +assert (udp_port->up_flags & UPF_WRITE_IP); + if (!count) + { + result= (int)offset; +assert (udp_port->up_wr_pack); + bf_afree(udp_port->up_wr_pack); + udp_port->up_wr_pack= 0; + if (udp_port->up_flags & UPF_WRITE_SP) + { + if (udp_port->up_write_fd) + { + udp_fd= udp_port->up_write_fd; + udp_port->up_write_fd= NULL; + udp_fd->uf_flags &= ~UFF_WRITE_IP; + reply_thr_get(udp_fd, result, FALSE); + } + udp_port->up_flags &= ~(UPF_WRITE_SP | + UPF_WRITE_IP); + if (udp_port->up_flags & UPF_MORE2WRITE) + { + udp_restart_write_port(udp_port); + } + } + else + udp_port->up_flags &= ~UPF_WRITE_IP; + } + else + { + return bf_cut (udp_port->up_wr_pack, offset, count); + } + break; + default: +#if !CRAMPED + printf("udp_get_data(%d, 0x%x, 0x%x) called but up_state= 0x%x\n", + port, offset, count, udp_port->up_state); +#endif + break; + } + return NULL; +} + +PRIVATE int udp_put_data (fd, offset, data, for_ioctl) +int fd; +size_t offset; +acc_t *data; +int for_ioctl; +{ + udp_port_t *udp_port; + int result; + + udp_port= &udp_port_table[fd]; + + switch (udp_port->up_state) + { + case UPS_GETCONF: + if (!data) + { + result= (int)offset; + if (result<0) + { + udp_port->up_state= UPS_ERROR; + return NW_OK; + } + udp_port->up_state= UPS_MAIN; + if (udp_port->up_flags & UPF_SUSPEND) + udp_main(udp_port); + } + else + { + struct nwio_ipconf *ipconf; + + data= bf_packIffLess(data, sizeof(*ipconf)); + ipconf= (struct nwio_ipconf *)ptr2acc_data(data); +assert (ipconf->nwic_flags & NWIC_IPADDR_SET); + udp_port->up_ipaddr= ipconf->nwic_ipaddr; + bf_afree(data); + } + break; + case UPS_MAIN: + assert(0); + + assert (udp_port->up_flags & UPF_READ_IP); + if (!data) + { + result= (int)offset; + compare (result, >=, 0); + if (udp_port->up_flags & UPF_READ_SP) + { + udp_port->up_flags &= ~(UPF_READ_SP| + UPF_READ_IP); + read_ip_packets(udp_port); + } + else + udp_port->up_flags &= ~UPF_READ_IP; + } + else + { +assert (!offset); /* This isn't a valid assertion but ip sends only + * whole datagrams up */ + udp_ip_arrived(fd, data, bf_bufsize(data)); + } + break; +#if !CRAMPED + default: + ip_panic(( + "udp_put_data(%d, 0x%x, 0x%x) called but up_state= 0x%x\n", + fd, offset, data, udp_port->up_state )); +#endif + } + return NW_OK; +} + +int udp_ioctl (fd, req) +int fd; +ioreq_t req; +{ + udp_fd_t *udp_fd; + udp_port_t *udp_port; + nwio_udpopt_t *udp_opt; + acc_t *opt_acc; + int result; + + udp_fd= &udp_fd_table[fd]; + +assert (udp_fd->uf_flags & UFF_INUSE); + + udp_port= udp_fd->uf_port; + udp_fd->uf_flags |= UFF_IOCTL_IP; + udp_fd->uf_ioreq= req; + + if (udp_port->up_state != UPS_MAIN) + return NW_SUSPEND; + + switch(req) + { + case NWIOSUDPOPT: + result= udp_setopt(udp_fd); + break; + case NWIOGUDPOPT: + opt_acc= bf_memreq(sizeof(*udp_opt)); +assert (opt_acc->acc_length == sizeof(*udp_opt)); + udp_opt= (nwio_udpopt_t *)ptr2acc_data(opt_acc); + + *udp_opt= udp_fd->uf_udpopt; + udp_opt->nwuo_locaddr= udp_fd->uf_port->up_ipaddr; + result= (*udp_fd->uf_put_userdata)(udp_fd->uf_srfd, 0, opt_acc, + TRUE); + if (result == NW_OK) + reply_thr_put(udp_fd, NW_OK, TRUE); + break; + default: + reply_thr_get(udp_fd, EBADIOCTL, TRUE); + result= NW_OK; + break; + } + if (result != NW_SUSPEND) + udp_fd->uf_flags &= ~UFF_IOCTL_IP; + return result; +} + +PRIVATE int udp_setopt(udp_fd) +udp_fd_t *udp_fd; +{ + udp_fd_t *fd_ptr; + nwio_udpopt_t oldopt, newopt; + acc_t *data; + int result; + udpport_t port; + unsigned int new_en_flags, new_di_flags, old_en_flags, old_di_flags, + all_flags, flags; + unsigned long new_flags; + int i; + + data= (*udp_fd->uf_get_userdata)(udp_fd->uf_srfd, 0, + sizeof(nwio_udpopt_t), TRUE); + + if (!data) + return EFAULT; + + data= bf_packIffLess(data, sizeof(nwio_udpopt_t)); +assert (data->acc_length == sizeof(nwio_udpopt_t)); + + newopt= *(nwio_udpopt_t *)ptr2acc_data(data); + bf_afree(data); + oldopt= udp_fd->uf_udpopt; + + old_en_flags= oldopt.nwuo_flags & 0xffff; + old_di_flags= (oldopt.nwuo_flags >> 16) & 0xffff; + + new_en_flags= newopt.nwuo_flags & 0xffff; + new_di_flags= (newopt.nwuo_flags >> 16) & 0xffff; + + if (new_en_flags & new_di_flags) + { + DBLOCK(1, printf("returning EBADMODE\n")); + + reply_thr_get(udp_fd, EBADMODE, TRUE); + return NW_OK; + } + + /* NWUO_ACC_MASK */ + if (new_di_flags & NWUO_ACC_MASK) + { + DBLOCK(1, printf("returning EBADMODE\n")); + + reply_thr_get(udp_fd, EBADMODE, TRUE); + return NW_OK; + /* access modes can't be disabled */ + } + + if (!(new_en_flags & NWUO_ACC_MASK)) + new_en_flags |= (old_en_flags & NWUO_ACC_MASK); + + /* NWUO_LOCPORT_MASK */ + if (new_di_flags & NWUO_LOCPORT_MASK) + { + DBLOCK(1, printf("returning EBADMODE\n")); + + reply_thr_get(udp_fd, EBADMODE, TRUE); + return NW_OK; + /* the loc ports can't be disabled */ + } + if (!(new_en_flags & NWUO_LOCPORT_MASK)) + { + new_en_flags |= (old_en_flags & NWUO_LOCPORT_MASK); + newopt.nwuo_locport= oldopt.nwuo_locport; + } + else if ((new_en_flags & NWUO_LOCPORT_MASK) == NWUO_LP_SEL) + { + newopt.nwuo_locport= find_unused_port(udp_fd-udp_fd_table); + } + else if ((new_en_flags & NWUO_LOCPORT_MASK) == NWUO_LP_SET) + { + if (!newopt.nwuo_locport) + { + DBLOCK(1, printf("returning EBADMODE\n")); + + reply_thr_get(udp_fd, EBADMODE, TRUE); + return NW_OK; + } + } + + /* NWUO_LOCADDR_MASK */ + if (!((new_en_flags | new_di_flags) & NWUO_LOCADDR_MASK)) + { + new_en_flags |= (old_en_flags & NWUO_LOCADDR_MASK); + new_di_flags |= (old_di_flags & NWUO_LOCADDR_MASK); + } + + /* NWUO_BROAD_MASK */ + if (!((new_en_flags | new_di_flags) & NWUO_BROAD_MASK)) + { + new_en_flags |= (old_en_flags & NWUO_BROAD_MASK); + new_di_flags |= (old_di_flags & NWUO_BROAD_MASK); + } + + /* NWUO_REMPORT_MASK */ + if (!((new_en_flags | new_di_flags) & NWUO_REMPORT_MASK)) + { + new_en_flags |= (old_en_flags & NWUO_REMPORT_MASK); + new_di_flags |= (old_di_flags & NWUO_REMPORT_MASK); + newopt.nwuo_remport= oldopt.nwuo_remport; + } + + /* NWUO_REMADDR_MASK */ + if (!((new_en_flags | new_di_flags) & NWUO_REMADDR_MASK)) + { + new_en_flags |= (old_en_flags & NWUO_REMADDR_MASK); + new_di_flags |= (old_di_flags & NWUO_REMADDR_MASK); + newopt.nwuo_remaddr= oldopt.nwuo_remaddr; + } + + /* NWUO_RW_MASK */ + if (!((new_en_flags | new_di_flags) & NWUO_RW_MASK)) + { + new_en_flags |= (old_en_flags & NWUO_RW_MASK); + new_di_flags |= (old_di_flags & NWUO_RW_MASK); + } + + /* NWUO_IPOPT_MASK */ + if (!((new_en_flags | new_di_flags) & NWUO_IPOPT_MASK)) + { + new_en_flags |= (old_en_flags & NWUO_IPOPT_MASK); + new_di_flags |= (old_di_flags & NWUO_IPOPT_MASK); + } + + new_flags= ((unsigned long)new_di_flags << 16) | new_en_flags; + if ((new_flags & NWUO_RWDATONLY) && + ((new_flags & NWUO_LOCPORT_MASK) == NWUO_LP_ANY || + (new_flags & (NWUO_RP_ANY|NWUO_RA_ANY|NWUO_EN_IPOPT)))) + { + DBLOCK(1, printf("returning EBADMODE\n")); + + reply_thr_get(udp_fd, EBADMODE, TRUE); + return NW_OK; + } + + /* Check the access modes */ + if ((new_flags & NWUO_LOCPORT_MASK) == NWUO_LP_SEL || + (new_flags & NWUO_LOCPORT_MASK) == NWUO_LP_SET) + { + for (i= 0, fd_ptr= udp_fd_table; i<UDP_FD_NR; i++, fd_ptr++) + { + if (fd_ptr == udp_fd) + continue; + if (!(fd_ptr->uf_flags & UFF_INUSE)) + continue; + if (fd_ptr->uf_port != udp_fd->uf_port) + continue; + flags= fd_ptr->uf_udpopt.nwuo_flags; + if ((flags & NWUO_LOCPORT_MASK) != NWUO_LP_SEL && + (flags & NWUO_LOCPORT_MASK) != NWUO_LP_SET) + continue; + if (fd_ptr->uf_udpopt.nwuo_locport != + newopt.nwuo_locport) + { + continue; + } + if ((flags & NWUO_ACC_MASK) != + (new_flags & NWUO_ACC_MASK)) + { + DBLOCK(1, printf( + "address inuse: new fd= %d, old_fd= %d, port= %u\n", + udp_fd-udp_fd_table, + fd_ptr-udp_fd_table, + newopt.nwuo_locport)); + + reply_thr_get(udp_fd, EADDRINUSE, TRUE); + return NW_OK; + } + } + } + + if (udp_fd->uf_flags & UFF_OPTSET) + unhash_fd(udp_fd); + + newopt.nwuo_flags= new_flags; + udp_fd->uf_udpopt= newopt; + + all_flags= new_en_flags | new_di_flags; + if ((all_flags & NWUO_ACC_MASK) && (all_flags & NWUO_LOCPORT_MASK) && + (all_flags & NWUO_LOCADDR_MASK) && + (all_flags & NWUO_BROAD_MASK) && + (all_flags & NWUO_REMPORT_MASK) && + (all_flags & NWUO_REMADDR_MASK) && + (all_flags & NWUO_RW_MASK) && + (all_flags & NWUO_IPOPT_MASK)) + udp_fd->uf_flags |= UFF_OPTSET; + else + { + udp_fd->uf_flags &= ~UFF_OPTSET; + } + + if (udp_fd->uf_flags & UFF_OPTSET) + hash_fd(udp_fd); + + reply_thr_get(udp_fd, NW_OK, TRUE); + return NW_OK; +} + +PRIVATE udpport_t find_unused_port(fd) +int fd; +{ + udpport_t port, nw_port; + + nw_port= htons(0xC000+fd); + if (is_unused_port(nw_port)) + return nw_port; + + for (port= 0xC000+UDP_FD_NR; port < 0xFFFF; port++) + { + nw_port= htons(port); + if (is_unused_port(nw_port)) + return nw_port; + } +#if !CRAMPED + ip_panic(( "unable to find unused port (shouldn't occur)" )); + return 0; +#endif +} + +/* +reply_thr_put +*/ + +PRIVATE void reply_thr_put(udp_fd, reply, for_ioctl) +udp_fd_t *udp_fd; +int reply; +int for_ioctl; +{ + int result; + + result= (*udp_fd->uf_put_userdata)(udp_fd->uf_srfd, reply, + (acc_t *)0, for_ioctl); + assert(result == NW_OK); +} + +/* +reply_thr_get +*/ + +PRIVATE void reply_thr_get(udp_fd, reply, for_ioctl) +udp_fd_t *udp_fd; +int reply; +int for_ioctl; +{ + acc_t *result; + result= (*udp_fd->uf_get_userdata)(udp_fd->uf_srfd, reply, + (size_t)0, for_ioctl); + assert (!result); +} + +PRIVATE int is_unused_port(port) +udpport_t port; +{ + int i; + udp_fd_t *udp_fd; + + for (i= 0, udp_fd= udp_fd_table; i<UDP_FD_NR; i++, + udp_fd++) + { + if (!(udp_fd->uf_flags & UFF_OPTSET)) + continue; + if (udp_fd->uf_udpopt.nwuo_locport == port) + return FALSE; + } + return TRUE; +} + +PRIVATE void read_ip_packets(udp_port) +udp_port_t *udp_port; +{ + int result; + + do + { + udp_port->up_flags |= UPF_READ_IP; + result= ip_read(udp_port->up_ipfd, UDP_MAX_DATAGRAM); + if (result == NW_SUSPEND) + { + udp_port->up_flags |= UPF_READ_SP; + return; + } +assert(result == NW_OK); + udp_port->up_flags &= ~UPF_READ_IP; + } while(!(udp_port->up_flags & UPF_READ_IP)); +} + + +PUBLIC int udp_read (fd, count) +int fd; +size_t count; +{ + udp_fd_t *udp_fd; + acc_t *tmp_acc, *next_acc; + + udp_fd= &udp_fd_table[fd]; + if (!(udp_fd->uf_flags & UFF_OPTSET)) + { + reply_thr_put(udp_fd, EBADMODE, FALSE); + return NW_OK; + } + + udp_fd->uf_rd_count= count; + + if (udp_fd->uf_rdbuf_head) + { + if (get_time() <= udp_fd->uf_exp_tim) + return udp_packet2user (udp_fd); + tmp_acc= udp_fd->uf_rdbuf_head; + while (tmp_acc) + { + next_acc= tmp_acc->acc_ext_link; + bf_afree(tmp_acc); + tmp_acc= next_acc; + } + udp_fd->uf_rdbuf_head= NULL; + } + udp_fd->uf_flags |= UFF_READ_IP; + return NW_SUSPEND; +} + +PRIVATE int udp_packet2user (udp_fd) +udp_fd_t *udp_fd; +{ + acc_t *pack, *tmp_pack; + udp_io_hdr_t *hdr; + int result, hdr_len; + size_t size, transf_size; + + pack= udp_fd->uf_rdbuf_head; + udp_fd->uf_rdbuf_head= pack->acc_ext_link; + + size= bf_bufsize (pack); + + if (udp_fd->uf_udpopt.nwuo_flags & NWUO_RWDATONLY) + { + + pack= bf_packIffLess (pack, UDP_IO_HDR_SIZE); + assert (pack->acc_length >= UDP_IO_HDR_SIZE); + + hdr= (udp_io_hdr_t *)ptr2acc_data(pack); +#if CONF_UDP_IO_NW_BYTE_ORDER + hdr_len= UDP_IO_HDR_SIZE+NTOHS(hdr->uih_ip_opt_len); +#else + hdr_len= UDP_IO_HDR_SIZE+hdr->uih_ip_opt_len; +#endif + + assert (size>= hdr_len); + size -= hdr_len; + tmp_pack= bf_cut(pack, hdr_len, size); + bf_afree(pack); + pack= tmp_pack; + } + + if (size>udp_fd->uf_rd_count) + { + tmp_pack= bf_cut (pack, 0, udp_fd->uf_rd_count); + bf_afree(pack); + pack= tmp_pack; + transf_size= udp_fd->uf_rd_count; + } + else + transf_size= size; + + result= (*udp_fd->uf_put_userdata)(udp_fd->uf_srfd, + (size_t)0, pack, FALSE); + + if (result >= 0) + if (size > transf_size) + result= EPACKSIZE; + else + result= transf_size; + + udp_fd->uf_flags &= ~UFF_READ_IP; + result= (*udp_fd->uf_put_userdata)(udp_fd->uf_srfd, result, + (acc_t *)0, FALSE); +assert (result == 0); + + return result; +} + +PRIVATE void udp_ip_arrived(port, pack, pack_size) +int port; +acc_t *pack; +size_t pack_size; +{ + udp_port_t *udp_port; + udp_fd_t *udp_fd, *share_fd; + acc_t *ip_hdr_acc, *udp_acc, *ipopt_pack, *no_ipopt_pack, *tmp_acc; + ip_hdr_t *ip_hdr; + udp_hdr_t *udp_hdr; + udp_io_hdr_t *udp_io_hdr; + size_t ip_hdr_size, udp_size, data_size, opt_size; + ipaddr_t src_addr, dst_addr; + udpport_t src_port, dst_port; + u8_t u16[2]; + u16_t chksum; + unsigned long dst_type, flags; + time_t exp_tim; + int i, delivered, hash; + + udp_port= &udp_port_table[port]; + + ip_hdr_acc= bf_cut(pack, 0, IP_MIN_HDR_SIZE); + ip_hdr_acc= bf_packIffLess(ip_hdr_acc, IP_MIN_HDR_SIZE); + ip_hdr= (ip_hdr_t *)ptr2acc_data(ip_hdr_acc); + ip_hdr_size= (ip_hdr->ih_vers_ihl & IH_IHL_MASK) << 2; + if (ip_hdr_size != IP_MIN_HDR_SIZE) + { + bf_afree(ip_hdr_acc); + ip_hdr_acc= bf_cut(pack, 0, ip_hdr_size); + ip_hdr_acc= bf_packIffLess(ip_hdr_acc, ip_hdr_size); + ip_hdr= (ip_hdr_t *)ptr2acc_data(ip_hdr_acc); + } + + udp_acc= bf_delhead(pack, ip_hdr_size); + pack= NULL; + + pack_size -= ip_hdr_size; + if (pack_size < UDP_HDR_SIZE) + { + DBLOCK(1, printf("packet too small\n")); + + bf_afree(ip_hdr_acc); + bf_afree(udp_acc); + return; + } + + udp_acc= bf_packIffLess(udp_acc, UDP_HDR_SIZE); + udp_hdr= (udp_hdr_t *)ptr2acc_data(udp_acc); + udp_size= ntohs(udp_hdr->uh_length); + if (udp_size > pack_size) + { + DBLOCK(1, printf("packet too large\n")); + + bf_afree(ip_hdr_acc); + bf_afree(udp_acc); + return; + } + + src_addr= ip_hdr->ih_src; + dst_addr= ip_hdr->ih_dst; + + if (udp_hdr->uh_chksum) + { + u16[0]= 0; + u16[1]= ip_hdr->ih_proto; + chksum= pack_oneCsum(udp_acc); + chksum= oneC_sum(chksum, (u16_t *)&src_addr, sizeof(ipaddr_t)); + chksum= oneC_sum(chksum, (u16_t *)&dst_addr, sizeof(ipaddr_t)); + chksum= oneC_sum(chksum, (u16_t *)u16, sizeof(u16)); + chksum= oneC_sum(chksum, (u16_t *)&udp_hdr->uh_length, + sizeof(udp_hdr->uh_length)); + if (~chksum & 0xffff) + { + DBLOCK(1, printf("checksum error in udp packet\n"); + printf("src ip_addr= "); + writeIpAddr(src_addr); + printf(" dst ip_addr= "); + writeIpAddr(dst_addr); + printf("\n"); + printf("packet chksum= 0x%x, sum= 0x%x\n", + udp_hdr->uh_chksum, chksum)); + + bf_afree(ip_hdr_acc); + bf_afree(udp_acc); + return; + } + } + + exp_tim= get_time() + UDP_READ_EXP_TIME; + src_port= udp_hdr->uh_src_port; + dst_port= udp_hdr->uh_dst_port; + + /* Send an ICMP port unreachable if the packet could not be + * delivered. + */ + delivered= 0; + + if (dst_addr == udp_port->up_ipaddr) + dst_type= NWUO_EN_LOC; + else + { + dst_type= NWUO_EN_BROAD; + + /* Don't send ICMP error packets for broadcast packets */ + delivered= 1; + } + + DBLOCK(0x20, printf("udp: got packet from "); + writeIpAddr(src_addr); + printf(".%u to ", ntohs(src_port)); + writeIpAddr(dst_addr); + printf(".%u\n", ntohs(dst_port))); + + no_ipopt_pack= bf_memreq(UDP_IO_HDR_SIZE); + udp_io_hdr= (udp_io_hdr_t *)ptr2acc_data(no_ipopt_pack); + udp_io_hdr->uih_src_addr= src_addr; + udp_io_hdr->uih_dst_addr= dst_addr; + udp_io_hdr->uih_src_port= src_port; + udp_io_hdr->uih_dst_port= dst_port; + data_size = udp_size-UDP_HDR_SIZE; +#if CONF_UDP_IO_NW_BYTE_ORDER + udp_io_hdr->uih_ip_opt_len= HTONS(0); + udp_io_hdr->uih_data_len= htons(data_size); +#else + udp_io_hdr->uih_ip_opt_len= 0; + udp_io_hdr->uih_data_len= data_size; +#endif + no_ipopt_pack->acc_next= bf_cut(udp_acc, UDP_HDR_SIZE, data_size); + + if (ip_hdr_size == IP_MIN_HDR_SIZE) + { + ipopt_pack= no_ipopt_pack; + ipopt_pack->acc_linkC++; + } + else + { + ipopt_pack= bf_memreq(UDP_IO_HDR_SIZE); + *(udp_io_hdr_t *)ptr2acc_data(ipopt_pack)= *udp_io_hdr; + udp_io_hdr= (udp_io_hdr_t *)ptr2acc_data(ipopt_pack); + opt_size = ip_hdr_size-IP_MIN_HDR_SIZE; +#if CONF_UDP_IO_NW_BYTE_ORDER + udp_io_hdr->uih_ip_opt_len= htons(opt_size); +#else + udp_io_hdr->uih_ip_opt_len= opt_size; +#endif + tmp_acc= bf_cut(ip_hdr_acc, (size_t)IP_MIN_HDR_SIZE, opt_size); + assert(tmp_acc->acc_linkC == 1); + assert(tmp_acc->acc_next == NULL); + ipopt_pack->acc_next= tmp_acc; + + tmp_acc->acc_next= no_ipopt_pack->acc_next; + if (tmp_acc->acc_next) + tmp_acc->acc_next->acc_linkC++; + } + + hash= dst_port; + hash ^= (hash >> 8); + hash &= (UDP_PORT_HASH_NR-1); + + for (i= 0; i<2; i++) + { + share_fd= NULL; + + udp_fd= (i == 0) ? udp_port->up_port_any : + udp_port->up_port_hash[hash]; + for (; udp_fd; udp_fd= udp_fd->uf_port_next) + { + if (i && udp_fd->uf_udpopt.nwuo_locport != dst_port) + continue; + + assert(udp_fd->uf_flags & UFF_INUSE); + assert(udp_fd->uf_flags & UFF_OPTSET); + + if (udp_fd->uf_port != udp_port) + continue; + + flags= udp_fd->uf_udpopt.nwuo_flags; + if (!(flags & dst_type)) + continue; + + if ((flags & NWUO_RP_SET) && + udp_fd->uf_udpopt.nwuo_remport != src_port) + { + continue; + } + + if ((flags & NWUO_RA_SET) && + udp_fd->uf_udpopt.nwuo_remaddr != src_addr) + { + continue; + } + + if (i) + { + /* Packet is considdered to be delivered */ + delivered= 1; + } + + if ((flags & NWUO_ACC_MASK) == NWUO_SHARED && + (!share_fd || !udp_fd->uf_rdbuf_head)) + { + share_fd= udp_fd; + continue; + } + + if (flags & NWUO_EN_IPOPT) + pack= ipopt_pack; + else + pack= no_ipopt_pack; + + pack->acc_linkC++; + udp_rd_enqueue(udp_fd, pack, exp_tim); + if (udp_fd->uf_flags & UFF_READ_IP) + udp_packet2user(udp_fd); + } + + if (share_fd) + { + flags= share_fd->uf_udpopt.nwuo_flags; + if (flags & NWUO_EN_IPOPT) + pack= ipopt_pack; + else + pack= no_ipopt_pack; + + pack->acc_linkC++; + udp_rd_enqueue(share_fd, pack, exp_tim); + if (share_fd->uf_flags & UFF_READ_IP) + udp_packet2user(share_fd); + } + } + + if (ipopt_pack) + bf_afree(ipopt_pack); + if (no_ipopt_pack) + bf_afree(no_ipopt_pack); + + if (!delivered) + { + DBLOCK(0x2, printf("udp: could not deliver packet from "); + writeIpAddr(src_addr); + printf(".%u to ", ntohs(src_port)); + writeIpAddr(dst_addr); + printf(".%u\n", ntohs(dst_port))); + + pack= bf_append(ip_hdr_acc, udp_acc); + ip_hdr_acc= NULL; + udp_acc= NULL; + icmp_snd_unreachable(udp_port->up_ipdev, pack, + ICMP_PORT_UNRCH); + return; + } + + assert (ip_hdr_acc); + bf_afree(ip_hdr_acc); + assert (udp_acc); + bf_afree(udp_acc); +} + +PUBLIC void udp_close(fd) +int fd; +{ + udp_fd_t *udp_fd; + acc_t *tmp_acc, *next_acc; + + udp_fd= &udp_fd_table[fd]; + + assert (udp_fd->uf_flags & UFF_INUSE); + + if (udp_fd->uf_flags & UFF_OPTSET) + unhash_fd(udp_fd); + + udp_fd->uf_flags= UFF_EMPTY; + tmp_acc= udp_fd->uf_rdbuf_head; + while (tmp_acc) + { + next_acc= tmp_acc->acc_ext_link; + bf_afree(tmp_acc); + tmp_acc= next_acc; + } + udp_fd->uf_rdbuf_head= NULL; +} + +PUBLIC int udp_write(fd, count) +int fd; +size_t count; +{ + udp_fd_t *udp_fd; + udp_port_t *udp_port; + + udp_fd= &udp_fd_table[fd]; + udp_port= udp_fd->uf_port; + + if (!(udp_fd->uf_flags & UFF_OPTSET)) + { + reply_thr_get (udp_fd, EBADMODE, FALSE); + return NW_OK; + } + +assert (!(udp_fd->uf_flags & UFF_WRITE_IP)); + + udp_fd->uf_wr_count= count; + + udp_fd->uf_flags |= UFF_WRITE_IP; + + restart_write_fd(udp_fd); + + if (udp_fd->uf_flags & UFF_WRITE_IP) + { + DBLOCK(1, printf("replying NW_SUSPEND\n")); + + return NW_SUSPEND; + } + else + { + return NW_OK; + } +} + +PRIVATE void restart_write_fd(udp_fd) +udp_fd_t *udp_fd; +{ + udp_port_t *udp_port; + acc_t *pack, *ip_hdr_pack, *udp_hdr_pack, *ip_opt_pack, *user_data; + udp_hdr_t *udp_hdr; + udp_io_hdr_t *udp_io_hdr; + ip_hdr_t *ip_hdr; + size_t ip_opt_size, user_data_size; + unsigned long flags; + u16_t chksum; + u8_t u16[2]; + int result; + + udp_port= udp_fd->uf_port; + + if (udp_port->up_flags & UPF_WRITE_IP) + { + udp_port->up_flags |= UPF_MORE2WRITE; + return; + } + +assert (udp_fd->uf_flags & UFF_WRITE_IP); + udp_fd->uf_flags &= ~UFF_WRITE_IP; + +assert (!udp_port->up_wr_pack); + + pack= (*udp_fd->uf_get_userdata)(udp_fd->uf_srfd, 0, + udp_fd->uf_wr_count, FALSE); + if (!pack) + { + udp_fd->uf_flags &= ~UFF_WRITE_IP; + reply_thr_get (udp_fd, EFAULT, FALSE); + return; + } + + flags= udp_fd->uf_udpopt.nwuo_flags; + + ip_hdr_pack= bf_memreq(IP_MIN_HDR_SIZE); + ip_hdr= (ip_hdr_t *)ptr2acc_data(ip_hdr_pack); + + udp_hdr_pack= bf_memreq(UDP_HDR_SIZE); + udp_hdr= (udp_hdr_t *)ptr2acc_data(udp_hdr_pack); + + if (flags & NWUO_RWDATALL) + { + pack= bf_packIffLess(pack, UDP_IO_HDR_SIZE); + udp_io_hdr= (udp_io_hdr_t *)ptr2acc_data(pack); +#if CONF_UDP_IO_NW_BYTE_ORDER + ip_opt_size= ntohs(udp_io_hdr->uih_ip_opt_len); +#else + ip_opt_size= udp_io_hdr->uih_ip_opt_len; +#endif + if (UDP_IO_HDR_SIZE+ip_opt_size>udp_fd->uf_wr_count) + { + bf_afree(ip_hdr_pack); + bf_afree(udp_hdr_pack); + bf_afree(pack); + reply_thr_get (udp_fd, EINVAL, FALSE); + return; + } + if (ip_opt_size & 3) + { + bf_afree(ip_hdr_pack); + bf_afree(udp_hdr_pack); + bf_afree(pack); + reply_thr_get (udp_fd, EFAULT, FALSE); + return; + } + if (ip_opt_size) + ip_opt_pack= bf_cut(pack, UDP_IO_HDR_SIZE, ip_opt_size); + else + ip_opt_pack= 0; + user_data_size= udp_fd->uf_wr_count-UDP_IO_HDR_SIZE- + ip_opt_size; + user_data= bf_cut(pack, UDP_IO_HDR_SIZE+ip_opt_size, + user_data_size); + bf_afree(pack); + } + else + { + udp_io_hdr= 0; + ip_opt_size= 0; + user_data_size= udp_fd->uf_wr_count; + ip_opt_pack= 0; + user_data= pack; + } + + ip_hdr->ih_vers_ihl= (IP_MIN_HDR_SIZE+ip_opt_size) >> 2; + ip_hdr->ih_tos= UDP_TOS; + ip_hdr->ih_flags_fragoff= HTONS(UDP_IP_FLAGS); + ip_hdr->ih_ttl= UDP_TTL; + ip_hdr->ih_proto= IPPROTO_UDP; + if (flags & NWUO_RA_SET) + { + DBLOCK(1, printf("NWUO_RA_SET\n")); + + ip_hdr->ih_dst= udp_fd->uf_udpopt.nwuo_remaddr; + } + else + { +assert (udp_io_hdr); + ip_hdr->ih_dst= udp_io_hdr->uih_dst_addr; + } + + if ((flags & NWUO_LOCPORT_MASK) != NWUO_LP_ANY) + udp_hdr->uh_src_port= udp_fd->uf_udpopt.nwuo_locport; + else + { +assert (udp_io_hdr); + udp_hdr->uh_src_port= udp_io_hdr->uih_src_port; + } + + if (flags & NWUO_RP_SET) + udp_hdr->uh_dst_port= udp_fd->uf_udpopt.nwuo_remport; + else + { +assert (udp_io_hdr); + udp_hdr->uh_dst_port= udp_io_hdr->uih_dst_port; + } + + udp_hdr->uh_length= htons(UDP_HDR_SIZE+user_data_size); + udp_hdr->uh_chksum= 0; + + udp_hdr_pack->acc_next= user_data; + chksum= pack_oneCsum(udp_hdr_pack); + chksum= oneC_sum(chksum, (u16_t *)&udp_fd->uf_port->up_ipaddr, + sizeof(ipaddr_t)); + chksum= oneC_sum(chksum, (u16_t *)&ip_hdr->ih_dst, sizeof(ipaddr_t)); + u16[0]= 0; + u16[1]= IPPROTO_UDP; + chksum= oneC_sum(chksum, (u16_t *)u16, sizeof(u16)); + chksum= oneC_sum(chksum, (u16_t *)&udp_hdr->uh_length, sizeof(u16_t)); + if (~chksum) + chksum= ~chksum; + udp_hdr->uh_chksum= chksum; + + if (ip_opt_pack) + { + ip_opt_pack= bf_packIffLess(ip_opt_pack, ip_opt_size); + ip_opt_pack->acc_next= udp_hdr_pack; + udp_hdr_pack= ip_opt_pack; + } + ip_hdr_pack->acc_next= udp_hdr_pack; + +assert (!udp_port->up_wr_pack); +assert (!(udp_port->up_flags & UPF_WRITE_IP)); + + udp_port->up_wr_pack= ip_hdr_pack; + udp_port->up_flags |= UPF_WRITE_IP; + result= ip_write(udp_port->up_ipfd, bf_bufsize(ip_hdr_pack)); + if (result == NW_SUSPEND) + { + udp_port->up_flags |= UPF_WRITE_SP; + udp_fd->uf_flags |= UFF_WRITE_IP; + udp_port->up_write_fd= udp_fd; + } + else if (result<0) + reply_thr_get(udp_fd, result, FALSE); + else + reply_thr_get (udp_fd, udp_fd->uf_wr_count, FALSE); +} + +PRIVATE u16_t pack_oneCsum(pack) +acc_t *pack; +{ + u16_t prev; + int odd_byte; + char *data_ptr; + int length; + char byte_buf[2]; + + assert (pack); + + prev= 0; + + odd_byte= FALSE; + for (; pack; pack= pack->acc_next) + { + + data_ptr= ptr2acc_data(pack); + length= pack->acc_length; + + if (!length) + continue; + if (odd_byte) + { + byte_buf[1]= *data_ptr; + prev= oneC_sum(prev, (u16_t *)byte_buf, 2); + data_ptr++; + length--; + odd_byte= FALSE; + } + if (length & 1) + { + odd_byte= TRUE; + length--; + byte_buf[0]= data_ptr[length]; + } + if (!length) + continue; + prev= oneC_sum (prev, (u16_t *)data_ptr, length); + } + if (odd_byte) + { + byte_buf[1]= 0; + prev= oneC_sum (prev, (u16_t *)byte_buf, 1); + } + return prev; +} + +PRIVATE void udp_restart_write_port(udp_port ) +udp_port_t *udp_port; +{ + udp_fd_t *udp_fd; + int i; + +assert (!udp_port->up_wr_pack); +assert (!(udp_port->up_flags & (UPF_WRITE_IP|UPF_WRITE_SP))); + + while (udp_port->up_flags & UPF_MORE2WRITE) + { + udp_port->up_flags &= ~UPF_MORE2WRITE; + + for (i= 0, udp_fd= udp_port->up_next_fd; i<UDP_FD_NR; + i++, udp_fd++) + { + if (udp_fd == &udp_fd_table[UDP_FD_NR]) + udp_fd= udp_fd_table; + + if (!(udp_fd->uf_flags & UFF_INUSE)) + continue; + if (!(udp_fd->uf_flags & UFF_WRITE_IP)) + continue; + if (udp_fd->uf_port != udp_port) + continue; + restart_write_fd(udp_fd); + if (udp_port->up_flags & UPF_WRITE_IP) + { + udp_port->up_next_fd= udp_fd+1; + udp_port->up_flags |= UPF_MORE2WRITE; + return; + } + } + } +} + +PUBLIC int udp_cancel(fd, which_operation) +int fd; +int which_operation; +{ + udp_fd_t *udp_fd; + + DBLOCK(0x10, printf("udp_cancel(%d, %d)\n", fd, which_operation)); + + udp_fd= &udp_fd_table[fd]; + + switch (which_operation) + { + case SR_CANCEL_READ: +assert (udp_fd->uf_flags & UFF_READ_IP); + udp_fd->uf_flags &= ~UFF_READ_IP; + reply_thr_put(udp_fd, EINTR, FALSE); + break; + case SR_CANCEL_WRITE: +assert (udp_fd->uf_flags & UFF_WRITE_IP); + udp_fd->uf_flags &= ~UFF_WRITE_IP; + if (udp_fd->uf_port->up_write_fd == udp_fd) + udp_fd->uf_port->up_write_fd= NULL; + reply_thr_get(udp_fd, EINTR, FALSE); + break; + case SR_CANCEL_IOCTL: +assert (udp_fd->uf_flags & UFF_IOCTL_IP); + udp_fd->uf_flags &= ~UFF_IOCTL_IP; + reply_thr_get(udp_fd, EINTR, TRUE); + break; +#if !CRAMPED + default: + ip_panic(( "got unknown cancel request" )); +#endif + } + return NW_OK; +} + +PRIVATE void udp_buffree (priority) +int priority; +{ + int i; + time_t curr_tim; + udp_fd_t *udp_fd; + acc_t *tmp_acc, *next_acc; + + if (priority == UDP_PRI_FDBUFS_EXTRA) + { + for (i=0, udp_fd= udp_fd_table; i<UDP_FD_NR; i++, udp_fd++) + { + while (udp_fd->uf_rdbuf_head && + udp_fd->uf_rdbuf_head->acc_ext_link) + { + tmp_acc= udp_fd->uf_rdbuf_head; + udp_fd->uf_rdbuf_head= tmp_acc->acc_ext_link; + bf_afree(tmp_acc); + } + } + } + + if (priority == UDP_PRI_FDBUFS) + { + for (i=0, udp_fd= udp_fd_table; i<UDP_FD_NR; i++, udp_fd++) + { + while (udp_fd->uf_rdbuf_head) + { + tmp_acc= udp_fd->uf_rdbuf_head; + udp_fd->uf_rdbuf_head= tmp_acc->acc_ext_link; + bf_afree(tmp_acc); + } + } + } +} + +PRIVATE void udp_rd_enqueue(udp_fd, pack, exp_tim) +udp_fd_t *udp_fd; +acc_t *pack; +time_t exp_tim; +{ + acc_t *tmp_acc; + + if (pack->acc_linkC != 1) + { + tmp_acc= bf_dupacc(pack); + bf_afree(pack); + pack= tmp_acc; + } + pack->acc_ext_link= NULL; + if (udp_fd->uf_rdbuf_head == NULL) + { + udp_fd->uf_exp_tim= exp_tim; + udp_fd->uf_rdbuf_head= pack; + } + else + udp_fd->uf_rdbuf_tail->acc_ext_link= pack; + udp_fd->uf_rdbuf_tail= pack; +} + +PRIVATE void hash_fd(udp_fd) +udp_fd_t *udp_fd; +{ + udp_port_t *udp_port; + int hash; + + udp_port= udp_fd->uf_port; + if ((udp_fd->uf_udpopt.nwuo_flags & NWUO_LOCPORT_MASK) == + NWUO_LP_ANY) + { + udp_fd->uf_port_next= udp_port->up_port_any; + udp_port->up_port_any= udp_fd; + } + else + { + hash= udp_fd->uf_udpopt.nwuo_locport; + hash ^= (hash >> 8); + hash &= (UDP_PORT_HASH_NR-1); + + udp_fd->uf_port_next= udp_port->up_port_hash[hash]; + udp_port->up_port_hash[hash]= udp_fd; + } +} + +PRIVATE void unhash_fd(udp_fd) +udp_fd_t *udp_fd; +{ + udp_port_t *udp_port; + udp_fd_t *prev, *curr, **udp_fd_p; + int hash; + + udp_port= udp_fd->uf_port; + if ((udp_fd->uf_udpopt.nwuo_flags & NWUO_LOCPORT_MASK) == + NWUO_LP_ANY) + { + udp_fd_p= &udp_port->up_port_any; + } + else + { + hash= udp_fd->uf_udpopt.nwuo_locport; + hash ^= (hash >> 8); + hash &= (UDP_PORT_HASH_NR-1); + + udp_fd_p= &udp_port->up_port_hash[hash]; + } + for (prev= NULL, curr= *udp_fd_p; curr; + prev= curr, curr= curr->uf_port_next) + { + if (curr == udp_fd) + break; + } + assert(curr); + if (prev) + prev->uf_port_next= curr->uf_port_next; + else + *udp_fd_p= curr->uf_port_next; +} + +#ifdef BUF_CONSISTENCY_CHECK +PRIVATE void udp_bufcheck() +{ + int i; + udp_port_t *udp_port; + udp_fd_t *udp_fd; + acc_t *tmp_acc; + + for (i= 0, udp_port= udp_port_table; i<ip_conf_nr; i++, udp_port++) + { + if (udp_port->up_wr_pack) + bf_check_acc(udp_port->up_wr_pack); + } + + for (i= 0, udp_fd= udp_fd_table; i<UDP_FD_NR; i++, udp_fd++) + { + for (tmp_acc= udp_fd->uf_rdbuf_head; tmp_acc; + tmp_acc= tmp_acc->acc_ext_link) + { + bf_check_acc(tmp_acc); + } + } +} +#endif + +/* + * $PchId: udp.c,v 1.10 1996/08/06 06:48:05 philip Exp $ + */ diff --git a/servers/inet/generic/udp.h b/servers/inet/generic/udp.h new file mode 100644 index 000000000..b2190ca78 --- /dev/null +++ b/servers/inet/generic/udp.h @@ -0,0 +1,37 @@ +/* +udp.h + +Copyright 1995 Philip Homburg +*/ + +#ifndef UDP_H +#define UDP_H + +#define UDP_DEF_OPT NWUO_NOFLAGS +#define UDP_MAX_DATAGRAM 40000 /* 8192 */ +#define UDP_READ_EXP_TIME (10L * HZ) +#define UDP_TOS 0 +#define UDP_IP_FLAGS 0 +#define UDP_TTL 30 + +#define UDP0 0 + +struct acc; + +void udp_prep ARGS(( void )); +void udp_init ARGS(( void )); +int udp_open ARGS(( int port, int srfd, + get_userdata_t get_userdata, put_userdata_t put_userdata, + put_pkt_t put_pkt )); +int udp_ioctl ARGS(( int fd, ioreq_t req )); +int udp_read ARGS(( int fd, size_t count )); +int udp_write ARGS(( int fd, size_t count )); +void udp_close ARGS(( int fd )); +int udp_cancel ARGS(( int fd, int which_operation )); + +#endif /* UDP_H */ + + +/* + * $PchId: udp.h,v 1.6 1996/05/07 20:53:31 philip Exp $ + */ diff --git a/servers/inet/inet.c b/servers/inet/inet.c new file mode 100644 index 000000000..bd954ed50 --- /dev/null +++ b/servers/inet/inet.c @@ -0,0 +1,273 @@ +/* this file contains the interface of the network software with rest of + minix. Furthermore it contains the main loop of the network task. + +Copyright 1995 Philip Homburg + +Changes: + Oct 10, 2004 Get own process number with SYS_GETINFO (Jorrit N. Herder) + Sep 30, 2004 Updated system calls done in clock.c. (Jorrit N. Herder) + Sep 15, 2004 Exit on HARD_STOP notification (Jorrit N. Herder) + Aug 24, 2004 Alarms no longer from SYNALRM task (Jorrit N. Herder) + +The valid messages and their parameters are: + +from FS: + __________________________________________________________________ +| | | | | | | +| m_type | DEVICE | PROC_NR | COUNT | POSITION | ADDRESS | +|_______________|___________|_________|_______|__________|_________| +| | | | | | | +| NW_OPEN | minor dev | proc nr | mode | | | +|_______________|___________|_________|_______|__________|_________| +| | | | | | | +| NW_CLOSE | minor dev | proc nr | | | | +|_______________|___________|_________|_______|__________|_________| +| | | | | | | +| NW_IOCTL | minor dev | proc nr | | NWIO.. | address | +|_______________|___________|_________|_______|__________|_________| +| | | | | | | +| NW_READ | minor dev | proc nr | count | | address | +|_______________|___________|_________|_______|__________|_________| +| | | | | | | +| NW_WRITE | minor dev | proc nr | count | | address | +|_______________|___________|_________|_______|__________|_________| +| | | | | | | +| NW_CANCEL | minor dev | proc nr | | | | +|_______________|___________|_________|_______|__________|_________| + +from the Ethernet task: + _______________________________________________________________________ +| | | | | | | +| m_type | DL_PORT | DL_PROC | DL_COUNT | DL_STAT | DL_TIME | +|_______________|___________|_________|__________|____________|_________| +| | | | | | | +| DL_TASK_INT | minor dev | proc nr | rd_count | 0 | stat | time | +|_______________|___________|_________|__________|____________|_________| +| | | | | | | +| DL_TASK_REPLY | minor dev | proc nr | rd_count | err | stat | time | | +|_______________|___________|_________|__________|____________|_________| +*/ + +#include "inet.h" + +#define _MINIX 1 + +#include <unistd.h> +#include <sys/svrctl.h> +#include <minix/syslib.h> +#include <minix/utils.h> + +#include "mq.h" +#include "proto.h" +#include "generic/type.h" + +#include "generic/assert.h" +#include "generic/buf.h" +#include "generic/clock.h" +#include "generic/eth.h" +#include "generic/event.h" +#if !CRAMPED +#include "generic/arp.h" +#include "generic/ip.h" +#include "generic/psip.h" +#include "generic/sr.h" +#include "generic/tcp.h" +#include "generic/udp.h" +#endif + +THIS_FILE + +int this_proc; /* Process number of this server. */ + +#ifdef BUF_CONSISTENCY_CHECK +extern int inet_buf_debug; +#endif + +_PROTOTYPE( void main, (void) ); + +FORWARD _PROTOTYPE( void nw_init, (void) ); + +PUBLIC void main() +{ + mq_t *mq; + int r; + int source; + + DBLOCK(1, printf("%s\n", version)); + + nw_init(); + while (TRUE) + { +#ifdef BUF_CONSISTENCY_CHECK + if (inet_buf_debug) + { + static int buf_debug_count= 0; + + if (buf_debug_count++ > inet_buf_debug) + { + buf_debug_count= 0; + if (!bf_consistency_check()) + break; + } + } +#endif + if (ev_head) + { + ev_process(); + continue; + } + if (clck_call_expire) + { + clck_expire_timers(); + continue; + } + mq= mq_get(); + if (!mq) + ip_panic(("out of messages")); + + r= receive (ANY, &mq->mq_mess); + if (r<0) { + ip_panic(("unable to receive: %d", r)); + } + reset_time(); + source= mq->mq_mess.m_source; + if (source == FS_PROC_NR) { + sr_rec(mq); + } else if (source == HARDWARE) /* a notification! */ + { + if (mq->mq_mess.m_type == SYN_ALARM) { + clck_tick(&mq->mq_mess); + mq_free(mq); + } else if (mq->mq_mess.m_type == HARD_STOP) { + sys_exit(0); + } + } + else + { +compare(mq->mq_mess.m_type, ==, DL_TASK_REPLY); + eth_rec(&mq->mq_mess); + mq_free(mq); + } + } + ip_panic(("task is not allowed to terminate")); +} + +PRIVATE void nw_init() +{ + struct fssignon device; + int pnr; + + /* Read configuration. */ + read_conf(); + eth_prep(); + arp_prep(); + psip_prep(); + ip_prep(); + tcp_prep(); + udp_prep(); + + + /* Sign on as a server at all offices in the proper order. */ + if (svrctl(MMSIGNON, (void *) NULL) == -1) { + printf("inet: server signon failed\n"); + exit(1); + } + if (svrctl(SYSSIGNON, (void *) NULL) == -1) pause(); + + /* Our new identity as a server. */ + if (get_proc_nr(&this_proc, NULL) != OK) + ip_panic(( "unable to find own process nr\n")); + + /* Register the device group. */ + device.dev= ip_dev; + device.style= STYLE_CLONE; + if (svrctl(FSSIGNON, (void *) &device) == -1) { + printf("inet: error %d on registering ethernet devices\n", + errno); + pause(); + } + + +#ifdef BUF_CONSISTENCY_CHECK + inet_buf_debug= 100; + if (inet_buf_debug) + { + ip_warning(( "buffer consistency check enabled" )); + } +#endif + mq_init(); + bf_init(); + clck_init(); + sr_init(); + eth_init(); +#if ENABLE_ARP + arp_init(); +#endif +#if ENABLE_PSIP + psip_init(); +#endif +#if ENABLE_IP + ip_init(); +#endif +#if ENABLE_TCP + tcp_init(); +#endif +#if ENABLE_UDP + udp_init(); +#endif +} + +#if !CRAMPED +PUBLIC void panic0(file, line) +char *file; +int line; +{ + printf("panic at %s, %d: ", file, line); +} + +PUBLIC void panic() +{ + printf("\ninet stacktrace: "); + stacktrace(); + server_panic("INET","aborted due to a panic",NO_NUM); +} + +#else /* CRAMPED */ + +PUBLIC void panic(file, line) +char *file; +int line; +{ + printf("panic at %s, %d\n", file, line); + server_panic("INET","aborted due to a panic",NO_NUM); +} +#endif + +#if !NDEBUG +PUBLIC void bad_assertion(file, line, what) +char *file; +int line; +char *what; +{ + panic0(file, line); + printf("assertion \"%s\" failed", what); + panic(); +} + + +PUBLIC void bad_compare(file, line, lhs, what, rhs) +char *file; +int line; +int lhs; +char *what; +int rhs; +{ + panic0(file, line); + printf("compare (%d) %s (%d) failed", lhs, what, rhs); + panic(); +} +#endif /* !NDEBUG */ + +/* + * $PchId: inet.c,v 1.12 1996/12/17 07:58:19 philip Exp $ + */ diff --git a/servers/inet/inet.h b/servers/inet/inet.h new file mode 100644 index 000000000..7a0123000 --- /dev/null +++ b/servers/inet/inet.h @@ -0,0 +1,111 @@ +/* +inet/inet.h + +Created: Dec 30, 1991 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +#ifndef INET__INET_H +#define INET__INET_H + +#define _SYSTEM 1 /* get OK and negative error codes */ + +#include <ansi.h> + +#define CRAMPED (_EM_WSIZE==2) /* 64K code and data is quite cramped. */ +#define ZERO 0 /* Used to comment out initialization code that does nothing. */ + +#include <sys/types.h> +#include <errno.h> +#include <stddef.h> +#include <stdlib.h> +#include <string.h> + +#include <minix/config.h> +#include <minix/type.h> +#include <minix/const.h> +#include <minix/com.h> +#include <minix/syslib.h> +#include <net/hton.h> +#include <net/gen/ether.h> +#include <net/gen/eth_hdr.h> +#include <net/gen/eth_io.h> +#include <net/gen/in.h> +#include <net/gen/ip_hdr.h> +#include <net/gen/ip_io.h> +#include <net/gen/icmp.h> +#include <net/gen/icmp_hdr.h> +#include <net/gen/oneCsum.h> +#include <net/gen/psip_hdr.h> +#include <net/gen/psip_io.h> +#include <net/gen/route.h> +#include <net/gen/tcp.h> +#include <net/gen/tcp_hdr.h> +#include <net/gen/tcp_io.h> +#include <net/gen/udp.h> +#include <net/gen/udp_hdr.h> +#include <net/gen/udp_io.h> +#include <net/ioctl.h> + +#include "const.h" +#include "inet_config.h" + +#define PUBLIC +#define EXTERN extern +#define PRIVATE static +#define FORWARD static + +typedef int ioreq_t; + +#define THIS_FILE static char *this_file= __FILE__; + +#if CRAMPED + +/* Minimum panic info. */ +#define ip_panic(print_list) panic(this_file, __LINE__) +_PROTOTYPE( void panic, (char *file, int line) ); + +#else /* !CRAMPED */ + +/* Maximum panic info. */ +#define ip_panic(print_list) \ + (panic0(this_file, __LINE__), printf print_list, panic()) +_PROTOTYPE( void panic0, (char *file, int line) ); +_PROTOTYPE( void panic, (void) ); + +#endif /* !CRAMPED */ + +#if DEBUG +#define ip_warning(print_list) \ + ( \ + printf("warning at %s, %d: ", this_file, __LINE__), \ + printf print_list, \ + printf("\ninet stacktrace: "), \ + stacktrace() \ + ) + +#define DBLOCK(level, code) \ + do { if ((level) & DEBUG) { where(); code; } } while(0) +#define DIFBLOCK(level, condition, code) \ + do { if (((level) & DEBUG) && (condition)) \ + { where(); code; } } while(0) + +#else /* !DEBUG */ +#define ip_warning(print_list) 0 +#define DBLOCK(level, code) 0 +#define DIFBLOCK(level, condition, code) 0 +#endif + +#define ARGS(x) _ARGS(x) + +extern char version[]; +extern int this_proc; + +void stacktrace ARGS(( void )); + +#endif /* INET__INET_H */ + +/* + * $PchId: inet.h,v 1.8 1996/05/07 21:05:04 philip Exp $ + */ diff --git a/servers/inet/inet_config.c b/servers/inet/inet_config.c new file mode 100644 index 000000000..c1b33c00a --- /dev/null +++ b/servers/inet/inet_config.c @@ -0,0 +1,303 @@ +/* +inet/inet_config.c + +Created: Nov 11, 1992 by Philip Homburg + +Modified: Apr 07, 2001 by Kees J. Bot + Read the configuration file and fill in the xx_conf[] arrays. + +Copyright 1995 Philip Homburg +*/ + +#define _MINIX 1 + +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <minix/config.h> +#include <minix/type.h> +#include <minix/syslib.h> +#include "inet_config.h" + +#define CRAMPED (_EM_WSIZE==2) /* 64K code and data is quite cramped. */ +#if CRAMPED +#endif + +struct eth_conf eth_conf[IP_PORT_MAX]; +struct psip_conf psip_conf[IP_PORT_MAX]; +struct ip_conf ip_conf[IP_PORT_MAX]; +dev_t ip_dev; + +int eth_conf_nr; +#if ENABLE_PSIP +int psip_conf_nr; +#endif +int ip_conf_nr; + +static u8_t iftype[IP_PORT_MAX]; /* Interface in use as? */ +static int ifdefault= -1; /* Default network interface. */ + +static void fatal(char *label) +{ + printf("init: %s: Error %d\n", label, errno); + exit(1); +} + +static void check_rm(char *device) +/* Check if a device is not among the living. */ +{ + if (unlink(device) < 0) { + if (errno == ENOENT) return; + fatal(device); + } + printf("rm %s\n", device); +} + +static void check_mknod(char *device, mode_t mode, int minor) +/* Check if a device exists with the proper device number. */ +{ + struct stat st; + dev_t dev; + + dev= (ip_dev & 0xFF00) | minor; + + if (stat(device, &st) < 0) { + if (errno != ENOENT) fatal(device); + } else { + if (S_ISCHR(st.st_mode) && st.st_rdev == dev) return; + if (unlink(device) < 0) fatal(device); + } + + if (mknod(device, S_IFCHR | mode, dev) < 0) fatal(device); + printf("mknod %s c %d %d\n", device, (ip_dev >> 8), minor); +} + +static void check_ln(char *old, char *new) +/* Check if 'old' and 'new' are still properly linked. */ +{ + struct stat st_old, st_new; + + if (stat(old, &st_old) < 0) fatal(old); + if (stat(new, &st_new) < 0) { + if (errno != ENOENT) fatal(new); + } else { + if (st_new.st_dev == st_old.st_dev + && st_new.st_ino == st_old.st_ino) { + return; + } + if (unlink(new) < 0) fatal(new); + } + + if (link(old, new) < 0) fatal(new); + printf("ln %s %s\n", old, new); +} + +static void check_dev(int type, int ifno) +/* Check if the device group with interface number 'ifno' exists and has the + * proper device numbers. If 'type' is -1 then the device group must be + * removed. + */ +{ + static struct devlist { + char *defname; + mode_t mode; + u8_t minor_off; + } devlist[] = { + { "/dev/eth", 0600, ETH_DEV_OFF }, + { "/dev/psip", 0600, PSIP_DEV_OFF }, + { "/dev/ip", 0600, IP_DEV_OFF }, + { "/dev/tcp", 0666, TCP_DEV_OFF }, + { "/dev/udp", 0666, UDP_DEV_OFF }, + }; + struct devlist *dvp; + int i; + char device[sizeof("/dev/psip99")]; + char *dp; + + for (i= 0; i < sizeof(devlist) / sizeof(devlist[0]); i++) { + dvp= &devlist[i]; + strcpy(device, dvp->defname); + dp= device + strlen(device); + if (ifno >= 10) *dp++ = '0' + (ifno / 10); + *dp++ = '0' + (ifno % 10); + *dp = 0; + + if (type == 0 + || (i == 0 && type != NETTYPE_ETH) + || (i == 1 && type != NETTYPE_PSIP) + ) { + check_rm(device); + if (ifno == ifdefault) check_rm(dvp->defname); + } else { + check_mknod(device, dvp->mode, + if2minor(ifno, dvp->minor_off)); + if (ifno == ifdefault) check_ln(device, dvp->defname); + } + } +} + +static int cfg_fd; +static char word[16]; +static unsigned line; + +static void error(void) +{ + printf("inet: error on line %u\n", line); + exit(1); +} + +static void token(int need) +{ + /* Read a word from the configuration file. Return a null string on + * EOF. Return a punctiation as a one character word. If 'need' is + * true then an actual word is expected at this point, so err out if + * not. + */ + unsigned char *wp; + static unsigned char c= '\n'; + + wp= (unsigned char *) word; + *wp = 0; + + while (c <= ' ') { + if (c == '\n') line++; + if (read(cfg_fd, &c, 1) != 1) { + if (need) error(); + return; + } + } + + do { + if (wp < (unsigned char *) word + sizeof(word)-1) *wp++ = c; + if (read(cfg_fd, &c, 1) != 1) c= ' '; + if (word[0] == ';' || word[0] == '{' || word[0] == '}') { + if (need) error(); + break; + } + } while (c > ' ' && c != ';' && c != '{' && c != '}'); + *wp = 0; +} + +static unsigned number(char *str, unsigned max) +{ + /* Interpret a string as an unsigned decimal number, no bigger than + * 'max'. Return this number. + */ + char *s; + unsigned n, d; + + s= str; + n= 0; + while ((d= (*s - '0')) < 10 && n <= max) { + n= n * 10 + d; + s++; + } + if (*s != 0 || n > max) { + printf("inet: '%s' is not a number <= %u\n", str, max); + error(); + } + return n; +} + +void read_conf(void) +{ + int i, j, ifno, type, port; + struct eth_conf *ecp; + struct psip_conf *pcp; + struct ip_conf *icp; + struct stat st; + + /* Open the configuration file. */ + if ((cfg_fd= open(PATH_INET_CONF, O_RDONLY)) == -1) + fatal(PATH_INET_CONF); + + ecp= eth_conf; + pcp= psip_conf; + icp= ip_conf; + + while (token(0), word[0] != 0) { + if (strncmp(word, "eth", 3) == 0) { + ecp->ec_ifno= ifno= number(word+3, IP_PORT_MAX-1); + type= NETTYPE_ETH; + port= eth_conf_nr; + token(1); + ecp->ec_task= alloc(strlen(word)+1); + strcpy(ecp->ec_task, word); + token(1); + ecp->ec_port= number(word, IP_PORT_MAX-1); + ecp++; + eth_conf_nr++; +#if ENABLE_PSIP + } else + if (strncmp(word, "psip", 4) == 0) { + pcp->pc_ifno= ifno= number(word+4, IP_PORT_MAX-1); + type= NETTYPE_PSIP; + port= psip_conf_nr; + pcp++; + psip_conf_nr++; +#endif + } else { + printf("inet: Unknown device '%s'\n", word); + error(); + } + iftype[ifno]= type; + icp->ic_ifno= ifno; + icp->ic_devtype= type; + icp->ic_port= port; + + token(0); + if (word[0] == '{') { + token(0); + if (strcmp(word, "default") == 0) { + if (ifdefault != -1) { + printf( + "inet: ip%d and ip%d can't both be default\n", + ifdefault, ifno); + error(); + } + ifdefault= ifno; + token(0); + } + if (word[0] == ';') token(0); + if (word[0] != '}') error(); + token(0); + } + if (word[0] != ';' && word[0] != 0) error(); + icp++; + ip_conf_nr++; + } + + if (ifdefault == -1) { + printf("inet: No networks or no default network defined\n"); + exit(1); + } + + /* Set umask 0 so we can creat mode 666 devices. */ + (void) umask(0); + + /* See what the device number of /dev/ip is. That's what we + * used last time for the network devices, so we keep doing so. + */ + if (stat("/dev/ip", &st) < 0) fatal("/dev/ip"); + ip_dev= st.st_rdev; + + for (i= 0; i < IP_PORT_MAX; i++) { + /* Create network devices. */ + check_dev(iftype[i], i); + } +} + +void *alloc(size_t size) +{ + /* Allocate memory on the heap with sbrk(). */ + + return sbrk((size + (sizeof(char *) - 1)) & ~(sizeof(char *) - 1)); +} + +/* + * $PchId: inet_config.c,v 1.6 1998/10/23 20:15:27 philip Exp $ + */ diff --git a/servers/inet/inet_config.h b/servers/inet/inet_config.h new file mode 100644 index 000000000..3f0ba6e7f --- /dev/null +++ b/servers/inet/inet_config.h @@ -0,0 +1,75 @@ +/* +inet/inet_config.h + +Created: Nov 11, 1992 by Philip Homburg + +Defines values for configurable parameters. The structure definitions for +configuration information are also here. + +Copyright 1995 Philip Homburg +*/ + +#ifndef INET__INET_CONFIG_H +#define INET__INET_CONFIG_H + +#define ENABLE_ARP 1 +#define ENABLE_IP 1 +#define ENABLE_PSIP 1 +#define ENABLE_TCP 1 +#define ENABLE_UDP 1 + +/* Inet configuration file. */ +#define PATH_INET_CONF "/etc/inet.conf" + +#define IP_PORT_MAX (1*sizeof(char*)) /* Up to this many network devices */ +extern int eth_conf_nr; /* Number of ethernets */ +extern int psip_conf_nr; /* Number of Pseudo IP networks */ +extern int ip_conf_nr; /* Number of configured TCP/IP layers */ + +extern dev_t ip_dev; /* Device number of /dev/ip */ + +struct eth_conf +{ + char *ec_task; /* Kernel ethernet task name */ + u8_t ec_port; /* Task port */ + u8_t ec_ifno; /* Interface number of /dev/eth* */ +}; + +struct psip_conf +{ + u8_t pc_ifno; /* Interface number of /dev/psip* */ +}; + +struct ip_conf +{ + u8_t ic_devtype; /* Underlying device type: Ethernet / PSIP */ + u8_t ic_port; /* Port of underlying device */ + u8_t ic_ifno; /* Interface number of /dev/ip*, tcp*, udp* */ +}; + +/* Types of networks. */ +#define NETTYPE_ETH 1 +#define NETTYPE_PSIP 2 + +/* To compute the minor device number for a device on an interface. */ +#define if2minor(ifno, dev) ((ifno) * 8 + (dev)) + +/* Offsets of the minor device numbers within a group per interface. */ +#define ETH_DEV_OFF 0 +#define PSIP_DEV_OFF 0 +#define IP_DEV_OFF 1 +#define TCP_DEV_OFF 2 +#define UDP_DEV_OFF 3 + +extern struct eth_conf eth_conf[IP_PORT_MAX]; +extern struct psip_conf psip_conf[IP_PORT_MAX]; +extern struct ip_conf ip_conf[IP_PORT_MAX]; +void read_conf(void); +extern char *sbrk(int); +void *alloc(size_t size); + +#endif /* INET__INET_CONFIG_H */ + +/* + * $PchId: inet_config.h,v 1.6 1998/10/23 20:14:28 philip Exp $ + */ diff --git a/servers/inet/mnx_eth.c b/servers/inet/mnx_eth.c new file mode 100644 index 000000000..76663fc26 --- /dev/null +++ b/servers/inet/mnx_eth.c @@ -0,0 +1,570 @@ +/* +inet/mnx_eth.c + +Created: Jan 2, 1992 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "proto.h" +#include "osdep_eth.h" +#include "generic/type.h" + +#include "generic/assert.h" +#include "generic/buf.h" +#include "generic/clock.h" +#include "generic/eth.h" +#include "generic/eth_int.h" +#include "generic/sr.h" + +#include <minix/syslib.h> + +THIS_FILE + +FORWARD _PROTOTYPE( void setup_read, (eth_port_t *eth_port) ); +FORWARD _PROTOTYPE( void read_int, (eth_port_t *eth_port, int count) ); +FORWARD _PROTOTYPE( void write_int, (eth_port_t *eth_port) ); +FORWARD _PROTOTYPE( void eth_recvev, (event_t *ev, ev_arg_t ev_arg) ); +FORWARD _PROTOTYPE( void eth_sendev, (event_t *ev, ev_arg_t ev_arg) ); +FORWARD _PROTOTYPE( eth_port_t *find_port, (message *m) ); + +PUBLIC void osdep_eth_init() +{ + int i, r, tasknr; + struct eth_conf *ecp; + eth_port_t *eth_port; + message mess, repl_mess; + + for (i= 0, eth_port= eth_port_table, ecp= eth_conf; + i<eth_conf_nr; i++, eth_port++, ecp++) + { + printf("INET: sys_getprocnr() for %s\n", ecp->ec_task); + r = sys_getprocnr(&tasknr, ecp->ec_task, strlen(ecp->ec_task)); + if (r != OK) + { + ip_panic(( "unable to find task %s: %d\n", + ecp->ec_task, r )); + } + + + eth_port->etp_osdep.etp_port= ecp->ec_port; + eth_port->etp_osdep.etp_task= tasknr; + ev_init(ð_port->etp_osdep.etp_recvev); + + mess.m_type= DL_INIT; + mess.DL_PORT= eth_port->etp_osdep.etp_port; + mess.DL_PROC= this_proc; + mess.DL_MODE= DL_NOMODE; + + r= send(eth_port->etp_osdep.etp_task, &mess); + if (r<0) + { +#if !CRAMPED + printf( + "osdep_eth_init: unable to send to ethernet task, error= %d\n", + r); +#endif + continue; + } + + if (receive(eth_port->etp_osdep.etp_task, &mess)<0) + ip_panic(("unable to receive")); + + if (mess.m3_i1 == ENXIO) + { +#if !CRAMPED + printf( + "osdep_eth_init: no ethernet device at task=%d,port=%d\n", + eth_port->etp_osdep.etp_task, + eth_port->etp_osdep.etp_port); +#endif + continue; + } + if (mess.m3_i1 != eth_port->etp_osdep.etp_port) + ip_panic(("osdep_eth_init: DL_INIT error or wrong port: %d\n", + mess.m3_i1)); + + eth_port->etp_ethaddr= *(ether_addr_t *)mess.m3_ca1; + + sr_add_minor(if2minor(ecp->ec_ifno, ETH_DEV_OFF), + i, eth_open, eth_close, eth_read, + eth_write, eth_ioctl, eth_cancel); + + eth_port->etp_flags |= EPF_ENABLED; + eth_port->etp_wr_pack= 0; + eth_port->etp_rd_pack= 0; + setup_read (eth_port); + eth_port++; + } +} + +PUBLIC void eth_write_port(eth_port, pack) +eth_port_t *eth_port; +acc_t *pack; +{ + eth_port_t *loc_port; + message mess1, block_msg; + int i, pack_size; + acc_t *pack_ptr; + iovec_t *iovec; + u8_t *eth_dst_ptr; + int multicast, r; + ev_arg_t ev_arg; + + assert(eth_port->etp_wr_pack == NULL); + eth_port->etp_wr_pack= pack; + + iovec= eth_port->etp_osdep.etp_wr_iovec; + pack_size= 0; + for (i=0, pack_ptr= pack; i<IOVEC_NR && pack_ptr; i++, + pack_ptr= pack_ptr->acc_next) + { + iovec[i].iov_addr= (vir_bytes)ptr2acc_data(pack_ptr); + pack_size += iovec[i].iov_size= pack_ptr->acc_length; + } + if (i>= IOVEC_NR) + { + pack= bf_pack(pack); /* packet is too fragmented */ + eth_port->etp_wr_pack= pack; + pack_size= 0; + for (i=0, pack_ptr= pack; i<IOVEC_NR && pack_ptr; + i++, pack_ptr= pack_ptr->acc_next) + { + iovec[i].iov_addr= (vir_bytes)ptr2acc_data(pack_ptr); + pack_size += iovec[i].iov_size= pack_ptr->acc_length; + } + } + assert (i< IOVEC_NR); + assert (pack_size >= ETH_MIN_PACK_SIZE); + + if (i == 1) + { + /* simple packets can be sent using DL_WRITE instead of + * DL_WRITEV. + */ + mess1.DL_COUNT= iovec[0].iov_size; + mess1.DL_ADDR= (char *)iovec[0].iov_addr; + mess1.m_type= DL_WRITE; + } + else + { + mess1.DL_COUNT= i; + mess1.DL_ADDR= (char *)iovec; + mess1.m_type= DL_WRITEV; + } + mess1.DL_PORT= eth_port->etp_osdep.etp_port; + mess1.DL_PROC= this_proc; + mess1.DL_MODE= DL_NOMODE; + + for (;;) + { + r= send (eth_port->etp_osdep.etp_task, &mess1); + if (r != ELOCKED) + break; + + /* ethernet task is sending to this task, I hope */ + r= receive(eth_port->etp_osdep.etp_task, &block_msg); + if (r < 0) + ip_panic(("unable to receive")); + + loc_port= eth_port; + if (loc_port->etp_osdep.etp_port != block_msg.DL_PORT) + { + loc_port= find_port(&block_msg); + } + assert(block_msg.DL_STAT & (DL_PACK_SEND|DL_PACK_RECV)); + if (block_msg.DL_STAT & DL_PACK_SEND) + { + assert(loc_port != eth_port); + loc_port->etp_osdep.etp_sendrepl= block_msg; + ev_arg.ev_ptr= loc_port; + ev_enqueue(&loc_port->etp_sendev, eth_sendev, ev_arg); + } + if (block_msg.DL_STAT & DL_PACK_RECV) + { + loc_port->etp_osdep.etp_recvrepl= block_msg; + ev_arg.ev_ptr= loc_port; + ev_enqueue(&loc_port->etp_osdep.etp_recvev, + eth_recvev, ev_arg); + } + } + + if (r < 0) + ip_panic(("unable to send")); + + r= receive(eth_port->etp_osdep.etp_task, &mess1); + if (r < 0) + ip_panic(("unable to receive")); + + assert(mess1.m_type == DL_TASK_REPLY && + mess1.DL_PORT == mess1.DL_PORT && + mess1.DL_PROC == this_proc); + assert((mess1.DL_STAT >> 16) == OK); + + if (mess1.DL_STAT & DL_PACK_RECV) + { + eth_port->etp_osdep.etp_recvrepl= mess1; + ev_arg.ev_ptr= eth_port; + ev_enqueue(ð_port->etp_osdep.etp_recvev, eth_recvev, + ev_arg); + } + if (!(mess1.DL_STAT & DL_PACK_SEND)) + { + /* Packet is not yet sent. */ + return; + } + + /* If the port is in promiscuous mode or the packet is + * broadcasted/multicasted, enqueue the reply packet. + */ + eth_dst_ptr= (u8_t *)ptr2acc_data(pack); + multicast= (*eth_dst_ptr & 1); /* low order bit indicates multicast */ + if (multicast || (eth_port->etp_osdep.etp_recvconf & NWEO_EN_PROMISC)) + { + eth_port->etp_osdep.etp_sendrepl= mess1; + ev_arg.ev_ptr= eth_port; + ev_enqueue(ð_port->etp_sendev, eth_sendev, ev_arg); + + /* Pretend that we didn't get a reply. */ + return; + } + + /* packet is sent */ + bf_afree(eth_port->etp_wr_pack); + eth_port->etp_wr_pack= NULL; +} + +PUBLIC void eth_rec(m) +message *m; +{ + int i; + eth_port_t *loc_port; + int stat; + + assert(m->m_type == DL_TASK_REPLY); + + set_time (m->DL_CLCK); + + for (i=0, loc_port= eth_port_table; i<eth_conf_nr; i++, loc_port++) + { + if (loc_port->etp_osdep.etp_port == m->DL_PORT && + loc_port->etp_osdep.etp_task == m->m_source) + break; + } + if (i == eth_conf_nr) + { + ip_panic(("message from unknown source: %d:%d", + m->m_source, m->DL_PORT)); + } + + stat= m->DL_STAT & 0xffff; + + assert(stat & (DL_PACK_SEND|DL_PACK_RECV)); + if (stat & DL_PACK_SEND) + write_int(loc_port); + if (stat & DL_PACK_RECV) + read_int(loc_port, m->DL_COUNT); +} + +#ifndef notdef +PUBLIC int eth_get_stat(eth_port, eth_stat) +eth_port_t *eth_port; +eth_stat_t *eth_stat; +{ + acc_t *acc; + int result; + message mess, mlocked; + + mess.m_type= DL_GETSTAT; + mess.DL_PORT= eth_port->etp_osdep.etp_port; + mess.DL_PROC= this_proc; + mess.DL_ADDR= (char *)eth_stat; + + for (;;) + { + result= send(eth_port->etp_osdep.etp_task, &mess); + if (result != ELOCKED) + break; + result= receive(eth_port->etp_osdep.etp_task, &mlocked); + assert(result == OK); + + compare(mlocked.m_type, ==, DL_TASK_REPLY); + eth_rec(&mlocked); + } + assert(result == OK); + + result= receive(eth_port->etp_osdep.etp_task, &mess); + assert(result == OK); + assert(mess.m_type == DL_TASK_REPLY); + + result= mess.DL_STAT >> 16; +assert (result == 0); + + if (mess.DL_STAT) + { +#if DEBUG + { where(); printf("calling eth_rec()\n"); } +#endif + eth_rec(&mess); + } + return OK; +} +#endif + +#ifndef notdef +PUBLIC void eth_set_rec_conf (eth_port, flags) +eth_port_t *eth_port; +u32_t flags; +{ + int result; + unsigned dl_flags; + message mess, repl_mess; + + dl_flags= DL_NOMODE; + if (flags & NWEO_EN_BROAD) + dl_flags |= DL_BROAD_REQ; + if (flags & NWEO_EN_MULTI) + dl_flags |= DL_MULTI_REQ; + if (flags & NWEO_EN_PROMISC) + dl_flags |= DL_PROMISC_REQ; + + mess.m_type= DL_INIT; + mess.DL_PORT= eth_port->etp_osdep.etp_port; + mess.DL_PROC= this_proc; + mess.DL_MODE= dl_flags; + + do + { + result= send (eth_port->etp_osdep.etp_task, &mess); + if (result == ELOCKED) + /* Ethernet task is sending to this task, I hope */ + { + if (receive (eth_port->etp_osdep.etp_task, + &repl_mess)< 0) + { + ip_panic(("unable to receive")); + } + + compare(repl_mess.m_type, ==, DL_TASK_REPLY); + eth_rec(&repl_mess); + } + } while (result == ELOCKED); + + if (result < 0) + ip_panic(("unable to send(%d)", result)); + + if (receive (eth_port->etp_osdep.etp_task, &repl_mess) < 0) + ip_panic(("unable to receive")); + + assert (repl_mess.m_type == DL_INIT_REPLY); + if (repl_mess.m3_i1 != eth_port->etp_osdep.etp_port) + { + ip_panic(("got reply for wrong port")); + } + eth_port->etp_osdep.etp_recvconf= flags; +} +#endif + +PRIVATE void write_int(eth_port) +eth_port_t *eth_port; +{ + acc_t *pack; + int multicast; + u8_t *eth_dst_ptr; + + pack= eth_port->etp_wr_pack; + eth_port->etp_wr_pack= NULL; + + eth_dst_ptr= (u8_t *)ptr2acc_data(pack); + multicast= (*eth_dst_ptr & 1); /* low order bit indicates multicast */ + if (multicast || (eth_port->etp_osdep.etp_recvconf & NWEO_EN_PROMISC)) + eth_arrive(eth_port, pack, bf_bufsize(pack)); + else + bf_afree(pack); + + eth_restart_write(eth_port); +} + +PRIVATE void read_int(eth_port, count) +eth_port_t *eth_port; +int count; +{ + acc_t *pack, *cut_pack; + + pack= eth_port->etp_rd_pack; + eth_port->etp_rd_pack= NULL; + + cut_pack= bf_cut(pack, 0, count); + bf_afree(pack); + + eth_arrive(eth_port, cut_pack, count); + + eth_port->etp_flags &= ~(EPF_READ_IP|EPF_READ_SP); + setup_read(eth_port); +} + +PRIVATE void setup_read(eth_port) +eth_port_t *eth_port; +{ + eth_port_t *loc_port; + acc_t *pack, *pack_ptr; + message mess1, block_msg; + iovec_t *iovec; + ev_arg_t ev_arg; + int i, r; + + assert(!(eth_port->etp_flags & (EPF_READ_IP|EPF_READ_SP))); + + do + { + assert (!eth_port->etp_rd_pack); + + iovec= eth_port->etp_osdep.etp_rd_iovec; + pack= bf_memreq (ETH_MAX_PACK_SIZE); + + for (i=0, pack_ptr= pack; i<RD_IOVEC && pack_ptr; + i++, pack_ptr= pack_ptr->acc_next) + { + iovec[i].iov_addr= (vir_bytes)ptr2acc_data(pack_ptr); + iovec[i].iov_size= (vir_bytes)pack_ptr->acc_length; + } + assert (!pack_ptr); + + mess1.m_type= DL_READV; + mess1.DL_PORT= eth_port->etp_osdep.etp_port; + mess1.DL_PROC= this_proc; + mess1.DL_COUNT= i; + mess1.DL_ADDR= (char *)iovec; + + for (;;) + { + r= send (eth_port->etp_osdep.etp_task, &mess1); + if (r != ELOCKED) + break; + + /* ethernet task is sending to this task, I hope */ + r= receive(eth_port->etp_osdep.etp_task, &block_msg); + if (r < 0) + ip_panic(("unable to receive")); + + loc_port= eth_port; + if (loc_port->etp_osdep.etp_port != block_msg.DL_PORT) + { + loc_port= find_port(&block_msg); + } + assert(block_msg.DL_STAT & + (DL_PACK_SEND|DL_PACK_RECV)); + if (block_msg.DL_STAT & DL_PACK_SEND) + { + loc_port->etp_osdep.etp_sendrepl= block_msg; + ev_arg.ev_ptr= loc_port; + ev_enqueue(&loc_port->etp_sendev, eth_sendev, + ev_arg); + } + if (block_msg.DL_STAT & DL_PACK_RECV) + { + assert(loc_port != eth_port); + loc_port->etp_osdep.etp_recvrepl= block_msg; + ev_arg.ev_ptr= loc_port; + ev_enqueue(&loc_port->etp_osdep.etp_recvev, + eth_recvev, ev_arg); + } + } + + if (r < 0) + ip_panic(("unable to send")); + + r= receive (eth_port->etp_osdep.etp_task, &mess1); + if (r < 0) + ip_panic(("unable to receive")); + + assert (mess1.m_type == DL_TASK_REPLY && + mess1.DL_PORT == mess1.DL_PORT && + mess1.DL_PROC == this_proc); + compare((mess1.DL_STAT >> 16), ==, OK); + + if (mess1.DL_STAT & DL_PACK_RECV) + { + /* packet received */ + pack_ptr= bf_cut(pack, 0, mess1.DL_COUNT); + bf_afree(pack); + + eth_arrive(eth_port, pack_ptr, mess1.DL_COUNT); + } + else + { + /* no packet received */ + eth_port->etp_rd_pack= pack; + eth_port->etp_flags |= EPF_READ_IP; + } + + if (mess1.DL_STAT & DL_PACK_SEND) + { + eth_port->etp_osdep.etp_sendrepl= mess1; + ev_arg.ev_ptr= eth_port; + ev_enqueue(ð_port->etp_sendev, eth_sendev, ev_arg); + } + } while (!(eth_port->etp_flags & EPF_READ_IP)); + eth_port->etp_flags |= EPF_READ_SP; +} + +PRIVATE void eth_recvev(ev, ev_arg) +event_t *ev; +ev_arg_t ev_arg; +{ + eth_port_t *eth_port; + message *m_ptr; + + eth_port= ev_arg.ev_ptr; + assert(ev == ð_port->etp_osdep.etp_recvev); + m_ptr= ð_port->etp_osdep.etp_recvrepl; + + assert(m_ptr->m_type == DL_TASK_REPLY); + assert(eth_port->etp_osdep.etp_port == m_ptr->DL_PORT); + + assert(m_ptr->DL_STAT & DL_PACK_RECV); + m_ptr->DL_STAT &= ~DL_PACK_RECV; + + read_int(eth_port, m_ptr->DL_COUNT); +} + +PRIVATE void eth_sendev(ev, ev_arg) +event_t *ev; +ev_arg_t ev_arg; +{ + eth_port_t *eth_port; + message *m_ptr; + + eth_port= ev_arg.ev_ptr; + assert(ev == ð_port->etp_sendev); + m_ptr= ð_port->etp_osdep.etp_sendrepl; + + assert (m_ptr->m_type == DL_TASK_REPLY); + assert(eth_port->etp_osdep.etp_port == m_ptr->DL_PORT); + + assert(m_ptr->DL_STAT & DL_PACK_SEND); + m_ptr->DL_STAT &= ~DL_PACK_SEND; + + /* packet is sent */ + write_int(eth_port); +} + +PRIVATE eth_port_t *find_port(m) +message *m; +{ + eth_port_t *loc_port; + int i; + + for (i=0, loc_port= eth_port_table; i<eth_conf_nr; i++, loc_port++) + { + if (loc_port->etp_osdep.etp_port == m->DL_PORT) + break; + } + assert (i<eth_conf_nr); + return loc_port; +} + +/* + * $PchId: mnx_eth.c,v 1.8 1995/11/21 06:41:57 philip Exp $ + */ diff --git a/servers/inet/mq.c b/servers/inet/mq.c new file mode 100644 index 000000000..836edf364 --- /dev/null +++ b/servers/inet/mq.c @@ -0,0 +1,58 @@ +/* +inet/mq.c + +Created: Jan 3, 1992 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" +#include "mq.h" +#include "generic/assert.h" + +THIS_FILE + +#define MQ_SIZE 128 + +PRIVATE mq_t mq_list[MQ_SIZE]; +PRIVATE mq_t *mq_freelist; + +void mq_init() +{ + int i; + + mq_freelist= NULL; + for (i= 0; i<MQ_SIZE; i++) + { + mq_list[i].mq_next= mq_freelist; + mq_freelist= &mq_list[i]; + mq_list[i].mq_allocated= 0; + } +} + +mq_t *mq_get() +{ + mq_t *mq; + + mq= mq_freelist; + assert(mq != NULL); + + mq_freelist= mq->mq_next; + mq->mq_next= NULL; + assert(mq->mq_allocated == 0); + mq->mq_allocated= 1; + return mq; +} + +void mq_free(mq) +mq_t *mq; +{ + mq->mq_next= mq_freelist; + mq_freelist= mq; + assert(mq->mq_allocated == 1); + mq->mq_allocated= 0; +} + +/* + * $PchId: mq.c,v 1.6 1996/05/07 21:10:16 philip Exp $ + */ diff --git a/servers/inet/mq.h b/servers/inet/mq.h new file mode 100644 index 000000000..28423688b --- /dev/null +++ b/servers/inet/mq.h @@ -0,0 +1,27 @@ +/* +inet/mq.h + +Created: Jan 3, 1992 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +#ifndef INET__MQ_H +#define INET__MQ_H + +typedef struct mq +{ + message mq_mess; + struct mq *mq_next; + int mq_allocated; +} mq_t; + +_PROTOTYPE( mq_t *mq_get, (void) ); +_PROTOTYPE( void mq_free, (mq_t *mq) ); +_PROTOTYPE( void mq_init, (void) ); + +#endif /* INET__MQ_H */ + +/* + * $PchId: mq.h,v 1.4 1995/11/21 06:40:30 philip Exp $ + */ diff --git a/servers/inet/osdep_eth.h b/servers/inet/osdep_eth.h new file mode 100644 index 000000000..11caca691 --- /dev/null +++ b/servers/inet/osdep_eth.h @@ -0,0 +1,33 @@ +/* +inet/osdep_eth.h + +Created: Dec 30, 1991 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +#ifndef INET__OSDEP_ETH_H +#define INET__OSDEP_ETH_H + +#include "generic/event.h" + +#define IOVEC_NR 16 +#define RD_IOVEC ((ETH_MAX_PACK_SIZE + BUF_S -1)/BUF_S) + +typedef struct osdep_eth_port +{ + int etp_task; + int etp_port; + int etp_recvconf; + iovec_t etp_wr_iovec[IOVEC_NR]; + iovec_t etp_rd_iovec[RD_IOVEC]; + event_t etp_recvev; + message etp_sendrepl; + message etp_recvrepl; +} osdep_eth_port_t; + +#endif /* INET__OSDEP_ETH_H */ + +/* + * $PchId: osdep_eth.h,v 1.5 1995/11/21 06:41:28 philip Exp $ + */ diff --git a/servers/inet/proto.h b/servers/inet/proto.h new file mode 100644 index 000000000..be2977a78 --- /dev/null +++ b/servers/inet/proto.h @@ -0,0 +1,27 @@ +/* +inet/proto.h + +Created: Jan 2, 1992 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +/* clock.c */ + +_PROTOTYPE( void clck_tick, (message *mess) ); + +/* mnx_eth.c */ + +_PROTOTYPE( void eth_rec, (message *m) ); + +/* sr.c */ + +struct mq; +_PROTOTYPE( void sr_rec, (struct mq *m) ); + + + + +/* + * $PchId: proto.h,v 1.4 1995/11/21 06:36:37 philip Exp $ + */ diff --git a/servers/inet/sr.c b/servers/inet/sr.c new file mode 100644 index 000000000..2f809cc91 --- /dev/null +++ b/servers/inet/sr.c @@ -0,0 +1,774 @@ +/* this file contains the interface of the network software with the file + * system. + * + * Copyright 1995 Philip Homburg + * + * The valid messages and their parameters are: + * + * Requests: + * + * m_type NDEV_MINOR NDEV_PROC NDEV_REF NDEV_MODE + * ------------------------------------------------------------- + * | DEV_OPEN |minor dev | proc nr | fd | mode | + * |-------------+-----------+-----------+-----------+----------+ + * | DEV_CLOSE |minor dev | proc nr | fd | | + * |-------------+-----------+-----------+-----------+----------+ + * + * m_type NDEV_MINOR NDEV_PROC NDEV_REF NDEV_COUNT NDEV_BUFFER + * --------------------------------------------------------------------------- + * | DEV_READ |minor dev | proc nr | fd | count | buf ptr | + * |-------------+-----------+-----------+-----------+-----------+-----------| + * | DEV_WRITE |minor dev | proc nr | fd | count | buf ptr | + * |-------------+-----------+-----------+-----------+-----------+-----------| + * + * m_type NDEV_MINOR NDEV_PROC NDEV_REF NDEV_IOCTL NDEV_BUFFER + * --------------------------------------------------------------------------- + * | DEV_IOCTL3 |minor dev | proc nr | fd | command | buf ptr | + * |-------------+-----------+-----------+-----------+-----------+-----------| + * + * m_type NDEV_MINOR NDEV_PROC NDEV_REF NDEV_OPERATION + * -------------------------------------------------------------------| + * | DEV_CANCEL |minor dev | proc nr | fd | which operation| + * |-------------+-----------+-----------+-----------+----------------| + * + * Replies: + * + * m_type REP_PROC_NR REP_STATUS REP_REF REP_OPERATION + * ----------------------------------------------------------------------| + * | DEVICE_REPLY | proc nr | status | fd | which operation | + * |--------------+-------------+------------+---------+-----------------| + */ + +#include "inet.h" + +#include <minix/callnr.h> + +#include "mq.h" +#include "proto.h" +#include "generic/type.h" + +#include "generic/assert.h" +#include "generic/buf.h" +#include "generic/sr.h" + +THIS_FILE + +#define FD_NR (16*IP_PORT_MAX) + +typedef struct sr_fd +{ + int srf_flags; + int srf_fd; + int srf_port; + sr_open_t srf_open; + sr_close_t srf_close; + sr_write_t srf_write; + sr_read_t srf_read; + sr_ioctl_t srf_ioctl; + sr_cancel_t srf_cancel; + mq_t *srf_ioctl_q, *srf_ioctl_q_tail; + mq_t *srf_read_q, *srf_read_q_tail; + mq_t *srf_write_q, *srf_write_q_tail; +} sr_fd_t; + +#define SFF_FLAGS 0x0F +# define SFF_FREE 0x00 +# define SFF_MINOR 0x01 +# define SFF_INUSE 0x02 +# define SFF_BUSY 0x3C +# define SFF_IOCTL_IP 0x04 +# define SFF_READ_IP 0x08 +# define SFF_WRITE_IP 0x10 +# define SFF_PENDING_REQ 0x30 +# define SFF_SUSPENDED 0x1C0 +# define SFF_IOCTL_SUSP 0x40 +# define SFF_READ_SUSP 0x80 +# define SFF_WRITE_SUSP 0x100 + +FORWARD _PROTOTYPE ( int sr_open, (message *m) ); +FORWARD _PROTOTYPE ( void sr_close, (message *m) ); +FORWARD _PROTOTYPE ( int sr_rwio, (mq_t *m) ); +FORWARD _PROTOTYPE ( int sr_cancel, (message *m) ); +FORWARD _PROTOTYPE ( void sr_reply, (mq_t *m, int reply, int can_enqueue) ); +FORWARD _PROTOTYPE ( sr_fd_t *sr_getchannel, (int minor)); +FORWARD _PROTOTYPE ( acc_t *sr_get_userdata, (int fd, vir_bytes offset, + vir_bytes count, int for_ioctl) ); +FORWARD _PROTOTYPE ( int sr_put_userdata, (int fd, vir_bytes offset, + acc_t *data, int for_ioctl) ); +FORWARD _PROTOTYPE ( int sr_repl_queue, (int proc, int ref, int operation) ); +FORWARD _PROTOTYPE ( int walk_queue, (sr_fd_t *sr_fd, mq_t *q_head, + mq_t **q_tail_ptr, int type, int proc_nr, int ref) ); +FORWARD _PROTOTYPE ( void process_req_q, (mq_t *mq, mq_t *tail, + mq_t **tail_ptr) ); +FORWARD _PROTOTYPE ( int cp_u2b, (int proc, char *src, acc_t **var_acc_ptr, + int size) ); +FORWARD _PROTOTYPE ( int cp_b2u, (acc_t *acc_ptr, int proc, char *dest) ); + +PRIVATE sr_fd_t sr_fd_table[FD_NR]; +PRIVATE mq_t *repl_queue, *repl_queue_tail; +PRIVATE cpvec_t cpvec[CPVEC_NR]; + +PUBLIC void sr_init() +{ +#if ZERO + int i; + + for (i=0; i<FD_NR; i++) + sr_fd_table[i].srf_flags= SFF_FREE; + repl_queue= NULL; +#endif +} + +PUBLIC void sr_rec(m) +mq_t *m; +{ + int result; + int send_reply, free_mess; + + if (repl_queue) + { + if (m->mq_mess.m_type == NW_CANCEL) + { + result= sr_repl_queue(m->mq_mess.PROC_NR, 0, 0); + if (result) + { + mq_free(m); + return; /* canceled request in queue */ + } + } + else + sr_repl_queue(ANY, 0, 0); + } + + switch (m->mq_mess.m_type) + { + case DEV_OPEN: + result= sr_open(&m->mq_mess); + send_reply= 1; + free_mess= 1; + break; + case DEV_CLOSE: + sr_close(&m->mq_mess); + result= OK; + send_reply= 1; + free_mess= 1; + break; + case DEV_READ: + case DEV_WRITE: + case DEV_IOCTL: + result= sr_rwio(m); + assert(result == OK || result == SUSPEND); + send_reply= (result == SUSPEND); + free_mess= 0; + break; + case CANCEL: + result= sr_cancel(&m->mq_mess); + assert(result == OK || result == EINTR); + send_reply= (result == EINTR); + free_mess= 1; + m->mq_mess.m_type= 0; + break; +#if !CRAMPED + default: + ip_panic(("unknown message, from %d, type %d", + m->mq_mess.m_source, m->mq_mess.m_type)); +#endif + } + if (send_reply) + { + sr_reply(m, result, FALSE); + } + if (free_mess) + mq_free(m); +} + +PUBLIC void sr_add_minor(minor, port, openf, closef, readf, writef, + ioctlf, cancelf) +int minor; +int port; +sr_open_t openf; +sr_close_t closef; +sr_read_t readf; +sr_write_t writef; +sr_ioctl_t ioctlf; +sr_cancel_t cancelf; +{ + sr_fd_t *sr_fd; + + assert (minor>=0 && minor<FD_NR); + + sr_fd= &sr_fd_table[minor]; + + assert(!(sr_fd->srf_flags & SFF_INUSE)); + + sr_fd->srf_flags= SFF_INUSE | SFF_MINOR; + sr_fd->srf_port= port; + sr_fd->srf_open= openf; + sr_fd->srf_close= closef; + sr_fd->srf_write= writef; + sr_fd->srf_read= readf; + sr_fd->srf_ioctl= ioctlf; + sr_fd->srf_cancel= cancelf; +} + +PRIVATE int sr_open(m) +message *m; +{ + sr_fd_t *sr_fd; + + int minor= m->DEVICE; + int i, fd; + + if (minor<0 || minor>FD_NR) + { + DBLOCK(1, printf("replying EINVAL\n")); + return EINVAL; + } + if (!(sr_fd_table[minor].srf_flags & SFF_MINOR)) + { + DBLOCK(1, printf("replying ENXIO\n")); + return ENXIO; + } + for (i=0; i<FD_NR && (sr_fd_table[i].srf_flags & SFF_INUSE); i++); + + if (i>=FD_NR) + { + DBLOCK(1, printf("replying ENFILE\n")); + return ENFILE; + } + + sr_fd= &sr_fd_table[i]; + *sr_fd= sr_fd_table[minor]; + sr_fd->srf_flags= SFF_INUSE; + fd= (*sr_fd->srf_open)(sr_fd->srf_port, i, sr_get_userdata, + sr_put_userdata, 0); + if (fd<0) + { + sr_fd->srf_flags= SFF_FREE; + DBLOCK(1, printf("replying %d\n", fd)); + return fd; + } + sr_fd->srf_fd= fd; + return i; +} + +PRIVATE void sr_close(m) +message *m; +{ + sr_fd_t *sr_fd; + + sr_fd= sr_getchannel(m->DEVICE); + assert (sr_fd); + + assert (!(sr_fd->srf_flags & SFF_BUSY)); + + assert (!(sr_fd->srf_flags & SFF_MINOR)); + (*sr_fd->srf_close)(sr_fd->srf_fd); + sr_fd->srf_flags= SFF_FREE; +} + +PRIVATE int sr_rwio(m) +mq_t *m; +{ + sr_fd_t *sr_fd; + mq_t **q_head_ptr, **q_tail_ptr; + int ip_flag, susp_flag; + int r; + ioreq_t request; + size_t size; + + sr_fd= sr_getchannel(m->mq_mess.DEVICE); + assert (sr_fd); + + switch(m->mq_mess.m_type) + { + case DEV_READ: + q_head_ptr= &sr_fd->srf_read_q; + q_tail_ptr= &sr_fd->srf_read_q_tail; + ip_flag= SFF_READ_IP; + susp_flag= SFF_READ_SUSP; + break; + case DEV_WRITE: + q_head_ptr= &sr_fd->srf_write_q; + q_tail_ptr= &sr_fd->srf_write_q_tail; + ip_flag= SFF_WRITE_IP; + susp_flag= SFF_WRITE_SUSP; + break; + case DEV_IOCTL: + q_head_ptr= &sr_fd->srf_ioctl_q; + q_tail_ptr= &sr_fd->srf_ioctl_q_tail; + ip_flag= SFF_IOCTL_IP; + susp_flag= SFF_IOCTL_SUSP; + break; +#if !CRAMPED + default: + ip_panic(("illegal case entry")); +#endif + } + + if (sr_fd->srf_flags & ip_flag) + { + assert(sr_fd->srf_flags & susp_flag); + assert(*q_head_ptr); + + (*q_tail_ptr)->mq_next= m; + *q_tail_ptr= m; + return SUSPEND; + } + assert(!*q_head_ptr); + + *q_tail_ptr= *q_head_ptr= m; + sr_fd->srf_flags |= ip_flag; + + switch(m->mq_mess.m_type) + { + case DEV_READ: + r= (*sr_fd->srf_read)(sr_fd->srf_fd, + m->mq_mess.COUNT); + break; + case DEV_WRITE: + r= (*sr_fd->srf_write)(sr_fd->srf_fd, + m->mq_mess.COUNT); + break; + case DEV_IOCTL: + request= m->mq_mess.REQUEST; +#ifdef _IOCPARM_MASK + size= (request >> 16) & _IOCPARM_MASK; + if (size>MAX_IOCTL_S) + { + DBLOCK(1, printf("replying EINVAL\n")); + r= sr_put_userdata(sr_fd-sr_fd_table, EINVAL, + NULL, 1); + assert(r == OK); + return OK; + } +#endif + r= (*sr_fd->srf_ioctl)(sr_fd->srf_fd, request); + break; +#if !CRAMPED + default: + ip_panic(("illegal case entry")); +#endif + } + + assert(r == OK || r == SUSPEND || + (printf("r= %d\n", r), 0)); + if (r == SUSPEND) + sr_fd->srf_flags |= susp_flag; + return r; +} + +PRIVATE int sr_cancel(m) +message *m; +{ + sr_fd_t *sr_fd; + int i, result; + mq_t *q_ptr, *q_ptr_prv; + int proc_nr, ref, operation; + + result=EINTR; + proc_nr= m->PROC_NR; + ref= 0; + operation= 0; + sr_fd= sr_getchannel(m->DEVICE); + assert (sr_fd); + + { + result= walk_queue(sr_fd, sr_fd->srf_ioctl_q, + &sr_fd->srf_ioctl_q_tail, SR_CANCEL_IOCTL, + proc_nr, ref); + if (result != EAGAIN) + return result; + } + { + result= walk_queue(sr_fd, sr_fd->srf_read_q, + &sr_fd->srf_read_q_tail, SR_CANCEL_READ, + proc_nr, ref); + if (result != EAGAIN) + return result; + } + { + result= walk_queue(sr_fd, sr_fd->srf_write_q, + &sr_fd->srf_write_q_tail, SR_CANCEL_WRITE, + proc_nr, ref); + if (result != EAGAIN) + return result; + } +#if !CRAMPED + ip_panic(( +"request not found: from %d, type %d, MINOR= %d, PROC= %d, REF= %d OPERATION= %d", + m->m_source, m->m_type, m->DEVICE, + m->PROC_NR, 0, 0)); +#endif +} + +PRIVATE int walk_queue(sr_fd, q_head, q_tail_ptr, type, proc_nr, ref) +sr_fd_t *sr_fd; +mq_t *q_head, **q_tail_ptr; +int type; +int proc_nr; +int ref; +{ + mq_t *q_ptr_prv, *q_ptr; + int result; + + for(q_ptr_prv= NULL, q_ptr= q_head; q_ptr; + q_ptr_prv= q_ptr, q_ptr= q_ptr->mq_next) + { + if (q_ptr->mq_mess.PROC_NR != proc_nr) + continue; + if (!q_ptr_prv) + { + result= (*sr_fd->srf_cancel)(sr_fd->srf_fd, type); + assert(result == OK); + return OK; + } + q_ptr_prv->mq_next= q_ptr->mq_next; + mq_free(q_ptr); + if (!q_ptr_prv->mq_next) + *q_tail_ptr= q_ptr_prv; + return EINTR; + } + return EAGAIN; +} + +PRIVATE sr_fd_t *sr_getchannel(minor) +int minor; +{ + sr_fd_t *loc_fd; + + compare(minor, >=, 0); + compare(minor, <, FD_NR); + + loc_fd= &sr_fd_table[minor]; + + assert (!(loc_fd->srf_flags & SFF_MINOR) && + (loc_fd->srf_flags & SFF_INUSE)); + + return loc_fd; +} + +PRIVATE void sr_reply (mq, status, can_enqueue) +mq_t *mq; +int status; +int can_enqueue; +{ + int result, proc, ref,operation; + message reply, *mp; + + proc= mq->mq_mess.PROC_NR; + ref= 0; + operation= mq->mq_mess.m_type; + + if (can_enqueue) + mp= &mq->mq_mess; + else + mp= &reply; + + mp->m_type= REVIVE; + mp->REP_PROC_NR= proc; + mp->REP_STATUS= status; + result= send(mq->mq_mess.m_source, mp); + if (result == ELOCKED && can_enqueue) + { + if (repl_queue) + repl_queue_tail->mq_next= mq; + else + repl_queue= mq; + repl_queue_tail= mq; + return; + } + if (result != OK) + ip_panic(("unable to send")); + if (can_enqueue) + mq_free(mq); +} + +PRIVATE acc_t *sr_get_userdata (fd, offset, count, for_ioctl) +int fd; +vir_bytes offset; +vir_bytes count; +int for_ioctl; +{ + sr_fd_t *loc_fd; + mq_t **head_ptr, **tail_ptr, *m, *tail, *mq; + int ip_flag, susp_flag; + int result; + int suspended; + char *src; + acc_t *acc; + + loc_fd= &sr_fd_table[fd]; + + if (for_ioctl) + { + head_ptr= &loc_fd->srf_ioctl_q; + tail_ptr= &loc_fd->srf_ioctl_q_tail; + ip_flag= SFF_IOCTL_IP; + susp_flag= SFF_IOCTL_SUSP; + } + else + { + head_ptr= &loc_fd->srf_write_q; + tail_ptr= &loc_fd->srf_write_q_tail; + ip_flag= SFF_WRITE_IP; + susp_flag= SFF_WRITE_SUSP; + } + +assert (loc_fd->srf_flags & ip_flag); + + if (!count) + { + m= *head_ptr; + *head_ptr= NULL; + tail= *tail_ptr; +assert(m); + mq= m->mq_next; + result= (int)offset; + sr_reply (m, result, 1); + suspended= (loc_fd->srf_flags & susp_flag); + loc_fd->srf_flags &= ~(ip_flag|susp_flag); + if (suspended) + { + process_req_q(mq, tail, tail_ptr); + } + else + { +assert(!mq); + } + return NULL; + } + + src= (*head_ptr)->mq_mess.ADDRESS + offset; + result= cp_u2b ((*head_ptr)->mq_mess.PROC_NR, src, &acc, count); + + return result<0 ? NULL : acc; +} + +PRIVATE int sr_put_userdata (fd, offset, data, for_ioctl) +int fd; +vir_bytes offset; +acc_t *data; +int for_ioctl; +{ + sr_fd_t *loc_fd; + mq_t **head_ptr, **tail_ptr, *m, *tail, *mq; + int ip_flag, susp_flag; + int result; + int suspended; + char *dst; + + loc_fd= &sr_fd_table[fd]; + + if (for_ioctl) + { + head_ptr= &loc_fd->srf_ioctl_q; + tail_ptr= &loc_fd->srf_ioctl_q_tail; + ip_flag= SFF_IOCTL_IP; + susp_flag= SFF_IOCTL_SUSP; + } + else + { + head_ptr= &loc_fd->srf_read_q; + tail_ptr= &loc_fd->srf_read_q_tail; + ip_flag= SFF_READ_IP; + susp_flag= SFF_READ_SUSP; + } + + assert (loc_fd->srf_flags & ip_flag); + + if (!data) + { + m= *head_ptr; + assert(m); + + *head_ptr= NULL; + tail= *tail_ptr; + mq= m->mq_next; + result= (int)offset; + sr_reply (m, result, 1); + suspended= (loc_fd->srf_flags & susp_flag); + loc_fd->srf_flags &= ~(ip_flag|susp_flag); + if (suspended) + { + process_req_q(mq, tail, tail_ptr); + } + else + { + assert(!mq); + } + return OK; + } + + dst= (*head_ptr)->mq_mess.ADDRESS + offset; + return cp_b2u (data, (*head_ptr)->mq_mess.PROC_NR, dst); +} + +PRIVATE void process_req_q(mq, tail, tail_ptr) +mq_t *mq, *tail, **tail_ptr; +{ + mq_t *m; + int result; + + for(;mq;) + { + m= mq; + mq= mq->mq_next; + + DBLOCK(1, printf("calling rwio\n")); + + result= sr_rwio(m); + if (result == SUSPEND) + { + if (mq) + { + (*tail_ptr)->mq_next= mq; + *tail_ptr= tail; + } + return; + } + } + return; +} + +PRIVATE int cp_u2b (proc, src, var_acc_ptr, size) +int proc; +char *src; +acc_t **var_acc_ptr; +int size; +{ + static message mess; + acc_t *acc; + int i; + + acc= bf_memreq(size); + + *var_acc_ptr= acc; + i=0; + + while (acc) + { + size= (vir_bytes)acc->acc_length; + + cpvec[i].cpv_src= (vir_bytes)src; + cpvec[i].cpv_dst= (vir_bytes)ptr2acc_data(acc); + cpvec[i].cpv_size= size; + + src += size; + acc= acc->acc_next; + i++; + + if (i == CPVEC_NR || acc == NULL) + { + mess.m_type= SYS_VCOPY; + mess.m1_i1= proc; + mess.m1_i2= this_proc; + mess.m1_i3= i; + mess.m1_p1= (char *)cpvec; + if (sendrec(SYSTASK, &mess) <0) + ip_panic(("unable to sendrec")); + if (mess.m_type <0) + { + bf_afree(*var_acc_ptr); + *var_acc_ptr= 0; + return mess.m_type; + } + i= 0; + } + } + return OK; +} + +PRIVATE int cp_b2u (acc_ptr, proc, dest) +acc_t *acc_ptr; +int proc; +char *dest; +{ + static message mess; + acc_t *acc; + int i, size; + + acc= acc_ptr; + i=0; + + while (acc) + { + size= (vir_bytes)acc->acc_length; + + if (size) + { + cpvec[i].cpv_src= (vir_bytes)ptr2acc_data(acc); + cpvec[i].cpv_dst= (vir_bytes)dest; + cpvec[i].cpv_size= size; + i++; + } + + dest += size; + acc= acc->acc_next; + + if (i == CPVEC_NR || acc == NULL) + { + mess.m_type= SYS_VCOPY; + mess.m1_i1= this_proc; + mess.m1_i2= proc; + mess.m1_i3= i; + mess.m1_p1= (char *)cpvec; + if (sendrec(SYSTASK, &mess) <0) + ip_panic(("unable to sendrec")); + if (mess.m_type <0) + { + bf_afree(acc_ptr); + return mess.m_type; + } + i= 0; + } + } + bf_afree(acc_ptr); + return OK; +} + +PRIVATE int sr_repl_queue(proc, ref, operation) +int proc; +int ref; +int operation; +{ + mq_t *m, *m_cancel, *m_tmp; + int result; + + m_cancel= NULL; + + for (m= repl_queue; m;) + { + if (m->mq_mess.REP_PROC_NR == proc) + { +assert(!m_cancel); + m_cancel= m; + m= m->mq_next; + continue; + } +assert(m->mq_mess.m_source != MM_PROC_NR); +assert(m->mq_mess.m_type == REVIVE); + result= send(m->mq_mess.m_source, &m->mq_mess); + if (result != OK) + ip_panic(("unable to send: %d", result)); + m_tmp= m; + m= m->mq_next; + mq_free(m_tmp); + } + repl_queue= NULL; + if (m_cancel) + { +assert(m_cancel->mq_mess.m_source != MM_PROC_NR); +assert(m_cancel->mq_mess.m_type == REVIVE); + result= send(m_cancel->mq_mess.m_source, &m_cancel->mq_mess); + if (result != OK) + ip_panic(("unable to send: %d", result)); + mq_free(m_cancel); + return 1; + } + return 0; +} + +/* + * $PchId: sr.c,v 1.9 1996/05/07 21:11:14 philip Exp $ + */ diff --git a/servers/inet/stacktrace.c b/servers/inet/stacktrace.c new file mode 100644 index 000000000..d4e8adf68 --- /dev/null +++ b/servers/inet/stacktrace.c @@ -0,0 +1,35 @@ +/* +stacktrace.c + +Created: Jan 19, 1993 by Philip Homburg + +Copyright 1995 Philip Homburg +*/ + +#include "inet.h" + +PUBLIC void stacktrace() +{ + typedef unsigned int reg_t; + reg_t bp, pc, hbp; + extern reg_t get_bp ARGS(( void )); + + bp= get_bp(); + while(bp) + { + pc= ((reg_t *)bp)[1]; + hbp= ((reg_t *)bp)[0]; + printf("0x%lx ", (unsigned long)pc); + if (hbp != 0 && hbp <= bp) + { + printf("???"); + break; + } + bp= hbp; + } + printf("\n"); +} + +/* + * $PchId: stacktrace.c,v 1.6 1996/05/07 21:11:34 philip Exp $ + */ diff --git a/servers/inet/version.c b/servers/inet/version.c new file mode 100644 index 000000000..951133d22 --- /dev/null +++ b/servers/inet/version.c @@ -0,0 +1,9 @@ +/* +version.c +*/ + +char version[]= "inet 0.35K, last compiled on " __DATE__ " " __TIME__; + +/* + * $PchId: version.c,v 1.9 1996/12/17 08:01:39 philip Exp philip $ + */ diff --git a/servers/init/Makefile b/servers/init/Makefile new file mode 100644 index 000000000..f3901b2d4 --- /dev/null +++ b/servers/init/Makefile @@ -0,0 +1,38 @@ +# Makefile for the init program (INIT) +SERVER = init + +# directories +u = /usr +i = $u/include +s = $i/sys +h = $i/minix +k = $u/src/kernel + +# programs, flags, etc. +CC = exec cc +CFLAGS = -I$i -O -D_MINIX -D_POSIX_SOURCE +LDFLAGS = -i + +OBJ = init.o + +# build local binary +all build: $(SERVER) +$(SERVER): $(OBJ) + $(CC) $(CFLAGS) -o $@ $(LDFLAGS) $(OBJ) -lutils + install -S 192w $@ + +# install with other servers +install: /usr/sbin/servers/$(SERVER) +/usr/sbin/servers/$(SERVER): $(SERVER) + install -o root -cs $? $@ + +# clean up local files +clean: + rm -f $(SERVER) *.o *.bak + +# dependencies +a = $s/types.h $s/wait.h $s/stat.h $s/svrctl.h \ + $i/ttyent.h $i/fcntl.h $i/unistd.h $i/time.h $i/stdlib.h \ + $i/limits.h $i/errno.h $i/signal.h $i/string.h $i/utmp.h + +init.o: $a diff --git a/servers/init/init.c b/servers/init/init.c new file mode 100644 index 000000000..a01c3a035 --- /dev/null +++ b/servers/init/init.c @@ -0,0 +1,461 @@ +/* This process is the father (mother) of all Minix user processes. When + * Minix comes up, this is process number 2, and has a pid of 1. It + * executes the /etc/rc shell file, and then reads the /etc/ttytab file to + * determine which terminals need a login process. + * + * If the files /usr/adm/wtmp and /etc/utmp exist and are writable, init + * (with help from login) will maintain login accounting. Sending a + * signal 1 (SIGHUP) to init will cause it to rescan /etc/ttytab and start + * up new shell processes if necessary. It will not, however, kill off + * login processes for lines that have been turned off; do this manually. + * Signal 15 (SIGTERM) makes init stop spawning new processes, this is + * used by shutdown and friends when they are about to close the system + * down. + */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/svrctl.h> +#include <ttyent.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <signal.h> +#include <string.h> +#include <time.h> +#include <stdlib.h> +#include <unistd.h> +#include <utmp.h> + +/* Command to execute as a response to the three finger salute. */ +char *REBOOT_CMD[] = { "shutdown", "now", "CTRL-ALT-DEL", NULL }; + +/* Associated fake ttytab entry. */ +struct ttyent TT_REBOOT = { "console", "-", REBOOT_CMD, NULL }; + +char PATH_UTMP[] = "/etc/utmp"; /* current logins */ +char PATH_WTMP[] = "/usr/adm/wtmp"; /* login/logout history */ + +#define PIDSLOTS 32 /* first this many ttys can be on */ + +struct slotent { + int errct; /* error count */ + pid_t pid; /* pid of login process for this tty line */ +}; + +#define ERRCT_DISABLE 10 /* disable after this many errors */ +#define NO_PID 0 /* pid value indicating no process */ + +struct slotent slots[PIDSLOTS]; /* init table of ttys and pids */ + +int gothup = 0; /* flag, showing signal 1 was received */ +int gotabrt = 0; /* flag, showing signal 6 was received */ +int spawn = 1; /* flag, spawn processes only when set */ + +void tell(int fd, char *s); +void report(int fd, char *label); +void wtmp(int type, int linenr, char *line, pid_t pid); +void startup(int linenr, struct ttyent *ttyp); +int execute(char **cmd); +void onhup(int sig); +void onterm(int sig); +void onabrt(int sig); + +int main(void) +{ + pid_t pid; /* pid of child process */ + int fd; /* generally useful */ + int linenr; /* loop variable */ + int check; /* check if a new process must be spawned */ + struct slotent *slotp; /* slots[] pointer */ + struct ttyent *ttyp; /* ttytab entry */ + struct sigaction sa; + struct stat stb; + + if (fstat(0, &stb) < 0) { + /* Open standard input, output & error. */ + (void) open("/dev/null", O_RDONLY); + (void) open("/dev/log", O_WRONLY); + dup(1); + } + + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + + /* Hangup: Reexamine /etc/ttytab for newly enabled terminal lines. */ + sa.sa_handler = onhup; + sigaction(SIGHUP, &sa, NULL); + + /* Terminate: Stop spawning login processes, shutdown is near. */ + sa.sa_handler = onterm; + sigaction(SIGTERM, &sa, NULL); + + /* Abort: Sent by the kernel on CTRL-ALT-DEL; shut the system down. */ + sa.sa_handler = onabrt; + sigaction(SIGABRT, &sa, NULL); + + /* Execute the /etc/rc file. */ + if ((pid = fork()) != 0) { + /* Parent just waits. */ + while (wait(NULL) != pid) { + if (gotabrt) reboot(RBT_HALT); + } + } else { +#if ! SYS_GETKENV + struct sysgetenv sysgetenv; +#endif + char bootopts[16]; + static char *rc_command[] = { "sh", "/etc/rc", NULL, NULL, NULL }; + char **rcp = rc_command + 2; + + /* Get the boot options from the boot environment. */ + sysgetenv.key = "bootopts"; + sysgetenv.keylen = 8+1; + sysgetenv.val = bootopts; + sysgetenv.vallen = sizeof(bootopts); + if (svrctl(MMGETPARAM, &sysgetenv) == 0) *rcp++ = bootopts; + *rcp = "start"; + + execute(rc_command); + report(2, "sh /etc/rc"); + _exit(1); /* impossible, we hope */ + } + + /* Clear /etc/utmp if it exists. */ + if ((fd = open(PATH_UTMP, O_WRONLY | O_TRUNC)) >= 0) close(fd); + + /* Log system reboot. */ + wtmp(BOOT_TIME, 0, NULL, 0); + + /* Main loop. If login processes have already been started up, wait for one + * to terminate, or for a HUP signal to arrive. Start up new login processes + * for all ttys which don't have them. Note that wait() also returns when + * somebody's orphan dies, in which case ignore it. If the TERM signal is + * sent then stop spawning processes, shutdown time is near. + */ + + check = 1; + while (1) { + while ((pid = waitpid(-1, NULL, check ? WNOHANG : 0)) > 0) { + /* Search to see which line terminated. */ + for (linenr = 0; linenr < PIDSLOTS; linenr++) { + slotp = &slots[linenr]; + if (slotp->pid == pid) { + /* Record process exiting. */ + wtmp(DEAD_PROCESS, linenr, NULL, pid); + slotp->pid = NO_PID; + check = 1; + } + } + } + + /* If a signal 1 (SIGHUP) is received, simply reset error counts. */ + if (gothup) { + gothup = 0; + for (linenr = 0; linenr < PIDSLOTS; linenr++) { + slots[linenr].errct = 0; + } + check = 1; + } + + /* Shut down on signal 6 (SIGABRT). */ + if (gotabrt) { + gotabrt = 0; + startup(0, &TT_REBOOT); + } + + if (spawn && check) { + /* See which lines need a login process started up. */ + for (linenr = 0; linenr < PIDSLOTS; linenr++) { + slotp = &slots[linenr]; + if ((ttyp = getttyent()) == NULL) break; + + if (ttyp->ty_getty != NULL + && ttyp->ty_getty[0] != NULL + && slotp->pid == NO_PID + && slotp->errct < ERRCT_DISABLE) + { + startup(linenr, ttyp); + } + } + endttyent(); + } + check = 0; + } +} + +void onhup(int sig) +{ + gothup = 1; + spawn = 1; +} + +void onterm(int sig) +{ + spawn = 0; +} + +void onabrt(int sig) +{ + static int count; + + if (++count == 2) reboot(RBT_HALT); + gotabrt = 1; +} + +void startup(int linenr, struct ttyent *ttyp) +{ + /* Fork off a process for the indicated line. */ + + struct slotent *slotp; /* pointer to ttyslot */ + pid_t pid; /* new pid */ + int err[2]; /* error reporting pipe */ + char line[32]; /* tty device name */ + int status; + + slotp = &slots[linenr]; + + /* Error channel for between fork and exec. */ + if (pipe(err) < 0) err[0] = err[1] = -1; + + if ((pid = fork()) == -1 ) { + report(2, "fork()"); + sleep(10); + return; + } + + if (pid == 0) { + /* Child */ + close(err[0]); + fcntl(err[1], F_SETFD, fcntl(err[1], F_GETFD) | FD_CLOEXEC); + + /* A new session. */ + setsid(); + + /* Construct device name. */ + strcpy(line, "/dev/"); + strncat(line, ttyp->ty_name, sizeof(line) - 6); + + /* Open the line for standard input and output. */ + close(0); + close(1); + if (open(line, O_RDWR) < 0 || dup(0) < 0) { + write(err[1], &errno, sizeof(errno)); + _exit(1); + } + + if (ttyp->ty_init != NULL && ttyp->ty_init[0] != NULL) { + /* Execute a command to initialize the terminal line. */ + + if ((pid = fork()) == -1) { + report(2, "fork()"); + errno= 0; + write(err[1], &errno, sizeof(errno)); + _exit(1); + } + + if (pid == 0) { + alarm(10); + execute(ttyp->ty_init); + report(2, ttyp->ty_init[0]); + _exit(1); + } + + while (waitpid(pid, &status, 0) != pid) {} + if (status != 0) { + tell(2, "init: "); + tell(2, ttyp->ty_name); + tell(2, ": "); + tell(2, ttyp->ty_init[0]); + tell(2, ": bad exit status\n"); + errno = 0; + write(err[1], &errno, sizeof(errno)); + _exit(1); + } + } + + /* Redirect standard error too. */ + dup2(0, 2); + + /* Execute the getty process. */ + execute(ttyp->ty_getty); + + /* Oops, disaster strikes. */ + fcntl(2, F_SETFL, fcntl(2, F_GETFL) | O_NONBLOCK); + if (linenr != 0) report(2, ttyp->ty_getty[0]); + write(err[1], &errno, sizeof(errno)); + _exit(1); + } + + /* Parent */ + if (ttyp != &TT_REBOOT) slotp->pid = pid; + + close(err[1]); + if (read(err[0], &errno, sizeof(errno)) != 0) { + /* If an errno value goes down the error pipe: Problems. */ + + switch (errno) { + case ENOENT: + case ENODEV: + case ENXIO: + /* Device nonexistent, no driver, or no minor device. */ + slotp->errct = ERRCT_DISABLE; + close(err[0]); + return; + case 0: + /* Error already reported. */ + break; + default: + /* Any other error on the line. */ + report(2, ttyp->ty_name); + } + close(err[0]); + + if (++slotp->errct >= ERRCT_DISABLE) { + tell(2, "init: "); + tell(2, ttyp->ty_name); + tell(2, ": excessive errors, shutting down\n"); + } else { + sleep(5); + } + return; + } + close(err[0]); + + if (ttyp != &TT_REBOOT) wtmp(LOGIN_PROCESS, linenr, ttyp->ty_name, pid); + slotp->errct = 0; +} + + +int execute(char **cmd) +{ + /* Execute a command with a path search along /sbin:/bin:/usr/sbin:/usr/bin. + */ + static char *nullenv[] = { NULL }; + char command[128]; + char *path[] = { "/sbin", "/bin", "/usr/sbin", "/usr/bin" }; + int i; + + if (cmd[0][0] == '/') { + /* A full path. */ + return execve(cmd[0], cmd, nullenv); + } + + /* Path search. */ + for (i = 0; i < 4; i++) { + if (strlen(path[i]) + 1 + strlen(cmd[0]) + 1 > sizeof(command)) { + errno= ENAMETOOLONG; + return -1; + } + strcpy(command, path[i]); + strcat(command, "/"); + strcat(command, cmd[0]); + execve(command, cmd, nullenv); + if (errno != ENOENT) break; + } + return -1; +} + + +void wtmp(type, linenr, line, pid) +int type; /* type of entry */ +int linenr; /* line number in ttytab */ +char *line; /* tty name (only good on login) */ +pid_t pid; /* pid of process */ +{ +/* Log an event into the UTMP and WTMP files. */ + + struct utmp utmp; /* UTMP/WTMP User Accounting */ + int fd; + + /* Clear the utmp record. */ + memset((void *) &utmp, 0, sizeof(utmp)); + + /* Fill in utmp. */ + switch (type) { + case BOOT_TIME: + /* Make a special reboot record. */ + strcpy(utmp.ut_name, "reboot"); + strcpy(utmp.ut_line, "~"); + break; + + case LOGIN_PROCESS: + /* A new login, fill in line name. */ + strncpy(utmp.ut_line, line, sizeof(utmp.ut_line)); + break; + + case DEAD_PROCESS: + /* A logout. Use the current utmp entry, but make sure it is a + * user process exiting, and not getty or login giving up. + */ + if ((fd = open(PATH_UTMP, O_RDONLY)) < 0) { + if (errno != ENOENT) report(2, PATH_UTMP); + return; + } + if (lseek(fd, (off_t) (linenr+1) * sizeof(utmp), SEEK_SET) == -1 + || read(fd, &utmp, sizeof(utmp)) == -1 + ) { + report(2, PATH_UTMP); + close(fd); + return; + } + close(fd); + if (utmp.ut_type != USER_PROCESS) return; + strncpy(utmp.ut_name, "", sizeof(utmp.ut_name)); + break; + } + + /* Finish new utmp entry. */ + utmp.ut_pid = pid; + utmp.ut_type = type; + utmp.ut_time = time((time_t *) 0); + + switch (type) { + case LOGIN_PROCESS: + case DEAD_PROCESS: + /* Write new entry to utmp. */ + if ((fd = open(PATH_UTMP, O_WRONLY)) < 0 + || lseek(fd, (off_t) (linenr+1) * sizeof(utmp), SEEK_SET) == -1 + || write(fd, &utmp, sizeof(utmp)) == -1 + ) { + if (errno != ENOENT) report(2, PATH_UTMP); + } + if (fd != -1) close(fd); + break; + } + + switch (type) { + case BOOT_TIME: + case DEAD_PROCESS: + /* Add new wtmp entry. */ + if ((fd = open(PATH_WTMP, O_WRONLY | O_APPEND)) < 0 + || write(fd, &utmp, sizeof(utmp)) == -1 + ) { + if (errno != ENOENT) report(2, PATH_WTMP); + } + if (fd != -1) close(fd); + break; + } +} + +void tell(fd, s) +int fd; +char *s; +{ + write(fd, s, strlen(s)); +} + +void report(fd, label) +int fd; +char *label; +{ + int err = errno; + + tell(fd, "init: "); + tell(fd, label); + tell(fd, ": "); + tell(fd, strerror(err)); + tell(fd, "\n"); + errno= err; +} diff --git a/servers/is/Makefile b/servers/is/Makefile new file mode 100644 index 000000000..b729a5c73 --- /dev/null +++ b/servers/is/Makefile @@ -0,0 +1,52 @@ +# Makefile for Information Server (IS) +SERVER = is + +# directories +u = /usr +i = $u/include +s = $i/sys +m = $i/minix +b = $i/ibm +k = $u/src/kernel + +# programs, flags, etc. +CC = exec cc +CFLAGS = -I$i +LDFLAGS = -i +LIBS = -lsys -lutils + +OBJ = main.o dmp.o diag.o kputc.o + +# build local binary +all build: $(SERVER) +$(SERVER): $(OBJ) + $(CC) -o $@ $(LDFLAGS) $(OBJ) $(LIBS) + install -S 256w $@ + +# install with other servers +install: /usr/sbin/servers/$(SERVER) +/usr/sbin/servers/$(SERVER): $(SERVER) + install -o root -cs $? $@ + +# clean up local files +clean: + rm -f $(SERVER) *.o *.bak + +# dependencies +a = is.h proto.h glo.h \ + $i/unistd.h $i/stdlib.h $i/stdio.h $i/ansi.h $i/limits.h $i/errno.h \ + $s/types.h $m/config.h $m/type.h $m/const.h $m/com.h $m/keymap.h \ + $m/syslib.h $s/types.h \ + $m/utils.h $m/serverassert.h $m/devio.h + +main.o: $a + +diag.o: $a +diag.o: $k/type.h + +dmp.o: $a +dmp.o: $i/timers.h $i/string.h $b/interrupt.h +dmp.o: $k/proc.h $k/sendmask.h $k/type.h $k/const.h + +kputc.o: $a + diff --git a/servers/is/diag.c b/servers/is/diag.c new file mode 100644 index 000000000..4a2c9b017 --- /dev/null +++ b/servers/is/diag.c @@ -0,0 +1,107 @@ +#include "is.h" +#include "../../kernel/const.h" +#include "../../kernel/type.h" + +/*==========================================================================* + * do_new_kmess * + *==========================================================================*/ +PUBLIC int do_new_kmess(m) +message *m; /* notification message */ +{ +/* Notification for a new kernel message. */ + struct kmessages kmess; /* entire kmess structure */ + char print_buf[KMESS_BUF_SIZE]; /* copy new message here */ + static int prev_next = 0; + int size, next; + int bytes; + int i, r; + + /* Try to get a fresh copy of the buffer with kernel messages. */ + if ((r=sys_getkmessages(&kmess)) != OK) { + report("Couldn't get copy of kmessages", r); + return; + } + + /* Print only the new part. Determine how many new bytes there are with + * help of the current and previous 'next' index. Note that the kernel + * buffer is circular. This works fine if less then KMESS_BUF_SIZE bytes + * is new data; else we miss % KMESS_BUF_SIZE here. + * Check for size being positive, the buffer might as well be emptied! + */ + if (kmess.km_size > 0) { + bytes = ((kmess.km_next + KMESS_BUF_SIZE) - prev_next) % KMESS_BUF_SIZE; + r=prev_next; /* start at previous old */ + i=0; + while (bytes > 0) { + print_buf[i] = kmess.km_buf[(r%KMESS_BUF_SIZE)]; + diag_putc( kmess.km_buf[(r%KMESS_BUF_SIZE)] ); + bytes --; + r ++; + i ++; + } + /* Now terminate the new message and print it. */ + print_buf[i] = 0; + printf(print_buf); + } + + /* Almost done, store 'next' so that we can determine what part of the + * kernel messages buffer to print next time a notification arrives. + */ + prev_next = next; + return EDONTREPLY; +} + + +/*===========================================================================* + * do_diagnostics * + *===========================================================================*/ +PUBLIC int do_diagnostics(message *m) +{ +/* The IS server handles all diagnostic messages from servers and device + * drivers. It forwards the message to the TTY driver to display it to the + * user. It also saves a copy in a local buffer so that messages can be + * reviewed at a later time. + */ + int result; + int proc_nr; + vir_bytes src; + int count; + char c; + + /* Forward the message to the TTY driver. Inform the TTY driver about the + * original sender, so that it knows where the buffer to be printed is. + * The message type, DIAGNOSTICS, remains the same. + */ + if ((proc_nr = m->DIAG_PROC_NR) == SELF) + m->DIAG_PROC_NR = proc_nr = m->m_source; + result = _sendrec(TTY, m); + + /* Now also make a copy for the private buffer at the IS server, so + * that the messages can be reviewed at a later time. + */ + src = (vir_bytes) m->DIAG_PRINT_BUF; + count = m->DIAG_BUF_COUNT; + while (count > 0) { + if (sys_datacopy(proc_nr, src, SELF, (vir_bytes) &c, 1) != OK) + break; /* stop copying on error */ + diag_putc(c); /* accumulate character */ + src ++; + count --; + } + + return result; +} + + +/*===========================================================================* + * diag_putc * + *===========================================================================*/ +PUBLIC void diag_putc(c) +int c; /* char to be added to diag buffer */ +{ + diag_buf[diag_next] = c; + diag_next = (diag_next + 1) % DIAG_BUF_SIZE; + if (diag_size < DIAG_BUF_SIZE) + diag_size += 1; +} + diff --git a/servers/is/dmp.c b/servers/is/dmp.c new file mode 100644 index 000000000..80276338b --- /dev/null +++ b/servers/is/dmp.c @@ -0,0 +1,509 @@ +/* This file contains information dump procedures. During the initialization + * of the Information Service 'known' function keys are registered at the TTY + * server in order to receive a notification if one is pressed. Here, the + * corresponding dump procedure is called. + * + * The entry points into this file are + * handle_fkey: handle a function key pressed notification + */ + + +/* Several header files from the kernel are included here so that we can use + * the constants, macros, and types needed to make the debugging dumps. + */ +#include "is.h" +#include <timers.h> +#include <string.h> +#include <ibm/interrupt.h> +#include "../../kernel/const.h" +#include "../../kernel/type.h" +#include "../../kernel/proc.h" +#include "../../kernel/sendmask.h" + +#define click_to_round_k(n) \ + ((unsigned) ((((unsigned long) (n) << CLICK_SHIFT) + 512) / 1024)) + + +/* Declare some local dump procedures. */ +FORWARD _PROTOTYPE( char *proc_name, (int proc_nr) ); +FORWARD _PROTOTYPE( void proctab_dmp, (void) ); +FORWARD _PROTOTYPE( void memmap_dmp, (void) ); +FORWARD _PROTOTYPE( void sendmask_dmp, (void) ); +FORWARD _PROTOTYPE( void image_dmp, (void) ); +FORWARD _PROTOTYPE( void irqtab_dmp, (void) ); +FORWARD _PROTOTYPE( void kmessages_dmp, (void) ); +FORWARD _PROTOTYPE( void diagnostics_dmp, (void) ); +FORWARD _PROTOTYPE( void sched_dmp, (void) ); +FORWARD _PROTOTYPE( void monparams_dmp, (void) ); +FORWARD _PROTOTYPE( void kenviron_dmp, (void) ); +FORWARD _PROTOTYPE( void memchunks_dmp, (void) ); + +/* Some global data that is shared among several dumping procedures. + * Note that the process table copy has the same name as in the kernel + * so that most macros and definitions from proc.h also apply here. + */ +struct proc proc[NR_TASKS + NR_PROCS]; +struct system_image image[IMAGE_SIZE]; + + + +/*===========================================================================* + * handle_fkey * + *===========================================================================*/ +PUBLIC int do_fkey_pressed(message *m) +{ + if (F1 <= m->FKEY_CODE && m->FKEY_CODE <= F12) { + switch(m->FKEY_CODE) { + case F1: proctab_dmp(); break; + case F2: memmap_dmp(); break; + case F3: image_dmp(); break; + case F4: sendmask_dmp(); break; + case F5: monparams_dmp(); break; + case F6: irqtab_dmp(); break; + case F7: kmessages_dmp(); break; + case F9: diagnostics_dmp(); break; + case F10: kenviron_dmp(); break; + case F11: memchunks_dmp(); break; + case F12: sched_dmp(); break; + default: + printf("IS: unhandled notification for F%d\n", m->FKEY_NUM); + } + } + return(EDONTREPLY); +} + + +/*===========================================================================* + * diagnostics_dmp * + *===========================================================================*/ +PRIVATE void diagnostics_dmp() +{ + char print_buf[DIAG_BUF_SIZE+1]; /* buffer used to print */ + int start; /* calculate start of messages */ + int size, r; + + /* Try to print the kernel messages. First determine start and copy the + * buffer into a print-buffer. This is done because the messages in the + * copy may wrap (the kernel buffer is circular). + */ + start = ((diag_next + DIAG_BUF_SIZE) - diag_size) % DIAG_BUF_SIZE; + r = 0; + size = diag_size; + while (size > 0) { + print_buf[r] = diag_buf[(start+r) % DIAG_BUF_SIZE]; + r ++; + size --; + } + print_buf[r] = 0; /* make sure it terminates */ + printf("Dump of diagnostics from device drivers and servers.\n\n"); + printf(print_buf); /* print the messages */ +} + + +/*===========================================================================* + * kmessages_dmp * + *===========================================================================*/ +PRIVATE void kmessages_dmp() +{ + struct kmessages kmess; /* get copy of kernel messages */ + char print_buf[KMESS_BUF_SIZE+1]; /* this one is used to print */ + int next, size; /* vars returned by sys_kmess() */ + int start; /* calculate start of messages */ + int r; + + /* Try to get a copy of the kernel messages. */ + if ((r = sys_getkmessages(&kmess)) != OK) { + report("warning: couldn't get copy of kmessages", r); + return; + } + + /* Try to print the kernel messages. First determine start and copy the + * buffer into a print-buffer. This is done because the messages in the + * copy may wrap (the kernel buffer is circular). + */ + start = ((kmess.km_next + KMESS_BUF_SIZE) - kmess.km_size) % KMESS_BUF_SIZE; + r = 0; + while (kmess.km_size > 0) { + print_buf[r] = kmess.km_buf[(start+r) % KMESS_BUF_SIZE]; + r ++; + kmess.km_size --; + } + print_buf[r] = 0; /* make sure it terminates */ + printf("Dump of all messages generated by the kernel.\n\n"); + printf(print_buf); /* print the messages */ +} + + +/*===========================================================================* + * monparams_dmp * + *===========================================================================*/ +PRIVATE void monparams_dmp() +{ + char val[1024]; + char *e; + int r; + + /* Try to get a copy of the boot monitor parameters. */ + if ((r = sys_getmonparams(val, sizeof(val))) != OK) { + report("warning: couldn't get copy of monitor params", r); + return; + } + + /* Append new lines to the result. */ + e = val; + do { + e += strlen(e); + *e++ = '\n'; + } while (*e != 0); + + /* Finally, print the result. */ + printf("Dump of kernel environment strings set by boot monitor.\n"); + printf("\n%s\n", val); +} + + +/*===========================================================================* + * irqtab_dmp * + *===========================================================================*/ +PRIVATE void irqtab_dmp() +{ + int i,j,r; + struct irqtab irqtab[NR_IRQ_VECTORS]; + struct irqtab *e; /* irq tab entry */ + int p; /* policy */ + char *irq[] = { + "clock", /* 00 */ + "keyboard", /* 01 */ + "cascade", /* 02 */ + "eth/rs232", /* 03 */ + "rs232", /* 04 */ + "xt_wini", /* 05 */ + "floppy", /* 06 */ + "printer", /* 07 */ + "", /* 08 */ + "", /* 09 */ + "", /* 10 */ + "", /* 11 */ + "", /* 12 */ + "", /* 13 */ + "at_wini_0", /* 14 */ + "at_wini_1", /* 15 */ + }; + + if ((r = sys_getirqtab(irqtab)) != OK) { + report("warning: couldn't get copy of irqtab", r); + return; + } + + printf("IRQ table dump showing hardware interrupt policies for each IRQ vector.\n"); + printf("-irq name/nr- -pnr- --port-- msk_val --addr-- -type-rdp-str-ech-wrp-ena- \n"); + for (i=0; i<NR_IRQ_VECTORS; i++) { + e = &irqtab[i]; + p = e->policy; + printf("%9s %2d ", irq[i], i); + if (e->proc_nr!=NONE) printf("%4d ", e->proc_nr); + else printf(" "); + printf(" 0x%06x 0x%05x 0x%06x %c%c%c %d %d %d %d %d\n", + e->port, e->mask_val, e->addr, + (p&IRQ_BYTE)?'B':'-', (p&IRQ_WORD)?'W':'-', (p&IRQ_LONG)?'L':'-', + ((p&IRQ_READ_PORT) != 0), + ((p&IRQ_STROBE) != 0), + ((p&IRQ_ECHO_VAL) != 0), + ((p&IRQ_WRITE_PORT) != 0), + ((p&IRQ_REENABLE) != 0) + ); + } + printf("\n"); +} + + +/*===========================================================================* + * image_dmp * + *===========================================================================*/ +PRIVATE void image_dmp() +{ + int i,j,r; + struct system_image *ip; + char maskstr[NR_TASKS + NR_PROCS] = "mask"; + char* types[] = {"task", "system","driver", "server", "user", "idle"}; + char* priorities[] = {"task", "higher","high", "normal", "low", "lower", "user","idle"}; + + if ((r = sys_getimage(image)) != OK) { + report("warning: couldn't get copy of image table", r); + return; + } + printf("Image table dump showing all processes included in system image.\n"); + printf("---name-- -nr- --type- -priority- ----pc- -stack- ------sendmask-------\n"); + for (i=0; i<IMAGE_SIZE; i++) { + ip = &image[i]; + for (j=-NR_TASKS; j<LOW_USER+2; j++) + maskstr[j+NR_TASKS] = (isallowed(ip->sendmask, j)) ? '1' : '0'; + maskstr[j+NR_TASKS] = '\0'; + printf("%8s %4d %7s %10s %7lu %7lu %s\n", + ip->name, ip->proc_nr, types[ip->type], priorities[ip->priority], + (long)ip->initial_pc, ip->stksize, maskstr); + } + printf("\n"); +} + +/*===========================================================================* + * sched_dmp * + *===========================================================================*/ +PRIVATE void sched_dmp() +{ + struct proc *rdy_head[NR_SCHED_QUEUES]; + char *types[] = {"task","higher","high","normal","low","lower","user","idle"}; + struct kenviron kenviron; + register struct proc *rp; + vir_bytes ptr_diff; + int r; + + /* First obtain a scheduling information. */ + if ((r = sys_getschedinfo(proc, rdy_head)) != OK) { + report("warning: couldn't get copy of process table", r); + return; + } + /* Then obtain kernel addresses to correct pointer information. */ + if ((r = sys_getkenviron(&kenviron)) != OK) { + report("warning: couldn't get kernel addresses", r); + return; + } + + /* Update all pointers. Nasty pointer algorithmic ... */ + ptr_diff = (vir_bytes) proc - (vir_bytes) kenviron.proc_addr; + for (r=0;r<NR_SCHED_QUEUES; r++) + if (rdy_head[r] != NIL_PROC) + rdy_head[r] = + (struct proc *)((vir_bytes) rdy_head[r] + ptr_diff); + for (rp=BEG_PROC_ADDR; rp < END_PROC_ADDR; rp++) + if (rp->p_nextready != NIL_PROC) + rp->p_nextready = + (struct proc *)((vir_bytes) rp->p_nextready + ptr_diff); + + /* Now show scheduling queues. */ + printf("Dumping scheduling queues.\n"); + + for (r=0;r<NR_SCHED_QUEUES; r++) { + printf("* %6s: ", types[r]); + rp = rdy_head[r]; + while (rp != NIL_PROC) { + printf("%3d, ", rp->p_nr); + rp = rp->p_nextready; + } + printf("NIL\n"); + } + printf("\n"); +} + +/*===========================================================================* + * kenviron_dmp * + *===========================================================================*/ +PRIVATE void kenviron_dmp() +{ + struct kenviron kenv; + int r; + if ((r = sys_getkenviron(&kenv)) != OK) { + report("warning: couldn't get copy of kernel env", r); + return; + } + + printf("Dump of kernel variables based on the environment set by the boot loader.\n"); + printf("- pc_at: %3d\n", kenv.pc_at); + printf("- ps_mca: %3d\n", kenv.ps_mca); + printf("- processor: %3d\n", kenv.processor); + printf("- protected: %3d\n", kenv.protected); + printf("- ega: %3d\n", kenv.ega); + printf("- vga: %3d\n", kenv.vga); + printf("- params_base:%5u\n", kenv.params_base); + printf("- params_size:%5u\n", kenv.params_size); + printf("- kmem_base:%5u\n", kenv.kmem_base); + printf("- kmem_size:%5u\n", kenv.kmem_size); + printf("- bootfs_base:%5u\n", kenv.bootfs_base); + printf("- bootfs_size:%5u\n", kenv.bootfs_size); + printf("\n"); +} + +/*===========================================================================* + * memchunks_dmp * + *===========================================================================*/ +PRIVATE void memchunks_dmp() +{ + int i,r; + struct memory mem[NR_MEMS]; + if ((r = sys_getmemchunks(mem)) != OK) { + report("warning: couldn't get copy of mem chunks", r); + return; + } + + printf("Memory chunks:\n"); + for (i=0; i<NR_MEMS; i++) { + printf("chunk %d: base %u, size %u\n", i, mem[i].base, mem[i].size); + } + printf("\n"); +} + + + + +/*===========================================================================* + * sendmask_dmp * + *===========================================================================*/ +PRIVATE void sendmask_dmp() +{ + register struct proc *rp; + static struct proc *oldrp = BEG_PROC_ADDR; + int r, i,j, n = 0; + + /* First obtain a fresh copy of the current process table. */ + if ((r = sys_getproctab(proc)) != OK) { + report("warning: couldn't get copy of process table", r); + return; + } + + printf("\n\n"); + printf("Sendmask dump for process table. User processes (*) don't have []."); + printf("\n"); + printf("The rows of bits indicate to which processes each process may send."); + printf("\n\n"); + + printf(" "); + for (j=proc_number(BEG_PROC_ADDR); j< LOW_USER+1; j++) { + printf("%3d", j); + } + printf(" *\n"); + + for (rp = oldrp; rp < END_PROC_ADDR; rp++) { + if (isemptyp(rp)) continue; + if (++n > 20) break; + + printf("%8s ", rp->p_name); + j = proc_number(rp); + switch(rp->p_type) { + case P_IDLE: printf("/%3d/ ", proc_number(rp)); break; + case P_TASK: printf("[%3d] ", proc_number(rp)); break; + case P_SYSTEM: printf("<%3d> ", proc_number(rp)); break; + case P_DRIVER: printf("{%3d} ", proc_number(rp)); break; + case P_SERVER: printf("(%3d) ", proc_number(rp)); break; + default: printf(" %3d ", proc_number(rp)); + } + + for (j=proc_number(BEG_PROC_ADDR); j<LOW_USER+2; j++) { + if (isallowed(rp->p_sendmask, j)) printf(" 1 "); + else printf(" 0 "); + } + printf("\n"); + } + if (rp == END_PROC_ADDR) { printf("\n"); rp = BEG_PROC_ADDR; } + else printf("--more--\r"); + oldrp = rp; +} + + + +/*===========================================================================* + * proctab_dmp * + *===========================================================================*/ +#if (CHIP == INTEL) +PRIVATE void proctab_dmp() +{ +/* Proc table dump */ + + register struct proc *rp; + static struct proc *oldrp = BEG_PROC_ADDR; + int r, n = 0; + phys_clicks text, data, size; + + /* First obtain a fresh copy of the current process table. */ + if ((r = sys_getproctab(proc)) != OK) { + report("warning: couldn't get copy of process table", r); + return; + } + + printf("\n-pid- -pri- --pc-- --sp-- -user- -sys- -text- -data- -size- -flags- -command-\n"); + + for (rp = oldrp; rp < END_PROC_ADDR; rp++) { + if (isemptyp(rp)) continue; + if (++n > 23) break; + text = rp->p_memmap[T].mem_phys; + data = rp->p_memmap[D].mem_phys; + size = rp->p_memmap[T].mem_len + + ((rp->p_memmap[S].mem_phys + rp->p_memmap[S].mem_len) - data); + switch(rp->p_type) { + case P_IDLE: printf("/%3d/ ", proc_number(rp)); break; + case P_TASK: printf("[%3d] ", proc_number(rp)); break; + case P_SYSTEM: printf("<%3d> ", proc_number(rp)); break; + case P_DRIVER: printf("{%3d} ", proc_number(rp)); break; + case P_SERVER: printf("(%3d) ", proc_number(rp)); break; + default: printf(" %3d ", proc_number(rp)); + } + printf("%3u %7lx%7lx %6lu%6lu%6uK%6uK%6uK %3x", + rp->p_priority, + (unsigned long) rp->p_reg.pc, + (unsigned long) rp->p_reg.sp, + rp->user_time, rp->sys_time, + click_to_round_k(text), click_to_round_k(data), + click_to_round_k(size), + rp->p_flags); + if (rp->p_flags & RECEIVING) { + printf(" %-7.7s", proc_name(rp->p_getfrom)); + } else + if (rp->p_flags & SENDING) { + printf(" S:%-5.5s", proc_name(rp->p_sendto)); + } else + if (rp->p_flags == 0) { + printf(" "); + } + printf(" %s\n", rp->p_name); + } + if (rp == END_PROC_ADDR) rp = BEG_PROC_ADDR; else printf("--more--\r"); + oldrp = rp; +} +#endif /* (CHIP == INTEL) */ + +/*===========================================================================* + * memmap_dmp * + *===========================================================================*/ +PRIVATE void memmap_dmp() +{ + register struct proc *rp; + static struct proc *oldrp = cproc_addr(HARDWARE); + int r, n = 0; + phys_clicks size; + + /* First obtain a fresh copy of the current process table. */ + if ((r = sys_getproctab(proc)) != OK) { + report("warning: couldn't get copy of process table", r); + return; + } + + printf("\n--proc name- -----text----- -----data----- ----stack----- -size-\n"); + for (rp = oldrp; rp < END_PROC_ADDR; rp++) { + if (isemptyp(rp)) continue; + if (++n > 23) break; + size = rp->p_memmap[T].mem_len + + ((rp->p_memmap[S].mem_phys + rp->p_memmap[S].mem_len) + - rp->p_memmap[D].mem_phys); + printf("%3d %-7.7s %4x %4x %4x %4x %4x %4x %4x %4x %4x %5uK\n", + proc_number(rp), + rp->p_name, + rp->p_memmap[T].mem_vir, rp->p_memmap[T].mem_phys, rp->p_memmap[T].mem_len, + rp->p_memmap[D].mem_vir, rp->p_memmap[D].mem_phys, rp->p_memmap[D].mem_len, + rp->p_memmap[S].mem_vir, rp->p_memmap[S].mem_phys, rp->p_memmap[S].mem_len, + click_to_round_k(size)); + } + if (rp == END_PROC_ADDR) rp = cproc_addr(HARDWARE); + else printf("--more--\r"); + oldrp = rp; +} + +/*===========================================================================* + * proc_name * + *===========================================================================*/ +PRIVATE char *proc_name(proc_nr) +int proc_nr; +{ + if (proc_nr == ANY) return "ANY"; + return cproc_addr(proc_nr)->p_name; +} + + diff --git a/servers/is/glo.h b/servers/is/glo.h new file mode 100644 index 000000000..092dd72be --- /dev/null +++ b/servers/is/glo.h @@ -0,0 +1,18 @@ +/* Global variables. */ +extern is_proc_nr; /* process number of IS server */ + +/* Parameters needed to keep diagnostics at IS. */ +#define DIAG_BUF_SIZE 1024 +extern char diag_buf[DIAG_BUF_SIZE]; /* buffer for messages */ +extern int diag_next; /* next index to be written */ +extern int diag_size; /* size of all messages */ + +/* The parameters of the call are kept here. */ +extern message m_in; /* the input message itself */ +extern message m_out; /* the output message used for reply */ +extern int who; /* caller's proc number */ +extern int callnr; /* system call number */ +extern int dont_reply; /* normally 0; set to 1 to inhibit reply */ + + + diff --git a/servers/is/is.h b/servers/is/is.h new file mode 100644 index 000000000..06b3411d8 --- /dev/null +++ b/servers/is/is.h @@ -0,0 +1,30 @@ +/* Header file for the system information server. + * + * Created: + * Jul 13, 2004 by Jorrit N. Herder + */ + +#define _SYSTEM 1 /* get OK and negative error codes */ +#define _MINIX 1 /* tell headers to include MINIX stuff */ + +#include <ansi.h> +#include <sys/types.h> +#include <limits.h> +#include <errno.h> + +#include <minix/callnr.h> +#include <minix/config.h> +#include <minix/type.h> +#include <minix/const.h> +#include <minix/com.h> +#include <minix/syslib.h> +#include <minix/utils.h> +#include <minix/keymap.h> + +#include <unistd.h> +#include <stdlib.h> +#include <stdio.h> + +#include "proto.h" +#include "glo.h" + diff --git a/servers/is/kputc.c b/servers/is/kputc.c new file mode 100644 index 000000000..180ab1afd --- /dev/null +++ b/servers/is/kputc.c @@ -0,0 +1,35 @@ +/* A server must occasionally print some message. It uses a simple version of + * printf() found in the system library that calls putk() to output characters. + * The IS server cannot use the regular putk() since we do not want to over- + * write kernel messages with the output of the IS. Hence, it uses a special + * putk that directly sends to the TTY task. + */ + +#include "is.h" + + +/*===========================================================================* + * kputc * + *===========================================================================*/ +void kputc(c) +int c; +{ +/* Accumulate another character. If 0 or buffer full, print it. */ + + static int buf_count; /* # characters in the buffer */ + static char print_buf[80]; /* output is buffered here */ + message m; + + if ((c == 0 && buf_count > 0) || buf_count == sizeof(print_buf)) { + m.DIAG_BUF_COUNT = buf_count; + m.DIAG_PRINT_BUF = print_buf; + m.DIAG_PROC_NR = SELF; + m.m_type = DIAGNOSTICS; /* request TTY to output this buffer */ + _sendrec(TTY, &m); /* if it fails, we cannot do better */ + buf_count = 0; /* clear buffer for next batch */ + } + if (c != 0) { + print_buf[buf_count++] = c; + } +} + diff --git a/servers/is/main.c b/servers/is/main.c new file mode 100644 index 000000000..73c3db2ff --- /dev/null +++ b/servers/is/main.c @@ -0,0 +1,149 @@ +/* System Information Service. + * This service handles the various debugging dumps, such as the process + * table, so that these no longer directly touch kernel memory. Instead, the + * system task is asked to copy some table in local memory. + * + * Created: + * Apr 29, 2004 by Jorrit N. Herder + */ + +#include "is.h" + +/* Set debugging level to 0, 1, or 2 to see no, some, all debug output. */ +#define DEBUG_LEVEL 1 +#define DPRINTF if (DEBUG_LEVEL > 0) printf + +/* Allocate space for the global variables. */ +int is_proc_nr; /* own process number */ +message m_in; /* the input message itself */ +message m_out; /* the output message used for reply */ +int who; /* caller's proc number */ +int callnr; /* system call number */ + +/* Diagnostic messages buffer. */ +char diag_buf[DIAG_BUF_SIZE]; +int diag_size = 0; +int diag_next = 0; + +/* Declare some local functions. */ +FORWARD _PROTOTYPE(void init_server, (void) ); +FORWARD _PROTOTYPE(void get_work, (void) ); +FORWARD _PROTOTYPE(void reply, (int whom, int result) ); + + +/*===========================================================================* + * main * + *===========================================================================*/ +PUBLIC void main(void) +{ +/* This is the main routine of this service. The main loop consists of + * three major activities: getting new work, processing the work, and + * sending the reply. The loop never terminates, unless a panic occurs. + */ + int result; + + /* Initialize the server, then go to work. */ + init_server(); + + /* Main loop - get work and do it, forever. */ + while (TRUE) { + + /* Wait for incoming message, sets 'callnr' and 'who'. */ + get_work(); + + switch (callnr) { + case NEW_KMESS: + result = do_new_kmess(&m_in); + break; + case DIAGNOSTICS: + result = do_diagnostics(&m_in); + break; + case FKEY_PRESSED: + result = do_fkey_pressed(&m_in); + break; + case HARD_STOP: + sys_exit(0); + /* never reached */ + continue; + default: + result = EINVAL; + } + + /* Finally send reply message, unless disabled. */ + if (result != EDONTREPLY) { + reply(who, result); + } + } +} + + +/*===========================================================================* + * report * + *===========================================================================*/ +PUBLIC void report(mess, num) +char *mess; /* message format to print */ +int num; /* number to go with the message */ +{ + if (num != NO_NUM) { + printf("IS: %s %d\n", mess, num); + } else { + printf("IS: %s\n", mess); + } +} + + +/*===========================================================================* + * init_server * + *===========================================================================*/ +PRIVATE void init_server() +{ +/* Initialize the information service. */ + message m; + int r; + long key; + + /* Set own process number. */ + is_proc_nr = IS_PROC_NR; + + /* Set key mappings. IS takes all of F2-F12. F1 is TTY reserved. */ + for (key=F1; key<=F12; key++) { + if ((r=fkey_enable(key)) != OK) { + printf("IS: WARNING: couldn't register F%d key: %d\n", + (key-F1+1), r); + } + } + + /* Display status message ... */ + printf("IS: information service is alive and kicking; press F1-F12 for dumps\n"); +} + +/*===========================================================================* + * get_work * + *===========================================================================*/ +PRIVATE void get_work() +{ + int status = 0; + status = receive(ANY, &m_in); /* this blocks until message arrives */ + if (OK != status) + server_panic("IS","failed to receive message!", status); + who = m_in.m_source; /* message arrived! set sender */ + callnr = m_in.m_type; /* set function call number */ +} + + +/*===========================================================================* + * reply * + *===========================================================================*/ +PRIVATE void reply(who, result) +int who; /* destination */ +int result; /* report result to replyee */ +{ + int send_status; + m_out.m_type = result; /* build reply message */ + send_status = send(who, &m_out); /* send the message */ + if (OK != send_status) + server_panic("IS", "unable to send reply!", send_status); +} + + + diff --git a/servers/is/proto.h b/servers/is/proto.h new file mode 100644 index 000000000..540051ffd --- /dev/null +++ b/servers/is/proto.h @@ -0,0 +1,20 @@ +/* Function prototypes. */ + +/* Structs used in prototypes must be declared as such first. */ + +/* main.c */ +_PROTOTYPE( void main, (void) ); +_PROTOTYPE( void report, (char *, int n) ); + +/* putk.c */ +_PROTOTYPE( void kputc, (int c) ); + +/* diag.c */ +_PROTOTYPE( int do_new_kmess, (message *m) ); +_PROTOTYPE( int do_diagnostics, (message *m) ); +_PROTOTYPE( void diag_putc, (int c) ); + +/* dmp.c */ +_PROTOTYPE( int do_fkey_pressed, (message *m) ); + + diff --git a/servers/mm/Makefile b/servers/mm/Makefile new file mode 100644 index 000000000..61983bb44 --- /dev/null +++ b/servers/mm/Makefile @@ -0,0 +1,134 @@ +# Makefile for Memory Manager (MM) +SERVER = mm + +# directories +u = /usr +i = $u/include +s = $i/sys +h = $i/minix +k = $u/src/kernel + +# programs, flags, etc. +CC = exec cc +CFLAGS = -I$i +LDFLAGS = -i + +OBJ = main.o forkexit.o break.o exec.o procutils.o \ + signal.o alloc.o utility.o table.o trace.o getset.o misc.o + +# build local binary +all build: $(SERVER) +$(SERVER): $(OBJ) + $(CC) -o $@ $(LDFLAGS) $(OBJ) -lsys -lutils + install -S 256w $@ + +# install with other servers +install: /usr/sbin/servers/$(SERVER) +/usr/sbin/servers/$(SERVER): $(SERVER) + install -o root -cs $? $@ + +# clean up local files +clean: + rm -f $(SERVER) *.o *.bak + +# dependencies +a = mm.h $h/config.h $s/types.h $h/const.h $h/type.h \ + $i/ansi.h $i/fcntl.h $i/unistd.h $h/syslib.h \ + $i/limits.h $i/errno.h const.h type.h proto.h glo.h + +alloc.o: $a +alloc.o: $i/signal.h +alloc.o: $h/com.h +alloc.o: $h/callnr.h +alloc.o: mproc.h +alloc.o: $k/type.h +alloc.o: $k/const.h + +break.o: $a +break.o: $i/signal.h +break.o: mproc.h +break.o: param.h + +exec.o: $a +exec.o: $s/stat.h +exec.o: $h/callnr.h +exec.o: $h/com.h +exec.o: $i/a.out.h +exec.o: $i/signal.h +exec.o: $i/string.h +exec.o: mproc.h +exec.o: param.h + +forkexit.o: $a +forkexit.o: $s/wait.h +forkexit.o: $h/callnr.h +forkexit.o: $h/com.h +forkexit.o: $h/utils.h +forkexit.o: $i/signal.h +forkexit.o: mproc.h +forkexit.o: param.h + +getset.o: $a +getset.o: $h/callnr.h +getset.o: $i/signal.h +getset.o: mproc.h +getset.o: param.h + +main.o: $a +main.o: $h/callnr.h +main.o: $h/com.h +main.o: $i/signal.h +main.o: $i/fcntl.h +main.o: $h/ioctl.h +main.o: $s/ioc_memory.h +main.o: $h/utils.h +main.o: mproc.h +main.o: param.h + +misc.o: $a +misc.o: $h/callnr.h +misc.o: $h/utils.h +misc.o: $i/signal.h +misc.o: $h/ioctl.h +misc.o: $s/svrctl.h +misc.o: mproc.h +misc.o: param.h + +procutils.o: $a +procutils.o: $i/timers.h +procutils.o: $i/string.h +procutils.o: $k/const.h +procutils.o: $k/type.h +procutils.o: $k/proc.h + +signal.o: $a +signal.o: $s/stat.h +signal.o: $h/callnr.h +signal.o: $h/utils.h +signal.o: $h/com.h +signal.o: $i/signal.h +signal.o: $s/sigcontext.h +signal.o: $i/string.h +signal.o: mproc.h +signal.o: param.h + +table.o: $a +table.o: $h/callnr.h +table.o: $i/signal.h +table.o: mproc.h +table.o: param.h + +trace.o: $a +trace.o: $h/com.h +trace.o: $s/ptrace.h +trace.o: $i/signal.h +trace.o: mproc.h +trace.o: param.h + +utility.o: $a +utility.o: $s/stat.h +utility.o: $h/callnr.h +utility.o: $h/com.h +utility.o: $i/fcntl.h +utility.o: $i/signal.h +utility.o: mproc.h diff --git a/servers/mm/alloc.c b/servers/mm/alloc.c new file mode 100644 index 000000000..91d0e32f9 --- /dev/null +++ b/servers/mm/alloc.c @@ -0,0 +1,428 @@ +/* This file is concerned with allocating and freeing arbitrary-size blocks of + * physical memory on behalf of the FORK and EXEC system calls. The key data + * structure used is the hole table, which maintains a list of holes in memory. + * It is kept sorted in order of increasing memory address. The addresses + * it contains refers to physical memory, starting at absolute address 0 + * (i.e., they are not relative to the start of MM). During system + * initialization, that part of memory containing the interrupt vectors, + * kernel, and MM are "allocated" to mark them as not available and to + * remove them from the hole list. + * + * The entry points into this file are: + * alloc_mem: allocate a given sized chunk of memory + * free_mem: release a previously allocated chunk of memory + * mem_init: initialize the tables when MM start up + * max_hole: returns the largest hole currently available + */ + +#include "mm.h" +#include <minix/com.h> +#include <minix/callnr.h> +#include <signal.h> +#include "mproc.h" +#include "../../kernel/const.h" +#include "../../kernel/type.h" + +#define NR_HOLES (2*NR_PROCS) /* max # entries in hole table */ +#define NIL_HOLE (struct hole *) 0 + +PRIVATE struct hole { + struct hole *h_next; /* pointer to next entry on the list */ + phys_clicks h_base; /* where does the hole begin? */ + phys_clicks h_len; /* how big is the hole? */ +} hole[NR_HOLES]; + +PRIVATE struct hole *hole_head; /* pointer to first hole */ +PRIVATE struct hole *free_slots;/* ptr to list of unused table slots */ +#if ENABLE_SWAP +PRIVATE int swap_fd = -1; /* file descriptor of open swap file/device */ +PRIVATE u32_t swap_offset; /* offset to start of swap area on swap file */ +PRIVATE phys_clicks swap_base; /* memory offset chosen as swap base */ +PRIVATE phys_clicks swap_maxsize;/* maximum amount of swap "memory" possible */ +PRIVATE struct mproc *in_queue; /* queue of processes wanting to swap in */ +PRIVATE struct mproc *outswap = &mproc[LOW_USER]; /* outswap candidate? */ +#else /* !SWAP */ +#define swap_base ((phys_clicks) -1) +#endif /* !SWAP */ + +FORWARD _PROTOTYPE( void del_slot, (struct hole *prev_ptr, struct hole *hp) ); +FORWARD _PROTOTYPE( void merge, (struct hole *hp) ); +#if ENABLE_SWAP +FORWARD _PROTOTYPE( int swap_out, (void) ); +#else +#define swap_out() (0) +#endif + +/*===========================================================================* + * alloc_mem * + *===========================================================================*/ +PUBLIC phys_clicks alloc_mem(clicks) +phys_clicks clicks; /* amount of memory requested */ +{ +/* Allocate a block of memory from the free list using first fit. The block + * consists of a sequence of contiguous bytes, whose length in clicks is + * given by 'clicks'. A pointer to the block is returned. The block is + * always on a click boundary. This procedure is called when memory is + * needed for FORK or EXEC. Swap other processes out if needed. + */ + + register struct hole *hp, *prev_ptr; + phys_clicks old_base; + + do { + hp = hole_head; + while (hp != NIL_HOLE && hp->h_base < swap_base) { + if (hp->h_len >= clicks) { + /* We found a hole that is big enough. Use it. */ + old_base = hp->h_base; /* remember where it started */ + hp->h_base += clicks; /* bite a piece off */ + hp->h_len -= clicks; /* ditto */ + + /* Delete the hole if used up completely. */ + if (hp->h_len == 0) del_slot(prev_ptr, hp); + + /* Return the start address of the acquired block. */ + return(old_base); + } + + prev_ptr = hp; + hp = hp->h_next; + } + } while (swap_out()); /* try to swap some other process out */ + return(NO_MEM); +} + +/*===========================================================================* + * free_mem * + *===========================================================================*/ +PUBLIC void free_mem(base, clicks) +phys_clicks base; /* base address of block to free */ +phys_clicks clicks; /* number of clicks to free */ +{ +/* Return a block of free memory to the hole list. The parameters tell where + * the block starts in physical memory and how big it is. The block is added + * to the hole list. If it is contiguous with an existing hole on either end, + * it is merged with the hole or holes. + */ + + register struct hole *hp, *new_ptr, *prev_ptr; + + if (clicks == 0) return; + if ( (new_ptr = free_slots) == NIL_HOLE) panic("Hole table full", NO_NUM); + new_ptr->h_base = base; + new_ptr->h_len = clicks; + free_slots = new_ptr->h_next; + hp = hole_head; + + /* If this block's address is numerically less than the lowest hole currently + * available, or if no holes are currently available, put this hole on the + * front of the hole list. + */ + if (hp == NIL_HOLE || base <= hp->h_base) { + /* Block to be freed goes on front of the hole list. */ + new_ptr->h_next = hp; + hole_head = new_ptr; + merge(new_ptr); + return; + } + + /* Block to be returned does not go on front of hole list. */ + while (hp != NIL_HOLE && base > hp->h_base) { + prev_ptr = hp; + hp = hp->h_next; + } + + /* We found where it goes. Insert block after 'prev_ptr'. */ + new_ptr->h_next = prev_ptr->h_next; + prev_ptr->h_next = new_ptr; + merge(prev_ptr); /* sequence is 'prev_ptr', 'new_ptr', 'hp' */ +} + +/*===========================================================================* + * del_slot * + *===========================================================================*/ +PRIVATE void del_slot(prev_ptr, hp) +register struct hole *prev_ptr; /* pointer to hole entry just ahead of 'hp' */ +register struct hole *hp; /* pointer to hole entry to be removed */ +{ +/* Remove an entry from the hole list. This procedure is called when a + * request to allocate memory removes a hole in its entirety, thus reducing + * the numbers of holes in memory, and requiring the elimination of one + * entry in the hole list. + */ + + if (hp == hole_head) + hole_head = hp->h_next; + else + prev_ptr->h_next = hp->h_next; + + hp->h_next = free_slots; + free_slots = hp; +} + +/*===========================================================================* + * merge * + *===========================================================================*/ +PRIVATE void merge(hp) +register struct hole *hp; /* ptr to hole to merge with its successors */ +{ +/* Check for contiguous holes and merge any found. Contiguous holes can occur + * when a block of memory is freed, and it happens to abut another hole on + * either or both ends. The pointer 'hp' points to the first of a series of + * three holes that can potentially all be merged together. + */ + + register struct hole *next_ptr; + + /* If 'hp' points to the last hole, no merging is possible. If it does not, + * try to absorb its successor into it and free the successor's table entry. + */ + if ( (next_ptr = hp->h_next) == NIL_HOLE) return; + if (hp->h_base + hp->h_len == next_ptr->h_base) { + hp->h_len += next_ptr->h_len; /* first one gets second one's mem */ + del_slot(hp, next_ptr); + } else { + hp = next_ptr; + } + + /* If 'hp' now points to the last hole, return; otherwise, try to absorb its + * successor into it. + */ + if ( (next_ptr = hp->h_next) == NIL_HOLE) return; + if (hp->h_base + hp->h_len == next_ptr->h_base) { + hp->h_len += next_ptr->h_len; + del_slot(hp, next_ptr); + } +} + +/*===========================================================================* + * mem_init * + *===========================================================================*/ +PUBLIC void mem_init(free) +phys_clicks *free; /* memory size summaries */ +{ +/* Initialize hole lists. There are two lists: 'hole_head' points to a linked + * list of all the holes (unused memory) in the system; 'free_slots' points to + * a linked list of table entries that are not in use. Initially, the former + * list has one entry for each chunk of physical memory, and the second + * list links together the remaining table slots. As memory becomes more + * fragmented in the course of time (i.e., the initial big holes break up into + * smaller holes), new table slots are needed to represent them. These slots + * are taken from the list headed by 'free_slots'. + */ + struct memory mem[NR_MEMS]; /* chunks of physical memory */ + int i; + register struct hole *hp; + phys_clicks base; /* base address of chunk */ + phys_clicks size; /* size of chunk */ + message mess; + + /* Get a copy of the physical memory chunks found at the kernel. */ + if ((i=sys_getmemchunks(mem)) != OK) + panic("MM couldn't get mem chunks",i); + + /* Put all holes on the free list. */ + for (hp = &hole[0]; hp < &hole[NR_HOLES]; hp++) hp->h_next = hp + 1; + hole[NR_HOLES-1].h_next = NIL_HOLE; + hole_head = NIL_HOLE; + free_slots = &hole[0]; + + /* Ask the kernel for chunks of physical memory and allocate holes. */ + *free = 0; + for (i=0; i<NR_MEMS; i++) { + if (mem[i].size > 0) { + free_mem(mem[i].base, mem[i].size); + *free += mem[i].size; +#if ENABLE_SWAP + if (swap_base < mem[i].base + mem[i].size) + swap_base = mem[i].base+mem[i].size; +#endif + } + } + +#if ENABLE_SWAP + /* The swap area is represented as a hole above and separate of regular + * memory. A hole at the size of the swap file is allocated on "swapon". + */ + swap_base++; /* make separate */ + swap_maxsize = 0 - swap_base; /* maximum we can possibly use */ +#endif +} + +#if ENABLE_SWAP +/*===========================================================================* + * swap_on * + *===========================================================================*/ +PUBLIC int swap_on(file, offset, size) +char *file; /* file to swap on */ +u32_t offset, size; /* area on swap file to use */ +{ +/* Turn swapping on. */ + + if (swap_fd != -1) return(EBUSY); /* already have swap? */ + + tell_fs(CHDIR, who, FALSE, 0); /* be like the caller for open() */ + if ((swap_fd = open(file, O_RDWR)) < 0) return(-errno); + swap_offset = offset; + size >>= CLICK_SHIFT; + if (size > swap_maxsize) size = swap_maxsize; + if (size > 0) free_mem(swap_base, (phys_clicks) size); +} + +/*===========================================================================* + * swap_off * + *===========================================================================*/ +PUBLIC int swap_off() +{ +/* Turn swapping off. */ + struct mproc *rmp; + struct hole *hp, *prev_ptr; + + if (swap_fd == -1) return(OK); /* can't turn off what isn't on */ + + /* Put all swapped out processes on the inswap queue and swap in. */ + for (rmp = &mproc[LOW_USER]; rmp < &mproc[NR_PROCS]; rmp++) { + if (rmp->mp_flags & ONSWAP) swap_inqueue(rmp); + } + swap_in(); + + /* All in memory? */ + for (rmp = &mproc[LOW_USER]; rmp < &mproc[NR_PROCS]; rmp++) { + if (rmp->mp_flags & ONSWAP) return(ENOMEM); + } + + /* Yes. Remove the swap hole and close the swap file descriptor. */ + for (hp = hole_head; hp != NIL_HOLE; prev_ptr = hp, hp = hp->h_next) { + if (hp->h_base >= swap_base) { + del_slot(prev_ptr, hp); + hp = hole_head; + } + } + close(swap_fd); + swap_fd = -1; + return(OK); +} + +/*===========================================================================* + * swap_inqueue * + *===========================================================================*/ +PUBLIC void swap_inqueue(rmp) +register struct mproc *rmp; /* process to add to the queue */ +{ +/* Put a swapped out process on the queue of processes to be swapped in. This + * happens when such a process gets a signal, or if a reply message must be + * sent, like when a process doing a wait() has a child that exits. + */ + struct mproc **pmp; + + if (rmp->mp_flags & SWAPIN) return; /* already queued */ + + + for (pmp = &in_queue; *pmp != NULL; pmp = &(*pmp)->mp_swapq) {} + *pmp = rmp; + rmp->mp_swapq = NULL; + rmp->mp_flags |= SWAPIN; +} + +/*===========================================================================* + * swap_in * + *===========================================================================*/ +PUBLIC void swap_in() +{ +/* Try to swap in a process on the inswap queue. We want to send it a message, + * interrupt it, or something. + */ + struct mproc **pmp, *rmp; + phys_clicks old_base, new_base, size; + off_t off; + int proc_nr; + + pmp = &in_queue; + while ((rmp = *pmp) != NULL) { + proc_nr = (rmp - mproc); + size = rmp->mp_seg[S].mem_vir + rmp->mp_seg[S].mem_len + - rmp->mp_seg[D].mem_vir; + + if (!(rmp->mp_flags & SWAPIN)) { + /* Guess it got killed. (Queue is cleaned here.) */ + *pmp = rmp->mp_swapq; + continue; + } else + if ((new_base = alloc_mem(size)) == NO_MEM) { + /* No memory for this one, try the next. */ + pmp = &rmp->mp_swapq; + } else { + /* We've found memory. Update map and swap in. */ + old_base = rmp->mp_seg[D].mem_phys; + rmp->mp_seg[D].mem_phys = new_base; + rmp->mp_seg[S].mem_phys = rmp->mp_seg[D].mem_phys + + (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir); + sys_newmap(proc_nr, rmp->mp_seg); + off = swap_offset + ((off_t) (old_base-swap_base)<<CLICK_SHIFT); + lseek(swap_fd, off, SEEK_SET); + rw_seg(0, swap_fd, proc_nr, D, (phys_bytes)size << CLICK_SHIFT); + free_mem(old_base, size); + rmp->mp_flags &= ~(ONSWAP|SWAPIN); + *pmp = rmp->mp_swapq; + check_pending(rmp); /* a signal may have waked this one */ + } + } +} + +/*===========================================================================* + * swap_out * + *===========================================================================*/ +PRIVATE int swap_out() +{ +/* Try to find a process that can be swapped out. Candidates are those blocked + * on a system call that MM handles, like wait(), pause() or sigsuspend(). + */ + struct mproc *rmp; + struct hole *hp, *prev_ptr; + phys_clicks old_base, new_base, size; + off_t off; + int proc_nr; + + rmp = outswap; + do { + if (++rmp == &mproc[NR_PROCS]) rmp = &mproc[LOW_USER]; + + /* A candidate? */ + if (!(rmp->mp_flags & (PAUSED | WAITING | SIGSUSPENDED))) continue; + + /* Already on swap or otherwise to be avoided? */ + if (rmp->mp_flags & (TRACED | REPLY | ONSWAP)) continue; + + /* Got one, find a swap hole and swap it out. */ + proc_nr = (rmp - mproc); + size = rmp->mp_seg[S].mem_vir + rmp->mp_seg[S].mem_len + - rmp->mp_seg[D].mem_vir; + + prev_ptr = NIL_HOLE; + for (hp = hole_head; hp != NIL_HOLE; prev_ptr = hp, hp = hp->h_next) { + if (hp->h_base >= swap_base && hp->h_len >= size) break; + } + if (hp == NIL_HOLE) continue; /* oops, not enough swapspace */ + new_base = hp->h_base; + hp->h_base += size; + hp->h_len -= size; + if (hp->h_len == 0) del_slot(prev_ptr, hp); + + off = swap_offset + ((off_t) (new_base - swap_base) << CLICK_SHIFT); + lseek(swap_fd, off, SEEK_SET); + rw_seg(1, swap_fd, proc_nr, D, (phys_bytes)size << CLICK_SHIFT); + old_base = rmp->mp_seg[D].mem_phys; + rmp->mp_seg[D].mem_phys = new_base; + rmp->mp_seg[S].mem_phys = rmp->mp_seg[D].mem_phys + + (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir); + sys_newmap(proc_nr, rmp->mp_seg); + free_mem(old_base, size); + rmp->mp_flags |= ONSWAP; + + outswap = rmp; /* next time start here */ + return(TRUE); + } while (rmp != outswap); + + return(FALSE); /* no candidate found */ +} +#endif /* SWAP */ diff --git a/servers/mm/break.c b/servers/mm/break.c new file mode 100644 index 000000000..fbbf27d24 --- /dev/null +++ b/servers/mm/break.c @@ -0,0 +1,170 @@ +/* The MINIX model of memory allocation reserves a fixed amount of memory for + * the combined text, data, and stack segments. The amount used for a child + * process created by FORK is the same as the parent had. If the child does + * an EXEC later, the new size is taken from the header of the file EXEC'ed. + * + * The layout in memory consists of the text segment, followed by the data + * segment, followed by a gap (unused memory), followed by the stack segment. + * The data segment grows upward and the stack grows downward, so each can + * take memory from the gap. If they meet, the process must be killed. The + * procedures in this file deal with the growth of the data and stack segments. + * + * The entry points into this file are: + * do_brk: BRK/SBRK system calls to grow or shrink the data segment + * adjust: see if a proposed segment adjustment is allowed + * size_ok: see if the segment sizes are feasible + */ + +#include "mm.h" +#include <signal.h> +#include "mproc.h" +#include "param.h" + +#define DATA_CHANGED 1 /* flag value when data segment size changed */ +#define STACK_CHANGED 2 /* flag value when stack size changed */ + +/*===========================================================================* + * do_brk * + *===========================================================================*/ +PUBLIC int do_brk() +{ +/* Perform the brk(addr) system call. + * + * The call is complicated by the fact that on some machines (e.g., 8088), + * the stack pointer can grow beyond the base of the stack segment without + * anybody noticing it. + * The parameter, 'addr' is the new virtual address in D space. + */ + + register struct mproc *rmp; + int r; + vir_bytes v, new_sp; + vir_clicks new_clicks; + + rmp = mp; + v = (vir_bytes) m_in.addr; + new_clicks = (vir_clicks) ( ((long) v + CLICK_SIZE - 1) >> CLICK_SHIFT); + if (new_clicks < rmp->mp_seg[D].mem_vir) { + rmp->mp_reply.reply_ptr = (char *) -1; + return(ENOMEM); + } + new_clicks -= rmp->mp_seg[D].mem_vir; + if ((r=p_getsp(who, &new_sp)) != OK) /* ask kernel for current sp value */ + panic("MM couldn't get stack pointer", r); + r = adjust(rmp, new_clicks, new_sp); + rmp->mp_reply.reply_ptr = (r == OK ? m_in.addr : (char *) -1); + return(r); /* return new address or -1 */ +} + + +/*===========================================================================* + * adjust * + *===========================================================================*/ +PUBLIC int adjust(rmp, data_clicks, sp) +register struct mproc *rmp; /* whose memory is being adjusted? */ +vir_clicks data_clicks; /* how big is data segment to become? */ +vir_bytes sp; /* new value of sp */ +{ +/* See if data and stack segments can coexist, adjusting them if need be. + * Memory is never allocated or freed. Instead it is added or removed from the + * gap between data segment and stack segment. If the gap size becomes + * negative, the adjustment of data or stack fails and ENOMEM is returned. + */ + + register struct mem_map *mem_sp, *mem_dp; + vir_clicks sp_click, gap_base, lower, old_clicks; + int changed, r, ft; + long base_of_stack, delta; /* longs avoid certain problems */ + + mem_dp = &rmp->mp_seg[D]; /* pointer to data segment map */ + mem_sp = &rmp->mp_seg[S]; /* pointer to stack segment map */ + changed = 0; /* set when either segment changed */ + + if (mem_sp->mem_len == 0) return(OK); /* don't bother init */ + + /* See if stack size has gone negative (i.e., sp too close to 0xFFFF...) */ + base_of_stack = (long) mem_sp->mem_vir + (long) mem_sp->mem_len; + sp_click = sp >> CLICK_SHIFT; /* click containing sp */ + if (sp_click >= base_of_stack) return(ENOMEM); /* sp too high */ + + /* Compute size of gap between stack and data segments. */ + delta = (long) mem_sp->mem_vir - (long) sp_click; + lower = (delta > 0 ? sp_click : mem_sp->mem_vir); + + /* Add a safety margin for future stack growth. Impossible to do right. */ +#define SAFETY_BYTES (384 * sizeof(char *)) +#define SAFETY_CLICKS ((SAFETY_BYTES + CLICK_SIZE - 1) / CLICK_SIZE) + gap_base = mem_dp->mem_vir + data_clicks + SAFETY_CLICKS; + if (lower < gap_base) return(ENOMEM); /* data and stack collided */ + + /* Update data length (but not data orgin) on behalf of brk() system call. */ + old_clicks = mem_dp->mem_len; + if (data_clicks != mem_dp->mem_len) { + mem_dp->mem_len = data_clicks; + changed |= DATA_CHANGED; + } + + /* Update stack length and origin due to change in stack pointer. */ + if (delta > 0) { + mem_sp->mem_vir -= delta; + mem_sp->mem_phys -= delta; + mem_sp->mem_len += delta; + changed |= STACK_CHANGED; + } + + /* Do the new data and stack segment sizes fit in the address space? */ + ft = (rmp->mp_flags & SEPARATE); + r = size_ok(ft, rmp->mp_seg[T].mem_len, rmp->mp_seg[D].mem_len, + rmp->mp_seg[S].mem_len, rmp->mp_seg[D].mem_vir, rmp->mp_seg[S].mem_vir); + if (r == OK) { + if (changed) sys_newmap((int)(rmp - mproc), rmp->mp_seg); + return(OK); + } + + /* New sizes don't fit or require too many page/segment registers. Restore.*/ + if (changed & DATA_CHANGED) mem_dp->mem_len = old_clicks; + if (changed & STACK_CHANGED) { + mem_sp->mem_vir += delta; + mem_sp->mem_phys += delta; + mem_sp->mem_len -= delta; + } + return(ENOMEM); +} + + +/*===========================================================================* + * size_ok * + *===========================================================================*/ +PUBLIC int size_ok(file_type, tc, dc, sc, dvir, s_vir) +int file_type; /* SEPARATE or 0 */ +vir_clicks tc; /* text size in clicks */ +vir_clicks dc; /* data size in clicks */ +vir_clicks sc; /* stack size in clicks */ +vir_clicks dvir; /* virtual address for start of data seg */ +vir_clicks s_vir; /* virtual address for start of stack seg */ +{ +/* Check to see if the sizes are feasible and enough segmentation registers + * exist. On a machine with eight 8K pages, text, data, stack sizes of + * (32K, 16K, 16K) will fit, but (33K, 17K, 13K) will not, even though the + * former is bigger (64K) than the latter (63K). Even on the 8088 this test + * is needed, since the data and stack may not exceed 4096 clicks. + */ + +#if (CHIP == INTEL && _WORD_SIZE == 2) + int pt, pd, ps; /* segment sizes in pages */ + + pt = ( (tc << CLICK_SHIFT) + PAGE_SIZE - 1)/PAGE_SIZE; + pd = ( (dc << CLICK_SHIFT) + PAGE_SIZE - 1)/PAGE_SIZE; + ps = ( (sc << CLICK_SHIFT) + PAGE_SIZE - 1)/PAGE_SIZE; + + if (file_type == SEPARATE) { + if (pt > MAX_PAGES || pd + ps > MAX_PAGES) return(ENOMEM); + } else { + if (pt + pd + ps > MAX_PAGES) return(ENOMEM); + } +#endif + + if (dvir + dc > s_vir) return(ENOMEM); + + return(OK); +} diff --git a/servers/mm/const.h b/servers/mm/const.h new file mode 100644 index 000000000..fa4d1c885 --- /dev/null +++ b/servers/mm/const.h @@ -0,0 +1,14 @@ +/* Constants used by the Memory Manager. */ + +#define NO_MEM ((phys_clicks) 0) /* returned by alloc_mem() with mem is up */ + +#if (CHIP == INTEL && _WORD_SIZE == 2) +/* These definitions are used in size_ok and are not needed for 386. + * The 386 segment granularity is 1 for segments smaller than 1M and 4096 + * above that. + */ +#define PAGE_SIZE 16 /* how many bytes in a page (s.b.HCLICK_SIZE)*/ +#define MAX_PAGES 4096 /* how many pages in the virtual addr space */ +#endif + +#define INIT_PID 1 /* init's process id number */ diff --git a/servers/mm/exec.c b/servers/mm/exec.c new file mode 100644 index 000000000..3d4c82cd9 --- /dev/null +++ b/servers/mm/exec.c @@ -0,0 +1,598 @@ +/* This file handles the EXEC system call. It performs the work as follows: + * - see if the permissions allow the file to be executed + * - read the header and extract the sizes + * - fetch the initial args and environment from the user space + * - allocate the memory for the new process + * - copy the initial stack from MM to the process + * - read in the text and data segments and copy to the process + * - take care of setuid and setgid bits + * - fix up 'mproc' table + * - tell kernel about EXEC + * - save offset to initial argc (for ps) + * + * The entry points into this file are: + * do_exec: perform the EXEC system call + * rw_seg: read or write a segment from or to a file + * find_share: find a process whose text segment can be shared + */ + +#include "mm.h" +#include <sys/stat.h> +#include <minix/callnr.h> +#include <minix/com.h> +#include <a.out.h> +#include <signal.h> +#include <string.h> +#include "mproc.h" +#include "param.h" + +FORWARD _PROTOTYPE( int new_mem, (struct mproc *sh_mp, vir_bytes text_bytes, + vir_bytes data_bytes, vir_bytes bss_bytes, + vir_bytes stk_bytes, phys_bytes tot_bytes) ); +FORWARD _PROTOTYPE( void patch_ptr, (char stack[ARG_MAX], vir_bytes base) ); +FORWARD _PROTOTYPE( int insert_arg, (char stack[ARG_MAX], + vir_bytes *stk_bytes, char *arg, int replace) ); +FORWARD _PROTOTYPE( char *patch_stack, (int fd, char stack[ARG_MAX], + vir_bytes *stk_bytes, char *script) ); +FORWARD _PROTOTYPE( int read_header, (int fd, int *ft, vir_bytes *text_bytes, + vir_bytes *data_bytes, vir_bytes *bss_bytes, + phys_bytes *tot_bytes, long *sym_bytes, vir_clicks sc, + vir_bytes *pc) ); + +#define ESCRIPT (-2000) /* Returned by read_header for a #! script. */ +#define PTRSIZE sizeof(char *) /* Size of pointers in argv[] and envp[]. */ + +/*===========================================================================* + * do_exec * + *===========================================================================*/ +PUBLIC int do_exec() +{ +/* Perform the execve(name, argv, envp) call. The user library builds a + * complete stack image, including pointers, args, environ, etc. The stack + * is copied to a buffer inside MM, and then to the new core image. + */ + + register struct mproc *rmp; + struct mproc *sh_mp; + int m, r, fd, ft, sn; + static char mbuf[ARG_MAX]; /* buffer for stack and zeroes */ + static char name_buf[PATH_MAX]; /* the name of the file to exec */ + char *new_sp, *name, *basename; + vir_bytes src, dst, text_bytes, data_bytes, bss_bytes, stk_bytes, vsp; + phys_bytes tot_bytes; /* total space for program, including gap */ + long sym_bytes; + vir_clicks sc; + struct stat s_buf[2], *s_p; + vir_bytes pc; + + /* Do some validity checks. */ + rmp = mp; + stk_bytes = (vir_bytes) m_in.stack_bytes; + if (stk_bytes > ARG_MAX) return(ENOMEM); /* stack too big */ + if (m_in.exec_len <= 0 || m_in.exec_len > PATH_MAX) return(EINVAL); + + /* Get the exec file name and see if the file is executable. */ + src = (vir_bytes) m_in.exec_name; + dst = (vir_bytes) name_buf; + r = sys_datacopy(who, (vir_bytes) src, + MM_PROC_NR, (vir_bytes) dst, (phys_bytes) m_in.exec_len); + if (r != OK) return(r); /* file name not in user data segment */ + + /* Fetch the stack from the user before destroying the old core image. */ + src = (vir_bytes) m_in.stack_ptr; + dst = (vir_bytes) mbuf; + r = sys_datacopy(who, (vir_bytes) src, + MM_PROC_NR, (vir_bytes) dst, (phys_bytes)stk_bytes); + + if (r != OK) return(EACCES); /* can't fetch stack (e.g. bad virtual addr) */ + + r = 0; /* r = 0 (first attempt), or 1 (interpreted script) */ + name = name_buf; /* name of file to exec. */ + do { + s_p = &s_buf[r]; + tell_fs(CHDIR, who, FALSE, 0); /* switch to the user's FS environ */ + fd = allowed(name, s_p, X_BIT); /* is file executable? */ + if (fd < 0) return(fd); /* file was not executable */ + + /* Read the file header and extract the segment sizes. */ + sc = (stk_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT; + + m = read_header(fd, &ft, &text_bytes, &data_bytes, &bss_bytes, + &tot_bytes, &sym_bytes, sc, &pc); + if (m != ESCRIPT || ++r > 1) break; + } while ((name = patch_stack(fd, mbuf, &stk_bytes, name_buf)) != NULL); + + if (m < 0) { + close(fd); /* something wrong with header */ + return(stk_bytes > ARG_MAX ? ENOMEM : ENOEXEC); + } + + /* Can the process' text be shared with that of one already running? */ + sh_mp = find_share(rmp, s_p->st_ino, s_p->st_dev, s_p->st_ctime); + + /* Allocate new memory and release old memory. Fix map and tell kernel. */ + r = new_mem(sh_mp, text_bytes, data_bytes, bss_bytes, stk_bytes, tot_bytes); + if (r != OK) { + close(fd); /* insufficient core or program too big */ + return(r); + } + + /* Save file identification to allow it to be shared. */ + rmp->mp_ino = s_p->st_ino; + rmp->mp_dev = s_p->st_dev; + rmp->mp_ctime = s_p->st_ctime; + + /* Patch up stack and copy it from MM to new core image. */ + vsp = (vir_bytes) rmp->mp_seg[S].mem_vir << CLICK_SHIFT; + vsp += (vir_bytes) rmp->mp_seg[S].mem_len << CLICK_SHIFT; + vsp -= stk_bytes; + patch_ptr(mbuf, vsp); + src = (vir_bytes) mbuf; + r = sys_datacopy(MM_PROC_NR, (vir_bytes) src, + who, (vir_bytes) vsp, (phys_bytes)stk_bytes); + if (r != OK) panic("do_exec stack copy err on", who); + + /* Read in text and data segments. */ + if (sh_mp != NULL) { + lseek(fd, (off_t) text_bytes, SEEK_CUR); /* shared: skip text */ + } else { + rw_seg(0, fd, who, T, text_bytes); + } + rw_seg(0, fd, who, D, data_bytes); + + close(fd); /* don't need exec file any more */ + + /* Take care of setuid/setgid bits. */ + if ((rmp->mp_flags & TRACED) == 0) { /* suppress if tracing */ + if (s_buf[0].st_mode & I_SET_UID_BIT) { + rmp->mp_effuid = s_buf[0].st_uid; + tell_fs(SETUID,who, (int)rmp->mp_realuid, (int)rmp->mp_effuid); + } + if (s_buf[0].st_mode & I_SET_GID_BIT) { + rmp->mp_effgid = s_buf[0].st_gid; + tell_fs(SETGID,who, (int)rmp->mp_realgid, (int)rmp->mp_effgid); + } + } + + /* Save offset to initial argc (for ps) */ + rmp->mp_procargs = vsp; + + /* Fix 'mproc' fields, tell kernel that exec is done, reset caught sigs. */ + for (sn = 1; sn <= _NSIG; sn++) { + if (sigismember(&rmp->mp_catch, sn)) { + sigdelset(&rmp->mp_catch, sn); + rmp->mp_sigact[sn].sa_handler = SIG_DFL; + sigemptyset(&rmp->mp_sigact[sn].sa_mask); + } + } + + rmp->mp_flags &= ~SEPARATE; /* turn off SEPARATE bit */ + rmp->mp_flags |= ft; /* turn it on for separate I & D files */ + new_sp = (char *) vsp; + + tell_fs(EXEC, who, 0, 0); /* allow FS to handle FD_CLOEXEC files */ + + /* System will save command line for debugging, ps(1) output, etc. */ + basename = strrchr(name, '/'); + if (basename == NULL) basename = name; else basename++; + strncpy(rmp->mp_name, basename, PROC_NAME_LEN-1); + rmp->mp_name[PROC_NAME_LEN] = '\0'; + sys_exec(who, new_sp, rmp->mp_flags & TRACED, basename, pc); + + return(SUSPEND); /* no reply, new program just runs */ +} + + +/*===========================================================================* + * read_header * + *===========================================================================*/ +PRIVATE int read_header(fd, ft, text_bytes, data_bytes, bss_bytes, + tot_bytes, sym_bytes, sc, pc) +int fd; /* file descriptor for reading exec file */ +int *ft; /* place to return ft number */ +vir_bytes *text_bytes; /* place to return text size */ +vir_bytes *data_bytes; /* place to return initialized data size */ +vir_bytes *bss_bytes; /* place to return bss size */ +phys_bytes *tot_bytes; /* place to return total size */ +long *sym_bytes; /* place to return symbol table size */ +vir_clicks sc; /* stack size in clicks */ +vir_bytes *pc; /* program entry point (initial PC) */ +{ +/* Read the header and extract the text, data, bss and total sizes from it. */ + + int m, ct; + vir_clicks tc, dc, s_vir, dvir; + phys_clicks totc; + struct exec hdr; /* a.out header is read in here */ + + /* Read the header and check the magic number. The standard MINIX header + * is defined in <a.out.h>. It consists of 8 chars followed by 6 longs. + * Then come 4 more longs that are not used here. + * Byte 0: magic number 0x01 + * Byte 1: magic number 0x03 + * Byte 2: normal = 0x10 (not checked, 0 is OK), separate I/D = 0x20 + * Byte 3: CPU type, Intel 16 bit = 0x04, Intel 32 bit = 0x10, + * Motorola = 0x0B, Sun SPARC = 0x17 + * Byte 4: Header length = 0x20 + * Bytes 5-7 are not used. + * + * Now come the 6 longs + * Bytes 8-11: size of text segments in bytes + * Bytes 12-15: size of initialized data segment in bytes + * Bytes 16-19: size of bss in bytes + * Bytes 20-23: program entry point + * Bytes 24-27: total memory allocated to program (text, data + stack) + * Bytes 28-31: size of symbol table in bytes + * The longs are represented in a machine dependent order, + * little-endian on the 8088, big-endian on the 68000. + * The header is followed directly by the text and data segments, and the + * symbol table (if any). The sizes are given in the header. Only the + * text and data segments are copied into memory by exec. The header is + * used here only. The symbol table is for the benefit of a debugger and + * is ignored here. + */ + + if ((m= read(fd, &hdr, A_MINHDR)) < 2) return(ENOEXEC); + + /* Interpreted script? */ + if (((char *) &hdr)[0] == '#' && ((char *) &hdr)[1] == '!') return(ESCRIPT); + + if (m != A_MINHDR) return(ENOEXEC); + + /* Check magic number, cpu type, and flags. */ + if (BADMAG(hdr)) return(ENOEXEC); +#if (CHIP == INTEL && _WORD_SIZE == 2) + if (hdr.a_cpu != A_I8086) return(ENOEXEC); +#endif +#if (CHIP == INTEL && _WORD_SIZE == 4) + if (hdr.a_cpu != A_I80386) return(ENOEXEC); +#endif + if ((hdr.a_flags & ~(A_NSYM | A_EXEC | A_SEP)) != 0) return(ENOEXEC); + + *ft = ( (hdr.a_flags & A_SEP) ? SEPARATE : 0); /* separate I & D or not */ + + /* Get text and data sizes. */ + *text_bytes = (vir_bytes) hdr.a_text; /* text size in bytes */ + *data_bytes = (vir_bytes) hdr.a_data; /* data size in bytes */ + *bss_bytes = (vir_bytes) hdr.a_bss; /* bss size in bytes */ + *tot_bytes = hdr.a_total; /* total bytes to allocate for prog */ + *sym_bytes = hdr.a_syms; /* symbol table size in bytes */ + if (*tot_bytes == 0) return(ENOEXEC); + + if (*ft != SEPARATE) { + /* If I & D space is not separated, it is all considered data. Text=0*/ + *data_bytes += *text_bytes; + *text_bytes = 0; + } + *pc = hdr.a_entry; /* initial address to start execution */ + + /* Check to see if segment sizes are feasible. */ + tc = ((unsigned long) *text_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT; + dc = (*data_bytes + *bss_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT; + totc = (*tot_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT; + if (dc >= totc) return(ENOEXEC); /* stack must be at least 1 click */ + dvir = (*ft == SEPARATE ? 0 : tc); + s_vir = dvir + (totc - sc); + m = size_ok(*ft, tc, dc, sc, dvir, s_vir); + ct = hdr.a_hdrlen & BYTE; /* header length */ + if (ct > A_MINHDR) lseek(fd, (off_t) ct, SEEK_SET); /* skip unused hdr */ + return(m); +} + + +/*===========================================================================* + * new_mem * + *===========================================================================*/ +PRIVATE int new_mem(sh_mp, text_bytes, data_bytes,bss_bytes,stk_bytes,tot_bytes) +struct mproc *sh_mp; /* text can be shared with this process */ +vir_bytes text_bytes; /* text segment size in bytes */ +vir_bytes data_bytes; /* size of initialized data in bytes */ +vir_bytes bss_bytes; /* size of bss in bytes */ +vir_bytes stk_bytes; /* size of initial stack segment in bytes */ +phys_bytes tot_bytes; /* total memory to allocate, including gap */ +{ +/* Allocate new memory and release the old memory. Change the map and report + * the new map to the kernel. Zero the new core image's bss, gap and stack. + */ + + register struct mproc *rmp; + vir_clicks text_clicks, data_clicks, gap_clicks, stack_clicks, tot_clicks; + phys_clicks new_base; + static char zero[1024]; /* used to zero bss */ + phys_bytes bytes, base, count, bss_offset; + + /* No need to allocate text if it can be shared. */ + if (sh_mp != NULL) text_bytes = 0; + + /* Allow the old data to be swapped out to make room. (Which is really a + * waste of time, because we are going to throw it away anyway.) + */ + rmp->mp_flags |= WAITING; + + /* Acquire the new memory. Each of the 4 parts: text, (data+bss), gap, + * and stack occupies an integral number of clicks, starting at click + * boundary. The data and bss parts are run together with no space. + */ + text_clicks = ((unsigned long) text_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT; + data_clicks = (data_bytes + bss_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT; + stack_clicks = (stk_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT; + tot_clicks = (tot_bytes + CLICK_SIZE - 1) >> CLICK_SHIFT; + gap_clicks = tot_clicks - data_clicks - stack_clicks; + if ( (int) gap_clicks < 0) return(ENOMEM); + + /* Try to allocate memory for the new process. */ + new_base = alloc_mem(text_clicks + tot_clicks); + if (new_base == NO_MEM) return(ENOMEM); + + /* We've got memory for the new core image. Release the old one. */ + rmp = mp; + + if (find_share(rmp, rmp->mp_ino, rmp->mp_dev, rmp->mp_ctime) == NULL) { + /* No other process shares the text segment, so free it. */ + free_mem(rmp->mp_seg[T].mem_phys, rmp->mp_seg[T].mem_len); + } + /* Free the data and stack segments. */ + free_mem(rmp->mp_seg[D].mem_phys, + rmp->mp_seg[S].mem_vir + rmp->mp_seg[S].mem_len - rmp->mp_seg[D].mem_vir); + + /* We have now passed the point of no return. The old core image has been + * forever lost, memory for a new core image has been allocated. Set up + * and report new map. + */ + if (sh_mp != NULL) { + /* Share the text segment. */ + rmp->mp_seg[T] = sh_mp->mp_seg[T]; + } else { + rmp->mp_seg[T].mem_phys = new_base; + rmp->mp_seg[T].mem_vir = 0; + rmp->mp_seg[T].mem_len = text_clicks; + } + rmp->mp_seg[D].mem_phys = new_base + text_clicks; + rmp->mp_seg[D].mem_vir = 0; + rmp->mp_seg[D].mem_len = data_clicks; + rmp->mp_seg[S].mem_phys = rmp->mp_seg[D].mem_phys + data_clicks + gap_clicks; + rmp->mp_seg[S].mem_vir = rmp->mp_seg[D].mem_vir + data_clicks + gap_clicks; + rmp->mp_seg[S].mem_len = stack_clicks; + +#if (CHIP == M68000) + rmp->mp_seg[T].mem_vir = 0; + rmp->mp_seg[D].mem_vir = rmp->mp_seg[T].mem_len; + rmp->mp_seg[S].mem_vir = rmp->mp_seg[D].mem_vir + rmp->mp_seg[D].mem_len + gap_clicks; +#endif + + sys_newmap(who, rmp->mp_seg); /* report new map to the kernel */ + + /* The old memory may have been swapped out, but the new memory is real. */ + rmp->mp_flags &= ~(WAITING|ONSWAP|SWAPIN); + + /* Zero the bss, gap, and stack segment. */ + bytes = (phys_bytes)(data_clicks + gap_clicks + stack_clicks) << CLICK_SHIFT; + base = (phys_bytes) rmp->mp_seg[D].mem_phys << CLICK_SHIFT; + bss_offset = (data_bytes >> CLICK_SHIFT) << CLICK_SHIFT; + base += bss_offset; + bytes -= bss_offset; + + while (bytes > 0) { + count = MIN(bytes, (phys_bytes) sizeof(zero)); + if (sys_copy(MM_PROC_NR, D, (phys_bytes) zero, + ABS, 0, base, count) != OK) { + panic("new_mem can't zero", NO_NUM); + } + base += count; + bytes -= count; + } + return(OK); +} + + +/*===========================================================================* + * patch_ptr * + *===========================================================================*/ +PRIVATE void patch_ptr(stack, base) +char stack[ARG_MAX]; /* pointer to stack image within MM */ +vir_bytes base; /* virtual address of stack base inside user */ +{ +/* When doing an exec(name, argv, envp) call, the user builds up a stack + * image with arg and env pointers relative to the start of the stack. Now + * these pointers must be relocated, since the stack is not positioned at + * address 0 in the user's address space. + */ + + char **ap, flag; + vir_bytes v; + + flag = 0; /* counts number of 0-pointers seen */ + ap = (char **) stack; /* points initially to 'nargs' */ + ap++; /* now points to argv[0] */ + while (flag < 2) { + if (ap >= (char **) &stack[ARG_MAX]) return; /* too bad */ + if (*ap != NULL) { + v = (vir_bytes) *ap; /* v is relative pointer */ + v += base; /* relocate it */ + *ap = (char *) v; /* put it back */ + } else { + flag++; + } + ap++; + } +} + + +/*===========================================================================* + * insert_arg * + *===========================================================================*/ +PRIVATE int insert_arg(stack, stk_bytes, arg, replace) +char stack[ARG_MAX]; /* pointer to stack image within MM */ +vir_bytes *stk_bytes; /* size of initial stack */ +char *arg; /* argument to prepend/replace as new argv[0] */ +int replace; +{ +/* Patch the stack so that arg will become argv[0]. Be careful, the stack may + * be filled with garbage, although it normally looks like this: + * nargs argv[0] ... argv[nargs-1] NULL envp[0] ... NULL + * followed by the strings "pointed" to by the argv[i] and the envp[i]. The + * pointers are really offsets from the start of stack. + * Return true iff the operation succeeded. + */ + int offset, a0, a1, old_bytes = *stk_bytes; + + /* Prepending arg adds at least one string and a zero byte. */ + offset = strlen(arg) + 1; + + a0 = (int) ((char **) stack)[1]; /* argv[0] */ + if (a0 < 4 * PTRSIZE || a0 >= old_bytes) return(FALSE); + + a1 = a0; /* a1 will point to the strings to be moved */ + if (replace) { + /* Move a1 to the end of argv[0][] (argv[1] if nargs > 1). */ + do { + if (a1 == old_bytes) return(FALSE); + --offset; + } while (stack[a1++] != 0); + } else { + offset += PTRSIZE; /* new argv[0] needs new pointer in argv[] */ + a0 += PTRSIZE; /* location of new argv[0][]. */ + } + + /* stack will grow by offset bytes (or shrink by -offset bytes) */ + if ((*stk_bytes += offset) > ARG_MAX) return(FALSE); + + /* Reposition the strings by offset bytes */ + memmove(stack + a1 + offset, stack + a1, old_bytes - a1); + + strcpy(stack + a0, arg); /* Put arg in the new space. */ + + if (!replace) { + /* Make space for a new argv[0]. */ + memmove(stack + 2 * PTRSIZE, stack + 1 * PTRSIZE, a0 - 2 * PTRSIZE); + + ((char **) stack)[0]++; /* nargs++; */ + } + /* Now patch up argv[] and envp[] by offset. */ + patch_ptr(stack, (vir_bytes) offset); + ((char **) stack)[1] = (char *) a0; /* set argv[0] correctly */ + return(TRUE); +} + + +/*===========================================================================* + * patch_stack * + *===========================================================================*/ +PRIVATE char *patch_stack(fd, stack, stk_bytes, script) +int fd; /* file descriptor to open script file */ +char stack[ARG_MAX]; /* pointer to stack image within MM */ +vir_bytes *stk_bytes; /* size of initial stack */ +char *script; /* name of script to interpret */ +{ +/* Patch the argument vector to include the path name of the script to be + * interpreted, and all strings on the #! line. Returns the path name of + * the interpreter. + */ + char *sp, *interp = NULL; + int n; + enum { INSERT=FALSE, REPLACE=TRUE }; + + /* Make script[] the new argv[0]. */ + if (!insert_arg(stack, stk_bytes, script, REPLACE)) return(NULL); + + if (lseek(fd, 2L, 0) == -1 /* just behind the #! */ + || (n= read(fd, script, PATH_MAX)) < 0 /* read line one */ + || (sp= memchr(script, '\n', n)) == NULL) /* must be a proper line */ + return(NULL); + + /* Move sp backwards through script[], prepending each string to stack. */ + for (;;) { + /* skip spaces behind argument. */ + while (sp > script && (*--sp == ' ' || *sp == '\t')) {} + if (sp == script) break; + + sp[1] = 0; + /* Move to the start of the argument. */ + while (sp > script && sp[-1] != ' ' && sp[-1] != '\t') --sp; + + interp = sp; + if (!insert_arg(stack, stk_bytes, sp, INSERT)) return(NULL); + } + + /* Round *stk_bytes up to the size of a pointer for alignment contraints. */ + *stk_bytes= ((*stk_bytes + PTRSIZE - 1) / PTRSIZE) * PTRSIZE; + + close(fd); + return(interp); +} + + +/*===========================================================================* + * rw_seg * + *===========================================================================*/ +PUBLIC void rw_seg(rw, fd, proc, seg, seg_bytes0) +int rw; /* 0 = read, 1 = write */ +int fd; /* file descriptor to read from / write to */ +int proc; /* process number */ +int seg; /* T, D, or S */ +phys_bytes seg_bytes0; /* how much is to be transferred? */ +{ +/* Transfer text or data from/to a file and copy to/from a process segment. + * This procedure is a little bit tricky. The logical way to transfer a + * segment would be block by block and copying each block to/from the user + * space one at a time. This is too slow, so we do something dirty here, + * namely send the user space and virtual address to the file system in the + * upper 10 bits of the file descriptor, and pass it the user virtual address + * instead of a MM address. The file system extracts these parameters when + * gets a read or write call from the memory manager, which is the only process + * that is permitted to use this trick. The file system then copies the whole + * segment directly to/from user space, bypassing MM completely. + * + * The byte count on read is usually smaller than the segment count, because + * a segment is padded out to a click multiple, and the data segment is only + * partially initialized. + */ + + int new_fd, bytes, r; + char *ubuf_ptr; + struct mem_map *sp = &mproc[proc].mp_seg[seg]; + phys_bytes seg_bytes = seg_bytes0; + + new_fd = (proc << 7) | (seg << 5) | fd; + ubuf_ptr = (char *) ((vir_bytes) sp->mem_vir << CLICK_SHIFT); + + while (seg_bytes != 0) { +#define MM_CHUNK_SIZE 8192 + bytes = MIN((INT_MAX / MM_CHUNK_SIZE) * MM_CHUNK_SIZE, seg_bytes); + if (rw == 0) { + r = read(new_fd, ubuf_ptr, bytes); + } else { + r = write(new_fd, ubuf_ptr, bytes); + } + if (r != bytes) break; + ubuf_ptr += bytes; + seg_bytes -= bytes; + } +} + + +/*===========================================================================* + * find_share * + *===========================================================================*/ +PUBLIC struct mproc *find_share(mp_ign, ino, dev, ctime) +struct mproc *mp_ign; /* process that should not be looked at */ +ino_t ino; /* parameters that uniquely identify a file */ +dev_t dev; +time_t ctime; +{ +/* Look for a process that is the file <ino, dev, ctime> in execution. Don't + * accidentally "find" mp_ign, because it is the process on whose behalf this + * call is made. + */ + struct mproc *sh_mp; + + for (sh_mp = &mproc[INIT_PROC_NR]; sh_mp < &mproc[NR_PROCS]; sh_mp++) { + if (!(sh_mp->mp_flags & SEPARATE)) continue; + if (sh_mp == mp_ign) continue; + if (sh_mp->mp_ino != ino) continue; + if (sh_mp->mp_dev != dev) continue; + if (sh_mp->mp_ctime != ctime) continue; + return sh_mp; + } + return(NULL); +} diff --git a/servers/mm/forkexit.c b/servers/mm/forkexit.c new file mode 100644 index 000000000..c791328c0 --- /dev/null +++ b/servers/mm/forkexit.c @@ -0,0 +1,288 @@ +/* This file deals with creating processes (via FORK) and deleting them (via + * EXIT/WAIT). When a process forks, a new slot in the 'mproc' table is + * allocated for it, and a copy of the parent's core image is made for the + * child. Then the kernel and file system are informed. A process is removed + * from the 'mproc' table when two events have occurred: (1) it has exited or + * been killed by a signal, and (2) the parent has done a WAIT. If the process + * exits first, it continues to occupy a slot until the parent does a WAIT. + * + * The entry points into this file are: + * do_fork: perform the FORK system call + * do_mm_exit: perform the EXIT system call (by calling mm_exit()) + * mm_exit: actually do the exiting + * do_wait: perform the WAITPID or WAIT system call + */ + + +#include "mm.h" +#include <sys/wait.h> +#include <minix/callnr.h> +#include <minix/com.h> +#include <minix/utils.h> +#include <signal.h> +#include "mproc.h" +#include "param.h" + +#define LAST_FEW 2 /* last few slots reserved for superuser */ + +PRIVATE pid_t next_pid = INIT_PID+1; /* next pid to be assigned */ + +FORWARD _PROTOTYPE (void cleanup, (register struct mproc *child) ); + +/*===========================================================================* + * do_fork * + *===========================================================================*/ +PUBLIC int do_fork() +{ +/* The process pointed to by 'mp' has forked. Create a child process. */ + + register struct mproc *rmp; /* pointer to parent */ + register struct mproc *rmc; /* pointer to child */ + int i, child_nr, t; + phys_clicks prog_clicks, child_base; + phys_bytes prog_bytes, parent_abs, child_abs; /* Intel only */ + + /* If tables might fill up during FORK, don't even start since recovery half + * way through is such a nuisance. + */ + rmp = mp; + if ((procs_in_use == NR_PROCS) || + (procs_in_use >= NR_PROCS-LAST_FEW && rmp->mp_effuid != 0)) + { + printf("MM: proc table full!\n"); + return(EAGAIN); + } + + /* Determine how much memory to allocate. Only the data and stack need to + * be copied, because the text segment is either shared or of zero length. + */ + prog_clicks = (phys_clicks) rmp->mp_seg[S].mem_len; + prog_clicks += (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir); + prog_bytes = (phys_bytes) prog_clicks << CLICK_SHIFT; + if ( (child_base = alloc_mem(prog_clicks)) == NO_MEM) return(ENOMEM); + + /* Create a copy of the parent's core image for the child. */ + child_abs = (phys_bytes) child_base << CLICK_SHIFT; + parent_abs = (phys_bytes) rmp->mp_seg[D].mem_phys << CLICK_SHIFT; + i = sys_physcopy(parent_abs, child_abs, prog_bytes); + if (i < 0) panic("do_fork can't copy", i); + + /* Find a slot in 'mproc' for the child process. A slot must exist. */ + for (rmc = &mproc[0]; rmc < &mproc[NR_PROCS]; rmc++) + if ( (rmc->mp_flags & IN_USE) == 0) break; + + /* Set up the child and its memory map; copy its 'mproc' slot from parent. */ + child_nr = (int)(rmc - mproc); /* slot number of the child */ + procs_in_use++; + *rmc = *rmp; /* copy parent's process slot to child's */ + + rmc->mp_parent = who; /* record child's parent */ + rmc->mp_flags &= (IN_USE|SEPARATE); /* inherit only these flags */ + + /* A separate I&D child keeps the parents text segment. The data and stack + * segments must refer to the new copy. + */ + if (!(rmc->mp_flags & SEPARATE)) rmc->mp_seg[T].mem_phys = child_base; + rmc->mp_seg[D].mem_phys = child_base; + rmc->mp_seg[S].mem_phys = rmc->mp_seg[D].mem_phys + + (rmp->mp_seg[S].mem_vir - rmp->mp_seg[D].mem_vir); + rmc->mp_exitstatus = 0; + rmc->mp_sigstatus = 0; + + /* Find a free pid for the child and put it in the table. */ + do { + t = 0; /* 't' = 0 means pid still free */ + next_pid = (next_pid < 30000 ? next_pid + 1 : INIT_PID + 1); + for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) + if (rmp->mp_pid == next_pid || rmp->mp_procgrp == next_pid) { + t = 1; + break; + } + rmc->mp_pid = next_pid; /* assign pid to child */ + } while (t); + + /* Tell kernel and file system about the (now successful) FORK. */ + sys_fork(who, child_nr, rmc->mp_pid); + tell_fs(FORK, who, child_nr, rmc->mp_pid); + + /* Report child's memory map to kernel. */ + sys_newmap(child_nr, rmc->mp_seg); + + /* Reply to child to wake it up. */ + setreply(child_nr, 0); + return(next_pid); /* child's pid */ +} + + +/*===========================================================================* + * do_mm_exit * + *===========================================================================*/ +PUBLIC int do_mm_exit() +{ +/* Perform the exit(status) system call. The real work is done by mm_exit(), + * which is also called when a process is killed by a signal. + */ + + mm_exit(mp, m_in.status); + return(SUSPEND); /* can't communicate from beyond the grave */ +} + + +/*===========================================================================* + * mm_exit * + *===========================================================================*/ +PUBLIC void mm_exit(rmp, exit_status) +register struct mproc *rmp; /* pointer to the process to be terminated */ +int exit_status; /* the process' exit status (for parent) */ +{ +/* A process is done. Release most of the process' possessions. If its + * parent is waiting, release the rest, else keep the process slot and + * become a zombie. + */ + + register int proc_nr; + int parent_waiting, right_child; + pid_t pidarg, procgrp; + struct mproc *p_mp; + + proc_nr = (int) (rmp - mproc); /* get process slot number */ + + /* Remember a session leader's process group. */ + procgrp = (rmp->mp_pid == mp->mp_procgrp) ? mp->mp_procgrp : 0; + + /* If the exited process has a timer pending, kill it. */ + if (rmp->mp_flags & ALARM_ON) set_alarm(proc_nr, (unsigned) 0); + + /* Tell the kernel and FS that the process is no longer runnable. */ + tell_fs(EXIT, proc_nr, 0, 0); /* file system can free the proc slot */ + sys_xit(rmp->mp_parent, proc_nr); + + /* Release the memory occupied by the child. */ + if (find_share(rmp, rmp->mp_ino, rmp->mp_dev, rmp->mp_ctime) == NULL) { + /* No other process shares the text segment, so free it. */ + free_mem(rmp->mp_seg[T].mem_phys, rmp->mp_seg[T].mem_len); + } + /* Free the data and stack segments. */ + free_mem(rmp->mp_seg[D].mem_phys, + rmp->mp_seg[S].mem_vir + rmp->mp_seg[S].mem_len - rmp->mp_seg[D].mem_vir); + + /* The process slot can only be freed if the parent has done a WAIT. */ + rmp->mp_exitstatus = (char) exit_status; + + p_mp = &mproc[rmp->mp_parent]; /* process' parent */ + pidarg = p_mp->mp_wpid; /* who's being waited for? */ + parent_waiting = p_mp->mp_flags & WAITING; + + right_child = /* child meets one of the 3 tests? */ + (pidarg == -1 || pidarg == rmp->mp_pid || -pidarg == rmp->mp_procgrp); + + if (parent_waiting && right_child) { + cleanup(rmp); /* tell parent and release child slot */ + } else { + rmp->mp_flags = IN_USE|ZOMBIE; /* parent not waiting, zombify child */ + sig_proc(p_mp, SIGCHLD); /* send parent a "child died" signal */ + } + + /* If the process has children, disinherit them. INIT is the new parent. */ + for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) { + if (rmp->mp_flags & IN_USE && rmp->mp_parent == proc_nr) { + /* 'rmp' now points to a child to be disinherited. */ + rmp->mp_parent = INIT_PROC_NR; + parent_waiting = mproc[INIT_PROC_NR].mp_flags & WAITING; + if (parent_waiting && (rmp->mp_flags & ZOMBIE)) cleanup(rmp); + } + } + + /* Send a hangup to the process' process group if it was a session leader. */ + if (procgrp != 0) check_sig(-procgrp, SIGHUP); +} + + +/*===========================================================================* + * do_waitpid * + *===========================================================================*/ +PUBLIC int do_waitpid() +{ +/* A process wants to wait for a child to terminate. If one is already waiting, + * go clean it up and let this WAIT call terminate. Otherwise, really wait. + * Both WAIT and WAITPID are handled by this code. + */ + + register struct mproc *rp; + int pidarg, options, children; + + /* A process calling WAIT never gets a reply in the usual way at the end + * of the main loop (unless WNOHANG is set or no qualifying child exists). + * If a child has already exited, the routine cleanup() sends the reply + * to awaken the caller. + */ + + /* Set internal variables, depending on whether this is WAIT or WAITPID. */ + pidarg = (call_nr == WAIT ? -1 : m_in.pid); /* 1st param of waitpid */ + options = (call_nr == WAIT ? 0 : m_in.sig_nr); /* 3rd param of waitpid */ + if (pidarg == 0) pidarg = -mp->mp_procgrp; /* pidarg < 0 ==> proc grp */ + + /* Is there a child waiting to be collected? At this point, pidarg != 0: + * pidarg > 0 means pidarg is pid of a specific process to wait for + * pidarg == -1 means wait for any child + * pidarg < -1 means wait for any child whose process group = -pidarg + */ + children = 0; + for (rp = &mproc[0]; rp < &mproc[NR_PROCS]; rp++) { + if ( (rp->mp_flags & IN_USE) && rp->mp_parent == who) { + /* The value of pidarg determines which children qualify. */ + if (pidarg > 0 && pidarg != rp->mp_pid) continue; + if (pidarg < -1 && -pidarg != rp->mp_procgrp) continue; + + children++; /* this child is acceptable */ + if (rp->mp_flags & ZOMBIE) { + /* This child meets the pid test and has exited. */ + cleanup(rp); /* this child has already exited */ + return(SUSPEND); + } + if ((rp->mp_flags & STOPPED) && rp->mp_sigstatus) { + /* This child meets the pid test and is being traced.*/ + mp->mp_reply.reply_res2 = 0177|(rp->mp_sigstatus << 8); + rp->mp_sigstatus = 0; + return(rp->mp_pid); + } + } + } + + /* No qualifying child has exited. Wait for one, unless none exists. */ + if (children > 0) { + /* At least 1 child meets the pid test exists, but has not exited. */ + if (options & WNOHANG) return(0); /* parent does not want to wait */ + mp->mp_flags |= WAITING; /* parent wants to wait */ + mp->mp_wpid = (pid_t) pidarg; /* save pid for later */ + return(SUSPEND); /* do not reply, let it wait */ + } else { + /* No child even meets the pid test. Return error immediately. */ + return(ECHILD); /* no - parent has no children */ + } +} + + +/*===========================================================================* + * cleanup * + *===========================================================================*/ +PRIVATE void cleanup(child) +register struct mproc *child; /* tells which process is exiting */ +{ +/* Finish off the exit of a process. The process has exited or been killed + * by a signal, and its parent is waiting. + */ + + struct mproc *parent = &mproc[child->mp_parent]; + int exitstatus; + + /* Wake up the parent. */ + exitstatus = (child->mp_exitstatus << 8) | (child->mp_sigstatus & 0377); + parent->mp_reply.reply_res2 = exitstatus; + setreply(child->mp_parent, child->mp_pid); + parent->mp_flags &= ~WAITING; /* parent no longer waiting */ + + /* Release the process table entry. */ + child->mp_flags = 0; + procs_in_use--; +} diff --git a/servers/mm/getset.c b/servers/mm/getset.c new file mode 100644 index 000000000..cd8d6d55a --- /dev/null +++ b/servers/mm/getset.c @@ -0,0 +1,78 @@ +/* This file handles the 4 system calls that get and set uids and gids. + * It also handles getpid(), setsid(), and getpgrp(). The code for each + * one is so tiny that it hardly seemed worthwhile to make each a separate + * function. + */ + +#include "mm.h" +#include <minix/callnr.h> +#include <signal.h> +#include "mproc.h" +#include "param.h" + +/*===========================================================================* + * do_getset * + *===========================================================================*/ +PUBLIC int do_getset() +{ +/* Handle GETUID, GETGID, GETPID, GETPGRP, SETUID, SETGID, SETSID. The four + * GETs and SETSID return their primary results in 'r'. GETUID, GETGID, and + * GETPID also return secondary results (the effective IDs, or the parent + * process ID) in 'reply_res2', which is returned to the user. + */ + + register struct mproc *rmp = mp; + register int r; + + switch(call_nr) { + case GETUID: + r = rmp->mp_realuid; + rmp->mp_reply.reply_res2 = rmp->mp_effuid; + break; + + case GETGID: + r = rmp->mp_realgid; + rmp->mp_reply.reply_res2 = rmp->mp_effgid; + break; + + case GETPID: + r = mproc[who].mp_pid; + rmp->mp_reply.reply_res2 = mproc[rmp->mp_parent].mp_pid; + break; + + case SETUID: + if (rmp->mp_realuid != (uid_t) m_in.usr_id && + rmp->mp_effuid != SUPER_USER) + return(EPERM); + rmp->mp_realuid = (uid_t) m_in.usr_id; + rmp->mp_effuid = (uid_t) m_in.usr_id; + tell_fs(SETUID, who, rmp->mp_realuid, rmp->mp_effuid); + r = OK; + break; + + case SETGID: + if (rmp->mp_realgid != (gid_t) m_in.grp_id && + rmp->mp_effuid != SUPER_USER) + return(EPERM); + rmp->mp_realgid = (gid_t) m_in.grp_id; + rmp->mp_effgid = (gid_t) m_in.grp_id; + tell_fs(SETGID, who, rmp->mp_realgid, rmp->mp_effgid); + r = OK; + break; + + case SETSID: + if (rmp->mp_procgrp == rmp->mp_pid) return(EPERM); + rmp->mp_procgrp = rmp->mp_pid; + tell_fs(SETSID, who, 0, 0); + /*FALL THROUGH*/ + + case GETPGRP: + r = rmp->mp_procgrp; + break; + + default: + r = EINVAL; + break; + } + return(r); +} diff --git a/servers/mm/glo.h b/servers/mm/glo.h new file mode 100644 index 000000000..6f99a59b1 --- /dev/null +++ b/servers/mm/glo.h @@ -0,0 +1,20 @@ +/* EXTERN should be extern except in table.c */ +#ifdef _TABLE +#undef EXTERN +#define EXTERN +#endif + +/* Global variables. */ +EXTERN struct mproc *mp; /* ptr to 'mproc' slot of current process */ +EXTERN int procs_in_use; /* how many processes are marked as IN_USE */ + +/* The parameters of the call are kept here. */ +EXTERN message m_in; /* the incoming message itself is kept here. */ +EXTERN int who; /* caller's proc number */ +EXTERN int call_nr; /* system call number */ + +extern _PROTOTYPE (int (*call_vec[]), (void) ); /* system call handlers */ +extern char core_name[]; /* file name where core images are produced */ +EXTERN sigset_t core_sset; /* which signals cause core images */ +EXTERN sigset_t ign_sset; /* which signals are by default ignored */ + diff --git a/servers/mm/main.c b/servers/mm/main.c new file mode 100644 index 000000000..9dde099c0 --- /dev/null +++ b/servers/mm/main.c @@ -0,0 +1,197 @@ +/* This file contains the main program of the memory manager and some related + * procedures. When MINIX starts up, the kernel runs for a little while, + * initializing itself and its tasks, and then it runs MM and FS. Both MM + * and FS initialize themselves as far as they can. FS then makes a call to + * MM, because MM has to wait for FS to acquire a RAM disk. MM asks the + * kernel for all free memory and starts serving requests. + * + * The entry points into this file are: + * main: starts MM running + * setreply: set the reply to be sent to process making an MM system call + */ + +#include "mm.h" +#include <minix/utils.h> +#include <minix/callnr.h> +#include <minix/com.h> +#include <signal.h> +#include <fcntl.h> +#include <sys/ioc_memory.h> +#include "mproc.h" +#include "param.h" + +FORWARD _PROTOTYPE( void get_work, (void) ); +FORWARD _PROTOTYPE( void mm_init, (void) ); + +#define click_to_round_k(n) \ + ((unsigned) ((((unsigned long) (n) << CLICK_SHIFT) + 512) / 1024)) + +/*===========================================================================* + * main * + *===========================================================================*/ +PUBLIC void main() +{ +/* Main routine of the memory manager. */ + + int result, proc_nr; + struct mproc *rmp; + + mm_init(); /* initialize memory manager tables */ + + /* This is MM's main loop- get work and do it, forever and forever. */ + while (TRUE) { + get_work(); /* wait for an MM system call */ + + /* Check for system notifications first. Special cases. */ + if (call_nr == HARD_STOP) { /* MINIX is shutting down */ + check_sig(-1, SIGKILL); /* kill all processes */ + sys_exit(0); + /* never reached */ + } else if (call_nr == KSIG_PENDING) { /* signals pending */ + (void) ksig_pending(); + result = SUSPEND; /* don't reply */ + } + /* Else, if the system call number is valid, perform the call. */ + else if ((unsigned) call_nr >= NCALLS) { + result = ENOSYS; + } else { + result = (*call_vec[call_nr])(); + } + + /* Send the results back to the user to indicate completion. */ + if (result != SUSPEND) setreply(who, result); + + swap_in(); /* maybe a process can be swapped in? */ + + /* Send out all pending reply messages, including the answer to + * the call just made above. The processes must not be swapped out. + */ + for (proc_nr=0, rmp=mproc; proc_nr < NR_PROCS; proc_nr++, rmp++) { + if ((rmp->mp_flags & (REPLY | ONSWAP)) == REPLY) { + if (send(proc_nr, &rmp->mp_reply) != OK) + panic("MM can't reply to", proc_nr); + rmp->mp_flags &= ~REPLY; + } + } + } +} + + +/*===========================================================================* + * get_work * + *===========================================================================*/ +PRIVATE void get_work() +{ +/* Wait for the next message and extract useful information from it. */ + + if (receive(ANY, &m_in) != OK) panic("MM receive error", NO_NUM); + who = m_in.m_source; /* who sent the message */ + call_nr = m_in.m_type; /* system call number */ + + /* Process slot of caller. Misuse MM's own process slot if the kernel is + * calling. The can happen in case of pending kernel signals. + */ + mp = &mproc[who < 0 ? MM_PROC_NR : who]; +} + + +/*===========================================================================* + * setreply * + *===========================================================================*/ +PUBLIC void setreply(proc_nr, result) +int proc_nr; /* process to reply to */ +int result; /* result of the call (usually OK or error #)*/ +{ +/* Fill in a reply message to be sent later to a user process. System calls + * may occasionally fill in other fields, this is only for the main return + * value, and for setting the "must send reply" flag. + */ + + register struct mproc *rmp = &mproc[proc_nr]; + + rmp->mp_reply.reply_res = result; + rmp->mp_flags |= REPLY; /* reply pending */ + + if (rmp->mp_flags & ONSWAP) + swap_inqueue(rmp); /* must swap this process back in */ +} + + +/*===========================================================================* + * mm_init * + *===========================================================================*/ +PRIVATE void mm_init() +{ +/* Initialize the memory manager. */ + int s; + static char core_sigs[] = { SIGQUIT, SIGILL, SIGTRAP, SIGABRT, + SIGEMT, SIGFPE, SIGUSR1, SIGSEGV, SIGUSR2 }; + static char ign_sigs[] = { SIGCHLD }; + register int proc_nr; + register struct mproc *rmp; + register char *sig_ptr; + phys_clicks ram_clicks, total_clicks, minix_clicks, free_clicks; + message mess; + struct mem_map kernel_map[NR_LOCAL_SEGS]; + int mem; + + /* Build the set of signals which cause core dumps, and the set of signals + * that are by default ignored. + */ + sigemptyset(&core_sset); + for (sig_ptr = core_sigs; sig_ptr < core_sigs+sizeof(core_sigs); sig_ptr++) + sigaddset(&core_sset, *sig_ptr); + sigemptyset(&ign_sset); + for (sig_ptr = ign_sigs; sig_ptr < ign_sigs+sizeof(ign_sigs); sig_ptr++) + sigaddset(&ign_sset, *sig_ptr); + + /* Get the memory map of the kernel to see how much memory it uses. */ + if ((s=p_getmap(SYSTASK, kernel_map)) != OK) + panic("MM couldn't get proc entry of SYSTASK",s); + minix_clicks = (kernel_map[S].mem_phys + kernel_map[S].mem_len) + - kernel_map[T].mem_phys; + + /* Initialize MM's tables. Request a copy of the system image table that + * is defined at the kernel level to see which slots to fill in. + */ + for (proc_nr = 0; proc_nr <= INIT_PROC_NR; proc_nr++) { + rmp = &mproc[proc_nr]; + rmp->mp_flags |= IN_USE; + if ((s=p_getmap(proc_nr, rmp->mp_seg)) != OK) + panic("MM couldn't get proc entry",s); + if (rmp->mp_seg[T].mem_len != 0) rmp->mp_flags |= SEPARATE; + minix_clicks += (rmp->mp_seg[S].mem_phys + rmp->mp_seg[S].mem_len) + - rmp->mp_seg[T].mem_phys; + } + mproc[INIT_PROC_NR].mp_pid = INIT_PID; + sigemptyset(&mproc[INIT_PROC_NR].mp_ignore); + sigemptyset(&mproc[INIT_PROC_NR].mp_catch); + procs_in_use = LOW_USER + 1; + + /* Wait for FS to send a message telling the RAM disk size then go "on-line". + */ + if (receive(FS_PROC_NR, &mess) != OK) + panic("MM can't obtain RAM disk size from FS", NO_NUM); + + ram_clicks = mess.MEM_CHUNK_SIZE; + + /* Initialize tables to all physical mem. */ + mem_init(&free_clicks); + total_clicks = minix_clicks + ram_clicks + free_clicks; + + /* Print memory information. */ + printf("Memory size=%uK ", click_to_round_k(total_clicks)); + printf("MINIX=%uK ", click_to_round_k(minix_clicks)); + printf("RAM disk=%uK ", click_to_round_k(ram_clicks)); + printf("Available=%uK\n\n", click_to_round_k(free_clicks)); + + /* Tell FS to continue. */ + if (send(FS_PROC_NR, &mess) != OK) + panic("MM can't sync up with FS", NO_NUM); + + /* Tell the memory task where my process table is for the sake of ps(1). */ + if ((mem = open("/dev/ram", O_RDWR)) != -1) { + ioctl(mem, MIOCSPSINFO, (void *) mproc); + close(mem); + } +} diff --git a/servers/mm/misc.c b/servers/mm/misc.c new file mode 100644 index 000000000..00aeb83bc --- /dev/null +++ b/servers/mm/misc.c @@ -0,0 +1,219 @@ +/* Miscellaneous system calls. Author: Kees J. Bot + * 31 Mar 2000 + * The entry points into this file are: + * do_reboot: kill all processes, then reboot system + * do_svrctl: memory manager control + * do_getsysinfo: request copy of MM data structure + */ + +#include "mm.h" +#include <minix/callnr.h> +#include <signal.h> +#include <sys/svrctl.h> +#include <minix/com.h> +#include <minix/utils.h> +#include <string.h> +#include "mproc.h" +#include "param.h" + +FORWARD _PROTOTYPE( char *find_key, (const char *params, const char *key)); + +/* MM gets a copy of all boot monitor parameters. */ +PRIVATE char monitor_params[128*sizeof(char *)]; + +/*=====================================================================* + * do_getsysinfo * + *=====================================================================*/ +PUBLIC int do_getsysinfo() +{ + return(OK); +} + + +/*=====================================================================* + * do_reboot * + *=====================================================================*/ +PUBLIC int do_reboot() +{ + register struct mproc *rmp = mp; + char monitor_code[32*sizeof(char *)]; + + if (rmp->mp_effuid != SUPER_USER) return(EPERM); + + switch (m_in.reboot_flag) { + case RBT_HALT: + case RBT_REBOOT: + case RBT_PANIC: + case RBT_RESET: + break; + case RBT_MONITOR: + if (m_in.reboot_size >= sizeof(monitor_code)) return(EINVAL); + if (sys_datacopy(who, (vir_bytes) m_in.reboot_code, + MM_PROC_NR, (vir_bytes) monitor_code, + (phys_bytes) (m_in.reboot_size+1)) != OK) return(EFAULT); + if (monitor_code[m_in.reboot_size] != 0) return(EINVAL); + break; + default: + return(EINVAL); + } + + check_sig(-1, SIGKILL); /* kill all processes except init */ + tell_fs(REBOOT,0,0,0); /* tell FS to prepare for shutdown */ + + sys_abort(m_in.reboot_flag, MM_PROC_NR, monitor_code, m_in.reboot_size); + sys_exit(0); +} + +/*=====================================================================* + * do_svrctl * + *=====================================================================*/ +PUBLIC int do_svrctl() +{ + static int initialized = 0; + int s, req; + vir_bytes ptr; + req = m_in.svrctl_req; + ptr = (vir_bytes) m_in.svrctl_argp; + + /* Initialize private copy of monitor parameters on first call. */ + if (! initialized) { + if ((s=sys_getmonparams(monitor_params, sizeof(monitor_params))) != OK) + printf("MM: Warning couldn't get copy of monitor params: %d\n",s); + else + initialized = 1; + } + + /* Binary compatibility check. */ + if (req == SYSGETENV) { + printf("SYSGETENV used by %d\n", who); + req = MMGETPARAM; + } + + /* Is the request for the kernel? Forward it, except for SYSGETENV. */ + if (((req >> 8) & 0xFF) == 'S') { + + /* Simply forward call to the SYSTEM task. */ + return(sys_svrctl(who, req, mp->mp_effuid == SUPER_USER, ptr)); + } + + /* Control operations local to the MM. */ + switch(req) { + case MMGETPARAM: { + struct sysgetenv sysgetenv; + char search_key[64]; + char *val_start; + size_t val_len; + size_t copy_len; + + /* Check if boot monitor parameters are in place. */ + if (! initialized) return(EAGAIN); + + /* Copy sysgetenv structure to MM. */ + if (sys_datacopy(who, ptr, SELF, (vir_bytes) &sysgetenv, + sizeof(sysgetenv)) != OK) return(EFAULT); + + if (sysgetenv.keylen == 0) { /* copy all parameters */ + val_start = monitor_params; + val_len = sizeof(monitor_params); + } + else { /* lookup value for key */ + /* Try to get a copy of the requested key. */ + if (sysgetenv.keylen > sizeof(search_key)) return(EINVAL); + if ((s = sys_datacopy(who, (vir_bytes) sysgetenv.key, + SELF, (vir_bytes) search_key, sysgetenv.keylen)) != OK) + return(s); + + /* Make sure key is null-terminated and lookup value. */ + search_key[sysgetenv.keylen-1]= '\0'; + if ((val_start = find_key(monitor_params, search_key)) == NULL) + return(ESRCH); + val_len = strlen(val_start) + 1; + } + + /* Value found, make the actual copy (as far as possible). */ + copy_len = MAX(val_len, sysgetenv.vallen); + if ((s=sys_datacopy(SELF, (vir_bytes) val_start, + who, (vir_bytes) sysgetenv.val, copy_len)) != OK) + return(s); + + /* See if it fits in the client's buffer. */ + return (copy_len > sysgetenv.vallen) ? E2BIG : OK; + } + case MMSIGNON: { + /* A user process becomes a task. Simulate an exit by + * releasing a waiting parent and disinheriting children. + */ + struct mproc *rmp; + pid_t pidarg; + + if (mp->mp_effuid != SUPER_USER) return(EPERM); + + rmp = &mproc[mp->mp_parent]; + tell_fs(EXIT, who, 0, 0); + + pidarg = rmp->mp_wpid; + if ((rmp->mp_flags & WAITING) && (pidarg == -1 + || pidarg == mp->mp_pid || -pidarg == mp->mp_procgrp)) + { + /* Wake up the parent. */ + rmp->mp_reply.reply_res2 = 0; + setreply(mp->mp_parent, mp->mp_pid); + rmp->mp_flags &= ~WAITING; + } + + /* Disinherit children. */ + for (rmp = &mproc[0]; rmp < &mproc[NR_PROCS]; rmp++) { + if (rmp->mp_flags & IN_USE && rmp->mp_parent == who) { + rmp->mp_parent = INIT_PROC_NR; + } + } + + /* Become like MM and FS. */ + mp->mp_pid = mp->mp_procgrp = 0; + mp->mp_parent = 0; + return(OK); } + +#if ENABLE_SWAP + case MMSWAPON: { + struct mmswapon swapon; + + if (mp->mp_effuid != SUPER_USER) return(EPERM); + + if (sys_datacopy(who, (phys_bytes) ptr, + MM_PROC_NR, (phys_bytes) &swapon, + (phys_bytes) sizeof(swapon)) != OK) return(EFAULT); + + return(swap_on(swapon.file, swapon.offset, swapon.size)); } + + case MMSWAPOFF: { + if (mp->mp_effuid != SUPER_USER) return(EPERM); + + return(swap_off()); } +#endif /* SWAP */ + + default: + return(EINVAL); + } +} + +/*==========================================================================* + * find_key * + *==========================================================================*/ +PRIVATE char *find_key(params,name) +const char *params; +const char *name; +{ + register const char *namep; + register char *envp; + + for (envp = (char *) params; *envp != 0;) { + for (namep = name; *namep != 0 && *namep == *envp; namep++, envp++) + ; + if (*namep == '\0' && *envp == '=') + return(envp + 1); + while (*envp++ != 0) + ; + } + return(NULL); +} + diff --git a/servers/mm/mm.h b/servers/mm/mm.h new file mode 100644 index 000000000..9170251e1 --- /dev/null +++ b/servers/mm/mm.h @@ -0,0 +1,25 @@ +/* This is the master header for mm. It includes some other files + * and defines the principal constants. + */ +#define _POSIX_SOURCE 1 /* tell headers to include POSIX stuff */ +#define _MINIX 1 /* tell headers to include MINIX stuff */ +#define _SYSTEM 1 /* tell headers that this is the kernel */ + +/* The following are so basic, all the *.c files get them automatically. */ +#include <minix/config.h> /* MUST be first */ +#include <ansi.h> /* MUST be second */ +#include <sys/types.h> +#include <minix/const.h> +#include <minix/type.h> + +#include <fcntl.h> +#include <unistd.h> +#include <minix/syslib.h> + +#include <limits.h> +#include <errno.h> + +#include "const.h" +#include "type.h" +#include "proto.h" +#include "glo.h" diff --git a/servers/mm/mproc.h b/servers/mm/mproc.h new file mode 100644 index 000000000..2973cfe8e --- /dev/null +++ b/servers/mm/mproc.h @@ -0,0 +1,62 @@ +/* This table has one slot per process. It contains all the memory management + * information for each process. Among other things, it defines the text, data + * and stack segments, uids and gids, and various flags. The kernel and file + * systems have tables that are also indexed by process, with the contents + * of corresponding slots referring to the same process in all three. + */ + +EXTERN struct mproc { + struct mem_map mp_seg[NR_LOCAL_SEGS]; /* points to text, data, stack */ + char mp_exitstatus; /* storage for status when process exits */ + char mp_sigstatus; /* storage for signal # for killed procs */ + pid_t mp_pid; /* process id */ + pid_t mp_procgrp; /* pid of process group (used for signals) */ + pid_t mp_wpid; /* pid this process is waiting for */ + int mp_parent; /* index of parent process */ + + /* Real and effective uids and gids. */ + uid_t mp_realuid; /* process' real uid */ + uid_t mp_effuid; /* process' effective uid */ + gid_t mp_realgid; /* process' real gid */ + gid_t mp_effgid; /* process' effective gid */ + + /* File identification for sharing. */ + ino_t mp_ino; /* inode number of file */ + dev_t mp_dev; /* device number of file system */ + time_t mp_ctime; /* inode changed time */ + + /* Signal handling information. */ + sigset_t mp_ignore; /* 1 means ignore the signal, 0 means don't */ + sigset_t mp_catch; /* 1 means catch the signal, 0 means don't */ + sigset_t mp_sigmask; /* signals to be blocked */ + sigset_t mp_sigmask2; /* saved copy of mp_sigmask */ + sigset_t mp_sigpending; /* signals being blocked */ + struct sigaction mp_sigact[_NSIG + 1]; /* as in sigaction(2) */ + vir_bytes mp_sigreturn; /* address of C library __sigreturn function */ + + /* Backwards compatibility for signals. */ + sighandler_t mp_func; /* all sigs vectored to a single user fcn */ + + unsigned mp_flags; /* flag bits */ + vir_bytes mp_procargs; /* ptr to proc's initial stack arguments */ + struct mproc *mp_swapq; /* queue of procs waiting to be swapped in */ + message mp_reply; /* reply message to be sent to one */ + + char mp_name[PROC_NAME_LEN]; /* process name */ +} mproc[NR_PROCS]; + +/* Flag values */ +#define IN_USE 0x001 /* set when 'mproc' slot in use */ +#define WAITING 0x002 /* set by WAIT system call */ +#define ZOMBIE 0x004 /* set by EXIT, cleared by WAIT */ +#define PAUSED 0x008 /* set by PAUSE system call */ +#define ALARM_ON 0x010 /* set when SIGALRM timer started */ +#define SEPARATE 0x020 /* set if file is separate I & D space */ +#define TRACED 0x040 /* set if process is to be traced */ +#define STOPPED 0x080 /* set if process stopped for tracing */ +#define SIGSUSPENDED 0x100 /* set by SIGSUSPEND system call */ +#define REPLY 0x200 /* set if a reply message is pending */ +#define ONSWAP 0x400 /* set if data segment is swapped out */ +#define SWAPIN 0x800 /* set if on the "swap this in" queue */ + +#define NIL_MPROC ((struct mproc *) 0) diff --git a/servers/mm/param.h b/servers/mm/param.h new file mode 100644 index 000000000..e5764812c --- /dev/null +++ b/servers/mm/param.h @@ -0,0 +1,46 @@ +/* The following names are synonyms for the variables in the input message. */ +#define addr m1_p1 +#define exec_name m1_p1 +#define exec_len m1_i1 +#define func m6_f1 +#define grp_id m1_i1 +#define namelen m1_i1 +#define pid m1_i1 +#define seconds m1_i1 +#define sig m6_i1 +#define stack_bytes m1_i2 +#define stack_ptr m1_p2 +#define status m1_i1 +#define usr_id m1_i1 +#define request m2_i2 +#define taddr m2_l1 +#define data m2_l2 +#define sig_nr m1_i2 +#define sig_nsa m1_p1 +#define sig_osa m1_p2 +#define sig_ret m1_p3 +#define sig_set m2_l1 +#define sig_how m2_i1 +#define sig_flags m2_i2 +#define sig_context m2_p1 +#ifdef _SIGMESSAGE +#define sig_msg m1_i1 +#endif +#define reboot_flag m1_i1 +#define reboot_code m1_p1 +#define reboot_size m1_i2 +#define svrctl_req m2_i1 +#define svrctl_argp m2_p1 + +/* The following names are synonyms for the variables in a reply message. */ +#define reply_res m_type +#define reply_res2 m2_i1 +#define reply_ptr m2_p1 +#define reply_mask m2_l1 +#define reply_trace m2_l2 + +/* The following names are used to inform the FS about certain events. */ +#define tell_fs_arg1 m1_i1 +#define tell_fs_arg2 m1_i2 +#define tell_fs_arg3 m1_i3 + diff --git a/servers/mm/procutils.c b/servers/mm/procutils.c new file mode 100644 index 000000000..d478b5f95 --- /dev/null +++ b/servers/mm/procutils.c @@ -0,0 +1,45 @@ +#include "mm.h" +#include <minix/config.h> +#include <timers.h> +#include <string.h> +#include "../../kernel/const.h" +#include "../../kernel/type.h" +#include "../../kernel/proc.h" + +/* The entry points into this file are: + * p_getmap: get memory map of given process + * p_getsp: get stack pointer of given process + */ + +/*===========================================================================* + * p_getmap * + *===========================================================================*/ +PUBLIC int p_getmap(proc_nr, mem_map) +int proc_nr; /* process to get map of */ +struct mem_map *mem_map; /* put memory map here */ +{ + struct proc p; + int s; + + if ((s=sys_getproc(&p, proc_nr)) != OK) + return(s); + memcpy(mem_map, p.p_memmap, sizeof(p.p_memmap)); + return(OK); +} + +/*===========================================================================* + * p_getsp * + *===========================================================================*/ +PUBLIC int p_getsp(proc_nr, sp) +int proc_nr; /* process to get sp of */ +vir_bytes *sp; /* put stack pointer here */ +{ + struct proc p; + int s; + + if ((s=sys_getproc(&p, proc_nr)) != OK) + return(s); + *sp = p.p_reg.sp; + return(OK); +} + diff --git a/servers/mm/proto.h b/servers/mm/proto.h new file mode 100644 index 000000000..9f755acd5 --- /dev/null +++ b/servers/mm/proto.h @@ -0,0 +1,91 @@ +/* Function prototypes. */ + +struct mproc; +struct stat; +struct mem_map; + +/* alloc.c */ +_PROTOTYPE( phys_clicks alloc_mem, (phys_clicks clicks) ); +_PROTOTYPE( void free_mem, (phys_clicks base, phys_clicks clicks) ); +_PROTOTYPE( void mem_init, (phys_clicks *free) ); +#if ENABLE_SWAP +_PROTOTYPE( int swap_on, (char *file, u32_t offset, u32_t size) ); +_PROTOTYPE( int swap_off, (void) ); +_PROTOTYPE( void swap_in, (void) ); +_PROTOTYPE( void swap_inqueue, (struct mproc *rmp) ); +#else /* !SWAP */ +#define swap_in() ((void)0) +#define swap_inqueue(rmp) ((void)0) +#endif /* !SWAP */ + +/* break.c */ +_PROTOTYPE( int adjust, (struct mproc *rmp, + vir_clicks data_clicks, vir_bytes sp) ); +_PROTOTYPE( int do_brk, (void) ); +_PROTOTYPE( int size_ok, (int file_type, vir_clicks tc, vir_clicks dc, + vir_clicks sc, vir_clicks dvir, vir_clicks s_vir) ); + +/* devio.c */ +_PROTOTYPE( int do_dev_io, (void) ); +_PROTOTYPE( int do_dev_io, (void) ); + +/* exec.c */ +_PROTOTYPE( int do_exec, (void) ); +_PROTOTYPE( void rw_seg, (int rw, int fd, int proc, int seg, + phys_bytes seg_bytes) ); +_PROTOTYPE( struct mproc *find_share, (struct mproc *mp_ign, Ino_t ino, + Dev_t dev, time_t ctime) ); + +/* forkexit.c */ +_PROTOTYPE( int do_fork, (void) ); +_PROTOTYPE( int do_mm_exit, (void) ); +_PROTOTYPE( int do_waitpid, (void) ); +_PROTOTYPE( void mm_exit, (struct mproc *rmp, int exit_status) ); + +/* getset.c */ +_PROTOTYPE( int do_getset, (void) ); + +/* main.c */ +_PROTOTYPE( void main, (void) ); + +/* misc.c */ +_PROTOTYPE( int do_reboot, (void) ); +_PROTOTYPE( int do_getsysinfo, (void) ); +_PROTOTYPE( int do_svrctl, (void) ); +_PROTOTYPE( int do_mstats, (void) ); + +#if (MACHINE == MACINTOSH) +_PROTOTYPE( phys_clicks start_click, (void) ); +#endif + +_PROTOTYPE( void setreply, (int proc_nr, int result) ); + +/* signal.c */ +_PROTOTYPE( int do_alarm, (void) ); +_PROTOTYPE( int do_kill, (void) ); +_PROTOTYPE( int ksig_pending, (void) ); +_PROTOTYPE( int do_ksig, (void) ); +_PROTOTYPE( int do_pause, (void) ); +_PROTOTYPE( int set_alarm, (int proc_nr, int sec) ); +_PROTOTYPE( int check_sig, (pid_t proc_id, int signo) ); +_PROTOTYPE( void sig_proc, (struct mproc *rmp, int sig_nr) ); +_PROTOTYPE( int do_sigaction, (void) ); +_PROTOTYPE( int do_sigpending, (void) ); +_PROTOTYPE( int do_sigprocmask, (void) ); +_PROTOTYPE( int do_sigreturn, (void) ); +_PROTOTYPE( int do_sigsuspend, (void) ); +_PROTOTYPE( void check_pending, (struct mproc *rmp) ); + +/* trace.c */ +_PROTOTYPE( int do_trace, (void) ); +_PROTOTYPE( void stop_proc, (struct mproc *rmp, int sig_nr) ); + +/* utility.c */ +_PROTOTYPE( int allowed, (char *name_buf, struct stat *s_buf, int mask) ); +_PROTOTYPE( int no_sys, (void) ); +_PROTOTYPE( void panic, (char *format, int num) ); +_PROTOTYPE( void tell_fs, (int what, int p1, int p2, int p3) ); + +/* procutils.c */ +_PROTOTYPE( int p_getsp, (int proc_nr, vir_bytes *sp) ); +_PROTOTYPE( int p_getmap, (int proc_nr, struct mem_map *mem_map) ); diff --git a/servers/mm/signal.c b/servers/mm/signal.c new file mode 100644 index 000000000..dbe39e46b --- /dev/null +++ b/servers/mm/signal.c @@ -0,0 +1,646 @@ +/* This file handles signals, which are asynchronous events and are generally + * a messy and unpleasant business. Signals can be generated by the KILL + * system call, or from the keyboard (SIGINT) or from the clock (SIGALRM). + * In all cases control eventually passes to check_sig() to see which processes + * can be signaled. The actual signaling is done by sig_proc(). + * + * The entry points into this file are: + * do_sigaction: perform the SIGACTION system call + * do_sigpending: perform the SIGPENDING system call + * do_sigprocmask: perform the SIGPROCMASK system call + * do_sigreturn: perform the SIGRETURN system call + * do_sigsuspend: perform the SIGSUSPEND system call + * do_kill: perform the KILL system call + * do_ksig: accept a signal originating in the kernel (e.g., SIGINT) + * do_alarm: perform the ALARM system call by calling set_alarm() + * set_alarm: tell the clock task to start or stop a timer + * do_pause: perform the PAUSE system call + * ksig_pending: the kernel notified about pending signals + * sig_proc: interrupt or terminate a signaled process + * check_sig: check which processes to signal with sig_proc() + * check_pending: check if a pending signal can now be delivered + */ + +#include "mm.h" +#include <minix/utils.h> +#include <sys/stat.h> +#include <minix/callnr.h> +#include <minix/com.h> +#include <signal.h> +#include <sys/sigcontext.h> +#include <string.h> +#include "mproc.h" +#include "param.h" + +#define CORE_MODE 0777 /* mode to use on core image files */ +#define DUMPED 0200 /* bit set in status when core dumped */ + +FORWARD _PROTOTYPE( void dump_core, (struct mproc *rmp) ); +FORWARD _PROTOTYPE( void unpause, (int pro) ); +FORWARD _PROTOTYPE( void handle_ksig, (int proc_nr, sigset_t sig_map) ); + + +/*===========================================================================* + * do_sigaction * + *===========================================================================*/ +PUBLIC int do_sigaction() +{ + int r; + struct sigaction svec; + struct sigaction *svp; + + if (m_in.sig_nr == SIGKILL) return(OK); + if (m_in.sig_nr < 1 || m_in.sig_nr > _NSIG) return (EINVAL); + svp = &mp->mp_sigact[m_in.sig_nr]; + if ((struct sigaction *) m_in.sig_osa != (struct sigaction *) NULL) { + r = sys_datacopy(MM_PROC_NR,(vir_bytes) svp, + who, (vir_bytes) m_in.sig_osa, (phys_bytes) sizeof(svec)); + if (r != OK) return(r); + } + + if ((struct sigaction *) m_in.sig_nsa == (struct sigaction *) NULL) + return(OK); + + /* Read in the sigaction structure. */ + r = sys_datacopy(who, (vir_bytes) m_in.sig_nsa, + MM_PROC_NR, (vir_bytes) &svec, (phys_bytes) sizeof(svec)); + if (r != OK) return(r); + + if (svec.sa_handler == SIG_IGN) { + sigaddset(&mp->mp_ignore, m_in.sig_nr); + sigdelset(&mp->mp_sigpending, m_in.sig_nr); + sigdelset(&mp->mp_catch, m_in.sig_nr); + } else { + sigdelset(&mp->mp_ignore, m_in.sig_nr); + if (svec.sa_handler == SIG_DFL) + sigdelset(&mp->mp_catch, m_in.sig_nr); + else + sigaddset(&mp->mp_catch, m_in.sig_nr); + } + mp->mp_sigact[m_in.sig_nr].sa_handler = svec.sa_handler; + sigdelset(&svec.sa_mask, SIGKILL); + mp->mp_sigact[m_in.sig_nr].sa_mask = svec.sa_mask; + mp->mp_sigact[m_in.sig_nr].sa_flags = svec.sa_flags; + mp->mp_sigreturn = (vir_bytes) m_in.sig_ret; + return(OK); +} + +/*===========================================================================* + * do_sigpending * + *===========================================================================*/ +PUBLIC int do_sigpending() +{ + mp->mp_reply.reply_mask = (long) mp->mp_sigpending; + return OK; +} + +/*===========================================================================* + * do_sigprocmask * + *===========================================================================*/ +PUBLIC int do_sigprocmask() +{ +/* Note that the library interface passes the actual mask in sigmask_set, + * not a pointer to the mask, in order to save a copy. Similarly, + * the old mask is placed in the return message which the library + * interface copies (if requested) to the user specified address. + * + * The library interface must set SIG_INQUIRE if the 'act' argument + * is NULL. + */ + + int i; + + mp->mp_reply.reply_mask = (long) mp->mp_sigmask; + + switch (m_in.sig_how) { + case SIG_BLOCK: + sigdelset((sigset_t *)&m_in.sig_set, SIGKILL); + for (i = 1; i <= _NSIG; i++) { + if (sigismember((sigset_t *)&m_in.sig_set, i)) + sigaddset(&mp->mp_sigmask, i); + } + break; + + case SIG_UNBLOCK: + for (i = 1; i <= _NSIG; i++) { + if (sigismember((sigset_t *)&m_in.sig_set, i)) + sigdelset(&mp->mp_sigmask, i); + } + check_pending(mp); + break; + + case SIG_SETMASK: + sigdelset((sigset_t *) &m_in.sig_set, SIGKILL); + mp->mp_sigmask = (sigset_t) m_in.sig_set; + check_pending(mp); + break; + + case SIG_INQUIRE: + break; + + default: + return(EINVAL); + break; + } + return OK; +} + +/*===========================================================================* + * do_sigsuspend * + *===========================================================================*/ +PUBLIC int do_sigsuspend() +{ + mp->mp_sigmask2 = mp->mp_sigmask; /* save the old mask */ + mp->mp_sigmask = (sigset_t) m_in.sig_set; + sigdelset(&mp->mp_sigmask, SIGKILL); + mp->mp_flags |= SIGSUSPENDED; + check_pending(mp); + return(SUSPEND); +} + + +/*===========================================================================* + * do_sigreturn * + *===========================================================================*/ +PUBLIC int do_sigreturn() +{ +/* A user signal handler is done. Restore context and check for + * pending unblocked signals. + */ + + int r; + + mp->mp_sigmask = (sigset_t) m_in.sig_set; + sigdelset(&mp->mp_sigmask, SIGKILL); + + r = sys_sigreturn(who, (struct sigmsg *) m_in.sig_context, m_in.sig_flags); + check_pending(mp); + return(r); +} + +/*===========================================================================* + * do_kill * + *===========================================================================*/ +PUBLIC int do_kill() +{ +/* Perform the kill(pid, signo) system call. */ + + return check_sig(m_in.pid, m_in.sig_nr); +} + +/*===========================================================================* + * do_ksig_pending * + *===========================================================================*/ +PUBLIC int ksig_pending() +{ +/* The kernel has notified the MM about pending signals. Request pending + * signals until all signals are handled. If there are no more signals, + * NONE is returned in the process number field. + */ + int proc_nr; + sigset_t sig_map; + + while (TRUE) { + sys_getsig(&proc_nr, &sig_map); /* get an arbitrary pending signal */ + if (NONE == proc_nr) { /* stop if no more pending signals */ + break; + } else { + handle_ksig(proc_nr, sig_map); /* handle the receive signal */ + } + } + return(SUSPEND); /* prevents sending reply */ +} + +/*===========================================================================* + * do_ksig * + *===========================================================================*/ +PUBLIC int do_ksig() +{ +/* Certain signals, such as segmentation violations and DEL, originate in the + * kernel. When the kernel detects such signals, it sets bits in a bit map. + * As soon as MM is awaiting new work, the kernel sends MM a message containing + * the process slot and bit map. That message comes here. The File System + * also uses this mechanism to signal writing on broken pipes (SIGPIPE). + */ + int proc_nr; + sigset_t sig_map; + + /* Only kernel may make this call. */ + if (who != HARDWARE) return(EPERM); + proc_nr = m_in.SIG_PROC; + sig_map = (sigset_t) m_in.SIG_MAP; + handle_ksig(proc_nr, sig_map); + return(SUSPEND); +} + +/*===========================================================================* + * handle_ksig * + *===========================================================================*/ +PRIVATE void handle_ksig(proc_nr, sig_map) +int proc_nr; +sigset_t sig_map; +{ + register struct mproc *rmp; + int i; + pid_t proc_id, id; + + rmp = &mproc[proc_nr]; + if ((rmp->mp_flags & (IN_USE | ZOMBIE)) != IN_USE) return; + proc_id = rmp->mp_pid; + mp = &mproc[0]; /* pretend kernel signals are from MM */ + mp->mp_procgrp = rmp->mp_procgrp; /* get process group right */ + + /* Check each bit in turn to see if a signal is to be sent. Unlike + * kill(), the kernel may collect several unrelated signals for a + * process and pass them to MM in one blow. Thus loop on the bit + * map. For SIGINT and SIGQUIT, use proc_id 0 to indicate a broadcast + * to the recipient's process group. For SIGKILL, use proc_id -1 to + * indicate a systemwide broadcast. + */ + for (i = 1; i <= _NSIG; i++) { + if (!sigismember(&sig_map, i)) continue; + switch (i) { + case SIGINT: + case SIGQUIT: + id = 0; break; /* broadcast to process group */ + case SIGKILL: + id = -1; break; /* broadcast to all except INIT */ + case SIGALRM: + /* Disregard SIGALRM when the target process has not + * requested an alarm. This only applies for a KERNEL + * generated signal. + */ + if ((rmp->mp_flags & ALARM_ON) == 0) continue; + rmp->mp_flags &= ~ALARM_ON; + /* fall through */ + default: + id = proc_id; + break; + } + check_sig(id, i); + sys_endsig(proc_nr); /* tell kernel it's done */ + } +} + + +/*===========================================================================* + * do_alarm * + *===========================================================================*/ +PUBLIC int do_alarm() +{ +/* Perform the alarm(seconds) system call. */ + return(set_alarm(who, m_in.seconds)); +} + + +/*===========================================================================* + * set_alarm * + *===========================================================================*/ +PUBLIC int set_alarm(proc_nr, sec) +int proc_nr; /* process that wants the alarm */ +int sec; /* how many seconds delay before the signal */ +{ +/* This routine is used by do_alarm() to set the alarm timer. It is also used + * to turn the timer off when a process exits with the timer still on. + */ + clock_t ticks; /* number of ticks for alarm */ + int remaining; /* previous time left in seconds */ + int s; + + if (sec != 0) mproc[proc_nr].mp_flags |= ALARM_ON; + else mproc[proc_nr].mp_flags &= ~ALARM_ON; + + /* Tell the clock task to provide a signal message when the time comes. + * + * Large delays cause a lot of problems. First, the alarm system call + * takes an unsigned seconds count and the library has cast it to an int. + * That probably works, but on return the library will convert "negative" + * unsigneds to errors. Presumably no one checks for these errors, so + * force this call through. Second, If unsigned and long have the same + * size, converting from seconds to ticks can easily overflow. Finally, + * the kernel has similar overflow bugs adding ticks. + * + * Fixing this requires a lot of ugly casts to fit the wrong interface + * types and to avoid overflow traps. ALRM_EXP_TIME has the right type + * (clock_t) although it is declared as long. How can variables like + * this be declared properly without combinatorial explosion of message + * types? + */ + ticks = (clock_t) (HZ * (unsigned long) (unsigned) sec); + if ( (unsigned long) ticks / HZ != (unsigned) sec) + ticks = LONG_MAX; /* eternity (really TMR_NEVER) */ + + if ((s=sys_signalrm(proc_nr, &ticks)) != OK) + panic("MM couldn't set signal alarm", s); + + remaining = (int) ((ticks + (HZ-1))/HZ); + if (remaining < 0) remaining = INT_MAX; /* true value is too large */ + return(remaining); +} + + +/*===========================================================================* + * do_pause * + *===========================================================================*/ +PUBLIC int do_pause() +{ +/* Perform the pause() system call. */ + + mp->mp_flags |= PAUSED; + return(SUSPEND); +} + + +/*===========================================================================* + * sig_proc * + *===========================================================================*/ +PUBLIC void sig_proc(rmp, signo) +register struct mproc *rmp; /* pointer to the process to be signaled */ +int signo; /* signal to send to process (1 to _NSIG) */ +{ +/* Send a signal to a process. Check to see if the signal is to be caught, + * ignored, or blocked. If the signal is to be caught, coordinate with + * KERNEL to push a sigcontext structure and a sigframe structure onto + * the catcher's stack. Also, KERNEL will reset the program counter and + * stack pointer, so that when the process next runs, it will be executing + * the signal handler. When the signal handler returns, sigreturn(2) + * will be called. Then KERNEL will restore the signal context from the + * sigcontext structure. + * + * If there is insufficient stack space, kill the process. + */ + + vir_bytes new_sp; + int s; + int slot; + int sigflags; + struct sigmsg sm; + + slot = (int) (rmp - mproc); + if ((rmp->mp_flags & (IN_USE | ZOMBIE)) != IN_USE) { + printf("MM: signal %d sent to %s process %d\n", + (rmp->mp_flags & ZOMBIE) ? "zombie" : "dead", signo, slot); + panic("", NO_NUM); + } + if ((rmp->mp_flags & TRACED) && signo != SIGKILL) { + /* A traced process has special handling. */ + unpause(slot); + stop_proc(rmp, signo); /* a signal causes it to stop */ + return; + } + /* Some signals are ignored by default. */ + if (sigismember(&rmp->mp_ignore, signo)) return; + + if (sigismember(&rmp->mp_sigmask, signo)) { + /* Signal should be blocked. */ + sigaddset(&rmp->mp_sigpending, signo); + return; + } + sigflags = rmp->mp_sigact[signo].sa_flags; + if (sigismember(&rmp->mp_catch, signo)) { + if (rmp->mp_flags & ONSWAP) { + /* Process is swapped out, leave signal pending. */ + sigaddset(&rmp->mp_sigpending, signo); + swap_inqueue(rmp); + return; + } + if (rmp->mp_flags & SIGSUSPENDED) + sm.sm_mask = rmp->mp_sigmask2; + else + sm.sm_mask = rmp->mp_sigmask; + sm.sm_signo = signo; + sm.sm_sighandler = (vir_bytes) rmp->mp_sigact[signo].sa_handler; + sm.sm_sigreturn = rmp->mp_sigreturn; + if ((s=p_getsp(slot, &new_sp)) != OK) + panic("MM couldn't get new stack pointer",s); + sm.sm_stkptr = new_sp; + + /* Make room for the sigcontext and sigframe struct. */ + new_sp -= sizeof(struct sigcontext) + + 3 * sizeof(char *) + 2 * sizeof(int); + + if (adjust(rmp, rmp->mp_seg[D].mem_len, new_sp) != OK) + goto doterminate; + + rmp->mp_sigmask |= rmp->mp_sigact[signo].sa_mask; + if (sigflags & SA_NODEFER) + sigdelset(&rmp->mp_sigmask, signo); + else + sigaddset(&rmp->mp_sigmask, signo); + + if (sigflags & SA_RESETHAND) { + sigdelset(&rmp->mp_catch, signo); + rmp->mp_sigact[signo].sa_handler = SIG_DFL; + } + + sys_sendsig(slot, &sm); + sigdelset(&rmp->mp_sigpending, signo); + /* If process is hanging on PAUSE, WAIT, SIGSUSPEND, tty, pipe, etc., + * release it. + */ + unpause(slot); + return; + } +doterminate: + /* Signal should not or cannot be caught. Take default action. */ + if (sigismember(&ign_sset, signo)) return; + + rmp->mp_sigstatus = (char) signo; + if (sigismember(&core_sset, signo)) { + if (rmp->mp_flags & ONSWAP) { + /* Process is swapped out, leave signal pending. */ + sigaddset(&rmp->mp_sigpending, signo); + swap_inqueue(rmp); + return; + } + /* Switch to the user's FS environment and dump core. */ + tell_fs(CHDIR, slot, FALSE, 0); + dump_core(rmp); + } + mm_exit(rmp, 0); /* terminate process */ +} + + +/*===========================================================================* + * check_sig * + *===========================================================================*/ +PUBLIC int check_sig(proc_id, signo) +pid_t proc_id; /* pid of proc to sig, or 0 or -1, or -pgrp */ +int signo; /* signal to send to process (0 to _NSIG) */ +{ +/* Check to see if it is possible to send a signal. The signal may have to be + * sent to a group of processes. This routine is invoked by the KILL system + * call, and also when the kernel catches a DEL or other signal. + */ + + register struct mproc *rmp; + int count; /* count # of signals sent */ + int error_code; + + if (signo < 0 || signo > _NSIG) return(EINVAL); + + /* Return EINVAL for attempts to send SIGKILL to INIT alone. */ + if (proc_id == INIT_PID && signo == SIGKILL) return(EINVAL); + + /* Search the proc table for processes to signal. (See forkexit.c about + * pid magic.) + */ + count = 0; + error_code = ESRCH; + for (rmp = &mproc[INIT_PROC_NR]; rmp < &mproc[NR_PROCS]; rmp++) { + if (!(rmp->mp_flags & IN_USE)) continue; + if ((rmp->mp_flags & ZOMBIE) && signo != 0) continue; + + /* Check for selection. */ + if (proc_id > 0 && proc_id != rmp->mp_pid) continue; + if (proc_id == 0 && mp->mp_procgrp != rmp->mp_procgrp) continue; + if (proc_id == -1 && rmp->mp_pid <= INIT_PID) continue; + if (proc_id < -1 && rmp->mp_procgrp != -proc_id) continue; + + /* Check for permission. */ + if (mp->mp_effuid != SUPER_USER + && mp->mp_realuid != rmp->mp_realuid + && mp->mp_effuid != rmp->mp_realuid + && mp->mp_realuid != rmp->mp_effuid + && mp->mp_effuid != rmp->mp_effuid) { + error_code = EPERM; + continue; + } + + count++; + if (signo == 0) continue; + + /* 'sig_proc' will handle the disposition of the signal. The + * signal may be caught, blocked, ignored, or cause process + * termination, possibly with core dump. + */ + sig_proc(rmp, signo); + + if (proc_id > 0) break; /* only one process being signaled */ + } + + /* If the calling process has killed itself, don't reply. */ + if ((mp->mp_flags & (IN_USE | ZOMBIE)) != IN_USE) return(SUSPEND); + return(count > 0 ? OK : error_code); +} + + +/*===========================================================================* + * check_pending * + *===========================================================================*/ +PUBLIC void check_pending(rmp) +register struct mproc *rmp; +{ + /* Check to see if any pending signals have been unblocked. The + * first such signal found is delivered. + * + * If multiple pending unmasked signals are found, they will be + * delivered sequentially. + * + * There are several places in this file where the signal mask is + * changed. At each such place, check_pending() should be called to + * check for newly unblocked signals. + */ + + int i; + + for (i = 1; i <= _NSIG; i++) { + if (sigismember(&rmp->mp_sigpending, i) && + !sigismember(&rmp->mp_sigmask, i)) { + sigdelset(&rmp->mp_sigpending, i); + sig_proc(rmp, i); + break; + } + } +} + + +/*===========================================================================* + * unpause * + *===========================================================================*/ +PRIVATE void unpause(pro) +int pro; /* which process number */ +{ +/* A signal is to be sent to a process. If that process is hanging on a + * system call, the system call must be terminated with EINTR. Possible + * calls are PAUSE, WAIT, READ and WRITE, the latter two for pipes and ttys. + * First check if the process is hanging on an MM call. If not, tell FS, + * so it can check for READs and WRITEs from pipes, ttys and the like. + */ + + register struct mproc *rmp; + + rmp = &mproc[pro]; + + /* Check to see if process is hanging on a PAUSE, WAIT or SIGSUSPEND call. */ + if (rmp->mp_flags & (PAUSED | WAITING | SIGSUSPENDED)) { + rmp->mp_flags &= ~(PAUSED | WAITING | SIGSUSPENDED); + setreply(pro, EINTR); + return; + } + + /* Process is not hanging on an MM call. Ask FS to take a look. */ + tell_fs(UNPAUSE, pro, 0, 0); +} + + +/*===========================================================================* + * dump_core * + *===========================================================================*/ +PRIVATE void dump_core(rmp) +register struct mproc *rmp; /* whose core is to be dumped */ +{ +/* Make a core dump on the file "core", if possible. */ + + int s, fd, fake_fd, nr_written, seg, slot; + char *buf; + vir_bytes current_sp; + phys_bytes left; /* careful; 64K might overflow vir_bytes */ + unsigned nr_to_write; /* unsigned for arg to write() but < INT_MAX */ + long trace_data, trace_off; + + slot = (int) (rmp - mproc); + + /* Can core file be written? We are operating in the user's FS environment, + * so no special permission checks are needed. + */ + if (rmp->mp_realuid != rmp->mp_effuid) return; + if ( (fd = open(core_name, O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, + CORE_MODE)) < 0) return; + rmp->mp_sigstatus |= DUMPED; + + /* Make sure the stack segment is up to date. + * We don't want adjust() to fail unless current_sp is preposterous, + * but it might fail due to safety checking. Also, we don't really want + * the adjust() for sending a signal to fail due to safety checking. + * Maybe make SAFETY_BYTES a parameter. + */ + if ((s=p_getsp(slot, ¤t_sp)) != OK) + panic("MM couldn't get new stack pointer",s); + adjust(rmp, rmp->mp_seg[D].mem_len, current_sp); + + /* Write the memory map of all segments to begin the core file. */ + if (write(fd, (char *) rmp->mp_seg, (unsigned) sizeof rmp->mp_seg) + != (unsigned) sizeof rmp->mp_seg) { + close(fd); + return; + } + + /* Write out the whole kernel process table entry to get the regs. */ + trace_off = 0; + while (sys_trace(3, slot, trace_off, &trace_data) == OK) { + if (write(fd, (char *) &trace_data, (unsigned) sizeof (long)) + != (unsigned) sizeof (long)) { + close(fd); + return; + } + trace_off += sizeof (long); + } + + /* Loop through segments and write the segments themselves out. */ + for (seg = 0; seg < NR_LOCAL_SEGS; seg++) { + rw_seg(1, fd, slot, seg, + (phys_bytes) rmp->mp_seg[seg].mem_len << CLICK_SHIFT); + } + close(fd); +} diff --git a/servers/mm/table.c b/servers/mm/table.c new file mode 100644 index 000000000..1962678cd --- /dev/null +++ b/servers/mm/table.c @@ -0,0 +1,108 @@ +/* This file contains the table used to map system call numbers onto the + * routines that perform them. + */ + +#define _TABLE + +#include "mm.h" +#include <minix/callnr.h> +#include <signal.h> +#include "mproc.h" +#include "param.h" + +/* Miscellaneous */ +char core_name[] = "core"; /* file name where core images are produced */ + +_PROTOTYPE (int (*call_vec[NCALLS]), (void) ) = { + no_sys, /* 0 = unused */ + do_mm_exit, /* 1 = exit */ + do_fork, /* 2 = fork */ + no_sys, /* 3 = read */ + no_sys, /* 4 = write */ + no_sys, /* 5 = open */ + no_sys, /* 6 = close */ + do_waitpid, /* 7 = wait */ + no_sys, /* 8 = creat */ + no_sys, /* 9 = link */ + no_sys, /* 10 = unlink */ + do_waitpid, /* 11 = waitpid */ + no_sys, /* 12 = chdir */ + no_sys, /* 13 = time */ + no_sys, /* 14 = mknod */ + no_sys, /* 15 = chmod */ + no_sys, /* 16 = chown */ + do_brk, /* 17 = break */ + no_sys, /* 18 = stat */ + no_sys, /* 19 = lseek */ + do_getset, /* 20 = getpid */ + no_sys, /* 21 = mount */ + no_sys, /* 22 = umount */ + do_getset, /* 23 = setuid */ + do_getset, /* 24 = getuid */ + no_sys, /* 25 = stime */ + do_trace, /* 26 = ptrace */ + do_alarm, /* 27 = alarm */ + no_sys, /* 28 = fstat */ + do_pause, /* 29 = pause */ + no_sys, /* 30 = utime */ + no_sys, /* 31 = (stty) */ + no_sys, /* 32 = (gtty) */ + no_sys, /* 33 = access */ + no_sys, /* 34 = (nice) */ + no_sys, /* 35 = (ftime) */ + no_sys, /* 36 = sync */ + do_kill, /* 37 = kill */ + no_sys, /* 38 = rename */ + no_sys, /* 39 = mkdir */ + no_sys, /* 40 = rmdir */ + no_sys, /* 41 = dup */ + no_sys, /* 42 = pipe */ + no_sys, /* 43 = times */ + no_sys, /* 44 = (prof) */ + no_sys, /* 45 = unused */ + do_getset, /* 46 = setgid */ + do_getset, /* 47 = getgid */ + no_sys, /* 48 = (signal)*/ + no_sys, /* 49 = unused */ + no_sys, /* 50 = unused */ + no_sys, /* 51 = (acct) */ + no_sys, /* 52 = (phys) */ + no_sys, /* 53 = (lock) */ + no_sys, /* 54 = ioctl */ + no_sys, /* 55 = fcntl */ + no_sys, /* 56 = (mpx) */ + no_sys, /* 57 = unused */ + no_sys, /* 58 = unused */ + do_exec, /* 59 = execve */ + no_sys, /* 60 = umask */ + no_sys, /* 61 = chroot */ + do_getset, /* 62 = setsid */ + do_getset, /* 63 = getpgrp */ + + do_ksig, /* 64 = KSIG: signals originating in the kernel */ + no_sys, /* 65 = UNPAUSE */ + no_sys, /* 66 = unused */ + no_sys, /* 67 = REVIVE */ + no_sys, /* 68 = TASK_REPLY */ + no_sys, /* 69 = unused */ + no_sys, /* 70 = unused */ + do_sigaction, /* 71 = sigaction */ + do_sigsuspend, /* 72 = sigsuspend */ + do_sigpending, /* 73 = sigpending */ + do_sigprocmask, /* 74 = sigprocmask */ + do_sigreturn, /* 75 = sigreturn */ + do_reboot, /* 76 = reboot */ + do_svrctl, /* 77 = svrctl */ + + no_sys, /* 78 = cmostime */ + do_getsysinfo, /* 79 = getsysinfo */ +#if ENABLE_MESSAGE_STATS + do_mstats, /* 80 = mstats */ +#else + no_sys, +#endif + no_sys, /* 81 = unused */ + no_sys, /* 82 = unused */ +}; +/* This should not fail with "array size is negative": */ +extern int dummy[sizeof(call_vec) == NCALLS * sizeof(call_vec[0]) ? 1 : -1]; diff --git a/servers/mm/trace.c b/servers/mm/trace.c new file mode 100644 index 000000000..731324ecf --- /dev/null +++ b/servers/mm/trace.c @@ -0,0 +1,111 @@ +/* This file handles the memory manager's part of debugging, using the + * ptrace system call. Most of the commands are passed on to the system + * task for completion. + * + * The debugging commands available are: + * T_STOP stop the process + * T_OK enable tracing by parent for this process + * T_GETINS return value from instruction space + * T_GETDATA return value from data space + * T_GETUSER return value from user process table + * T_SETINS set value in instruction space + * T_SETDATA set value in data space + * T_SETUSER set value in user process table + * T_RESUME resume execution + * T_EXIT exit + * T_STEP set trace bit + * + * The T_OK and T_EXIT commands are handled here, and the T_RESUME and + * T_STEP commands are partially handled here and completed by the system + * task. The rest are handled entirely by the system task. + */ + +#include "mm.h" +#include <minix/com.h> +#include <sys/ptrace.h> +#include <signal.h> +#include "mproc.h" +#include "param.h" + +#define NIL_MPROC ((struct mproc *) 0) + +FORWARD _PROTOTYPE( struct mproc *findproc, (pid_t lpid) ); + +/*===========================================================================* + * do_trace * + *===========================================================================*/ +PUBLIC int do_trace() +{ + register struct mproc *child; + + /* the T_OK call is made by the child fork of the debugger before it execs + * the process to be traced + */ + if (m_in.request == T_OK) { /* enable tracing by parent for this proc */ + mp->mp_flags |= TRACED; + mp->mp_reply.reply_trace = 0; + return(OK); + } + if ((child=findproc(m_in.pid))==NIL_MPROC || !(child->mp_flags & STOPPED)) { + return(ESRCH); + } + /* all the other calls are made by the parent fork of the debugger to + * control execution of the child + */ + switch (m_in.request) { + case T_EXIT: /* exit */ + mm_exit(child, (int) m_in.data); + mp->mp_reply.reply_trace = 0; + return(OK); + case T_RESUME: + case T_STEP: /* resume execution */ + if (m_in.data < 0 || m_in.data > _NSIG) return(EIO); + if (m_in.data > 0) { /* issue signal */ + child->mp_flags &= ~TRACED; /* so signal is not diverted */ + sig_proc(child, (int) m_in.data); + child->mp_flags |= TRACED; + } + child->mp_flags &= ~STOPPED; + break; + } + if (sys_trace(m_in.request,(int)(child-mproc),m_in.taddr,&m_in.data) != OK) + return(-errno); + mp->mp_reply.reply_trace = m_in.data; + return(OK); +} + +/*===========================================================================* + * findproc * + *===========================================================================*/ +PRIVATE struct mproc *findproc(lpid) +pid_t lpid; +{ + register struct mproc *rmp; + + for (rmp = &mproc[INIT_PROC_NR + 1]; rmp < &mproc[NR_PROCS]; rmp++) + if (rmp->mp_flags & IN_USE && rmp->mp_pid == lpid) return(rmp); + return(NIL_MPROC); +} + +/*===========================================================================* + * stop_proc * + *===========================================================================*/ +PUBLIC void stop_proc(rmp, signo) +register struct mproc *rmp; +int signo; +{ +/* A traced process got a signal so stop it. */ + + register struct mproc *rpmp = mproc + rmp->mp_parent; + + if (sys_trace(-1, (int) (rmp - mproc), 0L, (long *) 0) != OK) return; + rmp->mp_flags |= STOPPED; + if (rpmp->mp_flags & WAITING) { + rpmp->mp_flags &= ~WAITING; /* parent is no longer waiting */ + rpmp->mp_reply.reply_res2 = 0177 | (signo << 8); + setreply(rmp->mp_parent, rmp->mp_pid); + } else { + rmp->mp_sigstatus = signo; + } + return; +} diff --git a/servers/mm/type.h b/servers/mm/type.h new file mode 100644 index 000000000..65d9c846c --- /dev/null +++ b/servers/mm/type.h @@ -0,0 +1,5 @@ +/* If there were any type definitions local to the Memory Manager, they would + * be here. This file is included only for symmetry with the kernel and File + * System, which do have some local type definitions. + */ + diff --git a/servers/mm/utility.c b/servers/mm/utility.c new file mode 100644 index 000000000..be37552c2 --- /dev/null +++ b/servers/mm/utility.c @@ -0,0 +1,114 @@ +/* This file contains some utility routines for MM. + * + * The entry points are: + * allowed: see if an access is permitted + * no_sys: this routine is called for invalid system call numbers + * panic: MM has run aground of a fatal error and cannot continue + * tell_fs: interface to FS + */ + +#include "mm.h" +#include <sys/stat.h> +#include <minix/callnr.h> +#include <minix/com.h> +#include <fcntl.h> +#include <signal.h> /* needed only because mproc.h needs it */ +#include "mproc.h" +#include "param.h" + +/*===========================================================================* + * allowed * + *===========================================================================*/ +PUBLIC int allowed(name_buf, s_buf, mask) +char *name_buf; /* pointer to file name to be EXECed */ +struct stat *s_buf; /* buffer for doing and returning stat struct*/ +int mask; /* R_BIT, W_BIT, or X_BIT */ +{ +/* Check to see if file can be accessed. Return EACCES or ENOENT if the access + * is prohibited. If it is legal open the file and return a file descriptor. + */ + + int fd; + int save_errno; + + /* Use the fact that mask for access() is the same as the permissions mask. + * E.g., X_BIT in <minix/const.h> is the same as X_OK in <unistd.h> and + * S_IXOTH in <sys/stat.h>. tell_fs(DO_CHDIR, ...) has set MM's real ids + * to the user's effective ids, so access() works right for setuid programs. + */ + if (access(name_buf, mask) < 0) return(-errno); + + /* The file is accessible but might not be readable. Make it readable. */ + tell_fs(SETUID, MM_PROC_NR, (int) SUPER_USER, (int) SUPER_USER); + + /* Open the file and fstat it. Restore the ids early to handle errors. */ + fd = open(name_buf, O_RDONLY | O_NONBLOCK); + save_errno = errno; /* open might fail, e.g. from ENFILE */ + tell_fs(SETUID, MM_PROC_NR, (int) mp->mp_effuid, (int) mp->mp_effuid); + if (fd < 0) return(-save_errno); + if (fstat(fd, s_buf) < 0) panic("allowed: fstat failed", NO_NUM); + + /* Only regular files can be executed. */ + if (mask == X_BIT && (s_buf->st_mode & I_TYPE) != I_REGULAR) { + close(fd); + return(EACCES); + } + return(fd); +} + + +/*===========================================================================* + * no_sys * + *===========================================================================*/ +PUBLIC int no_sys() +{ +/* A system call number not implemented by MM has been requested. */ + + return(EINVAL); +} + + +/*===========================================================================* + * panic * + *===========================================================================*/ +PUBLIC void panic(format, num) +char *format; /* format string */ +int num; /* number to go with format string */ +{ +/* Something awful has happened. Panics are caused when an internal + * inconsistency is detected, e.g., a programming error or illegal value of a + * defined constant. + */ + + printf("Memory manager panic: %s ", format); + if (num != NO_NUM) printf("%d",num); + printf("\n"); + tell_fs(SYNC, 0, 0, 0); /* flush the cache to the disk */ + sys_abort(RBT_PANIC); +} + + +/*===========================================================================* + * tell_fs * + *===========================================================================*/ +PUBLIC void tell_fs(what, p1, p2, p3) +int what, p1, p2, p3; +{ +/* This routine is only used by MM to inform FS of certain events: + * tell_fs(CHDIR, slot, dir, 0) + * tell_fs(EXEC, proc, 0, 0) + * tell_fs(EXIT, proc, 0, 0) + * tell_fs(FORK, parent, child, pid) + * tell_fs(SETGID, proc, realgid, effgid) + * tell_fs(SETSID, proc, 0, 0) + * tell_fs(SETUID, proc, realuid, effuid) + * tell_fs(SYNC, 0, 0, 0) + * tell_fs(UNPAUSE, proc, signr, 0) + */ + message m; + + m.tell_fs_arg1 = p1; + m.tell_fs_arg2 = p2; + m.tell_fs_arg3 = p3; + _taskcall(FS_PROC_NR, what, &m); +} diff --git a/test/Makefile b/test/Makefile new file mode 100644 index 000000000..99d22e037 --- /dev/null +++ b/test/Makefile @@ -0,0 +1,75 @@ +# Makefile for the tests. + +CC = exec cc +CFLAGS= -O -D_MINIX -D_POSIX_SOURCE + +OBJ= test1 test2 test3 test4 test5 test6 test7 test8 test9 \ + test10 test12 test13 test14 test15 test16 test17 test18 test19 \ + test21 test22 test23 test25 test26 test27 test28 test29 \ + test30 test31 test32 test34 test35 test36 test37 test38 test39 \ + test40 t10a t11a t11b + +BIGOBJ= test20 test24 +ROOTOBJ= test11 test33 + +all: $(OBJ) $(BIGOBJ) $(ROOTOBJ) + +$(OBJ): + $(CC) $(CFLAGS) -o $@ $@.c + install -S 10kw $@ + +$(BIGOBJ): + $(CC) $(CFLAGS) -o $@ $@.c + install -S 32kw $@ + +$(ROOTOBJ): + $(CC) $(CFLAGS) $@.c + install -c -S 10kw -o root -m 4755 a.out $@ + rm a.out + +clean: + @rm -f *.o *.s *.bak test? test?? t10a t11a t11b DIR* + +test1: test1.c +test2: test2.c +test3: test3.c +test4: test4.c +test5: test5.c +test6: test6.c +test7: test7.c +test8: test8.c +test9: test9.c +test10: test10.c +t10a: t10a.c +test11: test11.c +t11a: t11a.c +t11b: t11b.c +test12: test12.c +test13: test13.c +test14: test14.c +test15: test15.c +test16: test16.c +test17: test17.c +test18: test18.c +test19: test19.c +test20: test20.c +test21: test21.c +test22: test22.c +test23: test23.c +test24: test24.c +test25: test25.c +test26: test26.c +test27: test27.c +test28: test28.c +test29: test29.c +test30: test30.c +test31: test31.c +test32: test32.c +test33: test33.c +test34: test34.c +test35: test35.c +test36: test36.c +test37: test37.c +test38: test38.c +test39: test39.c +test40: test40.c diff --git a/test/common.c b/test/common.c new file mode 100644 index 000000000..12fb34a0b --- /dev/null +++ b/test/common.c @@ -0,0 +1,103 @@ +/* Utility routines for Minix tests. + * This is designed to be #includ'ed near the top of test programs. It is + * self-contained except for MAX_ERRORS. + */ + +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +int common_test_nr = -1, errct = 0, subtest; + +_PROTOTYPE(void cleanup, (void)); +_PROTOTYPE(void e, (int n)); +_PROTOTYPE(void quit, (void)); +_PROTOTYPE(void rm_rf_dir, (int test_nr)); +_PROTOTYPE(void rm_rf_ppdir, (int test_nr)); +_PROTOTYPE(void start, (int test_nr)); + +void start(test_nr) +int test_nr; +{ + char buf[64]; + + common_test_nr = test_nr; + printf("Test %2d ", test_nr); + fflush(stdout); /* since stdout is probably line buffered */ + sync(); + rm_rf_dir(test_nr); + sprintf(buf, "mkdir DIR_%02d", test_nr); + if (system(buf) != 0) { + e(666); + quit(); + } + sprintf(buf, "DIR_%02d", test_nr); + if (chdir(buf) != 0) { + e(6666); + quit(); + } +} + +void rm_rf_dir(test_nr) +int test_nr; +{ + char buf[128]; + + /* "rm -rf dir" will not work unless all the subdirectories have suitable + * permissions. Minix chmod is not recursive so it is not easy to change + * all the permissions. I had to fix opendir() to stop the bash shell + * from hanging when it opendir()s fifos. + */ + sprintf(buf, "chmod 777 DIR_%02d DIR_%02d/* DIR_%02d/*/* >/dev/null 2>&1", + test_nr, test_nr, test_nr); + (void) system(buf); /* usually fails */ + sprintf(buf, "rm -rf DIR_%02d >/dev/null 2>&1", test_nr); + if (system(buf) != 0) printf("Warning: system(\"%s\") failed\n", buf); +} + +void rm_rf_ppdir(test_nr) +int test_nr; +{ +/* Attempt to remove everything in the test directory (== the current dir). */ + + char buf[128]; + + sprintf(buf, "chmod 777 ../DIR_%02d/* ../DIR_%02d/*/* >/dev/null 2>&1", + test_nr, test_nr); + (void) system(buf); + sprintf(buf, "rm -rf ../DIR_%02d >/dev/null 2>&1", test_nr); + if (system(buf) != 0) printf("Warning: system(\"%s\") failed\n", buf); +} + +void e(n) +int n; +{ + if (errct == 0) printf("\n"); /* finish header */ + printf("Subtest %d, error %d, errno %d: %s\n", + subtest, n, errno, strerror(errno)); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + cleanup(); + exit(1); + } + errno = 0; /* don't leave it around to confuse next e() */ +} + +void cleanup() +{ + if (chdir("..") == 0 && common_test_nr != -1) rm_rf_dir(common_test_nr); +} + +void quit() +{ + cleanup(); + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/run.sh b/test/run.sh new file mode 100755 index 000000000..af37de9ae --- /dev/null +++ b/test/run.sh @@ -0,0 +1,35 @@ +#!/bin/sh + +# Initialization +PATH=:/bin:/usr/bin +export PATH + +rm -rf DIR* # remove any old junk lying around +passed=`expr 0` # count number of tests run correctly +failed=`expr 0` # count number of tests that failed +total=`expr 0` # total number of tests tried +badones= # list of tests that failed + +# Run all the tests, keeping track of who failed. +clr +for i in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 \ + 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 +do total=`expr $total + 1` + if test$i + then passed=`expr $passed + 1` + else failed=`expr $failed + 1` + badones=`echo $badones " " $i` + fi +done + +# Print results of the tests. +echo " " +if test $total = $passed + then echo All $passed tests completed without error. + else echo Testing completed. Score: $passed passed, $failed failed + echo The following tests failed: $badones +fi + +# echo " " +# sh1.sh +# sh2.sh diff --git a/test/sh1.sh b/test/sh1.sh new file mode 100755 index 000000000..6412290dc --- /dev/null +++ b/test/sh1.sh @@ -0,0 +1,310 @@ +#!/bin/sh + +# Shell script used to test MINIX. + +PATH=:/bin:/usr/bin +export PATH + +echo -n "Shell test 1 " +rm -rf DIR_SH1 +mkdir DIR_SH1 +cd DIR_SH1 + +f=../test1.c +if test -r $f; then : ; else echo sh1 cannot read $f; exit 1; fi + +#Initial setup +echo "abcdefghijklmnopqrstuvwxyz" >alpha +echo "ABCDEFGHIJKLMNOPQRSTUVWXYZ" >ALPHA +echo "0123456789" >num +echo "!@#$%^&*()_+=-{}[]:;<>?/.," >special +cp /etc/rc rc +cp /etc/passwd passwd +cat alpha ALPHA num rc passwd special >tmp +cat tmp tmp tmp >f1 + + +#Test cp +mkdir foo +cp /etc/rc /etc/passwd foo +if cmp -s foo/rc /etc/rc ; then : ; else echo Error on cp test 1; fi +if cmp -s foo/passwd /etc/passwd ; then : ; else echo Error on cp test 2; fi +rm -rf foo + +#Test cat +cat num num num num num >y +wc -c y >x1 +echo " 55 y" >x2 +if cmp -s x1 x2; then : ; else echo Error on cat test 1; fi +cat <y >z +if cmp -s y z; then : ; else echo Error on cat test 2; fi + +#Test ar +cat passwd >p +cp passwd q +if cmp -s p q; then : ; else echo Error on ar test 1; fi +date >r +ar r x.a p q r 2>/dev/null +ar r x.a /usr/bin/cp +ar r x.a /usr/bin/cat +rm p q +mv r R +ar x x.a +if cmp -s p /etc/passwd; then : ; else Error on ar test 2; fi +if cmp -s q /etc/passwd; then : ; else Error on ar test 3; fi +if cmp -s r R; then : ; else Error on ar test 4; fi +if cmp -s cp /usr/bin/cp; then : ; else Error on ar test 5; fi +if cmp -s cat /usr/bin/cat; then : ; else Error on ar test 6; fi +rm cp cat p q r +ar d x.a r >/dev/null +ar x x.a +if test -r r; then echo Error on ar test 7; fi +rm -rf p q r R + +#Test basename +if test `basename /usr/ast/foo.c .c` != 'foo' + then echo Error on basename test 1 +fi + +if test `basename a/b/c/d` != 'd'; then Error on basename test 2; fi + +#Test cdiff, sed, and patch +cp $f x.c # x.c is a copy $f +echo "/a/s//#####/g" >s # create sed script +sed -f s <x.c >y.c # y.c is new version of x.c +cdiff x.c y.c >y # y is cdiff listing +patch x.c y 2>/dev/null # z should be y.c +if cmp -s x.c y.c; then : ; else echo Error in cdiff test; fi +rm x.c* y.c s y + +#Test comm, grep -v +ls /etc >x # x = list of /etc +grep -v "passwd" x >y # y = x except for /etc/passwd +comm -3 x y >z # should only be 1 line, /etc/passwd +echo "passwd" >w +if cmp -s w z; then : else echo Error on comm test 1; fi + +comm -13 x y >z # should be empty +if test -s z; then echo Error on comm test 2; fi +rm -rf w x y z + +#Test compress +compress -fc $f >x.c.Z # compress the test file +compress -cd x.c.Z >y # uncompress it +if cmp -s $f y; then : else echo Error in compress test 1; fi +rm -rf x.c.Z y + +#Test ed +cp $f x # copy $f to x +cat >y <<END +g/a/s//#####/g +g/b/s//@@@@@/g +g/c/s//!!!!!/g +w +q +END +ed x <y >/dev/null +cat >y <<END +g/#####/s//a/g +g/@@@@@/s//b/g +g/!!!!!/s//c/g +w +q +END +ed x <y >/dev/null +if cmp -s x $f; then : ; else echo Error in ed test 1; fi +rm x y + +#Test expr +if test `expr 1 + 1` != 2; then echo Error on expr test 1; fi +if test `expr 10000 - 1` != 9999; then echo Error on expr test 2; fi +if test `expr 100 '*' 50` != 5000; then echo Error on expr test 3; fi +if test `expr 120 / 5` != 24; then echo Error on expr test 4; fi +if test `expr 143 % 7` != 3; then echo Error on expr test 5; fi +a=100 +a=`expr $a + 1` +if test $a != '101'; then echo Error on expr test 6; fi + +#Test fgrep +fgrep "abc" alpha >z +if cmp -s alpha z ; then : else echo Error on fgrep test 1; fi +fgrep "abc" num >z +if test -s z; then echo Error on fgrep test 2; fi +cat alpha num >z +fgrep "012" z >w +if cmp -s w num; then : ; else echo Error fgrep test 3; fi + + +#Test find +date >Rabbit +echo "Rabbit" >y +find . -name Rabbit -print >z +if cmp -s y z; then : else echo Error on find test 1; fi +find . -name Bunny -print >z +if test -s z; then echo Error on find test 2; fi +rm Rabbit y z + +#Test grep +grep "a" alpha >x +if cmp -s x alpha; then : ; else echo Error on grep test 1; fi +grep "a" ALPHA >x +if test -s x; then echo Error on grep test 2; fi +grep -v "0" alpha >x +if cmp -s x alpha; then : ; else echo Error on grep test 3; fi +grep -s "a" alpha >x +if test -s x; then echo Error on grep test 4; fi +if grep -s "a" alpha >x; then : else echo Error on grep test 5; fi +if grep -s "a" ALPHA >x; then echo Error on grep test 6; fi + +#Test head +head -1 f1 >x +if cmp -s x alpha; then : else echo Error on head test 1; fi +head -2 f1 >x +cat alpha ALPHA >y +if cmp -s x y; then : else echo Error on head test 2; fi + +#Test ls +mkdir FOO +cp passwd FOO/z +cp alpha FOO/x +cp ALPHA FOO/y +cd FOO +ls >w +cat >w1 <<END +w +x +y +z +END +if cmp -s w w1; then : ; else echo Error on ls test 1; fi +rm * +cd .. +rmdir FOO + +#Test mkdir/rmdir +mkdir Foo Bar +if test -d Foo; then : ; else echo Error on mkdir test 1; fi +if test -d Bar; then : ; else echo Error on mkdir test 2; fi +rmdir Foo Bar +if test -d Foo; then echo Error on mkdir test 3; fi +if test -d Foo; then echo Error on rmdir test 4; fi + +#Test mv +mkdir MVDIR +cp $f x +mv x y +mv y z +if cmp -s $f z; then : ; else echo Error on mv test 1; fi +cp $f x +mv x MVDIR/y +if cmp -s $f MVDIR/y; then : ; else echo Error on mv test 2; fi + +#Test rev +rev <f1 | head -1 >ahpla +echo "zyxwvutsrqponmlkjihgfedcba" >x +if cmp -s x ahpla; then : ; else echo Error on rev test 1; fi +rev <$f >x +rev <x >y +if cmp -s $f x; then echo Error on rev test 2; fi +if cmp -s $f y; then : ; else echo error on rev test 3; fi + +#Test shar +cp $f w +cp alpha x +cp ALPHA y +cp num z +shar w x y z >x1 +rm w x y z +sh <x1 >/dev/null +if cmp -s w $f; then : ; else echo Error on shar test 1; fi +if cmp -s x alpha; then : ; else echo Error on shar test 2; fi +if cmp -s y ALPHA; then : ; else echo Error on shar test 3; fi +if cmp -s z num; then : ; else echo Error on shar test 4; fi + +#Test sort +sort <$f >x +wc <$f >x1 +wc <x >x2 +if cmp -s x1 x2; then : ; else echo Error on sort test 1; fi +cat >x <<END +demit 10 +epitonic 40 +apoop 20 +bibelot 3 +comate 4 +END +cat >y <<END +apoop 20 +bibelot 3 +comate 4 +demit 10 +epitonic 40 +END +cat >z <<END +epitonic 40 +demit 10 +comate 4 +bibelot 3 +apoop 20 +END +sort <x >x1 +if cmp -s y x1; then : ; else echo Error on sort test 2; fi +sort -r <x1 >x2 +if cmp -s x2 z; then : ; else echo Error on sort test 3; fi +sort +1 -n <x |head -1 >y +echo "bibelot 3" >z +if cmp -s y z; then : ; else echo Error on sort test 4; fi + +#Test tail +tail -1 f1 >x +if cmp -s x special; then : ; else echo Error on tail test 1; fi + +#Test tsort +cat >x <<END +a d +b e +c f +a c +p z +k p +a k +a b +b c +c d +d e +e f +f k +END +cat >answer <<END +a +b +c +d +e +f +k +p +z +END +tsort <x >y +if cmp -s y answer; then : ; else echo Error on tsort test 1; fi + +#Test uue/uud +cp $f x +uue x +if test -s x.uue; then : ; else echo Error on uue/uud test 1; fi +rm x +uud x.uue +if cmp -s x $f; then : ; else echo Error on uue/uud test 2; fi + +compress -fc x >x.Z 2>/dev/null +uue x.Z +rm x x.Z +uud x.uue +compress -cd x.Z >x +if cmp -s x $f; then : ; else echo Error on uue/uud test 3; fi + +cd .. +rm -rf DIR_SH1 + +echo ok diff --git a/test/sh2.sh b/test/sh2.sh new file mode 100755 index 000000000..325f23136 --- /dev/null +++ b/test/sh2.sh @@ -0,0 +1,260 @@ +#!/bin/sh + +# Shell script #2 used to test MINIX. + +PATH=:/bin:/usr/bin +export PATH + +# CC="exec cc -wo -F" # nonstandard flags for ACK :-( +CC=cc + +ARCH=`arch` + +echo -n "Shell test 2 " +rm -rf DIR_SH2 +mkdir DIR_SH2 # all files are created here +cd DIR_SH2 + +cat >file <<END +The time has come the walrus said to talk of many things +Of shoes and ships and sealing wax of cabbages and kings +Of why the sea is boiling hot and whether pigs have wings +END +f=file # scratch file + +cat >makefile <<END # create a makefile +all: x.c + @$CC x.c >/dev/null 2>&1 +END +cat >x.c <<END # create a C program +#include <stdio.h> +char s[] = {"MS-DOS: Just say no"}; /* used by strings later */ +main() +{ + int i; + for (i = 15; i < 18; i++) printf("%d\\n",i*i); +} +END + +cat >answer <<END # C program should produce these results +225 +256 +289 +END + +make +if test -f a.out; then : ; else echo Compilation failed; fi +a.out >x +if test -f x; then : ; else echo No compiler output; fi +if cmp -s x answer; then : ; else echo Error in cc test 1; fi + +#Test chmod +echo Hi there folks >x +if test -r x; then : ; else echo Error on chmod test 1; fi +chmod 377 x +if test -r x; then test -w / || echo Error on chmod test 2; fi +chmod 700 x +if test -r x; then : ; else echo Error on chmod test 3; fi + +#Test cut +cat >x <<END # x is a test file with 3 columns +1 white bunny +2 gray rabbits +3 brown hares +4 black conies +END + +cat >answer <<END # after cutting out cols 3-7, we get this +white +gray +brown +black +END + +cut -c 3-7 x >y # extract columns 3-7 +if cmp -s y answer; then : ; else echo Error in cut test 1; fi + +#Test dd +dd if=$f of=x bs=12 count=1 2>/dev/null # x = bytes 0-11 +dd if=$f of=y bs=6 count=4 skip=2 2>/dev/null # y = bytes 11-35 +cat x y >z # z = bytes 0-35 +dd if=$f of=answer bs=9 count=4 2>/dev/null # answer = bytes 0-35 +if cmp -s z answer; then : ; else echo Error in dd test 1; fi + +#Test df # hard to make a sensible Test here +rm ? +df >x +if test -r x; then : ; else echo Error in df Test 1; fi + +#Test du # see df +rm ? +du >x +if test -r x; then : ; else echo Error in du Test 1; fi + +#Test od +head -1 $f |od >x # see if od converts ascii to octal ok +if [ $ARCH = i86 -o $ARCH = i386 ] +then +cat >answer <<END +0000000 064124 020145 064564 062555 064040 071541 061440 066557 +0000020 020145 064164 020145 060567 071154 071565 071440 064541 +0000040 020144 067564 072040 066141 020153 063157 066440 067141 +0000060 020171 064164 067151 071547 000012 +0000071 +END +else +cat >answer <<END +0000000 052150 062440 072151 066545 020150 060563 020143 067555 +0000020 062440 072150 062440 073541 066162 072563 020163 060551 +0000040 062040 072157 020164 060554 065440 067546 020155 060556 +0000060 074440 072150 064556 063563 005000 +0000071 +END +fi + +if cmp -s x answer; then : ; else echo Error in od test 1; fi + +head -1 $f |od -d >x # see if od converts ascii to decimal ok +if [ $ARCH = i86 -o $ARCH = i386 ] +then +cat >answer <<END +0000000 26708 08293 26996 25965 26656 29537 25376 28015 +0000020 08293 26740 08293 24951 29292 29557 29472 26977 +0000040 08292 28532 29728 27745 08299 26223 27936 28257 +0000060 08313 26740 28265 29543 00010 +0000071 +END +else +cat >answer <<END +0000000 21608 25888 29801 28005 08296 24947 08291 28525 +0000020 25888 29800 25888 30561 27762 30067 08307 24937 +0000040 25632 29807 08308 24940 27424 28518 08301 24942 +0000060 31008 29800 26990 26483 02560 +0000071 +END +fi + +if cmp -s x answer; then : ; else echo Error in od test 2; fi + +#Test paste +cat >x <<END +red +green +blue +END + +cat >y <<END +rood +groen +blauw +END +cat >answer <<END +red rood +green groen +blue blauw +END + +paste x y >z +if cmp -s z answer; then : ; else echo Error in paste test 1; fi + +#Test prep +echo >x <<END +"Hi," said Carol, laughing, "How's life?" +END + +echo >answer <<END +hi +said +carol +laughing +how's +life +END + +if cmp -s x answer; then : ; else echo Error in prep test 1; fi + +#Test printenv +printenv >x +if grep HOME x >/dev/null; then : ; else echo Error in printenv test 1; fi +if grep PATH x >/dev/null; then : ; else echo Error in printenv test 2; fi +if grep SHELL x >/dev/null; then : ; else echo Error in printenv test 3; fi +if grep USER x >/dev/null; then : ; else echo Error in printenv test 4; fi + +#Test pwd +pwd >Pwd_file +cd `pwd` +pwd >x +if test -s Pwd_file; then : ; else echo Error in pwd test 1; fi +if cmp -s Pwd_file x; then : ; else echo Error in pwd test 2; fi + +#Test strings +strings a.out | grep "MS-DOS" >x +cat >answer <<END +MS-DOS: Just say no +END + +if cmp -s x answer; then : ; else echo Error in strings test 1; fi + +#Test sum +sum $f >x +cat >answer <<END +29904 1 +END + +if cmp -s x answer; then : ; else echo Error in sum test 1; fi + +#Test tee +cat $f | tee x >/dev/null +if cmp -s x $f; then : ; else echo Error in tee test 1; fi + +#Test true +if true ; then : ; else echo Error in true test 1; fi + +#Test uniq +cat >x <<END +100 +200 +200 +300 +END + +cat >answer <<END +100 +200 +300 +END + +uniq <x >y +if cmp -s y answer; then : ; else echo Error in uniq test 1; fi + +#Test pipelines +cat >x <<END +the big black dog +the little white cat +the big white sheep +the little black cat +END + +cat >answer <<END + 2 big + 2 black + 2 cat + 1 dog + 2 little + 1 sheep + 4 the + 2 white +END + +prep x | sort | uniq -c >y1 +sort +1 <y1 >y +if cmp -s y answer; then : ; else echo Error in pipeline test 1; fi + +cat $f $f $f | sort | uniq >x +sort <$f >y +if cmp -s x y; then : ; else echo Error in pipeline test 2; fi + +cd .. +rm -rf DIR_SH2 + +echo ok diff --git a/test/t10a.c b/test/t10a.c new file mode 100644 index 000000000..5d0c756f9 --- /dev/null +++ b/test/t10a.c @@ -0,0 +1,8 @@ +#include <stdlib.h> + +_PROTOTYPE(int main, (void)); + +int main() +{ + exit(0); +} diff --git a/test/t11a.c b/test/t11a.c new file mode 100644 index 000000000..20f59cad6 --- /dev/null +++ b/test/t11a.c @@ -0,0 +1,71 @@ +/* t11a */ + +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#define MAX_ERROR 4 + +int errct, subtest=1; + +_PROTOTYPE(int main, (int argc, char *argv [], char *envp [])); +_PROTOTYPE(int diff, (char *s1, char *s2)); +_PROTOTYPE(void e, (int n)); + +int main(argc, argv, envp) +int argc; +char *argv[], *envp[]; +{ +/* See if arguments passed ok. */ + + char aa[4]; + + if (diff(argv[0], "t11a")) e(21); + if (diff(argv[1], "arg0")) e(22); + if (diff(argv[2], "arg1")) e(23); + if (diff(argv[3], "arg2")) e(24); + if (diff(envp[0], "spring")) e(25); + if (diff(envp[1], "summer")) e(26); + if (argc != 4) e(27); + + /* Now see if the files are ok. */ + if (read(3, aa, 4) != 2) e(28); + if (aa[0] != 7 || aa[1] != 9) e(29); + + if (getuid() == 10) e(30); + if (geteuid() != 10) e(31); + if (getgid() == 20) e(32); + if (getegid() != 20) e(33); + + if (open("t1", 0) < 0) e(34); + if (open("t2", 0) < 0) e(35); + exit(100); +} + +int diff(s1, s2) +char *s1, *s2; +{ + while (1) { + if (*s1 == 0 && *s2 == 0) return(0); + if (*s1 != *s2) return (1); + s1++; + s2++; + } +} + + +void e(n) +int n; +{ + printf("Subtest %d, error %d errno=%d ", subtest, n, errno); + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} diff --git a/test/t11b.c b/test/t11b.c new file mode 100644 index 000000000..c0b28a912 --- /dev/null +++ b/test/t11b.c @@ -0,0 +1,55 @@ +/* t11b */ + +#include <sys/types.h> +#include <errno.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#define MAX_ERROR 4 + +int errct, subtest=1; + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(int diff, (char *s1, char *s2)); +_PROTOTYPE(void e, (int n)); + +int main(argc, argv) +int argc; +char *argv[]; +{ +/* See if arguments passed ok. */ + + if (diff(argv[0], "t11b")) e(31); + if (diff(argv[1], "abc")) e(32); + if (diff(argv[2], "defghi")) e(33); + if (diff(argv[3], "j")) e(34); + if (argv[4] != 0) e(35); + if (argc != 4) e(36); + + exit(75); +} + +int diff(s1, s2) +char *s1, *s2; +{ + while (1) { + if (*s1 == 0 && *s2 == 0) return(0); + if (*s1 != *s2) return (1); + s1++; + s2++; + } +} + +void e(n) +int n; +{ + printf("Subtest %d, error %d errno=%d ", subtest, n, errno); + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} diff --git a/test/test1.c b/test/test1.c new file mode 100644 index 000000000..3054d3c63 --- /dev/null +++ b/test/test1.c @@ -0,0 +1,150 @@ +/* test 1 */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#define SIGNUM 10 +#define MAX_ERROR 4 +#define ITERATIONS 10 + +_VOLATILE int glov, gct; +int errct; +int subtest; + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void test1a, (void)); +_PROTOTYPE(void parent, (void)); +_PROTOTYPE(void child, (int i)); +_PROTOTYPE(void test1b, (void)); +_PROTOTYPE(void parent1, (int childpid)); +_PROTOTYPE(void func, (int s)); +_PROTOTYPE(void child1, (void)); +_PROTOTYPE(void e, (int n)); +_PROTOTYPE(void quit, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + + if (argc == 2) m = atoi(argv[1]); + + printf("Test 1 "); + fflush(stdout); /* have to flush for child's benefit */ + + system("rm -rf DIR_01; mkdir DIR_01"); + chdir("DIR_01"); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 00001) test1a(); + if (m & 00002) test1b(); + } + + quit(); + return(-1); /* impossible */ +} + +void test1a() +{ + int i, n, pid; + + subtest = 1; + n = 4; + for (i = 0; i < n; i++) { + if ((pid = fork())) { + if (pid < 0) { + printf("\nTest 1 fork failed\n"); + exit(1); + } + parent(); + } else + child(i); + } +} + +void parent() +{ + + int n; + + n = getpid(); + wait(&n); +} + +void child(i) +int i; +{ + int n; + + n = getpid(); + exit(100+i); +} + +void test1b() +{ + int i, k; + + subtest = 2; + for (i = 0; i < 4; i++) { + glov = 0; + signal(SIGNUM, func); + if ((k = fork())) { + if (k < 0) { + printf("Test 1 fork failed\n"); + exit(1); + } + parent1(k); + } else + child1(); + } +} + + +void parent1(childpid) +int childpid; +{ + + int n; + + for (n = 0; n < 5000; n++); + while (kill(childpid, SIGNUM) < 0) /* null statement */ + ; + wait(&n); +} + +void func(s) +int s; /* for ANSI */ +{ + glov++; + gct++; +} + +void child1() +{ + while (glov == 0); + exit(gct); +} + + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test10.c b/test/test10.c new file mode 100644 index 000000000..1b73dbe42 --- /dev/null +++ b/test/test10.c @@ -0,0 +1,153 @@ +/* test 10 */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +char *name[] = {"t10a", "t10b", "t10c", "t10d", "t10e", "t10f", "t10g", + "t10h", "t10i", "t10j"}; +int errct; +long prog[300]; +int psize; + +_PROTOTYPE(int main, (void)); +_PROTOTYPE(void spawn, (int n)); +_PROTOTYPE(void mkfiles, (void)); +_PROTOTYPE(void cr_file, (char *name, int size)); +_PROTOTYPE(void rmfiles, (void)); +_PROTOTYPE(void quit, (void)); + +int main() +{ + int i, n, pid, r; + + printf("Test 10 "); + fflush(stdout); /* have to flush for child's benefit */ + + system("rm -rf DIR_10; mkdir DIR_10; cp t10a DIR_10"); + chdir("DIR_10"); + + pid = getpid(); + + /* Create files t10b ... t10h */ + mkfiles(); + + if (getpid() == pid) + if (fork() == 0) { + execl("t10a", (char *) 0); + exit(0); + } + if (getpid() == pid) + if (fork() == 0) { + execl("t10b", (char *) 0); + exit(0); + } + if (getpid() == pid) + if (fork() == 0) { + execl("t10c", (char *) 0); + exit(0); + } + if (getpid() == pid) + if (fork() == 0) { + execl("t10d", (char *) 0); + exit(0); + } + + srand(100); + for (i = 0; i < 60; i++) { + r = rand() & 07; + spawn(r); + } + + for (i = 0; i < 4; i++) wait(&n); + rmfiles(); + quit(); + return(-1); /* impossible */ +} + +void spawn(n) +int n; +{ + int pid, k; + + if (pid = fork()) { + wait(&n); /* wait for some child (any one) */ + } else { + k = execl(name[n], (char *) 0); + errct++; + printf("Child execl didn't take. file=%s errno=%d\n", name[n], errno); + rmfiles(); + exit(1); + printf("Worse yet, EXIT didn't exit\n"); + } +} + +void mkfiles() +{ + int fd; + fd = open("t10a", 0); + if (fd < 0) { + printf("Can't open t10a\n"); + exit(1); + } + psize = read(fd, (char *) prog, 300 * 4); + cr_file("t10b", 1600); + cr_file("t10c", 1400); + cr_file("t10d", 2300); + cr_file("t10e", 3100); + cr_file("t10f", 2400); + cr_file("t10g", 1700); + cr_file("t10h", 1500); + cr_file("t10i", 4000); + cr_file("t10j", 2250); + close(fd); +} + + +void cr_file(name, size) +char *name; +int size; + +{ + int fd; + +#if (CHIP == SPARC) + size += 4000; +#endif + prog[6] = (long) size; + fd = creat(name, 0755); + write(fd, (char *) prog, psize); + close(fd); +} + +void rmfiles() +{ + unlink("t10b"); + unlink("t10c"); + unlink("t10d"); + unlink("t10e"); + unlink("t10f"); + unlink("t10g"); + unlink("t10h"); + unlink("t10i"); + unlink("t10j"); +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test11.c b/test/test11.c new file mode 100644 index 000000000..22c5bcc9d --- /dev/null +++ b/test/test11.c @@ -0,0 +1,228 @@ +/* test 11 */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +#define ITERATIONS 10 +#define MAX_ERROR 1 + +int errct, subtest; +char *envp[3] = {"spring", "summer", 0}; +char *passwd_file = "/etc/passwd"; + +_PROTOTYPE(int main, (int argc, char *argv[])); +_PROTOTYPE(void test11a, (void)); +_PROTOTYPE(void test11b, (void)); +_PROTOTYPE(void test11c, (void)); +_PROTOTYPE(void test11d, (void)); +_PROTOTYPE(void e, (int n)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + if (argc == 2) m = atoi(argv[1]); + + printf("Test 11 "); + fflush(stdout); /* have to flush for child's benefit */ + + if (geteuid() != 0) { + printf("must be setuid root; test aborted\n"); + exit(1); + } + if (getuid() == 0) { + printf("must be setuid root logged in as someone else; test aborted\n"); + exit(1); + } + +/* + system("rm -rf DIR_11; mkdir DIR_11"); + chdir("DIR_11"); +*/ + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test11a(); + if (m & 0002) test11b(); + if (m & 0004) test11c(); + if (m & 0010) test11d(); + } + if (errct == 0) + printf("ok\n"); + else + printf(" %d errors\n", errct); + +/* + chdir(".."); + system("rm -rf DIR_11"); +*/ + return(0); +} + + +void test11a() +{ +/* Test exec */ + int n, fd; + char aa[4]; + + + subtest = 1; + + if (fork()) { + wait(&n); + if (n != 25600) e(1); + unlink("t1"); + unlink("t2"); + } else { + if (chown("t11a", 10, 20) < 0) e(2); + chmod("t11a", 0666); + + /* The following call should fail because the mode has no X + * bits on. If a bug lets it unexpectedly succeed, the child + * will print an error message since the arguments are wrong. + */ + execl("t11a", (char *) 0, envp); /* should fail -- no X bits */ + + /* Control should come here after the failed execl(). */ + chmod("t11a", 06555); + if ((fd = creat("t1", 0600)) != 3) e(3); + if (close(fd) < 0) e(4); + if (open("t1", O_RDWR) != 3) e(5); + if (chown("t1", 10, 99) < 0) e(6); + if ((fd = creat("t2", 0060)) != 4) e(7); + if (close(fd) < 0) e(8); + if (open("t2", O_RDWR) != 4) e(9); + if (chown("t2", 99, 20) < 0) e(10); + if (setgid(6) < 0) e(11); + if (setuid(5) < 0) e(12); + if (getuid() != 5) e(13); + if (geteuid() != 5) e(14); + if (getgid() != 6) e(15); + if (getegid() != 6) e(16); + aa[0] = 3; + aa[1] = 5; + aa[2] = 7; + aa[3] = 9; + if (write(3, aa, 4) != 4) e(17); + lseek(3, 2L, 0); + execle("t11a", "t11a", "arg0", "arg1", "arg2", (char *) 0, envp); + e(18); + printf("Can't exec t11a\n"); + exit(3); + } +} + + + +void test11b() +{ + int n; + char *argv[5]; + + subtest = 2; + if (fork()) { + wait(&n); + if (n != (75 << 8)) e(20); + } else { + /* Child tests execv. */ + argv[0] = "t11b"; + argv[1] = "abc"; + argv[2] = "defghi"; + argv[3] = "j"; + argv[4] = 0; + execv("t11b", argv); + e(19); + } +} + +void test11c() +{ +/* Test getlogin() and cuserid(). This test MUST run setuid root. */ + + int n, etc_uid; + uid_t ruid, euid; + char *lnamep, *cnamep, *p; + char array[L_cuserid], save[L_cuserid], save2[L_cuserid]; + FILE *stream; + + subtest = 3; + errno = -2000; /* None of these calls set errno. */ + array[0] = '@'; + array[1] = '0'; + save[0] = '#'; + save[1] = '0'; + ruid = getuid(); + euid = geteuid(); + lnamep = getlogin(); + strcpy(save, lnamep); + cnamep = cuserid(array); + strcpy(save2, cnamep); + + /* Because we are setuid root, cuser == array == 'root'; login != 'root' */ + if (euid != 0) e(1); + if (ruid == 0) e(2); + if (strcmp(cnamep, "root") != 0) e(3); + if (strcmp(array, "root") != 0) e(4); + if ( (n = strlen(save)) == 0) e(5); + if (strcmp(save, cnamep) == 0) e(6); /* they must be different */ + cnamep = cuserid(NULL); + if (strcmp(cnamep, save2) != 0) e(7); + + /* Check login against passwd file. First lookup login in /etc/passwd. */ + if (n == 0) return; /* if login not found, don't look it up */ + if ( (stream = fopen(passwd_file, "r")) == NULL) e(8); + while (fgets(array, L_cuserid, stream) != NULL) { + if (strncmp(array, save, n) == 0) { + p = &array[0]; /* hunt for uid */ + while (*p != ':') p++; + p++; + while (*p != ':') p++; + p++; /* p now points to uid */ + etc_uid = atoi(p); + if (etc_uid != ruid) e(9); + break; /* 1 entry per login please */ + } + } + fclose(stream); +} + +void test11d() +{ + int fd; + struct stat statbuf; + + subtest = 4; + fd = creat("T11.1", 0750); + if (fd < 0) e(1); + if (chown("T11.1", 8, 1) != 0) e(2); + if (chmod("T11.1", 0666) != 0) e(3); + if (stat("T11.1", &statbuf) != 0) e(4); + if ((statbuf.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) != 0666) e(5); + if (close(fd) != 0) e(6); + if (unlink("T11.1") != 0) e(7); +} + +void e(n) +int n; +{ + int err_num = errno; /* save errno in case printf clobbers it */ + + printf("Subtest %d, error %d errno=%d ", subtest, n, errno); + errno = err_num; /* restore errno, just in case */ + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} diff --git a/test/test12.c b/test/test12.c new file mode 100644 index 000000000..29e95ecd8 --- /dev/null +++ b/test/test12.c @@ -0,0 +1,55 @@ +/* test 12 */ + +/* Copyright (C) 1987 by Martin Leisner. All rights reserved. */ +/* Used by permission. */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#define NUM_TIMES 1000 + +int errct = 0; + +_PROTOTYPE(int main, (void)); +_PROTOTYPE(void quit, (void)); + +int main() +{ + register int i; + int k; + + printf("Test 12 "); + fflush(stdout); /* have to flush for child's benefit */ + + system("rm -rf DIR_12; mkdir DIR_12"); + chdir("DIR_12"); + + for (i = 0; i < NUM_TIMES; i++) switch (fork()) { + case 0: exit(1); break; + case -1: + printf("fork broke\n"); + exit(1); + default: wait(&k); break; + } + + quit(); + return(-1); /* impossible */ +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test13.c b/test/test13.c new file mode 100644 index 000000000..081775b82 --- /dev/null +++ b/test/test13.c @@ -0,0 +1,72 @@ +/* test 13 */ + +/* File: pipes.c - created by Marty Leisner */ +/* Leisner.Henr 1-Dec-87 8:55:04 */ + +/* Copyright (C) 1987 by Martin Leisner. All rights reserved. */ +/* Used by permission. */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#define BLOCK_SIZE 1000 +#define NUM_BLOCKS 1000 + +int errct = 0; +char buffer[BLOCK_SIZE]; + +_PROTOTYPE(int main, (void)); +_PROTOTYPE(void quit, (void)); + +int main() +{ + int stat_loc, pipefd[2]; + register int i; + pipe(pipefd); + + printf("Test 13 "); + fflush(stdout); /* have to flush for child's benefit */ + + system("rm -rf DIR_13; mkdir DIR_13"); + chdir("DIR_13"); + + pipe(pipefd); + + switch (fork()) { + case 0: + /* Child code */ + for (i = 0; i < NUM_BLOCKS; i++) + if (read(pipefd[0], buffer, BLOCK_SIZE) != BLOCK_SIZE) break; + exit(0); + + case -1: + perror("fork broke"); + exit(1); + + default: + /* Parent code */ + for (i = 0; i < NUM_BLOCKS; i++) write(pipefd[1], buffer, BLOCK_SIZE); + wait(&stat_loc); + break; + } + quit(); + return(-1); /* impossible */ +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test14.c b/test/test14.c new file mode 100644 index 000000000..39f2437e4 --- /dev/null +++ b/test/test14.c @@ -0,0 +1,82 @@ +/* Test 14. unlinking an open file. */ + +#include <sys/types.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#define TRIALS 100 +#define MAX_ERROR 4 + +char name[20] = {"TMP14."}; +int errct; +int subtest = 1; + +_PROTOTYPE(int main, (void)); +_PROTOTYPE(void e, (int n)); +_PROTOTYPE(void quit, (void)); + +int main() +{ + int fd0, i, pid; + + printf("Test 14 "); + fflush(stdout); + + system("rm -rf DIR_14; mkdir DIR_14"); + chdir("DIR_14"); + + pid = getpid(); + name[6] = (pid & 037) + 33; + name[7] = ((pid * pid) & 037) + 33; + name[8] = 0; + + + for (i = 0; i < TRIALS; i++) { + if ( (fd0 = creat(name, 0777)) < 0) e(1); + if (write(fd0, name, 20) != 20) e(2); + if (unlink(name) != 0) e(3); + if (close(fd0) != 0) e(4); + } + + + fd0 = creat(name, 0777); + write(fd0, name, 20); + unlink(name); + quit(); + return(-1); /* impossible */ +} + + +void e(n) +int n; +{ + int err_num = errno; /* save errno in case printf clobbers it */ + + printf("Subtest %d, error %d errno=%d ", subtest, n, errno); + errno = err_num; /* restore errno, just in case */ + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test15.c b/test/test15.c new file mode 100644 index 000000000..630d4d4a3 --- /dev/null +++ b/test/test15.c @@ -0,0 +1,705 @@ +/* Test program for string(3) routines. + * + * Slightly modified from Henry Spencer's original routine. + * - incorporates semantic changes per the ANSI standard (original tests + * can be recovered by defining the symbol NOT_ANSI while compiling, + * except for the change of memcpy() to memmove()). + * - makes additional tests of functions on unaligned buffers and strings. + */ + +#include <sys/types.h> +#include <fcntl.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> + +_PROTOTYPE( int chdir, (char *_path)); /* from <unistd.h> */ + +#define STREQ(a, b) (strcmp((a), (b)) == 0) + +char *it = "<UNSET>"; /* Routine name for message routines. */ +int errct; /* count errors */ +int waserror = 0; /* For exit status. */ + +char uctest[] = "\004\203"; /* For testing signedness of chars. */ +int charsigned; /* Result. */ + +_PROTOTYPE(void check, (int thing, int number)); +_PROTOTYPE(void equal, (char *a, char *b, int number)); +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void first, (void)); +_PROTOTYPE(void second, (void)); +_PROTOTYPE(void quit, (void)); + +/* + - check - complain if condition is not true + */ +void check(thing, number) +int thing; +int number; /* Test number for error message. */ +{ + if (!thing) { + printf("%s flunked test %d\n", it, number); + waserror = 1; + errct++; + } +} + +/* + - equal - complain if first two args don't strcmp as equal + */ +void equal(a, b, number) +char *a; +char *b; +int number; /* Test number for error message. */ +{ + check(a != NULL && b != NULL && STREQ(a, b), number); +} + +char one[50]; +char two[50]; + +#ifdef UNIXERR +#define ERR 1 +#endif +#ifdef BERKERR +#define ERR 1 +#endif +#ifdef ERR +int f; +extern char *sys_errlist[]; +extern int sys_nerr; +#endif + +/* ARGSUSED */ +int main(argc, argv) +int argc; +char *argv[]; +{ + printf("Test 15 "); + fflush(stdout); + + system("rm -rf DIR_15; mkdir DIR_15"); + chdir("DIR_15"); + /* First, establish whether chars are signed. */ + if (uctest[0] < uctest[1]) + charsigned = 0; + else + charsigned = 1; + + /* Then, do the rest of the work. Split into two functions because + * some compilers get unhappy about a single immense function. */ + first(); + second(); + + errct = waserror; + quit(); + return(-1); /* impossible */ +} + +void first() +{ + /* Test strcmp first because we use it to test other things. */ + it = "strcmp"; + check(strcmp("", "") == 0, 1);/* Trivial case. */ + check(strcmp("a", "a") == 0, 2); /* Identity. */ + check(strcmp("abc", "abc") == 0, 3); /* Multicharacter. */ + check(strcmp("abc", "abcd") < 0, 4); /* Length mismatches. */ + check(strcmp("abcd", "abc") > 0, 5); + check(strcmp("abcd", "abce") < 0, 6); /* Honest miscompares. */ + check(strcmp("abce", "abcd") > 0, 7); + check(strcmp("a\203", "a") > 0, 8); /* Tricky if char signed. */ + +#ifdef NOT_ANSI + if (charsigned) + check(strcmp("a\203", "a\003") < 0, 9); + else + check(strcmp("a\203", "a\003") > 0, 9); +#else + check(strcmp("a\203", "a\003") > 0, 9); +#endif + + check(strcmp("abcd" + 1, "abcd" + 1) == 0, 10); /* Unaligned tests. */ + check(strcmp("abcd" + 1, "abce" + 1) < 0, 11); + check(strcmp("abcd" + 1, "bcd") == 0, 12); + check(strcmp("abce" + 1, "bcd") > 0, 13); + check(strcmp("abcd" + 2, "bcd" + 1) == 0, 14); + check(strcmp("abcd" + 2, "bce" + 1) < 0, 15); + + /* Test strcpy next because we need it to set up other tests. */ + it = "strcpy"; + check(strcpy(one, "abcd") == one, 1); /* Returned value. */ + equal(one, "abcd", 2); /* Basic test. */ + + (void) strcpy(one, "x"); + equal(one, "x", 3); /* Writeover. */ + equal(one + 2, "cd", 4); /* Wrote too much? */ + + (void) strcpy(two, "hi there"); + (void) strcpy(one, two); + equal(one, "hi there", 5); /* Basic test encore. */ + equal(two, "hi there", 6); /* Stomped on source? */ + + (void) strcpy(one, ""); + equal(one, "", 7); /* Boundary condition. */ + + (void) strcpy(one, "abcdef" + 1); /* Unaligned tests. */ + equal(one, "bcdef", 8); + (void) strcpy(one + 1, "*xy" + 1); + equal(one, "bxy", 9); + equal(one + 4, "f", 10); + + /* Strcat */ + it = "strcat"; + (void) strcpy(one, "ijk"); + check(strcat(one, "lmn") == one, 1); /* Returned value. */ + equal(one, "ijklmn", 2); /* Basic test. */ + + (void) strcpy(one, "x"); + (void) strcat(one, "yz"); + equal(one, "xyz", 3); /* Writeover. */ + equal(one + 4, "mn", 4); /* Wrote too much? */ + + (void) strcpy(one, "gh"); + (void) strcpy(two, "ef"); + (void) strcat(one, two); + equal(one, "ghef", 5); /* Basic test encore. */ + equal(two, "ef", 6); /* Stomped on source? */ + + (void) strcpy(one, ""); + (void) strcat(one, ""); + equal(one, "", 7); /* Boundary conditions. */ + (void) strcpy(one, "ab"); + (void) strcat(one, ""); + equal(one, "ab", 8); + (void) strcpy(one, ""); + (void) strcat(one, "cd"); + equal(one, "cd", 9); + + /* Strncat - first test it as strcat, with big counts, then test the + * count mechanism. */ + it = "strncat"; + (void) strcpy(one, "ijk"); + check(strncat(one, "lmn", 99) == one, 1); /* Returned value. */ + equal(one, "ijklmn", 2); /* Basic test. */ + + (void) strcpy(one, "x"); + (void) strncat(one, "yz", 99); + equal(one, "xyz", 3); /* Writeover. */ + equal(one + 4, "mn", 4); /* Wrote too much? */ + + (void) strcpy(one, "gh"); + (void) strcpy(two, "ef"); + (void) strncat(one, two, 99); + equal(one, "ghef", 5); /* Basic test encore. */ + equal(two, "ef", 6); /* Stomped on source? */ + + (void) strcpy(one, ""); + (void) strncat(one, "", 99); + equal(one, "", 7); /* Boundary conditions. */ + (void) strcpy(one, "ab"); + (void) strncat(one, "", 99); + equal(one, "ab", 8); + (void) strcpy(one, ""); + (void) strncat(one, "cd", 99); + equal(one, "cd", 9); + + (void) strcpy(one, "ab"); + (void) strncat(one, "cdef", 2); + equal(one, "abcd", 10); /* Count-limited. */ + + (void) strncat(one, "gh", 0); + equal(one, "abcd", 11); /* Zero count. */ + + (void) strncat(one, "gh", 2); + equal(one, "abcdgh", 12); /* Count and length equal. */ + + (void) strcpy(one, "abcdefghij"); /* Unaligned tests. */ + (void) strcpy(one, "abcd"); + (void) strcpy(one, "abc"); + (void) strncat(one, "de" + 1, 1); + equal(one, "abce", 13); + equal(one + 4, "", 14); + equal(one + 5, "fghij", 15); + + /* Strncmp - first test as strcmp with big counts, then test count code. */ + it = "strncmp"; + check(strncmp("", "", 99) == 0, 1); /* Trivial case. */ + check(strncmp("a", "a", 99) == 0, 2); /* Identity. */ + check(strncmp("abc", "abc", 99) == 0, 3); /* Multicharacter. */ + check(strncmp("abc", "abcd", 99) < 0, 4); /* Length unequal. */ + check(strncmp("abcd", "abc", 99) > 0, 5); + check(strncmp("abcd", "abce", 99) < 0, 6); /* Honestly unequal. */ + check(strncmp("abce", "abcd", 99) > 0, 7); + check(strncmp("a\203", "a", 2) > 0, 8); /* Tricky if '\203' < 0 */ + +#ifdef NOT_ANSI + if (charsigned) + check(strncmp("a\203", "a\003", 2) < 0, 9); + else + check(strncmp("a\203", "a\003", 2) > 0, 9); +#else + check(strncmp("a\203", "a\003", 2) > 0, 9); +#endif + + check(strncmp("abce", "abcd", 3) == 0, 10); /* Count limited. */ + check(strncmp("abce", "abc", 3) == 0, 11); /* Count == length. */ + check(strncmp("abcd", "abce", 4) < 0, 12); /* Nudging limit. */ + check(strncmp("abc", "def", 0) == 0, 13); /* Zero count. */ + + /* Strncpy - testing is a bit different because of odd semantics */ + it = "strncpy"; + check(strncpy(one, "abc", 4) == one, 1); /* Returned value. */ + equal(one, "abc", 2); /* Did the copy go right? */ + + (void) strcpy(one, "abcdefgh"); + (void) strncpy(one, "xyz", 2); + equal(one, "xycdefgh", 3); /* Copy cut by count. */ + + (void) strcpy(one, "abcdefgh"); + (void) strncpy(one, "xyz", 3);/* Copy cut just before NUL. */ + equal(one, "xyzdefgh", 4); + + (void) strcpy(one, "abcdefgh"); + (void) strncpy(one, "xyz", 4);/* Copy just includes NUL. */ + equal(one, "xyz", 5); + equal(one + 4, "efgh", 6); /* Wrote too much? */ + + (void) strcpy(one, "abcdefgh"); + (void) strncpy(one, "xyz", 5);/* Copy includes padding. */ + equal(one, "xyz", 7); + equal(one + 4, "", 8); + equal(one + 5, "fgh", 9); + + (void) strcpy(one, "abc"); + (void) strncpy(one, "xyz", 0);/* Zero-length copy. */ + equal(one, "abc", 10); + + (void) strncpy(one, "", 2); /* Zero-length source. */ + equal(one, "", 11); + equal(one + 1, "", 12); + equal(one + 2, "c", 13); + + (void) strcpy(one, "hi there"); + (void) strncpy(two, one, 9); + equal(two, "hi there", 14); /* Just paranoia. */ + equal(one, "hi there", 15); /* Stomped on source? */ + + /* Strlen */ + it = "strlen"; + check(strlen("") == 0, 1); /* Empty. */ + check(strlen("a") == 1, 2); /* Single char. */ + check(strlen("abcd") == 4, 3);/* Multiple chars. */ + check(strlen("abcd" + 1) == 3, 4); /* Unaligned test. */ + + /* Strchr */ + it = "strchr"; + check(strchr("abcd", 'z') == NULL, 1); /* Not found. */ + (void) strcpy(one, "abcd"); + check(strchr(one, 'c') == one + 2, 2); /* Basic test. */ + check(strchr(one, 'd') == one + 3, 3); /* End of string. */ + check(strchr(one, 'a') == one, 4); /* Beginning. */ + check(strchr(one, '\0') == one + 4, 5); /* Finding NUL. */ + (void) strcpy(one, "ababa"); + check(strchr(one, 'b') == one + 1, 6); /* Finding first. */ + (void) strcpy(one, ""); + check(strchr(one, 'b') == NULL, 7); /* Empty string. */ + check(strchr(one, '\0') == one, 8); /* NUL in empty string. */ + + /* Index - just like strchr */ + it = "index"; + check(index("abcd", 'z') == NULL, 1); /* Not found. */ + (void) strcpy(one, "abcd"); + check(index(one, 'c') == one + 2, 2); /* Basic test. */ + check(index(one, 'd') == one + 3, 3); /* End of string. */ + check(index(one, 'a') == one, 4); /* Beginning. */ + check(index(one, '\0') == one + 4, 5); /* Finding NUL. */ + (void) strcpy(one, "ababa"); + check(index(one, 'b') == one + 1, 6); /* Finding first. */ + (void) strcpy(one, ""); + check(index(one, 'b') == NULL, 7); /* Empty string. */ + check(index(one, '\0') == one, 8); /* NUL in empty string. */ + + /* Strrchr */ + it = "strrchr"; + check(strrchr("abcd", 'z') == NULL, 1); /* Not found. */ + (void) strcpy(one, "abcd"); + check(strrchr(one, 'c') == one + 2, 2); /* Basic test. */ + check(strrchr(one, 'd') == one + 3, 3); /* End of string. */ + check(strrchr(one, 'a') == one, 4); /* Beginning. */ + check(strrchr(one, '\0') == one + 4, 5); /* Finding NUL. */ + (void) strcpy(one, "ababa"); + check(strrchr(one, 'b') == one + 3, 6); /* Finding last. */ + (void) strcpy(one, ""); + check(strrchr(one, 'b') == NULL, 7); /* Empty string. */ + check(strrchr(one, '\0') == one, 8); /* NUL in empty string. */ + + /* Rindex - just like strrchr */ + it = "rindex"; + check(rindex("abcd", 'z') == NULL, 1); /* Not found. */ + (void) strcpy(one, "abcd"); + check(rindex(one, 'c') == one + 2, 2); /* Basic test. */ + check(rindex(one, 'd') == one + 3, 3); /* End of string. */ + check(rindex(one, 'a') == one, 4); /* Beginning. */ + check(rindex(one, '\0') == one + 4, 5); /* Finding NUL. */ + (void) strcpy(one, "ababa"); + check(rindex(one, 'b') == one + 3, 6); /* Finding last. */ + (void) strcpy(one, ""); + check(rindex(one, 'b') == NULL, 7); /* Empty string. */ + check(rindex(one, '\0') == one, 8); /* NUL in empty string. */ +} + +void second() +{ + /* Strpbrk - somewhat like strchr */ + it = "strpbrk"; + check(strpbrk("abcd", "z") == NULL, 1); /* Not found. */ + (void) strcpy(one, "abcd"); + check(strpbrk(one, "c") == one + 2, 2); /* Basic test. */ + check(strpbrk(one, "d") == one + 3, 3); /* End of string. */ + check(strpbrk(one, "a") == one, 4); /* Beginning. */ + check(strpbrk(one, "") == NULL, 5); /* Empty search list. */ + check(strpbrk(one, "cb") == one + 1, 6); /* Multiple search. */ + (void) strcpy(one, "abcabdea"); + check(strpbrk(one, "b") == one + 1, 7); /* Finding first. */ + check(strpbrk(one, "cb") == one + 1, 8); /* With multiple search. */ + check(strpbrk(one, "db") == one + 1, 9); /* Another variant. */ + (void) strcpy(one, ""); + check(strpbrk(one, "bc") == NULL, 10); /* Empty string. */ + check(strpbrk(one, "") == NULL, 11); /* Both strings empty. */ + + /* Strstr - somewhat like strchr */ + it = "strstr"; + check(strstr("abcd", "z") == NULL, 1); /* Not found. */ + check(strstr("abcd", "abx") == NULL, 2); /* Dead end. */ + (void) strcpy(one, "abcd"); + check(strstr(one, "c") == one + 2, 3); /* Basic test. */ + check(strstr(one, "bc") == one + 1, 4); /* Multichar. */ + check(strstr(one, "d") == one + 3, 5); /* End of string. */ + check(strstr(one, "cd") == one + 2, 6); /* Tail of string. */ + check(strstr(one, "abc") == one, 7); /* Beginning. */ + check(strstr(one, "abcd") == one, 8); /* Exact match. */ + check(strstr(one, "abcde") == NULL, 9); /* Too long. */ + check(strstr(one, "de") == NULL, 10); /* Past end. */ +#ifdef NOT_ANSI + check(strstr(one, "") == one + 4, 11); /* Finding empty. */ +#else + check(strstr(one, "") == one, 11); /* Finding empty. */ +#endif + (void) strcpy(one, "ababa"); + check(strstr(one, "ba") == one + 1, 12); /* Finding first. */ + (void) strcpy(one, ""); + check(strstr(one, "b") == NULL, 13); /* Empty string. */ + check(strstr(one, "") == one, 14); /* Empty in empty string. */ + (void) strcpy(one, "bcbca"); + check(strstr(one, "bca") == one + 2, 15); /* False start. */ + (void) strcpy(one, "bbbcabbca"); + check(strstr(one, "bbca") == one + 1, 16); /* With overlap. */ + + /* Strspn */ + it = "strspn"; + check(strspn("abcba", "abc") == 5, 1); /* Whole string. */ + check(strspn("abcba", "ab") == 2, 2); /* Partial. */ + check(strspn("abc", "qx") == 0, 3); /* None. */ + check(strspn("", "ab") == 0, 4); /* Null string. */ + check(strspn("abc", "") == 0, 5); /* Null search list. */ + + /* Strcspn */ + it = "strcspn"; + check(strcspn("abcba", "qx") == 5, 1); /* Whole string. */ + check(strcspn("abcba", "cx") == 2, 2); /* Partial. */ + check(strcspn("abc", "abc") == 0, 3); /* None. */ + check(strcspn("", "ab") == 0, 4); /* Null string. */ + check(strcspn("abc", "") == 3, 5); /* Null search list. */ + + /* Strtok - the hard one */ + it = "strtok"; + (void) strcpy(one, "first, second, third"); + equal(strtok(one, ", "), "first", 1); /* Basic test. */ + equal(one, "first", 2); + equal(strtok((char *) NULL, ", "), "second", 3); + equal(strtok((char *) NULL, ", "), "third", 4); + check(strtok((char *) NULL, ", ") == NULL, 5); + (void) strcpy(one, ", first, "); + equal(strtok(one, ", "), "first", 6); /* Extra delims, 1 tok. */ + check(strtok((char *) NULL, ", ") == NULL, 7); + (void) strcpy(one, "1a, 1b; 2a, 2b"); + equal(strtok(one, ", "), "1a", 8); /* Changing delim lists. */ + equal(strtok((char *) NULL, "; "), "1b", 9); + equal(strtok((char *) NULL, ", "), "2a", 10); + (void) strcpy(two, "x-y"); + equal(strtok(two, "-"), "x", 11); /* New string before done. */ + equal(strtok((char *) NULL, "-"), "y", 12); + check(strtok((char *) NULL, "-") == NULL, 13); + (void) strcpy(one, "a,b, c,, ,d"); + equal(strtok(one, ", "), "a", 14); /* Different separators. */ + equal(strtok((char *) NULL, ", "), "b", 15); + equal(strtok((char *) NULL, " ,"), "c", 16); /* Permute list too. */ + equal(strtok((char *) NULL, " ,"), "d", 17); + check(strtok((char *) NULL, ", ") == NULL, 18); + check(strtok((char *) NULL, ", ") == NULL, 19); /* Persistence. */ + (void) strcpy(one, ", "); + check(strtok(one, ", ") == NULL, 20); /* No tokens. */ + (void) strcpy(one, ""); + check(strtok(one, ", ") == NULL, 21); /* Empty string. */ + (void) strcpy(one, "abc"); + equal(strtok(one, ", "), "abc", 22); /* No delimiters. */ + check(strtok((char *) NULL, ", ") == NULL, 23); + (void) strcpy(one, "abc"); + equal(strtok(one, ""), "abc", 24); /* Empty delimiter list. */ + check(strtok((char *) NULL, "") == NULL, 25); + (void) strcpy(one, "abcdefgh"); + (void) strcpy(one, "a,b,c"); + equal(strtok(one, ","), "a", 26); /* Basics again... */ + equal(strtok((char *) NULL, ","), "b", 27); + equal(strtok((char *) NULL, ","), "c", 28); + check(strtok((char *) NULL, ",") == NULL, 29); + equal(one + 6, "gh", 30); /* Stomped past end? */ + equal(one, "a", 31); /* Stomped old tokens? */ + equal(one + 2, "b", 32); + equal(one + 4, "c", 33); + + /* Memcmp */ + it = "memcmp"; + check(memcmp("a", "a", 1) == 0, 1); /* Identity. */ + check(memcmp("abc", "abc", 3) == 0, 2); /* Multicharacter. */ + check(memcmp("abcd", "abce", 4) < 0, 3); /* Honestly unequal. */ + check(memcmp("abce", "abcd", 4) > 0, 4); + check(memcmp("alph", "beta", 4) < 0, 5); + +#ifdef NOT_ANSI + if (charsigned) + check(memcmp("a\203", "a\003", 2) < 0, 6); + else + check(memcmp("a\203", "a\003", 2) > 0, 6); +#else + check(memcmp("a\203", "a\003", 2) > 0, 6); +#endif + + check(memcmp("abce", "abcd", 3) == 0, 7); /* Count limited. */ + check(memcmp("abc", "def", 0) == 0, 8); /* Zero count. */ + + check(memcmp("a" + 1, "a" + 1, 1) == 0, 9); /* Unaligned tests. */ + check(memcmp("abc" + 1, "bc", 2) == 0, 10); + check(memcmp("bc", "abc" + 1, 2) == 0, 11); + check(memcmp("abcd" + 1, "abce" + 1, 3) < 0, 12); + check(memcmp("abce" + 1, "abcd" + 1, 3) > 0, 13); +/* + check(memcmp("a\203" + 1, "a\003" + 1, 1) > 0, 14); +*/ + check(memcmp("abcde" + 1, "abcdf" + 1, 3) == 0, 15); + + /* Memchr */ + it = "memchr"; + check(memchr("abcd", 'z', 4) == NULL, 1); /* Not found. */ + (void) strcpy(one, "abcd"); + check( (char *)memchr(one, 'c', 4) == one + 2, 2); /* Basic test. */ + check( (char *)memchr(one, 'd', 4) == one + 3, 3); /* End of string. */ + check( (char *)memchr(one, 'a', 4) == one, 4); /* Beginning. */ + check( (char *)memchr(one, '\0', 5) == one + 4, 5); /* Finding NUL. */ + (void) strcpy(one, "ababa"); + check( (char *)memchr(one, 'b', 5) == one + 1, 6); /* Finding first. */ + check( (char *)memchr(one, 'b', 0) == NULL, 7); /* Zero count. */ + check( (char *)memchr(one, 'a', 1) == one, 8); /* Singleton case. */ + (void) strcpy(one, "a\203b"); + check( (char *)memchr(one, 0203, 3) == one + 1, 9); /* Unsignedness. */ + + /* Memcpy + * Note that X3J11 says memcpy may fail on overlap. */ + it = "memcpy"; + check( (char *)memcpy(one, "abc", 4) == one, 1); /* Returned value. */ + equal(one, "abc", 2); /* Did the copy go right? */ + + (void) strcpy(one, "abcdefgh"); + (void) memcpy(one + 1, "xyz", 2); + equal(one, "axydefgh", 3); /* Basic test. */ + + (void) strcpy(one, "abc"); + (void) memcpy(one, "xyz", 0); + equal(one, "abc", 4); /* Zero-length copy. */ + + (void) strcpy(one, "hi there"); + (void) strcpy(two, "foo"); + (void) memcpy(two, one, 9); + equal(two, "hi there", 5); /* Just paranoia. */ + equal(one, "hi there", 6); /* Stomped on source? */ + + (void) strcpy(one, "abcde"); /* Unaligned tests. */ + (void) memcpy(one + 1, "\0\0\0\0\0", 1); + equal(one, "a", 7); + equal(one + 2, "cde", 8); + (void) memcpy(one + 1, "xyz" + 1, 2); + equal(one, "ayzde", 9); + (void) memcpy(one + 1, "xyz" + 1, 3); + equal(one, "ayz", 10); + + /* Memmove + * Note that X3J11 says memmove must work regardless of overlap. */ + it = "memmove"; + check( (char *)memmove(one, "abc", 4) == one, 1); /* Returned value. */ + equal(one, "abc", 2); /* Did the copy go right? */ + + (void) strcpy(one, "abcdefgh"); + (void) memmove(one + 1, "xyz", 2); + equal(one, "axydefgh", 3); /* Basic test. */ + + (void) strcpy(one, "abc"); + (void) memmove(one, "xyz", 0); + equal(one, "abc", 4); /* Zero-length copy. */ + + (void) strcpy(one, "hi there"); + (void) strcpy(two, "foo"); + (void) memmove(two, one, 9); + equal(two, "hi there", 5); /* Just paranoia. */ + equal(one, "hi there", 6); /* Stomped on source? */ + + (void) strcpy(one, "abcdefgh"); + (void) memmove(one + 1, one, 9); + equal(one, "aabcdefgh", 7); /* Overlap, right-to-left. */ + + (void) strcpy(one, "abcdefgh"); + (void) memmove(one + 1, one + 2, 7); + equal(one, "acdefgh", 8); /* Overlap, left-to-right. */ + + (void) strcpy(one, "abcdefgh"); + (void) memmove(one, one, 9); + equal(one, "abcdefgh", 9); /* 100% overlap. */ + + (void) strcpy(one, "abcde"); /* Unaligned tests. */ + (void) memmove(one + 1, "\0\0\0\0\0", 1); + equal(one, "a", 10); + equal(one + 2, "cde", 11); + (void) memmove(one + 1, "xyz" + 1, 2); + equal(one, "ayzde", 12); + (void) memmove(one + 1, "xyz" + 1, 3); + equal(one, "ayz", 13); + (void) strcpy(one, "abcdefgh"); + (void) memmove(one + 2, one + 1, 8); + equal(one, "abbcdefgh", 14); + + + /* Memccpy - first test like memcpy, then the search part + * The SVID, the only place where memccpy is mentioned, says overlap + * might fail, so we don't try it. Besides, it's hard to see the + * rationale for a non-left-to-right memccpy. */ + it = "memccpy"; + check(memccpy(one, "abc", 'q', 4) == NULL, 1); /* Returned value. */ + equal(one, "abc", 2); /* Did the copy go right? */ + + (void) strcpy(one, "abcdefgh"); + (void) memccpy(one + 1, "xyz", 'q', 2); + equal(one, "axydefgh", 3); /* Basic test. */ + + (void) strcpy(one, "abc"); + (void) memccpy(one, "xyz", 'q', 0); + equal(one, "abc", 4); /* Zero-length copy. */ + + (void) strcpy(one, "hi there"); + (void) strcpy(two, "foo"); + (void) memccpy(two, one, 'q', 9); + equal(two, "hi there", 5); /* Just paranoia. */ + equal(one, "hi there", 6); /* Stomped on source? */ + + (void) strcpy(one, "abcdefgh"); + (void) strcpy(two, "horsefeathers"); + check( (char *)memccpy(two, one, 'f', 9) == two + 6, 7);/* Returned value. */ + equal(one, "abcdefgh", 8); /* Source intact? */ + equal(two, "abcdefeathers", 9); /* Copy correct? */ + + (void) strcpy(one, "abcd"); + (void) strcpy(two, "bumblebee"); + check((char *)memccpy(two, one, 'a', 4) == two + 1, 10); /* First char. */ + equal(two, "aumblebee", 11); + check((char *)memccpy(two, one, 'd', 4) == two + 4, 12); /* Last char. */ + equal(two, "abcdlebee", 13); + (void) strcpy(one, "xyz"); + check((char *)memccpy(two, one, 'x', 1) == two + 1, 14); /* Singleton. */ + equal(two, "xbcdlebee", 15); + + /* Memset */ + it = "memset"; + (void) strcpy(one, "abcdefgh"); + check( (char *) memset(one + 1, 'x', 3) == one + 1, 1); /* Return value. */ + equal(one, "axxxefgh", 2); /* Basic test. */ + + (void) memset(one + 2, 'y', 0); + equal(one, "axxxefgh", 3); /* Zero-length set. */ + + (void) memset(one + 5, 0, 1); + equal(one, "axxxe", 4); /* Zero fill. */ + equal(one + 6, "gh", 5); /* And the leftover. */ + + (void) memset(one + 2, 010045, 1); + equal(one, "ax\045xe", 6); /* Unsigned char convert. */ + + /* Bcopy - much like memcpy + * Berklix manual is silent about overlap, so don't test it. */ + it = "bcopy"; + (void) bcopy("abc", one, 4); + equal(one, "abc", 1); /* Simple copy. */ + + (void) strcpy(one, "abcdefgh"); + (void) bcopy("xyz", one + 1, 2); + equal(one, "axydefgh", 2); /* Basic test. */ + + (void) strcpy(one, "abc"); + (void) bcopy("xyz", one, 0); + equal(one, "abc", 3); /* Zero-length copy. */ + + (void) strcpy(one, "hi there"); + (void) strcpy(two, "foo"); + (void) bcopy(one, two, 9); + equal(two, "hi there", 4); /* Just paranoia. */ + equal(one, "hi there", 5); /* Stomped on source? */ + + /* Bzero */ + it = "bzero"; + (void) strcpy(one, "abcdef"); + bzero(one + 2, 2); + equal(one, "ab", 1); /* Basic test. */ + equal(one + 3, "", 2); + equal(one + 4, "ef", 3); + + (void) strcpy(one, "abcdef"); + bzero(one + 2, 0); + equal(one, "abcdef", 4); /* Zero-length copy. */ + + /* Bcmp - somewhat like memcmp */ + it = "bcmp"; + check(bcmp("a", "a", 1) == 0, 1); /* Identity. */ + check(bcmp("abc", "abc", 3) == 0, 2); /* Multicharacter. */ + check(bcmp("abcd", "abce", 4) != 0, 3); /* Honestly unequal. */ + check(bcmp("abce", "abcd", 4) != 0, 4); + check(bcmp("alph", "beta", 4) != 0, 5); + check(bcmp("abce", "abcd", 3) == 0, 6); /* Count limited. */ + check(bcmp("abc", "def", 0) == 0, 8); /* Zero count. */ + +#ifdef ERR + /* Strerror - VERY system-dependent */ + it = "strerror"; + f = open("/", O_WRONLY); /* Should always fail. */ + check(f < 0 && errno > 0 && errno < sys_nerr, 1); + equal(strerror(errno), sys_errlist[errno], 2); +#ifdef UNIXERR + equal(strerror(errno), "Is a directory", 3); +#endif +#ifdef BERKERR + equal(strerror(errno), "Permission denied", 3); +#endif +#endif +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test16.c b/test/test16.c new file mode 100644 index 000000000..2e5f2ded7 --- /dev/null +++ b/test/test16.c @@ -0,0 +1,255 @@ +/* test 16 */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <utime.h> +#include <stdio.h> + +#define MAX_ERROR 4 + +int errct, subtest, passes; + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void test16a, (void)); +_PROTOTYPE(void get_times, (char *name, time_t *a, time_t *c, time_t *m)); +_PROTOTYPE(void e, (int n)); +_PROTOTYPE(void quit, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int i, m; + + m = (argc == 2 ? atoi(argv[1]) : 0xFFFF); + + system("rm -rf DIR_16; mkdir DIR_16"); + chdir("DIR_16"); + + printf("Test 16 "); + fflush(stdout); + for (i = 0; i < 4; i++) { + if (m & 0001) test16a(); + passes++; + } + quit(); + return(-1); /* impossible */ +} + + +void test16a() +{ +/* Test atime, ctime, and mtime. */ + + int fd, fd1, fd2, fd3, fd4; + time_t a, c, m, pa, pc, pm, xa, xc, xm, ya, yc, ym, za, zc, zm, ta, tc, tm; + time_t wa, wc, wm; + char buf[1024]; + struct stat s; + + subtest = 1; + if (passes > 0) return; /* takes too long to repeat this test */ + + if ( (fd = creat("T16.a", 0666)) < 0) e(1); + if (write(fd, buf, 1024) != 1024) e(2); + if (close(fd) < 0) e(3); + sleep(1); /* wait 1 sec before continuing */ + if ( (fd = open("T16.a", O_RDONLY)) < 0) e(4); + if (read(fd, buf, 3) != 3) e(5); + if (close(fd) != 0) e(6); + if (stat("T16.a", &s) != 0) e(7); + a = s.st_atime; + c = s.st_ctime; + m = s.st_mtime; + if (a == 0) { + /* Almost certainly means we are running a V1 file system. */ + printf(" (atime = 0. Probably V1 file system. V2 tests skipped.) "); + return; + } + + /* Many system calls affect atime, ctime, and mtime. Test them. They + * fall into several groups. The members of each group can be tested + * together. Start with creat(), mkdir(), and mkfifo, all of which + * set all 3 times on the created object, and ctime and mtime of the dir. + */ + if ( (fd = creat("T16.b", 0666)) < 0) e(8); + if (close(fd) != 0) e(9); + get_times("T16.b", &a, &c, &m); + get_times(".", &pa, &pc, &pm); + if (a != c) e(10); + if (a != m) e(11); + if (a != pc) e(12); + if (a != pm) e(13); + if (unlink("T16.b") < 0) e(14); + + /* Test the times for mkfifo. */ + if ( (fd = mkfifo("T16.c", 0666)) != 0) e(15); + if (access("T16.c", R_OK | W_OK) != 0) e(16); + get_times("T16.c", &a, &c, &m); + get_times(".", &pa, &pc, &pm); + if (a != c) e(17); + if (a != m) e(18); + if (a != pc) e(19); + if (a != pm) e(20); + if (unlink("T16.c") < 0) e(21); + + /* Test the times for mkdir. */ + if (mkdir("T16.d", 0666) < 0) e(22); + get_times("T16.d", &a, &c, &m); + get_times(".", &pa, &pc, &pm); + if (a != c) e(23); + if (a != m) e(24); + if (a != pc) e(25); + if (a != pm) e(26); + sleep(1); + if (rmdir("T16.d") < 0) e(27); + get_times(".", &xa, &xc, &xm); + if (c == xc) e(28); + if (m == xm) e(29); + if (xc != xm) e(30); + + /* Test open(file, O_TRUNC). */ + if ( (fd = open("T16.e", O_WRONLY|O_CREAT, 0666)) < 0) e(31); + if (write(fd, buf, 1024) != 1024) e(32); + if (close(fd) != 0) e(33); + get_times("T16.e", &a, &c, &m); + get_times(".", &pa, &pc, &pm); + sleep(1); + if ( (fd = open("T16.e", O_WRONLY|O_TRUNC)) < 0) e(34); + get_times("T16.e", &xa, &xc, &xm); + get_times(".", &ya, &yc, &ym); + if (c != m) e(35); + if (pc != pm) e(36); + if (c == xc) e(37); + if (m == xm) e(38); + if (yc != pc) e(39); + if (ym != pm) e(40); + if (close(fd) != 0) e(41); + + /* Test the times for link/unlink. */ + get_times("T16.e", &a, &c, &m); + get_times(".", &ya, &yc, &ym); + sleep(1); + if (link("T16.e", "T16.f") != 0) e(42); /* second link */ + get_times("T16.e", &xa, &xc, &xm); + get_times(".", &pa, &pc, &pm); + if (a != xa) e(43); + if (m != xm) e(44); +#ifndef V1_FILESYSTEM + if (c == xc) e(45); +#endif + if (ya != pa) e(46); + if (yc == pc) e(47); + if (ym == pm) e(48); + if (yc != ym) e(49); + if (pc != pm) e(50); + sleep(1); + if (unlink("T16.f") != 0) e(46); + get_times("T16.e", &a, &c, &m); + get_times(".", &ya, &yc, &ym); + if (a != xa) e(51); + if (m != xm) e(52); +#ifndef V1_FILESYSTEM + if (c == xc) e(53); +#endif + if (pa != ya) e(54); + if (pc == yc) e(55); + if (pm == ym) e(56); + if (yc != ym) e(57); + if (unlink("T16.e") != 0) e(58); + + /* Test rename, read, write, chmod, utime. */ + get_times(".", &pa, &pc, &pm); + if ( (fd = open("T16.g", O_RDWR|O_CREAT)) < 0) e(59); + if ( (fd1 = open("T16.h", O_WRONLY|O_CREAT, 0666)) < 0) e(60); + if ( (fd2 = open("T16.i", O_WRONLY|O_CREAT, 0666)) < 0) e(61); + if ( (fd3 = open("T16.j", O_WRONLY|O_CREAT, 0666)) < 0) e(62); + if ( (fd4 = open("T16.k", O_RDWR|O_CREAT, 0666)) < 0) e(63); + if (write(fd, buf, 1024) != 1024) e(64); + get_times("T16.g", &a, &c, &m); + get_times("T16.h", &pa, &pc, &pm); + get_times("T16.i", &xa, &xc, &xm); + get_times("T16.j", &ya, &yc, &ym); + get_times("T16.k", &za, &zc, &zm); + get_times(".", &wa, &wc, &wm); + sleep(1); + lseek(fd, 0L, SEEK_SET); + if (read(fd, buf, 35) != 35) e(65); + get_times("T16.g", &ta, &tc, &tm); + if (a == ta || c != tc || m != tm) e(66); + if (write(fd1, buf, 35) != 35) e(67); + get_times("T16.h", &ta, &tc, &tm); + if (pa != ta || pc == tc || pm == tm) e(69); + if (rename("T16.i", "T16.i1") != 0) e(70); + get_times("T16.i1", &ta, &tc, &tm); + if (xa != ta || xc != tc || xm != tm) e(71); + get_times(".", &a, &c, &m); + if (a != wa || c == wc || m == wm || wc != wm) e(72); + if (chmod("T16.j", 0777) != 0) e(73); + get_times("T16.j", &ta, &tc, &tm); + if (ya != ta || yc == tc || ym != tm) e(74); + if (utime("T16.k", (void *) 0) != 0) e(75); + get_times("T16.k", &ta, &tc, &tm); + if (za == ta || zc == tc) e(76); + if (close(fd) != 0) e(77); + if (close(fd1) != 0) e(78); + if (close(fd2) != 0) e(79); + if (close(fd3) != 0) e(80); + if (close(fd4) != 0) e(81); + if (unlink("T16.g") != 0) e(82); + if (unlink("T16.h") != 0) e(83); + if (unlink("T16.i1") != 0) e(84); + if (unlink("T16.j") != 0) e(85); + if (unlink("T16.k") != 0) e(86); +} + + +void get_times(name, a, c, m) +char *name; +time_t *a, *c, *m; +{ + struct stat s; + + if (stat(name, &s) != 0) e(500); + *a = s.st_atime; + *c = s.st_ctime; + *m = s.st_mtime; +} + + + +void e(n) +int n; +{ + int err_num = errno; /* save errno in case printf clobbers it */ + + printf("Subtest %d, error %d errno=%d ", subtest, n, errno); + errno = err_num; /* restore errno, just in case */ + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} + diff --git a/test/test17.c b/test/test17.c new file mode 100644 index 000000000..2211a76ad --- /dev/null +++ b/test/test17.c @@ -0,0 +1,1168 @@ +/* Comment on usage and program: ark!/mnt/rene/prac/os/unix/comment.changes */ + +/* "const.h", created by Rene Montsma and Menno Wilcke */ + +#include <sys/types.h> /* type defs */ +#include <sys/stat.h> /* struct stat */ +#include <sys/wait.h> +#include <errno.h> /* the error-numbers */ +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <utime.h> +#include <stdio.h> + +#define NOCRASH 1 /* test11(), 2nd pipe */ +#define PDPNOHANG 1 /* test03(), write_standards() */ +#define MAXERR 2 + +#define USER_ID 12 +#define GROUP_ID 1 +#define FF 3 /* first free filedes. */ +#define USER 1 /* uid */ +#define GROUP 0 /* gid */ + +#define ARSIZE 256 /* array size */ +#define PIPESIZE 3584 /* max number of bytes to be written on pipe */ +#define MAXOPEN 17 /* maximum number of extra open files */ +#define MAXLINK 0177 /* maximum number of links per file */ +#define LINKCOUNT 5 +#define MASK 0777 /* selects lower nine bits */ +#define END_FILE 0 /* returned by read-call at eof */ + +#define OK 0 +#define FAIL -1 + +#define R 0 /* read (open-call) */ +#define W 1 /* write (open-call) */ +#define RW 2 /* read & write (open-call) */ + +#define RWX 7 /* read & write & execute (mode) */ + +#define NIL "" +#define UMASK "umask" +#define CREAT "creat" +#define WRITE "write" +#define READ "read" +#define OPEN "open" +#define CLOSE "close" +#define LSEEK "lseek" +#define ACCESS "access" +#define CHDIR "chdir" +#define CHMOD "chmod" +#define LINK "link" +#define UNLINK "unlink" +#define PIPE "pipe" +#define STAT "stat" +#define FSTAT "fstat" +#define DUP "dup" +#define UTIME "utime" + +int errct; + +char *file[]; +char *fnames[]; +char *dir[]; + +/* "decl.c", created by Rene Montsma and Menno Wilcke */ + +/* Used in open_alot, close_alot */ +char *file[20] = {"f0", "f1", "f2", "f3", "f4", "f5", "f6", + "f7", "f8", "f9", "f10", "f11", "f12", "f13", + "f14", "f15", "f16", "f17", "f18", "f19"}, *fnames[8] = {"---", "--x", "-w-", "-wx", "r--", + "r-x", "rw-", "rwx"}, *dir[8] = {"d---", "d--x", "d-w-", "d-wx", "dr--", "dr-x", + "drw-", "drwx"}; + /* Needed for easy creating and deleting of directories */ + +/* "test.c", created by Rene Montsma and Menno Wilcke */ + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void test, (int mask)); +_PROTOTYPE(void test01, (void)); +_PROTOTYPE(void test02, (void)); +_PROTOTYPE(void test08, (void)); +_PROTOTYPE(void test09, (void)); +_PROTOTYPE(void test10, (void)); +_PROTOTYPE(int link_alot, (char *bigboss)); +_PROTOTYPE(int unlink_alot, (int number)); +_PROTOTYPE(void get_new, (char name [])); +_PROTOTYPE(void test11, (void)); +_PROTOTYPE(void comp_stats, (struct stat *stbf1, struct stat *stbf2)); +_PROTOTYPE(void comp_inodes, (int m, int m1)); +_PROTOTYPE(void e, (char *string)); +_PROTOTYPE(void nlcr, (void)); +_PROTOTYPE(void str, (char *s)); +_PROTOTYPE(void err, (int number, char *scall, char *name)); +_PROTOTYPE(void make_and_fill_dirs, (void)); +_PROTOTYPE(void put_file_in_dir, (char *dirname, int mode)); +_PROTOTYPE(void init_array, (char *a)); +_PROTOTYPE(void clear_array, (char *b)); +_PROTOTYPE(int comp_array, (char *a, char *b, int range)); +_PROTOTYPE(void try_close, (int filedes, char *name)); +_PROTOTYPE(void try_unlink, (char *fname)); +_PROTOTYPE(void Remove, (int fdes, char *fname)); +_PROTOTYPE(int get_mode, (char *name)); +_PROTOTYPE(void check, (char *scall, int number)); +_PROTOTYPE(void put, (int nr)); +_PROTOTYPE(int open_alot, (void)); +_PROTOTYPE(int close_alot, (int number)); +_PROTOTYPE(void clean_up_the_mess, (void)); +_PROTOTYPE(void chmod_8_dirs, (int sw)); +_PROTOTYPE(void quit, (void)); + +/***************************************************************************** + * TEST * + ****************************************************************************/ +int main(argc, argv) +int argc; +char *argv[]; +{ + int n, mask; + + sync(); + if (geteuid() == 0 || getuid() == 0) { + printf("Test 17 cannot run as root; test aborted\n"); + exit(1); + } + + system("rm -rf DIR_18; mkdir DIR_18"); + chdir("DIR_18"); + + mask = (argc == 2 ? atoi(argv[1]) : 0xFFFF); + + if (fork()) { + printf("Test 17 "); + fflush(stdout); + + wait(&n); + clean_up_the_mess(); + quit(); + } else { + test(mask); + exit(0); + } + return(-1); /* impossible */ +} + +void test(mask) +int mask; +{ + umask(0); /* not honest, but i always forget */ + + if (mask & 00001) test01(); + if (mask & 00002) make_and_fill_dirs(); + if (mask & 00004) test02(); + if (mask & 00010) test08(); + if (mask & 00020) test09(); + if (mask & 00040) test10(); + if (mask & 00100) test11(); + umask(022); +} /* test */ + + + +/* "t1.c" created by Rene Montsma and Menno Wilcke */ + +/***************************************************************************** + * test UMASK * + ****************************************************************************/ +void test01() +{ + int oldvalue, newvalue, tempvalue; + int nr; + + if ((oldvalue = umask(0777)) != 0) err(0, UMASK, NIL); + + /* Special test: only the lower 9 bits (protection bits) may part- * + * icipate. ~0777 means: 111 000 000 000. Giving this to umask must* + * not change any value. */ + + if ((newvalue = umask(~0777)) != 0777) err(1, UMASK, "illegal"); + if (oldvalue == newvalue) err(11, UMASK, "not change mask"); + + if ((tempvalue = umask(0)) != 0) err(2, UMASK, "values"); + + /* Now test all possible modes of umask on a file */ + for (newvalue = MASK; newvalue >= 0; newvalue -= 0111) { + tempvalue = umask(newvalue); + if (tempvalue != oldvalue) { + err(1, UMASK, "illegal"); + break; /* no use trying more */ + } else if ((nr = creat("file01", 0777)) < 0) + err(5, CREAT, "'file01'"); + else { + try_close(nr, "'file01'"); + if (get_mode("file01") != (MASK & ~newvalue)) + err(7, UMASK, "mode computed"); + try_unlink("file01"); + } + oldvalue = newvalue; + } + + /* The loop has terminated with umask(0) */ + if ((tempvalue = umask(0)) != 0) + err(7, UMASK, "umask may influence rest of tests!"); +} /* test01 */ + +/***************************************************************************** + * test CREAT * + ****************************************************************************/ +void test02() +{ + int n, n1, mode; + char a[ARSIZE], b[ARSIZE]; + struct stat stbf1; + + mode = 0; + /* Create twenty files, check filedes */ + for (n = 0; n < MAXOPEN; n++) { + if (creat(file[n], mode) != FF + n) + err(13, CREAT, file[n]); + else { + if (get_mode(file[n]) != mode) + err(7, CREAT, "mode set while creating many files"); + + /* Change mode of file to standard mode, we want to * + * use a lot (20) of files to be opened later, see * + * open_alot(), close_alot(). */ + if (chmod(file[n], 0700) != OK) err(5, CHMOD, file[n]); + + } + mode = (mode + 0100) % 01000; + } + + /* Already twenty files opened; opening another has to fail */ + if (creat("file02", 0777) != FAIL) + err(9, CREAT, "created"); + else + check(CREAT, EMFILE); + + /* Close all files: seems blunt, but it isn't because we've * + * checked all fd's already */ + if ((n = close_alot(MAXOPEN)) < MAXOPEN) err(5, CLOSE, "MAXOPEN files"); + + /* Creat 1 file twice; check */ + if ((n = creat("file02", 0777)) < 0) + err(5, CREAT, "'file02'"); + else { + init_array(a); + if (write(n, a, ARSIZE) != ARSIZE) err(1, WRITE, "bad"); + + if ((n1 = creat("file02", 0755)) < 0) /* receate 'file02' */ + err(5, CREAT, "'file02' (2nd time)"); + else { + /* Fd should be at the top after recreation */ + if (lseek(n1, 0L, SEEK_END) != 0) + err(11, CREAT, "not truncate file by recreation"); + else { + /* Try to write on recreated file */ + clear_array(b); + + if (lseek(n1, 0L, SEEK_SET) != 0) + err(5, LSEEK, "to top of 2nd fd 'file02'"); + if (write(n1, a, ARSIZE) != ARSIZE) + err(1, WRITE, "(2) bad"); + + /* In order to read we've to close and open again */ + try_close(n1, "'file02' (2nd creation)"); + if ((n1 = open("file02", RW)) < 0) + err(5, OPEN, "'file02' (2nd recreation)"); + + /* Continue */ + if (lseek(n1, 0L, SEEK_SET) != 0) + err(5, LSEEK, "to top 'file02'(2nd fd) (2)"); + if (read(n1, b, ARSIZE) != ARSIZE) + err(1, READ, "wrong"); + + if (comp_array(a, b, ARSIZE) != OK) err(11, CREAT, + "not really truncate file by recreation"); + } + if (get_mode("file02") != 0777) + err(11, CREAT, "not maintain mode by recreation"); + try_close(n1, "recreated 'file02'"); + + } + Remove(n, "file02"); + } + + /* Give 'creat' wrong input: dir not searchable */ + if (creat("drw-/file02", 0777) != FAIL) + err(4, CREAT, "'drw-'"); + else + check(CREAT, EACCES); + + /* Dir not writable */ + if (creat("dr-x/file02", 0777) != FAIL) + err(12, CREAT, "'dr-x/file02'"); + else + check(CREAT, EACCES); + + /* File not writable */ + if (creat("drwx/r-x", 0777) != FAIL) + err(11, CREAT, "recreate non-writable file"); + else + check(CREAT, EACCES); + + /* Try to creat a dir */ + if ((n = creat("dir", 040777)) != FAIL) { + if (fstat(n, &stbf1) != OK) + err(5, FSTAT, "'dir'"); + else if (stbf1.st_mode != (mode_t) 0100777) + /* cast because mode is negative :-( */ + err(11, CREAT, "'creat' a new directory"); + Remove(n, "dir"); + } + + /* We don't consider it to be a bug when creat * does not accept + * tricky modes */ + + /* File is an existing dir */ + if (creat("drwx", 0777) != FAIL) + err(11, CREAT, "create an existing dir!"); + else + check(CREAT, EISDIR); +} /* test02 */ + + + +void test08() +{ + /* Test chdir to searchable dir */ + if (chdir("drwx") != OK) + err(5, CHDIR, "to accessible dir"); + else if (chdir("..") != OK) + err(11, CHDIR, "not return to '..'"); + + /* Check the chdir(".") and chdir("..") mechanism */ + if (chdir("drwx") != OK) + err(5, CHDIR, "to 'drwx'"); + else { + if (chdir(".") != OK) err(5, CHDIR, "to working dir (.)"); + + /* If we still are in 'drwx' , we should be able to access * + * file 'rwx'. */ + if (access("rwx", 0) != OK) err(5, CHDIR, "rightly to '.'"); + + /* Try to return to previous dir ('/' !!) */ + if (chdir("././../././d--x/../d--x/././..") != OK) + err(5, CHDIR, "to motherdir (..)"); + + /* Check whether we are back in '/' */ + if (chdir("d--x") != OK) err(5, CHDIR, "rightly to a '..'"); + } + + /* Return to '..' */ + if (chdir("..") != OK) err(5, CHDIR, "to '..'"); + + if (chdir("././././drwx") != OK) + err(11, CHDIR, "not follow a path"); + else if (chdir("././././..") != OK) + err(11, CHDIR, "not return to path"); + + /* Try giving chdir wrong parameters */ + if (chdir("drwx/rwx") != FAIL) + err(11, CHDIR, "chdir to a file"); + else + check(CHDIR, ENOTDIR); + + if (chdir("drw-") != FAIL) + err(4, CHDIR, "'/drw-'"); + else + check(CHDIR, EACCES); + + /* To be sure: return to root */ + /* If (chdir("/") != OK) err(5, CHDIR, "to '/' (2nd time)"); */ +} /* test08 */ + +/* New page */ +/***************************************************************************** + * test CHMOD * + ****************************************************************************/ +void test09() +{ + int n; + + /* Prepare file09 */ + if ((n = creat("drwx/file09", 0644)) != FF) err(5, CREAT, "'drwx/file09'"); + + try_close(n, "'file09'"); + + /* Try to chmod a file, check and restore old values, check */ + if (chmod("drwx/file09", 0700) != OK) + err(5, CHMOD, "'drwx/file09'"); /* set rwx */ + else { + /* Check protection */ + if (get_mode("drwx/file09") != 0700) err(7, CHMOD, "mode"); + + /* Test if chmod accepts just filenames too */ + if (chdir("drwx") != OK) + err(5, CHDIR, "to '/drwx'"); + else if (chmod("file09", 0177) != OK) /* restore oldies */ + err(5, CHMOD, "'h1'"); + else + /* Check if value has been restored */ + if (get_mode("../drwx/file09") != 0177) + err(7, CHMOD, "restored mode"); + } + + /* Try setuid and setgid */ + if ((chmod("file09", 04777) != OK) || (get_mode("file09") != 04777)) + err(11, CHMOD, "not set uid-bit"); + if ((chmod("file09", 02777) != OK) || (get_mode("file09") != 02777)) + err(11, CHMOD, "not set gid-bit"); + + /* Remove testfile */ + try_unlink("file09"); + + if (chdir("..") != OK) err(5, CHDIR, "to '..'"); + + /* Try to chmod directory */ + if (chmod("d---", 0777) != OK) + err(5, CHMOD, "dir 'd---'"); + else { + if (get_mode("d---") != 0777) err(7, CHMOD, "protection value"); + if (chmod("d---", 0000) != OK) err(5, CHMOD, "dir 'a' 2nd time"); + + /* Check if old value has been restored */ + if (get_mode("d---") != 0000) + err(7, CHMOD, "restored protection value"); + } + + /* Try to make chmod failures */ + + /* We still are in dir root */ + /* Wrong filename */ + if (chmod("non-file", 0777) != FAIL) + err(3, CHMOD, NIL); + else + check(CHMOD, ENOENT); + +} /* test 09 */ + +/* New page */ + + +/* "t4.c", created by Rene Montsma and Menno Wilcke */ + +/***************************************************************************** + * test LINK/UNLINK * + ****************************************************************************/ +void test10() +{ + int n, n1; + char a[ARSIZE], b[ARSIZE], *f, *lf; + + f = "file10"; + lf = "linkfile10"; + + if ((n = creat(f, 0702)) != FF) /* no other open files */ + err(13, CREAT, f); + else { + /* Now link correctly */ + if (link(f, lf) != OK) + err(5, LINK, lf); + else if ((n1 = open(lf, RW)) < 0) + err(5, OPEN, "'linkfile10'"); + else { + init_array(a); + clear_array(b); + + /* Write on 'file10' means being able to * read + * through linked filedescriptor */ + if (write(n, a, ARSIZE) != ARSIZE) err(1, WRITE, "bad"); + if (read(n1, b, ARSIZE) != ARSIZE) err(1, READ, "bad"); + if (comp_array(a, b, ARSIZE) != OK) err(8, "r/w", NIL); + + /* Clean up: unlink and close (twice): */ + Remove(n, f); + try_close(n1, "'linkfile10'"); + + /* Check if "linkfile" exists and the info * on it + * is correct ('file' has been deleted) */ + if ((n1 = open(lf, R)) < 0) + err(5, OPEN, "'linkfile10'"); + else { + /* See if 'linkfile' still contains 0..511 ? */ + + clear_array(b); + if (read(n1, b, ARSIZE) != ARSIZE) + err(1, READ, "bad"); + if (comp_array(a, b, ARSIZE) != OK) + err(8, "r/w", NIL); + + try_close(n1, "'linkfile10' 2nd time"); + try_unlink(lf); + } + } + } + + /* Try if unlink fails with incorrect parameters */ + /* File does not exist: */ + if (unlink("non-file") != FAIL) + err(2, UNLINK, "name"); + else + check(UNLINK, ENOENT); + + /* Dir can't be written */ + if (unlink("dr-x/rwx") != FAIL) + err(11, UNLINK, "could unlink in non-writable dir."); + else + check(UNLINK, EACCES); + + /* Try to unlink a dir being user */ + if (unlink("drwx") != FAIL) + err(11, UNLINK, "unlink dir's as user"); + else + check(UNLINK, EPERM); + + /* Try giving link wrong input */ + + /* First try if link fails with incorrect parameters * name1 does not + * exist. */ + if (link("non-file", "linkfile") != FAIL) + err(2, LINK, "1st name"); + else + check(LINK, ENOENT); + + /* Name2 exists already */ + if (link("drwx/rwx", "drwx/rw-") != FAIL) + err(2, LINK, "2nd name"); + else + check(LINK, EEXIST); + + /* Directory of name2 not writable: */ + if (link("drwx/rwx", "dr-x/linkfile") != FAIL) + err(11, LINK, "link non-writable file"); + else + check(LINK, EACCES); + + /* Try to link a dir, being a user */ + if (link("drwx", "linkfile") != FAIL) + err(11, LINK, "link a dir without superuser!"); + else + check(LINK, EPERM); + + /* File has too many links */ + if ((n = link_alot("drwx/rwx")) != LINKCOUNT - 1) /* file already has one + * link */ + err(5, LINK, "many files"); + if (unlink_alot(n) != n) err(5, UNLINK, "all linked files"); + +} /* test10 */ + + +int link_alot(bigboss) +char *bigboss; +{ + int i; + static char employee[6] = "aaaaa"; + + /* Every file has already got 1 link, so link 0176 times */ + for (i = 1; i < LINKCOUNT; i++) { + if (link(bigboss, employee) != OK) + break; + else + get_new(employee); + } + + return(i - 1); /* number of linked files */ +} /* link_alot */ + +int unlink_alot(number) +int number; /* number of files to be unlinked */ +{ + int j; + static char employee[6] = "aaaaa"; + + for (j = 0; j < number; j++) { + if (unlink(employee) != OK) + break; + else + get_new(employee); + } + + return(j); /* return number of unlinked files */ +} /* unlink_alot */ + +void get_new(name) +char name[]; + /* Every call changes string 'name' to a string alphabetically * + * higher. Start with "aaaaa", next value: "aaaab" . * + * N.B. after "aaaaz" comes "aaabz" and not "aaaba" (not needed). * + * The last possibility will be "zzzzz". * + * Total # possibilities: 26+25*4 = 126 = MAXLINK -1 (exactly needed) */ +{ + int i; + + for (i = 4; i >= 0; i--) + if (name[i] != 'z') { + name[i]++; + break; + } +} /* get_new */ + +/* New page */ + +/***************************************************************************** + * test PIPE * + ****************************************************************************/ +void test11() +{ + int n, fd[2]; + char a[ARSIZE], b[ARSIZE]; + + if (pipe(fd) != OK) + err(13, PIPE, NIL); + else { + /* Try reading and writing on a pipe */ + init_array(a); + clear_array(b); + + if (write(fd[1], a, ARSIZE) != ARSIZE) + err(5, WRITE, "on pipe"); + else if (read(fd[0], b, (ARSIZE / 2)) != (ARSIZE / 2)) + err(5, READ, "on pipe (2nd time)"); + else if (comp_array(a, b, (ARSIZE / 2)) != OK) + err(7, PIPE, "values read/written"); + else if (read(fd[0], b, (ARSIZE / 2)) != (ARSIZE / 2)) + err(5, READ, "on pipe 2"); + else if (comp_array(&a[ARSIZE / 2], b, (ARSIZE / 2)) != OK) + err(7, PIPE, "pipe created"); + + /* Try to let the pipe make a mistake */ + if (write(fd[0], a, ARSIZE) != FAIL) + err(11, WRITE, "write on fd[0]"); + if (read(fd[1], b, ARSIZE) != FAIL) err(11, READ, "read on fd[1]"); + + try_close(fd[1], "'fd[1]'"); + + /* Now we shouldn't be able to read, because fd[1] has been closed */ + if (read(fd[0], b, ARSIZE) != END_FILE) err(2, PIPE, "'fd[1]'"); + + try_close(fd[0], "'fd[0]'"); + } + if (pipe(fd) < 0) + err(5, PIPE, "2nd time"); + else { + /* Test lseek on a pipe: should fail */ + if (write(fd[1], a, ARSIZE) != ARSIZE) + err(5, WRITE, "on pipe (2nd time)"); + if (lseek(fd[1], 10L, SEEK_SET) != FAIL) + err(11, LSEEK, "lseek on a pipe"); + else + check(PIPE, ESPIPE); + + /* Eat half of the pipe: no writing should be possible */ + try_close(fd[0], "'fd[0]' (2nd time)"); + + /* This makes UNIX crash: omit it if pdp or VAX */ +#ifndef NOCRASH + if (write(fd[1], a, ARSIZE) != FAIL) + err(11, WRITE, "write on wrong pipe"); + else + check(PIPE, EPIPE); +#endif + try_close(fd[1], "'fd[1]' (2nd time)"); + } + + /* BUG : * + * Here we planned to test if we could write 4K bytes on a pipe. * + * However, this was not possible to implement, because the whole * + * Monix system crashed when we tried to write more then 3584 bytes * + * (3.5K) on a pipe. That's why we try to write only 3.5K in the * + * folowing test. */ + if (pipe(fd) < 0) + err(5, PIPE, "3rd time"); + else { + for (n = 0; n < (PIPESIZE / ARSIZE); n++) + if (write(fd[1], a, ARSIZE) != ARSIZE) + err(5, WRITE, "on pipe (3rd time) 4K"); + try_close(fd[1], "'fd[1]' (3rd time)"); + + for (n = 0; n < (PIPESIZE / ARSIZE); n++) + if (read(fd[0], b, ARSIZE) != ARSIZE) + err(5, READ, "from pipe (3rd time) 4K"); + try_close(fd[0], "'fd[0]' (3rd time)"); + } + + /* Test opening a lot of files */ + if ((n = open_alot()) != MAXOPEN) err(5, OPEN, "MAXOPEN files"); + if (pipe(fd) != FAIL) + err(9, PIPE, "open"); + else + check(PIPE, EMFILE); + if (close_alot(n) != n) err(5, CLOSE, "all opened files"); +} /* test11 */ + +/* New page */ + + +void comp_stats(stbf1, stbf2) +struct stat *stbf1, *stbf2; +{ + if (stbf1->st_dev != stbf2->st_dev) err(7, "st/fst", "'dev'"); + if (stbf1->st_ino != stbf2->st_ino) err(7, "st/fst", "'ino'"); + if (stbf1->st_mode != stbf2->st_mode) err(7, "st/fst", "'mode'"); + if (stbf1->st_nlink != stbf2->st_nlink) err(7, "st/fst", "'nlink'"); + if (stbf1->st_uid != stbf2->st_uid) err(7, "st/fst", "'uid'"); + if (stbf1->st_gid != stbf2->st_gid) err(7, "st/fst", "'gid'"); + if (stbf1->st_rdev != stbf2->st_rdev) err(7, "st/fst", "'rdev'"); + if (stbf1->st_size != stbf2->st_size) err(7, "st/fst", "'size'"); + if (stbf1->st_atime != stbf2->st_atime) err(7, "st/fst", "'atime'"); + if (stbf1->st_mtime != stbf2->st_mtime) err(7, "st/fst", "'mtime'"); +} /* comp_stats */ + +/* New page */ + + +/* "t5.c", created by Rene Montsma and Menno Wilcke */ + + + +void comp_inodes(m, m1) +int m, m1; /* twee filedes's */ +{ + struct stat stbf1, stbf2; + + if (fstat(m, &stbf1) == OK) + if (fstat(m1, &stbf2) == OK) { + if (stbf1.st_ino != stbf2.st_ino) + err(7, DUP, "inode number"); + } else + err(100, "comp_inodes", "cannot 'fstat' (m1)"); + else + err(100, "comp_inodes", "cannot 'fstat' (m)"); +} /* comp_inodes */ + +/* "support.c", created by Rene Montsma and Menno Wilcke */ + +/* Err, make_and_fill_dirs, init_array, clear_array, comp_array, + try_close, try_unlink, Remove, get_mode, check, open_alot, + close_alot, clean_up_the_mess. +*/ + +/*********************************************************************** + * EXTENDED FIONS * + **********************************************************************/ +/* First extended functions (i.e. not oldfashioned monixcalls. + e(), nlcr(), octal.*/ + +void e(string) +char *string; +{ + printf("Error: %s ", string); +} + +void nlcr() +{ + printf("\n"); +} + +void str(s) +char *s; +{ + printf(s); +} + +/***************************************************************************** +* * +* ERR(or) messages * +* * +*****************************************************************************/ +void err(number, scall, name) + /* Give nice error messages */ + +char *scall, *name; +int number; + +{ + errct++; + if (errct > MAXERR) { + printf("Too many errors; test aborted\n"); + quit(); + } + e(""); + str("\t"); + switch (number) { + case 0: + str(scall); + str(": illegal initial value."); + break; + case 1: + str(scall); + str(": "); + str(name); + str(" value returned."); + break; + case 2: + str(scall); + str(": accepting illegal "); + str(name); + str("."); + break; + case 3: + str(scall); + str(": accepting non-existing file."); + break; + case 4: + str(scall); + str(": could search non-searchable dir ("); + str(name); + str(")."); + break; + case 5: + str(scall); + str(": cannot "); + str(scall); + str(" "); + str(name); + str("."); + break; + case 7: + str(scall); + str(": incorrect "); + str(name); + str("."); + break; + case 8: + str(scall); + str(": wrong values."); + break; + case 9: + str(scall); + str(": accepting too many "); + str(name); + str(" files."); + break; + case 10: + str(scall); + str(": even a superuser can't do anything!"); + break; + case 11: + str(scall); + str(": could "); + str(name); + str("."); + break; + case 12: + str(scall); + str(": could write in non-writable dir ("); + str(name); + str(")."); + break; + case 13: + str(scall); + str(": wrong filedes returned ("); + str(name); + str(")."); + break; + case 100: + str(scall); /* very common */ + str(": "); + str(name); + str("."); + break; + default: str("errornumber does not exist!\n"); + } + nlcr(); +} /* err */ + +/***************************************************************************** +* * +* MAKE_AND_FILL_DIRS * +* * +*****************************************************************************/ + +void make_and_fill_dirs() + /* Create 8 dir.'s: "d---", "d--x", "d-w-", "d-wx", "dr--", "dr-x", * + * "drw-", "drwx". * Then create 8 files + * in "drwx", and some needed files in other dirs. */ +{ + int mode, i; + + for (i = 0; i < 8; i++) { + mkdir(dir[i], 0700); + chown(dir[i], USER_ID, GROUP_ID); + } + setuid(USER_ID); + setgid(GROUP_ID); + + for (mode = 0; mode < 8; mode++) put_file_in_dir("drwx", mode); + + put_file_in_dir("d-wx", RWX); + put_file_in_dir("dr-x", RWX); + put_file_in_dir("drw-", RWX); + + chmod_8_dirs(8); /* 8 means; 8 different modes */ + +} /* make_and_fill_dirs */ + +void put_file_in_dir(dirname, mode) +char *dirname; +int mode; + /* Fill directory 'dirname' with file with mode 'mode'. */ +{ + int nr; + + if (chdir(dirname) != OK) + err(5, CHDIR, "to dirname (put_f_in_dir)"); + else { + /* Creat the file */ + if ((nr = creat(fnames[mode], mode * 0100)) < 0) + err(13, CREAT, fnames[mode]); + else + try_close(nr, fnames[mode]); + + if (chdir("..") != OK) + err(5, CHDIR, "to previous dir (put_f_in_dir)"); + } +} /* put_file_in_dir */ + +/***************************************************************************** +* * +* MISCELLANEOUS * +* * +*(all about arrays, 'try_close', 'try_unlink', 'Remove', 'get_mode')* +* * +*****************************************************************************/ + +void init_array(a) +char *a; +{ + int i; + + i = 0; + while (i++ < ARSIZE) *a++ = 'a' + (i % 26); +} /* init_array */ + +void clear_array(b) +char *b; +{ + int i; + + i = 0; + while (i++ < ARSIZE) *b++ = '0'; + +} /* clear_array */ + +int comp_array(a, b, range) +char *a, *b; +int range; +{ + if ((range < 0) || (range > ARSIZE)) { + err(100, "comp_array", "illegal range"); + return(FAIL); + } else { + while (range-- && (*a++ == *b++)); + if (*--a == *--b) + return(OK); + else + return(FAIL); + } +} /* comp_array */ + +void try_close(filedes, name) +int filedes; +char *name; +{ + if (close(filedes) != OK) err(5, CLOSE, name); +} /* try_close */ + +void try_unlink(fname) +char *fname; +{ + if (unlink(fname) != 0) err(5, UNLINK, fname); +} /* try_unlink */ + +void Remove(fdes, fname) +int fdes; +char *fname; +{ + try_close(fdes, fname); + try_unlink(fname); +} /* Remove */ + +int get_mode(name) +char *name; +{ + struct stat stbf1; + + if (stat(name, &stbf1) != OK) { + err(5, STAT, name); + return(stbf1.st_mode); /* return a mode which will cause * + * error in the calling function * + * (file/dir bit) */ + } else + return(stbf1.st_mode & 07777); /* take last 4 bits */ +} /* get_mode */ + +/***************************************************************************** +* * +* CHECK * +* * +*****************************************************************************/ + +void check(scall, number) +int number; +char *scall; +{ + if (errno != number) { + e(NIL); + str("\t"); + str(scall); + str(": bad errno-value: "); + put(errno); + str(" should have been: "); + put(number); + nlcr(); + } +} /* check */ + +void put(nr) +int nr; +{ + switch (nr) { + case 0: str("unused"); break; + case 1: str("EPERM"); break; + case 2: str("ENOENT"); break; + case 3: str("ESRCH"); break; + case 4: str("EINTR"); break; + case 5: str("EIO"); break; + case 6: str("ENXIO"); break; + case 7: str("E2BIG"); break; + case 8: str("ENOEXEC"); break; + case 9: str("EBADF"); break; + case 10: str("ECHILD"); break; + case 11: str("EAGAIN"); break; + case 12: str("ENOMEM"); break; + case 13: str("EACCES"); break; + case 14: str("EFAULT"); break; + case 15: str("ENOTBLK"); break; + case 16: str("EBUSY"); break; + case 17: str("EEXIST"); break; + case 18: str("EXDEV"); break; + case 19: str("ENODEV"); break; + case 20: str("ENOTDIR"); break; + case 21: str("EISDIR"); break; + case 22: str("EINVAL"); break; + case 23: str("ENFILE"); break; + case 24: str("EMFILE"); break; + case 25: str("ENOTTY"); break; + case 26: str("ETXTBSY"); break; + case 27: str("EFBIG"); break; + case 28: str("ENOSPC"); break; + case 29: str("ESPIPE"); break; + case 30: str("EROFS"); break; + case 31: str("EMLINK"); break; + case 32: str("EPIPE"); break; + case 33: str("EDOM"); break; + case 34: str("ERANGE"); break; + } +} + +/***************************************************************************** +* * +* ALOT-functions * +* * +*****************************************************************************/ + + +int open_alot() +{ + int i; + + for (i = 0; i < MAXOPEN; i++) + if (open(file[i], R) == FAIL) break; + if (i == 0) err(5, "open_alot", "at all"); + return(i); +} /* open_alot */ + +int close_alot(number) +int number; +{ + int i, count = 0; + + if (number > MAXOPEN) + err(5, "close_alot", "accept this argument"); + else + for (i = FF; i < number + FF; i++) + if (close(i) != OK) count++; + + return(number - count); /* return number of closed files */ +} /* close_alot */ + +/***************************************************************************** +* * +* CLEAN UP THE MESS * +* * +*****************************************************************************/ + +void clean_up_the_mess() +{ + int i; + char dirname[6]; + + /* First remove 'a lot' files */ + for (i = 0; i < MAXOPEN; i++) try_unlink(file[i]); + + /* Unlink the files in dir 'drwx' */ + if (chdir("drwx") != OK) + err(5, CHDIR, "to 'drwx'"); + else { + for (i = 0; i < 8; i++) try_unlink(fnames[i]); + if (chdir("..") != OK) err(5, CHDIR, "to '..'"); + } + + /* Before unlinking files in some dirs, make them writable */ + chmod_8_dirs(RWX); + + /* Unlink files in other dirs */ + try_unlink("d-wx/rwx"); + try_unlink("dr-x/rwx"); + try_unlink("drw-/rwx"); + + /* Unlink dirs */ + for (i = 0; i < 8; i++) { + strcpy(dirname, "d"); + strcat(dirname, fnames[i]); + + /* 'dirname' contains the directoryname */ + rmdir(dirname); + } + + /* FINISH */ +} /* clean_up_the_mess */ + +void chmod_8_dirs(sw) +int sw; /* if switch == 8, give all different + * mode,else the same mode */ +{ + int mode; + int i; + + if (sw == 8) + mode = 0; + else + mode = sw; + + for (i = 0; i < 8; i++) { + chmod(dir[i], 040000 + mode * 0100); + if (sw == 8) mode++; + } +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test18.c b/test/test18.c new file mode 100644 index 000000000..c756221a0 --- /dev/null +++ b/test/test18.c @@ -0,0 +1,1350 @@ +/* test 18 */ + +/* Comment on usage and program: ark!/mnt/rene/prac/os/unix/comment.changes */ + +/* "const.h", created by Rene Montsma and Menno Wilcke */ + +#include <sys/types.h> /* needed in struct stat */ +#include <sys/stat.h> /* struct stat */ +#include <sys/wait.h> +#include <errno.h> /* the error-numbers */ +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <stdio.h> + +#define NOCRASH 1 /* test11(), 2nd pipe */ +#define PDPNOHANG 1 /* test03(), write_standards() */ +#define MAXERR 5 + +#define USER_ID 12 +#define GROUP_ID 1 +#define FF 3 /* first free filedes. */ +#define USER 1 /* uid */ +#define GROUP 0 /* gid */ + +#define ARSIZE 256 /* array size */ +#define PIPESIZE 3584 /* maxnumber of bytes to be written on pipe */ +#define MAXOPEN 17 /* maximum number of extra open files */ +#define MAXLINK 0177 /* maximum number of links per file */ +#define MASK 0777 /* selects lower nine bits */ +#define READ_EOF 0 /* returned by read-call at eof */ + +#define OK 0 +#define FAIL -1 + +#define R 0 /* read (open-call) */ +#define W 1 /* write (open-call) */ +#define RW 2 /* read & write (open-call) */ + +#define RWX 7 /* read & write & execute (mode) */ + +#define NIL "" +#define UMASK "umask" +#define CREAT "creat" +#define WRITE "write" +#define READ "read" +#define OPEN "open" +#define CLOSE "close" +#define LSEEK "lseek" +#define ACCESS "access" +#define CHDIR "chdir" +#define CHMOD "chmod" +#define LINK "link" +#define UNLINK "unlink" +#define PIPE "pipe" +#define STAT "stat" +#define FSTAT "fstat" +#define DUP "dup" +#define UTIME "utime" + +int errct; + +char *file[]; +char *fnames[]; +char *dir[]; + +/* "decl.c", created by Rene Montsma and Menno Wilcke */ + +/* Used in open_alot, close_alot */ +char *file[20] = {"f0", "f1", "f2", "f3", "f4", "f5", "f6", + "f7", "f8", "f9", "f10", "f11", "f12", "f13", + "f14", "f15", "f16", "f17", "f18", "f19"}, *fnames[8] = {"---", "--x", "-w-", "-wx", "r--", + "r-x", "rw-", "rwx"}, *dir[8] = {"d---", "d--x", "d-w-", "d-wx", "dr--", "dr-x", + "drw-", "drwx"}; + /* Needed for easy creating and deleting of directories */ + +/* "test.c", created by Rene Montsma and Menno Wilcke */ + +_PROTOTYPE(int main, (void)); +_PROTOTYPE(void test, (void)); +_PROTOTYPE(void test01, (void)); +_PROTOTYPE(void test02, (void)); +_PROTOTYPE(void test03, (void)); +_PROTOTYPE(void write_standards, (int filedes, char a [])); +_PROTOTYPE(void test04, (void)); +_PROTOTYPE(void read_standards, (int filedes, char a [])); +_PROTOTYPE(void read_more, (int filedes, char a [])); +_PROTOTYPE(void test05, (void)); +_PROTOTYPE(void try_open, (char *fname, int mode, int test)); +_PROTOTYPE(void test06, (void)); +_PROTOTYPE(void test07, (void)); +_PROTOTYPE(void access_standards, (void)); +_PROTOTYPE(void try_access, (char *fname, int mode, int test)); +_PROTOTYPE(void e, (char *string)); +_PROTOTYPE(void nlcr, (void)); +_PROTOTYPE(void str, (char *s)); +_PROTOTYPE(void err, (int number, char *scall, char *name)); +_PROTOTYPE(void make_and_fill_dirs, (void)); +_PROTOTYPE(void put_file_in_dir, (char *dirname, int mode)); +_PROTOTYPE(void init_array, (char *a)); +_PROTOTYPE(void clear_array, (char *b)); +_PROTOTYPE(int comp_array, (char *a, char *b, int range)); +_PROTOTYPE(void try_close, (int filedes, char *name)); +_PROTOTYPE(void try_unlink, (char *fname)); +_PROTOTYPE(void Remove, (int fdes, char *fname)); +_PROTOTYPE(int get_mode, (char *name)); +_PROTOTYPE(void check, (char *scall, int number)); +_PROTOTYPE(void put, (int nr)); +_PROTOTYPE(int open_alot, (void)); +_PROTOTYPE(int close_alot, (int number)); +_PROTOTYPE(void clean_up_the_mess, (void)); +_PROTOTYPE(void chmod_8_dirs, (int sw)); +_PROTOTYPE(void quit, (void)); + + +/***************************************************************************** + * TEST * + ****************************************************************************/ +int main() +{ + int n; + + if (geteuid() == 0 || getuid() == 0) { + printf("Test 18 cannot run as root; test aborted\n"); + exit(1); + } + + system("rm -rf DIR_18; mkdir DIR_18"); + chdir("DIR_18"); + + if (fork()) { + printf("Test 18 "); + fflush(stdout); /* have to flush for child's benefit */ + + wait(&n); + clean_up_the_mess(); + quit(); + } else { + test(); + exit(0); + } + + return(0); +} + + + +void test() +{ + umask(0); /* not honest, but i always forget */ + + test01(); + make_and_fill_dirs(); + test02(); + test03(); + test04(); + test05(); + test06(); + test07(); + umask(022); +} /* test */ + + + + + + +/* "t1.c" created by Rene Montsma and Menno Wilcke */ + +/***************************************************************************** + * test UMASK * + ****************************************************************************/ +void test01() +{ + int oldvalue, newvalue, tempvalue; + int nr; + + if ((oldvalue = umask(0777)) != 0) err(0, UMASK, NIL); + + /* Special test: only the lower 9 bits (protection bits) may part- * + * icipate. ~0777 means: 111 000 000 000. Giving this to umask must* + * not change any value. */ + + if ((newvalue = umask(~0777)) != 0777) err(1, UMASK, "illegal"); + if (oldvalue == newvalue) err(11, UMASK, "not change mask"); + + if ((tempvalue = umask(0)) != 0) err(2, UMASK, "values"); + + + /* Now test all possible modes of umask on a file */ + for (newvalue = MASK; newvalue >= 0; newvalue -= 0111) { + tempvalue = umask(newvalue); + if (tempvalue != oldvalue) { + err(1, UMASK, "illegal"); + break; /* no use trying more */ + } else if ((nr = creat("file01", 0777)) < 0) + err(5, CREAT, "'file01'"); + else { + try_close(nr, "'file01'"); + if (get_mode("file01") != (MASK & ~newvalue)) + err(7, UMASK, "mode computed"); + try_unlink("file01"); + } + oldvalue = newvalue; + } + + /* The loop has terminated with umask(0) */ + if ((tempvalue = umask(0)) != 0) + err(7, UMASK, "umask may influence rest of tests!"); +} /* test01 */ + + + +/***************************************************************************** + * test CREAT * + ****************************************************************************/ +void test02() +{ + int n, n1, mode; + char a[ARSIZE], b[ARSIZE]; + struct stat stbf1; + + mode = 0; + /* Create twenty files, check filedes */ + for (n = 0; n < MAXOPEN; n++) { + if (creat(file[n], mode) != FF + n) + err(13, CREAT, file[n]); + else { + if (get_mode(file[n]) != mode) + err(7, CREAT, "mode set while creating many files"); + + /* Change mode of file to standard mode, we want to * + * use a lot (20) of files to be opened later, see * + * open_alot(), close_alot(). */ + if (chmod(file[n], 0700) != OK) err(5, CHMOD, file[n]); + + } + mode = (mode + 0100) % 01000; + } + + /* Already twenty files opened; opening another has to fail */ + if (creat("file02", 0777) != FAIL) + err(9, CREAT, "created"); + else + check(CREAT, EMFILE); + + /* Close all files: seems blunt, but it isn't because we've * + * checked all fd's already */ + if ((n = close_alot(MAXOPEN)) < MAXOPEN) err(5, CLOSE, "MAXOPEN files"); + + /* Creat 1 file twice; check */ + if ((n = creat("file02", 0777)) < 0) + err(5, CREAT, "'file02'"); + else { + init_array(a); + if (write(n, a, ARSIZE) != ARSIZE) err(1, WRITE, "bad"); + + if ((n1 = creat("file02", 0755)) < 0) /* receate 'file02' */ + err(5, CREAT, "'file02' (2nd time)"); + else { + /* Fd should be at the top after recreation */ + if (lseek(n1, 0L, SEEK_END) != 0) + err(11, CREAT, "not truncate file by recreation"); + else { + /* Try to write on recreated file */ + clear_array(b); + + if (lseek(n1, 0L, SEEK_SET) != 0) + err(5, LSEEK, "to top of 2nd fd 'file02'"); + if (write(n1, a, ARSIZE) != ARSIZE) + err(1, WRITE, "(2) bad"); + + /* In order to read we've to close and open again */ + try_close(n1, "'file02' (2nd creation)"); + if ((n1 = open("file02", RW)) < 0) + err(5, OPEN, "'file02' (2nd recreation)"); + + /* Continue */ + if (lseek(n1, 0L, SEEK_SET) != 0) + err(5, LSEEK, "to top 'file02'(2nd fd) (2)"); + if (read(n1, b, ARSIZE) != ARSIZE) + err(1, READ, "wrong"); + + if (comp_array(a, b, ARSIZE) != OK) err(11, CREAT, + "not really truncate file by recreation"); + } + if (get_mode("file02") != 0777) + err(11, CREAT, "not maintain mode by recreation"); + try_close(n1, "recreated 'file02'"); + + } + Remove(n, "file02"); + } + + /* Give 'creat' wrong input: dir not searchable */ + if (creat("drw-/file02", 0777) != FAIL) + err(4, CREAT, "'drw-'"); + else + check(CREAT, EACCES); + + /* Dir not writable */ + if (creat("dr-x/file02", 0777) != FAIL) + err(12, CREAT, "'dr-x/file02'"); + else + check(CREAT, EACCES); + + /* File not writable */ + if (creat("drwx/r-x", 0777) != FAIL) + err(11, CREAT, "recreate non-writable file"); + else + check(CREAT, EACCES); + + /* Try to creat a dir */ + if ((n = creat("dir", 040777)) != FAIL) { + if (fstat(n, &stbf1) != OK) + err(5, FSTAT, "'dir'"); + else if (stbf1.st_mode != (mode_t) 0100777) + /* Cast because mode is negative :-(. + * HACK DEBUG FIXME: this appears to duplicate + * code in test17.c. + */ + err(11, CREAT, "'creat' a new directory"); + Remove(n, "dir"); + } + + /* We don't consider it to be a bug when creat * does not accept + * tricky modes */ + + /* File is an existing dir */ + if (creat("drwx", 0777) != FAIL) + err(11, CREAT, "create an existing dir!"); + else + check(CREAT, EISDIR); +} /* test02 */ + + + + + +/***************************************************************************** + * test WRITE * + ****************************************************************************/ +void test03() +{ + int n, n1; + int fd[2]; + char a[ARSIZE]; + + init_array(a); + + /* Test write after a CREAT */ + if ((n = creat("file03", 0700)) != FF) /* 'file03' only open file */ + err(13, CREAT, "'file03'"); + else { + write_standards(n, a); /* test simple writes, wrong input too */ + try_close(n, "'file03'"); + } + + /* Test write after an OPEN */ + if ((n = open("file03", W)) < 0) + err(5, OPEN, "'file03'"); + else + write_standards(n, a); /* test simple writes, wrong input too */ + + /* Test write after a DUP */ + if ((n1 = dup(n)) < 0) + err(5, DUP, "'file03'"); + else { + write_standards(n1, a); + try_close(n1, "duplicated fd 'file03'"); + } + + /* Remove testfile */ + Remove(n, "file03"); + + /* Test write after a PIPE */ + if (pipe(fd) < 0) + err(5, PIPE, NIL); + else { + write_standards(fd[1], a); + try_close(fd[0], "'fd[0]'"); + try_close(fd[1], "'fd[1]'"); + } + + /* Last test: does write check protections ? */ + if ((n = open("drwx/r--", R)) < 0) + err(5, OPEN, "'drwx/r--'"); + else { + if (write(n, a, ARSIZE) != FAIL) + err(11, WRITE, "write on non-writ. file"); + else + check(WRITE, EBADF); + try_close(n, "'drwx/r--'"); + } +} /* test03 */ + + +void write_standards(filedes, a) +int filedes; +char a[]; +{ + + /* Write must return written account of numbers */ + if (write(filedes, a, ARSIZE) != ARSIZE) err(1, WRITE, "bad"); + + /* Try giving 'write' wrong input */ + /* Wrong filedes */ + if (write(-1, a, ARSIZE) != FAIL) + err(2, WRITE, "filedes"); + else + check(WRITE, EBADF); + + /* Wrong length (illegal) */ +#ifndef PDPNOHANG + if (write(filedes, a, -ARSIZE) != FAIL) + err(2, WRITE, "length"); + else + check(WRITE, EINVAL); /* EFAULT on vu45 */ +#endif +} /* write_standards */ + + + + + + +/* "t2.c", created by Rene Montsma and Menno Wilcke */ + + +/***************************************************************************** + * test READ * + ****************************************************************************/ +void test04() +{ + int n, n1, fd[2]; + char a[ARSIZE]; + + /* Test read after creat */ + if ((n = creat("file04", 0700)) != FF) /* no other open files may be + * left */ + err(13, CREAT, "'file04'"); + else { + /* Closing and opening needed before writing */ + try_close(n, "'file04'"); + if ((n = open("file04", RW)) < 0) err(5, OPEN, "'file04'"); + + init_array(a); + + if (write(n, a, ARSIZE) != ARSIZE) + err(1, WRITE, "bad"); + else { + if (lseek(n, 0L, SEEK_SET) != 0) err(5, LSEEK, "'file04'"); + read_standards(n, a); + read_more(n, a); + } + try_close(n, "'file04'"); + } + + /* Test read after OPEN */ + if ((n = open("file04", R)) < 0) + err(5, OPEN, "'file04'"); + else { + read_standards(n, a); + read_more(n, a); + try_close(n, "'file04'"); + } + + /* Test read after DUP */ + if ((n = open("file04", R)) < 0) err(5, OPEN, "'file04'"); + if ((n1 = dup(n)) < 0) + err(5, DUP, "'file04'"); + else { + read_standards(n1, a); + read_more(n1, a); + try_close(n1, "duplicated fd 'file04'"); + } + + /* Remove testfile */ + Remove(n, "file04"); + + /* Test read after pipe */ + if (pipe(fd) < 0) + err(5, PIPE, NIL); + else { + if (write(fd[1], a, ARSIZE) != ARSIZE) { + err(5, WRITE, "'fd[1]'"); + try_close(fd[1], "'fd[1]'"); + } else { + try_close(fd[1], "'fd[1]'"); + read_standards(fd[0], a); + } + try_close(fd[0], "'fd[0]'"); + } + + /* Last test: try to read a read-protected file */ + if ((n = open("drwx/-wx", W)) < 0) + err(5, OPEN, "'drwx/-wx'"); + else { + if (read(n, a, ARSIZE) != FAIL) + err(11, READ, "read a non-read. file"); + else + check(READ, EBADF); + try_close(n, "'/drwx/-wx'"); + } +} /* test04 */ + + +void read_standards(filedes, a) +int filedes; +char a[]; +{ + char b[ARSIZE]; + + clear_array(b); + if (read(filedes, b, ARSIZE) != ARSIZE) + err(1, READ, "bad"); + else if (comp_array(a, b, ARSIZE) != OK) + err(7, "read/write", "values"); + else if (read(filedes, b, ARSIZE) != READ_EOF) + err(11, READ, "read beyond endoffile"); + + + /* Try giving read wrong input: wrong filedes */ + if (read(FAIL, b, ARSIZE) != FAIL) + err(2, READ, "filedes"); + else + check(READ, EBADF); + + + /* Wrong length */ + if (read(filedes, b, -ARSIZE) != FAIL) + err(2, READ, "length"); + else + check(READ, EINVAL); +} /* read_standards */ + + + +void read_more(filedes, a) +int filedes; +char a[]; + /* Separated from read_standards() because the PIPE test * would fail. */ +{ + int i; + char b[ARSIZE]; + + if (lseek(filedes, (long) (ARSIZE / 2), SEEK_SET) != ARSIZE / 2) + err(5, LSEEK, "to location ARSIZE/2"); + + clear_array(b); + if (read(filedes, b, ARSIZE) != ARSIZE / 2) err(1, READ, "bad"); + + for (i = 0; i < ARSIZE / 2; i++) + if (b[i] != a[(ARSIZE / 2) + i]) + err(7, READ, "from location ARSIZE/2"); +} + +/***************************************************************************** + * test OPEN/CLOSE * + ****************************************************************************/ +void test05() +{ + int n, n1, mode, fd[2]; + char b[ARSIZE]; + + /* Test open after CREAT */ + if ((n = creat("file05", 0700)) != FF) /* no other open files left */ + err(13, CREAT, "'file05'"); + else { + if ((n1 = open("file05", RW)) != FF + 1) + err(13, OPEN, "'file05' after creation"); + try_close(n1, "'file05' (open after creation)"); + + try_close(n, "'file05'"); + if ((n = open("file05", R)) != FF) + err(13, OPEN, "after closing"); + else + try_close(n, "'file05' (open after closing)"); + + /* Remove testfile */ + try_unlink("file05"); + } + + /* Test all possible modes, try_open not only opens file (sometimes) * + * but closes files too (when opened) */ + if ((n = creat("file05", 0700)) < 0) /* no other files left */ + err(5, CREAT, "'file05' (2nd time)"); + else { + try_close(n, "file05"); + for (mode = 0; mode <= 0700; mode += 0100) { + if (chmod("file05", mode) != OK) err(5, CHMOD, "'file05'"); + + if (mode <= 0100) { + try_open("file05", R, FAIL); + try_open("file05", W, FAIL); + try_open("file05", RW, FAIL); + } else if (mode >= 0200 && mode <= 0300) { + try_open("file05", R, FAIL); + try_open("file05", W, FF); + try_open("file05", RW, FAIL); + } else if (mode >= 0400 && mode <= 0500) { + try_open("file05", R, FF); + try_open("file05", W, FAIL); + try_open("file05", RW, FAIL); + } else { + try_open("file05", R, FF); + try_open("file05", W, FF); + try_open("file05", RW, FF); + } + } + } + + /* Test opening existing file */ + if ((n = open("drwx/rwx", R)) < 0) + err(13, OPEN, "existing file"); + else { /* test close after DUP */ + if ((n1 = dup(n)) < 0) + err(13, DUP, "'drwx/rwx'"); + else { + try_close(n1, "duplicated fd 'drwx/rwx'"); + + if (read(n1, b, ARSIZE) != FAIL) + err(11, READ, "on closed dupped fd 'drwx/rwx'"); + else + check(READ, EBADF); + + if (read(n, b, ARSIZE) == FAIL) /* should read an eof */ + err(13, READ, "on fd '/drwx/rwx'"); + } + try_close(n, "'drwx/rwx'"); + } + + /* Test close after PIPE */ + if (pipe(fd) < 0) + err(13, PIPE, NIL); + else { + try_close(fd[1], "duplicated fd 'fd[1]'"); + + /* Fd[1] really should be closed now; check */ + clear_array(b); + if (read(fd[0], b, ARSIZE) != READ_EOF) + err(11, READ, "read on empty pipe (and fd[1] was closed)"); + try_close(fd[0], "duplicated fd 'fd[0]'"); + } + + /* Try to open a non-existing file */ + if (open("non-file", R) != FAIL) + err(11, OPEN, "open non-executable file"); + else + check(OPEN, ENOENT); + + /* Dir does not exist */ + if (open("dzzz/file05", R) != FAIL) + err(11, OPEN, "open in an non-searchable dir"); + else + check(OPEN, ENOENT); + + /* Dir is not searchable */ + if (n = open("drw-/rwx", R) != FAIL) + err(11, OPEN, "open in an non-searchabledir"); + else + check(OPEN, EACCES); + + + /* Unlink testfile */ + try_unlink("file05"); + + + /* File is not readable */ + if (open("drwx/-wx", R) != FAIL) + err(11, OPEN, "open unreadable file for reading"); + else + check(OPEN, EACCES); + + /* File is not writable */ + if (open("drwx/r-x", W) != FAIL) + err(11, OPEN, "open unwritable file for writing"); + else + check(OPEN, EACCES); + + /* Try opening more than MAXOPEN ('extra' (19-8-85)) files */ + if ((n = open_alot()) != MAXOPEN) + err(13, OPEN, "MAXOPEN files"); + else + /* Maximum # of files opened now, another open should fail + * because * all filedescriptors have already been used. */ + if (open("drwx/rwx", RW) != FAIL) + err(9, OPEN, "open"); + else + check(OPEN, EMFILE); + if (close_alot(n) != n) err(5, CLOSE, "all opened files"); + + /* Can close make mistakes ? */ + if (close(-1) != FAIL) + err(2, CLOSE, "filedes"); + else + check(CLOSE, EBADF); +} /* test05 */ + + +void try_open(fname, mode, test) +int mode, test; +char *fname; +{ + int n; + + if ((n = open(fname, mode)) != test) + err(11, OPEN, "break through filepermission with an incorrect mode"); + if (n != FAIL) try_close(n, fname); /* cleanup */ +} /* try_open */ + + + +/***************************************************************************** + * test LSEEK * + ****************************************************************************/ +void test06() +{ + char a[ARSIZE], b[ARSIZE]; + int fd; + + if ((fd = open("drwx/rwx", RW)) != FF) /* there should be no */ + err(13, OPEN, "'drwx/rwx'"); /* other open files */ + else { + init_array(a); + if (write(fd, a, 10) != 10) + err(1, WRITE, "bad"); + else { + /* Lseek back to begin file */ + if (lseek(fd, 0L, SEEK_SET) != 0) + err(5, LSEEK, "to begin file"); + else if (read(fd, b, 10) != 10) + err(1, READ, "bad"); + else if (comp_array(a, b, 10) != OK) + err(7, LSEEK, "values r/w after lseek to begin"); + /* Lseek to endoffile */ + if (lseek(fd, 0L, SEEK_END) != 10) + err(5, LSEEK, "to end of file"); + else if (read(fd, b, 1) != READ_EOF) + err(7, LSEEK, "read at end of file"); + /* Lseek beyond file */ + if (lseek(fd, 10L, SEEK_CUR) != 20) + err(5, LSEEK, "beyond end of file"); + else if (write(fd, a, 10) != 10) + err(1, WRITE, "bad"); + else { + /* Lseek to begin second write */ + if (lseek(fd, 20L, SEEK_SET) != 20) + err(5, LSEEK, "'/drwx/rwx'"); + if (read(fd, b, 10) != 10) + err(1, READ, "bad"); + else if (comp_array(a, b, 10) != OK) + err(7, LSEEK, + "values read after lseek MAXOPEN"); + } + } + + /* Lseek to position before begin of file */ + if (lseek(fd, -1L, 0) != FAIL) + err(11, LSEEK, "lseek before beginning of file"); + + try_close(fd, "'drwx/rwx'"); + } + + /* Lseek on invalid filediscriptor */ + if (lseek(-1, 0L, SEEK_SET) != FAIL) + err(2, LSEEK, "filedes"); + else + check(LSEEK, EBADF); + +} + + + + +/* "t3.c", created by Rene Montsma and Menno Wilcke */ + + +/***************************************************************************** + * test ACCESS * + ****************************************************************************/ +void test07() +{ + /* Check with proper parameters */ + if (access("drwx/rwx", RWX) != OK) err(5, ACCESS, "accessible file"); + + if (access("./././drwx/././rwx", 0) != OK) + err(5, ACCESS, "'/./.(etc)/drwx///rwx'"); + + /* Check 8 files with 8 different modes on 8 accesses */ + if (chdir("drwx") != OK) err(5, CHDIR, "'drwx'"); + + access_standards(); + + if (chdir("..") != OK) err(5, CHDIR, "'..'"); + + + /* Check several wrong combinations */ + /* File does not exist */ + if (access("non-file", 0) != FAIL) + err(11, ACCESS, "access non-existing file"); + else + check(ACCESS, ENOENT); + + /* Non-searchable dir */ + if (access("drw-/rwx", 0) != FAIL) + err(4, ACCESS, "'drw-'"); + else + check(ACCESS, EACCES); + + /* Searchable dir, but wrong file-mode */ + if (access("drwx/--x", RWX) != FAIL) + err(11, ACCESS, "a non accessible file"); + else + check(ACCESS, EACCES); + +} /* test07 */ + + +void access_standards() +{ + int i, mode = 0; + + for (i = 0; i < 8; i++) + if (i == 0) + try_access(fnames[mode], i, OK); + else + try_access(fnames[mode], i, FAIL); + mode++; + + for (i = 0; i < 8; i++) + if (i < 2) + try_access(fnames[mode], i, OK); + else + try_access(fnames[mode], i, FAIL); + mode++; + + for (i = 0; i < 8; i++) + if (i == 0 || i == 2) + try_access(fnames[mode], i, OK); + else + try_access(fnames[mode], i, FAIL); + mode++; + + for (i = 0; i < 8; i++) + if (i < 4) + try_access(fnames[mode], i, OK); + else + try_access(fnames[mode], i, FAIL); + mode++; + + for (i = 0; i < 8; i++) + if (i == 0 || i == 4) + try_access(fnames[mode], i, OK); + else + try_access(fnames[mode], i, FAIL); + mode++; + + for (i = 0; i < 8; i++) + if (i == 0 || i == 1 || i == 4 || i == 5) + try_access(fnames[mode], i, OK); + else + try_access(fnames[mode], i, FAIL); + mode++; + + for (i = 0; i < 8; i++) + if (i % 2 == 0) + try_access(fnames[mode], i, OK); + else + try_access(fnames[mode], i, FAIL); + mode++; + + for (i = 0; i < 8; i++) try_access(fnames[mode], i, OK); +} /* access_standards */ + + + +void try_access(fname, mode, test) +int mode, test; +char *fname; +{ + if (access(fname, mode) != test) + err(100, ACCESS, "incorrect access on a file (try_access)"); +} /* try_access */ + + + + + + + +/* "support.c", created by Rene Montsma and Menno Wilcke */ + + +/* Err, make_and_fill_dirs, init_array, clear_array, comp_array, + try_close, try_unlink, Remove, get_mode, check, open_alot, + close_alot, clean_up_the_mess. +*/ + + +/*********************************************************************** + * EXTENDED FIONS * + **********************************************************************/ +/* First extended functions (i.e. not oldfashioned monixcalls. + e(), nlcr(), octal.*/ + +void e(string) +char *string; +{ + printf("Test program error: %s\n", string); + errct++; +} + +void nlcr() +{ + printf("\n"); +} + +void str(s) +char *s; +{ + printf(s); +} + + +/***************************************************************************** +* * +* ERR(or) messages * +* * +*****************************************************************************/ +void err(number, scall, name) + /* Give nice error messages */ + +char *scall, *name; +int number; + +{ + errct++; + if (errct > MAXERR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + quit(); + } + e(""); + str("\t"); + switch (number) { + case 0: + str(scall); + str(": illegal initial value."); + break; + case 1: + str(scall); + str(": "); + str(name); + str(" value returned."); + break; + case 2: + str(scall); + str(": accepting illegal "); + str(name); + str("."); + break; + case 3: + str(scall); + str(": accepting non-existing file."); + break; + case 4: + str(scall); + str(": could search non-searchable dir ("); + str(name); + str(")."); + break; + case 5: + str(scall); + str(": cannot "); + str(scall); + str(" "); + str(name); + str("."); + break; + case 7: + str(scall); + str(": incorrect "); + str(name); + str("."); + break; + case 8: + str(scall); + str(": wrong values."); + break; + case 9: + str(scall); + str(": accepting too many "); + str(name); + str(" files."); + break; + case 10: + str(scall); + str(": even a superuser can't do anything!"); + break; + case 11: + str(scall); + str(": could "); + str(name); + str("."); + break; + case 12: + str(scall); + str(": could write in non-writable dir ("); + str(name); + str(")."); + break; + case 13: + str(scall); + str(": wrong filedes returned ("); + str(name); + str(")."); + break; + case 100: + str(scall); /* very common */ + str(": "); + str(name); + str("."); + break; + default: str("errornumber does not exist!\n"); + } + nlcr(); +} /* err */ + + +/***************************************************************************** +* * +* MAKE_AND_FILL_DIRS * +* * +*****************************************************************************/ + +void make_and_fill_dirs() + /* Create 8 dir.'s: "d---", "d--x", "d-w-", "d-wx", "dr--", "dr-x", * + * "drw-", "drwx". * Then create 8 files + * in "drwx", and some needed files in other dirs. */ +{ + int mode, i; + + for (i = 0; i < 8; i++) { + mkdir(dir[i], 0700); + chown(dir[i], USER_ID, GROUP_ID); + } + setuid(USER_ID); + setgid(GROUP_ID); + + for (mode = 0; mode < 8; mode++) put_file_in_dir("drwx", mode); + + put_file_in_dir("d-wx", RWX); + put_file_in_dir("dr-x", RWX); + put_file_in_dir("drw-", RWX); + + chmod_8_dirs(8); /* 8 means; 8 different modes */ + +} /* make_and_fill_dirs */ + + + +void put_file_in_dir(dirname, mode) +char *dirname; +int mode; + /* Fill directory 'dirname' with file with mode 'mode'. */ +{ + int nr; + + + if (chdir(dirname) != OK) + err(5, CHDIR, "to dirname (put_f_in_dir)"); + else { + /* Creat the file */ + if ((nr = creat(fnames[mode], mode * 0100)) < 0) + err(13, CREAT, fnames[mode]); + else + try_close(nr, fnames[mode]); + + if (chdir("..") != OK) + err(5, CHDIR, "to previous dir (put_f_in_dir)"); + } +} /* put_file_in_dir */ + + +/***************************************************************************** +* * +* MISCELLANEOUS * +* * +*(all about arrays, 'try_close', 'try_unlink', 'Remove', 'get_mode') * +* * +*****************************************************************************/ + + +void init_array(a) +char *a; +{ + int i; + + i = 0; + while (i++ < ARSIZE) *a++ = 'a' + (i % 26); +} /* init_array */ + +void clear_array(b) +char *b; +{ + int i; + + i = 0; + while (i++ < ARSIZE) *b++ = '0'; + +} /* clear_array */ + + +int comp_array(a, b, range) +char *a, *b; +int range; +{ + if ((range < 0) || (range > ARSIZE)) { + err(100, "comp_array", "illegal range"); + return(FAIL); + } else { + while (range-- && (*a++ == *b++)); + if (*--a == *--b) + return(OK); + else + return(FAIL); + } +} /* comp_array */ + + +void try_close(filedes, name) +int filedes; +char *name; +{ + if (close(filedes) != OK) err(5, CLOSE, name); +} /* try_close */ + + +void try_unlink(fname) +char *fname; +{ + if (unlink(fname) != 0) err(5, UNLINK, fname); +} /* try_unlink */ + + +void Remove(fdes, fname) +int fdes; +char *fname; +{ + try_close(fdes, fname); + try_unlink(fname); +} /* Remove */ + + +int get_mode(name) +char *name; +{ + struct stat stbf1; + + if (stat(name, &stbf1) != OK) { + err(5, STAT, name); + return(stbf1.st_mode); /* return a mode which will cause * + * error in the calling function * + * (file/dir bit) */ + } else + return(stbf1.st_mode & 07777); /* take last 4 bits */ +} /* get_mode */ + + +/***************************************************************************** +* * +* CHECK * +* * +*****************************************************************************/ + + +void check(scall, number) +int number; +char *scall; +{ + if (errno != number) { + e(NIL); + str("\t"); + str(scall); + str(": bad errno-value: "); + put(errno); + str(" should have been: "); + put(number); + nlcr(); + } +} /* check */ + +void put(nr) +int nr; +{ + switch (nr) { + case 0: str("unused"); break; + case 1: str("EPERM"); break; + case 2: str("ENOENT"); break; + case 3: str("ESRCH"); break; + case 4: str("EINTR"); break; + case 5: str("EIO"); break; + case 6: str("ENXIO"); break; + case 7: str("E2BIG"); break; + case 8: str("ENOEXEC"); break; + case 9: str("EBADF"); break; + case 10: str("ECHILD"); break; + case 11: str("EAGAIN"); break; + case 12: str("ENOMEM"); break; + case 13: str("EACCES"); break; + case 14: str("EFAULT"); break; + case 15: str("ENOTBLK"); break; + case 16: str("EBUSY"); break; + case 17: str("EEXIST"); break; + case 18: str("EXDEV"); break; + case 19: str("ENODEV"); break; + case 20: str("ENOTDIR"); break; + case 21: str("EISDIR"); break; + case 22: str("EINVAL"); break; + case 23: str("ENFILE"); break; + case 24: str("EMFILE"); break; + case 25: str("ENOTTY"); break; + case 26: str("ETXTBSY"); break; + case 27: str("EFBIG"); break; + case 28: str("ENOSPC"); break; + case 29: str("ESPIPE"); break; + case 30: str("EROFS"); break; + case 31: str("EMLINK"); break; + case 32: str("EPIPE"); break; + case 33: str("EDOM"); break; + case 34: str("ERANGE"); break; + } +} + + +/***************************************************************************** +* * +* ALOT-functions * +* * +*****************************************************************************/ + + + + +int open_alot() +{ + int i; + + for (i = 0; i < MAXOPEN; i++) + if (open(file[i], R) == FAIL) break; + if (i == 0) err(5, "open_alot", "at all"); + return(i); +} /* open_alot */ + + +int close_alot(number) +int number; +{ + int i, count = 0; + + if (number > MAXOPEN) + err(5, "close_alot", "accept this argument"); + else + for (i = FF; i < number + FF; i++) + if (close(i) != OK) count++; + + return(number - count); /* return number of closed files */ +} /* close_alot */ + + +/***************************************************************************** +* * +* CLEAN UP THE MESS * +* * +*****************************************************************************/ + + +void clean_up_the_mess() +{ + int i; + char dirname[6]; + + /* First remove 'alot' files */ + for (i = 0; i < MAXOPEN; i++) try_unlink(file[i]); + + /* Unlink the files in dir 'drwx' */ + if (chdir("drwx") != OK) + err(5, CHDIR, "to 'drwx'"); + else { + for (i = 0; i < 8; i++) try_unlink(fnames[i]); + if (chdir("..") != OK) err(5, CHDIR, "to '..'"); + } + + + /* Before unlinking files in some dirs, make them writable */ + chmod_8_dirs(RWX); + + /* Unlink files in other dirs */ + try_unlink("d-wx/rwx"); + try_unlink("dr-x/rwx"); + try_unlink("drw-/rwx"); + + /* Unlink dirs */ + for (i = 0; i < 8; i++) { + strcpy(dirname, "d"); + strcat(dirname, fnames[i]); + /* 'dirname' contains the directoryname */ + rmdir(dirname); + } + + /* FINISH */ +} /* clean_up_the_mess */ + +void chmod_8_dirs(sw) +int sw; /* if switch == 8, give all different + * mode,else the same mode */ +{ + int mode; + int i; + + if (sw == 8) + mode = 0; + else + mode = sw; + + for (i = 0; i < 8; i++) { + chmod(dir[i], 040000 + mode * 0100); + if (sw == 8) mode++; + } +} + + + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test19.c b/test/test19.c new file mode 100644 index 000000000..760f96550 --- /dev/null +++ b/test/test19.c @@ -0,0 +1,531 @@ +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define NB 30L +#define NBOUNDS 6 + +int errct, subtest, passes, pipesigs; +long t1; + +char aa[100]; +char b[4] = {0, 1, 2, 3}, c[4] = {10, 20, 30, 40}, d[4] = {6, 7, 8, 9}; +long bounds[NBOUNDS] = {7, 9, 50, 519, 520, 40000L}; +char buff[30000]; + +_PROTOTYPE(int main, (int argc, char *argv[])); +_PROTOTYPE(void test19a, (void)); +_PROTOTYPE(void test19b, (void)); +_PROTOTYPE(void test19c, (void)); +_PROTOTYPE(void test19d, (void)); +_PROTOTYPE(void test19e, (void)); +_PROTOTYPE(void test19f, (void)); +_PROTOTYPE(void test19g, (void)); +_PROTOTYPE(void clraa, (void)); +_PROTOTYPE(void pipecatcher, (int s)); +_PROTOTYPE(void e, (int n)); +_PROTOTYPE(void quit, (void)); + + +int main(argc, argv) +int argc; +char *argv[]; +{ + int i, m; + + m = (argc == 2 ? atoi(argv[1]) : 0xFFFF); + + if (geteuid() == 0 || getuid() == 0) { + printf("Test 19 cannot run as root; test aborted\n"); + exit(1); + } + + system("rm -rf DIR_19; mkdir DIR_19"); + chdir("DIR_19"); + + printf("Test 19 "); + fflush(stdout); + for (i = 0; i < 4; i++) { + if (m & 0001) test19a(); + if (m & 0002) test19b(); + if (m & 0004) test19c(); + if (m & 0010) test19d(); + if (m & 0020) test19e(); + if (m & 0040) test19f(); + if (m & 0100) test19g(); + passes++; + } + quit(); + return(-1); /* impossible */ +} + +void test19a() +{ +/* Test open with O_CREAT and O_EXCL. */ + + int fd; + + subtest = 1; + + if ( (fd = creat("T19.a1", 0777)) != 3) e(1); /* create test file */ + if (close(fd) != 0) e(2); + if ( (fd = open("T19.a1", O_RDONLY)) != 3) e(3); + if (close(fd) != 0) e(4); + if ( (fd = open("T19.a1", O_WRONLY)) != 3) e(5); + if (close(fd) != 0) e(6); + if ( (fd = open("T19.a1", O_RDWR)) != 3) e(7); + if (close(fd) != 0) e(8); + + /* See if O_CREAT actually creates a file. */ + if ( (fd = open("T19.a2", O_RDONLY)) != -1) e(9); /* must fail */ + if ( (fd = open("T19.a2", O_RDONLY | O_CREAT, 0444)) != 3) e(10); + if (close(fd) != 0) e(11); + if ( (fd = open("T19.a2", O_RDONLY)) != 3) e(12); + if (close(fd) != 0) e(13); + if ( (fd = open("T19.a2", O_WRONLY)) != -1) e(14); + if ( (fd = open("T19.a2", O_RDWR)) != -1) e(15); + + /* See what O_CREAT does on an existing file. */ + if ( (fd = open("T19.a2", O_RDONLY | O_CREAT, 0777)) != 3) e(16); + if (close(fd) != 0) e(17); + if ( (fd = open("T19.a2", O_RDONLY)) != 3) e(18); + if (close(fd) != 0) e(19); + if ( (fd = open("T19.a2", O_WRONLY)) != -1) e(20); + if ( (fd = open("T19.a2", O_RDWR)) != -1) e(21); + + /* See if O_EXCL works. */ + if ( (fd = open("T19.a2", O_RDONLY | O_EXCL)) != 3) e(22); + if (close(fd) != 0) e(23); + if ( (fd = open("T19.a2", O_WRONLY | O_EXCL)) != -1) e(24); + if ( (fd = open("T19.a3", O_RDONLY | O_EXCL)) != -1) e(25); + if ( (fd = open("T19.a3", O_RDONLY | O_CREAT | O_EXCL, 0444)) != 3) e(26); + if (close(fd) != 0) e(27); + errno = 0; + if ( (fd = open("T19.a3", O_RDONLY | O_CREAT | O_EXCL, 0444)) != -1) e(28); + if (errno != EEXIST) e(29); + + if (unlink("T19.a1") != 0) e(30); + if (unlink("T19.a2") != 0) e(31); + if (unlink("T19.a3") != 0) e(32); +} + + +void test19b() +{ +/* Test open with O_APPEND and O_TRUNC. */ + + int fd; + + subtest = 2; + + if ( (fd = creat("T19.b1", 0777)) != 3) e(1); /* create test file */ + if (write(fd, b, 4) != 4) e(2); + if (close(fd) != 0) e(3); + clraa(); + if ( (fd = open("T19.b1", O_RDWR | O_APPEND)) != 3) e(4); + if (read(fd, aa, 100) != 4) e(5); + if (aa[0] != 0 || aa[1] != 1 || aa[2] != 2 || aa[3] != 3) e(6); + if (close(fd) != 0) e(7); + if ( (fd = open("T19.b1", O_RDWR | O_APPEND)) != 3) e(8); + if (write(fd, b, 4) != 4) e(9); + if (lseek(fd, 0L, SEEK_SET) != 0L) e(10); + clraa(); + if (read(fd, aa, 100) != 8) e(11); + if (aa[4] != 0 || aa[5] != 1 || aa[6] != 2 || aa[7] != 3) e(12); + if (close(fd) != 0) e(13); + + if ( (fd = open("T19.b1", O_RDWR | O_TRUNC)) != 3) e(14); + if (read(fd, aa, 100) != 0) e(15); + if (close(fd) != 0) e(16); + + unlink("T19.b1"); +} + + +void test19c() +{ +/* Test program for open(), close(), creat(), read(), write(), lseek(). */ + + int i, n, n1, n2; + + subtest = 3; + if ((n = creat("foop", 0777)) != 3) e(1); + if ((n1 = creat("foop", 0777)) != 4) e(2); + if ((n2 = creat("/", 0777)) != -1) e(3); + if (close(n) != 0) e(4); + if ((n = open("foop", O_RDONLY)) != 3) e(5); + if ((n2 = open("nofile", O_RDONLY)) != -1) e(6); + if (close(n1) != 0) e(7); + + /* N is the only one open now. */ + for (i = 0; i < 2; i++) { + n1 = creat("File2", 0777); + if (n1 != 4) { + printf("creat yielded fd=%d, expected 4\n", n1); + e(8); + } + if ((n2 = open("File2", O_RDONLY)) != 5) e(9); + if (close(n1) != 0) e(10); + if (close(n2) != 0) e(11); + } + unlink("File2"); + if (close(n) != 0) e(12); + + /* All files closed now. */ + for (i = 0; i < 2; i++) { + if ((n = creat("foop", 0777)) != 3) e(13); + if (close(n) != 0) e(14); + if ((n = open("foop", O_RDWR)) != 3) e(15); + + /* Read/write tests */ + if (write(n, b, 4) != 4) e(16); + if (read(n, aa, 4) != 0) e(17); + if (lseek(n, 0L, SEEK_SET) != 0L) e(18); + if (read(n, aa, 4) != 4) e(19); + if (aa[0] != 0 || aa[1] != 1 || aa[2] != 2 || aa[3] != 3) e(20); + if (lseek(n, 0L, SEEK_SET) != 0L) e(21); + if (lseek(n, 2L, SEEK_CUR) != 2L) e(22); + if (read(n, aa, 4) != 2) e(23); + if (aa[0] != 2 || aa[1] != 3 || aa[2] != 2 || aa[3] != 3) e(24); + if (lseek(n, 2L, SEEK_SET) != 2L) e(25); + clraa(); + if (write(n, c, 4) != 4) e(26); + if (lseek(n, 0L, SEEK_SET) != 0L) e(27); + if (read(n, aa, 10) != 6) e(28); + if (aa[0] != 0 || aa[1] != 1 || aa[2] != 10 || aa[3] != 20) e(29); + if (lseek(n, 16L, SEEK_SET) != 16L) e(30); + if (lseek(n, 2040L, SEEK_END) != 2046L) e(31); + if (read(n, aa, 10) != 0) e(32); + if (lseek(n, 0L, SEEK_CUR) != 2046L) e(33); + clraa(); + if (write(n, c, 4) != 4) e(34); + if (lseek(n, 0L, SEEK_CUR) != 2050L) e(35); + if (lseek(n, 2040L, SEEK_SET) != 2040L) e(36); + clraa(); + if (read(n, aa, 20) != 10) e(37); + if (aa[0] != 0 || aa[5] != 0 || aa[6] != 10 || aa[9] != 40) e(38); + if (lseek(n, 10239L, SEEK_SET) != 10239L) e(39); + if (write(n, d, 2) != 2) e(40); + if (lseek(n, -2L, SEEK_END) != 10239L) e(41); + if (read(n, aa, 2) != 2) e(42); + if (aa[0] != 6 || aa[1] != 7) e(43); + if (lseek(n, NB * 1024L - 2L, SEEK_SET) != NB * 1024L - 2L) e(44); + if (write(n, b, 4) != 4) e(45); + if (lseek(n, 0L, SEEK_SET) != 0L) e(46); + if (lseek(n, -6L, SEEK_END) != 1024L * NB - 4) e(47); + clraa(); + if (read(n, aa, 100) != 6) e(48); + if (aa[0] != 0 || aa[1] != 0 || aa[3] != 1 || aa[4] != 2|| aa[5] != 3) + e(49); + if (lseek(n, 20000L, SEEK_SET) != 20000L) e(50); + if (write(n, c, 4) != 4) e(51); + if (lseek(n, -4L, SEEK_CUR) != 20000L) e(52); + if (read(n, aa, 4) != 4) e(53); + if (aa[0] != 10 || aa[1] != 20 || aa[2] != 30 || aa[3] != 40) e(54); + if (close(n) != 0) e(55); + if ((n1 = creat("foop", 0777)) != 3) e(56); + if (close(n1) != 0) e(57); + unlink("foop"); + + } +} + +void test19d() +{ +/* Test read. */ + + int i, fd, pd[2]; + char bb[100]; + + subtest = 4; + + for (i = 0; i < 100; i++) bb[i] = i; + if ( (fd = creat("T19.d1", 0777)) != 3) e(1); /* create test file */ + if (write(fd, bb, 100) != 100) e(2); + if (close(fd) != 0) e(3); + clraa(); + if ( (fd = open("T19.d1", O_RDONLY)) != 3) e(4); + errno = 1000; + if (read(fd, aa, 0) != 0) e(5); + if (errno != 1000) e(6); + if (read(fd, aa, 100) != 100) e(7); + if (lseek(fd, 37L, SEEK_SET) != 37L) e(8); + if (read(fd, aa, 10) != 10) e(9); + if (lseek(fd, 0L, SEEK_CUR) != 47L) e(10); + if (read(fd, aa, 100) != 53) e(11); + if (aa[0] != 47) e(12); + if (read(fd, aa, 1) != 0) e(13); + if (close(fd) != 0) e(14); + + /* Read from pipe with no writer open. */ + if (pipe(pd) != 0) e(15); + if (close(pd[1]) != 0) e(16); + errno = 2000; + if (read(pd[0], aa, 1) != 0) e(17); /* must return EOF */ + if (errno != 2000) e(18); + + /* Read from a pipe with O_NONBLOCK set. */ + if (fcntl(pd[0], F_SETFL, O_NONBLOCK) != 0) e(19); /* set O_NONBLOCK */ +/* + if (read(pd[0], aa, 1) != -1) e(20); + if (errno != EAGAIN) e(21); +*/ + if (close(pd[0]) != 0) e(19); + if (unlink("T19.d1") != 0) e(20); +} + + + +void test19e() +{ +/* Test link, unlink, stat, fstat, dup, umask. */ + + int i, j, n, n1, flag; + char a[255], b[255]; + struct stat s, s1; + + subtest = 5; + for (i = 0; i < 2; i++) { + umask(0); + + if ((n = creat("T3", 0702)) < 0) e(1); + if (link("T3", "newT3") < 0) e(2); + if ((n1 = open("newT3", O_RDWR)) < 0) e(3); + for (j = 0; j < 255; j++) a[j] = j; + if (write(n, a, 255) != 255) e(4); + if (read(n1, b, 255) != 255) e(5); + flag = 0; + for (j = 0; j < 255; j++) + if (a[j] != b[j]) flag++; + if (flag) e(6); + if (unlink("T3") < 0) e(7); + if (close(n) < 0) e(8); + if (close(n1) < 0) e(9); + if ((n1 = open("newT3", O_RDONLY)) < 0) e(10); + if (read(n1, b, 255) != 255) e(11); + flag = 0; + for (j = 0; j < 255; j++) + if (a[j] != b[j]) flag++; + if (flag) e(12); + + /* Now check out stat, fstat. */ + if (stat("newT3", &s) < 0) e(13); + if (s.st_mode != (mode_t) 0100702) e(14); + /* The cast was because regular modes are + * negative :-(. Anyway, the magic number + * should be (S_IFREG | S_IRWXU | S_IWOTH) + * for POSIX. + */ + if (s.st_nlink != 1) e(15); + if (s.st_size != 255L) e(16); + if (fstat(n1, &s1) < 0) e(17); + if (s.st_dev != s1.st_dev) e(18); + if (s.st_ino != s1.st_ino) e(19); + if (s.st_mode != s1.st_mode) e(20); + if (s.st_nlink != s1.st_nlink) e(21); + if (s.st_uid != s1.st_uid) e(22); + if (s.st_gid != s1.st_gid) e(23); + if (s.st_rdev != s1.st_rdev) e(24); + if (s.st_size != s1.st_size) e(25); + if (s.st_atime != s1.st_atime) e(26); + if (close(n1) < 0) e(27); + if (unlink("newT3") < 0) e(28); + + umask(040); + if ((n = creat("T3a", 0777)) < 0) e(29); + if (stat("T3a", &s) < 0) e(30); + if (s.st_mode != (mode_t) 0100737) e(31); /* negative :-( */ + if (unlink("T3a") < 0) e(32); + if (close(n1) < 0) e(33); + + /* Dup */ + if ((n = creat("T3b", 0777)) < 0) e(34); + if (close(n) < 0) e(35); + if ((n = open("T3b", O_RDWR)) < 0) e(36); + if ((n1 = dup(n)) != n + 1) e(37); + if (write(n, a, 255) != 255) e(38); + read(n1, b, 20); + if (lseek(n, 0L, SEEK_SET) != 0L) e(39); + if ((j = read(n1, b, 1024)) != 255) e(40); + if (unlink("T3b") < 0) e(41); + if (close(n) < 0) e(42); + if (close(n1) < 0) e(43); + + } +} + +void test19f() +{ +/* Test large files to see if indirect block stuff works. */ + + int fd, i; + long pos; + + subtest = 6; + + if (passes > 0) return; /* takes too long to repeat this test */ + for (i = 0; i < NBOUNDS; i ++) { + pos = 1024L * bounds[i]; + fd = creat("T19f", 0777); + if (fd < 0) e(10*i+1); + if (lseek(fd, pos, 0) < 0) e(10*i+2); + if (write(fd, buff, 30720) != 30720) e(10*i+3); + if (close(fd) < 0) e(10*i+3); + if (unlink("T19f") < 0) e(10*i+4); + } +} + + +void test19g() +{ +/* Test POSIX calls for pipe, read, write, lseek and close. */ + + int pipefd[2], n, i, fd; + char buf[512], buf2[512]; + + subtest = 7; + + for (i = 0; i < 512; i++) buf[i] = i % 128; + + if (pipe(pipefd) < 0) e(1); + if (write(pipefd[1], buf, 512) != 512) e(2); + if (read(pipefd[0], buf2, 512) != 512) e(3); + if (close(pipefd[1]) != 0) e(4); + if (close(pipefd[1]) >= 0) e(5); + if (read(pipefd[0], buf2, 1) != 0) e(6); + if (close(pipefd[0]) != 0) e(7); + + /* Test O_NONBLOCK on pipes. */ + if (pipe(pipefd) < 0) e(8); + if (fcntl(pipefd[0], F_SETFL, O_NONBLOCK) != 0) e(9); + if (read(pipefd[0], buf2, 1) != -1) e(10); + if (errno != EAGAIN) e(11); + if (close(pipefd[0]) != 0) e(12); + if (close(pipefd[1]) != 0) e(13); + + /* Test read and lseek. */ + if ( (fd = creat("T19.g1", 0777)) != 3) e(14); /* create test file */ + if (write(fd, buf, 512) != 512) e(15); + errno = 3000; + if (read(fd, buf2, 512) != -1) e(16); + if (errno != EBADF) e(17); + if (close(fd) != 0) e(18); + if ( (fd = open("T19.g1", O_RDWR)) != 3) e(19); + if (read(fd, buf2, 512) != 512) e(20); + if (read(fd, buf2, 512) != 0) e(21); + if (lseek(fd, 100L, SEEK_SET) != 100L) e(22); + if (read(fd, buf2, 512) != 412) e(23); + if (lseek(fd, 1000L, SEEK_SET) != 1000L) e(24); + + /* Test write. */ + if (lseek(fd, -1000L, SEEK_CUR) != 0) e(25); + if (write(fd, buf, 512) != 512) e(26); + if (lseek(fd, 2L, SEEK_SET) != 2) e(27); + if (write(fd, buf, 3) != 3) e(28); + if (lseek(fd, -2L, SEEK_CUR) != 3) e(29); + if (write(fd, &buf[30], 1) != 1) e(30); + if (lseek(fd, 2L, SEEK_CUR) != 6) e(31); + if (write(fd, &buf[60], 1) != 1) e(32); + if (lseek(fd, -512L, SEEK_END) != 0) e(33); + if (read(fd, buf2, 8) != 8) e(34); + errno = 4000; + if (buf2[0] != 0 || buf2[1] != 1 || buf2[2] != 0 || buf2[3] != 30) e(35); + if (buf2[4] != 2 || buf2[5] != 5 || buf2[6] != 60 || buf2[7] != 7) e(36); + + /* Turn the O_APPEND flag on. */ + if (fcntl(fd, F_SETFL, O_APPEND) != 0) e(37); + if (lseek(fd, 0L, SEEK_SET) != 0) e(38); + if (write(fd, &buf[100], 1) != 1) e(39); + if (lseek(fd, 0L, SEEK_SET) != 0) e(40); + if (read(fd, buf2, 10) != 10) e(41); + if (buf2[0] != 0) e(42); + if (lseek(fd, -1L, SEEK_END) != 512) e(43); + if (read(fd, buf2, 10) != 1) e(44); + if (buf2[0] != 100) e(45); + if (close(fd) != 0) e(46); + + /* Now try write with O_NONBLOCK. */ + if (pipe(pipefd) != 0) e(47); + if (fcntl(pipefd[1], F_SETFL, O_NONBLOCK) != 0) e(48); + if (write(pipefd[1], buf, 512) != 512) e(49); + if (write(pipefd[1], buf, 512) != 512) e(50); + errno = 0; + for (i = 1; i < 20; i++) { + n = write(pipefd[1], buf, 512); + if (n == 512) continue; + if (n != -1 || errno != EAGAIN) {e(51); break;} + } + if (read(pipefd[0], buf, 512) != 512) e(52); + if (close(pipefd[0]) != 0) e(53); + + /* Write to a pipe with no reader. This should generate a signal. */ + signal(SIGPIPE, pipecatcher); + errno = 0; + if (write(pipefd[1], buf, 1) != -1) e(54); + if (errno != EPIPE) e(55); + if (pipesigs != passes + 1) e(56); /* we should have had the sig now */ + if (close(pipefd[1]) != 0) e(57); + errno = 0; + if (write(100, buf, 512) != -1) e(58); + if (errno != EBADF) e(59); + if (unlink("T19.g1") != 0) e(60); +} + + +void clraa() +{ + int i; + for (i = 0; i < 100; i++) aa[i] = 0; +} + + +void pipecatcher(s) +int s; /* it is supposed to have an arg */ +{ + pipesigs++; +} + + +void e(n) +int n; +{ + int err_num = errno; /* save errno in case printf clobbers it */ + + printf("Subtest %d, error %d errno=%d ", subtest, n, errno); + fflush(stdout); /* aargh! Most results go to stdout and are + * messed up by perror going to stderr. + * Should replace perror by printf and strerror + * in all the tests. + */ + errno = err_num; /* restore errno, just in case */ + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} + + diff --git a/test/test2.c b/test/test2.c new file mode 100644 index 000000000..f2b0d1c8e --- /dev/null +++ b/test/test2.c @@ -0,0 +1,416 @@ +/* test 2 */ + +#include <sys/types.h> +#include <sys/times.h> +#include <sys/wait.h> +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <time.h> +#include <stdio.h> + +#define ITERATIONS 5 +#define MAX_ERROR 4 + +int is, array[4], parsigs, parcum, sigct, cumsig, errct, subtest; +int iteration, kk = 0, errct = 0; +char buf[2048]; + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void test2a, (void)); +_PROTOTYPE(void test2b, (void)); +_PROTOTYPE(void test2c, (void)); +_PROTOTYPE(void test2d, (void)); +_PROTOTYPE(void test2e, (void)); +_PROTOTYPE(void test2f, (void)); +_PROTOTYPE(void test2g, (void)); +_PROTOTYPE(void test2h, (void)); +_PROTOTYPE(void sigpip, (int s)); +_PROTOTYPE(void quit, (void)); +_PROTOTYPE(void e, (int n)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + + if (argc == 2) m = atoi(argv[1]); + + printf("Test 2 "); + fflush(stdout); /* have to flush for child's benefit */ + + system("rm -rf DIR_02; mkdir DIR_02"); + chdir("DIR_02"); + + for (i = 0; i < ITERATIONS; i++) { + iteration = i; + if (m & 0001) test2a(); + if (m & 0002) test2b(); + if (m & 0004) test2c(); + if (m & 0010) test2d(); + if (m & 0020) test2e(); + if (m & 0040) test2f(); + if (m & 0100) test2g(); + if (m & 0200) test2h(); + } + subtest = 100; + if (cumsig != ITERATIONS) e(101); + quit(); + return(-1); /* impossible */ +} + + +void test2a() +{ +/* Test pipes */ + + int fd[2]; + int n, i, j, q = 0; + + subtest = 1; + if (pipe(fd) < 0) { + printf("pipe error. errno= %d\n", errno); + errct++; + quit(); + } + i = fork(); + if (i < 0) { + printf("fork failed\n"); + errct++; + quit(); + } + if (i != 0) { + /* Parent code */ + close(fd[0]); + for (i = 0; i < 2048; i++) buf[i] = i & 0377; + for (q = 0; q < 8; q++) { + if (write(fd[1], buf, 2048) < 0) { + printf("write pipe err. errno=%d\n", errno); + errct++; + quit(); + } + } + close(fd[1]); + wait(&q); + if (q != 256 * 58) { + printf("wrong exit code %d\n", q); + errct++; + quit(); + } + } else { + /* Child code */ + close(fd[1]); + for (q = 0; q < 32; q++) { + n = read(fd[0], buf, 512); + if (n != 512) { + printf("read yielded %d bytes, not 512\n", n); + errct++; + quit(); + } + for (j = 0; j < n; j++) + if ((buf[j] & 0377) != (kk & 0377)) { + printf("wrong data: %d %d %d \n ", + j, buf[j] & 0377, kk & 0377); + } else { + kk++; + } + } + exit(58); + } +} + + +void test2b() +{ + int fd[2], n; + char buf[4]; + + subtest = 2; + sigct = 0; + signal(SIGPIPE, sigpip); + pipe(fd); + if (fork()) { + /* Parent */ + close(fd[0]); + while (sigct == 0) { + write(fd[1], buf, 1); + } + wait(&n); + } else { + /* Child */ + close(fd[0]); + close(fd[1]); + exit(0); + } +} + + + +void test2c() +{ + int n; + + subtest = 3; + signal(SIGINT, SIG_DFL); + is = 0; + if ((array[is++] = fork()) > 0) { + if ((array[is++] = fork()) > 0) { + if ((array[is++] = fork()) > 0) { + if ((array[is++] = fork()) > 0) { + signal(SIGINT, SIG_IGN); + kill(array[0], SIGINT); + kill(array[1], SIGINT); + kill(array[2], SIGINT); + kill(array[3], SIGINT); + wait(&n); + wait(&n); + wait(&n); + wait(&n); + } else { + pause(); + } + } else { + pause(); + } + } else { + pause(); + } + } else { + pause(); + } +} + +void test2d() +{ + + int pid, stat_loc, s; + + /* Test waitpid. */ + subtest = 4; + + /* Test waitpid(pid, arg2, 0) */ + pid = fork(); + if (pid < 0) e(1); + if (pid > 0) { + /* Parent. */ + s = waitpid(pid, &stat_loc, 0); + if (s != pid) e(2); + if (WIFEXITED(stat_loc) == 0) e(3); + if (WIFSIGNALED(stat_loc) != 0) e(4); + if (WEXITSTATUS(stat_loc) != 22) e(5); + } else { + /* Child */ + exit(22); + } + + /* Test waitpid(-1, arg2, 0) */ + pid = fork(); + if (pid < 0) e(6); + if (pid > 0) { + /* Parent. */ + s = waitpid(-1, &stat_loc, 0); + if (s != pid) e(7); + if (WIFEXITED(stat_loc) == 0) e(8); + if (WIFSIGNALED(stat_loc) != 0) e(9); + if (WEXITSTATUS(stat_loc) != 33) e(10); + } else { + /* Child */ + exit(33); + } + + /* Test waitpid(0, arg2, 0) */ + pid = fork(); + if (pid < 0) e(11); + if (pid > 0) { + /* Parent. */ + s = waitpid(0, &stat_loc, 0); + if (s != pid) e(12); + if (WIFEXITED(stat_loc) == 0) e(13); + if (WIFSIGNALED(stat_loc) != 0) e(14); + if (WEXITSTATUS(stat_loc) != 44) e(15); + } else { + /* Child */ + exit(44); + } + + /* Test waitpid(0, arg2, WNOHANG) */ + signal(SIGTERM, SIG_DFL); + pid = fork(); + if (pid < 0) e(16); + if (pid > 0) { + /* Parent. */ + s = waitpid(0, &stat_loc, WNOHANG); + if (s != 0) e(17); + if (kill(pid, SIGTERM) != 0) e(18); + if (waitpid(pid, &stat_loc, 0) != pid) e(19); + if (WIFEXITED(stat_loc) != 0) e(20); + if (WIFSIGNALED(stat_loc) == 0) e(21); + if (WTERMSIG(stat_loc) != SIGTERM) e(22); + } else { + /* Child */ + pause(); + } + + /* Test some error conditions. */ + errno = 9999; + if (waitpid(0, &stat_loc, 0) != -1) e(23); + if (errno != ECHILD) e(24); + errno = 9999; + if (waitpid(0, &stat_loc, WNOHANG) != -1) e(25); + if (errno != ECHILD) e(26); +} + + +void test2e() +{ + + int pid1, pid2, stat_loc, s; + + /* Test waitpid with two children. */ + subtest = 5; + if (iteration > 1) return; /* slow test, don't do it too much */ + if ( (pid1 = fork())) { + /* Parent. */ + if ( (pid2 = fork()) ) { + /* Parent. Collect second child first. */ + s = waitpid(pid2, &stat_loc, 0); + if (s != pid2) e(1); + if (WIFEXITED(stat_loc) == 0) e(2); + if (WIFSIGNALED(stat_loc) != 0) e(3); + if (WEXITSTATUS(stat_loc) != 222) e(4); + + /* Now collect first child. */ + s = waitpid(pid1, &stat_loc, 0); + if (s != pid1) e(5); + if (WIFEXITED(stat_loc) == 0) e(6); + if (WIFSIGNALED(stat_loc) != 0) e(7); + if (WEXITSTATUS(stat_loc) != 111) e(8); + } else { + /* Child 2. */ + sleep(2); /* child 2 delays before exiting. */ + exit(222); + } + } else { + /* Child 1. */ + exit(111); /* child 1 exits immediately */ + } + +} + + +void test2f() +{ +/* test getpid, getppid, getuid, etc. */ + + pid_t pid, pid1, ppid, cpid, stat_loc, err; + + subtest = 6; + errno = -2000; + err = 0; + pid = getpid(); + if ( (pid1 = fork())) { + /* Parent. Do nothing. */ + if (wait(&stat_loc) != pid1) e(1); + if (WEXITSTATUS(stat_loc) != (pid1 & 0377)) e(2); + } else { + /* Child. Get ppid. */ + cpid = getpid(); + ppid = getppid(); + if (ppid != pid) err = 3; + if (cpid == ppid) err = 4; + exit(cpid & 0377); + } + if (err != 0) e(err); +} + +void test2g() +{ +/* test time(), times() */ + + time_t t1, t2; + clock_t t3, t4; + struct tms tmsbuf; + + subtest = 7; + errno = -7000; + + /* First time(). */ + t1 = -1; + t2 = -2; + t1 = time(&t2); + if (t1 < 650000000L) e(1); /* 650000000 is Sept. 1990 */ + if (t1 != t2) e(2); + t1 = -1; + t1 = time( (time_t *) NULL); + if (t1 < 650000000L) e(3); + t3 = times(&tmsbuf); + sleep(1); + t2 = time( (time_t *) NULL); + if (t2 < 0L) e(4); + if (t2 - t1 < 1) e(5); + + /* Now times(). */ + t4 = times(&tmsbuf); + if ( t4 == (clock_t) -1) e(6); + if (t4 - t3 < CLOCKS_PER_SEC) e(7); + if (tmsbuf.tms_utime < 0) e(8); + if (tmsbuf.tms_stime < 0) e(9); + if (tmsbuf.tms_cutime < 0) e(10); + if (tmsbuf.tms_cstime < 0) e(11); +} + +void test2h() +{ +/* Test getgroups(). */ + + gid_t g[10]; + + subtest = 8; + errno = -8000; + if (getgroups(10, g) != 0) e(1); + if (getgroups(1, g) != 0) e(2); + if (getgroups(0, g) != 0) e(3); +} + + +void sigpip(s) +int s; /* for ANSI */ +{ + sigct++; + cumsig++; +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(4); + } +} + +void e(n) +int n; +{ + int err_num = errno; /* save errno in case printf clobbers it */ + + printf("Subtest %d, error %d errno=%d ", subtest, n, errno); + errno = err_num; /* restore errno, just in case */ + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} diff --git a/test/test20.c b/test/test20.c new file mode 100644 index 000000000..9f0b76c60 --- /dev/null +++ b/test/test20.c @@ -0,0 +1,735 @@ +/* POSIX test program (20). Author: Andy Tanenbaum */ + +/* The following POSIX calls are tested: + * + * opendir() + * readdir() + * rewinddir() + * closedir() + * chdir() + * getcwd() + */ + + +#include <sys/types.h> +#include <sys/stat.h> +#include <dirent.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <utime.h> +#include <stdio.h> + +#define DIR_NULL (DIR*) NULL +#define ITERATIONS 5 +#define MAX_FD 100 /* must be large enough to cause error */ +#define BUF_SIZE PATH_MAX+20 +#define ERR_CODE -1 /* error return */ +#define RD_BUF 200 +#define MAX_ERROR 4 + +char str[] = {"The time has come the walrus said to talk of many things.\n"}; +char str2[] = {"Of ships and shoes and sealing wax, of cabbages and kings.\n"}; +char str3[] = {"Of why the sea is boiling hot and whether pigs have wings\n"}; + +int subtest, errct; + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void test20a, (void)); +_PROTOTYPE(void checkdir, (DIR *dirp, int t)); +_PROTOTYPE(void test20b, (void)); +_PROTOTYPE(void test20c, (void)); +_PROTOTYPE(void test20d, (void)); +_PROTOTYPE(void test20e, (void)); +_PROTOTYPE(void test20f, (void)); +_PROTOTYPE(void test20g, (void)); +_PROTOTYPE(void test20h, (void)); +_PROTOTYPE(void test20i, (void)); +_PROTOTYPE(void test20j, (void)); +_PROTOTYPE(void e, (int n)); +_PROTOTYPE(void quit, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + + int i, m = 0xFFFF; + + sync(); + if (geteuid() == 0 || getuid() == 0) { + printf("Test 20 cannot run as root; test aborted\n"); + exit(1); + } + + if (argc == 2) m = atoi(argv[1]); + printf("Test 20 "); + fflush(stdout); + + system("rm -rf DIR_20; mkdir DIR_20"); + chdir("DIR_20"); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 00001) test20a(); /* test for correct operation */ + if (m & 00002) test20b(); /* test general error handling */ + if (m & 00004) test20c(); /* test for EMFILE error */ + if (m & 00010) test20d(); /* test chdir() and getcwd() */ + if (m & 00020) test20e(); /* test open() */ + if (m & 00040) test20f(); /* test umask(), stat(), fstat() */ + if (m & 00100) test20g(); /* test link() and unlink() */ + if (m & 00200) test20h(); /* test access() */ + if (m & 00400) test20i(); /* test chmod() and chown() */ + if (m & 01000) test20j(); /* test utime() */ + } + quit(); + return(-1); /* impossible */ +} + +void test20a() +{ +/* Subtest 1. Correct operation */ + + int f1, f2, f3, f4, f5; + DIR *dirp; + + /* Remove any residue of previous tests. */ + subtest = 1; + + system("rm -rf foo"); + + /* Create a directory foo with 5 files in it. */ + mkdir("foo", 0777); + if ((f1 = creat("foo/f1", 0666)) < 0) e(1); + if ((f2 = creat("foo/f2", 0666)) < 0) e(2); + if ((f3 = creat("foo/f3", 0666)) < 0) e(3); + if ((f4 = creat("foo/f4", 0666)) < 0) e(4); + if ((f5 = creat("foo/f5", 0666)) < 0) e(5); + + /* Now remove 2 files to create holes in the directory. */ + if (unlink("foo/f2") < 0) e(6); + if (unlink("foo/f4") < 0) e(7); + + /* Close the files. */ + close(f1); + close(f2); + close(f3); + close(f4); + close(f5); + + /* Open the directory. */ + dirp = opendir("./foo"); + if (dirp == DIR_NULL) e(6); + + /* Read the 5 files from it. */ + checkdir(dirp, 2); + + /* Rewind dir and test again. */ + rewinddir(dirp); + checkdir(dirp, 3); + + /* We're done. Close the directory stream. */ + if (closedir(dirp) < 0) e(7); + + /* Remove dir for next time. */ + system("rm -rf foo"); +} + +void checkdir(dirp, t) +DIR *dirp; /* poinrter to directory stream */ +int t; /* subtest number to use */ +{ + + int i, f1, f2, f3, f4, f5, dot, dotdot, subt; + struct dirent *d; + char *s; + + /* Save subtest number */ + subt = subtest; + subtest = t; + + /* Clear the counters. */ + f1 = 0; + f2 = 0; + f3 = 0; + f4 = 0; + f5 = 0; + dot = 0; + dotdot = 0; + + /* Read the directory. It should contain 5 entries, ".", ".." and 3 + * files. */ + for (i = 0; i < 5; i++) { + d = readdir(dirp); + if (d == (struct dirent *) NULL) { + e(1); + subtest = subt; /* restore subtest number */ + return; + } + s = d->d_name; + if (strcmp(s, ".") == 0) dot++; + if (strcmp(s, "..") == 0) dotdot++; + if (strcmp(s, "f1") == 0) f1++; + if (strcmp(s, "f2") == 0) f2++; + if (strcmp(s, "f3") == 0) f3++; + if (strcmp(s, "f4") == 0) f4++; + if (strcmp(s, "f5") == 0) f5++; + } + + /* Check results. */ + d = readdir(dirp); + if (d != (struct dirent *) NULL) e(2); + if (f1 != 1 || f3 != 1 || f5 != 1) e(3); + if (f2 != 0 || f4 != 0) e(4); + if (dot != 1 || dotdot != 1) e(5); + subtest = subt; + return; +} + + +void test20b() +{ +/* Subtest 4. Test error handling. */ + + int fd; + DIR *dirp; + + subtest = 4; + + if (opendir("foo/xyz/---") != DIR_NULL) e(1); + if (errno != ENOENT) e(2); + if (mkdir("foo", 0777) < 0) e(3); + if (chmod("foo", 0) < 0) e(4); + if (opendir("foo/xyz/--") != DIR_NULL) e(5); + if (errno != EACCES) e(6); + if (chmod("foo", 0777) != 0) e(7); + if (rmdir("foo") != 0) e(8); + if ((fd = creat("abc", 0666)) < 0) e(9); + if (close(fd) < 0) e(10); + if (opendir("abc/xyz") != DIR_NULL) e(11); + if (errno != ENOTDIR) e(12); + if ((dirp = opendir(".")) == DIR_NULL) e(13); + if (closedir(dirp) != 0) e(14); + if (unlink("abc") != 0) e(15); + +} + + +void test20c() +{ +/* Subtest 5. See what happens if we open too many directory streams. */ + + int i, j; + DIR *dirp[MAX_FD]; + + subtest = 5; + + for (i = 0; i < MAX_FD; i++) { + dirp[i] = opendir("."); + if (dirp[i] == (DIR *) NULL) { + /* We have hit the limit. */ + if (errno != EMFILE && errno != ENOMEM) e(1); + for (j = 0; j < i; j++) { + if (closedir(dirp[j]) != 0) e(2); /* close */ + } + return; + } + } + + /* Control should never come here. This is an error. */ + e(3); + for (i = 0; i < MAX_FD; i++) closedir(dirp[i]); /* don't check */ +} + +void test20d() +{ +/* Test chdir and getcwd(). */ + + int fd; + char *s; + char base[BUF_SIZE], buf2[BUF_SIZE], tmp[BUF_SIZE]; + + subtest = 6; + + if (getcwd(base, BUF_SIZE) == (char *) NULL) e(1); /* get test dir's path */ + if (system("rm -rf Dir") != 0) e(2); /* remove residue of previous test */ + if (mkdir("Dir", 0777) < 0) e(3); /* create directory called "Dir" */ + + /* Change to Dir and verify that it worked. */ + if (chdir("Dir") < 0) e(4); /* go to Dir */ + s = getcwd(buf2, BUF_SIZE); /* get full path of Dir */ + if (s == (char *) NULL) e(5); /* check for error return */ + if (s != buf2) e(6); /* if successful, first arg is returned */ + strcpy(tmp, base); /* concatenate base name and "/Dir" */ + strcat(tmp, "/"); + strcat(tmp, "Dir"); + if (strcmp(tmp, s) != 0) e(7); + + /* Change to ".." and verify that it worked. */ + if (chdir("..") < 0) e(8); + if (getcwd(buf2, BUF_SIZE) != buf2) e(9); + if (strcmp(buf2, base) != 0) e(10); + + /* Now make calls that do nothing, but do it in a strange way. */ + if (chdir("Dir/..") < 0) e(11); + if (getcwd(buf2, BUF_SIZE) != buf2) e(12); + if (strcmp(buf2, base) != 0) e(13); + + if (chdir("Dir/../Dir/..") < 0) e(14); + if (getcwd(buf2, BUF_SIZE) != buf2) e(15); + if (strcmp(buf2, base) != 0) e(16); + + if (chdir("Dir/../Dir/../Dir/../Dir/../Dir/../Dir/../Dir/..") < 0) e(17); + if (getcwd(buf2, BUF_SIZE) != buf2) e(18); + if (strcmp(buf2, base) != 0) e(19); + + /* Make Dir unreadable and unsearchable. Check error message. */ + if (chmod("Dir", 0) < 0) e(20); + if (chdir("Dir") >= 0) e(21); + if (errno != EACCES) e(22); + + /* Check error message for bad path. */ + if (chmod("Dir", 0777) < 0) e(23); + if (chdir("Dir/x/y") != ERR_CODE) e(24); + if (errno != ENOENT) e(25); + + if ( (fd=creat("Dir/x", 0777)) < 0) e(26); + if (close(fd) != 0) e(27); + if (chdir("Dir/x/y") != ERR_CODE) e(28); + if (errno != ENOTDIR) e(29); + + /* Check empty string. */ + if (chdir("") != ERR_CODE) e(30); + if (errno != ENOENT) e(31); + + /* Remove the directory. */ + if (unlink("Dir/x") != 0) e(32); + if (system("rmdir Dir") != 0) e(33); +} + + +void test20e() +{ +/* Test open. */ + + int fd, bytes, bytes2; + char buf[RD_BUF]; + + subtest = 7; + + unlink("T20"); /* get rid of it in case it exists */ + + /* Create a test file. */ + bytes = strlen(str); + bytes2 = strlen(str2); + if ((fd = creat("T20", 0777)) < 0) e(1); + if (write(fd, str, bytes) != bytes) e(2); /* T20 now has 'bytes' bytes */ + if (close(fd) != 0) e(3); + + /* Test opening a file with O_RDONLY. */ + if ((fd = open("T20", O_RDONLY)) < 0) e(4); + buf[0] = '\0'; + if (read(fd, buf, RD_BUF) != bytes) e(5); + if (strncmp(buf, str, bytes) != 0) e(6); + if (close(fd) < 0) e(7); + + /* Test the same thing, only with O_RDWR now. */ + if ((fd = open("T20", O_RDWR)) < 0) e(8); + buf[0] = '\0'; + if (read(fd, buf, RD_BUF) != bytes) e(9); + if (strncmp(buf, str, bytes) != 0) e(10); + if (close(fd) < 0) e(11); + + /* Try opening and reading with O_WRONLY. It should fail. */ + if ((fd = open("T20", O_WRONLY)) < 0) e(12); + buf[0] = '\0'; + if (read(fd, buf, RD_BUF) >= 0) e(13); + if (close(fd) != 0) e(14); + + /* Test O_APPEND. */ + if ((fd = open("T20", O_RDWR | O_APPEND)) < 0) e(15); + if (lseek(fd, 0L, SEEK_SET) < 0) e(16); /* go to start of file */ + if ( write(fd, str2, bytes2) != bytes2) e(17); /* write at start of file */ + if (lseek(fd, 0L, SEEK_SET) < 0) e(18); /* go back to start again */ + if (read(fd, buf, RD_BUF) != bytes + bytes2) e(19); /* read whole file */ + if (strncmp(buf, str, bytes) != 0) e(20); + if (close(fd) != 0) e(21); + + /* Get rid of the file. */ + if (unlink("T20") < 0) e(22); +} + +void test20f() +{ +/* Test stat, fstat, umask. */ + int i, fd; + mode_t m1; + struct stat stbuf1, stbuf2; + time_t t, t1; + + subtest = 8; + + m1 = umask(~0777); + if (system("rm -rf foo xxx") != 0) e(1); + if ((fd = creat("foo", 0777)) < 0) e(2); + if (stat("foo", &stbuf1) < 0) e(3); + if (fstat(fd, &stbuf2) < 0) e(4); + if (stbuf1.st_mode != stbuf2.st_mode) e(5); + if (stbuf1.st_ino != stbuf2.st_ino) e(6); + if (stbuf1.st_dev != stbuf2.st_dev) e(7); + if (stbuf1.st_nlink != stbuf2.st_nlink) e(8); + if (stbuf1.st_uid != stbuf2.st_uid) e(9); + if (stbuf1.st_gid != stbuf2.st_gid) e(10); + if (stbuf1.st_size != stbuf2.st_size) e(11); + if (stbuf1.st_atime != stbuf2.st_atime) e(12); + if (stbuf1.st_mtime != stbuf2.st_mtime) e(13); + if (stbuf1.st_ctime != stbuf2.st_ctime) e(14); + + if (!S_ISREG(stbuf1.st_mode)) e(15); + if (S_ISDIR(stbuf1.st_mode)) e(16); + if (S_ISCHR(stbuf1.st_mode)) e(17); + if (S_ISBLK(stbuf1.st_mode)) e(18); + if (S_ISFIFO(stbuf1.st_mode)) e(19); + + if ((stbuf1.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) != 0777) e(20); + if (stbuf1.st_nlink != 1) e(21); + if (stbuf1.st_uid != getuid()) e(22); + if (stbuf1.st_gid != getgid()) e(23); + if (stbuf1.st_size != 0L) e(24); + + /* First unlink, then close -- harder test */ + if (unlink("foo") < 0) e(25); + if (close(fd) < 0) e(26); + + /* Now try umask a bit more. */ + fd = 0; + if ((i = umask(~0704)) != 0) e(27); + if ((fd = creat("foo", 0777)) < 0) e(28); + if (stat("foo", &stbuf1) < 0) e(29); + if (fstat(fd, &stbuf2) < 0) e(30); + if (stbuf1.st_mode != stbuf2.st_mode) e(31); + if ((stbuf1.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)) != 0704) e(32); + + /* First unlink, then close -- harder test */ + if (unlink("foo") < 0) e(33); + if (close(fd) < 0) e(34); + if (umask(m1) != 073) e(35); + + /* Test some errors. */ + if (system("mkdir Dir; date >Dir/x; chmod 666 Dir") != 0) e(36); + if (stat("Dir/x", &stbuf1) >= 0) e(37); + if (errno != EACCES) e(38); + if (stat("......", &stbuf1) >= 0) e(39); + if (errno != ENOENT) e(40); + if (stat("", &stbuf1) >= 0) e(41); + if (errno != ENOENT) e(42); + if (stat("xxx/yyy/zzz", &stbuf1) >= 0) e(43); + if (errno != ENOENT) e(44); + if (fstat(10000, &stbuf1) >= 0) e(45); + if (errno != EBADF) e(46); + if (chmod("Dir", 0777) != 0) e(47); + if (system("rm -rf foo Dir") != 0) e(48); + + /* See if time looks reasonable. */ + errno = 0; + t = time(&t1); /* current time */ + if (t < 650000000L) e(49); /* 650000000 is Sept. 1990 */ + unlink("T20f"); + fd = creat("T20f", 0777); + if (fd < 0) e(50); + if (close(fd) < 0) e(51); + if (stat("T20f", &stbuf1) < 0) e(52); + if (stbuf1.st_mtime < t) e(53); + if (unlink("T20f") < 0) e(54); +} + + +void test20g() +{ +/* Test link and unlink. */ + int i, fd; + struct stat stbuf; + char name[4]; + + subtest = 9; + + if (system("rm -rf L? L?? Dir; mkdir Dir") != 0) e(1); + if ( (fd = creat("L1", 0666)) < 0) e(2); + if (fstat(fd, &stbuf) != 0) e(3); + if (stbuf.st_nlink != 1) e(4); + if (link("L1", "L2") != 0) e(5); + if (fstat(fd, &stbuf) != 0) e(6); + if (stbuf.st_nlink != 2) e(7); + if (unlink("L2") != 0) e(8); + if (link("L1", "L2") != 0) e(9); + if (unlink("L1") != 0) e(10); + if (close(fd) != 0) e(11); + + /* L2 exists at this point. */ + if ( (fd = creat("L1", 0666)) < 0) e(12); + if (stat("L1", &stbuf) != 0) e(13); + if (stbuf.st_nlink != 1) e(14); + if (link("L1", "Dir/L2") != 0) e(15); + if (stat("L1", &stbuf) != 0) e(16); + if (stbuf.st_nlink != 2) e(17); + if (stat("Dir/L2", &stbuf) != 0) e(18); + if (stbuf.st_nlink != 2) e(19); + + /* L1, L2, and Dir/L2 exist at this point. */ + if (unlink("Dir/L2") != 0) e(20); + if (link("L1", "Dir/L2") != 0) e(21); + if (unlink("L1") != 0) e(22); + if (close(fd) != 0) e(23); + if (chdir("Dir") != 0) e(24); + if (unlink("L2") != 0) e(25); + if (chdir("..") != 0) e(26); + + /* L2 exists at this point. Test linking to unsearchable dir. */ + if (link("L2", "Dir/L2") != 0) e(27); + if (chmod("Dir", 0666) != 0) e(27); + if (link("L2", "Dir/L2") != -1) e(28); + if (errno != EACCES) e(29); + errno = 0; + if (link("Dir/L2", "L3") != -1) e(30); + if (errno != EACCES) e(31); + if (chmod("Dir", 0777) != 0) e(32); + if (unlink("Dir/L2") != 0) e(33); + if (unlink("L3") == 0) e(34); + + /* L2 exists at this point. Test linking to unwriteable dir. */ + if (chmod("Dir", 0555) != 0) e(35); + if (link("L2", "Dir/L2") != -1) e(36); + if (errno != EACCES) e(37); + if (chmod("Dir", 0777) != 0) e(38); + + /* L2 exists at this point. Test linking mode 0 file. */ + if (chmod("L2", 0) != 0) e(39); + if (link("L2", "L3") != 0) e(40); + if (stat("L3", &stbuf) != 0) e(41); + if (stbuf.st_nlink != 2) e(42); + if (unlink("L2") != 0) e(43); + + /* L3 exists at this point. Test linking to an existing file. */ + if ( (fd = creat("L1", 0666)) < 0) e(44); + if (link("L1", "L3") != -1) e(45); + if (errno != EEXIST) e(46); + errno = 0; + if (link("L1", "L1") != -1) e(47); + if (errno != EEXIST) e(48); + if (unlink("L3") != 0) e(49); + + /* L1 exists at this point. Test creating too many links. */ +if (LINK_MAX > 127) { + printf("[skip] "); /* takes too many resources */ +} else { + name[0] = 'L'; + name[1] = 'x'; + name[2] = 1; + name[3] = 0; + for (i = 2; i <= LINK_MAX; i++) { + if (link("L1", name) != 0) e(50); + name[2]++; + } + if (stat("L1", &stbuf) != 0) e(51); + if (stbuf.st_nlink != LINK_MAX) e(52); + if (link("L1", "L2") != -1) e(53); + if (errno != EMLINK) e(54); + name[2] = 1; + for (i = 2; i <= LINK_MAX; i++) { + if (unlink(name) != 0) e(55); + name[2]++; + } +} + if (stat("L1", &stbuf) != 0) e(56); + if (stbuf.st_nlink != 1) e(57); + + /* L1 exists. Test ENOENT. */ + errno = 0; + if (link("xx/L1", "L2") != -1) e(58); + if (errno != ENOENT) e(59); + errno = 0; + if (link("L1", "xx/L2") != -1) e(60); + if (errno != ENOENT) e(61); + errno = 0; + if (link("L4", "L5") != -1) e(62); + if (errno != ENOENT) e(63); + errno = 0; + if (link("", "L5") != -1) e(64); + if (errno != ENOENT) e(65); + errno = 0; + if (link("L1", "") != -1) e(66); + if (errno != ENOENT) e(67); + + /* L1 exists. Test ENOTDIR. */ + errno = 0; + if (link("/dev/tty/x", "L2") != -1) e(68); + if (errno != ENOTDIR) e(69); + + /* L1 exists. Test EPERM. */ + if (link(".", "L2") != -1) e(70); + if (errno != EPERM) e(71); + + /* L1 exists. Test unlink. */ + if (link("L1", "Dir/L1") != 0) e(72); + if (chmod("Dir", 0666) != 0) e(73); + if (unlink("Dir/L1") != -1) e(74); + if (errno != EACCES) e(75); + errno = 0; + if (chmod("Dir", 0555) != 0) e(76); + if (unlink("Dir/L1") != -1) e(77); + if (errno != EACCES) e(78); + + if (unlink("L7") != -1) e(79); + if (errno != ENOENT) e(80); + errno = 0; + if (unlink("") != -1) e(81); + if (errno != ENOENT) e(82); + + if (unlink("Dir/L1/L2") != -1) e(83); + if (errno != ENOTDIR) e(84); + + if (chmod("Dir", 0777) != 0) e(85); + if (unlink("Dir/L1") != 0) e(86); + if (unlink("Dir") != -1) e(87); + if (errno != EPERM) e(88); + if (unlink("L1") != 0) e(89); + if (system("rm -rf Dir") != 0) e(90); + if (close(fd) != 0) e(91); +} + +void test20h() +{ +/* Test access. */ + + int fd; + + subtest = 10; + system("rm -rf A1"); + if ( (fd = creat("A1", 0777)) < 0) e(1); + if (close(fd) != 0) e(2); + if (access("A1", R_OK) != 0) e(3); + if (access("A1", W_OK) != 0) e(4); + if (access("A1", X_OK) != 0) e(5); + if (access("A1", (R_OK|W_OK|X_OK)) != 0) e(6); + + if (chmod("A1", 0400) != 0) e(7); + if (access("A1", R_OK) != 0) e(8); + if (access("A1", W_OK) != -1) e(9); + if (access("A1", X_OK) != -1) e(10); + if (access("A1", (R_OK|W_OK|X_OK)) != -1) e(11); + + if (chmod("A1", 0077) != 0) e(12); + if (access("A1", R_OK) != -1) e(13); + if (access("A1", W_OK) != -1) e(14); + if (access("A1", X_OK) != -1) e(15); + if (access("A1", (R_OK|W_OK|X_OK)) != -1) e(16); + if (errno != EACCES) e(17); + + if (access("", R_OK) != -1) e(18); + if (errno != ENOENT) e(19); + if (access("./A1/x", R_OK) != -1) e(20); + if (errno != ENOTDIR) e(21); + + if (unlink("A1") != 0) e(22); +} + +void test20i() +{ +/* Test chmod. */ + + int fd, i; + struct stat stbuf; + + subtest = 11; + system("rm -rf A1"); + if ( (fd = creat("A1", 0777)) < 0) e(1); + + for (i = 0; i < 511; i++) { + if (chmod("A1", i) != 0) e(100+i); + if (fstat(fd, &stbuf) != 0) e(200+i); + if ( (stbuf.st_mode&(S_IRWXU|S_IRWXG|S_IRWXO)) != i) e(300+i); + } + if (close(fd) != 0) e(2); + + if (chmod("A1/x", 0777) != -1) e(3); + if (errno != ENOTDIR) e(4); + if (chmod("Axxx", 0777) != -1) e(5); + if (errno != ENOENT) e(6); + errno = 0; + if (chmod ("", 0777) != -1) e(7); + if (errno != ENOENT) e(8); + + /* Now perform limited chown tests. These should work even as non su */ + i = getuid(); +/* DEBUG -- Not yet implemented + if (chown("A1", i, 0) != 0) e(9); + if (chown("A1", i, 1) != 0) e(10); + if (chown("A1", i, 2) != 0) e(11); + if (chown("A1", i, 3) != 0) e(12); + if (chown("A1", i, 4) != 0) e(13); + if (chown("A1", i, 0) != 0) e(14); +*/ + + if (unlink("A1") != 0) e(9); +} + +void test20j() +{ +/* Test utime. */ + + int fd; + time_t tloc; + struct utimbuf times; + struct stat stbuf; + + subtest = 12; + if (system("rm -rf A2") != 0) e(1); + if ( (fd = creat("A2", 0666)) < 0) e(2); + times.modtime = 100; + if (utime("A2", ×) != 0) e(3); + if (stat("A2", &stbuf) != 0) e(4); + if (stbuf.st_mtime != 100) e(5); + + tloc = time((time_t *)NULL); /* get current time */ + times.modtime = tloc; + if (utime("A2", ×) != 0) e(6); + if (stat("A2", &stbuf) != 0) e(7); + if (stbuf.st_mtime != tloc) e(8); + if (close(fd) != 0) e(9); + if (unlink("A2") != 0) e(10); +} + +void e(n) +int n; +{ + int err_num = errno; /* save errno in case printf clobbers it */ + + printf("Subtest %d, error %d errno=%d ", subtest, n, errno); + fflush(stdout); /* stdout and stderr are mixed horribly */ + errno = err_num; /* restore errno, just in case */ + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test21.c b/test/test21.c new file mode 100644 index 000000000..6d0863f3b --- /dev/null +++ b/test/test21.c @@ -0,0 +1,691 @@ +/* POSIX test program (21). Author: Andy Tanenbaum */ + +/* The following POSIX calls are tested: + * + * rename(), mkdir(), rmdir() + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +#define ITERATIONS 1 +#define MAX_ERROR 4 + +int subtest, errct; + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void test21a, (void)); +_PROTOTYPE(void test21b, (void)); +_PROTOTYPE(void test21c, (void)); +_PROTOTYPE(void test21d, (void)); +_PROTOTYPE(void test21e, (void)); +_PROTOTYPE(void test21f, (void)); +_PROTOTYPE(void test21g, (void)); +_PROTOTYPE(void test21h, (void)); +_PROTOTYPE(void test21i, (void)); +_PROTOTYPE(void test21k, (void)); +_PROTOTYPE(void test21l, (void)); +_PROTOTYPE(void test21m, (void)); +_PROTOTYPE(void test21n, (void)); +_PROTOTYPE(void test21o, (void)); +_PROTOTYPE(int get_link, (char *name)); +_PROTOTYPE(void e, (int n)); +_PROTOTYPE(void quit, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + + int i, m = 0xFFFF; + + sync(); + if (geteuid() == 0 || getuid() == 0) { + printf("Test 21 cannot run as root; test aborted\n"); + exit(1); + } + + if (argc == 2) m = atoi(argv[1]); + printf("Test 21 "); + fflush(stdout); + + system("rm -rf DIR_21; mkdir DIR_21"); + chdir("DIR_21"); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 00001) test21a(); + if (m & 00002) test21b(); + if (m & 00004) test21c(); + if (m & 00010) test21d(); + if (m & 00020) test21e(); + if (m & 00040) test21f(); + if (m & 01000) test21g(); + if (m & 00200) test21h(); + if (m & 00400) test21i(); + if (m & 01000) test21k(); + if (m & 02000) test21l(); + if (m & 04000) test21m(); + if (m & 010000) test21n(); + if (m & 020000) test21o(); + } + quit(); + return(-1); /* impossible */ +} + +void test21a() +{ +/* Test rename(). */ + + int fd, fd2; + char buf[PATH_MAX+1], buf1[PATH_MAX+1], buf2[PATH_MAX+1]; + struct stat stat1, stat2; + + subtest = 1; + + unlink("A1"); /* get rid of it if it exists */ + unlink("A2"); /* get rid of it if it exists */ + unlink("A3"); /* get rid of it if it exists */ + unlink("A4"); /* get rid of it if it exists */ + unlink("A5"); /* get rid of it if it exists */ + unlink("A6"); /* get rid of it if it exists */ + unlink("A7"); /* get rid of it if it exists */ + + /* Basic test. Rename A1 to A2 and then A2 to A3. */ + if ( (fd=creat("A1", 0666)) < 0) e(1); + if (write(fd, buf, 20) != 20) e(2); + if (close(fd) < 0) e(3); + if (rename("A1", "A2") < 0) e(4); + if ( (fd=open("A2", O_RDONLY)) < 0) e(5); + if (rename("A2", "A3") < 0) e(6); + if ( (fd2=open("A3", O_RDONLY)) < 0) e(7); + if (close(fd) != 0) e(8); + if (close(fd2) != 0) e(9); + if (unlink("A3") != 0) e(10); + + /* Now get the absolute path name of the current directory using getcwd() + * and use it to test RENAME using different combinations of relative and + * absolute path names. + */ + if (getcwd(buf, PATH_MAX) == (char *) NULL) e(11); + if ( (fd = creat("A4", 0666)) < 0) e(12); + if (write(fd, buf, 30) != 30) e(13); + if (close(fd) != 0) e(14); + strcpy(buf1, buf); + strcat(buf1, "/A4"); + if (rename(buf1, "A5") != 0) e(15); /* rename(absolute, relative) */ + if (access("A5", 6) != 0) e(16); /* use access to see if file exists */ + strcpy(buf2, buf); + strcat(buf2, "/A6"); + if (rename("A5", buf2) != 0) e(17); /* rename(relative, absolute) */ + if (access("A6", 6) != 0) e(18); + if (access(buf2, 6) != 0) e(19); + strcpy(buf1, buf); + strcat(buf1, "/A6"); + strcpy(buf2, buf); + strcat(buf2, "/A7"); + if (rename(buf1, buf2) != 0) e(20); /* rename(absolute, absolute) */ + if (access("A7", 6) != 0) e(21); + if (access(buf2, 6) != 0) e(22); + + /* Try renaming using names like "./A8" */ + if (rename("A7", "./A8") != 0) e(23); + if (access("A8", 6) != 0) e(24); + if (rename("./A8", "A9") != 0) e(25); + if (access("A9", 6) != 0) e(26); + if (rename("./A9", "./A10") != 0) e(27); + if (access("A10", 6) != 0) e(28); + if (access("./A10", 6) != 0) e(29); + if (unlink("A10") != 0) e(30); + + /* Now see if directories can be renamed. */ + if (system("rm -rf ?uzzy scsi") != 0) e(31); + if (system("mkdir fuzzy") != 0) e(32); + if (rename("fuzzy", "wuzzy") != 0) e(33); + if ( (fd=creat("wuzzy/was_a_bear", 0666)) < 0) e(34); + if (access("wuzzy/was_a_bear", 6) != 0) e(35); + if (unlink("wuzzy/was_a_bear") != 0) e(36); + if (close(fd) != 0) e(37); + if (rename("wuzzy", "buzzy") != 0) e(38); + if (system("rmdir buzzy") != 0) e(39); + + /* Now start testing the case that 'new' exists. */ + if ( (fd = creat("horse", 0666)) < 0) e(40); + if ( (fd2 = creat("sheep", 0666)) < 0) e(41); + if (write(fd, buf, PATH_MAX) != PATH_MAX) e(42); + if (write(fd2, buf, 23) != 23) e(43); + if (stat("horse", &stat1) != 0) e(44); + if (rename("horse", "sheep") != 0) e(45); + if (stat("sheep", &stat2) != 0) e(46); + if (stat1.st_dev != stat2.st_dev) e(47); + if (stat1.st_ino != stat2.st_ino) e(48); + if (stat2.st_size != PATH_MAX) e(49); + if (access("horse", 6) == 0) e(50); + if (close(fd) != 0) e(51); + if (close(fd2) != 0) e(52); + if (rename("sheep", "sheep") != 0) e(53); + if (unlink("sheep") != 0) e(54); + + /* Now try renaming something to a directory that already exists. */ + if (system("mkdir fuzzy wuzzy") != 0) e(55); + if ( (fd = creat("fuzzy/was_a_bear", 0666)) < 0) e(56); + if (close(fd) != 0) e(57); + if (rename("fuzzy", "wuzzy") != 0) e(58); /* 'new' is empty dir */ + if (system("mkdir scsi") != 0) e(59); + if (rename("scsi", "wuzzy") == 0) e(60); /* 'new' is full dir */ + if (errno != EEXIST && errno != ENOTEMPTY) e(61); + + /* Test 14 character names--always tricky. */ + if (rename("wuzzy/was_a_bear", "wuzzy/was_not_a_bear") != 0) e(62); + if (access("wuzzy/was_not_a_bear", 6) != 0) e(63); + if (rename("wuzzy/was_not_a_bear", "wuzzy/was_not_a_duck") != 0) e(64); + if (access("wuzzy/was_not_a_duck", 6) != 0) e(65); + if (rename("wuzzy/was_not_a_duck", "wuzzy/was_a_bird") != 0) e(65); + if (access("wuzzy/was_a_bird", 6) != 0) e(66); + + /* Test moves between directories. */ + if (rename("wuzzy/was_a_bird", "beast") != 0) e(67); + if (access("beast", 6) != 0) e(68); + if (rename("beast", "wuzzy/was_a_cat") != 0) e(69); + if (access("wuzzy/was_a_cat", 6) != 0) e(70); + + /* Test error conditions. 'scsi' and 'wuzzy/was_a_cat' exist now. */ + if (rename("wuzzy/was_a_cat", "wuzzy/was_a_dog") != 0) e(71); + if (access("wuzzy/was_a_dog", 6) != 0) e(72); + if (chmod("wuzzy", 0) != 0) e(73); + + errno = 0; + if (rename("wuzzy/was_a_dog", "wuzzy/was_a_pig") != -1) e(74); + if (errno != EACCES) e(75); + + errno = 0; + if (rename("wuzzy/was_a_dog", "doggie") != -1) e(76); + if (errno != EACCES) e(77); + + errno = 0; + if ( (fd = creat("beast", 0666)) < 0) e(78); + if (close(fd) != 0) e(79); + if (rename("beast", "wuzzy/was_a_twit") != -1) e(80); + if (errno != EACCES) e(81); + + errno = 0; + if (rename("beast", "wuzzy") != -1) e(82); + if (errno != EISDIR) e(83); + + errno = 0; + if (rename("beest", "baste") != -1) e(84); + if (errno != ENOENT) e(85); + + errno = 0; + if (rename("wuzzy", "beast") != -1) e(86); + if (errno != ENOTDIR) e(87); + + /* Test prefix rule. */ + errno = 0; + if (chmod("wuzzy", 0777) != 0) e(88); + if (unlink("wuzzy/was_a_dog") != 0) e(89); + strcpy(buf1, buf); + strcat(buf1, "/wuzzy"); + if (rename(buf, buf1) != -1) e(90); + if (errno != EINVAL) e(91); + + if (system("rm -rf wuzzy beast scsi") != 0) e(92); +} + + + +void test21b() +{ +/* Test mkdir() and rmdir(). */ + + int i; + char name[3]; + struct stat statbuf; + + subtest = 2; + + /* Simple stuff. */ + if (mkdir("D1", 0700) != 0) e(1); + if (stat("D1", &statbuf) != 0) e(2); + if (!S_ISDIR(statbuf.st_mode)) e(3); + if ( (statbuf.st_mode & 0777) != 0700) e(4); + if (rmdir("D1") != 0) e(5); + + /* Make and remove 40 directories. By doing so, the directory has to + * grow to 2 blocks. That presents plenty of opportunity for bugs. + */ + name[0] = 'D'; + name[2] = '\0'; + for (i = 0; i < 40; i++) { + name[1] = 'A' + i; + if (mkdir(name, 0700 + i%7) != 0) e(10+i); /* for simplicity */ + } + for (i = 0; i < 40; i++) { + name[1] = 'A' + i; + if (rmdir(name) != 0) e(50+i); + } +} + +void test21c() +{ +/* Test mkdir() and rmdir(). */ + + subtest = 3; + + if (mkdir("D1", 0777) != 0) e(1); + if (mkdir("D1/D2", 0777) != 0) e(2); + if (mkdir("D1/D2/D3", 0777) != 0) e(3); + if (mkdir("D1/D2/D3/D4", 0777) != 0) e(4); + if (mkdir("D1/D2/D3/D4/D5", 0777) != 0) e(5); + if (mkdir("D1/D2/D3/D4/D5/D6", 0777) != 0) e(6); + if (mkdir("D1/D2/D3/D4/D5/D6/D7", 0777) != 0) e(7); + if (mkdir("D1/D2/D3/D4/D5/D6/D7/D8", 0777) != 0) e(8); + if (mkdir("D1/D2/D3/D4/D5/D6/D7/D8/D9", 0777) != 0) e(9); + if (access("D1/D2/D3/D4/D5/D6/D7/D8/D9", 7) != 0) e(10); + if (rmdir("D1/D2/D3/D4/D5/D6/D7/D8/D9") != 0) e(11); + if (rmdir("D1/D2/D3/D4/D5/D6/D7/D8") != 0) e(12); + if (rmdir("D1/D2/D3/D4/D5/D6/D7") != 0) e(13); + if (rmdir("D1/D2/D3/D4/D5/D6") != 0) e(11); + if (rmdir("D1/D2/D3/D4/D5") != 0) e(13); + if (rmdir("D1/D2/D3/D4") != 0) e(14); + if (rmdir("D1/D2/D3") != 0) e(15); + if (rmdir("D1/D2") != 0) e(16); + if (rmdir("D1") != 0) e(17); +} + +void test21d() +{ +/* Test making directories with files and directories in them. */ + + int fd1, fd2, fd3, fd4, fd5, fd6, fd7, fd8, fd9; + + subtest = 4; + + if (mkdir("D1", 0777) != 0) e(1); + if (mkdir("D1/D2", 0777) != 0) e(2); + if (mkdir("./D1/D3", 0777) != 0) e(3); + if (mkdir("././D1/D4", 0777) != 0) e(4); + if ( (fd1 = creat("D1/D2/x", 0700)) < 0) e(5); + if ( (fd2 = creat("D1/D2/y", 0700)) < 0) e(6); + if ( (fd3 = creat("D1/D2/z", 0700)) < 0) e(7); + if ( (fd4 = creat("D1/D3/x", 0700)) < 0) e(8); + if ( (fd5 = creat("D1/D3/y", 0700)) < 0) e(9); + if ( (fd6 = creat("D1/D3/z", 0700)) < 0) e(10); + if ( (fd7 = creat("D1/D4/x", 0700)) < 0) e(11); + if ( (fd8 = creat("D1/D4/y", 0700)) < 0) e(12); + if ( (fd9 = creat("D1/D4/z", 0700)) < 0) e(13); + if (unlink("D1/D2/z") != 0) e(14); + if (unlink("D1/D2/y") != 0) e(15); + if (unlink("D1/D2/x") != 0) e(16); + if (unlink("D1/D3/x") != 0) e(17); + if (unlink("D1/D3/z") != 0) e(18); + if (unlink("D1/D3/y") != 0) e(19); + if (unlink("D1/D4/y") != 0) e(20); + if (unlink("D1/D4/z") != 0) e(21); + if (unlink("D1/D4/x") != 0) e(22); + if (rmdir("D1/D2") != 0) e(23); + if (rmdir("D1/D3") != 0) e(24); + if (rmdir("D1/D4") != 0) e(25); + if (rmdir("D1") != 0) e(26); + if (close(fd1) != 0) e(27); + if (close(fd2) != 0) e(28); + if (close(fd3) != 0) e(29); + if (close(fd4) != 0) e(30); + if (close(fd5) != 0) e(31); + if (close(fd6) != 0) e(32); + if (close(fd7) != 0) e(33); + if (close(fd8) != 0) e(34); + if (close(fd9) != 0) e(35); + +} + + +void test21e() +{ +/* Test error conditions. */ + + subtest = 5; + + if (mkdir("D1", 0677) != 0) e(1); + errno = 0; + if (mkdir("D1/D2", 0777) != -1) e(2); + if (errno != EACCES) e(3); + if (chmod ("D1", 0577) != 0) e(4); + errno = 0; + if (mkdir("D1/D2", 0777) != -1) e(5); + if (errno != EACCES) e(6); + if (chmod ("D1", 0777) != 0) e(7); + errno = 0; + if (mkdir("D1", 0777) != -1) e(8); + if (errno != EEXIST) e(9); +#if NAME_MAX == 14 + if (mkdir("D1/ABCDEFGHIJKLMNOPQRSTUVWXYZ", 0777) != 0) e(10); + if (access("D1/ABCDEFGHIJKLMN", 7 ) != 0) e(11); + if (rmdir("D1/ABCDEFGHIJKLMNOPQ") != 0) e(12); + if (access("D1/ABCDEFGHIJKLMN", 7 ) != -1) e(13); +#endif + errno = 0; + if (mkdir("D1/D2/x", 0777) != -1) e(14); + if (errno != ENOENT) e(15); + + /* A particularly nasty test is when the parent has mode 0. Although + * this is unlikely to work, it had better not muck up the file system + */ + if (mkdir("D1/D2", 0777) != 0) e(16); + if (chmod("D1", 0) != 0) e(17); + errno = 0; + if (rmdir("D1/D2") != -1) e(18); + if (errno != EACCES) e(19); + if (chmod("D1", 0777) != 0) e(20); + if (rmdir("D1/D2") != 0) e(21); + if (rmdir("D1") != 0) e(22); +} + +void test21f() +{ +/* The rename() function affects the link count of all the files and + * directories it goes near. Test to make sure it gets everything ok. + * There are four cases: + * + * 1. rename("d1/file1", "d1/file2"); - rename file without moving it + * 2. rename("d1/file1", "d2/file2"); - move a file to another dir + * 3. rename("d1/dir1", "d2/dir2"); - rename a dir without moving it + * 4. rename("d1/dir1", "d2/dir2"); - move a dir to another dir + * + * Furthermore, a distinction has to be made when the target file exists + * and when it does not exist, giving 8 cases in all. + */ + + int fd, D1_before, D1_after, x_link, y_link; + + /* Test case 1: renaming a file within the same directory. */ + subtest = 6; + if (mkdir("D1", 0777) != 0) e(1); + if ( (fd = creat("D1/x", 0777)) < 0) e(2); + if (close(fd) != 0) e(3); + D1_before = get_link("D1"); + x_link = get_link("D1/x"); + if (rename("D1/x", "D1/y") != 0) e(4); + y_link = get_link("D1/y"); + D1_after = get_link("D1"); + if (D1_before != 2) e(5); + if (D1_after != 2) e(6); + if (x_link != 1) e(7); + if (y_link != 1) e(8); + if (access("D1/y", 7) != 0) e(9); + if (unlink("D1/y") != 0) e(10); + if (rmdir("D1") != 0) e(11); +} + + +void test21g() +{ + int fd, D1_before, D1_after, D2_before, D2_after, x_link, y_link; + + /* Test case 2: move a file to a new directory. */ + subtest = 7; + if (mkdir("D1", 0777) != 0) e(1); + if (mkdir("D2", 0777) != 0) e(2); + if ( (fd = creat("D1/x", 0777)) < 0) e(3); + if (close(fd) != 0) e(4); + D1_before = get_link("D1"); + D2_before = get_link("D2"); + x_link = get_link("D1/x"); + if (rename("D1/x", "D2/y") != 0) e(5); + y_link = get_link("D2/y"); + D1_after = get_link("D1"); + D2_after = get_link("D2"); + if (D1_before != 2) e(6); + if (D2_before != 2) e(7); + if (D1_after != 2) e(8); + if (D2_after != 2) e(9); + if (x_link != 1) e(10); + if (y_link != 1) e(11); + if (access("D2/y", 7) != 0) e(12); + if (unlink("D2/y") != 0) e(13); + if (rmdir("D1") != 0) e(14); + if (rmdir("D2") != 0) e(15); +} + +void test21h() +{ + int D1_before, D1_after, x_link, y_link; + + /* Test case 3: renaming a directory within the same directory. */ + subtest = 8; + if (mkdir("D1", 0777) != 0) e(1); + if (mkdir("D1/X", 0777) != 0) e(2); + D1_before = get_link("D1"); + x_link = get_link("D1/X"); + if (rename("D1/X", "D1/Y") != 0) e(3); + y_link = get_link("D1/Y"); + D1_after = get_link("D1"); + if (D1_before != 3) e(4); + if (D1_after != 3) e(5); + if (x_link != 2) e(6); + if (y_link != 2) e(7); + if (access("D1/Y", 7) != 0) e(8); + if (rmdir("D1/Y") != 0) e(9); + if (get_link("D1") != 2) e(10); + if (rmdir("D1") != 0) e(11); +} + +void test21i() +{ + int D1_before, D1_after, D2_before, D2_after, x_link, y_link; + + /* Test case 4: move a directory to a new directory. */ + subtest = 9; + if (mkdir("D1", 0777) != 0) e(1); + if (mkdir("D2", 0777) != 0) e(2); + if (mkdir("D1/X", 0777) != 0) e(3); + D1_before = get_link("D1"); + D2_before = get_link("D2"); + x_link = get_link("D1/X"); + if (rename("D1/X", "D2/Y") != 0) e(4); + y_link = get_link("D2/Y"); + D1_after = get_link("D1"); + D2_after = get_link("D2"); + if (D1_before != 3) e(5); + if (D2_before != 2) e(6); + if (D1_after != 2) e(7); + if (D2_after != 3) e(8); + if (x_link != 2) e(9); + if (y_link != 2) e(10); + if (access("D2/Y", 7) != 0) e(11); + if (rename("D2/Y", "D1/Z") != 0) e(12); + if (get_link("D1") != 3) e(13); + if (get_link("D2") != 2) e(14); + if (rmdir("D1/Z") != 0) e(15); + if (get_link("D1") != 2) e(16); + if (rmdir("D1") != 0) e(17); + if (rmdir("D2") != 0) e(18); +} + +void test21k() +{ +/* Now test the same 4 cases, except when the target exists. */ + + int fd, D1_before, D1_after, x_link, y_link; + + /* Test case 5: renaming a file within the same directory. */ + subtest = 10; + if (mkdir("D1", 0777) != 0) e(1); + if ( (fd = creat("D1/x", 0777)) < 0) e(2); + if (close(fd) != 0) e(3); + if ( (fd = creat("D1/y", 0777)) < 0) e(3); + if (close(fd) != 0) e(4); + D1_before = get_link("D1"); + x_link = get_link("D1/x"); + if (rename("D1/x", "D1/y") != 0) e(5); + y_link = get_link("D1/y"); + D1_after = get_link("D1"); + if (D1_before != 2) e(6); + if (D1_after != 2) e(7); + if (x_link != 1) e(8); + if (y_link != 1) e(9); + if (access("D1/y", 7) != 0) e(10); + if (unlink("D1/y") != 0) e(11); + if (rmdir("D1") != 0) e(12); +} + +void test21l() +{ + int fd, D1_before, D1_after, D2_before, D2_after, x_link, y_link; + + /* Test case 6: move a file to a new directory. */ + subtest = 11; + if (mkdir("D1", 0777) != 0) e(1); + if (mkdir("D2", 0777) != 0) e(2); + if ( (fd = creat("D1/x", 0777)) < 0) e(3); + if (close(fd) != 0) e(4); + if ( (fd = creat("D2/y", 0777)) < 0) e(5); + if (close(fd) != 0) e(6); + D1_before = get_link("D1"); + D2_before = get_link("D2"); + x_link = get_link("D1/x"); + if (rename("D1/x", "D2/y") != 0) e(7); + y_link = get_link("D2/y"); + D1_after = get_link("D1"); + D2_after = get_link("D2"); + if (D1_before != 2) e(8); + if (D2_before != 2) e(9); + if (D1_after != 2) e(10); + if (D2_after != 2) e(11); + if (x_link != 1) e(12); + if (y_link != 1) e(13); + if (access("D2/y", 7) != 0) e(14); + if (unlink("D2/y") != 0) e(15); + if (rmdir("D1") != 0) e(16); + if (rmdir("D2") != 0) e(17); +} + +void test21m() +{ + int D1_before, D1_after, x_link, y_link; + + /* Test case 7: renaming a directory within the same directory. */ + subtest = 12; + if (mkdir("D1", 0777) != 0) e(1); + if (mkdir("D1/X", 0777) != 0) e(2); + if (mkdir("D1/Y", 0777) != 0) e(3); + D1_before = get_link("D1"); + x_link = get_link("D1/X"); + if (rename("D1/X", "D1/Y") != 0) e(4); + y_link = get_link("D1/Y"); + D1_after = get_link("D1"); + if (D1_before != 4) e(5); + if (D1_after != 3) e(6); + if (x_link != 2) e(7); + if (y_link != 2) e(8); + if (access("D1/Y", 7) != 0) e(9); + if (rmdir("D1/Y") != 0) e(10); + if (get_link("D1") != 2) e(11); + if (rmdir("D1") != 0) e(12); +} + + +void test21n() +{ + int D1_before, D1_after, D2_before, D2_after, x_link, y_link; + + /* Test case 8: move a directory to a new directory. */ + subtest = 13; + if (mkdir("D1", 0777) != 0) e(1); + if (mkdir("D2", 0777) != 0) e(2); + if (mkdir("D1/X", 0777) != 0) e(3); + if (mkdir("D2/Y", 0777) != 0) e(4); + D1_before = get_link("D1"); + D2_before = get_link("D2"); + x_link = get_link("D1/X"); + if (rename("D1/X", "D2/Y") != 0) e(5); + y_link = get_link("D2/Y"); + D1_after = get_link("D1"); + D2_after = get_link("D2"); + if (D1_before != 3) e(6); + if (D2_before != 3) e(7); + if (D1_after != 2) e(8); + if (D2_after != 3) e(9); + if (x_link != 2) e(10); + if (y_link != 2) e(11); + if (access("D2/Y", 7) != 0) e(12); + if (rename("D2/Y", "D1/Z") != 0) e(13); + if (get_link("D1") != 3) e(14); + if (get_link("D2") != 2) e(15); + if (rmdir("D1/Z") != 0) e(16); + if (get_link("D1") != 2) e(17); + if (rmdir("D1") != 0) e(18); + if (rmdir("D2") != 0) e(19); +} + + +void test21o() +{ + /* Test trying to remove . and .. */ + subtest = 14; + if (mkdir("D1", 0777) != 0) e(1); + if (chdir("D1") != 0) e(2); + if (rmdir(".") == 0) e(3); + if (rmdir("..") == 0) e(4); + if (mkdir("D2", 0777) != 0) e(5); + if (mkdir("D3", 0777) != 0) e(6); + if (mkdir("D4", 0777) != 0) e(7); + if (rmdir("D2/../D3/../D4") != 0) e(8); /* legal way to remove D4 */ + if (rmdir("D2/../D3/../D2/..") == 0) e(9); /* removing self is illegal */ + if (rmdir("D2/../D3/../D2/../..") == 0) e(10);/* removing parent is illegal*/ + if (rmdir("../D1/../D1/D3") != 0) e(11); /* legal way to remove D3 */ + if (rmdir("./D2/../D2") != 0) e(12); /* legal way to remove D2 */ + if (chdir("..") != 0) e(13); + if (rmdir("D1") != 0) e(14); +} + + +int get_link(name) +char *name; +{ + struct stat statbuf; + + if (stat(name, &statbuf) != 0) { + printf("Unable to stat %s\n", name); + errct++; + return(-1); + } + return(statbuf.st_nlink); +} + + +void e(n) +int n; +{ + int err_num = errno; /* save errno in case printf clobbers it */ + + printf("Subtest %d, error %d errno=%d ", subtest, n, errno); + errno = err_num; /* restore errno, just in case */ + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} + +void quit() +{ + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test22.c b/test/test22.c new file mode 100644 index 000000000..f0bdfe668 --- /dev/null +++ b/test/test22.c @@ -0,0 +1,202 @@ +/* test22: umask() (p) Jan-Mark Wams. email: jms@cs.vu.nl */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <sys/wait.h> +#include <stdio.h> + +#define MAX_ERROR 4 /* Stop after ``MAX_ERROR'' errors. */ +#define ITERATIONS 2 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) + +int errct = 0; /* Total error counter. */ +int subtest = 1; + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test22a, (void)); +_PROTOTYPE(int mode, (char *filename)); +_PROTOTYPE(int umode, (char *filename)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 22 "); + fflush(stdout); + system("chmod 777 DIR_22/* DIR_22/*/* > /dev/null 2>&1"); + System("rm -rf DIR_22; mkdir DIR_22"); + Chdir("DIR_22"); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test22a(); + } + + quit(); +} + +void test22a() +{ + int fd1, fd2; + int i, oldmask; + int stat_loc; /* For the wait sys call. */ + + subtest = 1; + + system("chmod 777 ../DIR_22/* ../DIR_22/*/* > /dev/null 2>&1"); + System("rm -rf ../DIR_22/*"); + + oldmask = 0123; /* Set oldmask and umask. */ + umask(oldmask); /* Set oldmask and umask. */ + + /* Check all the possible values of umask. */ + for (i = 0000; i <= 0777; i++) { + if (oldmask != umask(i)) e(1); /* set umask() */ + fd1 = open("open", O_CREAT, 0777); + if (fd1 != 3) e(2); /* test open(), */ + fd2 = creat("creat", 0777); + if (fd2 != 4) e(3); /* creat(), */ + if (mkdir("dir", 0777) != 0) e(4); /* mkdir(), */ + if (mkfifo("fifo", 0777) != 0) e(5); /* and mkfifo(). */ + + if (umode("open") != i) e(6); /* see if they have */ + if (umode("creat") != i) e(7); /* the proper mode */ + if (umode("dir") != i) e(8); + if (umode("fifo") != i) e(9); + + /* Clean up */ + if (close(fd1) != 0) e(10); + if (close(fd2) != 0) e(11); /* close fd's and */ + unlink("open"); /* clean the dir */ + unlink("creat"); + rmdir("dir"); + unlink("fifo"); + oldmask = i; /* save current mask */ + } + + /* Check-reset mask */ + if (umask(0124) != 0777) e(12); + + /* Check if a umask of 0000 leaves the modes alone. */ + if (umask(0000) != 0124) e(13); + for (i = 0000; i <= 0777; i++) { + fd1 = open("open", O_CREAT, i); + if (fd1 != 3) e(14); /* test open(), */ + fd2 = creat("creat", i); + if (fd2 != 4) e(15); /* creat(), */ + if (mkdir("dir", i) != 0) e(16); /* mkdir(), */ + if (mkfifo("fifo", i) != 0) e(17); /* and mkfifo(). */ + + if (mode("open") != i) e(18); /* see if they have */ + if (mode("creat") != i) e(19); /* the proper mode */ + if (mode("dir") != i) e(20); + if (mode("fifo") != i) e(21); + + /* Clean up */ + if (close(fd1) != 0) e(22); + if (close(fd2) != 0) e(23); + if (unlink("open") != 0) e(24); + unlink("creat"); + rmdir("dir"); + unlink("fifo"); + } + + /* Check if umask survives a fork() */ + if (umask(0124) != 0000) e(25); + switch (fork()) { + case -1: fprintf(stderr, "Can't fork\n"); break; + case 0: + mkdir("bar", 0777); /* child makes a dir */ + exit(0); + default: + if (wait(&stat_loc) == -1) e(26); + } + if (umode("bar") != 0124) e(27); + rmdir("bar"); + + /* Check if umask in child changes umask in parent. */ + switch (fork()) { + case -1: fprintf(stderr, "Can't fork\n"); break; + case 0: + switch (fork()) { + case -1: + fprintf(stderr, "Can't fork\n"); + break; + case 0: + if (umask(0432) != 0124) e(28); + exit(0); + default: + if (wait(&stat_loc) == -1) e(29); + } + if (umask(0423) != 0124) e(30); + exit(0); + default: + if (wait(&stat_loc) == -1) e(31); + } + if (umask(0342) != 0124) e(32); + + /* See if extra bits are ignored */ + if (umask(0xFFFF) != 0342) e(33); + if (umask(0xFE00) != 0777) e(34); + if (umask(01777) != 0000) e(35); + if (umask(0022) != 0777) e(36); +} + +int mode(arg) +char *arg; +{ /* return the file mode. */ + struct stat st; + Stat(arg, &st); + return st.st_mode & 0777; +} + +int umode(arg) +char *arg; +{ /* return the umask used for this file */ + return 0777 ^ mode(arg); +} + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + system("chmod 777 ../DIR_22/* ../DIR_22/*/* > /dev/null 2>&1"); + System("rm -rf DIR_22"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test23.c b/test/test23.c new file mode 100644 index 000000000..4f5386c81 --- /dev/null +++ b/test/test23.c @@ -0,0 +1,420 @@ +/* test23: chdir(), getcwd() Author: Jan-Mark Wams (jms@cs.vu.nl) */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/dir.h> +#include <limits.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define ITERATIONS 3 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) + +int errct; +int subtest; +int superuser; /* True if we are root. */ + +char cwd[PATH_MAX]; /* Space for path names. */ +char cwd2[PATH_MAX]; +char buf[PATH_MAX]; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; /* Same for path, both too long */ + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test23a, (void)); +_PROTOTYPE(void test23b, (void)); +_PROTOTYPE(void test23c, (void)); +_PROTOTYPE(void makelongnames, (void)); /* Fill MaxName etc. */ +_PROTOTYPE(char *last_index, (char *string, int ch)); +_PROTOTYPE(char *my_getcwd, (char *buf, int size)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 23 "); + fflush(stdout); + System("rm -rf DIR_23; mkdir DIR_23"); + Chdir("DIR_23"); + makelongnames(); + superuser = (geteuid() == 0); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test23a(); /* Test normal operation */ + if (m & 0002) test23b(); /* Test critical operation */ + if (m & 0004) test23c(); /* Test error operation */ + } + + quit(); +} + +void test23a() +{ /* Test normal operation. */ + register int i; + + subtest = 1; + + System("rm -rf ../DIR_23/*"); + + /* Let's do some fiddeling with path names. */ + if (getcwd(cwd, PATH_MAX) != cwd) e(1); + if (chdir(cwd) != 0) e(2); + if (getcwd(buf, PATH_MAX) != buf) e(3); + if (strcmp(buf, cwd) != 0) e(4); + if (chdir(".") != 0) e(5); + if (getcwd(buf, PATH_MAX) != buf) e(6); + if (strcmp(buf, cwd) != 0) e(7); + if (chdir("./././.") != 0) e(8); + if (getcwd(buf, PATH_MAX) != buf) e(9); + if (strcmp(buf, cwd) != 0) e(10); + + /* Creat a working dir named "foo", remove any previous residues. */ + System("rm -rf foo"); + if (mkdir("foo", 0777) != 0) e(11); + + /* Do some more fiddeling with path names. */ + if (chdir("foo/.././foo/..") != 0) e(12); /* change to cwd */ + if (getcwd(buf, PATH_MAX) != buf) e(13); + if (strcmp(buf, cwd) != 0) e(13); + if (chdir("foo") != 0) e(14); /* change to foo */ + if (chdir("..") != 0) e(15); /* and back again */ + if (getcwd(buf, PATH_MAX) != buf) e(16); + if (strcmp(buf, cwd) != 0) e(17); + + /* Make 30 sub dirs, eg. ./bar/bar/bar/bar/bar...... */ + System("rm -rf bar"); /* get ridd of bar */ + for (i = 0; i < 30; i++) { + if (mkdir("bar", 0777) != 0) e(18); + if (chdir("bar") != 0) e(19); /* change to bar */ + } + for (i = 0; i < 30; i++) { + if (chdir("..") != 0) e(20); /* and back again */ + if (rmdir("bar") != 0) e(21); + } + + /* Make sure we are back where we started. */ + if (getcwd(buf, PATH_MAX) != buf) e(22); + if (strcmp(buf, cwd) != 0) e(23); + System("rm -rf bar"); /* just incase */ + + /* Do some normal checks on `Chdir()' and `getcwd()' */ + if (chdir("/") != 0) e(24); + if (getcwd(buf, PATH_MAX) != buf) e(25); + if (strcmp(buf, "/") != 0) e(26); + if (chdir("..") != 0) e(27); /* go to parent of / */ + if (getcwd(buf, PATH_MAX) != buf) e(28); + if (strcmp(buf, "/") != 0) e(29); + if (chdir(cwd) != 0) e(30); + if (getcwd(buf, PATH_MAX) != buf) e(31); + if (strcmp(buf, cwd) != 0) e(32); + if (chdir("/etc") != 0) e(33); /* /etc might be on RAM */ + if (getcwd(buf, PATH_MAX) != buf) e(34); /* might make a difference */ + if (strcmp(buf, "/etc") != 0) e(35); + if (chdir(cwd) != 0) e(36); + if (getcwd(buf, PATH_MAX) != buf) e(37); + if (strcmp(buf, cwd) != 0) e(38); + if (chdir(".//.//") != 0) e(39); /* .//.// == current dir */ + if (getcwd(buf, PATH_MAX) != buf) e(40); + if (strcmp(buf, cwd) != 0) e(41); /* we might be at '/' */ +#ifdef _MINIX + /* XXX - my_getcwd() is old rubbish. It reads the directory directly instead + * of through the directory library. It uses a fixed size buffer instead of + * a size related to PATH_MAX, NAME_MAX or the size required. + */ + if (my_getcwd(buf, PATH_MAX) != buf) e(42); /* get cwd my way */ + if (strcmp(cwd, buf) != 0) e(43); +#endif + System("rm -rf foo"); +} + +void test23b() +{ /* Test critical values. */ + subtest = 2; + + System("rm -rf ../DIR_23/*"); + + /* Fiddle with the size (2nt) parameter of `getcwd ()'. */ + if (getcwd(cwd, PATH_MAX) != cwd) e(1); /* get cwd */ + if (getcwd(buf, strlen(cwd)) != (char *) 0) e(2); /* size 1 to small */ + if (errno != ERANGE) e(3); + if (getcwd(buf, PATH_MAX) != buf) e(4); + if (strcmp(buf, cwd) != 0) e(5); + Chdir(cwd); /* getcwd might cd / */ + if (getcwd(buf, strlen(cwd) + 1) != buf) e(6); /* size just ok */ + if (getcwd(buf, PATH_MAX) != buf) e(7); + if (strcmp(buf, cwd) != 0) e(8); + + /* Let's see how "MaxName" and "ToLongName" are handled. */ + if (mkdir(MaxName, 0777) != 0) e(9); + if (chdir(MaxName) != 0) e(10); + if (chdir("..") != 0) e(11); + if (rmdir(MaxName) != 0) e(12); + if (getcwd(buf, PATH_MAX) != buf) e(13); + if (strcmp(buf, cwd) != 0) e(14); + if (chdir(MaxPath) != 0) e(15); + if (getcwd(buf, PATH_MAX) != buf) e(16); + if (strcmp(buf, cwd) != 0) e(17); + + if (chdir(ToLongName) != -1) e(18); +#ifdef _POSIX_NO_TRUNC +# if _POSIX_NO_TRUNC - 0 != -1 + if (errno != ENAMETOOLONG) e(20); +# else + if (errno != ENOENT) e(20); +# endif +#else +# include "error, this case requires dynamic checks and is not handled" +#endif + + if (getcwd(buf, PATH_MAX) != buf) e(21); + if (strcmp(buf, cwd) != 0) e(22); + if (chdir(ToLongPath) != -1) e(23); + if (errno != ENAMETOOLONG) e(24); + if (getcwd(buf, PATH_MAX) != buf) e(25); + if (strcmp(buf, cwd) != 0) e(26); +} + +void test23c() +{ /* Check reaction to errors */ + subtest = 3; + + System("rm -rf ../DIR_23/*"); + + if (getcwd(cwd, PATH_MAX) != cwd) e(1); /* get cwd */ + + /* Creat a working dir named "foo", remove any previous residues. */ + System("rm -rf foo; mkdir foo"); + + /* Check some obviouse errors. */ + if (chdir("") != -1) e(2); + if (errno != ENOENT) e(3); + if (getcwd(buf, PATH_MAX) != buf) e(4); + if (strcmp(buf, cwd) != 0) e(5); + if (getcwd(buf, 0) != (char *) 0) e(6); + if (errno != EINVAL) e(7); + if (getcwd(buf, PATH_MAX) != buf) e(8); + if (strcmp(buf, cwd) != 0) e(9); + if (getcwd(buf, 0) != (char *) 0) e(10); + if (errno != EINVAL) e(11); + if (getcwd(buf, PATH_MAX) != buf) e(12); + if (strcmp(buf, cwd) != 0) e(13); + if (chdir(cwd) != 0) e(14); /* getcwd might be buggy. */ + + /* Change the mode of foo, and check the effect. */ + if (chdir("foo") != 0) e(15); /* change to foo */ + if (mkdir("bar", 0777) != 0) e(16); /* make a new dir bar */ + if (getcwd(cwd2, PATH_MAX) != cwd2) e(17); /* get the new cwd */ + if (getcwd(buf, 3) != (char *) 0) e(18); /* size is too small */ + if (errno != ERANGE) e(19); + if (getcwd(buf, PATH_MAX) != buf) e(20); + if (strcmp(buf, cwd2) != 0) e(21); + Chdir(cwd2); /* getcwd() might cd / */ + System("chmod 377 ."); /* make foo unreadable */ + if (getcwd(buf, PATH_MAX) != buf) e(22); /* dir not readable */ + if (getcwd(buf, PATH_MAX) != buf) e(23); + if (strcmp(buf, cwd2) != 0) e(24); + if (chdir("bar") != 0) e(25); /* at .../foo/bar */ + if (!superuser) { + if (getcwd(buf, PATH_MAX) != (char *) 0) e(26); + if (errno != EACCES) e(27); + } + if (superuser) { + if (getcwd(buf, PATH_MAX) != buf) e(28); + } + if (chdir(cwd2) != 0) e(29); + if (getcwd(buf, PATH_MAX) != buf) e(30); + if (strcmp(buf, cwd2) != 0) e(31); + System("chmod 677 ."); /* make foo inaccessable */ + if (!superuser) { + if (getcwd(buf, PATH_MAX) != (char *) 0) e(32); /* try to get cwd */ + if (errno != EACCES) e(33); /* but no access */ + if (chdir("..") != -1) e(34); /* try to get back */ + if (errno != EACCES) e(35); /* again no access */ + if (chdir(cwd) != 0) e(36); /* get back to cwd */ + /* `Chdir()' might do path optimizing, it shouldn't. */ + if (chdir("foo/..") != -1) e(37); /* no op */ + if (chdir("foo") != -1) e(38); /* try to cd to foo */ + if (errno != EACCES) e(39); /* no have access */ + if (getcwd(buf, PATH_MAX) != buf) e(40); + if (strcmp(buf, cwd) != 0) e(41); + } + if (superuser) { + if (getcwd(buf, PATH_MAX) != buf) e(42); + if (strcmp(buf, cwd2) != 0) e(43); + if (chdir("..") != 0) e(44); /* get back to cwd */ + if (chdir("foo") != 0) e(45); /* get back to foo */ + if (chdir(cwd) != 0) e(46); /* get back to cwd */ + } + if (getcwd(buf, PATH_MAX) != buf) e(47); /* check we are */ + if (strcmp(buf, cwd) != 0) e(48); /* back at cwd. */ + Chdir(cwd); /* just in case... */ + + if (chdir("/etc/passwd") != -1) e(49); /* try to change to a file */ + if (errno != ENOTDIR) e(50); + if (getcwd(buf, PATH_MAX) != buf) e(51); + if (strcmp(buf, cwd) != 0) e(52); + if (chdir("/notexist") != -1) e(53); + if (errno != ENOENT) e(54); + if (getcwd(buf, PATH_MAX) != buf) e(55); + if (strcmp(buf, cwd) != 0) e(56); + System("chmod 777 foo"); + if (chdir("foo") != 0) e(57); + + /* XXX - this comment was botched by 'pretty'. */ + /* * Since `foo' is the cwd, it should not be removeable but * if it + * were, this code would be found here; * + * + * System ("cd .. ; rm -rf foo"); remove foo * if (chdir (".") + * != -1) e(); try go to. * if (errno != ENOENT) e(); + * hould not be an entry * if (chdir ("..") != -1) e(); try + * to get back * if (errno != ENOENT) e(); should not be + * an entry * if (getcwd (buf, PATH_MAX) != (char *)0) e(); don't + * know where we are * + * + * What should errno be now ? The cwd might be gone if te superuser * + * removed the cwd. (Might even have linked it first.) But this * + * testing should be done by the test program for `rmdir()'. */ + if (chdir(cwd) != 0) e(58); +} + + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(ToLongName, MaxName); /* copy them Max to ToLong */ + strcpy(ToLongPath, MaxPath); + + ToLongName[NAME_MAX] = 'a'; + ToLongName[NAME_MAX + 1] = '\0'; /* extend ToLongName by one too many */ + ToLongPath[PATH_MAX - 1] = '/'; + ToLongPath[PATH_MAX] = '\0'; /* inc ToLongPath by one */ +} + + +/* The following code, is take from pwd written by Adri Koppes */ + +/* My_getcwd() helper. */ +char *last_index(string, ch) +char *string; +char ch; +{ + register char *retval = '\0'; + + while (*string != '\0') { + if (*string == ch) retval = string; + string++; + } + return(retval); +} + + +char *my_getcwd(buf, size) +char *buf; +int size; +{ /* should be like getcwd() */ + int sd; + register int fd; + register char *n; + char name[128]; + struct stat s, st, sk; + struct direct d; + + if (size <= 0) return(char *) 0; + + *buf = '\0'; + *name = '\0'; + stat(".", &s); + do { + if ((fd = open("..", O_RDONLY)) < 0) return(char *) 0; + st.st_dev = s.st_dev; + st.st_ino = s.st_ino; + stat("..", &s); + Chdir(".."); + sd = sizeof(struct direct); + if (s.st_dev == st.st_dev) { + do { + if (read(fd, (char *) &d, sd) < sd) return(char *) 0; + } while (d.d_ino != st.st_ino); + } else { + do { + if (read(fd, (char *) &d, sd) < sd) return(char *) 0; + stat(d.d_name, &sk); + } while ((sk.st_dev != st.st_dev) || + (sk.st_ino != st.st_ino)); + } + close(fd); + if (strcmp(".", d.d_name) != 0) { + strcat(name, "/"); + strcat(name, d.d_name); + } + } while ((s.st_ino != st.st_ino) || (s.st_dev != st.st_dev)); + if (*name == '\0') + strncat(buf, "/", size); + else + while ((n = last_index(name, '/')) != NULL) { + n[NAME_MAX] = '\0'; + strncat(buf, n, size - strlen(buf)); + *n = '\0'; + } + strncat(buf, name, size - strlen(buf)); + buf[size - 1] = '\0'; + Chdir(buf); /* get back there */ + return buf; +} + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_23"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test24.c b/test/test24.c new file mode 100644 index 000000000..4b89a7c6c --- /dev/null +++ b/test/test24.c @@ -0,0 +1,405 @@ +/* Test24: opendir, readdir, rewinddir, closedir Author: Jan-Mark Wams */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <limits.h> +#include <fcntl.h> +#include <dirent.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void chk_dir, (DIR * dirpntr)); +_PROTOTYPE(void test24a, (void)); +_PROTOTYPE(void test24b, (void)); +_PROTOTYPE(void test24c, (void)); +_PROTOTYPE(void makelongnames, (void)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); + +#define OVERFLOW_DIR_NR (OPEN_MAX + 1) +#define MAX_ERROR 4 +#define ITERATIONS 5 + +#define DIRENT0 ((struct dirent *) NULL) +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) + +int errct = 0; +int subtest = 1; +int superuser; + +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; /* Same for path, both too long */ + + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 24 "); + fflush(stdout); + System("rm -rf DIR_24; mkdir DIR_24"); + Chdir("DIR_24"); + makelongnames(); + superuser = (geteuid() == 0); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test24a(); + if (m & 0002) test24b(); + if (m & 0004) test24c(); + } + quit(); +} + +void test24a() +{ /* Test normal operations. */ + int fd3, fd4, fd5; + DIR *dirp; + int j, ret, fd, flags; + struct stat st1, st2; + int stat_loc; + time_t time1; + + subtest = 1; + + System("rm -rf ../DIR_24/*"); + + if ((fd = dup(0)) != 3) e(1); /* dup stdin */ + close(fd); /* free the fd again */ + dirp = opendir("/"); /* open "/" */ + if (dirp == ((DIR *) NULL)) e(2); /* has to succseed */ + if ((fd = dup(0)) <= 2) e(3); /* dup stdin */ + if (fd > 3) { /* if opendir() uses fd 3 */ + flags = fcntl(3, F_GETFD); /* get fd fags of 3 */ + if (!(flags & FD_CLOEXEC)) e(4); /* it should be closed on */ + } /* exec..() calls */ + close(fd); /* free the fd again */ + ret = closedir(dirp); /* close, we don't need it */ + if (ret == -1) e(5); /* closedir () unsucces full */ + if (ret != 0) e(6); /* should be 0 or -1 */ + if ((fd = dup(0)) != 3) e(7); /* see if next fd is same */ + close(fd); /* free the fd again */ + + System("rm -rf foo; mkdir foo"); + Chdir("foo"); + System("touch f1 f2 f3 f4 f5"); /* make f1 .. f5 */ + System("rm f[24]"); /* creat `holes' in entrys */ + Chdir(".."); + + if ((dirp = opendir("foo")) == ((DIR *) NULL)) e(8); /* open foo */ + chk_dir(dirp); /* test if foo's ok */ + for (j = 0; j < 10; j++) { + errno = j * 47 % 7; /* there should */ + if (readdir(dirp) != DIRENT0) e(9); /* be nomore dir */ + if (errno != j * 47 % 7) e(10); /* entrys */ + } + rewinddir(dirp); /* rewind foo */ + chk_dir(dirp); /* test foosok */ + for (j = 0; j < 10; j++) { + errno = j * 23 % 7; /* there should */ + if (readdir(dirp) != DIRENT0) e(11); /* be nomore dir */ + if (errno != j * 23 % 7) e(12); /* entrys */ + } + if ((fd4 = creat("foo/f4", 0666)) <= 2) e(13); /* Open a file. */ + System("rm foo/f4"); /* Kill entry. */ + rewinddir(dirp); /* Rewind foo. */ + if ((fd3 = open("foo/f3", O_WRONLY)) <= 2) e(14); /* Open more files. */ + if ((fd5 = open("foo/f5", O_WRONLY)) <= 2) e(15); + if (write(fd3, "Hello", 6) != 6) e(16); + if (write(fd4, "Hello", 6) != 6) e(17); /* write some data */ + if (close(fd5) != 0) e(18); + chk_dir(dirp); + for (j = 0; j < 10; j++) { + errno = j * 101 % 7; /* there should */ + if (readdir(dirp) != DIRENT0) e(19); /* be nomore dir */ + if (errno != j * 101 % 7) e(20); /* entrys */ + } + if (close(fd4) != 0) e(21); /* shouldn't matter */ + if (close(fd3) != 0) e(22); /* when we do this */ + if (closedir(dirp) != 0) e(23); /* close foo again */ + + Chdir("foo"); + if ((dirp = opendir(".//")) == ((DIR *) NULL)) e(24); /* open foo again */ + Chdir(".."); + chk_dir(dirp); /* foosok? */ + for (j = 0; j < 10; j++) { + errno = (j * 101) % 7; /* there should */ + if (readdir(dirp) != DIRENT0) e(25); /* be nomore dir */ + if (errno != (j * 101) % 7) e(26); /* entrys */ + } + + if (closedir(dirp) != 0) e(27); /* It should be closable */ + + stat("foo", &st1); /* get stat */ + time(&time1); + while (time1 >= time((time_t *)0)) + ; + if ((dirp = opendir("foo")) == ((DIR *) NULL)) e(28); /* open, */ + if (readdir(dirp) == DIRENT0) e(29); /* read and */ + stat("foo", &st2); /* get new stat */ + if (st1.st_atime > st2.st_atime) e(30); /* st_atime check */ + + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + rewinddir(dirp); /* rewind childs dirp */ + if (readdir(dirp) == DIRENT0) e(31); /* read should be ok */ + if (closedir(dirp) != 0) e(32); /* close child'd foo */ + exit(0); /* 0 stops here */ + default: + if (wait(&stat_loc) == -1) e(33); /* PARENT wait()'s */ + break; + } + if (closedir(dirp) != 0) e(34); /* close parent's foo */ +} + + +void test24b() +{ +/* See what happens with too many dir's open. Check if file size seems ok, + * and independency. + */ + + int i, j; /* i = highest open dir count */ + DIR *dirp[OVERFLOW_DIR_NR], *dp; + struct dirent *dep, *dep1, *dep2; + char name[NAME_MAX + 2]; /* buffer for file name, and count */ + int dot = 0, dotdot = 0; + + subtest = 2; + + System("rm -rf ../DIR_24/*"); + + for (i = 0; i < OVERFLOW_DIR_NR; i++) { + dirp[i] = opendir("/"); + if (dirp[i] == ((DIR *) NULL)) { + if (errno != EMFILE) e(1); + break; + } + } + if (i <= 4) e(2); /* sounds resanable */ + if (i >= OVERFLOW_DIR_NR) e(3); /* might be to small */ + for (j = 0; j < i; j++) { + if (closedir(dirp[(j + 5) % i]) != 0) e(4); /* neat! */ + } + + /* Now check if number of bytes in d_name can go up till NAME_MAX */ + System("rm -rf foo; mkdir foo"); + Chdir("foo"); + name[0] = 0; + for (i = 0; i <= NAME_MAX; i++) { + if (strcat(name, "X") != name) e(5); + close(creat(name, 0666)); /* fails once on */ + } /* XX..XX, 1 too long */ + Chdir(".."); + /* Now change i-th X to Y in name buffer record file of length i. */ + if ((dp = opendir("foo")) == ((DIR *) NULL)) e(6); + while ((dep = readdir(dp)) != DIRENT0) { + if (strcmp("..", dep->d_name) == 0) + dotdot++; + else if (strcmp(".", dep->d_name) == 0) + dot++; + else + name[strlen(dep->d_name)] += 1; /* 'X' + 1 == 'Y' */ + } + if (closedir(dp) != 0) e(7); + for (i = 1; i <= NAME_MAX; i++) { /* Check if every length */ + if (name[i] != 'Y') e(8); /* has been seen once. */ + } + + /* Check upper and lower bound. */ + if (name[0] != 'X') e(9); + if (name[NAME_MAX + 1] != '\0') e(10); + + /* Now check if two simultaniouse open dirs do the same */ + if ((dirp[1] = opendir("foo")) == ((DIR *) NULL)) e(11); + if ((dirp[2] = opendir("foo")) == ((DIR *) NULL)) e(12); + if ((dep1 = readdir(dirp[1])) == DIRENT0) e(13); + if ((dep2 = readdir(dirp[2])) == DIRENT0) e(14); + if (dep1->d_name == dep2->d_name) e(15); /* 1 & 2 Should be */ + strcpy(name, dep2->d_name); /* differand buffers */ + if (strcmp(dep1->d_name, name) != 0) e(16); /* But hold the same */ + if ((dep1 = readdir(dirp[1])) == DIRENT0) e(17); + if ((dep1 = readdir(dirp[1])) == DIRENT0) e(18); /* lose some entries */ + if ((dep1 = readdir(dirp[1])) == DIRENT0) e(19); /* Using dirp 1 has */ + if (dep1->d_name == dep2->d_name) e(20); /* no effect on 2 */ + if (strcmp(dep2->d_name, name) != 0) e(21); + rewinddir(dirp[1]); /* Rewinding dirp 1 */ + if ((dep2 = readdir(dirp[2])) == DIRENT0) e(22); /* can't effect 2 */ + if (strcmp(dep2->d_name, name) == 0) e(23); /* Must be next */ + if (closedir(dirp[1]) != 0) e(24); /* Closing dirp 1 */ + if ((dep2 = readdir(dirp[2])) == DIRENT0) e(25); /* can't effect 2 */ + if (strcmp(dep2->d_name, name) == 0) e(26); /* Must be next */ + if (closedir(dirp[2]) != 0) e(27); +} + + +void test24c() +{ +/* Test whether wrong things go wrong right. */ + + DIR *dirp; + + subtest = 3; + + System("rm -rf ../DIR_24/*"); + + if (opendir("foo/bar/nono") != ((DIR *) NULL)) e(1); /* nonexistent */ + if (errno != ENOENT) e(2); + System("mkdir foo; chmod 677 foo"); /* foo inaccesable */ + if (opendir("foo/bar/nono") != ((DIR *) NULL)) e(3); + if (superuser) { + if (errno != ENOENT) e(4); /* su has access */ + System("chmod 377 foo"); + if ((dirp = opendir("foo")) == ((DIR *) NULL)) e(5); + if (closedir(dirp) != 0) e(6); + } + if (!superuser) { + if (errno != EACCES) e(7); /* we don't ;-) */ + System("chmod 377 foo"); + if (opendir("foo") != ((DIR *) NULL)) e(8); + } + System("chmod 777 foo"); + + if (mkdir(MaxName, 0777) != 0) e(9); /* make longdir */ + if ((dirp = opendir(MaxName)) == ((DIR *) NULL)) e(10); /* open it */ + if (closedir(dirp) != 0) e(11); /* close it */ + if (rmdir(MaxName) != 0) e(12); /* then remove it */ + if ((dirp = opendir(MaxPath)) == ((DIR *) NULL)) e(13); /* open '.' */ + if (closedir(dirp) != 0) e(14); /* close it */ +#if 0 /* XXX - anything could happen with the bad pointer */ + if (closedir(dirp) != -1) e(15); /* close it again */ + if (closedir(dirp) != -1) e(16); /* and again */ +#endif /* 0 */ + if (opendir(ToLongName) != ((DIR *) NULL)) e(17); /* is too long */ +#ifdef _POSIX_NO_TRUNC +# if _POSIX_NO_TRUNC - 0 != -1 + if (errno != ENAMETOOLONG) e(18); +# else + if (errno != ENOENT) e(19); +# endif +#else +# include "error, this case requires dynamic checks and is not handled" +#endif + if (opendir(ToLongPath) != ((DIR *) NULL)) e(20); /* path is too long */ + if (errno != ENAMETOOLONG) e(21); + System("touch foo/abc"); /* make a file */ + if (opendir("foo/abc") != ((DIR *) NULL)) e(22); /* not a dir */ + if (errno != ENOTDIR) e(23); +} + +void chk_dir(dirp) /* dir should contain */ +DIR *dirp; /* (`f1', `f3', `f5', `.', `..') */ +{ /* no more, no less */ + int f1 = 0, f2 = 0, f3 = 0, f4 = 0, f5 = 0, /* counters for all */ + other = 0, dot = 0, dotdot = 0; /* possible entrys */ + int i; + struct dirent *dep; + char *fname; + int oldsubtest = subtest; + + subtest = 4; + + for (i = 0; i < 5; i++) { /* 3 files and `.' and `..' == 5 entrys */ + dep = readdir(dirp); + if (dep == DIRENT0) { /* not einough */ + if (dep == DIRENT0) e(1); + break; + } + fname = dep->d_name; + if (strcmp(fname, ".") == 0) + dot++; + else if (strcmp(fname, "..") == 0) + dotdot++; + else if (strcmp(fname, "f1") == 0) + f1++; + else if (strcmp(fname, "f2") == 0) + f2++; + else if (strcmp(fname, "f3") == 0) + f3++; + else if (strcmp(fname, "f4") == 0) + f4++; + else if (strcmp(fname, "f5") == 0) + f5++; + else + other++; + } /* do next dir entry */ + + if (dot != 1) e(2); /* Check the entrys */ + if (dotdot != 1) e(3); + if (f1 != 1) e(4); + if (f3 != 1) e(5); + if (f5 != 1) e(6); + if (f2 != 0) e(7); + if (f4 != 0) e(8); + if (other != 0) e(9); + + subtest = oldsubtest; +} + + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(ToLongName, MaxName); /* copy them Max to ToLong */ + strcpy(ToLongPath, MaxPath); + + ToLongName[NAME_MAX] = 'a'; + ToLongName[NAME_MAX + 1] = '\0'; /* extend ToLongName by one too many */ + ToLongPath[PATH_MAX - 1] = '/'; + ToLongPath[PATH_MAX] = '\0'; /* inc ToLongPath by one */ +} + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_24"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test25.c b/test/test25.c new file mode 100644 index 000000000..b8bf07d19 --- /dev/null +++ b/test/test25.c @@ -0,0 +1,733 @@ +/* test25: open (), close () (p) Jan-Mark Wams. email: jms@cs.vu.nl */ + +/* Not tested: O_NONBLOCK on special files, supporting it. +** On a read-only file system, some error reports are to be expected. +*/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define ITERATIONS 2 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) +#define Creat(f) if (close(creat(f,0777))!=0) printf("Can't creat %s\n",f) +#define Report(s,n) printf("Subtest %d" s,subtest,(n)) + +int errct = 0; +int subtest = 1; +int superuser; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; /* Same for path, both too long */ + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test25a, (void)); +_PROTOTYPE(void test25b, (void)); +_PROTOTYPE(void test25c, (void)); +_PROTOTYPE(void test25d, (void)); +_PROTOTYPE(void test25e, (void)); +_PROTOTYPE(void makelongnames, (void)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (geteuid() == 0 || getuid() == 0) { + printf("Test 25 cannot run as root; test aborted\n"); + exit(1); + } + + if (argc == 2) m = atoi(argv[1]); + printf("Test 25 "); + fflush(stdout); + System("rm -rf DIR_25; mkdir DIR_25"); + Chdir("DIR_25"); + makelongnames(); + superuser = (geteuid() == 0); + + /* Close all files, the parent might have opened. */ + for (i = 3; i < 100; i++) close(i); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 001) test25a(); + if (m & 002) test25b(); + if (m & 004) test25c(); + if (m & 010) test25d(); + if (m & 020) test25e(); + } + quit(); +} + +void test25a() +{ /* Test fcntl flags. */ + subtest = 1; + +#define EXCLUDE(a,b) (((a)^(b)) == ((a)|(b))) +#define ADDIT (O_APPEND | O_CREAT | O_EXCL | O_NONBLOCK | O_TRUNC) + + /* If this compiles all flags are defined but they have to be or-able. */ + if (!(EXCLUDE(O_NONBLOCK, O_TRUNC))) e(1); + if (!(EXCLUDE(O_EXCL, O_NONBLOCK | O_TRUNC))) e(2); + if (!(EXCLUDE(O_CREAT, O_EXCL | O_NONBLOCK | O_TRUNC))) e(3); + if (!(EXCLUDE(O_APPEND, O_CREAT | O_EXCL | O_NONBLOCK | O_TRUNC))) e(4); + if (!(EXCLUDE(O_RDONLY, ADDIT))) e(5); + if (!(EXCLUDE(O_WRONLY, ADDIT))) e(6); + if (!(EXCLUDE(O_RDWR, ADDIT))) e(7); +} + + +void test25b() +{ /* Test normal operation. */ + +#define BUF_SIZE 1024 + + int fd1, fd2, fd3, fd4, fd5; + char buf[BUF_SIZE]; + struct stat st1, st2, st3; + time_t time1, time2; + int stat_loc; + + subtest = 2; + + System("rm -rf ../DIR_25/*"); + + System("echo Hello > he"); /* make test files */ + System("echo Hello > ha"); /* size 6 bytes */ + System("echo Hello > hi"); + System("echo Hello > ho"); + + /* Check path resolution. Check if lowest fds are returned */ + if ((fd1 = open("he", O_RDONLY)) != 3) e(1); + if (read(fd1, buf, BUF_SIZE) != 6) e(2); + if ((fd2 = open("./ha", O_RDONLY)) != 4) e(3); + if ((fd3 = open("../DIR_25/he", O_RDWR)) != 5) e(4); + if ((fd4 = open("ho", O_WRONLY)) != 6) e(5); + if (close(fd4) != 0) e(6); + if (close(fd1) != 0) e(7); + if ((fd1 = open("./././ho", O_RDWR)) != 3) e(8); + if ((fd4 = open("../DIR_25/he", O_RDONLY)) != 6) e(9); + if (close(fd2) != 0) e(10); + if (close(fd3) != 0) e(11); + if ((fd2 = open("ha", O_RDONLY)) != 4) e(12); + if ((fd3 = open("/etc/passwd", O_RDONLY)) != 5) e(13); + if (close(fd4) != 0) e(14); /* close all */ + if (close(fd1) != 0) e(15); + if (close(fd3) != 0) e(16); + + /* Check if processes share fd2, and if they have independent new fds */ + System("rm -rf /tmp/sema.25"); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + + case 0: + if ((fd1 = open("he", O_WRONLY)) != 3) e(17); + if ((fd3 = open("../././DIR_25/ha", O_WRONLY)) != 5) e(18); + if ((fd4 = open("../DIR_25/hi", O_WRONLY)) != 6) e(19); + if ((fd5 = open("ho", O_WRONLY)) != 7) e(20); + system("while test ! -f /tmp/sema.25; do sleep 1; done"); /* parent */ + if (read(fd2, buf, BUF_SIZE) != 3) e(21); /* gets Hel */ + if (strncmp(buf, "lo\n", 3) != 0) e(22); /* we get lo */ + if (close(fd1) != 0) e(23); + if (close(fd2) != 0) e(24); + if (close(fd3) != 0) e(25); + if (close(fd4) != 0) e(26); + if (close(fd5) != 0) e(27); + exit(0); + + default: + if ((fd1 = open("ha", O_RDONLY)) != 3) e(28); + if ((fd3 = open("./he", O_RDONLY)) != 5) e(29); + if ((fd4 = open("../DIR_25/hi", O_RDWR)) != 6) e(30); + if ((fd5 = open("ho", O_WRONLY)) != 7) e(31); + if (close(fd1) != 0) e(32); + if (read(fd2, buf, 3) != 3) e(33); /* get Hel */ + Creat("/tmp/sema.25"); + if (strncmp(buf, "Hel", 3) != 0) e(34); + if (close(fd2) != 0) e(35); + if (close(fd3) != 0) e(36); + if (close(fd4) != 0) e(37); + if (close(fd5) != 0) e(38); + if (wait(&stat_loc) == -1) e(39); + if (stat_loc != 0) e(40); + } + System("rm -f /tmp/sema.25"); + + /* Check if the file status information is updated correctly */ + Stat("hi", &st1); /* get info */ + Stat("ha", &st2); /* of files */ + time(&time1); + while (time1 >= time((time_t *)0)) + ; /* wait a sec */ + if ((fd1 = open("hi", O_RDONLY)) != 3) e(41); /* open files */ + if ((fd2 = open("ha", O_WRONLY)) != 4) e(42); + if (read(fd1, buf, 1) != 1) e(43); /* read one */ + if (close(fd1) != 0) e(44); /* close one */ + Stat("hi", &st3); /* get info */ + if (st1.st_uid != st3.st_uid) e(45); + if (st1.st_gid != st3.st_gid) e(46); /* should be same */ + if (st1.st_mode != st3.st_mode) e(47); + if (st1.st_size != st3.st_size) e(48); + if (st1.st_nlink != st3.st_nlink) e(49); + if (st1.st_mtime != st3.st_mtime) e(50); + if (st1.st_ctime != st3.st_ctime) e(51); +#ifndef V1_FILESYSTEM + if (st1.st_atime >= st3.st_atime) e(52); /* except for atime. */ +#endif + if (write(fd2, "Howdy\n", 6) != 6) e(53); /* Update c & mtime. */ + if ((fd1 = open("ha", O_RDWR)) != 3) e(54); + if (read(fd1, buf, 6) != 6) e(55); /* Update atime. */ + if (strncmp(buf, "Howdy\n", 6) != 0) e(56); + if (close(fd1) != 0) e(57); + Stat("ha", &st3); + if (st2.st_uid != st3.st_uid) e(58); + if (st2.st_gid != st3.st_gid) e(59); /* should be same */ + if (st2.st_mode != st3.st_mode) e(60); + if (st2.st_nlink != st3.st_nlink) e(61); + if (st2.st_ctime >= st3.st_ctime) e(62); +#ifndef V1_FILESYSTEM + if (st2.st_atime >= st3.st_atime) e(63); +#endif + if (st2.st_mtime >= st3.st_mtime) e(64); + if (st2.st_size != st3.st_size) e(65); + if (close(fd2) != 0) e(66); + + /* Let's see if RDONLY files are read only. */ + if ((fd1 = open("hi", O_RDONLY)) != 3) e(67); + if (write(fd1, " again", 7) != -1) e(68); /* we can't write */ + if (errno != EBADF) e(69); /* a read only fd */ + if (read(fd1, buf, 7) != 6) e(70); /* but we can read */ + if (close(fd1) != 0) e(71); + + /* Let's see if WRONLY files are write only. */ + if ((fd1 = open("hi", O_WRONLY)) != 3) e(72); + if (read(fd1, buf, 7) != -1) e(73); /* we can't read */ + if (errno != EBADF) e(74); /* a write only fd */ + if (write(fd1, "hELLO", 6) != 6) e(75); /* but we can write */ + if (close(fd1) != 0) e(76); + + /* Let's see if files are closable only once. */ + if (close(fd1) != -1) e(77); + if (errno != EBADF) e(78); + + /* Let's see how calling close() with bad fds is handled. */ + if (close(10) != -1) e(79); + if (errno != EBADF) e(80); + if (close(111) != -1) e(81); + if (errno != EBADF) e(82); + if (close(-432) != -1) e(83); + if (errno != EBADF) e(84); + + /* Let's see if RDWR files are read & write able. */ + if ((fd1 = open("hi", O_RDWR)) != 3) e(85); + if (read(fd1, buf, 6) != 6) e(86); /* we can read */ + if (strncmp(buf, "hELLO", 6) != 0) e(87); /* and we can write */ + if (write(fd1, "Hello", 6) != 6) e(88); /* a read write fd */ + if (close(fd1) != 0) e(89); + + /* Check if APPENDed files are realy appended */ + if ((fd1 = open("hi", O_RDWR | O_APPEND)) != 3) e(90); /* open hi */ + + /* An open should set the file offset to 0. */ + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 0) e(91); + + /* Writing 0 bytes should not have an effect. */ + if (write(fd1, "", 0) != 0) e(92); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 0) e(93); /* the end? */ + + /* A seek befor a wirte should not matter with O_APPEND. */ + Stat("hi", &st1); + if (lseek(fd1, (off_t) - 3, SEEK_END) != st1.st_size - 3) e(94); + + /* By writing 1 byte, we force the offset to the end of the file */ + if (write(fd1, "1", 1) != 1) e(95); + Stat("hi", &st1); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != st1.st_size) e(96); + if (write(fd1, "2", 1) != 1) e(97); + Stat("hi", &st1); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != st1.st_size) e(98); + if (write(fd1, "3", 1) != 1) e(99); + Stat("hi", &st1); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != st1.st_size) e(100); + if (lseek(fd1, (off_t) - 2, SEEK_CUR) <= 0) e(101); + if (write(fd1, "4", 1) != 1) e(102); + + /* Since the mode was O_APPEND, the offset should be reset to EOF */ + Stat("hi", &st1); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != st1.st_size) e(103); + if (lseek(fd1, (off_t) - 4, SEEK_CUR) != st1.st_size - 4) e(104); + if (read(fd1, buf, BUF_SIZE) != 4) e(105); + if (strncmp(buf, "1234", 4) != 0) e(106); + if (close(fd1) != 0) e(107); + + /* Check the effect of O_CREAT */ + Stat("ho", &st1); + fd1 = open("ho", O_RDWR | O_CREAT, 0000); + if (fd1 != 3) e(108); + Stat("ho", &st2); + if (memcmp(&st1, &st2, sizeof(struct stat)) != 0) e(109); + if (read(fd1, buf, 6) != 6) e(110); + if (strncmp(buf, "Hello\n", 6) != 0) e(111); + if (write(fd1, "@", 1) != 1) e(112); + if (close(fd1) != 0) e(113); + (void) umask(0000); + fd1 = open("ho", O_RDWR | O_CREAT | O_EXCL, 0777); + if (fd1 != -1) e(114); /* ho exists */ + System("rm -rf new"); + time(&time1); + while (time1 >= time((time_t *)0)) + ; + fd1 = open("new", O_RDWR | O_CREAT, 0716); + if (fd1 != 3) e(115); /* new file */ + Stat("new", &st1); + time(&time2); + while (time2 >= time((time_t *)0)) + ; + time(&time2); + if (st1.st_uid != geteuid()) e(116); /* try this as superuser. */ + if (st1.st_gid != getegid()) e(117); + if ((st1.st_mode & 0777) != 0716) e(118); + if (st1.st_nlink != 1) e(119); + if (st1.st_mtime <= time1) e(120); + if (st1.st_mtime >= time2) e(121); +#ifndef V1_FILESYSTEM + if (st1.st_atime != st1.st_mtime) e(122); +#endif + if (st1.st_ctime != st1.st_mtime) e(123); + if (st1.st_size != 0) e(124); + if (write(fd1, "I'm new in town", 16) != 16) e(125); + if (lseek(fd1, (off_t) - 5, SEEK_CUR) != 11) e(126); + if (read(fd1, buf, 5) != 5) e(127); + if (strncmp(buf, "town", 5) != 0) e(128); + if (close(fd1) != 0) e(129); + + /* Let's test the O_TRUNC flag on this new file. */ + time(&time1); + while (time1 >= time((time_t *)0)); + if ((fd1 = open("new", O_RDWR | O_TRUNC)) != 3) e(130); + Stat("new", &st1); + time(&time2); + while (time2 >= time((time_t *)0)); + time(&time2); + if ((st1.st_mode & 0777) != 0716) e(131); + if (st1.st_size != (size_t) 0) e(132); /* TRUNCed ? */ + if (st1.st_mtime <= time1) e(133); + if (st1.st_mtime >= time2) e(134); + if (st1.st_ctime != st1.st_mtime) e(135); + if (close(fd1) != 0) e(136); + + /* Test if file permission bits and the file ownership are unchanged. */ + /* So we will see if `O_CREAT' has no effect if the file exists. */ + if (superuser) { + System("echo > bar; chmod 077 bar"); /* Make bar 077 */ + System("chown daemon bar"); + System("chgrp daemon bar"); /* Daemon's bar */ + fd1 = open("bar", O_RDWR | O_CREAT | O_TRUNC, 0777); /* knock knock */ + if (fd1 == -1) e(137); + if (write(fd1, "foo", 3) != 3) e(138); /* rewrite bar */ + if (close(fd1) != 0) e(139); + Stat("bar", &st1); + if (st1.st_uid != 1) e(140); /* bar is still */ + if (st1.st_gid != 1) e(141); /* owned by daemon */ + if ((st1.st_mode & 0777) != 077) e(142); /* mode still is 077 */ + if (st1.st_size != (size_t) 3) e(143); /* 3 bytes long */ + + /* We do the whole thing again, but with O_WRONLY */ + fd1 = open("bar", O_WRONLY | O_CREAT | O_TRUNC, 0777); + if (fd1 == -1) e(144); + if (write(fd1, "foobar", 6) != 6) e(145); /* rewrite bar */ + if (close(fd1) != 0) e(146); + Stat("bar", &st1); + if (st1.st_uid != 1) e(147); /* bar is still */ + if (st1.st_gid != 1) e(148); /* owned by daemon */ + if ((st1.st_mode & 0777) != 077) e(149); /* mode still is 077 */ + if (st1.st_size != (size_t) 6) e(150); /* 6 bytes long */ + } +} + + +void test25c() +{ /* Test normal operation Part two. */ + int fd1, fd2; + char buf[BUF_SIZE]; + struct stat st; + int stat_loc; + static int iteration=0; + + subtest = 3; + iteration++; + + System("rm -rf ../DIR_25/*"); + + /* Fifo file test here. */ + if (mkfifo("fifo", 0777) != 0) e(1); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); /* Give child 20 seconds to live. */ + if ((fd1 = open("fifo", O_RDONLY)) != 3) e(2); + if (read(fd1, buf, BUF_SIZE) != 23) e(3); + if (strncmp(buf, "1 2 3 testing testing\n", 23) != 0) e(4); + if (close(fd1) != 0) e(5); + exit(0); + default: + if ((fd1 = open("fifo", O_WRONLY)) != 3) e(6); + if (write(fd1, "1 2 3 testing testing\n", 23) != 23) e(7); + if (close(fd1) != 0) e(8); + if (wait(&stat_loc) == -1) e(9); + if (stat_loc != 0) e(10); /* The alarm went off? */ + } + + /* Try opening for writing with O_NONBLOCK. */ + fd1 = open("fifo", O_WRONLY | O_NONBLOCK); + if (fd1 != -1) e(11); + if (errno != ENXIO) e(12); + close(fd1); + + /* Try opening for writing with O_NONBLOCK and O_CREAT. */ + fd1 = open("fifo", O_WRONLY | O_CREAT | O_NONBLOCK, 0777); + if (fd1 != -1) e(13); + if (errno != ENXIO) e(14); + close(fd1); + + /* Both the NONBLOCK and the EXCLusive give raise to error. */ + fd1 = open("fifo", O_WRONLY | O_CREAT | O_EXCL | O_NONBLOCK, 0777); + if (fd1 != -1) e(15); + if (errno != EEXIST && errno != ENXIO) e(16); + close(fd1); /* Just in case. */ + + /* Try opening for reading with O_NONBLOCK. */ + fd1 = open("fifo", O_RDONLY | O_NONBLOCK); + if (fd1 != 3) e(17); + if (close(fd1) != 0) e(18); + +/* Nopt runs out of memory. ;-< We just cut out some valid code */ + /* FIFO's should always append. (They have no file position.) */ + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); /* Give child 20 seconds to live. */ + if ((fd1 = open("fifo", O_WRONLY)) != 3) e(19); + if ((fd2 = open("fifo", O_WRONLY)) != 4) e(20); + if (write(fd1, "I did see Elvis.\n", 18) != 18) e(21); + if (write(fd2, "I DID.\n", 8) != 8) e(22); + if (close(fd2) != 0) e(23); + if (close(fd1) != 0) e(24); + exit(0); + default: + if ((fd1 = open("fifo", O_RDONLY)) != 3) e(25); + if (read(fd1, buf, 18) != 18) e(26); + if (strncmp(buf, "I did see Elvis.\n", 18) != 0) e(27); + if (read(fd1, buf, BUF_SIZE) != 8) e(28); + if (strncmp(buf, "I DID.\n", 8) != 0) e(29); + if (close(fd1) != 0) e(30); + if (wait(&stat_loc) == -1) e(31); + if (stat_loc != 0) e(32); /* The alarm went off? */ + } + + /* O_TRUNC should have no effect on FIFO files. */ + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); /* Give child 20 seconds to live. */ + if ((fd1 = open("fifo", O_WRONLY)) != 3) e(33); + if (write(fd1, "I did see Elvis.\n", 18) != 18) e(34); + if ((fd2 = open("fifo", O_WRONLY | O_TRUNC)) != 4) e(35); + if (write(fd2, "I DID.\n", 8) != 8) e(36); + if (close(fd2) != 0) e(37); + if (close(fd1) != 0) e(38); + exit(0); + default: + if ((fd1 = open("fifo", O_RDONLY)) != 3) e(39); + if (read(fd1, buf, 18) != 18) e(40); + if (strncmp(buf, "I did see Elvis.\n", 18) != 0) e(41); + if (read(fd1, buf, BUF_SIZE) != 8) e(42); + if (strncmp(buf, "I DID.\n", 8) != 0) e(43); + if (close(fd1) != 0) e(44); + if (wait(&stat_loc) == -1) e(45); + if (stat_loc != 0) e(46); /* The alarm went off? */ + } + + /* Closing the last fd should flush all data to the bitbucket. */ + System("rm -rf /tmp/sema.25"); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + + case 0: + alarm(20); /* Give child 20 seconds to live. */ + if ((fd1 = open("fifo", O_WRONLY)) != 3) e(47); + if (write(fd1, "I did see Elvis.\n", 18) != 18) e(48); + Creat("/tmp/sema.25"); + sleep(2); /* give parent a chance to open */ + /* this was sleep(1), but that's too short: child also sleeps(1) */ + if (close(fd1) != 0) e(49); + exit(0); + + default: + if ((fd1 = open("fifo", O_RDONLY)) != 3) e(50); + /* Make `sure' write has closed. */ + while (stat("/tmp/sema.25", &st) != 0) sleep(1); + if (close(fd1) != 0) e(51); + if ((fd1 = open("fifo", O_RDONLY | O_NONBLOCK)) != 3) e(52); + if (read(fd1, buf, BUF_SIZE) != 18) e(53); + if (close(fd1) != 0) e(54); + if (wait(&stat_loc) == -1) e(55); + if (stat_loc != 0) e(56); /* The alarm went off? */ + } + + /* Let's try one too many. */ + System("rm -rf /tmp/sema.25"); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); /* Give child 20 seconds to live. */ + if ((fd1 = open("fifo", O_WRONLY)) != 3) e(57); + if (write(fd1, "I did see Elvis.\n", 18) != 18) e(58); + + /* Keep open till second reader is opened. */ + while (stat("/tmp/sema.25", &st) != 0) sleep(1); + if (close(fd1) != 0) e(59); + exit(0); + default: + if ((fd1 = open("fifo", O_RDONLY)) != 3) e(60); + if (read(fd1, buf, 2) != 2) e(61); + if (strncmp(buf, "I ", 2) != 0) e(62); + if (close(fd1) != 0) e(63); + if ((fd1 = open("fifo", O_RDONLY)) != 3) e(64); + + /* Signal second reader is open. */ + Creat("/tmp/sema.25"); + if (read(fd1, buf, 4) != 4) e(65); + if (strncmp(buf, "did ", 4) != 0) e(66); + if ((fd2 = open("fifo", O_RDONLY)) != 4) e(67); + if (read(fd2, buf, BUF_SIZE) != 12) e(68); + if (strncmp(buf, "see Elvis.\n", 12) != 0) e(69); + if (close(fd2) != 0) e(70); + if (close(fd1) != 0) e(71); + if (wait(&stat_loc) == -1) e(72); + if (stat_loc != 0) e(73); /* The alarm went off? */ + } + System("rm -rf fifo /tmp/sema.25"); + + /* O_TRUNC should have no effect on directroys. */ + System("mkdir dir; touch dir/f1 dir/f2 dir/f3"); + if ((fd1 = open("dir", O_WRONLY | O_TRUNC)) != -1) e(74); + if (errno != EISDIR) e(75); + close(fd1); + + /* Opening a directory for reading should be possible. */ + if ((fd1 = open("dir", O_RDONLY)) != 3) e(76); + if (close(fd1) != 0) e(77); + if (unlink("dir/f1") != 0) e(78); /* Should still be there. */ + if (unlink("dir/f2") != 0) e(79); + if (unlink("dir/f3") != 0) e(80); + if (rmdir("dir") != 0) e(81); + + if (!superuser) { + /* Test if O_CREAT is not usable to open files with the wrong mode */ + (void) umask(0200); /* nono has no */ + System("touch nono"); /* write bit */ + (void) umask(0000); + fd1 = open("nono", O_RDWR | O_CREAT, 0777); /* try to open */ + if (fd1 != -1) e(82); + if (errno != EACCES) e(83); /* but no access */ + } +} + +void test25d() +{ + int fd; + + subtest = 4; + + System("rm -rf ../DIR_25/*"); + + /* Test maximal file name length. */ + if ((fd = open(MaxName, O_RDWR | O_CREAT, 0777)) != 3) e(1); + if (close(fd) != 0) e(2); + MaxPath[strlen(MaxPath) - 2] = '/'; + MaxPath[strlen(MaxPath) - 1] = 'a'; /* make ././.../a */ + if ((fd = open(MaxPath, O_RDWR | O_CREAT, 0777)) != 3) e(3); + if (close(fd) != 0) e(4); + MaxPath[strlen(MaxPath) - 1] = '/'; /* make ././.../a */ +} + + +void test25e() +{ + int fd; + char *noread = "noread"; /* Name for unreadable file. */ + char *nowrite = "nowrite"; /* Same for unwritable. */ + int stat_loc; + + subtest = 5; + + System("rm -rf ../DIR_25/*"); + + mkdir("bar", 0777); /* make bar */ + + /* Check if no access on part of path generates the correct error. */ + System("chmod 677 bar"); /* rw-rwxrwx */ + if (open("bar/nono", O_RDWR | O_CREAT, 0666) != -1) e(1); + if (errno != EACCES) e(2); + + /* Ditto for no write permission. */ + System("chmod 577 bar"); /* r-xrwxrwx */ + if (open("bar/nono", O_RDWR | O_CREAT, 0666) != -1) e(3); + if (errno != EACCES) e(4); + + /* Clean up bar. */ + System("rm -rf bar"); + + /* Improper flags set on existing file. */ + System("touch noread; chmod 377 noread"); /* noread */ + if (open(noread, O_RDONLY) != -1) e(5); + if (open(noread, O_RDWR) != -1) e(6); + if (open(noread, O_RDWR | O_CREAT, 0777) != -1) e(7); + if (open(noread, O_RDWR | O_CREAT | O_TRUNC, 0777) != -1) e(8); + if ((fd = open(noread, O_WRONLY)) != 3) e(9); + if (close(fd) != 0) e(10); + System("touch nowrite; chmod 577 nowrite"); /* nowrite */ + if (open(nowrite, O_WRONLY) != -1) e(11); + if (open(nowrite, O_RDWR) != -1) e(12); + if (open(nowrite, O_RDWR | O_CREAT, 0777) != -1) e(13); + if (open(nowrite, O_RDWR | O_CREAT | O_TRUNC, 0777) != -1) e(14); + if ((fd = open(nowrite, O_RDONLY)) != 3) e(15); + if (close(fd) != 0) e(16); + if (superuser) { + /* If we can make a file ownd by some one else, test access again. */ + System("chmod 733 noread"); + System("chown bin noread"); + System("chgrp system noread"); + System("chmod 755 nowrite"); + System("chown bin nowrite"); + System("chgrp system nowrite"); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + setuid(1); + setgid(1); /* become daemon */ + if (open(noread, O_RDONLY) != -1) e(17); + if (open(noread, O_RDWR) != -1) e(18); + if (open(noread, O_RDWR | O_CREAT, 0777) != -1) e(19); + fd = open(noread, O_RDWR | O_CREAT | O_TRUNC, 0777); + if (fd != -1) e(20); + if ((fd = open(noread, O_WRONLY)) != 3) e(21); + if (close(fd) != 0) e(22); + if (open(nowrite, O_WRONLY) != -1) e(23); + if (open(nowrite, O_RDWR) != -1) e(24); + if (open(nowrite, O_RDWR | O_CREAT, 0777) != -1) e(25); + fd = open(nowrite, O_RDWR | O_CREAT | O_TRUNC, 0777); + if (fd != -1) e(26); + if ((fd = open(nowrite, O_RDONLY)) != 3) e(27); + if (close(fd) != 0) e(28); + exit(0); + default: + if (wait(&stat_loc) == -1) e(29); + } + } + + /* Clean up the noread and nowrite files. */ + System("rm -rf noread nowrite"); + + /* Test the O_EXCL flag. */ + System("echo > exists"); + if (open("exists", O_RDWR | O_CREAT | O_EXCL, 0777) != -1) e(30); + if (errno != EEXIST) e(31); + if (open("exists", O_RDONLY | O_CREAT | O_EXCL, 0777) != -1) e(32); + if (errno != EEXIST) e(33); + if (open("exists", O_WRONLY | O_CREAT | O_EXCL, 0777) != -1) e(34); + if (errno != EEXIST) e(35); + fd = open("exists", O_RDWR | O_CREAT | O_EXCL | O_TRUNC, 0777); + if (fd != -1) e(36); + if (errno != EEXIST) e(37); + fd = open("exists", O_RDONLY | O_CREAT | O_EXCL | O_TRUNC, 0777); + if (fd != -1) e(38); + if (errno != EEXIST) e(39); + fd = open("exists", O_WRONLY | O_CREAT | O_EXCL | O_TRUNC, 0777); + if (fd != -1) e(40); + if (errno != EEXIST) e(41); + + /* Test ToLongName and ToLongPath */ + if ((fd = open(ToLongName, O_RDWR | O_CREAT, 0777)) != 3) e(45); + if (close(fd) != 0) e(46); + ToLongPath[PATH_MAX - 2] = '/'; + ToLongPath[PATH_MAX - 1] = 'a'; + if ((fd = open(ToLongPath, O_RDWR | O_CREAT, 0777)) != -1) e(47); + if (errno != ENAMETOOLONG) e(48); + if (close(fd) != -1) e(49); + ToLongPath[PATH_MAX - 1] = '/'; +} + + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(ToLongName, MaxName); /* copy them Max to ToLong */ + strcpy(ToLongPath, MaxPath); + + ToLongName[NAME_MAX] = 'a'; + ToLongName[NAME_MAX + 1] = '\0'; /* extend ToLongName by one too many */ + ToLongPath[PATH_MAX - 1] = '/'; + ToLongPath[PATH_MAX] = '\0'; /* inc ToLongPath by one */ +} + + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_25"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test26.c b/test/test26.c new file mode 100644 index 000000000..2420eab67 --- /dev/null +++ b/test/test26.c @@ -0,0 +1,357 @@ +/* test26: link() unlink() Aithor: Jan-Mark Wams (jms@cs.vu.nl) */ + +/* + * Not tested readonly file systems + * Not tested fs full + * Not tested unlinking bussy files + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <limits.h> +#include <string.h> +#include <time.h> +#include <stdio.h> + +int errct = 0; +int subtest = 1; +int superuser; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; /* Same for path, both too long */ + +#define MAX_ERROR 4 +#define ITERATIONS 4 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test26a, (void)); +_PROTOTYPE(void test26b, (void)); +_PROTOTYPE(void test26c, (void)); +_PROTOTYPE(int stateq, (struct stat *stp1, struct stat *stp2)); +_PROTOTYPE(void makelongnames, (void)); +_PROTOTYPE(void e, (int __n)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 26 "); + fflush(stdout); + System("rm -rf DIR_26; mkdir DIR_26"); + Chdir("DIR_26"); + superuser = (getuid() == 0); + makelongnames(); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test26a(); + if (m & 0002) test26b(); + if (m & 0004) test26c(); + } + quit(); +} + +void test26a() +{ /* Test normal operation. */ + struct stat st1, st2, st3; + time_t time1; + + subtest = 1; + + /* Clean up any residu. */ + System("rm -rf ../DIR_26/*"); + + System("touch foo"); /* make source file */ + Stat("foo", &st1); /* get info of foo */ + Stat(".", &st2); /* and the cwd */ + time(&time1); + while (time1 >= time((time_t *)0)) + ; /* wait a sec */ + if (link("foo", "bar") != 0) e(1); /* link foo to bar */ + Stat("foo", &st3); /* get new status */ + if (st1.st_nlink + 1 != st3.st_nlink) e(2); /* link count foo up 1 */ +#ifndef V1_FILESYSTEM + if (st1.st_ctime >= st3.st_ctime) e(3); /* check stattime changed */ +#endif + Stat(".", &st1); /* get parend dir info */ + if (st2.st_ctime >= st1.st_ctime) e(4); /* ctime and mtime */ + if (st2.st_mtime >= st1.st_mtime) e(5); /* should be updated */ + Stat("bar", &st2); /* get info of bar */ + if (st2.st_nlink != st3.st_nlink) e(6); /* link count foo == bar */ + if (st2.st_ino != st3.st_ino) e(7); /* ino should be same */ + if (st2.st_mode != st3.st_mode) e(8); /* check mode same */ + if (st2.st_uid != st3.st_uid) e(9); /* check uid same */ + if (st2.st_gid != st3.st_gid) e(10); /* check gid same */ + if (st2.st_size != st3.st_size) e(11); /* check size */ + if (st2.st_ctime != st3.st_ctime) e(12); /* check ctime */ + if (st2.st_atime != st3.st_atime) e(13); /* check atime */ + if (st2.st_mtime != st3.st_mtime) e(14); /* check mtime */ + Stat("foo", &st1); /* get fooinfo */ + Stat(".", &st2); /* get dir info */ + time(&time1); + while (time1 >= time((time_t *)0)) + ; /* wait a sec */ + if (unlink("bar") != 0) e(15);/* rm bar */ + if (stat("bar", &st2) != -1) e(16); /* it's gone */ + Stat("foo", &st3); /* get foo again */ + if (st1.st_nlink != st3.st_nlink + 1) e(17); /* link count back to normal */ +#ifndef V1_FILESYSTEM + if (st1.st_ctime >= st3.st_ctime) e(18); /* check ctime */ +#endif + Stat(".", &st3); /* get parend dir info */ + if (st2.st_ctime >= st3.st_ctime) e(19); /* ctime and mtime */ + if (st2.st_mtime >= st3.st_mtime) e(20); /* should be updated */ +} + +void test26b() +{ + register int nlink; + char *bar = "bar.xxxxx"; /* xxxxx will hold a number up to 99999 */ + struct stat st, st2; + + subtest = 2; + + /* Clean up any residu. */ + System("rm -rf ../DIR_26/*"); + + /* Test what happens if we make LINK_MAX number of links. */ + System("touch foo"); +if (LINK_MAX > 127) { + printf("[skip] "); /* takes too many resources */ +} else { + if (LINK_MAX >= 99999) e(1); /* check "xxxxx" are enough */ + for (nlink = 2; nlink <= LINK_MAX; nlink++) { + bar[4] = (char) ((nlink / 10000) % 10) + '0'; + bar[5] = (char) ((nlink / 1000) % 10) + '0'; + bar[6] = (char) ((nlink / 100) % 10) + '0'; + bar[7] = (char) ((nlink / 10) % 10) + '0'; + bar[8] = (char) (nlink % 10) + '0'; + if (link("foo", bar) != 0) e(2); + Stat(bar, &st); + if (st.st_nlink != nlink) e(3); + Stat("foo", &st); + if (st.st_nlink != nlink) e(4); + } + + /* Check if we have LINK_MAX links that are all the same. */ + Stat("foo", &st); + if (st.st_nlink != LINK_MAX) e(5); + for (nlink = 2; nlink <= LINK_MAX; nlink++) { + bar[4] = (char) ((nlink / 10000) % 10) + '0'; + bar[5] = (char) ((nlink / 1000) % 10) + '0'; + bar[6] = (char) ((nlink / 100) % 10) + '0'; + bar[7] = (char) ((nlink / 10) % 10) + '0'; + bar[8] = (char) (nlink % 10) + '0'; + Stat(bar, &st2); + if (!stateq(&st, &st2)) e(6); + } + + /* Test no more links are possible. */ + if (link("foo", "nono") != -1) e(7); + if (stat("nono", &st) != -1) e(8); + Stat("foo", &st); + if (st.st_nlink != LINK_MAX) e(9); /* recheck the number of links */ + + /* Now unlink() the bar.### files */ + for (nlink = LINK_MAX; nlink >= 2; nlink--) { + bar[4] = (char) ((nlink / 100) % 10) + '0'; + bar[5] = (char) ((nlink / 10) % 10) + '0'; + bar[6] = (char) (nlink % 10) + '0'; + Stat(bar, &st); + if (st.st_nlink != nlink) e(10); + Stat("foo", &st2); + if (!stateq(&st, &st2)) e(11); + if (unlink(bar) != 0) e(12); + } +} + Stat("foo", &st); + if (st.st_nlink != 1) e(13); /* number of links back to 1 */ + + /* Test max path ed. */ + if (link("foo", MaxName) != 0) e(14); /* link to MaxName */ + if (unlink(MaxName) != 0) e(15); /* and remove it */ + MaxPath[strlen(MaxPath) - 2] = '/'; + MaxPath[strlen(MaxPath) - 1] = 'a'; /* make ././.../a */ + if (link("foo", MaxPath) != 0) e(16); /* it should be */ + if (unlink(MaxPath) != 0) e(17); /* (un)linkable */ + + System("rm -f ../DIR_26/*"); /* clean cwd */ +} + +void test26c() +{ + subtest = 3; + + /* Clean up any residu. */ + System("rm -rf ../DIR_26/*"); + + /* Check some simple things. */ + if (link("bar/nono", "nono") != -1) e(1); /* nonexistent */ + if (errno != ENOENT) e(2); + Chdir(".."); + System("touch DIR_26/foo"); + System("chmod 677 DIR_26"); /* make inaccesable */ + if (!superuser) { + if (unlink("DIR_26/foo") != -1) e(3); + if (errno != EACCES) e(4); + } + if (link("DIR_26/bar/nono", "DIR_26/nono") != -1) e(5); /* nono no be */ + if (superuser) { + if (errno != ENOENT) e(6); /* su has access */ + } + if (!superuser) { + if (errno != EACCES) e(7); /* we don't ;-) */ + } + System("chmod 577 DIR_26"); /* make unwritable */ + if (superuser) { + if (link("DIR_26/foo", "DIR_26/nono") != 0) e(8); + if (unlink("DIR_26/nono") != 0) e(9); + } + if (!superuser) { + if (link("DIR_26/foo", "DIR_26/nono") != -1) e(10); + if (errno != EACCES) e(11); + if (unlink("DIR_26/foo") != -1) e(12); /* try to rm foo/foo */ + if (errno != EACCES) e(13); + } + System("chmod 755 DIR_26"); /* back to normal */ + Chdir("DIR_26"); + + /* Too-long path and name test */ + ToLongPath[strlen(ToLongPath) - 2] = '/'; + ToLongPath[strlen(ToLongPath) - 1] = 'a'; /* make ././.../a */ + if (link("foo", ToLongPath) != -1) e(18); /* path is too long */ + if (errno != ENAMETOOLONG) e(19); + if (unlink(ToLongPath) != -1) e(20); /* path is too long */ + if (errno != ENAMETOOLONG) e(21); + if (link("foo", "foo") != -1) e(22); /* try linking foo to foo */ + if (errno != EEXIST) e(23); + if (link("foo", "bar") != 0) e(24); /* make a link to bar */ + if (link("foo", "bar") != -1) e(25); /* try linking to bar again */ + if (errno != EEXIST) e(26); + if (link("foo", "bar") != -1) e(27); /* try linking to bar again */ + if (errno != EEXIST) e(28); + if (unlink("nono") != -1) e(29); /* try rm <not exist> */ + if (errno != ENOENT) e(30); + if (unlink("") != -1) e(31); /* try unlinking empty */ + if (errno != ENOENT) e(32); + if (link("foo", "") != -1) e(33); /* try linking to "" */ + if (errno != ENOENT) e(34); + if (link("", "foo") != -1) e(35); /* try linking "" */ + if (errno != ENOENT) e(36); + if (link("", "") != -1) e(37);/* try linking "" to "" */ + if (errno != ENOENT) e(38); + if (link("/foo/bar/foo", "a") != -1) e(39); /* try no existing path */ + if (errno != ENOENT) e(40); + if (link("foo", "/foo/bar/foo") != -1) e(41); /* try no existing path */ + if (errno != ENOENT) e(42); + if (link("/a/b/c", "/d/e/f") != -1) e(43); /* try no existing path */ + if (errno != ENOENT) e(44); + if (link("abc", "a") != -1) e(45); /* try no existing file */ + if (errno != ENOENT) e(46); + if (link("foo/bar", "bar") != -1) e(47); /* foo is a file */ + if (errno != ENOTDIR) e(48); + if (link("foo", "foo/bar") != -1) e(49); /* foo is not a dir */ + if (errno != ENOTDIR) e(50); + if (unlink("foo/bar") != -1) e(51); /* foo still no dir */ + if (errno != ENOTDIR) e(52); + if (!superuser) { + if (link(".", "root") != -1) e(55); + if (errno != EPERM) e(56); /* noroot can't */ + if (unlink("root") != -1) e(57); + if (errno != ENOENT) e(58); + } + if (mkdir("dir", 0777) != 0) e(59); + if (superuser) { + if (rmdir("dir") != 0) e(63); + } + if (!superuser) { + if (unlink("dir") != -1) e(64); + if (errno != EPERM) e(65); /* that ain't w'rkn */ + if (rmdir("dir") != 0) e(66); /* that's the way to do it */ + } +} + +int stateq(stp1, stp2) +struct stat *stp1, *stp2; +{ + if (stp1->st_dev != stp2->st_dev) return 0; + if (stp1->st_ino != stp2->st_ino) return 0; + if (stp1->st_mode != stp2->st_mode) return 0; + if (stp1->st_nlink != stp2->st_nlink) return 0; + if (stp1->st_uid != stp2->st_uid) return 0; + if (stp1->st_gid != stp2->st_gid) return 0; + if (stp1->st_rdev != stp2->st_rdev) return 0; + if (stp1->st_size != stp2->st_size) return 0; + if (stp1->st_atime != stp2->st_atime) return 0; + if (stp1->st_mtime != stp2->st_mtime) return 0; + if (stp1->st_ctime != stp2->st_ctime) return 0; + return 1; +} + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(ToLongName, MaxName); /* copy them Max to ToLong */ + strcpy(ToLongPath, MaxPath); + + ToLongName[NAME_MAX] = 'a'; + ToLongName[NAME_MAX + 1] = '\0'; /* extend ToLongName by one + * too many */ + ToLongPath[PATH_MAX - 1] = '/'; + ToLongPath[PATH_MAX] = '\0'; /* inc ToLongPath by one */ +} + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + chdir(".."); + system("rm -rf DIR_26"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test27.c b/test/test27.c new file mode 100644 index 000000000..7145e2a70 --- /dev/null +++ b/test/test27.c @@ -0,0 +1,351 @@ +/* test27: stat() fstat() Author: Jan-Mark Wams (jms@cs.vu.nl) */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdlib.h> +#include <errno.h> +#include <limits.h> +#include <string.h> +#include <limits.h> +#include <time.h> +#include <stdio.h> + +#define MODE_MASK (S_IRWXU | S_IRWXG | S_IRWXO | S_ISUID | S_ISGID) +#define MAX_ERROR 4 +#define ITERATIONS 2 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) + +int errct = 0; +int subtest = 1; +int superuser; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test27a, (void)); +_PROTOTYPE(void test27b, (void)); +_PROTOTYPE(void test27c, (void)); +_PROTOTYPE(void makelongnames, (void)); +_PROTOTYPE(void e, (int __n)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 27 "); + fflush(stdout); + System("rm -rf DIR_27; mkdir DIR_27"); + Chdir("DIR_27"); + superuser = (getuid() == 0); + makelongnames(); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test27a(); + if (m & 0002) test27b(); + if (m & 0004) test27c(); + } + quit(); +} + + +void test27a() +{ /* Test Normal operation. */ + struct stat st1, st2; + time_t time1, time2; + int fd, pfd[2]; + + subtest = 1; + + time(&time1); /* get time before */ + while (time1 >= time((time_t *)0)) + ; /* Wait for time to change. */ + System("echo 7bytes > foo; chmod 4750 foo"); + if (stat("foo", &st1) != 0) e(1); /* get foo's info */ + time(&time2); + while (time2 >= time((time_t *)0)) + ; /* Wait for next second. */ + time(&time2); /* get time after */ + if ((st1.st_mode & MODE_MASK) != 04750) e(2); + if (st1.st_nlink != 1) e(3); /* check stat */ + if (st1.st_uid != geteuid()) e(4); +#if defined(NGROUPS_MAX) && NGROUPS_MAX == 0 + if (st1.st_gid != getegid()) e(5); +#endif /* defined(NGROUPS_MAX) && NGROUPS_MAX == 0 */ + if (st1.st_size != (size_t) 7) e(6); + if (st1.st_atime <= time1) e(7); + if (st1.st_atime >= time2) e(8); + if (st1.st_ctime <= time1) e(9); + if (st1.st_ctime >= time2) e(10); + if (st1.st_mtime <= time1) e(11); + if (st1.st_mtime >= time2) e(12); + + /* Compair stat and fstat. */ + System("echo 7bytes > bar"); + fd = open("bar", O_RDWR | O_APPEND); /* the bar is open! */ + if (fd != 3) e(13); /* should be stderr + 1 */ + if (stat("bar", &st1) != 0) e(14); /* get bar's info */ + if (fstat(fd, &st2) != 0) e(15); /* get bar's info */ + + /* St1 en st2 should be the same. */ + if (st1.st_dev != st2.st_dev) e(16); + if (st1.st_ino != st2.st_ino) e(17); + if (st1.st_mode != st2.st_mode) e(18); + if (st1.st_nlink != st2.st_nlink) e(19); + if (st1.st_uid != st2.st_uid) e(20); + if (st1.st_gid != st2.st_gid) e(21); + if (st1.st_size != st2.st_size) e(22); + if (st1.st_atime != st2.st_atime) e(23); + if (st1.st_ctime != st2.st_ctime) e(24); + if (st1.st_mtime != st2.st_mtime) e(25); + time(&time1); /* wait a sec. */ + while (time1 >= time((time_t *)0)) + ; + System("chmod 755 bar"); /* chainge mode */ + System("rm -f foobar; ln bar foobar"); /* chainge # links */ + if (write(fd, "foo", 4) != 4) e(26); /* write a bit (or two) */ + if (stat("bar", &st2) != 0) e(27); /* get new info */ + if (st2.st_dev != st1.st_dev) e(28); + if (st2.st_ino != st1.st_ino) e(29); /* compair the fealds */ + if ((st2.st_mode & MODE_MASK) != 0755) e(30); + if (!S_ISREG(st2.st_mode)) e(31); + if (st2.st_nlink != st1.st_nlink + 1) e(32); + if (st2.st_uid != st1.st_uid) e(33); + if (st2.st_gid != st1.st_gid) e(34); + if (st2.st_size != (size_t) 11) e(35); + if (st2.st_atime != st1.st_atime) e(36); + if (st2.st_ctime <= st1.st_ctime) e(37); + if (st2.st_mtime <= st1.st_mtime) e(38); + if (close(fd) != 0) e(39); /* sorry the bar is closed */ + + /* Check special file. */ + if (stat("/dev/tty", &st1) != 0) e(40); + if (!S_ISCHR(st1.st_mode)) e(41); +#ifdef _MINIX + if (stat("/dev/ram", &st1) != 0) e(42); + if (!S_ISBLK(st1.st_mode)) e(43); +#endif + + /* Check fifos. */ + time(&time1); + while (time1 >= time((time_t *)0)) + ; + if (mkfifo("fifo", 0640) != 0) e(44); + if (stat("fifo", &st1) != 0) e(45); /* get fifo's info */ + time(&time2); + while (time2 >= time((time_t *)0)) + ; + time(&time2); + if (!S_ISFIFO(st1.st_mode)) e(46); + if (st1.st_nlink != 1) e(47); /* check the stat info */ + if (st1.st_uid != geteuid()) e(48); +#if defined(NGROUPS_MAX) && NGROUPS_MAX == 0 + if (st1.st_gid != getegid()) e(49); +#endif /* defined(NGROUPS_MAX) && NGROUPS_MAX == 0 */ + if (st1.st_size != (size_t) 0) e(50); + if (st1.st_atime <= time1) e(51); + if (st1.st_atime >= time2) e(52); + if (st1.st_ctime <= time1) e(53); + if (st1.st_ctime >= time2) e(54); + if (st1.st_mtime <= time1) e(55); + if (st1.st_mtime >= time2) e(56); + + /* Note: the st_mode of a fstat on a pipe should contain a isfifo bit. */ + /* Check pipes. */ + time(&time1); + while (time1 >= time((time_t *)0)) + ; + if (pipe(pfd) != 0) e(57); + if (fstat(pfd[0], &st1) != 0) e(58); /* get pipe input info */ + time(&time2); + while (time2 >= time((time_t *)0)) + ; + time(&time2); + if (!(S_ISFIFO(st1.st_mode))) e(59); /* check stat struct */ + if (st1.st_uid != geteuid()) e(60); + if (st1.st_gid != getegid()) e(61); + if (st1.st_size != (size_t) 0) e(62); + if (st1.st_atime <= time1) e(63); + if (st1.st_atime >= time2) e(64); + if (st1.st_ctime <= time1) e(65); + if (st1.st_ctime >= time2) e(66); + if (st1.st_mtime <= time1) e(67); + if (st1.st_mtime >= time2) e(68); + if (fstat(pfd[1], &st1) != 0) e(69); /* get pipe output info */ + if (!(S_ISFIFO(st1.st_mode))) e(70); + if (st1.st_uid != geteuid()) e(71); + if (st1.st_gid != getegid()) e(72); + if (st1.st_size != (size_t) 0) e(73); + if (st1.st_atime < time1) e(74); + if (st1.st_atime > time2) e(75); + if (st1.st_ctime < time1) e(76); + if (st1.st_ctime > time2) e(77); + if (st1.st_mtime < time1) e(78); + if (st1.st_mtime > time2) e(79); + if (close(pfd[0]) != 0) e(80); + if (close(pfd[1]) != 0) e(81);/* close pipe */ + + /* Check dirs. */ + time(&time1); + while (time1 >= time((time_t *)0)) + ; + System("mkdir dir"); + if (stat("dir", &st1) != 0) e(82); /* get dir info */ + time(&time2); + while (time2 >= time((time_t *)0)) + ; + time(&time2); + if (!(S_ISDIR(st1.st_mode))) e(83); /* check stat struct */ + if (st1.st_uid != geteuid()) e(84); +#if defined(NGROUPS_MAX) && NGROUPS_MAX == 0 + if (st1.st_gid != getegid()) e(85); +#endif /* defined(NGROUPS_MAX) && NGROUPS_MAX == 0 */ + if (st1.st_atime < time1) e(86); + if (st1.st_atime > time2) e(87); + if (st1.st_ctime < time1) e(88); + if (st1.st_ctime > time2) e(89); + if (st1.st_mtime < time1) e(90); + if (st1.st_mtime > time2) e(91); + System("rm -rf ../DIR_27/*"); +} + +void test27b() +{ /* Test maxima. */ + struct stat st; + int fd; + + subtest = 2; + + /* Check stats on maximum length files names. */ + if (mkdir(MaxName, 0777) != 0) e(1); + if (stat(MaxName, &st) != 0) e(2); + if ((fd = open(MaxName, O_RDONLY)) != 3) e(3); + if (fstat(fd, &st) != 0) e(4); + if (close(fd) != 0) e(5); + if (rmdir(MaxName) != 0) e(6); + if (stat(MaxPath, &st) != 0) e(7); + if ((fd = open(MaxPath, O_RDONLY)) != 3) e(8); + if (fstat(fd, &st) != 0) e(9); + if (close(fd) != 0) e(10); + System("rm -rf ../DIR_27/*"); +} + +void test27c() +{ /* Test error response. */ + struct stat st; + int fd, i; + + subtest = 3; + + System("echo Hi > foo"); /* Make a file called foo. */ + /* Check if a un searchable dir is handled ok. */ + Chdir(".."); /* cd .. */ + System("chmod 677 DIR_27"); /* no search permission */ + if (stat("DIR_27/nono", &st) != -1) e(1); + if (superuser) { + if (errno != ENOENT) e(2); /* su has access */ + } + if (!superuser) { + if (errno != EACCES) e(3); /* we don't ;-) */ + } + System("chmod 777 DIR_27"); + Chdir("DIR_27"); /* back to test dir */ + + /* Check on ToLongName etc. */ +#ifdef _POSIX_NO_TRUNC +# if _POSIX_NO_TRUNC - 0 != -1 + if (stat(ToLongName, &st) != -1) e(4); /* name is too long */ + if (errno != ENAMETOOLONG) e(5); +# endif +#else +# include "error, this case requires dynamic checks and is not handled" +#endif + if (stat(ToLongPath, &st) != -1) e(6); /* path is too long */ + if (errno != ENAMETOOLONG) e(7); + + /* Test some common errors. */ + if (stat("nono", &st) != -1) e(8); /* nono nonexistent */ + if (errno != ENOENT) e(9); + if (stat("", &st) != -1) e(10); /* try empty */ + if (errno != ENOENT) e(11); + if (stat("foo/bar", &st) != -1) e(12); /* foo is a file */ + if (errno != ENOTDIR) e(13); + + /* Test fstat on file descriptors that are not open. */ + for (i = 3; i < 6; i++) { + if (fstat(i, &st) != -1) e(14); + if (errno != EBADF) e(15); + } + + /* Test if a just closed file is `fstat()'-able. */ + if ((fd = open("foo", O_RDONLY)) != 3) e(16); /* open foo */ + if (fstat(fd, &st) != 0) e(17); /* get stat */ + if (close(fd) != 0) e(18); /* close it */ + if (fstat(fd, &st) != -1) e(19); /* get stat */ + if (errno != EBADF) e(20); + System("rm -rf ../DIR_27/*"); +} + + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(ToLongName, MaxName); /* copy them Max to ToLong */ + strcpy(ToLongPath, MaxPath); + + ToLongName[NAME_MAX] = 'a'; + ToLongName[NAME_MAX + 1] = '\0'; /* extend ToLongName by one too many */ + ToLongPath[PATH_MAX - 1] = '/'; + ToLongPath[PATH_MAX] = '\0'; /* inc ToLongPath by one */ +} + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_27"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test28.c b/test/test28.c new file mode 100644 index 000000000..d22b0180d --- /dev/null +++ b/test/test28.c @@ -0,0 +1,449 @@ + /* test28: mkdir() rmdir() Author: Jan-Mark Wams (jms@cs.vu.nl) */ + +/* +** Not tested readonly file systems (EROFS.) +** Not tested fs full (ENOSPC.) +** Not really tested EBUSY. +** Not tested unlinking busy directories. +*/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <dirent.h> +#include <limits.h> +#include <time.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define ITERATIONS 2 + +#define DIRENT0 ((struct dirent *) NULL) + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) + +int errct = 0; +int subtest = 1; +int superuser; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test28a, (void)); +_PROTOTYPE(void test28c, (void)); +_PROTOTYPE(void test28b, (void)); +_PROTOTYPE(void makelongnames, (void)); +_PROTOTYPE(void e, (int n)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 28 "); + fflush(stdout); + superuser = (getuid() == 0); + makelongnames(); + system("chmod 777 DIR_28/* DIR_28/*/* > /dev/null 2>&1"); + System("rm -rf DIR_28; mkdir DIR_28"); + Chdir("DIR_28"); + umask(0000); /* no umask */ + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test28a(); + if (m & 0002) test28b(); + if (m & 0004) test28c(); + } + quit(); +} + + +void test28a() +{ + int mode; /* used in for loop */ + struct stat st; + time_t time1, time2; + DIR *dirp; + struct dirent *dep; + int dot = 0, dotdot = 0; + + subtest = 1; + + System("rm -rf foo /tmp/foo");/* clean up junk */ + + /* Check relative path names */ + if (mkdir("./foo", 0777) != 0) e(1); /* make a dir foo */ + if (mkdir("./foo/bar", 0777) != 0) e(2); /* make foo/bar */ + if (rmdir("foo/bar") != 0) e(3); /* delete bar */ + if (mkdir("foo/../foo/bar", 0777) != 0) e(4); /* make bar again */ + if (rmdir("./foo/bar") != 0) e(5); /* and remove again */ + + /* Foo should be empty (ie. contain only "." and ".." */ + if ((dirp = opendir("foo")) == (DIR *) NULL) e(6); /* open foo */ + if ((dep = readdir(dirp)) == DIRENT0) e(7); /* get first entry */ + if (strcmp(dep->d_name, ".") == 0) dot += 1; /* record what it is */ + if (strcmp(dep->d_name, "..") == 0) dotdot += 1; + if ((dep = readdir(dirp)) == DIRENT0) e(8); /* get second entry */ + if (strcmp(dep->d_name, ".") == 0) dot += 1; /* record again */ + if (strcmp(dep->d_name, "..") == 0) dotdot += 1; + if ((dep = readdir(dirp)) != DIRENT0) e(9); /* no 3d entry */ + if (dot == 1 && dotdot != 1) e(10); /* only . and .. */ + if (closedir(dirp) != 0) e(11); /* close foo */ + if (rmdir("./foo") != 0) e(12); /* remove dir foo */ + + /* Check absolute path names */ + if (mkdir("/tmp/foo", 0777) != 0) e(13); + if (mkdir("/tmp/foo/bar", 0777) != 0) e(14); + if (rmdir("/tmp/foo/bar") != 0) e(15); /* make some dirs */ + if (rmdir("/tmp/foo") != 0) e(16); + + /* Check the mode arument for mkdir() */ + for (mode = 0; mode <= 0777; mode++) { + if (mkdir("foo", mode) != 0) e(17); /* make foo */ + if (stat("foo", &st) != 0) e(18); + if ((st.st_mode & 0777) != mode) e(19); /* check it's mode */ + if (rmdir("foo") != 0) e(20); /* and remove it */ + } + + /* Check the stat */ + time(&time1); + while (time1 >= time((time_t *)0)) + ; + if (mkdir("foo", 0765) != 0) e(21); /* make foo */ + if (stat("foo", &st) != 0) e(22); + time(&time2); + while (time2 >= time((time_t *)0)) + ; + time(&time2); + if (st.st_nlink != 2) e(23); + if (st.st_uid != geteuid()) e(24); + if (st.st_gid != getegid()) e(25); + if (st.st_size < 0) e(26); + if ((st.st_mode & 0777) != 0765) e(27); + if (st.st_atime <= time1) e(28); + if (st.st_atime >= time2) e(29); + if (st.st_ctime <= time1) e(30); + if (st.st_ctime >= time2) e(31); + if (st.st_mtime <= time1) e(32); + if (st.st_mtime >= time2) e(33); + + /* Check if parent is updated */ + if (stat(".", &st) != 0) e(34); + time(&time2); + while (time2 >= time((time_t *)0)) + ; + time(&time2); + if (st.st_ctime <= time1) e(35); + if (st.st_ctime >= time2) e(36); + if (st.st_mtime <= time1) e(37); + if (st.st_mtime >= time2) e(38); + time(&time1); + while (time1 >= time((time_t *)0)) + ; + if (rmdir("foo") != 0) e(39); + if (stat(".", &st) != 0) e(40); + time(&time2); + while (time2 >= time((time_t *)0)) + ; + time(&time2); + if (st.st_ctime <= time1) e(41); + if (st.st_ctime >= time2) e(42); + if (st.st_mtime <= time1) e(43); + if (st.st_mtime >= time2) e(44); +} + + +void test28b() +{ /* Test critical values. */ + struct stat st; + DIR *dirp; + struct dirent *dep; + int fd; /* file descriptor */ + int other = 0, dot = 0, dotdot = 0; /* dirent counters */ + int rmdir_result; /* tmp var */ + nlink_t nlink; + static char bar[] = "foo/bar.xxxxx"; + int stat_loc; + + subtest = 2; + + System("rm -rf ../DIR_28/*"); + + /* Check funny but valid path names */ + if (mkdir("/../../..////.//../tmp/foo/", 0777) != 0) e(1); + if (mkdir("/tmp/foo//////..//foo//../foo/bar/", 0777) != 0) e(2); + if (rmdir("///tmp/..//tmp/foo/bar//../..//foo/bar") != 0) e(3); + if (mkdir("///tmp/foo/foobar//", 0777) != 0) e(4); + if (rmdir("/tmp/foo/foobar//") != 0) e(5); + if (rmdir("/.././/././/tmp/foo///////////////") != 0) e(6); + if (rmdir("/tmp/foo") != -1) e(7); /* try again */ + + if (LINK_MAX >= 99999) e(8); /* "xxxxx" in filename not long inough */ + + /* Test max path ed. */ + if (mkdir(MaxName, 0777) != 0) e(9); /* make dir MaxName */ + if (rmdir(MaxName) != 0) e(10); /* and remove it */ + MaxPath[strlen(MaxPath) - 2] = '/'; /* convert MaxPath */ + MaxPath[strlen(MaxPath) - 1] = 'a'; /* to ././.../a */ + if (mkdir(MaxPath, 0777) != 0) e(11); /* it should be */ + if (rmdir(MaxPath) != 0) e(12); /* ok */ + + /* Test too long path ed. */ + if (mkdir(ToLongName, 0777) != 0) e(17); /* Try ToLongName */ + if (rmdir(ToLongName) != 0) e(18); /* and remove it */ + ToLongPath[strlen(ToLongPath) - 2] = '/'; /* make ToLongPath */ + ToLongPath[strlen(ToLongPath) - 1] = 'a'; /* contain ././.../a */ + if (mkdir(ToLongPath, 0777) != -1) e(19); /* it should */ + if (errno != ENAMETOOLONG) e(20); /* not be ok */ + if (rmdir(ToLongPath) != -1) e(21); + if (errno != ENAMETOOLONG) e(22); + + /* Test what happens if the parent link count > LINK_MAX. */ + if (mkdir("foo", 0777) != 0) e(23); /* make foo */ +if (LINK_MAX > 127) { + printf("[skip] "); /* takes to many resources */ +} else { + for (nlink = 2; nlink < LINK_MAX; nlink++) { /* make all */ + bar[8] = (char) ((nlink / 10000) % 10) + '0'; + bar[9] = (char) ((nlink / 1000) % 10) + '0'; + bar[10] = (char) ((nlink / 100) % 10) + '0'; + bar[11] = (char) ((nlink / 10) % 10) + '0'; + bar[12] = (char) (nlink % 10) + '0'; + if (mkdir(bar, 0777) != 0) { + printf("nlink=%u\n", nlink); + e(24); + } + } + if (stat("foo", &st) != 0) e(25); /* foo now */ + if (st.st_nlink != LINK_MAX) e(26); /* is full */ + if (mkdir("foo/nono", 0777) != -1) e(27); /* no more */ + if (errno != EMLINK) e(28); /* entrys. */ + System("rm -rf foo/nono"); /* Just in case. */ + + /* Test if rmdir removes only empty dirs */ + if (rmdir("foo") != -1) e(29);/* not empty */ + if (errno != EEXIST && errno != ENOTEMPTY) e(30); +} + /* Test if rmdir removes a dir with an empty file (it shouldn't.) */ + System("rm -rf foo"); /* cleanup */ + if (mkdir("foo", 0777) != 0) e(31); + System("> foo/empty"); /* > empty */ + if (rmdir("foo") != -1) e(32);/* not empty */ + if (errno != EEXIST && errno != ENOTEMPTY) e(33); + if (unlink("foo/empty") != 0) e(34); /* rm empty */ + + /* See what happens if foo is linked. */ + if (superuser) { + if (link("foo", "footoo") != 0) e(35); /* foo still */ + if (rmdir("footoo") != 0) e(36); /* exist */ + if (chdir("footoo") != -1) e(37); /* footoo */ + if (errno != ENOENT) e(38); /* is gone */ + } +#ifdef _MINIX + /* Some implementations might allow users to link directories. */ + if (!superuser) { + if (link("foo", "footoo") != -1) e(39); + if (errno != EPERM) e(40); + if (unlink("foo") != -1) e(41); + if (errno != EPERM) e(42); + } +#endif + + /* See if ".." and "." are removed from the dir, and if it is + * unwriteable * Note, we can not remove any files in the PARENT + * process, because this * will make readdir unpredicatble. (see + * 1003.1 page 84 line 30.) However * removal of the directory is + * not specified in the standard. + */ + System("rm -rf /tmp/sema[12].07"); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + + case 0: + alarm(20); + if ((fd = open("foo", O_RDONLY)) <= 2) e(43); /* open */ + if ((dirp = opendir("foo")) == (DIR *) NULL) e(44); /* opendir */ + /* UpA downB */ + system(">/tmp/sema1.07; while test -f /tmp/sema1.07; do sleep 1;done"); + while ((dep = readdir(dirp)) != DIRENT0) { + if (strcmp(dep->d_name, "..") == 0) + dotdot += 1; + else if (strcmp(dep->d_name, ".") == 0) + dot += 1; + else + other += 1; + } + if (dotdot != 0) e(45); /* no entrys */ + if (dot != 0) e(46); /* shoul be */ + if (other != 0) e(47); /* left or */ + + /* No new files (entrys) are allowed on foo */ + if (creat("foo/nono", 0777) != -1) e(48); /* makeable */ + if (closedir(dirp) != 0) e(49); /* close foo */ + system("while test ! -f /tmp/sema2.07; do sleep 1; done"); /* downA */ + System("rm -f /tmp/sema2.07"); /* clean up */ + + /* Foo still exist, so we should be able to get a fstat */ + if (fstat(fd, &st) != 0) e(50); + if (st.st_nlink != (nlink_t) 0) e(51); /* 0 left */ + if (close(fd) != 0) e(52); /* last one */ + exit(0); + + default: + system("while test ! -f /tmp/sema1.07; do sleep 1; done"); /* downA */ + if (rmdir("foo") != 0) e(53); /* cleanerup */ + System("rm -f /tmp/sema1.07"); /* upB */ + if (chdir("foo") != -1) e(54); /* it should */ + if (errno != ENOENT) e(55); /* be gone */ + System("> /tmp/sema2.07"); /* upA */ + if (wait(&stat_loc) == -1) e(56); + if (stat_loc != 0) e(57); + } + + /* See if foo isn't accessible any more */ + if (chdir("foo") != -1) e(58); + if (errno != ENOENT) e(59); + + /* Let's see if we can get a EBUSSY..... */ + if (mkdir("foo", 0777) != 0) e(60); /* mkdir foo */ + System("rm -f /tmp/sema.07"); /* unness */ + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + if (chdir("foo") != 0) e(61); /* child goes */ + System("> /tmp/sema.07"); /* upA */ + system("while test -f /tmp/sema.07; do sleep 1; done"); /* downB */ + sleep(1); + exit(0); + default: + system("while test ! -f /tmp/sema.07; do sleep 1; done"); /* downA */ + rmdir_result = rmdir("foo"); /* try remove */ + if (rmdir_result == -1) { /* if it failed */ + if (errno != EBUSY) e(62); /* foo is busy */ + } else { + if (rmdir_result != 0) e(63); + if (rmdir("foo") != -1) e(64); /* not removable */ + if (errno != ENOENT) e(65); /* again. */ + if (chdir("foo") != -1) e(66); /* we can't go */ + if (errno != ENOENT) e(67); /* there any more */ + if (mkdir("foo", 0777) != 0) e(68); /* we can remake foo */ + } + System("rm -f /tmp/sema.07"); /* upB */ + if (wait(&stat_loc) == -1) e(69); + if (stat_loc != 0) e(70); + } + if (rmdir("foo") != 0) e(71); /* clean up */ +} + +void test28c() +{ /* Test error handeling. */ + subtest = 3; + + System("rm -rf ../DIR_28/*"); + System("rm -rf foo /tmp/foo");/* clean up junk */ + + /* Test common errors */ + if (mkdir("foo", 0777) != 0) e(1); /* mkdir shouldn't fail */ + if (mkdir("foo", 0777) != -1) e(2); /* should fail the 2d time */ + if (errno != EEXIST) e(3); /* because it exists already */ + if (rmdir("foo") != 0) e(4); /* rmdir shouldn't fail */ + if (rmdir("foo") != -1) e(5); /* but it should now because */ + if (errno != ENOENT) e(6); /* it's gone the 1st time */ + /* Test on access etc. */ + if (mkdir("foo", 0777) != 0) e(7); + if (mkdir("foo/bar", 0777) != 0) e(8); + if (!superuser) { + System("chmod 677 foo");/* make foo inaccesable */ + if (mkdir("foo/foo", 0777) != -1) e(9); + if (errno != EACCES) e(10); + if (rmdir("foo/bar") != -1) e(11); + if (errno != EACCES) e(12); + System("chmod 577 foo");/* make foo unwritable */ + if (mkdir("foo/foo", 0777) != -1) e(13); + if (errno != EACCES) e(14); + if (rmdir("foo/bar") != -1) e(15); + if (errno != EACCES) e(16); + System("chmod 777 foo");/* make foo full accessable */ + } + if (rmdir("foo/bar") != 0) e(17); /* bar should be removable */ + if (mkdir("foo/no/foo", 0777) != -1) e(18); /* Note: "no" doesn't exist */ + if (errno != ENOENT) e(19); + if (mkdir("", 0777) != -1) e(20); /* empty string isn't ok */ + if (errno != ENOENT) e(21); + if (rmdir("") != -1) e(22); /* empty string isn't ok */ + if (errno != ENOENT) e(23); + System("> foo/no"); /* make a file "no" */ + if (mkdir("foo/no/foo", 0777) != -1) e(24); + if (errno != ENOTDIR) e(25); /* note: "no" is not a a dir */ + if (rmdir("foo/no/foo") != -1) e(26); + if (errno != ENOTDIR) e(27); + System("rm -rf foo"); /* clean up */ +} + + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(ToLongName, MaxName); /* copy them Max to ToLong */ + strcpy(ToLongPath, MaxPath); + + ToLongName[NAME_MAX] = 'a'; + ToLongName[NAME_MAX + 1] = '\0'; /* extend ToLongName by one too many */ + ToLongPath[PATH_MAX - 1] = '/'; + ToLongPath[PATH_MAX] = '\0'; /* inc ToLongPath by one */ +} + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_28"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test29.c b/test/test29.c new file mode 100644 index 000000000..9f58a5c9d --- /dev/null +++ b/test/test29.c @@ -0,0 +1,750 @@ +/* Many of the tests require 1.6.n, n > 16, so we may as well assume that + * POSIX signals are implemented. + */ +#define SIGACTION + +/* test29: read(), write() Author: Jan-Mark Wams (jms@cs.vu.nl) */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <time.h> +#include <signal.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define ITERATIONS 3 +#define BUF_SIZE 1024 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) + +int errct = 0; +int subtest = 1; +int superuser; +int signumber = 0; + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test29a, (void)); +_PROTOTYPE(void test29b, (void)); +_PROTOTYPE(void test29c, (void)); +_PROTOTYPE(void setsignumber, (int _signumber)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 29 "); + fflush(stdout); + System("rm -rf DIR_29; mkdir DIR_29"); + Chdir("DIR_29"); + superuser = (geteuid() == 0); + umask(0000); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test29a(); + if (m & 0002) test29b(); + if (m & 0004) test29c(); + } + quit(); +} + +void test29a() +{ /* Try normal operation. */ + int fd1; + struct stat st1, st2; + time_t time1; + char buf[BUF_SIZE]; + int stat_loc; + int i, j; + int tube[2]; + + subtest = 1; + System("rm -rf ../DIR_29/*"); + + /* Let's open bar. */ + if ((fd1 = open("bar", O_RDWR | O_CREAT, 0777)) != 3) e(1); + Stat("bar", &st1); + + /* Writing nothing should not affect the file at all. */ + if (write(fd1, "", 0) != 0) e(2); + Stat("bar", &st2); + if (st1.st_uid != st2.st_uid) e(3); + if (st1.st_gid != st2.st_gid) e(4); /* should be same */ + if (st1.st_mode != st2.st_mode) e(5); + if (st1.st_size != st2.st_size) e(6); + if (st1.st_nlink != st2.st_nlink) e(7); + if (st1.st_mtime != st2.st_mtime) e(8); + if (st1.st_ctime != st2.st_ctime) e(9); + if (st1.st_atime != st2.st_atime) e(10); + + /* A write should update some status fields. */ + time(&time1); + while (time1 >= time((time_t *)0)) + ; + if (write(fd1, "foo", 4) != 4) e(11); + Stat("bar", &st2); + if (st1.st_mode != st2.st_mode) e(12); + if (st1.st_size >= st2.st_size) e(13); + if ((off_t) 4 != st2.st_size) e(14); + if (st1.st_nlink != st2.st_nlink) e(15); + if (st1.st_mtime >= st2.st_mtime) e(16); + if (st1.st_ctime >= st2.st_ctime) e(17); + if (st1.st_atime != st2.st_atime) e(18); + + /* Lseeks should not change the file status. */ + if (lseek(fd1, (off_t) - 2, SEEK_END) != 2) e(19); + Stat("bar", &st1); + if (st1.st_mode != st2.st_mode) e(20); + if (st1.st_size != st2.st_size) e(21); + if (st1.st_nlink != st2.st_nlink) e(22); + if (st1.st_mtime != st2.st_mtime) e(23); + if (st1.st_ctime != st2.st_ctime) e(24); + if (st1.st_atime != st2.st_atime) e(25); + + /* Writing should start at the current (2) position. */ + if (write(fd1, "foo", 4) != 4) e(26); + Stat("bar", &st2); + if (st1.st_mode != st2.st_mode) e(27); + if (st1.st_size >= st2.st_size) e(28); + if ((off_t) 6 != st2.st_size) e(29); + if (st1.st_nlink != st2.st_nlink) e(30); + if (st1.st_mtime > st2.st_mtime) e(31); + if (st1.st_ctime > st2.st_ctime) e(32); + if (st1.st_atime != st2.st_atime) e(33); + + /* A read of zero bytes should not affect anything. */ + if (read(fd1, buf, 0) != 0) e(34); + Stat("bar", &st1); + if (st1.st_uid != st2.st_uid) e(35); + if (st1.st_gid != st2.st_gid) e(36); /* should be same */ + if (st1.st_mode != st2.st_mode) e(37); + if (st1.st_size != st2.st_size) e(38); + if (st1.st_nlink != st2.st_nlink) e(39); + if (st1.st_mtime != st2.st_mtime) e(40); + if (st1.st_ctime != st2.st_ctime) e(41); + if (st1.st_atime != st2.st_atime) e(42); + + /* The file now should contain ``fofoo\0'' Let's check that. */ + if (lseek(fd1, (off_t) 0, SEEK_SET) != 0) e(43); + if (read(fd1, buf, BUF_SIZE) != 6) e(44); + if (strcmp(buf, "fofoo") != 0) e(45); + + /* Only the Access Time should be updated. */ + Stat("bar", &st2); + if (st1.st_mtime != st2.st_mtime) e(46); + if (st1.st_ctime != st2.st_ctime) e(47); + if (st1.st_atime >= st2.st_atime) e(48); + + /* A read of zero bytes should do nothing even at the end of the file. */ + time(&time1); + while (time1 >= time((time_t *)0)) + ; + if (read(fd1, buf, 0) != 0) e(49); + Stat("bar", &st1); + if (st1.st_size != st2.st_size) e(50); + if (st1.st_mtime != st2.st_mtime) e(51); + if (st1.st_ctime != st2.st_ctime) e(52); + if (st1.st_atime != st2.st_atime) e(53); + + /* Reading should be done from the current offset. */ + if (read(fd1, buf, BUF_SIZE) != 0) e(54); + if (lseek(fd1, (off_t) 2, SEEK_SET) != 2) e(55); + if (read(fd1, buf, BUF_SIZE) != 4) e(56); + if (strcmp(buf, "foo") != 0) e(57); + + /* Reading should effect the current file position. */ + if (lseek(fd1, (off_t) 2, SEEK_SET) != 2) e(58); + if (read(fd1, buf, 1) != 1) e(59); + if (*buf != 'f') e(60); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 3) e(61); + if (read(fd1, buf, 1) != 1) e(62); + if (*buf != 'o') e(63); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 4) e(64); + if (read(fd1, buf, 1) != 1) e(65); + if (*buf != 'o') e(66); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 5) e(67); + if (read(fd1, buf, 1) != 1) e(68); + if (*buf != '\0') e(69); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 6) e(70); + + /* Read's at EOF should return 0. */ + if (read(fd1, buf, BUF_SIZE) != 0) e(71); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 6) e(72); + if (read(fd1, buf, BUF_SIZE) != 0) e(73); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 6) e(74); + if (read(fd1, buf, BUF_SIZE) != 0) e(75); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 6) e(76); + if (read(fd1, buf, BUF_SIZE) != 0) e(77); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 6) e(78); + if (read(fd1, buf, BUF_SIZE) != 0) e(79); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 6) e(80); + + /* Writing should not always change the file size. */ + if (lseek(fd1, (off_t) 2, SEEK_SET) != 2) e(81); + if (write(fd1, "ba", 2) != 2) e(82); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 4) e(83); + Stat("bar", &st1); + if (st1.st_size != 6) e(84); + + /* Kill the \0 at the end. */ + if (lseek(fd1, (off_t) 5, SEEK_SET) != 5) e(85); + if (write(fd1, "x", 1) != 1) e(86); + + /* And close the bar. */ + if (close(fd1) != 0) e(87); + + /* Try some stuff with O_APPEND. Bar contains ``fobaox'' */ + if ((fd1 = open("bar", O_RDWR | O_APPEND)) != 3) e(88); + + /* No matter what the file position is. Writes should append. */ + if (lseek(fd1, (off_t) 2, SEEK_SET) != 2) e(89); + if (write(fd1, "y", 1) != 1) e(90); + Stat("bar", &st1); + if (st1.st_size != (off_t) 7) e(91); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 7) e(92); + if (lseek(fd1, (off_t) 2, SEEK_SET) != 2) e(93); + if (write(fd1, "z", 2) != 2) e(94); + + /* The file should contain ``fobaoxyz\0'' == 9 chars long. */ + Stat("bar", &st1); + if (st1.st_size != (off_t) 9) e(95); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 9) e(96); + + /* Reading on a O_APPEND flag should be from the current offset. */ + if (lseek(fd1, (off_t) 0, SEEK_SET) != 0) e(97); + if (read(fd1, buf, BUF_SIZE) != 9) e(98); + if (strcmp(buf, "fobaoxyz") != 0) e(99); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 9) e(100); + + if (close(fd1) != 0) e(101); + + /* Let's test fifo writes. First blocking. */ + if (mkfifo("fifo", 0777) != 0) e(102); + + /* Read from fifo but no writer. */ + System("rm -rf /tmp/sema.29a"); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + + case 0: + alarm(20); + if ((fd1 = open("fifo", O_RDONLY)) != 3) e(103); + system("> /tmp/sema.29a"); + system("while test -f /tmp/sema.29a; do sleep 1; done"); +errno =0; + if (read(fd1, buf, BUF_SIZE) != 0) e(104); + if (read(fd1, buf, BUF_SIZE) != 0) e(105); + if (read(fd1, buf, BUF_SIZE) != 0) e(106); + if (close(fd1) != 0) e(107); + exit(0); + + default: + if ((fd1 = open("fifo", O_WRONLY)) != 3) e(108); + while (stat("/tmp/sema.29a", &st1) != 0) sleep(1); + if (close(fd1) != 0) e(109); + unlink("/tmp/sema.29a"); + if (wait(&stat_loc) == -1) e(110); + if (stat_loc != 0) e(111); /* Alarm? */ + } + + /* Read from fifo should wait for writer. */ + switch (fork()) { + case -1: printf("Can't fork\n"); break; + + case 0: + alarm(20); + if ((fd1 = open("fifo", O_RDONLY)) != 3) e(112); + if (read(fd1, buf, BUF_SIZE) != 10) e(113); + if (strcmp(buf, "Hi reader") != 0) e(114); + if (close(fd1) != 0) e(115); + exit(0); + + default: + if ((fd1 = open("fifo", O_WRONLY)) != 3) e(116); + sleep(1); + if (write(fd1, "Hi reader", 10) != 10) e(117); + if (close(fd1) != 0) e(118); + if (wait(&stat_loc) == -1) e(119); + if (stat_loc != 0) e(120); /* Alarm? */ + } + + /* Read from fifo should wait for all writers to close. */ + switch (fork()) { + case -1: printf("Can't fork\n"); break; + + case 0: + alarm(20); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + if ((fd1 = open("fifo", O_WRONLY)) != 3) e(121); + if (close(fd1) != 0) e(122); + exit(0); + default: + if ((fd1 = open("fifo", O_WRONLY)) != 3) e(123); + sleep(1); + if (close(fd1) != 0) e(124); + if (wait(&stat_loc) == -1) e(125); + if (stat_loc != 0) e(126); /* Alarm? */ + } + exit(stat_loc); + + default: + if ((fd1 = open("fifo", O_RDONLY)) != 3) e(127); + if (read(fd1, buf, BUF_SIZE) != 0) e(128); + if (close(fd1) != 0) e(129); + if (wait(&stat_loc) == -1) e(130); + if (stat_loc != 0) e(131); /* Alarm? */ + } + + /* PIPE_BUF has to have a nice value. */ + if (PIPE_BUF < 5) e(132); + if (BUF_SIZE < 1000) e(133); + + /* Writes of blocks smaller than PIPE_BUF should be atomic. */ + System("rm -rf /tmp/sema.29b;> /tmp/sema.29b"); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + + case 0: + alarm(20); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + + case 0: + alarm(20); + if ((fd1 = open("fifo", O_WRONLY)) != 3) e(134); + for (i = 0; i < 100; i++) write(fd1, "1234 ", 5); + system("while test -f /tmp/sema.29b; do sleep 1; done"); + if (close(fd1) != 0) e(135); + exit(0); + + default: + if ((fd1 = open("fifo", O_WRONLY)) != 3) e(136); + for (i = 0; i < 100; i++) write(fd1, "1234 ", 5); + while (stat("/tmp/sema.29b", &st1) == 0) sleep(1); + if (close(fd1) != 0) e(137); + if (wait(&stat_loc) == -1) e(138); + if (stat_loc != 0) e(139); /* Alarm? */ + } + exit(stat_loc); + + default: + if ((fd1 = open("fifo", O_RDONLY)) != 3) e(140); + i = 0; + memset(buf, '\0', BUF_SIZE); + + /* Read buffer full or till EOF. */ + do { + j = read(fd1, buf + i, BUF_SIZE - i); + if (j > 0) { + if (j % 5 != 0) e(141); + i += j; + } + } while (j > 0 && i < 1000); + + /* Signal the children to close write ends. This should not be */ + /* Necessary. But due to a bug in 1.16.6 this is necessary. */ + unlink("/tmp/sema.29b"); + if (j < 0) e(142); + if (i != 1000) e(143); + if (wait(&stat_loc) == -1) e(144); + if (stat_loc != 0) e(145); /* Alarm? */ + + /* Check 200 times 1234. */ + for (i = 0; i < 200; i++) + if (strncmp(buf + (i * 5), "1234 ", 5) != 0) break; + if (i != 200) e(146); + if (buf[1000] != '\0') e(147); + if (buf[1005] != '\0') e(148); + if (buf[1010] != '\0') e(149); + if (read(fd1, buf, BUF_SIZE) != 0) e(150); + if (close(fd1) != 0) e(151); + } + + /* Read from pipe should wait for writer. */ + if (pipe(tube) != 0) e(152); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + if (close(tube[1]) != 0) e(153); + if (read(tube[0], buf, BUF_SIZE) != 10) e(154); + if (strcmp(buf, "Hi reader") != 0) e(155); + if (close(tube[0]) != 0) e(156); + exit(0); + default: + if (close(tube[0]) != 0) e(157); + sleep(1); + if (write(tube[1], "Hi reader", 10) != 10) e(158); + if (close(tube[1]) != 0) e(159); + if (wait(&stat_loc) == -1) e(160); + if (stat_loc != 0) e(161); /* Alarm? */ + } + + /* Read from pipe should wait for all writers to close. */ + if (pipe(tube) != 0) e(162); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + if (close(tube[0]) != 0) e(163); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + if (close(tube[1]) != 0) e(164); + exit(0); + default: + sleep(1); + if (close(tube[1]) != 0) e(165); + if (wait(&stat_loc) == -1) e(166); + if (stat_loc != 0) e(167); /* Alarm? */ + } + exit(stat_loc); + default: + if (close(tube[1]) != 0) e(168); + if (read(tube[0], buf, BUF_SIZE) != 0) e(169); + if (close(tube[0]) != 0) e(170); + if (wait(&stat_loc) == -1) e(171); + if (stat_loc != 0) e(172); /* Alarm? */ + } + + /* Writes of blocks smaller than PIPE_BUF should be atomic. */ + System("rm -rf /tmp/sema.29c;> /tmp/sema.29c"); + if (pipe(tube) != 0) e(173); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + if (close(tube[0]) != 0) e(174); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + for (i = 0; i < 100; i++) write(tube[1], "1234 ", 5); + system("while test -f /tmp/sema.29c; do sleep 1; done"); + if (close(tube[1]) != 0) e(175); + exit(0); + default: + for (i = 0; i < 100; i++) write(tube[1], "1234 ", 5); + while (stat("/tmp/sema.29c", &st1) == 0) sleep(1); + if (close(tube[1]) != 0) e(176); + if (wait(&stat_loc) == -1) e(177); + if (stat_loc != 0) e(178); /* Alarm? */ + } + exit(stat_loc); + default: + i = 0; + if (close(tube[1]) != 0) e(179); + memset(buf, '\0', BUF_SIZE); + do { + j = read(tube[0], buf + i, BUF_SIZE - i); + if (j > 0) { + if (j % 5 != 0) e(180); + i += j; + } else + break; /* EOF seen. */ + } while (i < 1000); + unlink("/tmp/sema.29c"); + if (j < 0) e(181); + if (i != 1000) e(182); + if (close(tube[0]) != 0) e(183); + if (wait(&stat_loc) == -1) e(184); + if (stat_loc != 0) e(185); /* Alarm? */ + + /* Check 200 times 1234. */ + for (i = 0; i < 200; i++) + if (strncmp(buf + (i * 5), "1234 ", 5) != 0) break; + if (i != 200) e(186); + } +} + + +void test29b() +{ + int i, fd, stat_loc; + char buf[BUF_SIZE]; + char buf2[BUF_SIZE]; + struct stat st; + + subtest = 2; + System("rm -rf ../DIR_29/*"); + + /* Lets try sequential writes. */ + system("rm -rf /tmp/sema.29d"); + System("> testing"); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + if ((fd = open("testing", O_WRONLY | O_APPEND)) != 3) e(1); + if (write(fd, "one ", 4) != 4) e(2); + if (close(fd) != 0) e(3); + system("> /tmp/sema.29d"); + system("while test -f /tmp/sema.29d; do sleep 1; done"); + if ((fd = open("testing", O_WRONLY | O_APPEND)) != 3) e(4); + if (write(fd, "three ", 6) != 6) e(5); + if (close(fd) != 0) e(6); + system("> /tmp/sema.29d"); + exit(0); + default: + while (stat("/tmp/sema.29d", &st) != 0) sleep(1); + if ((fd = open("testing", O_WRONLY | O_APPEND)) != 3) e(7); + if (write(fd, "two ", 4) != 4) e(8); + if (close(fd) != 0) e(9); + unlink("/tmp/sema.29d"); + while (stat("/tmp/sema.29d", &st) != 0) sleep(1); + if ((fd = open("testing", O_WRONLY | O_APPEND)) != 3) e(10); + if (write(fd, "four", 5) != 5) e(11); + if (close(fd) != 0) e(12); + if (wait(&stat_loc) == -1) e(13); + if (stat_loc != 0) e(14); /* The alarm went off? */ + unlink("/tmp/sema.29d"); + } + if ((fd = open("testing", O_RDONLY)) != 3) e(15); + if (read(fd, buf, BUF_SIZE) != 19) e(16); + if (strcmp(buf, "one two three four") != 0) e(17); + if (close(fd) != 0) e(18); + + + /* Non written bytes in regular files should be zero. */ + memset(buf2, '\0', BUF_SIZE); + if ((fd = open("bigfile", O_RDWR | O_CREAT, 0644)) != 3) e(19); + if (lseek(fd, (off_t) 102400, SEEK_SET) != (off_t) 102400L) e(20); + if (read(fd, buf, BUF_SIZE) != 0) e(21); + if (write(fd, ".", 1) != 1) e(22); + Stat("bigfile", &st); + if (st.st_size != (off_t) 102401) e(23); + if (lseek(fd, (off_t) 0, SEEK_SET) != 0) e(24); + for (i = 0; i < 102400 / BUF_SIZE; i++) { + if (read(fd, buf, BUF_SIZE) != BUF_SIZE) e(25); + if (memcmp(buf, buf2, BUF_SIZE) != 0) e(26); + } + if (close(fd) != 0) e(27); +} + +void test29c() +{ /* Test correct error behavior. */ + char buf[BUF_SIZE]; + int fd, tube[2], stat_loc; + struct stat st; + pid_t pid; +#ifdef SIGACTION + struct sigaction act, oact; +#else +#if _ANSI + void (*oldfunc) (int); +#else + void (*oldfunc) (); +#endif +#endif + + subtest = 3; + System("rm -rf ../DIR_29/*"); + + /* To test if writing processes on closed pipes are signumbered. */ +#ifdef SIGACTION + act.sa_handler = setsignumber; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (sigaction(SIGPIPE, &act, &oact) != 0) e(1); +#else + oldfunc = signal(SIGPIPE, setsignumber); +#endif + + /* Non valid file descriptors should be an error. */ + for (fd = -111; fd < 0; fd++) { + errno = 0; + if (read(fd, buf, BUF_SIZE) != -1) e(2); + if (errno != EBADF) e(3); + } + for (fd = 3; fd < 111; fd++) { + errno = 0; + if (read(fd, buf, BUF_SIZE) != -1) e(4); + if (errno != EBADF) e(5); + } + for (fd = -111; fd < 0; fd++) { + errno = 0; + if (write(fd, buf, BUF_SIZE) != -1) e(6); + if (errno != EBADF) e(7); + } + for (fd = 3; fd < 111; fd++) { + errno = 0; + if (write(fd, buf, BUF_SIZE) != -1) e(8); + if (errno != EBADF) e(9); + } + + /* Writing a pipe with no readers should trigger SIGPIPE. */ + if (pipe(tube) != 0) e(10); + close(tube[0]); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + signumber = 0; + if (write(tube[1], buf, BUF_SIZE) != -1) e(11); + if (errno != EPIPE) e(12); + if (signumber != SIGPIPE) e(13); + if (close(tube[1]) != 0) e(14); + exit(0); + default: + close(tube[1]); + if (wait(&stat_loc) == -1) e(15); + if (stat_loc != 0) e(16); /* Alarm? */ + } + + /* Writing a fifo with no readers should trigger SIGPIPE. */ + System("> /tmp/sema.29e"); + if (mkfifo("fifo", 0666) != 0) e(17); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + if ((fd = open("fifo", O_WRONLY)) != 3) e(18); + system("while test -f /tmp/sema.29e; do sleep 1; done"); + signumber = 0; + if (write(fd, buf, BUF_SIZE) != -1) e(19); + if (errno != EPIPE) e(20); + if (signumber != SIGPIPE) e(21); + if (close(fd) != 0) e(22); + exit(0); + default: + if ((fd = open("fifo", O_RDONLY)) != 3) e(23); + if (close(fd) != 0) e(24); + unlink("/tmp/sema.29e"); + if (wait(&stat_loc) == -1) e(25); + if (stat_loc != 0) e(26); /* Alarm? */ + } + +#ifdef SIGACTION + /* Restore normal (re)action to SIGPIPE. */ + if (sigaction(SIGPIPE, &oact, NULL) != 0) e(27); +#else + signal(SIGPIPE, oldfunc); +#endif + + /* Read from fifo should return -1 and set errno to EAGAIN. */ + System("rm -rf /tmp/sema.29[fgh]"); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + system("while test ! -f /tmp/sema.29f; do sleep 1; done"); + System("rm -rf /tmp/sema.29f"); + if ((fd = open("fifo", O_WRONLY | O_NONBLOCK)) != 3) e(28); + close(creat("/tmp/sema.29g", 0666)); + system("while test ! -f /tmp/sema.29h; do sleep 1; done"); + if (close(fd) != 0) e(29); + System("rm -rf /tmp/sema.29h"); + exit(0); + default: + if ((fd = open("fifo", O_RDONLY | O_NONBLOCK)) != 3) e(30); + close(creat("/tmp/sema.29f", 0666)); + system("while test ! -f /tmp/sema.29g; do sleep 1; done"); + System("rm -rf /tmp/sema.29g"); + if (read(fd, buf, BUF_SIZE) != -1) e(31); + if (errno != EAGAIN) e(32); + if (read(fd, buf, BUF_SIZE) != -1) e(33); + if (errno != EAGAIN) e(34); + if (read(fd, buf, BUF_SIZE) != -1) e(35); + if (errno != EAGAIN) e(36); + close(creat("/tmp/sema.29h", 0666)); + while (stat("/tmp/sema.29h", &st) == 0) sleep(1); + if (read(fd, buf, BUF_SIZE) != 0) e(37); + if (close(fd) != 0) e(38); + if (wait(&stat_loc) == -1) e(39); + if (stat_loc != 0) e(40); /* Alarm? */ + } + System("rm -rf fifo"); + + /* If a read is interrupted by a SIGNAL. */ + if (pipe(tube) != 0) e(41); + switch (pid = fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); +#ifdef SIGACTION + act.sa_handler = setsignumber; + sigemptyset(&act.sa_mask); + act.sa_flags = 0; + if (sigaction(SIGUSR1, &act, &oact) != 0) e(42); +#else + oldfunc = signal(SIGUSR1, setsignumber); +#endif + if (read(tube[0], buf, BUF_SIZE) != -1) e(43); + if (errno != EINTR) e(44); + if (signumber != SIGUSR1) e(45); +#ifdef SIGACTION + /* Restore normal (re)action to SIGPIPE. */ + if (sigaction(SIGUSR1, &oact, NULL) != 0) e(46); +#else + signal(SIGUSR1, oldfunc); +#endif + close(tube[0]); + close(tube[1]); + exit(0); + default: + /* The sleep 1 should give the child time to start the read. */ + sleep(1); + close(tube[0]); + kill(pid, SIGUSR1); + wait(&stat_loc); + if (stat_loc != 0) e(47); /* Alarm? */ + close(tube[1]); + } +} + + +void setsignumber(signum) +int signum; +{ + signumber = signum; +} + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_29"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test3.c b/test/test3.c new file mode 100644 index 000000000..4ecac234c --- /dev/null +++ b/test/test3.c @@ -0,0 +1,256 @@ +/* test 3 - library routines rather than system calls */ + +#include <sys/types.h> +#include <sys/utsname.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +#define ITERATIONS 10 +#define MAX_ERROR 4 +#define SIZE 64 + +int errct, subtest; +char el_weirdo[] = "\n\t\\\e@@!!##\e\e\n\n"; + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void test3a, (void)); +_PROTOTYPE(void test3b, (void)); +_PROTOTYPE(void test3c, (void)); +_PROTOTYPE(void test3d, (void)); +_PROTOTYPE(void test3e, (void)); +_PROTOTYPE(void quit, (void)); +_PROTOTYPE(void e, (int n)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (geteuid() == 0 || getuid() == 0) { + printf("Test 3 cannot run as root; test aborted\n"); + exit(1); + } + + if (argc == 2) m = atoi(argv[1]); + + printf("Test 3 "); + fflush(stdout); /* have to flush for child's benefit */ + + system("rm -rf DIR_03; mkdir DIR_03"); + chdir("DIR_03"); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test3a(); + if (m & 0002) test3b(); + if (m & 0004) test3c(); + if (m & 0010) test3d(); + if (m & 0020) test3e(); + } + quit(); + return(-1); /* impossible */ + +} + +void test3a() +{ +/* Signal set manipulation. */ + + sigset_t s, s1; + + subtest = 1; + errno = -1000; /* None of these calls set errno. */ + if (sigemptyset(&s) != 0) e(1); + if (sigemptyset(&s1) != 0) e(2); + if (sigaddset(&s, SIGABRT) != 0) e(3); + if (sigaddset(&s, SIGALRM) != 0) e(4); + if (sigaddset(&s, SIGFPE ) != 0) e(5); + if (sigaddset(&s, SIGHUP ) != 0) e(6); + if (sigaddset(&s, SIGILL ) != 0) e(7); + if (sigaddset(&s, SIGINT ) != 0) e(8); + if (sigaddset(&s, SIGKILL) != 0) e(9); + if (sigaddset(&s, SIGPIPE) != 0) e(10); + if (sigaddset(&s, SIGQUIT) != 0) e(11); + if (sigaddset(&s, SIGSEGV) != 0) e(12); + if (sigaddset(&s, SIGTERM) != 0) e(13); + if (sigaddset(&s, SIGUSR1) != 0) e(14); + if (sigaddset(&s, SIGUSR2) != 0) e(15); + + if (sigismember(&s, SIGABRT) != 1) e(16); + if (sigismember(&s, SIGALRM) != 1) e(17); + if (sigismember(&s, SIGFPE ) != 1) e(18); + if (sigismember(&s, SIGHUP ) != 1) e(19); + if (sigismember(&s, SIGILL ) != 1) e(20); + if (sigismember(&s, SIGINT ) != 1) e(21); + if (sigismember(&s, SIGKILL) != 1) e(22); + if (sigismember(&s, SIGPIPE) != 1) e(23); + if (sigismember(&s, SIGQUIT) != 1) e(24); + if (sigismember(&s, SIGSEGV) != 1) e(25); + if (sigismember(&s, SIGTERM) != 1) e(26); + if (sigismember(&s, SIGUSR1) != 1) e(27); + if (sigismember(&s, SIGUSR2) != 1) e(28); + + if (sigdelset(&s, SIGABRT) != 0) e(29); + if (sigdelset(&s, SIGALRM) != 0) e(30); + if (sigdelset(&s, SIGFPE ) != 0) e(31); + if (sigdelset(&s, SIGHUP ) != 0) e(32); + if (sigdelset(&s, SIGILL ) != 0) e(33); + if (sigdelset(&s, SIGINT ) != 0) e(34); + if (sigdelset(&s, SIGKILL) != 0) e(35); + if (sigdelset(&s, SIGPIPE) != 0) e(36); + if (sigdelset(&s, SIGQUIT) != 0) e(37); + if (sigdelset(&s, SIGSEGV) != 0) e(38); + if (sigdelset(&s, SIGTERM) != 0) e(39); + if (sigdelset(&s, SIGUSR1) != 0) e(40); + if (sigdelset(&s, SIGUSR2) != 0) e(41); + + if (s != s1) e(42); + + if (sigaddset(&s, SIGILL) != 0) e(43); + if (s == s1) e(44); + + if (sigfillset(&s) != 0) e(45); + if (sigismember(&s, SIGABRT) != 1) e(46); + if (sigismember(&s, SIGALRM) != 1) e(47); + if (sigismember(&s, SIGFPE ) != 1) e(48); + if (sigismember(&s, SIGHUP ) != 1) e(49); + if (sigismember(&s, SIGILL ) != 1) e(50); + if (sigismember(&s, SIGINT ) != 1) e(51); + if (sigismember(&s, SIGKILL) != 1) e(52); + if (sigismember(&s, SIGPIPE) != 1) e(53); + if (sigismember(&s, SIGQUIT) != 1) e(54); + if (sigismember(&s, SIGSEGV) != 1) e(55); + if (sigismember(&s, SIGTERM) != 1) e(56); + if (sigismember(&s, SIGUSR1) != 1) e(57); + if (sigismember(&s, SIGUSR2) != 1) e(58); + + /* Test error returns. */ + if (sigaddset(&s, -1) != -1) e(59); + if (sigaddset(&s, -1) != -1) e(60); + if (sigismember(&s, -1) != -1) e(61); + if (sigaddset(&s, 10000) != -1) e(62); + if (sigaddset(&s, 10000) != -1) e(63); + if (sigismember(&s, 10000) != -1) e(64); + +} + +void test3b() +{ +/* Test uname. */ + + struct utsname u; /* contains all kinds of system ids */ + + subtest = 2; +#if 0 + errno = -2000; /* None of these calls set errno. */ + if (uname(&u) != 0) e(1); + if (strcmp(u.sysname, "MINIX") != 0 + && strcmp(u.sysname, "Minix") != 0) e(2); /* only one defined */ +#endif +} + +void test3c() +{ +/* Test getenv. Asume HOME, PATH, and LOGNAME exist (not strictly required).*/ + + char *p, name[SIZE]; + + subtest = 3; + errno = -3000; /* None of these calls set errno. */ + if ( (p = getenv("HOME")) == NULL) e(1); + if (*p != '/') e(2); /* path must be absolute */ + if ( (p = getenv("PATH")) == NULL) e(3); + if ( (p = getenv("LOGNAME")) == NULL) e(5); + strcpy(name, p); /* save it, since getlogin might wipe it out */ + p = getlogin(); + if (strcmp(p, name) != 0) e(6); + + /* The following test could fail in a legal POSIX system. However, if it + * does, you deserve it to fail. + */ + if (getenv(el_weirdo) != NULL) e(7); +} + +void test3d() +{ +/* Test ctermid, ttyname, and isatty. */ + + int fd; + char *p, name[L_ctermid]; + + subtest = 4; + errno = -4000; /* None of these calls set errno. */ + + /* Test ctermid first. */ + if ( (p = ctermid(name)) == NULL) e(1); + if (strcmp(p, name) != 0) e(2); + if (strncmp(p, "/dev/tty", 8) != 0) e(3); /* MINIX convention */ + + if ( (p = ttyname(0)) == NULL) e(4); + if (strncmp(p, "/dev/tty", 8) != 0 && strcmp(p, "/dev/console") != 0) e(5); + if ( (p = ttyname(3)) != NULL) e(6); + if (ttyname(5000) != NULL) e(7); + if ( (fd = creat("T3a", 0777)) < 0) e(8); + if (ttyname(fd) != NULL) e(9); + + if (isatty(0) != 1) e(10); + if (isatty(3) != 0) e(11); + if (isatty(fd) != 0) e(12); + if (close(fd) != 0) e(13); + if (ttyname(fd) != NULL) e(14); +} + +void test3e() +{ +/* Test ctermid, ttyname, and isatty. */ + + subtest = 5; + errno = -5000; /* None of these calls set errno. */ + + if (sysconf(_SC_ARG_MAX) < _POSIX_ARG_MAX) e(1); + if (sysconf(_SC_CHILD_MAX) < _POSIX_CHILD_MAX) e(2); + if (sysconf(_SC_NGROUPS_MAX) < 0) e(3); + if (sysconf(_SC_OPEN_MAX) < _POSIX_OPEN_MAX) e(4); + + /* The rest are MINIX specific */ + if (sysconf(_SC_JOB_CONTROL) >= 0) e(5); /* no job control! */ +} + +void quit() +{ + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(4); + } +} + + + +void e(n) +int n; +{ + int err_num = errno; /* save errno in case printf clobbers it */ + + printf("Subtest %d, error %d errno=%d ", subtest, n, errno); + errno = err_num; /* restore errno, just in case */ + perror(""); + if (errct++ > MAX_ERROR) { + printf("Test aborted. Too many errors: "); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} diff --git a/test/test30.c b/test/test30.c new file mode 100644 index 000000000..1f2cfae69 --- /dev/null +++ b/test/test30.c @@ -0,0 +1,348 @@ +/* test30: creat() (p) Jan-Mark Wams. email: jms@cs.vu.nl */ + +/* +** Creat() should be equivalent to: +** open(path, O_WRONLY | O_CREAT | O_TRUNC, mode); +** Since we can not look in the source, we can not assume creat() is +** a mere sysnonym (= a systemcall synonym). +*/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define ITERATIONS 10 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) + +int errct = 0; +int subtest = 1; +int superuser; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; /* Same for path, both too long */ + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test30a, (void)); +_PROTOTYPE(void test30b, (void)); +_PROTOTYPE(void test30c, (void)); +_PROTOTYPE(void makelongnames, (void)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 30 "); + fflush(stdout); + System("rm -rf DIR_30; mkdir DIR_30"); + Chdir("DIR_30"); + makelongnames(); + superuser = (geteuid() == 0); + + umask(0000); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test30a(); + if (m & 0002) test30b(); + if (m & 0004) test30c(); + } + quit(); +} + + +void test30a() +{ /* Test normal operation. */ + +#define BUF_SIZE 1024 + + int fd1, fd2; + char buf[BUF_SIZE]; + struct stat st, dirst; + time_t time1, time2; + int stat_loc, cnt; + + subtest = 1; + + System("rm -rf ../DIR_30/*"); + + /* Check if processes have independent new fds */ + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + if ((fd1 = creat("myfile", 0644)) != 3) e(1); + if (close(fd1) != 0) e(2); + exit(0); + default: + if ((fd1 = creat("myfile", 0644)) != 3) e(3); + if (close(fd1) != 0) e(4); + if (wait(&stat_loc) == -1) e(5); + if (stat_loc != 0) e(6); + } + + /* Save the dir status. */ + Stat(".", &dirst); + time(&time1); + while (time1 == time((time_t *)0)) + ; + + /* Check if the file status information is updated correctly */ + cnt = 0; + System("rm -rf myfile"); + do { + time(&time1); + if ((fd1 = creat("myfile", 0644)) != 3) e(7); + Stat("myfile", &st); + time(&time2); + } while (time1 != time2 && cnt++ < 100); + if (cnt >= 100) e(8); + if (st.st_uid != geteuid()) e(9); /* Uid should be set. */ +#if defined(NGROUPS_MAX) && NGROUPS_MAX == 0 + if (st.st_gid != getegid()) e(10); +#endif /* defined(NGROUPS_MAX) && NGROUPS_MAX == 0 */ + if (!S_ISREG(st.st_mode)) e(11); + if (st.st_mode & 0777 != 0644) e(12); + if (st.st_nlink != 1) e(13); + if (st.st_ctime != time1) e(14); /* All time fields should be updated */ + if (st.st_atime != time1) e(15); + if (st.st_mtime != time1) e(16); + if (st.st_size != 0) e(17); /* File should be trunked. */ + + /* Check if c and mtime for "." is updated. */ + Stat(".", &st); + if (st.st_ctime <= dirst.st_ctime) e(18); + if (st.st_mtime <= dirst.st_mtime) e(19); + + /* Let's see if cread fds are write only. */ + if (read(fd1, buf, 7) != -1) e(20); /* we can't read */ + if (errno != EBADF) e(21); /* a write only fd */ + if (write(fd1, "HELLO", 6) != 6) e(22); /* but we can write */ + + /* No O_APPEND flag should have been used. */ + if (lseek(fd1, (off_t) 1, SEEK_SET) != 1) e(23); + if (write(fd1, "ello", 5) != 5) e(24); + Stat("myfile", &st); + if (st.st_size != 6) e(25); + if (st.st_size == 11) e(26); /* O_APPEND should make it 11. */ + if (close(fd1) != 0) e(27); + + /* A creat should set the file offset to 0. */ + if ((fd1 = creat("myfile", 0644)) != 3) e(28); + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 0) e(29); + if (close(fd1) != 0) e(30); + + /* Test if file permission bits and the file ownership are unchanged. */ + /* So we will see if creat() is just an open() if the file exists. */ + if (superuser) { + System("echo > bar; chmod 073 bar"); /* Make bar 073 */ + if (chown("bar", 1, 1) != 0) e(1137); + fd1 = creat("bar", 0777); /* knock knock */ + if (fd1 == -1) e(31); + Stat("bar", &st); + if (st.st_size != (size_t) 0) e(32); /* empty file. */ + if (write(fd1, "foo", 3) != 3) e(33); /* rewrite bar */ + if (close(fd1) != 0) e(34); + Stat("bar", &st); + if (st.st_uid != 1) e(35); + if (st.st_gid != 1) e(36); + if ((st.st_mode & 0777) != 073) e(37); /* mode still is 077 */ + if (st.st_size != (size_t) 3) e(38); + } + + /* Fifo's should be openable with creat(). */ + if (mkfifo("fifo", 0644) != 0) e(39); + + /* Creat() should have no effect on FIFO files. */ + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); /* Give child 20 seconds to live. */ + if ((fd1 = open("fifo", O_WRONLY)) != 3) e(40); + if (write(fd1, "I did see Elvis.\n", 18) != 18) e(41); + if ((fd2 = creat("fifo", 0644)) != 4) e(42); + if (write(fd2, "I DID.\n", 8) != 8) e(43); + if (close(fd2) != 0) e(44); + if (close(fd1) != 0) e(45); + exit(0); + default: + if ((fd1 = open("fifo", O_RDONLY)) != 3) e(46); + if (read(fd1, buf, 18) != 18) e(47); + if (strcmp(buf, "I did see Elvis.\n") != 0) e(48); + if (strcmp(buf, "I DID.\n") == 0) e(49); + if (read(fd1, buf, BUF_SIZE) != 8) e(50); + if (strcmp(buf, "I DID.\n") != 0) e(51); + if (close(fd1) != 0) e(52); + if (wait(&stat_loc) == -1) e(53); + if (stat_loc != 0) e(54); /* The alarm went off? */ + } + + /* Creat() should have no effect on directroys. */ + System("mkdir dir; touch dir/f1 dir/f2 dir/f3"); + if ((fd1 = creat("dir", 0644)) != -1) e(55); + if (errno != EISDIR) e(56); + close(fd1); + + /* The path should contain only dirs in the prefix. */ + if ((fd1 = creat("dir/f1/nono", 0644)) != -1) e(57); + if (errno != ENOTDIR) e(58); + close(fd1); + + /* The path should contain only dirs in the prefix. */ + if ((fd1 = creat("", 0644)) != -1) e(59); + if (errno != ENOENT) e(60); + close(fd1); + if ((fd1 = creat("dir/noso/nono", 0644)) != -1) e(61); + if (errno != ENOENT) e(62); + close(fd1); + +} + +void test30b() +{ + int fd; + + subtest = 2; + + System("rm -rf ../DIR_30/*"); + + /* Test maximal file name length. */ + if ((fd = creat(MaxName, 0777)) != 3) e(1); + if (close(fd) != 0) e(2); + MaxPath[strlen(MaxPath) - 2] = '/'; + MaxPath[strlen(MaxPath) - 1] = 'a'; /* make ././.../a */ + if ((fd = creat(MaxPath, 0777)) != 3) e(3); + if (close(fd) != 0) e(4); + MaxPath[strlen(MaxPath) - 1] = '/'; /* make ././.../a */ +} + + +void test30c() +{ + int fd; + + subtest = 3; + + System("rm -rf ../DIR_30/*"); + + if (!superuser) { + /* Test if creat is not usable to open files with the wrong mode */ + System("> nono; chmod 177 nono"); + fd = creat("nono", 0777); + if (fd != -1) e(1); + if (errno != EACCES) e(2); + } + if (mkdir("bar", 0777) != 0) e(3); /* make bar */ + + /* Check if no access on part of path generates the correct error. */ + System("chmod 577 bar"); /* r-xrwxrwx */ + if (!superuser) { + /* Normal users can't creat without write permision. */ + if (creat("bar/nono", 0666) != -1) e(4); + if (errno != EACCES) e(5); + if (creat("bar/../nono", 0666) != -1) e(6); + if (errno != EACCES) e(7); + } + if (superuser) { + /* Super user can still creat stuff. */ + if ((fd = creat("bar/nono", 0666)) != 3) e(8); + if (close(fd) != 0) e(9); + if (unlink("bar/nono") != 0) e(10); + } + + /* Clean up bar. */ + System("rm -rf bar"); + + /* Test ToLongName and ToLongPath */ +#ifdef _POSIX_NO_TRUNC +# if _POSIX_NO_TRUNC - 0 != -1 + if ((fd = creat(ToLongName, 0777)) != -1) e(11); + if (errno != ENAMETOOLONG) e(12); + close(fd); /* Just in case. */ +# else + if ((fd = creat(ToLongName, 0777)) != 3) e(13); + if (close(fd) != 0) e(14); +# endif +#else +# include "error, this case requires dynamic checks and is not handled" +#endif + ToLongPath[PATH_MAX - 2] = '/'; + ToLongPath[PATH_MAX - 1] = 'a'; + if ((fd = creat(ToLongPath, 0777)) != -1) e(15); + if (errno != ENAMETOOLONG) e(16); + if (close(fd) != -1) e(17); + ToLongPath[PATH_MAX - 1] = '/'; +} + + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(ToLongName, MaxName); /* copy them Max to ToLong */ + strcpy(ToLongPath, MaxPath); + + ToLongName[NAME_MAX] = 'a'; + ToLongName[NAME_MAX + 1] = '\0'; /* extend ToLongName by one too many */ + ToLongPath[PATH_MAX - 1] = '/'; + ToLongPath[PATH_MAX] = '\0'; /* inc ToLongPath by one */ +} + + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_30"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test31.c b/test/test31.c new file mode 100644 index 000000000..5b96ae06d --- /dev/null +++ b/test/test31.c @@ -0,0 +1,275 @@ +/* test31: mkfifo() Author: Jan-Mark Wams (jms@cs.vu.nl) */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define ITERATIONS 10 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) + +int errct = 0; +int subtest = 1; +int superuser; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; /* Same for path, both too long */ + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test31a, (void)); +_PROTOTYPE(void test31b, (void)); +_PROTOTYPE(void test31c, (void)); +_PROTOTYPE(void makelongnames, (void)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 31 "); + fflush(stdout); + System("rm -rf DIR_31; mkdir DIR_31"); + Chdir("DIR_31"); + makelongnames(); + superuser = (geteuid() == 0); + + umask(0000); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test31a(); + if (m & 0002) test31b(); + if (m & 0004) test31c(); + } + quit(); +} + + +void test31a() +{ /* Test normal operation. */ + +#define BUF_SIZE 1024 + + int fd; + char buf[BUF_SIZE]; + struct stat st, dirst; + time_t time1, time2; + int stat_loc, cnt; + + subtest = 1; + + System("rm -rf ../DIR_31/*"); + + /* Check if the file status information is updated correctly */ + System("rm -rf fifo"); + cnt = 0; + Stat(".", &dirst); + time(&time1); + while (time1 == time((time_t *)0)) + ; + + do { + time(&time1); + if (mkfifo("fifo", 0644) != 0) e(1); + Stat("fifo", &st); + time(&time2); + } while (time1 != time2 && cnt++ < 100); + + if (cnt >= 100) e(2); + if (st.st_uid != geteuid()) e(3); /* Uid should be set. */ +#if defined(NGROUPS_MAX) && NGROUPS_MAX == 0 + if (st.st_gid != getegid()) e(4); +#endif /* defined(NGROUPS_MAX) && NGROUPS_MAX == 0 */ + if (!S_ISFIFO(st.st_mode)) e(5); + if (st.st_mode & 0777 != 0644) e(6); + if (st.st_nlink != 1) e(7); + if (st.st_ctime != time1) e(8); + if (st.st_atime != time1) e(9); + if (st.st_mtime != time1) e(10); + if (st.st_size != 0) e(11); /* File should be empty. */ + + /* Check if status for "." is updated. */ + Stat(".", &st); + if (st.st_ctime <= dirst.st_ctime) e(12); + if (st.st_mtime <= dirst.st_mtime) e(13); + + /* Basic checking if a fifo file created with mkfifo() is a pipe. */ + alarm(10); /* in case fifo hangs */ + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + if ((fd = open("fifo", O_RDONLY)) != 3) e(14); + if (read(fd, buf, BUF_SIZE) != 7) e(15); + if (strcmp(buf, "banana") != 0) e(16); + if (close(fd) != 0) e(17); + if ((fd = open("fifo", O_WRONLY)) != 3) e(18); + if (write(fd, "thanks", 7) != 7) e(19); + if (close(fd) != 0) e(20); + exit(0); + + default: + if ((fd = open("fifo", O_WRONLY)) != 3) e(21); + if (write(fd, "banana", 7) != 7) e(22); + if (close(fd) != 0) e(23); + if ((fd = open("fifo", O_RDONLY)) != 3) e(24); + if (read(fd, buf, BUF_SIZE) != 7) e(25); + if (strcmp(buf, "thanks") != 0) e(26); + if (close(fd) != 0) e(27); + wait(&stat_loc); + if (stat_loc != 0) e(28); /* Alarm? */ + } + alarm(0); +} + +void test31b() +{ + subtest = 2; + + System("rm -rf ../DIR_31/*"); + + /* Test maximal file name length. */ + if (mkfifo(MaxName, 0777) != 0) e(1); + if (unlink(MaxName) != 0) e(2); + MaxPath[strlen(MaxPath) - 2] = '/'; + MaxPath[strlen(MaxPath) - 1] = 'a'; /* make ././.../a */ + if (mkfifo(MaxPath, 0777) != 0) e(3); + if (unlink(MaxPath) != 0) e(4); + MaxPath[strlen(MaxPath) - 1] = '/'; /* make ././.../a */ +} + + +void test31c() +{ + subtest = 3; + + System("rm -rf ../DIR_31/*"); + + /* Check if mkfifo() removes, files, fifos, dirs. */ + if (mkfifo("fifo", 0777) != 0) e(1); + System("mkdir dir; > file"); + if (mkfifo("fifo", 0777) != -1) e(2); + if (errno != EEXIST) e(3); + if (mkfifo("dir", 0777) != -1) e(4); + if (errno != EEXIST) e(5); + if (mkfifo("file", 0777) != -1) e(6); + if (errno != EEXIST) e(7); + + /* Test empty path. */ + if (mkfifo("", 0777) != -1) e(8); + if (errno != ENOENT) e(9); + if (mkfifo("/tmp/noway/out", 0777) != -1) e(10); + if (errno != ENOENT) e(11); + + /* Test if path prefix is a directory. */ + if (mkfifo("/etc/passwd/nono", 0777) != -1) e(12); + if (errno != ENOTDIR) e(13); + + mkdir("bar", 0777); /* make bar */ + + /* Check if no access on part of path generates the correct error. */ + System("chmod 577 bar"); /* r-xrwxrwx */ + if (!superuser) { + if (mkfifo("bar/nono", 0666) != -1) e(14); + if (errno != EACCES) e(15); + } + if (superuser) { + if (mkfifo("bar/nono", 0666) != 0) e(14); + if (unlink("bar/nono") != 0) e(666); + } + System("chmod 677 bar"); /* rw-rwxrwx */ + if (!superuser) { + if (mkfifo("bar/../nono", 0666) != -1) e(16); + if (errno != EACCES) e(17); + } + if (unlink("nono") != -1) e(18); + + /* Clean up bar. */ + System("rm -rf bar"); + + /* Test ToLongName and ToLongPath */ +#ifdef _POSIX_NO_TRUNC +# if _POSIX_NO_TRUNC - 0 != -1 + if (mkfifo(ToLongName, 0777) != -1) e(19); + if (errno != ENAMETOOLONG) e(20); +# else + if (mkfifo(ToLongName, 0777) != 0) e(21); +# endif +#else +# include "error, this case requires dynamic checks and is not handled" +#endif + ToLongPath[PATH_MAX - 2] = '/'; + ToLongPath[PATH_MAX - 1] = 'a'; + if (mkfifo(ToLongPath, 0777) != -1) e(22); + if (errno != ENAMETOOLONG) e(23); + ToLongPath[PATH_MAX - 1] = '/'; +} + + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(ToLongName, MaxName); /* copy them Max to ToLong */ + strcpy(ToLongPath, MaxPath); + + ToLongName[NAME_MAX] = 'a'; + ToLongName[NAME_MAX + 1] = '\0'; /* extend ToLongName by one too many */ + ToLongPath[PATH_MAX - 1] = '/'; + ToLongPath[PATH_MAX] = '\0'; /* inc ToLongPath by one */ +} + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_31"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test32.c b/test/test32.c new file mode 100644 index 000000000..bc3eb1264 --- /dev/null +++ b/test/test32.c @@ -0,0 +1,370 @@ +/* test32: rename() Author: Jan-Mark Wams (jms@cs.vu.nl) */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define ITERATIONS 2 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) +#define Creat(f) if (close(creat(f,0777))!=0) printf("Can't creat %s\n",f) + +int errct = 0; +int subtest = 1; +int superuser; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; /* Same for path, both too long */ + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test32a, (void)); +_PROTOTYPE(void test32b, (void)); +_PROTOTYPE(void test32c, (void)); +_PROTOTYPE(void makelongnames, (void)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 32 "); + fflush(stdout); + System("rm -rf DIR_32; mkdir DIR_32"); + Chdir("DIR_32"); + makelongnames(); + superuser = (geteuid() == 0); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test32a(); + if (m & 0002) test32b(); + if (m & 0004) test32c(); + } + quit(); +} + +#define BUF_SIZE 1024 + +void test32a() +{ /* Test normal operation. */ + struct stat st1, st2; + int fd1, fd2; + time_t time1, time2, time3; + char buf[BUF_SIZE]; + + subtest = 1; + System("rm -rf ../DIR_32/*"); + + /* Test normal file renamal. */ + System("echo haha > old"); + Stat("old", &st1); + if (rename("old", "new") != 0) e(1); + Stat("new", &st2); + + /* The status of new should be the same as old. */ + if (st1.st_dev != st2.st_dev) e(2); + if (st1.st_ino != st2.st_ino) e(3); + if (st1.st_mode != st2.st_mode) e(4); + if (st1.st_nlink != st2.st_nlink) e(5); + if (st1.st_uid != st2.st_uid) e(6); + if (st1.st_gid != st2.st_gid) e(7); + if (st1.st_rdev != st2.st_rdev) e(8); + if (st1.st_size != st2.st_size) e(9); + if (st1.st_atime != st2.st_atime) e(10); + if (st1.st_mtime != st2.st_mtime) e(11); + if (st1.st_ctime != st2.st_ctime) e(12); + + /* If new exists, it should be removed. */ + System("ln new new2"); + System("echo foobar > old"); + Stat("old", &st1); + if (rename("old", "new") != 0) e(13); + Stat("new", &st2); + + /* The status of new should be the same as old. */ + if (st1.st_dev != st2.st_dev) e(14); + if (st1.st_ino != st2.st_ino) e(15); + if (st1.st_mode != st2.st_mode) e(16); + if (st1.st_nlink != st2.st_nlink) e(17); + if (st1.st_uid != st2.st_uid) e(18); + if (st1.st_gid != st2.st_gid) e(19); + if (st1.st_rdev != st2.st_rdev) e(20); + if (st1.st_size != st2.st_size) e(21); + if (st1.st_atime != st2.st_atime) e(22); + if (st1.st_mtime != st2.st_mtime) e(23); + if (st1.st_ctime != st2.st_ctime) e(24); + + /* The link count on new2 should be one since the old new is removed. */ + Stat("new2", &st1); + if (st1.st_nlink != 1) e(25); + + /* Check if status for "." is updated. */ + System("> OLD"); + Stat(".", &st1); + time(&time1); + while (time1 == time((time_t *)0)) + ; + time(&time2); + rename("OLD", "NEW"); + Stat(".", &st2); + time(&time3); + while (time3 == time((time_t *)0)) + ; + time(&time3); + if (st1.st_ctime >= st2.st_ctime) e(26); + if (st1.st_mtime >= st2.st_mtime) e(27); + if (st1.st_ctime > time1) e(28); + if (st1.st_mtime > time1) e(29); + if (st1.st_ctime >= time2) e(30); + if (st1.st_mtime >= time2) e(31); + if (st2.st_ctime < time2) e(32); + if (st2.st_mtime < time2) e(33); + if (st2.st_ctime >= time3) e(34); + if (st2.st_mtime >= time3) e(35); + + /* If the new file is removed while it's open it should still be + * readable. */ + System("rm -rf new NEW old OLD"); + if ((fd1 = creat("new", 0644)) != 3) e(36); + if (write(fd1, "Hi there! I am Sammy the string", 33) != 33) e(37); + if (close(fd1) != 0) e(38); + if ((fd1 = creat("old", 0644)) != 3) e(39); + if (write(fd1, "I need a new name", 18) != 18) e(40); + if (close(fd1) != 0) e(41); + if ((fd1 = open("new", O_RDONLY)) != 3) e(42); + if ((fd2 = open("new", O_RDONLY)) != 4) e(43); + if (rename("old", "new") != 0) e(44); + if (stat("old", &st1) == 0) e(45); + if (close(fd1) != 0) e(46); + if ((fd1 = open("new", O_RDONLY)) != 3) e(47); + if (read(fd2, buf, BUF_SIZE) != 33) e(48); + if (strcmp(buf, "Hi there! I am Sammy the string") != 0) e(49); + if (read(fd1, buf, BUF_SIZE) != 18) e(50); + if (strcmp(buf, "I need a new name") != 0) e(51); + if (close(fd1) != 0) e(52); + if (close(fd2) != 0) e(53); +} + +void test32b() +{ + char MaxPath2[PATH_MAX]; /* Same for path */ + char MaxName2[NAME_MAX + 1]; /* Name of maximum length */ + int fd, i; + int stat_loc; + struct stat st; + + subtest = 2; + System("rm -rf ../DIR_32/*"); + + /* Test maximal file name length. */ + if ((fd = creat(MaxName, 0777)) != 3) e(1); + if (close(fd) != 0) e(2); + strcpy(MaxName2, MaxName); + MaxName2[strlen(MaxName2) - 1] ^= 1; + if (rename(MaxName, MaxName2) != 0) e(3); + if (rename(MaxName2, MaxName) != 0) e(4); + MaxName2[strlen(MaxName2) - 1] ^= 2; + if (rename(MaxName, MaxName2) != 0) e(5); + MaxPath[strlen(MaxPath) - 2] = '/'; + MaxPath[strlen(MaxPath) - 1] = 'a'; /* make ././.../a */ + if ((fd = creat(MaxPath, 0777)) != 3) e(6); + if (close(fd) != 0) e(7); + strcpy(MaxPath2, MaxPath); + MaxPath2[strlen(MaxPath2) - 1] ^= 1; + if (rename(MaxPath, MaxPath2) != 0) e(8); + if (rename(MaxPath2, MaxPath) != 0) e(9); + MaxPath2[strlen(MaxPath2) - 1] ^= 2; + if (rename(MaxPath, MaxPath2) != 0) e(10); + MaxPath[strlen(MaxPath) - 1] = '/'; /* make ././.../a */ + + /* Test if linked files are renamable. */ + System("> foo; ln foo bar"); + if (rename("foo", "bar") != 0) e(11); + if (rename("bar", "foo") != 0) e(12); + System("ln foo foobar"); + if (rename("foo", "foobar") != 0) e(13); + if (rename("bar", "foobar") != 0) e(14); + + /* Since the same files have the same links.... */ + if (rename("bar", "bar") != 0) e(15); + if (rename("foo", "foo") != 0) e(16); + if (rename("foobar", "foobar") != 0) e(17); + + /* In ``rename(old, new)'' with new existing, there is always an new + * entry. */ + for (i = 0; i < 5; i++) { + System("echo old > old"); + System("echo news > new"); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + sleep(1); + rename("old", "new"); + exit(0); + default: + while (stat("old", &st) != 0) + if (stat("new", &st) != 0) e(18); + wait(&stat_loc); + if (stat_loc != 0) e(19); /* Alarm? */ + } + } + +} + +void test32c() +{ /* Test behavior under error contitions. */ + struct stat st1; + int stat_loc; + + subtest = 3; + System("rm -rf ../DIR_32/*"); + + /* Test if we have access. */ + system("chmod 777 noacc nowrite > /dev/null 2>/dev/null"); + system("rm -rf noacc nowrite"); + + System("mkdir noacc nowrite"); + System("> noacc/file"); + System("> nowrite/file"); + System("> file"); + System("chmod 677 noacc"); + System("chmod 577 nowrite"); + if (!superuser) { + if (rename("noacc/file", "nono") != -1) e(1); + if (errno != EACCES) e(2); + if (rename("nowrite/file", "nono") != -1) e(3); + if (errno != EACCES) e(4); + if (rename("file", "noacc/file") != -1) e(5); + if (errno != EACCES) e(6); + if (rename("file", "nowrite/file") != -1) e(7); + if (errno != EACCES) e(8); + } + if (superuser) { + /* Super user heeft access. */ + if (rename("noacc/file", "noacc/yes") != 0) e(9); + if (rename("nowrite/file", "nowrite/yes") != 0) e(10); + if (rename("file", "yes") != 0) e(11); + if (rename("noacc/yes", "noacc/file") != 0) e(12); + if (rename("nowrite/yes", "nowrite/file") != 0) e(13); + if (rename("yes", "file") != 0) e(14); + } + System("chmod 777 noacc nowrite"); + + /* If rmdir() doesn't remove a directory, rename() shouldn't eighter. */ + System("mkdir newdir olddir"); + System("rm -rf /tmp/sema.11[ab]"); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + /* Child A. */ + alarm(20); + if (chdir("newdir") != 0) e(15); + Creat("/tmp/sema.11a"); + while (stat("/tmp/sema.11a", &st1) == -1) sleep(1); + exit(0); + default: + wait(&stat_loc); + if (stat_loc != 0) e(16); /* Alarm? */ + } + + /* Child B. */ + if (chdir("olddir") != 0) e(17); + Creat("/tmp/sema.11b"); + while (stat("/tmp/sema.11b", &st1) == -1) sleep(1); + exit(0); + default: + /* Wait for child A. It will keep ``newdir'' bussy. */ + while (stat("/tmp/sema.11a", &st1) == -1) sleep(1); + if (rmdir("newdir") == -1) { + if (rename("olddir", "newdir") != -1) e(18); + if (errno != EBUSY) e(19); + } + (void) unlink("/tmp/sema.11a"); + + /* Wait for child B. It will keep ``olddir'' bussy. */ + while (stat("/tmp/sema.11b", &st1) == -1) sleep(1); + if (rmdir("olddir") == -1) { + if (rename("olddir", "newdir") != -1) e(20); + if (errno != EBUSY) e(21); + } + (void) unlink("/tmp/sema.11b"); + wait(&stat_loc); + if (stat_loc != 0) e(22); /* Alarm? */ + } +} + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(ToLongName, MaxName); /* copy them Max to ToLong */ + strcpy(ToLongPath, MaxPath); + + ToLongName[NAME_MAX] = 'a'; + ToLongName[NAME_MAX + 1] = '\0'; /* extend ToLongName by one too many */ + ToLongPath[PATH_MAX - 1] = '/'; + ToLongPath[PATH_MAX] = '\0'; /* inc ToLongPath by one */ +} + + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_32"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test33.c b/test/test33.c new file mode 100644 index 000000000..98d777515 --- /dev/null +++ b/test/test33.c @@ -0,0 +1,658 @@ +/* test33: access() Author: Jan-Mark Wams (jms@cs.vu.nl) */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> + +#define MAX_ERROR 1 +#define ITERATIONS 2 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) +#define Chmod(a,b) if (chmod(a,b) != 0) printf("Can't chmod %s\n", a) +#define Mkfifo(f) if (mkfifo(f,0777)!=0) printf("Can't make fifo %s\n", f) + +int errct = 0; +int subtest = 1; +int superuser; /* nonzero if uid == euid (euid == 0 always) */ +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; /* Same for path, both too long */ + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test33a, (void)); +_PROTOTYPE(void test33b, (void)); +_PROTOTYPE(void test33c, (void)); +_PROTOTYPE(void test33d, (void)); +_PROTOTYPE(void test_access, (void)); +_PROTOTYPE(void makelongnames, (void)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 33 "); + fflush(stdout); + + if (geteuid() != 0) { + printf("must be setuid root; test aborted\n"); + exit(1); + } + if (getuid() == 0) { + printf("must be setuid root logged in as someone else; test aborted\n"); + exit(1); + } + + umask(0000); + System("rm -rf DIR_33; mkdir DIR_33"); + Chdir("DIR_33"); + makelongnames(); + superuser = (getuid() == 0); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test33a(); + if (m & 0002) test33b(); + if (m & 0004) test33c(); + if (m & 0010) test33d(); + } + quit(); +} + +void test33a() +{ /* Test normal operation. */ + int stat_loc; /* For the wait(&stat_loc) call. */ + + subtest = 1; + System("rm -rf ../DIR_33/*"); + + /* To test normal access first make some files for real uid. */ + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + setuid(getuid()); /* (Re)set the effective ids to the + * real ids. */ + setgid(getgid()); + System("> rwx; chmod 700 rwx"); + System("> rw_; chmod 600 rw_"); + System("> r_x; chmod 500 r_x"); + System("> r__; chmod 400 r__"); + System("> _wx; chmod 300 _wx"); + System("> _w_; chmod 200 _w_"); + System("> __x; chmod 100 __x"); + System("> ___; chmod 000 ___"); + exit(0); + + default: + wait(&stat_loc); + if (stat_loc != 0) e(1);/* Alarm? */ + } + test_access(); + + /* Let's test access() on directorys. */ + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + setuid(getuid()); /* (Re)set the effective ids to the + * real ids. */ + setgid(getgid()); + System("rm -rf [_r][_w][_x]"); + System("mkdir rwx; chmod 700 rwx"); + System("mkdir rw_; chmod 600 rw_"); + System("mkdir r_x; chmod 500 r_x"); + System("mkdir r__; chmod 400 r__"); + System("mkdir _wx; chmod 300 _wx"); + System("mkdir _w_; chmod 200 _w_"); + System("mkdir __x; chmod 100 __x"); + System("mkdir ___; chmod 000 ___"); + exit(0); + + default: + wait(&stat_loc); + if (stat_loc != 0) e(2);/* Alarm? */ + } + test_access(); + + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + setuid(getuid()); /* (Re)set the effective ids to the + * real ids. */ + setgid(getgid()); + System("rmdir [_r][_w][_x]"); + Mkfifo("rwx"); + System("chmod 700 rwx"); + Mkfifo("rw_"); + System("chmod 600 rw_"); + Mkfifo("r_x"); + System("chmod 500 r_x"); + Mkfifo("r__"); + System("chmod 400 r__"); + Mkfifo("_wx"); + System("chmod 300 _wx"); + Mkfifo("_w_"); + System("chmod 200 _w_"); + Mkfifo("__x"); + System("chmod 100 __x"); + Mkfifo("___"); + System("chmod 000 ___"); + exit(0); + + default: + wait(&stat_loc); + if (stat_loc != 0) e(3);/* Alarm? */ + } + test_access(); + + /* Remove all the fifos. */ + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + setuid(getuid()); + setgid(getgid()); + System("rm -rf [_r][_w][_x]"); + exit(0); + + default: + wait(&stat_loc); + if (stat_loc != 0) e(4);/* Alarm? */ + } +} + + + +void test33b() +{ + int stat_loc; /* For the wait(&stat_loc) call. */ + + subtest = 2; + System("rm -rf ../DIR_33/*"); + + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + + /* (Re)set the effective ids to the real ids. */ + setuid(getuid()); + setgid(getgid()); + System("> ______rwx; chmod 007 ______rwx"); + System("> ________x; chmod 001 ________x"); + System("> _________; chmod 000 _________"); + exit(0); + + default: + wait(&stat_loc); + if (stat_loc != 0) e(1);/* Alarm? */ + } + + /* If we are superuser, we have access to all. */ + /* Well, almost, execution access might need at least one X bit. */ + if (superuser) { + if (access("_________", R_OK) != 0) e(2); + if (access("_________", W_OK) != 0) e(3); + if (access("________x", R_OK) != 0) e(4); + if (access("________x", W_OK) != 0) e(5); + if (access("________x", X_OK) != 0) e(6); + if (access("______rwx", R_OK) != 0) e(7); + if (access("______rwx", W_OK) != 0) e(8); + if (access("______rwx", X_OK) != 0) e(9); + } + if (!superuser) { + if (access("_________", R_OK) != -1) e(10); + if (errno != EACCES) e(11); + if (access("_________", W_OK) != -1) e(12); + if (errno != EACCES) e(13); + if (access("_________", X_OK) != -1) e(14); + if (errno != EACCES) e(15); + if (access("________x", R_OK) != -1) e(16); + if (errno != EACCES) e(17); + if (access("________x", W_OK) != -1) e(18); + if (errno != EACCES) e(19); + if (access("________x", X_OK) != -1) e(20); + if (errno != EACCES) e(21); + if (access("______rwx", R_OK) != -1) e(22); + if (errno != EACCES) e(23); + if (access("______rwx", W_OK) != -1) e(24); + if (errno != EACCES) e(25); + if (access("______rwx", X_OK) != -1) e(26); + if (errno != EACCES) e(27); + } + + /* If the real uid != effective uid. */ + if (!superuser) { + System("rm -rf [_r][_w][_x]"); + System("> rwx"); + Chmod("rwx", 0700); + System("> rw_"); + Chmod("rw_", 0600); + System("> r_x"); + Chmod("r_x", 0500); + System("> r__"); + Chmod("r__", 0400); + System("> _wx"); + Chmod("_wx", 0300); + System("> _w_"); + Chmod("_w_", 0200); + System("> __x"); + Chmod("__x", 0100); + System("> ___"); + Chmod("___", 0000); + + if (access("rwx", F_OK) != 0) e(28); + if (access("rwx", R_OK) != -1) e(29); + if (errno != EACCES) e(30); + if (access("rwx", W_OK) != -1) e(31); + if (errno != EACCES) e(32); + if (access("rwx", X_OK) != -1) e(33); + if (errno != EACCES) e(34); + + if (access("rw_", F_OK) != 0) e(35); + if (access("rw_", R_OK) != -1) e(36); + if (errno != EACCES) e(37); + if (access("rw_", W_OK) != -1) e(38); + if (errno != EACCES) e(39); + if (access("rw_", X_OK) != -1) e(40); + if (errno != EACCES) e(41); + + if (access("r_x", F_OK) != 0) e(42); + if (access("r_x", R_OK) != -1) e(43); + if (errno != EACCES) e(44); + if (access("r_x", W_OK) != -1) e(45); + if (errno != EACCES) e(46); + if (access("r_x", X_OK) != -1) e(47); + if (errno != EACCES) e(48); + + if (access("r__", F_OK) != 0) e(49); + if (access("r__", R_OK) != -1) e(50); + if (errno != EACCES) e(51); + if (access("r__", W_OK) != -1) e(52); + if (errno != EACCES) e(53); + if (access("r__", X_OK) != -1) e(54); + if (errno != EACCES) e(55); + + if (access("_wx", F_OK) != 0) e(56); + if (access("_wx", R_OK) != -1) e(57); + if (errno != EACCES) e(58); + if (access("_wx", W_OK) != -1) e(59); + if (errno != EACCES) e(60); + if (access("_wx", X_OK) != -1) e(61); + if (errno != EACCES) e(62); + + if (access("_w_", F_OK) != 0) e(63); + if (access("_w_", R_OK) != -1) e(64); + if (errno != EACCES) e(65); + if (access("_w_", W_OK) != -1) e(66); + if (errno != EACCES) e(67); + if (access("_w_", X_OK) != -1) e(68); + if (errno != EACCES) e(69); + + if (access("__x", F_OK) != 0) e(70); + if (access("__x", R_OK) != -1) e(71); + if (errno != EACCES) e(72); + if (access("__x", W_OK) != -1) e(73); + if (errno != EACCES) e(74); + if (access("__x", X_OK) != -1) e(75); + if (errno != EACCES) e(76); + + if (access("___", F_OK) != 0) e(77); + if (access("___", R_OK) != -1) e(78); + if (errno != EACCES) e(79); + if (access("___", W_OK) != -1) e(80); + if (errno != EACCES) e(81); + if (access("___", X_OK) != -1) e(82); + if (errno != EACCES) e(83); + + System("rm -rf [_r][_w][_x]"); + } +} + +void test33c() +{ /* Test errors returned. */ + int i; + + subtest = 3; + System("rm -rf ../DIR_33/*"); + + /* Test what access() does with non existing files. */ + System("rm -rf nonexist"); + if (access("noexist", F_OK) != -1) e(1); + if (errno != ENOENT) e(2); + if (access("noexist", R_OK) != -1) e(3); + if (errno != ENOENT) e(4); + if (access("noexist", W_OK) != -1) e(5); + if (errno != ENOENT) e(6); + if (access("noexist", X_OK) != -1) e(7); + if (errno != ENOENT) e(8); + if (access("noexist", R_OK | W_OK) != -1) e(9); + if (errno != ENOENT) e(10); + if (access("noexist", R_OK | X_OK) != -1) e(11); + if (errno != ENOENT) e(12); + if (access("noexist", W_OK | X_OK) != -1) e(13); + if (errno != ENOENT) e(14); + if (access("noexist", R_OK | W_OK | X_OK) != -1) e(15); + if (errno != ENOENT) e(16); + + /* Test access on a nonsearchable path. */ + if (mkdir("nosearch", 0777) != 0) e(1000); + if ( (i = creat("nosearch/file", 0666)) < 0) e(1001); + if (close(i) < 0) e(1002); + if ( (i = creat("file", 0666) < 0)) e(1003); + if (close(i) < 0) e(1004); + if (chmod("nosearch/file", 05777) < 0) e(1005); + if (chmod("file", 05777) < 0) e(1006); + if (chmod("nosearch", 0677) != 0) e(1007); + if (access("nosearch/file", F_OK) != 0) e(17); + + /* Test ToLongName and ToLongPath */ +#ifdef _POSIX_NO_TRUNC +# if _POSIX_NO_TRUNC - 0 != -1 + if (access(ToLongName, F_OK) != -1) e(23); + if (errno != ENAMETOOLONG) e(24); +# else + if (close(creat(ToLongName, 0777)) != 0) e(25); + if (access(ToLongName, F_OK) != 0) e(26); +# endif +#else +# include "error, this case requires dynamic checks and is not handled" +#endif + ToLongPath[PATH_MAX - 2] = '/'; + ToLongPath[PATH_MAX - 1] = 'a'; + if (access(ToLongPath, F_OK) != -1) e(27); + if (errno != ENAMETOOLONG) e(28); + ToLongPath[PATH_MAX - 1] = '/'; + + /* Test empty strings. */ + if (access("", F_OK) != -1) e(29); + if (errno != ENOENT) e(30); + System("rm -rf idonotexist"); + if (access("idonotexist", F_OK) != -1) e(31); + if (errno != ENOENT) e(32); + + /* Test non directorys in prefix of path. */ + if (access("/etc/passwd/dir/foo", F_OK) != -1) e(33); + if (errno != ENOTDIR) e(34); + System("rm -rf nodir; > nodir"); + if (access("nodir/foo", F_OK) != -1) e(35); + if (errno != ENOTDIR) e(36); + + /* Test if invalid amode arguments are signaled. */ + System("> allmod"); + Chmod("allmod", 05777); + for (i = -1025; i < 1025; i++) { + if ((mode_t) i != F_OK && ((mode_t) i & ~(R_OK | W_OK | X_OK)) != 0) { + if (access("allmod", (mode_t) i) != -1) e(37); + if (errno != EINVAL) e(38); + } else + if (access("allmod", (mode_t) i) != 0) e(39); + } +} + +void test33d() +{ /* Test access() flags. */ +#define EXCLUDE(a,b) (((a)^(b)) == ((a)|(b))) + subtest = 4; + System("rm -rf ../DIR_33/*"); + + /* The test are rather strong, stronger that POSIX specifies. */ + /* The should be OR able, this test tests if all the 1 bits */ + /* Are in diferent places. This should be what one wants. */ + if (!EXCLUDE(R_OK, W_OK | X_OK)) e(1); + if (!EXCLUDE(W_OK, R_OK | X_OK)) e(2); + if (!EXCLUDE(X_OK, R_OK | W_OK)) e(3); + if (F_OK == R_OK) e(4); + if (F_OK == W_OK) e(5); + if (F_OK == X_OK) e(6); + if (F_OK == (R_OK | W_OK)) e(7); + if (F_OK == (W_OK | X_OK)) e(8); + if (F_OK == (R_OK | X_OK)) e(9); + if (F_OK == (R_OK | W_OK | X_OK)) e(10); +} + +void test_access() +{ /* Test all [_r][_w][_x] files. */ + if (!superuser) { + /* Test normal access. */ + if (access("rwx", F_OK) != 0) e(11); + if (access("rwx", R_OK) != 0) e(12); + if (access("rwx", W_OK) != 0) e(13); + if (access("rwx", X_OK) != 0) e(14); + if (access("rwx", R_OK | W_OK) != 0) e(15); + if (access("rwx", R_OK | X_OK) != 0) e(16); + if (access("rwx", W_OK | X_OK) != 0) e(17); + if (access("rwx", R_OK | W_OK | X_OK) != 0) e(18); + + if (access("rw_", F_OK) != 0) e(19); + if (access("rw_", R_OK) != 0) e(20); + if (access("rw_", W_OK) != 0) e(21); + if (access("rw_", X_OK) != -1) e(22); + if (errno != EACCES) e(23); + if (access("rw_", R_OK | W_OK) != 0) e(24); + if (access("rw_", R_OK | X_OK) != -1) e(25); + if (errno != EACCES) e(26); + if (access("rw_", W_OK | X_OK) != -1) e(27); + if (errno != EACCES) e(28); + if (access("rw_", R_OK | W_OK | X_OK) != -1) e(29); + if (errno != EACCES) e(30); + + if (access("r_x", F_OK) != 0) e(31); + if (access("r_x", R_OK) != 0) e(32); + if (access("r_x", W_OK) != -1) e(33); + if (errno != EACCES) e(34); + if (access("r_x", X_OK) != 0) e(35); + if (access("r_x", R_OK | W_OK) != -1) e(36); + if (errno != EACCES) e(37); + if (access("r_x", R_OK | X_OK) != 0) e(38); + if (access("r_x", W_OK | X_OK) != -1) e(39); + if (errno != EACCES) e(40); + if (access("r_x", R_OK | W_OK | X_OK) != -1) e(41); + if (errno != EACCES) e(42); + + if (access("r__", F_OK) != 0) e(43); + if (access("r__", R_OK) != 0) e(44); + if (access("r__", W_OK) != -1) e(45); + if (errno != EACCES) e(46); + if (access("r__", X_OK) != -1) e(47); + if (errno != EACCES) e(48); + if (access("r__", R_OK | W_OK) != -1) e(49); + if (errno != EACCES) e(50); + if (access("r__", R_OK | X_OK) != -1) e(51); + if (errno != EACCES) e(52); + if (access("r__", W_OK | X_OK) != -1) e(53); + if (errno != EACCES) e(54); + if (access("r__", R_OK | W_OK | X_OK) != -1) e(55); + if (errno != EACCES) e(56); + + if (access("_wx", F_OK) != 0) e(57); + if (access("_wx", R_OK) != -1) e(58); + if (errno != EACCES) e(59); + if (access("_wx", W_OK) != 0) e(60); + if (access("_wx", X_OK) != 0) e(61); + if (access("_wx", R_OK | W_OK) != -1) e(62); + if (errno != EACCES) e(63); + if (access("_wx", R_OK | X_OK) != -1) e(64); + if (errno != EACCES) e(65); + if (access("_wx", W_OK | X_OK) != 0) e(66); + if (access("_wx", R_OK | W_OK | X_OK) != -1) e(67); + if (errno != EACCES) e(68); + + if (access("_w_", F_OK) != 0) e(69); + if (access("_w_", R_OK) != -1) e(70); + if (errno != EACCES) e(71); + if (access("_w_", W_OK) != 0) e(72); + if (access("_w_", X_OK) != -1) e(73); + if (errno != EACCES) e(74); + if (access("_w_", R_OK | W_OK) != -1) e(75); + if (errno != EACCES) e(76); + if (access("_w_", R_OK | X_OK) != -1) e(77); + if (errno != EACCES) e(78); + if (access("_w_", W_OK | X_OK) != -1) e(79); + if (errno != EACCES) e(80); + if (access("_w_", R_OK | W_OK | X_OK) != -1) e(81); + if (errno != EACCES) e(82); + + if (access("__x", F_OK) != 0) e(83); + if (access("__x", R_OK) != -1) e(84); + if (errno != EACCES) e(85); + if (access("__x", W_OK) != -1) e(86); + if (errno != EACCES) e(87); + if (access("__x", X_OK) != 0) e(88); + if (access("__x", R_OK | W_OK) != -1) e(89); + if (errno != EACCES) e(90); + if (access("__x", R_OK | X_OK) != -1) e(91); + if (errno != EACCES) e(92); + if (access("__x", W_OK | X_OK) != -1) e(93); + if (errno != EACCES) e(94); + if (access("__x", R_OK | W_OK | X_OK) != -1) e(95); + if (errno != EACCES) e(96); + + if (access("___", F_OK) != 0) e(97); + if (access("___", R_OK) != -1) e(98); + if (errno != EACCES) e(99); + if (access("___", W_OK) != -1) e(100); + if (errno != EACCES) e(101); + if (access("___", X_OK) != -1) e(102); + if (errno != EACCES) e(103); + if (access("___", R_OK | W_OK) != -1) e(104); + if (errno != EACCES) e(105); + if (access("___", R_OK | X_OK) != -1) e(106); + if (errno != EACCES) e(107); + if (access("___", W_OK | X_OK) != -1) e(108); + if (errno != EACCES) e(109); + if (access("___", R_OK | W_OK | X_OK) != -1) e(110); + if (errno != EACCES) e(111); + } + if (superuser) { + /* Test root access don't test X_OK on [_r][_w]_ files. */ + if (access("rwx", F_OK) != 0) e(112); + if (access("rwx", R_OK) != 0) e(113); + if (access("rwx", W_OK) != 0) e(114); + if (access("rwx", X_OK) != 0) e(115); + if (access("rwx", R_OK | W_OK) != 0) e(116); + if (access("rwx", R_OK | X_OK) != 0) e(117); + if (access("rwx", W_OK | X_OK) != 0) e(118); + if (access("rwx", R_OK | W_OK | X_OK) != 0) e(119); + + if (access("rw_", F_OK) != 0) e(120); + if (access("rw_", R_OK) != 0) e(121); + if (access("rw_", W_OK) != 0) e(122); + if (access("rw_", R_OK | W_OK) != 0) e(123); + + if (access("r_x", F_OK) != 0) e(124); + if (access("r_x", R_OK) != 0) e(125); + if (access("r_x", W_OK) != 0) e(126); + if (access("r_x", X_OK) != 0) e(127); + if (access("r_x", R_OK | W_OK) != 0) e(128); + if (access("r_x", R_OK | X_OK) != 0) e(129); + if (access("r_x", W_OK | X_OK) != 0) e(130); + if (access("r_x", R_OK | W_OK | X_OK) != 0) e(131); + + if (access("r__", F_OK) != 0) e(132); + if (access("r__", R_OK) != 0) e(133); + if (access("r__", W_OK) != 0) e(134); + if (access("r__", R_OK | W_OK) != 0) e(135); + + if (access("_wx", F_OK) != 0) e(136); + if (access("_wx", R_OK) != 0) e(137); + if (access("_wx", W_OK) != 0) e(138); + if (access("_wx", X_OK) != 0) e(139); + if (access("_wx", R_OK | W_OK) != 0) e(140); + if (access("_wx", R_OK | X_OK) != 0) e(141); + if (access("_wx", W_OK | X_OK) != 0) e(142); + if (access("_wx", R_OK | W_OK | X_OK) != 0) e(143); + + if (access("_w_", F_OK) != 0) e(144); + if (access("_w_", R_OK) != 0) e(145); + if (access("_w_", W_OK) != 0) e(146); + if (access("_w_", R_OK | W_OK) != 0) e(147); + + if (access("__x", F_OK) != 0) e(148); + if (access("__x", R_OK) != 0) e(149); + if (access("__x", W_OK) != 0) e(150); + if (access("__x", X_OK) != 0) e(151); + if (access("__x", R_OK | W_OK) != 0) e(152); + if (access("__x", R_OK | X_OK) != 0) e(153); + if (access("__x", W_OK | X_OK) != 0) e(154); + if (access("__x", R_OK | W_OK | X_OK) != 0) e(155); + + if (access("___", F_OK) != 0) e(156); + if (access("___", R_OK) != 0) e(157); + if (access("___", W_OK) != 0) e(158); + if (access("___", R_OK | W_OK) != 0) e(159); + } +} + + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(ToLongName, MaxName); /* copy them Max to ToLong */ + strcpy(ToLongPath, MaxPath); + + ToLongName[NAME_MAX] = 'a'; + ToLongName[NAME_MAX + 1] = '\0'; /* extend ToLongName by one too many */ + ToLongPath[PATH_MAX - 1] = '/'; + ToLongPath[PATH_MAX] = '\0'; /* inc ToLongPath by one */ +} + + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_33"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test34.c b/test/test34.c new file mode 100644 index 000000000..d1f7f386a --- /dev/null +++ b/test/test34.c @@ -0,0 +1,668 @@ +/* test34: chmod() chown() Author: Jan-Mark Wams (jms@cs.vu.nl) */ + +/* There is a problem getting valid uids and gids, so we use the passwd +** file (ie. /etc/passwd). I don't like this, but I see no other way. +** The read-only-device-error (EROFS) is not checked! +** Supplementary group IDs are ignored. +*/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <ctype.h> +#include <time.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define ITERATIONS 4 +#define N 100 + +#define ALL_RWXB (S_IRWXU | S_IRWXG | S_IRWXO) +#define ALL_SETB (S_ISUID | S_ISGID) +#define ALL_BITS (ALL_RWXB | ALL_SETB) + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) +#define Mkfifo(f) if (mkfifo(f,0777)!=0) printf("Can't make fifo %s\n", f) +#define Mkdir(f) if (mkdir(f,0777)!=0) printf("Can't make dir %s\n", f) +#define Creat(f) if (close(creat(f,0777))!=0) printf("Can't creat %s\n",f) + +/* This program uses /etc/passwd and assumes things about it's contents. */ +#define PASSWD_FILE "/etc/passwd" + +int errct = 0; +int subtest = 1; +int superuser; +int I_can_chown; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char NameTooLong[NAME_MAX + 2]; /* Name of maximum +1 length */ +char PathTooLong[PATH_MAX + 1]; /* Same for path, both too long */ + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test34a, (void)); +_PROTOTYPE(void test34b, (void)); +_PROTOTYPE(void test34c, (void)); +_PROTOTYPE(mode_t mode, (char *file_name)); +_PROTOTYPE(void makelongnames, (void)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); +_PROTOTYPE(void getids, (uid_t * uid, gid_t * gid)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 34 "); + fflush(stdout); + (void) system("chmod 777 DIR_34/* > /dev/null 2> /dev/null"); + System("rm -rf DIR_34; mkdir DIR_34"); + if (chdir("DIR_34") != 0) { + fprintf(stderr, "Can't go to DIR_34\n"); + system("rm -rf DIR_34"); + exit(1); + } + makelongnames(); + superuser = (geteuid() == (uid_t) 0); + +#ifdef _POSIX_CHOWN_RESTRICTED + I_can_chown = superuser; +#else + I_can_chown = 1; +#endif + + umask(0000); + + for (i = 1; i < ITERATIONS; i++) { + if (m & 0001) test34a(); + if (m & 0002) test34b(); + if (m & 0004) test34c(); + } + quit(); +} + +void test34a() +{ /* Test normal operation. */ + time_t time1, time2; + mode_t mod; + struct stat st1, st2; + int cnt; + uid_t uid, uid2; + gid_t gid, gid2; + int stat_loc; + + subtest = 1; + + /* Make scratch file. */ + Creat("foo"); + + for (mod = 0; mod <= ALL_BITS; mod++) { + if ((mod & ALL_BITS) != mod) /* If not a valid mod next. */ + continue; + Stat("foo", &st1); + if (time(&time1) == (time_t) - 1) e(1); + if (chmod("foo", mod) != 0) e(2); + Stat("foo", &st2); + if (time(&time2) == (time_t) - 1) e(3); + if (superuser) + if ((st2.st_mode & ALL_BITS) != mod) e(4); + if (!superuser) + if ((st2.st_mode & ALL_RWXB) != (mod & ALL_RWXB)) e(5); + + /* Test the C time feald. */ + if (st1.st_ctime > st2.st_ctime) e(6); + if (st1.st_ctime > time1) e(7); + if (st1.st_ctime > time2) e(8); +#ifndef V1_FILESYSTEM + if (st2.st_ctime < time1) e(9); +#endif + if (st2.st_ctime > time2) e(10); + if (st1.st_atime != st2.st_atime) e(11); + if (st1.st_mtime != st2.st_mtime) e(12); + } /* End for loop. */ + + /* Check if chown(file, geteuid(), getegid()) works. */ + for (cnt = 0; cnt < 20; cnt++) { + /* Set all rights on foo, including the set .id bits. */ + if (chmod("foo", ALL_BITS) != 0) e(13); + Stat("foo", &st1); + if (time(&time1) == (time_t) -1) e(14); + + if (chown("foo", geteuid(), getegid()) != 0) e(15); + Stat("foo", &st2); + if (time(&time2) == (time_t) -1) e(16); + + /* Check ``chown()'' killed the set .id bits. */ + if (!superuser) { + if ((st1.st_mode & ALL_RWXB) != ALL_RWXB) e(17); + if ((st2.st_mode & ALL_BITS) != ALL_RWXB) e(18); + } + if (superuser) { + if ((st1.st_mode & ALL_BITS) != ALL_BITS) e(19); + if ((st1.st_mode & ALL_RWXB) != ALL_RWXB) e(20); + } + + /* Check the timing. */ + if (st1.st_ctime > st2.st_ctime) e(21); + if (st1.st_ctime > time1) e(22); + if (st1.st_ctime > time2) e(23); +#ifndef V1_FILESYSTEM + if (st2.st_ctime < time1) e(24); +#endif + if (st2.st_ctime > time2) e(25); + if (st1.st_atime != st2.st_atime) e(26); + if (st1.st_mtime != st2.st_mtime) e(27); + } /* End for loop. */ + + /* Make scratch file. */ + if (chmod("foo", ALL_RWXB) != 0) e(28); + + if (I_can_chown) { + /* Do a 20 tests on a gid and uid. */ + for (cnt = 0; cnt < 20; cnt++) { + /* Get a uid and a gid, test chown. */ + getids(&uid, &gid); + Stat("foo", &st1); + if (time(&time1) == (time_t) -1) e(29); + if (chown("foo", (uid_t) 0, (gid_t) 0) != 0) e(30); + Stat("foo", &st2); + if (time(&time2) == (time_t) -1) e(31); + + /* Test the C time field. */ + if (st1.st_ctime > st2.st_ctime) e(32); + if (st1.st_ctime > time1) e(33); + if (st1.st_ctime > time2) e(34); + if (st2.st_ctime < time1) e(35); + if (st2.st_ctime > time2) e(36); + if (st1.st_atime != st2.st_atime) e(37); + if (st1.st_mtime != st2.st_mtime) e(38); + + /* Do aditional tests. */ + if (chown("foo", (uid_t) 0, gid) != 0) e(39); + if (chown("foo", uid, (gid_t) 0) != 0) e(40); + if (chown("foo", uid, gid) != 0) e(41); + } + } + if (superuser) { + /* Check if a non-superuser can change a files gid to gid2 * + * if gid2 is the current process gid. */ + for (cnt = 0; cnt < 5; cnt++) { + switch (fork()) { + case -1: + printf("Can't fork\n"); + break; + case 0: + alarm(20); + + getids(&uid, &gid); + if (uid == 0) { + getids(&uid, &gid); + if (uid == 0) e(42); + } + getids(&uid2, &gid2); + if (gid == gid2) e(43); + + /* Creat boo and bar for user uid of group gid. */ + Creat("boo"); + if (chown("boo", uid, gid) != 0) e(44); + if (chmod("boo", ALL_BITS) != 0) e(45); + Creat("bar"); + if (chown("bar", uid, gid) != 0) e(46); + if (chmod("bar", ALL_BITS) != 0) e(47); + + /* We now become user uid of group gid2. */ + setgid(gid2); + setuid(uid); + + Stat("bar", &st1); + if (time(&time1) == (time_t) -1) e(48); + if (chown("bar", uid, gid2) != 0) e(49); + Stat("bar", &st2); + if (time(&time2) == (time_t) -1) e(50); + + /* Check if the SET_BITS are cleared. */ + if ((st1.st_mode & ALL_BITS) != ALL_BITS) e(51); + if ((st2.st_mode & ALL_BITS) != ALL_RWXB) e(52); + + /* Check the st_times. */ + if (st1.st_ctime > st2.st_ctime) e(53); + if (st1.st_ctime > time1) e(54); + if (st1.st_ctime > time2) e(55); + if (st2.st_ctime < time1) e(56); + if (st2.st_ctime > time2) e(57); + if (st1.st_atime != st2.st_atime) e(58); + if (st1.st_mtime != st2.st_mtime) e(59); + + Stat("boo", &st1); + if (chmod("boo", ALL_BITS) != 0) e(60); + Stat("boo", &st2); + + /* Check if the set gid bit is cleared. */ + if ((st1.st_mode & ALL_RWXB) != ALL_RWXB) e(61); + if ((st2.st_mode & S_ISGID) != 0) e(62); + + if (chown("boo", uid, gid2) != 0) e(63); + Stat("boo", &st1); + + /* Check if the set uid bit is cleared. */ + if ((st1.st_mode & S_ISUID) != 0) e(64); + + exit(0); + default: + wait(&stat_loc); + if (stat_loc != 0) e(65); /* Alarm? */ + } + } /* end for loop. */ + } /* end if (superuser). */ + if (chmod("foo", ALL_BITS) != 0) e(66); + Stat("foo", &st1); + if (chown("foo", geteuid(), getegid()) != 0) e(67); + Stat("foo", &st2); + if ((st1.st_mode & ALL_BITS) != ALL_BITS) e(68); /* See intro! */ + if (superuser) + if ((st2.st_mode & ALL_RWXB) != ALL_RWXB) e(69); + if (!superuser) + if ((st2.st_mode & ALL_BITS) != ALL_RWXB) e(70); + + (void) system("chmod 777 ../DIR_34/* > /dev/null 2> /dev/null"); + System("rm -rf ../DIR_34/*"); +} + +void test34b() +{ + time_t time1, time2; + mode_t mod; + struct stat st1, st2; + + subtest = 2; + + /* Test chmod() and chown() on non regular files and on MaxName and + * MaxPath. * Funny, but dirs should also have S_IS.ID bits. + */ + Mkfifo("fifo"); + Mkdir("dir"); + Creat(MaxName); + MaxPath[strlen(MaxPath) - 2] = '/'; + MaxPath[strlen(MaxPath) - 1] = 'a'; /* make ././.../a */ + Creat(MaxPath); + + for (mod = 1; mod <= ALL_BITS; mod <<= 1) { + if ((mod & ALL_BITS) != mod) continue; /* bad mod */ + Stat("dir", &st1); + if (time(&time1) == (time_t) -1) e(1); + if (chmod("dir", mod) != 0) e(2); + Stat("dir", &st2); + if (time(&time2) == (time_t) -1) e(3); + if (superuser) + if ((st2.st_mode & ALL_BITS) != mod) e(4); + if (!superuser) + if ((st2.st_mode & ALL_RWXB) != (mod & ALL_RWXB)) e(5); + + /* Test the C time field. */ + if (st1.st_ctime > st2.st_ctime) e(6); + if (st1.st_ctime > time1) e(7); + if (st1.st_ctime > time2) e(8); +#ifndef V1_FILESYSTEM + if (st2.st_ctime < time1) e(9); +#endif + if (st2.st_ctime > time2) e(10); + if (st1.st_atime != st2.st_atime) e(11); + if (st1.st_mtime != st2.st_mtime) e(12); + + Stat("fifo", &st1); + if (time(&time1) == (time_t) -1) e(13); + if (chmod("fifo", mod) != 0) e(14); + Stat("fifo", &st2); + if (time(&time2) == (time_t) -1) e(15); + if (superuser) + if ((st2.st_mode & ALL_BITS) != mod) e(16); + if (!superuser) + if ((st2.st_mode & ALL_RWXB) != (mod & ALL_RWXB)) e(17); + + /* Test the C time field. */ + if (st1.st_ctime > st2.st_ctime) e(18); + if (st1.st_ctime > time1) e(19); + if (st1.st_ctime > time2) e(20); +#ifndef V1_FILESYSTEM + if (st2.st_ctime < time1) e(21); +#endif + if (st2.st_ctime > time2) e(22); + if (st1.st_atime != st2.st_atime) e(23); + if (st1.st_mtime != st2.st_mtime) e(24); + + Stat(MaxName, &st1); + if (time(&time1) == (time_t) -1) e(25); + if (chmod(MaxName, mod) != 0) e(26); + Stat(MaxName, &st2); + if (time(&time2) == (time_t) -1) e(27); + if (superuser) + if ((st2.st_mode & ALL_BITS) != mod) e(28); + if (!superuser) + if ((st2.st_mode & ALL_RWXB) != (mod & ALL_RWXB)) e(29); + + /* Test the C time field. */ + if (st1.st_ctime > st2.st_ctime) e(30); + if (st1.st_ctime > time1) e(31); + if (st1.st_ctime > time2) e(32); +#ifndef V1_FILESYSTEM + if (st2.st_ctime < time1) e(33); +#endif + if (st2.st_ctime > time2) e(34); + if (st1.st_atime != st2.st_atime) e(35); + if (st1.st_mtime != st2.st_mtime) e(36); + + Stat(MaxPath, &st1); + if (time(&time1) == (time_t) -1) e(37); + if (chmod(MaxPath, mod) != 0) e(38); + Stat(MaxPath, &st2); + if (time(&time2) == (time_t) -1) e(39); + if (superuser) + if ((st2.st_mode & ALL_BITS) != mod) e(40); + if (!superuser) + if ((st2.st_mode & ALL_RWXB) != (mod & ALL_RWXB)) e(41); + + /* Test the C time field. */ + if (st1.st_ctime > st2.st_ctime) e(42); + if (st1.st_ctime > time1) e(43); + if (st1.st_ctime > time2) e(44); +#ifndef V1_FILESYSTEM + if (st2.st_ctime < time1) e(45); +#endif + if (st2.st_ctime > time2) e(46); + if (st1.st_atime != st2.st_atime) e(47); + if (st1.st_mtime != st2.st_mtime) e(48); + } + + if (chmod("dir", 0777) != 0) e(49); + if (chmod("fifo", 0777) != 0) e(50); + if (chmod(MaxName, 0777) != 0) e(51); + if (chmod(MaxPath, 0777) != 0) e(52); + + (void) system("chmod 777 ../DIR_34/* > /dev/null 2> /dev/null"); + System("rm -rf ../DIR_34/*"); +} + +void test34c() +{ + struct stat st; + uid_t uid, uid2; + gid_t gid, gid2; + int stat_loc; + + subtest = 3; + + Mkdir("dir"); + Creat("dir/try_me"); + + /* Disalow search permission and see if chmod() and chown() return + * EACCES. + */ + if (chmod("dir", ALL_BITS & ~S_IXUSR) != 0) e(1); + if (!superuser) { + if (chmod("dir/try_me", 0) != -1) e(2); + if (errno != EACCES) e(3); + if (I_can_chown) { + if (chown("dir/try_me", geteuid(), getegid()) != -1) e(4); + if (errno != EACCES) e(5); + } + } + + /* Check ENOTDIR. */ + Mkfifo("fifo"); + if (chmod("fifo/try_me", 0) != -1) e(6); + if (errno != ENOTDIR) e(7); + if (chown("fifo/try_me", geteuid(), getegid()) != -1) e(8); + if (errno != ENOTDIR) e(9); + + Creat("file"); + if (chmod("file/try_me", 0) != -1) e(10); + if (errno != ENOTDIR) e(11); + if (chown("file/try_me", geteuid(), getegid()) != -1) e(12); + if (errno != ENOTDIR) e(13); + + /* Check empty path. */ + if (chmod("", 0) != -1) e(14); + if (errno != ENOENT) e(15); + if (chown("", geteuid(), getegid()) != -1) e(16); + if (errno != ENOENT) e(17); + + /* Check non existing file name. */ + if (chmod("non_exist", 0) != -1) e(18); + if (errno != ENOENT) e(19); + if (chown("non_exist", geteuid(), getegid()) != -1) e(20); + if (errno != ENOENT) e(21); + + /* Check what we get if we do not have permisson. */ + if (!superuser) { + Stat("/", &st); + if (st.st_uid == geteuid()) e(22); + + /* First I had 0, I changed it to st.st_mode 8-). */ + if (chmod("/", st.st_mode) != -1) e(23); + if (errno != EPERM) e(24); + } + if (!I_can_chown) { + Stat("/", &st); + if (st.st_uid == geteuid()) e(25); + if (chown("/", geteuid(), getegid()) != -1) e(26); + if (errno != EPERM) e(27); + } + + /* If we are superuser, we can test all id combinations. */ + if (superuser) { + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + + getids(&uid, &gid); + if (uid == 0) { + getids(&uid, &gid); + if (uid == 0) e(28); + } + getids(&uid2, &gid2); + if (gid == gid2) e(29); + if (uid == uid2) e(30); + + /* Creat boo, owned by root. */ + Creat("boo"); + if (chmod("boo", ALL_BITS) != 0) e(31); + + /* Creat boo for user uid2 of group gid2. */ + Creat("bar"); + if (chown("bar", uid2, gid2) != 0) e(32); + if (chmod("bar", ALL_BITS) != 0) e(33); + + /* Creat my_gid for user uid2 of group gid. */ + Creat("my_gid"); + if (chown("my_gid", uid2, gid) != 0) e(34); + if (chmod("my_gid", ALL_BITS) != 0) e(35); + + /* Creat my_uid for user uid of uid gid. */ + Creat("my_uid"); + if (chown("my_uid", uid, gid) != 0) e(36); + if (chmod("my_uid", ALL_BITS) != 0) e(37); + + /* We now become user uid of uid gid. */ + setgid(gid); + setuid(uid); + + if (chown("boo", uid, gid) != -1) e(38); + if (errno != EPERM) e(39); + if (chown("bar", uid, gid) != -1) e(40); + if (errno != EPERM) e(41); + if (chown("my_gid", uid, gid) != -1) e(42); + if (errno != EPERM) e(43); + if (chown("my_uid", uid, gid2) != -1) e(44); + + /* The EPERM is not strict POSIX. */ + if (errno != EPERM) e(45); + + if (chmod("boo", 0) != -1) e(46); + if (errno != EPERM) e(47); + if (chmod("bar", 0) != -1) e(48); + if (errno != EPERM) e(49); + if (chmod("my_gid", 0) != -1) e(50); + if (errno != EPERM) e(51); + + exit(0); + default: + wait(&stat_loc); + if (stat_loc != 0) e(52); /* Alarm? */ + } + } + + /* Check too long path ed. */ + Creat(NameTooLong); + if (chmod(NameTooLong, 0777) != 0) e(57); + if (chown(NameTooLong, geteuid(), getegid()) != 0) e(58); + + /* Make PathTooLong contain ././.../a */ + PathTooLong[strlen(PathTooLong) - 2] = '/'; + PathTooLong[strlen(PathTooLong) - 1] = 'a'; + Creat("a"); + if (chmod(PathTooLong, 0777) != -1) e(59); + if (errno != ENAMETOOLONG) e(60); + if (chown(PathTooLong, geteuid(), getegid()) != -1) e(61); + if (errno != ENAMETOOLONG) e(62); + + (void) system("chmod 777 ../DIR_34/* > /dev/null 2> /dev/null"); + System("rm -rf ../DIR_34/*"); +} + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(NameTooLong, MaxName); /* copy them Max to TooLong */ + strcpy(PathTooLong, MaxPath); + + NameTooLong[NAME_MAX] = 'a'; + NameTooLong[NAME_MAX + 1] = '\0'; /* extend NameTooLong by one too many*/ + PathTooLong[PATH_MAX - 1] = '/'; + PathTooLong[PATH_MAX] = '\0'; /* inc PathTooLong by one */ +} + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + system("rm -rf DIR_34"); + exit(1); + } + errno = 0; +} + +void quit() +{ + Chdir(".."); + (void) system("chmod 777 DIR_34/* > /dev/null 2> /dev/null"); + System("rm -rf DIR_34"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} + +/* Getids returns a valid uid and gid. Is used PASSWD FILE. + * It assumes the following format for a passwd file line: + * <user_name>:<passwd>:<uid>:<gid>:<other_stuff> + * If no uids and gids can be found, it will only return 0 ids. + */ +void getids(r_uid, r_gid) +uid_t * r_uid; +gid_t * r_gid; +{ + char line[N]; + char *p; + uid_t uid; + gid_t gid; + FILE *fp; + int i; + + static uid_t a_uid[N]; /* Array for uids. */ + static gid_t a_gid[N]; /* Array for gids. */ + static int nuid = 0, ngid = 0;/* The number of user & group ids. */ + static int cuid = 0, cgid = 0;/* The current id index. */ + + /* If we don't have any uids go read some from the passwd file. */ + if (nuid == 0) { + a_uid[nuid++] = 0; /* Root uid and gid. */ + a_gid[ngid++] = 0; + if ((fp = fopen(PASSWD_FILE, "r")) == NULL) { + printf("Can't open "); + perror(PASSWD_FILE); + } + while (fp != NULL && fgets(line, sizeof(line), fp) != NULL) { + p = strchr(line, ':'); + if (p != NULL) p = strchr(p + 1, ':'); + if (p != NULL) { + p++; + uid = 0; + while (isdigit(*p)) { + uid *= 10; + uid += (uid_t) (*p - '0'); + p++; + } + if (*p != ':') continue; + p++; + gid = 0; + while (isdigit(*p)) { + gid *= 10; + gid += (gid_t) (*p - '0'); + p++; + } + if (*p != ':') continue; + if (nuid < N) { + for (i = 0; i < nuid; i++) + if (a_uid[i] == uid) break; + if (i == nuid) a_uid[nuid++] = uid; + } + if (ngid < N) { + for (i = 0; i < ngid; i++) + if (a_gid[i] == gid) break; + if (i == ngid) a_gid[ngid++] = gid; + } + if (nuid >= N && ngid >= N) break; + } + } + if (fp != NULL) fclose(fp); + } + + /* We now have uids and gids in a_uid and a_gid. */ + if (cuid >= nuid) cuid = 0; + if (cgid >= ngid) cgid = 0; + *r_uid = a_uid[cuid++]; + *r_gid = a_gid[cgid++]; +} diff --git a/test/test35.c b/test/test35.c new file mode 100644 index 000000000..7459cc4c4 --- /dev/null +++ b/test/test35.c @@ -0,0 +1,422 @@ +/* test35: utime() Author: Jan-Mark Wams (jms@cs.vu.nl) */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <utime.h> +#include <errno.h> +#include <time.h> +#include <ctype.h> +#include <stdio.h> + +#define MAX_ERROR 1 +#define ITERATIONS 10 +#define N 100 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) +#define Mkfifo(f) if (mkfifo(f,0777)!=0) printf("Can't make fifo %s\n", f) +#define Mkdir(f) if (mkdir(f,0777)!=0) printf("Can't make dir %s\n", f) +#define Creat(f) if (close(creat(f,0777))!=0) printf("Can't creat %s\n",f) +#define Time(t) if (time(t) == (time_t)-1) printf("Time error\n") +#define Chown(f,u,g) if (chown(f,u,g) != 0) printf("Can't chown %s\n", f) +#define Chmod(f,m) if (chmod(f,m) != 0) printf("Can't chmod %s\n", f) + +#define PASSWD_FILE "/etc/passwd" + +int errct = 0; +int subtest = 1; +int I_can_chown; +int superuser; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char NameTooLong[NAME_MAX + 2]; /* Name of maximum +1 length */ +char PathTooLong[PATH_MAX + 1]; /* Same for path, both too long */ + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test35a, (void)); +_PROTOTYPE(void test35b, (void)); +_PROTOTYPE(void test35c, (void)); +_PROTOTYPE(void makelongnames, (void)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); +_PROTOTYPE(void getids, (uid_t * uid, gid_t * gid)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 35 "); + fflush(stdout); + System("rm -rf DIR_35; mkdir DIR_35"); + Chdir("DIR_35"); + makelongnames(); + superuser = (geteuid() == 0); + +#ifdef _POSIX_CHOWN_RESTRICTED +# if _POSIX_CHOWN_RESTRICTED - 0 != -1 + I_can_chown = superuser; +# else + I_can_chown = 1; +# endif +#else +# include "error, this case requires dynamic checks and is not handled" +#endif + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test35a(); + if (m & 0002) test35b(); + if (m & 0004) test35c(); + } + quit(); +} + +void test35a() +{ /* Test normal operation. */ + struct stat st; + struct utimbuf ub; + time_t time1, time2; + int cnt; + + subtest = 1; + + /* Creat scratch file. */ + Creat("foo"); + + /* Set file times back two seconds. */ + Stat("foo", &st); + ub.actime = st.st_atime - 2; + ub.modtime = st.st_mtime - 2; + Time(&time1); + utime("foo", &ub); + Time(&time2); + Stat("foo", &st); + if (ub.actime != st.st_atime) e(1); + if (ub.modtime != st.st_mtime) e(2); + + /* The status changed time sould be changed. */ +#ifndef V1_FILESYSTEM + if (st.st_ctime < time1) e(3); +#endif + if (st.st_ctime > time2) e(4); + + /* Add twenty seconds. */ + Stat("foo", &st); + ub.actime = st.st_atime + 20; + ub.modtime = st.st_mtime + 20; + Time(&time1); + utime("foo", &ub); + Time(&time2); + Stat("foo", &st); + if (ub.actime != st.st_atime) e(5); + if (ub.modtime != st.st_mtime) e(6); + if (st.st_ctime < time1) e(7); +#ifndef V1_FILESYSTEM + if (st.st_ctime > time2) e(8); +#endif + + /* Try 100 times to do utime in less than one second. */ + cnt = 0; + do { + Time(&time1); + utime("foo", (struct utimbuf *) NULL); + Time(&time2); + } while (time1 != time2 && cnt++ < 100); + if (time1 == time2) { + Stat("foo", &st); + Time(&time2); + if (st.st_atime != time1) e(9); + if (st.st_mtime != time1) e(10); + } else { + Stat("foo", &st); + if (st.st_atime > time2) e(11); + if (st.st_mtime > time2) e(12); + Time(&time2); + if (st.st_atime < time1) e(13); + if (st.st_mtime < time1) e(14); + } + if (st.st_ctime < time1) e(15); + if (st.st_ctime > time2) e(16); + + System("rm -rf ../DIR_35/*"); +} + +void test35b() +{ + subtest = 2; + + /* MaxPath and MaxName checkup. */ + Creat(MaxName); + MaxPath[strlen(MaxPath) - 2] = '/'; + MaxPath[strlen(MaxPath) - 1] = 'a'; /* make ././.../a */ + Creat(MaxPath); + if (utime(MaxName, NULL) != 0) e(1); + if (utime(MaxPath, NULL) != 0) e(2); + + /* The owner doesn't need write permisson to set times. */ + Creat("foo"); + if (chmod("foo", 0) != 0) e(3); + if (utime("foo", NULL) != 0) e(4); + if (chmod("foo", 0777) != 0) e(5); + if (utime("foo", NULL) != 0) e(6); + + + + System("rm -rf ../DIR_35/*"); +} + +void test35c() +{ + gid_t gid, gid2; + uid_t uid, uid2; + struct utimbuf ub; + int stat_loc; + + subtest = 3; + + /* Access problems. */ + Mkdir("bar"); + Creat("bar/tryme"); + if (superuser) { + Chmod("bar", 0000); /* No search permisson at all. */ + if (utime("bar/tryme", NULL) != 0) e(1); + } + if (!superuser) { + Chmod("bar", 0677); /* No search permisson. */ + if (utime("bar/tryme", NULL) != -1) e(2); + if (errno != EACCES) e(3); + } + Chmod("bar", 0777); + + if (I_can_chown) { + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + + /* Get two differend non root uids. */ + if (superuser) { + getids(&uid, &gid); + if (uid == 0) getids(&uid, &gid); + if (uid == 0) e(4); + } + if (!superuser) { + uid = geteuid(); + gid = getegid(); + } + getids(&uid2, &gid); + if (uid == uid2) getids(&uid2, &gid2); + if (uid == uid2) e(5); + + /* Creat a number of files for root, user and user2. */ + Creat("rootfile"); /* Owned by root. */ + Chmod("rootfile", 0600); + Chown("rootfile", 0, 0); + Creat("user2file"); /* Owned by user 2, writeable. */ + Chmod("user2file", 0020); + Chown("user2file", uid2, gid); + Creat("user2private"); /* Owned by user 2, privately. */ + Chmod("user2private", 0600); + Chown("user2private", uid2, gid); + + if (superuser) { + setgid(gid); + setuid(uid); + } + + /* We now are user ``uid'' from group ``gid''. */ + ub.actime = (time_t) 12345L; + ub.modtime = (time_t) 12345L; + + if (utime("rootfile", NULL) != -1) e(6); + if (errno != EACCES) e(7); + if (utime("rootfile", &ub) != -1) e(8); + if (errno != EPERM) e(9); + + if (utime("user2file", NULL) != 0) e(10); + if (utime("user2file", &ub) != -1) e(11); + if (errno != EPERM) e(12); + + if (utime("user2private", NULL) != -1) e(13); + if (errno != EACCES) e(14); + if (utime("user2private", &ub) != -1) e(15); + if (errno != EPERM) e(16); + + exit(errct ? 1 : 0); + default: + wait(&stat_loc); + if (stat_loc != 0) e(17); /* Alarm? */ + } + } + + /* Test names that are too long. */ +#ifdef _POSIX_NO_TRUNC +# if _POSIX_NO_TRUNC - 0 != -1 + /* Not exist might also be a propper response? */ + if (utime(NameTooLong, NULL) != -1) e(18); + if (errno != ENAMETOOLONG) e(19); +# else + Creat(NameTooLong); + if (utime(NameTooLong, NULL) != 0) e(20); +# endif +#else +# include "error, this case requires dynamic checks and is not handled" +#endif + + /* Make PathTooLong contain ././.../a */ + PathTooLong[strlen(PathTooLong) - 2] = '/'; + PathTooLong[strlen(PathTooLong) - 1] = 'a'; + Creat("a"); + if (utime(PathTooLong, NULL) != -1) e(21); + if (errno != ENAMETOOLONG) e(22); + + /* Non existing file name. */ + if (utime("nonexist", NULL) != -1) e(23); + if (errno != ENOENT) e(24); + + /* Empty file name. */ + if (utime("", NULL) != -1) e(25); + if (errno != ENOENT) e(26); + + System("rm -rf ../DIR_35/*"); +} + + + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(NameTooLong, MaxName); /* copy them Max to TooLong */ + strcpy(PathTooLong, MaxPath); + + NameTooLong[NAME_MAX] = 'a'; + NameTooLong[NAME_MAX + 1] = '\0'; /* extend NameTooLong by one too many*/ + PathTooLong[PATH_MAX - 1] = '/'; + PathTooLong[PATH_MAX] = '\0'; /* inc PathTooLong by one */ +} + + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR* > /dev/null 2>/dev/null"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_35"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} + + +/* Getids returns a valid uid and gid. Is used PASSWD FILE. +** It assumes the following format for a passwd file line: +** <user_name>:<passwd>:<uid>:<gid>:<other_stuff> +** If no uids and gids can be found, it will only return 0 ids. +*/ +void getids(r_uid, r_gid) +uid_t *r_uid; +gid_t *r_gid; +{ + char line[N]; + char *p; + uid_t uid; + gid_t gid; + FILE *fp; + int i; + + static uid_t a_uid[N]; /* Array for uids. */ + static gid_t a_gid[N]; /* Array for gids. */ + static int nuid = 0, ngid = 0;/* The number of user & group ids. */ + static int cuid = 0, cgid = 0;/* The current id index. */ + + /* If we don't have any uids go read some from the passwd file. */ + if (nuid == 0) { + a_uid[nuid++] = 0; /* Root uid and gid. */ + a_gid[ngid++] = 0; + if ((fp = fopen(PASSWD_FILE, "r")) == NULL) { + printf("Can't open "); + perror(PASSWD_FILE); + } + while (fp != NULL && fgets(line, sizeof(line), fp) != NULL) { + p = strchr(line, ':'); + if (p != NULL) p = strchr(p + 1, ':'); + if (p != NULL) { + p++; + uid = 0; + while (isdigit(*p)) { + uid *= 10; + uid += (uid_t) (*p - '0'); + p++; + } + if (*p != ':') continue; + p++; + gid = 0; + while (isdigit(*p)) { + gid *= 10; + gid += (gid_t) (*p - '0'); + p++; + } + if (*p != ':') continue; + if (nuid < N) { + for (i = 0; i < nuid; i++) + if (a_uid[i] == uid) break; + if (i == nuid) a_uid[nuid++] = uid; + } + if (ngid < N) { + for (i = 0; i < ngid; i++) + if (a_gid[i] == gid) break; + if (i == ngid) a_gid[ngid++] = gid; + } + if (nuid >= N && ngid >= N) break; + } + } + if (fp != NULL) fclose(fp); + } + + /* We now have uids and gids in a_uid and a_gid. */ + if (cuid >= nuid) cuid = 0; + if (cgid >= ngid) cgid = 0; + *r_uid = a_uid[cuid++]; + *r_gid = a_gid[cgid++]; +} diff --git a/test/test36.c b/test/test36.c new file mode 100644 index 000000000..5f92194ff --- /dev/null +++ b/test/test36.c @@ -0,0 +1,251 @@ +/* test36: pathconf() fpathconf() Author: Jan-mark Wams */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define ITERATIONS 10 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) + +int errct = 0; +int subtest = 1; +int superuser; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; /* Same for path, both too long */ + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test36a, (void)); +_PROTOTYPE(void test36b, (void)); +_PROTOTYPE(void test36c, (void)); +_PROTOTYPE(void test36d, (void)); +_PROTOTYPE(void makelongnames, (void)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); +_PROTOTYPE(int not_provided_option, (int _option)); +_PROTOTYPE(int provided_option, (int _option, int _minimum_value)); +_PROTOTYPE(int variating_option, (int _option, int _minimum_value)); + + +char *testdirs[] = { + "/", + "/etc", + "/tmp", + "/usr", + "/usr/bin", + ".", + NULL +}; + +char *testfiles[] = { + "/", + "/etc", + "/etc/passwd", + "/tmp", + "/dev/tty", + "/usr", + "/usr/bin", + ".", + NULL +}; + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 36 "); + fflush(stdout); + System("rm -rf DIR_36; mkdir DIR_36"); + Chdir("DIR_36"); + makelongnames(); + superuser = (geteuid() == 0); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test36a(); + if (m & 0002) test36b(); + if (m & 0004) test36c(); + if (m & 0010) test36d(); + } + quit(); +} + +void test36a() +{ /* Test normal operation. */ + subtest = 1; + System("rm -rf ../DIR_36/*"); + +#ifdef _POSIX_CHOWN_RESTRICTED +# if _POSIX_CHOWN_RESTRICTED - 0 == -1 + if (not_provided_option(_PC_CHOWN_RESTRICTED) != 0) e(1); +# else + if (provided_option(_PC_CHOWN_RESTRICTED, 0) != 0) e(2); +# endif +#else + if (variating_option(_PC_CHOWN_RESTRICTED, 0) != 0) e(3); +#endif + +#ifdef _POSIX_NO_TRUNC +# if _POSIX_NO_TRUNC - 0 == -1 + if (not_provided_option(_PC_NO_TRUNC) != 0) e(4); +# else + if (provided_option(_PC_NO_TRUNC, 0) != 0) e(5); +# endif +#else + if (variating_option(_PC_NO_TRUNC, 0) != 0) e(6); +#endif + +#ifdef _POSIX_VDISABLE +# if _POSIX_VDISABLE - 0 == -1 + if (not_provided_option(_PC_VDISABLE) != 0) e(7); +# else + if (provided_option(_PC_VDISABLE, 0) != 0) e(8); +# endif +#else + if (variating_option(_PC_VDISABLE, 0) != 0) e(9); +#endif + +} + +void test36b() +{ + subtest = 2; + System("rm -rf ../DIR_36/*"); +} + +void test36c() +{ + subtest = 3; + System("rm -rf ../DIR_36/*"); +} + +void test36d() +{ + subtest = 4; + System("rm -rf ../DIR_36/*"); +} + + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(ToLongName, MaxName); /* copy them Max to ToLong */ + strcpy(ToLongPath, MaxPath); + + ToLongName[NAME_MAX] = 'a'; + ToLongName[NAME_MAX + 1] = '\0'; /* extend ToLongName by one too many */ + ToLongPath[PATH_MAX - 1] = '/'; + ToLongPath[PATH_MAX] = '\0'; /* inc ToLongPath by one */ +} + + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_36"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} + +int not_provided_option(option) +int option; +{ + char **p; + + for (p = testfiles; *p != (char *) NULL; p++) { + if (pathconf(*p, option) != -1) return printf("*p == %s\n", *p), 1; + } + return 0; +} + + +int provided_option(option, minimum) +int option, minimum; +{ + char **p; + + /* These three options are only defined on directorys. */ + if (option == _PC_NO_TRUNC + || option == _PC_NAME_MAX + || option == _PC_PATH_MAX) + p = testdirs; + else + p = testfiles; + + for (; *p != NULL; p++) { + if (pathconf(*p, option) < minimum) + return printf("*p == %s\n", *p), 1; + } + return 0; +} + + +int variating_option(option, minimum) +int option, minimum; +{ + char **p; + + /* These three options are only defined on directorys. */ + if (option == _PC_NO_TRUNC + || option == _PC_NAME_MAX + || option == _PC_PATH_MAX) + p = testdirs; + else + p = testfiles; + + for (; *p != NULL; p++) { + if (pathconf(*p, option) < minimum) + return printf("*p == %s\n", *p), 1; + } + return 0; +} diff --git a/test/test37.c b/test/test37.c new file mode 100644 index 000000000..b544eb278 --- /dev/null +++ b/test/test37.c @@ -0,0 +1,268 @@ +/* test37: pipe() Author: Jan-Mark Wams (jms@cs.vu.nl) */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define ITERATIONS 60 + +#define Fstat(a,b) if (fstat(a,b) != 0) printf("Can't fstat %d\n", a) +#define Time(t) if (time(t) == (time_t)-1) printf("Time error\n") + +int errct = 0; +int subtest = 1; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; /* Same for path, both too long */ + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test37a, (void)); +_PROTOTYPE(void test37b, (void)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 37 "); + fflush(stdout); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test37a(); + if (m & 0002) test37b(); + } + quit(); +} + +void test37a() +{ /* Test fcntl flags. */ + int tube[2], t1[2], t2[2], t3[2]; + time_t time1, time2; + char buf[128]; + struct stat st1, st2; + int stat_loc, flags; + + subtest = 1; + + /* Check if lowest fds are returned. */ + if (pipe(tube) != 0) e(1); + if (tube[0] != 3 && tube[1] != 3) e(2); + if (tube[1] != 4 && tube[0] != 4) e(3); + if (tube[1] == tube[0]) e(4); + if (pipe(t1) != 0) e(5); + if (t1[0] != 5 && t1[1] != 5) e(6); + if (t1[1] != 6 && t1[0] != 6) e(7); + if (t1[1] == t1[0]) e(8); + if (close(t1[0]) != 0) e(9); + if (close(tube[0]) != 0) e(10); + if (pipe(t2) != 0) e(11); + if (t2[0] != tube[0] && t2[1] != tube[0]) e(12); + if (t2[1] != t1[0] && t2[0] != t1[0]) e(13); + if (t2[1] == t2[0]) e(14); + if (pipe(t3) != 0) e(15); + if (t3[0] != 7 && t3[1] != 7) e(16); + if (t3[1] != 8 && t3[0] != 8) e(17); + if (t3[1] == t3[0]) e(18); + if (close(tube[1]) != 0) e(19); + if (close(t1[1]) != 0) e(20); + if (close(t2[0]) != 0) e(21); + if (close(t2[1]) != 0) e(22); + if (close(t3[0]) != 0) e(23); + if (close(t3[1]) != 0) e(24); + + /* All time fields should be marked for update. */ + Time(&time1); + if (pipe(tube) != 0) e(25); + Fstat(tube[0], &st1); + Fstat(tube[1], &st2); + Time(&time2); + if (st1.st_atime < time1) e(26); + if (st1.st_ctime < time1) e(27); + if (st1.st_mtime < time1) e(28); + if (st1.st_atime > time2) e(29); + if (st1.st_ctime > time2) e(30); + if (st1.st_mtime > time2) e(31); + if (st2.st_atime < time1) e(32); + if (st2.st_ctime < time1) e(33); + if (st2.st_mtime < time1) e(34); + if (st2.st_atime > time2) e(35); + if (st2.st_ctime > time2) e(36); + if (st2.st_mtime > time2) e(37); + + /* Check the file characteristics. */ + if ((flags = fcntl(tube[0], F_GETFD)) != 0) e(38); + if ((flags & FD_CLOEXEC) != 0) e(39); + if ((flags = fcntl(tube[0], F_GETFL)) != 0) e(40); + if ((flags & O_RDONLY) != O_RDONLY) e(41); + if ((flags & O_NONBLOCK) != 0) e(42); + if ((flags & O_RDWR) != 0) e(43); + if ((flags & O_WRONLY) != 0) e(44); + + if ((flags = fcntl(tube[1], F_GETFD)) != 0) e(45); + if ((flags & FD_CLOEXEC) != 0) e(46); + if ((flags = fcntl(tube[1], F_GETFL)) == -1) e(47); + if ((flags & O_WRONLY) != O_WRONLY) e(48); + if ((flags & O_NONBLOCK) != 0) e(49); + if ((flags & O_RDWR) != 0) e(50); + if ((flags & O_RDONLY) != 0) e(51); + + /* Check if we can read and write. */ + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + if (close(tube[0]) != 0) e(52); + if (write(tube[1], "Hello", 6) != 6) e(53); + if (close(tube[1]) != 0) e(54); + exit(0); + default: + if (read(tube[0], buf, sizeof(buf)) != 6) e(55); + if (strncmp(buf, "Hello", 6) != 0) e(56); + wait(&stat_loc); + if (stat_loc != 0) e(57); /* Alarm? */ + } + if (close(tube[0]) != 0) e(58); + if (close(tube[1]) != 0) e(59); +} + + +void test37b() +{ + int tube[2], child2parent[2], parent2child[2]; + int i, nchild = 0, nopen = 3, stat_loc; + int fd; + int forkfailed = 0; + char c; + + subtest = 2; + + /* Take all the pipes we can get. */ + while (nopen < OPEN_MAX - 2) { + if (pipe(tube) != 0) { + /* We have not reached OPEN_MAX yet, so we have ENFILE. */ + if (errno != ENFILE) e(1); + sleep(2); /* Wait for others to (maybe) closefiles. */ + break; + } + nopen += 2; + } + + if (nopen < OPEN_MAX - 2) { + if (pipe(tube) != -1) e(2); + switch (errno) { + case EMFILE: /* Errno value is ok. */ + break; + case ENFILE: /* No process can open files any more. */ + switch (fork()) { + case -1: + printf("Can't fork\n"); + break; + case 0: + alarm(20); + if (open("/", O_RDONLY) != -1) e(3); + if (errno != ENFILE) e(4); + exit(0); + default: + wait(&stat_loc); + if (stat_loc != 0) e(5); /* Alarm? */ + } + break; + default: /* Wrong value for errno. */ + e(6); + } + } + + /* Close all but stdin,out,err. */ + for (i = 3; i < OPEN_MAX; i++) (void) close(i); + + /* ENFILE test. Have children each grab OPEN_MAX fds. */ + if (pipe(child2parent) != 0) e(7); + if (pipe(parent2child) != 0) e(8); + while (!forkfailed && (fd = open("/", O_RDONLY)) != -1) { + close(fd); + switch (fork()) { + case -1: + forkfailed = 1; + break; + case 0: + alarm(60); + + /* Grab all the fds. */ + while (pipe(tube) != -1); + while (open("/", O_RDONLY) != -1); + + /* Signal parent OPEN_MAX fds gone. */ + if (write(child2parent[1], "*", 1) != 1) e(9); + + /* Wait for parent befor freeing all the fds. */ + if (read(parent2child[0], &c, 1) != 1) e(10); + exit(0); + default: + + /* Wait for child to grab OPEN_MAX fds. */ + if (read(child2parent[0], &c, 1) != 1) e(11); + nchild++; + break; + } + } + + if (!forkfailed) { + if (pipe(tube) != -1) e(12); + if (errno != ENFILE) e(13); + } + + /* Signal children to die and wait for it. */ + while (nchild-- > 0) { + if (write(parent2child[1], "*", 1) != 1) e(14); + wait(&stat_loc); + if (stat_loc != 0) e(15); /* Alarm? */ + } + + /* Close all but stdin,out,err. */ + for (i = 3; i < OPEN_MAX; i++) (void) close(i); +} + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test38.c b/test/test38.c new file mode 100644 index 000000000..9d917f436 --- /dev/null +++ b/test/test38.c @@ -0,0 +1,250 @@ +/* test38: dup() dup2() Author: Jan-Mark Wams (jms@cs.vu.nl) */ + +/* The definition of ``dup2()'' is realy a big mess! For: +** +** (1) if fildes2 is less than zero or greater than {OPEN_MAX} +** errno has to set to [EBADF]. But if fildes2 equals {OPEN_MAX} +** errno has to be set to [EINVAL]. And ``fcntl(F_DUPFD...)'' always +** returns [EINVAL] if fildes2 is out of range! +** +** (2) if the number of file descriptors would exceed {OPEN_MAX}, or no +** file descriptors above fildes2 are available, errno has to be set +** to [EMFILE]. But this can never occur! +*/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define ITERATIONS 10 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) + +#define IS_CLOEXEC(fd) ((fcntl(fd, F_GETFD) & FD_CLOEXEC) == FD_CLOEXEC) +#define SET_CLOEXEC(fd) fcntl(fd, F_SETFD, FD_CLOEXEC) + +int errct = 0; +int subtest = 1; +int superuser; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; /* Same for path, both too long */ + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test38a, (void)); +_PROTOTYPE(void test38b, (void)); +_PROTOTYPE(void test38c, (void)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 38 "); + fflush(stdout); + System("rm -rf DIR_38; mkdir DIR_38"); + Chdir("DIR_38"); + superuser = (geteuid() == 0); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test38a(); + if (m & 0002) test38b(); + if (m & 0004) test38c(); + } + quit(); +} + +void test38a() +{ + int fd1, fd2, fd3, fd4, fd5; + struct flock flock; + + subtest = 1; + + /* Basic checking. */ + if ((fd1 = dup(0)) != 3) e(1); + if ((fd2 = dup(0)) != 4) e(2); + if ((fd3 = dup(0)) != 5) e(3); + if ((fd4 = dup(0)) != 6) e(4); + if ((fd5 = dup(0)) != 7) e(5); + if (close(fd2) != 0) e(6); + if (close(fd4) != 0) e(7); + if ((fd2 = dup(0)) != 4) e(8); + if ((fd4 = dup(0)) != 6) e(9); + if (close(fd1) != 0) e(10); + if (close(fd3) != 0) e(11); + if (close(fd5) != 0) e(12); + if ((fd1 = dup(0)) != 3) e(13); + if ((fd3 = dup(0)) != 5) e(14); + if ((fd5 = dup(0)) != 7) e(15); + if (close(fd1) != 0) e(16); + if (close(fd2) != 0) e(17); + if (close(fd3) != 0) e(18); + if (close(fd4) != 0) e(19); + if (close(fd5) != 0) e(20); + + /* FD_CLOEXEC should be cleared. */ + if ((fd1 = dup(0)) != 3) e(21); + if (SET_CLOEXEC(fd1) == -1) e(22); + if (!IS_CLOEXEC(fd1)) e(23); + if ((fd2 = dup(fd1)) != 4) e(24); + if ((fd3 = dup(fd2)) != 5) e(25); + if (IS_CLOEXEC(fd2)) e(26); + if (IS_CLOEXEC(fd3)) e(27); + if (SET_CLOEXEC(fd2) == -1) e(28); + if (!IS_CLOEXEC(fd2)) e(29); + if (IS_CLOEXEC(fd3)) e(30); + if (close(fd1) != 0) e(31); + if (close(fd2) != 0) e(32); + if (close(fd3) != 0) e(33); + + /* Locks should be shared, so we can lock again. */ + System("echo 'Hallo' > file"); + if ((fd1 = open("file", O_RDWR)) != 3) e(34); + flock.l_whence = SEEK_SET; + flock.l_start = 0; + flock.l_len = 10; + flock.l_type = F_WRLCK; + if (fcntl(fd1, F_SETLK, &flock) == -1) e(35); + if (fcntl(fd1, F_SETLK, &flock) == -1) e(36); + if ((fd2 = dup(fd1)) != 4) e(37); + if (fcntl(fd1, F_SETLK, &flock) == -1) e(38); + if (fcntl(fd1, F_GETLK, &flock) == -1) e(39); +#if 0 /* XXX - see test7.c */ + if (flock.l_type != F_WRLCK) e(40); + if (flock.l_pid != getpid()) e(41); +#endif /* 0 */ + flock.l_type = F_WRLCK; + if (fcntl(fd2, F_GETLK, &flock) == -1) e(42); +#if 0 /* XXX - see test7.c */ + if (flock.l_type != F_WRLCK) e(43); + if (flock.l_pid != getpid()) e(44); +#endif /* 0 */ + if (close(fd1) != 0) e(45); + if (close(fd2) != 0) e(46); + + System("rm -rf ../DIR_38/*"); +} + +void test38b() +{ + int fd; + char buf[32]; + + subtest = 2; + + /* Test file called ``file''. */ + System("echo 'Hallo!' > file"); + + /* Check dup2() call with the same fds. Should have no effect. */ + if ((fd = open("file", O_RDONLY)) != 3) e(1); + if (read(fd, buf, 2) != 2) e(2); + if (strncmp(buf, "Ha", 2) != 0) e(3); + if (dup2(fd, fd) != fd) e(4); + if (read(fd, buf, 2) != 2) e(5); + if (strncmp(buf, "ll", 2) != 0) e(6); + if (dup2(fd, fd) != fd) e(7); + if (read(fd, buf, 2) != 2) e(8); + if (strncmp(buf, "o!", 2) != 0) e(9); + if (close(fd) != 0) e(10); + + /* If dup2() call fails, the fildes2 argument has to stay open. */ + if ((fd = open("file", O_RDONLY)) != 3) e(11); + if (read(fd, buf, 2) != 2) e(12); + if (strncmp(buf, "Ha", 2) != 0) e(13); + if (dup2(OPEN_MAX + 3, fd) != -1) e(14); + if (errno != EBADF) e(15); + if (read(fd, buf, 2) != 2) e(16); + if (strncmp(buf, "ll", 2) != 0) e(17); + if (dup2(-4, fd) != -1) e(18); + if (errno != EBADF) e(19); + if (read(fd, buf, 2) != 2) e(20); + if (strncmp(buf, "o!", 2) != 0) e(21); + if (close(fd) != 0) e(22); + + + System("rm -rf ../DIR_38/*"); +} + +void test38c() +{ + int i; + + subtest = 3; + + /* Check bad arguments to dup() and dup2(). */ + for (i = -OPEN_MAX; i < OPEN_MAX * 2; i++) { + + /* ``i'' is a valid and open fd. */ + if (i >= 0 && i < 3) continue; + + /* If ``i'' is a valid fd it is not open. */ + if (dup(i) != -1) e(1); + if (errno != EBADF) e(2); + + /* ``i'' Is OPEN_MAX. */ + if (i == OPEN_MAX) { + if (dup2(0, i) != -1) e(3); + if (errno != EINVAL) e(4); + } + + /* ``i'' Is out of range. */ + if (i < 0 || i > OPEN_MAX) { + if (dup2(0, i) != -1) e(5); + if (errno != EBADF) e(6); + } + } + + System("rm -rf ../DIR_38/*"); +} + + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_38"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test39.c b/test/test39.c new file mode 100644 index 000000000..2235a966e --- /dev/null +++ b/test/test39.c @@ -0,0 +1,403 @@ +/* test39: fcntl() Author: Jan-Mark Wams (jms@cs.vu.nl) */ + +/* Some things have to be checked for ``exec()'' call's. Therefor +** there is a check routine called ``do_check()'' that will be +** called if the first argument (``argv[0]'') equals ``DO CHECK.'' +** Note that there is no way the shell (``/bin/sh'') will set +** ``argv[0]'' to this funny value. (Unless we rename ``test39'' +** to ``DO CHECK'' ;-) +*/ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define ITERATIONS 10 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) + +int errct = 0; +int subtest = 1; +int superuser; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; /* Same for path, both too long */ + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test39a, (void)); +_PROTOTYPE(void test39b, (void)); +_PROTOTYPE(void test39c, (void)); +_PROTOTYPE(void test39d, (void)); +_PROTOTYPE(int do_check, (void)); +_PROTOTYPE(void makelongnames, (void)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); + +char executable[1024]; + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + + /* If we have to check things, call do_check(). */ + if (strcmp(argv[0], "DO CHECK") == 0) exit(do_check()); + + /* Get the path of the executable. */ + strcpy(executable, "../"); + strcat(executable, argv[0]); + + printf("Test 39 "); + fflush(stdout); + System("rm -rf DIR_39; mkdir DIR_39"); + Chdir("DIR_39"); + makelongnames(); + superuser = (geteuid() == 0); + + for (i = 0; i < ITERATIONS; i++) { + test39a(); + test39b(); + test39c(); + test39d(); + } + quit(); +} + +void test39a() +{ /* Test normal operation. */ + subtest = 1; + System("rm -rf ../DIR_39/*"); +} + +void test39b() +{ + subtest = 2; + System("rm -rf ../DIR_39/*"); +} + +void test39c() +{ + subtest = 3; + System("rm -rf ../DIR_39/*"); +} + +/* Open fds 3, 4, 5 and 6. Set FD_CLOEXEC on 5 and 6. Exclusively lock the +** first 10 bytes of fd no. 3. Shared lock fd no. 7. Lock fd no. 8 after +** the fork. Do a ``exec()'' call with a funny argv[0] and check the return +** value. +*/ +void test39d() +{ /* Test locks with ``fork()'' and ``exec().'' */ + int fd3, fd4, fd5, fd6, fd7, fd8; + int stat_loc; + int do_check_retval; + char *argv[2]; + struct flock fl; + + subtest = 4; + + argv[0] = "DO CHECK"; + argv[1] = (char *) NULL; + + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 10; + + /* Make a dummy files and open them. */ + System("echo 'Great Balls Of Fire!' > file3"); + System("echo 'Great Balls Of Fire!' > file4"); + System("echo 'Great Balls Of Fire!' > file7"); + System("echo 'Great Balls Of Fire!' > file8"); + System("echo 'Great Balls Of Fire!' > file"); + if ((fd3 = open("file3", O_RDWR)) != 3) e(1); + if ((fd4 = open("file4", O_RDWR)) != 4) e(2); + if ((fd5 = open("file", O_RDWR)) != 5) e(3); + if ((fd6 = open("file", O_RDWR)) != 6) e(4); + if ((fd7 = open("file7", O_RDWR)) != 7) e(5); + if ((fd8 = open("file8", O_RDWR)) != 8) e(6); + + /* Set FD_CLOEXEC flags on fd5 and fd6. */ + if (fcntl(fd5, F_SETFD, FD_CLOEXEC) == -1) e(7); + if (fcntl(fd6, F_SETFD, FD_CLOEXEC) == -1) e(8); + + /* Lock the first ten bytes from fd3 (for writing). */ + fl.l_type = F_WRLCK; + if (fcntl(fd3, F_SETLK, &fl) == -1) e(9); + + /* Lock (for reading) fd7. */ + fl.l_type = F_RDLCK; + if (fcntl(fd7, F_SETLK, &fl) == -1) e(10); + + + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + + /* Lock fd8. */ + fl.l_type = F_WRLCK; + if (fcntl(fd8, F_SETLK, &fl) == -1) e(11); + + /* Check the lock on fd3 and fd7. */ + fl.l_type = F_WRLCK; + if (fcntl(fd3, F_GETLK, &fl) == -1) e(12); + if (fl.l_type != F_WRLCK) e(13); + if (fl.l_pid != getppid()) e(14); + fl.l_type = F_WRLCK; + if (fcntl(fd7, F_GETLK, &fl) == -1) e(15); + if (fl.l_type != F_RDLCK) e(16); + if (fl.l_pid != getppid()) e(17); + + /* Check FD_CLOEXEC flags. */ + if ((fcntl(fd3, F_GETFD) & FD_CLOEXEC) != 0) e(18); + if ((fcntl(fd4, F_GETFD) & FD_CLOEXEC) != 0) e(19); + if ((fcntl(fd5, F_GETFD) & FD_CLOEXEC) != FD_CLOEXEC) e(20); + if ((fcntl(fd6, F_GETFD) & FD_CLOEXEC) != FD_CLOEXEC) e(21); + if ((fcntl(fd7, F_GETFD) & FD_CLOEXEC) != 0) e(22); + if ((fcntl(fd8, F_GETFD) & FD_CLOEXEC) != 0) e(23); + + execlp(executable + 3, "DO CHECK", (char *) NULL); + execlp(executable, "DO CHECK", (char *) NULL); + printf("Can't exec %s or %s\n", executable + 3, executable); + exit(0); + + default: + wait(&stat_loc); + if (WIFSIGNALED(stat_loc)) e(24); /* Alarm? */ + if (WIFEXITED(stat_loc) == 0) { + errct=10000; + quit(); + } + } + + /* Check the return value of do_check(). */ + do_check_retval = WEXITSTATUS(stat_loc); + if ((do_check_retval & 0x11) == 0x11) e(25); + if ((do_check_retval & 0x12) == 0x12) e(26); + if ((do_check_retval & 0x14) == 0x14) e(27); + if ((do_check_retval & 0x18) == 0x18) e(28); + if ((do_check_retval & 0x21) == 0x21) e(29); + if ((do_check_retval & 0x22) == 0x22) e(30); + if ((do_check_retval & 0x24) == 0x24) e(31); + if ((do_check_retval & 0x28) == 0x28) e(32); + if ((do_check_retval & 0x41) == 0x41) e(33); + if ((do_check_retval & 0x42) == 0x42) e(34); + if ((do_check_retval & 0x44) == 0x44) e(35); + if ((do_check_retval & 0x48) == 0x48) e(36); + if ((do_check_retval & 0x81) == 0x81) e(37); + if ((do_check_retval & 0x82) == 0x82) e(38); + if ((do_check_retval & 0x84) == 0x84) e(39); + if ((do_check_retval & 0x88) == 0x88) e(40); + + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + + /* Lock fd8. */ + fl.l_type = F_WRLCK; + if (fcntl(fd8, F_SETLK, &fl) == -1) e(41); + + execvp(executable + 3, argv); + execvp(executable, argv); + printf("Can't exec %s or %s\n", executable + 3, executable); + exit(0); + + default: + wait(&stat_loc); + if (WIFSIGNALED(stat_loc)) e(48); /* Alarm? */ + } + + /* Check the return value of do_check(). */ + do_check_retval = WEXITSTATUS(stat_loc); + if ((do_check_retval & 0x11) == 0x11) e(49); + if ((do_check_retval & 0x12) == 0x12) e(50); + if ((do_check_retval & 0x14) == 0x14) e(51); + if ((do_check_retval & 0x18) == 0x18) e(52); + if ((do_check_retval & 0x21) == 0x21) e(53); + if ((do_check_retval & 0x22) == 0x22) e(54); + if ((do_check_retval & 0x24) == 0x24) e(55); + if ((do_check_retval & 0x28) == 0x28) e(56); + if ((do_check_retval & 0x41) == 0x41) e(57); + if ((do_check_retval & 0x42) == 0x42) e(58); + if ((do_check_retval & 0x44) == 0x44) e(59); + if ((do_check_retval & 0x48) == 0x48) e(60); + if ((do_check_retval & 0x81) == 0x81) e(61); + if ((do_check_retval & 0x82) == 0x82) e(62); + if ((do_check_retval & 0x84) == 0x84) e(63); + if ((do_check_retval & 0x88) == 0x88) e(64); + + fl.l_type = F_UNLCK; + if (fcntl(fd3, F_SETLK, &fl) == -1) e(65); + if (fcntl(fd7, F_SETLK, &fl) == -1) e(66); + + if (close(fd3) != 0) e(67); + if (close(fd4) != 0) e(68); + if (close(fd5) != 0) e(69); + if (close(fd6) != 0) e(70); + if (close(fd7) != 0) e(71); + if (close(fd8) != 0) e(72); + + System("rm -f ../DIR_39/*\n"); +} + + +/* This routine checks that fds 0 through 4, 7 and 8 are open and the rest +** is closed. It also checks if we can lock the first 10 bytes on fd no. 3 +** and 4. It should not be possible to lock fd no. 3, but it should be +** possible to lock fd no. 4. See ``test39d()'' for usage of this routine. +*/ +int do_check() +{ + int i; + int retval = 0; + struct flock fl; + + fl.l_whence = SEEK_SET; + fl.l_start = 0; + fl.l_len = 10; + + /* All std.. are open. */ + if (fcntl(0, F_GETFD) == -1) retval |= 0x11; + if (fcntl(1, F_GETFD) == -1) retval |= 0x11; + if (fcntl(2, F_GETFD) == -1) retval |= 0x11; + + /* Fd no. 3, 4, 7 and 8 are open. */ + if (fcntl(3, F_GETFD) == -1) retval |= 0x12; + if (fcntl(4, F_GETFD) == -1) retval |= 0x12; + if (fcntl(7, F_GETFD) == -1) retval |= 0x12; + + /* Fd no. 5, 6 and 9 trough OPEN_MAX are closed. */ + if (fcntl(5, F_GETFD) != -1) retval |= 0x14; + if (fcntl(6, F_GETFD) != -1) retval |= 0x14; + for (i = 9; i < OPEN_MAX; i++) + if (fcntl(i, F_GETFD) != -1) retval |= 0x18; + +#if 0 + /* Fd no. 3 is WRLCKed. */ + fl.l_type = F_WRLCK; + if (fcntl(3, F_SETLK, &fl) != -1) retval |= 0x21; + if (errno != EACCES && errno != EAGAIN) retval |= 0x22; + fl.l_type = F_RDLCK; + if (fcntl(3, F_SETLK, &fl) != -1) retval |= 0x24; + if (errno != EACCES && errno != EAGAIN) retval |= 0x22; + fl.l_type = F_RDLCK; + if (fcntl(3, F_GETLK, &fl) == -1) retval |= 0x28; + if (fl.l_type != F_WRLCK) retval |= 0x28; + if (fl.l_pid != getpid()) retval |= 0x28; + fl.l_type = F_WRLCK; + if (fcntl(3, F_GETLK, &fl) == -1) retval |= 0x28; + if (fl.l_type != F_WRLCK) retval |= 0x28; + if (fl.l_pid != getpid()) retval |= 0x28; +#endif + + /* Fd no. 4 is not locked. */ + fl.l_type = F_WRLCK; + if (fcntl(4, F_SETLK, &fl) == -1) retval |= 0x41; + if (fcntl(4, F_GETLK, &fl) == -1) retval |= 0x42; +#if 0 /* XXX - see test7.c */ + if (fl.l_type != F_WRLCK) retval |= 0x42; + if (fl.l_pid != getpid()) retval |= 0x42; +#endif /* 0 */ + + /* Fd no. 8 is locked after the fork, it is ours. */ + fl.l_type = F_WRLCK; + if (fcntl(8, F_SETLK, &fl) == -1) retval |= 0x44; + if (fcntl(8, F_GETLK, &fl) == -1) retval |= 0x48; +#if 0 /* XXX - see test7.c */ + if (fl.l_type != F_WRLCK) retval |= 0x48; + if (fl.l_pid != getpid()) retval |= 0x48; +#endif /* 0 */ + +#if 0 + /* Fd no. 7 is RDLCKed. */ + fl.l_type = F_WRLCK; + if (fcntl(7, F_SETLK, &fl) != -1) retval |= 0x81; + if (errno != EACCES && errno != EAGAIN) retval |= 0x82; + fl.l_type = F_RDLCK; + if (fcntl(7, F_SETLK, &fl) == -1) retval |= 0x84; + fl.l_type = F_RDLCK; + if (fcntl(7, F_GETLK, &fl) == -1) retval |= 0x88; + if (fl.l_type != F_UNLCK) retval |= 0x88; + fl.l_type = F_WRLCK; + if (fcntl(7, F_GETLK, &fl) == -1) retval |= 0x88; + if (fl.l_type != F_RDLCK) retval |= 0x88; + if (fl.l_pid != getppid()) retval |= 0x88; +#endif + + return retval; +} + +void makelongnames() +{ + register int i; + + memset(MaxName, 'a', NAME_MAX); + MaxName[NAME_MAX] = '\0'; + for (i = 0; i < PATH_MAX - 1; i++) { /* idem path */ + MaxPath[i++] = '.'; + MaxPath[i] = '/'; + } + MaxPath[PATH_MAX - 1] = '\0'; + + strcpy(ToLongName, MaxName); /* copy them Max to ToLong */ + strcpy(ToLongPath, MaxPath); + + ToLongName[NAME_MAX] = 'a'; + ToLongName[NAME_MAX + 1] = '\0'; /* extend ToLongName by one too many */ + ToLongPath[PATH_MAX - 1] = '/'; + ToLongPath[PATH_MAX] = '\0'; /* inc ToLongPath by one */ +} + + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_39"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else if (errct < 10000) { + printf("%d errors\n", errct); + exit(1); + } else { + printf("errors\n"); + exit(2); + } +} diff --git a/test/test4.c b/test/test4.c new file mode 100644 index 000000000..bd72ed3fe --- /dev/null +++ b/test/test4.c @@ -0,0 +1,104 @@ +/* test 4 */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <fcntl.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> + +pid_t pid0, pid1, pid2, pid3; +int s, i, fd, nextb, errct = 0; +char *tempfile = "test4.temp"; +char buf[1024]; +extern int errno; + +_PROTOTYPE(int main, (void)); +_PROTOTYPE(void subr, (void)); +_PROTOTYPE(void nofork, (void)); +_PROTOTYPE(void quit, (void)); + +int main() +{ + int k; + + printf("Test 4 "); + fflush(stdout); /* have to flush for child's benefit */ + + system("rm -rf DIR_04; mkdir DIR_04"); + chdir("DIR_04"); + + creat(tempfile, 0777); + for (k = 0; k < 20; k++) { + subr(); + } + unlink(tempfile); + quit(); + return(-1); /* impossible */ +} + + +void subr() +{ + if ( (pid0 = fork()) != 0) { + /* Parent 0 */ + if (pid0 < 0) nofork(); + if ( (pid1 = fork()) != 0) { + /* Parent 1 */ + if (pid1 < 0) nofork(); + if ( (pid2 = fork()) != 0) { + /* Parent 2 */ + if (pid2 < 0) nofork(); + if ( (pid3 = fork()) != 0) { + /* Parent 3 */ + if (pid3 < 0) nofork(); + for (i = 0; i < 10000; i++); + kill(pid2, 9); + kill(pid1, 9); + kill(pid0, 9); + wait(&s); + wait(&s); + wait(&s); + wait(&s); + } else { + fd = open(tempfile, O_RDONLY); + lseek(fd, 20480L * nextb, 0); + for (i = 0; i < 10; i++) read(fd, buf, 1024); + nextb++; + close(fd); + exit(0); + } + } else { + while (1) getpid(); + } + } else { + while (1) getpid(); + } + } else { + while (1) getpid(); + } +} + +void nofork() +{ + int e = errno; + printf("Fork failed: %s (%d)\n",strerror(e),e); + exit(1); +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test40.c b/test/test40.c new file mode 100644 index 000000000..c8456708b --- /dev/null +++ b/test/test40.c @@ -0,0 +1,279 @@ +/* test40: lseek() Author: Jan-Mark Wams (jms@cs.vu.nl) */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <limits.h> +#include <errno.h> +#include <time.h> +#include <stdio.h> + +#define MAX_ERROR 4 +#define ITERATIONS 10 + +#define System(cmd) if (system(cmd) != 0) printf("``%s'' failed\n", cmd) +#define Chdir(dir) if (chdir(dir) != 0) printf("Can't goto %s\n", dir) +#define Stat(a,b) if (stat(a,b) != 0) printf("Can't stat %s\n", a) +#define Mkfifo(f) if (mkfifo(f,0777)!=0) printf("Can't make fifo %s\n", f) + +int errct = 0; +int subtest = 1; +char MaxName[NAME_MAX + 1]; /* Name of maximum length */ +char MaxPath[PATH_MAX]; /* Same for path */ +char ToLongName[NAME_MAX + 2]; /* Name of maximum +1 length */ +char ToLongPath[PATH_MAX + 1]; /* Same for path, both too long */ + +_PROTOTYPE(void main, (int argc, char *argv[])); +_PROTOTYPE(void test40a, (void)); +_PROTOTYPE(void test40b, (void)); +_PROTOTYPE(void test40c, (void)); +_PROTOTYPE(void e, (int number)); +_PROTOTYPE(void quit, (void)); + +void main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (argc == 2) m = atoi(argv[1]); + printf("Test 40 "); + fflush(stdout); + System("rm -rf DIR_40; mkdir DIR_40"); + Chdir("DIR_40"); + + for (i = 0; i < 10; i++) { + if (m & 0001) test40a(); + if (m & 0002) test40b(); + if (m & 0004) test40c(); + } + quit(); +} + +void test40a() +{ /* Test normal operation. */ + int fd; + char buf[20]; + int i, j; + struct stat st; + + subtest = 1; + System("rm -rf ../DIR_40/*"); + + + System("echo -n hihaho > hihaho"); + if ((fd = open("hihaho", O_RDONLY)) != 3) e(1); + if (lseek(fd, (off_t) 3, SEEK_SET) != (off_t) 3) e(2); + if (read(fd, buf, 1) != 1) e(3); + if (buf[0] != 'a') e(4); + if (lseek(fd, (off_t) - 1, SEEK_END) != 5) e(5); + if (read(fd, buf, 1) != 1) e(6); + if (buf[0] != 'o') e(7); + + /* Seek past end of file. */ + if (lseek(fd, (off_t) 1000, SEEK_END) != 1006) e(8); + if (read(fd, buf, 1) != 0) e(9); + + /* Lseek() should not extend the file. */ + if (fstat(fd, &st) != 0) e(10); + if (st.st_size != (off_t) 6) e(11); + if (close(fd) != 0) e(12); + + /* Probeer lseek met write. */ + if ((fd = open("hihaho", O_WRONLY)) != 3) e(13); + if (lseek(fd, (off_t) 3, SEEK_SET) != (off_t) 3) e(14); + if (write(fd, "e", 1) != 1) e(15); + if (lseek(fd, (off_t) 1000, SEEK_END) != 1006) e(16); + + /* Lseek() should not extend the file. */ + if (fstat(fd, &st) != 0) e(17); + if (st.st_size != (off_t) 6) e(18); + if (write(fd, "e", 1) != 1) e(19); + + /* Lseek() and a subsequent write should! */ + if (fstat(fd, &st) != 0) e(20); + if (st.st_size != (off_t) 1007) e(21); + + if (close(fd) != 0) e(22); + + /* Check the file, it should start with hiheho. */ + if ((fd = open("hihaho", O_RDONLY)) != 3) e(23); + if (read(fd, buf, 6) != 6) e(24); + if (strncmp(buf, "hiheho", 6) != 0) e(25); + + /* The should be zero bytes and a trailing ``e''. */ + if (sizeof(buf) < 10) e(26); + for (i = 1; i <= 20; i++) { + if (read(fd, buf, 10) != 10) e(27); + for (j = 0; j < 10; j++) + if (buf[j] != '\0') break; + if (j != 10) e(28); + if (lseek(fd, (off_t) 15, SEEK_CUR) != (off_t) i * 25 + 6) e(29); + } + + if (lseek(fd, (off_t) 1006, SEEK_SET) != (off_t) 1006) e(30); + if (read(fd, buf, sizeof(buf)) != 1) e(31); + if (buf[0] != 'e') e(32); + + if (lseek(fd, (off_t) - 1, SEEK_END) != (off_t) 1006) e(33); + if (read(fd, buf, sizeof(buf)) != 1) e(34); + if (buf[0] != 'e') e(35); + + /* Closing time. */ + if (close(fd) != 0) e(36); +} + +void test40b() +{ + int fd1, fd2, fd3; + int stat_loc; + + subtest = 2; + System("rm -rf ../DIR_40/*"); + + /* See if childs lseek() is effecting the parent. * See also if + * lseeking() on same file messes things up. */ + + /* Creat a file of 11 bytes. */ + if ((fd1 = open("santa", O_WRONLY | O_CREAT, 0777)) != 3) e(1); + if (write(fd1, "ho ho ho ho", 11) != 11) e(2); + if (close(fd1) != 0) e(3); + + /* Open it multiple times. */ + if ((fd1 = open("santa", O_RDONLY)) != 3) e(4); + if ((fd2 = open("santa", O_WRONLY)) != 4) e(5); + if ((fd3 = open("santa", O_RDWR)) != 5) e(6); + + /* Set all offsets different. */ + if (lseek(fd1, (off_t) 2, SEEK_SET) != 2) e(7); + if (lseek(fd2, (off_t) 4, SEEK_SET) != 4) e(8); + if (lseek(fd3, (off_t) 7, SEEK_SET) != 7) e(9); + + /* Have a child process do additional offset changes. */ + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(20); + if (lseek(fd1, (off_t) 1, SEEK_CUR) != 3) e(10); + if (lseek(fd2, (off_t) 5, SEEK_SET) != 5) e(11); + if (lseek(fd3, (off_t) - 4, SEEK_END) != 7) e(12); + exit(0); + default: + wait(&stat_loc); + if (stat_loc != 0) e(13); /* Alarm? */ + } + + /* Check if the new offsets are correct. */ + if (lseek(fd1, (off_t) 0, SEEK_CUR) != 3) e(14); + if (lseek(fd2, (off_t) 0, SEEK_CUR) != 5) e(15); + if (lseek(fd3, (off_t) 0, SEEK_CUR) != 7) e(16); + + /* Close the file. */ + if (close(fd1) != 0) e(17); + if (close(fd2) != 0) e(18); + if (close(fd3) != 0) e(19); +} + +void test40c() +{ /* Test error returns. */ + int fd; + int tube[2]; + int i, stat_loc; + + subtest = 3; + System("rm -rf ../DIR_40/*"); + + /* Fifo's can't be lseeked(). */ + Mkfifo("fifo"); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(3); /* Try for max 3 secs. */ + if ((fd = open("fifo", O_RDONLY)) != 3) e(1); + if (lseek(fd, (off_t) 0, SEEK_SET) != (off_t) - 1) e(2); + if (errno != ESPIPE) e(3); + if (close(fd) != 0) e(4); + exit(0); + default: + if ((fd = open("fifo", O_WRONLY)) != 3) e(5); + wait(&stat_loc); + if (stat_loc != 0) e(6);/* Alarm? */ + if (close(fd) != 0) e(7); + } + + /* Pipes can't be lseeked() eigther. */ + if (pipe(tube) != 0) e(8); + switch (fork()) { + case -1: printf("Can't fork\n"); break; + case 0: + alarm(3); /* Max 3 sconds wait. */ + if (lseek(tube[0], (off_t) 0, SEEK_SET) != (off_t) - 1) e(9); + if (errno != ESPIPE) e(10); + if (lseek(tube[1], (off_t) 0, SEEK_SET) != (off_t) - 1) e(11); + if (errno != ESPIPE) e(12); + exit(0); + default: + wait(&stat_loc); + if (stat_loc != 0) e(14); /* Alarm? */ + } + + /* Close the pipe. */ + if (close(tube[0]) != 0) e(15); + if (close(tube[1]) != 0) e(16); + + /* Whence arument invalid. */ + System("echo -n contact > file"); + if ((fd = open("file", O_RDWR)) != 3) e(17); + for (i = -1000; i < 1000; i++) { + if (i == SEEK_SET || i == SEEK_END || i == SEEK_CUR) continue; + if (lseek(fd, (off_t) 0, i) != (off_t) -1) e(18); + if (errno != EINVAL) e(19); + } + if (close(fd) != 0) e(20); + + /* EBADF for bad fides. */ + for (i = -1000; i < 1000; i++) { + if (i >= 0 && i < OPEN_MAX) continue; + if (lseek(i, (off_t) 0, SEEK_SET) != (off_t) - 1) e(21); + if (lseek(i, (off_t) 0, SEEK_END) != (off_t) - 1) e(22); + if (lseek(i, (off_t) 0, SEEK_CUR) != (off_t) - 1) e(23); + } +} + + +void e(n) +int n; +{ + int err_num = errno; /* Save in case printf clobbers it. */ + + printf("Subtest %d, error %d errno=%d: ", subtest, n, errno); + errno = err_num; + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } + errno = 0; +} + + +void quit() +{ + Chdir(".."); + System("rm -rf DIR_40"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test5.c b/test/test5.c new file mode 100644 index 000000000..71991314d --- /dev/null +++ b/test/test5.c @@ -0,0 +1,413 @@ +/* test 5 */ + +#include <sys/types.h> +#include <sys/wait.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> +#include <string.h> + +#define ITERATIONS 2 +#define MAX_ERROR 4 + +int errct; +int subtest; +int zero[1024]; + +int sigmap[5] = {9, 10, 11}; + +_PROTOTYPE(int main, (int argc, char *argv[])); +_PROTOTYPE(void test5a, (void)); +_PROTOTYPE(void parent, (int childpid)); +_PROTOTYPE(void child, (int parpid)); +_PROTOTYPE(void func1, (int s)); +_PROTOTYPE(void func8, (int s)); +_PROTOTYPE(void func10, (int s)); +_PROTOTYPE(void func11, (int s)); +_PROTOTYPE(void test5b, (void)); +_PROTOTYPE(void test5c, (void)); +_PROTOTYPE(void test5d, (void)); +_PROTOTYPE(void test5e, (void)); +_PROTOTYPE(void test5f, (void)); +_PROTOTYPE(void test5g, (void)); +_PROTOTYPE(void funcalrm, (int s)); +_PROTOTYPE(void test5h, (void)); +_PROTOTYPE(void test5i, (void)); +_PROTOTYPE(void ex, (void)); +_PROTOTYPE(void e, (int n)); +_PROTOTYPE(void quit, (void)); + + +#ifdef _ANSI +void (*Signal(int _sig, void (*_func)(int)))(int); +#define SIG_ZERO ((void (*)(int))0) /* default signal handling */ +#else +sighandler_t Signal(); +/* void (*Signal()) (); */ +#define SIG_ZERO ((void (*)())0) /* default signal handling */ +#endif + +_VOLATILE int childsigs, parsigs, alarms; + +int main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0x7777; + + printf("Test 5 "); + fflush(stdout); /* have to flush for child's benefit */ + + system("rm -rf DIR_05; mkdir DIR_05"); + chdir("DIR_05"); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 0001) test5a(); + if (m & 0002) test5b(); + if (m & 0004) test5c(); + if (m & 0010) test5d(); + if (m & 0020) test5e(); + if (m & 0040) test5f(); + if (m & 0100) test5g(); + if (m & 0200) test5h(); + if (m & 0400) test5i(); + } + quit(); + return(-1); /* impossible */ +} + + + +void test5a() +{ + int parpid, childpid, flag, *zp; + + subtest = 0; + flag = 0; + for (zp = &zero[0]; zp < &zero[1024]; zp++) + if (*zp != 0) flag = 1; + if (flag) e(0); /* check if bss is cleared to 0 */ + if (Signal(1, func1) == SIG_ERR) e(1); + if (Signal(10, func10) < SIG_ZERO) e(2); + parpid = getpid(); + if (childpid = fork()) { + if (childpid < 0) ex(); + parent(childpid); + } else { + child(parpid); + } + if (Signal(1, SIG_DFL) < SIG_ZERO) e(4); + if (Signal(10, SIG_DFL) < SIG_ZERO) e(5); +} + +void parent(childpid) +int childpid; +{ + int i, pid; + + for (i = 0; i < 3; i++) { + if (kill(childpid, 1) < 0) e(6); + while (parsigs == 0); + parsigs--; + } + if ( (pid = wait(&i)) < 0) e(7); + if (i != 256 * 6) e(8); +} + +void child(parpid) +int parpid; +{ + + int i; + + for (i = 0; i < 3; i++) { + while (childsigs == 0); + childsigs--; + if (kill(parpid, 10) < 0) e(9); + } + exit(6); +} + +void func1(s) +int s; /* for ANSI */ +{ + if (Signal(1, func1) < SIG_ZERO) e(10); + childsigs++; +} + +void func8(s) +int s; +{ +} + +void func10(s) +int s; /* for ANSI */ +{ + if (Signal(10, func10) < SIG_ZERO) e(11); + parsigs++; +} + +void func11(s) +int s; /* for ANSI */ +{ + e(38); +} + + +void test5b() +{ + int cpid, n, pid; + + subtest = 1; + if ((pid = fork())) { + if (pid < 0) ex(); + if ((pid = fork())) { + if (pid < 0) ex(); + if (cpid = fork()) { + if (cpid < 0) ex(); + if (kill(cpid, 9) < 0) e(12); + if (wait(&n) < 0) e(13); + if (wait(&n) < 0) e(14); + if (wait(&n) < 0) e(15); + } else { + pause(); + while (1); + } + } else { + exit(0); + } + } else { + exit(0); + } +} + + +void test5c() +{ + int n, i, pid, wpid; + + /* Test exit status codes for processes killed by signals. */ + subtest = 3; + for (i = 0; i < 2; i++) { + if (pid = fork()) { + if (pid < 0) ex(); + sleep(2); /* wait for child to pause */ + if (kill(pid, sigmap[i]) < 0) { + e(20); + exit(1); + } + if ((wpid = wait(&n)) < 0) e(21); + if ((n & 077) != sigmap[i]) e(22); + if (pid != wpid) e(23); + } else { + pause(); + exit(0); + } + } +} + +void test5d() +{ +/* Test alarm */ + + int i; + + subtest = 4; + alarms = 0; + for (i = 0; i < 8; i++) { + Signal(SIGALRM, funcalrm); + alarm(1); + pause(); + if (alarms != i + 1) e(24); + } +} + + + +void test5e() +{ +/* When a signal knocks a processes out of WAIT or PAUSE, it is supposed to + * get EINTR as error status. Check that. + */ + int n, j; + + subtest = 5; + if (Signal(8, func8) < SIG_ZERO) e(25); + if (n = fork()) { + /* Parent must delay to give child a chance to pause. */ + if (n < 0) ex(); + sleep(1); + if (kill(n, 8) < 0) e(26); + if (wait(&n) < 0) e(27); + if (Signal(8, SIG_DFL) < SIG_ZERO) e(28); + } else { + j = pause(); + if (errno != EINTR && -errno != EINTR) e(29); + exit(0); + } +} + + +void test5f() +{ + int i, j, k, n; + + subtest = 6; + if (getuid() != 0) return; + n = fork(); + if (n < 0) ex(); + if (n) { + wait(&i); + i = (i >> 8) & 0377; + if (i != (n & 0377)) e(30); + } else { + i = getgid(); + j = getegid(); + k = (i + j + 7) & 0377; + if (setgid(k) < 0) e(31); + if (getgid() != k) e(32); + if (getegid() != k) e(33); + i = getuid(); + j = geteuid(); + k = (i + j + 1) & 0377; + if (setuid(k) < 0) e(34); + if (getuid() != k) e(35); + if (geteuid() != k) e(36); + i = getpid() & 0377; + if (wait(&j) != -1) e(37); + exit(i); + } +} + + +void test5g() +{ + int n; + + subtest = 7; + Signal(11, func11); + Signal(11, SIG_IGN); + n = getpid(); + if (kill(n, 11) != 0) e(1); + Signal(11, SIG_DFL); +} + +void funcalrm(s) +int s; /* for ANSI */ +{ + alarms++; +} + + +void test5h() +{ +/* When a signal knocks a processes out of PIPE, it is supposed to + * get EINTR as error status. Check that. + */ + int n, j, fd[2]; + + subtest = 8; + unlink("XXX.test5"); + if (Signal(8, func8) < SIG_ZERO) e(1); + pipe(fd); + if (n = fork()) { + /* Parent must delay to give child a chance to pause. */ + if (n < 0) ex(); + while (access("XXX.test5", 0) != 0) /* just wait */ ; + sleep(1); + unlink("XXX.test5"); + if (kill(n, 8) < 0) e(2); + if (wait(&n) < 0) e(3); + if (Signal(8, SIG_DFL) < SIG_ZERO) e(4); + if (close(fd[0]) != 0) e(5); + if (close(fd[1]) != 0) e(6); + } else { + if (creat("XXX.test5", 0777) < 0) e(7); + j = read(fd[0], (char *) &n, 1); + if (errno != EINTR) e(8); + exit(0); + } +} + +void test5i() +{ + int fd[2], pid, buf[10], n; + + subtest = 9; + pipe(fd); + unlink("XXXxxxXXX"); + + if ( (pid = fork())) { + /* Parent */ + /* Wait until child has started and has created the XXXxxxXXX file. */ + while (access("XXXxxxXXX", 0) != 0) /* loop */ ; + sleep(1); + if (kill(pid, SIGKILL) != 0) e(1); + if (wait(&n) < 0) e(2); + if (close(fd[0]) != 0) e(3); + if (close(fd[1]) != 0) e(4); + } else { + if (creat("XXXxxxXXX", 0777) < 0) e(5); + read(fd[0], (char *) buf, 1); + e(5); /* should be killed by signal and not get here */ + } + unlink("XXXxxxXXX"); +} + + +void ex() +{ + int e = errno; + printf("Fork failed: %s (%d)\n", strerror(e), e); + exit(1); +} + + +void e(n) +int n; +{ + int err_num = errno; /* save errno in case printf clobbers it */ + + printf("Subtest %d, error %d errno=%d ", subtest, n, errno); + errno = err_num; /* restore errno, just in case */ + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} + +#ifdef _ANSI +void (*Signal(int a, void (*b)(int)))(int) +#else +sighandler_t Signal(a, b) +int a; +void (*b)(); +#endif +{ + if (signal(a, (void (*) ()) b) == (void (*)()) -1) + return(SIG_ERR); + else + return(SIG_ZERO); +} + + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test6.c b/test/test6.c new file mode 100644 index 000000000..dd6588358 --- /dev/null +++ b/test/test6.c @@ -0,0 +1,218 @@ +/* test 6 */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#define MAX_ERROR 4 + +int errct; +int subtest = 1; +int zilch[5000]; +char curdir[PATH_MAX]; + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void test6a, (void)); +_PROTOTYPE(void test6b, (void)); +_PROTOTYPE(void test6c, (void)); +_PROTOTYPE(void e, (int n)); +_PROTOTYPE(void quit, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + if (geteuid() == 0 || getuid() == 0) { + printf("Test 6 cannot run as root; test aborted\n"); + exit(1); + } + + if (argc == 2) m = atoi(argv[1]); + + printf("Test 6 "); + fflush(stdout); + + getcwd(curdir, PATH_MAX); + system("rm -rf DIR_06; mkdir DIR_06"); + chdir("DIR_06"); + + for (i = 0; i < 70; i++) { + if (m & 00001) test6a(); + if (m & 00002) test6b(); + if (m & 00004) test6c(); + } + + quit(); + return(-1); /* impossible */ +} + + + +void test6a() +{ +/* Test sbrk() and brk(). */ + + char *addr, *addr2, *addr3; + int i, del, click, click2; + + subtest = 1; + addr = sbrk(0); + addr = sbrk(0); /* force break to a click boundary */ + for (i = 0; i < 10; i++) sbrk(7 * i); + for (i = 0; i < 10; i++) sbrk(-7 * i); + if (sbrk(0) != addr) e(1); + sbrk(30); + if (brk(addr) != 0) e(2); + if (sbrk(0) != addr) e(3); + + del = 0; + do { + del++; + brk(addr + del); + addr2 = sbrk(0); + } while (addr2 == addr); + click = addr2 - addr; + sbrk(-1); + if (sbrk(0) != addr) e(4); + brk(addr); + if (sbrk(0) != addr) e(5); + + del = 0; + do { + del++; + brk(addr - del); + addr3 = sbrk(0); + } while (addr3 == addr); + click2 = addr - addr3; + sbrk(1); + if (sbrk(0) != addr) e(6); + brk(addr); + if (sbrk(0) != addr) e(8); + if (click != click2) e(9); + + brk(addr + 2 * click); + if (sbrk(0) != addr + 2 * click) e(10); + sbrk(3 * click); + if (sbrk(0) != addr + 5 * click) e(11); + sbrk(-5 * click); + if (sbrk(0) != addr) e(12); +} + + +void test6b() +{ + int i, err; + + subtest = 2; + signal(SIGQUIT, SIG_IGN); + err = 0; + for (i = 0; i < 5000; i++) + if (zilch[i] != 0) err++; + if (err > 0) e(1); + kill(getpid(), SIGQUIT); +} + + + +void test6c() +{ +/* Test mknod, chdir, chmod, chown, access. */ + + int i, j; + struct stat s; + + subtest = 3; + if (getuid() != 0) return; + for (j = 0; j < 2; j++) { + umask(0); + + if (chdir("/") < 0) e(1); + if (mknod("dir", 040700, 0) < 0) e(2); + if (link("/", "/dir/..") < 0) e(3); + if (mknod("T3a", 0777, 0) < 0) e(4); + if (mknod("/dir/T3b", 0777, 0) < 0) e(5); + if (mknod("dir/T3c", 0777, 0) < 0) e(6); + if ((i = open("/dir/T3b", 0)) < 0) e(7); + if (close(i) < 0) e(8); + if ((i = open("dir/T3c", O_RDONLY)) < 0) e(9); + if (close(i) < 0) e(10); + if (chdir("dir") < 0) e(11); + if ((i = open("T3b", 0)) < 0) e(12); + if (close(i) < 0) e(13); + if ((i = open("../T3a", O_RDONLY)) < 0) e(14); + if (close(i) < 0) e(15); + if ((i = open("../dir/../dir/../dir/../dir/../dir/T3c", O_RDONLY)) < 0) + e(16); + if (close(i) < 0) e(17); + + if (chmod("../dir/../dir/../dir/../dir/../T3a", 0123) < 0) e(18); + if (stat("../dir/../dir/../dir/../T3a", &s) < 0) e(19); + if ((s.st_mode & 077777) != 0123) e(20); + if (chmod("../dir/../dir/../T3a", 0456) < 0) e(21); + if (stat("../T3a", &s) < 0) e(22); + if ((s.st_mode & 077777) != 0456) e(23); + if (chown("../dir/../dir/../T3a", 20, 30) < 0) e(24); + if (stat("../T3a", &s) < 0) e(25); + if (s.st_uid != 20) e(26); + if (s.st_gid != 30) e(27); + + if ((i = open("/T3c", O_RDONLY)) >= 0) e(28); + if ((i = open("/T3a", O_RDONLY)) < 0) e(29); + if (close(i) < 0) e(30); + + if (access("/T3a", 4) < 0) e(31); + if (access("/dir/T3b", 4) < 0) e(32); + if (access("/dir/T3d", 4) >= 0) e(33); + + if (unlink("T3b") < 0) e(34); + if (unlink("T3c") < 0) e(35); + if (unlink("..") < 0) e(36); + if (chdir("/") < 0) e(37); + if (unlink("dir") < 0) e(38); + if (unlink("/T3a") < 0) e(39); + } + +} + +void e(n) +int n; +{ + + int err_num = errno; /* save errno in case printf clobbers it */ + + printf("Subtest %d, error %d errno=%d ", subtest, n, errno); + errno = err_num; /* restore errno, just in case */ + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + chdir(curdir); + system("rm -rf DIR*"); + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test7.c b/test/test7.c new file mode 100644 index 000000000..be072170b --- /dev/null +++ b/test/test7.c @@ -0,0 +1,705 @@ +/* POSIX test program (7). Author: Andy Tanenbaum */ + +/* The following POSIX calls are tested: + * pipe(), mkfifo(), fcntl() + */ + + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <stdio.h> + +#define ITERATIONS 4 +#define MAX_ERROR 4 +#define ITEMS 32 +#define READ 10 +#define WRITE 20 +#define UNLOCK 30 +#define U 70 +#define L 80 + +char buf[ITEMS] = {0,1,2,3,4,5,6,7,8,9,8,7,6,5,4,3,2,1,0,1,2,3,4,5,6,7,8,9}; + +int subtest, errct, xfd; +int whence = SEEK_SET, func_code = F_SETLK; +extern char **environ; + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void test7a, (void)); +_PROTOTYPE(void test7b, (void)); +_PROTOTYPE(void test7c, (void)); +_PROTOTYPE(void test7d, (void)); +_PROTOTYPE(void test7e, (void)); +_PROTOTYPE(void test7f, (void)); +_PROTOTYPE(void test7g, (void)); +_PROTOTYPE(void test7h, (void)); +_PROTOTYPE(void test7i, (void)); +_PROTOTYPE(void test7j, (void)); +_PROTOTYPE(void cloexec_test, (void)); +_PROTOTYPE(int set, (int how, int first, int last)); +_PROTOTYPE(int locked, (int b)); +_PROTOTYPE(void e, (int n)); +_PROTOTYPE(void sigfunc, (int s)); +_PROTOTYPE(void quit, (void)); + + +int main(argc, argv) +int argc; +char *argv[]; +{ + + int i, m = 0xFFFF; + + sync(); + + if (argc == 2) m = atoi(argv[1]); + if (m == 0) cloexec_test(); /* important; do not remove this! */ + printf("Test 7 "); + fflush(stdout); + + system("rm -rf DIR_07; mkdir DIR_07"); + chdir("DIR_07"); + + for (i = 0; i < ITERATIONS; i++) { + if (m & 00001) test7a(); + if (m & 00002) test7b(); + if (m & 00004) test7c(); + if (m & 00010) test7d(); + if (m & 00020) test7e(); + if (m & 00040) test7f(); + if (m & 00100) test7g(); + if (m & 00200) test7h(); + if (m & 00400) test7i(); + if (m & 01000) test7j(); + } + quit(); + return(-1); /* impossible */ +} + +void test7a() +{ +/* Test pipe(). */ + + int i, fd[2], ect; + char buf2[ITEMS+1]; + + /* Create a pipe, write on it, and read it back. */ + subtest = 1; + if (pipe(fd) != 0) e(1); + if (write(fd[1], buf, ITEMS) != ITEMS) e(2); + buf2[0] = 0; + if (read(fd[0], buf2, ITEMS+1) != ITEMS) e(3); + ect = 0; + for (i = 0; i < ITEMS; i++) if (buf[i] != buf2[i]) ect++; + if (ect != 0) e(4); + if (close(fd[0]) != 0) e(5); + if (close(fd[1]) != 0) e(6); + + /* Error test. Keep opening pipes until it fails. Check error code. */ + errno = 0; + while (1) { + if (pipe(fd) < 0) break; + } + if (errno != EMFILE) e(7); + + /* Close all the pipes. */ + for (i = 3; i < OPEN_MAX; i++) close(i); +} + + +void test7b() +{ +/* Test mkfifo(). */ + + int fdr, fdw, status; + char buf2[ITEMS+1]; + int efork; + + /* Create a fifo, write on it, and read it back. */ + subtest = 2; + if (mkfifo("T7.b", 0777) != 0) e(1); + switch (fork()) { + case -1: + efork = errno; + printf("Fork failed: %s (%d)\n", strerror(efork), efork); + exit(1); + case 0: + /* Child reads from the fifo. */ + if ( (fdr = open("T7.b", O_RDONLY)) < 0) e(5); + if (read(fdr, buf2, ITEMS+1) != ITEMS) e(6); + if (strcmp(buf, buf2) != 0) e(7); + if (close(fdr) != 0) e(8); + exit(0); + default: + /* Parent writes on the fifo. */ + if ( (fdw = open("T7.b", O_WRONLY)) < 0) e(2); + if (write(fdw, buf, ITEMS) != ITEMS) e(3); + wait(&status); + if (close(fdw) != 0) e(4); + } + + /* Check some error conditions. */ + if (mkfifo("T7.b", 0777) != -1) e(9); + errno = 0; + if (mkfifo("a/b/c", 0777) != -1) e(10); + if (errno != ENOENT) e(11); + errno = 0; + if (mkfifo("", 0777) != -1) e(12); + if (errno != ENOENT) e(13); + errno = 0; + if (mkfifo("T7.b/x", 0777) != -1) e(14); + if (errno != ENOTDIR) e(15); + if (unlink("T7.b") != 0) e(16); + + /* Now check fifos and the O_NONBLOCK flag. */ + if (mkfifo("T7.b", 0600) != 0) e(17); + errno = 0; + if (open("T7.b", O_WRONLY | O_NONBLOCK) != -1) e(18); + if (errno != ENXIO) e(19); + if ( (fdr = open("T7.b", O_RDONLY | O_NONBLOCK)) < 0) e(20); + if (fork()) { + /* Parent reads from fdr. */ + wait(&status); /* but first make sure writer has already run*/ + if ( ( (status>>8) & 0377) != 77) e(21); + if (read(fdr, buf2, ITEMS+1) != ITEMS) e(22); + if (strcmp(buf, buf2) != 0) e(23); + if (close(fdr) != 0) e(24); + } else { + /* Child opens the fifo for writing and writes to it. */ + if ( (fdw = open("T7.b", O_WRONLY | O_NONBLOCK)) < 0) e(25); + if (write(fdw, buf, ITEMS) != ITEMS) e(26); + if (close(fdw) != 0) e(27); + exit(77); + } + + if (unlink("T7.b") != 0) e(28); +} + + +void test7c() +{ +/* Test fcntl(). */ + + int fd, m, s, newfd, newfd2; + struct stat stat1, stat2, stat3; + + subtest = 3; + errno = -100; + if ( (fd = creat("T7.c", 0777)) < 0) e(1); + + /* Turn the per-file-descriptor flags on and off. */ + if (fcntl(fd, F_GETFD) != 0) e(2); /* FD_CLOEXEC is initially off */ + if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) e(3);/* turn it on */ + if (fcntl(fd, F_GETFD) != FD_CLOEXEC) e(4); /* should be on now */ + if (fcntl(fd, F_SETFD, 0) != 0) e(5); /* turn it off */ + if (fcntl(fd, F_GETFD) != 0) e(6); /* should be off now */ + + /* Turn the open-file-description flags on and off. Start with O_APPEND. */ + m = O_WRONLY; + if (fcntl(fd, F_GETFL) != m) e(7); /* O_APPEND, O_NONBLOCK are off */ + if (fcntl(fd, F_SETFL, O_APPEND) != 0) e(8); /* turn on O_APPEND */ + if (fcntl(fd, F_GETFL) != (O_APPEND | m)) e(9);/* should be on now */ + if (fcntl(fd, F_SETFL, 0) != 0) e(10); /* turn it off */ + if (fcntl(fd, F_GETFL) != m) e(11); /* should be off now */ + + /* Turn the open-file-description flags on and off. Now try O_NONBLOCK. */ + if (fcntl(fd, F_SETFL, O_NONBLOCK) != 0) e(12); /* turn on O_NONBLOCK */ + if (fcntl(fd, F_GETFL) != (O_NONBLOCK | m)) e(13); /* should be on now */ + if (fcntl(fd, F_SETFL, 0) != 0) e(14); /* turn it off */ + if (fcntl(fd, F_GETFL) != m) e(15); /* should be off now */ + + /* Now both at once. */ + if (fcntl(fd, F_SETFL, O_APPEND|O_NONBLOCK) != 0) e(16); + if (fcntl(fd, F_GETFL) != (O_NONBLOCK | O_APPEND | m)) e(17); + if (fcntl(fd, F_SETFL, 0) != 0) e(18); + if (fcntl(fd, F_GETFL) != m) e(19); + + /* Now test F_DUPFD. */ + if ( (newfd = fcntl(fd, F_DUPFD, 0)) != 4) e(20); /* 0-4 open */ + if ( (newfd2 = fcntl(fd, F_DUPFD, 0)) != 5) e(21); /* 0-5 open */ + if (close(newfd) != 0) e(22); /* 0-3, 5 open */ + if ( (newfd = fcntl(fd, F_DUPFD, 0)) != 4) e(23); /* 0-5 open */ + if (close(newfd) != 0) e(24); /* 0-3, 5 open */ + if ( (newfd = fcntl(fd, F_DUPFD, 5)) != 6) e(25); /* 0-3, 5, 6 open */ + if (close(newfd2) != 0) e(26); /* 0-3, 6 open */ + + /* O_APPEND should be inherited, but FD_CLOEXEC should be cleared. Check. */ + if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) e(26);/* turn FD_CLOEXEC on */ + if (fcntl(fd, F_SETFL, O_APPEND) != 0) e(27); /* turn O_APPEND on */ + if ( (newfd2 = fcntl(fd, F_DUPFD, 10)) != 10) e(28); /* 0-3, 6, 10 open */ + if (fcntl(newfd2, F_GETFD) != 0) e(29); /* FD_CLOEXEC must be 0 */ + if (fcntl(newfd2, F_GETFL) != (O_APPEND | m)) e(30); /* O_APPEND set */ + if (fcntl(fd, F_SETFD, 0) != 0) e(31);/* turn FD_CLOEXEC off */ + + /* Check if newfd and newfd2 are the same inode. */ + if (fstat(fd, &stat1) != 0) e(32); + if (fstat(fd, &stat2) != 0) e(33); + if (fstat(fd, &stat3) != 0) e(34); + if (stat1.st_dev != stat2.st_dev) e(35); + if (stat1.st_dev != stat3.st_dev) e(36); + if (stat1.st_ino != stat2.st_ino) e(37); + if (stat1.st_ino != stat3.st_ino) e(38); + + /* Now check on the FD_CLOEXEC flag. Set it for fd (3) and newfd2 (10) */ + if (fd != 3 || newfd2 != 10 || newfd != 6) e(39); + if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) e(40); /* close 3 on exec */ + if (fcntl(newfd2, F_SETFD, FD_CLOEXEC) != 0) e(41); /* close 10 on exec */ + if (fcntl(newfd, F_SETFD, 0) != 0) e(42); /* don't close 6 */ + if (fork()) { + wait(&s); /* parent just waits */ + if (WEXITSTATUS(s) != 0) e(43); + } else { + execle("../test7", "test7", "0", (char *) 0, environ); + exit(1); /* the impossible never happens, right? */ + } + + /* Finally, close all the files. */ + if (fcntl(fd, F_SETFD, 0) != 0) e(44); /* FD_CLOEXEC off */ + if (fcntl(newfd2, F_SETFD, 0) != 0) e(45); /* FD_CLOEXEC off */ + if (close(fd) != 0) e(46); + if (close(newfd) != 0) e(47); + if (close(newfd2) != 0) e(48); +} + + +void test7d() +{ +/* Test file locking. */ + + subtest = 4; + + if ( (xfd = creat("T7.d", 0777)) != 3) e(1); + close(xfd); + if ( (xfd = open("T7.d", O_RDWR)) < 0) e(2); + if (write(xfd, buf, ITEMS) != ITEMS) e(3); + if (set(WRITE, 0, 3) != 0) e(4); + if (set(WRITE, 5, 9) != 0) e(5); + if (set(UNLOCK, 0, 3) != 0) e(6); + if (set(UNLOCK, 4, 9) != 0) e(7); + + if (set(READ, 1, 4) != 0) e(8); + if (set(READ, 4, 7) != 0) e(9); + if (set(UNLOCK, 4, 7) != 0) e(10); + if (set(UNLOCK, 1, 4) != 0) e(11); + + if (set(WRITE, 0, 3) != 0) e(12); + if (set(WRITE, 5, 7) != 0) e(13); + if (set(WRITE, 9 ,10) != 0) e(14); + if (set(UNLOCK, 0, 4) != 0) e(15); + if (set(UNLOCK, 0, 7) != 0) e(16); + if (set(UNLOCK, 0, 2000) != 0) e(17); + + if (set(WRITE, 0, 3) != 0) e(18); + if (set(WRITE, 5, 7) != 0) e(19); + if (set(WRITE, 9 ,10) != 0) e(20); + if (set(UNLOCK, 0, 100) != 0) e(21); + + if (set(WRITE, 0, 9) != 0) e(22); + if (set(UNLOCK, 8, 9) != 0) e(23); + if (set(UNLOCK, 0, 2) != 0) e(24); + if (set(UNLOCK, 5, 5) != 0) e(25); + if (set(UNLOCK, 4, 6) != 0) e(26); + if (set(UNLOCK, 3, 3) != 0) e(27); + if (set(UNLOCK, 7, 7) != 0) e(28); + + if (set(WRITE, 0, 10) != 0) e(29); + if (set(UNLOCK, 0, 1000) != 0) e(30); + + /* Up until now, all locks have been disjoint. Now try conflicts. */ + if (set(WRITE, 0, 4) != 0) e(31); + if (set(WRITE, 4, 7) != 0) e(32); /* owner may lock same byte twice */ + if (set(WRITE, 5, 10) != 0) e(33); + if (set(UNLOCK, 0, 11) != 0) e(34); + + /* File is now unlocked. Length 0 means whole file. */ + if (set(WRITE, 2, 1) != 0) e(35); /* this locks whole file */ + if (set(WRITE, 9,10) != 0) e(36); /* a process can relock its file */ + if (set(WRITE, 3, 3) != 0) e(37); + if (set(UNLOCK, 0, -1) != 0) e(38); /* file is now unlocked. */ + + /* Test F_GETLK. */ + if (set(WRITE, 2, 3) != 0) e(39); + if (locked(1) != U) e(40); + if (locked(2) != L) e(41); + if (locked(3) != L) e(42); + if (locked(4) != U) e(43); + if (set(UNLOCK, 2, 3) != 0) e(44); + if (locked(2) != U) e(45); + if (locked(3) != U) e(46); + + close(xfd); +} + +void test7e() +{ +/* Test to see if SETLKW blocks as it should. */ + + int pid, s; + + subtest = 5; + + if ( (xfd = creat("T7.e", 0777)) != 3) e(1); + if (close(xfd) != 0) e(2); + if ( (xfd = open("T7.e", O_RDWR)) < 0) e(3); + if (write(xfd, buf, ITEMS) != ITEMS) e(4); + if (set(WRITE, 0, 3) != 0) e(5); + + if ( (pid = fork()) ) { + /* Parent waits until child has started before signaling it. */ + while (access("T7.e1", 0) != 0) ; + unlink("T7.e1"); + sleep(1); + if (kill(pid, SIGKILL) < 0) e(6); + if (wait(&s) != pid) e(7); + } else { + /* Child tries to lock and should block. */ + if (creat("T7.e1", 0777) < 0) e(8); + func_code = F_SETLKW; + if (set(WRITE, 0, 3) != 0) e(9); /* should block */ + errno = -1000; + e(10); /* process should be killed by signal */ + exit(0); /* should never happen */ + } + close(xfd); +} + +void test7f() +{ +/* Test to see if SETLKW gives EINTR when interrupted. */ + + int pid, s; + + subtest = 6; + + if ( (xfd = creat("T7.f", 0777)) != 3) e(1); + if (close(xfd) != 0) e(2); + if ( (xfd = open("T7.f", O_RDWR)) < 0) e(3); + if (write(xfd, buf, ITEMS) != ITEMS) e(4); + if (set(WRITE, 0, 3) != 0) e(5); + + if ( (pid = fork()) ) { + /* Parent waits until child has started before signaling it. */ + while (access("T7.f1", 0) != 0) ; + unlink("T7.f1"); + sleep(1); + if (kill(pid, SIGTERM) < 0) e(6); + if (wait(&s) != pid) e(7); + if ( (s>>8) != 19) e(8); + } else { + /* Child tries to lock and should block. + * `signal(SIGTERM, sigfunc);' to set the signal handler is inadequate + * because on systems like BSD the sigaction flags for signal include + * `SA_RESTART' so syscalls are restarted after they have been + * interrupted by a signal. + */ + struct sigaction sa, osa; + + sa.sa_handler = sigfunc; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + if (sigaction(SIGTERM, &sa, &osa) < 0) e(999); + if (creat("T7.f1", 0777) < 0) e(9); + func_code = F_SETLKW; + if (set(WRITE, 0, 3) != -1) e(10); /* should block */ + if (errno != EINTR) e(11); /* signal should release it */ + exit(19); + } + close(xfd); +} + + +void test7g() +{ +/* Test to see if SETLKW unlocks when the needed lock becomes available. */ + + int pid, s; + + subtest = 7; + + if ( (xfd = creat("T7.g", 0777)) != 3) e(1); + if (close(xfd) != 0) e(2); + if ( (xfd = open("T7.g", O_RDWR)) < 0) e(3); + if (write(xfd, buf, ITEMS) != ITEMS) e(4); + if (set(WRITE, 0, 3) != 0) e(5); /* bytes 0 to 3 are now locked */ + + if ( (pid = fork()) ) { + /* Parent waits for child to start. */ + while (access("T7.g1", 0) != 0) ; + unlink("T7.g1"); + sleep(1); + if (set(UNLOCK, 0, 3) != 0) e(5); + if (wait(&s) != pid) e(6); + if ( (s >> 8) != 29) e(7); + } else { + /* Child tells parent it is alive, then tries to lock and is blocked.*/ + func_code = F_SETLKW; + if (creat("T7.g1", 0777) < 0) e(8); + if (set(WRITE, 3, 3) != 0) e(9); /* process must block now */ + if (set(UNLOCK, 3, 3) != 0) e(10); + exit(29); + } + close(xfd); +} + + +void test7h() +{ +/* Test to see what happens if two processed block on the same lock. */ + + int pid, pid2, s, w; + + subtest = 8; + + if ( (xfd = creat("T7.h", 0777)) != 3) e(1); + if (close(xfd) != 0) e(2); + if ( (xfd = open("T7.h", O_RDWR)) < 0) e(3); + if (write(xfd, buf, ITEMS) != ITEMS) e(4); + if (set(WRITE, 0, 3) != 0) e(5); /* bytes 0 to 3 are now locked */ + + if ( (pid = fork()) ) { + if ( (pid2 = fork()) ) { + /* Parent waits for child to start. */ + while (access("T7.h1", 0) != 0) ; + while (access("T7.h2", 0) != 0) ; + unlink("T7.h1"); + unlink("T7.h2"); + sleep(1); + if (set(UNLOCK, 0, 3) != 0) e(6); + w = wait(&s); + if (w != pid && w != pid2) e(7); + s = s >> 8; + if (s != 39 && s != 49) e(8); + w = wait(&s); + if (w != pid && w != pid2) e(9); + s = s >> 8; + if (s != 39 && s != 49) e(10); + } else { + func_code = F_SETLKW; + if (creat("T7.h1", 0777) < 0) e(11); + if (set(WRITE, 0, 0) != 0) e(12); /* block now */ + if (set(UNLOCK, 0, 0) != 0) e(13); + exit(39); + } + } else { + /* Child tells parent it is alive, then tries to lock and is blocked.*/ + func_code = F_SETLKW; + if (creat("T7.h2", 0777) < 0) e(14); + if (set(WRITE, 0, 1) != 0) e(15); /* process must block now */ + if (set(UNLOCK, 0, 1) != 0) e(16); + exit(49); + } + close(xfd); +} + +void test7i() +{ +/* Check error conditions for fcntl(). */ + + int tfd, i; + + subtest = 9; + + errno = 0; + if ( (xfd = creat("T7.i", 0777)) != 3) e(1); + if (close(xfd) != 0) e(2); + if ( (xfd = open("T7.i", O_RDWR)) < 0) e(3); + if (write(xfd, buf, ITEMS) != ITEMS) e(4); + if (set(WRITE, 0, 3) != 0) e(5); /* bytes 0 to 3 are now locked */ + if (set(WRITE, 0, 0) != 0) e(6); + if (errno != 0) e(7); + errno = 0; + if (set(WRITE, 3, 3) != 0) e(8); + if (errno != 0) e(9); + tfd = xfd; /* hold good value */ + xfd = -99; + errno = 0; + if (set(WRITE, 0, 0) != -1) e(10); + if (errno != EBADF) e(11); + + errno = 0; + if ( (xfd = open("T7.i", O_WRONLY)) < 0) e(12); + if (set(READ, 0, 0) != -1) e(13); + if (errno != EBADF) e(14); + if (close(xfd) != 0) e(15); + + errno = 0; + if ( (xfd = open("T7.i", O_RDONLY)) < 0) e(16); + if (set(WRITE, 0, 0) != -1) e(17); + if (errno != EBADF) e(18); + if (close(xfd) != 0) e(19); + xfd = tfd; /* restore legal xfd value */ + + /* Check for EINVAL. */ + errno = 0; + if (fcntl(xfd, F_DUPFD, OPEN_MAX) != -1) e(20); + if (errno != EINVAL) e(21); + errno = 0; + if (fcntl(xfd, F_DUPFD, -1) != -1) e(22); + if (errno != EINVAL) e(23); + + xfd = 0; /* stdin does not support locking */ + errno = 0; + if (set(READ, 0, 0) != -1) e(24); + if (errno != EINVAL) e(25); + xfd = tfd; + + /* Check ENOLCK. */ + for (i = 0; i < ITEMS; i++) { + if (set(WRITE, i, i) == 0) continue; + if (errno != ENOLCK) { + e(26); + break; + } + } + + + /* Check EMFILE. */ + for (i = xfd + 1; i < OPEN_MAX; i++) open("T7.i", 0); /* use up all fds */ + errno = 0; + if (fcntl(xfd, F_DUPFD, 0) != -1) e(27); /* No fds left */ + if (errno != EMFILE) e(28); + + for (i = xfd; i < OPEN_MAX; i++) if (close(i) != 0) e(29); +} + + +void test7j() +{ +/* Test file locking with two processes. */ + + int s; + + subtest = 10; + + if ( (xfd = creat("T7.j", 0777)) != 3) e(1); + close(xfd); + if ( (xfd = open("T7.j", O_RDWR)) < 0) e(2); + if (write(xfd, buf, ITEMS) != ITEMS) e(3); + if (set(WRITE, 0, 4) != 0) e(4); /* lock belongs to parent */ + if (set(READ, 10, 16) != 0) e(5); /* lock belongs to parent */ + + /* Up until now, all locks have been disjoint. Now try conflicts. */ + if (fork()) { + /* Parent just waits for child to finish. */ + wait(&s); + } else { + /* Child does the testing. */ + errno = -100; + if (set(WRITE, 5, 7) < 0) e(6); /* should work */ + if (set(WRITE, 4, 7) >= 0) e(7); /* child may not lock byte 4 */ + if (errno != EACCES && errno != EAGAIN) e(8); + if (set(WRITE, 5, 9) != 0) e(9); + if (set(UNLOCK, 5, 9) != 0) e(10); + if (set(READ, 9, 17) < 0) e(11); /* shared READ lock is ok */ + exit(0); + } + close(xfd); +} + +void cloexec_test() +{ +/* To text whether the FD_CLOEXEC flag actually causes files to be + * closed upon exec, we have to exec something. The test is carried + * out by forking, and then having the child exec test7 itself, but + * with argument 0. This is detected, and control comes here. + * File descriptors 3 and 10 should be closed here, and 10 open. + */ + + if (close(3) == 0) e(1001); /* close should fail; it was closed on exec */ + if (close(6) != 0) e(1002); /* close should succeed */ + if (close(10) == 0) e(1003); /* close should fail */ + fflush(stdout); + exit(0); +} + + +int set(how, first, last) +int how, first, last; +{ + int r; + struct flock flock; + + if (how == READ) flock.l_type = F_RDLCK; + if (how == WRITE) flock.l_type = F_WRLCK; + if (how == UNLOCK) flock.l_type = F_UNLCK; + flock.l_whence = whence; + flock.l_start = (long) first; + flock.l_len = (long) last - (long) first + 1; + r = fcntl(xfd, func_code, &flock); + if (r != -1) + return(0); + else + return(-1); +} + +int locked(b) +int b; +/* Test to see if byte b is locked. Return L or U */ +{ + struct flock flock; + pid_t pid; + int status; + + flock.l_type = F_WRLCK; + flock.l_whence = whence; + flock.l_start = (long) b; + flock.l_len = 1; + + /* Process' own locks are invisible to F_GETLK, so fork a child to test. */ + pid = fork(); + if (pid == 0) { + if (fcntl(xfd, F_GETLK, &flock) != 0) e(2000); + exit(flock.l_type == F_UNLCK ? U : L); + } + if (pid == -1) e(2001); + if (fcntl(xfd, F_GETLK, &flock) != 0) e(2002); + if (flock.l_type != F_UNLCK) e(2003); + if (wait(&status) != pid) e(2004); + if (!WIFEXITED(status)) e(2005); + return(WEXITSTATUS(status)); +} + +void e(n) +int n; +{ + int err_num = errno; /* save errno in case printf clobbers it */ + + printf("Subtest %d, error %d errno=%d ", subtest, n, errno); + fflush(stdout); + errno = err_num; /* restore errno, just in case */ + perror(""); + if (errct++ > MAX_ERROR) { + printf("Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} + +void sigfunc(s) +int s; /* for ANSI */ +{ +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(1); + } +} diff --git a/test/test8.c b/test/test8.c new file mode 100644 index 000000000..bd65d21d8 --- /dev/null +++ b/test/test8.c @@ -0,0 +1,1051 @@ +/* test 8 - signals */ + +#include <sys/types.h> +#include <sys/times.h> +#ifdef _MINIX +#include <sys/sigcontext.h> +#endif +#include <sys/wait.h> +#include <errno.h> +#include <signal.h> +#include <setjmp.h> +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#define ITERATIONS 2 +#define SIGS 14 +#define MAX_ERROR 4 + +int iteration, cumsig, subtest, errct = 0, sig1, sig2; + +int sigarray[SIGS] = {SIGHUP, SIGILL, SIGTRAP, SIGABRT, SIGIOT, SIGUNUSED, + SIGFPE, SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, + SIGTERM}; + +/* Prototypes produced automatically by mkptypes. */ +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void test8a, (void)); +_PROTOTYPE(void func1, (int sig)); +_PROTOTYPE(void func2, (int sig)); +_PROTOTYPE(void test8b, (void)); +_PROTOTYPE(void catch1, (int signo)); +_PROTOTYPE(void catch2, (int signo)); +_PROTOTYPE(void test8c, (void)); +_PROTOTYPE(void catch3, (int signo)); +_PROTOTYPE(void test8d, (void)); +_PROTOTYPE(void catch4, (int signo)); +_PROTOTYPE(void test8e, (void)); +_PROTOTYPE(void catch5, (int signo)); +_PROTOTYPE(void test8f, (void)); +_PROTOTYPE(void sigint_handler, (int signo)); +_PROTOTYPE(void sigpipe_handler, (int signo)); +_PROTOTYPE(void test8g, (void)); +_PROTOTYPE(void sighup8, (int signo)); +_PROTOTYPE(void sigpip8, (int signo)); +_PROTOTYPE(void sigter8, (int signo)); +_PROTOTYPE(void test8h, (void)); +_PROTOTYPE(void sighup9, (int signo)); +_PROTOTYPE(void sigter9, (int signo)); +_PROTOTYPE(void test8i, (void)); +_PROTOTYPE(void sighup10, (int signo)); +_PROTOTYPE(void sigalrm_handler10, (int signo)); +_PROTOTYPE(void test8j, (void)); +_PROTOTYPE(void test8k, (void)); +_PROTOTYPE(void test8l, (void)); +_PROTOTYPE(void func_m1, (void)); +_PROTOTYPE(void func_m2, (void)); +_PROTOTYPE(void test8m, (void)); +_PROTOTYPE(void longjerr, (void)); +_PROTOTYPE(void catch14, (int signo, int code, struct sigcontext * scp)); +_PROTOTYPE(void test8n, (void)); +_PROTOTYPE(void catch15, (int signo)); +_PROTOTYPE(void test8o, (void)); +_PROTOTYPE(void clearsigstate, (void)); +_PROTOTYPE(void quit, (void)); +_PROTOTYPE(void wait_for, (int pid)); +_PROTOTYPE(void e, (int n)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + int i, m = 0xFFFF; + + sync(); + + if (argc == 2) m = atoi(argv[1]); + + printf("Test 8 "); + fflush(stdout); /* have to flush for child's benefit */ + + system("rm -rf DIR_08; mkdir DIR_08"); + chdir("DIR_08"); + + + for (i = 0; i < ITERATIONS; i++) { + iteration = i; + if (m & 0000001) test8a(); + if (m & 0000002) test8b(); + if (m & 0000004) test8c(); + if (m & 0000010) test8d(); + if (m & 0000020) test8e(); + if (m & 0000040) test8f(); + if (m & 0000100) test8g(); + if (m & 0000200) test8h(); + if (m & 0000400) test8i(); + if (m & 0001000) test8j(); + if (m & 0002000) test8k(); + if (m & 0004000) test8l(); + if (m & 0010000) test8m(); + if (m & 0020000) test8n(); + if (m & 0040000) test8o(); + } + + quit(); + return(-1); /* impossible */ +} + + +void test8a() +{ +/* Test signal set management. */ + + sigset_t s; + + subtest = 1; + clearsigstate(); + + /* Create an empty set and see if any bits are on. */ + if (sigemptyset(&s) != 0) e(1); + if (sigismember(&s, SIGHUP) != 0) e(2); + if (sigismember(&s, SIGINT) != 0) e(3); + if (sigismember(&s, SIGQUIT) != 0) e(4); + if (sigismember(&s, SIGILL) != 0) e(5); + if (sigismember(&s, SIGTRAP) != 0) e(6); + if (sigismember(&s, SIGABRT) != 0) e(7); + if (sigismember(&s, SIGIOT) != 0) e(8); + if (sigismember(&s, SIGUNUSED) != 0) e(9); + if (sigismember(&s, SIGFPE) != 0) e(10); + if (sigismember(&s, SIGKILL) != 0) e(11); + if (sigismember(&s, SIGUSR1) != 0) e(12); + if (sigismember(&s, SIGSEGV) != 0) e(13); + if (sigismember(&s, SIGUSR2) != 0) e(14); + if (sigismember(&s, SIGPIPE) != 0) e(15); + if (sigismember(&s, SIGALRM) != 0) e(16); + if (sigismember(&s, SIGTERM) != 0) e(17); + + /* Create a full set and see if any bits are off. */ + if (sigfillset(&s) != 0) e(19); + if (sigemptyset(&s) != 0) e(20); + if (sigfillset(&s) != 0) e(21); + if (sigismember(&s, SIGHUP) != 1) e(22); + if (sigismember(&s, SIGINT) != 1) e(23); + if (sigismember(&s, SIGQUIT) != 1) e(24); + if (sigismember(&s, SIGILL) != 1) e(25); + if (sigismember(&s, SIGTRAP) != 1) e(26); + if (sigismember(&s, SIGABRT) != 1) e(27); + if (sigismember(&s, SIGIOT) != 1) e(28); + if (sigismember(&s, SIGUNUSED) != 1) e(29); + if (sigismember(&s, SIGFPE) != 1) e(30); + if (sigismember(&s, SIGKILL) != 1) e(31); + if (sigismember(&s, SIGUSR1) != 1) e(32); + if (sigismember(&s, SIGSEGV) != 1) e(33); + if (sigismember(&s, SIGUSR2) != 1) e(34); + if (sigismember(&s, SIGPIPE) != 1) e(35); + if (sigismember(&s, SIGALRM) != 1) e(36); + if (sigismember(&s, SIGTERM) != 1) e(37); + + /* Create an empty set, then turn on bits individually. */ + if (sigemptyset(&s) != 0) e(39); + if (sigaddset(&s, SIGHUP) != 0) e(40); + if (sigaddset(&s, SIGINT) != 0) e(41); + if (sigaddset(&s, SIGQUIT) != 0) e(42); + if (sigaddset(&s, SIGILL) != 0) e(43); + if (sigaddset(&s, SIGTRAP) != 0) e(44); + + /* See if the bits just turned on are indeed on. */ + if (sigismember(&s, SIGHUP) != 1) e(45); + if (sigismember(&s, SIGINT) != 1) e(46); + if (sigismember(&s, SIGQUIT) != 1) e(47); + if (sigismember(&s, SIGILL) != 1) e(48); + if (sigismember(&s, SIGTRAP) != 1) e(49); + + /* The others should be turned off. */ + if (sigismember(&s, SIGABRT) != 0) e(50); + if (sigismember(&s, SIGIOT) != 0) e(51); + if (sigismember(&s, SIGUNUSED) != 0) e(52); + if (sigismember(&s, SIGFPE) != 0) e(53); + if (sigismember(&s, SIGKILL) != 0) e(54); + if (sigismember(&s, SIGUSR1) != 0) e(55); + if (sigismember(&s, SIGSEGV) != 0) e(56); + if (sigismember(&s, SIGUSR2) != 0) e(57); + if (sigismember(&s, SIGPIPE) != 0) e(58); + if (sigismember(&s, SIGALRM) != 0) e(59); + if (sigismember(&s, SIGTERM) != 0) e(60); + + /* Now turn them off and see if all are off. */ + if (sigdelset(&s, SIGHUP) != 0) e(62); + if (sigdelset(&s, SIGINT) != 0) e(63); + if (sigdelset(&s, SIGQUIT) != 0) e(64); + if (sigdelset(&s, SIGILL) != 0) e(65); + if (sigdelset(&s, SIGTRAP) != 0) e(66); + + if (sigismember(&s, SIGHUP) != 0) e(67); + if (sigismember(&s, SIGINT) != 0) e(68); + if (sigismember(&s, SIGQUIT) != 0) e(69); + if (sigismember(&s, SIGILL) != 0) e(70); + if (sigismember(&s, SIGTRAP) != 0) e(71); + if (sigismember(&s, SIGABRT) != 0) e(72); + if (sigismember(&s, SIGIOT) != 0) e(73); + if (sigismember(&s, SIGUNUSED) != 0) e(74); + if (sigismember(&s, SIGFPE) != 0) e(75); + if (sigismember(&s, SIGKILL) != 0) e(76); + if (sigismember(&s, SIGUSR1) != 0) e(77); + if (sigismember(&s, SIGSEGV) != 0) e(78); + if (sigismember(&s, SIGUSR2) != 0) e(79); + if (sigismember(&s, SIGPIPE) != 0) e(80); + if (sigismember(&s, SIGALRM) != 0) e(81); + if (sigismember(&s, SIGTERM) != 0) e(82); +} + +void func1(sig) +int sig; +{ + sig1++; +} + +void func2(sig) +int sig; +{ + sig2++; +} + +void test8b() +{ +/* Test sigprocmask and sigpending. */ + int i; + pid_t p; + sigset_t s, s1, s_empty, s_full, s_ill, s_ill_pip, s_nokill; + struct sigaction sa, osa; + + subtest = 2; + clearsigstate(); + + /* Construct s_ill = {SIGILL} and s_ill_pip {SIGILL | SIGPIP}, etc. */ + if (sigemptyset(&s_empty) != 0) e(1); + if (sigemptyset(&s_ill) != 0) e(2); + if (sigemptyset(&s_ill_pip) != 0) e(3); + if (sigaddset(&s_ill, SIGILL) != 0) e(4); + if (sigaddset(&s_ill_pip, SIGILL) != 0) e(5); + if (sigaddset(&s_ill_pip, SIGPIPE) != 0) e(6); + if (sigfillset(&s_full) != 0) e(7); + s_nokill = s_full; + if (sigdelset(&s_nokill, SIGKILL) != 0) e(8); +#ifndef _MINIX /* XXX - should unsupported signals be <= _NSIG? */ + if (SIGSTOP > _NSIG) e(666); +#endif /* _MINIX */ + if (SIGSTOP <= _NSIG && sigdelset(&s_nokill, SIGSTOP) != 0) e(888); + + /* Now get most of the signals into default state. Don't change SIGINT + * or SIGQUIT, so this program can be killed. SIGKILL is also special. + */ + sa.sa_handler = SIG_DFL; + sa.sa_mask = s_empty; + sa.sa_flags = 0; + for (i = 0; i < SIGS; i++) sigaction(i, &sa, &osa); + + /* The second argument may be zero. See if it wipes out the system. */ + for (i = 0; i < SIGS; i++) sigaction(i, (struct sigaction *) NULL, &osa); + + /* Install a signal handler. */ + sa.sa_handler = func1; + sa.sa_mask = s_ill; + sa.sa_flags = SA_NODEFER | SA_NOCLDSTOP; + osa.sa_handler = SIG_IGN; + osa.sa_mask = s_empty; + osa.sa_flags = 0; + if (sigaction(SIGHUP, &sa, &osa) != 0) e(9); + if (osa.sa_handler != SIG_DFL) e(10); + if (osa.sa_mask != 0) e(11); + if (osa.sa_flags != s_empty) e(12); + + /* Replace action and see if old value is read back correctly. */ + sa.sa_handler = func2; + sa.sa_mask = s_ill_pip; + sa.sa_flags = SA_RESETHAND | SA_NODEFER; + osa.sa_handler = SIG_IGN; + osa.sa_mask = s_empty; + osa.sa_flags = 0; + if (sigaction(SIGHUP, &sa, &osa) != 0) e(13); + if (osa.sa_handler != func1) e(14); + if (osa.sa_mask != s_ill) e(15); + if (osa.sa_flags != SA_NODEFER + && osa.sa_flags != (SA_NODEFER | SA_NOCLDSTOP)) e(16); + + /* Replace action once more and check what is read back. */ + sa.sa_handler = SIG_DFL; + sa.sa_mask = s_empty; + osa.sa_handler = SIG_IGN; + osa.sa_mask = s_empty; + osa.sa_flags = 0; + if (sigaction(SIGHUP, &sa, &osa) != 0) e(17); + if (osa.sa_handler != func2) e(18); + if (osa.sa_mask != s_ill_pip) e(19); + if (osa.sa_flags != (SA_RESETHAND | SA_NODEFER)) e(20); + + /* Test sigprocmask(SIG_SETMASK, ...). */ + if (sigprocmask(SIG_SETMASK, &s_full, &s1) != 0) e(18); /* block all */ + if (sigemptyset(&s1) != 0) e(19); + if (sigprocmask(SIG_SETMASK, &s_empty, &s1) != 0) e(20); /* block none */ + if (s1 != s_nokill) e(21); + if (sigprocmask(SIG_SETMASK, &s_ill, &s1) != 0) e(22); /* block SIGILL */ + if (s1 != s_empty) e(23); + if (sigprocmask(SIG_SETMASK, &s_ill_pip, &s1) != 0) e(24); /* SIGILL+PIP */ + if (s1 != s_ill) e(25); + if (sigprocmask(SIG_SETMASK, &s_full, &s1) != 0) e(26); /* block all */ + if (s1 != s_ill_pip) e(27); + + /* Test sigprocmask(SIG_UNBLOCK, ...) */ + if (sigprocmask(SIG_UNBLOCK, &s_ill, &s1) != 0) e(28); + if (s1 != s_nokill) e(29); + if (sigprocmask(SIG_UNBLOCK, &s_ill_pip, &s1) != 0) e(30); + s = s_nokill; + if (sigdelset(&s, SIGILL) != 0) e(31); + if (s != s1) e(32); + if (sigprocmask(SIG_UNBLOCK, &s_empty, &s1) != 0) e(33); + s = s_nokill; + if (sigdelset(&s, SIGILL) != 0) e(34); + if (sigdelset(&s, SIGPIPE) != 0) e(35); + if (s != s1) e(36); + s1 = s_nokill; + if (sigprocmask(SIG_SETMASK, &s_empty, &s1) != 0) e(37); + if (s != s1) e(38); + + /* Test sigprocmask(SIG_BLOCK, ...) */ + if (sigprocmask(SIG_BLOCK, &s_ill, &s1) != 0) e(39); + if (s1 != s_empty) e(40); + if (sigprocmask(SIG_BLOCK, &s_ill_pip, &s1) != 0) e(41); + if (s1 != s_ill) e(42); + if (sigprocmask(SIG_SETMASK, &s_full, &s1) != 0) e(43); + if (s1 != s_ill_pip) e(44); + + /* Check error condition. */ + errno = 0; + if (sigprocmask(20000, &s_full, &s1) != -1) e(45); + if (errno != EINVAL) e(46); + if (sigprocmask(SIG_SETMASK, &s_full, &s1) != 0) e(47); + if (s1 != s_nokill) e(48); + + /* If second arg is 0, nothing is set. */ + if (sigprocmask(SIG_SETMASK, (sigset_t *) NULL, &s1) != 0) e(49); + if (s1 != s_nokill) e(50); + if (sigprocmask(SIG_SETMASK, &s_ill_pip, &s1) != 0) e(51); + if (s1 != s_nokill) e(52); + if (sigprocmask(SIG_SETMASK, (sigset_t *) NULL, &s1) != 0) e(53); + if (s1 != s_ill_pip) e(54); + if (sigprocmask(SIG_BLOCK, (sigset_t *) NULL, &s1) != 0) e(55); + if (s1 != s_ill_pip) e(56); + if (sigprocmask(SIG_UNBLOCK, (sigset_t *) NULL, &s1) != 0) e(57); + if (s1 != s_ill_pip) e(58); + + /* Trying to block SIGKILL is not allowed, but is not an error, either. */ + s = s_empty; + if (sigaddset(&s, SIGKILL) != 0) e(59); + if (sigprocmask(SIG_BLOCK, &s, &s1) != 0) e(60); + if (s1 != s_ill_pip) e(61); + if (sigprocmask(SIG_SETMASK, &s_full, &s1) != 0) e(62); + if (s1 != s_ill_pip) e(63); + + /* Test sigpending. At this moment, all signals are blocked. */ + sa.sa_handler = func2; + sa.sa_mask = s_empty; + if (sigaction(SIGHUP, &sa, &osa) != 0) e(64); + p = getpid(); + kill(p, SIGHUP); /* send SIGHUP to self */ + if (sigpending(&s) != 0) e(65); + if (sigemptyset(&s1) != 0) e(66); + if (sigaddset(&s1, SIGHUP) != 0) e(67); + if (s != s1) e(68); + sa.sa_handler = SIG_IGN; + if (sigaction(SIGHUP, &sa, &osa) != 0) e(69); + if (sigpending(&s) != 0) e(70); + if (s != s_empty) e(71); +} + +/*---------------------------------------------------------------------------*/ +int x; +sigset_t glo_vol_set; + +void catch1(signo) +int signo; +{ + x = 42; +} + +void catch2(signo) +int signo; +{ + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, (sigset_t *) &glo_vol_set) != 0) + e(1); +} + +/* Verify that signal(2), which is now built on top of sigaction(2), still +* works. +*/ +void test8c() +{ + pid_t pid; + sigset_t sigset_var; + + subtest = 3; + clearsigstate(); + x = 0; + + /* Verify an installed signal handler persists across a fork(2). */ + if (signal(SIGTERM, catch1) == SIG_ERR) e(1); + switch (pid = fork()) { + case 0: /* child */ + errct = 0; + while (x == 0); + if (x != 42) e(2); + exit(errct == 0 ? 0 : 1); + case -1: e(3); break; + default: /* parent */ + sleep(1); + if (kill(pid, SIGTERM) != 0) e(4); + wait_for(pid); + break; + } + + /* Verify that the return value is the previous handler. */ + signal(SIGINT, SIG_IGN); + if (signal(SIGINT, catch2) != SIG_IGN) e(5); + if (signal(SIGINT, catch1) != catch2) e(6); + if (signal(SIGINT, SIG_DFL) != catch1) e(7); + if (signal(SIGINT, catch1) != SIG_DFL) e(8); + if (signal(SIGINT, SIG_DFL) != catch1) e(9); + if (signal(SIGINT, SIG_DFL) != SIG_DFL) e(10); + if (signal(SIGINT, catch1) != SIG_DFL) e(11); + + /* Verify that SIG_ERR is correctly generated. */ + if (signal(_NSIG + 1, catch1) != SIG_ERR) e(12); + if (signal(0, catch1) != SIG_ERR) e(13); + if (signal(-1, SIG_DFL) != SIG_ERR) e(14); + + /* Verify that caught signals are automatically reset to the default, + * and that further instances of the same signal are not blocked here + * or in the signal handler. + */ + if (signal(SIGTERM, catch1) == SIG_ERR) e(15); + switch ((pid = fork())) { + case 0: /* child */ + errct = 0; + while (x == 0); + if (x != 42) e(16); + if (sigismember((sigset_t *) &glo_vol_set, SIGTERM)) e(17); + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &sigset_var) != 0) e(18); + if (sigismember(&sigset_var, SIGTERM)) e(19); + +#if 0 +/* Use this if you have compiled signal() to have the broken SYSV behaviour. */ + if (signal(SIGTERM, catch1) != SIG_DFL) e(20); +#else + if (signal(SIGTERM, catch1) != catch1) e(20); +#endif + exit(errct == 0 ? 0 : 1); + default: /* parent */ + sleep(1); + if (kill(pid, SIGTERM) != 0) e(21); + wait_for(pid); + break; + case -1: e(22); break; + } +} + +/*---------------------------------------------------------------------------*/ +/* Test that the signal handler can be invoked recursively with the +* state being properly saved and restored. +*/ + +static int y; +static int z; + +void catch3(signo) +int signo; +{ + if (z == 1) { /* catching a nested signal */ + y = 2; + return; + } + z = 1; + if (kill(getpid(), SIGHUP) != 0) e(1); + while (y != 2); + y = 1; +} + +void test8d() +{ + struct sigaction act; + + subtest = 4; + clearsigstate(); + y = 0; + z = 0; + + act.sa_handler = catch3; + act.sa_mask = 0; + act.sa_flags = SA_NODEFER; /* Otherwise, nested occurence of + * SIGINT is blocked. */ + if (sigaction(SIGHUP, &act, (struct sigaction *) NULL) != 0) e(2); + if (kill(getpid(), SIGHUP) != 0) e(3); + if (y != 1) e(4); +} + +/*---------------------------------------------------------------------------*/ + +/* Test that the signal mask in effect for the duration of a signal handler +* is as specified in POSIX Section 3, lines 718 -724. Test that the +* previous signal mask is restored when the signal handler returns. +*/ + +void catch4(signo) +int signo; +{ + sigset_t oset; + sigset_t set; + + if (sigemptyset(&set) == -1) e(5001); + if (sigaddset(&set, SIGTERM) == -1) e(5002); + if (sigaddset(&set, SIGHUP) == -1) e(5003); + if (sigaddset(&set, SIGINT) == -1) e(5004); + if (sigaddset(&set, SIGPIPE) == -1) e(5005); + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &oset) != 0) e(5006); + if (oset != set) e(5007); +} + +void test8e() +{ + struct sigaction act, oact; + sigset_t set, oset; + + subtest = 5; + clearsigstate(); + + act.sa_handler = catch4; + sigemptyset(&act.sa_mask); + sigaddset(&act.sa_mask, SIGTERM); + sigaddset(&act.sa_mask, SIGHUP); + act.sa_flags = 0; + if (sigaction(SIGINT, &act, &oact) == -1) e(2); + + if (sigemptyset(&set) == -1) e(3); + if (sigaddset(&set, SIGPIPE) == -1) e(4); + if (sigprocmask(SIG_SETMASK, &set, &oset) == -1) e(5); + if (kill(getpid(), SIGINT) == -1) e(6); + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &oset) == -1) e(7); + if (sigemptyset(&set) == -1) e(8); + if (sigaddset(&set, SIGPIPE) == -1) e(9); + if (set != oset) e(10); +} + +/*---------------------------------------------------------------------------*/ + +/* Test the basic functionality of sigsuspend(2). */ + +void catch5(signo) +int signo; +{ + x = 1; +} + +void test8f() +{ + sigset_t set; + int r; + struct sigaction act; + pid_t pid; + + subtest = 6; + clearsigstate(); + + switch (pid = fork()) { + case 0: /* child */ + errct = 0; + sleep(1); + if (kill(getppid(), SIGINT) == -1) e(1); + exit(errct == 0 ? 0 : 1); + case -1: e(2); break; + default: /* parent */ + if (sigemptyset(&act.sa_mask) == -1) e(3); + act.sa_flags = 0; + act.sa_handler = catch5; + if (sigaction(SIGINT, &act, (struct sigaction *) NULL) == -1) e(4); + + if (sigemptyset(&set) == -1) e(5); + r = sigsuspend(&set); + + if (r != -1 || errno != EINTR || x != 1) e(6); + wait_for(pid); + break; + } +} + +/*----------------------------------------------------------------------*/ + +/* Test that sigsuspend() does block the signals specified in its +* argument, and after sigsuspend returns, the previous signal +* mask is restored. +* +* The child sends two signals to the parent SIGINT and then SIGPIPE, +* separated by a long delay. The parent executes sigsuspend() with +* SIGINT blocked. It is expected that the parent's SIGPIPE handler +* will be invoked, then sigsuspend will return restoring the +* original signal mask, and then the SIGPIPE handler will be +* invoked. +*/ + +void sigint_handler(signo) +int signo; +{ + x = 1; + z++; +} + +void sigpipe_handler(signo) +int signo; +{ + x = 2; + z++; +} + +void test8g() +{ + sigset_t set; + int r; + struct sigaction act; + pid_t pid; + + subtest = 7; + clearsigstate(); + x = 0; + z = 0; + + switch (pid = fork()) { + case 0: /* child */ + errct = 0; + sleep(1); + if (kill(getppid(), SIGINT) == -1) e(1); + sleep(1); + if (kill(getppid(), SIGPIPE) == -1) e(2); + exit(errct == 0 ? 0 : 1); + case -1: e(3); break; + default: /* parent */ + if (sigemptyset(&act.sa_mask) == -1) e(3); + act.sa_flags = 0; + act.sa_handler = sigint_handler; + if (sigaction(SIGINT, &act, (struct sigaction *) NULL) == -1) e(4); + + act.sa_handler = sigpipe_handler; + if (sigaction(SIGPIPE, &act, (struct sigaction *) NULL) == -1) e(5); + + if (sigemptyset(&set) == -1) e(6); + if (sigaddset(&set, SIGINT) == -1) e(7); + r = sigsuspend(&set); + if (r != -1) e(8); + if (errno != EINTR) e(9); + if (z != 2) e(10); + if (x != 1) e(11); + wait_for(pid); + break; + } +} + +/*--------------------------------------------------------------------------*/ + +/* Test that sigsuspend() does block the signals specified in its +* argument, and after sigsuspend returns, the previous signal +* mask is restored. +* +* The child sends three signals to the parent: SIGHUP, then SIGPIPE, +* and then SIGTERM, separated by a long delay. The parent executes +* sigsuspend() with SIGHUP and SIGPIPE blocked. It is expected that +* the parent's SIGTERM handler will be invoked first, then sigsuspend() +* will return restoring the original signal mask, and then the other +* two handlers will be invoked. +*/ + +void sighup8(signo) +int signo; +{ + x = 1; + z++; +} + +void sigpip8(signo) +int signo; +{ + x = 1; + z++; +} + +void sigter8(signo) +int signo; +{ + x = 2; + z++; +} + +void test8h() +{ + sigset_t set; + int r; + struct sigaction act; + pid_t pid; + + subtest = 8; + clearsigstate(); + x = 0; + z = 0; + + switch (pid = fork()) { + case 0: /* child */ + errct = 0; + sleep(1); + if (kill(getppid(), SIGHUP) == -1) e(1); + sleep(1); + if (kill(getppid(), SIGPIPE) == -1) e(2); + sleep(1); + if (kill(getppid(), SIGTERM) == -1) e(3); + exit(errct == 0 ? 0 : 1); + case -1: e(5); break; + default: /* parent */ + if (sigemptyset(&act.sa_mask) == -1) e(6); + act.sa_flags = 0; + act.sa_handler = sighup8; + if (sigaction(SIGHUP, &act, (struct sigaction *) NULL) == -1) e(7); + + act.sa_handler = sigpip8; + if (sigaction(SIGPIPE, &act, (struct sigaction *) NULL) == -1) e(8); + + act.sa_handler = sigter8; + if (sigaction(SIGTERM, &act, (struct sigaction *) NULL) == -1) e(9); + + if (sigemptyset(&set) == -1) e(10); + if (sigaddset(&set, SIGHUP) == -1) e(11); + if (sigaddset(&set, SIGPIPE) == -1) e(12); + r = sigsuspend(&set); + if (r != -1) e(13); + if (errno != EINTR) e(14); + if (z != 3) e(15); + if (x != 1) e(16); + wait_for(pid); + break; + } +} + +/*--------------------------------------------------------------------------*/ + +/* Block SIGHUP and SIGTERM with sigprocmask(), send ourself SIGHUP +* and SIGTERM, unblock these signals with sigprocmask, and verify +* that these signals are delivered. +*/ + +void sighup9(signo) +int signo; +{ + y++; +} + +void sigter9(signo) +int signo; +{ + z++; +} + +void test8i() +{ + sigset_t set; + struct sigaction act; + + subtest = 9; + clearsigstate(); + y = 0; + z = 0; + + if (sigemptyset(&act.sa_mask) == -1) e(1); + act.sa_flags = 0; + + act.sa_handler = sighup9; + if (sigaction(SIGHUP, &act, (struct sigaction *) NULL) == -1) e(2); + + act.sa_handler = sigter9; + if (sigaction(SIGTERM, &act, (struct sigaction *) NULL) == -1) e(3); + + if (sigemptyset(&set) == -1) e(4); + if (sigaddset(&set, SIGTERM) == -1) e(5); + if (sigaddset(&set, SIGHUP) == -1) e(6); + if (sigprocmask(SIG_SETMASK, &set, (sigset_t *)NULL) == -1) e(7); + + if (kill(getpid(), SIGHUP) == -1) e(8); + if (kill(getpid(), SIGTERM) == -1) e(9); + if (y != 0) e(10); + if (z != 0) e(11); + + if (sigemptyset(&set) == -1) e(12); + if (sigprocmask(SIG_SETMASK, &set, (sigset_t *)NULL) == -1) e(12); + if (y != 1) e(13); + if (z != 1) e(14); +} + +/*---------------------------------------------------------------------------*/ + +/* Block SIGINT and then send this signal to ourself. +* +* Install signal handlers for SIGALRM and SIGINT. +* +* Set an alarm for 6 seconds, then sleep for 7. +* +* The SIGALRM should interrupt the sleep, but the SIGINT +* should remain pending. +*/ + +void sighup10(signo) +int signo; +{ + y++; +} + +void sigalrm_handler10(signo) +int signo; +{ + z++; +} + +void test8j() +{ + sigset_t set, set2; + struct sigaction act; + + subtest = 10; + clearsigstate(); + y = 0; + z = 0; + + if (sigemptyset(&act.sa_mask) == -1) e(1); + act.sa_flags = 0; + + act.sa_handler = sighup10; + if (sigaction(SIGHUP, &act, (struct sigaction *) NULL) == -1) e(2); + + act.sa_handler = sigalrm_handler10; + if (sigaction(SIGALRM, &act, (struct sigaction *) NULL) == -1) e(3); + + if (sigemptyset(&set) == -1) e(4); + if (sigaddset(&set, SIGHUP) == -1) e(5); + if (sigprocmask(SIG_SETMASK, &set, (sigset_t *)NULL) == -1) e(6); + + if (kill(getpid(), SIGHUP) == -1) e(7); + if (sigpending(&set) == -1) e(8); + if (sigemptyset(&set2) == -1) e(9); + if (sigaddset(&set2, SIGHUP) == -1) e(10); + if (set2 != set) e(11); + alarm(6); + sleep(7); + if (sigpending(&set) == -1) e(12); + if (set != set2) e(13); + if (y != 0) e(14); + if (z != 1) e(15); +} + +/*--------------------------------------------------------------------------*/ + +void test8k() +{ + subtest = 11; +} +void test8l() +{ + subtest = 12; +} + +/*---------------------------------------------------------------------------*/ + +/* Basic test for setjmp/longjmp. This includes testing that the +* signal mask is properly restored. +*/ + +void test8m() +{ + jmp_buf jb; + sigset_t ss; + + subtest = 13; + clearsigstate(); + + ss = 0x32; + if (sigprocmask(SIG_SETMASK, &ss, (sigset_t *)NULL) == -1) e(1); + if (setjmp(jb)) { + if (sigprocmask(SIG_BLOCK, (sigset_t *)NULL, &ss) == -1) e(2); + if (ss != 0x32) e(388); + return; + } + ss = 0x3abc; + if (sigprocmask(SIG_SETMASK, &ss, (sigset_t *)NULL) == -1) e(4); + longjmp(jb, 1); +} + +void longjerr() +{ + e(5); +} + +/*--------------------------------------------------------------------------*/ + +/* Test for setjmp/longjmp. +* +* Catch a signal. While in signal handler do setjmp/longjmp. +*/ + +void catch14(signo, code, scp) +int signo; +int code; +struct sigcontext *scp; +{ + jmp_buf jb; + + if (setjmp(jb)) { + x++; + sigreturn(scp); + e(1); + } + y++; + longjmp(jb, 1); + e(2); +} + +void test8n() +{ + struct sigaction act; + typedef _PROTOTYPE( void (*sighandler_t), (int sig) ); + + subtest = 14; + clearsigstate(); + x = 0; + y = 0; + + act.sa_flags = 0; + act.sa_mask = 0; + act.sa_handler = (sighandler_t) catch14; /* fudge */ + if (sigaction(SIGSEGV, &act, (struct sigaction *) NULL) == -1) e(3); + if (kill(getpid(), SIGSEGV) == -1) e(4); + + if (x != 1) e(5); + if (y != 1) e(6); +} + +/*---------------------------------------------------------------------------*/ + +/* Test for setjmp/longjmp. + * + * Catch a signal. Longjmp out of signal handler. + */ +jmp_buf glo_jb; + +void catch15(signo) +int signo; +{ + z++; + longjmp(glo_jb, 7); + e(1); + +} + +void test8o() +{ + struct sigaction act; + int k; + + subtest = 15; + clearsigstate(); + z = 0; + + act.sa_flags = 0; + act.sa_mask = 0; + act.sa_handler = catch15; + if (sigaction(SIGALRM, &act, (struct sigaction *) NULL) == -1) e(2); + + if ((k = setjmp(glo_jb))) { + if (z != 1) e(399); + if (k != 7) e(4); + return; + } + if (kill(getpid(), SIGALRM) == -1) e(5); +} + +void clearsigstate() +{ + int i; + sigset_t sigset_var; + + /* Clear the signal state. */ + for (i = 1; i <= _NSIG; i++) signal(i, SIG_IGN); + for (i = 1; i <= _NSIG; i++) signal(i, SIG_DFL); + sigfillset(&sigset_var); + sigprocmask(SIG_UNBLOCK, &sigset_var, (sigset_t *)NULL); +} + +void quit() +{ + + chdir(".."); + system("rm -rf DIR*"); + + if (errct == 0) { + printf("ok\n"); + exit(0); + } else { + printf("%d errors\n", errct); + exit(4); + } +} + +void wait_for(pid) +pid_t pid; +{ +/* Expect exactly one child, and that it exits with 0. */ + + int r; + int status; + + errno = 0; + while (1) { + errno = 0; + r = wait(&status); + if (r == pid) { + errno = 0; + if (status != 0) e(90); + return; + } + if (r < 0) { + e(91); + return; + } + e(92); + } +} + +void e(n) +int n; +{ + char msgbuf[80]; + + sprintf(msgbuf, "Subtest %d, error %d errno=%d ", subtest, n, errno); + perror(msgbuf); + if (errct++ > MAX_ERROR) { + fprintf(stderr, "Too many errors; test aborted\n"); + chdir(".."); + system("rm -rf DIR*"); + exit(1); + } +} diff --git a/test/test9.c b/test/test9.c new file mode 100644 index 000000000..59ced5998 --- /dev/null +++ b/test/test9.c @@ -0,0 +1,274 @@ +/* Test 9 setjmp with register variables. Author: Ceriel Jacobs */ + +#include <sys/types.h> +#include <setjmp.h> +#include <signal.h> + +#define MAX_ERROR 4 + +#include "common.c" + +char *tmpa; + +_PROTOTYPE(int main, (int argc, char *argv [])); +_PROTOTYPE(void test9a, (void)); +_PROTOTYPE(void test9b, (void)); +_PROTOTYPE(void test9c, (void)); +_PROTOTYPE(void test9d, (void)); +_PROTOTYPE(void test9e, (void)); +_PROTOTYPE(void test9f, (void)); +_PROTOTYPE(char *addr, (void)); +_PROTOTYPE(void garbage, (void)); +_PROTOTYPE(void level1, (void)); +_PROTOTYPE(void level2, (void)); +_PROTOTYPE(void dolev, (void)); +_PROTOTYPE(void catch, (int s)); +_PROTOTYPE(void hard, (void)); + +int main(argc, argv) +int argc; +char *argv[]; +{ + jmp_buf envm; + int i, j, m = 0xFFFF; + + start(9); + if (argc == 2) m = atoi(argv[1]); + for (j = 0; j < 100; j++) { + if (m & 00001) test9a(); + if (m & 00002) test9b(); + if (m & 00004) test9c(); + if (m & 00010) test9d(); + if (m & 00020) test9e(); + if (m & 00040) test9f(); + } + if (errct) quit(); + i = 1; + if (setjmp(envm) == 0) { + i = 2; + longjmp(envm, 1); + } else { + if (i == 2) { + /* Correct */ + } else if (i == 1) { + printf("WARNING: The setjmp/longjmp of this machine restore register variables\n\ +to the value they had at the time of the Setjmp\n"); + } else { + printf("Aha, I just found one last error\n"); + return 1; + } + } + quit(); + return(-1); /* impossible */ +} + + +void test9a() +{ + register p; + + subtest = 1; + p = 200; + garbage(); + if (p != 200) e(1); +} + +void test9b() +{ + register p, q; + + subtest = 2; + p = 200; + q = 300; + garbage(); + if (p != 200) e(1); + if (q != 300) e(2); +} + +void test9c() +{ + register p, q, r; + + subtest = 3; + p = 200; + q = 300; + r = 400; + garbage(); + if (p != 200) e(1); + if (q != 300) e(2); + if (r != 400) e(3); +} + +char buf[512]; + +void test9d() +{ + register char *p; + + subtest = 4; + p = &buf[100]; + garbage(); + if (p != &buf[100]) e(1); +} + +void test9e() +{ + register char *p, *q; + + subtest = 5; + p = &buf[100]; + q = &buf[200]; + garbage(); + if (p != &buf[100]) e(1); + if (q != &buf[200]) e(2); +} + +void test9f() +{ + register char *p, *q, *r; + + subtest = 6; + p = &buf[100]; + q = &buf[200]; + r = &buf[300]; + garbage(); + if (p != &buf[100]) e(1); + if (q != &buf[200]) e(2); + if (r != &buf[300]) e(3); +} + +jmp_buf env; + +/* return address of local variable. + This way we can check that the stack is not polluted. +*/ +char * + addr() +{ + char a; + + return &a; +} + +void garbage() +{ + register i, j, k; + register char *p, *q, *r; + char *a; + + p = &buf[300]; + q = &buf[400]; + r = &buf[500]; + i = 10; + j = 20; + k = 30; + switch (setjmp(env)) { + case 0: + a = addr(); +#ifdef __GNUC__ + /* + * to defeat the smartness of the GNU C optimizer we pretend we + * use 'a'. Otherwise the optimizer will not detect the looping + * effectuated by setjmp/longjmp, so that it thinks it can get + * rid of the assignment to 'a'. + */ + srand((unsigned)&a); +#endif + longjmp(env, 1); + break; + case 1: + if (i != 10) e(11); + if (j != 20) e(12); + if (k != 30) e(13); + if (p != &buf[300]) e(14); + if (q != &buf[400]) e(15); + if (r != &buf[500]) e(16); + tmpa = addr(); + if (a != tmpa) e(17); + level1(); + break; + case 2: + if (i != 10) e(21); + if (j != 20) e(22); + if (k != 30) e(23); + if (p != &buf[300]) e(24); + if (q != &buf[400]) e(25); + if (r != &buf[500]) e(26); + tmpa = addr(); + if (a != tmpa) e(27); + level2(); + break; + case 3: + if (i != 10) e(31); + if (j != 20) e(32); + if (k != 30) e(33); + if (p != &buf[300]) e(34); + if (q != &buf[400]) e(35); + if (r != &buf[500]) e(36); + tmpa = addr(); + if (a != tmpa) e(37); + hard(); + case 4: + if (i != 10) e(41); + if (j != 20) e(42); + if (k != 30) e(43); + if (p != &buf[300]) e(44); + if (q != &buf[400]) e(45); + if (r != &buf[500]) e(46); + tmpa = addr(); + if (a != tmpa) e(47); + return; + break; + default: e(100); + } + e(200); +} + +void level1() +{ + register char *p; + register i; + + i = 1000; + p = &buf[10]; + i = 200; + p = &buf[20]; + longjmp(env, 2); +} + +void level2() +{ + register char *p; + register i; + + i = 0200; + p = &buf[2]; + *p = i; + dolev(); +} + +void dolev() +{ + register char *p; + register i; + + i = 010; + p = &buf[3]; + *p = i; + longjmp(env, 3); +} + +void catch(s) +int s; +{ + longjmp(env, 4); +} + +void hard() +{ + register char *p; + + signal(SIGHUP, catch); + for (p = buf; p <= &buf[511]; p++) *p = 025; + kill(getpid(), SIGHUP); +} diff --git a/tools/Makefile b/tools/Makefile new file mode 100755 index 000000000..dcc178f3a --- /dev/null +++ b/tools/Makefile @@ -0,0 +1,76 @@ +# Makefile for the kernel image. +# +u=/usr +CC= exec cc +CFLAGS= -O -D_MINIX -D_POSIX_SOURCE +MDEC= /usr/mdec +MAKE= exec make -$(MAKEFLAGS) + +# specify the programs that are part of the system image +PROGRAMS= ../kernel/kernel \ + ../servers/mm/mm \ + ../servers/fs/fs \ + ../servers/is/is \ + ../drivers/tty/tty \ + ../drivers/memory/memory \ + ../drivers/at_wini/at_wini \ + ../drivers/floppy/floppy \ + ../drivers/printer/printer \ + ../servers/init/init \ + #bootfs.img + +usage: + @echo " " >&2 + @echo "Master Makefile to create new MINIX configuration. Root privileges required." >&2 + @echo "Usage:" >&2 + @echo " make libraries # Make system libraries" >&2 + @echo " make programs # Compile and install all programs" >&2 + @echo " make fresh # Make clean, libraries, and programs" >&2 + @echo " make image # Make programs and create system image" >&2 + @echo " make hdboot # Make image, and install to hard disk" >&2 + @echo " make fdboot # Make image, and install to floppy disk" >&2 + @echo " make bootable # Make hard disk bootable" >&2 + @echo " make clean # Remove all compiler results, except libs" >&2 + @echo " " >&2 + @echo "To create a fresh MINIX configuration, try:" >&2 + @echo " make fresh hdboot" >&2 + @echo " " >&2 + +# create a fresh configuration or system image +fresh: + cd ../lib && $(MAKE) clean + $(MAKE) clean + $(MAKE) libraries programs + +image: programs + installboot -image $@ $(PROGRAMS) + + +# rebuild the program or system libraries +programs: + cd ../kernel && $(MAKE) + cd ../servers && $(MAKE) install + cd ../drivers && $(MAKE) install + +libraries: + cd ../lib && $(MAKE) install + + +# make bootable and place system images +bootable: + exec su root mkboot bootable + +hdboot: image + exec sh mkboot $@ + +fdboot: image + exec su root mkboot $@ + + +# clean up compile results +clean: + cd ../kernel && $(MAKE) $@ + cd ../servers && $(MAKE) $@ + cd ../drivers && $(MAKE) $@ + rm -f *.bak image + diff --git a/tools/chrootmake.sh b/tools/chrootmake.sh new file mode 100755 index 000000000..53821dd94 --- /dev/null +++ b/tools/chrootmake.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +DIST=/usr/dist +export PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin + +cd /usr/src || exit 1 +mkdir -p $DIST +#echo "Making src.tar" +#tar cf /dist/src.tar /usr/src || exit 1 +make world install || exit 1 + +cd tools || exit 1 +make hdboot + +#rm -rf /usr/src +#echo "Making bin.tar" +#tar cf /dist/bin.tar / + +exit 0 + diff --git a/tools/mkboot b/tools/mkboot new file mode 100755 index 000000000..2170871fc --- /dev/null +++ b/tools/mkboot @@ -0,0 +1,159 @@ +#!/bin/sh +# +# mkboot 2.0 - make boot floppy, make root device bootable, etc. +# Author: Kees J. Bot + +trap 'e=$?; rm -f /tmp/mkb.$$; exit $e' 0 2 + +mdec=/usr/mdec # bootstraps + +# Check arguments. +case "$#:$1" in +1:bootable | 1:hdboot | [12]:fdboot | 1:cdfdboot ) + action=$1 dev=$2 + ;; +*) echo "Usage: $0 [bootable | hdboot | cdfdboot | fdboot [device]]" >&2 + exit 1 +esac + +# Get the device table. +. /etc/fstab + +# The real root device may be the RAM disk. +realroot=`printroot -r` + +case $action in +bootable | hdboot) + # We need the root device. + if [ $realroot = $root ] + then + rootdir= + else + umount $root 2>/dev/null + mount $root /mnt || exit + rootdir=/mnt + fi +esac + +case $action in +bootable) + # Install the boot monitor on the root device and make it bootable. + install -cs -m 644 $mdec/boot $rootdir/boot/boot || exit + sync + installboot -device $root $mdec/bootblock /boot/boot || exit + test $realroot != $root && umount $root + ;; +hdboot) + # Install a new image on the root device. + if [ ! -d $rootdir/boot/image ] + then + # /boot/image is not yet a directory! Fix it. + su root -c \ + "exec mv $rootdir/boot/image /M" + install -d $rootdir/boot/image + su root -c \ + "exec mv $rootdir/M $rootdir/boot/image/`uname -r`.`uname -v`" + fi + + ./tell_config OS_RELEASE . OS_VERSION >/tmp/mkb.$$ + version=`sed 's/[" ]//g;/^$/d' </tmp/mkb.$$` + + revision=`cat revision 2>/dev/null` + oldrev=$revision + target="${version}r${revision}" + + if [ -z "$revision" ] + then + revision=0 + elif [ -f $rootdir/boot/image/$target ] + then + if [ $rootdir/boot/image/$target -newer image ] + then + echo "$root:/boot/image/$target is up to date" + test $realroot != $root && umount $root + exit 0 + fi + revision=`expr $revision + 1` + fi + target="${version}r${revision}" + + set -- `ls -t $rootdir/boot/image` + + case $# in + 0|1) + # Not much there, do not remove a thing. + ;; + *) + # Remove the newest image in /boot/image. This seems strange, + # but the old image is normally the "stable" image. + + echo "rm $root:/boot/image/$1" + rm -f "$rootdir/boot/image/$1" + esac + + # Install the new image. + echo "cp image $root:/boot/image/$target" + cp -p image $rootdir/boot/image/$target || exit + + # Save the revision number. + test "$revision" != "$oldrev" && echo $revision >revision + + test $realroot != $root && umount $root + echo "Done." + ;; +fdboot) + # fdboot: Make a boot floppy. + + if [ -z "$dev" ] + then + echo -n \ +"Finish the name of the floppy device to write (by default 'fd0'): /dev/"; + read dev + case "$dev" in + '') dev=/dev/fd0 + ;; + /dev/*) + ;; + *) dev=/dev/$dev + esac + fi + + # Make a file system. + umount $dev 2>/dev/null + mkfs -B 1024 -i 512 $dev || exit + + # Install /dev, /boot/boot and /boot/image. + mount $dev /mnt || exit + mkdir /mnt/boot || exit + mkdir /mnt/image || exit + cpdir /dev /mnt/dev || exit + cp -p $mdec/boot /mnt/boot/boot || exit + cp -p image /mnt/boot/image/ || exit + umount $dev || exit + + # Make bootable and copy the boot parameters. + installboot -d $dev $mdec/bootblock /boot/boot || exit + dd if=$root of=$dev skip=1 seek=1 count=1 conv=silent || exit + edparams $dev 'main(){delay 2000;boot}; save' || exit + echo "Test kernel installed on $dev with boot parameters from $root" + ;; + +cdfdboot) + # cdfdboot: Make a boot floppy image to go on a CD for booting from. + dev=/dev/ram + umount $dev 2>/dev/null + mkfs -B 1024 -b 1440 -i 512 $dev || exit 1 + + # Install /dev, /boot + mount $dev /mnt || exit 1 + mkdir /mnt/dev + ( cd /mnt/dev && /usr/src/commands/scripts/MAKEDEV.sh std ) + cp -p image /mnt/minix || exit 1 + cp -p ../boot/boot /mnt/boot || exit 1 + umount $dev || exit 1 + installboot -d $dev ../boot/bootblock boot || exit 1 + + # copy image + dd if=$dev of=cdfdimage bs=8192 count=180 +esac +exit 0 diff --git a/tools/release.sh b/tools/release.sh new file mode 100755 index 000000000..cfc2ebb20 --- /dev/null +++ b/tools/release.sh @@ -0,0 +1,59 @@ +#!/bin/sh + +COPYITEMS="usr/src usr/bin bin usr/lib" +RELEASEDIR=/usr/r/release +IMAGE=cdfdimage +ISO=minix.iso +RAM=/dev/ram +if [ `wc -c $RAM | awk '{ print $1 }'` -ne 1474560 ] +then echo "$RAM should be exactly 1440k." + exit 1 +fi +echo "Warning: I'm going to mkfs $RAM!" +echo "Temporary (sub)partition to use to make the /usr FS image? " +echo "It has to be at least 40MB, not much more because it's going to be " +echo "appended to the .iso. It will be mkfsed!" +echo -n "Device: /dev/" +read dev || exit 1 +TMPDISK=/dev/$dev + +if [ -b $TMPDISK ] +then : +else echo "$TMPDISK is not a block device.." + exit 1 +fi + +umount $TMPDISK +umount $RAM + +( cd .. && make clean ) +rm -rf $RELEASEDIR $ISO $IMAGE +mkdir -p $RELEASEDIR +mkfs -b 1440 -B 1024 $RAM || exit +echo " * mounting $RAM as $RELEASEDIR" +mount $RAM $RELEASEDIR || exit +mkdir $RELEASEDIR/usr + +mkfs -B 1024 $TMPDISK +echo " * mounting $TMPDISK as $RELEASEDIR/usr" +mount $TMPDISK $RELEASEDIR/usr || exit +mkdir -p $RELEASEDIR/tmp +mkdir -p $RELEASEDIR/usr/tmp +echo " * Transfering $COPYITEMS to $RELEASEDIR" +( cd / && tar cf - $COPYITEMS ) | ( cd $RELEASEDIR && tar xf - ) || exit 1 +echo " * Chroot build" +chroot $RELEASEDIR '/bin/sh -x /usr/src/tools/chrootmake.sh' || exit 1 +echo " * Chroot build done" +umount $TMPDISK || exit +umount $RAM || exit +cp $RAM rootimage +make programs image +(cd ../boot && make) +make image || exit 1 +./mkboot cdfdboot +writeisofs -l MINIX -b $IMAGE /tmp $ISO || exit 1 +echo "Appending Minix root filesystem" +cat >>$ISO rootimage || exit 1 +echo "Appending Minix usr filesystem" +cat >>$ISO $TMPDISK || exit 1 +ls -al $ISO diff --git a/tools/revision b/tools/revision new file mode 100644 index 000000000..b1e7d265f --- /dev/null +++ b/tools/revision @@ -0,0 +1 @@ +61 diff --git a/tools/tell_config b/tools/tell_config new file mode 100755 index 000000000..89e571ec3 --- /dev/null +++ b/tools/tell_config @@ -0,0 +1,13 @@ +#!/bin/sh +# +# tellconfig - Tell the value of a <minix/config.h> parameter +# Author: Kees J. Bot + +echo " +#include <minix/config.h> +$* +" >/tmp/tell.$$ +exec </tmp/tell.$$ +rm /tmp/tell.$$ + +exec cc -P -E -